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
tzc_read_build_config(vaddr_t base)55 static uint32_t tzc_read_build_config(vaddr_t base)
56 {
57 return io_read32(base + BUILD_CONFIG_OFF);
58 }
59
tzc_write_action(vaddr_t base,enum tzc_action action)60 static void tzc_write_action(vaddr_t base, enum tzc_action action)
61 {
62 io_write32(base + ACTION_OFF, action);
63 }
64
tzc_read_action(vaddr_t base)65 static uint32_t tzc_read_action(vaddr_t base)
66 {
67 return io_read32(base + ACTION_OFF);
68 }
69
tzc_write_region_base_low(vaddr_t base,uint32_t region,uint32_t val)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
tzc_write_region_base_high(vaddr_t base,uint32_t region,uint32_t val)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
tzc_read_region_attributes(vaddr_t base,uint32_t region)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
tzc_write_region_attributes(vaddr_t base,uint32_t region,uint32_t val)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
tzc_init(vaddr_t base)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 */
tzc_security_inversion_en(vaddr_t base)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 */
tzc_region_enable(uint8_t region)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 */
tzc_fail_dump(void)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 TZC400_REG_SIZE);
144
145 EMSG("Fail address Low 0x%" PRIx32,
146 io_read32(base + FAIL_ADDRESS_LOW_OFF));
147 EMSG("Fail address High 0x%" PRIx32,
148 io_read32(base + FAIL_ADDRESS_HIGH_OFF));
149 EMSG("Fail Control 0x%" PRIx32, io_read32(base + FAIL_CONTROL_OFF));
150 EMSG("Fail Id 0x%" PRIx32, io_read32(base + FAIL_ID));
151 }
152
tzc_int_clear(void)153 void tzc_int_clear(void)
154 {
155 vaddr_t base = core_mmu_get_va(tzc.base, MEM_AREA_IO_SEC,
156 TZC400_REG_SIZE);
157
158 io_write32(base + INT_CLEAR, 0);
159 }
160
addr_low(vaddr_t addr)161 static uint32_t addr_low(vaddr_t addr)
162 {
163 return (uint32_t)addr;
164 }
165
addr_high(vaddr_t addr __maybe_unused)166 static uint32_t addr_high(vaddr_t addr __maybe_unused)
167 {
168 #if (UINTPTR_MAX == UINT64_MAX)
169 return addr >> 32;
170 #else
171 return 0;
172 #endif
173 }
174
175
176 /*
177 * `tzc_configure_region` is used to program regions into the TrustZone
178 * controller.
179 */
tzc_configure_region(uint8_t region,vaddr_t region_base,uint32_t attr)180 void tzc_configure_region(uint8_t region, vaddr_t region_base, uint32_t attr)
181 {
182 assert(tzc.base);
183
184 assert(region < tzc.num_regions);
185
186 /*
187 * For region 0, this high/low/size/en field is Read Only (RO).
188 * So should not configure those field for region 0.
189 */
190 if (region) {
191 tzc_write_region_base_low(tzc.base, region,
192 addr_low(region_base));
193 tzc_write_region_base_high(tzc.base, region,
194 addr_high(region_base));
195 tzc_write_region_attributes(tzc.base, region, attr);
196 } else {
197 tzc_write_region_attributes(tzc.base, region,
198 attr & TZC_ATTR_SP_MASK);
199 }
200 }
201
tzc_set_action(enum tzc_action action)202 void tzc_set_action(enum tzc_action action)
203 {
204 assert(tzc.base);
205
206 /*
207 * - Currently no handler is provided to trap an error via interrupt
208 * or exception.
209 * - The interrupt action has not been tested.
210 */
211 tzc_write_action(tzc.base, action);
212 }
213
tzc_get_action(void)214 uint32_t tzc_get_action(void)
215 {
216 assert(tzc.base);
217
218 return tzc_read_action(tzc.base);
219 }
220
tzc_auto_configure(vaddr_t addr,vaddr_t size,uint32_t attr,uint8_t region)221 int tzc_auto_configure(vaddr_t addr, vaddr_t size, uint32_t attr,
222 uint8_t region)
223 {
224 uint64_t sub_region_size = 0;
225 uint64_t area = 0;
226 uint8_t lregion = region;
227 uint64_t region_size = 0;
228 vaddr_t sub_address = 0;
229 vaddr_t address = addr;
230 uint64_t lsize = size;
231 uint32_t mask = 0;
232 int i = 0;
233 uint8_t pow = 0;
234
235 assert(tzc.base);
236
237 /*
238 * TZC380 RM
239 * For region_attributes_<n> registers, region_size:
240 * Note: The AXI address width, that is AXI_ADDRESS_MSB+1, controls the
241 * upper limit value of the field.
242 */
243 pow = tzc.addr_width;
244
245 while (lsize != 0 && pow > 15) {
246 region_size = 1ULL << pow;
247
248 /* Case region fits alignment and covers requested area */
249 if ((address % region_size == 0) &&
250 ((address + lsize) % region_size == 0)) {
251 tzc_configure_region(lregion, address,
252 TZC_ATTR_REGION_SIZE(pow - 1) |
253 TZC_ATTR_REGION_EN_MASK |
254 attr);
255 lregion++;
256 address += region_size;
257 lsize -= region_size;
258 pow = tzc.addr_width;
259 continue;
260 }
261
262 /* Cover area using several subregions */
263 sub_region_size = region_size / 8;
264 if (address % sub_region_size == 0 &&
265 lsize > 2 * sub_region_size) {
266 sub_address = (address / region_size) * region_size;
267 mask = 0;
268 for (i = 0; i < 8; i++) {
269 area = (i + 1) * sub_region_size;
270 if (sub_address + area <= address ||
271 sub_address + area > address + lsize) {
272 mask |= TZC_ATTR_SUBREGION_DIS(i);
273 } else {
274 address += sub_region_size;
275 lsize -= sub_region_size;
276 }
277 }
278 tzc_configure_region(lregion, sub_address,
279 TZC_ATTR_REGION_SIZE(pow - 1) |
280 TZC_ATTR_REGION_EN_MASK |
281 mask | attr);
282 lregion++;
283 pow = tzc.addr_width;
284 continue;
285 }
286 pow--;
287 }
288 assert(lsize == 0);
289 assert(address == addr + size);
290 return lregion;
291 }
292
293 /*
294 * `region_lockdown` is used to lockdown the TZC380 configuration to prevent
295 * unintended overwrites of the configuration. Returns TEE_ERROR_SECURITY in
296 * case the lockdown fails.
297 */
tzc_regions_lockdown(void)298 TEE_Result tzc_regions_lockdown(void)
299 {
300 uint32_t val = 0;
301 uint32_t check = 0;
302
303 val = LOCKDOWN_RANGE_ENABLE | tzc.num_regions;
304 io_write32(tzc.base + LOCKDOWN_RANGE_OFF, val);
305 check = io_read32(tzc.base + LOCKDOWN_RANGE_OFF);
306 if (check != val)
307 return TEE_ERROR_SECURITY;
308
309 val = LOCKDOWN_SELECT_RANGE_ENABLE;
310 io_write32(tzc.base + LOCKDOWN_SELECT_OFF, val);
311 check = io_read32(tzc.base + LOCKDOWN_SELECT_OFF);
312 if (check != val)
313 return TEE_ERROR_SECURITY;
314
315 return TEE_SUCCESS;
316 }
317
318 #if TRACE_LEVEL >= TRACE_DEBUG
319
tzc_read_region_base_low(vaddr_t base,uint32_t region)320 static uint32_t tzc_read_region_base_low(vaddr_t base, uint32_t region)
321 {
322 return io_read32(base + REGION_SETUP_LOW_OFF(region));
323 }
324
tzc_read_region_base_high(vaddr_t base,uint32_t region)325 static uint32_t tzc_read_region_base_high(vaddr_t base, uint32_t region)
326 {
327 return io_read32(base + REGION_SETUP_HIGH_OFF(region));
328 }
329
330 #define REGION_MAX 16
tzc_dump_state(void)331 void tzc_dump_state(void)
332 {
333 uint32_t n;
334 uint32_t temp_32reg, temp_32reg_h;
335
336 DMSG("TZC380 configuration:");
337 DMSG("security_inversion_en %x",
338 io_read32(tzc.base + SECURITY_INV_EN_OFF));
339 for (n = 0; n <= REGION_MAX; n++) {
340 temp_32reg = tzc_read_region_attributes(tzc.base, n);
341 if (!(temp_32reg & TZC_ATTR_REGION_EN_MASK))
342 continue;
343
344 DMSG("");
345 DMSG("region %d", n);
346 temp_32reg = tzc_read_region_base_low(tzc.base, n);
347 temp_32reg_h = tzc_read_region_base_high(tzc.base, n);
348 DMSG("region_base: 0x%08x%08x", temp_32reg_h, temp_32reg);
349 temp_32reg = tzc_read_region_attributes(tzc.base, n);
350 DMSG("region sp: %x", temp_32reg >> TZC_ATTR_SP_SHIFT);
351 DMSG("region size: %x", (temp_32reg & TZC_REGION_SIZE_MASK) >>
352 TZC_REGION_SIZE_SHIFT);
353 }
354 DMSG("Lockdown select: %"PRIx32,
355 io_read32(tzc.base + LOCKDOWN_SELECT_OFF));
356 DMSG("Lockdown range: %"PRIx32,
357 io_read32(tzc.base + LOCKDOWN_RANGE_OFF));
358 DMSG("Action register: %"PRIx32, tzc_get_action());
359 DMSG("exit");
360 }
361
362 #endif /* CFG_TRACE_LEVEL >= TRACE_DEBUG */
363