acpi: Use x86 PCI config method directly during startup

Fixes #18778, #19602, #19716, #19948, #20165

The ACPI tables on certain recent-ish Intel platforms read some
values from PCI config space during ACPI init. Since we haven't
initialized PCI yet during ACPI init, instead of reading from
config space, the ACPI bytecode gets garbage values. These values
are later used in an address compution. An address compution with
garbage values produces a garbage address. That garbage address
has certain upper bits set, which causes the corresponding page
table entry to have those upper bits set as well (since the page
mapping code doesn't sanitize the address). These upper bits being
set triggers a page fault due to invalid bits being set in the
page table, which we erroneously think is an SMAP violation.

Co-authored-by: Augustin Cavalier <[email protected]>
Change-Id: If75dffe47ebf3e14831ceac1dfbe492c6a6a6964
Reviewed-on: https://review.haiku-os.org/c/haiku/+/11272
Reviewed-by: waddlesplash <[email protected]>
(cherry picked from commit aa519fec255ea2196453b7e62c6f1540efbeefee)
Reviewed-on: https://review.haiku-os.org/c/haiku/+/11281
This commit is contained in:
Sam Roberts
2026-07-21 02:05:50 +00:00
committed by waddlesplash
parent 46fb8f1ca3
commit b422ca4d3a
3 changed files with 88 additions and 15 deletions
@@ -130,6 +130,10 @@
# include <boot_item.h>
# include <kernel.h>
# include <vm/vm.h>
# if defined(__i386__) || defined(__x86_64__)
# include "../../busses/pci/x86/PCI_x86.h"
# endif
#endif
__BEGIN_DECLS
@@ -940,6 +944,33 @@ AcpiOsReadPciConfiguration(ACPI_PCI_ID *pciId, UINT32 reg, UINT64 *value,
#ifdef _KERNEL_MODE
DEBUG_FUNCTION();
#if defined(__i386__) || defined(__x86_64__)
// On some x86 machines, ACPI initialization accesses PCI configuration space,
// but PCI won't be initialized yet, as ECAM depends on ACPI being initialized.
// To break the circular dependency, we access config space directly during early boot.
if (gKernelStartup) {
if (reg > 0xff)
return AE_ERROR;
out32(PCI_MECH1_REQ_DATA(pciId->Bus, pciId->Device, pciId->Function, reg),
PCI_MECH1_REQ_PORT);
switch (width / 8) {
case 1:
*value = in8(PCI_MECH1_DATA_PORT + (reg & 3));
break;
case 2:
*value = in16(PCI_MECH1_DATA_PORT + (reg & 3));
break;
case 4:
*value = in32(PCI_MECH1_DATA_PORT);
break;
default:
return AE_ERROR;
}
return AE_OK;
}
#endif
switch (width) {
case 8:
case 16:
@@ -977,6 +1008,31 @@ AcpiOsWritePciConfiguration(ACPI_PCI_ID *pciId, UINT32 reg,
{
#ifdef _KERNEL_MODE
DEBUG_FUNCTION();
#if defined(__i386__) || defined(__x86_64__)
if (gKernelStartup) {
if (reg > 0xff)
return AE_ERROR;
out32(PCI_MECH1_REQ_DATA(pciId->Bus, pciId->Device, pciId->Function, reg),
PCI_MECH1_REQ_PORT);
switch (width / 8) {
case 1:
out8((uint8)value, (uint16)(PCI_MECH1_DATA_PORT + (reg & 3)));
break;
case 2:
out16((uint16)value, (uint16)(PCI_MECH1_DATA_PORT + (reg & 3)));
break;
case 4:
out32((uint32)value, (uint16)PCI_MECH1_DATA_PORT);
break;
default:
return AE_ERROR;
}
return AE_OK;
}
#endif
gPCIManager->write_pci_config(
pciId->Bus, pciId->Device, pciId->Function, reg, width / 8, value);
return AE_OK;
@@ -0,0 +1,24 @@
/*
* Copyright 2022-2026, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef PCI_X86_H
#define PCI_X86_H
#include <SupportDefs.h>
#define PCI_MECH1_REQ_PORT 0xCF8
#define PCI_MECH1_DATA_PORT 0xCFC
#define PCI_MECH1_REQ_DATA(bus, device, func, offset) \
(0x80000000 | (bus << 16) | (device << 11) | (func << 8) | (offset & ~3))
#define PCI_MECH2_ENABLE_PORT 0x0cf8
#define PCI_MECH2_FORWARD_PORT 0x0cfa
#define PCI_MECH2_CONFIG_PORT(dev, offset) \
(uint16)(0xC00 | (dev << 8) | offset)
#endif // PCI_X86_H
@@ -14,19 +14,10 @@
#include <string.h>
#include <new>
#define PCI_MECH1_REQ_PORT 0xCF8
#define PCI_MECH1_DATA_PORT 0xCFC
#define PCI_MECH1_REQ_DATA(bus, device, func, offset) \
(0x80000000 | (bus << 16) | (device << 11) | (func << 8) | (offset & ~3))
#define PCI_MECH2_ENABLE_PORT 0x0cf8
#define PCI_MECH2_FORWARD_PORT 0x0cfa
#define PCI_MECH2_CONFIG_PORT(dev, offset) \
(uint16)(0xC00 | (dev << 8) | offset)
#include "PCI_x86.h"
//#pragma mark - driver
// #pragma mark - driver
float
@@ -247,14 +238,15 @@ X86PCIControllerMeth1::WriteConfig(
}
status_t X86PCIControllerMeth1::GetMaxBusDevices(int32& count)
status_t
X86PCIControllerMeth1::GetMaxBusDevices(int32& count)
{
count = 32;
return B_OK;
}
//#pragma mark - X86PCIControllerMeth2
// #pragma mark - X86PCIControllerMeth2
status_t
@@ -334,14 +326,15 @@ X86PCIControllerMeth2::WriteConfig(
}
status_t X86PCIControllerMeth2::GetMaxBusDevices(int32& count)
status_t
X86PCIControllerMeth2::GetMaxBusDevices(int32& count)
{
count = 16;
return B_OK;
}
//#pragma mark - X86PCIControllerMethPcie
// #pragma mark - X86PCIControllerMethPcie
status_t