aboutsummaryrefslogtreecommitdiff
path: root/hw/i386
diff options
context:
space:
mode:
Diffstat (limited to 'hw/i386')
-rw-r--r--hw/i386/acpi-build.c114
-rw-r--r--hw/i386/acpi-build.h5
-rw-r--r--hw/i386/pc.c21
-rw-r--r--hw/i386/pc_q35.c11
4 files changed, 133 insertions, 18 deletions
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 357437ff1d..17836149fe 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -219,10 +219,6 @@ static void acpi_get_pm_info(MachineState *machine, AcpiPmInfo *pm)
/* w2k requires FADT(rev1) or it won't boot, keep PC compatible */
pm->fadt.rev = 1;
pm->cpu_hp_io_base = PIIX4_CPU_HOTPLUG_IO_BASE;
- pm->pcihp_io_base =
- object_property_get_uint(obj, ACPI_PCIHP_IO_BASE_PROP, NULL);
- pm->pcihp_io_len =
- object_property_get_uint(obj, ACPI_PCIHP_IO_LEN_PROP, NULL);
}
if (lpc) {
uint64_t smi_features = object_property_get_uint(lpc,
@@ -238,6 +234,10 @@ static void acpi_get_pm_info(MachineState *machine, AcpiPmInfo *pm)
pm->smi_on_cpu_unplug =
!!(smi_features & BIT_ULL(ICH9_LPC_SMI_F_CPU_HOT_UNPLUG_BIT));
}
+ pm->pcihp_io_base =
+ object_property_get_uint(obj, ACPI_PCIHP_IO_BASE_PROP, NULL);
+ pm->pcihp_io_len =
+ object_property_get_uint(obj, ACPI_PCIHP_IO_LEN_PROP, NULL);
/* The above need not be conditional on machine type because the reset port
* happens to be the same on PIIX (pc) and ICH9 (q35). */
@@ -299,7 +299,7 @@ static void acpi_get_misc_info(AcpiMiscInfo *info)
* Because of the PXB hosts we cannot simply query TYPE_PCI_HOST_BRIDGE.
* On i386 arch we only have two pci hosts, so we can look only for them.
*/
-static Object *acpi_get_i386_pci_host(void)
+Object *acpi_get_i386_pci_host(void)
{
PCIHostState *host;
@@ -320,7 +320,10 @@ static void acpi_get_pci_holes(Range *hole, Range *hole64)
Object *pci_host;
pci_host = acpi_get_i386_pci_host();
- g_assert(pci_host);
+
+ if (!pci_host) {
+ return;
+ }
range_set_bounds1(hole,
object_property_get_uint(pci_host,
@@ -392,6 +395,9 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
if (!pdev) {
if (bsel) { /* add hotplug slots for non present devices */
+ if (pci_bus_is_express(bus) && slot > 0) {
+ break;
+ }
dev = aml_device("S%.02X", PCI_DEVFN(slot, 0));
aml_append(dev, aml_name_decl("_SUN", aml_int(slot)));
aml_append(dev, aml_name_decl("_ADR", aml_int(slot << 16)));
@@ -521,7 +527,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
QLIST_FOREACH(sec, &bus->child, sibling) {
int32_t devfn = sec->parent_dev->devfn;
- if (pci_bus_is_root(sec) || pci_bus_is_express(sec)) {
+ if (pci_bus_is_root(sec)) {
continue;
}
@@ -1251,7 +1257,7 @@ static void build_piix4_isa_bridge(Aml *table)
aml_append(table, scope);
}
-static void build_piix4_pci_hotplug(Aml *table)
+static void build_x86_acpi_pci_hotplug(Aml *table, uint64_t pcihp_addr)
{
Aml *scope;
Aml *field;
@@ -1260,20 +1266,22 @@ static void build_piix4_pci_hotplug(Aml *table)
scope = aml_scope("_SB.PCI0");
aml_append(scope,
- aml_operation_region("PCST", AML_SYSTEM_IO, aml_int(0xae00), 0x08));
+ aml_operation_region("PCST", AML_SYSTEM_IO, 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", AML_SYSTEM_IO, aml_int(0xae08), 0x04));
+ aml_operation_region("SEJ", AML_SYSTEM_IO,
+ 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", AML_SYSTEM_IO, aml_int(0xae10), 0x08));
+ aml_operation_region("BNMR", AML_SYSTEM_IO,
+ 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));
@@ -1407,7 +1415,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
build_piix4_isa_bridge(dsdt);
build_isa_devices_aml(dsdt);
if (pm->pcihp_bridge_en || pm->pcihp_root_en) {
- build_piix4_pci_hotplug(dsdt);
+ build_x86_acpi_pci_hotplug(dsdt, pm->pcihp_io_base);
}
build_piix4_pci0_int(dsdt);
} else {
@@ -1455,6 +1463,9 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
}
build_q35_isa_bridge(dsdt);
build_isa_devices_aml(dsdt);
+ if (pm->pcihp_bridge_en) {
+ build_x86_acpi_pci_hotplug(dsdt, pm->pcihp_io_base);
+ }
build_q35_pci0_int(dsdt);
if (pcms->smbus && !pcmc->do_not_add_smb_acpi) {
build_smb0(dsdt, pcms->smbus, ICH9_SMB_DEV, ICH9_SMB_FUNC);
@@ -1489,7 +1500,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
{
aml_append(scope, aml_name_decl("_HID", aml_string("ACPI0006")));
- if (misc->is_piix4 && (pm->pcihp_bridge_en || pm->pcihp_root_en)) {
+ if (pm->pcihp_bridge_en || pm->pcihp_root_en) {
method = aml_method("_E01", 0, AML_NOTSERIALIZED);
aml_append(method,
aml_acquire(aml_name("\\_SB.PCI0.BLCK"), 0xFFFF));
@@ -1757,6 +1768,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
PCIBus *bus = NULL;
pci_host = acpi_get_i386_pci_host();
+
if (pci_host) {
bus = PCI_HOST_BRIDGE(pci_host)->bus;
}
@@ -2011,6 +2023,56 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
}
/*
+ * Insert DMAR scope for PCI bridges and endpoint devcie
+ */
+static void
+insert_scope(PCIBus *bus, PCIDevice *dev, void *opaque)
+{
+ GArray *scope_blob = opaque;
+ AcpiDmarDeviceScope *scope = NULL;
+
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) {
+ /* Dmar Scope Type: 0x02 for PCI Bridge */
+ build_append_int_noprefix(scope_blob, 0x02, 1);
+ } else {
+ /* Dmar Scope Type: 0x01 for PCI Endpoint Device */
+ build_append_int_noprefix(scope_blob, 0x01, 1);
+ }
+
+ /* length */
+ build_append_int_noprefix(scope_blob,
+ sizeof(*scope) + sizeof(scope->path[0]), 1);
+ /* reserved */
+ build_append_int_noprefix(scope_blob, 0, 2);
+ /* enumeration_id */
+ build_append_int_noprefix(scope_blob, 0, 1);
+ /* bus */
+ build_append_int_noprefix(scope_blob, pci_bus_num(bus), 1);
+ /* device */
+ build_append_int_noprefix(scope_blob, PCI_SLOT(dev->devfn), 1);
+ /* function */
+ build_append_int_noprefix(scope_blob, PCI_FUNC(dev->devfn), 1);
+}
+
+/* For a given PCI host bridge, walk and insert DMAR scope */
+static int
+dmar_host_bridges(Object *obj, void *opaque)
+{
+ GArray *scope_blob = opaque;
+
+ if (object_dynamic_cast(obj, TYPE_PCI_HOST_BRIDGE)) {
+ PCIBus *bus = PCI_HOST_BRIDGE(obj)->bus;
+
+ if (bus && !pci_bus_bypass_iommu(bus)) {
+ pci_for_each_device(bus, pci_bus_num(bus), insert_scope,
+ scope_blob);
+ }
+ }
+
+ return 0;
+}
+
+/*
* VT-d spec 8.1 DMA Remapping Reporting Structure
* (version Oct. 2014 or later)
*/
@@ -2029,6 +2091,15 @@ build_dmar_q35(GArray *table_data, BIOSLinker *linker, const char *oem_id,
/* Root complex IOAPIC use one path[0] only */
size_t ioapic_scope_size = sizeof(*scope) + sizeof(scope->path[0]);
IntelIOMMUState *intel_iommu = INTEL_IOMMU_DEVICE(iommu);
+ GArray *scope_blob = g_array_new(false, true, 1);
+
+ /*
+ * A PCI bus walk, for each PCI host bridge.
+ * Insert scope for each PCI bridge and endpoint device which
+ * is attached to a bus with iommu enabled.
+ */
+ object_child_foreach_recursive(object_get_root(),
+ dmar_host_bridges, scope_blob);
assert(iommu);
if (x86_iommu_ir_supported(iommu)) {
@@ -2042,8 +2113,9 @@ build_dmar_q35(GArray *table_data, BIOSLinker *linker, const char *oem_id,
/* DMAR Remapping Hardware Unit Definition structure */
drhd = acpi_data_push(table_data, sizeof(*drhd) + ioapic_scope_size);
drhd->type = cpu_to_le16(ACPI_DMAR_TYPE_HARDWARE_UNIT);
- drhd->length = cpu_to_le16(sizeof(*drhd) + ioapic_scope_size);
- drhd->flags = ACPI_DMAR_INCLUDE_PCI_ALL;
+ drhd->length =
+ cpu_to_le16(sizeof(*drhd) + ioapic_scope_size + scope_blob->len);
+ drhd->flags = 0; /* Don't include all pci device */
drhd->pci_segment = cpu_to_le16(0);
drhd->address = cpu_to_le64(Q35_HOST_BRIDGE_IOMMU_ADDR);
@@ -2057,6 +2129,10 @@ build_dmar_q35(GArray *table_data, BIOSLinker *linker, const char *oem_id,
scope->path[0].device = PCI_SLOT(Q35_PSEUDO_DEVFN_IOAPIC);
scope->path[0].function = PCI_FUNC(Q35_PSEUDO_DEVFN_IOAPIC);
+ /* Add scope found above */
+ g_array_append_vals(table_data, scope_blob->data, scope_blob->len);
+ g_array_free(scope_blob, true);
+
if (iommu->dt_supported) {
atsr = acpi_data_push(table_data, sizeof(*atsr));
atsr->type = cpu_to_le16(ACPI_DMAR_TYPE_ATSR);
@@ -2187,7 +2263,7 @@ ivrs_host_bridges(Object *obj, void *opaque)
if (object_dynamic_cast(obj, TYPE_PCI_HOST_BRIDGE)) {
PCIBus *bus = PCI_HOST_BRIDGE(obj)->bus;
- if (bus) {
+ if (bus && !pci_bus_bypass_iommu(bus)) {
pci_for_each_device(bus, pci_bus_num(bus), insert_ivhd, ivhd_blob);
}
}
@@ -2313,7 +2389,9 @@ static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg)
QObject *o;
pci_host = acpi_get_i386_pci_host();
- g_assert(pci_host);
+ if (!pci_host) {
+ return false;
+ }
o = object_property_get_qobject(pci_host, PCIE_HOST_MCFG_BASE, NULL);
if (!o) {
@@ -2343,7 +2421,7 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine)
AcpiPmInfo pm;
AcpiMiscInfo misc;
AcpiMcfgInfo mcfg;
- Range pci_hole, pci_hole64;
+ Range pci_hole = {}, pci_hole64 = {};
uint8_t *u;
size_t aml_len = 0;
GArray *tables_blob = tables->table_data;
diff --git a/hw/i386/acpi-build.h b/hw/i386/acpi-build.h
index 74df5fc612..0dce155c8c 100644
--- a/hw/i386/acpi-build.h
+++ b/hw/i386/acpi-build.h
@@ -5,6 +5,11 @@
extern const struct AcpiGenericAddress x86_nvdimm_acpi_dsmio;
+/* PCI Hot-plug registers bases. See docs/spec/acpi_pci_hotplug.txt */
+#define ACPI_PCIHP_SEJ_BASE 0x8
+#define ACPI_PCIHP_BNMR_BASE 0x10
+
void acpi_setup(void);
+Object *acpi_get_i386_pci_host(void);
#endif
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index aa79c5e0e6..c2b9d62a35 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -99,6 +99,7 @@ GlobalProperty pc_compat_6_0[] = {
{ "qemu64" "-" TYPE_X86_CPU, "model", "6" },
{ "qemu64" "-" TYPE_X86_CPU, "stepping", "3" },
{ TYPE_X86_CPU, "x-vendor-cpuid-only", "off" },
+ { "ICH9-LPC", "acpi-pci-hotplug-with-bridge-support", "off" },
};
const size_t pc_compat_6_0_len = G_N_ELEMENTS(pc_compat_6_0);
@@ -1523,6 +1524,21 @@ static void pc_machine_set_hpet(Object *obj, bool value, Error **errp)
pcms->hpet_enabled = value;
}
+static bool pc_machine_get_default_bus_bypass_iommu(Object *obj, Error **errp)
+{
+ PCMachineState *pcms = PC_MACHINE(obj);
+
+ return pcms->default_bus_bypass_iommu;
+}
+
+static void pc_machine_set_default_bus_bypass_iommu(Object *obj, bool value,
+ Error **errp)
+{
+ PCMachineState *pcms = PC_MACHINE(obj);
+
+ pcms->default_bus_bypass_iommu = value;
+}
+
static void pc_machine_get_max_ram_below_4g(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
@@ -1622,6 +1638,7 @@ static void pc_machine_initfn(Object *obj)
#ifdef CONFIG_HPET
pcms->hpet_enabled = true;
#endif
+ pcms->default_bus_bypass_iommu = false;
pc_system_flash_create(pcms);
pcms->pcspk = isa_new(TYPE_PC_SPEAKER);
@@ -1746,6 +1763,10 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
object_class_property_add_bool(oc, "hpet",
pc_machine_get_hpet, pc_machine_set_hpet);
+ object_class_property_add_bool(oc, "default_bus_bypass_iommu",
+ pc_machine_get_default_bus_bypass_iommu,
+ pc_machine_set_default_bus_bypass_iommu);
+
object_class_property_add(oc, PC_MACHINE_MAX_FW_SIZE, "size",
pc_machine_get_max_fw_size, pc_machine_set_max_fw_size,
NULL, NULL);
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index 46a0f196f4..04b4a4788d 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -37,6 +37,7 @@
#include "sysemu/kvm.h"
#include "hw/kvm/clock.h"
#include "hw/pci-host/q35.h"
+#include "hw/pci/pcie_port.h"
#include "hw/qdev-properties.h"
#include "hw/i386/x86.h"
#include "hw/i386/pc.h"
@@ -136,6 +137,7 @@ static void pc_q35_init(MachineState *machine)
ram_addr_t lowmem;
DriveInfo *hd[MAX_SATA_PORTS];
MachineClass *mc = MACHINE_GET_CLASS(machine);
+ bool acpi_pcihp;
/* Check whether RAM fits below 4G (leaving 1/2 GByte for IO memory
* and 256 Mbytes for PCI Express Enhanced Configuration Access Mapping
@@ -236,6 +238,15 @@ static void pc_q35_init(MachineState *machine)
object_property_set_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP,
OBJECT(lpc), &error_abort);
+ acpi_pcihp = object_property_get_bool(OBJECT(lpc),
+ "acpi-pci-hotplug-with-bridge-support",
+ NULL);
+
+ if (acpi_pcihp) {
+ object_register_sugar_prop(TYPE_PCIE_SLOT, "native-hotplug",
+ "false", true);
+ }
+
/* irq lines */
gsi_state = pc_gsi_create(&x86ms->gsi, pcmc->pci_enabled);