ARM: Tegra186: calculate load addresses at boot
[platform/kernel/u-boot.git] / arch / arm / mach-tegra / tegra186 / nvtboot_board.c
1 /*
2  * Copyright (c) 2016-2018, NVIDIA CORPORATION.
3  *
4  * SPDX-License-Identifier: GPL-2.0+
5  */
6
7 #include <stdlib.h>
8 #include <common.h>
9 #include <fdt_support.h>
10 #include <fdtdec.h>
11 #include <asm/arch/tegra.h>
12 #include <asm/armv8/mmu.h>
13
14 DECLARE_GLOBAL_DATA_PTR;
15
16 extern unsigned long nvtboot_boot_x0;
17
18 /*
19  * The following few functions run late during the boot process and dynamically
20  * calculate the load address of various binaries. To keep track of multiple
21  * allocations, some writable list of RAM banks must be used. tegra_mem_map[]
22  * is used for this purpose to avoid making yet another copy of the list of RAM
23  * banks. This is safe because tegra_mem_map[] is only used once during very
24  * early boot to create U-Boot's page tables, long before this code runs. If
25  * this assumption becomes invalid later, we can just fix the code to copy the
26  * list of RAM banks into some private data structure before running.
27  */
28
29 extern struct mm_region tegra_mem_map[];
30
31 static char *gen_varname(const char *var, const char *ext)
32 {
33         size_t len_var = strlen(var);
34         size_t len_ext = strlen(ext);
35         size_t len = len_var + len_ext + 1;
36         char *varext = malloc(len);
37
38         if (!varext)
39                 return 0;
40         strcpy(varext, var);
41         strcpy(varext + len_var, ext);
42         return varext;
43 }
44
45 static void mark_ram_allocated(int bank, u64 allocated_start, u64 allocated_end)
46 {
47         u64 bank_start = tegra_mem_map[bank].virt;
48         u64 bank_size = tegra_mem_map[bank].size;
49         u64 bank_end = bank_start + bank_size;
50         bool keep_front = allocated_start != bank_start;
51         bool keep_tail = allocated_end != bank_end;
52
53         if (keep_front && keep_tail) {
54                 /*
55                  * There are CONFIG_NR_DRAM_BANKS DRAM entries in the array,
56                  * starting at index 1 (index 0 is MMIO). So, we are at DRAM
57                  * entry "bank" not "bank - 1" as for a typical 0-base array.
58                  * The number of remaining DRAM entries is therefore
59                  * "CONFIG_NR_DRAM_BANKS - bank". We want to duplicate the
60                  * current entry and shift up the remaining entries, dropping
61                  * the last one. Thus, we must copy one fewer entry than the
62                  * number remaining.
63                  */
64                 memmove(&tegra_mem_map[bank + 1], &tegra_mem_map[bank],
65                         CONFIG_NR_DRAM_BANKS - bank - 1);
66                 tegra_mem_map[bank].size = allocated_start - bank_start;
67                 bank++;
68                 tegra_mem_map[bank].virt = allocated_end;
69                 tegra_mem_map[bank].phys = allocated_end;
70                 tegra_mem_map[bank].size = bank_end - allocated_end;
71         } else if (keep_front) {
72                 tegra_mem_map[bank].size = allocated_start - bank_start;
73         } else if (keep_tail) {
74                 tegra_mem_map[bank].virt = allocated_end;
75                 tegra_mem_map[bank].phys = allocated_end;
76                 tegra_mem_map[bank].size = bank_end - allocated_end;
77         } else {
78                 /*
79                  * We could move all subsequent banks down in the array but
80                  * that's not necessary for subsequent allocations to work, so
81                  * we skip doing so.
82                  */
83                 tegra_mem_map[bank].size = 0;
84         }
85 }
86
87 static void reserve_ram(u64 start, u64 size)
88 {
89         int bank;
90         u64 end = start + size;
91
92         for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) {
93                 u64 bank_start = tegra_mem_map[bank].virt;
94                 u64 bank_size = tegra_mem_map[bank].size;
95                 u64 bank_end = bank_start + bank_size;
96
97                 if (end <= bank_start || start > bank_end)
98                         continue;
99                 mark_ram_allocated(bank, start, end);
100                 break;
101         }
102 }
103
104 static u64 alloc_ram(u64 size, u64 align, u64 offset)
105 {
106         int bank;
107
108         for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) {
109                 u64 bank_start = tegra_mem_map[bank].virt;
110                 u64 bank_size = tegra_mem_map[bank].size;
111                 u64 bank_end = bank_start + bank_size;
112                 u64 allocated = ROUND(bank_start, align) + offset;
113                 u64 allocated_end = allocated + size;
114
115                 if (allocated_end > bank_end)
116                         continue;
117                 mark_ram_allocated(bank, allocated, allocated_end);
118                 return allocated;
119         }
120         return 0;
121 }
122
123 static void set_calculated_aliases(char *aliases, u64 address)
124 {
125         char *tmp, *alias;
126         int err;
127
128         aliases = strdup(aliases);
129         if (!aliases) {
130                 pr_err("strdup(aliases) failed");
131                 return;
132         }
133
134         tmp = aliases;
135         while (true) {
136                 alias = strsep(&tmp, " ");
137                 if (!alias)
138                         break;
139                 debug("%s: alias: %s\n", __func__, alias);
140                 err = env_set_hex(alias, address);
141                 if (err)
142                         pr_err("Could not set %s\n", alias);
143         }
144
145         free(aliases);
146 }
147
148 static void set_calculated_env_var(const char *var)
149 {
150         char *var_size;
151         char *var_align;
152         char *var_offset;
153         char *var_aliases;
154         u64 size;
155         u64 align;
156         u64 offset;
157         char *aliases;
158         u64 address;
159         int err;
160
161         var_size = gen_varname(var, "_size");
162         if (!var_size)
163                 return;
164         var_align = gen_varname(var, "_align");
165         if (!var_align)
166                 goto out_free_var_size;
167         var_offset = gen_varname(var, "_offset");
168         if (!var_offset)
169                 goto out_free_var_align;
170         var_aliases = gen_varname(var, "_aliases");
171         if (!var_aliases)
172                 goto out_free_var_offset;
173
174         size = env_get_hex(var_size, 0);
175         if (!size) {
176                 pr_err("%s not set or zero\n", var_size);
177                 goto out_free_var_aliases;
178         }
179         align = env_get_hex(var_align, 1);
180         /* Handle extant variables, but with a value of 0 */
181         if (!align)
182                 align = 1;
183         offset = env_get_hex(var_offset, 0);
184         aliases = env_get(var_aliases);
185
186         debug("%s: Calc var %s; size=%llx, align=%llx, offset=%llx\n",
187               __func__, var, size, align, offset);
188         if (aliases)
189                 debug("%s: Aliases: %s\n", __func__, aliases);
190
191         address = alloc_ram(size, align, offset);
192         if (!address) {
193                 pr_err("Could not allocate %s\n", var);
194                 goto out_free_var_aliases;
195         }
196         debug("%s: Address %llx\n", __func__, address);
197
198         err = env_set_hex(var, address);
199         if (err)
200                 pr_err("Could not set %s\n", var);
201         if (aliases)
202                 set_calculated_aliases(aliases, address);
203
204 out_free_var_aliases:
205         free(var_aliases);
206 out_free_var_offset:
207         free(var_offset);
208 out_free_var_align:
209         free(var_align);
210 out_free_var_size:
211         free(var_size);
212 }
213
214 #ifdef DEBUG
215 static void dump_ram_banks(void)
216 {
217         int bank;
218
219         for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) {
220                 u64 bank_start = tegra_mem_map[bank].virt;
221                 u64 bank_size = tegra_mem_map[bank].size;
222                 u64 bank_end = bank_start + bank_size;
223
224                 if (!bank_size)
225                         continue;
226                 printf("%d: %010llx..%010llx (+%010llx)\n", bank - 1,
227                        bank_start, bank_end, bank_size);
228         }
229 }
230 #endif
231
232 static void set_calculated_env_vars(void)
233 {
234         char *vars, *tmp, *var;
235
236 #ifdef DEBUG
237         printf("RAM banks before any calculated env. var.s:\n");
238         dump_ram_banks();
239 #endif
240
241         reserve_ram(nvtboot_boot_x0, fdt_totalsize(nvtboot_boot_x0));
242
243 #ifdef DEBUG
244         printf("RAM after reserving cboot DTB:\n");
245         dump_ram_banks();
246 #endif
247
248         vars = env_get("calculated_vars");
249         if (!vars) {
250                 debug("%s: No env var calculated_vars\n", __func__);
251                 return;
252         }
253
254         vars = strdup(vars);
255         if (!vars) {
256                 pr_err("strdup(calculated_vars) failed");
257                 return;
258         }
259
260         tmp = vars;
261         while (true) {
262                 var = strsep(&tmp, " ");
263                 if (!var)
264                         break;
265                 debug("%s: var: %s\n", __func__, var);
266                 set_calculated_env_var(var);
267 #ifdef DEBUG
268                 printf("RAM banks affter allocating %s:\n", var);
269                 dump_ram_banks();
270 #endif
271         }
272
273         free(vars);
274 }
275
276 static int set_fdt_addr(void)
277 {
278         int ret;
279
280         ret = env_set_hex("fdt_addr", nvtboot_boot_x0);
281         if (ret) {
282                 printf("Failed to set fdt_addr to point at DTB: %d\n", ret);
283                 return ret;
284         }
285
286         return 0;
287 }
288
289 /*
290  * Attempt to use /chosen/nvidia,ether-mac in the nvtboot DTB to U-Boot's
291  * ethaddr environment variable if possible.
292  */
293 static int set_ethaddr_from_nvtboot(void)
294 {
295         const void *nvtboot_blob = (void *)nvtboot_boot_x0;
296         int ret, node, len;
297         const u32 *prop;
298
299         /* Already a valid address in the environment? If so, keep it */
300         if (env_get("ethaddr"))
301                 return 0;
302
303         node = fdt_path_offset(nvtboot_blob, "/chosen");
304         if (node < 0) {
305                 printf("Can't find /chosen node in nvtboot DTB\n");
306                 return node;
307         }
308         prop = fdt_getprop(nvtboot_blob, node, "nvidia,ether-mac", &len);
309         if (!prop) {
310                 printf("Can't find nvidia,ether-mac property in nvtboot DTB\n");
311                 return -ENOENT;
312         }
313
314         ret = env_set("ethaddr", (void *)prop);
315         if (ret) {
316                 printf("Failed to set ethaddr from nvtboot DTB: %d\n", ret);
317                 return ret;
318         }
319
320         return 0;
321 }
322
323 int tegra_soc_board_init_late(void)
324 {
325         set_calculated_env_vars();
326         /*
327          * Ignore errors here; the value may not be used depending on
328          * extlinux.conf or boot script content.
329          */
330         set_fdt_addr();
331         /* Ignore errors here; not all cases care about Ethernet addresses */
332         set_ethaddr_from_nvtboot();
333
334         return 0;
335 }