[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH 1/2] ARM: ITS: implement quirks and add support for Renesas Gen4 ITS
On Fri, 13 Dec 2024, Mykyta Poturai wrote: > From: Oleksandr Andrushchenko <oleksandr_andrushchenko@xxxxxxxx> > > There are number of ITS implementations exist which are different from > the base one which have number of functionalities defined as is > "IMPLEMENTATION DEFINED", e.g. there may exist differences in cacheability, > shareability and memory requirements and others. This requires > appropriate handling of such HW requirements which are implemented as > ITS quirks: GITS_IIDR (ITS Implementer Identification Register) is used to > differentiate the ITS implementations and select appropriate set of > quirks if any. > > As an example of such ITSes add quirk implementation for Renesas Gen4 ITS: > - add possibility to override default cacheability and shareability > settings used for ITS memory allocations; > - add possibility to allocate memory used by ITS with specific memory > requirements: introduce _x{z|m}alloc_whole_pages functions and free the > memory with xfree as usual. > > The idea of the quirk implementation is inspired by the Linux kernel ITS > quirk implementation [1]. > > Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@xxxxxxxx> > Signed-off-by: Mykyta Poturai <mykyta_poturai@xxxxxxxx> > > [1] > https://elixir.bootlin.com/linux/v5.16.1/source/drivers/irqchip/irq-gic-v3-its.c > --- > xen/arch/arm/gic-v3-its.c | 90 ++++++++++++++++++++++++--- > xen/arch/arm/gic-v3-lpi.c | 14 +++-- > xen/arch/arm/include/asm/gic_v3_its.h | 8 +++ > xen/arch/arm/vgic-v3-its.c | 8 +-- > xen/common/xmalloc_tlsf.c | 18 +++++- > xen/include/xen/xmalloc.h | 4 ++ > 6 files changed, 122 insertions(+), 20 deletions(-) > > diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c > index 5fd83af25a..b1a2cdccc5 100644 > --- a/xen/arch/arm/gic-v3-its.c > +++ b/xen/arch/arm/gic-v3-its.c > @@ -50,6 +50,77 @@ struct its_device { > struct pending_irq *pend_irqs; /* One struct per event */ > }; > > +/* > + * It is unlikely that a platform implements ITSes with different quirks, > + * so assume they all share the same. > + */ > +struct its_quirk { > + const char *desc; > + bool (*init)(struct host_its *hw_its); > + uint32_t iidr; > + uint32_t mask; > +}; > + > +uint32_t its_quirk_flags; static > +static bool gicv3_its_enable_quirk_gen4(struct host_its *hw_its) > +{ > + its_quirk_flags |= HOST_ITS_WORKAROUND_NC_NS | > + HOST_ITS_WORKAROUND_32BIT_ADDR; > + > + return true; > +} > + > +static const struct its_quirk its_quirks[] = { > + { > + .desc = "R-Car Gen4", > + .iidr = 0x0201743b, > + .mask = 0xffffffff, > + .init = gicv3_its_enable_quirk_gen4, > + }, > + { > + /* Sentinel. */ > + } > +}; > + > +static void gicv3_its_enable_quirks(struct host_its *hw_its) > +{ > + const struct its_quirk *quirks = its_quirks; > + uint32_t iidr = readl_relaxed(hw_its->its_base + GITS_IIDR); > + > + for ( ; quirks->desc; quirks++ ) > + { > + if ( quirks->iidr != (quirks->mask & iidr) ) > + continue; > + if ( quirks->init(hw_its) ) > + printk("GICv3: enabling workaround for ITS: %s\n", quirks->desc); > + } > +} > + > +uint64_t gicv3_its_get_cacheability(void) > +{ > + if ( its_quirk_flags & HOST_ITS_WORKAROUND_NC_NS ) > + return GIC_BASER_CACHE_nC; > + > + return GIC_BASER_CACHE_RaWaWb; > +} > + > +uint64_t gicv3_its_get_shareability(void) > +{ > + if ( its_quirk_flags & HOST_ITS_WORKAROUND_NC_NS ) > + return GIC_BASER_NonShareable; > + > + return GIC_BASER_InnerShareable; > +} > + > +unsigned int gicv3_its_get_memflags(void) > +{ > + if ( its_quirk_flags & HOST_ITS_WORKAROUND_32BIT_ADDR ) > + return MEMF_bits(32); > + > + return 0; > +} > + > bool gicv3_its_host_has_its(void) > { > return !list_empty(&host_its_list); > @@ -291,11 +362,12 @@ static void *its_map_cbaser(struct host_its *its) > uint64_t reg; > void *buffer; > > - reg = GIC_BASER_InnerShareable << GITS_BASER_SHAREABILITY_SHIFT; > + reg = gicv3_its_get_shareability() << GITS_BASER_SHAREABILITY_SHIFT; > reg |= GIC_BASER_CACHE_SameAsInner << > GITS_BASER_OUTER_CACHEABILITY_SHIFT; > - reg |= GIC_BASER_CACHE_RaWaWb << GITS_BASER_INNER_CACHEABILITY_SHIFT; > + reg |= gicv3_its_get_cacheability() << > GITS_BASER_INNER_CACHEABILITY_SHIFT; > > - buffer = _xzalloc(ITS_CMD_QUEUE_SZ, SZ_64K); > + buffer = _xzalloc_whole_pages(ITS_CMD_QUEUE_SZ, SZ_64K, > + gicv3_its_get_memflags()); > if ( !buffer ) > return NULL; > > @@ -342,9 +414,9 @@ static int its_map_baser(void __iomem *basereg, uint64_t > regc, > unsigned int table_size; > void *buffer; > > - attr = GIC_BASER_InnerShareable << GITS_BASER_SHAREABILITY_SHIFT; > + attr = gicv3_its_get_shareability() << GITS_BASER_SHAREABILITY_SHIFT; > attr |= GIC_BASER_CACHE_SameAsInner << > GITS_BASER_OUTER_CACHEABILITY_SHIFT; > - attr |= GIC_BASER_CACHE_RaWaWb << GITS_BASER_INNER_CACHEABILITY_SHIFT; > + attr |= gicv3_its_get_cacheability() << > GITS_BASER_INNER_CACHEABILITY_SHIFT; > > /* > * Setup the BASE register with the attributes that we like. Then read > @@ -357,7 +429,8 @@ retry: > /* The BASE registers support at most 256 pages. */ > table_size = min(table_size, 256U << BASER_PAGE_BITS(pagesz)); > > - buffer = _xzalloc(table_size, BIT(BASER_PAGE_BITS(pagesz), UL)); > + buffer = _xzalloc_whole_pages(table_size, BIT(BASER_PAGE_BITS(pagesz), > UL), > + gicv3_its_get_memflags()); > if ( !buffer ) > return -ENOMEM; > > @@ -453,6 +526,8 @@ static int gicv3_its_init_single_its(struct host_its > *hw_its) > if ( ret ) > return ret; > > + gicv3_its_enable_quirks(hw_its); We have an issue here because gicv3_its_enable_quirks is called for every ITS in the system, setting its_quirk_flags as appropriate, but there is only one its_quirk_flags. In the unlikely case of multiple ITSes, this wouldn't work. One option is to call gicv3_its_enable_quirks only for the first ITS and assume the others are the same (ideally we would check and BUG_ON if it is not the case). Another option is to have one its_quirk_flags per ITS. > reg = readq_relaxed(hw_its->its_base + GITS_TYPER); > hw_its->devid_bits = GITS_TYPER_DEVICE_ID_BITS(reg); > hw_its->evid_bits = GITS_TYPER_EVENT_ID_BITS(reg); > @@ -681,7 +756,8 @@ int gicv3_its_map_guest_device(struct domain *d, > ret = -ENOMEM; > > /* An Interrupt Translation Table needs to be 256-byte aligned. */ > - itt_addr = _xzalloc(nr_events * hw_its->itte_size, 256); > + itt_addr = _xzalloc_whole_pages(nr_events * hw_its->itte_size, 256, > + gicv3_its_get_memflags()); > if ( !itt_addr ) > goto out_unlock; > > diff --git a/xen/arch/arm/gic-v3-lpi.c b/xen/arch/arm/gic-v3-lpi.c > index de4b0eb4a4..72b725a624 100644 > --- a/xen/arch/arm/gic-v3-lpi.c > +++ b/xen/arch/arm/gic-v3-lpi.c > @@ -237,7 +237,8 @@ static int gicv3_lpi_allocate_pendtable(unsigned int cpu) > * The GICv3 imposes a 64KB alignment requirement, also requires > * physically contiguous memory. > */ > - pendtable = _xzalloc(lpi_data.max_host_lpi_ids / 8, SZ_64K); > + pendtable = _xmalloc_whole_pages(lpi_data.max_host_lpi_ids / 8, SZ_64K, > + gicv3_its_get_memflags()); > if ( !pendtable ) > return -ENOMEM; > > @@ -272,9 +273,9 @@ static int gicv3_lpi_set_pendtable(void __iomem > *rdist_base) > > ASSERT(!(virt_to_maddr(pendtable) & ~GENMASK(51, 16))); > > - val = GIC_BASER_CACHE_RaWaWb << GICR_PENDBASER_INNER_CACHEABILITY_SHIFT; > + val = gicv3_its_get_cacheability() << > GICR_PENDBASER_INNER_CACHEABILITY_SHIFT; > val |= GIC_BASER_CACHE_SameAsInner << > GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT; > - val |= GIC_BASER_InnerShareable << GICR_PENDBASER_SHAREABILITY_SHIFT; > + val |= gicv3_its_get_shareability() << GICR_PENDBASER_SHAREABILITY_SHIFT; > val |= GICR_PENDBASER_PTZ; > val |= virt_to_maddr(pendtable); > > @@ -301,9 +302,9 @@ static int gicv3_lpi_set_proptable(void __iomem * > rdist_base) > { > uint64_t reg; > > - reg = GIC_BASER_CACHE_RaWaWb << GICR_PROPBASER_INNER_CACHEABILITY_SHIFT; > + reg = gicv3_its_get_cacheability() << > GICR_PROPBASER_INNER_CACHEABILITY_SHIFT; > reg |= GIC_BASER_CACHE_SameAsInner << > GICR_PROPBASER_OUTER_CACHEABILITY_SHIFT; > - reg |= GIC_BASER_InnerShareable << GICR_PROPBASER_SHAREABILITY_SHIFT; > + reg |= gicv3_its_get_shareability() << GICR_PROPBASER_SHAREABILITY_SHIFT; > > /* > * The property table is shared across all redistributors, so allocate > @@ -312,7 +313,8 @@ static int gicv3_lpi_set_proptable(void __iomem * > rdist_base) > if ( !lpi_data.lpi_property ) > { > /* The property table holds one byte per LPI. */ > - void *table = _xmalloc(lpi_data.max_host_lpi_ids, SZ_4K); > + void *table = _xmalloc_whole_pages(lpi_data.max_host_lpi_ids, SZ_4K, > + gicv3_its_get_memflags()); > > if ( !table ) > return -ENOMEM; > diff --git a/xen/arch/arm/include/asm/gic_v3_its.h > b/xen/arch/arm/include/asm/gic_v3_its.h > index c24d4752d0..0737e67aa6 100644 > --- a/xen/arch/arm/include/asm/gic_v3_its.h > +++ b/xen/arch/arm/include/asm/gic_v3_its.h > @@ -110,6 +110,9 @@ > #define HOST_ITS_FLUSH_CMD_QUEUE (1U << 0) > #define HOST_ITS_USES_PTA (1U << 1) > > +#define HOST_ITS_WORKAROUND_NC_NS (1U << 0) > +#define HOST_ITS_WORKAROUND_32BIT_ADDR (1U << 1) > + > /* We allocate LPIs on the hosts in chunks of 32 to reduce handling > overhead. */ > #define LPI_BLOCK 32U > > @@ -197,6 +200,11 @@ struct pending_irq *gicv3_assign_guest_event(struct > domain *d, > void gicv3_lpi_update_host_entry(uint32_t host_lpi, int domain_id, > uint32_t virt_lpi); > > +/* ITS quirks handling. */ > +uint64_t gicv3_its_get_cacheability(void); > +uint64_t gicv3_its_get_shareability(void); > +unsigned int gicv3_its_get_memflags(void); > + > #else > > #ifdef CONFIG_ACPI > diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c > index c65c1dbf52..f5deb18497 100644 > --- a/xen/arch/arm/vgic-v3-its.c > +++ b/xen/arch/arm/vgic-v3-its.c > @@ -1191,7 +1191,7 @@ static void sanitize_its_base_reg(uint64_t *reg) > { > case GIC_BASER_OuterShareable: > r &= ~GITS_BASER_SHAREABILITY_MASK; > - r |= GIC_BASER_InnerShareable << GITS_BASER_SHAREABILITY_SHIFT; > + r |= gicv3_its_get_shareability() << GITS_BASER_SHAREABILITY_SHIFT; > break; > default: > break; > @@ -1203,7 +1203,7 @@ static void sanitize_its_base_reg(uint64_t *reg) > case GIC_BASER_CACHE_nCnB: > case GIC_BASER_CACHE_nC: > r &= ~GITS_BASER_INNER_CACHEABILITY_MASK; > - r |= GIC_BASER_CACHE_RaWb << GITS_BASER_INNER_CACHEABILITY_SHIFT; > + r |= gicv3_its_get_cacheability() << > GITS_BASER_INNER_CACHEABILITY_SHIFT; gicv3_its_get_cacheability returns GIC_BASER_CACHE_RaWaWb, because that is the value that was previously present in all the other cases. In this case, however, we end up replacing GIC_BASER_CACHE_RaWb with GIC_BASER_CACHE_RaWaWb. Is that OK? > break; > default: > break; > @@ -1455,9 +1455,9 @@ static int vgic_v3_its_init_virtual(struct domain *d, > paddr_t guest_addr, > if ( !its ) > return -ENOMEM; > > - base_attr = GIC_BASER_InnerShareable << GITS_BASER_SHAREABILITY_SHIFT; > + base_attr = gicv3_its_get_shareability() << > GITS_BASER_SHAREABILITY_SHIFT; > base_attr |= GIC_BASER_CACHE_SameAsInner << > GITS_BASER_OUTER_CACHEABILITY_SHIFT; > - base_attr |= GIC_BASER_CACHE_RaWaWb << > GITS_BASER_INNER_CACHEABILITY_SHIFT; > + base_attr |= gicv3_its_get_cacheability() << > GITS_BASER_INNER_CACHEABILITY_SHIFT; > > its->cbaser = base_attr; > base_attr |= 0ULL << GITS_BASER_PAGE_SIZE_SHIFT; /* 4K pages */ > diff --git a/xen/common/xmalloc_tlsf.c b/xen/common/xmalloc_tlsf.c > index 5e55fc463e..f7f00db044 100644 > --- a/xen/common/xmalloc_tlsf.c > +++ b/xen/common/xmalloc_tlsf.c > @@ -537,14 +537,15 @@ static void cf_check xmalloc_pool_put(void *p) > free_xenheap_page(p); > } > > -static void *xmalloc_whole_pages(unsigned long size, unsigned long align) > +void *_xmalloc_whole_pages(unsigned long size, unsigned long align, > + unsigned int memflags) > { > unsigned int i, order; > void *res, *p; > > order = get_order_from_bytes(max(align, size)); > > - res = alloc_xenheap_pages(order, 0); > + res = alloc_xenheap_pages(order, memflags); > if ( res == NULL ) > return NULL; > > @@ -562,6 +563,17 @@ static void *xmalloc_whole_pages(unsigned long size, > unsigned long align) > return res; > } > > +void *_xzalloc_whole_pages(unsigned long size, unsigned long align, > + unsigned int memflags) > +{ > + void *p = _xmalloc_whole_pages(size, align, memflags); > + > + if ( p ) > + memset(p, 0, size); > + > + return p; > +} > + > static void tlsf_init(void) > { > xenpool = xmem_pool_create("xmalloc", xmalloc_pool_get, > @@ -628,7 +640,7 @@ void *_xmalloc(unsigned long size, unsigned long align) > if ( size < PAGE_SIZE ) > p = xmem_pool_alloc(size, xenpool); > if ( p == NULL ) > - return xmalloc_whole_pages(size - align + MEM_ALIGN, align); > + return _xmalloc_whole_pages(size - align + MEM_ALIGN, align, 0); > > /* Add alignment padding. */ > p = add_padding(p, align); > diff --git a/xen/include/xen/xmalloc.h b/xen/include/xen/xmalloc.h > index b903fa2e26..3b05e992ef 100644 > --- a/xen/include/xen/xmalloc.h > +++ b/xen/include/xen/xmalloc.h > @@ -79,6 +79,10 @@ extern void xfree(void *p); > extern void *_xmalloc(unsigned long size, unsigned long align); > extern void *_xzalloc(unsigned long size, unsigned long align); > extern void *_xrealloc(void *ptr, unsigned long size, unsigned long align); > +extern void *_xmalloc_whole_pages(unsigned long size, unsigned long align, > + unsigned int memflags); > +extern void *_xzalloc_whole_pages(unsigned long size, unsigned long align, > + unsigned int memflags); > > static inline void *_xmalloc_array( > unsigned long size, unsigned long align, unsigned long num) > -- > 2.34.1 >
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |