summaryrefslogtreecommitdiff
path: root/arch/arm/mach-stm32mp/tzc400.c
blob: cdc4a40edafc2f936eaa89dc0776371641ff5f83 (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
// SPDX-License-Identifier: GPL-2.0+
/*
 * Simple API for configuring TrustZone memory restrictions for TZC400
 */

#define LOG_CATEGORY LOGC_ARCH

#include <linux/iopoll.h>
#include <mach/tzc.h>

#define TZC_TIMEOUT_US		100

#define TZC_BUILD_CONFIG	0x00
#define TZC_ACTION		0x04
#define TZC_ACTION_NONE		0
#define TZC_ACTION_ERR		1
#define TZC_ACTION_INT		2
#define TZC_ACTION_INT_ERR	3
#define TZC_GATE_KEEPER		0x08

#define TZC_REGION0_OFFSET	0x100
#define TZC_REGION_CFG_SIZE	0x20
#define TZC_REGION1_OFFSET	0x120
#define TZC_REGION_BASE		0x00
#define TZC_REGION_TOP		0x08
#define TZC_REGION_ATTRIBUTE	0x10
#define TZC_REGION_ACCESS	0x14

static uint32_t tzc_read(uintptr_t tzc, size_t reg)
{
	return readl(tzc + reg);
}

static void tzc_write(uintptr_t tzc, size_t reg, uint32_t val)
{
	writel(val, tzc + reg);
}

static uint16_t tzc_config_get_active_filters(const struct tzc_region *cfg)
{
	uint16_t active_filters = 0;

	for ( ; cfg->top != 0; cfg++)
		active_filters |= cfg->filters_mask;

	return active_filters;
}

int tzc_configure(uintptr_t tzc, const struct tzc_region *cfg)
{
	uintptr_t region = tzc + TZC_REGION1_OFFSET;
	uint32_t nsid, attr_reg, active_filters;
	int ret;

	active_filters = tzc_config_get_active_filters(cfg);
	if (active_filters == 0)
		return -EINVAL;

	ret = tzc_disable_filters(tzc, active_filters);
	if (ret < 0)
		return ret;

	for ( ; cfg->top != 0; cfg++, region += TZC_REGION_CFG_SIZE) {
		attr_reg = (cfg->sec_mode & 0x03) << 30;
		attr_reg |= (cfg->filters_mask & 0x03) << 0;
		nsid = cfg->nsec_id & 0xffff;
		nsid |= nsid << 16;

		tzc_write(region, TZC_REGION_BASE, cfg->base);
		tzc_write(region, TZC_REGION_TOP, cfg->top);
		tzc_write(region, TZC_REGION_ACCESS, nsid);
		tzc_write(region, TZC_REGION_ATTRIBUTE, attr_reg);
	}

	tzc_write(tzc, TZC_ACTION, TZC_ACTION_ERR);
	return tzc_enable_filters(tzc, active_filters);
}

int tzc_disable_filters(uintptr_t tzc, uint16_t filters_mask)
{
	uint32_t gate = tzc_read(tzc, TZC_GATE_KEEPER);
	uint32_t filter_status = filters_mask << 16;

	gate &= ~filters_mask;
	tzc_write(tzc, TZC_GATE_KEEPER, gate);

	return readl_poll_timeout(tzc + TZC_GATE_KEEPER, gate,
				 (gate & filter_status) == 0, TZC_TIMEOUT_US);
}

int tzc_enable_filters(uintptr_t tzc, uint16_t filters_mask)
{
	uint32_t gate = tzc_read(tzc, TZC_GATE_KEEPER);
	uint32_t filter_status = filters_mask << 16;

	gate |= filters_mask;
	tzc_write(tzc, TZC_GATE_KEEPER, gate);

	return readl_poll_timeout(tzc + TZC_GATE_KEEPER, gate,
				 (gate & filter_status) == filter_status,
				 TZC_TIMEOUT_US);
}

static const char *sec_access_str_from_attr(uint32_t attr)
{
	const char *const sec_mode[] = { "none", "RO  ", "WO  ", "RW  " };

	return sec_mode[(attr >> 30) & 0x03];
}

void tzc_dump_config(uintptr_t tzc)
{
	uint32_t build_config, base, top, attr, nsaid;
	int num_regions, i;
	uintptr_t region;

	build_config = tzc_read(tzc, TZC_BUILD_CONFIG);
	num_regions = ((build_config >> 0) & 0x1f) + 1;

	for (i = 0; i < num_regions; i++) {
		region = tzc + TZC_REGION0_OFFSET + i * TZC_REGION_CFG_SIZE;

		base = tzc_read(region, TZC_REGION_BASE);
		top = tzc_read(region, TZC_REGION_TOP);
		attr = tzc_read(region, TZC_REGION_ATTRIBUTE);
		nsaid = tzc_read(region, TZC_REGION_ACCESS);

		if (attr == 0 && nsaid == 0)
			continue;

		log_info("TZC region %u: %08x->%08x - filters 0x%x\n",
			 i, base, top, (attr >> 0) & 0xf);
		log_info("\t Secure access %s NSAID %08x\n",
			 sec_access_str_from_attr(attr), nsaid);
	}
}