ladybird/Kernel/ACPI/ACPIStaticParser.cpp
Liav A 1e1a6a57ed Kernel: Introduce the ACPI subsystem
ACPI subsystem includes 3 types of parsers that are created during
runtime, each one capable of parsing ACPI tables at different level.

ACPIParser is the most basic parser which is essentialy a parser that
can't parse anything useful, due to a user request to disable ACPI
support in a kernel boot parameter.

ACPIStaticParser is a derived class from ACPIParser, which is able to
parse only static data (e.g. FADT, HPET, MCFG and other tables), thus
making it not able to parse AML (ACPI Machine Language) nor to support
handling of hardware events and power management. This type of parser
can be created with a kernel boot parameter.

ACPIDynamicParser is a derived class from ACPIStaticParser, which
includes all the capabilities of the latter, but *should* implement an
AML interpretation, (by building the ACPI AML namespace) and handling
power & hardware events. Currently the methods to support AML
interpretation are not implemented.
This type of parser is created automatically during runtime if the user
didn't specify a boot parameter related to ACPI initialization.

Also, adding strncmp function definition in StdLib.h, to be able to use
it in ACPIStaticParser class.
2020-01-02 00:50:09 +01:00

372 lines
14 KiB
C++

#include <Kernel/ACPI/ACPIStaticParser.h>
#include <Kernel/IO.h>
#include <Kernel/StdLib.h>
#include <Kernel/VM/MemoryManager.h>
void ACPIStaticParser::initialize(ACPI_RAW::RSDPDescriptor20& rsdp)
{
if (!ACPIParser::is_initialized()) {
new ACPIStaticParser(rsdp);
}
}
void ACPIStaticParser::initialize_without_rsdp()
{
if (!ACPIParser::is_initialized()) {
new ACPIStaticParser();
}
}
bool ACPIStaticParser::is_initialized()
{
return ACPIParser::is_initialized();
}
void ACPIStaticParser::locate_static_data()
{
locate_main_system_description_table();
initialize_main_system_description_table();
init_fadt();
locate_all_aml_tables();
}
ACPI_RAW::SDTHeader* ACPIStaticParser::find_table(const char* sig)
{
#ifdef ACPI_DEBUG
dbgprintf("ACPI: Calling Find Table method!\n");
#endif
for (auto* physical_sdt_ptr : m_main_sdt->get_sdt_pointers()) {
auto region = MM.allocate_kernel_region((PAGE_SIZE * 2), "ACPI Static Parser Tables Finding", Region::Access::Read);
mmap_region(*region, PhysicalAddress((u32)physical_sdt_ptr));
ACPI_RAW::SDTHeader* sdt = (ACPI_RAW::SDTHeader*)(region->vaddr().get() + ((u32)physical_sdt_ptr & (~PAGE_MASK)));
#ifdef ACPI_DEBUG
dbgprintf("ACPI: Examining Table @ P 0x%x\n", physical_sdt_ptr);
#endif
if (!strncmp(sdt->sig, sig, 4)) {
#ifdef ACPI_DEBUG
dbgprintf("ACPI: Found Table @ P 0x%x\n", physical_sdt_ptr);
#endif
return physical_sdt_ptr;
}
}
return nullptr;
}
ACPI_RAW::SDTHeader& ACPIStaticParser::find_fadt()
{
kprintf("ACPI: Searching for the Fixed ACPI Data Table\n");
for (auto* physical_sdt_ptr : m_main_sdt->get_sdt_pointers()) {
auto region = MM.allocate_kernel_region((PAGE_SIZE * 2), "ACPI Static Parser AML Tables Finding", Region::Access::Read);
mmap_region(*region, PhysicalAddress((u32)physical_sdt_ptr & PAGE_MASK));
ACPI_RAW::SDTHeader* sdt = (ACPI_RAW::SDTHeader*)(region->vaddr().get() + ((u32)physical_sdt_ptr & (~PAGE_MASK)));
#ifdef ACPI_DEBUG
dbgprintf("ACPI: Examining Table @ P 0x%x\n", physical_sdt_ptr);
#endif
if (!strncmp(sdt->sig, "FACP", 4)) {
kprintf("ACPI: Found FADT Table @ P 0x%x, registering\n", physical_sdt_ptr);
return *physical_sdt_ptr;
}
}
ASSERT_NOT_REACHED(); // If we reached this point, there's something very incorrect with the system!
}
void ACPIStaticParser::init_fadt()
{
kprintf("ACPI: Initializing Fixed ACPI data\n");
ACPI_RAW::SDTHeader& physical_fadt = find_fadt();
auto checkup_region = MM.allocate_kernel_region((PAGE_SIZE * 2), "ACPI Static Parser", Region::Access::Read);
#ifdef ACPI_DEBUG
dbgprintf("ACPI: Checking FADT Length to choose the correct mapping size\n");
#endif
mmap_region(*checkup_region, PhysicalAddress((u32)(&physical_fadt) & PAGE_MASK));
ACPI_RAW::SDTHeader* sdt = (ACPI_RAW::SDTHeader*)(checkup_region->vaddr().get() + ((u32)(&physical_fadt) & (~PAGE_MASK)));
#ifdef ACPI_DEBUG
dbgprintf("ACPI: FADT @ V 0x%x, P 0x%x\n", sdt, &physical_fadt);
#endif
u32 length = sdt->length;
kprintf("ACPI: Fixed ACPI data, Revision %u\n", sdt->revision);
auto fadt_region = MM.allocate_kernel_region(PAGE_ROUND_UP(length) + PAGE_SIZE, "ACPI Static Parser", Region::Access::Read);
mmap_region(*fadt_region, PhysicalAddress((u32)(&physical_fadt) & PAGE_MASK));
m_fadt = OwnPtr<ACPI::FixedACPIData>(new ACPI::FixedACPIData(*((ACPI_RAW::FADT*)(fadt_region->vaddr().get() + ((u32)(&physical_fadt) & (~PAGE_MASK))))));
#ifdef ACPI_DEBUG
dbgprintf("ACPI: Finished to initialize Fixed ACPI data\n");
#endif
}
void ACPIStaticParser::do_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
#ifdef ACPI_DEBUG
dbgprintf("ACPI: Rebooting, Probing FADT (P @ 0x%x)\n", m_fadt.ptr());
#endif
if (m_fadt->m_revision >= 2) {
kprintf("ACPI: Reboot, Sending value 0%x to Port 0x%x\n", m_fadt->m_reset_value, m_fadt->m_reset_reg.address);
IO::out8(m_fadt->m_reset_reg.address, m_fadt->m_reset_value);
} else {
kprintf("ACPI: Reboot, Not supported!\n");
}
ASSERT_NOT_REACHED(); /// If rebooting didn't work, halt.
}
void ACPIStaticParser::do_acpi_shutdown()
{
kprintf("ACPI: Shutdown is not supported with the current configuration, Abort!\n");
ASSERT_NOT_REACHED();
}
void ACPIStaticParser::initialize_main_system_description_table()
{
auto checkup_region = MM.allocate_kernel_region((PAGE_SIZE * 2), "ACPI Static Parser Copying Method", Region::Access::Read);
#ifdef ACPI_DEBUG
dbgprintf("ACPI: Checking Main SDT Length to choose the correct mapping size");
#endif
mmap_region(*checkup_region, PhysicalAddress((u32)m_main_system_description_table & PAGE_MASK));
auto* sdt = (ACPI_RAW::SDTHeader*)(checkup_region->vaddr().get() + ((u32)m_main_system_description_table & (~PAGE_MASK)));
u32 length = sdt->length;
u8 revision = sdt->revision;
auto main_sdt_region = MM.allocate_kernel_region(PAGE_ROUND_UP(length) + PAGE_SIZE, "ACPI Static Parser Copying Method", Region::Access::Read);
mmap_region(*main_sdt_region, PhysicalAddress((u32)m_main_system_description_table & PAGE_MASK));
Vector<ACPI_RAW::SDTHeader*> sdt_pointers;
if (m_xsdt_supported) {
ACPI_RAW::XSDT* xsdt = (ACPI_RAW::XSDT*)(main_sdt_region->vaddr().get() + ((u32)m_main_system_description_table & (~PAGE_MASK)));
kprintf("ACPI: Using XSDT, Enumerating tables @ P 0x%x\n", m_main_system_description_table);
kprintf("ACPI: XSDT Revision %d, Total length - %u\n", revision, length);
for (u32 i = 0; i < ((xsdt->h.length - sizeof(ACPI_RAW::SDTHeader)) / sizeof(u64)); i++) {
#ifdef ACPI_DEBUG
dbgprintf("ACPI: Found new table, @ P 0x%x\n", xsdt->table_ptrs[i]);
#endif
sdt_pointers.append((ACPI_RAW::SDTHeader*)xsdt->table_ptrs[i]);
}
} else {
ACPI_RAW::RSDT* rsdt = (ACPI_RAW::RSDT*)(main_sdt_region->vaddr().get() + ((u32)m_main_system_description_table & (~PAGE_MASK)));
kprintf("ACPI: Using RSDT, Enumerating tables @ P 0x%x\n", m_main_system_description_table);
kprintf("ACPI: RSDT Revision %d, Total length - %u\n", revision, length);
for (u32 i = 0; i < ((rsdt->h.length - sizeof(ACPI_RAW::SDTHeader)) / sizeof(u32)); i++) {
#ifdef ACPI_DEBUG
dbgprintf("ACPI: Found new table, @ P 0x%x\n", rsdt->table_ptrs[i]);
#endif
sdt_pointers.append((ACPI_RAW::SDTHeader*)rsdt->table_ptrs[i]);
}
}
m_main_sdt = OwnPtr<ACPI::MainSystemDescriptionTable>(new ACPI::MainSystemDescriptionTable(move(sdt_pointers)));
}
void ACPIStaticParser::locate_main_system_description_table()
{
if (m_rsdp->base.revision == 0) {
m_xsdt_supported = false;
} else if (m_rsdp->base.revision >= 2) {
if (m_rsdp->xsdt_ptr != (u64) nullptr) {
m_xsdt_supported = true;
} else {
m_xsdt_supported = false;
}
}
if (!m_xsdt_supported) {
m_main_system_description_table = (ACPI_RAW::SDTHeader*)m_rsdp->base.rsdt_ptr;
} else {
m_main_system_description_table = (ACPI_RAW::SDTHeader*)m_rsdp->xsdt_ptr;
}
}
void ACPIStaticParser::locate_all_aml_tables()
{
// Note: According to the ACPI spec, DSDT pointer may be found in the FADT table.
// All other continuation of the DSDT can be found as pointers in the RSDT/XSDT.
kprintf("ACPI: Searching for AML Tables\n");
m_aml_tables_ptrs.append(m_fadt->get_dsdt());
for (auto* sdt_ptr : m_main_sdt->get_sdt_pointers()) {
auto region = MM.allocate_kernel_region((PAGE_SIZE * 2), "ACPI Static Parser AML Tables Finding", Region::Access::Read);
mmap_region(*region, PhysicalAddress((u32)sdt_ptr));
auto* sdt = (ACPI_RAW::SDTHeader*)(region->vaddr().get() + ((u32)sdt_ptr & (~PAGE_MASK)));
#ifdef ACPI_DEBUG
dbgprintf("ACPI: Examining Table @ P 0x%x\n", sdt_ptr);
#endif
if (!strncmp(sdt->sig, "SSDT", 4)) {
kprintf("ACPI: Found AML Table @ P 0x%x, registering\n", sdt_ptr);
m_aml_tables_ptrs.append(sdt);
}
}
}
ACPIStaticParser::ACPIStaticParser()
: ACPIParser(true)
, m_rsdp(nullptr)
, m_main_sdt(nullptr)
, m_fadt(nullptr)
{
m_rsdp = search_rsdp();
if (m_rsdp != nullptr) {
kprintf("ACPI: Using RSDP @ P 0x%x\n", m_rsdp);
m_operable = true;
locate_static_data();
} else {
m_operable = false;
kprintf("ACPI: Disabled, due to RSDP being absent\n");
}
}
ACPI_RAW::RSDPDescriptor20* ACPIStaticParser::search_rsdp()
{
auto region = MM.allocate_kernel_region(PAGE_SIZE, "ACPI Static Parser RSDP Finding", Region::Access::Read);
mmap_region(*region, PhysicalAddress(0));
u16 ebda_seg = (u16) * ((uint16_t*)((region->vaddr().get() & PAGE_MASK) + 0x40e));
kprintf("ACPI: Probing EBDA, Segment 0x%x\n", ebda_seg);
// FIXME: Ensure that we always have identity mapping (identity paging) here! Don't rely on existing mapping...
for (char* rsdp_str = (char*)(PhysicalAddress(ebda_seg << 4).as_ptr()); rsdp_str < (char*)((ebda_seg << 4) + 1024); rsdp_str += 16) {
#ifdef ACPI_DEBUG
dbgprintf("ACPI: Looking for RSDP in EBDA @ Px%x\n", rsdp_str);
#endif
if (!strncmp("RSD PTR ", rsdp_str, strlen("RSD PTR ")))
return (ACPI_RAW::RSDPDescriptor20*)rsdp_str;
}
// FIXME: Ensure that we always have identity mapping (identity paging) here! Don't rely on existing mapping...
for (char* rsdp_str = (char*)(PhysicalAddress(0xE0000).as_ptr()); rsdp_str < (char*)0xFFFFF; rsdp_str += 16) {
#ifdef ACPI_DEBUG
dbgprintf("ACPI: Looking for RSDP in EBDA @ Px%x\n", rsdp_str);
#endif
if (!strncmp("RSD PTR ", rsdp_str, strlen("RSD PTR ")))
return (ACPI_RAW::RSDPDescriptor20*)rsdp_str;
}
return nullptr;
}
void ACPIStaticParser::mmap(VirtualAddress vaddr, PhysicalAddress paddr, u32 length)
{
unsigned i = 0;
while (length >= PAGE_SIZE) {
MM.map_for_kernel(VirtualAddress(vaddr.offset(i * PAGE_SIZE).get()), PhysicalAddress(paddr.offset(i * PAGE_SIZE).get()));
#ifdef ACPI_DEBUG
dbgprintf("ACPI: map - V 0x%x -> P 0x%x\n", vaddr.offset(i * PAGE_SIZE).get(), paddr.offset(i * PAGE_SIZE).get());
#endif
length -= PAGE_SIZE;
i++;
}
if (length > 0) {
MM.map_for_kernel(vaddr.offset(i * PAGE_SIZE), paddr.offset(i * PAGE_SIZE), true);
}
#ifdef ACPI_DEBUG
dbgprintf("ACPI: Finished mapping\n");
#endif
}
void ACPIStaticParser::mmap_region(Region& region, PhysicalAddress paddr)
{
#ifdef ACPI_DEBUG
dbgprintf("ACPI: Mapping region, size - %u\n", region.size());
#endif
mmap(region.vaddr(), paddr, region.size());
}
ACPIStaticParser::ACPIStaticParser(ACPI_RAW::RSDPDescriptor20& rsdp)
: ACPIParser(true)
, m_rsdp(&rsdp)
, m_main_sdt(nullptr)
, m_fadt(nullptr)
{
kprintf("ACPI: Using RSDP @ Px%x\n", &rsdp);
m_operable = true;
locate_static_data();
}
ACPI::MainSystemDescriptionTable::MainSystemDescriptionTable(Vector<ACPI_RAW::SDTHeader*>&& sdt_pointers)
{
for (auto* sdt_ptr : sdt_pointers) {
#ifdef ACPI_DEBUG
dbgprintf("ACPI: Register new table in Main SDT, @ P 0x%x\n", sdt_ptr);
#endif
m_sdt_pointers.append(sdt_ptr);
}
}
Vector<ACPI_RAW::SDTHeader*>& ACPI::MainSystemDescriptionTable::get_sdt_pointers()
{
return m_sdt_pointers;
}
ACPI::FixedACPIData::FixedACPIData(ACPI_RAW::FADT& fadt)
{
m_dsdt_ptr = fadt.dsdt_ptr;
#ifdef ACPI_DEBUG
dbgprintf("ACPI: DSDT pointer @ P 0x%x\n", m_dsdt_ptr);
#endif
m_revision = fadt.h.revision;
m_x_dsdt_ptr = fadt.x_dsdt;
m_preferred_pm_profile = fadt.preferred_pm_profile;
m_sci_int = fadt.sci_int;
m_smi_cmd = fadt.smi_cmd;
m_acpi_enable_value = fadt.acpi_enable_value;
m_acpi_disable_value = fadt.acpi_disable_value;
m_s4bios_req = fadt.s4bios_req;
m_pstate_cnt = fadt.pstate_cnt;
m_PM1a_EVT_BLK = fadt.PM1a_EVT_BLK;
m_PM1b_EVT_BLK = fadt.PM1b_EVT_BLK;
m_PM1a_CNT_BLK = fadt.PM1a_CNT_BLK;
m_PM1b_CNT_BLK = fadt.PM1b_CNT_BLK;
m_PM2_CNT_BLK = fadt.PM2_CNT_BLK;
m_PM_TMR_BLK = fadt.PM_TMR_BLK;
m_GPE0_BLK = fadt.GPE0_BLK;
m_GPE1_BLK = fadt.GPE1_BLK;
m_PM1_EVT_LEN = fadt.PM1_EVT_LEN;
m_PM1_CNT_LEN = fadt.PM1_CNT_LEN;
m_PM2_CNT_LEN = fadt.PM2_CNT_LEN;
m_PM_TMR_LEN = fadt.PM_TMR_LEN;
m_GPE0_BLK_LEN = fadt.GPE0_BLK_LEN;
m_GPE1_BLK_LEN = fadt.GPE1_BLK_LEN;
m_GPE1_BASE = fadt.GPE1_BASE;
m_cst_cnt = fadt.cst_cnt;
m_P_LVL2_LAT = fadt.P_LVL2_LAT;
m_P_LVL3_LAT = fadt.P_LVL3_LAT;
m_flush_size = fadt.flush_size;
m_flush_stride = fadt.flush_stride;
m_duty_offset = fadt.duty_offset;
m_duty_width = fadt.duty_width;
m_day_alrm = fadt.day_alrm;
m_mon_alrm = fadt.mon_alrm;
m_century = fadt.century;
m_ia_pc_boot_arch_flags = fadt.ia_pc_boot_arch_flags;
m_flags = fadt.flags;
m_reset_reg = fadt.reset_reg;
#ifdef ACPI_DEBUG
dbgprintf("ACPI: Reset Register @ IO 0x%x\n", m_reset_reg.address);
dbgprintf("ACPI: Reset Register Address space %x\n", fadt.reset_reg.address_space);
#endif
m_reset_value = fadt.reset_value;
#ifdef ACPI_DEBUG
dbgprintf("ACPI: Reset Register value @ P 0x%x\n", m_reset_value);
#endif
m_x_pm1a_evt_blk = fadt.x_pm1a_evt_blk;
m_x_pm1b_evt_blk = fadt.x_pm1b_evt_blk;
m_x_pm1a_cnt_blk = fadt.x_pm1a_cnt_blk;
m_x_pm1b_cnt_blk = fadt.x_pm1b_cnt_blk;
m_x_pm2_cnt_blk = fadt.x_pm2_cnt_blk;
m_x_pm_tmr_blk = fadt.x_pm_tmr_blk;
m_x_gpe0_blk = fadt.x_gpe0_blk;
m_x_gpe1_blk = fadt.x_gpe1_blk;
m_sleep_control = fadt.sleep_control;
m_sleep_status = fadt.sleep_status;
m_hypervisor_vendor_identity = fadt.hypervisor_vendor_identity;
}
ACPI_RAW::SDTHeader* ACPI::FixedACPIData::get_dsdt()
{
if (m_x_dsdt_ptr != (u32) nullptr)
return (ACPI_RAW::SDTHeader*)m_x_dsdt_ptr;
else {
ASSERT((ACPI_RAW::SDTHeader*)m_dsdt_ptr != nullptr);
return (ACPI_RAW::SDTHeader*)m_dsdt_ptr;
}
}