1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Simple API for configuring TrustZone memory restrictions for TZC400
4  */
5 
6 #define LOG_CATEGORY LOGC_ARCH
7 
8 #include <linux/iopoll.h>
9 #include <mach/tzc.h>
10 
11 #define TZC_TIMEOUT_US		100
12 
13 #define TZC_BUILD_CONFIG	0x00
14 #define TZC_ACTION		0x04
15 #define TZC_ACTION_NONE		0
16 #define TZC_ACTION_ERR		1
17 #define TZC_ACTION_INT		2
18 #define TZC_ACTION_INT_ERR	3
19 #define TZC_GATE_KEEPER		0x08
20 
21 #define TZC_REGION0_OFFSET	0x100
22 #define TZC_REGION_CFG_SIZE	0x20
23 #define TZC_REGION1_OFFSET	0x120
24 #define TZC_REGION_BASE		0x00
25 #define TZC_REGION_TOP		0x08
26 #define TZC_REGION_ATTRIBUTE	0x10
27 #define TZC_REGION_ACCESS	0x14
28 
tzc_read(uintptr_t tzc,size_t reg)29 static uint32_t tzc_read(uintptr_t tzc, size_t reg)
30 {
31 	return readl(tzc + reg);
32 }
33 
tzc_write(uintptr_t tzc,size_t reg,uint32_t val)34 static void tzc_write(uintptr_t tzc, size_t reg, uint32_t val)
35 {
36 	writel(val, tzc + reg);
37 }
38 
tzc_config_get_active_filters(const struct tzc_region * cfg)39 static uint16_t tzc_config_get_active_filters(const struct tzc_region *cfg)
40 {
41 	uint16_t active_filters = 0;
42 
43 	for ( ; cfg->top != 0; cfg++)
44 		active_filters |= cfg->filters_mask;
45 
46 	return active_filters;
47 }
48 
tzc_configure(uintptr_t tzc,const struct tzc_region * cfg)49 int tzc_configure(uintptr_t tzc, const struct tzc_region *cfg)
50 {
51 	uintptr_t region = tzc + TZC_REGION1_OFFSET;
52 	uint32_t nsid, attr_reg, active_filters;
53 	int ret;
54 
55 	active_filters = tzc_config_get_active_filters(cfg);
56 	if (active_filters == 0)
57 		return -EINVAL;
58 
59 	ret = tzc_disable_filters(tzc, active_filters);
60 	if (ret < 0)
61 		return ret;
62 
63 	for ( ; cfg->top != 0; cfg++, region += TZC_REGION_CFG_SIZE) {
64 		attr_reg = (cfg->sec_mode & 0x03) << 30;
65 		attr_reg |= (cfg->filters_mask & 0x03) << 0;
66 		nsid = cfg->nsec_id & 0xffff;
67 		nsid |= nsid << 16;
68 
69 		tzc_write(region, TZC_REGION_BASE, cfg->base);
70 		tzc_write(region, TZC_REGION_TOP, cfg->top);
71 		tzc_write(region, TZC_REGION_ACCESS, nsid);
72 		tzc_write(region, TZC_REGION_ATTRIBUTE, attr_reg);
73 	}
74 
75 	tzc_write(tzc, TZC_ACTION, TZC_ACTION_ERR);
76 	return tzc_enable_filters(tzc, active_filters);
77 }
78 
tzc_disable_filters(uintptr_t tzc,uint16_t filters_mask)79 int tzc_disable_filters(uintptr_t tzc, uint16_t filters_mask)
80 {
81 	uint32_t gate = tzc_read(tzc, TZC_GATE_KEEPER);
82 	uint32_t filter_status = filters_mask << 16;
83 
84 	gate &= ~filters_mask;
85 	tzc_write(tzc, TZC_GATE_KEEPER, gate);
86 
87 	return readl_poll_timeout(tzc + TZC_GATE_KEEPER, gate,
88 				 (gate & filter_status) == 0, TZC_TIMEOUT_US);
89 }
90 
tzc_enable_filters(uintptr_t tzc,uint16_t filters_mask)91 int tzc_enable_filters(uintptr_t tzc, uint16_t filters_mask)
92 {
93 	uint32_t gate = tzc_read(tzc, TZC_GATE_KEEPER);
94 	uint32_t filter_status = filters_mask << 16;
95 
96 	gate |= filters_mask;
97 	tzc_write(tzc, TZC_GATE_KEEPER, gate);
98 
99 	return readl_poll_timeout(tzc + TZC_GATE_KEEPER, gate,
100 				 (gate & filter_status) == filter_status,
101 				 TZC_TIMEOUT_US);
102 }
103 
sec_access_str_from_attr(uint32_t attr)104 static const char *sec_access_str_from_attr(uint32_t attr)
105 {
106 	const char *const sec_mode[] = { "none", "RO  ", "WO  ", "RW  " };
107 
108 	return sec_mode[(attr >> 30) & 0x03];
109 }
110 
tzc_dump_config(uintptr_t tzc)111 void tzc_dump_config(uintptr_t tzc)
112 {
113 	uint32_t build_config, base, top, attr, nsaid;
114 	int num_regions, i;
115 	uintptr_t region;
116 
117 	build_config = tzc_read(tzc, TZC_BUILD_CONFIG);
118 	num_regions = ((build_config >> 0) & 0x1f) + 1;
119 
120 	for (i = 0; i < num_regions; i++) {
121 		region = tzc + TZC_REGION0_OFFSET + i * TZC_REGION_CFG_SIZE;
122 
123 		base = tzc_read(region, TZC_REGION_BASE);
124 		top = tzc_read(region, TZC_REGION_TOP);
125 		attr = tzc_read(region, TZC_REGION_ATTRIBUTE);
126 		nsaid = tzc_read(region, TZC_REGION_ACCESS);
127 
128 		if (attr == 0 && nsaid == 0)
129 			continue;
130 
131 		log_info("TZC region %u: %08x->%08x - filters 0x%x\n",
132 			 i, base, top, (attr >> 0) & 0xf);
133 		log_info("\t Secure access %s NSAID %08x\n",
134 			 sec_access_str_from_attr(attr), nsaid);
135 	}
136 }
137