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