Repository navigation
Internal driver engine and PCI driver binding - #3260
Closed
valentinbreiz wants to merge 5 commits into
Closed
valentinbreiz wants to merge 5 commits into
valentinbreiz wants to merge 5 commits into
Conversation
RegisterDevice incremented the count before it stored the device, so a reader walking the table up to the count could see an empty slot. Store the slot and the primary index first, then publish the count with a Volatile.Write that DeviceCount and GetAdapter read back. Registration can now run after boot, from the driver pass.
A kernel registers PciDriverRegistrations from its constructor through the internal DriverCore. Global.StartKernel runs one pass after enabling interrupts and before USB hot-plug: each PCI function no driver owns, bridges excepted, is offered to its matching registrations, most specific match first, until one returns Bound and claims it. Each attempt gets a PciDeviceContext: - BARs sized once with decoding off, mapped as bounds-checked regions; - DMA buffers under an address ceiling; - MSI-X programmed masked and unmasked only after Bound, otherwise the handler polled from the timer; - work items on a lazily started driver-work thread, and events; - mice and network links published to System's managers after Bound. A declined or failed attempt is torn down in a fixed order, which puts back the Command register with bus mastering off, frees its DMA and releases its MSI-X vectors. Every type stays internal in its final shape until the seam opens.
The suite registers its own drivers and checks, per cell: - registration rules, ranking and the per-function device list; - edu: identification, factorial, DMA below 2^28, a polled interrupt and a work item; - nvme, free because the suite now builds without Storage: a failed attempt releases its interrupts, then a second driver gets MSI-X again and completes an Identify; - e1000e on arm64: MSI-X through the GICv3 ITS; - rtl8139: a driver-published link obtains a DHCP lease. Core grants the suite temporary internals access to count vectors.
🧪 Drivers Tests
Skipped
📎 Artifacts
|
🧪 Graphic Tests
Skipped
📎 Artifacts
|
🧪 Storage Tests
Skipped
📎 Artifacts
|
This was referenced Sep 26, 2026
🧪 Interrupts Tests
Skipped
📎 Artifacts
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #3259 (base
feature/driver-kit-test-axes), itself on #3258 and #3257. Retarget togen3once those merge.Fourth step towards letting a user kernel register its own PCI and USB drivers. This PR adds the driver engine and PCI binding. Everything stays internal: each type already has its final name, namespace and signature, so the PR that opens the seam only makes them public and marks them experimental (COSMOS0003). No public API or
PublicAPI.*.txtchange. No USB binding yet.Problem
NetworkManager.RegisterDevicealso published the count before the slot (s_devices[s_deviceCount++] = device), so a reader walking up to the count could see an empty slot.Fix
Registration and the pass (
Cosmos.Kernel.HAL.Drivers.Engine.DriverCore):PciDriverRegistrations (name, factory,PciMatches) from its constructor.Registerreturns false for a taken name (built-in owner names included) and throws after the pass, or from inside a factory, a Probe or a work item.Global.StartKernelruns one pass after enabling interrupts and before USB hot-plug starts, behindPCIEnabledalone so a kernel without PCI trims the engine. Registration closes as the pass starts.gopreservation included) and any bridge. Each function goes to its matching registrations, most specific first (Device>Class(b,s,p)>Class(b,s), then registration order), until one returnsBound, which claims it under the registration's name. Every decision is logged, e.g.[Drivers] pci/0000:00:02.0 -> rtl8139 (class match).[Drivers] No driver registered.Per-attempt context (
PciDeviceContext,MmioRegion,PortRegion,DmaBuffer):TryMapBarmaps every 2 MiB block throughEnsureMmioMappedand then enables memory decoding. Accesses are bounds- and alignment-checked, with a DMA write barrier before each write and a read barrier after each read.TryMapIoBaris false where the platform has no port I/O (ARM64).TryAllocateDma(length, maximumDeviceAddress)returns zeroed pages, or false when they would end above the limit.Interrupts, work items and events:
TryRequestInterruptswraps the handler in a trampoline that stays disarmed untilBound.Bound.driver-workthread started on first use throughKernelThread.TryStart. One scheduled during Probe runs only afterBound.DeviceEvent.Waitthrows inside Probe and returns false once the binding is gone.IrqSafeLockwraps the IRQ-safe spinlock.Teardown of a declined or failed attempt, in a fixed order:
MsiX.Disable, then release the vectors (UnbindEntryandReleaseDevicefrom Groundwork for user drivers: thread-safe allocation and mapping, MSI-X teardown, DMA barriers #3257).Publishing:
PublishMouseandPublishNetworkLinkqueue a publication during Probe, delivered afterBound, dropped on failure. AdaptersPublishedMouse : MouseDeviceandPublishedNetworkDevice : NetworkDevicereach the managers through sinks System installs in its initializer, inside the Mouse and Network guards. Built-in devices register first and stay primary.NetworkLink.Delivercopies each frame into a fresh array and runs the stack with interrupts masked.NetworkManager.RegisterDevicestores the slot, then publishes the count withVolatile.Write.Other changes:
MsiX.TryGetTable(where the table lives, so it can be mapped beforeEnable), andPciOwner.IsBuiltIn.Verification
Build.
make setup: 0 errors, no warnings in the files this PR adds or changes.Drivers suite. 47 tests per cell. It now builds with Storage off, so the built-in NVMe driver leaves the controller free for the new
nvmecell.Every skip is a cell gate. What the new cells check:
Registerafter the pass or from a driver callback.Boundor after teardown.[NetworkStack] Configured IP 10.0.2.15 on device rtl8139 pci/0000:00:02.0.After the review fixes, the fixer removed each fix in turn (bus-master clear, poll-timer stop, re-entrancy flag, work-item drop): exactly the cell written for it failed.
Other suites, QEMU. Same totals as #3259, test by test:
GarbageCollector on arm64:
GC_InteriorPointerRootfails (46/47).GCSegment.Enumerator.MoveNextcomputesCurrent + sizeon aGCObject*, so the size is scaled bysizeof(GCObject), and the heap walk stops early (GCSegment.cs:135). An array whose only root is an interior pointer is then never marked and gets swept.Real hardware. The runs above are QEMU only. The pass does nothing unless a kernel registers a driver.
Known gaps.
driver-workthread gets the default stack:KernelThread.TryStarttakes no stack size.MouseManager.UnregisterMouse,NetworkManager.UnregisterDeviceand a generation stamp onNetworkAdaptercome with USB unplug, which is their first caller.Found here but not changed:
PIT.RegisterTimerreprograms channel 0 after leaving its masked scope. A tick between the LSB and MSB writes can stop the timer for good. The engine registers with interrupts masked.MsiX.EnableignoresEnsureMmioMapped's result, so a table that fails to map would fault on ARM64 in the built-ins. The engine maps the table first.constof an enum type from another kernel assembly (AssemblyResolutionExceptioninMetadataBuilder.GetConstantType). A user driver declaringconst MouseButtonswould hit it once the seam is public.AddressMap.Add(0.0.0.0, ...)once per device and hit a duplicate key, and it gives the lease to device 0 whichever NIC answered. This comes from reading the code; it was not reproduced.ExpectedTestCountis below the number of tests it runs. This was already the case before this PR.