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