xref: /optee_os/core/drivers/tzc380.c (revision b99a4a1850c2ce661156ebc25f48d47efa8a41c1)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright 2017 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 = TZC380_POW;
232 
233 	while (lsize != 0 && pow > 15) {
234 		region_size = 1ULL << pow;
235 
236 		/* Case region fits alignment and covers requested area */
237 		if ((address % region_size == 0) &&
238 		    ((address + lsize) % region_size == 0)) {
239 			tzc_configure_region(lregion, address,
240 					     TZC_ATTR_REGION_SIZE(pow - 1) |
241 					     TZC_ATTR_REGION_EN_MASK |
242 					     attr);
243 			lregion++;
244 			address += region_size;
245 			lsize -= region_size;
246 			pow--;
247 			continue;
248 		}
249 
250 		/* Cover area using several subregions */
251 		sub_region_size = region_size / 8;
252 		if (address % sub_region_size == 0 &&
253 		    lsize > 2 * sub_region_size) {
254 			sub_address = (address / region_size) * region_size;
255 			mask = 0;
256 			for (i = 0; i < 8; i++) {
257 				area = (i + 1) * sub_region_size;
258 				if (sub_address + area <= address ||
259 				    sub_address + area > address + lsize) {
260 					mask |= TZC_ATTR_SUBREGION_DIS(i);
261 				} else {
262 					address += sub_region_size;
263 					lsize -= sub_region_size;
264 				}
265 			}
266 			tzc_configure_region(lregion, sub_address,
267 					     TZC_ATTR_REGION_SIZE(pow - 1) |
268 					     TZC_ATTR_REGION_EN_MASK |
269 					     mask | attr);
270 			lregion++;
271 			pow--;
272 			continue;
273 		}
274 		pow--;
275 	}
276 	assert(lsize == 0);
277 	assert(address == addr + size);
278 	return lregion;
279 }
280 
281 #if TRACE_LEVEL >= TRACE_DEBUG
282 
283 static uint32_t tzc_read_region_base_low(vaddr_t base, uint32_t region)
284 {
285 	return io_read32(base + REGION_SETUP_LOW_OFF(region));
286 }
287 
288 static uint32_t tzc_read_region_base_high(vaddr_t base, uint32_t region)
289 {
290 	return io_read32(base + REGION_SETUP_HIGH_OFF(region));
291 }
292 
293 #define	REGION_MAX	16
294 void tzc_dump_state(void)
295 {
296 	uint32_t n;
297 	uint32_t temp_32reg, temp_32reg_h;
298 
299 	DMSG("enter");
300 	DMSG("security_inversion_en %x",
301 	     io_read32(tzc.base + SECURITY_INV_EN_OFF));
302 	for (n = 0; n <= REGION_MAX; n++) {
303 		temp_32reg = tzc_read_region_attributes(tzc.base, n);
304 		if (!(temp_32reg & TZC_ATTR_REGION_EN_MASK))
305 			continue;
306 
307 		DMSG("");
308 		DMSG("region %d", n);
309 		temp_32reg = tzc_read_region_base_low(tzc.base, n);
310 		temp_32reg_h = tzc_read_region_base_high(tzc.base, n);
311 		DMSG("region_base: 0x%08x%08x", temp_32reg_h, temp_32reg);
312 		temp_32reg = tzc_read_region_attributes(tzc.base, n);
313 		DMSG("region sp: %x", temp_32reg >> TZC_ATTR_SP_SHIFT);
314 		DMSG("region size: %x", (temp_32reg & TZC_REGION_SIZE_MASK) >>
315 				TZC_REGION_SIZE_SHIFT);
316 	}
317 	DMSG("exit");
318 }
319 
320 #endif /* CFG_TRACE_LEVEL >= TRACE_DEBUG */
321