summaryrefslogtreecommitdiff
path: root/Kernel/Firmware/ACPI
diff options
context:
space:
mode:
authorLiav A <liavalb@gmail.com>2021-09-11 10:39:47 +0300
committerAndreas Kling <kling@serenityos.org>2021-09-12 11:52:16 +0200
commit9132596b8e79a56a1ab0aa146bd837266ed61b09 (patch)
tree33ff453378549b12a0bbd43b5e7d56a7eca8f181 /Kernel/Firmware/ACPI
parenta9ec98028b7c50a7fd0308ff78cd86480e224d32 (diff)
downloadserenity-9132596b8e79a56a1ab0aa146bd837266ed61b09.zip
Kernel: Move ACPI and BIOS code into the new Firmware directory
This will somwhat help unify them also under the same SysFS directory in the commit. Also, it feels much more like this change reflects the reality that both ACPI and the BIOS are part of the firmware on x86 computers.
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;
+};
+
+}