Storage¶
The disk is asynchronous, by design¶
The two ATA tasks -- ATAWorkLoopTask (id 0x20, priority 0x33) and ATAWorkLoopIRQTask (id
0x21, priority 9) -- own bulk disk work, and they block rather than spin.
So the filesystem never calls the disk. It posts to a queue and waits. This is not a design
Chikuma chose; it is the one in the image, and it has a practical consequence for anyone tracing:
a stack walk at the ATA command register write can never name the caller, because the caller is in a
different task by construction. Trace the command write, not the data fetch -- DMA completes long
after the driver returned, so at the data fetch lr reads zero and the stack holds nothing useful.
The layers¶
HFS / HFS+ reader FAT16 reader
------------------------------------------
partition layer (APM, MBR)
------------------------------------------
ATA driver (LBA28 and LBA48)
HFS and HFS+ are one driver, because they are one driver in the firmware.
Sector size, which has bitten this project¶
On the iPod's disk, MBR partition LBAs are in 4096-byte units, not 512. A test image built on the 512-byte assumption does not fail loudly; it produces offsets that are wrong by a factor of eight and land on plausible-looking data.
Struct offsets are tested, not remembered¶
Two mistakes made here, both from trusting memory over the specification:
- HFS+ fork data at
0x70/0xB8instead of0x70/0xC0/0x110; - the B-tree header's
nodeSizeread at+10, which isfirstLeafNode, instead of+18.
Neither produced an error. Both produced numbers that looked fine and made every later read land nowhere.
The fix is the rule the project now uses generally: assert that numbers are physically possible. A zero-byte file spanning four million blocks fails a test instantly and costs nothing to check.
Tests run against a real volume¶
Host tests read retailos_re/build/ipod_apm.img -- an actual APM disk with a FAT16 firmware
partition and an HFS+ data partition -- and the real resource archive, never a fixture written to
match the parser. That is what caught both offset bugs above, in seconds each.
The music database¶
The library is Apple's iTunesDB, not a filesystem walk, and its readers are recovered: the track
parser (mhlt → mhit → mhod), the playlist parser (mhlp → mhyp → mhod → mhip), the MHSD
dispatcher, and the artwork database with its .ithmb thumbnail atlases. Genius is a separate
SQLite database, which is why SQLite is in scope.
A real bug in Apple's firmware¶
The firmware builds its track lookup index by walking the parsed track list in file order and never sorting it, then binary-searches that index. A binary search over an unsorted array silently fails for some entries, and the playlist parser skips any entry whose lookup fails -- so those tracks vanish from every browse screen while still being counted on the main screen. A neighbouring function doing the same job qsorts its index first; this one does not.
In practice iTunes always wrote track lists sorted by id ascending (9,671 tracks, zero inversions), so the bug rarely fires on a device synced the normal way. Any replacement firmware should sort the index or use a hash map, and the whole class of failure disappears.
This is the kind of finding that argues for the project: you cannot fix what you cannot read.
Known gap¶
The emulator's ATA model has no write path -- firmware writes are dropped. That is the first emulator gap worth closing, because it blocks a subsystem rather than a screen.