blob: e2342b2aece70c39268fb9831e7ff4924af56b89 [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
Simon Glassc7694dd2019-08-01 09:46:46 -06006#include <env.h>
Thierry Redinga0dbc132019-04-15 11:32:28 +02007#include <fdt_support.h>
8#include <fdtdec.h>
Simon Glassdb41d652019-12-28 10:45:07 -07009#include <hang.h>
Simon Glass691d7192020-05-10 11:40:02 -060010#include <init.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -060011#include <log.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>
Simon Glass401d1c42020-10-30 21:38:53 -060016#include <asm/global_data.h>
Simon Glass1e94b462023-09-14 18:21:46 -060017#include <linux/printk.h>
Thierry Redinga0dbc132019-04-15 11:32:28 +020018
Thierry Redingb5717662019-04-15 11:32:31 +020019#include <linux/ctype.h>
Thierry Redinga0dbc132019-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ún236f2ec2021-05-20 13:23:52 +020052static int ram_bank_count __section(".data");
Thierry Redinga0dbc132019-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ún236f2ec2021-05-20 13:23:52 +020060static u64 ram_top __section(".data");
Thierry Redinga0dbc132019-04-15 11:32:28 +020061/* The base address of the region of RAM that ends at ram_top */
Marek Behún236f2ec2021-05-20 13:23:52 +020062static u64 region_base __section(".data");
Thierry Redinga0dbc132019-04-15 11:32:28 +020063
Thierry Redingce353ba2019-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ún236f2ec2021-05-20 13:23:52 +020068unsigned long cboot_boot_x0 __section(".data");
Thierry Redingce353ba2019-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 Redinga0dbc132019-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
Thierry Redinga0dbc132019-04-15 11:32:28 +0200192 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 Reding34e12e02019-04-15 11:32:30 +0200473static int cboot_get_ethaddr_legacy(const void *fdt, uint8_t mac[ETH_ALEN])
Thierry Redinga0dbc132019-04-15 11:32:28 +0200474{
Thierry Reding34e12e02019-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 Redinga0dbc132019-04-15 11:32:28 +0200482
Thierry Reding34e12e02019-04-15 11:32:30 +0200483 node = fdt_path_offset(fdt, "/chosen");
Thierry Redinga0dbc132019-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 Reding34e12e02019-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 Redinga0dbc132019-04-15 11:32:28 +0200495 if (!prop) {
Thierry Reding34e12e02019-04-15 11:32:30 +0200496 printf("Can't find Ethernet MAC address in cboot DTB\n");
Thierry Redinga0dbc132019-04-15 11:32:28 +0200497 return -ENOENT;
498 }
499
Joe Hershbergerfb8977c2019-09-13 19:21:16 -0500500 string_to_enetaddr(prop, mac);
Thierry Reding34e12e02019-04-15 11:32:30 +0200501
502 if (!is_valid_ethaddr(mac)) {
503 printf("Invalid MAC address: %s\n", prop);
504 return -EINVAL;
Thierry Redinga0dbc132019-04-15 11:32:28 +0200505 }
506
Thierry Reding34e12e02019-04-15 11:32:30 +0200507 debug("Legacy MAC address: %pM\n", mac);
508
Thierry Redinga0dbc132019-04-15 11:32:28 +0200509 return 0;
510}
511
Thierry Reding34e12e02019-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 Redingb5717662019-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 Redinga0dbc132019-04-15 11:32:28 +0200591int cboot_late_init(void)
592{
Thierry Reding34e12e02019-04-15 11:32:30 +0200593 const void *fdt = (const void *)cboot_boot_x0;
594 uint8_t mac[ETH_ALEN];
Thierry Redingb5717662019-04-15 11:32:31 +0200595 char *bootargs;
Thierry Reding34e12e02019-04-15 11:32:30 +0200596 int err;
597
Thierry Redinga0dbc132019-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 Reding34e12e02019-04-15 11:32:30 +0200604
Thierry Redinga0dbc132019-04-15 11:32:28 +0200605 /* Ignore errors here; not all cases care about Ethernet addresses */
Thierry Reding34e12e02019-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 Redinga0dbc132019-04-15 11:32:28 +0200614
Thierry Redingb5717662019-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 Redinga0dbc132019-04-15 11:32:28 +0200621 return 0;
622}