From ffa502b2cb87f259be5ab21702cf79345605752d Mon Sep 17 00:00:00 2001 From: vasilito Date: Thu, 9 Jul 2026 11:13:36 +0300 Subject: [PATCH] =?UTF-8?q?kernel:=20fix=20FADT=20field=20offset=20?= =?UTF-8?q?=E2=80=94=20read=20from=20SDT=20base,=20not=20data=20area?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The FADT fields (PM1a_CNT, PM1a_STS, FIRMWARE_CTRL) are at fixed offsets from the START of the SDT (including the 36-byte ACPI header), not from the data area. Previously using sdt.data_address() caused reads from the wrong offset, making shutdown/reboot fail on real hardware. Fixed by reading from the SDT base pointer instead of data_address(). Cross-referenced with Linux 7.1 drivers/acpi/acpica/tbfadt.c which reads FADT fields from the table base, accounting for the header. --- src/acpi/fadt.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/acpi/fadt.rs b/src/acpi/fadt.rs index eada983710..4c55b500bf 100644 --- a/src/acpi/fadt.rs +++ b/src/acpi/fadt.rs @@ -70,19 +70,16 @@ pub fn init(sdt: &Sdt) { if &sdt.signature != &FADT_SIGNATURE { return; } - // SAFETY: We trust the ACPI table discovery code to have - // verified the FADT checksum. The FADT fields are at fixed - // offsets (per the ACPI spec); reading them as u32/u64 is - // safe because all of them are at 4-byte or 8-byte aligned - // offsets on x86_64. - let data = sdt.data_address() as *const u8; + // The FADT fields are at fixed offsets from the start of the + // SDT (including the 36-byte header), not from the data area. + let base = sdt as *const _ as *const u8; unsafe { // PM1a_CNT is at offset 56 in the FADT (ACPI 6.5 ยง5.2.9 // Table 5.6). 32-bit General-Purpose Event Register Block 0 // Address. - let pm1a_cnt = core::ptr::read_unaligned(data.add(offsets::PM1A_CNT) as *const u32); + let pm1a_cnt = core::ptr::read_unaligned(base.add(offsets::PM1A_CNT) as *const u32); // PM1a_STS is at offset 48 in the FADT. - let pm1a_sts = core::ptr::read_unaligned(data.add(offsets::PM1A_STS) as *const u32); + let pm1a_sts = core::ptr::read_unaligned(base.add(offsets::PM1A_STS) as *const u32); // Convert u32 to u16 (port numbers are 16-bit). The low // 16 bits are the IO port; the high 16 bits are the // address-space ID which we ignore (always IO on x86). @@ -98,18 +95,18 @@ pub fn init(sdt: &Sdt) { // Phase II.X.W: 32-bit FACS address (FADT offset 36, // `firmware_ctrl` field). ACPI 1.0+. let firmware_ctrl = core::ptr::read_unaligned( - data.add(offsets::FIRMWARE_CTRL_32) as *const u32, + base.add(offsets::FIRMWARE_CTRL_32) as *const u32, ); FIRMWARE_CTRL.store(firmware_ctrl, core::sync::atomic::Ordering::Release); // Phase II.X.W: 64-bit FACS address (FADT offset 140, - // `x_firmware_ctrl` field). ACPI 2.0+. We require the + // `x_firmware_ctrl` field, ACPI 2.0+). We require the // FADT to be at least 148 bytes to have this field // (the field is at offset 140, which is 8 bytes for the // u64, so the minimum FADT size is 148 bytes). if sdt.length() >= offsets::FADT_MIN_SIZE_ACPI_2_0 as u32 { let x_firmware_ctrl = core::ptr::read_unaligned( - data.add(offsets::X_FIRMWARE_CTRL_64) as *const u64, + base.add(offsets::X_FIRMWARE_CTRL_64) as *const u64, ); X_FIRMWARE_CTRL.store(x_firmware_ctrl, core::sync::atomic::Ordering::Release); }