pc,pci,virtio: lots of new features

Lots of last minute stuff.
 
 vhost-user-i2c.
 vhost-vsock SOCK_SEQPACKET support.
 IOMMU bypass.
 ACPI based pci hotplug.
 
 Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 
 iQFDBAABCAAtFiEEXQn9CHHI+FuUyooNKB8NuNKNVGkFAmDxoZ8PHG1zdEByZWRo
 YXQuY29tAAoJECgfDbjSjVRp76oH/1NJm5vFD+IjXC2sjKfxgqhi4QZGZOgU5rP6
 fDQA4HA0VM0iO7wUqIwc77Dn2BJLDnHG2biNlr1E0LnUWcpTATjX9qlonMz4gFn0
 1j49t1CmlkXinW55rVLsD5tjish1dR4mdzZMLQAxuUVxb5lH+rI2RDcbS2xrBs9G
 jPOm5TBl1FDREqnToVpWD+/gaCQAvFyjpXbSBKDahsxAZDL9GCpcZKNGgyBQrnPK
 1cYxPJZwTjVPwDXVArbFNCqUFemux2f5Hgx8gNFHomiNk9+mW0Q2quuVLwWHkbHC
 KEI7ZTfpjJkDXsQayhQ7HCTsiFl0qwj44BNUX07ZvpNkxNXXRHM=
 =4iA8
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/mst/tags/for_upstream3' into staging

pc,pci,virtio: lots of new features

Lots of last minute stuff.

vhost-user-i2c.
vhost-vsock SOCK_SEQPACKET support.
IOMMU bypass.
ACPI based pci hotplug.

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>

# gpg: Signature made Fri 16 Jul 2021 16:11:27 BST
# gpg:                using RSA key 5D09FD0871C8F85B94CA8A0D281F0DB8D28D5469
# gpg:                issuer "mst@redhat.com"
# gpg: Good signature from "Michael S. Tsirkin <mst@kernel.org>" [full]
# gpg:                 aka "Michael S. Tsirkin <mst@redhat.com>" [full]
# Primary key fingerprint: 0270 606B 6F3C DF3D 0B17  0970 C350 3912 AFBE 8E67
#      Subkey fingerprint: 5D09 FD08 71C8 F85B 94CA  8A0D 281F 0DB8 D28D 5469

* remotes/mst/tags/for_upstream3:
  vhost-vsock: SOCK_SEQPACKET feature bit support
  docs: Add documentation for iommu bypass
  hw/i386/acpi-build: Add IVRS support to bypass iommu
  hw/i386/acpi-build: Add DMAR support to bypass iommu
  hw/arm/virt-acpi-build: Add IORT support to bypass SMMUv3
  hw/pci: Add pci_bus_range() to get PCI bus number range
  hw/i386: Add a default_bus_bypass_iommu pc machine option
  hw/arm/virt: Add default_bus_bypass_iommu machine option
  hw/pxb: Add a bypass iommu property
  hw/pci/pci_host: Allow PCI host to bypass iommu
  docs: Add '-device intel-iommu' entry
  hw/virtio: add vhost-user-i2c-pci boilerplate
  hw/virtio: add boilerplate for vhost-user-i2c device
  bios-tables-test: Update golden binaries
  hw/acpi/ich9: Set ACPI PCI hot-plug as default on Q35
  bios-tables-test: Allow changes in DSDT ACPI tables
  hw/pci/pcie: Do not set HPC flag if acpihp is used
  hw/acpi/ich9: Enable ACPI PCI hot-plug
  hw/i386/acpi-build: Add ACPI PCI hot-plug methods to Q35

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2021-07-16 16:34:42 +01:00
commit a97fca4ceb
43 changed files with 949 additions and 42 deletions

89
docs/bypass-iommu.txt Normal file
View File

@ -0,0 +1,89 @@
BYPASS IOMMU PROPERTY
=====================
Description
===========
Traditionally, there is a global switch to enable/disable vIOMMU. All
devices in the system can only support go through vIOMMU or not, which
is not flexible. We introduce this bypass iommu property to support
coexist of devices go through vIOMMU and devices not. This is useful to
passthrough devices with no-iommu mode and devices go through vIOMMU in
the same virtual machine.
PCI host bridges have a bypass_iommu property. This property is used to
determine whether the devices attached on the PCI host bridge will bypass
virtual iommu. The bypass_iommu property is valid only when there is a
virtual iommu in the system, it is implemented to allow some devices to
bypass vIOMMU. When bypass_iommu property is not set for a host bridge,
the attached devices will go through vIOMMU by default.
Usage
=====
The bypass iommu feature support PXB host bridge and default main host
bridge, we add a bypass_iommu property for PXB and default_bus_bypass_iommu
for machine. Note that default_bus_bypass_iommu is available only when
the 'q35' machine type on x86 architecture and the 'virt' machine type
on AArch64. Other machine types do not support bypass iommu for default
root bus.
1. The following is the bypass iommu options:
(1) PCI expander bridge
qemu -device pxb-pcie,bus_nr=0x10,addr=0x1,bypass_iommu=true
(2) Arm default host bridge
qemu -machine virt,iommu=smmuv3,default_bus_bypass_iommu=true
(3) X86 default root bus bypass iommu:
qemu -machine q35,default_bus_bypass_iommu=true
2. Here is the detailed qemu command line for 'virt' machine with PXB on
AArch64:
qemu-system-aarch64 \
-machine virt,kernel_irqchip=on,iommu=smmuv3,default_bus_bypass_iommu=true \
-device pxb-pcie,bus_nr=0x10,id=pci.10,bus=pcie.0,addr=0x3.0x1 \
-device pxb-pcie,bus_nr=0x20,id=pci.20,bus=pcie.0,addr=0x3.0x2,bypass_iommu=true \
And we got:
- a default host bridge which bypass SMMUv3
- a pxb host bridge which go through SMMUv3
- a pxb host bridge which bypass SMMUv3
3. Here is the detailed qemu command line for 'q35' machine with PXB on
x86 architecture:
qemu-system-x86_64 \
-machine q35,accel=kvm,default_bus_bypass_iommu=true \
-device pxb-pcie,bus_nr=0x10,id=pci.10,bus=pcie.0,addr=0x3 \
-device pxb-pcie,bus_nr=0x20,id=pci.20,bus=pcie.0,addr=0x4,bypass_iommu=true \
-device intel-iommu \
And we got:
- a default host bridge which bypass iommu
- a pxb host bridge which go through iommu
- a pxb host bridge which bypass iommu
Limitations
===========
There might be potential security risk when devices bypass iommu, because
devices might send malicious dma request to virtual machine if there is no
iommu isolation. So it would be necessary to only bypass iommu for trusted
device.
Implementation
==============
The bypass iommu feature includes:
- Address space
Add bypass iommu property check of PCI Host and do not get iommu address
space for devices bypass iommu.
- Arm SMMUv3 support
We traverse all PCI root bus and get bus number ranges, then build explicit
RID mapping for devices which do not bypass iommu.
- X86 IOMMU support
To support Intel iommu, we traverse all PCI host bridge and get information
of devices which do not bypass iommu, then fill the DMAR drhd struct with
explicit device scope info. To support AMD iommu, add check of bypass iommu
when traverse the PCI hsot bridge.
- Machine and PXB options
We add bypass iommu options in machine option for default root bus, and add
option for PXB also. Note that the default value of bypass iommu is false,
so that the devices will by default go through iommu if there exist one.

View File

@ -1,7 +1,13 @@
#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;
}

View File

@ -217,6 +217,26 @@ static const VMStateDescription vmstate_cpuhp_state = {
}
};
static bool vmstate_test_use_pcihp(void *opaque)
{
ICH9LPCPMRegs *s = opaque;
return s->use_acpi_hotplug_bridge;
}
static const VMStateDescription vmstate_pcihp_state = {
.name = "ich9_pm/pcihp",
.version_id = 1,
.minimum_version_id = 1,
.needed = vmstate_test_use_pcihp,
.fields = (VMStateField[]) {
VMSTATE_PCI_HOTPLUG(acpi_pci_hotplug,
ICH9LPCPMRegs,
NULL, NULL),
VMSTATE_END_OF_LIST()
}
};
const VMStateDescription vmstate_ich9_pm = {
.name = "ich9_pm",
.version_id = 1,
@ -238,6 +258,7 @@ const VMStateDescription vmstate_ich9_pm = {
&vmstate_memhp_state,
&vmstate_tco_io_state,
&vmstate_cpuhp_state,
&vmstate_pcihp_state,
NULL
}
};
@ -259,6 +280,10 @@ 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);
}
acpi_update_sci(&pm->acpi_regs, pm->irq);
}
@ -297,6 +322,18 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
pm->enable_tco = true;
acpi_pm_tco_init(&pm->tco_regs, &pm->io);
if (pm->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)),
OBJECT(lpc_pci));
}
pm->irq = sci_irq;
qemu_register_reset(pm_reset, pm);
pm->powerdown_notifier.notify = pm_powerdown_req;
@ -368,6 +405,20 @@ static void ich9_pm_set_enable_tco(Object *obj, bool value, Error **errp)
s->pm.enable_tco = value;
}
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;
}
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;
}
void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm)
{
static const uint32_t gpe0_len = ICH9_PMIO_GPE0_LEN;
@ -376,6 +427,7 @@ 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;
object_property_add_uint32_ptr(obj, ACPI_PM_PROP_PM_IO_BASE,
&pm->pm_io_base, OBJ_PROP_FLAG_READ);
@ -399,6 +451,9 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm)
object_property_add_bool(obj, ACPI_PM_PROP_TCO_ENABLED,
ich9_pm_get_enable_tco,
ich9_pm_set_enable_tco);
object_property_add_bool(obj, "acpi-pci-hotplug-with-bridge-support",
ich9_pm_get_acpi_pci_hotplug,
ich9_pm_set_acpi_pci_hotplug);
}
void ich9_pm_device_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
@ -406,6 +461,11 @@ void ich9_pm_device_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
{
ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev);
if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
acpi_pcihp_device_pre_plug_cb(hotplug_dev, dev, errp);
return;
}
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) &&
!lpc->pm.acpi_memory_hotplug.is_enabled) {
error_setg(errp,
@ -441,6 +501,9 @@ void ich9_pm_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
} else {
acpi_cpu_plug_cb(hotplug_dev, &lpc->pm.cpuhp_state, dev, errp);
}
} else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
acpi_pcihp_device_plug_cb(hotplug_dev, &lpc->pm.acpi_pci_hotplug,
dev, errp);
} else {
error_setg(errp, "acpi: device plug request for not supported device"
" type: %s", object_get_typename(OBJECT(dev)));
@ -473,6 +536,10 @@ void ich9_pm_device_unplug_request_cb(HotplugHandler *hotplug_dev,
acpi_cpu_unplug_request_cb(hotplug_dev, &lpc->pm.cpuhp_state,
dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
acpi_pcihp_device_unplug_request_cb(hotplug_dev,
&lpc->pm.acpi_pci_hotplug,
dev, errp);
} else {
error_setg(errp, "acpi: device unplug request for not supported device"
" type: %s", object_get_typename(OBJECT(dev)));
@ -490,6 +557,9 @@ void ich9_pm_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU) &&
!lpc->pm.cpu_hotplug_legacy) {
acpi_cpu_unplug_cb(&lpc->pm.cpuhp_state, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
acpi_pcihp_device_unplug_cb(hotplug_dev, &lpc->pm.acpi_pci_hotplug,
dev, errp);
} else {
error_setg(errp, "acpi: device unplug for not supported device"
" type: %s", object_get_typename(OBJECT(dev)));

View File

@ -30,6 +30,9 @@
#include "hw/pci-host/i440fx.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_bridge.h"
#include "hw/pci/pci_host.h"
#include "hw/pci/pcie_port.h"
#include "hw/i386/acpi-build.h"
#include "hw/acpi/acpi.h"
#include "hw/pci/pci_bus.h"
#include "migration/vmstate.h"
@ -37,7 +40,6 @@
#include "qom/qom-qobject.h"
#include "trace.h"
#define ACPI_PCIHP_ADDR 0xae00
#define ACPI_PCIHP_SIZE 0x0018
#define PCI_UP_BASE 0x0000
#define PCI_DOWN_BASE 0x0004
@ -104,6 +106,7 @@ static void *acpi_set_bsel(PCIBus *bus, void *opaque)
static void acpi_set_pci_info(void)
{
static bool bsel_is_set;
Object *host = acpi_get_i386_pci_host();
PCIBus *bus;
unsigned bsel_alloc = ACPI_PCIHP_BSEL_DEFAULT;
@ -112,7 +115,11 @@ static void acpi_set_pci_info(void)
}
bsel_is_set = true;
bus = find_i440fx(); /* TODO: Q35 support */
if (!host) {
return;
}
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);
@ -122,13 +129,14 @@ static void acpi_set_pci_info(void)
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 = find_i440fx();
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);
@ -329,6 +337,13 @@ void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s,
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));
@ -488,10 +503,11 @@ 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)
MemoryRegion *address_space_io, bool bridges_enabled,
uint16_t io_base)
{
s->io_len = ACPI_PCIHP_SIZE;
s->io_base = ACPI_PCIHP_ADDR;
s->io_base = io_base;
s->root = root_bus;
s->legacy_piix = !bridges_enabled;

View File

@ -49,6 +49,8 @@
#define GPE_BASE 0xafe0
#define GPE_LEN 4
#define ACPI_PCIHP_ADDR_PIIX4 0xae00
struct pci_status {
uint32_t up; /* deprecated, maintained for migration compatibility */
uint32_t down;
@ -607,7 +609,7 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent,
if (s->use_acpi_hotplug_bridge || s->use_acpi_root_pci_hotplug) {
acpi_pcihp_init(OBJECT(s), &s->acpi_pci_hotplug, bus, parent,
s->use_acpi_hotplug_bridge);
s->use_acpi_hotplug_bridge, ACPI_PCIHP_ADDR_PIIX4);
}
s->cpu_hotplug_legacy = true;

View File

@ -44,6 +44,7 @@
#include "hw/acpi/tpm.h"
#include "hw/pci/pcie_host.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_bus.h"
#include "hw/pci-host/gpex.h"
#include "hw/arm/virt.h"
#include "hw/mem/nvdimm.h"
@ -239,23 +240,89 @@ static void acpi_dsdt_add_tpm(Aml *scope, VirtMachineState *vms)
}
#endif
/* Build the iort ID mapping to SMMUv3 for a given PCI host bridge */
static int
iort_host_bridges(Object *obj, void *opaque)
{
GArray *idmap_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)) {
int min_bus, max_bus;
pci_bus_range(bus, &min_bus, &max_bus);
AcpiIortIdMapping idmap = {
.input_base = min_bus << 8,
.id_count = (max_bus - min_bus + 1) << 8,
};
g_array_append_val(idmap_blob, idmap);
}
}
return 0;
}
static int iort_idmap_compare(gconstpointer a, gconstpointer b)
{
AcpiIortIdMapping *idmap_a = (AcpiIortIdMapping *)a;
AcpiIortIdMapping *idmap_b = (AcpiIortIdMapping *)b;
return idmap_a->input_base - idmap_b->input_base;
}
static void
build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
{
int nb_nodes, iort_start = table_data->len;
int i, nb_nodes, rc_mapping_count, iort_start = table_data->len;
AcpiIortIdMapping *idmap;
AcpiIortItsGroup *its;
AcpiIortTable *iort;
AcpiIortSmmu3 *smmu;
size_t node_size, iort_node_offset, iort_length, smmu_offset = 0;
AcpiIortRC *rc;
GArray *smmu_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping));
GArray *its_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping));
iort = acpi_data_push(table_data, sizeof(*iort));
if (vms->iommu == VIRT_IOMMU_SMMUV3) {
AcpiIortIdMapping next_range = {0};
object_child_foreach_recursive(object_get_root(),
iort_host_bridges, smmu_idmaps);
/* Sort the smmu idmap by input_base */
g_array_sort(smmu_idmaps, iort_idmap_compare);
/*
* Split the whole RIDs by mapping from RC to SMMU,
* build the ID mapping from RC to ITS directly.
*/
for (i = 0; i < smmu_idmaps->len; i++) {
idmap = &g_array_index(smmu_idmaps, AcpiIortIdMapping, i);
if (next_range.input_base < idmap->input_base) {
next_range.id_count = idmap->input_base - next_range.input_base;
g_array_append_val(its_idmaps, next_range);
}
next_range.input_base = idmap->input_base + idmap->id_count;
}
/* Append the last RC -> ITS ID mapping */
if (next_range.input_base < 0xFFFF) {
next_range.id_count = 0xFFFF - next_range.input_base;
g_array_append_val(its_idmaps, next_range);
}
nb_nodes = 3; /* RC, ITS, SMMUv3 */
rc_mapping_count = smmu_idmaps->len + its_idmaps->len;
} else {
nb_nodes = 2; /* RC, ITS */
rc_mapping_count = 1;
}
iort_length = sizeof(*iort);
@ -307,13 +374,13 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
}
/* Root Complex Node */
node_size = sizeof(*rc) + sizeof(*idmap);
node_size = sizeof(*rc) + sizeof(*idmap) * rc_mapping_count;
iort_length += node_size;
rc = acpi_data_push(table_data, node_size);
rc->type = ACPI_IORT_NODE_PCI_ROOT_COMPLEX;
rc->length = cpu_to_le16(node_size);
rc->mapping_count = cpu_to_le32(1);
rc->mapping_count = cpu_to_le32(rc_mapping_count);
rc->mapping_offset = cpu_to_le32(sizeof(*rc));
/* fully coherent device */
@ -321,20 +388,45 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
rc->memory_properties.memory_flags = 0x3; /* CCA = CPM = DCAS = 1 */
rc->pci_segment_number = 0; /* MCFG pci_segment */
/* Identity RID mapping covering the whole input RID range */
idmap = &rc->id_mapping_array[0];
idmap->input_base = 0;
idmap->id_count = cpu_to_le32(0xFFFF);
idmap->output_base = 0;
if (vms->iommu == VIRT_IOMMU_SMMUV3) {
/* output IORT node is the smmuv3 node */
idmap->output_reference = cpu_to_le32(smmu_offset);
AcpiIortIdMapping *range;
/* translated RIDs connect to SMMUv3 node: RC -> SMMUv3 -> ITS */
for (i = 0; i < smmu_idmaps->len; i++) {
idmap = &rc->id_mapping_array[i];
range = &g_array_index(smmu_idmaps, AcpiIortIdMapping, i);
idmap->input_base = cpu_to_le32(range->input_base);
idmap->id_count = cpu_to_le32(range->id_count);
idmap->output_base = cpu_to_le32(range->input_base);
/* output IORT node is the smmuv3 node */
idmap->output_reference = cpu_to_le32(smmu_offset);
}
/* bypassed RIDs connect to ITS group node directly: RC -> ITS */
for (i = 0; i < its_idmaps->len; i++) {
idmap = &rc->id_mapping_array[smmu_idmaps->len + i];
range = &g_array_index(its_idmaps, AcpiIortIdMapping, i);
idmap->input_base = cpu_to_le32(range->input_base);
idmap->id_count = cpu_to_le32(range->id_count);
idmap->output_base = cpu_to_le32(range->input_base);
/* output IORT node is the ITS group node (the first node) */
idmap->output_reference = cpu_to_le32(iort_node_offset);
}
} else {
/* Identity RID mapping covering the whole input RID range */
idmap = &rc->id_mapping_array[0];
idmap->input_base = cpu_to_le32(0);
idmap->id_count = cpu_to_le32(0xFFFF);
idmap->output_base = cpu_to_le32(0);
/* output IORT node is the ITS group node (the first node) */
idmap->output_reference = cpu_to_le32(iort_node_offset);
}
g_array_free(smmu_idmaps, true);
g_array_free(its_idmaps, true);
/*
* Update the pointer address in case table_data->data moves during above
* acpi_data_push operations.

View File

@ -1367,6 +1367,7 @@ static void create_pcie(VirtMachineState *vms)
}
pci = PCI_HOST_BRIDGE(dev);
pci->bypass_iommu = vms->default_bus_bypass_iommu;
vms->bus = pci->bus;
if (vms->bus) {
for (i = 0; i < nb_nics; i++) {
@ -2322,6 +2323,21 @@ static void virt_set_iommu(Object *obj, const char *value, Error **errp)
}
}
static bool virt_get_default_bus_bypass_iommu(Object *obj, Error **errp)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
return vms->default_bus_bypass_iommu;
}
static void virt_set_default_bus_bypass_iommu(Object *obj, bool value,
Error **errp)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
vms->default_bus_bypass_iommu = value;
}
static CpuInstanceProperties
virt_cpu_index_to_props(MachineState *ms, unsigned cpu_index)
{
@ -2661,6 +2677,13 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
"Set the IOMMU type. "
"Valid values are none and smmuv3");
object_class_property_add_bool(oc, "default_bus_bypass_iommu",
virt_get_default_bus_bypass_iommu,
virt_set_default_bus_bypass_iommu);
object_class_property_set_description(oc, "default_bus_bypass_iommu",
"Set on/off to enable/disable "
"bypass_iommu for default root bus");
object_class_property_add_bool(oc, "ras", virt_get_ras,
virt_set_ras);
object_class_property_set_description(oc, "ras",
@ -2728,6 +2751,9 @@ static void virt_instance_init(Object *obj)
/* Default disallows iommu instantiation */
vms->iommu = VIRT_IOMMU_NONE;
/* The default root bus is attached to iommu by default */
vms->default_bus_bypass_iommu = false;
/* Default disallows RAS instantiation */
vms->ras = false;

View File

@ -584,7 +584,6 @@ static void machine_set_memdev(Object *obj, const char *value, Error **errp)
ms->ram_memdev_id = g_strdup(value);
}
static void machine_init_notify(Notifier *notifier, void *data)
{
MachineState *machine = MACHINE(qdev_get_machine());

View File

@ -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;
}
@ -2010,6 +2022,56 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
x86ms->oem_table_id);
}
/*
* 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;

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -57,6 +57,7 @@ struct PXBDev {
uint8_t bus_nr;
uint16_t numa_node;
bool bypass_iommu;
};
static PXBDev *convert_to_pxb(PCIDevice *dev)
@ -255,6 +256,7 @@ static void pxb_dev_realize_common(PCIDevice *dev, bool pcie, Error **errp)
bus->map_irq = pxb_map_irq_fn;
PCI_HOST_BRIDGE(ds)->bus = bus;
PCI_HOST_BRIDGE(ds)->bypass_iommu = pxb->bypass_iommu;
pxb_register_bus(dev, bus, &local_err);
if (local_err) {
@ -301,6 +303,7 @@ static Property pxb_dev_properties[] = {
/* Note: 0 is not a legal PXB bus number. */
DEFINE_PROP_UINT8("bus_nr", PXBDev, bus_nr, 0),
DEFINE_PROP_UINT16("numa_node", PXBDev, numa_node, NUMA_NODE_UNASSIGNED),
DEFINE_PROP_BOOL("bypass_iommu", PXBDev, bypass_iommu, false),
DEFINE_PROP_END_OF_LIST(),
};

View File

@ -65,6 +65,8 @@ static void q35_host_realize(DeviceState *dev, Error **errp)
s->mch.address_space_io,
0, TYPE_PCIE_BUS);
PC_MACHINE(qdev_get_machine())->bus = pci->bus;
pci->bypass_iommu =
PC_MACHINE(qdev_get_machine())->default_bus_bypass_iommu;
qdev_realize(DEVICE(&s->mch), BUS(pci->bus), &error_fatal);
}

View File

@ -416,6 +416,22 @@ const char *pci_root_bus_path(PCIDevice *dev)
return rootbus->qbus.name;
}
bool pci_bus_bypass_iommu(PCIBus *bus)
{
PCIBus *rootbus = bus;
PCIHostState *host_bridge;
if (!pci_bus_is_root(bus)) {
rootbus = pci_device_root_bus(bus->parent_dev);
}
host_bridge = PCI_HOST_BRIDGE(rootbus->qbus.parent);
assert(host_bridge->bus == rootbus);
return host_bridge->bypass_iommu;
}
static void pci_root_bus_init(PCIBus *bus, DeviceState *parent,
MemoryRegion *address_space_mem,
MemoryRegion *address_space_io,
@ -521,6 +537,22 @@ int pci_bus_num(PCIBus *s)
return PCI_BUS_GET_CLASS(s)->bus_num(s);
}
/* Returns the min and max bus numbers of a PCI bus hierarchy */
void pci_bus_range(PCIBus *bus, int *min_bus, int *max_bus)
{
int i;
*min_bus = *max_bus = pci_bus_num(bus);
for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) {
PCIDevice *dev = bus->devices[i];
if (dev && PCI_DEVICE_GET_CLASS(dev)->is_bridge) {
*min_bus = MIN(*min_bus, dev->config[PCI_SECONDARY_BUS]);
*max_bus = MAX(*max_bus, dev->config[PCI_SUBORDINATE_BUS]);
}
}
}
int pci_bus_numa_node(PCIBus *bus)
{
return PCI_BUS_GET_CLASS(bus)->numa_node(bus);
@ -2718,7 +2750,7 @@ AddressSpace *pci_device_iommu_address_space(PCIDevice *dev)
iommu_bus = parent_bus;
}
if (iommu_bus && iommu_bus->iommu_fn) {
if (!pci_bus_bypass_iommu(bus) && iommu_bus && iommu_bus->iommu_fn) {
return iommu_bus->iommu_fn(bus, iommu_bus->iommu_opaque, devfn);
}
return &address_space_memory;

View File

@ -222,6 +222,7 @@ const VMStateDescription vmstate_pcihost = {
static Property pci_host_properties_common[] = {
DEFINE_PROP_BOOL("x-config-reg-migration-enabled", PCIHostState,
mig_enabled, true),
DEFINE_PROP_BOOL("bypass-iommu", PCIHostState, bypass_iommu, false),
DEFINE_PROP_END_OF_LIST(),
};

View File

@ -529,7 +529,13 @@ void pcie_cap_slot_init(PCIDevice *dev, PCIESlot *s)
PCI_EXP_SLTCAP_PIP |
PCI_EXP_SLTCAP_AIP |
PCI_EXP_SLTCAP_ABP);
if (s->hotplug) {
/*
* Enable native hot-plug on all hot-plugged bridges unless
* hot-plug is disabled on the slot.
*/
if (s->hotplug &&
(s->native_hotplug || DEVICE(dev)->hotplugged)) {
pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCAP,
PCI_EXP_SLTCAP_HPS |
PCI_EXP_SLTCAP_HPC);

View File

@ -148,6 +148,7 @@ static Property pcie_slot_props[] = {
DEFINE_PROP_UINT8("chassis", PCIESlot, chassis, 0),
DEFINE_PROP_UINT16("slot", PCIESlot, slot, 0),
DEFINE_PROP_BOOL("hotplug", PCIESlot, hotplug, true),
DEFINE_PROP_BOOL("native-hotplug", PCIESlot, native_hotplug, true),
DEFINE_PROP_END_OF_LIST()
};

View File

@ -58,3 +58,8 @@ config VIRTIO_MEM
depends on LINUX
depends on VIRTIO_MEM_SUPPORTED
select MEM_DEVICE
config VHOST_USER_I2C
bool
default y
depends on VIRTIO && VHOST_USER

View File

@ -25,6 +25,8 @@ virtio_ss.add(when: 'CONFIG_VHOST_USER_VSOCK', if_true: files('vhost-user-vsock.
virtio_ss.add(when: 'CONFIG_VIRTIO_RNG', if_true: files('virtio-rng.c'))
virtio_ss.add(when: 'CONFIG_VIRTIO_IOMMU', if_true: files('virtio-iommu.c'))
virtio_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem.c'))
virtio_ss.add(when: 'CONFIG_VHOST_USER_I2C', if_true: files('vhost-user-i2c.c'))
virtio_ss.add(when: ['CONFIG_VIRTIO_PCI', 'CONFIG_VHOST_USER_I2C'], if_true: files('vhost-user-i2c-pci.c'))
virtio_pci_ss = ss.source_set()
virtio_pci_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock-pci.c'))

View File

@ -0,0 +1,69 @@
/*
* Vhost-user i2c virtio device PCI glue
*
* Copyright (c) 2021 Viresh Kumar <viresh.kumar@linaro.org>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "hw/qdev-properties.h"
#include "hw/virtio/vhost-user-i2c.h"
#include "virtio-pci.h"
struct VHostUserI2CPCI {
VirtIOPCIProxy parent_obj;
VHostUserI2C vdev;
};
typedef struct VHostUserI2CPCI VHostUserI2CPCI;
#define TYPE_VHOST_USER_I2C_PCI "vhost-user-i2c-pci-base"
DECLARE_INSTANCE_CHECKER(VHostUserI2CPCI, VHOST_USER_I2C_PCI,
TYPE_VHOST_USER_I2C_PCI)
static void vhost_user_i2c_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
{
VHostUserI2CPCI *dev = VHOST_USER_I2C_PCI(vpci_dev);
DeviceState *vdev = DEVICE(&dev->vdev);
vpci_dev->nvectors = 1;
qdev_realize(vdev, BUS(&vpci_dev->bus), errp);
}
static void vhost_user_i2c_pci_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
k->realize = vhost_user_i2c_pci_realize;
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
pcidev_k->device_id = 0; /* Set by virtio-pci based on virtio id */
pcidev_k->revision = 0x00;
pcidev_k->class_id = PCI_CLASS_COMMUNICATION_OTHER;
}
static void vhost_user_i2c_pci_instance_init(Object *obj)
{
VHostUserI2CPCI *dev = VHOST_USER_I2C_PCI(obj);
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
TYPE_VHOST_USER_I2C);
}
static const VirtioPCIDeviceTypeInfo vhost_user_i2c_pci_info = {
.base_name = TYPE_VHOST_USER_I2C_PCI,
.non_transitional_name = "vhost-user-i2c-pci",
.instance_size = sizeof(VHostUserI2CPCI),
.instance_init = vhost_user_i2c_pci_instance_init,
.class_init = vhost_user_i2c_pci_class_init,
};
static void vhost_user_i2c_pci_register(void)
{
virtio_pci_types_register(&vhost_user_i2c_pci_info);
}
type_init(vhost_user_i2c_pci_register);

288
hw/virtio/vhost-user-i2c.c Normal file
View File

@ -0,0 +1,288 @@
/*
* Vhost-user i2c virtio device
*
* Copyright (c) 2021 Viresh Kumar <viresh.kumar@linaro.org>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "hw/qdev-properties.h"
#include "hw/virtio/virtio-bus.h"
#include "hw/virtio/vhost-user-i2c.h"
#include "qemu/error-report.h"
#include "standard-headers/linux/virtio_ids.h"
/* Remove this once the header is updated in Linux kernel */
#ifndef VIRTIO_ID_I2C_ADAPTER
#define VIRTIO_ID_I2C_ADAPTER 34
#endif
static void vu_i2c_start(VirtIODevice *vdev)
{
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
VHostUserI2C *i2c = VHOST_USER_I2C(vdev);
int ret, i;
if (!k->set_guest_notifiers) {
error_report("binding does not support guest notifiers");
return;
}
ret = vhost_dev_enable_notifiers(&i2c->vhost_dev, vdev);
if (ret < 0) {
error_report("Error enabling host notifiers: %d", -ret);
return;
}
ret = k->set_guest_notifiers(qbus->parent, i2c->vhost_dev.nvqs, true);
if (ret < 0) {
error_report("Error binding guest notifier: %d", -ret);
goto err_host_notifiers;
}
i2c->vhost_dev.acked_features = vdev->guest_features;
ret = vhost_dev_start(&i2c->vhost_dev, vdev);
if (ret < 0) {
error_report("Error starting vhost-user-i2c: %d", -ret);
goto err_guest_notifiers;
}
/*
* guest_notifier_mask/pending not used yet, so just unmask
* everything here. virtio-pci will do the right thing by
* enabling/disabling irqfd.
*/
for (i = 0; i < i2c->vhost_dev.nvqs; i++) {
vhost_virtqueue_mask(&i2c->vhost_dev, vdev, i, false);
}
return;
err_guest_notifiers:
k->set_guest_notifiers(qbus->parent, i2c->vhost_dev.nvqs, false);
err_host_notifiers:
vhost_dev_disable_notifiers(&i2c->vhost_dev, vdev);
}
static void vu_i2c_stop(VirtIODevice *vdev)
{
VHostUserI2C *i2c = VHOST_USER_I2C(vdev);
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
int ret;
if (!k->set_guest_notifiers) {
return;
}
vhost_dev_stop(&i2c->vhost_dev, vdev);
ret = k->set_guest_notifiers(qbus->parent, i2c->vhost_dev.nvqs, false);
if (ret < 0) {
error_report("vhost guest notifier cleanup failed: %d", ret);
return;
}
vhost_dev_disable_notifiers(&i2c->vhost_dev, vdev);
}
static void vu_i2c_set_status(VirtIODevice *vdev, uint8_t status)
{
VHostUserI2C *i2c = VHOST_USER_I2C(vdev);
bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK;
if (!vdev->vm_running) {
should_start = false;
}
if (i2c->vhost_dev.started == should_start) {
return;
}
if (should_start) {
vu_i2c_start(vdev);
} else {
vu_i2c_stop(vdev);
}
}
static uint64_t vu_i2c_get_features(VirtIODevice *vdev,
uint64_t requested_features, Error **errp)
{
/* No feature bits used yet */
return requested_features;
}
static void vu_i2c_handle_output(VirtIODevice *vdev, VirtQueue *vq)
{
/*
* Not normally called; it's the daemon that handles the queue;
* however virtio's cleanup path can call this.
*/
}
static void vu_i2c_guest_notifier_mask(VirtIODevice *vdev, int idx, bool mask)
{
VHostUserI2C *i2c = VHOST_USER_I2C(vdev);
vhost_virtqueue_mask(&i2c->vhost_dev, vdev, idx, mask);
}
static bool vu_i2c_guest_notifier_pending(VirtIODevice *vdev, int idx)
{
VHostUserI2C *i2c = VHOST_USER_I2C(vdev);
return vhost_virtqueue_pending(&i2c->vhost_dev, idx);
}
static void do_vhost_user_cleanup(VirtIODevice *vdev, VHostUserI2C *i2c)
{
vhost_user_cleanup(&i2c->vhost_user);
virtio_delete_queue(i2c->vq);
virtio_cleanup(vdev);
g_free(i2c->vhost_dev.vqs);
i2c->vhost_dev.vqs = NULL;
}
static int vu_i2c_connect(DeviceState *dev)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VHostUserI2C *i2c = VHOST_USER_I2C(vdev);
if (i2c->connected) {
return 0;
}
i2c->connected = true;
/* restore vhost state */
if (virtio_device_started(vdev, vdev->status)) {
vu_i2c_start(vdev);
}
return 0;
}
static void vu_i2c_disconnect(DeviceState *dev)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VHostUserI2C *i2c = VHOST_USER_I2C(vdev);
if (!i2c->connected) {
return;
}
i2c->connected = false;
if (i2c->vhost_dev.started) {
vu_i2c_stop(vdev);
}
}
static void vu_i2c_event(void *opaque, QEMUChrEvent event)
{
DeviceState *dev = opaque;
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VHostUserI2C *i2c = VHOST_USER_I2C(vdev);
switch (event) {
case CHR_EVENT_OPENED:
if (vu_i2c_connect(dev) < 0) {
qemu_chr_fe_disconnect(&i2c->chardev);
return;
}
break;
case CHR_EVENT_CLOSED:
vu_i2c_disconnect(dev);
break;
case CHR_EVENT_BREAK:
case CHR_EVENT_MUX_IN:
case CHR_EVENT_MUX_OUT:
/* Ignore */
break;
}
}
static void vu_i2c_device_realize(DeviceState *dev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VHostUserI2C *i2c = VHOST_USER_I2C(dev);
int ret;
if (!i2c->chardev.chr) {
error_setg(errp, "vhost-user-i2c: missing chardev");
return;
}
if (!vhost_user_init(&i2c->vhost_user, &i2c->chardev, errp)) {
return;
}
virtio_init(vdev, "vhost-user-i2c", VIRTIO_ID_I2C_ADAPTER, 0);
i2c->vhost_dev.nvqs = 1;
i2c->vq = virtio_add_queue(vdev, 4, vu_i2c_handle_output);
i2c->vhost_dev.vqs = g_new0(struct vhost_virtqueue, i2c->vhost_dev.nvqs);
ret = vhost_dev_init(&i2c->vhost_dev, &i2c->vhost_user,
VHOST_BACKEND_TYPE_USER, 0, errp);
if (ret < 0) {
do_vhost_user_cleanup(vdev, i2c);
}
qemu_chr_fe_set_handlers(&i2c->chardev, NULL, NULL, vu_i2c_event, NULL,
dev, NULL, true);
}
static void vu_i2c_device_unrealize(DeviceState *dev)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VHostUserI2C *i2c = VHOST_USER_I2C(dev);
/* This will stop vhost backend if appropriate. */
vu_i2c_set_status(vdev, 0);
vhost_dev_cleanup(&i2c->vhost_dev);
do_vhost_user_cleanup(vdev, i2c);
}
static const VMStateDescription vu_i2c_vmstate = {
.name = "vhost-user-i2c",
.unmigratable = 1,
};
static Property vu_i2c_properties[] = {
DEFINE_PROP_CHR("chardev", VHostUserI2C, chardev),
DEFINE_PROP_END_OF_LIST(),
};
static void vu_i2c_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
device_class_set_props(dc, vu_i2c_properties);
dc->vmsd = &vu_i2c_vmstate;
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
vdc->realize = vu_i2c_device_realize;
vdc->unrealize = vu_i2c_device_unrealize;
vdc->get_features = vu_i2c_get_features;
vdc->set_status = vu_i2c_set_status;
vdc->guest_notifier_mask = vu_i2c_guest_notifier_mask;
vdc->guest_notifier_pending = vu_i2c_guest_notifier_pending;
}
static const TypeInfo vu_i2c_info = {
.name = TYPE_VHOST_USER_I2C,
.parent = TYPE_VIRTIO_DEVICE,
.instance_size = sizeof(VHostUserI2C),
.class_init = vu_i2c_class_init,
};
static void vu_i2c_register_types(void)
{
type_register_static(&vu_i2c_info);
}
type_init(vu_i2c_register_types)

View File

@ -21,6 +21,11 @@
#include "hw/virtio/vhost-vsock.h"
#include "monitor/monitor.h"
const int feature_bits[] = {
VIRTIO_VSOCK_F_SEQPACKET,
VHOST_INVALID_FEATURE_BIT
};
static void vhost_vsock_get_config(VirtIODevice *vdev, uint8_t *config)
{
VHostVSock *vsock = VHOST_VSOCK(vdev);
@ -108,8 +113,11 @@ static uint64_t vhost_vsock_get_features(VirtIODevice *vdev,
uint64_t requested_features,
Error **errp)
{
/* No feature bits used yet */
return requested_features;
VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
virtio_add_feature(&requested_features, VIRTIO_VSOCK_F_SEQPACKET);
return vhost_get_features(&vvc->vhost_dev, feature_bits,
requested_features);
}
static const VMStateDescription vmstate_virtio_vhost_vsock = {

View File

@ -24,10 +24,13 @@
#include "hw/acpi/acpi.h"
#include "hw/acpi/cpu_hotplug.h"
#include "hw/acpi/cpu.h"
#include "hw/acpi/pcihp.h"
#include "hw/acpi/memory_hotplug.h"
#include "hw/acpi/acpi_dev_interface.h"
#include "hw/acpi/tco.h"
#define ACPI_PCIHP_ADDR_ICH9 0x0cc4
typedef struct ICH9LPCPMRegs {
/*
* In ich9 spec says that pm1_cnt register is 32bit width and
@ -53,6 +56,8 @@ typedef struct ICH9LPCPMRegs {
AcpiCpuHotplug gpe_cpu;
CPUHotplugState cpuhp_state;
bool use_acpi_hotplug_bridge;
AcpiPciHpState acpi_pci_hotplug;
MemHotplugState acpi_memory_hotplug;
uint8_t disable_s3;

View File

@ -55,7 +55,8 @@ typedef struct AcpiPciHpState {
} AcpiPciHpState;
void acpi_pcihp_init(Object *owner, AcpiPciHpState *, PCIBus *root,
MemoryRegion *address_space_io, bool bridges_enabled);
MemoryRegion *address_space_io, bool bridges_enabled,
uint16_t io_base);
void acpi_pcihp_device_pre_plug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp);

View File

@ -147,6 +147,7 @@ struct VirtMachineState {
OnOffAuto acpi;
VirtGICType gic_version;
VirtIOMMUType iommu;
bool default_bus_bypass_iommu;
VirtMSIControllerType msi_controller;
uint16_t virtio_iommu_bdf;
struct arm_boot_info bootinfo;

View File

@ -44,6 +44,7 @@ typedef struct PCMachineState {
bool sata_enabled;
bool pit_enabled;
bool hpet_enabled;
bool default_bus_bypass_iommu;
uint64_t max_fw_size;
/* NUMA information: */

View File

@ -450,6 +450,7 @@ static inline PCIBus *pci_get_bus(const PCIDevice *dev)
return PCI_BUS(qdev_get_parent_bus(DEVICE(dev)));
}
int pci_bus_num(PCIBus *s);
void pci_bus_range(PCIBus *bus, int *min_bus, int *max_bus);
static inline int pci_dev_bus_num(const PCIDevice *dev)
{
return pci_bus_num(pci_get_bus(dev));
@ -480,6 +481,7 @@ void pci_for_each_bus(PCIBus *bus,
PCIBus *pci_device_root_bus(const PCIDevice *d);
const char *pci_root_bus_path(PCIDevice *dev);
bool pci_bus_bypass_iommu(PCIBus *bus);
PCIDevice *pci_find_device(PCIBus *bus, int bus_num, uint8_t devfn);
int pci_qdev_find_device(const char *id, PCIDevice **pdev);
void pci_bus_get_w64_range(PCIBus *bus, Range *range);

View File

@ -43,6 +43,7 @@ struct PCIHostState {
uint32_t config_reg;
bool mig_enabled;
PCIBus *bus;
bool bypass_iommu;
QLIST_ENTRY(PCIHostState) next;
};

View File

@ -57,8 +57,11 @@ struct PCIESlot {
/* Disable ACS (really for a pcie_root_port) */
bool disable_acs;
/* Indicates whether hot-plug is enabled on the slot */
/* Indicates whether any type of hot-plug is allowed on the slot */
bool hotplug;
bool native_hotplug;
QLIST_ENTRY(PCIESlot) next;
};

View File

@ -0,0 +1,28 @@
/*
* Vhost-user i2c virtio device
*
* Copyright (c) 2021 Viresh Kumar <viresh.kumar@linaro.org>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef _QEMU_VHOST_USER_I2C_H
#define _QEMU_VHOST_USER_I2C_H
#include "hw/virtio/vhost.h"
#include "hw/virtio/vhost-user.h"
#define TYPE_VHOST_USER_I2C "vhost-user-i2c-device"
OBJECT_DECLARE_SIMPLE_TYPE(VHostUserI2C, VHOST_USER_I2C)
struct VHostUserI2C {
VirtIODevice parent;
CharBackend chardev;
struct vhost_virtqueue *vhost_vq;
struct vhost_dev vhost_dev;
VhostUserState vhost_user;
VirtQueue *vq;
bool connected;
};
#endif /* _QEMU_VHOST_USER_I2C_H */

View File

@ -939,6 +939,39 @@ SRST
``-device pci-ipmi-bt,bmc=id``
Like the KCS interface, but defines a BT interface on the PCI bus.
``-device intel-iommu[,option=...]``
This is only supported by ``-machine q35``, which will enable Intel VT-d
emulation within the guest. It supports below options:
``intremap=on|off`` (default: auto)
This enables interrupt remapping feature. It's required to enable
complete x2apic. Currently it only supports kvm kernel-irqchip modes
``off`` or ``split``, while full kernel-irqchip is not yet supported.
The default value is "auto", which will be decided by the mode of
kernel-irqchip.
``caching-mode=on|off`` (default: off)
This enables caching mode for the VT-d emulated device. When
caching-mode is enabled, each guest DMA buffer mapping will generate an
IOTLB invalidation from the guest IOMMU driver to the vIOMMU device in
a synchronous way. It is required for ``-device vfio-pci`` to work
with the VT-d device, because host assigned devices requires to setup
the DMA mapping on the host before guest DMA starts.
``device-iotlb=on|off`` (default: off)
This enables device-iotlb capability for the emulated VT-d device. So
far virtio/vhost should be the only real user for this parameter,
paired with ats=on configured for the device.
``aw-bits=39|48`` (default: 39)
This decides the address width of IOVA address space. The address
space has 39 bits width for 3-level IOMMU page tables, and 48 bits for
4-level IOMMU page tables.
Please also refer to the wiki page for general scenarios of VT-d
emulation in QEMU: https://wiki.qemu.org/Features/VT-d.
ERST
DEF("name", HAS_ARG, QEMU_OPTION_name,

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.