diff options
author | Liav A <liavalb@gmail.com> | 2020-03-08 16:50:46 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-03-09 10:53:13 +0100 |
commit | 0f45a1b5e74f22d0cad4db54f5d00cb2e2e8f328 (patch) | |
tree | 7219ddbf3db60b38590f6eb8561cb2f39e197882 | |
parent | 8639ee26406158ebf3c37e85729db2c4883d414b (diff) | |
download | serenity-0f45a1b5e74f22d0cad4db54f5d00cb2e2e8f328.zip |
Kernel: Allow to reboot in ACPI via PCI or MMIO access
Also, we determine if ACPI reboot is supported by checking the FADT
flags' field.
-rw-r--r-- | Kernel/ACPI/ACPIStaticParser.cpp | 110 | ||||
-rw-r--r-- | Kernel/ACPI/ACPIStaticParser.h | 3 | ||||
-rw-r--r-- | Kernel/ACPI/Definitions.h | 46 | ||||
-rw-r--r-- | Kernel/PCI/Access.cpp | 19 | ||||
-rw-r--r-- | Kernel/PCI/Access.h | 8 | ||||
-rw-r--r-- | Kernel/PCI/Definitions.h | 1 | ||||
-rw-r--r-- | Kernel/PCI/IOAccess.h | 8 | ||||
-rw-r--r-- | Kernel/PCI/MMIOAccess.h | 7 | ||||
-rw-r--r-- | Kernel/Process.cpp | 6 |
9 files changed, 183 insertions, 25 deletions
diff --git a/Kernel/ACPI/ACPIStaticParser.cpp b/Kernel/ACPI/ACPIStaticParser.cpp index 8e6006c6ad..c39bf56fb6 100644 --- a/Kernel/ACPI/ACPIStaticParser.cpp +++ b/Kernel/ACPI/ACPIStaticParser.cpp @@ -25,6 +25,7 @@ */ #include <Kernel/ACPI/ACPIStaticParser.h> +#include <Kernel/PCI/Access.h> #include <Kernel/VM/MemoryManager.h> #include <LibBareMetal/IO.h> #include <LibBareMetal/StdLib.h> @@ -104,26 +105,118 @@ namespace ACPI { bool StaticParser::can_reboot() { - // FIXME: Determine if we need to do MMIO/PCI/IO access to reboot, according to ACPI spec 6.2, Section 4.8.3.6 auto region = MM.allocate_kernel_region(m_fadt.page_base(), (PAGE_SIZE * 2), "ACPI Static Parser", Region::Access::Read); auto* fadt = (const Structures::FADT*)region->vaddr().offset(m_fadt.offset_in_page()).as_ptr(); - return fadt->h.revision >= 2; + if (fadt->h.revision < 2) + return false; + return (fadt->flags & (u32)FADTFeatureFlags::RESET_REG_SUPPORTED) != 0; + } + + void StaticParser::access_generic_address(const Structures::GenericAddressStructure& structure, u32 value) + { + switch (structure.address_space) { + case (u8)GenericAddressStructure::AddressSpace::SystemIO: { + dbg() << "ACPI: Sending value 0x" << String::format("%x", value) << " to " << IOAddress(structure.address); + switch (structure.access_size) { + case (u8)GenericAddressStructure::AccessSize::Byte: { + IO::out8(structure.address, value); + break; + } + case (u8)GenericAddressStructure::AccessSize::Word: { + IO::out16(structure.address, value); + break; + } + case (u8)GenericAddressStructure::AccessSize::DWord: { + IO::out32(structure.address, value); + break; + } + case (u8)GenericAddressStructure::AccessSize::QWord: { + dbg() << "Trying to send QWord to IO port"; + ASSERT_NOT_REACHED(); + break; + } + default: + // FIXME: Determine if for reset register we can actually determine the right IO operation. + dbg() << "ACPI Warning: Unknown access size " << structure.access_size; + IO::out8(structure.address, value); + break; + } + return; + } + case (u8)GenericAddressStructure::AddressSpace::SystemMemory: { + auto p_reg = PhysicalAddress(structure.address); + auto p_region = MM.allocate_kernel_region(p_reg.page_base(), (PAGE_SIZE * 2), "ACPI Static Parser", Region::Access::Read); + dbg() << "ACPI: Sending value 0x" << String::format("%x", value) << " to " << p_reg; + switch (structure.access_size) { + case (u8)GenericAddressStructure::AccessSize::Byte: { + auto* reg = (volatile u8*)p_region->vaddr().offset(p_reg.offset_in_page()).as_ptr(); + (*reg) = value; + break; + } + case (u8)GenericAddressStructure::AccessSize::Word: { + auto* reg = (volatile u16*)p_region->vaddr().offset(p_reg.offset_in_page()).as_ptr(); + (*reg) = value; + break; + } + case (u8)GenericAddressStructure::AccessSize::DWord: { + auto* reg = (volatile u32*)p_region->vaddr().offset(p_reg.offset_in_page()).as_ptr(); + (*reg) = value; + break; + } + case (u8)GenericAddressStructure::AccessSize::QWord: { + auto* reg = (volatile u64*)p_region->vaddr().offset(p_reg.offset_in_page()).as_ptr(); + (*reg) = value; + break; + } + default: + ASSERT_NOT_REACHED(); + } + return; + } + case (u8)GenericAddressStructure::AddressSpace::PCIConfigurationSpace: { + // According to the ACPI specification 6.2, page 168, 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)); + dbg() << "ACPI: Sending value 0x" << String::format("%x", value) << " to " << pci_address; + u32 offset_in_pci_address = structure.address & 0xFFFF; + if (structure.access_size == (u8)GenericAddressStructure::AccessSize::QWord) { + dbg() << "Trying to send QWord to PCI configuration space"; + ASSERT_NOT_REACHED(); + } + ASSERT(structure.access_size != (u8)GenericAddressStructure::AccessSize::Undefined); + PCI::raw_access(pci_address, offset_in_pci_address, (1 << (structure.access_size - 1)), value); + return; + } + default: + ASSERT_NOT_REACHED(); + } + ASSERT_NOT_REACHED(); + } + + bool StaticParser::validate_reset_register() + { + // According to the ACPI spec 6.2, page 152, The reset register can only be located in I/O bus, PCI bus or memory-mapped. + auto region = MM.allocate_kernel_region(m_fadt.page_base(), (PAGE_SIZE * 2), "ACPI Static Parser", Region::Access::Read); + auto* fadt = (const Structures::FADT*)region->vaddr().offset(m_fadt.offset_in_page()).as_ptr(); + 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 StaticParser::try_acpi_reboot() { - // FIXME: Determine if we need to do MMIO/PCI/IO access to reboot, according to ACPI spec 6.2, Section 4.8.3.6 + InterruptDisabler disabler; + if (!can_reboot()) { + klog() << "ACPI: Reboot, Not supported!"; + return; + } #ifdef ACPI_DEBUG dbg() << "ACPI: Rebooting, Probing FADT (" << m_fadt << ")"; #endif auto region = MM.allocate_kernel_region(m_fadt.page_base(), (PAGE_SIZE * 2), "ACPI Static Parser", Region::Access::Read); auto* fadt = (const Structures::FADT*)region->vaddr().offset(m_fadt.offset_in_page()).as_ptr(); - if (fadt->h.revision >= 2) { - klog() << "ACPI: Reboot, Sending value 0x" << String::format("%x", fadt->reset_value) << " to Port 0x" << String::format("%x", fadt->reset_reg.address); - IO::out8(fadt->reset_reg.address, fadt->reset_value); - } - klog() << "ACPI: Reboot, Not supported!"; + ASSERT(validate_reset_register()); + access_generic_address(fadt->reset_reg, fadt->reset_value); + for (;;) + ; } void StaticParser::try_acpi_shutdown() @@ -352,6 +445,5 @@ namespace ACPI { } return {}; } - } } diff --git a/Kernel/ACPI/ACPIStaticParser.h b/Kernel/ACPI/ACPIStaticParser.h index 0886c1aeb3..0d5dcca078 100644 --- a/Kernel/ACPI/ACPIStaticParser.h +++ b/Kernel/ACPI/ACPIStaticParser.h @@ -58,6 +58,9 @@ namespace ACPI { void init_fadt(); void init_facs(); + bool validate_reset_register(); + void access_generic_address(const Structures::GenericAddressStructure&, u32 value); + PhysicalAddress m_rsdp; PhysicalAddress m_main_system_description_table; diff --git a/Kernel/ACPI/Definitions.h b/Kernel/ACPI/Definitions.h index a34ecd7057..a6ab700a62 100644 --- a/Kernel/ACPI/Definitions.h +++ b/Kernel/ACPI/Definitions.h @@ -34,6 +34,51 @@ namespace Kernel { namespace ACPI { + + enum class FADTFeatureFlags : 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 + }; + + 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 + }; + } + namespace Structures { struct [[gnu::packed]] RSDPDescriptor { @@ -246,7 +291,6 @@ namespace ACPI { u64 reserved; PCI_MMIO_Descriptor descriptors[]; }; - } class StaticParser; diff --git a/Kernel/PCI/Access.cpp b/Kernel/PCI/Access.cpp index 652b331571..c9e8cf2aa6 100644 --- a/Kernel/PCI/Access.cpp +++ b/Kernel/PCI/Access.cpp @@ -107,11 +107,30 @@ void PCI::Access::disable_bus_mastering(Address address) } namespace PCI { + void enumerate_all(Function<void(Address, ID)> callback) { PCI::Access::the().enumerate_all(callback); } + void raw_access(Address address, u32 field, size_t access_size, u32 value) + { + ASSERT(access_size != 0); + if (access_size == 1) { + PCI::Access::the().write8_field(address, field, value); + return; + } + if (access_size == 2) { + PCI::Access::the().write16_field(address, field, value); + return; + } + if (access_size == 4) { + PCI::Access::the().write32_field(address, field, value); + return; + } + ASSERT_NOT_REACHED(); + } + ID get_id(Address address) { return PCI::Access::the().get_id(address); diff --git a/Kernel/PCI/Access.h b/Kernel/PCI/Access.h index 6979ef2a54..05cb50e49b 100644 --- a/Kernel/PCI/Access.h +++ b/Kernel/PCI/Access.h @@ -85,16 +85,16 @@ public: virtual uint8_t get_segment_end_bus(u32 segment) = 0; virtual String get_access_type() = 0; + virtual void write8_field(Address address, u32 field, u8 value) = 0; + virtual void write16_field(Address address, u32 field, u16 value) = 0; + virtual void write32_field(Address address, u32 field, u32 value) = 0; + protected: Access(); virtual u8 read8_field(Address address, u32 field) = 0; virtual u16 read16_field(Address address, u32 field) = 0; virtual u32 read32_field(Address address, u32 field) = 0; - - virtual void write8_field(Address address, u32 field, u8 value) = 0; - virtual void write16_field(Address address, u32 field, u16 value) = 0; - virtual void write32_field(Address address, u32 field, u32 value) = 0; }; } diff --git a/Kernel/PCI/Definitions.h b/Kernel/PCI/Definitions.h index b80696e81d..a943d8a80c 100644 --- a/Kernel/PCI/Definitions.h +++ b/Kernel/PCI/Definitions.h @@ -174,6 +174,7 @@ namespace PCI { void enable_interrupt_line(Address); void disable_interrupt_line(Address); u8 get_interrupt_line(Address); + void raw_access(Address, u32, size_t, u32); u32 get_BAR0(Address); u32 get_BAR1(Address); u32 get_BAR2(Address); diff --git a/Kernel/PCI/IOAccess.h b/Kernel/PCI/IOAccess.h index 20e24d5257..7fc58e148b 100644 --- a/Kernel/PCI/IOAccess.h +++ b/Kernel/PCI/IOAccess.h @@ -37,6 +37,10 @@ public: virtual String get_access_type() override final { return "IO-Access"; }; virtual uint32_t get_segments_count() { return 1; }; + virtual void write8_field(Address address, u32, u8) override final; + virtual void write16_field(Address address, u32, u16) override final; + virtual void write32_field(Address address, u32, u32) override final; + protected: IOAccess(); @@ -44,13 +48,9 @@ private: virtual u8 read8_field(Address address, u32) override final; virtual u16 read16_field(Address address, u32) override final; virtual u32 read32_field(Address address, u32) override final; - virtual void write8_field(Address address, u32, u8) override final; - virtual void write16_field(Address address, u32, u16) override final; - virtual void write32_field(Address address, u32, u32) override final; virtual uint8_t get_segment_start_bus(u32) { return 0x0; }; virtual uint8_t get_segment_end_bus(u32) { return 0xFF; }; }; } - diff --git a/Kernel/PCI/MMIOAccess.h b/Kernel/PCI/MMIOAccess.h index 992c45cb6d..f03bb18885 100644 --- a/Kernel/PCI/MMIOAccess.h +++ b/Kernel/PCI/MMIOAccess.h @@ -45,6 +45,10 @@ public: virtual String get_access_type() override final { return "MMIO-Access"; }; virtual u32 get_segments_count(); + virtual void write8_field(Address address, u32, u8) override final; + virtual void write16_field(Address address, u32, u16) override final; + virtual void write32_field(Address address, u32, u32) override final; + protected: explicit MMIOAccess(PhysicalAddress mcfg); @@ -52,9 +56,6 @@ private: virtual u8 read8_field(Address address, u32) override final; virtual u16 read16_field(Address address, u32) override final; virtual u32 read32_field(Address address, u32) override final; - virtual void write8_field(Address address, u32, u8) override final; - virtual void write16_field(Address address, u32, u16) override final; - virtual void write32_field(Address address, u32, u32) override final; void map_device(Address address); virtual u8 get_segment_start_bus(u32); diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index b392793473..beaa98c4e7 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -4007,10 +4007,8 @@ int Process::sys$reboot() FS::lock_all(); dbg() << "syncing mounted filesystems..."; FS::sync(); - if (ACPI::Parser::the().can_reboot()) { - dbg() << "attempting reboot via ACPI"; - ACPI::Parser::the().try_acpi_reboot(); - } + dbg() << "attempting reboot via ACPI"; + ACPI::Parser::the().try_acpi_reboot(); dbg() << "attempting reboot via KB Controller..."; IO::out8(0x64, 0xFE); |