Skip to content

dedupe glow/spin, fix disk-image bugs, add ci chd-sync, bump deps - #85

Merged
techomancer merged 7 commits into
techomancer:mainfrom
danifunker:misc-fixes
Aug 19, 2026
Merged

dedupe glow/spin, fix disk-image bugs, add ci chd-sync, bump deps#85
techomancer merged 7 commits into
techomancer:mainfrom
danifunker:misc-fixes

Conversation

@danifunker

Copy link
Copy Markdown
Contributor

Dependency hygiene

  • glow: iris pinned 0.13 while eframe pulled 0.17, so both compiled into iris-gui. Bumped iris to 0.17 and adapted the call sites -- tex_image_2d takes PixelUnpackData now, and PixelUnpackData/PixelPackData::Slice wrap an Option as of glow 0.14.
  • spin: iris asked for 0.12 while flume (via nokhwa) pins ^0.9.8. Moved to 0.9 to unify; usage is two spin::Mutex imports with an identical API.
  • block 0.1.6 declares a static of an uninhabited type, a future hard error (rust#74840). Its final release was 2016, so upstream will never fix it -- vendored under third_party/ with Class made opaque-but-inhabited, the same shape its maintained successor block2 uses. Also clears the future-incompatibility report.
  • eframe/egui 0.35 -> 0.36.1 (no source changes; still resolves winit 0.30.13, so the vendored App Store SkyLight patch and the glow 0.17 dedupe both still hold), rtrb 0.3 -> 0.4, plus cargo update (cpal, pcap, ureq, icu_*, ...). Deliberately held back: glow 0.18 and spin 0.12 would re-split the graph, region 4.0 duplicates against cranelift-jit's ^3, libc 1.0 is a pre-release.

iris-gui

  • Browse opened the OS default folder rather than the disk's own. Two causes: scsi_menu's pick_disk/pick_iso never called set_directory at all, and the Disks tab's "Attach..." seeded a bare relative "scsiN.raw" whose parent is "", so path_row's guard fell through. Both now seed from the current path and fall back to the managed disks dir.
  • Create-disk used File::create, which truncates, and pre-fills the filename with the disk already attached at that SCSI id -- so a second visit to the dialog silently erased an installed image. It now warns with the existing file's size and gates Create behind an explicit erase checkbox.
  • Port forwards: added SSH (2222->22) and VNC (5901->5900; host side dodges macOS Screen Sharing, which owns 5900). Telnet 2323->23 already existed.

CI

  • iris-ci chd-sync [ID|all] folds pending CHD diffs back into their bases, refusing to run while the CPU is up -- folding releases the disk backend, which would yank the drive out from under a live guest.
  • iris-ci quit --sync-chd [ID|all] stops the machine and folds before exit, mirroring the GUI's flatten-on-exit ordering.
  • sync_chd_disks gained an only: Option<usize> selector; None means all. Fold commands use a 3h client read timeout, since recompressing a base routinely outruns the 300s default.

Warning-free across 27 iris and 8 iris-gui feature combinations plus the release profile; 360 + 28 tests pass.

danifunker and others added 7 commits August 18, 2026 20:51
Dependency hygiene
- glow: iris pinned 0.13 while eframe pulled 0.17, so both compiled into
  iris-gui. Bumped iris to 0.17 and adapted the call sites -- tex_image_2d
  takes PixelUnpackData now, and PixelUnpackData/PixelPackData::Slice wrap
  an Option as of glow 0.14.
- spin: iris asked for 0.12 while flume (via nokhwa) pins ^0.9.8. Moved to
  0.9 to unify; usage is two spin::Mutex imports with an identical API.
- block 0.1.6 declares a static of an uninhabited type, a future hard error
  (rust#74840). Its final release was 2016, so upstream will never fix it --
  vendored under third_party/ with Class made opaque-but-inhabited, the same
  shape its maintained successor block2 uses. Also clears the
  future-incompatibility report.
- eframe/egui 0.35 -> 0.36.1 (no source changes; still resolves winit
  0.30.13, so the vendored App Store SkyLight patch and the glow 0.17 dedupe
  both still hold), rtrb 0.3 -> 0.4, plus cargo update (cpal, pcap, ureq,
  icu_*, ...). Deliberately held back: glow 0.18 and spin 0.12 would re-split
  the graph, region 4.0 duplicates against cranelift-jit's ^3, libc 1.0 is a
  pre-release.

iris-gui
- Browse opened the OS default folder rather than the disk's own. Two causes:
  scsi_menu's pick_disk/pick_iso never called set_directory at all, and the
  Disks tab's "Attach..." seeded a bare relative "scsiN.raw" whose parent is
  "", so path_row's guard fell through. Both now seed from the current path
  and fall back to the managed disks dir.
- Create-disk used File::create, which truncates, and pre-fills the filename
  with the disk already attached at that SCSI id -- so a second visit to the
  dialog silently erased an installed image. It now warns with the existing
  file's size and gates Create behind an explicit erase checkbox.
- Port forwards: added SSH (2222->22) and VNC (5901->5900; host side dodges
  macOS Screen Sharing, which owns 5900). Telnet 2323->23 already existed.

CI
- iris-ci chd-sync [ID|all] folds pending CHD diffs back into their bases,
  refusing to run while the CPU is up -- folding releases the disk backend,
  which would yank the drive out from under a live guest.
- iris-ci quit --sync-chd [ID|all] stops the machine and folds before exit,
  mirroring the GUI's flatten-on-exit ordering.
- sync_chd_disks gained an `only: Option<usize>` selector; None means all.
  Fold commands use a 3h client read timeout, since recompressing a base
  routinely outruns the 300s default.

Warning-free across 27 iris and 8 iris-gui feature combinations plus the
release profile; 360 + 28 tests pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds the volume directory (0x048, 15 x 16 bytes) to the SGI volume header
writer so an image can carry named standalone files the PROM boots by name
(boot -f dksc(0,<id>,8)<name>), plus a build/dump CLI.

- VolumeHeader: parse/mutate a 512-byte header, checksum refreshed after
  every mutation; voldir + partition-table accessors.
- build_image(): raw image from an ImageSpec (voldir files, partition
  entries, optional raw partition contents); slot 8 sized to cover the files.
- mkvh build/dump. Dump validated against media IRIX wrote — an installed
  6.5 disk lists ide/sash at the right lbn with a valid checksum; see
  rules/testing/sgi-volume-directory-layout-verified-on-real-media.md.

create_scratch_image() behaviour is unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Validated against the IRIX 6.5.22 Installation Tools CD, which mkvh dump now
reproduces exactly (4 voldir entries, 3 partitions, valid csum):

- Partition 8 must span every file the voldir points at — 48736 blocks on that
  CD, not the 8-block scratch minimum. build_image() sizes it from the placed
  files, and PartitionSpec::first_block is now Option: None starts a partition
  where the volume header ends, as the CD does (partition 7 at block 48736).
- An EFS filesystem partition is type 5 (PT_SYSV), not type 7 (PT_EFS) — the
  EFS superblock is genuinely there (fs_magic 0x00072959, fs_size 1280320).
  mkvh's --part help says so.
- dump prints rootpt/swappt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Load a static ELF32 MSB binary straight into RAM and jump to its entry point,
so a bare-metal test binary can be run without building a disk image.

- src/elf.rs: static ELF32 MSB ET_EXEC/EM_MIPS reader. Rejects ELF64, LSB,
  ET_DYN and dynamic binaries by name; sign-extends 32-bit vaddrs so KSEG0
  works in both addressing modes.
- Monitor `loadelf <file>` / `loadbin <file> <addr>`, and `--load-elf FILE`.
- Writes go through the CPU's virtual-address path, then the loaded range is
  writeback-invalidated out of L1D/L2 and invalidated out of L1I, so a stale
  decoded instruction can't run. Loading requires a stopped CPU, which is what
  makes the v1 JIT safe (its CodeCache dies with the CPU thread); jitv2
  self-invalidates from the per-page generation counter.

Pre-POST RAM: MEMCFG0/1 are 0 at reset, so RAM is unmapped and UnmappedRam
swallows writes silently. Two fixes: every load probes the bus (not the cache,
which would absorb the probe) at both ends of the range and refuses with an
actionable message if nothing is mapped; and --load-elf programs the banks the
way POST does first, so a cold start with no PROM works.

Verified: a hand-assembled ELF loaded by --load-elf on a cold machine printed
over the SCC and spun; reloading a different binary at the same address from
the monitor ran the new code (new serial output and marker word).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The PROM has a complete BOOTP + TFTP client and IRIS already answers BOOTP,
so `boot -f bootp()<file>` only lacked a TFTP server. Adds a read-only one,
default-off, on UDP 69 at the gateway address.

- src/tftp.rs: pure RFC 1350 state machine (no sockets), driven by the NAT the
  way nfsudp.rs is. RRQ only; WRQ answered with ERROR 2; ERROR 1 for a missing
  file; 512-byte ACK-driven DATA; a short block ends the transfer, including
  the zero-length one after a file that is an exact multiple of 512; duplicate
  ACKs never advance; last DATA is retransmitted on timeout, bounded retries
  then the transfer is dropped.
- Octet only, case-insensitive; netascii is refused rather than silently served
  raw. The embedded PROM image only ever sends "octet".
- Paths resolve under the configured root; absolute paths, `..` and symlinks
  leading out of it are refused. Nothing ever opens for writing.
- Enabled by `[network] tftp_dir` or --tftp-dir; unset means no TFTP at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A default-off MMIO device (--test-device) so a bare-metal test binary can
report to the host without the SCC: SIGNATURE (so the guest can detect it and
fall back to SCC-only on real hardware), PUTC (byte to stdout), DUMP (full
machine state to JSON, the written value tagging the dump point) and EXIT
(terminate with the guest's exit code).

Mapped at GIO expansion slot 0 (0x1F400000), which is empty on a stock Indy
and otherwise answers with a GIO timeout, so it displaces no real hardware —
graphics are at 0x1F000000, slot 1 at 0x1F600000, MC/HPC3/PROM higher still.
Enabling it together with the ultra64 dev board, which claims the same slot,
is refused. Disabled (the default) the range reads 0xFFFFFFFF and swallows
writes exactly as before.

The dump carries gpr[32], hi/lo, pc, the CP0 registers by name, fpr[32] plus
FCSR/FIR, and identifies R4400 vs R5000 from PRId; values were checked against
the monitor's own regs/cop0/cop1. State round-trips through snapshot
save/load (unit test plus an end-to-end save/load).

Also --cheritest-dump-hook: a guest write to CP0 26 triggers the same dump, for
the external cheritest corpus. Strictly opt-in and verified inert by default —
CP0 26 is ECC on a real R4400 and the PROM writes it during cache init.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- docs/cpu-test-support.md: how the four features are used together, and which
  channel carries what back to a CI harness.
- rules/testing/loading-code-before-post-ram-is-unmapped.md: MEMCFG0/1 are 0 at
  reset so nothing is mapped, UnmappedRam swallows the write, and a cached probe
  can't see it. Plus Indy RAM at 0x88000000+ and the KSEG1 masking trap.
- rules/testing/scc-serial-output-from-bare-metal-code.md: IOC serial 1 is SCC
  channel B (8881), TX needs WR5.TX_ENABLE, and the TCP backend's one-client
  behaviour silently eats output.
- NAT-level TFTP tests: a guest RRQ frame in, DATA frames from the gateway out.
- testdev: the PROM's slot-0 probe is at 0x1F46A07C, outside the decoded window.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@techomancer
techomancer merged commit 66d8f98 into techomancer:main Aug 19, 2026
1 check passed
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.

2 participants