| /* |
| * 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, ¶ms().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 |