summaryrefslogtreecommitdiff
path: root/arch/arm/mach-apple/sart.c
blob: e9b017ad570b8d8089859ac43c348f6fcfd77d01 (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
// SPDX-License-Identifier: MIT
/*
 * The sart code is copied from m1n1 (https://github.com/AsahiLinux/m1n1) and
 * licensed as MIT.
 *
 * (C) Copyright 2022 The Asahi Linux Contributors
 */

#include <asm/io.h>
#include <asm/arch/sart.h>

#include <linux/bitfield.h>
#include <linux/types.h>

#include <malloc.h>

#define APPLE_SART_MAX_ENTRIES 16

/* This is probably a bitfield but the exact meaning of each bit is unknown. */
#define APPLE_SART_FLAGS_ALLOW 0xff

/* SARTv2 registers */
#define APPLE_SART2_CONFIG(idx)       (0x00 + 4 * (idx))
#define APPLE_SART2_CONFIG_FLAGS      GENMASK(31, 24)
#define APPLE_SART2_CONFIG_SIZE       GENMASK(23, 0)
#define APPLE_SART2_CONFIG_SIZE_SHIFT 12
#define APPLE_SART2_CONFIG_SIZE_MAX   GENMASK(23, 0)

#define APPLE_SART2_PADDR(idx)  (0x40 + 4 * (idx))
#define APPLE_SART2_PADDR_SHIFT 12

/* SARTv3 registers */
#define APPLE_SART3_CONFIG(idx) (0x00 + 4 * (idx))

#define APPLE_SART3_PADDR(idx)  (0x40 + 4 * (idx))
#define APPLE_SART3_PADDR_SHIFT 12

#define APPLE_SART3_SIZE(idx)  (0x80 + 4 * (idx))
#define APPLE_SART3_SIZE_SHIFT 12
#define APPLE_SART3_SIZE_MAX   GENMASK(29, 0)

struct apple_sart {
	uintptr_t base;
	u32 protected_entries;

	void (*get_entry)(struct apple_sart *sart, int index, u8 *flags, void **paddr,
			  size_t *size);
	bool (*set_entry)(struct apple_sart *sart, int index, u8 flags, void *paddr,
			  size_t size);
};

static void sart2_get_entry(struct apple_sart *sart, int index, u8 *flags, void **paddr,
			    size_t *size)
{
	u32 cfg = readl(sart->base + APPLE_SART2_CONFIG(index));
	*flags = FIELD_GET(APPLE_SART2_CONFIG_FLAGS, cfg);
	*size = (size_t)FIELD_GET(APPLE_SART2_CONFIG_SIZE, cfg) << APPLE_SART2_CONFIG_SIZE_SHIFT;
	*paddr = (void *)
		((u64)readl(sart->base + APPLE_SART2_PADDR(index)) << APPLE_SART2_PADDR_SHIFT);
}

static bool sart2_set_entry(struct apple_sart *sart, int index, u8 flags, void *paddr_,
			    size_t size)
{
	u32 cfg;
	u64 paddr = (u64)paddr_;

	if (size & ((1 << APPLE_SART2_CONFIG_SIZE_SHIFT) - 1))
		return false;
	if (paddr & ((1 << APPLE_SART2_PADDR_SHIFT) - 1))
		return false;

	size >>= APPLE_SART2_CONFIG_SIZE_SHIFT;
	paddr >>= APPLE_SART2_PADDR_SHIFT;

	if (size > APPLE_SART2_CONFIG_SIZE_MAX)
		return false;

	cfg = FIELD_PREP(APPLE_SART2_CONFIG_FLAGS, flags);
	cfg |= FIELD_PREP(APPLE_SART2_CONFIG_SIZE, size);

	writel(paddr, sart->base + APPLE_SART2_PADDR(index));
	writel(cfg, sart->base + APPLE_SART2_CONFIG(index));

	return true;
}

static void sart3_get_entry(struct apple_sart *sart, int index, u8 *flags, void **paddr,
			    size_t *size)
{
	*flags = readl(sart->base + APPLE_SART3_CONFIG(index));
	*size = (size_t)readl(sart->base + APPLE_SART3_SIZE(index)) << APPLE_SART3_SIZE_SHIFT;
	*paddr = (void *)
		((u64)readl(sart->base + APPLE_SART3_PADDR(index)) << APPLE_SART3_PADDR_SHIFT);
}

static bool sart3_set_entry(struct apple_sart *sart, int index, u8 flags, void *paddr_,
			    size_t size)
{
	u64 paddr = (u64)paddr_;

	if (size & ((1 << APPLE_SART3_SIZE_SHIFT) - 1))
		return false;
	if (paddr & ((1 << APPLE_SART3_PADDR_SHIFT) - 1))
		return false;

	paddr >>= APPLE_SART3_PADDR_SHIFT;
	size >>= APPLE_SART3_SIZE_SHIFT;

	if (size > APPLE_SART3_SIZE_MAX)
		return false;

	writel(paddr, sart->base + APPLE_SART3_PADDR(index));
	writel(size, sart->base + APPLE_SART3_SIZE(index));
	writel(flags, sart->base + APPLE_SART3_CONFIG(index));

	return true;
}

struct apple_sart *sart_init(ofnode node)
{
	phys_addr_t base;
	u32 sart_version;
	struct apple_sart *sart;

	base = ofnode_get_addr(node);
	if (base == FDT_ADDR_T_NONE)
		return NULL;

	if (ofnode_device_is_compatible(node, "apple,t8103-sart")) {
		sart_version = 2;
	} else if (ofnode_device_is_compatible(node, "apple,t6000-sart")) {
		sart_version = 3;
	} else {
		printf("sart: unknown SART compatible: %sd\n",
		       ofnode_read_string(node, "compatible"));
		return NULL;
	}

	sart = calloc(sizeof(*sart), 1);
	if (!sart)
		return NULL;

	sart->base = base;

	switch (sart_version) {
	case 2:
		sart->get_entry = sart2_get_entry;
		sart->set_entry = sart2_set_entry;
		break;
	case 3:
		sart->get_entry = sart3_get_entry;
		sart->set_entry = sart3_set_entry;
		break;
	default:
		printf("sart: SART has unknown version %d\n", sart_version);
		free(sart);
		return NULL;
	}

	sart->protected_entries = 0;
	for (unsigned int i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) {
		void *paddr;
		u8 flags;
		size_t sz;

		sart->get_entry(sart, i, &flags, &paddr, &sz);
		if (flags)
			sart->protected_entries |= 1 << i;
	}

	return sart;
}

void sart_free(struct apple_sart *sart)
{
	for (unsigned int i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) {
		if (sart->protected_entries & (1 << i))
			continue;
		sart->set_entry(sart, i, 0, NULL, 0);
	}

	free(sart);
}

bool sart_add_allowed_region(struct apple_sart *sart, void *paddr, size_t sz)
{
	for (unsigned int i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) {
		void *e_paddr;
		u8 e_flags;
		size_t e_sz;

		if (sart->protected_entries & (1 << i))
			continue;

		sart->get_entry(sart, i, &e_flags, &e_paddr, &e_sz);
		if (e_flags)
			continue;

		return sart->set_entry(sart, i, APPLE_SART_FLAGS_ALLOW, paddr, sz);
	}

	printf("sart: no more free entries\n");
	return false;
}

bool sart_remove_allowed_region(struct apple_sart *sart, void *paddr, size_t sz)
{
	for (unsigned int i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) {
		void *e_paddr;
		u8 e_flags;
		size_t e_sz;

		if (sart->protected_entries & (1 << i))
			continue;

		sart->get_entry(sart, i, &e_flags, &e_paddr, &e_sz);
		if (!e_flags)
			continue;
		if (e_paddr != paddr)
			continue;
		if (e_sz != sz)
			continue;

		return sart->set_entry(sart, i, 0, NULL, 0);
	}

	printf("sart: could not find entry to be removed\n");
	return false;
}