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:
Maximilian Stein
2021-03-11 19:31:36 +01:00
parent bd99d938c0
commit 6e1b3d7da0
6 changed files with 248 additions and 52 deletions

View File

@@ -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',

View File

@@ -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')

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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