fdt_fixup.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. /*
  2. * Copyright (c) 2016-2022, ARM Limited and Contributors. All rights reserved.
  3. *
  4. * SPDX-License-Identifier: BSD-3-Clause
  5. */
  6. /*
  7. * Contains generic routines to fix up the device tree blob passed on to
  8. * payloads like BL32 and BL33 (and further down the boot chain).
  9. * This allows to easily add PSCI nodes, when the original DT does not have
  10. * it or advertises another method.
  11. * Also it supports to add reserved memory nodes to describe memory that
  12. * is used by the secure world, so that non-secure software avoids using
  13. * that.
  14. */
  15. #include <errno.h>
  16. #include <stdio.h>
  17. #include <string.h>
  18. #include <libfdt.h>
  19. #include <arch.h>
  20. #include <common/debug.h>
  21. #include <common/fdt_fixup.h>
  22. #include <common/fdt_wrappers.h>
  23. #include <drivers/console.h>
  24. #include <lib/psci/psci.h>
  25. #include <plat/common/platform.h>
  26. static int append_psci_compatible(void *fdt, int offs, const char *str)
  27. {
  28. return fdt_appendprop(fdt, offs, "compatible", str, strlen(str) + 1);
  29. }
  30. /*
  31. * Those defines are for PSCI v0.1 legacy clients, which we expect to use
  32. * the same execution state (AArch32/AArch64) as TF-A.
  33. * Kernels running in AArch32 on an AArch64 TF-A should use PSCI v0.2.
  34. */
  35. #ifdef __aarch64__
  36. #define PSCI_CPU_SUSPEND_FNID PSCI_CPU_SUSPEND_AARCH64
  37. #define PSCI_CPU_ON_FNID PSCI_CPU_ON_AARCH64
  38. #else
  39. #define PSCI_CPU_SUSPEND_FNID PSCI_CPU_SUSPEND_AARCH32
  40. #define PSCI_CPU_ON_FNID PSCI_CPU_ON_AARCH32
  41. #endif
  42. /*******************************************************************************
  43. * dt_add_psci_node() - Add a PSCI node into an existing device tree
  44. * @fdt: pointer to the device tree blob in memory
  45. *
  46. * Add a device tree node describing PSCI into the root level of an existing
  47. * device tree blob in memory.
  48. * This will add v0.1, v0.2 and v1.0 compatible strings and the standard
  49. * function IDs for v0.1 compatibility.
  50. * An existing PSCI node will not be touched, the function will return success
  51. * in this case. This function will not touch the /cpus enable methods, use
  52. * dt_add_psci_cpu_enable_methods() for that.
  53. *
  54. * Return: 0 on success, -1 otherwise.
  55. ******************************************************************************/
  56. int dt_add_psci_node(void *fdt)
  57. {
  58. int offs;
  59. if (fdt_path_offset(fdt, "/psci") >= 0) {
  60. WARN("PSCI Device Tree node already exists!\n");
  61. return 0;
  62. }
  63. offs = fdt_path_offset(fdt, "/");
  64. if (offs < 0)
  65. return -1;
  66. offs = fdt_add_subnode(fdt, offs, "psci");
  67. if (offs < 0)
  68. return -1;
  69. if (append_psci_compatible(fdt, offs, "arm,psci-1.0"))
  70. return -1;
  71. if (append_psci_compatible(fdt, offs, "arm,psci-0.2"))
  72. return -1;
  73. if (append_psci_compatible(fdt, offs, "arm,psci"))
  74. return -1;
  75. if (fdt_setprop_string(fdt, offs, "method", "smc"))
  76. return -1;
  77. if (fdt_setprop_u32(fdt, offs, "cpu_suspend", PSCI_CPU_SUSPEND_FNID))
  78. return -1;
  79. if (fdt_setprop_u32(fdt, offs, "cpu_off", PSCI_CPU_OFF))
  80. return -1;
  81. if (fdt_setprop_u32(fdt, offs, "cpu_on", PSCI_CPU_ON_FNID))
  82. return -1;
  83. return 0;
  84. }
  85. /*
  86. * Find the first subnode that has a "device_type" property with the value
  87. * "cpu" and which's enable-method is not "psci" (yet).
  88. * Returns 0 if no such subnode is found, so all have already been patched
  89. * or none have to be patched in the first place.
  90. * Returns 1 if *one* such subnode has been found and successfully changed
  91. * to "psci".
  92. * Returns negative values on error.
  93. *
  94. * Call in a loop until it returns 0. Recalculate the node offset after
  95. * it has returned 1.
  96. */
  97. static int dt_update_one_cpu_node(void *fdt, int offset)
  98. {
  99. int offs;
  100. /* Iterate over all subnodes to find those with device_type = "cpu". */
  101. for (offs = fdt_first_subnode(fdt, offset); offs >= 0;
  102. offs = fdt_next_subnode(fdt, offs)) {
  103. const char *prop;
  104. int len;
  105. int ret;
  106. prop = fdt_getprop(fdt, offs, "device_type", &len);
  107. if (prop == NULL)
  108. continue;
  109. if ((strcmp(prop, "cpu") != 0) || (len != 4))
  110. continue;
  111. /* Ignore any nodes which already use "psci". */
  112. prop = fdt_getprop(fdt, offs, "enable-method", &len);
  113. if ((prop != NULL) &&
  114. (strcmp(prop, "psci") == 0) && (len == 5))
  115. continue;
  116. ret = fdt_setprop_string(fdt, offs, "enable-method", "psci");
  117. if (ret < 0)
  118. return ret;
  119. /*
  120. * Subnode found and patched.
  121. * Restart to accommodate potentially changed offsets.
  122. */
  123. return 1;
  124. }
  125. if (offs == -FDT_ERR_NOTFOUND)
  126. return 0;
  127. return offs;
  128. }
  129. /*******************************************************************************
  130. * dt_add_psci_cpu_enable_methods() - switch CPU nodes in DT to use PSCI
  131. * @fdt: pointer to the device tree blob in memory
  132. *
  133. * Iterate over all CPU device tree nodes (/cpus/cpu@x) in memory to change
  134. * the enable-method to PSCI. This will add the enable-method properties, if
  135. * required, or will change existing properties to read "psci".
  136. *
  137. * Return: 0 on success, or a negative error value otherwise.
  138. ******************************************************************************/
  139. int dt_add_psci_cpu_enable_methods(void *fdt)
  140. {
  141. int offs, ret;
  142. do {
  143. offs = fdt_path_offset(fdt, "/cpus");
  144. if (offs < 0)
  145. return offs;
  146. ret = dt_update_one_cpu_node(fdt, offs);
  147. } while (ret > 0);
  148. return ret;
  149. }
  150. #define HIGH_BITS(x) ((sizeof(x) > 4) ? ((x) >> 32) : (typeof(x))0)
  151. /*******************************************************************************
  152. * fdt_add_reserved_memory() - reserve (secure) memory regions in DT
  153. * @dtb: pointer to the device tree blob in memory
  154. * @node_name: name of the subnode to be used
  155. * @base: physical base address of the reserved region
  156. * @size: size of the reserved region
  157. *
  158. * Add a region of memory to the /reserved-memory node in a device tree in
  159. * memory, creating that node if required. Each region goes into a subnode
  160. * of that node and has a @node_name, a @base address and a @size.
  161. * This will prevent any device tree consumer from using that memory. It
  162. * can be used to announce secure memory regions, as it adds the "no-map"
  163. * property to prevent mapping and speculative operations on that region.
  164. *
  165. * See reserved-memory/reserved-memory.txt in the (Linux kernel) DT binding
  166. * documentation for details.
  167. * According to this binding, the address-cells and size-cells must match
  168. * those of the root node.
  169. *
  170. * Return: 0 on success, a negative error value otherwise.
  171. ******************************************************************************/
  172. int fdt_add_reserved_memory(void *dtb, const char *node_name,
  173. uintptr_t base, size_t size)
  174. {
  175. int offs = fdt_path_offset(dtb, "/reserved-memory");
  176. uint32_t addresses[4];
  177. int ac, sc;
  178. unsigned int idx = 0;
  179. ac = fdt_address_cells(dtb, 0);
  180. sc = fdt_size_cells(dtb, 0);
  181. if (offs < 0) { /* create if not existing yet */
  182. offs = fdt_add_subnode(dtb, 0, "reserved-memory");
  183. if (offs < 0) {
  184. return offs;
  185. }
  186. fdt_setprop_u32(dtb, offs, "#address-cells", ac);
  187. fdt_setprop_u32(dtb, offs, "#size-cells", sc);
  188. fdt_setprop(dtb, offs, "ranges", NULL, 0);
  189. }
  190. if (ac > 1) {
  191. addresses[idx] = cpu_to_fdt32(HIGH_BITS(base));
  192. idx++;
  193. }
  194. addresses[idx] = cpu_to_fdt32(base & 0xffffffff);
  195. idx++;
  196. if (sc > 1) {
  197. addresses[idx] = cpu_to_fdt32(HIGH_BITS(size));
  198. idx++;
  199. }
  200. addresses[idx] = cpu_to_fdt32(size & 0xffffffff);
  201. idx++;
  202. offs = fdt_add_subnode(dtb, offs, node_name);
  203. fdt_setprop(dtb, offs, "no-map", NULL, 0);
  204. fdt_setprop(dtb, offs, "reg", addresses, idx * sizeof(uint32_t));
  205. return 0;
  206. }
  207. /*******************************************************************************
  208. * fdt_add_cpu() Add a new CPU node to the DT
  209. * @dtb: Pointer to the device tree blob in memory
  210. * @parent: Offset of the parent node
  211. * @mpidr: MPIDR for the current CPU
  212. *
  213. * Create and add a new cpu node to a DTB.
  214. *
  215. * Return the offset of the new node or a negative value in case of error
  216. ******************************************************************************/
  217. static int fdt_add_cpu(void *dtb, int parent, u_register_t mpidr)
  218. {
  219. int cpu_offs;
  220. int err;
  221. char snode_name[15];
  222. uint64_t reg_prop;
  223. reg_prop = mpidr & MPID_MASK & ~MPIDR_MT_MASK;
  224. snprintf(snode_name, sizeof(snode_name), "cpu@%x",
  225. (unsigned int)reg_prop);
  226. cpu_offs = fdt_add_subnode(dtb, parent, snode_name);
  227. if (cpu_offs < 0) {
  228. ERROR ("FDT: add subnode \"%s\" failed: %i\n",
  229. snode_name, cpu_offs);
  230. return cpu_offs;
  231. }
  232. err = fdt_setprop_string(dtb, cpu_offs, "compatible", "arm,armv8");
  233. if (err < 0) {
  234. ERROR ("FDT: write to \"%s\" property of node at offset %i failed\n",
  235. "compatible", cpu_offs);
  236. return err;
  237. }
  238. err = fdt_setprop_u64(dtb, cpu_offs, "reg", reg_prop);
  239. if (err < 0) {
  240. ERROR ("FDT: write to \"%s\" property of node at offset %i failed\n",
  241. "reg", cpu_offs);
  242. return err;
  243. }
  244. err = fdt_setprop_string(dtb, cpu_offs, "device_type", "cpu");
  245. if (err < 0) {
  246. ERROR ("FDT: write to \"%s\" property of node at offset %i failed\n",
  247. "device_type", cpu_offs);
  248. return err;
  249. }
  250. err = fdt_setprop_string(dtb, cpu_offs, "enable-method", "psci");
  251. if (err < 0) {
  252. ERROR ("FDT: write to \"%s\" property of node at offset %i failed\n",
  253. "enable-method", cpu_offs);
  254. return err;
  255. }
  256. return cpu_offs;
  257. }
  258. /******************************************************************************
  259. * fdt_add_cpus_node() - Add the cpus node to the DTB
  260. * @dtb: pointer to the device tree blob in memory
  261. * @afflv0: Maximum number of threads per core (affinity level 0).
  262. * @afflv1: Maximum number of CPUs per cluster (affinity level 1).
  263. * @afflv2: Maximum number of clusters (affinity level 2).
  264. *
  265. * Iterate over all the possible MPIDs given the maximum affinity levels and
  266. * add a cpus node to the DTB with all the valid CPUs on the system.
  267. * If there is already a /cpus node, exit gracefully
  268. *
  269. * A system with two CPUs would generate a node equivalent or similar to:
  270. *
  271. * cpus {
  272. * #address-cells = <2>;
  273. * #size-cells = <0>;
  274. *
  275. * cpu0: cpu@0 {
  276. * compatible = "arm,armv8";
  277. * reg = <0x0 0x0>;
  278. * device_type = "cpu";
  279. * enable-method = "psci";
  280. * };
  281. * cpu1: cpu@10000 {
  282. * compatible = "arm,armv8";
  283. * reg = <0x0 0x100>;
  284. * device_type = "cpu";
  285. * enable-method = "psci";
  286. * };
  287. * };
  288. *
  289. * Full documentation about the CPU bindings can be found at:
  290. * https://www.kernel.org/doc/Documentation/devicetree/bindings/arm/cpus.txt
  291. *
  292. * Return the offset of the node or a negative value on error.
  293. ******************************************************************************/
  294. int fdt_add_cpus_node(void *dtb, unsigned int afflv0,
  295. unsigned int afflv1, unsigned int afflv2)
  296. {
  297. int offs;
  298. int err;
  299. unsigned int i, j, k;
  300. u_register_t mpidr;
  301. int cpuid;
  302. if (fdt_path_offset(dtb, "/cpus") >= 0) {
  303. return -EEXIST;
  304. }
  305. offs = fdt_add_subnode(dtb, 0, "cpus");
  306. if (offs < 0) {
  307. ERROR ("FDT: add subnode \"cpus\" node to parent node failed");
  308. return offs;
  309. }
  310. err = fdt_setprop_u32(dtb, offs, "#address-cells", 2);
  311. if (err < 0) {
  312. ERROR ("FDT: write to \"%s\" property of node at offset %i failed\n",
  313. "#address-cells", offs);
  314. return err;
  315. }
  316. err = fdt_setprop_u32(dtb, offs, "#size-cells", 0);
  317. if (err < 0) {
  318. ERROR ("FDT: write to \"%s\" property of node at offset %i failed\n",
  319. "#size-cells", offs);
  320. return err;
  321. }
  322. /*
  323. * Populate the node with the CPUs.
  324. * As libfdt prepends subnodes within a node, reverse the index count
  325. * so the CPU nodes would be better ordered.
  326. */
  327. for (i = afflv2; i > 0U; i--) {
  328. for (j = afflv1; j > 0U; j--) {
  329. for (k = afflv0; k > 0U; k--) {
  330. mpidr = ((i - 1) << MPIDR_AFF2_SHIFT) |
  331. ((j - 1) << MPIDR_AFF1_SHIFT) |
  332. ((k - 1) << MPIDR_AFF0_SHIFT) |
  333. (read_mpidr_el1() & MPIDR_MT_MASK);
  334. cpuid = plat_core_pos_by_mpidr(mpidr);
  335. if (cpuid >= 0) {
  336. /* Valid MPID found */
  337. err = fdt_add_cpu(dtb, offs, mpidr);
  338. if (err < 0) {
  339. ERROR ("FDT: %s 0x%08x\n",
  340. "error adding CPU",
  341. (uint32_t)mpidr);
  342. return err;
  343. }
  344. }
  345. }
  346. }
  347. }
  348. return offs;
  349. }
  350. /*******************************************************************************
  351. * fdt_add_cpu_idle_states() - add PSCI CPU idle states to cpu nodes in the DT
  352. * @dtb: pointer to the device tree blob in memory
  353. * @states: array of idle state descriptions, ending with empty element
  354. *
  355. * Add information about CPU idle states to the devicetree. This function
  356. * assumes that CPU idle states are not already present in the devicetree, and
  357. * that all CPU states are equally applicable to all CPUs.
  358. *
  359. * See arm/idle-states.yaml and arm/psci.yaml in the (Linux kernel) DT binding
  360. * documentation for more details.
  361. *
  362. * Return: 0 on success, a negative error value otherwise.
  363. ******************************************************************************/
  364. int fdt_add_cpu_idle_states(void *dtb, const struct psci_cpu_idle_state *state)
  365. {
  366. int cpu_node, cpus_node, idle_states_node, ret;
  367. uint32_t count, phandle;
  368. ret = fdt_find_max_phandle(dtb, &phandle);
  369. phandle++;
  370. if (ret < 0) {
  371. return ret;
  372. }
  373. cpus_node = fdt_path_offset(dtb, "/cpus");
  374. if (cpus_node < 0) {
  375. return cpus_node;
  376. }
  377. /* Create the idle-states node and its child nodes. */
  378. idle_states_node = fdt_add_subnode(dtb, cpus_node, "idle-states");
  379. if (idle_states_node < 0) {
  380. return idle_states_node;
  381. }
  382. ret = fdt_setprop_string(dtb, idle_states_node, "entry-method", "psci");
  383. if (ret < 0) {
  384. return ret;
  385. }
  386. for (count = 0U; state->name != NULL; count++, phandle++, state++) {
  387. int idle_state_node;
  388. idle_state_node = fdt_add_subnode(dtb, idle_states_node,
  389. state->name);
  390. if (idle_state_node < 0) {
  391. return idle_state_node;
  392. }
  393. fdt_setprop_string(dtb, idle_state_node, "compatible",
  394. "arm,idle-state");
  395. fdt_setprop_u32(dtb, idle_state_node, "arm,psci-suspend-param",
  396. state->power_state);
  397. if (state->local_timer_stop) {
  398. fdt_setprop_empty(dtb, idle_state_node,
  399. "local-timer-stop");
  400. }
  401. fdt_setprop_u32(dtb, idle_state_node, "entry-latency-us",
  402. state->entry_latency_us);
  403. fdt_setprop_u32(dtb, idle_state_node, "exit-latency-us",
  404. state->exit_latency_us);
  405. fdt_setprop_u32(dtb, idle_state_node, "min-residency-us",
  406. state->min_residency_us);
  407. if (state->wakeup_latency_us) {
  408. fdt_setprop_u32(dtb, idle_state_node,
  409. "wakeup-latency-us",
  410. state->wakeup_latency_us);
  411. }
  412. fdt_setprop_u32(dtb, idle_state_node, "phandle", phandle);
  413. }
  414. if (count == 0U) {
  415. return 0;
  416. }
  417. /* Link each cpu node to the idle state nodes. */
  418. fdt_for_each_subnode(cpu_node, dtb, cpus_node) {
  419. const char *device_type;
  420. fdt32_t *value;
  421. /* Only process child nodes with device_type = "cpu". */
  422. device_type = fdt_getprop(dtb, cpu_node, "device_type", NULL);
  423. if (device_type == NULL || strcmp(device_type, "cpu") != 0) {
  424. continue;
  425. }
  426. /* Allocate space for the list of phandles. */
  427. ret = fdt_setprop_placeholder(dtb, cpu_node, "cpu-idle-states",
  428. count * sizeof(phandle),
  429. (void **)&value);
  430. if (ret < 0) {
  431. return ret;
  432. }
  433. /* Fill in the phandles of the idle state nodes. */
  434. for (uint32_t i = 0U; i < count; ++i) {
  435. value[i] = cpu_to_fdt32(phandle - count + i);
  436. }
  437. }
  438. return 0;
  439. }
  440. /**
  441. * fdt_adjust_gic_redist() - Adjust GICv3 redistributor size
  442. * @dtb: Pointer to the DT blob in memory
  443. * @nr_cores: Number of CPU cores on this system.
  444. * @gicr_base: Base address of the first GICR frame, or ~0 if unchanged
  445. * @gicr_frame_size: Size of the GICR frame per core
  446. *
  447. * On a GICv3 compatible interrupt controller, the redistributor provides
  448. * a number of 64k pages per each supported core. So with a dynamic topology,
  449. * this size cannot be known upfront and thus can't be hardcoded into the DTB.
  450. *
  451. * Find the DT node describing the GICv3 interrupt controller, and adjust
  452. * the size of the redistributor to match the number of actual cores on
  453. * this system.
  454. * A GICv4 compatible redistributor uses four 64K pages per core, whereas GICs
  455. * without support for direct injection of virtual interrupts use two 64K pages.
  456. * The @gicr_frame_size parameter should be 262144 and 131072, respectively.
  457. * Also optionally allow adjusting the GICR frame base address, when this is
  458. * different due to ITS frames between distributor and redistributor.
  459. *
  460. * Return: 0 on success, negative error value otherwise.
  461. */
  462. int fdt_adjust_gic_redist(void *dtb, unsigned int nr_cores,
  463. uintptr_t gicr_base, unsigned int gicr_frame_size)
  464. {
  465. int offset = fdt_node_offset_by_compatible(dtb, 0, "arm,gic-v3");
  466. uint64_t reg_64;
  467. uint32_t reg_32;
  468. void *val;
  469. int parent, ret;
  470. int ac, sc;
  471. if (offset < 0) {
  472. return offset;
  473. }
  474. parent = fdt_parent_offset(dtb, offset);
  475. if (parent < 0) {
  476. return parent;
  477. }
  478. ac = fdt_address_cells(dtb, parent);
  479. sc = fdt_size_cells(dtb, parent);
  480. if (ac < 0 || sc < 0) {
  481. return -EINVAL;
  482. }
  483. if (gicr_base != INVALID_BASE_ADDR) {
  484. if (ac == 1) {
  485. reg_32 = cpu_to_fdt32(gicr_base);
  486. val = &reg_32;
  487. } else {
  488. reg_64 = cpu_to_fdt64(gicr_base);
  489. val = &reg_64;
  490. }
  491. /*
  492. * The redistributor base address is the second address in
  493. * the "reg" entry, so we have to skip one address and one
  494. * size cell.
  495. */
  496. ret = fdt_setprop_inplace_namelen_partial(dtb, offset,
  497. "reg", 3,
  498. (ac + sc) * 4,
  499. val, ac * 4);
  500. if (ret < 0) {
  501. return ret;
  502. }
  503. }
  504. if (sc == 1) {
  505. reg_32 = cpu_to_fdt32(nr_cores * gicr_frame_size);
  506. val = &reg_32;
  507. } else {
  508. reg_64 = cpu_to_fdt64(nr_cores * (uint64_t)gicr_frame_size);
  509. val = &reg_64;
  510. }
  511. /*
  512. * The redistributor is described in the second "reg" entry.
  513. * So we have to skip one address and one size cell, then another
  514. * address cell to get to the second size cell.
  515. */
  516. return fdt_setprop_inplace_namelen_partial(dtb, offset, "reg", 3,
  517. (ac + sc + ac) * 4,
  518. val, sc * 4);
  519. }
  520. /**
  521. * fdt_set_mac_address () - store MAC address in device tree
  522. * @dtb: pointer to the device tree blob in memory
  523. * @eth_idx: number of Ethernet interface in /aliases node
  524. * @mac_addr: pointer to 6 byte MAC address to store
  525. *
  526. * Use the generic local-mac-address property in a network device DT node
  527. * to define the MAC address this device should be using. Many platform
  528. * network devices lack device-specific non-volatile storage to hold this
  529. * address, and leave it up to firmware to find and store a unique MAC
  530. * address in the DT.
  531. * The MAC address could be read from some board or firmware defined storage,
  532. * or could be derived from some other unique property like a serial number.
  533. *
  534. * Return: 0 on success, a negative libfdt error value otherwise.
  535. */
  536. int fdt_set_mac_address(void *dtb, unsigned int ethernet_idx,
  537. const uint8_t *mac_addr)
  538. {
  539. char eth_alias[12];
  540. const char *path;
  541. int node;
  542. if (ethernet_idx > 9U) {
  543. return -FDT_ERR_BADVALUE;
  544. }
  545. snprintf(eth_alias, sizeof(eth_alias), "ethernet%d", ethernet_idx);
  546. path = fdt_get_alias(dtb, eth_alias);
  547. if (path == NULL) {
  548. return -FDT_ERR_NOTFOUND;
  549. }
  550. node = fdt_path_offset(dtb, path);
  551. if (node < 0) {
  552. ERROR("Path \"%s\" not found in DT: %d\n", path, node);
  553. return node;
  554. }
  555. return fdt_setprop(dtb, node, "local-mac-address", mac_addr, 6);
  556. }