1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2016-2018, NVIDIA CORPORATION.
4  */
5 
6 #include <stdlib.h>
7 #include <common.h>
8 #include <fdt_support.h>
9 #include <fdtdec.h>
10 #include <asm/arch/tegra.h>
11 #include <asm/armv8/mmu.h>
12 
13 extern unsigned long nvtboot_boot_x0;
14 
15 /*
16  * The following few functions run late during the boot process and dynamically
17  * calculate the load address of various binaries. To keep track of multiple
18  * allocations, some writable list of RAM banks must be used. tegra_mem_map[]
19  * is used for this purpose to avoid making yet another copy of the list of RAM
20  * banks. This is safe because tegra_mem_map[] is only used once during very
21  * early boot to create U-Boot's page tables, long before this code runs. If
22  * this assumption becomes invalid later, we can just fix the code to copy the
23  * list of RAM banks into some private data structure before running.
24  */
25 
26 extern struct mm_region tegra_mem_map[];
27 
gen_varname(const char * var,const char * ext)28 static char *gen_varname(const char *var, const char *ext)
29 {
30 	size_t len_var = strlen(var);
31 	size_t len_ext = strlen(ext);
32 	size_t len = len_var + len_ext + 1;
33 	char *varext = malloc(len);
34 
35 	if (!varext)
36 		return 0;
37 	strcpy(varext, var);
38 	strcpy(varext + len_var, ext);
39 	return varext;
40 }
41 
mark_ram_allocated(int bank,u64 allocated_start,u64 allocated_end)42 static void mark_ram_allocated(int bank, u64 allocated_start, u64 allocated_end)
43 {
44 	u64 bank_start = tegra_mem_map[bank].virt;
45 	u64 bank_size = tegra_mem_map[bank].size;
46 	u64 bank_end = bank_start + bank_size;
47 	bool keep_front = allocated_start != bank_start;
48 	bool keep_tail = allocated_end != bank_end;
49 
50 	if (keep_front && keep_tail) {
51 		/*
52 		 * There are CONFIG_NR_DRAM_BANKS DRAM entries in the array,
53 		 * starting at index 1 (index 0 is MMIO). So, we are at DRAM
54 		 * entry "bank" not "bank - 1" as for a typical 0-base array.
55 		 * The number of remaining DRAM entries is therefore
56 		 * "CONFIG_NR_DRAM_BANKS - bank". We want to duplicate the
57 		 * current entry and shift up the remaining entries, dropping
58 		 * the last one. Thus, we must copy one fewer entry than the
59 		 * number remaining.
60 		 */
61 		memmove(&tegra_mem_map[bank + 1], &tegra_mem_map[bank],
62 			CONFIG_NR_DRAM_BANKS - bank - 1);
63 		tegra_mem_map[bank].size = allocated_start - bank_start;
64 		bank++;
65 		tegra_mem_map[bank].virt = allocated_end;
66 		tegra_mem_map[bank].phys = allocated_end;
67 		tegra_mem_map[bank].size = bank_end - allocated_end;
68 	} else if (keep_front) {
69 		tegra_mem_map[bank].size = allocated_start - bank_start;
70 	} else if (keep_tail) {
71 		tegra_mem_map[bank].virt = allocated_end;
72 		tegra_mem_map[bank].phys = allocated_end;
73 		tegra_mem_map[bank].size = bank_end - allocated_end;
74 	} else {
75 		/*
76 		 * We could move all subsequent banks down in the array but
77 		 * that's not necessary for subsequent allocations to work, so
78 		 * we skip doing so.
79 		 */
80 		tegra_mem_map[bank].size = 0;
81 	}
82 }
83 
reserve_ram(u64 start,u64 size)84 static void reserve_ram(u64 start, u64 size)
85 {
86 	int bank;
87 	u64 end = start + size;
88 
89 	for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) {
90 		u64 bank_start = tegra_mem_map[bank].virt;
91 		u64 bank_size = tegra_mem_map[bank].size;
92 		u64 bank_end = bank_start + bank_size;
93 
94 		if (end <= bank_start || start > bank_end)
95 			continue;
96 		mark_ram_allocated(bank, start, end);
97 		break;
98 	}
99 }
100 
alloc_ram(u64 size,u64 align,u64 offset)101 static u64 alloc_ram(u64 size, u64 align, u64 offset)
102 {
103 	int bank;
104 
105 	for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) {
106 		u64 bank_start = tegra_mem_map[bank].virt;
107 		u64 bank_size = tegra_mem_map[bank].size;
108 		u64 bank_end = bank_start + bank_size;
109 		u64 allocated = ROUND(bank_start, align) + offset;
110 		u64 allocated_end = allocated + size;
111 
112 		if (allocated_end > bank_end)
113 			continue;
114 		mark_ram_allocated(bank, allocated, allocated_end);
115 		return allocated;
116 	}
117 	return 0;
118 }
119 
set_calculated_aliases(char * aliases,u64 address)120 static void set_calculated_aliases(char *aliases, u64 address)
121 {
122 	char *tmp, *alias;
123 	int err;
124 
125 	aliases = strdup(aliases);
126 	if (!aliases) {
127 		pr_err("strdup(aliases) failed");
128 		return;
129 	}
130 
131 	tmp = aliases;
132 	while (true) {
133 		alias = strsep(&tmp, " ");
134 		if (!alias)
135 			break;
136 		debug("%s: alias: %s\n", __func__, alias);
137 		err = env_set_hex(alias, address);
138 		if (err)
139 			pr_err("Could not set %s\n", alias);
140 	}
141 
142 	free(aliases);
143 }
144 
set_calculated_env_var(const char * var)145 static void set_calculated_env_var(const char *var)
146 {
147 	char *var_size;
148 	char *var_align;
149 	char *var_offset;
150 	char *var_aliases;
151 	u64 size;
152 	u64 align;
153 	u64 offset;
154 	char *aliases;
155 	u64 address;
156 	int err;
157 
158 	var_size = gen_varname(var, "_size");
159 	if (!var_size)
160 		return;
161 	var_align = gen_varname(var, "_align");
162 	if (!var_align)
163 		goto out_free_var_size;
164 	var_offset = gen_varname(var, "_offset");
165 	if (!var_offset)
166 		goto out_free_var_align;
167 	var_aliases = gen_varname(var, "_aliases");
168 	if (!var_aliases)
169 		goto out_free_var_offset;
170 
171 	size = env_get_hex(var_size, 0);
172 	if (!size) {
173 		pr_err("%s not set or zero\n", var_size);
174 		goto out_free_var_aliases;
175 	}
176 	align = env_get_hex(var_align, 1);
177 	/* Handle extant variables, but with a value of 0 */
178 	if (!align)
179 		align = 1;
180 	offset = env_get_hex(var_offset, 0);
181 	aliases = env_get(var_aliases);
182 
183 	debug("%s: Calc var %s; size=%llx, align=%llx, offset=%llx\n",
184 	      __func__, var, size, align, offset);
185 	if (aliases)
186 		debug("%s: Aliases: %s\n", __func__, aliases);
187 
188 	address = alloc_ram(size, align, offset);
189 	if (!address) {
190 		pr_err("Could not allocate %s\n", var);
191 		goto out_free_var_aliases;
192 	}
193 	debug("%s: Address %llx\n", __func__, address);
194 
195 	err = env_set_hex(var, address);
196 	if (err)
197 		pr_err("Could not set %s\n", var);
198 	if (aliases)
199 		set_calculated_aliases(aliases, address);
200 
201 out_free_var_aliases:
202 	free(var_aliases);
203 out_free_var_offset:
204 	free(var_offset);
205 out_free_var_align:
206 	free(var_align);
207 out_free_var_size:
208 	free(var_size);
209 }
210 
211 #ifdef DEBUG
dump_ram_banks(void)212 static void dump_ram_banks(void)
213 {
214 	int bank;
215 
216 	for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) {
217 		u64 bank_start = tegra_mem_map[bank].virt;
218 		u64 bank_size = tegra_mem_map[bank].size;
219 		u64 bank_end = bank_start + bank_size;
220 
221 		if (!bank_size)
222 			continue;
223 		printf("%d: %010llx..%010llx (+%010llx)\n", bank - 1,
224 		       bank_start, bank_end, bank_size);
225 	}
226 }
227 #endif
228 
set_calculated_env_vars(void)229 static void set_calculated_env_vars(void)
230 {
231 	char *vars, *tmp, *var;
232 
233 #ifdef DEBUG
234 	printf("RAM banks before any calculated env. var.s:\n");
235 	dump_ram_banks();
236 #endif
237 
238 	reserve_ram(nvtboot_boot_x0, fdt_totalsize(nvtboot_boot_x0));
239 
240 #ifdef DEBUG
241 	printf("RAM after reserving cboot DTB:\n");
242 	dump_ram_banks();
243 #endif
244 
245 	vars = env_get("calculated_vars");
246 	if (!vars) {
247 		debug("%s: No env var calculated_vars\n", __func__);
248 		return;
249 	}
250 
251 	vars = strdup(vars);
252 	if (!vars) {
253 		pr_err("strdup(calculated_vars) failed");
254 		return;
255 	}
256 
257 	tmp = vars;
258 	while (true) {
259 		var = strsep(&tmp, " ");
260 		if (!var)
261 			break;
262 		debug("%s: var: %s\n", __func__, var);
263 		set_calculated_env_var(var);
264 #ifdef DEBUG
265 		printf("RAM banks affter allocating %s:\n", var);
266 		dump_ram_banks();
267 #endif
268 	}
269 
270 	free(vars);
271 }
272 
set_fdt_addr(void)273 static int set_fdt_addr(void)
274 {
275 	int ret;
276 
277 	ret = env_set_hex("fdt_addr", nvtboot_boot_x0);
278 	if (ret) {
279 		printf("Failed to set fdt_addr to point at DTB: %d\n", ret);
280 		return ret;
281 	}
282 
283 	return 0;
284 }
285 
286 /*
287  * Attempt to use /chosen/nvidia,ether-mac in the nvtboot DTB to U-Boot's
288  * ethaddr environment variable if possible.
289  */
set_ethaddr_from_nvtboot(void)290 static int set_ethaddr_from_nvtboot(void)
291 {
292 	const void *nvtboot_blob = (void *)nvtboot_boot_x0;
293 	int ret, node, len;
294 	const u32 *prop;
295 
296 	/* Already a valid address in the environment? If so, keep it */
297 	if (env_get("ethaddr"))
298 		return 0;
299 
300 	node = fdt_path_offset(nvtboot_blob, "/chosen");
301 	if (node < 0) {
302 		printf("Can't find /chosen node in nvtboot DTB\n");
303 		return node;
304 	}
305 	prop = fdt_getprop(nvtboot_blob, node, "nvidia,ether-mac", &len);
306 	if (!prop) {
307 		printf("Can't find nvidia,ether-mac property in nvtboot DTB\n");
308 		return -ENOENT;
309 	}
310 
311 	ret = env_set("ethaddr", (void *)prop);
312 	if (ret) {
313 		printf("Failed to set ethaddr from nvtboot DTB: %d\n", ret);
314 		return ret;
315 	}
316 
317 	return 0;
318 }
319 
tegra_soc_board_init_late(void)320 int tegra_soc_board_init_late(void)
321 {
322 	set_calculated_env_vars();
323 	/*
324 	 * Ignore errors here; the value may not be used depending on
325 	 * extlinux.conf or boot script content.
326 	 */
327 	set_fdt_addr();
328 	/* Ignore errors here; not all cases care about Ethernet addresses */
329 	set_ethaddr_from_nvtboot();
330 
331 	return 0;
332 }
333