Pārlūkot izejas kodu

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.
Liav A 5 gadi atpakaļ
vecāks
revīzija
0f45a1b5e7

+ 101 - 9
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 {};
     }
-
 }
 }

+ 3 - 0
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;
 

+ 45 - 1
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;

+ 19 - 0
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);

+ 4 - 4
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;
 };
 
 }

+ 1 - 0
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);

+ 4 - 4
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; };
 };
 
 }
-

+ 4 - 3
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);

+ 2 - 4
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);