Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
98ae5a7
WIP: stage B in progress (declaration ABI, _CRS reader, acpiserver de…
Japabu Oct 14, 2026
633c4e1
Merge remote-tracking branch 'origin/main' into wt/toyos-aperture-b
Japabu Oct 14, 2026
cd2c246
Stage B: acpiserver declares each PCI root's _CRS windows; kernel dri…
Japabu Oct 14, 2026
d84d8c3
Fail-closed judge reads a wait under a millisecond
Japabu Oct 14, 2026
401b072
The declaration is per root bridge: segment, buses, _CRS windows and …
Japabu Oct 14, 2026
4f5142f
Merge remote-tracking branch 'origin/main' into wt/toyos-aperture-b
Japabu Oct 14, 2026
0a67b01
One window list across roots, bounded by the declaration; a refused d…
Japabu Oct 15, 2026
71bce12
Review round 1: a malformed record refused by name, the root _OSC que…
Japabu Oct 15, 2026
cf31476
Option 1 removed: no identity domain for a kernel driver
Japabu Oct 15, 2026
48af2c2
Merge remote-tracking branch 'origin/main' into wt/toyos-aperture-b
Japabu Oct 15, 2026
de8e988
virt_claim_lpi: VirtIts has two roots, so its root 0 ends at bus 7
Japabu Oct 15, 2026
a18b42b
The per-root record carries the _OSC commit beside the offer
Japabu Oct 15, 2026
a417a64
Merge origin/main (3962d606f) into wt/toyos-aperture-b
Japabu Oct 22, 2026
1ffbdba
Every image whose rows claim a PCI function starts the ACPI server
Japabu Oct 22, 2026
674b636
issues: an aarch64 cargo run boot panics on its console's first queue
Japabu Oct 22, 2026
4e32df6
Merge origin/main (27acd19a6) into wt/toyos-aperture-b
Japabu Oct 22, 2026
e307e60
tests: acpi_server_death is unregistered until its T14 boot has a log
Japabu Oct 22, 2026
3d62570
Merge origin/main (d7f269dbc) into wt/toyos-aperture-b
Japabu Oct 22, 2026
e6dd4ad
Review round 2: true comments, acpi_release goes, owners and wording
Japabu Oct 22, 2026
f9e44e0
supervisor: every acpi holder starts before the disk drivers, whereve…
Japabu Oct 22, 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
35 changes: 35 additions & 0 deletions build/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3066,6 +3066,40 @@ mod tests {
}
}

/// A config whose rows claim a PCI function starts the `acpi` claim's
/// holder: every such claim waits for the root bridges' memory it declares,
/// and one made on a boot where nothing does is refused, as are all after
/// it. `undeclaredcase` is that boot, booted to judge the refusal. A claim
/// a program makes at run time through a `device` syscap is not a row's
/// and is not seen here: its image's own test meets the refusal.
#[test]
fn every_config_that_claims_a_pci_function_starts_its_declarer() {
const UNDECLARED: &str = "tests/images/undeclaredcase.toml";
let root = Path::new(env!("CARGO_MANIFEST_DIR"));
for config in ALL_CONFIGS {
let parsed = parse_config(&root.join(config));
let claimants: Vec<&String> = parsed
.programs
.iter()
.filter(|(name, program)| {
!program.drives.is_empty()
|| (parsed.boot.start.contains(name) && program.devices.iter().any(|d| d.starts_with("pci:")))
})
.map(|(name, _)| name)
.collect();
let declares = parsed
.boot
.start
.iter()
.any(|name| parsed.programs.get(name).is_some_and(|p| p.devices.iter().any(|d| d == "acpi")));
assert_eq!(
declares || claimants.is_empty(),
*config != UNDECLARED,
"{config}: {claimants:?} claim PCI functions, and starting the `acpi` claim's holder is {declares}",
);
}
}

/// Every config renders, so a row the manifest refuses — one that serves a
/// port and is not marked `service` — reds here rather than at a build.
#[test]
Expand Down Expand Up @@ -3157,6 +3191,7 @@ mod tests {
"tests/images/proctreecase.toml",
"tests/images/scalecase.toml",
"tests/images/testcases.toml",
"tests/images/undeclaredcase.toml",
"tests/images/virtclaimcase.toml",
"tests/images/virtjobcase.toml",
"tests/images/virtpaniccase.toml",
Expand Down
12 changes: 6 additions & 6 deletions build/licence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,15 +406,15 @@ pub const COMMITTED_FILES: &[(&str, &str, &str, Terms)] = &[
Terms::Spdx("MIT OR Apache-2.0"),
),
(
"lib/acpi/fixtures/qemu-11.1.1-virt-pptt/pptt.bin",
"fff116c865c8081e748e03fee505f2ed4fc1b01b8fbd3b131a8d1720552c86ec",
"ours: QEMU `virt`'s own PPTT, captured by the commit that decodes it",
"lib/acpi/fixtures/qemu-11.1.1-virt/dsdt.bin",
"908d068d6d70db725bd139ac5a8fe9511373ab59a0570a0f285c3c59d60cd227",
"ours: QEMU `virt`'s own DSDT, captured by the commit that declares its root bridge's `_CRS`",
Terms::Spdx("MIT OR Apache-2.0"),
),
(
"lib/acpi/fixtures/ovmf-pure-efi/root-bridge-0.bin",
"eb00e68be746a09ac7f0ce1ca492ce8c858e3af1152112b49ffb4708883acbfb",
"ours: OVMF's answer on a q35 guest, read off that boot's own loader log",
"lib/acpi/fixtures/qemu-11.1.1-virt-pptt/pptt.bin",
"fff116c865c8081e748e03fee505f2ed4fc1b01b8fbd3b131a8d1720552c86ec",
"ours: QEMU `virt`'s own PPTT, captured by the commit that decodes it",
Terms::Spdx("MIT OR Apache-2.0"),
),
// A bcachefs volume upstream's own tools wrote, gzipped. The bytes inside
Expand Down
4 changes: 3 additions & 1 deletion images/system.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ serves = ["log"]
# firmware's own access: the kernel makes the AML's reads and writes for it,
# inside the fences of `toyos_userbound::firmware`. `power` is how a press
# stops the machine: through the supervisor, which has `logkeeper` make the
# log whole.
# log whole. It declares the memory the PCI root bridges decode out of each
# root's `_CRS`, which every PCI claim waits for, so the supervisor starts it
# before every other row; an image that claims a PCI function starts it.
[programs.acpiserver]
service = true
devices = ["acpi"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ median went from 1,025 to 1,029 µs on the T14 and from 2,054 to 2,086 µs on
the AMD bench, base to head, one boot each, read from the second judgement's
`testcases` readbacks.

Owned by stage S3 of the scheduler design (#872), whose 1:1 pair rule decides
where a pair's wakes go.
Owned by the placement track (`issues/placement-has-no-migration-cost-or-wake-affinity.md`), whose wake-affine
decision is where a pair's wakes go.

**Exit**: S3 decides this case, with a simulator scenario of a wake whose waker
parks next and a metal row that prices the forward against the local switch.
**Exit**: that decision covers this case, with a simulator scenario of a wake
whose waker parks next and a metal row that prices the forward against the
local switch.
58 changes: 24 additions & 34 deletions issues/an-aarch64-cargo-run-boot-hands-over-no-pci-claim.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,43 +6,33 @@ opened: 2026-10-13

# An aarch64 `cargo run` boot hands over no PCI claim

`cargo run -- --arch aarch64` launches `virt` with the GICv3's ITS, on
QEMU's own GIC under HVF (`Arch::irqchip`), which arms it and maps each
claim's DeviceID to its slot's LPI: the same launch by hand with
`its=on,kernel-irqchip=off` (QEMU 11.1.1) showed it. It still hands over
none: diskserver's NVMe, soundserver, netstack and vdagent are each
refused with `its BAR was moved onto 8 64-bit address(es) inside the
windows firmware declared and the function answered at none of them` (lines 343, 418, 450, 487 of
[the boot's log](https://github.com/ToyOSOrg/evidence/blob/e626cced7ab73c04efdbbfd0b98a2f0a0ff8d86b/fix2-60f08b937/aarch64-its-boot.log)).
Each answers at its firmware BAR: `0x0f0107ff`, `0x79000000`,
`0x30bf8024` and `0x00000103` (lines 327, 386, 433, 470). At every
address it is moved onto the read is `0xffffffff`, except the first,
`0x4010000000`, the ECAM base (line 212), which line 225 lists among the
windows firmware declared: there the NVMe's read is `0x00081b36`
(line 327) and the other three's `0x00000000`.
The kernel's own xHCI driver is given no LPI there (`not armed — a driver
in this kernel is given no LPI`) and initialises no controller, so no USB
keyboard reaches the compositor, which panics on "the manifest gives this
program the keyboard".
`cargo run -- --arch aarch64` launches `virt` with the GICv3's ITS and
the SMMUv3, with the console's virtio-serial function behind the unit
(`build/qemu.rs`). It reaches no userland: the kernel panics in
`kernel/src/drivers/virtio.rs`'s `wait_used`, its console's first queue
having completed nothing in five seconds.

So on aarch64 the clipboard agent's claim and its handshake are unbooted:
its function is declined by the console driver and the console comes up
on the next one, and the agent itself exits on "no virtio-serial function
was handed to this program". Its handshake on aarch64 rests on the code
that passes on q35.
The kernel's virtio console comes up before the `acpi` claim's holder has
declared the root bridges' windows, and every domain is built clear of
them (`kernel::iova`), so the console is given no domain of its own
(`iommu: no domain of its own for a device: no declaration of the root
bridges' windows is held`) and keeps the machine's addresses. On VT-d that
is the identity domain: the console works, but loses the domain of its own
`main` gives it, built clear of the loader's window list. On the SMMUv3 a stream with no domain aborts what it is
given, so the console's DMA reaches nothing.
Measured at #935's merge of `main` (`3962d606f`), QEMU 11.1.1 under HVF.

The windows a claim's BAR is checked against come from the loader's list, which takes every
memory range firmware's map types MMIO and nobody owns, ECAM and flash among
them; the root's own `_CRS` on this machine declares `0x10000000..0x3eff0000`
and `0x8000000000..0x10000000000` and neither. Taken up by #916: its stage A
holds the declaration as a kernel state, and its stage B makes acpiserver
declare each root's `_CRS` on both architectures, which is what hands these
four claims over and closes this file.
Before that merge a domain was placed above the CPU's addresses where the
unit translates wider, which needed no declaration. `main`'s IOVA
allocator removed that rule, and `main` still builds its domains clear of
the loader's window list, which #935 removes. #935's own boot at
`4f5142fea`, before the merge, handed over all four claims (diskserver's
NVMe, soundserver, netstack, vdagent).

Owner: the arm64 track, `issues/toyos-runs-on-arm64.md`, whose stage 7
exit (`ssh` from the host, the desktop on virtio-gpu) a `cargo run` boot
cannot meet until this is fixed.
Owner: the PCI apertures track (#935). How a driver in this kernel gets a
domain before any declaration is the owner's decision. The arm64 track's
stage 7 exit (`issues/toyos-runs-on-arm64.md`) waits on it.

**Exit condition.** A `cargo run -- --arch aarch64` boot hands over a
claim: its console shows diskserver serving its NVMe partitions and
claim: its console shows the disk driver serving its NVMe partitions and
`vdagent: port 1 open`.
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ bound (PR #874's fourth round). An 8-vCPU guest showed the pass's time going to
process's fair-share lock, which the storm's other waiters were taking as
they re-parked.

Owned by stage S3 of the scheduler design (#872), which decides where a
pair's wakes go: a wake whose waker parks next is cheapest left here
Owned by the placement track (`issues/placement-has-no-migration-cost-or-wake-affinity.md`),
whose wake-affine decision is where a pair's wakes go: a wake whose waker parks
next is cheapest left here
(`issues/a-wake-whose-waker-parks-next-is-forwarded-to-an-idle-cpu.md`).

**Exit**: a wake posted on its own CPU by a poster that keeps running reaches
Expand Down
31 changes: 31 additions & 0 deletions issues/an-x86-machine-with-no-acpi-row-hands-over-no-pci-claim.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
status: open
kind: defect
opened: 2026-10-15
---

# An x86 machine with no ACPI row hands over no PCI claim

Every PCI claim waits for the holder of the `acpi` claim to declare the root
bridges (`kernel/src/pcidev`'s `declare`, made by `/system/bin/acpiserver`),
and on x86-64 that claim exists only over a row `acpi_mode::init`
(`kernel/src/arch/x86_64/acpi_mode.rs`) fills. It fills none where the FADT is
unusable, its fixed hardware is refused (`HW_REDUCED_ACPI` among the
refusals), no PM1a control block is declared, its `SMI_CMD` was not declared,
no I/O APIC carries the SCI, or the SCI's line is another row's. On such a
machine acpiserver gets no claim and declares nothing, so the first PCI claim
waits its 30 s deadline and every claim of the boot is refused, the disk
drivers' among them. Before #935 the loader's window list handed those
claims over.

Measured: none of the machines in reach is such a machine. q35, the
ThinkPad T14 and the AMD bench each fill the row (their `acpi: the ACPI row:`
lines); a hardware-reduced x86 guest (Cloud Hypervisor, QEMU's `microvm`)
would not, as `issues/a-hardware-reduced-x86-machine-has-no-power-off-and-no-power-button.md`
records for its power-off.

**Owner**: the ACPI track
(`issues/toyos-runs-the-machine-in-acpi-mode-and-interprets-its-aml.md`).

**Exit**: such a machine declares its root bridges through a path that needs
no `acpi` claim row, or the gap is measured absent on every supported machine.
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@ opened: 2026-09-13
# No boot reaches the refusal that keeps a BAR read inside a declared window

`kernel/src/pcidev`'s `bars` withholds a BAR with `Refusal::BarUnrouted` when
the pages firmware assigned it lie inside no window firmware declared: it is
the pages firmware assigned it lie inside no declared root-bridge window: it is
the check that keeps a holder from issuing a load no bridge forwards, which on
real hardware does not complete. Nothing in this tree reaches it.

- q35's virtio-net answers at `0x800000000`, inside the window
`EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL::Configuration()` names, so `netcase`
takes the other branch.
- q35's virtio-net answers at `0x800000000`, inside the window its root's
`_CRS` declares, so `netcase` takes the other branch.
- QEMU's e1000e answers at `0xc0060000`, inside `mem 0xc0000000..0xc0100000`,
so `e1000case` takes it too.
- The ThinkPad T14's I219 answers at `0xbcf00000`, inside
Expand All @@ -25,5 +24,5 @@ is pure and tested over the T14's own windows.

**Exit condition.** A boot whose claimed function's firmware-assigned BAR lies
inside no declared window — a guest config that hands over a function QEMU puts
outside the window OVMF declares, or a machine that has one — with an arm that
outside its root's `_CRS`, or a machine that has one — with an arm that
asserts the `keeps BAR` record and that the holder was given no window for it.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ opened: 2026-10-07
`SMI_CMD` on the boot processor: a caller on another CPU kicks it and spins
until it has written. The T14's `acpi_server_death` row reads that crossing
for the enable, because its job claims from a thread it found off the boot
processor and the judge refuses `asked from cpu0` there. Three things it
processor and the judge refuses `asked from cpu0` there. That row is
unregistered, with its job and judge, until its boot has a log
(`issues/the-acpi-servers-death-has-no-readback-on-the-t14.md`); what
follows is of the row as it was and comes back with it. Three things it
leaves to the scheduler or does not read:

- **The disable asked from another CPU.** `acpi_mode::release` runs in the
Expand All @@ -28,8 +31,7 @@ leaves to the scheduler or does not read:
kick or from a spin.
- **The enable's arrangement has a window.** The job's claiming thread reads
its x2APIC id and then enters the kernel
(`tests/guest/rust-tests/src/bin/acpi_release.rs`,
`claim_off_the_boot_processor`). The kernel moves no running thread, so the
(`acpi_release`'s `claim_off_the_boot_processor`). The kernel moves no running thread, so the
asker is the CPU the thread read, unless a preemption between that read and
the kernel's lock in `smi_cmd::write` queues the thread behind another and
an idle boot processor takes it. With no affinity in the tree the window
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Owned by `issues/toyos-runs-the-machine-in-acpi-mode-and-interprets-its-aml.md`.

**Exit**: the release's `legacy mode again` line says what the PM1 enable
register and the GPE0 enable bytes read at the write on the T14's
`acpi_server_death` boot, and either every one reads zero there by the
`acpi_server_death` boot, once it is registered again
(`issues/the-acpi-servers-death-has-no-readback-on-the-t14.md`), and either every one reads zero there by the
release's own doing or this file is promoted with what the firmware did with
the ones that did not.
37 changes: 37 additions & 0 deletions issues/the-acpi-servers-death-has-no-readback-on-the-t14.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
status: open
kind: defect
opened: 2026-10-22
---

# The ACPI server's death has no readback on the T14

`acpi_server_death` judged the T14's boot of
`tests/images/acpicase.toml`: its job, `acpi_release`, makes the boot's first
`acpi` claim off the boot processor, hands it to `/system/bin/acpiserver` and
kills the server, and the judge reads the kernel's `ACPI_ENABLE` and
`ACPI_DISABLE` lines off the log the boot leaves on its stick. That log
reaches the stick through usbd's claim of the boot controller.

Every PCI claim waits for the `acpi` claim holder's declaration of the root
bridges' memory (`kernel/src/pcidev`'s `declare`), and the supervisor makes
the disk drivers' claims while it starts its rows, before any job runs
(`Supervisor::boot`). On a boot whose first holder is a job no row declares,
so usbd's claim would wait out its 30 s deadline and leave every claim of the
boot refused. `acpicase` therefore starts no storage, and the row has no log
to judge. Measured at #935's `4e32df68c`: the boot ran its job and stopped
cleanly (its black box reads `Rebooting.` and DONE), and `kernel.log` came
back empty. The row's registration, its judge and its job,
`tests/guest/rust-tests/src/bin/acpi_release.rs`, went with #935: nothing
else runs the job, and QEMU's firmware hands over in ACPI mode, so its
release has no legacy mode to return to.

**Owner**: the ACPI track
(`issues/toyos-runs-the-machine-in-acpi-mode-and-interprets-its-aml.md`).

**Exit**: `acpi_server_death` is registered again, with its job and its judge
(`ACPI_ENABLE` asked off the boot processor for the job's claim,
`ACPI_DISABLE` written and `SCI_EN` read clear at the server's death, the
FACS outside `AddressRangeMemory`) restored from the tree before #935, on a boot
that runs its job with the job's claim the boot's first and whose log reaches
the stick, and it passes on the T14.
30 changes: 0 additions & 30 deletions issues/the-gcd-maps-held-mmio-descriptors-are-logged-nowhere.md

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ workloads at 8, 16 and 64 CPUs, the worst stretch measured is 17 rounds against
a bound of 66. It is the derivation that is missing, not a schedule that
crosses it.

Exit: stage S2 of the scheduler design (#872) replaces the most-surplus victim
with two random choices and the cursor push with a nearest-first one, and
derives I15's bound for that protocol over any number of victims.
Owned by the placement track (`issues/placement-has-no-migration-cost-or-wake-affinity.md`),
which owns the push and the victim choice.

Exit: I15's bound is derived for the shipped balance protocol over any number
of victims.
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ kicking a busy target (PR #874), two of their claims stop being shown:
CCX. A neighbour that looks loaded and has nothing stealable needs load the
fair band does not express.

Owned by stage S2 of the scheduler design (#872), which replaces the push and
the victim choice and derives I15's bound for them.
Owned by the placement track (`issues/placement-has-no-migration-cost-or-wake-affinity.md`),
which owns the push and the victim choice and the derivation of I15's bound
for them.

**Exit**: S2's tests assert that a sampled schedule comes within half of I15's
**Exit**: the simulator's tests assert that a sampled schedule comes within half of I15's
bound at both benches' shapes, and `group_imbalance` stages CCX load no probe
takes at the shipped threshold.
3 changes: 1 addition & 2 deletions issues/toyos-runs-in-any-open-source-hypervisor.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@ only by the `watchdog` parameter and refused by name elsewhere), netstack's
with no PMU, `kernel::cpu`'s reading of `CPUID.1:ECX[31]`, which Intel
defines as "Not Used: Intel processors always return 0. Allocated for use by
software emulation" (Intel SDM Vol. 1, 253665-093US, Table 21-9) and AMD as
the hypervisor's (APM Vol. 3 §E.3.2), and the loader's read of PI's DXE services (`loader/src/gcd.rs`),
which hands over the root bridges' windows alone where firmware has none.
the hypervisor's (APM Vol. 3 §E.3.2).

## Each hypervisor, x86-64

Expand Down
Loading