Repository navigation
PCI root bridges declared by acpiserver, device lifecycle and native hot-plug - #934
Conversation
…clare, pcidev state) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016UzbDnM9m6nQWhbjGi6Lv1
# Conflicts: # kernel/src/pcidev/mod.rs # lib/acpi/tests/resource.rs
…vers map at identity Work in progress before review; the PR body states the change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016UzbDnM9m6nQWhbjGi6Lv1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016UzbDnM9m6nQWhbjGi6Lv1
…the _OSC grant Ruled for lifecycle stage 2, which arms hot-plug ports under a granting root: acpiserver negotiates each root's _OSC under the PCI host bridge UUID (PCI Firmware Specification 3.3 section 4.5) in the pass that reads its _CRS, and the kernel keeps the records and says each grant. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016UzbDnM9m6nQWhbjGi6Lv1
Stage 2 of issues/a-device-function-is-driven-from-its-arrival-to-its-end.md. - The device set (toyos_abi::devset, SYS_DEVICE_SET/SYS_DEVICE_SET_READ): a level-triggered snapshot of every PCI function the kernel hands out, minted from DEVICE with READ and WAIT only, so the supervisor alone holds it. Its header carries the time by which a slot may change with no interrupt (PCIe 6.0 §6.6.1's wait, a command's §6.7.3.2 timeout). - pcidev keeps one record per requester, published one at a time, each carrying its live arrival: a returning device is a new arrival on the same record, so its domain id is reused. A claim of an older arrival answers Gone on every call; a departed function's release asks the device nothing, and its windows, firmware BARs, parked grants and residue are retired. - Native PCIe hot-plug on a port below a host bridge whose _OSC granted native hot-plug and capability structure control. The port is the kernel's, its MSI-X entry armed through the claim path's remapped message; the handler reads and clears Slot Status, takes a lost or button-removed slot's functions out of the set, issues at most one Slot Control write and posts the set's watch. Powering a slot, the 100 ms wait and enumeration run on the set's reader. The last release behind a removed slot powers it off. - An unassigned BAR behind a bridge is placed by placement::behind_bridges and accepted when the register reads the written address back with its flag bits intact. - toyos-supervisor: a departed arrival is never started again and is forgotten once nothing runs on it; the restart budget is the address's. - The supervisor reads the set, says what joined and left, and waits on its watch and its settle time. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016UzbDnM9m6nQWhbjGi6Lv1
# Conflicts: # kernel/src/arch/aarch64/smmu/domain.rs # kernel/src/arch/x86_64/amdvi/domain.rs # kernel/src/arch/x86_64/amdvi/mod.rs # kernel/src/pcidev/mod.rs # tests/toyos.rs
…to nothing is no BAR A slot's handler posts the device set's watch, which the scheduler must exist for: the boot-time call is made after scheduler::init (a real declaration arrives from acpiserver later still). An unassigned register behind a bridge is placed only where its size probe answers a size: QEMU's edu's BAR 1 reads 0 and sizes to nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016UzbDnM9m6nQWhbjGi6Lv1
…clared bus-mastering site Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016UzbDnM9m6nQWhbjGi6Lv1
…ords Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016UzbDnM9m6nQWhbjGi6Lv1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016UzbDnM9m6nQWhbjGi6Lv1
…eclaration fails every metal boot The AMD bench's root has more memory windows than the per-root bound of 8, so at 4f5142f its declaration was refused, every claim on it would have been, and no row saw it: its boot claims nothing and its row judges kernel-driven domains. The windows are now one list of 64 across every root; acpiserver says each root's count and its _CRS bytes under OWN, so the bench's next boot records them. Every metal boot now fails on the kernel's refused-declaration record, and the AMD row requires the declaration. The host bridge _OSC tests gain QEMU q35's masked shape. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016UzbDnM9m6nQWhbjGi6Lv1
…ried only, the no-acpi-row loss recorded - A declaration whose records the ABI does not name (a count or run past its array, an unnamed _OSC word, a reversed bus range) or whose roots share a bus on one segment is refused through Held like any other refusal: logged, posted, and the answer (toyos_pci::aperture::records). - The root _OSC is a query alone and its offer is recorded; the hot-plug service that acts on native hot-plug control (#934) commits it, so the osc module's condition holds. - issues/an-x86-machine-with-no-acpi-row-hands-over-no-pci-claim.md. - window.rs: Addresses::map's contract stated per arm. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016UzbDnM9m6nQWhbjGi6Lv1
Owner ruling: the kernel stops driving PCI functions (step 10) before this lands, so no kernel driver needs a domain before the declaration. The window rule is the one rule again, for every domain: above the CPU where the unit has room, and otherwise clear of the declared windows, with no domain where nothing is declared. A claim passes its declaration; a kernel driver, which comes up before any declaration, passes none. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016UzbDnM9m6nQWhbjGi6Lv1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016UzbDnM9m6nQWhbjGi6Lv1
So #934 commits through this ABI unchanged: a root's record holds what its query offered and what a commit (Query clear) was granted, none in stage B. A commit beside no offer, or of bits not offered, is a record the ABI does not name, refused by name with the declaration. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016UzbDnM9m6nQWhbjGi6Lv1
Conflicts resolved against main's changes since 3b77b9b: - #947 deleted kernel/pci/src/placement.rs and issues/no-boot-reaches-the-retry-that-undoes-a-placement-nothing-answered.md; the branch's hunks there (a doc comment and an import, and the issue's exit) were about the placement that no longer exists, so both stay deleted. issues/an-aarch64-cargo-run-boot-hands-over-no-pci-claim.md stays deleted: main's one-line edit restated how its windows are read, which this branch replaces. - K1's IOVA allocator (6cb27ce): a domain's allowed addresses are still the unit's range less every reserved range, but the root bridges' windows now come per domain from the declaration (`Domains::prepare`), beside the backend's own reserved ranges (RMRRs, IVMDs, the interrupt range, AMD-Vi's special range, the ITS doorbell), which `avoid` keeps. No declaration, no domain. `OwnSpace::create` takes the declared windows; the IOMMU selftests, which run before any declaration on QEMU's root bus, pass none. - pcidev: main's `publish` without the loader's list, and the declaration, its wait and its refusals as on the branch. - KernelArgs: main's `slot_volume_guid` follows `microcode_len` at 456; 472 bytes. - The supervisor (27b75c3): the `acpi` claim's holder starts before the disk drivers, whose claims wait for its declaration. - acpicase: main's nvme and usbd rows claim PCI functions on a boot whose only `acpi` holder is a job, so it keeps no storage rows; the probe's held-function arm takes the console function the kernel drives, the only function a kernel driver still holds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q
Main's benchcase and hostedclangcase came with nvme and usbd rows and no `acpi` claim holder, so on this branch their first claim would wait out its deadline and every claim of the boot would be refused. Both start acpiserver now, and a build host test holds the rule for every config: one whose rows claim a PCI function starts a row holding the `acpi` claim, undeclaredcase alone excepted, since it is booted to judge that refusal. Issues directly affected by the loader's window reads going: - the GCD map's held MMIO descriptors: deleted, since the loader reads no GCD map any more; - the hypervisor and ACPI tracks and the BarUnrouted tooling issue name `_CRS` where they named the loader's reads; - the x86 no-acpi-row issue names the disk drivers, which main's block service split made the claimants; - new: the ACPI server's death has no readback on the T14, since its boot's first `acpi` holder is a job and no row declares before usbd's claim. Issues that named closed drafts as owners: the four scheduler findings #872's S2 and S3 were to settle are the placement track's, which owns the push, the victim choice and wake placement; the speed track's line naming #865 as the CPU cap's owner goes, since #976 sized every per-CPU table for the CPUs firmware names; the ACPI track's `_CST` line keeps its issue and drops #870. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q
The merge of main left the kernel's virtio console with no domain before the declaration: main's IOVA allocator builds every domain clear of the root bridges' windows, and the console comes up before acpiserver declares them. On VT-d the console keeps the identity domain and works; on virt's SMMUv3 its stream aborts, so `cargo run -- --arch aarch64` panics in `wait_used` before userland. The issue this branch had closed stays open with that cause, owned by this track, since how a kernel driver gets a domain before any declaration is the owner's to decide. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q
benchcase: main's osbench round 2 beside the ACPI server this branch starts there. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q
…2d60) into wt/toyos-lifecycle-2 Lifecycle stage 2 rebuilt on main and stage B. - pcidev: stage B's module is the base. The per-requester records, the device set's generation and snapshot, `Gone` on an older arrival's claim, the departed release and retirement, and the slot's interrupt are this branch's, over main's BAR records (`Decoded`); `Machine::windows` and the placement state are gone with #947. - An arrived function's BARs: `place_unassigned` and `behind_bridges` are gone. Each BAR the device leaves unassigned is given, before the function is listed, the lowest address inside its port's windows that no other function decodes in and that the declared root bridges' memory holds (`toyos_pci::hotplug::{Windows, assign}`, host-tested), written once and kept where the register reads it back, with no probe through it. Its BARs enter `Machine::decoded` and leave it on retirement. `bar::placement` stays for that one write, refusing only an I/O BAR and asserting the caller's own choice. - The slots are armed by stage B's declaration, from `pcidev::declare`, below a root whose `_OSC` commit was granted native hot-plug and the capability structure. acpiserver commits both where its query offered both (`osc::negotiate_root`); the declaration carries the grant. The boot-time arm with no bridges is gone. - A slot's port reads its own bus numbers and windows: main deleted `toyos_pci::bridge` and `PciDevice::bridge`. `PciDevice::header_type` tells a bridge below a slot. - Two readers of the set never enumerate one arrival twice (`REFRESHING`). - ABI: the device set's object kind is 15 and its syscalls 135 and 136, past main's; the static `IrqWatch` poll returns for its watch. - supervisor: main's `drive_where` reads the device set, says what joined and left, and decides over the rows asked for. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q
Stage B's reopened aarch64 issue and main's osbench round 2. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q
- slot: an armed or refused slot names its root bridge's `_OSC` commit, which is what arms it now. - devset: the malformed-snapshot test's non-function record is a `Loaded` one; main retired the inventory's `Block` record. - bar: clippy's checked conversion in `placement`'s 32-bit assert. - iommu: a stray blank line goes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q
`unclaimable` refused every function the IORT does not route through the unit, which pre-empted the message refusal `virt_claim_lpi` judges for an e1000e the IORT routes straight to the ITS. It now refuses only a function that arrived after the unit was programmed, whose route was never read: the unit keeps the functions enumerated then. `a_function_arrives_and_leaves` requires the kernel's line that the plugged `edu`'s BAR 0 was assigned inside the port's window, and reads the holder's end as the supervisor now words it (`ended, code=0`). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q
Kernel clippy's type-complexity and argument-count lints. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q
The deferred-callback issue they pointed at went with the kernel's xHCI; the owner is the lifecycle track again, and the exit is its own. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q
Review of #934, round 3, at
|
1. pcidev::declare validates the declaration, arms the slots it commits,
and only then publishes it and posts its watch, under one DECLARING lock
that withhold takes too, outside the acpi claim's borrow. No claim runs
while a slot is armed, so isolate_behind's held skip and its made check
go, and Machine::roots and arm_slots with them.
2. A slot arms only where the unit blocks every requester on the port's
buses it has not seen (iommu::governs):
- VT-d: a programmed unit includes every PCI device, or its device scope
names a bridge's sub-hierarchy covering the buses (§8.3);
- AMD-Vi: every id there is a programmed unit's, and no IVMD keeps one
on identity;
- SMMUv3: the IORT, read again, routes every RID there through the armed
unit.
Otherwise the slot is not armed, said by port. The checks that change
nothing (message, remapping, the port's isolation, coverage, each
function behind it) are asked before the port is taken and its
functions are moved to their own domains.
3. inventory's doc is back on inventory; listed states its one claim and
its dead arrival-0 term goes.
The hot-plug row's machine has an edu behind the port from boot: the row
reads it moved to its own domain before the slot is armed, then pulls it
and plugs another. The track and the slot module say what is checked, and
that on VT-d a domain maps the regions firmware reserved for its requester.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Round 3 at
Optional, all taken:
Checks at
T14 and AMD metal are staged into the new 🤖 Generated with Claude Code |
Review of #934, round 4, at
|
…e device set is 164/165 Resolutions: - sdk/abi/src/syscall.rs: SYS_DEVICE_SET 164 and SYS_DEVICE_SET_READ 165, above main's SYS_CURRENT_CPU 163; 167 profile bins, the assert on 165. - kernel/src/drivers/virtio_console.rs and kernel/src/mm/dma.rs: deleted, as main deleted them (#1062 moved the VM's console to the userland virtio-serial driver). This branch's hunks there were the SMMUv3 refusal of the console's function and DmaPool's removed Drop; both go with the files. - kernel/src/iommu/mod.rs: main's, DeviceSpace deleted. Its only users were the console and dma.rs. - kernel/src/arch/x86_64/iommu_unit.rs: main's unclaimable doc, this branch's governs; untranslated and aborts_undomained deleted, both DeviceSpace's alone. - kernel/src/arch/aarch64/smmu/domain.rs: untranslated and aborts_undomained deleted for the same reason; this branch's governs and arrival-refusing unclaimable kept. - kernel/src/pcidev/mod.rs: main's toyos_register rename, this branch's hotplug import. - tests/images/{drivescase,hostedclangcase,jobcase,linkcase,netcase, proctreecase,clipboardcase}.toml: both main's virtio-console row and this branch's acpiserver row; clipboardcase starts acpiserver and main's vdagent. - tests/images/acpicase.toml: this branch's, storage-less, so without main's virtio-console row and logkeeper carrier: its first acpi holder is a job, and a PCI claimant there would wait out the declaration's deadline. - issues/an-aarch64-cargo-run-boot-hands-over-no-pci-claim.md: deleted, as this branch renamed it; main's edit described the userland console, which the renamed record is brought up to after this merge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… coverage is judged on its own segment and by range - A slot is listed in SLOTS, and its reader may enumerate a card behind it, before the declaration that armed it is published: `declare` arms every remaining port first. `arrive` read the published answer and called the unpublished case unreachable, so a card seated in that window panicked the kernel. HotPlug now keeps the declared windows `arm` is given, and an arrival's BARs are placed inside them; the lookup and the `unreachable!` go. A claim on such an arrival still waits for the published answer. - VT-d `governs` takes INCLUDE_PCI_ALL and bridge sub-hierarchies only from a DRHD on `toyos_acpi::SEGMENT_GROUP`, as RMRR resolution and AMD-Vi already do: a catch-all unit on another segment, or a scope whose path happens to match a bridge here, serves none of the functions this kernel enumerates. - SMMUv3 `governs` asked the IORT for each of up to 65 536 requester ids, each a walk of every node and its mappings, under DECLARING with preemption off. `Iort::translates` answers the same question for a range from the root complex's mappings: per mapping that holds part of it, one walk for an overlap and one for a shared stream, which `alone` now judges over a stream range for `route` and `translates` alike. Where the unit sends a stream on is not read: an arrival behind an SMMUv3 is unclaimable anyway, and governance is about aborting it. Host-tested against `route` on every range over crafted tables' edges, with a read bound a per-requester walk breaks by four orders of magnitude, and in the corruption sweep. - `program` takes the RSDP beside the base, so `Live` has no placeholder. - `governs` docs no longer sit between `unclaimable`'s doc and its function on VT-d and AMD-Vi; the track's VT-d parenthetical names the bridge-scope arm and the segment; `sys_acpi`'s comment says DECLARE acts after the borrow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s holder launchcase, libccase, panelcase, scalecase and sessioncase claim the VM's console function through main's userland virtio-console driver, and every PCI claim waits for the root bridges' declaration: without acpiserver each would wait out its deadline and refuse every claim of the boot. `every_config_that_claims_a_pci_function_starts_its_declarer` refused them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t-plug boot carries its console Main's harness reads a virtio machine's log from its console port, which the userland virtio-console driver carries through a claim, and refuses an image without that row. Since #934 every claim waits for the root bridges' declaration, so: - hotplugcase declares, and gets main's virtio-console row and logkeeper's carrier. - acpicase's `acpi` holders are jobs that declare nothing, and undeclaredcase has no holder: every claim there is refused, so the console port could never be carried. They boot `Profile::HeadlessNoConsolePort` and `Profile::HeadlessTwoEduNoConsolePort`, where the UART is the console, as main's own profiles without a remapping unit do. - `acpi_mediated`'s arm writing the console function's configuration and reading `HeldFunction` is removed: on acpicase no function is held, by a claim or by a kernel driver on q35 with VT-d. The decision is host-tested in `toyos-userbound`; the guest's reading of the kernel's `held` is recorded in issues/no-guest-reads-a-held-functions-configuration-refused-to-the-acpi-holder.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… claims are handed over The record (renamed by #934 from main's `an-aarch64-cargo-run-boot-hands-over-no-pci-claim.md`) described the kernel's virtio console refused behind the SMMUv3, which main deleted with #1062. Measured at #934's ded3c60, `cargo run -- --arch aarch64` under HVF on a pseudo-terminal: the root bridges declared, every claim handed over (NVMe, virtio-net, both virtio-serial functions), the UART quiet once the console's carrier has the log, and the terminal showing it, the disk driver's partition served and `vdagent: the host announces capabilities` among it. Both records' exits are met, so it goes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the signed image's row, and every PCI claim's mappings ending with it
The display driver needs the IGD OpRegion live. Its VBT holds the panel's
PSR and backlight settings, and its mailboxes are firmware's channel to the
driver. It is firmware memory outside the claim's BARs, which root
CLAUDE.md allows only as a privilege the signed image's system.toml grants
to a program it ships.
- **Manifest.** `firmware = ["igd-opregion"]` on a `drives` row of the
image's manifest. The render refuses an unknown window and one on a row
with no `drives`. Packages have no spelling for it.
- **Mint.** The supervisor mints the function's claim with the windows its
row names, carried in the mint's second selector word
(`DeviceRequest::Function(arrival, FirmwareWindows)`). A window exists
only from the mint, by construction: there is no call that adds one
later. A window the function has none of refuses the mint, so a row
naming one fails its start by name. The IGD OpRegion goes only to an
Intel function of class 03h, a bug fence on the row.
- **Window.** SYS_DEVICE_FIRMWARE_WINDOW (160) answers a granted claim's
window, by IGD OpRegion specification rev 0.5:
- It reads ASLS (config FCh, §5.1.2).
- `arch::opregion::window` decides the 8 KB at it in one pass with
`toyos_userbound::window` (§3.1.2's size; §2.3's firmware-reserved
NVS). Every page must be reserved or ACPI NVS in firmware's map,
inside the direct map, none memory the kernel hands out, and none a
BAR or driven window. Every 2 MiB leaf must be cacheable in the
direct map.
- It reads the header there and holds it to "IntelGraphicsMem" and
8 KB (§3.1.1-3.1.2).
- A refusal comes back named (`WindowRefused`). The aperture is
cacheable, and its handle carries MAP and WRITE alone.
- AArch64 has no such machine, and its direct map no such reach.
- **Every mapping made through a claim ends with it.** The claim records
each aperture it hands out, BARs and firmware windows alike.
`pcidev::revoke_handed`, one function, takes each back from every space
that maps it, through the revoke an aperture's last handle already does.
It runs at the claim's last handle (its release, its holder's death) and
when the claim moves, by IPC or a spawn's endowment. So no process keeps
a mapping of a function its claim no longer gives it. This is the exit of
`issues/a-bars-mapping-outlives-the-claim-it-was-asked-on.md` but for a
function's departure, which #934 wires to the same function.
- **tgl-display** maps the window, validates a copy with
`opregion::OpRegion::parse`, says OVER and MBOX raw, and lets the mapping
go.
- MBOX needs only bit 3, the VBT's. The bits the 2016 revision calls
reserved may name a later revision's mailboxes.
- The VBT is handed out raw: Intel has published no layout for it.
- `bar_map_again` keeps a BAR's handle past its claim's end and requires
its map to be refused, `Gone`.
- metalcase runs tgl-display with the grant, and the T14 row
`igd_opregion_window` requires the window read and valid.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd no aperture is handed past the revoke it should meet - **A move takes what the claim handed out under the lock that moves it.** IPC's send and a spawn's commit take the claim's `handed` list under the sender's process lock, before the claim is visible to its receiver, and revoke it once that lock is let go. A send that is refused gives it back. So a receiver that takes the claim and maps a BAR before the sender's revoke runs keeps its mapping (r2 required 2). - **An aperture is installed only while the caller still holds the claim.** `bar_map` and `firmware_window` install the new handle under the caller's process lock, and only if its table holds this claim at `handle`. A release or a move takes the claim out under that lock and revokes after it, so every aperture either met that revoke or is refused `Gone` (required 3). - `pcidev::revoke_handed(number)` is the by-number form #934's departure calls from its refresh loop. `Handed::revoke` documents its terms: thread context only, never the hot-plug interrupt, never with `MACHINE` held. The false clause about a departure is gone (required 4). - The BAR issue states what `bar_map_again` now reads and what it still does not (required 5). - `bar_map_again` sends its claim to itself over a connection. A BAR handle kept past the move maps `Gone`, and the new holder's fresh BAR maps and reads the device. The judge requires both lines (optional 5). - The window fence is decided from `Machine::identities` in `pcidev::claim`, before the function is touched, so a refused window no longer resets it (optional 1). An unknown window bit is `InvalidArgument` in `sys_device_claim` (optional 2). - `Bound::firmware` is `FirmwareWindows`, and `FirmwareWindow::ALL` is the one list of windows (optional 8). The ABI doc says what the kernel checks (optional 4), and the AArch64 stub keeps only the direct map's reason (optional 3). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Review r4, answered at Required
Optional
The merge's own consequences (
Metal
🤖 Generated with Claude Code |
…n the lifecycle track The owner approved (1-2 Nov 2026) removing the kernel's claim-time wait for the root bridges' declaration: the supervisor starts `drives` processes off the device set once the declaration is in it. Recorded as the next step of stage 2, with the two coverage records it restores. Built in its own PR after this one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd no aperture is handed past the revoke it should meet - **A move takes what the claim handed out under the lock that moves it.** IPC's send and a spawn's commit take the claim's `handed` list under the sender's process lock, before the claim is visible to its receiver, and revoke it once that lock is let go. A send that is refused gives it back. So a receiver that takes the claim and maps a BAR before the sender's revoke runs keeps its mapping (r2 required 2). - **An aperture is installed only while the caller still holds the claim.** `bar_map` and `firmware_window` install the new handle under the caller's process lock, and only if its table holds this claim at `handle`. A release or a move takes the claim out under that lock and revokes after it, so every aperture either met that revoke or is refused `Gone` (required 3). - `pcidev::revoke_handed(number)` is the by-number form #934's departure calls from its refresh loop. `Handed::revoke` documents its terms: thread context only, never the hot-plug interrupt, never with `MACHINE` held. The false clause about a departure is gone (required 4). - The BAR issue states what `bar_map_again` now reads and what it still does not (required 5). - `bar_map_again` sends its claim to itself over a connection. A BAR handle kept past the move maps `Gone`, and the new holder's fresh BAR maps and reads the device. The judge requires both lines (optional 5). - The window fence is decided from `Machine::identities` in `pcidev::claim`, before the function is touched, so a refused window no longer resets it (optional 1). An unknown window bit is `InvalidArgument` in `sys_device_claim` (optional 2). - `Bound::firmware` is `FirmwareWindows`, and `FirmwareWindow::ALL` is the one list of windows (optional 8). The ABI doc says what the kernel checks (optional 4), and the AArch64 stub keeps only the direct map's reason (optional 3). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d out Resolutions: - sdk/abi/src/syscall.rs: main's SYS_DEVICE_FIRMWARE_WINDOW keeps 164; SYS_DEVICE_SET is 165 and SYS_DEVICE_SET_READ 166, with 168 profile bins. - issues/a-bars-mapping-outlives-the-claim-it-was-asked-on.md: main's, which names this PR's departure as what is left; the next commit wires it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1137 revokes what a claim handed out at its last handle and when it moves. A function that leaves from behind a hot-plug slot while its claim is still held kept its BAR mappings until that claim was released, and the next arrival behind the port may be assigned the same addresses. `slot::refresh`'s per-record loop, which consumes each departure, now takes the departed claim's `Handed` there, under `REFRESHING`, where it can only be the departed claim's: an arrival on the record is listed in a later refresh, after §6.6.1's wait. It is revoked once `REFRESHING` is let go, on the reader's thread with no `MACHINE` held, because the revoke's shootdown waits on other CPUs and `REFRESHING` disables preemption. That is `revoke_handed`'s take and revoke split around the lock; never the slot's interrupt. `pciholder` maps its BAR 0 before the pull and keeps the handle; once the function has left, mapping that handle again answers `Gone`, which `a_function_arrives_and_leaves` now requires (`kept=Some(Gone)`). The BAR issue's departure item and the track's gap are closed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #934, round 5, at
|
…d wakes its holder after the revoke - The departure read what the claim had handed out by taking it off the claim (`699baad1c`). That left a concurrent release nothing to revoke before it gave the function up: between the reader's take and its revoke, a release, a reseat and a later reader's enumeration could place a new card at the departed BAR addresses while the old mapping still reached them. Now the reader copies the list (`handed_now`) and the claim keeps it, so the release revokes everything before `held` clears, as it did before this PR; revoking twice is idempotent (`SharedMemObject::revoke`). The departure only makes the revoke prompt. - Correction to `699baad1c`'s message: it said a function that left while its claim was held kept its mappings "until that claim's release, and the next arrival behind the port may be given the same addresses". No such reuse hazard existed while a claim is held: `next()` enumerates nothing behind a held function, `retire` keeps its BARs, and the release revoked first. What the departure adds is promptness, not isolation. - A departed function's holder is woken by the set's reader after the revoke, no longer by `depart` from the slot's interrupt, so a holder woken by its departure finds its apertures gone. Every call its claim makes still answers `Gone` at once, through `arrival`. The hot-plug row's `kept=Some(Gone)` is now ordered by construction. - `with_bound` checks `here()` again under the claim's lock, so an aperture handed out across a departure is in what the departure reads. - `refresh`'s doubled doc summary is one sentence. - The track drops the departure entry from its gaps (the BAR issue holds it, corrected likewise). A launch of `cargo run` with no terminal relaying nothing of the console port is recorded in issues/a-cargo-run-with-no-terminal-relays-nothing-of-the-console-port.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolution: sdk/abi/src/syscall.rs keeps main's SYS_TIMING_CONTROL at 165; SYS_DEVICE_SET is 166 and SYS_DEVICE_SET_READ 167, with 169 profile bins and the assert on 167. Everything else merged cleanly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Review r5, answered at Required
Optional
Merge with #1063: Checks: Metal: the r5b readbacks (boot 🤖 Generated with Claude Code |
#934 took syscalls 166 and 167 for the device set; this branch's SYS_NAMESPACE_NAMES moves above them to 168, with 170 profile bins and the one bound assertion on the top number. Object kinds: this branch removed File and main added DeviceSet, which is 20 here. acpicase.toml takes main's form, which starts no storage at all. libccase.toml keeps main's acpiserver beside this branch's MEMORY file server. The supervisor's boot starts are main's start_service, starting a row's binary as this branch's Binary::Row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Status: draft. One PR for #935 (PCI apertures stage B) and #934 (lifecycle stage 2), on
maine3a421563, headda40835f2. It answers review rounds 1 to 5, and wires #1137's revocation to a function's departure. #935 is closed in its favour; its branchwt/toyos-aperture-b(f9e44e0fb) is kept and is an ancestor of this one. Host CI, both image builds and the guest rows are green. The r5b metal boots (699baad1c) judge green on both benches and stand as the metal evidence.Problem
Configuration()plus every unowned GCD MMIO range) counted ECAM and flash among the declared windows, so an aarch64cargo runhanded over no PCI claim. The owner ruled that aperture discovery leaves the loader, AML stays in acpiserver, and a refused or missing declaration fails closed.What this changes
Root bridges are acpiserver's declaration (from #935):
_SEG, bus range and_CRSmemory windows, queries its_OSC(host bridge UUID, Support bits 0 and 4, Control bits 0 and 4), and declares them throughop::DECLAREon the acpi claim (toyos_abi::acpi::Apertures: up to 16 roots and 64 windows across all of them).loader/src/gcd.rs,loader/src/rootbridge.rs,PciRootBridgeIo,KernelArgs::root_bridge_windowsand its count (KernelArgs2592 → 1560 bytes), and the OVMFConfiguration()fixture.Functions arrive and leave (from #934):
toyos_abi::devset,ObjectKind::DeviceSet21,SYS_DEVICE_SET166,SYS_DEVICE_SET_READ167) is a level-triggered snapshot of every function the kernel hands out. It is minted fromDEVICEwithREADandWAITonly, and the supervisor'sdrivediffs it._OSCcommit granted both bits. It fails closed: acpiserver commits only where the image passes--hot-plug-ports-internal(the QEMU hot-plug image alone), and never on a root with anExternalFacingPortport or an unreadable_DSD. So no metal slot is armed.Gone.place_unassignedis gone.Review round 4 (
b89897e5e):arrive'sunreachable!, a kernel panic decided by when the card went in. The lookup and theunreachable!are gone.toyos_acpi::SEGMENT_GROUP, as RMRR resolution and AMD-Vi already do.Iort::translates, decided by range.alonenow judges a stream range forrouteandtranslatesalike.routefor each of up to 65 536 requesters underDECLARINGwith preemption off, each a walk of every node and its mappings. Now the work under the lock is bounded by the table, not by the bus count.programtakes the RSDP, the misplacedgovernsdocs are moved, and the track's VT-d text andsys_acpi's comment are corrected.A departed function's apertures are revoked promptly (
699baad1c, corrected inad70b5bf5, after merging #1137 in8388ab868):retirekeeps its BARs, and the release revokes before it gives the function up. What a departure lacked was promptness: a mapping still reached the departed slot until the claim was released.slot::refresh's per-record loop, underREFRESHING, and leaves it on the claim. It revokes once the lock is let go, on its thread, never the slot's interrupt, and with noMACHINEheld. The revoke's shootdown waits on other CPUs, andREFRESHINGdisables preemption.heldclears. Revoking twice is idempotent.departin the interrupt, wakes the departed function's holder, and only after the revoke. A holder woken by its departure finds its apertures gone, while every call on its claim still answersGoneat once.with_boundre-checkshere()under the claim's lock.pciholderkeeps a BAR 0 handle from before the pull, anda_function_arrives_and_leavesrequires that mapping it again answersGone. Five runs pass. With the reader's revoke dropped and its wake kept, it answersNoneand the row fails.Main's merge (
06241e941,f234a58a2,ded3c605c):mm/dma.rsandDeviceSpace. This PR's console refusal behind an armed SMMUv3 went with them, along withuntranslatedandaborts_undomained.acpiholders are jobs that declare nothing, and undeclaredcase has no holder. Every claim on those boots is refused, so a console port could never be carried. Their rows bootProfile::HeadlessNoConsolePortandProfile::HeadlessTwoEduNoConsolePort, where the UART is the console. Main's own profiles with no remapping unit do the same.acpi_mediated's HeldFunction arm is removed. It wrote the console function's configuration and expectedHeldFunction. On acpicase no function is held, by a claim or by a kernel driver, so the arm had nothing to ask about. The decision itself is host-tested intoyos-userbound. The guest reading of the kernel'sheldis recorded inissues/no-guest-reads-a-held-functions-configuration-refused-to-the-acpi-holder.md.cargo run -- --arch aarch64now hands over every claim and shows its log on the terminal.Why the guest rows need QEMU
a_function_arrives_and_leaves(Profile::HeadlessHotplugPort: q35, apcie-root-portwith anedubehind it from boot, ACPI PCI hot-plug off) reads the booteduisolated before the slot arms, pulls it over QMP and plugs another. No bench has a hot-plug port the owner lets a driver claim behind.a_claim_waits_out_an_undeclared_machine(Profile::HeadlessTwoEduNoConsolePort) boots q35 without acpiserver to judge the refusal.Profile::HeadlessNoConsolePort) mediate the firmware's own registers, which no host build has._CRS,_OSCand the IORT's range judgement are host-tested.Checks
Evidence: ToyOSOrg/evidence
pr-934at732d80ef8(review r5's answers), with earlier rounds incombined-r1/tocombined-r6/.cargo run -- --ci hostad70b5bf5,da40835f2cargo run -- --build-only, x86-64 and aarch64ad70b5bf5,da40835f2a_function_arrives_and_leavesad70b5bf5×5,da40835f2×1kept=Some(Gone); without the reader's revoke,kept=Noneand exit 1a_drives_row_holds_one_process_per_function,a_claim_waits_out_an_undeclared_machine,acpi_mediated_access,virt_claim_lpi,the_log_lands_on_the_boot_stick,a_link_ends_under_a_held_stream_and_comes_back,usbd_drives_the_spare,the_opening_exchange_with_qemus_agent_completes,bar_map_again699baad1ccombined-r6/)cargo test -p toyos-acpi --test iort, with 4 negative controlsb89897e5ecombined-r5/)699baad1c), judgedad70b5bf5699baad1c, the revoke changes only what a departure copies and who wakes its holder, pluswith_bound's re-check, andda40835f2renumbers two syscalls. The hot-plug row covers the departure path.issues/a-cargo-run-with-no-terminal-relays-nothing-of-the-console-port.md.Metal
699baad1c(the merge of kernel, supervisor: a claim's firmware window, granted at its mint by the signed image's row, and every PCI claim's mappings ending with it #1137 and the departure wiring), read back:acpi_tables_loaded,claim_reuses_its_remapping_entry,domains_map_clear_of_the_host_bridges,usbd_drives_the_type_c_controller). It declares 3 windows; the first claim waited 70 ms; its three slots stay unarmed because_OSCcommitted nothing.amdvi_translates_the_boot_controller,amdvi_remaps_the_boot_controller,amdvi_usbd_drives_its_own_entry). It declares its 16-window_CRS; the first claim waited 20 ms.4424a76f9read the same: 4/4 and 3/3.ad70b5bf5andda40835f2change no path these rows read, the declaration, the domains or the remapping entries. No bench arms a slot, so a departure is QEMU's alone.Risks and open points
acpi_server_deathis unregistered (issues/the-acpi-servers-death-has-no-readback-on-the-t14.md). The guest arm on a held function's configuration is gone for the same reason (its new issue). Both follow from boots whose acpi holder is a job that declares nothing. The owner ruled (1–2 Nov) to restore both: the kernel's claim-time wait goes, and the supervisor starts storage and the PCI drivers off the device set once the declaration is held. That is recorded on the lifecycle track and built in its own PR after this one lands.route.cargo runwithout a terminal: with stdin on/dev/nulland no terminal, the console port relayed nothing after the UART went quiet. On a pseudo-terminal it relays the whole log. I take this to be the launch, not a defect; the cause is not established.🤖 Generated with Claude Code