Repository navigation
A reset register that is SMI_CMD, a machine with no ECDT and a control-method power button each leave ACPI mode to the server, and AML reaches only the three port writes measured - #845
Conversation
…l-method power button each leave ACPI mode to the server An AMD laptop's FADT names port 0xb0 as both its reset register (value 0xfb) and SMI_CMD, has no ECDT, and makes its power button a control method device. The kernel refused it ACPI mode three ways; it is now claimed, and the server serves what only its AML names. The shared port is declared once, as SMI_CMD, before the IDT, with the reset register: `power::init_reset` declares both FADT roles, and where they are one port the reset register is SMI_CMD's declaration and the reset's value is a sixth kept byte (`SmiCmd::named`, `KeptCommands`), so no write the acpi holder asks of the port resets the machine. The reset's `out` stays a plain write from whichever CPU ends the machine: Table 5.9 asks the boot processor of a command to SMI_CMD and nothing of the reset. The ECDT stopgap is deleted, as the owner's "Stopgap, delete later" asked the day the controller is read from the DSDT's own device. The kernel reads no ECDT, its row is PM1a events and GPE0, and `AcpiInfo` loses the controller's fields. The server finds PNP0C09 in the namespace its load keeps (`devices.rs`): `_STA`, `_CRS`'s two fixed I/O ports through the new `toyos_acpi::io_ports` (data first, command second, ACPI 6.5 §12.11), and `_GPE` held to GPE0. Its ports are no row's, so the server reaches them through the kernel's mediated access, as any port AML names. A control-method power button is served as §4.8.2.2.1.2 has it: where the FADT says the button is a control method device, the server finds every present PNP0C0C, runs each controller query's `_Qxx`, and a Notify of 0x80 to a button is a press on the existing power-off path. To let a query's method reach its Notify the host now passes a port write to the kernel, whose policy decides it; memory and configuration space stay unwritten. On a machine whose button is the fixed one no query's method runs, as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
|
build-only part 1 of 1 ( |
|
ci-host part 1 of 9 ( |
|
ci-host part 2 of 9 ( |
|
ci-host part 3 of 9 ( |
|
ci-host part 4 of 9 ( |
|
ci-host part 5 of 9 ( |
|
ci-host part 6 of 9 ( |
|
ci-host part 7 of 9 ( |
|
ci-host part 8 of 9 ( |
|
ci-host part 9 of 9 ( |
|
guest part 1 of 1 ( |
|
mutations part 1 of 1 (patches, script and verdicts at 3605706, every arm EXIT=101) |
|
mutation-logs part 1 of 1 (each mutation's whole cargo test output at 3605706) |
|
negative-control part 1 of 2 (the whole production change reverted onto 60dc7fc, tests kept: guest EXIT=1, toyos-acpi EXIT=101) |
|
negative-control part 2 of 2 (the whole production change reverted onto 60dc7fc, tests kept: guest EXIT=1, toyos-acpi EXIT=101) |
|
metal-stage part 1 of 1 ( |
|
oracle part 1 of 1 (out of the tree: the server's devices.rs and toyos-acpi at 3605706 against the laptop's own tables, EXIT=0) |
Of 204 out-of-tree evaluations of its 102 query methods, 142 wrote nothing; none wrote a port but 0x80. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
|
build-only-4259cf814 part 1 of 1 ( |
|
ci-host-4259cf814 part 1 of 9 ( |
|
ci-host-4259cf814 part 2 of 9 ( |
|
ci-host-4259cf814 part 3 of 9 ( |
|
ci-host-4259cf814 part 4 of 9 ( |
|
ci-host-4259cf814 part 5 of 9 ( |
|
ci-host-4259cf814 part 6 of 9 ( |
|
ci-host-4259cf814 part 7 of 9 ( |
|
ci-host-4259cf814 part 8 of 9 ( |
|
ci-host-4259cf814 part 9 of 9 ( |
|
guest-4259cf814-suspended part 1 of 1 ( |
|
|
|
Mutations of the no-namespace ruling, round 4. Each was applied with
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Review of #845 at 3f55d8a, round 4. Earlier BLOCKERs
BLOCKER NOTE
Merge: the merge base is origin/main 2cca280, and Net lines:
LAND |
acpi_mode takes main's MCFG windows (pci::windows) and keeps this branch's ECDT removal and FACS handoff; toyos-acpi exports main's mcfg beside this branch's io_ports; corpus.rs imports ecam_allocations without the ECDT names. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
An AMD laptop's firmware stopped ToyOS's ACPI mode three ways; each is now the server's to serve, and the kernel refuses none of them. Its FADT names port 0xb0 as both the reset register (value 0xfb) and
SMI_CMD, it has no ECDT, and its power button is a control method device (FADT flag bit 4) whose press arrives as embedded-controller query 0x28 notifying\_SB.PWRB.What changed, and why
B1: one port, two FADT roles (
kernel/src/arch/x86_64/power.rs,smi_cmd.rs,toyos-acpi/src/fadt.rs,toyos-userbound/src/port.rs)power::init_resetnow declares both ports a write commands, still before the IDT. Where they are one port it is declared once, asSMI_CMD(Mediated::Command), and the reset register is that declaration (smi_cmd::declared). Elsewhere the reset register keeps its ownKeptdeclaration, as before.SmiCmd::named(s4bios, reset)returns a sixth kept byte:RESET_VALUEwhere the reset register is this port.KeptCommandsis[Option<u8>; 6]. So a byte 0xfb the holder asks for isKernelCommand, never a firmware call that resets the machine.smi_cmd::writekeeps every command, on the boot processor.power::resetkeeps its one lock-freeoutof the reset value from whichever CPU ends the machine: ACPI 6.5 Table 5.9 asks the boot processor of a command toSMI_CMDand nothing of the reset register.init_resetreads the FACS once, forS4BIOS_F, and the boot hands that read toacpi_mode::initfor the Global Lock (Platform::facs).acpi_mode::initno longer declaresSMI_CMD;FADT_FOR_RESETis gone.B2: no ECDT (the ECDT stopgap deleted, as the owner's "Stopgap, delete later" asked for the day the controller is read from the DSDT)
toyos_acpi::ecdtand its tests are deleted. The row is PM1a events and GPE0, andAcpiInfolosesec_command,ec_data,ec_gpeandhas_ec(ABI;reservedis[u16; 3]).userland/acpiserver/src/devices.rs, new). After the load it finds the PNP0C09 device in the namespace the load keeps (§6.1.5 EISA id or string). It checks_STAbit 0 (§6.3.7)._CRSmust be exactly two fixed I/O runs, data first and command/status second (§12.11), decoded by the newtoyos_acpi::io_ports(§6.4.2.5, §6.4.2.6)._GPEmust be an integer inside GPE0; a package (a GPE block device) is refused by name. More than one controller present means none is served.Claim::port_in/port_out). A port the kernel refuses is no panic: the status port's refusal at arming, and any refusal of either port later, saysCONTROLLER_NONEand the kernel's refusal by name, and a later one disables the controller's GPE and drops what it had waiting (Server::unserve).B3: control-method power button (ACPI 6.5 §4.8.2.2.1.2)
<EC>._Qxx, and only where a button was found (run_query).Firmware::notifycounts a Notify of 0x80 (§5.6.6) to one of those buttons as a press. It goes to the existingpress(), so the power-off is the one path.run_query's host test holds it._Lxx/_Exxis not served: the server enables no GPE but the controller's. This laptop's\_GPEholds no_Lxx.AML's port writes: three, measured (
userland/acpiserver/src/host.rs,FORWARDED)\_S5, the device search and all 102 query methods, under six read arms (portwrites, its logs):_HID,_STA,_CRS,_GPE): nothing.none(main's policy: every write denied),postandno72runs load 0 of 18 blocks.UNFORWARDED) without asking the kernel. So no reset-capable port (0xCF9, 0x92), noSMI_CMDbyte and no data register (0x73, 0xCD7) is reached from AML. The reset register is the kernel's to keep wherever it is, asKeptor as a kept byte ofSMI_CMD.acpiserver: a SystemIO write of 0x80, Word, asked of the kernel for the first time, as a Notify is.aml.rs,host.rs,tables.rs,ledger.rsanddevices.rs, compiled out of the tree by path at 3d26c03, ran on the laptop's tables against a kernel stand-in that records every write asked of it. Results:\_S5was handed over as 5.0x72 Byte x4,0xCD6 Byte x2and0x80 Word x30.The controller's refusal path, host-tested (
userland/acpiserver/src/main.rs)Kernel.take_queries,transactand the controller'sport_in/port_outask anyKernel, asrun_queryalready did. The production kernel is still theClaim.Ports. The server reads and writes its PM1 event and GPE0 registers throughPorts. On the machine that isMachine, its own ports; in a host test it is bytes. The server no longer holds theAcpiDev:armandservetake it, the only two that use the device itself.a_controller_port_the_kernel_refuses_ends_the_controllerdrivesdraininto a refused status port, and into a data port refused inside a query's transaction. It reads four things: the controller is no longer served, its GPE enable bit is clear and no other bit of the byte is, its waiting queries are dropped, and a second drain asks the kernel nothing.Lines and judges
acpiserver_api::CONTROLLER_SERVED/CONTROLLER_NONE.counters_metalwaits on those two constants.acpi_events_on_metalreads the kernel's row line without the controller, and the controller line...on GPE 0x6e at 0x66/0x62.qemus_tables_load_and_s5_is_what_its_kernel_decodednow reads this by runningdevices::findon QEMU's own DSDT. The guest testacpi_power_buttonkeeps only theACPI_ARMEDwording the server's new line forced.A control-method button the server does not serve stays dead in ACPI mode, as the owner ruled (
userland/acpiserver/src/main.rs)ACPI_ENABLE, and the server serves neither the button nor the controller and keeps the claim, so a short press reaches nothing. On main the claim was refused, and the firmware kept the button.no_namespace_leaves_the_button_and_the_controller_unserved_and_keeps_the_claimrunsarm_controllerwith no namespace on a control-method-button machine. It reads that the call returns, soservefollows and the claim is kept; that the button is not served as the fixed one; that no controller and no controller GPE is served; that no GPE enable bit is set; and that the kernel was asked for nothing. Its mutations are under Checks.The issue this branch takes up, and no other (
issues/toyos-runs-the-machine-in-acpi-mode-and-interprets-its-aml.md)FORWARDED. It says what the measurement found, and records a port write outside the three as a weakness with an exit.No other issue file moves. Round 3 edited
acpiserver-lines-are-spelled-again-by-their-readers.mdandno-t14-row-reads-the-power-off-after-the-kernels-own-acpi-enable.md; both are back to main's, under the rule that a pull request edits only the issue it takes up. What those edits said stays true of the tree, and is left to the work that takes each of them up:counters_metalno longer spells the controller line, which it reads fromacpiserver-api.The laptop extract (
toyos-acpi/tests/amd_laptop.rs). It is committed under the orchestrator's ruling: decoded fields of this laptop's FACP may be committed only where no field is an OEM string or identifier. None is. The header's OEM ID, OEM table ID, OEM revision and creator fields are zeros (common::sdt). The tree names the machine only as "an AMD laptop". Its module doc records the ruling.Gates
cargo run -- --ci hostcargo test(whole guest suite)cargo run -- --ci hostcargo test --test toyos-build(whole guest suite)cargo test, the first runacpi_power_button,machine_shutdownandnested_nmi_is_loudwaited up to 840 s on the sysroot lock, the filed wedge; 54 of 57 had passed (see Unsure)cargo test -p acpiserver --bin acpiserver no_namespace)cargo run -- --ci hostcargo test --test toyos-build(whole guest suite)https_fetchwaited 70 minutes on the sysroot lock; 55 of 56 had passed (see Unsure)cargo test -p acpiserver)cargo run -- --build-onlycargo run -- --build-only --arch aarch64cargo run -- --ci hostcargo test --test toyos-build(whole guest suite)cargo test --test toyos-build -- --metal --metal-readback <scratch>/amdlaptop/acpimode/r2/metal acpi_server_events acpi_tables_loaded acpi_server_death counters acpi_table_inventoryrequest.txt; T14 not touched)cargo test -p acpiserver)d0538cccddiffers from3d26c03ecin the kernel's FACS hand-off, two issues and one test doc, and from19be6a88fin those and the server'shost.rs,main.rs,aml.rstests andtests/common/power.rs.devices.rs,toyos-amlandtoyos-acpi/src, which the measurement ran, are byte-identical at 4259cf8, 19be6a8 and d0538cc.83af0b399differs fromd0538cccdin the merge of origin/main at 2262cb2 (#842, which touches none of this branch's files buttests/toyos.rs,kernel/src/arch/*/boot.rsandtoyos-abi) and in the server'smain.rsalone.3f55d8a6ediffers from83af0b399in the track's ruling, the two other issue files put back, the new host test, and the merge of origin/main at 2cca280, which touchestests/toyos.rsamong this branch's files.fe275ea50differs from3f55d8a6ein the merge of origin/main at a5242a4 (#837, #839, #840) alone. It conflicted inacpi_mode.rs,toyos-acpi/src/lib.rsandtoyos-acpi/tests/corpus.rs: each keeps this branch's ECDT deletion,io_portsand FACS hand-off beside main's MCFG windows (pci::windows,EcamWindow,ecam_allocations).Hardware readings
QEMU reaches none of B1–B3.
testcases).acpi_server_eventsreadCONTROLLER_SERVEDon GPE 0x6e at 0x66/0x62,acpi_tables_loaded14 of 14, andacpi_table_inventorythe separate reset register 0xcf9 <- 0x06. The T14 has not run 83af0b3; this round's change is the server'smain.rsseam, which the five rows read through.wt/toyos-yoga-tpat100e61b9e. It merges this branch atd0538cccdwith the other laptop branches and the image's hacks.B1. It read
ACPI: reset register SystemIO 0xb0 <- 0xfb, which is SMI_CMD too, andACPI_ENABLE0xa0 written toSMI_CMD0xb0 withSCI_ENset. None of B1's files (power.rs,smi_cmd.rs,fadt.rs,port.rs) differ betweend0538cccdand the image, or betweend0538cccdand this head.B2 and B3. It read the controller served on GPE 0x3 at 0x66/0x62, query 0x28's method run, and one short press shutting the machine down. That tree is not this branch's, though: on this branch alone the laptop's DSDT does not load, and the button is dead, as ruled.
The image's diff of this branch's files.
git diff --stat d0538cccd 100e61b9e -- <the files git diff --name-only 1079e854a d0538cccd names>lists 14 files, 13 of them code:kernel/src/arch/x86_64/acpi_mode.rs(64 lines changed);kernel/src/arch/x86_64/boot.rs(25);kernel/src/main.rs(30);tests/common/power.rs(129);tests/toyos.rs(252);toyos-acpi/src/lib.rs(26);toyos-acpi/tests/corpus.rs(11);toyos-userbound/tests/firmware.rs(83);userland/acpiserver-api/src/lib.rs(13);userland/acpiserver/src/aml.rs(66);userland/acpiserver/src/devices.rs(6);userland/acpiserver/src/host.rs(283);userland/acpiserver/src/main.rs(178).The 14th is the track,
issues/toyos-runs-the-machine-in-acpi-mode-and-interprets-its-aml.md(37).Checks of the high-risk code
Mutations of the no-namespace ruling, round 4 (all), on the tree committed as 27d3a81, applied as checked patches, run and restored by one script, the tree clean before and after:
no_namespace_leaves_the_button_and_the_controller_unserved_and_keeps_the_claimA give-back written as
std::process::exit(0)insidearm_controllerwould not red: it ends libtest's process with exit 0. The test reads thatarm_controllerreturns; thatmainthen callsserve, which never returns, is read frommain's four lines.Mutations of
unserve, round 3 (all), at 83af0b3, applied, run and restored by one script, the tree clean before and after:self.ec = Nonedroppeda_controller_port_the_kernel_refuses_ends_the_controllera_controller_port_the_kernel_refuses_ends_the_controllerMutations, round 2 (all). Each was applied as a checked patch by one script, built and run, restored, and the tree was clean before and after:
(Some(aml), Some(ec)) if !aml.host.buttons.is_empty() =>to(Some(aml), Some(ec)) =>, now inrun_querya_querys_method_runs_only_where_a_control_method_button_is_servedonly_a_forwarded_port_write_is_asked_of_the_kernelonly_a_forwarded_port_write_is_asked_of_the_kernelRound 1's mutations m1–m8 each red, at 3605706:
the_reset_register_is_smi_cmd_and_its_value_is_one_the_tables_name.query_0x28_on_an_amd_laptop_is_a_press_of_its_power_buttonandonly_a_notify_of_a_press_to_a_served_button_is_a_press.an_io_run_that_is_no_fixed_port_is_refused_by_name.<=, and m7,_STAignored:a_controller_this_server_cannot_serve_is_none._CRSorder swapped:an_amd_laptops_controller_and_button_are_found_in_its_dsdt.m5's test is now
only_a_forwarded_port_write_is_asked_of_the_kernel.Negative control, the whole change, at 3605706: production reverted onto 60dc7fc, tests kept.
acpi_power_button: exit 1.toyos-acpi: exit 101.acpiserver: exit 0, which shows nothing. Its tests live in the reverted files; the mutations are its controls.Independent oracle: the laptop's own tables, outside the tree, three runs.
findandquery(0x28).The tables stay local. A line the server's code prints under
OWNis counted and not printed.The fixtures are decoded extracts, never the tables.
amd_laptop.rsholds the FACP's fixed-hardware and reset fields at their offsets.resource.rsholds the controller's_CRSas its two descriptors' fields.devices.rs's tests hold AML built from the decoded objects.What QEMU can produce, and why no new guest test
SMI_CMDequal to the reset port: no. QEMU's FADT is fixed at 0xb2 and 0xcf9.acpi_power_button's change is the armed line's wording alone, which the server's new line forced. The DSDT search it read last round is now the host test's.Unsure
CONTROLLER_SERVED0x6e at 0x66/0x62, which is what Linux's ECDT reading says. Its device search was refused by no write outsideFORWARDED, or the row would have readCONTROLLER_NONE._REG(3, 1)is run before the queries. acpiserver reads the battery and the AC adapter through the machine's AML and its embedded controller, and waits out the firmware's hold of the Global Lock #838 runs_REG.Controllertakes the row's ports, which no longer exist. On merge its port pair becomes the mediatedport_in/port_out, free functions over anyKernelreturningResult<_, Unmade>. This branch'sPortstrait, the row's PM1 and GPE0 registers, shares its name with acpiserver reads the battery and the AC adapter through the machine's AML and its embedded controller, and waits out the firmware's hold of the Global Lock #838'sPorts; one of them is renamed on merge. The_HIDdecoders in itsbattery.rsand thisdevices.rsbecome one.https_fetch, then waited 70 minutes inbuildlock::keyed_using'sflock(LOCK_SH)on the sysroot key's lock, reached fromsysroot::ensureunderHeld::without_shared. Its own process held a second descriptor on that lock file, and no thread was building (sampleshowed one thread inflock, the rest idle). The other 55 tests passed. I killed my own run by its PID, and the rerun, against the built sysroot, passed 56 of 56. That defect is filed on main asissues/a-suite-that-builds-its-sysroot-can-wedge-on-its-own-locks.md. This branch does not edit that file: it edits only the track it takes up.cargo run -- --ci hostrunning beside it in the same worktree. The harness built4f6b16f91d38d8f5by 17:42:16. From then onacpi_power_button,machine_shutdownandnested_nmi_is_loudwaited onusing sysroot 4f6b16f91d38d8f5 — the holder left no readable note, up to 840 s by the kill.lsofshowed the key's lock file open four times, in the harness alone. The other 54 tests had passed. I killed my own run by its PID (exit 143). The rerun, against the built sysroot, passed 57 of 57. This recurrence is for the issue's owner to record; this branch does not edit that file.Net lines
git diff --shortstat origin/main...fe275ea50: 27 files, +1396 −473.devices.rs224,main.rs+141 andhost.rs+50.toyos-abi: −11.toyos-acpi: +4, with the ECDT decoder's 70 lines deleted.main.rs+158 −58. Its production grew by 34 lines (thePortstrait andMachine,Server::new), and its tests by 66.🤖 Generated with Claude Code
https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C