]>
git.ipfire.org Git - thirdparty/u-boot.git/blob - arch/arm/mach-tegra/cboot.c
1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (c) 2016-2018, NVIDIA CORPORATION.
8 #include <fdt_support.h>
16 #include <linux/ctype.h>
17 #include <linux/sizes.h>
19 #include <asm/arch/tegra.h>
20 #include <asm/arch-tegra/cboot.h>
21 #include <asm/armv8/mmu.h>
24 * Size of a region that's large enough to hold the relocated U-Boot and all
25 * other allocations made around it (stack, heap, page tables, etc.)
26 * In practice, running "bdinfo" at the shell prompt, the stack reaches about
27 * 5MB from the address selected for ram_top as of the time of writing,
28 * so a 16MB region should be plenty.
30 #define MIN_USABLE_RAM_SIZE SZ_16M
32 * The amount of space we expect to require for stack usage. Used to validate
33 * that all reservations fit into the region selected for the relocation target
35 #define MIN_USABLE_STACK_SIZE SZ_1M
37 DECLARE_GLOBAL_DATA_PTR
;
39 extern struct mm_region tegra_mem_map
[];
42 * These variables are written to before relocation, and hence cannot be
43 * in.bss, since .bss overlaps the DTB that's appended to the U-Boot binary.
44 * The section attribute forces this into .data and avoids this issue. This
45 * also has the nice side-effect of the content being valid after relocation.
48 /* The number of valid entries in ram_banks[] */
49 static int ram_bank_count
__attribute__((section(".data")));
52 * The usable top-of-RAM for U-Boot. This is both:
53 * a) Below 4GB to avoid issues with peripherals that use 32-bit addressing.
54 * b) At the end of a region that has enough space to hold the relocated U-Boot
55 * and all other allocations made around it (stack, heap, page tables, etc.)
57 static u64 ram_top
__attribute__((section(".data")));
58 /* The base address of the region of RAM that ends at ram_top */
59 static u64 region_base
__attribute__((section(".data")));
62 * Explicitly put this in the .data section because it is written before the
63 * .bss section is zeroed out but it needs to persist.
65 unsigned long cboot_boot_x0
__attribute__((section(".data")));
67 void cboot_save_boot_params(unsigned long x0
, unsigned long x1
,
68 unsigned long x2
, unsigned long x3
)
73 int cboot_dram_init(void)
76 const void *cboot_blob
= (void *)cboot_boot_x0
;
83 na
= fdtdec_get_uint(cboot_blob
, 0, "#address-cells", 2);
84 ns
= fdtdec_get_uint(cboot_blob
, 0, "#size-cells", 2);
86 node
= fdt_path_offset(cboot_blob
, "/memory");
88 pr_err("Can't find /memory node in cboot DTB");
91 prop
= fdt_getprop(cboot_blob
, node
, "reg", &len
);
93 pr_err("Can't find /memory/reg property in cboot DTB");
97 /* Calculate the true # of base/size pairs to read */
98 len
/= 4; /* Convert bytes to number of cells */
99 len
/= (na
+ ns
); /* Convert cells to number of banks */
100 if (len
> CONFIG_NR_DRAM_BANKS
)
101 len
= CONFIG_NR_DRAM_BANKS
;
103 /* Parse the /memory node, and save useful entries */
106 for (i
= 0; i
< len
; i
++) {
107 u64 bank_start
, bank_end
, bank_size
, usable_bank_size
;
109 /* Extract raw memory region data from DTB */
110 bank_start
= fdt_read_number(prop
, na
);
112 bank_size
= fdt_read_number(prop
, ns
);
114 gd
->ram_size
+= bank_size
;
115 bank_end
= bank_start
+ bank_size
;
116 debug("Bank %d: %llx..%llx (+%llx)\n", i
,
117 bank_start
, bank_end
, bank_size
);
120 * Align the bank to MMU section size. This is not strictly
121 * necessary, since the translation table construction code
122 * handles page granularity without issue. However, aligning
123 * the MMU entries reduces the size and number of levels in the
124 * page table, so is worth it.
126 bank_start
= ROUND(bank_start
, SZ_2M
);
127 bank_end
= bank_end
& ~(SZ_2M
- 1);
128 bank_size
= bank_end
- bank_start
;
129 debug(" aligned: %llx..%llx (+%llx)\n",
130 bank_start
, bank_end
, bank_size
);
131 if (bank_end
<= bank_start
)
134 /* Record data used to create MMU translation tables */
136 /* Index below is deliberately 1-based to skip MMIO entry */
137 tegra_mem_map
[ram_bank_count
].virt
= bank_start
;
138 tegra_mem_map
[ram_bank_count
].phys
= bank_start
;
139 tegra_mem_map
[ram_bank_count
].size
= bank_size
;
140 tegra_mem_map
[ram_bank_count
].attrs
=
141 PTE_BLOCK_MEMTYPE(MT_NORMAL
) | PTE_BLOCK_INNER_SHARE
;
143 /* Determine best bank to relocate U-Boot into */
144 if (bank_end
> SZ_4G
)
146 debug(" end %llx (usable)\n", bank_end
);
147 usable_bank_size
= bank_end
- bank_start
;
148 debug(" size %llx (usable)\n", usable_bank_size
);
149 if ((usable_bank_size
>= MIN_USABLE_RAM_SIZE
) &&
150 (bank_end
> ram_top
)) {
152 region_base
= bank_start
;
153 debug("ram top now %llx\n", ram_top
);
157 /* Ensure memory map contains the desired sentinel entry */
158 tegra_mem_map
[ram_bank_count
+ 1].virt
= 0;
159 tegra_mem_map
[ram_bank_count
+ 1].phys
= 0;
160 tegra_mem_map
[ram_bank_count
+ 1].size
= 0;
161 tegra_mem_map
[ram_bank_count
+ 1].attrs
= 0;
163 /* Error out if a relocation target couldn't be found */
165 pr_err("Can't find a usable RAM top");
172 int cboot_dram_init_banksize(void)
176 if (ram_bank_count
== 0)
179 if ((gd
->start_addr_sp
- region_base
) < MIN_USABLE_STACK_SIZE
) {
180 pr_err("Reservations exceed chosen region size");
184 for (i
= 0; i
< ram_bank_count
; i
++) {
185 gd
->bd
->bi_dram
[i
].start
= tegra_mem_map
[1 + i
].virt
;
186 gd
->bd
->bi_dram
[i
].size
= tegra_mem_map
[1 + i
].size
;
190 gd
->pci_ram_top
= ram_top
;
196 ulong
cboot_get_usable_ram_top(ulong total_size
)
202 * The following few functions run late during the boot process and dynamically
203 * calculate the load address of various binaries. To keep track of multiple
204 * allocations, some writable list of RAM banks must be used. tegra_mem_map[]
205 * is used for this purpose to avoid making yet another copy of the list of RAM
206 * banks. This is safe because tegra_mem_map[] is only used once during very
207 * early boot to create U-Boot's page tables, long before this code runs. If
208 * this assumption becomes invalid later, we can just fix the code to copy the
209 * list of RAM banks into some private data structure before running.
212 static char *gen_varname(const char *var
, const char *ext
)
214 size_t len_var
= strlen(var
);
215 size_t len_ext
= strlen(ext
);
216 size_t len
= len_var
+ len_ext
+ 1;
217 char *varext
= malloc(len
);
222 strcpy(varext
+ len_var
, ext
);
226 static void mark_ram_allocated(int bank
, u64 allocated_start
, u64 allocated_end
)
228 u64 bank_start
= tegra_mem_map
[bank
].virt
;
229 u64 bank_size
= tegra_mem_map
[bank
].size
;
230 u64 bank_end
= bank_start
+ bank_size
;
231 bool keep_front
= allocated_start
!= bank_start
;
232 bool keep_tail
= allocated_end
!= bank_end
;
234 if (keep_front
&& keep_tail
) {
236 * There are CONFIG_NR_DRAM_BANKS DRAM entries in the array,
237 * starting at index 1 (index 0 is MMIO). So, we are at DRAM
238 * entry "bank" not "bank - 1" as for a typical 0-base array.
239 * The number of remaining DRAM entries is therefore
240 * "CONFIG_NR_DRAM_BANKS - bank". We want to duplicate the
241 * current entry and shift up the remaining entries, dropping
242 * the last one. Thus, we must copy one fewer entry than the
245 memmove(&tegra_mem_map
[bank
+ 1], &tegra_mem_map
[bank
],
246 CONFIG_NR_DRAM_BANKS
- bank
- 1);
247 tegra_mem_map
[bank
].size
= allocated_start
- bank_start
;
249 tegra_mem_map
[bank
].virt
= allocated_end
;
250 tegra_mem_map
[bank
].phys
= allocated_end
;
251 tegra_mem_map
[bank
].size
= bank_end
- allocated_end
;
252 } else if (keep_front
) {
253 tegra_mem_map
[bank
].size
= allocated_start
- bank_start
;
254 } else if (keep_tail
) {
255 tegra_mem_map
[bank
].virt
= allocated_end
;
256 tegra_mem_map
[bank
].phys
= allocated_end
;
257 tegra_mem_map
[bank
].size
= bank_end
- allocated_end
;
260 * We could move all subsequent banks down in the array but
261 * that's not necessary for subsequent allocations to work, so
264 tegra_mem_map
[bank
].size
= 0;
268 static void reserve_ram(u64 start
, u64 size
)
271 u64 end
= start
+ size
;
273 for (bank
= 1; bank
<= CONFIG_NR_DRAM_BANKS
; bank
++) {
274 u64 bank_start
= tegra_mem_map
[bank
].virt
;
275 u64 bank_size
= tegra_mem_map
[bank
].size
;
276 u64 bank_end
= bank_start
+ bank_size
;
278 if (end
<= bank_start
|| start
> bank_end
)
280 mark_ram_allocated(bank
, start
, end
);
285 static u64
alloc_ram(u64 size
, u64 align
, u64 offset
)
289 for (bank
= 1; bank
<= CONFIG_NR_DRAM_BANKS
; bank
++) {
290 u64 bank_start
= tegra_mem_map
[bank
].virt
;
291 u64 bank_size
= tegra_mem_map
[bank
].size
;
292 u64 bank_end
= bank_start
+ bank_size
;
293 u64 allocated
= ROUND(bank_start
, align
) + offset
;
294 u64 allocated_end
= allocated
+ size
;
296 if (allocated_end
> bank_end
)
298 mark_ram_allocated(bank
, allocated
, allocated_end
);
304 static void set_calculated_aliases(char *aliases
, u64 address
)
309 aliases
= strdup(aliases
);
311 pr_err("strdup(aliases) failed");
317 alias
= strsep(&tmp
, " ");
320 debug("%s: alias: %s\n", __func__
, alias
);
321 err
= env_set_hex(alias
, address
);
323 pr_err("Could not set %s\n", alias
);
329 static void set_calculated_env_var(const char *var
)
342 var_size
= gen_varname(var
, "_size");
345 var_align
= gen_varname(var
, "_align");
347 goto out_free_var_size
;
348 var_offset
= gen_varname(var
, "_offset");
350 goto out_free_var_align
;
351 var_aliases
= gen_varname(var
, "_aliases");
353 goto out_free_var_offset
;
355 size
= env_get_hex(var_size
, 0);
357 pr_err("%s not set or zero\n", var_size
);
358 goto out_free_var_aliases
;
360 align
= env_get_hex(var_align
, 1);
361 /* Handle extant variables, but with a value of 0 */
364 offset
= env_get_hex(var_offset
, 0);
365 aliases
= env_get(var_aliases
);
367 debug("%s: Calc var %s; size=%llx, align=%llx, offset=%llx\n",
368 __func__
, var
, size
, align
, offset
);
370 debug("%s: Aliases: %s\n", __func__
, aliases
);
372 address
= alloc_ram(size
, align
, offset
);
374 pr_err("Could not allocate %s\n", var
);
375 goto out_free_var_aliases
;
377 debug("%s: Address %llx\n", __func__
, address
);
379 err
= env_set_hex(var
, address
);
381 pr_err("Could not set %s\n", var
);
383 set_calculated_aliases(aliases
, address
);
385 out_free_var_aliases
:
396 static void dump_ram_banks(void)
400 for (bank
= 1; bank
<= CONFIG_NR_DRAM_BANKS
; bank
++) {
401 u64 bank_start
= tegra_mem_map
[bank
].virt
;
402 u64 bank_size
= tegra_mem_map
[bank
].size
;
403 u64 bank_end
= bank_start
+ bank_size
;
407 printf("%d: %010llx..%010llx (+%010llx)\n", bank
- 1,
408 bank_start
, bank_end
, bank_size
);
413 static void set_calculated_env_vars(void)
415 char *vars
, *tmp
, *var
;
418 printf("RAM banks before any calculated env. var.s:\n");
422 reserve_ram(cboot_boot_x0
, fdt_totalsize(cboot_boot_x0
));
425 printf("RAM after reserving cboot DTB:\n");
429 vars
= env_get("calculated_vars");
431 debug("%s: No env var calculated_vars\n", __func__
);
437 pr_err("strdup(calculated_vars) failed");
443 var
= strsep(&tmp
, " ");
446 debug("%s: var: %s\n", __func__
, var
);
447 set_calculated_env_var(var
);
449 printf("RAM banks after allocating %s:\n", var
);
457 static int set_fdt_addr(void)
461 ret
= env_set_hex("fdt_addr", cboot_boot_x0
);
463 printf("Failed to set fdt_addr to point at DTB: %d\n", ret
);
471 * Attempt to use /chosen/nvidia,ether-mac in the cboot DTB to U-Boot's
472 * ethaddr environment variable if possible.
474 static int cboot_get_ethaddr_legacy(const void *fdt
, uint8_t mac
[ETH_ALEN
])
476 const char *const properties
[] = {
477 "nvidia,ethernet-mac",
484 node
= fdt_path_offset(fdt
, "/chosen");
486 printf("Can't find /chosen node in cboot DTB\n");
490 for (i
= 0; i
< ARRAY_SIZE(properties
); i
++) {
491 prop
= fdt_getprop(fdt
, node
, properties
[i
], &len
);
497 printf("Can't find Ethernet MAC address in cboot DTB\n");
501 string_to_enetaddr(prop
, mac
);
503 if (!is_valid_ethaddr(mac
)) {
504 printf("Invalid MAC address: %s\n", prop
);
508 debug("Legacy MAC address: %pM\n", mac
);
513 int cboot_get_ethaddr(const void *fdt
, uint8_t mac
[ETH_ALEN
])
515 int node
, len
, err
= 0;
519 path
= fdt_get_alias(fdt
, "ethernet");
525 debug("ethernet alias found: %s\n", path
);
527 node
= fdt_path_offset(fdt
, path
);
533 prop
= fdt_getprop(fdt
, node
, "local-mac-address", &len
);
539 if (len
!= ETH_ALEN
) {
544 debug("MAC address: %pM\n", prop
);
545 memcpy(mac
, prop
, ETH_ALEN
);
549 err
= cboot_get_ethaddr_legacy(fdt
, mac
);
554 static char *strip(const char *ptr
)
558 while (*ptr
&& isblank(*ptr
))
570 while (isblank(*end
))
573 return strndup(ptr
, end
- ptr
+ 1);
576 static char *cboot_get_bootargs(const void *fdt
)
581 offset
= fdt_path_offset(fdt
, "/chosen");
585 args
= fdt_getprop(fdt
, offset
, "bootargs", &len
);
592 int cboot_late_init(void)
594 const void *fdt
= (const void *)cboot_boot_x0
;
595 uint8_t mac
[ETH_ALEN
];
599 set_calculated_env_vars();
601 * Ignore errors here; the value may not be used depending on
602 * extlinux.conf or boot script content.
606 /* Ignore errors here; not all cases care about Ethernet addresses */
607 err
= cboot_get_ethaddr(fdt
, mac
);
609 void *blob
= (void *)gd
->fdt_blob
;
611 err
= fdtdec_set_ethernet_mac_address(blob
, mac
, sizeof(mac
));
613 printf("failed to set MAC address %pM: %d\n", mac
, err
);
616 bootargs
= cboot_get_bootargs(fdt
);
618 env_set("cbootargs", bootargs
);