diff --git a/tools/include/xenctrl.h b/tools/include/xenctrl.h index 924f9a35f7..29617585c5 100644 --- a/tools/include/xenctrl.h +++ b/tools/include/xenctrl.h @@ -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); diff --git a/tools/libs/ctrl/xc_domain.c b/tools/libs/ctrl/xc_domain.c index f2d9d14b4d..e3538ec0ba 100644 --- a/tools/libs/ctrl/xc_domain.c +++ b/tools/libs/ctrl/xc_domain.c @@ -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, diff --git a/tools/libs/ctrl/xc_physdev.c b/tools/libs/ctrl/xc_physdev.c index 460a8e779c..25e686d7b3 100644 --- a/tools/libs/ctrl/xc_physdev.c +++ b/tools/libs/ctrl/xc_physdev.c @@ -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) diff --git a/tools/libs/light/libxl_arch.h b/tools/libs/light/libxl_arch.h index f88f11d6de..c8ef52ddbe 100644 --- a/tools/libs/light/libxl_arch.h +++ b/tools/libs/light/libxl_arch.h @@ -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 diff --git a/tools/libs/light/libxl_arm.c b/tools/libs/light/libxl_arm.c index a4029e3ac8..5a9db5e85f 100644 --- a/tools/libs/light/libxl_arm.c +++ b/tools/libs/light/libxl_arm.c @@ -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 diff --git a/tools/libs/light/libxl_pci.c b/tools/libs/light/libxl_pci.c index 96cb4da079..8ef6a7b5fe 100644 --- a/tools/libs/light/libxl_pci.c +++ b/tools/libs/light/libxl_pci.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; - } - - 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); + /* 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; } - rc = xc_domain_irq_permission(ctx->xch, domid, irq, 0); - if (rc < 0) { - LOGED(ERROR, domid, "xc_domain_irq_permission irq=%d", irq); - } - } + } else { + sysfs_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/irq", pci->domain, + pci->bus, pci->dev, pci->func); - fclose(f); + f = fopen(sysfs_path, "r"); + if (f == NULL) { + LOGED(ERROR, domid, "Couldn't open %s", sysfs_path); + goto skip_legacy_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); + } skip_legacy_irq: diff --git a/tools/libs/light/libxl_x86.c b/tools/libs/light/libxl_x86.c index 60643d6f53..a3164a3077 100644 --- a/tools/libs/light/libxl_x86.c +++ b/tools/libs/light/libxl_x86.c @@ -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