|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH V2 14/25] x86/vvtd: Process interrupt remapping request
On Wed, Aug 09, 2017 at 04:34:15PM -0400, Lan Tianyu wrote:
> From: Chao Gao <chao.gao@xxxxxxxxx>
>
> When a remapping interrupt request arrives, remapping hardware computes the
> interrupt_index per the algorithm described in VTD spec
> "Interrupt Remapping Table", interprets the IRTE and generates a remapped
> interrupt request.
>
> This patch introduces viommu_handle_irq_request() to emulate the process how
> remapping hardware handles a remapping interrupt request.
>
> Signed-off-by: Chao Gao <chao.gao@xxxxxxxxx>
> Signed-off-by: Lan Tianyu <tianyu.lan@xxxxxxxxx>
> ---
> xen/drivers/passthrough/vtd/iommu.h | 21 +++
> xen/drivers/passthrough/vtd/vtd.h | 6 +
> xen/drivers/passthrough/vtd/vvtd.c | 276
> +++++++++++++++++++++++++++++++++++-
> 3 files changed, 302 insertions(+), 1 deletion(-)
>
> diff --git a/xen/drivers/passthrough/vtd/iommu.h
> b/xen/drivers/passthrough/vtd/iommu.h
> index 102b4f3..70e64cf 100644
> --- a/xen/drivers/passthrough/vtd/iommu.h
> +++ b/xen/drivers/passthrough/vtd/iommu.h
> @@ -244,6 +244,21 @@
> #define dma_frcd_source_id(c) (c & 0xffff)
> #define dma_frcd_page_addr(d) (d & (((u64)-1) << 12)) /* low 64 bit */
>
> +enum VTD_FAULT_TYPE
> +{
> + /* Interrupt remapping transition faults */
> + VTD_FR_IR_REQ_RSVD = 0x20, /* One or more IR request reserved
> + * fields set */
> + VTD_FR_IR_INDEX_OVER = 0x21, /* Index value greater than max */
> + VTD_FR_IR_ENTRY_P = 0x22, /* Present (P) not set in IRTE */
> + VTD_FR_IR_ROOT_INVAL = 0x23, /* IR Root table invalid */
> + VTD_FR_IR_IRTE_RSVD = 0x24, /* IRTE Rsvd field non-zero with
> + * Present flag set */
> + VTD_FR_IR_REQ_COMPAT = 0x25, /* Encountered compatible IR
> + * request while disabled */
> + VTD_FR_IR_SID_ERR = 0x26, /* Invalid Source-ID */
> +};
Could you please align the values, like:
enum VTD_FAULT_TYPE
{
/* Interrupt remapping transition faults */
VTD_FR_IR_REQ_RSVD = 0x20, /* One or more IR request reserved
* fields set */
VTD_FR_IR_INDEX_OVER = 0x21, /* Index value greater than max */
> +
> /*
> * 0: Present
> * 1-11: Reserved
> @@ -384,6 +399,12 @@ struct iremap_entry {
> };
>
> /*
> + * When VT-d doesn't enable Extended Interrupt Mode. Hardware only interprets
> + * only 8-bits ([15:8]) of Destination-ID field in the IRTEs.
The above comment needs to be rewritten. My knowledge of VT-d is
limited, what is IRTE referring to? I thought I was referring to the
IO APIC redirection table registers, but the mask then doesn't make
sense.
> + */
> +#define IRTE_xAPIC_DEST_MASK 0xff00
> +
> +/*
> * Posted-interrupt descriptor address is 64 bits with 64-byte aligned, only
> * the upper 26 bits of lest significiant 32 bits is available.
> */
> diff --git a/xen/drivers/passthrough/vtd/vtd.h
> b/xen/drivers/passthrough/vtd/vtd.h
> index bb8889f..1032b46 100644
> --- a/xen/drivers/passthrough/vtd/vtd.h
> +++ b/xen/drivers/passthrough/vtd/vtd.h
> @@ -47,6 +47,8 @@ struct IO_APIC_route_remap_entry {
> };
> };
>
> +#define IOAPIC_REMAP_ENTRY_INDEX(x) ((x.index_15 << 15) + x.index_0_14)
Could you use entry instead of x? And you should add parentheses
around it's usage in the macro.
> +
> struct msi_msg_remap_entry {
> union {
> u32 val;
> @@ -65,4 +67,8 @@ struct msi_msg_remap_entry {
> u32 data; /* msi message data */
> };
>
> +#define MSI_REMAP_ENTRY_INDEX(x) ((x.address_lo.index_15 << 15) + \
> + x.address_lo.index_0_14 + \
> + (x.address_lo.SHV ? (uint16_t)x.data : 0))
Same here. And it would be clearer to place both macros together IMHO.
> #endif // _VTD_H_
> diff --git a/xen/drivers/passthrough/vtd/vvtd.c
> b/xen/drivers/passthrough/vtd/vvtd.c
> index 8e8dbe6..2bee352 100644
> --- a/xen/drivers/passthrough/vtd/vvtd.c
> +++ b/xen/drivers/passthrough/vtd/vvtd.c
> @@ -23,11 +23,16 @@
> #include <xen/types.h>
> #include <xen/viommu.h>
> #include <xen/xmalloc.h>
> +#include <asm/apic.h>
> #include <asm/current.h>
> +#include <asm/event.h>
> #include <asm/hvm/domain.h>
> +#include <asm/io_apic.h>
> #include <asm/page.h>
> +#include <asm/p2m.h>
>
> #include "iommu.h"
> +#include "vtd.h"
>
> struct hvm_hw_vvtd_regs {
> uint8_t data[1024];
> @@ -38,6 +43,9 @@ struct hvm_hw_vvtd_regs {
> #define VIOMMU_STATUS_IRQ_REMAPPING_ENABLED (1 << 0)
> #define VIOMMU_STATUS_DMA_REMAPPING_ENABLED (1 << 1)
>
> +#define vvtd_irq_remapping_enabled(vvtd) \
> + (vvtd->status & VIOMMU_STATUS_IRQ_REMAPPING_ENABLED)
> +
> struct vvtd {
> /* VIOMMU_STATUS_XXX */
> int status;
> @@ -120,6 +128,140 @@ static inline uint8_t vvtd_get_reg_byte(struct vvtd
> *vtd, uint32_t reg)
> vvtd_set_reg(vvtd, (reg) + 4, (val) >> 32); \
> } while(0)
>
> +static int map_guest_page(struct domain *d, uint64_t gfn, void **virt)
You can make this function return a void * and encode the error in
there. See IS_ERR_VALUE and ERR_PTR.
> +{
> + struct page_info *p;
> +
> + p = get_page_from_gfn(d, gfn, NULL, P2M_ALLOC);
> + if ( !p )
> + return -EINVAL;
> +
> + if ( !get_page_type(p, PGT_writable_page) )
> + {
> + put_page(p);
> + return -EINVAL;
> + }
> +
> + *virt = __map_domain_page_global(p);
> + if ( !*virt )
> + {
> + put_page_and_type(p);
> + return -ENOMEM;
> + }
Newline
> + return 0;
> +}
> +
> +static void unmap_guest_page(void *virt)
> +{
> + struct page_info *page;
> +
> + if ( !virt )
> + return;
> +
> + virt = (void *)((unsigned long)virt & PAGE_MASK);
This should maybe be an ASSERT? Are you going to call unmap_guest_page
with something different than the return of map_guest_page?
> + page = mfn_to_page(domain_page_map_to_mfn(virt));
> +
> + unmap_domain_page_global(virt);
> + put_page_and_type(page);
> +}
> +
> +static void vvtd_inj_irq(
> + struct vlapic *target,
> + uint8_t vector,
> + uint8_t trig_mode,
> + uint8_t delivery_mode)
Indentation.
> +{
> + VVTD_DEBUG(VVTD_DBG_INFO, "dest=v%d, delivery_mode=%x vector=%d "
> + "trig_mode=%d.",
> + vlapic_vcpu(target)->vcpu_id, delivery_mode,
> + vector, trig_mode);
> +
> + ASSERT((delivery_mode == dest_Fixed) ||
> + (delivery_mode == dest_LowestPrio));
> +
> + vlapic_set_irq(target, vector, trig_mode);
> +}
> +
> +static int vvtd_delivery(
> + struct domain *d, int vector,
uint8_t vector?
> + uint32_t dest, uint8_t dest_mode,
> + uint8_t delivery_mode, uint8_t trig_mode)
Indentation.
> +{
> + struct vlapic *target;
> + struct vcpu *v;
> +
> + switch ( delivery_mode )
> + {
> + case dest_LowestPrio:
> + target = vlapic_lowest_prio(d, NULL, 0, dest, dest_mode);
> + if ( target != NULL )
> + {
> + vvtd_inj_irq(target, vector, trig_mode, delivery_mode);
> + break;
> + }
> + VVTD_DEBUG(VVTD_DBG_INFO, "null round robin: vector=%02x\n", vector);
> + break;
> +
> + case dest_Fixed:
> + for_each_vcpu ( d, v )
> + if ( vlapic_match_dest(vcpu_vlapic(v), NULL, 0, dest,
> + dest_mode) )
> + vvtd_inj_irq(vcpu_vlapic(v), vector,
> + trig_mode, delivery_mode);
> + break;
> +
> + case dest_NMI:
> + for_each_vcpu ( d, v )
> + if ( vlapic_match_dest(vcpu_vlapic(v), NULL, 0, dest, dest_mode)
> + && !test_and_set_bool(v->nmi_pending) )
> + vcpu_kick(v);
> + break;
> +
> + default:
> + printk(XENLOG_G_WARNING
> + "%pv: Unsupported VTD delivery mode %d for Dom%d\n",
> + current, delivery_mode, d->domain_id);
gprintk? Or at least this should be rate-limited somehow, it seems
like the guest can trigger such behavior?
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static uint32_t irq_remapping_request_index(struct irq_remapping_request
> *irq)
irq should be const.
> +{
> + if ( irq->type == VIOMMU_REQUEST_IRQ_MSI )
> + {
> + struct msi_msg_remap_entry msi_msg = { { irq->msg.msi.addr }, 0,
> + irq->msg.msi.data };
I would rather prefer that you use designated initializers, ie:
struct msi_msg_remap_entry msi_msg =
{
.address_lo = { .val = irq->msg.msi.addr },
.data = irq->msg.msi.data,
};
> +
> + return MSI_REMAP_ENTRY_INDEX(msi_msg);
> + }
> + else if ( irq->type == VIOMMU_REQUEST_IRQ_APIC )
> + {
> + struct IO_APIC_route_remap_entry remap_rte = { { irq->msg.rte } };
> +
> + return IOAPIC_REMAP_ENTRY_INDEX(remap_rte);
> + }
> + BUG();
ASSERT_UNREACHABLE();
And newline.
> + return 0;
> +}
> +
> +static inline uint32_t irte_dest(struct vvtd *vvtd, uint32_t dest)
vvtd should be const.
> +{
> + uint64_t irta;
> +
> + vvtd_get_reg_quad(vvtd, DMAR_IRTA_REG, irta);
> + /* In xAPIC mode, only 8-bits([15:8]) are valid */
> + return DMA_IRTA_EIME(irta) ? dest : MASK_EXTR(dest,
> IRTE_xAPIC_DEST_MASK);
> +}
> +
> +static int vvtd_record_fault(struct vvtd *vvtd,
> + struct irq_remapping_request *irq,
> + int reason)
> +{
> + return 0;
> +}
I guess this is going to be expanded later in the series? Seems
pointless to introduce it now.
> +
> static int vvtd_handle_gcmd_sirtp(struct vvtd *vvtd, uint32_t val)
> {
> uint64_t irta;
> @@ -259,6 +401,137 @@ static const struct hvm_mmio_ops vvtd_mmio_ops = {
> .write = vvtd_write
> };
>
> +static bool ir_sid_valid(struct iremap_entry *irte, uint32_t source_id)
> +{
> + return true;
> +}
Same here, it's pointless to introduce such helper now if it's a dummy
one anyway.
> +
> +/*
> + * 'record_fault' is a flag to indicate whether we need recording a fault
> + * and notifying guest when a fault happens during fetching vIRTE.
^ fetch of
> + */
> +static int vvtd_get_entry(struct vvtd *vvtd,
> + struct irq_remapping_request *irq,
Seems like vvtd and irq could be const.
> + struct iremap_entry *dest,
> + bool record_fault)
> +{
> + int ret;
> + uint32_t entry = irq_remapping_request_index(irq);
> + struct iremap_entry *irte, *irt_page;
> +
> + VVTD_DEBUG(VVTD_DBG_TRANS, "interpret a request with index %x", entry);
> +
> + if ( entry > vvtd->irt_max_entry )
> + {
> + ret = VTD_FR_IR_INDEX_OVER;
> + goto handle_fault;
> + }
> +
> + ret = map_guest_page(vvtd->domain, vvtd->irt + (entry >>
> IREMAP_ENTRY_ORDER),
> + (void**)&irt_page);
> + if ( ret )
> + {
> + ret = VTD_FR_IR_ROOT_INVAL;
> + goto handle_fault;
> + }
> +
> + irte = irt_page + (entry % (1 << IREMAP_ENTRY_ORDER));
> + dest->val = irte->val;
> + if ( !qinval_present(*irte) )
> + {
> + ret = VTD_FR_IR_ENTRY_P;
> + goto unmap_handle_fault;
> + }
> +
> + /* Check reserved bits */
> + if ( (irte->remap.res_1 || irte->remap.res_2 || irte->remap.res_3 ||
> + irte->remap.res_4) )
> + {
> + ret = VTD_FR_IR_IRTE_RSVD;
> + goto unmap_handle_fault;
> + }
> +
> + if (!ir_sid_valid(irte, irq->source_id))
> + {
> + ret = VTD_FR_IR_SID_ERR;
> + goto unmap_handle_fault;
> + }
> + unmap_guest_page(irt_page);
Newline.
> + return 0;
> +
> + unmap_handle_fault:
> + unmap_guest_page(irt_page);
> + handle_fault:
> + if ( !record_fault )
> + return ret;
> +
> + switch ( ret )
> + {
> + case VTD_FR_IR_SID_ERR:
> + case VTD_FR_IR_IRTE_RSVD:
> + case VTD_FR_IR_ENTRY_P:
> + if ( qinval_fault_disable(*irte) )
> + break;
> + /* fall through */
> + case VTD_FR_IR_INDEX_OVER:
> + case VTD_FR_IR_ROOT_INVAL:
> + vvtd_record_fault(vvtd, irq, ret);
> + break;
> +
> + default:
> + gdprintk(XENLOG_G_INFO, "Can't handle VT-d fault %x\n", ret);
> + }
> + return ret;
In order to avoid the usage of labels I would put the code in
handle_fault in a helper function and call it on error.
> +}
> +
> +static int vvtd_irq_request_sanity_check(struct vvtd *vvtd,
> + struct irq_remapping_request *irq)
> +{
> + if ( irq->type == VIOMMU_REQUEST_IRQ_APIC )
> + {
> + struct IO_APIC_route_remap_entry rte = { { irq->msg.rte } };
> +
> + ASSERT(rte.format);
> + return (!rte.reserved) ? 0 : VTD_FR_IR_REQ_RSVD;
> + }
> + else if ( irq->type == VIOMMU_REQUEST_IRQ_MSI )
> + {
> + struct msi_msg_remap_entry msi_msg = { { irq->msg.msi.addr } };
> +
> + ASSERT(msi_msg.address_lo.format);
> + return 0;
> + }
> + BUG();
ASSERT_UNREACHABLE(); and newline.
> + return 0;
> +}
> +
> +static int vvtd_handle_irq_request(struct domain *d,
> + struct irq_remapping_request *irq)
> +{
> + struct iremap_entry irte;
> + int ret;
> + struct vvtd *vvtd = domain_vvtd(d);
> +
> + if ( !vvtd || !vvtd_irq_remapping_enabled(vvtd) )
> + return -EINVAL;
ENODEV.
> +
> + ret = vvtd_irq_request_sanity_check(vvtd, irq);
> + if ( ret )
> + {
> + vvtd_record_fault(vvtd, irq, ret);
> + return ret;
> + }
> +
> + if ( !vvtd_get_entry(vvtd, irq, &irte, true) )
You are losing the return value of vvtd_get_entry here.
> + {
> + vvtd_delivery(vvtd->domain, irte.remap.vector,
> + irte_dest(vvtd, irte.remap.dst), irte.remap.dm,
> + irte.remap.dlm, irte.remap.tm);
You are losing the return value of vvtd_delivery.
> + return 0;
> + }
Newline.
> + return -EFAULT;
> +}
> +
Roger.
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |