blob: f1b61d736bb8ae9c3dc0583d23de8e8ac9a89ecd [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <hypervisor/acpi.h>
#include <hypervisor/address.h>
#include <sys/stat.h>
#if __x86_64__
extern "C" {
#include <acpica/acpi.h>
#include <acpica/actables.h>
#include <acpica/actypes.h>
}
static const char kDsdtPath[] = "/boot/data/dsdt.aml";
static const char kMadtPath[] = "/boot/data/madt.aml";
static const char kMcfgPath[] = "/boot/data/mcfg.aml";
static uint8_t acpi_checksum(void* table, uint32_t length) {
auto checksum = UINT8_MAX - AcpiTbChecksum(static_cast<uint8_t*>(table), length) + 1;
return static_cast<uint8_t>(checksum);
}
static void acpi_header(ACPI_TABLE_HEADER* header, const char* signature, uint32_t length) {
memcpy(header->Signature, signature, ACPI_NAME_SIZE);
header->Length = length;
header->Checksum = acpi_checksum(header, header->Length);
}
static zx_status_t load_file(const char* path, uintptr_t addr, size_t size, uint32_t* actual) {
int fd = open(path, O_RDONLY);
if (fd < 0) {
fprintf(stderr, "Failed to open ACPI table \"%s\"\n", path);
return ZX_ERR_IO;
}
struct stat stat;
int ret = fstat(fd, &stat);
if (ret < 0) {
fprintf(stderr, "Failed to stat ACPI table \"%s\"\n", path);
return ZX_ERR_IO;
}
if ((size_t)stat.st_size > size) {
fprintf(stderr, "Not enough space for ACPI table \"%s\"\n", path);
return ZX_ERR_IO;
}
ssize_t count = read(fd, (void*)addr, stat.st_size);
if (count < 0 || count != stat.st_size) {
fprintf(stderr, "Failed to read ACPI table \"%s\"\n", path);
return ZX_ERR_IO;
}
*actual = static_cast<uint32_t>(stat.st_size);
return ZX_OK;
}
#endif // __x86_64__
zx_status_t guest_create_acpi_table(uintptr_t addr, size_t size, uintptr_t acpi_off) {
#if __x86_64__
if (size < acpi_off + PAGE_SIZE)
return ZX_ERR_BUFFER_TOO_SMALL;
const uint32_t rsdt_entries = 3;
const uint32_t rsdt_length = sizeof(ACPI_TABLE_RSDT) + (rsdt_entries - 1) * sizeof(uint32_t);
// RSDP. ACPI 1.0.
ACPI_RSDP_COMMON* rsdp = (ACPI_RSDP_COMMON*)(addr + acpi_off);
ACPI_MAKE_RSDP_SIG(rsdp->Signature);
memcpy(rsdp->OemId, "ZX", 2);
rsdp->RsdtPhysicalAddress = static_cast<uint32_t>(acpi_off + sizeof(ACPI_RSDP_COMMON));
rsdp->Checksum = acpi_checksum(rsdp, ACPI_RSDP_CHECKSUM_LENGTH);
// FADT.
const uint32_t fadt_off = rsdp->RsdtPhysicalAddress + rsdt_length;
ACPI_TABLE_FADT* fadt = (ACPI_TABLE_FADT*)(addr + fadt_off);
const uint32_t dsdt_off = static_cast<uint32_t>(fadt_off + sizeof(ACPI_TABLE_FADT));
fadt->Dsdt = dsdt_off;
fadt->Pm1aEventBlock = PM1_EVENT_PORT;
fadt->Pm1EventLength = (ACPI_PM1_REGISTER_WIDTH / 8) * 2 /* enable and status registers */;
fadt->Pm1aControlBlock = PM1_CONTROL_PORT;
fadt->Pm1ControlLength = ACPI_PM1_REGISTER_WIDTH / 8;
acpi_header(&fadt->Header, ACPI_SIG_FADT, sizeof(ACPI_TABLE_FADT));
// DSDT.
uint32_t actual;
zx_status_t status = load_file(kDsdtPath, addr + dsdt_off, size - dsdt_off, &actual);
if (status != ZX_OK)
return status;
// MADT.
const uint32_t madt_off = dsdt_off + actual;
status = load_file(kMadtPath, addr + madt_off, size - madt_off, &actual);
if (status != ZX_OK)
return status;
// MCFG.
const uint32_t mcfg_off = madt_off + actual;
status = load_file(kMcfgPath, addr + mcfg_off, size - mcfg_off, &actual);
if (status != ZX_OK)
return status;
// RSDT.
ACPI_TABLE_RSDT* rsdt = (ACPI_TABLE_RSDT*)(addr + rsdp->RsdtPhysicalAddress);
rsdt->TableOffsetEntry[0] = fadt_off;
rsdt->TableOffsetEntry[1] = madt_off;
rsdt->TableOffsetEntry[2] = mcfg_off;
acpi_header(&rsdt->Header, ACPI_SIG_RSDT, rsdt_length);
return ZX_OK;
#else // __x86_64__
return ZX_ERR_NOT_SUPPORTED;
#endif // __x86_64__
}