Compare commits

...
Author SHA1 Message Date
Workbench IA 53ebaa7e40 Réconcilier la validation 2.1.1 avec le RTC D1759 2026-10-01 07:47:57 +00:00
Mahtan 11a98a7bac Merge pull request 'Émuler le RTC D1759 dès le démarrage' (#4) from agent/rtc-d1759 into ipod_touch_2g 2026-10-01 09:45:44 +02:00
Workbench IA d2c3478fa6 Émuler le RTC D1759 et supprimer le réglage automatique de l'heure 2026-10-01 07:41:33 +00:00
Workbench IA 21a155d35f Valider iPhone OS 2.1.1 et documenter les tests des applications 2026-10-01 07:17:19 +00:00
Mahtan 436bdc536c Valider les boutons, le tactile et les outils du simulateur
Relecture et validations locales terminées : iPhone OS 2.2.1 et 2.1.1, gestes, HTTP, saisie, installation et persistance.
2026-10-01 09:15:58 +02:00
Workbench IA 5f65af21fc Fiabiliser les transferts et automatiser les tests d'applications 2026-09-30 16:27:48 +00:00
Workbench IA 524e9bbb17 Corriger les accès GPIO et fiabiliser le tactile au réveil 2026-09-30 16:25:06 +00:00
Mahtan e0fd5d5eff Fiabiliser le simulateur et déverrouiller SpringBoard après installation
Relecture et validations locales effectuées : démarrage v11, installation persistante et lancement HelloEmu, session déjà lancée, erreurs QEMU et réseau.
2026-09-30 17:13:13 +02:00
Workbench IA 7b6afeb477 Attendre le déverrouillage d’une session déjà lancée 2026-09-30 15:12:13 +00:00
Workbench IA e28583407a Déverrouiller SpringBoard avant le lancement des applications 2026-09-30 15:08:21 +00:00
Workbench IA 72c64688f1 Fiabiliser la reprise du simulateur après déplacement des homes 2026-09-30 14:38:10 +00:00
Codex 163e253af8 Support iPod OS 2.2.1 networking and persistent NAND writes
Implement UART/PPP support, firmware AES/SHA1 compatibility, production VSVFL NAND page programming and block erases, and display clock status.

Preserve the host tools, emuagent v10, firmware generation helpers, dependency patches and handoff in contrib/ipod-emu. NAND data survives restart; application installation can still leave SpringBoard reporting device locked.
2026-09-30 13:15:07 +00:00
Martijn de Vos 328815631b Merge pull request #213 from mrcxlinux/ipod_touch_2g
Include commands for Arch Linux and it's derivatives (RUNNING.MD)
2025-01-02 09:20:51 +01:00
陈华 c5998ab084 Include commands for Arch Linux and it's derivatives 2025-01-01 17:57:06 +02:00
Martijn de Vos 1c586e8a0c Also fixed type of fsize in SPI 2024-12-25 09:46:50 +01:00
Martijn de Vos 7022646c4d Fixed gsize type 2024-12-24 09:06:06 +01:00
Martijn de Vos fa0981b602 Merge pull request #205 from devos50/brightness
Added support for brightness
2024-12-22 11:44:06 +01:00
daniilfigasystems 0cb390e281 Added support for brightness 2024-12-22 11:41:17 +01:00
Martijn de Vos 0d8014f8db Fixed path to configure 2024-12-22 10:39:03 +01:00
Martijn de Vos e241f91a5b Merge pull request #176 from Patrosi73/ipod_touch_2g
fix swapLong argument
2024-08-04 16:44:39 +02:00
PatRyk 4400b861c8 Update ipod_touch_sha1.c
fix swapLong argument
2024-08-04 16:34:49 +02:00
Martijn de Vos cae1b51a78 Merge pull request #172 from daniilfigasystems/ipod_touch_2g
Bugfix
2024-07-31 09:30:34 +02:00
daniilfigasystems a99e51abfa Format code, add more verbosity (for implementing lock screen), fixed warnings 2024-07-31 11:22:16 +09:00
daniilfigasystems ce74809f11 Rename unknown1 to render 2024-07-31 11:00:16 +09:00
daniilfigasystems ceb93afd79 Fix printf newline 2024-07-31 10:58:04 +09:00
Martijn de Vos bad3d0cc90 Merge pull request #170 from daniilfigasystems/ipod_touch_2g
Fixed bugs added new devices
2024-07-30 13:01:57 +02:00
daniilfigasystems 82b95a84a3 Add ctrl reg axis enabled check 2024-07-30 11:35:19 +09:00
daniilfigasystems 28f87fc7d6 Fixed bugs added new devices
Fixed unexpected CLCD interrupt (for now only unexcepted interrupt from mulltitouch), added audio codec skeleton, added light sensor, fixed pressing other buttons lead to crash, added kernel patch check (improves perfomance a little), added more verbosity, planning to add working wifi (need datasheet)
2024-07-30 10:22:49 +09:00
olivia1246 973b5a90ef Build on Linux (Ubuntu) (RUNNING.md) (#143)
* Update RUNNING.md

I think is good now super big thanks to https://github.com/Polytelus on https://github.com/devos50/qemu-ios/issues/92 and ChatGPT lolz

* Update RUNNING.md

thanks for pointing it out https://github.com/MrDiagnose I didnt realise that

* Update RUNNING.md

thank you everyone for the build dir recommendation

* Update RUNNING.md

a little space for easier copying I hope
2024-07-29 20:47:34 +02:00
Martijn de Vos adaea1d394 Merge pull request #122 from devos50/gpio
Generalized GPIO logic
2024-01-13 13:58:25 +01:00
Martijn de Vos 7d55fc7991 Generalized GPIO logic
As preparation for the emulation of DFU mode
2024-01-13 13:56:30 +01:00
Martijn de Vos d8e7109d35 Merge pull request #121 from thebreaddev/ipod_touch_2g
fix FPE and allow running any code
2024-01-12 16:38:26 +01:00
BreadDEV 93dbb7ad69 fix floating point exception
used code from https://github.com/devos50/qemu-ios/commit/8acdcfc48ad19ee6dfb30d7be3abcb6110cb2f25
it was on ipod_touch_1g branch but not on 2g
2024-01-12 21:38:03 +07:00
BreadDEV f7f46cc0b3 boot args modification to disable code signature check
i added "amfi_allow_any_signature=1" boot arg so you can run .app that was fakesigned with ldid -S
2024-01-11 18:16:12 +07:00
Martijn de Vos 32e2ae008a Merge pull request #109 from devos50/vol_buttons
Implemented support for volume up/down buttons
2024-01-01 21:01:44 +01:00
Martijn de Vos cde16baff0 Implemented support for volume up/down buttons
It's not doing much as the audio device is not working yet.
2024-01-01 20:59:53 +01:00
Martijn de Vos 38cebf5d0b Merge pull request #108 from ChaseKnowlden/enable-cocoa
Enable cocoa on Apple Silicon Macs
2024-01-01 10:37:27 +01:00
ChaseKnowlden c87aa5415a Enable cocoa on Apple Silicon Macs 2023-12-31 19:42:53 -05:00
Martijn de Vos a4e3aa4e1a Simplified running instructions 2023-12-31 15:10:26 +01:00
Martijn de Vos a29e52d425 Fixed for rebase on v8.2.0 branch 2023-12-31 15:08:53 +01:00
MrDiagnose 0d7c0f02c2 Update RUNNING.md
updated build instructions for linux & windows
2023-12-31 14:46:49 +01:00
Martijn de Vos b50ce88239 Update RUNNING.md 2023-12-31 14:46:49 +01:00
Martijn de Vos c592b20a82 Update README.md with a schematic 2023-12-31 14:46:49 +01:00
jvyden 2cee0001cf Fix NAND path check 2023-12-31 14:46:49 +01:00
jvyden 9ff58710dc Check if Bootrom/NOR/NAND files exist before attempting to use 2023-12-31 14:46:49 +01:00
Ryan 1efd4fac28 Include disablement of the stack protector in the configure script
This commit is intended to address the issue of stack smashing being detected when running QEMU-IOS on Microsoft Windows built with MINGW64, which causes the emulator to be unable to run.
2023-12-31 14:46:49 +01:00
Ryan 9a16d1b224 Switch position of "extern" and "G_NORETURN"
Please read https://www.mail-archive.com/qemu-devel@nongnu.org/msg950158.html, it fixes a bug in compilation on Microsoft Windows.
2023-12-31 14:46:47 +01:00
Ryan b623ef5316 Different file paths on Microsoft Windows 2023-12-31 14:45:45 +01:00
Martijn de Vos 62cae19be7 Deleted workflow from IT2G branch 2023-12-31 14:45:45 +01:00
breakgimme 19a9358da7 fix the configure command for apple silicon macs 2023-12-31 14:45:45 +01:00
Martijn de Vos 94d7dbdb57 Update README.md
Added image
2023-12-31 14:45:45 +01:00
Martijn de Vos 04a18b4691 Changed README.md 2023-12-31 14:45:45 +01:00
Martijn de Vos 52b9396b85 Added run instructions 2023-12-31 14:45:45 +01:00
Martijn de Vos 26fb52905a Made paths flexible 2023-12-31 14:45:44 +01:00
Martijn de Vos fbd054a4fc Removed print statement 2023-12-31 14:45:22 +01:00
Martijn de Vos 3f8c24809d Home/power buttons working 2023-12-31 14:45:22 +01:00
Martijn de Vos a914ee741e Prevent GUI grab 2023-12-31 14:45:21 +01:00
Martijn de Vos 287b4f33ba LCD + Multitouch working!! 2023-12-31 14:45:21 +01:00
Martijn de Vos 51aeda9707 Worked on TVOut 2023-12-31 14:45:21 +01:00
Martijn de Vos d9aae4f4af Started working on TVOut
This is blocking starting the display server
2023-12-31 14:45:20 +01:00
Martijn de Vos 7e6d4c7f01 Device is activated! 2023-12-31 14:44:57 +01:00
Martijn de Vos 6adb8f8d7c BCM4325 driver gets past initialization!! 2023-12-31 14:44:57 +01:00
Martijn de Vos 8ff7007198 More work on SDIO/BCM4325 2023-12-31 14:44:57 +01:00
Martijn de Vos eefd289876 Got SDIO IRQ working 2023-12-31 14:44:57 +01:00
Martijn de Vos 51865fe6dd Got a tiny bit further in the boot process 2023-12-31 14:44:57 +01:00
Martijn de Vos 9e0bc7a888 Made some progress with the BCM4325 2023-12-31 14:44:57 +01:00
zoe-vb 923144e37a Update ipod_touch_fmss.c
fix issue where boot arguments were not actually updating when recompiling qemu.
2023-12-31 14:44:56 +01:00
Martijn de Vos fd542ea941 Started working on SDIO 2023-12-31 14:44:56 +01:00
Martijn de Vos 97c5e54cae Booting up until lockdownd! 2023-12-31 14:44:36 +01:00
Martijn de Vos 5e9f53bfd8 Booting SpringBoard 2023-12-31 14:44:36 +01:00
Martijn de Vos 2c92a18892 Multiple fixes for SPI 2023-12-31 14:44:36 +01:00
Martijn de Vos 5a756faaa0 Several fixes and changes 2023-12-31 14:44:35 +01:00
Martijn de Vos fd56eb635b Got NOR up and running on both SPI0 and SPI1 2023-12-31 14:44:12 +01:00
Martijn de Vos 94c88d8121 Mikey is loading 2023-12-31 14:44:11 +01:00
Martijn de Vos c16e8a325a Got some more stuff running 2023-12-31 14:43:40 +01:00
Martijn de Vos e89d8fa26c Got more peripherals up and running! 2023-12-31 14:43:39 +01:00
Martijn de Vos 3011345486 Added workaround for device tree and several memory mappings 2023-12-31 14:43:19 +01:00
Martijn de Vos 97e57b6cbb Able to load kernel, with some hacks! 2023-12-31 14:43:19 +01:00
Martijn de Vos 1300c08302 Circumventing NAND 2023-12-31 14:43:18 +01:00
Martijn de Vos 1b255b1bd6 Implemented missing registers 2023-12-31 14:42:56 +01:00
Martijn de Vos 3cb4e8f8d0 Got the NAND working a bit more 2023-12-31 14:42:56 +01:00
Martijn de Vos d13b9ed88f Got some of the NAND initializations working 2023-12-31 14:42:56 +01:00
Martijn de Vos 3e98d59d18 Worked on several components 2023-12-31 14:42:55 +01:00
Martijn de Vos 0186293fa8 Some work on LCD/MIPI DSI 2023-12-31 14:42:33 +01:00
Martijn de Vos 03b479e6a3 Hardcoded iBoot decryption keys 2023-12-31 14:42:06 +01:00
Martijn de Vos 49c58b5f2e Working on iBoot 2023-12-31 14:42:06 +01:00
Martijn de Vos f36b77805e Fixed SPI again 2023-12-31 14:42:06 +01:00
Martijn de Vos b2d53838fd Worked on LLB 2023-12-31 14:42:05 +01:00
Martijn de Vos b52f4ffdf3 AES engine is correctly decrypting LLB payload 2023-12-31 14:40:15 +01:00
Martijn de Vos 5fb1e11d77 Fixed various things 2023-12-31 14:40:15 +01:00
Martijn de Vos b9529e0774 PKE engine seems to be operational 2023-12-31 14:40:14 +01:00
Martijn de Vos 2bb40f5dd2 Implemented a first version of the PKE engine 2023-12-31 14:40:13 +01:00
Martijn de Vos c7ce40a7bd NOR SPI seems to work 2023-12-31 14:39:34 +01:00
Martijn de Vos ee68a07899 S5L8720 SPI seems to be functional 2023-12-31 14:39:08 +01:00
Martijn de Vos d106e08273 Added NOR SPI peripheral 2023-12-31 14:39:06 +01:00
Martijn de Vos 5d280d6049 More work on iPod Touch 2G emulation 2023-12-31 14:38:19 +01:00
Martijn de Vos 96da27fe3b Started working on iPod Touch 2G 2023-12-31 14:35:48 +01:00
120 changed files with 12959 additions and 210 deletions

No files matched your search

-30
View File
@@ -1,30 +0,0 @@
# Configuration for Repo Lockdown - https://github.com/dessant/repo-lockdown
name: 'Repo Lockdown'
on:
pull_request_target:
types: opened
permissions:
pull-requests: write
jobs:
action:
runs-on: ubuntu-latest
steps:
- uses: dessant/repo-lockdown@v2
with:
pr-comment: |
Thank you for your interest in the QEMU project.
This repository is a read-only mirror of the project's repostories hosted
on https://gitlab.com/qemu-project/qemu.git.
The project does not process merge requests filed on GitHub.
QEMU welcomes contributions of code (either fixing bugs or adding new
functionality). However, we get a lot of patches, and so we have some
guidelines about contributing on the project website:
https://www.qemu.org/contribute/
lock-pr: true
close-pr: true
+2
View File
@@ -20,3 +20,5 @@ GTAGS
*.swp
*.patch
*.gcov
*.DS_Store
+20
View File
@@ -0,0 +1,20 @@
# QEMU-iOS
QEMU-iOS is an emulator for legacy Apple devices.
Currently, the iPod Touch 1G and iPod Touch 2G are supported.
<img width="331" alt="it2g-qemu" src="https://github.com/devos50/qemu-ios/assets/1707075/9bf7f6c1-5918-47e9-bb3e-2e39ae15d519">
The schematic below shows the most important hardware components of the iPod Touch 2G and their interactions.
The schematic for the iPod Touch 1G is mostly similar.
<img width="80%" alt="it2g-schematic" src="https://github.com/devos50/qemu-ios/assets/1707075/4b8eca9a-74b0-4590-ad23-bc056acde434">
### Running the iPod Touch 1G
Instructions on how to run the iPod Touch 1G emulator can be found [here](https://devos50.github.io/blog/2022/ipod-touch-qemu-pt2/).
A technical blog post with more information about the peripherals and reverse engineering process is published [here](https://devos50.github.io/blog/2022/ipod-touch-qemu/).
### Running the iPod Touch 2G
Instructions on how to run the iPod Touch 2G emulator can be found [here](https://github.com/devos50/qemu-ios/blob/ipod_touch_2g/RUNNING.md).
-171
View File
@@ -1,171 +0,0 @@
===========
QEMU README
===========
QEMU is a generic and open source machine & userspace emulator and
virtualizer.
QEMU is capable of emulating a complete machine in software without any
need for hardware virtualization support. By using dynamic translation,
it achieves very good performance. QEMU can also integrate with the Xen
and KVM hypervisors to provide emulated hardware while allowing the
hypervisor to manage the CPU. With hypervisor support, QEMU can achieve
near native performance for CPUs. When QEMU emulates CPUs directly it is
capable of running operating systems made for one machine (e.g. an ARMv7
board) on a different machine (e.g. an x86_64 PC board).
QEMU is also capable of providing userspace API virtualization for Linux
and BSD kernel interfaces. This allows binaries compiled against one
architecture ABI (e.g. the Linux PPC64 ABI) to be run on a host using a
different architecture ABI (e.g. the Linux x86_64 ABI). This does not
involve any hardware emulation, simply CPU and syscall emulation.
QEMU aims to fit into a variety of use cases. It can be invoked directly
by users wishing to have full control over its behaviour and settings.
It also aims to facilitate integration into higher level management
layers, by providing a stable command line interface and monitor API.
It is commonly invoked indirectly via the libvirt library when using
open source applications such as oVirt, OpenStack and virt-manager.
QEMU as a whole is released under the GNU General Public License,
version 2. For full licensing details, consult the LICENSE file.
Documentation
=============
Documentation can be found hosted online at
`<https://www.qemu.org/documentation/>`_. The documentation for the
current development version that is available at
`<https://www.qemu.org/docs/master/>`_ is generated from the ``docs/``
folder in the source tree, and is built by `Sphinx
<https://www.sphinx-doc.org/en/master/>`_.
Building
========
QEMU is multi-platform software intended to be buildable on all modern
Linux platforms, OS-X, Win32 (via the Mingw64 toolchain) and a variety
of other UNIX targets. The simple steps to build QEMU are:
.. code-block:: shell
mkdir build
cd build
../configure
make
Additional information can also be found online via the QEMU website:
* `<https://wiki.qemu.org/Hosts/Linux>`_
* `<https://wiki.qemu.org/Hosts/Mac>`_
* `<https://wiki.qemu.org/Hosts/W32>`_
Submitting patches
==================
The QEMU source code is maintained under the GIT version control system.
.. code-block:: shell
git clone https://gitlab.com/qemu-project/qemu.git
When submitting patches, one common approach is to use 'git
format-patch' and/or 'git send-email' to format & send the mail to the
qemu-devel@nongnu.org mailing list. All patches submitted must contain
a 'Signed-off-by' line from the author. Patches should follow the
guidelines set out in the `style section
<https://www.qemu.org/docs/master/devel/style.html>`_ of
the Developers Guide.
Additional information on submitting patches can be found online via
the QEMU website
* `<https://wiki.qemu.org/Contribute/SubmitAPatch>`_
* `<https://wiki.qemu.org/Contribute/TrivialPatches>`_
The QEMU website is also maintained under source control.
.. code-block:: shell
git clone https://gitlab.com/qemu-project/qemu-web.git
* `<https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/>`_
A 'git-publish' utility was created to make above process less
cumbersome, and is highly recommended for making regular contributions,
or even just for sending consecutive patch series revisions. It also
requires a working 'git send-email' setup, and by default doesn't
automate everything, so you may want to go through the above steps
manually for once.
For installation instructions, please go to
* `<https://github.com/stefanha/git-publish>`_
The workflow with 'git-publish' is:
.. code-block:: shell
$ git checkout master -b my-feature
$ # work on new commits, add your 'Signed-off-by' lines to each
$ git publish
Your patch series will be sent and tagged as my-feature-v1 if you need to refer
back to it in the future.
Sending v2:
.. code-block:: shell
$ git checkout my-feature # same topic branch
$ # making changes to the commits (using 'git rebase', for example)
$ git publish
Your patch series will be sent with 'v2' tag in the subject and the git tip
will be tagged as my-feature-v2.
Bug reporting
=============
The QEMU project uses GitLab issues to track bugs. Bugs
found when running code built from QEMU git or upstream released sources
should be reported via:
* `<https://gitlab.com/qemu-project/qemu/-/issues>`_
If using QEMU via an operating system vendor pre-built binary package, it
is preferable to report bugs to the vendor's own bug tracker first. If
the bug is also known to affect latest upstream code, it can also be
reported via GitLab.
For additional information on bug reporting consult:
* `<https://wiki.qemu.org/Contribute/ReportABug>`_
ChangeLog
=========
For version history and release notes, please visit
`<https://wiki.qemu.org/ChangeLog/>`_ or look at the git history for
more detailed information.
Contact
=======
The QEMU community can be contacted in a number of ways, with the two
main methods being email and IRC
* `<mailto:qemu-devel@nongnu.org>`_
* `<https://lists.nongnu.org/mailman/listinfo/qemu-devel>`_
* #qemu on irc.oftc.net
Information on additional methods of contacting the community can be
found online via the QEMU website:
* `<https://wiki.qemu.org/Contribute/StartHere>`_
+80
View File
@@ -0,0 +1,80 @@
## Running the iPod Touch 2G Emulator
This file contains the instructions on how to run the iPod Touch 2G emulator using QEMU.
Note that this is an experimental release and the functionality of the device is still limited.
Linux compatibility is currently unstable. Efforts are underway to improve it.
### Getting the Source Code
Clone the repository using the following commands:
```
git clone https://github.com/devos50/qemu-ios.git
cd qemu-ios
```
Now you can proceed with the rest of the instructions for building and running the emulator.
### Building QEMU
Make sure you have the required libraries installed to compile QEMU:
```
# On MacOS
brew install glib ninja pixman pkg-config sdl2
# On Linux (Ubuntu)
sudo apt install make ninja-build pkg-config libssl-dev libsdl2-dev libpixman-1-dev libpixman-1-0 libglib2.0-dev
# On Linux (Arch)
sudo pacman -S --needed make ninja pkgconf openssl sdl2 pixman glib2
# On Windows (MSYS2/mingw64)
pacman -S base-devel mingw-w64-x86_64-toolchain git python ninja mingw-w64-x86_64-glib2 mingw-w64-x86_64-pixman python-setuptools mingw-w64-x86_64-SDL2
```
Compile QEMU by running the following commands from the root directory:
```
mkdir build
cd build
# On Intel Macs
../configure --target-list=arm-softmmu --extra-cflags=-I/usr/local/opt/openssl@3/include --extra-ldflags='-L/usr/local/opt/openssl@3/lib -lcrypto'
# On Apple Silicon Macs
../configure --enable-sdl --target-list=arm-softmmu --disable-capstone --disable-pie --disable-slirp --extra-cflags=-I/opt/homebrew/opt/openssl@3/include --extra-ldflags='-L/opt/homebrew/opt/openssl@3/lib -lcrypto'
# On Linux
../configure --enable-sdl --disable-cocoa --target-list=arm-softmmu --disable-capstone --disable-slirp --extra-cflags=-I/usr/include/openssl --extra-ldflags='-lcrypto' --disable-werror --enable-pie
# On Microsoft Windows (with MINGW64)
../configure --enable-sdl --disable-cocoa --target-list=arm-softmmu --disable-capstone --disable-slirp --disable-pie --extra-cflags=-I/mingw64/include/openssl --extra-ldflags='-L/mingw64/lib -lcrypto' --disable-stack-protector --disable-werror
make
```
Note that we’re explicitly enabling compilation of the SDL library which is used for interaction with the emulator (e.g., capturing keyboard and mouse events). Also, we only configure and build the ARM emulator.
We are also linking against OpenSSL as the AES/SHA1/PKE engines use some of the library’s cryptographic functions.
Remember to update the include and library paths to the OpenSSL library in case they are located elsewhere.
You can speed up the `make` command by passing the number of available CPU cores with the `-j` flag, e.g., use `make -j6` to compile using six CPU cores.
The compilation process should produce the `qemu-system-arm` binary in the `build/arm-softmmu` directory.
### Downloading the Required Files
We need a few files to successfully boot the iPod Touch emulator to the home screen, which I published [here](https://github.com/devos50/qemu-ios/releases/tag/n72ap_v1) for convenience. You can download all these files from here, and they include the following:
- The S5L8720 bootrom binary, which is started as the very first thing when booting.
- A NOR image that contains various auxillary files used by the bootloader. I provide some instructions on how to generate this image yourself [here](https://github.com/devos50/qemu-ios-generate-nor).
- A NAND image that contains the root file system. I provide some instructions on how to generate this image yourself [here](https://github.com/devos50/qemu-ios-generate-nand).
Download all the required files and save them to a convenient location. You should unzip the `nand_n72ap.zip` file, which contains a single directory named `nand`.
### Running the Emulator
We are now ready to run the emulator from the build directory with the following command:
```
./arm-softmmu/qemu-system-arm -M iPod-Touch,bootrom=<path to bootrom>,nand=<path to NAND directory>,nor=<path to NOR directory> -serial mon:stdio -cpu max -m 2G -d unimp
```
If there are any issues running the above commands, please let me know by [opening an issue](https://github.com/devos50/qemu-ios/issues/new).
+1
View File
@@ -44,3 +44,4 @@ CONFIG_ARM_VIRT=y
# CONFIG_FSL_IMX7=n
# CONFIG_FSL_IMX6UL=n
# CONFIG_ALLWINNER_H3=n
CONFIG_IPOD_TOUCH_2G=y
+24
View File
@@ -0,0 +1,24 @@
__pycache__/
*.pyc
/agent/emuagent
/agent/overlay/
/netd/ipod-netd
/gen-v1/generate_nand_v1
/apps/HelloEmu/HelloEmu.app/HelloEmu
/assets/
/images/
/ipsw/
/run/
/work/
/fs/
/fw-*/
/*.img
/keys-*.txt
/dmg2img/
/xpwn/
/qemu-ios-generate-nand/
/qemu-ios-generate-nor/
!/patches/
!/patches/*.patch
/apps/TouchProbe/TouchProbe.app/
+271
View File
@@ -0,0 +1,271 @@
# Passation — simulateur iPod Touch 2G / iPhone OS 2.2.1
État au 2026-10-01. Environnement : conteneur Arch Linux, utilisateur `dev` avec sudo, sans `CAP_NET_ADMIN`, sans `/dev/ppp` ni TUN.
## Dernière mise à jour — RTC D1759
Le modèle `d1759` remplace `pcf50633` sur la machine 2G. Il corrige le
pointeur I²C et fournit le compteur LE32 en `0x5c` avec décalage Unix LE32 en
`0x64`. Les notes anciennes attribuant un accès indirect RTC à `0x61/0x62`
étaient incorrectes : ces registres concernent le démarrage.
`start -W` et `wait` ne règlent plus l'heure via l'agent.
Voir [RTC.md](RTC.md) pour le protocole, les preuves et les limites.
Le raccordement GPIO est aussi borné aux cinq IRQ définies, pour éviter
l'accès hors limites révélé à la recompilation.
## Validation du 1er octobre 2026 — iPhone OS 2.1.1
- Image distincte `5F138-validation-20261001`, créée depuis la base 5F138 conservée,
avec agent v12, TouchProbe et HelloEmu. Session : `work/validation-20261001/`.
`system-version.plist` confirme 2.1.1 / 5F138 ; démarrage déverrouillé en 28 s.
- Calibration : sept points exacts de `(20,40)` à `(300,460)` dans
`calibration-complete.log`, après attente du marqueur `TouchProbe ready`.
Les ordonnées du journal sont relatives à la vue, située à y=20.
- Six cycles alternant Power/Home et Power/Power passent, sans perte du tactile.
- Safari : clavier virtuel QWERTY, formulaire envoyé avec `texte=bonjour`,
défilement jusqu'au lien final et requête `/fin` reçue par le serveur HTTP local.
Journal : `http/requests.jsonl`. Une version réutilisable de la page et du serveur
est fournie dans `tests/http-fixture.py` ; aucun site extérieur n'est requis.
- Calculatrice : `7 × 8 = 56`, capture `calculatrice-56.png`.
- Notes : note « Bonjour » créée par le clavier virtuel puis sauvegardée.
Les lignes de `Note` et `note_bodies` sont identiques dans `notes-avant.db` et
`notes-apres.db`, et la note réapparaît dans l'interface après redémarrage.
- Nouvelle app `HelloEmu21.app` (bundle-id `com.ioslegacy.hello21`) installée via
`push-app`, cache reconstruit, redémarrage sans réinitialiser la NAND en 50 s,
lancement au premier plan et journal HelloEmu validés. Captures :
`hello21-apres-push.png` et `note-apres-reboot.png`.
- Synthèse locale : `validation.json`. Session et serveur HTTP de test arrêtés.
Aucun défaut `Killing device`, `Fatal Exception` ou `MACH Reboot` relevé dans
la console finale. Les images et NAND d'origine sont conservées.
- Particularités : l'image 5F138 conserve ses réglages de langue et de fuseau
d'origine ; les captures sont en anglais. Le lancement `--url` d'un Safari
déjà ouvert n'a pas changé la page pendant le test : passer par les liens ou
relancer le navigateur. Le RTC est depuis implémenté (voir ci-dessus) ; la veille profonde reste à faire.
## Historique — fiabilisation GPIO/SPI et agent v12
Les sections de diagnostic plus anciennes ci-dessous décrivent les étapes précédentes.
Le workflow `push-app` puis déverrouillage/lancement a été validé avec l'agent v11.
Cette suite corrige les débordements GPIO/SYSIC, les commandes tactiles invalides,
les trames instables et les transactions SPI abandonnées à la reprise de l'écran.
Huit cycles veille/réveil alternant Home et Power ont passé sur 5H11a sans perte
du tactile. La calibration TouchProbe retrouve les coordonnées écran sur sept
points ; la vitesse verticale et le calcul signé des vitesses sont corrigés.
L'agent v12 conserve le protocole et transfère par blocs de 64 Kio avec fichiers
temporaires distincts. `ipod-test-app` prépare une NAND indépendante, vérifie le
premier plan, récupère journal et capture, puis arrête sa propre session.
Voir le README pour les commandes et les tests reproductibles ASan/UBSan.
Validation finale : huit régressions matérielles avec ASan/UBSan et sept tests
sur les transferts ; HelloEmu réussit le workflow automatique, l'application
absente échoue avec capture et arrêt de la VM. Un fichier de 8 460 608 octets
fait l'aller-retour via l'agent v12 avec SHA-256 identique. Trois cycles de
veille/réveil supplémentaires avec la calibration finale et l'agent v12 passent,
puis HelloEmu se lance au premier plan avec son journal. Diagnostics locaux :
`work/fiabilisation-20260930/` (dont `test-hello`, `test-echec`, `hello-final.png`).
Les sessions de validation sont arrêtées.
Les fichiers générés, images et diagnostics restent hors du dépôt. Les images
5H11a et les NAND d'origine sont conservées ; le RTC, le Wi-Fi matériel, l'audio
et la veille profonde restent à implémenter. Les nouveaux correctifs n’étaient
pas encore validés sur 5F138 à cette étape.
## Objectif
On fournit un simulateur d'**iPod Touch 2G sous iPhone OS 2.2.1** qui sert surtout à un agent IA pour tester les applications iOS 2.x d'AlloVoisins. Les apps sont compilées en armv6 avec `/opt/cctools` et `/opt/theos/sdks/iPhoneOS2.2.1.sdk`. L'agent doit pouvoir faire les choses suivantes, sans intervention humaine :
- installer une app ;
- la lancer ;
- lire ses logs ;
- toucher l'écran et prendre des captures ;
- utiliser le réseau.
On veut le comportement le plus fidèle possible à un vrai appareil. La consigne de l'utilisateur est de travailler en autonomie et de se créer ses propres outils, voire une skill.
## Ce qui marche
- **Boot 2.2.1 (5H11a)**, SpringBoard prêt en environ 25 s.
- **2.1.1 (5F138)** marche aussi, avec l'ancienne image de devos50.
- **Écran** : capture 320×480 par QMP. Touch, boutons et VNC sur `127.0.0.1:5900`.
- **Réseau / Internet** : PPP sur l'UART1 émulée, relié à `ipod-netd` côté hôte (libslirp, en userland).
- iPod en 10.0.2.15, passerelle 10.0.2.2, DNS 10.0.2.3.
- Le DNS et HTTP marchent depuis l'iPod, et Safari affiche example.com.
- **Agent sur l'iPod** (`emuagent`, v10), joignable depuis l'hôte sur `127.0.0.1:7788` via `ipod-agent`. Commandes :
- `launch` (bundle id, avec `--log` pour stdout/stderr et `--url`), `apps`, `front` ;
- `exec`, `ps`, `kill`, `respring` ;
- `put`, `get`, `ls`, `rm`, `mkdir` ;
- `fetch`, `net`, `routes`, `sc` (SCDynamicStore) ;
- `settime`, `sync`.
- **Persistance NAND** : programmation des pages et métadonnées, effacement des blocs de 128 pages et détection des
pages vierges fonctionnent avec le format VSVFL de production. La base 5H11a a été migrée ; `nand-write=on` est
inscrit dans ses options machine. Les nouveaux `ipod-build-fw` / `ipod-mkimage` utilisent `gen-v1/mknand`.
- **Apps** : `ipod-mkimage MonApp.app|.ipa|.deb|dossier/` construit une image dérivée. Les apps signées avec `ldid` passent grâce au boot-arg `amfi_allow_any_signature`.
- **Heure** : `start -W` / `wait` envoie `settime` dès que l'agent répond, avant la fin du démarrage de SpringBoard.
Ce réglage précoce évite d'initialiser les timers de l'interface en 1970 avec des préférences datées dans le futur.
- **Veille** : agent v10, propriété `SleepDisabled=true` sur `IOPMrootDomain` (acceptée). Les assertions publiques
`IOPMAssertionCreateWithName` sont non supportées sur ce firmware. Le SoC ne sait toujours pas reprendre une veille.
- **Réglages de l'image** : langue fr, région fr_FR, fuseau Europe/Paris. Verrouillage auto désactivé, pas d'alerte « Edit Home Screen ».
- **Reconstruction complète** depuis l'IPSW Apple : `ipod-build-fw 5H11a`.
## Limites connues, et suite logique par priorité
1. **Installation / lancement dans une session conservée** : les apps copiées dans `/Applications` sont maintenant
persistées, mais la reconstruction du catalogue lors d'un `respring` reste peu fiable. `push-app` synchronise puis
redémarre l'émulateur **sans `-f` et sans reconstruire l'image**. Le lancement après ce redémarrage répond encore
parfois `device locked` ; un écran de code a été observé dans l'image de test. Ne pas annoncer ce workflow comme
entièrement validé. `ipod-mkimage` reste disponible pour intégrer l'app avant le premier démarrage.
- La persistance elle-même est validée : témoin texte et binaire de 256 Kio identiques après redémarrage ; fichier
aléatoire de 8 Mio conservé, SHA-1 calculé dans l'iPod identique à l'hôte :
`4d322e06f3433b874bc77d8b0298d97346210eb5`.
- Le téléchargement de 8 Mio par `get` a été tronqué. Le client vérifie désormais la longueur reçue et signale
ce cas. Utiliser un calcul d'empreinte dans l'iPod pour distinguer le transport de l'intégrité NAND.
- Ancienne base sauvegardée : `images/5H11a/nand-readonly-backup`, `rootfs-v8-backup.img`,
`machine-opts-readonly-backup`. Session d'origine : `run/nand-before-persistence` et `fw-before-persistence`.
Session de test avec app et fichier de 8 Mio : `run/nand-persistence-verified` (firmware `persistence-test-v10`).
2. **Horloge RTC du PMU** : compteur et décalage D1759 implémentés ; IRQ d'alarme et persistance du décalage entre instances QEMU restent absentes. Voir la dernière mise à jour et `RTC.md`.
3. **Réglages > Wi-Fi** affiche « Pas de Wi-Fi », parce que la puce BCM4325 n'est pas émulée. Le réseau passe par une interface ppp0, que l'app peut voir comme un VPN ou une connexion transitoire. Émuler le WiFi pour de vrai (SDIO + firmware FullMAC) serait un gros chantier ; le pont slirp est réutilisable.
4. **Pas encore écrits** :
- un script `ipod-test-app` (construit l'image, boote, lance l'app avec `--log`, fait une capture, affiche les logs) ;
- une **skill** Claude/Codex qui documente ce workflow ;
- la mise à jour de `~/ipod-emu/README.md` (réseau, agent, heure, français).
5. **Mise à jour de l'agent à chaud** (`put` puis `kill emuagent`) : elle coupe PPP pendant la renégociation. Préférer reconstruire l'image.
## Démarrer
```bash
ipod-emu start -f -W # 2.2.1 (défaut), NAND vierge, attend SpringBoard
ipod-agent ping # l'agent répond quand PPP est monté (quelques secondes)
ipod-agent launch com.apple.mobilesafari --url http://example.com/
ipod-qmp screenshot /tmp/s.png # tap X Y | swipe | home | hmp "info registers"
ipod-emu stop
```
Ne **jamais** toucher l'écran avant la fin de `ipod-emu wait` ou `start -W` : le contrôleur tactile fait planter QEMU s'il est touché pendant son démarrage.
Logs :
- `~/ipod-emu/run/serial.log` : console kernel et syslog, pppd, emuagent ;
- `~/ipod-emu/run/qemu.log` ;
- `~/ipod-emu/run/netd.log` : avec `IPOD_NETD_FWD=-v`, trace de chaque paquet IP.
Debug : `IPOD_QEMU_EXTRA="-d unimp,guest_errors -D ~/ipod-emu/run/int.log"` ; `IPOD_QEMU=~/ipod-emu/bin/qemu-gdb` pour avoir une backtrace en cas de crash.
## Carte des fichiers
| Où | Quoi |
|---|---|
| `~/qemu-ios` | Fork Mahtan/devos50 (branche ipod_touch_2g). Sources et outils sauvegardés dans le dépôt (outils sous `contrib/ipod-emu`). Build : `cd build && make -j4` |
| `/opt/ios-legacy/bin/` | Copies installées des outils (réinstaller avec `sudo install -m 755 ~/ipod-emu/bin/X /opt/ios-legacy/bin/`) |
| `~/ipod-emu/bin/ipod-emu` | Launcher : QEMU (serial0 = fichier, serial1 = `run/uart1.sock`), ipod-netd, wait, settime |
| `~/ipod-emu/bin/ipod-qmp` | Écran, touch, boutons, HMP |
| `~/ipod-emu/bin/ipod-agent` | Client de l'agent (protocole : ligne à champs séparés par TAB, réponse `OK <n> [info]\n` + n octets) |
| `~/ipod-emu/bin/ipod-mkimage` | Image dérivée avec apps / .deb / overlay (re-signe avec `ldid` en **gardant les entitlements**) |
| `~/ipod-emu/bin/ipod-build-fw` | IPSW → NOR + rootfs (`mkbase-rootfs`) + NAND (`gen-v1/mknand`, VSVFL de production, signature 2 et écritures activées) |
| `~/ipod-emu/bin/mkbase-rootfs` | LaunchDaemons réduits, fstab rw, Lockdown (`assets/`), prefs, fr/Paris, **intègre `agent/overlay`** |
| `~/ipod-emu/agent/` | `emuagent.c` + `Makefile` (`make` → `overlay/`), `entitlements.plist` (`com.apple.springboard.launchapplications` etc., **indispensables** pour `SBSLaunchApplication`) |
| `~/ipod-emu/netd/ipod-netd.c` | Serveur PPP (HDLC, LCP, IPCP) ↔ libslirp. `gcc -O2 -o ipod-netd ipod-netd.c $(pkg-config --cflags --libs slirp) -Wno-deprecated-declarations` |
| `~/ipod-emu/bin/armdis.py`, `dtdump.py` | Désassembleur capstone (base, début, fin, `arm`) et dump du DeviceTree. Kexts du kernelcache : base de fichier `0xc0029000` |
| `~/ipod-emu/fw-5H11a/dec/` | kernelcache, iBoot, LLB, DeviceTree déchiffrés (clés via api.ipsw.me) |
| `~/ipod-emu/xpwn/` | Outil `hfsplus` (patché : `grow` alloue ensuite dans l'espace neuf) |
## Découvertes clés (modifs QEMU)
- **AES** (`ipod_touch_aes.c`) :
- clé GID choisie d'après le contenu du KBAG (`ipod_touch_gid_keys.h`) ;
- le dernier bloc incomplet d'une IMG3 est laissé en clair ;
- la vérification de longueur LZSS d'iBoot est patchée, trouvée par signature ;
- dans le DeviceTree déchiffré, les propriétés DMA de `uart1` sont renommées : le pilote reste en PIO.
- **SHA1** (`ipod_touch_sha1.c`) : c'est une fonction de compression brute.
- `CONFIG 0x2` = premier bloc, `0x8` puis `0xa` = suite ;
- état H0–H4 lisible et écrivable, mots byte-swappés ;
- bit `0x4` = mode asynchrone, avec IRQ 40 (activée par `0x0c`, acquittée par `0x08`, levée par un timer de 20 µs).
- **UART** (`ipod_touch_uart.c`, nouveau modèle, remplace l'Exynos) :
- IRQ activées dans UCON (`0x2000` TX, `0x1000`/`0x0880` RX) ;
- flags en attente dans UTRSTAT (`0x08`/`0x10`/`0x20`), effacés en écrivant 1 ;
- `UFSTAT` : un FIFO plein vaut 0 + le bit `0x100` ;
- FIFO limité à 8 octets.
- **NAND / FMSS** (`ipod_touch_fmss.c`) :
- La programmation `CSGENRC=0xa02` utilise une liste de paires en `D10`, terminée par un mot zéro.
`D08=8` est la taille d'une paire, pas le numéro de page ; `D18` est une ancienne valeur de lecture.
- Premier mot : masque CE dans les bits 0–3, commande 80h/81h dans les bits 16–23 et flags dans les autres bits.
Deuxième mot : page physique. `D20` pointe deux adresses de données (2048 octets chacune) par page ; `D1C`
contient les métadonnées de 12 octets. Toutes les paires doivent être enregistrées.
- Effacement reconnu dans la liste du séquenceur `C04` par les écritures immédiates NAND 60h/D0h ; `D0C` pointe
la page de début de bloc, `D10` un **index** CE, `D18=1`. Supprimer les 128 fichiers de pages du bloc.
- Une page sans fichier est vierge (`FF`). `C30=0x20000000` pour une lecture entièrement vierge fait retourner
`0xe00002e5` par le pilote ; sans ce statut, `_FTLRestore` ignore les blocs de logs et perd les écritures.
- Les autres micro-commandes restent émulées par les chemins de haut niveau existants ; pas d'interpréteur complet.
- `.ipod-nand-format=vsvfl-production-v1` dans la NAND : le launcher refuse `nand-write=on` sur une ancienne image.
- **Écran MIPI DSI** : `CLKCTRL[31]` contrôle `STATUS.TxReadyHsClk` ; le bit doit aussi pouvoir retomber à zéro.
Le modèle précédent le renvoyait toujours actif, bloquant le pilote lors de l'arrêt de l'écran.
- **Machine** :
- stubs RAM MPVD (`0x39600000`) et `0x38100000` ;
- la NAND 2.2.1 exige la signature `0x43313132` ;
- options `legacy-nor` et `nand-write`.
- **Réseau iOS** :
- pppd est lancé avec `serviceid emuppp` ;
- l'agent publie `Setup:/Network/Service/emuppp` et `Setup:/Network/Global/IPv4 ServiceOrder`, sans quoi IPMonitor n'installe ni route ni DNS ;
- mDNSResponder est réactivé (nécessaire au DNS) ;
- tous les descripteurs passés à pppd doivent être `FD_CLOEXEC` ;
- SpringBoard refuse `SBSLaunchApplication` sans l'entitlement `launchapplications`.
## Pièges
- `pkill -f 'ipod-netd -s'` tue aussi le shell qui lance la commande : utiliser `pkill -x ipod-netd`.
- `ipod-build-fw` réécrit `images/5H11a/`. Refaire `make` dans `agent/` avant, si l'agent a changé.
- `-f` efface la session persistée. Ne pas l'utiliser pour vérifier la conservation d'un fichier.
- QEMU et netd sont lancés avec `setsid` pour survivre à la fin du terminal appelant. Le launcher suit netd par PID.
- Un `.deb` 2.x qui dépend d'un jailbreak (bash, etc.) ne marchera pas : aucun shell dans l'image.
## Reprise du 30 septembre 2026 — après déplacement des homes
- Projets retrouvés sous `/srv/homes/iphoneos2/{ipod-emu,qemu-ios}`.
- Bibliothèques QEMU/libslirp et ImageMagick réinstallées dans le conteneur courant ;
lien BootROM 5H11a corrigé en relatif ; commandes accessibles par wrappers dans
`/home/dev/.local/bin`. Ces installations locales ne modifient pas l'image Docker.
- Lanceur adapté aux chemins déplacés, sélection automatique des keymaps via `-L`,
contrôle du démarrage QEMU et du relais, nettoyage du relais même après arrêt QEMU.
`ipod-qmp` prend en compte `IPOD_EMU_HOME`.
- Test sur une copie de NAND dans `ipod-emu/work/reprise-20260930` : boot de l'image
de base en 28 s, Safari et HTTP validés. `push-app HelloEmu.app` copie les deux
fichiers, redémarre sans réinitialiser la NAND ; HelloEmu apparaît ensuite dans
le catalogue `apps`. Le lancement répond toujours `device locked`, écran noir.
Le programme de diagnostic `unlock` retourne 0 sans débloquer le lancement.
Ne pas considérer le lancement après installation comme validé.
- La copie de la session initiale est conservée dans
`work/reprise-20260930/run-session-initiale` ; la session d'origine `run/nand`
n'a pas été modifiée. Les captures et préférences de diagnostic sont dans le
dossier de reprise. Les sessions de test ont été arrêtées.
- Un ancien relais survivant à QEMU occupait 7788 : supprimé avant de refaire le
test. L'ancien code continuait malgré l'échec de redirection ; le nouveau échoue.
- Vérifications : syntaxe Bash/Python, compilation du relais avec `-Wall`,
échec QEMU volontaire détecté immédiatement, port 7788 occupé détecté et QEMU
arrêté, `stop` après échec. Aucun changement des modèles matériels dans cette reprise.
## Correction du verrouillage — agent v11
- `SBGetScreenLockStatus` confirme `locked=1 passcode=0` après redémarrage.
`SBApplicationRequestedDeviceUnlock` réveille un clavier de code sans déverrouiller.
- Solution validée : `SBDimScreen(port, false)`, attendre deux secondes, glisser
`(60,450)` → `(319,450)`, attendre deux secondes, vérifier `locked=0`.
Le geste historique `(40,430)` → `(280,430)` n'atteint pas correctement le bord
de la surface tactile émulée. Home/Power et gestes lors de la reprise du pilote
peuvent encore provoquer un arrêt QEMU ; aucun correctif matériel n'est revendiqué.
- Agent v11 : commandes `screen` et `wake`. Client : `unlock`, vérification des
marqueurs de disponibilité, refus si un code est actif, trois essais maximum.
`start -W` / `wait` ne rendent la main avec succès qu'après déverrouillage.
- Validations sans débogueur sur la session copiée : démarrage en 36 s ; nouvelle
installation de HelloEmu avec `push-app`, redémarrage conservant la NAND en 47 s,
`launch` réussi et `front` = `com.allovoisins.helloemu`. Capture :
`work/reprise-20260930/hello-v11-apres-push.png`.
- L'agent v11 a été installé atomiquement dans la session de test puis synchronisé
avant redémarrage ; aucune relance à chaud de l'agent/PPP.
Nouvelle image `images/5H11a-agent-v11` construite avec l'overlay de l'agent,
sans modifier l'image 5H11a ni la NAND d'origine. L'image de base v10 doit être
mise à jour avant d'utiliser le déverrouillage automatique.
- La faute `diff_y` utilisant `x` dans `get_frame` reste à étudier séparément ;
elle n'a pas été modifiée pour cette validation.
## Relecture avant merge
- Corrigé `start -W` sur une session déjà lancée : attendre et vérifier le
déverrouillage avant de rendre la main, au lieu de retourner immédiatement.
- Image distincte 5H11a-agent-v11 démarrée et déverrouillée en 27 s ; agent v11
confirmé. Les deux sessions de test antérieures sont conservées séparément.
+236
View File
@@ -0,0 +1,236 @@
# Simulateur iPod Touch 2G (iPhone OS 2.2.1)
Émulateur [qemu-ios](https://git.mahtan-melwasul.com/Mahtan/qemu-ios) (branche iPod Touch 2G, N72AP), adapté pour
démarrer **iPhone OS 2.2.1 (5H11a)**. iPhone OS 2.1.1 (5F138) reste disponible. Les outils sont dans `ipod-emu/bin` ; dans le workbench actuel, des wrappers les exposent via `/home/dev/.local/bin`.
## Environnement déplacé
Dans le workbench actuel, les projets sont sous `/srv/homes/iphoneos2` :
`ipod-emu` contient les images et les outils, `qemu-ios` contient QEMU.
Les outils utilisés directement depuis `ipod-emu/bin` retrouvent ce dossier ;
le lanceur cherche QEMU dans le dossier voisin et transmet son dossier `pc-bios`
pour retrouver les keymaps VNC après déplacement. `IPOD_EMU_HOME`, `IPOD_QEMU`,
`IPOD_QEMU_DATA` et `IPOD_QMP` permettent de choisir d'autres chemins.
`ipod-qmp` respecte également `IPOD_EMU_HOME` pour le socket de session.
Vérifier les liens symboliques des images après déplacement : le lien de
`images/5H11a/bootrom` doit viser `../5F138/bootrom`, plutôt qu'un ancien home absolu.
Sur Arch, le binaire construit dans cet environnement utilise notamment `pixman`,
`libpng`, `libjpeg-turbo`, `sdl2` et `libslirp` ; les captures PNG utilisent
`imagemagick`. Compiler le relais réseau demande aussi `pkgconf` et `glib2-devel`.
Ces dépendances doivent être présentes dans chaque nouveau conteneur.
Le lanceur échoue explicitement si QEMU ou le relais réseau s'arrête au démarrage.
Un port agent déjà occupé est une erreur : le relais ne poursuit plus sans sa
redirection. `stop` nettoie le relais suivi par PID même si QEMU est déjà arrêté.
## Utilisation
```bash
ipod-emu start -w 5H11a-agent-v12 -W # attend SpringBoard et déverrouille (~25–50 s)
# Ajouter -f uniquement pour remettre la NAND à zéro.
ipod-qmp screenshot ecran.png # capture 320x480
ipod-qmp tap 160 326 # touche (coordonnées écran), ipod-qmp tap X Y 1.5 = appui long
ipod-qmp swipe 300 250 20 250 # glisser (ex. page suivante de SpringBoard)
ipod-qmp home | power # boutons
ipod-emu log # console du kernel + syslog iOS
ipod-emu stop
```
- Attendre `ipod-emu wait` / `start -W` avant les gestes : le pilote tactile invité doit avoir terminé son démarrage.
- VNC sur `127.0.0.1:5900` pour interagir à la main (tunnel SSH : `ssh -L 5900:127.0.0.1:5900 …`).
- Le lancement d'une app prend 10-20 s.
## Déverrouillage après redémarrage
Avec l'agent **v11 ou ultérieur**, `start -W` et `wait` vérifient le verrouillage, réveillent
l'écran et effectuent le glissement normal avant de rendre la main.
`ipod-agent screen` retourne `locked=0 passcode=0` une fois déverrouillé ;
`ipod-agent unlock` permet de refaire ce parcours après démarrage.
Si un code est activé, le client s'arrête et demande un déverrouillage manuel.
Aucun geste n'est envoyé avant les marqueurs de démarrage du pilote tactile.
Le réveil utilise `SBDimScreen(port, false)`, puis un glissement de `(60,450)`
à `(319,450)` adapté à la surface tactile du modèle. Le client laisse deux
secondes au réveil et à la fin du geste, puis vérifie le verrouillage ; trois
essais maximum. `SBApplicationRequestedDeviceUnlock` affiche le clavier de code
sur 2.2.1 même sans code actif : ce n'est pas le parcours utilisé.
Une image contenant l'agent v10 doit être mise à jour avant d'utiliser ce workflow.
Créer une image distincte préserve l'image de base :
```bash
cd /srv/homes/iphoneos2/ipod-emu/agent
PATH=/opt/cctools/bin:$PATH make
ipod-mkimage -o 5H11a-agent-v11 overlay
```
Le firmware se choisit avec `start -w 5H11a-agent-v11 -W`. Attention : comme pour
tout changement de firmware, cette commande remplace la NAND du dossier `run` ;
conserver sa session précédente ou utiliser un environnement de test séparé.
Le dossier de diagnostic `work/reprise-20260930` conserve une session avec l'agent
v11 et HelloEmu, indépendante de la session d'origine.
## Tester nos apps
```bash
ipod-mkimage -b 5H11a-agent-v11 MonApp.app autre.ipa paquet.deb # -> images/5H11a-agent-v11-apps
ipod-emu start -f -W -w 5H11a-agent-v11-apps
ipod-agent launch com.exemple.monapp --log /private/var/mobile/monapp.log
ipod-agent get /private/var/mobile/monapp.log
# Installation sans reconstruire l'image (synchronise puis redémarre l'iPod) :
ipod-agent push-app MonApp.app
```
Les `.app`/`.ipa` vont dans `/Applications` (re-signés avec `ldid -S` ; le kernel les accepte grâce au boot-arg
`amfi_allow_any_signature` injecté par l'émulateur), les `.deb` sont décompressés à la racine. Compiler en armv6 avec
la toolchain de l'environnement (`arm-apple-darwin9-gcc -isysroot /opt/theos/sdks/iPhoneOS2.2.1.sdk`), voir
`apps/HelloEmu`. Environ 230 Mo libres dans l'image 2.2.1.
## Test automatique et diagnostics
```bash
# Construire d'abord l'agent v12 et une image dédiée depuis la base inchangée :
PATH=/opt/cctools/bin:$PATH make -C agent
ipod-mkimage -b 5H11a -o 5H11a-agent-v12 agent/overlay
# Après avoir arrêté sa session, tester dans une NAND indépendante :
ipod-test-app MonApp.app
ipod-test-app MonApp.ipa --output /chemin/nouveau-dossier
ipod-test-app paquet.deb --bundle-id com.exemple.monapp
```
Les chemins `agent/` ci-dessus sont relatifs au dossier des outils ; dans le dépôt,
utiliser `contrib/ipod-emu/agent`. Choisir un nom d'image absent lors de sa création.
`--base` permet de choisir une autre image contenant l'agent v11 ou ultérieur.
Le bundle-id est lu dans le plist des `.app` et `.ipa` ; il doit être fourni pour
les `.deb` et overlays. Le runner refuse de démarrer si les ports VNC ou agent
sont déjà occupés. Chaque test crée une image et une session dans son propre
répertoire ; la NAND de `run/` et l'image de base restent conservées.
Le code de sortie vaut zéro uniquement si l'app apparaît puis reste au premier
plan pendant `--settle` secondes (3 par défaut), si les diagnostics sont collectés
et si la VM est arrêtée. Ce contrôle ne remplace pas les tests fonctionnels de l'app.
Les résultats vont dans `work/tests/<date>-<identifiant>/`, ou dans un nouveau dossier
`--output` : `result.json`, `workflow.log`, `app.log`, `screen.png` et les logs QEMU,
série et réseau dans `session/run/`. En cas d'échec, les diagnostics disponibles
sont conservés et la session de test est arrêtée.
## Tactile et transferts
Home/Power ne doivent plus écrire hors du tableau GPIO : le port C est inclus.
Les trous des registres SYSIC ne débordent plus dans le groupe voisin.
Le tactile traite séparément les requêtes de longueur et de données, accepte
EA/EB, conserve une trame stable, et abandonne proprement une commande lors de
la réinitialisation SPI. Les commandes et rapports inconnus ne font plus arrêter
QEMU. Le découpage des requêtes est documenté également dans
[OpeniBoot Z2](https://github.com/iDroid-Project/openiBoot/blob/master/plat-s5l8900/multitouch-z2.c).
La vitesse verticale utilise `y`, les vitesses conservent leur signe et sont
bornées à 16 bits. `apps/TouchProbe` permet de mesurer la calibration : compiler
avec son Makefile, lancer avec `--log`, puis comparer les gestes QMP au marqueur
bleu et aux coordonnées du journal (ajouter 20 pixels à l'ordonnée locale de la vue).
La calibration a été vérifiée sur 5H11a et 5F138, sur sept points de `(20,40)` à `(300,460)`.
Agent **v12** : `put` et `get` utilisent des blocs de 64 Kio au lieu d'allouer
le fichier entier dans l'iPod. Un upload incomplet supprime son temporaire et
conserve le fichier précédent ; le client fait de même pour les téléchargements.
Le protocole reste compatible avec l'agent v11. Les téléchargements vers stdout
peuvent naturellement afficher un préfixe avant une erreur de transport.
Depuis le dépôt, les tests sans firmware sont :
```bash
contrib/ipod-emu/tests/run-rtc # protocole I²C et horloge D1759, ASan/UBSan
contrib/ipod-emu/tests/run-multitouch # build QEMU arm existant, Ninja, compilateur avec ASan/UBSan
python3 contrib/ipod-emu/tests/test-transfers.py # Python et compilateur C, sockets locales
```
## Validation sous iPhone OS 2.1.1
Le firmware 5F138 a été vérifié avec l'agent v12 et les mêmes corrections QEMU.
Conserver l'image 5F138 d'origine et produire une image dérivée avec un nom absent :
```bash
ipod-mkimage -b 5F138 -o 5F138-agent-v12 agent/overlay apps/TouchProbe/TouchProbe.app apps/HelloEmu/HelloEmu.app
```
Utiliser ensuite un environnement `IPOD_EMU_HOME` indépendant pour démarrer cette
image ; un changement de firmware remplace la NAND de la session choisie.
`SystemVersion.plist` confirme iPhone OS 2.1.1 / 5F138. Les sept points de
calibration sont exacts et six cycles de veille/réveil alternant Home et Power
passent, sans désactivation du tactile.
Pour tester Safari sans dépendre d'un site extérieur, depuis le dépôt :
```bash
python3 contrib/ipod-emu/tests/http-fixture.py --output /chemin/diagnostic-http
```
Ouvrir l'URL `http://10.0.2.2:<port>/` affichée dans Safari invité. Le serveur
écoute uniquement sur la boucle locale de l'hôte ; il conserve le port et les
requêtes dans le dossier choisi. Saisir `bonjour` avec le clavier virtuel et
soumettre : le journal doit contenir `/submit?texte=bonjour`. Suivre le lien de
défilement, glisser jusqu'au bas, attendre la fin de l'inertie et toucher le lien
final : `/fin` doit apparaître. Arrêter le serveur avec Ctrl-C après le test.
Attendre l'affichage réel d'une application avant d'envoyer des gestes. Pour
TouchProbe, attendre `TouchProbe ready` dans le journal ; une réponse de lancement
ne garantit pas que la vue soit déjà prête. Sous 2.1.1, lancer Safari avec `--url`
alors qu'il est déjà ouvert n'a pas changé la page lors du test ; utiliser les
liens de la page ou relancer Safari pour ouvrir une autre URL.
Notes et la calculatrice complètent ce contrôle : saisir une note, la sauvegarder,
puis comparer son texte dans `Library/Notes/notes.db` avant et après redémarrage ;
vérifier visuellement `7 × 8 = 56` dans la calculatrice. Les diagnostics et le
résultat détaillé de la validation du 1er octobre sont décrits dans `HANDOFF.md`.
## Limites
- **Persistance NAND** : les pages et métadonnées écrites par iOS, ainsi que les effacements de blocs, sont conservés
dans `run/nand`. `ipod-emu start` conserve cet état ; `-f` ou un changement de firmware le remplace par l'image de base.
Les images produites par `gen-v1/mknand` utilisent le format VSVFL de production et `nand-write=on`.
Les anciennes images de `mknand-v1` doivent être reconstruites avant d'activer les écritures.
- **Internet** : PPP sur UART1, pont `ipod-netd` vers libslirp ; DNS et HTTP fonctionnent. `ipod-agent fetch
http://example.com/` teste le réseau depuis l'iPod. L'écran Réglages affiche toujours « Pas de Wi-Fi », car la puce
BCM4325 n'est pas émulée.
- **Heure** : le RTC D1759 fournit l'heure dès le démarrage, avec la configuration QEMU `-rtc`
(UTC et horloge hôte par défaut). `start -W` / `wait` ne modifie plus la date.
`ipod-agent settime SECONDES_UNIX` reste disponible pour les tests explicites.
Le décalage écrit par iOS reste en mémoire pendant la session ; une nouvelle instance QEMU repart
de sa base RTC. Les IRQ d'alarme matérielle ne sont pas émulées. Voir [RTC.md](RTC.md).
L'image de base est en français, région France, fuseau Europe/Paris.
- **Relance de SpringBoard** : `respring` peut bloquer la reconstruction du catalogue d'apps. `push-app` redémarre
donc l'émulateur sans reconstruire l'image ni réinitialiser la NAND. Avec l'agent v11, le déverrouillage puis
le lancement de HelloEmu au premier plan sont validés après cette installation. Les corrections GPIO/SPI
ont permis huit cycles veille/réveil alternant Home et Power sur 5H11a. Le déverrouillage automatique
continue à vérifier l'état de l'écran.
- **Veille** : l'agent v10 pose `SleepDisabled` sur `IOPMrootDomain`, le SoC ne sachant pas reprendre une veille profonde.
- Pas de son, pas d'accélération graphique (SpringBoard tourne avec `LK_ENABLE_MBX2D=0`).
## Reconstruire les images
`ipod-build-fw 5H11a` télécharge l'IPSW iPod2,1 chez Apple et les clés sur api.ipsw.me, génère la NOR, déchiffre et
adapte le rootfs (`bin/mkbase-rootfs` : LaunchDaemons réduits, fstab à une partition, activation Lockdown
d'`assets/Lockdown`, verrouillage auto désactivé) et génère la NAND (`gen-v1/mknand`, format VSVFL de production du générateur
qemu-ios-generate-nand 2ee0d46, signature de format 2 et écritures activées). L'image 5F138 vient de la release `n72ap_v1` de qemu-ios.
## Ce qui a été modifié dans qemu-ios (`~/qemu-ios`)
- `ipod_touch_aes.c` : clés GID choisies d'après le contenu du KBAG (table `ipod_touch_gid_keys.h`, 5F138 + 5H11a)
au lieu de l'ordre des appels ; le dernier bloc incomplet d'une image IMG3 est laissé en clair (supprime les hacks
Adler32 du kernel et de fin de DeviceTree) ; patch de la vérif de longueur LZSS d'iBoot trouvé par signature.
- `ipod_touch_sha1.c` : réécrit comme la fonction de compression SHA1 du matériel (état lisible/écrivable, `0x2` =
premier bloc, `0xa` = suite) + interruption de fin (IRQ 40, registre `0x0c`) — utilisés par le kernel 2.2.1 pour la
signature des pages de code et `IOSHA1Accelerator`.
- `ipod_touch_fmss.c` : chemin Bluetooth dans iBoot trouvé par recherche ; décodage des listes de programmation,
persistance des données et des 12 octets de métadonnées, effacement des blocs de 128 pages ; indicateur de page
vierge dans `0xC30`, nécessaire à la récupération FTL après arrêt de QEMU.
- `ipod_touch_mipi_dsi.c` : le bit d'horloge prête suit `CLKCTRL[31]`, permettant au pilote de terminer l'arrêt
de l'écran. Le SoC ne sait pas encore sortir de veille ; garder le verrouillage automatique désactivé.
- `ipod_touch_uart.c` : UART avec FIFO et interruptions utilisées par PPP.
- `ipod_touch_mbx.c` : patch BCM4325 du kernel limité à 5F138.
- `ipod_touch_2g.c` : stubs mémoire MPVD (`0x39600000`, kernel panic à la mise en veille) et `0x38100000` (écrit
par LLB 2.2.1), option machine `legacy-nor` (NOR à tags SHSH non chiffrés, `off` pour les NOR générées ici).
- xpwn `hfs/hfslib.c` : après `grow`, les nouveaux fichiers sont alloués dans l'espace ajouté (sinon fragmentés).
+82
View File
@@ -0,0 +1,82 @@
# RTC du D1759 — iPod Touch 2G
Le modèle `d1759` fournit l'heure avant le lancement des services iOS. La
machine 2G l'utilise à l'adresse I²C `0x73` ; le modèle historique `pcf50633`
reste disponible séparément. Les images et l'agent v12 existants conviennent.
Il faut reconstruire QEMU et utiliser le lanceur mis à jour.
## Protocole observé
Le désassemblage local du pilote `AppleD1759PMURTC` de 5H11a montre :
| Registres | Comportement |
| --- | --- |
| `0x5c` à `0x5f` | Compteur de secondes sur 32 bits, petit-boutiste |
| `0x64` à `0x67` | Décalage ajouté au compteur pour obtenir les secondes Unix |
| `0x50` à `0x5a` | Réglages d'alarme conservés en mémoire, sans IRQ |
| `0x60` à `0x63` | Suivi du démarrage, indépendant du RTC |
Sur le kernelcache déchiffré, le segment `__PRELINK` a le décalage adresse
virtuelle/fichier `0xc0029000`. La routine à `0xc03c8f08` lit quatre octets en
`0x5c` et ajoute le décalage chargé au démarrage par `0xc03c8c10` depuis `0x64`.
La routine à `0xc03c8f48` règle l'heure en écrivant la différence en `0x64`.
Les routines PMU à `0xc03c11e8` et `0xc03c1228` transmettent directement le
registre au nub I²C. Ce sont des observations du binaire local, pas des
symboles exportés ni une spécification complète du composant.
Le protocole I²C standard, registre puis données, est aussi visible dans
[OpeniBoot pour le S5L8720](https://github.com/iDroid-Project/openiBoot/blob/master/plat-s5l8720/pmu.c).
Sa fonction RTC est laissée à implémenter et ne constitue pas une preuve du
format du compteur. Les anciennes notes décrivant `0x61/0x62` comme un accès
indirect RTC étaient incorrectes.
Une lecture capture le compteur au début de la réception pour éviter de
mélanger deux secondes entre les octets. Les sélections de registre avec STOP
et avec START répété sont acceptées. Le compteur est en lecture seule ; les
écritures de décalage sont conservées pendant la session.
## Configuration et limites
QEMU initialise le compteur via `qemu_get_timedate`, puis le fait avancer avec
`rtc_clock`. Les options habituelles `-rtc base=...,clock=...` sont respectées.
Par défaut, la base est UTC et le temps suit l'horloge de l'hôte. Le fuseau
horaire d'affichage reste un réglage d'iOS. Une base reproductible peut être
passée au lanceur, par exemple :
```bash
IPOD_QEMU_EXTRA='-rtc base=2024-02-29T23:59:59,clock=vm' ipod-emu start -W
```
`start -W` et `wait` ne lancent plus `settime`. La commande
`ipod-agent settime SECONDES_UNIX` reste un réglage volontaire pour les tests.
Le décalage RTC ne persiste pas entre deux processus QEMU ; une nouvelle
instance repart de sa configuration `-rtc`. Les IRQ d'alarme, la veille
profonde et la migration de l'état RTC ne sont pas implémentées.
Un reset QEMU à chaud peut laisser PPP inaccessible. Pour redémarrer une
session conservée, utiliser `ipod-agent sync`, `ipod-emu stop`, puis
`ipod-emu start -W`, qui conserve la NAND.
## Validation
```bash
contrib/ipod-emu/tests/run-rtc
```
Le test compile le modèle réel avec ASan/UBSan et utilise le bus I²C QEMU :
base UTC avec fuseau hôte différent, date fixe bissextile, cohérence des
lectures au changement de seconde, décalages positifs/négatifs et séparation
des registres de démarrage.
Validation du 1er octobre 2026 sur des copies de NAND :
- 5H11a / iPhone OS 2.2.1 et 5F138 / iPhone OS 2.1.1 démarrent sans `settime` ;
`gettimeofday` dans un programme armv6 retrouve l'heure Unix de l'hôte.
- Un changement volontaire de date continue à avancer. Après un reset à
chaud de 5F138, les journaux précoces d'iOS retrouvent le décalage écrit ;
PPP ne reprend pas correctement après ce reset.
- Les démarrages normaux, le déverrouillage et le lancement d'applications
sont vérifiés avec le lanceur sans synchronisation par l'agent.
Les diagnostics restent hors du dépôt dans
`/srv/homes/iphoneos2/ipod-emu/work/rtc-20261001/`.
+32
View File
@@ -0,0 +1,32 @@
# Sources de l'environnement iPod
Ce dossier sauvegarde les sources de `~/ipod-emu` avec les modifications QEMU.
Pour retrouver les chemins utilisés par les outils, copier ce dossier dans `~/ipod-emu`
(ou définir `IPOD_EMU_HOME`). Ne pas remplacer un environnement existant sans conserver
ses images, ses données NAND et ses fichiers d'activation.
Les images Apple, les SDK, les exécutables construits, les sessions NAND et les
certificats/clés privés Lockdown sont exclus de Git. Conserver `assets/Lockdown`
séparément : `mkbase-rootfs` en a besoin pour préparer une image activée. Le dépôt
sauvegarde le code ; il ne constitue pas une sauvegarde des données de l'iPod.
Dépendances Git à cloner sous `~/ipod-emu`, avec les révisions utilisées :
| Dossier | Dépôt | Révision |
| --- | --- | --- |
| xpwn | https://github.com/planetbeing/xpwn | 20c32e5c12d1b22a9d55a59a0ff6267f539b77f4 |
| dmg2img | https://github.com/Lekensteyn/dmg2img | a3e413489ccdd05431401357bf21690536425012 |
| qemu-ios-generate-nand | https://github.com/devos50/qemu-ios-generate-nand | 2ee0d4690612c670dd8d1e9bf3cc7b65aedc4242 |
| qemu-ios-generate-nor | https://github.com/devos50/qemu-ios-generate-nor | 4f822cbe2c74dab3e2e8444c00dfa676ed43dd5e |
Après clonage et checkout, appliquer les adaptations locales avant compilation :
```sh
git -C xpwn apply --unidiff-zero ../patches/xpwn.patch
git -C qemu-ios-generate-nand apply --unidiff-zero ../patches/qemu-ios-generate-nand.patch
```
Les commandes de compilation et démarrage, les validations de persistance et les
limites restantes figurent dans `HANDOFF.md` et `README.md`. L'agent requiert le
compilateur armv6, le SDK iPhone OS 2.2.1 et `ldid` indiqués dans son Makefile.
Le relais réseau `netd/ipod-netd.c` utilise libslirp.
+23
View File
@@ -0,0 +1,23 @@
# Agent iPod émulé (armv6, iPhone OS 2.x)
SDK := /opt/theos/sdks/iPhoneOS2.2.1.sdk
CC := /opt/cctools/bin/arm-apple-darwin9-gcc
CFLAGS := -isysroot $(SDK) -arch armv6 -miphoneos-version-min=2.0 -O2 -Wall -Wno-deprecated-declarations
FRAMEWORKS := -framework CoreFoundation -framework SystemConfiguration
all: overlay
emuagent: emuagent.c entitlements.plist
$(CC) $(CFLAGS) -o $@ $< $(FRAMEWORKS)
ldid -Sentitlements.plist $@ # entitlements vérifiés par SpringBoard pour SBSLaunchApplication
overlay: emuagent com.allovoisins.emuagent.plist
rm -rf overlay && mkdir -p overlay/usr/libexec overlay/System/Library/LaunchDaemons
cp emuagent overlay/usr/libexec/
cp com.allovoisins.emuagent.plist overlay/System/Library/LaunchDaemons/
# résolution DNS (retirée de l'image de base avec les autres démons)
cp apple/com.apple.mDNSResponder.plist apple/com.apple.mDNSResponderHelper.plist overlay/System/Library/LaunchDaemons/
clean:
rm -rf emuagent overlay
.PHONY: all overlay clean
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.apple.mDNSResponder</string>
<key>OnDemand</key>
<false/>
<key>UserName</key>
<string>_mdnsresponder</string>
<key>GroupName</key>
<string>_mdnsresponder</string>
<key>ProgramArguments</key>
<array>
<string>/usr/sbin/mDNSResponder</string>
<string>-launchd</string>
</array>
<key>MachServices</key>
<dict>
<key>com.apple.mDNSResponder</key>
<true/>
</dict>
<key>Sockets</key>
<dict>
<key>Listeners</key>
<dict>
<key>SockFamily</key>
<string>Unix</string>
<key>SockPathName</key>
<string>/var/run/mDNSResponder</string>
<key>SockPathMode</key>
<integer>438</integer>
</dict>
</dict>
<key>ServiceIPC</key>
<true/>
</dict>
</plist>
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.apple.mDNSResponderHelper</string>
<key>OnDemand</key>
<true/>
<key>ProgramArguments</key>
<array>
<string>/usr/sbin/mDNSResponderHelper</string>
</array>
<key>MachServices</key>
<dict>
<key>com.apple.mDNSResponderHelper</key>
<true/>
</dict>
<key>ServiceIPC</key>
<true/>
</dict>
</plist>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>Label</key><string>com.allovoisins.emuagent</string>
<key>ProgramArguments</key><array><string>/usr/libexec/emuagent</string></array>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
<key>UserName</key><string>root</string>
</dict></plist>
+913
View File
@@ -0,0 +1,913 @@
/*
* emuagent : agent de pilotage de l'iPod émulé (iPhone OS 2.x), lancé par launchd en root.
*
* 1. Réseau : lance pppd sur l'UART1 (/dev/tty.bluetooth, reliée à ipod-netd côté hôte) et publie le lien dans le
* SCDynamicStore de configd (IPv4, routeur, DNS), pour qu'iOS le considère comme le réseau actif.
* 2. Pilotage : serveur TCP sur le port 7788 (redirigé par ipod-netd vers 127.0.0.1:7788 sur l'hôte).
*
* Protocole : une requête = une ligne, champs séparés par des tabulations. Réponse : "OK <n> [info]\n" ou
* "ERR <n>\n", suivi de n octets. Pour "put", n octets de contenu suivent la ligne de requête.
*/
#include <CoreFoundation/CoreFoundation.h>
/* SCDynamicStore est dans SystemConfiguration.framework mais marqué indisponible dans les en-têtes iPhone */
typedef const struct __SCDynamicStore *SCDynamicStoreRef;
extern SCDynamicStoreRef SCDynamicStoreCreate(CFAllocatorRef, CFStringRef, void *, void *);
extern Boolean SCDynamicStoreSetValue(SCDynamicStoreRef, CFStringRef, CFPropertyListRef);
extern CFPropertyListRef SCDynamicStoreCopyValue(SCDynamicStoreRef, CFStringRef);
extern CFArrayRef SCDynamicStoreCopyKeyList(SCDynamicStoreRef, CFStringRef);
#include <arpa/inet.h>
#include <dirent.h>
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <ifaddrs.h>
#include <net/if.h>
/* net/route.h absent du SDK iPhone : structures du socket de routage de XNU 1228 (Darwin 9) */
struct rt_metrics {
u_int32_t rmx_locks, rmx_mtu, rmx_hopcount;
int32_t rmx_expire;
u_int32_t rmx_recvpipe, rmx_sendpipe, rmx_ssthresh, rmx_rtt, rmx_rttvar, rmx_pksent;
u_int32_t rmx_filler[4];
};
struct rt_msghdr {
u_short rtm_msglen;
u_char rtm_version, rtm_type;
u_short rtm_index;
int rtm_flags, rtm_addrs;
pid_t rtm_pid;
int rtm_seq, rtm_errno, rtm_use;
u_int32_t rtm_inits;
struct rt_metrics rtm_rmx;
};
#define RTM_VERSION 5
#define RTM_ADD 0x1
#define RTM_CHANGE 0x3
#define NET_RT_DUMP 1
#define RTF_UP 0x1
#define RTF_GATEWAY 0x2
#define RTF_STATIC 0x800
#define RTA_DST 0x1
#define RTA_GATEWAY 0x2
#define RTA_NETMASK 0x4
#include <netdb.h>
#include <netinet/in.h>
#include <signal.h>
#include <spawn.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <termios.h>
#include <notify.h>
#include <unistd.h>
#define VERSION "12"
#define PORT 7788
#define PPP_TTY "/dev/tty.bluetooth"
#define GUEST_IP "10.0.2.15"
#define GW_IP "10.0.2.2"
#define DNS_IP "10.0.2.3"
#define SERVICE_ID "emuppp" /* pppd publie son état sous State:/Network/Service/<id> */
extern char **environ;
static FILE *con;
static pid_t pppd_pid;
static SCDynamicStoreRef store;
static int net_published;
static void clog_(const char *fmt, ...)
{
va_list ap;
if (!con) return;
fputs("emuagent: ", con);
va_start(ap, fmt);
vfprintf(con, fmt, ap);
va_end(ap);
fputc('\n', con);
}
/* ---------- réseau ---------- */
static void start_pppd(void)
{
char *argv[] = { "/usr/sbin/pppd", PPP_TTY, "115200", "local", "nocrtscts", "noauth", "nodetach",
"persist", "maxfail", "0", "holdoff", "2", "defaultroute", "noipdefault",
"ipcp-accept-local", "ipcp-accept-remote", "usepeerdns", "novj", "noccp",
"lcp-echo-interval", "0", "mtu", "1500", "mru", "1500", "serviceid", SERVICE_ID, NULL };
posix_spawn_file_actions_t fa;
posix_spawn_file_actions_init(&fa);
posix_spawn_file_actions_addopen(&fa, 0, "/dev/null", O_RDONLY, 0);
posix_spawn_file_actions_addopen(&fa, 1, "/dev/console", O_WRONLY, 0);
posix_spawn_file_actions_addopen(&fa, 2, "/dev/console", O_WRONLY, 0);
if (posix_spawn(&pppd_pid, argv[0], &fa, NULL, argv, environ) != 0) {
clog_("pppd : échec du lancement (%s)", strerror(errno));
pppd_pid = 0;
} else {
clog_("pppd lancé (pid %d) sur %s", pppd_pid, PPP_TTY);
}
posix_spawn_file_actions_destroy(&fa);
}
static int ppp_up(char *ifname, size_t len)
{
struct ifaddrs *ifa, *i;
int up = 0;
if (getifaddrs(&ifa)) return 0;
for (i = ifa; i; i = i->ifa_next) {
if (!strncmp(i->ifa_name, "ppp", 3) && i->ifa_addr && i->ifa_addr->sa_family == AF_INET && (i->ifa_flags & IFF_UP)) {
strncpy(ifname, i->ifa_name, len);
up = 1;
}
}
freeifaddrs(ifa);
return up;
}
static CFArrayRef str_array(const char *s)
{
CFStringRef v = CFStringCreateWithCString(NULL, s, kCFStringEncodingUTF8);
CFArrayRef a = CFArrayCreate(NULL, (const void **)&v, 1, &kCFTypeArrayCallBacks);
CFRelease(v);
return a;
}
static void set_key(CFStringRef key, CFDictionaryRef val)
{
if (!SCDynamicStoreSetValue(store, key, val)) clog_("SCDynamicStoreSetValue a échoué");
}
/* Route par défaut via la passerelle PPP (RTM_ADD sur le socket de routage, comme route add default) */
static int route_msg(int type)
{
struct {
struct rt_msghdr h;
struct sockaddr_in dst, gw, mask;
} m;
memset(&m, 0, sizeof m);
m.h.rtm_msglen = sizeof m;
m.h.rtm_version = RTM_VERSION;
m.h.rtm_type = type;
m.h.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
m.h.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
m.h.rtm_pid = getpid();
m.h.rtm_seq = 1;
m.dst.sin_len = m.gw.sin_len = m.mask.sin_len = sizeof(struct sockaddr_in);
m.dst.sin_family = m.gw.sin_family = m.mask.sin_family = AF_INET;
inet_aton(GW_IP, &m.gw.sin_addr);
int s = socket(PF_ROUTE, SOCK_RAW, 0);
if (s < 0) return -1;
int r = write(s, &m, sizeof m) == sizeof m ? 0 : -1;
int e = errno;
close(s);
errno = e;
return r;
}
static int add_default_route(void)
{
if (route_msg(RTM_ADD) == 0) return 0;
if (errno == EEXIST && route_msg(RTM_CHANGE) == 0) return 0; /* une route par défaut existait déjà */
clog_("route par défaut : %s", strerror(errno));
return -1;
}
static CFMutableDictionaryRef dict(void)
{
return CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
}
/*
* Déclare le service PPP côté Setup: (ce que contiendrait preferences.plist pour un service configuré) : sans cela,
* IPMonitor ignore l'état publié par pppd et n'installe ni la route par défaut ni le DNS global.
*/
static void publish_setup(void)
{
if (!store) store = SCDynamicStoreCreate(NULL, CFSTR("emuagent"), NULL, NULL);
if (!store) { clog_("SCDynamicStoreCreate a échoué"); return; }
CFMutableDictionaryRef svc = dict(), itf = dict(), ipv4 = dict(), glob = dict();
CFDictionarySetValue(svc, CFSTR("UserDefinedName"), CFSTR("Émulateur (PPP)"));
set_key(CFSTR("Setup:/Network/Service/" SERVICE_ID), svc);
CFDictionarySetValue(itf, CFSTR("Type"), CFSTR("PPP"));
CFDictionarySetValue(itf, CFSTR("SubType"), CFSTR("PPPSerial"));
CFDictionarySetValue(itf, CFSTR("DeviceName"), CFSTR("tty.bluetooth"));
set_key(CFSTR("Setup:/Network/Service/" SERVICE_ID "/Interface"), itf);
CFDictionarySetValue(ipv4, CFSTR("ConfigMethod"), CFSTR("PPP"));
set_key(CFSTR("Setup:/Network/Service/" SERVICE_ID "/IPv4"), ipv4);
CFArrayRef order = str_array(SERVICE_ID);
CFDictionarySetValue(glob, CFSTR("ServiceOrder"), order);
set_key(CFSTR("Setup:/Network/Global/IPv4"), glob);
CFRelease(order); CFRelease(svc); CFRelease(itf); CFRelease(ipv4); CFRelease(glob);
}
/* Lien monté : vérifie qu'IPMonitor a bien installé la route par défaut, sinon la pose nous-mêmes */
static void publish_network(const char *ifname)
{
net_published = 1;
sleep(1);
if (add_default_route() == 0) clog_("lien %s prêt (%s via %s, DNS %s)", ifname, GUEST_IP, GW_IP, DNS_IP);
}
static void net_tick(void)
{
int status;
char ifname[IFNAMSIZ];
if (pppd_pid && waitpid(pppd_pid, &status, WNOHANG) == pppd_pid) {
clog_("pppd terminé (statut %d), relance", status);
pppd_pid = 0;
net_published = 0;
}
if (!pppd_pid) { publish_setup(); start_pppd(); }
int up = ppp_up(ifname, sizeof ifname);
if (up && !net_published) publish_network(ifname);
if (!up) net_published = 0;
}
/* ---------- réponses ---------- */
static int send_all(int fd, const void *b, size_t n)
{
const char *p = b;
while (n) {
ssize_t w = write(fd, p, n);
if (w <= 0) { if (w < 0 && errno == EINTR) continue; if (!w) errno = EIO; return -1; }
p += w; n -= w;
}
return 0;
}
static void reply(int fd, int ok, const char *info, const void *data, size_t len)
{
char hdr[128];
snprintf(hdr, sizeof hdr, "%s %zu%s%s\n", ok ? "OK" : "ERR", len, info ? " " : "", info ? info : "");
send_all(fd, hdr, strlen(hdr));
if (len) send_all(fd, data, len);
}
static void reply_str(int fd, int ok, const char *fmt, ...)
{
char buf[4096];
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof buf, fmt, ap);
va_end(ap);
reply(fd, ok, NULL, buf, strlen(buf));
}
/* tampon dynamique */
struct buf { char *p; size_t n, cap; };
static void bput(struct buf *b, const void *d, size_t n)
{
if (b->n + n + 1 > b->cap) {
b->cap = (b->n + n + 1) * 2;
b->p = realloc(b->p, b->cap);
}
memcpy(b->p + b->n, d, n);
b->n += n;
b->p[b->n] = 0;
}
static void bprintf(struct buf *b, const char *fmt, ...)
{
char tmp[2048];
va_list ap;
va_start(ap, fmt);
int n = vsnprintf(tmp, sizeof tmp, fmt, ap);
va_end(ap);
bput(b, tmp, n < (int)sizeof tmp ? n : (int)sizeof tmp - 1);
}
/* ---------- commandes ---------- */
static void cmd_exec(int fd, char **args, int nargs)
{
int pfd[2];
pid_t pid;
if (nargs < 1) { reply_str(fd, 0, "usage: exec<TAB>programme<TAB>args..."); return; }
pipe(pfd);
posix_spawn_file_actions_t fa;
posix_spawn_file_actions_init(&fa);
posix_spawn_file_actions_addopen(&fa, 0, "/dev/null", O_RDONLY, 0);
posix_spawn_file_actions_adddup2(&fa, pfd[1], 1);
posix_spawn_file_actions_adddup2(&fa, pfd[1], 2);
posix_spawn_file_actions_addclose(&fa, pfd[0]);
char *argv[64];
int i;
for (i = 0; i < nargs && i < 63; i++) argv[i] = args[i];
argv[i] = NULL;
int err = posix_spawn(&pid, argv[0], &fa, NULL, argv, environ);
posix_spawn_file_actions_destroy(&fa);
close(pfd[1]);
if (err) { close(pfd[0]); reply_str(fd, 0, "posix_spawn %s : %s", argv[0], strerror(err)); return; }
struct buf out = { 0 };
char tmp[4096];
time_t start = time(NULL);
fcntl(pfd[0], F_SETFL, O_NONBLOCK);
for (;;) {
fd_set rs;
struct timeval tv = { 1, 0 };
FD_ZERO(&rs); FD_SET(pfd[0], &rs);
if (select(pfd[0] + 1, &rs, NULL, NULL, &tv) > 0) {
ssize_t n = read(pfd[0], tmp, sizeof tmp);
if (n > 0) { bput(&out, tmp, n); continue; }
if (n == 0) break;
}
if (time(NULL) - start > 120) { kill(pid, SIGKILL); bprintf(&out, "\n[emuagent: délai dépassé, processus tué]\n"); break; }
}
close(pfd[0]);
int status = 0;
waitpid(pid, &status, 0);
char info[32];
snprintf(info, sizeof info, "%d", WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status));
reply(fd, 1, info, out.p, out.n);
free(out.p);
}
static int (*p_SBSLaunchApplication)(CFStringRef, CFURLRef, CFArrayRef, CFDictionaryRef, CFStringRef, CFStringRef, unsigned);
static CFStringRef (*p_SBSApplicationLaunchingErrorString)(int);
static CFStringRef (*p_SBSCopyFrontmostApplicationDisplayIdentifier)(void);
static unsigned (*p_SBSSpringBoardServerPort)(void);
static int (*p_SBGetScreenLockStatus)(unsigned, unsigned char *, unsigned char *);
static int (*p_SBDimScreen)(unsigned, unsigned char);
static void load_sbs(void)
{
static void *h;
if (h) return;
h = dlopen("/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices", RTLD_NOW);
if (!h) return;
p_SBSSpringBoardServerPort = dlsym(h, "SBSSpringBoardServerPort");
p_SBGetScreenLockStatus = dlsym(h, "SBGetScreenLockStatus");
p_SBDimScreen = dlsym(h, "SBDimScreen");
p_SBSLaunchApplication = dlsym(h, "SBSLaunchApplication");
p_SBSApplicationLaunchingErrorString = dlsym(h, "SBSApplicationLaunchingErrorString");
p_SBSCopyFrontmostApplicationDisplayIdentifier = dlsym(h, "SBSCopyFrontmostApplicationDisplayIdentifier");
}
static void cfstr(CFStringRef s, char *out, size_t len)
{
out[0] = 0;
if (s) CFStringGetCString(s, out, len, kCFStringEncodingUTF8);
}
/* API MIG du firmware 2.2.1 : les deux états sont des octets, retour kern_return_t. */
static void cmd_screen(int fd, int wake)
{
load_sbs();
if (!p_SBSSpringBoardServerPort || !p_SBGetScreenLockStatus || !p_SBDimScreen) {
reply_str(fd, 0, "API d'écran indisponible"); return;
}
unsigned port = p_SBSSpringBoardServerPort();
unsigned char locked = 0, passcode = 0;
int rc = p_SBGetScreenLockStatus(port, &locked, &passcode);
if (rc) { reply_str(fd, 0, "SBGetScreenLockStatus : %d", rc); return; }
if (wake) {
/* SBApplicationRequestedDeviceUnlock affiche le clavier de code même
sans code activé. Réveiller seulement l'écran, puis glisser côté hôte. */
rc = p_SBDimScreen(port, 0);
if (rc) { reply_str(fd, 0, "SBDimScreen : %d", rc); return; }
}
reply_str(fd, 1, "locked=%u passcode=%u", locked, passcode);
}
static void cmd_launch(int fd, char **args, int nargs)
{
load_sbs();
if (!p_SBSLaunchApplication) { reply_str(fd, 0, "SpringBoardServices indisponible"); return; }
if (nargs < 1) { reply_str(fd, 0, "usage: launch<TAB>bundle-id[<TAB>fichier stdout/stderr ou -[<TAB>url]]"); return; }
CFStringRef id = CFStringCreateWithCString(NULL, args[0], kCFStringEncodingUTF8);
CFStringRef out = nargs > 1 && args[1][0] != '-' ? CFStringCreateWithCString(NULL, args[1], kCFStringEncodingUTF8) : NULL;
CFURLRef url = NULL;
if (nargs > 2) {
CFStringRef us = CFStringCreateWithCString(NULL, args[2], kCFStringEncodingUTF8);
url = CFURLCreateWithString(NULL, us, NULL);
CFRelease(us);
}
/* SpringBoard refuse les demandes de lancement venant de root (« security policy error ») : l'appel est fait
par un fils qui passe en mobile (501) */
int r = -1;
pid_t child = fork();
if (child == 0) {
setgid(501);
setuid(501);
_exit(p_SBSLaunchApplication(id, url, NULL, NULL, out, out, 0) & 0xff);
}
int status = 0;
for (int i = 0; child > 0 && i < 300; i++) { /* 30 s max : SpringBoard peut être occupé (relance, cache des apps) */
if (waitpid(child, &status, WNOHANG) == child) { if (WIFEXITED(status)) r = WEXITSTATUS(status); child = 0; break; }
usleep(100000);
}
if (child > 0) {
kill(child, SIGKILL);
waitpid(child, NULL, 0);
if (url) CFRelease(url);
CFRelease(id);
if (out) CFRelease(out);
reply_str(fd, 0, "SpringBoard ne répond pas (relance ou reconstruction du cache des apps en cours ?)");
return;
}
if (url) CFRelease(url);
CFRelease(id);
if (out) CFRelease(out);
if (r == 0) { reply_str(fd, 1, "lancé"); return; }
char err[256] = "?";
if (p_SBSApplicationLaunchingErrorString) cfstr(p_SBSApplicationLaunchingErrorString(r), err, sizeof err);
reply_str(fd, 0, "SBSLaunchApplication : %d (%s)", r, err);
}
/* fetch<TAB>http://hôte[:port]/chemin : résolution DNS + GET HTTP depuis l'iPod (diagnostic réseau) */
static void cmd_fetch(int fd, char **args, int nargs)
{
char host[256], path[1024] = "/", port[8] = "80";
if (nargs < 1 || strncmp(args[0], "http://", 7)) { reply_str(fd, 0, "usage: fetch<TAB>http://hôte[:port]/chemin"); return; }
const char *h = args[0] + 7, *slash = strchr(h, '/');
size_t hl = slash ? (size_t)(slash - h) : strlen(h);
if (hl >= sizeof host) hl = sizeof host - 1;
memcpy(host, h, hl); host[hl] = 0;
if (slash) strncpy(path, slash, sizeof path - 1);
char *colon = strchr(host, ':');
if (colon) { *colon = 0; strncpy(port, colon + 1, sizeof port - 1); }
struct addrinfo hints = { .ai_family = AF_INET, .ai_socktype = SOCK_STREAM }, *res;
int e = getaddrinfo(host, port, &hints, &res);
if (e) { reply_str(fd, 0, "getaddrinfo %s : %s", host, gai_strerror(e)); return; }
char ip[32];
inet_ntop(AF_INET, &((struct sockaddr_in *)res->ai_addr)->sin_addr, ip, sizeof ip);
int s = socket(AF_INET, SOCK_STREAM, 0);
struct timeval to = { 15, 0 };
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &to, sizeof to);
setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &to, sizeof to);
if (connect(s, res->ai_addr, res->ai_addrlen)) { reply_str(fd, 0, "connect %s (%s) : %s", host, ip, strerror(errno)); close(s); freeaddrinfo(res); return; }
freeaddrinfo(res);
char req[1400];
snprintf(req, sizeof req, "GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: emuagent\r\nConnection: close\r\n\r\n", path, host);
send_all(s, req, strlen(req));
struct buf b = { 0 };
bprintf(&b, "[%s -> %s]\n", host, ip);
char tmp[4096];
ssize_t n;
while (b.n < 256 * 1024 && (n = read(s, tmp, sizeof tmp)) > 0) bput(&b, tmp, n);
close(s);
reply(fd, 1, NULL, b.p, b.n);
free(b.p);
}
/* sc[<TAB>clé] : liste des clés du SCDynamicStore, ou valeur d'une clé (plist XML) */
static void cmd_sc(int fd, char **args, int nargs)
{
if (!store) store = SCDynamicStoreCreate(NULL, CFSTR("emuagent"), NULL, NULL);
CFStringRef k = CFStringCreateWithCString(NULL, nargs ? args[0] : ".*", kCFStringEncodingUTF8);
CFPropertyListRef v = nargs && !strchr(args[0], '*') ? SCDynamicStoreCopyValue(store, k) : SCDynamicStoreCopyKeyList(store, k);
CFRelease(k);
if (!v) { reply_str(fd, 0, "clé absente"); return; }
CFDataRef x = CFPropertyListCreateXMLData(NULL, v);
reply(fd, 1, NULL, CFDataGetBytePtr(x), CFDataGetLength(x));
CFRelease(x);
CFRelease(v);
}
/* routes : table de routage IPv4 (sysctl NET_RT_DUMP) */
static void cmd_routes(int fd)
{
int mib[6] = { CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_DUMP, 0 };
size_t len = 0;
if (sysctl(mib, 6, NULL, &len, NULL, 0)) { reply_str(fd, 0, "sysctl : %s", strerror(errno)); return; }
char *b = malloc(len), *p;
sysctl(mib, 6, b, &len, NULL, 0);
struct buf out = { 0 };
for (p = b; p < b + len;) {
struct rt_msghdr *h = (struct rt_msghdr *)p;
struct sockaddr *sa = (struct sockaddr *)(h + 1);
char txt[3][32] = { "-", "-", "-" };
for (int i = 0; i < 3; i++) {
if (!(h->rtm_addrs & (1 << i))) continue;
if (sa->sa_family == AF_INET) inet_ntop(AF_INET, &((struct sockaddr_in *)sa)->sin_addr, txt[i], 32);
else if (sa->sa_family == AF_LINK) strcpy(txt[i], "link");
else if (sa->sa_len == 0) strcpy(txt[i], "0");
sa = (struct sockaddr *)((char *)sa + (sa->sa_len ? ((sa->sa_len + 3) & ~3) : 4));
}
char ifn[IFNAMSIZ] = "?";
if_indextoname(h->rtm_index, ifn);
bprintf(&out, "%-16s gw %-16s mask %-16s %s flags 0x%x\n", txt[0], txt[1], txt[2], ifn, h->rtm_flags);
p += h->rtm_msglen;
}
free(b);
reply(fd, 1, NULL, out.p ? out.p : "", out.n);
free(out.p);
}
static void cmd_front(int fd)
{
load_sbs();
char s[256] = "";
if (p_SBSCopyFrontmostApplicationDisplayIdentifier) {
CFStringRef f = p_SBSCopyFrontmostApplicationDisplayIdentifier();
cfstr(f, s, sizeof s);
if (f) CFRelease(f);
}
reply_str(fd, 1, "%s", s[0] ? s : "com.apple.springboard");
}
static void list_apps_in(struct buf *b, const char *dir, int depth)
{
DIR *d = opendir(dir);
struct dirent *e;
if (!d) return;
while ((e = readdir(d))) {
char path[1024];
if (e->d_name[0] == '.') continue;
snprintf(path, sizeof path, "%s/%s", dir, e->d_name);
size_t l = strlen(e->d_name);
if (l > 4 && !strcmp(e->d_name + l - 4, ".app")) {
CFURLRef u = CFURLCreateFromFileSystemRepresentation(NULL, (UInt8 *)path, strlen(path), true);
CFBundleRef bundle = CFBundleCreate(NULL, u);
char id[256] = "", name[256] = "";
if (bundle) {
cfstr(CFBundleGetIdentifier(bundle), id, sizeof id);
CFTypeRef n = CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("CFBundleDisplayName"));
if (!n) n = CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleNameKey);
if (n && CFGetTypeID(n) == CFStringGetTypeID()) cfstr(n, name, sizeof name);
CFRelease(bundle);
}
CFRelease(u);
bprintf(b, "%s\t%s\t%s\n", id, name, path);
} else if (depth > 0) {
list_apps_in(b, path, depth - 1);
}
}
closedir(d);
}
static void cmd_apps(int fd)
{
struct buf b = { 0 };
list_apps_in(&b, "/Applications", 0);
list_apps_in(&b, "/private/var/mobile/Applications", 1);
reply(fd, 1, NULL, b.p, b.n);
free(b.p);
}
static int mkdirs(const char *path)
{
char tmp[1024];
strncpy(tmp, path, sizeof tmp - 1);
tmp[sizeof tmp - 1] = 0;
for (char *p = tmp + 1; *p; p++) {
if (*p == '/') { *p = 0; mkdir(tmp, 0755); *p = '/'; }
}
return mkdir(tmp, 0755) == 0 || errno == EEXIST ? 0 : -1;
}
static int read_full(int fd, void *b, size_t n)
{
char *p = b;
while (n) {
ssize_t r = read(fd, p, n);
if (r <= 0) { if (r < 0 && errno == EINTR) continue; if (!r) errno = EIO; return -1; }
p += r; n -= r;
}
return 0;
}
/* Transferts par blocs : ne pas allouer la taille du fichier sur l'iPod. */
static void cmd_put(int fd, char **args, int nargs)
{
if (nargs < 3) { reply_str(fd, 0, "usage: put<TAB>chemin<TAB>mode octal<TAB>taille + données"); return; }
char *end;
errno = 0;
unsigned long size = strtoul(args[2], &end, 10);
if (errno || !args[2][0] || args[2][0] == '-' || *end) {
reply_str(fd, 0, "taille invalide"); return;
}
char dir[1024];
if (strlen(args[0]) >= sizeof dir) { reply_str(fd, 0, "chemin trop long"); return; }
strcpy(dir, args[0]);
char *slash = strrchr(dir, '/');
if (slash && slash != dir) { *slash = 0; mkdirs(dir); }
char tmp[1100];
snprintf(tmp, sizeof tmp, "%s.emuagent-XXXXXX", args[0]);
int f = mkstemp(tmp);
if (f < 0) { reply_str(fd, 0, "création : %s", strerror(errno)); return; }
char data[65536];
size_t remaining = size;
int failed = 0;
while (remaining) {
size_t chunk = remaining < sizeof data ? remaining : sizeof data;
if (read_full(fd, data, chunk) || send_all(f, data, chunk)) { failed = 1; break; }
remaining -= chunk;
}
if (!failed && (fchmod(f, strtoul(args[1], NULL, 8)) || fsync(f))) failed = 1;
int saved = errno;
if (close(f) && !failed) { failed = 1; saved = errno; }
if (!failed && rename(tmp, args[0])) { failed = 1; saved = errno; }
if (failed) {
unlink(tmp);
reply_str(fd, 0, "écriture de %s : %s", args[0], strerror(saved));
return;
}
reply_str(fd, 1, "%lu octets écrits", size);
}
static void cmd_get(int fd, char **args, int nargs)
{
if (nargs < 1) { reply_str(fd, 0, "usage: get<TAB>chemin"); return; }
int f = open(args[0], O_RDONLY | O_NONBLOCK);
struct stat st;
if (f < 0 || fstat(f, &st) || !S_ISREG(st.st_mode)) {
reply_str(fd, 0, "lecture de %s impossible", args[0]);
if (f >= 0) close(f);
return;
}
char hdr[128], data[65536];
snprintf(hdr, sizeof hdr, "OK %llu\n", (unsigned long long)st.st_size);
if (send_all(fd, hdr, strlen(hdr))) { close(f); return; }
off_t remaining = st.st_size;
while (remaining) {
size_t chunk = remaining < (off_t)sizeof data ? (size_t)remaining : sizeof data;
/* Après l'en-tête, une erreur impose de fermer la connexion :
le client détecte la réponse incomplète et conserve son ancien fichier. */
if (read_full(f, data, chunk) || send_all(fd, data, chunk)) {
close(f); shutdown(fd, SHUT_RDWR); return;
}
remaining -= chunk;
}
close(f);
}
static void cmd_ls(int fd, char **args, int nargs)
{
const char *path = nargs ? args[0] : "/";
DIR *d = opendir(path);
if (!d) { reply_str(fd, 0, "%s : %s", path, strerror(errno)); return; }
struct buf b = { 0 };
struct dirent *e;
while ((e = readdir(d))) {
char p[1024];
struct stat st;
if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, "..")) continue;
snprintf(p, sizeof p, "%s/%s", path, e->d_name);
if (lstat(p, &st)) continue;
bprintf(&b, "%c%04o %5d %5d %10lld %s\n", S_ISDIR(st.st_mode) ? 'd' : S_ISLNK(st.st_mode) ? 'l' : '-',
st.st_mode & 07777, st.st_uid, st.st_gid, (long long)st.st_size, e->d_name);
}
closedir(d);
reply(fd, 1, NULL, b.p ? b.p : "", b.n);
free(b.p);
}
static int rm_rf(const char *path)
{
struct stat st;
if (lstat(path, &st)) return -1;
if (S_ISDIR(st.st_mode)) {
DIR *d = opendir(path);
struct dirent *e;
while (d && (e = readdir(d))) {
char p[1024];
if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, "..")) continue;
snprintf(p, sizeof p, "%s/%s", path, e->d_name);
rm_rf(p);
}
if (d) closedir(d);
return rmdir(path);
}
return unlink(path);
}
static void cmd_ps(int fd)
{
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
size_t len = 0;
sysctl(mib, 3, NULL, &len, NULL, 0);
struct kinfo_proc *procs = malloc(len);
if (sysctl(mib, 3, procs, &len, NULL, 0)) { reply_str(fd, 0, "sysctl : %s", strerror(errno)); free(procs); return; }
struct buf b = { 0 };
bprintf(&b, " PID PPID UID COMMAND\n");
for (size_t i = 0; i < len / sizeof *procs; i++) {
struct kinfo_proc *p = &procs[i];
bprintf(&b, "%5d %5d %5d %s\n", p->kp_proc.p_pid, p->kp_eproc.e_ppid, p->kp_eproc.e_ucred.cr_uid, p->kp_proc.p_comm);
}
reply(fd, 1, NULL, b.p, b.n);
free(b.p);
free(procs);
}
static int kill_by_name(const char *name, int sig)
{
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 }, n = 0;
size_t len = 0;
sysctl(mib, 3, NULL, &len, NULL, 0);
struct kinfo_proc *procs = malloc(len);
if (!sysctl(mib, 3, procs, &len, NULL, 0)) {
for (size_t i = 0; i < len / sizeof *procs; i++) {
if (!strcmp(procs[i].kp_proc.p_comm, name) && !kill(procs[i].kp_proc.p_pid, sig)) n++;
}
}
free(procs);
return n;
}
static void cmd_kill(int fd, char **args, int nargs)
{
if (nargs < 1) { reply_str(fd, 0, "usage: kill<TAB>pid|nom[<TAB>signal]"); return; }
int sig = nargs > 1 ? atoi(args[1]) : SIGTERM;
char *end;
long pid = strtol(args[0], &end, 10);
if (!*end) {
if (kill(pid, sig)) reply_str(fd, 0, "kill %ld : %s", pid, strerror(errno));
else reply_str(fd, 1, "1");
} else {
int n = kill_by_name(args[0], sig);
reply_str(fd, n > 0, "%d", n);
}
}
static void cmd_net(int fd)
{
struct ifaddrs *ifa, *i;
struct buf b = { 0 };
if (!getifaddrs(&ifa)) {
for (i = ifa; i; i = i->ifa_next) {
if (i->ifa_addr && i->ifa_addr->sa_family == AF_INET) {
char a[32], dst[32] = "";
inet_ntop(AF_INET, &((struct sockaddr_in *)i->ifa_addr)->sin_addr, a, sizeof a);
if (i->ifa_dstaddr && (i->ifa_flags & IFF_POINTOPOINT))
inet_ntop(AF_INET, &((struct sockaddr_in *)i->ifa_dstaddr)->sin_addr, dst, sizeof dst);
bprintf(&b, "%s %s%s%s %s\n", i->ifa_name, a, dst[0] ? " -> " : "", dst, (i->ifa_flags & IFF_UP) ? "up" : "down");
}
}
freeifaddrs(ifa);
}
bprintf(&b, "pppd: %s, configd: %s\n", pppd_pid ? "actif" : "arrêté", net_published ? "publié" : "non publié");
reply(fd, 1, NULL, b.p, b.n);
free(b.p);
}
static void handle(int fd, char *line)
{
char *args[64];
int n = 0;
for (char *t = strtok(line, "\t"); t && n < 64; t = strtok(NULL, "\t")) args[n++] = t;
if (!n) return;
char *cmd = args[0];
char **a = args + 1;
int na = n - 1;
if (!strcmp(cmd, "ping")) reply_str(fd, 1, "emuagent %s", VERSION);
else if (!strcmp(cmd, "exec")) cmd_exec(fd, a, na);
else if (!strcmp(cmd, "launch")) cmd_launch(fd, a, na);
else if (!strcmp(cmd, "front")) cmd_front(fd);
else if (!strcmp(cmd, "screen")) cmd_screen(fd, 0);
else if (!strcmp(cmd, "wake")) cmd_screen(fd, 1);
else if (!strcmp(cmd, "fetch")) cmd_fetch(fd, a, na);
else if (!strcmp(cmd, "sc")) cmd_sc(fd, a, na);
else if (!strcmp(cmd, "routes")) cmd_routes(fd);
else if (!strcmp(cmd, "settime")) {
/* Réglage explicite de l'heure pour les tests (secondes Unix). */
struct timeval tv = { na ? strtol(a[0], NULL, 10) : 0, 0 };
if (na && !settimeofday(&tv, NULL)) {
notify_post("com.apple.system.clock_set"); /* SpringBoard et les apps mettent leur horloge à jour */
reply_str(fd, 1, "%ld", (long)tv.tv_sec);
}
else reply_str(fd, 0, "settimeofday : %s", strerror(errno));
}
else if (!strcmp(cmd, "apps")) cmd_apps(fd);
else if (!strcmp(cmd, "put")) cmd_put(fd, a, na);
else if (!strcmp(cmd, "get")) cmd_get(fd, a, na);
else if (!strcmp(cmd, "ls")) cmd_ls(fd, a, na);
else if (!strcmp(cmd, "rm")) { if (na && !rm_rf(a[0])) reply_str(fd, 1, "supprimé"); else reply_str(fd, 0, "rm : %s", strerror(errno)); }
else if (!strcmp(cmd, "mkdir")) { if (na && !mkdirs(a[0])) reply_str(fd, 1, "ok"); else reply_str(fd, 0, "mkdir : %s", strerror(errno)); }
else if (!strcmp(cmd, "ps")) cmd_ps(fd);
else if (!strcmp(cmd, "kill")) cmd_kill(fd, a, na);
else if (!strcmp(cmd, "net")) cmd_net(fd);
else if (!strcmp(cmd, "respring")) reply_str(fd, kill_by_name("SpringBoard", SIGTERM) > 0, "SpringBoard relancé");
else if (!strcmp(cmd, "sync")) { sync(); reply_str(fd, 1, "ok"); }
else reply_str(fd, 0, "commande inconnue : %s (ping exec launch fetch sc routes settime screen wake front apps put get ls rm mkdir ps kill net respring sync)", cmd);
}
static void serve_client(int fd)
{
char line[8192];
size_t len = 0;
for (;;) {
char c;
ssize_t r = read(fd, &c, 1);
if (r <= 0) break;
if (c == '\n') {
line[len] = 0;
handle(fd, line);
len = 0;
} else if (len < sizeof line - 1) {
line[len++] = c;
}
}
close(fd);
}
/* diagnostic : si /private/var/root/emuagent-ttytest existe, lit la ligne série brute pendant 20 s avant pppd */
static void tty_selftest(void)
{
struct stat st;
if (stat("/private/var/root/emuagent-ttytest", &st)) return;
int fd = open(PPP_TTY, O_RDWR | O_NOCTTY | O_NONBLOCK);
clog_("ttytest : open %s -> %d (%s)", PPP_TTY, fd, fd < 0 ? strerror(errno) : "ok");
if (fd < 0) return;
struct termios t;
tcgetattr(fd, &t); cfmakeraw(&t); cfsetspeed(&t, B115200); t.c_cflag |= CLOCAL | CREAD; t.c_cflag &= ~CRTSCTS;
tcsetattr(fd, TCSANOW, &t);
write(fd, "EMUAGENT-TTYTEST\n", 17);
time_t start = time(NULL);
size_t total = 0;
while (time(NULL) - start < 20) {
char b[256];
ssize_t n = read(fd, b, sizeof b);
if (n > 0) {
total += n;
char hex[64] = "";
for (int i = 0; i < n && i < 16; i++) sprintf(hex + 3 * i, "%02x ", (unsigned char)b[i]);
clog_("ttytest : %zd octets : %s", n, hex);
} else {
usleep(100000);
}
}
clog_("ttytest : %zu octets reçus au total", total);
close(fd);
}
/* iPhone OS 2.x does not implement the public idle-sleep assertions.
* Its root power domain does support SleepDisabled, which is required
* because this emulated SoC cannot resume from deep sleep. */
static void prevent_idle_sleep(void)
{
void *h = dlopen("/System/Library/Frameworks/IOKit.framework/IOKit", RTLD_NOW);
CFMutableDictionaryRef (*matching)(const char *) = h ? dlsym(h, "IOServiceMatching") : NULL;
unsigned (*get_service)(unsigned, CFDictionaryRef) = h ? dlsym(h, "IOServiceGetMatchingService") : NULL;
int (*set_property)(unsigned, CFStringRef, CFTypeRef) = h ? dlsym(h, "IORegistryEntrySetCFProperty") : NULL;
int (*release)(unsigned) = h ? dlsym(h, "IOObjectRelease") : NULL;
if (!matching || !get_service || !set_property || !release) {
clog_("controle de veille IOKit indisponible");
return;
}
unsigned root = get_service(0, matching("IOPMrootDomain"));
if (!root) {
clog_("IOPMrootDomain indisponible");
return;
}
int rc = set_property(root, CFSTR("SleepDisabled"), kCFBooleanTrue);
release(root);
clog_("SleepDisabled: 0x%x", rc);
}
int main(void)
{
con = fopen("/dev/console", "w");
if (con) setvbuf(con, NULL, _IONBF, 0);
signal(SIGPIPE, SIG_IGN);
clog_("version %s, port %d", VERSION, PORT);
prevent_idle_sleep();
tty_selftest();
/* agent relancé (mise à jour, crash) : le pppd de l'instance précédente tient encore la ligne série */
if (kill_by_name("pppd", SIGTERM) > 0) { clog_("ancien pppd arrêté"); sleep(2); }
int s = socket(AF_INET, SOCK_STREAM, 0), one = 1;
fcntl(s, F_SETFD, FD_CLOEXEC); /* sinon pppd hérite du port d'écoute */
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one);
struct sockaddr_in a = { .sin_family = AF_INET, .sin_port = htons(PORT), .sin_addr.s_addr = htonl(INADDR_ANY) };
while (bind(s, (struct sockaddr *)&a, sizeof a)) { clog_("bind : %s, nouvel essai", strerror(errno)); sleep(2); }
listen(s, 4);
for (;;) {
pid_t z;
int zs;
while ((z = waitpid(-1, &zs, WNOHANG)) > 0) {
if (z == pppd_pid) { clog_("pppd terminé (statut %d), relance", zs); pppd_pid = 0; net_published = 0; }
}
net_tick();
fd_set rs;
struct timeval tv = { 2, 0 };
FD_ZERO(&rs);
FD_SET(s, &rs);
if (select(s + 1, &rs, NULL, NULL, &tv) > 0) {
int c = accept(s, NULL, NULL);
if (c >= 0) {
fcntl(c, F_SETFD, FD_CLOEXEC);
/* un processus par client : une commande longue ou bloquée ne gêne ni les autres ni le suivi réseau */
pid_t p = fork();
if (p == 0) {
close(s);
struct timeval to = { 300, 0 };
setsockopt(c, SOL_SOCKET, SO_RCVTIMEO, &to, sizeof to);
serve_client(c);
_exit(0);
}
close(c);
}
}
}
}
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>com.apple.springboard.launchapplications</key><true/>
<key>com.apple.springboard.debugapplications</key><true/>
<key>com.apple.springboard.opensensitiveurl</key><true/>
</dict></plist>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>CFBundleExecutable</key><string>HelloEmu</string>
<key>CFBundleIdentifier</key><string>com.allovoisins.helloemu</string>
<key>CFBundleName</key><string>HelloEmu</string>
<key>CFBundleDisplayName</key><string>HelloEmu</string>
<key>CFBundlePackageType</key><string>APPL</string>
<key>CFBundleVersion</key><string>1.0</string>
<key>MinimumOSVersion</key><string>2.0</string>
</dict></plist>
+27
View File
@@ -0,0 +1,27 @@
#import <UIKit/UIKit.h>
@interface HelloApp : NSObject <UIApplicationDelegate> { UIWindow *window; }
@end
@implementation HelloApp
- (void)applicationDidFinishLaunching:(UIApplication *)app {
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
window.backgroundColor = [UIColor colorWithRed:0.1 green:0.4 blue:0.8 alpha:1];
UILabel *l = [[UILabel alloc] initWithFrame:CGRectMake(0, 200, 320, 60)];
l.text = @"HelloEmu OK";
l.textAlignment = UITextAlignmentCenter;
l.font = [UIFont boldSystemFontOfSize:32];
l.textColor = [UIColor whiteColor];
l.backgroundColor = [UIColor clearColor];
[window addSubview:l];
[window makeKeyAndVisible];
NSLog(@"HelloEmu: launched");
}
@end
int main(int argc, char *argv[]) {
NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init];
int r = UIApplicationMain(argc, argv, nil, @"HelloApp");
[p release];
return r;
}
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>TouchProbe</string>
<key>CFBundleIdentifier</key>
<string>com.ioslegacy.touchprobe</string>
<key>CFBundleName</key>
<string>TouchProbe</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>2.0</string>
</dict>
</plist>
+10
View File
@@ -0,0 +1,10 @@
# Sonde de calibration tactile pour iPhone OS 2.
SDK := /opt/theos/sdks/iPhoneOS2.2.1.sdk
CC := /opt/cctools/bin/arm-apple-darwin9-gcc
all: TouchProbe.app/TouchProbe
TouchProbe.app/TouchProbe: main.m Info.plist
mkdir -p TouchProbe.app
$(CC) -isysroot $(SDK) -arch armv6 -miphoneos-version-min=2.0 -O2 -o $@ main.m -framework UIKit -framework Foundation
cp Info.plist TouchProbe.app/
ldid -S $@
.PHONY: all
+30
View File
@@ -0,0 +1,30 @@
#import <UIKit/UIKit.h>
@interface ProbeView : UIView
@end
@implementation ProbeView
- (void)record:(NSSet *)touches phase:(NSString *)phase {
CGPoint p=[[touches anyObject] locationInView:self];
UIView *marker=[self viewWithTag:99];
if (!marker) {
marker=[[[UIView alloc] initWithFrame:CGRectMake(0,0,0,0)] autorelease];
marker.tag=99; marker.backgroundColor=[UIColor blueColor]; marker.userInteractionEnabled=NO;
[self addSubview:marker];
}
marker.frame=CGRectMake(p.x-2,p.y-2,5,5);
NSLog(@"TOUCH %@ %.1f %.1f",phase,p.x,p.y);
}
- (void)touchesBegan:(NSSet *)t withEvent:(UIEvent *)e { [self record:t phase:@"begin"]; }
- (void)touchesMoved:(NSSet *)t withEvent:(UIEvent *)e { [self record:t phase:@"move"]; }
- (void)touchesEnded:(NSSet *)t withEvent:(UIEvent *)e { [self record:t phase:@"end"]; }
@end
@interface ProbeApp : NSObject <UIApplicationDelegate> { UIWindow *window; }
@end
@implementation ProbeApp
- (void)applicationDidFinishLaunching:(UIApplication *)app {
window=[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
ProbeView *v=[[ProbeView alloc] initWithFrame:CGRectMake(0,20,320,460)];
v.backgroundColor=[UIColor whiteColor];[window addSubview:v];[v release];[window makeKeyAndVisible];
NSLog(@"TouchProbe ready window=%@ view=%@",NSStringFromCGRect(window.frame),NSStringFromCGRect(v.frame));
}
@end
int main(int argc,char **argv) { NSAutoreleasePool *p=[[NSAutoreleasePool alloc] init];int rc=UIApplicationMain(argc,argv,nil,@"ProbeApp");[p release];return rc; }
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/env python3
# armdis.py FICHIER BASE DEBUT FIN [arm] — désassemble (Thumb par défaut) avec résolution des ldr pc-relatifs
import sys, struct, capstone
f, base, start, end = sys.argv[1], int(sys.argv[2], 16), int(sys.argv[3], 16), int(sys.argv[4], 16)
mode = capstone.CS_MODE_ARM if len(sys.argv) > 5 else capstone.CS_MODE_THUMB
d = open(f, 'rb').read()
md = capstone.Cs(capstone.CS_ARCH_ARM, mode); md.detail = True; md.skipdata = True
def s(a):
o = a - base
if 0 <= o < len(d):
e = d.find(b'\0', o, o + 80)
t = d[o:e]
if len(t) >= 4 and all(32 <= c < 127 for c in t): return ' "%s"' % t.decode()
return ''
for i in md.disasm(d[start - base:end - base], start):
x = ''
if i.mnemonic.startswith('ldr') and '[pc' in i.op_str:
disp = i.operands[1].mem.disp
la = ((i.address + (4 if mode == capstone.CS_MODE_THUMB else 8)) & ~3) + disp
v = struct.unpack('<I', d[la - base:la - base + 4])[0]
x = ' ; =%#x%s' % (v, s(v))
elif i.mnemonic in ('bl', 'blx', 'b', 'b.w'):
pass
print('%08x: %-8s %s%s' % (i.address, i.mnemonic, i.op_str, x))
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/env python3
# Dump d'un DeviceTree Apple (format img3 déchiffré, "flat" iBoot)
import struct, sys
d = open(sys.argv[1], 'rb').read()
if d[:4] == b'3gmI': # img3 -> DATA tag
o = 20
while o < len(d):
magic, total, dlen = struct.unpack('<4sII', d[o:o+12])
if magic == b'ATAD': d = d[o+12:o+12+dlen]; break
o += total
def node(o, depth, path):
np, nc = struct.unpack('<II', d[o:o+8]); o += 8
props = {}
for _ in range(np):
name = d[o:o+32].split(b'\0')[0].decode(); ln = struct.unpack('<I', d[o+32:o+36])[0] & 0x7fffffff
props[name] = d[o+36:o+36+ln]; o += 36 + ((ln + 3) & ~3)
nm = props.get('name', b'?').split(b'\0')[0].decode()
p = path + '/' + nm
reg = props.get('reg')
comp = props.get('compatible', b'').split(b'\0')[0].decode()
regs = ' '.join('%08x+%x' % struct.unpack('<II', reg[i:i+8]) for i in range(0, len(reg) - 7, 8)) if reg else ''
print('%s%s [%s] reg=%s' % (' ' * depth, p, comp, regs))
for _ in range(nc): o = node(o, depth + 1, p)
return o
node(0, 0, '')
+244
View File
@@ -0,0 +1,244 @@
#!/usr/bin/env python3
"""Client de l'agent de l'iPod émulé (emuagent, via ipod-netd sur 127.0.0.1:7788).
ipod-agent ping
ipod-agent apps bundle-id, nom et chemin des apps installées
ipod-agent launch BUNDLE_ID [--log F] [--url URL]
lance une app (stdout/stderr, donc NSLog, dans F sur l'iPod ; URL à ouvrir)
ipod-agent fetch http://hôte/chemin requête HTTP depuis l'iPod (test réseau : DNS + TCP)
ipod-agent front app au premier plan
ipod-agent screen | wake état du verrouillage / réveil sans clavier de code (agent v11)
ipod-agent unlock réveille puis glisse et vérifie le déverrouillage (sans code)
ipod-agent exec PROG [ARGS...] exécute un programme (pas de shell : chemin absolu)
ipod-agent ps | net | respring | sync
ipod-agent kill PID|NOM [SIGNAL]
ipod-agent ls CHEMIN | rm CHEMIN | mkdir CHEMIN
ipod-agent get CHEMIN_IPOD [FICHIER_LOCAL]
ipod-agent put FICHIER_LOCAL CHEMIN_IPOD [MODE]
ipod-agent push-app MonApp.app copie l'app, synchronise et redémarre l'iPod (NAND conservée)
ipod-agent wait [SECONDES] attend que l'agent réponde (réseau PPP monté)
Code de sortie : 0 si OK, 1 si erreur, code de sortie du programme pour exec.
"""
import os, socket, subprocess, sys, tempfile, time
HOST = os.environ.get("IPOD_AGENT_HOST", "127.0.0.1")
PORT = int(os.environ.get("IPOD_AGENT_PORT", "7788"))
class Agent:
def __init__(self, timeout=330):
self.s = socket.create_connection((HOST, PORT), timeout=timeout)
self.f = self.s.makefile("rb")
def call(self, *fields, data=None, sink=None):
line = "\t".join(str(x) for x in fields) + "\n"
self.s.sendall(line.encode())
if data is not None:
if hasattr(data, "read"):
remaining = int(fields[-1])
while remaining:
chunk = data.read(min(65536, remaining))
if not chunk:
raise ConnectionError("fichier local tronqué pendant l'upload")
self.s.sendall(chunk)
remaining -= len(chunk)
else:
self.s.sendall(data)
hdr = self.f.readline(4096).decode().rstrip("\n")
if not hdr:
raise ConnectionError("connexion fermée par l'agent")
parts = hdr.split(" ", 2)
if len(parts) < 2:
raise ConnectionError("en-tête invalide")
n = int(parts[1])
if n < 0 or parts[0] not in ("OK", "ERR"):
raise ConnectionError("en-tête invalide")
body = bytearray()
received = 0
while received < n:
chunk = self.f.read(min(65536, n - received))
if not chunk:
raise ConnectionError(f"réponse tronquée : {received} octets reçus sur {n}")
received += len(chunk)
if sink is not None and parts[0] == "OK":
sink.write(chunk)
else:
body.extend(chunk)
body = bytes(body)
return parts[0] == "OK", body, parts[2] if len(parts) > 2 else None
def out(body):
sys.stdout.buffer.write(body)
if body and not body.endswith(b"\n"):
sys.stdout.buffer.write(b"\n")
def emu_home():
tools_home = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
default_home = tools_home if os.path.isdir(os.path.join(tools_home, "images")) else os.path.expanduser("~/ipod-emu")
return os.environ.get("IPOD_EMU_HOME", default_home)
def unlock(a):
def screen():
ok, body, _ = a.call("screen")
if not ok:
sys.exit("État d'écran indisponible (agent v11 requis) : " + body.decode())
return dict(field.split("=", 1) for field in body.decode().split())
state = screen()
if state.get("locked") == "0":
print("Écran déverrouillé")
return
if state.get("passcode") != "0":
sys.exit("Un code de verrouillage est activé : déverrouiller manuellement.")
# Le pilote tactile doit avoir terminé son démarrage avant tout glissement.
try:
with open(os.path.join(emu_home(), "run", "serial.log"), "rb") as f:
serial = f.read()
except OSError as e:
sys.exit(f"Console de session inaccessible : {e}")
helper_ready = any(b"SCHelper" in line and b'"EXIT"' in line for line in serial.splitlines())
if serial.count(b"AppleMultitouchSPIUserClient::attach") < 2 or not helper_ready:
sys.exit("Contrôleur tactile pas encore prêt : attendre ipod-emu wait.")
qmp = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ipod-qmp")
sock = os.environ.get("IPOD_QMP", os.path.join(emu_home(), "run", "qmp.sock"))
for attempt in range(3):
ok, body, _ = a.call("wake")
if not ok:
sys.exit(body.decode())
time.sleep(2) # laisser le pilote réactiver l'écran et le tactile
# Terminer le geste au bord droit du curseur de déverrouillage.
subprocess.run([qmp, "-s", sock, "swipe", "60", "450", "319", "450"], check=True)
time.sleep(2)
state = screen()
if state.get("locked") == "0":
print("Écran déverrouillé")
return
if state.get("passcode") != "0":
sys.exit("Un code de verrouillage est activé : déverrouiller manuellement.")
sys.exit("Écran toujours verrouillé après trois glissements.")
def push_app(a, app):
home = emu_home()
run = os.path.join(home, "run")
try:
with open(os.path.join(run, "nand", ".ipod-nand-format")) as f:
layout = f.read().strip()
with open(os.path.join(run, "fw")) as f:
firmware = f.read().strip()
with open(os.path.join(home, "images", firmware, "machine-opts")) as f:
machine_opts = f.read()
except OSError:
sys.exit("push-app nécessite une image locale avec persistance NAND (ipod-mkimage / ipod-build-fw).")
if layout != "vsvfl-production-v1" or ",nand-write=on" not in machine_opts:
sys.exit("Écritures NAND désactivées : reconstruire l'image avant push-app.")
app = app.rstrip("/")
dest = "/Applications/" + os.path.basename(app)
a.call("rm", dest)
n = 0
for root, dirs, files in os.walk(app):
for f in files:
p = os.path.join(root, f)
rel = os.path.relpath(p, app)
st = os.stat(p)
with open(p, "rb") as fh:
data = fh.read()
mode = "755" if (st.st_mode & 0o111 or data[:4] in (b"\xce\xfa\xed\xfe", b"\xca\xfe\xba\xbe")) else "644"
ok, body, _ = a.call("put", f"{dest}/{rel}", mode, len(data), data=data)
if not ok:
sys.exit(body.decode())
n += 1
# SpringBoard ne relit la liste des apps qu'à son démarrage
a.call("rm", "/private/var/mobile/Library/Caches/com.apple.mobile.installation.plist")
ok, body, _ = a.call("sync")
if not ok:
sys.exit(body.decode())
# Le cache d'installation iOS 2.x peut bloquer SpringBoard lors d'une
# relance seule. Un démarrage complet le reconstruit, sans refaire l'image.
a.f.close()
a.s.close()
launcher = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ipod-emu")
print(f"{n} fichiers copiés dans {dest}, redémarrage de l'iPod", flush=True)
subprocess.run([launcher, "stop"], check=True)
subprocess.run([launcher, "start", "-w", firmware, "-W"], check=True)
def main():
args = sys.argv[1:]
if not args or args[0] in ("-h", "--help"):
sys.exit(__doc__)
cmd, rest = args[0], args[1:]
if cmd == "wait":
limit = float(rest[0]) if rest else 300
t0 = time.time()
while True:
try:
ok, body, _ = Agent(timeout=5).call("ping")
print(body.decode(), f"({time.time() - t0:.0f} s)")
return
except OSError:
if time.time() - t0 > limit:
sys.exit("agent injoignable (ipod-netd lancé ? réseau PPP monté ?)")
time.sleep(2)
try:
a = Agent()
except OSError as e:
sys.exit(f"agent injoignable sur {HOST}:{PORT} ({e}) — ipod-netd lancé ?")
if cmd == "unlock":
a.s.settimeout(15)
unlock(a)
return
if cmd == "get":
tmp = None
try:
if len(rest) > 1:
destination = os.path.abspath(rest[1])
fd, tmp = tempfile.mkstemp(prefix=".ipod-get-", dir=os.path.dirname(destination))
with os.fdopen(fd, "wb") as fh:
ok, body, _ = a.call("get", rest[0], sink=fh)
fh.flush()
os.fsync(fh.fileno())
if ok:
os.replace(tmp, destination)
tmp = None
else:
ok, body, _ = a.call("get", rest[0], sink=sys.stdout.buffer)
if not ok:
sys.exit(body.decode())
finally:
if tmp is not None:
os.unlink(tmp)
return
if cmd == "put":
mode = rest[2] if len(rest) > 2 else ("755" if os.access(rest[0], os.X_OK) else "644")
with open(rest[0], "rb") as fh:
ok, body, _ = a.call("put", rest[1], mode, os.fstat(fh.fileno()).st_size, data=fh)
elif cmd == "push-app":
push_app(a, rest[0])
return
elif cmd == "launch":
fields = ["launch", rest[0], rest[rest.index("--log") + 1] if "--log" in rest else "-"]
if "--url" in rest:
fields.append(rest[rest.index("--url") + 1])
ok, body, _ = a.call(*fields)
else:
ok, body, info = a.call(cmd, *rest)
if cmd == "exec":
out(body)
sys.exit(int(info) if ok and info else (0 if ok else 1))
out(body)
sys.exit(0 if ok else 1)
if __name__ == "__main__":
try:
main()
except (OSError, ValueError, ConnectionError) as e:
sys.exit(f"Erreur de transfert ou de protocole : {e}")
+54
View File
@@ -0,0 +1,54 @@
#!/usr/bin/env bash
# Reconstruit une image firmware de l'émulateur à partir de l'IPSW officiel iPod2,1.
#
# ipod-build-fw [BUILD] (défaut : 5H11a = iPhone OS 2.2.1)
#
# Étapes : téléchargement de l'IPSW (serveurs Apple) et des clés (api.ipsw.me), NOR (LLB, iBoot, DeviceTree, logos),
# déchiffrement du rootfs, adaptations pour l'émulateur (mkbase-rootfs), NAND. Résultat : ~/ipod-emu/images/BUILD/
set -euo pipefail
BUILD=${1:-5H11a}
E="${IPOD_EMU_HOME:-$HOME/ipod-emu}"
W="$E/fw-$BUILD"
OUT="$E/images/$BUILD"
BOOTROM="$E/images/5F138/bootrom" # bootrom S5L8720 (release n72ap_v1 de qemu-ios)
case $BUILD in
5F138) NAND_VERSION=1 ;; # 2.1.1 : driver NAND au format 1
*) NAND_VERSION=2 ;; # 2.2.x : iBoot 385.49 exige le format 2
esac
mkdir -p "$W/enc" "$W/dec" "$OUT" "$E/ipsw"
info=$(curl -fsS "https://api.ipsw.me/v4/ipsw/iPod2,1/$BUILD")
url=$(python3 -c 'import json,sys; print(json.load(sys.stdin)["url"])' <<<"$info")
ipsw="$E/ipsw/$(basename "$url")"
[ -f "$ipsw" ] || { echo "Téléchargement de $url"; curl -fL -o "$ipsw" "$url"; }
curl -fsS "https://api.ipsw.me/v4/keys/ipsw/iPod2,1/$BUILD" > "$W/keys.json"
key() { python3 -c 'import json,sys; k=[k for k in json.load(open(sys.argv[1]))["keys"] if k["image"]==sys.argv[2]][0]; print(k["filename"], k["key"])' "$W/keys.json" "$1"; }
echo "== NOR"
unzip -qo "$ipsw" 'Firmware/all_flash/*' -d "$W/enc"
F=$(echo "$W"/enc/Firmware/all_flash/all_flash.n72ap.production)
nor="$E/qemu-ios-generate-nor"
rm -rf "$nor/data" && mkdir "$nor/data"
for pair in applelogo:applelogo batterylow0:batterylow0 batterylow1:batterylow1 dtree:DeviceTree \
glyphcharging:glyphcharging glyphplugin:glyphplugin iboot:iBoot llb:LLB \
needservice:needservice recoverymode:recoverymode; do
cp "$F/${pair#*:}"*.img3 "$nor/data/${pair%%:*}.img3"
done
(cd "$nor" && ./generate_nor >/dev/null && mv nor.bin "$OUT/nor.bin")
echo "== rootfs"
read -r rootfs_dmg rootfs_key < <(key RootFS)
unzip -qo "$ipsw" "$rootfs_dmg" -d "$W/enc"
"$E/dmg2img/vfdecrypt" -i "$W/enc/$rootfs_dmg" -k "$rootfs_key" -o "$W/dec/rootfs.dmg" >/dev/null
part=$("$E/dmg2img/dmg2img" -l "$W/dec/rootfs.dmg" 2>&1 | sed -n 's/^partition \([0-9]*\): .*Apple_HFS.*/\1/p' | head -1)
"$E/dmg2img/dmg2img" -p "$part" "$W/dec/rootfs.dmg" -o "$W/dec/rootfs-$BUILD.img" >/dev/null 2>&1
"$E/bin/mkbase-rootfs" "$W/dec/rootfs-$BUILD.img" "$OUT/rootfs.img"
echo "== NAND"
"$E/gen-v1/mknand" "$OUT/rootfs.img" "$OUT/nand" "$NAND_VERSION"
echo "$NAND_VERSION" > "$OUT/nand-version"
echo ",legacy-nor=off,nand-write=on" > "$OUT/machine-opts"
ln -sf "$BOOTROM" "$OUT/bootrom"
echo "Image prête : $OUT → ipod-emu start -f -W -w $BUILD"
+143
View File
@@ -0,0 +1,143 @@
#!/usr/bin/env bash
# Lance / arrête l'émulateur iPod Touch 2G (qemu-ios, N72AP).
set -euo pipefail
TOOLS_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
DEFAULT_EMU_HOME="$HOME/ipod-emu"
# Les outils peuvent être utilisés directement depuis un home de toolchain déplacé.
[ ! -d "$TOOLS_DIR/../images" ] || DEFAULT_EMU_HOME=$(cd "$TOOLS_DIR/.." && pwd)
EMU_HOME="${IPOD_EMU_HOME:-$DEFAULT_EMU_HOME}"
QEMU="${IPOD_QEMU:-$(dirname "$EMU_HOME")/qemu-ios/build/qemu-system-arm}"
QEMU_DATA="${IPOD_QEMU_DATA:-$(dirname "$(dirname "$QEMU")")/pc-bios}"
FW="${IPOD_FW:-5H11a}" # dossier $EMU_HOME/images/<FW>/ : bootrom, nor.bin, nand/ (5H11a = 2.2.1, 5F138 = 2.1.1)
RUN="$EMU_HOME/run"
VNC="${IPOD_VNC:-127.0.0.1:0}" # :0 => port 5900
usage() {
cat <<USAGE
Usage : ipod-emu start [-f] [-W] [-w FW] démarre (headless, VNC $VNC, QMP $RUN/qmp.sock)
-f repart d'une NAND vierge (sinon garde l'état de la session précédente)
-W attend que SpringBoard soit prêt à recevoir des touches
-w FW firmware à utiliser (défaut : $FW ; dispo : $(ls "$EMU_HOME/images" 2>/dev/null | tr '\n' ' '))
ipod-emu wait [SECONDES] attend SpringBoard (défaut 300 s) — n'envoyez PAS de touch avant :
le pilote tactile doit avoir terminé son démarrage
ipod-emu stop | status | log | console
Pilotage (écran, touch, boutons) : ipod-qmp --help
USAGE
exit "${1:-0}"
}
ready() {
local n; n=$(grep -ac "AppleMultitouchSPIUserClient::attach" "$RUN/serial.log" 2>/dev/null); [ "${n:-0}" -ge 2 ] &&
grep -aq 'SCHelper.*"EXIT"' "$RUN/serial.log"
}
wait_ready() {
local t=${1:-300} s=$SECONDS
# Le RTC D1759 fournit l'heure avant le démarrage des services iOS.
until ready; do
[ -n "$(pid)" ] || { echo "L'émulateur s'est arrêté (voir $RUN/qemu.log)" >&2; return 1; }
grep -aq 'Fatal Exception\|MACH Reboot' "$RUN/serial.log" && { echo "Kernel panic / reboot (voir $RUN/serial.log)" >&2; return 1; }
[ $((SECONDS - s)) -lt "$t" ] || { echo "Timeout : SpringBoard pas prêt après ${t}s" >&2; return 1; }
sleep 2
done
sleep 3
"$(dirname "$0")/ipod-agent" unlock
echo "SpringBoard prêt et déverrouillé ($((SECONDS - s)) s)"
}
pid() { [ -f "$RUN/qemu.pid" ] && kill -0 "$(cat "$RUN/qemu.pid")" 2>/dev/null && cat "$RUN/qemu.pid"; }
stop_netd() {
local netd_pid
netd_pid=$(cat "$RUN/netd.pid" 2>/dev/null || true)
if [ -n "$netd_pid" ] && [ -r "/proc/$netd_pid/comm" ] &&
[ "$(cat "/proc/$netd_pid/comm")" = "ipod-netd" ]; then
kill "$netd_pid" 2>/dev/null || true
fi
rm -f "$RUN/netd.pid"
}
cmd=${1:-}; shift || true
case $cmd in
start)
fresh=0 waitr=0
while getopts "fWw:" o; do case $o in f) fresh=1 ;; W) waitr=1 ;; w) FW=$OPTARG ;; *) usage 1 ;; esac; done
img="$EMU_HOME/images/$FW"
[ -d "$img" ] || { echo "Firmware inconnu : $img" >&2; exit 1; }
if [ -n "$(pid)" ]; then
echo "Déjà lancé (pid $(pid))"
[ $waitr = 0 ] || wait_ready
exit 0
fi
[ -x "$QEMU" ] || { echo "QEMU introuvable : $QEMU (définir IPOD_QEMU)" >&2; exit 1; }
[ -r "$img/bootrom" ] || { echo "BootROM introuvable : $img/bootrom (vérifier les liens après déplacement du home)" >&2; exit 1; }
[ -x "$EMU_HOME/netd/ipod-netd" ] || { echo "Relais réseau introuvable : $EMU_HOME/netd/ipod-netd" >&2; exit 1; }
"$QEMU" --version >/dev/null
qemu_data_args=()
[ ! -d "$QEMU_DATA/keymaps" ] || qemu_data_args=(-L "$QEMU_DATA")
mkdir -p "$RUN"
if [ $fresh = 1 ] || [ ! -d "$RUN/nand" ] || [ "$(cat "$RUN/fw" 2>/dev/null)" != "$FW" ]; then
rm -rf "$RUN/nand"; cp -rL "$img/nand" "$RUN/nand"; echo "$FW" > "$RUN/fw"
fi
stop_netd
rm -f "$RUN/qmp.sock" "$RUN/serial.log"
mopts=$(cat "$img/machine-opts" 2>/dev/null || true) # ex. ",legacy-nor=off"
if [[ "$mopts" == *",nand-write=on"* ]] &&
[ "$(cat "$RUN/nand/.ipod-nand-format" 2>/dev/null || true)" != "vsvfl-production-v1" ]; then
echo "NAND incompatible avec les écritures : reconstruire l'image avec ipod-mkimage / ipod-build-fw, puis démarrer avec -f." >&2
exit 1
fi
nohup setsid "$QEMU" -M "iPod-Touch,bootrom=$img/bootrom,nand=$RUN/nand,nor=$img/nor.bin$mopts" \
"${qemu_data_args[@]}" -cpu max -m 2G -serial "file:$RUN/serial.log" -serial "unix:$RUN/uart1.sock,server=on,wait=off" \
-display none -vnc "$VNC" -qmp "unix:$RUN/qmp.sock,server,nowait" \
-pidfile "$RUN/qemu.pid" ${IPOD_QEMU_EXTRA:-} > "$RUN/qemu.log" 2>&1 &
qemu_pid=$!
for _ in $(seq 50); do
if ! kill -0 "$qemu_pid" 2>/dev/null; then
echo "Échec du démarrage de QEMU :" >&2
cat "$RUN/qemu.log" >&2
exit 1
fi
[ ! -S "$RUN/qmp.sock" ] || [ -z "$(pid)" ] || break
sleep 0.2
done
if [ ! -S "$RUN/qmp.sock" ] || [ -z "$(pid)" ]; then
kill "$qemu_pid" 2>/dev/null || true
echo "QEMU n'a pas ouvert son socket QMP (voir $RUN/qemu.log)" >&2
exit 1
fi
# réseau : PPP sur l'UART1 -> slirp (NAT, DNS, 127.0.0.1:7788 -> agent de l'iPod)
stop_netd
nohup setsid "$EMU_HOME/netd/ipod-netd" -s "$RUN/uart1.sock" ${IPOD_NETD_FWD:-} > "$RUN/netd.log" 2>&1 &
netd_pid=$!
echo "$netd_pid" > "$RUN/netd.pid"
sleep 0.3
if ! kill -0 "$netd_pid" 2>/dev/null; then
echo "Échec du relais réseau :" >&2
cat "$RUN/netd.log" >&2
"$(dirname "$0")/ipod-qmp" -s "$RUN/qmp.sock" quit 2>/dev/null || kill "$qemu_pid"
stop_netd
exit 1
fi
echo "Démarré ($FW) — pid $(pid), VNC $VNC. SpringBoard apparaît en ~1 min."
echo "Logs iOS : $RUN/serial.log"
[ $waitr = 0 ] || wait_ready
;;
wait) wait_ready "${1:-300}" ;;
stop)
p=$(pid) || { stop_netd; echo "Pas lancé"; exit 0; }
"$(dirname "$0")/ipod-qmp" -s "$RUN/qmp.sock" quit 2>/dev/null || kill "$p"
for _ in $(seq 50); do kill -0 "$p" 2>/dev/null || break; sleep 0.2; done
kill -0 "$p" 2>/dev/null && kill -9 "$p"
stop_netd
echo "Arrêté"
;;
status)
if p=$(pid); then echo "En marche (pid $p, firmware $(cat "$RUN/fw"), VNC $VNC)"; else echo "Arrêté"; exit 1; fi
;;
log) tail -f "$RUN/serial.log" ;;
console) tail -f "$RUN/qemu.log" ;;
-h|--help|help) usage ;;
*) usage 1 ;;
esac
+162
View File
@@ -0,0 +1,162 @@
#!/usr/bin/env python3
"""Construit une image NAND pour l'émulateur iPod Touch 2G avec nos apps / paquets dedans.
ipod-mkimage [-b BASE] [-o NOM] fichier.app|fichier.ipa|fichier.deb|dossier/ [...]
-b BASE firmware de base (défaut : 5H11a = iPhone OS 2.2.1 ; 5F138 = 2.1.1) — utilise ~/ipod-emu/images/BASE/rootfs.img
-o NOM nom de l'image produite (défaut : BASE-apps) — ensuite : ipod-emu start -f -w NOM
Les .app et .ipa sont installés dans /Applications (apps système : pas besoin d'installd,
la signature ldid suffit), les .deb sont décompressés à la racine comme dpkg le ferait, un dossier est copié tel quel
à la racine (arborescence « overlay » : ses binaires Mach-O sont signés avec ldid).
"""
import argparse, io, lzma, gzip, bz2, os, re, shutil, subprocess, sys, tarfile, tempfile, zipfile
EMU = os.environ.get("IPOD_EMU_HOME", os.path.expanduser("~/ipod-emu"))
HFSPLUS = os.path.join(EMU, "xpwn/b/hfs/hfsplus")
MKNAND = os.path.join(EMU, "gen-v1/mknand")
MACHO = (b"\xce\xfa\xed\xfe", b"\xca\xfe\xba\xbe")
def hfs(img, *args, check=True):
r = subprocess.run([HFSPLUS, img, *args], capture_output=True, text=True)
if check and r.returncode:
sys.exit(f"hfsplus {' '.join(args)} : {r.stderr or r.stdout}")
return r.stdout
def exists(img, path):
return "No such file" not in hfs(img, "ls", path, check=False) and hfs(img, "ls", path, check=False).strip() != ""
def mkdirs(img, path):
cur = ""
for part in path.strip("/").split("/"):
cur += "/" + part
if not exists(img, cur):
hfs(img, "mkdir", cur)
def add_tree(img, src, dest):
"""Copie récursive src -> dest dans l'image, en gardant les bits d'exécution."""
mkdirs(img, dest)
for root, dirs, files in os.walk(src):
rel = os.path.relpath(root, src)
tgt = dest if rel == "." else f"{dest}/{rel}"
for d in dirs:
p = os.path.join(root, d)
if os.path.islink(p):
continue
hfs(img, "mkdir", f"{tgt}/{d}", check=False)
for f in files:
p = os.path.join(root, f)
if os.path.islink(p):
print(f" (lien symbolique ignoré : {p})")
continue
hfs(img, "add", p, f"{tgt}/{f}")
with open(p, "rb") as fh:
macho = fh.read(4) in MACHO
if macho or os.stat(p).st_mode & 0o111:
hfs(img, "chmod", "755", f"{tgt}/{f}")
def sign(tree):
for root, _, files in os.walk(tree):
for f in files:
p = os.path.join(root, f)
if os.path.islink(p):
continue
with open(p, "rb") as fh:
if fh.read(4) not in MACHO:
continue
# re-signe en gardant les entitlements déjà présents (ldid -S seul les efface)
ents = subprocess.run(["ldid", "-e", p], capture_output=True).stdout
if ents.strip():
with tempfile.NamedTemporaryFile(suffix=".plist", delete=False) as t:
t.write(ents)
subprocess.run(["ldid", "-S" + t.name, p], check=True)
os.unlink(t.name)
else:
subprocess.run(["ldid", "-S", p], check=True)
def deb_data(path, out):
with open(path, "rb") as f:
assert f.read(8) == b"!<arch>\n", f"{path} : pas un .deb"
while hdr := f.read(60):
name, size = hdr[:16].decode().strip().rstrip("/"), int(hdr[48:58])
data = f.read(size)
f.read(size % 2)
if name.startswith("data.tar"):
opener = {".gz": gzip.decompress, ".bz2": bz2.decompress, ".xz": lzma.decompress, ".lzma": lzma.decompress}
ext = os.path.splitext(name)[1]
raw = opener.get(ext, lambda x: x)(data)
tarfile.open(fileobj=io.BytesIO(raw)).extractall(out, filter="fully_trusted")
return
sys.exit(f"{path} : pas de data.tar")
def main():
p = argparse.ArgumentParser(usage=__doc__)
p.add_argument("-b", "--base", default="5H11a")
p.add_argument("-o", "--out")
p.add_argument("items", nargs="+")
a = p.parse_args()
out = a.out or f"{a.base}-apps"
base_dir = os.path.join(EMU, "images", a.base)
base_img = os.path.join(base_dir, "rootfs.img")
if not os.path.exists(base_img):
sys.exit(f"Pas de {base_img}")
out_dir = os.path.join(EMU, "images", out)
os.makedirs(out_dir, exist_ok=True)
for f in ("bootrom", "nor.bin", "nand-version"):
src, dst = os.path.join(base_dir, f), os.path.join(out_dir, f)
if os.path.exists(src) and not os.path.lexists(dst):
os.symlink(os.path.realpath(src), dst)
with tempfile.TemporaryDirectory() as tmp:
img = os.path.join(out_dir, "rootfs.img")
shutil.copyfile(base_img, img)
for item in a.items:
item = os.path.abspath(item.rstrip("/"))
print(f"+ {os.path.basename(item)}")
stage = tempfile.mkdtemp(dir=tmp)
if item.endswith(".app") and os.path.isdir(item):
shutil.copytree(item, os.path.join(stage, os.path.basename(item)), symlinks=True)
sign(stage)
add_tree(img, os.path.join(stage, os.path.basename(item)), f"/Applications/{os.path.basename(item)}")
elif item.endswith(".ipa"):
zipfile.ZipFile(item).extractall(stage)
apps = [d for d in os.listdir(os.path.join(stage, "Payload")) if d.endswith(".app")]
for app in apps:
src = os.path.join(stage, "Payload", app)
sign(src)
add_tree(img, src, f"/Applications/{app}")
elif os.path.isdir(item):
shutil.copytree(item, os.path.join(stage, "root"), symlinks=True)
sign(os.path.join(stage, "root"))
add_tree(img, os.path.join(stage, "root"), "/")
elif item.endswith(".deb"):
deb_data(item, stage)
add_tree(img, stage, "/")
else:
sys.exit(f"Type non géré : {item}")
print(hfs(img, "ls", "/Applications").splitlines()[-1])
try:
version = open(os.path.join(base_dir, "nand-version")).read().strip()
except FileNotFoundError:
version = "1"
subprocess.run([MKNAND, img, os.path.join(out_dir, "nand"), version], check=True)
opts_path = os.path.join(base_dir, "machine-opts")
opts = open(opts_path).read().strip() if os.path.exists(opts_path) else ""
opts = re.sub(r",nand-write=(?:on|off)", "", opts) + ",nand-write=on"
out_opts = os.path.join(out_dir, "machine-opts")
if os.path.islink(out_opts):
os.unlink(out_opts)
with open(out_opts, "w") as f:
f.write(opts + "\n")
print(f"Image prête : {out_dir}\n ipod-emu start -f -w {out}")
if __name__ == "__main__":
main()
+114
View File
@@ -0,0 +1,114 @@
#!/usr/bin/env python3
"""Pilote l'émulateur iPod Touch 2G (qemu-ios) via son socket QMP.
ipod-qmp screenshot [sortie.png] capture l'écran (320x480, PNG ou PPM)
ipod-qmp tap X Y [DURÉE] touche l'écran (coordonnées 320x480, appui 0.3 s par défaut)
ipod-qmp swipe X1 Y1 X2 Y2 glisse (ex. déverrouiller : swipe 40 430 280 430)
ipod-qmp home | power appuie sur Home / Power
ipod-qmp status état de la VM
ipod-qmp hmp COMMANDE commande du moniteur QEMU (ex. "info registers", "x/8i $pc")
ipod-qmp quit arrête l'émulateur
Socket : -s SOCK, sinon $IPOD_QMP, sinon ~/ipod-emu/run/qmp.sock
"""
import argparse, json, os, socket, subprocess, sys, tempfile, time
W, H = 320, 480
class QMP:
def __init__(self, path):
self.s = socket.socket(socket.AF_UNIX)
self.s.connect(path)
self.f = self.s.makefile("rw")
json.loads(self.f.readline()) # greeting
self.cmd("qmp_capabilities")
def cmd(self, name, **args):
msg = {"execute": name}
if args:
msg["arguments"] = args
self.f.write(json.dumps(msg) + "\n")
self.f.flush()
while True:
line = self.f.readline()
if not line:
return None
r = json.loads(line)
if "event" in r:
continue
if "error" in r:
sys.exit(f"QMP {name}: {r['error']['desc']}")
return r.get("return")
def pos(x, y):
return [{"type": "abs", "data": {"axis": "x", "value": int(x * 0x7fff / (W - 1))}},
{"type": "abs", "data": {"axis": "y", "value": int(y * 0x7fff / (H - 1))}}]
def btn(down):
return [{"type": "btn", "data": {"button": "left", "down": down}}]
def key(q, qcode):
for down in (True, False):
q.cmd("input-send-event", events=[{"type": "key", "data": {"down": down, "key": {"type": "qcode", "data": qcode}}}])
time.sleep(0.2)
def main():
p = argparse.ArgumentParser(usage=__doc__)
tools_home = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
default_home = tools_home if os.path.isdir(os.path.join(tools_home, "images")) else os.path.expanduser("~/ipod-emu")
emu_home = os.environ.get("IPOD_EMU_HOME", default_home)
p.add_argument("-s", "--sock", default=os.environ.get("IPOD_QMP", os.path.join(emu_home, "run", "qmp.sock")))
p.add_argument("action")
p.add_argument("args", nargs="*")
a = p.parse_args()
q = QMP(a.sock)
if a.action == "screenshot":
out = os.path.abspath(a.args[0] if a.args else time.strftime("ipod-%Y%m%d-%H%M%S.png"))
if out.endswith(".ppm"):
q.cmd("screendump", filename=out)
else:
fd, tmp = tempfile.mkstemp(suffix=".ppm")
os.close(fd)
q.cmd("screendump", filename=tmp)
subprocess.run(["magick", tmp, out], check=True)
os.unlink(tmp)
print(out)
elif a.action == "tap":
x, y = map(float, a.args[:2])
hold = float(a.args[2]) if len(a.args) > 2 else 0.3
q.cmd("input-send-event", events=pos(x, y))
time.sleep(0.1)
q.cmd("input-send-event", events=btn(True))
time.sleep(hold)
q.cmd("input-send-event", events=btn(False))
elif a.action == "swipe":
x1, y1, x2, y2 = map(float, a.args)
q.cmd("input-send-event", events=pos(x1, y1))
q.cmd("input-send-event", events=btn(True))
for i in range(1, 16):
t = i / 15
q.cmd("input-send-event", events=pos(x1 + (x2 - x1) * t, y1 + (y2 - y1) * t))
time.sleep(0.03)
q.cmd("input-send-event", events=btn(False))
elif a.action == "home":
key(q, "h")
elif a.action == "power":
key(q, "p")
elif a.action == "status":
print(q.cmd("query-status"))
elif a.action == "hmp":
print(q.cmd("human-monitor-command", **{"command-line": " ".join(a.args)}))
elif a.action == "quit":
q.cmd("quit")
else:
sys.exit(__doc__)
if __name__ == "__main__":
main()
+147
View File
@@ -0,0 +1,147 @@
#!/usr/bin/env python3
"""Teste une application dans une NAND indépendante et conserve les diagnostics."""
import argparse
import json
import os
from pathlib import Path
import plistlib
import shutil
import socket
import subprocess
import tempfile
import time
import zipfile
def bundle_id(package):
if package.is_dir() and package.suffix == '.app':
with (package / 'Info.plist').open('rb') as f:
return plistlib.load(f)['CFBundleIdentifier']
if package.suffix == '.ipa':
with zipfile.ZipFile(package) as z:
plists = [n for n in z.namelist() if n.startswith('Payload/') and
n.endswith('.app/Info.plist') and n.count('/') == 2]
if len(plists) != 1:
raise ValueError('IPA ambiguë : préciser --bundle-id')
return plistlib.loads(z.read(plists[0]))['CFBundleIdentifier']
raise ValueError('Préciser --bundle-id pour un paquet .deb ou un overlay')
def main():
p = argparse.ArgumentParser(description=__doc__)
p.add_argument('package', type=Path, help='.app, .ipa, .deb ou overlay')
p.add_argument('--bundle-id')
p.add_argument('--base', default=os.environ.get('IPOD_TEST_BASE', '5H11a-agent-v12'))
p.add_argument('--output', type=Path, help='nouveau dossier de résultats (doit être absent)')
p.add_argument('--settle', type=float, default=3, help='attente après apparition au premier plan')
args = p.parse_args()
package = args.package.resolve()
tools = Path(__file__).resolve().parent
default_home = tools.parent if (tools.parent / 'images').is_dir() else Path.home() / 'ipod-emu'
home = Path(os.environ.get('IPOD_EMU_HOME', default_home)).resolve()
try:
if not package.exists():
raise ValueError('Application introuvable')
identifier = args.bundle_id or bundle_id(package)
if not isinstance(identifier, str) or not identifier or any(c in identifier for c in '\t\r\n'):
raise ValueError('Bundle-id invalide')
if Path(args.base).name != args.base or args.base in ('.', '..'):
raise ValueError('Nom de firmware invalide')
if args.settle < 0:
raise ValueError('--settle doit être positif')
base = home / 'images' / args.base
if not (base / 'rootfs.img').is_file():
raise ValueError(f'Image de base introuvable : {base}')
# Les ports sont partagés par les sessions : ne jamais arrêter une VM tierce.
vnc = os.environ.get('IPOD_VNC', '127.0.0.1:0')
host, display = vnc.rsplit(':', 1)
for address, port in [(os.environ.get('IPOD_AGENT_HOST', '127.0.0.1'),
int(os.environ.get('IPOD_AGENT_PORT', '7788'))),
(host or '127.0.0.1', 5900 + int(display))]:
try:
connection = socket.create_connection((address, port), timeout=1)
except ConnectionRefusedError:
continue
else:
connection.close()
raise ValueError(f'Port {port} occupé : arrêter votre session avant ce test')
if args.output:
result = args.output.resolve()
result.mkdir(parents=True, exist_ok=False)
else:
parent = home / 'work' / 'tests'
parent.mkdir(parents=True, exist_ok=True)
result = Path(tempfile.mkdtemp(prefix=time.strftime('%Y%m%d-%H%M%S-'), dir=parent))
except (OSError, ValueError, KeyError, plistlib.InvalidFileException, zipfile.BadZipFile) as e:
p.exit(1, f'{e}\n')
print(f'Diagnostics : {result}', flush=True)
session = result / 'session'
(session / 'images').mkdir(parents=True)
(session / 'images' / args.base).symlink_to(base, target_is_directory=True)
for dependency in ('xpwn', 'gen-v1', 'netd'):
(session / dependency).symlink_to(home / dependency, target_is_directory=True)
env = dict(os.environ, IPOD_EMU_HOME=str(session),
IPOD_QEMU=os.environ.get('IPOD_QEMU', str(home.parent / 'qemu-ios/build/qemu-system-arm')))
# Un socket de session explicite ne doit pas viser la VM de l'utilisateur.
env.pop('IPOD_QMP', None)
image_name = 'test-app' if args.base != 'test-app' else 'test-app-result'
started = False
report = {'package': str(package), 'bundle_id': identifier, 'base': args.base, 'ok': False}
with (result / 'workflow.log').open('w') as workflow:
def run(tool, *argv, capture=False, timeout=360):
r = subprocess.run([str(tools / tool), *map(str, argv)], env=env,
stdout=subprocess.PIPE if capture else workflow,
stderr=workflow, text=True, timeout=timeout)
if capture:
workflow.write(r.stdout)
workflow.flush()
if r.returncode:
raise RuntimeError(f'{tool} {argv[0] if argv else ""} : code {r.returncode}')
return r.stdout if capture else None
try:
run('ipod-mkimage', '-b', args.base, '-o', image_name, package, timeout=900)
started = True # nettoie également un démarrage partiel
run('ipod-emu', 'start', '-w', image_name, '-W')
run('ipod-agent', 'launch', identifier, '--log', '/private/var/mobile/ipod-test-app.log')
deadline = time.monotonic() + 30
while run('ipod-agent', 'front', capture=True, timeout=20).strip() != identifier:
if time.monotonic() >= deadline:
raise RuntimeError('Application absente du premier plan après 30 s')
time.sleep(1)
time.sleep(args.settle)
front = run('ipod-agent', 'front', capture=True, timeout=20).strip()
report['front'] = front
if front != identifier:
raise RuntimeError('Application quittée pendant la période de stabilisation')
report['ok'] = True
except (OSError, RuntimeError, subprocess.TimeoutExpired) as e:
report['error'] = str(e)
finally:
if started:
for tool, argv in [('ipod-agent', ['get', '/private/var/mobile/ipod-test-app.log', result / 'app.log']),
('ipod-qmp', ['screenshot', result / 'screen.png'])]:
try:
run(tool, *argv, timeout=30)
except (OSError, RuntimeError, subprocess.TimeoutExpired) as e:
report.setdefault('diagnostic_errors', []).append(str(e))
try:
run('ipod-agent', 'sync', timeout=15)
except (OSError, RuntimeError, subprocess.TimeoutExpired):
pass
try:
run('ipod-emu', 'stop', timeout=30)
except (OSError, RuntimeError, subprocess.TimeoutExpired) as e:
report['ok'] = False
report['stop_error'] = str(e)
if report.get('diagnostic_errors'):
report['ok'] = False
(result / 'result.json').write_text(json.dumps(report, indent=2, ensure_ascii=False) + '\n')
print('Test réussi' if report['ok'] else 'Test échoué : consulter workflow.log et result.json')
return 0 if report['ok'] else 1
if __name__ == '__main__':
raise SystemExit(main())
+102
View File
@@ -0,0 +1,102 @@
#!/usr/bin/env python3
"""Prépare le rootfs d'un firmware stock pour l'émulateur (mêmes modifs que l'image 5F138 de devos50).
mkbase-rootfs ROOTFS_STOCK.img SORTIE.img [--size-mb 700]
- ne garde que les LaunchDaemons nécessaires à SpringBoard (les autres attendent du matériel absent)
- SpringBoard sans accélération MBX 2D (LK_ENABLE_MBX2D=0), lockbot sans redirection vers /dev/null
- une seule partition, montée en écriture (pas de /private/var séparé)
- enregistrements d'activation Lockdown (sinon écran « Connectez à iTunes »)
- verrouillage automatique désactivé (le SoC émulé ne sait pas sortir de veille)
- emuagent (réseau PPP + pilotage, ~/ipod-emu/agent/overlay, à construire avec make) et mDNSResponder (DNS)
"""
import argparse, os, plistlib, shutil, subprocess, sys, tempfile
from importlib.machinery import SourceFileLoader
EMU = os.environ.get("IPOD_EMU_HOME", os.path.expanduser("~/ipod-emu"))
mk = SourceFileLoader("mk", os.path.join(os.path.dirname(os.path.realpath(__file__)), "ipod-mkimage")).load_module()
hfs = mk.hfs
KEEP = {
"com.apple.ReportCrash.DirectoryService.plist", "com.apple.ReportCrash.Jetsam.plist",
"com.apple.ReportCrash.SafetyNet.plist", "com.apple.ReportCrash.SimulateCrash.plist",
"com.apple.ReportCrash.plist", "com.apple.SCHelper-embedded.plist", "com.apple.SpringBoard.plist",
"com.apple.configd.plist", "com.apple.mobile.lockbot.plist", "com.apple.mobile.lockdown.plist",
"com.apple.notifyd.plist",
}
LD = "/System/Library/LaunchDaemons"
AGENT = os.path.join(EMU, "agent/overlay")
LOCKDOWN = os.path.join(EMU, "assets/Lockdown") # enregistrements d'activation (extraits de la NAND 5F138 de devos50)
def edit_plist(img, path, fn, tmp):
data = subprocess.run([mk.HFSPLUS, img, "cat", path], capture_output=True, check=True).stdout
p = plistlib.loads(data)
fn(p)
out = os.path.join(tmp, os.path.basename(path))
with open(out, "wb") as f:
plistlib.dump(p, f, fmt=plistlib.FMT_BINARY)
hfs(img, "rm", path)
hfs(img, "add", out, path)
def main():
a = argparse.ArgumentParser(usage=__doc__)
a.add_argument("src"); a.add_argument("dst"); a.add_argument("--size-mb", type=int, default=700)
a = a.parse_args()
shutil.copyfile(a.src, a.dst)
img = a.dst
with tempfile.TemporaryDirectory() as tmp:
for line in hfs(img, "ls", LD).splitlines():
name = line.split()[-1] if line.startswith("1") else None
if name and name.endswith(".plist") and name not in KEEP:
hfs(img, "rm", f"{LD}/{name}")
# grow juste après les suppressions : il lui faut un peu de place pour agrandir le bitmap d'allocation (le
# rootfs stock est plein), et les fichiers ajoutés ensuite sont alloués d'un bloc dans l'espace neuf
out = hfs(img, "grow", str(a.size_mb * 1024 * 1024))
if "grew volume" not in out:
sys.exit(f"grow : {out}")
edit_plist(img, f"{LD}/com.apple.SpringBoard.plist",
lambda p: p.setdefault("EnvironmentVariables", {}).update({"LK_ENABLE_MBX2D": "0"}), tmp)
edit_plist(img, f"{LD}/com.apple.mobile.lockbot.plist",
lambda p: [p.pop(k, None) for k in ("StandardErrorPath", "StandardOutPath")], tmp)
fstab = os.path.join(tmp, "fstab")
open(fstab, "w").write("/dev/disk0s1 / hfs rw 0 1\n")
hfs(img, "rm", "/private/etc/fstab")
hfs(img, "add", fstab, "/private/etc/fstab")
mk.add_tree(img, LOCKDOWN, "/private/var/root/Library/Lockdown")
sb = os.path.join(tmp, "com.apple.springboard.plist")
with open(sb, "wb") as f:
# pas de verrouillage auto, pas d'alerte « Edit Home Screen » au premier démarrage
plistlib.dump({"SBAutoLockTime": -1, "SBAutoDimTime": -1, "SBDidShowReorderText": True}, f, fmt=plistlib.FMT_BINARY)
prefs = "/private/var/mobile/Library/Preferences"
mk.mkdirs(img, prefs)
hfs(img, "add", sb, f"{prefs}/com.apple.springboard.plist")
# iPod d'un utilisateur français : langue, région, fuseau (localtime est un lien symbolique, que xpwn ne sait
# pas créer : on y copie le fichier de zone)
gp = os.path.join(tmp, ".GlobalPreferences.plist")
g = plistlib.loads(subprocess.run([mk.HFSPLUS, img, "cat", f"{prefs}/.GlobalPreferences.plist"], capture_output=True).stdout)
langs = [l for l in g.get("AppleLanguages", []) if l != "fr"]
g["AppleLanguages"] = ["fr"] + langs
g["AppleLocale"] = "fr_FR"
with open(gp, "wb") as f:
plistlib.dump(g, f, fmt=plistlib.FMT_BINARY)
hfs(img, "rm", f"{prefs}/.GlobalPreferences.plist")
hfs(img, "add", gp, f"{prefs}/.GlobalPreferences.plist")
tz = os.path.join(tmp, "localtime")
with open(tz, "wb") as f:
f.write(subprocess.run([mk.HFSPLUS, img, "cat", "/usr/share/zoneinfo/Europe/Paris"], capture_output=True, check=True).stdout)
hfs(img, "rm", "/private/var/db/timezone/localtime")
hfs(img, "add", tz, "/private/var/db/timezone/localtime")
if not os.path.isdir(AGENT):
sys.exit(f"{AGENT} absent : lancer make dans {os.path.dirname(AGENT)}")
mk.add_tree(img, AGENT, "/")
print(hfs(img, "ls", LD).splitlines()[-1])
if __name__ == "__main__":
main()
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/env python3
"""Extrait l'image HFS+ d'une NAND produite par l'ancien qemu-ios-generate-nand (<= 10f542f, 2023)."""
import struct, sys
nand, out = sys.argv[1], sys.argv[2]
PPB, PPSB, CS, BANKS, PAGE = 128, 1024, 4, 8, 4096
def lpn2ppn(lpn):
vpn = (lpn // PPSB + 1) * PPSB + lpn % PPSB
bank, vbn = vpn % BANKS, vpn // PPSB
tpn = vbn * PPB
cpn = (tpn & (PPB - 1)) | (PPB if (bank // CS) & 1 else 0) | ((tpn & ~(PPB - 1)) << 1)
return bank % CS, (cpn // PPB) * PPB + (vpn % PPSB) // BANKS
def page(lpn):
cs, ppn = lpn2ppn(lpn)
try:
return open(f'{nand}/cs{cs}/{ppn}.page', 'rb').read()[:PAGE]
except FileNotFoundError:
return bytes(PAGE)
first = page(3)
sig, ver, attr, lmv, jib, cdate, mdate, bdate, chk, files, folders, bsize, total = struct.unpack('>2sHI4sIIIIIIIII', first[1024:1024+48])
assert sig in (b'H+', b'HX'), sig
n = bsize * total // PAGE
print(f'{sig.decode()} blocksize={bsize} blocks={total} -> {n} pages', file=sys.stderr)
with open(out, 'wb') as f:
for i in range(n):
f.write(page(3 + i))
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
# lance qemu-system-arm sous gdb et affiche la backtrace en cas de crash (IPOD_QEMU=.../qemu-gdb ipod-emu start)
exec gdb -q -batch -ex 'handle SIGUSR1 SIGUSR2 SIGPIPE nostop noprint' -ex run -ex 'bt 15' -ex 'info registers pc' --args "$HOME/qemu-ios/build/qemu-system-arm" "$@"
+85
View File
@@ -0,0 +1,85 @@
#ifndef FTL_H
#define FTL_H
#include "wmr.h"
#include <stdint.h>
#define FTL_CXT_SECTION_SIZE 3
#define FTL_SPARE_TYPE_CXT_INDEX 0x43
#define FREE_SECTION_SIZE 20
#define FREE_SECTION_START FTL_CXT_SECTION_SIZE
#define FREE_LIST_SIZE 3
#define FTL_READ_REFRESH_LIST_SIZE 5
#define BYTES_PER_SECTOR 512
#define LOG_SECTION_SIZE (FREE_SECTION_SIZE - FREE_LIST_SIZE)
#define MAX_NUM_OF_MAP_TABLES (WMR_MAX_VB / (WMR_SECTORS_PER_PAGE_MIN * WMR_SECTOR_SIZE) + (WMR_MAX_VB % (WMR_SECTORS_PER_PAGE_MIN * WMR_SECTOR_SIZE) ? 1 : 0)) * (sizeof(uint16_t))
#define MAX_NUM_OF_EC_TABLES (WMR_MAX_VB / (WMR_SECTORS_PER_PAGE_MIN * WMR_SECTOR_SIZE) + (WMR_MAX_VB % (WMR_SECTORS_PER_PAGE_MIN * WMR_SECTOR_SIZE) ? 1 : 0)) * (sizeof(uint32_t))
#define MAX_NUM_OF_LOGCXT_MAPS (((LOG_SECTION_SIZE * WMR_MAX_PAGES_PER_BLOCK * WMR_NUM_OF_BANKS * sizeof(uint16_t)) / (WMR_SECTOR_SIZE * WMR_SECTORS_PER_PAGE_MIN)) + (((LOG_SECTION_SIZE * WMR_MAX_PAGES_PER_BLOCK * WMR_NUM_OF_BANKS * sizeof(uint16_t)) % (WMR_SECTOR_SIZE * WMR_SECTORS_PER_PAGE_MIN)) ? 1 : 0))
#define FTL_CXT_SECTION_START 201
typedef struct __attribute__((__packed__))
{
uint32_t dwLogAge; /* log age - uses a global counter starting from 0 */
uint16_t wVbn; /* the virtual block number of log block */
uint16_t wLbn; /* the logical block number of log block */
uint32_t paPOffsetL2P; /* L2P page offset mapping table *//* size - PAGES_PER_SUBLK * sizeof(UInt16) functionality - maps pages of a logical block inside the block */
uint16_t wFreePOffset; /* free page offset in log block *//* the next physical page to write to */
uint16_t wNumOfValidLP; /* the number of valid page *//* the number of valid pages in the log - stay the same when rewriting a page */
uint32_t boolCopyMerge; /* can be copymerged or not *//* ??? */
/* name sure this structure is aligned 16-bit */
} LOGCxt;
typedef struct
{
uint16_t wLbn;
uint16_t wVbn;
} FTLReadRefreshStruct;
typedef struct
{
uint32_t dwAge; /* Cxt Write Age - Counter starting From 0xFFFFFFFF going down */
uint32_t dwWriteAge; /* counter starting from 0 - unique age of the first ftl write operation to the block */
uint16_t wNumOfFreeVb; /* number of free VB */
uint16_t wFreeVbListTail; /* marks the first available VB in a cyclic list */
uint16_t wWearLevelCounter; /* this counter is by the FTL to decide whether an Autowearlevel operation is needed after FTL_Write is done */
uint16_t awFreeVbList[FREE_SECTION_SIZE]; /* cyclic list of the free VBs */
uint32_t adwMapTablePtrs[MAX_NUM_OF_MAP_TABLES]; /* page address (Vpn) of the Map table pages in the Cxt block - only valid when boolFlashCxtIsValid is TRUE32 */
uint32_t adwECTablePtrs[MAX_NUM_OF_EC_TABLES]; /* page address of the Erase Counter table */
uint32_t adwLOGCxtMapPtrs[MAX_NUM_OF_LOGCXT_MAPS]; /* page address of the LOGCxt map table */
uint32_t pawMapTable; /* cached map table logical blocks to virtual blocks - value will be recalculated in FTL_Init */
uint32_t pawECCacheTable; /* cached Erase counter table - value will be recalculated in FTL_Init - data is recovered from the last valid Erase Map in case of power failure */
uint32_t pawLOGCxtMapTable; /* pointer to the map of the logs - this is a pointer and will be calculated in the Init - data can be restored if the info is flushed */
LOGCxt aLOGCxtTable[LOG_SECTION_SIZE + 1]; /* LOGCxt array */
uint32_t dwMetaWearLevelCounter; /* used to decide whether the index info super blocks need to be replaced or not (slows down VFL Cxt blocks wear) */
uint16_t wOverErasedBlock; /* should not be used - mark the last erased vb before setting boolFlushECTable */
uint16_t awMapCxtVbn[FTL_CXT_SECTION_SIZE]; /* vb address of the FTL Cxt information */
uint32_t dwCurrMapCxtPage; /* pointer to the last written Cxt info page */
uint32_t boolFlashCxtIsValid; /* mark whether the FTLCxt on the flash is up to date or not */
// this code was added Apr 4th 2007
uint32_t adwRCTablePtrs[MAX_NUM_OF_EC_TABLES]; /* page address of the Read Counter table */
uint32_t pawRCCacheTable; /* cached Erase counter table - value will be recalculated in FTL_Init - data is recovered from the last valid Erase Map in case of power failure */
FTLReadRefreshStruct aFTLReadRefreshList[FTL_READ_REFRESH_LIST_SIZE];
uint32_t dwRefreshListIdx;
uint32_t dwReadsSinceLastRefreshMark;
// this code was added Apr 20th 2007
uint32_t adwStatPtrs[2]; // allocate 2 pages for stats
} FTLCxt2;
typedef struct
{
FTLCxt2 stFTLCxt;
uint8_t abReserved[((BYTES_PER_SECTOR - 2) * sizeof(uint32_t)) - sizeof(FTLCxt2)];
uint32_t dwVersion;
uint32_t dwVersionNot;
} FTLMeta;
#endif
+377
View File
@@ -0,0 +1,377 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stddef.h>
#include "vfl.h"
#include "mbr.h"
#include "gpt.h"
#define BYTES_PER_PAGE 4096
#define BYTES_PER_SPARE 64
#define PAGES_PER_BLOCK 128
#define VFL_CTX_PHYSICAL_PAGE PAGES_PER_BLOCK * 1
#define PAGES_PER_SUBLOCK 1024
#define CS_TOTAL 4
#define NAND_SIG_PAGE 524160
#define BBT_PAGE 524161
#define FTL_CTX_VBLK_IND 0 // virtual block index of the FTL Context
#define NUM_PARTITIONS 1
#define BOOT_PARTITION_FIRST_PAGE (NUM_PARTITIONS + 2) // first two LBAs are for MBR and GUID header
static uint32_t crc32_table[256];
static int crc32_table_computed = 0;
/*
CRC32 logic
*/
static void make_crc32_table(void)
{
uint32_t c;
int n, k;
for (n = 0; n < 256; n++) {
c = (uint32_t) n;
for (k = 0; k < 8; k++) {
if (c & 1)
c = 0xedb88320L ^ (c >> 1);
else
c = c >> 1;
}
crc32_table[n] = c;
}
crc32_table_computed = 1;
}
uint32_t update_crc32(uint32_t crc, const uint8_t *buf,
int len)
{
uint32_t c = crc;
int n;
if (!crc32_table_computed)
make_crc32_table();
for (n = 0; n < len; n++) {
c = crc32_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
}
return c;
}
uint32_t crc32(const uint8_t *buf, int len)
{
return update_crc32(0xffffffffL, buf, len) ^ 0xffffffffL;
}
uint8_t *get_valid_ftl_spare() {
uint32_t *spare = (uint32_t *)calloc(BYTES_PER_SPARE, sizeof(char));
spare[2] = 0x00FF00FF;
return (uint8_t *)spare;
}
void _Helper_ConvertP2C_OneBitReorder(uint32_t dwBank, uint32_t dwPpn, uint32_t* pdwCE, uint32_t* pdwCpn, uint32_t dwReorderMask)
{
const uint32_t dwBelowReorderMask = dwReorderMask - 1; // Assumption: dwReorderMask is a power of 2!
const uint32_t dwAboveReorderMask = ~dwBelowReorderMask;
// insert reorder bit back in correct position of "chip" page number by extracting from MSB of "physical" bank number
*pdwCpn = ((dwPpn & dwBelowReorderMask) |
(((dwBank / CS_TOTAL) & 0x1) ? dwReorderMask : 0) |
((dwPpn & dwAboveReorderMask) << 1));
// strip reorder bit from MSB of "physical" bank number to produce "chip" CE
*pdwCE = dwBank % CS_TOTAL;
}
void _pfnConvertP2C_TwoPlaneLSB(uint32_t dwBank, uint32_t dwPpn, uint32_t* pdwCE, uint32_t* pdwCpn)
{
_Helper_ConvertP2C_OneBitReorder(dwBank, dwPpn, pdwCE, pdwCpn, PAGES_PER_BLOCK);
}
void _ConvertT2P_Default(uint16_t wBank, uint16_t wTbn, uint16_t *pwPbn)
{
uint32_t dwCpn, dwCS;
_pfnConvertP2C_TwoPlaneLSB((uint32_t)wBank, (uint32_t)(wTbn * PAGES_PER_BLOCK), &dwCS, &dwCpn);
*pwPbn = (uint16_t)(dwCpn / PAGES_PER_BLOCK);
}
void _Vpn2Ppn(uint32_t dwVpn, uint16_t *pwCS, uint32_t *pdwPpn) {
uint16_t wPbn, wPOffset;
uint16_t wBank = dwVpn % WMR_NUM_OF_BANKS;
uint16_t wVbn = dwVpn / PAGES_PER_SUBLOCK;
_ConvertT2P_Default((wBank & 0xffff), wVbn, &wPbn);
*pwCS = wBank % CS_TOTAL;
wPOffset = (uint16_t)((dwVpn % PAGES_PER_SUBLOCK) / WMR_NUM_OF_BANKS);
*pdwPpn = wPbn * PAGES_PER_BLOCK + wPOffset;
}
void _Lpn2Ppn(uint32_t dwLpn, uint16_t *pwCS, uint32_t *pdwPpn) {
uint32_t dwLbn = dwLpn / PAGES_PER_SUBLOCK;
uint16_t wLPOffset = (uint16_t)(dwLpn - dwLbn * PAGES_PER_SUBLOCK);
uint32_t dwVbn = dwLbn + 1;
uint32_t dwVpn = dwVbn * PAGES_PER_SUBLOCK + wLPOffset;
_Vpn2Ppn(dwVpn, pwCS, pdwPpn);
}
void write_page(uint8_t *page, uint8_t *spare, int cs, int page_index) {
char filename[100];
sprintf(filename, "nand/cs%d", cs);
struct stat st = {0};
if (stat(filename, &st) == -1) {
mkdir(filename, 0700);
}
sprintf(filename, "nand/cs%d/%d.page", cs, page_index);
FILE *f = fopen(filename, "wb");
if(!page) {
page = (uint8_t *)calloc(BYTES_PER_PAGE, sizeof(char));
}
fwrite(page, sizeof(char), BYTES_PER_PAGE, f);
if(!spare) {
spare = (uint8_t *)calloc(BYTES_PER_SPARE, sizeof(char));
}
fwrite(spare, sizeof(char), BYTES_PER_SPARE, f);
fclose(f);
}
void write_bbts() {
// create the bad block table on each CS
for(int cs = 0; cs < CS_TOTAL; cs++) {
uint8_t *page = calloc(BYTES_PER_PAGE, sizeof(char));
memcpy(page, "DEVICEINFOBBT\0\0\0", 16);
// set all bits in this page to 1, to indicate that all blocks are of good health.
for(int i = 16; i < BYTES_PER_PAGE; i++) {
page[i] = 0xFF;
}
write_page(page, NULL, cs, BBT_PAGE);
}
}
void write_nand_sig_page() {
// write the NAND signature page
uint8_t *page = calloc(0x100, sizeof(uint8_t));
char *magic = "NANDDRIVERSIGN";
memcpy(page, magic, strlen(magic));
page[0x34] = 0x4; // length of the info
// signature (0x4331313X): the last digit is the NAND driver format version, 1 for iPhone OS 2.1.1, 2 for 2.2.x
const char *ver = getenv("NAND_SIG_VERSION");
page[0x38] = (ver && *ver) ? ver[0] : 0x31;
page[0x39] = 0x31;
page[0x3A] = 0x31;
page[0x3B] = 0x43;
write_page(page, NULL, 0, NAND_SIG_PAGE);
}
void write_vfl_context() {
for (int wCSIdx = 0; wCSIdx < CS_TOTAL; wCSIdx++) {
// initialize VFL context block on physical block 35, page 0
VFLMeta *vfl_meta = (VFLMeta *)calloc(sizeof(VFLMeta), sizeof(char));
vfl_meta->dwVersion = VFL_META_VERSION;
VFLCxt *pVFLCxt = &vfl_meta->stVFLCxt;
// we set the number of VFL/FTL user blocks to 2048 which doesn't give any room for the BBT table. This is fine as we do not have bad blocks.
pVFLCxt->wNumOfVFLSuBlk = 2048;
pVFLCxt->wNumOfFTLSuBlk = 2048;
pVFLCxt->abVSFormtType = VFL_VENDOR_SPECIFIC_TYPE;
pVFLCxt->dwGlobalCxtAge = wCSIdx;
pVFLCxt->wCxtLocation = 1; // the VFL context is located in the first physical block
for (int wIdx = 0; wIdx < FTL_CXT_SECTION_SIZE; wIdx++)
{
pVFLCxt->awFTLCxtVbn[wIdx] = (uint16_t)(wIdx);
}
pVFLCxt->wNumOfInitBadBlk = 0;
for (int wIdx = 0, wPbn = VFL_FIRST_BLK_TO_SEARCH_CXT; wIdx < VFL_INFO_SECTION_SIZE && wPbn < VFL_LAST_BLK_TO_SEARCH_CXT; wPbn++)
{
pVFLCxt->awInfoBlk[wIdx++] = wPbn;
}
// update the bad block map
for(int i = 0; i < WMR_MAX_RESERVED_SIZE; i++) {
pVFLCxt->awBadMapTable[i] = VFL_BAD_MAP_TABLE_AVAILABLE_MARK;
}
VFLSpare *vfl_ctx_spare = (VFLSpare *)calloc(BYTES_PER_SPARE, sizeof(char));
vfl_ctx_spare->dwCxtAge = 1;
vfl_ctx_spare->bSpareType = VFL_CTX_SPARE_TYPE;
// store some copies of the VFL
for (uint8_t wPageIdx = 0; wPageIdx < VFL_NUM_OF_VFL_CXT_COPIES; wPageIdx++) {
write_page((uint8_t *)vfl_meta, (uint8_t *)vfl_ctx_spare, wCSIdx, VFL_CTX_PHYSICAL_PAGE + wPageIdx);
}
}
}
void write_ftl_context() {
uint16_t cs;
uint32_t ppn;
// set the FTL spare type of the first page of the FTL CXT block to indicate that there is a CTX index
VFLSpare *vfl_ctx_spare = (VFLSpare *)calloc(BYTES_PER_SPARE, sizeof(char));
vfl_ctx_spare->bSpareType = FTL_SPARE_TYPE_CXT_INDEX;
write_page(NULL, (uint8_t *)vfl_ctx_spare, 0, 0);
// create the FTL Meta page on the last page of the FTL Cxt block and embed the right versions
FTLMeta *ftl_meta = (FTLMeta *)calloc(sizeof(FTLMeta), sizeof(char));
ftl_meta->dwVersion = 0x46560000;
ftl_meta->dwVersionNot = -0x46560001;
// initialize the number of empty VBs for logs and the free areas
ftl_meta->stFTLCxt.wNumOfFreeVb = FREE_SECTION_SIZE;
for(int i = 0; i < FREE_SECTION_SIZE; i++) {
ftl_meta->stFTLCxt.awFreeVbList[i] = FREE_SECTION_START + i;
}
// create empty logs
for(int i = 0; i < LOG_SECTION_SIZE + 1; i++) {
ftl_meta->stFTLCxt.aLOGCxtTable[i].wVbn = 0xFFFF;
}
// prepare the logical block -> virtual block mapping tables
for(int i = 0; i < MAX_NUM_OF_MAP_TABLES; i++) {
ftl_meta->stFTLCxt.adwMapTablePtrs[i] = i + 5; // the mapping will start from the 2nd page in the FTL context block
uint16_t *mapping_page = calloc(BYTES_PER_PAGE / sizeof(uint16_t), sizeof(uint16_t));
uint32_t items_per_map = BYTES_PER_PAGE / sizeof(uint16_t);
for(int ind_in_map = 0; ind_in_map < items_per_map; ind_in_map++) {
mapping_page[ind_in_map] = (i * items_per_map) + ind_in_map + 1;
}
_Vpn2Ppn(i + 5, &cs, &ppn);
printf("Writing logical -> virtual block map page %d to physical page %d @ cs %d\n", i, ppn, cs);
write_page((uint8_t *)mapping_page, NULL, cs, ppn);
}
vfl_ctx_spare = (VFLSpare *)calloc(BYTES_PER_SPARE, sizeof(char));
vfl_ctx_spare->bSpareType = FTL_SPARE_TYPE_CXT_INDEX;
// we place the FTL Meta on the last page of the first virtual block.
_Vpn2Ppn(PAGES_PER_SUBLOCK - 1, &cs, &ppn);
printf("Writing FTL Meta to physical page %d @ cs %d\n", ppn, cs);
write_page((uint8_t *)ftl_meta, (uint8_t *)vfl_ctx_spare, cs, ppn);
}
uint32_t write_hfs_partition(char *filename, uint32_t page_offset) {
// write the HFS+ partition to the first page and update the associated spare
uint16_t cs; uint32_t ppn;
FILE *hfs_file = fopen(filename, "rb");
fseek(hfs_file, 0L, SEEK_END);
int partition_size = ftell(hfs_file);
fclose(hfs_file);
hfs_file = fopen(filename, "rb");
int lpn = page_offset;
uint8_t *spare = get_valid_ftl_spare();
uint32_t required_pages_for_partition = partition_size / BYTES_PER_PAGE;
printf("Writing HFS partition using %d pages...\n", required_pages_for_partition);
for(int i = 0; i < required_pages_for_partition; i++) {
uint8_t *page = malloc(BYTES_PER_PAGE);
fread(page, BYTES_PER_PAGE, sizeof(uint8_t), hfs_file);
_Lpn2Ppn(lpn, &cs, &ppn);
printf("Writing HFS partition to physical page %d @ cs %d\n", ppn, cs);
write_page(page, spare, cs, ppn);
lpn++;
//if(i == 2000) break;
}
fclose(hfs_file);
return required_pages_for_partition;
}
void write_mbr(int boot_partition_size) {
uint16_t cs; uint32_t ppn;
// write the MBR bytes (LBA 0)
uint8_t *mbr_page = malloc(BYTES_PER_PAGE);
struct mbr_partition *boot_partition = (struct mbr_partition *)(mbr_page + MBR_ADDRESS);
boot_partition->sysid = 0xEE;
boot_partition->startlba = BOOT_PARTITION_FIRST_PAGE;
boot_partition->size = boot_partition_size;
mbr_page[510] = 0x55;
mbr_page[511] = 0xAA;
_Lpn2Ppn(0, &cs, &ppn);
printf("Writing MBR to physical page %d @ cs %d\n", ppn, cs);
uint8_t *spare = get_valid_ftl_spare();
write_page(mbr_page, spare, cs, ppn);
}
void write_filesystem() {
uint16_t cs; uint32_t ppn;
int pages_for_boot_partition = write_hfs_partition("filesystem-it2g-readonly.img", BOOT_PARTITION_FIRST_PAGE);
printf("Required pages for boot partition: %d\n", pages_for_boot_partition);
// initialize the EFI header (LBA 1)
uint8_t *gpt_header_page = malloc(BYTES_PER_PAGE);
gpt_hdr *gpt_header = (gpt_hdr *)gpt_header_page;
// create the boot partition entry (LBA 2)
uint8_t *gpt_entry_boot_partition_page = malloc(BYTES_PER_PAGE);
gpt_ent *gpt_entry_boot_partition = (gpt_ent *)gpt_entry_boot_partition_page;
gpt_entry_boot_partition->ent_type[0] = 0x48465300;
gpt_entry_boot_partition->ent_type[1] = 0x11AA0000;
gpt_entry_boot_partition->ent_type[2] = 0x300011AA;
gpt_entry_boot_partition->ent_type[3] = 0xACEC4365;
gpt_entry_boot_partition->ent_lba_start = BOOT_PARTITION_FIRST_PAGE;
gpt_entry_boot_partition->ent_lba_end = BOOT_PARTITION_FIRST_PAGE + pages_for_boot_partition;
printf("Boot system partition located on page %lld - %lld\n", gpt_entry_boot_partition->ent_lba_start, gpt_entry_boot_partition->ent_lba_end);
_Lpn2Ppn(2, &cs, &ppn);
printf("Writing GUID boot partition entry to page %d @ cs %d\n", ppn, cs);
uint8_t *spare = get_valid_ftl_spare();
write_page(gpt_entry_boot_partition_page, spare, cs, ppn);
// finalize the GPT header
// TODO add the secondary GPT entry!
memcpy(gpt_header, GPT_HDR_SIG, 8);
gpt_header->hdr_revision = GPT_HDR_REVISION;
gpt_header->hdr_size = 0x5C; // 92 bytes
gpt_header->hdr_lba_table = 2;
gpt_header->hdr_entries = NUM_PARTITIONS;
gpt_header->hdr_entsz = 0x80;
gpt_header->hdr_crc_table = crc32(gpt_entry_boot_partition_page, sizeof(gpt_ent));
gpt_header->hdr_crc_self = crc32((uint8_t *)gpt_header, 0x5C);
_Lpn2Ppn(1, &cs, &ppn);
printf("Writing GUID header to page %d @ cs %d\n", ppn, cs);
write_page(gpt_header_page, spare, cs, ppn);
// finally, write the MBR
write_mbr(pages_for_boot_partition);
}
int main(int argc, char *argv[]) {
// create the output dir if it does not exist
struct stat st = {0};
if (stat("nand", &st) == -1) {
mkdir("nand", 0700);
}
write_vfl_context();
write_ftl_context();
write_nand_sig_page();
write_bbts();
write_filesystem();
// testing
uint32_t num = 49188;
uint16_t cs; uint32_t ppn;
_Lpn2Ppn(num, &cs, &ppn);
printf("LPN %d => %d, cs %d\n", num, ppn, cs);
}
+36
View File
@@ -0,0 +1,36 @@
#ifndef GPT_H
#define GPT_H
typedef struct gpt_hdr
{
uint8_t hdr_sig[8];
uint32_t hdr_revision;
uint32_t hdr_size;
uint32_t hdr_crc_self;
uint32_t __reserved;
uint64_t hdr_lba_self;
uint64_t hdr_lba_alt;
uint64_t hdr_lba_start;
uint64_t hdr_lba_end;
uint8_t hdr_uuid[16];
uint64_t hdr_lba_table;
uint32_t hdr_entries; // the number of partition entries
uint32_t hdr_entsz; // size of each partition entry
uint32_t hdr_crc_table;
uint32_t padding;
} gpt_hdr;
typedef struct gpt_ent
{
uint32_t ent_type[4];
uint8_t ent_uuid[16];
uint64_t ent_lba_start;
uint64_t ent_lba_end;
uint64_t ent_attr;
uint16_t ent_name[36];
} gpt_ent;
#define GPT_HDR_SIG "EFI PART"
#define GPT_HDR_REVISION 0x00010000
#endif
+19
View File
@@ -0,0 +1,19 @@
#ifndef MBR_H
#define MBR_H
#define MBR_ADDRESS 0x1be
struct mbr_partition {
uint8_t bootid;
uint8_t starthead;
uint8_t startsect;
uint8_t startcyl;
uint8_t sysid;
uint8_t endhead;
uint8_t endsect;
uint8_t endcyl;
uint32_t startlba;
uint32_t size;
} __packed;
#endif
+8
View File
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
# mknand <image HFS+> <dossier nand de sortie> [version] — NAND au format VSVFL/FTL de production
# (qemu-ios-generate-nand 2ee0d46, supporte les écritures). version : 1 = iPhone OS 2.1.1, 2 = 2.2.x (défaut 1)
set -euo pipefail
here=$(dirname "$(readlink -f "$0")")
rm -rf "$2"
NAND_SIG_VERSION="${3:-1}" "$here/../qemu-ios-generate-nand/generate_nand" "$(readlink -f "$1")" "$(readlink -m "$2")" >/dev/null
printf 'vsvfl-production-v1\n' > "$2/.ipod-nand-format"
+7
View File
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
# mknand-v1 <image HFS+> <dossier nand de sortie> [version] — NAND au format compatible avec le FMSS actuel (lecture seule)
# version : version du format du driver NAND dans la signature (1 = iPhone OS 2.1.1, 2 = 2.2.x ; défaut 1)
set -euo pipefail
here=$(dirname "$(readlink -f "$0")"); img=$(readlink -f "$1"); out=$(readlink -m "$2"); tmp=$(mktemp -d)
cd "$tmp" && ln -s "$img" filesystem-it2g-readonly.img && NAND_SIG_VERSION="${3:-1}" "$here/generate_nand_v1" >/dev/null
rm -rf "$out" && mv nand "$out" && cd / && rm -rf "$tmp"
+74
View File
@@ -0,0 +1,74 @@
#ifndef VFL_H
#define VFL_H
#include "ftl.h"
#define VFL_CTX_SPARE_TYPE 0x80
#define VFL_INFO_SECTION_SIZE 4
#define VFL_BAD_MARK_INFO_TABLE_SIZE (WMR_MAX_VB / 8 / BAD_MARK_COMPRESS_SIZE)
#define VFL_NUM_OF_VFL_CXT_COPIES 8
#define VFL_AREA_START 0x0
#define VFL_FIRST_BLK_TO_SEARCH_CXT VFL_AREA_START
#define VFL_LAST_BLK_TO_SEARCH_CXT (VFL_AREA_START + 199)
#define AND_MAX_BANKS_PER_CS (8)
#define VFL_SCRUB_LIST_SIZE (20)
#define FTL_FORMAT_STRUCT_SIZE (16) // packed
#define BAD_MARK_COMPRESS_SIZE 8
#define VFL_BAD_MAP_TABLE_AVAILABLE_MARK 0xFFF0;
#define VFL_META_VERSION 0x00000002
#define VFL_VENDOR_SPECIFIC_TYPE 0x100014
typedef struct
{
uint32_t dwGlobalCxtAge; /* age for FTL meta information search */
uint32_t dwCxtAge; /* context age 0xFFFFFFFF --> 0x0 */
uint32_t dwFTLType; /* FTL identifier */
uint16_t wCxtLocation; /* current context block location (Physical) */
uint16_t wNextCxtPOffset; /* current context page offset information */
/* this data is used for summary */
uint16_t wNumOfInitBadBlk; /* the number of initial bad blocks - used for VFL format */
uint16_t wNumOfWriteFail; /* the number of failed write operations*/
uint16_t wNumOfEraseFail; /* the number of failed erase operations */
/* bad blocks management table & good block pointer */
uint16_t awReplacementIdx[AND_MAX_BANKS_PER_CS]; /* index to the last bad block updated in the awBadMapTable per bank
(some might be bad blocks) the array is divided to banks */
uint16_t awBadMapTable[WMR_MAX_RESERVED_SIZE]; /* remapping table of bad blocks - the UInt 16 value set here is the virtual block (vfl address space) that is being replaced */
/* bad blocks management table within VFL info area */
uint16_t awInfoBlk[VFL_INFO_SECTION_SIZE]; /* physical block addresses where FTL Cxt information is stored */
uint16_t wNumOfFTLSuBlk; /* the number of super blocks allocated for the FTL - default id to give all to FTL */
uint16_t wNumOfVFLSuBlk; /* the total number of available super blocks */
uint16_t awFTLCxtVbn[FTL_CXT_SECTION_SIZE]; /* page address (FTL virtual addressing space) of FTL Cxt */
uint16_t wScrubIdx; /* Index for the scrub list */
uint16_t awScrubList[VFL_SCRUB_LIST_SIZE]; /* scrub list */
uint8_t abFTLFormat[FTL_FORMAT_STRUCT_SIZE];
uint32_t abVSFormtType;
} __attribute__((packed)) VFLCxt;
typedef struct
{
VFLCxt stVFLCxt;
uint8_t abReserved[2048 - ((3 * sizeof(uint32_t)) + sizeof(VFLCxt))];
uint32_t dwVersion;
uint32_t dwCheckSum;
uint32_t dwXorSum;
} VFLMeta;
typedef struct
{
uint32_t dwCxtAge; /* context age 0xFFFFFFFF --> 0x0 */
uint32_t dwReserved; /* reserved */
uint8_t cStatusMark; /* status (confirm) mark - currently not used for anything */
uint8_t bSpareType; /* spare type */
/* reserved for main ECC */
} VFLSpare;
#endif
+11
View File
@@ -0,0 +1,11 @@
#ifndef WMR_H
#define WMR_H
#define WMR_MAX_RESERVED_SIZE 820
#define WMR_MAX_VB 18000
#define WMR_SECTORS_PER_PAGE_MIN 4
#define WMR_SECTOR_SIZE 512
#define WMR_MAX_PAGES_PER_BLOCK 0x100
#define WMR_NUM_OF_BANKS 8
#endif
+657
View File
@@ -0,0 +1,657 @@
#define SLIRP_DEPRECATED_OK 1
/*
* ipod-netd : réseau pour l'émulateur iPod Touch 2G, sans privilèges.
*
* Termine le lien PPP que pppd (côté iPhone OS) ouvre sur l'UART1 émulée, et passe les paquets IP à libslirp :
* NAT vers Internet, DNS (10.0.2.3), redirections de ports hôte -> iPod.
*
* ipod-netd [-s uart1.sock] [-L [addr:]hostport:guestport]... [-v]
*
* Adresses : iPod 10.0.2.15, passerelle 10.0.2.2, DNS 10.0.2.3. Défaut : -L 127.0.0.1:7788:7788 (emuagent).
*/
#include <arpa/inet.h>
#include <errno.h>
#include <poll.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <time.h>
#include <unistd.h>
#include <slirp/libslirp.h>
#define GUEST_IP "10.0.2.15"
#define GW_IP "10.0.2.2"
#define DNS_IP "10.0.2.3"
static const uint8_t guest_mac[6] = { 0x52, 0x54, 0x00, 0x12, 0x34, 0x56 };
static const uint8_t slirp_mac[6] = { 0x52, 0x55, 0x0a, 0x00, 0x02, 0x02 };
static int verbose;
static int uart = -1;
static const char *sock_path;
static Slirp *slirp;
static void logm(const char *fmt, ...)
{
va_list ap;
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
fprintf(stderr, "[%ld.%03ld] ", (long)ts.tv_sec % 100000, ts.tv_nsec / 1000000);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fputc('\n', stderr);
}
/* ---------- PPP : trames HDLC (RFC 1662) ---------- */
static uint16_t fcstab[256];
static void fcs_init(void)
{
for (int b = 0; b < 256; b++) {
uint16_t v = b;
for (int i = 0; i < 8; i++) {
v = (v & 1) ? (v >> 1) ^ 0x8408 : v >> 1;
}
fcstab[b] = v;
}
}
static uint16_t fcs16(uint16_t fcs, const uint8_t *p, size_t len)
{
while (len--) {
fcs = (fcs >> 8) ^ fcstab[(fcs ^ *p++) & 0xff];
}
return fcs;
}
static void ppp_send(uint16_t proto, const uint8_t *data, size_t len)
{
static uint8_t raw[4 + 2048], out[2 * (sizeof(raw) + 2) + 2];
if (uart < 0 || len > 2048) {
return;
}
raw[0] = 0xff; raw[1] = 0x03; raw[2] = proto >> 8; raw[3] = proto & 0xff;
memcpy(raw + 4, data, len);
size_t n = 4 + len;
uint16_t fcs = fcs16(0xffff, raw, n) ^ 0xffff;
raw[n++] = fcs & 0xff;
raw[n++] = fcs >> 8;
size_t o = 0;
out[o++] = 0x7e;
for (size_t i = 0; i < n; i++) {
uint8_t c = raw[i];
if (c < 0x20 || c == 0x7e || c == 0x7d) {
out[o++] = 0x7d;
c ^= 0x20;
}
out[o++] = c;
}
out[o++] = 0x7e;
for (size_t w = 0; w < o;) {
ssize_t r = write(uart, out + w, o - w);
if (r < 0) {
if (errno == EINTR || errno == EAGAIN) {
continue;
}
return;
}
w += r;
}
}
/* ---------- LCP / IPCP ---------- */
enum { CONF_REQ = 1, CONF_ACK, CONF_NAK, CONF_REJ, TERM_REQ, TERM_ACK, CODE_REJ, PROTO_REJ, ECHO_REQ, ECHO_REP, DISC_REQ };
#define PROTO_IP 0x0021
#define PROTO_LCP 0xc021
#define PROTO_IPCP 0x8021
struct ncp {
const char *name;
uint16_t proto;
int acked_peer; /* on a acquitté la requête du pair */
int acked_ours; /* le pair a acquitté la nôtre */
uint8_t id;
time_t last_req;
};
static struct ncp lcp = { "LCP", PROTO_LCP }, ipcp = { "IPCP", PROTO_IPCP };
static uint32_t lcp_magic = 0x49504f44; /* "IPOD" */
static int is_open(struct ncp *n)
{
return n->acked_peer && n->acked_ours;
}
static void send_ctl(uint16_t proto, uint8_t code, uint8_t id, const uint8_t *opts, size_t len)
{
uint8_t pkt[1500];
pkt[0] = code; pkt[1] = id; pkt[2] = (len + 4) >> 8; pkt[3] = (len + 4) & 0xff;
memcpy(pkt + 4, opts, len);
ppp_send(proto, pkt, len + 4);
}
static void send_conf_req(struct ncp *n)
{
uint8_t opts[16];
size_t len = 0;
if (n == &ipcp) {
struct in_addr a;
inet_aton(GW_IP, &a);
opts[len++] = 3; opts[len++] = 6;
memcpy(opts + len, &a, 4); len += 4;
} else {
opts[len++] = 5; opts[len++] = 6;
uint32_t m = htonl(lcp_magic);
memcpy(opts + len, &m, 4); len += 4;
}
send_ctl(n->proto, CONF_REQ, ++n->id, opts, len);
n->last_req = time(NULL);
}
static void guest_announce(void);
static void ncp_opened(struct ncp *n)
{
logm("%s ouvert", n->name);
if (n == &lcp) {
send_conf_req(&ipcp);
} else {
logm("lien IP prêt : iPod %s, passerelle %s, DNS %s", GUEST_IP, GW_IP, DNS_IP);
guest_announce();
}
}
/* Traite une Configure-Request du pair : Ack / Nak / Rej, option par option (RFC 1661 §5.1) */
static void handle_conf_req(struct ncp *n, uint8_t id, const uint8_t *opts, size_t len)
{
uint8_t ack[1500], nak[1500], rej[1500];
size_t na = 0, nn = 0, nr = 0;
struct in_addr want_ip, want_dns;
inet_aton(GUEST_IP, &want_ip);
inet_aton(DNS_IP, &want_dns);
for (size_t i = 0; i + 2 <= len;) {
uint8_t type = opts[i], olen = opts[i + 1];
if (olen < 2 || i + olen > len) {
break;
}
const uint8_t *o = opts + i;
int reject = 0, naked = 0;
if (n == &lcp) {
switch (type) {
case 1: /* MRU */
case 2: /* ACCM : on échappe de toute façon tous les caractères de contrôle */
case 5: /* magic */
case 7: /* PFC */
case 8: /* ACFC */
break;
default: /* authentification, callback... */
reject = 1;
}
} else {
if ((type == 3 || type == 129 || type == 131) && olen == 6) {
const struct in_addr *want = type == 3 ? &want_ip : &want_dns;
if (memcmp(o + 2, want, 4)) {
memcpy(nak + nn, o, 2);
memcpy(nak + nn + 2, want, 4);
nn += 6;
naked = 1;
}
} else {
reject = 1; /* compression VJ, NBNS... */
}
}
if (reject) {
memcpy(rej + nr, o, olen); nr += olen;
} else if (!naked) {
memcpy(ack + na, o, olen); na += olen;
}
i += olen;
}
if (nr) {
send_ctl(n->proto, CONF_REJ, id, rej, nr);
} else if (nn) {
send_ctl(n->proto, CONF_NAK, id, nak, nn);
} else {
int was_open = is_open(n);
send_ctl(n->proto, CONF_ACK, id, ack, na);
n->acked_peer = 1;
if (!n->acked_ours) {
send_conf_req(n);
}
if (!was_open && is_open(n)) {
ncp_opened(n);
}
}
}
static void handle_ctl(struct ncp *n, const uint8_t *p, size_t len)
{
if (len < 4) {
return;
}
uint8_t code = p[0], id = p[1];
size_t plen = (p[2] << 8) | p[3];
if (plen < 4 || plen > len) {
return;
}
const uint8_t *data = p + 4;
size_t dlen = plen - 4;
if (verbose) {
logm("%s <- code %d id %d len %zu", n->name, code, id, dlen);
}
switch (code) {
case CONF_REQ:
if (n == &lcp && is_open(&lcp)) { /* renégociation : tout repart de zéro */
lcp.acked_ours = 0;
ipcp.acked_peer = ipcp.acked_ours = 0;
}
if (n == &ipcp && !is_open(&lcp)) {
return;
}
handle_conf_req(n, id, data, dlen);
break;
case CONF_ACK:
if (id == n->id && !n->acked_ours) {
n->acked_ours = 1;
if (is_open(n)) {
ncp_opened(n);
}
}
break;
case CONF_NAK:
case CONF_REJ:
/* nos options sont minimales : on les renvoie sans l'option refusée */
send_ctl(n->proto, CONF_REQ, ++n->id, NULL, 0);
break;
case TERM_REQ:
send_ctl(n->proto, TERM_ACK, id, NULL, 0);
logm("%s fermé par l'iPod", n->name);
n->acked_peer = n->acked_ours = 0;
if (n == &lcp) {
ipcp.acked_peer = ipcp.acked_ours = 0;
}
break;
case ECHO_REQ:
if (n == &lcp && dlen >= 4) {
uint8_t rep[1500];
uint32_t m = htonl(lcp_magic);
memcpy(rep, &m, 4);
memcpy(rep + 4, data + 4, dlen - 4);
send_ctl(PROTO_LCP, ECHO_REP, id, rep, dlen);
}
break;
default:
break;
}
}
/* ---------- IP <-> slirp (trames Ethernet fictives) ---------- */
static void log_ip(const char *dir, const uint8_t *ip, size_t len)
{
if (!verbose || len < 20) return;
char a[16], b[16];
snprintf(a, sizeof a, "%d.%d.%d.%d", ip[12], ip[13], ip[14], ip[15]);
snprintf(b, sizeof b, "%d.%d.%d.%d", ip[16], ip[17], ip[18], ip[19]);
int ihl = (ip[0] & 0xf) * 4, proto = ip[9];
if (proto == 1 && len > (size_t)ihl + 1) logm("%s ICMP %s -> %s type %d code %d", dir, a, b, ip[ihl], ip[ihl + 1]);
else if ((proto == 6 || proto == 17) && len >= (size_t)ihl + 4)
logm("%s %s %s:%d -> %s:%d len %zu", dir, proto == 6 ? "TCP" : "UDP", a, (ip[ihl] << 8) | ip[ihl + 1], b, (ip[ihl + 2] << 8) | ip[ihl + 3], len);
else logm("%s proto %d %s -> %s", dir, proto, a, b);
}
static void slirp_ip_input(const uint8_t *ip, size_t len)
{
log_ip("iPod ->", ip, len);
uint8_t frame[14 + 2048];
if (len > 2048) {
return;
}
memcpy(frame, slirp_mac, 6);
memcpy(frame + 6, guest_mac, 6);
frame[12] = 0x08; frame[13] = 0x00;
memcpy(frame + 14, ip, len);
slirp_input(slirp, frame, len + 14);
}
static void arp_packet(uint16_t op, const uint8_t *dst_mac, const uint8_t *target_ip)
{
uint8_t f[42];
struct in_addr gip;
inet_aton(GUEST_IP, &gip);
memcpy(f, dst_mac, 6);
memcpy(f + 6, guest_mac, 6);
f[12] = 0x08; f[13] = 0x06;
f[14] = 0; f[15] = 1; f[16] = 8; f[17] = 0; f[18] = 6; f[19] = 4;
f[20] = 0; f[21] = op;
memcpy(f + 22, guest_mac, 6);
memcpy(f + 28, &gip, 4);
memcpy(f + 32, op == 2 ? dst_mac : (const uint8_t *)"\0\0\0\0\0\0", 6);
memcpy(f + 38, target_ip, 4);
slirp_input(slirp, f, sizeof f);
}
/* ARP gratuit : slirp apprend l'adresse MAC de l'iPod */
static void guest_announce(void)
{
static const uint8_t bcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
struct in_addr gw;
inet_aton(GW_IP, &gw);
arp_packet(1, bcast, (const uint8_t *)&gw);
}
static slirp_ssize_t slirp_send(const void *buf, size_t len, void *opaque)
{
const uint8_t *f = buf;
if (len < 14) {
return len;
}
uint16_t type = (f[12] << 8) | f[13];
if (type == 0x0806 && len >= 42 && f[21] == 1) {
struct in_addr gip;
inet_aton(GUEST_IP, &gip);
if (!memcmp(f + 38, &gip, 4)) {
arp_packet(2, f + 22, f + 28);
}
} else if (type == 0x0800 && is_open(&ipcp)) {
log_ip("-> iPod", f + 14, len - 14);
ppp_send(PROTO_IP, f + 14, len - 14);
}
return len;
}
/* ---------- réception PPP ---------- */
static void ppp_frame(uint8_t *p, size_t len)
{
if (len < 4 || fcs16(0xffff, p, len) != 0xf0b8) {
if (verbose && len) {
logm("trame invalide (%zu octets)", len);
}
return;
}
len -= 2;
if (len >= 2 && p[0] == 0xff && p[1] == 0x03) {
p += 2; len -= 2;
}
if (!len) {
return;
}
uint16_t proto;
if (p[0] & 1) {
proto = p[0]; p += 1; len -= 1;
} else {
if (len < 2) {
return;
}
proto = (p[0] << 8) | p[1]; p += 2; len -= 2;
}
switch (proto) {
case PROTO_LCP:
handle_ctl(&lcp, p, len);
break;
case PROTO_IPCP:
if (is_open(&lcp)) {
handle_ctl(&ipcp, p, len);
}
break;
case PROTO_IP:
if (is_open(&ipcp)) {
slirp_ip_input(p, len);
}
break;
default:
if (is_open(&lcp)) { /* IPV6CP, CCP... : Protocol-Reject */
uint8_t rej[1500];
size_t n = len > 1400 ? 1400 : len;
rej[0] = proto >> 8; rej[1] = proto & 0xff;
memcpy(rej + 2, p, n);
send_ctl(PROTO_LCP, PROTO_REJ, ++lcp.id, rej, n + 2);
}
}
}
static void uart_input(const uint8_t *buf, size_t n)
{
static uint8_t frame[4096];
static size_t flen;
static int esc;
for (size_t i = 0; i < n; i++) {
uint8_t c = buf[i];
if (c == 0x7e) {
ppp_frame(frame, flen);
flen = 0; esc = 0;
} else if (c == 0x7d) {
esc = 1;
} else {
if (esc) {
c ^= 0x20; esc = 0;
}
if (flen < sizeof frame) {
frame[flen++] = c;
}
}
}
}
/* ---------- boucle principale, timers slirp ---------- */
struct timer { SlirpTimerCb cb; void *cb_opaque; SlirpTimerId id; int by_id; int64_t expire; int used; };
static struct timer timers[16];
static int64_t now_ns(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * 1000000000LL + ts.tv_nsec;
}
static int64_t cb_clock(void *o) { return now_ns(); }
static void cb_guest_error(const char *msg, void *o) { logm("slirp : %s", msg); }
static void cb_notify(void *o) {}
static void cb_register_poll_fd(int fd, void *o) {} /* on repasse tous les sockets à poll() à chaque tour */
static void cb_unregister_poll_fd(int fd, void *o) {}
static struct timer *timer_alloc(void)
{
for (int i = 0; i < 16; i++) {
if (!timers[i].used) {
memset(&timers[i], 0, sizeof timers[i]);
timers[i].used = 1;
timers[i].expire = -1;
return &timers[i];
}
}
abort();
}
static void *cb_timer_new(SlirpTimerCb cb, void *cb_opaque, void *o)
{
struct timer *t = timer_alloc();
t->cb = cb; t->cb_opaque = cb_opaque;
return t;
}
static void *cb_timer_new_opaque(SlirpTimerId id, void *cb_opaque, void *o)
{
struct timer *t = timer_alloc();
t->id = id; t->by_id = 1; t->cb_opaque = cb_opaque;
return t;
}
static void cb_timer_free(void *t, void *o) { ((struct timer *)t)->used = 0; }
static void cb_timer_mod(void *t, int64_t expire_ms, void *o) { ((struct timer *)t)->expire = expire_ms; }
static struct pollfd pfds[1024];
static int npfds;
static int add_poll(slirp_os_socket fd, int events, void *o)
{
short ev = 0;
if (events & SLIRP_POLL_IN) ev |= POLLIN;
if (events & SLIRP_POLL_OUT) ev |= POLLOUT;
if (events & SLIRP_POLL_PRI) ev |= POLLPRI;
pfds[npfds].fd = fd;
pfds[npfds].events = ev;
pfds[npfds].revents = 0;
return npfds++;
}
static int get_revents(int idx, void *o)
{
short r = pfds[idx].revents;
int ev = 0;
if (r & POLLIN) ev |= SLIRP_POLL_IN;
if (r & POLLOUT) ev |= SLIRP_POLL_OUT;
if (r & POLLPRI) ev |= SLIRP_POLL_PRI;
if (r & POLLERR) ev |= SLIRP_POLL_ERR;
if (r & POLLHUP) ev |= SLIRP_POLL_HUP;
return ev;
}
static int uart_connect(void)
{
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un a = { .sun_family = AF_UNIX };
strncpy(a.sun_path, sock_path, sizeof a.sun_path - 1);
if (connect(fd, (struct sockaddr *)&a, sizeof a) < 0) {
close(fd);
return -1;
}
return fd;
}
static void add_fwd(const char *spec)
{
char buf[128], *parts[3];
int n = 0;
strncpy(buf, spec, sizeof buf - 1);
buf[sizeof buf - 1] = 0;
for (char *t = strtok(buf, ":"); t && n < 3; t = strtok(NULL, ":")) {
parts[n++] = t;
}
struct in_addr host, guest;
inet_aton("127.0.0.1", &host);
inet_aton(GUEST_IP, &guest);
int hp, gp;
if (n == 3) {
inet_aton(parts[0], &host);
hp = atoi(parts[1]); gp = atoi(parts[2]);
} else if (n == 2) {
hp = atoi(parts[0]); gp = atoi(parts[1]);
} else {
fprintf(stderr, "redirection invalide : %s\n", spec);
exit(1);
}
if (slirp_add_hostfwd(slirp, 0, host, hp, guest, gp) < 0) {
logm("redirection %s impossible (port occupé ?)", spec);
/* Ne pas laisser le client contacter le relais d'une autre session. */
exit(1);
} else {
logm("redirection tcp %s:%d -> iPod:%d", inet_ntoa(host), hp, gp);
}
}
int main(int argc, char **argv)
{
const char *fwds[32];
int nfwd = 0;
static char default_sock[512];
snprintf(default_sock, sizeof default_sock, "%s/ipod-emu/run/uart1.sock", getenv("HOME"));
sock_path = default_sock;
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-s") && i + 1 < argc) sock_path = argv[++i];
else if (!strcmp(argv[i], "-L") && i + 1 < argc && nfwd < 32) fwds[nfwd++] = argv[++i];
else if (!strcmp(argv[i], "-v")) verbose = 1;
else { fprintf(stderr, "usage: %s [-s uart1.sock] [-L [addr:]hostport:guestport]... [-v]\n", argv[0]); return 1; }
}
if (!nfwd) fwds[nfwd++] = "127.0.0.1:7788:7788";
signal(SIGPIPE, SIG_IGN);
fcs_init();
SlirpConfig cfg = { 0 };
cfg.version = 4;
cfg.in_enabled = true;
inet_aton("10.0.2.0", &cfg.vnetwork);
inet_aton("255.255.255.0", &cfg.vnetmask);
inet_aton(GW_IP, &cfg.vhost);
inet_aton(DNS_IP, &cfg.vnameserver);
inet_aton(GUEST_IP, &cfg.vdhcp_start);
cfg.vhostname = "ipod-emu-host";
cfg.if_mtu = 1500;
cfg.if_mru = 1500;
static const SlirpCb cb = {
.send_packet = slirp_send, .guest_error = cb_guest_error, .clock_get_ns = cb_clock,
.timer_new = cb_timer_new, .timer_free = cb_timer_free, .timer_mod = cb_timer_mod,
.notify = cb_notify, .timer_new_opaque = cb_timer_new_opaque,
.register_poll_fd = cb_register_poll_fd, .unregister_poll_fd = cb_unregister_poll_fd,
};
slirp = slirp_new(&cfg, &cb, NULL);
for (int i = 0; i < nfwd; i++) add_fwd(fwds[i]);
for (;;) {
if (uart < 0) {
uart = uart_connect();
if (uart < 0) {
sleep(1);
continue;
}
logm("connecté à %s, en attente de pppd", sock_path);
lcp.acked_peer = lcp.acked_ours = ipcp.acked_peer = ipcp.acked_ours = 0;
}
uint32_t timeout = 1000;
npfds = 0;
int uidx = add_poll(uart, SLIRP_POLL_IN, NULL);
slirp_pollfds_fill_socket(slirp, &timeout, add_poll, NULL);
int64_t now_ms = now_ns() / 1000000;
for (int i = 0; i < 16; i++) {
if (timers[i].used && timers[i].expire >= 0) {
int64_t d = timers[i].expire - now_ms;
if (d < 0) d = 0;
if (d < timeout) timeout = d;
}
}
int r = poll(pfds, npfds, timeout);
slirp_pollfds_poll(slirp, r < 0, get_revents, NULL);
now_ms = now_ns() / 1000000;
for (int i = 0; i < 16; i++) {
struct timer *t = &timers[i];
if (t->used && t->expire >= 0 && t->expire <= now_ms) {
t->expire = -1;
if (t->by_id) slirp_handle_timer(slirp, t->id, t->cb_opaque);
else t->cb(t->cb_opaque);
}
}
if (r > 0 && (pfds[uidx].revents & (POLLIN | POLLHUP | POLLERR))) {
uint8_t buf[4096];
ssize_t n = read(uart, buf, sizeof buf);
if (n <= 0) {
logm("UART déconnectée (émulateur arrêté ?)");
close(uart);
uart = -1;
continue;
}
uart_input(buf, n);
}
/* relance des Configure-Request non acquittées */
time_t t = time(NULL);
if (is_open(&lcp) && !ipcp.acked_ours && t - ipcp.last_req >= 2) send_conf_req(&ipcp);
if (lcp.acked_peer && !lcp.acked_ours && t - lcp.last_req >= 2) send_conf_req(&lcp);
}
}
@@ -0,0 +1,9 @@
diff --git a/generate_nand.c b/generate_nand.c
index 8fd3837..4ba5c12 100644
--- a/generate_nand.c
+++ b/generate_nand.c
@@ -212 +212,3 @@ static void write_nand_sig_page(void) {
- uint32_t signature = 0x43313131;
+ // the low byte is the NAND driver format version: '1' for iPhone OS 2.1.1, '2' for 2.2.x (NAND_SIG_VERSION)
+ const char *ver = getenv("NAND_SIG_VERSION");
+ uint32_t signature = 0x43313100 | (uint8_t)((ver && *ver) ? ver[0] : '1');
+8
View File
@@ -0,0 +1,8 @@
diff --git a/hfs/hfslib.c b/hfs/hfslib.c
index 2b0274b..78aa3bc 100644
--- a/hfs/hfslib.c
+++ b/hfs/hfslib.c
@@ -225,0 +226,3 @@ void grow_hfs(Volume* volume, uint64_t newSize) {
+ /* allocate new files in the new, contiguous free space: the free blocks left in a full image are scattered and
+ files spread over them need extents overflow records */
+ volume->volumeHeader->nextAllocation = volume->volumeHeader->totalBlocks;
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env python3
"""Page HTTP compatible Safari mobile 3 pour valider saisie et défilement.
Lancer sur l'hôte avec --output DOSSIER ; ouvrir l'URL 10.0.2.2 affichée dans
Safari invité. Le port et les requêtes reçues sont enregistrés dans ce dossier.
Arrêter avec Ctrl-C après le test. Aucun accès Internet n'est nécessaire.
"""
import argparse
from datetime import datetime, timezone
from html import escape
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
from pathlib import Path
from urllib.parse import parse_qs, urlparse
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--output', type=Path, required=True)
args = parser.parse_args()
args.output.mkdir(parents=True, exist_ok=True)
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
with (args.output / 'requests.jsonl').open('a', encoding='utf-8') as log:
log.write(json.dumps({'utc': datetime.now(timezone.utc).isoformat(),
'path': self.path}, ensure_ascii=False) + '\n')
url = urlparse(self.path)
if url.path == '/submit':
content = 'Recu : ' + escape(parse_qs(url.query).get('texte', [''])[0])
elif url.path == '/scroll':
content = '<h1>Debut</h1>' + ''.join(
'<p style="height:60px">Ligne %02d</p>' % i for i in range(24))
content += '<a href="/fin">Fin du defilement</a>'
elif url.path == '/fin':
content = '<h1>Defilement OK</h1>'
else:
content = ('<h1>Validation HTTP</h1><form action="/submit" method="get">'
'<input type="text" name="texte"><input type="submit" value="Envoyer">'
'</form><p><a href="/scroll">Test de defilement</a></p>')
body = ('<html><head><meta name="viewport" content="width=device-width">'
'<title>Test iPhone OS</title></head><body>' + content + '</body></html>').encode('utf-8')
self.send_response(200)
self.send_header('Content-Type', 'text/html; charset=utf-8')
self.send_header('Content-Length', str(len(body)))
self.end_headers()
self.wfile.write(body)
with HTTPServer(('127.0.0.1', 0), Handler) as server:
(args.output / 'port').write_text(str(server.server_port) + '\n', encoding='utf-8')
print(f'URL depuis l’iPod : http://10.0.2.2:{server.server_port}/', flush=True)
try:
server.serve_forever()
except KeyboardInterrupt:
pass
if __name__ == '__main__':
main()
+42
View File
@@ -0,0 +1,42 @@
#!/usr/bin/env python3
"""Compile et exécute les régressions tactiles avec ASan/UBSan sur un build QEMU existant."""
import argparse
import json
import os
from pathlib import Path
import shlex
import subprocess
p = argparse.ArgumentParser(description=__doc__)
p.add_argument('--build', type=Path)
a = p.parse_args()
root = Path(__file__).resolve().parents[3]
build = (a.build or root / 'build').resolve()
source = Path(__file__).with_name('test-multitouch.c')
commands = json.loads((build / 'compile_commands.json').read_text())
c = next(c for c in commands if c['file'].endswith('ipod_touch_multitouch.c'))
old_root = str(Path(c['directory']).parent)
args = shlex.split(c['command'].replace(old_root, str(root)))
obj = str(build / 'test-ipod-multitouch.o')
args[args.index('-c') + 1] = str(source)
args[args.index('-o') + 1] = obj
args[args.index('-MF') + 1] = obj + '.d'
args += ['-fsanitize=address,undefined', '-fno-omit-frame-pointer']
subprocess.run(args, cwd=build, check=True)
links = json.loads(subprocess.check_output(
['ninja', '-C', str(build), '-t', 'compdb', '-x', 'c_LINKER_RSP', 'c_LINKER'], text=True))
link = next(c for c in links if c.get('output') == 'qemu-system-arm')
args = shlex.split(link['command'].replace(old_root, str(root)))
args = [x for x in args if x not in (
'qemu-system-arm.p/system_main.c.o',
'libqemu-arm-softmmu.fa.p/hw_arm_ipod_touch_multitouch.c.o',
'libqemu-arm-softmmu.fa.p/hw_arm_ipod_touch_gpio.c.o',
'libqemu-arm-softmmu.fa.p/hw_arm_ipod_touch_sysic.c.o',
'libqemu-arm-softmmu.fa.p/hw_arm_ipod_touch_spi.c.o')]
out = str(build / 'test-ipod-multitouch')
args[args.index('-o') + 1] = out
args += [obj, '-fsanitize=address,undefined']
subprocess.run(args, cwd=build, check=True)
# QEMU conserve volontairement ses registres de types et sa boucle globale à la sortie.
env = dict(os.environ, ASAN_OPTIONS='detect_leaks=0', UBSAN_OPTIONS='halt_on_error=1')
subprocess.run([out], env=env, check=True)
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/env python3
"""Compile et exécute les régressions RTC avec ASan/UBSan sur un build QEMU existant."""
import argparse
import json
import os
from pathlib import Path
import shlex
import subprocess
p = argparse.ArgumentParser(description=__doc__)
p.add_argument('--build', type=Path)
a = p.parse_args()
root = Path(__file__).resolve().parents[3]
build = (a.build or root / 'build').resolve()
source = Path(__file__).with_name('test-rtc.c')
commands = json.loads((build / 'compile_commands.json').read_text())
c = next(c for c in commands if c['file'].endswith('ipod_touch_pcf50633_pmu.c'))
old_root = str(Path(c['directory']).parent)
args = shlex.split(c['command'].replace(old_root, str(root)))
obj = str(build / 'test-ipod-rtc.o')
args[args.index('-c') + 1] = str(source)
args[args.index('-o') + 1] = obj
args[args.index('-MF') + 1] = obj + '.d'
args += ['-fsanitize=address,undefined', '-fno-omit-frame-pointer']
subprocess.run(args, cwd=build, check=True)
links = json.loads(subprocess.check_output(
['ninja', '-C', str(build), '-t', 'compdb', '-x', 'c_LINKER_RSP', 'c_LINKER'], text=True))
link = next(c for c in links if c.get('output') == 'qemu-system-arm')
args = shlex.split(link['command'].replace(old_root, str(root)))
args = [x for x in args if x not in (
'qemu-system-arm.p/system_main.c.o',
'libqemu-arm-softmmu.fa.p/hw_arm_ipod_touch_pcf50633_pmu.c.o')]
out = str(build / 'test-ipod-rtc')
args[args.index('-o') + 1] = out
args += [obj, '-fsanitize=address,undefined']
subprocess.run(args, cwd=build, check=True)
# QEMU conserve volontairement ses registres de types et sa boucle globale à la sortie.
env = dict(os.environ, ASAN_OPTIONS='detect_leaks=0', UBSAN_OPTIONS='halt_on_error=1')
subprocess.run([out], env=env, check=True)
+200
View File
@@ -0,0 +1,200 @@
/* Régressions du protocole tactile : commandes invalides et trames concurrentes. */
#include "qemu/osdep.h"
#include "qemu/main-loop.h"
#include "sysemu/cpus.h"
#include "hw/arm/ipod_touch_multitouch.c"
#include "hw/arm/ipod_touch_gpio.c"
#include "hw/arm/ipod_touch_sysic.c"
#include "hw/arm/ipod_touch_spi.c"
static IPodTouchMultitouchState *new_sensor(void)
{
IPodTouchMultitouchState *s = IPOD_TOUCH_MULTITOUCH(
object_new(TYPE_IPOD_TOUCH_MULTITOUCH));
ipod_touch_multitouch_realize(SSI_PERIPHERAL(s), &error_abort);
return s;
}
static void destroy_sensor(IPodTouchMultitouchState *s)
{
finish_transfer(s);
timer_free(s->touch_timer);
timer_free(s->touch_end_timer);
g_free(s->next_frame);
g_free(s->read_frame);
object_unref(OBJECT(s));
}
static uint8_t transfer(IPodTouchMultitouchState *s, uint8_t byte)
{
return ipod_touch_multitouch_transfer(SSI_PERIPHERAL(s), byte);
}
static void test_unknown(void)
{
IPodTouchMultitouchState *s = new_sensor();
for (int i = 0; i < 10000; i++) {
g_assert_cmpuint(transfer(s, 0xab), ==, 0xff);
g_assert_cmpuint(s->cur_cmd, ==, 0);
g_assert_null(s->in_buffer);
}
uint8_t out[16];
out[0] = transfer(s, MT_CMD_GET_INTERFACE_VERSION);
for (int i = 1; i < 16; i++) out[i] = transfer(s, 0);
g_assert_cmpuint(out[2], ==, MT_INTERFACE_VERSION);
destroy_sensor(s);
}
static void test_snapshot(void)
{
IPodTouchMultitouchState *s = new_sensor();
MTFrame expected = *s->next_frame;
uint8_t out[sizeof(MTFrame)];
out[0] = transfer(s, MT_CMD_FRAME_READ);
for (int i = 1; i < 16; i++) out[i] = transfer(s, 0);
g_free(s->next_frame);
s->next_frame = get_frame(s, MT_EVENT_TOUCH_START, .4, .6,
100, 660, 580, 150);
g_assert_cmpuint(s->cur_cmd, ==, 0);
out[16] = transfer(s, 0xeb);
out[17] = transfer(s, 0);
out[18] = transfer(s, 1);
for (size_t i = 19; i < sizeof out; i++) out[i] = transfer(s, 0);
expected.frame_packet.unused1 = expected.frame_length.length1;
expected.frame_packet.checksum_pad = -(expected.frame_packet.cmd +
expected.frame_packet.unused1 + expected.frame_packet.length1 +
expected.frame_packet.length2);
g_assert_cmpmem(out, sizeof out, &expected, sizeof expected);
g_assert_cmpuint(s->next_frame->finger_data.event, ==, MT_EVENT_TOUCH_START);
destroy_sensor(s);
}
static void test_reports(void)
{
IPodTouchMultitouchState *s = new_sensor();
uint8_t out[16];
for (int cmd = MT_CMD_GET_REPORT_INFO; cmd <= MT_CMD_SHORT_CONTROL_READ; cmd += 3) {
out[0] = transfer(s, cmd);
out[1] = transfer(s, 0xff);
for (int i = 2; i < 16; i++) out[i] = transfer(s, 0);
g_assert_cmpuint(out[2], ==, 1);
unsigned sum = 0;
for (int i = 0; i < 14; i++) sum += out[i];
g_assert_cmpuint(lduw_le_p(out + 14), ==, sum);
}
transfer(s, MT_CMD_SHORT_CONTROL_READ);
transfer(s, MT_REPORT_SENSOR_DIMENSIONS);
for (int i = 2; i < 16; i++) out[i] = transfer(s, 0);
g_assert_cmpuint(ldl_le_p(out + 3), ==, 5000);
g_assert_cmpuint(ldl_le_p(out + 7), ==, 7500);
destroy_sensor(s);
}
static void test_spi_abort(void)
{
IPodTouchSPIState *spi = IPOD_TOUCH_SPI(object_new(TYPE_IPOD_TOUCH_SPI));
DEVICE(spi)->id = g_strdup("spi-test");
set_spi_base(4);
ipod_touch_spi_realize(DEVICE(spi), &error_abort);
ipod_touch_spi_write(spi, R_TXCNT, 2, 4);
ipod_touch_spi_write(spi, R_RXCNT, 2, 4);
ipod_touch_spi_write(spi, R_TXDATA, MT_CMD_FRAME_READ, 4);
ipod_touch_spi_write(spi, R_TXDATA, 2, 4);
ipod_touch_spi_write(spi, R_CTRL, R_CTRL_RUN, 4);
g_assert_cmpuint(spi->mt->in_buffer_ind, ==, 2);
ipod_touch_spi_write(spi, R_CTRL, R_CTRL_TX_RESET | R_CTRL_RX_RESET, 4);
g_assert_cmpuint(spi->mt->cur_cmd, ==, 0);
g_assert_null(spi->mt->in_buffer);
uint8_t response[16];
response[0] = transfer(spi->mt, MT_CMD_GET_INTERFACE_VERSION);
for (int i = 1; i < 16; i++) response[i] = transfer(spi->mt, 0);
g_assert_cmpuint(response[2], ==, MT_INTERFACE_VERSION);
timer_free(spi->mt->touch_timer);
timer_free(spi->mt->touch_end_timer);
g_free(spi->mt->next_frame);
fifo8_destroy(&spi->tx_fifo);
fifo8_destroy(&spi->rx_fifo);
object_unref(OBJECT(spi));
}
static void test_hbpp(void)
{
IPodTouchMultitouchState *s = new_sensor();
uint8_t bad[10] = {MT_CMD_HBPP_DATA_PACKET, 0, 0, 4};
for (int i = 0; i < 10; i++) transfer(s, bad[i]);
g_assert_cmpuint(s->cur_cmd, ==, 0);
uint8_t good[10] = {MT_CMD_HBPP_DATA_PACKET, 0, 0, 4, 0, 0, 0, 0, 0, 4};
for (int i = 0; i < 10; i++) transfer(s, good[i]);
for (int i = 1; i < 21; i++) transfer(s, 0);
g_assert_cmpuint(s->cur_cmd, ==, 0);
destroy_sensor(s);
}
static void test_velocity(void)
{
IPodTouchMultitouchState *s = new_sensor();
MTFrame *f = get_frame(s, MT_EVENT_TOUCH_MOVED, .4, .6, 1, 1, 1, 1);
g_free(f);
f = get_frame(s, MT_EVENT_TOUCH_MOVED, .4, .7, 1, 1, 1, 1);
g_assert_cmpint(f->finger_data.velX, ==, 0);
g_assert_cmpint(f->finger_data.velY, >, 0);
g_free(f);
f = get_frame(s, MT_EVENT_TOUCH_MOVED, .4, .7, 1, 1, 1, 1);
g_assert_cmpint(f->finger_data.velX, ==, 0);
g_assert_cmpint(f->finger_data.velY, ==, 0);
g_free(f);
destroy_sensor(s);
}
static void test_buttons(void)
{
IPodTouchGPIOState *s = IPOD_TOUCH_GPIO(object_new(TYPE_IPOD_TOUCH_GPIO));
g_assert_cmpuint(GPIO2PAD(GPIO_BUTTON_HOME), <, NUM_GPIO_PADS);
gpio_set_on(s->gpio_state, GPIO_BUTTON_HOME);
gpio_set_on(s->gpio_state, GPIO_BUTTON_POWER);
g_assert_true(gpio_is_on(s->gpio_state, GPIO_BUTTON_HOME));
g_assert_true(gpio_is_on(s->gpio_state, GPIO_BUTTON_POWER));
gpio_set_off(s->gpio_state, GPIO_BUTTON_HOME);
g_assert_true(gpio_is_off(s->gpio_state, GPIO_BUTTON_HOME));
g_assert_true(gpio_is_on(s->gpio_state, GPIO_BUTTON_POWER));
object_unref(OBJECT(s));
}
static void test_gpio_ranges(void)
{
IPodTouchSYSICState *s = IPOD_TOUCH_SYSIC(object_new(TYPE_IPOD_TOUCH_SYSIC));
uint32_t arrays[4][GPIO_NUMINTGROUPS];
s->gpio_int_level[0] = 0x1234;
memcpy(arrays[0], s->gpio_int_level, sizeof arrays[0]);
memcpy(arrays[1], s->gpio_int_status, sizeof arrays[1]);
memcpy(arrays[2], s->gpio_int_enabled, sizeof arrays[2]);
memcpy(arrays[3], s->gpio_int_type, sizeof arrays[3]);
/* Les quatre trous entre groupes ne doivent pas accéder au tableau suivant. */
for (int addr = GPIO_INTLEVEL; addr <= GPIO_INTTYPE; addr += 0x20) {
ipod_touch_sysic_write(s, addr + GPIO_NUMINTGROUPS * 4, 0xffffffff, 4);
g_assert_cmpuint(ipod_touch_sysic_read(s, addr + GPIO_NUMINTGROUPS * 4, 4), ==, 0);
}
g_assert_cmpmem(arrays[0], sizeof arrays[0], s->gpio_int_level, sizeof arrays[0]);
g_assert_cmpmem(arrays[1], sizeof arrays[1], s->gpio_int_status, sizeof arrays[1]);
g_assert_cmpmem(arrays[2], sizeof arrays[2], s->gpio_int_enabled, sizeof arrays[2]);
g_assert_cmpmem(arrays[3], sizeof arrays[3], s->gpio_int_type, sizeof arrays[3]);
object_unref(OBJECT(s));
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
module_call_init(MODULE_INIT_QOM);
qemu_init_main_loop(&error_abort);
qemu_init_cpu_loop();
qemu_mutex_lock_iothread();
g_test_add_func("/ipod-multitouch/unknown", test_unknown);
g_test_add_func("/ipod-multitouch/snapshot", test_snapshot);
g_test_add_func("/ipod-multitouch/hbpp", test_hbpp);
g_test_add_func("/ipod-multitouch/spi-abort", test_spi_abort);
g_test_add_func("/ipod-multitouch/reports", test_reports);
g_test_add_func("/ipod-multitouch/velocity", test_velocity);
g_test_add_func("/ipod-input/buttons", test_buttons);
g_test_add_func("/ipod-input/gpio-ranges", test_gpio_ranges);
return g_test_run();
}
+155
View File
@@ -0,0 +1,155 @@
/* Régressions du D1759 sur le bus I²C réel de QEMU. */
#include "qemu/osdep.h"
#include "qemu/main-loop.h"
#include "qemu/option.h"
#include "sysemu/cpus.h"
#include "hw/arm/ipod_touch_pcf50633_pmu.c"
static QemuOptsList rtc_test_opts = {
.name = "rtc-test",
.head = QTAILQ_HEAD_INITIALIZER(rtc_test_opts.head),
.desc = {
{ .name = "base", .type = QEMU_OPT_STRING },
{ .name = "clock", .type = QEMU_OPT_STRING },
{ }
}
};
static I2CBus *bus;
static Pcf50633State *pmu;
static DeviceState *parent;
static void setup(const char *base, const char *clock)
{
QemuOpts *opts = qemu_opts_create(&rtc_test_opts, NULL, 0, &error_abort);
qemu_opt_set(opts, "base", base, &error_abort);
qemu_opt_set(opts, "clock", clock, &error_abort);
configure_rtc(opts);
qemu_opts_del(opts);
parent = qdev_new(TYPE_PCF50633);
bus = i2c_init_bus(parent, "rtc-test");
pmu = PCF50633(i2c_slave_create_simple(bus, TYPE_D1759, 0x73));
}
static void teardown(void)
{
qdev_unrealize(DEVICE(pmu));
object_unref(OBJECT(parent));
}
static void select_register(uint8_t reg, bool stop)
{
g_assert_cmpint(i2c_start_transfer(bus, 0x73, false), ==, 0);
g_assert_cmpint(i2c_send(bus, reg), ==, 0);
if (stop) {
i2c_end_transfer(bus);
}
g_assert_cmpint(i2c_start_transfer(bus, 0x73, true), ==, 0);
}
static uint32_t read_word(uint8_t reg, bool stop)
{
uint32_t value = 0;
select_register(reg, stop);
for (int i = 0; i < 4; i++) {
value |= (uint32_t)i2c_recv(bus) << (i * 8);
}
i2c_end_transfer(bus);
return value;
}
static void write_word(uint8_t reg, uint32_t value)
{
g_assert_cmpint(i2c_start_transfer(bus, 0x73, false), ==, 0);
g_assert_cmpint(i2c_send(bus, reg), ==, 0);
for (int i = 0; i < 4; i++) {
g_assert_cmpint(i2c_send(bus, value >> (i * 8)), ==, 0);
}
i2c_end_transfer(bus);
}
static void test_host_utc(void)
{
time_t before = time(NULL);
/* Le fuseau de l'hôte ne doit pas décaler le compteur Unix. */
setenv("TZ", "Pacific/Honolulu", 1);
tzset();
setup("utc", "host");
uint32_t value = read_word(0x5c, false);
g_assert_cmpuint(value, >=, before);
g_assert_cmpuint(value, <=, time(NULL));
g_assert_cmpuint(read_word(0x64, true), ==, 0);
teardown();
unsetenv("TZ");
tzset();
}
static void test_fixed_base(void)
{
setup("2024-02-29T23:59:59", "vm");
g_assert_cmpint(rtc_clock, ==, QEMU_CLOCK_VIRTUAL);
g_assert_cmpuint(read_word(0x5c, true), ==, 1709251199);
g_assert_cmpuint(read_word(0x5c, false), ==, 1709251199);
teardown();
}
static void test_snapshot_rollover(void)
{
setup("2024-02-29T23:59:59", "vm");
uint32_t boundaries[] = { 1709251199, 0x00ffffff, 0xffffffff };
for (size_t n = 0; n < G_N_ELEMENTS(boundaries); n++) {
pmu->rtc_tick_offset = boundaries[n];
select_register(0x5c, false);
uint32_t value = i2c_recv(bus);
/* Le temps change entre deux octets, après capture du premier. */
pmu->rtc_tick_offset++;
for (int i = 1; i < 4; i++) {
value |= (uint32_t)i2c_recv(bus) << (8 * i);
}
i2c_end_transfer(bus);
g_assert_cmpuint(value, ==, boundaries[n]);
g_assert_cmpuint(read_word(0x5c, true), ==, boundaries[n] + 1);
}
teardown();
}
static void test_offsets(void)
{
setup("2024-02-29T23:59:59", "vm");
uint32_t counter = read_word(0x5c, true);
uint32_t offsets[] = { 3600, (uint32_t)-86400, 0x12345678, 0 };
for (size_t i = 0; i < G_N_ELEMENTS(offsets); i++) {
write_word(0x64, offsets[i]);
g_assert_cmpuint(read_word(0x64, false), ==, offsets[i]);
g_assert_cmpuint(read_word(0x5c, true), ==, counter);
}
/* Un changement de date ne doit pas réécrire le compteur matériel. */
write_word(0x5c, 1234);
g_assert_cmpuint(read_word(0x5c, false), ==, counter);
teardown();
}
static void test_boot_registers(void)
{
setup("2024-02-29T23:59:59", "vm");
write_word(0x60, 0xffffffff);
g_assert_cmpuint(read_word(0x60, true), ==, 0);
g_assert_cmpuint(read_word(0x5c, false), ==, 1709251199);
g_assert_cmpuint(read_word(0x64, true), ==, 0);
teardown();
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
module_call_init(MODULE_INIT_QOM);
qemu_init_main_loop(&error_abort);
qemu_init_cpu_loop();
qemu_mutex_lock_iothread();
g_test_add_func("/ipod-rtc/host-utc", test_host_utc);
g_test_add_func("/ipod-rtc/fixed-base", test_fixed_base);
g_test_add_func("/ipod-rtc/snapshot-rollover", test_snapshot_rollover);
g_test_add_func("/ipod-rtc/offsets", test_offsets);
g_test_add_func("/ipod-rtc/boot-registers", test_boot_registers);
return g_test_run();
}
+180
View File
@@ -0,0 +1,180 @@
#!/usr/bin/env python3
"""Teste les transferts réels de l'agent sur socket local, sans firmware Apple."""
import importlib.util
from importlib.machinery import SourceFileLoader
import io
import os
from pathlib import Path
import socket
import subprocess
import tempfile
import threading
import unittest
ROOT = Path(__file__).resolve().parents[1]
loader = SourceFileLoader('ipod_agent', str(ROOT / 'bin/ipod-agent'))
spec = importlib.util.spec_from_loader(loader.name, loader)
client = importlib.util.module_from_spec(spec)
loader.exec_module(client)
class Transfers(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.work = tempfile.TemporaryDirectory()
cls.binary = Path(cls.work.name) / 'transfer-agent'
source = (ROOT / 'agent/emuagent.c').read_text()
# Compile les fonctions de production, sans dépendances privées d'iOS.
functions = source[source.index('static int send_all('):source.index('/* tampon dynamique */')]
functions += source[source.index('static int read_full('):source.index('static void cmd_ls(')]
harness = '''#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <unistd.h>
static void mkdirs(const char *path) { (void)path; }
''' + functions + '''
int main(int argc, char **argv) {
if (argc < 4) return 2;
int fd = atoi(argv[1]);
if (!strcmp(argv[2], "get")) cmd_get(fd, argv+3, argc-3);
else cmd_put(fd, argv+3, argc-3);
char ignored[256]; read(fd, ignored, sizeof ignored);
close(fd); return 0;
}
'''
c = Path(cls.work.name) / 'transfer-agent.c'
c.write_text(harness)
subprocess.run(['cc', '-Wall', '-Wextra', '-Werror', '-fsanitize=undefined',
'-o', str(cls.binary), str(c)], check=True)
@classmethod
def tearDownClass(cls):
cls.work.cleanup()
def agent(self, command, *args):
a, b = socket.socketpair()
process = subprocess.Popen([str(self.binary), str(b.fileno()), command, *map(str, args)],
pass_fds=(b.fileno(),), env=dict(os.environ, UBSAN_OPTIONS='halt_on_error=1'))
b.close()
agent = client.Agent.__new__(client.Agent)
agent.s, agent.f = a, a.makefile('rb')
self.addCleanup(agent.f.close)
self.addCleanup(a.close)
self.addCleanup(lambda: self.assertEqual(process.wait(timeout=5), 0))
return agent
def test_large_round_trip(self):
data = os.urandom(8 * 1024 * 1024 + 37)
with tempfile.TemporaryDirectory() as d:
path = Path(d) / 'file'
a = self.agent('put', path, '640', len(data))
# Le harness reçoit les arguments hors bande, puis le contenu du protocole.
a.s.sendall(data)
ok, _, _ = a.call() # ligne vide ignorée par ce harness
self.assertTrue(ok)
self.assertEqual(path.read_bytes(), data)
self.assertEqual(path.stat().st_mode & 0o777, 0o640)
a = self.agent('get', path)
with (Path(d) / 'download').open('wb') as out:
ok, _, _ = a.call(sink=out)
self.assertTrue(ok)
self.assertEqual((Path(d) / 'download').read_bytes(), data)
def test_interrupted_put_preserves_existing_file(self):
with tempfile.TemporaryDirectory() as d:
path = Path(d) / 'file'
path.write_bytes(b'ancienne version')
a = self.agent('put', path, '644', 100000)
a.s.sendall(b'court')
a.s.shutdown(socket.SHUT_WR)
response = a.f.read()
self.assertTrue(response.startswith(b'ERR '))
self.assertEqual(path.read_bytes(), b'ancienne version')
self.assertEqual(list(Path(d).iterdir()), [path])
def test_empty_file(self):
with tempfile.TemporaryDirectory() as d:
path = Path(d) / 'empty'
a = self.agent('put', path, '644', 0)
self.assertTrue(a.call()[0])
self.assertEqual(path.read_bytes(), b'')
self.assertEqual(self.agent('get', path).call()[1], b'')
def test_download_failure_preserves_destination(self):
listener = socket.socket()
listener.bind(('127.0.0.1', 0)); listener.listen()
def server():
connection, _ = listener.accept()
connection.recv(1024)
connection.sendall(b'OK 100\npartiel')
connection.close()
thread = threading.Thread(target=server); thread.start()
try:
with tempfile.TemporaryDirectory() as d:
destination = Path(d) / 'download'
destination.write_bytes(b'original')
r = subprocess.run([str(ROOT / 'bin/ipod-agent'), 'get', '/file', str(destination)],
env=dict(os.environ, IPOD_AGENT_PORT=str(listener.getsockname()[1]),
IPOD_AGENT_HOST='127.0.0.1'), capture_output=True)
self.assertEqual(r.returncode, 1)
self.assertIn('tronquée', r.stderr.decode())
self.assertEqual(destination.read_bytes(), b'original')
self.assertEqual(list(Path(d).iterdir()), [destination])
finally:
thread.join(); listener.close()
def test_upload_respects_announced_size(self):
a, b = socket.socketpair()
agent = client.Agent.__new__(client.Agent)
agent.s, agent.f = a, a.makefile('rb')
received = []
def server():
f = b.makefile('rb')
received.append(f.readline())
received.append(f.read(3))
b.sendall(b'OK 0\n')
f.close(); b.close()
thread = threading.Thread(target=server); thread.start()
try:
data = io.BytesIO(b'abcEXTRA')
self.assertTrue(agent.call('put', '/file', '644', 3, data=data)[0])
self.assertEqual(data.tell(), 3)
self.assertEqual(received, [b'put\t/file\t644\t3\n', b'abc'])
finally:
agent.f.close(); a.close(); thread.join()
def test_get_rejects_fifo_without_blocking(self):
with tempfile.TemporaryDirectory() as d:
path = Path(d) / 'fifo'
os.mkfifo(path)
self.assertFalse(self.agent('get', path).call()[0])
def test_fragmented_and_truncated_reply(self):
for truncated in (False, True):
a, b = socket.socketpair()
agent = client.Agent.__new__(client.Agent)
agent.s, agent.f = a, a.makefile('rb')
def server():
b.recv(100)
for part in (b'OK ', b'10\n', b'123', b'4567', b'890' if not truncated else b''):
b.sendall(part)
b.close()
thread = threading.Thread(target=server)
thread.start()
try:
if truncated:
with self.assertRaisesRegex(ConnectionError, 'tronquée'):
agent.call('get', '/test')
else:
self.assertEqual(agent.call('get', '/test')[1], b'1234567890')
finally:
agent.f.close(); a.close(); thread.join()
if __name__ == '__main__':
unittest.main()
+5
View File
@@ -679,3 +679,8 @@ config ARMSSE
select UNIMP
select SSE_COUNTER
select SSE_TIMER
config IPOD_TOUCH_2G
bool
select IPOD_TOUCH_2G
select PL192
+586
View File
@@ -0,0 +1,586 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "hw/arm/boot.h"
#include "exec/address-spaces.h"
#include "hw/misc/unimp.h"
#include "hw/arm/ipod_touch_aes.h"
#include "hw/arm/ipod_touch_uart.h"
#include "hw/irq.h"
#include "sysemu/sysemu.h"
#include "sysemu/reset.h"
#include "hw/platform-bus.h"
#include "hw/block/flash.h"
#include "hw/qdev-clock.h"
#include "hw/arm/exynos4210.h"
#include "hw/arm/ipod_touch_2g.h"
#include "target/arm/cpregs.h"
#include "qemu/error-report.h"
#define VMSTATE_IT2G_CPREG(name) \
VMSTATE_UINT64(IT2G_CPREG_VAR_NAME(name), IPodTouchMachineState)
#define IT2G_CPREG_DEF(p_name, p_op0, p_op1, p_crn, p_crm, p_op2, p_access, p_reset) \
{ \
.cp = 15, \
.name = #p_name, .opc0 = p_op0, .crn = p_crn, .crm = p_crm, \
.opc1 = p_op1, .opc2 = p_op2, .access = p_access, .resetvalue = p_reset, \
.state = ARM_CP_STATE_AA32, .type = ARM_CP_OVERRIDE, \
.fieldoffset = offsetof(IPodTouchMachineState, IT2G_CPREG_VAR_NAME(p_name)) \
- offsetof(ARMCPU, env) \
}
static void allocate_ram(MemoryRegion *top, const char *name, uint32_t addr, uint32_t size)
{
MemoryRegion *sec = g_new(MemoryRegion, 1);
memory_region_init_ram(sec, NULL, name, size, &error_fatal);
memory_region_add_subregion(top, addr, sec);
}
static const ARMCPRegInfo it2g_cp_reginfo_tcg[] = {
IT2G_CPREG_DEF(REG0, 0, 0, 7, 6, 0, PL1_RW, 0),
IT2G_CPREG_DEF(REG1, 0, 0, 15, 2, 4, PL1_RW, 0),
IT2G_CPREG_DEF(REG1, 0, 0, 7, 14, 0, PL1_RW, 0),
IT2G_CPREG_DEF(REG1, 0, 0, 7, 10, 0, PL1_RW, 0),
};
static void ipod_touch_cpu_setup(MachineState *machine, MemoryRegion **sysmem, ARMCPU **cpu, AddressSpace **nsas)
{
Object *cpuobj = object_new(machine->cpu_type);
*cpu = ARM_CPU(cpuobj);
CPUState *cs = CPU(*cpu);
*sysmem = get_system_memory();
object_property_set_link(cpuobj, "memory", OBJECT(*sysmem), &error_abort);
object_property_set_bool(cpuobj, "has_el3", false, NULL);
object_property_set_bool(cpuobj, "has_el2", false, NULL);
object_property_set_bool(cpuobj, "realized", true, &error_fatal);
*nsas = cpu_get_address_space(cs, ARMASIdx_NS);
define_arm_cp_regs(*cpu, it2g_cp_reginfo_tcg);
object_unref(cpuobj);
}
static void ipod_touch_cpu_reset(void *opaque)
{
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE((MachineState *)opaque);
ARMCPU *cpu = nms->cpu;
CPUState *cs = CPU(cpu);
cpu_reset(cs);
//env->regs[0] = nms->kbootargs_pa;
//cpu_set_pc(CPU(cpu), 0xc00607ec);
cpu_set_pc(CPU(cpu), VROM_MEM_BASE);
//env->regs[0] = 0x9000000;
//cpu_set_pc(CPU(cpu), LLB_BASE + 0x100);
//cpu_set_pc(CPU(cpu), VROM_MEM_BASE);
}
static void ipod_touch_memory_setup(MachineState *machine, MemoryRegion *sysmem, AddressSpace *nsas)
{
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(machine);
allocate_ram(sysmem, "insecure_ram", INSECURE_RAM_MEM_BASE, 0x3000000);
allocate_ram(sysmem, "secure_ram", SECURE_RAM_MEM_BASE, 0x4B04000);
allocate_ram(sysmem, "iboot", IBOOT_MEM_BASE, 0x100000);
allocate_ram(sysmem, "llb", 0x22000000, 0x100000);
allocate_ram(sysmem, "sram1", SRAM1_MEM_BASE, 0x100000);
allocate_ram(sysmem, "framebuffer", FRAMEBUFFER_MEM_BASE, 0x400000);
allocate_ram(sysmem, "edgeic", EDGEIC_MEM_BASE, 0x1000);
allocate_ram(sysmem, "swi", SWI_MEM_BASE, 0x1000);
allocate_ram(sysmem, "h264", H264_MEM_BASE, 0x4000);
allocate_ram(sysmem, "mpvd", MPVD_MEM_BASE, 0x70000); // évite un kernel panic dans AppleMPVDDriver::setPowerStateGated
allocate_ram(sysmem, "unknown2", UNKNOWN2_MEM_BASE, 0x1000);
// load the bootrom (vrom)
uint8_t *file_data = NULL;
gsize fsize;
if (g_file_get_contents(nms->bootrom_path, (char **)&file_data, &fsize, NULL)) {
allocate_ram(sysmem, "vrom", 0x0, 0x20000);
address_space_rw(nsas, VROM_MEM_BASE, MEMTXATTRS_UNSPECIFIED, (uint8_t *)file_data, fsize, 1);
}
}
static char *ipod_touch_get_bootrom_path(Object *obj, Error **errp)
{
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
return g_strdup(nms->bootrom_path);
}
static void ipod_touch_set_bootrom_path(Object *obj, const char *value, Error **errp)
{
gboolean bootrom_exists = g_file_test(value, G_FILE_TEST_EXISTS);
if(!bootrom_exists) {
error_report("bootrom at path \"%s\" must exist", value);
exit(1);
}
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
g_strlcpy(nms->bootrom_path, value, sizeof(nms->bootrom_path));
}
static char *ipod_touch_get_nor_path(Object *obj, Error **errp)
{
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
return g_strdup(nms->nor_path);
}
static void ipod_touch_set_nor_path(Object *obj, const char *value, Error **errp)
{
gboolean nor_exists = g_file_test(value, G_FILE_TEST_EXISTS);
if(!nor_exists) {
error_report("NOR at path \"%s\" must exist", value);
exit(1);
}
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
g_strlcpy(nms->nor_path, value, sizeof(nms->nor_path));
}
static char *ipod_touch_get_nand_path(Object *obj, Error **errp)
{
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
return g_strdup(nms->nand_path);
}
static void ipod_touch_set_nand_path(Object *obj, const char *value, Error **errp)
{
gboolean nand_exists = g_file_test(value, G_FILE_TEST_IS_DIR);
if(!nand_exists) {
error_report("NAND at path \"%s\" must be a directory", value);
exit(1);
}
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
g_strlcpy(nms->nand_path, value, sizeof(nms->nand_path));
}
static bool ipod_touch_get_legacy_nor(Object *obj, Error **errp)
{
return ipod_touch_legacy_nor;
}
static void ipod_touch_set_legacy_nor(Object *obj, bool value, Error **errp)
{
ipod_touch_legacy_nor = value;
}
static bool ipod_touch_get_nand_write(Object *obj, Error **errp) { return ipod_touch_nand_write; }
static void ipod_touch_set_nand_write(Object *obj, bool value, Error **errp) { ipod_touch_nand_write = value; }
static void ipod_touch_instance_init(Object *obj)
{
object_property_add_str(obj, "bootrom", ipod_touch_get_bootrom_path, ipod_touch_set_bootrom_path);
object_property_set_description(obj, "bootrom", "Path to the S5L8720 bootrom binary");
object_property_add_str(obj, "nor", ipod_touch_get_nor_path, ipod_touch_set_nor_path);
object_property_set_description(obj, "nor", "Path to the S5L8720 NOR image");
object_property_add_str(obj, "nand", ipod_touch_get_nand_path, ipod_touch_set_nand_path);
object_property_set_description(obj, "nand", "Path to the NAND files");
object_property_add_bool(obj, "legacy-nor", ipod_touch_get_legacy_nor, ipod_touch_set_legacy_nor);
object_property_add_bool(obj, "nand-write", ipod_touch_get_nand_write, ipod_touch_set_nand_write);
object_property_set_description(obj, "nand-write", "Persist NAND page programs and block erases (requires production VSVFL image; default off)");
object_property_set_description(obj, "legacy-nor", "The NOR image has unencrypted SHSH tags (old qemu-ios-generate-nor, default on)");
}
static inline qemu_irq s5l8900_get_irq(IPodTouchMachineState *s, int n)
{
return s->irq[n / S5L8720_VIC_SIZE][n % S5L8720_VIC_SIZE];
}
static uint32_t s5l8720_usb_hwcfg[] = {
0,
0x7a8f60d0,
0x082000e8,
0x01f08024
};
static void ipod_touch_key_event(void *opaque, int keycode)
{
bool do_irq = false;
int gpio_group = 0, gpio_selector = 0;
IPodTouchMultitouchState *s = (IPodTouchMultitouchState *)opaque;
if(keycode == KEY_P_DOWN || keycode == KEY_P_UP) {
// power button
gpio_group = GPIO_BUTTON_POWER_IRQ / NUM_GPIO_PINS;
gpio_selector = GPIO_BUTTON_POWER_IRQ % NUM_GPIO_PINS;
if(keycode == KEY_P_DOWN && gpio_is_off(s->gpio_state->gpio_state, GPIO_BUTTON_POWER)) {
gpio_set_on(s->gpio_state->gpio_state, GPIO_BUTTON_POWER);
}
else if(keycode == KEY_P_UP) {
gpio_set_off(s->gpio_state->gpio_state, GPIO_BUTTON_POWER);
}
}
else if(keycode == KEY_H_DOWN || keycode == KEY_H_UP) {
// home button
gpio_group = GPIO_BUTTON_HOME_IRQ / NUM_GPIO_PINS;
gpio_selector = GPIO_BUTTON_HOME_IRQ % NUM_GPIO_PINS;
if(keycode == KEY_H_DOWN && gpio_is_off(s->gpio_state->gpio_state, GPIO_BUTTON_HOME)) {
gpio_set_on(s->gpio_state->gpio_state, GPIO_BUTTON_HOME);
}
else if(keycode == KEY_H_UP) {
gpio_set_off(s->gpio_state->gpio_state, GPIO_BUTTON_HOME);
}
}
else if(keycode == KEY_MIN_DOWN || keycode == KEY_MIN_UP) {
// volume down button
gpio_group = GPIO_BUTTON_VOLDOWN_IRQ / NUM_GPIO_PINS;
gpio_selector = GPIO_BUTTON_VOLDOWN_IRQ % NUM_GPIO_PINS;
if(keycode == KEY_MIN_DOWN && gpio_is_off(s->gpio_state->gpio_state, GPIO_BUTTON_VOLDOWN)) {
gpio_set_on(s->gpio_state->gpio_state, GPIO_BUTTON_VOLDOWN);
}
else if(keycode == KEY_MIN_UP) {
gpio_set_off(s->gpio_state->gpio_state, GPIO_BUTTON_VOLDOWN);
}
}
else if(keycode == KEY_PLUS_DOWN || keycode == KEY_PLUS_UP) {
// volume down button
gpio_group = GPIO_BUTTON_VOLUP_IRQ / NUM_GPIO_PINS;
gpio_selector = GPIO_BUTTON_VOLUP_IRQ % NUM_GPIO_PINS;
if(keycode == KEY_PLUS_DOWN && gpio_is_off(s->gpio_state->gpio_state, GPIO_BUTTON_VOLUP)) {
gpio_set_on(s->gpio_state->gpio_state, GPIO_BUTTON_VOLUP);
}
else if(keycode == KEY_PLUS_UP) {
gpio_set_off(s->gpio_state->gpio_state, GPIO_BUTTON_VOLUP);
}
}
else return;
s->sysic->gpio_int_status[gpio_group] |= (1 << gpio_selector);
qemu_irq_raise(s->sysic->gpio_irqs[gpio_group]);
}
static void ipod_touch_machine_init(MachineState *machine)
{
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(machine);
MemoryRegion *sysmem;
AddressSpace *nsas;
ARMCPU *cpu;
ipod_touch_cpu_setup(machine, &sysmem, &cpu, &nsas);
// setup clock
nms->sysclk = clock_new(OBJECT(machine), "SYSCLK");
clock_set_hz(nms->sysclk, 12000000ULL);
nms->cpu = cpu;
nms->nsas = nsas;
// setup VICs
nms->irq = g_malloc0(sizeof(qemu_irq *) * 2);
DeviceState *dev = pl192_manual_init("vic0", qdev_get_gpio_in(DEVICE(nms->cpu), ARM_CPU_IRQ), qdev_get_gpio_in(DEVICE(nms->cpu), ARM_CPU_FIQ), NULL);
PL192State *s = PL192(dev);
nms->vic0 = s;
memory_region_add_subregion(sysmem, VIC0_MEM_BASE, &nms->vic0->iomem);
nms->irq[0] = g_malloc0(sizeof(qemu_irq) * 32);
for (int i = 0; i < 32; i++) { nms->irq[0][i] = qdev_get_gpio_in(dev, i); }
dev = pl192_manual_init("vic1", NULL);
s = PL192(dev);
nms->vic1 = s;
memory_region_add_subregion(sysmem, VIC1_MEM_BASE, &nms->vic1->iomem);
nms->irq[1] = g_malloc0(sizeof(qemu_irq) * 32);
for (int i = 0; i < 32; i++) { nms->irq[1][i] = qdev_get_gpio_in(dev, i); }
// // chain VICs together
nms->vic1->daisy = nms->vic0;
// init clock 0
dev = qdev_new("ipodtouch.clock");
IPodTouchClockState *clock0_state = IPOD_TOUCH_CLOCK(dev);
nms->clock0 = clock0_state;
memory_region_add_subregion(sysmem, CLOCK0_MEM_BASE, &clock0_state->iomem);
// init clock 1
dev = qdev_new("ipodtouch.clock");
IPodTouchClockState *clock1_state = IPOD_TOUCH_CLOCK(dev);
nms->clock1 = clock1_state;
memory_region_add_subregion(sysmem, CLOCK1_MEM_BASE, &clock1_state->iomem);
// init the timer
dev = qdev_new("ipodtouch.timer");
IPodTouchTimerState *timer_state = IPOD_TOUCH_TIMER(dev);
nms->timer1 = timer_state;
memory_region_add_subregion(sysmem, TIMER1_MEM_BASE, &timer_state->iomem);
SysBusDevice *busdev = SYS_BUS_DEVICE(dev);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_TIMER1_IRQ));
//sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_TIMER1_IRQ - 1));
timer_state->sysclk = nms->sysclk;
// init sysic
dev = qdev_new("ipodtouch.sysic");
IPodTouchSYSICState *sysic_state = IPOD_TOUCH_SYSIC(dev);
nms->sysic = (IPodTouchSYSICState *) g_malloc0(sizeof(struct IPodTouchSYSICState));
memory_region_add_subregion(sysmem, SYSIC_MEM_BASE, &sysic_state->iomem);
busdev = SYS_BUS_DEVICE(dev);
for (int grp = 0; grp < G_N_ELEMENTS(S5L8900_GPIO_IRQS); grp++) {
sysbus_connect_irq(busdev, grp, s5l8900_get_irq(nms, S5L8900_GPIO_IRQS[grp]));
}
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_GPIO_G0_IRQ));
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_GPIO_G1_IRQ));
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_GPIO_G2_IRQ));
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_GPIO_G3_IRQ));
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_GPIO_G4_IRQ));
// init GPIO
dev = qdev_new("ipodtouch.gpio");
IPodTouchGPIOState *gpio_state = IPOD_TOUCH_GPIO(dev);
nms->gpio_state = gpio_state;
memory_region_add_subregion(sysmem, GPIO_MEM_BASE, &gpio_state->iomem);
// init SDIO
dev = qdev_new("ipodtouch.sdio");
IPodTouchSDIOState *sdio_state = IPOD_TOUCH_SDIO(dev);
nms->sdio_state = sdio_state;
memory_region_add_subregion(sysmem, SDIO_MEM_BASE, &sdio_state->iomem);
busdev = SYS_BUS_DEVICE(dev);
sysbus_realize(busdev, &error_fatal);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_SDIO_IRQ));
dev = ipod_touch_uart_create(UART0_MEM_BASE, serial_hd(0), nms->irq[0][24]);
if (!dev) {
hw_error("Failed to create UART0 device!");
}
dev = ipod_touch_uart_create(UART1_MEM_BASE, serial_hd(1), nms->irq[0][25]);
if (!dev) {
hw_error("Failed to create UART0 device!");
}
dev = ipod_touch_uart_create(UART2_MEM_BASE, serial_hd(2), nms->irq[0][26]);
if (!dev) {
hw_error("Failed to create UART0 device!");
}
dev = ipod_touch_uart_create(UART3_MEM_BASE, serial_hd(3), nms->irq[0][27]);
if (!dev) {
hw_error("Failed to create UART0 device!");
}
// dev = ipod_touch_uart_create(UART4_MEM_BASE, serial_hd(4), nms->irq[0][28]);
// if (!dev) {
// printf("Failed to create uart4 device!\n");
// abort();
// }
// init spis
set_spi_base(0);
dev = sysbus_create_simple("ipodtouch.spi", SPI0_MEM_BASE, s5l8900_get_irq(nms, S5L8720_SPI0_IRQ));
IPodTouchSPIState *spi0_state = IPOD_TOUCH_SPI(dev);
spi0_state->nor->nor_path = nms->nor_path;
nms->spi0_state = spi0_state;
set_spi_base(1);
dev = sysbus_create_simple("ipodtouch.spi", SPI1_MEM_BASE, s5l8900_get_irq(nms, S5L8720_SPI1_IRQ));
IPodTouchSPIState *spi1_state = IPOD_TOUCH_SPI(dev);
nms->spi1_state = spi1_state;
set_spi_base(2);
sysbus_create_simple("ipodtouch.spi", SPI2_MEM_BASE, s5l8900_get_irq(nms, S5L8720_SPI2_IRQ));
set_spi_base(3);
sysbus_create_simple("ipodtouch.spi", SPI3_MEM_BASE, s5l8900_get_irq(nms, S5L8720_SPI3_IRQ));
set_spi_base(4);
dev = sysbus_create_simple("ipodtouch.spi", SPI4_MEM_BASE, s5l8900_get_irq(nms, S5L8720_SPI4_IRQ));
IPodTouchSPIState *spi4_state = IPOD_TOUCH_SPI(dev);
spi4_state->mt->sysic = sysic_state;
spi4_state->mt->gpio_state = gpio_state;
nms->spi4_state = spi4_state;
// init the chip ID module
dev = qdev_new("ipodtouch.chipid");
IPodTouchChipIDState *chipid_state = IPOD_TOUCH_CHIPID(dev);
nms->chipid_state = chipid_state;
memory_region_add_subregion(sysmem, CHIPID_MEM_BASE, &chipid_state->iomem);
// init the TVOut instance
dev = qdev_new("ipodtouch.tvout");
IPodTouchTVOutState *tvout_state = IPOD_TOUCH_TVOUT(dev);
nms->tvout_state = tvout_state;
memory_region_add_subregion(sysmem, TVOUT_MIXER1_MEM_BASE, &tvout_state->mixer1_iomem);
memory_region_add_subregion(sysmem, TVOUT_MIXER2_MEM_BASE, &tvout_state->mixer2_iomem);
memory_region_add_subregion(sysmem, TVOUT_SDO_MEM_BASE, &tvout_state->sdo_iomem);
busdev = SYS_BUS_DEVICE(dev);
sysbus_realize(busdev, &error_fatal);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_TVOUT_SDO_IRQ));
// init the unknown1 module
dev = qdev_new("ipodtouch.unknown1");
IPodTouchUnknown1State *unknown1_state = IPOD_TOUCH_UNKNOWN1(dev);
memory_region_add_subregion(sysmem, UNKNOWN1_MEM_BASE, &unknown1_state->iomem);
// init USB OTG
dev = ipod_touch_init_usb_otg(s5l8900_get_irq(nms, S5L8720_USB_OTG_IRQ), s5l8720_usb_hwcfg);
synopsys_usb_state *usb_otg = S5L8900USBOTG(dev);
nms->usb_otg = usb_otg;
memory_region_add_subregion(sysmem, USBOTG_MEM_BASE, &nms->usb_otg->iomem);
// init two pl080 DMAC0 devices
dev = qdev_new("pl080");
PL080State *pl080_1 = PL080(dev);
object_property_set_link(OBJECT(dev), "downstream", OBJECT(sysmem), &error_fatal);
memory_region_add_subregion(sysmem, DMAC0_MEM_BASE, &pl080_1->iomem1);
busdev = SYS_BUS_DEVICE(dev);
sysbus_realize(busdev, &error_fatal);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_DMAC0_IRQ));
dev = qdev_new("pl080");
PL080State *pl080_2 = PL080(dev);
object_property_set_link(OBJECT(dev), "downstream", OBJECT(sysmem), &error_fatal);
memory_region_add_subregion(sysmem, DMAC1_0_MEM_BASE, &pl080_2->iomem1);
memory_region_add_subregion(sysmem, DMAC1_1_MEM_BASE, &pl080_2->iomem2);
busdev = SYS_BUS_DEVICE(dev);
sysbus_realize(busdev, &error_fatal);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_DMAC1_IRQ));
// Init I2C0
dev = qdev_new("ipodtouch.i2c");
IPodTouchI2CState *i2c_state = IPOD_TOUCH_I2C(dev);
i2c_state->base = 0;
nms->i2c0_state = i2c_state;
busdev = SYS_BUS_DEVICE(dev);
memory_region_add_subregion(sysmem, I2C0_MEM_BASE, &i2c_state->iomem);
sysbus_realize(busdev, &error_fatal);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_I2C0_IRQ));
// init the PMU
I2CSlave * pmu = i2c_slave_create_simple(i2c_state->bus, "d1759", 0x73);
// init the accelerometer
I2CSlave *accelerometer = i2c_slave_create_simple(i2c_state->bus, "lis302dl", 0x1D);
// init the audio codec (disabled because unused)
// I2CSlave *audio_codec = i2c_slave_create_simple(i2c_state->bus, "cs42l58", 0x4A);
// Init I2C1
dev = qdev_new("ipodtouch.i2c");
i2c_state = IPOD_TOUCH_I2C(dev);
nms->i2c1_state = i2c_state;
i2c_state->base = 1;
busdev = SYS_BUS_DEVICE(dev);
memory_region_add_subregion(sysmem, I2C1_MEM_BASE, &i2c_state->iomem);
sysbus_realize(busdev, &error_fatal);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_I2C1_IRQ));
// Init the light sensor
I2CSlave *isl29003dl = i2c_slave_create_simple(i2c_state->bus, "isl29003dl", 0x44);
// init the Mikey
I2CSlave *cd327mikey = i2c_slave_create_simple(i2c_state->bus, "cd3272mikey", 0x39);
// init the FMSS flash controller
dev = qdev_new("ipodtouch.fmss");
IPodTouchFMSSState *fmss_state = IPOD_TOUCH_FMSS(dev);
fmss_state->nand_path = nms->nand_path;
nms->fmss_state = fmss_state;
busdev = SYS_BUS_DEVICE(dev);
memory_region_add_subregion(sysmem, FMSS_MEM_BASE, &fmss_state->iomem);
sysbus_realize(busdev, &error_fatal);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_FMSS_IRQ));
// init the USB module
dev = qdev_new("ipodtouch.usbphys");
IPodTouchUSBPhysState *usb_phys_state = IPOD_TOUCH_USB_PHYS(dev);
nms->usb_phys_state = usb_phys_state;
memory_region_add_subregion(sysmem, USBPHYS_MEM_BASE, &usb_phys_state->iomem);
ipod_touch_memory_setup(machine, sysmem, nsas);
// init the MIPI SDI controller
dev = qdev_new("ipodtouch.mipidsi");
IPodTouchMIPIDSIState *mipi_dsi_state = IPOD_TOUCH_MIPI_DSI(dev);
nms->mipi_dsi_state = mipi_dsi_state;
memory_region_add_subregion(sysmem, MIPI_DSI_MEM_BASE, &mipi_dsi_state->iomem);
// init LCD
dev = qdev_new("ipodtouch.lcd");
IPodTouchLCDState *lcd_state = IPOD_TOUCH_LCD(dev);
lcd_state->sysmem = sysmem;
lcd_state->mt = spi4_state->mt;
nms->lcd_state = lcd_state;
busdev = SYS_BUS_DEVICE(dev);
memory_region_add_subregion(sysmem, DISPLAY_MEM_BASE, &lcd_state->iomem);
sysbus_realize(busdev, &error_fatal);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_LCD_IRQ));
// init scaler / CSC
dev = qdev_new("ipodtouch.scalercsc");
IPodTouchScalerCSCState *scaler_csc_state = IPOD_TOUCH_SCALER_CSC(dev);
nms->scaler_csc_state = scaler_csc_state;
busdev = SYS_BUS_DEVICE(dev);
memory_region_add_subregion(sysmem, SCALER_CSC_MEM_BASE, &scaler_csc_state->iomem);
sysbus_realize(busdev, &error_fatal);
// init SHA1 engine
dev = qdev_new("ipodtouch.sha1");
IPodTouchSHA1State *sha1_state = IPOD_TOUCH_SHA1(dev);
nms->sha1_state = sha1_state;
memory_region_add_subregion(sysmem, SHA1_MEM_BASE, &sha1_state->iomem);
sysbus_realize(SYS_BUS_DEVICE(dev), &error_fatal);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, s5l8900_get_irq(nms, S5L8720_SHA1_IRQ));
// init AES engine
dev = qdev_new("ipodtouch.aes");
IPodTouchAESState *aes_state = IPOD_TOUCH_AES(dev);
nms->aes_state = aes_state;
memory_region_add_subregion(sysmem, AES_MEM_BASE, &aes_state->iomem);
// init PKE engine
dev = qdev_new("ipodtouch.pke");
IPodTouchPKEState *pke_state = IPOD_TOUCH_PKE(dev);
nms->pke_state = pke_state;
memory_region_add_subregion(sysmem, PKE_MEM_BASE, &pke_state->iomem);
// init the MBX
dev = qdev_new("ipodtouch.mbx");
IPodTouchMBXState *mbx_state = IPOD_TOUCH_MBX(dev);
nms->mbx_state = mbx_state;
memory_region_add_subregion(sysmem, MBX1_MEM_BASE, &mbx_state->iomem1);
memory_region_add_subregion(sysmem, MBX2_MEM_BASE, &mbx_state->iomem2);
qemu_register_reset(ipod_touch_cpu_reset, nms);
qemu_add_kbd_event_handler(ipod_touch_key_event, spi4_state->mt);
}
static void ipod_touch_machine_class_init(ObjectClass *klass, void *data)
{
MachineClass *mc = MACHINE_CLASS(klass);
mc->desc = "iPod Touch";
mc->init = ipod_touch_machine_init;
mc->max_cpus = 1;
mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm1176");
}
static const TypeInfo ipod_touch_machine_info = {
.name = TYPE_IPOD_TOUCH_MACHINE,
.parent = TYPE_MACHINE,
.instance_size = sizeof(IPodTouchMachineState),
.class_size = sizeof(IPodTouchMachineClass),
.class_init = ipod_touch_machine_class_init,
.instance_init = ipod_touch_instance_init,
};
static void ipod_touch_machine_types(void)
{
type_register_static(&ipod_touch_machine_info);
}
type_init(ipod_touch_machine_types)
+236
View File
@@ -0,0 +1,236 @@
#include "hw/arm/ipod_touch_aes.h"
#include "hw/arm/ipod_touch_gid_keys.h"
const char *ipod_touch_fw_build = NULL;
bool ipod_touch_legacy_nor = true;
/*
* Device tree patches, applied once iBoot has decrypted it (its signature has been checked by then).
* uart1 (/dev/tty.bluetooth, the PPP line to the host) loses its DMA channels: the serial driver then receives in PIO
* mode, which the emulated UART supports (the emulated DMA controller does not serve the UART).
*/
static uint32_t dt_node(uint8_t *d, uint32_t len, uint32_t o, int in_uart1)
{
if(o + 8 > len) return len;
uint32_t nprops = ldl_le_p(d + o), nchildren = ldl_le_p(d + o + 4);
o += 8;
uint32_t props = o;
int is_uart1 = 0;
for(uint32_t i = 0; i < nprops && o + 36 <= len; i++) {
uint32_t plen = ldl_le_p(d + o + 32) & 0x7fffffff;
if(!strcmp((char *)d + o, "name") && plen >= 6 && !memcmp(d + o + 36, "uart1", 6)) is_uart1 = 1;
o += 36 + ((plen + 3) & ~3);
}
if(is_uart1) {
for(uint32_t p = props, i = 0; i < nprops && p + 36 <= len; i++) {
uint32_t plen = ldl_le_p(d + p + 32) & 0x7fffffff;
if(!strcmp((char *)d + p, "dma-channels") || !strcmp((char *)d + p, "dma-types") || !strcmp((char *)d + p, "dma-parent")) {
d[p] = 'x';
printf("AES: device tree: uart1 %s disabled (PIO serial)\n", (char *)d + p);
}
p += 36 + ((plen + 3) & ~3);
}
}
for(uint32_t i = 0; i < nchildren && o < len; i++) o = dt_node(d, len, o, is_uart1);
return o;
}
static const IPodTouchGIDKey *ipod_touch_find_gid_key(const uint8_t *kbag, uint32_t len)
{
if(len < 32) return NULL;
for(size_t i = 0; i < ARRAY_SIZE(ipod_touch_gid_keys); i++) {
if(memcmp(ipod_touch_gid_keys[i].kbag, kbag, 32) == 0) return &ipod_touch_gid_keys[i];
}
return NULL;
}
// iBoot checks that the LZSS decompression produced exactly the expected number of bytes. NOP out the
// "cmp r5, r3; bne" (same code in iBoot-385.22 and iBoot-385.49, at a different address).
static void ipod_touch_patch_iboot_lzss_check(void)
{
static const uint8_t sig[] = { 0x04, 0x23, 0x00, 0xe0, 0x02, 0x23, 0x53, 0x43, 0x9d, 0x42, 0x20, 0xd1 };
const uint32_t iboot_base = 0x0ff00000, iboot_size = 0x29000;
uint8_t *iboot = g_malloc(iboot_size);
cpu_physical_memory_read(iboot_base, iboot, iboot_size);
for(uint32_t off = 0; off + sizeof(sig) <= iboot_size; off += 2) {
if(memcmp(iboot + off, sig, sizeof(sig)) == 0) {
uint32_t nop = 0;
cpu_physical_memory_write(iboot_base + off + 8, &nop, 4);
printf("AES: patched iBoot LZSS length check at 0x%08x\n", iboot_base + off + 8);
break;
}
}
g_free(iboot);
}
static uint64_t ipod_touch_aes_read(void *opaque, hwaddr offset, unsigned size)
{
struct IPodTouchAESState *aesop = (struct IPodTouchAESState *)opaque;
switch(offset) {
case AES_STATUS:
return aesop->status;
default:
//fprintf(stderr, "%s: UNMAPPED AES_ADDR @ offset 0x%08x\n", __FUNCTION__, offset);
break;
}
return 0;
}
static void ipod_touch_aes_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
{
struct IPodTouchAESState *aesop = (struct IPodTouchAESState *)opaque;
uint8_t *inbuf;
uint8_t *buf;
// fprintf(stderr, "%s: offset 0x%08x value 0x%08x\n", __FUNCTION__, offset, value);
switch(offset) {
case AES_GO:
// PATCH OUT
// in iBoot, we also patch the length check after LZSS compression since that can lead to issues
inbuf = (uint8_t *)malloc(aesop->insize);
cpu_physical_memory_read((aesop->inaddr), inbuf, aesop->insize);
switch(aesop->keytype) {
case AESGID:
break;
case AESUID:
AES_set_decrypt_key(key_uid, sizeof(key_uid) * 8, &aesop->decryptKey);
break;
case AESCustom:
AES_set_decrypt_key((uint8_t *)(&aesop->custkey[4]), 0x10 * 8, &aesop->decryptKey);
break;
}
buf = (uint8_t *) malloc(aesop->insize);
printf("In size: %d, out size: %d, in addr: 0x%08x, in buf: 0x%08x, out addr: 0x%08x, %d\n", aesop->insize, aesop->outsize, aesop->inaddr, ((uint32_t *)inbuf)[0], aesop->outaddr, aesop->gid_encryption_count);
if(aesop->keytype == AESGID) {
// We don't have the GID key. The GID key is only used to decrypt the KBAG of an IMG3 image, so we
// look the KBAG up in the table of published keys and return its IV + key.
const IPodTouchGIDKey *gk = ipod_touch_find_gid_key(inbuf, aesop->insize);
if(gk) {
printf("AES: GID key for %s %s\n", gk->build, gk->image);
ipod_touch_fw_build = gk->build;
aesop->next_is_devicetree = strncmp(gk->image, "DeviceTree", 10) == 0;
memcpy(buf, gk->iv_key, MIN(aesop->insize, sizeof(gk->iv_key)));
} else {
printf("AES: unknown KBAG %08x (GID decryption #%d), output left zeroed\n", ((uint32_t *)inbuf)[0], aesop->gid_encryption_count);
memset(buf, 0, aesop->insize);
}
// the image after iBoot is decompressed by iBoot: skip its length check after the LZSS decompression
if(aesop->gid_encryption_count == 2) {
ipod_touch_patch_iboot_lzss_check();
}
aesop->gid_encryption_count++;
}
else {
// IMG3 payloads are encrypted in whole AES blocks only: a trailing partial block is stored in clear
// (e.g. the kernelcache and the device tree). Decrypt the whole blocks and copy the rest unchanged.
uint32_t full = aesop->insize & ~(AES_BLOCK_SIZE - 1);
AES_cbc_encrypt(inbuf, buf, full, &aesop->decryptKey, (uint8_t *)aesop->ivec, AES_DECRYPT);
memcpy(buf + full, inbuf + full, aesop->insize - full);
// the payload following the device tree KBAG is the device tree itself
if(aesop->next_is_devicetree && aesop->insize > 0x1000) {
dt_node(buf, aesop->insize, 0, 0);
aesop->next_is_devicetree = false;
}
}
// the NOR images generated by older versions of qemu-ios-generate-nor have an unencrypted SHSH tag: leave it
// as is (only the 5F138 NOR image published with qemu-ios is concerned)
bool legacy_shsh = aesop->outaddr == 0x220100ac || aesop->outaddr == 0x0bf08468 || aesop->outaddr == 0x0fb9bcdc;
if(!(legacy_shsh && ipod_touch_legacy_nor)) {
cpu_physical_memory_write((aesop->outaddr), buf, aesop->insize);
}
memset(aesop->custkey, 0, 0x20);
memset(aesop->ivec, 0, 0x10);
free(inbuf);
free(buf);
aesop->outsize = aesop->insize;
aesop->status = 0xf;
break;
case AES_KEYLEN:
aesop->operation = value;
aesop->keylen = value;
break;
case AES_INADDR:
aesop->inaddr = value;
break;
case AES_INSIZE:
aesop->insize = value;
break;
case AES_OUTSIZE:
aesop->outsize = value;
break;
case AES_OUTADDR:
aesop->outaddr = value;
break;
case AES_TYPE:
aesop->keytype = value;
break;
case AES_KEY_REG ... ((AES_KEY_REG + AES_KEYSIZE) - 1):
{
uint8_t idx = (offset - AES_KEY_REG) / 4;
aesop->custkey[idx] |= value;
break;
}
case AES_IV_REG ... ((AES_IV_REG + AES_IVSIZE) -1 ):
{
uint8_t idx = (offset - AES_IV_REG) / 4;
aesop->ivec[idx] |= value;
break;
}
default:
//fprintf(stderr, "%s: UNMAPPED AES_ADDR @ offset 0x%08x - 0x%08x\n", __FUNCTION__, offset, value);
break;
}
}
static const MemoryRegionOps aes_ops = {
.read = ipod_touch_aes_read,
.write = ipod_touch_aes_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void ipod_touch_aes_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
DeviceState *dev = DEVICE(sbd);
IPodTouchAESState *s = IPOD_TOUCH_AES(dev);
memory_region_init_io(&s->iomem, obj, &aes_ops, s, "aes", 0x100);
sysbus_init_mmio(sbd, &s->iomem);
memset(&s->custkey, 0, 8 * sizeof(uint32_t));
memset(&s->ivec, 0, 4 * sizeof(uint32_t));
s->gid_encryption_count = 0;
}
static void ipod_touch_aes_class_init(ObjectClass *klass, void *data)
{
}
static const TypeInfo ipod_touch_aes_info = {
.name = TYPE_IPOD_TOUCH_AES,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IPodTouchAESState),
.instance_init = ipod_touch_aes_init,
.class_init = ipod_touch_aes_class_init,
};
static void ipod_touch_machine_types(void)
{
type_register_static(&ipod_touch_aes_info);
}
type_init(ipod_touch_machine_types)
+58
View File
@@ -0,0 +1,58 @@
#include "hw/arm/ipod_touch_cd3272_mikey.h"
static int cd3272_mikey_event(I2CSlave *i2c, enum i2c_event event)
{
return 0;
}
static uint8_t cd3272_mikey_recv(I2CSlave *i2c)
{
CD3272MikeyState *s = CD3272MIKEY(i2c);
printf("Reading mikey register %d\n", s->cmd);
int res = 0;
switch(s->cmd) {
default:
res = 0;
}
s->cmd += 1;
return res;
}
static int cd3272_mikey_send(I2CSlave *i2c, uint8_t data)
{
CD3272MikeyState *s = CD3272MIKEY(i2c);
s->cmd = data;
return 0;
}
static void cd3272_mikey_init(Object *obj)
{
}
static void cd3272_mikey_class_init(ObjectClass *klass, void *data)
{
I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
k->event = cd3272_mikey_event;
k->recv = cd3272_mikey_recv;
k->send = cd3272_mikey_send;
}
static const TypeInfo cd3272_mikey_info = {
.name = TYPE_CD3272MIKEY,
.parent = TYPE_I2C_SLAVE,
.instance_init = cd3272_mikey_init,
.instance_size = sizeof(CD3272MikeyState),
.class_init = cd3272_mikey_class_init,
};
static void cd3272_mikey_register_types(void)
{
type_register_static(&cd3272_mikey_info);
}
type_init(cd3272_mikey_register_types)
+55
View File
@@ -0,0 +1,55 @@
#include "hw/arm/ipod_touch_chipid.h"
static uint64_t ipod_touch_chipid_read(void *opaque, hwaddr addr, unsigned size)
{
//fprintf(stderr, "%s: offset = 0x%08x\n", __func__, addr);
switch (addr) {
case CHIPID_UNKNOWN1:
return (1 << 5); // ind5 = production mode
case CHIPID_INFO:
return (0x8720 << 16) | (1 << 2); // ind16 = chipid, ind2 = security domain,
case CHIPID_UNKNOWN2:
return 0;
case CHIPID_UNKNOWN3:
return 0;
default:
hw_error("%s: reading from unknown chip ID register 0x%08x\n", __func__, addr);
}
return 0;
}
static const MemoryRegionOps ipod_touch_chipid_ops = {
.read = ipod_touch_chipid_read,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void ipod_touch_chipid_init(Object *obj)
{
IPodTouchChipIDState *s = IPOD_TOUCH_CHIPID(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
memory_region_init_io(&s->iomem, obj, &ipod_touch_chipid_ops, s, TYPE_IPOD_TOUCH_CHIPID, 0x14);
sysbus_init_mmio(sbd, &s->iomem);
}
static void ipod_touch_chipid_class_init(ObjectClass *klass, void *data)
{
}
static const TypeInfo ipod_touch_chipid_type_info = {
.name = TYPE_IPOD_TOUCH_CHIPID,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IPodTouchChipIDState),
.instance_init = ipod_touch_chipid_init,
.class_init = ipod_touch_chipid_class_init,
};
static void ipod_touch_chipid_register_types(void)
{
type_register_static(&ipod_touch_chipid_type_info);
}
type_init(ipod_touch_chipid_register_types)
+156
View File
@@ -0,0 +1,156 @@
#include "hw/arm/ipod_touch_clock.h"
static void s5l8900_clock_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
{
IPodTouchClockState *s = (struct IPodTouchClockState *) opaque;
switch (addr) {
case CLOCK_CONFIG0:
s->config0 = val;
break;
case CLOCK_CONFIG1:
s->config1 = val;
break;
case CLOCK_CONFIG2:
s->config2 = val;
break;
case CLOCK_CONFIG3:
s->config3 = val;
break;
case CLOCK_CONFIG4:
s->config4 = val;
break;
case CLOCK_CONFIG5:
s->config5 = val;
break;
case CLOCK_PLL0CON:
s->pll0con = val;
break;
case CLOCK_PLL1CON:
s->pll1con = val;
break;
case CLOCK_PLL2CON:
s->pll2con = val;
break;
case CLOCK_PLL3CON:
s->pll3con = val;
break;
case CLOCK_PLL0LCNT:
s->pll0lcnt = val;
break;
case CLOCK_PLL1LCNT:
s->pll1lcnt = val;
break;
case CLOCK_PLL2LCNT:
s->pll2lcnt = val;
break;
case CLOCK_PLL3LCNT:
s->pll3lcnt = val;
break;
case CLOCK_PLLLOCK:
hw_error("%s: Forbidden write to PLLLOCK register 0x%08x\n", __func__, addr);
break;
case CLOCK_PLLMODE:
s->pllmode = val;
break;
case CLOCK_PWRCON0:
s->pwrcon0 = val;
break;
case CLOCK_PWRCON1:
s->pwrcon1 = val;
break;
case CLOCK_PWRCON2:
s->pwrcon2 = val;
break;
case CLOCK_PWRCON3:
s->pwrcon3 = val;
break;
case CLOCK_PWRCON4:
s->pwrcon4 = val;
break;
default:
hw_error("%s: writing value 0x%08x to unknown clock register 0x%08x\n", __func__, val, addr);
}
}
static uint64_t s5l8900_clock_read(void *opaque, hwaddr addr, unsigned size)
{
IPodTouchClockState *s = (struct IPodTouchClockState *) opaque;
switch (addr) {
case CLOCK_CONFIG0:
return s->config0;
case CLOCK_CONFIG1:
return s->config1;
case CLOCK_CONFIG2:
return s->config2;
case CLOCK_CONFIG3:
return s->config3;
case CLOCK_CONFIG4:
return s->config4;
case CLOCK_CONFIG5:
return s->config5;
case CLOCK_PLL0CON:
return s->pll0con;
case CLOCK_PLL1CON:
return s->pll1con;
case CLOCK_PLL2CON:
return s->pll2con;
case CLOCK_PLL3CON:
return s->pll3con;
case CLOCK_PLLLOCK:
return (1 | 2 | 4 | 8); // all PLLs are locked
case CLOCK_PLLMODE:
return s->pllmode;
case CLOCK_PWRCON0:
return s->pwrcon0;
case CLOCK_PWRCON1:
return s->pwrcon1;
case CLOCK_PWRCON2:
return s->pwrcon2;
case CLOCK_PWRCON3:
return s->pwrcon3;
case CLOCK_PWRCON4:
return s->pwrcon4;
default:
hw_error("%s: reading from unknown clock register 0x%08x\n", __func__, addr);
}
return 0;
}
static const MemoryRegionOps clock_ops = {
.read = s5l8900_clock_read,
.write = s5l8900_clock_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void s5l8900_clock_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
DeviceState *dev = DEVICE(sbd);
IPodTouchClockState *s = IPOD_TOUCH_CLOCK(dev);
memory_region_init_io(&s->iomem, obj, &clock_ops, s, "clock", 0x80);
}
static void s5l8900_clock_class_init(ObjectClass *klass, void *data)
{
}
static const TypeInfo ipod_touch_clock_info = {
.name = TYPE_IPOD_TOUCH_CLOCK,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IPodTouchClockState),
.instance_init = s5l8900_clock_init,
.class_init = s5l8900_clock_class_init,
};
static void ipod_touch_machine_types(void)
{
type_register_static(&ipod_touch_clock_info);
}
type_init(ipod_touch_machine_types)
+54
View File
@@ -0,0 +1,54 @@
#include "hw/arm/ipod_touch_cs42l58.h"
static int cs42l58_event(I2CSlave *i2c, enum i2c_event event)
{
return 0;
}
static uint8_t cs42l58_recv(I2CSlave *i2c)
{
CS42L58State *s = CS42L58(i2c);
printf("Reading stereo codec register %d\n", s->cmd);
switch(s->cmd) {
default:
return 0;
}
}
static int cs42l58_send(I2CSlave *i2c, uint8_t data)
{
CS42L58State *s = CS42L58(i2c);
s->cmd = data;
printf("Writing stereo codec register %d\n", s->cmd);
return 0;
}
static void cs42l58_init(Object *obj)
{
}
static void cs42l58_class_init(ObjectClass *klass, void *data)
{
I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
k->event = cs42l58_event;
k->recv = cs42l58_recv;
k->send = cs42l58_send;
}
static const TypeInfo cs42l58_info = {
.name = TYPE_CS42L58,
.parent = TYPE_I2C_SLAVE,
.instance_init = cs42l58_init,
.instance_size = sizeof(CS42L58State),
.class_init = cs42l58_class_init,
};
static void cs42l58_register_types(void)
{
type_register_static(&cs42l58_info);
}
type_init(cs42l58_register_types)
+331
View File
@@ -0,0 +1,331 @@
#include "hw/arm/ipod_touch_fmss.h"
#include "qemu/log.h"
uint64_t temp_storage[512];
bool ipod_touch_nand_write = false;
static uint8_t find_bit_index(uint8_t num) {
int index = 0;
while (num > 1) {
num >>= 1;
index++;
}
return index;
}
static void write_chip_info(IPodTouchFMSSState *s)
{
uint32_t chipid[] = { 0xb614d5ad, 0xb614d5ad, 0xb614d5ad, 0xb614d5ad };
cpu_physical_memory_write(s->reg_cinfo_target_addr, &chipid, 0x10);
}
static void read_nand_pages(IPodTouchFMSSState *s)
{
// boot args
const char *boot_args = "kextlog=0xfff debug=0x8 cpus=1 rd=disk0s1 serial=1 pmu-debug=0x1 io=0xffff8fff debug-usb=0xffffffff amfi_allow_any_signature=1 -v zalloc_debug"; // if not const then overwritten
cpu_physical_memory_write(0x0ff2a584, boot_args, strlen(boot_args));
// patch iBoot - we want to inject the bluetooth MAC address which is located as sub-node of uart1 and not uart3 in the device tree...
// (0x0ff2206c in iBoot-385.22, 0x0ff22084 in iBoot-385.49: look the string up once)
static bool bt_path_patched = false;
if(!bt_path_patched) {
const uint32_t iboot_base = 0x0ff00000, iboot_size = 0x29000;
const char *orig = "arm-io/uart3/bluetooth";
char *iboot = g_malloc(iboot_size);
cpu_physical_memory_read(iboot_base, iboot, iboot_size);
char *hit = memmem(iboot, iboot_size, orig, strlen(orig) + 1);
if(hit) {
const char *chr = "arm-io/uart1/bluetooth";
cpu_physical_memory_write(iboot_base + (hit - iboot), chr, strlen(chr));
printf("FMSS: patched iBoot bluetooth node path at 0x%08lx\n", iboot_base + (hit - iboot));
}
bt_path_patched = true;
g_free(iboot);
}
int page_out_buf_ind = 0;
bool all_erased = s->reg_num_pages != 0;
s->read_pending = true;
//printf("Start CMD...\n");
for(int page_ind = 0; page_ind < s->reg_num_pages; page_ind++) {
uint32_t page_nr = 0;
uint32_t page_out_addr = 0;
uint32_t cs = 0;
cpu_physical_memory_read(s->reg_pages_in_addr + (page_ind * sizeof(uint32_t)), &page_nr, sizeof(uint32_t));
cpu_physical_memory_read(s->reg_cs_buf_addr + (page_ind * sizeof(uint32_t)), &cs, sizeof(uint32_t));
uint32_t og_cs = cs;
cs = find_bit_index(cs);
if(cs > 3) {
printf("CS %d invalid! (original CS: %d, reading page %d)\n", cs, og_cs, page_nr);
hw_error("CS %d invalid!", cs);
}
// prepare the page
char filename[200];
sprintf(filename, "%s/cs%d/%d.page", s->nand_path, cs, page_nr);
struct stat st = {0};
if (stat(filename, &st) == -1) {
// page storage does not exist - initialize an empty buffer
memset(s->page_buffer, 0xff, NAND_BYTES_PER_PAGE);
memset(s->page_spare_buffer, 0xff, NAND_BYTES_PER_SPARE);
}
else {
all_erased = false;
FILE *f = fopen(filename, "rb");
if (f == NULL) { hw_error("Unable to read file!"); }
fread(s->page_buffer, sizeof(char), NAND_BYTES_PER_PAGE, f);
fread(s->page_spare_buffer, sizeof(char), NAND_BYTES_PER_SPARE, f);
fclose(f);
}
// we write away the page in two parts, 2048 bytes first and then the other 2048 bytes.
int write_buf_size = NAND_BYTES_PER_PAGE / 2;
for(int i = 0; i < 2; i++) {
cpu_physical_memory_read(s->reg_pages_out_addr + (page_out_buf_ind * sizeof(uint32_t)), &page_out_addr, sizeof(uint32_t));
cpu_physical_memory_write(page_out_addr, s->page_buffer + i * write_buf_size, write_buf_size);
//printf("Will read page %d @ cs %d into address 0x%08x and spare into address 0x%08x\n", page_nr, cs, page_out_addr, s->reg_page_spare_out_addr);
page_out_buf_ind++;
}
// finally, write the spare
cpu_physical_memory_write(s->reg_page_spare_out_addr + page_ind * 0xc, s->page_spare_buffer, 12);
}
/* C30 bit 29 with no ECC correction bits makes the driver return
* kIOReturnNotFound (blank NAND), which FTL recovery relies on. */
s->reg_result = all_erased ? 0x20000000 : 0;
}
/* The program script consumes a NAND command/address pair from D10.
* D08 is its byte length; D18 is left over from the last read request.
* The low nibble selects a CE, bits 16..23 contain the NAND command.
*/
static void write_nand_pages(IPodTouchFMSSState *s)
{
if (s->shadow[0xd08 / 4] != 8) {
hw_error("FMSS: unsupported program command length");
}
/* A zero command terminates the list. D18 is not a program count. */
for (unsigned page = 0; page <= NAND_MAX_TRANSFER_PAGES; page++) {
uint32_t cmd[2], data_addr[2];
cpu_physical_memory_read(s->reg_cs_buf_addr + page * 8, cmd, sizeof(cmd[0]));
if (!cmd[0]) {
return;
}
if (page == NAND_MAX_TRANSFER_PAGES) {
hw_error("FMSS: unterminated program list");
}
cpu_physical_memory_read(s->reg_cs_buf_addr + page * 8 + 4,
&cmd[1], sizeof(cmd[1]));
uint32_t cs_mask = cmd[0] & 0xf;
if (!cs_mask || (cs_mask & (cs_mask - 1)) ||
((cmd[0] >> 16) & 0xfe) != 0x80 || cmd[1] >= NAND_PAGES_PER_CHIP) {
hw_error("FMSS: unsupported program %08x %08x", cmd[0], cmd[1]);
}
unsigned cs = find_bit_index(cs_mask);
cpu_physical_memory_read(s->reg_pages_out_addr + page * 8, data_addr, sizeof(data_addr));
for (int i = 0; i < 2; i++) {
cpu_physical_memory_read(data_addr[i],
s->page_buffer + i * NAND_BYTES_PER_PAGE / 2,
NAND_BYTES_PER_PAGE / 2);
}
memset(s->page_spare_buffer, 0xff, NAND_BYTES_PER_SPARE);
cpu_physical_memory_read(s->reg_page_spare_out_addr + page * 12, s->page_spare_buffer, 12);
g_autofree char *filename = g_strdup_printf("%s/cs%u/%u.page",
s->nand_path, cs, cmd[1]);
FILE *f = fopen(filename, "wb");
if (!f) {
hw_error("FMSS: cannot write %s", filename);
}
bool ok = fwrite(s->page_buffer, 1, NAND_BYTES_PER_PAGE, f) == NAND_BYTES_PER_PAGE;
ok &= fwrite(s->page_spare_buffer, 1, NAND_BYTES_PER_SPARE, f) == NAND_BYTES_PER_SPARE;
int close_result = fclose(f);
if (!ok || close_result) {
hw_error("FMSS: failed writing %s", filename);
}
qemu_log_mask(LOG_UNIMP, "fmss: PROGRAM cs%u page %u\n", cs, cmd[1]);
}
hw_error("FMSS: unterminated program list");
}
/* Identify the sequencer's erase operation by its NAND 60h/D0h pair,
* independently of where iBoot or the kernel allocated the script.
* The other sequencer operations retain the existing high-level emulation.
*/
static bool erase_nand_blocks(IPodTouchFMSSState *s)
{
uint32_t header[8];
cpu_physical_memory_read(s->shadow[0xc04 / 4], header, sizeof(header));
if (header[6] != 0x01000c4c || header[7] > 8192 || (header[7] & 7)) {
return false;
}
bool setup = false, confirm = false;
for (unsigned offset = 0; offset < header[7]; offset += 8) {
uint32_t op[2];
cpu_physical_memory_read(s->shadow[0xc04 / 4] + offset, op, sizeof(op));
if (op[0] == 0x01000008) {
setup |= op[1] == 0x60;
confirm |= op[1] == 0xd0;
}
}
if (!setup || !confirm) {
return false;
}
if (s->reg_num_pages != 1) {
hw_error("FMSS: unsupported erase count %u", s->reg_num_pages);
}
uint32_t cs, page;
cpu_physical_memory_read(s->reg_cs_buf_addr, &cs, sizeof(cs));
cpu_physical_memory_read(s->reg_pages_in_addr, &page, sizeof(page));
/* Erase uses a CE index, unlike read's mask and program's packed command. */
if (cs >= 4 || page >= NAND_PAGES_PER_CHIP || (page & (NAND_PAGES_PER_BLOCK - 1))) {
hw_error("FMSS: invalid erase cs%u page %u", cs, page);
}
for (unsigned i = 0; i < NAND_PAGES_PER_BLOCK; i++) {
g_autofree char *filename = g_strdup_printf("%s/cs%u/%u.page",
s->nand_path, cs, page + i);
if (unlink(filename) && errno != ENOENT) {
hw_error("FMSS: cannot erase %s", filename);
}
}
qemu_log_mask(LOG_UNIMP, "fmss: ERASE cs%u block %u\n", cs, page / NAND_PAGES_PER_BLOCK);
return true;
}
static uint64_t ipod_touch_fmss_read(void *opaque, hwaddr addr, unsigned size)
{
//fprintf(stderr, "%s: read from location 0x%08lx\n", __func__, addr);
IPodTouchFMSSState *s = (IPodTouchFMSSState *)opaque;
switch(addr)
{
case FMSS__CS_BUF_RST_OK:
return 0x1;
case FMSS__CS_IRQ:
return s->reg_cs_irq_bit;
case FMSS__CS_IRQMASK:
return 0x1;
case FMSS__FMCTRL1:
return (0x1 << 30);
case 0xD00:
return 42;
case 0x00000C30:
return s->reg_result;
default:
qemu_log_mask(LOG_UNIMP, "%s: read invalid location 0x%08" HWADDR_PRIx "\n", __func__, addr);
break;
}
return 0;
}
static void ipod_touch_fmss_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
{
IPodTouchFMSSState *s = (IPodTouchFMSSState *)opaque;
if(addr < 0x1000) s->shadow[addr / 4] = val;
if(qemu_loglevel_mask(LOG_UNIMP))
qemu_log("fmss: write 0x%03x = 0x%08x\n", (unsigned)addr, (unsigned)val);
switch(addr) {
case 0xC00:
if(val == 0x0000ffb5) { s->reg_cs_irq_bit = 1; } // TODO ugly and hard-coded
if(val == 0xfff5) {
if (!s->read_pending) {
s->reg_result = 0;
}
s->read_pending = false;
if (ipod_touch_nand_write) {
erase_nand_blocks(s);
}
s->reg_cs_irq_bit = 1; qemu_set_irq(s->irq, 1);
}
break;
case FMSS__CS_IRQ:
s->reg_cs_irq_bit = 0;
qemu_set_irq(s->irq, 0);
break;
case FMSS_CINFO_TARGET_ADDR:
// le même registre sert aussi au numéro de page dans les séquences d'écriture : pas une adresse RAM
s->reg_cinfo_target_addr = val;
if(val >= 0x08000000) write_chip_info(s);
break;
case FMSS_PAGES_IN_ADDR:
s->reg_pages_in_addr = val;
break;
case FMSS_CS_BUF_ADDR:
s->reg_cs_buf_addr = val;
break;
case FMSS_NUM_PAGES:
s->reg_num_pages = val;
break;
case FMSS_PAGE_SPARE_OUT_ADDR:
s->reg_page_spare_out_addr = val;
break;
case FMSS_PAGES_OUT_ADDR:
s->reg_pages_out_addr = val;
break;
case FMSS_CSGENRC:
s->reg_csgenrc = val;
break;
case 0xD38:
if(s->reg_csgenrc == 0xa01) { read_nand_pages(s); }
/* Programs use a packed command list, not the read page/CE arrays. */
else if(s->reg_csgenrc == 0xa02 && ipod_touch_nand_write) { write_nand_pages(s); }
else {
qemu_log_mask(LOG_UNIMP, "fmss: unhandled command csgenrc 0x%x go 0x%x pages %u in 0x%x cs 0x%x out 0x%x spare 0x%x\n",
s->reg_csgenrc, (unsigned)val, s->reg_num_pages, s->reg_pages_in_addr, s->reg_cs_buf_addr, s->reg_pages_out_addr, s->reg_page_spare_out_addr);
}
break;
default:
//printf("%s unknown write %lx to %lx\n", __func__, val, addr);
break;
}
}
static const MemoryRegionOps fmss_ops = {
.read = ipod_touch_fmss_read,
.write = ipod_touch_fmss_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void ipod_touch_fmss_realize(DeviceState *dev, Error **errp)
{
}
static void ipod_touch_fmss_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
DeviceState *dev = DEVICE(sbd);
IPodTouchFMSSState *s = IPOD_TOUCH_FMSS(dev);
memory_region_init_io(&s->iomem, obj, &fmss_ops, s, "fmss", 0xF00);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
s->page_buffer = (uint8_t *)malloc(NAND_BYTES_PER_PAGE);
s->page_spare_buffer = (uint8_t *)malloc(NAND_BYTES_PER_SPARE);
}
static void ipod_touch_fmss_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = ipod_touch_fmss_realize;
}
static const TypeInfo ipod_touch_fmss_info = {
.name = TYPE_IPOD_TOUCH_FMSS,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IPodTouchFMSSState),
.instance_init = ipod_touch_fmss_init,
.class_init = ipod_touch_fmss_class_init,
};
static void ipod_touch_machine_types(void)
{
type_register_static(&ipod_touch_fmss_info);
}
type_init(ipod_touch_machine_types)
+84
View File
@@ -0,0 +1,84 @@
#include "hw/arm/ipod_touch_gpio.h"
static void s5l8900_gpio_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
{
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, value, addr);
IPodTouchGPIOState *s = (struct IPodTouchGPIOState *) opaque;
switch(addr) {
default:
break;
}
}
static uint64_t s5l8900_gpio_read(void *opaque, hwaddr addr, unsigned size)
{
printf("%s: read from location 0x%08x\n", __func__, addr);
IPodTouchGPIOState *s = (struct IPodTouchGPIOState *) opaque;
switch(addr) {
case 0x4:
return 0;
case 0x24 ... 0x184:
return s->gpio_state[GPIOADDR2PAD(addr)];
default:
break;
}
return 0;
}
bool gpio_is_on(uint32_t *state, uint32_t gpio)
{
return (state[GPIO2PAD(gpio)] & (1 << GPIO2PIN(gpio)));
}
bool gpio_is_off(uint32_t *state, uint32_t gpio)
{
return !gpio_is_on(state, gpio);
}
void gpio_set_on(uint32_t *state, uint32_t gpio)
{
state[GPIO2PAD(gpio)] |= (1 << GPIO2PIN(gpio));
}
void gpio_set_off(uint32_t *state, uint32_t gpio)
{
state[GPIO2PAD(gpio)] &= ~(1 << GPIO2PIN(gpio));
}
static const MemoryRegionOps gpio_ops = {
.read = s5l8900_gpio_read,
.write = s5l8900_gpio_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void s5l8900_gpio_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
DeviceState *dev = DEVICE(sbd);
IPodTouchGPIOState *s = IPOD_TOUCH_GPIO(dev);
memory_region_init_io(&s->iomem, obj, &gpio_ops, s, "gpio", 0x1000);
}
static void s5l8900_gpio_class_init(ObjectClass *klass, void *data)
{
}
static const TypeInfo ipod_touch_gpio_info = {
.name = TYPE_IPOD_TOUCH_GPIO,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IPodTouchGPIOState),
.instance_init = s5l8900_gpio_init,
.class_init = s5l8900_gpio_class_init,
};
static void ipod_touch_machine_types(void)
{
type_register_static(&ipod_touch_gpio_info);
}
type_init(ipod_touch_machine_types)
+101
View File
@@ -0,0 +1,101 @@
#include "hw/arm/ipod_touch_isl29003dl.h"
static int isl29003dl_event(I2CSlave *i2c, enum i2c_event event)
{
ISL29003DLState *s = ISL29003DL(i2c);
printf("%s Got event %d\n", __func__, event);
if (event == I2C_START_SEND)
s->ready = 1;
return 0;
}
static uint8_t isl29003dl_recv(I2CSlave *i2c)
{
ISL29003DLState *s = ISL29003DL(i2c);
printf("Reading lightsensor register %d\n", s->cmd);
int res = 0;
switch(s->cmd) {
case LIGHTSENSOR_COMMAND:
res = s->command;
break;
case LIGHTSENSOR_CONTROL:
res = s->ctrl;
break;
case LIGHTSENSOR_LSB_SENSOR:
res = s->lsb_sensor;
break;
case LIGHTSENSOR_MSB_SENSOR:
res = s->msb_sensor;
break;
default:
res = 0;
printf("%s unknown register %d\n", __func__, s->cmd);
}
s->cmd += 1;
return res;
}
static int isl29003dl_send(I2CSlave *i2c, uint8_t data)
{
ISL29003DLState *s = ISL29003DL(i2c);
if (s->ready)
{
s->curreg = data;
s->ready = 0;
printf("%s Lightsensor ready\n", __func__);
}
else
{
switch(s->curreg++)
{
case LIGHTSENSOR_COMMAND:
s->command = data;
if ((s->command >> 7) == 1)
{
s->lsb_sensor = 0xE;
s->msb_sensor = 0xE << 4;
s->it_hi = 0xF;
s->it_lo = 0x0;
}
break;
default:
printf("%s unknown register %d\n", __func__, s->curreg-1);
}
}
printf("Writing lightsensor data %d\n", s->cmd);
return 0;
}
static void isl29003dl_init(Object *obj)
{
}
static void isl29003dl_class_init(ObjectClass *klass, void *data)
{
I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
k->event = isl29003dl_event;
k->recv = isl29003dl_recv;
k->send = isl29003dl_send;
}
static const TypeInfo isl29003dl_info = {
.name = TYPE_ISL29003DL,
.parent = TYPE_I2C_SLAVE,
.instance_init = isl29003dl_init,
.instance_size = sizeof(ISL29003DLState),
.class_init = isl29003dl_class_init,
};
static void isl29003dl_register_types(void)
{
type_register_static(&isl29003dl_info);
}
type_init(isl29003dl_register_types)
+230
View File
@@ -0,0 +1,230 @@
#include "hw/arm/ipod_touch_lcd.h"
#include "ui/pixel_ops.h"
#include "ui/console.h"
#include "hw/display/framebuffer.h"
int lcd_brightness = 255;
static uint64_t ipod_touch_lcd_read(void *opaque, hwaddr addr, unsigned size)
{
// printf("%s: read from location 0x%08x\n", __func__, addr);
IPodTouchLCDState *s = (IPodTouchLCDState *)opaque;
switch(addr)
{
case 0x0:
return 2;
case 0x4:
return s->lcd_con;
case 0xC:
return 0x1; //s->unknown1;
case 0x20:
return s->w1_display_depth_info;
case 0x24:
return s->w1_framebuffer_base;
case 0x28:
return s->w1_hspan;
case 0x30:
return s->w1_display_resolution_info;
case 0x1b10:
return 2;
case 0x1b14:
return 0x3;
default:
printf("%s: read invalid location 0x%08x.\n", __func__, addr);
break;
}
return 0;
}
static void ipod_touch_lcd_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
{
IPodTouchLCDState *s = (IPodTouchLCDState *)opaque;
// printf("%s: writing 0x%08x to 0x%08x\n", __func__, val, addr);
switch(addr) {
case 0x4:
s->lcd_con = val;
break;
case 0xC:
s->render = val;
qemu_irq_lower(s->irq);
// qemu_irq_raise(s->irq);
break;
case 0x20:
s->w1_display_depth_info = val;
break;
case 0x24:
s->w1_framebuffer_base = val;
break;
case 0x28:
s->w1_hspan = val;
break;
case 0x30:
s->w1_display_resolution_info = val;
break;
}
}
void lcd_changebrightness(int brightness)
{
lcd_brightness = brightness & 0xFF;
}
static void lcd_invalidate(void *opaque)
{
IPodTouchLCDState *s = opaque;
s->invalidate = 1;
}
static void draw_line32_32(void *opaque, uint8_t *d, const uint8_t *s, int width, int deststep)
{
// IPodTouchLCDState *lcd = (IPodTouchLCDState *) opaque;
uint8_t r, g, b;
do {
//v = lduw_le_p((void *) s);
//printf("V: %d\n", *s);
b = s[0];
g = s[1];
r = s[2];
// printf("R: %d, G: %d, B: %d, A: %d\n", r, g, b, lcd_brightness);
((uint32_t *) d)[0] = rgb_to_pixel32(round((float)r * ((float)lcd_brightness / 255.0f)), round((float)g * ((float)lcd_brightness / 255.0f)), round((float)b * ((float)lcd_brightness / 255.0f)));
s += 4;
d += 4;
} while (-- width != 0);
}
static void lcd_refresh(void *opaque)
{
// printf("%s: refreshing LCD screen\n", __func__);
IPodTouchLCDState *lcd = (IPodTouchLCDState *) opaque;
DisplaySurface *surface = qemu_console_surface(lcd->con);
drawfn draw_line;
int src_width, dest_width;
int height, first, last;
int width, linesize;
if (!lcd || !lcd->con || !surface_bits_per_pixel(surface))
return;
dest_width = 4;
draw_line = draw_line32_32;
/* Resolution */
first = last = 0;
width = 320;
height = 480;
lcd->invalidate = 1;
src_width = 4 * width;
linesize = surface_stride(surface);
if(lcd->invalidate) {
framebuffer_update_memory_section(&lcd->fbsection, lcd->sysmem, lcd->w1_framebuffer_base, height, 4 * width);
}
framebuffer_update_display(surface, &lcd->fbsection,
width, height,
src_width, /* Length of source line, in bytes. */
linesize, /* Bytes between adjacent horizontal output pixels. */
dest_width, /* Bytes between adjacent vertical output pixels. */
lcd->invalidate,
draw_line, NULL,
&first, &last);
if (first >= 0) {
dpy_gfx_update(lcd->con, 0, first, width, last - first + 1);
}
lcd->invalidate = 0;
}
static const MemoryRegionOps lcd_ops = {
.read = ipod_touch_lcd_read,
.write = ipod_touch_lcd_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static const GraphicHwOps gfx_ops = {
.invalidate = lcd_invalidate,
.gfx_update = lcd_refresh,
};
static void ipod_touch_lcd_mouse_event(void *opaque, int x, int y, int z, int buttons_state)
{
// printf("x %d y %d z %d state %d\n", x, y, z, buttons_state);
// convert x and y to fractional numbers
float fx = x / pow(2, 15);
float fy = 1 - y / pow(2, 15);
IPodTouchLCDState *lcd = (IPodTouchLCDState *) opaque;
lcd->mt->touch_x = fx;
lcd->mt->touch_y = fy;
if(buttons_state && !lcd->mt->touch_down) {
ipod_touch_multitouch_on_touch(lcd->mt);
}
else if(!buttons_state && lcd->mt->touch_down) {
ipod_touch_multitouch_on_release(lcd->mt);
}
}
static void refresh_timer_tick(void *opaque)
{
IPodTouchLCDState *s = (IPodTouchLCDState *)opaque;
if (s->render == 0x1)
qemu_irq_raise(s->irq);
else if (s->render == 0xFF)
qemu_irq_lower(s->irq);
timer_mod(s->refresh_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / 60);//LCD_REFRESH_RATE_FREQUENCY);
}
static void ipod_touch_lcd_realize(DeviceState *dev, Error **errp)
{
IPodTouchLCDState *s = IPOD_TOUCH_LCD(dev);
s->con = graphic_console_init(dev, 0, &gfx_ops, s);
qemu_console_resize(s->con, 320, 480);
// add mouse handler
qemu_add_mouse_event_handler(ipod_touch_lcd_mouse_event, s, 1, "iPod Touch Touchscreen");
// initialize the refresh timer
s->refresh_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, refresh_timer_tick, s);
timer_mod(s->refresh_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / LCD_REFRESH_RATE_FREQUENCY);
}
static void ipod_touch_lcd_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
DeviceState *dev = DEVICE(sbd);
IPodTouchLCDState *s = IPOD_TOUCH_LCD(dev);
memory_region_init_io(&s->iomem, obj, &lcd_ops, s, "lcd", 0x10000);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
}
static void ipod_touch_lcd_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = ipod_touch_lcd_realize;
}
static const TypeInfo ipod_touch_lcd_info = {
.name = TYPE_IPOD_TOUCH_LCD,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IPodTouchLCDState),
.instance_init = ipod_touch_lcd_init,
.class_init = ipod_touch_lcd_class_init,
};
static void ipod_touch_machine_types(void)
{
type_register_static(&ipod_touch_lcd_info);
}
type_init(ipod_touch_machine_types)
+68
View File
@@ -0,0 +1,68 @@
#include "hw/arm/ipod_touch_lis302dl.h"
static int lis302dl_event(I2CSlave *i2c, enum i2c_event event)
{
return 0;
}
static uint8_t lis302dl_recv(I2CSlave *i2c)
{
LIS302DLState *s = LIS302DL(i2c);
printf("Reading accelerometer register %d\n", s->cmd);
switch(s->cmd) {
case ACCEL_WHOAMI:
return ACCEL_WHOAMI_VALUE;
case ACCEL_OUT_X:
return s->ctrl_reg1 == 1 ? s->out_x : 0;
case ACCEL_OUT_Y:
return s->ctrl_reg1 << 1 == 1 ? s->out_y : 0;
case ACCEL_OUT_Z:
return s->ctrl_reg1 << 2 == 1 ? s->out_z : 0;
case ACCEL_CTRL_REG1:
return s->ctrl_reg1;
case ACCEL_CTRL_REG2:
return s->ctrl_reg2;
case ACCEL_CTRL_REG3:
return s->ctrl_reg3;
default:
printf("%s Unknown register %d\n", __func__, s->cmd);
return 0;
}
}
static int lis302dl_send(I2CSlave *i2c, uint8_t data)
{
LIS302DLState *s = LIS302DL(i2c);
s->cmd = data;
return 0;
}
static void lis302dl_init(Object *obj)
{
}
static void lis302dl_class_init(ObjectClass *klass, void *data)
{
I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
k->event = lis302dl_event;
k->recv = lis302dl_recv;
k->send = lis302dl_send;
}
static const TypeInfo lis302dl_info = {
.name = TYPE_LIS302DL,
.parent = TYPE_I2C_SLAVE,
.instance_init = lis302dl_init,
.instance_size = sizeof(LIS302DLState),
.class_init = lis302dl_class_init,
};
static void lis302dl_register_types(void)
{
type_register_static(&lis302dl_info);
}
type_init(lis302dl_register_types)
+171
View File
@@ -0,0 +1,171 @@
#include "hw/arm/ipod_touch_aes.h"
#include "hw/arm/ipod_touch_mbx.h"
static uint32_t reverse_byte_order(uint32_t value) {
return ((value & 0x000000FF) << 24) |
((value & 0x0000FF00) << 8) |
((value & 0x00FF0000) >> 8) |
((value & 0xFF000000) >> 24);
}
static uint64_t ipod_touch_mbx1_read(void *opaque, hwaddr addr, unsigned size)
{
IPodTouchMBXState *s = (IPodTouchMBXState *)opaque;
if (addr != 0x1020)
printf("%s: read from location 0x%08x\n", __func__, addr);
switch(addr)
{
case 0x12c:
return 0x40;
case 0xf00:
return (2 << 0x10) | (1 << 0x18); // seems to be some kind of identifier
case 0x1020:
return s->addr != 0x0 ? s->addr : 0x10000;
default:
break;
}
return 0;
}
static void ipod_touch_mbx1_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
{
IPodTouchMBXState *s = (IPodTouchMBXState *)opaque;
fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, val, addr);
switch(addr)
{
case 0x1020:
s->addr = val;
break;
}
}
static void patch_kernel(bool alreadypatched)
{
if (alreadypatched) return;
// the addresses below are those of the 5F138 (iPhone OS 2.1.1) kernel
if (!ipod_touch_fw_build || strcmp(ipod_touch_fw_build, "5F138") != 0) return;
alreadypatched = 1;
// patch the loading of the AppleBCM4325 driver
char *bcm4325_vars = "test";
// write the pointer to our custom subroutine
uint32_t *data = malloc(sizeof(uint32_t) * 200);
data[0] = 0xC0460000;
cpu_physical_memory_write(0x8324aa8, (uint8_t *)data, sizeof(uint32_t) * 1);
// create the call to the subroutine
data = malloc(sizeof(uint32_t) * 200);
data[0] = reverse_byte_order(0x0640A0E1); // mov r4, r6
data[1] = reverse_byte_order(0x9C309FE5); // ldr r3, [pc, #0x9c]
data[2] = reverse_byte_order(0x33FF2FE1); // blx r3
data[3] = reverse_byte_order(0x00F020E3); // NOP
data[4] = reverse_byte_order(0x00F020E3); // NOP
data[5] = reverse_byte_order(0x00F020E3); // NOP
cpu_physical_memory_write(0x8324a00, (uint8_t *)data, sizeof(uint32_t) * 6);
// fill in the driver load subroutine
data = malloc(sizeof(uint32_t) * 200);
data[0] = reverse_byte_order(0xFE402DE9); // push on stack
// TODO I should clean this up
for(int i = 1; i < 21; i++) { data[i] = reverse_byte_order(0x00F020E3); } // NOP
// call the IONetworkController metaclass initialization
data[21] = reverse_byte_order(0x0100B0E3); // movs r0, #0x1
data[22] = reverse_byte_order(0xB8109FE5); // ldr r1, [pc, #0xb8]
data[23] = reverse_byte_order(0xB8209FE5); // ldr r2, [pc, #0xb8]
data[24] = reverse_byte_order(0x32FF2FE1); // blx r2
// load the "com.apple.driver.AppleBCM4325" kext
data[25] = reverse_byte_order(0xB4009FE5); // ldr r0, [pc, #0xb4]
data[26] = reverse_byte_order(0x0110B0E3); // movs r1, #0x1
data[27] = reverse_byte_order(0xB0209FE5); // ldr r2, [pc, #0xd8]
data[28] = reverse_byte_order(0x32FF2FE1); // blx r2
data[29] = reverse_byte_order(0xFE80BDE8); // pop from stack
cpu_physical_memory_write(0x8460000, (uint8_t *)data, sizeof(uint32_t) * 50);
// write the data section of the driver load subroutine (0x100 items from the start of the subroutine)
data = malloc(sizeof(uint32_t) * 200);
data[0] = 0xc0460200; // the address of the BCM4325Vars string
data[1] = 0xc013c373; // the address of OSData::withBytes
data[2] = 0xc013cc3d; // the address of OSDictionary::withCapacity
data[3] = 0xc03467bc; // the "BCM4325Vars" string
data[4] = 0xc013ad8d; // the address of OSObject::operator.new
data[5] = 0xc032c294; // the object initialization method of AppleBCM4325
data[6] = 0xffff; // the 2nd parameter for the call to the IONetworkController metaclass initialization
data[7] = 0xc02f94f9; // the initialization method of the IONetworkController metaclass
data[8] = 0xc038a320; // the "com.apple.driver.AppleBCM4325" string
data[9] = 0xc015de01; // the kmod_load_request method
cpu_physical_memory_write(0x8460100, (uint8_t *)data, sizeof(uint32_t) * 10);
}
static uint64_t ipod_touch_mbx2_read(void *opaque, hwaddr addr, unsigned size)
{
IPodTouchMBXState *s = (IPodTouchMBXState *)opaque;
printf("%s: read from location 0x%08x\n", __func__, addr);
switch(addr)
{
case 0xC:
patch_kernel(s->alreadypatched);
break;
case 0x4:
return 0xFF;
break;
default:
break;
}
return 0;
}
static void ipod_touch_mbx2_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
{
IPodTouchMBXState *s = (IPodTouchMBXState *)opaque;
fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, val, addr);
}
static const MemoryRegionOps ipod_touch_mbx1_ops = {
.read = ipod_touch_mbx1_read,
.write = ipod_touch_mbx1_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static const MemoryRegionOps ipod_touch_mbx2_ops = {
.read = ipod_touch_mbx2_read,
.write = ipod_touch_mbx2_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void ipod_touch_mbx_init(Object *obj)
{
IPodTouchMBXState *s = IPOD_TOUCH_MBX(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
memory_region_init_io(&s->iomem1, obj, &ipod_touch_mbx1_ops, s, TYPE_IPOD_TOUCH_MBX, 0x1000000);
sysbus_init_mmio(sbd, &s->iomem1);
memory_region_init_io(&s->iomem2, obj, &ipod_touch_mbx2_ops, s, TYPE_IPOD_TOUCH_MBX, 0x1000);
sysbus_init_mmio(sbd, &s->iomem2);
}
static void ipod_touch_mbx_class_init(ObjectClass *klass, void *data)
{
}
static const TypeInfo ipod_touch_mbx_type_info = {
.name = TYPE_IPOD_TOUCH_MBX,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IPodTouchMBXState),
.instance_init = ipod_touch_mbx_init,
.class_init = ipod_touch_mbx_class_init,
};
static void ipod_touch_mbx_register_types(void)
{
type_register_static(&ipod_touch_mbx_type_info);
}
type_init(ipod_touch_mbx_register_types)
+100
View File
@@ -0,0 +1,100 @@
#include "hw/arm/ipod_touch_mipi_dsi.h"
static uint64_t ipod_touch_mipi_dsi_read(void *opaque, hwaddr addr, unsigned size)
{
if (addr != 0x00000)
fprintf(stderr, "%s: read from location 0x%08lx\n", __func__, addr);
IPodTouchMIPIDSIState *s = (IPodTouchMIPIDSIState *)opaque;
switch(addr)
{
case 0x0:
/* The panel driver waits for this bit to clear when powering down. */
return 0x103 | ((s->clkctrl_reg & DSIM_CLKCTRL_TX_REQUEST_HSCLK) ?
rDSIM_STATUS_TxReadyHsClk : 0);
case REG_CLKCTRL:
return s->clkctrl_reg;
case REG_INTSRC:
return rDSIM_INTSRC_RxDatDone;
case REG_RXFIFO:
if(!s->return_panel_id) {
s->return_panel_id = true;
// TODO this should be rewritten as a proper queue!
return DSIM_RSP_LONG_READ | (3 << 8); // the latter part indicates the length of the response (the panel ID)
} else {
s->return_panel_id = false;
return 0x00a1d13c;
}
case REG_FIFOCTRL:
return rDSIM_FIFOCTRL_EmptyHSfr;
default:
printf("%s: read invalid location 0x%08lx.\n", __func__, addr);
break;
}
return 0;
}
static void ipod_touch_mipi_dsi_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
{
IPodTouchMIPIDSIState *s = (IPodTouchMIPIDSIState *)opaque;
fprintf(stderr, "%s: writing 0x%08lx to 0x%08lx\n", __func__, val, addr);
switch(addr)
{
case REG_PKTHDR:
s->pkthdr_reg = val;
break;
case REG_CLKCTRL:
s->clkctrl_reg = val;
break;
default:
break;
}
}
static const MemoryRegionOps mipi_dsi_ops = {
.read = ipod_touch_mipi_dsi_read,
.write = ipod_touch_mipi_dsi_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void ipod_touch_mipi_dsi_realize(DeviceState *dev, Error **errp)
{
}
static void ipod_touch_mipi_dsi_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
DeviceState *dev = DEVICE(sbd);
IPodTouchMIPIDSIState *s = IPOD_TOUCH_MIPI_DSI(dev);
memory_region_init_io(&s->iomem, obj, &mipi_dsi_ops, s, "mipi_dsi", 0x10000);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
s->return_panel_id = 0;
}
static void ipod_touch_mipi_dsi_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = ipod_touch_mipi_dsi_realize;
}
static const TypeInfo ipod_touch_mipi_dsi_info = {
.name = TYPE_IPOD_TOUCH_MIPI_DSI,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IPodTouchMIPIDSIState),
.instance_init = ipod_touch_mipi_dsi_init,
.class_init = ipod_touch_mipi_dsi_class_init,
};
static void ipod_touch_machine_types(void)
{
type_register_static(&ipod_touch_mipi_dsi_info);
}
type_init(ipod_touch_machine_types)
+489
View File
@@ -0,0 +1,489 @@
#include "hw/arm/ipod_touch_multitouch.h"
#include "qemu/log.h"
#include "qemu/bswap.h"
static void prepare_interface_version_response(IPodTouchMultitouchState *s) {
memset(s->out_buffer + 1, 0, 15);
// set the interface version
s->out_buffer[2] = MT_INTERFACE_VERSION;
// set the max packet size
s->out_buffer[3] = (MT_MAX_PACKET_SIZE & 0xFF);
s->out_buffer[4] = (MT_MAX_PACKET_SIZE >> 8) & 0xFF;
// compute and set the checksum
uint32_t checksum = 0;
for(int i = 0; i < 14; i++) {
checksum += s->out_buffer[i];
}
s->out_buffer[14] = (checksum & 0xFF);
s->out_buffer[15] = (checksum >> 8) & 0xFF;
}
static void prepare_cmd_status_response(IPodTouchMultitouchState *s) {
memset(s->out_buffer + 1, 0, 15);
// TODO we should probably set some CMD status here
// compute and set the checksum
uint32_t checksum = 0;
for(int i = 0; i < 14; i++) {
checksum += s->out_buffer[i];
}
s->out_buffer[14] = (checksum & 0xFF);
s->out_buffer[15] = (checksum >> 8) & 0xFF;
}
static void prepare_report_info_response(IPodTouchMultitouchState *s, uint8_t report_id) {
memset(s->out_buffer + 1, 0, 15);
// set the error
s->out_buffer[2] = 0;
// set the report length
uint32_t report_length = 0;
if(report_id == MT_REPORT_UNKNOWN1) {
report_length = MT_REPORT_UNKNOWN1_SIZE;
}
else if(report_id == MT_REPORT_FAMILY_ID) {
report_length = MT_REPORT_FAMILY_ID_SIZE;
}
else if(report_id == MT_REPORT_SENSOR_INFO) {
report_length = MT_REPORT_SENSOR_INFO_SIZE;
}
else if(report_id == MT_REPORT_SENSOR_REGION_DESC) {
report_length = MT_REPORT_SENSOR_REGION_DESC_SIZE;
}
else if(report_id == MT_REPORT_SENSOR_REGION_PARAM) {
report_length = MT_REPORT_SENSOR_REGION_PARAM_SIZE;
}
else if(report_id == MT_REPORT_SENSOR_DIMENSIONS) {
report_length = MT_REPORT_SENSOR_DIMENSIONS_SIZE;
}
else {
qemu_log_mask(LOG_GUEST_ERROR, "%s: rapport inconnu 0x%02x\n",
__func__, report_id);
s->out_buffer[2] = 1;
}
s->out_buffer[3] = (report_length & 0xFF);
s->out_buffer[4] = (report_length >> 8) & 0xFF;
// compute and set the checksum
uint32_t checksum = 0;
for(int i = 0; i < 14; i++) {
checksum += s->out_buffer[i];
}
s->out_buffer[14] = (checksum & 0xFF);
s->out_buffer[15] = (checksum >> 8) & 0xFF;
}
static void prepare_short_control_response(IPodTouchMultitouchState *s, uint8_t report_id) {
memset(s->out_buffer + 1, 0, 15);
if(report_id == MT_REPORT_FAMILY_ID) {
s->out_buffer[3] = MT_FAMILY_ID;
}
else if(report_id == MT_REPORT_SENSOR_INFO) {
s->out_buffer[3] = MT_ENDIANNESS;
s->out_buffer[4] = MT_SENSOR_ROWS;
s->out_buffer[5] = MT_SENSOR_COLUMNS;
s->out_buffer[6] = (MT_BCD_VERSION & 0xFF);
s->out_buffer[7] = (MT_BCD_VERSION >> 8) & 0xFF;
}
else if(report_id == MT_REPORT_SENSOR_REGION_DESC) {
s->out_buffer[3] = MT_SENSOR_REGION_DESC;
}
else if(report_id == MT_REPORT_SENSOR_REGION_PARAM) {
s->out_buffer[3] = MT_SENSOR_REGION_PARAM;
}
else if(report_id == MT_REPORT_SENSOR_DIMENSIONS) {
stl_le_p(&s->out_buffer[3], MT_SENSOR_SURFACE_WIDTH);
stl_le_p(&s->out_buffer[7], MT_SENSOR_SURFACE_HEIGHT);
}
else {
qemu_log_mask(LOG_GUEST_ERROR, "%s: rapport inconnu 0x%02x\n",
__func__, report_id);
s->out_buffer[2] = 1;
}
// compute and set the checksum
uint32_t checksum = 0;
for(int i = 0; i < 14; i++) {
checksum += s->out_buffer[i];
}
s->out_buffer[14] = (checksum & 0xFF);
s->out_buffer[15] = (checksum >> 8) & 0xFF;
}
static MTFrame *get_frame(IPodTouchMultitouchState *s, uint8_t event,
float x, float y, uint16_t radius1, uint16_t radius2,
uint16_t radius3, uint16_t contactDensity);
static void finish_transfer(IPodTouchMultitouchState *s)
{
g_clear_pointer(&s->in_buffer, g_free);
g_clear_pointer(&s->out_buffer, g_free);
s->cur_cmd = 0;
s->buf_size = s->buf_ind = s->in_buffer_ind = 0;
s->in_buffer_capacity = 0;
}
static uint32_t ipod_touch_multitouch_transfer(SSIPeripheral *dev, uint32_t value)
{
IPodTouchMultitouchState *s = IPOD_TOUCH_MULTITOUCH(dev);
//printf("<MULTITOUCH> Got value: 0x%02x\n", value);
if(s->cur_cmd == 0) {
//printf("Starting command 0x%02x\n", value);
// we're currently not in a command - start a new command
s->cur_cmd = value;
s->out_buffer = g_malloc0(0x100);
s->out_buffer[0] = value; // the response header
s->buf_ind = 0;
s->in_buffer = g_malloc0(0x100);
s->in_buffer_capacity = 0x100;
s->in_buffer_ind = 0;
if(value == 0x18) { // filler packet??
s->buf_size = 2;
s->out_buffer[1] = 0xE1;
}
else if(value == 0x1A) { // HBPP ACK
s->buf_size = 2;
if(s->hbpp_atn_ack_response[0] == 0 && s->hbpp_atn_ack_response[1] == 0) {
// return the default ACK response
s->out_buffer[0] = 0x4B;
s->out_buffer[1] = 0xC1;
}
else {
s->out_buffer[0] = s->hbpp_atn_ack_response[0];
s->out_buffer[1] = s->hbpp_atn_ack_response[1];
}
}
else if(value == 0x1C) { // read register
s->buf_size = 8;
memset(s->out_buffer, 0, 8); // just return zeros
}
else if(value == 0x1D) { // execute
s->buf_size = 12;
memset(s->out_buffer, 0, 12); // just return zeros
}
else if(value == 0x1F) { // calibration
s->buf_size = 2;
s->out_buffer[1] = 0x0;
}
else if(value == 0x1E) { // write register
s->buf_size = 16;
memset(s->out_buffer, 0, 16); // just return zeros
}
else if(value == 0x1F) { // calibration
s->buf_size = 2;
s->out_buffer[1] = 0x0;
}
else if(value == MT_CMD_HBPP_DATA_PACKET) {
s->buf_size = 20; // should be enough initially, until we get the packet length
memset(s->out_buffer + 1, 0, 20 - 1); // just return zeros
}
else if(value == 0x47) { // unknown command, probably used to clear the interrupt
s->buf_size = 2;
}
else if(value == MT_CMD_GET_CMD_STATUS) {
s->buf_size = 16;
prepare_cmd_status_response(s);
}
else if(value == MT_CMD_GET_INTERFACE_VERSION) {
s->buf_size = 16;
prepare_interface_version_response(s);
}
else if(value == MT_CMD_GET_REPORT_INFO) {
s->buf_size = 16;
}
else if(value == MT_CMD_SHORT_CONTROL_WRITE) {
s->buf_size = 16;
}
else if(value == MT_CMD_SHORT_CONTROL_READ) {
s->buf_size = 16;
}
else if(value == MT_CMD_FRAME_READ || value == MT_CMD_FRAME_READ_ALT) {
s->buf_size = sizeof(MTFrameLengthPacket);
g_free(s->out_buffer);
/* La trame doit rester stable entre sa longueur et ses données,
même si un événement hôte arrive pendant les lectures SPI. */
s->out_buffer = g_memdup2(s->read_frame ? s->read_frame : s->next_frame,
sizeof(MTFrame));
}
else {
qemu_log_mask(LOG_GUEST_ERROR, "%s: commande inconnue 0x%02x\n",
__func__, value);
finish_transfer(s);
return 0xff;
}
}
if (s->buf_ind >= s->buf_size || s->in_buffer_ind >= s->in_buffer_capacity) {
finish_transfer(s);
return 0xff;
}
s->in_buffer[s->in_buffer_ind] = value;
s->in_buffer_ind++;
if ((s->cur_cmd == MT_CMD_FRAME_READ || s->cur_cmd == MT_CMD_FRAME_READ_ALT) &&
s->in_buffer_ind == 3) {
/* Le troisième octet distingue longueur (0) et données (1).
Chaque requête est une transaction complète : une lecture de
longueur abandonnée pendant la veille ne décale pas la suivante. */
if (value == 1) {
MTFrame *frame = (MTFrame *)s->out_buffer;
/* Le deuxième octet est déjà émis avant de connaître le sélecteur.
Il est ignoré dans les données, mais participe au checksum. */
uint8_t second = s->out_buffer[1];
frame->frame_packet.unused1 = second;
frame->frame_packet.checksum_pad = -(frame->frame_packet.cmd +
second + frame->frame_packet.length1 + frame->frame_packet.length2);
memmove(s->out_buffer, &frame->frame_packet,
sizeof(MTFrame) - sizeof(MTFrameLengthPacket));
s->buf_size = sizeof(MTFrame) - sizeof(MTFrameLengthPacket);
g_clear_pointer(&s->read_frame, g_free);
} else if (value == 0) {
g_free(s->read_frame);
s->read_frame = g_memdup2(s->next_frame, sizeof(MTFrame));
memcpy(s->out_buffer, s->read_frame, sizeof(MTFrame));
} else {
finish_transfer(s);
return 0xff;
}
}
else if(s->cur_cmd == MT_CMD_HBPP_DATA_PACKET && s->in_buffer_ind == 10) {
// verify the header checksum
uint32_t checksum = 0;
for(int i = 2; i < 8; i++) {
checksum += s->in_buffer[i];
}
if(checksum != (s->in_buffer[8] << 8 | s->in_buffer[9])) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: somme de contrôle HBPP invalide\n",
__func__);
finish_transfer(s);
return 0xff;
}
uint32_t data_len = ((s->in_buffer[2] << 10) | (s->in_buffer[3] << 2)) + 5;
if (data_len < s->in_buffer_ind) {
finish_transfer(s);
return 0xff;
}
/* Conserver l'en-tête reçu et dimensionner aussi les petites trames. */
s->in_buffer_capacity = MAX(data_len + 10, 0x100);
s->in_buffer = g_realloc(s->in_buffer, s->in_buffer_capacity);
g_free(s->out_buffer);
s->out_buffer = g_malloc0(data_len);
s->buf_size = data_len;
s->buf_ind = 0;
}
else if(s->cur_cmd == MT_CMD_GET_REPORT_INFO && s->in_buffer_ind == 2) {
prepare_report_info_response(s, s->in_buffer[1]);
}
else if(s->cur_cmd == MT_CMD_SHORT_CONTROL_WRITE && s->in_buffer_ind == 16) {
// TODO we should persist the report here!
}
else if(s->cur_cmd == MT_CMD_SHORT_CONTROL_READ && s->in_buffer_ind == 2) {
prepare_short_control_response(s, s->in_buffer[1]);
}
// TODO process register writes!
uint8_t ret_val = s->out_buffer[s->buf_ind];
s->buf_ind++;
//printf("<MULTITOUCH> Got value: 0x%02x, returning 0x%02x (index: %d, buffer length: %d)\n", value, ret_val, s->buf_ind, s->buf_size);
if(s->buf_ind == s->buf_size) {
//printf("Finished command 0x%02x\n", s->cur_cmd);
if(s->cur_cmd == 0x1E) {
// make sure we return a success status on the next HBPP ACK
s->hbpp_atn_ack_response[0] = 0x4A;
s->hbpp_atn_ack_response[1] = 0xD1;
}
// we're done with the command
finish_transfer(s);
}
return ret_val;
}
static MTFrame *get_frame(IPodTouchMultitouchState *s, uint8_t event, float x, float y, uint16_t radius1, uint16_t radius2, uint16_t radius3, uint16_t contactDensity) {
MTFrame *frame = g_new0(MTFrame, 1);
uint16_t data_len = sizeof(MTFrameHeader) + sizeof(FingerData) + 2;
/// create the frame length packet
frame->frame_length.cmd = MT_CMD_FRAME_READ;
frame->frame_length.length1 = (data_len & 0xFF);
frame->frame_length.length2 = (data_len >> 8) & 0xFF;
uint16_t checksum = 0;
for(int i = 0; i < 14; i++) {
checksum += ((uint8_t *) &frame->frame_length)[i];
}
frame->frame_length.checksum1 = (checksum & 0xFF);
frame->frame_length.checksum2 = (checksum >> 8) & 0xFF;
// create the frame packet
frame->frame_packet.cmd = MT_CMD_FRAME_READ;
frame->frame_packet.length1 = (data_len & 0xFF);
frame->frame_packet.length2 = (data_len >> 8) & 0xFF;
checksum = 0;
for(int i = 0; i < 4; i++) {
checksum += ((uint8_t *) &frame->frame_length)[i];
}
// the first five bytes have to sum up to 0.
frame->frame_packet.checksum_pad = 0xFF - (checksum & 0xFF) + 1;
frame->frame_packet.header.type = MT_FRAME_TYPE_PATH;
frame->frame_packet.header.frameNum = s->frame_counter;
frame->frame_packet.header.headerLen = sizeof(MTFrameHeader);
uint64_t elapsed_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / 1000000;
frame->frame_packet.header.timestamp = elapsed_ns;
frame->frame_packet.header.numFingers = 1;
frame->frame_packet.header.fingerDataLen = sizeof(FingerData);
// create the finger data
frame->finger_data.id = 1;
frame->finger_data.event = event;
frame->finger_data.unk_2 = 2;
frame->finger_data.unk_3 = 1;
// compute the velocity
int diff_x = (int)((x - s->prev_touch_x) * MT_INTERNAL_SENSOR_SURFACE_WIDTH);
int diff_y = (int)((y - s->prev_touch_y) * MT_INTERNAL_SENSOR_SURFACE_HEIGHT);
uint64_t dt = MAX(elapsed_ns - s->last_frame_timestamp, 1);
frame->finger_data.velX = CLAMP((int64_t)diff_x * 1000 / (int64_t)dt,
INT16_MIN, INT16_MAX);
frame->finger_data.velY = CLAMP((int64_t)diff_y * 1000 / (int64_t)dt,
INT16_MIN, INT16_MAX);
s->prev_touch_x = x;
s->prev_touch_y = y;
frame->finger_data.x = (int)(x * MT_INTERNAL_SENSOR_SURFACE_WIDTH) + MT_INTERNAL_SENSOR_ORIGIN_X;
frame->finger_data.y = (int)(y * MT_INTERNAL_SENSOR_SURFACE_HEIGHT) + MT_INTERNAL_SENSOR_ORIGIN_Y;
frame->finger_data.radius1 = radius1;
frame->finger_data.radius2 = radius2;
frame->finger_data.radius3 = radius3;
frame->finger_data.angle = 19317;
frame->finger_data.contactDensity = contactDensity; // seems to be a medium press
// compute the checksum over the frame data.
checksum = 0;
for(int i = 0; i < data_len - 2; i++) {
checksum += ((uint8_t *) &frame->frame_packet.header)[i];
}
frame->checksum1 = (checksum & 0xFF);
frame->checksum2 = (checksum >> 8) & 0xFF;
s->last_frame_timestamp = elapsed_ns;
s->frame_counter += 1;
return frame;
}
static void ipod_touch_multitouch_inform_frame_ready(IPodTouchMultitouchState *s) {
s->sysic->gpio_int_status[3] |= (1 << 13); // the multitouch interrupt bit is in group 3 (32 interrupts per group), and the 13th of the 3th group
qemu_irq_raise(s->sysic->gpio_irqs[3]);
}
void ipod_touch_multitouch_on_touch(IPodTouchMultitouchState *s) {
s->touch_down = true;
g_free(s->next_frame);
s->next_frame = get_frame(s, MT_EVENT_TOUCH_START, s->touch_x, s->touch_y, 100, 660, 580, 150);
ipod_touch_multitouch_inform_frame_ready(s);
timer_mod(s->touch_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / 10);
}
void ipod_touch_multitouch_on_release(IPodTouchMultitouchState *s) {
g_free(s->next_frame);
s->next_frame = get_frame(s, MT_EVENT_TOUCH_ENDED, s->touch_x, s->touch_y, 0, 0, 0, 0);
s->touch_down = false;
ipod_touch_multitouch_inform_frame_ready(s);
timer_del(s->touch_timer);
timer_mod(s->touch_end_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / 10);
}
static void touch_timer_tick(void *opaque)
{
IPodTouchMultitouchState *s = (IPodTouchMultitouchState *)opaque;
g_free(s->next_frame);
s->next_frame = get_frame(s, MT_EVENT_TOUCH_MOVED, s->touch_x, s->touch_y, 100, 660, 580, 150);
ipod_touch_multitouch_inform_frame_ready(s);
if(s->touch_down) {
// reschedule the timer
timer_mod(s->touch_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / 10);
}
}
static void touch_end_timer_tick(void *opaque)
{
IPodTouchMultitouchState *s = (IPodTouchMultitouchState *)opaque;
g_free(s->next_frame);
s->next_frame = get_frame(s, MT_EVENT_TOUCH_FULL_END, s->touch_x, s->touch_y, 0, 0, 0, 0);
s->touch_down = false;
ipod_touch_multitouch_inform_frame_ready(s);
}
static void multitouch_cs(void *opaque, int pin, int level)
{
IPodTouchMultitouchState *s = opaque;
if (level) finish_transfer(s);
}
static void ipod_touch_multitouch_realize(SSIPeripheral *d, Error **errp)
{
IPodTouchMultitouchState *s = IPOD_TOUCH_MULTITOUCH(d);
qdev_init_gpio_in_named(DEVICE(d), multitouch_cs, SSI_GPIO_CS, 1);
memset(s->hbpp_atn_ack_response, 0, 2);
s->touch_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, touch_timer_tick, s);
s->touch_end_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, touch_end_timer_tick, s);
s->prev_touch_x = 0;
s->prev_touch_y = 0;
s->last_frame_timestamp = 0;
/* Une lecture sans toucher doit renvoyer une trame de fin valide. */
s->next_frame = get_frame(s, MT_EVENT_TOUCH_FULL_END, 0, 0, 0, 0, 0, 0);
}
static void ipod_touch_multitouch_class_init(ObjectClass *klass, void *data)
{
SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass);
k->realize = ipod_touch_multitouch_realize;
k->transfer = ipod_touch_multitouch_transfer;
}
static const TypeInfo ipod_touch_multitouch_type_info = {
.name = TYPE_IPOD_TOUCH_MULTITOUCH,
.parent = TYPE_SSI_PERIPHERAL,
.instance_size = sizeof(IPodTouchMultitouchState),
.class_init = ipod_touch_multitouch_class_init,
};
static void ipod_touch_multitouch_register_types(void)
{
type_register_static(&ipod_touch_multitouch_type_info);
}
type_init(ipod_touch_multitouch_register_types)
+138
View File
@@ -0,0 +1,138 @@
#include "hw/arm/ipod_touch_nor_spi.h"
static void initialize_nor(IPodTouchNORSPIState *s)
{
gsize fsize;
if (g_file_get_contents(s->nor_path, (char **)&s->nor_data, &fsize, NULL)) {
s->nor_initialized = true;
}
}
static uint32_t ipod_touch_nor_spi_transfer(SSIPeripheral *dev, uint32_t value)
{
IPodTouchNORSPIState *s = IPOD_TOUCH_NOR_SPI(dev);
if(s->cur_cmd == NOR_READ_DATA_CMD && s->in_buf_cur_ind == s->in_buf_size && value != 0xFF) {
// if we are currently reading from the NOR data and we receive a value that's not the sentinel, reset the current command.
s->cur_cmd = 0;
}
if(s->cur_cmd == 0) {
// this is a new command -> set it
s->cur_cmd = value;
s->out_buf = malloc(0x1000);
s->in_buf = malloc(0x1000);
s->in_buf[0] = value;
s->in_buf_size = 0;
s->in_buf_cur_ind = 1;
s->out_buf_cur_ind = 0;
if(value == NOR_WRITE_TO_STATUS_REG) {
s->in_buf_size = 1;
s->out_buf_size = 1;
s->out_buf[0] = 0;
}
else if(value == NOR_GET_STATUS_CMD) {
s->in_buf_size = 1;
s->out_buf_size = 1;
s->out_buf[0] = 0;
}
else if(value == NOR_WRITE_DATA_CMD) {
s->in_buf_size = 4;
s->out_buf_size = 256;
for(int i = 0; i < 256; i++) { s->out_buf[i] = 0; }; // TODO we ignore this command for now
}
else if(value == NOR_READ_DATA_CMD) {
s->in_buf_size = 4;
s->out_buf_size = 4096;
}
else if(value == NOR_ERASE_BLOCK) {
s->in_buf_size = 1;
s->out_buf_size = 3;
for(int i = 0; i < 3; i++) { s->out_buf[i] = 0x0; } // TODO we ignore this command for now
}
else if(value == NOR_ENABLE_WRITE) {
s->write_enabled = 1;
s->cur_cmd = 0;
}
else if(value == NOR_DISABLE_WRITE) {
s->write_enabled = 0;
s->cur_cmd = 0;
}
else if(value == NOR_GET_JEDECID) {
s->in_buf_size = 1;
s->out_buf_size = 3;
s->out_buf[0] = 0x1F; // vendor: atmel, device: 0x02 -> AT25DF081A
s->out_buf[1] = 0x45;
s->out_buf[2] = 0x02;
}
else {
printf("%s Unknown command 0x%02x!\n", __func__, value);
// hw_error("Unknown command 0x%02x!", value);
}
return 0x0;
}
else if(s->cur_cmd != 0 && s->in_buf_cur_ind < s->in_buf_size) {
// we're reading the command
s->in_buf[s->in_buf_cur_ind] = value;
s->in_buf_cur_ind++;
if(s->cur_cmd == NOR_GET_STATUS_CMD && s->in_buf_cur_ind == s->in_buf_size) {
s->out_buf[0] = 0x0; // indicates that the NOR is ready
}
else if(s->cur_cmd == NOR_READ_DATA_CMD && s->in_buf_cur_ind == s->in_buf_size) {
if(!s->nor_initialized) { initialize_nor(s); }
s->nor_read_ind = (s->in_buf[1] << 16) | (s->in_buf[2] << 8) | s->in_buf[3];
}
return 0x0;
}
else {
uint8_t ret_val;
// otherwise, we're outputting the response
if(s->cur_cmd == NOR_READ_DATA_CMD) {
uint8_t ret_val = s->nor_data[s->nor_read_ind];
s->nor_read_ind++;
return ret_val;
}
else {
ret_val = s->out_buf[s->out_buf_cur_ind];
s->out_buf_cur_ind++;
if(s->cur_cmd != 0 && (s->out_buf_cur_ind == s->out_buf_size)) {
// the command is done - clean up
s->cur_cmd = 0;
free(s->in_buf);
free(s->out_buf);
}
}
return ret_val;
}
}
static void ipod_touch_nor_spi_realize(SSIPeripheral *d, Error **errp)
{
IPodTouchNORSPIState *s = IPOD_TOUCH_NOR_SPI(d);
s->nor_initialized = 0;
}
static void ipod_touch_nor_spi_class_init(ObjectClass *klass, void *data)
{
SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass);
k->realize = ipod_touch_nor_spi_realize;
k->transfer = ipod_touch_nor_spi_transfer;
}
static const TypeInfo ipod_touch_nor_spi_type_info = {
.name = TYPE_IPOD_TOUCH_NOR_SPI,
.parent = TYPE_SSI_PERIPHERAL,
.instance_size = sizeof(IPodTouchNORSPIState),
.class_init = ipod_touch_nor_spi_class_init,
};
static void ipod_touch_nor_spi_register_types(void)
{
type_register_static(&ipod_touch_nor_spi_type_info);
}
type_init(ipod_touch_nor_spi_register_types)
+235
View File
@@ -0,0 +1,235 @@
#include "hw/arm/ipod_touch_pcf50633_pmu.h"
#include "hw/arm/ipod_touch_lcd.h"
#include "qemu/cutils.h"
#include "sysemu/rtc.h"
#include "sysemu/sysemu.h"
static int pcf50633_event(I2CSlave *i2c, enum i2c_event event)
{
Pcf50633State *s = PCF50633(i2c);
// printf("%s Event %d\n", __func__, s->cmd);
if (event == I2C_START_SEND)
{
// printf("%s start send %d\n", __func__, s->cmd);
}
else if (event == I2C_FINISH)
{
s->ready = 1;
// printf("%s end send %d\n", __func__, s->cmd);
}
return 0;
}
static int int_to_bcd(int value) {
int shift = 0;
int res = 0;
while (value > 0) {
res |= (value % 10) << (shift++ << 2);
value /= 10;
}
return res;
}
static uint8_t pcf50633_recv(I2CSlave *i2c)
{
Pcf50633State *s = PCF50633(i2c);
printf("Reading PMU register %d\n", s->cmd);
time_t t = time(NULL);
struct tm tm = *localtime(&t);
int res = 0;
switch(s->cmd) {
case PMU_MBCS1:
res = 1; // battery power source
break;
case PMU_ADCC1:
res = 3; // battery charge voltage
break;
case PMU_RTCSC: // seconds
res = int_to_bcd(tm.tm_sec);
break;
case PMU_RTCMN: // minutes
res = int_to_bcd(tm.tm_min);
break;
case PMU_RTCHR: // hours
res = int_to_bcd(tm.tm_hour);
break;
case PMU_RTCDT: // days
res = int_to_bcd(tm.tm_mday);
break;
case PMU_RTCMT: // month
res = int_to_bcd(tm.tm_mon + 1);
break;
case PMU_RTCYR: // year
res = int_to_bcd(tm.tm_year - 100); // the year counts from 1900
break;
case 0x67:
res = 1; // whether we should enable debug UARTS
break;
case 0x69:
res = 0; // boot count error/panic
break;
case 0x76:
res = 0; // unknown register
break;
default:
res = 0;
}
s->cmd += 1;
return res;
}
static int pcf50633_send(I2CSlave *i2c, uint8_t data)
{
Pcf50633State *s = PCF50633(i2c);
if (s->ready)
{
s->curreg = data;
s->ready = 0;
}
else
{
s->cmd = data;
}
printf("Writing PMU register cmd %d reg %d\n", s->cmd, s->curreg);
switch(s->curreg) {
case PMU_DSBL1:
lcd_changebrightness(s->cmd);
break;
}
return 0;
}
static void pcf50633_init(Object *obj)
{
}
static void pcf50633_class_init(ObjectClass *klass, void *data)
{
I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
k->event = pcf50633_event;
k->recv = pcf50633_recv;
k->send = pcf50633_send;
}
static const TypeInfo pcf50633_info = {
.name = TYPE_PCF50633,
.parent = TYPE_I2C_SLAVE,
.instance_init = pcf50633_init,
.instance_size = sizeof(Pcf50633State),
.class_init = pcf50633_class_init,
};
/* Le pilote AppleD1759PMURTC lit un compteur LE32 en 0x5c et un
* décalage LE32 en 0x64. settimeofday ne réécrit que ce décalage.
* Les registres 0x61/0x62 concernent les erreurs de démarrage, pas le RTC.
*/
static uint32_t d1759_counter(Pcf50633State *s)
{
return s->rtc_tick_offset +
qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND;
}
static int d1759_event(I2CSlave *i2c, enum i2c_event event)
{
Pcf50633State *s = PCF50633(i2c);
if (event == I2C_START_SEND || event == I2C_FINISH) {
s->ready = 1;
} else if (event == I2C_START_RECV) {
/* Une lecture multioctet reste cohérente au passage d'une seconde. */
s->rtc_snapshot = d1759_counter(s);
}
return 0;
}
static uint8_t d1759_recv(I2CSlave *i2c)
{
Pcf50633State *s = PCF50633(i2c);
uint8_t reg = s->curreg++;
if (reg >= 0x5c && reg <= 0x5f) {
return s->rtc_snapshot >> ((reg - 0x5c) * 8);
}
if (reg >= 0x64 && reg <= 0x67) {
return s->rtc_unix_offset[reg - 0x64];
}
if (reg >= 0x50 && reg <= 0x5a) {
return s->rtc_alarm[reg - 0x50];
}
switch (reg) {
case 0x02: /* Conversion ADC terminée. */
return 2;
case PMU_MBCS1:
return 1;
case 0x69:
case 0x76:
default:
return 0;
}
}
static int d1759_send(I2CSlave *i2c, uint8_t data)
{
Pcf50633State *s = PCF50633(i2c);
uint8_t reg;
if (s->ready) {
s->curreg = data;
s->ready = 0;
return 0;
}
reg = s->curreg++;
if (reg >= 0x64 && reg <= 0x67) {
s->rtc_unix_offset[reg - 0x64] = data;
} else if (reg >= 0x50 && reg <= 0x5a) {
/* Conservation des réglages d'alarme ; IRQ RTC non émulée. */
s->rtc_alarm[reg - 0x50] = data;
} else if (reg == PMU_DSBL1) {
lcd_changebrightness(data);
}
return 0;
}
static void d1759_init(Object *obj)
{
Pcf50633State *s = PCF50633(obj);
struct tm tm;
qemu_get_timedate(&tm, 0);
s->rtc_tick_offset = mktimegm(&tm) -
qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND;
s->ready = 1;
}
static void d1759_class_init(ObjectClass *klass, void *data)
{
I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
k->event = d1759_event;
k->recv = d1759_recv;
k->send = d1759_send;
}
static const TypeInfo d1759_info = {
.name = TYPE_D1759,
.parent = TYPE_PCF50633,
.instance_init = d1759_init,
.class_init = d1759_class_init,
};
static void pcf50633_register_types(void)
{
type_register_static(&pcf50633_info);
type_register_static(&d1759_info);
}
type_init(pcf50633_register_types)
+158
View File
@@ -0,0 +1,158 @@
#include "hw/arm/ipod_touch_pke.h"
#include <openssl/bn.h>
#include <openssl/bio.h>
static uint8_t *datahex(char* string) {
if(string == NULL)
return NULL;
size_t slength = strlen(string);
if((slength % 2) != 0) // must be even
return NULL;
size_t dlength = slength / 2;
uint8_t* data = malloc(dlength);
memset(data, 0, dlength);
size_t index = 0;
while (index < slength) {
char c = string[index];
int value = 0;
if(c >= '0' && c <= '9')
value = (c - '0');
else if (c >= 'A' && c <= 'F')
value = (10 + (c - 'A'));
else if (c >= 'a' && c <= 'f')
value = (10 + (c - 'a'));
else {
free(data);
return NULL;
}
data[(index/2)] += value << (((index + 1) % 2) * 4);
index++;
}
return data;
}
static uint64_t ipod_touch_pke_read(void *opaque, hwaddr offset, unsigned size)
{
IPodTouchPKEState *s = (IPodTouchPKEState *)opaque;
//printf("%s: offset 0x%08x\n", __FUNCTION__, offset);
switch(offset) {
case REG_PKE_SEG_SIZE:
return s->seg_size_reg;
case REG_PKE_SEG_START ... (REG_PKE_SEG_START + 1024):
{
uint32_t *res = (uint32_t *)s->segments;
return res[(offset - REG_PKE_SEG_START) / 4];
}
default:
break;
}
return 0;
}
static void ipod_touch_pke_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
{
IPodTouchPKEState *s = (IPodTouchPKEState *)opaque;
//printf("%s: offset 0x%08x value 0x%08x\n", __FUNCTION__, offset, value);
switch(offset) {
case 0x0:
s->num_started = 0;
break;
case 0x10:
break;
case REG_PKE_SEG_START ... (REG_PKE_SEG_START + 1024):
{
uint32_t *segments_cast = (uint32_t *)s->segments;
segments_cast[(offset - REG_PKE_SEG_START) / 4] = value;
break;
}
case REG_PKE_START:
{
s->num_started++;
if(s->num_started == 5) { // TODO this is arbitrary!
BIGNUM *mod_bn = BN_lebin2bn(s->segments, s->segment_size, NULL);
BIGNUM *base_bn = BN_lebin2bn(s->segments + s->segment_size, s->segment_size, NULL);
BIGNUM *exp_bn = BN_new();
BN_dec2bn(&exp_bn,"65537");
// BN_print(BIO_new_fp(stdout, BIO_NOCLOSE), exp_bn);
// printf("\n\n");
BIGNUM *res = BN_new();
BN_CTX *ctx = BN_CTX_new();
BN_mod_exp(res, base_bn, exp_bn, mod_bn, ctx);
// BN_print(BIO_new_fp(stdout, BIO_NOCLOSE), res);
// printf("\n\n");
char *res_hex = datahex(BN_bn2hex(res));
// copy this into SEG1 - note that the hex conversion removes the first 0x00 bytes so we add it back and shift everything to the right one place.
for(int i = 0; i < (s->segment_size - 1); i++) { s->segments[s->segment_size + s->segment_size - 2 - i] = res_hex[i]; }
s->segments[s->segment_size + s->segment_size - 1] = 0x0;
}
break;
}
case REG_PKE_SEG_SIZE:
s->seg_size_reg = value;
uint32_t size_bit = (s->seg_size_reg >> 6);
if(size_bit == 0) { s->segment_size = 256; }
else if(size_bit == 1) { s->segment_size = 128; }
else { }
break;
case REG_PKE_SWRESET:
s->num_started = 0;
break;
default:
break;
}
}
static const MemoryRegionOps pke_ops = {
.read = ipod_touch_pke_read,
.write = ipod_touch_pke_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void ipod_touch_pke_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
DeviceState *dev = DEVICE(sbd);
IPodTouchPKEState *s = IPOD_TOUCH_PKE(dev);
memory_region_init_io(&s->iomem, obj, &pke_ops, s, "pke", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
}
static void ipod_touch_pke_class_init(ObjectClass *klass, void *data)
{
}
static const TypeInfo ipod_touch_pke_info = {
.name = TYPE_IPOD_TOUCH_PKE,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IPodTouchPKEState),
.instance_init = ipod_touch_pke_init,
.class_init = ipod_touch_pke_class_init,
};
static void ipod_touch_machine_types(void)
{
type_register_static(&ipod_touch_pke_info);
}
type_init(ipod_touch_machine_types)
+54
View File
@@ -0,0 +1,54 @@
#include "hw/arm/ipod_touch_scaler_csc.h"
static uint64_t ipod_touch_scaler_csc_read(void *opaque, hwaddr addr, unsigned size)
{
//fprintf(stderr, "%s: offset = 0x%08x\n", __func__, addr);
switch (addr) {
default:
return 0;
}
return 0;
}
static void ipod_touch_scaler_csc_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
{
IPodTouchScalerCSCState *s = IPOD_TOUCH_SCALER_CSC(opaque);
//printf("%s (base %d): writing 0x%08x to 0x%08x\n", __func__, s->base, data, addr);
}
static const MemoryRegionOps ipod_touch_scaler_csc_ops = {
.read = ipod_touch_scaler_csc_read,
.write = ipod_touch_scaler_csc_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void ipod_touch_scaler_csc_init(Object *obj)
{
IPodTouchScalerCSCState *s = IPOD_TOUCH_SCALER_CSC(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
memory_region_init_io(&s->iomem, obj, &ipod_touch_scaler_csc_ops, s, TYPE_IPOD_TOUCH_SCALER_CSC, 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
}
static void ipod_touch_scaler_csc_class_init(ObjectClass *klass, void *data)
{
}
static const TypeInfo ipod_touch_scaler_csc_type_info = {
.name = TYPE_IPOD_TOUCH_SCALER_CSC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IPodTouchScalerCSCState),
.instance_init = ipod_touch_scaler_csc_init,
.class_init = ipod_touch_scaler_csc_class_init,
};
static void ipod_touch_scaler_csc_register_types(void)
{
type_register_static(&ipod_touch_scaler_csc_type_info);
}
type_init(ipod_touch_scaler_csc_register_types)
+255
View File
@@ -0,0 +1,255 @@
#include "hw/arm/ipod_touch_sdio.h"
static void trigger_irq(void *opaque)
{
IPodTouchSDIOState *s = (IPodTouchSDIOState *)opaque;
s->irq_reg = 0x2;
qemu_irq_raise(s->irq);
}
void sdio_exec_cmd(IPodTouchSDIOState *s)
{
uint32_t cmd_type = s->cmd & 0x3f;
uint32_t addr = (s->arg >> 9) & 0x1ffff;
uint32_t func = (s->arg >> 28) & 0x7;
printf("SDIO CMD: %d, ADDR: %d, FUNC: %d\n", cmd_type, addr, func);
if(cmd_type == 0x3) {
// RCA request - ignore
}
else if(cmd_type == 0x5) {
if(addr == 0) {
// reading slot 0 - make sure there is a device here
//s->resp0 = (1 << 31) /* indicate ready */ | (BCM4325_FUNCTIONS << CMD5_FUNC_OFFSET) /* number of functions */;
}
}
else if(cmd_type == 0x7) {
// select card - ignore
}
else if(cmd_type == 0x34) {
// CMD52 - read/write from a register
bool is_write = (s->arg >> 31) != 0;
if(is_write) {
uint8_t data = s->arg & 0xFF;
s->registers[addr] = data;
if(addr == 0x2) { s->registers[0x3] = data; } // if we write to register 2, we also write the same result to register 3 (this is the enabled functions register)
printf("SDIO: Executing cmd52 by writing 0x%02x to register 0x%02x\n", data, addr);
} else {
if(addr == 0x1000e) {
// misc register
s->resp0 = (1 << 6) /* enable ALP clock */ | (1 << 7); /* enable HT clock */
}
else if(addr == 0x2020) {
// some indication that packets are ready??
s->resp0 = (1 << 6);
}
else {
printf("SDIO: Executing cmd52 by reading from 0x%02x (value: 0x%02x)\n", addr, s->registers[addr]);
s->resp0 = s->registers[addr];
}
}
}
else if(cmd_type == 0x35) {
// CMD53 - block transfer
addr = addr & 0x7fff;
bool is_write = (s->arg >> 31) != 0;
printf("SDIO: Executing cmd53 func %x with block size %d and %d blocks (reg address: 0x%08x, destination address: 0x%08x, write? %d)\n", func, s->blklen, s->numblk, addr, s->baddr, is_write);
if(is_write) {
if(func == 0x1) {
cpu_physical_memory_read(s->baddr, &s->registers[addr], s->blklen * s->numblk);
}
else if(func == 0x2) {
// this is a BCM4325 command - add a frame to the queue and schedule the IRQ request to indicate that the command has been completed
BCM4325FrameHeaderPacket *frame_header = calloc(sizeof(BCM4325FrameHeaderPacket), sizeof(uint8_t *));
uint16_t length = s->blklen * s->numblk;
frame_header->frame_length = length;
frame_header->checksum = length ^ 0xffff;
g_queue_push_tail(s->rx_fifo, frame_header);
timer_mod(s->irq_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / 100);
}
} else {
if(func == 0x1) {
if(addr == 0x0) {
// chip ID register
uint32_t chipid[] = { 0x5 << 0x10 };
cpu_physical_memory_write(s->baddr, &chipid, 0x4);
}
}
else if(func == 0x2) {
// we're reading a frame
BCM4325FrameHeaderPacket *frame_header = (BCM4325FrameHeaderPacket *) g_queue_pop_head(s->rx_fifo);
if(!frame_header) {
// create an empty frame
frame_header = calloc(sizeof(BCM4325FrameHeaderPacket), sizeof(uint8_t *));
frame_header->frame_length = s->blklen * s->numblk;
frame_header->checksum = (s->blklen * s->numblk) ^ 0xffff;
}
cpu_physical_memory_write(s->baddr, frame_header, sizeof(BCM4325FrameHeaderPacket));
}
}
// toggle IRQ register
s->irq_reg = 0x1;
qemu_irq_raise(s->irq);
//printf("Raised IRQ\n");
}
else {
hw_error("Unknown SDIO command %d", cmd_type);
}
}
static void ipod_touch_sdio_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
{
printf("%s: writing 0x%08x to 0x%08x\n", __func__, value, addr);
IPodTouchSDIOState *s = (struct IPodTouchSDIOState *) opaque;
switch(addr) {
case SDIO_CMD:
s->cmd = value;
if(value & (1 << 31)) { // execute bit is set
sdio_exec_cmd(s);
}
break;
case SDIO_ARGU:
s->arg = value;
break;
case SDIO_STATE:
s->state = value;
break;
case SDIO_STAC:
s->stac = value;
break;
case SDIO_CSR:
s->csr = value;
break;
case SDIO_IRQ:
qemu_irq_lower(s->irq);
break;
case SDIO_IRQMASK:
s->irq_mask = value;
break;
case SDIO_BADDR:
s->baddr = value;
break;
case SDIO_BLKLEN:
s->blklen = value;
break;
case SDIO_NUMBLK:
s->numblk = value;
break;
default:
break;
}
}
static uint64_t ipod_touch_sdio_read(void *opaque, hwaddr addr, unsigned size)
{
printf("%s: offset = 0x%08x\n", __func__, addr);
IPodTouchSDIOState *s = (struct IPodTouchSDIOState *) opaque;
switch (addr) {
case SDIO_CMD:
return s->cmd;
case SDIO_ARGU:
return s->arg;
case SDIO_STATE:
return s->state;
case SDIO_STAC:
return s->stac;
case SDIO_DSTA:
return (1 << 0) | (1 << 4) ; // 0x1 indicates that the SDIO is ready for a CMD, (1 << 4) that the command is complete
case SDIO_RESP0:
return s->resp0;
case SDIO_RESP1:
return s->resp1 == 0 ? 0x7465 : s->resp1;
case SDIO_RESP2:
return s->resp2 == 0 ? 0x7374 : s->resp2;
case SDIO_RESP3:
return s->resp3 == 0 ? 0xFFFF : s->resp3;
case SDIO_CSR:
return s->csr;
case SDIO_IRQ:
return s->irq_reg;
case SDIO_IRQMASK:
return s->irq_mask;
case SDIO_BADDR:
return s->baddr;
case SDIO_BLKLEN:
return s->blklen;
case SDIO_NUMBLK:
return s->numblk;
default:
break;
}
return 0;
}
static const MemoryRegionOps ipod_touch_sdio_ops = {
.read = ipod_touch_sdio_read,
.write = ipod_touch_sdio_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void ipod_touch_sdio_init(Object *obj)
{
DeviceState *dev = DEVICE(obj);
IPodTouchSDIOState *s = IPOD_TOUCH_SDIO(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
s->registers[0x9] = CIS_OFFSET; // registers 0x9 - 0xB contain the relative address offset, which we set to 0xC8 (200)
s->registers[19] = 0x1; // enable support for high speed mode
// set the vendor information
s->registers[CIS_OFFSET] = CIS_MANUFACTURER_ID;
s->registers[CIS_OFFSET + 1] = 0x4;
s->registers[CIS_OFFSET + 2] = BCM4325_MANUFACTURER & 0xFF;
s->registers[CIS_OFFSET + 3] = (BCM4325_MANUFACTURER >> 8) & 0xFF;
s->registers[CIS_OFFSET + 4] = BCM4325_PRODUCT_ID & 0xFF;
s->registers[CIS_OFFSET + 5] = (BCM4325_PRODUCT_ID >> 8) & 0xFF;
// set the MAC address (00:23:32:6E:AA:10)
s->registers[CIS_OFFSET + 6] = CIS_FUNCTION_EXTENSION;
s->registers[CIS_OFFSET + 8] = 0x4; // unknown
s->registers[CIS_OFFSET + 9] = 0x6; // the length of the MAC address
s->registers[CIS_OFFSET + 10] = 0x0;
s->registers[CIS_OFFSET + 11] = 0x23;
s->registers[CIS_OFFSET + 12] = 0x32;
s->registers[CIS_OFFSET + 13] = 0x6E;
s->registers[CIS_OFFSET + 14] = 0xAA;
s->registers[CIS_OFFSET + 15] = 0x10;
memory_region_init_io(&s->iomem, obj, &ipod_touch_sdio_ops, s, TYPE_IPOD_TOUCH_SDIO, 4096);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
s->irq_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, trigger_irq, s);
s->rx_fifo = g_queue_new();
}
static void ipod_touch_sdio_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
}
static const TypeInfo ipod_touch_sdio_type_info = {
.name = TYPE_IPOD_TOUCH_SDIO,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IPodTouchSDIOState),
.instance_init = ipod_touch_sdio_init,
.class_init = ipod_touch_sdio_class_init,
};
static void ipod_touch_sdio_register_types(void)
{
type_register_static(&ipod_touch_sdio_type_info);
}
type_init(ipod_touch_sdio_register_types)
+195
View File
@@ -0,0 +1,195 @@
#include "hw/arm/ipod_touch_sha1.h"
#include "qemu/log.h"
#include "qemu/bswap.h"
#include "hw/irq.h"
#include "hw/sysbus.h"
/*
* SHA1 engine of the S5L8720.
*
* The engine only runs the SHA1 compression function over 64-byte blocks, starting either from the standard initial
* state or from the current state; padding and length are up to the software:
* - CONFIG = 0x2: process a block starting from the initial state, CONFIG = 0x8 then 0xa: continue from the current
* state (iBoot, which pads the message itself)
* - the state registers (0x20-0x30) are readable and writable: the 2.2.1 kernel loads the initial state itself,
* hashes a code page in memory mode (MEMORY_START / INSIZE) and does the final padding block in software.
* The state registers hold the state words byte-swapped (the digest bytes in memory order).
*/
// deprecated in OpenSSL 3, but it is exactly the primitive the hardware implements
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
static void sha1_init_state(IPodTouchSHA1State *s)
{
SHA1_Init(&s->ctx);
}
static void sha1_process(IPodTouchSHA1State *s, const uint8_t *data, size_t len)
{
for(size_t off = 0; off + 64 <= len; off += 64) {
SHA1_Transform(&s->ctx, data + off);
}
}
static uint32_t *sha1_state_word(IPodTouchSHA1State *s, int i)
{
switch(i) {
case 0: return &s->ctx.h0;
case 1: return &s->ctx.h1;
case 2: return &s->ctx.h2;
case 3: return &s->ctx.h3;
default: return &s->ctx.h4;
}
}
static void sha1_irq_fire(void *opaque)
{
IPodTouchSHA1State *s = opaque;
s->irq_status = 1;
qemu_irq_raise(s->irq);
}
static void sha1_reset(IPodTouchSHA1State *s)
{
s->config = 0;
s->memory_start = 0;
s->memory_mode = 0;
s->insize = 0;
memset(&s->hw_buffer, 0, 0x10 * sizeof(uint32_t));
s->hw_buffer_dirty = false;
s->irq_enable = 0;
s->irq_status = 0;
if(s->irq_timer) timer_del(s->irq_timer);
qemu_irq_lower(s->irq);
sha1_init_state(s);
}
static uint64_t ipod_touch_sha1_read(void *opaque, hwaddr offset, unsigned size)
{
IPodTouchSHA1State *s = (IPodTouchSHA1State *)opaque;
switch(offset) {
case SHA_CONFIG:
return s->config;
case SHA_RESET:
return 0;
case SHA_MEMORY_START:
return s->memory_start;
case SHA_MEMORY_MODE:
return s->memory_mode;
case SHA_INSIZE:
return s->insize;
case SHA_IRQ:
return s->irq_status;
case 0x20 ... 0x33:
return bswap32(*sha1_state_word(s, (offset - 0x20) / 4));
}
return 0;
}
static void ipod_touch_sha1_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
{
IPodTouchSHA1State *s = (IPodTouchSHA1State *)opaque;
switch(offset) {
case SHA_CONFIG:
if(value & 0x2) {
// go: bit 3 clear means that this is the first block of a message
if(!(value & 0x8)) {
sha1_init_state(s);
}
if(s->hw_buffer_dirty) {
sha1_process(s, (uint8_t *)s->hw_buffer, 0x40);
memset(s->hw_buffer, 0, 0x40);
s->hw_buffer_dirty = false;
}
if(s->memory_mode) {
uint8_t *data = g_malloc(s->insize);
cpu_physical_memory_read(s->memory_start, data, s->insize);
sha1_process(s, data, s->insize);
g_free(data);
}
// the operation completes immediately: signal it if the driver asked for an interrupt for this
// operation (bit 2 of CONFIG, used by the 2.2.1 IOSHA1Accelerator; the synchronous code signing
// hashes poll and must not get one)
// raised a little later: the driver records the pending command after starting the engine
if(s->irq_enable && (value & 0x4)) {
timer_mod(s->irq_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 20 * SCALE_US);
}
} else {
s->config = value;
}
break;
case SHA_RESET:
sha1_reset(s);
break;
case SHA_IRQ:
s->irq_enable = value & 1;
break;
case SHA_IRQ_ACK:
// the interrupt handler (AppleS5L8900XSHA1) acknowledges with 1 before chaining the next chunk
s->irq_status = 0;
qemu_irq_lower(s->irq);
break;
case SHA_MEMORY_START:
s->memory_start = value;
break;
case SHA_MEMORY_MODE:
s->memory_mode = value;
break;
case SHA_INSIZE:
s->insize = value;
break;
case 0x20 ... 0x33:
*sha1_state_word(s, (offset - 0x20) / 4) = bswap32(value);
break;
case 0x40 ... 0x7c:
// write to the hardware buffer
s->hw_buffer[(offset - 0x40) / 4] |= value;
s->hw_buffer_dirty = true;
break;
}
}
static const MemoryRegionOps sha1_ops = {
.read = ipod_touch_sha1_read,
.write = ipod_touch_sha1_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void ipod_touch_sha1_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
DeviceState *dev = DEVICE(sbd);
IPodTouchSHA1State *s = IPOD_TOUCH_SHA1(dev);
memory_region_init_io(&s->iomem, obj, &sha1_ops, s, "sha1", 0x100);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
s->irq_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sha1_irq_fire, s);
sha1_reset(s);
}
static void ipod_touch_sha1_class_init(ObjectClass *klass, void *data)
{
}
static const TypeInfo ipod_touch_sha1_info = {
.name = TYPE_IPOD_TOUCH_SHA1,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IPodTouchSHA1State),
.instance_init = ipod_touch_sha1_init,
.class_init = ipod_touch_sha1_class_init,
};
static void ipod_touch_machine_types(void)
{
type_register_static(&ipod_touch_sha1_info);
}
type_init(ipod_touch_machine_types)
+336
View File
@@ -0,0 +1,336 @@
/*
* S5L8900 SPI Emulation
*
* by cmw
*/
#include "hw/arm/ipod_touch_spi.h"
static int apple_spi_word_size(IPodTouchSPIState *s)
{
switch (R_CFG_WORD_SIZE(REG(s, R_CFG))) {
case R_CFG_WORD_SIZE_8B:
return 1;
case R_CFG_WORD_SIZE_16B:
return 2;
case R_CFG_WORD_SIZE_32B:
return 4;
default:
break;
}
g_assert_not_reached();
}
static void apple_spi_update_xfer_tx(IPodTouchSPIState *s)
{
if (fifo8_is_empty(&s->tx_fifo)) {
REG(s, R_STATUS) |= R_STATUS_TXEMPTY;
}
}
static void apple_spi_update_xfer_rx(IPodTouchSPIState *s)
{
if (!fifo8_is_empty(&s->rx_fifo)) {
REG(s, R_STATUS) |= R_STATUS_RXREADY;
}
}
static void apple_spi_update_irq(IPodTouchSPIState *s)
{
uint32_t irq = 0;
uint32_t mask = 0;
if (REG(s, R_CFG) & R_CFG_IE_RXREADY) {
mask |= R_STATUS_RXREADY;
}
if (REG(s, R_CFG) & R_CFG_IE_TXEMPTY) {
mask |= R_STATUS_TXEMPTY;
}
if (REG(s, R_CFG) & R_CFG_IE_COMPLETE) {
mask |= R_STATUS_COMPLETE;
}
if (REG(s, R_STATUS) & mask) {
irq = 1;
}
if (irq != s->last_irq) {
s->last_irq = irq;
qemu_set_irq(s->irq, irq);
}
}
static void apple_spi_update_cs(IPodTouchSPIState *s)
{
BusState *b = BUS(s->spi);
BusChild *kid = QTAILQ_FIRST(&b->children);
if (kid) {
if (object_dynamic_cast(OBJECT(kid->child), TYPE_IPOD_TOUCH_MULTITOUCH)) {
qemu_set_irq(qdev_get_gpio_in_named(kid->child, SSI_GPIO_CS, 0),
(REG(s, R_PIN) & R_PIN_CS) != 0);
}
/* La sélection de la NOR conserve son comportement historique. */
}
}
static void apple_spi_cs_set(void *opaque, int pin, int level)
{
IPodTouchSPIState *s = IPOD_TOUCH_SPI(opaque);
if (level) {
REG(s, R_PIN) |= R_PIN_CS;
} else {
REG(s, R_PIN) &= ~R_PIN_CS;
}
apple_spi_update_cs(s);
}
static void apple_spi_run(IPodTouchSPIState *s)
{
uint32_t tx;
uint32_t rx;
if (!(REG(s, R_CTRL) & R_CTRL_RUN)) {
return;
}
if (REG(s, R_RXCNT) == 0 && REG(s, R_TXCNT) == 0) {
return;
}
apple_spi_update_xfer_tx(s);
//printf("TX queue: %d, RX queue: %d\n", REG(s, R_TXCNT), REG(s, R_RXCNT));
//printf("TX buffer size: %d, RX buffer size: %d\n", fifo8_num_used(&s->tx_fifo), fifo8_num_used(&s->rx_fifo));
while (REG(s, R_TXCNT) && !fifo8_is_empty(&s->tx_fifo)) {
tx = (uint32_t)fifo8_pop(&s->tx_fifo);
rx = ssi_transfer(s->spi, tx);
REG(s, R_TXCNT)--;
apple_spi_update_xfer_tx(s);
if (REG(s, R_RXCNT) > 0) {
if (fifo8_is_full(&s->rx_fifo)) {
hw_error("Rx buffer overflow: %d\n", fifo8_num_free(&s->rx_fifo));
qemu_log_mask(LOG_GUEST_ERROR, "%s: rx overflow\n", __func__);
REG(s, R_STATUS) |= R_STATUS_RXOVERFLOW;
} else {
fifo8_push(&s->rx_fifo, (uint8_t)rx);
REG(s, R_RXCNT)--;
apple_spi_update_xfer_rx(s);
}
}
}
// fetch the remaining bytes by sending sentinel bytes.
while (!fifo8_is_full(&s->rx_fifo) && (REG(s, R_RXCNT) > 0) && (REG(s, R_CFG) & R_CFG_AGD)) {
rx = ssi_transfer(s->spi, 0xff);
if (fifo8_is_full(&s->rx_fifo)) {
hw_error("Rx buffer overflow: %d\n", fifo8_num_free(&s->rx_fifo));
qemu_log_mask(LOG_GUEST_ERROR, "%s: rx overflow\n", __func__);
REG(s, R_STATUS) |= R_STATUS_RXOVERFLOW;
break;
} else {
fifo8_push(&s->rx_fifo, (uint8_t)rx);
REG(s, R_RXCNT)--;
apple_spi_update_xfer_rx(s);
}
}
if (REG(s, R_RXCNT) == 0 && REG(s, R_TXCNT) == 0) {
REG(s, R_STATUS) |= R_STATUS_COMPLETE;
}
//printf("<after> TX buffer size: %d, RX buffer size: %d\n", fifo8_num_used(&s->tx_fifo), fifo8_num_used(&s->rx_fifo));
}
static uint64_t ipod_touch_spi_read(void *opaque, hwaddr addr, unsigned size)
{
IPodTouchSPIState *s = IPOD_TOUCH_SPI(opaque);
//printf("%s (base %d): read from location 0x%08x\n", __func__, s->base, addr);
uint32_t r;
bool run = false;
r = s->regs[addr >> 2];
switch (addr) {
case R_RXDATA: {
const uint8_t *buf = NULL;
int word_size = apple_spi_word_size(s);
uint32_t num = 0;
if (fifo8_is_empty(&s->rx_fifo)) {
hw_error("Rx buffer underflow\n");
qemu_log_mask(LOG_GUEST_ERROR, "%s: rx underflow\n", __func__);
r = 0;
break;
}
buf = fifo8_pop_buf(&s->rx_fifo, word_size, &num);
memcpy(&r, buf, num);
if (fifo8_is_empty(&s->rx_fifo)) {
run = true;
}
break;
}
case R_STATUS: {
int val = 0;
val |= (fifo8_num_used(&s->tx_fifo) << R_STATUS_TXFIFO_SHIFT);
val |= (fifo8_num_used(&s->rx_fifo) << R_STATUS_RXFIFO_SHIFT);
r |= val;
break;
}
default:
break;
}
if (run) {
apple_spi_run(s);
}
apple_spi_update_irq(s);
return r;
}
static void ipod_touch_spi_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
{
IPodTouchSPIState *s = IPOD_TOUCH_SPI(opaque);
//printf("%s (base %d): writing 0x%08x to 0x%08x\n", __func__, s->base, data, addr);
uint32_t r = data;
uint32_t *mmio = &REG(s, addr);
uint32_t old = *mmio;
bool cs_flg = false;
bool run = false;
switch (addr) {
case R_CTRL:
if (r & R_CTRL_TX_RESET) {
fifo8_reset(&s->tx_fifo);
/* Le pilote interrompt aussi les commandes au retour de veille.
Ne pas réutiliser les octets d'une transaction abandonnée. */
if (s->mt) {
qemu_set_irq(qdev_get_gpio_in_named(DEVICE(s->mt), SSI_GPIO_CS, 0), 1);
}
}
if (r & R_CTRL_RX_RESET) {
fifo8_reset(&s->rx_fifo);
}
if (r & R_CTRL_RUN && !fifo8_is_empty(&s->tx_fifo)) {
run = true;
}
break;
case R_STATUS:
run = true;
r = old & (~r);
break;
case R_PIN:
cs_flg = true;
break;
case R_TXDATA ... R_TXDATA + 3: {
int word_size = apple_spi_word_size(s);
if (fifo8_is_full(&s->tx_fifo) || fifo8_num_free(&s->tx_fifo) < word_size) {
hw_error("Tx buffer overflow: %d\n", fifo8_num_free(&s->tx_fifo));
}
fifo8_push_all(&s->tx_fifo, (uint8_t *)&r, word_size);
break;
case R_CFG:
run = true;
break;
}
default:
break;
}
*mmio = r;
if (cs_flg) {
apple_spi_update_cs(s);
}
if (run) {
apple_spi_run(s);
}
if(addr == R_STATUS) {
apple_spi_update_xfer_tx(s);
apple_spi_update_xfer_rx(s);
}
apple_spi_update_irq(s);
}
static const MemoryRegionOps spi_ops = {
.read = ipod_touch_spi_read,
.write = ipod_touch_spi_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void ipod_touch_spi_reset(DeviceState *d)
{
IPodTouchSPIState *s = (IPodTouchSPIState *)d;
memset(s->regs, 0, sizeof(s->regs));
fifo8_reset(&s->tx_fifo);
fifo8_reset(&s->rx_fifo);
}
static uint32_t base_addr = 0;
void set_spi_base(uint32_t base)
{
base_addr = base;
}
static void ipod_touch_spi_realize(DeviceState *dev, struct Error **errp)
{
IPodTouchSPIState *s = IPOD_TOUCH_SPI(dev);
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
char bus_name[32] = { 0 };
snprintf(bus_name, sizeof(bus_name), "%s.bus", dev->id);
s->spi = ssi_create_bus(dev, (const char *)bus_name);
sysbus_init_irq(sbd, &s->irq);
sysbus_init_irq(sbd, &s->cs_line);
qdev_init_gpio_in_named(dev, apple_spi_cs_set, SSI_GPIO_CS, 1);
char name[5];
snprintf(name, 5, "spi%d", base_addr);
memory_region_init_io(&s->iomem, OBJECT(s), &spi_ops, s, name, 0x100);
sysbus_init_mmio(sbd, &s->iomem);
s->base = base_addr;
fifo8_create(&s->tx_fifo, R_FIFO_TX_DEPTH);
fifo8_create(&s->rx_fifo, R_FIFO_RX_DEPTH);
// create the peripheral
switch(s->base) {
case 0:
{
DeviceState *dev = ssi_create_peripheral(s->spi, TYPE_IPOD_TOUCH_NOR_SPI);
IPodTouchNORSPIState *nor = IPOD_TOUCH_NOR_SPI(dev);
s->nor = nor;
break;
}
case 1:
break;
case 4:
{
DeviceState *dev = ssi_create_peripheral(s->spi, TYPE_IPOD_TOUCH_MULTITOUCH);
IPodTouchMultitouchState *mt = IPOD_TOUCH_MULTITOUCH(dev);
s->mt = mt;
break;
}
}
}
static void ipod_touch_spi_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = ipod_touch_spi_realize;
dc->reset = ipod_touch_spi_reset;
}
static const TypeInfo ipod_touch_spi_info = {
.name = TYPE_IPOD_TOUCH_SPI,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IPodTouchSPIState),
.class_init = ipod_touch_spi_class_init,
};
static void ipod_touch_spi_register_types(void)
{
type_register_static(&ipod_touch_spi_info);
}
type_init(ipod_touch_spi_register_types)
+129
View File
@@ -0,0 +1,129 @@
#include "hw/arm/ipod_touch_sysic.h"
static uint64_t ipod_touch_sysic_read(void *opaque, hwaddr addr, unsigned size)
{
IPodTouchSYSICState *s = (IPodTouchSYSICState *) opaque;
//fprintf(stderr, "%s: offset = 0x%08x\n", __func__, addr);
switch (addr) {
case POWER_ID:
return s->power_id;
case POWER_SETSTATE:
case POWER_STATE:
return s->power_state;
case 0x7a:
case 0x7c:
return 1;
case GPIO_INTLEVEL ... (GPIO_INTLEVEL + (GPIO_NUMINTGROUPS - 1) * 4):
{
uint8_t group = (addr - GPIO_INTLEVEL) / 4;
return s->gpio_int_level[group];
}
case GPIO_INTSTAT ... (GPIO_INTSTAT + (GPIO_NUMINTGROUPS - 1) * 4):
{
uint8_t group = (addr - GPIO_INTSTAT) / 4;
return s->gpio_int_status[group];
}
case GPIO_INTEN ... (GPIO_INTEN + (GPIO_NUMINTGROUPS - 1) * 4):
{
uint8_t group = (addr - GPIO_INTEN) / 4;
return s->gpio_int_enabled[group];
}
case GPIO_INTTYPE ... (GPIO_INTTYPE + (GPIO_NUMINTGROUPS - 1) * 4):
{
uint8_t group = (addr - GPIO_INTTYPE) / 4;
return s->gpio_int_type[group];
}
default:
break;
}
return 0;
}
static void ipod_touch_sysic_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
{
IPodTouchSYSICState *s = (IPodTouchSYSICState *) opaque;
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, val, addr);
switch (addr) {
case POWER_ID:
s->power_id = val;
break;
case POWER_ONCTRL:
if((val & 0x20) != 0 || (val & 0x4) != 0 || (val & POWER_ID_ADM) != 0) { break; } // make sure that we do not record the 'on' state of some devices so it appears like they are turned on immediately.
s->power_state = val;
break;
case POWER_OFFCTRL:
s->power_state = val;
break;
case GPIO_INTLEVEL ... (GPIO_INTLEVEL + (GPIO_NUMINTGROUPS - 1) * 4):
{
break;
}
case GPIO_INTSTAT ... (GPIO_INTSTAT + (GPIO_NUMINTGROUPS - 1) * 4):
{
uint8_t group = (addr - GPIO_INTSTAT) / 4;
// acknowledge the interrupts and clear the corresponding bits
s->gpio_int_status[group] = s->gpio_int_status[group] & ~val;
qemu_irq_lower(s->gpio_irqs[group]);
break;
}
case GPIO_INTEN ... (GPIO_INTEN + (GPIO_NUMINTGROUPS - 1) * 4):
{
uint8_t group = (addr - GPIO_INTEN) / 4;
s->gpio_int_enabled[group] = val;
break;
}
case GPIO_INTTYPE ... (GPIO_INTTYPE + (GPIO_NUMINTGROUPS - 1) * 4):
{
uint8_t group = (addr - GPIO_INTTYPE) / 4;
s->gpio_int_type[group] = val;
break;
}
default:
break;
}
}
static const MemoryRegionOps ipod_touch_sysic_ops = {
.read = ipod_touch_sysic_read,
.write = ipod_touch_sysic_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void ipod_touch_sysic_init(Object *obj)
{
IPodTouchSYSICState *s = IPOD_TOUCH_SYSIC(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
memory_region_init_io(&s->iomem, obj, &ipod_touch_sysic_ops, s, TYPE_IPOD_TOUCH_SYSIC, 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
for(int grp = 0; grp < GPIO_NUMINTGROUPS; grp++) {
sysbus_init_irq(sbd, &s->gpio_irqs[grp]);
}
}
static void ipod_touch_sysic_class_init(ObjectClass *klass, void *data)
{
}
static const TypeInfo ipod_touch_sysic_type_info = {
.name = TYPE_IPOD_TOUCH_SYSIC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IPodTouchSYSICState),
.instance_init = ipod_touch_sysic_init,
.class_init = ipod_touch_sysic_class_init,
};
static void ipod_touch_sysic_register_types(void)
{
type_register_static(&ipod_touch_sysic_type_info);
}
type_init(ipod_touch_sysic_register_types)
+143
View File
@@ -0,0 +1,143 @@
#include "hw/arm/ipod_touch_timer.h"
static void s5l8900_st_update(IPodTouchTimerState *s)
{
s->freq_out = 1000000000 / 100;
s->tick_interval = /* bcount1 * get_ticks / freq + ((bcount2 * get_ticks / freq)*/
muldiv64((s->bcount1 < 1000) ? 1000 : s->bcount1, NANOSECONDS_PER_SECOND, s->freq_out);
s->next_planned_tick = 0;
}
static void s5l8900_st_set_timer(IPodTouchTimerState *s)
{
uint64_t last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->base_time;
s->next_planned_tick = last + (s->tick_interval - last % s->tick_interval);
timer_mod(s->st_timer, s->next_planned_tick + s->base_time);
s->last_tick = last;
}
static void s5l8900_st_tick(void *opaque)
{
IPodTouchTimerState *s = (IPodTouchTimerState *)opaque;
if (s->status & TIMER_STATE_START) {
//fprintf(stderr, "%s: Raising irq\n", __func__);
qemu_irq_raise(s->irq);
/* schedule next interrupt */
if(!(s->status & TIMER_STATE_MANUALUPDATE)) {
s5l8900_st_set_timer(s);
}
} else {
s->next_planned_tick = 0;
s->last_tick = 0;
timer_del(s->st_timer);
}
}
static void s5l8900_timer1_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
{
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, value, addr);
IPodTouchTimerState *s = (struct IPodTouchTimerState *) opaque;
switch(addr){
case TIMER_IRQSTAT:
s->irqstat = value;
return;
case TIMER_IRQLATCH:
//fprintf(stderr, "%s: lowering irq\n", __func__);
qemu_irq_lower(s->irq);
return;
case TIMER_4 + TIMER_CONFIG:
s5l8900_st_update(s);
s->config = value;
break;
case TIMER_4 + TIMER_STATE:
if (value & TIMER_STATE_START) {
s->base_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
s5l8900_st_update(s);
s5l8900_st_set_timer(s);
} else if (value == TIMER_STATE_STOP) {
timer_del(s->st_timer);
}
s->status = value;
break;
case TIMER_4 + TIMER_COUNT_BUFFER:
s->bcount1 = s->bcreload = value;
break;
case TIMER_4 + TIMER_COUNT_BUFFER2:
s->bcount2 = value;
break;
default:
break;
}
}
static uint64_t s5l8900_timer1_read(void *opaque, hwaddr addr, unsigned size)
{
//fprintf(stderr, "%s: read from location 0x%08x\n", __func__, addr);
IPodTouchTimerState *s = (struct IPodTouchTimerState *) opaque;
uint64_t elapsed_ns, ticks;
switch (addr) {
case TIMER_TICKSHIGH: // needs to be fixed so that read from low first works as well
elapsed_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / 2; // the timer ticks twice as slow as the CPU frequency in the kernel
ticks = clock_ns_to_ticks(s->sysclk, elapsed_ns);
//printf("TICKS: %lld\n", ticks);
s->ticks_high = (ticks >> 32);
s->ticks_low = (ticks & 0xFFFFFFFF);
return s->ticks_high;
case TIMER_TICKSLOW:
return s->ticks_low;
case TIMER_IRQSTAT:
return s->irqstat; // ~0; // s->irqstat;
case TIMER_IRQLATCH:
return 0xffffffff;
default:
break;
}
return 0;
}
static const MemoryRegionOps timer1_ops = {
.read = s5l8900_timer1_read,
.write = s5l8900_timer1_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void s5l8900_timer_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
DeviceState *dev = DEVICE(sbd);
IPodTouchTimerState *s = IPOD_TOUCH_TIMER(dev);
memory_region_init_io(&s->iomem, obj, &timer1_ops, s, "timer1", 0x10001);
sysbus_init_irq(sbd, &s->irq);
s->base_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
s->st_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s5l8900_st_tick, s);
}
static void s5l8900_timer_class_init(ObjectClass *klass, void *data)
{
}
static const TypeInfo ipod_touch_timer_info = {
.name = TYPE_IPOD_TOUCH_TIMER,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IPodTouchTimerState),
.instance_init = s5l8900_timer_init,
.class_init = s5l8900_timer_class_init,
};
static void ipod_touch_machine_types(void)
{
type_register_static(&ipod_touch_timer_info);
}
type_init(ipod_touch_machine_types)
+178
View File
@@ -0,0 +1,178 @@
#include "hw/arm/ipod_touch_tvout.h"
#include "qapi/error.h"
static uint64_t ipod_touch_tvout_sdo_read(void *opaque, hwaddr offset, unsigned size)
{
IPodTouchTVOutState *s = (IPodTouchTVOutState *)opaque;
//printf("%s: offset = 0x%08x\n", __func__, offset);
switch(offset) {
case SDO_CLKCON:
return s->sdo_clkcon;
case SDO_CONFIG:
return s->sdo_config;
case SDO_IRQ:
return s->sdo_irq;
case SDO_IRQMASK:
return s->sdo_irq_mask;
default:
break;
}
return 0;
}
static void ipod_touch_tvout_sdo_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
{
IPodTouchTVOutState *s = (IPodTouchTVOutState *)opaque;
//printf("%s: writing 0x%08x to 0x%08x\n", __func__, value, offset);
switch(offset) {
case SDO_CLKCON:
s->sdo_clkcon = value;
return;
case SDO_CONFIG:
s->sdo_config = value;
return;
case SDO_IRQ:
s->sdo_irq = 0x0;
qemu_irq_lower(s->irq);
return;
case SDO_IRQMASK:
s->sdo_irq_mask = value;
return;
}
}
static uint64_t ipod_touch_tvout_mixer1_read(void *opaque, hwaddr offset, unsigned size)
{
IPodTouchTVOutState *s = (IPodTouchTVOutState *)opaque;
//printf("%s: offset = 0x%08x\n", __func__, offset);
switch(offset) {
case MXR_STATUS:
return 0x4;
case MXR_CFG:
return s->mixer1_cfg;
default:
break;
}
return 0;
}
static void ipod_touch_tvout_mixer1_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
{
IPodTouchTVOutState *s = (IPodTouchTVOutState *)opaque;
//printf("%s: writing 0x%08x to 0x%08x\n", __func__, value, offset);
switch(offset) {
case MXR_STATUS:
if((value & 0x1) && (s->sdo_irq_mask & 1) == 0 && s->irq_count < 2) {
s->sdo_irq = 0x1;
s->irq_count += 1; // ugly hack for now
qemu_irq_raise(s->irq);
}
break;
case MXR_CFG:
s->mixer1_cfg = value;
break;
}
}
static uint64_t ipod_touch_tvout_mixer2_read(void *opaque, hwaddr offset, unsigned size)
{
IPodTouchTVOutState *s = (IPodTouchTVOutState *)opaque;
//printf("%s: offset = 0x%08x\n", __func__, offset);
switch(offset) {
case MXR_STATUS:
return s->mixer2_status;
case MXR_CFG:
return s->mixer2_cfg;
default:
break;
}
return 0;
}
static void ipod_touch_tvout_mixer2_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
{
IPodTouchTVOutState *s = (IPodTouchTVOutState *)opaque;
//printf("%s: writing 0x%08x to 0x%08x\n", __func__, value, offset);
switch(offset) {
case MXR_STATUS:
s->mixer2_status = value;
break;
case MXR_CFG:
s->mixer2_cfg = value;
break;
}
}
static const MemoryRegionOps ipod_touch_tvout_sdo_ops = {
.read = ipod_touch_tvout_sdo_read,
.write = ipod_touch_tvout_sdo_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static const MemoryRegionOps ipod_touch_tvout_mixer1_ops = {
.read = ipod_touch_tvout_mixer1_read,
.write = ipod_touch_tvout_mixer1_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static const MemoryRegionOps ipod_touch_tvout_mixer2_ops = {
.read = ipod_touch_tvout_mixer2_read,
.write = ipod_touch_tvout_mixer2_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void ipod_touch_tvout_init(Object *obj)
{
DeviceState *dev = DEVICE(obj);
IPodTouchTVOutState *s = IPOD_TOUCH_TVOUT(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
// mixer 1
memory_region_init_io(&s->mixer1_iomem, obj, &ipod_touch_tvout_mixer1_ops, s, "tvout_mixer1", 4096);
sysbus_init_mmio(sbd, &s->mixer1_iomem);
// mixer 2
memory_region_init_io(&s->mixer2_iomem, obj, &ipod_touch_tvout_mixer2_ops, s, "tvout_mixer2", 4096);
sysbus_init_mmio(sbd, &s->mixer2_iomem);
// SDO
memory_region_init_io(&s->sdo_iomem, obj, &ipod_touch_tvout_sdo_ops, s, "tvout_sdo", 4096);
sysbus_init_mmio(sbd, &s->sdo_iomem);
sysbus_init_irq(sbd, &s->irq);
}
static void ipod_touch_tvout_class_init(ObjectClass *klass, void *data)
{
}
static const TypeInfo ipod_touch_tvout_type_info = {
.name = TYPE_IPOD_TOUCH_TVOUT,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IPodTouchTVOutState),
.instance_init = ipod_touch_tvout_init,
.class_init = ipod_touch_tvout_class_init,
};
static void ipod_touch_tvout_register_types(void)
{
type_register_static(&ipod_touch_tvout_type_info);
}
type_init(ipod_touch_tvout_register_types)
+237
View File
@@ -0,0 +1,237 @@
/*
* S5L8720 UART (iPod Touch 2G)
*
* Samsung-derived UART with Apple's interrupt scheme, as used by AppleS5L8900XSerial (iPhone OS 2.x):
* - interrupts are enabled in UCON: 0x2000 TX, 0x1000 RX data, 0x0880 RX timeout, 0x14000 modem status
* - pending interrupts are flags of UTRSTAT, cleared by writing 1: 0x08 RX data, 0x10 RX timeout, 0x20 TX,
* 0x40 modem status, 0x100 (unused here); bits 0-2 are the usual RX ready / TX buffer empty / TX empty status
* - UFSTAT: RX count in bits 0-3 (full: bit 8), TX count in bits 4-7 (full: bit 9)
* Transmission is immediate, so the TX FIFO is always empty.
*/
#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/irq.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
#include "chardev/char-fe.h"
#include "qapi/error.h"
#include "qemu/module.h"
#include "qom/object.h"
#include "hw/arm/ipod_touch_uart.h"
#define ULCON 0x00
#define UCON 0x04
#define UFCON 0x08
#define UMCON 0x0c
#define UTRSTAT 0x10
#define UERSTAT 0x14
#define UFSTAT 0x18
#define UMSTAT 0x1c
#define UTXH 0x20
#define URXH 0x24
#define UBRDIV 0x28
#define UCON_TX_INT 0x2000
#define UCON_RX_INT 0x1000
#define UCON_RXTO_INT 0x0880
#define UCON_MODEM_INT 0x14000
#define UTRSTAT_RX_READY 0x01
#define UTRSTAT_TXBUF_EMPTY 0x02
#define UTRSTAT_TX_EMPTY 0x04
#define UTRSTAT_RX_INT 0x08
#define UTRSTAT_RXTO_INT 0x10
#define UTRSTAT_TX_INT 0x20
#define UTRSTAT_PENDING 0x1f8
#define UFCON_RX_RESET 0x2
#define UFCON_TX_RESET 0x4
#define RX_FIFO_SIZE 16
// the driver takes a full RX FIFO for an overrun and resets it: never fill it (a real line at 115200 bauds
// doesn't either, the driver drains it at the RX trigger level long before)
#define RX_FIFO_LIMIT 8
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchUARTState, IPOD_TOUCH_UART)
struct IPodTouchUARTState {
SysBusDevice parent_obj;
MemoryRegion iomem;
CharBackend chr;
qemu_irq irq;
uint32_t ulcon, ucon, ufcon, umcon, ubrdiv;
uint32_t pending;
uint8_t rx_fifo[RX_FIFO_SIZE];
uint32_t rx_head, rx_count;
};
static void uart_update(IPodTouchUARTState *s)
{
if (s->rx_count) {
if (s->ucon & UCON_RX_INT) {
s->pending |= UTRSTAT_RX_INT;
}
if (s->ucon & UCON_RXTO_INT) {
s->pending |= UTRSTAT_RXTO_INT;
}
}
// the TX FIFO drains instantly: the TX interrupt is pending as long as it is enabled
if (s->ucon & UCON_TX_INT) {
s->pending |= UTRSTAT_TX_INT;
}
qemu_set_irq(s->irq, (s->pending & UTRSTAT_PENDING) != 0);
}
static uint64_t uart_read(void *opaque, hwaddr addr, unsigned size)
{
IPodTouchUARTState *s = opaque;
uint32_t v = 0;
switch (addr) {
case ULCON: return s->ulcon;
case UCON: return s->ucon;
case UFCON: return s->ufcon;
case UMCON: return s->umcon;
case UBRDIV: return s->ubrdiv;
case UTRSTAT:
v = UTRSTAT_TXBUF_EMPTY | UTRSTAT_TX_EMPTY | (s->pending & UTRSTAT_PENDING);
if (s->rx_count) {
v |= UTRSTAT_RX_READY;
}
return v;
case UERSTAT:
return 0;
case UFSTAT:
// the driver computes (UFSTAT & 0xf) | ((UFSTAT & 0x100) >> 4): a full FIFO is 0 + the full bit
v = s->rx_count & 0xf;
if (s->rx_count >= RX_FIFO_SIZE) {
v |= 0x100;
}
return v;
case UMSTAT:
return 0x1; // clear to send
case URXH:
if (s->rx_count) {
v = s->rx_fifo[s->rx_head];
s->rx_head = (s->rx_head + 1) % RX_FIFO_SIZE;
s->rx_count--;
if (!s->rx_count) {
s->pending &= ~(UTRSTAT_RX_INT | UTRSTAT_RXTO_INT);
}
uart_update(s);
qemu_chr_fe_accept_input(&s->chr);
}
return v;
}
return 0;
}
static void uart_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
{
IPodTouchUARTState *s = opaque;
uint8_t ch;
switch (addr) {
case ULCON: s->ulcon = val; break;
case UCON:
s->ucon = val; break;
case UMCON: s->umcon = val; break;
case UBRDIV: s->ubrdiv = val; break;
case UFCON:
s->ufcon = val & ~(UFCON_RX_RESET | UFCON_TX_RESET);
if (val & UFCON_RX_RESET) {
s->rx_head = s->rx_count = 0;
s->pending &= ~(UTRSTAT_RX_INT | UTRSTAT_RXTO_INT);
qemu_chr_fe_accept_input(&s->chr);
}
break;
case UTRSTAT:
s->pending &= ~(val & UTRSTAT_PENDING);
// the handler clears the flags first, then services the FIFO: don't re-raise the RX flags here
qemu_set_irq(s->irq, (s->pending & UTRSTAT_PENDING) != 0);
return;
case UTXH:
ch = val;
qemu_chr_fe_write_all(&s->chr, &ch, 1);
break;
default:
return;
}
uart_update(s);
}
static const MemoryRegionOps uart_ops = {
.read = uart_read,
.write = uart_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = { .min_access_size = 1, .max_access_size = 4 },
};
static int uart_can_receive(void *opaque)
{
IPodTouchUARTState *s = opaque;
return s->rx_count < RX_FIFO_LIMIT ? RX_FIFO_LIMIT - s->rx_count : 0;
}
static void uart_receive(void *opaque, const uint8_t *buf, int size)
{
IPodTouchUARTState *s = opaque;
for (int i = 0; i < size && s->rx_count < RX_FIFO_SIZE; i++) {
s->rx_fifo[(s->rx_head + s->rx_count) % RX_FIFO_SIZE] = buf[i];
s->rx_count++;
}
uart_update(s);
}
static void uart_realize(DeviceState *dev, Error **errp)
{
IPodTouchUARTState *s = IPOD_TOUCH_UART(dev);
qemu_chr_fe_set_handlers(&s->chr, uart_can_receive, uart_receive, NULL, NULL, s, NULL, true);
}
static void uart_init(Object *obj)
{
IPodTouchUARTState *s = IPOD_TOUCH_UART(obj);
memory_region_init_io(&s->iomem, obj, &uart_ops, s, TYPE_IPOD_TOUCH_UART, 0x1000);
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
}
static Property uart_properties[] = {
DEFINE_PROP_CHR("chardev", IPodTouchUARTState, chr),
DEFINE_PROP_END_OF_LIST(),
};
static void uart_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = uart_realize;
device_class_set_props(dc, uart_properties);
}
static const TypeInfo uart_info = {
.name = TYPE_IPOD_TOUCH_UART,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IPodTouchUARTState),
.instance_init = uart_init,
.class_init = uart_class_init,
};
static void uart_register_types(void)
{
type_register_static(&uart_info);
}
type_init(uart_register_types)
DeviceState *ipod_touch_uart_create(hwaddr addr, Chardev *chr, qemu_irq irq)
{
DeviceState *dev = qdev_new(TYPE_IPOD_TOUCH_UART);
qdev_prop_set_chr(dev, "chardev", chr);
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq);
return dev;
}
+56
View File
@@ -0,0 +1,56 @@
#include "hw/arm/ipod_touch_unknown1.h"
static uint64_t ipod_touch_unknown1_read(void *opaque, hwaddr addr, unsigned size)
{
fprintf(stderr, "%s: offset = 0x%08x\n", __func__, addr);
switch (addr) {
case 0x140:
return 0x2;
case 0x144:
return 0x3;
default:
break;
}
return 0;
}
static void ipod_touch_unknown1_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
{
}
static const MemoryRegionOps ipod_touch_unknown1_ops = {
.read = ipod_touch_unknown1_read,
.write = ipod_touch_unknown1_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void ipod_touch_unknown1_init(Object *obj)
{
IPodTouchUnknown1State *s = IPOD_TOUCH_UNKNOWN1(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
memory_region_init_io(&s->iomem, obj, &ipod_touch_unknown1_ops, s, TYPE_IPOD_TOUCH_UNKNOWN1, 0x200);
sysbus_init_mmio(sbd, &s->iomem);
}
static void ipod_touch_unknown1_class_init(ObjectClass *klass, void *data)
{
}
static const TypeInfo ipod_touch_unknown1_type_info = {
.name = TYPE_IPOD_TOUCH_UNKNOWN1,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IPodTouchUnknown1State),
.instance_init = ipod_touch_unknown1_init,
.class_init = ipod_touch_unknown1_class_init,
};
static void ipod_touch_unknown1_register_types(void)
{
type_register_static(&ipod_touch_unknown1_type_info);
}
type_init(ipod_touch_unknown1_register_types)
+616
View File
@@ -0,0 +1,616 @@
/*
* Synopsys DesignWareCore for USB OTG.
*
* Copyright (c) 2011 Richard Ian Taylor.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "hw/platform-bus.h"
#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/arm/ipod_touch_usb_otg.h"
static inline size_t synopsys_usb_tx_fifo_start(synopsys_usb_state *_state, uint32_t _fifo)
{
if(_fifo == 0)
return _state->gnptxfsiz >> 16;
else
return _state->dptxfsiz[_fifo-1] >> 16;
}
static inline size_t synopsys_usb_tx_fifo_size(synopsys_usb_state *_state, uint32_t _fifo)
{
if(_fifo == 0)
return _state->gnptxfsiz & 0xFFFF;
else
return _state->dptxfsiz[_fifo-1] & 0xFFFF;
}
static void synopsys_usb_update_irq(synopsys_usb_state *_state)
{
_state->daintsts = 0;
_state->gintsts &=~ (GINTMSK_OEP | GINTMSK_INEP | GINTMSK_OTG);
if(_state->gotgint)
_state->gintsts |= GINTMSK_OTG;
int i;
for(i = 0; i < USB_NUM_ENDPOINTS; i++)
{
if(_state->out_eps[i].interrupt_status & _state->doepmsk)
{
_state->daintsts |= 1 << (i+DAINT_OUT_SHIFT);
if(_state->daintmsk & (1 << (i+DAINT_OUT_SHIFT)))
_state->gintsts |= GINTMSK_OEP;
}
if(_state->in_eps[i].interrupt_status & _state->diepmsk)
{
_state->daintsts |= 1 << (i+DAINT_IN_SHIFT);
if(_state->daintmsk & (1 << (i+DAINT_IN_SHIFT)))
_state->gintsts |= GINTMSK_INEP;
}
}
if((_state->pcgcctl & 3) == 0 && _state->gintmsk & _state->gintsts)
{
//printf("USB: IRQ triggered 0x%08x & 0x%08x.\n", _state->gintsts, _state->gintmsk);
qemu_irq_raise(_state->irq);
}
else
qemu_irq_lower(_state->irq);
}
static void synopsys_usb_update_ep(synopsys_usb_state *_state, synopsys_usb_ep_state *_ep)
{
if(_ep->control & USB_EPCON_SETNAK)
{
_ep->control |= USB_EPCON_NAKSTS;
_ep->interrupt_status |= USB_EPINT_INEPNakEff;
_ep->control &=~ USB_EPCON_SETNAK;
}
if(_ep->control & USB_EPCON_DISABLE)
{
_ep->interrupt_status |= USB_EPINT_EPDisbld;
_ep->control &=~ (USB_EPCON_DISABLE | USB_EPCON_ENABLE);
}
}
static void synopsys_usb_update_in_ep(synopsys_usb_state *_state, uint8_t _ep)
{
synopsys_usb_ep_state *eps = &_state->in_eps[_ep];
synopsys_usb_update_ep(_state, eps);
if(eps->control & USB_EPCON_ENABLE)
;//printf("USB: IN transfer queued on %d.\n", _ep);
}
static void synopsys_usb_update_out_ep(synopsys_usb_state *_state, uint8_t _ep)
{
synopsys_usb_ep_state *eps = &_state->out_eps[_ep];
synopsys_usb_update_ep(_state, eps);
if(eps->control & USB_EPCON_ENABLE)
;//printf("USB: OUT transfer queued on %d.\n", _ep);
}
static uint32_t synopsys_usb_in_ep_read(synopsys_usb_state *_state, uint8_t _ep, hwaddr _addr)
{
if(_ep >= USB_NUM_ENDPOINTS)
{
hw_error("usb_synopsys: Tried to read from disabled EP %d.\n", _ep);
return 0;
}
switch (_addr)
{
case 0x00:
return _state->in_eps[_ep].control;
case 0x08:
return _state->in_eps[_ep].interrupt_status;
case 0x10:
return _state->in_eps[_ep].tx_size;
case 0x14:
return _state->in_eps[_ep].dma_address;
case 0x1C:
return _state->in_eps[_ep].dma_buffer;
default:
hw_error("usb_synopsys: bad ep read offset 0x" HWADDR_FMT_plx "\n", _addr);
break;
}
return 0;
}
static uint32_t synopsys_usb_out_ep_read(synopsys_usb_state *_state, int _ep, hwaddr _addr)
{
if(_ep >= USB_NUM_ENDPOINTS)
{
hw_error("usb_synopsys: Tried to read from disabled EP %d.\n", _ep);
return 0;
}
switch (_addr)
{
case 0x00:
return _state->out_eps[_ep].control;
case 0x08:
return _state->out_eps[_ep].interrupt_status;
case 0x10:
return _state->out_eps[_ep].tx_size;
case 0x14:
return _state->out_eps[_ep].dma_address;
case 0x1C:
return _state->out_eps[_ep].dma_buffer;
default:
hw_error("usb_synopsys: bad ep read offset 0x" HWADDR_FMT_plx "\n", _addr);
break;
}
return 0;
}
static uint64_t synopsys_usb_read(void *opaque, hwaddr _addr, unsigned size)
{
synopsys_usb_state *state = (synopsys_usb_state *)opaque;
//printf("USB: Read 0x%08x.\n", _addr);
switch(_addr)
{
case PCGCCTL:
return state->pcgcctl;
case GOTGCTL:
return state->gotgctl;
case GOTGINT:
return state->gotgint;
case GRSTCTL:
return state->grstctl;
case GHWCFG1:
return state->ghwcfg1;
case GHWCFG2:
return state->ghwcfg2;
case GHWCFG3:
return state->ghwcfg3;
case GHWCFG4:
return state->ghwcfg4;
case GAHBCFG:
return state->gahbcfg;
case GUSBCFG:
return state->gusbcfg;
case GINTMSK:
return state->gintmsk;
case GINTSTS:
return state->gintsts;
case DIEPMSK:
return state->diepmsk;
case DOEPMSK:
return state->doepmsk;
case DAINTMSK:
return state->daintmsk;
case DAINTSTS:
return state->daintsts;
case DCTL:
return state->dctl;
case DCFG:
return state->dcfg;
case DSTS:
return state->dsts;
case GRXSTSR:
case GRXSTSP:
return 0; // TODO: Do something about this?
case GNPTXFSTS:
return 0xFFFFFFFF;
case GRXFSIZ:
return state->grxfsiz;
case GNPTXFSIZ:
return state->gnptxfsiz;
case DIEPTXF(1) ... DIEPTXF(USB_NUM_FIFOS+1):
_addr -= DIEPTXF(1);
_addr >>= 2;
return state->dptxfsiz[_addr];
case USB_INREGS ... (USB_INREGS + USB_EPREGS_SIZE - 4):
_addr -= USB_INREGS;
return synopsys_usb_in_ep_read(state, _addr >> 5, _addr & 0x1f);
case USB_OUTREGS ... (USB_OUTREGS + USB_EPREGS_SIZE - 4):
_addr -= USB_OUTREGS;
return synopsys_usb_out_ep_read(state, _addr >> 5, _addr & 0x1f);
case USB_FIFO_START ... USB_FIFO_END-4:
_addr -= USB_FIFO_START;
return *((uint32_t*)(&state->fifos[_addr]));
default:
hw_error("USB: Unhandled read address 0x%08x!\n", _addr);
}
return 0;
}
static void synopsys_usb_in_ep_write(synopsys_usb_state *_state, int _ep, hwaddr _addr, uint32_t _val)
{
if(_ep >= USB_NUM_ENDPOINTS)
{
hw_error("usb_synopsys: Wrote to disabled EP %d.\n", _ep);
return;
}
switch (_addr)
{
case 0x00:
_state->in_eps[_ep].control = _val;
synopsys_usb_update_in_ep(_state, _ep);
return;
case 0x08:
_state->in_eps[_ep].interrupt_status &=~ _val;
synopsys_usb_update_irq(_state);
return;
case 0x10:
_state->in_eps[_ep].tx_size = _val;
return;
case 0x14:
_state->in_eps[_ep].dma_address = _val;
return;
case 0x1C:
_state->in_eps[_ep].dma_buffer = _val;
return;
default:
hw_error("usb_synopsys: bad ep write offset 0x" HWADDR_FMT_plx "\n", _addr);
break;
}
}
static void synopsys_usb_out_ep_write(synopsys_usb_state *_state, int _ep, hwaddr _addr, uint32_t _val)
{
if(_ep >= USB_NUM_ENDPOINTS)
{
hw_error("usb_synopsys: Wrote to disabled EP %d.\n", _ep);
return;
}
switch (_addr)
{
case 0x00:
_state->out_eps[_ep].control = _val;
synopsys_usb_update_out_ep(_state, _ep);
return;
case 0x08:
_state->out_eps[_ep].interrupt_status &=~ _val;
synopsys_usb_update_irq(_state);
return;
case 0x10:
_state->out_eps[_ep].tx_size = _val;
return;
case 0x14:
_state->out_eps[_ep].dma_address = _val;
return;
case 0x1C:
_state->out_eps[_ep].dma_buffer = _val;
return;
default:
hw_error("usb_synopsys: bad ep write offset 0x" HWADDR_FMT_plx "\n", _addr);
break;
}
}
static void synopsys_usb_write(void *opaque, hwaddr _addr, uint64_t _val, unsigned size)
{
synopsys_usb_state *state = (synopsys_usb_state *)opaque;
//printf("USB: Write 0x%08x to 0x%08x.\n", _val, _addr);
switch(_addr)
{
case PCGCCTL:
state->pcgcctl = _val;
synopsys_usb_update_irq(state);
return;
case GOTGCTL:
state->gotgctl = _val;
break;
case GOTGINT:
state->gotgint &=~ _val;
synopsys_usb_update_irq(state);
return;
case GRSTCTL:
if(_val & GRSTCTL_CORESOFTRESET)
{
state->grstctl = GRSTCTL_CORESOFTRESET;
// Do reset stuff
// if(state->server_host)
// {
// tcp_usb_cleanup(&state->tcp_state);
// tcp_usb_init(&state->tcp_state, synopsys_usb_tcp_callback, NULL, state);
// printf("Connecting to USB server at %s:%d...\n",
// state->server_host, state->server_port);
// int ret = tcp_usb_connect(&state->tcp_state, state->server_host, state->server_port);
// if(ret < 0)
// hw_error("Failed to connect to USB server (%d).\n", ret);
// printf("Connected to USB server.\n");
// }
state->grstctl &= ~GRSTCTL_CORESOFTRESET;
state->grstctl |= GRSTCTL_AHBIDLE;
state->gintsts |= GINTMSK_RESET;
synopsys_usb_update_irq(state);
}
else if(_val == 0)
state->grstctl = _val;
return;
case GINTMSK:
state->gintmsk = _val;
synopsys_usb_update_irq(state);
break;
case GINTSTS:
state->gintsts &=~ _val;
synopsys_usb_update_irq(state);
return;
case DOEPMSK:
state->doepmsk = _val;
synopsys_usb_update_irq(state);
return;
case DIEPMSK:
state->diepmsk = _val;
synopsys_usb_update_irq(state);
return;
case DAINTMSK:
state->daintmsk = _val;
synopsys_usb_update_irq(state);
return;
case DAINTSTS:
state->daintsts &=~ _val;
synopsys_usb_update_irq(state);
return;
case GAHBCFG:
state->gahbcfg = _val;
return;
case GUSBCFG:
state->gusbcfg = _val;
return;
case DCTL:
if((_val & DCTL_SGNPINNAK) != (state->dctl & DCTL_SGNPINNAK)
&& (_val & DCTL_SGNPINNAK))
{
state->gintsts |= GINTMSK_GINNAKEFF;
_val &=~ DCTL_SGNPINNAK;
}
if((_val & DCTL_SGOUTNAK) != (state->dctl & DCTL_SGOUTNAK)
&& (_val & DCTL_SGOUTNAK))
{
state->gintsts |= GINTMSK_GOUTNAKEFF;
_val &=~ DCTL_SGOUTNAK;
}
state->dctl = _val;
synopsys_usb_update_irq(state);
return;
case DCFG:
//printf("USB: dcfg = 0x%08x.\n", _val);
state->dcfg = _val;
return;
case GRXFSIZ:
state->grxfsiz = _val;
return;
case GNPTXFSIZ:
state->gnptxfsiz = _val;
return;
case DIEPTXF(1) ... DIEPTXF(USB_NUM_FIFOS+1):
_addr -= DIEPTXF(1);
_addr >>= 2;
state->dptxfsiz[_addr] = _val;
return;
case USB_INREGS ... (USB_INREGS + USB_EPREGS_SIZE - 4):
_addr -= USB_INREGS;
synopsys_usb_in_ep_write(state, _addr >> 5, _addr & 0x1f, _val);
return;
case USB_OUTREGS ... (USB_OUTREGS + USB_EPREGS_SIZE - 4):
_addr -= USB_OUTREGS;
synopsys_usb_out_ep_write(state, _addr >> 5, _addr & 0x1f, _val);
return;
case USB_FIFO_START ... USB_FIFO_END-4:
_addr -= USB_FIFO_START;
*((uint32_t*)(&state->fifos[_addr])) = _val;
return;
default:
hw_error("USB: Unhandled write address 0x%08x!\n", _addr);
}
}
static const MemoryRegionOps usb_otg_ops = {
.read = synopsys_usb_read,
.write = synopsys_usb_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void s5l8900_usb_otg_reset(DeviceState *d)
{
synopsys_usb_state *state = S5L8900USBOTG(d);
printf("USB: cfg = 0x%08x, 0x%08x, 0x%08x, 0x%08x.\n",
state->ghwcfg1,
state->ghwcfg2,
state->ghwcfg3,
state->ghwcfg4);
state->pcgcctl = 3;
state->gahbcfg = 0;
state->gusbcfg = 0;
state->dctl = 0;
state->dcfg = 0;
state->dsts = 0;
state->gotgctl = 0;
state->gotgint = 0;
state->gintmsk = 0;
state->gintsts = 0;
state->daintmsk = 0;
state->daintsts = 0;
state->diepmsk = 0;
state->doepmsk = 0;
state->grxfsiz = 0x100;
state->gnptxfsiz = (0x100 << 16) | 0x100;
uint32_t counter = 0x200;
int i;
for(i = 0; i < USB_NUM_FIFOS; i++)
{
state->dptxfsiz[i] = (counter << 16) | 0x100;
counter += 0x100;
}
for(i = 0; i < USB_NUM_ENDPOINTS; i++)
{
synopsys_usb_ep_state *in = &state->in_eps[i];
in->control = 0;
in->dma_address = 0;
in->fifo = 0;
in->tx_size = 0;
synopsys_usb_ep_state *out = &state->out_eps[i];
out->control = 0;
out->dma_address = 0;
out->fifo = 0;
out->tx_size = 0;
}
synopsys_usb_update_irq(state);
}
static void s5l8900_usb_otg_init1(Object *obj)
{
DeviceState *dev = DEVICE(obj);
synopsys_usb_state *s = S5L8900USBOTG(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
memory_region_init_io(&s->iomem, OBJECT(s), &usb_otg_ops, s, "usb_otg", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
}
// Helper for adding to a machine
DeviceState *ipod_touch_init_usb_otg(qemu_irq _irq, uint32_t _hwcfg[4])
{
DeviceState *dev = qdev_new(TYPE_S5L8900USBOTG);
synopsys_usb_state *state = S5L8900USBOTG(dev);
state->ghwcfg1 = _hwcfg[0];
state->ghwcfg2 = _hwcfg[1];
state->ghwcfg3 = _hwcfg[2];
state->ghwcfg4 = _hwcfg[3];
SysBusDevice *sdev = SYS_BUS_DEVICE(dev);
sysbus_connect_irq(sdev, 0, _irq);
return dev;
}
static void s5l8900_usb_otg_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = s5l8900_usb_otg_reset;
}
static const TypeInfo s5l8900_usb_otg_info = {
.name = TYPE_S5L8900USBOTG,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(synopsys_usb_state),
.instance_init = s5l8900_usb_otg_init1,
.class_init = s5l8900_usb_otg_class_init,
};
static void s5l8900_usb_otg_register_types(void)
{
type_register_static(&s5l8900_usb_otg_info);
}
type_init(s5l8900_usb_otg_register_types)
+87
View File
@@ -0,0 +1,87 @@
#include "hw/arm/ipod_touch_usb_phys.h"
static uint64_t ipod_touch_usb_phys_read(void *opaque, hwaddr addr, unsigned size)
{
IPodTouchUSBPhysState *s = (IPodTouchUSBPhysState *) opaque;
switch(addr)
{
case REG_OPHYPWR:
return s->usb_ophypwr;
case REG_OPHYCLK:
return s->usb_ophyclk;
case REG_ORSTCON:
return s->usb_orstcon;
case REG_UNKNOWN1:
return s->usb_unknown1;
case REG_OPHYTUNE:
return s->usb_ophytune;
default:
//return 0x0;
printf("%s: read invalid location 0x%08x\n", __func__, addr);
return 0x0;
}
}
static void ipod_touch_usb_phys_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
{
IPodTouchUSBPhysState *s = (IPodTouchUSBPhysState *) opaque;
switch(addr)
{
case REG_OPHYPWR:
s->usb_ophypwr = val;
return;
case REG_OPHYCLK:
s->usb_ophyclk = val;
return;
case REG_ORSTCON:
s->usb_orstcon = val;
return;
case REG_UNKNOWN1:
s->usb_unknown1 = val;
return;
case REG_OPHYTUNE:
s->usb_ophytune = val;
return;
default:
printf("%s: write invalid location 0x%08x.\n", __func__, addr);
return;
}
}
static const MemoryRegionOps ipod_touch_usb_phys_ops = {
.read = ipod_touch_usb_phys_read,
.write = ipod_touch_usb_phys_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void ipod_touch_usb_phys_init(Object *obj)
{
IPodTouchUSBPhysState *s = IPOD_TOUCH_USB_PHYS(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
memory_region_init_io(&s->iomem, obj, &ipod_touch_usb_phys_ops, s, TYPE_IPOD_TOUCH_USB_PHYS, 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
}
static void ipod_touch_usb_phys_class_init(ObjectClass *klass, void *data)
{
}
static const TypeInfo ipod_touch_usb_phys_type_info = {
.name = TYPE_IPOD_TOUCH_USB_PHYS,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IPodTouchUSBPhysState),
.instance_init = ipod_touch_usb_phys_init,
.class_init = ipod_touch_usb_phys_class_init,
};
static void ipod_touch_usb_phys_register_types(void)
{
type_register_static(&ipod_touch_usb_phys_type_info);
}
type_init(ipod_touch_usb_phys_register_types)
+1
View File
@@ -65,6 +65,7 @@ arm_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmuv3.c'))
arm_ss.add(when: 'CONFIG_FSL_IMX6UL', if_true: files('fsl-imx6ul.c', 'mcimx6ul-evk.c'))
arm_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_soc.c'))
arm_ss.add(when: 'CONFIG_XEN', if_true: files('xen_arm.c'))
arm_ss.add(when: 'CONFIG_IPOD_TOUCH_2G', if_true: files('ipod_touch_2g.c', 'ipod_touch_uart.c', 'ipod_touch_clock.c', 'ipod_touch_chipid.c', 'ipod_touch_gpio.c', 'ipod_touch_sysic.c', 'ipod_touch_timer.c', 'ipod_touch_usb_otg.c', 'ipod_touch_usb_phys.c', 'ipod_touch_spi.c', 'ipod_touch_nor_spi.c', 'ipod_touch_sha1.c', 'ipod_touch_aes.c', 'ipod_touch_pke.c', 'ipod_touch_pcf50633_pmu.c', 'ipod_touch_unknown1.c', 'ipod_touch_lcd.c', 'ipod_touch_mipi_dsi.c', 'ipod_touch_fmss.c', 'ipod_touch_mbx.c', 'ipod_touch_cd3272_mikey.c', 'ipod_touch_lis302dl.c', 'ipod_touch_multitouch.c', 'ipod_touch_scaler_csc.c', 'ipod_touch_sdio.c', 'ipod_touch_tvout.c', 'ipod_touch_isl29003dl.c'))
system_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmu-common.c'))
system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4_boards.c'))
+19 -7
View File
@@ -142,12 +142,12 @@ again:
case 0:
break;
case 1:
if ((req & (1u << dest_id)) == 0)
size = 0;
// if ((req & (1u << dest_id)) == 0)
// size = 0;
break;
case 2:
if ((req & (1u << src_id)) == 0)
size = 0;
// if ((req & (1u << src_id)) == 0)
// size = 0;
break;
case 3:
if ((req & (1u << src_id)) == 0
@@ -155,8 +155,9 @@ again:
size = 0;
break;
}
if (!size)
if (!size) {
continue;
}
/* Transfer one element. */
/* ??? Should transfer multiple elements for a burst request. */
@@ -179,6 +180,7 @@ again:
ch->dest += swidth;
}
//printf("Transfer size: %d, destination: 0x%08x\n", size, ch->dest);
size--;
ch->ctrl = (ch->ctrl & 0xfffff000) | size;
if (size == 0) {
@@ -204,6 +206,7 @@ again:
ch->conf &= ~PL080_CCONF_E;
}
if (ch->ctrl & PL080_CCTRL_I) {
//printf("Setting interrupt status of channel %d\n", c);
s->tc_int |= 1 << c;
}
}
@@ -212,6 +215,7 @@ again:
if (--s->running)
s->running = 1;
}
pl080_update(s);
}
static uint64_t pl080_read(void *opaque, hwaddr offset,
@@ -289,24 +293,30 @@ static void pl080_write(void *opaque, hwaddr offset,
PL080State *s = (PL080State *)opaque;
int i;
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, value, offset);
if (offset >= 0x100 && offset < 0x200) {
i = (offset & 0xe0) >> 5;
if (i >= s->nchannels)
goto bad_offset;
switch ((offset >> 2) & 7) {
case 0: /* SrcAddr */
//printf("%s: setting source address of channel %d to 0x%08x\n", __func__, i, value);
s->chan[i].src = value;
break;
case 1: /* DestAddr */
//printf("%s: setting destination address of channel %d to 0x%08x\n", __func__, i, value);
s->chan[i].dest = value;
break;
case 2: /* LLI */
s->chan[i].lli = value;
break;
case 3: /* Control */
//printf("%s: setting control of channel %d to 0x%08x (transfer size: %d)\n", __func__, i, value, value & 0xfff);
s->chan[i].ctrl = value;
break;
case 4: /* Configuration */
//printf("%s: setting configuration of channel %d to 0x%08x\n", __func__, i, value);
s->chan[i].conf = value;
pl080_run(s);
break;
@@ -381,8 +391,10 @@ static void pl080_init(Object *obj)
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
PL080State *s = PL080(obj);
memory_region_init_io(&s->iomem, OBJECT(s), &pl080_ops, s, "pl080", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
memory_region_init_io(&s->iomem1, OBJECT(s), &pl080_ops, s, "pl080", 0x1000);
sysbus_init_mmio(sbd, &s->iomem1);
memory_region_init_io(&s->iomem2, OBJECT(s), &pl080_ops, s, "pl080", 0x1000);
sysbus_init_mmio(sbd, &s->iomem2);
sysbus_init_irq(sbd, &s->irq);
sysbus_init_irq(sbd, &s->interr);
sysbus_init_irq(sbd, &s->inttc);
+244
View File
@@ -0,0 +1,244 @@
/*
* IPod Touch I2C Bus Serial Interface Emulation
*
* Copyright (C) 2012 Samsung Electronics Co Ltd.
* Maksim Kozlov, <m.kozlov@samsung.com>
* Igor Mitsyanko, <i.mitsyanko@samsung.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
#include "hw/i2c/ipod_touch_i2c.h"
static void s5l8900_i2c_update(IPodTouchI2CState *s)
{
uint16_t level;
level = (s->status & S5L8900_IICSTAT_START) &&
(s->control & S5L8900_IICCON_IRQEN);
if (s->control & S5L8900_IICCON_IRQPEND)
level = 0;
qemu_irq_raise(s->irq);
}
static int s5l8900_i2c_receive(IPodTouchI2CState *s)
{
int r;
r = i2c_recv(s->bus);
s5l8900_i2c_update(s);
return r;
}
static int s5l8900_i2c_send(IPodTouchI2CState *s, uint8_t data)
{
if (!(s->status & S5L8900_IICSTAT_LASTBIT)) {
s->status |= S5L8900_IICCON_ACKEN;
s->data = data;
s->iicreg20 |= 0x100;
i2c_send(s->bus, s->data);
}
s5l8900_i2c_update(s);
return 1;
}
/* I2C read function */
static uint64_t ipod_touch_i2c_read(void *opaque, hwaddr offset, unsigned size)
{
IPodTouchI2CState *s = (IPodTouchI2CState *)opaque;
//fprintf(stderr, "s5l8900_i2c_read(): offset = 0x%08x\n", offset);
switch (offset) {
case I2CCON:
return s->control;
case I2CSTAT:
return s->status;
case I2CADD:
return s->address;
case I2CDS:
s->iicreg20 |= 0x100;
s->data = s5l8900_i2c_receive(s);
return s->data;
case I2CLC:
return s->line_ctrl;
case IICREG20:
{
// clear the flags
uint32_t tmp_reg20 = s->iicreg20;
s->iicreg20 &= ~0x100;
s->iicreg20 &= ~0x2000;
return tmp_reg20;
}
default:
break;
//hw_error("s5l8900.i2c: bad read offset 0x" TARGET_FMT_plx "\n", offset);
//fprintf(stderr, "%s: bad read offset 0x%08x\n", __func__, offset);
}
return 0;
}
/* I2C write function */
static void ipod_touch_i2c_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
{
IPodTouchI2CState *s = (IPodTouchI2CState *)opaque;
int mode;
//printf("s5l8900_i2c_write (base %d): offset = 0x%08x, val = 0x%08x\n", s->base, offset, value);
qemu_irq_lower(s->irq);
switch (offset) {
case I2CCON:
if(value & ~(S5L8900_IICCON_ACKEN)) {
s->iicreg20 |= 0x100;
}
if((value & 0x10) && (s->status == 0x90)) {
s->iicreg20 |= 0x2000;
}
s->control = value & 0xff;
qemu_irq_raise(s->irq);
break;
case I2CSTAT:
/* We have to make sure we don't miss an end transfer */
if((!s->active) && ((s->status >> 6) != ((value >> 6)))) {
s->status = value & 0xff;
/* If they toggle the tx bit then we have to force an end transfer before mode update */
} else if((s->active) && ((s->status >> 6) != ((value >> 6)))) {
i2c_end_transfer(s->bus);
s->active=0;
s->status = value & 0xff;
s->status |= S5L8900_IICSTAT_TXRXEN;
break;
}
mode = (s->status >> 6) & 0x3;
if (value & S5L8900_IICSTAT_TXRXEN) {
/* IIC-bus data output enable/disable bit */
switch(mode) {
case SR_MODE:
s->data = s5l8900_i2c_receive(s);
break;
case ST_MODE:
s->data = s5l8900_i2c_receive(s);
break;
case MR_MODE:
if (value & S5L8900_IICSTAT_START) {
/* START condition */
s->status &= ~S5L8900_IICSTAT_LASTBIT;
s->iicreg20 |= 0x100;
s->active = 1;
i2c_start_transfer(s->bus, s->data >> 1, 1);
} else {
i2c_end_transfer(s->bus);
s->active = 0;
s->status |= S5L8900_IICSTAT_TXRXEN;
}
break;
case MT_MODE:
if (value & S5L8900_IICSTAT_START) {
/* START condition */
s->status &= ~S5L8900_IICSTAT_LASTBIT;
s->iicreg20 |= 0x100;
s->active = 1;
i2c_start_transfer(s->bus, s->data >> 1, 0);
} else {
i2c_end_transfer(s->bus);
s->active = 0;
s->status |= S5L8900_IICSTAT_TXRXEN;
}
break;
default:
break;
}
}
s5l8900_i2c_update(s);
break;
case I2CADD:
s->address = value & 0xff;
break;
case I2CDS:
s5l8900_i2c_send(s, value & 0xff);
break;
case I2CLC:
s->line_ctrl = value & 0xff;
break;
case IICREG20:
//s->iicreg20 &= ~value;
break;
default:
break;
//hw_error("s5l8900.i2c: bad write offset 0x" TARGET_FMT_plx "\n", offset);
//fprintf(stderr, "%s: bad write offset 0x%08x\n", __func__, offset);
}
}
static const MemoryRegionOps ipod_touch_i2c_ops = {
.read = ipod_touch_i2c_read,
.write = ipod_touch_i2c_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static int i2c_index = 0;
static void ipod_touch_i2c_init(Object *obj)
{
DeviceState *dev = DEVICE(obj);
IPodTouchI2CState *s = IPOD_TOUCH_I2C(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
memory_region_init_io(&s->iomem, obj, &ipod_touch_i2c_ops, s, TYPE_IPOD_TOUCH_I2C, 0x100);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
char bus_name[5];
sprintf(bus_name, "i2c%d", i2c_index);
s->bus = i2c_init_bus(dev, bus_name);
i2c_index += 1;
}
static void ipod_touch_i2c_reset(DeviceState *d)
{
}
static void ipod_touch_i2c_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = ipod_touch_i2c_reset;
}
static const TypeInfo ipod_touch_i2c_type_info = {
.name = TYPE_IPOD_TOUCH_I2C,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IPodTouchI2CState),
.instance_init = ipod_touch_i2c_init,
.class_init = ipod_touch_i2c_class_init,
};
static void ipod_touch_i2c_register_types(void)
{
type_register_static(&ipod_touch_i2c_type_info);
}
type_init(ipod_touch_i2c_register_types)
+1
View File
@@ -17,4 +17,5 @@ i2c_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_i2c.c'))
i2c_ss.add(when: 'CONFIG_PPC4XX', if_true: files('ppc4xx_i2c.c'))
i2c_ss.add(when: 'CONFIG_PCA954X', if_true: files('i2c_mux_pca954x.c'))
i2c_ss.add(when: 'CONFIG_PMBUS', if_true: files('pmbus_device.c'))
i2c_ss.add(when: 'CONFIG_IPOD_TOUCH_2G', if_true: files('ipod_touch_i2c.c'))
system_ss.add_all(when: 'CONFIG_I2C', if_true: i2c_ss)
+3
View File
@@ -8,6 +8,9 @@ config I8259
config PL190
bool
config PL192
bool
config IOAPIC
bool
select I8259
+1
View File
@@ -24,6 +24,7 @@ system_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic_common.c'))
system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_intc.c'))
system_ss.add(when: 'CONFIG_OPENPIC', if_true: files('openpic.c'))
system_ss.add(when: 'CONFIG_PL190', if_true: files('pl190.c'))
system_ss.add(when: 'CONFIG_PL192', if_true: files('pl192.c'))
system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_ic.c', 'bcm2836_control.c'))
system_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview_gic.c'))
system_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_intctl.c'))
+439
View File
@@ -0,0 +1,439 @@
/*
* ARM PrimeCell PL192 Vector Interrupt Controller
*
* Copyright (c) 2009 Samsung Electronics.
* Contributed by Kirill Batuzov <batuzovk@ispras.ru>
*/
#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/irq.h"
#include "hw/hw.h"
#include "qapi/error.h"
#include "hw/intc/pl192.h"
extern CPUState *getMainCpuEnv(void);
static void pl192_update(PL192State *);
static void pl192_raise(PL192State *s, int is_fiq)
{
if (is_fiq) {
if (s->fiq) {
/* Raise parent FIQ */
qemu_irq_raise(s->fiq);
} else {
if (s->daisy) {
/* FIQ is directly propagated through daisy chain */
pl192_raise(s->daisy, is_fiq);
} else {
fprintf(stderr, "pl192i cannot raise FIQ. This usually means that "
"initialization was done incorrectly.\n");
}
}
} else {
if (s->irq) {
/* Raise parent IRQ */
qemu_irq_raise(s->irq);
} else {
if (s->daisy) {
/* Setup daisy input of the next chained contorller and force
it to update it's state */
s->daisy->daisy_vectaddr = s->address;
s->daisy->daisy_callback = s;
s->daisy->daisy_input = 1;
pl192_update(s->daisy);
} else {
// TODO Needs urgent fixing!
hw_error("pl192: cannot raise IRQ. This usually means that initialization was done incorrectly.\n");
}
}
}
}
static void pl192_lower(PL192State *s, int is_fiq)
{
/* Lower parrent interrupt if there is one */
if (is_fiq && s->fiq) {
qemu_irq_lower(s->fiq);
}
if (!is_fiq && s->irq) {
qemu_irq_lower(s->irq);
}
/* Propagate to the previous controller in chain if needed */
if (s->daisy) {
if (!is_fiq) {
s->daisy->daisy_input = 0;
pl192_update(s->daisy);
} else {
pl192_lower(s->daisy, is_fiq);
}
}
}
/* Find interrupt of the highest priority */
static uint32_t pl192_priority_sorter(PL192State *s)
{
int i;
uint32_t prio_irq[PL192_PRIO_LEVELS];
for (i = 0; i < PL192_PRIO_LEVELS; i++) {
prio_irq[i] = PL192_NO_IRQ;
}
if (s->daisy_input) {
prio_irq[s->daisy_priority] = PL192_DAISY_IRQ;
}
for (i = PL192_INT_SOURCES - 1; i >= 0; i--) {
if (s->irq_status & (1 << i)) {
prio_irq[s->vect_priority[i]] = i;
}
}
for (i = 0; i < PL192_PRIO_LEVELS; i++) {
if ((s->sw_priority_mask & (1 << i)) &&
prio_irq[i] <= PL192_DAISY_IRQ) {
return prio_irq[i];
}
}
return PL192_NO_IRQ;
}
static void pl192_update(PL192State *s)
{
/* TODO: does SOFTINT affects IRQ_STATUS??? */
s->irq_status = (s->rawintr | s->softint) & s->intenable & ~s->intselect;
s->fiq_status = (s->rawintr | s->softint) & s->intenable & s->intselect;
if (s->fiq_status) {
pl192_raise(s, 1);
} else {
pl192_lower(s, 1);
}
if (s->irq_status || s->daisy_input) {
s->current_highest = pl192_priority_sorter(s);
if (s->current_highest < PL192_INT_SOURCES) {
s->address = s->vect_addr[s->current_highest];
} else {
s->address = s->daisy_vectaddr;
}
if (s->current_highest != s->current) {
if (s->current_highest < PL192_INT_SOURCES) {
if (s->vect_priority[s->current_highest] >= s->priority) {
return ;
}
}
if (s->current_highest == PL192_DAISY_IRQ) {
if (s->daisy_priority >= s->priority) {
return ;
}
}
if (s->current_highest <= PL192_DAISY_IRQ) {
pl192_raise(s, 0);
} else {
pl192_lower(s, 0);
}
}
} else {
s->current_highest = PL192_NO_IRQ;
pl192_lower(s, 0);
}
}
/* Set priority level when an interrupt have been acknoledged by CPU.
Also save interrupt id and priority to stack so it can be restored
lately. */
static inline void pl192_mask_priority(PL192State *s)
{
if (s->stack_i >= PL192_INT_SOURCES) {
hw_error("pl192: internal error (trying to mask when there are no more sources)\n");
}
s->stack_i++;
if (s->current == PL192_DAISY_IRQ) {
s->priority = s->daisy_priority;
} else {
s->priority = s->vect_priority[s->current];
}
s->priority_stack[s->stack_i] = s->priority;
s->irq_stack[s->stack_i] = s->current;
}
/* Set priority level when interrupt have been successfully processed by CPU.
Also restore previous interrupt id and priority level. */
static inline void pl192_unmask_priority(PL192State *s)
{
if (s->stack_i < 1) {
return; // simply ignore this event
//hw_error("pl192: internal error (mask stack insufficient)\n");
}
s->stack_i--;
s->priority = s->priority_stack[s->stack_i];
s->current = s->irq_stack[s->stack_i];
}
/* IRQ was acknoledged by CPU. Update controller state accordingly */
static uint32_t pl192_irq_ack(PL192State *s)
{
int is_daisy = (s->current_highest == PL192_DAISY_IRQ);
uint32_t res = s->address;
s->current = s->current_highest;
pl192_mask_priority(s);
if (is_daisy) {
pl192_mask_priority(s->daisy_callback);
}
pl192_update(s);
return res;
}
/* IRQ was processed by CPU. Update controller state accrodingly */
static void pl192_irq_fin(PL192State *s)
{
int is_daisy = (s->current == PL192_DAISY_IRQ);
pl192_unmask_priority(s);
if (is_daisy) {
pl192_unmask_priority(s->daisy_callback);
}
pl192_update(s);
/* hmm is this right?*/
/*
if (s->current == PL192_NO_IRQ && (s->current_highest >= PL192_INT_SOURCES)) {
pl192_lower(s, 0);
}
*/
}
static uint64_t pl192_read(void *opaque, hwaddr offset, unsigned size)
{
PL192State *s = (PL192State *) opaque;
if (offset & 3) {
fprintf(stderr, "pl192: bad read offset (1) " HWADDR_FMT_plx "\n", offset);
return 0;
}
if (offset >= 0xfe0 && offset < 0x1000) {
unsigned char pl192_id[] = { 0x92, 0x11, 0x04, 0x00, 0x0D, 0xF0, 0x05, 0xB1 };
return pl192_id[(offset - 0xfe0) >> 2];
}
if (offset >= 0x100 && offset < 0x180) {
return s->vect_addr[(offset - 0x100) >> 2];
}
if (offset >= 0x200 && offset < 0x280) {
return s->vect_priority[(offset - 0x200) >> 2];
}
switch (offset) {
case PL192_IRQSTATUS:
//fprintf(stderr, "%s: irqstatus 0x%08x\n", __FUNCTION__, s->irq_status);
return s->irq_status;
case PL192_FIQSTATUS:
return s->fiq_status;
case PL192_RAWINTR:
return s->rawintr;
case PL192_INTSELECT:
return s->intselect;
case PL192_INTENABLE:
return s->intenable;
case PL192_SOFTINT:
return s->softint;
case PL192_PROTECTION:
return s->protection;
case PL192_SWPRIORITYMASK:
return s->sw_priority_mask;
case PL192_PRIORITYDAISY:
return s->daisy_priority;
case PL192_INTENCLEAR:
return 0;
case PL192_SOFTINTCLEAR:
fprintf(stderr, "pl192: attempt to read write-only register (offset = "
HWADDR_FMT_plx ")\n", offset);
case PL192_VECTADDR:
return pl192_irq_ack(s);
/* Workaround for kernel code using PL190 */
case PL190_ITCR:
case PL190_VECTADDR:
case PL190_DEFVECTADDR:
return 0;
default:
fprintf(stderr, "pl192: bad read offset (2) " HWADDR_FMT_plx "\n", offset);
return 0;
}
}
static void pl192_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
{
PL192State *s = (PL192State *) opaque;
if (offset & 3) {
hw_error("pl192: bad write offset (1) " HWADDR_FMT_plx "\n", offset);
}
if (offset >= 0xfe0 && offset < 0x1000) {
hw_error("pl192: attempt to write to a read-only register (offset = "
HWADDR_FMT_plx ")\n", offset);
}
if (offset >= 0x100 && offset < 0x180) {
s->vect_addr[(offset - 0x100) >> 2] = value;
pl192_update(s);
return;
}
if (offset >= 0x200 && offset < 0x280) {
s->vect_priority[(offset - 0x200) >> 2] = value & 0xf;
pl192_update(s);
return;
}
switch (offset) {
case PL192_IRQSTATUS:
/* This is a readonly register, but linux tries to write to it
anyway. Ignore the write. */
return;
case PL192_FIQSTATUS:
case PL192_RAWINTR:
hw_error("pl192: attempt to write to a read-only register (offset = "
HWADDR_FMT_plx ")\n", offset);
break;
case PL192_INTSELECT:
s->intselect = value;
break;
case PL192_INTENABLE:
s->intenable |= value;
break;
case PL192_INTENCLEAR:
s->intenable &= ~value;
break;
case PL192_SOFTINT:
s->softint |= value;
break;
case PL192_SOFTINTCLEAR:
s->softint &= ~value;
break;
case PL192_PROTECTION:
/* TODO: implement protection */
s->protection = value & 1;
break;
case PL192_SWPRIORITYMASK:
s->sw_priority_mask = value & 0xffff;
break;
case PL192_PRIORITYDAISY:
s->daisy_priority = value & 0xf;
break;
case PL192_VECTADDR:
pl192_irq_fin(s);
return;
case PL190_ITCR:
case PL190_VECTADDR:
case PL190_DEFVECTADDR:
/* NB: This thing is not present here, but linux wants to write it */
/* Ignore written value */
return;
default:
fprintf(stderr, "pl192: bad write offset (2) " HWADDR_FMT_plx "\n", offset);
return;
}
pl192_update(s);
}
static void pl192_irq_handler(void *opaque, int irq, int level)
{
PL192State *s = (PL192State *) opaque;
if (level) {
s->rawintr |= 1 << irq;
} else {
s->rawintr &= ~(1 << irq);
}
pl192_update(opaque);
}
static void pl192_reset(DeviceState *d)
{
PL192State *s = PL192(d);
int i;
for (i = 0; i < PL192_INT_SOURCES; i++) {
s->vect_priority[i] = 0xf;
}
s->sw_priority_mask = 0xffff;
s->daisy_priority = 0xf;
s->current = PL192_NO_IRQ;
s->current_highest = PL192_NO_IRQ;
s->stack_i = 0;
s->priority_stack[0] = 0x10;
s->irq_stack[0] = PL192_NO_IRQ;
s->priority = 0x10;
}
static const MemoryRegionOps pl192_ops = {
.read = pl192_read,
.write = pl192_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
DeviceState *pl192_manual_init(char *mem_name, ...)
{
DeviceState *dev = qdev_new(TYPE_PL192);
PL192State *s = PL192(dev);
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
va_list va;
qemu_irq irq;
memory_region_init_io(&s->iomem, OBJECT(s), &pl192_ops, s, mem_name, 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
qdev_init_gpio_in(dev, pl192_irq_handler, PL192_INT_SOURCES);
sysbus_init_irq(sbd, &s->irq);
sysbus_init_irq(sbd, &s->fiq);
sysbus_realize_and_unref(sbd, &error_fatal);
va_start(va, mem_name);
int n = 0;
while (1) {
irq = va_arg(va, qemu_irq);
if (!irq) {
break;
}
sysbus_connect_irq(sbd, n, irq);
n++;
}
return dev;
}
static void pl192_init(Object *obj)
{
DeviceState *dev = DEVICE(obj);
PL192State *s = PL192(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
//memory_region_init_io(&s->iomem, obj, &pl192_ops, s, "pl192", 0x1000);
//sysbus_init_mmio(sbd, &s->iomem);
//qdev_init_gpio_in(dev, pl192_irq_handler, PL192_INT_SOURCES);
//sysbus_init_irq(sbd, s->irq);
//sysbus_init_irq(sbd, s->fiq);
}
static void pl192_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = pl192_reset;
//dc->vmsd = &vmstate_pl192;
// TODO save VM
}
static const TypeInfo pl192_info = {
.name = TYPE_PL192,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(PL192State),
.instance_init = pl192_init,
.class_init = pl192_class_init,
};
static void pl192_register_types(void)
{
type_register_static(&pl192_info);
}
type_init(pl192_register_types)
+182
View File
@@ -0,0 +1,182 @@
#ifndef HW_ARM_IPOD_TOUCH_H
#define HW_ARM_IPOD_TOUCH_H
#include "exec/hwaddr.h"
#include "hw/boards.h"
#include "hw/intc/pl192.h"
#include "hw/arm/boot.h"
#include "ui/console.h"
#include "cpu.h"
#include "hw/dma/pl080.h"
#include "hw/i2c/ipod_touch_i2c.h"
#include "hw/arm/ipod_touch_timer.h"
#include "hw/arm/ipod_touch_clock.h"
#include "hw/arm/ipod_touch_chipid.h"
#include "hw/arm/ipod_touch_gpio.h"
#include "hw/arm/ipod_touch_sysic.h"
#include "hw/arm/ipod_touch_usb_otg.h"
#include "hw/arm/ipod_touch_usb_phys.h"
#include "hw/arm/ipod_touch_spi.h"
#include "hw/arm/ipod_touch_sha1.h"
#include "hw/arm/ipod_touch_aes.h"
#include "hw/arm/ipod_touch_pke.h"
#include "hw/arm/ipod_touch_unknown1.h"
#include "hw/arm/ipod_touch_lcd.h"
#include "hw/arm/ipod_touch_mipi_dsi.h"
#include "hw/arm/ipod_touch_fmss.h"
#include "hw/arm/ipod_touch_mbx.h"
#include "hw/arm/ipod_touch_scaler_csc.h"
#include "hw/arm/ipod_touch_sdio.h"
#include "hw/arm/ipod_touch_tvout.h"
#define TYPE_IPOD_TOUCH "iPod-Touch"
#define S5L8720_VIC_N 2
#define S5L8720_VIC_SIZE 32
#define S5L8720_TIMER1_IRQ 0x7
#define S5L8720_USB_OTG_IRQ 0x13
#define S5L8720_SPI0_IRQ 0x9
#define S5L8720_SPI1_IRQ 0xA
#define S5L8720_SPI2_IRQ 0xB
#define S5L8720_LCD_IRQ 0xD
#define S5L8720_DMAC0_IRQ 0x10
#define S5L8720_DMAC1_IRQ 0x11
#define S5L8720_I2C0_IRQ 0x15
#define S5L8720_SPI3_IRQ 0x1C
#define S5L8720_I2C1_IRQ 0x16
#define S5L8720_TVOUT_SDO_IRQ 0x1E
#define S5L8720_SDIO_IRQ 0x2A
#define S5L8720_FMSS_IRQ 0x36
#define S5L8720_SPI4_IRQ 0x37
#define S5L8720_SHA1_IRQ 0x28
// GPIO interrupts
#define S5L8900_GPIO_G0_IRQ 0x21
#define S5L8900_GPIO_G1_IRQ 0x20
#define S5L8900_GPIO_G2_IRQ 0x1F
#define S5L8900_GPIO_G3_IRQ 0x03
#define S5L8900_GPIO_G4_IRQ 0x02
// key codes
#define KEY_UP (1 << 7)
#define KEY_P 25
#define KEY_P_DOWN KEY_P
#define KEY_P_UP (KEY_P_DOWN | KEY_UP)
#define KEY_H 35
#define KEY_H_DOWN KEY_H
#define KEY_H_UP (KEY_H_DOWN | KEY_UP)
#define KEY_MIN 12
#define KEY_MIN_DOWN KEY_MIN
#define KEY_MIN_UP (KEY_MIN_DOWN | KEY_UP)
#define KEY_PLUS 13
#define KEY_PLUS_DOWN KEY_PLUS
#define KEY_PLUS_UP (KEY_PLUS_DOWN | KEY_UP)
const int S5L8900_GPIO_IRQS[5] = { S5L8900_GPIO_G0_IRQ, S5L8900_GPIO_G1_IRQ, S5L8900_GPIO_G2_IRQ, S5L8900_GPIO_G3_IRQ, S5L8900_GPIO_G4_IRQ };
#define IT2G_CPREG_VAR_NAME(name) cpreg_##name
#define IT2G_CPREG_VAR_DEF(name) uint64_t IT2G_CPREG_VAR_NAME(name)
#define TYPE_IPOD_TOUCH_MACHINE MACHINE_TYPE_NAME(TYPE_IPOD_TOUCH)
#define IPOD_TOUCH_MACHINE(obj) \
OBJECT_CHECK(IPodTouchMachineState, (obj), TYPE_IPOD_TOUCH_MACHINE)
// memory addresses
#define VROM_MEM_BASE 0x0
#define INSECURE_RAM_MEM_BASE 0x8000000
#define SECURE_RAM_MEM_BASE 0xB000000
#define FRAMEBUFFER_MEM_BASE 0xFB00000
#define IBOOT_MEM_BASE 0xFF00000
#define SRAM1_MEM_BASE 0x22020000
#define SHA1_MEM_BASE 0x38000000
#define DMAC0_MEM_BASE 0x38200000
#define USBOTG_MEM_BASE 0x38400000
#define DMAC1_0_MEM_BASE 0x38700000
#define DISPLAY_MEM_BASE 0x38900000
#define FMSS_MEM_BASE 0x38A00000
#define AES_MEM_BASE 0x38C00000
#define SDIO_MEM_BASE 0x38D00000
#define VIC0_MEM_BASE 0x38E00000
#define VIC1_MEM_BASE 0x38E01000
#define EDGEIC_MEM_BASE 0x38E02000
#define H264_MEM_BASE 0x38F00000
#define MPVD_MEM_BASE 0x39600000 // arm-io/mpvd (décodeur vidéo), 7 pages de registres
#define UNKNOWN2_MEM_BASE 0x38100000 // configuré par LLB 2.2.1 (écritures en 0x40/0x44), absent du device tree
#define SCALER_CSC_MEM_BASE 0x39000000
#define TVOUT_MIXER2_MEM_BASE 0x39100000
#define TVOUT_MIXER1_MEM_BASE 0x39200000
#define TVOUT_SDO_MEM_BASE 0x39300000
#define SYSIC_MEM_BASE 0x39700000
#define DMAC1_1_MEM_BASE 0x39900000
#define MBX1_MEM_BASE 0x3B000000
#define MBX2_MEM_BASE 0x39400000
#define SPI0_MEM_BASE 0x3C300000
#define USBPHYS_MEM_BASE 0x3C400000
#define CLOCK0_MEM_BASE 0x3C500000
#define I2C0_MEM_BASE 0x3C600000
#define TIMER1_MEM_BASE 0x3C700000
#define I2C1_MEM_BASE 0x3C900000
#define UART0_MEM_BASE 0x3CC00000
#define SPI1_MEM_BASE 0x3CE00000
#define GPIO_MEM_BASE 0x3CF00000
#define PKE_MEM_BASE 0x3D000000
#define CHIPID_MEM_BASE 0x3D100000
#define SPI2_MEM_BASE 0x3D200000
#define UNKNOWN1_MEM_BASE 0x3D700000
#define MIPI_DSI_MEM_BASE 0x3D800000
#define SPI3_MEM_BASE 0x3DA00000
#define UART1_MEM_BASE 0x3DB00000
#define UART2_MEM_BASE 0x3DC00000
#define UART3_MEM_BASE 0x3DD00000
#define SWI_MEM_BASE 0x3DE00000
#define CLOCK1_MEM_BASE 0x3E000000
#define SPI4_MEM_BASE 0x3E100000
typedef struct {
MachineClass parent;
} IPodTouchMachineClass;
typedef struct {
MachineState parent;
AddressSpace *nsas;
qemu_irq **irq;
ARMCPU *cpu;
PL192State *vic0;
PL192State *vic1;
IPodTouchClockState *clock0;
IPodTouchClockState *clock1;
IPodTouchTimerState *timer1;
IPodTouchSPIState *spi0_state;
IPodTouchSPIState *spi1_state;
IPodTouchSPIState *spi4_state;
IPodTouchChipIDState *chipid_state;
IPodTouchGPIOState *gpio_state;
IPodTouchSYSICState *sysic;
synopsys_usb_state *usb_otg;
IPodTouchUSBPhysState *usb_phys_state;
IPodTouchSHA1State *sha1_state;
IPodTouchAESState *aes_state;
IPodTouchPKEState *pke_state;
IPodTouchI2CState *i2c0_state;
IPodTouchI2CState *i2c1_state;
IPodTouchLCDState *lcd_state;
IPodTouchMIPIDSIState *mipi_dsi_state;
IPodTouchFMSSState *fmss_state;
IPodTouchMBXState *mbx_state;
IPodTouchScalerCSCState *scaler_csc_state;
IPodTouchSDIOState *sdio_state;
IPodTouchTVOutState *tvout_state;
Clock *sysclk;
char bootrom_path[1024];
char nor_path[1024];
char nand_path[1024];
IT2G_CPREG_VAR_DEF(REG0);
IT2G_CPREG_VAR_DEF(REG1);
} IPodTouchMachineState;
#endif
+76
View File
@@ -0,0 +1,76 @@
#ifndef HW_ARM_IPOD_TOUCH_AES_H
#define HW_ARM_IPOD_TOUCH_AES_H
#include "qemu/osdep.h"
#include "hw/platform-bus.h"
#include "hw/hw.h"
#include "exec/hwaddr.h"
#include "exec/memory.h"
#include <openssl/aes.h>
// build of the firmware being booted ("5F138", "5H11a"), known once the first IMG3 KBAG is decrypted
extern const char *ipod_touch_fw_build;
// the NOR image comes from an old qemu-ios-generate-nor (unencrypted SHSH tags)
extern bool ipod_touch_legacy_nor;
#define TYPE_IPOD_TOUCH_AES "ipodtouch.aes"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchAESState, IPOD_TOUCH_AES)
#define key_uid ((uint8_t[]){0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF})
#define AES_128_CBC_BLOCK_SIZE 64
#define AES_CONTROL 0x0
#define AES_GO 0x4
#define AES_UNKREG0 0x8
#define AES_STATUS 0xC
#define AES_UNKREG1 0x10
#define AES_KEYLEN 0x14
#define AES_INSIZE 0x18
#define AES_INADDR 0x20
#define AES_OUTSIZE 0x24
#define AES_OUTADDR 0x28
#define AES_AUXSIZE 0x2C
#define AES_AUXADDR 0x30
#define AES_SIZE3 0x34
#define AES_KEY_REG 0x4C
#define AES_TYPE 0x6C
#define AES_IV_REG 0x74
#define AES_KEYSIZE 0x20
#define AES_IVSIZE 0x10
typedef enum AESKeyType {
AESCustom = 0,
AESGID = 1,
AESUID = 2
} AESKeyType;
typedef enum AESKeyLen {
AES128 = 0,
AES192 = 1,
AES256 = 2
} AESKeyLen;
typedef struct IPodTouchAESState
{
SysBusDevice busdev;
MemoryRegion iomem;
AES_KEY decryptKey;
uint32_t ivec[4];
uint32_t insize;
uint32_t inaddr;
uint32_t outsize;
uint32_t outaddr;
uint32_t auxaddr;
uint32_t keytype;
bool next_is_devicetree;
uint32_t status;
uint32_t ctrl;
uint32_t unkreg0;
uint32_t unkreg1;
uint32_t operation;
uint32_t keylen;
uint32_t custkey[8];
uint8_t gid_encryption_count;
} IPodTouchAESState;
#endif
+20
View File
@@ -0,0 +1,20 @@
#ifndef HW_CD3272_MIKEY_H
#define HW_CD3272_MIKEY_H
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/sysbus.h"
#include "hw/i2c/i2c.h"
#include "hw/irq.h"
#include "time.h"
#define TYPE_CD3272MIKEY "cd3272mikey"
OBJECT_DECLARE_SIMPLE_TYPE(CD3272MikeyState, CD3272MIKEY)
typedef struct CD3272MikeyState {
I2CSlave i2c;
uint32_t cmd;
} CD3272MikeyState;
#endif
+21
View File
@@ -0,0 +1,21 @@
#ifndef HW_ARM_IPOD_TOUCH_CHIPID_H
#define HW_ARM_IPOD_TOUCH_CHIPID_H
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#define TYPE_IPOD_TOUCH_CHIPID "ipodtouch.chipid"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchChipIDState, IPOD_TOUCH_CHIPID)
#define CHIPID_UNKNOWN1 0x4
#define CHIPID_INFO 0x8
#define CHIPID_UNKNOWN2 0xC
#define CHIPID_UNKNOWN3 0x10
typedef struct IPodTouchChipIDState {
SysBusDevice busdev;
MemoryRegion iomem;
} IPodTouchChipIDState;
#endif
+68
View File
@@ -0,0 +1,68 @@
#ifndef IPOD_TOUCH_CLOCK_H
#define IPOD_TOUCH_CLOCK_H
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "hw/irq.h"
#include "hw/clock.h"
#define TYPE_IPOD_TOUCH_CLOCK "ipodtouch.clock"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchClockState, IPOD_TOUCH_CLOCK)
#define CLOCK_CONFIG0 0x0
#define CLOCK_CONFIG1 0x4
#define CLOCK_CONFIG2 0x8
#define CLOCK_CONFIG3 0xC
#define CLOCK_CONFIG4 0x10
#define CLOCK_CONFIG5 0x14
#define CLOCK_PLL0CON 0x20
#define CLOCK_PLL1CON 0x24
#define CLOCK_PLL2CON 0x28
#define CLOCK_PLL3CON 0x2C
#define CLOCK_PLL0LCNT 0x30
#define CLOCK_PLL1LCNT 0x34
#define CLOCK_PLL2LCNT 0x38
#define CLOCK_PLL3LCNT 0x3C
#define CLOCK_PLLLOCK 0x40
#define CLOCK_PLLMODE 0x44
#define CLOCK_PWRCON0 0x48
#define CLOCK_PWRCON1 0x4C
#define CLOCK_PWRCON2 0x58
#define CLOCK_PWRCON3 0x68
#define CLOCK_PWRCON4 0x6C
typedef struct IPodTouchClockState
{
SysBusDevice busdev;
MemoryRegion iomem;
uint32_t config0;
uint32_t config1;
uint32_t config2;
uint32_t config3;
uint32_t config4;
uint32_t config5;
uint32_t pll0con;
uint32_t pll1con;
uint32_t pll2con;
uint32_t pll3con;
uint32_t pll0lcnt;
uint32_t pll1lcnt;
uint32_t pll2lcnt;
uint32_t pll3lcnt;
uint32_t pllmode;
uint32_t pwrcon0;
uint32_t pwrcon1;
uint32_t pwrcon2;
uint32_t pwrcon3;
uint32_t pwrcon4;
} IPodTouchClockState;
#endif
+19
View File
@@ -0,0 +1,19 @@
#ifndef HW_CS42L58_H
#define HW_CS42L58_H
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/sysbus.h"
#include "hw/i2c/i2c.h"
#include "hw/irq.h"
#define TYPE_CS42L58 "cs42l58"
OBJECT_DECLARE_SIMPLE_TYPE(CS42L58State, CS42L58)
typedef struct CS42L58State {
I2CSlave i2c;
uint32_t cmd;
} CS42L58State;
#endif
+58
View File
@@ -0,0 +1,58 @@
#ifndef IPOD_TOUCH_FMSS_H
#define IPOD_TOUCH_FMSS_H
#include <math.h>
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/sysbus.h"
#include "hw/hw.h"
#include "hw/irq.h"
#define TYPE_IPOD_TOUCH_FMSS "ipodtouch.fmss"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchFMSSState, IPOD_TOUCH_FMSS)
#define NAND_BYTES_PER_PAGE 4096
#define NAND_PAGES_PER_BLOCK 128
#define NAND_PAGES_PER_CHIP (4096 * NAND_PAGES_PER_BLOCK)
#define NAND_MAX_TRANSFER_PAGES 1024
extern bool ipod_touch_nand_write;
#define NAND_BYTES_PER_SPARE 64
#define FMSS__FMCTRL1 0x4
#define FMSS__CS_IRQ 0xC0C
#define FMSS__CS_IRQMASK 0xC10
#define FMSS__CS_BUF_RST_OK 0xC64
#define FMSS_CINFO_TARGET_ADDR 0xD08
#define FMSS_PAGES_IN_ADDR 0xD0C
#define FMSS_CS_BUF_ADDR 0xD10
#define FMSS_NUM_PAGES 0xD18
#define FMSS_PAGE_SPARE_OUT_ADDR 0xD1C
#define FMSS_PAGES_OUT_ADDR 0xD20
#define FMSS_CSGENRC 0xD30
typedef struct IPodTouchFMSSState
{
SysBusDevice parent_obj;
MemoryRegion iomem;
qemu_irq irq;
uint32_t shadow[0x400]; // dernière valeur écrite dans chaque registre (mot)
uint8_t *page_buffer;
uint8_t *page_spare_buffer;
uint32_t reg_cs_irq_bit;
uint32_t reg_result;
bool read_pending;
uint32_t reg_cinfo_target_addr;
uint32_t reg_pages_in_addr;
uint32_t reg_cs_buf_addr;
uint32_t reg_num_pages;
uint32_t reg_page_spare_out_addr;
uint32_t reg_pages_out_addr;
uint32_t reg_csgenrc;
char *nand_path;
} IPodTouchFMSSState;
#endif
+89
View File
@@ -0,0 +1,89 @@
/*
* Clés des images IMG3 de l'iPod Touch 2G (N72AP), indexées par le contenu de leur KBAG.
*
* On n'a pas la clé GID : quand le bootrom / LLB / iBoot demandent au moteur AES de déchiffrer
* un KBAG avec la clé GID, on renvoie directement l'IV et la clé publiés pour cette image.
* Généré depuis les IPSW iPod2,1 et https://api.ipsw.me/v4/keys/ipsw/iPod2,1/<build>.
*/
#ifndef HW_ARM_IPOD_TOUCH_GID_KEYS_H
#define HW_ARM_IPOD_TOUCH_GID_KEYS_H
#include <stdint.h>
typedef struct IPodTouchGIDKey {
const char *build;
const char *image;
uint8_t kbag[32]; /* IV + clé chiffrés avec la clé GID, tels que dans le tag KBAG */
uint8_t iv_key[32]; /* IV + clé en clair */
} IPodTouchGIDKey;
static const IPodTouchGIDKey ipod_touch_gid_keys[] = {
{ "5F138", "DeviceTree.n72ap.img3",
{ 0x0f, 0xe0, 0x82, 0xd8, 0x51, 0x40, 0xb9, 0x4d, 0x79, 0xcf, 0xd2, 0x10, 0x5d, 0xd6, 0x09, 0x2d, 0xee, 0x76, 0x8c, 0xbd, 0x10, 0xa2, 0x1a, 0x20, 0xf2, 0x54, 0x52, 0xbd, 0x1c, 0x3f, 0xd9, 0xd3 },
{ 0xcc, 0xff, 0x63, 0x4e, 0xe1, 0x27, 0x35, 0xf0, 0x19, 0x16, 0xc4, 0xa6, 0xb2, 0x0f, 0xf1, 0x45, 0xe1, 0x7b, 0xcd, 0x56, 0x8d, 0xf1, 0xcd, 0xdc, 0x8f, 0xec, 0xbf, 0x54, 0x87, 0xd5, 0xc3, 0xce } },
{ "5F138", "LLB.n72ap.RELEASE.img3",
{ 0xce, 0x46, 0x33, 0x70, 0xdb, 0xe5, 0x92, 0x6c, 0xa5, 0x3a, 0x32, 0x60, 0x76, 0xd5, 0xcd, 0xd4, 0xc4, 0xbd, 0x89, 0x09, 0x0a, 0x20, 0xb1, 0x53, 0x7f, 0x22, 0x25, 0x8f, 0x10, 0x5a, 0x21, 0x1b },
{ 0xce, 0x97, 0xa7, 0xc8, 0x2e, 0xf8, 0x64, 0x67, 0x5e, 0xd3, 0x68, 0x05, 0x97, 0xec, 0x2a, 0xef, 0x27, 0x73, 0x2a, 0x6b, 0xbf, 0xb1, 0x4a, 0x07, 0x25, 0x0a, 0x2e, 0x46, 0x82, 0xbf, 0x3c, 0xba } },
{ "5F138", "applelogo.s5l8720x.img3",
{ 0xf4, 0x94, 0xbb, 0x24, 0x68, 0x0c, 0xde, 0xa1, 0x2e, 0xeb, 0x59, 0x5f, 0x3b, 0x2e, 0x50, 0x60, 0x55, 0xff, 0xfb, 0x37, 0xfc, 0x2e, 0xdb, 0x8b, 0xb7, 0x43, 0x10, 0x6a, 0xdc, 0x7a, 0x03, 0x5a },
{ 0x64, 0x23, 0x8f, 0xb0, 0x32, 0x91, 0x42, 0x25, 0x22, 0xb5, 0xdd, 0x28, 0x3f, 0xc3, 0x89, 0x5c, 0x85, 0x9f, 0xd4, 0xd3, 0x82, 0xb8, 0x38, 0x51, 0x56, 0xfc, 0x58, 0x1a, 0x7f, 0x1d, 0x97, 0x22 } },
{ "5F138", "batterylow0.s5l8720x.img3",
{ 0x44, 0xfe, 0xfa, 0xc4, 0x35, 0x39, 0x92, 0x9a, 0x49, 0x67, 0xb3, 0xa7, 0x06, 0x40, 0x5d, 0xc5, 0x1d, 0x40, 0x1f, 0xce, 0x51, 0xcb, 0x3f, 0xd9, 0x19, 0xb1, 0x44, 0x2b, 0x50, 0x1d, 0xcc, 0x1f },
{ 0xcd, 0x40, 0xa1, 0x8b, 0x7f, 0x45, 0x49, 0x72, 0xa5, 0x7f, 0x7b, 0xff, 0x2d, 0x99, 0x47, 0x42, 0x9f, 0xcb, 0xdc, 0xe6, 0x09, 0x2a, 0xd1, 0xdd, 0xfe, 0xea, 0x62, 0x24, 0xd4, 0x03, 0xad, 0x55 } },
{ "5F138", "batterylow1.s5l8720x.img3",
{ 0x8f, 0xae, 0xf1, 0x9f, 0x5c, 0x21, 0x7b, 0xc3, 0x25, 0x2d, 0xfd, 0x64, 0x47, 0xdf, 0x9e, 0x53, 0x66, 0x79, 0xb8, 0x78, 0x57, 0x66, 0xaf, 0x3b, 0x39, 0x2e, 0x79, 0x1d, 0xce, 0x85, 0xe0, 0x8f },
{ 0xec, 0x0f, 0xb1, 0x00, 0x72, 0xfd, 0xa6, 0x82, 0xed, 0x5f, 0xff, 0xf8, 0x04, 0x00, 0xf8, 0x03, 0x31, 0x5a, 0xa2, 0x95, 0x7e, 0x58, 0x1c, 0xaf, 0xb0, 0xae, 0x3f, 0xc7, 0xf5, 0x5c, 0xb9, 0xac } },
{ "5F138", "glyphcharging.s5l8720x.img3",
{ 0x42, 0x2c, 0x5d, 0xf0, 0xdc, 0xc2, 0x28, 0x0b, 0xc6, 0xaa, 0x8a, 0xd5, 0x8d, 0xbf, 0x00, 0x20, 0x55, 0x59, 0x3c, 0x91, 0x6f, 0xc4, 0x9d, 0x96, 0xf1, 0x92, 0x1e, 0xac, 0x30, 0x7e, 0x76, 0x60 },
{ 0x4d, 0x13, 0x15, 0x95, 0xde, 0x30, 0xc6, 0x7f, 0xd2, 0x77, 0x27, 0x9c, 0x33, 0xee, 0x1b, 0xca, 0x72, 0x67, 0x78, 0x5f, 0x56, 0xba, 0x9a, 0x4a, 0x05, 0xe1, 0x16, 0x58, 0xa5, 0xa3, 0xec, 0x3d } },
{ "5F138", "glyphplugin.s5l8720x.img3",
{ 0xf2, 0xb5, 0xc7, 0x68, 0x2f, 0xfc, 0x4e, 0x27, 0x96, 0xad, 0xd2, 0xa7, 0x0d, 0xc0, 0xe7, 0x3d, 0xd9, 0x6e, 0x6a, 0x3f, 0x43, 0x1b, 0x42, 0x66, 0x33, 0x17, 0x9f, 0x4f, 0xff, 0xdd, 0x10, 0xd2 },
{ 0xc4, 0x2a, 0x87, 0xb7, 0xa9, 0x2b, 0x24, 0xbc, 0xde, 0xa5, 0x6e, 0xdf, 0x1b, 0x74, 0x41, 0x7a, 0xa7, 0x67, 0x6d, 0x77, 0x67, 0xca, 0x1d, 0x12, 0xa7, 0xdb, 0xf3, 0x12, 0x70, 0xe2, 0xb6, 0xc9 } },
{ "5F138", "iBoot.n72ap.RELEASE.img3",
{ 0x3c, 0xa4, 0xa2, 0x21, 0x41, 0x5a, 0x77, 0xe4, 0x24, 0xe3, 0x6c, 0xb3, 0xe2, 0x75, 0x4e, 0x77, 0x15, 0xb2, 0x5b, 0xfe, 0x11, 0xb9, 0x29, 0x9d, 0x71, 0xb0, 0x8f, 0xbd, 0x48, 0xb6, 0xeb, 0xe7 },
{ 0xb3, 0x63, 0x3a, 0xfb, 0xe0, 0x2e, 0x0e, 0x9b, 0xa4, 0xd7, 0x36, 0x6c, 0x47, 0xab, 0xe5, 0xa8, 0x2d, 0x91, 0x6d, 0xab, 0xb6, 0xdf, 0xd4, 0x59, 0x4d, 0xbe, 0x36, 0x35, 0xb4, 0xc7, 0x16, 0x62 } },
{ "5F138", "needservice.s5l8720x.img3",
{ 0x1a, 0xa1, 0x76, 0xdb, 0x4f, 0x6b, 0x2c, 0xb5, 0x65, 0xfa, 0x6c, 0xab, 0xae, 0xbb, 0xeb, 0x67, 0x16, 0x55, 0xd3, 0xb6, 0xde, 0x49, 0xe0, 0xda, 0x4a, 0xba, 0x37, 0x91, 0x8a, 0x1d, 0x21, 0x7c },
{ 0x19, 0xf9, 0xf7, 0xab, 0x10, 0xec, 0x1f, 0xbb, 0x27, 0x18, 0x51, 0x7f, 0x45, 0x8b, 0x9a, 0x8d, 0x9c, 0xf8, 0x07, 0x9c, 0x22, 0x1c, 0x8a, 0x08, 0x00, 0xd2, 0xa0, 0x86, 0x7d, 0xad, 0xa5, 0x1b } },
{ "5F138", "recoverymode.s5l8720x.img3",
{ 0xff, 0x23, 0x0a, 0xeb, 0x78, 0x10, 0x91, 0xcd, 0x94, 0x4f, 0x92, 0x2b, 0x81, 0xd6, 0x61, 0xc1, 0x24, 0x43, 0x65, 0xa6, 0x06, 0xde, 0xa1, 0xdb, 0x5a, 0x71, 0xb4, 0x5c, 0x6b, 0x7c, 0x94, 0x49 },
{ 0x47, 0x62, 0x1c, 0xf2, 0x2d, 0xc1, 0xb4, 0x5a, 0xda, 0x5a, 0xaa, 0x4b, 0x01, 0x24, 0xdd, 0xed, 0xc9, 0xde, 0x08, 0x34, 0x42, 0x8c, 0xa2, 0x8e, 0xcf, 0x53, 0x92, 0xec, 0x91, 0x95, 0xce, 0x79 } },
{ "5F138", "kernelcache.release.s5l8720x",
{ 0xa3, 0x47, 0x83, 0x31, 0xa2, 0x30, 0x9a, 0x66, 0x1b, 0x8c, 0xcb, 0x07, 0xf8, 0xe4, 0x50, 0xc8, 0x1c, 0xf5, 0xd0, 0xdb, 0x1d, 0x03, 0x11, 0x33, 0xb6, 0x8f, 0xad, 0x42, 0xae, 0xfa, 0xb3, 0x77 },
{ 0xa1, 0x91, 0x29, 0x12, 0x90, 0xd4, 0x87, 0xff, 0x07, 0x31, 0x96, 0x9c, 0x5f, 0xc8, 0xd9, 0x18, 0x0e, 0x4d, 0x23, 0xfa, 0x67, 0x59, 0x99, 0xd5, 0x95, 0x9d, 0xd1, 0x0c, 0x8d, 0xd7, 0x3d, 0x20 } },
{ "5H11a", "DeviceTree.n72ap.img3",
{ 0x34, 0x7a, 0xb5, 0x47, 0xe3, 0x86, 0x4a, 0x91, 0x66, 0xdd, 0xd3, 0x5b, 0xaa, 0x51, 0x87, 0x6f, 0x25, 0x98, 0x49, 0x38, 0x45, 0x5d, 0x73, 0x2f, 0x01, 0xd5, 0x2c, 0xf2, 0x00, 0x65, 0xf1, 0x39 },
{ 0x61, 0xa0, 0x0d, 0x92, 0xa1, 0xd9, 0x5f, 0x87, 0x94, 0xc6, 0xc6, 0xb9, 0x2c, 0x8f, 0x9a, 0xc3, 0xbf, 0xc1, 0xb2, 0xd5, 0xb6, 0x1d, 0x21, 0x62, 0xc4, 0x3f, 0xa6, 0x14, 0xec, 0xa8, 0xa7, 0x44 } },
{ "5H11a", "LLB.n72ap.RELEASE.img3",
{ 0x87, 0xb7, 0xe1, 0xea, 0x87, 0x39, 0xd5, 0x62, 0x1a, 0x11, 0x92, 0x5d, 0x90, 0x5c, 0x54, 0x47, 0xfa, 0xe5, 0x17, 0xef, 0xb0, 0x26, 0x64, 0x3f, 0xb8, 0x3c, 0xc8, 0x65, 0x22, 0xa5, 0x91, 0x45 },
{ 0x23, 0x52, 0xa6, 0x84, 0x1d, 0x61, 0x85, 0xcd, 0x0f, 0xb4, 0x39, 0x6f, 0xcf, 0xb3, 0x1c, 0x71, 0xef, 0xc8, 0x5f, 0x46, 0x10, 0x9b, 0xde, 0xbf, 0xbf, 0x46, 0x56, 0xc0, 0x52, 0x56, 0xc9, 0xd0 } },
{ "5H11a", "applelogo.s5l8720x.img3",
{ 0xd5, 0x5c, 0x18, 0xcf, 0x2e, 0x56, 0xd2, 0x84, 0x2f, 0xe4, 0xa2, 0x64, 0x47, 0x72, 0x54, 0x1e, 0xf0, 0x9a, 0x6f, 0xb6, 0xb8, 0x62, 0x73, 0x2f, 0xb9, 0x07, 0x1c, 0xc0, 0x91, 0xe0, 0x41, 0x71 },
{ 0x62, 0x94, 0x02, 0x72, 0xd1, 0xa3, 0xa3, 0x41, 0x06, 0xe5, 0xbb, 0xf6, 0xd7, 0xc1, 0x66, 0xea, 0xe8, 0xfd, 0xd3, 0x05, 0xf4, 0x8d, 0xe7, 0xc4, 0xf0, 0x86, 0xad, 0x17, 0xee, 0xc1, 0x80, 0xe8 } },
{ "5H11a", "batterylow0.s5l8720x.img3",
{ 0x49, 0x62, 0x52, 0xcf, 0x96, 0x29, 0xd2, 0x43, 0x1c, 0xa4, 0x7c, 0xb5, 0xaa, 0x58, 0xb8, 0xd3, 0x19, 0xe0, 0x77, 0x5b, 0xc4, 0x7f, 0xd5, 0xae, 0x94, 0x32, 0x74, 0xb2, 0xc9, 0xe0, 0xb4, 0xcd },
{ 0x94, 0x7f, 0x00, 0xc4, 0x36, 0xed, 0xc9, 0x49, 0x2a, 0xf0, 0xa0, 0x04, 0xd6, 0x21, 0x8b, 0x35, 0x14, 0x6f, 0x97, 0x5a, 0xf2, 0x0a, 0xaf, 0x3c, 0xcb, 0x43, 0x87, 0xab, 0x09, 0xcd, 0x45, 0x75 } },
{ "5H11a", "batterylow1.s5l8720x.img3",
{ 0xf8, 0xb5, 0x00, 0x5a, 0xbe, 0x94, 0x88, 0xb1, 0xed, 0x3c, 0x54, 0xe7, 0xe5, 0xa6, 0x91, 0xb3, 0xec, 0xe8, 0x11, 0x0c, 0x6b, 0x1e, 0xff, 0x21, 0x97, 0x38, 0x87, 0x8e, 0x11, 0xc4, 0xd5, 0xe7 },
{ 0xdb, 0xf1, 0xc4, 0xb7, 0xcc, 0x55, 0xa3, 0x9b, 0xf0, 0xa8, 0xe5, 0x5f, 0x97, 0x4c, 0xa7, 0x00, 0x8f, 0x6a, 0x56, 0xf1, 0x80, 0x07, 0x5b, 0xf5, 0xa8, 0x0d, 0x32, 0x0a, 0x55, 0x53, 0xdf, 0x46 } },
{ "5H11a", "glyphcharging.s5l8720x.img3",
{ 0xe0, 0x38, 0xde, 0x24, 0x5a, 0x33, 0xcf, 0x61, 0xc7, 0xab, 0xbb, 0x95, 0x93, 0xba, 0x00, 0x56, 0x70, 0x4f, 0x5c, 0x25, 0xca, 0x3a, 0xbc, 0x5e, 0x84, 0x77, 0x13, 0xe3, 0xd6, 0xb9, 0xa3, 0x90 },
{ 0xad, 0xed, 0x23, 0x37, 0x0f, 0x71, 0x85, 0x15, 0x07, 0xc0, 0xe6, 0xf5, 0x79, 0x41, 0x7c, 0xbf, 0x0e, 0xd5, 0x2a, 0x9d, 0x0e, 0xd0, 0xfe, 0xf0, 0xa0, 0x2c, 0x5f, 0x63, 0xa5, 0x66, 0x2d, 0x67 } },
{ "5H11a", "glyphplugin.s5l8720x.img3",
{ 0x95, 0xf7, 0xfb, 0x3f, 0x0f, 0xac, 0xd6, 0x7e, 0x64, 0x92, 0x1b, 0x62, 0x8a, 0x0d, 0x77, 0xb5, 0x24, 0xca, 0x71, 0xa9, 0x93, 0x1f, 0x09, 0xa6, 0x32, 0xfb, 0x69, 0xd1, 0xc0, 0xe6, 0x55, 0x70 },
{ 0xa7, 0x89, 0x64, 0x24, 0xe4, 0x2d, 0xcc, 0x72, 0x52, 0xbb, 0x22, 0x97, 0x6b, 0xa3, 0x03, 0x27, 0x27, 0x71, 0x81, 0xc2, 0x9f, 0x3a, 0x82, 0x4f, 0x53, 0xd6, 0x36, 0x8a, 0x15, 0xbb, 0xb3, 0xd4 } },
{ "5H11a", "iBoot.n72ap.RELEASE.img3",
{ 0x67, 0x2e, 0x63, 0x58, 0x7f, 0x8c, 0x68, 0x75, 0x53, 0x74, 0x9f, 0x4e, 0xfc, 0x5a, 0x81, 0x28, 0xb6, 0xbb, 0xc9, 0x22, 0x5e, 0x8e, 0x1f, 0x79, 0x55, 0x0a, 0x08, 0x5b, 0x1b, 0x1f, 0x2d, 0xbf },
{ 0xf7, 0xb0, 0x95, 0xd2, 0x91, 0x50, 0x1e, 0x74, 0x09, 0x34, 0x16, 0xfc, 0x24, 0x53, 0xa9, 0x10, 0xec, 0x5a, 0xab, 0xc0, 0xa5, 0x4a, 0x26, 0x8c, 0xaf, 0x0e, 0x5d, 0xab, 0x33, 0x72, 0x20, 0xee } },
{ "5H11a", "needservice.s5l8720x.img3",
{ 0x2c, 0x16, 0xf3, 0x74, 0x74, 0xe9, 0x31, 0xeb, 0x8d, 0x39, 0x99, 0x83, 0x49, 0xdc, 0xed, 0x26, 0x2c, 0xdd, 0xcd, 0xe2, 0xdf, 0x8e, 0xac, 0x44, 0xb1, 0xa1, 0x53, 0x21, 0x09, 0x86, 0xfb, 0x93 },
{ 0x03, 0x64, 0xcd, 0x10, 0x38, 0x40, 0x32, 0x21, 0x69, 0x6b, 0x03, 0xb2, 0x2a, 0xfb, 0x3e, 0xfc, 0xea, 0x13, 0xaf, 0x63, 0x4f, 0x6b, 0x74, 0xe7, 0x7b, 0x4c, 0x7d, 0x5e, 0x54, 0xa4, 0xe5, 0xec } },
{ "5H11a", "recoverymode.s5l8720x.img3",
{ 0x8c, 0xc8, 0x33, 0x07, 0x77, 0xca, 0xb7, 0x06, 0x9a, 0x05, 0xde, 0xa1, 0xc9, 0xc2, 0x1c, 0x5f, 0x3d, 0xab, 0x4f, 0x6d, 0xfa, 0xb5, 0x6f, 0x43, 0x08, 0xed, 0x6f, 0x3d, 0x19, 0x27, 0x11, 0x89 },
{ 0x0e, 0x72, 0xab, 0x37, 0x42, 0xf1, 0xb1, 0x43, 0xa5, 0x1d, 0x8c, 0x77, 0xbb, 0x92, 0xa8, 0x71, 0x22, 0xef, 0x10, 0xb6, 0x9b, 0xd3, 0x2b, 0xd5, 0xb0, 0x96, 0xc3, 0x1d, 0x92, 0xf3, 0x4c, 0x22 } },
{ "5H11a", "kernelcache.release.s5l8720x",
{ 0x1d, 0x5b, 0xa9, 0x98, 0x7f, 0x34, 0xb0, 0x7d, 0x2d, 0xd1, 0xde, 0x5c, 0x27, 0x60, 0xa9, 0x47, 0x12, 0x8f, 0xa9, 0xaa, 0x52, 0x29, 0xd3, 0x2a, 0x01, 0xfc, 0x15, 0x7a, 0x7a, 0xc8, 0xdf, 0xa5 },
{ 0xca, 0x0a, 0x9c, 0x1f, 0xaf, 0x45, 0xf1, 0x0e, 0x47, 0x6a, 0xbe, 0x71, 0x17, 0x83, 0xbb, 0x96, 0x9e, 0x47, 0x0c, 0x53, 0x6e, 0x67, 0xe4, 0xf2, 0x50, 0x33, 0x8e, 0xf4, 0xe5, 0x69, 0x69, 0x84 } },
};
#endif
+43
View File
@@ -0,0 +1,43 @@
#ifndef IPOD_TOUCH_GPIO_H
#define IPOD_TOUCH_GPIO_H
#include <math.h>
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/sysbus.h"
#define TYPE_IPOD_TOUCH_GPIO "ipodtouch.gpio"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchGPIOState, IPOD_TOUCH_GPIO)
#define GPIO_BUTTON_POWER 0xC02
#define GPIO_BUTTON_HOME 0xC01
#define GPIO_BUTTON_VOLUP 0x902
#define GPIO_BUTTON_VOLDOWN 0xC00
#define GPIO_FORCE_DFU 0xB03
#define GPIO_BUTTON_POWER_IRQ 0x7A
#define GPIO_BUTTON_HOME_IRQ 0x79
#define GPIO_BUTTON_VOLUP_IRQ 0x78
#define GPIO_BUTTON_VOLDOWN_IRQ 0x62
#define NUM_GPIO_PADS 0xD /* Ports 0 à C inclus, dont Home et Power. */
#define NUM_GPIO_PINS 0x20
#define GPIO2PIN(gpio) ((gpio) & 7)
#define GPIO2PAD(gpio) (((gpio) >> 8) & 0xFF)
#define GPIOADDR2PAD(addr) (addr - 0x4) / 0x20
typedef struct IPodTouchGPIOState
{
SysBusDevice parent_obj;
MemoryRegion iomem;
uint32_t gpio_state[NUM_GPIO_PADS];
} IPodTouchGPIOState;
bool gpio_is_on(uint32_t *state, uint32_t gpio);
bool gpio_is_off(uint32_t *state, uint32_t gpio);
void gpio_set_on(uint32_t *state, uint32_t gpio);
void gpio_set_off(uint32_t *state, uint32_t gpio);
#endif
+39
View File
@@ -0,0 +1,39 @@
#ifndef HW_ISL29003DL_H
#define HW_ISL29003DL_H
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/sysbus.h"
#include "hw/i2c/i2c.h"
#include "hw/irq.h"
#include "time.h"
#define TYPE_ISL29003DL "isl29003dl"
OBJECT_DECLARE_SIMPLE_TYPE(ISL29003DLState, ISL29003DL)
#define LIGHTSENSOR_COMMAND 0x00
#define LIGHTSENSOR_CONTROL 0x01
#define LIGHTSENSOR_IT_HI 0x02
#define LIGHTSENSOR_IT_LO 0x03
#define LIGHTSENSOR_LSB_SENSOR 0x04
#define LIGHTSENSOR_MSB_SENSOR 0x05
#define LIGHTSENSOR_LSB_TIMER 0x06
#define LIGHTSENSOR_MSB_TIMER 0x07
typedef struct ISL29003DLState {
I2CSlave i2c;
uint32_t cmd;
uint32_t command;
uint32_t ctrl;
uint32_t curreg;
uint32_t ready;
uint32_t it_hi;
uint32_t it_lo;
uint32_t lsb_sensor;
uint32_t msb_sensor;
uint32_t lsb_timer;
uint32_t msb_timer;
} ISL29003DLState;
#endif
+42
View File
@@ -0,0 +1,42 @@
#ifndef IPOD_TOUCH_LCD_H
#define IPOD_TOUCH_LCD_H
#include <math.h>
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/sysbus.h"
#include "hw/irq.h"
#include "hw/arm/ipod_touch_multitouch.h"
#define TYPE_IPOD_TOUCH_LCD "ipodtouch.lcd"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchLCDState, IPOD_TOUCH_LCD)
#define LCD_REFRESH_RATE_FREQUENCY 10
typedef struct IPodTouchLCDState
{
SysBusDevice parent_obj;
MemoryRegion *sysmem;
MemoryRegion iomem;
QemuConsole *con;
IPodTouchMultitouchState *mt;
int invalidate;
uint8_t brightness;
MemoryRegionSection fbsection;
qemu_irq irq;
uint32_t lcd_con;
uint32_t w1_display_resolution_info;
uint32_t w1_framebuffer_base;
uint32_t w1_hspan;
uint32_t w1_display_depth_info;
uint32_t render;
QEMUTimer *refresh_timer;
} IPodTouchLCDState;
void lcd_changebrightness(int brightness);
#endif
+35
View File
@@ -0,0 +1,35 @@
#ifndef HW_LIS302DL_H
#define HW_LIS302DL_H
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/sysbus.h"
#include "hw/i2c/i2c.h"
#include "hw/irq.h"
#define TYPE_LIS302DL "lis302dl"
OBJECT_DECLARE_SIMPLE_TYPE(LIS302DLState, LIS302DL)
#define ACCEL_WHOAMI 0x0F
#define ACCEL_OUT_X 0x29
#define ACCEL_OUT_Y 0x2B
#define ACCEL_OUT_Z 0x2D
#define ACCEL_CTRL_REG1 0x20
#define ACCEL_CTRL_REG2 0x21
#define ACCEL_CTRL_REG3 0x22
#define ACCEL_WHOAMI_VALUE 0x3B
typedef struct LIS302DLState {
I2CSlave i2c;
uint32_t cmd;
uint32_t out_x;
uint32_t out_y;
uint32_t out_z;
uint16_t ctrl_reg1;
uint16_t ctrl_reg2;
uint16_t ctrl_reg3;
} LIS302DLState;
#endif
+19
View File
@@ -0,0 +1,19 @@
#ifndef HW_ARM_IPOD_TOUCH_MBX_H
#define HW_ARM_IPOD_TOUCH_MBX_H
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#define TYPE_IPOD_TOUCH_MBX "ipodtouch.mbx"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchMBXState, IPOD_TOUCH_MBX)
typedef struct IPodTouchMBXState {
SysBusDevice busdev;
MemoryRegion iomem1;
MemoryRegion iomem2;
uint64_t addr;
bool alreadypatched;
} IPodTouchMBXState;
#endif
+37
View File
@@ -0,0 +1,37 @@
#ifndef IPOD_TOUCH_MIPI_DSI_H
#define IPOD_TOUCH_MIPI_DSI_H
#include <math.h>
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/sysbus.h"
#include "hw/irq.h"
#define TYPE_IPOD_TOUCH_MIPI_DSI "ipodtouch.mipidsi"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchMIPIDSIState, IPOD_TOUCH_MIPI_DSI)
#define REG_CLKCTRL 0x08
#define DSIM_CLKCTRL_TX_REQUEST_HSCLK (1u << 31)
#define REG_INTSRC 0x2C
#define REG_PKTHDR 0x34
#define REG_RXFIFO 0x3C
#define REG_FIFOCTRL 0x44
#define DSIM_RSP_LONG_READ 0x1A
#define rDSIM_FIFOCTRL_EmptyHSfr 0x400000
#define rDSIM_STATUS_TxReadyHsClk 0x400
#define rDSIM_INTSRC_RxDatDone 0x00040000
typedef struct IPodTouchMIPIDSIState
{
SysBusDevice parent_obj;
MemoryRegion iomem;
qemu_irq irq;
uint32_t pkthdr_reg;
uint32_t clkctrl_reg;
bool return_panel_id;
} IPodTouchMIPIDSIState;
#endif
Loaded 100 of 120 files, more files were not shown because too many files have changed in this diff. Show more