115 lines
3.1 KiB
C++
115 lines
3.1 KiB
C++
#pragma once
|
|
|
|
#include "util/number.h"
|
|
#include "memory/pointer.h"
|
|
|
|
namespace acpi {
|
|
|
|
constexpr uint32_t fold_signature(const char (&text)[5]) {
|
|
return static_cast<uint32_t>(static_cast<uint8_t>(text[0]))
|
|
| static_cast<uint32_t>(static_cast<uint8_t>(text[1])) << 8
|
|
| static_cast<uint32_t>(static_cast<uint8_t>(text[2])) << 16
|
|
| static_cast<uint32_t>(static_cast<uint8_t>(text[3])) << 24;
|
|
}
|
|
|
|
enum class signature : uint32_t {
|
|
fadt = fold_signature("FACP"),
|
|
madt = fold_signature("APIC"),
|
|
hpet = fold_signature("HPET"),
|
|
mcfg = fold_signature("MCFG"),
|
|
srat = fold_signature("SRAT"),
|
|
ssdt = fold_signature("SSDT"),
|
|
};
|
|
|
|
struct signature_text_t {
|
|
char text[5];
|
|
};
|
|
|
|
inline signature_text_t unfold_signature(uint32_t folded) {
|
|
signature_text_t result{};
|
|
memcpy(result.text, &folded, 4);
|
|
result.text[4] = '\0';
|
|
return result;
|
|
}
|
|
|
|
struct sdt_header_t {
|
|
uint32_t signature;
|
|
uint32_t length;
|
|
uint8_t revision;
|
|
uint8_t checksum;
|
|
char oem_id[6];
|
|
char oem_table_id[8];
|
|
uint32_t oem_revision;
|
|
uint32_t creator_id;
|
|
uint32_t creator_revision;
|
|
} __attribute__((packed));
|
|
static_assert(sizeof(sdt_header_t) == 36);
|
|
|
|
struct rsdp_t {
|
|
char signature[8];
|
|
uint8_t checksum;
|
|
char oem_id[6];
|
|
uint8_t revision;
|
|
uint32_t rsdt_address;
|
|
|
|
uint32_t length;
|
|
uint64_t xsdt_address;
|
|
uint8_t extended_checksum;
|
|
uint8_t reserved[3];
|
|
} __attribute__((packed));
|
|
static_assert(sizeof(rsdp_t) == 36);
|
|
|
|
struct madt_t;
|
|
|
|
struct Rsdp {
|
|
explicit Rsdp(paddr_t rsdp_physical) : physical(rsdp_physical) {}
|
|
|
|
template<typename Fn>
|
|
void for_each_table(Fn&& callback) {
|
|
auto root = root_table().access<sdt_header_t>();
|
|
auto entry_size = uses_xsdt() ? 8 : 4;
|
|
auto count = (root->length - sizeof(sdt_header_t)) / entry_size;
|
|
auto entries = reinterpret_cast<const uint8_t*>(root) + sizeof(sdt_header_t);
|
|
for (auto index = 0u; index < count; index++) {
|
|
uint64_t table_physical = 0;
|
|
memcpy(&table_physical, entries + index * entry_size, entry_size);
|
|
dispatch(paddr_t{table_physical}.access<sdt_header_t>(), callback);
|
|
}
|
|
}
|
|
|
|
private:
|
|
template<typename Fn>
|
|
static void dispatch(const sdt_header_t* header, Fn&& fn) {
|
|
#define ACPI_DISPATCH(signature_enum, TableType) \
|
|
case signature::signature_enum: \
|
|
if constexpr (requires { fn(static_cast<const TableType*>(nullptr)); }) { \
|
|
fn(reinterpret_cast<const TableType*>(header)); \
|
|
return; \
|
|
} \
|
|
break;
|
|
|
|
switch (static_cast<signature>(header->signature)) {
|
|
ACPI_DISPATCH(madt, madt_t)
|
|
default: break;
|
|
}
|
|
|
|
#undef ACPI_DISPATCH
|
|
|
|
if constexpr (requires { fn(static_cast<const sdt_header_t*>(nullptr)); }) {
|
|
fn(header);
|
|
}
|
|
}
|
|
|
|
bool uses_xsdt() {
|
|
return physical.access<rsdp_t>()->revision >= 2;
|
|
}
|
|
|
|
paddr_t root_table() {
|
|
auto rsdp = physical.access<rsdp_t>();
|
|
return uses_xsdt() ? paddr_t{rsdp->xsdt_address} : paddr_t{rsdp->rsdt_address};
|
|
}
|
|
|
|
paddr_t physical;
|
|
};
|
|
|
|
} // namespace acpi
|