mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge tag 'for_upstream' of https://git.kernel.org/pub/scm/virt/kvm/mst/qemu into staging
virtio,pci,pc: features, fixes, tests vhost will now no longer set a call notifier if unused some work towards loongarch testing based on bios-tables-test some core pci work for SVM support in vtd vhost vdpa init has been optimized for response time to QMP A couple more fixes Signed-off-by: Michael S. Tsirkin <mst@redhat.com> # -----BEGIN PGP SIGNATURE----- # # iQFDBAABCgAtFiEEXQn9CHHI+FuUyooNKB8NuNKNVGkFAmg97ZUPHG1zdEByZWRo # YXQuY29tAAoJECgfDbjSjVRpRBsH/0Fx4NNMaynXmVOgV1rMFirTydhQG5NSdeJv # i1RHd25Rne/RXH0CL71UPuOPADWh6bv9iZTg6RU6g7TwI8K9v3M0R71RlPLh1Lh1 # x7fifWNSNXVi18fM9/j+mIg7I2Ye0AaqveezRJWGzqoOxQKKlVI2xspKZBCCkygd # i2tgtR1ORB6+ji6wVoTDPlL42X5Jef5MUT3XOcRR5biHm0JfqxxQKVM83mD+5yMI # 0YqjT2BVRzo5rGN7mSuf7tQ50xI6I0wI1+eoWeKHRbg08f709M8TZRDKuVh24Evg # 9WnIhKLTzRVdCNLNbw9h9EhxoANpWCyvmnn6GCfkJui40necFHY= # =0lO6 # -----END PGP SIGNATURE----- # gpg: Signature made Mon 02 Jun 2025 14:29:41 EDT # 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 * tag 'for_upstream' of https://git.kernel.org/pub/scm/virt/kvm/mst/qemu: (26 commits) hw/i386/pc_piix: Fix RTC ISA IRQ wiring of isapc machine vdpa: move memory listener register to vhost_vdpa_init vdpa: move iova_tree allocation to net_vhost_vdpa_init vdpa: reorder listener assignment vdpa: add listener_registered vdpa: set backend capabilities at vhost_vdpa_init vdpa: reorder vhost_vdpa_set_backend_cap vdpa: check for iova tree initialized at net_client_start vhost: Don't set vring call if guest notifier is unused tests/qtest/bios-tables-test: Use MiB macro rather hardcode value tests/data/uefi-boot-images: Add ISO image for LoongArch system uefi-test-tools:: Add LoongArch64 support pci: Add a PCI-level API for PRI pci: Add a pci-level API for ATS pci: Add a pci-level initialization function for IOMMU notifiers memory: Store user data pointer in the IOMMU notifiers pci: Add an API to get IOMMU's min page size and virtual address width pci: Cache the bus mastering status in the device pcie: Helper functions to check to check if PRI is enabled pcie: Add a helper to declare the PRI capability for a pcie device ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
+10
-10
@@ -1426,7 +1426,6 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn)
|
||||
AMDVIState *s = opaque;
|
||||
AMDVIAddressSpace **iommu_as, *amdvi_dev_as;
|
||||
int bus_num = pci_bus_num(bus);
|
||||
X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s);
|
||||
|
||||
iommu_as = s->address_spaces[bus_num];
|
||||
|
||||
@@ -1486,15 +1485,8 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn)
|
||||
AMDVI_INT_ADDR_FIRST,
|
||||
&amdvi_dev_as->iommu_ir, 1);
|
||||
|
||||
if (!x86_iommu->pt_supported) {
|
||||
memory_region_set_enabled(&amdvi_dev_as->iommu_nodma, false);
|
||||
memory_region_set_enabled(MEMORY_REGION(&amdvi_dev_as->iommu),
|
||||
true);
|
||||
} else {
|
||||
memory_region_set_enabled(MEMORY_REGION(&amdvi_dev_as->iommu),
|
||||
false);
|
||||
memory_region_set_enabled(&amdvi_dev_as->iommu_nodma, true);
|
||||
}
|
||||
memory_region_set_enabled(&amdvi_dev_as->iommu_nodma, false);
|
||||
memory_region_set_enabled(MEMORY_REGION(&amdvi_dev_as->iommu), true);
|
||||
}
|
||||
return &iommu_as[devfn]->as;
|
||||
}
|
||||
@@ -1723,6 +1715,14 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp)
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (s->xtsup) {
|
||||
if (kvm_irqchip_is_split() && !kvm_enable_x2apic()) {
|
||||
error_report("AMD IOMMU xtsup=on requires x2APIC support on "
|
||||
"the KVM side");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
pci_setup_iommu(bus, &amdvi_iommu_ops, s);
|
||||
amdvi_init(s);
|
||||
}
|
||||
|
||||
@@ -285,6 +285,8 @@ static void pc_init1(MachineState *machine, const char *pci_type)
|
||||
pcms->idebus[0] = qdev_get_child_bus(dev, "ide.0");
|
||||
pcms->idebus[1] = qdev_get_child_bus(dev, "ide.1");
|
||||
} else {
|
||||
uint32_t irq;
|
||||
|
||||
isa_bus = isa_bus_new(NULL, system_memory, system_io,
|
||||
&error_abort);
|
||||
isa_bus_register_input_irqs(isa_bus, x86ms->gsi);
|
||||
@@ -292,6 +294,9 @@ static void pc_init1(MachineState *machine, const char *pci_type)
|
||||
x86ms->rtc = isa_new(TYPE_MC146818_RTC);
|
||||
qdev_prop_set_int32(DEVICE(x86ms->rtc), "base_year", 2000);
|
||||
isa_realize_and_unref(x86ms->rtc, isa_bus, &error_fatal);
|
||||
irq = object_property_get_uint(OBJECT(x86ms->rtc), "irq",
|
||||
&error_fatal);
|
||||
isa_connect_gpio_out(ISA_DEVICE(x86ms->rtc), 0, irq);
|
||||
|
||||
i8257_dma_init(OBJECT(machine), isa_bus, 0);
|
||||
pcms->hpet_enabled = false;
|
||||
|
||||
+195
-11
@@ -128,6 +128,12 @@ static GSequence *pci_acpi_index_list(void)
|
||||
return used_acpi_index_list;
|
||||
}
|
||||
|
||||
static void pci_set_master(PCIDevice *d, bool enable)
|
||||
{
|
||||
memory_region_set_enabled(&d->bus_master_enable_region, enable);
|
||||
d->is_master = enable; /* cache the status */
|
||||
}
|
||||
|
||||
static void pci_init_bus_master(PCIDevice *pci_dev)
|
||||
{
|
||||
AddressSpace *dma_as = pci_device_iommu_address_space(pci_dev);
|
||||
@@ -135,7 +141,7 @@ static void pci_init_bus_master(PCIDevice *pci_dev)
|
||||
memory_region_init_alias(&pci_dev->bus_master_enable_region,
|
||||
OBJECT(pci_dev), "bus master",
|
||||
dma_as->root, 0, memory_region_size(dma_as->root));
|
||||
memory_region_set_enabled(&pci_dev->bus_master_enable_region, false);
|
||||
pci_set_master(pci_dev, false);
|
||||
memory_region_add_subregion(&pci_dev->bus_master_container_region, 0,
|
||||
&pci_dev->bus_master_enable_region);
|
||||
}
|
||||
@@ -804,9 +810,8 @@ static int get_pci_config_device(QEMUFile *f, void *pv, size_t size,
|
||||
pci_bridge_update_mappings(PCI_BRIDGE(s));
|
||||
}
|
||||
|
||||
memory_region_set_enabled(&s->bus_master_enable_region,
|
||||
pci_get_word(s->config + PCI_COMMAND)
|
||||
& PCI_COMMAND_MASTER);
|
||||
pci_set_master(s, pci_get_word(s->config + PCI_COMMAND)
|
||||
& PCI_COMMAND_MASTER);
|
||||
|
||||
g_free(config);
|
||||
return 0;
|
||||
@@ -1725,7 +1730,7 @@ static void pci_update_mappings(PCIDevice *d)
|
||||
pci_update_vga(d);
|
||||
}
|
||||
|
||||
static inline int pci_irq_disabled(PCIDevice *d)
|
||||
int pci_irq_disabled(PCIDevice *d)
|
||||
{
|
||||
return pci_get_word(d->config + PCI_COMMAND) & PCI_COMMAND_INTX_DISABLE;
|
||||
}
|
||||
@@ -1787,9 +1792,8 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val_in, int
|
||||
|
||||
if (ranges_overlap(addr, l, PCI_COMMAND, 2)) {
|
||||
pci_update_irq_disabled(d, was_irq_disabled);
|
||||
memory_region_set_enabled(&d->bus_master_enable_region,
|
||||
(pci_get_word(d->config + PCI_COMMAND)
|
||||
& PCI_COMMAND_MASTER) && d->enabled);
|
||||
pci_set_master(d, (pci_get_word(d->config + PCI_COMMAND) &
|
||||
PCI_COMMAND_MASTER) && d->enabled);
|
||||
}
|
||||
|
||||
msi_write_config(d, addr, val_in, l);
|
||||
@@ -2935,6 +2939,23 @@ AddressSpace *pci_device_iommu_address_space(PCIDevice *dev)
|
||||
return &address_space_memory;
|
||||
}
|
||||
|
||||
int pci_iommu_init_iotlb_notifier(PCIDevice *dev, IOMMUNotifier *n,
|
||||
IOMMUNotify fn, void *opaque)
|
||||
{
|
||||
PCIBus *bus;
|
||||
PCIBus *iommu_bus;
|
||||
int devfn;
|
||||
|
||||
pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn);
|
||||
if (iommu_bus && iommu_bus->iommu_ops->init_iotlb_notifier) {
|
||||
iommu_bus->iommu_ops->init_iotlb_notifier(bus, iommu_bus->iommu_opaque,
|
||||
devfn, n, fn, opaque);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
bool pci_device_set_iommu_device(PCIDevice *dev, HostIOMMUDevice *hiod,
|
||||
Error **errp)
|
||||
{
|
||||
@@ -2966,6 +2987,170 @@ void pci_device_unset_iommu_device(PCIDevice *dev)
|
||||
}
|
||||
}
|
||||
|
||||
int pci_pri_request_page(PCIDevice *dev, uint32_t pasid, bool priv_req,
|
||||
bool exec_req, hwaddr addr, bool lpig,
|
||||
uint16_t prgi, bool is_read, bool is_write)
|
||||
{
|
||||
PCIBus *bus;
|
||||
PCIBus *iommu_bus;
|
||||
int devfn;
|
||||
|
||||
if (!dev->is_master ||
|
||||
((pasid != PCI_NO_PASID) && !pcie_pasid_enabled(dev))) {
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (!pcie_pri_enabled(dev)) {
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn);
|
||||
if (iommu_bus && iommu_bus->iommu_ops->pri_request_page) {
|
||||
return iommu_bus->iommu_ops->pri_request_page(bus,
|
||||
iommu_bus->iommu_opaque,
|
||||
devfn, pasid, priv_req,
|
||||
exec_req, addr, lpig, prgi,
|
||||
is_read, is_write);
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int pci_pri_register_notifier(PCIDevice *dev, uint32_t pasid,
|
||||
IOMMUPRINotifier *notifier)
|
||||
{
|
||||
PCIBus *bus;
|
||||
PCIBus *iommu_bus;
|
||||
int devfn;
|
||||
|
||||
if (!dev->is_master ||
|
||||
((pasid != PCI_NO_PASID) && !pcie_pasid_enabled(dev))) {
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn);
|
||||
if (iommu_bus && iommu_bus->iommu_ops->pri_register_notifier) {
|
||||
iommu_bus->iommu_ops->pri_register_notifier(bus,
|
||||
iommu_bus->iommu_opaque,
|
||||
devfn, pasid, notifier);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
void pci_pri_unregister_notifier(PCIDevice *dev, uint32_t pasid)
|
||||
{
|
||||
PCIBus *bus;
|
||||
PCIBus *iommu_bus;
|
||||
int devfn;
|
||||
|
||||
pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn);
|
||||
if (iommu_bus && iommu_bus->iommu_ops->pri_unregister_notifier) {
|
||||
iommu_bus->iommu_ops->pri_unregister_notifier(bus,
|
||||
iommu_bus->iommu_opaque,
|
||||
devfn, pasid);
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t pci_ats_request_translation(PCIDevice *dev, uint32_t pasid,
|
||||
bool priv_req, bool exec_req,
|
||||
hwaddr addr, size_t length,
|
||||
bool no_write, IOMMUTLBEntry *result,
|
||||
size_t result_length,
|
||||
uint32_t *err_count)
|
||||
{
|
||||
PCIBus *bus;
|
||||
PCIBus *iommu_bus;
|
||||
int devfn;
|
||||
|
||||
if (!dev->is_master ||
|
||||
((pasid != PCI_NO_PASID) && !pcie_pasid_enabled(dev))) {
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (result_length == 0) {
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
if (!pcie_ats_enabled(dev)) {
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn);
|
||||
if (iommu_bus && iommu_bus->iommu_ops->ats_request_translation) {
|
||||
return iommu_bus->iommu_ops->ats_request_translation(bus,
|
||||
iommu_bus->iommu_opaque,
|
||||
devfn, pasid, priv_req,
|
||||
exec_req, addr, length,
|
||||
no_write, result,
|
||||
result_length, err_count);
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int pci_iommu_register_iotlb_notifier(PCIDevice *dev, uint32_t pasid,
|
||||
IOMMUNotifier *n)
|
||||
{
|
||||
PCIBus *bus;
|
||||
PCIBus *iommu_bus;
|
||||
int devfn;
|
||||
|
||||
if ((pasid != PCI_NO_PASID) && !pcie_pasid_enabled(dev)) {
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn);
|
||||
if (iommu_bus && iommu_bus->iommu_ops->register_iotlb_notifier) {
|
||||
iommu_bus->iommu_ops->register_iotlb_notifier(bus,
|
||||
iommu_bus->iommu_opaque, devfn,
|
||||
pasid, n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int pci_iommu_unregister_iotlb_notifier(PCIDevice *dev, uint32_t pasid,
|
||||
IOMMUNotifier *n)
|
||||
{
|
||||
PCIBus *bus;
|
||||
PCIBus *iommu_bus;
|
||||
int devfn;
|
||||
|
||||
if ((pasid != PCI_NO_PASID) && !pcie_pasid_enabled(dev)) {
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn);
|
||||
if (iommu_bus && iommu_bus->iommu_ops->unregister_iotlb_notifier) {
|
||||
iommu_bus->iommu_ops->unregister_iotlb_notifier(bus,
|
||||
iommu_bus->iommu_opaque,
|
||||
devfn, pasid, n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int pci_iommu_get_iotlb_info(PCIDevice *dev, uint8_t *addr_width,
|
||||
uint32_t *min_page_size)
|
||||
{
|
||||
PCIBus *bus;
|
||||
PCIBus *iommu_bus;
|
||||
int devfn;
|
||||
|
||||
pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn);
|
||||
if (iommu_bus && iommu_bus->iommu_ops->get_iotlb_info) {
|
||||
iommu_bus->iommu_ops->get_iotlb_info(iommu_bus->iommu_opaque,
|
||||
addr_width, min_page_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
void pci_setup_iommu(PCIBus *bus, const PCIIOMMUOps *ops, void *opaque)
|
||||
{
|
||||
/*
|
||||
@@ -3100,9 +3285,8 @@ void pci_set_enabled(PCIDevice *d, bool state)
|
||||
|
||||
d->enabled = state;
|
||||
pci_update_mappings(d);
|
||||
memory_region_set_enabled(&d->bus_master_enable_region,
|
||||
(pci_get_word(d->config + PCI_COMMAND)
|
||||
& PCI_COMMAND_MASTER) && d->enabled);
|
||||
pci_set_master(d, (pci_get_word(d->config + PCI_COMMAND)
|
||||
& PCI_COMMAND_MASTER) && d->enabled);
|
||||
if (qdev_is_realized(&d->qdev)) {
|
||||
pci_device_reset(d);
|
||||
}
|
||||
|
||||
@@ -1214,3 +1214,81 @@ void pcie_acs_reset(PCIDevice *dev)
|
||||
pci_set_word(dev->config + dev->exp.acs_cap + PCI_ACS_CTRL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* PASID */
|
||||
void pcie_pasid_init(PCIDevice *dev, uint16_t offset, uint8_t pasid_width,
|
||||
bool exec_perm, bool priv_mod)
|
||||
{
|
||||
static const uint16_t control_reg_rw_mask = 0x07;
|
||||
uint16_t capability_reg;
|
||||
|
||||
assert(pasid_width <= PCI_EXT_CAP_PASID_MAX_WIDTH);
|
||||
|
||||
pcie_add_capability(dev, PCI_EXT_CAP_ID_PASID, PCI_PASID_VER, offset,
|
||||
PCI_EXT_CAP_PASID_SIZEOF);
|
||||
|
||||
capability_reg = ((uint16_t)pasid_width) << PCI_PASID_CAP_WIDTH_SHIFT;
|
||||
capability_reg |= exec_perm ? PCI_PASID_CAP_EXEC : 0;
|
||||
capability_reg |= priv_mod ? PCI_PASID_CAP_PRIV : 0;
|
||||
pci_set_word(dev->config + offset + PCI_PASID_CAP, capability_reg);
|
||||
|
||||
/* Everything is disabled by default */
|
||||
pci_set_word(dev->config + offset + PCI_PASID_CTRL, 0);
|
||||
|
||||
pci_set_word(dev->wmask + offset + PCI_PASID_CTRL, control_reg_rw_mask);
|
||||
|
||||
dev->exp.pasid_cap = offset;
|
||||
}
|
||||
|
||||
/* PRI */
|
||||
void pcie_pri_init(PCIDevice *dev, uint16_t offset, uint32_t outstanding_pr_cap,
|
||||
bool prg_response_pasid_req)
|
||||
{
|
||||
static const uint16_t control_reg_rw_mask = 0x3;
|
||||
static const uint16_t status_reg_rw1_mask = 0x3;
|
||||
static const uint32_t pr_alloc_reg_rw_mask = 0xffffffff;
|
||||
uint16_t status_reg;
|
||||
|
||||
status_reg = prg_response_pasid_req ? PCI_PRI_STATUS_PASID : 0;
|
||||
status_reg |= PCI_PRI_STATUS_STOPPED; /* Stopped by default */
|
||||
|
||||
pcie_add_capability(dev, PCI_EXT_CAP_ID_PRI, PCI_PRI_VER, offset,
|
||||
PCI_EXT_CAP_PRI_SIZEOF);
|
||||
/* Disabled by default */
|
||||
|
||||
pci_set_word(dev->config + offset + PCI_PRI_STATUS, status_reg);
|
||||
pci_set_long(dev->config + offset + PCI_PRI_MAX_REQ, outstanding_pr_cap);
|
||||
|
||||
pci_set_word(dev->wmask + offset + PCI_PRI_CTRL, control_reg_rw_mask);
|
||||
pci_set_word(dev->w1cmask + offset + PCI_PRI_STATUS, status_reg_rw1_mask);
|
||||
pci_set_long(dev->wmask + offset + PCI_PRI_ALLOC_REQ, pr_alloc_reg_rw_mask);
|
||||
|
||||
dev->exp.pri_cap = offset;
|
||||
}
|
||||
|
||||
bool pcie_pri_enabled(const PCIDevice *dev)
|
||||
{
|
||||
if (!pci_is_express(dev) || !dev->exp.pri_cap) {
|
||||
return false;
|
||||
}
|
||||
return (pci_get_word(dev->config + dev->exp.pri_cap + PCI_PRI_CTRL) &
|
||||
PCI_PRI_CTRL_ENABLE) != 0;
|
||||
}
|
||||
|
||||
bool pcie_pasid_enabled(const PCIDevice *dev)
|
||||
{
|
||||
if (!pci_is_express(dev) || !dev->exp.pasid_cap) {
|
||||
return false;
|
||||
}
|
||||
return (pci_get_word(dev->config + dev->exp.pasid_cap + PCI_PASID_CTRL) &
|
||||
PCI_PASID_CTRL_ENABLE) != 0;
|
||||
}
|
||||
|
||||
bool pcie_ats_enabled(const PCIDevice *dev)
|
||||
{
|
||||
if (!pci_is_express(dev) || !dev->exp.ats_cap) {
|
||||
return false;
|
||||
}
|
||||
return (pci_get_word(dev->config + dev->exp.ats_cap + PCI_ATS_CTRL) &
|
||||
PCI_ATS_CTRL_ENABLE) != 0;
|
||||
}
|
||||
|
||||
+69
-38
@@ -594,6 +594,36 @@ static void vhost_vdpa_init_svq(struct vhost_dev *hdev, struct vhost_vdpa *v)
|
||||
v->shadow_vqs = g_steal_pointer(&shadow_vqs);
|
||||
}
|
||||
|
||||
static int vhost_vdpa_set_backend_cap(struct vhost_dev *dev)
|
||||
{
|
||||
struct vhost_vdpa *v = dev->opaque;
|
||||
|
||||
uint64_t features;
|
||||
uint64_t f = 0x1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2 |
|
||||
0x1ULL << VHOST_BACKEND_F_IOTLB_BATCH |
|
||||
0x1ULL << VHOST_BACKEND_F_IOTLB_ASID |
|
||||
0x1ULL << VHOST_BACKEND_F_SUSPEND;
|
||||
int r;
|
||||
|
||||
if (vhost_vdpa_call(dev, VHOST_GET_BACKEND_FEATURES, &features)) {
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
features &= f;
|
||||
|
||||
if (vhost_vdpa_first_dev(dev)) {
|
||||
r = vhost_vdpa_call(dev, VHOST_SET_BACKEND_FEATURES, &features);
|
||||
if (r) {
|
||||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
dev->backend_cap = features;
|
||||
v->shared->backend_cap = features;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp)
|
||||
{
|
||||
struct vhost_vdpa *v = opaque;
|
||||
@@ -603,7 +633,12 @@ static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp)
|
||||
|
||||
v->dev = dev;
|
||||
dev->opaque = opaque ;
|
||||
v->shared->listener = vhost_vdpa_memory_listener;
|
||||
|
||||
ret = vhost_vdpa_set_backend_cap(dev);
|
||||
if (unlikely(ret != 0)) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
vhost_vdpa_init_svq(dev, v);
|
||||
|
||||
error_propagate(&dev->migration_blocker, v->migration_blocker);
|
||||
@@ -639,6 +674,7 @@ static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp)
|
||||
vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE |
|
||||
VIRTIO_CONFIG_S_DRIVER);
|
||||
|
||||
v->shared->listener = vhost_vdpa_memory_listener;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -841,36 +877,6 @@ static int vhost_vdpa_set_features(struct vhost_dev *dev,
|
||||
return vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_FEATURES_OK);
|
||||
}
|
||||
|
||||
static int vhost_vdpa_set_backend_cap(struct vhost_dev *dev)
|
||||
{
|
||||
struct vhost_vdpa *v = dev->opaque;
|
||||
|
||||
uint64_t features;
|
||||
uint64_t f = 0x1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2 |
|
||||
0x1ULL << VHOST_BACKEND_F_IOTLB_BATCH |
|
||||
0x1ULL << VHOST_BACKEND_F_IOTLB_ASID |
|
||||
0x1ULL << VHOST_BACKEND_F_SUSPEND;
|
||||
int r;
|
||||
|
||||
if (vhost_vdpa_call(dev, VHOST_GET_BACKEND_FEATURES, &features)) {
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
features &= f;
|
||||
|
||||
if (vhost_vdpa_first_dev(dev)) {
|
||||
r = vhost_vdpa_call(dev, VHOST_SET_BACKEND_FEATURES, &features);
|
||||
if (r) {
|
||||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
dev->backend_cap = features;
|
||||
v->shared->backend_cap = features;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vhost_vdpa_get_device_id(struct vhost_dev *dev,
|
||||
uint32_t *device_id)
|
||||
{
|
||||
@@ -888,8 +894,14 @@ static int vhost_vdpa_reset_device(struct vhost_dev *dev)
|
||||
|
||||
ret = vhost_vdpa_call(dev, VHOST_VDPA_SET_STATUS, &status);
|
||||
trace_vhost_vdpa_reset_device(dev);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
memory_listener_unregister(&v->shared->listener);
|
||||
v->shared->listener_registered = false;
|
||||
v->suspended = false;
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vhost_vdpa_get_vq_index(struct vhost_dev *dev, int idx)
|
||||
@@ -1373,7 +1385,15 @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started)
|
||||
"IOMMU and try again");
|
||||
return -1;
|
||||
}
|
||||
memory_listener_register(&v->shared->listener, dev->vdev->dma_as);
|
||||
if (v->shared->listener_registered &&
|
||||
dev->vdev->dma_as != v->shared->listener.address_space) {
|
||||
memory_listener_unregister(&v->shared->listener);
|
||||
v->shared->listener_registered = false;
|
||||
}
|
||||
if (!v->shared->listener_registered) {
|
||||
memory_listener_register(&v->shared->listener, dev->vdev->dma_as);
|
||||
v->shared->listener_registered = true;
|
||||
}
|
||||
|
||||
return vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK);
|
||||
}
|
||||
@@ -1383,8 +1403,6 @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started)
|
||||
|
||||
static void vhost_vdpa_reset_status(struct vhost_dev *dev)
|
||||
{
|
||||
struct vhost_vdpa *v = dev->opaque;
|
||||
|
||||
if (!vhost_vdpa_last_dev(dev)) {
|
||||
return;
|
||||
}
|
||||
@@ -1392,7 +1410,6 @@ static void vhost_vdpa_reset_status(struct vhost_dev *dev)
|
||||
vhost_vdpa_reset_device(dev);
|
||||
vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE |
|
||||
VIRTIO_CONFIG_S_DRIVER);
|
||||
memory_listener_unregister(&v->shared->listener);
|
||||
}
|
||||
|
||||
static int vhost_vdpa_set_log_base(struct vhost_dev *dev, uint64_t base,
|
||||
@@ -1526,12 +1543,27 @@ static int vhost_vdpa_get_features(struct vhost_dev *dev,
|
||||
|
||||
static int vhost_vdpa_set_owner(struct vhost_dev *dev)
|
||||
{
|
||||
int r;
|
||||
struct vhost_vdpa *v;
|
||||
|
||||
if (!vhost_vdpa_first_dev(dev)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
trace_vhost_vdpa_set_owner(dev);
|
||||
return vhost_vdpa_call(dev, VHOST_SET_OWNER, NULL);
|
||||
r = vhost_vdpa_call(dev, VHOST_SET_OWNER, NULL);
|
||||
if (unlikely(r < 0)) {
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Being optimistic and listening address space memory. If the device
|
||||
* uses vIOMMU, it is changed at vhost_vdpa_dev_start.
|
||||
*/
|
||||
v = dev->opaque;
|
||||
memory_listener_register(&v->shared->listener, &address_space_memory);
|
||||
v->shared->listener_registered = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vhost_vdpa_vq_get_addr(struct vhost_dev *dev,
|
||||
@@ -1563,7 +1595,6 @@ const VhostOps vdpa_ops = {
|
||||
.vhost_set_vring_kick = vhost_vdpa_set_vring_kick,
|
||||
.vhost_set_vring_call = vhost_vdpa_set_vring_call,
|
||||
.vhost_get_features = vhost_vdpa_get_features,
|
||||
.vhost_set_backend_cap = vhost_vdpa_set_backend_cap,
|
||||
.vhost_set_owner = vhost_vdpa_set_owner,
|
||||
.vhost_set_vring_endian = NULL,
|
||||
.vhost_backend_memslots_limit = vhost_vdpa_memslots_limit,
|
||||
|
||||
@@ -1213,7 +1213,12 @@ static int virtio_pci_set_guest_notifier(DeviceState *d, int n, bool assign,
|
||||
static bool virtio_pci_query_guest_notifiers(DeviceState *d)
|
||||
{
|
||||
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
|
||||
return msix_enabled(&proxy->pci_dev);
|
||||
|
||||
if (msix_enabled(&proxy->pci_dev)) {
|
||||
return true;
|
||||
} else {
|
||||
return pci_irq_disabled(&proxy->pci_dev);
|
||||
}
|
||||
}
|
||||
|
||||
static int virtio_pci_set_guest_notifiers(DeviceState *d, int nvqs, bool assign)
|
||||
|
||||
@@ -205,6 +205,15 @@ static const char *virtio_id_to_name(uint16_t device_id)
|
||||
return name;
|
||||
}
|
||||
|
||||
static void virtio_check_indirect_feature(VirtIODevice *vdev)
|
||||
{
|
||||
if (!virtio_vdev_has_feature(vdev, VIRTIO_RING_F_INDIRECT_DESC)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"Device %s: indirect_desc was not negotiated!\n",
|
||||
vdev->name);
|
||||
}
|
||||
}
|
||||
|
||||
/* Called within call_rcu(). */
|
||||
static void virtio_free_region_cache(VRingMemoryRegionCaches *caches)
|
||||
{
|
||||
@@ -1733,6 +1742,7 @@ static void *virtqueue_split_pop(VirtQueue *vq, size_t sz)
|
||||
virtio_error(vdev, "Invalid size for indirect buffer table");
|
||||
goto done;
|
||||
}
|
||||
virtio_check_indirect_feature(vdev);
|
||||
|
||||
/* loop over the indirect descriptor table */
|
||||
len = address_space_cache_init(&indirect_desc_cache, vdev->dma_as,
|
||||
@@ -1870,6 +1880,7 @@ static void *virtqueue_packed_pop(VirtQueue *vq, size_t sz)
|
||||
virtio_error(vdev, "Invalid size for indirect buffer table");
|
||||
goto done;
|
||||
}
|
||||
virtio_check_indirect_feature(vdev);
|
||||
|
||||
/* loop over the indirect descriptor table */
|
||||
len = address_space_cache_init(&indirect_desc_cache, vdev->dma_as,
|
||||
|
||||
@@ -375,6 +375,28 @@ void pci_bus_get_w64_range(PCIBus *bus, Range *range);
|
||||
|
||||
void pci_device_deassert_intx(PCIDevice *dev);
|
||||
|
||||
/* Page Request Interface */
|
||||
typedef enum {
|
||||
IOMMU_PRI_RESP_SUCCESS,
|
||||
IOMMU_PRI_RESP_INVALID_REQUEST,
|
||||
IOMMU_PRI_RESP_FAILURE,
|
||||
} IOMMUPRIResponseCode;
|
||||
|
||||
typedef struct IOMMUPRIResponse {
|
||||
IOMMUPRIResponseCode response_code;
|
||||
uint16_t prgi;
|
||||
} IOMMUPRIResponse;
|
||||
|
||||
struct IOMMUPRINotifier;
|
||||
|
||||
typedef void (*IOMMUPRINotify)(struct IOMMUPRINotifier *notifier,
|
||||
IOMMUPRIResponse *response);
|
||||
|
||||
typedef struct IOMMUPRINotifier {
|
||||
IOMMUPRINotify notify;
|
||||
} IOMMUPRINotifier;
|
||||
|
||||
#define PCI_PRI_PRGI_MASK 0x1ffU
|
||||
|
||||
/**
|
||||
* struct PCIIOMMUOps: callbacks structure for specific IOMMU handlers
|
||||
@@ -429,6 +451,179 @@ typedef struct PCIIOMMUOps {
|
||||
* @devfn: device and function number of the PCI device.
|
||||
*/
|
||||
void (*unset_iommu_device)(PCIBus *bus, void *opaque, int devfn);
|
||||
/**
|
||||
* @get_iotlb_info: get properties required to initialize a device IOTLB.
|
||||
*
|
||||
* Callback required if devices are allowed to cache translations.
|
||||
*
|
||||
* @opaque: the data passed to pci_setup_iommu().
|
||||
*
|
||||
* @addr_width: the address width of the IOMMU (output parameter).
|
||||
*
|
||||
* @min_page_size: the page size of the IOMMU (output parameter).
|
||||
*/
|
||||
void (*get_iotlb_info)(void *opaque, uint8_t *addr_width,
|
||||
uint32_t *min_page_size);
|
||||
/**
|
||||
* @init_iotlb_notifier: initialize an IOMMU notifier.
|
||||
*
|
||||
* Optional callback.
|
||||
*
|
||||
* @bus: the #PCIBus of the PCI device.
|
||||
*
|
||||
* @opaque: the data passed to pci_setup_iommu().
|
||||
*
|
||||
* @devfn: device and function number of the PCI device.
|
||||
*
|
||||
* @n: the notifier to be initialized.
|
||||
*
|
||||
* @fn: the callback to be installed.
|
||||
*
|
||||
* @user_opaque: a user pointer that can be used to track a state.
|
||||
*/
|
||||
void (*init_iotlb_notifier)(PCIBus *bus, void *opaque, int devfn,
|
||||
IOMMUNotifier *n, IOMMUNotify fn,
|
||||
void *user_opaque);
|
||||
/**
|
||||
* @register_iotlb_notifier: setup an IOTLB invalidation notifier.
|
||||
*
|
||||
* Callback required if devices are allowed to cache translations.
|
||||
*
|
||||
* @bus: the #PCIBus of the PCI device.
|
||||
*
|
||||
* @opaque: the data passed to pci_setup_iommu().
|
||||
*
|
||||
* @devfn: device and function number of the PCI device.
|
||||
*
|
||||
* @pasid: the pasid of the address space to watch.
|
||||
*
|
||||
* @n: the notifier to register.
|
||||
*/
|
||||
void (*register_iotlb_notifier)(PCIBus *bus, void *opaque, int devfn,
|
||||
uint32_t pasid, IOMMUNotifier *n);
|
||||
/**
|
||||
* @unregister_iotlb_notifier: remove an IOTLB invalidation notifier.
|
||||
*
|
||||
* Callback required if devices are allowed to cache translations.
|
||||
*
|
||||
* @bus: the #PCIBus of the PCI device.
|
||||
*
|
||||
* @opaque: the data passed to pci_setup_iommu().
|
||||
*
|
||||
* @devfn: device and function number of the PCI device.
|
||||
*
|
||||
* @pasid: the pasid of the address space to stop watching.
|
||||
*
|
||||
* @n: the notifier to unregister.
|
||||
*/
|
||||
void (*unregister_iotlb_notifier)(PCIBus *bus, void *opaque, int devfn,
|
||||
uint32_t pasid, IOMMUNotifier *n);
|
||||
/**
|
||||
* @ats_request_translation: issue an ATS request.
|
||||
*
|
||||
* Callback required if devices are allowed to use the address
|
||||
* translation service.
|
||||
*
|
||||
* @bus: the #PCIBus of the PCI device.
|
||||
*
|
||||
* @opaque: the data passed to pci_setup_iommu().
|
||||
*
|
||||
* @devfn: device and function number of the PCI device.
|
||||
*
|
||||
* @pasid: the pasid of the address space to use for the request.
|
||||
*
|
||||
* @priv_req: privileged mode bit (PASID TLP).
|
||||
*
|
||||
* @exec_req: execute request bit (PASID TLP).
|
||||
*
|
||||
* @addr: start address of the memory range to be translated.
|
||||
*
|
||||
* @length: length of the memory range in bytes.
|
||||
*
|
||||
* @no_write: request a read-only translation (if supported).
|
||||
*
|
||||
* @result: buffer in which the TLB entries will be stored.
|
||||
*
|
||||
* @result_length: result buffer length.
|
||||
*
|
||||
* @err_count: number of untranslated subregions.
|
||||
*
|
||||
* Returns: the number of translations stored in the result buffer, or
|
||||
* -ENOMEM if the buffer is not large enough.
|
||||
*/
|
||||
ssize_t (*ats_request_translation)(PCIBus *bus, void *opaque, int devfn,
|
||||
uint32_t pasid, bool priv_req,
|
||||
bool exec_req, hwaddr addr,
|
||||
size_t length, bool no_write,
|
||||
IOMMUTLBEntry *result,
|
||||
size_t result_length,
|
||||
uint32_t *err_count);
|
||||
/**
|
||||
* @pri_register_notifier: setup the PRI completion callback.
|
||||
*
|
||||
* Callback required if devices are allowed to use the page request
|
||||
* interface.
|
||||
*
|
||||
* @bus: the #PCIBus of the PCI device.
|
||||
*
|
||||
* @opaque: the data passed to pci_setup_iommu().
|
||||
*
|
||||
* @devfn: device and function number of the PCI device.
|
||||
*
|
||||
* @pasid: the pasid of the address space to track.
|
||||
*
|
||||
* @notifier: the notifier to register.
|
||||
*/
|
||||
void (*pri_register_notifier)(PCIBus *bus, void *opaque, int devfn,
|
||||
uint32_t pasid, IOMMUPRINotifier *notifier);
|
||||
/**
|
||||
* @pri_unregister_notifier: remove the PRI completion callback.
|
||||
*
|
||||
* Callback required if devices are allowed to use the page request
|
||||
* interface.
|
||||
*
|
||||
* @bus: the #PCIBus of the PCI device.
|
||||
*
|
||||
* @opaque: the data passed to pci_setup_iommu().
|
||||
*
|
||||
* @devfn: device and function number of the PCI device.
|
||||
*
|
||||
* @pasid: the pasid of the address space to stop tracking.
|
||||
*/
|
||||
void (*pri_unregister_notifier)(PCIBus *bus, void *opaque, int devfn,
|
||||
uint32_t pasid);
|
||||
/**
|
||||
* @pri_request_page: issue a PRI request.
|
||||
*
|
||||
* Callback required if devices are allowed to use the page request
|
||||
* interface.
|
||||
*
|
||||
* @bus: the #PCIBus of the PCI device.
|
||||
*
|
||||
* @opaque: the data passed to pci_setup_iommu().
|
||||
*
|
||||
* @devfn: device and function number of the PCI device.
|
||||
*
|
||||
* @pasid: the pasid of the address space to use for the request.
|
||||
*
|
||||
* @priv_req: privileged mode bit (PASID TLP).
|
||||
*
|
||||
* @exec_req: execute request bit (PASID TLP).
|
||||
*
|
||||
* @addr: untranslated address of the requested page.
|
||||
*
|
||||
* @lpig: last page in group.
|
||||
*
|
||||
* @prgi: page request group index.
|
||||
*
|
||||
* @is_read: request read access.
|
||||
*
|
||||
* @is_write: request write access.
|
||||
*/
|
||||
int (*pri_request_page)(PCIBus *bus, void *opaque, int devfn,
|
||||
uint32_t pasid, bool priv_req, bool exec_req,
|
||||
hwaddr addr, bool lpig, uint16_t prgi, bool is_read,
|
||||
bool is_write);
|
||||
} PCIIOMMUOps;
|
||||
|
||||
AddressSpace *pci_device_iommu_address_space(PCIDevice *dev);
|
||||
@@ -436,6 +631,126 @@ bool pci_device_set_iommu_device(PCIDevice *dev, HostIOMMUDevice *hiod,
|
||||
Error **errp);
|
||||
void pci_device_unset_iommu_device(PCIDevice *dev);
|
||||
|
||||
/**
|
||||
* pci_iommu_get_iotlb_info: get properties required to initialize a
|
||||
* device IOTLB.
|
||||
*
|
||||
* Returns 0 on success, or a negative errno otherwise.
|
||||
*
|
||||
* @dev: the device that wants to get the information.
|
||||
* @addr_width: the address width of the IOMMU (output parameter).
|
||||
* @min_page_size: the page size of the IOMMU (output parameter).
|
||||
*/
|
||||
int pci_iommu_get_iotlb_info(PCIDevice *dev, uint8_t *addr_width,
|
||||
uint32_t *min_page_size);
|
||||
|
||||
/**
|
||||
* pci_iommu_init_iotlb_notifier: initialize an IOMMU notifier.
|
||||
*
|
||||
* This function is used by devices before registering an IOTLB notifier.
|
||||
*
|
||||
* @dev: the device.
|
||||
* @n: the notifier to be initialized.
|
||||
* @fn: the callback to be installed.
|
||||
* @opaque: a user pointer that can be used to track a state.
|
||||
*/
|
||||
int pci_iommu_init_iotlb_notifier(PCIDevice *dev, IOMMUNotifier *n,
|
||||
IOMMUNotify fn, void *opaque);
|
||||
|
||||
/**
|
||||
* pci_ats_request_translation: perform an ATS request.
|
||||
*
|
||||
* Returns the number of translations stored in @result in case of success,
|
||||
* a negative error code otherwise.
|
||||
* -ENOMEM is returned when the result buffer is not large enough to store
|
||||
* all the translations.
|
||||
*
|
||||
* @dev: the ATS-capable PCI device.
|
||||
* @pasid: the pasid of the address space in which the translation will be done.
|
||||
* @priv_req: privileged mode bit (PASID TLP).
|
||||
* @exec_req: execute request bit (PASID TLP).
|
||||
* @addr: start address of the memory range to be translated.
|
||||
* @length: length of the memory range in bytes.
|
||||
* @no_write: request a read-only translation (if supported).
|
||||
* @result: buffer in which the TLB entries will be stored.
|
||||
* @result_length: result buffer length.
|
||||
* @err_count: number of untranslated subregions.
|
||||
*/
|
||||
ssize_t pci_ats_request_translation(PCIDevice *dev, uint32_t pasid,
|
||||
bool priv_req, bool exec_req,
|
||||
hwaddr addr, size_t length,
|
||||
bool no_write, IOMMUTLBEntry *result,
|
||||
size_t result_length,
|
||||
uint32_t *err_count);
|
||||
|
||||
/**
|
||||
* pci_pri_request_page: perform a PRI request.
|
||||
*
|
||||
* Returns 0 if the PRI request has been sent to the guest OS,
|
||||
* an error code otherwise.
|
||||
*
|
||||
* @dev: the PRI-capable PCI device.
|
||||
* @pasid: the pasid of the address space in which the translation will be done.
|
||||
* @priv_req: privileged mode bit (PASID TLP).
|
||||
* @exec_req: execute request bit (PASID TLP).
|
||||
* @addr: untranslated address of the requested page.
|
||||
* @lpig: last page in group.
|
||||
* @prgi: page request group index.
|
||||
* @is_read: request read access.
|
||||
* @is_write: request write access.
|
||||
*/
|
||||
int pci_pri_request_page(PCIDevice *dev, uint32_t pasid, bool priv_req,
|
||||
bool exec_req, hwaddr addr, bool lpig,
|
||||
uint16_t prgi, bool is_read, bool is_write);
|
||||
|
||||
/**
|
||||
* pci_pri_register_notifier: register the PRI callback for a given address
|
||||
* space.
|
||||
*
|
||||
* Returns 0 on success, an error code otherwise.
|
||||
*
|
||||
* @dev: the PRI-capable PCI device.
|
||||
* @pasid: the pasid of the address space to track.
|
||||
* @notifier: the notifier to register.
|
||||
*/
|
||||
int pci_pri_register_notifier(PCIDevice *dev, uint32_t pasid,
|
||||
IOMMUPRINotifier *notifier);
|
||||
|
||||
/**
|
||||
* pci_pri_unregister_notifier: remove the PRI callback from a given address
|
||||
* space.
|
||||
*
|
||||
* @dev: the PRI-capable PCI device.
|
||||
* @pasid: the pasid of the address space to stop tracking.
|
||||
*/
|
||||
void pci_pri_unregister_notifier(PCIDevice *dev, uint32_t pasid);
|
||||
|
||||
/**
|
||||
* pci_iommu_register_iotlb_notifier: register a notifier for changes to
|
||||
* IOMMU translation entries in a specific address space.
|
||||
*
|
||||
* Returns 0 on success, or a negative errno otherwise.
|
||||
*
|
||||
* @dev: the device that wants to get notified.
|
||||
* @pasid: the pasid of the address space to track.
|
||||
* @n: the notifier to register.
|
||||
*/
|
||||
int pci_iommu_register_iotlb_notifier(PCIDevice *dev, uint32_t pasid,
|
||||
IOMMUNotifier *n);
|
||||
|
||||
/**
|
||||
* pci_iommu_unregister_iotlb_notifier: unregister a notifier that has been
|
||||
* registerd with pci_iommu_register_iotlb_notifier.
|
||||
*
|
||||
* Returns 0 on success, or a negative errno otherwise.
|
||||
*
|
||||
* @dev: the device that wants to stop notifications.
|
||||
* @pasid: the pasid of the address space to stop tracking.
|
||||
* @n: the notifier to unregister.
|
||||
*/
|
||||
int pci_iommu_unregister_iotlb_notifier(PCIDevice *dev, uint32_t pasid,
|
||||
IOMMUNotifier *n);
|
||||
|
||||
/**
|
||||
* pci_setup_iommu: Initialize specific IOMMU handlers for a PCIBus
|
||||
*
|
||||
@@ -668,6 +983,7 @@ void lsi53c8xx_handle_legacy_cmdline(DeviceState *lsi_dev);
|
||||
|
||||
qemu_irq pci_allocate_irq(PCIDevice *pci_dev);
|
||||
void pci_set_irq(PCIDevice *pci_dev, int level);
|
||||
int pci_irq_disabled(PCIDevice *d);
|
||||
|
||||
static inline void pci_irq_assert(PCIDevice *pci_dev)
|
||||
{
|
||||
|
||||
@@ -90,6 +90,7 @@ struct PCIDevice {
|
||||
char name[64];
|
||||
PCIIORegion io_regions[PCI_NUM_REGIONS];
|
||||
AddressSpace bus_master_as;
|
||||
bool is_master;
|
||||
MemoryRegion bus_master_container_region;
|
||||
MemoryRegion bus_master_enable_region;
|
||||
|
||||
|
||||
+12
-1
@@ -70,8 +70,10 @@ struct PCIExpressDevice {
|
||||
uint16_t aer_cap;
|
||||
PCIEAERLog aer_log;
|
||||
|
||||
/* Offset of ATS capability in config space */
|
||||
/* Offset of ATS, PRI and PASID capabilities in config space */
|
||||
uint16_t ats_cap;
|
||||
uint16_t pasid_cap;
|
||||
uint16_t pri_cap;
|
||||
|
||||
/* ACS */
|
||||
uint16_t acs_cap;
|
||||
@@ -150,4 +152,13 @@ void pcie_cap_slot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
|
||||
Error **errp);
|
||||
void pcie_cap_slot_unplug_request_cb(HotplugHandler *hotplug_dev,
|
||||
DeviceState *dev, Error **errp);
|
||||
|
||||
void pcie_pasid_init(PCIDevice *dev, uint16_t offset, uint8_t pasid_width,
|
||||
bool exec_perm, bool priv_mod);
|
||||
void pcie_pri_init(PCIDevice *dev, uint16_t offset, uint32_t outstanding_pr_cap,
|
||||
bool prg_response_pasid_req);
|
||||
|
||||
bool pcie_pri_enabled(const PCIDevice *dev);
|
||||
bool pcie_pasid_enabled(const PCIDevice *dev);
|
||||
bool pcie_ats_enabled(const PCIDevice *dev);
|
||||
#endif /* QEMU_PCIE_H */
|
||||
|
||||
@@ -86,6 +86,14 @@ typedef enum PCIExpLinkWidth {
|
||||
#define PCI_ARI_VER 1
|
||||
#define PCI_ARI_SIZEOF 8
|
||||
|
||||
/* PASID */
|
||||
#define PCI_PASID_VER 1
|
||||
#define PCI_EXT_CAP_PASID_MAX_WIDTH 20
|
||||
#define PCI_PASID_CAP_WIDTH_SHIFT 8
|
||||
|
||||
/* PRI */
|
||||
#define PCI_PRI_VER 1
|
||||
|
||||
/* AER */
|
||||
#define PCI_ERR_VER 2
|
||||
#define PCI_ERR_SIZEOF 0x48
|
||||
|
||||
@@ -43,7 +43,21 @@ typedef struct vhost_vdpa_shared {
|
||||
struct vhost_vdpa_iova_range iova_range;
|
||||
QLIST_HEAD(, vdpa_iommu) iommu_list;
|
||||
|
||||
/* IOVA mapping used by the Shadow Virtqueue */
|
||||
/*
|
||||
* IOVA mapping used by the Shadow Virtqueue
|
||||
*
|
||||
* It is shared among all ASID for simplicity, whether CVQ shares ASID with
|
||||
* guest or not:
|
||||
* - Memory listener need access to guest's memory addresses allocated in
|
||||
* the IOVA tree.
|
||||
* - There should be plenty of IOVA address space for both ASID not to
|
||||
* worry about collisions between them. Guest's translations are still
|
||||
* validated with virtio virtqueue_pop so there is no risk for the guest
|
||||
* to access memory that it shouldn't.
|
||||
*
|
||||
* To allocate a iova tree per ASID is doable but it complicates the code
|
||||
* and it is not worth it for the moment.
|
||||
*/
|
||||
VhostIOVATree *iova_tree;
|
||||
|
||||
/* Copy of backend features */
|
||||
@@ -51,6 +65,12 @@ typedef struct vhost_vdpa_shared {
|
||||
|
||||
bool iotlb_batch_begin_sent;
|
||||
|
||||
/*
|
||||
* The memory listener has been registered, so DMA maps have been sent to
|
||||
* the device.
|
||||
*/
|
||||
bool listener_registered;
|
||||
|
||||
/* Vdpa must send shadow addresses as IOTLB key for data queues, not GPA */
|
||||
bool shadow_data;
|
||||
|
||||
|
||||
@@ -183,6 +183,7 @@ struct IOMMUNotifier {
|
||||
hwaddr start;
|
||||
hwaddr end;
|
||||
int iommu_idx;
|
||||
void *opaque;
|
||||
QLIST_ENTRY(IOMMUNotifier) node;
|
||||
};
|
||||
typedef struct IOMMUNotifier IOMMUNotifier;
|
||||
|
||||
+3
-31
@@ -235,6 +235,7 @@ static void vhost_vdpa_cleanup(NetClientState *nc)
|
||||
return;
|
||||
}
|
||||
qemu_close(s->vhost_vdpa.shared->device_fd);
|
||||
g_clear_pointer(&s->vhost_vdpa.shared->iova_tree, vhost_iova_tree_delete);
|
||||
g_free(s->vhost_vdpa.shared);
|
||||
}
|
||||
|
||||
@@ -362,14 +363,8 @@ static int vdpa_net_migration_state_notifier(NotifierWithReturn *notifier,
|
||||
|
||||
static void vhost_vdpa_net_data_start_first(VhostVDPAState *s)
|
||||
{
|
||||
struct vhost_vdpa *v = &s->vhost_vdpa;
|
||||
|
||||
migration_add_notifier(&s->migration_state,
|
||||
vdpa_net_migration_state_notifier);
|
||||
if (v->shadow_vqs_enabled) {
|
||||
v->shared->iova_tree = vhost_iova_tree_new(v->shared->iova_range.first,
|
||||
v->shared->iova_range.last);
|
||||
}
|
||||
}
|
||||
|
||||
static int vhost_vdpa_net_data_start(NetClientState *nc)
|
||||
@@ -416,19 +411,12 @@ static int vhost_vdpa_net_data_load(NetClientState *nc)
|
||||
static void vhost_vdpa_net_client_stop(NetClientState *nc)
|
||||
{
|
||||
VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
|
||||
struct vhost_dev *dev;
|
||||
|
||||
assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
|
||||
|
||||
if (s->vhost_vdpa.index == 0) {
|
||||
migration_remove_notifier(&s->migration_state);
|
||||
}
|
||||
|
||||
dev = s->vhost_vdpa.dev;
|
||||
if (dev->vq_index + dev->nvqs == dev->vq_index_end) {
|
||||
g_clear_pointer(&s->vhost_vdpa.shared->iova_tree,
|
||||
vhost_iova_tree_delete);
|
||||
}
|
||||
}
|
||||
|
||||
static NetClientInfo net_vhost_vdpa_info = {
|
||||
@@ -600,24 +588,6 @@ out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If other vhost_vdpa already have an iova_tree, reuse it for simplicity,
|
||||
* whether CVQ shares ASID with guest or not, because:
|
||||
* - Memory listener need access to guest's memory addresses allocated in
|
||||
* the IOVA tree.
|
||||
* - There should be plenty of IOVA address space for both ASID not to
|
||||
* worry about collisions between them. Guest's translations are still
|
||||
* validated with virtio virtqueue_pop so there is no risk for the guest
|
||||
* to access memory that it shouldn't.
|
||||
*
|
||||
* To allocate a iova tree per ASID is doable but it complicates the code
|
||||
* and it is not worth it for the moment.
|
||||
*/
|
||||
if (!v->shared->iova_tree) {
|
||||
v->shared->iova_tree = vhost_iova_tree_new(v->shared->iova_range.first,
|
||||
v->shared->iova_range.last);
|
||||
}
|
||||
|
||||
r = vhost_vdpa_cvq_map_buf(&s->vhost_vdpa, s->cvq_cmd_out_buffer,
|
||||
vhost_vdpa_net_cvq_cmd_page_len(), false);
|
||||
if (unlikely(r < 0)) {
|
||||
@@ -1726,6 +1696,8 @@ static NetClientState *net_vhost_vdpa_init(NetClientState *peer,
|
||||
s->vhost_vdpa.shared->device_fd = vdpa_device_fd;
|
||||
s->vhost_vdpa.shared->iova_range = iova_range;
|
||||
s->vhost_vdpa.shared->shadow_data = svq;
|
||||
s->vhost_vdpa.shared->iova_tree = vhost_iova_tree_new(iova_range.first,
|
||||
iova_range.last);
|
||||
} else if (!is_datapath) {
|
||||
s->cvq_cmd_out_buffer = mmap(NULL, vhost_vdpa_net_cvq_cmd_page_len(),
|
||||
PROT_READ | PROT_WRITE,
|
||||
|
||||
Binary file not shown.
@@ -1622,7 +1622,7 @@ static void test_acpi_aarch64_virt_tcg_memhp(void)
|
||||
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
|
||||
.cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
|
||||
.ram_start = 0x40000000ULL,
|
||||
.scan_len = 256ULL * 1024 * 1024,
|
||||
.scan_len = 256ULL * MiB,
|
||||
};
|
||||
|
||||
data.variant = ".memhp";
|
||||
@@ -1717,7 +1717,7 @@ static void test_acpi_riscv64_virt_tcg_numamem(void)
|
||||
.uefi_fl2 = "pc-bios/edk2-riscv-vars.fd",
|
||||
.cd = "tests/data/uefi-boot-images/bios-tables-test.riscv64.iso.qcow2",
|
||||
.ram_start = 0x80000000ULL,
|
||||
.scan_len = 128ULL * 1024 * 1024,
|
||||
.scan_len = 128ULL * MiB,
|
||||
};
|
||||
|
||||
data.variant = ".numamem";
|
||||
@@ -1743,7 +1743,7 @@ static void test_acpi_aarch64_virt_tcg_numamem(void)
|
||||
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
|
||||
.cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
|
||||
.ram_start = 0x40000000ULL,
|
||||
.scan_len = 128ULL * 1024 * 1024,
|
||||
.scan_len = 128ULL * MiB,
|
||||
};
|
||||
|
||||
data.variant = ".numamem";
|
||||
@@ -1765,7 +1765,7 @@ static void test_acpi_aarch64_virt_tcg_pxb(void)
|
||||
.uefi_fl1 = "pc-bios/edk2-aarch64-code.fd",
|
||||
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
|
||||
.ram_start = 0x40000000ULL,
|
||||
.scan_len = 128ULL * 1024 * 1024,
|
||||
.scan_len = 128ULL * MiB,
|
||||
};
|
||||
/*
|
||||
* While using -cdrom, the cdrom would auto plugged into pxb-pcie,
|
||||
@@ -1841,7 +1841,7 @@ static void test_acpi_aarch64_virt_tcg_acpi_hmat(void)
|
||||
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
|
||||
.cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
|
||||
.ram_start = 0x40000000ULL,
|
||||
.scan_len = 128ULL * 1024 * 1024,
|
||||
.scan_len = 128ULL * MiB,
|
||||
};
|
||||
|
||||
data.variant = ".acpihmatvirt";
|
||||
@@ -2095,7 +2095,7 @@ static void test_acpi_riscv64_virt_tcg(void)
|
||||
.uefi_fl2 = "pc-bios/edk2-riscv-vars.fd",
|
||||
.cd = "tests/data/uefi-boot-images/bios-tables-test.riscv64.iso.qcow2",
|
||||
.ram_start = 0x80000000ULL,
|
||||
.scan_len = 128ULL * 1024 * 1024,
|
||||
.scan_len = 128ULL * MiB,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -2117,7 +2117,7 @@ static void test_acpi_aarch64_virt_tcg(void)
|
||||
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
|
||||
.cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
|
||||
.ram_start = 0x40000000ULL,
|
||||
.scan_len = 128ULL * 1024 * 1024,
|
||||
.scan_len = 128ULL * MiB,
|
||||
};
|
||||
|
||||
data.smbios_cpu_max_speed = 2900;
|
||||
@@ -2138,7 +2138,7 @@ static void test_acpi_aarch64_virt_tcg_topology(void)
|
||||
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
|
||||
.cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
|
||||
.ram_start = 0x40000000ULL,
|
||||
.scan_len = 128ULL * 1024 * 1024,
|
||||
.scan_len = 128ULL * MiB,
|
||||
};
|
||||
|
||||
test_acpi_one("-cpu cortex-a57 "
|
||||
@@ -2223,7 +2223,7 @@ static void test_acpi_aarch64_virt_viot(void)
|
||||
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
|
||||
.cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
|
||||
.ram_start = 0x40000000ULL,
|
||||
.scan_len = 128ULL * 1024 * 1024,
|
||||
.scan_len = 128ULL * MiB,
|
||||
};
|
||||
|
||||
test_acpi_one("-cpu cortex-a57 "
|
||||
@@ -2407,7 +2407,7 @@ static void test_acpi_aarch64_virt_oem_fields(void)
|
||||
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
|
||||
.cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
|
||||
.ram_start = 0x40000000ULL,
|
||||
.scan_len = 128ULL * 1024 * 1024,
|
||||
.scan_len = 128ULL * MiB,
|
||||
};
|
||||
char *args;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
edk2_dir := ../../roms/edk2
|
||||
images_dir := ../data/uefi-boot-images
|
||||
emulation_targets := arm aarch64 i386 x86_64 riscv64
|
||||
emulation_targets := arm aarch64 i386 x86_64 riscv64 loongarch64
|
||||
uefi_binaries := bios-tables-test
|
||||
intermediate_suffixes := .efi .fat .iso.raw
|
||||
|
||||
@@ -56,7 +56,8 @@ Build/%.iso.raw: Build/%.fat
|
||||
# stripped from, the argument.
|
||||
map_arm_to_uefi = $(subst arm,ARM,$(1))
|
||||
map_aarch64_to_uefi = $(subst aarch64,AA64,$(call map_arm_to_uefi,$(1)))
|
||||
map_riscv64_to_uefi = $(subst riscv64,RISCV64,$(call map_aarch64_to_uefi,$(1)))
|
||||
map_loongarch64_to_uefi = $(subst loongarch64,LOONGARCH64,$(call map_aarch64_to_uefi,$(1)))
|
||||
map_riscv64_to_uefi = $(subst riscv64,RISCV64,$(call map_loongarch64_to_uefi,$(1)))
|
||||
map_i386_to_uefi = $(subst i386,IA32,$(call map_riscv64_to_uefi,$(1)))
|
||||
map_x86_64_to_uefi = $(subst x86_64,X64,$(call map_i386_to_uefi,$(1)))
|
||||
map_to_uefi = $(subst .,,$(call map_x86_64_to_uefi,$(1)))
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
PLATFORM_VERSION = 0.1
|
||||
PLATFORM_NAME = UefiTestTools
|
||||
SKUID_IDENTIFIER = DEFAULT
|
||||
SUPPORTED_ARCHITECTURES = ARM|AARCH64|IA32|X64|RISCV64
|
||||
SUPPORTED_ARCHITECTURES = ARM|AARCH64|IA32|X64|RISCV64|LOONGARCH64
|
||||
BUILD_TARGETS = DEBUG
|
||||
|
||||
[BuildOptions.IA32]
|
||||
@@ -65,6 +65,10 @@
|
||||
[LibraryClasses.RISCV64]
|
||||
BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
|
||||
|
||||
[LibraryClasses.LOONGARCH64]
|
||||
BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
|
||||
StackCheckLib|MdePkg/Library/StackCheckLibNull/StackCheckLibNull.inf
|
||||
|
||||
[PcdsFixedAtBuild]
|
||||
gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x8040004F
|
||||
gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x2F
|
||||
|
||||
@@ -21,6 +21,16 @@ dest = ./Build
|
||||
arch = AARCH64
|
||||
cpy1 = AARCH64/BiosTablesTest.efi bios-tables-test.aarch64.efi
|
||||
|
||||
####################################################################################
|
||||
# loongarch64
|
||||
|
||||
[build.loongarch64]
|
||||
conf = UefiTestToolsPkg/UefiTestToolsPkg.dsc
|
||||
plat = UefiTestTools
|
||||
dest = ./Build
|
||||
arch = LOONGARCH64
|
||||
cpy1 = LOONGARCH64/BiosTablesTest.efi bios-tables-test.loongarch64.efi
|
||||
|
||||
####################################################################################
|
||||
# riscv64
|
||||
|
||||
|
||||
Reference in New Issue
Block a user