diff options
Diffstat (limited to 'Kernel/Firmware/ACPI')
-rw-r--r-- | Kernel/Firmware/ACPI/Definitions.h | 334 | ||||
-rw-r--r-- | Kernel/Firmware/ACPI/Initialize.cpp | 41 | ||||
-rw-r--r-- | Kernel/Firmware/ACPI/Initialize.h | 14 | ||||
-rw-r--r-- | Kernel/Firmware/ACPI/MultiProcessorParser.cpp | 157 | ||||
-rw-r--r-- | Kernel/Firmware/ACPI/MultiProcessorParser.h | 170 | ||||
-rw-r--r-- | Kernel/Firmware/ACPI/Parser.cpp | 440 | ||||
-rw-r--r-- | Kernel/Firmware/ACPI/Parser.h | 102 |
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; +}; + +} |