Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
cd3c0a5
toyos-amdvi: the interrupt half, the 128-bit remapping entry and its …
Japabu Oct 14, 2026
9622274
Merge #918 (toyos-claim-slots at 9b39b7ce7) ahead of its landing
Japabu Oct 14, 2026
b85bbb0
AMD-Vi remaps interrupts: a table per requester indexed by vector, 12…
Japabu Oct 14, 2026
f0462d2
pcidev hands over an MSI-X table's BAR where the machine remaps; xhci…
Japabu Oct 14, 2026
0899a16
Merge #918 (toyos-claim-slots at abe38d5b3, with origin/main 4e11ebc1…
Japabu Oct 14, 2026
c227453
Merge origin/main (5b48a3d17: #918, #922) into wt/toyos-amdvi-intremap
Japabu Oct 14, 2026
e81f97f
Merge origin/main (f0a987bc6: #919) into wt/toyos-amdvi-intremap
Japabu Oct 14, 2026
4f1d6fc
amdvi: SMI filter on every programmed unit, the table's BAR only wher…
Japabu Oct 14, 2026
3206782
amdvi: the SMI filter goes on once the kernel's drivers are settled, …
Japabu Oct 14, 2026
2a47a6d
Merge origin/main (cc1a44c9b: #916, #923 and more) into wt/toyos-amdv…
Japabu Oct 14, 2026
c648c14
Merge origin/main (2c3f61a3e: #924, #905) into wt/toyos-amdvi-intremap
Japabu Oct 14, 2026
a4f3e6c
Merge origin/main (e7e748fb2: #925, #926, #927, #930); the AMD claim …
Japabu Oct 14, 2026
857b653
toyos-amdvi: an SMI's record may carry the interrupt range sign-extended
Japabu Oct 14, 2026
a8791b6
usbd_spare: the spare left to a claim is taken by value
Japabu Oct 14, 2026
6390289
Merge origin/main (335927b83: #928, #929) into wt/toyos-amdvi-intremap
Japabu Oct 14, 2026
1ceab43
amdvi: a message refused from a platform requester id is the platform…
Japabu Oct 14, 2026
4b052d5
Landing re-review: records without the withheld table BAR, the MSI-X …
Japabu Oct 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,16 @@ where every bridge on the function's path forwards
(`placement::behind_bridges`, `kernel/pci/src/placement.rs`); where none of
that is free, the BAR stays where firmware put it and is handed to nobody,
named `NoRoutedRun`, and a function with no other mappable BAR is refused.
A bridge firmware sized to its BARs leaves no such 2 MiB. Where the BAR is
also the one holding the function's MSI-X table, a claim withholds it and
the function is refused `NoMappableBar` before placement; a claim that
handed the table's BAR over, as #921 does, would meet `NoRoutedRun`:
A bridge firmware sized to its BARs leaves no such 2 MiB. The BAR holding
a function's MSI-X table is placed like any other (PR #921), so it meets
the same refusal:

- **The AMD bench**: the AX200 at 01:00.0 has BAR 0 at 0xd0600000, behind
bridge 00:02.2, which forwards 0xd0600000..0xd0700000 (#884's E13 §1,
https://github.com/ToyOSOrg/evidence/blob/2eb315326d1b4d0363b4c918da8dd2bfaabd1234/e13-amd-ax200-bar.log);
the xHCI at 03:00.4 is behind 00:08.1, whose 0xd0000000..0xd0500000
bus 3 fills. Its one BAR holds its MSI-X table, which a claim withholds,
so it is refused `NoMappableBar` before placement; a claim that handed
the table's BAR over, as #921 does, would meet `NoRoutedRun` there.
bus 3 fills. Its one BAR, which holds its MSI-X table, meets
`NoRoutedRun`, and with no other BAR the claim is refused `NoMappableBar`.
- **The T14**, as S1 read it (lines 111 to 116,
https://github.com/ToyOSOrg/evidence/blob/cd368b25b2259a76b80dc31f4219dac1c91d84dc/s1/wifi-s1-a-kernel.log):
the AX210 at 09:00.0 has BAR 0 at 0xbc300000, 16 KiB, its MSI-X table
Expand Down
15 changes: 1 addition & 14 deletions issues/a-claims-own-refusals-are-read-by-nothing.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,6 @@ Refusals of `kernel/src/pcidev/mod.rs` that no test reaches:
- every bound `SYS_DEVICE_BAR_MAP` and `SYS_DEVICE_DMA_ALLOC` check, which is
`bar_object`'s index and zero-length bounds and `dma_alloc`'s
`MAX_GRANT_BYTES` and `MAX_GRANT_TOTAL`;
- the withholding of the MSI-X table's own BAR on the *ordinary* path:
`place_bars`'s `Some(index) == table_bar` (`kernel/src/pcidev/mod.rs`)
mutated to `(Some(index) == table_bar && false)` — the spelling that builds,
where a bare `false` is `unused variable: table_bar` — hands that BAR to the
holder of every function that publishes a table, and `https_tls13`,
`https_tls13_e1000e`, `pci_function_is_exclusive` and `userdev_dma_fault` are
each green on it. A holder that can write the
table aims the device's message at any address the LAPIC decodes, and
`msix_bar` is named as the boundary in the module header while nothing reads
it back. A refused claim is not the arm that covers this: the refusal spends
no BAR at all, so what is unread is the hand-over that succeeds.

A one-field mutation of any of them — an index bound compared `<=` rather than
`<`, a window overlap accepted, a grant total never summed — leaves every arm
Expand All @@ -42,6 +31,4 @@ Owned by whoever next adds a boot config whose own test binary holds a claimable
function: netd holds this machine's only claim, and a test that makes it fail
this way costs the machine its NIC for that boot. Exit condition: a guest arm in
which a test binary's claim is refused each of these ways, each one red against
a kernel whose corresponding check answers `Ok` — and, for the withheld table
BAR, one that holds a function publishing a table and is told that BAR's size
is zero.
a kernel whose corresponding check answers `Ok`.

This file was deleted.

40 changes: 0 additions & 40 deletions issues/an-amd-vi-machine-isolates-no-device.md

This file was deleted.

11 changes: 5 additions & 6 deletions issues/every-driver-is-still-in-the-kernel.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,11 @@ What is left of the staged work:
splitting the controller.** It shares the controller, the event ring and the
lock with the boot disk.
- **Where each xHCI keeps its MSI-X table**, which decides whether usbd's
claim is handed over (`pcidev::msix_bar`). QEMU 11.1's `nec-usb-xhci` arms
MSI-X and publishes one memory BAR, BAR 0, so its table is there and the
claim is refused as
`issues/a-controller-whose-msix-table-is-in-bar-0-cannot-be-driven-from-userland.md`
says; the device's `msix=off` leaves it on MSI, as `msix-exclusive-bar=on`
moves NVMe's table for blockd. The T14's two xHCI functions each publish
claim arms MSI-X or MSI (`bring_up`, `kernel/src/pcidev/mod.rs`). QEMU
11.1's `nec-usb-xhci` arms MSI-X and publishes one memory BAR, BAR 0, so
its table is there, and a claim hands that BAR over with the rest where
the machine remaps; the device's `msix=off` leaves it on MSI, as
`msix-exclusive-bar=on` moves NVMe's table for blockd. The T14's two xHCI functions each publish
only BAR 0 and armed MSI in the kernel's own bring-up, with no `MSI-X not
armed` line and one remapping entry each (a `testcases` readback): no MSI-X
capability came off either walk, so a claim arms MSI and hands BAR 0 over —
Expand Down
9 changes: 4 additions & 5 deletions issues/no-claim-gets-an-address-space-under-virtio-iommu.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@ A process is handed a PCI function only once the function has an address
space of its own at a unit and, on x86-64, interrupt remapping
(`kernel/src/pcidev/mod.rs`'s header; `Refusal::NotRemapped`,
`Refusal::Untranslated`). The units this kernel programs are VT-d (`DMAR`,
`kernel/src/arch/x86_64/vtd/`) and SMMUv3 (`IORT`,
`kernel/src/arch/aarch64/smmu/`). It finds AMD-Vi units (`IVRS`) and programs
none (`kernel/src/arch/x86_64/iommu_unit.rs`), so a machine whose unit is AMD-Vi,
VirtualBox's `--iommu amd` and QEMU's `amd-iommu` among them, refuses every
claim until `issues/an-amd-vi-machine-isolates-no-device.md` lands.
`kernel/src/arch/x86_64/vtd/`), AMD-Vi (`IVRS`, `kernel/src/arch/x86_64/amdvi/`)
and SMMUv3 (`IORT`, `kernel/src/arch/aarch64/smmu/`). An AMD-Vi machine grants
a claim only where every unit also filters SMIs, which QEMU's `amd-iommu` does
not, so a QEMU guest behind it refuses every claim.

QEMU emulates VT-d, AMD-Vi and SMMUv3, and VirtualBox VT-d or AMD-Vi under
`--iommu` on its ICH9 chipset, off by default (`VBoxManage modifyvm`). Cloud
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ So `amdvi_selftest` (`tests/common/iommu.rs`) holds the record to its
requester and its name, not its address or its access, and the record's
`addr=`/`access=` under QEMU are QEMU's, not the device's.

Owner: the orchestrator's AMD laptop track
(`issues/an-amd-vi-machine-isolates-no-device.md`); the AMD bench's
`usbd` fault row reads a real unit's record at its aimed address.
Owner: the orchestrator's AMD laptop track; the AMD bench's claim of
`03:00.4`, in P1's exit (`issues/there-is-no-wifi.md`), reads a real unit's
record at its aimed address.

**Exit:** under the QEMU `build/ci/qemu-version` names, the record of
`amdvi-selftest`'s refused write names `0x400000000000` and a write, and
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
status: open
kind: tooling
opened: 2026-10-14
---

# QEMU's `amd-iommu` records no fault for a message its table refuses

AMD 48882 rev 3.11 §2.2.5.2, step 7: a message whose remapping entry has
`RemapEn` clear is target-aborted and reported as an `IO_PAGE_FAULT` with `I`
set (Table 57). Under QEMU 11.1.1's `amd-iommu` (`intremap=on,xtsup=on`,
128-bit entries), a claim's holder rewrote its MSI-X entry from the claim's
vector 0x42 to 0x43; usbd's No-Op went unannounced ("the bring-up's No-Op
command: Silent") and no event record was written (PR #921's evidence, `g3`
logs). Whether QEMU dropped the message or delivered it unremapped to 0x43,
which no claim held, was not measured.

QEMU's unit has no SMI filter (`SmiFSup` 0), so under the ruling on #921 it
grants no claim behind it and the arm no longer runs there; the AMD bench's
claim of `03:00.4`, in P1's exit (`issues/there-is-no-wifi.md`), holds the
record and the census.

Owner: the orchestrator's AMD laptop track.

**Exit:** under the QEMU `build/ci/qemu-version` names, a message on an
absent remapping entry is recorded against its requester, and a guest test
asserts the record.
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@ switch-off runs: its negative control reds only on the register half, and the
DMA half is held by the AMD bench's `iommu_firmware_left` row alone, in
whatever run the orchestrator stages.

Owner: the orchestrator's AMD laptop track
(`issues/an-amd-vi-machine-isolates-no-device.md`), whose translation stage
is the next code to rest on what QEMU does on this command.
Owner: the orchestrator's AMD laptop track, whose translation code
(`kernel/src/arch/x86_64/amdvi/`) rests on what QEMU does on this command.

**Exit:** under the QEMU `build/ci/qemu-version` names, `amdvi_firmware_left`
with `switch_off` skipping every field reds on `usb-storage: 1 device(s)`,
Expand Down
10 changes: 4 additions & 6 deletions issues/the-kernel-is-small-interrupts-post-and-threads-wait.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,12 +225,10 @@ Beside that order, each waiting on something outside it:
over `inspect`; no class is bound, and no shipped image runs it.
**Left**, first P1 (order item 4), then the whole xHCI, one usbd per
controller (the orchestrator's stage-0 ruling). pcidev today arms MSI
only on a function with no MSI-X, and maps no BAR that holds the
table (`kernel/src/pcidev/mod.rs`). QEMU's xHCI keeps its MSI-X table
in the BAR that holds the registers, which refuses usbd's claim as it
refuses diskserver's
(`issues/a-controller-whose-msix-table-is-in-bar-0-cannot-be-driven-from-userland.md`,
whose exit P1 rewrites), so QEMU's spare today is
only on a function with no MSI-X, and hands the BAR holding the
table over like any other where the machine remaps
(`kernel/src/pcidev/mod.rs`). QEMU's xHCI keeps its MSI-X table in
the BAR that holds the registers; QEMU's spare today is
`qemu-xhci,msix=off,msi=on` (`XHCI_SPARE` in `tests/common/qemu.rs`).
P1 serves usbd when that spare is `qemu-xhci` with its MSI-X left on
and `usbd_drives_the_spare` passes on it. The T14's xHCI has no MSI-X
Expand Down
27 changes: 18 additions & 9 deletions issues/there-is-no-wifi.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,15 +151,24 @@ page included.** Owner: the orchestrator.
- `issues/a-bar-behind-a-bridge-with-no-free-2-mib-of-its-windows-is-not-handed-over.md`
closed, and each of the two placement issues it names closed or its exit
rewritten to the placement P1 lands;
- the exit of
`issues/a-controller-whose-msix-table-is-in-bar-0-cannot-be-driven-from-userland.md`
rewritten, from a refused write to the table's page to the NVMe driven
through a table in BAR 0.

**P2 and P3, AMD-Vi translation and interrupt remapping.** Owner:
`issues/an-amd-vi-machine-isolates-no-device.md`, held by the orchestrator's
AMD track. Depends on: that track.
- **Exit:** that track's exit, and three readings on the AMD bench:
- on the AMD bench, `03:00.4` (`xhci-leave=03:00.4`, its MSI-X table in
BAR 0, its only BAR) claimed with that BAR in place and driven by usbd
through its own remapping entry, every unit filtering SMIs: the
`usbd_spare`, `usbd_spare_fault`, `end_returns_claims` and
`usbd_spare_vector` jobs pass, the aimed fault is recorded against the
claim at its address, and the holder's rewrite of its entry to a vector
not its own is refused, recorded, and absent from the stop's census of
unclaimed vectors. The row, `amdvi_usbd_drives_its_own_entry`, left the
registered AMD rows with PR #921 (its judge, `amdvi_usbd` in
`tests/toyos.rs` at `c648c1439`, comes back with it):
the bench's port `00:08.1` forwards only `0xd0000000..0xd0500000`, which
bus 3's BARs fill, so the BAR has nowhere routed to move to and stays
where firmware put it, which only 4 KiB pages can hand over.

**P2 and P3, AMD-Vi translation and interrupt remapping.** Owner: the
orchestrator's AMD track. Translation (PR #909) and remapping with the SMI
filter (PR #921) have landed. Depends on: P1, for the claim.
- **Exit:** three readings on the AMD bench:
- the stick's xHCI and the NVMe working in their domains under
translation;
- the AX200's claim handed over with its own remapping table;
Expand Down
11 changes: 4 additions & 7 deletions issues/toyos-runs-in-any-open-source-hypervisor.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,6 @@ and, filed before this track:
- `issues/a-machine-without-an-iommu-refuses-every-claim.md` — bhyve; crosvm
for every device it emulates; and VirtualBox, QEMU and Cloud Hypervisor by
default (`issues/no-claim-gets-an-address-space-under-virtio-iommu.md`).
- `issues/a-controller-whose-msix-table-is-in-bar-0-cannot-be-driven-from-userland.md`
— QEMU's NVMe by default; and every virtio function of Cloud Hypervisor and
crosvm, which keep the MSI-X table and PBA in the BAR their virtio
structures are in.
- `issues/a-hid-interface-outside-the-boot-subclass-is-read-as-qemus-tablet.md`
— VirtualBox's USB tablet, a subclass-0 interface; bhyve's declares the
boot mouse subclass and protocol.
Expand All @@ -53,9 +49,10 @@ and, filed before this track:
- `issues/pci-is-enumerated-on-segment-group-0-alone.md` — Cloud
Hypervisor's `num_pci_segments`.

`issues/an-amd-vi-machine-isolates-no-device.md` is met only where a target is
asked for AMD-Vi, VirtualBox's `--iommu amd` and QEMU's `amd-iommu`, and no
target's default asks.
AMD-Vi isolation (`kernel/src/arch/x86_64/amdvi/`) applies only where a
target is asked for AMD-Vi, VirtualBox's `--iommu amd` and QEMU's
`amd-iommu`, and no target's default asks; QEMU's unit has no SMI filter, so
a claim is refused there.

Three of the launcher's QEMU options keep three of these off every guest
test: the `-non-transitional` virtio devices the transitional id,
Expand Down
45 changes: 41 additions & 4 deletions kernel/amdvi/src/dte.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,58 @@ const SYS_MGT_SHIFT: u32 = 40;
pub const MODE: u8 = 4;

/// What an IVHD device entry's DTE-setting byte declares for its requesters
/// (§5.2.2, Table 106). Of it, a translation entry carries `SysMgt` alone:
/// the pass bits are the interrupt half's, which nothing here sets valid.
/// (§5.2.2, Table 106): the `SysMgt` its translation half carries, and the
/// pass bits its interrupt half does.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub struct Setting(pub u8);

impl Setting {
/// What a function a process drives is given whatever firmware declared:
/// no `SysMgt`, and no NMI, INIT, ExtInt or LINT forwarded unremapped
/// (Table 10), so its device reaches no CPU but by its own vectors.
pub const CLAIMED: Self = Self(0);

/// Bits 5:4, the `SysMgt` firmware declares the requester needs.
pub const fn sys_mgt(self) -> u64 {
(self.0 as u64 >> 4) & 0b11
}

/// The pass bits, at their places in the interrupt half's doubleword
/// (Table 106 bits 0, 1, 2, 6, 7 to Table 7 bits 184, 185, 186, 190, 191).
const fn passes(self) -> u64 {
let byte = self.0 as u64;
(byte & 0b111) << 56 | (byte >> 6 & 0b11) << 62
}
}

/// `IV`, bit 128: the interrupt half is valid.
const IV: u64 = 1 << 0;
const INT_TAB_LEN_SHIFT: u32 = 1;
/// `IntCtl`, bits 189:188.
const INT_CTL_SHIFT: u32 = 60;
const INT_CTL_REMAPPED: u64 = 0b10;

/// Entries in a requester's remapping table: 256, `IntTabLen` 1000b, one
/// per vector, 4 KiB of 128-bit entries.
pub const INTERRUPT_ENTRIES: usize = 256;

/// One entry's interrupt half, bits 191:128, which an interrupt change
/// replaces in one 64-bit write (§2.2.2.2).
pub const fn interrupts_blocked(setting: Setting) -> u64 {
// `IntCtl` 00b: every fixed and arbitrated message target-aborted (Table 9).
IV | setting.passes()
}

/// The interrupt half remapping every fixed and arbitrated message through
/// the 256-entry table at `table` (Table 7: root pointer 179:134, `IntTabLen`
/// 132:129).
pub const fn interrupts_remapped(table: Phys<12>, setting: Setting) -> u64 {
IV | 0b1000 << INT_TAB_LEN_SHIFT | table.get() | INT_CTL_REMAPPED << INT_CTL_SHIFT | setting.passes()
}

/// One entry's translation half, the 128 bits a translation change replaces
/// in one write (§2.2.2.2); the interrupt half above it stays zero, `IV`
/// clear, so the unit forwards a requester's interrupts unremapped (Table 9).
/// in one write (§2.2.2.2); the interrupt half above it is replaced on its
/// own.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Dte([u64; 2]);

Expand Down
Loading