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