Ben, On Tue, Apr 1, 2014 at 6:02 PM, Scott Wood <scottwood@xxxxxxxxxxxxx> wrote: > 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). Can you please comment on the re-licensing of this code. Rob > 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