#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"
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) {
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:
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