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