summaryrefslogtreecommitdiff
path: root/Kernel/Firmware/ACPI
diff options
context:
space:
mode:
Diffstat (limited to 'Kernel/Firmware/ACPI')
-rw-r--r--Kernel/Firmware/ACPI/Definitions.h334
-rw-r--r--Kernel/Firmware/ACPI/Initialize.cpp41
-rw-r--r--Kernel/Firmware/ACPI/Initialize.h14
-rw-r--r--Kernel/Firmware/ACPI/MultiProcessorParser.cpp157
-rw-r--r--Kernel/Firmware/ACPI/MultiProcessorParser.h170
-rw-r--r--Kernel/Firmware/ACPI/Parser.cpp440
-rw-r--r--Kernel/Firmware/ACPI/Parser.h102
7 files changed, 1258 insertions, 0 deletions
diff --git a/Kernel/Firmware/ACPI/Definitions.h b/Kernel/Firmware/ACPI/Definitions.h
new file mode 100644
index 0000000000..ee46562144
--- /dev/null
+++ b/Kernel/Firmware/ACPI/Definitions.h
@@ -0,0 +1,334 @@
+/*
+ * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/RefCounted.h>
+#include <AK/Types.h>
+#include <AK/Vector.h>
+#include <Kernel/PhysicalAddress.h>
+
+namespace Kernel::ACPI {
+
+namespace FADTFlags {
+
+// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#fixed-acpi-description-table-fixed-feature-flags
+enum class FeatureFlags : u32 {
+ WBINVD = 1 << 0,
+ WBINVD_FLUSH = 1 << 1,
+ PROC_C1 = 1 << 2,
+ P_LVL2_UP = 1 << 3,
+ PWR_BUTTON = 1 << 4,
+ SLP_BUTTON = 1 << 5,
+ FIX_RTC = 1 << 6,
+ RTC_s4 = 1 << 7,
+ TMR_VAL_EXT = 1 << 8,
+ DCK_CAP = 1 << 9,
+ RESET_REG_SUPPORTED = 1 << 10,
+ SEALED_CASE = 1 << 11,
+ HEADLESS = 1 << 12,
+ CPU_SW_SLP = 1 << 13,
+ PCI_EXP_WAK = 1 << 14,
+ USE_PLATFORM_CLOCK = 1 << 15,
+ S4_RTC_STS_VALID = 1 << 16,
+ REMOTE_POWER_ON_CAPABLE = 1 << 17,
+ FORCE_APIC_CLUSTER_MODEL = 1 << 18,
+ FORCE_APIC_PHYSICAL_DESTINATION_MODE = 1 << 19,
+ HW_REDUCED_ACPI = 1 << 20,
+ LOW_POWER_S0_IDLE_CAPABLE = 1 << 21
+};
+
+// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#fixed-acpi-description-table-boot-ia-pc-boot-architecture-flags
+enum class IA_PC_Flags : u8 {
+ Legacy_Devices = 1 << 0,
+ PS2_8042 = 1 << 1,
+ VGA_Not_Present = 1 << 2,
+ MSI_Not_Supported = 1 << 3,
+ PCIe_ASPM_Controls = 1 << 4,
+ CMOS_RTC_Not_Present = 1 << 5
+};
+
+struct [[gnu::packed]] HardwareFeatures {
+ bool wbinvd : 1;
+ bool wbinvd_flush : 1;
+ bool processor_c1 : 1;
+ bool multiprocessor_c2 : 1;
+ bool power_button : 1;
+ bool sleep_button : 1;
+ bool fix_rtc : 1;
+ bool rtc_s4 : 1;
+ bool timer_value_extension : 1;
+ bool docking_capability : 1;
+ bool reset_register_supported : 1;
+ bool sealed_case : 1;
+ bool headless : 1;
+ bool cpu_software_sleep : 1;
+ bool pci_express_wake : 1;
+ bool use_platform_clock : 1;
+ bool s4_rtc_status_valid : 1;
+ bool remote_power_on_capable : 1;
+ bool force_apic_cluster_model : 1;
+ bool force_apic_physical_destination_mode : 1;
+ bool hardware_reduced_acpi : 1;
+ bool low_power_s0_idle_capable : 1;
+};
+struct [[gnu::packed]] x86_Specific_Flags {
+ bool legacy_devices : 1;
+ bool keyboard_8042 : 1;
+ bool vga_not_present : 1;
+ bool msi_not_supported : 1;
+ bool cmos_rtc_not_present : 1;
+};
+};
+
+namespace GenericAddressStructure {
+enum class AddressSpace {
+ SystemMemory = 0,
+ SystemIO = 1,
+ PCIConfigurationSpace = 2,
+ EmbeddedController = 3,
+ SMBus = 4,
+ PCC = 0xA,
+ FunctionalFixedHardware = 0x7F
+};
+enum class AccessSize {
+ Undefined = 0,
+ Byte = 1,
+ Word = 2,
+ DWord = 3,
+ QWord = 4
+};
+enum class BitWidth {
+ Undefined = 0,
+ Byte = 8,
+ Word = 16,
+ DWord = 32,
+ QWord = 64
+};
+}
+
+namespace Structures {
+
+// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#root-system-description-pointer-rsdp-structure
+struct [[gnu::packed]] RSDPDescriptor {
+ char sig[8];
+ u8 checksum;
+ char oem_id[6];
+ u8 revision;
+ u32 rsdt_ptr;
+};
+
+struct [[gnu::packed]] RSDPDescriptor20 {
+ RSDPDescriptor base;
+ u32 length;
+ u64 xsdt_ptr;
+ u8 ext_checksum;
+ u8 reserved[3];
+};
+
+// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#system-description-table-header
+struct [[gnu::packed]] SDTHeader {
+ char sig[4];
+ u32 length;
+ u8 revision;
+ u8 checksum;
+ char oem_id[6];
+ char oem_table_id[8];
+ u32 oem_revision;
+ u32 creator_id;
+ u32 creator_revision;
+};
+
+// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#root-system-description-table-rsdt
+struct [[gnu::packed]] RSDT {
+ SDTHeader h;
+ u32 table_ptrs[];
+};
+
+// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#extended-system-description-table-xsdt
+struct [[gnu::packed]] XSDT {
+ SDTHeader h;
+ u64 table_ptrs[];
+};
+
+struct [[gnu::packed]] GenericAddressStructure {
+ u8 address_space;
+ u8 bit_width;
+ u8 bit_offset;
+ u8 access_size;
+ u64 address;
+};
+
+struct [[gnu::packed]] HPET {
+ SDTHeader h;
+ u8 hardware_revision_id;
+ u8 attributes;
+ u16 pci_vendor_id;
+ GenericAddressStructure event_timer_block;
+ u8 hpet_number;
+ u16 mininum_clock_tick;
+ u8 page_protection;
+};
+
+// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#fixed-acpi-description-table-fadt
+struct [[gnu::packed]] FADT {
+ SDTHeader h;
+ u32 firmware_ctrl;
+ u32 dsdt_ptr;
+ u8 reserved;
+ u8 preferred_pm_profile;
+ u16 sci_int;
+ u32 smi_cmd;
+ u8 acpi_enable_value;
+ u8 acpi_disable_value;
+ u8 s4bios_req;
+ u8 pstate_cnt;
+ u32 PM1a_EVT_BLK;
+ u32 PM1b_EVT_BLK;
+ u32 PM1a_CNT_BLK;
+ u32 PM1b_CNT_BLK;
+ u32 PM2_CNT_BLK;
+ u32 PM_TMR_BLK;
+ u32 GPE0_BLK;
+ u32 GPE1_BLK;
+ u8 PM1_EVT_LEN;
+ u8 PM1_CNT_LEN;
+ u8 PM2_CNT_LEN;
+ u8 PM_TMR_LEN;
+ u8 GPE0_BLK_LEN;
+ u8 GPE1_BLK_LEN;
+ u8 GPE1_BASE;
+ u8 cst_cnt;
+ u16 P_LVL2_LAT;
+ u16 P_LVL3_LAT;
+ u16 flush_size;
+ u16 flush_stride;
+ u8 duty_offset;
+ u8 duty_width;
+ u8 day_alrm;
+ u8 mon_alrm;
+ u8 century;
+ u16 ia_pc_boot_arch_flags;
+ u8 reserved2;
+ u32 flags;
+ GenericAddressStructure reset_reg;
+ u8 reset_value;
+ u16 arm_boot_arch;
+ u8 fadt_minor_version;
+ u64 x_firmware_ctrl;
+ u64 x_dsdt;
+ GenericAddressStructure x_pm1a_evt_blk;
+ GenericAddressStructure x_pm1b_evt_blk;
+ GenericAddressStructure x_pm1a_cnt_blk;
+ GenericAddressStructure x_pm1b_cnt_blk;
+ GenericAddressStructure x_pm2_cnt_blk;
+ GenericAddressStructure x_pm_tmr_blk;
+ GenericAddressStructure x_gpe0_blk;
+ GenericAddressStructure x_gpe1_blk;
+ GenericAddressStructure sleep_control;
+ GenericAddressStructure sleep_status;
+ u64 hypervisor_vendor_identity;
+};
+
+// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#interrupt-controller-structure-types
+enum class MADTEntryType {
+ LocalAPIC = 0x0,
+ IOAPIC = 0x1,
+ InterruptSourceOverride = 0x2,
+ NMI_Source = 0x3,
+ LocalAPIC_NMI = 0x4,
+ LocalAPIC_Address_Override = 0x5,
+ IO_SAPIC = 0x6,
+ Local_SAPIC = 0x7,
+ Platform_interrupt_Sources = 0x8,
+ Local_x2APIC = 0x9,
+ Local_x2APIC_NMI = 0xA,
+ GIC_CPU = 0xB,
+ GIC_Distributor = 0xC,
+ GIC_MSI = 0xD,
+ GIC_Redistrbutor = 0xE,
+ GIC_Interrupt_Translation = 0xF
+};
+
+struct [[gnu::packed]] MADTEntryHeader {
+ u8 type;
+ u8 length;
+};
+
+namespace MADTEntries {
+
+// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#i-o-apic-structure
+struct [[gnu::packed]] IOAPIC {
+ MADTEntryHeader h;
+ u8 ioapic_id;
+ u8 reserved;
+ u32 ioapic_address;
+ u32 gsi_base;
+};
+
+// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#processor-local-apic-structure
+struct [[gnu::packed]] ProcessorLocalAPIC {
+ MADTEntryHeader h;
+ u8 acpi_processor_id;
+ u8 apic_id;
+ u32 flags;
+};
+
+// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#processor-local-x2apic-structure
+struct [[gnu::packed]] ProcessorLocalX2APIC {
+ MADTEntryHeader h;
+ u16 reserved;
+ u32 apic_id;
+ u32 flags;
+ u32 acpi_processor_id;
+};
+
+// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#interrupt-source-override-structure
+struct [[gnu::packed]] InterruptSourceOverride {
+ MADTEntryHeader h;
+ u8 bus;
+ u8 source;
+ u32 global_system_interrupt;
+ u16 flags;
+};
+}
+
+// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#multiple-apic-description-table-madt-format
+struct [[gnu::packed]] MADT {
+ SDTHeader h;
+ u32 lapic_address;
+ u32 flags;
+ MADTEntryHeader entries[];
+};
+
+struct [[gnu::packed]] AMLTable {
+ SDTHeader h;
+ char aml_code[];
+};
+
+struct [[gnu::packed]] PCI_MMIO_Descriptor {
+ u64 base_addr;
+ u16 seg_group_number;
+ u8 start_pci_bus;
+ u8 end_pci_bus;
+ u32 reserved;
+};
+
+struct [[gnu::packed]] MCFG {
+ SDTHeader header;
+ u64 reserved;
+ PCI_MMIO_Descriptor descriptors[];
+};
+}
+
+class Parser;
+
+namespace StaticParsing {
+Optional<PhysicalAddress> find_rsdp();
+Optional<PhysicalAddress> find_table(PhysicalAddress rsdp, const StringView& signature);
+}
+
+}
diff --git a/Kernel/Firmware/ACPI/Initialize.cpp b/Kernel/Firmware/ACPI/Initialize.cpp
new file mode 100644
index 0000000000..07ae24e4bb
--- /dev/null
+++ b/Kernel/Firmware/ACPI/Initialize.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020-2021, Liav A. <liavalb@hotmail.co.il>
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <Kernel/CommandLine.h>
+#include <Kernel/Firmware/ACPI/Parser.h>
+#include <Kernel/Memory/TypedMapping.h>
+#include <Kernel/Sections.h>
+
+namespace Kernel::ACPI {
+
+UNMAP_AFTER_INIT void initialize()
+{
+ auto feature_level = kernel_command_line().acpi_feature_level();
+ if (feature_level == AcpiFeatureLevel::Disabled)
+ return;
+
+ auto rsdp = StaticParsing::find_rsdp();
+ if (!rsdp.has_value())
+ return;
+
+ auto facp = StaticParsing::find_table(rsdp.value(), "FACP");
+ if (!facp.has_value())
+ return;
+ auto facp_table = Memory::map_typed<Structures::FADT>(facp.value());
+ u8 irq_line = facp_table->sci_int;
+
+ Parser::must_initialize(rsdp.value(), facp.value(), irq_line);
+ if (kernel_command_line().acpi_feature_level() == AcpiFeatureLevel::Enabled)
+ Parser::the()->enable_aml_parsing();
+}
+
+bool is_enabled()
+{
+ return Parser::the();
+}
+
+}
diff --git a/Kernel/Firmware/ACPI/Initialize.h b/Kernel/Firmware/ACPI/Initialize.h
new file mode 100644
index 0000000000..4bf3946c29
--- /dev/null
+++ b/Kernel/Firmware/ACPI/Initialize.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+namespace Kernel::ACPI {
+
+bool is_enabled();
+void initialize();
+
+}
diff --git a/Kernel/Firmware/ACPI/MultiProcessorParser.cpp b/Kernel/Firmware/ACPI/MultiProcessorParser.cpp
new file mode 100644
index 0000000000..745f63d90a
--- /dev/null
+++ b/Kernel/Firmware/ACPI/MultiProcessorParser.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/StringView.h>
+#include <Kernel/Debug.h>
+#include <Kernel/Firmware/ACPI/MultiProcessorParser.h>
+#include <Kernel/Firmware/BIOS.h>
+#include <Kernel/Interrupts/IOAPIC.h>
+#include <Kernel/Memory/TypedMapping.h>
+#include <Kernel/Sections.h>
+#include <Kernel/StdLib.h>
+
+namespace Kernel {
+
+UNMAP_AFTER_INIT OwnPtr<MultiProcessorParser> MultiProcessorParser::autodetect()
+{
+ auto floating_pointer = find_floating_pointer();
+ if (!floating_pointer.has_value())
+ return {};
+ auto parser = adopt_own_if_nonnull(new (nothrow) MultiProcessorParser(floating_pointer.value()));
+ VERIFY(parser != nullptr);
+ return parser;
+}
+
+UNMAP_AFTER_INIT MultiProcessorParser::MultiProcessorParser(PhysicalAddress floating_pointer)
+ : m_floating_pointer(floating_pointer)
+{
+ dbgln("MultiProcessor: Floating Pointer Structure @ {}", m_floating_pointer);
+ parse_floating_pointer_data();
+ parse_configuration_table();
+}
+
+UNMAP_AFTER_INIT void MultiProcessorParser::parse_floating_pointer_data()
+{
+ auto floating_pointer = Memory::map_typed<MultiProcessor::FloatingPointer>(m_floating_pointer);
+ m_configuration_table = PhysicalAddress(floating_pointer->physical_address_ptr);
+ dbgln("Features {}, IMCR? {}", floating_pointer->feature_info[0], (floating_pointer->feature_info[0] & (1 << 7)));
+}
+
+UNMAP_AFTER_INIT void MultiProcessorParser::parse_configuration_table()
+{
+ auto configuration_table_length = Memory::map_typed<MultiProcessor::ConfigurationTableHeader>(m_configuration_table)->length;
+ auto config_table = Memory::map_typed<MultiProcessor::ConfigurationTableHeader>(m_configuration_table, configuration_table_length);
+
+ size_t entry_count = config_table->entry_count;
+ auto* entry = config_table->entries;
+ while (entry_count > 0) {
+ dbgln_if(MULTIPROCESSOR_DEBUG, "MultiProcessor: Entry Type {} detected.", entry->entry_type);
+ switch (entry->entry_type) {
+ case ((u8)MultiProcessor::ConfigurationTableEntryType::Processor):
+ entry = (MultiProcessor::EntryHeader*)(FlatPtr)entry + sizeof(MultiProcessor::ProcessorEntry);
+ break;
+ case ((u8)MultiProcessor::ConfigurationTableEntryType::Bus):
+ m_bus_entries.append(*(const MultiProcessor::BusEntry*)entry);
+ entry = (MultiProcessor::EntryHeader*)(FlatPtr)entry + sizeof(MultiProcessor::BusEntry);
+ break;
+ case ((u8)MultiProcessor::ConfigurationTableEntryType::IOAPIC):
+ entry = (MultiProcessor::EntryHeader*)(FlatPtr)entry + sizeof(MultiProcessor::IOAPICEntry);
+ break;
+ case ((u8)MultiProcessor::ConfigurationTableEntryType::IO_Interrupt_Assignment):
+ m_io_interrupt_assignment_entries.append(*(const MultiProcessor::IOInterruptAssignmentEntry*)entry);
+ entry = (MultiProcessor::EntryHeader*)(FlatPtr)entry + sizeof(MultiProcessor::IOInterruptAssignmentEntry);
+ break;
+ case ((u8)MultiProcessor::ConfigurationTableEntryType::Local_Interrupt_Assignment):
+ entry = (MultiProcessor::EntryHeader*)(FlatPtr)entry + sizeof(MultiProcessor::LocalInterruptAssignmentEntry);
+ break;
+ case ((u8)MultiProcessor::ConfigurationTableEntryType::SystemAddressSpaceMapping):
+ entry = (MultiProcessor::EntryHeader*)(FlatPtr)entry + sizeof(MultiProcessor::SystemAddressSpaceMappingEntry);
+ break;
+ case ((u8)MultiProcessor::ConfigurationTableEntryType::BusHierarchyDescriptor):
+ entry = (MultiProcessor::EntryHeader*)(FlatPtr)entry + sizeof(MultiProcessor::BusHierarchyDescriptorEntry);
+ break;
+ case ((u8)MultiProcessor::ConfigurationTableEntryType::CompatibilityBusAddressSpaceModifier):
+ entry = (MultiProcessor::EntryHeader*)(FlatPtr)entry + sizeof(MultiProcessor::CompatibilityBusAddressSpaceModifierEntry);
+ break;
+ default:
+ VERIFY_NOT_REACHED();
+ }
+ --entry_count;
+ }
+}
+
+UNMAP_AFTER_INIT Optional<PhysicalAddress> MultiProcessorParser::find_floating_pointer()
+{
+ StringView signature("_MP_");
+ auto mp_floating_pointer = map_ebda().find_chunk_starting_with(signature, 16);
+ if (mp_floating_pointer.has_value())
+ return mp_floating_pointer;
+ return map_bios().find_chunk_starting_with(signature, 16);
+}
+
+UNMAP_AFTER_INIT Vector<u8> MultiProcessorParser::get_pci_bus_ids() const
+{
+ Vector<u8> pci_bus_ids;
+ for (auto& entry : m_bus_entries) {
+ if (!strncmp("PCI ", entry.bus_type, strlen("PCI ")))
+ pci_bus_ids.append(entry.bus_id);
+ }
+ return pci_bus_ids;
+}
+
+UNMAP_AFTER_INIT Vector<PCIInterruptOverrideMetadata> MultiProcessorParser::get_pci_interrupt_redirections()
+{
+ dbgln("MultiProcessor: Get PCI IOAPIC redirections");
+ Vector<PCIInterruptOverrideMetadata> overrides;
+ auto pci_bus_ids = get_pci_bus_ids();
+ for (auto& entry : m_io_interrupt_assignment_entries) {
+ for (auto id : pci_bus_ids) {
+ if (id == entry.source_bus_id) {
+
+ dbgln("Interrupts: Bus {}, polarity {}, trigger mode {}, INT {}, IOAPIC {}, IOAPIC INTIN {}",
+ entry.source_bus_id,
+ entry.polarity,
+ entry.trigger_mode,
+ entry.source_bus_irq,
+ entry.destination_ioapic_id,
+ entry.destination_ioapic_intin_pin);
+ overrides.empend(
+ entry.source_bus_id,
+ entry.polarity,
+ entry.trigger_mode,
+ entry.source_bus_irq,
+ entry.destination_ioapic_id,
+ entry.destination_ioapic_intin_pin);
+ }
+ }
+ }
+
+ for (auto& override_metadata : overrides) {
+ dbgln("Interrupts: Bus {}, polarity {}, PCI device {}, trigger mode {}, INT {}, IOAPIC {}, IOAPIC INTIN {}",
+ override_metadata.bus(),
+ override_metadata.polarity(),
+ override_metadata.pci_device_number(),
+ override_metadata.trigger_mode(),
+ override_metadata.pci_interrupt_pin(),
+ override_metadata.ioapic_id(),
+ override_metadata.ioapic_interrupt_pin());
+ }
+ return overrides;
+}
+
+UNMAP_AFTER_INIT PCIInterruptOverrideMetadata::PCIInterruptOverrideMetadata(u8 bus_id, u8 polarity, u8 trigger_mode, u8 source_irq, u32 ioapic_id, u16 ioapic_int_pin)
+ : m_bus_id(bus_id)
+ , m_polarity(polarity)
+ , m_trigger_mode(trigger_mode)
+ , m_pci_interrupt_pin(source_irq & 0b11)
+ , m_pci_device_number((source_irq >> 2) & 0b11111)
+ , m_ioapic_id(ioapic_id)
+ , m_ioapic_interrupt_pin(ioapic_int_pin)
+{
+}
+
+}
diff --git a/Kernel/Firmware/ACPI/MultiProcessorParser.h b/Kernel/Firmware/ACPI/MultiProcessorParser.h
new file mode 100644
index 0000000000..dcbed5950a
--- /dev/null
+++ b/Kernel/Firmware/ACPI/MultiProcessorParser.h
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Types.h>
+#include <Kernel/Memory/Region.h>
+#include <Kernel/PhysicalAddress.h>
+#include <Kernel/VirtualAddress.h>
+
+namespace Kernel {
+namespace MultiProcessor {
+
+struct [[gnu::packed]] FloatingPointer {
+ char sig[4];
+ u32 physical_address_ptr;
+ u8 length;
+ u8 specification_revision;
+ u8 checksum;
+ u8 feature_info[5];
+};
+
+struct [[gnu::packed]] EntryHeader {
+ u8 entry_type;
+};
+
+struct [[gnu::packed]] ConfigurationTableHeader {
+ char sig[4];
+ u16 length;
+ u8 specification_revision;
+ u8 checksum;
+ char oem_id[8];
+ char product_id[12];
+ u32 oem_table_ptr;
+ u16 oem_table_size;
+ u16 entry_count;
+ u32 local_apic_address;
+ u16 ext_table_length;
+ u8 ext_table_checksum;
+ u8 reserved;
+ EntryHeader entries[];
+};
+
+enum class ConfigurationTableEntryType {
+ Processor = 0,
+ Bus = 1,
+ IOAPIC = 2,
+ IO_Interrupt_Assignment = 3,
+ Local_Interrupt_Assignment = 4,
+ SystemAddressSpaceMapping = 128,
+ BusHierarchyDescriptor = 129,
+ CompatibilityBusAddressSpaceModifier = 130
+};
+
+struct [[gnu::packed]] ExtEntryHeader {
+ u8 entry_type;
+ u8 entry_length;
+};
+
+struct [[gnu::packed]] ProcessorEntry {
+ EntryHeader h;
+ u8 local_apic_id;
+ u8 local_apic_version;
+ u8 cpu_flags;
+ u32 cpu_signature;
+ u32 feature_flags;
+ u8 reserved[8];
+};
+
+struct [[gnu::packed]] BusEntry {
+ EntryHeader h;
+ u8 bus_id;
+ char bus_type[6];
+};
+
+struct [[gnu::packed]] IOAPICEntry {
+ EntryHeader h;
+ u8 ioapic_id;
+ u8 ioapic_version;
+ u8 ioapic_flags;
+ u32 ioapic_address;
+};
+
+enum class InterruptType {
+ INT = 0,
+ NMI = 1,
+ SMI = 2,
+ ExtINT = 3,
+};
+
+struct [[gnu::packed]] IOInterruptAssignmentEntry {
+ EntryHeader h;
+ u8 interrupt_type;
+ u8 polarity;
+ u8 trigger_mode;
+ u8 source_bus_id;
+ u8 source_bus_irq;
+ u8 destination_ioapic_id;
+ u8 destination_ioapic_intin_pin;
+};
+
+struct [[gnu::packed]] LocalInterruptAssignmentEntry {
+ EntryHeader h;
+ u8 interrupt_type;
+ u8 polarity;
+ u8 trigger_mode;
+ u8 source_bus_id;
+ u8 source_bus_irq;
+ u8 destination_lapic_id;
+ u8 destination_lapic_lintin_pin;
+};
+
+enum class SystemAddressType {
+ IO = 0,
+ Memory = 1,
+ Prefetch = 2,
+};
+
+struct [[gnu::packed]] SystemAddressSpaceMappingEntry {
+ ExtEntryHeader h;
+ u8 bus_id;
+ u8 address_type;
+ u64 address_base;
+ u64 length;
+};
+
+struct [[gnu::packed]] BusHierarchyDescriptorEntry {
+ ExtEntryHeader h;
+ u8 bus_id;
+ u8 bus_info;
+ u8 parent_bus;
+ u8 reserved[3];
+};
+
+struct [[gnu::packed]] CompatibilityBusAddressSpaceModifierEntry {
+ ExtEntryHeader h;
+ u8 bus_id;
+ u8 address_modifier;
+ u32 predefined_range_list;
+};
+
+}
+
+class PCIInterruptOverrideMetadata;
+
+class MultiProcessorParser final {
+public:
+ static OwnPtr<MultiProcessorParser> autodetect();
+
+ Vector<PCIInterruptOverrideMetadata> get_pci_interrupt_redirections();
+
+private:
+ explicit MultiProcessorParser(PhysicalAddress floating_pointer);
+
+ void parse_configuration_table();
+ void parse_floating_pointer_data();
+
+ Vector<u8> get_pci_bus_ids() const;
+
+ static Optional<PhysicalAddress> find_floating_pointer();
+
+ PhysicalAddress m_floating_pointer;
+ PhysicalAddress m_configuration_table;
+ Vector<MultiProcessor::IOInterruptAssignmentEntry> m_io_interrupt_assignment_entries;
+ Vector<MultiProcessor::BusEntry> m_bus_entries;
+};
+}
diff --git a/Kernel/Firmware/ACPI/Parser.cpp b/Kernel/Firmware/ACPI/Parser.cpp
new file mode 100644
index 0000000000..62f29801ad
--- /dev/null
+++ b/Kernel/Firmware/ACPI/Parser.cpp
@@ -0,0 +1,440 @@
+/*
+ * Copyright (c) 2020-2021, Liav A. <liavalb@hotmail.co.il>
+ * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/Format.h>
+#include <AK/StringView.h>
+#include <Kernel/Arch/x86/InterruptDisabler.h>
+#include <Kernel/Bus/PCI/API.h>
+#include <Kernel/Debug.h>
+#include <Kernel/Firmware/ACPI/Parser.h>
+#include <Kernel/Firmware/BIOS.h>
+#include <Kernel/IO.h>
+#include <Kernel/Memory/TypedMapping.h>
+#include <Kernel/Sections.h>
+#include <Kernel/StdLib.h>
+
+namespace Kernel::ACPI {
+
+static Parser* s_acpi_parser;
+
+Parser* Parser::the()
+{
+ return s_acpi_parser;
+}
+
+void Parser::must_initialize(PhysicalAddress rsdp, PhysicalAddress fadt, u8 irq_number)
+{
+ VERIFY(!s_acpi_parser);
+ s_acpi_parser = new (nothrow) Parser(rsdp, fadt, irq_number);
+ VERIFY(s_acpi_parser);
+}
+
+UNMAP_AFTER_INIT NonnullRefPtr<ACPISysFSComponent> ACPISysFSComponent::create(String name, PhysicalAddress paddr, size_t table_size)
+{
+ return adopt_ref(*new (nothrow) ACPISysFSComponent(name, paddr, table_size));
+}
+
+KResultOr<size_t> ACPISysFSComponent::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, OpenFileDescription*) const
+{
+ auto blob = TRY(try_to_generate_buffer());
+
+ if ((size_t)offset >= blob->size())
+ return KSuccess;
+
+ ssize_t nread = min(static_cast<off_t>(blob->size() - offset), static_cast<off_t>(count));
+ TRY(buffer.write(blob->data() + offset, nread));
+ return nread;
+}
+
+KResultOr<NonnullOwnPtr<KBuffer>> ACPISysFSComponent::try_to_generate_buffer() const
+{
+ auto acpi_blob = Memory::map_typed<u8>((m_paddr), m_length);
+ return KBuffer::try_create_with_bytes(Span<u8> { acpi_blob.ptr(), m_length });
+}
+
+UNMAP_AFTER_INIT ACPISysFSComponent::ACPISysFSComponent(String name, PhysicalAddress paddr, size_t table_size)
+ : SysFSComponent(name)
+ , m_paddr(paddr)
+ , m_length(table_size)
+{
+}
+
+UNMAP_AFTER_INIT void ACPISysFSDirectory::initialize()
+{
+ auto acpi_directory = adopt_ref(*new (nothrow) ACPISysFSDirectory());
+ SysFSComponentRegistry::the().register_new_component(acpi_directory);
+}
+
+UNMAP_AFTER_INIT ACPISysFSDirectory::ACPISysFSDirectory()
+ : SysFSDirectory("acpi", SysFSComponentRegistry::the().root_directory())
+{
+ NonnullRefPtrVector<SysFSComponent> components;
+ size_t ssdt_count = 0;
+ ACPI::Parser::the()->enumerate_static_tables([&](const StringView& signature, PhysicalAddress p_table, size_t length) {
+ if (signature == "SSDT") {
+ components.append(ACPISysFSComponent::create(String::formatted("{:4s}{}", signature.characters_without_null_termination(), ssdt_count), p_table, length));
+ ssdt_count++;
+ return;
+ }
+ components.append(ACPISysFSComponent::create(signature, p_table, length));
+ });
+ m_components = components;
+
+ auto rsdp = Memory::map_typed<Structures::RSDPDescriptor20>(ACPI::Parser::the()->rsdp());
+ m_components.append(ACPISysFSComponent::create("RSDP", ACPI::Parser::the()->rsdp(), rsdp->base.revision == 0 ? sizeof(Structures::RSDPDescriptor) : rsdp->length));
+
+ auto main_system_description_table = Memory::map_typed<Structures::SDTHeader>(ACPI::Parser::the()->main_system_description_table());
+ if (ACPI::Parser::the()->is_xsdt_supported()) {
+ m_components.append(ACPISysFSComponent::create("XSDT", ACPI::Parser::the()->main_system_description_table(), main_system_description_table->length));
+ } else {
+ m_components.append(ACPISysFSComponent::create("RSDT", ACPI::Parser::the()->main_system_description_table(), main_system_description_table->length));
+ }
+}
+
+void Parser::enumerate_static_tables(Function<void(const StringView&, PhysicalAddress, size_t)> callback)
+{
+ for (auto& p_table : m_sdt_pointers) {
+ auto table = Memory::map_typed<Structures::SDTHeader>(p_table);
+ callback({ table->sig, 4 }, p_table, table->length);
+ }
+}
+
+static bool match_table_signature(PhysicalAddress table_header, const StringView& signature);
+static Optional<PhysicalAddress> search_table_in_xsdt(PhysicalAddress xsdt, const StringView& signature);
+static Optional<PhysicalAddress> search_table_in_rsdt(PhysicalAddress rsdt, const StringView& signature);
+static bool validate_table(const Structures::SDTHeader&, size_t length);
+
+UNMAP_AFTER_INIT void Parser::locate_static_data()
+{
+ locate_main_system_description_table();
+ initialize_main_system_description_table();
+ process_fadt_data();
+}
+
+UNMAP_AFTER_INIT Optional<PhysicalAddress> Parser::find_table(const StringView& signature)
+{
+ dbgln_if(ACPI_DEBUG, "ACPI: Calling Find Table method!");
+ for (auto p_sdt : m_sdt_pointers) {
+ auto sdt = Memory::map_typed<Structures::SDTHeader>(p_sdt);
+ dbgln_if(ACPI_DEBUG, "ACPI: Examining Table @ {}", p_sdt);
+ if (!strncmp(sdt->sig, signature.characters_without_null_termination(), 4)) {
+ dbgln_if(ACPI_DEBUG, "ACPI: Found Table @ {}", p_sdt);
+ return p_sdt;
+ }
+ }
+ return {};
+}
+
+bool Parser::handle_irq(const RegisterState&)
+{
+ TODO();
+}
+
+UNMAP_AFTER_INIT void Parser::enable_aml_parsing()
+{
+ // FIXME: When enabled, do other things to "parse AML".
+ m_can_process_bytecode = true;
+}
+
+UNMAP_AFTER_INIT void Parser::process_fadt_data()
+{
+ dmesgln("ACPI: Initializing Fixed ACPI data");
+
+ VERIFY(!m_fadt.is_null());
+ dbgln_if(ACPI_DEBUG, "ACPI: FADT @ {}", m_fadt);
+
+ auto sdt = Memory::map_typed<Structures::FADT>(m_fadt);
+ dmesgln("ACPI: Fixed ACPI data, Revision {}, length: {} bytes", (size_t)sdt->h.revision, (size_t)sdt->h.length);
+ dmesgln("ACPI: DSDT {}", PhysicalAddress(sdt->dsdt_ptr));
+ m_x86_specific_flags.cmos_rtc_not_present = (sdt->ia_pc_boot_arch_flags & (u8)FADTFlags::IA_PC_Flags::CMOS_RTC_Not_Present);
+
+ // FIXME: QEMU doesn't report that we have an i8042 controller in these flags, even if it should (when FADT revision is 3),
+ // Later on, we need to make sure that we enumerate the ACPI namespace (AML encoded), instead of just using this value.
+ m_x86_specific_flags.keyboard_8042 = (sdt->h.revision <= 3) || (sdt->ia_pc_boot_arch_flags & (u8)FADTFlags::IA_PC_Flags::PS2_8042);
+
+ m_x86_specific_flags.legacy_devices = (sdt->ia_pc_boot_arch_flags & (u8)FADTFlags::IA_PC_Flags::Legacy_Devices);
+ m_x86_specific_flags.msi_not_supported = (sdt->ia_pc_boot_arch_flags & (u8)FADTFlags::IA_PC_Flags::MSI_Not_Supported);
+ m_x86_specific_flags.vga_not_present = (sdt->ia_pc_boot_arch_flags & (u8)FADTFlags::IA_PC_Flags::VGA_Not_Present);
+
+ m_hardware_flags.cpu_software_sleep = (sdt->flags & (u32)FADTFlags::FeatureFlags::CPU_SW_SLP);
+ m_hardware_flags.docking_capability = (sdt->flags & (u32)FADTFlags::FeatureFlags::DCK_CAP);
+ m_hardware_flags.fix_rtc = (sdt->flags & (u32)FADTFlags::FeatureFlags::FIX_RTC);
+ m_hardware_flags.force_apic_cluster_model = (sdt->flags & (u32)FADTFlags::FeatureFlags::FORCE_APIC_CLUSTER_MODEL);
+ m_hardware_flags.force_apic_physical_destination_mode = (sdt->flags & (u32)FADTFlags::FeatureFlags::FORCE_APIC_PHYSICAL_DESTINATION_MODE);
+ m_hardware_flags.hardware_reduced_acpi = (sdt->flags & (u32)FADTFlags::FeatureFlags::HW_REDUCED_ACPI);
+ m_hardware_flags.headless = (sdt->flags & (u32)FADTFlags::FeatureFlags::HEADLESS);
+ m_hardware_flags.low_power_s0_idle_capable = (sdt->flags & (u32)FADTFlags::FeatureFlags::LOW_POWER_S0_IDLE_CAPABLE);
+ m_hardware_flags.multiprocessor_c2 = (sdt->flags & (u32)FADTFlags::FeatureFlags::P_LVL2_UP);
+ m_hardware_flags.pci_express_wake = (sdt->flags & (u32)FADTFlags::FeatureFlags::PCI_EXP_WAK);
+ m_hardware_flags.power_button = (sdt->flags & (u32)FADTFlags::FeatureFlags::PWR_BUTTON);
+ m_hardware_flags.processor_c1 = (sdt->flags & (u32)FADTFlags::FeatureFlags::PROC_C1);
+ m_hardware_flags.remote_power_on_capable = (sdt->flags & (u32)FADTFlags::FeatureFlags::REMOTE_POWER_ON_CAPABLE);
+ m_hardware_flags.reset_register_supported = (sdt->flags & (u32)FADTFlags::FeatureFlags::RESET_REG_SUPPORTED);
+ m_hardware_flags.rtc_s4 = (sdt->flags & (u32)FADTFlags::FeatureFlags::RTC_s4);
+ m_hardware_flags.s4_rtc_status_valid = (sdt->flags & (u32)FADTFlags::FeatureFlags::S4_RTC_STS_VALID);
+ m_hardware_flags.sealed_case = (sdt->flags & (u32)FADTFlags::FeatureFlags::SEALED_CASE);
+ m_hardware_flags.sleep_button = (sdt->flags & (u32)FADTFlags::FeatureFlags::SLP_BUTTON);
+ m_hardware_flags.timer_value_extension = (sdt->flags & (u32)FADTFlags::FeatureFlags::TMR_VAL_EXT);
+ m_hardware_flags.use_platform_clock = (sdt->flags & (u32)FADTFlags::FeatureFlags::USE_PLATFORM_CLOCK);
+ m_hardware_flags.wbinvd = (sdt->flags & (u32)FADTFlags::FeatureFlags::WBINVD);
+ m_hardware_flags.wbinvd_flush = (sdt->flags & (u32)FADTFlags::FeatureFlags::WBINVD_FLUSH);
+}
+
+bool Parser::can_reboot()
+{
+ auto fadt = Memory::map_typed<Structures::FADT>(m_fadt);
+ if (fadt->h.revision < 2)
+ return false;
+ return m_hardware_flags.reset_register_supported;
+}
+
+void Parser::access_generic_address(const Structures::GenericAddressStructure& structure, u32 value)
+{
+ switch ((GenericAddressStructure::AddressSpace)structure.address_space) {
+ case GenericAddressStructure::AddressSpace::SystemIO: {
+ IOAddress address(structure.address);
+ dbgln("ACPI: Sending value {:x} to {}", value, address);
+ switch (structure.access_size) {
+ case (u8)GenericAddressStructure::AccessSize::QWord: {
+ dbgln("Trying to send QWord to IO port");
+ VERIFY_NOT_REACHED();
+ break;
+ }
+ case (u8)GenericAddressStructure::AccessSize::Undefined: {
+ dbgln("ACPI Warning: Unknown access size {}", structure.access_size);
+ VERIFY(structure.bit_width != (u8)GenericAddressStructure::BitWidth::QWord);
+ VERIFY(structure.bit_width != (u8)GenericAddressStructure::BitWidth::Undefined);
+ dbgln("ACPI: Bit Width - {} bits", structure.bit_width);
+ address.out(value, structure.bit_width);
+ break;
+ }
+ default:
+ address.out(value, (8 << (structure.access_size - 1)));
+ break;
+ }
+ return;
+ }
+ case GenericAddressStructure::AddressSpace::SystemMemory: {
+ dbgln("ACPI: Sending value {:x} to {}", value, PhysicalAddress(structure.address));
+ switch ((GenericAddressStructure::AccessSize)structure.access_size) {
+ case GenericAddressStructure::AccessSize::Byte:
+ *Memory::map_typed<u8>(PhysicalAddress(structure.address)) = value;
+ break;
+ case GenericAddressStructure::AccessSize::Word:
+ *Memory::map_typed<u16>(PhysicalAddress(structure.address)) = value;
+ break;
+ case GenericAddressStructure::AccessSize::DWord:
+ *Memory::map_typed<u32>(PhysicalAddress(structure.address)) = value;
+ break;
+ case GenericAddressStructure::AccessSize::QWord: {
+ *Memory::map_typed<u64>(PhysicalAddress(structure.address)) = value;
+ break;
+ }
+ default:
+ VERIFY_NOT_REACHED();
+ }
+ return;
+ }
+ case GenericAddressStructure::AddressSpace::PCIConfigurationSpace: {
+ // According to https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#address-space-format,
+ // PCI addresses must be confined to devices on Segment group 0, bus 0.
+ auto pci_address = PCI::Address(0, 0, ((structure.address >> 24) & 0xFF), ((structure.address >> 16) & 0xFF));
+ dbgln("ACPI: Sending value {:x} to {}", value, pci_address);
+ u32 offset_in_pci_address = structure.address & 0xFFFF;
+ if (structure.access_size == (u8)GenericAddressStructure::AccessSize::QWord) {
+ dbgln("Trying to send QWord to PCI configuration space");
+ VERIFY_NOT_REACHED();
+ }
+ VERIFY(structure.access_size != (u8)GenericAddressStructure::AccessSize::Undefined);
+ PCI::raw_access(pci_address, offset_in_pci_address, (1 << (structure.access_size - 1)), value);
+ return;
+ }
+ default:
+ VERIFY_NOT_REACHED();
+ }
+ VERIFY_NOT_REACHED();
+}
+
+bool Parser::validate_reset_register()
+{
+ // According to https://uefi.org/specs/ACPI/6.4/04_ACPI_Hardware_Specification/ACPI_Hardware_Specification.html#reset-register,
+ // the reset register can only be located in I/O bus, PCI bus or memory-mapped.
+ auto fadt = Memory::map_typed<Structures::FADT>(m_fadt);
+ return (fadt->reset_reg.address_space == (u8)GenericAddressStructure::AddressSpace::PCIConfigurationSpace || fadt->reset_reg.address_space == (u8)GenericAddressStructure::AddressSpace::SystemMemory || fadt->reset_reg.address_space == (u8)GenericAddressStructure::AddressSpace::SystemIO);
+}
+
+void Parser::try_acpi_reboot()
+{
+ InterruptDisabler disabler;
+ if (!can_reboot()) {
+ dmesgln("ACPI: Reboot not supported!");
+ return;
+ }
+ dbgln_if(ACPI_DEBUG, "ACPI: Rebooting, probing FADT ({})", m_fadt);
+
+ auto fadt = Memory::map_typed<Structures::FADT>(m_fadt);
+ VERIFY(validate_reset_register());
+ access_generic_address(fadt->reset_reg, fadt->reset_value);
+ Processor::halt();
+}
+
+void Parser::try_acpi_shutdown()
+{
+ dmesgln("ACPI: Shutdown is not supported with the current configuration, aborting!");
+}
+
+size_t Parser::get_table_size(PhysicalAddress table_header)
+{
+ InterruptDisabler disabler;
+ dbgln_if(ACPI_DEBUG, "ACPI: Checking SDT Length");
+ return Memory::map_typed<Structures::SDTHeader>(table_header)->length;
+}
+
+u8 Parser::get_table_revision(PhysicalAddress table_header)
+{
+ InterruptDisabler disabler;
+ dbgln_if(ACPI_DEBUG, "ACPI: Checking SDT Revision");
+ return Memory::map_typed<Structures::SDTHeader>(table_header)->revision;
+}
+
+UNMAP_AFTER_INIT void Parser::initialize_main_system_description_table()
+{
+ dbgln_if(ACPI_DEBUG, "ACPI: Checking Main SDT Length to choose the correct mapping size");
+ VERIFY(!m_main_system_description_table.is_null());
+ auto length = get_table_size(m_main_system_description_table);
+ auto revision = get_table_revision(m_main_system_description_table);
+
+ auto sdt = Memory::map_typed<Structures::SDTHeader>(m_main_system_description_table, length);
+
+ dmesgln("ACPI: Main Description Table valid? {}", validate_table(*sdt, length));
+
+ if (m_xsdt_supported) {
+ auto& xsdt = (const Structures::XSDT&)*sdt;
+ dmesgln("ACPI: Using XSDT, enumerating tables @ {}", m_main_system_description_table);
+ dmesgln("ACPI: XSDT revision {}, total length: {}", revision, length);
+ dbgln_if(ACPI_DEBUG, "ACPI: XSDT pointer @ {}", VirtualAddress { &xsdt });
+ for (u32 i = 0; i < ((length - sizeof(Structures::SDTHeader)) / sizeof(u64)); i++) {
+ dbgln_if(ACPI_DEBUG, "ACPI: Found new table [{0}], @ V{1:p} - P{1:p}", i, &xsdt.table_ptrs[i]);
+ m_sdt_pointers.append(PhysicalAddress(xsdt.table_ptrs[i]));
+ }
+ } else {
+ auto& rsdt = (const Structures::RSDT&)*sdt;
+ dmesgln("ACPI: Using RSDT, enumerating tables @ {}", m_main_system_description_table);
+ dmesgln("ACPI: RSDT revision {}, total length: {}", revision, length);
+ dbgln_if(ACPI_DEBUG, "ACPI: RSDT pointer @ V{}", &rsdt);
+ for (u32 i = 0; i < ((length - sizeof(Structures::SDTHeader)) / sizeof(u32)); i++) {
+ dbgln_if(ACPI_DEBUG, "ACPI: Found new table [{0}], @ V{1:p} - P{1:p}", i, &rsdt.table_ptrs[i]);
+ m_sdt_pointers.append(PhysicalAddress(rsdt.table_ptrs[i]));
+ }
+ }
+}
+
+UNMAP_AFTER_INIT void Parser::locate_main_system_description_table()
+{
+ auto rsdp = Memory::map_typed<Structures::RSDPDescriptor20>(m_rsdp);
+ if (rsdp->base.revision == 0) {
+ m_xsdt_supported = false;
+ } else if (rsdp->base.revision >= 2) {
+ if (rsdp->xsdt_ptr != (u64) nullptr) {
+ m_xsdt_supported = true;
+ } else {
+ m_xsdt_supported = false;
+ }
+ }
+ if (!m_xsdt_supported) {
+ m_main_system_description_table = PhysicalAddress(rsdp->base.rsdt_ptr);
+ } else {
+ m_main_system_description_table = PhysicalAddress(rsdp->xsdt_ptr);
+ }
+}
+
+UNMAP_AFTER_INIT Parser::Parser(PhysicalAddress rsdp, PhysicalAddress fadt, u8 irq_number)
+ : IRQHandler(irq_number)
+ , m_rsdp(rsdp)
+ , m_fadt(fadt)
+{
+ dmesgln("ACPI: Using RSDP @ {}", rsdp);
+ locate_static_data();
+}
+
+static bool validate_table(const Structures::SDTHeader& v_header, size_t length)
+{
+ u8 checksum = 0;
+ auto* sdt = (const u8*)&v_header;
+ for (size_t i = 0; i < length; i++)
+ checksum += sdt[i];
+ if (checksum == 0)
+ return true;
+ return false;
+}
+
+// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#finding-the-rsdp-on-ia-pc-systems
+UNMAP_AFTER_INIT Optional<PhysicalAddress> StaticParsing::find_rsdp()
+{
+ StringView signature("RSD PTR ");
+ auto rsdp = map_ebda().find_chunk_starting_with(signature, 16);
+ if (rsdp.has_value())
+ return rsdp;
+ return map_bios().find_chunk_starting_with(signature, 16);
+}
+
+UNMAP_AFTER_INIT Optional<PhysicalAddress> StaticParsing::find_table(PhysicalAddress rsdp_address, const StringView& signature)
+{
+ // FIXME: There's no validation of ACPI tables here. Use the checksum to validate the tables.
+ VERIFY(signature.length() == 4);
+
+ auto rsdp = Memory::map_typed<Structures::RSDPDescriptor20>(rsdp_address);
+
+ if (rsdp->base.revision == 0)
+ return search_table_in_rsdt(PhysicalAddress(rsdp->base.rsdt_ptr), signature);
+
+ if (rsdp->base.revision >= 2) {
+ if (rsdp->xsdt_ptr)
+ return search_table_in_xsdt(PhysicalAddress(rsdp->xsdt_ptr), signature);
+ return search_table_in_rsdt(PhysicalAddress(rsdp->base.rsdt_ptr), signature);
+ }
+ VERIFY_NOT_REACHED();
+}
+
+UNMAP_AFTER_INIT static Optional<PhysicalAddress> search_table_in_xsdt(PhysicalAddress xsdt_address, const StringView& signature)
+{
+ // FIXME: There's no validation of ACPI tables here. Use the checksum to validate the tables.
+ VERIFY(signature.length() == 4);
+
+ auto xsdt = Memory::map_typed<Structures::XSDT>(xsdt_address);
+
+ for (size_t i = 0; i < ((xsdt->h.length - sizeof(Structures::SDTHeader)) / sizeof(u64)); ++i) {
+ if (match_table_signature(PhysicalAddress((PhysicalPtr)xsdt->table_ptrs[i]), signature))
+ return PhysicalAddress((PhysicalPtr)xsdt->table_ptrs[i]);
+ }
+ return {};
+}
+
+static bool match_table_signature(PhysicalAddress table_header, const StringView& signature)
+{
+ // FIXME: There's no validation of ACPI tables here. Use the checksum to validate the tables.
+ VERIFY(signature.length() == 4);
+
+ auto table = Memory::map_typed<Structures::RSDT>(table_header);
+ return !strncmp(table->h.sig, signature.characters_without_null_termination(), 4);
+}
+
+UNMAP_AFTER_INIT static Optional<PhysicalAddress> search_table_in_rsdt(PhysicalAddress rsdt_address, const StringView& signature)
+{
+ // FIXME: There's no validation of ACPI tables here. Use the checksum to validate the tables.
+ VERIFY(signature.length() == 4);
+
+ auto rsdt = Memory::map_typed<Structures::RSDT>(rsdt_address);
+
+ for (u32 i = 0; i < ((rsdt->h.length - sizeof(Structures::SDTHeader)) / sizeof(u32)); i++) {
+ if (match_table_signature(PhysicalAddress((PhysicalPtr)rsdt->table_ptrs[i]), signature))
+ return PhysicalAddress((PhysicalPtr)rsdt->table_ptrs[i]);
+ }
+ return {};
+}
+
+}
diff --git a/Kernel/Firmware/ACPI/Parser.h b/Kernel/Firmware/ACPI/Parser.h
new file mode 100644
index 0000000000..f4fbfe8003
--- /dev/null
+++ b/Kernel/Firmware/ACPI/Parser.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2020-2021, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Types.h>
+#include <Kernel/CommandLine.h>
+#include <Kernel/FileSystem/SysFSComponent.h>
+#include <Kernel/Firmware/ACPI/Definitions.h>
+#include <Kernel/Firmware/ACPI/Initialize.h>
+#include <Kernel/Interrupts/IRQHandler.h>
+#include <Kernel/Memory/Region.h>
+#include <Kernel/PhysicalAddress.h>
+#include <Kernel/VirtualAddress.h>
+
+namespace Kernel::ACPI {
+
+class ACPISysFSDirectory : public SysFSDirectory {
+public:
+ static void initialize();
+
+private:
+ ACPISysFSDirectory();
+};
+
+class ACPISysFSComponent : public SysFSComponent {
+public:
+ static NonnullRefPtr<ACPISysFSComponent> create(String name, PhysicalAddress, size_t table_size);
+
+ virtual KResultOr<size_t> read_bytes(off_t, size_t, UserOrKernelBuffer&, OpenFileDescription*) const override;
+
+protected:
+ KResultOr<NonnullOwnPtr<KBuffer>> try_to_generate_buffer() const;
+ ACPISysFSComponent(String name, PhysicalAddress, size_t table_size);
+
+ PhysicalAddress m_paddr;
+ size_t m_length;
+};
+
+class Parser final : public IRQHandler {
+public:
+ static Parser* the();
+
+ static void must_initialize(PhysicalAddress rsdp, PhysicalAddress fadt, u8 irq_number);
+
+ virtual StringView purpose() const override { return "ACPI Parser"; }
+ virtual bool handle_irq(const RegisterState&) override;
+
+ Optional<PhysicalAddress> find_table(const StringView& signature);
+
+ void try_acpi_reboot();
+ bool can_reboot();
+ void try_acpi_shutdown();
+ bool can_shutdown() { return false; }
+
+ void enable_aml_parsing();
+
+ PhysicalAddress rsdp() const { return m_rsdp; }
+ PhysicalAddress main_system_description_table() const { return m_main_system_description_table; }
+ bool is_xsdt_supported() const { return m_xsdt_supported; }
+
+ void enumerate_static_tables(Function<void(const StringView&, PhysicalAddress, size_t)>);
+
+ virtual bool have_8042() const
+ {
+ return m_x86_specific_flags.keyboard_8042;
+ }
+
+ const FADTFlags::HardwareFeatures& hardware_features() const { return m_hardware_flags; }
+ const FADTFlags::x86_Specific_Flags& x86_specific_flags() const { return m_x86_specific_flags; }
+
+ ~Parser() {};
+
+private:
+ Parser(PhysicalAddress rsdp, PhysicalAddress fadt, u8 irq_number);
+
+ void locate_static_data();
+ void locate_main_system_description_table();
+ void initialize_main_system_description_table();
+ size_t get_table_size(PhysicalAddress);
+ u8 get_table_revision(PhysicalAddress);
+ void process_fadt_data();
+
+ bool validate_reset_register();
+ void access_generic_address(const Structures::GenericAddressStructure&, u32 value);
+
+ PhysicalAddress m_rsdp;
+ PhysicalAddress m_main_system_description_table;
+
+ Vector<PhysicalAddress> m_sdt_pointers;
+ PhysicalAddress m_fadt;
+
+ bool m_xsdt_supported { false };
+ bool m_can_process_bytecode { false };
+ FADTFlags::HardwareFeatures m_hardware_flags;
+ FADTFlags::x86_Specific_Flags m_x86_specific_flags;
+};
+
+}