forked from Bananymous/banan-os
Kernel: Move ACPI to its own file
This commit is contained in:
parent
6d6bef1b04
commit
2f8c9746e3
|
@ -36,6 +36,7 @@ BUILDDIR=$(abspath build)
|
||||||
KERNEL_OBJS= \
|
KERNEL_OBJS= \
|
||||||
$(KERNEL_ARCH_OBJS) \
|
$(KERNEL_ARCH_OBJS) \
|
||||||
font/prefs.o \
|
font/prefs.o \
|
||||||
|
kernel/ACPI.o \
|
||||||
kernel/APIC.o \
|
kernel/APIC.o \
|
||||||
kernel/build_libc.o \
|
kernel/build_libc.o \
|
||||||
kernel/CPUID.o \
|
kernel/CPUID.o \
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Errors.h>
|
||||||
|
|
||||||
|
namespace Kernel
|
||||||
|
{
|
||||||
|
|
||||||
|
class ACPI
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct SDTHeader
|
||||||
|
{
|
||||||
|
uint8_t signature[4];
|
||||||
|
uint32_t length;
|
||||||
|
uint8_t revision;
|
||||||
|
uint8_t checksum;
|
||||||
|
uint8_t oemid[6];
|
||||||
|
uint64_t oem_table_id;
|
||||||
|
uint32_t oem_revision;
|
||||||
|
uint32_t creator_id;
|
||||||
|
uint32_t creator_revision;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
public:
|
||||||
|
static BAN::ErrorOr<void> initialize();
|
||||||
|
static ACPI& get();
|
||||||
|
|
||||||
|
BAN::ErrorOr<const SDTHeader*> get_header(const char[4]);
|
||||||
|
void unmap_header(const SDTHeader*);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ACPI() = default;
|
||||||
|
BAN::ErrorOr<void> initialize_impl();
|
||||||
|
|
||||||
|
const SDTHeader* get_header_from_index(size_t);
|
||||||
|
|
||||||
|
private:
|
||||||
|
uintptr_t m_header_table = 0;
|
||||||
|
uint32_t m_entry_size = 0;
|
||||||
|
uint32_t m_entry_count = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,172 @@
|
||||||
|
#include <BAN/ScopeGuard.h>
|
||||||
|
#include <BAN/StringView.h>
|
||||||
|
#include <kernel/ACPI.h>
|
||||||
|
#include <kernel/MMU.h>
|
||||||
|
|
||||||
|
#define RSPD_SIZE 20
|
||||||
|
#define RSPDv2_SIZE 36
|
||||||
|
|
||||||
|
namespace Kernel
|
||||||
|
{
|
||||||
|
|
||||||
|
struct RSDP
|
||||||
|
{
|
||||||
|
uint8_t signature[8];
|
||||||
|
uint8_t checksum;
|
||||||
|
uint8_t oemid[6];
|
||||||
|
uint8_t revision;
|
||||||
|
uint32_t rsdt_address;
|
||||||
|
|
||||||
|
// only in revision >= 2
|
||||||
|
uint32_t length;
|
||||||
|
uint64_t xsdt_address;
|
||||||
|
uint8_t extended_checksum;
|
||||||
|
uint8_t reserved[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RSDT : public ACPI::SDTHeader
|
||||||
|
{
|
||||||
|
uint32_t entries[];
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct XSDT : public ACPI::SDTHeader
|
||||||
|
{
|
||||||
|
uint64_t entries[];
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
static ACPI* s_instance = nullptr;
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> ACPI::initialize()
|
||||||
|
{
|
||||||
|
ASSERT(s_instance == nullptr);
|
||||||
|
s_instance = new ACPI;
|
||||||
|
if (s_instance == nullptr)
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
TRY(s_instance->initialize_impl());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ACPI& ACPI::get()
|
||||||
|
{
|
||||||
|
ASSERT(s_instance != nullptr);
|
||||||
|
return *s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_rsdp(uintptr_t rsdp_addr)
|
||||||
|
{
|
||||||
|
const RSDP* rsdp = (const RSDP*)rsdp_addr;
|
||||||
|
|
||||||
|
if (memcmp(rsdp->signature, "RSD PTR ", 8) != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
{
|
||||||
|
uint8_t checksum = 0;
|
||||||
|
for (uint32_t i = 0; i < RSPD_SIZE; i++)
|
||||||
|
checksum += ((const uint8_t*)rsdp)[i];
|
||||||
|
if (checksum != 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rsdp->revision == 2)
|
||||||
|
{
|
||||||
|
uint8_t checksum = 0;
|
||||||
|
for (uint32_t i = 0; i < RSPDv2_SIZE; i++)
|
||||||
|
checksum += ((const uint8_t*)rsdp)[i];
|
||||||
|
if (checksum != 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const RSDP* locate_rsdp()
|
||||||
|
{
|
||||||
|
// Look in main BIOS area below 1 MB
|
||||||
|
for (uintptr_t addr = 0x000E0000; addr < 0x000FFFFF; addr += 16)
|
||||||
|
if (is_rsdp(addr))
|
||||||
|
return (const RSDP*)addr;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_valid_std_header(const ACPI::SDTHeader* header)
|
||||||
|
{
|
||||||
|
uint8_t sum = 0;
|
||||||
|
for (uint32_t i = 0; i < header->length; i++)
|
||||||
|
sum += ((uint8_t*)header)[i];
|
||||||
|
return sum == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> ACPI::initialize_impl()
|
||||||
|
{
|
||||||
|
const RSDP* rsdp = locate_rsdp();
|
||||||
|
if (rsdp == nullptr)
|
||||||
|
return BAN::Error::from_c_string("Could not find RSDP");
|
||||||
|
|
||||||
|
if (rsdp->revision >= 2)
|
||||||
|
{
|
||||||
|
const XSDT* xsdt = (const XSDT*)rsdp->xsdt_address;
|
||||||
|
MMU::get().allocate_page((uintptr_t)xsdt, MMU::Flags::Present);
|
||||||
|
BAN::ScopeGuard _([xsdt] { MMU::get().unallocate_page((uintptr_t)xsdt); });
|
||||||
|
|
||||||
|
if (memcmp(xsdt->signature, "XSDT", 4) != 0)
|
||||||
|
return BAN::Error::from_c_string("XSDT has invalid signature");
|
||||||
|
if (!is_valid_std_header(xsdt))
|
||||||
|
return BAN::Error::from_c_string("XSDT has invalid checksum");
|
||||||
|
|
||||||
|
m_header_table = (uintptr_t)xsdt->entries;
|
||||||
|
m_entry_size = 8;
|
||||||
|
m_entry_count = (xsdt->length - sizeof(SDTHeader)) / 8;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const RSDT* rsdt = (const RSDT*)(uintptr_t)rsdp->rsdt_address;
|
||||||
|
MMU::get().allocate_page((uintptr_t)rsdt, MMU::Flags::Present);
|
||||||
|
BAN::ScopeGuard _([rsdt] { MMU::get().unallocate_page((uintptr_t)rsdt); });
|
||||||
|
|
||||||
|
if (memcmp(rsdt->signature, "RSDT", 4) != 0)
|
||||||
|
return BAN::Error::from_c_string("RSDT has invalid signature");
|
||||||
|
if (!is_valid_std_header(rsdt))
|
||||||
|
return BAN::Error::from_c_string("RSDT has invalid checksum");
|
||||||
|
|
||||||
|
m_header_table = (uintptr_t)rsdt->entries;
|
||||||
|
m_entry_size = 4;
|
||||||
|
m_entry_count = (rsdt->length - sizeof(SDTHeader)) / 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
MMU::get().allocate_range(m_header_table, m_entry_count * m_entry_size, MMU::Flags::Present);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<const ACPI::SDTHeader*> ACPI::get_header(const char signature[4])
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; i < m_entry_count; i++)
|
||||||
|
{
|
||||||
|
const SDTHeader* header = get_header_from_index(i);
|
||||||
|
MMU::get().allocate_range((uintptr_t)header, header->length, MMU::Flags::Present);
|
||||||
|
if (!is_valid_std_header(header))
|
||||||
|
{
|
||||||
|
unmap_header(header);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (memcmp(header->signature, signature, 4) == 0)
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
return BAN::Error::from_format("Could not find ACPI header '{}'", BAN::StringView(signature, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ACPI::unmap_header(const ACPI::SDTHeader* header)
|
||||||
|
{
|
||||||
|
MMU::get().unallocate_range((uintptr_t)header, header->length);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ACPI::SDTHeader* ACPI::get_header_from_index(size_t index)
|
||||||
|
{
|
||||||
|
ASSERT(index < m_entry_count);
|
||||||
|
ASSERT(m_entry_size == 4 || m_entry_size == 8);
|
||||||
|
|
||||||
|
uintptr_t header_address = (m_entry_size == 4) ? ((uint32_t*)m_header_table)[index] : ((uint64_t*)m_header_table)[index];
|
||||||
|
return (SDTHeader*)header_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
#include <BAN/ScopeGuard.h>
|
#include <BAN/ScopeGuard.h>
|
||||||
#include <kernel/Debug.h>
|
#include <kernel/Debug.h>
|
||||||
|
#include <kernel/ACPI.h>
|
||||||
#include <kernel/APIC.h>
|
#include <kernel/APIC.h>
|
||||||
#include <kernel/CPUID.h>
|
#include <kernel/CPUID.h>
|
||||||
#include <kernel/IDT.h>
|
#include <kernel/IDT.h>
|
||||||
|
@ -18,38 +19,8 @@
|
||||||
|
|
||||||
// https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html#multiple-apic-description-table-madt-format
|
// https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html#multiple-apic-description-table-madt-format
|
||||||
|
|
||||||
static constexpr uint32_t RSPD_SIZE = 20;
|
struct MADT : public Kernel::ACPI::SDTHeader
|
||||||
static constexpr uint32_t RSPDv2_SIZE = 36;
|
|
||||||
|
|
||||||
struct RSDP
|
|
||||||
{
|
{
|
||||||
char signature[8];
|
|
||||||
uint8_t checksum;
|
|
||||||
char OEMID[6];
|
|
||||||
uint8_t revision;
|
|
||||||
uint32_t rsdt_address;
|
|
||||||
uint32_t v2_length;
|
|
||||||
uint64_t v2_xsdt_address;
|
|
||||||
uint8_t v2_extended_checksum;
|
|
||||||
uint8_t v2_reserved[3];
|
|
||||||
} __attribute__ ((packed));
|
|
||||||
|
|
||||||
struct SDTHeader
|
|
||||||
{
|
|
||||||
char signature[4];
|
|
||||||
uint32_t length;
|
|
||||||
uint8_t revision;
|
|
||||||
uint8_t checksum;
|
|
||||||
char OEMID[6];
|
|
||||||
char OEM_table_id[8];
|
|
||||||
uint32_t OEM_revision;
|
|
||||||
uint32_t creator_id;
|
|
||||||
uint32_t creator_revision;
|
|
||||||
} __attribute__((packed));
|
|
||||||
|
|
||||||
struct MADT
|
|
||||||
{
|
|
||||||
SDTHeader header;
|
|
||||||
uint32_t local_apic;
|
uint32_t local_apic;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
@ -110,104 +81,6 @@ union RedirectionEntry
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool is_rsdp(uintptr_t rsdp_addr)
|
|
||||||
{
|
|
||||||
const RSDP* rsdp = (const RSDP*)rsdp_addr;
|
|
||||||
|
|
||||||
if (memcmp(rsdp->signature, "RSD PTR ", 8) != 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
{
|
|
||||||
uint8_t checksum = 0;
|
|
||||||
for (uint32_t i = 0; i < RSPD_SIZE; i++)
|
|
||||||
checksum += ((uint8_t*)rsdp)[i];
|
|
||||||
if (checksum != 0)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rsdp->revision == 2)
|
|
||||||
{
|
|
||||||
uint8_t checksum = 0;
|
|
||||||
for (uint32_t i = 0; i < RSPDv2_SIZE; i++)
|
|
||||||
checksum += ((uint8_t*)rsdp)[i];
|
|
||||||
if (checksum != 0)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uintptr_t locate_rsdp()
|
|
||||||
{
|
|
||||||
// Look in main BIOS area below 1 MB
|
|
||||||
for (uintptr_t addr = 0x000E0000; addr < 0x000FFFFF; addr += 16)
|
|
||||||
if (is_rsdp(addr))
|
|
||||||
return addr;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_valid_std_header(const SDTHeader* header)
|
|
||||||
{
|
|
||||||
uint8_t sum = 0;
|
|
||||||
for (uint32_t i = 0; i < header->length; i++)
|
|
||||||
sum += ((uint8_t*)header)[i];
|
|
||||||
return sum == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t locate_madt(uintptr_t rsdp_addr)
|
|
||||||
{
|
|
||||||
uintptr_t entry_address_base = 0;
|
|
||||||
ptrdiff_t entry_pointer_size = 0;
|
|
||||||
uint32_t entry_count = 0;
|
|
||||||
|
|
||||||
const RSDP* rsdp = (const RSDP*)rsdp_addr;
|
|
||||||
if (rsdp->revision == 2)
|
|
||||||
{
|
|
||||||
uintptr_t xsdt_addr = rsdp->v2_xsdt_address;
|
|
||||||
MMU::get().allocate_page(xsdt_addr, MMU::Flags::ReadWrite | MMU::Flags::Present);
|
|
||||||
entry_address_base = xsdt_addr + sizeof(SDTHeader);
|
|
||||||
entry_count = (((const SDTHeader*)xsdt_addr)->length - sizeof(SDTHeader)) / 8;
|
|
||||||
entry_pointer_size = 8;
|
|
||||||
MMU::get().unallocate_page(xsdt_addr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
uintptr_t rsdt_addr = rsdp->rsdt_address;
|
|
||||||
MMU::get().allocate_page(rsdt_addr, MMU::Flags::ReadWrite | MMU::Flags::Present);
|
|
||||||
entry_address_base = rsdt_addr + sizeof(SDTHeader);
|
|
||||||
entry_count = (((const SDTHeader*)rsdt_addr)->length - sizeof(SDTHeader)) / 4;
|
|
||||||
entry_pointer_size = 4;
|
|
||||||
MMU::get().unallocate_page(rsdt_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < entry_count; i++)
|
|
||||||
{
|
|
||||||
uintptr_t entry_addr_ptr = entry_address_base + i * entry_pointer_size;
|
|
||||||
MMU::get().allocate_page(entry_addr_ptr, MMU::Flags::ReadWrite | MMU::Flags::Present);
|
|
||||||
|
|
||||||
union dummy { uint32_t addr32; uint64_t addr64; } __attribute__((aligned(1), packed));
|
|
||||||
|
|
||||||
uintptr_t entry_addr;
|
|
||||||
if (entry_pointer_size == 4)
|
|
||||||
entry_addr = ((dummy*)entry_addr_ptr)->addr32;
|
|
||||||
else
|
|
||||||
entry_addr = ((dummy*)entry_addr_ptr)->addr64;
|
|
||||||
|
|
||||||
MMU::get().allocate_page(entry_addr, MMU::Flags::ReadWrite | MMU::Flags::Present);
|
|
||||||
|
|
||||||
BAN::ScopeGuard _([&]() {
|
|
||||||
MMU::get().unallocate_page(entry_addr);
|
|
||||||
MMU::get().unallocate_page(entry_addr_ptr);
|
|
||||||
});
|
|
||||||
|
|
||||||
const SDTHeader* entry = (const SDTHeader*)entry_addr;
|
|
||||||
if (memcmp(entry->signature, "APIC", 4) == 0 && is_valid_std_header(entry))
|
|
||||||
return entry_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
APIC* APIC::create()
|
APIC* APIC::create()
|
||||||
{
|
{
|
||||||
uint32_t ecx, edx;
|
uint32_t ecx, edx;
|
||||||
|
@ -218,23 +91,14 @@ APIC* APIC::create()
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
uintptr_t rsdp_addr = locate_rsdp();
|
auto header_or_error = Kernel::ACPI::get().get_header("APIC");
|
||||||
if (!rsdp_addr)
|
if (header_or_error.is_error())
|
||||||
{
|
{
|
||||||
dprintln("Could not locate RSDP");
|
dprintln("{}", header_or_error.error());
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
uintptr_t madt_addr = locate_madt(rsdp_addr);
|
const MADT* madt = (const MADT*)header_or_error.value();
|
||||||
if (!madt_addr)
|
|
||||||
{
|
|
||||||
dprintln("Could not find MADT in RSDP");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
MMU::get().allocate_page(madt_addr, MMU::Flags::ReadWrite | MMU::Flags::Present);
|
|
||||||
|
|
||||||
const MADT* madt = (const MADT*)madt_addr;
|
|
||||||
|
|
||||||
APIC* apic = new APIC;
|
APIC* apic = new APIC;
|
||||||
apic->m_local_apic = madt->local_apic;
|
apic->m_local_apic = madt->local_apic;
|
||||||
|
@ -242,7 +106,7 @@ APIC* APIC::create()
|
||||||
apic->m_irq_overrides[i] = i;
|
apic->m_irq_overrides[i] = i;
|
||||||
|
|
||||||
uintptr_t madt_entry_addr = (uintptr_t)madt + sizeof(MADT);
|
uintptr_t madt_entry_addr = (uintptr_t)madt + sizeof(MADT);
|
||||||
while (madt_entry_addr < (uintptr_t)madt + madt->header.length)
|
while (madt_entry_addr < (uintptr_t)madt + madt->length)
|
||||||
{
|
{
|
||||||
const MADTEntry* entry = (const MADTEntry*)madt_entry_addr;
|
const MADTEntry* entry = (const MADTEntry*)madt_entry_addr;
|
||||||
switch (entry->type)
|
switch (entry->type)
|
||||||
|
@ -274,7 +138,8 @@ APIC* APIC::create()
|
||||||
}
|
}
|
||||||
madt_entry_addr += entry->length;
|
madt_entry_addr += entry->length;
|
||||||
}
|
}
|
||||||
MMU::get().unallocate_page((uintptr_t)madt);
|
|
||||||
|
Kernel::ACPI::get().unmap_header(madt);
|
||||||
|
|
||||||
if (apic->m_local_apic == 0 || apic->m_io_apics.empty())
|
if (apic->m_local_apic == 0 || apic->m_io_apics.empty())
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include <kernel/ACPI.h>
|
||||||
#include <kernel/Arch.h>
|
#include <kernel/Arch.h>
|
||||||
#include <kernel/Debug.h>
|
#include <kernel/Debug.h>
|
||||||
#include <kernel/FS/VirtualFileSystem.h>
|
#include <kernel/FS/VirtualFileSystem.h>
|
||||||
|
@ -126,6 +127,9 @@ extern "C" void kernel_main()
|
||||||
TTY* tty1 = new TTY(terminal_driver);
|
TTY* tty1 = new TTY(terminal_driver);
|
||||||
ASSERT(tty1);
|
ASSERT(tty1);
|
||||||
|
|
||||||
|
MUST(ACPI::initialize());
|
||||||
|
dprintln("ACPI initialized");
|
||||||
|
|
||||||
InterruptController::initialize(cmdline.force_pic);
|
InterruptController::initialize(cmdline.force_pic);
|
||||||
dprintln("Interrupt controller initialized");
|
dprintln("Interrupt controller initialized");
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue