1 /* 2 * (C) Copyright 2003 3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 4 * 5 * SPDX-License-Identifier: GPL-2.0+ 6 */ 7 8 #include <common.h> 9 #include <image.h> 10 #include <fdt_support.h> 11 #include <asm/addrspace.h> 12 #include <asm/io.h> 13 14 DECLARE_GLOBAL_DATA_PTR; 15 16 #define LINUX_MAX_ENVS 256 17 #define LINUX_MAX_ARGS 256 18 19 static int linux_argc; 20 static char **linux_argv; 21 static char *linux_argp; 22 23 static char **linux_env; 24 static char *linux_env_p; 25 static int linux_env_idx; 26 27 static ulong arch_get_sp(void) 28 { 29 ulong ret; 30 31 __asm__ __volatile__("move %0, $sp" : "=r"(ret) : ); 32 33 return ret; 34 } 35 36 void arch_lmb_reserve(struct lmb *lmb) 37 { 38 ulong sp; 39 40 sp = arch_get_sp(); 41 debug("## Current stack ends at 0x%08lx\n", sp); 42 43 /* adjust sp by 4K to be safe */ 44 sp -= 4096; 45 lmb_reserve(lmb, sp, gd->ram_top - sp); 46 } 47 48 static void linux_cmdline_init(void) 49 { 50 linux_argc = 1; 51 linux_argv = (char **)UNCACHED_SDRAM(gd->bd->bi_boot_params); 52 linux_argv[0] = 0; 53 linux_argp = (char *)(linux_argv + LINUX_MAX_ARGS); 54 } 55 56 static void linux_cmdline_set(const char *value, size_t len) 57 { 58 linux_argv[linux_argc] = linux_argp; 59 memcpy(linux_argp, value, len); 60 linux_argp[len] = 0; 61 62 linux_argp += len + 1; 63 linux_argc++; 64 } 65 66 static void linux_cmdline_dump(void) 67 { 68 int i; 69 70 debug("## cmdline argv at 0x%p, argp at 0x%p\n", 71 linux_argv, linux_argp); 72 73 for (i = 1; i < linux_argc; i++) 74 debug(" arg %03d: %s\n", i, linux_argv[i]); 75 } 76 77 static void linux_cmdline_legacy(bootm_headers_t *images) 78 { 79 const char *bootargs, *next, *quote; 80 81 linux_cmdline_init(); 82 83 bootargs = env_get("bootargs"); 84 if (!bootargs) 85 return; 86 87 next = bootargs; 88 89 while (bootargs && *bootargs && linux_argc < LINUX_MAX_ARGS) { 90 quote = strchr(bootargs, '"'); 91 next = strchr(bootargs, ' '); 92 93 while (next && quote && quote < next) { 94 /* 95 * we found a left quote before the next blank 96 * now we have to find the matching right quote 97 */ 98 next = strchr(quote + 1, '"'); 99 if (next) { 100 quote = strchr(next + 1, '"'); 101 next = strchr(next + 1, ' '); 102 } 103 } 104 105 if (!next) 106 next = bootargs + strlen(bootargs); 107 108 linux_cmdline_set(bootargs, next - bootargs); 109 110 if (*next) 111 next++; 112 113 bootargs = next; 114 } 115 } 116 117 static void linux_cmdline_append(bootm_headers_t *images) 118 { 119 char buf[24]; 120 ulong mem, rd_start, rd_size; 121 122 /* append mem */ 123 mem = gd->ram_size >> 20; 124 sprintf(buf, "mem=%luM", mem); 125 linux_cmdline_set(buf, strlen(buf)); 126 127 /* append rd_start and rd_size */ 128 rd_start = images->initrd_start; 129 rd_size = images->initrd_end - images->initrd_start; 130 131 if (rd_size) { 132 sprintf(buf, "rd_start=0x%08lX", rd_start); 133 linux_cmdline_set(buf, strlen(buf)); 134 sprintf(buf, "rd_size=0x%lX", rd_size); 135 linux_cmdline_set(buf, strlen(buf)); 136 } 137 } 138 139 static void linux_env_init(void) 140 { 141 linux_env = (char **)(((ulong) linux_argp + 15) & ~15); 142 linux_env[0] = 0; 143 linux_env_p = (char *)(linux_env + LINUX_MAX_ENVS); 144 linux_env_idx = 0; 145 } 146 147 static void linux_env_set(const char *env_name, const char *env_val) 148 { 149 if (linux_env_idx < LINUX_MAX_ENVS - 1) { 150 linux_env[linux_env_idx] = linux_env_p; 151 152 strcpy(linux_env_p, env_name); 153 linux_env_p += strlen(env_name); 154 155 if (CONFIG_IS_ENABLED(MALTA)) { 156 linux_env_p++; 157 linux_env[++linux_env_idx] = linux_env_p; 158 } else { 159 *linux_env_p++ = '='; 160 } 161 162 strcpy(linux_env_p, env_val); 163 linux_env_p += strlen(env_val); 164 165 linux_env_p++; 166 linux_env[++linux_env_idx] = 0; 167 } 168 } 169 170 static void linux_env_legacy(bootm_headers_t *images) 171 { 172 char env_buf[12]; 173 const char *cp; 174 ulong rd_start, rd_size; 175 176 if (CONFIG_IS_ENABLED(MEMSIZE_IN_BYTES)) { 177 sprintf(env_buf, "%lu", (ulong)gd->ram_size); 178 debug("## Giving linux memsize in bytes, %lu\n", 179 (ulong)gd->ram_size); 180 } else { 181 sprintf(env_buf, "%lu", (ulong)(gd->ram_size >> 20)); 182 debug("## Giving linux memsize in MB, %lu\n", 183 (ulong)(gd->ram_size >> 20)); 184 } 185 186 rd_start = UNCACHED_SDRAM(images->initrd_start); 187 rd_size = images->initrd_end - images->initrd_start; 188 189 linux_env_init(); 190 191 linux_env_set("memsize", env_buf); 192 193 sprintf(env_buf, "0x%08lX", rd_start); 194 linux_env_set("initrd_start", env_buf); 195 196 sprintf(env_buf, "0x%lX", rd_size); 197 linux_env_set("initrd_size", env_buf); 198 199 sprintf(env_buf, "0x%08X", (uint) (gd->bd->bi_flashstart)); 200 linux_env_set("flash_start", env_buf); 201 202 sprintf(env_buf, "0x%X", (uint) (gd->bd->bi_flashsize)); 203 linux_env_set("flash_size", env_buf); 204 205 cp = env_get("ethaddr"); 206 if (cp) 207 linux_env_set("ethaddr", cp); 208 209 cp = env_get("eth1addr"); 210 if (cp) 211 linux_env_set("eth1addr", cp); 212 213 if (CONFIG_IS_ENABLED(MALTA)) { 214 sprintf(env_buf, "%un8r", gd->baudrate); 215 linux_env_set("modetty0", env_buf); 216 } 217 } 218 219 static int boot_reloc_ramdisk(bootm_headers_t *images) 220 { 221 ulong rd_len = images->rd_end - images->rd_start; 222 223 /* 224 * In case of legacy uImage's, relocation of ramdisk is already done 225 * by do_bootm_states() and should not repeated in 'bootm prep'. 226 */ 227 if (images->state & BOOTM_STATE_RAMDISK) { 228 debug("## Ramdisk already relocated\n"); 229 return 0; 230 } 231 232 return boot_ramdisk_high(&images->lmb, images->rd_start, 233 rd_len, &images->initrd_start, &images->initrd_end); 234 } 235 236 static int boot_reloc_fdt(bootm_headers_t *images) 237 { 238 /* 239 * In case of legacy uImage's, relocation of FDT is already done 240 * by do_bootm_states() and should not repeated in 'bootm prep'. 241 */ 242 if (images->state & BOOTM_STATE_FDT) { 243 debug("## FDT already relocated\n"); 244 return 0; 245 } 246 247 #if CONFIG_IS_ENABLED(MIPS_BOOT_FDT) && CONFIG_IS_ENABLED(OF_LIBFDT) 248 boot_fdt_add_mem_rsv_regions(&images->lmb, images->ft_addr); 249 return boot_relocate_fdt(&images->lmb, &images->ft_addr, 250 &images->ft_len); 251 #else 252 return 0; 253 #endif 254 } 255 256 int arch_fixup_fdt(void *blob) 257 { 258 #if CONFIG_IS_ENABLED(MIPS_BOOT_FDT) && CONFIG_IS_ENABLED(OF_LIBFDT) 259 u64 mem_start = virt_to_phys((void *)gd->bd->bi_memstart); 260 u64 mem_size = gd->ram_size; 261 262 return fdt_fixup_memory_banks(blob, &mem_start, &mem_size, 1); 263 #else 264 return 0; 265 #endif 266 } 267 268 static int boot_setup_fdt(bootm_headers_t *images) 269 { 270 return image_setup_libfdt(images, images->ft_addr, images->ft_len, 271 &images->lmb); 272 } 273 274 static void boot_prep_linux(bootm_headers_t *images) 275 { 276 boot_reloc_ramdisk(images); 277 278 if (CONFIG_IS_ENABLED(MIPS_BOOT_FDT) && images->ft_len) { 279 boot_reloc_fdt(images); 280 boot_setup_fdt(images); 281 } else { 282 if (CONFIG_IS_ENABLED(MIPS_BOOT_CMDLINE_LEGACY)) { 283 linux_cmdline_legacy(images); 284 285 if (!CONFIG_IS_ENABLED(MIPS_BOOT_ENV_LEGACY)) 286 linux_cmdline_append(images); 287 288 linux_cmdline_dump(); 289 } 290 291 if (CONFIG_IS_ENABLED(MIPS_BOOT_ENV_LEGACY)) 292 linux_env_legacy(images); 293 } 294 } 295 296 static void boot_jump_linux(bootm_headers_t *images) 297 { 298 typedef void __noreturn (*kernel_entry_t)(int, ulong, ulong, ulong); 299 kernel_entry_t kernel = (kernel_entry_t) images->ep; 300 ulong linux_extra = 0; 301 302 debug("## Transferring control to Linux (at address %p) ...\n", kernel); 303 304 bootstage_mark(BOOTSTAGE_ID_RUN_OS); 305 306 if (CONFIG_IS_ENABLED(MALTA)) 307 linux_extra = gd->ram_size; 308 309 #if CONFIG_IS_ENABLED(BOOTSTAGE_FDT) 310 bootstage_fdt_add_report(); 311 #endif 312 #if CONFIG_IS_ENABLED(BOOTSTAGE_REPORT) 313 bootstage_report(); 314 #endif 315 316 if (images->ft_len) 317 kernel(-2, (ulong)images->ft_addr, 0, 0); 318 else 319 kernel(linux_argc, (ulong)linux_argv, (ulong)linux_env, 320 linux_extra); 321 } 322 323 int do_bootm_linux(int flag, int argc, char * const argv[], 324 bootm_headers_t *images) 325 { 326 /* No need for those on MIPS */ 327 if (flag & BOOTM_STATE_OS_BD_T) 328 return -1; 329 330 /* 331 * Cmdline init has been moved to 'bootm prep' because it has to be 332 * done after relocation of ramdisk to always pass correct values 333 * for rd_start and rd_size to Linux kernel. 334 */ 335 if (flag & BOOTM_STATE_OS_CMDLINE) 336 return 0; 337 338 if (flag & BOOTM_STATE_OS_PREP) { 339 boot_prep_linux(images); 340 return 0; 341 } 342 343 if (flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)) { 344 boot_jump_linux(images); 345 return 0; 346 } 347 348 /* does not return */ 349 return 1; 350 } 351