tzc400.c 9.34 KB
Newer Older
Harry Liebel's avatar
Harry Liebel committed
1
/*
2
 * Copyright (c) 2014-2015, ARM Limited and Contributors. All rights reserved.
Harry Liebel's avatar
Harry Liebel committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of ARM nor the names of its contributors may be used
 * to endorse or promote products derived from this software without specific
 * prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <assert.h>
32
#include <debug.h>
33
34
35
#include <mmio.h>
#include <stddef.h>
#include <tzc400.h>
Harry Liebel's avatar
Harry Liebel committed
36

37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/*
 * Implementation defined values used to validate inputs later.
 * Filters : max of 4 ; 0 to 3
 * Regions : max of 9 ; 0 to 8
 * Address width : Values between 32 to 64
 */
typedef struct tzc_instance {
	uint64_t base;
	uint8_t addr_width;
	uint8_t num_filters;
	uint8_t num_regions;
} tzc_instance_t;

tzc_instance_t tzc;


static inline uint32_t tzc_read_build_config(uint64_t base)
Harry Liebel's avatar
Harry Liebel committed
54
55
56
57
{
	return mmio_read_32(base + BUILD_CONFIG_OFF);
}

58
static inline uint32_t tzc_read_gate_keeper(uint64_t base)
Harry Liebel's avatar
Harry Liebel committed
59
60
61
62
{
	return mmio_read_32(base + GATE_KEEPER_OFF);
}

63
static inline void tzc_write_gate_keeper(uint64_t base, uint32_t val)
Harry Liebel's avatar
Harry Liebel committed
64
65
66
67
{
	mmio_write_32(base + GATE_KEEPER_OFF, val);
}

68
static inline void tzc_write_action(uint64_t base, tzc_action_t action)
Harry Liebel's avatar
Harry Liebel committed
69
70
71
72
{
	mmio_write_32(base + ACTION_OFF, action);
}

73
74
75
static inline void tzc_write_region_base_low(uint64_t base,
					uint32_t region,
					uint32_t val)
Harry Liebel's avatar
Harry Liebel committed
76
{
77
78
	mmio_write_32(base + REGION_BASE_LOW_OFF +
		REGION_NUM_OFF(region), val);
Harry Liebel's avatar
Harry Liebel committed
79
80
}

81
82
83
static inline void tzc_write_region_base_high(uint64_t base,
					uint32_t region,
					uint32_t val)
Harry Liebel's avatar
Harry Liebel committed
84
{
85
86
	mmio_write_32(base + REGION_BASE_HIGH_OFF +
		REGION_NUM_OFF(region), val);
Harry Liebel's avatar
Harry Liebel committed
87
88
}

89
90
91
static inline void tzc_write_region_top_low(uint64_t base,
					uint32_t region,
					uint32_t val)
Harry Liebel's avatar
Harry Liebel committed
92
{
93
94
	mmio_write_32(base + REGION_TOP_LOW_OFF +
		REGION_NUM_OFF(region), val);
Harry Liebel's avatar
Harry Liebel committed
95
96
}

97
98
99
static inline void tzc_write_region_top_high(uint64_t base,
					uint32_t region,
					uint32_t val)
Harry Liebel's avatar
Harry Liebel committed
100
{
101
102
	mmio_write_32(base + REGION_TOP_HIGH_OFF +
		REGION_NUM_OFF(region), val);
Harry Liebel's avatar
Harry Liebel committed
103
104
}

105
106
107
static inline void tzc_write_region_attributes(uint64_t base,
					uint32_t region,
					uint32_t val)
Harry Liebel's avatar
Harry Liebel committed
108
{
109
110
	mmio_write_32(base + REGION_ATTRIBUTES_OFF +
		REGION_NUM_OFF(region), val);
Harry Liebel's avatar
Harry Liebel committed
111
112
}

113
114
115
static inline void tzc_write_region_id_access(uint64_t base,
					uint32_t region,
					uint32_t val)
Harry Liebel's avatar
Harry Liebel committed
116
{
117
118
	mmio_write_32(base + REGION_ID_ACCESS_OFF +
		REGION_NUM_OFF(region), val);
Harry Liebel's avatar
Harry Liebel committed
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
}

static uint32_t tzc_read_component_id(uint64_t base)
{
	uint32_t id;

	id = mmio_read_8(base + CID0_OFF);
	id |= (mmio_read_8(base + CID1_OFF) << 8);
	id |= (mmio_read_8(base + CID2_OFF) << 16);
	id |= (mmio_read_8(base + CID3_OFF) << 24);

	return id;
}

static uint32_t tzc_get_gate_keeper(uint64_t base, uint8_t filter)
{
	uint32_t tmp;

	tmp = (tzc_read_gate_keeper(base) >> GATE_KEEPER_OS_SHIFT) &
		GATE_KEEPER_OS_MASK;

140
	return (tmp >> filter) & GATE_KEEPER_FILTER_MASK;
Harry Liebel's avatar
Harry Liebel committed
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
}

/* This function is not MP safe. */
static void tzc_set_gate_keeper(uint64_t base, uint8_t filter, uint32_t val)
{
	uint32_t tmp;

	/* Upper half is current state. Lower half is requested state. */
	tmp = (tzc_read_gate_keeper(base) >> GATE_KEEPER_OS_SHIFT) &
		GATE_KEEPER_OS_MASK;

	if (val)
		tmp |=  (1 << filter);
	else
		tmp &= ~(1 << filter);

	tzc_write_gate_keeper(base, (tmp & GATE_KEEPER_OR_MASK) <<
			      GATE_KEEPER_OR_SHIFT);

	/* Wait here until we see the change reflected in the TZC status. */
	while (((tzc_read_gate_keeper(base) >> GATE_KEEPER_OS_SHIFT) &
		GATE_KEEPER_OS_MASK) != tmp)
	  ;
}


167
void tzc_init(uint64_t base)
Harry Liebel's avatar
Harry Liebel committed
168
169
170
{
	uint32_t tzc_id, tzc_build;

171
172
	assert(base);
	tzc.base = base;
Harry Liebel's avatar
Harry Liebel committed
173
174
175
176
177

	/*
	 * We expect to see a tzc400. Check component ID. The TZC-400 TRM shows
	 * component ID is expected to be "0xB105F00D".
	 */
178
	tzc_id = tzc_read_component_id(tzc.base);
Harry Liebel's avatar
Harry Liebel committed
179
180
181
182
183
184
	if (tzc_id != TZC400_COMPONENT_ID) {
		ERROR("TZC : Wrong device ID (0x%x).\n", tzc_id);
		panic();
	}

	/* Save values we will use later. */
185
186
	tzc_build = tzc_read_build_config(tzc.base);
	tzc.num_filters = ((tzc_build >> BUILD_CONFIG_NF_SHIFT) &
Harry Liebel's avatar
Harry Liebel committed
187
			   BUILD_CONFIG_NF_MASK) + 1;
188
	tzc.addr_width  = ((tzc_build >> BUILD_CONFIG_AW_SHIFT) &
Harry Liebel's avatar
Harry Liebel committed
189
			   BUILD_CONFIG_AW_MASK) + 1;
190
	tzc.num_regions = ((tzc_build >> BUILD_CONFIG_NR_SHIFT) &
Harry Liebel's avatar
Harry Liebel committed
191
192
193
			   BUILD_CONFIG_NR_MASK) + 1;
}

194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
/*
 * `tzc_configure_region0` is used to program region 0 into the TrustZone
 * controller. Region 0 covers the whole address space that is not mapped
 * to any other region, and is enabled on all filters; this cannot be
 * changed. This function only changes the access permissions.
 */
void tzc_configure_region0(tzc_region_attributes_t sec_attr,
			   uint32_t ns_device_access)
{
	assert(tzc.base);

	VERBOSE("TZC : Configuring region 0 (sec_attr=0x%x, ns_devs=0x%x)\n",
		sec_attr, ns_device_access);

	assert(sec_attr <= TZC_REGION_S_RDWR);

	/* Set secure attributes on region 0 */
	tzc_write_region_attributes(tzc.base, 0,
		sec_attr << REG_ATTR_SEC_SHIFT);

	/*
	 * Specify which non-secure devices have permission to access
	 * region 0.
	 */
	tzc_write_region_id_access(tzc.base, 0, ns_device_access);
}

Harry Liebel's avatar
Harry Liebel committed
221
222
223
224
225
226

/*
 * `tzc_configure_region` is used to program regions into the TrustZone
 * controller. A region can be associated with more than one filter. The
 * associated filters are passed in as a bitmap (bit0 = filter0).
 * NOTE:
227
228
 * Region 0 is special; it is preferable to use tzc_configure_region0
 * for this region (see comment for that function).
Harry Liebel's avatar
Harry Liebel committed
229
 */
230
void tzc_configure_region(uint32_t filters,
Harry Liebel's avatar
Harry Liebel committed
231
232
233
			  uint8_t  region,
			  uint64_t region_base,
			  uint64_t region_top,
234
			  tzc_region_attributes_t sec_attr,
Harry Liebel's avatar
Harry Liebel committed
235
236
			  uint32_t ns_device_access)
{
237
	assert(tzc.base);
Harry Liebel's avatar
Harry Liebel committed
238

239
240
241
242
243
244
245
	VERBOSE("TZC : Configuring region (filters=0x%x, region=%d, ...\n",
		filters, region);
	VERBOSE("TZC : ... base=0x%lx, top=0x%lx, ...\n",
		region_base, region_top);
	VERBOSE("TZC : ... sec_attr=0x%x, ns_devs=0x%x)\n",
		sec_attr, ns_device_access);

Harry Liebel's avatar
Harry Liebel committed
246
	/* Do range checks on filters and regions. */
247
248
	assert(((filters >> tzc.num_filters) == 0) &&
	       (region < tzc.num_regions));
Harry Liebel's avatar
Harry Liebel committed
249
250
251
252
253

	/*
	 * Do address range check based on TZC configuration. A 64bit address is
	 * the max and expected case.
	 */
254
255
	assert(((region_top <= (UINT64_MAX >> (64 - tzc.addr_width))) &&
		(region_base < region_top)));
Harry Liebel's avatar
Harry Liebel committed
256
257
258
259
260
261
262
263
264
265
266

	/* region_base and (region_top + 1) must be 4KB aligned */
	assert(((region_base | (region_top + 1)) & (4096 - 1)) == 0);

	assert(sec_attr <= TZC_REGION_S_RDWR);

	/*
	 * Inputs look ok, start programming registers.
	 * All the address registers are 32 bits wide and have a LOW and HIGH
	 * component used to construct a up to a 64bit address.
	 */
267
268
269
270
	tzc_write_region_base_low(tzc.base, region,
				(uint32_t)(region_base));
	tzc_write_region_base_high(tzc.base, region,
				(uint32_t)(region_base >> 32));
Harry Liebel's avatar
Harry Liebel committed
271

272
273
274
275
	tzc_write_region_top_low(tzc.base, region,
				(uint32_t)(region_top));
	tzc_write_region_top_high(tzc.base, region,
				(uint32_t)(region_top >> 32));
Harry Liebel's avatar
Harry Liebel committed
276
277

	/* Assign the region to a filter and set secure attributes */
278
	tzc_write_region_attributes(tzc.base, region,
279
		(sec_attr << REG_ATTR_SEC_SHIFT) | filters);
Harry Liebel's avatar
Harry Liebel committed
280
281
282
283
284

	/*
	 * Specify which non-secure devices have permission to access this
	 * region.
	 */
285
	tzc_write_region_id_access(tzc.base, region, ns_device_access);
Harry Liebel's avatar
Harry Liebel committed
286
287
288
}


289
void tzc_set_action(tzc_action_t action)
Harry Liebel's avatar
Harry Liebel committed
290
{
291
	assert(tzc.base);
Harry Liebel's avatar
Harry Liebel committed
292
293
294
295
296
297

	/*
	 * - Currently no handler is provided to trap an error via interrupt
	 *   or exception.
	 * - The interrupt action has not been tested.
	 */
298
	tzc_write_action(tzc.base, action);
Harry Liebel's avatar
Harry Liebel committed
299
300
301
}


302
void tzc_enable_filters(void)
Harry Liebel's avatar
Harry Liebel committed
303
304
305
306
{
	uint32_t state;
	uint32_t filter;

307
	assert(tzc.base);
Harry Liebel's avatar
Harry Liebel committed
308

309
310
	for (filter = 0; filter < tzc.num_filters; filter++) {
		state = tzc_get_gate_keeper(tzc.base, filter);
Harry Liebel's avatar
Harry Liebel committed
311
		if (state) {
312
313
314
315
316
317
318
			/* The TZC filter is already configured. Changing the
			 * programmer's view in an active system can cause
			 * unpredictable behavior therefore panic for now rather
			 * than try to determine whether this is safe in this
			 * instance. See:
			 * http://infocenter.arm.com/help/index.jsp?\
			 * topic=/com.arm.doc.ddi0504c/CJHHECBF.html */
Harry Liebel's avatar
Harry Liebel committed
319
320
321
322
			ERROR("TZC : Filter %d Gatekeeper already enabled.\n",
				filter);
			panic();
		}
323
		tzc_set_gate_keeper(tzc.base, filter, 1);
Harry Liebel's avatar
Harry Liebel committed
324
325
326
327
	}
}


328
void tzc_disable_filters(void)
Harry Liebel's avatar
Harry Liebel committed
329
330
331
{
	uint32_t filter;

332
	assert(tzc.base);
Harry Liebel's avatar
Harry Liebel committed
333
334
335
336
337

	/*
	 * We don't do the same state check as above as the Gatekeepers are
	 * disabled after reset.
	 */
338
339
	for (filter = 0; filter < tzc.num_filters; filter++)
		tzc_set_gate_keeper(tzc.base, filter, 0);
Harry Liebel's avatar
Harry Liebel committed
340
}