mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-24 13:35:12 +00:00
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.
This commit is contained in:
parent
8639ee2640
commit
0f45a1b5e7
Notes:
sideshowbarker
2024-07-19 08:49:08 +09:00
Author: https://github.com/supercomputer7 Commit: https://github.com/SerenityOS/serenity/commit/0f45a1b5e74 Pull-request: https://github.com/SerenityOS/serenity/pull/1385 Reviewed-by: https://github.com/awesomekling
9 changed files with 183 additions and 25 deletions
|
@ -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 {};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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; };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue