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