1 #include "libfdt_env.h" 2 3 #include <fdt.h> 4 #include <libfdt.h> 5 6 #include "libfdt_internal.h" 7 8 static uint32_t overlay_get_target_phandle(const void *fdto, int fragment) 9 { 10 const uint32_t *val; 11 int len; 12 13 val = fdt_getprop(fdto, fragment, "target", &len); 14 if (!val) 15 return 0; 16 17 if ((*val == 0xffffffff) || (len != sizeof(*val))) 18 return 0xffffffff; 19 20 return fdt32_to_cpu(*val); 21 } 22 23 static int overlay_get_target(const void *fdt, const void *fdto, 24 int fragment) 25 { 26 uint32_t phandle; 27 const char *path; 28 29 /* Try first to do a phandle based lookup */ 30 phandle = overlay_get_target_phandle(fdto, fragment); 31 if (phandle == -1) 32 return -FDT_ERR_BADPHANDLE; 33 34 if (phandle) 35 return fdt_node_offset_by_phandle(fdt, phandle); 36 37 /* And then a path based lookup */ 38 path = fdt_getprop(fdto, fragment, "target-path", NULL); 39 if (!path) 40 return -FDT_ERR_NOTFOUND; 41 42 return fdt_path_offset(fdt, path); 43 } 44 45 static int overlay_phandle_add_offset(void *fdt, int node, 46 const char *name, uint32_t delta) 47 { 48 const uint32_t *val; 49 uint32_t adj_val; 50 int len; 51 52 val = fdt_getprop(fdt, node, name, &len); 53 if (!val) 54 return len; 55 56 if (len != sizeof(*val)) 57 return -FDT_ERR_BADSTRUCTURE; 58 59 adj_val = fdt32_to_cpu(*val); 60 if ((adj_val + delta) < adj_val) 61 return -FDT_ERR_BADPHANDLE; 62 63 adj_val += delta; 64 return fdt_setprop_inplace_u32(fdt, node, name, adj_val); 65 } 66 67 static int overlay_adjust_node_phandles(void *fdto, int node, 68 uint32_t delta) 69 { 70 bool found = false; 71 int child; 72 int ret; 73 74 ret = overlay_phandle_add_offset(fdto, node, "phandle", delta); 75 if (ret && ret != -FDT_ERR_NOTFOUND) 76 return ret; 77 78 if (!ret) 79 found = true; 80 81 ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta); 82 if (ret && ret != -FDT_ERR_NOTFOUND) 83 return ret; 84 85 /* 86 * If neither phandle nor linux,phandle have been found return 87 * an error. 88 */ 89 if (!found && !ret) 90 return ret; 91 92 fdt_for_each_subnode(fdto, child, node) 93 overlay_adjust_node_phandles(fdto, child, delta); 94 95 return 0; 96 } 97 98 static int overlay_adjust_local_phandles(void *fdto, uint32_t delta) 99 { 100 /* 101 * Start adjusting the phandles from the overlay root 102 */ 103 return overlay_adjust_node_phandles(fdto, 0, delta); 104 } 105 106 static int overlay_update_local_node_references(void *fdto, 107 int tree_node, 108 int fixup_node, 109 uint32_t delta) 110 { 111 int fixup_prop; 112 int fixup_child; 113 int ret; 114 115 fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) { 116 const uint32_t *val = NULL; 117 uint32_t adj_val, index; 118 const char *name; 119 int fixup_len; 120 int tree_len; 121 122 val = fdt_getprop_by_offset(fdto, fixup_prop, 123 &name, &fixup_len); 124 if (!val) 125 return fixup_len; 126 index = fdt32_to_cpu(*val); 127 128 val = fdt_getprop(fdto, tree_node, name, &tree_len); 129 if (!val) 130 return tree_len; 131 132 /* 133 * The index can be unaligned. 134 * 135 * Use a memcpy for the architectures that do not 136 * support unaligned accesses. 137 */ 138 memcpy(&adj_val, (unsigned char *)val + index, 139 sizeof(uint32_t)); 140 141 adj_val = fdt32_to_cpu(adj_val); 142 adj_val += delta; 143 adj_val = cpu_to_fdt32(adj_val); 144 145 ret = fdt_setprop_inplace_namelen_partial(fdto, tree_node, 146 name, strlen(name), 147 index, &adj_val, 148 sizeof(adj_val)); 149 if (ret) 150 return ret; 151 } 152 153 fdt_for_each_subnode(fdto, fixup_child, fixup_node) { 154 const char *fixup_child_name = fdt_get_name(fdto, fixup_child, 155 NULL); 156 int tree_child; 157 158 tree_child = fdt_subnode_offset(fdto, tree_node, 159 fixup_child_name); 160 if (tree_child < 0) 161 return tree_child; 162 163 ret = overlay_update_local_node_references(fdto, 164 tree_child, 165 fixup_child, 166 delta); 167 if (ret) 168 return ret; 169 } 170 171 return 0; 172 } 173 174 static int overlay_update_local_references(void *dto, uint32_t delta) 175 { 176 int fixups; 177 178 fixups = fdt_path_offset(dto, "/__local_fixups__"); 179 if (fixups < 0) { 180 /* There's no local phandles to adjust, bail out */ 181 if (fixups == -FDT_ERR_NOTFOUND) 182 return 0; 183 184 return fixups; 185 } 186 187 /* 188 * Update our local references from the root of the tree 189 */ 190 return overlay_update_local_node_references(dto, 0, fixups, 191 delta); 192 } 193 194 static int overlay_fixup_one_phandle(void *fdt, void *fdto, 195 int symbols_off, 196 const char *path, uint32_t path_len, 197 const char *name, uint32_t name_len, 198 int index, const char *label) 199 { 200 const char *symbol_path; 201 uint32_t phandle; 202 int symbol_off, fixup_off; 203 int prop_len; 204 205 symbol_path = fdt_getprop(fdt, symbols_off, label, 206 &prop_len); 207 if (!symbol_path) 208 return -FDT_ERR_NOTFOUND; 209 210 symbol_off = fdt_path_offset(fdt, symbol_path); 211 if (symbol_off < 0) 212 return symbol_off; 213 214 phandle = fdt_get_phandle(fdt, symbol_off); 215 if (!phandle) 216 return -FDT_ERR_NOTFOUND; 217 218 fixup_off = fdt_path_offset_namelen(fdto, path, path_len); 219 if (fixup_off < 0) 220 return fixup_off; 221 222 phandle = cpu_to_fdt32(phandle); 223 return fdt_setprop_inplace_namelen_partial(fdto, fixup_off, 224 name, name_len, index, 225 &phandle, sizeof(phandle)); 226 }; 227 228 static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off, 229 int property) 230 { 231 const char *value; 232 const char *label; 233 int len; 234 235 value = fdt_getprop_by_offset(fdto, property, 236 &label, &len); 237 if (!value) 238 return len; 239 240 do { 241 const char *prop_string = value; 242 const char *path, *name; 243 uint32_t prop_len = strlen(value); 244 uint32_t path_len, name_len; 245 char *sep, *endptr; 246 int index; 247 int ret; 248 249 path = prop_string; 250 sep = memchr(prop_string, ':', prop_len); 251 if (*sep != ':') 252 return -FDT_ERR_BADSTRUCTURE; 253 path_len = sep - path; 254 255 name = sep + 1; 256 sep = memchr(name, ':', prop_len); 257 if (*sep != ':') 258 return -FDT_ERR_BADSTRUCTURE; 259 name_len = sep - name; 260 261 index = strtoul(sep + 1, &endptr, 10); 262 if ((*endptr != '\0') || (endptr <= (sep + 1))) 263 return -FDT_ERR_BADSTRUCTURE; 264 265 len -= prop_len + 1; 266 value += prop_len + 1; 267 268 ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off, 269 path, path_len, name, name_len, 270 index, label); 271 if (ret) 272 return ret; 273 } while (len > 0); 274 275 return 0; 276 } 277 278 static int overlay_fixup_phandles(void *dt, void *dto) 279 { 280 int fixups_off, symbols_off; 281 int property; 282 283 symbols_off = fdt_path_offset(dt, "/__symbols__"); 284 fixups_off = fdt_path_offset(dto, "/__fixups__"); 285 286 fdt_for_each_property_offset(property, dto, fixups_off) 287 overlay_fixup_phandle(dt, dto, symbols_off, property); 288 289 return 0; 290 } 291 292 static int apply_overlay_node(void *dt, int target, 293 void *dto, int fragment) 294 { 295 int property; 296 int node; 297 298 fdt_for_each_property_offset(property, dto, fragment) { 299 const char *name; 300 const void *prop; 301 int prop_len; 302 int ret; 303 304 prop = fdt_getprop_by_offset(dto, property, &name, 305 &prop_len); 306 if (prop_len == -FDT_ERR_NOTFOUND) 307 return -FDT_ERR_INTERNAL; 308 if (prop_len < 0) 309 return prop_len; 310 311 ret = fdt_setprop(dt, target, name, prop, prop_len); 312 if (ret) 313 return ret; 314 } 315 316 fdt_for_each_subnode(dto, node, fragment) { 317 const char *name = fdt_get_name(dto, node, NULL); 318 int nnode; 319 int ret; 320 321 nnode = fdt_add_subnode(dt, target, name); 322 if (nnode == -FDT_ERR_EXISTS) 323 nnode = fdt_subnode_offset(dt, target, name); 324 325 if (nnode < 0) 326 return nnode; 327 328 ret = apply_overlay_node(dt, nnode, dto, node); 329 if (ret) 330 return ret; 331 } 332 333 return 0; 334 } 335 336 static int overlay_merge(void *dt, void *dto) 337 { 338 int fragment; 339 340 fdt_for_each_subnode(dto, fragment, 0) { 341 int overlay; 342 int target; 343 int ret; 344 345 target = overlay_get_target(dt, dto, fragment); 346 if (target < 0) 347 continue; 348 349 overlay = fdt_subnode_offset(dto, fragment, "__overlay__"); 350 if (overlay < 0) 351 return overlay; 352 353 ret = apply_overlay_node(dt, target, dto, overlay); 354 if (ret) 355 return ret; 356 } 357 358 return 0; 359 } 360 361 int fdt_overlay_apply(void *fdt, void *fdto) 362 { 363 uint32_t delta = fdt_get_max_phandle(fdt) + 1; 364 int ret; 365 366 FDT_CHECK_HEADER(fdt); 367 FDT_CHECK_HEADER(fdto); 368 369 ret = overlay_adjust_local_phandles(fdto, delta); 370 if (ret) 371 goto err; 372 373 ret = overlay_update_local_references(fdto, delta); 374 if (ret) 375 goto err; 376 377 ret = overlay_fixup_phandles(fdt, fdto); 378 if (ret) 379 goto err; 380 381 ret = overlay_merge(fdt, fdto); 382 if (ret) 383 goto err; 384 385 /* 386 * The overlay has been damaged, erase its magic. 387 */ 388 fdt_set_magic(fdto, ~0); 389 390 return 0; 391 392 err: 393 /* 394 * The overlay might have been damaged, erase its magic. 395 */ 396 fdt_set_magic(fdto, ~0); 397 398 /* 399 * The base device tree might have been damaged, erase its 400 * magic. 401 */ 402 fdt_set_magic(fdt, ~0); 403 404 return ret; 405 } 406