summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLiav A <liavalb@gmail.com>2020-03-08 16:50:46 +0200
committerAndreas Kling <kling@serenityos.org>2020-03-09 10:53:13 +0100
commit0f45a1b5e74f22d0cad4db54f5d00cb2e2e8f328 (patch)
tree7219ddbf3db60b38590f6eb8561cb2f39e197882
parent8639ee26406158ebf3c37e85729db2c4883d414b (diff)
downloadserenity-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.cpp110
-rw-r--r--Kernel/ACPI/ACPIStaticParser.h3
-rw-r--r--Kernel/ACPI/Definitions.h46
-rw-r--r--Kernel/PCI/Access.cpp19
-rw-r--r--Kernel/PCI/Access.h8
-rw-r--r--Kernel/PCI/Definitions.h1
-rw-r--r--Kernel/PCI/IOAccess.h8
-rw-r--r--Kernel/PCI/MMIOAccess.h7
-rw-r--r--Kernel/Process.cpp6
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);