xref: /optee_os/core/kernel/dt.c (revision 6cfa381e534b362afbd103f526b132048e54ba47)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2016, Linaro Limited
4  */
5 
6 #include <assert.h>
7 #include <config.h>
8 #include <initcall.h>
9 #include <kernel/dt.h>
10 #include <kernel/dt_driver.h>
11 #include <kernel/interrupt.h>
12 #include <kernel/linker.h>
13 #include <libfdt.h>
14 #include <mm/core_memprot.h>
15 #include <mm/core_mmu.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <trace.h>
19 
20 static struct dt_descriptor external_dt __nex_bss;
21 
22 const struct dt_driver *dt_find_compatible_driver(const void *fdt, int offs)
23 {
24 	const struct dt_device_match *dm;
25 	const struct dt_driver *drv;
26 
27 	for_each_dt_driver(drv) {
28 		for (dm = drv->match_table; dm; dm++) {
29 			if (!dm->compatible) {
30 				break;
31 			}
32 			if (!fdt_node_check_compatible(fdt, offs,
33 						       dm->compatible)) {
34 				return drv;
35 			}
36 		}
37 	}
38 
39 	return NULL;
40 }
41 
42 bool dt_have_prop(const void *fdt, int offs, const char *propname)
43 {
44 	const void *prop;
45 
46 	prop = fdt_getprop(fdt, offs, propname, NULL);
47 
48 	return prop;
49 }
50 
51 int dt_disable_status(void *fdt, int node)
52 {
53 	const char *prop = NULL;
54 	int len = 0;
55 
56 	prop = fdt_getprop(fdt, node, "status", &len);
57 	if (!prop) {
58 		if (fdt_setprop_string(fdt, node, "status", "disabled"))
59 			return -1;
60 	} else {
61 		/*
62 		 * Status is there, modify it.
63 		 * Ask to set "disabled" value to the property. The value
64 		 * will be automatically truncated with "len" size by the
65 		 * fdt_setprop_inplace function.
66 		 * Setting a value different from "ok" or "okay" will disable
67 		 * the property.
68 		 * Setting a truncated value of "disabled" with the original
69 		 * property "len" is preferred to not increase the DT size and
70 		 * losing time in recalculating the overall DT offsets.
71 		 * If original length of the status property is larger than
72 		 * "disabled", the property will start with "disabled" and be
73 		 * completed with the rest of the original property.
74 		 */
75 		if (fdt_setprop_inplace(fdt, node, "status", "disabled", len))
76 			return -1;
77 	}
78 
79 	return 0;
80 }
81 
82 int dt_enable_secure_status(void *fdt, int node)
83 {
84 	if (dt_disable_status(fdt, node)) {
85 		EMSG("Unable to disable Normal Status");
86 		return -1;
87 	}
88 
89 	if (fdt_setprop_string(fdt, node, "secure-status", "okay"))
90 		return -1;
91 
92 	return 0;
93 }
94 
95 int dt_map_dev(const void *fdt, int offs, vaddr_t *base, size_t *size,
96 	       enum dt_map_dev_directive mapping)
97 {
98 	enum teecore_memtypes mtype;
99 	paddr_t pbase;
100 	vaddr_t vbase;
101 	size_t sz;
102 	int st;
103 
104 	assert(cpu_mmu_enabled());
105 
106 	st = fdt_get_status(fdt, offs);
107 	if (st == DT_STATUS_DISABLED)
108 		return -1;
109 
110 	pbase = fdt_reg_base_address(fdt, offs);
111 	if (pbase == DT_INFO_INVALID_REG)
112 		return -1;
113 	sz = fdt_reg_size(fdt, offs);
114 	if (sz == DT_INFO_INVALID_REG_SIZE)
115 		return -1;
116 
117 	switch (mapping) {
118 	case DT_MAP_AUTO:
119 		if ((st & DT_STATUS_OK_SEC) && !(st & DT_STATUS_OK_NSEC))
120 			mtype = MEM_AREA_IO_SEC;
121 		else
122 			mtype = MEM_AREA_IO_NSEC;
123 		break;
124 	case DT_MAP_SECURE:
125 		mtype = MEM_AREA_IO_SEC;
126 		break;
127 	case DT_MAP_NON_SECURE:
128 		mtype = MEM_AREA_IO_NSEC;
129 		break;
130 	default:
131 		panic("Invalid mapping specified");
132 		break;
133 	}
134 
135 	/* Check if we have a mapping, create one if needed */
136 	vbase = (vaddr_t)core_mmu_add_mapping(mtype, pbase, sz);
137 	if (!vbase) {
138 		EMSG("Failed to map %zu bytes at PA 0x%"PRIxPA,
139 		     (size_t)sz, pbase);
140 		return -1;
141 	}
142 
143 	*base = vbase;
144 	*size = sz;
145 	return 0;
146 }
147 
148 /* Read a physical address (n=1 or 2 cells) */
149 static paddr_t fdt_read_paddr(const uint32_t *cell, int n)
150 {
151 	paddr_t addr;
152 
153 	if (n < 1 || n > 2)
154 		goto bad;
155 
156 	addr = fdt32_to_cpu(*cell);
157 	cell++;
158 	if (n == 2) {
159 #ifdef ARM32
160 		if (addr) {
161 			/* High order 32 bits can't be nonzero */
162 			goto bad;
163 		}
164 		addr = fdt32_to_cpu(*cell);
165 #else
166 		addr = (addr << 32) | fdt32_to_cpu(*cell);
167 #endif
168 	}
169 
170 	return addr;
171 bad:
172 	return DT_INFO_INVALID_REG;
173 
174 }
175 
176 paddr_t fdt_reg_base_address(const void *fdt, int offs)
177 {
178 	const void *reg;
179 	int ncells;
180 	int len;
181 	int parent;
182 
183 	parent = fdt_parent_offset(fdt, offs);
184 	if (parent < 0)
185 		return DT_INFO_INVALID_REG;
186 
187 	reg = fdt_getprop(fdt, offs, "reg", &len);
188 	if (!reg)
189 		return DT_INFO_INVALID_REG;
190 
191 	ncells = fdt_address_cells(fdt, parent);
192 	if (ncells < 0)
193 		return DT_INFO_INVALID_REG;
194 
195 	return fdt_read_paddr(reg, ncells);
196 }
197 
198 static size_t fdt_read_size(const uint32_t *cell, int n)
199 {
200 	uint32_t sz = 0;
201 
202 	sz = fdt32_to_cpu(*cell);
203 	if (n == 2) {
204 		if (sz)
205 			return DT_INFO_INVALID_REG_SIZE;
206 
207 		cell++;
208 		sz = fdt32_to_cpu(*cell);
209 	}
210 
211 	return sz;
212 }
213 
214 size_t fdt_reg_size(const void *fdt, int offs)
215 {
216 	const uint32_t *reg;
217 	int n;
218 	int len;
219 	int parent;
220 
221 	parent = fdt_parent_offset(fdt, offs);
222 	if (parent < 0)
223 		return DT_INFO_INVALID_REG_SIZE;
224 
225 	reg = (const uint32_t *)fdt_getprop(fdt, offs, "reg", &len);
226 	if (!reg)
227 		return DT_INFO_INVALID_REG_SIZE;
228 
229 	n = fdt_address_cells(fdt, parent);
230 	if (n < 1 || n > 2)
231 		return DT_INFO_INVALID_REG_SIZE;
232 
233 	reg += n;
234 
235 	n = fdt_size_cells(fdt, parent);
236 	if (n < 1 || n > 2)
237 		return DT_INFO_INVALID_REG_SIZE;
238 
239 	return fdt_read_size(reg, n);
240 }
241 
242 static bool is_okay(const char *st, int len)
243 {
244 	return !strncmp(st, "ok", len) || !strncmp(st, "okay", len);
245 }
246 
247 int fdt_get_status(const void *fdt, int offs)
248 {
249 	const char *prop;
250 	int st = 0;
251 	int len;
252 
253 	prop = fdt_getprop(fdt, offs, "status", &len);
254 	if (!prop || is_okay(prop, len)) {
255 		/* If status is not specified, it defaults to "okay" */
256 		st |= DT_STATUS_OK_NSEC;
257 	}
258 
259 	prop = fdt_getprop(fdt, offs, "secure-status", &len);
260 	if (!prop) {
261 		/*
262 		 * When secure-status is not specified it defaults to the same
263 		 * value as status
264 		 */
265 		if (st & DT_STATUS_OK_NSEC)
266 			st |= DT_STATUS_OK_SEC;
267 	} else {
268 		if (is_okay(prop, len))
269 			st |= DT_STATUS_OK_SEC;
270 	}
271 
272 	return st;
273 }
274 
275 void fdt_fill_device_info(const void *fdt, struct dt_node_info *info, int offs)
276 {
277 	struct dt_node_info dinfo = {
278 		.reg = DT_INFO_INVALID_REG,
279 		.reg_size = DT_INFO_INVALID_REG_SIZE,
280 		.clock = DT_INFO_INVALID_CLOCK,
281 		.reset = DT_INFO_INVALID_RESET,
282 		.interrupt = DT_INFO_INVALID_INTERRUPT,
283 	};
284 	const fdt32_t *cuint;
285 
286 	dinfo.reg = fdt_reg_base_address(fdt, offs);
287 	dinfo.reg_size = fdt_reg_size(fdt, offs);
288 
289 	cuint = fdt_getprop(fdt, offs, "clocks", NULL);
290 	if (cuint) {
291 		cuint++;
292 		dinfo.clock = (int)fdt32_to_cpu(*cuint);
293 	}
294 
295 	cuint = fdt_getprop(fdt, offs, "resets", NULL);
296 	if (cuint) {
297 		cuint++;
298 		dinfo.reset = (int)fdt32_to_cpu(*cuint);
299 	}
300 
301 	dinfo.interrupt = dt_get_irq_type_prio(fdt, offs, &dinfo.type,
302 					       &dinfo.prio);
303 
304 	dinfo.status = fdt_get_status(fdt, offs);
305 
306 	*info = dinfo;
307 }
308 
309 int fdt_read_uint32_array(const void *fdt, int node, const char *prop_name,
310 			  uint32_t *array, size_t count)
311 {
312 	const fdt32_t *cuint = NULL;
313 	int len = 0;
314 	uint32_t i = 0;
315 
316 	cuint = fdt_getprop(fdt, node, prop_name, &len);
317 	if (!cuint)
318 		return len;
319 
320 	if ((uint32_t)len != (count * sizeof(uint32_t)))
321 		return -FDT_ERR_BADLAYOUT;
322 
323 	for (i = 0; i < ((uint32_t)len / sizeof(uint32_t)); i++) {
324 		*array = fdt32_to_cpu(*cuint);
325 		array++;
326 		cuint++;
327 	}
328 
329 	return 0;
330 }
331 
332 int fdt_read_uint32_index(const void *fdt, int node, const char *prop_name,
333 			  int index, uint32_t *value)
334 {
335 	const fdt32_t *cuint = NULL;
336 	int len = 0;
337 
338 	cuint = fdt_getprop(fdt, node, prop_name, &len);
339 	if (!cuint)
340 		return len;
341 
342 	if ((uint32_t)len < (sizeof(uint32_t) * (index + 1)))
343 		return -FDT_ERR_BADLAYOUT;
344 
345 	*value = fdt32_to_cpu(cuint[index]);
346 
347 	return 0;
348 }
349 
350 int fdt_read_uint32(const void *fdt, int node, const char *prop_name,
351 		    uint32_t *value)
352 {
353 	return fdt_read_uint32_array(fdt, node, prop_name, value, 1);
354 }
355 
356 uint32_t fdt_read_uint32_default(const void *fdt, int node,
357 				 const char *prop_name, uint32_t dflt_value)
358 {
359 	uint32_t ret = dflt_value;
360 
361 	fdt_read_uint32_index(fdt, node, prop_name, 0, &ret);
362 
363 	return ret;
364 }
365 
366 int fdt_get_reg_props_by_index(const void *fdt, int node, int index,
367 			       paddr_t *base, size_t *size)
368 {
369 	const fdt32_t *prop = NULL;
370 	int parent = 0;
371 	int len = 0;
372 	int address_cells = 0;
373 	int size_cells = 0;
374 	int cell = 0;
375 
376 	parent = fdt_parent_offset(fdt, node);
377 	if (parent < 0)
378 		return parent;
379 
380 	address_cells = fdt_address_cells(fdt, parent);
381 	if (address_cells < 0)
382 		return address_cells;
383 
384 	size_cells = fdt_size_cells(fdt, parent);
385 	if (size_cells < 0)
386 		return size_cells;
387 
388 	cell = index * (address_cells + size_cells);
389 
390 	prop = fdt_getprop(fdt, node, "reg", &len);
391 	if (!prop)
392 		return len;
393 
394 	if (((cell + address_cells + size_cells) * (int)sizeof(uint32_t)) > len)
395 		return -FDT_ERR_BADVALUE;
396 
397 	if (base) {
398 		*base = fdt_read_paddr(&prop[cell], address_cells);
399 		if (*base == DT_INFO_INVALID_REG)
400 			return -FDT_ERR_BADVALUE;
401 	}
402 
403 	if (size) {
404 		*size = fdt_read_size(&prop[cell + address_cells], size_cells);
405 		if (*size == DT_INFO_INVALID_REG_SIZE)
406 			return -FDT_ERR_BADVALUE;
407 	}
408 
409 	return 0;
410 }
411 
412 int fdt_get_reg_props_by_name(const void *fdt, int node, const char *name,
413 			      paddr_t *base, size_t *size)
414 {
415 	int index = 0;
416 
417 	index = fdt_stringlist_search(fdt, node, "reg-names", name);
418 	if (index < 0)
419 		return index;
420 
421 	return fdt_get_reg_props_by_index(fdt, node, index, base, size);
422 }
423 
424 int dt_getprop_as_number(const void *fdt, int nodeoffset, const char *name,
425 			 uint64_t *num)
426 {
427 	const void *prop = NULL;
428 	int len = 0;
429 
430 	prop = fdt_getprop(fdt, nodeoffset, name, &len);
431 	if (!prop)
432 		return len;
433 
434 	switch (len) {
435 	case sizeof(uint32_t):
436 		*num = fdt32_ld(prop);
437 		return 0;
438 	case sizeof(uint64_t):
439 		*num = fdt64_ld(prop);
440 		return 0;
441 	default:
442 		return -FDT_ERR_BADVALUE;
443 	}
444 }
445 
446 void *get_dt(void)
447 {
448 	void *fdt = get_embedded_dt();
449 
450 	if (!fdt)
451 		fdt = get_external_dt();
452 
453 	return fdt;
454 }
455 
456 void *get_secure_dt(void)
457 {
458 	void *fdt = get_embedded_dt();
459 
460 	if (!fdt && IS_ENABLED(CFG_MAP_EXT_DT_SECURE))
461 		fdt = get_external_dt();
462 
463 	return fdt;
464 }
465 
466 #if defined(CFG_EMBED_DTB)
467 void *get_embedded_dt(void)
468 {
469 	static bool checked;
470 
471 	assert(cpu_mmu_enabled());
472 
473 	if (!checked) {
474 		IMSG("Embedded DTB found");
475 
476 		if (fdt_check_header(embedded_secure_dtb))
477 			panic("Invalid embedded DTB");
478 
479 		checked = true;
480 	}
481 
482 	return embedded_secure_dtb;
483 }
484 #else
485 void *get_embedded_dt(void)
486 {
487 	return NULL;
488 }
489 #endif /*CFG_EMBED_DTB*/
490 
491 #ifdef _CFG_USE_DTB_OVERLAY
492 static int add_dt_overlay_fragment(struct dt_descriptor *dt, int ioffs)
493 {
494 	char frag[32] = { };
495 	int offs = 0;
496 	int ret = 0;
497 
498 	snprintf(frag, sizeof(frag), "fragment@%d", dt->frag_id);
499 	offs = fdt_add_subnode(dt->blob, ioffs, frag);
500 	if (offs < 0)
501 		return offs;
502 
503 	dt->frag_id += 1;
504 
505 	ret = fdt_setprop_string(dt->blob, offs, "target-path", "/");
506 	if (ret < 0)
507 		return ret;
508 
509 	return fdt_add_subnode(dt->blob, offs, "__overlay__");
510 }
511 
512 static int init_dt_overlay(struct dt_descriptor *dt, int __maybe_unused dt_size)
513 {
514 	int fragment = 0;
515 
516 	if (IS_ENABLED(CFG_EXTERNAL_DTB_OVERLAY)) {
517 		if (!fdt_check_header(dt->blob)) {
518 			fdt_for_each_subnode(fragment, dt->blob, 0)
519 				dt->frag_id += 1;
520 			return 0;
521 		}
522 	}
523 
524 	return fdt_create_empty_tree(dt->blob, dt_size);
525 }
526 #else
527 static int add_dt_overlay_fragment(struct dt_descriptor *dt __unused, int offs)
528 {
529 	return offs;
530 }
531 
532 static int init_dt_overlay(struct dt_descriptor *dt __unused,
533 			   int dt_size __unused)
534 {
535 	return 0;
536 }
537 #endif /* _CFG_USE_DTB_OVERLAY */
538 
539 struct dt_descriptor *get_external_dt_desc(void)
540 {
541 	if (!IS_ENABLED(CFG_EXTERNAL_DT))
542 		return NULL;
543 
544 	return &external_dt;
545 }
546 
547 void init_external_dt(unsigned long phys_dt)
548 {
549 	struct dt_descriptor *dt = &external_dt;
550 	void *fdt = NULL;
551 	int ret = 0;
552 
553 	if (!IS_ENABLED(CFG_EXTERNAL_DT))
554 		return;
555 
556 	if (!phys_dt) {
557 		/*
558 		 * No need to panic as we're not using the DT in OP-TEE
559 		 * yet, we're only adding some nodes for normal world use.
560 		 * This makes the switch to using DT easier as we can boot
561 		 * a newer OP-TEE with older boot loaders. Once we start to
562 		 * initialize devices based on DT we'll likely panic
563 		 * instead of returning here.
564 		 */
565 		IMSG("No non-secure external DT");
566 		return;
567 	}
568 
569 	fdt = core_mmu_add_mapping(MEM_AREA_EXT_DT, phys_dt, CFG_DTB_MAX_SIZE);
570 	if (!fdt)
571 		panic("Failed to map external DTB");
572 
573 	dt->blob = fdt;
574 
575 	ret = init_dt_overlay(dt, CFG_DTB_MAX_SIZE);
576 	if (ret < 0) {
577 		EMSG("Device Tree Overlay init fail @ %#lx: error %d", phys_dt,
578 		     ret);
579 		panic();
580 	}
581 
582 	ret = fdt_open_into(fdt, fdt, CFG_DTB_MAX_SIZE);
583 	if (ret < 0) {
584 		EMSG("Invalid Device Tree at %#lx: error %d", phys_dt, ret);
585 		panic();
586 	}
587 
588 	IMSG("Non-secure external DT found");
589 }
590 
591 void *get_external_dt(void)
592 {
593 	if (!IS_ENABLED(CFG_EXTERNAL_DT))
594 		return NULL;
595 
596 	assert(cpu_mmu_enabled());
597 	return external_dt.blob;
598 }
599 
600 static TEE_Result release_external_dt(void)
601 {
602 	int ret = 0;
603 
604 	if (!IS_ENABLED(CFG_EXTERNAL_DT))
605 		return TEE_SUCCESS;
606 
607 	if (!external_dt.blob)
608 		return TEE_SUCCESS;
609 
610 	ret = fdt_pack(external_dt.blob);
611 	if (ret < 0) {
612 		EMSG("Failed to pack Device Tree at 0x%" PRIxPA ": error %d",
613 		     virt_to_phys(external_dt.blob), ret);
614 		panic();
615 	}
616 
617 	if (core_mmu_remove_mapping(MEM_AREA_EXT_DT, external_dt.blob,
618 				    CFG_DTB_MAX_SIZE))
619 		panic("Failed to remove temporary Device Tree mapping");
620 
621 	/* External DTB no more reached, reset pointer to invalid */
622 	external_dt.blob = NULL;
623 
624 	return TEE_SUCCESS;
625 }
626 
627 boot_final(release_external_dt);
628 
629 int add_dt_path_subnode(struct dt_descriptor *dt, const char *path,
630 			const char *subnode)
631 {
632 	int offs = 0;
633 
634 	offs = fdt_path_offset(dt->blob, path);
635 	if (offs < 0)
636 		return offs;
637 	offs = add_dt_overlay_fragment(dt, offs);
638 	if (offs < 0)
639 		return offs;
640 	return fdt_add_subnode(dt->blob, offs, subnode);
641 }
642 
643 static void set_dt_val(void *data, uint32_t cell_size, uint64_t val)
644 {
645 	if (cell_size == 1) {
646 		fdt32_t v = cpu_to_fdt32((uint32_t)val);
647 
648 		memcpy(data, &v, sizeof(v));
649 	} else {
650 		fdt64_t v = cpu_to_fdt64(val);
651 
652 		memcpy(data, &v, sizeof(v));
653 	}
654 }
655 
656 int add_res_mem_dt_node(struct dt_descriptor *dt, const char *name,
657 			paddr_t pa, size_t size)
658 {
659 	int offs = 0;
660 	int ret = 0;
661 	int addr_size = -1;
662 	int len_size = -1;
663 	bool found = true;
664 	char subnode_name[80] = { };
665 
666 	offs = fdt_path_offset(dt->blob, "/reserved-memory");
667 
668 	if (offs < 0) {
669 		found = false;
670 		offs = 0;
671 	}
672 
673 	if (IS_ENABLED2(_CFG_USE_DTB_OVERLAY)) {
674 		len_size = sizeof(paddr_t) / sizeof(uint32_t);
675 		addr_size = sizeof(paddr_t) / sizeof(uint32_t);
676 	} else {
677 		len_size = fdt_size_cells(dt->blob, offs);
678 		if (len_size < 0)
679 			return len_size;
680 		addr_size = fdt_address_cells(dt->blob, offs);
681 		if (addr_size < 0)
682 			return addr_size;
683 	}
684 
685 	if (!found) {
686 		offs = add_dt_path_subnode(dt, "/", "reserved-memory");
687 		if (offs < 0)
688 			return offs;
689 		ret = fdt_setprop_cell(dt->blob, offs, "#address-cells",
690 				       addr_size);
691 		if (ret < 0)
692 			return ret;
693 		ret = fdt_setprop_cell(dt->blob, offs, "#size-cells", len_size);
694 		if (ret < 0)
695 			return ret;
696 		ret = fdt_setprop(dt->blob, offs, "ranges", NULL, 0);
697 		if (ret < 0)
698 			return ret;
699 	}
700 
701 	ret = snprintf(subnode_name, sizeof(subnode_name),
702 		       "%s@%" PRIxPA, name, pa);
703 	if (ret < 0 || ret >= (int)sizeof(subnode_name))
704 		DMSG("truncated node \"%s@%" PRIxPA"\"", name, pa);
705 	offs = fdt_add_subnode(dt->blob, offs, subnode_name);
706 	if (offs >= 0) {
707 		uint32_t data[FDT_MAX_NCELLS * 2] = { };
708 
709 		set_dt_val(data, addr_size, pa);
710 		set_dt_val(data + addr_size, len_size, size);
711 		ret = fdt_setprop(dt->blob, offs, "reg", data,
712 				  sizeof(uint32_t) * (addr_size + len_size));
713 		if (ret < 0)
714 			return ret;
715 		ret = fdt_setprop(dt->blob, offs, "no-map", NULL, 0);
716 		if (ret < 0)
717 			return ret;
718 	} else {
719 		return offs;
720 	}
721 	return 0;
722 }
723