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 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 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 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 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 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 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 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 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 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 */ 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 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