arch-x86: Implement ACPI root tables
The RSDP points to the RSDT (32 bit) and/or the XSDT (64 bit), which are both instances of the abstract System Description Table. This commit implements the mechanism to write the three data structures to memory based on the full system's configuration. The SysDescTable class acts as base class for the RSDT and XSDT as well as any future implementation of other System Description Tables. Change-Id: I710279a72376c04f2a636ff2e96fa80228d03eaf Signed-off-by: Maximilian Stein <m@steiny.biz> Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/42824 Reviewed-by: Gabe Black <gabe.black@gmail.com> Maintainer: Gabe Black <gabe.black@gmail.com> Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
@@ -79,6 +79,7 @@ DebugFlag('Faults', "Trace all faults/exceptions/traps")
|
||||
DebugFlag('LocalApic', "Local APIC debugging")
|
||||
DebugFlag('Decoder', "Decoder debug output")
|
||||
DebugFlag('X86', "Generic X86 ISA debugging")
|
||||
DebugFlag('ACPI', "ACPI debugging")
|
||||
|
||||
python_files = (
|
||||
'__init__.py',
|
||||
|
||||
@@ -48,8 +48,8 @@ class X86ACPISysDescTable(SimObject):
|
||||
oem_table_id = Param.String('', 'oem table ID')
|
||||
oem_revision = Param.UInt32(0, 'oem revision number for the table')
|
||||
|
||||
creator_id = Param.String('',
|
||||
'string identifying the generator of the table')
|
||||
creator_id = Param.UInt32(0,
|
||||
'ID identifying the generator of the table')
|
||||
creator_revision = Param.UInt32(0,
|
||||
'revision number for the creator of the table')
|
||||
|
||||
@@ -78,6 +78,7 @@ class X86ACPIRSDP(SimObject):
|
||||
# here.
|
||||
revision = Param.UInt8(2, 'revision of ACPI being used, zero indexed')
|
||||
|
||||
rsdt = Param.X86ACPIRSDT(NULL, 'root system description table')
|
||||
rsdt = Param.X86ACPIRSDT(X86ACPIRSDT(),
|
||||
'root system description table')
|
||||
xsdt = Param.X86ACPIXSDT(X86ACPIXSDT(),
|
||||
'extended system description table')
|
||||
|
||||
@@ -37,16 +37,34 @@
|
||||
|
||||
#include "arch/x86/bios/acpi.hh"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
#include "base/trace.hh"
|
||||
#include "mem/port.hh"
|
||||
#include "params/X86ACPIRSDP.hh"
|
||||
#include "params/X86ACPIRSDT.hh"
|
||||
#include "params/X86ACPISysDescTable.hh"
|
||||
#include "params/X86ACPIXSDT.hh"
|
||||
#include "mem/port_proxy.hh"
|
||||
#include "sim/byteswap.hh"
|
||||
#include "sim/sim_object.hh"
|
||||
|
||||
namespace X86ISA
|
||||
{
|
||||
|
||||
namespace ACPI
|
||||
{
|
||||
|
||||
const char RSDP::signature[] = "RSD PTR ";
|
||||
|
||||
static uint8_t
|
||||
apic_checksum(uint8_t* ptr, std::size_t size)
|
||||
{
|
||||
uint8_t sum = 0;
|
||||
for (unsigned i = 0; i < size; ++i)
|
||||
sum += ptr[i];
|
||||
return 0x100 - sum;
|
||||
}
|
||||
|
||||
Addr
|
||||
X86ISA::ACPI::LinearAllocator::alloc(std::size_t size, unsigned align)
|
||||
LinearAllocator::alloc(std::size_t size, unsigned align)
|
||||
{
|
||||
if (align) {
|
||||
unsigned offset = next % align;
|
||||
@@ -59,24 +77,124 @@ X86ISA::ACPI::LinearAllocator::alloc(std::size_t size, unsigned align)
|
||||
return chunk;
|
||||
}
|
||||
|
||||
const char X86ISA::ACPI::RSDP::signature[] = "RSD PTR ";
|
||||
|
||||
X86ISA::ACPI::RSDP::RSDP(const Params &p) : SimObject(p), oemID(p.oem_id),
|
||||
revision(p.revision), rsdt(p.rsdt), xsdt(p.xsdt)
|
||||
RSDP::RSDP(const Params &p) :
|
||||
SimObject(p),
|
||||
rsdt(p.rsdt),
|
||||
xsdt(p.xsdt)
|
||||
{}
|
||||
|
||||
X86ISA::ACPI::SysDescTable::SysDescTable(const Params &p,
|
||||
const char * _signature, uint8_t _revision) : SimObject(p),
|
||||
signature(_signature), revision(_revision),
|
||||
oemID(p.oem_id), oemTableID(p.oem_table_id),
|
||||
oemRevision(p.oem_revision),
|
||||
creatorID(p.creator_id), creatorRevision(p.creator_revision)
|
||||
Addr
|
||||
RSDP::write(PortProxy& phys_proxy, Allocator& alloc) const
|
||||
{
|
||||
std::vector<uint8_t> mem(sizeof(Mem));
|
||||
Addr addr = alloc.alloc(mem.size(), 16);
|
||||
|
||||
Mem* data = (Mem*)mem.data();
|
||||
static_assert(sizeof(signature) - 1 == sizeof(data->signature),
|
||||
"signature length mismatch");
|
||||
std::memcpy(data->signature, signature, sizeof(data->signature));
|
||||
std::strncpy(data->oemID, params().oem_id.c_str(), sizeof(data->oemID));
|
||||
data->revision = params().revision;
|
||||
data->length = mem.size();
|
||||
|
||||
if (rsdt) {
|
||||
data->rsdtAddress = rsdt->write(phys_proxy, alloc);
|
||||
DPRINTF(ACPI, "Allocated RSDT @ %llx\n", data->rsdtAddress);
|
||||
}
|
||||
if (xsdt) {
|
||||
data->xsdtAddress = xsdt->write(phys_proxy, alloc);
|
||||
DPRINTF(ACPI, "Allocated XSDT @ %llx\n", data->xsdtAddress);
|
||||
}
|
||||
|
||||
// checksum calculation
|
||||
data->checksum = apic_checksum(mem.data(), sizeof(MemR0));
|
||||
data->extendedChecksum = apic_checksum(mem.data(), mem.size());
|
||||
|
||||
// write the whole thing
|
||||
phys_proxy.writeBlob(addr, mem.data(), mem.size());
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
Addr
|
||||
SysDescTable::writeBuf(PortProxy& phys_proxy, Allocator& alloc,
|
||||
std::vector<uint8_t> &mem) const
|
||||
{
|
||||
// An empty SysDescTable doesn't make any sense, so assert that somebody
|
||||
// else allocated a large enough blob.
|
||||
assert(mem.size() >= sizeof(Mem));
|
||||
|
||||
// Allocate a place to write this blob.
|
||||
Addr addr = alloc.alloc(mem.size());
|
||||
|
||||
DPRINTF(ACPI, "Writing system description table [%llx - %llx]\n", addr,
|
||||
addr + mem.size());
|
||||
|
||||
// Fill in the header.
|
||||
auto& p = params();
|
||||
Mem* header = (Mem*)mem.data();
|
||||
std::strncpy(header->signature, signature, sizeof(header->signature));
|
||||
header->length = mem.size();
|
||||
header->revision = revision;
|
||||
std::strncpy(header->oemID, p.oem_id.c_str(), sizeof(header->oemID));
|
||||
std::strncpy(header->oemTableID, p.oem_table_id.c_str(),
|
||||
sizeof(header->oemTableID));
|
||||
header->oemRevision = p.oem_revision;
|
||||
header->creatorID = p.creator_id;
|
||||
header->creatorRevision = p.creator_revision;
|
||||
|
||||
// Update checksum.
|
||||
header->checksum = apic_checksum(mem.data(), mem.size());
|
||||
|
||||
// Write to memory.
|
||||
phys_proxy.writeBlob(addr, mem.data(), mem.size());
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
//// RSDT, XSDT
|
||||
template<class T>
|
||||
RXSDT<T>::RXSDT(const Params& p, const char *_signature, uint8_t _revision) :
|
||||
SysDescTable(p, _signature, _revision)
|
||||
{}
|
||||
|
||||
X86ISA::ACPI::RSDT::RSDT(const Params &p) :
|
||||
SysDescTable(p, "RSDT", 1), entries(p.entries)
|
||||
{}
|
||||
template<class T>
|
||||
Addr
|
||||
RXSDT<T>::writeBuf(PortProxy& phys_proxy, Allocator& alloc,
|
||||
std::vector<uint8_t>& mem) const
|
||||
{
|
||||
// Since this table ends with a variably sized array, it can't be extended
|
||||
// by another table type.
|
||||
assert(mem.empty());
|
||||
mem.resize(sizeof(Mem));
|
||||
|
||||
X86ISA::ACPI::XSDT::XSDT(const Params &p) :
|
||||
SysDescTable(p, "XSDT", 1), entries(p.entries)
|
||||
{}
|
||||
auto base_size = mem.size();
|
||||
mem.resize(base_size + sizeof(Ptr) * entries.size());
|
||||
|
||||
Ptr* ptr_array = reinterpret_cast<Ptr*>(mem.data() + base_size);
|
||||
DPRINTF(ACPI, "RXSDT: Writing %d entries (ptr size: %d)\n", entries.size(),
|
||||
sizeof(Ptr));
|
||||
for (const auto *entry : entries) {
|
||||
Addr entry_addr = entry->write(phys_proxy, alloc);
|
||||
fatal_if((entry_addr & mask(sizeof(Ptr) * 8)) != entry_addr,
|
||||
"RXSDT: Entry address doesn't fit in pointer type.");
|
||||
DPRINTF(ACPI, "RXSDT: wrote entry @ %llx\n", entry_addr);
|
||||
*ptr_array++ = entry_addr;
|
||||
}
|
||||
|
||||
return SysDescTable::writeBuf(phys_proxy, alloc, mem);
|
||||
}
|
||||
|
||||
RSDT::RSDT(const Params& p) : RXSDT(p, "RSDT", 1)
|
||||
{
|
||||
entries = p.entries;
|
||||
}
|
||||
|
||||
XSDT::XSDT(const Params& p) : RXSDT(p, "XSDT", 1)
|
||||
{
|
||||
entries = p.entries;
|
||||
}
|
||||
|
||||
} // namespace ACPI
|
||||
|
||||
} // namespace X86ISA
|
||||
|
||||
@@ -39,18 +39,20 @@
|
||||
#define __ARCH_X86_BIOS_ACPI_HH__
|
||||
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "base/compiler.hh"
|
||||
#include "base/types.hh"
|
||||
#include "debug/ACPI.hh"
|
||||
#include "params/X86ACPIRSDP.hh"
|
||||
#include "params/X86ACPIRSDT.hh"
|
||||
#include "params/X86ACPISysDescTable.hh"
|
||||
#include "params/X86ACPIXSDT.hh"
|
||||
#include "sim/sim_object.hh"
|
||||
|
||||
class Port;
|
||||
|
||||
struct X86ACPIRSDPParams;
|
||||
|
||||
struct X86ACPISysDescTableParams;
|
||||
struct X86ACPIRSDTParams;
|
||||
struct X86ACPIXSDTParams;
|
||||
class PortProxy;
|
||||
|
||||
namespace X86ISA
|
||||
{
|
||||
@@ -60,7 +62,6 @@ namespace ACPI
|
||||
|
||||
class RSDT;
|
||||
class XSDT;
|
||||
class SysDescTable;
|
||||
|
||||
struct Allocator
|
||||
{
|
||||
@@ -83,56 +84,110 @@ struct LinearAllocator : public Allocator
|
||||
class RSDP : public SimObject
|
||||
{
|
||||
protected:
|
||||
typedef X86ACPIRSDPParams Params;
|
||||
PARAMS(X86ACPIRSDP);
|
||||
|
||||
static const char signature[];
|
||||
|
||||
std::string oemID;
|
||||
uint8_t revision;
|
||||
struct M5_ATTR_PACKED MemR0
|
||||
{
|
||||
// src: https://wiki.osdev.org/RSDP
|
||||
char signature[8] = {};
|
||||
uint8_t checksum = 0;
|
||||
char oemID[6] = {};
|
||||
uint8_t revision = 0;
|
||||
uint32_t rsdtAddress = 0;
|
||||
};
|
||||
static_assert(std::is_trivially_copyable<MemR0>::value,
|
||||
"Type not suitable for memcpy.");
|
||||
|
||||
RSDT * rsdt;
|
||||
XSDT * xsdt;
|
||||
struct M5_ATTR_PACKED Mem : public MemR0
|
||||
{
|
||||
// since version 2
|
||||
uint32_t length = 0;
|
||||
uint64_t xsdtAddress = 0;
|
||||
uint8_t extendedChecksum = 0;
|
||||
uint8_t _reserved[3] = {};
|
||||
};
|
||||
static_assert(std::is_trivially_copyable<Mem>::value,
|
||||
"Type not suitable for memcpy,");
|
||||
|
||||
RSDT* rsdt;
|
||||
XSDT* xsdt;
|
||||
|
||||
public:
|
||||
RSDP(const Params &p);
|
||||
|
||||
Addr write(PortProxy& phys_proxy, Allocator& alloc) const;
|
||||
};
|
||||
|
||||
class SysDescTable : public SimObject
|
||||
{
|
||||
protected:
|
||||
typedef X86ACPISysDescTableParams Params;
|
||||
PARAMS(X86ACPISysDescTable);
|
||||
|
||||
const char * signature;
|
||||
struct M5_ATTR_PACKED Mem
|
||||
{
|
||||
// src: https://wiki.osdev.org/RSDT
|
||||
char signature[4] = {};
|
||||
uint32_t length = 0;
|
||||
uint8_t revision = 0;
|
||||
uint8_t checksum = 0;
|
||||
char oemID[6] = {};
|
||||
char oemTableID[8] = {};
|
||||
uint32_t oemRevision = 0;
|
||||
uint32_t creatorID = 0;
|
||||
uint32_t creatorRevision = 0;
|
||||
};
|
||||
static_assert(std::is_trivially_copyable<Mem>::value,
|
||||
"Type not suitable for memcpy.");
|
||||
|
||||
virtual Addr writeBuf(PortProxy& phys_proxy, Allocator& alloc,
|
||||
std::vector<uint8_t>& mem) const = 0;
|
||||
|
||||
const char* signature;
|
||||
uint8_t revision;
|
||||
|
||||
std::string oemID;
|
||||
std::string oemTableID;
|
||||
uint32_t oemRevision;
|
||||
|
||||
std::string creatorID;
|
||||
uint32_t creatorRevision;
|
||||
SysDescTable(const Params& p, const char* _signature, uint8_t _revision) :
|
||||
SimObject(p), signature(_signature), revision(_revision)
|
||||
{}
|
||||
|
||||
public:
|
||||
SysDescTable(const Params &p, const char * _signature, uint8_t _revision);
|
||||
Addr
|
||||
write(PortProxy& phys_proxy, Allocator& alloc) const
|
||||
{
|
||||
std::vector<uint8_t> mem;
|
||||
return writeBuf(phys_proxy, alloc, mem);
|
||||
}
|
||||
};
|
||||
|
||||
class RSDT : public SysDescTable
|
||||
template<class T>
|
||||
class RXSDT : public SysDescTable
|
||||
{
|
||||
protected:
|
||||
typedef X86ACPIRSDTParams Params;
|
||||
using Ptr = T;
|
||||
|
||||
std::vector<SysDescTable *> entries;
|
||||
|
||||
Addr writeBuf(PortProxy& phys_proxy, Allocator& alloc,
|
||||
std::vector<uint8_t>& mem) const override;
|
||||
|
||||
protected:
|
||||
RXSDT(const Params& p, const char* _signature, uint8_t _revision);
|
||||
};
|
||||
|
||||
class RSDT : public RXSDT<uint32_t>
|
||||
{
|
||||
protected:
|
||||
PARAMS(X86ACPIRSDT);
|
||||
|
||||
public:
|
||||
RSDT(const Params &p);
|
||||
};
|
||||
|
||||
class XSDT : public SysDescTable
|
||||
class XSDT : public RXSDT<uint64_t>
|
||||
{
|
||||
protected:
|
||||
typedef X86ACPIXSDTParams Params;
|
||||
|
||||
std::vector<SysDescTable *> entries;
|
||||
PARAMS(X86ACPIXSDT);
|
||||
|
||||
public:
|
||||
XSDT(const Params &p);
|
||||
|
||||
@@ -38,11 +38,13 @@
|
||||
|
||||
#include "arch/x86/fs_workload.hh"
|
||||
|
||||
#include "arch/x86/bios/acpi.hh"
|
||||
#include "arch/x86/bios/intelmp.hh"
|
||||
#include "arch/x86/bios/smbios.hh"
|
||||
#include "arch/x86/faults.hh"
|
||||
#include "base/loader/object_file.hh"
|
||||
#include "cpu/thread_context.hh"
|
||||
#include "debug/ACPI.hh"
|
||||
#include "params/X86FsWorkload.hh"
|
||||
#include "sim/system.hh"
|
||||
|
||||
@@ -330,6 +332,10 @@ FsWorkload::initState()
|
||||
// Write out the Intel MP Specification configuration table.
|
||||
writeOutMPTable(ebdaPos, fixed, table);
|
||||
ebdaPos += (fixed + table);
|
||||
|
||||
// Write out ACPI tables
|
||||
writeOutACPITables(ebdaPos, table);
|
||||
ebdaPos += table;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -375,4 +381,17 @@ FsWorkload::writeOutMPTable(Addr fp, Addr &fpSize, Addr &tableSize, Addr table)
|
||||
assert(fpSize == 0x10);
|
||||
}
|
||||
|
||||
void
|
||||
FsWorkload::writeOutACPITables(Addr fp, Addr &fpSize)
|
||||
{
|
||||
fpSize = 0;
|
||||
if (rsdp) {
|
||||
ACPI::LinearAllocator alloc(fp, 0x000FFFFF);
|
||||
rsdp->write(system->physProxy, alloc);
|
||||
fpSize = alloc.alloc(0, 0) - fp;
|
||||
DPRINTF(ACPI, "Wrote ACPI tables to memory at %llx with size %llx.\n",
|
||||
fp, fpSize);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace X86ISA
|
||||
|
||||
@@ -89,6 +89,8 @@ class FsWorkload : public KernelWorkload
|
||||
|
||||
void writeOutMPTable(Addr fp,
|
||||
Addr &fpSize, Addr &tableSize, Addr table=0);
|
||||
|
||||
void writeOutACPITables(Addr begin, Addr &size);
|
||||
};
|
||||
|
||||
} // namespace X86ISA
|
||||
|
||||
Reference in New Issue
Block a user