Skip to content

Commit

Permalink
Kernel: Allow to reboot in ACPI via PCI or MMIO access
Browse files Browse the repository at this point in the history
Also, we determine if ACPI reboot is supported by checking the FADT
flags' field.
  • Loading branch information
supercomputer7 authored and awesomekling committed Mar 9, 2020
1 parent 8639ee2 commit 0f45a1b
Show file tree
Hide file tree
Showing 9 changed files with 183 additions and 25 deletions.
110 changes: 101 additions & 9 deletions Kernel/ACPI/ACPIStaticParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -352,6 +445,5 @@ namespace ACPI {
}
return {};
}

}
}
3 changes: 3 additions & 0 deletions Kernel/ACPI/ACPIStaticParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
46 changes: 45 additions & 1 deletion Kernel/ACPI/Definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -246,7 +291,6 @@ namespace ACPI {
u64 reserved;
PCI_MMIO_Descriptor descriptors[];
};

}

class StaticParser;
Expand Down
19 changes: 19 additions & 0 deletions Kernel/PCI/Access.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 4 additions & 4 deletions Kernel/PCI/Access.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,16 +85,16 @@ class PCI::Access {
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 change: 1 addition & 0 deletions Kernel/PCI/Definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 4 additions & 4 deletions Kernel/PCI/IOAccess.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,20 @@ class PCI::IOAccess final : public PCI::Access {
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();

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

}

7 changes: 4 additions & 3 deletions Kernel/PCI/MMIOAccess.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,17 @@ class PCI::MMIOAccess final : public PCI::Access {
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);

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);
Expand Down
6 changes: 2 additions & 4 deletions Kernel/Process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down

0 comments on commit 0f45a1b

Please sign in to comment.