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 <command.h> 10ea0364f1SPeter Tyser #include <image.h> 11ea0364f1SPeter Tyser #include <u-boot/zlib.h> 12ea0364f1SPeter Tyser #include <asm/byteorder.h> 13ea0364f1SPeter Tyser #include <asm/addrspace.h> 14ea0364f1SPeter Tyser 15ea0364f1SPeter Tyser DECLARE_GLOBAL_DATA_PTR; 16ea0364f1SPeter Tyser 17ea0364f1SPeter Tyser #define LINUX_MAX_ENVS 256 18ea0364f1SPeter Tyser #define LINUX_MAX_ARGS 256 19ea0364f1SPeter Tyser 207a9d109bSPaul Burton #if defined(CONFIG_MALTA) 217a9d109bSPaul Burton #define mips_boot_malta 1 22b87493f4SDaniel Schwierzeck #else 237a9d109bSPaul Burton #define mips_boot_malta 0 24b87493f4SDaniel Schwierzeck #endif 25b87493f4SDaniel Schwierzeck 26ea0364f1SPeter Tyser static int linux_argc; 27ea0364f1SPeter Tyser static char **linux_argv; 2859e8cbdbSDaniel Schwierzeck static char *linux_argp; 29ea0364f1SPeter Tyser 30ea0364f1SPeter Tyser static char **linux_env; 31ea0364f1SPeter Tyser static char *linux_env_p; 32ea0364f1SPeter Tyser static int linux_env_idx; 33ea0364f1SPeter Tyser 34f66cc1e3SDaniel Schwierzeck static ulong arch_get_sp(void) 35f66cc1e3SDaniel Schwierzeck { 36f66cc1e3SDaniel Schwierzeck ulong ret; 37f66cc1e3SDaniel Schwierzeck 38f66cc1e3SDaniel Schwierzeck __asm__ __volatile__("move %0, $sp" : "=r"(ret) : ); 39f66cc1e3SDaniel Schwierzeck 40f66cc1e3SDaniel Schwierzeck return ret; 41f66cc1e3SDaniel Schwierzeck } 42f66cc1e3SDaniel Schwierzeck 43f66cc1e3SDaniel Schwierzeck void arch_lmb_reserve(struct lmb *lmb) 44f66cc1e3SDaniel Schwierzeck { 45f66cc1e3SDaniel Schwierzeck ulong sp; 46f66cc1e3SDaniel Schwierzeck 47f66cc1e3SDaniel Schwierzeck sp = arch_get_sp(); 48f66cc1e3SDaniel Schwierzeck debug("## Current stack ends at 0x%08lx\n", sp); 49f66cc1e3SDaniel Schwierzeck 50f66cc1e3SDaniel Schwierzeck /* adjust sp by 4K to be safe */ 51f66cc1e3SDaniel Schwierzeck sp -= 4096; 52f66cc1e3SDaniel Schwierzeck lmb_reserve(lmb, sp, CONFIG_SYS_SDRAM_BASE + gd->ram_size - sp); 53f66cc1e3SDaniel Schwierzeck } 54f66cc1e3SDaniel Schwierzeck 55*c9639421SDaniel Schwierzeck static int boot_setup_linux(bootm_headers_t *images) 56*c9639421SDaniel Schwierzeck { 57*c9639421SDaniel Schwierzeck int ret; 58*c9639421SDaniel Schwierzeck ulong rd_len; 59*c9639421SDaniel Schwierzeck 60*c9639421SDaniel Schwierzeck rd_len = images->rd_end - images->rd_start; 61*c9639421SDaniel Schwierzeck ret = boot_ramdisk_high(&images->lmb, images->rd_start, 62*c9639421SDaniel Schwierzeck rd_len, &images->initrd_start, &images->initrd_end); 63*c9639421SDaniel Schwierzeck if (ret) 64*c9639421SDaniel Schwierzeck return ret; 65*c9639421SDaniel Schwierzeck 66*c9639421SDaniel Schwierzeck return 0; 67*c9639421SDaniel Schwierzeck } 68*c9639421SDaniel Schwierzeck 6959e8cbdbSDaniel Schwierzeck static void linux_cmdline_init(void) 7059e8cbdbSDaniel Schwierzeck { 7159e8cbdbSDaniel Schwierzeck linux_argc = 1; 7259e8cbdbSDaniel Schwierzeck linux_argv = (char **)UNCACHED_SDRAM(gd->bd->bi_boot_params); 7359e8cbdbSDaniel Schwierzeck linux_argv[0] = 0; 7459e8cbdbSDaniel Schwierzeck linux_argp = (char *)(linux_argv + LINUX_MAX_ARGS); 7559e8cbdbSDaniel Schwierzeck } 7659e8cbdbSDaniel Schwierzeck 7759e8cbdbSDaniel Schwierzeck static void linux_cmdline_set(const char *value, size_t len) 7859e8cbdbSDaniel Schwierzeck { 7959e8cbdbSDaniel Schwierzeck linux_argv[linux_argc] = linux_argp; 8059e8cbdbSDaniel Schwierzeck memcpy(linux_argp, value, len); 8159e8cbdbSDaniel Schwierzeck linux_argp[len] = 0; 8259e8cbdbSDaniel Schwierzeck 8359e8cbdbSDaniel Schwierzeck linux_argp += len + 1; 8459e8cbdbSDaniel Schwierzeck linux_argc++; 8559e8cbdbSDaniel Schwierzeck } 8659e8cbdbSDaniel Schwierzeck 8759e8cbdbSDaniel Schwierzeck static void linux_cmdline_dump(void) 8859e8cbdbSDaniel Schwierzeck { 8959e8cbdbSDaniel Schwierzeck int i; 9059e8cbdbSDaniel Schwierzeck 9159e8cbdbSDaniel Schwierzeck debug("## cmdline argv at 0x%p, argp at 0x%p\n", 9259e8cbdbSDaniel Schwierzeck linux_argv, linux_argp); 9359e8cbdbSDaniel Schwierzeck 9459e8cbdbSDaniel Schwierzeck for (i = 1; i < linux_argc; i++) 9559e8cbdbSDaniel Schwierzeck debug(" arg %03d: %s\n", i, linux_argv[i]); 9659e8cbdbSDaniel Schwierzeck } 9759e8cbdbSDaniel Schwierzeck 9859e8cbdbSDaniel Schwierzeck static void boot_cmdline_linux(bootm_headers_t *images) 9959e8cbdbSDaniel Schwierzeck { 10059e8cbdbSDaniel Schwierzeck const char *bootargs, *next, *quote; 10159e8cbdbSDaniel Schwierzeck 10259e8cbdbSDaniel Schwierzeck linux_cmdline_init(); 10359e8cbdbSDaniel Schwierzeck 10459e8cbdbSDaniel Schwierzeck bootargs = getenv("bootargs"); 10559e8cbdbSDaniel Schwierzeck if (!bootargs) 10659e8cbdbSDaniel Schwierzeck return; 10759e8cbdbSDaniel Schwierzeck 10859e8cbdbSDaniel Schwierzeck next = bootargs; 10959e8cbdbSDaniel Schwierzeck 11059e8cbdbSDaniel Schwierzeck while (bootargs && *bootargs && linux_argc < LINUX_MAX_ARGS) { 11159e8cbdbSDaniel Schwierzeck quote = strchr(bootargs, '"'); 11259e8cbdbSDaniel Schwierzeck next = strchr(bootargs, ' '); 11359e8cbdbSDaniel Schwierzeck 11459e8cbdbSDaniel Schwierzeck while (next && quote && quote < next) { 11559e8cbdbSDaniel Schwierzeck /* 11659e8cbdbSDaniel Schwierzeck * we found a left quote before the next blank 11759e8cbdbSDaniel Schwierzeck * now we have to find the matching right quote 11859e8cbdbSDaniel Schwierzeck */ 11959e8cbdbSDaniel Schwierzeck next = strchr(quote + 1, '"'); 12059e8cbdbSDaniel Schwierzeck if (next) { 12159e8cbdbSDaniel Schwierzeck quote = strchr(next + 1, '"'); 12259e8cbdbSDaniel Schwierzeck next = strchr(next + 1, ' '); 12359e8cbdbSDaniel Schwierzeck } 12459e8cbdbSDaniel Schwierzeck } 12559e8cbdbSDaniel Schwierzeck 12659e8cbdbSDaniel Schwierzeck if (!next) 12759e8cbdbSDaniel Schwierzeck next = bootargs + strlen(bootargs); 12859e8cbdbSDaniel Schwierzeck 12959e8cbdbSDaniel Schwierzeck linux_cmdline_set(bootargs, next - bootargs); 13059e8cbdbSDaniel Schwierzeck 13159e8cbdbSDaniel Schwierzeck if (*next) 13259e8cbdbSDaniel Schwierzeck next++; 13359e8cbdbSDaniel Schwierzeck 13459e8cbdbSDaniel Schwierzeck bootargs = next; 13559e8cbdbSDaniel Schwierzeck } 13659e8cbdbSDaniel Schwierzeck 13759e8cbdbSDaniel Schwierzeck linux_cmdline_dump(); 13859e8cbdbSDaniel Schwierzeck } 13959e8cbdbSDaniel Schwierzeck 14015f8aa90SDaniel Schwierzeck static void linux_env_init(void) 14115f8aa90SDaniel Schwierzeck { 14215f8aa90SDaniel Schwierzeck linux_env = (char **)(((ulong) linux_argp + 15) & ~15); 14315f8aa90SDaniel Schwierzeck linux_env[0] = 0; 14415f8aa90SDaniel Schwierzeck linux_env_p = (char *)(linux_env + LINUX_MAX_ENVS); 14515f8aa90SDaniel Schwierzeck linux_env_idx = 0; 14615f8aa90SDaniel Schwierzeck } 14715f8aa90SDaniel Schwierzeck 14815f8aa90SDaniel Schwierzeck static void linux_env_set(const char *env_name, const char *env_val) 14915f8aa90SDaniel Schwierzeck { 15015f8aa90SDaniel Schwierzeck if (linux_env_idx < LINUX_MAX_ENVS - 1) { 15115f8aa90SDaniel Schwierzeck linux_env[linux_env_idx] = linux_env_p; 15215f8aa90SDaniel Schwierzeck 15315f8aa90SDaniel Schwierzeck strcpy(linux_env_p, env_name); 15415f8aa90SDaniel Schwierzeck linux_env_p += strlen(env_name); 15515f8aa90SDaniel Schwierzeck 1567a9d109bSPaul Burton if (mips_boot_malta) { 157b87493f4SDaniel Schwierzeck linux_env_p++; 158b87493f4SDaniel Schwierzeck linux_env[++linux_env_idx] = linux_env_p; 159b87493f4SDaniel Schwierzeck } else { 16015f8aa90SDaniel Schwierzeck *linux_env_p++ = '='; 161b87493f4SDaniel Schwierzeck } 16215f8aa90SDaniel Schwierzeck 16315f8aa90SDaniel Schwierzeck strcpy(linux_env_p, env_val); 16415f8aa90SDaniel Schwierzeck linux_env_p += strlen(env_val); 16515f8aa90SDaniel Schwierzeck 16615f8aa90SDaniel Schwierzeck linux_env_p++; 16715f8aa90SDaniel Schwierzeck linux_env[++linux_env_idx] = 0; 16815f8aa90SDaniel Schwierzeck } 16915f8aa90SDaniel Schwierzeck } 17015f8aa90SDaniel Schwierzeck 1710ea7213fSGabor Juhos static void boot_prep_linux(bootm_headers_t *images) 172ea0364f1SPeter Tyser { 173ea0364f1SPeter Tyser char env_buf[12]; 17415f8aa90SDaniel Schwierzeck const char *cp; 1756c154552SDaniel Schwierzeck ulong rd_start, rd_size; 176ea0364f1SPeter Tyser 177ea0364f1SPeter Tyser #ifdef CONFIG_MEMSIZE_IN_BYTES 178ea0364f1SPeter Tyser sprintf(env_buf, "%lu", (ulong)gd->ram_size); 179ea0364f1SPeter Tyser debug("## Giving linux memsize in bytes, %lu\n", (ulong)gd->ram_size); 180ea0364f1SPeter Tyser #else 181ea0364f1SPeter Tyser sprintf(env_buf, "%lu", (ulong)(gd->ram_size >> 20)); 182e51a6b7aSDaniel Schwierzeck debug("## Giving linux memsize in MB, %lu\n", 183e51a6b7aSDaniel Schwierzeck (ulong)(gd->ram_size >> 20)); 184ea0364f1SPeter Tyser #endif /* CONFIG_MEMSIZE_IN_BYTES */ 185ea0364f1SPeter Tyser 1866c154552SDaniel Schwierzeck rd_start = UNCACHED_SDRAM(images->initrd_start); 1876c154552SDaniel Schwierzeck rd_size = images->initrd_end - images->initrd_start; 1886c154552SDaniel Schwierzeck 18915f8aa90SDaniel Schwierzeck linux_env_init(); 19015f8aa90SDaniel Schwierzeck 191ea0364f1SPeter Tyser linux_env_set("memsize", env_buf); 192ea0364f1SPeter Tyser 1936c154552SDaniel Schwierzeck sprintf(env_buf, "0x%08lX", rd_start); 194ea0364f1SPeter Tyser linux_env_set("initrd_start", env_buf); 195ea0364f1SPeter Tyser 1966c154552SDaniel Schwierzeck sprintf(env_buf, "0x%lX", rd_size); 197ea0364f1SPeter Tyser linux_env_set("initrd_size", env_buf); 198ea0364f1SPeter Tyser 199ea0364f1SPeter Tyser sprintf(env_buf, "0x%08X", (uint) (gd->bd->bi_flashstart)); 200ea0364f1SPeter Tyser linux_env_set("flash_start", env_buf); 201ea0364f1SPeter Tyser 202ea0364f1SPeter Tyser sprintf(env_buf, "0x%X", (uint) (gd->bd->bi_flashsize)); 203ea0364f1SPeter Tyser linux_env_set("flash_size", env_buf); 204ea0364f1SPeter Tyser 205ea0364f1SPeter Tyser cp = getenv("ethaddr"); 206e51a6b7aSDaniel Schwierzeck if (cp) 207ea0364f1SPeter Tyser linux_env_set("ethaddr", cp); 208ea0364f1SPeter Tyser 209ea0364f1SPeter Tyser cp = getenv("eth1addr"); 210e51a6b7aSDaniel Schwierzeck if (cp) 211ea0364f1SPeter Tyser linux_env_set("eth1addr", cp); 212b87493f4SDaniel Schwierzeck 213d18d49d7SPaul Burton if (mips_boot_malta) { 214d18d49d7SPaul Burton sprintf(env_buf, "%un8r", gd->baudrate); 215d18d49d7SPaul Burton linux_env_set("modetty0", env_buf); 216d18d49d7SPaul Burton } 2170ea7213fSGabor Juhos } 218ea0364f1SPeter Tyser 2190ea7213fSGabor Juhos static void boot_jump_linux(bootm_headers_t *images) 2200ea7213fSGabor Juhos { 221c4b37847SDaniel Schwierzeck typedef void __noreturn (*kernel_entry_t)(int, ulong, ulong, ulong); 222c4b37847SDaniel Schwierzeck kernel_entry_t kernel = (kernel_entry_t) images->ep; 223b87493f4SDaniel Schwierzeck ulong linux_extra = 0; 2240ea7213fSGabor Juhos 225c4b37847SDaniel Schwierzeck debug("## Transferring control to Linux (at address %p) ...\n", kernel); 2260ea7213fSGabor Juhos 2270ea7213fSGabor Juhos bootstage_mark(BOOTSTAGE_ID_RUN_OS); 2280ea7213fSGabor Juhos 2297a9d109bSPaul Burton if (mips_boot_malta) 230b87493f4SDaniel Schwierzeck linux_extra = gd->ram_size; 231b87493f4SDaniel Schwierzeck 2320ea7213fSGabor Juhos /* we assume that the kernel is in place */ 2330ea7213fSGabor Juhos printf("\nStarting kernel ...\n\n"); 2340ea7213fSGabor Juhos 235b87493f4SDaniel Schwierzeck kernel(linux_argc, (ulong)linux_argv, (ulong)linux_env, linux_extra); 2360ea7213fSGabor Juhos } 2370ea7213fSGabor Juhos 2380ea7213fSGabor Juhos int do_bootm_linux(int flag, int argc, char * const argv[], 2390ea7213fSGabor Juhos bootm_headers_t *images) 2400ea7213fSGabor Juhos { 241*c9639421SDaniel Schwierzeck int ret; 242*c9639421SDaniel Schwierzeck 2439c170e2eSGabor Juhos /* No need for those on MIPS */ 24459e8cbdbSDaniel Schwierzeck if (flag & BOOTM_STATE_OS_BD_T) 2459c170e2eSGabor Juhos return -1; 2469c170e2eSGabor Juhos 24759e8cbdbSDaniel Schwierzeck if (flag & BOOTM_STATE_OS_CMDLINE) { 24859e8cbdbSDaniel Schwierzeck boot_cmdline_linux(images); 24959e8cbdbSDaniel Schwierzeck return 0; 25059e8cbdbSDaniel Schwierzeck } 25159e8cbdbSDaniel Schwierzeck 2529c170e2eSGabor Juhos if (flag & BOOTM_STATE_OS_PREP) { 2539c170e2eSGabor Juhos boot_prep_linux(images); 2549c170e2eSGabor Juhos return 0; 2559c170e2eSGabor Juhos } 2569c170e2eSGabor Juhos 2579c170e2eSGabor Juhos if (flag & BOOTM_STATE_OS_GO) { 2589c170e2eSGabor Juhos boot_jump_linux(images); 2599c170e2eSGabor Juhos return 0; 2609c170e2eSGabor Juhos } 2610ea7213fSGabor Juhos 262*c9639421SDaniel Schwierzeck ret = boot_setup_linux(images); 263*c9639421SDaniel Schwierzeck if (ret) 264*c9639421SDaniel Schwierzeck return ret; 265*c9639421SDaniel Schwierzeck 26659e8cbdbSDaniel Schwierzeck boot_cmdline_linux(images); 2670ea7213fSGabor Juhos boot_prep_linux(images); 268e08634c7SGabor Juhos boot_jump_linux(images); 269e51a6b7aSDaniel Schwierzeck 270ea0364f1SPeter Tyser /* does not return */ 271ea0364f1SPeter Tyser return 1; 272ea0364f1SPeter Tyser } 273