Skip to content

The ACPI server is trusted platform code: the kernel makes the firmware's writes but into RAM, the IOMMU, an interrupt controller, a held device or the kernel's power limit (design) - #906

Draft
Japabu wants to merge 1 commit into
mainfrom
wt/toyos-acpi-trusted

Conversation

@Japabu

@Japabu Japabu commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

Design round. Nothing is built. The branch is one empty commit (8fec83d). The build round drops that commit once the design below is accepted in a comment on this pull request. Round 3 redesigns #906 under the trust model the owner approved on 2026-10-13, as the orchestrator's brief for this round records it. A trust-model pull request is carrying that model into root CLAUDE.md. The model says:

  • ToyOS trusts the CPU and chipset vendor and the platform firmware, AML and SMM included, because they already hold the whole machine.
  • Inside that trusted base, a check is a bug fence. It is kept where it is cheap and catches our own mistakes, and it never withholds a feature for want of a vendor document.
  • A privilege beyond a normal device claim is granted only by name, in the signed image's system.toml, to a program shipped in that image.

One standing ruling applies. It is the orchestrator's, as owner of issues/the-kernel-owns-cpu-performance-state.md, not the owner's: MCHBAR 59A0h (PACKAGE_RAPL_LIMIT) belongs to the kernel's performance track.

What this brings back

  • The T14. Both _INIs run.
    • EC._INI writes byte 0x01 to the GPIO pad at 0xFD6E0A70.
    • HKEY._INI writes MCHBAR +0x5F50 and +0x5F54, and they are made. Its writes to +0x59A0 and +0x59A1 are refused KernelRegister. They are not made, and the evaluation goes on (§3 below).
    • Sources: host trace, pr-875 round5/facts/host-trace-pc00-ec-hkey-ini.txt, lines 98, 230–240 and 256.
  • The AMD bench. _REG(3, 1) and _PSR run. ALIB's mailbox writes 00:00.0 offsets 0xB8 and 0xBC through ECAM, and those writes are made. The battery reading names the adapter AC online or AC offline, not AC unknown.

Design

1. The grant: the acpi claim, by name, in the image's manifest

images/system.toml and images/console.toml already name acpiserver as the one holder of devices = ["acpi"]. No shipped manifest gives any program syscap = ["device"], which is what could mint the claim at run time. That name is the grant, so no key is added.

The build adds one clause to that row's comment: the acpi claim hands the program the firmware's own access, and the kernel makes the AML's writes for it inside the fences below.

A test image (tests/images/acpicase.toml) mints the claim for a probe shipped in that same image.

2. The kernel makes the firmware's writes, fenced

toyos_userbound::firmware decides as it does today, with three changes.

Memory. A write passes wherever a read passes today. An unlisted address, or memory typed EfiMemoryMappedIO, is written under the read's conditions:

  • at or above 1 MiB;
  • inside the direct map;
  • typed uncacheable by the boot processor's range registers;
  • no CPU's range registers differ from the boot processor's.

That arm's if !write goes. These refusals stay, unchanged:

  • UsableMemory: RAM.
  • DeviceMemory: the LAPIC window, every window the kernel drives, and every recorded BAR's pages. That covers the I/O APICs, the HPET and the IOMMU units, VT-d and AMD-Vi alike (iommu_unit::register_window maps them as driven windows).
  • TableWrite, FacsWrite, Unmapped, Straddles.
  • RegisterCached and RangeRegistersDiffer, which now also hold the RAM fence for memory the map does not list.

Configuration space. A write passes, made through the ECAM window, with three fences. A write is held to the same shape as a read (ConfigSpan, ConfigUnreachable).

refused where why
HeldFunction the whole space of a function that a claim holds or that a kernel driver masters the bus of, and of every function of class 08h subclass 00h (interrupt controller) or 06h (IOMMU) a held device's config space is its driver's. The IOMMU and the interrupt controllers are the kernel's whether or not it programs them (PCI Code and ID Assignment Specification, base class 08h).
AddressMap 00h–3Fh of every function: the Type 0 and Type 1 headers (PCI Express Base Specification §7.5.1) the header holds every function's decode enables and BARs, and every bridge's bus numbers, windows and bridge control. The kernel enumerated and placed by them, and a held device behind a bridge lives by them.
AddressMap 40h–BFh of 00:00.0, on the five host bridges 631122 covers 631122 §3.1.13–§3.1.34. These registers place RAM (TOM, TOUUD, TOLUD, BDSM, BGSM, TSEGMB, DPR, PAM), the ECAM (PCIEXBAR 60h) and MCHBAR (48h), which the fence below keys on.

ConfigWrite is deleted.

MCHBAR's kept registers. Two registers inside MCHBAR are the kernel's, on the five host bridges 631122 covers. A write overlapping either is refused KernelRegister. Reads pass.

register offset owner 631122
PACKAGE_RAPL_LIMIT 59A0h–59A7h the performance track (the standing ruling) §3.3.31
INTRDIRCTL 5418h–541Bh interrupt delivery: "the MSI redirection logic" §3.4.5

How the kernel finds them:

  • The DIDs are 9A02h, 9A04h, 9A12h, 9A14h and 9A1Ah (631121-012 Table 15-3; 631122's title page). The T14 reads 8086:9A14.
  • At the ACPI row's init, after enumeration, the kernel reads 00:00.0's DID and its MCHBAR at 48h, with enable bit 0 and a 128 KiB window (§3.1.14). These are configuration reads only. Nothing is unhidden, and no line is printed.
  • The rows are refused even where firmware types the page reserved: the check runs before the type check, as DeviceMemory's does.

No other host bridge has a row:

  • the H line's 9A26h and 9A36h;
  • q35's 29C0h;
  • AMD's 1630h.

On those, every write that passes the other fences passes.

Index windows. None is fenced, because no document we hold maps a target behind one on a bench part. On the AMD bench, 00:00.0's 0xB8/0xBC reaches the SMN. Review 1's table of every held AMD document stands: none maps 0xB8/0xBC on Family 17h Model 60h. 55772 and 55901 map sibling parts' SMN, not this part's.

The P2SB sideband is not fenced. That includes its ITSS port, C4h (631119-007 Table 109), which holds the 8259/IOxAPIC routing and polarity. A fence would need the kernel to learn SBREG_BAR, which the T14's firmware hides behind P2SBC.HIDE (631120-002 §3.1.22). That means a configuration write to unhide a device, open to an SMI taken in between. That is not cheap, and a mistake of ours that lands on port C4h exactly is not likely. This departs from the brief's interrupt-controller fence by name. The orchestrator may overrule it, and the cost of doing so is round 2's P2SB machinery.

3. The server: a kept register's write is not made, and the evaluation goes on

system/acpiserver/src/host.rs, Firmware::write:

  • KernelRegister. The write is not made. The server says it the first time per register, as one quotable line by kind, with the address under OWN. It answers the interpreter Ok, so the evaluation goes on.
    • Every other refusal still denies the evaluation, as today.
    • Why not deny: ACPI gives AML no notion of a refused write. A register the kernel owns by declaration is one the firmware's write is superseded on. Continuing is the one outcome that keeps the rest of the firmware's method: on the T14, DYTC records its mode in NVS at 0x949B8F04 after CTCN returns (trace line 256).
  • The first write per window. The server says the first write the kernel made into each 4 KiB page outside firmware-typed memory (the answer's memory_type is UNLISTED or EfiMemoryMappedIO, ECAM pages included), and into each function by PCI_Config. That is one quotable line by kind, with the address, width and value under OWN. This is the trust model's record of what the firmware wrote where no fence looks.
    • It lives in the server, not the kernel. The server keeps a Ledger already and is told each answer's memory type, and the kernel keeps no per-claim state for it.

What else changes

  • ABI. toyos_abi::acpi::Refused loses ConfigWrite and gains HeldFunction, AddressMap and KernelRegister. MemoryType stays, for types no access passes. No syscall is added.
  • Kernel.
    • acpi_mode: the MCHBAR read at init, and the configuration write path, which makes a passed write through pci::function_window.
    • pcidev: one predicate, whether a function is held: kernel-driven, claimed, or of class 0800h/0806h.
  • build/ fetches nothing new. The build round's agent transcribes the DID list from 631121-012 into the policy's source.
  • Issues.
    • The build deletes issues/the-t14s-controller-and-hotkey-ini-are-refused-for-writes-outside-the-map.md and issues/the-amd-benchs-ec-reg-and-psr-are-refused-for-a-write-to-the-root-complexs-index.md: both exits are met by the rows below.
    • It edits the AML track's Finished bullet, issues/toyos-runs-the-machine-in-acpi-mode-and-interprets-its-aml.md, as the issue it takes up. The two named exceptions go, and the AMD adapter's AC unknown goes.
    • It files one defect, issues/the-t14s-thermal-modes-do-not-move-its-package-power-limits.md:
      • DYTC's PL1/PL2 writes to 59A0h are refused KernelRegister, so Lenovo's thermal modes do not move PL1/PL2.
      • Owner: the performance track (the orchestrator).
      • Exit: that track exposes a way for a firmware mode to move the kernel's declared limits, and the T14 reads PL1 back at the mode's value after DYTC. Or the owner rules that ToyOS does not follow the firmware's modes, and the row says so.
  • The policy's module header (lib/userbound/src/firmware.rs) states the contract: what the kernel fences, and what it does not fence (the residuals in the roast). It carries the closed issues' one durable line: a write the firmware's AML makes is made unless a named fence refuses it.

How the earlier reviews bind

review finding under the trust model
R1 AMD documents table: no held document maps 0xB8/0xBC on 17h M60h binds as fact. It is why the AMD index window has no fence. It is now passed and logged instead of refused.
R1 W widens the ruling retired. The trust model admits acpiserver.
R1 unlisted MMIO and config writes from 40h reach MSI/MSI-X, PM D-state, SR-IOV, EA and resizable BAR binds in part. A held function is refused whole and every header is refused, so "never written its BARs" holds for every function's header and every held function. Past 0xFF on an unheld function, SR-IOV and resizable BAR remain: a residual of the trust model, stated in the roast.
R1 sideband per-register narrowing retired. No sideband fence; see §2.
R1 MCHBAR "OS Access" let in the BIOS mailbox retired. The mailbox is the firmware's. Only the two kept registers are fenced.
R1 the DID list nobody read binds. 631121-012 Table 15-3 was fetched in round 2 and keys the MCHBAR rows.
R1 the first-write kernel line shipped only for the metal row binds as to the kernel. The kernel prints no line, and the boot window lines are gone too. The first-write record the brief asks for is the server's (§3), where it records the trusted writes on every machine.
R1/R2 MTRR types binds; answered. See "Unsure → resolved" below.
R1 lock bits (RegisterLock) retired. A firmware lock is the firmware's. 59A0h is kept from it in any case.
R1 "the build fetches" binds. build/ fetches nothing.
R1 the guest section binds. The guest behaviour changes now; the reason is under Tests.
R1/R2 P2SB HIDE window open to an SMI; enumeration order retired with the P2SB read, which round 3 deletes.
R2 sideband 196 rows vs the one the T14 writes retired. No sideband fence.
R2 owner of 59A0h answered by the standing ruling. The kernel's: refused KernelRegister, recorded in the new defect above.
R2 name one issue for every weakness binds, narrowed. The one weakness that withholds a feature, PL1/PL2, is the new defect. The trust model's residuals are the policy's contract and live in its module header, the site.

Roast

Lines. These are estimates, not measurements. Round 2 estimated about +150 production and +200 tests.

file round 3
sdk/abi/src/acpi.rs +12 −4
lib/userbound/src/firmware.rs (arms, one DID-keyed table of 5 DIDs, 2 MCHBAR rows and 1 config range, module header) +45 −20
kernel/src/arch/x86_64/acpi_mode.rs (MCHBAR read, config write) +30 −3
kernel/src/pcidev/mod.rs (held predicate) +12
system/acpiserver/src/host.rs (kept write, first-write ledger, module doc) +25 −6
images/*.toml comment +2
production about +126 −33, net about +93
tests (host +200 −40, server +50, guest ±25, tests/toyos.rs Ruled −45 and expectations +25) net about +165

What round 3 deletes against round 2's design:

  • the P2SB HIDE clear, read and restore, and its SMI window;
  • the eSPI DID range A080h–A09Fh;
  • the 196-row PAD_CFG_DW0 runs and their 8 run boundaries;
  • the three-register MCHBAR allowlist and its lock-bit check;
  • VendorRegister and RegisterLock;
  • both kernel boot lines.

Against main, it deletes:

  • ConfigWrite;
  • the "never written" arm;
  • tests/toyos.rs's Ruled struct and its says/held.

What it adds:

  • three refusal names;
  • one table;
  • the server's one exception and its ledger.

Could less do?

  • The 00:00.0 40h–BFh range is one constant on a DID check the 59A0h ruling already forces. It keeps the kernel's own fence (MCHBAR) and its ECAM from moving under it.
  • INTRDIRCTL is one row on the same table.
  • The class 08h fence reads a field pcidev already records.
  • Dropping any of these saves under 5 lines each, and gives up a fence the brief names.

The KernelRegister exception is a second outcome for a refusal. It is the one place a refusal does not end the evaluation. The AML then believes PL1 moved, reads 59A1h back at the hardware's value, and records the mode in NVS. That gap between the mode and the limits is exactly the recorded defect. Denying instead would make HKEY._INI refused by name, and the firmware's own mode record would be lost with it.

What the fences do not see, the trust model's residuals, stated in the policy's header:

  • the sideband, ITSS's interrupt routing and polarity among it (§2);
  • the AMD SMN behind 0xB8/0xBC. On sibling parts it reaches the IOMMU's, the data fabric's and the memory controllers' registers;
  • on an unheld function, past 40h: its MSI/MSI-X, PM D-state, and extended-space SR-IOV and resizable BAR. Enabling VFs or resizing a BAR moves decode the kernel did not record;
  • a GPIO output pad wired to a held device's line. Once a driver holds the GPIO controller, its window is a claimed window and fenced;
  • on any Intel host bridge 631122 does not cover: 59A0h, INTRDIRCTL and the host bridge's address map;
  • CONFIG_TDP_CONTROL 5F50h, which selects a TDP level and is not in the ruling (see Unsure);
  • the firmware locking 5F50h or 5F54h until reset.

What the T14's AML now does that it did not:

  • drives GPPC_A_13 high;
  • sets the cTDP level and turbo activation ratio. The kernel runs HWP, under which 631122 §3.3.51 gives the ratio "no affect";
  • finishes WGIN and DYTC.

What the AMD bench's does: it sends ALIB messages to the SMU through the mailbox, twelve under the 0xFF model below.

Tests and metal rows that show it

Host, lib/userbound/tests/firmware.rs (pure policy, the cheapest tier). Each test has a negative control: the whole change reverted onto 8fec83d, red. Each fence arm gets a mutation: the arm deleted, its test red. The oracles are 631122 §3.1.14, §3.1.13–§3.1.34, §3.3.31 and §3.4.5; 631121-012 Table 15-3; PCIe Base §7.5.1; and PCI Code and ID Assignment base class 08h, each typed into the test from the document.

  • an_unlisted_register_is_written_where_it_is_read.
    • Passes: the T14's 0xFD6E0A70 byte, and an EfiMemoryMappedIO address.
    • Refused: RegisterCached below 1 MiB and where cached; RangeRegistersDiffer; DeviceMemory on the LAPIC, a driven window and a BAR page.
  • a_register_the_kernel_keeps_is_refused_kernelregister.
    • Refused: on 9A14h with MCHBAR 0xFEDC0000, every width overlapping 59A0h–59A7h and 5418h–541Bh, including where the page is typed reserved.
    • Passes: 599Fh and 59A8h, the T14's 5F50h and 5F54h, and every read.
  • kept_registers_exist_only_on_the_host_bridges_631122_covers.
    • Rows: 9A02h, 9A04h, 9A12h, 9A14h and 9A1Ah.
    • No rows: 9A26h, 9A36h, 8086:29C0, 1022:1630, and MCHBAREN clear.
  • a_configuration_write_is_refused_in_every_header.
    • Refused AddressMap: 00h–3Fh, each width, by address and through ECAM.
    • Passes: 40h on an unheld function.
  • a_held_function_is_refused_whole.
    • Refused HeldFunction: claimed, kernel-driven, class 0800h and class 0806h, at 40h, B8h and 100h.
  • the_host_bridges_address_map_is_refused_where_631122_maps_it.
    • Refused AddressMap on 9A14h: 48h, 54h, 60h, 80h and BCh.
    • Passes on 9A14h: C8h and DCh.
    • Passes on 1022:1630: B8h and BCh, the AMD bench's writes.
  • Today's "never written" tests become "written where read".

Host, system/acpiserver units.

  • a_write_the_kernel_keeps_is_not_made_and_the_evaluation_goes_on. A Kernel that answers KernelRegister, and a method that writes a kept register and then stores. The method returns. The store is made. The line is said once.
  • the_first_write_into_each_page_and_function_is_said_once.
  • every_other_refusal_still_denies_the_evaluation. Its mutation turns every refusal into the kept one, and goes red.

Guest, acpi_mediated_access: its behaviour changes.

  • The unlisted write at 0xD0000000 now passes.
  • Header writes are AddressMap.
  • 00:00.0 at 44h, written with its own value, passes and reads back.
  • The NVMe function diskserver holds is HeldFunction at 44h and 100h.
  • A write to a function nothing answers at passes.

Why QEMU:

  • The host tests hold every decision, but no host test reaches the kernel's assembly of them: the ECAM write path, pcidev's held set reaching the policy, and the ABI round trip.
  • The metal rows see only what the firmware's AML asks, which now all passes. The T14's AML makes no configuration write at all, so no metal row reaches a refusal.
  • The probe is written against q35's layout, and its line moves with the test (ACPI_MEDIATED_SAID in tests/toyos.rs).

Metal, T14 (cargo test --test toyos-build -- --metal --metal-readback <dir> <row> in the build round):

  • acpi_tables_loaded:
    • T14.ruled goes.
    • The census reads both \_SB_.PC00.LPCB.EC__._INI and …EC__.HKEY._INI run, and "evaluations refused, by kind: none".
    • The server says the KernelRegister line once, with 0xFEDC59A0 under OWN.
    • It says the first-write line for the pages 0xFD6E0000 and 0xFEDC5000.
  • battery_read: nothing refused. The kept line is not a refusal of an evaluation.
  • acpi_server_events, acpi_server_death and counters stay green. counters reads WGSV's firmware calls as calls.

Metal, AMD bench:

  • acpi_tables_loaded:
    • AMD.ruled goes.
    • reg reads _REG(3, 1) in 1 scope(s).
    • Nothing is refused but SSDT8's load.
    • The battery reading ends AC online or AC offline.
    • The first-write line names 00:00.0's ECAM page.

Independent oracles: the vendors' documents above, and the two benches. Negative control: the whole change reverted onto 8fec83d. The host tests go red on every pass arm, and the T14 and AMD rows go red on the methods refused, as main reads them today.

Measurements behind this

Evidence pr-906:

  • round 3, 36be6e2:
    • The AMD bench's _REG(3, 1) and _PSR, traced on the host under toyos-aml at 8fec83d, over the bench's own tables, with the host a model (source; 0x00 fill, EXIT=0; 0xFF fill, EXIT=0).
      • Under both models, every write either method makes is 00:00.0's 0xB8/0xBC through ECAM, plus port 0x80.
      • Under 0xFF, where NDPC's ODV0 is set, it also writes ports 0x72/0x73 and one unlisted byte at 0xFE70039C. No recorded BAR covers that byte (round3/amd-acpi_tables_loaded.log lines 197–206).
      • No write reaches a header, another function or 00:00.2.
      • So no round-3 fence refuses either method under either model. The real NVS decides the path; no dump of it exists.
    • 631122's host-bridge registers: §3.1, §3.1.14, §3.1.19 and §3.4.2–§3.4.5.
  • round 2, f7f1f8c:
    • 631121-012 Table 15-3: HTTP 200, sha256 37745c3e…;
    • 631122 §3.3.31, §3.3.50 and §3.3.51.
  • round 1, afb02d7: PPR 55922 searches, 631119 Tables 6 and 109, and 631120 §3.1.5 and §3.1.22.

The bench enumerations are pr-875 round3/t14-testcases.log lines 162–182 and round3/amd-acpi_tables_loaded.log lines 179–206. No other implementation was read or cited.

Unsure

  • Resolved: the MTRR types (review 2's NOTE).
    • The T14's SGOV reads 0xFD6E0A70 just before writing it (trace line 97). ToyOS refused the write, not the read (t14-testcases.log lines 497–498). So that address is mapped, its CPUs agree, and ToyOS's own check finds it uncached.
    • CTCN reads 0xFEDC5F50 before its first write there (trace lines 227–230). That write was the refused one, MemoryType, which is a write's refusal. 59A0h lies in the same 4 KiB page, 0xFEDC5000.
    • So both of the T14's newly made writes meet the read conditions, at the 4 KiB granularity the range registers type at.
    • On the AMD bench, IA32_MTRR_DEF_TYPE is 0xc00, so the default is UC (log line 241). If 0xFE70039C is ever reached and a variable pair types it cached, the write is refused RegisterCached, and the build reports it.
  • 5F50h, CONFIG_TDP_CONTROL. The ruling names 59A0h alone, so 5F50h passes. Its TDP level also moves the package's TDP. If the performance track wants it, it is one row on the same table.
  • Stale after the build. issues/the-acpi-claims-holder-reaches-every-port-and-firmware-range-the-kernel-did-not-declare.md says no configuration write is made. That is no longer true once this builds. That issue is neither the one this PR takes up nor one a rule names, so the build leaves it, for the trust-model pull request or the orchestrator.
  • The interrupt-controller fence on the sideband (§2): declined on cost. That is the orchestrator's to overrule.

Gates

None run: design round, nothing built. The host trace's build and runs are evidence, not gates (EXIT=0 each, in the README).

🤖 Generated with Claude Code

https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy

…ect windows held to the vendors' maps

The design round's empty commit: the design and its roast are the pull
request's body. Nothing is built until it is accepted; the build drops
this commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
@Japabu

Japabu commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

Design review, head 8fec83d (one empty commit; evidence pr-906 afb02d7)

Net lines: git diff --shortstat origin/main...8fec83d is empty. Production 0, tests 0.

Does an AMD map of 00:00.0's 0xB8/0xBC exist in the documents we hold?

I extracted text from every PDF with PDFKit. That is a third extractor, independent of the designer's two. The bench is the part 55922 covers: cpuid.1.eax=0x860f01 is Family 17h Model 60h A1 (pr-875 round3/amd-acpi_tables_loaded.log:151), and its root complex is 1022:1630 (line 179).

document part names the B8/BC pair? SMN ranges it maps
55922-A1 rev 3.06 (control) 17h M60h A1, the bench no none: 0 hits for "SMN", 0 for "IOHC", and its SMN memory-map page is empty. This confirms the designer's search.
56255 OSRR rev 3.03 17h M00h–2Fh no one register: SMU::THM at 0005_9800h
55772-A1 rev 3.08 17h M20h (Dali) no SMU::THM 0005_9800h; IOMMUMMIO 0240_0000h..+4132Ch; IOMMUCFG 13F0_0000h; IOMMUL2B 13F0_1000h; IOMMUL1INT0/1 1470_0000h and 1480_0000h; IOMMUL2A 1570_0000h (its "Memory Map - SMN", p. 589). It names no index/data pair.
55901 rev 0.47, vol 1–6 19h M11h (server) no. It names IOHC::NB_SMN_INDEX_3/DATA_3 at 0xC4/0xC8, "security level 7" (vol 3 Table 162–163) HSMP mailbox 03B1_0534h–03B1_09FCh (vol 3 Table 152); NBCFG 13B0_0000h–13E0_0000h; IOHCMISC 13B1_0000h–13E1_0000h; DF D18F4/D18F7 4900_4000h/4900_7000h; UMC 0005_0000h–00B5_0000h; eSPI 02DC_5000h/02DC_A000h
BKDG 42301 rev 3.14 15h M00h–0Fh no: the part has no D0F0xB8, and its root complex is off-die none
BKDG 49125 rev 3.06 15h M30h–3Fh yes: D0F0xB8 "SMU Index Address" and D0F0xBC data, reaching D0F0xBC_x[FFFFFFFF:0] SMU only: 3F800h–3FDD4h, C010_40A0h, C020_0110h, C210_0000h–C210_0040h, E000_3040h
BKDG 52740 rev 3.06 16h M30h–3Fh yes, worded as in 49125 SMU only: 3F800h–3FDD4h, C020_008Ch/0098h/0110h, C210_0000h–C210_0040h
sog-17h (55723 rev 3.00) 17h optimization guide no none: it has no register maps

This is not enough to keep the IOMMU's, RAM's or the interrupt controllers' registers out of reach of a write through the index on the bench's part:

  • No document names 0xB8/0xBC as the SMN pair on any Zen part. Two pre-Zen BKDGs name it, and their index space is the SMU's under a different address layout. The one Zen document that names an SMN pair puts it at 0xC4/0xC8, on another family.
  • ALIB's targets are the SMU's mailbox. ALIB writes 03B1_0528h, 03B1_0564h and 03B1_0998h. Those sit inside the block 55901 gives as MP1's HSMP message mailbox on 19h M11h (Message ID 03B1_0534h, Response 03B1_0980h). So an allowlist of exactly those three addresses would still hand the server the SMU's commands, and no held document lists those commands for M60h.
  • On sibling parts, SMN reaches the very registers the ruling protects. 55772 maps the IOMMU's own registers into SMN on 17h M20h, and 55901 maps the DRAM controllers and the data fabric there on 19h. No held document maps M60h's SMN: not its IOMMU, its DF/UMC, its IOHC I/O APIC configuration, or its MP1 message set.
  • Deny-list or allow-list, neither works. A deny-list built from a sibling part's partial map leaves every unmapped address open. An allow-list has nothing to admit by.

So, as the ruling is given, AMD's index stays refused. That is arm L.

BLOCKER

  • PR body, "The ruling…", arm W — W widens the ruling; it is not one of its arms. The ruling as given checks indirect windows against the vendor's map, and still refuses anything that overlaps the IOMMU, RAM, the interrupt controllers or another driver's device. With no map (table above), that resolves to L. Nothing in the ruling requires AMD's _REG/_PSR back. The body's "contradiction" therefore does not exist, and W, presented as "the design below", gives the server SMN. Make L the design. W can go to the owner only as a separate question: whether to widen the trusted base.

  • PR body §1 and §3 (W) — W breaks the ruling's refusals on every machine, not only through SMN. By the body's own roast table, unlisted uncached writes reach the PMC and FCH power gating of held devices, and the FCH SPI controller. Configuration writes at offset 40h and above reach:

    • each unheld function's MSI/MSI-X capability, which sets interrupt message address and data (the interrupt clause);
    • its PM capability's D-state;
    • extended-space SR-IOV, Enhanced Allocation and resizable-BAR registers, which are BARs by another name.

    "Never written its BARs" is therefore false past 0xFF. This is dropped under L. Do not carry it into the build.

  • PR body §2, sideband — passing a whole GPIO community by Target Port ID lets the interrupt and held-device refusals fail. 631120-002 §27.1.36 PAD_CFG_DW0:

    • bits 20/19/18/17 GPIROUTIOXAPIC/SCI/SMI/NMI route a pad to the IOxAPIC and to NMI;
    • §27.1.35 GPI_NMI_EN is RW;
    • PMODE remuxes a pad from a held device's native function to GPIO.

    The T14's write is byte 0 of PAD_CFG_DW0_GPPC_A_13, where bits 7:2 are reserved RO, bit 1 GPIORXSTATE is RO and bit 0 GPIOTXSTATE is RW. Hold the window to the map at register granularity: admit only a byte write to byte 0 of a PAD_CFG_DW0 row, and refuse every other offset of every community. That admits the T14's write and nothing else. What is left is a TX level on a pad whose wiring the kernel cannot know. Record it.

  • PR body §2, MCHBAR — admitting by the "OS Access RW" column lets in an undocumented mailbox, which is the reason the design gives for refusing AMD's index. 631122 §3.3.41–3.3.42: BIOS_MAILBOX_DATA/INTERFACE at 5DA0h/5DA4h are OS RW, and their commands are "documented in the BIOS Writers Guide". The body's own roast concedes this. Admitting Intel's undocumented mailbox while refusing AMD's is two standards in one policy. Admit the three registers the T14 writes and whose fields 631122 defines (PACKAGE_RAPL_LIMIT 59A0h, CONFIG_TDP_CONTROL 5F50h, TURBO_ACTIVATION_RATIO 5F54h). That replaces the ~40-row table with three rows.

  • PR body "Unsure: The DIDs" — the MCHBAR arm keys on a host-bridge DID list nobody has read. It is in 631121 (Vol 1), which is not fetched. It is a cheap measurement: fetch it, and state in the body which DIDs 631122 covers and that 9A14 is among them, before this design is accepted.

  • PR body "What else changes", "the kernel says … the first write it passed into each" — this kernel line ships only for the metal row. The server's census already reports both _INIs as run, which is what acpi_tables_loaded reads. Drop the first-write line. Keep, at most, the line giving the window the kernel read.

NOTE

  • PR body "Unsure: The MTRR types" — the body says "The T14's MTRRs (IA32_MTRR_DEF_TYPE 0xc00) default to UC", which is false of the record. The T14 reads 0xc06, so its default type is WB (pr-875 round3/t14-testcases.log:235); 0xc00 is the AMD bench's (round3/amd-acpi_tables_loaded.log:241). The T14's mmio: lines show 0xc0000000, 0xfec00000, 0xfed00000 and 0xfed85000–0xfed91000 typed MTRR UC. So whether 0xFD6E0000 and 0xFEDC0000 are UC rests on one variable pair, and nothing prints it.
  • PR body §2 MCHBAR — PKG_PWR_LIM_LOCK (59A0h bit 63), CONFIG_TDP_LOCK (5F50h bit 31) and TURBO_ACTIVATION_RATIO_LOCK (5F54h bit 31) are RW/L "locked till next reset" (631122 §3.3.31, §3.3.50, §3.3.51). Refuse a write that sets one, or record that the server can freeze them for the rest of the boot.
  • PR body "The DID list … which the build fetches" — if "the build" means build/, that is a new fetch, which Fit refuses. The body should say it means the build round's agent.
  • PR body "Guest", acpi_mediated_access — that section is W's. Under L, unlisted writes stay refused and q35 has no mapped window, so the body should say which guest behaviour changes, if any, and why a cheaper tier cannot reach it.
  • PR body §2, P2SB — the kernel's own HIDE clear/restore is bounded in accesses, not in time. An SMI taken on the BSP inside that window sees the P2SB visible. Record it with the rest of the record issue's weaknesses.

Roast against reviewer.md

  • Cost under L. About +180 production lines (the designer's estimate) buys the T14's two _INIs: one GPIO TX level, plus DYTC setting PL1/PL2/cTDP. With the per-register holds above (one PAD_CFG_DW0 byte rule, three MCHBAR rows), the transcribed tables shrink from ~40 rows plus six ports to a handful.
  • Could a prompt sentence replace a check? No. Each check the design adds decides a runtime access, which reading cannot see, so none is a prompt sentence's job. The one addition that guards nothing at runtime is the first-passed-write log line, which goes (BLOCKER above).

SEND BACK

@Japabu Japabu changed the title The ACPI server trusted with the firmware's writes, its indirect windows held to the vendors' maps (design) The ACPI server writes the T14's GPIO pad and three PCU registers, held to Intel's register maps; AMD's SMN index stays refused Oct 13, 2026
@Japabu Japabu changed the title The ACPI server writes the T14's GPIO pad and three PCU registers, held to Intel's register maps; AMD's SMN index stays refused The ACPI server writes the T14's GPIO pad and three PCU registers, held to Intel's register maps; AMD's SMN index stays refused (design) Oct 13, 2026
@Japabu

Japabu commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

Design review, round 2, head 8fec83d (one empty commit; evidence pr-906 f7f1f8c design-r2/)

Net lines: git diff --shortstat origin/main...8fec83d is empty. Production 0, tests 0.

Earlier findings

  • BLOCKER, W widens the ruling — CLOSED. L is the design. W appears only under "Not taken", and it goes to the owner as a question of the trusted base.
  • BLOCKER, W breaks the refusals through unlisted MMIO and config writes from 40h up — CLOSED. Under L, unlisted writes stay MemoryType and config writes stay ConfigWrite, whole.
  • BLOCKER, sideband passed whole communities by Target Port ID — CLOSED as round 1 worded it. The window is now byte 0 of a PAD_CFG_DW0 row. The 196 rows check out against design-r2/intel-631120-pad-cfg-dw0.txt lines 104–304, and the run arithmetic is 24+2+24, 52+2, 12, 48+24 and 8. Round 1's "That admits the T14's write and nothing else" was false: the rule admits 196 rows. See the new BLOCKER on the orchestrator's ruling.
  • BLOCKER, MCHBAR admitted by "OS Access RW", which let in the BIOS mailbox — CLOSED. The window is three registers, and 5DA0h/5DA4h are refused VendorRegister. design-r2/intel-631122-mchbar-three.txt carries §3.3.31, §3.3.50 and §3.3.51 verbatim, with their lock bits.
  • BLOCKER, the host-bridge DID list was never read — CLOSED. 631121-012 Table 15-3 was fetched: HTTP 200, sha256 37745c3e…, ToyOS User-Agent. 9A14h is UP3 4 Cores and H35 4 Cores. 631122's title page names UP3/UP4/UP3-Refresh/H35/H35-Refresh, so it excludes 9A26h and 9A36h.
  • BLOCKER, the kernel's first-write line shipped for the metal row only — CLOSED. That line is dropped. Each window gets one line, with its base's range-register type.
  • NOTE, MTRR 0xc00 was the AMD bench's — CLOSED. The body now quotes 0xc06 from t14-testcases.log:235.
  • NOTE, lock bits — CLOSED. A write that sets a lock bit is refused RegisterLock.
  • NOTE, "the build fetches" — CLOSED. The body says the build round's agent transcribes the lists, and build/ fetches nothing.
  • NOTE, the guest section was W's — CLOSED. It now reads "Guest: none changes". q35's 29C0/2918 match neither window, and the body says the T14 row reaches the kernel's assembly of the windows.
  • NOTE, P2SB HIDE window open to an SMI — CLOSED. It is listed among the weak points the record issue holds.

BLOCKER

  • PR body §1 "The sideband window", "The one write that passes" — the orchestrator's ruling narrows the Intel windows to the exact registers the T14 writes. The design admits 196 of them. The T14 writes one sideband register: PAD_CFG_DW0_GPPC_A_13, port 6Eh, offset A70h (host-trace-pc00-ec-hkey-ini.txt:98). The body's own roast names the residual: "a GPIO output pad wired to … another driver's device's reset or power-enable line". That is the ruling's "a device another driver holds" clause, recorded rather than refused. It applies to every GPIO-mode output pad in five communities, where it could apply to one. issues/the-t14-touchpad-is-i2c-hid-and-unbuilt.md is the first held device that will sit on such a pad. MCHBAR already follows the ruling, with exactly the three registers the T14 writes. Hold the sideband to the same standard: byte 0 of port 6Eh offset A70h only, with the residual reduced to one pad. Or the orchestrator states on this PR that the ruling meant the register kind. The host test's "first and last row of each run" passes then become VendorRegister refusals.
  • PR body §1 "MCHBAR", PACKAGE_RAPL_LIMIT 59A0h — this hands the server a register an open track gives the kernel, and the design never says which of them owns it.
    • issues/the-kernel-owns-cpu-performance-state.md, "RAPL", has the kernel declare PL1, PL2 and their windows "through MSR_PKG_POWER_LIMIT and the MMIO mirror in the host bridge's MCHBAR, at the bar's values". Its exit: the T14 reads both back at the bar's values.
    • 631122 §3.3.31 says "Power limits from this MMIO register and the MSR are evaluated separately". The T14 firmware's CTCN writes PL1 = 0x0A0 with enable set (trace lines 236–240), so that limit holds whatever the kernel puts in the MSR.
    • The server writes it from HKEY._INI → DYTC, and again whenever the firmware calls DYTC at run time.
    • Two writers of one register is the "one declaration read by every reader" rule broken, and it defeats the fixed envelope the self-hosting bar is measured under.
    • The design must name the owner of 59A0h. One option: the server's write is refused once the kernel's declaration owns it, so HKEY._INI is refused again, and the body must say so. The other: the perf-state track moves to the firmware's values. The roast must cite that track, and the orchestrator, who owns it, must accept the split before the build.

NOTE

  • PR body "Unsure: The MTRR types" — the record already answers this for the sideband. The check order in lib/userbound/src/firmware.rs:229–243 refuses MemoryType before Unmapped, RangeRegistersDiffer and RegisterCached, and those three apply to reads too. On the T14, SGOV reads 0xFD6E0A70 just before writing it (trace line 97), and ToyOS refused the write, not the read (t14-testcases.log:497–498). So 0xFD6E0A70 is mapped, its CPUs' range registers agree, and it is uncached by ToyOS's own check. MCHBAR's page is probably the same: the reads at 0xFEDC5930 (trace line 212) come before HKEY._INI's refused write. That inference is weaker, because the server prints no address for the second refusal. State this in the body instead of leaving it "unsure".
  • PR body "Roast", "What it can newly do" — "This is recorded" names no issue. The list headed "recorded in one issue owned by the AML track" does not include the pad-TX residual or the PL1/PL2/cTDP denial of service. Also, the T14 issue the build closes cannot hold them. Name the new issue file and put all five weaknesses in it, each with an exit something can read.
  • PR body §1 "How the kernel finds the base" (P2SB) — the body does not say when the clear–read–restore of HIDE runs relative to the kernel's PCI enumeration (t14-testcases.log:162–182 lists no 00:1F.1). If it runs during enumeration, the P2SB is enumerated as a device. Say it runs after enumeration, with nothing else touching configuration space in between.

SEND BACK

@Japabu Japabu changed the title The ACPI server writes the T14's GPIO pad and three PCU registers, held to Intel's register maps; AMD's SMN index stays refused (design) The ACPI server is trusted platform code: the kernel makes the firmware's writes but into RAM, the IOMMU, an interrupt controller, a held device or the kernel's power limit (design) Oct 13, 2026
@Japabu

Japabu commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

Orchestrator on design round 3's open calls.

  1. ITSS on the P2SB sideband stays unfenced, and that is recorded. Fencing it would bring back the P2SB unhide machinery. Under the trust model a fence is kept only where it is cheap, so this one isn't kept. Record it as a weakness in the round's new issue.
  2. cTDP (MCHBAR 5F50h) follows ownership, not likeness. It is refused by name only if issues/the-kernel-owns-cpu-performance-state.md has the kernel write it. If the track names only PL1/PL2 (59A0h and its MSR), 5F50h passes. The reviewer checks the track's text.
  3. The stale issue that says no configuration write is made goes in the build round. The build takes it up, or names it as the issue it closes.
  4. The proposed rule (a refusal that lets the method continue only for a register the kernel owns by a recorded ruling) is declined as a rule. It is this design's own decision, and it belongs in the module header at the site.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant