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