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 } 336