fdt_fixup.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  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. int node;
  177. uint32_t addresses[4];
  178. int ac, sc;
  179. unsigned int idx = 0;
  180. ac = fdt_address_cells(dtb, 0);
  181. sc = fdt_size_cells(dtb, 0);
  182. if (offs < 0) { /* create if not existing yet */
  183. offs = fdt_add_subnode(dtb, 0, "reserved-memory");
  184. if (offs < 0) {
  185. return offs;
  186. }
  187. fdt_setprop_u32(dtb, offs, "#address-cells", ac);
  188. fdt_setprop_u32(dtb, offs, "#size-cells", sc);
  189. fdt_setprop(dtb, offs, "ranges", NULL, 0);
  190. }
  191. /* Check for existing regions */
  192. fdt_for_each_subnode(node, dtb, offs) {
  193. uintptr_t c_base;
  194. size_t c_size;
  195. int ret;
  196. ret = fdt_get_reg_props_by_index(dtb, node, 0, &c_base, &c_size);
  197. /* Ignore illegal subnodes */
  198. if (ret != 0) {
  199. continue;
  200. }
  201. /* existing region entirely contains the new region */
  202. if (base >= c_base && (base + size) <= (c_base + c_size)) {
  203. return 0;
  204. }
  205. }
  206. if (ac > 1) {
  207. addresses[idx] = cpu_to_fdt32(HIGH_BITS(base));
  208. idx++;
  209. }
  210. addresses[idx] = cpu_to_fdt32(base & 0xffffffff);
  211. idx++;
  212. if (sc > 1) {
  213. addresses[idx] = cpu_to_fdt32(HIGH_BITS(size));
  214. idx++;
  215. }
  216. addresses[idx] = cpu_to_fdt32(size & 0xffffffff);
  217. idx++;
  218. offs = fdt_add_subnode(dtb, offs, node_name);
  219. fdt_setprop(dtb, offs, "no-map", NULL, 0);
  220. fdt_setprop(dtb, offs, "reg", addresses, idx * sizeof(uint32_t));
  221. return 0;
  222. }
  223. /*******************************************************************************
  224. * fdt_add_cpu() Add a new CPU node to the DT
  225. * @dtb: Pointer to the device tree blob in memory
  226. * @parent: Offset of the parent node
  227. * @mpidr: MPIDR for the current CPU
  228. *
  229. * Create and add a new cpu node to a DTB.
  230. *
  231. * Return the offset of the new node or a negative value in case of error
  232. ******************************************************************************/
  233. static int fdt_add_cpu(void *dtb, int parent, u_register_t mpidr)
  234. {
  235. int cpu_offs;
  236. int err;
  237. char snode_name[15];
  238. uint64_t reg_prop;
  239. reg_prop = mpidr & MPID_MASK & ~MPIDR_MT_MASK;
  240. snprintf(snode_name, sizeof(snode_name), "cpu@%x",
  241. (unsigned int)reg_prop);
  242. cpu_offs = fdt_add_subnode(dtb, parent, snode_name);
  243. if (cpu_offs < 0) {
  244. ERROR ("FDT: add subnode \"%s\" failed: %i\n",
  245. snode_name, cpu_offs);
  246. return cpu_offs;
  247. }
  248. err = fdt_setprop_string(dtb, cpu_offs, "compatible", "arm,armv8");
  249. if (err < 0) {
  250. ERROR ("FDT: write to \"%s\" property of node at offset %i failed\n",
  251. "compatible", cpu_offs);
  252. return err;
  253. }
  254. err = fdt_setprop_u64(dtb, cpu_offs, "reg", reg_prop);
  255. if (err < 0) {
  256. ERROR ("FDT: write to \"%s\" property of node at offset %i failed\n",
  257. "reg", cpu_offs);
  258. return err;
  259. }
  260. err = fdt_setprop_string(dtb, cpu_offs, "device_type", "cpu");
  261. if (err < 0) {
  262. ERROR ("FDT: write to \"%s\" property of node at offset %i failed\n",
  263. "device_type", cpu_offs);
  264. return err;
  265. }
  266. err = fdt_setprop_string(dtb, cpu_offs, "enable-method", "psci");
  267. if (err < 0) {
  268. ERROR ("FDT: write to \"%s\" property of node at offset %i failed\n",
  269. "enable-method", cpu_offs);
  270. return err;
  271. }
  272. return cpu_offs;
  273. }
  274. /******************************************************************************
  275. * fdt_add_cpus_node() - Add the cpus node to the DTB
  276. * @dtb: pointer to the device tree blob in memory
  277. * @afflv0: Maximum number of threads per core (affinity level 0).
  278. * @afflv1: Maximum number of CPUs per cluster (affinity level 1).
  279. * @afflv2: Maximum number of clusters (affinity level 2).
  280. *
  281. * Iterate over all the possible MPIDs given the maximum affinity levels and
  282. * add a cpus node to the DTB with all the valid CPUs on the system.
  283. * If there is already a /cpus node, exit gracefully
  284. *
  285. * A system with two CPUs would generate a node equivalent or similar to:
  286. *
  287. * cpus {
  288. * #address-cells = <2>;
  289. * #size-cells = <0>;
  290. *
  291. * cpu0: cpu@0 {
  292. * compatible = "arm,armv8";
  293. * reg = <0x0 0x0>;
  294. * device_type = "cpu";
  295. * enable-method = "psci";
  296. * };
  297. * cpu1: cpu@10000 {
  298. * compatible = "arm,armv8";
  299. * reg = <0x0 0x100>;
  300. * device_type = "cpu";
  301. * enable-method = "psci";
  302. * };
  303. * };
  304. *
  305. * Full documentation about the CPU bindings can be found at:
  306. * https://www.kernel.org/doc/Documentation/devicetree/bindings/arm/cpus.txt
  307. *
  308. * Return the offset of the node or a negative value on error.
  309. ******************************************************************************/
  310. int fdt_add_cpus_node(void *dtb, unsigned int afflv0,
  311. unsigned int afflv1, unsigned int afflv2)
  312. {
  313. int offs;
  314. int err;
  315. unsigned int i, j, k;
  316. u_register_t mpidr;
  317. int cpuid;
  318. if (fdt_path_offset(dtb, "/cpus") >= 0) {
  319. return -EEXIST;
  320. }
  321. offs = fdt_add_subnode(dtb, 0, "cpus");
  322. if (offs < 0) {
  323. ERROR ("FDT: add subnode \"cpus\" node to parent node failed");
  324. return offs;
  325. }
  326. err = fdt_setprop_u32(dtb, offs, "#address-cells", 2);
  327. if (err < 0) {
  328. ERROR ("FDT: write to \"%s\" property of node at offset %i failed\n",
  329. "#address-cells", offs);
  330. return err;
  331. }
  332. err = fdt_setprop_u32(dtb, offs, "#size-cells", 0);
  333. if (err < 0) {
  334. ERROR ("FDT: write to \"%s\" property of node at offset %i failed\n",
  335. "#size-cells", offs);
  336. return err;
  337. }
  338. /*
  339. * Populate the node with the CPUs.
  340. * As libfdt prepends subnodes within a node, reverse the index count
  341. * so the CPU nodes would be better ordered.
  342. */
  343. for (i = afflv2; i > 0U; i--) {
  344. for (j = afflv1; j > 0U; j--) {
  345. for (k = afflv0; k > 0U; k--) {
  346. mpidr = ((i - 1) << MPIDR_AFF2_SHIFT) |
  347. ((j - 1) << MPIDR_AFF1_SHIFT) |
  348. ((k - 1) << MPIDR_AFF0_SHIFT) |
  349. (read_mpidr_el1() & MPIDR_MT_MASK);
  350. cpuid = plat_core_pos_by_mpidr(mpidr);
  351. if (cpuid >= 0) {
  352. /* Valid MPID found */
  353. err = fdt_add_cpu(dtb, offs, mpidr);
  354. if (err < 0) {
  355. ERROR ("FDT: %s 0x%08x\n",
  356. "error adding CPU",
  357. (uint32_t)mpidr);
  358. return err;
  359. }
  360. }
  361. }
  362. }
  363. }
  364. return offs;
  365. }
  366. /*******************************************************************************
  367. * fdt_add_cpu_idle_states() - add PSCI CPU idle states to cpu nodes in the DT
  368. * @dtb: pointer to the device tree blob in memory
  369. * @states: array of idle state descriptions, ending with empty element
  370. *
  371. * Add information about CPU idle states to the devicetree. This function
  372. * assumes that CPU idle states are not already present in the devicetree, and
  373. * that all CPU states are equally applicable to all CPUs.
  374. *
  375. * See arm/idle-states.yaml and arm/psci.yaml in the (Linux kernel) DT binding
  376. * documentation for more details.
  377. *
  378. * Return: 0 on success, a negative error value otherwise.
  379. ******************************************************************************/
  380. int fdt_add_cpu_idle_states(void *dtb, const struct psci_cpu_idle_state *state)
  381. {
  382. int cpu_node, cpus_node, idle_states_node, ret;
  383. uint32_t count, phandle;
  384. ret = fdt_find_max_phandle(dtb, &phandle);
  385. phandle++;
  386. if (ret < 0) {
  387. return ret;
  388. }
  389. cpus_node = fdt_path_offset(dtb, "/cpus");
  390. if (cpus_node < 0) {
  391. return cpus_node;
  392. }
  393. /* Create the idle-states node and its child nodes. */
  394. idle_states_node = fdt_add_subnode(dtb, cpus_node, "idle-states");
  395. if (idle_states_node < 0) {
  396. return idle_states_node;
  397. }
  398. ret = fdt_setprop_string(dtb, idle_states_node, "entry-method", "psci");
  399. if (ret < 0) {
  400. return ret;
  401. }
  402. for (count = 0U; state->name != NULL; count++, phandle++, state++) {
  403. int idle_state_node;
  404. idle_state_node = fdt_add_subnode(dtb, idle_states_node,
  405. state->name);
  406. if (idle_state_node < 0) {
  407. return idle_state_node;
  408. }
  409. fdt_setprop_string(dtb, idle_state_node, "compatible",
  410. "arm,idle-state");
  411. fdt_setprop_u32(dtb, idle_state_node, "arm,psci-suspend-param",
  412. state->power_state);
  413. if (state->local_timer_stop) {
  414. fdt_setprop_empty(dtb, idle_state_node,
  415. "local-timer-stop");
  416. }
  417. fdt_setprop_u32(dtb, idle_state_node, "entry-latency-us",
  418. state->entry_latency_us);
  419. fdt_setprop_u32(dtb, idle_state_node, "exit-latency-us",
  420. state->exit_latency_us);
  421. fdt_setprop_u32(dtb, idle_state_node, "min-residency-us",
  422. state->min_residency_us);
  423. if (state->wakeup_latency_us) {
  424. fdt_setprop_u32(dtb, idle_state_node,
  425. "wakeup-latency-us",
  426. state->wakeup_latency_us);
  427. }
  428. fdt_setprop_u32(dtb, idle_state_node, "phandle", phandle);
  429. }
  430. if (count == 0U) {
  431. return 0;
  432. }
  433. /* Link each cpu node to the idle state nodes. */
  434. fdt_for_each_subnode(cpu_node, dtb, cpus_node) {
  435. const char *device_type;
  436. fdt32_t *value;
  437. /* Only process child nodes with device_type = "cpu". */
  438. device_type = fdt_getprop(dtb, cpu_node, "device_type", NULL);
  439. if (device_type == NULL || strcmp(device_type, "cpu") != 0) {
  440. continue;
  441. }
  442. /* Allocate space for the list of phandles. */
  443. ret = fdt_setprop_placeholder(dtb, cpu_node, "cpu-idle-states",
  444. count * sizeof(phandle),
  445. (void **)&value);
  446. if (ret < 0) {
  447. return ret;
  448. }
  449. /* Fill in the phandles of the idle state nodes. */
  450. for (uint32_t i = 0U; i < count; ++i) {
  451. value[i] = cpu_to_fdt32(phandle - count + i);
  452. }
  453. }
  454. return 0;
  455. }
  456. /**
  457. * fdt_adjust_gic_redist() - Adjust GICv3 redistributor size
  458. * @dtb: Pointer to the DT blob in memory
  459. * @nr_cores: Number of CPU cores on this system.
  460. * @gicr_base: Base address of the first GICR frame, or ~0 if unchanged
  461. * @gicr_frame_size: Size of the GICR frame per core
  462. *
  463. * On a GICv3 compatible interrupt controller, the redistributor provides
  464. * a number of 64k pages per each supported core. So with a dynamic topology,
  465. * this size cannot be known upfront and thus can't be hardcoded into the DTB.
  466. *
  467. * Find the DT node describing the GICv3 interrupt controller, and adjust
  468. * the size of the redistributor to match the number of actual cores on
  469. * this system.
  470. * A GICv4 compatible redistributor uses four 64K pages per core, whereas GICs
  471. * without support for direct injection of virtual interrupts use two 64K pages.
  472. * The @gicr_frame_size parameter should be 262144 and 131072, respectively.
  473. * Also optionally allow adjusting the GICR frame base address, when this is
  474. * different due to ITS frames between distributor and redistributor.
  475. *
  476. * Return: 0 on success, negative error value otherwise.
  477. */
  478. int fdt_adjust_gic_redist(void *dtb, unsigned int nr_cores,
  479. uintptr_t gicr_base, unsigned int gicr_frame_size)
  480. {
  481. int offset = fdt_node_offset_by_compatible(dtb, 0, "arm,gic-v3");
  482. uint64_t reg_64;
  483. uint32_t reg_32;
  484. void *val;
  485. int parent, ret;
  486. int ac, sc;
  487. if (offset < 0) {
  488. return offset;
  489. }
  490. parent = fdt_parent_offset(dtb, offset);
  491. if (parent < 0) {
  492. return parent;
  493. }
  494. ac = fdt_address_cells(dtb, parent);
  495. sc = fdt_size_cells(dtb, parent);
  496. if (ac < 0 || sc < 0) {
  497. return -EINVAL;
  498. }
  499. if (gicr_base != INVALID_BASE_ADDR) {
  500. if (ac == 1) {
  501. reg_32 = cpu_to_fdt32(gicr_base);
  502. val = &reg_32;
  503. } else {
  504. reg_64 = cpu_to_fdt64(gicr_base);
  505. val = &reg_64;
  506. }
  507. /*
  508. * The redistributor base address is the second address in
  509. * the "reg" entry, so we have to skip one address and one
  510. * size cell.
  511. */
  512. ret = fdt_setprop_inplace_namelen_partial(dtb, offset,
  513. "reg", 3,
  514. (ac + sc) * 4,
  515. val, ac * 4);
  516. if (ret < 0) {
  517. return ret;
  518. }
  519. }
  520. if (sc == 1) {
  521. reg_32 = cpu_to_fdt32(nr_cores * gicr_frame_size);
  522. val = &reg_32;
  523. } else {
  524. reg_64 = cpu_to_fdt64(nr_cores * (uint64_t)gicr_frame_size);
  525. val = &reg_64;
  526. }
  527. /*
  528. * The redistributor is described in the second "reg" entry.
  529. * So we have to skip one address and one size cell, then another
  530. * address cell to get to the second size cell.
  531. */
  532. return fdt_setprop_inplace_namelen_partial(dtb, offset, "reg", 3,
  533. (ac + sc + ac) * 4,
  534. val, sc * 4);
  535. }
  536. /**
  537. * fdt_set_mac_address () - store MAC address in device tree
  538. * @dtb: pointer to the device tree blob in memory
  539. * @eth_idx: number of Ethernet interface in /aliases node
  540. * @mac_addr: pointer to 6 byte MAC address to store
  541. *
  542. * Use the generic local-mac-address property in a network device DT node
  543. * to define the MAC address this device should be using. Many platform
  544. * network devices lack device-specific non-volatile storage to hold this
  545. * address, and leave it up to firmware to find and store a unique MAC
  546. * address in the DT.
  547. * The MAC address could be read from some board or firmware defined storage,
  548. * or could be derived from some other unique property like a serial number.
  549. *
  550. * Return: 0 on success, a negative libfdt error value otherwise.
  551. */
  552. int fdt_set_mac_address(void *dtb, unsigned int ethernet_idx,
  553. const uint8_t *mac_addr)
  554. {
  555. char eth_alias[12];
  556. const char *path;
  557. int node;
  558. if (ethernet_idx > 9U) {
  559. return -FDT_ERR_BADVALUE;
  560. }
  561. snprintf(eth_alias, sizeof(eth_alias), "ethernet%d", ethernet_idx);
  562. path = fdt_get_alias(dtb, eth_alias);
  563. if (path == NULL) {
  564. return -FDT_ERR_NOTFOUND;
  565. }
  566. node = fdt_path_offset(dtb, path);
  567. if (node < 0) {
  568. ERROR("Path \"%s\" not found in DT: %d\n", path, node);
  569. return node;
  570. }
  571. return fdt_setprop(dtb, node, "local-mac-address", mac_addr, 6);
  572. }