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