Files
qemu-ios/contrib/ipod-emu/README.md
T

17 KiB
Raw Blame History

Simulateur iPod Touch 2G (iPhone OS 2.2.1)

Émulateur 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

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 :

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.

Redémarrage à chaud

ipod-emu respring -W            # relance SpringBoard, attend puis déverrouille
ipod-emu reset -W               # redémarre iOS dans le même processus QEMU

Les deux commandes synchronisent les écritures avant la relance. Elles conservent la NAND de session et les processus QEMU et ipod-netd. Sans -W, elles rendent la main après la demande ; ipod-emu wait peut attendre la suite. L'attente ne lit que les nouveaux marqueurs du journal, puis vérifie le déverrouillage. Aucun glissement automatique n'est effectué si un code est activé.

La sortie TV émulée répond à chaque soumission, sans l'ancien quota de deux interruptions qui bloquait SpringBoard dans IOServiceClose. Le LCD sépare le masque d'interruptions de leur acquittement. Un reset réinitialise aussi le contrôleur MIPI, l'état du tactile et les interruptions SPI.

Le reset de l'UART ferme la connexion série. ipod-netd se reconnecte au même socket et renégocie LCP/IPCP avec le nouveau pppd. Pour vérifier le réseau :

ipod-agent ping
ipod-agent net                  # ppp0 up et configd publié

ipod-agent respring et ipod-qmp reset restent les accès bruts, sans attente ni mise à jour du point de départ du journal. Avant un reset brut, exécuter ipod-agent sync. Préférer les commandes ipod-emu pour un cycle complet.

Tester nos apps

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

# 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.

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 :

contrib/ipod-emu/tests/run-uart          # reset des registres/FIFO/IRQ et trois reconnexions socket, ASan/UBSan
contrib/ipod-emu/tests/run-display       # LCD, MIPI, sortie TV et resets, ASan/UBSan
python3 contrib/ipod-emu/tests/test-reset-launcher.py  # synchronisation et nouveaux marqueurs
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 :

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 :

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. L'image de base est en français, région France, fuseau Europe/Paris.
  • Relance de SpringBoard : ipod-emu respring -W attend le retour de l'interface et son déverrouillage. push-app garde son redémarrage de QEMU, 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).

Régression native du redémarrage

Avec un firmware local, le test QMP/QTest vérifie trois resets des vrais périphériques d'affichage (CPU arrêté, NAND en lecture seule) :

python3 contrib/ipod-emu/tests/test-display-reset.py --firmware /chemin/images/5H11a

Pour vérifier iOS, préparer et démarrer une session de test séparée, avec un port d'agent distinct. Le test suivant relance cette session, ouvre la calculatrice et conserve les captures et résultats JSON. Il vérifie le déverrouillage, les PID, la persistance d'un fichier témoin et HTTP avec un serveur local temporaire :

python3 contrib/ipod-emu/tests/test-display-session.py \
  --emu-home /chemin/session-test --agent-port 7789 \
  --action respring --cycles 3 --output /chemin/resultats-respring
# Même commande avec --action reset pour le redémarrage complet d'iOS.

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).