aboutsummaryrefslogtreecommitdiff
path: root/hw/acpi
diff options
context:
space:
mode:
Diffstat (limited to 'hw/acpi')
-rw-r--r--hw/acpi/acpi-pci-hotplug-stub.c2
-rw-r--r--hw/acpi/aml-build.c27
-rw-r--r--hw/acpi/bios-linker-loader.c2
-rw-r--r--hw/acpi/generic_event_device.c77
-rw-r--r--hw/acpi/ich9.c7
-rw-r--r--hw/acpi/pci-bridge.c54
-rw-r--r--hw/acpi/pci.c50
-rw-r--r--hw/acpi/pcihp.c439
-rw-r--r--hw/acpi/piix4.c5
9 files changed, 641 insertions, 22 deletions
diff --git a/hw/acpi/acpi-pci-hotplug-stub.c b/hw/acpi/acpi-pci-hotplug-stub.c
index b7bc6e4..d58ea72 100644
--- a/hw/acpi/acpi-pci-hotplug-stub.c
+++ b/hw/acpi/acpi-pci-hotplug-stub.c
@@ -4,7 +4,7 @@
const VMStateDescription vmstate_acpi_pcihp_pci_status;
-void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus,
+void acpi_pcihp_init(Object *owner, AcpiPciHpState *s,
MemoryRegion *address_space_io, uint16_t io_base)
{
}
diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c
index f8f93a9..1e685f9 100644
--- a/hw/acpi/aml-build.c
+++ b/hw/acpi/aml-build.c
@@ -160,7 +160,7 @@ void crs_replace_with_free_ranges(GPtrArray *ranges,
*/
static void crs_range_merge(GPtrArray *range)
{
- GPtrArray *tmp = g_ptr_array_new_with_free_func(crs_range_free);
+ g_autoptr(GPtrArray) tmp = g_ptr_array_new_with_free_func(crs_range_free);
CrsRangeEntry *entry;
uint64_t range_base, range_limit;
int i;
@@ -191,7 +191,6 @@ static void crs_range_merge(GPtrArray *range)
entry = g_ptr_array_index(tmp, i);
crs_range_insert(range, entry->base, entry->limit);
}
- g_ptr_array_free(tmp, true);
}
static void
@@ -2153,6 +2152,7 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms,
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;
+ uint32_t root_offset;
int n;
AcpiTable table = { .sig = "PPTT", .rev = 2,
.oem_id = oem_id, .oem_table_id = oem_table_id };
@@ -2160,6 +2160,18 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms,
acpi_table_begin(&table, table_data);
/*
+ * Build a root node for all the processor nodes. Otherwise when
+ * building a multi-socket system each socket tree is separated
+ * and will be hard for the OS like Linux to know whether the
+ * system is homogeneous.
+ */
+ root_offset = table_data->len - pptt_start;
+ build_processor_hierarchy_node(table_data,
+ (1 << 0) | /* Physical package */
+ (1 << 4), /* Identical Implementation */
+ 0, 0, NULL, 0);
+
+ /*
* 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
@@ -2173,8 +2185,9 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms,
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);
+ (1 << 0) | /* Physical package */
+ (1 << 4), /* Identical Implementation */
+ root_offset, socket_id, NULL, 0);
}
if (mc->smp_props.clusters_supported && mc->smp_props.has_clusters) {
@@ -2184,7 +2197,8 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms,
core_id = -1;
cluster_offset = table_data->len - pptt_start;
build_processor_hierarchy_node(table_data,
- (0 << 0), /* Not a physical package */
+ (0 << 0) | /* Not a physical package */
+ (1 << 4), /* Identical Implementation */
socket_offset, cluster_id, NULL, 0);
}
} else {
@@ -2202,7 +2216,8 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms,
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 */
+ (0 << 0) | /* Not a physical package */
+ (1 << 4), /* Identical Implementation */
cluster_offset, core_id, NULL, 0);
}
diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c
index 1080618..c9ffe44 100644
--- a/hw/acpi/bios-linker-loader.c
+++ b/hw/acpi/bios-linker-loader.c
@@ -22,8 +22,6 @@
#include "hw/acpi/bios-linker-loader.h"
#include "hw/nvram/fw_cfg.h"
-#include "qemu/bswap.h"
-
/*
* Linker/loader is a paravirtualized interface that passes commands to guest.
* The commands can be used to request guest to
diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
index 7a62f8d..95682b7 100644
--- a/hw/acpi/generic_event_device.c
+++ b/hw/acpi/generic_event_device.c
@@ -12,10 +12,13 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "hw/acpi/acpi.h"
+#include "hw/acpi/pcihp.h"
#include "hw/acpi/generic_event_device.h"
+#include "hw/pci/pci.h"
#include "hw/irq.h"
#include "hw/mem/pc-dimm.h"
#include "hw/mem/nvdimm.h"
+#include "hw/pci/pci_device.h"
#include "hw/qdev-properties.h"
#include "migration/vmstate.h"
#include "qemu/error-report.h"
@@ -26,6 +29,7 @@ static const uint32_t ged_supported_events[] = {
ACPI_GED_PWR_DOWN_EVT,
ACPI_GED_NVDIMM_HOTPLUG_EVT,
ACPI_GED_CPU_HOTPLUG_EVT,
+ ACPI_GED_PCI_HOTPLUG_EVT,
};
/*
@@ -121,6 +125,12 @@ void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev,
aml_notify(aml_name("\\_SB.NVDR"),
aml_int(0x80)));
break;
+ case ACPI_GED_PCI_HOTPLUG_EVT:
+ aml_append(if_ctx,
+ aml_acquire(aml_name("\\_SB.PCI0.BLCK"), 0xFFFF));
+ aml_append(if_ctx, aml_call0("\\_SB.PCI0.PCNT"));
+ aml_append(if_ctx, aml_release(aml_name("\\_SB.PCI0.BLCK")));
+ break;
default:
/*
* Please make sure all the events in ged_supported_events[]
@@ -227,6 +237,14 @@ static const MemoryRegionOps ged_regs_ops = {
},
};
+static void acpi_ged_device_pre_plug_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
+ acpi_pcihp_device_pre_plug_cb(hotplug_dev, dev, errp);
+ }
+}
+
static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
@@ -240,6 +258,8 @@ static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
}
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
acpi_cpu_plug_cb(hotplug_dev, &s->cpuhp_state, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
+ acpi_pcihp_device_plug_cb(hotplug_dev, &s->pcihp_state, dev, errp);
} else {
error_setg(errp, "virt: device plug request for unsupported device"
" type: %s", object_get_typename(OBJECT(dev)));
@@ -256,6 +276,9 @@ static void acpi_ged_unplug_request_cb(HotplugHandler *hotplug_dev,
acpi_memory_unplug_request_cb(hotplug_dev, &s->memhp_state, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
acpi_cpu_unplug_request_cb(hotplug_dev, &s->cpuhp_state, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
+ acpi_pcihp_device_unplug_request_cb(hotplug_dev, &s->pcihp_state,
+ dev, errp);
} else {
error_setg(errp, "acpi: device unplug request for unsupported device"
" type: %s", object_get_typename(OBJECT(dev)));
@@ -271,6 +294,8 @@ static void acpi_ged_unplug_cb(HotplugHandler *hotplug_dev,
acpi_memory_unplug_cb(&s->memhp_state, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
acpi_cpu_unplug_cb(&s->cpuhp_state, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
+ acpi_pcihp_device_unplug_cb(hotplug_dev, &s->pcihp_state, dev, errp);
} else {
error_setg(errp, "acpi: device unplug for unsupported device"
" type: %s", object_get_typename(OBJECT(dev)));
@@ -299,6 +324,8 @@ static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
sel = ACPI_GED_NVDIMM_HOTPLUG_EVT;
} else if (ev & ACPI_CPU_HOTPLUG_STATUS) {
sel = ACPI_GED_CPU_HOTPLUG_EVT;
+ } else if (ev & ACPI_PCI_HOTPLUG_STATUS) {
+ sel = ACPI_GED_PCI_HOTPLUG_EVT;
} else {
/* Unknown event. Return without generating interrupt. */
warn_report("GED: Unsupported event %d. No irq injected", ev);
@@ -318,6 +345,10 @@ static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
static const Property acpi_ged_properties[] = {
DEFINE_PROP_UINT32("ged-event", AcpiGedState, ged_event_bitmap, 0),
+ DEFINE_PROP_BOOL(ACPI_PM_PROP_ACPI_PCIHP_BRIDGE, AcpiGedState,
+ pcihp_state.use_acpi_hotplug_bridge, 0),
+ DEFINE_PROP_LINK("bus", AcpiGedState, pcihp_state.root,
+ TYPE_PCI_BUS, PCIBus *),
};
static const VMStateDescription vmstate_memhp_state = {
@@ -386,6 +417,25 @@ static const VMStateDescription vmstate_ghes_state = {
}
};
+static bool pcihp_needed(void *opaque)
+{
+ AcpiGedState *s = opaque;
+ return s->pcihp_state.use_acpi_hotplug_bridge;
+}
+
+static const VMStateDescription vmstate_pcihp_state = {
+ .name = "acpi-ged/pcihp",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = pcihp_needed,
+ .fields = (const VMStateField[]) {
+ VMSTATE_PCI_HOTPLUG(pcihp_state,
+ AcpiGedState,
+ NULL, NULL),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_acpi_ged = {
.name = "acpi-ged",
.version_id = 1,
@@ -398,6 +448,7 @@ static const VMStateDescription vmstate_acpi_ged = {
&vmstate_memhp_state,
&vmstate_cpuhp_state,
&vmstate_ghes_state,
+ &vmstate_pcihp_state,
NULL
}
};
@@ -406,9 +457,13 @@ static void acpi_ged_realize(DeviceState *dev, Error **errp)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
AcpiGedState *s = ACPI_GED(dev);
+ AcpiPciHpState *pcihp_state = &s->pcihp_state;
uint32_t ged_events;
int i;
+ if (pcihp_state->use_acpi_hotplug_bridge) {
+ s->ged_event_bitmap |= ACPI_GED_PCI_HOTPLUG_EVT;
+ }
ged_events = ctpop32(s->ged_event_bitmap);
for (i = 0; i < ARRAY_SIZE(ged_supported_events) && ged_events; i++) {
@@ -428,6 +483,13 @@ static void acpi_ged_realize(DeviceState *dev, Error **errp)
cpu_hotplug_hw_init(&s->container_cpuhp, OBJECT(dev),
&s->cpuhp_state, 0);
break;
+ case ACPI_GED_PCI_HOTPLUG_EVT:
+ memory_region_init(&s->container_pcihp, OBJECT(dev),
+ ACPI_PCIHP_REGION_NAME, ACPI_PCIHP_SIZE);
+ sysbus_init_mmio(sbd, &s->container_pcihp);
+ acpi_pcihp_init(OBJECT(s), &s->pcihp_state,
+ &s->container_pcihp, 0);
+ qbus_set_hotplug_handler(BUS(s->pcihp_state.root), OBJECT(dev));
}
ged_events--;
}
@@ -469,20 +531,34 @@ static void acpi_ged_initfn(Object *obj)
sysbus_init_mmio(sbd, &ged_st->regs);
}
+static void ged_reset_hold(Object *obj, ResetType type)
+{
+ AcpiGedState *s = ACPI_GED(obj);
+
+ if (s->pcihp_state.use_acpi_hotplug_bridge) {
+ acpi_pcihp_reset(&s->pcihp_state);
+ }
+}
+
static void acpi_ged_class_init(ObjectClass *class, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(class);
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
+ ResettableClass *rc = RESETTABLE_CLASS(class);
+ AcpiGedClass *gedc = ACPI_GED_CLASS(class);
dc->desc = "ACPI Generic Event Device";
device_class_set_props(dc, acpi_ged_properties);
dc->vmsd = &vmstate_acpi_ged;
dc->realize = acpi_ged_realize;
+ hc->pre_plug = acpi_ged_device_pre_plug_cb;
hc->plug = acpi_ged_device_plug_cb;
hc->unplug_request = acpi_ged_unplug_request_cb;
hc->unplug = acpi_ged_unplug_cb;
+ resettable_class_set_parent_phases(rc, NULL, ged_reset_hold, NULL,
+ &gedc->parent_phases);
adevc->ospm_status = acpi_ged_ospm_status;
adevc->send_event = acpi_ged_send_event;
@@ -494,6 +570,7 @@ static const TypeInfo acpi_ged_info = {
.instance_size = sizeof(AcpiGedState),
.instance_init = acpi_ged_initfn,
.class_init = acpi_ged_class_init,
+ .class_size = sizeof(AcpiGedClass),
.interfaces = (const InterfaceInfo[]) {
{ TYPE_HOTPLUG_HANDLER },
{ TYPE_ACPI_DEVICE_IF },
diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
index 967b674..2b3b493 100644
--- a/hw/acpi/ich9.c
+++ b/hw/acpi/ich9.c
@@ -322,9 +322,10 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, qemu_irq sci_irq)
}
if (pm->acpi_pci_hotplug.use_acpi_hotplug_bridge) {
+ object_property_set_link(OBJECT(lpc_pci), "bus",
+ OBJECT(pci_get_bus(lpc_pci)), &error_abort);
acpi_pcihp_init(OBJECT(lpc_pci),
&pm->acpi_pci_hotplug,
- pci_get_bus(lpc_pci),
pci_address_space_io(lpc_pci),
ACPI_PCIHP_ADDR_ICH9);
@@ -428,6 +429,10 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm)
object_property_add_uint32_ptr(obj, ACPI_PM_PROP_PM_IO_BASE,
&pm->pm_io_base, OBJ_PROP_FLAG_READ);
+ object_property_add_link(obj, "bus", TYPE_PCI_BUS,
+ (Object **)&pm->acpi_pci_hotplug.root,
+ object_property_allow_set_link,
+ OBJ_PROP_LINK_STRONG);
object_property_add(obj, ACPI_PM_PROP_GPE0_BLK, "uint32",
ich9_pm_get_gpe0_blk,
NULL, NULL, pm);
diff --git a/hw/acpi/pci-bridge.c b/hw/acpi/pci-bridge.c
index 7baa703..394a919 100644
--- a/hw/acpi/pci-bridge.c
+++ b/hw/acpi/pci-bridge.c
@@ -35,3 +35,57 @@ void build_pci_bridge_aml(AcpiDevAmlIf *adev, Aml *scope)
}
}
}
+
+Aml *build_pci_bridge_edsm(void)
+{
+ Aml *method, *ifctx;
+ Aml *zero = aml_int(0);
+ Aml *func = aml_arg(2);
+ Aml *ret = aml_local(0);
+ Aml *aidx = aml_local(1);
+ Aml *params = aml_arg(4);
+
+ method = aml_method("EDSM", 5, AML_SERIALIZED);
+
+ /* get supported functions */
+ ifctx = aml_if(aml_equal(func, zero));
+ {
+ /* 1: have supported functions */
+ /* 7: support for function 7 */
+ const uint8_t caps = 1 | BIT(7);
+ build_append_pci_dsm_func0_common(ifctx, ret);
+ aml_append(ifctx, aml_store(aml_int(caps), aml_index(ret, zero)));
+ aml_append(ifctx, aml_return(ret));
+ }
+ aml_append(method, ifctx);
+
+ /* handle specific functions requests */
+ /*
+ * PCI Firmware Specification 3.1
+ * 4.6.7. _DSM for Naming a PCI or PCI Express Device Under
+ * Operating Systems
+ */
+ ifctx = aml_if(aml_equal(func, aml_int(7)));
+ {
+ Aml *pkg = aml_package(2);
+ aml_append(pkg, zero);
+ /* optional, if not impl. should return null string */
+ aml_append(pkg, aml_string("%s", ""));
+ aml_append(ifctx, aml_store(pkg, ret));
+
+ /*
+ * IASL is fine when initializing Package with computational data,
+ * however it makes guest unhappy /it fails to process such AML/.
+ * So use runtime assignment to set acpi-index after initializer
+ * to make OSPM happy.
+ */
+ aml_append(ifctx,
+ aml_store(aml_derefof(aml_index(params, aml_int(0))), aidx));
+ aml_append(ifctx, aml_store(aidx, aml_index(ret, zero)));
+ aml_append(ifctx, aml_return(ret));
+ }
+ aml_append(method, ifctx);
+
+ return method;
+}
+
diff --git a/hw/acpi/pci.c b/hw/acpi/pci.c
index d511a85..2228f12 100644
--- a/hw/acpi/pci.c
+++ b/hw/acpi/pci.c
@@ -301,3 +301,53 @@ void build_srat_generic_affinity_structures(GArray *table_data)
object_child_foreach_recursive(object_get_root(), build_acpi_generic_port,
table_data);
}
+
+Aml *build_pci_host_bridge_osc_method(bool enable_native_pcie_hotplug)
+{
+ Aml *if_ctx;
+ Aml *if_ctx2;
+ Aml *else_ctx;
+ Aml *method;
+ Aml *a_cwd1 = aml_name("CDW1");
+ Aml *a_ctrl = aml_local(0);
+
+ method = aml_method("_OSC", 4, AML_NOTSERIALIZED);
+ aml_append(method, aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1"));
+
+ if_ctx = aml_if(aml_equal(
+ aml_arg(0), aml_touuid("33DB4D5B-1FF7-401C-9657-7441C03DD766")));
+ aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(4), "CDW2"));
+ aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3"));
+
+ aml_append(if_ctx, aml_store(aml_name("CDW3"), a_ctrl));
+
+ /*
+ * Always allow native PME, AER (no dependencies)
+ * Allow SHPC (PCI bridges can have SHPC controller)
+ * Disable PCIe Native Hot-plug if ACPI PCI Hot-plug is enabled.
+ */
+ aml_append(if_ctx, aml_and(a_ctrl,
+ aml_int(0x1E | (enable_native_pcie_hotplug ? 0x1 : 0x0)), a_ctrl));
+
+ if_ctx2 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(1))));
+ /* Unknown revision */
+ aml_append(if_ctx2, aml_or(a_cwd1, aml_int(0x08), a_cwd1));
+ aml_append(if_ctx, if_ctx2);
+
+ if_ctx2 = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), a_ctrl)));
+ /* Capabilities bits were masked */
+ aml_append(if_ctx2, aml_or(a_cwd1, aml_int(0x10), a_cwd1));
+ aml_append(if_ctx, if_ctx2);
+
+ /* Update DWORD3 in the buffer */
+ aml_append(if_ctx, aml_store(a_ctrl, aml_name("CDW3")));
+ aml_append(method, if_ctx);
+
+ else_ctx = aml_else();
+ /* Unrecognized UUID */
+ aml_append(else_ctx, aml_or(a_cwd1, aml_int(4), a_cwd1));
+ aml_append(method, else_ctx);
+
+ aml_append(method, aml_return(aml_arg(3)));
+ return method;
+}
diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c
index 497281a..4922bbc 100644
--- a/hw/acpi/pcihp.c
+++ b/hw/acpi/pcihp.c
@@ -26,7 +26,8 @@
#include "qemu/osdep.h"
#include "hw/acpi/pcihp.h"
-
+#include "hw/acpi/aml-build.h"
+#include "hw/acpi/acpi_aml_interface.h"
#include "hw/pci-host/i440fx.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_bridge.h"
@@ -39,9 +40,9 @@
#include "migration/vmstate.h"
#include "qapi/error.h"
#include "qom/qom-qobject.h"
+#include "qobject/qnum.h"
#include "trace.h"
-#define ACPI_PCIHP_SIZE 0x0018
#define PCI_UP_BASE 0x0000
#define PCI_DOWN_BASE 0x0004
#define PCI_EJ_BASE 0x0008
@@ -97,10 +98,10 @@ static void *acpi_set_bsel(PCIBus *bus, void *opaque)
return info;
}
-static void acpi_set_pci_info(bool has_bridge_hotplug)
+static void acpi_set_pci_info(AcpiPciHpState *s)
{
static bool bsel_is_set;
- Object *host = acpi_get_i386_pci_host();
+ bool has_bridge_hotplug = s->use_acpi_hotplug_bridge;
PCIBus *bus;
BSELInfo info = { .bsel_alloc = ACPI_PCIHP_BSEL_DEFAULT,
.has_bridge_hotplug = has_bridge_hotplug };
@@ -110,11 +111,8 @@ static void acpi_set_pci_info(bool has_bridge_hotplug)
}
bsel_is_set = true;
- if (!host) {
- return;
- }
- bus = PCI_HOST_BRIDGE(host)->bus;
+ bus = s->root;
if (bus) {
/* Scan all PCI buses. Set property to enable acpi based hotplug. */
pci_for_each_bus_depth_first(bus, acpi_set_bsel, NULL, &info);
@@ -264,7 +262,7 @@ static void acpi_pcihp_update(AcpiPciHpState *s)
void acpi_pcihp_reset(AcpiPciHpState *s)
{
- acpi_set_pci_info(s->use_acpi_hotplug_bridge);
+ acpi_set_pci_info(s);
acpi_pcihp_update(s);
}
@@ -495,13 +493,13 @@ static const MemoryRegionOps acpi_pcihp_io_ops = {
},
};
-void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus,
+void acpi_pcihp_init(Object *owner, AcpiPciHpState *s,
MemoryRegion *io, uint16_t io_base)
{
s->io_len = ACPI_PCIHP_SIZE;
s->io_base = io_base;
- s->root = root_bus;
+ assert(s->root);
memory_region_init_io(&s->io, owner, &acpi_pcihp_io_ops, s,
"acpi-pci-hotplug", s->io_len);
@@ -513,6 +511,425 @@ void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus,
OBJ_PROP_FLAG_READ);
}
+void build_append_pci_dsm_func0_common(Aml *ctx, Aml *retvar)
+{
+ Aml *UUID, *ifctx1;
+ uint8_t byte_list[1] = { 0 }; /* nothing supported yet */
+
+ aml_append(ctx, aml_store(aml_buffer(1, byte_list), retvar));
+ /*
+ * PCI Firmware Specification 3.1
+ * 4.6. _DSM Definitions for PCI
+ */
+ UUID = aml_touuid("E5C937D0-3553-4D7A-9117-EA4D19C3434D");
+ ifctx1 = aml_if(aml_lnot(aml_equal(aml_arg(0), UUID)));
+ {
+ /* call is for unsupported UUID, bail out */
+ aml_append(ifctx1, aml_return(retvar));
+ }
+ aml_append(ctx, ifctx1);
+
+ ifctx1 = aml_if(aml_lless(aml_arg(1), aml_int(2)));
+ {
+ /* call is for unsupported REV, bail out */
+ aml_append(ifctx1, aml_return(retvar));
+ }
+ aml_append(ctx, ifctx1);
+}
+
+static Aml *aml_pci_pdsm(void)
+{
+ Aml *method, *ifctx, *ifctx1;
+ Aml *ret = aml_local(0);
+ Aml *caps = aml_local(1);
+ Aml *acpi_index = aml_local(2);
+ Aml *zero = aml_int(0);
+ Aml *one = aml_int(1);
+ Aml *not_supp = aml_int(0xFFFFFFFF);
+ Aml *func = aml_arg(2);
+ Aml *params = aml_arg(4);
+ Aml *bnum = aml_derefof(aml_index(params, aml_int(0)));
+ Aml *sunum = aml_derefof(aml_index(params, aml_int(1)));
+
+ method = aml_method("PDSM", 5, AML_SERIALIZED);
+
+ /* get supported functions */
+ ifctx = aml_if(aml_equal(func, zero));
+ {
+ build_append_pci_dsm_func0_common(ifctx, ret);
+
+ aml_append(ifctx, aml_store(zero, caps));
+ aml_append(ifctx,
+ aml_store(aml_call2("AIDX", bnum, sunum), acpi_index));
+ /*
+ * advertise function 7 if device has acpi-index
+ * acpi_index values:
+ * 0: not present (default value)
+ * FFFFFFFF: not supported (old QEMU without PIDX reg)
+ * other: device's acpi-index
+ */
+ ifctx1 = aml_if(aml_lnot(
+ aml_or(aml_equal(acpi_index, zero),
+ aml_equal(acpi_index, not_supp), NULL)
+ ));
+ {
+ /* have supported functions */
+ aml_append(ifctx1, aml_or(caps, one, caps));
+ /* support for function 7 */
+ aml_append(ifctx1,
+ aml_or(caps, aml_shiftleft(one, aml_int(7)), caps));
+ }
+ aml_append(ifctx, ifctx1);
+
+ aml_append(ifctx, aml_store(caps, aml_index(ret, zero)));
+ aml_append(ifctx, aml_return(ret));
+ }
+ aml_append(method, ifctx);
+
+ /* handle specific functions requests */
+ /*
+ * PCI Firmware Specification 3.1
+ * 4.6.7. _DSM for Naming a PCI or PCI Express Device Under
+ * Operating Systems
+ */
+ ifctx = aml_if(aml_equal(func, aml_int(7)));
+ {
+ Aml *pkg = aml_package(2);
+
+ aml_append(ifctx, aml_store(aml_call2("AIDX", bnum, sunum), acpi_index));
+ aml_append(ifctx, aml_store(pkg, ret));
+ /*
+ * Windows calls func=7 without checking if it's available,
+ * as workaround Microsoft has suggested to return invalid for func7
+ * Package, so return 2 elements package but only initialize elements
+ * when acpi_index is supported and leave them uninitialized, which
+ * leads elements to being Uninitialized ObjectType and should trip
+ * Windows into discarding result as an unexpected and prevent setting
+ * bogus 'PCI Label' on the device.
+ */
+ ifctx1 = aml_if(aml_lnot(aml_lor(
+ aml_equal(acpi_index, zero), aml_equal(acpi_index, not_supp)
+ )));
+ {
+ aml_append(ifctx1, aml_store(acpi_index, aml_index(ret, zero)));
+ /*
+ * optional, if not impl. should return null string
+ */
+ aml_append(ifctx1, aml_store(aml_string("%s", ""),
+ aml_index(ret, one)));
+ }
+ aml_append(ifctx, ifctx1);
+
+ aml_append(ifctx, aml_return(ret));
+ }
+
+ aml_append(method, ifctx);
+ return method;
+}
+
+void build_acpi_pci_hotplug(Aml *table, AmlRegionSpace rs, uint64_t pcihp_addr)
+{
+ Aml *scope;
+ Aml *field;
+ Aml *method;
+
+ scope = aml_scope("_SB.PCI0");
+
+ aml_append(scope,
+ aml_operation_region("PCST", rs, aml_int(pcihp_addr), 0x08));
+ field = aml_field("PCST", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS);
+ aml_append(field, aml_named_field("PCIU", 32));
+ aml_append(field, aml_named_field("PCID", 32));
+ aml_append(scope, field);
+
+ aml_append(scope,
+ aml_operation_region("SEJ", rs,
+ aml_int(pcihp_addr + ACPI_PCIHP_SEJ_BASE), 0x04));
+ field = aml_field("SEJ", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS);
+ aml_append(field, aml_named_field("B0EJ", 32));
+ aml_append(scope, field);
+
+ aml_append(scope,
+ aml_operation_region("BNMR", rs,
+ aml_int(pcihp_addr + ACPI_PCIHP_BNMR_BASE), 0x08));
+ field = aml_field("BNMR", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS);
+ aml_append(field, aml_named_field("BNUM", 32));
+ aml_append(field, aml_named_field("PIDX", 32));
+ aml_append(scope, field);
+
+ aml_append(scope, aml_mutex("BLCK", 0));
+
+ method = aml_method("PCEJ", 2, AML_NOTSERIALIZED);
+ aml_append(method, aml_acquire(aml_name("BLCK"), 0xFFFF));
+ aml_append(method, aml_store(aml_arg(0), aml_name("BNUM")));
+ aml_append(method,
+ aml_store(aml_shiftleft(aml_int(1), aml_arg(1)), aml_name("B0EJ")));
+ aml_append(method, aml_release(aml_name("BLCK")));
+ aml_append(method, aml_return(aml_int(0)));
+ aml_append(scope, method);
+
+ method = aml_method("AIDX", 2, AML_NOTSERIALIZED);
+ aml_append(method, aml_acquire(aml_name("BLCK"), 0xFFFF));
+ aml_append(method, aml_store(aml_arg(0), aml_name("BNUM")));
+ aml_append(method,
+ aml_store(aml_shiftleft(aml_int(1), aml_arg(1)), aml_name("PIDX")));
+ aml_append(method, aml_store(aml_name("PIDX"), aml_local(0)));
+ aml_append(method, aml_release(aml_name("BLCK")));
+ aml_append(method, aml_return(aml_local(0)));
+ aml_append(scope, method);
+
+ aml_append(scope, aml_pci_pdsm());
+
+ aml_append(table, scope);
+}
+
+/* Reserve PCIHP resources */
+void build_append_pcihp_resources(Aml *scope /* \\_SB.PCI0 */,
+ uint64_t io_addr, uint64_t io_len)
+{
+ Aml *dev, *crs;
+
+ dev = aml_device("PHPR");
+ aml_append(dev, aml_name_decl("_HID", aml_string("PNP0A06")));
+ aml_append(dev,
+ aml_name_decl("_UID", aml_string("PCI Hotplug resources")));
+ /* device present, functioning, decoding, not shown in UI */
+ aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
+ crs = aml_resource_template();
+ aml_append(crs, aml_io(AML_DECODE16, io_addr, io_addr, 1, io_len));
+ aml_append(dev, aml_name_decl("_CRS", crs));
+ aml_append(scope, dev);
+}
+
+bool build_append_notification_callback(Aml *parent_scope, const PCIBus *bus)
+{
+ Aml *method;
+ PCIBus *sec;
+ QObject *bsel;
+ int nr_notifiers = 0;
+ GQueue *pcnt_bus_list = g_queue_new();
+
+ QLIST_FOREACH(sec, &bus->child, sibling) {
+ Aml *br_scope = aml_scope("S%.02X", sec->parent_dev->devfn);
+ if (pci_bus_is_root(sec)) {
+ continue;
+ }
+ nr_notifiers = nr_notifiers +
+ build_append_notification_callback(br_scope, sec);
+ /*
+ * add new child scope to parent
+ * and keep track of bus that have PCNT,
+ * bus list is used later to call children PCNTs from this level PCNT
+ */
+ if (nr_notifiers) {
+ g_queue_push_tail(pcnt_bus_list, sec);
+ aml_append(parent_scope, br_scope);
+ }
+ }
+
+ /*
+ * Append PCNT method to notify about events on local and child buses.
+ * ps: hostbridge might not have hotplug (bsel) enabled but might have
+ * child bridges that do have bsel.
+ */
+ method = aml_method("PCNT", 0, AML_NOTSERIALIZED);
+
+ /* If bus supports hotplug select it and notify about local events */
+ bsel = object_property_get_qobject(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, NULL);
+ if (bsel) {
+ uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel));
+
+ aml_append(method, aml_store(aml_int(bsel_val), aml_name("BNUM")));
+ aml_append(method, aml_call2("DVNT", aml_name("PCIU"),
+ aml_int(1))); /* Device Check */
+ aml_append(method, aml_call2("DVNT", aml_name("PCID"),
+ aml_int(3))); /* Eject Request */
+ nr_notifiers++;
+ }
+
+ /* Notify about child bus events in any case */
+ while ((sec = g_queue_pop_head(pcnt_bus_list))) {
+ aml_append(method, aml_name("^S%.02X.PCNT", sec->parent_dev->devfn));
+ }
+
+ aml_append(parent_scope, method);
+ qobject_unref(bsel);
+ g_queue_free(pcnt_bus_list);
+ return !!nr_notifiers;
+}
+
+static Aml *aml_pci_device_dsm(void)
+{
+ Aml *method;
+
+ method = aml_method("_DSM", 4, AML_SERIALIZED);
+ {
+ Aml *params = aml_local(0);
+ Aml *pkg = aml_package(2);
+ aml_append(pkg, aml_int(0));
+ aml_append(pkg, aml_int(0));
+ aml_append(method, aml_store(pkg, params));
+ aml_append(method,
+ aml_store(aml_name("BSEL"), aml_index(params, aml_int(0))));
+ aml_append(method,
+ aml_store(aml_name("ASUN"), aml_index(params, aml_int(1))));
+ aml_append(method,
+ aml_return(aml_call5("PDSM", aml_arg(0), aml_arg(1),
+ aml_arg(2), aml_arg(3), params))
+ );
+ }
+ return method;
+}
+
+static Aml *aml_pci_static_endpoint_dsm(PCIDevice *pdev)
+{
+ Aml *method;
+
+ g_assert(pdev->acpi_index != 0);
+ method = aml_method("_DSM", 4, AML_SERIALIZED);
+ {
+ Aml *params = aml_local(0);
+ Aml *pkg = aml_package(1);
+ aml_append(pkg, aml_int(pdev->acpi_index));
+ aml_append(method, aml_store(pkg, params));
+ aml_append(method,
+ aml_return(aml_call5("EDSM", aml_arg(0), aml_arg(1),
+ aml_arg(2), aml_arg(3), params))
+ );
+ }
+ return method;
+}
+
+static void build_append_pcihp_notify_entry(Aml *method, int slot)
+{
+ Aml *if_ctx;
+ int32_t devfn = PCI_DEVFN(slot, 0);
+
+ if_ctx = aml_if(aml_and(aml_arg(0), aml_int(0x1U << slot), NULL));
+ aml_append(if_ctx, aml_notify(aml_name("S%.02X", devfn), aml_arg(1)));
+ aml_append(method, if_ctx);
+}
+
+static bool is_devfn_ignored_generic(const int devfn, const PCIBus *bus)
+{
+ const PCIDevice *pdev = bus->devices[devfn];
+
+ if (PCI_FUNC(devfn)) {
+ if (IS_PCI_BRIDGE(pdev)) {
+ /*
+ * Ignore only hotplugged PCI bridges on !0 functions, but
+ * allow describing cold plugged bridges on all functions
+ */
+ if (DEVICE(pdev)->hotplugged) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static bool is_devfn_ignored_hotplug(const int devfn, const PCIBus *bus)
+{
+ PCIDevice *pdev = bus->devices[devfn];
+ if (pdev) {
+ return is_devfn_ignored_generic(devfn, bus) ||
+ !DEVICE_GET_CLASS(pdev)->hotpluggable ||
+ /* Cold plugged bridges aren't themselves hot-pluggable */
+ (IS_PCI_BRIDGE(pdev) && !DEVICE(pdev)->hotplugged);
+ } else { /* non populated slots */
+ /*
+ * hotplug is supported only for non-multifunction device
+ * so generate device description only for function 0
+ */
+ if (PCI_FUNC(devfn) ||
+ (pci_bus_is_express(bus) && PCI_SLOT(devfn) > 0)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void build_append_pcihp_slots(Aml *parent_scope, PCIBus *bus)
+{
+ int devfn;
+ Aml *dev, *notify_method = NULL, *method;
+ QObject *bsel = object_property_get_qobject(OBJECT(bus),
+ ACPI_PCIHP_PROP_BSEL, NULL);
+ uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel));
+ qobject_unref(bsel);
+
+ aml_append(parent_scope, aml_name_decl("BSEL", aml_int(bsel_val)));
+ notify_method = aml_method("DVNT", 2, AML_NOTSERIALIZED);
+
+ for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) {
+ int slot = PCI_SLOT(devfn);
+ int adr = slot << 16 | PCI_FUNC(devfn);
+
+ if (is_devfn_ignored_hotplug(devfn, bus)) {
+ continue;
+ }
+
+ if (bus->devices[devfn]) {
+ dev = aml_scope("S%.02X", devfn);
+ } else {
+ dev = aml_device("S%.02X", devfn);
+ aml_append(dev, aml_name_decl("_ADR", aml_int(adr)));
+ }
+
+ /*
+ * Can't declare _SUN here for every device as it changes 'slot'
+ * enumeration order in linux kernel, so use another variable for it
+ */
+ aml_append(dev, aml_name_decl("ASUN", aml_int(slot)));
+ aml_append(dev, aml_pci_device_dsm());
+
+ aml_append(dev, aml_name_decl("_SUN", aml_int(slot)));
+ /* add _EJ0 to make slot hotpluggable */
+ method = aml_method("_EJ0", 1, AML_NOTSERIALIZED);
+ aml_append(method,
+ aml_call2("PCEJ", aml_name("BSEL"), aml_name("_SUN"))
+ );
+ aml_append(dev, method);
+
+ build_append_pcihp_notify_entry(notify_method, slot);
+
+ /* device descriptor has been composed, add it into parent context */
+ aml_append(parent_scope, dev);
+ }
+ aml_append(parent_scope, notify_method);
+}
+
+void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus)
+{
+ int devfn;
+ Aml *dev;
+
+ for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) {
+ /* ACPI spec: 1.0b: Table 6-2 _ADR Object Bus Types, PCI type */
+ int adr = PCI_SLOT(devfn) << 16 | PCI_FUNC(devfn);
+ PCIDevice *pdev = bus->devices[devfn];
+
+ if (!pdev || is_devfn_ignored_generic(devfn, bus)) {
+ continue;
+ }
+
+ /* start to compose PCI device descriptor */
+ dev = aml_device("S%.02X", devfn);
+ aml_append(dev, aml_name_decl("_ADR", aml_int(adr)));
+
+ call_dev_aml_func(DEVICE(bus->devices[devfn]), dev);
+ /* add _DSM if device has acpi-index set */
+ if (pdev->acpi_index &&
+ !object_property_get_bool(OBJECT(pdev), "hotpluggable",
+ &error_abort)) {
+ aml_append(dev, aml_pci_static_endpoint_dsm(pdev));
+ }
+
+ /* device descriptor has been composed, add it into parent context */
+ aml_append(parent_scope, dev);
+ }
+}
+
const VMStateDescription vmstate_acpi_pcihp_pci_status = {
.name = "acpi_pcihp_pci_status",
.version_id = 1,
diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
index d98b80d..7a18f18 100644
--- a/hw/acpi/piix4.c
+++ b/hw/acpi/piix4.c
@@ -567,7 +567,8 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent,
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,
+ object_property_set_link(OBJECT(s), "bus", OBJECT(bus), &error_abort);
+ acpi_pcihp_init(OBJECT(s), &s->acpi_pci_hotplug, parent,
ACPI_PCIHP_ADDR_PIIX4);
qbus_set_hotplug_handler(BUS(pci_get_bus(PCI_DEVICE(s))), OBJECT(s));
}
@@ -611,6 +612,8 @@ static const Property piix4_pm_properties[] = {
acpi_pci_hotplug.use_acpi_hotplug_bridge, true),
DEFINE_PROP_BOOL(ACPI_PM_PROP_ACPI_PCI_ROOTHP, PIIX4PMState,
acpi_pci_hotplug.use_acpi_root_pci_hotplug, true),
+ DEFINE_PROP_LINK("bus", PIIX4PMState, acpi_pci_hotplug.root,
+ TYPE_PCI_BUS, PCIBus *),
DEFINE_PROP_BOOL("memory-hotplug-support", PIIX4PMState,
acpi_memory_hotplug.is_enabled, true),
DEFINE_PROP_BOOL("smm-compat", PIIX4PMState, smm_compat, false),