| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Hardware run 3 showed the same PED=0 SETUP halt and then never reached the
prompt. Two boot-time hazards explain the death:
- reset_port polled PED with a 2,000,000-iteration MMIO loop (seconds on real
silicon) and the reset retry ran it twice per port, so the port scan alone
exceeded the boot watchdog window (task 0 in one frame for 7.5 s panics).
- while no HID was found the cursor task re-ran the whole EHCI scan, port
resets included, every second -- a rate-matching hub never settles.
Changes:
- every EHCI wait is wall-clock bounded: halt/reset/run 500 ms, PED 200 ms
- control-transfer timeout 5 s -> 500 ms
- per-controller 3 s port-scan budget; no automatic re-scan (explicit
`usb rescan` only)
- 300 ms power-up settle before the reset walk (RMH bring-up)
- boot-phase watchdog 750 -> 2000 ticks (20 s), room for hub bring-up
- xHCI gets the same persistent bottom-row port/device summary
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Second OptiPlex QR: the Host System Error is gone (the 64-bit QH/qTD layout
fix worked), but the SETUP qTD now halts with TBYTES=0, C_ERR=3 and no
XactErr/Babble/DBE -- a split transaction with no valid response. PORTSC
shows CCS=1, PP=1 but PED=0, i.e. the port never enabled, so the split has no
TT to address. On Intel PCH a healthy USB2 port shows the rate-matching hub
as high-speed (PED=1), so the next step is to see every port's state.
- settle 100 ms after powering ports and clear stale change bits before the
reset walk (RMHs come up slower than direct devices)
- retry the port reset once after settling
- log a clear line when a connected port remains disabled
- retry a failed full-speed device as low-speed (the PORTSC speed field is
only valid once the port is enabled)
- persistent bottom-row summary: controller count, caps64, USB legacy handoff
result, every connected port's PORTSC and the HID device count. A hardware
run without serial now reports the whole picture instead of one QR.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
First OptiPlex run: the controller halted with HSE (USBSTS bit4) 20 ms into
the first control transfer, with otherwise valid QH/qTD fields (control QH,
DTC, MPS 8, split hub 0/port 1, active qTD).
The PCH EHCI advertises HCCPARAMS bit0 (64-bit addressing capability), which
extends every QH with bufptr_hi[5] (17 dwords / 68 B) and every qTD the same
way (13 dwords / 52 B). Packing them 32-bit-style (48/32 B) made the
controller read the next qTD as the overlay's high buffer pointer and DMA to
a bogus 64-bit address -> Host System Error.
Space every structure for the 64-bit variant (32-bit controllers ignore the
tail): HID qTD at QH+0x80; control qTDs at QH+0x100/0x140/0x180 with the
setup packet at +0x1C0. The failure QR now reports the capability and the
DMA bases so the next hardware iteration is self-describing.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
- test-usb boots an EHCI and an xHCI controller with a mouse + keyboard and
asserts enumeration, report decoding, and input reaching the shell. The
EHCI case asserts one key end-to-end (QEMU's EHCI model delivers only the
first report through root-port split transactions); the xHCI case asserts a
whole typed command.
- lib.sh: qemu_args() already supplies -bios, so the fresh OVMF varstore copy
was silently overridden by the system firmware. Pass the copy last (QEMU
honours the last -bios) so tests really boot clean NVRAM.
- DEBUG_NOTES: document why USB never worked on the OptiPlex (EHCI shipped
with DEBUG_MATRIX enabled, no RMH/hub support, only the first controller,
no boot protocol, unbounded DMA), the success/failure log to look for on
hardware, and the QEMU coverage limits.
- Makefile: add test-usb and include it in `make test`.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
hardening
EHCI (OptiPlex 7010 / Q77 and similar Intel PCH):
- enumerate *both* EHCI controllers (00:1a.0 + 00:1d.0), not just the first
- hub support: read the hub class descriptor, power/reset each port via hub
class requests, read the child speed, then drive the child with split
transactions carrying the hub's address and port (TT). On 7-series PCH the
root port presents the integrated Rate Matching Hub (8087:0024, class 9)
and the mouse/keyboard sit behind it; without this the driver stopped at
"no HID mouse/keyboard endpoint in config".
- HID mouse *and* keyboard, boot protocol + idle, per-device periodic QH
chained into the frame list, per-device report buffers and counters
- periodic re-arm follows the standard shape (qTD re-activated, Next-qTD
re-linked); the HC owns the status/toggle overlay
- DMA from the page allocator inside [16 MiB, 4 GiB): EHCI programs 32-bit
addresses and the page bitmap spans all RAM
- 2 ms recovery after SET_ADDRESS
xHCI:
- scratchpad buffers (HCSPARAMS2) and MaxSlotsEn > 1, with per-slot EP0 and
interrupt rings plus per-slot device contexts
- DMA from the page allocator instead of the heap allocator
- boot protocol + idle, mouse + keyboard, first-report diagnostics
- transfer events are routed to every device before matching a waiter, so a
second device's enumeration cannot swallow an armed device's report
shared:
- rsnix/src/hid.rs: HID usage -> scancode-set-1 tables, one scancode FIFO
feeding ps2::feed_scancode, per-device boot-keyboard report differ
- WM input pump polls PS/2 -> UHCI -> EHCI -> xHCI
- memory::alloc_pages_range(count, min, max) for bounded DMA allocations
QEMU coverage: make test-usb (EHCI + xHCI, mouse + keyboard). QEMU's EHCI
split path delivers only the first report for FS devices, so repeated-report
delivery and the RMH path remain hardware-test items.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
- ehci: DEBUG_MATRIX was left true in be868a4, so every boot replaced
enumeration with the one-shot diagnostic transfer matrix and returned
false -- the EHCI driver never brought a device up. Default it off;
flip it back on only when bisecting a controller fault.
- new rsnix/src/hid.rs: boot-keyboard HID usage -> scancode-set-1
tables, the shared scancode FIFO, and a per-device report differ.
UHCI now uses it instead of its own copy, so EHCI/xHCI keyboards can
feed the same PS/2 decoder (`ps2::feed_scancode`).
- uhci: SET_PROTOCOL now asks for the boot protocol (wValue 0) instead
of report protocol (wValue 1). In report protocol the report layout
is device-defined, but every driver parses boot reports.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
`make` did not parse at all (missing separator at line 139), so every
make target was dead. The folder also carried three overlapping
launchers (boot.sh, qemu.sh, boot-image.sh) plus a fourth partial one
in the Makefile, `cargo build --package rsdos` copy-pasted eight times,
the QEMU invocation copy-pasted per script, and the test helpers
(fail/wait_log/wait_qemu_exit/qemu_hmp) duplicated in five tests.
New layout:
- scripts/lib.sh: single config (image, OVMF, mem/smp), the one cargo
invocation (build_all), the one QEMU shape (qemu_args), and the test
harness (test_boot, wait_log, wait_qemu_exit, qemu_hmp, sendkeys)
- scripts/build.sh: kernel + every bins/* in one pass
- scripts/mksystem.sh: freshness-aware image build that now stages
every bins/*.efi into /bin/cli|gui (they were never copied before)
- scripts/run.sh: build + stage + boot in one command; --headless,
--debug (GDB stub), --monitor (HMP socket), --fresh, --mem/--smp/
--bios/--image; on Wayland it auto-selects XWayland plus
grab-on-hover so the mouse can be captured (Ctrl+Alt+G toggles)
- scripts/tests/: the five automated tests on the shared harness
- deleted: boot.sh, qemu.sh, boot-image.sh, lib-boot-args.sh,
build-image-release.sh, build-bootloader.sh (rsboot chain dropped)
The Makefile is now a thin wrapper: make run, make test, make system,
make inspect, make help. Two-command flow: ./scripts/build.sh then
./scripts/run.sh (run.sh alone also works).
Stale test assumptions fixed so the suite is green again: test-gfx
waited for "gfx: check" while the shell prints "gfx check: PASS";
test-mouse/test-msg hardcoded msgrecv = task 3 (wm-input took slot 3 —
ids are parsed from the log now); the queue-full probe targeted task 0,
whose exec pump drains its own queue as completion codes (moved to the
cursor task, which never recvs).
Verified in QEMU: make test -> test-disk, test-gfx, test-mouse,
test-msg, test-fault all PASS.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
ADR 0006 deferred root-path unification because it "needs VFS
bind/alias machinery". Implement the /bin half: paths whose first
component is `bin` are rewritten to the first mounted volume whose
root has a \bin directory, so `ls /bin/cli`, `cat /bin/cli/x.efi` and
a binary's own fs_resolve("/bin/cli/x.efi") work without knowing the
volume UID.
The alias is transparent — it creates no node, so `ls /` keeps its old
listing and /bin simply does not exist when no system volume is
mounted — and allocates nothing for non-matching paths. exec::resolve
keeps its per-file volume scan (stricter for multiple volumes).
Update ADR 0006 to record the decision and what remains deferred
(\etc and friends at the root).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
exec::spawn passes C-style argv with argv[0] = the command name, but
ls, cat, mkdir, touch and rm treated argv[0] as their first user
argument. Every path they were given resolved to the literal command
name ("ls", "mkdir", ...), so they failed silently: `mkdir .../sub`
never created the directory, `cat` printed nothing.
date formatted its last year digit as `y as u8 % 10`, which truncates
before the modulo (2026 -> 234 -> '4'), printing "2024".
Only visible now that the system image actually stages /bin — before,
/bin was empty and every command fell back to a built-in.
|
| |
|
|
|
|
|
|
| |
Makefile: PROFILE=release, CARGO_FLAGS=--release; make release target
kept as no-op (now default). boot.sh: PROFILE=release default;
--debug flag opt-in. mksystem.sh: PROFILE=release default.
test-disk/fault/gfx/mouse/msg: all use --release.
All scripts syntax-checked.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
boot.sh: replace esp/ + DISK_IMG with single SYSTEM_IMG on ide.0
(hooks into mksystem.sh --force for auto-staging).
boot-image.sh: delegate to mksystem.sh --chain (build-image.sh deleted).
build-image-release.sh: delegate to mksystem.sh --chain --gpt.
qemu.sh: flexible launcher uses SYSTEM_IMG; --chain flag for
full-chain build.
test-disk.sh: drop ESP_DIR (mksystem.sh stages everything).
test-fault.sh: boot system.img on ide.0 + CRASHLOG stick on ide.1;
replace deleted mkdisk.sh with inline mkfs.fat.
test-gfx.sh: system.img on ide.0, stage via mksystem.sh.
test-mouse.sh: system.img on ide.0, stage via mksystem.sh.
test-msg.sh: system.img on ide.0, stage via mksystem.sh.
Makefile: remove stale build-image.sh/mkdisk.sh references from help;
image target delegates to mksystem.sh --chain.
All scripts syntax-checked (bash -n). build-image.sh and mkdisk.sh
no longer referenced anywhere.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
exec_external now tries CLI first (blocking), then GUI (fire-and-forget).
GUI binaries spawn as tasks and return immediately — the shell continues
to the next prompt while the binary runs its own event loop. Exit is
handled by the scheduler (task cleanup on death), not by waiting on a
completion message.
bins/paint staged at /bin/gui/paint.efi on the system image.
All 11 tool binaries + 3 gate binaries (hello, boom, count) verified
across the v2 ABI. test-disk regression passed on the same kernel.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
rsapi: WmEvent (kind=u8 enum: 0=key, 1=mouse, 2=focus, 3=close),
WmKeyKind (Char/Enter/Backspace/F1-F4/…), WmMouseEvent (x/y/buttons),
WindowInfo (ptr/w/h). Five new exports: wm_create, wm_buffer,
wm_flush, wm_destroy, wm_recv (non-blocking TaskMsg dispatch → WmEvent).
Shell: abi_wm_* wrappers bridge rsapi types to rsnix::wm and
sched::recv(); wm_recv translates TaskMsg::Key/Mouse/Focus/Close
into flat WmEvent union.
bins/paint: gate binary — create_window + buffer + event loop with
Bresenham line drawing, color select (1-8), clear (c), F4 close.
All via wm_* ABI exports.
Fully migrated: date, mem, pci, cat, ls, mkdir, touch, rm, klog,
info, paint (11 external binaries in /bin/cli/ + 3 gate binaries).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
rsapi v2: 14 new ABI function pointers appended after v1 — fs_resolve,
fs_dir_next (iterator), fs_create, fs_remove, fs_read, fs_write,
fs_size, fs_close, rtc_now, mem_stats, pci_probe, pci_device,
klog_dump, kernel_version. New C-repr structs: DirEntry, RtcTime,
MemStats, PciDevice, KernelVersion. ABI_VERSION bumped to 2.
Shell-side: build_exports() constructs the full table at runtime per
invocation (replaces static const). 14 kernel-side wrapper functions
bridge rsapi types to rsnix kernel APIs; fs_dir_next maintains a
per-handle cache of children() results for iteration.
Tool binaries (10 crates, all linked against rsapi only):
- date: rtc_now → formatted YYYY-MM-DD HH:MM:SS
- mem: heap/pages free/total via mem_stats
- pci: pci_probe + pci_device enumerate all devices
- cat: fs_read paths or get_line stdin; put_line output
- ls: fs_resolve + fs_dir_next iteration; trailing / for dirs
- mkdir: fs_create(path, true)
- touch: fs_create(path, false)
- rm: fs_remove(path)
- klog: klog_dump 2048 bytes → put_line
- info: rtc_now + kernel_version (minimal; mem/pci deferred)
Gate verified: date prints RTC; pci lists all 4 QEMU devices
(8086:29c0, 1234:1111, 8086:10d3, 8086:2918); hello/boom/count
still work; test-disk PASS; poweroff clean.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
exec: LineChannel bounded ring (cap=16) with abort/eof/marker fields.
Shell 'pumping mode' assembles canonical lines from its queue into the
channel while the command runs; ^C marks the channel aborted.
Shell-side grammar: USE/TIL pre-drain into the channel before spawn;
<cmd> <path> TIL <marker> sugar drains paths until the marker line.
Interactive TIL: pump_key checks each submitted line against the marker
and closes the channel when it matches.
Exports callbacks run in the command task but touch shell-owned state:
this is safe by construction — the shell blocks in exec_external until
the command completes. put_line feeds Pipeline::line directly;
get_line pops from the channel (yield-poll when empty).
load: drop NX from image mapping (single-privilege kernel, NX bit 63
reserved when EFER.NXE is set by OVMF); compute image span from section
RVAs not file size.
bins/count: gate binary — reads lines via get_line, reports count via
put_line. Verified: count USE file → 6; count <path> TIL END → 2;
interactive count TIL DONE → 3; -h filter across task boundary;
^C abort.
v1 stream exports: put_line → Pipeline, get_line ← channel, log_line
→ klog append, exit → abort_current.
Accounting: print_one_line → pipe.line() returns false (head kill /
pager ^C) → the binary should wrap up promptly; accepted: binary does
not check the return value in v1 (phase 3 simplification).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
rsapi: the kernel<->binary ABI crate (no_std, no deps). Versioned
Exports table (magic + version header), EntryPoint = extern "C" on
x86_64-unknown-uefi (Win64: RCX/RDX/R8), Line/Arg/StreamResult types.
Binaries link only this crate.
rsnix::load: PE32+ parser — DOS/COFF/optional headers, section copy,
BSS zero-fill, .reloc base relocations (DIR64) via rva_to_file mapping
(the reloc directory is an RVA, not a file offset). Images are
allocated at LOAD_BASE=192MiB inside the page allocator's conventional
span and identity-mapped user+writable+nx.
rsnix::exec: resolution name -> /disk/<UID>/bin/<kind>/<name>.efi per
mounted volume; spawn reserves a sched slot, stores a per-task ExecCtx
(exports + argv block + image) before launch_at installs the frame —
no adopt race. Trampoline calls entry(&Exports, argc, argv), posts
TaskMsg::User(exit_code) to the shell. abort_current notifies the
shell with u64::MAX when an exec'd binary dies, so a faulting command
cannot leave the shell blocked forever.
sched: reserve_slot/launch_at split out of spawn; task_exit made pub.
shell: exec_external runs before non-frozen built-ins (ADR 0006
resolution rule); frozen core (help/clear/echo/halt/poweroff/exit/
rescan) is exempt. Exports v1 wires put_line to the terminal and
get_line to EOF; stream injection lands in Phase 3. ^C while waiting
kills the command task.
bins/{hello,boom}: gate binaries. hello prints through the exports
table; boom faults on 0x400000000000 to prove containment. The image
entry point IS the ABI entry (efi_main carries the Exports signature).
Version 0.6.0: exec exists end-to-end.
Gates verified in QEMU: 'hello' loads from \\bin\\cli\\hello.efi, calls
put_line across the ABI, exits 0, shell unblocks; 'boom' faults, task
dies gracefully with full report, shell survives, jobs shows the corpse;
built-in fallback works when the file is absent; test-disk PASS.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
wm: FOCUSED is now a plain window id — window 0 (home terminal) is an
ordinary focusable window in the z-order. The CONSOLE sentinel
(usize::MAX) and the focused()==0 special cases are gone; focus_console()
is an alias for focus(0). Desktop clicks focus window 0. The WM input
pump routes every key unconditionally: IRQ ring -> pump -> focused
window's owner; nothing polls the scancode rings directly any more.
tty: read_line() reads from the calling task's message queue (the old
global-ring read_line and the duplicate read_line_from_queue are merged);
raw mode (read_byte/try_read_byte) consumes the same queue;
interrupt_pressed drains the queue instead of the ring.
shell: task 0 owns home terminal window 0 and blocks on its queue like a
windowed shell; cat/hexdump stdin paths pass the home terminal handle.
sched: watchdog comments updated (shell parks on recv_blocking).
Verified in QEMU: F1 spawns terminals, F2 cycles focus home->term1->
term2, keystrokes land only in the focused window, ^C cancels a line,
edit runs raw over the queue and exits to a working prompt; test-disk
regression PASS.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
mksystem.sh stages one FAT32 image: \bzImage + \initramfs.cpio.gz
(rsbl paths), \EFI\BOOT\BOOTX64.EFI (kernel direct by default,
--chain for rsboot), \etc\motd.txt, empty \bin\cli + \bin\gui,
seed files at the root. --gpt wraps it in a partition table for real
USB dd targets.
Makefile: run/run-gui/debug attach only system.img on ide.0 (vvfat
lines deleted); stamp-based 'system' target rebuilds only when the
kernel is newer, so runtime writes survive reboots; system-fresh
wipes; inspect lists via mtools. build-image.sh/mkdisk.sh removed.
test-disk.sh boots system.img with a throwaway OVMF varstore — a
stale NvVars makes the firmware skip the \EFI\BOOT fallback.
sendkeys.py pacing 50ms->250ms/1.5s: keystroke loss under host load
corrupted typed lines (guest-side PS/2 ring overflow).
Gate: test-disk PASS (fsck clean, file survives poweroff).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
ADR 0006: PE32+ loader in rsnix, versioned exports-table ABI constructed
per invocation, task-per-binary, single dd-able system image with
/bin/cli and /bin/gui, shell-owned grammar with injected line streams,
per-terminal input discipline as prerequisite phase, frozen built-in
core, 0.6.0 at first exec.
Plan: five phases (image → input discipline → loader/exec → streams +
migration → GUI binaries), each with a QEMU verification gate.
Glossary: Binary, Loader, Exports table, System image; kind = launch
surface.
|
| |
|
|
|
|
|
|
| |
ox.saturating_sub(pad) / oy.saturating_sub(pad) — plain subtraction
on usize wraps to usize::MAX when the window sits at x=0 or y=0,
causing composite_rect to draw at the far end of the framebuffer
instead of near the window. Also broke drag-to-move when stuck
against the top or left edge.
|
| |
|
|
|
|
|
|
|
|
| |
- draw_window_border(): 1px border around every window except the root.
Blue (TITLE_BG_FOCUS) when focused, grey (TITLE_BG) when not.
- focus() / focus_console(): composite the full window area + border pad
on both the old and new focused windows (was title-bar-only before).
- flush(): include border pad in the damage rectangle.
- move_window(): redraw border area at old and new positions.
- Remove redundant term.flush() from tty Focus handler (focus() covers it).
|
| |
|
|
|
|
| |
focus() only composites the title bar — the window content stays
stale until the next write. Call term.flush() on Focus(true) so
the full window is re-composited immediately when switching to it.
|
| |
|
|
|
|
|
|
|
|
| |
- tty: read_line_from_queue() now takes &mut Terminal and handles
TaskMsg::Focus: Focus(true) sets ACTIVE to the terminal,
Focus(false) clears it. This fixes the bug where the console
shell's gop_print! output was redirected to the soft terminal
even after focus switched away from it.
- shell: run_windowed() passes the terminal through to read_line_from_queue.
- wm: remove noisy per-key debug logging from pump_main.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Architecture:
- vt: ACTIVE terminal redirect — when set, all gop_print!/put_char/write_str
go there instead of HOME. Terminal::from_buffer() renders into a
WM-owned pixel buffer.
- tty: read_line_from_queue() — canonical-mode line input from the task's
own message queue (TaskMsg::Key). Returns None on Close.
- shell: run_windowed() — same shell as the console but reads from the
message queue. Returns on ^D or window close.
- wm: F1 calls SPAWN_TERMINAL_CB (set by rsdos). No shell logic in the WM.
- rsdos: terminal_task callback + terminal_entry task — creates a 640x400
chrome window, wires up Terminal::from_buffer, sets ACTIVE, runs
shell::run_windowed(), cleans up on exit.
- shell: 'term' debug command that invokes the callback directly and
logs each step to serial.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
- wm rewrite: chrome flag per window, create_home_window for the
console (window 0, chrome-less, bottom-pinned, never closed),
composite_rect fills desktop background then blits windows bottom-to-top,
draw_window_intersect handles chrome-less windows, hit_test returns
Content for chrome-less, destroy/move_window no-op for id 0.
- vt: add window_params() so main.rs can wire the Terminal to wm window 0.
- main.rs: create home terminal window 0 and attach Terminal after wm::init().
- Focus fix: focus(0) now stores CONSOLE sentinel (not raw 0) so
focused() returns None when the home terminal has focus. Without this,
closing a GUI window left FOCUSED=0, which tty::poll_key() interpreted
as 'a GUI app has focus', spinning forever and swallowing all keyboard
input.
|
| |
|
|
|
|
|
|
|
|
|
| |
- Refactor vt.rs into a per-instance Terminal struct (owned cell/rendered
grids, cursor, ANSI parser, scroll state), rendering glyphs into a
caller-owned pixel buffer with dirty-region blitting
- Add gop buffer primitives (buffer_glyph_px, buffer_shift_up) and keep
the legacy vt:: API as thin wrappers around the home terminal
- Remove the now-obsolete scroll hook and shadow scroll path
- Home terminal renders into its own buffer; window_id stays None until
the WM integration lands
|
| |
|
|
|
|
|
|
|
| |
- ADR 0005: persistent home terminal (full-screen, chrome-less,
bottom-pinned), F1 terminal windows, per-instance vt/tty/shell,
queue-based input for all terminals, and the disk controller
- Glossary: add the Terminal term; update Task/App for the new model
- Add a re-entrant disk lock (DiskGuard over the preemption-depth
counter) and wrap all fs/fat mutating operations per-call
|
| |
|
|
|
|
|
| |
The console's batched scroll (gop::shift_up_n) shifts the whole shared
shadow, which dragged any composited window pixels up with it. Add a
scroll hook so the window manager re-composites every visible window
after a console scroll, restoring them at their fixed positions.
|
| |
|
|
|
|
|
|
|
| |
- Track the active mouse gesture (none / window-move / app-stroke) so a
press on the title bar becomes a move drag instead of leaking Move
events into the app's content
- Add move_window (clamped to the canvas, repaint old + new regions)
- Always deliver MouseKind::Release so a stroke that ends outside the
window still terminates
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
- Add the wm window-manager facility per ADR 0004: overlapping
pixel-surface windows, damage-rectangle compositing, WM-drawn title
bar, focus/minimize/close, and F1-F4 keybindings
- Add gop shadow compositing primitives and vt::repaint_region so the
console stays layer 0 beneath windows
- Route mouse (absolute coords, press/release edges) and keyboard
(focus-aware input pump) to the focused window
- Add the rspaint drawing-program crate (pen, 8 colors, clear) and shell
draw/windows/restore commands
- Extend TaskMsg with AppMouse/Focus/Close and document the design in
CONTEXT.md + ADR 0004
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
- Add polled EHCI (USB2) and UHCI (USB1.1) host-controller drivers with
HID boot mouse support, wired into cursor polling and the 'usb' shell cmd
- Add USB Legacy ownership handoff (claim_usb_legacy) plus config_write_byte
- Harden boot: verify the PIT/timer ticks before shell entry, EOI stray
interrupts in the LAPIC to stop interrupt storms, bound PS/2 controller
waits, and INTx-disable AHCI (polled driver)
- Add boot stage markers, klog screen echo, and QR lock; scheduler
mark_shell_running; NO_USB stopgap to keep BIOS USB-Legacy input alive
- alloc_pages_above for DMA buffers that must avoid low conventional memory
- Expand DEBUG_NOTES with EHCI/QEMU bring-up findings
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Polled xHCI driver (mouse-only): controller bring-up, port reset,
enable-slot -> address-device -> config-descriptor walk -> HID boot
mouse IN endpoint, one outstanding IN TRB drained on poll. Cursor
task polls usb first, PS/2 second. New 'usb'/'usb rescan' shell cmds.
Verified in QEMU (q35 + OVMF + qemu-xhci + usb-mouse): movement and
all three buttons tracked exactly; heap stays clean.
Fixes en route: MaxPorts=HCSPARAMS1[31:24]; operational regs CRCR=0x18
DCBAAP=0x30 CONFIG=0x38; pass input-context ptr in Address-Device;
Input Control Context DW0=Drop DW1=Add; HID walker bInterfaceClass at
+5; async discovery from cursor task.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Real hardware has no serial console, so a kernel crash only leaves a
frozen screen. Every dump_lines() report now also renders the crash
text as a scannable QR below the white-on-red band (photograph the
screen instead of relaying text).
- rsnix/src/qr.rs: from-scratch no_std/no_alloc byte-mode QR encoder
(EC-L, v1..v40, .bss statics only — safe on the fault path):
RS over GF(256), codeword interleave, zigzag placement, 8 masks with
N1-N4 penalty auto-selection, format/version info.
- gop.rs: dump_qr() joins dump lines into a static buffer, truncates
with '...' at the v40-L capacity, encodes, and renders centered
black-on-white with a 4-module quiet zone.
- tools/qrcheck: host harness (embedds qr.rs) + qrcheck.py cross-check
against segno's placement primitives. Verified bit-exact: 12
versions x 8 masks forced, plus auto-mask choice matches segno's
penalty evaluation. Caught 3 encoder bugs (multi-block interleave
offsets, transposed version info, mask eval ordering).
- QEMU e2e: page-fault dump screendump decodes via zbarimg, content
matches the serial log line-for-line.
- test-msg.sh and test-fault.sh both PASS; debug+release 0 warnings.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The pre-verify 0x70020 'mystery' was a debug artifact, not corruption:
BLANK = {ch:32, fg:7, bg:0} = raw 0x70020, and vec![BLANK; n] fills both
grids with it at init. The old check compared cells against 0 and its
probe loop break-ed on the first non-zero cell, so probes 1-3 stayed at
their zero-initialized slots — c=[0x70020,0,0,0] was bogus on every
healthy boot. A full-grid dump proves QEMU grids are 100% BLANK at
shell entry (nz=3700 blank=3700).
Fix: probes compare against blank_raw (0x70020), record all four values
(no early break), probe both cells and rendered, and add a full-grid
nz/blank summary line. The OptiPlex firmware 0x00AAAAAA scrub remains
cleanly distinguishable (scrub != blank).
Markers: the shell banner occupies grid rows 0-1 and the prompt row 2
(proven by screendump), and a stale crash.log replays at rows 2+, so the
shadow and vt markers moved to the bottom of the screen via the new
gop::text_rows()/bottom_row(off) helpers instead of rows 1-2.
QEMU verified: verify: ok every boot; test-msg + test-fault PASS; debug
and release builds 0 warnings.
|
| | |
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
shadow marker
The klog -t 2 crash drops a 126-byte String (the vt verify klog line)
whose data ptr 0x40444970 points into the first-heap-block region whose
content is the firmware 0x00AAAAAA scrub — a stale pointer, not a real
allocation (back-pointer slot never written). Upgrades so the next
real-hardware crash shows the full picture:
- guard: explicit candidate block bases (b=ptr-24 / b16=ptr-32) with
their size/free fields, plus a 16-word window (mem..mem4) from
ptr-0x30 instead of the old 8 words
- vt::debug_verify: content probe of the cells grid (must be all-BLANK
at shell entry) — catches a stale in-heap cells pointer that passes
the range checks
- gop: on-screen marker for shadow carved vs NO-SHADOW fallback
(the scrub content at the crash ptr suggests the shadow was rejected
and its block stayed free + firmware-scrubbed)
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
| |
The OptiPlex's first banner write #GPs when the vt cells VecData holds
firmware 0xAA fill instead of a real buffer pointer. The poison/fill
test hooks (stack + free-heap + free-page 0x00AAAAAA) were removed from
the release image — they changed boot behavior and may walk a corrupt
free list over live blocks. Added vt::debug_verify() (cells/rendered
ptr/cap/len + in-heap check) at shell entry: the next real-hardware
boot shows whether the corruption is present at shell entry or lands
during the banner write.
|
| |
|
|
|
|
|
|
| |
The OptiPlex crashes free garbage (0xaaaaaa, 0xaaaaaa00aaaaaa) whose
value is the firmware's 0x00AAAAAA scrub pattern left in stack/heap.
The drop size/align (the dealloc layout) now prints on the band, so the
next repro shows both the garbage ptr AND the size it was freed with —
cap×elem_size pinpoints which struct's fields were clobbered.
|
| |
|
|
|
|
|
|
| |
The OptiPlex repro copies are truncated at the ~110-char band width, so
the old single-line 'stk:' (12 words, >200 chars) lost the return chain
after a few words. Split mem/stk dumps into 4-words-per-line blocks:
mem+mem2 (8 words around the corruption) and stk..stk4 (16 words) —
the drop caller chain now survives hand transcription.
|
| |
|
|
|
|
|
| |
The OptiPlex heap-corruption repro (guard report) came in hand-copied
and garbled; the exact heap start/end was the missing datum. The heap
self-test range was serial-only; now it is also a boot marker, so the
next boot shows 'rsnix: heap 0x..-0x.. (N MiB) ok' on the framebuffer.
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The OptiPlex #GP (vec=13 in __rust_dealloc) frees a garbage pointer: the
back-pointer at [ptr-8] is clobbered by the time the shell drops its
Parsed struct. A blind store to the bogus block header was an opaque
#GP — now it is a readable report:
- dealloc validates the back-pointer (in-heap block, sane size, in-use)
before trusting it; on failure it prints ptr/block/heap range, the
memory around the block header, and the live stack chain, writes the
crash band + crash.log, and halts (memory.rs: heap_guard_report).
- fault dumps now carry the full register set (rax..rdi, rbx, r8..r15)
so the next repro shows the exact freed ptr and clobbered value.
- new 'corrupt' shell hook deliberately clobbers a live block's
back-pointer to prove the guard fires (test-fault-style halt).
Verified: test-msg/test-disk/test-fault pass; corrupt hook reports
ptr=.. block=0xdeedbeef... with mem dump instead of #GP. rs-dos.img
rebuilt with the release kernel.
|
| |
|
|
|
|
|
|
|
|
|
| |
- fault_dump prints img= (kernel image base) on the serial line and
the on-screen band, so a real-hardware repro can be resolved against
the PDB without serial
- backtrace: rbp-chain walk then a raw scan of the stack above the
interrupted rsp; both filter to the .text range (PE-header parse via
memory::text_range) so only code addresses are collected
- on-screen boot marker shows the kernel image range at every boot
- crash.log now records img= + bt= lines alongside rip/rsp
|
| |
|
|
|
|
|
| |
Real hardware has no serial console, and the ahci/fat/fs boot
diagnostics were serial-only — a silent fs failure left the user with
no way to see where boot stopped. Add boot_marker lines for the AHCI
disk count, FAT volume count, and the VFS tree + fs self-test result.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The fault handler now writes crash.log to the volume labeled CRASHLOG
(label-selected via fat::crash_vol), falling back to slot 0. On real
hardware the boot USB may be unwritable, so a second stick labeled
CRASHLOG survives the write; the shell auto-prints the previous crash
report from it at next boot (read_crash_log resolves /disk/<uid>/crash.log
through the FAT path, since Disk nodes hold no cached children).
- fat.rs: volume_label() + crash_vol() (no allocation)
- fs.rs: write_crash_log targets crash_vol(); read_crash_log prefers the
CRASHLOG disk node; drop now-unused first_disk()
- mkdisk.sh: --label option
- test-fault.sh: boots with a dedicated CRASHLOG stick (mkfs -F 32 -n
CRASHLOG), verifies crash.log lands there and NOT on the ESP
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
User's machine: boot reached 'rsnix: boot done — shell' then panicked
'len is 0, index 0' at the first cell write — CELLS (the vt grid) was
valid at vt::init and empty at the first shell paint, i.e. something
stomped the heap between vt::init and the shell banner. The only
change in that window vs the working v0.5.3 was the v0.5.4 move of the
1.9 MB display shadow from the heap to the fb-aware page allocator:
on this machine's memory layout the page-allocator shadow's fill loop
(480K words) clobbered the heap's vt vectors.
- Shadow is again the *first* heap allocation, in gop::init before
vt::init builds its grids (the v0.5.3 layout that provably boots on
the user's hardware) — nothing else in the heap can collide with it.
- init_heap now splits the free list at the GOP framebuffer hole
instead of subtracting its size from one block that still *spanned*
the hole — no heap allocation of any size can land on the display.
- Kept the panic-proof try_reserve + fb-overlap rejection + no-shadow
warning band; dropped the late init_shadow() call in main.rs.
- Verified on QEMU: shadow @ 0x1980020 origin=heap, allocated before
vt::init, fb clean at 0x80000000; all 5 suites PASS; 0 warnings.
|