<feed xmlns='http://www.w3.org/2005/Atom'>
<title>rs-dos/rsnix/src/lib.rs, branch qemu</title>
<subtitle>Simple, kernel-only, 64bit OS, early stage.
</subtitle>
<id>https://git.2137697.xyz/rs-dos/atom?h=qemu</id>
<link rel='self' href='https://git.2137697.xyz/rs-dos/atom?h=qemu'/>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rs-dos/'/>
<updated>2026-11-01T15:05:09Z</updated>
<entry>
<title>usb: re-enable EHCI, share HID keyboard decoding, fix boot protocol</title>
<updated>2026-11-01T15:05:09Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-11-01T15:05:09Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rs-dos/commit/?id=561354b69c5ae5e73b036b382988fda02bc9fb01'/>
<id>urn:sha1:561354b69c5ae5e73b036b382988fda02bc9fb01</id>
<content type='text'>
- 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 -&gt; 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.
</content>
</entry>
<entry>
<title>streams: per-invocation channel, pumping mode, filter pipeline wiring</title>
<updated>2026-08-27T14:58:31Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-08-27T14:58:31Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rs-dos/commit/?id=300c0acbd7ab3fae8596ff839c39d9a17c70c68d'/>
<id>urn:sha1:300c0acbd7ab3fae8596ff839c39d9a17c70c68d</id>
<content type='text'>
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;
&lt;cmd&gt; &lt;path&gt; TIL &lt;marker&gt; 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 &lt;path&gt; 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).
</content>
</entry>
<entry>
<title>exec: PE32+ loader, versioned ABI, first external binaries (v0.6.0)</title>
<updated>2026-08-21T19:17:44Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-08-21T19:17:44Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rs-dos/commit/?id=71b6445b048456bf198d8b622570db95f4665cb9'/>
<id>urn:sha1:71b6445b048456bf198d8b622570db95f4665cb9</id>
<content type='text'>
rsapi: the kernel&lt;-&gt;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 -&gt; /disk/&lt;UID&gt;/bin/&lt;kind&gt;/&lt;name&gt;.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(&amp;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.
</content>
</entry>
<entry>
<title>Windowed GUI: wm module, input routing, and the paint app</title>
<updated>2026-08-17T16:56:04Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-08-17T16:56:04Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rs-dos/commit/?id=5558e3509d020a0e021a77f0617044497b2f2aee'/>
<id>urn:sha1:5558e3509d020a0e021a77f0617044497b2f2aee</id>
<content type='text'>
- 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
</content>
</entry>
<entry>
<title>USB2/1.1 (EHCI/UHCI) drivers + real-hardware bring-up hardening</title>
<updated>2026-08-17T12:53:52Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-08-17T12:53:52Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rs-dos/commit/?id=be868a43185826b02bf6d091019bb779678525bb'/>
<id>urn:sha1:be868a43185826b02bf6d091019bb779678525bb</id>
<content type='text'>
- 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
</content>
</entry>
<entry>
<title>v0.5.10: USB HID mouse via polled xHCI (rsnix/src/usb.rs)</title>
<updated>2026-08-04T01:06:06Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-08-04T01:06:06Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rs-dos/commit/?id=5e1bdc12bd298a9751da4285b7e93c68b1314332'/>
<id>urn:sha1:5e1bdc12bd298a9751da4285b7e93c68b1314332</id>
<content type='text'>
Polled xHCI driver (mouse-only): controller bring-up, port reset,
enable-slot -&gt; address-device -&gt; config-descriptor walk -&gt; 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.
</content>
</entry>
<entry>
<title>v0.5.8: heap-free QR encoder + crash-dump QR on framebuffer</title>
<updated>2026-08-03T00:23:35Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-08-03T00:23:35Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rs-dos/commit/?id=9fde94d068f87b34e0bc6bfa48388d89bb54f1e4'/>
<id>urn:sha1:9fde94d068f87b34e0bc6bfa48388d89bb54f1e4</id>
<content type='text'>
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.
</content>
</entry>
<entry>
<title>rsnix v0.5.0: graphics layer + cursor compositor</title>
<updated>2026-08-01T17:52:05Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-08-01T17:52:05Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rs-dos/commit/?id=819778f292ee4e1294603b98662e083e49e23463'/>
<id>urn:sha1:819778f292ee4e1294603b98662e083e49e23463</id>
<content type='text'>
The first visible GUI surface: real rect primitives, a single
shadow→display push path, and an on-screen mouse cursor that can
never race the text surface.

gop:
- fill_rect + push_rect(x,y,w,h): every raster op rasterises into the
  RAM shadow, then push_rect copies the rect to the display (write-only,
  still never read) and composites the cursor on top. blit_glyph and
  scroll shifts now route through it.
- cursor overlay: 12x16 pointer (const-fn built from column lists) drawn
  white with a black offset outline; position in one AtomicU64 (x:high
  32, y:low 32) — unpack masks the low word (a bare 'as usize' cast is
  a no-op on 64-bit). The cursor never enters the shadow, so erasing is
  just a shadow push of the old rect.
- cursor_redraw after scroll shifts and once at spawn (the vt diff never
  re-blits idle cells).

cursor task (task 2, spawned at boot when a mouse is present):
- owns the PS/2 ring; polls every 10 ms, moves the cursor (clamped),
  tracks buttons, and forwards every event to a task's queue when
  'mouse &lt;id&gt;' armed it (the window-manager input path).
- /dev/mouse is now a position device (x/y/buttons snapshot); 'mouse'
  watches the cursor live (prints on change); 'cursor' prints one-shot
  state; 'gfx' draws a status-bar demo; 'gfx check' is an in-kernel
  pixel self-test (shadow readback, display readback, cursor overlay
  purity) — PASS/FAIL per stage.

Tests (headless QEMU + monitor injection):
- make test-gfx (new): all four compositor checks PASS.
- make test-mouse (rewritten): cursor tracks mouse_move deltas, buttons
  ride along, /dev/mouse snapshot, live watch, real-event queue routing.
- make test-msg: task numbering shifted (cursor is task 2; msgrecv is
  task 3) — updated, still PASS.
- make test-disk regression re-passes. 0 build warnings.
</content>
</entry>
<entry>
<title>rsnix: preemptive kernel tasks (ADR 0003) — sched, IST fault stacks, 176-byte frames</title>
<updated>2026-08-01T15:38:57Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-08-01T15:38:57Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rs-dos/commit/?id=8f05ea44f69cb796e7b1f581ba1b8045c98d615b'/>
<id>urn:sha1:8f05ea44f69cb796e7b1f581ba1b8045c98d615b</id>
<content type='text'>
- sched: 16-slot static table, shell=task 0, idle=task 1, spawn with
  guard page + 8 KiB stack, RR on the 100 Hz tick, int 0x60 yield,
  sleep_ms, lazy reap, jobs/bg/sleep/kill shell commands
- gdt: own GDT mirroring UEFI selectors + TSS with IST1/IST2
  (guard-page overflows die gracefully instead of triple-faulting)
- interrupts: isr_dispatch returns next frame in rax; IntFrame grows
  rsp/ss fields — QEMU's 64-bit iretq always pops all five slots from
  a 176-byte frame, so synthetic task frames must carry rsp=top+ss
  (a 160-byte frame made the first iretq pop rsp/ss from the unmapped
  page beyond the frame)
- panic handler: task panics abort the task, shell keeps running
- main: sched::init moved after page allocator + adopted tables
  (the idle's stack+guard need both; it was silently not spawning)
</content>
</entry>
<entry>
<title>klog: kernel log ring + /dev/klog + klog shell command</title>
<updated>2026-08-01T12:09:14Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-08-01T12:09:14Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rs-dos/commit/?id=c7540c31d45dac68fa1852f5d25645d569b2da9d'/>
<id>urn:sha1:c7540c31d45dac68fa1852f5d25645d569b2da9d</id>
<content type='text'>
- rsnix/src/klog.rs: RAM ring of the last 128 serial log lines (128
  chars each), fed by serial_println! so every boot/low-level message
  is captured for free.  Sequence numbers per line, wrap/drop
  accounting; the ring never allocates (const-constructible static).
- /dev/klog char device; shell 'klog' is a thin alias for cat
  /dev/klog with the same filters/redirection, so 'klog -t 5',
  'klog -g fat' and 'klog WRITING /disk/.../boot.log' all work.
  klog rejects positional args (usage) and input keywords.
- Shell user-facing output (banner, prompt, term_print/term_println,
  pipeline sink, halt/poweroff) now writes to serial directly instead
  of through serial_println!, keeping echo/cat output out of the boot
  log (a streaming cat /dev/mem would otherwise wrap the ring).
- serial_println! macro hooks klog::append_fmt via format_args! (no
  alloc at call sites); klog::dump formats the log for the device.

Verified in QEMU: 28 boot lines captured (seq 0..27), shell output
absent from the ring, klog WRITING persists a boot log (mtype-checked),
wrap verified with a temporary 140-line boot loop (128 kept / 40
dropped, seq 40..167, oldest-live correct; loop reverted); full
test-disk round trip still fsck-clean.
</content>
</entry>
</feed>
