[Skiboot] [PATCH 1/2] device: implement dt_translate_address() properly

Michael Neuling mikey at neuling.org
Tue Mar 7 14:01:50 AEDT 2017


On Fri, 2017-01-27 at 20:09 +1100, Oliver O'Halloran wrote:
> Currently this is implemented by calling dt_get_address() which only
> works when a device is a child of the root node. This patch implements
> the functionality to work with nested nodes when all parent nodes have
> an appropriate "ranges" property.
> 
> This implementation only works for up to 64 bit addresses. Properly
> supporting larger addressing schemes is a fair amount of (probably
> pointless) work, so I'm leaving supporting that until we have an
> actual a need for it.
> 
> Cc: Rob Lippert <rlippert at google.com>
> Signed-off-by: Oliver O'Halloran <oohall at gmail.com>

Tested-by: Michael Neuling <mikey at neuling.org>

> ---
>  core/device.c          | 65 ++++++++++++++++++++++++++++++++++++++++++++++++-
> -
>  core/test/run-device.c | 47 +++++++++++++++++++++++++++++++++++-
>  2 files changed, 109 insertions(+), 3 deletions(-)
> 
> diff --git a/core/device.c b/core/device.c
> index 30b31f461c45..76c2f8481b9f 100644
> --- a/core/device.c
> +++ b/core/device.c
> @@ -942,11 +942,72 @@ unsigned int dt_count_addresses(const struct dt_node
> *node)
>  	return p->len / n;
>  }
>  
> +/* Translates an address from the given bus into its parent's address space
> */
> +static u64 dt_translate_one(const struct dt_node *bus, u64 addr)
> +{
> +	u32 ranges_count, na, ns, parent_na;
> +	const struct dt_property *p;
> +	const u32 *ranges;
> +	int i, stride;
> +
> +	assert(bus->parent);
> +
> +	na = dt_prop_get_u32_def(bus, "#address-cells", 2);
> +	ns = dt_prop_get_u32_def(bus, "#size-cells", 2);
> +	parent_na = dt_n_address_cells(bus);
> +
> +	stride = na + ns + parent_na;
> +
> +	/*
> +	 * FIXME: We should handle arbitrary length addresses, rather than
> +	 *        limiting it to 64bit. If someone wants/needs that they
> +	 *        can implement the bignum math for it :)
> +	 */
> +	assert(na <= 2);
> +	assert(parent_na <= 2);
> +
> +	/* We should never be trying to translate an address without a ranges
> */
> +	p = dt_require_property(bus, "ranges", -1);
> +
> +	ranges = (u32 *) &p->prop;
> +	ranges_count = (p->len / 4) / (na + parent_na + ns);
> +
> +	/* An empty ranges property implies 1-1 translation */
> +	if (ranges_count == 0)
> +		return addr;
> +
> +	for (i = 0; i < ranges_count; i++, ranges += stride) {
> +		/* ranges format: <child base> <parent base> <size> */
> +		u64 child_base = dt_get_number(ranges, na);
> +		u64 parent_base = dt_get_number(ranges + na, parent_na);
> +		u64 size = dt_get_number(ranges + na + parent_na, ns);
> +
> +		if (addr >= child_base && addr < child_base + size)
> +			return (addr - child_base) + parent_base;
> +	}
> +
> +	/* input address was outside the any of our mapped ranges */
> +	return 0;
> +}
> +
>  u64 dt_translate_address(const struct dt_node *node, unsigned int index,
>  			 u64 *out_size)
>  {
> -	/* XXX TODO */
> -	return dt_get_address(node, index, out_size);
> +	u64 addr = dt_get_address(node, index, NULL);
> +	struct dt_node *bus = node->parent;
> +
> +	/* FIXME: One day we will probably want to use this, but for now just
> +	 * force it it to be zero since we only support returning a u64 or
> u32
> +	 */
> +	assert(!out_size);
> +
> +	/* apply each translation until we hit the root bus */
> +	while (bus->parent) {
> +		addr = dt_translate_one(bus, addr);
> +		bus = bus->parent;
> +	}
> +
> +	return addr;
>  }
>  
>  bool dt_node_is_enabled(struct dt_node *node)
> diff --git a/core/test/run-device.c b/core/test/run-device.c
> index 3da4a2fbcc44..e91e5a5fc5c6 100644
> --- a/core/test/run-device.c
> +++ b/core/test/run-device.c
> @@ -90,7 +90,7 @@ static bool is_sorted(const struct dt_node *root)
>  
>  int main(void)
>  {
> -	struct dt_node *root, *c1, *c2, *gc1, *gc2, *gc3, *ggc1;
> +	struct dt_node *root, *c1, *c2, *gc1, *gc2, *gc3, *ggc1, *ggc2;
>  	struct dt_node *addrs, *addr1, *addr2;
>  	struct dt_node *i;
>  	const struct dt_property *p;
> @@ -366,6 +366,51 @@ int main(void)
>  
>  	dt_free(root);
>  
> +	/* check dt_translate_address */
> +
> +	/* NB: the root bus has two address cells */
> +	root = dt_new_root("");
> +
> +	c1 = dt_new_addr(root, "some-32bit-bus", 0x80000000);
> +	dt_add_property_cells(c1, "#address-cells", 1);
> +	dt_add_property_cells(c1, "#size-cells", 1);
> +	dt_add_property_cells(c1, "ranges", 0x0, 0x8, 0x0, 0x1000);
> +
> +	gc1 = dt_new_addr(c1, "test", 0x0500);
> +	dt_add_property_cells(gc1, "reg", 0x0500, 0x10);
> +
> +	assert(dt_translate_address(gc1, 0, NULL) == 0x800000500ul);
> +
> +	/* try three level translation */
> +
> +	gc2 = dt_new_addr(c1, "another-32bit-bus", 0x40000000);
> +	dt_add_property_cells(gc2, "#address-cells", 1);
> +	dt_add_property_cells(gc2, "#size-cells", 1);
> +	dt_add_property_cells(gc2, "ranges",	0x0, 0x600, 0x100,
> +						0x100, 0x800, 0x100);
> +
> +	ggc1 = dt_new_addr(gc2, "test", 0x50);
> +	dt_add_property_cells(ggc1, "reg", 0x50, 0x10);
> +	assert(dt_translate_address(ggc1, 0, NULL) == 0x800000650ul);
> +
> +	/* test multiple ranges work */
> +	ggc2 = dt_new_addr(gc2, "test", 0x150);
> +	dt_add_property_cells(ggc2, "reg", 0x150, 0x10);
> +	assert(dt_translate_address(ggc2, 0, NULL) == 0x800000850ul);
> +
> +	/* try 64bit -> 64bit */
> +
> +	c2 = dt_new_addr(root, "some-64bit-bus", 0xe00000000);
> +	dt_add_property_cells(c2, "#address-cells", 2);
> +	dt_add_property_cells(c2, "#size-cells", 2);
> +	dt_add_property_cells(c2, "ranges", 0x0, 0x0, 0xe, 0x0, 0x2, 0x0);
> +
> +	gc2 = dt_new_addr(c2, "test", 0x100000000ul);
> +	dt_add_property_u64s(gc2, "reg", 0x100000000ul, 0x10ul);
> +	assert(dt_translate_address(gc2, 0, NULL) == 0xf00000000ul);
> +
> +	dt_free(root);
> +
>  	return 0;
>  }
>  


More information about the Skiboot mailing list