Re: [PATCH] libfdt: add address translation support

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



On Tue, 2014-04-01 at 11:57 -0500, Rob Herring wrote:
> From: Rob Herring <robh@xxxxxxxxxx>
> 
> Add FDT based address translation. Currently, only simple bus
> translations are supported, but the framework allows adding other
> buses like PCI. Adding this to libfdt allows the address
> translation code to be shared amongst u-boot, Linux kernel and other
> projects.
> 
> This code is based on GPL only licensed code. It must first be
> re-licensed for dual GPL/BSD to add to libfdt which requires approval
> from the copyright holders.
> 
> This code is copied from u-boot common/fdt_support.c. The portion used
> here was originally added to u-boot by Kumar Gala in 2010 and Freescale
> is the copyright holder. Later changes have also only been done by
> Freescale authors. The u-boot code appears to have been copied from the
> Linux kernel's address translation code in drivers/of/address.c which
> has no copyright. This code was moved by Grant Likely in 2010 and
> originated from arch/powerpc/kernel/prom_parse.c which was written by
> Ben Herrenschmidt. Who is the copyright holder on a file in the kernel
> with no copyright? Does this default to the author or Linus or nobody?

ACK for the Freescale contribution (on copyright issues, not technical -- see below).

FWIW, if you have trouble tracking everyone down for this code, there's
address translation code in arch/powerpc/boot/devtree.c that has a
cleaner history (I wrote it from scratch, and there's been only minor
modifications since then, by Mark Greer).

> Signed-off-by: Rob Herring <robh@xxxxxxxxxx>
> Cc: Grant Likely <grant.likely@xxxxxxxxxx>
> Cc: Scott Wood <scottwood@xxxxxxxxxxxxx>
> Cc: Kim Phillips <kim.phillips@xxxxxxxxxxxxx>
> Cc: Kumar Gala <galak@xxxxxxxxxxxxxxxxxxx>
> Cc: Benjamin Herrenschmidt <benh@xxxxxxxxxxxxxxxxxxx>
> ---
>  libfdt/fdt_ro.c | 203 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  libfdt/libfdt.h |   4 ++
>  2 files changed, 207 insertions(+)
> 
> diff --git a/libfdt/fdt_ro.c b/libfdt/fdt_ro.c
> index 50007f6..3c1bde1 100644
> --- a/libfdt/fdt_ro.c
> +++ b/libfdt/fdt_ro.c
> @@ -571,3 +571,206 @@ int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
>  
>  	return offset; /* error from fdt_next_node() */
>  }
> +
> +/* Max address size we deal with */
> +#define OF_MAX_ADDR_CELLS	4
> +#define OF_CHECK_COUNTS(na, ns)	((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \
> +			(ns) > 0)
> +
> +/* Helper to read a big number; size is in cells (not bytes) */
> +static uint64_t fdt_read_number(const fdt32_t *cell, int size)
> +{
> +	uint64_t r = 0;
> +	while (size--)
> +		r = (r << 32) | fdt32_to_cpu(*(cell++));
> +	return r;
> +}
> +
> +/* Callbacks for bus specific translators */
> +struct of_bus {
> +	void (*count_cells)(void *blob, int parentoffset,
> +			    int *addrc, int *sizec);
> +	uint64_t (*map)(fdt32_t *addr, const fdt32_t *range,
> +				int na, int ns, int pna);
> +	int (*translate)(fdt32_t *addr, uint64_t offset, int na);
> +};
> +
> +/* Default translator (generic bus) */
> +static void fdt_bus_default_count_cells(void *blob, int parentoffset,
> +				 	int *addrc, int *sizec)
> +{
> +	const fdt32_t *prop;
> +
> +	if (addrc) {
> +		prop = fdt_getprop(blob, parentoffset, "#address-cells", NULL);
> +		if (prop)
> +			*addrc = fdt32_to_cpu(*prop);
> +		else
> +			*addrc = 2;
> +	}
> +
> +	if (sizec) {
> +		prop = fdt_getprop(blob, parentoffset, "#size-cells", NULL);
> +		if (prop)
> +			*sizec = fdt32_to_cpu(*prop);
> +		else
> +			*sizec = 2;
> +	}
> +}

#size-cells should default to 1 as per IEEE1275 (ePAPR agrees, though
it calls a missing #size-cells non-compliant).  This is also what
kernel code currently seems to do (see OF_ROOT_NODE_SIZE_CELLS_DEFAULT).

> +static uint64_t fdt_bus_default_map(fdt32_t *addr, const fdt32_t *range,
> +				    int na, int ns, int pna)
> +{
> +	uint64_t cp, s, da;
> +
> +	cp = fdt_read_number(range, na);
> +	s  = fdt_read_number(range + na + pna, ns);
> +	da = fdt_read_number(addr, na);
> +
> +	if (da < cp || da >= (cp + s))
> +		return FDT_BAD_ADDR;
> +	return da - cp;
> +}
> +
> +static int fdt_bus_default_translate(fdt32_t *addr, uint64_t offset, int na)
> +{
> +	uint64_t a = fdt_read_number(addr, na);
> +	memset(addr, 0, na * 4);
> +	a += offset;
> +	if (na > 1)
> +		addr[na - 2] = cpu_to_fdt32(a >> 32);
> +	addr[na - 1] = cpu_to_fdt32(a & 0xffffffffu);
> +
> +	return 0;
> +}
> +
> +/* Array of bus specific translators */
> +static const struct of_bus of_busses[] = {
> +	/* Default */
> +	{
> +		.count_cells = fdt_bus_default_count_cells,
> +		.map = fdt_bus_default_map,
> +		.translate = fdt_bus_default_translate,
> +	},
> +};
> +
> +static int fdt_translate_one(void * blob, int parent,
> +			     const struct of_bus *bus,
> +			     const struct of_bus *pbus, fdt32_t *addr,
> +			     int na, int ns, int pna, const char *rprop)
> +{
> +	const fdt32_t *ranges;
> +	int rlen;
> +	int rone;
> +	uint64_t offset = FDT_BAD_ADDR;
> +
> +	/* Normally, an absence of a "ranges" property means we are
> +	 * crossing a non-translatable boundary, and thus the addresses
> +	 * below the current not cannot be converted to CPU physical ones.
> +	 * Unfortunately, while this is very clear in the spec, it's not
> +	 * what Apple understood, and they do have things like /uni-n or
> +	 * /ht nodes with no "ranges" property and a lot of perfectly
> +	 * useable mapped devices below them. Thus we treat the absence of
> +	 * "ranges" as equivalent to an empty "ranges" property which means
> +	 * a 1:1 translation at that level. It's up to the caller not to try
> +	 * to translate addresses that aren't supposed to be translated in
> +	 * the first place. --BenH.
> +	 */

I don't think libfdt should be copying a non-compliant hack that was
meant to deal with broken Apple firmware that doesn't use flat trees.

-Scott

> +	ranges = fdt_getprop(blob, parent, rprop, &rlen);
> +	if (ranges == NULL || rlen == 0) {
> +		offset = fdt_read_number(addr, na);
> +		memset(addr, 0, pna * 4);
> +		goto finish;
> +	}
> +
> +	/* Now walk through the ranges */
> +	rlen /= 4;
> +	rone = na + pna + ns;
> +	for (; rlen >= rone; rlen -= rone, ranges += rone) {
> +		offset = bus->map(addr, ranges, na, ns, pna);
> +		if (offset != FDT_BAD_ADDR)
> +			break;
> +	}
> +	if (offset == FDT_BAD_ADDR)
> +		return -FDT_ERR_NOTFOUND;
> +
> +	memcpy(addr, ranges + na, 4 * pna);
> +
> + finish:
> +	/* Translate it into parent bus space */
> +	return pbus->translate(addr, offset, pna);
> +}
> +
> +static uint64_t __fdt_translate_address(void *blob, int node_offset,
> +					const char *rprop)
> +{
> +	int parent, len;
> +	const struct of_bus *bus, *pbus;
> +	const fdt32_t *reg;
> +	fdt32_t addr[OF_MAX_ADDR_CELLS];
> +	int na, ns, pna, pns;
> +	uint64_t result = FDT_BAD_ADDR;
> +
> +	reg = fdt_getprop(blob, node_offset, "reg", &len);
> +	if (!reg)
> +		goto bail;
> +
> +	/* Get parent & match bus type */
> +	parent = fdt_parent_offset(blob, node_offset);
> +	if (parent < 0)
> +		goto bail;
> +	bus = &of_busses[0];
> +
> +	/* Cound address cells & copy address locally */
> +	bus->count_cells(blob, parent, &na, &ns);
> +	if (!OF_CHECK_COUNTS(na, ns))
> +		goto bail;
> +
> +	memcpy(addr, reg, na * 4);
> +
> +	/* Translate */
> +	for (;;) {
> +		/* Switch to parent bus */
> +		node_offset = parent;
> +		parent = fdt_parent_offset(blob, node_offset);
> +
> +		/* If root, we have finished */
> +		if (parent < 0) {
> +			result = fdt_read_number(addr, na);
> +			break;
> +		}
> +
> +		/* Get new parent bus and counts */
> +		pbus = &of_busses[0];
> +		pbus->count_cells(blob, parent, &pna, &pns);
> +		if (!OF_CHECK_COUNTS(pna, pns))
> +			break;
> +
> +		/* Apply bus translation */
> +		if (fdt_translate_one(blob, node_offset, bus, pbus,
> +					addr, na, ns, pna, "ranges"))
> +			break;
> +
> +		/* Complete the move up one level */
> +		na = pna;
> +		ns = pns;
> +		bus = pbus;
> +	}
> + bail:
> +	return result;
> +}
> +
> +/*
> + * Translate an address from the device-tree into a CPU physical address,
> + * this walks up the tree and applies the various bus mappings on the
> + * way.
> + *
> + * Note: We consider that crossing any level with #size-cells == 0 to mean
> + * that translation is impossible (that is we are not dealing with a value
> + * that can be mapped to a cpu physical address). This is not really specified
> + * that way, but this is traditionally the way IBM at least do things
> + */
> +uint64_t fdt_translate_address(void *fdt, int node_offset)
> +{
> +	return __fdt_translate_address(fdt, node_offset, "ranges");
> +}
> diff --git a/libfdt/libfdt.h b/libfdt/libfdt.h
> index c4d5a91..afbea65 100644
> --- a/libfdt/libfdt.h
> +++ b/libfdt/libfdt.h
> @@ -118,6 +118,8 @@
>  
>  #define FDT_ERR_MAX		13
>  
> +#define FDT_BAD_ADDR		((uint64_t)-1)
> +
>  /**********************************************************************/
>  /* Low-level functions (you probably don't need these)                */
>  /**********************************************************************/
> @@ -852,6 +854,8 @@ int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
>   */
>  int fdt_stringlist_contains(const char *strlist, int listlen, const char *str);
>  
> +uint64_t fdt_translate_address(void *blob, int node_offset);
> +
>  /**********************************************************************/
>  /* Write-in-place functions                                           */
>  /**********************************************************************/



--
To unsubscribe from this list: send the line "unsubscribe devicetree-compiler" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Device Tree]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux