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