summaryrefslogtreecommitdiff
path: root/arch/arm/mach-mediatek/tzcfg.c
blob: c8fe8ac0e9b5f137e164106cd5f09383e147e582 (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
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2024 MediaTek Inc.
 *
 * Author: Weijie Gao <weijie.gao@mediatek.com>
 */

#include <errno.h>
#include <stdio.h>
#include <asm/global_data.h>
#include <linux/kernel.h>
#include <linux/arm-smccc.h>
#include <linux/sizes.h>
#include <command.h>
#include <fdtdec.h>
#include <fdt_support.h>
#include <lmb.h>

DECLARE_GLOBAL_DATA_PTR;

#define MTK_SIP_GET_BL31_REGION		0x82000300
#define MTK_SIP_GET_BL32_REGION		0x82000301

#define BL31_DEFAULT_ADDR		0x43000000
#define BL31_DEFAULT_SIZE		0x80000

#define TZ_REGION_MAX_SIZE		SZ_16M
#define U_BOOT_MIN_SIZE			SZ_4M
#define U_BOOT_MIN_STACK_SIZE		SZ_1M
#define REGION_ALIGNMENT		SZ_64K

struct tz_reserved_region {
	phys_addr_t addr;
	phys_addr_t size;
};

static bool fix_tz_region(struct tz_reserved_region region[],
			  uint32_t used_regions)
{
	phys_addr_t size;

	if (region[0].addr + region[0].size > gd->ram_top) {
		if (region[0].addr >= gd->ram_top) {
			debug("Discarded region 0x%08llx, size 0x%llx\n",
			      region[0].addr, region[0].size);

			if (used_regions > 1)
				region[0] = region[1];

			return true;
		}

		size = gd->ram_top - region[0].addr;

		debug("Truncated region 0x%08llx, size 0x%llx -> 0x%llx\n",
		      region[0].addr, region[0].size, size);

		region[0].size = size;
	}

	return false;
}

phys_addr_t board_get_usable_ram_top(phys_size_t total_size)
{
	phys_addr_t uboot_ram_top, pstore_size, uboot_size = 0;
	struct tz_reserved_region region[2], tmp;
	phys_addr_t top_addr, low_addr;
	struct arm_smccc_res res;
	u32 used_regions = 1;

	/* BL31 region */
	arm_smccc_smc(MTK_SIP_GET_BL31_REGION, 0, 0, 0, 0, 0, 0, 0, &res);
	if (res.a0) {
		/* Assume PIE is not enabled for BL31 */
		region[0].addr = BL31_DEFAULT_ADDR;
		region[0].size = BL31_DEFAULT_SIZE;
	} else {
		region[0].addr = res.a1;
		region[0].size = res.a2;
	}

	debug("BL31 @ 0x%08llx, size 0x%llx\n", region[0].addr,
	      region[0].size);

	/* BL32 region is optional */
	arm_smccc_smc(MTK_SIP_GET_BL32_REGION, 0, 0, 0, 0, 0, 0, 0, &res);
	if (!res.a0 && res.a1 && res.a2) {
		region[used_regions].addr = res.a1;
		region[used_regions].size = res.a2;

		debug("BL32 @ 0x%08llx, size 0x%llx\n",
		      region[used_regions].addr, region[used_regions].size);

		used_regions++;
	}

	if (used_regions == 2) {
		if (region[0].addr < region[1].addr) {
			/* Make sure region[0] is higher than region[1] */
			tmp = region[0];
			region[0] = region[1];
			region[1] = tmp;
		}

		top_addr = region[0].addr + region[0].size;
		low_addr = min_t(phys_addr_t, region[0].addr, region[1].addr);

		if (top_addr - low_addr <= TZ_REGION_MAX_SIZE) {
			/* Merge region if they're overlapped or close enough */
			region[0].size = top_addr - low_addr;
			region[0].addr = low_addr;

			debug("Merged region @ 0x%08llx, size 0x%llx\n",
			      region[0].addr, region[0].size);

			used_regions = 1;
		}
	}

	debug("Effective memory @ 0x%08zx, size 0x%llx\n", gd->ram_base,
	      gd->ram_top - gd->ram_base);

	/* Discard/fix region which is outside the effective memory */
	if (fix_tz_region(region, used_regions)) {
		used_regions--;

		if (used_regions) {
			if (fix_tz_region(region, used_regions))
				used_regions--;
		}
	}

	/* Size needed for u-boot & pstore */
#if IS_ENABLED(CONFIG_CMD_PSTORE)
	/* pstore will be placed under ram top */
	pstore_size = (CONFIG_CMD_PSTORE_MEM_SIZE + REGION_ALIGNMENT - 1) &
		      ~(REGION_ALIGNMENT - 1);
	/* u-boot will be placed under pstore */
	uboot_size += pstore_size;
#endif

	uboot_size += max_t(uintptr_t, U_BOOT_MIN_SIZE, total_size);
	uboot_size += U_BOOT_MIN_STACK_SIZE + REGION_ALIGNMENT - 1;
	uboot_size &= ~(REGION_ALIGNMENT - 1);

	uboot_ram_top = gd->ram_top & ~(REGION_ALIGNMENT - 1);

	if (!used_regions ||
	    (uboot_ram_top - region[0].addr - region[0].size >= uboot_size)) {
		/* No reserved region present,
		 * or gap between high region and ram top is large enough
		 */
		uboot_ram_top -= pstore_size;
		return uboot_ram_top;
	}

	uboot_ram_top = region[0].addr & ~(REGION_ALIGNMENT - 1);

	if (used_regions == 2 &&
	    (uboot_ram_top - region[1].addr - region[1].size >= uboot_size)) {
		/* Gap between high region and low region is large enough */
		uboot_ram_top -= pstore_size;
		return uboot_ram_top;
	}

	uboot_ram_top = region[used_regions - 1].addr & ~(REGION_ALIGNMENT - 1);

	/* Under low region */
	uboot_ram_top -= pstore_size;
	return uboot_ram_top;
}

int arch_misc_init(void)
{
	phys_addr_t addr;
	struct arm_smccc_res res;

	/*
	 * Since board_get_usable_ram_top is be called before arch_misc_init,
	 * there's no need to check the result
	 */
	arm_smccc_smc(MTK_SIP_GET_BL31_REGION, 0, 0, 0, 0, 0, 0, 0, &res);
	addr = (phys_addr_t)res.a1;
	lmb_alloc_mem(LMB_MEM_ALLOC_ADDR, 0, &addr, res.a2, LMB_NOMAP);

	arm_smccc_smc(MTK_SIP_GET_BL32_REGION, 0, 0, 0, 0, 0, 0, 0, &res);
	addr = (phys_addr_t)res.a1;
	if (!res.a0 && res.a1 && res.a2)
		lmb_alloc_mem(LMB_MEM_ALLOC_ADDR, 0, &addr, res.a2,
			      LMB_NOMAP);

#if IS_ENABLED(CONFIG_CMD_PSTORE)
	char cmd[64];

	/* Override default pstore address */
	snprintf(cmd, sizeof(cmd), "pstore set 0x%llx 0x%x", gd->ram_top,
		 CONFIG_CMD_PSTORE_MEM_SIZE);
	run_command(cmd, 0);
#endif

	return 0;
}

/* For board-level setup */
__weak int mtk_ft_system_setup(void *blob, struct bd_info *bd)
{
	return 0;
}

int ft_system_setup(void *blob, struct bd_info *bd)
{
	struct arm_smccc_res res;
	struct fdt_memory mem;
	int ret;

	arm_smccc_smc(MTK_SIP_GET_BL31_REGION, 0, 0, 0, 0, 0, 0, 0, &res);

	mem.start = res.a1;
	mem.end = res.a1 + res.a2 - 1;

	ret = fdtdec_add_reserved_memory(blob, "secmon", &mem, NULL, 0, NULL,
					 FDTDEC_RESERVED_MEMORY_NO_MAP);
	if (ret < 0) {
		log_err("Failed to add reserved-memory for BL31: %s\n",
			fdt_strerror(ret));
		return ret;
	}

	arm_smccc_smc(MTK_SIP_GET_BL32_REGION, 0, 0, 0, 0, 0, 0, 0, &res);
	if (!res.a0 && res.a1 && res.a2) {
		mem.start = res.a1;
		mem.end = res.a1 + res.a2 - 1;

		ret = fdtdec_add_reserved_memory(blob, "trustzone", &mem, NULL,
						 0, NULL,
						 FDTDEC_RESERVED_MEMORY_NO_MAP);
		if (ret < 0) {
			log_err("Failed to add reserved-memory for BL32: %s\n",
				fdt_strerror(ret));
			return ret;
		}
	}

	return mtk_ft_system_setup(blob, bd);
}