mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-20 09:46:20 +00:00
hw/x86: add ACPI tables for resume
The ACPI table FACS and FADT are required to support ACPI suspend/resume. The commits add the lookup of the ACPI table in bootstrap and the general usage of the ACPI registers via the MMIO framework. Issue #4669
This commit is contained in:
parent
dc1996d289
commit
32b1aa605a
@ -45,7 +45,7 @@ class Bootstrap::Platform
|
||||
{
|
||||
Memory_region_array early_ram_regions { };
|
||||
Memory_region_array late_ram_regions { };
|
||||
Mmio_space const core_mmio;
|
||||
Mmio_space core_mmio;
|
||||
unsigned cpus { ::Board::NR_OF_CPUS };
|
||||
::Board::Boot_info info { };
|
||||
|
||||
|
@ -170,43 +170,37 @@ Bootstrap::Platform::Board::Board()
|
||||
uint64_t const table_addr = acpi_rsdp.xsdt ? acpi_rsdp.xsdt : acpi_rsdp.rsdt;
|
||||
|
||||
if (table_addr) {
|
||||
Hw::Acpi_generic * table = reinterpret_cast<Hw::Acpi_generic *>(table_addr);
|
||||
auto rsdt_xsdt_lambda = [&](auto paddr_table) {
|
||||
addr_t const table_virt_addr = paddr_table;
|
||||
Hw::Acpi_generic * table = reinterpret_cast<Hw::Acpi_generic *>(table_virt_addr);
|
||||
|
||||
if (!memcmp(table->signature, "FACP", 4)) {
|
||||
info.acpi_fadt = addr_t(table);
|
||||
|
||||
auto mem_aligned = paddr_table & _align_mask(12);
|
||||
auto mem_size = align_addr(paddr_table + table->size, 12) - mem_aligned;
|
||||
core_mmio.add({ mem_aligned, mem_size });
|
||||
}
|
||||
|
||||
if (memcmp(table->signature, "APIC", 4))
|
||||
return;
|
||||
|
||||
Hw::for_each_apic_struct(*table,[&](Hw::Apic_madt const *e){
|
||||
if (e->type == Hw::Apic_madt::LAPIC) {
|
||||
Hw::Apic_madt::Lapic lapic(e);
|
||||
|
||||
/* check if APIC is enabled in hardware */
|
||||
if (lapic.valid())
|
||||
cpus ++;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
auto table = reinterpret_cast<Hw::Acpi_generic *>(table_addr);
|
||||
if (!memcmp(table->signature, "RSDT", 4)) {
|
||||
Hw::for_each_rsdt_entry(*table, [&](uint32_t paddr_table) {
|
||||
addr_t const table_virt_addr = paddr_table;
|
||||
Hw::Acpi_generic * table = reinterpret_cast<Hw::Acpi_generic *>(table_virt_addr);
|
||||
|
||||
if (memcmp(table->signature, "APIC", 4))
|
||||
return;
|
||||
|
||||
Hw::for_each_apic_struct(*table,[&](Hw::Apic_madt const *e){
|
||||
if (e->type == Hw::Apic_madt::LAPIC) {
|
||||
Hw::Apic_madt::Lapic lapic(e);
|
||||
|
||||
/* check if APIC is enabled in hardware */
|
||||
if (lapic.valid())
|
||||
cpus ++;
|
||||
}
|
||||
});
|
||||
});
|
||||
Hw::for_each_rsdt_entry(*table, rsdt_xsdt_lambda);
|
||||
} else if (!memcmp(table->signature, "XSDT", 4)) {
|
||||
Hw::for_each_xsdt_entry(*table, [&](uint64_t paddr_table) {
|
||||
addr_t const table_virt_addr = paddr_table;
|
||||
Hw::Acpi_generic * table = reinterpret_cast<Hw::Acpi_generic *>(table_virt_addr);
|
||||
|
||||
if (memcmp(table->signature, "APIC", 4))
|
||||
return;
|
||||
|
||||
Hw::for_each_apic_struct(*table,[&](Hw::Apic_madt const *e){
|
||||
if (e->type == Hw::Apic_madt::LAPIC) {
|
||||
Hw::Apic_madt::Lapic lapic(e);
|
||||
|
||||
/* check if APIC is enabled in hardware */
|
||||
if (lapic.valid())
|
||||
cpus ++;
|
||||
}
|
||||
});
|
||||
});
|
||||
Hw::for_each_xsdt_entry(*table, rsdt_xsdt_lambda);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Genode Labs GmbH
|
||||
* Copyright (C) 2018-2022 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
@ -20,6 +20,8 @@ namespace Hw {
|
||||
|
||||
struct Acpi_generic;
|
||||
struct Apic_madt;
|
||||
struct Acpi_fadt;
|
||||
struct Acpi_facs;
|
||||
|
||||
template <typename FUNC>
|
||||
void for_each_rsdt_entry(Hw::Acpi_generic &, FUNC);
|
||||
@ -78,6 +80,292 @@ struct Hw::Apic_madt
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
/* ACPI spec 5.2.9 and ACPI GAS 5.2.3.2 */
|
||||
struct Hw::Acpi_fadt : Genode::Mmio
|
||||
{
|
||||
enum Addressspace { IO = 0x1 };
|
||||
|
||||
enum { FW_OFFSET_EXT = 0x84, SLEEP_CONTROL_REG = 236 };
|
||||
|
||||
struct Size : Register < 0x04, 32> { };
|
||||
struct Fw_ctrl : Register < 0x24, 32> { };
|
||||
struct Fw_ctrl_ext : Register <FW_OFFSET_EXT, 64> { };
|
||||
|
||||
struct Pm1a_cnt_blk : Register < 64, 32> {
|
||||
struct Slp_typ : Bitfield < 10, 3> { };
|
||||
struct Slp_ena : Bitfield < 13, 1> { };
|
||||
};
|
||||
struct Pm1b_cnt_blk : Register < 68, 32> {
|
||||
struct Slp_typ : Bitfield < 10, 3> { };
|
||||
struct Slp_ena : Bitfield < 13, 1> { };
|
||||
};
|
||||
|
||||
struct Gpe0_blk : Register < 80, 32> { };
|
||||
struct Gpe1_blk : Register < 84, 32> { };
|
||||
|
||||
struct Gpe0_blk_len : Register < 92, 8> { };
|
||||
struct Gpe1_blk_len : Register < 93, 8> { };
|
||||
|
||||
struct Pm1_cnt_len : Register < 89, 8> { };
|
||||
|
||||
struct Pm1a_cnt_blk_ext : Register < 172, 32> {
|
||||
struct Addressspace : Bitfield < 0, 8> { };
|
||||
struct Width : Bitfield < 8, 8> { };
|
||||
};
|
||||
struct Pm1a_cnt_blk_ext_addr : Register < 172 + 4, 64> { };
|
||||
|
||||
struct Pm1b_cnt_blk_ext : Register < 184, 32> {
|
||||
struct Addressspace : Bitfield < 0, 8> { };
|
||||
struct Width : Bitfield < 8, 8> { };
|
||||
};
|
||||
struct Pm1b_cnt_blk_ext_addr : Register < 184 + 4, 64> { };
|
||||
|
||||
struct Gpe0_blk_ext : Register < 220, 32> {
|
||||
struct Addressspace : Bitfield < 0, 8> { };
|
||||
struct Width : Bitfield < 8, 8> { };
|
||||
};
|
||||
struct Gpe0_blk_ext_addr : Register < 220 + 4, 64> { };
|
||||
|
||||
struct Gpe1_blk_ext : Register < 232, 32> {
|
||||
struct Addressspace : Bitfield < 0, 8> { };
|
||||
struct Width : Bitfield < 8, 8> { };
|
||||
};
|
||||
struct Gpe1_blk_ext_addr : Register < 232 + 4, 64> { };
|
||||
|
||||
/**
|
||||
* Read from I/O port
|
||||
*/
|
||||
Genode::uint8_t inb(Genode::uint16_t port)
|
||||
{
|
||||
Genode::uint8_t res;
|
||||
asm volatile ("inb %w1, %0" : "=a"(res) : "Nd"(port));
|
||||
return res;
|
||||
}
|
||||
|
||||
Genode::uint16_t inw(Genode::uint16_t port)
|
||||
{
|
||||
Genode::uint16_t res;
|
||||
asm volatile ("inw %w1, %0" : "=a"(res) : "Nd"(port));
|
||||
return res;
|
||||
}
|
||||
|
||||
Genode::uint32_t inl(Genode::uint16_t port)
|
||||
{
|
||||
Genode::uint32_t res;
|
||||
asm volatile ("inl %w1, %0" : "=a"(res) : "Nd"(port));
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to I/O port
|
||||
*/
|
||||
inline void outb(Genode::uint16_t port, Genode::uint8_t val)
|
||||
{
|
||||
asm volatile ("outb %0, %w1" : : "a"(val), "Nd"(port));
|
||||
}
|
||||
|
||||
inline void outw(Genode::uint16_t port, Genode::uint16_t val)
|
||||
{
|
||||
asm volatile ("outw %0, %w1" : : "a"(val), "Nd"(port));
|
||||
}
|
||||
|
||||
inline void outl(Genode::uint16_t port, Genode::uint32_t val)
|
||||
{
|
||||
asm volatile ("outl %0, %w1" : : "a"(val), "Nd"(port));
|
||||
}
|
||||
|
||||
Acpi_fadt(Acpi_generic const * a) : Mmio(Genode::addr_t(a)) { }
|
||||
|
||||
addr_t facs() const
|
||||
{
|
||||
addr_t facs { };
|
||||
|
||||
if (read<Size>() >= FW_OFFSET_EXT + 8)
|
||||
facs = read<Fw_ctrl_ext>();
|
||||
|
||||
if (!facs)
|
||||
facs = read<Fw_ctrl>();
|
||||
|
||||
return facs;
|
||||
}
|
||||
|
||||
void clear_gpe0_status()
|
||||
{
|
||||
auto constexpr half_register = true;
|
||||
|
||||
return _write<Gpe0_blk_len, Gpe0_blk, Gpe0_blk_ext::Width,
|
||||
Gpe0_blk_ext::Addressspace, Gpe0_blk_ext_addr>(~0ULL,
|
||||
half_register);
|
||||
}
|
||||
|
||||
void clear_gpe1_status()
|
||||
{
|
||||
auto constexpr half_register = true;
|
||||
|
||||
return _write<Gpe1_blk_len, Gpe1_blk, Gpe1_blk_ext::Width,
|
||||
Gpe1_blk_ext::Addressspace, Gpe1_blk_ext_addr>(~0ULL,
|
||||
half_register);
|
||||
}
|
||||
|
||||
unsigned read_cnt_blk()
|
||||
{
|
||||
auto const pm1_a = _read<Pm1_cnt_len, Pm1a_cnt_blk,
|
||||
Pm1a_cnt_blk_ext::Width,
|
||||
Pm1a_cnt_blk_ext::Addressspace,
|
||||
Pm1a_cnt_blk_ext_addr>();
|
||||
auto const pm1_b = _read<Pm1_cnt_len, Pm1b_cnt_blk,
|
||||
Pm1b_cnt_blk_ext::Width,
|
||||
Pm1b_cnt_blk_ext::Addressspace,
|
||||
Pm1b_cnt_blk_ext_addr>();
|
||||
return pm1_a | pm1_b;
|
||||
}
|
||||
|
||||
void write_cnt_blk(unsigned value_a, unsigned value_b)
|
||||
{
|
||||
_write<Pm1_cnt_len, Pm1a_cnt_blk, Pm1a_cnt_blk_ext::Width,
|
||||
Pm1a_cnt_blk_ext::Addressspace, Pm1a_cnt_blk_ext_addr>(value_a);
|
||||
_write<Pm1_cnt_len, Pm1b_cnt_blk, Pm1b_cnt_blk_ext::Width,
|
||||
Pm1b_cnt_blk_ext::Addressspace, Pm1b_cnt_blk_ext_addr>(value_b);
|
||||
}
|
||||
|
||||
void suspend(Genode::uint8_t typ_slp_a, Genode::uint8_t typ_slp_b)
|
||||
{
|
||||
auto cnt = read_cnt_blk();
|
||||
auto value_a = cnt, value_b = cnt;
|
||||
|
||||
Pm1a_cnt_blk::Slp_typ::set(value_a, typ_slp_a);
|
||||
Pm1a_cnt_blk::Slp_ena::set(value_a, 1);
|
||||
|
||||
Pm1b_cnt_blk::Slp_typ::set(value_b, typ_slp_b);
|
||||
Pm1b_cnt_blk::Slp_ena::set(value_b, 1);
|
||||
|
||||
write_cnt_blk(value_a, value_b);
|
||||
}
|
||||
|
||||
template <typename REG_LEN, typename BLK_ADDR, typename BLK_WIDTH_EXT,
|
||||
typename BLK_AS_EXT, typename BLK_ADDR_EXT, typename T>
|
||||
unsigned _access(bool hw_read, T value, bool half_register = false,
|
||||
bool half_offset = false)
|
||||
{
|
||||
unsigned long long raw_io_addr = 0ull;
|
||||
unsigned width_bits = 0u;
|
||||
|
||||
auto const register_len = read<REG_LEN>();
|
||||
auto const reg_size = read<Size>();
|
||||
|
||||
if (reg_size >= SLEEP_CONTROL_REG && read<BLK_ADDR_EXT>() != 0ull) {
|
||||
if (register_len)
|
||||
width_bits = register_len * 8;
|
||||
else {
|
||||
width_bits = read<BLK_WIDTH_EXT>();
|
||||
}
|
||||
|
||||
auto const as = read<BLK_AS_EXT>();
|
||||
if (as != Addressspace::IO) {
|
||||
Genode::error("unsupported address space access method ", as);
|
||||
return 0;
|
||||
}
|
||||
|
||||
raw_io_addr = read<BLK_ADDR_EXT>();
|
||||
} else if (read<BLK_ADDR>() != 0 && register_len != 0) {
|
||||
width_bits = register_len * 8;
|
||||
raw_io_addr = read<BLK_ADDR>();
|
||||
}
|
||||
|
||||
if (!width_bits || !raw_io_addr)
|
||||
return 0u;
|
||||
|
||||
/* I/O address has to be 16 bit */
|
||||
if (raw_io_addr >= (1ull << 16) || width_bits >= (1ull << 16)) {
|
||||
Genode::error("too large I/O address ", raw_io_addr, " ", width_bits);
|
||||
return 0u;
|
||||
}
|
||||
|
||||
Genode::uint16_t io_addr = Genode::uint16_t(raw_io_addr);
|
||||
|
||||
if (half_register)
|
||||
width_bits /= 2;
|
||||
|
||||
if (half_offset)
|
||||
io_addr += Genode::uint16_t(width_bits) / 2 / 8;
|
||||
|
||||
if (hw_read) {
|
||||
switch (width_bits) {
|
||||
case 8:
|
||||
return inb(io_addr);
|
||||
case 16:
|
||||
return inw(io_addr);
|
||||
case 32:
|
||||
return inl(io_addr);
|
||||
default:
|
||||
Genode::error("unsupported width for I/O IN : ", width_bits);
|
||||
}
|
||||
} else {
|
||||
switch (width_bits) {
|
||||
case 8:
|
||||
outb(io_addr, Genode::uint8_t(value));
|
||||
break;
|
||||
case 16:
|
||||
outw(io_addr, Genode::uint16_t(value));
|
||||
break;
|
||||
case 32:
|
||||
outl(io_addr, Genode::uint32_t(value));
|
||||
break;
|
||||
case 64:
|
||||
outl(io_addr, Genode::uint32_t(value));
|
||||
if (sizeof(value) == 8)
|
||||
outl(io_addr + 4, Genode::uint32_t(value >> 32));
|
||||
break;
|
||||
default:
|
||||
Genode::error("unsupported width for I/O OUT : ", width_bits);
|
||||
}
|
||||
}
|
||||
return 0u;
|
||||
}
|
||||
|
||||
template <typename REG_LEN, typename BLK_ADDR, typename BLK_WIDTH_EXT,
|
||||
typename BLK_AS_EXT, typename BLK_ADDR_EXT>
|
||||
unsigned _read(bool half_register = false, bool half_offset = false)
|
||||
{
|
||||
enum { READ = true };
|
||||
unsigned value = 0;
|
||||
|
||||
return _access<REG_LEN, BLK_ADDR, BLK_WIDTH_EXT, BLK_AS_EXT,
|
||||
BLK_ADDR_EXT>(READ, value, half_register, half_offset);
|
||||
}
|
||||
|
||||
template <typename REG_LEN, typename BLK_ADDR, typename BLK_WIDTH_EXT,
|
||||
typename BLK_AS_EXT, typename BLK_ADDR_EXT, typename T>
|
||||
void _write(T value, bool half_register = false, bool half_offset = false)
|
||||
{
|
||||
enum { WRITE = false };
|
||||
|
||||
_access<REG_LEN, BLK_ADDR, BLK_WIDTH_EXT, BLK_AS_EXT,
|
||||
BLK_ADDR_EXT>(WRITE, value, half_register, half_offset);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Hw::Acpi_facs : Genode::Mmio
|
||||
{
|
||||
struct Length : Register < 0x04, 32> { };
|
||||
struct Fw_wake_vector : Register < 0x0c, 32> { };
|
||||
struct Fw_wake_vector_ext : Register < 0x18, 64> { };
|
||||
|
||||
void wakeup_vector(addr_t const entry)
|
||||
{
|
||||
auto len = read<Length>();
|
||||
if (len >= 0x0c + 4)
|
||||
write<Fw_wake_vector>(unsigned(entry));
|
||||
|
||||
if (len >= 0x18 + 8)
|
||||
write<Fw_wake_vector_ext>(0);
|
||||
}
|
||||
|
||||
Acpi_facs(addr_t const mmio) : Mmio(mmio) { }
|
||||
};
|
||||
|
||||
|
||||
template <typename FUNC>
|
||||
void Hw::for_each_rsdt_entry(Hw::Acpi_generic &rsdt, FUNC fn)
|
||||
{
|
||||
|
@ -39,6 +39,7 @@ struct Hw::Pc_board::Boot_info
|
||||
Acpi_rsdp acpi_rsdp { };
|
||||
Framebuffer framebuffer { };
|
||||
Genode::addr_t efi_system_table { 0 };
|
||||
Genode::addr_t acpi_fadt { 0 };
|
||||
|
||||
Boot_info() {}
|
||||
Boot_info(Acpi_rsdp const &acpi_rsdp,
|
||||
|
Loading…
x
Reference in New Issue
Block a user