+ +/// Crowbar v3 Step 2 — optionally install full ctx bytes at `ctx_ptr` +/// from a binary file specified by `XENIA_CROWBAR_CTX_BIN`. Bytes are +/// written verbatim (no byte-swap) via `write_u8` because the file is +/// expected to be a raw guest-endian (big-endian) capture of the ctx +/// layout from canary's runtime memory. Logs the first 16 u32 slots +/// after install for verification. If the env var is unset, this is a +/// no-op so v2 behaviour is preserved exactly. +/// +/// Captured via canary's `audit_68_host_mem_read_probe` cvar — see +/// `xenia-rs/audit-runs/review-a-step1c-crowbar-v3/canary-probe-run1.log`. +/// +/// **Option γ (per v3 brief)**: install verbatim, including canary-VA +/// pointer fields like `[ctx+44]=0xBCE25640`. These VAs may be unmapped +/// in ours's address space — if a worker dereferences one and faults, +/// that confirms the case-(C) recursion is required (v4 work). +fn crowbar_maybe_install_ctx_from_file(mem: &GuestMemory, ctx_ptr: u32) { + let path = match std::env::var("XENIA_CROWBAR_CTX_BIN") { + Ok(p) if !p.is_empty() => p, + _ => { + tracing::warn!( + "CROWBAR: XENIA_CROWBAR_CTX_BIN not set — skipping ctx install \ + (v2 behaviour; only +0/+4/+8/+12 are populated; \ + workers will likely fault on [ctx+44] dispatch)" + ); + return; + } + }; + let bytes = match std::fs::read(&path) { + Ok(b) => b, + Err(e) => { + tracing::error!( + "CROWBAR: failed to read ctx bin {:?}: {} — skipping install", + path, + e, + ); + return; + } + }; + let n = bytes.len().min(256); + for (i, b) in bytes.iter().take(n).enumerate() { + mem.write_u8(ctx_ptr + i as u32, *b); + } + tracing::warn!( + "CROWBAR: installed {} bytes at ctx_ptr={:#010x} from {:?}", + n, + ctx_ptr, + path, + ); + // Verify: log the first 16 u32 slots after install. + for slot in 0..16u32 { + let off = slot * 4; + let v = mem.read_u32(ctx_ptr + off); + tracing::warn!( + "CROWBAR: post-ctx-install ctx[+{:>3}] (={:#010x}) = {:#010x}", + off, + ctx_ptr + off, + v, + ); + } +} + +/// Crowbar entry point — allocate the worker ctx, install the vtable +/// + self-pointer doubly-linked-list head pattern that AUDIT-068 S3 +/// captured, spawn all 4 workers suspended, then resume each one. +/// Returns the number of workers successfully resumed (0..=4). +/// +/// **Reading-error #37 discipline**: the value written at `ctx+0` is the +/// vtable BASE `0x8200A1E8`, NOT the slot-N address `0x8200A208` cited +/// in older audits. Per AUDIT-068 S3 measurement. +pub fn crowbar_force_spawn_workers(state: &mut KernelState, mem: &GuestMemory) -> u32 { + // 0. Crowbar v2 Step 0 diagnostic — dump 256 bytes at the vtable base + // BEFORE doing anything else. Distinguishes case (A) vtable .rdata + // is missing/zero in ours vs case (B) .rdata present but vtable[35] + // is not statically populated (= runtime install needed). Per + // Reading-error #37: 0x8200A1E8 is vtable BASE; slot N is at base+4*N. + // For workers we care about slots 35/36/37/38 (offsets 140/144/148/152). + // Bump dump to 512 bytes (128 slots) so we see vtable[64] which is read + // by the slot-35 callee `sub_82506B08` at +256. + crowbar_dump_vtable_region(mem, CROWBAR_VTABLE_BASE, 512); + + // 1. Allocate ctx struct (one heap page is plenty; the real struct is -- + + // 2c. Crowbar v3 Step 2 — full ctx-bytes install. + // If the cvar `XENIA_CROWBAR_CTX_BIN=` is set AND the file + // exists, read up to 256 bytes from it and write them at ctx_ptr. + // The file should be a raw guest-endian (big-endian) capture of the + // ctx layout — see canary's `audit_68_host_mem_read_probe` cvar. + // The v2 init at +0/+4/+8/+12 above is intentionally retained as a + // fallback when the env var is unset; the file install overwrites + // those four slots verbatim (the bytes match the v2 pattern). + // + // **Option γ (per v3 brief)**: canary-VA pointer fields like + // `[ctx+44]=0xBCE25640` are written as-is even if unmapped in + // ours — diagnostic intent is to OBSERVE the fault PC, not avoid + // it. + crowbar_maybe_install_ctx_from_file(mem, ctx_ptr); + + // 3. Spawn the 4 workers suspended (matching canary jitter sample). + let mut handles: [u32; 4] = [0; 4]; + let mut spawned = 0u32; + for (i, entry) in CROWBAR_WORKER_ENTRIES.iter().enumerate() { + if let Some(h) = crowbar_spawn_one_worker(state, mem, *entry, ctx_ptr) { + handles[i] = h; + spawned += 1; + } + } + + // 4. Resume each spawned worker directly through the scheduler.