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                if (new_format) {
 101                        np->name = pathp;
 102                        has_name = 1;
 103                }
 104                np->full_name = fn;
 105                if (new_format) {
 106                        /* rebuild full path for new format */
 107                        if (dad && dad->parent) {
 108                                strcpy(fn, dad->full_name);
 109#ifdef DEBUG
 110                                if ((strlen(fn) + l + 1) != allocl) {
 111                                        debug("%s: p: %d, l: %d, a: %d\n",
 112                                              pathp, (int)strlen(fn), l,
 113                                              allocl);
 114                                }
 115#endif
 116                                fn += strlen(fn);
 117                        }
 118                        *(fn++) = '/';
 119                }
 120                memcpy(fn, pathp, l);
 121
 122                prev_pp = &np->properties;
 123                if (dad != NULL) {
 124                        np->parent = dad;
 125                        np->sibling = dad->child;
 126                        dad->child = np;
 127                }
 128        }
 129        /* process properties */
 130        for (offset = fdt_first_property_offset(blob, *poffset);
 131             (offset >= 0);
 132             (offset = fdt_next_property_offset(blob, offset))) {
 133                const char *pname;
 134                int sz;
 135
 136                p = fdt_getprop_by_offset(blob, offset, &pname, &sz);
 137                if (!p) {
 138                        offset = -FDT_ERR_INTERNAL;
 139                        break;
 140                }
 141
 142                if (pname == NULL) {
 143                        debug("Can't find property name in list !\n");
 144                        break;
 145                }
 146                if (strcmp(pname, "name") == 0)
 147                        has_name = 1;
 148                pp = unflatten_dt_alloc(&mem, sizeof(struct property),
 149                                        __alignof__(struct property));
 150                if (!dryrun) {
 151                        /*
 152                         * We accept flattened tree phandles either in
 153                         * ePAPR-style "phandle" properties, or the
 154                         * legacy "linux,phandle" properties.  If both
 155                         * appear and have different values, things
 156                         * will get weird.  Don't do that. */
 157                        if ((strcmp(pname, "phandle") == 0) ||
 158                            (strcmp(pname, "linux,phandle") == 0)) {
 159                                if (np->phandle == 0)
 160                                        np->phandle = be32_to_cpup(p);
 161                        }
 162                        /*
 163                         * And we process the "ibm,phandle" property
 164                         * used in pSeries dynamic device tree
 165                         * stuff */
 166                        if (strcmp(pname, "ibm,phandle") == 0)
 167                                np->phandle = be32_to_cpup(p);
 168                        pp->name = (char *)pname;
 169                        pp->length = sz;
 170                        pp->value = (__be32 *)p;
 171                        *prev_pp = pp;
 172                        prev_pp = &pp->next;
 173                }
 174        }
 175        /*
 176         * with version 0x10 we may not have the name property, recreate
 177         * it here from the unit name if absent
 178         */
 179        if (!has_name) {
 180                const char *p1 = pathp, *ps = pathp, *pa = NULL;
 181                int sz;
 182
 183                while (*p1) {
 184                        if ((*p1) == '@')
 185                                pa = p1;
 186                        if ((*p1) == '/')
 187                                ps = p1 + 1;
 188                        p1++;
 189                }
 190                if (pa < ps)
 191                        pa = p1;
 192                sz = (pa - ps) + 1;
 193                pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,
 194                                        __alignof__(struct property));
 195                if (!dryrun) {
 196                        pp->name = "name";
 197                        pp->length = sz;
 198                        pp->value = pp + 1;
 199                        *prev_pp = pp;
 200                        prev_pp = &pp->next;
 201                        memcpy(pp->value, ps, sz - 1);
 202                        ((char *)pp->value)[sz - 1] = 0;
 203                        debug("fixed up name for %s -> %s\n", pathp,
 204                              (char *)pp->value);
 205                }
 206        }
 207        if (!dryrun) {
 208                *prev_pp = NULL;
 209                if (!has_name)
 210                        np->name = of_get_property(np, "name", NULL);
 211                np->type = of_get_property(np, "device_type", NULL);
 212
 213                if (!np->name)
 214                        np->name = "<NULL>";
 215                if (!np->type)
 216                        np->type = "<NULL>";    }
 217
 218        old_depth = depth;
 219        *poffset = fdt_next_node(blob, *poffset, &depth);
 220        if (depth < 0)
 221                depth = 0;
 222        while (*poffset > 0 && depth > old_depth) {
 223                mem = unflatten_dt_node(blob, mem, poffset, np, NULL,
 224                                        fpsize, dryrun);
 225                if (!mem)
 226                        return NULL;
 227        }
 228
 229        if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND) {
 230                debug("unflatten: error %d processing FDT\n", *poffset);
 231                return NULL;
 232        }
 233
 234        /*
 235         * Reverse the child list. Some drivers assumes node order matches .dts
 236         * node order
 237         */
 238        if (!dryrun && np->child) {
 239                struct device_node *child = np->child;
 240                np->child = NULL;
 241                while (child) {
 242                        struct device_node *next = child->sibling;
 243
 244                        child->sibling = np->child;
 245                        np->child = child;
 246                        child = next;
 247                }
 248        }
 249
 250        if (nodepp)
 251                *nodepp = np;
 252
 253        return mem;
 254}
 255
 256int unflatten_device_tree(const void *blob, struct device_node **mynodes)
 257{
 258        unsigned long size;
 259        int start;
 260        void *mem;
 261
 262        debug(" -> unflatten_device_tree()\n");
 263
 264        if (!blob) {
 265                debug("No device tree pointer\n");
 266                return -EINVAL;
 267        }
 268
 269        debug("Unflattening device tree:\n");
 270        debug("magic: %08x\n", fdt_magic(blob));
 271        debug("size: %08x\n", fdt_totalsize(blob));
 272        debug("version: %08x\n", fdt_version(blob));
 273
 274        if (fdt_check_header(blob)) {
 275                debug("Invalid device tree blob header\n");
 276                return -EINVAL;
 277        }
 278
 279        /* First pass, scan for size */
 280        start = 0;
 281        size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL,
 282                                                0, true);
 283        if (!size)
 284                return -EFAULT;
 285        size = ALIGN(size, 4);
 286
 287        debug("  size is %lx, allocating...\n", size);
 288
 289        /* Allocate memory for the expanded device tree */
 290        mem = malloc(size + 4);
 291        memset(mem, '\0', size);
 292
 293        *(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef);
 294
 295        debug("  unflattening %p...\n", mem);
 296
 297        /* Second pass, do actual unflattening */
 298        start = 0;
 299        unflatten_dt_node(blob, mem, &start, NULL, mynodes, 0, false);
 300        if (be32_to_cpup(mem + size) != 0xdeadbeef) {
 301                debug("End of tree marker overwritten: %08x\n",
 302                      be32_to_cpup(mem + size));
 303                return -ENOSPC;
 304        }
 305
 306        debug(" <- unflatten_device_tree()\n");
 307
 308        return 0;
 309}
 310
 311int of_live_build(const void *fdt_blob, struct device_node **rootp)
 312{
 313        int ret;
 314
 315        debug("%s: start\n", __func__);
 316        ret = unflatten_device_tree(fdt_blob, rootp);
 317        if (ret) {
 318                debug("Failed to create live tree: err=%d\n", ret);
 319                return ret;
 320        }
 321        ret = of_alias_scan();
 322        if (ret) {
 323                debug("Failed to scan live tree aliases: err=%d\n", ret);
 324                return ret;
 325        }
 326        debug("%s: stop\n", __func__);
 327
 328        return ret;
 329}
 330