Files
xen/tools/libs/light/libxl_arm_acpi.c
T
Juergen Gross 41aea82de2 tools/libxl: move libxenlight to tools/libs/light
Carve out all libxenlight related sources and move them to
tools/libs/light in order to use the generic library build environment.

The closely related sources for libxl-save-helper and the libxl test
environment are being moved, too.

Signed-off-by: Juergen Gross <jgross@suse.com>
Acked-by: Wei Liu <wl@xen.org>
2020-10-01 13:26:09 +02:00

414 lines
13 KiB
C

/*
* ARM DomU ACPI generation
*
* Copyright (C) 2016 Linaro Ltd.
*
* Author: Shannon Zhao <shannon.zhao@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; version 2.1 only. with the special
* exception on linking described in file LICENSE.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*/
#include "libxl_arm.h"
#include <stdint.h>
/* Below typedefs are useful for the headers under acpi/ */
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int64_t s64;
#include <acpi/acconfig.h>
#include <acpi/actbl.h>
#ifndef BITS_PER_LONG
#ifdef _LP64
#define BITS_PER_LONG 64
#else
#define BITS_PER_LONG 32
#endif
#endif
#define ACPI_MACHINE_WIDTH BITS_PER_LONG
#define COMPILER_DEPENDENT_INT64 int64_t
#define COMPILER_DEPENDENT_UINT64 uint64_t
#include <acpi/actypes.h>
_hidden
extern const unsigned char dsdt_anycpu_arm[];
_hidden
extern const int dsdt_anycpu_arm_len;
#define ACPI_OEM_ID "Xen\0\0"
#define ACPI_OEM_TABLE_ID "ARM\0\0\0\0"
#define ACPI_ASL_COMPILER_ID "XL\0"
enum {
RSDP,
XSDT,
GTDT,
MADT,
FADT,
DSDT,
MAX_TABLE_NUMS,
};
struct acpitable {
uint64_t addr;
size_t size;
};
static int libxl__estimate_madt_size(libxl__gc *gc,
const libxl_domain_build_info *info,
size_t *size)
{
int rc = 0;
switch (info->arch_arm.gic_version) {
case LIBXL_GIC_VERSION_V2:
*size = sizeof(struct acpi_table_madt) +
ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus +
sizeof(struct acpi_madt_generic_distributor);
break;
case LIBXL_GIC_VERSION_V3:
*size = sizeof(struct acpi_table_madt) +
ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus +
sizeof(struct acpi_madt_generic_distributor) +
sizeof(struct acpi_madt_generic_redistributor);
break;
default:
LOG(ERROR, "Unknown GIC version");
*size = 0;
rc = ERROR_FAIL;
break;
}
return rc;
}
int libxl__get_acpi_size(libxl__gc *gc,
const libxl_domain_build_info *info,
uint64_t *out)
{
uint64_t size;
int rc = 0;
rc = libxl__estimate_madt_size(gc, info, &size);
if (rc < 0)
goto out;
*out = ROUNDUP(size, 3) +
ROUNDUP(sizeof(struct acpi_table_rsdp), 3) +
ROUNDUP(sizeof(struct acpi_table_xsdt), 3) +
ROUNDUP(sizeof(struct acpi_table_gtdt), 3) +
ROUNDUP(sizeof(struct acpi_table_fadt), 3) +
ROUNDUP(sizeof(dsdt_anycpu_arm_len), 3);
out:
return rc;
}
static int libxl__allocate_acpi_tables(libxl__gc *gc,
libxl_domain_build_info *info,
struct xc_dom_image *dom,
struct acpitable acpitables[])
{
int rc;
size_t size;
acpitables[RSDP].addr = GUEST_ACPI_BASE;
acpitables[RSDP].size = sizeof(struct acpi_table_rsdp);
dom->acpi_modules[0].length += ROUNDUP(acpitables[RSDP].size, 3);
acpitables[XSDT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
/*
* Currently only 3 tables(GTDT, FADT, MADT) are pointed by XSDT. Alloc
* entries for them.
*/
acpitables[XSDT].size = sizeof(struct acpi_table_xsdt) +
sizeof(uint64_t) * 2;
dom->acpi_modules[0].length += ROUNDUP(acpitables[XSDT].size, 3);
acpitables[GTDT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
acpitables[GTDT].size = sizeof(struct acpi_table_gtdt);
dom->acpi_modules[0].length += ROUNDUP(acpitables[GTDT].size, 3);
acpitables[MADT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
rc = libxl__estimate_madt_size(gc, info, &size);
if (rc < 0)
goto out;
acpitables[MADT].size = size;
dom->acpi_modules[0].length += ROUNDUP(acpitables[MADT].size, 3);
acpitables[FADT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
acpitables[FADT].size = sizeof(struct acpi_table_fadt);
dom->acpi_modules[0].length += ROUNDUP(acpitables[FADT].size, 3);
acpitables[DSDT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
acpitables[DSDT].size = dsdt_anycpu_arm_len;
dom->acpi_modules[0].length += ROUNDUP(acpitables[DSDT].size, 3);
assert(dom->acpi_modules[0].length <= GUEST_ACPI_SIZE);
dom->acpi_modules[0].data = libxl__zalloc(gc, dom->acpi_modules[0].length);
rc = 0;
out:
return rc;
}
static void calculate_checksum(void *table, uint32_t checksum_offset,
uint32_t length)
{
uint8_t *p, sum = 0;
p = table;
p[checksum_offset] = 0;
while (length--)
sum = sum + *p++;
p = table;
p[checksum_offset] = -sum;
}
static void make_acpi_rsdp(libxl__gc *gc, struct xc_dom_image *dom,
struct acpitable acpitables[])
{
uint64_t offset = acpitables[RSDP].addr - GUEST_ACPI_BASE;
struct acpi_table_rsdp *rsdp = (void *)dom->acpi_modules[0].data + offset;
memcpy(rsdp->signature, "RSD PTR ", sizeof(rsdp->signature));
BUILD_BUG_ON(sizeof(ACPI_OEM_ID) != sizeof(rsdp->oem_id));
memcpy(rsdp->oem_id, ACPI_OEM_ID, sizeof(rsdp->oem_id));
rsdp->length = acpitables[RSDP].size;
rsdp->revision = 0x02;
rsdp->xsdt_physical_address = acpitables[XSDT].addr;
calculate_checksum(rsdp,
offsetof(struct acpi_table_rsdp, extended_checksum),
acpitables[RSDP].size);
}
static void make_acpi_header(struct acpi_table_header *h, const char *sig,
size_t len, uint8_t rev)
{
memcpy(h->signature, sig, 4);
h->length = len;
h->revision = rev;
BUILD_BUG_ON(sizeof(ACPI_OEM_ID) != sizeof(h->oem_id));
memcpy(h->oem_id, ACPI_OEM_ID, sizeof(h->oem_id));
BUILD_BUG_ON(sizeof(ACPI_OEM_TABLE_ID) != sizeof(h->oem_table_id));
memcpy(h->oem_table_id, ACPI_OEM_TABLE_ID, sizeof(h->oem_table_id));
h->oem_revision = 0;
BUILD_BUG_ON(sizeof(ACPI_ASL_COMPILER_ID) != sizeof(h->asl_compiler_id));
memcpy(h->asl_compiler_id, ACPI_ASL_COMPILER_ID,
sizeof(h->asl_compiler_id));
h->asl_compiler_revision = 0;
h->checksum = 0;
}
static void make_acpi_xsdt(libxl__gc *gc, struct xc_dom_image *dom,
struct acpitable acpitables[])
{
uint64_t offset = acpitables[XSDT].addr - GUEST_ACPI_BASE;
struct acpi_table_xsdt *xsdt = (void *)dom->acpi_modules[0].data + offset;
xsdt->table_offset_entry[0] = acpitables[MADT].addr;
xsdt->table_offset_entry[1] = acpitables[GTDT].addr;
xsdt->table_offset_entry[2] = acpitables[FADT].addr;
make_acpi_header(&xsdt->header, "XSDT", acpitables[XSDT].size, 1);
calculate_checksum(xsdt, offsetof(struct acpi_table_header, checksum),
acpitables[XSDT].size);
}
static void make_acpi_gtdt(libxl__gc *gc, struct xc_dom_image *dom,
struct acpitable acpitables[])
{
uint64_t offset = acpitables[GTDT].addr - GUEST_ACPI_BASE;
struct acpi_table_gtdt *gtdt = (void *)dom->acpi_modules[0].data + offset;
gtdt->non_secure_el1_interrupt = GUEST_TIMER_PHYS_NS_PPI;
gtdt->non_secure_el1_flags =
(ACPI_LEVEL_SENSITIVE << ACPI_GTDT_INTERRUPT_MODE)
|(ACPI_ACTIVE_LOW << ACPI_GTDT_INTERRUPT_POLARITY);
gtdt->virtual_timer_interrupt = GUEST_TIMER_VIRT_PPI;
gtdt->virtual_timer_flags =
(ACPI_LEVEL_SENSITIVE << ACPI_GTDT_INTERRUPT_MODE)
|(ACPI_ACTIVE_LOW << ACPI_GTDT_INTERRUPT_POLARITY);
gtdt->counter_block_addresss = ~((uint64_t)0);
gtdt->counter_read_block_address = ~((uint64_t)0);
make_acpi_header(&gtdt->header, "GTDT", acpitables[GTDT].size, 2);
calculate_checksum(gtdt, offsetof(struct acpi_table_header, checksum),
acpitables[GTDT].size);
}
static void make_acpi_madt_gicc(void *table, int nr_cpus, uint64_t gicc_base)
{
int i;
struct acpi_madt_generic_interrupt *gicc = table;
for (i = 0; i < nr_cpus; i++) {
gicc->header.type = ACPI_MADT_TYPE_GENERIC_INTERRUPT;
gicc->header.length = ACPI_MADT_GICC_SIZE_v5;
gicc->base_address = gicc_base;
gicc->cpu_interface_number = i;
gicc->arm_mpidr = libxl__compute_mpdir(i);
gicc->uid = i;
gicc->flags = ACPI_MADT_ENABLED;
gicc = table + ACPI_MADT_GICC_SIZE_v5;
}
}
static void make_acpi_madt_gicd(void *table, uint64_t gicd_base,
uint8_t gic_version)
{
struct acpi_madt_generic_distributor *gicd = table;
gicd->header.type = ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR;
gicd->header.length = sizeof(*gicd);
gicd->base_address = gicd_base;
/* This version field has no meaning before ACPI 5.1 errata. */
gicd->version = gic_version;
}
static void make_acpi_madt_gicr(void *table, uint64_t gicr_base,
uint64_t gicr_size)
{
struct acpi_madt_generic_redistributor *gicr = table;
gicr->header.type = ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR;
gicr->header.length = sizeof(*gicr);
gicr->base_address = gicr_base;
gicr->length = gicr_size;
}
static int make_acpi_madt(libxl__gc *gc, struct xc_dom_image *dom,
libxl_domain_build_info *info,
struct acpitable acpitables[])
{
uint64_t offset = acpitables[MADT].addr - GUEST_ACPI_BASE;
void *table = dom->acpi_modules[0].data + offset;
struct acpi_table_madt *madt = table;
int rc = 0;
switch (info->arch_arm.gic_version) {
case LIBXL_GIC_VERSION_V2:
table += sizeof(struct acpi_table_madt);
make_acpi_madt_gicc(table, info->max_vcpus, GUEST_GICC_BASE);
table += ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus;
make_acpi_madt_gicd(table, GUEST_GICD_BASE, ACPI_MADT_GIC_VERSION_V2);
break;
case LIBXL_GIC_VERSION_V3:
table += sizeof(struct acpi_table_madt);
make_acpi_madt_gicc(table, info->max_vcpus, 0);
table += ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus;
make_acpi_madt_gicd(table, GUEST_GICV3_GICD_BASE,
ACPI_MADT_GIC_VERSION_V3);
table += sizeof(struct acpi_madt_generic_distributor);
make_acpi_madt_gicr(table, GUEST_GICV3_GICR0_BASE,
GUEST_GICV3_GICR0_SIZE);
break;
default:
LOG(ERROR, "Unknown GIC version");
rc = ERROR_FAIL;
goto out;
}
make_acpi_header(&madt->header, "APIC", acpitables[MADT].size, 3);
calculate_checksum(madt, offsetof(struct acpi_table_header, checksum),
acpitables[MADT].size);
out:
return rc;
}
static void make_acpi_fadt(libxl__gc *gc, struct xc_dom_image *dom,
struct acpitable acpitables[])
{
uint64_t offset = acpitables[FADT].addr - GUEST_ACPI_BASE;
struct acpi_table_fadt *fadt = (void *)dom->acpi_modules[0].data + offset;
/* Hardware Reduced = 1 and use PSCI 0.2+ and with HVC */
fadt->flags = ACPI_FADT_HW_REDUCED;
fadt->arm_boot_flags = ACPI_FADT_PSCI_COMPLIANT | ACPI_FADT_PSCI_USE_HVC;
/* ACPI v5.1 (fadt->revision.fadt->minor_revision) */
fadt->minor_revision = 0x1;
fadt->dsdt = acpitables[DSDT].addr;
make_acpi_header(&fadt->header, "FACP", acpitables[FADT].size, 5);
calculate_checksum(fadt, offsetof(struct acpi_table_header, checksum),
acpitables[FADT].size);
}
static void make_acpi_dsdt(libxl__gc *gc, struct xc_dom_image *dom,
struct acpitable acpitables[])
{
uint64_t offset = acpitables[DSDT].addr - GUEST_ACPI_BASE;
void *dsdt = dom->acpi_modules[0].data + offset;
memcpy(dsdt, dsdt_anycpu_arm, dsdt_anycpu_arm_len);
}
int libxl__prepare_acpi(libxl__gc *gc, libxl_domain_build_info *info,
struct xc_dom_image *dom)
{
const libxl_version_info *vers;
int rc = 0;
struct acpitable acpitables[MAX_TABLE_NUMS];
vers = libxl_get_version_info(CTX);
if (vers == NULL) {
rc = ERROR_FAIL;
goto out;
}
LOG(DEBUG, "constructing ACPI tables for Xen version %d.%d guest",
vers->xen_version_major, vers->xen_version_minor);
dom->acpi_modules[0].data = NULL;
dom->acpi_modules[0].length = 0;
dom->acpi_modules[0].guest_addr_out = GUEST_ACPI_BASE;
rc = libxl__allocate_acpi_tables(gc, info, dom, acpitables);
if (rc)
goto out;
make_acpi_rsdp(gc, dom, acpitables);
make_acpi_xsdt(gc, dom, acpitables);
make_acpi_gtdt(gc, dom, acpitables);
rc = make_acpi_madt(gc, dom, info, acpitables);
if (rc)
goto out;
make_acpi_fadt(gc, dom, acpitables);
make_acpi_dsdt(gc, dom, acpitables);
out:
return rc;
}
/*
* Local variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/