blob: 8d60571f3afe70108873f2e2e2a46e7f8f61de5f [file] [log] [blame]
/*
* Copyright 2017, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the GNU General Public License version 2. Note that NO WARRANTY is provided.
* See "LICENSE_GPLv2.txt" for details.
*
* @TAG(DATA61_GPL)
*/
/* Routines for generating guest ACPI tables.
Author: W.A. */
#include <stdlib.h>
#include <string.h>
#include "vmm/debug.h"
#include "vmm/platform/acpi.h"
#include "vmm/platform/guest_memory.h"
#include "vmm/platform/guest_vspace.h"
#include "vmm/processor/apicdef.h"
#include "platsupport/plat/acpi/acpi.h"
#define APIC_FLAGS_ENABLED (1)
uint8_t acpi_calc_checksum(const char* table, int length)
{
uint32_t sum = 0;
for (int i = 0; i < length; i++) {
sum += *table++;
}
return 0x100 - (sum & 0xFF);
}
static void acpi_fill_table_head(acpi_header_t *head, const char *signature, uint8_t rev) {
const char *oem = "NICTA ";
const char *padd = " ";
memcpy(head->signature, signature, sizeof(head->signature));
memcpy(head->oem_id, oem, sizeof(head->oem_id));
memcpy(head->creator_id, oem, sizeof(head->creator_id));
memcpy(head->oem_table_id, signature, sizeof(head->signature));
memcpy(head->oem_table_id + sizeof(head->signature), padd, 4);
head->revision = rev;
head->checksum = 0;
head->length = sizeof(*head);
head->oem_revision = rev;
head->creator_revision = 1;
}
static int make_guest_acpi_tables_continued(uintptr_t paddr, void *vaddr,
size_t size, size_t offset, void *cookie) {
(void)offset;
memcpy((char *)vaddr, (char *)cookie, size);
return 0;
}
// Give some ACPI tables to the guest
int make_guest_acpi_tables(vmm_t *vmm) {
DPRINTF(2, "Making ACPI tables\n");
int cpus = vmm->num_vcpus;
int err;
// XSDT and other tables
void *tables[MAX_ACPI_TABLES];
size_t table_sizes[MAX_ACPI_TABLES];
int num_tables = 1;
// MADT
int madt_size = sizeof(acpi_madt_t)
/* + sizeof(acpi_madt_ioapic_t)*/
+ sizeof(acpi_madt_local_apic_t) * cpus;
acpi_madt_t *madt = malloc(madt_size);
acpi_fill_table_head(&madt->header, "APIC", 3);
madt->local_int_crt_address = APIC_DEFAULT_PHYS_BASE;
madt->flags = 1;
char *madt_entry = (char *)madt + sizeof(acpi_madt_t);
#if 0
acpi_madt_ioapic_t ioapic = { // MADT IOAPIC entry
.header = {
.type = ACPI_APIC_IOAPIC,
.length = sizeof(acpi_madt_ioapic_t)
},
.ioapic_id = 0,
.address = IOAPIC_DEFAULT_PHYS_BASE,
.gs_interrupt_base = 0 // TODO set this up?
};
memcpy(madt_entry, &ioapic, sizeof(ioapic));
madt_entry += sizeof(ioapic);
#endif
for (int i = 0; i < cpus; i++) { // MADT APIC entries
acpi_madt_local_apic_t apic = {
.header = {
.type = ACPI_APIC_LOCAL,
.length = sizeof(acpi_madt_local_apic_t)
},
.processor_id = i + 1,
.apic_id = i,
.flags = APIC_FLAGS_ENABLED
};
memcpy(madt_entry, &apic, sizeof(apic));
madt_entry += sizeof(apic);
}
madt->header.length = madt_size;
madt->header.checksum = acpi_calc_checksum((char *)madt, madt_size);
tables[num_tables] = madt;
table_sizes[num_tables] = madt_size;
num_tables++;
// Could set up other tables here...
// XSDT
size_t xsdt_size = sizeof(acpi_xsdt_t) + sizeof(uint64_t) * (num_tables - 1);
// How much space will all the tables take up?
size_t acpi_size = xsdt_size;
for (int i = 1; i < num_tables; i++) {
acpi_size += table_sizes[i];
}
// Allocate some frames for this
uintptr_t xsdt_addr = 0xe1000; // TODO actually allocate frames, can be anywhere
acpi_xsdt_t *xsdt = malloc(xsdt_size);
acpi_fill_table_head(&xsdt->header, "XSDT", 1);
// Add previous tables to XSDT pointer list
uintptr_t table_paddr = xsdt_addr + xsdt_size;
uint64_t *entry = (uint64_t *)((char *)xsdt + sizeof(acpi_xsdt_t));
for (int i = 1; i < num_tables; i++) {
*entry++ = (uint64_t)table_paddr;
table_paddr += table_sizes[i];
}
xsdt->header.length = xsdt_size;
xsdt->header.checksum = acpi_calc_checksum((char *)xsdt, xsdt_size);
tables[0] = xsdt;
table_sizes[0] = xsdt_size;
// Copy all the tables to guest
table_paddr = xsdt_addr;
for (int i = 0; i < num_tables; i++) {
DPRINTF(2, "ACPI table \"%.4s\", addr = %p, size = %zu bytes\n",
(char *)tables[i], (void*)table_paddr, table_sizes[i]);
err = vmm_guest_vspace_touch(&vmm->guest_mem.vspace, table_paddr,
table_sizes[i], make_guest_acpi_tables_continued, tables[i]);
if (err) {
return err;
}
table_paddr += table_sizes[i];
}
// RSDP
uintptr_t rsdp_addr = ACPI_START;
err = vmm_alloc_guest_device_at(vmm, ACPI_START, sizeof(acpi_rsdp_t));
if (err) {
return err;
}
acpi_rsdp_t rsdp = {
.signature = "RSD PTR ",
.oem_id = "NICTA ",
.revision = 2, /* ACPI v3*/
.checksum = 0,
.rsdt_address = xsdt_addr,
/* rsdt_addrss will not be inspected as the xsdt is present.
This is not ACPI 1 compliant */
.length = sizeof(acpi_rsdp_t),
.xsdt_address = xsdt_addr,
.extended_checksum = 0,
.reserved = {0}
};
rsdp.checksum = acpi_calc_checksum((char *)&rsdp, 20);
rsdp.extended_checksum = acpi_calc_checksum((char *)&rsdp, sizeof(rsdp));
DPRINTF(2, "ACPI RSDP addr = %p\n", (void*)rsdp_addr);
return vmm_guest_vspace_touch(&vmm->guest_mem.vspace, rsdp_addr, sizeof(rsdp),
make_guest_acpi_tables_continued, &rsdp);
}