1ea0364f1SPeter Tyser /* 2ea0364f1SPeter Tyser * (C) Copyright 2003 3ea0364f1SPeter Tyser * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 4ea0364f1SPeter Tyser * 51a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 6ea0364f1SPeter Tyser */ 7ea0364f1SPeter Tyser 8ea0364f1SPeter Tyser #include <common.h> 9ea0364f1SPeter Tyser #include <image.h> 10ea0364f1SPeter Tyser #include <asm/addrspace.h> 11ea0364f1SPeter Tyser 12ea0364f1SPeter Tyser DECLARE_GLOBAL_DATA_PTR; 13ea0364f1SPeter Tyser 14ea0364f1SPeter Tyser #define LINUX_MAX_ENVS 256 15ea0364f1SPeter Tyser #define LINUX_MAX_ARGS 256 16ea0364f1SPeter Tyser 177a9d109bSPaul Burton #if defined(CONFIG_MALTA) 187a9d109bSPaul Burton #define mips_boot_malta 1 19b87493f4SDaniel Schwierzeck #else 207a9d109bSPaul Burton #define mips_boot_malta 0 21b87493f4SDaniel Schwierzeck #endif 22b87493f4SDaniel Schwierzeck 2325fc664fSDaniel Schwierzeck #if defined(CONFIG_MIPS_BOOT_CMDLINE_LEGACY) 2425fc664fSDaniel Schwierzeck #define mips_boot_cmdline_legacy 1 2525fc664fSDaniel Schwierzeck #else 2625fc664fSDaniel Schwierzeck #define mips_boot_cmdline_legacy 0 2725fc664fSDaniel Schwierzeck #endif 2825fc664fSDaniel Schwierzeck 29ca65e585SDaniel Schwierzeck #if defined(CONFIG_MIPS_BOOT_ENV_LEGACY) 30ca65e585SDaniel Schwierzeck #define mips_boot_env_legacy 1 31ca65e585SDaniel Schwierzeck #else 32ca65e585SDaniel Schwierzeck #define mips_boot_env_legacy 0 33ca65e585SDaniel Schwierzeck #endif 34ca65e585SDaniel Schwierzeck 35ea0364f1SPeter Tyser static int linux_argc; 36ea0364f1SPeter Tyser static char **linux_argv; 3759e8cbdbSDaniel Schwierzeck static char *linux_argp; 38ea0364f1SPeter Tyser 39ea0364f1SPeter Tyser static char **linux_env; 40ea0364f1SPeter Tyser static char *linux_env_p; 41ea0364f1SPeter Tyser static int linux_env_idx; 42ea0364f1SPeter Tyser 43f66cc1e3SDaniel Schwierzeck static ulong arch_get_sp(void) 44f66cc1e3SDaniel Schwierzeck { 45f66cc1e3SDaniel Schwierzeck ulong ret; 46f66cc1e3SDaniel Schwierzeck 47f66cc1e3SDaniel Schwierzeck __asm__ __volatile__("move %0, $sp" : "=r"(ret) : ); 48f66cc1e3SDaniel Schwierzeck 49f66cc1e3SDaniel Schwierzeck return ret; 50f66cc1e3SDaniel Schwierzeck } 51f66cc1e3SDaniel Schwierzeck 52f66cc1e3SDaniel Schwierzeck void arch_lmb_reserve(struct lmb *lmb) 53f66cc1e3SDaniel Schwierzeck { 54f66cc1e3SDaniel Schwierzeck ulong sp; 55f66cc1e3SDaniel Schwierzeck 56f66cc1e3SDaniel Schwierzeck sp = arch_get_sp(); 57f66cc1e3SDaniel Schwierzeck debug("## Current stack ends at 0x%08lx\n", sp); 58f66cc1e3SDaniel Schwierzeck 59f66cc1e3SDaniel Schwierzeck /* adjust sp by 4K to be safe */ 60f66cc1e3SDaniel Schwierzeck sp -= 4096; 61f66cc1e3SDaniel Schwierzeck lmb_reserve(lmb, sp, CONFIG_SYS_SDRAM_BASE + gd->ram_size - sp); 62f66cc1e3SDaniel Schwierzeck } 63f66cc1e3SDaniel Schwierzeck 64c9639421SDaniel Schwierzeck static int boot_setup_linux(bootm_headers_t *images) 65c9639421SDaniel Schwierzeck { 66c9639421SDaniel Schwierzeck int ret; 67c9639421SDaniel Schwierzeck ulong rd_len; 68c9639421SDaniel Schwierzeck 69c9639421SDaniel Schwierzeck rd_len = images->rd_end - images->rd_start; 70c9639421SDaniel Schwierzeck ret = boot_ramdisk_high(&images->lmb, images->rd_start, 71c9639421SDaniel Schwierzeck rd_len, &images->initrd_start, &images->initrd_end); 72c9639421SDaniel Schwierzeck if (ret) 73c9639421SDaniel Schwierzeck return ret; 74c9639421SDaniel Schwierzeck 75c9639421SDaniel Schwierzeck return 0; 76c9639421SDaniel Schwierzeck } 77c9639421SDaniel Schwierzeck 7859e8cbdbSDaniel Schwierzeck static void linux_cmdline_init(void) 7959e8cbdbSDaniel Schwierzeck { 8059e8cbdbSDaniel Schwierzeck linux_argc = 1; 8159e8cbdbSDaniel Schwierzeck linux_argv = (char **)UNCACHED_SDRAM(gd->bd->bi_boot_params); 8259e8cbdbSDaniel Schwierzeck linux_argv[0] = 0; 8359e8cbdbSDaniel Schwierzeck linux_argp = (char *)(linux_argv + LINUX_MAX_ARGS); 8459e8cbdbSDaniel Schwierzeck } 8559e8cbdbSDaniel Schwierzeck 8659e8cbdbSDaniel Schwierzeck static void linux_cmdline_set(const char *value, size_t len) 8759e8cbdbSDaniel Schwierzeck { 8859e8cbdbSDaniel Schwierzeck linux_argv[linux_argc] = linux_argp; 8959e8cbdbSDaniel Schwierzeck memcpy(linux_argp, value, len); 9059e8cbdbSDaniel Schwierzeck linux_argp[len] = 0; 9159e8cbdbSDaniel Schwierzeck 9259e8cbdbSDaniel Schwierzeck linux_argp += len + 1; 9359e8cbdbSDaniel Schwierzeck linux_argc++; 9459e8cbdbSDaniel Schwierzeck } 9559e8cbdbSDaniel Schwierzeck 9659e8cbdbSDaniel Schwierzeck static void linux_cmdline_dump(void) 9759e8cbdbSDaniel Schwierzeck { 9859e8cbdbSDaniel Schwierzeck int i; 9959e8cbdbSDaniel Schwierzeck 10059e8cbdbSDaniel Schwierzeck debug("## cmdline argv at 0x%p, argp at 0x%p\n", 10159e8cbdbSDaniel Schwierzeck linux_argv, linux_argp); 10259e8cbdbSDaniel Schwierzeck 10359e8cbdbSDaniel Schwierzeck for (i = 1; i < linux_argc; i++) 10459e8cbdbSDaniel Schwierzeck debug(" arg %03d: %s\n", i, linux_argv[i]); 10559e8cbdbSDaniel Schwierzeck } 10659e8cbdbSDaniel Schwierzeck 10725fc664fSDaniel Schwierzeck static void linux_cmdline_legacy(bootm_headers_t *images) 10859e8cbdbSDaniel Schwierzeck { 10959e8cbdbSDaniel Schwierzeck const char *bootargs, *next, *quote; 11059e8cbdbSDaniel Schwierzeck 11159e8cbdbSDaniel Schwierzeck linux_cmdline_init(); 11259e8cbdbSDaniel Schwierzeck 11359e8cbdbSDaniel Schwierzeck bootargs = getenv("bootargs"); 11459e8cbdbSDaniel Schwierzeck if (!bootargs) 11559e8cbdbSDaniel Schwierzeck return; 11659e8cbdbSDaniel Schwierzeck 11759e8cbdbSDaniel Schwierzeck next = bootargs; 11859e8cbdbSDaniel Schwierzeck 11959e8cbdbSDaniel Schwierzeck while (bootargs && *bootargs && linux_argc < LINUX_MAX_ARGS) { 12059e8cbdbSDaniel Schwierzeck quote = strchr(bootargs, '"'); 12159e8cbdbSDaniel Schwierzeck next = strchr(bootargs, ' '); 12259e8cbdbSDaniel Schwierzeck 12359e8cbdbSDaniel Schwierzeck while (next && quote && quote < next) { 12459e8cbdbSDaniel Schwierzeck /* 12559e8cbdbSDaniel Schwierzeck * we found a left quote before the next blank 12659e8cbdbSDaniel Schwierzeck * now we have to find the matching right quote 12759e8cbdbSDaniel Schwierzeck */ 12859e8cbdbSDaniel Schwierzeck next = strchr(quote + 1, '"'); 12959e8cbdbSDaniel Schwierzeck if (next) { 13059e8cbdbSDaniel Schwierzeck quote = strchr(next + 1, '"'); 13159e8cbdbSDaniel Schwierzeck next = strchr(next + 1, ' '); 13259e8cbdbSDaniel Schwierzeck } 13359e8cbdbSDaniel Schwierzeck } 13459e8cbdbSDaniel Schwierzeck 13559e8cbdbSDaniel Schwierzeck if (!next) 13659e8cbdbSDaniel Schwierzeck next = bootargs + strlen(bootargs); 13759e8cbdbSDaniel Schwierzeck 13859e8cbdbSDaniel Schwierzeck linux_cmdline_set(bootargs, next - bootargs); 13959e8cbdbSDaniel Schwierzeck 14059e8cbdbSDaniel Schwierzeck if (*next) 14159e8cbdbSDaniel Schwierzeck next++; 14259e8cbdbSDaniel Schwierzeck 14359e8cbdbSDaniel Schwierzeck bootargs = next; 14459e8cbdbSDaniel Schwierzeck } 14525fc664fSDaniel Schwierzeck } 14659e8cbdbSDaniel Schwierzeck 147*8cec725aSDaniel Schwierzeck static void linux_cmdline_append(bootm_headers_t *images) 148*8cec725aSDaniel Schwierzeck { 149*8cec725aSDaniel Schwierzeck char buf[24]; 150*8cec725aSDaniel Schwierzeck ulong mem, rd_start, rd_size; 151*8cec725aSDaniel Schwierzeck 152*8cec725aSDaniel Schwierzeck /* append mem */ 153*8cec725aSDaniel Schwierzeck mem = gd->ram_size >> 20; 154*8cec725aSDaniel Schwierzeck sprintf(buf, "mem=%luM", mem); 155*8cec725aSDaniel Schwierzeck linux_cmdline_set(buf, strlen(buf)); 156*8cec725aSDaniel Schwierzeck 157*8cec725aSDaniel Schwierzeck /* append rd_start and rd_size */ 158*8cec725aSDaniel Schwierzeck rd_start = images->initrd_start; 159*8cec725aSDaniel Schwierzeck rd_size = images->initrd_end - images->initrd_start; 160*8cec725aSDaniel Schwierzeck 161*8cec725aSDaniel Schwierzeck if (rd_size) { 162*8cec725aSDaniel Schwierzeck sprintf(buf, "rd_start=0x%08lX", rd_start); 163*8cec725aSDaniel Schwierzeck linux_cmdline_set(buf, strlen(buf)); 164*8cec725aSDaniel Schwierzeck sprintf(buf, "rd_size=0x%lX", rd_size); 165*8cec725aSDaniel Schwierzeck linux_cmdline_set(buf, strlen(buf)); 166*8cec725aSDaniel Schwierzeck } 167*8cec725aSDaniel Schwierzeck } 168*8cec725aSDaniel Schwierzeck 16925fc664fSDaniel Schwierzeck static void boot_cmdline_linux(bootm_headers_t *images) 17025fc664fSDaniel Schwierzeck { 17125fc664fSDaniel Schwierzeck if (mips_boot_cmdline_legacy) { 17225fc664fSDaniel Schwierzeck linux_cmdline_legacy(images); 173*8cec725aSDaniel Schwierzeck 174*8cec725aSDaniel Schwierzeck if (!mips_boot_env_legacy) 175*8cec725aSDaniel Schwierzeck linux_cmdline_append(images); 176*8cec725aSDaniel Schwierzeck 17759e8cbdbSDaniel Schwierzeck linux_cmdline_dump(); 17859e8cbdbSDaniel Schwierzeck } 17925fc664fSDaniel Schwierzeck } 18059e8cbdbSDaniel Schwierzeck 18115f8aa90SDaniel Schwierzeck static void linux_env_init(void) 18215f8aa90SDaniel Schwierzeck { 18315f8aa90SDaniel Schwierzeck linux_env = (char **)(((ulong) linux_argp + 15) & ~15); 18415f8aa90SDaniel Schwierzeck linux_env[0] = 0; 18515f8aa90SDaniel Schwierzeck linux_env_p = (char *)(linux_env + LINUX_MAX_ENVS); 18615f8aa90SDaniel Schwierzeck linux_env_idx = 0; 18715f8aa90SDaniel Schwierzeck } 18815f8aa90SDaniel Schwierzeck 18915f8aa90SDaniel Schwierzeck static void linux_env_set(const char *env_name, const char *env_val) 19015f8aa90SDaniel Schwierzeck { 19115f8aa90SDaniel Schwierzeck if (linux_env_idx < LINUX_MAX_ENVS - 1) { 19215f8aa90SDaniel Schwierzeck linux_env[linux_env_idx] = linux_env_p; 19315f8aa90SDaniel Schwierzeck 19415f8aa90SDaniel Schwierzeck strcpy(linux_env_p, env_name); 19515f8aa90SDaniel Schwierzeck linux_env_p += strlen(env_name); 19615f8aa90SDaniel Schwierzeck 1977a9d109bSPaul Burton if (mips_boot_malta) { 198b87493f4SDaniel Schwierzeck linux_env_p++; 199b87493f4SDaniel Schwierzeck linux_env[++linux_env_idx] = linux_env_p; 200b87493f4SDaniel Schwierzeck } else { 20115f8aa90SDaniel Schwierzeck *linux_env_p++ = '='; 202b87493f4SDaniel Schwierzeck } 20315f8aa90SDaniel Schwierzeck 20415f8aa90SDaniel Schwierzeck strcpy(linux_env_p, env_val); 20515f8aa90SDaniel Schwierzeck linux_env_p += strlen(env_val); 20615f8aa90SDaniel Schwierzeck 20715f8aa90SDaniel Schwierzeck linux_env_p++; 20815f8aa90SDaniel Schwierzeck linux_env[++linux_env_idx] = 0; 20915f8aa90SDaniel Schwierzeck } 21015f8aa90SDaniel Schwierzeck } 21115f8aa90SDaniel Schwierzeck 212ca65e585SDaniel Schwierzeck static void linux_env_legacy(bootm_headers_t *images) 213ea0364f1SPeter Tyser { 214ea0364f1SPeter Tyser char env_buf[12]; 21515f8aa90SDaniel Schwierzeck const char *cp; 2166c154552SDaniel Schwierzeck ulong rd_start, rd_size; 217ea0364f1SPeter Tyser 218ea0364f1SPeter Tyser #ifdef CONFIG_MEMSIZE_IN_BYTES 219ea0364f1SPeter Tyser sprintf(env_buf, "%lu", (ulong)gd->ram_size); 220ea0364f1SPeter Tyser debug("## Giving linux memsize in bytes, %lu\n", (ulong)gd->ram_size); 221ea0364f1SPeter Tyser #else 222ea0364f1SPeter Tyser sprintf(env_buf, "%lu", (ulong)(gd->ram_size >> 20)); 223e51a6b7aSDaniel Schwierzeck debug("## Giving linux memsize in MB, %lu\n", 224e51a6b7aSDaniel Schwierzeck (ulong)(gd->ram_size >> 20)); 225ea0364f1SPeter Tyser #endif /* CONFIG_MEMSIZE_IN_BYTES */ 226ea0364f1SPeter Tyser 2276c154552SDaniel Schwierzeck rd_start = UNCACHED_SDRAM(images->initrd_start); 2286c154552SDaniel Schwierzeck rd_size = images->initrd_end - images->initrd_start; 2296c154552SDaniel Schwierzeck 23015f8aa90SDaniel Schwierzeck linux_env_init(); 23115f8aa90SDaniel Schwierzeck 232ea0364f1SPeter Tyser linux_env_set("memsize", env_buf); 233ea0364f1SPeter Tyser 2346c154552SDaniel Schwierzeck sprintf(env_buf, "0x%08lX", rd_start); 235ea0364f1SPeter Tyser linux_env_set("initrd_start", env_buf); 236ea0364f1SPeter Tyser 2376c154552SDaniel Schwierzeck sprintf(env_buf, "0x%lX", rd_size); 238ea0364f1SPeter Tyser linux_env_set("initrd_size", env_buf); 239ea0364f1SPeter Tyser 240ea0364f1SPeter Tyser sprintf(env_buf, "0x%08X", (uint) (gd->bd->bi_flashstart)); 241ea0364f1SPeter Tyser linux_env_set("flash_start", env_buf); 242ea0364f1SPeter Tyser 243ea0364f1SPeter Tyser sprintf(env_buf, "0x%X", (uint) (gd->bd->bi_flashsize)); 244ea0364f1SPeter Tyser linux_env_set("flash_size", env_buf); 245ea0364f1SPeter Tyser 246ea0364f1SPeter Tyser cp = getenv("ethaddr"); 247e51a6b7aSDaniel Schwierzeck if (cp) 248ea0364f1SPeter Tyser linux_env_set("ethaddr", cp); 249ea0364f1SPeter Tyser 250ea0364f1SPeter Tyser cp = getenv("eth1addr"); 251e51a6b7aSDaniel Schwierzeck if (cp) 252ea0364f1SPeter Tyser linux_env_set("eth1addr", cp); 253b87493f4SDaniel Schwierzeck 254d18d49d7SPaul Burton if (mips_boot_malta) { 255d18d49d7SPaul Burton sprintf(env_buf, "%un8r", gd->baudrate); 256d18d49d7SPaul Burton linux_env_set("modetty0", env_buf); 257d18d49d7SPaul Burton } 2580ea7213fSGabor Juhos } 259ea0364f1SPeter Tyser 260ca65e585SDaniel Schwierzeck static void boot_prep_linux(bootm_headers_t *images) 261ca65e585SDaniel Schwierzeck { 262ca65e585SDaniel Schwierzeck if (mips_boot_env_legacy) 263ca65e585SDaniel Schwierzeck linux_env_legacy(images); 264ca65e585SDaniel Schwierzeck } 265ca65e585SDaniel Schwierzeck 2660ea7213fSGabor Juhos static void boot_jump_linux(bootm_headers_t *images) 2670ea7213fSGabor Juhos { 268c4b37847SDaniel Schwierzeck typedef void __noreturn (*kernel_entry_t)(int, ulong, ulong, ulong); 269c4b37847SDaniel Schwierzeck kernel_entry_t kernel = (kernel_entry_t) images->ep; 270b87493f4SDaniel Schwierzeck ulong linux_extra = 0; 2710ea7213fSGabor Juhos 272c4b37847SDaniel Schwierzeck debug("## Transferring control to Linux (at address %p) ...\n", kernel); 2730ea7213fSGabor Juhos 2740ea7213fSGabor Juhos bootstage_mark(BOOTSTAGE_ID_RUN_OS); 2750ea7213fSGabor Juhos 2767a9d109bSPaul Burton if (mips_boot_malta) 277b87493f4SDaniel Schwierzeck linux_extra = gd->ram_size; 278b87493f4SDaniel Schwierzeck 2790ea7213fSGabor Juhos /* we assume that the kernel is in place */ 2800ea7213fSGabor Juhos printf("\nStarting kernel ...\n\n"); 2810ea7213fSGabor Juhos 282b87493f4SDaniel Schwierzeck kernel(linux_argc, (ulong)linux_argv, (ulong)linux_env, linux_extra); 2830ea7213fSGabor Juhos } 2840ea7213fSGabor Juhos 2850ea7213fSGabor Juhos int do_bootm_linux(int flag, int argc, char * const argv[], 2860ea7213fSGabor Juhos bootm_headers_t *images) 2870ea7213fSGabor Juhos { 288c9639421SDaniel Schwierzeck int ret; 289c9639421SDaniel Schwierzeck 2909c170e2eSGabor Juhos /* No need for those on MIPS */ 29159e8cbdbSDaniel Schwierzeck if (flag & BOOTM_STATE_OS_BD_T) 2929c170e2eSGabor Juhos return -1; 2939c170e2eSGabor Juhos 29459e8cbdbSDaniel Schwierzeck if (flag & BOOTM_STATE_OS_CMDLINE) { 29559e8cbdbSDaniel Schwierzeck boot_cmdline_linux(images); 29659e8cbdbSDaniel Schwierzeck return 0; 29759e8cbdbSDaniel Schwierzeck } 29859e8cbdbSDaniel Schwierzeck 2999c170e2eSGabor Juhos if (flag & BOOTM_STATE_OS_PREP) { 3009c170e2eSGabor Juhos boot_prep_linux(images); 3019c170e2eSGabor Juhos return 0; 3029c170e2eSGabor Juhos } 3039c170e2eSGabor Juhos 3049c170e2eSGabor Juhos if (flag & BOOTM_STATE_OS_GO) { 3059c170e2eSGabor Juhos boot_jump_linux(images); 3069c170e2eSGabor Juhos return 0; 3079c170e2eSGabor Juhos } 3080ea7213fSGabor Juhos 309c9639421SDaniel Schwierzeck ret = boot_setup_linux(images); 310c9639421SDaniel Schwierzeck if (ret) 311c9639421SDaniel Schwierzeck return ret; 312c9639421SDaniel Schwierzeck 31359e8cbdbSDaniel Schwierzeck boot_cmdline_linux(images); 3140ea7213fSGabor Juhos boot_prep_linux(images); 315e08634c7SGabor Juhos boot_jump_linux(images); 316e51a6b7aSDaniel Schwierzeck 317ea0364f1SPeter Tyser /* does not return */ 318ea0364f1SPeter Tyser return 1; 319ea0364f1SPeter Tyser } 320