<feed xmlns='http://www.w3.org/2005/Atom'>
<title>rs-dos, 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-01T16:09:59Z</updated>
<entry>
<title>ehci: bound all init waits; stop auto-rescanning; wider boot watchdog</title>
<updated>2026-11-01T16:09:59Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-11-01T16:09:59Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rs-dos/commit/?id=d99906b48698c1bd722a1fa5f2270b76317ec661'/>
<id>urn:sha1:d99906b48698c1bd722a1fa5f2270b76317ec661</id>
<content type='text'>
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 -&gt; 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 -&gt; 2000 ticks (20 s), room for hub bring-up
- xHCI gets the same persistent bottom-row port/device summary
</content>
</entry>
<entry>
<title>ehci: diagnostics + hardening for the port-not-enabled case</title>
<updated>2026-11-01T15:55:14Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-11-01T15:55:14Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rs-dos/commit/?id=e53161d6b9191533caeea0ef6148f247f4e22441'/>
<id>urn:sha1:e53161d6b9191533caeea0ef6148f247f4e22441</id>
<content type='text'>
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.
</content>
</entry>
<entry>
<title>ehci: reserve the 64-bit QH/qTD layout (fixes hardware HSE)</title>
<updated>2026-11-01T15:48:40Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-11-01T15:48:40Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rs-dos/commit/?id=12549dabf96bd6eb08c6834134fedf9edc7452b5'/>
<id>urn:sha1:12549dabf96bd6eb08c6834134fedf9edc7452b5</id>
<content type='text'>
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 -&gt; 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.
</content>
</entry>
<entry>
<title>scripts: add test-usb; fix duplicate -bios in the test harness</title>
<updated>2026-11-01T15:33:09Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-11-01T15:33:09Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rs-dos/commit/?id=363e24bcd18a28948f63fa78ac8a4eaa8784fe59'/>
<id>urn:sha1:363e24bcd18a28948f63fa78ac8a4eaa8784fe59</id>
<content type='text'>
- 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`.
</content>
</entry>
<entry>
<title>usb: real-hardware rework — hubs, both EHCI controllers, keyboard, xHCI hardening</title>
<updated>2026-11-01T15:33:05Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-11-01T15:33:05Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rs-dos/commit/?id=50b4d2f0ab05901a847d8e1318152308265e579b'/>
<id>urn:sha1:50b4d2f0ab05901a847d8e1318152308265e579b</id>
<content type='text'>
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 &gt; 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 -&gt; scancode-set-1 tables, one scancode FIFO
  feeding ps2::feed_scancode, per-device boot-keyboard report differ
- WM input pump polls PS/2 -&gt; UHCI -&gt; EHCI -&gt; 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.
</content>
</entry>
<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>scripts: rebuild the dev loop around build.sh/run.sh</title>
<updated>2026-11-01T14:53:29Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-11-01T14:53:29Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rs-dos/commit/?id=7958276c9f0d230053520ba20f2a00f5ecda18e4'/>
<id>urn:sha1:7958276c9f0d230053520ba20f2a00f5ecda18e4</id>
<content type='text'>
`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 -&gt; test-disk, test-gfx, test-mouse,
test-msg, test-fault all PASS.
</content>
</entry>
<entry>
<title>fs: /bin alias for the system volume</title>
<updated>2026-11-01T14:52:44Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-11-01T14:52:44Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rs-dos/commit/?id=316a2a239446fe927e35b029b651cbeb08ff3f48'/>
<id>urn:sha1:316a2a239446fe927e35b029b651cbeb08ff3f48</id>
<content type='text'>
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).
</content>
</entry>
<entry>
<title>bins: read user args from argv[1..]; fix date year digit</title>
<updated>2026-11-01T14:52:39Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-11-01T14:52:39Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rs-dos/commit/?id=ef2d8e354a91ef30e34e2ddb61381131a989591c'/>
<id>urn:sha1:ef2d8e354a91ef30e34e2ddb61381131a989591c</id>
<content type='text'>
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 -&gt; 234 -&gt; '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.
</content>
</entry>
<entry>
<title>build: default profile is release (debug too slow for dev loop)</title>
<updated>2026-08-27T16:39:29Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-08-27T16:39:29Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rs-dos/commit/?id=277218fe1ba1e3616bd147c9d9abecfc56b9957f'/>
<id>urn:sha1:277218fe1ba1e3616bd147c9d9abecfc56b9957f</id>
<content type='text'>
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.
</content>
</entry>
</feed>
