xref: /optee_os/core/drivers/tzc380.c (revision 5b25c76ac40f830867e3d60800120ffd7874e8dc)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright 2017-2020 NXP
4  * All rights reserved.
5  *
6  * Peng Fan <peng.fan@nxp.com>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright notice,
12  * this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright notice,
15  * this list of conditions and the following disclaimer in the documentation
16  * and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include <assert.h>
32 #include <drivers/tzc380.h>
33 #include <io.h>
34 #include <kernel/panic.h>
35 #include <mm/core_memprot.h>
36 #include <mm/core_mmu.h>
37 #include <stddef.h>
38 #include <trace.h>
39 #include <util.h>
40 
41 /*
42  * Implementation defined values used to validate inputs later.
43  * Filters : max of 4 ; 0 to 3
44  * Regions : max of 9 ; 0 to 8
45  * Address width : Values between 32 to 64
46  */
47 struct tzc_instance {
48 	vaddr_t base;
49 	uint8_t addr_width;
50 	uint8_t num_regions;
51 };
52 
53 static struct tzc_instance tzc;
54 
55 static uint32_t tzc_read_build_config(vaddr_t base)
56 {
57 	return io_read32(base + BUILD_CONFIG_OFF);
58 }
59 
60 static void tzc_write_action(vaddr_t base, enum tzc_action action)
61 {
62 	io_write32(base + ACTION_OFF, action);
63 }
64 
65 static uint32_t tzc_read_action(vaddr_t base)
66 {
67 	return io_read32(base + ACTION_OFF);
68 }
69 
70 static void tzc_write_region_base_low(vaddr_t base, uint32_t region,
71 				      uint32_t val)
72 {
73 	io_write32(base + REGION_SETUP_LOW_OFF(region), val);
74 }
75 
76 static void tzc_write_region_base_high(vaddr_t base, uint32_t region,
77 				       uint32_t val)
78 {
79 	io_write32(base + REGION_SETUP_HIGH_OFF(region), val);
80 }
81 
82 static uint32_t tzc_read_region_attributes(vaddr_t base, uint32_t region)
83 {
84 	return io_read32(base + REGION_ATTRIBUTES_OFF(region));
85 }
86 
87 static void tzc_write_region_attributes(vaddr_t base, uint32_t region,
88 					uint32_t val)
89 {
90 	io_write32(base + REGION_ATTRIBUTES_OFF(region), val);
91 }
92 
93 void tzc_init(vaddr_t base)
94 {
95 	uint32_t tzc_build;
96 
97 	assert(base);
98 	tzc.base = base;
99 
100 	/* Save values we will use later. */
101 	tzc_build = tzc_read_build_config(tzc.base);
102 	tzc.addr_width  = ((tzc_build >> BUILD_CONFIG_AW_SHIFT) &
103 			   BUILD_CONFIG_AW_MASK) + 1;
104 	tzc.num_regions = ((tzc_build >> BUILD_CONFIG_NR_SHIFT) &
105 			   BUILD_CONFIG_NR_MASK) + 1;
106 }
107 
108 /*
109  * There are two modes of operation for the region security
110  * permissions, with or without security inversion.
111  * Check TZC380 "2.2.5 Region security permissions" for
112  * more details.
113  */
114 void tzc_security_inversion_en(vaddr_t base)
115 {
116 	io_write32(base + SECURITY_INV_EN_OFF, 1);
117 }
118 
119 /*
120  * Enable a single region. Sometimes we could not use tzc_configure_region
121  * to enable the region, when security inversion is on.
122  * When need security inversion, we need to first configure
123  * region address and attribute, then configure security inversion,
124  * then enable the regions.
125  */
126 void tzc_region_enable(uint8_t region)
127 {
128 	uint32_t val;
129 
130 	val = tzc_read_region_attributes(tzc.base, region);
131 	val |= TZC_ATTR_REGION_EN_MASK;
132 	tzc_write_region_attributes(tzc.base, region, val);
133 }
134 
135 /*
136  * Dump info when TZC380 catchs an unallowed access with TZC
137  * interrupt enabled.
138  */
139 void tzc_fail_dump(void)
140 {
141 	vaddr_t base __maybe_unused = core_mmu_get_va(tzc.base,
142 						      MEM_AREA_IO_SEC);
143 
144 	EMSG("Fail address Low 0x%" PRIx32,
145 	     io_read32(base + FAIL_ADDRESS_LOW_OFF));
146 	EMSG("Fail address High 0x%" PRIx32,
147 	     io_read32(base + FAIL_ADDRESS_HIGH_OFF));
148 	EMSG("Fail Control 0x%" PRIx32, io_read32(base + FAIL_CONTROL_OFF));
149 	EMSG("Fail Id 0x%" PRIx32, io_read32(base + FAIL_ID));
150 }
151 
152 void tzc_int_clear(void)
153 {
154 	vaddr_t base = core_mmu_get_va(tzc.base, MEM_AREA_IO_SEC);
155 
156 	io_write32(base + INT_CLEAR, 0);
157 }
158 
159 static uint32_t addr_low(vaddr_t addr)
160 {
161 	return (uint32_t)addr;
162 }
163 
164 static uint32_t addr_high(vaddr_t addr __maybe_unused)
165 {
166 #if (UINTPTR_MAX == UINT64_MAX)
167 	return addr >> 32;
168 #else
169 	return 0;
170 #endif
171 }
172 
173 
174 /*
175  * `tzc_configure_region` is used to program regions into the TrustZone
176  * controller.
177  */
178 void tzc_configure_region(uint8_t region, vaddr_t region_base, uint32_t attr)
179 {
180 	assert(tzc.base);
181 
182 	assert(region < tzc.num_regions);
183 
184 	/*
185 	 * For region 0, this high/low/size/en field is Read Only (RO).
186 	 * So should not configure those field for region 0.
187 	 */
188 	if (region) {
189 		tzc_write_region_base_low(tzc.base, region,
190 					  addr_low(region_base));
191 		tzc_write_region_base_high(tzc.base, region,
192 					   addr_high(region_base));
193 		tzc_write_region_attributes(tzc.base, region, attr);
194 	} else {
195 		tzc_write_region_attributes(tzc.base, region,
196 					    attr & TZC_ATTR_SP_MASK);
197 	}
198 }
199 
200 void tzc_set_action(enum tzc_action action)
201 {
202 	assert(tzc.base);
203 
204 	/*
205 	 * - Currently no handler is provided to trap an error via interrupt
206 	 *   or exception.
207 	 * - The interrupt action has not been tested.
208 	 */
209 	tzc_write_action(tzc.base, action);
210 }
211 
212 uint32_t tzc_get_action(void)
213 {
214 	assert(tzc.base);
215 
216 	return tzc_read_action(tzc.base);
217 }
218 
219 int tzc_auto_configure(vaddr_t addr, vaddr_t size, uint32_t attr,
220 		       uint8_t region)
221 {
222 	uint64_t sub_region_size = 0;
223 	uint64_t area = 0;
224 	uint8_t lregion = region;
225 	uint64_t region_size = 0;
226 	vaddr_t sub_address = 0;
227 	vaddr_t address = addr;
228 	uint64_t lsize = size;
229 	uint32_t mask = 0;
230 	int i = 0;
231 	uint8_t pow = 0;
232 
233 	assert(tzc.base);
234 
235 	/*
236 	 * TZC380 RM
237 	 * For region_attributes_<n> registers, region_size:
238 	 * Note: The AXI address width, that is AXI_ADDRESS_MSB+1, controls the
239 	 * upper limit value of the field.
240 	 */
241 	pow = tzc.addr_width;
242 
243 	while (lsize != 0 && pow > 15) {
244 		region_size = 1ULL << pow;
245 
246 		/* Case region fits alignment and covers requested area */
247 		if ((address % region_size == 0) &&
248 		    ((address + lsize) % region_size == 0)) {
249 			tzc_configure_region(lregion, address,
250 					     TZC_ATTR_REGION_SIZE(pow - 1) |
251 					     TZC_ATTR_REGION_EN_MASK |
252 					     attr);
253 			lregion++;
254 			address += region_size;
255 			lsize -= region_size;
256 			pow--;
257 			continue;
258 		}
259 
260 		/* Cover area using several subregions */
261 		sub_region_size = region_size / 8;
262 		if (address % sub_region_size == 0 &&
263 		    lsize > 2 * sub_region_size) {
264 			sub_address = (address / region_size) * region_size;
265 			mask = 0;
266 			for (i = 0; i < 8; i++) {
267 				area = (i + 1) * sub_region_size;
268 				if (sub_address + area <= address ||
269 				    sub_address + area > address + lsize) {
270 					mask |= TZC_ATTR_SUBREGION_DIS(i);
271 				} else {
272 					address += sub_region_size;
273 					lsize -= sub_region_size;
274 				}
275 			}
276 			tzc_configure_region(lregion, sub_address,
277 					     TZC_ATTR_REGION_SIZE(pow - 1) |
278 					     TZC_ATTR_REGION_EN_MASK |
279 					     mask | attr);
280 			lregion++;
281 			pow--;
282 			continue;
283 		}
284 		pow--;
285 	}
286 	assert(lsize == 0);
287 	assert(address == addr + size);
288 	return lregion;
289 }
290 
291 /*
292  * `region_lockdown` is used to lockdown the TZC380 configuration to prevent
293  * unintended overwrites of the configuration. Returns TEE_ERROR_SECURITY in
294  * case the lockdown fails.
295  */
296 TEE_Result tzc_regions_lockdown(void)
297 {
298 	uint32_t val = 0;
299 	uint32_t check = 0;
300 
301 	val = LOCKDOWN_RANGE_ENABLE | tzc.num_regions;
302 	io_write32(tzc.base + LOCKDOWN_RANGE_OFF, val);
303 	check = io_read32(tzc.base + LOCKDOWN_RANGE_OFF);
304 	if (check != val)
305 		return TEE_ERROR_SECURITY;
306 
307 	val = LOCKDOWN_SELECT_RANGE_ENABLE;
308 	io_write32(tzc.base + LOCKDOWN_SELECT_OFF, val);
309 	check = io_read32(tzc.base + LOCKDOWN_SELECT_OFF);
310 	if (check != val)
311 		return TEE_ERROR_SECURITY;
312 
313 	return TEE_SUCCESS;
314 }
315 
316 #if TRACE_LEVEL >= TRACE_DEBUG
317 
318 static uint32_t tzc_read_region_base_low(vaddr_t base, uint32_t region)
319 {
320 	return io_read32(base + REGION_SETUP_LOW_OFF(region));
321 }
322 
323 static uint32_t tzc_read_region_base_high(vaddr_t base, uint32_t region)
324 {
325 	return io_read32(base + REGION_SETUP_HIGH_OFF(region));
326 }
327 
328 #define	REGION_MAX	16
329 void tzc_dump_state(void)
330 {
331 	uint32_t n;
332 	uint32_t temp_32reg, temp_32reg_h;
333 
334 	DMSG("TZC380 configuration:");
335 	DMSG("security_inversion_en %x",
336 	     io_read32(tzc.base + SECURITY_INV_EN_OFF));
337 	for (n = 0; n <= REGION_MAX; n++) {
338 		temp_32reg = tzc_read_region_attributes(tzc.base, n);
339 		if (!(temp_32reg & TZC_ATTR_REGION_EN_MASK))
340 			continue;
341 
342 		DMSG("");
343 		DMSG("region %d", n);
344 		temp_32reg = tzc_read_region_base_low(tzc.base, n);
345 		temp_32reg_h = tzc_read_region_base_high(tzc.base, n);
346 		DMSG("region_base: 0x%08x%08x", temp_32reg_h, temp_32reg);
347 		temp_32reg = tzc_read_region_attributes(tzc.base, n);
348 		DMSG("region sp: %x", temp_32reg >> TZC_ATTR_SP_SHIFT);
349 		DMSG("region size: %x", (temp_32reg & TZC_REGION_SIZE_MASK) >>
350 				TZC_REGION_SIZE_SHIFT);
351 	}
352 	DMSG("Lockdown select: %"PRIx32,
353 	     io_read32(tzc.base + LOCKDOWN_SELECT_OFF));
354 	DMSG("Lockdown range: %"PRIx32,
355 	     io_read32(tzc.base + LOCKDOWN_RANGE_OFF));
356 	DMSG("Action register: %"PRIx32, tzc_get_action());
357 	DMSG("exit");
358 }
359 
360 #endif /* CFG_TRACE_LEVEL >= TRACE_DEBUG */
361