blob: 628909f291375e0d0e87fe1819f0bab14f547a48 [file] [log] [blame]
Thierry Reding7cef2b22019-04-15 11:32:28 +02001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (c) 2016-2018, NVIDIA CORPORATION.
4 */
5
6#include <common.h>
Thierry Reding37bb8292019-04-15 11:32:30 +02007#include <environment.h>
Thierry Reding7cef2b22019-04-15 11:32:28 +02008#include <fdt_support.h>
9#include <fdtdec.h>
10#include <stdlib.h>
11
12#include <linux/sizes.h>
13
14#include <asm/arch/tegra.h>
15#include <asm/arch-tegra/cboot.h>
16#include <asm/armv8/mmu.h>
17
18/*
19 * Size of a region that's large enough to hold the relocated U-Boot and all
20 * other allocations made around it (stack, heap, page tables, etc.)
21 * In practice, running "bdinfo" at the shell prompt, the stack reaches about
22 * 5MB from the address selected for ram_top as of the time of writing,
23 * so a 16MB region should be plenty.
24 */
25#define MIN_USABLE_RAM_SIZE SZ_16M
26/*
27 * The amount of space we expect to require for stack usage. Used to validate
28 * that all reservations fit into the region selected for the relocation target
29 */
30#define MIN_USABLE_STACK_SIZE SZ_1M
31
32DECLARE_GLOBAL_DATA_PTR;
33
34extern struct mm_region tegra_mem_map[];
35
36/*
37 * These variables are written to before relocation, and hence cannot be
38 * in.bss, since .bss overlaps the DTB that's appended to the U-Boot binary.
39 * The section attribute forces this into .data and avoids this issue. This
40 * also has the nice side-effect of the content being valid after relocation.
41 */
42
43/* The number of valid entries in ram_banks[] */
44static int ram_bank_count __attribute__((section(".data")));
45
46/*
47 * The usable top-of-RAM for U-Boot. This is both:
48 * a) Below 4GB to avoid issues with peripherals that use 32-bit addressing.
49 * b) At the end of a region that has enough space to hold the relocated U-Boot
50 * and all other allocations made around it (stack, heap, page tables, etc.)
51 */
52static u64 ram_top __attribute__((section(".data")));
53/* The base address of the region of RAM that ends at ram_top */
54static u64 region_base __attribute__((section(".data")));
55
Thierry Redingd8593c72019-04-15 11:32:29 +020056/*
57 * Explicitly put this in the .data section because it is written before the
58 * .bss section is zeroed out but it needs to persist.
59 */
60unsigned long cboot_boot_x0 __attribute__((section(".data")));
61
62void cboot_save_boot_params(unsigned long x0, unsigned long x1,
63 unsigned long x2, unsigned long x3)
64{
65 cboot_boot_x0 = x0;
66}
67
Thierry Reding7cef2b22019-04-15 11:32:28 +020068int cboot_dram_init(void)
69{
70 unsigned int na, ns;
71 const void *cboot_blob = (void *)cboot_boot_x0;
72 int node, len, i;
73 const u32 *prop;
74
75 if (!cboot_blob)
76 return -EINVAL;
77
78 na = fdtdec_get_uint(cboot_blob, 0, "#address-cells", 2);
79 ns = fdtdec_get_uint(cboot_blob, 0, "#size-cells", 2);
80
81 node = fdt_path_offset(cboot_blob, "/memory");
82 if (node < 0) {
83 pr_err("Can't find /memory node in cboot DTB");
84 hang();
85 }
86 prop = fdt_getprop(cboot_blob, node, "reg", &len);
87 if (!prop) {
88 pr_err("Can't find /memory/reg property in cboot DTB");
89 hang();
90 }
91
92 /* Calculate the true # of base/size pairs to read */
93 len /= 4; /* Convert bytes to number of cells */
94 len /= (na + ns); /* Convert cells to number of banks */
95 if (len > CONFIG_NR_DRAM_BANKS)
96 len = CONFIG_NR_DRAM_BANKS;
97
98 /* Parse the /memory node, and save useful entries */
99 gd->ram_size = 0;
100 ram_bank_count = 0;
101 for (i = 0; i < len; i++) {
102 u64 bank_start, bank_end, bank_size, usable_bank_size;
103
104 /* Extract raw memory region data from DTB */
105 bank_start = fdt_read_number(prop, na);
106 prop += na;
107 bank_size = fdt_read_number(prop, ns);
108 prop += ns;
109 gd->ram_size += bank_size;
110 bank_end = bank_start + bank_size;
111 debug("Bank %d: %llx..%llx (+%llx)\n", i,
112 bank_start, bank_end, bank_size);
113
114 /*
115 * Align the bank to MMU section size. This is not strictly
116 * necessary, since the translation table construction code
117 * handles page granularity without issue. However, aligning
118 * the MMU entries reduces the size and number of levels in the
119 * page table, so is worth it.
120 */
121 bank_start = ROUND(bank_start, SZ_2M);
122 bank_end = bank_end & ~(SZ_2M - 1);
123 bank_size = bank_end - bank_start;
124 debug(" aligned: %llx..%llx (+%llx)\n",
125 bank_start, bank_end, bank_size);
126 if (bank_end <= bank_start)
127 continue;
128
129 /* Record data used to create MMU translation tables */
130 ram_bank_count++;
131 /* Index below is deliberately 1-based to skip MMIO entry */
132 tegra_mem_map[ram_bank_count].virt = bank_start;
133 tegra_mem_map[ram_bank_count].phys = bank_start;
134 tegra_mem_map[ram_bank_count].size = bank_size;
135 tegra_mem_map[ram_bank_count].attrs =
136 PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_INNER_SHARE;
137
138 /* Determine best bank to relocate U-Boot into */
139 if (bank_end > SZ_4G)
140 bank_end = SZ_4G;
141 debug(" end %llx (usable)\n", bank_end);
142 usable_bank_size = bank_end - bank_start;
143 debug(" size %llx (usable)\n", usable_bank_size);
144 if ((usable_bank_size >= MIN_USABLE_RAM_SIZE) &&
145 (bank_end > ram_top)) {
146 ram_top = bank_end;
147 region_base = bank_start;
148 debug("ram top now %llx\n", ram_top);
149 }
150 }
151
152 /* Ensure memory map contains the desired sentinel entry */
153 tegra_mem_map[ram_bank_count + 1].virt = 0;
154 tegra_mem_map[ram_bank_count + 1].phys = 0;
155 tegra_mem_map[ram_bank_count + 1].size = 0;
156 tegra_mem_map[ram_bank_count + 1].attrs = 0;
157
158 /* Error out if a relocation target couldn't be found */
159 if (!ram_top) {
160 pr_err("Can't find a usable RAM top");
161 hang();
162 }
163
164 return 0;
165}
166
167int cboot_dram_init_banksize(void)
168{
169 int i;
170
171 if (ram_bank_count == 0)
172 return -EINVAL;
173
174 if ((gd->start_addr_sp - region_base) < MIN_USABLE_STACK_SIZE) {
175 pr_err("Reservations exceed chosen region size");
176 hang();
177 }
178
179 for (i = 0; i < ram_bank_count; i++) {
180 gd->bd->bi_dram[i].start = tegra_mem_map[1 + i].virt;
181 gd->bd->bi_dram[i].size = tegra_mem_map[1 + i].size;
182 }
183
184#ifdef CONFIG_PCI
185 gd->pci_ram_top = ram_top;
186#endif
187
188 return 0;
189}
190
191ulong cboot_get_usable_ram_top(ulong total_size)
192{
193 return ram_top;
194}
195
196/*
197 * The following few functions run late during the boot process and dynamically
198 * calculate the load address of various binaries. To keep track of multiple
199 * allocations, some writable list of RAM banks must be used. tegra_mem_map[]
200 * is used for this purpose to avoid making yet another copy of the list of RAM
201 * banks. This is safe because tegra_mem_map[] is only used once during very
202 * early boot to create U-Boot's page tables, long before this code runs. If
203 * this assumption becomes invalid later, we can just fix the code to copy the
204 * list of RAM banks into some private data structure before running.
205 */
206
207static char *gen_varname(const char *var, const char *ext)
208{
209 size_t len_var = strlen(var);
210 size_t len_ext = strlen(ext);
211 size_t len = len_var + len_ext + 1;
212 char *varext = malloc(len);
213
214 if (!varext)
215 return 0;
216 strcpy(varext, var);
217 strcpy(varext + len_var, ext);
218 return varext;
219}
220
221static void mark_ram_allocated(int bank, u64 allocated_start, u64 allocated_end)
222{
223 u64 bank_start = tegra_mem_map[bank].virt;
224 u64 bank_size = tegra_mem_map[bank].size;
225 u64 bank_end = bank_start + bank_size;
226 bool keep_front = allocated_start != bank_start;
227 bool keep_tail = allocated_end != bank_end;
228
229 if (keep_front && keep_tail) {
230 /*
231 * There are CONFIG_NR_DRAM_BANKS DRAM entries in the array,
232 * starting at index 1 (index 0 is MMIO). So, we are at DRAM
233 * entry "bank" not "bank - 1" as for a typical 0-base array.
234 * The number of remaining DRAM entries is therefore
235 * "CONFIG_NR_DRAM_BANKS - bank". We want to duplicate the
236 * current entry and shift up the remaining entries, dropping
237 * the last one. Thus, we must copy one fewer entry than the
238 * number remaining.
239 */
240 memmove(&tegra_mem_map[bank + 1], &tegra_mem_map[bank],
241 CONFIG_NR_DRAM_BANKS - bank - 1);
242 tegra_mem_map[bank].size = allocated_start - bank_start;
243 bank++;
244 tegra_mem_map[bank].virt = allocated_end;
245 tegra_mem_map[bank].phys = allocated_end;
246 tegra_mem_map[bank].size = bank_end - allocated_end;
247 } else if (keep_front) {
248 tegra_mem_map[bank].size = allocated_start - bank_start;
249 } else if (keep_tail) {
250 tegra_mem_map[bank].virt = allocated_end;
251 tegra_mem_map[bank].phys = allocated_end;
252 tegra_mem_map[bank].size = bank_end - allocated_end;
253 } else {
254 /*
255 * We could move all subsequent banks down in the array but
256 * that's not necessary for subsequent allocations to work, so
257 * we skip doing so.
258 */
259 tegra_mem_map[bank].size = 0;
260 }
261}
262
263static void reserve_ram(u64 start, u64 size)
264{
265 int bank;
266 u64 end = start + size;
267
268 for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) {
269 u64 bank_start = tegra_mem_map[bank].virt;
270 u64 bank_size = tegra_mem_map[bank].size;
271 u64 bank_end = bank_start + bank_size;
272
273 if (end <= bank_start || start > bank_end)
274 continue;
275 mark_ram_allocated(bank, start, end);
276 break;
277 }
278}
279
280static u64 alloc_ram(u64 size, u64 align, u64 offset)
281{
282 int bank;
283
284 for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) {
285 u64 bank_start = tegra_mem_map[bank].virt;
286 u64 bank_size = tegra_mem_map[bank].size;
287 u64 bank_end = bank_start + bank_size;
288 u64 allocated = ROUND(bank_start, align) + offset;
289 u64 allocated_end = allocated + size;
290
291 if (allocated_end > bank_end)
292 continue;
293 mark_ram_allocated(bank, allocated, allocated_end);
294 return allocated;
295 }
296 return 0;
297}
298
299static void set_calculated_aliases(char *aliases, u64 address)
300{
301 char *tmp, *alias;
302 int err;
303
304 aliases = strdup(aliases);
305 if (!aliases) {
306 pr_err("strdup(aliases) failed");
307 return;
308 }
309
310 tmp = aliases;
311 while (true) {
312 alias = strsep(&tmp, " ");
313 if (!alias)
314 break;
315 debug("%s: alias: %s\n", __func__, alias);
316 err = env_set_hex(alias, address);
317 if (err)
318 pr_err("Could not set %s\n", alias);
319 }
320
321 free(aliases);
322}
323
324static void set_calculated_env_var(const char *var)
325{
326 char *var_size;
327 char *var_align;
328 char *var_offset;
329 char *var_aliases;
330 u64 size;
331 u64 align;
332 u64 offset;
333 char *aliases;
334 u64 address;
335 int err;
336
337 var_size = gen_varname(var, "_size");
338 if (!var_size)
339 return;
340 var_align = gen_varname(var, "_align");
341 if (!var_align)
342 goto out_free_var_size;
343 var_offset = gen_varname(var, "_offset");
344 if (!var_offset)
345 goto out_free_var_align;
346 var_aliases = gen_varname(var, "_aliases");
347 if (!var_aliases)
348 goto out_free_var_offset;
349
350 size = env_get_hex(var_size, 0);
351 if (!size) {
352 pr_err("%s not set or zero\n", var_size);
353 goto out_free_var_aliases;
354 }
355 align = env_get_hex(var_align, 1);
356 /* Handle extant variables, but with a value of 0 */
357 if (!align)
358 align = 1;
359 offset = env_get_hex(var_offset, 0);
360 aliases = env_get(var_aliases);
361
362 debug("%s: Calc var %s; size=%llx, align=%llx, offset=%llx\n",
363 __func__, var, size, align, offset);
364 if (aliases)
365 debug("%s: Aliases: %s\n", __func__, aliases);
366
367 address = alloc_ram(size, align, offset);
368 if (!address) {
369 pr_err("Could not allocate %s\n", var);
370 goto out_free_var_aliases;
371 }
372 debug("%s: Address %llx\n", __func__, address);
373
374 err = env_set_hex(var, address);
375 if (err)
376 pr_err("Could not set %s\n", var);
377 if (aliases)
378 set_calculated_aliases(aliases, address);
379
380out_free_var_aliases:
381 free(var_aliases);
382out_free_var_offset:
383 free(var_offset);
384out_free_var_align:
385 free(var_align);
386out_free_var_size:
387 free(var_size);
388}
389
390#ifdef DEBUG
391static void dump_ram_banks(void)
392{
393 int bank;
394
395 for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) {
396 u64 bank_start = tegra_mem_map[bank].virt;
397 u64 bank_size = tegra_mem_map[bank].size;
398 u64 bank_end = bank_start + bank_size;
399
400 if (!bank_size)
401 continue;
402 printf("%d: %010llx..%010llx (+%010llx)\n", bank - 1,
403 bank_start, bank_end, bank_size);
404 }
405}
406#endif
407
408static void set_calculated_env_vars(void)
409{
410 char *vars, *tmp, *var;
411
412#ifdef DEBUG
413 printf("RAM banks before any calculated env. var.s:\n");
414 dump_ram_banks();
415#endif
416
417 reserve_ram(cboot_boot_x0, fdt_totalsize(cboot_boot_x0));
418
419#ifdef DEBUG
420 printf("RAM after reserving cboot DTB:\n");
421 dump_ram_banks();
422#endif
423
424 vars = env_get("calculated_vars");
425 if (!vars) {
426 debug("%s: No env var calculated_vars\n", __func__);
427 return;
428 }
429
430 vars = strdup(vars);
431 if (!vars) {
432 pr_err("strdup(calculated_vars) failed");
433 return;
434 }
435
436 tmp = vars;
437 while (true) {
438 var = strsep(&tmp, " ");
439 if (!var)
440 break;
441 debug("%s: var: %s\n", __func__, var);
442 set_calculated_env_var(var);
443#ifdef DEBUG
444 printf("RAM banks after allocating %s:\n", var);
445 dump_ram_banks();
446#endif
447 }
448
449 free(vars);
450}
451
452static int set_fdt_addr(void)
453{
454 int ret;
455
456 ret = env_set_hex("fdt_addr", cboot_boot_x0);
457 if (ret) {
458 printf("Failed to set fdt_addr to point at DTB: %d\n", ret);
459 return ret;
460 }
461
462 return 0;
463}
464
465/*
466 * Attempt to use /chosen/nvidia,ether-mac in the cboot DTB to U-Boot's
467 * ethaddr environment variable if possible.
468 */
Thierry Reding37bb8292019-04-15 11:32:30 +0200469static int cboot_get_ethaddr_legacy(const void *fdt, uint8_t mac[ETH_ALEN])
Thierry Reding7cef2b22019-04-15 11:32:28 +0200470{
Thierry Reding37bb8292019-04-15 11:32:30 +0200471 const char *const properties[] = {
472 "nvidia,ethernet-mac",
473 "nvidia,ether-mac",
474 };
475 const char *prop;
476 unsigned int i;
477 int node, len;
Thierry Reding7cef2b22019-04-15 11:32:28 +0200478
Thierry Reding37bb8292019-04-15 11:32:30 +0200479 node = fdt_path_offset(fdt, "/chosen");
Thierry Reding7cef2b22019-04-15 11:32:28 +0200480 if (node < 0) {
481 printf("Can't find /chosen node in cboot DTB\n");
482 return node;
483 }
Thierry Reding37bb8292019-04-15 11:32:30 +0200484
485 for (i = 0; i < ARRAY_SIZE(properties); i++) {
486 prop = fdt_getprop(fdt, node, properties[i], &len);
487 if (prop)
488 break;
489 }
490
Thierry Reding7cef2b22019-04-15 11:32:28 +0200491 if (!prop) {
Thierry Reding37bb8292019-04-15 11:32:30 +0200492 printf("Can't find Ethernet MAC address in cboot DTB\n");
Thierry Reding7cef2b22019-04-15 11:32:28 +0200493 return -ENOENT;
494 }
495
Thierry Reding37bb8292019-04-15 11:32:30 +0200496 eth_parse_enetaddr(prop, mac);
497
498 if (!is_valid_ethaddr(mac)) {
499 printf("Invalid MAC address: %s\n", prop);
500 return -EINVAL;
Thierry Reding7cef2b22019-04-15 11:32:28 +0200501 }
502
Thierry Reding37bb8292019-04-15 11:32:30 +0200503 debug("Legacy MAC address: %pM\n", mac);
504
Thierry Reding7cef2b22019-04-15 11:32:28 +0200505 return 0;
506}
507
Thierry Reding37bb8292019-04-15 11:32:30 +0200508int cboot_get_ethaddr(const void *fdt, uint8_t mac[ETH_ALEN])
509{
510 int node, len, err = 0;
511 const uchar *prop;
512 const char *path;
513
514 path = fdt_get_alias(fdt, "ethernet");
515 if (!path) {
516 err = -ENOENT;
517 goto out;
518 }
519
520 debug("ethernet alias found: %s\n", path);
521
522 node = fdt_path_offset(fdt, path);
523 if (node < 0) {
524 err = -ENOENT;
525 goto out;
526 }
527
528 prop = fdt_getprop(fdt, node, "local-mac-address", &len);
529 if (!prop) {
530 err = -ENOENT;
531 goto out;
532 }
533
534 if (len != ETH_ALEN) {
535 err = -EINVAL;
536 goto out;
537 }
538
539 debug("MAC address: %pM\n", prop);
540 memcpy(mac, prop, ETH_ALEN);
541
542out:
543 if (err < 0)
544 err = cboot_get_ethaddr_legacy(fdt, mac);
545
546 return err;
547}
548
Thierry Reding7cef2b22019-04-15 11:32:28 +0200549int cboot_late_init(void)
550{
Thierry Reding37bb8292019-04-15 11:32:30 +0200551 const void *fdt = (const void *)cboot_boot_x0;
552 uint8_t mac[ETH_ALEN];
553 int err;
554
Thierry Reding7cef2b22019-04-15 11:32:28 +0200555 set_calculated_env_vars();
556 /*
557 * Ignore errors here; the value may not be used depending on
558 * extlinux.conf or boot script content.
559 */
560 set_fdt_addr();
Thierry Reding37bb8292019-04-15 11:32:30 +0200561
Thierry Reding7cef2b22019-04-15 11:32:28 +0200562 /* Ignore errors here; not all cases care about Ethernet addresses */
Thierry Reding37bb8292019-04-15 11:32:30 +0200563 err = cboot_get_ethaddr(fdt, mac);
564 if (!err) {
565 void *blob = (void *)gd->fdt_blob;
566
567 err = fdtdec_set_ethernet_mac_address(blob, mac, sizeof(mac));
568 if (err < 0)
569 printf("failed to set MAC address %pM: %d\n", mac, err);
570 }
Thierry Reding7cef2b22019-04-15 11:32:28 +0200571
572 return 0;
573}