xen: arm: Parse PCI DT nodes' ranges and interrupt-map
v2 cover xen: arm: Parse PCI DT nodes' ranges and interrupt-map
This series adds parsing of the DT ranges and interrupt-map properties for PCI devices, these contain the MMIOs and IRQs used by children on the bus. This replaces the specific mapping stuff on xgene.
This is pretty much a rewrite of v1, which was wrong in several aspects relating to its parsing of the various properties.
There are several TODOs in the final patch, I'm mainly sending this now because in [0] I suggested to Vijay that this might make the need for the GSER specific mapping on ThunderX. Vijay, it would be great if you could confirm or deny this in practice.
Suravee, I think you were interested in this for Seattle too so I'm interested how you get on there.
I've tested on Mustang and on a FastModel with virtio-pci based rootfs.
Ian.
[0] http://article.gmane.org/gmane.comp.emulators.xen.devel/234959
PATCH v2 1/3 xen: dt: add dt_for_each_irq_map helper
This function iterates over a nodes interrupt-map property and calls a callback for each interrupt. For now it only supplies the raw IRQ since my use case has no need of e.g. child unit address. These can be added as needed by any future users.
This follows much the same logic as dt_irq_map_raw when parsing the interrupt-map, but doesn't walk up the tree doing the actual translation and it iterates over all entries instead of just looking for the first match.
I looked into refactoring dt_irq_map_raw but I couldn't find a way which I was confident in, plus I was reluctant to diverge from the Linux roots of this function any further.
--- xen/common/device_tree.c | 140 +++++++++++++++++++++++++++++++++++++++++ xen/include/xen/device_tree.h | 12 ++++ 2 files changed, 152 insertions(+)
xen/common/device_tree.c +140 -0
@@ -816,6 +816,146 @@ unsigned int dt_number_of_address(const struct dt_device_node *dev)return (psize / onesize); } +int dt_for_each_irq_map (const struct dt_device_node *dev, +int (*cb)(const struct dt_device_node *, +const struct dt_raw_irq *, +void *), +void *data) +{ +const struct dt_device_node *ipar, *tnode, *old =NULL ; +const __be32 *tmp, *imap; +u32 intsize =1 , addrsize, pintsize =0 , paddrsize =0 ; +u32 imaplen; +int i, ret; + +struct dt_raw_irq dt_raw_irq; + +dt_dprintk ("%s: par=%s cb=%p data=%p\n" , __func__, + dev->full_name, cb, data); + + ipar = dev; + +/* First get the #interrupt-cells property of the current cursor +* that tells us how to interpret the passed-in intspec. If there +* is none, we are nice and just walk up the tree +*/ +do { + tmp =dt_get_property (ipar,"#interrupt-cells" ,NULL ); +if ( tmp !=NULL ) + { + intsize =be32_to_cpu (*tmp); +break ; + } + tnode = ipar; + ipar =dt_irq_find_parent (ipar); + }while ( ipar ); +if ( ipar ==NULL ) + { +dt_dprintk (" -> no parent found !\n" ); + goto fail; + } + +dt_dprintk ("%s: ipar=%s, size=%d\n" , __func__, ipar->full_name, intsize); + +if ( intsize >DT_MAX_IRQ_SPEC ) + { +dt_dprintk (" -> too many irq specifier cells\n" ); + goto fail; + } + +/* Look for this #address-cells. We have to implement the old linux +* trick of looking for the parent here as some device-trees rely on it +*/ + old = ipar; +do { + tmp =dt_get_property (old,"#address-cells" ,NULL ); + tnode =dt_get_parent (old); + old = tnode; + }while ( old && tmp ==NULL ); + + old =NULL ; + addrsize = (tmp ==NULL ) ?2 :be32_to_cpu (*tmp); + +dt_dprintk (" -> addrsize=%d\n" , addrsize); + +/* Now look for an interrupt-map */ + imap =dt_get_property (dev,"interrupt-map" , &imaplen); +/* No interrupt map, check for an interrupt parent */ +if ( imap ==NULL ) + { +dt_dprintk (" -> no map, ignoring\n" ); + goto fail; + } + imaplen /=sizeof (u32); + +/* Parse interrupt-map */ +while ( imaplen > (addrsize + intsize +1 ) ) + { +/* skip child unit address and child interrupt specifier */ + imap += addrsize + intsize; + imaplen -= addrsize + intsize; + +/* Get the interrupt parent */ + ipar =dt_find_node_by_phandle (be32_to_cpup (imap)); + imap++; + --imaplen; + +/* Check if not found */ +if ( ipar ==NULL ) + { +dt_dprintk (" -> imap parent not found !\n" ); + goto fail; + } + +dt_dprintk (" -> ipar %s\n" ,dt_node_name (ipar)); + +/* Get #interrupt-cells and #address-cells of new +* parent +*/ + tmp =dt_get_property (ipar,"#interrupt-cells" ,NULL ); +if ( tmp ==NULL ) + { +dt_dprintk (" -> parent lacks #interrupt-cells!\n" ); + goto fail; + } + pintsize =be32_to_cpu (*tmp); + tmp =dt_get_property (ipar,"#address-cells" ,NULL ); + paddrsize = (tmp ==NULL ) ?0 :be32_to_cpu (*tmp); + +dt_dprintk (" -> pintsize=%d, paddrsize=%d\n" , + pintsize, paddrsize); + +/* Check for malformed properties */ +if ( imaplen < (paddrsize + pintsize) ) + goto fail; + + imap += paddrsize; + imaplen -= paddrsize; + + dt_raw_irq.controller = ipar; + dt_raw_irq.size = pintsize; +for ( i =0 ; i < pintsize; i++ ) + dt_raw_irq.specifier[i] =dt_read_number (imap + i,1 ); + + ret =cb (dev, &dt_raw_irq, data); +if ( ret <0 ) + { +dt_dprintk (" -> callback failed=%d\n" , ret); +return ret; + } + + imap += pintsize; + imaplen -= pintsize; + +dt_dprintk (" -> imaplen=%d\n" , imaplen); + } + +return 0 ; + +fail: +return -EINVAL ; +} +/** * dt_irq_map_raw - Low level interrupt tree parsing * @parent: the device interrupt parent
xen/include/xen/device_tree.h +12 -0
@@ -529,6 +529,18 @@ int dt_device_get_raw_irq(const struct dt_device_node *device, int index,int dt_irq_translate (const struct dt_raw_irq *raw,struct dt_irq *out_irq);/** +* dt_for_each_irq_map - Iterate over a nodes interrupt-map property +* @dev: The node whose interrupt-map property should be iterated over +* @cb: Call back to call for each entry +* @data: Caller data passed to callback +*/ +int dt_for_each_irq_map (const struct dt_device_node *dev, +int (*cb)(const struct dt_device_node *, +const struct dt_raw_irq *, +void *), +void *data); + +/** * dt_n_size_cells - Helper to retrieve the number of cell for the size * @np: node to get the value *
Signature · 1 line
1.7.10.4
PATCH v2 2/3 xen: arm: propagate gic's #interrupt-cells property to dom0.
In reply to Ian CampbellThis is similar to 816f5bb1f074 "xen: arm: propagate gic's should propagate (rather than invent our own value) since this value is used to size fields within other properties within the tree.
I'm not sure why I didn't do this as part of 816f5bb1f074. I think probably just because #interrupt-cells must always be 3 for a GIC whereas #address-cells can legitimately differ. Regardless, I think we might as well do this in common code.
--- xen/arch/arm/domain_build.c | 18 +++++++++++++----- xen/arch/arm/gic-hip04.c | 4 ---- xen/arch/arm/gic-v2.c | 4 ---- xen/arch/arm/gic-v3.c | 4 ---- 4 files changed, 13 insertions(+), 17 deletions(-)
xen/arch/arm/domain_build.c +13 -5
@@ -784,8 +784,8 @@ static int make_gic_node(const struct domain *d, void *fdt, {const struct dt_device_node *gic = dt_interrupt_controller;int res =0 ; -const void *addrcells; -u32 addrcells_len; +const void *cells; +u32 cells_len;/* * Xen currently supports only a single GIC. Discard any secondary
@@ -815,10 +815,18 @@ static int make_gic_node(const struct domain *d, void *fdt,return res; } - addrcells =dt_get_property (gic,"#address-cells" , &addrcells_len); -if ( addrcells ) + cells =dt_get_property (gic,"#address-cells" , &cells_len); +if ( cells ) { - res =fdt_property (fdt,"#address-cells" , addrcells, addrcells_len); + res =fdt_property (fdt,"#address-cells" , cells, cells_len); +if ( res ) +return res; + } + + cells =dt_get_property (gic,"#interrupt-cells" , &cells_len); +if ( cells ) + { + res =fdt_property (fdt,"#interrupt-cells" , cells, cells_len);if ( res )return res; }
xen/arch/arm/gic-hip04.c +0 -4
@@ -636,10 +636,6 @@ static int hip04gic_make_dt_node(const struct domain *d,if ( res )return res; - res =fdt_property_cell (fdt,"#interrupt-cells" ,3 ); -if ( res ) -return res; - res =fdt_property (fdt,"interrupt-controller" ,NULL ,0 );if ( res )
xen/arch/arm/gic-v2.c +0 -4
@@ -622,10 +622,6 @@ static int gicv2_make_dt_node(const struct domain *d,if ( res )return res; - res =fdt_property_cell (fdt,"#interrupt-cells" ,3 ); -if ( res ) -return res; - res =fdt_property (fdt,"interrupt-controller" ,NULL ,0 );if ( res )
xen/arch/arm/gic-v3.c +0 -4
@@ -1102,10 +1102,6 @@ static int gicv3_make_dt_node(const struct domain *d,if ( res )return res; - res =fdt_property_cell (fdt,"#interrupt-cells" ,3 ); -if ( res ) -return res; - res =fdt_property (fdt,"interrupt-controller" ,NULL ,0 );if ( res )return res;
Signature · 1 line
1.7.10.4
Re: [PATCH v2 2/3] xen: arm: propagate gic's #interrupt-cells property to dom0.
Hi Ian,
Quoted 7 lines · Ian Campbell
This is similar to
816f5bb1f074"xen: arm: propagate gic's should propagate (rather than invent our own value) since this value is used to size fields within other properties within the tree. I'm not sure why I didn't do this as part of816f5bb1f074. I think probably just because #interrupt-cells must always be 3 for a GIC whereas #address-cells can legitimately differ. Regardless, I think we might as well do this in common code.
Hmmm... We are creating some interrupt ourself assuming the number of interrupt cells is 3. So it makes sense to hard-code (not really invent) the value.
Quoted 32 lines
Signed-off-by Ian Campbell <[email protected]>--- xen/arch/arm/domain_build.c | 18 +++++++++++++----- xen/arch/arm/gic-hip04.c | 4 ---- xen/arch/arm/gic-v2.c | 4 ---- xen/arch/arm/gic-v3.c | 4 ---- 4 files changed, 13 insertions(+), 17 deletions(-)xen/arch/arm/domain_build.c +4
-4@@ -784,8 +784,8 @@ static int make_gic_node(const struct domain *d, void *fdt, {const struct dt_device_node *gic = dt_interrupt_controller;int res =0 ; -const void *addrcells; -u32 addrcells_len; +const void *cells; +u32 cells_len;/* * Xen currently supports only a single GIC. Discard any secondary @@ -815,10 +815,18 @@ static int make_gic_node(const struct domain *d, void *fdt,return res; } - addrcells =dt_get_property (gic,"#address-cells" , &addrcells_len); -if ( addrcells ) + cells =dt_get_property (gic,"#address-cells" , &cells_len); +if ( cells ) {
xen/arch/arm/domain_build.c static int make_gic_node(const struct domain *d, void *fdt, · line 829
Quoted 10 lines
xen/arch/arm/domain_build.c +9
-1- res =fdt_property (fdt,"#address-cells" , addrcells, addrcells_len); + res =fdt_property (fdt,"#address-cells" , cells, cells_len); +if ( res ) +return res; + } + + cells =dt_get_property (gic,"#interrupt-cells" , &cells_len); +if ( cells ) + { + res =fdt_property (fdt,"#interrupt-cells" , cells, cells_len);
The #interrupt-cells as to be present at any time for the GIC. So I don't think it's worth to check if it presents. Maybe an ASSERT would be enough?
Also, I would check somewhere that the value is effectively 3 otherwise we are in trouble for the timer/evtchn interrupt creation. Though, it was there before too.
Quoted 39 lines
xen/arch/arm/domain_build.c +0
-0if ( res )return res; }xen/arch/arm/gic-hip04.c +0
-4@@ -636,10 +636,6 @@ static int hip04gic_make_dt_node(const struct domain *d,if ( res )return res; - res =fdt_property_cell (fdt,"#interrupt-cells" ,3 ); -if ( res ) -return res; - res =fdt_property (fdt,"interrupt-controller" ,NULL ,0 );if ( res )xen/arch/arm/gic-v2.c +0
-4@@ -622,10 +622,6 @@ static int gicv2_make_dt_node(const struct domain *d,if ( res )return res; - res =fdt_property_cell (fdt,"#interrupt-cells" ,3 ); -if ( res ) -return res; - res =fdt_property (fdt,"interrupt-controller" ,NULL ,0 );if ( res )xen/arch/arm/gic-v3.c +0
-0
xen/arch/arm/gic-v3.c static int gicv3_make_dt_node(const struct domain *d, · line 1107
Quoted 10 lines
xen/arch/arm/gic-v3.c +0
-4@@ -1102,10 +1102,6 @@ static int gicv3_make_dt_node(const struct domain *d,if ( res )return res; - res =fdt_property_cell (fdt,"#interrupt-cells" ,3 ); -if ( res ) -return res; - res =fdt_property (fdt,"interrupt-controller" ,NULL ,0 );if ( res )return res;
While you move #interrupt-cells to common code. Could you move interrupt-controller too?
Regards,
Signature · 1 line
Julien Grall
Re: [PATCH v2 2/3] xen: arm: propagate gic's #interrupt-cells property to dom0.
Quoted 11 lines · Julien Grall
Hi Ian,
Quoted 7 lines · Ian Campbell
This is similar to
816f5bb1f074"xen: arm: propagate gic's should propagate (rather than invent our own value) since this value is used to size fields within other properties within the tree. I'm not sure why I didn't do this as part of816f5bb1f074. I think probably just because #interrupt-cells must always be 3 for a GIC whereas #address-cells can legitimately differ. Regardless, I think we might as well do this in common code.
Quoted 3 lines
Hmmm... We are creating some interrupt ourself assuming the number of interrupt cells is 3. So it makes sense to hard-code (not really invent) the value.
I'll move the addition to common code but leave it as hard coded then.
Quoted 39 lines
Quoted 39 lines
Signed-off-by Ian Campbell <[email protected]>--- xen/arch/arm/domain_build.c | 18 +++++++++++++----- xen/arch/arm/gic-hip04.c | 4 ---- xen/arch/arm/gic-v2.c | 4 ---- xen/arch/arm/gic-v3.c | 4 ---- 4 files changed, 13 insertions(+), 17 deletions(-)xen/arch/arm/domain_build.c +9
-5@@ -784,8 +784,8 @@ static int make_gic_node(const struct domain *d, void *fdt, {const struct dt_device_node *gic = dt_interrupt_controller;int res =0 ; -const void *addrcells; -u32 addrcells_len; +const void *cells; +u32 cells_len;/* * Xen currently supports only a single GIC. Discard any secondary @@ -815,10 +815,18 @@ static int make_gic_node(const struct domain *d, void *fdt,return res; } - addrcells =dt_get_property (gic,"#address-cells" , &addrcells_len); -if ( addrcells ) + cells =dt_get_property (gic,"#address-cells" , &cells_len); +if ( cells ) { - res =fdt_property (fdt,"#address-cells" , addrcells, addrcells_len); + res =fdt_property (fdt,"#address-cells" , cells, cells_len); +if ( res ) +return res; + } +
Quoted 8 lines
xen/arch/arm/domain_build.c static int make_gic_node(const struct domain *d, void *fdt, · line 829
Quoted 4 lines
xen/arch/arm/domain_build.c +4
-0+ cells =dt_get_property (gic,"#interrupt-cells" , &cells_len); +if ( cells ) + { + res =fdt_property (fdt,"#interrupt-cells" , cells, cells_len);The #interrupt-cells as to be present at any time for the GIC. So I don't think it's worth to check if it presents. Maybe an ASSERT would be enough?
With the change discussed above it becomes moot.
Quoted 3 lines
Also, I would check somewhere that the value is effectively 3 otherwise we are in trouble for the timer/evtchn interrupt creation. Though, it was there before too.
Probably somewhere should but I'm not sure where.
Quoted 8 lines
Quoted 8 lines
xen/arch/arm/gic-v3.c +0
-0@@ -1102,10 +1102,6 @@ static int gicv3_make_dt_node(const struct domain *d,if ( res )return res;
Quoted 11 lines
xen/arch/arm/gic-v3.c static int gicv3_make_dt_node(const struct domain *d, · line 1107
Quoted 8 lines
xen/arch/arm/gic-v3.c +0
-4- res =fdt_property_cell (fdt,"#interrupt-cells" ,3 ); -if ( res ) -return res; - res =fdt_property (fdt,"interrupt-controller" ,NULL ,0 );if ( res )return res;While you move #interrupt-cells to common code. Could you move interrupt-controller too?
I suppose I may as well.
Ian.
PATCH v2 3/3 xen: arm: handle PCI DT node ranges and interrupt-map properties
In reply to Ian CampbellThese properties are defined in ePAPR (2.3.8 and 2.4.3.1 respectively) and the OpenFirmware PCI Bus Binding Specification (IEEE Std 1275-1994).
This replaces the xgene specific mapping. Tested on Mustang and on a model with a PCI virtio controller.
TODO: Use a helper iterator (e.g. dt_for_each_range) for the ranges property, like is already done for interrupts using dt_for_each_irq_map.
TODO: Should we stop calling map_device for nodes beneath this one (since we should have mapped everything underneath)? I think this is complex for cases which map interrupt but not ranges or vice versa, perhaps meaning we need to recurse on them separately. Maybe we can continue to descend and the mappings may just be harmlessly instantiated twice.
TODO: Related to the above there are also some buses for which we cannot use dt_bus_default_{map,translate}. We might want to pull in the of_bus_pci stuff from Linux, although I think that would be orthogonal to this fix.
---
v2: This is essentially a complete reworking, which actually parses things properly (obeying #{address,size,interrupt}-cells on the appriopriate nodes) and includes handling of interrupt-map too.
--- xen/arch/arm/domain_build.c | 156 ++++++++++++++++++++++++++++++++++ xen/arch/arm/platforms/xgene-storm.c | 143 ------------------------------- 2 files changed, 156 insertions(+), 143 deletions(-)
xen/arch/arm/domain_build.c +156 -0
@@ -23,6 +23,21 @@#include <xen/irq.h> #include "kernel.h" +/* +* Definitions for implementing parts of the OpenFirmware PCI Bus Binding +* Specification (IEEE Std 1275-1994). +*/ + +struct of_pci_unit_address { +__be32 hi, mid, lo; +} __attribute__((packed)); + +struct of_pci_ranges_entry { +struct of_pci_unit_address pci_addr; +__be64 cpu_addr; +__be64 length; +} __attribute__((packed)); +static unsigned int __initdata opt_dom0_max_vcpus;integer_param ("dom0_max_vcpus" , opt_dom0_max_vcpus);
@@ -911,6 +926,139 @@ static int make_timer_node(const struct domain *d, void *fdt,return res; } +static int map_pci_device_ranges (struct domain *d, +const struct dt_device_node *dev, +const struct of_pci_ranges_entry *ranges, +const u32 len) +{ +int parent_size_cells, parent_addr_cells; +int i, nr, res; + + parent_size_cells =dt_n_size_cells (dev); + parent_addr_cells =dt_n_addr_cells (dev); + +/* +* Range is child address, host address (#address-cells), length +* (#size-cells),see ePAPR 2.3.8. +* +* PCI child address is u32 space + u64 address, see ePAPR 6.2.2. +* +*/ + nr = len /sizeof (*ranges); + +for ( i =0 ; i < nr ; i++ ) + { +const struct of_pci_ranges_entry *range = &ranges[i]; +u64 addr, len; + + len =fdt64_to_cpu (range->length); + + addr =dt_translate_address (dev, (const __be32 *)&range->cpu_addr); +DPRINT ("PCI SPACE 0x%08x.%08x.%08x 0x%" PRIx64" size 0x%" PRIx64"\n" , +fdt32_to_cpu (range->pci_addr.hi), +fdt32_to_cpu (range->pci_addr.mid), +fdt32_to_cpu (range->pci_addr.lo), + addr, len); + + res =map_mmio_regions (d, +paddr_to_pfn (addr &PAGE_MASK ), +DIV_ROUND_UP (len,PAGE_SIZE ), +paddr_to_pfn (addr &PAGE_MASK )); +if ( res <0 ) + { +printk (XENLOG_ERR "Unable to map 0x%" PRIx64 +" - 0x%" PRIx64" in domain %d\n" , + addr &PAGE_MASK ,PAGE_ALIGN (addr + len) -1 , + d->domain_id); +return res; + } + } +return 0 ; +} + +static int map_device_ranges (struct domain *d,const struct dt_device_node *dev) +{ +const void *ranges; +u32 len; + + ranges =dt_get_property (dev,"ranges" , &len); +/* No ranges, nothing to do */ +if ( !ranges ) +return 0 ; + +if (dt_device_type_is_equal (dev,"pci" ) ) +return map_pci_device_ranges (d, dev, ranges, len); + +printk ("Cannot handle ranges for non-PCI device %s type %s\n" , +dt_node_name (dev), dev->type); + +/* Lets not worry for now... */ +return 0 ; +} + +static int map_interrupt_to_domain (const struct dt_device_node *dev, +const struct dt_raw_irq *dt_raw_irq, +void *data) +{ +struct domain *d = data; +struct dt_irq dt_irq; +int res; + + res =dt_irq_translate (dt_raw_irq, &dt_irq); +if ( res <0 ) + { +printk (XENLOG_ERR "%s: Failed to translate IRQ: %d\n" , +dt_node_name (dev), res); +return res; + } + +if ( dt_irq.irq <NR_LOCAL_IRQS ) + { +printk (XENLOG_ERR "%s: IRQ%" PRId32" is not a SPI\n" , +dt_node_name (dev), dt_irq.irq); +return -EINVAL ; + } + +/* Setup the IRQ type */ + res =irq_set_spi_type (dt_irq.irq, dt_irq.type); +if ( res ) + { +printk (XENLOG_ERR +"%s: Unable to setup IRQ%" PRId32" to dom%d\n" , +dt_node_name (dev), dt_irq.irq, d->domain_id); +return res; + } + + res =route_irq_to_guest (d, dt_irq.irq,dt_node_name (dev)); +if ( res <0 ) + { +printk (XENLOG_ERR "Unable to map IRQ%" PRId32" to dom%d\n" , + dt_irq.irq, d->domain_id); +return res; + } + +DPRINT ("PCI IRQ %u mapped to guest" , dt_irq.irq); + +return 0 ; +} + +static int map_device_interrupts (struct domain *d,const struct dt_device_node *dev) +{ + +if ( !dt_property_read_bool (dev,"interrupt-map" ) ) +return 0 ;/* No interrupt map to handle */ + +if (dt_device_type_is_equal (dev,"pci" ) ) +return dt_for_each_irq_map (dev, &map_interrupt_to_domain, d); + +printk ("Cannot handle interrupt-map for non-PCI device %s type %s\n" , +dt_node_name (dev), dev->type); + +/* Lets not worry for now... */ +return 0 ; +} + +/* Map the device in the domain */ static int map_device (struct domain *d,struct dt_device_node *dev) {
@@ -1025,6 +1173,14 @@ static int map_device(struct domain *d, struct dt_device_node *dev) } } + res =map_device_ranges (d, dev); +if ( res ) +return res; + + res =map_device_interrupts (d, dev); +if ( res ) +return res; +return 0 ; }
xen/arch/arm/platforms/xgene-storm.c +0 -143
155 lines not on this page · raw message
Signature · 1 line
1.7.10.4
Re: [PATCH v2 3/3] xen: arm: handle PCI DT node ranges and interrupt-map properties
Hi Ian,
Quoted 10 lines · Ian Campbell
These properties are defined in ePAPR (2.3.8 and 2.4.3.1 respectively) and the OpenFirmware PCI Bus Binding Specification (IEEE Std 1275-1994).
This replaces the xgene specific mapping. Tested on Mustang and on a model with a PCI virtio controller.
TODO: Use a helper iterator (e.g.
dt_for_each_range) for the ranges property, like is already done for interrupts usingdt_for_each_irq_map.
Quoted 6 lines
TODO: Should we stop calling
map_devicefor nodes beneath this one (since we should have mapped everything underneath)? I think this is complex for cases which map interrupt but not ranges or vice versa, perhaps meaning we need to recurse on them separately. Maybe we can continue to descend and the mappings may just be harmlessly instantiated twice.
There is not harm to route twice the same IRQ. Although it's pointless to continue.
How difficult would it be to introduce a new bus?
Quoted 101 lines
TODO: Related to the above there are also some buses for which we cannot use dt_bus_default_{map,translate}. We might want to pull in the
of_bus_pcistuff from Linux, although I think that would be orthogonal to this fix.Signed-off-by Ian Campbell <[email protected]>---
v2: This is essentially a complete reworking, which actually parses things properly (obeying #{address,size,interrupt}-cells on the appriopriate nodes) and includes handling of interrupt-map too.--- xen/arch/arm/domain_build.c | 156 ++++++++++++++++++++++++++++++++++ xen/arch/arm/platforms/xgene-storm.c | 143 ------------------------------- 2 files changed, 156 insertions(+), 143 deletions(-)xen/arch/arm/domain_build.c +71
-0@@ -23,6 +23,21 @@#include <xen/irq.h> #include "kernel.h" +/* +* Definitions for implementing parts of the OpenFirmware PCI Bus Binding +* Specification (IEEE Std 1275-1994). +*/ + +struct of_pci_unit_address { +__be32 hi, mid, lo; +} __attribute__((packed)); + +struct of_pci_ranges_entry { +struct of_pci_unit_address pci_addr; +__be64 cpu_addr; +__be64 length; +} __attribute__((packed)); +static unsigned int __initdata opt_dom0_max_vcpus;integer_param ("dom0_max_vcpus" , opt_dom0_max_vcpus);@@ -911,6 +926,139 @@ static int make_timer_node(const struct domain *d, void *fdt,return res; } +static int map_pci_device_ranges (struct domain *d, +const struct dt_device_node *dev, +const struct of_pci_ranges_entry *ranges, +const u32 len) +{ +int parent_size_cells, parent_addr_cells; +int i, nr, res; + + parent_size_cells =dt_n_size_cells (dev); + parent_addr_cells =dt_n_addr_cells (dev); + +/* +* Range is child address, host address (#address-cells), length +* (#size-cells),see ePAPR 2.3.8. +* +* PCI child address is u32 space + u64 address, see ePAPR 6.2.2. +* +*/ + nr = len /sizeof (*ranges); + +for ( i =0 ; i < nr ; i++ ) + { +const struct of_pci_ranges_entry *range = &ranges[i]; +u64 addr, len; + + len =fdt64_to_cpu (range->length); + + addr =dt_translate_address (dev, (const __be32 *)&range->cpu_addr); +DPRINT ("PCI SPACE 0x%08x.%08x.%08x 0x%" PRIx64" size 0x%" PRIx64"\n" , +fdt32_to_cpu (range->pci_addr.hi), +fdt32_to_cpu (range->pci_addr.mid), +fdt32_to_cpu (range->pci_addr.lo), + addr, len); + + res =map_mmio_regions (d, +paddr_to_pfn (addr &PAGE_MASK ), +DIV_ROUND_UP (len,PAGE_SIZE ), +paddr_to_pfn (addr &PAGE_MASK )); +if ( res <0 ) + { +printk (XENLOG_ERR "Unable to map 0x%" PRIx64 +" - 0x%" PRIx64" in domain %d\n" , + addr &PAGE_MASK ,PAGE_ALIGN (addr + len) -1 , + d->domain_id); +return res; + } + } +return 0 ; +} + +static int map_device_ranges (struct domain *d,const struct dt_device_node *dev) +{ +const void *ranges; +u32 len; + + ranges =dt_get_property (dev,"ranges" , &len);
xen/arch/arm/domain_build.c static int make_timer_node(const struct domain *d, void *fdt, · line 994
Quoted 10 lines
xen/arch/arm/domain_build.c +10
-0+/* No ranges, nothing to do */ +if ( !ranges ) +return 0 ; + +if (dt_device_type_is_equal (dev,"pci" ) ) +return map_pci_device_ranges (d, dev, ranges, len); + +printk ("Cannot handle ranges for non-PCI device %s type %s\n" , +dt_node_name (dev), dev->type); +
Is the printk really necessary? It will a spurious log on platform where ranges is not empty (midway, arndale, foundation model...).
In general, we should not handle ranges by default as we may overlap the mapping done during the domain creation (such as the GIC).
Quoted 44 lines
xen/arch/arm/domain_build.c +44
-0+/* Lets not worry for now... */ +return 0 ; +} + +static int map_interrupt_to_domain (const struct dt_device_node *dev, +const struct dt_raw_irq *dt_raw_irq, +void *data) +{ +struct domain *d = data; +struct dt_irq dt_irq; +int res; + + res =dt_irq_translate (dt_raw_irq, &dt_irq); +if ( res <0 ) + { +printk (XENLOG_ERR "%s: Failed to translate IRQ: %d\n" , +dt_node_name (dev), res); +return res; + } + +if ( dt_irq.irq <NR_LOCAL_IRQS ) + { +printk (XENLOG_ERR "%s: IRQ%" PRId32" is not a SPI\n" , +dt_node_name (dev), dt_irq.irq); +return -EINVAL ; + } + +/* Setup the IRQ type */ + res =irq_set_spi_type (dt_irq.irq, dt_irq.type); +if ( res ) + { +printk (XENLOG_ERR +"%s: Unable to setup IRQ%" PRId32" to dom%d\n" , +dt_node_name (dev), dt_irq.irq, d->domain_id); +return res; + } + + res =route_irq_to_guest (d, dt_irq.irq,dt_node_name (dev)); +if ( res <0 ) + { +printk (XENLOG_ERR "Unable to map IRQ%" PRId32" to dom%d\n" , + dt_irq.irq, d->domain_id); +return res; + }
xen/arch/arm/domain_build.c static int make_timer_node(const struct domain *d, void *fdt, · line 1048
Quoted 10 lines
xen/arch/arm/domain_build.c +10
-0+ +DPRINT ("PCI IRQ %u mapped to guest" , dt_irq.irq); + +return 0 ; +} + +static int map_device_interrupts (struct domain *d,const struct dt_device_node *dev) +{ + +if ( !dt_property_read_bool (dev,"interrupt-map" ) )
It's strange to use dt_property_read_bool here and dt_get_property above to check the emptiness.
I prefer the dt_get_property version which is less confusing.
Anyway, why do you need to check interrupt-map. Can't your new helper deal with empty property?
Quoted 17 lines
xen/arch/arm/domain_build.c +13
-0+return 0 ;/* No interrupt map to handle */ + +if (dt_device_type_is_equal (dev,"pci" ) ) +return dt_for_each_irq_map (dev, &map_interrupt_to_domain, d); + +printk ("Cannot handle interrupt-map for non-PCI device %s type %s\n" , +dt_node_name (dev), dev->type); + +/* Lets not worry for now... */ +return 0 ; +} + +/* Map the device in the domain */ static int map_device (struct domain *d,struct dt_device_node *dev) {
xen/arch/arm/domain_build.c static int map_device(struct domain *d, struct dt_device_node *dev) · line 1180
Quoted 8 lines
xen/arch/arm/domain_build.c +5
-0@@ -1025,6 +1173,14 @@ static int map_device(struct domain *d, struct dt_device_node *dev) } } + res =map_device_ranges (d, dev); +if ( res ) +return res; + + res =map_device_interrupts (d, dev);
The name of the function doesn't make much sense. We already map the interrupt above (see platform get_irq).
Regards,
Signature · 1 line
Julien Grall
Re: [PATCH v2 3/3] xen: arm: handle PCI DT node ranges and interrupt-map properties
Quoted 20 lines · Julien Grall
Hi Ian,
Quoted 10 lines · Ian Campbell
These properties are defined in ePAPR (2.3.8 and 2.4.3.1 respectively) and the OpenFirmware PCI Bus Binding Specification (IEEE Std 1275-1994).
This replaces the xgene specific mapping. Tested on Mustang and on a model with a PCI virtio controller.
TODO: Use a helper iterator (e.g.
dt_for_each_range) for the ranges property, like is already done for interrupts usingdt_for_each_irq_map.Quoted 6 lines
TODO: Should we stop calling
map_devicefor nodes beneath this one (since we should have mapped everything underneath)? I think this is complex for cases which map interrupt but not ranges or vice versa, perhaps meaning we need to recurse on them separately. Maybe we can continue to descend and the mappings may just be harmlessly instantiated twice.
Quoted 4 lines
There is not harm to route twice the same IRQ. Although it's pointless to continue.
How difficult would it be to introduce a new bus?
It's a couple of reasonably trivial hook functions (which one would naturally just get from Linux) and a struct tying a name to those. Not too hard.
Quoted 9 lines
xen/arch/arm/domain_build.c static int make_timer_node(const struct domain *d, void *fdt, · line 994
Quoted 6 lines
xen/arch/arm/domain_build.c +6
-0+if (dt_device_type_is_equal (dev,"pci" ) ) +return map_pci_device_ranges (d, dev, ranges, len); + +printk ("Cannot handle ranges for non-PCI device %s type %s\n" , +dt_node_name (dev), dev->type); +Is the printk really necessary? It will a spurious log on platform where ranges is not empty (midway, arndale, foundation model...).
If the ranges is present and non-empty it's not impossible that we need to be doing something with it. I'd rather try and figure out how to whitelist such nodes, perhaps they lack a dev_type completely?
Quoted 61 lines
In general, we should not handle ranges by default as we may overlap the mapping done during the domain creation (such as the GIC).
Quoted 44 lines
xen/arch/arm/domain_build.c +44
-0+/* Lets not worry for now... */ +return 0 ; +} + +static int map_interrupt_to_domain (const struct dt_device_node *dev, +const struct dt_raw_irq *dt_raw_irq, +void *data) +{ +struct domain *d = data; +struct dt_irq dt_irq; +int res; + + res =dt_irq_translate (dt_raw_irq, &dt_irq); +if ( res <0 ) + { +printk (XENLOG_ERR "%s: Failed to translate IRQ: %d\n" , +dt_node_name (dev), res); +return res; + } + +if ( dt_irq.irq <NR_LOCAL_IRQS ) + { +printk (XENLOG_ERR "%s: IRQ%" PRId32" is not a SPI\n" , +dt_node_name (dev), dt_irq.irq); +return -EINVAL ; + } + +/* Setup the IRQ type */ + res =irq_set_spi_type (dt_irq.irq, dt_irq.type); +if ( res ) + { +printk (XENLOG_ERR +"%s: Unable to setup IRQ%" PRId32" to dom%d\n" , +dt_node_name (dev), dt_irq.irq, d->domain_id); +return res; + } + + res =route_irq_to_guest (d, dt_irq.irq,dt_node_name (dev)); +if ( res <0 ) + { +printk (XENLOG_ERR "Unable to map IRQ%" PRId32" to dom%d\n" , + dt_irq.irq, d->domain_id); +return res; + }xen/arch/arm/domain_build.c static int make_timer_node(const struct domain *d, void *fdt, · line 1048
Quoted 10 lines
xen/arch/arm/domain_build.c +10
-0+ +DPRINT ("PCI IRQ %u mapped to guest" , dt_irq.irq); + +return 0 ; +} + +static int map_device_interrupts (struct domain *d,const struct dt_device_node *dev) +{ + +if ( !dt_property_read_bool (dev,"interrupt-map" ) )It's strange to use
dt_property_read_boolhere anddt_get_propertyabove to check the emptiness.
Quoted 4 lines
I prefer the
dt_get_propertyversion which is less confusing.Anyway, why do you need to check interrupt-map. Can't your new helper deal with empty property?
It can, but the code below would log for any non-pci device, which is undesirable if there is no interrupt-map present.
Quoted 21 lines
Quoted 21 lines
xen/arch/arm/domain_build.c +13
-0+return 0 ;/* No interrupt map to handle */ + +if (dt_device_type_is_equal (dev,"pci" ) ) +return dt_for_each_irq_map (dev, &map_interrupt_to_domain, d); + +printk ("Cannot handle interrupt-map for non-PCI device %s type %s\n" , +dt_node_name (dev), dev->type); + +/* Lets not worry for now... */ +return 0 ; +} + +/* Map the device in the domain */ static int map_device (struct domain *d,struct dt_device_node *dev) {@@ -1025,6 +1173,14 @@ static int map_device(struct domain *d, struct dt_device_node *dev) } }
Quoted 8 lines
xen/arch/arm/domain_build.c static int map_device(struct domain *d, struct dt_device_node *dev) · line 1180
Quoted 5 lines
xen/arch/arm/domain_build.c +5
-0+ res =map_device_ranges (d, dev); +if ( res ) +return res; + + res =map_device_interrupts (d, dev);The name of the function doesn't make much sense. We already map the interrupt above (see platform
get_irq).
child_interrupt perhaps?
Ian.
Re: [PATCH v2 3/3] xen: arm: handle PCI DT node ranges and interrupt-map properties
Quoted 10 lines · Ian Campbell
Quoted 9 lines
xen/arch/arm/domain_build.c static int make_timer_node(const struct domain *d, void *fdt, · line 994
Quoted 6 lines
xen/arch/arm/domain_build.c +6
-0+if (dt_device_type_is_equal (dev,"pci" ) ) +return map_pci_device_ranges (d, dev, ranges, len); + +printk ("Cannot handle ranges for non-PCI device %s type %s\n" , +dt_node_name (dev), dev->type); +Is the printk really necessary? It will a spurious log on platform where ranges is not empty (midway, arndale, foundation model...).
Quoted 3 lines
If the ranges is present and non-empty it's not impossible that we need to be doing something with it. I'd rather try and figure out how to whitelist such nodes, perhaps they lack a
dev_typecompletely?
Why would we compute the range? Any usable MMIO should be describe the child.
We have to compute the ranges for PCI because we want to map everything at boot time and the PCI devices are not discoverable via the device tree...
AFAICT the parsing of the range is only required when the child are not discoverable via device tree. So the lack of device_type is not a good check.
IHMO all those stuff could be removed by doing the parsing in PHYSDEVOP_pci_device_add.
Anyway this is a different approach which require Xen PCI support in Linux...
Quoted 8 lines
Quoted 8 lines
xen/arch/arm/domain_build.c static int make_timer_node(const struct domain *d, void *fdt, · line 1048
Quoted 4 lines
xen/arch/arm/domain_build.c +4
-0+static int map_device_interrupts (struct domain *d,const struct dt_device_node *dev) +{ + +if ( !dt_property_read_bool (dev,"interrupt-map" ) )It's strange to use
dt_property_read_boolhere anddt_get_propertyabove to check the emptiness.
Quoted 7 lines
Quoted 4 lines
I prefer the
dt_get_propertyversion which is less confusing.Anyway, why do you need to check interrupt-map. Can't your new helper deal with empty property?
It can, but the code below would log for any non-pci device, which is undesirable if there is no interrupt-map present.
It would be undesirable when interrupt-map is present for non-pci device...
We are able to go further down and map the interrupt so you will add more worrying log on normal platform.
It's like the ranges, if we try to compute interrupt-map on non-pci we may have some trouble and route IRQ we should not.
Quoted 30 lines
Quoted 21 lines
Quoted 21 lines
xen/arch/arm/domain_build.c +13
-0+return 0 ;/* No interrupt map to handle */ + +if (dt_device_type_is_equal (dev,"pci" ) ) +return dt_for_each_irq_map (dev, &map_interrupt_to_domain, d); + +printk ("Cannot handle interrupt-map for non-PCI device %s type %s\n" , +dt_node_name (dev), dev->type); + +/* Lets not worry for now... */ +return 0 ; +} + +/* Map the device in the domain */ static int map_device (struct domain *d,struct dt_device_node *dev) {@@ -1025,6 +1173,14 @@ static int map_device(struct domain *d, struct dt_device_node *dev) } }Quoted 8 lines
xen/arch/arm/domain_build.c static int map_device(struct domain *d, struct dt_device_node *dev) · line 1180
Quoted 5 lines
xen/arch/arm/domain_build.c +5
-0+ res =map_device_ranges (d, dev); +if ( res ) +return res; + + res =map_device_interrupts (d, dev);The name of the function doesn't make much sense. We already map the interrupt above (see platform
get_irq).
Quoted 1 line
child_interruptperhaps?
Sounds better. Although, with my point above those 2 functions is PCI related.
Regards,
Signature · 1 line
Julien Grall
Re: [PATCH v2 3/3] xen: arm: handle PCI DT node ranges and interrupt-map properties
Quoted 15 lines · Julien Grall
Quoted 10 lines · Ian Campbell
Quoted 9 lines
xen/arch/arm/domain_build.c static int make_timer_node(const struct domain *d, void *fdt, · line 994
Quoted 6 lines
xen/arch/arm/domain_build.c +6
-0+if (dt_device_type_is_equal (dev,"pci" ) ) +return map_pci_device_ranges (d, dev, ranges, len); + +printk ("Cannot handle ranges for non-PCI device %s type %s\n" , +dt_node_name (dev), dev->type); +Is the printk really necessary? It will a spurious log on platform where ranges is not empty (midway, arndale, foundation model...).
Quoted 3 lines
If the ranges is present and non-empty it's not impossible that we need to be doing something with it. I'd rather try and figure out how to whitelist such nodes, perhaps they lack a
dev_typecompletely?
Quoted 5 lines
Why would we compute the range? Any usable MMIO should be describe the child.
We have to compute the ranges for PCI because we want to map everything at boot time and the PCI devices are not discoverable via the device tree...
The same would be true for any similar discoverable bus.
Which I suppose is uncommon on ARM. I think I'll make this a debug level print, or even a #if DT_DEBUG. (Same for the interrupt case)
Re: [PATCH v2 3/3] xen: arm: handle PCI DT node ranges and interrupt-map properties
Quoted 19 lines · Ian Campbell
Quoted 18 lines · Julien Grall
Quoted 10 lines · Ian Campbell
Quoted 9 lines
xen/arch/arm/domain_build.c static int make_timer_node(const struct domain *d, void *fdt, · line 994
Quoted 6 lines
xen/arch/arm/domain_build.c +6
-0+if (dt_device_type_is_equal (dev,"pci" ) ) +return map_pci_device_ranges (d, dev, ranges, len); + +printk ("Cannot handle ranges for non-PCI device %s type %s\n" , +dt_node_name (dev), dev->type); +Is the printk really necessary? It will a spurious log on platform where ranges is not empty (midway, arndale, foundation model...).
Quoted 3 lines
If the ranges is present and non-empty it's not impossible that we need to be doing something with it. I'd rather try and figure out how to whitelist such nodes, perhaps they lack a
dev_typecompletely?Why would we compute the range? Any usable MMIO should be describe the child.
Quoted 7 lines
Quoted 2 lines
We have to compute the ranges for PCI because we want to map everything at boot time and the PCI devices are not discoverable via the device tree...
The same would be true for any similar discoverable bus.
Which I suppose is uncommon on ARM. I think I'll make this a debug level print, or even a #if
DT_DEBUG. (Same for the interrupt case)
I'm ok with this solution. With Maybe adding a comment explaining that we may need to parse it for discoverable bus?
Regards,
Signature · 1 line
Julien Grall
Quoted 3 lines · Ian Campbell
This series adds parsing of the DT ranges and interrupt-map properties for PCI devices, these contain the MMIOs and IRQs used by children on the bus. This replaces the specific mapping stuff on xgene.
Somehow I managed to completely miss sending out the first patch here (thanks Chen Baozi!)...
This should be inserted at the head of the series.
Quoted 1 line
From
d0a024dd49ca6f67b0ec0342fd2d819b750a52a4Mon Sep 17 00:00:00 2001
From: Ian Campbell <[email protected]>
Date: Fri, 6 Mar 2015 11:13:30 +0000
Subject: [PATCH] xen: dt: add dt_translate_address to translate a raw address
A future patch is going to want to translate an address which is not part of the reg property so the existing dt_device_get_address is not suitable.
This is the same function as Linux's of_translate_address but with the names changed to fit our context and the dev parameter constified.
--- xen/common/device_tree.c | 5 +++++ xen/include/xen/device_tree.h | 2 ++ 2 files changed, 7 insertions(+)
xen/common/device_tree.c +5 -0
@@ -682,6 +682,11 @@ bail:return result; } +u64 dt_translate_address (const struct dt_device_node *dev,const __be32 *in_addr) +{ +return __dt_translate_address (dev, in_addr,"ranges" ); +} +/* dt_device_address - Translate device tree address and return it */ int dt_device_get_address (const struct dt_device_node *dev,int index,u64 *addr,u64 *size)
xen/include/xen/device_tree.h +2 -0
@@ -464,6 +464,8 @@ struct dt_device_node *dt_find_node_by_path(const char *path);*/ const struct dt_device_node *dt_get_parent (const struct dt_device_node *node); +u64 dt_translate_address (const struct dt_device_node *np,const __be32 *addr); +/** * dt_device_get_address - Resolve an address for a device * @device: the device whose address is to be resolved
Signature · 1 line
1.7.10.4
Chen Baozi
Tested-byQuoted 15 lines · Ian Campbell
Quoted 3 lines · Ian Campbell
This series adds parsing of the DT ranges and interrupt-map properties for PCI devices, these contain the MMIOs and IRQs used by children on the bus. This replaces the specific mapping stuff on xgene.
Somehow I managed to completely miss sending out the first patch here (thanks Chen Baozi!)...
This should be inserted at the head of the series.
From
d0a024dd49ca6f67b0ec0342fd2d819b750a52a4Mon Sep 17 00:00:00 2001
From: Ian Campbell <[email protected]>
Date: Fri, 6 Mar 2015 11:13:30 +0000
Subject: [PATCH] xen: dt: adddt_translate_addressto translate a raw address
Quoted 8 lines
A future patch is going to want to translate an address which is not part of the reg property so the existing
dt_device_get_addressis not suitable.This is the same function as Linux's
of_translate_addressbut with the names changed to fit our context and the dev parameter constified.Signed-off-by Ian Campbell <[email protected]>
These 4 patches works for me.
Quoted 40 lines
--- xen/common/device_tree.c | 5 +++++ xen/include/xen/device_tree.h | 2 ++ 2 files changed, 7 insertions(+)xen/common/device_tree.c +5
-0@@ -682,6 +682,11 @@ bail:return result; } +u64 dt_translate_address (const struct dt_device_node *dev,const __be32 *in_addr) +{ +return __dt_translate_address (dev, in_addr,"ranges" ); +} +/* dt_device_address - Translate device tree address and return it */ int dt_device_get_address (const struct dt_device_node *dev,int index,u64 *addr,u64 *size)xen/include/xen/device_tree.h +2
-0@@ -464,6 +464,8 @@ struct dt_device_node *dt_find_node_by_path(const char *path);*/ const struct dt_device_node *dt_get_parent (const struct dt_device_node *node); +u64 dt_translate_address (const struct dt_device_node *np,const __be32 *addr); +/** * dt_device_get_address - Resolve an address for a device * @device: the device whose address is to be resolved Signature · 1 line
1.7.10.4