aboutsummaryrefslogtreecommitdiff
path: root/hw/i386/acpi-build.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/i386/acpi-build.c')
-rw-r--r--hw/i386/acpi-build.c1191
1 files changed, 507 insertions, 684 deletions
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 4944249b84..d0a5c85e10 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -54,6 +54,8 @@
#include "hw/i386/q35-acpi-dsdt.hex"
#include "hw/i386/acpi-dsdt.hex"
+#include "hw/acpi/aml-build.h"
+
#include "qapi/qmp/qint.h"
#include "qom/qom-qobject.h"
#include "exec/ram_addr.h"
@@ -99,15 +101,21 @@ typedef struct AcpiPmInfo {
uint32_t gpe0_blk;
uint32_t gpe0_blk_len;
uint32_t io_base;
+ uint16_t cpu_hp_io_base;
+ uint16_t cpu_hp_io_len;
+ uint16_t mem_hp_io_base;
+ uint16_t mem_hp_io_len;
+ uint16_t pcihp_io_base;
+ uint16_t pcihp_io_len;
} AcpiPmInfo;
typedef struct AcpiMiscInfo {
bool has_hpet;
bool has_tpm;
- DECLARE_BITMAP(slot_hotplug_enable, PCI_SLOT_MAX);
const unsigned char *dsdt_code;
unsigned dsdt_size;
uint16_t pvpanic_port;
+ uint16_t applesmc_io_base;
} AcpiMiscInfo;
typedef struct AcpiBuildPciBusHotplugState {
@@ -119,7 +127,6 @@ typedef struct AcpiBuildPciBusHotplugState {
static void acpi_get_dsdt(AcpiMiscInfo *info)
{
- uint16_t *applesmc_sta;
Object *piix = piix4_pm_find();
Object *lpc = ich9_lpc_find();
assert(!!piix != !!lpc);
@@ -127,17 +134,11 @@ static void acpi_get_dsdt(AcpiMiscInfo *info)
if (piix) {
info->dsdt_code = AcpiDsdtAmlCode;
info->dsdt_size = sizeof AcpiDsdtAmlCode;
- applesmc_sta = piix_dsdt_applesmc_sta;
}
if (lpc) {
info->dsdt_code = Q35AcpiDsdtAmlCode;
info->dsdt_size = sizeof Q35AcpiDsdtAmlCode;
- applesmc_sta = q35_dsdt_applesmc_sta;
}
-
- /* Patch in appropriate value for AppleSMC _STA */
- *(uint8_t *)(info->dsdt_code + *applesmc_sta) =
- applesmc_find() ? 0x0b : 0x00;
}
static
@@ -172,14 +173,26 @@ static void acpi_get_pm_info(AcpiPmInfo *pm)
Object *obj = NULL;
QObject *o;
+ pm->pcihp_io_base = 0;
+ pm->pcihp_io_len = 0;
if (piix) {
obj = piix;
+ pm->cpu_hp_io_base = PIIX4_CPU_HOTPLUG_IO_BASE;
+ pm->pcihp_io_base =
+ object_property_get_int(obj, ACPI_PCIHP_IO_BASE_PROP, NULL);
+ pm->pcihp_io_len =
+ object_property_get_int(obj, ACPI_PCIHP_IO_LEN_PROP, NULL);
}
if (lpc) {
obj = lpc;
+ pm->cpu_hp_io_base = ICH9_CPU_HOTPLUG_IO_BASE;
}
assert(obj);
+ pm->cpu_hp_io_len = ACPI_GPE_PROC_LEN;
+ pm->mem_hp_io_base = ACPI_MEMORY_HOTPLUG_BASE;
+ pm->mem_hp_io_len = ACPI_MEMORY_HOTPLUG_IO_LEN;
+
/* Fill in optional s3/s4 related properties */
o = object_property_get_qobject(obj, ACPI_PM_PROP_S3_DISABLED, NULL);
if (o) {
@@ -228,6 +241,7 @@ static void acpi_get_misc_info(AcpiMiscInfo *info)
info->has_hpet = hpet_find();
info->has_tpm = tpm_find();
info->pvpanic_port = pvpanic_port();
+ info->applesmc_io_base = applesmc_port();
}
static void acpi_get_pci_info(PcPciInfo *info)
@@ -280,208 +294,6 @@ build_header(GArray *linker, GArray *table_data,
table_data->data, h, len, &h->checksum);
}
-static inline GArray *build_alloc_array(void)
-{
- return g_array_new(false, true /* clear */, 1);
-}
-
-static inline void build_free_array(GArray *array)
-{
- g_array_free(array, true);
-}
-
-static inline void build_prepend_byte(GArray *array, uint8_t val)
-{
- g_array_prepend_val(array, val);
-}
-
-static inline void build_append_byte(GArray *array, uint8_t val)
-{
- g_array_append_val(array, val);
-}
-
-static inline void build_append_array(GArray *array, GArray *val)
-{
- g_array_append_vals(array, val->data, val->len);
-}
-
-#define ACPI_NAMESEG_LEN 4
-
-static void GCC_FMT_ATTR(2, 3)
-build_append_nameseg(GArray *array, const char *format, ...)
-{
- /* It would be nicer to use g_string_vprintf but it's only there in 2.22 */
- char s[] = "XXXX";
- int len;
- va_list args;
-
- va_start(args, format);
- len = vsnprintf(s, sizeof s, format, args);
- va_end(args);
-
- assert(len <= ACPI_NAMESEG_LEN);
-
- g_array_append_vals(array, s, len);
- /* Pad up to ACPI_NAMESEG_LEN characters if necessary. */
- g_array_append_vals(array, "____", ACPI_NAMESEG_LEN - len);
-}
-
-/* 5.4 Definition Block Encoding */
-enum {
- PACKAGE_LENGTH_1BYTE_SHIFT = 6, /* Up to 63 - use extra 2 bits. */
- PACKAGE_LENGTH_2BYTE_SHIFT = 4,
- PACKAGE_LENGTH_3BYTE_SHIFT = 12,
- PACKAGE_LENGTH_4BYTE_SHIFT = 20,
-};
-
-static void build_prepend_package_length(GArray *package, unsigned min_bytes)
-{
- uint8_t byte;
- unsigned length = package->len;
- unsigned length_bytes;
-
- if (length + 1 < (1 << PACKAGE_LENGTH_1BYTE_SHIFT)) {
- length_bytes = 1;
- } else if (length + 2 < (1 << PACKAGE_LENGTH_3BYTE_SHIFT)) {
- length_bytes = 2;
- } else if (length + 3 < (1 << PACKAGE_LENGTH_4BYTE_SHIFT)) {
- length_bytes = 3;
- } else {
- length_bytes = 4;
- }
-
- /* Force length to at least min_bytes.
- * This wastes memory but that's how bios did it.
- */
- length_bytes = MAX(length_bytes, min_bytes);
-
- /* PkgLength is the length of the inclusive length of the data. */
- length += length_bytes;
-
- switch (length_bytes) {
- case 1:
- byte = length;
- build_prepend_byte(package, byte);
- return;
- case 4:
- byte = length >> PACKAGE_LENGTH_4BYTE_SHIFT;
- build_prepend_byte(package, byte);
- length &= (1 << PACKAGE_LENGTH_4BYTE_SHIFT) - 1;
- /* fall through */
- case 3:
- byte = length >> PACKAGE_LENGTH_3BYTE_SHIFT;
- build_prepend_byte(package, byte);
- length &= (1 << PACKAGE_LENGTH_3BYTE_SHIFT) - 1;
- /* fall through */
- case 2:
- byte = length >> PACKAGE_LENGTH_2BYTE_SHIFT;
- build_prepend_byte(package, byte);
- length &= (1 << PACKAGE_LENGTH_2BYTE_SHIFT) - 1;
- /* fall through */
- }
- /*
- * Most significant two bits of byte zero indicate how many following bytes
- * are in PkgLength encoding.
- */
- byte = ((length_bytes - 1) << PACKAGE_LENGTH_1BYTE_SHIFT) | length;
- build_prepend_byte(package, byte);
-}
-
-static void build_package(GArray *package, uint8_t op, unsigned min_bytes)
-{
- build_prepend_package_length(package, min_bytes);
- build_prepend_byte(package, op);
-}
-
-static void build_extop_package(GArray *package, uint8_t op)
-{
- build_package(package, op, 1);
- build_prepend_byte(package, 0x5B); /* ExtOpPrefix */
-}
-
-static void build_append_value(GArray *table, uint32_t value, int size)
-{
- uint8_t prefix;
- int i;
-
- switch (size) {
- case 1:
- prefix = 0x0A; /* BytePrefix */
- break;
- case 2:
- prefix = 0x0B; /* WordPrefix */
- break;
- case 4:
- prefix = 0x0C; /* DWordPrefix */
- break;
- default:
- assert(0);
- return;
- }
- build_append_byte(table, prefix);
- for (i = 0; i < size; ++i) {
- build_append_byte(table, value & 0xFF);
- value = value >> 8;
- }
-}
-
-static void build_append_int(GArray *table, uint32_t value)
-{
- if (value == 0x00) {
- build_append_byte(table, 0x00); /* ZeroOp */
- } else if (value == 0x01) {
- build_append_byte(table, 0x01); /* OneOp */
- } else if (value <= 0xFF) {
- build_append_value(table, value, 1);
- } else if (value <= 0xFFFF) {
- build_append_value(table, value, 2);
- } else {
- build_append_value(table, value, 4);
- }
-}
-
-static GArray *build_alloc_method(const char *name, uint8_t arg_count)
-{
- GArray *method = build_alloc_array();
-
- build_append_nameseg(method, "%s", name);
- build_append_byte(method, arg_count); /* MethodFlags: ArgCount */
-
- return method;
-}
-
-static void build_append_and_cleanup_method(GArray *device, GArray *method)
-{
- uint8_t op = 0x14; /* MethodOp */
-
- build_package(method, op, 0);
-
- build_append_array(device, method);
- build_free_array(method);
-}
-
-static void build_append_notify_target_ifequal(GArray *method,
- GArray *target_name,
- uint32_t value, int size)
-{
- GArray *notify = build_alloc_array();
- uint8_t op = 0xA0; /* IfOp */
-
- build_append_byte(notify, 0x93); /* LEqualOp */
- build_append_byte(notify, 0x68); /* Arg0Op */
- build_append_value(notify, value, size);
- build_append_byte(notify, 0x86); /* NotifyOp */
- build_append_array(notify, target_name);
- build_append_byte(notify, 0x69); /* Arg1Op */
-
- /* Pack it up */
- build_package(notify, op, 1);
-
- build_append_array(method, notify);
-
- build_free_array(notify);
-}
-
/* End here */
#define ACPI_PORT_SMI_CMD 0x00b2 /* TODO: this is APM_CNT_IOPORT */
@@ -508,24 +320,6 @@ static void acpi_align_size(GArray *blob, unsigned align)
g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align));
}
-/* Set a value within table in a safe manner */
-#define ACPI_BUILD_SET_LE(table, size, off, bits, val) \
- do { \
- uint64_t ACPI_BUILD_SET_LE_val = cpu_to_le64(val); \
- memcpy(acpi_data_get_ptr(table, size, off, \
- (bits) / BITS_PER_BYTE), \
- &ACPI_BUILD_SET_LE_val, \
- (bits) / BITS_PER_BYTE); \
- } while (0)
-
-static inline void *acpi_data_get_ptr(uint8_t *table_data, unsigned table_size,
- unsigned off, unsigned size)
-{
- assert(off + size > off);
- assert(off + size <= table_size);
- return table_data + off;
-}
-
static inline void acpi_add_table(GArray *table_offsets, GArray *table_data)
{
uint32_t offset = cpu_to_le32(table_data->len);
@@ -673,115 +467,8 @@ build_madt(GArray *table_data, GArray *linker, AcpiCpuInfo *cpu,
table_data->len - madt_start, 1);
}
-/* Encode a hex value */
-static inline char acpi_get_hex(uint32_t val)
-{
- val &= 0x0f;
- return (val <= 9) ? ('0' + val) : ('A' + val - 10);
-}
-
-#include "hw/i386/ssdt-proc.hex"
-
-/* 0x5B 0x83 ProcessorOp PkgLength NameString ProcID */
-#define ACPI_PROC_OFFSET_CPUHEX (*ssdt_proc_name - *ssdt_proc_start + 2)
-#define ACPI_PROC_OFFSET_CPUID1 (*ssdt_proc_name - *ssdt_proc_start + 4)
-#define ACPI_PROC_OFFSET_CPUID2 (*ssdt_proc_id - *ssdt_proc_start)
-#define ACPI_PROC_SIZEOF (*ssdt_proc_end - *ssdt_proc_start)
-#define ACPI_PROC_AML (ssdp_proc_aml + *ssdt_proc_start)
-
-/* 0x5B 0x82 DeviceOp PkgLength NameString */
-#define ACPI_PCIHP_OFFSET_HEX (*ssdt_pcihp_name - *ssdt_pcihp_start + 1)
-#define ACPI_PCIHP_OFFSET_ID (*ssdt_pcihp_id - *ssdt_pcihp_start)
-#define ACPI_PCIHP_OFFSET_ADR (*ssdt_pcihp_adr - *ssdt_pcihp_start)
-#define ACPI_PCIHP_OFFSET_EJ0 (*ssdt_pcihp_ej0 - *ssdt_pcihp_start)
-#define ACPI_PCIHP_SIZEOF (*ssdt_pcihp_end - *ssdt_pcihp_start)
-#define ACPI_PCIHP_AML (ssdp_pcihp_aml + *ssdt_pcihp_start)
-
-#define ACPI_PCINOHP_OFFSET_HEX (*ssdt_pcinohp_name - *ssdt_pcinohp_start + 1)
-#define ACPI_PCINOHP_OFFSET_ADR (*ssdt_pcinohp_adr - *ssdt_pcinohp_start)
-#define ACPI_PCINOHP_SIZEOF (*ssdt_pcinohp_end - *ssdt_pcinohp_start)
-#define ACPI_PCINOHP_AML (ssdp_pcihp_aml + *ssdt_pcinohp_start)
-
-#define ACPI_PCIVGA_OFFSET_HEX (*ssdt_pcivga_name - *ssdt_pcivga_start + 1)
-#define ACPI_PCIVGA_OFFSET_ADR (*ssdt_pcivga_adr - *ssdt_pcivga_start)
-#define ACPI_PCIVGA_SIZEOF (*ssdt_pcivga_end - *ssdt_pcivga_start)
-#define ACPI_PCIVGA_AML (ssdp_pcihp_aml + *ssdt_pcivga_start)
-
-#define ACPI_PCIQXL_OFFSET_HEX (*ssdt_pciqxl_name - *ssdt_pciqxl_start + 1)
-#define ACPI_PCIQXL_OFFSET_ADR (*ssdt_pciqxl_adr - *ssdt_pciqxl_start)
-#define ACPI_PCIQXL_SIZEOF (*ssdt_pciqxl_end - *ssdt_pciqxl_start)
-#define ACPI_PCIQXL_AML (ssdp_pcihp_aml + *ssdt_pciqxl_start)
-
-#include "hw/i386/ssdt-mem.hex"
-
-/* 0x5B 0x82 DeviceOp PkgLength NameString DimmID */
-#define ACPI_MEM_OFFSET_HEX (*ssdt_mem_name - *ssdt_mem_start + 2)
-#define ACPI_MEM_OFFSET_ID (*ssdt_mem_id - *ssdt_mem_start + 7)
-#define ACPI_MEM_SIZEOF (*ssdt_mem_end - *ssdt_mem_start)
-#define ACPI_MEM_AML (ssdm_mem_aml + *ssdt_mem_start)
-
-#define ACPI_SSDT_SIGNATURE 0x54445353 /* SSDT */
-#define ACPI_SSDT_HEADER_LENGTH 36
-
-#include "hw/i386/ssdt-misc.hex"
-#include "hw/i386/ssdt-pcihp.hex"
#include "hw/i386/ssdt-tpm.hex"
-static void
-build_append_notify_method(GArray *device, const char *name,
- const char *format, int count)
-{
- int i;
- GArray *method = build_alloc_method(name, 2);
-
- for (i = 0; i < count; i++) {
- GArray *target = build_alloc_array();
- build_append_nameseg(target, format, i);
- assert(i < 256); /* Fits in 1 byte */
- build_append_notify_target_ifequal(method, target, i, 1);
- build_free_array(target);
- }
-
- build_append_and_cleanup_method(device, method);
-}
-
-static void patch_pcihp(int slot, uint8_t *ssdt_ptr)
-{
- unsigned devfn = PCI_DEVFN(slot, 0);
-
- ssdt_ptr[ACPI_PCIHP_OFFSET_HEX] = acpi_get_hex(devfn >> 4);
- ssdt_ptr[ACPI_PCIHP_OFFSET_HEX + 1] = acpi_get_hex(devfn);
- ssdt_ptr[ACPI_PCIHP_OFFSET_ID] = slot;
- ssdt_ptr[ACPI_PCIHP_OFFSET_ADR + 2] = slot;
-}
-
-static void patch_pcinohp(int slot, uint8_t *ssdt_ptr)
-{
- unsigned devfn = PCI_DEVFN(slot, 0);
-
- ssdt_ptr[ACPI_PCINOHP_OFFSET_HEX] = acpi_get_hex(devfn >> 4);
- ssdt_ptr[ACPI_PCINOHP_OFFSET_HEX + 1] = acpi_get_hex(devfn);
- ssdt_ptr[ACPI_PCINOHP_OFFSET_ADR + 2] = slot;
-}
-
-static void patch_pcivga(int slot, uint8_t *ssdt_ptr)
-{
- unsigned devfn = PCI_DEVFN(slot, 0);
-
- ssdt_ptr[ACPI_PCIVGA_OFFSET_HEX] = acpi_get_hex(devfn >> 4);
- ssdt_ptr[ACPI_PCIVGA_OFFSET_HEX + 1] = acpi_get_hex(devfn);
- ssdt_ptr[ACPI_PCIVGA_OFFSET_ADR + 2] = slot;
-}
-
-static void patch_pciqxl(int slot, uint8_t *ssdt_ptr)
-{
- unsigned devfn = PCI_DEVFN(slot, 0);
-
- ssdt_ptr[ACPI_PCIQXL_OFFSET_HEX] = acpi_get_hex(devfn >> 4);
- ssdt_ptr[ACPI_PCIQXL_OFFSET_HEX + 1] = acpi_get_hex(devfn);
- ssdt_ptr[ACPI_PCIQXL_OFFSET_ADR + 2] = slot;
-}
-
/* Assign BSEL property to all buses. In the future, this can be changed
* to only assign to buses that support hotplug.
*/
@@ -812,261 +499,155 @@ static void acpi_set_pci_info(void)
}
}
-static void build_pci_bus_state_init(AcpiBuildPciBusHotplugState *state,
- AcpiBuildPciBusHotplugState *parent,
- bool pcihp_bridge_en)
-{
- state->parent = parent;
- state->device_table = build_alloc_array();
- state->notify_table = build_alloc_array();
- state->pcihp_bridge_en = pcihp_bridge_en;
-}
-
-static void build_pci_bus_state_cleanup(AcpiBuildPciBusHotplugState *state)
+static void build_append_pcihp_notify_entry(Aml *method, int slot)
{
- build_free_array(state->device_table);
- build_free_array(state->notify_table);
-}
-
-static void *build_pci_bus_begin(PCIBus *bus, void *parent_state)
-{
- AcpiBuildPciBusHotplugState *parent = parent_state;
- AcpiBuildPciBusHotplugState *child = g_malloc(sizeof *child);
-
- build_pci_bus_state_init(child, parent, parent->pcihp_bridge_en);
+ Aml *if_ctx;
+ int32_t devfn = PCI_DEVFN(slot, 0);
- return child;
+ if_ctx = aml_if(aml_and(aml_arg(0), aml_int(0x1U << slot)));
+ aml_append(if_ctx, aml_notify(aml_name("S%.02X", devfn), aml_arg(1)));
+ aml_append(method, if_ctx);
}
-static void build_pci_bus_end(PCIBus *bus, void *bus_state)
+static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
+ bool pcihp_bridge_en)
{
- AcpiBuildPciBusHotplugState *child = bus_state;
- AcpiBuildPciBusHotplugState *parent = child->parent;
- GArray *bus_table = build_alloc_array();
- DECLARE_BITMAP(slot_hotplug_enable, PCI_SLOT_MAX);
- DECLARE_BITMAP(slot_device_present, PCI_SLOT_MAX);
- DECLARE_BITMAP(slot_device_system, PCI_SLOT_MAX);
- DECLARE_BITMAP(slot_device_vga, PCI_SLOT_MAX);
- DECLARE_BITMAP(slot_device_qxl, PCI_SLOT_MAX);
- uint8_t op;
- int i;
+ Aml *dev, *notify_method, *method;
QObject *bsel;
- GArray *method;
- bool bus_hotplug_support = false;
-
- /*
- * Skip bridge subtree creation if bridge hotplug is disabled
- * to make acpi tables compatible with legacy machine types.
- */
- if (!child->pcihp_bridge_en && bus->parent_dev) {
- return;
- }
-
- if (bus->parent_dev) {
- op = 0x82; /* DeviceOp */
- build_append_nameseg(bus_table, "S%.02X",
- bus->parent_dev->devfn);
- build_append_byte(bus_table, 0x08); /* NameOp */
- build_append_nameseg(bus_table, "_SUN");
- build_append_value(bus_table, PCI_SLOT(bus->parent_dev->devfn), 1);
- build_append_byte(bus_table, 0x08); /* NameOp */
- build_append_nameseg(bus_table, "_ADR");
- build_append_value(bus_table, (PCI_SLOT(bus->parent_dev->devfn) << 16) |
- PCI_FUNC(bus->parent_dev->devfn), 4);
- } else {
- op = 0x10; /* ScopeOp */;
- build_append_nameseg(bus_table, "PCI0");
- }
+ PCIBus *sec;
+ int i;
bsel = object_property_get_qobject(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, NULL);
if (bsel) {
- build_append_byte(bus_table, 0x08); /* NameOp */
- build_append_nameseg(bus_table, "BSEL");
- build_append_int(bus_table, qint_get_int(qobject_to_qint(bsel)));
- memset(slot_hotplug_enable, 0xff, sizeof slot_hotplug_enable);
- } else {
- /* No bsel - no slots are hot-pluggable */
- memset(slot_hotplug_enable, 0x00, sizeof slot_hotplug_enable);
- }
+ int64_t bsel_val = qint_get_int(qobject_to_qint(bsel));
- memset(slot_device_present, 0x00, sizeof slot_device_present);
- memset(slot_device_system, 0x00, sizeof slot_device_present);
- memset(slot_device_vga, 0x00, sizeof slot_device_vga);
- memset(slot_device_qxl, 0x00, sizeof slot_device_qxl);
+ aml_append(parent_scope, aml_name_decl("BSEL", aml_int(bsel_val)));
+ notify_method = aml_method("DVNT", 2);
+ }
for (i = 0; i < ARRAY_SIZE(bus->devices); i += PCI_FUNC_MAX) {
DeviceClass *dc;
PCIDeviceClass *pc;
PCIDevice *pdev = bus->devices[i];
int slot = PCI_SLOT(i);
+ bool hotplug_enabled_dev;
bool bridge_in_acpi;
if (!pdev) {
+ if (bsel) { /* add hotplug slots for non present devices */
+ dev = aml_device("S%.02X", PCI_DEVFN(slot, 0));
+ aml_append(dev, aml_name_decl("_SUN", aml_int(slot)));
+ aml_append(dev, aml_name_decl("_ADR", aml_int(slot << 16)));
+ method = aml_method("_EJ0", 1);
+ aml_append(method,
+ aml_call2("PCEJ", aml_name("BSEL"), aml_name("_SUN"))
+ );
+ aml_append(dev, method);
+ aml_append(parent_scope, dev);
+
+ build_append_pcihp_notify_entry(notify_method, slot);
+ }
continue;
}
- set_bit(slot, slot_device_present);
pc = PCI_DEVICE_GET_CLASS(pdev);
dc = DEVICE_GET_CLASS(pdev);
/* When hotplug for bridges is enabled, bridges are
* described in ACPI separately (see build_pci_bus_end).
* In this case they aren't themselves hot-pluggable.
+ * Hotplugged bridges *are* hot-pluggable.
*/
- bridge_in_acpi = pc->is_bridge && child->pcihp_bridge_en;
+ bridge_in_acpi = pc->is_bridge && pcihp_bridge_en &&
+ !DEVICE(pdev)->hotplugged;
- if (pc->class_id == PCI_CLASS_BRIDGE_ISA || bridge_in_acpi) {
- set_bit(slot, slot_device_system);
+ hotplug_enabled_dev = bsel && dc->hotpluggable && !bridge_in_acpi;
+
+ if (pc->class_id == PCI_CLASS_BRIDGE_ISA) {
+ continue;
}
+ /* start to compose PCI slot descriptor */
+ dev = aml_device("S%.02X", PCI_DEVFN(slot, 0));
+ aml_append(dev, aml_name_decl("_ADR", aml_int(slot << 16)));
+
if (pc->class_id == PCI_CLASS_DISPLAY_VGA) {
- set_bit(slot, slot_device_vga);
+ /* add VGA specific AML methods */
+ int s3d;
if (object_dynamic_cast(OBJECT(pdev), "qxl-vga")) {
- set_bit(slot, slot_device_qxl);
+ s3d = 3;
+ } else {
+ s3d = 0;
}
- }
- if (!dc->hotpluggable || bridge_in_acpi) {
- clear_bit(slot, slot_hotplug_enable);
- }
- }
+ method = aml_method("_S1D", 0);
+ aml_append(method, aml_return(aml_int(0)));
+ aml_append(dev, method);
+
+ method = aml_method("_S2D", 0);
+ aml_append(method, aml_return(aml_int(0)));
+ aml_append(dev, method);
+
+ method = aml_method("_S3D", 0);
+ aml_append(method, aml_return(aml_int(s3d)));
+ aml_append(dev, method);
+ } else if (hotplug_enabled_dev) {
+ /* add _SUN/_EJ0 to make slot hotpluggable */
+ aml_append(dev, aml_name_decl("_SUN", aml_int(slot)));
+
+ method = aml_method("_EJ0", 1);
+ aml_append(method,
+ aml_call2("PCEJ", aml_name("BSEL"), aml_name("_SUN"))
+ );
+ aml_append(dev, method);
+
+ if (bsel) {
+ build_append_pcihp_notify_entry(notify_method, slot);
+ }
+ } else if (bridge_in_acpi) {
+ /*
+ * device is coldplugged bridge,
+ * add child device descriptions into its scope
+ */
+ PCIBus *sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(pdev));
- /* Append Device object for each slot */
- for (i = 0; i < PCI_SLOT_MAX; i++) {
- bool can_eject = test_bit(i, slot_hotplug_enable);
- bool present = test_bit(i, slot_device_present);
- bool vga = test_bit(i, slot_device_vga);
- bool qxl = test_bit(i, slot_device_qxl);
- bool system = test_bit(i, slot_device_system);
- if (can_eject) {
- void *pcihp = acpi_data_push(bus_table,
- ACPI_PCIHP_SIZEOF);
- memcpy(pcihp, ACPI_PCIHP_AML, ACPI_PCIHP_SIZEOF);
- patch_pcihp(i, pcihp);
- bus_hotplug_support = true;
- } else if (qxl) {
- void *pcihp = acpi_data_push(bus_table,
- ACPI_PCIQXL_SIZEOF);
- memcpy(pcihp, ACPI_PCIQXL_AML, ACPI_PCIQXL_SIZEOF);
- patch_pciqxl(i, pcihp);
- } else if (vga) {
- void *pcihp = acpi_data_push(bus_table,
- ACPI_PCIVGA_SIZEOF);
- memcpy(pcihp, ACPI_PCIVGA_AML, ACPI_PCIVGA_SIZEOF);
- patch_pcivga(i, pcihp);
- } else if (system) {
- /* Nothing to do: system devices are in DSDT or in SSDT above. */
- } else if (present) {
- void *pcihp = acpi_data_push(bus_table,
- ACPI_PCINOHP_SIZEOF);
- memcpy(pcihp, ACPI_PCINOHP_AML, ACPI_PCINOHP_SIZEOF);
- patch_pcinohp(i, pcihp);
+ build_append_pci_bus_devices(dev, sec_bus, pcihp_bridge_en);
}
+ /* slot descriptor has been composed, add it into parent context */
+ aml_append(parent_scope, dev);
}
if (bsel) {
- method = build_alloc_method("DVNT", 2);
-
- for (i = 0; i < PCI_SLOT_MAX; i++) {
- GArray *notify;
- uint8_t op;
-
- if (!test_bit(i, slot_hotplug_enable)) {
- continue;
- }
-
- notify = build_alloc_array();
- op = 0xA0; /* IfOp */
-
- build_append_byte(notify, 0x7B); /* AndOp */
- build_append_byte(notify, 0x68); /* Arg0Op */
- build_append_int(notify, 0x1U << i);
- build_append_byte(notify, 0x00); /* NullName */
- build_append_byte(notify, 0x86); /* NotifyOp */
- build_append_nameseg(notify, "S%.02X", PCI_DEVFN(i, 0));
- build_append_byte(notify, 0x69); /* Arg1Op */
-
- /* Pack it up */
- build_package(notify, op, 0);
-
- build_append_array(method, notify);
-
- build_free_array(notify);
- }
-
- build_append_and_cleanup_method(bus_table, method);
+ aml_append(parent_scope, notify_method);
}
/* Append PCNT method to notify about events on local and child buses.
* Add unconditionally for root since DSDT expects it.
*/
- if (bus_hotplug_support || child->notify_table->len || !bus->parent_dev) {
- method = build_alloc_method("PCNT", 0);
-
- /* If bus supports hotplug select it and notify about local events */
- if (bsel) {
- build_append_byte(method, 0x70); /* StoreOp */
- build_append_int(method, qint_get_int(qobject_to_qint(bsel)));
- build_append_nameseg(method, "BNUM");
- build_append_nameseg(method, "DVNT");
- build_append_nameseg(method, "PCIU");
- build_append_int(method, 1); /* Device Check */
- build_append_nameseg(method, "DVNT");
- build_append_nameseg(method, "PCID");
- build_append_int(method, 3); /* Eject Request */
- }
-
- /* Notify about child bus events in any case */
- build_append_array(method, child->notify_table);
-
- build_append_and_cleanup_method(bus_table, method);
+ method = aml_method("PCNT", 0);
- /* Append description of child buses */
- build_append_array(bus_table, child->device_table);
-
- /* Pack it up */
- if (bus->parent_dev) {
- build_extop_package(bus_table, op);
- } else {
- build_package(bus_table, op, 0);
- }
-
- /* Append our bus description to parent table */
- build_append_array(parent->device_table, bus_table);
-
- /* Also tell parent how to notify us, invoking PCNT method.
- * At the moment this is not needed for root as we have a single root.
- */
- if (bus->parent_dev) {
- build_append_byte(parent->notify_table, '^'); /* ParentPrefixChar */
- build_append_byte(parent->notify_table, 0x2E); /* DualNamePrefix */
- build_append_nameseg(parent->notify_table, "S%.02X",
- bus->parent_dev->devfn);
- build_append_nameseg(parent->notify_table, "PCNT");
- }
+ /* If bus supports hotplug select it and notify about local events */
+ if (bsel) {
+ int64_t bsel_val = qint_get_int(qobject_to_qint(bsel));
+ aml_append(method, aml_store(aml_int(bsel_val), aml_name("BNUM")));
+ aml_append(method,
+ aml_call2("DVNT", aml_name("PCIU"), aml_int(1) /* Device Check */)
+ );
+ aml_append(method,
+ aml_call2("DVNT", aml_name("PCID"), aml_int(3)/* Eject Request */)
+ );
}
- qobject_decref(bsel);
- build_free_array(bus_table);
- build_pci_bus_state_cleanup(child);
- g_free(child);
-}
-
-static void patch_pci_windows(PcPciInfo *pci, uint8_t *start, unsigned size)
-{
- ACPI_BUILD_SET_LE(start, size, acpi_pci32_start[0], 32, pci->w32.begin);
-
- ACPI_BUILD_SET_LE(start, size, acpi_pci32_end[0], 32, pci->w32.end - 1);
+ /* Notify about child bus events in any case */
+ if (pcihp_bridge_en) {
+ QLIST_FOREACH(sec, &bus->child, sibling) {
+ int32_t devfn = sec->parent_dev->devfn;
- if (pci->w64.end || pci->w64.begin) {
- ACPI_BUILD_SET_LE(start, size, acpi_pci64_valid[0], 8, 1);
- ACPI_BUILD_SET_LE(start, size, acpi_pci64_start[0], 64, pci->w64.begin);
- ACPI_BUILD_SET_LE(start, size, acpi_pci64_end[0], 64, pci->w64.end - 1);
- ACPI_BUILD_SET_LE(start, size, acpi_pci64_length[0], 64, pci->w64.end - pci->w64.begin);
- } else {
- ACPI_BUILD_SET_LE(start, size, acpi_pci64_valid[0], 8, 0);
+ aml_append(method, aml_name("^S%.02X.PCNT", devfn));
+ }
}
+ aml_append(parent_scope, method);
}
static void
@@ -1077,112 +658,337 @@ build_ssdt(GArray *table_data, GArray *linker,
MachineState *machine = MACHINE(qdev_get_machine());
uint32_t nr_mem = machine->ram_slots;
unsigned acpi_cpus = guest_info->apic_id_limit;
- int ssdt_start = table_data->len;
- uint8_t *ssdt_ptr;
+ Aml *ssdt, *sb_scope, *scope, *pkg, *dev, *method, *crs, *field, *ifctx;
int i;
+ ssdt = init_aml_allocator();
/* The current AML generator can cover the APIC ID range [0..255],
* inclusive, for VCPU hotplug. */
QEMU_BUILD_BUG_ON(ACPI_CPU_HOTPLUG_ID_LIMIT > 256);
g_assert(acpi_cpus <= ACPI_CPU_HOTPLUG_ID_LIMIT);
- /* Copy header and patch values in the S3_ / S4_ / S5_ packages */
- ssdt_ptr = acpi_data_push(table_data, sizeof(ssdp_misc_aml));
- memcpy(ssdt_ptr, ssdp_misc_aml, sizeof(ssdp_misc_aml));
- if (pm->s3_disabled) {
- ssdt_ptr[acpi_s3_name[0]] = 'X';
+ /* Reserve space for header */
+ acpi_data_push(ssdt->buf, sizeof(AcpiTableHeader));
+
+ scope = aml_scope("\\_SB.PCI0");
+ /* build PCI0._CRS */
+ crs = aml_resource_template();
+ aml_append(crs,
+ aml_word_bus_number(aml_min_fixed, aml_max_fixed, aml_pos_decode,
+ 0x0000, 0x0000, 0x00FF, 0x0000, 0x0100));
+ aml_append(crs, aml_io(aml_decode16, 0x0CF8, 0x0CF8, 0x01, 0x08));
+
+ aml_append(crs,
+ aml_word_io(aml_min_fixed, aml_max_fixed,
+ aml_pos_decode, aml_entire_range,
+ 0x0000, 0x0000, 0x0CF7, 0x0000, 0x0CF8));
+ aml_append(crs,
+ aml_word_io(aml_min_fixed, aml_max_fixed,
+ aml_pos_decode, aml_entire_range,
+ 0x0000, 0x0D00, 0xFFFF, 0x0000, 0xF300));
+ aml_append(crs,
+ aml_dword_memory(aml_pos_decode, aml_min_fixed, aml_max_fixed,
+ aml_cacheable, aml_ReadWrite,
+ 0, 0x000A0000, 0x000BFFFF, 0, 0x00020000));
+ aml_append(crs,
+ aml_dword_memory(aml_pos_decode, aml_min_fixed, aml_max_fixed,
+ aml_non_cacheable, aml_ReadWrite,
+ 0, pci->w32.begin, pci->w32.end - 1, 0,
+ pci->w32.end - pci->w32.begin));
+ if (pci->w64.begin) {
+ aml_append(crs,
+ aml_qword_memory(aml_pos_decode, aml_min_fixed, aml_max_fixed,
+ aml_cacheable, aml_ReadWrite,
+ 0, pci->w64.begin, pci->w64.end - 1, 0,
+ pci->w64.end - pci->w64.begin));
}
- if (pm->s4_disabled) {
- ssdt_ptr[acpi_s4_name[0]] = 'X';
- } else {
- ssdt_ptr[acpi_s4_pkg[0] + 1] = ssdt_ptr[acpi_s4_pkg[0] + 3] =
- pm->s4_val;
+ aml_append(scope, aml_name_decl("_CRS", crs));
+
+ /* reserve GPE0 block resources */
+ dev = aml_device("GPE0");
+ aml_append(dev, aml_name_decl("_HID", aml_string("PNP0A06")));
+ aml_append(dev, aml_name_decl("_UID", aml_string("GPE0 resources")));
+ /* device present, functioning, decoding, not shown in UI */
+ aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
+ crs = aml_resource_template();
+ aml_append(crs,
+ aml_io(aml_decode16, pm->gpe0_blk, pm->gpe0_blk, 1, pm->gpe0_blk_len)
+ );
+ aml_append(dev, aml_name_decl("_CRS", crs));
+ aml_append(scope, dev);
+
+ /* reserve PCIHP resources */
+ if (pm->pcihp_io_len) {
+ dev = aml_device("PHPR");
+ aml_append(dev, aml_name_decl("_HID", aml_string("PNP0A06")));
+ aml_append(dev,
+ aml_name_decl("_UID", aml_string("PCI Hotplug resources")));
+ /* device present, functioning, decoding, not shown in UI */
+ aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
+ crs = aml_resource_template();
+ aml_append(crs,
+ aml_io(aml_decode16, pm->pcihp_io_base, pm->pcihp_io_base, 1,
+ pm->pcihp_io_len)
+ );
+ aml_append(dev, aml_name_decl("_CRS", crs));
+ aml_append(scope, dev);
+ }
+ aml_append(ssdt, scope);
+
+ /* create S3_ / S4_ / S5_ packages if necessary */
+ scope = aml_scope("\\");
+ if (!pm->s3_disabled) {
+ pkg = aml_package(4);
+ aml_append(pkg, aml_int(1)); /* PM1a_CNT.SLP_TYP */
+ aml_append(pkg, aml_int(1)); /* PM1b_CNT.SLP_TYP, FIXME: not impl. */
+ aml_append(pkg, aml_int(0)); /* reserved */
+ aml_append(pkg, aml_int(0)); /* reserved */
+ aml_append(scope, aml_name_decl("_S3", pkg));
}
- patch_pci_windows(pci, ssdt_ptr, sizeof(ssdp_misc_aml));
+ if (!pm->s4_disabled) {
+ pkg = aml_package(4);
+ aml_append(pkg, aml_int(pm->s4_val)); /* PM1a_CNT.SLP_TYP */
+ /* PM1b_CNT.SLP_TYP, FIXME: not impl. */
+ aml_append(pkg, aml_int(pm->s4_val));
+ aml_append(pkg, aml_int(0)); /* reserved */
+ aml_append(pkg, aml_int(0)); /* reserved */
+ aml_append(scope, aml_name_decl("_S4", pkg));
+ }
- ACPI_BUILD_SET_LE(ssdt_ptr, sizeof(ssdp_misc_aml),
- ssdt_isa_pest[0], 16, misc->pvpanic_port);
+ pkg = aml_package(4);
+ aml_append(pkg, aml_int(0)); /* PM1a_CNT.SLP_TYP */
+ aml_append(pkg, aml_int(0)); /* PM1b_CNT.SLP_TYP not impl. */
+ aml_append(pkg, aml_int(0)); /* reserved */
+ aml_append(pkg, aml_int(0)); /* reserved */
+ aml_append(scope, aml_name_decl("_S5", pkg));
+ aml_append(ssdt, scope);
+
+ if (misc->applesmc_io_base) {
+ scope = aml_scope("\\_SB.PCI0.ISA");
+ dev = aml_device("SMC");
+
+ aml_append(dev, aml_name_decl("_HID", aml_eisaid("APP0001")));
+ /* device present, functioning, decoding, not shown in UI */
+ aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
+
+ crs = aml_resource_template();
+ aml_append(crs,
+ aml_io(aml_decode16, misc->applesmc_io_base, misc->applesmc_io_base,
+ 0x01, APPLESMC_MAX_DATA_LENGTH)
+ );
+ aml_append(crs, aml_irq_no_flags(6));
+ aml_append(dev, aml_name_decl("_CRS", crs));
+
+ aml_append(scope, dev);
+ aml_append(ssdt, scope);
+ }
- ACPI_BUILD_SET_LE(ssdt_ptr, sizeof(ssdp_misc_aml),
- ssdt_mctrl_nr_slots[0], 32, nr_mem);
+ if (misc->pvpanic_port) {
+ scope = aml_scope("\\_SB.PCI0.ISA");
- {
- GArray *sb_scope = build_alloc_array();
- uint8_t op = 0x10; /* ScopeOp */
+ dev = aml_device("PEVR");
+ aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0002")));
+
+ crs = aml_resource_template();
+ aml_append(crs,
+ aml_io(aml_decode16, misc->pvpanic_port, misc->pvpanic_port, 1, 1)
+ );
+ aml_append(dev, aml_name_decl("_CRS", crs));
- build_append_nameseg(sb_scope, "_SB");
+ aml_append(dev, aml_operation_region("PEOR", aml_system_io,
+ misc->pvpanic_port, 1));
+ field = aml_field("PEOR", aml_byte_acc);
+ aml_append(field, aml_named_field("PEPT", 8));
+ aml_append(dev, field);
+
+ method = aml_method("RDPT", 0);
+ aml_append(method, aml_store(aml_name("PEPT"), aml_local(0)));
+ aml_append(method, aml_return(aml_local(0)));
+ aml_append(dev, method);
+
+ method = aml_method("WRPT", 1);
+ aml_append(method, aml_store(aml_arg(0), aml_name("PEPT")));
+ aml_append(dev, method);
+
+ aml_append(scope, dev);
+ aml_append(ssdt, scope);
+ }
+
+ sb_scope = aml_scope("_SB");
+ {
+ /* create PCI0.PRES device and its _CRS to reserve CPU hotplug MMIO */
+ dev = aml_device("PCI0." stringify(CPU_HOTPLUG_RESOURCE_DEVICE));
+ aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A06")));
+ aml_append(dev,
+ aml_name_decl("_UID", aml_string("CPU Hotplug resources"))
+ );
+ /* device present, functioning, decoding, not shown in UI */
+ aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
+ crs = aml_resource_template();
+ aml_append(crs,
+ aml_io(aml_decode16, pm->cpu_hp_io_base, pm->cpu_hp_io_base, 1,
+ pm->cpu_hp_io_len)
+ );
+ aml_append(dev, aml_name_decl("_CRS", crs));
+ aml_append(sb_scope, dev);
+ /* declare CPU hotplug MMIO region and PRS field to access it */
+ aml_append(sb_scope, aml_operation_region(
+ "PRST", aml_system_io, pm->cpu_hp_io_base, pm->cpu_hp_io_len));
+ field = aml_field("PRST", aml_byte_acc);
+ aml_append(field, aml_named_field("PRS", 256));
+ aml_append(sb_scope, field);
/* build Processor object for each processor */
for (i = 0; i < acpi_cpus; i++) {
- uint8_t *proc = acpi_data_push(sb_scope, ACPI_PROC_SIZEOF);
- memcpy(proc, ACPI_PROC_AML, ACPI_PROC_SIZEOF);
- proc[ACPI_PROC_OFFSET_CPUHEX] = acpi_get_hex(i >> 4);
- proc[ACPI_PROC_OFFSET_CPUHEX+1] = acpi_get_hex(i);
- proc[ACPI_PROC_OFFSET_CPUID1] = i;
- proc[ACPI_PROC_OFFSET_CPUID2] = i;
+ dev = aml_processor(i, 0, 0, "CP%.02X", i);
+
+ method = aml_method("_MAT", 0);
+ aml_append(method, aml_return(aml_call1("CPMA", aml_int(i))));
+ aml_append(dev, method);
+
+ method = aml_method("_STA", 0);
+ aml_append(method, aml_return(aml_call1("CPST", aml_int(i))));
+ aml_append(dev, method);
+
+ method = aml_method("_EJ0", 1);
+ aml_append(method,
+ aml_return(aml_call2("CPEJ", aml_int(i), aml_arg(0)))
+ );
+ aml_append(dev, method);
+
+ aml_append(sb_scope, dev);
}
/* build this code:
* Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...}
*/
/* Arg0 = Processor ID = APIC ID */
- build_append_notify_method(sb_scope, "NTFY", "CP%0.02X", acpi_cpus);
-
- /* build "Name(CPON, Package() { One, One, ..., Zero, Zero, ... })" */
- build_append_byte(sb_scope, 0x08); /* NameOp */
- build_append_nameseg(sb_scope, "CPON");
-
- {
- GArray *package = build_alloc_array();
- uint8_t op;
-
- /*
- * Note: The ability to create variable-sized packages was first introduced in ACPI 2.0. ACPI 1.0 only
- * allowed fixed-size packages with up to 255 elements.
- * Windows guests up to win2k8 fail when VarPackageOp is used.
- */
- if (acpi_cpus <= 255) {
- op = 0x12; /* PackageOp */
- build_append_byte(package, acpi_cpus); /* NumElements */
- } else {
- op = 0x13; /* VarPackageOp */
- build_append_int(package, acpi_cpus); /* VarNumElements */
- }
-
- for (i = 0; i < acpi_cpus; i++) {
- uint8_t b = test_bit(i, cpu->found_cpus) ? 0x01 : 0x00;
- build_append_byte(package, b);
- }
-
- build_package(package, op, 2);
- build_append_array(sb_scope, package);
- build_free_array(package);
+ method = aml_method("NTFY", 2);
+ for (i = 0; i < acpi_cpus; i++) {
+ ifctx = aml_if(aml_equal(aml_arg(0), aml_int(i)));
+ aml_append(ifctx,
+ aml_notify(aml_name("CP%.02X", i), aml_arg(1))
+ );
+ aml_append(method, ifctx);
}
+ aml_append(sb_scope, method);
+
+ /* build "Name(CPON, Package() { One, One, ..., Zero, Zero, ... })"
+ *
+ * Note: The ability to create variable-sized packages was first
+ * introduced in ACPI 2.0. ACPI 1.0 only allowed fixed-size packages
+ * ith up to 255 elements. Windows guests up to win2k8 fail when
+ * VarPackageOp is used.
+ */
+ pkg = acpi_cpus <= 255 ? aml_package(acpi_cpus) :
+ aml_varpackage(acpi_cpus);
- if (nr_mem) {
- assert(nr_mem <= ACPI_MAX_RAM_SLOTS);
- /* build memory devices */
- for (i = 0; i < nr_mem; i++) {
- char id[3];
- uint8_t *mem = acpi_data_push(sb_scope, ACPI_MEM_SIZEOF);
-
- snprintf(id, sizeof(id), "%02X", i);
- memcpy(mem, ACPI_MEM_AML, ACPI_MEM_SIZEOF);
- memcpy(mem + ACPI_MEM_OFFSET_HEX, id, 2);
- memcpy(mem + ACPI_MEM_OFFSET_ID, id, 2);
- }
+ for (i = 0; i < acpi_cpus; i++) {
+ uint8_t b = test_bit(i, cpu->found_cpus) ? 0x01 : 0x00;
+ aml_append(pkg, aml_int(b));
+ }
+ aml_append(sb_scope, aml_name_decl("CPON", pkg));
+
+ /* build memory devices */
+ assert(nr_mem <= ACPI_MAX_RAM_SLOTS);
+ scope = aml_scope("\\_SB.PCI0." stringify(MEMORY_HOTPLUG_DEVICE));
+ aml_append(scope,
+ aml_name_decl(stringify(MEMORY_SLOTS_NUMBER), aml_int(nr_mem))
+ );
+
+ crs = aml_resource_template();
+ aml_append(crs,
+ aml_io(aml_decode16, pm->mem_hp_io_base, pm->mem_hp_io_base, 0,
+ pm->mem_hp_io_len)
+ );
+ aml_append(scope, aml_name_decl("_CRS", crs));
+
+ aml_append(scope, aml_operation_region(
+ stringify(MEMORY_HOTPLUG_IO_REGION), aml_system_io,
+ pm->mem_hp_io_base, pm->mem_hp_io_len)
+ );
+
+ field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), aml_dword_acc);
+ aml_append(field, /* read only */
+ aml_named_field(stringify(MEMORY_SLOT_ADDR_LOW), 32));
+ aml_append(field, /* read only */
+ aml_named_field(stringify(MEMORY_SLOT_ADDR_HIGH), 32));
+ aml_append(field, /* read only */
+ aml_named_field(stringify(MEMORY_SLOT_SIZE_LOW), 32));
+ aml_append(field, /* read only */
+ aml_named_field(stringify(MEMORY_SLOT_SIZE_HIGH), 32));
+ aml_append(field, /* read only */
+ aml_named_field(stringify(MEMORY_SLOT_PROXIMITY), 32));
+ aml_append(scope, field);
+
+ field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), aml_byte_acc);
+ aml_append(field, aml_reserved_field(160 /* bits, Offset(20) */));
+ aml_append(field, /* 1 if enabled, read only */
+ aml_named_field(stringify(MEMORY_SLOT_ENABLED), 1));
+ aml_append(field,
+ /*(read) 1 if has a insert event. (write) 1 to clear event */
+ aml_named_field(stringify(MEMORY_SLOT_INSERT_EVENT), 1));
+ aml_append(scope, field);
+
+ field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), aml_dword_acc);
+ aml_append(field, /* DIMM selector, write only */
+ aml_named_field(stringify(MEMORY_SLOT_SLECTOR), 32));
+ aml_append(field, /* _OST event code, write only */
+ aml_named_field(stringify(MEMORY_SLOT_OST_EVENT), 32));
+ aml_append(field, /* _OST status code, write only */
+ aml_named_field(stringify(MEMORY_SLOT_OST_STATUS), 32));
+ aml_append(scope, field);
+
+ aml_append(sb_scope, scope);
+
+ for (i = 0; i < nr_mem; i++) {
+ #define BASEPATH "\\_SB.PCI0." stringify(MEMORY_HOTPLUG_DEVICE) "."
+ const char *s;
+
+ dev = aml_device("MP%02X", i);
+ aml_append(dev, aml_name_decl("_UID", aml_string("0x%02X", i)));
+ aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C80")));
+
+ method = aml_method("_CRS", 0);
+ s = BASEPATH stringify(MEMORY_SLOT_CRS_METHOD);
+ aml_append(method, aml_return(aml_call1(s, aml_name("_UID"))));
+ aml_append(dev, method);
+
+ method = aml_method("_STA", 0);
+ s = BASEPATH stringify(MEMORY_SLOT_STATUS_METHOD);
+ aml_append(method, aml_return(aml_call1(s, aml_name("_UID"))));
+ aml_append(dev, method);
+
+ method = aml_method("_PXM", 0);
+ s = BASEPATH stringify(MEMORY_SLOT_PROXIMITY_METHOD);
+ aml_append(method, aml_return(aml_call1(s, aml_name("_UID"))));
+ aml_append(dev, method);
+
+ method = aml_method("_OST", 3);
+ s = BASEPATH stringify(MEMORY_SLOT_OST_METHOD);
+ aml_append(method, aml_return(aml_call4(
+ s, aml_name("_UID"), aml_arg(0), aml_arg(1), aml_arg(2)
+ )));
+ aml_append(dev, method);
+
+ aml_append(sb_scope, dev);
+ }
- /* build Method(MEMORY_SLOT_NOTIFY_METHOD, 2) {
- * If (LEqual(Arg0, 0x00)) {Notify(MP00, Arg1)} ...
- */
- build_append_notify_method(sb_scope,
- stringify(MEMORY_SLOT_NOTIFY_METHOD),
- "MP%0.02X", nr_mem);
+ /* build Method(MEMORY_SLOT_NOTIFY_METHOD, 2) {
+ * If (LEqual(Arg0, 0x00)) {Notify(MP00, Arg1)} ...
+ */
+ method = aml_method(stringify(MEMORY_SLOT_NOTIFY_METHOD), 2);
+ for (i = 0; i < nr_mem; i++) {
+ ifctx = aml_if(aml_equal(aml_arg(0), aml_int(i)));
+ aml_append(ifctx,
+ aml_notify(aml_name("MP%.02X", i), aml_arg(1))
+ );
+ aml_append(method, ifctx);
}
+ aml_append(sb_scope, method);
{
- AcpiBuildPciBusHotplugState hotplug_state;
Object *pci_host;
PCIBus *bus = NULL;
bool ambiguous;
@@ -1192,26 +998,22 @@ build_ssdt(GArray *table_data, GArray *linker,
bus = PCI_HOST_BRIDGE(pci_host)->bus;
}
- build_pci_bus_state_init(&hotplug_state, NULL, pm->pcihp_bridge_en);
-
if (bus) {
+ Aml *scope = aml_scope("PCI0");
/* Scan all PCI buses. Generate tables to support hotplug. */
- pci_for_each_bus_depth_first(bus, build_pci_bus_begin,
- build_pci_bus_end, &hotplug_state);
+ build_append_pci_bus_devices(scope, bus, pm->pcihp_bridge_en);
+ aml_append(sb_scope, scope);
}
-
- build_append_array(sb_scope, hotplug_state.device_table);
- build_pci_bus_state_cleanup(&hotplug_state);
}
-
- build_package(sb_scope, op, 3);
- build_append_array(table_data, sb_scope);
- build_free_array(sb_scope);
+ aml_append(ssdt, sb_scope);
}
+ /* copy AML table into ACPI tables blob and patch header there */
+ g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len);
build_header(linker, table_data,
- (void *)(table_data->data + ssdt_start),
- "SSDT", table_data->len - ssdt_start, 1);
+ (void *)(table_data->data + table_data->len - ssdt->buf->len),
+ "SSDT", ssdt->buf->len, 1);
+ free_aml_allocator();
}
static void
@@ -1513,7 +1315,7 @@ static inline void acpi_build_tables_cleanup(AcpiBuildTables *tables, bool mfre)
{
void *linker_data = bios_linker_loader_cleanup(tables->linker);
g_free(linker_data);
- g_array_free(tables->rsdp, mfre);
+ g_array_free(tables->rsdp, true);
g_array_free(tables->table_data, true);
g_array_free(tables->tcpalog, mfre);
}
@@ -1522,10 +1324,12 @@ typedef
struct AcpiBuildState {
/* Copy of table in RAM (for patching). */
ram_addr_t table_ram;
- uint32_t table_size;
/* Is table patched? */
uint8_t patched;
PcGuestInfo *guest_info;
+ void *rsdp;
+ ram_addr_t rsdp_ram;
+ ram_addr_t linker_ram;
} AcpiBuildState;
static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg)
@@ -1574,6 +1378,7 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables)
PcPciInfo pci;
uint8_t *u;
size_t aml_len = 0;
+ GArray *tables_blob = tables->table_data;
acpi_get_cpu_info(&cpu);
acpi_get_pm_info(&pm);
@@ -1594,74 +1399,72 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables)
* We place it first since it's the only table that has alignment
* requirements.
*/
- facs = tables->table_data->len;
- build_facs(tables->table_data, tables->linker, guest_info);
+ facs = tables_blob->len;
+ build_facs(tables_blob, tables->linker, guest_info);
/* DSDT is pointed to by FADT */
- dsdt = tables->table_data->len;
- build_dsdt(tables->table_data, tables->linker, &misc);
+ dsdt = tables_blob->len;
+ build_dsdt(tables_blob, tables->linker, &misc);
/* Count the size of the DSDT and SSDT, we will need it for legacy
* sizing of ACPI tables.
*/
- aml_len += tables->table_data->len - dsdt;
+ aml_len += tables_blob->len - dsdt;
/* ACPI tables pointed to by RSDT */
- acpi_add_table(table_offsets, tables->table_data);
- build_fadt(tables->table_data, tables->linker, &pm, facs, dsdt);
+ acpi_add_table(table_offsets, tables_blob);
+ build_fadt(tables_blob, tables->linker, &pm, facs, dsdt);
- ssdt = tables->table_data->len;
- acpi_add_table(table_offsets, tables->table_data);
- build_ssdt(tables->table_data, tables->linker, &cpu, &pm, &misc, &pci,
+ ssdt = tables_blob->len;
+ acpi_add_table(table_offsets, tables_blob);
+ build_ssdt(tables_blob, tables->linker, &cpu, &pm, &misc, &pci,
guest_info);
- aml_len += tables->table_data->len - ssdt;
+ aml_len += tables_blob->len - ssdt;
- acpi_add_table(table_offsets, tables->table_data);
- build_madt(tables->table_data, tables->linker, &cpu, guest_info);
+ acpi_add_table(table_offsets, tables_blob);
+ build_madt(tables_blob, tables->linker, &cpu, guest_info);
if (misc.has_hpet) {
- acpi_add_table(table_offsets, tables->table_data);
- build_hpet(tables->table_data, tables->linker);
+ acpi_add_table(table_offsets, tables_blob);
+ build_hpet(tables_blob, tables->linker);
}
if (misc.has_tpm) {
- acpi_add_table(table_offsets, tables->table_data);
- build_tpm_tcpa(tables->table_data, tables->linker, tables->tcpalog);
+ acpi_add_table(table_offsets, tables_blob);
+ build_tpm_tcpa(tables_blob, tables->linker, tables->tcpalog);
- acpi_add_table(table_offsets, tables->table_data);
- build_tpm_ssdt(tables->table_data, tables->linker);
+ acpi_add_table(table_offsets, tables_blob);
+ build_tpm_ssdt(tables_blob, tables->linker);
}
if (guest_info->numa_nodes) {
- acpi_add_table(table_offsets, tables->table_data);
- build_srat(tables->table_data, tables->linker, guest_info);
+ acpi_add_table(table_offsets, tables_blob);
+ build_srat(tables_blob, tables->linker, guest_info);
}
if (acpi_get_mcfg(&mcfg)) {
- acpi_add_table(table_offsets, tables->table_data);
- build_mcfg_q35(tables->table_data, tables->linker, &mcfg);
+ acpi_add_table(table_offsets, tables_blob);
+ build_mcfg_q35(tables_blob, tables->linker, &mcfg);
}
if (acpi_has_iommu()) {
- acpi_add_table(table_offsets, tables->table_data);
- build_dmar_q35(tables->table_data, tables->linker);
+ acpi_add_table(table_offsets, tables_blob);
+ build_dmar_q35(tables_blob, tables->linker);
}
/* Add tables supplied by user (if any) */
for (u = acpi_table_first(); u; u = acpi_table_next(u)) {
unsigned len = acpi_table_len(u);
- acpi_add_table(table_offsets, tables->table_data);
- g_array_append_vals(tables->table_data, u, len);
+ acpi_add_table(table_offsets, tables_blob);
+ g_array_append_vals(tables_blob, u, len);
}
/* RSDT is pointed to by RSDP */
- rsdt = tables->table_data->len;
- build_rsdt(tables->table_data, tables->linker, table_offsets);
+ rsdt = tables_blob->len;
+ build_rsdt(tables_blob, tables->linker, table_offsets);
/* RSDP is in FSEG memory, so allocate it separately */
build_rsdp(tables->rsdp, tables->linker, rsdt);
/* We'll expose it all to Guest so we want to reduce
* chance of size changes.
- * RSDP is small so it's easy to keep it immutable, no need to
- * bother with alignment.
*
* We used to align the tables to 4k, but of course this would
* too simple to be enough. 4k turned out to be too small an
@@ -1685,23 +1488,23 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables)
guest_info->legacy_acpi_table_size +
ACPI_BUILD_LEGACY_CPU_AML_SIZE * max_cpus;
int legacy_table_size =
- ROUND_UP(tables->table_data->len - aml_len + legacy_aml_len,
+ ROUND_UP(tables_blob->len - aml_len + legacy_aml_len,
ACPI_BUILD_ALIGN_SIZE);
- if (tables->table_data->len > legacy_table_size) {
+ if (tables_blob->len > legacy_table_size) {
/* Should happen only with PCI bridges and -M pc-i440fx-2.0. */
error_report("Warning: migration may not work.");
}
- g_array_set_size(tables->table_data, legacy_table_size);
+ g_array_set_size(tables_blob, legacy_table_size);
} else {
/* Make sure we have a buffer in case we need to resize the tables. */
- if (tables->table_data->len > ACPI_BUILD_TABLE_SIZE / 2) {
+ if (tables_blob->len > ACPI_BUILD_TABLE_SIZE / 2) {
/* As of QEMU 2.1, this fires with 160 VCPUs and 255 memory slots. */
error_report("Warning: ACPI tables are larger than 64k.");
error_report("Warning: migration may not work.");
error_report("Warning: please remove CPUs, NUMA nodes, "
"memory slots or PCI bridges.");
}
- acpi_align_size(tables->table_data, ACPI_BUILD_TABLE_SIZE);
+ acpi_align_size(tables_blob, ACPI_BUILD_TABLE_SIZE);
}
acpi_align_size(tables->linker, ACPI_BUILD_ALIGN_SIZE);
@@ -1710,6 +1513,17 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables)
g_array_free(table_offsets, true);
}
+static void acpi_ram_update(ram_addr_t ram, GArray *data)
+{
+ uint32_t size = acpi_data_len(data);
+
+ /* Make sure RAM size is correct - in case it got changed e.g. by migration */
+ qemu_ram_resize(ram, size, &error_abort);
+
+ memcpy(qemu_get_ram_ptr(ram), data->data, size);
+ cpu_physical_memory_set_dirty_range_nocode(ram, size);
+}
+
static void acpi_build_update(void *build_opaque, uint32_t offset)
{
AcpiBuildState *build_state = build_opaque;
@@ -1725,18 +1539,15 @@ static void acpi_build_update(void *build_opaque, uint32_t offset)
acpi_build(build_state->guest_info, &tables);
- assert(acpi_data_len(tables.table_data) == build_state->table_size);
-
- /* Make sure RAM size is correct - in case it got changed by migration */
- qemu_ram_resize(build_state->table_ram, build_state->table_size,
- &error_abort);
+ acpi_ram_update(build_state->table_ram, tables.table_data);
- memcpy(qemu_get_ram_ptr(build_state->table_ram), tables.table_data->data,
- build_state->table_size);
-
- cpu_physical_memory_set_dirty_range_nocode(build_state->table_ram,
- build_state->table_size);
+ if (build_state->rsdp) {
+ memcpy(build_state->rsdp, tables.rsdp->data, acpi_data_len(tables.rsdp));
+ } else {
+ acpi_ram_update(build_state->rsdp_ram, tables.rsdp);
+ }
+ acpi_ram_update(build_state->linker_ram, tables.linker);
acpi_build_tables_cleanup(&tables, true);
}
@@ -1797,19 +1608,31 @@ void acpi_setup(PcGuestInfo *guest_info)
ACPI_BUILD_TABLE_FILE,
ACPI_BUILD_TABLE_MAX_SIZE);
assert(build_state->table_ram != RAM_ADDR_MAX);
- build_state->table_size = acpi_data_len(tables.table_data);
- acpi_add_rom_blob(NULL, tables.linker, "etc/table-loader", 0);
+ build_state->linker_ram =
+ acpi_add_rom_blob(build_state, tables.linker, "etc/table-loader", 0);
fw_cfg_add_file(guest_info->fw_cfg, ACPI_BUILD_TPMLOG_FILE,
tables.tcpalog->data, acpi_data_len(tables.tcpalog));
- /*
- * RSDP is small so it's easy to keep it immutable, no need to
- * bother with ROM blobs.
- */
- fw_cfg_add_file(guest_info->fw_cfg, ACPI_BUILD_RSDP_FILE,
- tables.rsdp->data, acpi_data_len(tables.rsdp));
+ if (!guest_info->rsdp_in_ram) {
+ /*
+ * Keep for compatibility with old machine types.
+ * Though RSDP is small, its contents isn't immutable, so
+ * we'll update it along with the rest of tables on guest access.
+ */
+ uint32_t rsdp_size = acpi_data_len(tables.rsdp);
+
+ build_state->rsdp = g_memdup(tables.rsdp->data, rsdp_size);
+ fw_cfg_add_file_callback(guest_info->fw_cfg, ACPI_BUILD_RSDP_FILE,
+ acpi_build_update, build_state,
+ build_state->rsdp, rsdp_size);
+ build_state->rsdp_ram = (ram_addr_t)-1;
+ } else {
+ build_state->rsdp = NULL;
+ build_state->rsdp_ram = acpi_add_rom_blob(build_state, tables.rsdp,
+ ACPI_BUILD_RSDP_FILE, 0);
+ }
qemu_register_reset(acpi_build_reset, build_state);
acpi_build_reset(build_state);