virtio-iommu: Default to bypass during boot
Currently the virtio-iommu device must be programmed before it allows DMA from any PCI device. This can make the VM entirely unusable when a virtio-iommu driver isn't present, for example in a bootloader that loads the OS from storage. Similarly to the other vIOMMU implementations, default to DMA bypassing the IOMMU during boot. Add a "boot-bypass" property, defaulting to true, that lets users change this behavior. Replace the VIRTIO_IOMMU_F_BYPASS feature, which didn't support bypass before feature negotiation, with VIRTIO_IOMMU_F_BYPASS_CONFIG. We add the bypass field to the migration stream without introducing subsections, based on the assumption that this virtio-iommu device isn't being used in production enough to require cross-version migration at the moment (all previous version required workarounds since they didn't support ACPI and boot-bypass). Reviewed-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org> Message-Id: <20220214124356.872985-3-jean-philippe@linaro.org> Acked-by: Cornelia Huck <cohuck@redhat.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Tested-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
parent
9919423516
commit
448179e33e
@ -89,9 +89,11 @@ virtio_mmio_setting_irq(int level) "virtio_mmio setting IRQ %d"
|
|||||||
|
|
||||||
# virtio-iommu.c
|
# virtio-iommu.c
|
||||||
virtio_iommu_device_reset(void) "reset!"
|
virtio_iommu_device_reset(void) "reset!"
|
||||||
|
virtio_iommu_system_reset(void) "system reset!"
|
||||||
virtio_iommu_get_features(uint64_t features) "device supports features=0x%"PRIx64
|
virtio_iommu_get_features(uint64_t features) "device supports features=0x%"PRIx64
|
||||||
virtio_iommu_device_status(uint8_t status) "driver status = %d"
|
virtio_iommu_device_status(uint8_t status) "driver status = %d"
|
||||||
virtio_iommu_get_config(uint64_t page_size_mask, uint64_t start, uint64_t end, uint32_t domain_start, uint32_t domain_end, uint32_t probe_size) "page_size_mask=0x%"PRIx64" input range start=0x%"PRIx64" input range end=0x%"PRIx64" domain range start=%d domain range end=%d probe_size=0x%x"
|
virtio_iommu_get_config(uint64_t page_size_mask, uint64_t start, uint64_t end, uint32_t domain_start, uint32_t domain_end, uint32_t probe_size, uint8_t bypass) "page_size_mask=0x%"PRIx64" input range start=0x%"PRIx64" input range end=0x%"PRIx64" domain range start=%d domain range end=%d probe_size=0x%x bypass=0x%x"
|
||||||
|
virtio_iommu_set_config(uint8_t bypass) "bypass=0x%x"
|
||||||
virtio_iommu_attach(uint32_t domain_id, uint32_t ep_id) "domain=%d endpoint=%d"
|
virtio_iommu_attach(uint32_t domain_id, uint32_t ep_id) "domain=%d endpoint=%d"
|
||||||
virtio_iommu_detach(uint32_t domain_id, uint32_t ep_id) "domain=%d endpoint=%d"
|
virtio_iommu_detach(uint32_t domain_id, uint32_t ep_id) "domain=%d endpoint=%d"
|
||||||
virtio_iommu_map(uint32_t domain_id, uint64_t virt_start, uint64_t virt_end, uint64_t phys_start, uint32_t flags) "domain=%d virt_start=0x%"PRIx64" virt_end=0x%"PRIx64 " phys_start=0x%"PRIx64" flags=%d"
|
virtio_iommu_map(uint32_t domain_id, uint64_t virt_start, uint64_t virt_end, uint64_t phys_start, uint32_t flags) "domain=%d virt_start=0x%"PRIx64" virt_end=0x%"PRIx64 " phys_start=0x%"PRIx64" flags=%d"
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include "hw/qdev-properties.h"
|
#include "hw/qdev-properties.h"
|
||||||
#include "hw/virtio/virtio.h"
|
#include "hw/virtio/virtio.h"
|
||||||
#include "sysemu/kvm.h"
|
#include "sysemu/kvm.h"
|
||||||
|
#include "sysemu/reset.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
@ -728,8 +729,7 @@ static IOMMUTLBEntry virtio_iommu_translate(IOMMUMemoryRegion *mr, hwaddr addr,
|
|||||||
.perm = IOMMU_NONE,
|
.perm = IOMMU_NONE,
|
||||||
};
|
};
|
||||||
|
|
||||||
bypass_allowed = virtio_vdev_has_feature(&s->parent_obj,
|
bypass_allowed = s->config.bypass;
|
||||||
VIRTIO_IOMMU_F_BYPASS);
|
|
||||||
|
|
||||||
sid = virtio_iommu_get_bdf(sdev);
|
sid = virtio_iommu_get_bdf(sdev);
|
||||||
|
|
||||||
@ -831,13 +831,37 @@ static void virtio_iommu_get_config(VirtIODevice *vdev, uint8_t *config_data)
|
|||||||
out_config->domain_range.start = cpu_to_le32(dev_config->domain_range.start);
|
out_config->domain_range.start = cpu_to_le32(dev_config->domain_range.start);
|
||||||
out_config->domain_range.end = cpu_to_le32(dev_config->domain_range.end);
|
out_config->domain_range.end = cpu_to_le32(dev_config->domain_range.end);
|
||||||
out_config->probe_size = cpu_to_le32(dev_config->probe_size);
|
out_config->probe_size = cpu_to_le32(dev_config->probe_size);
|
||||||
|
out_config->bypass = dev_config->bypass;
|
||||||
|
|
||||||
trace_virtio_iommu_get_config(dev_config->page_size_mask,
|
trace_virtio_iommu_get_config(dev_config->page_size_mask,
|
||||||
dev_config->input_range.start,
|
dev_config->input_range.start,
|
||||||
dev_config->input_range.end,
|
dev_config->input_range.end,
|
||||||
dev_config->domain_range.start,
|
dev_config->domain_range.start,
|
||||||
dev_config->domain_range.end,
|
dev_config->domain_range.end,
|
||||||
dev_config->probe_size);
|
dev_config->probe_size,
|
||||||
|
dev_config->bypass);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_iommu_set_config(VirtIODevice *vdev,
|
||||||
|
const uint8_t *config_data)
|
||||||
|
{
|
||||||
|
VirtIOIOMMU *dev = VIRTIO_IOMMU(vdev);
|
||||||
|
struct virtio_iommu_config *dev_config = &dev->config;
|
||||||
|
const struct virtio_iommu_config *in_config = (void *)config_data;
|
||||||
|
|
||||||
|
if (in_config->bypass != dev_config->bypass) {
|
||||||
|
if (!virtio_vdev_has_feature(vdev, VIRTIO_IOMMU_F_BYPASS_CONFIG)) {
|
||||||
|
virtio_error(vdev, "cannot set config.bypass");
|
||||||
|
return;
|
||||||
|
} else if (in_config->bypass != 0 && in_config->bypass != 1) {
|
||||||
|
virtio_error(vdev, "invalid config.bypass value '%u'",
|
||||||
|
in_config->bypass);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dev_config->bypass = in_config->bypass;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_virtio_iommu_set_config(in_config->bypass);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t virtio_iommu_get_features(VirtIODevice *vdev, uint64_t f,
|
static uint64_t virtio_iommu_get_features(VirtIODevice *vdev, uint64_t f,
|
||||||
@ -963,6 +987,19 @@ static int virtio_iommu_set_page_size_mask(IOMMUMemoryRegion *mr,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void virtio_iommu_system_reset(void *opaque)
|
||||||
|
{
|
||||||
|
VirtIOIOMMU *s = opaque;
|
||||||
|
|
||||||
|
trace_virtio_iommu_system_reset();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* config.bypass is sticky across device reset, but should be restored on
|
||||||
|
* system reset
|
||||||
|
*/
|
||||||
|
s->config.bypass = s->boot_bypass;
|
||||||
|
}
|
||||||
|
|
||||||
static void virtio_iommu_device_realize(DeviceState *dev, Error **errp)
|
static void virtio_iommu_device_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
||||||
@ -988,9 +1025,9 @@ static void virtio_iommu_device_realize(DeviceState *dev, Error **errp)
|
|||||||
virtio_add_feature(&s->features, VIRTIO_IOMMU_F_INPUT_RANGE);
|
virtio_add_feature(&s->features, VIRTIO_IOMMU_F_INPUT_RANGE);
|
||||||
virtio_add_feature(&s->features, VIRTIO_IOMMU_F_DOMAIN_RANGE);
|
virtio_add_feature(&s->features, VIRTIO_IOMMU_F_DOMAIN_RANGE);
|
||||||
virtio_add_feature(&s->features, VIRTIO_IOMMU_F_MAP_UNMAP);
|
virtio_add_feature(&s->features, VIRTIO_IOMMU_F_MAP_UNMAP);
|
||||||
virtio_add_feature(&s->features, VIRTIO_IOMMU_F_BYPASS);
|
|
||||||
virtio_add_feature(&s->features, VIRTIO_IOMMU_F_MMIO);
|
virtio_add_feature(&s->features, VIRTIO_IOMMU_F_MMIO);
|
||||||
virtio_add_feature(&s->features, VIRTIO_IOMMU_F_PROBE);
|
virtio_add_feature(&s->features, VIRTIO_IOMMU_F_PROBE);
|
||||||
|
virtio_add_feature(&s->features, VIRTIO_IOMMU_F_BYPASS_CONFIG);
|
||||||
|
|
||||||
qemu_mutex_init(&s->mutex);
|
qemu_mutex_init(&s->mutex);
|
||||||
|
|
||||||
@ -1001,6 +1038,8 @@ static void virtio_iommu_device_realize(DeviceState *dev, Error **errp)
|
|||||||
} else {
|
} else {
|
||||||
error_setg(errp, "VIRTIO-IOMMU is not attached to any PCI bus!");
|
error_setg(errp, "VIRTIO-IOMMU is not attached to any PCI bus!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qemu_register_reset(virtio_iommu_system_reset, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void virtio_iommu_device_unrealize(DeviceState *dev)
|
static void virtio_iommu_device_unrealize(DeviceState *dev)
|
||||||
@ -1008,6 +1047,8 @@ static void virtio_iommu_device_unrealize(DeviceState *dev)
|
|||||||
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
||||||
VirtIOIOMMU *s = VIRTIO_IOMMU(dev);
|
VirtIOIOMMU *s = VIRTIO_IOMMU(dev);
|
||||||
|
|
||||||
|
qemu_unregister_reset(virtio_iommu_system_reset, s);
|
||||||
|
|
||||||
g_hash_table_destroy(s->as_by_busptr);
|
g_hash_table_destroy(s->as_by_busptr);
|
||||||
if (s->domains) {
|
if (s->domains) {
|
||||||
g_tree_destroy(s->domains);
|
g_tree_destroy(s->domains);
|
||||||
@ -1141,21 +1182,22 @@ static int iommu_post_load(void *opaque, int version_id)
|
|||||||
|
|
||||||
static const VMStateDescription vmstate_virtio_iommu_device = {
|
static const VMStateDescription vmstate_virtio_iommu_device = {
|
||||||
.name = "virtio-iommu-device",
|
.name = "virtio-iommu-device",
|
||||||
.minimum_version_id = 1,
|
.minimum_version_id = 2,
|
||||||
.version_id = 1,
|
.version_id = 2,
|
||||||
.post_load = iommu_post_load,
|
.post_load = iommu_post_load,
|
||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_GTREE_DIRECT_KEY_V(domains, VirtIOIOMMU, 1,
|
VMSTATE_GTREE_DIRECT_KEY_V(domains, VirtIOIOMMU, 1,
|
||||||
&vmstate_domain, VirtIOIOMMUDomain),
|
&vmstate_domain, VirtIOIOMMUDomain),
|
||||||
|
VMSTATE_UINT8_V(config.bypass, VirtIOIOMMU, 2),
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const VMStateDescription vmstate_virtio_iommu = {
|
static const VMStateDescription vmstate_virtio_iommu = {
|
||||||
.name = "virtio-iommu",
|
.name = "virtio-iommu",
|
||||||
.minimum_version_id = 1,
|
.minimum_version_id = 2,
|
||||||
.priority = MIG_PRI_IOMMU,
|
.priority = MIG_PRI_IOMMU,
|
||||||
.version_id = 1,
|
.version_id = 2,
|
||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_VIRTIO_DEVICE,
|
VMSTATE_VIRTIO_DEVICE,
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
@ -1164,6 +1206,7 @@ static const VMStateDescription vmstate_virtio_iommu = {
|
|||||||
|
|
||||||
static Property virtio_iommu_properties[] = {
|
static Property virtio_iommu_properties[] = {
|
||||||
DEFINE_PROP_LINK("primary-bus", VirtIOIOMMU, primary_bus, "PCI", PCIBus *),
|
DEFINE_PROP_LINK("primary-bus", VirtIOIOMMU, primary_bus, "PCI", PCIBus *),
|
||||||
|
DEFINE_PROP_BOOL("boot-bypass", VirtIOIOMMU, boot_bypass, true),
|
||||||
DEFINE_PROP_END_OF_LIST(),
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1180,6 +1223,7 @@ static void virtio_iommu_class_init(ObjectClass *klass, void *data)
|
|||||||
vdc->unrealize = virtio_iommu_device_unrealize;
|
vdc->unrealize = virtio_iommu_device_unrealize;
|
||||||
vdc->reset = virtio_iommu_device_reset;
|
vdc->reset = virtio_iommu_device_reset;
|
||||||
vdc->get_config = virtio_iommu_get_config;
|
vdc->get_config = virtio_iommu_get_config;
|
||||||
|
vdc->set_config = virtio_iommu_set_config;
|
||||||
vdc->get_features = virtio_iommu_get_features;
|
vdc->get_features = virtio_iommu_get_features;
|
||||||
vdc->set_status = virtio_iommu_set_status;
|
vdc->set_status = virtio_iommu_set_status;
|
||||||
vdc->vmsd = &vmstate_virtio_iommu_device;
|
vdc->vmsd = &vmstate_virtio_iommu_device;
|
||||||
|
@ -58,6 +58,7 @@ struct VirtIOIOMMU {
|
|||||||
GTree *domains;
|
GTree *domains;
|
||||||
QemuMutex mutex;
|
QemuMutex mutex;
|
||||||
GTree *endpoints;
|
GTree *endpoints;
|
||||||
|
bool boot_bypass;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user