On Tue, 2014-04-01 at 11:57 -0500, Rob Herring wrote:
> From: Rob Herring <[email protected]>
> 
> 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 <[email protected]>
> Cc: Grant Likely <[email protected]>
> Cc: Scott Wood <[email protected]>
> Cc: Kim Phillips <[email protected]>
> Cc: Kumar Gala <[email protected]>
> Cc: Benjamin Herrenschmidt <[email protected]>
> ---
>  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 [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to