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