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