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