aboutsummaryrefslogtreecommitdiff
path: root/core/drivers/stm32_etzpc.c
blob: 61dd6a4daa909796bb4140b4f5da48de2dfded02 (plain)
1
2
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
// SPDX-License-Identifier: BSD-3-Clause
/*
 * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
 * Copyright (c) 2017-2019, STMicroelectronics
 */

/*
 * STM32 ETPZC acts as a firewall on stm32mp SoC peripheral interfaces and
 * internal memories. The driver expects a single instance of the controller
 * in the platform.
 *
 * The driver API is defined in header file stm32_etzpc.h.
 *
 * Driver registers a PM callback for restoration of the access permissions
 * when it resumes.
 */

#include <assert.h>
#include <drivers/stm32_etzpc.h>
#include <kernel/dt.h>
#include <kernel/generic_boot.h>
#include <initcall.h>
#include <io.h>
#include <keep.h>
#include <kernel/panic.h>
#include <kernel/pm.h>
#include <mm/core_memprot.h>
#include <util.h>

#ifdef CFG_DT
#include <libfdt.h>
#endif

/* Devicetree compatibulity */
#define ETZPC_COMPAT			"st,stm32-etzpc"

/* ID Registers */
#define ETZPC_TZMA0_SIZE		0x000U
#define ETZPC_DECPROT0			0x010U
#define ETZPC_DECPROT_LOCK0		0x030U
#define ETZPC_HWCFGR			0x3F0U
#define ETZPC_VERR			0x3F4U

/* ID Registers fields */
#define ETZPC_TZMA0_SIZE_LOCK		BIT(31)
#define ETZPC_DECPROT0_MASK		GENMASK_32(1, 0)
#define ETZPC_HWCFGR_NUM_TZMA_MASK	GENMASK_32(7, 0)
#define ETZPC_HWCFGR_NUM_TZMA_SHIFT	0
#define ETZPC_HWCFGR_NUM_PER_SEC_MASK	GENMASK_32(15, 8)
#define ETZPC_HWCFGR_NUM_PER_SEC_SHIFT	8
#define ETZPC_HWCFGR_NUM_AHB_SEC_MASK	GENMASK_32(23, 16)
#define ETZPC_HWCFGR_NUM_AHB_SEC_SHIFT	16
#define ETZPC_HWCFGR_CHUNCKS1N4_MASK	GENMASK_32(31, 24)
#define ETZPC_HWCFGR_CHUNCKS1N4_SHIFT	24

#define DECPROT_SHIFT			1
#define IDS_PER_DECPROT_REGS		16U
#define IDS_PER_DECPROT_LOCK_REGS	32U

/*
 * Implementation uses uint8_t to store each securable DECPROT configuration
 * and uint16_t to store each securable TZMA configuration. When resuming
 * from deep suspend, the DECPROT configurations are restored.
 */
#define PERIPH_PM_LOCK_BIT		BIT(7)
#define PERIPH_PM_ATTR_MASK		GENMASK_32(2, 0)
#define TZMA_PM_LOCK_BIT		BIT(15)
#define TZMA_PM_VALUE_MASK		GENMASK_32(9, 0)

/*
 * @base - iobase for interface base address
 * @num_tzma - number of TZMA zone, read from the hardware
 * @num_ahb_sec - number of securable AHB master zone, read from the hardware
 * @num_per_sec - number of securable AHB & APB periphs, read from the hardware
 * @periph_cfg - Backup for restoring DECPROT when resuming (PERIH_PM_*)
 * @tzma_cfg - Backup for restoring TZMA when resuming (TZMA_PM_*)
 */
struct etzpc_instance {
	struct io_pa_va base;
	unsigned int num_tzma;
	unsigned int num_per_sec;
	unsigned int num_ahb_sec;
	uint8_t *periph_cfg;
	uint16_t *tzma_cfg;
};

/* Only 1 instance of the ETZPC is expected per platform */
static struct etzpc_instance etzpc_dev;

static vaddr_t etzpc_base(void)
{
	return io_pa_or_va(&etzpc_dev.base);
}

static bool __maybe_unused valid_decprot_id(unsigned int id)
{
	return id < etzpc_dev.num_per_sec;
}

static bool __maybe_unused valid_tzma_id(unsigned int id)
{
	return id < etzpc_dev.num_tzma;
}

void etzpc_configure_decprot(uint32_t decprot_id,
			     enum etzpc_decprot_attributes decprot_attr)
{
	size_t offset = 4U * (decprot_id / IDS_PER_DECPROT_REGS);
	uint32_t shift = (decprot_id % IDS_PER_DECPROT_REGS) << DECPROT_SHIFT;
	uint32_t masked_decprot = (uint32_t)decprot_attr & ETZPC_DECPROT0_MASK;
	vaddr_t base = etzpc_base();

	assert(valid_decprot_id(decprot_id));

	io_clrsetbits32(base + ETZPC_DECPROT0 + offset,
			ETZPC_DECPROT0_MASK << shift,
			masked_decprot << shift);

	/* Save for PM */
	assert((decprot_attr & ~PERIPH_PM_ATTR_MASK) == 0);
	COMPILE_TIME_ASSERT(ETZPC_DECPROT_MAX <= UINT8_MAX);

	etzpc_dev.periph_cfg[decprot_id] &= ~PERIPH_PM_ATTR_MASK;
	etzpc_dev.periph_cfg[decprot_id] |= (uint8_t)decprot_attr;
}

enum etzpc_decprot_attributes etzpc_get_decprot(uint32_t decprot_id)
{
	size_t offset = 4U * (decprot_id / IDS_PER_DECPROT_REGS);
	uint32_t shift = (decprot_id % IDS_PER_DECPROT_REGS) << DECPROT_SHIFT;
	vaddr_t base = etzpc_base();
	uint32_t value = 0;

	assert(valid_decprot_id(decprot_id));

	value = (io_read32(base + ETZPC_DECPROT0 + offset) >> shift) &
		ETZPC_DECPROT0_MASK;

	return (enum etzpc_decprot_attributes)value;
}

void etzpc_lock_decprot(uint32_t decprot_id)
{
	size_t offset = 4U * (decprot_id / IDS_PER_DECPROT_LOCK_REGS);
	uint32_t mask = BIT(decprot_id % IDS_PER_DECPROT_LOCK_REGS);
	vaddr_t base = etzpc_base();

	assert(valid_decprot_id(decprot_id));

	io_write32(base + offset + ETZPC_DECPROT_LOCK0, mask);

	/* Save for PM */
	etzpc_dev.periph_cfg[decprot_id] |= PERIPH_PM_LOCK_BIT;
}

bool etzpc_get_lock_decprot(uint32_t decprot_id)
{
	size_t offset = 4U * (decprot_id / IDS_PER_DECPROT_LOCK_REGS);
	uint32_t mask = BIT(decprot_id % IDS_PER_DECPROT_LOCK_REGS);
	vaddr_t base = etzpc_base();

	assert(valid_decprot_id(decprot_id));

	return io_read32(base + offset + ETZPC_DECPROT_LOCK0) & mask;
}

void etzpc_configure_tzma(uint32_t tzma_id, uint16_t tzma_value)
{
	size_t offset = sizeof(uint32_t) * tzma_id;
	vaddr_t base = etzpc_base();

	assert(valid_tzma_id(tzma_id));

	io_write32(base + ETZPC_TZMA0_SIZE + offset, tzma_value);

	/* Save for PM */
	assert((tzma_value & ~TZMA_PM_VALUE_MASK) == 0);
	etzpc_dev.tzma_cfg[tzma_id] &= ~TZMA_PM_VALUE_MASK;
	etzpc_dev.tzma_cfg[tzma_id] |= tzma_value;
}

uint16_t etzpc_get_tzma(uint32_t tzma_id)
{
	size_t offset = sizeof(uint32_t) * tzma_id;
	vaddr_t base = etzpc_base();

	assert(valid_tzma_id(tzma_id));

	return io_read32(base + ETZPC_TZMA0_SIZE + offset);
}

void etzpc_lock_tzma(uint32_t tzma_id)
{
	size_t offset = sizeof(uint32_t) * tzma_id;
	vaddr_t base = etzpc_base();

	assert(valid_tzma_id(tzma_id));

	io_setbits32(base + ETZPC_TZMA0_SIZE + offset, ETZPC_TZMA0_SIZE_LOCK);

	/* Save for PM */
	etzpc_dev.tzma_cfg[tzma_id] |= TZMA_PM_LOCK_BIT;
}

bool etzpc_get_lock_tzma(uint32_t tzma_id)
{
	size_t offset = sizeof(uint32_t) * tzma_id;
	vaddr_t base = etzpc_base();

	assert(valid_tzma_id(tzma_id));

	return io_read32(base + ETZPC_TZMA0_SIZE + offset) &
	       ETZPC_TZMA0_SIZE_LOCK;
}

static TEE_Result etzpc_pm(enum pm_op op, unsigned int pm_hint __unused,
			  const struct pm_callback_handle *pm_handle)
{
	struct etzpc_instance *dev = NULL;
	unsigned int n = 0;

	if (op != PM_OP_RESUME)
		return TEE_SUCCESS;

	dev = (struct etzpc_instance *)PM_CALLBACK_GET_HANDLE(pm_handle);

	for (n = 0; n < dev->num_per_sec; n++) {
		unsigned int attr = dev->periph_cfg[n] & PERIPH_PM_ATTR_MASK;

		etzpc_configure_decprot(n, (enum etzpc_decprot_attributes)attr);

		if (dev->periph_cfg[n] & PERIPH_PM_LOCK_BIT)
			etzpc_lock_decprot(n);
	}

	for (n = 0; n < dev->num_tzma; n++) {
		uint16_t value = dev->tzma_cfg[n] & TZMA_PM_VALUE_MASK;

		etzpc_configure_tzma(n, value);

		if (dev->tzma_cfg[n] & TZMA_PM_LOCK_BIT)
			etzpc_lock_tzma(n);
	}

	return TEE_SUCCESS;
}
KEEP_PAGER(etzpc_pm);

static void init_pm(struct etzpc_instance *dev)
{
	unsigned int n = 0;

	dev->periph_cfg = calloc(dev->num_per_sec, sizeof(*dev->periph_cfg));
	dev->tzma_cfg = calloc(dev->num_tzma, sizeof(*dev->tzma_cfg));
	if (!dev->periph_cfg || !dev->tzma_cfg)
		panic();

	for (n = 0; n < dev->num_per_sec; n++) {
		dev->periph_cfg[n] = (uint8_t)etzpc_get_decprot(n);
		if (etzpc_get_lock_decprot(n))
			dev->periph_cfg[n] |= PERIPH_PM_LOCK_BIT;
	}

	for (n = 0; n < dev->num_ahb_sec; n++) {
		dev->tzma_cfg[n] = (uint8_t)etzpc_get_tzma(n);
		if (etzpc_get_lock_tzma(n))
			dev->tzma_cfg[n] |= TZMA_PM_LOCK_BIT;
	}

	register_pm_driver_cb(etzpc_pm, dev);
}

struct etzpc_hwcfg {
	unsigned int num_tzma;
	unsigned int num_per_sec;
	unsigned int num_ahb_sec;
	unsigned int chunk_size;
};

static void get_hwcfg(struct etzpc_hwcfg *hwcfg)
{
	uint32_t reg = io_read32(etzpc_base() + ETZPC_HWCFGR);

	hwcfg->num_tzma = (reg & ETZPC_HWCFGR_NUM_TZMA_MASK) >>
			  ETZPC_HWCFGR_NUM_TZMA_SHIFT;
	hwcfg->num_per_sec = (reg & ETZPC_HWCFGR_NUM_PER_SEC_MASK) >>
			     ETZPC_HWCFGR_NUM_PER_SEC_SHIFT;
	hwcfg->num_ahb_sec = (reg & ETZPC_HWCFGR_NUM_AHB_SEC_MASK) >>
			     ETZPC_HWCFGR_NUM_AHB_SEC_SHIFT;
	hwcfg->chunk_size = (reg & ETZPC_HWCFGR_CHUNCKS1N4_MASK) >>
			    ETZPC_HWCFGR_CHUNCKS1N4_SHIFT;
}

static void init_devive_from_hw_config(struct etzpc_instance *dev,
					      paddr_t pbase)
{
	struct etzpc_hwcfg hwcfg = { };

	assert(!dev->base.pa && cpu_mmu_enabled());
	dev->base.pa = pbase;
	dev->base.va = (vaddr_t)phys_to_virt(dev->base.pa, MEM_AREA_IO_SEC);
	assert(etzpc_base());

	get_hwcfg(&hwcfg);
	dev->num_tzma = hwcfg.num_tzma;
	dev->num_per_sec = hwcfg.num_per_sec;
	dev->num_ahb_sec = hwcfg.num_ahb_sec;

	DMSG("ETZPC revison 0x02%" PRIu8 ", per_sec %u, ahb_sec %u, tzma %u",
	     io_read8(etzpc_base() + ETZPC_VERR),
	     hwcfg.num_per_sec, hwcfg.num_ahb_sec, hwcfg.num_tzma);

	init_pm(dev);
}

void stm32_etzpc_init(paddr_t base)
{
	init_devive_from_hw_config(&etzpc_dev, base);
}

#ifdef CFG_DT
static TEE_Result init_etzpc_from_dt(void)
{
	void *fdt = get_embedded_dt();
	int node = fdt_node_offset_by_compatible(fdt, -1, ETZPC_COMPAT);
	int status = 0;
	paddr_t pbase = 0;

	/* When using DT, expect one and only one instance, secure enabled */

	if (node < 0)
		panic();
	assert(fdt_node_offset_by_compatible(fdt, node, ETZPC_COMPAT) < 0);

	status = _fdt_get_status(fdt, node);
	if (!(status & DT_STATUS_OK_SEC))
		panic();

	pbase = _fdt_reg_base_address(fdt, node);
	if (pbase == (paddr_t)-1)
		panic();

	init_devive_from_hw_config(&etzpc_dev, pbase);

	return TEE_SUCCESS;
}

driver_init(init_etzpc_from_dt);
#endif /*CFG_DT*/