Skip to content

Commit

Permalink
tools: Add new function to do PIRQ (un)map on PVH dom0
Browse files Browse the repository at this point in the history
When dom0 is PVH, and passthrough a device to dumU, xl will
use the gsi number of device to do a pirq mapping, see
pci_add_dm_done->xc_physdev_map_pirq, but the gsi number is
got from file /sys/bus/pci/devices/<sbdf>/irq, that confuses
irq and gsi, they are in different space and are not equal,
so it will fail when mapping.
To solve this issue, to get the real gsi and add a new function
xc_physdev_map_pirq_gsi to get a free pirq for gsi.
Note: why not use current function xc_physdev_map_pirq, because
it doesn't support to allocate a free pirq, what's more, to
prevent changing it and affecting its callers, so add
xc_physdev_map_pirq_gsi.

Besides, PVH dom0 doesn't have PIRQs flag, it doesn't do
PHYSDEVOP_map_pirq for each gsi. So grant function callstack
pci_add_dm_done->XEN_DOMCTL_irq_permission will fail at function
domain_pirq_to_irq. And old hypercall XEN_DOMCTL_irq_permission
requires passing in pirq, it is not suitable for PVH dom0 that
doesn't have PIRQs to grant irq permission.
To solve this issue, use the another hypercall
XEN_DOMCTL_gsi_permission to grant the permission of irq(
translate from gsi) to dumU when dom0 has no PIRQs.

Signed-off-by: Jiqian Chen <[email protected]>
Signed-off-by: Huang Rui <[email protected]>
Signed-off-by: Chen Jiqian <[email protected]>
Reviewed-by: Anthony PERARD <[email protected]>
  • Loading branch information
Jiqian Chen authored and jbeulich committed Sep 30, 2024
1 parent b93e598 commit f97f885
Show file tree
Hide file tree
Showing 7 changed files with 210 additions and 45 deletions.
10 changes: 10 additions & 0 deletions tools/include/xenctrl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down
15 changes: 15 additions & 0 deletions tools/libs/ctrl/xc_domain.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
27 changes: 27 additions & 0 deletions tools/libs/ctrl/xc_physdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions tools/libs/light/libxl_arch.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 15 additions & 0 deletions tools/libs/light/libxl_arm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
110 changes: 65 additions & 45 deletions tools/libs/light/libxl_pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -2229,33 +2240,42 @@ static void pci_remove_detached(libxl__egc *egc,
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:

Expand Down
72 changes: 72 additions & 0 deletions tools/libs/light/libxl_x86.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit f97f885

Please sign in to comment.