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