xref: /optee_os/core/kernel/dt.c (revision bc12b0e95e3c63f46850c1e69c79cd6879c68543)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2016, Linaro Limited
4  */
5 
6 #include <assert.h>
7 #include <kernel/dt.h>
8 #include <kernel/dt_driver.h>
9 #include <kernel/interrupt.h>
10 #include <kernel/linker.h>
11 #include <libfdt.h>
12 #include <mm/core_memprot.h>
13 #include <mm/core_mmu.h>
14 #include <string.h>
15 #include <trace.h>
16 
17 const struct dt_driver *dt_find_compatible_driver(const void *fdt, int offs)
18 {
19 	const struct dt_device_match *dm;
20 	const struct dt_driver *drv;
21 
22 	for_each_dt_driver(drv) {
23 		for (dm = drv->match_table; dm; dm++) {
24 			if (!dm->compatible) {
25 				break;
26 			}
27 			if (!fdt_node_check_compatible(fdt, offs,
28 						       dm->compatible)) {
29 				return drv;
30 			}
31 		}
32 	}
33 
34 	return NULL;
35 }
36 
37 bool dt_have_prop(const void *fdt, int offs, const char *propname)
38 {
39 	const void *prop;
40 
41 	prop = fdt_getprop(fdt, offs, propname, NULL);
42 
43 	return prop;
44 }
45 
46 int dt_disable_status(void *fdt, int node)
47 {
48 	const char *prop = NULL;
49 	int len = 0;
50 
51 	prop = fdt_getprop(fdt, node, "status", &len);
52 	if (!prop) {
53 		if (fdt_setprop_string(fdt, node, "status", "disabled"))
54 			return -1;
55 	} else {
56 		/*
57 		 * Status is there, modify it.
58 		 * Ask to set "disabled" value to the property. The value
59 		 * will be automatically truncated with "len" size by the
60 		 * fdt_setprop_inplace function.
61 		 * Setting a value different from "ok" or "okay" will disable
62 		 * the property.
63 		 * Setting a truncated value of "disabled" with the original
64 		 * property "len" is preferred to not increase the DT size and
65 		 * losing time in recalculating the overall DT offsets.
66 		 * If original length of the status property is larger than
67 		 * "disabled", the property will start with "disabled" and be
68 		 * completed with the rest of the original property.
69 		 */
70 		if (fdt_setprop_inplace(fdt, node, "status", "disabled", len))
71 			return -1;
72 	}
73 
74 	return 0;
75 }
76 
77 int dt_enable_secure_status(void *fdt, int node)
78 {
79 	if (dt_disable_status(fdt, node)) {
80 		EMSG("Unable to disable Normal Status");
81 		return -1;
82 	}
83 
84 	if (fdt_setprop_string(fdt, node, "secure-status", "okay"))
85 		return -1;
86 
87 	return 0;
88 }
89 
90 int dt_map_dev(const void *fdt, int offs, vaddr_t *base, size_t *size,
91 	       enum dt_map_dev_directive mapping)
92 {
93 	enum teecore_memtypes mtype;
94 	paddr_t pbase;
95 	vaddr_t vbase;
96 	size_t sz;
97 	int st;
98 
99 	assert(cpu_mmu_enabled());
100 
101 	st = fdt_get_status(fdt, offs);
102 	if (st == DT_STATUS_DISABLED)
103 		return -1;
104 
105 	pbase = fdt_reg_base_address(fdt, offs);
106 	if (pbase == DT_INFO_INVALID_REG)
107 		return -1;
108 	sz = fdt_reg_size(fdt, offs);
109 	if (sz == DT_INFO_INVALID_REG_SIZE)
110 		return -1;
111 
112 	switch (mapping) {
113 	case DT_MAP_AUTO:
114 		if ((st & DT_STATUS_OK_SEC) && !(st & DT_STATUS_OK_NSEC))
115 			mtype = MEM_AREA_IO_SEC;
116 		else
117 			mtype = MEM_AREA_IO_NSEC;
118 		break;
119 	case DT_MAP_SECURE:
120 		mtype = MEM_AREA_IO_SEC;
121 		break;
122 	case DT_MAP_NON_SECURE:
123 		mtype = MEM_AREA_IO_NSEC;
124 		break;
125 	default:
126 		panic("Invalid mapping specified");
127 		break;
128 	}
129 
130 	/* Check if we have a mapping, create one if needed */
131 	vbase = (vaddr_t)core_mmu_add_mapping(mtype, pbase, sz);
132 	if (!vbase) {
133 		EMSG("Failed to map %zu bytes at PA 0x%"PRIxPA,
134 		     (size_t)sz, pbase);
135 		return -1;
136 	}
137 
138 	*base = vbase;
139 	*size = sz;
140 	return 0;
141 }
142 
143 /* Read a physical address (n=1 or 2 cells) */
144 static paddr_t fdt_read_paddr(const uint32_t *cell, int n)
145 {
146 	paddr_t addr;
147 
148 	if (n < 1 || n > 2)
149 		goto bad;
150 
151 	addr = fdt32_to_cpu(*cell);
152 	cell++;
153 	if (n == 2) {
154 #ifdef ARM32
155 		if (addr) {
156 			/* High order 32 bits can't be nonzero */
157 			goto bad;
158 		}
159 		addr = fdt32_to_cpu(*cell);
160 #else
161 		addr = (addr << 32) | fdt32_to_cpu(*cell);
162 #endif
163 	}
164 
165 	return addr;
166 bad:
167 	return DT_INFO_INVALID_REG;
168 
169 }
170 
171 paddr_t fdt_reg_base_address(const void *fdt, int offs)
172 {
173 	const void *reg;
174 	int ncells;
175 	int len;
176 	int parent;
177 
178 	parent = fdt_parent_offset(fdt, offs);
179 	if (parent < 0)
180 		return DT_INFO_INVALID_REG;
181 
182 	reg = fdt_getprop(fdt, offs, "reg", &len);
183 	if (!reg)
184 		return DT_INFO_INVALID_REG;
185 
186 	ncells = fdt_address_cells(fdt, parent);
187 	if (ncells < 0)
188 		return DT_INFO_INVALID_REG;
189 
190 	return fdt_read_paddr(reg, ncells);
191 }
192 
193 static size_t fdt_read_size(const uint32_t *cell, int n)
194 {
195 	uint32_t sz = 0;
196 
197 	sz = fdt32_to_cpu(*cell);
198 	if (n == 2) {
199 		if (sz)
200 			return DT_INFO_INVALID_REG_SIZE;
201 
202 		cell++;
203 		sz = fdt32_to_cpu(*cell);
204 	}
205 
206 	return sz;
207 }
208 
209 size_t fdt_reg_size(const void *fdt, int offs)
210 {
211 	const uint32_t *reg;
212 	int n;
213 	int len;
214 	int parent;
215 
216 	parent = fdt_parent_offset(fdt, offs);
217 	if (parent < 0)
218 		return DT_INFO_INVALID_REG_SIZE;
219 
220 	reg = (const uint32_t *)fdt_getprop(fdt, offs, "reg", &len);
221 	if (!reg)
222 		return DT_INFO_INVALID_REG_SIZE;
223 
224 	n = fdt_address_cells(fdt, parent);
225 	if (n < 1 || n > 2)
226 		return DT_INFO_INVALID_REG_SIZE;
227 
228 	reg += n;
229 
230 	n = fdt_size_cells(fdt, parent);
231 	if (n < 1 || n > 2)
232 		return DT_INFO_INVALID_REG_SIZE;
233 
234 	return fdt_read_size(reg, n);
235 }
236 
237 static bool is_okay(const char *st, int len)
238 {
239 	return !strncmp(st, "ok", len) || !strncmp(st, "okay", len);
240 }
241 
242 int fdt_get_status(const void *fdt, int offs)
243 {
244 	const char *prop;
245 	int st = 0;
246 	int len;
247 
248 	prop = fdt_getprop(fdt, offs, "status", &len);
249 	if (!prop || is_okay(prop, len)) {
250 		/* If status is not specified, it defaults to "okay" */
251 		st |= DT_STATUS_OK_NSEC;
252 	}
253 
254 	prop = fdt_getprop(fdt, offs, "secure-status", &len);
255 	if (!prop) {
256 		/*
257 		 * When secure-status is not specified it defaults to the same
258 		 * value as status
259 		 */
260 		if (st & DT_STATUS_OK_NSEC)
261 			st |= DT_STATUS_OK_SEC;
262 	} else {
263 		if (is_okay(prop, len))
264 			st |= DT_STATUS_OK_SEC;
265 	}
266 
267 	return st;
268 }
269 
270 void fdt_fill_device_info(const void *fdt, struct dt_node_info *info, int offs)
271 {
272 	struct dt_node_info dinfo = {
273 		.reg = DT_INFO_INVALID_REG,
274 		.reg_size = DT_INFO_INVALID_REG_SIZE,
275 		.clock = DT_INFO_INVALID_CLOCK,
276 		.reset = DT_INFO_INVALID_RESET,
277 		.interrupt = DT_INFO_INVALID_INTERRUPT,
278 	};
279 	const fdt32_t *cuint;
280 
281 	dinfo.reg = fdt_reg_base_address(fdt, offs);
282 	dinfo.reg_size = fdt_reg_size(fdt, offs);
283 
284 	cuint = fdt_getprop(fdt, offs, "clocks", NULL);
285 	if (cuint) {
286 		cuint++;
287 		dinfo.clock = (int)fdt32_to_cpu(*cuint);
288 	}
289 
290 	cuint = fdt_getprop(fdt, offs, "resets", NULL);
291 	if (cuint) {
292 		cuint++;
293 		dinfo.reset = (int)fdt32_to_cpu(*cuint);
294 	}
295 
296 	dinfo.interrupt = dt_get_irq_type_prio(fdt, offs, &dinfo.type,
297 					       &dinfo.prio);
298 
299 	dinfo.status = fdt_get_status(fdt, offs);
300 
301 	*info = dinfo;
302 }
303 
304 int fdt_read_uint32_array(const void *fdt, int node, const char *prop_name,
305 			  uint32_t *array, size_t count)
306 {
307 	const fdt32_t *cuint = NULL;
308 	int len = 0;
309 	uint32_t i = 0;
310 
311 	cuint = fdt_getprop(fdt, node, prop_name, &len);
312 	if (!cuint)
313 		return len;
314 
315 	if ((uint32_t)len != (count * sizeof(uint32_t)))
316 		return -FDT_ERR_BADLAYOUT;
317 
318 	for (i = 0; i < ((uint32_t)len / sizeof(uint32_t)); i++) {
319 		*array = fdt32_to_cpu(*cuint);
320 		array++;
321 		cuint++;
322 	}
323 
324 	return 0;
325 }
326 
327 int fdt_read_uint32_index(const void *fdt, int node, const char *prop_name,
328 			  int index, uint32_t *value)
329 {
330 	const fdt32_t *cuint = NULL;
331 	int len = 0;
332 
333 	cuint = fdt_getprop(fdt, node, prop_name, &len);
334 	if (!cuint)
335 		return len;
336 
337 	if ((uint32_t)len < (sizeof(uint32_t) * (index + 1)))
338 		return -FDT_ERR_BADLAYOUT;
339 
340 	*value = fdt32_to_cpu(cuint[index]);
341 
342 	return 0;
343 }
344 
345 int fdt_read_uint32(const void *fdt, int node, const char *prop_name,
346 		    uint32_t *value)
347 {
348 	return fdt_read_uint32_array(fdt, node, prop_name, value, 1);
349 }
350 
351 uint32_t fdt_read_uint32_default(const void *fdt, int node,
352 				 const char *prop_name, uint32_t dflt_value)
353 {
354 	uint32_t ret = dflt_value;
355 
356 	fdt_read_uint32_index(fdt, node, prop_name, 0, &ret);
357 
358 	return ret;
359 }
360 
361 int fdt_get_reg_props_by_index(const void *fdt, int node, int index,
362 			       paddr_t *base, size_t *size)
363 {
364 	const fdt32_t *prop = NULL;
365 	int parent = 0;
366 	int len = 0;
367 	int address_cells = 0;
368 	int size_cells = 0;
369 	int cell = 0;
370 
371 	parent = fdt_parent_offset(fdt, node);
372 	if (parent < 0)
373 		return parent;
374 
375 	address_cells = fdt_address_cells(fdt, parent);
376 	if (address_cells < 0)
377 		return address_cells;
378 
379 	size_cells = fdt_size_cells(fdt, parent);
380 	if (size_cells < 0)
381 		return size_cells;
382 
383 	cell = index * (address_cells + size_cells);
384 
385 	prop = fdt_getprop(fdt, node, "reg", &len);
386 	if (!prop)
387 		return len;
388 
389 	if (((cell + address_cells + size_cells) * (int)sizeof(uint32_t)) > len)
390 		return -FDT_ERR_BADVALUE;
391 
392 	if (base) {
393 		*base = fdt_read_paddr(&prop[cell], address_cells);
394 		if (*base == DT_INFO_INVALID_REG)
395 			return -FDT_ERR_BADVALUE;
396 	}
397 
398 	if (size) {
399 		*size = fdt_read_size(&prop[cell + address_cells], size_cells);
400 		if (*size == DT_INFO_INVALID_REG_SIZE)
401 			return -FDT_ERR_BADVALUE;
402 	}
403 
404 	return 0;
405 }
406 
407 int fdt_get_reg_props_by_name(const void *fdt, int node, const char *name,
408 			      paddr_t *base, size_t *size)
409 {
410 	int index = 0;
411 
412 	index = fdt_stringlist_search(fdt, node, "reg-names", name);
413 	if (index < 0)
414 		return index;
415 
416 	return fdt_get_reg_props_by_index(fdt, node, index, base, size);
417 }
418 
419 int dt_getprop_as_number(const void *fdt, int nodeoffset, const char *name,
420 			 uint64_t *num)
421 {
422 	const void *prop = NULL;
423 	int len = 0;
424 
425 	prop = fdt_getprop(fdt, nodeoffset, name, &len);
426 	if (!prop)
427 		return len;
428 
429 	switch (len) {
430 	case sizeof(uint32_t):
431 		*num = fdt32_ld(prop);
432 		return 0;
433 	case sizeof(uint64_t):
434 		*num = fdt64_ld(prop);
435 		return 0;
436 	default:
437 		return -FDT_ERR_BADVALUE;
438 	}
439 }
440