Skip to content

iPod touch 2G — iOS 3.1.3

Latest

Choose a tag to compare

An iPod touch 2G (S5L8720) running iOS 3.1.3 (7E18). It boots to a usable home
screen, runs decrypted apps and real OpenGL ES games, plays sound (kinda), reaches the
network, and takes a debugger.

Just want to use it?

Download iPod-touch-2G-macOS.zip (249 MB). It is a notarized macOS app
with the emulator and the device inside it — no terminal, no Homebrew, nothing
to build. Unzip, drag to Applications, open.

Everything is in the Device menu: Home, Lock, volume, rotation, shake,
Install App…, Open Terminal (SSH), and Erase All Content and Settings.

It needs macOS 12 or later.

Six things worth knowing before you start:

  • Launch it twice the first time. The first launch unpacks the device and
    then stops. Open it again and you get the emulator. Every launch after that
    is a single one.
  • A frozen, unresponsive screen is usually just sleep. Sometimes it boots up and immediately sleeps.
    Press Cmd+L and see if it wakes up before assuming it has hung.
  • Join the WiFi yourself, in Settings ▸ Wi-Fi. The network is there; the
    device does not auto-join it.
  • Find apps to install on Legacy Store (shameless self promo).
    Apps must be decrypted and should support iOS 3.1.3 or lower.
    • If, on the first run, apps show their splash screen and then crash, you may need to restart the emulator. This is a one-time thing.
  • The on-screen keyboard mishandles Shift and the symbol keys. Letters and
    numbers are fine.
  • Some apps glitch in their launch and close animations. Known, cosmetic,
    and being worked on.

Building it yourself

Download the images below and verify against SHA256SUMS.
Setup guide →

Asset What it is
bootrom_240_4 S5L8720 bootrom, unchanged from upstream
nor_7E18.bin 3.1.3 NOR, SHSH wrapped for the emulated device
nand-canonical.tar.gz Clean 3.1.3 image
nand-appsync3.tar.gz Same, plus the patches that let decrypted apps launch

nand-appsync3 boots with its code-signing gates open — that is what lets
decrypted apps launch and lldb attach, and it means it is not a faithful
device. Neither NAND image is a dump; both are built from
iPod2,1_3.1.3_7E18_Restore.ipsw, and imgtools/build_nand.py reproduces them
from an IPSW you supply.

Works

  • Boots to an interactive home screen, and reboots
  • Touch, multitouch (pinch-to-zoom), the on-screen keyboard, rotation
  • WiFi — Safari renders real pages over the emulated BCM4325
  • Decrypted third-party apps install over USB and launch
  • Real OpenGL ES games. Guest GL traps to the host over a cp15 escape hatch
    and renders with a real OpenGL context, then goes to CoreAnimation through
    the IOSurface present path — so an app's GL is composited under ordinary
    UIKit views. The MBX is not on the path at all.
  • Root ssh over USB; lldb on guest processes
  • The host clipboard writes into the guest pasteboard
  • Save and restore a running machine — 86 MB in ~1.1 s
  • tests/ipod/run-regression.sh, a harness that asserts on what is on the
    panel rather than that the process is alive

Doesn't, yet

  • 3D is partial. One known app reaches a state where the panel keeps
    compositing a stale frame. The missing geometry is provably in the GL colour
    buffer and provably absent from the panel, and CA accepts every frame it is
    handed (ok=3600, failed=0) — so the signalling layer is ruled out by
    measurement. Pacing also judders, while the guest sits at 3.5% CPU.
  • Audio replays a fragment of a clip ~0.37 s later, one period of the
    guest's DMA ring. Ringtones (AAC, not raw PCM) are unexamined.
  • The .ipa install placeholder icon appears, but its progress bar never
    fills.
  • Encrypted App Store binaries cannot run.
  • A cosmetic stall in the app launch animation.

New since the last release

  • Games render, via the IOSurface present path and a GLES bind fix — a bind CA
    refused was destroying the drawable that was working.
  • An audit of all ~34 device models, and its first two sweeps: guest register
    values that reached host memory as unchecked indices or sizes are now bounded.
  • Multitouch reports at 60 Hz instead of 10 Hz.
  • imgtools/klog.py reads the guest kernel log out of RAM; the serial port only
    ever shows you iBoot.

History note. The QEMU v8.2.0 → v10.0.2 rebase is now a single commit
instead of 10943 upstream ones, so fork comparisons report a meaningful number.
The original graph is preserved at tag pre-squash-2026-08-04 and branch
qemu-upstream-history — use those for git blame on QEMU's own files.