xref: /optee_os/core/kernel/dt.c (revision 5b25c76ac40f830867e3d60800120ffd7874e8dc)
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/linker.h>
9 #include <libfdt.h>
10 #include <mm/core_memprot.h>
11 #include <mm/core_mmu.h>
12 #include <string.h>
13 #include <trace.h>
14 
15 const struct dt_driver *dt_find_compatible_driver(const void *fdt, int offs)
16 {
17 	const struct dt_device_match *dm;
18 	const struct dt_driver *drv;
19 
20 	for_each_dt_driver(drv) {
21 		for (dm = drv->match_table; dm; dm++) {
22 			if (!dm->compatible) {
23 				break;
24 			}
25 			if (!fdt_node_check_compatible(fdt, offs,
26 						       dm->compatible)) {
27 				return drv;
28 			}
29 		}
30 	}
31 
32 	return NULL;
33 }
34 
35 const struct dt_driver *__dt_driver_start(void)
36 {
37 	return &__rodata_dtdrv_start;
38 }
39 
40 const struct dt_driver *__dt_driver_end(void)
41 {
42 	return &__rodata_dtdrv_end;
43 }
44 
45 bool dt_have_prop(const void *fdt, int offs, const char *propname)
46 {
47 	const void *prop;
48 
49 	prop = fdt_getprop(fdt, offs, propname, NULL);
50 
51 	return prop;
52 }
53 
54 int dt_get_irq(void *fdt, int node)
55 {
56 	const uint32_t *int_prop = NULL;
57 	int len_prop = 0;
58 	int it_num = DT_INFO_INVALID_INTERRUPT;
59 
60 	/*
61 	 * Interrupt property can be defined with at least 2x32 bits word
62 	 *  - Type of interrupt
63 	 *  - Interrupt Number
64 	 */
65 	int_prop = fdt_getprop(fdt, node, "interrupts", &len_prop);
66 
67 	if (!int_prop || len_prop < 2)
68 		return it_num;
69 
70 	it_num = fdt32_to_cpu(int_prop[1]);
71 
72 	return it_num;
73 }
74 
75 int dt_disable_status(void *fdt, int node)
76 {
77 	const char *prop = NULL;
78 	int len = 0;
79 
80 	prop = fdt_getprop(fdt, node, "status", &len);
81 	if (!prop) {
82 		if (fdt_setprop_string(fdt, node, "status", "disabled"))
83 			return -1;
84 	} else {
85 		/*
86 		 * Status is there, modify it.
87 		 * Ask to set "disabled" value to the property. The value
88 		 * will be automatically truncated with "len" size by the
89 		 * fdt_setprop_inplace function.
90 		 * Setting a value different from "ok" or "okay" will disable
91 		 * the property.
92 		 * Setting a truncated value of "disabled" with the original
93 		 * property "len" is preferred to not increase the DT size and
94 		 * losing time in recalculating the overall DT offsets.
95 		 * If original length of the status property is larger than
96 		 * "disabled", the property will start with "disabled" and be
97 		 * completed with the rest of the original property.
98 		 */
99 		if (fdt_setprop_inplace(fdt, node, "status", "disabled", len))
100 			return -1;
101 	}
102 
103 	return 0;
104 }
105 
106 int dt_enable_secure_status(void *fdt, int node)
107 {
108 	if (dt_disable_status(fdt, node)) {
109 		EMSG("Unable to disable Normal Status");
110 		return -1;
111 	}
112 
113 	if (fdt_setprop_string(fdt, node, "secure-status", "okay"))
114 		return -1;
115 
116 	return 0;
117 }
118 
119 int dt_map_dev(const void *fdt, int offs, vaddr_t *base, size_t *size)
120 {
121 	enum teecore_memtypes mtype;
122 	paddr_t pbase;
123 	vaddr_t vbase;
124 	ssize_t sz;
125 	int st;
126 
127 	assert(cpu_mmu_enabled());
128 
129 	st = _fdt_get_status(fdt, offs);
130 	if (st == DT_STATUS_DISABLED)
131 		return -1;
132 
133 	pbase = _fdt_reg_base_address(fdt, offs);
134 	if (pbase == DT_INFO_INVALID_REG)
135 		return -1;
136 	sz = _fdt_reg_size(fdt, offs);
137 	if (sz < 0)
138 		return -1;
139 
140 	if ((st & DT_STATUS_OK_SEC) && !(st & DT_STATUS_OK_NSEC))
141 		mtype = MEM_AREA_IO_SEC;
142 	else
143 		mtype = MEM_AREA_IO_NSEC;
144 
145 	/* Check if we have a mapping, create one if needed */
146 	if (!core_mmu_add_mapping(mtype, pbase, sz)) {
147 		EMSG("Failed to map %zu bytes at PA 0x%"PRIxPA,
148 		     (size_t)sz, pbase);
149 		return -1;
150 	}
151 	vbase = (vaddr_t)phys_to_virt(pbase, mtype);
152 	if (!vbase) {
153 		EMSG("Failed to get VA for PA 0x%"PRIxPA, pbase);
154 		return -1;
155 	}
156 
157 	*base = vbase;
158 	*size = sz;
159 	return 0;
160 }
161 
162 /* Read a physical address (n=1 or 2 cells) */
163 static paddr_t _fdt_read_paddr(const uint32_t *cell, int n)
164 {
165 	paddr_t addr;
166 
167 	if (n < 1 || n > 2)
168 		goto bad;
169 
170 	addr = fdt32_to_cpu(*cell);
171 	cell++;
172 	if (n == 2) {
173 #ifdef ARM32
174 		if (addr) {
175 			/* High order 32 bits can't be nonzero */
176 			goto bad;
177 		}
178 		addr = fdt32_to_cpu(*cell);
179 #else
180 		addr = (addr << 32) | fdt32_to_cpu(*cell);
181 #endif
182 	}
183 
184 	if (!addr)
185 		goto bad;
186 
187 	return addr;
188 bad:
189 	return DT_INFO_INVALID_REG;
190 
191 }
192 
193 paddr_t _fdt_reg_base_address(const void *fdt, int offs)
194 {
195 	const void *reg;
196 	int ncells;
197 	int len;
198 	int parent;
199 
200 	parent = fdt_parent_offset(fdt, offs);
201 	if (parent < 0)
202 		return DT_INFO_INVALID_REG;
203 
204 	reg = fdt_getprop(fdt, offs, "reg", &len);
205 	if (!reg)
206 		return DT_INFO_INVALID_REG;
207 
208 	ncells = fdt_address_cells(fdt, parent);
209 	if (ncells < 0)
210 		return DT_INFO_INVALID_REG;
211 
212 	return _fdt_read_paddr(reg, ncells);
213 }
214 
215 ssize_t _fdt_reg_size(const void *fdt, int offs)
216 {
217 	const uint32_t *reg;
218 	uint32_t sz;
219 	int n;
220 	int len;
221 	int parent;
222 
223 	parent = fdt_parent_offset(fdt, offs);
224 	if (parent < 0)
225 		return DT_INFO_INVALID_REG;
226 
227 	reg = (const uint32_t *)fdt_getprop(fdt, offs, "reg", &len);
228 	if (!reg)
229 		return -1;
230 
231 	n = fdt_address_cells(fdt, parent);
232 	if (n < 1 || n > 2)
233 		return -1;
234 
235 	reg += n;
236 
237 	n = fdt_size_cells(fdt, parent);
238 	if (n < 1 || n > 2)
239 		return -1;
240 
241 	sz = fdt32_to_cpu(*reg);
242 	if (n == 2) {
243 		if (sz)
244 			return -1;
245 		reg++;
246 		sz = fdt32_to_cpu(*reg);
247 	}
248 
249 	return sz;
250 }
251 
252 static bool is_okay(const char *st, int len)
253 {
254 	return !strncmp(st, "ok", len) || !strncmp(st, "okay", len);
255 }
256 
257 int _fdt_get_status(const void *fdt, int offs)
258 {
259 	const char *prop;
260 	int st = 0;
261 	int len;
262 
263 	prop = fdt_getprop(fdt, offs, "status", &len);
264 	if (!prop || is_okay(prop, len)) {
265 		/* If status is not specified, it defaults to "okay" */
266 		st |= DT_STATUS_OK_NSEC;
267 	}
268 
269 	prop = fdt_getprop(fdt, offs, "secure-status", &len);
270 	if (!prop) {
271 		/*
272 		 * When secure-status is not specified it defaults to the same
273 		 * value as status
274 		 */
275 		if (st & DT_STATUS_OK_NSEC)
276 			st |= DT_STATUS_OK_SEC;
277 	} else {
278 		if (is_okay(prop, len))
279 			st |= DT_STATUS_OK_SEC;
280 	}
281 
282 	return st;
283 }
284 
285 void _fdt_fill_device_info(void *fdt, struct dt_node_info *info, int offs)
286 {
287 	struct dt_node_info dinfo = {
288 		.reg = DT_INFO_INVALID_REG,
289 		.clock = DT_INFO_INVALID_CLOCK,
290 		.reset = DT_INFO_INVALID_RESET,
291 	};
292 	const fdt32_t *cuint;
293 
294 	dinfo.reg = _fdt_reg_base_address(fdt, offs);
295 
296 	cuint = fdt_getprop(fdt, offs, "clocks", NULL);
297 	if (cuint) {
298 		cuint++;
299 		dinfo.clock = (int)fdt32_to_cpu(*cuint);
300 	}
301 
302 	cuint = fdt_getprop(fdt, offs, "resets", NULL);
303 	if (cuint) {
304 		cuint++;
305 		dinfo.reset = (int)fdt32_to_cpu(*cuint);
306 	}
307 
308 	dinfo.status = _fdt_get_status(fdt, offs);
309 
310 	*info = dinfo;
311 }
312