|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 3/5] x86/pt: introduce PT_IRQ_TYPE_GSI to bind GSIs to a PVH Dom0
The current type used to bind a legacy IRQ into a HVM guest is not suitable for
PVH Dom0 because the PCI device that's originating the interrupt is not know to
Xen.
To solve that a new bind type is introduced (PT_IRQ_TYPE_GSI), that takes a gsi
as a guest destination parameter (instead of a PCI device). This new binding
type builds on top of the existing PT_IRQ_TYPE_PCI, but since it's an identity
gsi binding some of the functionality needs to be branched (like the
assert/deassert of the gsi itself).
Right now this is limited to the hardware domain only, and to identity map
gsis.
Signed-off-by: Roger Pau Monné <roger.pau@xxxxxxxxxx>
---
Cc: Jan Beulich <jbeulich@xxxxxxxx>
Cc: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
---
xen/arch/x86/hvm/irq.c | 25 ++++++++
xen/drivers/passthrough/io.c | 147 ++++++++++++++++++++++++++++++++-----------
xen/include/public/domctl.h | 4 ++
xen/include/xen/hvm/irq.h | 6 ++
4 files changed, 144 insertions(+), 38 deletions(-)
diff --git a/xen/arch/x86/hvm/irq.c b/xen/arch/x86/hvm/irq.c
index 6e67cae9bd..2c35a1e044 100644
--- a/xen/arch/x86/hvm/irq.c
+++ b/xen/arch/x86/hvm/irq.c
@@ -118,6 +118,31 @@ void hvm_pci_intx_deassert(
spin_unlock(&d->arch.hvm_domain.irq_lock);
}
+void hvm_gsi_assert(struct domain *d, unsigned int gsi)
+{
+ struct hvm_irq *hvm_irq = hvm_domain_irq(d);
+
+ ASSERT(gsi < hvm_irq->nr_gsis);
+ ASSERT(!has_vpic(d));
+ spin_lock(&d->arch.hvm_domain.irq_lock);
+ if ( hvm_irq->gsi_assert_count[gsi]++ == 0 )
+ assert_gsi(d, gsi);
+ spin_unlock(&d->arch.hvm_domain.irq_lock);
+}
+
+void hvm_gsi_deassert(struct domain *d, unsigned int gsi)
+{
+ struct hvm_irq *hvm_irq = hvm_domain_irq(d);
+
+ ASSERT(gsi < hvm_irq->nr_gsis);
+ ASSERT(!has_vpic(d));
+ spin_lock(&d->arch.hvm_domain.irq_lock);
+ if ( hvm_irq->gsi_assert_count[gsi] )
+ hvm_irq->gsi_assert_count[gsi]--;
+ spin_unlock(&d->arch.hvm_domain.irq_lock);
+}
+
+
void hvm_isa_irq_assert(
struct domain *d, unsigned int isa_irq)
{
diff --git a/xen/drivers/passthrough/io.c b/xen/drivers/passthrough/io.c
index 3345db5759..e4cd22cf18 100644
--- a/xen/drivers/passthrough/io.c
+++ b/xen/drivers/passthrough/io.c
@@ -156,6 +156,21 @@ static int pt_irq_guest_eoi(struct domain *d, struct
hvm_pirq_dpci *pirq_dpci,
return 0;
}
+static void pt_girq_time_out(struct domain *d, unsigned int guest_gsi)
+{
+ const struct hvm_irq_dpci *dpci = domain_get_irq_dpci(d);
+ const struct hvm_girq_dpci_mapping *girq;
+
+ ASSERT(dpci);
+
+ list_for_each_entry ( girq, &dpci->girq[guest_gsi], list )
+ {
+ struct pirq *pirq = pirq_info(d, girq->machine_gsi);
+
+ pirq_dpci(pirq)->flags |= HVM_IRQ_DPCI_EOI_LATCH;
+ }
+}
+
static void pt_irq_time_out(void *data)
{
struct hvm_pirq_dpci *irq_map = data;
@@ -173,18 +188,18 @@ static void pt_irq_time_out(void *data)
}
list_for_each_entry ( digl, &irq_map->digl_list, list )
{
- unsigned int guest_gsi = hvm_pci_intx_gsi(digl->device, digl->intx);
- const struct hvm_girq_dpci_mapping *girq;
-
- list_for_each_entry ( girq, &dpci->girq[guest_gsi], list )
- {
- struct pirq *pirq = pirq_info(irq_map->dom, girq->machine_gsi);
-
- pirq_dpci(pirq)->flags |= HVM_IRQ_DPCI_EOI_LATCH;
- }
+ pt_girq_time_out(irq_map->dom,
+ hvm_pci_intx_gsi(digl->device, digl->intx));
hvm_pci_intx_deassert(irq_map->dom, digl->device, digl->intx);
}
+ if ( irq_map->guest_gsi != DPCI_INVALID_GSI )
+ {
+ ASSERT(is_hardware_domain(irq_map->dom));
+ pt_girq_time_out(irq_map->dom, irq_map->guest_gsi);
+ hvm_gsi_deassert(irq_map->dom, irq_map->guest_gsi);
+ }
+
pt_pirq_iterate(irq_map->dom, pt_irq_guest_eoi, NULL);
spin_unlock(&irq_map->dom->event_lock);
@@ -316,6 +331,9 @@ int pt_irq_create_bind(
if ( pirq < 0 || pirq >= d->nr_pirqs )
return -EINVAL;
+ if ( pt_irq_bind->irq_type == PT_IRQ_TYPE_GSI && !is_hardware_domain(d) )
+ return -EPERM;
+
restart:
spin_lock(&d->event_lock);
@@ -361,6 +379,7 @@ int pt_irq_create_bind(
goto restart;
}
+ pirq_dpci->guest_gsi = DPCI_INVALID_GSI;
switch ( pt_irq_bind->irq_type )
{
case PT_IRQ_TYPE_MSI:
@@ -464,32 +483,56 @@ int pt_irq_create_bind(
break;
}
+ case PT_IRQ_TYPE_GSI:
case PT_IRQ_TYPE_PCI:
case PT_IRQ_TYPE_MSI_TRANSLATE:
{
- unsigned int bus = pt_irq_bind->u.pci.bus;
- unsigned int device = pt_irq_bind->u.pci.device;
- unsigned int intx = pt_irq_bind->u.pci.intx;
- unsigned int guest_gsi = hvm_pci_intx_gsi(device, intx);
- unsigned int link = hvm_pci_intx_link(device, intx);
- struct dev_intx_gsi_link *digl = xmalloc(struct dev_intx_gsi_link);
+ unsigned int bus, device, intx, guest_gsi, link;
+ struct dev_intx_gsi_link *digl = NULL;
struct hvm_girq_dpci_mapping *girq =
xmalloc(struct hvm_girq_dpci_mapping);
- if ( !digl || !girq )
+ if ( !girq )
{
spin_unlock(&d->event_lock);
- xfree(girq);
- xfree(digl);
return -ENOMEM;
}
- hvm_irq_dpci->link_cnt[link]++;
+ if ( pt_irq_bind->irq_type != PT_IRQ_TYPE_GSI )
+ {
+ digl = xmalloc(struct dev_intx_gsi_link);
+ if ( !digl )
+ {
+ spin_unlock(&d->event_lock);
+ xfree(girq);
+ return -ENOMEM;
+ }
+
+ digl->bus = bus = pt_irq_bind->u.pci.bus;
+ digl->device = device = pt_irq_bind->u.pci.device;
+ digl->intx = intx = pt_irq_bind->u.pci.intx;
+ list_add_tail(&digl->list, &pirq_dpci->digl_list);
+
+ guest_gsi = hvm_pci_intx_gsi(device, intx);
+ link = hvm_pci_intx_link(device, intx);
- digl->bus = bus;
- digl->device = device;
- digl->intx = intx;
- list_add_tail(&digl->list, &pirq_dpci->digl_list);
+ hvm_irq_dpci->link_cnt[link]++;
+ }
+ else
+ {
+ guest_gsi = pt_irq_bind->u.gsi.gsi;
+
+ /* PT_IRQ_TYPE_GSI should only be used for identity bindings */
+ ASSERT(guest_gsi == pirq);
+ ASSERT(guest_gsi < hvm_domain_irq(d)->nr_gsis);
+
+ /*
+ * The actual PCI device(s) that use this GSI are unknown, Xen
+ * relies on machine_gsi to be identity bound into the guest.
+ */
+ bus = device = intx = 0;
+ pirq_dpci->guest_gsi = guest_gsi;
+ }
girq->bus = bus;
girq->device = device;
@@ -512,12 +555,15 @@ int pt_irq_create_bind(
HVM_IRQ_DPCI_TRANSLATE;
share = 0;
}
- else /* PT_IRQ_TYPE_PCI */
+ else /* PT_IRQ_TYPE_PCI or PT_IRQ_TYPE_GSI */
{
pirq_dpci->flags = HVM_IRQ_DPCI_MAPPED |
HVM_IRQ_DPCI_MACH_PCI |
HVM_IRQ_DPCI_GUEST_PCI;
- share = BIND_PIRQ__WILL_SHARE;
+ if ( pt_irq_bind->irq_type == PT_IRQ_TYPE_PCI )
+ share = BIND_PIRQ__WILL_SHARE;
+ else
+ share = io_apic_get_gsi_trigger(pirq);
}
/* Init timer before binding */
@@ -535,8 +581,11 @@ int pt_irq_create_bind(
*/
pirq_dpci->dom = NULL;
list_del(&girq->list);
- list_del(&digl->list);
- hvm_irq_dpci->link_cnt[link]--;
+ if ( pt_irq_bind->irq_type != PT_IRQ_TYPE_GSI)
+ {
+ list_del(&digl->list);
+ hvm_irq_dpci->link_cnt[link]--;
+ }
pirq_dpci->flags = 0;
pirq_cleanup_check(info, d);
spin_unlock(&d->event_lock);
@@ -610,14 +659,26 @@ int pt_irq_destroy_bind(
if ( pt_irq_bind->irq_type != PT_IRQ_TYPE_MSI )
{
- unsigned int bus = pt_irq_bind->u.pci.bus;
- unsigned int device = pt_irq_bind->u.pci.device;
- unsigned int intx = pt_irq_bind->u.pci.intx;
- unsigned int guest_gsi = hvm_pci_intx_gsi(device, intx);
- unsigned int link = hvm_pci_intx_link(device, intx);
+ unsigned int bus, device, intx, guest_gsi, link;
struct hvm_girq_dpci_mapping *girq;
struct dev_intx_gsi_link *digl, *tmp;
+ if ( pt_irq_bind->irq_type == PT_IRQ_TYPE_GSI )
+ {
+ bus = device = intx = 0;
+ guest_gsi = pt_irq_bind->u.gsi.gsi;
+ }
+ else
+ {
+ bus = pt_irq_bind->u.pci.bus;
+ device = pt_irq_bind->u.pci.device;
+ intx = pt_irq_bind->u.pci.intx;
+ guest_gsi = hvm_pci_intx_gsi(device, intx);
+ link = hvm_pci_intx_link(device, intx);
+ }
+
+
+
list_for_each_entry ( girq, &hvm_irq_dpci->girq[guest_gsi], list )
{
if ( girq->bus == bus &&
@@ -638,7 +699,8 @@ int pt_irq_destroy_bind(
return -EINVAL;
}
- hvm_irq_dpci->link_cnt[link]--;
+ if ( pt_irq_bind->irq_type != PT_IRQ_TYPE_GSI )
+ hvm_irq_dpci->link_cnt[link]--;
/* clear the mirq info */
if ( pirq_dpci && (pirq_dpci->flags & HVM_IRQ_DPCI_MAPPED) )
@@ -654,6 +716,7 @@ int pt_irq_destroy_bind(
}
}
what = list_empty(&pirq_dpci->digl_list) ? "final" : "partial";
+ pirq_dpci->guest_gsi = DPCI_INVALID_GSI;
}
else
what = "bogus";
@@ -835,6 +898,11 @@ static void hvm_dirq_assist(struct domain *d, struct
hvm_pirq_dpci *pirq_dpci)
hvm_pci_intx_assert(d, digl->device, digl->intx);
pirq_dpci->pending++;
}
+ if ( pirq_dpci->guest_gsi != DPCI_INVALID_GSI )
+ {
+ hvm_gsi_assert(d, pirq_dpci->guest_gsi);
+ pirq_dpci->pending++;
+ }
if ( pirq_dpci->flags & HVM_IRQ_DPCI_TRANSLATE )
{
@@ -862,12 +930,15 @@ static void __hvm_dpci_eoi(struct domain *d,
const union vioapic_redir_entry *ent)
{
struct pirq *pirq = pirq_info(d, girq->machine_gsi);
- struct hvm_pirq_dpci *pirq_dpci;
+ struct hvm_pirq_dpci *pirq_dpci = pirq_dpci(pirq);
if ( !hvm_domain_use_pirq(d, pirq) )
- hvm_pci_intx_deassert(d, girq->device, girq->intx);
-
- pirq_dpci = pirq_dpci(pirq);
+ {
+ if ( pirq_dpci->guest_gsi == DPCI_INVALID_GSI )
+ hvm_pci_intx_deassert(d, girq->device, girq->intx);
+ else
+ hvm_gsi_deassert(d, pirq_dpci->guest_gsi);
+ }
/*
* No need to get vector lock for timer
@@ -891,7 +962,7 @@ void hvm_dpci_eoi(struct domain *d, unsigned int guest_gsi,
if ( !iommu_enabled )
return;
- if ( guest_gsi < NR_ISAIRQS )
+ if ( guest_gsi < NR_ISAIRQS && !is_hardware_domain(d) )
{
hvm_dpci_isairq_eoi(d, guest_gsi);
return;
diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h
index 9e3ce21f71..31dd6ab986 100644
--- a/xen/include/public/domctl.h
+++ b/xen/include/public/domctl.h
@@ -554,6 +554,7 @@ typedef enum pt_irq_type_e {
PT_IRQ_TYPE_MSI,
PT_IRQ_TYPE_MSI_TRANSLATE,
PT_IRQ_TYPE_SPI, /* ARM: valid range 32-1019 */
+ PT_IRQ_TYPE_GSI,
} pt_irq_type_t;
struct xen_domctl_bind_pt_irq {
uint32_t machine_irq;
@@ -577,6 +578,9 @@ struct xen_domctl_bind_pt_irq {
struct {
uint16_t spi;
} spi;
+ struct {
+ uint32_t gsi;
+ } gsi;
} u;
};
typedef struct xen_domctl_bind_pt_irq xen_domctl_bind_pt_irq_t;
diff --git a/xen/include/xen/hvm/irq.h b/xen/include/xen/hvm/irq.h
index 8304cb5725..89bc505ca6 100644
--- a/xen/include/xen/hvm/irq.h
+++ b/xen/include/xen/hvm/irq.h
@@ -91,6 +91,7 @@ struct hvm_irq_dpci {
#define hvm_irq_dpci_size(cnt) offsetof(struct hvm_irq_dpci, girq[cnt])
+#define DPCI_INVALID_GSI UINT_MAX
/* Machine IRQ to guest device/intx mapping. */
struct hvm_pirq_dpci {
uint32_t flags;
@@ -98,6 +99,7 @@ struct hvm_pirq_dpci {
bool_t masked;
uint16_t pending;
struct list_head digl_list;
+ unsigned int guest_gsi;
struct domain *dom;
struct hvm_gmsi_info gmsi;
struct timer timer;
@@ -124,6 +126,10 @@ void hvm_isa_irq_assert(
void hvm_isa_irq_deassert(
struct domain *d, unsigned int isa_irq);
+/* Modify state of GSIs. */
+void hvm_gsi_assert(struct domain *d, unsigned int gsi);
+void hvm_gsi_deassert(struct domain *d, unsigned int gsi);
+
int hvm_set_pci_link_route(struct domain *d, u8 link, u8 isa_irq);
int hvm_inject_msi(struct domain *d, uint64_t addr, uint32_t data);
--
2.12.1
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |