uboot/lib/of_live.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright 2009 Benjamin Herrenschmidt, IBM Corp
   4 * benh@kernel.crashing.org
   5 *
   6 * Based on parts of drivers/of/fdt.c from Linux v4.9
   7 * Modifications for U-Boot
   8 * Copyright (c) 2017 Google, Inc
   9 */
  10
  11#include <common.h>
  12#include <log.h>
  13#include <linux/libfdt.h>
  14#include <of_live.h>
  15#include <malloc.h>
  16#include <dm/of_access.h>
  17#include <linux/err.h>
  18
  19static void *unflatten_dt_alloc(void **mem, unsigned long size,
  20                                unsigned long align)
  21{
  22        void *res;
  23
  24        *mem = PTR_ALIGN(*mem, align);
  25        res = *mem;
  26        *mem += size;
  27
  28        return res;
  29}
  30
  31/**
  32 * unflatten_dt_node() - Alloc and populate a device_node from the flat tree
  33 * @blob: The parent device tree blob
  34 * @mem: Memory chunk to use for allocating device nodes and properties
  35 * @poffset: pointer to node in flat tree
  36 * @dad: Parent struct device_node
  37 * @nodepp: The device_node tree created by the call
  38 * @fpsize: Size of the node path up at t05he current depth.
  39 * @dryrun: If true, do not allocate device nodes but still calculate needed
  40 * memory size
  41 */
  42static void *unflatten_dt_node(const void *blob, void *mem, int *poffset,
  43                               struct device_node *dad,
  44                               struct device_node **nodepp,
  45                               unsigned long fpsize, bool dryrun)
  46{
  47        const __be32 *p;
  48        struct device_node *np;
  49        struct property *pp, **prev_pp = NULL;
  50        const char *pathp;
  51        int l;
  52        unsigned int allocl;
  53        static int depth;
  54        int old_depth;
  55        int offset;
  56        int has_name = 0;
  57        int new_format = 0;
  58
  59        pathp = fdt_get_name(blob, *poffset, &l);
  60        if (!pathp)
  61                return mem;
  62
  63        allocl = ++l;
  64
  65        /*
  66         * version 0x10 has a more compact unit name here instead of the full
  67         * path. we accumulate the full path size using "fpsize", we'll rebuild
  68         * it later. We detect this because the first character of the name is
  69         * not '/'.
  70         */
  71        if ((*pathp) != '/') {
  72                new_format = 1;
  73                if (fpsize == 0) {
  74                        /*
  75                         * root node: special case. fpsize accounts for path
  76                         * plus terminating zero. root node only has '/', so
  77                         * fpsize should be 2, but we want to avoid the first
  78                         * level nodes to have two '/' so we use fpsize 1 here
  79                         */
  80                        fpsize = 1;
  81                        allocl = 2;
  82                        l = 1;
  83                        pathp = "";
  84                } else {
  85                        /*
  86                         * account for '/' and path size minus terminal 0
  87                         * already in 'l'
  88                         */
  89                        fpsize += l;
  90                        allocl = fpsize;
  91                }
  92        }
  93
  94        np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,
  95                                __alignof__(struct device_node));
  96        if (!dryrun) {
  97                char *fn;
  98
  99                fn = (char *)np + sizeof(*np);
 100                np->full_name = fn;
 101                if (new_format) {
 102                        /* rebuild full path for new format */
 103                        if (dad && dad->parent) {
 104                                strcpy(fn, dad->full_name);
 105#ifdef DEBUG
 106                                if ((strlen(fn) + l + 1) != allocl) {
 107                                        debug("%s: p: %d, l: %d, a: %d\n",
 108                                              pathp, (int)strlen(fn), l,
 109                                              allocl);
 110                                }
 111#endif
 112                                fn += strlen(fn);
 113                        }
 114                        *(fn++) = '/';
 115                }
 116                memcpy(fn, pathp, l);
 117
 118                prev_pp = &np->properties;
 119                if (dad != NULL) {
 120                        np->parent = dad;
 121                        np->sibling = dad->child;
 122                        dad->child = np;
 123                }
 124        }
 125        /* process properties */
 126        for (offset = fdt_first_property_offset(blob, *poffset);
 127             (offset >= 0);
 128             (offset = fdt_next_property_offset(blob, offset))) {
 129                const char *pname;
 130                int sz;
 131
 132                p = fdt_getprop_by_offset(blob, offset, &pname, &sz);
 133                if (!p) {
 134                        offset = -FDT_ERR_INTERNAL;
 135                        break;
 136                }
 137
 138                if (pname == NULL) {
 139                        debug("Can't find property name in list !\n");
 140                        break;
 141                }
 142                if (strcmp(pname, "name") == 0)
 143                        has_name = 1;
 144                pp = unflatten_dt_alloc(&mem, sizeof(struct property),
 145                                        __alignof__(struct property));
 146                if (!dryrun) {
 147                        /*
 148                         * We accept flattened tree phandles either in
 149                         * ePAPR-style "phandle" properties, or the
 150                         * legacy "linux,phandle" properties.  If both
 151                         * appear and have different values, things
 152                         * will get weird.  Don't do that. */
 153                        if ((strcmp(pname, "phandle") == 0) ||
 154                            (strcmp(pname, "linux,phandle") == 0)) {
 155                                if (np->phandle == 0)
 156                                        np->phandle = be32_to_cpup(p);
 157                        }
 158                        /*
 159                         * And we process the "ibm,phandle" property
 160                         * used in pSeries dynamic device tree
 161                         * stuff */
 162                        if (strcmp(pname, "ibm,phandle") == 0)
 163                                np->phandle = be32_to_cpup(p);
 164                        pp->name = (char *)pname;
 165                        pp->length = sz;
 166                        pp->value = (__be32 *)p;
 167                        *prev_pp = pp;
 168                        prev_pp = &pp->next;
 169                }
 170        }
 171        /*
 172         * with version 0x10 we may not have the name property, recreate
 173         * it here from the unit name if absent
 174         */
 175        if (!has_name) {
 176                const char *p1 = pathp, *ps = pathp, *pa = NULL;
 177                int sz;
 178
 179                while (*p1) {
 180                        if ((*p1) == '@')
 181                                pa = p1;
 182                        if ((*p1) == '/')
 183                                ps = p1 + 1;
 184                        p1++;
 185                }
 186                if (pa < ps)
 187                        pa = p1;
 188                sz = (pa - ps) + 1;
 189                pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,
 190                                        __alignof__(struct property));
 191                if (!dryrun) {
 192                        pp->name = "name";
 193                        pp->length = sz;
 194                        pp->value = pp + 1;
 195                        *prev_pp = pp;
 196                        prev_pp = &pp->next;
 197                        memcpy(pp->value, ps, sz - 1);
 198                        ((char *)pp->value)[sz - 1] = 0;
 199                        debug("fixed up name for %s -> %s\n", pathp,
 200                              (char *)pp->value);
 201                }
 202        }
 203        if (!dryrun) {
 204                *prev_pp = NULL;
 205                np->name = of_get_property(np, "name", NULL);
 206                np->type = of_get_property(np, "device_type", NULL);
 207
 208                if (!np->name)
 209                        np->name = "<NULL>";
 210                if (!np->type)
 211                        np->type = "<NULL>";    }
 212
 213        old_depth = depth;
 214        *poffset = fdt_next_node(blob, *poffset, &depth);
 215        if (depth < 0)
 216                depth = 0;
 217        while (*poffset > 0 && depth > old_depth) {
 218                mem = unflatten_dt_node(blob, mem, poffset, np, NULL,
 219                                        fpsize, dryrun);
 220                if (!mem)
 221                        return NULL;
 222        }
 223
 224        if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND) {
 225                debug("unflatten: error %d processing FDT\n", *poffset);
 226                return NULL;
 227        }
 228
 229        /*
 230         * Reverse the child list. Some drivers assumes node order matches .dts
 231         * node order
 232         */
 233        if (!dryrun && np->child) {
 234                struct device_node *child = np->child;
 235                np->child = NULL;
 236                while (child) {
 237                        struct device_node *next = child->sibling;
 238
 239                        child->sibling = np->child;
 240                        np->child = child;
 241                        child = next;
 242                }
 243        }
 244
 245        if (nodepp)
 246                *nodepp = np;
 247
 248        return mem;
 249}
 250
 251/**
 252 * unflatten_device_tree() - create tree of device_nodes from flat blob
 253 *
 254 * unflattens a device-tree, creating the
 255 * tree of struct device_node. It also fills the "name" and "type"
 256 * pointers of the nodes so the normal device-tree walking functions
 257 * can be used.
 258 * @blob: The blob to expand
 259 * @mynodes: The device_node tree created by the call
 260 * @return 0 if OK, -ve on error
 261 */
 262static int unflatten_device_tree(const void *blob,
 263                                 struct device_node **mynodes)
 264{
 265        unsigned long size;
 266        int start;
 267        void *mem;
 268
 269        debug(" -> unflatten_device_tree()\n");
 270
 271        if (!blob) {
 272                debug("No device tree pointer\n");
 273                return -EINVAL;
 274        }
 275
 276        debug("Unflattening device tree:\n");
 277        debug("magic: %08x\n", fdt_magic(blob));
 278        debug("size: %08x\n", fdt_totalsize(blob));
 279        debug("version: %08x\n", fdt_version(blob));
 280
 281        if (fdt_check_header(blob)) {
 282                debug("Invalid device tree blob header\n");
 283                return -EINVAL;
 284        }
 285
 286        /* First pass, scan for size */
 287        start = 0;
 288        size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL,
 289                                                0, true);
 290        if (!size)
 291                return -EFAULT;
 292        size = ALIGN(size, 4);
 293
 294        debug("  size is %lx, allocating...\n", size);
 295
 296        /* Allocate memory for the expanded device tree */
 297        mem = malloc(size + 4);
 298        memset(mem, '\0', size);
 299
 300        *(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef);
 301
 302        debug("  unflattening %p...\n", mem);
 303
 304        /* Second pass, do actual unflattening */
 305        start = 0;
 306        unflatten_dt_node(blob, mem, &start, NULL, mynodes, 0, false);
 307        if (be32_to_cpup(mem + size) != 0xdeadbeef) {
 308                debug("End of tree marker overwritten: %08x\n",
 309                      be32_to_cpup(mem + size));
 310                return -ENOSPC;
 311        }
 312
 313        debug(" <- unflatten_device_tree()\n");
 314
 315        return 0;
 316}
 317
 318int of_live_build(const void *fdt_blob, struct device_node **rootp)
 319{
 320        int ret;
 321
 322        debug("%s: start\n", __func__);
 323        ret = unflatten_device_tree(fdt_blob, rootp);
 324        if (ret) {
 325                debug("Failed to create live tree: err=%d\n", ret);
 326                return ret;
 327        }
 328        ret = of_alias_scan();
 329        if (ret) {
 330                debug("Failed to scan live tree aliases: err=%d\n", ret);
 331                return ret;
 332        }
 333        debug("%s: stop\n", __func__);
 334
 335        return ret;
 336}
 337