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