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 } 334