# DECODE_LOG — empirical notes for the crafted-SRAT rig (DF-5649/DF-5650)

Everything below was learned live on the audit QEMU guest (DragonFly
6.5-DEVELOPMENT, kernel built Thu Jul 2 06:02:54 2026, X86_64_GENERIC).
Read this before touching the blob generator.

## 1. ACPICA SRAT structs are PACKED (#pragma pack(1))

`sys/contrib/dev/acpica/source/include/actbl3.h:194` opens a `#pragma
pack(1)` region that contains all SRAT structures (closes at :1042). The
runtime layout (empirically decoded, see §3) is therefore the ACPI-spec
byte layout, NOT the C-naturally-aligned layout:

    ACPI_SRAT_CPU_AFFINITY (16 bytes):
      Type u8@0, Length u8@1, ProximityDomainLo u8@2, ApicId u8@3,
      Flags u32@4, LocalSapicEid u8@8, ProximityDomainHi[3]@9, ClockDomain u32@12

    ACPI_SRAT_MEM_AFFINITY (40 bytes):
      Type u8@0, Length u8@1, ProximityDomain u32@2, Reserved u16@6,
      BaseAddress u64@8, Length u64@16, Reserved1 u32@24,
      Flags u32@28, Reserved2 u64@32

Our first generator used naturally-aligned offsets (Prox@4, Base@16,
Len@24, Flags@36, size 48) — every crafted MEMORY entry was then silently
skipped (its "Flags" read hit our BaseAddress marker bytes, bit0 clear),
while the walk still iterated and advanced correctly. This produced hours
of apparent "silent parser" confusion. Fix: use the packed offsets above.

## 2. Blob placement

- 0x40000000 (free RAM per SMAP): planted by `-device loader` at reset and
  observed intact from SeaBIOS through `SI_SUB_CONFIGURE` (pmemsave dumps
  at loader-menu time, wedge time, and DSDT-trap time all matched). 10+/10
  reliable.
- 0xbffdd000 (inside the SMAP type=02 hole below 1GB): ZEROED by SeaBIOS's
  own table/allocation zone before the loader runs. Do not use.
- The MADT family rigs (DF-5430..) only need the blob to survive to
  `SI_BOOT2_PRESMP`; the SRAT parser runs later (`SI_BOOT2_NUMA =
  0x1a5c000`), so "worked for MADT" proves nothing for SRAT.

## 3. How the packed layout was proven (transcript excerpts)

1. `fireworks` (one entry per Type 0..14, all-0xFF bodies) printed exactly
   one line: `MemAffinity ffffffffffffffff,17592186044415MB Prox=4294967295
   (not found)` — the Type=1 entry; proves the print path works and the
   dispatch constant is 1.
2. `map` (FF@[2,10) + markers 0x1111..@10, 0x2222..@18, 0x3333..@26,
   0x4444..@34, 0x5555..@40):
   `MemAffinity 111111111111ffff,2345624805922MB Prox=4294967295`
   → Base@8 (bytes ff ff + 11*6), Length@16 (bytes 11*2 + 22*6 →
   2345624805922 == 0x2222222222221111 >> 20 exactly), Prox@2 (FF FF FF FF).
3. `bitprobe` (46 entries, Prox@2 = i, single FF byte at 2+i): exactly one
   entry printed — Prox=26, i.e. the FF byte at +28: the gate is
   `mem->mem.Flags` (u32@28) & bit0.
4. The faulting instruction of DF-5650's fault flavor is the gate itself:
   `Stopped at -0x7f3de188: testb $0x1,0x1c(%r12)` — `testb $1, +28(%r12)`.

## 4. Boot-path markers around the bug (stock kernel, this rig)

    line 85  CPU Topology: ...          (SI_BOOT2_CPU_TOPOLOGY 0x1a58000)
    line 86  [MemAffinity lines]        (srat_probe walk, SI_BOOT2_NUMA 0x1a5c000)
    line 87  vm_page: avg 893 pages per queue, 1020 queues   (finalize)
    ...
    line 331 ACPI: RSDP/RSDT/APIC/SRAT table listing        (acpi0, SI_SUB_CONFIGURE)
    line 351 Fatal trap 12 in AcpiTbLoadNamespace           (rig artifact: no DSDT in the planted chain)

The line-351 trap is the normal END STATE of every healthy boot under this
rig (the planted chain has no DSDT); it is reached ~2s after line 87. A
wedged (hang) run stops at line 85 forever; a faulting (fault3) run traps
at line 88 inside srat_probe instead.

## 5. Misc

- `hint.acpi.0.rsdp` survives only in the loader.conf of the *active* qcow2
  layer; `vm.sh reset with-src` reverts it — re-append before planted runs.
- Boots without the planted blob but with the hint fall back to the mptable
  (8 phantom cores) and then WEDGE during mptable SMP bringup — no ssh.
  To get ssh with the hint present, drive the loader prompt over a pipe
  serial and `unset hint.acpi.0.rsdp` before `boot` (boot_rig.py).
- HAMMER2 compresses /usr/src on the guest disk — kernel .rodata strings
  are findable in a raw disk dump, source file text is not.
