Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
@@ -0,0 +1,130 @@
---
status: open
kind: defect
opened: 2026-10-08
---

# A firmware call does what its handler chooses, and the kernel bounds only the call

A byte the `acpi` claim's holder stores to the FADT's `SMI_CMD` is a call into
the firmware, which the kernel makes for it
(`call`, `kernel/src/arch/x86_64/acpi_mode.rs`; decided by
`toyos_userbound::firmware::port`). The byte selects a handler that runs in
system management mode, above the kernel, and reads whatever the holder wrote
to firmware's memory before the call, which the mediated access passes both
ways (`issues/the-acpi-claims-holder-reaches-every-port-and-firmware-range-the-kernel-did-not-declare.md`).
So a bug in `/system/bin/acpiserver`, or AML it runs, reaches whatever the
machine's firmware does on any byte with any argument block.

What the kernel bounds:

- **Who**: a process that holds the claim, bound to it.
- **When**: never once the stop has begun.
- **Where**: the boot processor, read beside the `out` (ACPI 6.5 Table 5.9,
as `kernel/src/arch/x86_64/smi_cmd.rs` quotes it).
- **Which byte**: none the FADT gives a meaning, `ACPI_ENABLE`,
`ACPI_DISABLE`, `S4BIOS_REQ`, `PSTATE_CNT` and `CST_CNT`, each the kernel's
own command or nobody's. ACPI 6.5 Table 5.9 names no other value for the
port. A zero names none in four of them; `S4BIOS_REQ` names the byte it
holds, zero too, where the FACS's `S4BIOS_F` is set, and none where it is
clear (`SmiCmd::named`, `toyos-acpi/src/fadt.rs`, which quotes each). A
write wider than a byte that reaches the port is refused whole.
- **How often a byte is written to the command port**: eight in any second
(`firmware::CALLS`, `toyos-userbound/src/firmware.rs`), counted for every
holder there has been. The ninth is refused to the caller by name and
written nowhere. That is a bound on writes to `SMI_CMD` and on nothing
else that raises a firmware interrupt: the chipset's own SMI enable
register sits at a port no table names and nothing declared, so the holder
writes it as it writes any port
(`issues/the-acpi-claims-holder-reaches-every-port-and-firmware-range-the-kernel-did-not-declare.md`).
Eight a second is no bound on the firmware interrupts a holder can ask
for.

What it does not:

- **What the handler does.** Nothing reads the argument block, and nothing
could hold it to a meaning: the handlers are the machine's maker's.
- **How long a call holds the machine.** The write returns when the handler
does, with the boot processor's interrupts closed in `smi_cmd::answer` and
the asker, where it is another CPU, spinning under the mediation's lock
with preemption off; on the T14 a software interrupt to the firmware stops
every CPU. The kernel counts and times each
(`toyos_abi::counters::Counter::FirmwareCalls` and `FirmwareNanos`, and a
line for the first call of each byte) and can end none: system management
mode takes no interrupt of this kernel's, the NMI among them, which is
held pending until the handler returns (Intel SDM Vol. 3C, "NMI Handling
While in SMM").

What a handler that outlasts each of the kernel's bounds meets
(`kernel/src/arch/x86_64/smi_cmd.rs`, decided by `kernel::bootwrite`). The
decision is host-tested; the rest is by reading and staged nowhere, since no
guest's call runs a handler:

- **The kick's bound has no handler in it.** The asker gives the boot
processor `DEAF_CPU`, 5 s, to say it has the write, which it says before
the `out`. A handler's time is never read as a boot processor that takes no
interrupt.
- **A handler that holds the boot processor 5 s is a panic that says so**,
whichever CPU asked: `smi_cmd: the firmware has held the boot processor in
its handler for the 0x.. written to SMI_CMD and has not returned` from an
asker on another CPU that ran meanwhile, at 5 s from the take; and
`smi_cmd: the firmware held the boot processor ..ns in its handler` from
the boot processor itself once the write retires, which is the one that
speaks where the asker was the boot processor. Where the interrupt stopped
an asker on another CPU too, either may speak: the asker resumes with the
clock past the span and the round not yet published, and may panic first
with "has not returned" of a handler that has. Either message names the
firmware and the byte. So a handler that returns after 5 s still ends the
machine: the kernel gives up no CPU for that long, as it gives up none to
a TLB shootdown.
- **The hard-lockup bound, on an image that names a boot deadline**
(`kernel/src/hardlockup/mod.rs`; half the deadline). Its sample is an NMI,
delivered to the boot processor when the handler returns and before the
kernel's own judgement above. It finds `IF` clear in `smi_cmd::answer` and
no interrupt taken, and where that has lasted its bound it seals a `WEDGED`
record that names cpu0 and that `pc` and resets the machine, unless an
asker's panic stood it down first. Whether the counter that raises the
sample counts in system management mode on the T14 is unread.
- **The boot deadline, on such an image** (`kernel/src/deadline.rs`), is
polled from a timer entry, and none is taken while every CPU is stopped:
the first tick after a handler that outlasted it seals `the boot deadline
expired`, where nothing above took the seal.
- **A handler that never returns.** Where its interrupt stopped every CPU,
no instruction of this kernel runs again and it says nothing: the machine
is the firmware's, and a hand on the power button ends it. Where it
stopped the boot processor alone, an asker on another CPU panics at 5 s
with the words above. Asked on the boot processor itself, nothing waits on
the write, and the next wait on cpu0 speaks: a TLB shootdown's `DEAF_CPU`
panic, or the boot deadline on an image that names one. A machine on which
neither comes is not ended by this kernel.
- **A machine whose FADT names no `SMI_CMD`.** Nothing is declared there, so
its chipset's command port is a port like any other and the holder writes
it with no bound at all.

What is measured. Eight a second is this kernel's own number and no
measurement: the model of the T14's AML that was read makes two calls in one
evaluation at the most, and retries a call its handler has not answered once
a millisecond, ten thousand times; eight holds that storm to eight calls a
second and refuses the rest, and whether it refuses a call a healthy machine
needs is unread. No call but the kernel's own enable and disable has been
made on the T14: the server passes no write its AML asks for to the kernel
yet (`userland/acpiserver/src/host.rs`), so nothing there asks for one. On QEMU's q35 the chipset model keeps the byte and its
`SMI_EN` reads 0, so no guest's call interrupts a firmware, and what a guest
reads of one is that it was written, where, and how often.

The server holds no bound of its own on the calls one evaluation makes: that
is the slice's that evaluates the methods which call.

Owned by `issues/toyos-runs-the-machine-in-acpi-mode-and-interprets-its-aml.md`,
beside `issues/the-acpi-servers-holder-drives-the-embedded-controller-unfiltered.md`.

**Exit**: a call is made only for a byte the machine's loaded tables store
to the port, checked by something other than the holder, or the owner rules
the bound above is the one ToyOS keeps; and the rate is held against the
calls a T14 row reads the machine's own AML making, with the time each held
the boot processor; and the span is ruled. The slice that passes the first
write its AML asks for to the kernel reads on the T14 the time each call its
AML makes holds cpu0, and the owner rules, against those readings, whether a
handler that returns after `DEAF_CPU` ends the machine. Where the interrupt
stops every CPU the kernel such a handler returns to is whole, so the panic
there is this kernel's choice. That slice does not land without the ruling.
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ What the kernel's declarations do not follow:
where it counts the SMI its own `SMI_CMD` write raises.
- **A machine whose FADT names no `SMI_CMD`.** Nothing is declared there, so
the chipset's software-SMI port is a port like any other and the holder
writes it; the write is refused by name only where the FADT names the port.
writes it; a byte for it is the kernel's to write, on the boot processor
and under its bounds, only where the FADT names the port
(`issues/a-firmware-call-does-what-its-handler-chooses-and-the-kernel-bounds-only-the-call.md`).

So a bug in `/system/bin/acpiserver`, or AML it runs, can reach those; the
kernel bounds where, and not what. The owner's ruling on the server reading
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,33 @@ are this by reading, those kernels not reading the count.

**Owner**: stage 1 of
`issues/toyos-runs-the-machine-in-acpi-mode-and-interprets-its-aml.md`,
for the fix and for the reading: its exit holds the count flat on the T14 over
this exit's interval, so the stage builds the row, and it reads the count
through the general counters ("General counters", owner, 2026-10-03,
for the fix and for the reading: its exit holds the count on the T14 to what
ToyOS asked for over this exit's interval, so the stage builds the row, and
it reads the count through the general counters ("General counters", owner, 2026-10-03,
`issues/toyos-explains-itself.md`).

**Exit**: a T14 row reads `MSR_SMI_COUNT` on every CPU after init is spawned
and after the boot's last write to `SMI_CMD`, whoever makes it, and again at
the stop's report at least 4.444 s later, two of the longest period read, and
on every CPU the two agree. The interval opens after that write because the
ACPI enable is one, a write of `ACPI_ENABLE` to `SMI_CMD`, and raises one
firmware interrupt where `APMC_EN` is set.
**Exit**: every SMI in the interval is one ToyOS asked for. A T14 row reads
`MSR_SMI_COUNT` on every CPU and the boot processor's `firmware_calls`, the
kernel's count of its writes to `SMI_CMD`, after init is spawned and after
the ACPI enable, and again at the stop's report at least 4.444 s later, two
of the longest period read; and on every CPU the SMI count's delta equals the
boot processor's `firmware_calls` delta over that interval. A write to
`SMI_CMD` raises one firmware interrupt where `APMC_EN` is set, so the
interval may hold such writes, and each is counted on both sides.

Two things the equality rests on, which the row answers before it is read
over an interval that holds a call:

- **A round of the counters is not one instant.** A call that lands between
one CPU's sample and the boot processor's leaves that CPU's SMI delta one
short of `firmware_calls` with no unasked interrupt in it. The row takes
its two reads where no call is in flight, or bounds the difference by the
calls in flight.
- **"One command moves every CPU's count by exactly one" has been read for
the enable alone**: every CPU read the count the writer read after it. The
disable's line reads the boot processor's count only, and no other byte has
been written on the T14.

The measure holds unasked interrupts to none and says nothing of how many
ToyOS asks for: that is the rate's
(`issues/a-firmware-call-does-what-its-handler-chooses-and-the-kernel-bounds-only-the-call.md`).
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,9 @@ fixed event that needs no AML, and takes the EC's events. **Exit**: QEMU's
cleanly, through ToyOS's own power-off path (`SYS_SHUTDOWN`), with the press
and that stop in the boot's log. On the T14, `counters` reads
`MSR_SMI_COUNT`, through the general counters and not by a check of its own,
flat on every CPU over the interval the firmware issue's exit defines, and the
machine still in ACPI mode; `acpi_server_events` reads each EC query number
and holds the count to what ToyOS asked for over the interval the firmware
issue's exit defines, every CPU's delta equal to the boot processor's
`firmware_calls` delta, and the machine still in ACPI mode; `acpi_server_events` reads each EC query number
once with its count; and `acpi_server_death` kills the server and reads
`SCI_EN` clear in `PM1_CNT` afterwards, the kernel having written
`ACPI_DISABLE` to `SMI_CMD`.
Expand Down Expand Up @@ -322,6 +323,66 @@ mediated access, leaves open:
no second reading; it goes when the owner rules one in, and a guest test
then supplies a sleep type that is not the machine's and reads it refused.

What the firmware call, which the kernel makes for the server where its AML
stores a byte to `SMI_CMD`, leaves open. The server's AML makes none yet: its
host denies every write AML asks for and passes none to the kernel
(`userland/acpiserver/src/host.rs`).

- **What a call does there is the firmware's**, and the kernel bounds who,
when, where, which byte and how often:
`issues/a-firmware-call-does-what-its-handler-chooses-and-the-kernel-bounds-only-the-call.md`.
- **Eight calls in any second is no measurement**
(`toyos_userbound::firmware::CALLS`), and it bounds a count of calls, not
the time they hold the machine: the T14's enable held the boot processor
2.0 to 2.1 ms on three boots and stopped every CPU, so eight a second is
about 16 ms of the whole machine in every second if a call costs what the
enable does, and no call's cost has been read. Owner: this stage.
**Exit**: the slice that evaluates the methods which call brings the T14's
count of them and the time each held the boot processor, from the
`counters` row's `firmware_calls` and `firmware_nanos` and the kernel's
line for the first call of each byte; it reads what eight a second does to
the audio and latency rows on the T14, a timing verdict coming only from
there; and the owner rules the number against them.
- **The `counters` row holds every CPU's SMI count to the commands the
kernel wrote to `SMI_CMD`** between its first read and its last, and to
nothing else, where it held the count flat; with no call made the two are
one judgement. It rests on one reading, that the enable moved every CPU's
count by one: a call that moves a CPU's count by none or by two reds the
row, and is a reading for the owner and no flake. That equality is the
exit of
`issues/the-t14s-firmware-interrupts-every-cpu-every-2-2-s-under-toyos.md`,
which names the two things it rests on. Before the row is read over a span
that holds a call, the slice that evaluates the methods which call takes
the row's two reads where no call is in flight, or bounds each CPU's
difference by the calls in flight; and reads every CPU's SMI count either
side of one real call, which only the enable has been.
- **No guest's call interrupts a firmware, and no guest writes the enable.**
q35's chipset keeps the byte, with `SMI_EN` reading 0, and its firmware
hands the machine over in ACPI mode. So `acpi_mediated_access` reads that
a call was written, on which CPU, how often and counted, and that the
kernel's own commands were not; the time a handler takes, and the SMI
count either side of one, are read on the T14 alone, and there only for
the enable and the disable.
- **`acpi_mediated_access` needs one of three calls to be asked off the boot
processor**, each from a thread that read itself there first. The tree
has no affinity (`issues/no-test-can-hold-a-thread-on-a-named-cpu.md`), so
a thread preempted between that read and the kernel's lock may be taken by
the boot processor, the window
`issues/no-t14-row-arranges-an-acpi-disable-asked-off-the-boot-processor.md`
describes. A boot where all three fall in it reds as `no firmware call was
asked from another CPU`, over three kernel lines reading `asked from cpu0`
beside the probe's own line naming three non-zero x2APIC ids: that is the
window and no defect of the write, and it is answered by the pin, never by
a second run. None has been read.
- **`acpi_mediated_access`'s storm is refused only if nine calls fit in one
second of the guest's clock.** The probe asks one after another and needs
the ninth refused `CommandRate`. A guest whose host gives it less than
nine calls' worth of time in a second of its own clock is refused none,
and after ten thousand calls reds as `firmware calls in a row were made,
and none refused`: a dependence on rate in a guest test, the host's load
and no defect of the bound, which `toyos-userbound`'s host test holds on a
clock of its own. None has been read.

The press issue's measurement of 2026-10-07 found the three presses it lost
changing nothing its scout read, with the button's event enabled and no SMI
taken, and its hypothesis is that the controller wants the firmware's
Expand Down
Loading
Loading