1 /*
2  * Copyright (c) 2018-2021, ARM Limited and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 /* Helper functions to offer easier navigation of Device Tree Blob */
8 
9 #include <assert.h>
10 #include <errno.h>
11 #include <string.h>
12 
13 #include <libfdt.h>
14 
15 #include <common/debug.h>
16 #include <common/fdt_wrappers.h>
17 #include <common/uuid.h>
18 
19 /*
20  * Read cells from a given property of the given node. Any number of 32-bit
21  * cells of the property can be read. Returns 0 on success, or a negative
22  * FDT error value otherwise.
23  */
fdt_read_uint32_array(const void * dtb,int node,const char * prop_name,unsigned int cells,uint32_t * value)24 int fdt_read_uint32_array(const void *dtb, int node, const char *prop_name,
25 			  unsigned int cells, uint32_t *value)
26 {
27 	const fdt32_t *prop;
28 	int value_len;
29 
30 	assert(dtb != NULL);
31 	assert(prop_name != NULL);
32 	assert(value != NULL);
33 	assert(node >= 0);
34 
35 	/* Access property and obtain its length (in bytes) */
36 	prop = fdt_getprop(dtb, node, prop_name, &value_len);
37 	if (prop == NULL) {
38 		WARN("Couldn't find property %s in dtb\n", prop_name);
39 		return -FDT_ERR_NOTFOUND;
40 	}
41 
42 	/* Verify that property length can fill the entire array. */
43 	if (NCELLS((unsigned int)value_len) < cells) {
44 		WARN("Property length mismatch\n");
45 		return -FDT_ERR_BADVALUE;
46 	}
47 
48 	for (unsigned int i = 0U; i < cells; i++) {
49 		value[i] = fdt32_to_cpu(prop[i]);
50 	}
51 
52 	return 0;
53 }
54 
fdt_read_uint32(const void * dtb,int node,const char * prop_name,uint32_t * value)55 int fdt_read_uint32(const void *dtb, int node, const char *prop_name,
56 		    uint32_t *value)
57 {
58 	return fdt_read_uint32_array(dtb, node, prop_name, 1, value);
59 }
60 
fdt_read_uint32_default(const void * dtb,int node,const char * prop_name,uint32_t dflt_value)61 uint32_t fdt_read_uint32_default(const void *dtb, int node,
62 				 const char *prop_name, uint32_t dflt_value)
63 {
64 	uint32_t ret = dflt_value;
65 	int err = fdt_read_uint32(dtb, node, prop_name, &ret);
66 
67 	if (err < 0) {
68 		return dflt_value;
69 	}
70 
71 	return ret;
72 }
73 
fdt_read_uint64(const void * dtb,int node,const char * prop_name,uint64_t * value)74 int fdt_read_uint64(const void *dtb, int node, const char *prop_name,
75 		    uint64_t *value)
76 {
77 	uint32_t array[2] = {0, 0};
78 	int ret;
79 
80 	ret = fdt_read_uint32_array(dtb, node, prop_name, 2, array);
81 	if (ret < 0) {
82 		return ret;
83 	}
84 
85 	*value = ((uint64_t)array[0] << 32) | array[1];
86 	return 0;
87 }
88 
89 /*
90  * Read bytes from a given property of the given node. Any number of
91  * bytes of the property can be read. The fdt pointer is updated.
92  * Returns 0 on success, and -1 on error.
93  */
fdtw_read_bytes(const void * dtb,int node,const char * prop,unsigned int length,void * value)94 int fdtw_read_bytes(const void *dtb, int node, const char *prop,
95 		    unsigned int length, void *value)
96 {
97 	const void *ptr;
98 	int value_len;
99 
100 	assert(dtb != NULL);
101 	assert(prop != NULL);
102 	assert(value != NULL);
103 	assert(node >= 0);
104 
105 	/* Access property and obtain its length (in bytes) */
106 	ptr = fdt_getprop_namelen(dtb, node, prop, (int)strlen(prop),
107 					&value_len);
108 	if (ptr == NULL) {
109 		WARN("Couldn't find property %s in dtb\n", prop);
110 		return -1;
111 	}
112 
113 	/* Verify that property length is not less than number of bytes */
114 	if ((unsigned int)value_len < length) {
115 		WARN("Property length mismatch\n");
116 		return -1;
117 	}
118 
119 	(void)memcpy(value, ptr, length);
120 
121 	return 0;
122 }
123 
124 /*
125  * Read string from a given property of the given node. Up to 'size - 1'
126  * characters are read, and a NUL terminator is added. Returns 0 on success,
127  * and -1 upon error.
128  */
fdtw_read_string(const void * dtb,int node,const char * prop,char * str,size_t size)129 int fdtw_read_string(const void *dtb, int node, const char *prop,
130 		char *str, size_t size)
131 {
132 	const char *ptr;
133 	size_t len;
134 
135 	assert(dtb != NULL);
136 	assert(node >= 0);
137 	assert(prop != NULL);
138 	assert(str != NULL);
139 	assert(size > 0U);
140 
141 	ptr = fdt_getprop_namelen(dtb, node, prop, (int)strlen(prop), NULL);
142 	if (ptr == NULL) {
143 		WARN("Couldn't find property %s in dtb\n", prop);
144 		return -1;
145 	}
146 
147 	len = strlcpy(str, ptr, size);
148 	if (len >= size) {
149 		WARN("String of property %s in dtb has been truncated\n", prop);
150 		return -1;
151 	}
152 
153 	return 0;
154 }
155 
156 /*
157  * Read UUID from a given property of the given node. Returns 0 on success,
158  * and a negative value upon error.
159  */
fdtw_read_uuid(const void * dtb,int node,const char * prop,unsigned int length,uint8_t * uuid)160 int fdtw_read_uuid(const void *dtb, int node, const char *prop,
161 		   unsigned int length, uint8_t *uuid)
162 {
163 	/* Buffer for UUID string (plus NUL terminator) */
164 	char uuid_string[UUID_STRING_LENGTH + 1U];
165 	int err;
166 
167 	assert(dtb != NULL);
168 	assert(prop != NULL);
169 	assert(uuid != NULL);
170 	assert(node >= 0);
171 
172 	if (length < UUID_BYTES_LENGTH) {
173 		return -EINVAL;
174 	}
175 
176 	err = fdtw_read_string(dtb, node, prop, uuid_string,
177 			       UUID_STRING_LENGTH + 1U);
178 	if (err != 0) {
179 		return err;
180 	}
181 
182 	if (read_uuid(uuid, uuid_string) != 0) {
183 		return -FDT_ERR_BADVALUE;
184 	}
185 
186 	return 0;
187 }
188 
189 /*
190  * Write cells in place to a given property of the given node. At most 2 cells
191  * of the property are written. Returns 0 on success, and -1 upon error.
192  */
fdtw_write_inplace_cells(void * dtb,int node,const char * prop,unsigned int cells,void * value)193 int fdtw_write_inplace_cells(void *dtb, int node, const char *prop,
194 		unsigned int cells, void *value)
195 {
196 	int err, len;
197 
198 	assert(dtb != NULL);
199 	assert(prop != NULL);
200 	assert(value != NULL);
201 	assert(node >= 0);
202 
203 	/* We expect either 1 or 2 cell property */
204 	assert(cells <= 2U);
205 
206 	if (cells == 2U)
207 		*(uint64_t *)value = cpu_to_fdt64(*(uint64_t *)value);
208 	else
209 		*(uint32_t *)value = cpu_to_fdt32(*(uint32_t *)value);
210 
211 	len = (int)cells * 4;
212 
213 	/* Set property value in place */
214 	err = fdt_setprop_inplace(dtb, node, prop, value, len);
215 	if (err != 0) {
216 		WARN("Modify property %s failed with error %d\n", prop, err);
217 		return -1;
218 	}
219 
220 	return 0;
221 }
222 
223 /*
224  * Write bytes in place to a given property of the given node.
225  * Any number of bytes of the property can be written.
226  * Returns 0 on success, and < 0 on error.
227  */
fdtw_write_inplace_bytes(void * dtb,int node,const char * prop,unsigned int length,const void * data)228 int fdtw_write_inplace_bytes(void *dtb, int node, const char *prop,
229 			     unsigned int length, const void *data)
230 {
231 	const void *ptr;
232 	int namelen, value_len, err;
233 
234 	assert(dtb != NULL);
235 	assert(prop != NULL);
236 	assert(data != NULL);
237 	assert(node >= 0);
238 
239 	namelen = (int)strlen(prop);
240 
241 	/* Access property and obtain its length in bytes */
242 	ptr = fdt_getprop_namelen(dtb, node, prop, namelen, &value_len);
243 	if (ptr == NULL) {
244 		WARN("Couldn't find property %s in dtb\n", prop);
245 		return -1;
246 	}
247 
248 	/* Verify that property length is not less than number of bytes */
249 	if ((unsigned int)value_len < length) {
250 		WARN("Property length mismatch\n");
251 		return -1;
252 	}
253 
254 	/* Set property value in place */
255 	err = fdt_setprop_inplace_namelen_partial(dtb, node, prop,
256 						  namelen, 0,
257 						  data, (int)length);
258 	if (err != 0) {
259 		WARN("Set property %s failed with error %d\n", prop, err);
260 	}
261 
262 	return err;
263 }
264 
fdt_read_prop_cells(const fdt32_t * prop,int nr_cells)265 static uint64_t fdt_read_prop_cells(const fdt32_t *prop, int nr_cells)
266 {
267 	uint64_t reg = fdt32_to_cpu(prop[0]);
268 
269 	if (nr_cells > 1) {
270 		reg = (reg << 32) | fdt32_to_cpu(prop[1]);
271 	}
272 
273 	return reg;
274 }
275 
fdt_get_reg_props_by_index(const void * dtb,int node,int index,uintptr_t * base,size_t * size)276 int fdt_get_reg_props_by_index(const void *dtb, int node, int index,
277 			       uintptr_t *base, size_t *size)
278 {
279 	const fdt32_t *prop;
280 	int parent, len;
281 	int ac, sc;
282 	int cell;
283 
284 	parent = fdt_parent_offset(dtb, node);
285 	if (parent < 0) {
286 		return -FDT_ERR_BADOFFSET;
287 	}
288 
289 	ac = fdt_address_cells(dtb, parent);
290 	sc = fdt_size_cells(dtb, parent);
291 
292 	cell = index * (ac + sc);
293 
294 	prop = fdt_getprop(dtb, node, "reg", &len);
295 	if (prop == NULL) {
296 		WARN("Couldn't find \"reg\" property in dtb\n");
297 		return -FDT_ERR_NOTFOUND;
298 	}
299 
300 	if (((cell + ac + sc) * (int)sizeof(uint32_t)) > len) {
301 		return -FDT_ERR_BADVALUE;
302 	}
303 
304 	if (base != NULL) {
305 		*base = (uintptr_t)fdt_read_prop_cells(&prop[cell], ac);
306 	}
307 
308 	if (size != NULL) {
309 		*size = (size_t)fdt_read_prop_cells(&prop[cell + ac], sc);
310 	}
311 
312 	return 0;
313 }
314 
315 /*******************************************************************************
316  * This function fills reg node info (base & size) with an index found by
317  * checking the reg-names node.
318  * Returns 0 on success and a negative FDT error code on failure.
319  ******************************************************************************/
fdt_get_reg_props_by_name(const void * dtb,int node,const char * name,uintptr_t * base,size_t * size)320 int fdt_get_reg_props_by_name(const void *dtb, int node, const char *name,
321 			      uintptr_t *base, size_t *size)
322 {
323 	int index;
324 
325 	index = fdt_stringlist_search(dtb, node, "reg-names", name);
326 	if (index < 0) {
327 		return index;
328 	}
329 
330 	return fdt_get_reg_props_by_index(dtb, node, index, base, size);
331 }
332 
333 /*******************************************************************************
334  * This function gets the stdout path node.
335  * It reads the value indicated inside the device tree.
336  * Returns node offset on success and a negative FDT error code on failure.
337  ******************************************************************************/
fdt_get_stdout_node_offset(const void * dtb)338 int fdt_get_stdout_node_offset(const void *dtb)
339 {
340 	int node;
341 	const char *prop, *path;
342 	int len;
343 
344 	/* The /secure-chosen node takes precedence over the standard one. */
345 	node = fdt_path_offset(dtb, "/secure-chosen");
346 	if (node < 0) {
347 		node = fdt_path_offset(dtb, "/chosen");
348 		if (node < 0) {
349 			return -FDT_ERR_NOTFOUND;
350 		}
351 	}
352 
353 	prop = fdt_getprop(dtb, node, "stdout-path", NULL);
354 	if (prop == NULL) {
355 		return -FDT_ERR_NOTFOUND;
356 	}
357 
358 	/* Determine the actual path length, as a colon terminates the path. */
359 	path = strchr(prop, ':');
360 	if (path == NULL) {
361 		len = strlen(prop);
362 	} else {
363 		len = path - prop;
364 	}
365 
366 	/* Aliases cannot start with a '/', so it must be the actual path. */
367 	if (prop[0] == '/') {
368 		return fdt_path_offset_namelen(dtb, prop, len);
369 	}
370 
371 	/* Lookup the alias, as this contains the actual path. */
372 	path = fdt_get_alias_namelen(dtb, prop, len);
373 	if (path == NULL) {
374 		return -FDT_ERR_NOTFOUND;
375 	}
376 
377 	return fdt_path_offset(dtb, path);
378 }
379 
380 
381 /*******************************************************************************
382  * Only devices which are direct children of root node use CPU address domain.
383  * All other devices use addresses that are local to the device node and cannot
384  * directly used by CPU. Device tree provides an address translation mechanism
385  * through "ranges" property which provides mappings from local address space to
386  * parent address space. Since a device could be a child of a child node to the
387  * root node, there can be more than one level of address translation needed to
388  * map the device local address space to CPU address space.
389  * fdtw_translate_address() API performs address translation of a local address
390  * to a global address with help of various helper functions.
391  ******************************************************************************/
392 
fdtw_xlat_hit(const uint32_t * value,int child_addr_size,int parent_addr_size,int range_size,uint64_t base_address,uint64_t * translated_addr)393 static bool fdtw_xlat_hit(const uint32_t *value, int child_addr_size,
394 		int parent_addr_size, int range_size, uint64_t base_address,
395 		uint64_t *translated_addr)
396 {
397 	uint64_t local_address, parent_address, addr_range;
398 
399 	local_address = fdt_read_prop_cells(value, child_addr_size);
400 	parent_address = fdt_read_prop_cells(value + child_addr_size,
401 				parent_addr_size);
402 	addr_range = fdt_read_prop_cells(value + child_addr_size +
403 				parent_addr_size,
404 				range_size);
405 	VERBOSE("DT: Address %llx mapped to %llx with range %llx\n",
406 		local_address, parent_address, addr_range);
407 
408 	/* Perform range check */
409 	if ((base_address < local_address) ||
410 		(base_address >= local_address + addr_range)) {
411 		return false;
412 	}
413 
414 	/* Found hit for the addr range that needs to be translated */
415 	*translated_addr = parent_address + (base_address - local_address);
416 	VERBOSE("DT: child address %llx mapped to %llx in parent bus\n",
417 			local_address, parent_address);
418 	return true;
419 }
420 
421 #define ILLEGAL_ADDR	ULL(~0)
422 
fdtw_search_all_xlat_entries(const void * dtb,const struct fdt_property * ranges_prop,int local_bus,uint64_t base_address)423 static uint64_t fdtw_search_all_xlat_entries(const void *dtb,
424 				const struct fdt_property *ranges_prop,
425 				int local_bus, uint64_t base_address)
426 {
427 	uint64_t translated_addr;
428 	const uint32_t *next_entry;
429 	int parent_bus_node, nxlat_entries, length;
430 	int self_addr_cells, parent_addr_cells, self_size_cells, ncells_xlat;
431 
432 	/*
433 	 * The number of cells in one translation entry in ranges is the sum of
434 	 * the following values:
435 	 * self#address-cells + parent#address-cells + self#size-cells
436 	 * Ex: the iofpga ranges property has one translation entry with 4 cells
437 	 * They represent iofpga#addr-cells + motherboard#addr-cells + iofpga#size-cells
438 	 *              = 1                 + 2                      + 1
439 	 */
440 
441 	parent_bus_node = fdt_parent_offset(dtb, local_bus);
442 	self_addr_cells = fdt_address_cells(dtb, local_bus);
443 	self_size_cells = fdt_size_cells(dtb, local_bus);
444 	parent_addr_cells = fdt_address_cells(dtb, parent_bus_node);
445 
446 	/* Number of cells per translation entry i.e., mapping */
447 	ncells_xlat = self_addr_cells + parent_addr_cells + self_size_cells;
448 
449 	assert(ncells_xlat > 0);
450 
451 	/*
452 	 * Find the number of translations(mappings) specified in the current
453 	 * `ranges` property. Note that length represents number of bytes and
454 	 * is stored in big endian mode.
455 	 */
456 	length = fdt32_to_cpu(ranges_prop->len);
457 	nxlat_entries = (length/sizeof(uint32_t))/ncells_xlat;
458 
459 	assert(nxlat_entries > 0);
460 
461 	next_entry = (const uint32_t *)ranges_prop->data;
462 
463 	/* Iterate over the entries in the "ranges" */
464 	for (int i = 0; i < nxlat_entries; i++) {
465 		if (fdtw_xlat_hit(next_entry, self_addr_cells,
466 				parent_addr_cells, self_size_cells, base_address,
467 				&translated_addr)){
468 			return translated_addr;
469 		}
470 		next_entry = next_entry + ncells_xlat;
471 	}
472 
473 	INFO("DT: No translation found for address %llx in node %s\n",
474 		base_address, fdt_get_name(dtb, local_bus, NULL));
475 	return ILLEGAL_ADDR;
476 }
477 
478 
479 /*******************************************************************************
480  * address mapping needs to be done recursively starting from current node to
481  * root node through all intermediate parent nodes.
482  * Sample device tree is shown here:
483 
484 smb@0,0 {
485 	compatible = "simple-bus";
486 
487 	#address-cells = <2>;
488 	#size-cells = <1>;
489 	ranges = <0 0 0 0x08000000 0x04000000>,
490 		 <1 0 0 0x14000000 0x04000000>,
491 		 <2 0 0 0x18000000 0x04000000>,
492 		 <3 0 0 0x1c000000 0x04000000>,
493 		 <4 0 0 0x0c000000 0x04000000>,
494 		 <5 0 0 0x10000000 0x04000000>;
495 
496 	motherboard {
497 		arm,v2m-memory-map = "rs1";
498 		compatible = "arm,vexpress,v2m-p1", "simple-bus";
499 		#address-cells = <2>;
500 		#size-cells = <1>;
501 		ranges;
502 
503 		iofpga@3,00000000 {
504 			compatible = "arm,amba-bus", "simple-bus";
505 			#address-cells = <1>;
506 			#size-cells = <1>;
507 			ranges = <0 3 0 0x200000>;
508 			v2m_serial1: uart@a0000 {
509 				compatible = "arm,pl011", "arm,primecell";
510 				reg = <0x0a0000 0x1000>;
511 				interrupts = <0 6 4>;
512 				clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>;
513 				clock-names = "uartclk", "apb_pclk";
514 		};
515 	};
516 };
517 
518  * As seen above, there are 3 levels of address translations needed. An empty
519  * `ranges` property denotes identity mapping (as seen in `motherboard` node).
520  * Each ranges property can map a set of child addresses to parent bus. Hence
521  * there can be more than 1 (translation) entry in the ranges property as seen
522  * in the `smb` node which has 6 translation entries.
523  ******************************************************************************/
524 
525 /* Recursive implementation */
fdtw_translate_address(const void * dtb,int node,uint64_t base_address)526 uint64_t fdtw_translate_address(const void *dtb, int node,
527 				uint64_t base_address)
528 {
529 	int length, local_bus_node;
530 	const char *node_name;
531 	uint64_t global_address;
532 
533 	local_bus_node = fdt_parent_offset(dtb, node);
534 	node_name = fdt_get_name(dtb, local_bus_node, NULL);
535 
536 	/*
537 	 * In the example given above, starting from the leaf node:
538 	 * uart@a000 represents the current node
539 	 * iofpga@3,00000000 represents the local bus
540 	 * motherboard represents the parent bus
541 	 */
542 
543 	/* Read the ranges property */
544 	const struct fdt_property *property = fdt_get_property(dtb,
545 					local_bus_node, "ranges", &length);
546 
547 	if (property == NULL) {
548 		if (local_bus_node == 0) {
549 			/*
550 			 * root node doesn't have range property as addresses
551 			 * are in CPU address space.
552 			 */
553 			return base_address;
554 		}
555 		INFO("DT: Couldn't find ranges property in node %s\n",
556 			node_name);
557 		return ILLEGAL_ADDR;
558 	} else if (length == 0) {
559 		/* empty ranges indicates identity map to parent bus */
560 		return fdtw_translate_address(dtb, local_bus_node, base_address);
561 	}
562 
563 	VERBOSE("DT: Translation lookup in node %s at offset %d\n", node_name,
564 		local_bus_node);
565 	global_address = fdtw_search_all_xlat_entries(dtb, property,
566 				local_bus_node, base_address);
567 
568 	if (global_address == ILLEGAL_ADDR) {
569 		return ILLEGAL_ADDR;
570 	}
571 
572 	/* Translate the local device address recursively */
573 	return fdtw_translate_address(dtb, local_bus_node, global_address);
574 }
575