blob: f2dbcfbdc912c20e0063818667475ed764cf54fa [file] [log] [blame]
/*
* Copyright (c) 2008 The Hewlett-Packard Development Company
* All rights reserved.
*
* The license below extends only to copyright in the software and shall
* not be construed as granting a license to any other intellectual
* property including but not limited to intellectual property relating
* to a hardware implementation of the functionality of the software
* licensed hereunder. You may use the software subject to the license
* terms below provided that you ensure that this notice is replicated
* unmodified and in its entirety in all distributions of the software,
* modified or unmodified, in source code or in binary form.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "arch/x86/bios/acpi.hh"
#include <algorithm>
#include <cassert>
#include <cstring>
#include "base/trace.hh"
#include "mem/port.hh"
#include "mem/port_proxy.hh"
#include "sim/byteswap.hh"
#include "sim/sim_object.hh"
namespace gem5
{
namespace X86ISA
{
namespace ACPI
{
static void
fillCopy(void *dest, size_t dest_size, const void *src, size_t src_size)
{
src_size = std::min(src_size, dest_size);
const size_t zero_size = dest_size - src_size;
uint8_t *pos = (uint8_t *)dest;
std::memcpy(pos, src, src_size);
pos += src_size;
std::memset(pos, 0, zero_size);
}
static void
fillCopy(void *dest, size_t dest_size, const std::string &src)
{
fillCopy(dest, dest_size, src.c_str(), src.length());
}
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
LinearAllocator::alloc(std::size_t size, unsigned align)
{
if (align) {
unsigned offset = next % align;
if (offset)
next += (align - offset);
}
Addr chunk = next;
next += size;
assert(0 == end || next <= end);
return chunk;
}
RSDP::RSDP(const Params &p) :
SimObject(p),
rsdt(p.rsdt),
xsdt(p.xsdt)
{}
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));
fillCopy(data->oemID, sizeof(data->oemID), params().oem_id);
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();
fillCopy(header->signature, sizeof(header->signature), signature);
header->length = mem.size();
header->revision = revision;
fillCopy(header->oemID, sizeof(header->oemID), p.oem_id);
fillCopy(header->oemTableID, sizeof(header->oemTableID), p.oem_table_id);
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)
{}
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));
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;
}
//// MADT
MADT::MADT::MADT(const Params& p) :
SysDescTable(p, "APIC", 4),
records(p.records)
{}
Addr
MADT::MADT::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));
Mem* header = reinterpret_cast<Mem*>(mem.data());
header->localAPICAddress = params().local_apic_address;
header->flags = params().flags;
for (const auto& record : records) {
auto entry = record->prepare();
mem.insert(mem.end(), entry.begin(), entry.end());
}
DPRINTF(ACPI, "MADT: writing %d records (size: %d)\n",
records.size(), mem.size());
return SysDescTable::writeBuf(phys_proxy, alloc, mem);
}
void
MADT::Record::prepareBuf(std::vector<uint8_t>& mem) const
{
assert(mem.size() >= sizeof(Mem));
DPRINTF(ACPI, "MADT: writing record type %d (size: %d)\n",
type, mem.size());
Mem* header = reinterpret_cast<Mem*>(mem.data());
header->type = type;
header->length = mem.size();
}
void
MADT::LAPIC::prepareBuf(std::vector<uint8_t>& mem) const
{
assert(mem.empty());
mem.resize(sizeof(Mem));
Mem* data = reinterpret_cast<Mem*>(mem.data());
data->acpiProcessorId = params().acpi_processor_id;
data->apicId = params().apic_id;
data->flags = params().flags;
Record::prepareBuf(mem);
}
void
MADT::IOAPIC::prepareBuf(std::vector<uint8_t>& mem) const
{
assert(mem.empty());
mem.resize(sizeof(Mem));
Mem* data = reinterpret_cast<Mem*>(mem.data());
data->ioApicId = params().id;
data->ioApicAddress = params().address;
data->intBase = params().int_base;
Record::prepareBuf(mem);
}
void
MADT::IntSourceOverride::prepareBuf(std::vector<uint8_t>& mem) const
{
assert(mem.empty());
mem.resize(sizeof(Mem));
Mem* data = reinterpret_cast<Mem*>(mem.data());
data->busSource = params().bus_source;
data->irqSource = params().irq_source;
data->globalSystemInterrupt = params().sys_int;
data->flags = params().flags;
Record::prepareBuf(mem);
}
void
MADT::NMI::prepareBuf(std::vector<uint8_t>& mem) const
{
assert(mem.empty());
mem.resize(sizeof(Mem));
Mem* data = reinterpret_cast<Mem*>(mem.data());
data->acpiProcessorId = params().acpi_processor_id;
// The "flags" field is not properly aligned.
memcpy(&data->flags, &params().flags, sizeof(data->flags));
data->lintNo = params().lint_no;
Record::prepareBuf(mem);
}
void
MADT::LAPICOverride::prepareBuf(std::vector<uint8_t>& mem) const
{
assert(mem.empty());
mem.resize(sizeof(Mem));
Mem* data = reinterpret_cast<Mem*>(mem.data());
data->localAPICAddress = params().address;
Record::prepareBuf(mem);
}
} // namespace ACPI
} // namespace X86ISA
} // namespace gem5