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 
dt_find_compatible_driver(const void * fdt,int offs)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 
dt_have_prop(const void * fdt,int offs,const char * propname)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 
dt_disable_status(void * fdt,int node)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 
dt_enable_secure_status(void * fdt,int node)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 
dt_map_dev(const void * fdt,int offs,vaddr_t * base,size_t * size)89 int dt_map_dev(const void *fdt, int offs, vaddr_t *base, size_t *size)
90 {
91 	enum teecore_memtypes mtype;
92 	paddr_t pbase;
93 	vaddr_t vbase;
94 	size_t sz;
95 	int st;
96 
97 	assert(cpu_mmu_enabled());
98 
99 	st = _fdt_get_status(fdt, offs);
100 	if (st == DT_STATUS_DISABLED)
101 		return -1;
102 
103 	pbase = _fdt_reg_base_address(fdt, offs);
104 	if (pbase == DT_INFO_INVALID_REG)
105 		return -1;
106 	sz = _fdt_reg_size(fdt, offs);
107 	if (sz == DT_INFO_INVALID_REG_SIZE)
108 		return -1;
109 
110 	if ((st & DT_STATUS_OK_SEC) && !(st & DT_STATUS_OK_NSEC))
111 		mtype = MEM_AREA_IO_SEC;
112 	else
113 		mtype = MEM_AREA_IO_NSEC;
114 
115 	/* Check if we have a mapping, create one if needed */
116 	vbase = (vaddr_t)core_mmu_add_mapping(mtype, pbase, sz);
117 	if (!vbase) {
118 		EMSG("Failed to map %zu bytes at PA 0x%"PRIxPA,
119 		     (size_t)sz, pbase);
120 		return -1;
121 	}
122 
123 	*base = vbase;
124 	*size = sz;
125 	return 0;
126 }
127 
128 /* Read a physical address (n=1 or 2 cells) */
_fdt_read_paddr(const uint32_t * cell,int n)129 static paddr_t _fdt_read_paddr(const uint32_t *cell, int n)
130 {
131 	paddr_t addr;
132 
133 	if (n < 1 || n > 2)
134 		goto bad;
135 
136 	addr = fdt32_to_cpu(*cell);
137 	cell++;
138 	if (n == 2) {
139 #ifdef ARM32
140 		if (addr) {
141 			/* High order 32 bits can't be nonzero */
142 			goto bad;
143 		}
144 		addr = fdt32_to_cpu(*cell);
145 #else
146 		addr = (addr << 32) | fdt32_to_cpu(*cell);
147 #endif
148 	}
149 
150 	if (!addr)
151 		goto bad;
152 
153 	return addr;
154 bad:
155 	return DT_INFO_INVALID_REG;
156 
157 }
158 
_fdt_reg_base_address(const void * fdt,int offs)159 paddr_t _fdt_reg_base_address(const void *fdt, int offs)
160 {
161 	const void *reg;
162 	int ncells;
163 	int len;
164 	int parent;
165 
166 	parent = fdt_parent_offset(fdt, offs);
167 	if (parent < 0)
168 		return DT_INFO_INVALID_REG;
169 
170 	reg = fdt_getprop(fdt, offs, "reg", &len);
171 	if (!reg)
172 		return DT_INFO_INVALID_REG;
173 
174 	ncells = fdt_address_cells(fdt, parent);
175 	if (ncells < 0)
176 		return DT_INFO_INVALID_REG;
177 
178 	return _fdt_read_paddr(reg, ncells);
179 }
180 
_fdt_reg_size(const void * fdt,int offs)181 size_t _fdt_reg_size(const void *fdt, int offs)
182 {
183 	const uint32_t *reg;
184 	uint32_t sz;
185 	int n;
186 	int len;
187 	int parent;
188 
189 	parent = fdt_parent_offset(fdt, offs);
190 	if (parent < 0)
191 		return DT_INFO_INVALID_REG_SIZE;
192 
193 	reg = (const uint32_t *)fdt_getprop(fdt, offs, "reg", &len);
194 	if (!reg)
195 		return DT_INFO_INVALID_REG_SIZE;
196 
197 	n = fdt_address_cells(fdt, parent);
198 	if (n < 1 || n > 2)
199 		return DT_INFO_INVALID_REG_SIZE;
200 
201 	reg += n;
202 
203 	n = fdt_size_cells(fdt, parent);
204 	if (n < 1 || n > 2)
205 		return DT_INFO_INVALID_REG_SIZE;
206 
207 	sz = fdt32_to_cpu(*reg);
208 	if (n == 2) {
209 		if (sz)
210 			return DT_INFO_INVALID_REG_SIZE;
211 		reg++;
212 		sz = fdt32_to_cpu(*reg);
213 	}
214 
215 	return sz;
216 }
217 
is_okay(const char * st,int len)218 static bool is_okay(const char *st, int len)
219 {
220 	return !strncmp(st, "ok", len) || !strncmp(st, "okay", len);
221 }
222 
_fdt_get_status(const void * fdt,int offs)223 int _fdt_get_status(const void *fdt, int offs)
224 {
225 	const char *prop;
226 	int st = 0;
227 	int len;
228 
229 	prop = fdt_getprop(fdt, offs, "status", &len);
230 	if (!prop || is_okay(prop, len)) {
231 		/* If status is not specified, it defaults to "okay" */
232 		st |= DT_STATUS_OK_NSEC;
233 	}
234 
235 	prop = fdt_getprop(fdt, offs, "secure-status", &len);
236 	if (!prop) {
237 		/*
238 		 * When secure-status is not specified it defaults to the same
239 		 * value as status
240 		 */
241 		if (st & DT_STATUS_OK_NSEC)
242 			st |= DT_STATUS_OK_SEC;
243 	} else {
244 		if (is_okay(prop, len))
245 			st |= DT_STATUS_OK_SEC;
246 	}
247 
248 	return st;
249 }
250 
_fdt_fill_device_info(const void * fdt,struct dt_node_info * info,int offs)251 void _fdt_fill_device_info(const void *fdt, struct dt_node_info *info, int offs)
252 {
253 	struct dt_node_info dinfo = {
254 		.reg = DT_INFO_INVALID_REG,
255 		.reg_size = DT_INFO_INVALID_REG_SIZE,
256 		.clock = DT_INFO_INVALID_CLOCK,
257 		.reset = DT_INFO_INVALID_RESET,
258 		.interrupt = DT_INFO_INVALID_INTERRUPT,
259 	};
260 	const fdt32_t *cuint;
261 
262 	dinfo.reg = _fdt_reg_base_address(fdt, offs);
263 	dinfo.reg_size = _fdt_reg_size(fdt, offs);
264 
265 	cuint = fdt_getprop(fdt, offs, "clocks", NULL);
266 	if (cuint) {
267 		cuint++;
268 		dinfo.clock = (int)fdt32_to_cpu(*cuint);
269 	}
270 
271 	cuint = fdt_getprop(fdt, offs, "resets", NULL);
272 	if (cuint) {
273 		cuint++;
274 		dinfo.reset = (int)fdt32_to_cpu(*cuint);
275 	}
276 
277 	dinfo.interrupt = dt_get_irq_type_prio(fdt, offs, &dinfo.type,
278 					       &dinfo.prio);
279 
280 	dinfo.status = _fdt_get_status(fdt, offs);
281 
282 	*info = dinfo;
283 }
284