Skip to content

firmware: .ipod transport-format packaging (Phase 1 PR #2) - #42

Open
BrandonDedolph wants to merge 7 commits into
phase1/hw-build-skeletonfrom
phase1/image-packaging
Open

firmware: .ipod transport-format packaging (Phase 1 PR #2)#42
BrandonDedolph wants to merge 7 commits into
phase1/hw-build-skeletonfrom
phase1/image-packaging

Conversation

@BrandonDedolph

Copy link
Copy Markdown
Owner

Summary

Builds the host-side .ipod packager that turns the ARM ELF from #40 into a flashable artifact. Stacked on phase1/hw-build-skeleton; rebase to main once #40 merges.

  • Format (per core/docs/hw/08-boot-dock.md): [BE32 checksum][4-byte model name "ipvd"][raw image]. Checksum is the additive model-seed + sum-of-bytes, 32-bit wrap. firmware.Checksum (already shipped) does the math; new firmware.WriteIPodFile / ReadIPodFile do the file layout.
  • CLI: core firmware pack <bin> -o <out.ipod> / core firmware unpack <ipod> -o <out.bin>. Reader returns image bytes alongside ErrIPodChecksumMismatch so recovery tools can inspect corrupt input.
  • Build: meson now objcopies the ELF to a flat core.bin as a build_by_default byproduct; make ipod (new target) wraps core.bincore.ipod via go run ./cmd/core firmware pack.
  • Tests: core/cli/internal/firmware/ipodfile_test.go covers header layout, roundtrip, checksum mismatch, truncated header, empty input, empty image. Existing checksum tests already cover wrap-at-2^32.

End-to-end output for the PR #1 spin kernel: 60 image bytes + 8 header bytes = 68-byte .ipod. Checksum verified by core firmware unpack (no ErrIPodChecksumMismatch); unpacked bytes are cmp-identical to the original core.bin.

Test plan

  • cd core && make ipod produces build-hw/core.ipod (68 bytes for the current spin kernel)
  • xxd build-hw/core.ipod | head -1 shows ipvd at bytes 4–7
  • cd core && go run ./cli/cmd/core firmware unpack build-hw/core.ipod -o /tmp/u.bin && cmp /tmp/u.bin build-hw/core.bin — roundtrip clean
  • cd core/cli && go test ./... — all packages green (firmware now includes the .ipod cases)
  • cd core && make sim && meson test -C build-sim — 4/4 green, sim build unaffected

🤖 Generated with Claude Code

BrandonDedolph and others added 7 commits May 11, 2026 23:10
`make hw` now also emits `core.bin` (objcopy -O binary on the ELF);
`make ipod` wraps it as `core.ipod` — the ipodpatcher-style transport
format the host installer / updater pushes to the device. Format is
4-byte BE additive checksum (model seed + sum-of-bytes, 32-bit wrap)
+ 4-byte model name (`ipvd`) + raw image, per
core/docs/hw/08-boot-dock.md.

Bits:

- `firmware.WriteIPodFile` / `ReadIPodFile` in
  core/cli/internal/firmware/. Both are streaming-friendly; the reader
  returns the image bytes alongside ErrIPodChecksumMismatch so
  recovery tools can inspect corrupt images.
- `core firmware pack <bin> -o <ipod>` / `core firmware unpack <ipod>
  -o <bin>` subcommands wired into the existing CLI tree. Roundtrip
  verified end-to-end against the actual ARM ELF from PR #1.
- Meson custom_target invokes the cross-toolchain objcopy on the ELF
  to produce the flat binary as a build_by_default byproduct, so
  `core.bin` is always available next to `core.elf`.
- Makefile gains `make ipod` (depends on hw). It shells out via
  `go run ./cmd/core firmware pack` for now; once the CLI ships as a
  release binary we'll switch to that.

Test coverage in core/cli/internal/firmware/ipodfile_test.go:
header layout, roundtrip, checksum mismatch (returns bytes anyway),
truncated header, empty input, empty image. Existing checksum tests
already cover the wrap-at-2^32 case.

Sim build + all 4 suites unaffected (codec-kat, tcdb-reader, tag-mp3,
sim-audio-playback).

Stacked on phase1/hw-build-skeleton (#40).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three changes:

- Status table now reflects that Phase 1 has started: drops a new
  "Phase 1 hw bringup" row on the working-today side (cross-build +
  .ipod packaging) and softens the pending side from "Bootable ARM
  image (Phase 1)" to "Boots on hardware (UART → LCD → scheduler)" —
  the more honest description of what's still missing now that the
  build pipeline exists.
- Adds a "Quick start (hardware build)" section with `make hw` /
  `make ipod` and a readelf sanity check. Calls out that the kernel
  still spins, so flashing is gated on the install flow landing.
- Adds a "Firmware image packaging" section mirroring the existing
  tagcache one — `core firmware pack` / `unpack` examples and a link
  to the format spec.

Also drops the literal "55 commits on main" line since it rotates
fast; replaced with a one-liner that points at STATUS.md.
…ndoff

Review finding (confirmed against core/docs/hw/08-boot-dock.md): the
bootloader hands off with SDRAM at its native 0x10000000, NOT remapped
to 0x00000000 as crt0.S claimed — so an image linked at 0x0 would crash
on its first literal-pool load on real hardware.

crt0 now copies a self-contained remap stub to IRAM, programs
MMAP0_PHYSICAL/MMAP0_LOGICAL from there (so the remap doesn't pull the
rug out from under the executing code), and enters the remapped image
via a literal inside the copied stub. Everything pre-remap is
position-independent (immediates, PC-relative, IRAM addresses only) —
verified by objdump inspection.

Also drops the misleading ".data copy in subsequent PRs" promise: with
VMA == LMA and the whole image RAM-loaded, .data arrives in place and
no copy is ever needed.

TODO before first hardware boot: verify the MMAP0 flags nibble (doc
says 0x0F84; upstream Rockbox crt0-pp.S may use 0x3F84 — looks like a
32 MB vs 64 MB window mask, which matters on the 64 MB 5.5G).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Review findings (confirmed):
- unpack hardcoded ModelIPodVideo (0x05) as the checksum seed, so any
  intact non-Video .ipod (e.g. nano, seed 0x04) reported a false
  checksum mismatch — contradicting the README's "inspect third-party
  images" use case. ReadIPodFile now derives the seed from the embedded
  4-byte model name via ModelNumForName; unknown names get a distinct
  ErrUnknownModelName that still returns name + bytes (same recovery-
  tool philosophy as the existing mismatch path).
- pack/unpack silently overwrote --out via os.Create/os.WriteFile,
  diverging from the CLI's established safety pattern. Both now use
  O_EXCL-by-default with a --force flag, mirroring tagcache build.

New tests: nano round-trip (seed 0x04), unknown-name sentinel,
ModelNumForName mapping. go vet + go test ./... green; pack→unpack→cmp
round-trip and --force gating smoke-verified.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The new O_EXCL-by-default overwrite guard on `core firmware pack` made
a second `make ipod` fail on the previous run's core.ipod. Rebuilding
over your own build artifact is the expected case in a make target.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Verified against Rockbox crt0-pp.S + pp5020.h (master, 2026-06-10):
the window-size mask lives in the LOGICAL register, not the physical
flags — 0x3C00 = 64 MB window (what every Rockbox iPod Video build
writes, 32 MB 5Gs included; RAM size is probed at runtime), 0x3E00 =
32 MB. We were writing 0x00000000: mask bits all clear, a value no
known firmware ever writes. The earlier 0x0F84-vs-0x3F84 suspicion
was aimed at the wrong register — 0x0F84 is confirmed correct for
PP502x (0x3F84 is the PP5002 value); resolved that TODO.

Also matches Rockbox's write order (logical first, physical second)
and corrects 01-soc-pp5022.md: the remap section now documents the
mask semantics, and the memory-map intro no longer claims the
bootloader performs the remap (ipodloader2 restores the Apple-ROM
MMAP state and jumps at native 0x10000000 — confirmed from its
loader.c/interrupts.c — so the remap is the loaded image's job,
matching what crt0.S now does).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The handoff contract says only the CPU runs, but Rockbox parks the
second core at _start defensively — and the clicky emulator's HLE
boot proved why: it enters both cores, and the unparked COP raced the
CPU through BSS-zero and uart_init (doubled every UART character,
interleaved CPU/COP PROCESSOR_ID reads). Same failure mode would
apply on hardware to any loader that wakes both cores.

PROCESSOR_ID low-byte check (0x55 CPU / 0xAA COP), busy-spin park,
immediates + fixed MMIO only so the pre-remap position-independence
invariant holds. The scheduler PR replaces this with a proper
COP_CTL sleep + wake protocol.

Found via the clicky (github.com/daniel5151/clicky) PP emulator,
evaluated 2026-06-11 as a no-hardware regression rig: it models the
MMAP0 remap, the SER0 UART (TX -> stdout), and boots our unmodified
core.bin to the full banner. Repro recipe in STATUS.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant