[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH RFC 6/6] xen/arm: platforms/tegra: ensure the hwdom can only affect its own interrupts
On Mon, 5 Sep 2016, Kyle Temkin wrote: > From: "Kyle J. Temkin" <temkink@xxxxxxxxxxxx> > > Several Tegra hardware devices-- and the Tegra device tree-- expect > the presence of a Tegra Legacy Interrupt Controller (LIC) in the hardware > domain. Accordingly, we'll need to expose (most of) the LIC's registers > to the hardware domain. > > As the Tegra LIC provides the ability to modify interrupt delivery (e.g. > by masking interrupts, forcing asserting/clearing them, or adjusting > their prority), it's important that the hardware domain's access be > mediated. This commit adds read/write handlers that prohibit > modification of register sections corresponding to interrupts not owned > by the hardware domain. > > Note that this is written to be domain agnostic; this allows the > potential to e.g. map the ictlr into multiple domains if this is desired > for passthrough in the future. > > Signed-off-by: Kyle Temkin <temkink@xxxxxxxxxxxx> Overall is a good patch. A few style issues and a couple of simple improvements but the core is fine. > xen/arch/arm/platforms/tegra.c | 227 > +++++++++++++++++++++++++++++++++++++++-- > 1 file changed, 216 insertions(+), 11 deletions(-) > > diff --git a/xen/arch/arm/platforms/tegra.c b/xen/arch/arm/platforms/tegra.c > index e75242c..a119c01 100644 > --- a/xen/arch/arm/platforms/tegra.c > +++ b/xen/arch/arm/platforms/tegra.c > @@ -21,11 +21,13 @@ > #include <xen/stdbool.h> > #include <xen/sched.h> > #include <xen/vmap.h> > +#include <xen/iocap.h> > > #include <asm/io.h> > #include <asm/gic.h> > #include <asm/platform.h> > #include <asm/platforms/tegra.h> > +#include <asm/mmio.h> > > > /* Permanent mapping to the Tegra legacy interrupt controller. */ > @@ -258,6 +260,218 @@ static void tegra_route_irq_to_xen(struct irq_desc > *desc, unsigned int priority) > } > > /** > + * Parses a LIC MMIO read or write, and extracts the information needed to > + * complete the request. > + * > + * @param info Information describing the MMIO read/write being performed. > + * @param register_number The register number in the ictlr; e.g. > + * TEGRA_ICTLR_CPU_IER_SET. > + * @param register_offset The offset into tegra_icltr_base at which the > target > + * register exists. > + * @param The number of the first IRQ represented by the given ictlr > register. > + */ > +static void tegra_ictlr_parse_mmio_request(mmio_info_t *info, > + int *register_number, int *register_offset, int *irq_base) > +{ It doesn't look like you are using register_number anywhere. I would just remove it from the parameter list. > + /* Determine the offset of the access into the ICTLR region. */ > + uint32_t offset = info->gpa - TEGRA_ICTLR_BASE; > + > + if(register_number) > + *register_number = offset % TEGRA_ICTLR_SIZE; style > + if(register_offset) > + *register_offset = offset; style > + if(irq_base) { style > + int ictlr_number = offset / TEGRA_ICTLR_SIZE; > + *irq_base = (ictlr_number * TEGRA_IRQS_PER_ICTLR) + NR_LOCAL_IRQS; > + } > +} > + > +/** style > + * Returns true iff the given IRQ is currently routed to the given domain. > + * > + * @param irq The IRQ number for the IRQ in question. > + * @param d The domain in question. > + * @return True iff the given domain is the current IRQ target. > + */ > +static bool irq_owned_by_domain(int irq, struct domain *d) > +{ > + struct irq_desc *desc = irq_to_desc(irq); > + domid_t domid; > + unsigned long flags; > + > + BUG_ON(!desc); > + > + spin_lock_irqsave(&desc->lock, flags); > + domid = irq_get_domain_id(desc); > + spin_unlock_irqrestore(&desc->lock, flags); > + > + return (d->domain_id == domid); > +} > + > +/** > + * Mediates an MMIO-read to the Tegra legacy interrupt controller. > + * Ensures that each domain only is passed interrupt state for its > + * own interupts. > + */ > +static int tegra_ictlr_domain_read(struct vcpu *v, mmio_info_t *info, > + register_t *target_register, void *priv) > +{ > + register_t raw_value; > + > + int register_number; > + int register_offset; > + int irq_base; > + int i; > + > + tegra_ictlr_parse_mmio_request(info, ®ister_number, ®ister_offset, > + &irq_base); > + > + /* Sanity check the read. */ > + if ( register_offset & 0x3 ) { > + printk(XENLOG_G_ERR "d%u: Tegra LIC: Attempt to read unaligned ictlr > addr" > + "(%" PRIpaddr ")!", current->domain->domain_id, > info->gpa); > + domain_crash_synchronous(); > + } > + if ( info->dabt.size != DABT_WORD ) { > + printk(XENLOG_G_ERR "d%u: Tegra LIC: Non-word read from ictlr addr" > + "%" PRIpaddr "!", current->domain->domain_id, > info->gpa); > + domain_crash_synchronous(); > + } > + > + /* Ensure that we've only been handed an offset within our region. */ > + BUG_ON(register_offset > TEGRA_ICTLR_SIZE * TEGRA_ICTLR_COUNT); > + > + /* Perform the core ictlr read. */ > + raw_value = readl(tegra_ictlr_base + register_offset); > + > + /* > + * We don't want to leak information about interrupts not controlled > + * by the active domain. Thus, we'll zero out any ictlr slots for > + * IRQs not owned by the given domain. > + */ > + for (i = 0; i < TEGRA_IRQS_PER_ICTLR; ++i) { > + int irq = irq_base + i; > + int mask = BIT(irq % 32); Isn't (irq % 32) just "i"? So mask is just: mask = (1 << i); ? > + if(!irq_owned_by_domain(irq, current->domain)) style > + raw_value &= ~mask; > + } > + > + /* Finally, set the target register to our read value */ > + *target_register = raw_value; > + return 1; > +} > + > + > +/** > + * Mediates an MMIO-read to the Tegra legacy interrupt controller. > + * Ensures that each domain only can only control is own interrupts. > + */ > +static int tegra_ictlr_domain_write(struct vcpu *v, mmio_info_t *info, > + register_t new_value, void *priv) > +{ > + register_t write_mask = 0; > + register_t raw_value; > + > + int register_number; > + int register_offset; > + int irq_base; > + int i; > + > + tegra_ictlr_parse_mmio_request(info, ®ister_number, ®ister_offset, > + &irq_base); > + > + /* Sanity check the read. */ > + if ( register_offset & 0x3 ) { > + printk(XENLOG_G_ERR "d%u: Tegra LIC: Attempt to write unaligned > ictlr addr" > + "(%" PRIpaddr ")!", current->domain->domain_id, > info->gpa); > + domain_crash_synchronous(); > + return 0; > + } > + if ( info->dabt.size != DABT_WORD ) { > + printk(XENLOG_G_ERR "d%u: Tegra LIC: Non-word write to ictlr addr" > + "%" PRIpaddr "!", current->domain->domain_id, > info->gpa); > + domain_crash_synchronous(); > + return 0; > + } > + > + /* Ensure that we've only been handed an offset within our region. */ > + BUG_ON(register_offset > TEGRA_ICTLR_SIZE * TEGRA_ICTLR_COUNT); > + > + /* > + * We only want to write to bits that correspond to interrupts that the > + * current domain controls. Accordingly, we'll create a mask that has a > + * single bit set for each writable bit. > + */ > + for (i = 0; i < TEGRA_IRQS_PER_ICTLR; ++i) { > + int irq = irq_base + i; > + int bit_mask = BIT(irq % 32); same comment as before: mask = (1 << i); > + if(irq_owned_by_domain(irq, current->domain)) style > + write_mask |= bit_mask; > + } > + > + /* > + * Read in the original value. We'll use this to ensure that we maintain > + * the bit values for any bits not actively controlled by this domain. > Note > + * that we can perform this read without side effects, so this shouldn't > + * change the actual operation being performed. > + */ > + raw_value = readl(tegra_ictlr_base + register_offset); > + > + /* Merge in the bit values the guest is allowed to write. */ Please rephrase to: Remove bits that the guest is allowed to write. > + raw_value &= ~write_mask; > + raw_value |= (write_mask & new_value); > + > + /* Finally perform the write. */ > + writel(raw_value, tegra_ictlr_base + register_offset); > + return 1; > +} > + > + > +/** > + * MMIO operations for Tegra chips. These allow the hwdom 'direct' access to > + * the sections of the legacy interrupt controller that correspond to its > + * devices, but disallow access to the sections controlled by other domains > + * or by Xen. > + */ > +static struct mmio_handler_ops tegra_mmio_ops_ictlr = { > + .read = tegra_ictlr_domain_read, > + .write = tegra_ictlr_domain_write, > +}; > + > + > +/** > + * Set up the hardware domain for the Tegra, giving it mediated access to the > + * platform's legacy interrupt controller. > + */ > +static int tegra_specific_mapping(struct domain *d) > +{ > + int rc; > + unsigned long pfn_start, pfn_end; > + > + pfn_start = paddr_to_pfn(TEGRA_ICTLR_BASE); > + pfn_end = DIV_ROUND_UP(TEGRA_ICTLR_BASE + (TEGRA_ICTLR_SIZE * > TEGRA_ICTLR_COUNT), PAGE_SIZE); > + > + /* Force all access to the ictlr to go through our mediators. */ > + rc = iomem_deny_access(d, pfn_start, pfn_end); > + if (rc) > + panic("Could not deny access to the Tegra LIC iomem!\n"); > + rc = unmap_mmio_regions(d, _gfn(pfn_start), pfn_end - pfn_start + 1, > + _mfn(pfn_start)); > + if (rc) > + panic("Could not deny access to the Tegra LIC!\n"); > + > + register_mmio_handler(d, &tegra_mmio_ops_ictlr, > + TEGRA_ICTLR_BASE, TEGRA_ICTLR_SIZE * > TEGRA_ICTLR_COUNT, NULL); It is best to avoid mapping the TEGRA_ICTLR_BASE MMIO region to begin with, rather than mapping it, then unmapping it. Also .specific_mapping is actually for... mapping, not for unmapping and emulating :-) I would consider moving the emulator code out of this file into a proper separate emulator file, such as xen/arch/arm/vuart.c. Or at least initialize it from tegra_init. > + return 0; > +} > + > + > +/** > * Initialize the Tegra legacy interrupt controller, placing each interrupt > * into a default state. These defaults ensure that stray interrupts don't > * affect Xen. > @@ -296,17 +510,7 @@ static int > tegra_initialize_legacy_interrupt_controller(void) > */ > static int tegra_init(void) > { > - int rc; > - > - rc = tegra_initialize_legacy_interrupt_controller(); > - > - /* > - * TODO: Virtualize the MMIO regions for the ictlr, ensuring the hwdom > - * (and possibly passhtrough domains) can only access ictlr registers for > - * interrupts they actually own. > - */ > - > - return rc; > + return tegra_initialize_legacy_interrupt_controller(); > } Please remove this change from this patch and fold it into patch #4. > @@ -336,4 +540,5 @@ PLATFORM_START(tegra, "Tegra") > .irq_for_device = tegra_irq_for_device, > .route_irq_to_xen = tegra_route_irq_to_xen, > .route_irq_to_guest = tegra_route_irq_to_guest, > + .specific_mapping = tegra_specific_mapping, > PLATFORM_END _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx https://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |