aboutsummaryrefslogtreecommitdiff
path: root/hw/acpi
diff options
context:
space:
mode:
Diffstat (limited to 'hw/acpi')
-rw-r--r--hw/acpi/Kconfig28
-rw-r--r--hw/acpi/acpi-pci-hotplug-stub.c9
-rw-r--r--hw/acpi/acpi-qmp-cmds.c30
-rw-r--r--hw/acpi/acpi-stub.c4
-rw-r--r--hw/acpi/acpi-x86-stub.c6
-rw-r--r--hw/acpi/acpi_generic_initiator.c148
-rw-r--r--hw/acpi/acpi_interface.c18
-rw-r--r--hw/acpi/aml-build-stub.c10
-rw-r--r--hw/acpi/aml-build.c391
-rw-r--r--hw/acpi/core.c79
-rw-r--r--hw/acpi/cpu.c30
-rw-r--r--hw/acpi/cpu_hotplug.c52
-rw-r--r--hw/acpi/cxl-stub.c12
-rw-r--r--hw/acpi/cxl.c326
-rw-r--r--hw/acpi/erst.c1059
-rw-r--r--hw/acpi/generic_event_device.c20
-rw-r--r--hw/acpi/ghes.c12
-rw-r--r--hw/acpi/hmat.c34
-rw-r--r--hw/acpi/hmat.h3
-rw-r--r--hw/acpi/ich9.c72
-rw-r--r--hw/acpi/ich9_tco.c (renamed from hw/acpi/tco.c)48
-rw-r--r--hw/acpi/ipmi-stub.c2
-rw-r--r--hw/acpi/ipmi.c53
-rw-r--r--hw/acpi/memory_hotplug.c20
-rw-r--r--hw/acpi/meson.build21
-rw-r--r--hw/acpi/nvdimm.c232
-rw-r--r--hw/acpi/pci-bridge-stub.c20
-rw-r--r--hw/acpi/pci-bridge.c37
-rw-r--r--hw/acpi/pci.c18
-rw-r--r--hw/acpi/pcihp.c223
-rw-r--r--hw/acpi/piix4.c140
-rw-r--r--hw/acpi/trace-events40
-rw-r--r--hw/acpi/viot.c143
-rw-r--r--hw/acpi/viot.h13
-rw-r--r--hw/acpi/vmgenid.c33
35 files changed, 2763 insertions, 623 deletions
diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
index 3b5e118c54..e07d3204eb 100644
--- a/hw/acpi/Kconfig
+++ b/hw/acpi/Kconfig
@@ -5,15 +5,18 @@ config ACPI_X86
bool
select ACPI
select ACPI_NVDIMM
+ select ACPI_CXL
select ACPI_CPU_HOTPLUG
select ACPI_MEMORY_HOTPLUG
select ACPI_HMAT
- select ACPI_PIIX4
select ACPI_PCIHP
+ select ACPI_ERST
-config ACPI_X86_ICH
+config ACPI_ICH9
bool
+ select ACPI_SMBUS
select ACPI_X86
+ select APM
config ACPI_CPU_HOTPLUG
bool
@@ -28,12 +31,18 @@ config ACPI_NVDIMM
config ACPI_PIIX4
bool
- depends on ACPI
+ select ACPI
+ select ACPI_SMBUS
+ select APM
config ACPI_PCIHP
bool
depends on ACPI
+config ACPI_PCI_BRIDGE
+ bool
+ depends on ACPI && PCI && ACPI_PCIHP
+
config ACPI_HMAT
bool
depends on ACPI
@@ -51,8 +60,21 @@ config ACPI_VMGENID
default y
depends on PC
+config ACPI_VIOT
+ bool
+ depends on ACPI
+
config ACPI_HW_REDUCED
bool
select ACPI
select ACPI_MEMORY_HOTPLUG
select ACPI_NVDIMM
+
+config ACPI_ERST
+ bool
+ default y
+ depends on ACPI && PCI
+
+config ACPI_CXL
+ bool
+ depends on ACPI
diff --git a/hw/acpi/acpi-pci-hotplug-stub.c b/hw/acpi/acpi-pci-hotplug-stub.c
index 734e4c5986..dcee3ad7a1 100644
--- a/hw/acpi/acpi-pci-hotplug-stub.c
+++ b/hw/acpi/acpi-pci-hotplug-stub.c
@@ -5,8 +5,7 @@
const VMStateDescription vmstate_acpi_pcihp_pci_status;
void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus,
- MemoryRegion *address_space_io, bool bridges_enabled,
- uint16_t io_base)
+ MemoryRegion *address_space_io, uint16_t io_base)
{
return;
}
@@ -36,12 +35,12 @@ void acpi_pcihp_device_unplug_request_cb(HotplugHandler *hotplug_dev,
return;
}
-void acpi_pcihp_reset(AcpiPciHpState *s, bool acpihp_root_off)
+void acpi_pcihp_reset(AcpiPciHpState *s)
{
return;
}
-bool vmstate_acpi_pcihp_use_acpi_index(void *opaque, int version_id)
+bool acpi_pcihp_is_hotpluggbale_bus(AcpiPciHpState *s, BusState *bus)
{
- return false;
+ return true;
}
diff --git a/hw/acpi/acpi-qmp-cmds.c b/hw/acpi/acpi-qmp-cmds.c
new file mode 100644
index 0000000000..2d47cac52c
--- /dev/null
+++ b/hw/acpi/acpi-qmp-cmds.c
@@ -0,0 +1,30 @@
+/*
+ * QMP commands related to ACPI
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/acpi/acpi_dev_interface.h"
+#include "qapi/error.h"
+#include "qapi/qapi-commands-acpi.h"
+
+ACPIOSTInfoList *qmp_query_acpi_ospm_status(Error **errp)
+{
+ bool ambig;
+ ACPIOSTInfoList *head = NULL;
+ ACPIOSTInfoList **prev = &head;
+ Object *obj = object_resolve_path_type("", TYPE_ACPI_DEVICE_IF, &ambig);
+
+ if (obj) {
+ AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(obj);
+ AcpiDeviceIf *adev = ACPI_DEVICE_IF(obj);
+
+ adevc->ospm_status(adev, &prev);
+ } else {
+ error_setg(errp, "command is not supported, missing ACPI device");
+ }
+
+ return head;
+}
diff --git a/hw/acpi/acpi-stub.c b/hw/acpi/acpi-stub.c
index 4c9d081ed4..e268ce9b1a 100644
--- a/hw/acpi/acpi-stub.c
+++ b/hw/acpi/acpi-stub.c
@@ -19,11 +19,9 @@
*/
#include "qemu/osdep.h"
-#include "qapi/error.h"
-#include "qapi/qmp/qerror.h"
#include "hw/acpi/acpi.h"
void acpi_table_add(const QemuOpts *opts, Error **errp)
{
- error_setg(errp, QERR_UNSUPPORTED);
+ g_assert_not_reached();
}
diff --git a/hw/acpi/acpi-x86-stub.c b/hw/acpi/acpi-x86-stub.c
index e9e46c5c5f..9662a594ad 100644
--- a/hw/acpi/acpi-x86-stub.c
+++ b/hw/acpi/acpi-x86-stub.c
@@ -1,12 +1,6 @@
#include "qemu/osdep.h"
-#include "hw/i386/pc.h"
#include "hw/i386/acpi-build.h"
-void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid,
- const CPUArchIdList *apic_ids, GArray *entry)
-{
-}
-
Object *acpi_get_i386_pci_host(void)
{
return NULL;
diff --git a/hw/acpi/acpi_generic_initiator.c b/hw/acpi/acpi_generic_initiator.c
new file mode 100644
index 0000000000..17b9a052f5
--- /dev/null
+++ b/hw/acpi/acpi_generic_initiator.c
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved
+ */
+
+#include "qemu/osdep.h"
+#include "hw/acpi/acpi_generic_initiator.h"
+#include "hw/acpi/aml-build.h"
+#include "hw/boards.h"
+#include "hw/pci/pci_device.h"
+#include "qemu/error-report.h"
+
+typedef struct AcpiGenericInitiatorClass {
+ ObjectClass parent_class;
+} AcpiGenericInitiatorClass;
+
+OBJECT_DEFINE_TYPE_WITH_INTERFACES(AcpiGenericInitiator, acpi_generic_initiator,
+ ACPI_GENERIC_INITIATOR, OBJECT,
+ { TYPE_USER_CREATABLE },
+ { NULL })
+
+OBJECT_DECLARE_SIMPLE_TYPE(AcpiGenericInitiator, ACPI_GENERIC_INITIATOR)
+
+static void acpi_generic_initiator_init(Object *obj)
+{
+ AcpiGenericInitiator *gi = ACPI_GENERIC_INITIATOR(obj);
+
+ gi->node = MAX_NODES;
+ gi->pci_dev = NULL;
+}
+
+static void acpi_generic_initiator_finalize(Object *obj)
+{
+ AcpiGenericInitiator *gi = ACPI_GENERIC_INITIATOR(obj);
+
+ g_free(gi->pci_dev);
+}
+
+static void acpi_generic_initiator_set_pci_device(Object *obj, const char *val,
+ Error **errp)
+{
+ AcpiGenericInitiator *gi = ACPI_GENERIC_INITIATOR(obj);
+
+ gi->pci_dev = g_strdup(val);
+}
+
+static void acpi_generic_initiator_set_node(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ AcpiGenericInitiator *gi = ACPI_GENERIC_INITIATOR(obj);
+ MachineState *ms = MACHINE(qdev_get_machine());
+ uint32_t value;
+
+ if (!visit_type_uint32(v, name, &value, errp)) {
+ return;
+ }
+
+ if (value >= MAX_NODES) {
+ error_printf("%s: Invalid NUMA node specified\n",
+ TYPE_ACPI_GENERIC_INITIATOR);
+ exit(1);
+ }
+
+ gi->node = value;
+ ms->numa_state->nodes[gi->node].has_gi = true;
+}
+
+static void acpi_generic_initiator_class_init(ObjectClass *oc, void *data)
+{
+ object_class_property_add_str(oc, "pci-dev", NULL,
+ acpi_generic_initiator_set_pci_device);
+ object_class_property_add(oc, "node", "int", NULL,
+ acpi_generic_initiator_set_node, NULL, NULL);
+}
+
+/*
+ * ACPI 6.3:
+ * Table 5-78 Generic Initiator Affinity Structure
+ */
+static void
+build_srat_generic_pci_initiator_affinity(GArray *table_data, int node,
+ PCIDeviceHandle *handle)
+{
+ uint8_t index;
+
+ build_append_int_noprefix(table_data, 5, 1); /* Type */
+ build_append_int_noprefix(table_data, 32, 1); /* Length */
+ build_append_int_noprefix(table_data, 0, 1); /* Reserved */
+ build_append_int_noprefix(table_data, 1, 1); /* Device Handle Type: PCI */
+ build_append_int_noprefix(table_data, node, 4); /* Proximity Domain */
+
+ /* Device Handle - PCI */
+ build_append_int_noprefix(table_data, handle->segment, 2);
+ build_append_int_noprefix(table_data, handle->bdf, 2);
+ for (index = 0; index < 12; index++) {
+ build_append_int_noprefix(table_data, 0, 1);
+ }
+
+ build_append_int_noprefix(table_data, GEN_AFFINITY_ENABLED, 4); /* Flags */
+ build_append_int_noprefix(table_data, 0, 4); /* Reserved */
+}
+
+static int build_all_acpi_generic_initiators(Object *obj, void *opaque)
+{
+ MachineState *ms = MACHINE(qdev_get_machine());
+ AcpiGenericInitiator *gi;
+ GArray *table_data = opaque;
+ PCIDeviceHandle dev_handle;
+ PCIDevice *pci_dev;
+ Object *o;
+
+ if (!object_dynamic_cast(obj, TYPE_ACPI_GENERIC_INITIATOR)) {
+ return 0;
+ }
+
+ gi = ACPI_GENERIC_INITIATOR(obj);
+ if (gi->node >= ms->numa_state->num_nodes) {
+ error_printf("%s: Specified node %d is invalid.\n",
+ TYPE_ACPI_GENERIC_INITIATOR, gi->node);
+ exit(1);
+ }
+
+ o = object_resolve_path_type(gi->pci_dev, TYPE_PCI_DEVICE, NULL);
+ if (!o) {
+ error_printf("%s: Specified device must be a PCI device.\n",
+ TYPE_ACPI_GENERIC_INITIATOR);
+ exit(1);
+ }
+
+ pci_dev = PCI_DEVICE(o);
+
+ dev_handle.segment = 0;
+ dev_handle.bdf = PCI_BUILD_BDF(pci_bus_num(pci_get_bus(pci_dev)),
+ pci_dev->devfn);
+
+ build_srat_generic_pci_initiator_affinity(table_data,
+ gi->node, &dev_handle);
+
+ return 0;
+}
+
+void build_srat_generic_pci_initiator(GArray *table_data)
+{
+ object_child_foreach_recursive(object_get_root(),
+ build_all_acpi_generic_initiators,
+ table_data);
+}
diff --git a/hw/acpi/acpi_interface.c b/hw/acpi/acpi_interface.c
index 6583917b8e..8637ff18fc 100644
--- a/hw/acpi/acpi_interface.c
+++ b/hw/acpi/acpi_interface.c
@@ -1,6 +1,8 @@
#include "qemu/osdep.h"
#include "hw/acpi/acpi_dev_interface.h"
+#include "hw/acpi/acpi_aml_interface.h"
#include "qemu/module.h"
+#include "qemu/queue.h"
void acpi_send_event(DeviceState *dev, AcpiEventStatusBits event)
{
@@ -11,6 +13,15 @@ void acpi_send_event(DeviceState *dev, AcpiEventStatusBits event)
}
}
+void qbus_build_aml(BusState *bus, Aml *scope)
+{
+ BusChild *kid;
+
+ QTAILQ_FOREACH(kid, &bus->children, sibling) {
+ call_dev_aml_func(DEVICE(kid->child), scope);
+ }
+}
+
static void register_types(void)
{
static const TypeInfo acpi_dev_if_info = {
@@ -18,8 +29,15 @@ static void register_types(void)
.parent = TYPE_INTERFACE,
.class_size = sizeof(AcpiDeviceIfClass),
};
+ static const TypeInfo acpi_dev_aml_if_info = {
+ .name = TYPE_ACPI_DEV_AML_IF,
+ .parent = TYPE_INTERFACE,
+ .class_size = sizeof(AcpiDevAmlIfClass),
+ };
+
type_register_static(&acpi_dev_if_info);
+ type_register_static(&acpi_dev_aml_if_info);
}
type_init(register_types)
diff --git a/hw/acpi/aml-build-stub.c b/hw/acpi/aml-build-stub.c
index 8d8ad1a314..89a8fec4af 100644
--- a/hw/acpi/aml-build-stub.c
+++ b/hw/acpi/aml-build-stub.c
@@ -26,6 +26,16 @@ void aml_append(Aml *parent_ctx, Aml *child)
{
}
+Aml *aml_return(Aml *val)
+{
+ return NULL;
+}
+
+Aml *aml_method(const char *name, int arg_count, AmlSerializeFlag sflag)
+{
+ return NULL;
+}
+
Aml *aml_resource_template(void)
{
return NULL;
diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c
index d5103e6d7b..6d4517cfbe 100644
--- a/hw/acpi/aml-build.c
+++ b/hw/acpi/aml-build.c
@@ -52,6 +52,19 @@ static void build_append_byte(GArray *array, uint8_t val)
g_array_append_val(array, val);
}
+static void build_append_padded_str(GArray *array, const char *str,
+ size_t maxlen, char pad)
+{
+ size_t i;
+ size_t len = strlen(str);
+
+ g_assert(len <= maxlen);
+ g_array_append_vals(array, str, len);
+ for (i = maxlen - len; i > 0; i--) {
+ g_array_append_val(array, pad);
+ }
+}
+
static void build_append_array(GArray *array, GArray *val)
{
g_array_append_vals(array, val->data, val->len);
@@ -194,7 +207,7 @@ build_append_nameseg(GArray *array, const char *seg)
g_array_append_vals(array, "____", ACPI_NAMESEG_LEN - len);
}
-static void GCC_FMT_ATTR(2, 0)
+static void G_GNUC_PRINTF(2, 0)
build_append_namestringv(GArray *array, const char *format, va_list ap)
{
char *s;
@@ -257,7 +270,7 @@ build_append_namestringv(GArray *array, const char *format, va_list ap)
g_strfreev(segs);
}
-GCC_FMT_ATTR(2, 3)
+G_GNUC_PRINTF(2, 3)
static void build_append_namestring(GArray *array, const char *format, ...)
{
va_list ap;
@@ -299,7 +312,7 @@ build_prepend_package_length(GArray *package, unsigned length, bool incl_self)
/*
* PkgLength is the length of the inclusive length of the data
* and PkgLength's length itself when used for terms with
- * explitit length.
+ * explicit length.
*/
length += length_bytes;
}
@@ -667,7 +680,7 @@ Aml *aml_store(Aml *val, Aml *target)
* "Op Operand Operand Target"
* pattern.
*
- * Returns: The newly allocated and composed according to patter Aml object.
+ * Returns: The newly allocated and composed according to pattern Aml object.
*/
static Aml *
build_opcode_2arg_dst(uint8_t op, Aml *arg1, Aml *arg2, Aml *dst)
@@ -1692,27 +1705,53 @@ Aml *aml_object_type(Aml *object)
return var;
}
-void
-build_header(BIOSLinker *linker, GArray *table_data,
- AcpiTableHeader *h, const char *sig, int len, uint8_t rev,
- const char *oem_id, const char *oem_table_id)
-{
- unsigned tbl_offset = (char *)h - table_data->data;
- unsigned checksum_offset = (char *)&h->checksum - table_data->data;
- memcpy(&h->signature, sig, 4);
- h->length = cpu_to_le32(len);
- h->revision = rev;
-
- strpadcpy((char *)h->oem_id, sizeof h->oem_id, oem_id, ' ');
- strpadcpy((char *)h->oem_table_id, sizeof h->oem_table_id,
- oem_table_id, ' ');
-
- h->oem_revision = cpu_to_le32(1);
- memcpy(h->asl_compiler_id, ACPI_BUILD_APPNAME8, 4);
- h->asl_compiler_revision = cpu_to_le32(1);
- /* Checksum to be filled in by Guest linker */
+void acpi_table_begin(AcpiTable *desc, GArray *array)
+{
+
+ desc->array = array;
+ desc->table_offset = array->len;
+
+ /*
+ * ACPI spec 1.0b
+ * 5.2.3 System Description Table Header
+ */
+ g_assert(strlen(desc->sig) == 4);
+ g_array_append_vals(array, desc->sig, 4); /* Signature */
+ /*
+ * reserve space for Length field, which will be patched by
+ * acpi_table_end() when the table creation is finished.
+ */
+ build_append_int_noprefix(array, 0, 4); /* Length */
+ build_append_int_noprefix(array, desc->rev, 1); /* Revision */
+ build_append_int_noprefix(array, 0, 1); /* Checksum */
+ build_append_padded_str(array, desc->oem_id, 6, '\0'); /* OEMID */
+ /* OEM Table ID */
+ build_append_padded_str(array, desc->oem_table_id, 8, '\0');
+ build_append_int_noprefix(array, 1, 4); /* OEM Revision */
+ g_array_append_vals(array, ACPI_BUILD_APPNAME8, 4); /* Creator ID */
+ build_append_int_noprefix(array, 1, 4); /* Creator Revision */
+}
+
+void acpi_table_end(BIOSLinker *linker, AcpiTable *desc)
+{
+ /*
+ * ACPI spec 1.0b
+ * 5.2.3 System Description Table Header
+ * Table 5-2 DESCRIPTION_HEADER Fields
+ */
+ const unsigned checksum_offset = 9;
+ uint32_t table_len = desc->array->len - desc->table_offset;
+ uint32_t table_len_le = cpu_to_le32(table_len);
+ gchar *len_ptr = &desc->array->data[desc->table_offset + 4];
+
+ /* patch "Length" field that has been reserved by acpi_table_begin()
+ * to the actual length, i.e. accumulated table length from
+ * acpi_table_begin() till acpi_table_end()
+ */
+ memcpy(len_ptr, &table_len_le, sizeof table_len_le);
+
bios_linker_loader_add_checksum(linker, ACPI_BUILD_TABLE_FILE,
- tbl_offset, len, checksum_offset);
+ desc->table_offset, table_len, desc->table_offset + checksum_offset);
}
void *acpi_data_push(GArray *table_data, unsigned size)
@@ -1822,73 +1861,81 @@ build_rsdp(GArray *tbl, BIOSLinker *linker, AcpiRsdpData *rsdp_data)
32);
}
-/* Build rsdt table */
+/*
+ * ACPI 1.0 Root System Description Table (RSDT)
+ */
void
build_rsdt(GArray *table_data, BIOSLinker *linker, GArray *table_offsets,
const char *oem_id, const char *oem_table_id)
{
int i;
- unsigned rsdt_entries_offset;
- AcpiRsdtDescriptorRev1 *rsdt;
- int rsdt_start = table_data->len;
- const unsigned table_data_len = (sizeof(uint32_t) * table_offsets->len);
- const unsigned rsdt_entry_size = sizeof(rsdt->table_offset_entry[0]);
- const size_t rsdt_len = sizeof(*rsdt) + table_data_len;
-
- rsdt = acpi_data_push(table_data, rsdt_len);
- rsdt_entries_offset = (char *)rsdt->table_offset_entry - table_data->data;
+ AcpiTable table = { .sig = "RSDT", .rev = 1,
+ .oem_id = oem_id, .oem_table_id = oem_table_id };
+
+ acpi_table_begin(&table, table_data);
for (i = 0; i < table_offsets->len; ++i) {
uint32_t ref_tbl_offset = g_array_index(table_offsets, uint32_t, i);
- uint32_t rsdt_entry_offset = rsdt_entries_offset + rsdt_entry_size * i;
+ uint32_t rsdt_entry_offset = table.array->len;
+
+ /* reserve space for entry */
+ build_append_int_noprefix(table.array, 0, 4);
- /* rsdt->table_offset_entry to be filled by Guest linker */
+ /* mark position of RSDT entry to be filled by Guest linker */
bios_linker_loader_add_pointer(linker,
- ACPI_BUILD_TABLE_FILE, rsdt_entry_offset, rsdt_entry_size,
+ ACPI_BUILD_TABLE_FILE, rsdt_entry_offset, 4,
ACPI_BUILD_TABLE_FILE, ref_tbl_offset);
+
}
- build_header(linker, table_data,
- (void *)(table_data->data + rsdt_start),
- "RSDT", rsdt_len, 1, oem_id, oem_table_id);
+ acpi_table_end(linker, &table);
}
-/* Build xsdt table */
+/*
+ * ACPI 2.0 eXtended System Description Table (XSDT)
+ */
void
build_xsdt(GArray *table_data, BIOSLinker *linker, GArray *table_offsets,
const char *oem_id, const char *oem_table_id)
{
int i;
- unsigned xsdt_entries_offset;
- AcpiXsdtDescriptorRev2 *xsdt;
- int xsdt_start = table_data->len;
- const unsigned table_data_len = (sizeof(uint64_t) * table_offsets->len);
- const unsigned xsdt_entry_size = sizeof(xsdt->table_offset_entry[0]);
- const size_t xsdt_len = sizeof(*xsdt) + table_data_len;
-
- xsdt = acpi_data_push(table_data, xsdt_len);
- xsdt_entries_offset = (char *)xsdt->table_offset_entry - table_data->data;
+ AcpiTable table = { .sig = "XSDT", .rev = 1,
+ .oem_id = oem_id, .oem_table_id = oem_table_id };
+
+ acpi_table_begin(&table, table_data);
+
for (i = 0; i < table_offsets->len; ++i) {
uint64_t ref_tbl_offset = g_array_index(table_offsets, uint32_t, i);
- uint64_t xsdt_entry_offset = xsdt_entries_offset + xsdt_entry_size * i;
+ uint64_t xsdt_entry_offset = table.array->len;
- /* xsdt->table_offset_entry to be filled by Guest linker */
+ /* reserve space for entry */
+ build_append_int_noprefix(table.array, 0, 8);
+
+ /* mark position of RSDT entry to be filled by Guest linker */
bios_linker_loader_add_pointer(linker,
- ACPI_BUILD_TABLE_FILE, xsdt_entry_offset, xsdt_entry_size,
+ ACPI_BUILD_TABLE_FILE, xsdt_entry_offset, 8,
ACPI_BUILD_TABLE_FILE, ref_tbl_offset);
}
- build_header(linker, table_data,
- (void *)(table_data->data + xsdt_start),
- "XSDT", xsdt_len, 1, oem_id, oem_table_id);
+ acpi_table_end(linker, &table);
}
-void build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base,
+/*
+ * ACPI spec, Revision 4.0
+ * 5.2.16.2 Memory Affinity Structure
+ */
+void build_srat_memory(GArray *table_data, uint64_t base,
uint64_t len, int node, MemoryAffinityFlags flags)
{
- numamem->type = ACPI_SRAT_MEMORY;
- numamem->length = sizeof(*numamem);
- numamem->proximity = cpu_to_le32(node);
- numamem->flags = cpu_to_le32(flags);
- numamem->base_addr = cpu_to_le64(base);
- numamem->range_length = cpu_to_le64(len);
+ build_append_int_noprefix(table_data, 1, 1); /* Type */
+ build_append_int_noprefix(table_data, 40, 1); /* Length */
+ build_append_int_noprefix(table_data, node, 4); /* Proximity Domain */
+ build_append_int_noprefix(table_data, 0, 2); /* Reserved */
+ build_append_int_noprefix(table_data, base, 4); /* Base Address Low */
+ /* Base Address High */
+ build_append_int_noprefix(table_data, base >> 32, 4);
+ build_append_int_noprefix(table_data, len, 4); /* Length Low */
+ build_append_int_noprefix(table_data, len >> 32, 4); /* Length High */
+ build_append_int_noprefix(table_data, 0, 4); /* Reserved */
+ build_append_int_noprefix(table_data, flags, 4); /* Flags */
+ build_append_int_noprefix(table_data, 0, 8); /* Reserved */
}
/*
@@ -1898,11 +1945,12 @@ void build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base,
void build_slit(GArray *table_data, BIOSLinker *linker, MachineState *ms,
const char *oem_id, const char *oem_table_id)
{
- int slit_start, i, j;
- slit_start = table_data->len;
+ int i, j;
int nb_numa_nodes = ms->numa_state->num_nodes;
+ AcpiTable table = { .sig = "SLIT", .rev = 1,
+ .oem_id = oem_id, .oem_table_id = oem_table_id };
- acpi_data_push(table_data, sizeof(AcpiTableHeader));
+ acpi_table_begin(&table, table_data);
build_append_int_noprefix(table_data, nb_numa_nodes, 8);
for (i = 0; i < nb_numa_nodes; i++) {
@@ -1913,21 +1961,177 @@ void build_slit(GArray *table_data, BIOSLinker *linker, MachineState *ms,
1);
}
}
+ acpi_table_end(linker, &table);
+}
+
+/*
+ * ACPI spec, Revision 6.3
+ * 5.2.29.1 Processor hierarchy node structure (Type 0)
+ */
+static void build_processor_hierarchy_node(GArray *tbl, uint32_t flags,
+ uint32_t parent, uint32_t id,
+ uint32_t *priv_rsrc,
+ uint32_t priv_num)
+{
+ int i;
- build_header(linker, table_data,
- (void *)(table_data->data + slit_start),
- "SLIT",
- table_data->len - slit_start, 1, oem_id, oem_table_id);
+ build_append_byte(tbl, 0); /* Type 0 - processor */
+ build_append_byte(tbl, 20 + priv_num * 4); /* Length */
+ build_append_int_noprefix(tbl, 0, 2); /* Reserved */
+ build_append_int_noprefix(tbl, flags, 4); /* Flags */
+ build_append_int_noprefix(tbl, parent, 4); /* Parent */
+ build_append_int_noprefix(tbl, id, 4); /* ACPI Processor ID */
+
+ /* Number of private resources */
+ build_append_int_noprefix(tbl, priv_num, 4);
+
+ /* Private resources[N] */
+ if (priv_num > 0) {
+ assert(priv_rsrc);
+ for (i = 0; i < priv_num; i++) {
+ build_append_int_noprefix(tbl, priv_rsrc[i], 4);
+ }
+ }
+}
+
+void build_spcr(GArray *table_data, BIOSLinker *linker,
+ const AcpiSpcrData *f, const uint8_t rev,
+ const char *oem_id, const char *oem_table_id)
+{
+ AcpiTable table = { .sig = "SPCR", .rev = rev, .oem_id = oem_id,
+ .oem_table_id = oem_table_id };
+
+ acpi_table_begin(&table, table_data);
+ /* Interface type */
+ build_append_int_noprefix(table_data, f->interface_type, 1);
+ /* Reserved */
+ build_append_int_noprefix(table_data, 0, 3);
+ /* Base Address */
+ build_append_gas(table_data, f->base_addr.id, f->base_addr.width,
+ f->base_addr.offset, f->base_addr.size,
+ f->base_addr.addr);
+ /* Interrupt type */
+ build_append_int_noprefix(table_data, f->interrupt_type, 1);
+ /* IRQ */
+ build_append_int_noprefix(table_data, f->pc_interrupt, 1);
+ /* Global System Interrupt */
+ build_append_int_noprefix(table_data, f->interrupt, 4);
+ /* Baud Rate */
+ build_append_int_noprefix(table_data, f->baud_rate, 1);
+ /* Parity */
+ build_append_int_noprefix(table_data, f->parity, 1);
+ /* Stop Bits */
+ build_append_int_noprefix(table_data, f->stop_bits, 1);
+ /* Flow Control */
+ build_append_int_noprefix(table_data, f->flow_control, 1);
+ /* Language */
+ build_append_int_noprefix(table_data, f->language, 1);
+ /* Terminal Type */
+ build_append_int_noprefix(table_data, f->terminal_type, 1);
+ /* PCI Device ID */
+ build_append_int_noprefix(table_data, f->pci_device_id, 2);
+ /* PCI Vendor ID */
+ build_append_int_noprefix(table_data, f->pci_vendor_id, 2);
+ /* PCI Bus Number */
+ build_append_int_noprefix(table_data, f->pci_bus, 1);
+ /* PCI Device Number */
+ build_append_int_noprefix(table_data, f->pci_device, 1);
+ /* PCI Function Number */
+ build_append_int_noprefix(table_data, f->pci_function, 1);
+ /* PCI Flags */
+ build_append_int_noprefix(table_data, f->pci_flags, 4);
+ /* PCI Segment */
+ build_append_int_noprefix(table_data, f->pci_segment, 1);
+ /* Reserved */
+ build_append_int_noprefix(table_data, 0, 4);
+
+ acpi_table_end(linker, &table);
}
+/*
+ * ACPI spec, Revision 6.3
+ * 5.2.29 Processor Properties Topology Table (PPTT)
+ */
+void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms,
+ const char *oem_id, const char *oem_table_id)
+{
+ MachineClass *mc = MACHINE_GET_CLASS(ms);
+ CPUArchIdList *cpus = ms->possible_cpus;
+ int64_t socket_id = -1, cluster_id = -1, core_id = -1;
+ uint32_t socket_offset = 0, cluster_offset = 0, core_offset = 0;
+ uint32_t pptt_start = table_data->len;
+ int n;
+ AcpiTable table = { .sig = "PPTT", .rev = 2,
+ .oem_id = oem_id, .oem_table_id = oem_table_id };
+
+ acpi_table_begin(&table, table_data);
+
+ /*
+ * This works with the assumption that cpus[n].props.*_id has been
+ * sorted from top to down levels in mc->possible_cpu_arch_ids().
+ * Otherwise, the unexpected and duplicated containers will be
+ * created.
+ */
+ for (n = 0; n < cpus->len; n++) {
+ if (cpus->cpus[n].props.socket_id != socket_id) {
+ assert(cpus->cpus[n].props.socket_id > socket_id);
+ socket_id = cpus->cpus[n].props.socket_id;
+ cluster_id = -1;
+ core_id = -1;
+ socket_offset = table_data->len - pptt_start;
+ build_processor_hierarchy_node(table_data,
+ (1 << 0), /* Physical package */
+ 0, socket_id, NULL, 0);
+ }
-/* build rev1/rev3/rev5.1 FADT */
+ if (mc->smp_props.clusters_supported && mc->smp_props.has_clusters) {
+ if (cpus->cpus[n].props.cluster_id != cluster_id) {
+ assert(cpus->cpus[n].props.cluster_id > cluster_id);
+ cluster_id = cpus->cpus[n].props.cluster_id;
+ core_id = -1;
+ cluster_offset = table_data->len - pptt_start;
+ build_processor_hierarchy_node(table_data,
+ (0 << 0), /* Not a physical package */
+ socket_offset, cluster_id, NULL, 0);
+ }
+ } else {
+ cluster_offset = socket_offset;
+ }
+
+ if (ms->smp.threads == 1) {
+ build_processor_hierarchy_node(table_data,
+ (1 << 1) | /* ACPI Processor ID valid */
+ (1 << 3), /* Node is a Leaf */
+ cluster_offset, n, NULL, 0);
+ } else {
+ if (cpus->cpus[n].props.core_id != core_id) {
+ assert(cpus->cpus[n].props.core_id > core_id);
+ core_id = cpus->cpus[n].props.core_id;
+ core_offset = table_data->len - pptt_start;
+ build_processor_hierarchy_node(table_data,
+ (0 << 0), /* Not a physical package */
+ cluster_offset, core_id, NULL, 0);
+ }
+
+ build_processor_hierarchy_node(table_data,
+ (1 << 1) | /* ACPI Processor ID valid */
+ (1 << 2) | /* Processor is a Thread */
+ (1 << 3), /* Node is a Leaf */
+ core_offset, n, NULL, 0);
+ }
+ }
+
+ acpi_table_end(linker, &table);
+}
+
+/* build rev1/rev3/rev5.1/rev6.0 FADT */
void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f,
const char *oem_id, const char *oem_table_id)
{
int off;
- int fadt_start = tbl->len;
+ AcpiTable table = { .sig = "FACP", .rev = f->rev,
+ .oem_id = oem_id, .oem_table_id = oem_table_id };
- acpi_data_push(tbl, sizeof(AcpiTableHeader));
+ acpi_table_begin(&table, tbl);
/* FACS address to be filled by Guest linker at runtime */
off = tbl->len;
@@ -1986,12 +2190,18 @@ void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f,
build_append_int_noprefix(tbl, 0, 1); /* DAY_ALRM */
build_append_int_noprefix(tbl, 0, 1); /* MON_ALRM */
build_append_int_noprefix(tbl, f->rtc_century, 1); /* CENTURY */
- build_append_int_noprefix(tbl, 0, 2); /* IAPC_BOOT_ARCH */
+ /* IAPC_BOOT_ARCH */
+ if (f->rev == 1) {
+ build_append_int_noprefix(tbl, 0, 2);
+ } else {
+ /* since ACPI v2.0 */
+ build_append_int_noprefix(tbl, f->iapc_boot_arch, 2);
+ }
build_append_int_noprefix(tbl, 0, 1); /* Reserved */
build_append_int_noprefix(tbl, f->flags, 4); /* Flags */
if (f->rev == 1) {
- goto build_hdr;
+ goto done;
}
build_append_gas_from_struct(tbl, &f->reset_reg); /* RESET_REG */
@@ -2002,7 +2212,7 @@ void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f,
/* FADT Minor Version */
build_append_int_noprefix(tbl, f->minor_ver, 1);
} else {
- build_append_int_noprefix(tbl, 0, 3); /* Reserved upto ACPI 5.0 */
+ build_append_int_noprefix(tbl, 0, 3); /* Reserved up to ACPI 5.0 */
}
build_append_int_noprefix(tbl, 0, 8); /* X_FIRMWARE_CTRL */
@@ -2028,7 +2238,7 @@ void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f,
build_append_gas(tbl, AML_AS_SYSTEM_MEMORY, 0 , 0, 0, 0); /* X_GPE1_BLK */
if (f->rev <= 4) {
- goto build_hdr;
+ goto done;
}
/* SLEEP_CONTROL_REG */
@@ -2036,12 +2246,18 @@ void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f,
/* SLEEP_STATUS_REG */
build_append_gas_from_struct(tbl, &f->sleep_sts);
- /* TODO: extra fields need to be added to support revisions above rev5 */
- assert(f->rev == 5);
+ if (f->rev == 5) {
+ goto done;
+ }
+
+ /* Hypervisor Vendor Identity */
+ build_append_padded_str(tbl, "QEMU", 8, '\0');
+
+ /* TODO: extra fields need to be added to support revisions above rev6 */
+ assert(f->rev == 6);
-build_hdr:
- build_header(linker, tbl, (void *)(tbl->data + fadt_start),
- "FACP", tbl->len - fadt_start, f->rev, oem_id, oem_table_id);
+done:
+ acpi_table_end(linker, &table);
}
#ifdef CONFIG_TPM
@@ -2054,13 +2270,14 @@ void build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog,
const char *oem_id, const char *oem_table_id)
{
uint8_t start_method_params[12] = {};
- unsigned log_addr_offset, tpm2_start;
+ unsigned log_addr_offset;
uint64_t control_area_start_address;
TPMIf *tpmif = tpm_find();
uint32_t start_method;
+ AcpiTable table = { .sig = "TPM2", .rev = 4,
+ .oem_id = oem_id, .oem_table_id = oem_table_id };
- tpm2_start = table_data->len;
- acpi_data_push(table_data, sizeof(AcpiTableHeader));
+ acpi_table_begin(&table, table_data);
/* Platform Class */
build_append_int_noprefix(table_data, TPM2_ACPI_CLASS_CLIENT, 2);
@@ -2098,9 +2315,7 @@ void build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog,
bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE,
log_addr_offset, 8,
ACPI_BUILD_TPMLOG_FILE, 0);
- build_header(linker, table_data,
- (void *)(table_data->data + tpm2_start),
- "TPM2", table_data->len - tpm2_start, 4, oem_id, oem_table_id);
+ acpi_table_end(linker, &table);
}
#endif
diff --git a/hw/acpi/core.c b/hw/acpi/core.c
index 1e004d0078..ec5e127d17 100644
--- a/hw/acpi/core.c
+++ b/hw/acpi/core.c
@@ -32,6 +32,7 @@
#include "qemu/module.h"
#include "qemu/option.h"
#include "sysemu/runstate.h"
+#include "trace.h"
struct acpi_table_header {
uint16_t _length; /* our length, not actual part of the hdr */
@@ -185,7 +186,7 @@ static void acpi_table_install(const char unsigned *blob, size_t bloblen,
changed_fields = 0;
ext_hdr->_length = cpu_to_le16(acpi_payload_size);
- if (hdrs->has_sig) {
+ if (hdrs->sig) {
strncpy(ext_hdr->sig, hdrs->sig, sizeof ext_hdr->sig);
++changed_fields;
}
@@ -204,11 +205,11 @@ static void acpi_table_install(const char unsigned *blob, size_t bloblen,
ext_hdr->checksum = 0;
- if (hdrs->has_oem_id) {
+ if (hdrs->oem_id) {
strncpy(ext_hdr->oem_id, hdrs->oem_id, sizeof ext_hdr->oem_id);
++changed_fields;
}
- if (hdrs->has_oem_table_id) {
+ if (hdrs->oem_table_id) {
strncpy(ext_hdr->oem_table_id, hdrs->oem_table_id,
sizeof ext_hdr->oem_table_id);
++changed_fields;
@@ -217,7 +218,7 @@ static void acpi_table_install(const char unsigned *blob, size_t bloblen,
ext_hdr->oem_revision = cpu_to_le32(hdrs->oem_rev);
++changed_fields;
}
- if (hdrs->has_asl_compiler_id) {
+ if (hdrs->asl_compiler_id) {
strncpy(ext_hdr->asl_compiler_id, hdrs->asl_compiler_id,
sizeof ext_hdr->asl_compiler_id);
++changed_fields;
@@ -255,12 +256,12 @@ void acpi_table_add(const QemuOpts *opts, Error **errp)
if (!hdrs) {
goto out;
}
- if (hdrs->has_file == hdrs->has_data) {
+ if (!hdrs->file == !hdrs->data) {
error_setg(errp, "'-acpitable' requires one of 'data' or 'file'");
goto out;
}
- pathnames = g_strsplit(hdrs->has_file ? hdrs->file : hdrs->data, ":", 0);
+ pathnames = g_strsplit(hdrs->file ?: hdrs->data, ":", 0);
if (pathnames == NULL || pathnames[0] == NULL) {
error_setg(errp, "'-acpitable' requires at least one pathname");
goto out;
@@ -297,7 +298,7 @@ void acpi_table_add(const QemuOpts *opts, Error **errp)
close(fd);
}
- acpi_table_install(blob, bloblen, hdrs->has_file, hdrs, errp);
+ acpi_table_install(blob, bloblen, !!hdrs->file, hdrs, errp);
out:
g_free(blob);
@@ -345,8 +346,8 @@ int acpi_get_slic_oem(AcpiSlicOem *oem)
struct acpi_table_header *hdr = (void *)(u - sizeof(hdr->_length));
if (memcmp(hdr->sig, "SLIC", 4) == 0) {
- oem->id = hdr->oem_id;
- oem->table_id = hdr->oem_table_id;
+ oem->id = g_strndup(hdr->oem_id, 6);
+ oem->table_id = g_strndup(hdr->oem_table_id, 8);
return 0;
}
}
@@ -551,30 +552,6 @@ void acpi_pm_tmr_reset(ACPIREGS *ar)
}
/* ACPI PM1aCNT */
-static void acpi_pm1_cnt_write(ACPIREGS *ar, uint16_t val)
-{
- ar->pm1.cnt.cnt = val & ~(ACPI_BITMASK_SLEEP_ENABLE);
-
- if (val & ACPI_BITMASK_SLEEP_ENABLE) {
- /* change suspend type */
- uint16_t sus_typ = (val >> 10) & 7;
- switch (sus_typ) {
- case 0: /* soft power off */
- qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
- break;
- case 1:
- qemu_system_suspend_request();
- break;
- default:
- if (sus_typ == ar->pm1.cnt.s4_val) { /* S4 request */
- qapi_event_send_suspend_disk();
- qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
- }
- break;
- }
- }
-}
-
void acpi_pm1_cnt_update(ACPIREGS *ar,
bool sci_enable, bool sci_disable)
{
@@ -593,13 +570,37 @@ void acpi_pm1_cnt_update(ACPIREGS *ar,
static uint64_t acpi_pm_cnt_read(void *opaque, hwaddr addr, unsigned width)
{
ACPIREGS *ar = opaque;
- return ar->pm1.cnt.cnt;
+ return ar->pm1.cnt.cnt >> addr * 8;
}
static void acpi_pm_cnt_write(void *opaque, hwaddr addr, uint64_t val,
unsigned width)
{
- acpi_pm1_cnt_write(opaque, val);
+ ACPIREGS *ar = opaque;
+
+ if (addr == 1) {
+ val = val << 8 | (ar->pm1.cnt.cnt & 0xff);
+ }
+ ar->pm1.cnt.cnt = val & ~(ACPI_BITMASK_SLEEP_ENABLE);
+
+ if (val & ACPI_BITMASK_SLEEP_ENABLE) {
+ /* change suspend type */
+ uint16_t sus_typ = (val >> 10) & 7;
+ switch (sus_typ) {
+ case 0: /* soft power off */
+ qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
+ break;
+ case 1:
+ qemu_system_suspend_request();
+ break;
+ default:
+ if (sus_typ == ar->pm1.cnt.s4_val) { /* S4 request */
+ qapi_event_send_suspend_disk();
+ qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
+ }
+ break;
+ }
+ }
}
static const MemoryRegionOps acpi_pm_cnt_ops = {
@@ -688,9 +689,11 @@ void acpi_gpe_ioport_writeb(ACPIREGS *ar, uint32_t addr, uint32_t val)
cur = acpi_gpe_ioport_get_ptr(ar, addr);
if (addr < ar->gpe.len / 2) {
+ trace_acpi_gpe_sts_ioport_writeb(addr, val);
/* GPE_STS */
*cur = (*cur) & ~val;
} else if (addr < ar->gpe.len) {
+ trace_acpi_gpe_en_ioport_writeb(addr - (ar->gpe.len / 2), val);
/* GPE_EN */
*cur = val;
} else {
@@ -709,6 +712,12 @@ uint32_t acpi_gpe_ioport_readb(ACPIREGS *ar, uint32_t addr)
val = *cur;
}
+ if (addr < ar->gpe.len / 2) {
+ trace_acpi_gpe_sts_ioport_readb(addr, val);
+ } else {
+ trace_acpi_gpe_en_ioport_readb(addr - (ar->gpe.len / 2), val);
+ }
+
return val;
}
diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c
index f82e9512fd..2d81c1e790 100644
--- a/hw/acpi/cpu.c
+++ b/hw/acpi/cpu.c
@@ -1,6 +1,7 @@
#include "qemu/osdep.h"
#include "migration/vmstate.h"
#include "hw/acpi/cpu.h"
+#include "hw/core/cpu.h"
#include "qapi/error.h"
#include "qapi/qapi-events-acpi.h"
#include "trace.h"
@@ -35,7 +36,6 @@ static ACPIOSTInfo *acpi_cpu_device_status(int idx, AcpiCpuStatus *cdev)
DeviceState *dev = DEVICE(cdev->cpu);
if (dev->id) {
info->device = g_strdup(dev->id);
- info->has_device = true;
}
}
return info;
@@ -297,8 +297,7 @@ static const VMStateDescription vmstate_cpuhp_sts = {
.name = "CPU hotplug device state",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_BOOL(is_inserting, AcpiCpuStatus),
VMSTATE_BOOL(is_removing, AcpiCpuStatus),
VMSTATE_UINT32(ost_event, AcpiCpuStatus),
@@ -311,8 +310,7 @@ const VMStateDescription vmstate_cpu_hotplug = {
.name = "CPU hotplug state",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_UINT32(selector, CPUHotplugState),
VMSTATE_UINT8(command, CPUHotplugState),
VMSTATE_STRUCT_VARRAY_POINTER_UINT32(devs, CPUHotplugState, dev_count,
@@ -341,7 +339,7 @@ const VMStateDescription vmstate_cpu_hotplug = {
#define CPU_FW_EJECT_EVENT "CEJF"
void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
- hwaddr io_base,
+ build_madt_cpu_fn build_madt_cpu, hwaddr io_base,
const char *res_root,
const char *event_handler_method)
{
@@ -356,9 +354,6 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
MachineClass *mc = MACHINE_GET_CLASS(machine);
const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(machine);
char *cphp_res_path = g_strdup_printf("%s." CPUHP_RES_DEVICE, res_root);
- Object *obj = object_resolve_path_type("", TYPE_ACPI_DEVICE_IF, NULL);
- AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(obj);
- AcpiDeviceIf *adev = ACPI_DEVICE_IF(obj);
cpu_ctrl_dev = aml_device("%s", cphp_res_path);
{
@@ -668,22 +663,7 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
aml_append(dev, method);
/* build _MAT object */
- assert(adevc && adevc->madt_cpu);
- adevc->madt_cpu(adev, i, arch_ids, madt_buf);
- switch (madt_buf->data[0]) {
- case ACPI_APIC_PROCESSOR: {
- AcpiMadtProcessorApic *apic = (void *)madt_buf->data;
- apic->flags = cpu_to_le32(1);
- break;
- }
- case ACPI_APIC_LOCAL_X2APIC: {
- AcpiMadtProcessorX2Apic *apic = (void *)madt_buf->data;
- apic->flags = cpu_to_le32(1);
- break;
- }
- default:
- assert(0);
- }
+ build_madt_cpu(i, arch_ids, madt_buf, true); /* set enabled flag */
aml_append(dev, aml_name_decl("_MAT",
aml_buffer(madt_buf->len, (uint8_t *)madt_buf->data)));
g_array_free(madt_buf, true);
diff --git a/hw/acpi/cpu_hotplug.c b/hw/acpi/cpu_hotplug.c
index 53654f8638..83b8bc5deb 100644
--- a/hw/acpi/cpu_hotplug.c
+++ b/hw/acpi/cpu_hotplug.c
@@ -13,8 +13,8 @@
#include "hw/acpi/cpu_hotplug.h"
#include "qapi/error.h"
#include "hw/core/cpu.h"
-#include "hw/i386/pc.h"
-#include "hw/pci/pci.h"
+#include "hw/i386/x86.h"
+#include "hw/pci/pci_device.h"
#include "qemu/error-report.h"
#define CPU_EJECT_METHOD "CPEJ"
@@ -52,11 +52,15 @@ static const MemoryRegionOps AcpiCpuHotplug_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 1,
+ .max_access_size = 4,
+ },
+ .impl = {
.max_access_size = 1,
},
};
-static void acpi_set_cpu_present_bit(AcpiCpuHotplug *g, CPUState *cpu)
+static void acpi_set_cpu_present_bit(AcpiCpuHotplug *g, CPUState *cpu,
+ bool *swtchd_to_modern)
{
CPUClass *k = CPU_GET_CLASS(cpu);
int64_t cpu_id;
@@ -65,23 +69,34 @@ static void acpi_set_cpu_present_bit(AcpiCpuHotplug *g, CPUState *cpu)
if ((cpu_id / 8) >= ACPI_GPE_PROC_LEN) {
object_property_set_bool(g->device, "cpu-hotplug-legacy", false,
&error_abort);
+ *swtchd_to_modern = true;
return;
}
+ *swtchd_to_modern = false;
g->sts[cpu_id / 8] |= (1 << (cpu_id % 8));
}
void legacy_acpi_cpu_plug_cb(HotplugHandler *hotplug_dev,
AcpiCpuHotplug *g, DeviceState *dev, Error **errp)
{
- acpi_set_cpu_present_bit(g, CPU(dev));
- acpi_send_event(DEVICE(hotplug_dev), ACPI_CPU_HOTPLUG_STATUS);
+ bool swtchd_to_modern;
+ Error *local_err = NULL;
+
+ acpi_set_cpu_present_bit(g, CPU(dev), &swtchd_to_modern);
+ if (swtchd_to_modern) {
+ /* propagate the hotplug to the modern interface */
+ hotplug_handler_plug(hotplug_dev, dev, &local_err);
+ } else {
+ acpi_send_event(DEVICE(hotplug_dev), ACPI_CPU_HOTPLUG_STATUS);
+ }
}
void legacy_acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner,
AcpiCpuHotplug *gpe_cpu, uint16_t base)
{
CPUState *cpu;
+ bool swtchd_to_modern;
memory_region_init_io(&gpe_cpu->io, owner, &AcpiCpuHotplug_ops,
gpe_cpu, "acpi-cpu-hotplug", ACPI_GPE_PROC_LEN);
@@ -89,7 +104,7 @@ void legacy_acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner,
gpe_cpu->device = owner;
CPU_FOREACH(cpu) {
- acpi_set_cpu_present_bit(gpe_cpu, cpu);
+ acpi_set_cpu_present_bit(gpe_cpu, cpu, &swtchd_to_modern);
}
}
@@ -262,26 +277,27 @@ void build_legacy_cpu_hotplug_aml(Aml *ctx, MachineState *machine,
/* build Processor object for each processor */
for (i = 0; i < apic_ids->len; i++) {
- int apic_id = apic_ids->cpus[i].arch_id;
+ int cpu_apic_id = apic_ids->cpus[i].arch_id;
- assert(apic_id < ACPI_CPU_HOTPLUG_ID_LIMIT);
+ assert(cpu_apic_id < ACPI_CPU_HOTPLUG_ID_LIMIT);
- dev = aml_processor(i, 0, 0, "CP%.02X", apic_id);
+ dev = aml_processor(i, 0, 0, "CP%.02X", cpu_apic_id);
method = aml_method("_MAT", 0, AML_NOTSERIALIZED);
aml_append(method,
- aml_return(aml_call2(CPU_MAT_METHOD, aml_int(apic_id), aml_int(i))
+ aml_return(aml_call2(CPU_MAT_METHOD,
+ aml_int(cpu_apic_id), aml_int(i))
));
aml_append(dev, method);
method = aml_method("_STA", 0, AML_NOTSERIALIZED);
aml_append(method,
- aml_return(aml_call1(CPU_STATUS_METHOD, aml_int(apic_id))));
+ aml_return(aml_call1(CPU_STATUS_METHOD, aml_int(cpu_apic_id))));
aml_append(dev, method);
method = aml_method("_EJ0", 1, AML_NOTSERIALIZED);
aml_append(method,
- aml_return(aml_call2(CPU_EJECT_METHOD, aml_int(apic_id),
+ aml_return(aml_call2(CPU_EJECT_METHOD, aml_int(cpu_apic_id),
aml_arg(0)))
);
aml_append(dev, method);
@@ -295,11 +311,11 @@ void build_legacy_cpu_hotplug_aml(Aml *ctx, MachineState *machine,
/* Arg0 = APIC ID */
method = aml_method(AML_NOTIFY_METHOD, 2, AML_NOTSERIALIZED);
for (i = 0; i < apic_ids->len; i++) {
- int apic_id = apic_ids->cpus[i].arch_id;
+ int cpu_apic_id = apic_ids->cpus[i].arch_id;
- if_ctx = aml_if(aml_equal(aml_arg(0), aml_int(apic_id)));
+ if_ctx = aml_if(aml_equal(aml_arg(0), aml_int(cpu_apic_id)));
aml_append(if_ctx,
- aml_notify(aml_name("CP%.02X", apic_id), aml_arg(1))
+ aml_notify(aml_name("CP%.02X", cpu_apic_id), aml_arg(1))
);
aml_append(method, if_ctx);
}
@@ -316,13 +332,13 @@ void build_legacy_cpu_hotplug_aml(Aml *ctx, MachineState *machine,
aml_varpackage(x86ms->apic_id_limit);
for (i = 0, apic_idx = 0; i < apic_ids->len; i++) {
- int apic_id = apic_ids->cpus[i].arch_id;
+ int cpu_apic_id = apic_ids->cpus[i].arch_id;
- for (; apic_idx < apic_id; apic_idx++) {
+ for (; apic_idx < cpu_apic_id; apic_idx++) {
aml_append(pkg, aml_int(0));
}
aml_append(pkg, aml_int(apic_ids->cpus[i].cpu ? 1 : 0));
- apic_idx = apic_id + 1;
+ apic_idx = cpu_apic_id + 1;
}
aml_append(sb_scope, aml_name_decl(CPU_ON_BITMAP, pkg));
aml_append(ctx, sb_scope);
diff --git a/hw/acpi/cxl-stub.c b/hw/acpi/cxl-stub.c
new file mode 100644
index 0000000000..15bc21076b
--- /dev/null
+++ b/hw/acpi/cxl-stub.c
@@ -0,0 +1,12 @@
+
+/*
+ * Stubs for ACPI platforms that don't support CXl
+ */
+#include "qemu/osdep.h"
+#include "hw/acpi/aml-build.h"
+#include "hw/acpi/cxl.h"
+
+void build_cxl_osc_method(Aml *dev)
+{
+ g_assert_not_reached();
+}
diff --git a/hw/acpi/cxl.c b/hw/acpi/cxl.c
new file mode 100644
index 0000000000..9cd7905ea2
--- /dev/null
+++ b/hw/acpi/cxl.c
@@ -0,0 +1,326 @@
+/*
+ * CXL ACPI Implementation
+ *
+ * Copyright(C) 2020 Intel Corporation.
+ *
+ * This library 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; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "hw/sysbus.h"
+#include "hw/pci/pci_bridge.h"
+#include "hw/pci/pci_host.h"
+#include "hw/cxl/cxl.h"
+#include "hw/mem/memory-device.h"
+#include "hw/acpi/acpi.h"
+#include "hw/acpi/aml-build.h"
+#include "hw/acpi/bios-linker-loader.h"
+#include "hw/acpi/cxl.h"
+#include "qapi/error.h"
+#include "qemu/uuid.h"
+
+void build_cxl_dsm_method(Aml *dev)
+{
+ Aml *method, *ifctx, *ifctx2;
+
+ method = aml_method("_DSM", 4, AML_SERIALIZED);
+ {
+ Aml *function, *uuid;
+
+ uuid = aml_arg(0);
+ function = aml_arg(2);
+ /* CXL spec v3.0 9.17.3.1 _DSM Function for Retrieving QTG ID */
+ ifctx = aml_if(aml_equal(
+ uuid, aml_touuid("F365F9A6-A7DE-4071-A66A-B40C0B4F8E52")));
+
+ /* Function 0, standard DSM query function */
+ ifctx2 = aml_if(aml_equal(function, aml_int(0)));
+ {
+ uint8_t byte_list[1] = { 0x01 }; /* function 1 only */
+
+ aml_append(ifctx2,
+ aml_return(aml_buffer(sizeof(byte_list), byte_list)));
+ }
+ aml_append(ifctx, ifctx2);
+
+ /*
+ * Function 1
+ * Creating a package with static values. The max supported QTG ID will
+ * be 1 and recommended QTG IDs are 0 and then 1.
+ * The values here are statically created to simplify emulation. Values
+ * from a real BIOS would be determined by the performance of all the
+ * present CXL memory and then assigned.
+ */
+ ifctx2 = aml_if(aml_equal(function, aml_int(1)));
+ {
+ Aml *pak, *pak1;
+
+ /*
+ * Return: A package containing two elements - a WORD that returns
+ * the maximum throttling group that the platform supports, and a
+ * package containing the QTG ID(s) that the platform recommends.
+ * Package {
+ * Max Supported QTG ID
+ * Package {QTG Recommendations}
+ * }
+ *
+ * While the SPEC specified WORD that hints at the value being
+ * 16bit, the ACPI dump of BIOS DSDT table showed that the values
+ * are integers with no specific size specification. aml_int() will
+ * be used for the values.
+ */
+ pak1 = aml_package(2);
+ /* Set QTG ID of 0 */
+ aml_append(pak1, aml_int(0));
+ /* Set QTG ID of 1 */
+ aml_append(pak1, aml_int(1));
+
+ pak = aml_package(2);
+ /* Set Max QTG 1 */
+ aml_append(pak, aml_int(1));
+ aml_append(pak, pak1);
+
+ aml_append(ifctx2, aml_return(pak));
+ }
+ aml_append(ifctx, ifctx2);
+ }
+ aml_append(method, ifctx);
+ aml_append(dev, method);
+}
+
+static void cedt_build_chbs(GArray *table_data, PXBCXLDev *cxl)
+{
+ PXBDev *pxb = PXB_DEV(cxl);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(cxl->cxl_host_bridge);
+ struct MemoryRegion *mr = sbd->mmio[0].memory;
+
+ /* Type */
+ build_append_int_noprefix(table_data, 0, 1);
+
+ /* Reserved */
+ build_append_int_noprefix(table_data, 0, 1);
+
+ /* Record Length */
+ build_append_int_noprefix(table_data, 32, 2);
+
+ /* UID - currently equal to bus number */
+ build_append_int_noprefix(table_data, pxb->bus_nr, 4);
+
+ /* Version */
+ build_append_int_noprefix(table_data, 1, 4);
+
+ /* Reserved */
+ build_append_int_noprefix(table_data, 0, 4);
+
+ /* Base - subregion within a container that is in PA space */
+ build_append_int_noprefix(table_data, mr->container->addr + mr->addr, 8);
+
+ /* Length */
+ build_append_int_noprefix(table_data, memory_region_size(mr), 8);
+}
+
+/*
+ * CFMWS entries in CXL 2.0 ECN: CEDT CFMWS & QTG _DSM.
+ * Interleave ways encoding in CXL 2.0 ECN: 3, 6, 12 and 16-way memory
+ * interleaving.
+ */
+static void cedt_build_cfmws(GArray *table_data, CXLState *cxls)
+{
+ GList *it;
+
+ for (it = cxls->fixed_windows; it; it = it->next) {
+ CXLFixedWindow *fw = it->data;
+ int i;
+
+ /* Type */
+ build_append_int_noprefix(table_data, 1, 1);
+
+ /* Reserved */
+ build_append_int_noprefix(table_data, 0, 1);
+
+ /* Record Length */
+ build_append_int_noprefix(table_data, 36 + 4 * fw->num_targets, 2);
+
+ /* Reserved */
+ build_append_int_noprefix(table_data, 0, 4);
+
+ /* Base HPA */
+ build_append_int_noprefix(table_data, fw->mr.addr, 8);
+
+ /* Window Size */
+ build_append_int_noprefix(table_data, fw->size, 8);
+
+ /* Host Bridge Interleave Ways */
+ build_append_int_noprefix(table_data, fw->enc_int_ways, 1);
+
+ /* Host Bridge Interleave Arithmetic */
+ build_append_int_noprefix(table_data, 0, 1);
+
+ /* Reserved */
+ build_append_int_noprefix(table_data, 0, 2);
+
+ /* Host Bridge Interleave Granularity */
+ build_append_int_noprefix(table_data, fw->enc_int_gran, 4);
+
+ /* Window Restrictions */
+ build_append_int_noprefix(table_data, 0x0f, 2); /* No restrictions */
+
+ /* QTG ID */
+ build_append_int_noprefix(table_data, 0, 2);
+
+ /* Host Bridge List (list of UIDs - currently bus_nr) */
+ for (i = 0; i < fw->num_targets; i++) {
+ g_assert(fw->target_hbs[i]);
+ build_append_int_noprefix(table_data, PXB_DEV(fw->target_hbs[i])->bus_nr, 4);
+ }
+ }
+}
+
+static int cxl_foreach_pxb_hb(Object *obj, void *opaque)
+{
+ Aml *cedt = opaque;
+
+ if (object_dynamic_cast(obj, TYPE_PXB_CXL_DEV)) {
+ cedt_build_chbs(cedt->buf, PXB_CXL_DEV(obj));
+ }
+
+ return 0;
+}
+
+void cxl_build_cedt(GArray *table_offsets, GArray *table_data,
+ BIOSLinker *linker, const char *oem_id,
+ const char *oem_table_id, CXLState *cxl_state)
+{
+ Aml *cedt;
+ AcpiTable table = { .sig = "CEDT", .rev = 1, .oem_id = oem_id,
+ .oem_table_id = oem_table_id };
+
+ acpi_add_table(table_offsets, table_data);
+ acpi_table_begin(&table, table_data);
+ cedt = init_aml_allocator();
+
+ /* reserve space for CEDT header */
+
+ object_child_foreach_recursive(object_get_root(), cxl_foreach_pxb_hb, cedt);
+ cedt_build_cfmws(cedt->buf, cxl_state);
+
+ /* copy AML table into ACPI tables blob and patch header there */
+ g_array_append_vals(table_data, cedt->buf->data, cedt->buf->len);
+ free_aml_allocator();
+
+ acpi_table_end(linker, &table);
+}
+
+static Aml *__build_cxl_osc_method(void)
+{
+ Aml *method, *if_uuid, *else_uuid, *if_arg1_not_1, *if_cxl, *if_caps_masked;
+ Aml *a_ctrl = aml_local(0);
+ Aml *a_cdw1 = aml_name("CDW1");
+
+ method = aml_method("_OSC", 4, AML_NOTSERIALIZED);
+ /* CDW1 is used for the return value so is present whether or not a match occurs */
+ aml_append(method, aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1"));
+
+ /*
+ * Generate shared section between:
+ * CXL 2.0 - 9.14.2.1.4 and
+ * PCI Firmware Specification 3.0
+ * 4.5.1. _OSC Interface for PCI Host Bridge Devices
+ * The _OSC interface for a PCI/PCI-X/PCI Express hierarchy is
+ * identified by the Universal Unique IDentifier (UUID)
+ * 33DB4D5B-1FF7-401C-9657-7441C03DD766
+ * The _OSC interface for a CXL Host bridge is
+ * identified by the UUID 68F2D50B-C469-4D8A-BD3D-941A103FD3FC
+ * A CXL Host bridge is compatible with a PCI host bridge so
+ * for the shared section match both.
+ */
+ if_uuid = aml_if(
+ aml_lor(aml_equal(aml_arg(0),
+ aml_touuid("33DB4D5B-1FF7-401C-9657-7441C03DD766")),
+ aml_equal(aml_arg(0),
+ aml_touuid("68F2D50B-C469-4D8A-BD3D-941A103FD3FC"))));
+ aml_append(if_uuid, aml_create_dword_field(aml_arg(3), aml_int(4), "CDW2"));
+ aml_append(if_uuid, aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3"));
+
+ aml_append(if_uuid, aml_store(aml_name("CDW3"), a_ctrl));
+
+ /*
+ *
+ * Allows OS control for all 5 features:
+ * PCIeHotplug SHPCHotplug PME AER PCIeCapability
+ */
+ aml_append(if_uuid, aml_and(a_ctrl, aml_int(0x1F), a_ctrl));
+
+ /*
+ * Check _OSC revision.
+ * PCI Firmware specification 3.3 and CXL 2.0 both use revision 1
+ * Unknown Revision is CDW1 - BIT (3)
+ */
+ if_arg1_not_1 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(0x1))));
+ aml_append(if_arg1_not_1, aml_or(a_cdw1, aml_int(0x08), a_cdw1));
+ aml_append(if_uuid, if_arg1_not_1);
+
+ if_caps_masked = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), a_ctrl)));
+
+ /* Capability bits were masked */
+ aml_append(if_caps_masked, aml_or(a_cdw1, aml_int(0x10), a_cdw1));
+ aml_append(if_uuid, if_caps_masked);
+
+ aml_append(if_uuid, aml_store(aml_name("CDW2"), aml_name("SUPP")));
+ aml_append(if_uuid, aml_store(aml_name("CDW3"), aml_name("CTRL")));
+
+ /* Update DWORD3 (the return value) */
+ aml_append(if_uuid, aml_store(a_ctrl, aml_name("CDW3")));
+
+ /* CXL only section as per CXL 2.0 - 9.14.2.1.4 */
+ if_cxl = aml_if(aml_equal(
+ aml_arg(0), aml_touuid("68F2D50B-C469-4D8A-BD3D-941A103FD3FC")));
+ /* CXL support field */
+ aml_append(if_cxl, aml_create_dword_field(aml_arg(3), aml_int(12), "CDW4"));
+ /* CXL capabilities */
+ aml_append(if_cxl, aml_create_dword_field(aml_arg(3), aml_int(16), "CDW5"));
+ aml_append(if_cxl, aml_store(aml_name("CDW4"), aml_name("SUPC")));
+ aml_append(if_cxl, aml_store(aml_name("CDW5"), aml_name("CTRC")));
+
+ /* CXL 2.0 Port/Device Register access */
+ aml_append(if_cxl,
+ aml_or(aml_name("CDW5"), aml_int(0x1), aml_name("CDW5")));
+ aml_append(if_uuid, if_cxl);
+
+ aml_append(if_uuid, aml_return(aml_arg(3)));
+ aml_append(method, if_uuid);
+
+ /*
+ * If no UUID matched, return Unrecognized UUID via Arg3 DWord 1
+ * ACPI 6.4 - 6.2.11
+ * Unrecognised UUID - BIT(2)
+ */
+ else_uuid = aml_else();
+
+ aml_append(else_uuid,
+ aml_or(aml_name("CDW1"), aml_int(0x4), aml_name("CDW1")));
+ aml_append(else_uuid, aml_return(aml_arg(3)));
+ aml_append(method, else_uuid);
+
+ return method;
+}
+
+void build_cxl_osc_method(Aml *dev)
+{
+ aml_append(dev, aml_name_decl("SUPP", aml_int(0)));
+ aml_append(dev, aml_name_decl("CTRL", aml_int(0)));
+ aml_append(dev, aml_name_decl("SUPC", aml_int(0)));
+ aml_append(dev, aml_name_decl("CTRC", aml_int(0)));
+ aml_append(dev, __build_cxl_osc_method());
+}
diff --git a/hw/acpi/erst.c b/hw/acpi/erst.c
new file mode 100644
index 0000000000..b2f1b13630
--- /dev/null
+++ b/hw/acpi/erst.c
@@ -0,0 +1,1059 @@
+/*
+ * ACPI Error Record Serialization Table, ERST, Implementation
+ *
+ * ACPI ERST introduced in ACPI 4.0, June 16, 2009.
+ * ACPI Platform Error Interfaces : Error Serialization
+ *
+ * Copyright (c) 2021 Oracle and/or its affiliates.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/qdev-core.h"
+#include "exec/memory.h"
+#include "qom/object.h"
+#include "hw/pci/pci_device.h"
+#include "qom/object_interfaces.h"
+#include "qemu/error-report.h"
+#include "migration/vmstate.h"
+#include "hw/qdev-properties.h"
+#include "hw/acpi/acpi.h"
+#include "hw/acpi/acpi-defs.h"
+#include "hw/acpi/aml-build.h"
+#include "hw/acpi/bios-linker-loader.h"
+#include "exec/address-spaces.h"
+#include "sysemu/hostmem.h"
+#include "hw/acpi/erst.h"
+#include "trace.h"
+
+/* ACPI 4.0: Table 17-16 Serialization Actions */
+#define ACTION_BEGIN_WRITE_OPERATION 0x0
+#define ACTION_BEGIN_READ_OPERATION 0x1
+#define ACTION_BEGIN_CLEAR_OPERATION 0x2
+#define ACTION_END_OPERATION 0x3
+#define ACTION_SET_RECORD_OFFSET 0x4
+#define ACTION_EXECUTE_OPERATION 0x5
+#define ACTION_CHECK_BUSY_STATUS 0x6
+#define ACTION_GET_COMMAND_STATUS 0x7
+#define ACTION_GET_RECORD_IDENTIFIER 0x8
+#define ACTION_SET_RECORD_IDENTIFIER 0x9
+#define ACTION_GET_RECORD_COUNT 0xA
+#define ACTION_BEGIN_DUMMY_WRITE_OPERATION 0xB
+#define ACTION_RESERVED 0xC
+#define ACTION_GET_ERROR_LOG_ADDRESS_RANGE 0xD
+#define ACTION_GET_ERROR_LOG_ADDRESS_LENGTH 0xE
+#define ACTION_GET_ERROR_LOG_ADDRESS_RANGE_ATTRIBUTES 0xF
+#define ACTION_GET_EXECUTE_OPERATION_TIMINGS 0x10 /* ACPI 6.3 */
+
+/* ACPI 4.0: Table 17-17 Command Status Definitions */
+#define STATUS_SUCCESS 0x00
+#define STATUS_NOT_ENOUGH_SPACE 0x01
+#define STATUS_HARDWARE_NOT_AVAILABLE 0x02
+#define STATUS_FAILED 0x03
+#define STATUS_RECORD_STORE_EMPTY 0x04
+#define STATUS_RECORD_NOT_FOUND 0x05
+
+/* ACPI 4.0: Table 17-19 Serialization Instructions */
+#define INST_READ_REGISTER 0x00
+#define INST_READ_REGISTER_VALUE 0x01
+#define INST_WRITE_REGISTER 0x02
+#define INST_WRITE_REGISTER_VALUE 0x03
+#define INST_NOOP 0x04
+#define INST_LOAD_VAR1 0x05
+#define INST_LOAD_VAR2 0x06
+#define INST_STORE_VAR1 0x07
+#define INST_ADD 0x08
+#define INST_SUBTRACT 0x09
+#define INST_ADD_VALUE 0x0A
+#define INST_SUBTRACT_VALUE 0x0B
+#define INST_STALL 0x0C
+#define INST_STALL_WHILE_TRUE 0x0D
+#define INST_SKIP_NEXT_INSTRUCTION_IF_TRUE 0x0E
+#define INST_GOTO 0x0F
+#define INST_SET_SRC_ADDRESS_BASE 0x10
+#define INST_SET_DST_ADDRESS_BASE 0x11
+#define INST_MOVE_DATA 0x12
+
+/* UEFI 2.1: Appendix N Common Platform Error Record */
+#define UEFI_CPER_RECORD_MIN_SIZE 128U
+#define UEFI_CPER_RECORD_LENGTH_OFFSET 20U
+#define UEFI_CPER_RECORD_ID_OFFSET 96U
+
+/*
+ * NOTE that when accessing CPER fields within a record, memcpy()
+ * is utilized to avoid a possible misaligned access on the host.
+ */
+
+/*
+ * This implementation is an ACTION (cmd) and VALUE (data)
+ * interface consisting of just two 64-bit registers.
+ */
+#define ERST_REG_SIZE (16UL)
+#define ERST_ACTION_OFFSET (0UL) /* action (cmd) */
+#define ERST_VALUE_OFFSET (8UL) /* argument/value (data) */
+
+/*
+ * ERST_RECORD_SIZE is the buffer size for exchanging ERST
+ * record contents. Thus, it defines the maximum record size.
+ * As this is mapped through a PCI BAR, it must be a power of
+ * two and larger than UEFI_CPER_RECORD_MIN_SIZE.
+ * The backing storage is divided into fixed size "slots",
+ * each ERST_RECORD_SIZE in length, and each "slot"
+ * storing a single record. No attempt at optimizing storage
+ * through compression, compaction, etc is attempted.
+ * NOTE that slot 0 is reserved for the backing storage header.
+ * Depending upon the size of the backing storage, additional
+ * slots will be part of the slot 0 header in order to account
+ * for a record_id for each available remaining slot.
+ */
+/* 8KiB records, not too small, not too big */
+#define ERST_RECORD_SIZE (8192UL)
+
+#define ACPI_ERST_MEMDEV_PROP "memdev"
+#define ACPI_ERST_RECORD_SIZE_PROP "record_size"
+
+/*
+ * From the ACPI ERST spec sections:
+ * A record id of all 0s is used to indicate 'unspecified' record id.
+ * A record id of all 1s is used to indicate empty or end.
+ */
+#define ERST_UNSPECIFIED_RECORD_ID (0UL)
+#define ERST_EMPTY_END_RECORD_ID (~0UL)
+
+#define ERST_IS_VALID_RECORD_ID(rid) \
+ ((rid != ERST_UNSPECIFIED_RECORD_ID) && \
+ (rid != ERST_EMPTY_END_RECORD_ID))
+
+/*
+ * Implementation-specific definitions and types.
+ * Values are arbitrary and chosen for this implementation.
+ * See erst.rst documentation for details.
+ */
+#define ERST_EXECUTE_OPERATION_MAGIC 0x9CUL
+#define ERST_STORE_MAGIC 0x524F545354535245UL /* ERSTSTOR */
+typedef struct {
+ uint64_t magic;
+ uint32_t record_size;
+ uint32_t storage_offset; /* offset to record storage beyond header */
+ uint16_t version;
+ uint16_t reserved;
+ uint32_t record_count;
+ uint64_t map[]; /* contains record_ids, and position indicates index */
+} __attribute__((packed)) ERSTStorageHeader;
+
+/*
+ * Object cast macro
+ */
+#define ACPIERST(obj) \
+ OBJECT_CHECK(ERSTDeviceState, (obj), TYPE_ACPI_ERST)
+
+/*
+ * Main ERST device state structure
+ */
+typedef struct {
+ PCIDevice parent_obj;
+
+ /* Backend storage */
+ HostMemoryBackend *hostmem;
+ MemoryRegion *hostmem_mr;
+ uint32_t storage_size;
+ uint32_t default_record_size;
+
+ /* Programming registers */
+ MemoryRegion iomem_mr;
+
+ /* Exchange buffer */
+ MemoryRegion exchange_mr;
+
+ /* Interface state */
+ uint8_t operation;
+ uint8_t busy_status;
+ uint8_t command_status;
+ uint32_t record_offset;
+ uint64_t reg_action;
+ uint64_t reg_value;
+ uint64_t record_identifier;
+ ERSTStorageHeader *header;
+ unsigned first_record_index;
+ unsigned last_record_index;
+ unsigned next_record_index;
+
+} ERSTDeviceState;
+
+/*******************************************************************/
+/*******************************************************************/
+typedef struct {
+ GArray *table_data;
+ pcibus_t bar;
+ uint8_t instruction;
+ uint8_t flags;
+ uint8_t register_bit_width;
+ pcibus_t register_offset;
+} BuildSerializationInstructionEntry;
+
+/* ACPI 4.0: 17.4.1.2 Serialization Instruction Entries */
+static void build_serialization_instruction(
+ BuildSerializationInstructionEntry *e,
+ uint8_t serialization_action,
+ uint64_t value)
+{
+ /* ACPI 4.0: Table 17-18 Serialization Instruction Entry */
+ struct AcpiGenericAddress gas;
+ uint64_t mask;
+
+ /* Serialization Action */
+ build_append_int_noprefix(e->table_data, serialization_action, 1);
+ /* Instruction */
+ build_append_int_noprefix(e->table_data, e->instruction, 1);
+ /* Flags */
+ build_append_int_noprefix(e->table_data, e->flags, 1);
+ /* Reserved */
+ build_append_int_noprefix(e->table_data, 0, 1);
+ /* Register Region */
+ gas.space_id = AML_SYSTEM_MEMORY;
+ gas.bit_width = e->register_bit_width;
+ gas.bit_offset = 0;
+ gas.access_width = (uint8_t)ctz32(e->register_bit_width) - 2;
+ gas.address = (uint64_t)(e->bar + e->register_offset);
+ build_append_gas_from_struct(e->table_data, &gas);
+ /* Value */
+ build_append_int_noprefix(e->table_data, value, 8);
+ /* Mask */
+ mask = (1ULL << (e->register_bit_width - 1) << 1) - 1;
+ build_append_int_noprefix(e->table_data, mask, 8);
+}
+
+/* ACPI 4.0: 17.4.1 Serialization Action Table */
+void build_erst(GArray *table_data, BIOSLinker *linker, Object *erst_dev,
+ const char *oem_id, const char *oem_table_id)
+{
+ /*
+ * Serialization Action Table
+ * The serialization action table must be generated first
+ * so that its size can be known in order to populate the
+ * Instruction Entry Count field.
+ */
+ unsigned action;
+ GArray *table_instruction_data = g_array_new(FALSE, FALSE, sizeof(char));
+ pcibus_t bar0 = pci_get_bar_addr(PCI_DEVICE(erst_dev), 0);
+ AcpiTable table = { .sig = "ERST", .rev = 1, .oem_id = oem_id,
+ .oem_table_id = oem_table_id };
+ /* Contexts for the different ways ACTION and VALUE are accessed */
+ BuildSerializationInstructionEntry rd_value_32_val = {
+ .table_data = table_instruction_data, .bar = bar0, .flags = 0,
+ .instruction = INST_READ_REGISTER_VALUE,
+ .register_bit_width = 32,
+ .register_offset = ERST_VALUE_OFFSET,
+ };
+ BuildSerializationInstructionEntry rd_value_32 = {
+ .table_data = table_instruction_data, .bar = bar0, .flags = 0,
+ .instruction = INST_READ_REGISTER,
+ .register_bit_width = 32,
+ .register_offset = ERST_VALUE_OFFSET,
+ };
+ BuildSerializationInstructionEntry rd_value_64 = {
+ .table_data = table_instruction_data, .bar = bar0, .flags = 0,
+ .instruction = INST_READ_REGISTER,
+ .register_bit_width = 64,
+ .register_offset = ERST_VALUE_OFFSET,
+ };
+ BuildSerializationInstructionEntry wr_value_32_val = {
+ .table_data = table_instruction_data, .bar = bar0, .flags = 0,
+ .instruction = INST_WRITE_REGISTER_VALUE,
+ .register_bit_width = 32,
+ .register_offset = ERST_VALUE_OFFSET,
+ };
+ BuildSerializationInstructionEntry wr_value_32 = {
+ .table_data = table_instruction_data, .bar = bar0, .flags = 0,
+ .instruction = INST_WRITE_REGISTER,
+ .register_bit_width = 32,
+ .register_offset = ERST_VALUE_OFFSET,
+ };
+ BuildSerializationInstructionEntry wr_value_64 = {
+ .table_data = table_instruction_data, .bar = bar0, .flags = 0,
+ .instruction = INST_WRITE_REGISTER,
+ .register_bit_width = 64,
+ .register_offset = ERST_VALUE_OFFSET,
+ };
+ BuildSerializationInstructionEntry wr_action = {
+ .table_data = table_instruction_data, .bar = bar0, .flags = 0,
+ .instruction = INST_WRITE_REGISTER_VALUE,
+ .register_bit_width = 32,
+ .register_offset = ERST_ACTION_OFFSET,
+ };
+
+ trace_acpi_erst_pci_bar_0(bar0);
+
+ /* Serialization Instruction Entries */
+ action = ACTION_BEGIN_WRITE_OPERATION;
+ build_serialization_instruction(&wr_action, action, action);
+
+ action = ACTION_BEGIN_READ_OPERATION;
+ build_serialization_instruction(&wr_action, action, action);
+
+ action = ACTION_BEGIN_CLEAR_OPERATION;
+ build_serialization_instruction(&wr_action, action, action);
+
+ action = ACTION_END_OPERATION;
+ build_serialization_instruction(&wr_action, action, action);
+
+ action = ACTION_SET_RECORD_OFFSET;
+ build_serialization_instruction(&wr_value_32, action, 0);
+ build_serialization_instruction(&wr_action, action, action);
+
+ action = ACTION_EXECUTE_OPERATION;
+ build_serialization_instruction(&wr_value_32_val, action,
+ ERST_EXECUTE_OPERATION_MAGIC);
+ build_serialization_instruction(&wr_action, action, action);
+
+ action = ACTION_CHECK_BUSY_STATUS;
+ build_serialization_instruction(&wr_action, action, action);
+ build_serialization_instruction(&rd_value_32_val, action, 0x01);
+
+ action = ACTION_GET_COMMAND_STATUS;
+ build_serialization_instruction(&wr_action, action, action);
+ build_serialization_instruction(&rd_value_32, action, 0);
+
+ action = ACTION_GET_RECORD_IDENTIFIER;
+ build_serialization_instruction(&wr_action, action, action);
+ build_serialization_instruction(&rd_value_64, action, 0);
+
+ action = ACTION_SET_RECORD_IDENTIFIER;
+ build_serialization_instruction(&wr_value_64, action, 0);
+ build_serialization_instruction(&wr_action, action, action);
+
+ action = ACTION_GET_RECORD_COUNT;
+ build_serialization_instruction(&wr_action, action, action);
+ build_serialization_instruction(&rd_value_32, action, 0);
+
+ action = ACTION_BEGIN_DUMMY_WRITE_OPERATION;
+ build_serialization_instruction(&wr_action, action, action);
+
+ action = ACTION_GET_ERROR_LOG_ADDRESS_RANGE;
+ build_serialization_instruction(&wr_action, action, action);
+ build_serialization_instruction(&rd_value_64, action, 0);
+
+ action = ACTION_GET_ERROR_LOG_ADDRESS_LENGTH;
+ build_serialization_instruction(&wr_action, action, action);
+ build_serialization_instruction(&rd_value_64, action, 0);
+
+ action = ACTION_GET_ERROR_LOG_ADDRESS_RANGE_ATTRIBUTES;
+ build_serialization_instruction(&wr_action, action, action);
+ build_serialization_instruction(&rd_value_32, action, 0);
+
+ action = ACTION_GET_EXECUTE_OPERATION_TIMINGS;
+ build_serialization_instruction(&wr_action, action, action);
+ build_serialization_instruction(&rd_value_64, action, 0);
+
+ /* Serialization Header */
+ acpi_table_begin(&table, table_data);
+
+ /* Serialization Header Size */
+ build_append_int_noprefix(table_data, 48, 4);
+
+ /* Reserved */
+ build_append_int_noprefix(table_data, 0, 4);
+
+ /*
+ * Instruction Entry Count
+ * Each instruction entry is 32 bytes
+ */
+ g_assert((table_instruction_data->len) % 32 == 0);
+ build_append_int_noprefix(table_data,
+ (table_instruction_data->len / 32), 4);
+
+ /* Serialization Instruction Entries */
+ g_array_append_vals(table_data, table_instruction_data->data,
+ table_instruction_data->len);
+ g_array_free(table_instruction_data, TRUE);
+
+ acpi_table_end(linker, &table);
+}
+
+/*******************************************************************/
+/*******************************************************************/
+static uint8_t *get_nvram_ptr_by_index(ERSTDeviceState *s, unsigned index)
+{
+ uint8_t *rc = NULL;
+ off_t offset = (index * le32_to_cpu(s->header->record_size));
+
+ g_assert(offset < s->storage_size);
+
+ rc = memory_region_get_ram_ptr(s->hostmem_mr);
+ rc += offset;
+
+ return rc;
+}
+
+static void make_erst_storage_header(ERSTDeviceState *s)
+{
+ ERSTStorageHeader *header = s->header;
+ unsigned mapsz, headersz;
+
+ header->magic = cpu_to_le64(ERST_STORE_MAGIC);
+ header->record_size = cpu_to_le32(s->default_record_size);
+ header->version = cpu_to_le16(0x0100);
+ header->reserved = cpu_to_le16(0x0000);
+
+ /* Compute mapsize */
+ mapsz = s->storage_size / s->default_record_size;
+ mapsz *= sizeof(uint64_t);
+ /* Compute header+map size */
+ headersz = sizeof(ERSTStorageHeader) + mapsz;
+ /* Round up to nearest integer multiple of ERST_RECORD_SIZE */
+ headersz = QEMU_ALIGN_UP(headersz, s->default_record_size);
+ header->storage_offset = cpu_to_le32(headersz);
+
+ /*
+ * The HostMemoryBackend initializes contents to zero,
+ * so all record_ids stashed in the map are zero'd.
+ * As well the record_count is zero. Properly initialized.
+ */
+}
+
+static void check_erst_backend_storage(ERSTDeviceState *s, Error **errp)
+{
+ ERSTStorageHeader *header;
+ uint32_t record_size;
+
+ header = memory_region_get_ram_ptr(s->hostmem_mr);
+ s->header = header;
+
+ /* Ensure pointer to header is 64-bit aligned */
+ g_assert(QEMU_PTR_IS_ALIGNED(header, sizeof(uint64_t)));
+
+ /*
+ * Check if header is uninitialized; HostMemoryBackend inits to 0
+ */
+ if (le64_to_cpu(header->magic) == 0UL) {
+ make_erst_storage_header(s);
+ }
+
+ /* Validity check record_size */
+ record_size = le32_to_cpu(header->record_size);
+ if (!(
+ (record_size) && /* non zero */
+ (record_size >= UEFI_CPER_RECORD_MIN_SIZE) &&
+ (((record_size - 1) & record_size) == 0) && /* is power of 2 */
+ (record_size >= 4096) /* PAGE_SIZE */
+ )) {
+ error_setg(errp, "ERST record_size %u is invalid", record_size);
+ return;
+ }
+
+ /* Validity check header */
+ if (!(
+ (le64_to_cpu(header->magic) == ERST_STORE_MAGIC) &&
+ ((le32_to_cpu(header->storage_offset) % record_size) == 0) &&
+ (le16_to_cpu(header->version) == 0x0100) &&
+ (le16_to_cpu(header->reserved) == 0)
+ )) {
+ error_setg(errp, "ERST backend storage header is invalid");
+ return;
+ }
+
+ /* Check storage_size against record_size */
+ if (((s->storage_size % record_size) != 0) ||
+ (record_size > s->storage_size)) {
+ error_setg(errp, "ACPI ERST requires storage size be multiple of "
+ "record size (%uKiB)", record_size);
+ return;
+ }
+
+ /* Compute offset of first and last record storage slot */
+ s->first_record_index = le32_to_cpu(header->storage_offset)
+ / record_size;
+ s->last_record_index = (s->storage_size / record_size);
+}
+
+static void update_map_entry(ERSTDeviceState *s, unsigned index,
+ uint64_t record_id)
+{
+ if (index < s->last_record_index) {
+ s->header->map[index] = cpu_to_le64(record_id);
+ }
+}
+
+static unsigned find_next_empty_record_index(ERSTDeviceState *s)
+{
+ unsigned rc = 0; /* 0 not a valid index */
+ unsigned index = s->first_record_index;
+
+ for (; index < s->last_record_index; ++index) {
+ if (le64_to_cpu(s->header->map[index]) == ERST_UNSPECIFIED_RECORD_ID) {
+ rc = index;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static unsigned lookup_erst_record(ERSTDeviceState *s,
+ uint64_t record_identifier)
+{
+ unsigned rc = 0; /* 0 not a valid index */
+
+ /* Find the record_identifier in the map */
+ if (record_identifier != ERST_UNSPECIFIED_RECORD_ID) {
+ /*
+ * Count number of valid records encountered, and
+ * short-circuit the loop if identifier not found
+ */
+ uint32_t record_count = le32_to_cpu(s->header->record_count);
+ unsigned count = 0;
+ unsigned index;
+ for (index = s->first_record_index; index < s->last_record_index &&
+ count < record_count; ++index) {
+ if (le64_to_cpu(s->header->map[index]) == record_identifier) {
+ rc = index;
+ break;
+ }
+ if (le64_to_cpu(s->header->map[index]) !=
+ ERST_UNSPECIFIED_RECORD_ID) {
+ ++count;
+ }
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * ACPI 4.0: 17.4.1.1 Serialization Actions, also see
+ * ACPI 4.0: 17.4.2.2 Operations - Reading 6.c and 2.c
+ */
+static unsigned get_next_record_identifier(ERSTDeviceState *s,
+ uint64_t *record_identifier, bool first)
+{
+ unsigned found = 0;
+ unsigned index;
+
+ /* For operations needing to return 'first' record identifier */
+ if (first) {
+ /* Reset initial index to beginning */
+ s->next_record_index = s->first_record_index;
+ }
+ index = s->next_record_index;
+
+ *record_identifier = ERST_EMPTY_END_RECORD_ID;
+
+ if (le32_to_cpu(s->header->record_count)) {
+ for (; index < s->last_record_index; ++index) {
+ if (le64_to_cpu(s->header->map[index]) !=
+ ERST_UNSPECIFIED_RECORD_ID) {
+ /* where to start next time */
+ s->next_record_index = index + 1;
+ *record_identifier = le64_to_cpu(s->header->map[index]);
+ found = 1;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ /* at end (ie scan complete), reset */
+ s->next_record_index = s->first_record_index;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+/* ACPI 4.0: 17.4.2.3 Operations - Clearing */
+static unsigned clear_erst_record(ERSTDeviceState *s)
+{
+ unsigned rc = STATUS_RECORD_NOT_FOUND;
+ unsigned index;
+
+ /* Check for valid record identifier */
+ if (!ERST_IS_VALID_RECORD_ID(s->record_identifier)) {
+ return STATUS_FAILED;
+ }
+
+ index = lookup_erst_record(s, s->record_identifier);
+ if (index) {
+ /* No need to wipe record, just invalidate its map entry */
+ uint32_t record_count;
+ update_map_entry(s, index, ERST_UNSPECIFIED_RECORD_ID);
+ record_count = le32_to_cpu(s->header->record_count);
+ record_count -= 1;
+ s->header->record_count = cpu_to_le32(record_count);
+ rc = STATUS_SUCCESS;
+ }
+
+ return rc;
+}
+
+/* ACPI 4.0: 17.4.2.2 Operations - Reading */
+static unsigned read_erst_record(ERSTDeviceState *s)
+{
+ unsigned rc = STATUS_RECORD_NOT_FOUND;
+ unsigned exchange_length;
+ unsigned index;
+
+ /* Check if backend storage is empty */
+ if (le32_to_cpu(s->header->record_count) == 0) {
+ return STATUS_RECORD_STORE_EMPTY;
+ }
+
+ exchange_length = memory_region_size(&s->exchange_mr);
+
+ /* Check for record identifier of all 0s */
+ if (s->record_identifier == ERST_UNSPECIFIED_RECORD_ID) {
+ /* Set to 'first' record in storage */
+ get_next_record_identifier(s, &s->record_identifier, true);
+ /* record_identifier is now a valid id, or all 1s */
+ }
+
+ /* Check for record identifier of all 1s */
+ if (s->record_identifier == ERST_EMPTY_END_RECORD_ID) {
+ return STATUS_FAILED;
+ }
+
+ /* Validate record_offset */
+ if (s->record_offset > (exchange_length - UEFI_CPER_RECORD_MIN_SIZE)) {
+ return STATUS_FAILED;
+ }
+
+ index = lookup_erst_record(s, s->record_identifier);
+ if (index) {
+ uint8_t *nvram;
+ uint8_t *exchange;
+ uint32_t record_length;
+
+ /* Obtain pointer to the exchange buffer */
+ exchange = memory_region_get_ram_ptr(&s->exchange_mr);
+ exchange += s->record_offset;
+ /* Obtain pointer to slot in storage */
+ nvram = get_nvram_ptr_by_index(s, index);
+ /* Validate CPER record_length */
+ memcpy((uint8_t *)&record_length,
+ &nvram[UEFI_CPER_RECORD_LENGTH_OFFSET],
+ sizeof(uint32_t));
+ record_length = le32_to_cpu(record_length);
+ if (record_length < UEFI_CPER_RECORD_MIN_SIZE) {
+ rc = STATUS_FAILED;
+ }
+ if (record_length > exchange_length - s->record_offset) {
+ rc = STATUS_FAILED;
+ }
+ /* If all is ok, copy the record to the exchange buffer */
+ if (rc != STATUS_FAILED) {
+ memcpy(exchange, nvram, record_length);
+ rc = STATUS_SUCCESS;
+ }
+ } else {
+ /*
+ * See "Reading : 'The steps performed by the platform ...' 2.c"
+ * Set to 'first' record in storage
+ */
+ get_next_record_identifier(s, &s->record_identifier, true);
+ }
+
+ return rc;
+}
+
+/* ACPI 4.0: 17.4.2.1 Operations - Writing */
+static unsigned write_erst_record(ERSTDeviceState *s)
+{
+ unsigned rc = STATUS_FAILED;
+ unsigned exchange_length;
+ unsigned index;
+ uint64_t record_identifier;
+ uint32_t record_length;
+ uint8_t *exchange;
+ uint8_t *nvram = NULL;
+ bool record_found = false;
+
+ exchange_length = memory_region_size(&s->exchange_mr);
+
+ /* Validate record_offset */
+ if (s->record_offset > (exchange_length - UEFI_CPER_RECORD_MIN_SIZE)) {
+ return STATUS_FAILED;
+ }
+
+ /* Obtain pointer to record in the exchange buffer */
+ exchange = memory_region_get_ram_ptr(&s->exchange_mr);
+ exchange += s->record_offset;
+
+ /* Validate CPER record_length */
+ memcpy((uint8_t *)&record_length, &exchange[UEFI_CPER_RECORD_LENGTH_OFFSET],
+ sizeof(uint32_t));
+ record_length = le32_to_cpu(record_length);
+ if (record_length < UEFI_CPER_RECORD_MIN_SIZE) {
+ return STATUS_FAILED;
+ }
+ if (record_length > exchange_length - s->record_offset) {
+ return STATUS_FAILED;
+ }
+
+ /* Extract record identifier */
+ memcpy((uint8_t *)&record_identifier, &exchange[UEFI_CPER_RECORD_ID_OFFSET],
+ sizeof(uint64_t));
+ record_identifier = le64_to_cpu(record_identifier);
+
+ /* Check for valid record identifier */
+ if (!ERST_IS_VALID_RECORD_ID(record_identifier)) {
+ return STATUS_FAILED;
+ }
+
+ index = lookup_erst_record(s, record_identifier);
+ if (index) {
+ /* Record found, overwrite existing record */
+ nvram = get_nvram_ptr_by_index(s, index);
+ record_found = true;
+ } else {
+ /* Record not found, not an overwrite, allocate for write */
+ index = find_next_empty_record_index(s);
+ if (index) {
+ nvram = get_nvram_ptr_by_index(s, index);
+ } else {
+ /* All slots are occupied */
+ rc = STATUS_NOT_ENOUGH_SPACE;
+ }
+ }
+ if (nvram) {
+ /* Write the record into the slot */
+ memcpy(nvram, exchange, record_length);
+ memset(nvram + record_length, 0xFF, exchange_length - record_length);
+ /* If a new record, increment the record_count */
+ if (!record_found) {
+ uint32_t record_count;
+ record_count = le32_to_cpu(s->header->record_count);
+ record_count += 1; /* writing new record */
+ s->header->record_count = cpu_to_le32(record_count);
+ }
+ update_map_entry(s, index, record_identifier);
+ rc = STATUS_SUCCESS;
+ }
+
+ return rc;
+}
+
+/*******************************************************************/
+
+static uint64_t erst_rd_reg64(hwaddr addr,
+ uint64_t reg, unsigned size)
+{
+ uint64_t rdval;
+ uint64_t mask;
+ unsigned shift;
+
+ if (size == sizeof(uint64_t)) {
+ /* 64b access */
+ mask = 0xFFFFFFFFFFFFFFFFUL;
+ shift = 0;
+ } else {
+ /* 32b access */
+ mask = 0x00000000FFFFFFFFUL;
+ shift = ((addr & 0x4) == 0x4) ? 32 : 0;
+ }
+
+ rdval = reg;
+ rdval >>= shift;
+ rdval &= mask;
+
+ return rdval;
+}
+
+static uint64_t erst_wr_reg64(hwaddr addr,
+ uint64_t reg, uint64_t val, unsigned size)
+{
+ uint64_t wrval;
+ uint64_t mask;
+ unsigned shift;
+
+ if (size == sizeof(uint64_t)) {
+ /* 64b access */
+ mask = 0xFFFFFFFFFFFFFFFFUL;
+ shift = 0;
+ } else {
+ /* 32b access */
+ mask = 0x00000000FFFFFFFFUL;
+ shift = ((addr & 0x4) == 0x4) ? 32 : 0;
+ }
+
+ val &= mask;
+ val <<= shift;
+ mask <<= shift;
+ wrval = reg;
+ wrval &= ~mask;
+ wrval |= val;
+
+ return wrval;
+}
+
+static void erst_reg_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ ERSTDeviceState *s = (ERSTDeviceState *)opaque;
+
+ /*
+ * NOTE: All actions/operations/side effects happen on the WRITE,
+ * by this implementation's design. The READs simply return the
+ * reg_value contents.
+ */
+ trace_acpi_erst_reg_write(addr, val, size);
+
+ switch (addr) {
+ case ERST_VALUE_OFFSET + 0:
+ case ERST_VALUE_OFFSET + 4:
+ s->reg_value = erst_wr_reg64(addr, s->reg_value, val, size);
+ break;
+ case ERST_ACTION_OFFSET + 0:
+ /*
+ * NOTE: all valid values written to this register are of the
+ * ACTION_* variety. Thus there is no need to make this a 64-bit
+ * register, 32-bits is appropriate. As such ERST_ACTION_OFFSET+4
+ * is not needed.
+ */
+ switch (val) {
+ case ACTION_BEGIN_WRITE_OPERATION:
+ case ACTION_BEGIN_READ_OPERATION:
+ case ACTION_BEGIN_CLEAR_OPERATION:
+ case ACTION_BEGIN_DUMMY_WRITE_OPERATION:
+ case ACTION_END_OPERATION:
+ s->operation = val;
+ break;
+ case ACTION_SET_RECORD_OFFSET:
+ s->record_offset = s->reg_value;
+ break;
+ case ACTION_EXECUTE_OPERATION:
+ if ((uint8_t)s->reg_value == ERST_EXECUTE_OPERATION_MAGIC) {
+ s->busy_status = 1;
+ switch (s->operation) {
+ case ACTION_BEGIN_WRITE_OPERATION:
+ s->command_status = write_erst_record(s);
+ break;
+ case ACTION_BEGIN_READ_OPERATION:
+ s->command_status = read_erst_record(s);
+ break;
+ case ACTION_BEGIN_CLEAR_OPERATION:
+ s->command_status = clear_erst_record(s);
+ break;
+ case ACTION_BEGIN_DUMMY_WRITE_OPERATION:
+ s->command_status = STATUS_SUCCESS;
+ break;
+ case ACTION_END_OPERATION:
+ s->command_status = STATUS_SUCCESS;
+ break;
+ default:
+ s->command_status = STATUS_FAILED;
+ break;
+ }
+ s->busy_status = 0;
+ }
+ break;
+ case ACTION_CHECK_BUSY_STATUS:
+ s->reg_value = s->busy_status;
+ break;
+ case ACTION_GET_COMMAND_STATUS:
+ s->reg_value = s->command_status;
+ break;
+ case ACTION_GET_RECORD_IDENTIFIER:
+ s->command_status = get_next_record_identifier(s,
+ &s->reg_value, false);
+ break;
+ case ACTION_SET_RECORD_IDENTIFIER:
+ s->record_identifier = s->reg_value;
+ break;
+ case ACTION_GET_RECORD_COUNT:
+ s->reg_value = le32_to_cpu(s->header->record_count);
+ break;
+ case ACTION_GET_ERROR_LOG_ADDRESS_RANGE:
+ s->reg_value = (hwaddr)pci_get_bar_addr(PCI_DEVICE(s), 1);
+ break;
+ case ACTION_GET_ERROR_LOG_ADDRESS_LENGTH:
+ s->reg_value = le32_to_cpu(s->header->record_size);
+ break;
+ case ACTION_GET_ERROR_LOG_ADDRESS_RANGE_ATTRIBUTES:
+ s->reg_value = 0x0; /* intentional, not NVRAM mode */
+ break;
+ case ACTION_GET_EXECUTE_OPERATION_TIMINGS:
+ s->reg_value =
+ (100ULL << 32) | /* 100us max time */
+ (10ULL << 0) ; /* 10us min time */
+ break;
+ default:
+ /* Unknown action/command, NOP */
+ break;
+ }
+ break;
+ default:
+ /* This should not happen, but if it does, NOP */
+ break;
+ }
+}
+
+static uint64_t erst_reg_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ ERSTDeviceState *s = (ERSTDeviceState *)opaque;
+ uint64_t val = 0;
+
+ switch (addr) {
+ case ERST_ACTION_OFFSET + 0:
+ case ERST_ACTION_OFFSET + 4:
+ val = erst_rd_reg64(addr, s->reg_action, size);
+ break;
+ case ERST_VALUE_OFFSET + 0:
+ case ERST_VALUE_OFFSET + 4:
+ val = erst_rd_reg64(addr, s->reg_value, size);
+ break;
+ default:
+ break;
+ }
+ trace_acpi_erst_reg_read(addr, val, size);
+ return val;
+}
+
+static const MemoryRegionOps erst_reg_ops = {
+ .read = erst_reg_read,
+ .write = erst_reg_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+/*******************************************************************/
+/*******************************************************************/
+static int erst_post_load(void *opaque, int version_id)
+{
+ ERSTDeviceState *s = opaque;
+
+ /* Recompute pointer to header */
+ s->header = (ERSTStorageHeader *)get_nvram_ptr_by_index(s, 0);
+ trace_acpi_erst_post_load(s->header, le32_to_cpu(s->header->record_size));
+
+ return 0;
+}
+
+static const VMStateDescription erst_vmstate = {
+ .name = "acpi-erst",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .post_load = erst_post_load,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT8(operation, ERSTDeviceState),
+ VMSTATE_UINT8(busy_status, ERSTDeviceState),
+ VMSTATE_UINT8(command_status, ERSTDeviceState),
+ VMSTATE_UINT32(record_offset, ERSTDeviceState),
+ VMSTATE_UINT64(reg_action, ERSTDeviceState),
+ VMSTATE_UINT64(reg_value, ERSTDeviceState),
+ VMSTATE_UINT64(record_identifier, ERSTDeviceState),
+ VMSTATE_UINT32(next_record_index, ERSTDeviceState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void erst_realizefn(PCIDevice *pci_dev, Error **errp)
+{
+ ERRP_GUARD();
+ ERSTDeviceState *s = ACPIERST(pci_dev);
+
+ trace_acpi_erst_realizefn_in();
+
+ if (!s->hostmem) {
+ error_setg(errp, "'" ACPI_ERST_MEMDEV_PROP "' property is not set");
+ return;
+ } else if (host_memory_backend_is_mapped(s->hostmem)) {
+ error_setg(errp, "can't use already busy memdev: %s",
+ object_get_canonical_path_component(OBJECT(s->hostmem)));
+ return;
+ }
+
+ s->hostmem_mr = host_memory_backend_get_memory(s->hostmem);
+
+ /* HostMemoryBackend size will be multiple of PAGE_SIZE */
+ s->storage_size = object_property_get_int(OBJECT(s->hostmem), "size", errp);
+ if (*errp) {
+ return;
+ }
+
+ /* Initialize backend storage and record_count */
+ check_erst_backend_storage(s, errp);
+ if (*errp) {
+ return;
+ }
+
+ /* BAR 0: Programming registers */
+ memory_region_init_io(&s->iomem_mr, OBJECT(pci_dev), &erst_reg_ops, s,
+ TYPE_ACPI_ERST, ERST_REG_SIZE);
+ pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->iomem_mr);
+
+ /* BAR 1: Exchange buffer memory */
+ memory_region_init_ram(&s->exchange_mr, OBJECT(pci_dev),
+ "erst.exchange",
+ le32_to_cpu(s->header->record_size), errp);
+ if (*errp) {
+ return;
+ }
+ pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY,
+ &s->exchange_mr);
+
+ /* Include the backend storage in the migration stream */
+ vmstate_register_ram_global(s->hostmem_mr);
+
+ trace_acpi_erst_realizefn_out(s->storage_size);
+}
+
+static void erst_reset(DeviceState *dev)
+{
+ ERSTDeviceState *s = ACPIERST(dev);
+
+ trace_acpi_erst_reset_in(le32_to_cpu(s->header->record_count));
+ s->operation = 0;
+ s->busy_status = 0;
+ s->command_status = STATUS_SUCCESS;
+ s->record_identifier = ERST_UNSPECIFIED_RECORD_ID;
+ s->record_offset = 0;
+ s->next_record_index = s->first_record_index;
+ /* NOTE: first/last_record_index are computed only once */
+ trace_acpi_erst_reset_out(le32_to_cpu(s->header->record_count));
+}
+
+static Property erst_properties[] = {
+ DEFINE_PROP_LINK(ACPI_ERST_MEMDEV_PROP, ERSTDeviceState, hostmem,
+ TYPE_MEMORY_BACKEND, HostMemoryBackend *),
+ DEFINE_PROP_UINT32(ACPI_ERST_RECORD_SIZE_PROP, ERSTDeviceState,
+ default_record_size, ERST_RECORD_SIZE),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void erst_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ trace_acpi_erst_class_init_in();
+ k->realize = erst_realizefn;
+ k->vendor_id = PCI_VENDOR_ID_REDHAT;
+ k->device_id = PCI_DEVICE_ID_REDHAT_ACPI_ERST;
+ k->revision = 0x00;
+ k->class_id = PCI_CLASS_OTHERS;
+ dc->reset = erst_reset;
+ dc->vmsd = &erst_vmstate;
+ dc->user_creatable = true;
+ dc->hotpluggable = false;
+ device_class_set_props(dc, erst_properties);
+ dc->desc = "ACPI Error Record Serialization Table (ERST) device";
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ trace_acpi_erst_class_init_out();
+}
+
+static const TypeInfo erst_type_info = {
+ .name = TYPE_ACPI_ERST,
+ .parent = TYPE_PCI_DEVICE,
+ .class_init = erst_class_init,
+ .instance_size = sizeof(ERSTDeviceState),
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { }
+ }
+};
+
+static void erst_register_types(void)
+{
+ type_register_static(&erst_type_info);
+}
+
+type_init(erst_register_types)
diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
index e28457a7d1..2d6e91b124 100644
--- a/hw/acpi/generic_event_device.c
+++ b/hw/acpi/generic_event_device.c
@@ -267,6 +267,13 @@ static void acpi_ged_unplug_cb(HotplugHandler *hotplug_dev,
}
}
+static void acpi_ged_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list)
+{
+ AcpiGedState *s = ACPI_GED(adev);
+
+ acpi_memory_ospm_status(&s->memhp_state, list);
+}
+
static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
{
AcpiGedState *s = ACPI_GED(adev);
@@ -305,7 +312,7 @@ static const VMStateDescription vmstate_memhp_state = {
.name = "acpi-ged/memhp",
.version_id = 1,
.minimum_version_id = 1,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_MEMORY_HOTPLUG(memhp_state, AcpiGedState),
VMSTATE_END_OF_LIST()
}
@@ -315,7 +322,7 @@ static const VMStateDescription vmstate_ged_state = {
.name = "acpi-ged-state",
.version_id = 1,
.minimum_version_id = 1,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_UINT32(sel, GEDState),
VMSTATE_END_OF_LIST()
}
@@ -325,7 +332,7 @@ static const VMStateDescription vmstate_ghes = {
.name = "acpi-ghes",
.version_id = 1,
.minimum_version_id = 1,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_UINT64(ghes_addr_le, AcpiGhesState),
VMSTATE_END_OF_LIST()
},
@@ -342,7 +349,7 @@ static const VMStateDescription vmstate_ghes_state = {
.version_id = 1,
.minimum_version_id = 1,
.needed = ghes_needed,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_STRUCT(ghes_state, AcpiGedState, 1,
vmstate_ghes, AcpiGhesState),
VMSTATE_END_OF_LIST()
@@ -353,11 +360,11 @@ static const VMStateDescription vmstate_acpi_ged = {
.name = "acpi-ged",
.version_id = 1,
.minimum_version_id = 1,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_STRUCT(ged_state, AcpiGedState, 1, vmstate_ged_state, GEDState),
VMSTATE_END_OF_LIST(),
},
- .subsections = (const VMStateDescription * []) {
+ .subsections = (const VMStateDescription * const []) {
&vmstate_memhp_state,
&vmstate_ghes_state,
NULL
@@ -409,6 +416,7 @@ static void acpi_ged_class_init(ObjectClass *class, void *data)
hc->unplug_request = acpi_ged_unplug_request_cb;
hc->unplug = acpi_ged_unplug_cb;
+ adevc->ospm_status = acpi_ged_ospm_status;
adevc->send_event = acpi_ged_send_event;
}
diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c
index a749b84d62..e9511d9b8f 100644
--- a/hw/acpi/ghes.c
+++ b/hw/acpi/ghes.c
@@ -249,7 +249,7 @@ void build_ghes_error_table(GArray *hardware_errors, BIOSLinker *linker)
for (i = 0; i < ACPI_GHES_ERROR_SOURCE_COUNT; i++) {
/*
* Initialize the value of read_ack_register to 1, so GHES can be
- * writeable after (re)boot.
+ * writable after (re)boot.
* ACPI 6.2: 18.3.2.8 Generic Hardware Error Source version 2
* (GHESv2 - Type 10)
*/
@@ -362,18 +362,16 @@ static void build_ghes_v2(GArray *table_data, int source_id, BIOSLinker *linker)
void acpi_build_hest(GArray *table_data, BIOSLinker *linker,
const char *oem_id, const char *oem_table_id)
{
- uint64_t hest_start = table_data->len;
+ AcpiTable table = { .sig = "HEST", .rev = 1,
+ .oem_id = oem_id, .oem_table_id = oem_table_id };
- /* Hardware Error Source Table header*/
- acpi_data_push(table_data, sizeof(AcpiTableHeader));
+ acpi_table_begin(&table, table_data);
/* Error Source Count */
build_append_int_noprefix(table_data, ACPI_GHES_ERROR_SOURCE_COUNT, 4);
-
build_ghes_v2(table_data, ACPI_HEST_SRC_ID_SEA, linker);
- build_header(linker, table_data, (void *)(table_data->data + hest_start),
- "HEST", table_data->len - hest_start, 1, oem_id, oem_table_id);
+ acpi_table_end(linker, &table);
}
void acpi_ghes_add_fw_cfg(AcpiGhesState *ags, FWCfgState *s,
diff --git a/hw/acpi/hmat.c b/hw/acpi/hmat.c
index edb3fd91b2..9b1662b6b8 100644
--- a/hw/acpi/hmat.c
+++ b/hw/acpi/hmat.c
@@ -27,6 +27,7 @@
#include "qemu/osdep.h"
#include "qemu/units.h"
#include "sysemu/numa.h"
+#include "hw/acpi/aml-build.h"
#include "hw/acpi/hmat.h"
/*
@@ -77,12 +78,13 @@ static void build_hmat_lb(GArray *table_data, HMAT_LB_Info *hmat_lb,
uint32_t *initiator_list)
{
int i, index;
+ uint32_t initiator_to_index[MAX_NODES] = {};
HMAT_LB_Data *lb_data;
uint16_t *entry_list;
uint32_t base;
/* Length in bytes for entire structure */
uint32_t lb_length
- = 32 /* Table length upto and including Entry Base Unit */
+ = 32 /* Table length up to and including Entry Base Unit */
+ 4 * num_initiator /* Initiator Proximity Domain List */
+ 4 * num_target /* Target Proximity Domain List */
+ 2 * num_initiator * num_target; /* Latency or Bandwidth Entries */
@@ -120,6 +122,8 @@ static void build_hmat_lb(GArray *table_data, HMAT_LB_Info *hmat_lb,
/* Initiator Proximity Domain List */
for (i = 0; i < num_initiator; i++) {
build_append_int_noprefix(table_data, initiator_list[i], 4);
+ /* Reverse mapping for array possitions */
+ initiator_to_index[initiator_list[i]] = i;
}
/* Target Proximity Domain List */
@@ -128,10 +132,11 @@ static void build_hmat_lb(GArray *table_data, HMAT_LB_Info *hmat_lb,
}
/* Latency or Bandwidth Entries */
- entry_list = g_malloc0(num_initiator * num_target * sizeof(uint16_t));
+ entry_list = g_new0(uint16_t, num_initiator * num_target);
for (i = 0; i < hmat_lb->list->len; i++) {
lb_data = &g_array_index(hmat_lb->list, HMAT_LB_Data, i);
- index = lb_data->initiator * num_target + lb_data->target;
+ index = initiator_to_index[lb_data->initiator] * num_target +
+ lb_data->target;
entry_list[index] = (uint16_t)(lb_data->data / hmat_lb->base);
}
@@ -200,7 +205,16 @@ static void hmat_build_table_structs(GArray *table_data, NumaState *numa_state)
HMAT_LB_Info *hmat_lb;
NumaHmatCacheOptions *hmat_cache;
+ build_append_int_noprefix(table_data, 0, 4); /* Reserved */
+
for (i = 0; i < numa_state->num_nodes; i++) {
+ /*
+ * Linux rejects whole HMAT table if a node with no memory
+ * has one of these structures listing it as a target.
+ */
+ if (!numa_state->nodes[i].node_mem) {
+ continue;
+ }
flags = 0;
if (numa_state->nodes[i].initiator < MAX_NODES) {
@@ -211,7 +225,7 @@ static void hmat_build_table_structs(GArray *table_data, NumaState *numa_state)
}
for (i = 0; i < numa_state->num_nodes; i++) {
- if (numa_state->nodes[i].has_cpu) {
+ if (numa_state->nodes[i].has_cpu || numa_state->nodes[i].has_gi) {
initiator_list[num_initiator++] = i;
}
}
@@ -256,14 +270,10 @@ static void hmat_build_table_structs(GArray *table_data, NumaState *numa_state)
void build_hmat(GArray *table_data, BIOSLinker *linker, NumaState *numa_state,
const char *oem_id, const char *oem_table_id)
{
- int hmat_start = table_data->len;
-
- /* reserve space for HMAT header */
- acpi_data_push(table_data, 40);
+ AcpiTable table = { .sig = "HMAT", .rev = 2,
+ .oem_id = oem_id, .oem_table_id = oem_table_id };
+ acpi_table_begin(&table, table_data);
hmat_build_table_structs(table_data, numa_state);
-
- build_header(linker, table_data,
- (void *)(table_data->data + hmat_start),
- "HMAT", table_data->len - hmat_start, 2, oem_id, oem_table_id);
+ acpi_table_end(linker, &table);
}
diff --git a/hw/acpi/hmat.h b/hw/acpi/hmat.h
index b57f0e7e80..fd989cb661 100644
--- a/hw/acpi/hmat.h
+++ b/hw/acpi/hmat.h
@@ -27,7 +27,8 @@
#ifndef HMAT_H
#define HMAT_H
-#include "hw/acpi/aml-build.h"
+#include "hw/acpi/bios-linker-loader.h"
+#include "sysemu/numa.h"
/*
* ACPI 6.3: 5.2.27.3 Memory Proximity Domain Attributes Structure,
diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
index 1ee2ba2c50..573d032e8e 100644
--- a/hw/acpi/ich9.c
+++ b/hw/acpi/ich9.c
@@ -34,9 +34,9 @@
#include "sysemu/reset.h"
#include "sysemu/runstate.h"
#include "hw/acpi/acpi.h"
-#include "hw/acpi/tco.h"
+#include "hw/acpi/ich9_tco.h"
-#include "hw/i386/ich9.h"
+#include "hw/southbridge/ich9.h"
#include "hw/mem/pc-dimm.h"
#include "hw/mem/nvdimm.h"
@@ -163,9 +163,8 @@ static const VMStateDescription vmstate_memhp_state = {
.name = "ich9_pm/memhp",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.needed = vmstate_test_use_memhp,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_MEMORY_HOTPLUG(acpi_memory_hotplug, ICH9LPCPMRegs),
VMSTATE_END_OF_LIST()
}
@@ -181,9 +180,8 @@ static const VMStateDescription vmstate_tco_io_state = {
.name = "ich9_pm/tco",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.needed = vmstate_test_use_tco,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_STRUCT(tco_regs, ICH9LPCPMRegs, 1, vmstate_tco_io_sts,
TCOIORegs),
VMSTATE_END_OF_LIST()
@@ -208,10 +206,9 @@ static const VMStateDescription vmstate_cpuhp_state = {
.name = "ich9_pm/cpuhp",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.needed = vmstate_test_use_cpuhp,
.pre_load = vmstate_cpuhp_pre_load,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_CPU_HOTPLUG(cpuhp_state, ICH9LPCPMRegs),
VMSTATE_END_OF_LIST()
}
@@ -221,7 +218,7 @@ static bool vmstate_test_use_pcihp(void *opaque)
{
ICH9LPCPMRegs *s = opaque;
- return s->use_acpi_hotplug_bridge;
+ return s->acpi_pci_hotplug.use_acpi_hotplug_bridge;
}
static const VMStateDescription vmstate_pcihp_state = {
@@ -229,7 +226,7 @@ static const VMStateDescription vmstate_pcihp_state = {
.version_id = 1,
.minimum_version_id = 1,
.needed = vmstate_test_use_pcihp,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_PCI_HOTPLUG(acpi_pci_hotplug,
ICH9LPCPMRegs,
NULL, NULL),
@@ -242,7 +239,7 @@ const VMStateDescription vmstate_ich9_pm = {
.version_id = 1,
.minimum_version_id = 1,
.post_load = ich9_pm_post_load,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_UINT16(acpi_regs.pm1.evt.sts, ICH9LPCPMRegs),
VMSTATE_UINT16(acpi_regs.pm1.evt.en, ICH9LPCPMRegs),
VMSTATE_UINT16(acpi_regs.pm1.cnt.cnt, ICH9LPCPMRegs),
@@ -254,7 +251,7 @@ const VMStateDescription vmstate_ich9_pm = {
VMSTATE_UINT32(smi_sts, ICH9LPCPMRegs),
VMSTATE_END_OF_LIST()
},
- .subsections = (const VMStateDescription*[]) {
+ .subsections = (const VMStateDescription * const []) {
&vmstate_memhp_state,
&vmstate_tco_io_state,
&vmstate_cpuhp_state,
@@ -280,8 +277,8 @@ static void pm_reset(void *opaque)
}
pm->smi_en_wmask = ~0;
- if (pm->use_acpi_hotplug_bridge) {
- acpi_pcihp_reset(&pm->acpi_pci_hotplug, true);
+ if (pm->acpi_pci_hotplug.use_acpi_hotplug_bridge) {
+ acpi_pcihp_reset(&pm->acpi_pci_hotplug);
}
acpi_update_sci(&pm->acpi_regs, pm->irq);
@@ -294,9 +291,7 @@ static void pm_powerdown_req(Notifier *n, void *opaque)
acpi_pm1_evt_power_down(&pm->acpi_regs);
}
-void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
- bool smm_enabled,
- qemu_irq sci_irq)
+void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, qemu_irq sci_irq)
{
memory_region_init(&pm->io, OBJECT(lpc_pci), "ich9-pm", ICH9_PMIO_SIZE);
memory_region_set_enabled(&pm->io, false);
@@ -306,7 +301,7 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
acpi_pm_tmr_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io);
acpi_pm1_evt_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io);
acpi_pm1_cnt_init(&pm->acpi_regs, &pm->io, pm->disable_s3, pm->disable_s4,
- pm->s4_val, !pm->smm_compat && !smm_enabled);
+ pm->s4_val, !pm->smm_compat && !pm->smm_enabled);
acpi_gpe_init(&pm->acpi_regs, ICH9_PMIO_GPE0_LEN);
memory_region_init_io(&pm->io_gpe, OBJECT(lpc_pci), &ich9_gpe_ops, pm,
@@ -317,17 +312,15 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
"acpi-smi", 8);
memory_region_add_subregion(&pm->io, ICH9_PMIO_SMI_EN, &pm->io_smi);
- pm->smm_enabled = smm_enabled;
-
- pm->enable_tco = true;
- acpi_pm_tco_init(&pm->tco_regs, &pm->io);
+ if (pm->enable_tco) {
+ acpi_pm_tco_init(&pm->tco_regs, &pm->io);
+ }
- if (pm->use_acpi_hotplug_bridge) {
+ if (pm->acpi_pci_hotplug.use_acpi_hotplug_bridge) {
acpi_pcihp_init(OBJECT(lpc_pci),
&pm->acpi_pci_hotplug,
pci_get_bus(lpc_pci),
pci_address_space_io(lpc_pci),
- true,
ACPI_PCIHP_ADDR_ICH9);
qbus_set_hotplug_handler(BUS(pci_get_bus(lpc_pci)),
@@ -409,14 +402,28 @@ static bool ich9_pm_get_acpi_pci_hotplug(Object *obj, Error **errp)
{
ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
- return s->pm.use_acpi_hotplug_bridge;
+ return s->pm.acpi_pci_hotplug.use_acpi_hotplug_bridge;
}
static void ich9_pm_set_acpi_pci_hotplug(Object *obj, bool value, Error **errp)
{
ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
- s->pm.use_acpi_hotplug_bridge = value;
+ s->pm.acpi_pci_hotplug.use_acpi_hotplug_bridge = value;
+}
+
+static bool ich9_pm_get_keep_pci_slot_hpc(Object *obj, Error **errp)
+{
+ ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
+
+ return s->pm.keep_pci_slot_hpc;
+}
+
+static void ich9_pm_set_keep_pci_slot_hpc(Object *obj, bool value, Error **errp)
+{
+ ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
+
+ s->pm.keep_pci_slot_hpc = value;
}
void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm)
@@ -427,7 +434,9 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm)
pm->disable_s3 = 0;
pm->disable_s4 = 0;
pm->s4_val = 2;
- pm->use_acpi_hotplug_bridge = true;
+ pm->acpi_pci_hotplug.use_acpi_hotplug_bridge = true;
+ pm->keep_pci_slot_hpc = true;
+ pm->enable_tco = true;
object_property_add_uint32_ptr(obj, ACPI_PM_PROP_PM_IO_BASE,
&pm->pm_io_base, OBJ_PROP_FLAG_READ);
@@ -454,6 +463,9 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm)
object_property_add_bool(obj, ACPI_PM_PROP_ACPI_PCIHP_BRIDGE,
ich9_pm_get_acpi_pci_hotplug,
ich9_pm_set_acpi_pci_hotplug);
+ object_property_add_bool(obj, "x-keep-pci-slot-hpc",
+ ich9_pm_get_keep_pci_slot_hpc,
+ ich9_pm_set_keep_pci_slot_hpc);
}
void ich9_pm_device_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
@@ -566,6 +578,12 @@ void ich9_pm_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
}
}
+bool ich9_pm_is_hotpluggable_bus(HotplugHandler *hotplug_dev, BusState *bus)
+{
+ ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev);
+ return acpi_pcihp_is_hotpluggbale_bus(&lpc->pm.acpi_pci_hotplug, bus);
+}
+
void ich9_pm_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list)
{
ICH9LPCState *s = ICH9_LPC_DEVICE(adev);
diff --git a/hw/acpi/tco.c b/hw/acpi/ich9_tco.c
index cf1e68a539..81606219f7 100644
--- a/hw/acpi/tco.c
+++ b/hw/acpi/ich9_tco.c
@@ -9,10 +9,10 @@
#include "qemu/osdep.h"
#include "sysemu/watchdog.h"
-#include "hw/i386/ich9.h"
+#include "hw/southbridge/ich9.h"
#include "migration/vmstate.h"
-#include "hw/acpi/tco.h"
+#include "hw/acpi/ich9_tco.h"
#include "trace.h"
enum {
@@ -86,6 +86,7 @@ static inline int can_start_tco_timer(TCOIORegs *tr)
static uint32_t tco_ioport_readw(TCOIORegs *tr, uint32_t addr)
{
uint16_t rld;
+ uint32_t ret = 0;
switch (addr) {
case TCO_RLD:
@@ -96,35 +97,49 @@ static uint32_t tco_ioport_readw(TCOIORegs *tr, uint32_t addr)
} else {
rld = tr->tco.rld;
}
- return rld;
+ ret = rld;
+ break;
case TCO_DAT_IN:
- return tr->tco.din;
+ ret = tr->tco.din;
+ break;
case TCO_DAT_OUT:
- return tr->tco.dout;
+ ret = tr->tco.dout;
+ break;
case TCO1_STS:
- return tr->tco.sts1;
+ ret = tr->tco.sts1;
+ break;
case TCO2_STS:
- return tr->tco.sts2;
+ ret = tr->tco.sts2;
+ break;
case TCO1_CNT:
- return tr->tco.cnt1;
+ ret = tr->tco.cnt1;
+ break;
case TCO2_CNT:
- return tr->tco.cnt2;
+ ret = tr->tco.cnt2;
+ break;
case TCO_MESSAGE1:
- return tr->tco.msg1;
+ ret = tr->tco.msg1;
+ break;
case TCO_MESSAGE2:
- return tr->tco.msg2;
+ ret = tr->tco.msg2;
+ break;
case TCO_WDCNT:
- return tr->tco.wdcnt;
+ ret = tr->tco.wdcnt;
+ break;
case TCO_TMR:
- return tr->tco.tmr;
+ ret = tr->tco.tmr;
+ break;
case SW_IRQ_GEN:
- return tr->sw_irq_gen;
+ ret = tr->sw_irq_gen;
+ break;
}
- return 0;
+ trace_tco_io_read(addr, ret);
+ return ret;
}
static void tco_ioport_writew(TCOIORegs *tr, uint32_t addr, uint32_t val)
{
+ trace_tco_io_write(addr, val);
switch (addr) {
case TCO_RLD:
tr->timeouts_no = 0;
@@ -239,8 +254,7 @@ const VMStateDescription vmstate_tco_io_sts = {
.name = "tco io device status",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_UINT16(tco.rld, TCOIORegs),
VMSTATE_UINT8(tco.din, TCOIORegs),
VMSTATE_UINT8(tco.dout, TCOIORegs),
diff --git a/hw/acpi/ipmi-stub.c b/hw/acpi/ipmi-stub.c
index 8634fb325c..befaf0a882 100644
--- a/hw/acpi/ipmi-stub.c
+++ b/hw/acpi/ipmi-stub.c
@@ -10,6 +10,6 @@
#include "qemu/osdep.h"
#include "hw/acpi/ipmi.h"
-void build_acpi_ipmi_devices(Aml *table, BusState *bus, const char *resource)
+void build_ipmi_dev_aml(AcpiDevAmlIf *adev, Aml *scope)
{
}
diff --git a/hw/acpi/ipmi.c b/hw/acpi/ipmi.c
index 96e48eba15..a20e57d465 100644
--- a/hw/acpi/ipmi.c
+++ b/hw/acpi/ipmi.c
@@ -13,7 +13,7 @@
#include "hw/acpi/acpi.h"
#include "hw/acpi/ipmi.h"
-static Aml *aml_ipmi_crs(IPMIFwInfo *info, const char *resource)
+static Aml *aml_ipmi_crs(IPMIFwInfo *info)
{
Aml *crs = aml_resource_template();
@@ -49,7 +49,7 @@ static Aml *aml_ipmi_crs(IPMIFwInfo *info, const char *resource)
break;
case IPMI_MEMSPACE_SMBUS:
aml_append(crs, aml_i2c_serial_bus_device(info->base_address,
- resource));
+ "^"));
break;
default:
abort();
@@ -62,46 +62,27 @@ static Aml *aml_ipmi_crs(IPMIFwInfo *info, const char *resource)
return crs;
}
-static Aml *aml_ipmi_device(IPMIFwInfo *info, const char *resource)
+void build_ipmi_dev_aml(AcpiDevAmlIf *adev, Aml *scope)
{
Aml *dev;
- uint16_t version = ((info->ipmi_spec_major_revision << 8)
- | (info->ipmi_spec_minor_revision << 4));
+ IPMIFwInfo info = {};
+ IPMIInterface *ii = IPMI_INTERFACE(adev);
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+ uint16_t version;
- assert(info->ipmi_spec_minor_revision <= 15);
+ iic->get_fwinfo(ii, &info);
+ assert(info.ipmi_spec_minor_revision <= 15);
+ version = ((info.ipmi_spec_major_revision << 8)
+ | (info.ipmi_spec_minor_revision << 4));
- dev = aml_device("MI%d", info->uuid);
+ dev = aml_device("MI%d", info.uuid);
aml_append(dev, aml_name_decl("_HID", aml_eisaid("IPI0001")));
aml_append(dev, aml_name_decl("_STR", aml_string("ipmi_%s",
- info->interface_name)));
- aml_append(dev, aml_name_decl("_UID", aml_int(info->uuid)));
- aml_append(dev, aml_name_decl("_CRS", aml_ipmi_crs(info, resource)));
- aml_append(dev, aml_name_decl("_IFT", aml_int(info->interface_type)));
+ info.interface_name)));
+ aml_append(dev, aml_name_decl("_UID", aml_int(info.uuid)));
+ aml_append(dev, aml_name_decl("_CRS", aml_ipmi_crs(&info)));
+ aml_append(dev, aml_name_decl("_IFT", aml_int(info.interface_type)));
aml_append(dev, aml_name_decl("_SRV", aml_int(version)));
- return dev;
-}
-
-void build_acpi_ipmi_devices(Aml *scope, BusState *bus, const char *resource)
-{
-
- BusChild *kid;
-
- QTAILQ_FOREACH(kid, &bus->children, sibling) {
- IPMIInterface *ii;
- IPMIInterfaceClass *iic;
- IPMIFwInfo info;
- Object *obj = object_dynamic_cast(OBJECT(kid->child),
- TYPE_IPMI_INTERFACE);
-
- if (!obj) {
- continue;
- }
-
- ii = IPMI_INTERFACE(obj);
- iic = IPMI_INTERFACE_GET_CLASS(obj);
- memset(&info, 0, sizeof(info));
- iic->get_fwinfo(ii, &info);
- aml_append(scope, aml_ipmi_device(&info, resource));
- }
+ aml_append(scope, dev);
}
diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c
index af37889423..de6f974ebb 100644
--- a/hw/acpi/memory_hotplug.c
+++ b/hw/acpi/memory_hotplug.c
@@ -1,13 +1,14 @@
#include "qemu/osdep.h"
#include "hw/acpi/memory_hotplug.h"
-#include "hw/acpi/pc-hotplug.h"
#include "hw/mem/pc-dimm.h"
+#include "hw/boards.h"
#include "hw/qdev-core.h"
#include "migration/vmstate.h"
#include "trace.h"
#include "qapi/error.h"
#include "qapi/qapi-events-acpi.h"
#include "qapi/qapi-events-machine.h"
+#include "qapi/qapi-events-qdev.h"
#define MEMORY_SLOTS_NUMBER "MDNR"
#define MEMORY_HOTPLUG_IO_REGION "HPMR"
@@ -44,7 +45,6 @@ static ACPIOSTInfo *acpi_memory_device_status(int slot, MemStatus *mdev)
DeviceState *dev = DEVICE(mdev->dimm);
if (dev->id) {
info->device = g_strdup(dev->id);
- info->has_device = true;
}
}
return info;
@@ -178,8 +178,16 @@ static void acpi_memory_hotplug_write(void *opaque, hwaddr addr, uint64_t data,
hotplug_handler_unplug(hotplug_ctrl, dev, &local_err);
if (local_err) {
trace_mhp_acpi_pc_dimm_delete_failed(mem_st->selector);
- qapi_event_send_mem_unplug_error(dev->id,
+
+ /*
+ * Send both MEM_UNPLUG_ERROR and DEVICE_UNPLUG_GUEST_ERROR
+ * while the deprecation of MEM_UNPLUG_ERROR is
+ * pending.
+ */
+ qapi_event_send_mem_unplug_error(dev->id ? : "",
error_get_pretty(local_err));
+ qapi_event_send_device_unplug_guest_error(dev->id,
+ dev->canonical_path);
error_free(local_err);
break;
}
@@ -309,8 +317,7 @@ static const VMStateDescription vmstate_memhp_sts = {
.name = "memory hotplug device state",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_BOOL(is_enabled, MemStatus),
VMSTATE_BOOL(is_inserting, MemStatus),
VMSTATE_UINT32(ost_event, MemStatus),
@@ -323,8 +330,7 @@ const VMStateDescription vmstate_memory_hotplug = {
.name = "memory hotplug state",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_UINT32(selector, MemHotplugState),
VMSTATE_STRUCT_VARRAY_POINTER_UINT32(devs, MemHotplugState, dev_count,
vmstate_memhp_sts, MemStatus),
diff --git a/hw/acpi/meson.build b/hw/acpi/meson.build
index 7d8c0eb43e..fa5c07db90 100644
--- a/hw/acpi/meson.build
+++ b/hw/acpi/meson.build
@@ -1,5 +1,6 @@
acpi_ss = ss.source_set()
acpi_ss.add(files(
+ 'acpi_generic_initiator.c',
'acpi_interface.c',
'aml-build.c',
'bios-linker-loader.c',
@@ -13,20 +14,24 @@ acpi_ss.add(when: 'CONFIG_ACPI_MEMORY_HOTPLUG', if_false: files('acpi-mem-hotplu
acpi_ss.add(when: 'CONFIG_ACPI_NVDIMM', if_true: files('nvdimm.c'))
acpi_ss.add(when: 'CONFIG_ACPI_NVDIMM', if_false: files('acpi-nvdimm-stub.c'))
acpi_ss.add(when: 'CONFIG_ACPI_PCI', if_true: files('pci.c'))
+acpi_ss.add(when: 'CONFIG_ACPI_CXL', if_true: files('cxl.c'), if_false: files('cxl-stub.c'))
acpi_ss.add(when: 'CONFIG_ACPI_VMGENID', if_true: files('vmgenid.c'))
acpi_ss.add(when: 'CONFIG_ACPI_HW_REDUCED', if_true: files('generic_event_device.c'))
acpi_ss.add(when: 'CONFIG_ACPI_HMAT', if_true: files('hmat.c'))
acpi_ss.add(when: 'CONFIG_ACPI_APEI', if_true: files('ghes.c'), if_false: files('ghes-stub.c'))
acpi_ss.add(when: 'CONFIG_ACPI_PIIX4', if_true: files('piix4.c'))
+acpi_ss.add(when: 'CONFIG_ACPI_PCI_BRIDGE', if_true: files('pci-bridge.c'))
acpi_ss.add(when: 'CONFIG_ACPI_PCIHP', if_true: files('pcihp.c'))
acpi_ss.add(when: 'CONFIG_ACPI_PCIHP', if_false: files('acpi-pci-hotplug-stub.c'))
-acpi_ss.add(when: 'CONFIG_ACPI_X86_ICH', if_true: files('ich9.c', 'tco.c'))
+acpi_ss.add(when: 'CONFIG_ACPI_VIOT', if_true: files('viot.c'))
+acpi_ss.add(when: 'CONFIG_ACPI_ICH9', if_true: files('ich9.c', 'ich9_tco.c'))
+acpi_ss.add(when: 'CONFIG_ACPI_ERST', if_true: files('erst.c'))
acpi_ss.add(when: 'CONFIG_IPMI', if_true: files('ipmi.c'), if_false: files('ipmi-stub.c'))
acpi_ss.add(when: 'CONFIG_PC', if_false: files('acpi-x86-stub.c'))
-acpi_ss.add(when: 'CONFIG_TPM', if_true: files('tpm.c'))
-softmmu_ss.add(when: 'CONFIG_ACPI', if_false: files('acpi-stub.c', 'aml-build-stub.c', 'ghes-stub.c'))
-softmmu_ss.add_all(when: 'CONFIG_ACPI', if_true: acpi_ss)
-softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('acpi-stub.c', 'aml-build-stub.c',
- 'acpi-x86-stub.c', 'ipmi-stub.c', 'ghes-stub.c',
- 'acpi-mem-hotplug-stub.c', 'acpi-cpu-hotplug-stub.c',
- 'acpi-pci-hotplug-stub.c', 'acpi-nvdimm-stub.c'))
+if have_tpm
+ acpi_ss.add(files('tpm.c'))
+endif
+system_ss.add(when: 'CONFIG_ACPI', if_false: files('acpi-stub.c', 'aml-build-stub.c', 'ghes-stub.c', 'acpi_interface.c'))
+system_ss.add(when: 'CONFIG_ACPI_PCI_BRIDGE', if_false: files('pci-bridge-stub.c'))
+system_ss.add_all(when: 'CONFIG_ACPI', if_true: acpi_ss)
+system_ss.add(files('acpi-qmp-cmds.c'))
diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c
index e3d5fe1939..9ba90806f2 100644
--- a/hw/acpi/nvdimm.c
+++ b/hw/acpi/nvdimm.c
@@ -35,6 +35,7 @@
#include "hw/nvram/fw_cfg.h"
#include "hw/mem/nvdimm.h"
#include "qemu/nvdimm-utils.h"
+#include "trace.h"
/*
* define Byte Addressable Persistent Memory (PM) Region according to
@@ -45,22 +46,6 @@ static const uint8_t nvdimm_nfit_spa_uuid[] =
0x18, 0xb7, 0x8c, 0xdb);
/*
- * NVDIMM Firmware Interface Table
- * @signature: "NFIT"
- *
- * It provides information that allows OSPM to enumerate NVDIMM present in
- * the platform and associate system physical address ranges created by the
- * NVDIMMs.
- *
- * It is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT)
- */
-struct NvdimmNfitHeader {
- ACPI_TABLE_HEADER_DEF
- uint32_t reserved;
-} QEMU_PACKED;
-typedef struct NvdimmNfitHeader NvdimmNfitHeader;
-
-/*
* define NFIT structures according to ACPI 6.0: 5.2.25 NVDIMM Firmware
* Interface Table (NFIT).
*/
@@ -355,10 +340,10 @@ nvdimm_build_structure_caps(GArray *structures, uint32_t capabilities)
static GArray *nvdimm_build_device_structure(NVDIMMState *state)
{
- GSList *device_list = nvdimm_get_device_list();
+ GSList *device_list, *list = nvdimm_get_device_list();
GArray *structures = g_array_new(false, true /* clear */, 1);
- for (; device_list; device_list = device_list->next) {
+ for (device_list = list; device_list; device_list = device_list->next) {
DeviceState *dev = device_list->data;
/* build System Physical Address Range Structure. */
@@ -373,7 +358,7 @@ static GArray *nvdimm_build_device_structure(NVDIMMState *state)
/* build NVDIMM Control Region Structure. */
nvdimm_build_structure_dcr(structures, dev);
}
- g_slist_free(device_list);
+ g_slist_free(list);
if (state->persistence) {
nvdimm_build_structure_caps(structures, state->persistence);
@@ -401,25 +386,33 @@ void nvdimm_plug(NVDIMMState *state)
nvdimm_build_fit_buffer(state);
}
+/*
+ * NVDIMM Firmware Interface Table
+ * @signature: "NFIT"
+ *
+ * It provides information that allows OSPM to enumerate NVDIMM present in
+ * the platform and associate system physical address ranges created by the
+ * NVDIMMs.
+ *
+ * It is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT)
+ */
+
static void nvdimm_build_nfit(NVDIMMState *state, GArray *table_offsets,
GArray *table_data, BIOSLinker *linker,
const char *oem_id, const char *oem_table_id)
{
NvdimmFitBuffer *fit_buf = &state->fit_buf;
- unsigned int header;
+ AcpiTable table = { .sig = "NFIT", .rev = 1,
+ .oem_id = oem_id, .oem_table_id = oem_table_id };
acpi_add_table(table_offsets, table_data);
- /* NFIT header. */
- header = table_data->len;
- acpi_data_push(table_data, sizeof(NvdimmNfitHeader));
+ acpi_table_begin(&table, table_data);
+ /* Reserved */
+ build_append_int_noprefix(table_data, 0, 4);
/* NVDIMM device structures. */
g_array_append_vals(table_data, fit_buf->fit->data, fit_buf->fit->len);
-
- build_header(linker, table_data,
- (void *)(table_data->data + header), "NFIT",
- sizeof(NvdimmNfitHeader) + fit_buf->fit->len, 1, oem_id,
- oem_table_id);
+ acpi_table_end(linker, &table);
}
#define NVDIMM_DSM_MEMORY_SIZE 4096
@@ -484,7 +477,7 @@ struct NvdimmFuncGetLabelDataOut {
/* the size of buffer filled by QEMU. */
uint32_t len;
uint32_t func_ret_status; /* return status code. */
- uint8_t out_buf[]; /* the data got via Get Namesapce Label function. */
+ uint8_t out_buf[]; /* the data got via Get Namespace Label function. */
} QEMU_PACKED;
typedef struct NvdimmFuncGetLabelDataOut NvdimmFuncGetLabelDataOut;
QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncGetLabelDataOut) > NVDIMM_DSM_MEMORY_SIZE);
@@ -558,8 +551,8 @@ static void nvdimm_dsm_func_read_fit(NVDIMMState *state, NvdimmDsmIn *in,
fit = fit_buf->fit;
- nvdimm_debug("Read FIT: offset 0x%x FIT size 0x%x Dirty %s.\n",
- read_fit->offset, fit->len, fit_buf->dirty ? "Yes" : "No");
+ trace_acpi_nvdimm_read_fit(read_fit->offset, fit->len,
+ fit_buf->dirty ? "Yes" : "No");
if (read_fit->offset > fit->len) {
func_ret_status = NVDIMM_DSM_RET_STATUS_INVALID;
@@ -666,7 +659,7 @@ static void nvdimm_dsm_label_size(NVDIMMDevice *nvdimm, hwaddr dsm_mem_addr)
label_size = nvdimm->label_size;
mxfer = nvdimm_get_max_xfer_label_size();
- nvdimm_debug("label_size 0x%x, max_xfer 0x%x.\n", label_size, mxfer);
+ trace_acpi_nvdimm_label_info(label_size, mxfer);
label_size_out.func_ret_status = cpu_to_le32(NVDIMM_DSM_RET_STATUS_SUCCESS);
label_size_out.label_size = cpu_to_le32(label_size);
@@ -677,28 +670,31 @@ static void nvdimm_dsm_label_size(NVDIMMDevice *nvdimm, hwaddr dsm_mem_addr)
}
static uint32_t nvdimm_rw_label_data_check(NVDIMMDevice *nvdimm,
- uint32_t offset, uint32_t length)
+ uint32_t offset, uint32_t length,
+ bool is_write)
{
uint32_t ret = NVDIMM_DSM_RET_STATUS_INVALID;
if (offset + length < offset) {
- nvdimm_debug("offset 0x%x + length 0x%x is overflow.\n", offset,
- length);
+ trace_acpi_nvdimm_label_overflow(offset, length);
return ret;
}
if (nvdimm->label_size < offset + length) {
- nvdimm_debug("position 0x%x is beyond label data (len = %" PRIx64 ").\n",
- offset + length, nvdimm->label_size);
+ trace_acpi_nvdimm_label_oversize(offset + length, nvdimm->label_size);
return ret;
}
if (length > nvdimm_get_max_xfer_label_size()) {
- nvdimm_debug("length (0x%x) is larger than max_xfer (0x%x).\n",
- length, nvdimm_get_max_xfer_label_size());
+ trace_acpi_nvdimm_label_xfer_exceed(length,
+ nvdimm_get_max_xfer_label_size());
return ret;
}
+ if (is_write && nvdimm->readonly) {
+ return NVDIMM_DSM_RET_STATUS_UNSUPPORT;
+ }
+
return NVDIMM_DSM_RET_STATUS_SUCCESS;
}
@@ -718,11 +714,11 @@ static void nvdimm_dsm_get_label_data(NVDIMMDevice *nvdimm, NvdimmDsmIn *in,
get_label_data->offset = le32_to_cpu(get_label_data->offset);
get_label_data->length = le32_to_cpu(get_label_data->length);
- nvdimm_debug("Read Label Data: offset 0x%x length 0x%x.\n",
- get_label_data->offset, get_label_data->length);
+ trace_acpi_nvdimm_read_label(get_label_data->offset,
+ get_label_data->length);
status = nvdimm_rw_label_data_check(nvdimm, get_label_data->offset,
- get_label_data->length);
+ get_label_data->length, false);
if (status != NVDIMM_DSM_RET_STATUS_SUCCESS) {
nvdimm_dsm_no_payload(status, dsm_mem_addr);
return;
@@ -757,11 +753,11 @@ static void nvdimm_dsm_set_label_data(NVDIMMDevice *nvdimm, NvdimmDsmIn *in,
set_label_data->offset = le32_to_cpu(set_label_data->offset);
set_label_data->length = le32_to_cpu(set_label_data->length);
- nvdimm_debug("Write Label Data: offset 0x%x length 0x%x.\n",
- set_label_data->offset, set_label_data->length);
+ trace_acpi_nvdimm_write_label(set_label_data->offset,
+ set_label_data->length);
status = nvdimm_rw_label_data_check(nvdimm, set_label_data->offset,
- set_label_data->length);
+ set_label_data->length, true);
if (status != NVDIMM_DSM_RET_STATUS_SUCCESS) {
nvdimm_dsm_no_payload(status, dsm_mem_addr);
return;
@@ -829,7 +825,7 @@ static void nvdimm_dsm_device(NvdimmDsmIn *in, hwaddr dsm_mem_addr)
static uint64_t
nvdimm_dsm_read(void *opaque, hwaddr addr, unsigned size)
{
- nvdimm_debug("BUG: we never read _DSM IO Port.\n");
+ trace_acpi_nvdimm_read_io_port();
return 0;
}
@@ -840,7 +836,7 @@ nvdimm_dsm_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
NvdimmDsmIn *in;
hwaddr dsm_mem_addr = val;
- nvdimm_debug("dsm memory address 0x%" HWADDR_PRIx ".\n", dsm_mem_addr);
+ trace_acpi_nvdimm_dsm_mem_addr(dsm_mem_addr);
/*
* The DSM memory is mapped to guest address space so an evil guest
@@ -854,12 +850,10 @@ nvdimm_dsm_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
in->function = le32_to_cpu(in->function);
in->handle = le32_to_cpu(in->handle);
- nvdimm_debug("Revision 0x%x Handler 0x%x Function 0x%x.\n", in->revision,
- in->handle, in->function);
+ trace_acpi_nvdimm_dsm_info(in->revision, in->handle, in->function);
if (in->revision != 0x1 /* Currently we only support DSM Spec Rev1. */) {
- nvdimm_debug("Revision 0x%x is not supported, expect 0x%x.\n",
- in->revision, 0x1);
+ trace_acpi_nvdimm_invalid_revision(in->revision);
nvdimm_dsm_no_payload(NVDIMM_DSM_RET_STATUS_UNSUPPORT, dsm_mem_addr);
goto exit;
}
@@ -933,6 +927,7 @@ void nvdimm_init_acpi_state(NVDIMMState *state, MemoryRegion *io,
#define NVDIMM_DSM_RFIT_STATUS "RSTA"
#define NVDIMM_QEMU_RSVD_UUID "648B9CF2-CDA1-4312-8AD9-49C4AF32BD62"
+#define NVDIMM_DEVICE_DSM_UUID "4309AC30-0D11-11E4-9191-0800200C9A66"
static void nvdimm_build_common_dsm(Aml *dev,
NVDIMMState *nvdimm_state)
@@ -1040,15 +1035,14 @@ static void nvdimm_build_common_dsm(Aml *dev,
/* UUID for QEMU internal use */), expected_uuid));
aml_append(elsectx, ifctx);
elsectx2 = aml_else();
- aml_append(elsectx2, aml_store(
- aml_touuid("4309AC30-0D11-11E4-9191-0800200C9A66")
+ aml_append(elsectx2, aml_store(aml_touuid(NVDIMM_DEVICE_DSM_UUID)
/* UUID for NVDIMM Devices */, expected_uuid));
aml_append(elsectx, elsectx2);
aml_append(method, elsectx);
uuid_invalid = aml_lnot(aml_equal(uuid, expected_uuid));
- unsupport = aml_if(aml_or(unpatched, uuid_invalid, NULL));
+ unsupport = aml_if(aml_lor(unpatched, uuid_invalid));
/*
* function 0 is called to inquire what functions are supported by
@@ -1080,10 +1074,9 @@ static void nvdimm_build_common_dsm(Aml *dev,
* in the DSM Spec.
*/
pckg = aml_arg(3);
- ifctx = aml_if(aml_and(aml_equal(aml_object_type(pckg),
+ ifctx = aml_if(aml_land(aml_equal(aml_object_type(pckg),
aml_int(4 /* Package */)) /* It is a Package? */,
- aml_equal(aml_sizeof(pckg), aml_int(1)) /* 1 element? */,
- NULL));
+ aml_equal(aml_sizeof(pckg), aml_int(1)) /* 1 element? */));
pckg_index = aml_local(2);
pckg_buf = aml_local(3);
@@ -1109,7 +1102,7 @@ static void nvdimm_build_common_dsm(Aml *dev,
* be treated as an integer. Moreover, the integer size depends on
* DSDT tables revision number. If revision number is < 2, integer
* size is 32 bits, otherwise it is 64 bits.
- * Because of this CreateField() canot be used if RLEN < Integer Size.
+ * Because of this CreateField() cannot be used if RLEN < Integer Size.
*
* Also please note that APCI ASL operator SizeOf() doesn't support
* Integer and there isn't any other way to figure out the Integer
@@ -1255,6 +1248,7 @@ static void nvdimm_build_fit(Aml *dev)
static void nvdimm_build_nvdimm_devices(Aml *root_dev, uint32_t ram_slots)
{
uint32_t slot;
+ Aml *method, *pkg, *field, *com_call;
for (slot = 0; slot < ram_slots; slot++) {
uint32_t handle = nvdimm_slot_to_handle(slot);
@@ -1272,6 +1266,100 @@ static void nvdimm_build_nvdimm_devices(Aml *root_dev, uint32_t ram_slots)
*/
aml_append(nvdimm_dev, aml_name_decl("_ADR", aml_int(handle)));
+ /*
+ * ACPI v6.4: Section 6.5.10 NVDIMM Label Methods
+ */
+ /* _LSI */
+ method = aml_method("_LSI", 0, AML_SERIALIZED);
+ com_call = aml_call5(NVDIMM_COMMON_DSM,
+ aml_touuid(NVDIMM_DEVICE_DSM_UUID),
+ aml_int(1), aml_int(4), aml_int(0),
+ aml_int(handle));
+ aml_append(method, aml_store(com_call, aml_local(0)));
+
+ aml_append(method, aml_create_dword_field(aml_local(0),
+ aml_int(0), "STTS"));
+ aml_append(method, aml_create_dword_field(aml_local(0), aml_int(4),
+ "SLSA"));
+ aml_append(method, aml_create_dword_field(aml_local(0), aml_int(8),
+ "MAXT"));
+
+ pkg = aml_package(3);
+ aml_append(pkg, aml_name("STTS"));
+ aml_append(pkg, aml_name("SLSA"));
+ aml_append(pkg, aml_name("MAXT"));
+ aml_append(method, aml_store(pkg, aml_local(1)));
+ aml_append(method, aml_return(aml_local(1)));
+
+ aml_append(nvdimm_dev, method);
+
+ /* _LSR */
+ method = aml_method("_LSR", 2, AML_SERIALIZED);
+ aml_append(method, aml_name_decl("INPT", aml_buffer(8, NULL)));
+
+ aml_append(method, aml_create_dword_field(aml_name("INPT"),
+ aml_int(0), "OFST"));
+ aml_append(method, aml_create_dword_field(aml_name("INPT"),
+ aml_int(4), "LEN"));
+ aml_append(method, aml_store(aml_arg(0), aml_name("OFST")));
+ aml_append(method, aml_store(aml_arg(1), aml_name("LEN")));
+
+ pkg = aml_package(1);
+ aml_append(pkg, aml_name("INPT"));
+ aml_append(method, aml_store(pkg, aml_local(0)));
+
+ com_call = aml_call5(NVDIMM_COMMON_DSM,
+ aml_touuid(NVDIMM_DEVICE_DSM_UUID),
+ aml_int(1), aml_int(5), aml_local(0),
+ aml_int(handle));
+ aml_append(method, aml_store(com_call, aml_local(3)));
+ field = aml_create_dword_field(aml_local(3), aml_int(0), "STTS");
+ aml_append(method, field);
+ field = aml_create_field(aml_local(3), aml_int(32),
+ aml_shiftleft(aml_name("LEN"), aml_int(3)),
+ "LDAT");
+ aml_append(method, field);
+ aml_append(method, aml_name_decl("LSA", aml_buffer(0, NULL)));
+ aml_append(method, aml_to_buffer(aml_name("LDAT"), aml_name("LSA")));
+
+ pkg = aml_package(2);
+ aml_append(pkg, aml_name("STTS"));
+ aml_append(pkg, aml_name("LSA"));
+
+ aml_append(method, aml_store(pkg, aml_local(1)));
+ aml_append(method, aml_return(aml_local(1)));
+
+ aml_append(nvdimm_dev, method);
+
+ /* _LSW */
+ method = aml_method("_LSW", 3, AML_SERIALIZED);
+ aml_append(method, aml_store(aml_arg(2), aml_local(2)));
+ aml_append(method, aml_name_decl("INPT", aml_buffer(8, NULL)));
+ field = aml_create_dword_field(aml_name("INPT"),
+ aml_int(0), "OFST");
+ aml_append(method, field);
+ field = aml_create_dword_field(aml_name("INPT"),
+ aml_int(4), "TLEN");
+ aml_append(method, field);
+ aml_append(method, aml_store(aml_arg(0), aml_name("OFST")));
+ aml_append(method, aml_store(aml_arg(1), aml_name("TLEN")));
+
+ aml_append(method, aml_concatenate(aml_name("INPT"), aml_local(2),
+ aml_name("INPT")));
+ pkg = aml_package(1);
+ aml_append(pkg, aml_name("INPT"));
+ aml_append(method, aml_store(pkg, aml_local(0)));
+ com_call = aml_call5(NVDIMM_COMMON_DSM,
+ aml_touuid(NVDIMM_DEVICE_DSM_UUID),
+ aml_int(1), aml_int(6), aml_local(0),
+ aml_int(handle));
+ aml_append(method, aml_store(com_call, aml_local(3)));
+ field = aml_create_dword_field(aml_local(3), aml_int(0), "STTS");
+ aml_append(method, field);
+ aml_append(method, aml_return(aml_name("STTS")));
+
+ aml_append(nvdimm_dev, method);
+
nvdimm_build_device_dsm(nvdimm_dev, handle);
aml_append(root_dev, nvdimm_dev);
}
@@ -1282,14 +1370,15 @@ static void nvdimm_build_ssdt(GArray *table_offsets, GArray *table_data,
NVDIMMState *nvdimm_state,
uint32_t ram_slots, const char *oem_id)
{
+ int mem_addr_offset;
Aml *ssdt, *sb_scope, *dev;
- int mem_addr_offset, nvdimm_ssdt;
+ AcpiTable table = { .sig = "SSDT", .rev = 1,
+ .oem_id = oem_id, .oem_table_id = "NVDIMM" };
acpi_add_table(table_offsets, table_data);
+ acpi_table_begin(&table, table_data);
ssdt = init_aml_allocator();
- acpi_data_push(ssdt->buf, sizeof(AcpiTableHeader));
-
sb_scope = aml_scope("\\_SB");
dev = aml_device("NVDR");
@@ -1318,8 +1407,6 @@ static void nvdimm_build_ssdt(GArray *table_offsets, GArray *table_data,
aml_append(sb_scope, dev);
aml_append(ssdt, sb_scope);
- nvdimm_ssdt = table_data->len;
-
/* copy AML table into ACPI tables blob and patch header there */
g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len);
mem_addr_offset = build_append_named_dword(table_data,
@@ -1331,18 +1418,20 @@ static void nvdimm_build_ssdt(GArray *table_offsets, GArray *table_data,
bios_linker_loader_add_pointer(linker,
ACPI_BUILD_TABLE_FILE, mem_addr_offset, sizeof(uint32_t),
NVDIMM_DSM_MEM_FILE, 0);
- build_header(linker, table_data,
- (void *)(table_data->data + nvdimm_ssdt),
- "SSDT", table_data->len - nvdimm_ssdt, 1, oem_id, "NVDIMM");
free_aml_allocator();
+ /*
+ * must be executed as the last so that pointer patching command above
+ * would be executed by guest before it recalculated checksum which were
+ * scheduled by acpi_table_end()
+ */
+ acpi_table_end(linker, &table);
}
void nvdimm_build_srat(GArray *table_data)
{
- GSList *device_list = nvdimm_get_device_list();
+ GSList *device_list, *list = nvdimm_get_device_list();
- for (; device_list; device_list = device_list->next) {
- AcpiSratMemoryAffinity *numamem = NULL;
+ for (device_list = list; device_list; device_list = device_list->next) {
DeviceState *dev = device_list->data;
Object *obj = OBJECT(dev);
uint64_t addr, size;
@@ -1352,11 +1441,10 @@ void nvdimm_build_srat(GArray *table_data)
addr = object_property_get_uint(obj, PC_DIMM_ADDR_PROP, &error_abort);
size = object_property_get_uint(obj, PC_DIMM_SIZE_PROP, &error_abort);
- numamem = acpi_data_push(table_data, sizeof *numamem);
- build_srat_memory(numamem, addr, size, node,
+ build_srat_memory(table_data, addr, size, node,
MEM_AFFINITY_ENABLED | MEM_AFFINITY_NON_VOLATILE);
}
- g_slist_free(device_list);
+ g_slist_free(list);
}
void nvdimm_build_acpi(GArray *table_offsets, GArray *table_data,
diff --git a/hw/acpi/pci-bridge-stub.c b/hw/acpi/pci-bridge-stub.c
new file mode 100644
index 0000000000..9d78638c48
--- /dev/null
+++ b/hw/acpi/pci-bridge-stub.c
@@ -0,0 +1,20 @@
+/*
+ * QEMU ACPI PCI bridge stub
+ *
+ * Copyright (c) 2023 Red Hat, Inc.
+ *
+ * Author:
+ * Igor Mammedov <imammedo@redhat.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/acpi/pci.h"
+
+void build_pci_bridge_aml(AcpiDevAmlIf *adev, Aml *scope)
+{
+}
diff --git a/hw/acpi/pci-bridge.c b/hw/acpi/pci-bridge.c
new file mode 100644
index 0000000000..7baa7034a1
--- /dev/null
+++ b/hw/acpi/pci-bridge.c
@@ -0,0 +1,37 @@
+/*
+ * QEMU ACPI PCI bridge
+ *
+ * Copyright (c) 2023 Red Hat, Inc.
+ *
+ * Author:
+ * Igor Mammedov <imammedo@redhat.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/acpi/pci.h"
+#include "hw/pci/pci_bridge.h"
+#include "hw/acpi/pcihp.h"
+
+void build_pci_bridge_aml(AcpiDevAmlIf *adev, Aml *scope)
+{
+ PCIBridge *br = PCI_BRIDGE(adev);
+
+ if (!DEVICE(br)->hotplugged) {
+ PCIBus *sec_bus = pci_bridge_get_sec_bus(br);
+
+ build_append_pci_bus_devices(scope, sec_bus);
+
+ /*
+ * generate hotplug slots descriptors if
+ * bridge has ACPI PCI hotplug attached,
+ */
+ if (object_property_find(OBJECT(sec_bus), ACPI_PCIHP_PROP_BSEL)) {
+ build_append_pcihp_slots(scope, sec_bus);
+ }
+ }
+}
diff --git a/hw/acpi/pci.c b/hw/acpi/pci.c
index 75b1103ec4..20b70dcd81 100644
--- a/hw/acpi/pci.c
+++ b/hw/acpi/pci.c
@@ -28,19 +28,20 @@
#include "hw/acpi/pci.h"
#include "hw/pci/pcie_host.h"
+/*
+ * PCI Firmware Specification, Revision 3.0
+ * 4.1.2 MCFG Table Description.
+ */
void build_mcfg(GArray *table_data, BIOSLinker *linker, AcpiMcfgInfo *info,
const char *oem_id, const char *oem_table_id)
{
- int mcfg_start = table_data->len;
+ AcpiTable table = { .sig = "MCFG", .rev = 1,
+ .oem_id = oem_id, .oem_table_id = oem_table_id };
+
+ acpi_table_begin(&table, table_data);
- /*
- * PCI Firmware Specification, Revision 3.0
- * 4.1.2 MCFG Table Description.
- */
- acpi_data_push(table_data, sizeof(AcpiTableHeader));
/* Reserved */
build_append_int_noprefix(table_data, 0, 8);
-
/*
* Memory Mapped Enhanced Configuration Space Base Address Allocation
* Structure
@@ -56,6 +57,5 @@ void build_mcfg(GArray *table_data, BIOSLinker *linker, AcpiMcfgInfo *info,
/* Reserved */
build_append_int_noprefix(table_data, 0, 4);
- build_header(linker, table_data, (void *)(table_data->data + mcfg_start),
- "MCFG", table_data->len - mcfg_start, 1, oem_id, oem_table_id);
+ acpi_table_end(linker, &table);
}
diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c
index f610a25d2e..5f79c9016b 100644
--- a/hw/acpi/pcihp.c
+++ b/hw/acpi/pcihp.c
@@ -32,6 +32,7 @@
#include "hw/pci/pci_bridge.h"
#include "hw/pci/pci_host.h"
#include "hw/pci/pcie_port.h"
+#include "hw/pci-bridge/xio3130_downstream.h"
#include "hw/i386/acpi-build.h"
#include "hw/acpi/acpi.h"
#include "hw/pci/pci_bus.h"
@@ -53,21 +54,6 @@ typedef struct AcpiPciHpFind {
PCIBus *bus;
} AcpiPciHpFind;
-static gint g_cmp_uint32(gconstpointer a, gconstpointer b, gpointer user_data)
-{
- return a - b;
-}
-
-static GSequence *pci_acpi_index_list(void)
-{
- static GSequence *used_acpi_index_list;
-
- if (!used_acpi_index_list) {
- used_acpi_index_list = g_sequence_new(NULL);
- }
- return used_acpi_index_list;
-}
-
static int acpi_pcihp_get_bsel(PCIBus *bus)
{
Error *local_err = NULL;
@@ -84,31 +70,40 @@ static int acpi_pcihp_get_bsel(PCIBus *bus)
}
}
-/* Assign BSEL property to all buses. In the future, this can be changed
- * to only assign to buses that support hotplug.
- */
+typedef struct {
+ unsigned bsel_alloc;
+ bool has_bridge_hotplug;
+} BSELInfo;
+
+/* Assign BSEL property only to buses that support hotplug. */
static void *acpi_set_bsel(PCIBus *bus, void *opaque)
{
- unsigned *bsel_alloc = opaque;
+ BSELInfo *info = opaque;
unsigned *bus_bsel;
+ DeviceState *br = bus->qbus.parent;
+ bool is_bridge = IS_PCI_BRIDGE(br);
+ /* hotplugged bridges can't be described in ACPI ignore them */
if (qbus_is_hotpluggable(BUS(bus))) {
- bus_bsel = g_malloc(sizeof *bus_bsel);
+ if (!is_bridge || (!br->hotplugged && info->has_bridge_hotplug)) {
+ bus_bsel = g_malloc(sizeof *bus_bsel);
- *bus_bsel = (*bsel_alloc)++;
- object_property_add_uint32_ptr(OBJECT(bus), ACPI_PCIHP_PROP_BSEL,
- bus_bsel, OBJ_PROP_FLAG_READ);
+ *bus_bsel = info->bsel_alloc++;
+ object_property_add_uint32_ptr(OBJECT(bus), ACPI_PCIHP_PROP_BSEL,
+ bus_bsel, OBJ_PROP_FLAG_READ);
+ }
}
- return bsel_alloc;
+ return info;
}
-static void acpi_set_pci_info(void)
+static void acpi_set_pci_info(bool has_bridge_hotplug)
{
static bool bsel_is_set;
Object *host = acpi_get_i386_pci_host();
PCIBus *bus;
- unsigned bsel_alloc = ACPI_PCIHP_BSEL_DEFAULT;
+ BSELInfo info = { .bsel_alloc = ACPI_PCIHP_BSEL_DEFAULT,
+ .has_bridge_hotplug = has_bridge_hotplug };
if (bsel_is_set) {
return;
@@ -122,29 +117,10 @@ static void acpi_set_pci_info(void)
bus = PCI_HOST_BRIDGE(host)->bus;
if (bus) {
/* Scan all PCI buses. Set property to enable acpi based hotplug. */
- pci_for_each_bus_depth_first(bus, acpi_set_bsel, NULL, &bsel_alloc);
+ pci_for_each_bus_depth_first(bus, acpi_set_bsel, NULL, &info);
}
}
-static void acpi_pcihp_disable_root_bus(void)
-{
- static bool root_hp_disabled;
- Object *host = acpi_get_i386_pci_host();
- PCIBus *bus;
-
- if (root_hp_disabled) {
- return;
- }
-
- bus = PCI_HOST_BRIDGE(host)->bus;
- if (bus) {
- /* setting the hotplug handler to NULL makes the bus non-hotpluggable */
- qbus_set_hotplug_handler(BUS(bus), NULL);
- }
- root_hp_disabled = true;
- return;
-}
-
static void acpi_pcihp_test_hotplug_bus(PCIBus *bus, void *opaque)
{
AcpiPciHpFind *find = opaque;
@@ -190,14 +166,17 @@ static PCIBus *acpi_pcihp_find_hotplug_bus(AcpiPciHpState *s, int bsel)
static bool acpi_pcihp_pc_no_hotplug(AcpiPciHpState *s, PCIDevice *dev)
{
- PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
DeviceClass *dc = DEVICE_GET_CLASS(dev);
/*
* ACPI doesn't allow hotplug of bridge devices. Don't allow
* hot-unplug of bridge devices unless they were added by hotplug
* (and so, not described by acpi).
+ *
+ * Don't allow hot-unplug of SR-IOV Virtual Functions, as they
+ * will be removed implicitly, when Physical Function is unplugged.
*/
- return (pc->is_bridge && !dev->qdev.hotplugged) || !dc->hotpluggable;
+ return (IS_PCI_BRIDGE(dev) && !dev->qdev.hotplugged) || !dc->hotpluggable ||
+ pci_is_vf(dev);
}
static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slots)
@@ -222,9 +201,27 @@ static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slo
PCIDevice *dev = PCI_DEVICE(qdev);
if (PCI_SLOT(dev->devfn) == slot) {
if (!acpi_pcihp_pc_no_hotplug(s, dev)) {
- hotplug_ctrl = qdev_get_hotplug_handler(qdev);
- hotplug_handler_unplug(hotplug_ctrl, qdev, &error_abort);
- object_unparent(OBJECT(qdev));
+ /*
+ * partially_hotplugged is used by virtio-net failover:
+ * failover has asked the guest OS to unplug the device
+ * but we need to keep some references to the device
+ * to be able to plug it back in case of failure so
+ * we don't execute hotplug_handler_unplug().
+ */
+ if (dev->partially_hotplugged) {
+ /*
+ * pending_deleted_event is set to true when
+ * virtio-net failover asks to unplug the device,
+ * and set to false here when the operation is done
+ * This is used by the migration loop to detect the
+ * end of the operation and really start the migration.
+ */
+ qdev->pending_deleted_event = false;
+ } else {
+ hotplug_ctrl = qdev_get_hotplug_handler(qdev);
+ hotplug_handler_unplug(hotplug_ctrl, qdev, &error_abort);
+ object_unparent(OBJECT(qdev));
+ }
}
}
}
@@ -265,17 +262,12 @@ static void acpi_pcihp_update(AcpiPciHpState *s)
}
}
-void acpi_pcihp_reset(AcpiPciHpState *s, bool acpihp_root_off)
+void acpi_pcihp_reset(AcpiPciHpState *s)
{
- if (acpihp_root_off) {
- acpi_pcihp_disable_root_bus();
- }
- acpi_set_pci_info();
+ acpi_set_pci_info(s->use_acpi_hotplug_bridge);
acpi_pcihp_update(s);
}
-#define ONBOARD_INDEX_MAX (16 * 1024 - 1)
-
void acpi_pcihp_device_pre_plug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
@@ -288,34 +280,6 @@ void acpi_pcihp_device_pre_plug_cb(HotplugHandler *hotplug_dev,
ACPI_PCIHP_PROP_BSEL "' set");
return;
}
-
- /*
- * capped by systemd (see: udev-builtin-net_id.c)
- * as it's the only known user honor it to avoid users
- * misconfigure QEMU and then wonder why acpi-index doesn't work
- */
- if (pdev->acpi_index > ONBOARD_INDEX_MAX) {
- error_setg(errp, "acpi-index should be less or equal to %u",
- ONBOARD_INDEX_MAX);
- return;
- }
-
- /*
- * make sure that acpi-index is unique across all present PCI devices
- */
- if (pdev->acpi_index) {
- GSequence *used_indexes = pci_acpi_index_list();
-
- if (g_sequence_lookup(used_indexes, GINT_TO_POINTER(pdev->acpi_index),
- g_cmp_uint32, NULL)) {
- error_setg(errp, "a PCI device with acpi-index = %" PRIu32
- " already exist", pdev->acpi_index);
- return;
- }
- g_sequence_insert_sorted(used_indexes,
- GINT_TO_POINTER(pdev->acpi_index),
- g_cmp_uint32, NULL);
- }
}
void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s,
@@ -323,6 +287,8 @@ void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s,
{
PCIDevice *pdev = PCI_DEVICE(dev);
int slot = PCI_SLOT(pdev->devfn);
+ PCIDevice *bridge;
+ PCIBus *bus;
int bsel;
/* Don't send event when device is enabled during qemu machine creation:
@@ -333,17 +299,10 @@ void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s,
* Overwrite the default hotplug handler with the ACPI PCI one
* for cold plugged bridges only.
*/
- if (!s->legacy_piix &&
+ if (s->use_acpi_hotplug_bridge &&
object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) {
PCIBus *sec = pci_bridge_get_sec_bus(PCI_BRIDGE(pdev));
- /* Remove all hot-plug handlers if hot-plug is disabled on slot */
- if (object_dynamic_cast(OBJECT(dev), TYPE_PCIE_SLOT) &&
- !PCIE_SLOT(pdev)->hotplug) {
- qbus_set_hotplug_handler(BUS(sec), NULL);
- return;
- }
-
qbus_set_hotplug_handler(BUS(sec), OBJECT(hotplug_dev));
/* We don't have to overwrite any other hotplug handler yet */
assert(QLIST_EMPTY(&sec->child));
@@ -352,7 +311,14 @@ void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s,
return;
}
- bsel = acpi_pcihp_get_bsel(pci_get_bus(pdev));
+ bus = pci_get_bus(pdev);
+ bridge = pci_bridge_get_device(bus);
+ if (object_dynamic_cast(OBJECT(bridge), TYPE_PCIE_ROOT_PORT) ||
+ object_dynamic_cast(OBJECT(bridge), TYPE_XIO3130_DOWNSTREAM)) {
+ pcie_cap_slot_enable_power(bridge);
+ }
+
+ bsel = acpi_pcihp_get_bsel(bus);
g_assert(bsel >= 0);
s->acpi_pcihp_pci_status[bsel].up |= (1U << slot);
acpi_send_event(DEVICE(hotplug_dev), ACPI_PCI_HOTPLUG_STATUS);
@@ -366,17 +332,6 @@ void acpi_pcihp_device_unplug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s,
trace_acpi_pci_unplug(PCI_SLOT(pdev->devfn),
acpi_pcihp_get_bsel(pci_get_bus(pdev)));
- /*
- * clean up acpi-index so it could reused by another device
- */
- if (pdev->acpi_index) {
- GSequence *used_indexes = pci_acpi_index_list();
-
- g_sequence_remove(g_sequence_lookup(used_indexes,
- GINT_TO_POINTER(pdev->acpi_index),
- g_cmp_uint32, NULL));
- }
-
qdev_unrealize(dev);
}
@@ -396,10 +351,44 @@ void acpi_pcihp_device_unplug_request_cb(HotplugHandler *hotplug_dev,
return;
}
+ /*
+ * pending_deleted_event is used by virtio-net failover to detect the
+ * end of the unplug operation, the flag is set to false in
+ * acpi_pcihp_eject_slot() when the operation is completed.
+ */
+ pdev->qdev.pending_deleted_event = true;
+ /* if unplug was requested before OSPM is initialized,
+ * linux kernel will clear GPE0.sts[] bits during boot, which effectively
+ * hides unplug event. And than followup qmp_device_del() calls remain
+ * blocked by above flag permanently.
+ * Unblock qmp_device_del() by setting expire limit, so user can
+ * repeat unplug request later when OSPM has been booted.
+ */
+ pdev->qdev.pending_deleted_expires_ms =
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); /* 1 msec */
+
s->acpi_pcihp_pci_status[bsel].down |= (1U << slot);
acpi_send_event(DEVICE(hotplug_dev), ACPI_PCI_HOTPLUG_STATUS);
}
+bool acpi_pcihp_is_hotpluggbale_bus(AcpiPciHpState *s, BusState *bus)
+{
+ Object *o = OBJECT(bus->parent);
+
+ if (s->use_acpi_hotplug_bridge &&
+ object_dynamic_cast(o, TYPE_PCI_BRIDGE)) {
+ if (object_dynamic_cast(o, TYPE_PCIE_SLOT) && !PCIE_SLOT(o)->hotplug) {
+ return false;
+ }
+ return true;
+ }
+
+ if (s->use_acpi_root_pci_hotplug) {
+ return true;
+ }
+ return false;
+}
+
static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size)
{
AcpiPciHpState *s = opaque;
@@ -413,7 +402,7 @@ static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size)
switch (addr) {
case PCI_UP_BASE:
val = s->acpi_pcihp_pci_status[bsel].up;
- if (!s->legacy_piix) {
+ if (s->use_acpi_hotplug_bridge) {
s->acpi_pcihp_pci_status[bsel].up = 0;
}
trace_acpi_pci_up_read(val);
@@ -467,6 +456,9 @@ static void pci_write(void *opaque, hwaddr addr, uint64_t data,
}
bus = acpi_pcihp_find_hotplug_bus(s, s->hotplug_select);
+ if (!bus) {
+ break;
+ }
QTAILQ_FOREACH_SAFE(kid, &bus->qbus.children, sibling, next) {
Object *o = OBJECT(kid->child);
PCIDevice *dev = PCI_DEVICE(o);
@@ -485,7 +477,8 @@ static void pci_write(void *opaque, hwaddr addr, uint64_t data,
trace_acpi_pci_ej_write(addr, data);
break;
case PCI_SEL_BASE:
- s->hotplug_select = s->legacy_piix ? ACPI_PCIHP_BSEL_DEFAULT : data;
+ s->hotplug_select = s->use_acpi_hotplug_bridge ? data :
+ ACPI_PCIHP_BSEL_DEFAULT;
trace_acpi_pci_sel_write(addr, data);
default:
break;
@@ -503,18 +496,16 @@ static const MemoryRegionOps acpi_pcihp_io_ops = {
};
void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus,
- MemoryRegion *address_space_io, bool bridges_enabled,
- uint16_t io_base)
+ MemoryRegion *io, uint16_t io_base)
{
s->io_len = ACPI_PCIHP_SIZE;
s->io_base = io_base;
s->root = root_bus;
- s->legacy_piix = !bridges_enabled;
memory_region_init_io(&s->io, owner, &acpi_pcihp_io_ops, s,
"acpi-pci-hotplug", s->io_len);
- memory_region_add_subregion(address_space_io, s->io_base, &s->io);
+ memory_region_add_subregion(io, s->io_base, &s->io);
object_property_add_uint16_ptr(owner, ACPI_PCIHP_IO_BASE_PROP, &s->io_base,
OBJ_PROP_FLAG_READ);
@@ -522,17 +513,11 @@ void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus,
OBJ_PROP_FLAG_READ);
}
-bool vmstate_acpi_pcihp_use_acpi_index(void *opaque, int version_id)
-{
- AcpiPciHpState *s = opaque;
- return s->acpi_index;
-}
-
const VMStateDescription vmstate_acpi_pcihp_pci_status = {
.name = "acpi_pcihp_pci_status",
.version_id = 1,
.minimum_version_id = 1,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_UINT32(up, AcpiPciHpPciStatus),
VMSTATE_UINT32(down, AcpiPciHpPciStatus),
VMSTATE_END_OF_LIST()
diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
index f0b5fac44a..debe1adb84 100644
--- a/hw/acpi/piix4.c
+++ b/hw/acpi/piix4.c
@@ -20,20 +20,19 @@
*/
#include "qemu/osdep.h"
-#include "hw/i386/pc.h"
-#include "hw/southbridge/piix.h"
#include "hw/irq.h"
#include "hw/isa/apm.h"
#include "hw/i2c/pm_smbus.h"
#include "hw/pci/pci.h"
#include "hw/qdev-properties.h"
#include "hw/acpi/acpi.h"
+#include "hw/acpi/pcihp.h"
+#include "hw/acpi/piix4.h"
#include "sysemu/runstate.h"
#include "sysemu/sysemu.h"
#include "sysemu/xen.h"
#include "qapi/error.h"
#include "qemu/range.h"
-#include "hw/acpi/pcihp.h"
#include "hw/acpi/cpu_hotplug.h"
#include "hw/acpi/cpu.h"
#include "hw/hotplug.h"
@@ -43,7 +42,6 @@
#include "hw/acpi/acpi_dev_interface.h"
#include "migration/vmstate.h"
#include "hw/core/cpu.h"
-#include "trace.h"
#include "qom/object.h"
#define GPE_BASE 0xafe0
@@ -56,46 +54,6 @@ struct pci_status {
uint32_t down;
};
-struct PIIX4PMState {
- /*< private >*/
- PCIDevice parent_obj;
- /*< public >*/
-
- MemoryRegion io;
- uint32_t io_base;
-
- MemoryRegion io_gpe;
- ACPIREGS ar;
-
- APMState apm;
-
- PMSMBus smb;
- uint32_t smb_io_base;
-
- qemu_irq irq;
- qemu_irq smi_irq;
- int smm_enabled;
- bool smm_compat;
- Notifier machine_ready;
- Notifier powerdown_notifier;
-
- AcpiPciHpState acpi_pci_hotplug;
- bool use_acpi_hotplug_bridge;
- bool use_acpi_root_pci_hotplug;
-
- uint8_t disable_s3;
- uint8_t disable_s4;
- uint8_t s4_val;
-
- bool cpu_hotplug_legacy;
- AcpiCpuHotplug gpe_cpu;
- CPUHotplugState cpuhp_state;
-
- MemHotplugState acpi_memory_hotplug;
-};
-
-OBJECT_DECLARE_SIMPLE_TYPE(PIIX4PMState, PIIX4_PM)
-
static void piix4_acpi_system_hot_add_init(MemoryRegion *parent,
PCIBus *bus, PIIX4PMState *s);
@@ -189,7 +147,7 @@ static const VMStateDescription vmstate_gpe = {
.name = "gpe",
.version_id = 1,
.minimum_version_id = 1,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_GPE_ARRAY(sts, ACPIGPE),
VMSTATE_GPE_ARRAY(en, ACPIGPE),
VMSTATE_END_OF_LIST()
@@ -200,7 +158,7 @@ static const VMStateDescription vmstate_pci_status = {
.name = "pci_status",
.version_id = 1,
.minimum_version_id = 1,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_UINT32(up, struct AcpiPciHpPciStatus),
VMSTATE_UINT32(down, struct AcpiPciHpPciStatus),
VMSTATE_END_OF_LIST()
@@ -210,14 +168,14 @@ static const VMStateDescription vmstate_pci_status = {
static bool vmstate_test_use_acpi_hotplug_bridge(void *opaque, int version_id)
{
PIIX4PMState *s = opaque;
- return s->use_acpi_hotplug_bridge;
+ return s->acpi_pci_hotplug.use_acpi_hotplug_bridge;
}
static bool vmstate_test_no_use_acpi_hotplug_bridge(void *opaque,
int version_id)
{
PIIX4PMState *s = opaque;
- return !s->use_acpi_hotplug_bridge;
+ return !s->acpi_pci_hotplug.use_acpi_hotplug_bridge;
}
static bool vmstate_test_use_memhp(void *opaque)
@@ -230,9 +188,8 @@ static const VMStateDescription vmstate_memhp_state = {
.name = "piix4_pm/memhp",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.needed = vmstate_test_use_memhp,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_MEMORY_HOTPLUG(acpi_memory_hotplug, PIIX4PMState),
VMSTATE_END_OF_LIST()
}
@@ -255,10 +212,9 @@ static const VMStateDescription vmstate_cpuhp_state = {
.name = "piix4_pm/cpuhp",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.needed = vmstate_test_use_cpuhp,
.pre_load = vmstate_cpuhp_pre_load,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_CPU_HOTPLUG(cpuhp_state, PIIX4PMState),
VMSTATE_END_OF_LIST()
}
@@ -269,6 +225,17 @@ static bool piix4_vmstate_need_smbus(void *opaque, int version_id)
return pm_smbus_vmstate_needed();
}
+/*
+ * This is a fudge to turn off the acpi_index field,
+ * whose test was always broken on piix4 with 6.2 and older machine types.
+ */
+static bool vmstate_test_migrate_acpi_index(void *opaque, int version_id)
+{
+ PIIX4PMState *s = PIIX4_PM(opaque);
+ return s->acpi_pci_hotplug.use_acpi_hotplug_bridge &&
+ !s->not_migrate_acpi_index;
+}
+
/* qemu-kvm 1.2 uses version 3 but advertised as 2
* To support incoming qemu-kvm 1.2 migration, change version_id
* and minimum_version_id to 2 below (which breaks migration from
@@ -280,7 +247,7 @@ static const VMStateDescription vmstate_acpi = {
.version_id = 3,
.minimum_version_id = 3,
.post_load = vmstate_acpi_post_load,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_PCI_DEVICE(parent_obj, PIIX4PMState),
VMSTATE_UINT16(ar.pm1.evt.sts, PIIX4PMState),
VMSTATE_UINT16(ar.pm1.evt.en, PIIX4PMState),
@@ -299,10 +266,10 @@ static const VMStateDescription vmstate_acpi = {
struct AcpiPciHpPciStatus),
VMSTATE_PCI_HOTPLUG(acpi_pci_hotplug, PIIX4PMState,
vmstate_test_use_acpi_hotplug_bridge,
- vmstate_acpi_pcihp_use_acpi_index),
+ vmstate_test_migrate_acpi_index),
VMSTATE_END_OF_LIST()
},
- .subsections = (const VMStateDescription*[]) {
+ .subsections = (const VMStateDescription * const []) {
&vmstate_memhp_state,
&vmstate_cpuhp_state,
NULL
@@ -335,7 +302,10 @@ static void piix4_pm_reset(DeviceState *dev)
acpi_update_sci(&s->ar, s->irq);
pm_io_space_update(s);
- acpi_pcihp_reset(&s->acpi_pci_hotplug, !s->use_acpi_root_pci_hotplug);
+ if (s->acpi_pci_hotplug.use_acpi_hotplug_bridge ||
+ s->acpi_pci_hotplug.use_acpi_root_pci_hotplug) {
+ acpi_pcihp_reset(&s->acpi_pci_hotplug);
+ }
}
static void piix4_pm_powerdown_req(Notifier *n, void *opaque)
@@ -432,6 +402,13 @@ static void piix4_device_unplug_cb(HotplugHandler *hotplug_dev,
}
}
+static bool piix4_is_hotpluggable_bus(HotplugHandler *hotplug_dev,
+ BusState *bus)
+{
+ PIIX4PMState *s = PIIX4_PM(hotplug_dev);
+ return acpi_pcihp_is_hotpluggbale_bus(&s->acpi_pci_hotplug, bus);
+}
+
static void piix4_pm_machine_ready(Notifier *n, void *opaque)
{
PIIX4PMState *s = container_of(n, PIIX4PMState, machine_ready);
@@ -516,39 +493,22 @@ static void piix4_pm_realize(PCIDevice *dev, Error **errp)
s->machine_ready.notify = piix4_pm_machine_ready;
qemu_add_machine_init_done_notifier(&s->machine_ready);
+ if (xen_enabled()) {
+ s->acpi_pci_hotplug.use_acpi_hotplug_bridge = false;
+ }
+
piix4_acpi_system_hot_add_init(pci_address_space_io(dev),
pci_get_bus(dev), s);
- qbus_set_hotplug_handler(BUS(pci_get_bus(dev)), OBJECT(s));
piix4_pm_add_properties(s);
}
-I2CBus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
- qemu_irq sci_irq, qemu_irq smi_irq,
- int smm_enabled, DeviceState **piix4_pm)
+static void piix4_pm_init(Object *obj)
{
- PCIDevice *pci_dev;
- DeviceState *dev;
- PIIX4PMState *s;
-
- pci_dev = pci_new(devfn, TYPE_PIIX4_PM);
- dev = DEVICE(pci_dev);
- qdev_prop_set_uint32(dev, "smb_io_base", smb_io_base);
- if (piix4_pm) {
- *piix4_pm = dev;
- }
-
- s = PIIX4_PM(dev);
- s->irq = sci_irq;
- s->smi_irq = smi_irq;
- s->smm_enabled = smm_enabled;
- if (xen_enabled()) {
- s->use_acpi_hotplug_bridge = false;
- }
-
- pci_realize_and_unref(pci_dev, bus, &error_fatal);
+ PIIX4PMState *s = PIIX4_PM(obj);
- return s->smb.smbus;
+ qdev_init_gpio_out(DEVICE(obj), &s->irq, 1);
+ qdev_init_gpio_out_named(DEVICE(obj), &s->smi_irq, "smi-irq", 1);
}
static uint64_t gpe_readb(void *opaque, hwaddr addr, unsigned width)
@@ -556,7 +516,6 @@ static uint64_t gpe_readb(void *opaque, hwaddr addr, unsigned width)
PIIX4PMState *s = opaque;
uint32_t val = acpi_gpe_ioport_readb(&s->ar, addr);
- trace_piix4_gpe_readb(addr, width, val);
return val;
}
@@ -565,7 +524,6 @@ static void gpe_writeb(void *opaque, hwaddr addr, uint64_t val,
{
PIIX4PMState *s = opaque;
- trace_piix4_gpe_writeb(addr, width, val);
acpi_gpe_ioport_writeb(&s->ar, addr, val);
acpi_update_sci(&s->ar, s->irq);
}
@@ -607,9 +565,11 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent,
"acpi-gpe0", GPE_LEN);
memory_region_add_subregion(parent, GPE_BASE, &s->io_gpe);
- if (s->use_acpi_hotplug_bridge || s->use_acpi_root_pci_hotplug) {
+ if (s->acpi_pci_hotplug.use_acpi_hotplug_bridge ||
+ s->acpi_pci_hotplug.use_acpi_root_pci_hotplug) {
acpi_pcihp_init(OBJECT(s), &s->acpi_pci_hotplug, bus, parent,
- s->use_acpi_hotplug_bridge, ACPI_PCIHP_ADDR_PIIX4);
+ ACPI_PCIHP_ADDR_PIIX4);
+ qbus_set_hotplug_handler(BUS(pci_get_bus(PCI_DEVICE(s))), OBJECT(s));
}
s->cpu_hotplug_legacy = true;
@@ -648,12 +608,15 @@ static Property piix4_pm_properties[] = {
DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_DISABLED, PIIX4PMState, disable_s4, 0),
DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_VAL, PIIX4PMState, s4_val, 2),
DEFINE_PROP_BOOL(ACPI_PM_PROP_ACPI_PCIHP_BRIDGE, PIIX4PMState,
- use_acpi_hotplug_bridge, true),
+ acpi_pci_hotplug.use_acpi_hotplug_bridge, true),
DEFINE_PROP_BOOL(ACPI_PM_PROP_ACPI_PCI_ROOTHP, PIIX4PMState,
- use_acpi_root_pci_hotplug, true),
+ acpi_pci_hotplug.use_acpi_root_pci_hotplug, true),
DEFINE_PROP_BOOL("memory-hotplug-support", PIIX4PMState,
acpi_memory_hotplug.is_enabled, true),
DEFINE_PROP_BOOL("smm-compat", PIIX4PMState, smm_compat, false),
+ DEFINE_PROP_BOOL("smm-enabled", PIIX4PMState, smm_enabled, false),
+ DEFINE_PROP_BOOL("x-not-migrate-acpi-index", PIIX4PMState,
+ not_migrate_acpi_index, false),
DEFINE_PROP_END_OF_LIST(),
};
@@ -684,14 +647,15 @@ static void piix4_pm_class_init(ObjectClass *klass, void *data)
hc->plug = piix4_device_plug_cb;
hc->unplug_request = piix4_device_unplug_request_cb;
hc->unplug = piix4_device_unplug_cb;
+ hc->is_hotpluggable_bus = piix4_is_hotpluggable_bus;
adevc->ospm_status = piix4_ospm_status;
adevc->send_event = piix4_send_gpe;
- adevc->madt_cpu = pc_madt_cpu_entry;
}
static const TypeInfo piix4_pm_info = {
.name = TYPE_PIIX4_PM,
.parent = TYPE_PCI_DEVICE,
+ .instance_init = piix4_pm_init,
.instance_size = sizeof(PIIX4PMState),
.class_init = piix4_pm_class_init,
.interfaces = (InterfaceInfo[]) {
diff --git a/hw/acpi/trace-events b/hw/acpi/trace-events
index 974d770e8b..edc93e703c 100644
--- a/hw/acpi/trace-events
+++ b/hw/acpi/trace-events
@@ -17,6 +17,12 @@ mhp_acpi_clear_remove_evt(uint32_t slot) "slot[0x%"PRIx32"] clear remove event"
mhp_acpi_pc_dimm_deleted(uint32_t slot) "slot[0x%"PRIx32"] pc-dimm deleted"
mhp_acpi_pc_dimm_delete_failed(uint32_t slot) "slot[0x%"PRIx32"] pc-dimm delete failed"
+# core.c
+acpi_gpe_en_ioport_readb(uint32_t addr, uint8_t val) "addr: 0x%" PRIx32 " ==> 0x%02" PRIx8
+acpi_gpe_en_ioport_writeb(uint32_t addr, uint8_t val) "addr: 0x%" PRIx32 " <== 0x%02" PRIx8
+acpi_gpe_sts_ioport_readb(uint32_t addr, uint8_t val) "addr: 0x%" PRIx32 " ==> 0x%02" PRIx8
+acpi_gpe_sts_ioport_writeb(uint32_t addr, uint8_t val) "addr: 0x%" PRIx32 " <== 0x%02" PRIx8
+
# cpu.c
cpuhp_acpi_invalid_idx_selected(uint32_t idx) "0x%"PRIx32
cpuhp_acpi_read_flags(uint32_t idx, uint8_t flags) "idx[0x%"PRIx32"] flags: 0x%"PRIx8
@@ -48,10 +54,36 @@ acpi_pci_sel_read(uint32_t val) "%" PRIu32
acpi_pci_ej_write(uint64_t addr, uint64_t data) "0x%" PRIx64 " <== %" PRIu64
acpi_pci_sel_write(uint64_t addr, uint64_t data) "0x%" PRIx64 " <== %" PRIu64
-# piix4.c
-piix4_gpe_readb(uint64_t addr, unsigned width, uint64_t val) "addr: 0x%" PRIx64 " width: %d ==> 0x%" PRIx64
-piix4_gpe_writeb(uint64_t addr, unsigned width, uint64_t val) "addr: 0x%" PRIx64 " width: %d <== 0x%" PRIx64
-
# tco.c
tco_timer_reload(int ticks, int msec) "ticks=%d (%d ms)"
tco_timer_expired(int timeouts_no, bool strap, bool no_reboot) "timeouts_no=%d no_reboot=%d/%d"
+tco_io_write(uint64_t addr, uint32_t val) "addr=0x%" PRIx64 " val=0x%" PRIx32
+tco_io_read(uint64_t addr, uint32_t val) "addr=0x%" PRIx64 " val=0x%" PRIx32
+
+# erst.c
+acpi_erst_reg_write(uint64_t addr, uint64_t val, unsigned size) "addr: 0x%04" PRIx64 " <== 0x%016" PRIx64 " (size: %u)"
+acpi_erst_reg_read(uint64_t addr, uint64_t val, unsigned size) " addr: 0x%04" PRIx64 " ==> 0x%016" PRIx64 " (size: %u)"
+acpi_erst_mem_write(uint64_t addr, uint64_t val, unsigned size) "addr: 0x%06" PRIx64 " <== 0x%016" PRIx64 " (size: %u)"
+acpi_erst_mem_read(uint64_t addr, uint64_t val, unsigned size) " addr: 0x%06" PRIx64 " ==> 0x%016" PRIx64 " (size: %u)"
+acpi_erst_pci_bar_0(uint64_t addr) "BAR0: 0x%016" PRIx64
+acpi_erst_pci_bar_1(uint64_t addr) "BAR1: 0x%016" PRIx64
+acpi_erst_realizefn_in(void)
+acpi_erst_realizefn_out(unsigned size) "total nvram size %u bytes"
+acpi_erst_reset_in(unsigned record_count) "record_count %u"
+acpi_erst_reset_out(unsigned record_count) "record_count %u"
+acpi_erst_post_load(void *header, unsigned slot_size) "header: 0x%p slot_size %u"
+acpi_erst_class_init_in(void)
+acpi_erst_class_init_out(void)
+
+# nvdimm.c
+acpi_nvdimm_read_fit(uint32_t offset, uint32_t len, const char *dirty) "Read FIT: offset 0x%" PRIx32 " FIT size 0x%" PRIx32 " Dirty %s"
+acpi_nvdimm_label_info(uint32_t label_size, uint32_t mxfer) "label_size 0x%" PRIx32 ", max_xfer 0x%" PRIx32
+acpi_nvdimm_label_overflow(uint32_t offset, uint32_t length) "offset 0x%" PRIx32 " + length 0x%" PRIx32 " is overflow"
+acpi_nvdimm_label_oversize(uint32_t pos, uint64_t size) "position 0x%" PRIx32 " is beyond label data (len = %" PRIu64 ")"
+acpi_nvdimm_label_xfer_exceed(uint32_t length, uint32_t max_xfer) "length (0x%" PRIx32 ") is larger than max_xfer (0x%" PRIx32 ")"
+acpi_nvdimm_read_label(uint32_t offset, uint32_t length) "Read Label Data: offset 0x%" PRIx32 " length 0x%" PRIx32
+acpi_nvdimm_write_label(uint32_t offset, uint32_t length) "Write Label Data: offset 0x%" PRIx32 " length 0x%" PRIx32
+acpi_nvdimm_read_io_port(void) "Alert: we never read _DSM IO Port"
+acpi_nvdimm_dsm_mem_addr(uint64_t dsm_mem_addr) "dsm memory address 0x%" PRIx64
+acpi_nvdimm_dsm_info(uint32_t revision, uint32_t handle, uint32_t function) "Revision 0x%" PRIx32 " Handle 0x%" PRIx32 " Function 0x%" PRIx32
+acpi_nvdimm_invalid_revision(uint32_t revision) "Revision 0x%" PRIx32 " is not supported, expect 0x1"
diff --git a/hw/acpi/viot.c b/hw/acpi/viot.c
new file mode 100644
index 0000000000..4e0bf69067
--- /dev/null
+++ b/hw/acpi/viot.c
@@ -0,0 +1,143 @@
+/*
+ * ACPI Virtual I/O Translation table implementation
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/osdep.h"
+#include "hw/acpi/acpi.h"
+#include "hw/acpi/aml-build.h"
+#include "hw/acpi/viot.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_host.h"
+
+struct viot_pci_host_range {
+ int min_bus;
+ int max_bus;
+};
+
+static void build_pci_host_range(GArray *table_data, int min_bus, int max_bus,
+ uint16_t output_node)
+{
+ /* Type */
+ build_append_int_noprefix(table_data, 1 /* PCI range */, 1);
+ /* Reserved */
+ build_append_int_noprefix(table_data, 0, 1);
+ /* Length */
+ build_append_int_noprefix(table_data, 24, 2);
+ /* Endpoint start */
+ build_append_int_noprefix(table_data, PCI_BUILD_BDF(min_bus, 0), 4);
+ /* PCI Segment start */
+ build_append_int_noprefix(table_data, 0, 2);
+ /* PCI Segment end */
+ build_append_int_noprefix(table_data, 0, 2);
+ /* PCI BDF start */
+ build_append_int_noprefix(table_data, PCI_BUILD_BDF(min_bus, 0), 2);
+ /* PCI BDF end */
+ build_append_int_noprefix(table_data, PCI_BUILD_BDF(max_bus, 0xff), 2);
+ /* Output node */
+ build_append_int_noprefix(table_data, output_node, 2);
+ /* Reserved */
+ build_append_int_noprefix(table_data, 0, 6);
+}
+
+/* Build PCI range for a given PCI host bridge */
+static int enumerate_pci_host_bridges(Object *obj, void *opaque)
+{
+ GArray *pci_host_ranges = opaque;
+
+ if (object_dynamic_cast(obj, TYPE_PCI_HOST_BRIDGE)) {
+ PCIBus *bus = PCI_HOST_BRIDGE(obj)->bus;
+
+ if (bus && !pci_bus_bypass_iommu(bus)) {
+ int min_bus, max_bus;
+
+ pci_bus_range(bus, &min_bus, &max_bus);
+
+ const struct viot_pci_host_range pci_host_range = {
+ .min_bus = min_bus,
+ .max_bus = max_bus,
+ };
+ g_array_append_val(pci_host_ranges, pci_host_range);
+ }
+ }
+
+ return 0;
+}
+
+static gint pci_host_range_compare(gconstpointer a, gconstpointer b)
+{
+ struct viot_pci_host_range *range_a = (struct viot_pci_host_range *)a;
+ struct viot_pci_host_range *range_b = (struct viot_pci_host_range *)b;
+
+ if (range_a->min_bus < range_b->min_bus) {
+ return -1;
+ } else if (range_a->min_bus > range_b->min_bus) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/*
+ * Generate a VIOT table with one PCI-based virtio-iommu that manages PCI
+ * endpoints.
+ *
+ * Defined in the ACPI Specification (Version TBD)
+ */
+void build_viot(MachineState *ms, GArray *table_data, BIOSLinker *linker,
+ uint16_t virtio_iommu_bdf, const char *oem_id,
+ const char *oem_table_id)
+{
+ /* The virtio-iommu node follows the 48-bytes header */
+ int viommu_off = 48;
+ AcpiTable table = { .sig = "VIOT", .rev = 0,
+ .oem_id = oem_id, .oem_table_id = oem_table_id };
+ GArray *pci_host_ranges = g_array_new(false, true,
+ sizeof(struct viot_pci_host_range));
+ struct viot_pci_host_range *pci_host_range;
+ int i;
+
+ /* Build the list of PCI ranges that this viommu manages */
+ object_child_foreach_recursive(OBJECT(ms), enumerate_pci_host_bridges,
+ pci_host_ranges);
+
+ /* Sort the pci host ranges by min_bus */
+ g_array_sort(pci_host_ranges, pci_host_range_compare);
+
+ /* ACPI table header */
+ acpi_table_begin(&table, table_data);
+ /* Node count */
+ build_append_int_noprefix(table_data, pci_host_ranges->len + 1, 2);
+ /* Node offset */
+ build_append_int_noprefix(table_data, viommu_off, 2);
+ /* Reserved */
+ build_append_int_noprefix(table_data, 0, 8);
+
+ /* Virtio-iommu node */
+ /* Type */
+ build_append_int_noprefix(table_data, 3 /* virtio-pci IOMMU */, 1);
+ /* Reserved */
+ build_append_int_noprefix(table_data, 0, 1);
+ /* Length */
+ build_append_int_noprefix(table_data, 16, 2);
+ /* PCI Segment */
+ build_append_int_noprefix(table_data, 0, 2);
+ /* PCI BDF number */
+ build_append_int_noprefix(table_data, virtio_iommu_bdf, 2);
+ /* Reserved */
+ build_append_int_noprefix(table_data, 0, 8);
+
+ /* PCI ranges found above */
+ for (i = 0; i < pci_host_ranges->len; i++) {
+ pci_host_range = &g_array_index(pci_host_ranges,
+ struct viot_pci_host_range, i);
+
+ build_pci_host_range(table_data, pci_host_range->min_bus,
+ pci_host_range->max_bus, viommu_off);
+ }
+
+ g_array_free(pci_host_ranges, true);
+
+ acpi_table_end(linker, &table);
+}
+
diff --git a/hw/acpi/viot.h b/hw/acpi/viot.h
new file mode 100644
index 0000000000..9fe565bb87
--- /dev/null
+++ b/hw/acpi/viot.h
@@ -0,0 +1,13 @@
+/*
+ * ACPI Virtual I/O Translation Table implementation
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef VIOT_H
+#define VIOT_H
+
+void build_viot(MachineState *ms, GArray *table_data, BIOSLinker *linker,
+ uint16_t virtio_iommu_bdf, const char *oem_id,
+ const char *oem_table_id);
+
+#endif /* VIOT_H */
diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c
index 4f41a13ea0..e63c8af4c3 100644
--- a/hw/acpi/vmgenid.c
+++ b/hw/acpi/vmgenid.c
@@ -12,7 +12,6 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
-#include "qapi/qapi-commands-machine.h"
#include "qemu/module.h"
#include "hw/acpi/acpi.h"
#include "hw/acpi/aml-build.h"
@@ -29,6 +28,8 @@ void vmgenid_build_acpi(VmGenIdState *vms, GArray *table_data, GArray *guid,
Aml *ssdt, *dev, *scope, *method, *addr, *if_ctx;
uint32_t vgia_offset;
QemuUUID guid_le;
+ AcpiTable table = { .sig = "SSDT", .rev = 1,
+ .oem_id = oem_id, .oem_table_id = "VMGENID" };
/* Fill in the GUID values. These need to be converted to little-endian
* first, since that's what the guest expects
@@ -42,12 +43,10 @@ void vmgenid_build_acpi(VmGenIdState *vms, GArray *table_data, GArray *guid,
g_array_insert_vals(guid, VMGENID_GUID_OFFSET, guid_le.data,
ARRAY_SIZE(guid_le.data));
- /* Put this in a separate SSDT table */
+ /* Put VMGNEID into a separate SSDT table */
+ acpi_table_begin(&table, table_data);
ssdt = init_aml_allocator();
- /* Reserve space for header */
- acpi_data_push(ssdt->buf, sizeof(AcpiTableHeader));
-
/* Storage for the GUID address */
vgia_offset = table_data->len +
build_append_named_dword(ssdt->buf, "VGIA");
@@ -116,9 +115,8 @@ void vmgenid_build_acpi(VmGenIdState *vms, GArray *table_data, GArray *guid,
ACPI_BUILD_TABLE_FILE, vgia_offset, sizeof(uint32_t),
VMGENID_GUID_FW_CFG_FILE, 0);
- build_header(linker, table_data,
- (void *)(table_data->data + table_data->len - ssdt->buf->len),
- "SSDT", ssdt->buf->len, 1, oem_id, "VMGENID");
+ /* must be called after above command to ensure correct table checksum */
+ acpi_table_end(linker, &table);
free_aml_allocator();
}
@@ -180,7 +178,7 @@ static const VMStateDescription vmstate_vmgenid = {
.version_id = 1,
.minimum_version_id = 1,
.post_load = vmgenid_post_load,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_UINT8_ARRAY(vmgenid_addr_le, VmGenIdState, sizeof(uint64_t)),
VMSTATE_END_OF_LIST()
},
@@ -245,20 +243,3 @@ static void vmgenid_register_types(void)
}
type_init(vmgenid_register_types)
-
-GuidInfo *qmp_query_vm_generation_id(Error **errp)
-{
- GuidInfo *info;
- VmGenIdState *vms;
- Object *obj = find_vmgenid_dev();
-
- if (!obj) {
- error_setg(errp, "VM Generation ID device not found");
- return NULL;
- }
- vms = VMGENID(obj);
-
- info = g_malloc0(sizeof(*info));
- info->guid = qemu_uuid_unparse_strdup(&vms->guid);
- return info;
-}