Files
xen/tools/libacpi/mk_dsdt.c
T
Shannon Zhao c4ac107799 libxl/arm: Generate static ACPI DSDT table
It uses static DSDT table like the way x86 uses. Currently the DSDT
table only contains processor device objects and it generates the
maximal objects which so far is 128.

While the GUEST_MAX_VCPUS is defined under __XEN__ or __XEN_TOOLS__, it
needs to add -D__XEN_TOOLS__ to compile mk_dsdt.c.

Also only check iasl for aarch64 in configure since ACPI on ARM32 is not
supported.

Signed-off-by: Shannon Zhao <shannon.zhao@linaro.org>
Acked-by: Julien Grall <julien.grall@arm.com>
Acked-by: Wei Liu <wei.liu2@citrix.com>
[ wei: run autogen.sh and fix compilation on x86 ]
Signed-off-by: Wei Liu <wei.liu2@citrix.com>
2016-09-30 11:47:26 +01:00

469 lines
14 KiB
C

/*
* 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 <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#include <string.h>
#include <getopt.h>
#include <stdlib.h>
#include <stdbool.h>
#if defined(__i386__) || defined(__x86_64__)
#include <xen/hvm/hvm_info_table.h>
#elif defined(__aarch64__)
#include <xen/arch-arm.h>
#endif
static unsigned int indent_level;
static bool debug = false;
typedef enum dm_version {
QEMU_NONE,
QEMU_XEN_TRADITIONAL,
QEMU_XEN,
} dm_version;
static void indent(void)
{
unsigned int i;
for ( i = 0; i < indent_level; i++ )
printf(" ");
}
static __attribute__((format(printf, 2, 3)))
void _stmt(const char *name, const char *fmt, ...)
{
va_list args;
indent();
printf("%s", name);
if ( !fmt )
return;
printf(" ( ");
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
printf(" )");
}
#define stmt(n, f, a...) \
do { \
_stmt(n, f , ## a ); \
printf("\n"); \
} while (0)
#define push_block(n, f, a...) \
do { \
_stmt(n, f , ## a ); \
printf(" {\n"); \
indent_level++; \
} while (0)
static void pop_block(void)
{
indent_level--;
indent();
printf("}\n");
}
static void pci_hotplug_notify(unsigned int slt)
{
stmt("Notify", "\\_SB.PCI0.S%02X, EVT", slt);
}
static void decision_tree(
unsigned int s, unsigned int e, char *var, void (*leaf)(unsigned int))
{
if ( s == (e-1) )
{
(*leaf)(s);
return;
}
push_block("If", "And(%s, 0x%02x)", var, (e-s)/2);
decision_tree((s+e)/2, e, var, leaf);
pop_block();
push_block("Else", NULL);
decision_tree(s, (s+e)/2, var, leaf);
pop_block();
}
static struct option options[] = {
{ "maxcpu", 1, 0, 'c' },
{ "dm-version", 1, 0, 'q' },
{ "debug", 1, 0, 'd' },
{ 0, 0, 0, 0 }
};
int main(int argc, char **argv)
{
unsigned int slot, cpu, max_cpus;
dm_version dm_version = QEMU_XEN_TRADITIONAL;
#if defined(__i386__) || defined(__x86_64__)
max_cpus = HVM_MAX_VCPUS;
#elif defined(__aarch64__)
max_cpus = GUEST_MAX_VCPUS;
#endif
for ( ; ; )
{
int opt = getopt_long(argc, argv, "", options, NULL);
if ( opt == -1 )
break;
switch ( opt )
{
case 'c': {
long i = 0;
char *endptr;
i = strtol(optarg, &endptr, 10);
if ( (*optarg != '\0') && (*endptr == '\0') && (i >= 0) )
{
max_cpus = i;
}
else if ( !(strcmp(optarg, "any") == 0) )
{
fprintf(stderr, "`%s' is not a number or is < 0.\n", optarg);
return -1;
}
break;
}
case 'q':
if (strcmp(optarg, "qemu-xen") == 0) {
dm_version = QEMU_XEN;
} else if (strcmp(optarg, "qemu-xen-traditional") == 0) {
dm_version = QEMU_XEN_TRADITIONAL;
} else if (strcmp(optarg, "none") == 0) {
dm_version = QEMU_NONE;
} else {
fprintf(stderr, "Unknown device model version `%s'.\n", optarg);
return -1;
}
break;
case 'd':
if (*optarg == 'y')
debug = true;
break;
default:
return -1;
}
}
/**** DSDT DefinitionBlock start ****/
/* (we append to existing DSDT definition block) */
indent_level++;
/**** Processor start ****/
push_block("Scope", "\\_SB");
#if defined(__i386__) || defined(__x86_64__)
/* MADT checksum */
stmt("OperationRegion", "MSUM, SystemMemory, \\_SB.MSUA, 1");
push_block("Field", "MSUM, ByteAcc, NoLock, Preserve");
indent(); printf("MSU, 8\n");
pop_block();
/* Processor object helpers. */
push_block("Method", "PMAT, 2");
push_block("If", "LLess(Arg0, NCPU)");
stmt("Return", "ToBuffer(Arg1)");
pop_block();
stmt("Return", "Buffer() {0, 8, 0xff, 0xff, 0, 0, 0, 0}");
pop_block();
#endif
/* Define processor objects and control methods. */
for ( cpu = 0; cpu < max_cpus; cpu++)
{
push_block("Processor", "PR%02X, %d, 0x0000b010, 0x06", cpu, cpu);
stmt("Name", "_HID, \"ACPI0007\"");
stmt("Name", "_UID, %d", cpu);
#if defined(__aarch64__)
pop_block();
continue;
#endif
/* Name this processor's MADT LAPIC descriptor. */
stmt("OperationRegion",
"MATR, SystemMemory, Add(\\_SB.MAPA, %d), 8", cpu*8);
push_block("Field", "MATR, ByteAcc, NoLock, Preserve");
indent(); printf("MAT, 64\n");
pop_block();
push_block("Field", "MATR, ByteAcc, NoLock, Preserve");
indent(); printf("Offset(4),\n");
indent(); printf("FLG, 1\n");
pop_block();
push_block("Method", "_MAT, 0");
if ( cpu )
stmt("Return", "PMAT (%d, MAT)", cpu);
else
stmt("Return", "ToBuffer(MAT)");
pop_block();
push_block("Method", "_STA");
if ( cpu )
push_block("If", "LLess(%d, \\_SB.NCPU)", cpu);
push_block("If", "FLG");
stmt("Return", "0xF");
pop_block();
if ( cpu )
pop_block();
stmt("Return", "0x0");
pop_block();
push_block("Method", "_EJ0, 1, NotSerialized");
stmt("Sleep", "0xC8");
pop_block();
pop_block();
}
#if defined(__aarch64__)
pop_block();
/**** Processor end ****/
pop_block();
/**** DSDT DefinitionBlock end ****/
return 0;
#endif
/* Operation Region 'PRST': bitmask of online CPUs. */
stmt("OperationRegion", "PRST, SystemIO, 0xaf00, 32");
push_block("Field", "PRST, ByteAcc, NoLock, Preserve");
indent(); printf("PRS, %u\n", max_cpus);
pop_block();
/* Control method 'PRSC': CPU hotplug GPE handler. */
push_block("Method", "PRSC, 0");
stmt("Store", "ToBuffer(PRS), Local0");
for ( cpu = 0; cpu < max_cpus; cpu++ )
{
/* Read a byte at a time from the PRST online-CPU bitmask. */
if ( (cpu & 7) == 0 )
stmt("Store", "DerefOf(Index(Local0, %u)), Local1", cpu/8);
else
stmt("ShiftRight", "Local1, 1, Local1");
/* Extract current CPU's status: 0=offline; 1=online. */
stmt("And", "Local1, 1, Local2");
/* Check if status is up-to-date in the relevant MADT LAPIC entry... */
push_block("If", "LNotEqual(Local2, \\_SB.PR%02X.FLG)", cpu);
/* ...If not, update it and the MADT checksum, and notify OSPM. */
stmt("Store", "Local2, \\_SB.PR%02X.FLG", cpu);
push_block("If", "LEqual(Local2, 1)");
stmt("Notify", "PR%02X, 1", cpu); /* Notify: Device Check */
stmt("Subtract", "\\_SB.MSU, 1, \\_SB.MSU"); /* Adjust MADT csum */
pop_block();
push_block("Else", NULL);
stmt("Notify", "PR%02X, 3", cpu); /* Notify: Eject Request */
stmt("Add", "\\_SB.MSU, 1, \\_SB.MSU"); /* Adjust MADT csum */
pop_block();
pop_block();
}
stmt("Return", "One");
pop_block();
pop_block();
if (dm_version == QEMU_NONE) {
pop_block();
return 0;
}
/* Define GPE control method. */
push_block("Scope", "\\_GPE");
push_block("Method",
dm_version == QEMU_XEN_TRADITIONAL ? "_L02" : "_E02");
stmt("\\_SB.PRSC ()", NULL);
pop_block();
pop_block();
/**** Processor end ****/
/**** PCI0 start ****/
push_block("Scope", "\\_SB.PCI0");
/*
* Reserve the IO port ranges [0x10c0, 0x1101] and [0xb044, 0xb047].
* Or else, for a hotplugged-in device, the port IO BAR assigned
* by guest OS may conflict with the ranges here.
*/
push_block("Device", "HP0"); {
stmt("Name", "_HID, EISAID(\"PNP0C02\")");
if (dm_version == QEMU_XEN_TRADITIONAL) {
stmt("Name", "_CRS, ResourceTemplate() {"
" IO (Decode16, 0x10c0, 0x10c0, 0x00, 0x82)"
" IO (Decode16, 0xb044, 0xb044, 0x00, 0x04)"
"}");
} else {
stmt("Name", "_CRS, ResourceTemplate() {"
" IO (Decode16, 0xae00, 0xae00, 0x00, 0x10)"
" IO (Decode16, 0xb044, 0xb044, 0x00, 0x04)"
"}");
}
} pop_block();
/*
* Each PCI hotplug slot needs at least two methods to handle
* the ACPI event:
* _EJ0: eject a device
* _STA: return a device's status, e.g. enabled or removed
*
* Eject button would generate a general-purpose event, then the
* control method for this event uses Notify() to inform OSPM which
* action happened and on which device.
*
* Pls. refer "6.3 Device Insertion, Removal, and Status Objects"
* in ACPI spec 3.0b for details.
*
* QEMU provides a simple hotplug controller with some I/O to handle
* the hotplug action and status, which is beyond the ACPI scope.
*/
if (dm_version == QEMU_XEN_TRADITIONAL) {
for ( slot = 0; slot < 0x100; slot++ )
{
push_block("Device", "S%02X", slot);
/* _ADR == dev:fn (16:16) */
stmt("Name", "_ADR, 0x%08x", ((slot & ~7) << 13) | (slot & 7));
/* _SUN == dev */
stmt("Name", "_SUN, 0x%08x", slot >> 3);
push_block("Method", "_EJ0, 1");
if (debug)
{
stmt("Store", "0x%02x, \\_GPE.DPT1", slot);
stmt("Store", "0x88, \\_GPE.DPT2");
}
stmt("Store", "0x%02x, \\_GPE.PH%02X", /* eject */
(slot & 1) ? 0x10 : 0x01, slot & ~1);
pop_block();
push_block("Method", "_STA, 0");
if (debug)
{
stmt("Store", "0x%02x, \\_GPE.DPT1", slot);
stmt("Store", "0x89, \\_GPE.DPT2");
}
if ( slot & 1 )
stmt("ShiftRight", "0x4, \\_GPE.PH%02X, Local1", slot & ~1);
else
stmt("And", "\\_GPE.PH%02X, 0x0f, Local1", slot & ~1);
stmt("Return", "Local1"); /* IN status as the _STA */
pop_block();
pop_block();
}
} else {
stmt("OperationRegion", "SEJ, SystemIO, 0xae08, 0x04");
push_block("Field", "SEJ, DWordAcc, NoLock, WriteAsZeros");
indent(); printf("B0EJ, 32,\n");
pop_block();
/* hotplug_slot */
for (slot = 1; slot <= 31; slot++) {
push_block("Device", "S%i", slot); {
stmt("Name", "_ADR, %#06x0000", slot);
push_block("Method", "_EJ0,1"); {
stmt("Store", "%#010x, B0EJ", 1 << slot);
} pop_block();
stmt("Name", "_SUN, %i", slot);
} pop_block();
}
}
pop_block();
/**** PCI0 end ****/
/**** GPE start ****/
push_block("Scope", "\\_GPE");
if (dm_version == QEMU_XEN_TRADITIONAL) {
stmt("OperationRegion", "PHP, SystemIO, 0x10c0, 0x82");
push_block("Field", "PHP, ByteAcc, NoLock, Preserve");
indent(); printf("PSTA, 8,\n"); /* hotplug controller event reg */
indent(); printf("PSTB, 8,\n"); /* hotplug controller slot reg */
for ( slot = 0; slot < 0x100; slot += 2 )
{
indent();
/* Each hotplug control register manages a pair of pci functions. */
printf("PH%02X, 8,\n", slot);
}
pop_block();
} else {
stmt("OperationRegion", "PCST, SystemIO, 0xae00, 0x08");
push_block("Field", "PCST, DWordAcc, NoLock, WriteAsZeros");
indent(); printf("PCIU, 32,\n");
indent(); printf("PCID, 32,\n");
pop_block();
}
stmt("OperationRegion", "DG1, SystemIO, 0xb044, 0x04");
push_block("Field", "DG1, ByteAcc, NoLock, Preserve");
indent(); printf("DPT1, 8, DPT2, 8\n");
pop_block();
if (dm_version == QEMU_XEN_TRADITIONAL) {
push_block("Method", "_L03, 0, Serialized");
/* Detect slot and event (remove/add). */
stmt("Name", "SLT, 0x0");
stmt("Name", "EVT, 0x0");
stmt("Store", "PSTA, Local1");
stmt("And", "Local1, 0xf, EVT");
stmt("Store", "PSTB, Local1"); /* XXX: Store (PSTB, SLT) ? */
stmt("And", "Local1, 0xff, SLT");
if (debug)
{
stmt("Store", "SLT, DPT1");
stmt("Store", "EVT, DPT2");
}
/* Decision tree */
decision_tree(0x00, 0x100, "SLT", pci_hotplug_notify);
pop_block();
} else {
push_block("Method", "_E01");
for (slot = 1; slot <= 31; slot++) {
push_block("If", "And(PCIU, ShiftLeft(1, %i))", slot);
stmt("Notify", "\\_SB.PCI0.S%i, 1", slot);
pop_block();
push_block("If", "And(PCID, ShiftLeft(1, %i))", slot);
stmt("Notify", "\\_SB.PCI0.S%i, 3", slot);
pop_block();
}
pop_block();
}
pop_block();
/**** GPE end ****/
pop_block();
/**** DSDT DefinitionBlock end ****/
return 0;
}
/*
* Local variables:
* mode: C
* c-file-style: "BSD"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/