[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH] xen/arm: gicv2: Export GICv2m register frames to domain0 by device tree
Sorry, I forget to change to the plain text mode. On 25 April 2016 at 17:45, Stefano Stabellini <sstabellini@xxxxxxxxxx> wrote: > Hello Wei, > > could you please send email replies in plain text (rather than html)? > > Thanks, > > Stefano > > On Mon, 25 Apr 2016, Wei Chen wrote: >> Hi Julien, >> >> On 23 April 2016 at 02:29, Julien Grall <julien.grall@xxxxxxx> wrote: >> Hi Wei, >> >> On 21/04/16 09:24, Wei Chen wrote: >> This patch adds v2m extension support in GIC-v2 driver. The >> GICv2 driver >> detects the MSI frames from device tree and creates >> corresponding device >> tree nodes in Domain0's DTB. It also provides one hw_ops >> callback to map >> v2m MMIO regions to domain0 and route v2m SPIs to domain0. >> >> With this GICv2m extension support, the domain0 kernel can do >> GICv2m >> frame setup and initialization. >> >> This patch is based on the GICv2m patch of Suravee Suthikulpanit: >> [PATCH 2/2] xen/arm: gicv2: Adding support for GICv2m in Dom0 >> >> http://lists.xen.org/archives/html/xen-devel/2015-04/msg02613.html >> >> Signed-off-by: Wei Chen <Wei.Chen@xxxxxxxxxx> >> --- >> xen/arch/arm/domain_build.c | 5 + >> xen/arch/arm/gic-v2.c | 285 >> ++++++++++++++++++++++++++++++++++++++++++++ >> xen/arch/arm/gic.c | 9 ++ >> xen/include/asm-arm/gic.h | 3 + >> 4 files changed, 302 insertions(+) >> >> diff --git a/xen/arch/arm/domain_build.c >> b/xen/arch/arm/domain_build.c >> index aba714c..b5d82fc 100644 >> --- a/xen/arch/arm/domain_build.c >> +++ b/xen/arch/arm/domain_build.c >> @@ -2176,6 +2176,11 @@ int construct_dom0(struct domain *d) >> if ( rc < 0 ) >> return rc; >> >> + /* Map extra GIC MMIO, irqs and other hw stuffs to domain0. >> */ >> >> >> NIT: s/domain0/dom0/ >> >> + rc = gic_map_hwdom_extra_mappings(d); >> + if ( rc < 0 ) >> + return rc; >> + >> rc = platform_specific_mapping(d); >> if ( rc < 0 ) >> return rc; >> diff --git a/xen/arch/arm/gic-v2.c b/xen/arch/arm/gic-v2.c >> index 450755f..4a7d65a 100644 >> --- a/xen/arch/arm/gic-v2.c >> +++ b/xen/arch/arm/gic-v2.c >> @@ -21,6 +21,7 @@ >> #include <xen/lib.h> >> #include <xen/init.h> >> #include <xen/mm.h> >> +#include <xen/vmap.h> >> >> >> Why do you need to include this header? >> >> This header is used for "iounmap" API. >> >> #include <xen/irq.h> >> #include <xen/iocap.h> >> #include <xen/sched.h> >> @@ -66,6 +67,41 @@ >> #define GICH_V2_VMCR_PRIORITY_MASK 0x1f >> #define GICH_V2_VMCR_PRIORITY_SHIFT 27 >> >> +/* GICv2m extension register definitions. */ >> +/* >> +* MSI_TYPER: >> +* [31:26] Reserved >> +* [25:16] lowest SPI assigned to MSI >> +* [15:10] Reserved >> +* [9:0] Number of SPIs assigned to MSI >> +*/ >> +#define V2M_MSI_TYPER 0x008 >> +#define V2M_MSI_TYPER_BASE_SHIFT 16 >> +#define V2M_MSI_TYPER_BASE_MASK 0x3FF >> +#define V2M_MSI_TYPER_NUM_MASK 0x3FF >> +#define V2M_MSI_SETSPI_NS 0x040 >> +#define V2M_MIN_SPI 32 >> +#define V2M_MAX_SPI 1019 >> +#define V2M_MSI_IIDR 0xFCC >> + >> +#define V2M_MSI_TYPER_BASE_SPI(x) \ >> + (((x) >> V2M_MSI_TYPER_BASE_SHIFT) & >> V2M_MSI_TYPER_BASE_MASK) >> + >> +#define V2M_MSI_TYPER_NUM_SPI(x) ((x) & >> V2M_MSI_TYPER_NUM_MASK) >> + >> +struct v2m_data { >> + struct list_head entry; >> + /* Pointer to the DT node representing the v2m frame */ >> + const struct dt_device_node *dt_node; >> + paddr_t addr; /* Register frame base */ >> + paddr_t size; /* Register frame size */ >> + u32 spi_start; /* The SPI number that MSIs start */ >> + u32 nr_spis; /* The number of SPIs for MSIs */ >> +}; >> + >> +/* v2m extension register frame information list */ >> +static LIST_HEAD(gicv2m_info); >> + >> /* Global state */ >> static struct { >> void __iomem * map_dbase; /* IO mapped Address of >> distributor registers */ >> @@ -551,6 +587,173 @@ static void gicv2_irq_set_affinity(struct >> irq_desc *desc, const cpumask_t *cpu_m >> spin_unlock(&gicv2.lock); >> } >> >> +static int gicv2_map_hwdown_extra_mappings(struct domain *d) >> +{ >> + const struct v2m_data *v2m_data; >> + >> + /* >> + * For the moment, we'll assign all v2m frames to a single >> domain >> + * (i.e Domain0). >> >> >> NIT: s/Domain0/hardware domain/ >> >> Also, I would drop the i.e and say "... frames to the hardware domain." >> >> + */ >> + list_for_each_entry( v2m_data, &gicv2m_info, entry ) >> + { >> + int ret, spi; >> >> >> Please use "u32" for the variable spi. >> >> + >> + printk("GICv2: Mapping v2m frame to domain%d: addr=0x%lx >> size=0x%lx spi_base=%u num_spis=%u\n", >> >> >> NIT: You can shorten domain%d to d%d >> >> + d->domain_id, v2m_data->addr, v2m_data->size, >> + v2m_data->spi_start, v2m_data->nr_spis); >> + >> + ret = map_mmio_regions(d, paddr_to_pfn(v2m_data->addr), >> + DIV_ROUND_UP(v2m_data->size, >> PAGE_SIZE), >> + paddr_to_pfn(v2m_data->addr)); >> + if ( ret ) >> + { >> + printk(XENLOG_ERR >> + "GICv2: Map v2m frame to domain%d failed.\n", >> >> >> Ditto. >> >> Also the indentation looks wrong. >> >> It's very strange. This indentation looks well in the patch file, and all >> indentations are space chars. >> I'll try to send a revised patch. >> >> >> + d->domain_id); >> + return ret; >> + } >> + >> + /* >> + * Map all SPIs that are allocated to MSIs in a single >> frame to the >> >> >> s/in a single frame/for the frame/ >> >> + * domain. >> + */ >> + for ( spi = v2m_data->spi_start; >> + spi < (v2m_data->spi_start + v2m_data->nr_spis); >> spi++ ) >> + { >> + /* >> + * MSIs are always edge-triggered. Configure the >> associated SPIs >> + * to be edge-rising. >> + */ >> + ret = irq_set_spi_type(spi, IRQ_TYPE_EDGE_RISING); >> + if ( ret ) >> + { >> + printk(XENLOG_ERR >> + "GICv2: Failed to set v2m MSI SPI[%d] >> type.\n", spi); >> + return ret; >> + } >> + >> + /* Route a SPI that is allocated to MSI to the >> domain. */ >> + ret = route_irq_to_guest(d, spi, spi, "v2m"); >> + if ( ret ) >> + { >> + printk(XENLOG_ERR >> + "GICv2: Failed to route v2m MSI SPI[%d] >> to Dom%d.\n", >> + spi, d->domain_id); >> + return ret; >> + } >> + >> + /* Reserve a SPI that is allocated to MSI for the >> domain. */ >> + if ( !vgic_reserve_virq(d, spi) ) >> + { >> + printk(XENLOG_ERR >> + "GICv2: Failed to reserve v2m MSI >> SPI[%d] for Dom%d.\n", >> + spi, d->domain_id); >> + return -EINVAL; >> + } >> + } >> + } >> + >> + return 0; >> +} >> + >> +/* >> + * Set up gic v2m DT sub-node. >> + * Please refer to the binding document: >> + * >> https://www.kernel.org/doc/Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt >> + */ >> +static int gicv2m_make_dt_node(const struct domain *d, >> + const struct dt_device_node *gic, >> + void *fdt) >> >> >> The indentation looks wrong to me. It should be: >> >> foo(const struct ..., >> const struct ... >> void *...) >> >> Ditto. >> +{ >> + u32 len; >> + int res; >> + const void *prop = NULL; >> + const struct dt_device_node *v2m = NULL; >> + const struct v2m_data *v2m_data; >> + >> + /* The sub-nodes require the ranges property */ >> + prop = dt_get_property(gic, "ranges", &len); >> + if ( !prop ) >> + { >> + printk(XENLOG_ERR "Can't find ranges property for the >> gic node\n"); >> + return -FDT_ERR_XEN(ENOENT); >> + } >> + >> + res = fdt_property(fdt, "ranges", prop, len); >> + if ( res ) >> + return res; >> + >> + list_for_each_entry( v2m_data, &gicv2m_info, entry ) >> + { >> + v2m = v2m_data->dt_node; >> + >> + printk("GICv2: Creating v2m DT node for domain%d: >> addr=0x%lx size=0x%lx spi_base=%u num_spis=%u\n", >> >> >> NIT: s/domain%d/d%d/ >> >> + d->domain_id, v2m_data->addr, v2m_data->size, >> + v2m_data->spi_start, v2m_data->nr_spis); >> + >> + res = fdt_begin_node(fdt, v2m->name); >> + if ( res ) >> + return res; >> + >> + res = fdt_property_string(fdt, "compatible", >> "arm,gic-v2m-frame"); >> + if ( res ) >> + return res; >> + >> + res = fdt_property(fdt, "msi-controller", NULL, 0); >> + if ( res ) >> + return res; >> + >> + if ( v2m->phandle ) >> + { >> + res = fdt_property_cell(fdt, "phandle", >> v2m->phandle); >> + if ( res ) >> + return res; >> + } >> + >> + /* Use the same reg regions as v2m node in DTB. */ >> >> >> s/DTB/host DTB/ to make clear we are speaking about the host one and >> not the guest one. >> >> + prop = dt_get_property(v2m, "reg", &len); >> + if ( !prop ) >> + { >> + printk(XENLOG_ERR "GICv2: Can't find v2m reg >> property.\n"); >> + res = -FDT_ERR_XEN(ENOENT); >> + return res; >> + } >> + >> + res = fdt_property(fdt, "reg", prop, len); >> + if ( res ) >> + return res; >> + >> + /* >> + * Create msi-base-spi and msi-num-spis properties in >> Guest DT to >> + * override the hardware setting. From the binding >> document, we can >> + * find that these two properties are optional. But if >> these two >> + * properties are present in the v2m DT node, the guest >> v2m driver >> + * should read the configuration from these two >> properties instead >> + * of reading from hardware register. >> >> >> This paragraph is not clear. I would reword; >> >> "The properties msi-base-spi and msi-num-spis are used to override the >> hardware settings. Therefore it is fine to always write them in the >> guest DT.". >> >> + */ >> + res = fdt_property_u32(fdt, "arm,msi-base-spi", >> v2m_data->spi_start); >> + if ( res ) >> + { >> + printk(XENLOG_ERR >> + "GICv2: Failed to create v2m msi-base-spi in >> Guest DT.\n"); >> + return res; >> + } >> + >> + res = fdt_property_u32(fdt, "arm,msi-num-spis", >> v2m_data->nr_spis); >> + if ( res ) >> + { >> + printk(XENLOG_ERR >> + "GICv2: Failed to create v2m msi-num-spis in >> Guest DT.\n"); >> + return res; >> + } >> + >> + fdt_end_node(fdt); >> + } >> + >> + return res; >> +} >> + >> static int gicv2_make_hwdom_dt_node(const struct domain *d, >> const struct >> dt_device_node *gic, >> void *fdt) >> @@ -587,6 +790,10 @@ static int gicv2_make_hwdom_dt_node(const >> struct domain *d, >> len *= 2; >> >> res = fdt_property(fdt, "reg", regs, len); >> + if ( res ) >> + return res; >> + >> + res = gicv2m_make_dt_node(d, gic, fdt); >> >> return res; >> } >> @@ -632,6 +839,77 @@ static bool_t gicv2_is_aliased(paddr_t >> cbase, paddr_t csize) >> return ((val_low & 0xfff0fff) == 0x0202043B && val_low == >> val_high); >> } >> >> +static void gicv2_extension_dt_init(const struct dt_device_node >> *node) >> +{ >> + int res; >> + u32 spi_start, nr_spis; >> + paddr_t addr, size; >> + const struct dt_device_node *v2m = NULL; >> + >> + /* >> + * Check whether this GIC implements the v2m extension. If >> so, >> + * add v2m register frames to gicv2m_info. >> + */ >> + dt_for_each_child_node(node, v2m) >> + { >> + struct v2m_data *v2m_data; >> + >> + if ( !dt_device_is_compatible(v2m, "arm,gic-v2m-frame") >> ) >> + continue; >> + >> + v2m_data = xzalloc_bytes(sizeof(struct v2m_data)); >> + if ( !v2m_data ) >> + panic("GICv2: Cannot allocate memory for v2m >> frame"); >> + >> + /* >> + * Check whether DT uses msi-base-spi and msi-num-spis >> properties to >> + * override the hardware setting. >> + */ >> + if ( !dt_property_read_u32(v2m, "arm,msi-base-spi", >> &spi_start) || >> + !dt_property_read_u32(v2m, "arm,msi-num-spis", >> &nr_spis) ) >> + { >> + u32 msi_typer; >> + void __iomem *base; >> + >> + /* >> + * DT does not override the hardware setting, so we >> have to read >> + * base_spi and num_spis from hardware registers to >> reserve irqs. >> + */ >> + res = dt_device_get_address(v2m, 0, &addr, &size); >> + if ( res ) >> + panic("GICv2: Cannot find a valid v2m frame >> address"); >> + >> + base = ioremap_nocache(addr, size); >> + if ( !base ) >> + panic("GICv2: Cannot remap v2m register frame"); >> + >> + msi_typer = readl_relaxed(base + V2M_MSI_TYPER); >> + spi_start = V2M_MSI_TYPER_BASE_SPI(msi_typer); >> + nr_spis = V2M_MSI_TYPER_NUM_SPI(msi_typer); >> + >> + iounmap(base); >> + } >> + >> + /* Initialize a new entry to record new frame >> infomation. */ >> + INIT_LIST_HEAD(&v2m_data->entry); >> + v2m_data->addr = addr; >> + v2m_data->size = size; >> + v2m_data->spi_start = spi_start; >> + v2m_data->nr_spis = nr_spis; >> + v2m_data->dt_node = v2m; >> + >> + printk("GICv2m extension register frame:\n" >> + " gic_v2m_addr=%"PRIpaddr"\n" >> + " gic_v2m_size=%"PRIpaddr"\n" >> + " gic_v2m_spi_base=%u\n" >> + " gic_v2m_num_spis=%u\n", >> + v2m_data->addr, v2m_data->size, >> + v2m_data->spi_start, v2m_data->nr_spis); >> + >> + list_add_tail(&v2m_data->entry, &gicv2m_info); >> >> >> I would pull non-DT code in a separate function, so we can re-use the code >> later for the support of GICv2m with ACPI. >> >> + } >> +} >> + >> static paddr_t __initdata hbase, dbase, cbase, csize, vbase; >> >> static void __init gicv2_dt_init(void) >> @@ -683,6 +961,12 @@ static void __init gicv2_dt_init(void) >> if ( csize != vsize ) >> panic("GICv2: Sizes of GICC (%#"PRIpaddr") and GICV >> (%#"PRIpaddr") don't match\n", >> csize, vsize); >> + >> + /* >> + * Check whether this GIC implements the v2m extension. If so, >> + * add v2m register frames to gicv2_extension_info. >> + */ >> + gicv2_extension_dt_init(node); >> } >> >> static int gicv2_iomem_deny_access(const struct domain *d) >> @@ -936,6 +1220,7 @@ const static struct gic_hw_operations gicv2_ops = >> { >> .read_apr = gicv2_read_apr, >> .make_hwdom_dt_node = gicv2_make_hwdom_dt_node, >> .make_hwdom_madt = gicv2_make_hwdom_madt, >> + .map_hwdom_extra_mappings = gicv2_map_hwdown_extra_mappings, >> .iomem_deny_access = gicv2_iomem_deny_access, >> }; >> >> diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c >> index 2bfe4de..12bb0ab 100644 >> --- a/xen/arch/arm/gic.c >> +++ b/xen/arch/arm/gic.c >> @@ -230,6 +230,15 @@ int gic_irq_xlate(const u32 *intspec, unsigned >> int intsize, >> return 0; >> } >> >> +/* Map extra GIC MMIO, irqs and other hw stuffs to the hardware >> domain. */ >> +int gic_map_hwdom_extra_mappings(struct domain *d) >> +{ >> + if ( gic_hw_ops->map_hwdom_extra_mappings ) >> + return gic_hw_ops->map_hwdom_extra_mappings(d); >> + >> + return 0; >> +} >> + >> static void __init gic_dt_preinit(void) >> { >> int rc; >> diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h >> index cd97bb2..836f1ad 100644 >> --- a/xen/include/asm-arm/gic.h >> +++ b/xen/include/asm-arm/gic.h >> @@ -360,6 +360,8 @@ struct gic_hw_operations { >> const struct dt_device_node *gic, void >> *fdt); >> /* Create MADT table for the hardware domain */ >> int (*make_hwdom_madt)(const struct domain *d, u32 offset); >> + /* Map extra GIC MMIO, irqs and other hw stuffs to the hardware >> domain. */ >> + int (*map_hwdom_extra_mappings)(struct domain *d); >> /* Deny access to GIC regions */ >> int (*iomem_deny_access)(const struct domain *d); >> }; >> @@ -369,6 +371,7 @@ int gic_make_hwdom_dt_node(const struct domain *d, >> const struct dt_device_node *gic, >> void *fdt); >> int gic_make_hwdom_madt(const struct domain *d, u32 offset); >> +int gic_map_hwdom_extra_mappings(struct domain *d); >> int gic_iomem_deny_access(const struct domain *d); >> >> #endif /* __ASSEMBLY__ */ >> >> >> Regards, >> >> -- >> Julien Grall >> >> >> >> _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |