Claude Code keeps per-project memory at <home>/.claude/projects/<key>/,
where <key> is the project's absolute path with every separator and the
drive colon turned into a dash. That makes it both rename-fragile and
machine-specific, and this project has hit each of those once:
- D:\DEV\Schunix -> D:\DEV\SchOS changed the key, and 43 memories were
invisible until moved across by hand.
- Development happens from Windows and from Linux, and each looks in a
different place for the same checkout.
Committing .claude/memory/ fixes both -- git carries it across renames
and between hosts -- and `import` puts it wherever the local machine
expects. .claude/settings.local.json stays ignored: those are per-machine
permissions.
The colon maps to a dash rather than being dropped. Deleting it gives
D-DEV-SchOS, which matches nothing and silently finds zero memories; that
was the first attempt. On Git Bash the key comes from `cygpath -w`,
because Claude Code is a Windows program and sees D:\DEV\SchOS, not
/d/DEV/SchOS.
3.5 KiB
3.5 KiB
| name | description | metadata | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| schunix-driver-infrastructure | Dynamic IRQ registration, a DMA arena and PCI capabilities exist now; PCI interrupts are level/active-low and the old ioapicenable only did edge/high |
|
Built 2026-08-04, before USB, because xHCI, any NIC and virtio-gpu all need the same three things and none of them existed.
kernel/dev/irq.c+include/irq.h—irq_register(irq, fn, arg, name, level_low).trap.cused to be a switch listing every interrupt by name, which cannot work for a PCI device: its line comes out of config space at probe time, so there is no constant to write acaselabel for. Chains of up to 4 handlers per line, because PCI INTx pins are shared and a handler must recognise that its own device is idle and return. Registration publishes an entry then bumpsnwith a barrier, so dispatch needs no lock./proc/interrupts(minor 12) shows counts and trigger mode.kernel/core/dma.c+include/dma.h—dma_alloc(size, align)/dma_free(p, size)(Linux's contract: caller remembers the size). Carved off the top of physical memory inmain.cbeforekinit1, because kalloc's free list makes no contiguity promise. x86 PCI is cache coherent so no PAT games are needed — but ordering is still your problem.pci_find_cap()andpci_msi_enable()inkernel/dev/pci.c.
Four traps worth remembering:
- PCI interrupts are level-triggered, active low.
ioapicenable()hardcoded edge/active-high, which is right for every ISA-era device and wrong for every PCI one — and it fails silently: you get exactly one interrupt, then the device holds the line asserted, no further edge ever comes, and it is never heard from again.ioapicroute(irq, cpu, level_low)is the real call now. IRQ_ERROR(19) andIRQ_SPURIOUS(31) are LAPIC vectors that xv6 numbered as if they were IOAPIC pins, soT_IRQ0+19is already taken. A PCI device routed to pin 19 would collide.irq_registerrefuses them.cprintfhas no field widths.%04xis not a directive to it: it prints%and0literally, consumes no argument, and every argument after it lands in the wrong place. It understands only%d %u %l %x %p %b %s %%.- Unaligned MMIO reads return garbage. Reading a uint32 at BAR offset 2 to get xHCI's HCIVERSION returned zero. Read the aligned dword and shift.
- Never let a device raise an interrupt before its line is claimed. This
one cost real time. xHCI transfers completed, completion events sat in the
event ring,
USBSTS.EINTandIMAN.IPwere both set -- and the interrupt count stayed at zero. Cause: the controller asserted INTx during enumeration, while the IOAPIC entry was still masked. An interrupt that arrives at a masked pin is dropped, not deferred (the 82093AA manual says so, and QEMU implements it), and INTx is a level -- so the line then sits high forever with no further transition for anyone to notice. Unmasking afterwards finds nothing pending. The fix is ordering: enumerate with controller interrupts off, thenirq_register, then clear the device's pending status so it drives the line low, and only then enable. The next event is a genuine low-to-high transition.
Verify with pwsh -File tools/bootcheck.ps1 -Usb. See schunix-usb-xhci,
schunix-pci-drivers.