Re: [PATCH] libfdt: add address translation support

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



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




[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