Commit 8b50d526ea5b1e74934cddf6f1ee830a72401b79

Authored by Simon Glass
1 parent 644ec0a933

dm: Add a function to create a 'live' device tree

This function converts the flat device tree into a hierarchical one with
C structures and pointers. This is easier to access.

Signed-off-by: Simon Glass <sjg@chromium.org>

Showing 3 changed files with 358 additions and 0 deletions Side-by-side Diff

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