diff options
| author | salmonik <salmonik@tuta.io> | 2026-08-04 01:44:05 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-08-04 01:44:05 +0200 |
| commit | 3e2261d83176752f3ea21e0c97f330b4c1757437 (patch) | |
| tree | 8bbc92f917121fafaf193f1af1c42fba624abda9 /rsdos/src/main.rs | |
| parent | v0.5.8: heap-free QR encoder + crash-dump QR on framebuffer (diff) | |
| download | rs-dos-3e2261d83176752f3ea21e0c97f330b4c1757437.tar.gz rs-dos-3e2261d83176752f3ea21e0c97f330b4c1757437.zip | |
v0.5.9: fix OptiPlex heap crash — EBS retry must not rebuild the free list
Root cause: on real hardware SetMode can invalidate the pre-captured
ExitBootServices map key, so EBS fails and the retry refreshed the map
via capture_memory_map -> init_heap, which REBUILT the free list from
the raw map and wiped the allocation state of every pre-EBS live object
(the display shadow and the vt grids). Their spans became free while
still live, later allocations carved inside the shadow, and the display
writes clobbered them — zeroed back-pointers (family A ptr=0x404443e0
block=0x0) and gray glyph pixels (family B 0xaaaaaa00aaaaaa = 0x00AAAAAA
= PALETTE[7] fg-7 text colour, not a firmware scrub).
Fix: refresh_and_exit now re-captures the map and returns a fresh EBS
key without rebuilding the free list (refresh_map_key); the free list is
the authoritative live-allocation state. Verified in QEMU under a forced
EBS failure (SIMULATE_EBS_FAILURE test hook): verify ok, probe NOT free,
flhead outside the shadow — identical to a clean boot, and the full
shell sequence is crash-free.
Also: removed the crash-family debug markers from the screen (boot-time
heapdbg lines, vt verify marker, shadow marker) — the vt grid self-check
stays serial-only. Version 0.5.8 -> 0.5.9.
Diffstat (limited to 'rsdos/src/main.rs')
| -rw-r--r-- | rsdos/src/main.rs | 18 |
1 files changed, 11 insertions, 7 deletions
diff --git a/rsdos/src/main.rs b/rsdos/src/main.rs index 42f95e5..e40098d 100644 --- a/rsdos/src/main.rs +++ b/rsdos/src/main.rs | |||
| @@ -185,7 +185,7 @@ pub unsafe extern "efiapi" fn efi_main(image_handle: Handle, system_table: *mut | |||
| 185 | } else { | 185 | } else { |
| 186 | rsnix::serial_println!("gop: not found (0x{:x})", gop_result); | 186 | rsnix::serial_println!("gop: not found (0x{:x})", gop_result); |
| 187 | } | 187 | } |
| 188 | rsnix::gop::boot_marker("rsnix v0.5.5 booting.."); | 188 | rsnix::gop::boot_marker("rsnix v0.5.9 booting.."); |
| 189 | rsnix::vt::init(); | 189 | rsnix::vt::init(); |
| 190 | let (rows, cols) = rsnix::vt::size(); | 190 | let (rows, cols) = rsnix::vt::size(); |
| 191 | rsnix::serial_println!("vt: terminal ready ({}x{})", rows, cols); | 191 | rsnix::serial_println!("vt: terminal ready ({}x{})", rows, cols); |
| @@ -207,7 +207,15 @@ pub unsafe extern "efiapi" fn efi_main(image_handle: Handle, system_table: *mut | |||
| 207 | } | 207 | } |
| 208 | 208 | ||
| 209 | // ExitBootServices — we now own the machine | 209 | // ExitBootServices — we now own the machine |
| 210 | if unsafe { (bs.exit_boot_services)(image_handle, map_key) } != EFI_SUCCESS { | 210 | // DEBUG (OptiPlex sim): corrupt the first key so EBS fails once and |
| 211 | // the retry path (capture_memory_map -> init_heap rebuild) runs — the | ||
| 212 | // exact condition that wiped the pre-EBS shadow on real hardware. | ||
| 213 | let ebs_first_key = if rsnix::memory::simulate_ebs_failure() { | ||
| 214 | map_key ^ 1 | ||
| 215 | } else { | ||
| 216 | map_key | ||
| 217 | }; | ||
| 218 | if unsafe { (bs.exit_boot_services)(image_handle, ebs_first_key) } != EFI_SUCCESS { | ||
| 211 | rsnix::serial_println!("WARN: ExitBootServices failed, retrying..."); | 219 | rsnix::serial_println!("WARN: ExitBootServices failed, retrying..."); |
| 212 | match unsafe { rsnix::memory::refresh_and_exit(bs, image_handle) } { | 220 | match unsafe { rsnix::memory::refresh_and_exit(bs, image_handle) } { |
| 213 | Ok(_) => {} | 221 | Ok(_) => {} |
| @@ -351,14 +359,10 @@ pub unsafe extern "efiapi" fn efi_main(image_handle: Handle, system_table: *mut | |||
| 351 | // whether the corruption is already present at shell entry or lands | 359 | // whether the corruption is already present at shell entry or lands |
| 352 | // later, and prints the exact field values when it is. | 360 | // later, and prints the exact field values when it is. |
| 353 | rsnix::vt::debug_verify(); | 361 | rsnix::vt::debug_verify(); |
| 362 | |||
| 354 | shell::run(); | 363 | shell::run(); |
| 355 | } | 364 | } |
| 356 | 365 | ||
| 357 | /// Write the OptiPlex's 0x00AAAAAA scrub pattern over the stack region | ||
| 358 | /// the shell's frames will reuse, leaving only 256 B of live frame | ||
| 359 | /// headroom. 128 KiB covers the whole shell frame chain on QEMU and | ||
| 360 | /// the OptiPlex. Best-effort: the region is below rsp inside the boot | ||
| 361 | /// stack mapping; the call into `shell::run` overwrites the top word. | ||
| 362 | // --------------------------------------------------------------------------- | 366 | // --------------------------------------------------------------------------- |
| 363 | // Print a memory summary from the stashed memory map | 367 | // Print a memory summary from the stashed memory map |
| 364 | // --------------------------------------------------------------------------- | 368 | // --------------------------------------------------------------------------- |
