[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH v4 13/16] xen/arm: Add support for GIC v3



Hi Vijay,

On 05/26/2014 11:26 AM, vijay.kilari@xxxxxxxxx wrote:
> From: Vijaya Kumar K <Vijaya.Kumar@xxxxxxxxxxxxxxxxxx>
> 
> Add support for GIC v3 specification System register access(SRE)
> is enabled to access cpu and virtual interface regiseters based

s/regiseters/register/

[..]

> +#include <xen/config.h>
> +#include <xen/lib.h>
> +#include <xen/init.h>
> +#include <xen/cpu.h>
> +#include <xen/mm.h>
> +#include <xen/irq.h>
> +#include <xen/sched.h>
> +#include <xen/errno.h>
> +#include <xen/delay.h>
> +#include <xen/device_tree.h>
> +#include <xen/libfdt/libfdt.h>

NIT: I don't think you need this include...

> +#include <asm/p2m.h>
> +#include <asm/domain.h>
> +#include <asm/platform.h>

Same here.

> +#include <asm/io.h>
> +#include <asm/device.h>
> +#include <asm/gic.h>
> +#include <asm/gic_v3_defs.h>
> +
> +struct rdist_region {
> +    paddr_t base;
> +    paddr_t size;
> +    void __iomem *map_base;
> +};
> +
> +/* Global state */
> +static struct {
> +    paddr_t dbase;            /* Address of distributor registers */
> +    paddr_t dbase_size;
> +    void __iomem *map_dbase;  /* Mapped address of distributor registers */
> +    struct rdist_region *rdist_regions;
> +    uint32_t  rdist_stride;
> +    unsigned int rdist_count; /* Number of rdist regions count */
> +    unsigned int nr_priorities;
> +    spinlock_t lock;
> +}gicv3;

Missing after the brace.

> +
> +static struct gic_info gicv3_info;


> +/*
> + * System Register Enable (SRE). Enable to access CPU & Virtual
> + * interface registers as system registers in EL2
> + */
> +static void gicv3_enable_sre(void)
> +{
> +    uint32_t val;
> +
> +    val = READ_SYSREG32(ICC_SRE_EL2);
> +    val |= GICC_SRE_EL2_SRE | GICC_SRE_EL2_ENEL1 |
> +           GICC_SRE_EL2_DFB | GICC_SRE_EL2_DIB;

Please add a bit of comment explaining why you choose to enable those bits.

It's clear for SRE and ENEL1 but not DFB and DIB...

[..]

> +static void gicv3_save_state(struct vcpu *v)
> +{
> +
> +    /* No need for spinlocks here because interrupts are disabled around
> +     * this call and it only accesses struct vcpu fields that cannot be
> +     * accessed simultaneously by another pCPU.
> +     */
> +    dsb(sy);

Why a dsb here? I don't think it's useful. Hence it was not there on v3.

> +    gicv3_save_lr(gicv3_info.nr_lrs, v);

You are saving multiple LRs, so gicv3_save_lrs sounds a better name.

[..]

> +static void gicv3_disable_irq(struct irq_desc *irqd)
> +{
> +    /* Disable routing */
> +    if ( irqd->irq < NR_GIC_LOCAL_IRQS )
> +        writel_relaxed((1u << irqd->irq),
> +                       GICD_RDIST_SGI_BASE + GICR_ICENABLER0);
> +    else
> +        writel_relaxed(1u << (irqd->irq % 32),
> +                       GICD + GICD_ICENABLER + ((irqd->irq / 32) * 4));

Don't you forget gicv3_wait_for_rwp here?

The code of gicv3_disable_irq is very similar to gicv3_enable_irq. I
would create an helper here (see gic_poke_irq) on GICv3 for Linux.

> +}
> +
> +static void gicv3_eoi_irq(struct irq_desc *irqd)
> +{
> +    /* Lower the priority */
> +    WRITE_SYSREG32(irqd->irq, ICC_EOIR1_EL1);

Write to a sysreg register requires an isb after.

> +}
> +
> +static void gicv3_dir_irq(int irq)
> +{
> +    /* Deactivate */
> +    WRITE_SYSREG32(irq, ICC_DIR_EL1);

isb();

> +}
> +
> +static unsigned int gicv3_read_irq(void)
> +{
> +    return (READ_SYSREG32(ICC_IAR1_EL1) & GICC_IA_IRQ);
> +}
> +
> +static inline uint64_t gicv3_mpidr_to_affinity(uint64_t mpidr)

As said on V3, you are using
gicv3_mpidr_to_affinity(cpu_logical_map(foo)) every where.
You can rework this function to get a cpu in parameter and return the
affinity.

It will avoid you duplicate code.

> +{
> +     return (MPIDR_AFFINITY_LEVEL(mpidr, 3) << 32 |
> +             MPIDR_AFFINITY_LEVEL(mpidr, 2) << 16 |
> +             MPIDR_AFFINITY_LEVEL(mpidr, 1) << 8  |
> +             MPIDR_AFFINITY_LEVEL(mpidr, 0));
> +}
> +
> +static void gicv3_set_irq_properties(struct irq_desc *desc,
> +                                     const cpumask_t *cpu_mask,
> +                                     unsigned int priority)
> +{
> +    unsigned long flags;
> +    uint32_t cfg, edgebit;
> +    uint64_t affinity;
> +    void __iomem *base;
> +    unsigned int cpu = gicv3_get_cpu_from_mask(cpu_mask);
> +    unsigned int irq = desc->irq;
> +    unsigned int type = desc->arch.type;
> +
> +    spin_lock_irqsave(&gicv3.lock, flags);
> +
> +    /* SGI's are always edge-triggered not need to call GICD_ICFGR0 */
> +    if ( irq >= NR_GIC_SGI )
> +    {

gicv3_set_irq_properties is never called on SGI. Except adding another
indentation this is not useful.

> +        if ( irq < NR_GIC_LOCAL_IRQS)
> +            base = GICD + GICD_ICFGR + (irq / 16) * 4;
> +        else
> +            base = GICD_RDIST_SGI_BASE + GICR_ICFGR1;
> +
> +        cfg = readl_relaxed(base);
> +
> +        edgebit = 2u << (2 * (irq % 16));
> +        if ( type & DT_IRQ_TYPE_LEVEL_MASK )
> +           cfg &= ~edgebit;
> +        else if ( type & DT_IRQ_TYPE_EDGE_BOTH )
> +           cfg |= edgebit;
> +
> +        writel_relaxed(cfg, base);
> +    }
> +
> +    affinity = gicv3_mpidr_to_affinity(cpu_logical_map(cpu));
> +    /* Make sure we don't broadcast the interrupt */
> +    affinity &= ~GICD_IROUTER_SPI_MODE_ANY;
> +
> +    if ( irq >= NR_GIC_LOCAL_IRQS )

Sometimes you use this define and sometime the hardcode 32... Please be
consistent.

[..]

> +static int gicv3_enable_redist(void)
> +{
> +    uint32_t val;
> +    bool_t timeout = 0;
> +    s_time_t deadline = NOW() + MILLISECS(1000);
> +
> +    /* Wake up this CPU redistributor */
> +    val = readl_relaxed(GICD_RDIST_BASE + GICR_WAKER);
> +    val &= ~GICR_WAKER_ProcessorSleep;
> +    writel_relaxed(val, GICD_RDIST_BASE + GICR_WAKER);
> +
> +    do {
> +        val = readl_relaxed(GICD_RDIST_BASE + GICR_WAKER);
> +        if ( !(val & GICR_WAKER_ChildrenAsleep) )
> +            break;
> +        if ( NOW() > deadline )
> +        {
> +            timeout = 1;
> +            break;
> +        }
> +        cpu_relax();
> +        udelay(1);
> +    } while ( timeout );
> +
> +    if ( timeout )
> +        dprintk(XENLOG_ERR, "GICv3: Redist enable RWP timeout\n");
> +
> +    return 0;

Shouldn't you return an error if we timeout?

> +}
> +
> +static int __init gicv3_populate_rdist(void)
> +{
> +    int i;
> +    uint32_t aff;
> +    uint32_t reg;
> +    uint64_t typer;
> +    uint64_t mpidr = cpu_logical_map(smp_processor_id());
> +
> +    aff = gicv3_mpidr_to_affinity(mpidr);

This function is returning an uint64_t but you are using a uint32_t to
store the value. Is it normal?

> +
> +    for ( i = 0; i < gicv3.rdist_count; i++ )
> +    {
> +        void __iomem *ptr = gicv3.rdist_regions[i].map_base;
> +
> +        reg = readl_relaxed(ptr + GICR_PIDR2) & GICR_PIDR2_ARCH_MASK;
> +        if ( reg  != (GICV3_GICR_PIDR2 & GICR_PIDR2_ARCH_MASK) )

GICV3_GICR_PIDR2 is used for the emulation. You can't use this value to
test hardware stuff. Please use 0x30 (GICv3) which is clearer.

> +        {
> +            dprintk(XENLOG_ERR,
> +                    "GICv3: No redistributor present @%"PRIpaddr"\n",
> +                    gicv3.rdist_regions[i].base);
> +            break;
> +        }
> +
> +        do {
> +            typer = readq_relaxed(ptr + GICR_TYPER);
> +
> +            if ( (typer >> 32) == aff )
> +            {

Hrrmmm... now I understand why aff is 32 bits. Computing manually
(rather than using gicv3_mpidr_to_affinity function) was right on v3.
This was because TYPER doesn't exactly match the affinity field.

While you add again the correct affinity, please also add a comment such
as: "Convert affinity to a 32bit value that can be matched to GICR_TYPER
bits [63:32]."

> +                this_cpu(rbase) = ptr;
> +                printk("GICv3: CPU%d: Found redistributor in region %d 
> @%p\n",
> +                        smp_processor_id(), i, ptr);
> +                return 0;
> +            }
> +
> +            ptr += gicv3.rdist_stride;
> +            if ( typer & GICR_TYPER_VLPIS )
> +                    ptr += SZ_64K * 2; /* Skip VLPI_base + reserved page */

You differ from Linux code here (even though VLPIS is RAZ on GIcv3). The
Linux driver is only check VLPIS when rdist_stride is equal to 0.

Actually, you set rdist_stride to SZ_64K * 2 if the property is not
existent. I guess you have to check with Marc Zyngier what's the correct
solution.

[..]

> +    /* No priority grouping */
> +    WRITE_SYSREG32(0, ICC_BPR1_EL1);
> +
> +    /* Set priority mask register */
> +    WRITE_SYSREG32(DEFAULT_PMR_VALUE, ICC_PMR_EL1);
> +
> +    /* EOI drops priority too (mode 0) */
> +    WRITE_SYSREG32(GICC_CTLR_EL1_EOImode_drop, ICC_CTLR_EL1);

isb() ?

> +
> +    /* Enable Group1 interrupts */
> +    WRITE_SYSREG32(1, ICC_IGRPEN1_EL1);

isb() ?

> +
> +    return 0;
> +}
> +
> +static void gicv3_cpu_disable(void)
> +{
> +    WRITE_SYSREG32(0, ICC_CTLR_EL1);

isb() ?

> +}
> +
> +static void __cpuinit gicv3_hyp_init(void)
> +{
> +    uint32_t vtr;
> +
> +    vtr = READ_SYSREG32(ICH_VTR_EL2);
> +    gicv3_info.nr_lrs  = (vtr & GICH_VTR_NRLRGS) + 1;
> +    gicv3.nr_priorities = ((vtr >> GICH_VTR_PRIBITS_SHIFT) &
> +                          GICH_VTR_PRIBITS_MASK) + 1;
> +
> +    ASSERT((gicv3.nr_priorities > 4 && gicv3.nr_priorities < 8));

For hardware "feature" checking, it's better to print a correct error
message than ASSERT (which is not enabled on non-debug build).

> +
> +    WRITE_SYSREG32(GICH_VMCR_EOI | GICH_VMCR_VENG1, ICH_VMCR_EL2);
> +    WRITE_SYSREG32(GICH_HCR_EN, ICH_HCR_EL2);

isb()?

[..]

> +static void __cpuinit gicv3_hyp_disable(void)
> +{
> +    uint32_t vtr;
> +
> +    vtr = READ_SYSREG32(ICH_HCR_EL2);
> +    vtr &= ~0x1;
> +    WRITE_SYSREG32( vtr, ICH_HCR_EL2);

isb() ?

[..]

> +static void gicv3_send_sgi(const cpumask_t *cpumask, enum gic_sgi sgi,
> +                           uint8_t mode)
> +{
> +    int cpu = 0;
> +    uint64_t val;
> +
> +    dsb(sy);

Why a dsb there?

[..]

> +static void gicv3_update_lr(int lr, const struct pending_irq *p,
> +                            unsigned int state)
> +{
> +    uint64_t grp = GICH_LR_GRP1;
> +    uint64_t val = 0;
> +
> +    BUG_ON(lr >= gicv3_info.nr_lrs);
> +    BUG_ON(lr < 0);
> +
> +    val =  (((uint64_t)state & 0x3) << GICH_LR_STATE_SHIFT) | grp;
> +    val |= ((uint64_t)p->priority & 0xff) << GICH_LR_PRIORITY_SHIFT;
> +    val |= ((uint64_t)p->irq & GICH_LR_VIRTUAL_MASK) << 
> GICH_LR_VIRTUAL_SHIFT;
> +
> +   if ( p->desc != NULL )
> +       val |= GICH_LR_HW | (((uint64_t)p->desc->irq & GICH_LR_PHYSICAL_MASK)
> +                           << GICH_LR_PHYSICAL_SHIFT);
> +
> +    gicv3_ich_write_lr(lr, val);

isb() ? Might be better directly in gicv3_ich_write_lr.

> +}
> +
> +static void gicv3_clear_lr(int lr)
> +{
> +    gicv3_ich_write_lr(lr, 0);

Same question here and ...

[..]

> +static void gicv3_write_lr(int lr_reg, const struct gic_lr *lr)
> +{
> +    uint64_t lrv = 0;
> +
> +    lrv = ( ((u64)(lr->pirq & GICH_LR_PHYSICAL_MASK) << 
> GICH_LR_PHYSICAL_SHIFT)|
> +        ((u64)(lr->virq & GICH_LR_VIRTUAL_MASK)  << GICH_LR_VIRTUAL_SHIFT) |
> +        ((u64)(lr->priority & GICH_LR_PRIORITY_MASK) << 
> GICH_LR_PRIORITY_SHIFT)|
> +        ((u64)(lr->state & GICH_LR_STATE_MASK) << GICH_LR_STATE_SHIFT) |
> +        ((u64)(lr->hw_status & GICH_LR_HW_MASK) << GICH_LR_HW_SHIFT)  |
> +        ((u64)(lr->grp & GICH_LR_GRP_MASK) << GICH_LR_GRP_SHIFT) );
> +
> +    gicv3_ich_write_lr(lr_reg, lrv);

here.

> +}
> +
> +static int gicv_v3_init(struct domain *d)
> +{
> +    int i;
> +
> +    /*
> +     * Domain 0 gets the hardware address.
> +     * Guests get the virtual platform layout.
> +     */
> +    if ( d->domain_id == 0 )

is_hardware_domain(d)

[..]

> +static void gicv3_hcr_status(uint32_t flag, bool_t status)
> +{
> +    uint32_t hcr;
> +
> +    hcr = READ_SYSREG32(ICH_HCR_EL2);
> +    if ( status )
> +        WRITE_SYSREG32(hcr | flag, ICH_HCR_EL2);
> +    else
> +        WRITE_SYSREG32(hcr & (~flag), ICH_HCR_EL2);

isb()?

> +static void gicv3_host_irq_end(struct irq_desc *desc)
> +{
> +    /* Lower the priority */
> +    gicv3_eoi_irq(desc);
> +    /* Deactivate */
> +    gicv3_dir_irq(desc->irq);

You are not consistent between the 2 function. One is taking an irq_desc
the other an unsigned int.

[..]

> +const static hw_irq_controller gicv3_host_irq_type = {

[..]

> +const static hw_irq_controller gicv3_guest_irq_type = {

[..]

> +const static struct gic_hw_operations gicv3_ops = {

We tend to use static const rather than "const static".

> +
> +/* Set up the GIC */
> +static int __init gicv3_init(struct dt_device_node *node, const void *data)
> +{
> +    struct rdist_region *rdist_regs;
> +    int res, i;
> +    uint32_t reg;
> +
> +    dt_device_set_used_by(node, DOMID_XEN);
> +
> +    res = dt_device_get_address(node, 0, &gicv3.dbase, &gicv3.dbase_size);
> +    if ( res || !gicv3.dbase )
> +        panic("GICv3: Cannot find a valid distributor address");
> +
> +    if ( (gicv3.dbase & ~PAGE_MASK) || (gicv3.dbase_size & ~PAGE_MASK) )
> +        panic("GICv3:  Found unaligned distributor address %"PRIpaddr"",
> +              gicv3.dbase);
> +
> +    gicv3.map_dbase = ioremap_nocache(gicv3.dbase, gicv3.dbase_size);
> +    if ( !gicv3.map_dbase )
> +        panic("GICv3: Failed to ioremap for GIC distributor\n");
> +
> +    reg = readl_relaxed(GICD + GICD_PIDR2) & GICD_PIDR2_ARCH_MASK;
> +    if ( reg  != (GICV3_GICD_PIDR2 & GICD_PIDR2_ARCH_MASK) )
> +         panic("GICv3: no distributor detected\n");
> +
> +    if ( !dt_property_read_u32(node, "#redistributor-regions",
> +                &gicv3.rdist_count) )
> +        gicv3.rdist_count = 1;
> +
> +    if ( gicv3.rdist_count > MAX_RDIST_COUNT )
> +        panic("GICv3: Number of redistributor regions is more than \

You have to end the line with a " then beginning the new one with a ".
With your solution the compiler will drop append the second line from
the beginning, i.e with all the space...

> +               %d (Increase MAX_RDIST_COUNT!!)\n", MAX_RDIST_COUNT);
> +
> +    rdist_regs = xzalloc_array(struct rdist_region, gicv3.rdist_count);
> +    if ( !rdist_regs )
> +        panic("GICv3: Failed to allocate memory for rdist regions\n");
> +
> +    for ( i = 0; i < gicv3.rdist_count; i++ )
> +    {
> +        uint64_t rdist_base, rdist_size;
> +
> +        res = dt_device_get_address(node, 1 + i, &rdist_base, &rdist_size);
> +        if ( res || !rdist_base )
> +            panic("GICv3: No rdist base found for region %d\n", i);
> +
> +        rdist_regs[i].base = rdist_base;
> +        rdist_regs[i].size = rdist_size;
> +    }
> +
> +    /* If stride is not set in dt. Set default to 2 * SZ_64K */
> +    if ( !dt_property_read_u32(node, "redistributor-stride", 
> &gicv3.rdist_stride) )
> +        gicv3.rdist_stride = 2 * SZ_64K;

I don't think this is right, see my comment a bit above for the function
gicv3_populate_rdist.

[..]

> diff --git a/xen/include/xen/lib.h b/xen/include/xen/lib.h
> index 1369b2b..499a798 100644
> --- a/xen/include/xen/lib.h
> +++ b/xen/include/xen/lib.h
> @@ -67,6 +67,8 @@ do {                                                        
>     \
>  
>  #define reserve_bootmem(_p,_l) ((void)0)
>  
> +#define SZ_64K  0x00010000
> +
>  struct domain;
>  
>  void cmdline_parse(const char *cmdline);
> 

Same remark as on patch #10, you have to cc the relevant maintenairs for
every file you've modified (cced them).

You can use script/get_maintainers.pl for this purpose.

Regards,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.