Harden ACPI initialization and fix MADT traversal for malformed tables
Some checks failed
Builds / ExectOS (amd64, release) (push) Failing after 41s
Builds / ExectOS (i686, release) (push) Failing after 40s
Builds / ExectOS (i686, debug) (push) Failing after 53s
Builds / ExectOS (amd64, debug) (push) Failing after 56s

This commit is contained in:
2026-04-12 17:42:15 +02:00
parent 55cb12c978
commit d7f390b236

View File

@@ -4,6 +4,7 @@
* FILE: xtoskrnl/hl/x86/acpi.cc * FILE: xtoskrnl/hl/x86/acpi.cc
* DESCRIPTION: Advanced Configuration and Power Interface (ACPI) support * DESCRIPTION: Advanced Configuration and Power Interface (ACPI) support
* DEVELOPERS: Rafal Kupiec <belliash@codingworkshop.eu.org> * DEVELOPERS: Rafal Kupiec <belliash@codingworkshop.eu.org>
* Aiken Harris <harraiken91@gmail.com>
*/ */
#include <xtos.hh> #include <xtos.hh>
@@ -196,14 +197,23 @@ HL::Acpi::InitializeAcpiSystemDescriptionTable(OUT PACPI_DESCRIPTION_HEADER *Acp
AcpiResource = (PSYSTEM_RESOURCE_ACPI)ResourceHeader; AcpiResource = (PSYSTEM_RESOURCE_ACPI)ResourceHeader;
RsdpAddress.QuadPart = (LONGLONG)AcpiResource->Header.PhysicalAddress; RsdpAddress.QuadPart = (LONGLONG)AcpiResource->Header.PhysicalAddress;
/* Map RSDP and mark it as CD/WT to avoid delays in write-back cache */ /* Map RSDP using hardware memory pool */
Status = MM::HardwarePool::MapHardwareMemory(RsdpAddress, 1, TRUE, (PVOID *)&RsdpStructure); Status = MM::HardwarePool::MapHardwareMemory(RsdpAddress, 1, TRUE, (PVOID *)&RsdpStructure);
if(Status != STATUS_SUCCESS)
{
/* Failed to map RSDP, return error */
return Status;
}
/* Mark RSDP as CD/WT to avoid delays in write-back cache */
MM::HardwarePool::MarkHardwareMemoryWriteThrough(RsdpStructure, 1); MM::HardwarePool::MarkHardwareMemoryWriteThrough(RsdpStructure, 1);
/* Validate RSDP signature */ /* Validate RSDP signature */
if(Status != STATUS_SUCCESS || RsdpStructure->Signature != ACPI_RSDP_SIGNATURE) if(RsdpStructure->Signature != ACPI_RSDP_SIGNATURE)
{ {
/* Not mapped correctly or invalid RSDP signature, return error */ /* Invalid RSDP signature, unmap and return error */
MM::HardwarePool::UnmapHardwareMemory(RsdpStructure, 1, TRUE);
RsdpStructure = NULLPTR;
return STATUS_INVALID_PARAMETER; return STATUS_INVALID_PARAMETER;
} }
@@ -219,34 +229,40 @@ HL::Acpi::InitializeAcpiSystemDescriptionTable(OUT PACPI_DESCRIPTION_HEADER *Acp
RsdtAddress.QuadPart = (LONGLONG)RsdpStructure->RsdtAddress; RsdtAddress.QuadPart = (LONGLONG)RsdpStructure->RsdtAddress;
} }
/* Map RSDT/XSDT as CD/WT */ /* Map RSDT/XSDT using hardware memory pool */
Status = MM::HardwarePool::MapHardwareMemory(RsdtAddress, 2, TRUE, (PVOID *)&Rsdt); Status = MM::HardwarePool::MapHardwareMemory(RsdtAddress, 2, TRUE, (PVOID *)&Rsdt);
if(Status != STATUS_SUCCESS)
{
/* Failed to map RSDT/XSDT, return error */
return Status;
}
/* Mark RSDT/XSDT as CD/WT */
MM::HardwarePool::MarkHardwareMemoryWriteThrough(Rsdt, 2); MM::HardwarePool::MarkHardwareMemoryWriteThrough(Rsdt, 2);
/* Validate RSDT/XSDT signature */ /* Validate RSDT/XSDT signature */
if((Status != STATUS_SUCCESS) || if(Rsdt->Header.Signature != ACPI_RSDT_SIGNATURE && Rsdt->Header.Signature != ACPI_XSDT_SIGNATURE)
(Rsdt->Header.Signature != ACPI_RSDT_SIGNATURE &&
Rsdt->Header.Signature != ACPI_XSDT_SIGNATURE))
{ {
/* Not mapped correctly or invalid RSDT/XSDT signature, return error */ /* Not mapped correctly or invalid RSDT/XSDT signature, unmap and return error */
MM::HardwarePool::UnmapHardwareMemory(Rsdt, 2, TRUE);
return STATUS_INVALID_PARAMETER; return STATUS_INVALID_PARAMETER;
} }
/* Calculate the length of all available ACPI tables and remap it if needed */ /* Calculate the length of all available ACPI tables and remap it if needed */
RsdtPages = ((RsdtAddress.LowPart & (MM_PAGE_SIZE - 1)) + Rsdt->Header.Length + (MM_PAGE_SIZE - 1)) >> MM_PAGE_SHIFT; RsdtPages = (((RsdtAddress.LowPart & (MM_PAGE_SIZE - 1)) + Rsdt->Header.Length + (MM_PAGE_SIZE - 1)) >> MM_PAGE_SHIFT);
if(RsdtPages != 2) if(RsdtPages != 2)
{ {
/* RSDT/XSDT needs less or more than 2 pages, remap it */ /* RSDT/XSDT needs less or more than 2 pages, remap it */
MM::HardwarePool::UnmapHardwareMemory(Rsdt, 2, TRUE); MM::HardwarePool::UnmapHardwareMemory(Rsdt, 2, TRUE);
Status = MM::HardwarePool::MapHardwareMemory(RsdtAddress, RsdtPages, TRUE, (PVOID *)&Rsdt); Status = MM::HardwarePool::MapHardwareMemory(RsdtAddress, RsdtPages, TRUE, (PVOID *)&Rsdt);
MM::HardwarePool::MarkHardwareMemoryWriteThrough(Rsdt, RsdtPages);
/* Make sure remapping was successful */
if(Status != STATUS_SUCCESS) if(Status != STATUS_SUCCESS)
{ {
/* Remapping failed, return error */ /* Remapping failed, return error */
return STATUS_INSUFFICIENT_RESOURCES; return STATUS_INSUFFICIENT_RESOURCES;
} }
/* Mark remapped RSDT/XSDT as CD/WT */
MM::HardwarePool::MarkHardwareMemoryWriteThrough(Rsdt, RsdtPages);
} }
/* Get ACPI table header and return success */ /* Get ACPI table header and return success */
@@ -267,6 +283,7 @@ HL::Acpi::InitializeAcpiSystemInformation(VOID)
{ {
PACPI_MADT_LOCAL_X2APIC LocalX2Apic; PACPI_MADT_LOCAL_X2APIC LocalX2Apic;
PACPI_MADT_LOCAL_APIC LocalApic; PACPI_MADT_LOCAL_APIC LocalApic;
PACPI_SUBTABLE_HEADER SubTable;
ULONG_PTR MadtTable; ULONG_PTR MadtTable;
PACPI_MADT Madt; PACPI_MADT Madt;
XTSTATUS Status; XTSTATUS Status;
@@ -293,11 +310,20 @@ HL::Acpi::InitializeAcpiSystemInformation(VOID)
CpuCount = 0; CpuCount = 0;
/* Traverse all MADT tables to get system information */ /* Traverse all MADT tables to get system information */
while(MadtTable <= ((ULONG_PTR)Madt + Madt->Header.Length)) while(MadtTable < ((ULONG_PTR)Madt + Madt->Header.Length))
{ {
/* Get current MADT subtable header */
SubTable = (PACPI_SUBTABLE_HEADER)MadtTable;
/* Prevent infinite loops if BIOS provides 0 length */
if(SubTable->Length == 0)
{
/* Broken ACPI table, abort traversal */
break;
}
/* Check if this is a local APIC subtable */ /* Check if this is a local APIC subtable */
if((((PACPI_SUBTABLE_HEADER)MadtTable)->Type == ACPI_MADT_TYPE_LOCAL_APIC) && if(SubTable->Type == ACPI_MADT_TYPE_LOCAL_APIC && SubTable->Length >= sizeof(ACPI_MADT_LOCAL_APIC))
(((PACPI_SUBTABLE_HEADER)MadtTable)->Length == sizeof(ACPI_MADT_LOCAL_APIC)))
{ {
/* Get local APIC subtable */ /* Get local APIC subtable */
LocalApic = (PACPI_MADT_LOCAL_APIC)MadtTable; LocalApic = (PACPI_MADT_LOCAL_APIC)MadtTable;
@@ -313,12 +339,8 @@ HL::Acpi::InitializeAcpiSystemInformation(VOID)
/* Increment number of CPUs */ /* Increment number of CPUs */
CpuCount++; CpuCount++;
} }
/* Go to the next MADT table */
MadtTable += ((PACPI_SUBTABLE_HEADER)MadtTable)->Length;
} }
else if((((PACPI_SUBTABLE_HEADER)MadtTable)->Type == ACPI_MADT_TYPE_LOCAL_X2APIC) && else if(SubTable->Type == ACPI_MADT_TYPE_LOCAL_X2APIC && SubTable->Length >= sizeof(ACPI_MADT_LOCAL_X2APIC))
(((PACPI_SUBTABLE_HEADER)MadtTable)->Length == sizeof(ACPI_MADT_LOCAL_X2APIC)))
{ {
/* Get local X2APIC subtable */ /* Get local X2APIC subtable */
LocalX2Apic = (PACPI_MADT_LOCAL_X2APIC)MadtTable; LocalX2Apic = (PACPI_MADT_LOCAL_X2APIC)MadtTable;
@@ -334,15 +356,10 @@ HL::Acpi::InitializeAcpiSystemInformation(VOID)
/* Increment number of CPUs */ /* Increment number of CPUs */
CpuCount++; CpuCount++;
} }
}
/* Go to the next MADT table */ /* Safely advance pointer using proper subtable length */
MadtTable += ((PACPI_SUBTABLE_HEADER)MadtTable)->Length; MadtTable += SubTable->Length;
}
else
{
/* Any other MADT table, try to go to the next one byte-by-byte */
MadtTable += 1;
}
} }
/* Store number of CPUs */ /* Store number of CPUs */
@@ -596,18 +613,31 @@ HL::Acpi::QueryAcpiTables(IN ULONG Signature,
/* Check if DSDT or FACS table requested */ /* Check if DSDT or FACS table requested */
if(Signature == ACPI_DSDT_SIGNATURE) if(Signature == ACPI_DSDT_SIGNATURE)
{ {
/* Get DSDT address */ /* Prefer 64-bit address on ACPI 2.0+ */
TableAddress.LowPart = Fadt->Dsdt; if(Fadt->Header.Revision >= 2 && Fadt->XDsdt.QuadPart != 0)
{
TableAddress.QuadPart = Fadt->XDsdt.QuadPart;
}
else
{
TableAddress.LowPart = Fadt->Dsdt;
TableAddress.HighPart = 0;
}
} }
else else
{ {
/* Get FACS address */ /* Prefer 64-bit address on ACPI 2.0+ */
TableAddress.LowPart = Fadt->FirmwareCtrl; if(Fadt->Header.Revision >= 2 && Fadt->XFirmwareCtrl.QuadPart != 0)
{
TableAddress.QuadPart = Fadt->XFirmwareCtrl.QuadPart;
}
else
{
TableAddress.LowPart = Fadt->FirmwareCtrl;
TableAddress.HighPart = 0;
}
} }
/* Fill in high part of ACPI table address */
TableAddress.HighPart = 0;
/* Map table using hardware memory pool */ /* Map table using hardware memory pool */
Status = MM::HardwarePool::MapHardwareMemory(TableAddress, 2, TRUE, (PVOID*)&TableHeader); Status = MM::HardwarePool::MapHardwareMemory(TableAddress, 2, TRUE, (PVOID*)&TableHeader);
if(Status != STATUS_SUCCESS) if(Status != STATUS_SUCCESS)
@@ -618,11 +648,12 @@ HL::Acpi::QueryAcpiTables(IN ULONG Signature,
} }
else else
{ {
/* Query cache for XSDP table */ /* Query cache for XSDT table */
Status = QueryAcpiCache(ACPI_XSDT_SIGNATURE, (PACPI_DESCRIPTION_HEADER*)&Xsdt); Status = QueryAcpiCache(ACPI_XSDT_SIGNATURE, (PACPI_DESCRIPTION_HEADER*)&Xsdt);
if(Status != STATUS_SUCCESS) if(Status != STATUS_SUCCESS)
{ {
/* XSDP not found, query cache for RSDP table */ /* XSDT not found, query cache for RSDT table */
Xsdt = NULLPTR;
Status = QueryAcpiCache(ACPI_RSDT_SIGNATURE, (PACPI_DESCRIPTION_HEADER*)&Rsdt); Status = QueryAcpiCache(ACPI_RSDT_SIGNATURE, (PACPI_DESCRIPTION_HEADER*)&Rsdt);
} }
@@ -633,22 +664,22 @@ HL::Acpi::QueryAcpiTables(IN ULONG Signature,
return Status; return Status;
} }
/* Get table count depending on root table type */ /* Get table count depending on root table type securely */
if(Xsdt != NULLPTR) if(Xsdt != NULLPTR)
{ {
/* Get table count from XSDT */ if(Xsdt->Header.Length < sizeof(ACPI_DESCRIPTION_HEADER)) return STATUS_INVALID_PARAMETER;
TableCount = (Xsdt->Header.Length - sizeof(ACPI_DESCRIPTION_HEADER)) / 8; TableCount = (Xsdt->Header.Length - sizeof(ACPI_DESCRIPTION_HEADER)) / 8;
} }
else else
{ {
/* Get table count from RSDT */ if(Rsdt->Header.Length < sizeof(ACPI_DESCRIPTION_HEADER)) return STATUS_INVALID_PARAMETER;
TableCount = (Rsdt->Header.Length - sizeof(ACPI_DESCRIPTION_HEADER)) / 4; TableCount = (Rsdt->Header.Length - sizeof(ACPI_DESCRIPTION_HEADER)) / 4;
} }
/* Iterate over all ACPI tables */ /* Iterate over all ACPI tables */
for(TableIndex = 0; TableIndex < TableCount; TableIndex++) for(TableIndex = 0; TableIndex < TableCount; TableIndex++)
{ {
/* Check if XSDP or RSDT is used */ /* Check if XSDT or RSDT is used */
if(Xsdt != NULLPTR) if(Xsdt != NULLPTR)
{ {
/* Get table header physical address from XSDT */ /* Get table header physical address from XSDT */
@@ -661,13 +692,6 @@ HL::Acpi::QueryAcpiTables(IN ULONG Signature,
TableAddress.HighPart = 0; TableAddress.HighPart = 0;
} }
/* Check whether some table is already mapped */
if(TableHeader != NULLPTR)
{
/* Unmap previous table */
MM::HardwarePool::UnmapHardwareMemory(TableHeader, 2, TRUE);
}
/* Map table using hardware memory pool */ /* Map table using hardware memory pool */
Status = MM::HardwarePool::MapHardwareMemory(TableAddress, 2, TRUE, (PVOID*)&TableHeader); Status = MM::HardwarePool::MapHardwareMemory(TableAddress, 2, TRUE, (PVOID*)&TableHeader);
if(Status != STATUS_SUCCESS) if(Status != STATUS_SUCCESS)
@@ -682,25 +706,31 @@ HL::Acpi::QueryAcpiTables(IN ULONG Signature,
/* Found requested ACPI table */ /* Found requested ACPI table */
break; break;
} }
/* Unmap non-matching table and try next one */
MM::HardwarePool::UnmapHardwareMemory(TableHeader, 2, TRUE);
TableHeader = NULLPTR;
} }
} }
/* Make sure table was found */ /* Ensure the table was actually found and mapped */
if(TableHeader->Signature != Signature) if(TableHeader == NULLPTR)
{ {
/* ACPI table not found, check if cleanup is needed */ /* ACPI table not found, return error */
if(TableHeader != NULLPTR)
{
/* Unmap non-matching ACPI table */
MM::HardwarePool::UnmapHardwareMemory(TableHeader, 2, TRUE);
}
/* Return error */
return STATUS_NOT_FOUND; return STATUS_NOT_FOUND;
} }
/* Don't validate FADT on old, broken firmwares with ACPI 2.0 or older */ /* Check if we broke out of the loop with the wrong table (safety check) */
if(TableHeader->Signature != ACPI_FADT_SIGNATURE || TableHeader->Revision > 2) if(TableHeader->Signature != Signature)
{
/* Unmap non-matching ACPI table and return error */
MM::HardwarePool::UnmapHardwareMemory(TableHeader, 2, TRUE);
return STATUS_NOT_FOUND;
}
/* Don't validate FACS and FADT on old, broken firmwares with ACPI 2.0 or older */
if((TableHeader->Signature != ACPI_FADT_SIGNATURE || TableHeader->Revision > 2) &&
(TableHeader->Signature != ACPI_FACS_SIGNATURE))
{ {
/* Validate table checksum */ /* Validate table checksum */
if(!ValidateAcpiTable(TableHeader, TableHeader->Length)) if(!ValidateAcpiTable(TableHeader, TableHeader->Length))
@@ -712,7 +742,7 @@ HL::Acpi::QueryAcpiTables(IN ULONG Signature,
} }
/* Calculate the length of ACPI table and remap it if needed */ /* Calculate the length of ACPI table and remap it if needed */
TablePages = (((ULONG_PTR)TableHeader & (MM_PAGE_SIZE - 1)) + TableHeader->Length + (MM_PAGE_SIZE - 1)) >> MM_PAGE_SHIFT; TablePages = (((TableAddress.LowPart & (MM_PAGE_SIZE - 1)) + TableHeader->Length + (MM_PAGE_SIZE - 1)) >> MM_PAGE_SHIFT);
if(TablePages != 2) if(TablePages != 2)
{ {
/* ACPI table needs less or more than 2 pages, remap it */ /* ACPI table needs less or more than 2 pages, remap it */