@@ -1383,6 +1383,11 @@ int xc_domain_irq_permission(xc_interface *xch,
uint32_t pirq,
bool allow_access);
+int xc_domain_gsi_permission(xc_interface *xch,
+ uint32_t domid,
+ uint32_t gsi,
+ uint32_t flags);
+
int xc_domain_iomem_permission(xc_interface *xch,
uint32_t domid,
unsigned long first_mfn,
@@ -1638,6 +1643,11 @@ int xc_physdev_map_pirq_msi(xc_interface *xch,
int entry_nr,
uint64_t table_base);
+int xc_physdev_map_pirq_gsi(xc_interface *xch,
+ uint32_t domid,
+ int gsi,
+ int *pirq);
+
int xc_physdev_unmap_pirq(xc_interface *xch,
uint32_t domid,
int pirq);
@@ -1394,6 +1394,21 @@ int xc_domain_irq_permission(xc_interface *xch,
return do_domctl(xch, &domctl);
}
+int xc_domain_gsi_permission(xc_interface *xch,
+ uint32_t domid,
+ uint32_t gsi,
+ uint32_t flags)
+{
+ struct xen_domctl domctl = {
+ .cmd = XEN_DOMCTL_gsi_permission,
+ .domain = domid,
+ .u.gsi_permission.gsi = gsi,
+ .u.gsi_permission.flags = flags,
+ };
+
+ return do_domctl(xch, &domctl);
+}
+
int xc_domain_iomem_permission(xc_interface *xch,
uint32_t domid,
unsigned long first_mfn,
@@ -95,6 +95,33 @@ int xc_physdev_map_pirq_msi(xc_interface *xch,
return rc;
}
+int xc_physdev_map_pirq_gsi(xc_interface *xch,
+ uint32_t domid,
+ int gsi,
+ int *pirq)
+{
+ int rc;
+ struct physdev_map_pirq map = {
+ .domid = domid,
+ .type = MAP_PIRQ_TYPE_GSI,
+ .index = gsi,
+ };
+
+ if ( !pirq )
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ map.pirq = *pirq;
+
+ rc = do_physdev_op(xch, PHYSDEVOP_map_pirq, &map, sizeof(map));
+
+ if ( !rc )
+ *pirq = map.pirq;
+
+ return rc;
+}
+
int xc_physdev_unmap_pirq(xc_interface *xch,
uint32_t domid,
int pirq)
@@ -91,6 +91,12 @@ void libxl__arch_update_domain_config(libxl__gc *gc,
libxl_domain_config *dst,
const libxl_domain_config *src);
+_hidden
+int libxl__arch_hvm_map_gsi(libxl__gc *gc, uint32_t sbdf, uint32_t domid);
+_hidden
+int libxl__arch_hvm_unmap_gsi(libxl__gc *gc, uint32_t sbdf, uint32_t domid);
+_hidden
+bool libxl__arch_local_domain_has_pirq_notion(libxl__gc *gc);
#if defined(__i386__) || defined(__x86_64__)
#define LAPIC_BASE_ADDRESS 0xfee00000
@@ -1774,6 +1774,21 @@ void libxl__arch_update_domain_config(libxl__gc *gc,
{
}
+int libxl__arch_hvm_map_gsi(libxl__gc *gc, uint32_t sbdf, uint32_t domid)
+{
+ return ERROR_INVAL;
+}
+
+int libxl__arch_hvm_unmap_gsi(libxl__gc *gc, uint32_t sbdf, uint32_t domid)
+{
+ return ERROR_INVAL;
+}
+
+bool libxl__arch_local_domain_has_pirq_notion(libxl__gc *gc)
+{
+ return true;
+}
+
/*
* Local variables:
* mode: C
@@ -17,6 +17,7 @@
#include "libxl_osdeps.h" /* must come before any other headers */
#include "libxl_internal.h"
+#include "libxl_arch.h"
#define PCI_BDF "%04x:%02x:%02x.%01x"
#define PCI_BDF_SHORT "%02x:%02x.%01x"
@@ -1478,32 +1479,42 @@ static void pci_add_dm_done(libxl__egc *egc,
fclose(f);
if (!pci_supp_legacy_irq())
goto out_no_irq;
- sysfs_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/irq", pci->domain,
- pci->bus, pci->dev, pci->func);
- f = fopen(sysfs_path, "r");
- if (f == NULL) {
- LOGED(ERROR, domainid, "Couldn't open %s", sysfs_path);
- goto out_no_irq;
- }
- if ((fscanf(f, "%u", &irq) == 1) && irq) {
- r = xc_physdev_map_pirq(ctx->xch, domid, irq, &irq);
- if (r < 0) {
- LOGED(ERROR, domainid, "xc_physdev_map_pirq irq=%d (error=%d)",
- irq, r);
- fclose(f);
- rc = ERROR_FAIL;
+
+ /* When dom0 is PVH, should use gsi to map pirq and grant permission */
+ if (libxl__arch_local_domain_has_pirq_notion(gc) == false) {
+ rc = libxl__arch_hvm_map_gsi(gc, pci_encode_bdf(pci), domid);
+ if (rc) {
+ LOGD(ERROR, domainid, "libxl__arch_hvm_map_gsi failed");
goto out;
}
- r = xc_domain_irq_permission(ctx->xch, domid, irq, 1);
- if (r < 0) {
- LOGED(ERROR, domainid,
- "xc_domain_irq_permission irq=%d (error=%d)", irq, r);
- fclose(f);
- rc = ERROR_FAIL;
- goto out;
+ } else {
+ sysfs_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/irq", pci->domain,
+ pci->bus, pci->dev, pci->func);
+ f = fopen(sysfs_path, "r");
+ if (f == NULL) {
+ LOGED(ERROR, domainid, "Couldn't open %s", sysfs_path);
+ goto out_no_irq;
+ }
+ if ((fscanf(f, "%u", &irq) == 1) && irq) {
+ r = xc_physdev_map_pirq(ctx->xch, domid, irq, &irq);
+ if (r < 0) {
+ LOGED(ERROR, domainid, "xc_physdev_map_pirq irq=%d (error=%d)",
+ irq, r);
+ fclose(f);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ r = xc_domain_irq_permission(ctx->xch, domid, irq, 1);
+ if (r < 0) {
+ LOGED(ERROR, domainid,
+ "xc_domain_irq_permission irq=%d (error=%d)", irq, r);
+ fclose(f);
+ rc = ERROR_FAIL;
+ goto out;
+ }
}
+ fclose(f);
}
- fclose(f);
/* Don't restrict writes to the PCI config space from this VM */
if (pci->permissive) {
@@ -2229,33 +2240,42 @@ skip_bar:
if (!pci_supp_legacy_irq())
goto skip_legacy_irq;
- sysfs_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/irq", pci->domain,
- pci->bus, pci->dev, pci->func);
-
- f = fopen(sysfs_path, "r");
- if (f == NULL) {
- LOGED(ERROR, domid, "Couldn't open %s", sysfs_path);
- goto skip_legacy_irq;
- }
+ /* When dom0 is PVH, should use gsi to unmap pirq and deny permission */
+ if (libxl__arch_local_domain_has_pirq_notion(gc) == false) {
+ rc = libxl__arch_hvm_unmap_gsi(gc, pci_encode_bdf(pci), domid);
+ if (rc) {
+ LOGD(ERROR, domid, "libxl__arch_hvm_unmap_gsi failed");
+ goto out;
+ }
+ } else {
+ sysfs_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/irq", pci->domain,
+ pci->bus, pci->dev, pci->func);
- if ((fscanf(f, "%u", &irq) == 1) && irq) {
- rc = xc_physdev_unmap_pirq(ctx->xch, domid, irq);
- if (rc < 0) {
- /*
- * QEMU may have already unmapped the IRQ. So the error
- * may be spurious. For now, still print an error message as
- * it is not easy to distinguished between valid and
- * spurious error.
- */
- LOGED(ERROR, domid, "xc_physdev_unmap_pirq irq=%d", irq);
+ f = fopen(sysfs_path, "r");
+ if (f == NULL) {
+ LOGED(ERROR, domid, "Couldn't open %s", sysfs_path);
+ goto skip_legacy_irq;
}
- rc = xc_domain_irq_permission(ctx->xch, domid, irq, 0);
- if (rc < 0) {
- LOGED(ERROR, domid, "xc_domain_irq_permission irq=%d", irq);
+
+ if ((fscanf(f, "%u", &irq) == 1) && irq) {
+ rc = xc_physdev_unmap_pirq(ctx->xch, domid, irq);
+ if (rc < 0) {
+ /*
+ * QEMU may have already unmapped the IRQ. So the error
+ * may be spurious. For now, still print an error message as
+ * it is not easy to distinguished between valid and
+ * spurious error.
+ */
+ LOGED(ERROR, domid, "xc_physdev_unmap_pirq irq=%d", irq);
+ }
+ rc = xc_domain_irq_permission(ctx->xch, domid, irq, 0);
+ if (rc < 0) {
+ LOGED(ERROR, domid, "xc_domain_irq_permission irq=%d", irq);
+ }
}
- }
- fclose(f);
+ fclose(f);
+ }
skip_legacy_irq:
@@ -879,6 +879,78 @@ void libxl__arch_update_domain_config(libxl__gc *gc,
libxl_defbool_val(src->b_info.u.hvm.pirq));
}
+bool libxl__arch_local_domain_has_pirq_notion(libxl__gc *gc)
+{
+ int r;
+ xc_domaininfo_t info;
+
+ r = xc_domain_getinfo_single(CTX->xch, LIBXL_TOOLSTACK_DOMID, &info);
+ if (r == 0) {
+ if (!(info.flags & XEN_DOMINF_hvm_guest) ||
+ (info.arch_config.emulation_flags & XEN_X86_EMU_USE_PIRQ))
+ return true;
+ } else {
+ LOGE(ERROR, "getdomaininfo failed ret=%d", r);
+ }
+
+ return false;
+}
+
+int libxl__arch_hvm_map_gsi(libxl__gc *gc, uint32_t sbdf, uint32_t domid)
+{
+ int pirq = -1, gsi, r;
+
+ gsi = xc_pcidev_get_gsi(CTX->xch, sbdf);
+ if (gsi < 0) {
+ return ERROR_FAIL;
+ }
+
+ r = xc_physdev_map_pirq_gsi(CTX->xch, domid, gsi, &pirq);
+ if (r < 0) {
+ LOGED(ERROR, domid, "xc_physdev_map_pirq_gsi gsi=%d", gsi);
+ return ERROR_FAIL;
+ }
+
+ r = xc_domain_gsi_permission(CTX->xch, domid, gsi, XEN_DOMCTL_GSI_GRANT);
+ if (r < 0) {
+ LOGED(ERROR, domid, "xc_domain_gsi_permission gsi=%d", gsi);
+ return ERROR_FAIL;
+ }
+
+ return 0;
+}
+
+int libxl__arch_hvm_unmap_gsi(libxl__gc *gc, uint32_t sbdf, uint32_t domid)
+{
+ int pirq = -1, gsi, r;
+
+ gsi = xc_pcidev_get_gsi(CTX->xch, sbdf);
+ if (gsi < 0) {
+ return ERROR_FAIL;
+ }
+
+ /* Before unmapping, use mapping to get the already mapped pirq first */
+ r = xc_physdev_map_pirq_gsi(CTX->xch, domid, gsi, &pirq);
+ if (r < 0) {
+ LOGED(ERROR, domid, "xc_physdev_map_pirq_gsi gsi=%d", gsi);
+ return ERROR_FAIL;
+ }
+
+ r = xc_physdev_unmap_pirq(CTX->xch, domid, pirq);
+ if (r < 0) {
+ LOGED(ERROR, domid, "xc_physdev_unmap_pirq gsi=%d", gsi);
+ return ERROR_FAIL;
+ }
+
+ r = xc_domain_gsi_permission(CTX->xch, domid, gsi, XEN_DOMCTL_GSI_REVOKE);
+ if (r < 0) {
+ LOGED(ERROR, domid, "xc_domain_gsi_permission gsi=%d", gsi);
+ return ERROR_FAIL;
+ }
+
+ return 0;
+}
+
/*
* Local variables:
* mode: C