xref: /arm-trusted-firmware/drivers/arm/tzc/tzc400.c (revision 91f16700b400a8c0651d24a598fc48ee2997a0d7)
1*91f16700Schasinglulu /*
2*91f16700Schasinglulu  * Copyright (c) 2016-2022, ARM Limited and Contributors. All rights reserved.
3*91f16700Schasinglulu  *
4*91f16700Schasinglulu  * SPDX-License-Identifier: BSD-3-Clause
5*91f16700Schasinglulu  */
6*91f16700Schasinglulu 
7*91f16700Schasinglulu #include <assert.h>
8*91f16700Schasinglulu #include <stddef.h>
9*91f16700Schasinglulu 
10*91f16700Schasinglulu #include <common/debug.h>
11*91f16700Schasinglulu #include <drivers/arm/tzc400.h>
12*91f16700Schasinglulu #include <lib/mmio.h>
13*91f16700Schasinglulu #include <lib/utils_def.h>
14*91f16700Schasinglulu 
15*91f16700Schasinglulu #include "tzc_common_private.h"
16*91f16700Schasinglulu 
17*91f16700Schasinglulu /*
18*91f16700Schasinglulu  * Macros which will be used by common core functions.
19*91f16700Schasinglulu  */
20*91f16700Schasinglulu #define TZC_400_REGION_BASE_LOW_0_OFFSET	U(0x100)
21*91f16700Schasinglulu #define TZC_400_REGION_BASE_HIGH_0_OFFSET	U(0x104)
22*91f16700Schasinglulu #define TZC_400_REGION_TOP_LOW_0_OFFSET		U(0x108)
23*91f16700Schasinglulu #define TZC_400_REGION_TOP_HIGH_0_OFFSET	U(0x10c)
24*91f16700Schasinglulu #define TZC_400_REGION_ATTR_0_OFFSET		U(0x110)
25*91f16700Schasinglulu #define TZC_400_REGION_ID_ACCESS_0_OFFSET	U(0x114)
26*91f16700Schasinglulu 
27*91f16700Schasinglulu /*
28*91f16700Schasinglulu  * Implementation defined values used to validate inputs later.
29*91f16700Schasinglulu  * Filters : max of 4 ; 0 to 3
30*91f16700Schasinglulu  * Regions : max of 9 ; 0 to 8
31*91f16700Schasinglulu  * Address width : Values between 32 to 64
32*91f16700Schasinglulu  */
33*91f16700Schasinglulu typedef struct tzc400_instance {
34*91f16700Schasinglulu 	uintptr_t base;
35*91f16700Schasinglulu 	uint8_t addr_width;
36*91f16700Schasinglulu 	uint8_t num_filters;
37*91f16700Schasinglulu 	uint8_t num_regions;
38*91f16700Schasinglulu } tzc400_instance_t;
39*91f16700Schasinglulu 
40*91f16700Schasinglulu static tzc400_instance_t tzc400;
41*91f16700Schasinglulu 
42*91f16700Schasinglulu static inline unsigned int _tzc400_read_build_config(uintptr_t base)
43*91f16700Schasinglulu {
44*91f16700Schasinglulu 	return mmio_read_32(base + BUILD_CONFIG_OFF);
45*91f16700Schasinglulu }
46*91f16700Schasinglulu 
47*91f16700Schasinglulu static inline unsigned int _tzc400_read_gate_keeper(uintptr_t base)
48*91f16700Schasinglulu {
49*91f16700Schasinglulu 	return mmio_read_32(base + GATE_KEEPER_OFF);
50*91f16700Schasinglulu }
51*91f16700Schasinglulu 
52*91f16700Schasinglulu static inline void _tzc400_write_gate_keeper(uintptr_t base, unsigned int val)
53*91f16700Schasinglulu {
54*91f16700Schasinglulu 	mmio_write_32(base + GATE_KEEPER_OFF, val);
55*91f16700Schasinglulu }
56*91f16700Schasinglulu 
57*91f16700Schasinglulu /*
58*91f16700Schasinglulu  * Get the open status information for all filter units.
59*91f16700Schasinglulu  */
60*91f16700Schasinglulu #define get_gate_keeper_os(_base)	((_tzc400_read_gate_keeper(_base) >>  \
61*91f16700Schasinglulu 					GATE_KEEPER_OS_SHIFT) &		\
62*91f16700Schasinglulu 					GATE_KEEPER_OS_MASK)
63*91f16700Schasinglulu 
64*91f16700Schasinglulu 
65*91f16700Schasinglulu /* Define common core functions used across different TZC peripherals. */
66*91f16700Schasinglulu DEFINE_TZC_COMMON_WRITE_ACTION(400, 400)
67*91f16700Schasinglulu DEFINE_TZC_COMMON_WRITE_REGION_BASE(400, 400)
68*91f16700Schasinglulu DEFINE_TZC_COMMON_WRITE_REGION_TOP(400, 400)
69*91f16700Schasinglulu DEFINE_TZC_COMMON_WRITE_REGION_ATTRIBUTES(400, 400)
70*91f16700Schasinglulu DEFINE_TZC_COMMON_WRITE_REGION_ID_ACCESS(400, 400)
71*91f16700Schasinglulu DEFINE_TZC_COMMON_UPDATE_FILTERS(400, 400)
72*91f16700Schasinglulu DEFINE_TZC_COMMON_CONFIGURE_REGION0(400)
73*91f16700Schasinglulu DEFINE_TZC_COMMON_CONFIGURE_REGION(400)
74*91f16700Schasinglulu 
75*91f16700Schasinglulu static void _tzc400_clear_it(uintptr_t base, uint32_t filter)
76*91f16700Schasinglulu {
77*91f16700Schasinglulu 	mmio_write_32(base + INT_CLEAR, BIT_32(filter));
78*91f16700Schasinglulu }
79*91f16700Schasinglulu 
80*91f16700Schasinglulu static uint32_t _tzc400_get_int_by_filter(uintptr_t base, uint32_t filter)
81*91f16700Schasinglulu {
82*91f16700Schasinglulu 	return mmio_read_32(base + INT_STATUS) & BIT_32(filter);
83*91f16700Schasinglulu }
84*91f16700Schasinglulu 
85*91f16700Schasinglulu #if DEBUG
86*91f16700Schasinglulu static unsigned long _tzc400_get_fail_address(uintptr_t base, uint32_t filter)
87*91f16700Schasinglulu {
88*91f16700Schasinglulu 	unsigned long fail_address;
89*91f16700Schasinglulu 
90*91f16700Schasinglulu 	fail_address = mmio_read_32(base + FAIL_ADDRESS_LOW_OFF +
91*91f16700Schasinglulu 				    (filter * FILTER_OFFSET));
92*91f16700Schasinglulu #ifdef __aarch64__
93*91f16700Schasinglulu 	fail_address += (unsigned long)mmio_read_32(base + FAIL_ADDRESS_HIGH_OFF +
94*91f16700Schasinglulu 						    (filter * FILTER_OFFSET)) << 32;
95*91f16700Schasinglulu #endif
96*91f16700Schasinglulu 
97*91f16700Schasinglulu 	return fail_address;
98*91f16700Schasinglulu }
99*91f16700Schasinglulu 
100*91f16700Schasinglulu static uint32_t _tzc400_get_fail_id(uintptr_t base, uint32_t filter)
101*91f16700Schasinglulu {
102*91f16700Schasinglulu 	return mmio_read_32(base + FAIL_ID + (filter * FILTER_OFFSET));
103*91f16700Schasinglulu }
104*91f16700Schasinglulu 
105*91f16700Schasinglulu static uint32_t _tzc400_get_fail_control(uintptr_t base, uint32_t filter)
106*91f16700Schasinglulu {
107*91f16700Schasinglulu 	return mmio_read_32(base + FAIL_CONTROL_OFF + (filter * FILTER_OFFSET));
108*91f16700Schasinglulu }
109*91f16700Schasinglulu 
110*91f16700Schasinglulu static void _tzc400_dump_fail_filter(uintptr_t base, uint32_t filter)
111*91f16700Schasinglulu {
112*91f16700Schasinglulu 	uint32_t control_fail;
113*91f16700Schasinglulu 	uint32_t fail_id;
114*91f16700Schasinglulu 	unsigned long address_fail;
115*91f16700Schasinglulu 
116*91f16700Schasinglulu 	address_fail = _tzc400_get_fail_address(base, filter);
117*91f16700Schasinglulu 	ERROR("Illegal access to 0x%lx:\n", address_fail);
118*91f16700Schasinglulu 
119*91f16700Schasinglulu 	fail_id = _tzc400_get_fail_id(base, filter);
120*91f16700Schasinglulu 	ERROR("\tFAIL_ID = 0x%x\n", fail_id);
121*91f16700Schasinglulu 
122*91f16700Schasinglulu 	control_fail = _tzc400_get_fail_control(base, filter);
123*91f16700Schasinglulu 	if (((control_fail & BIT_32(FAIL_CONTROL_NS_SHIFT)) >> FAIL_CONTROL_NS_SHIFT) ==
124*91f16700Schasinglulu 	    FAIL_CONTROL_NS_NONSECURE) {
125*91f16700Schasinglulu 		ERROR("\tNon-Secure\n");
126*91f16700Schasinglulu 	} else {
127*91f16700Schasinglulu 		ERROR("\tSecure\n");
128*91f16700Schasinglulu 	}
129*91f16700Schasinglulu 
130*91f16700Schasinglulu 	if (((control_fail & BIT_32(FAIL_CONTROL_PRIV_SHIFT)) >> FAIL_CONTROL_PRIV_SHIFT) ==
131*91f16700Schasinglulu 	    FAIL_CONTROL_PRIV_PRIV) {
132*91f16700Schasinglulu 		ERROR("\tPrivilege\n");
133*91f16700Schasinglulu 	} else {
134*91f16700Schasinglulu 		ERROR("\tUnprivilege\n");
135*91f16700Schasinglulu 	}
136*91f16700Schasinglulu 
137*91f16700Schasinglulu 	if (((control_fail & BIT_32(FAIL_CONTROL_DIR_SHIFT)) >> FAIL_CONTROL_DIR_SHIFT) ==
138*91f16700Schasinglulu 	    FAIL_CONTROL_DIR_WRITE) {
139*91f16700Schasinglulu 		ERROR("\tWrite\n");
140*91f16700Schasinglulu 	} else {
141*91f16700Schasinglulu 		ERROR("\tRead\n");
142*91f16700Schasinglulu 	}
143*91f16700Schasinglulu }
144*91f16700Schasinglulu #endif /* DEBUG */
145*91f16700Schasinglulu 
146*91f16700Schasinglulu static unsigned int _tzc400_get_gate_keeper(uintptr_t base,
147*91f16700Schasinglulu 				unsigned int filter)
148*91f16700Schasinglulu {
149*91f16700Schasinglulu 	unsigned int open_status;
150*91f16700Schasinglulu 
151*91f16700Schasinglulu 	open_status = get_gate_keeper_os(base);
152*91f16700Schasinglulu 
153*91f16700Schasinglulu 	return (open_status >> filter) & GATE_KEEPER_FILTER_MASK;
154*91f16700Schasinglulu }
155*91f16700Schasinglulu 
156*91f16700Schasinglulu /* This function is not MP safe. */
157*91f16700Schasinglulu static void _tzc400_set_gate_keeper(uintptr_t base,
158*91f16700Schasinglulu 				unsigned int filter,
159*91f16700Schasinglulu 				int val)
160*91f16700Schasinglulu {
161*91f16700Schasinglulu 	unsigned int open_status;
162*91f16700Schasinglulu 
163*91f16700Schasinglulu 	/* Upper half is current state. Lower half is requested state. */
164*91f16700Schasinglulu 	open_status = get_gate_keeper_os(base);
165*91f16700Schasinglulu 
166*91f16700Schasinglulu 	if (val != 0)
167*91f16700Schasinglulu 		open_status |=  (1UL << filter);
168*91f16700Schasinglulu 	else
169*91f16700Schasinglulu 		open_status &= ~(1UL << filter);
170*91f16700Schasinglulu 
171*91f16700Schasinglulu 	_tzc400_write_gate_keeper(base, (open_status & GATE_KEEPER_OR_MASK) <<
172*91f16700Schasinglulu 			      GATE_KEEPER_OR_SHIFT);
173*91f16700Schasinglulu 
174*91f16700Schasinglulu 	/* Wait here until we see the change reflected in the TZC status. */
175*91f16700Schasinglulu 	while ((get_gate_keeper_os(base)) != open_status)
176*91f16700Schasinglulu 		;
177*91f16700Schasinglulu }
178*91f16700Schasinglulu 
179*91f16700Schasinglulu void tzc400_set_action(unsigned int action)
180*91f16700Schasinglulu {
181*91f16700Schasinglulu 	assert(tzc400.base != 0U);
182*91f16700Schasinglulu 	assert(action <= TZC_ACTION_ERR_INT);
183*91f16700Schasinglulu 
184*91f16700Schasinglulu 	_tzc400_write_action(tzc400.base, action);
185*91f16700Schasinglulu }
186*91f16700Schasinglulu 
187*91f16700Schasinglulu void tzc400_init(uintptr_t base)
188*91f16700Schasinglulu {
189*91f16700Schasinglulu #if DEBUG
190*91f16700Schasinglulu 	unsigned int tzc400_id;
191*91f16700Schasinglulu #endif
192*91f16700Schasinglulu 	unsigned int tzc400_build;
193*91f16700Schasinglulu 
194*91f16700Schasinglulu 	assert(base != 0U);
195*91f16700Schasinglulu 	tzc400.base = base;
196*91f16700Schasinglulu 
197*91f16700Schasinglulu #if DEBUG
198*91f16700Schasinglulu 	tzc400_id = _tzc_read_peripheral_id(base);
199*91f16700Schasinglulu 	if (tzc400_id != TZC_400_PERIPHERAL_ID) {
200*91f16700Schasinglulu 		ERROR("TZC-400 : Wrong device ID (0x%x).\n", tzc400_id);
201*91f16700Schasinglulu 		panic();
202*91f16700Schasinglulu 	}
203*91f16700Schasinglulu #endif
204*91f16700Schasinglulu 
205*91f16700Schasinglulu 	/* Save values we will use later. */
206*91f16700Schasinglulu 	tzc400_build = _tzc400_read_build_config(tzc400.base);
207*91f16700Schasinglulu 	tzc400.num_filters = (uint8_t)((tzc400_build >> BUILD_CONFIG_NF_SHIFT) &
208*91f16700Schasinglulu 					BUILD_CONFIG_NF_MASK) + 1U;
209*91f16700Schasinglulu 	tzc400.addr_width  = (uint8_t)((tzc400_build >> BUILD_CONFIG_AW_SHIFT) &
210*91f16700Schasinglulu 					BUILD_CONFIG_AW_MASK) + 1U;
211*91f16700Schasinglulu 	tzc400.num_regions = (uint8_t)((tzc400_build >> BUILD_CONFIG_NR_SHIFT) &
212*91f16700Schasinglulu 					BUILD_CONFIG_NR_MASK) + 1U;
213*91f16700Schasinglulu }
214*91f16700Schasinglulu 
215*91f16700Schasinglulu /*
216*91f16700Schasinglulu  * `tzc400_configure_region0` is used to program region 0 into the TrustZone
217*91f16700Schasinglulu  * controller. Region 0 covers the whole address space that is not mapped
218*91f16700Schasinglulu  * to any other region, and is enabled on all filters; this cannot be
219*91f16700Schasinglulu  * changed. This function only changes the access permissions.
220*91f16700Schasinglulu  */
221*91f16700Schasinglulu void tzc400_configure_region0(unsigned int sec_attr,
222*91f16700Schasinglulu 			   unsigned int ns_device_access)
223*91f16700Schasinglulu {
224*91f16700Schasinglulu 	assert(tzc400.base != 0U);
225*91f16700Schasinglulu 	assert(sec_attr <= TZC_REGION_S_RDWR);
226*91f16700Schasinglulu 
227*91f16700Schasinglulu 	_tzc400_configure_region0(tzc400.base, sec_attr, ns_device_access);
228*91f16700Schasinglulu }
229*91f16700Schasinglulu 
230*91f16700Schasinglulu /*
231*91f16700Schasinglulu  * `tzc400_configure_region` is used to program regions into the TrustZone
232*91f16700Schasinglulu  * controller. A region can be associated with more than one filter. The
233*91f16700Schasinglulu  * associated filters are passed in as a bitmap (bit0 = filter0), except that
234*91f16700Schasinglulu  * the value TZC_400_REGION_ATTR_FILTER_BIT_ALL selects all filters, based on
235*91f16700Schasinglulu  * the value of tzc400.num_filters.
236*91f16700Schasinglulu  * NOTE:
237*91f16700Schasinglulu  * Region 0 is special; it is preferable to use tzc400_configure_region0
238*91f16700Schasinglulu  * for this region (see comment for that function).
239*91f16700Schasinglulu  */
240*91f16700Schasinglulu void tzc400_configure_region(unsigned int filters,
241*91f16700Schasinglulu 			  unsigned int region,
242*91f16700Schasinglulu 			  unsigned long long region_base,
243*91f16700Schasinglulu 			  unsigned long long region_top,
244*91f16700Schasinglulu 			  unsigned int sec_attr,
245*91f16700Schasinglulu 			  unsigned int nsaid_permissions)
246*91f16700Schasinglulu {
247*91f16700Schasinglulu 	assert(tzc400.base != 0U);
248*91f16700Schasinglulu 
249*91f16700Schasinglulu 	/* Adjust filter mask by real filter number */
250*91f16700Schasinglulu 	if (filters == TZC_400_REGION_ATTR_FILTER_BIT_ALL) {
251*91f16700Schasinglulu 		filters = (1U << tzc400.num_filters) - 1U;
252*91f16700Schasinglulu 	}
253*91f16700Schasinglulu 
254*91f16700Schasinglulu 	/* Do range checks on filters and regions. */
255*91f16700Schasinglulu 	assert(((filters >> tzc400.num_filters) == 0U) &&
256*91f16700Schasinglulu 	       (region < tzc400.num_regions));
257*91f16700Schasinglulu 
258*91f16700Schasinglulu 	/*
259*91f16700Schasinglulu 	 * Do address range check based on TZC configuration. A 64bit address is
260*91f16700Schasinglulu 	 * the max and expected case.
261*91f16700Schasinglulu 	 */
262*91f16700Schasinglulu 	assert((region_top <= (UINT64_MAX >> (64U - tzc400.addr_width))) &&
263*91f16700Schasinglulu 		(region_base < region_top));
264*91f16700Schasinglulu 
265*91f16700Schasinglulu 	/* region_base and (region_top + 1) must be 4KB aligned */
266*91f16700Schasinglulu 	assert(((region_base | (region_top + 1U)) & (4096U - 1U)) == 0U);
267*91f16700Schasinglulu 
268*91f16700Schasinglulu 	assert(sec_attr <= TZC_REGION_S_RDWR);
269*91f16700Schasinglulu 
270*91f16700Schasinglulu 	_tzc400_configure_region(tzc400.base, filters, region, region_base,
271*91f16700Schasinglulu 						region_top,
272*91f16700Schasinglulu 						sec_attr, nsaid_permissions);
273*91f16700Schasinglulu }
274*91f16700Schasinglulu 
275*91f16700Schasinglulu void tzc400_update_filters(unsigned int region, unsigned int filters)
276*91f16700Schasinglulu {
277*91f16700Schasinglulu 	/* Do range checks on filters and regions. */
278*91f16700Schasinglulu 	assert(((filters >> tzc400.num_filters) == 0U) &&
279*91f16700Schasinglulu 	       (region < tzc400.num_regions));
280*91f16700Schasinglulu 
281*91f16700Schasinglulu 	_tzc400_update_filters(tzc400.base, region, tzc400.num_filters, filters);
282*91f16700Schasinglulu }
283*91f16700Schasinglulu 
284*91f16700Schasinglulu void tzc400_enable_filters(void)
285*91f16700Schasinglulu {
286*91f16700Schasinglulu 	unsigned int state;
287*91f16700Schasinglulu 	unsigned int filter;
288*91f16700Schasinglulu 
289*91f16700Schasinglulu 	assert(tzc400.base != 0U);
290*91f16700Schasinglulu 
291*91f16700Schasinglulu 	for (filter = 0U; filter < tzc400.num_filters; filter++) {
292*91f16700Schasinglulu 		state = _tzc400_get_gate_keeper(tzc400.base, filter);
293*91f16700Schasinglulu 		if (state != 0U) {
294*91f16700Schasinglulu 			/* Filter 0 is special and cannot be disabled.
295*91f16700Schasinglulu 			 * So here we allow it being already enabled. */
296*91f16700Schasinglulu 			if (filter == 0U) {
297*91f16700Schasinglulu 				continue;
298*91f16700Schasinglulu 			}
299*91f16700Schasinglulu 			/*
300*91f16700Schasinglulu 			 * The TZC filter is already configured. Changing the
301*91f16700Schasinglulu 			 * programmer's view in an active system can cause
302*91f16700Schasinglulu 			 * unpredictable behavior therefore panic for now rather
303*91f16700Schasinglulu 			 * than try to determine whether this is safe in this
304*91f16700Schasinglulu 			 * instance.
305*91f16700Schasinglulu 			 *
306*91f16700Schasinglulu 			 * See the 'ARM (R) CoreLink TM TZC-400 TrustZone (R)
307*91f16700Schasinglulu 			 * Address Space Controller' Technical Reference Manual.
308*91f16700Schasinglulu 			 */
309*91f16700Schasinglulu 			ERROR("TZC-400 : Filter %u Gatekeeper already enabled.\n",
310*91f16700Schasinglulu 			      filter);
311*91f16700Schasinglulu 			panic();
312*91f16700Schasinglulu 		}
313*91f16700Schasinglulu 		_tzc400_set_gate_keeper(tzc400.base, filter, 1);
314*91f16700Schasinglulu 	}
315*91f16700Schasinglulu }
316*91f16700Schasinglulu 
317*91f16700Schasinglulu void tzc400_disable_filters(void)
318*91f16700Schasinglulu {
319*91f16700Schasinglulu 	unsigned int filter;
320*91f16700Schasinglulu 	unsigned int state;
321*91f16700Schasinglulu 	unsigned int start = 0U;
322*91f16700Schasinglulu 
323*91f16700Schasinglulu 	assert(tzc400.base != 0U);
324*91f16700Schasinglulu 
325*91f16700Schasinglulu 	/* Filter 0 is special and cannot be disabled. */
326*91f16700Schasinglulu 	state = _tzc400_get_gate_keeper(tzc400.base, 0);
327*91f16700Schasinglulu 	if (state != 0U) {
328*91f16700Schasinglulu 		start++;
329*91f16700Schasinglulu 	}
330*91f16700Schasinglulu 	for (filter = start; filter < tzc400.num_filters; filter++)
331*91f16700Schasinglulu 		_tzc400_set_gate_keeper(tzc400.base, filter, 0);
332*91f16700Schasinglulu }
333*91f16700Schasinglulu 
334*91f16700Schasinglulu int tzc400_it_handler(void)
335*91f16700Schasinglulu {
336*91f16700Schasinglulu 	uint32_t filter;
337*91f16700Schasinglulu 	uint32_t filter_it_pending = tzc400.num_filters;
338*91f16700Schasinglulu 
339*91f16700Schasinglulu 	assert(tzc400.base != 0U);
340*91f16700Schasinglulu 
341*91f16700Schasinglulu 	for (filter = 0U; filter < tzc400.num_filters; filter++) {
342*91f16700Schasinglulu 		if (_tzc400_get_int_by_filter(tzc400.base, filter) != 0U) {
343*91f16700Schasinglulu 			filter_it_pending = filter;
344*91f16700Schasinglulu 			break;
345*91f16700Schasinglulu 		}
346*91f16700Schasinglulu 	}
347*91f16700Schasinglulu 
348*91f16700Schasinglulu 	if (filter_it_pending == tzc400.num_filters) {
349*91f16700Schasinglulu 		ERROR("TZC-400: No interrupt pending!\n");
350*91f16700Schasinglulu 		return -1;
351*91f16700Schasinglulu 	}
352*91f16700Schasinglulu 
353*91f16700Schasinglulu #if DEBUG
354*91f16700Schasinglulu 	_tzc400_dump_fail_filter(tzc400.base, filter_it_pending);
355*91f16700Schasinglulu #endif
356*91f16700Schasinglulu 
357*91f16700Schasinglulu 	_tzc400_clear_it(tzc400.base, filter_it_pending);
358*91f16700Schasinglulu 
359*91f16700Schasinglulu 	return 0;
360*91f16700Schasinglulu }
361