1ea0364f1SPeter Tyser /* 2ea0364f1SPeter Tyser * (C) Copyright 2002 3ea0364f1SPeter Tyser * Sysgo Real-Time Solutions, GmbH <www.elinos.com> 4ea0364f1SPeter Tyser * Marius Groeger <mgroeger@sysgo.de> 5ea0364f1SPeter Tyser * 6ea0364f1SPeter Tyser * Copyright (C) 2001 Erik Mouw (J.A.K.Mouw@its.tudelft.nl) 7ea0364f1SPeter Tyser * 8ea0364f1SPeter Tyser * This program is free software; you can redistribute it and/or modify 9ea0364f1SPeter Tyser * it under the terms of the GNU General Public License as published by 10ea0364f1SPeter Tyser * the Free Software Foundation; either version 2 of the License, or 11ea0364f1SPeter Tyser * (at your option) any later version. 12ea0364f1SPeter Tyser * 13ea0364f1SPeter Tyser * This program is distributed in the hope that it will be useful, 14ea0364f1SPeter Tyser * but WITHOUT ANY WARRANTY; without even the implied warranty of 15ea0364f1SPeter Tyser * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16ea0364f1SPeter Tyser * GNU General Public License for more details. 17ea0364f1SPeter Tyser * 18ea0364f1SPeter Tyser * You should have received a copy of the GNU General Public License 19ea0364f1SPeter Tyser * along with this program; if not, write to the Free Software 20ea0364f1SPeter Tyser * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21ea0364f1SPeter Tyser * 22ea0364f1SPeter Tyser */ 23ea0364f1SPeter Tyser 24ea0364f1SPeter Tyser #include <common.h> 25ea0364f1SPeter Tyser #include <command.h> 26ea0364f1SPeter Tyser #include <image.h> 27ea0364f1SPeter Tyser #include <u-boot/zlib.h> 28ea0364f1SPeter Tyser #include <asm/byteorder.h> 29*2d1916e4SJohn Rigby #include <fdt.h> 30*2d1916e4SJohn Rigby #include <libfdt.h> 31*2d1916e4SJohn Rigby #include <fdt_support.h> 32ea0364f1SPeter Tyser 33ea0364f1SPeter Tyser DECLARE_GLOBAL_DATA_PTR; 34ea0364f1SPeter Tyser 35ea0364f1SPeter Tyser #if defined (CONFIG_SETUP_MEMORY_TAGS) || \ 36ea0364f1SPeter Tyser defined (CONFIG_CMDLINE_TAG) || \ 37ea0364f1SPeter Tyser defined (CONFIG_INITRD_TAG) || \ 38ea0364f1SPeter Tyser defined (CONFIG_SERIAL_TAG) || \ 3947ea6edfSMinkyu Kang defined (CONFIG_REVISION_TAG) 40ea0364f1SPeter Tyser static void setup_start_tag (bd_t *bd); 41ea0364f1SPeter Tyser 42ea0364f1SPeter Tyser # ifdef CONFIG_SETUP_MEMORY_TAGS 43ea0364f1SPeter Tyser static void setup_memory_tags (bd_t *bd); 44ea0364f1SPeter Tyser # endif 45ea0364f1SPeter Tyser static void setup_commandline_tag (bd_t *bd, char *commandline); 46ea0364f1SPeter Tyser 47ea0364f1SPeter Tyser # ifdef CONFIG_INITRD_TAG 48ea0364f1SPeter Tyser static void setup_initrd_tag (bd_t *bd, ulong initrd_start, 49ea0364f1SPeter Tyser ulong initrd_end); 50ea0364f1SPeter Tyser # endif 51ea0364f1SPeter Tyser static void setup_end_tag (bd_t *bd); 52ea0364f1SPeter Tyser 53ea0364f1SPeter Tyser static struct tag *params; 54ea0364f1SPeter Tyser #endif /* CONFIG_SETUP_MEMORY_TAGS || CONFIG_CMDLINE_TAG || CONFIG_INITRD_TAG */ 55ea0364f1SPeter Tyser 56*2d1916e4SJohn Rigby static ulong get_sp(void); 57*2d1916e4SJohn Rigby #if defined(CONFIG_OF_LIBFDT) 58*2d1916e4SJohn Rigby static int bootm_linux_fdt(int machid, bootm_headers_t *images); 59*2d1916e4SJohn Rigby #endif 60*2d1916e4SJohn Rigby 61*2d1916e4SJohn Rigby void arch_lmb_reserve(struct lmb *lmb) 62*2d1916e4SJohn Rigby { 63*2d1916e4SJohn Rigby ulong sp; 64*2d1916e4SJohn Rigby 65*2d1916e4SJohn Rigby /* 66*2d1916e4SJohn Rigby * Booting a (Linux) kernel image 67*2d1916e4SJohn Rigby * 68*2d1916e4SJohn Rigby * Allocate space for command line and board info - the 69*2d1916e4SJohn Rigby * address should be as high as possible within the reach of 70*2d1916e4SJohn Rigby * the kernel (see CONFIG_SYS_BOOTMAPSZ settings), but in unused 71*2d1916e4SJohn Rigby * memory, which means far enough below the current stack 72*2d1916e4SJohn Rigby * pointer. 73*2d1916e4SJohn Rigby */ 74*2d1916e4SJohn Rigby sp = get_sp(); 75*2d1916e4SJohn Rigby debug("## Current stack ends at 0x%08lx ", sp); 76*2d1916e4SJohn Rigby 77*2d1916e4SJohn Rigby /* adjust sp by 1K to be safe */ 78*2d1916e4SJohn Rigby sp -= 1024; 79*2d1916e4SJohn Rigby lmb_reserve(lmb, sp, 80*2d1916e4SJohn Rigby gd->bd->bi_dram[0].start + gd->bd->bi_dram[0].size - sp); 81*2d1916e4SJohn Rigby } 82*2d1916e4SJohn Rigby 83*2d1916e4SJohn Rigby static void announce_and_cleanup(void) 84*2d1916e4SJohn Rigby { 85*2d1916e4SJohn Rigby printf("\nStarting kernel ...\n\n"); 86*2d1916e4SJohn Rigby 87*2d1916e4SJohn Rigby #ifdef CONFIG_USB_DEVICE 88*2d1916e4SJohn Rigby { 89*2d1916e4SJohn Rigby extern void udc_disconnect(void); 90*2d1916e4SJohn Rigby udc_disconnect(); 91*2d1916e4SJohn Rigby } 92*2d1916e4SJohn Rigby #endif 93*2d1916e4SJohn Rigby cleanup_before_linux(); 94*2d1916e4SJohn Rigby } 95*2d1916e4SJohn Rigby 96*2d1916e4SJohn Rigby int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images) 97ea0364f1SPeter Tyser { 98ea0364f1SPeter Tyser bd_t *bd = gd->bd; 99ea0364f1SPeter Tyser char *s; 100ea0364f1SPeter Tyser int machid = bd->bi_arch_number; 101*2d1916e4SJohn Rigby void (*kernel_entry)(int zero, int arch, uint params); 102ea0364f1SPeter Tyser 103ea0364f1SPeter Tyser #ifdef CONFIG_CMDLINE_TAG 104ea0364f1SPeter Tyser char *commandline = getenv ("bootargs"); 105ea0364f1SPeter Tyser #endif 106ea0364f1SPeter Tyser 107ea0364f1SPeter Tyser if ((flag != 0) && (flag != BOOTM_STATE_OS_GO)) 108ea0364f1SPeter Tyser return 1; 109ea0364f1SPeter Tyser 110ea0364f1SPeter Tyser s = getenv ("machid"); 111ea0364f1SPeter Tyser if (s) { 112ea0364f1SPeter Tyser machid = simple_strtoul (s, NULL, 16); 113ea0364f1SPeter Tyser printf ("Using machid 0x%x from environment\n", machid); 114ea0364f1SPeter Tyser } 115ea0364f1SPeter Tyser 116ea0364f1SPeter Tyser show_boot_progress (15); 117ea0364f1SPeter Tyser 118*2d1916e4SJohn Rigby #ifdef CONFIG_OF_LIBFDT 119*2d1916e4SJohn Rigby if (images->ft_len) 120*2d1916e4SJohn Rigby return bootm_linux_fdt(machid, images); 121*2d1916e4SJohn Rigby #endif 122*2d1916e4SJohn Rigby 123*2d1916e4SJohn Rigby kernel_entry = (void (*)(int, int, uint))images->ep; 124*2d1916e4SJohn Rigby 125ea0364f1SPeter Tyser debug ("## Transferring control to Linux (at address %08lx) ...\n", 126*2d1916e4SJohn Rigby (ulong) kernel_entry); 127ea0364f1SPeter Tyser 128ea0364f1SPeter Tyser #if defined (CONFIG_SETUP_MEMORY_TAGS) || \ 129ea0364f1SPeter Tyser defined (CONFIG_CMDLINE_TAG) || \ 130ea0364f1SPeter Tyser defined (CONFIG_INITRD_TAG) || \ 131ea0364f1SPeter Tyser defined (CONFIG_SERIAL_TAG) || \ 13247ea6edfSMinkyu Kang defined (CONFIG_REVISION_TAG) 133ea0364f1SPeter Tyser setup_start_tag (bd); 134ea0364f1SPeter Tyser #ifdef CONFIG_SERIAL_TAG 135ea0364f1SPeter Tyser setup_serial_tag (¶ms); 136ea0364f1SPeter Tyser #endif 137ea0364f1SPeter Tyser #ifdef CONFIG_REVISION_TAG 138ea0364f1SPeter Tyser setup_revision_tag (¶ms); 139ea0364f1SPeter Tyser #endif 140ea0364f1SPeter Tyser #ifdef CONFIG_SETUP_MEMORY_TAGS 141ea0364f1SPeter Tyser setup_memory_tags (bd); 142ea0364f1SPeter Tyser #endif 143ea0364f1SPeter Tyser #ifdef CONFIG_CMDLINE_TAG 144ea0364f1SPeter Tyser setup_commandline_tag (bd, commandline); 145ea0364f1SPeter Tyser #endif 146ea0364f1SPeter Tyser #ifdef CONFIG_INITRD_TAG 147ea0364f1SPeter Tyser if (images->rd_start && images->rd_end) 148ea0364f1SPeter Tyser setup_initrd_tag (bd, images->rd_start, images->rd_end); 149ea0364f1SPeter Tyser #endif 150ea0364f1SPeter Tyser setup_end_tag(bd); 151ea0364f1SPeter Tyser #endif 152ea0364f1SPeter Tyser 153*2d1916e4SJohn Rigby announce_and_cleanup(); 154ea0364f1SPeter Tyser 155*2d1916e4SJohn Rigby kernel_entry(0, machid, bd->bi_boot_params); 156ea0364f1SPeter Tyser /* does not return */ 157ea0364f1SPeter Tyser 158ea0364f1SPeter Tyser return 1; 159ea0364f1SPeter Tyser } 160ea0364f1SPeter Tyser 161*2d1916e4SJohn Rigby #if defined(CONFIG_OF_LIBFDT) 162*2d1916e4SJohn Rigby static int fixup_memory_node(void *blob) 163*2d1916e4SJohn Rigby { 164*2d1916e4SJohn Rigby bd_t *bd = gd->bd; 165*2d1916e4SJohn Rigby int bank; 166*2d1916e4SJohn Rigby u64 start[CONFIG_NR_DRAM_BANKS]; 167*2d1916e4SJohn Rigby u64 size[CONFIG_NR_DRAM_BANKS]; 168*2d1916e4SJohn Rigby 169*2d1916e4SJohn Rigby for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) { 170*2d1916e4SJohn Rigby start[bank] = bd->bi_dram[bank].start; 171*2d1916e4SJohn Rigby size[bank] = bd->bi_dram[bank].size; 172*2d1916e4SJohn Rigby } 173*2d1916e4SJohn Rigby 174*2d1916e4SJohn Rigby return fdt_fixup_memory_banks(blob, start, size, CONFIG_NR_DRAM_BANKS); 175*2d1916e4SJohn Rigby } 176*2d1916e4SJohn Rigby 177*2d1916e4SJohn Rigby static int bootm_linux_fdt(int machid, bootm_headers_t *images) 178*2d1916e4SJohn Rigby { 179*2d1916e4SJohn Rigby ulong rd_len; 180*2d1916e4SJohn Rigby bd_t *bd = gd->bd; 181*2d1916e4SJohn Rigby char *s; 182*2d1916e4SJohn Rigby void (*kernel_entry)(int zero, int dt_machid, void *dtblob); 183*2d1916e4SJohn Rigby ulong bootmap_base = getenv_bootm_low(); 184*2d1916e4SJohn Rigby ulong of_size = images->ft_len; 185*2d1916e4SJohn Rigby char **of_flat_tree = &images->ft_addr; 186*2d1916e4SJohn Rigby ulong *initrd_start = &images->initrd_start; 187*2d1916e4SJohn Rigby ulong *initrd_end = &images->initrd_end; 188*2d1916e4SJohn Rigby struct lmb *lmb = &images->lmb; 189*2d1916e4SJohn Rigby int ret; 190*2d1916e4SJohn Rigby 191*2d1916e4SJohn Rigby kernel_entry = (void (*)(int, int, void *))images->ep; 192*2d1916e4SJohn Rigby 193*2d1916e4SJohn Rigby rd_len = images->rd_end - images->rd_start; 194*2d1916e4SJohn Rigby ret = boot_ramdisk_high(lmb, images->rd_start, rd_len, 195*2d1916e4SJohn Rigby initrd_start, initrd_end); 196*2d1916e4SJohn Rigby if (ret) 197*2d1916e4SJohn Rigby return ret; 198*2d1916e4SJohn Rigby 199*2d1916e4SJohn Rigby ret = boot_relocate_fdt(lmb, bootmap_base, of_flat_tree, &of_size); 200*2d1916e4SJohn Rigby if (ret) 201*2d1916e4SJohn Rigby return ret; 202*2d1916e4SJohn Rigby 203*2d1916e4SJohn Rigby debug("## Transferring control to Linux (at address %08lx) ...\n", 204*2d1916e4SJohn Rigby (ulong) kernel_entry); 205*2d1916e4SJohn Rigby 206*2d1916e4SJohn Rigby fdt_chosen(*of_flat_tree, 1); 207*2d1916e4SJohn Rigby 208*2d1916e4SJohn Rigby fixup_memory_node(*of_flat_tree); 209*2d1916e4SJohn Rigby 210*2d1916e4SJohn Rigby fdt_initrd(*of_flat_tree, *initrd_start, *initrd_end, 1); 211*2d1916e4SJohn Rigby 212*2d1916e4SJohn Rigby announce_and_cleanup(); 213*2d1916e4SJohn Rigby 214*2d1916e4SJohn Rigby kernel_entry(0, machid, *of_flat_tree); 215*2d1916e4SJohn Rigby /* does not return */ 216*2d1916e4SJohn Rigby 217*2d1916e4SJohn Rigby return 1; 218*2d1916e4SJohn Rigby } 219*2d1916e4SJohn Rigby #endif 220ea0364f1SPeter Tyser 221ea0364f1SPeter Tyser #if defined (CONFIG_SETUP_MEMORY_TAGS) || \ 222ea0364f1SPeter Tyser defined (CONFIG_CMDLINE_TAG) || \ 223ea0364f1SPeter Tyser defined (CONFIG_INITRD_TAG) || \ 224ea0364f1SPeter Tyser defined (CONFIG_SERIAL_TAG) || \ 22547ea6edfSMinkyu Kang defined (CONFIG_REVISION_TAG) 226ea0364f1SPeter Tyser static void setup_start_tag (bd_t *bd) 227ea0364f1SPeter Tyser { 228ea0364f1SPeter Tyser params = (struct tag *) bd->bi_boot_params; 229ea0364f1SPeter Tyser 230ea0364f1SPeter Tyser params->hdr.tag = ATAG_CORE; 231ea0364f1SPeter Tyser params->hdr.size = tag_size (tag_core); 232ea0364f1SPeter Tyser 233ea0364f1SPeter Tyser params->u.core.flags = 0; 234ea0364f1SPeter Tyser params->u.core.pagesize = 0; 235ea0364f1SPeter Tyser params->u.core.rootdev = 0; 236ea0364f1SPeter Tyser 237ea0364f1SPeter Tyser params = tag_next (params); 238ea0364f1SPeter Tyser } 239ea0364f1SPeter Tyser 240ea0364f1SPeter Tyser 241ea0364f1SPeter Tyser #ifdef CONFIG_SETUP_MEMORY_TAGS 242ea0364f1SPeter Tyser static void setup_memory_tags (bd_t *bd) 243ea0364f1SPeter Tyser { 244ea0364f1SPeter Tyser int i; 245ea0364f1SPeter Tyser 246ea0364f1SPeter Tyser for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { 247ea0364f1SPeter Tyser params->hdr.tag = ATAG_MEM; 248ea0364f1SPeter Tyser params->hdr.size = tag_size (tag_mem32); 249ea0364f1SPeter Tyser 250ea0364f1SPeter Tyser params->u.mem.start = bd->bi_dram[i].start; 251ea0364f1SPeter Tyser params->u.mem.size = bd->bi_dram[i].size; 252ea0364f1SPeter Tyser 253ea0364f1SPeter Tyser params = tag_next (params); 254ea0364f1SPeter Tyser } 255ea0364f1SPeter Tyser } 256ea0364f1SPeter Tyser #endif /* CONFIG_SETUP_MEMORY_TAGS */ 257ea0364f1SPeter Tyser 258ea0364f1SPeter Tyser 259ea0364f1SPeter Tyser static void setup_commandline_tag (bd_t *bd, char *commandline) 260ea0364f1SPeter Tyser { 261ea0364f1SPeter Tyser char *p; 262ea0364f1SPeter Tyser 263ea0364f1SPeter Tyser if (!commandline) 264ea0364f1SPeter Tyser return; 265ea0364f1SPeter Tyser 266ea0364f1SPeter Tyser /* eat leading white space */ 267ea0364f1SPeter Tyser for (p = commandline; *p == ' '; p++); 268ea0364f1SPeter Tyser 269ea0364f1SPeter Tyser /* skip non-existent command lines so the kernel will still 270ea0364f1SPeter Tyser * use its default command line. 271ea0364f1SPeter Tyser */ 272ea0364f1SPeter Tyser if (*p == '\0') 273ea0364f1SPeter Tyser return; 274ea0364f1SPeter Tyser 275ea0364f1SPeter Tyser params->hdr.tag = ATAG_CMDLINE; 276ea0364f1SPeter Tyser params->hdr.size = 277ea0364f1SPeter Tyser (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2; 278ea0364f1SPeter Tyser 279ea0364f1SPeter Tyser strcpy (params->u.cmdline.cmdline, p); 280ea0364f1SPeter Tyser 281ea0364f1SPeter Tyser params = tag_next (params); 282ea0364f1SPeter Tyser } 283ea0364f1SPeter Tyser 284ea0364f1SPeter Tyser 285ea0364f1SPeter Tyser #ifdef CONFIG_INITRD_TAG 286ea0364f1SPeter Tyser static void setup_initrd_tag (bd_t *bd, ulong initrd_start, ulong initrd_end) 287ea0364f1SPeter Tyser { 288ea0364f1SPeter Tyser /* an ATAG_INITRD node tells the kernel where the compressed 289ea0364f1SPeter Tyser * ramdisk can be found. ATAG_RDIMG is a better name, actually. 290ea0364f1SPeter Tyser */ 291ea0364f1SPeter Tyser params->hdr.tag = ATAG_INITRD2; 292ea0364f1SPeter Tyser params->hdr.size = tag_size (tag_initrd); 293ea0364f1SPeter Tyser 294ea0364f1SPeter Tyser params->u.initrd.start = initrd_start; 295ea0364f1SPeter Tyser params->u.initrd.size = initrd_end - initrd_start; 296ea0364f1SPeter Tyser 297ea0364f1SPeter Tyser params = tag_next (params); 298ea0364f1SPeter Tyser } 299ea0364f1SPeter Tyser #endif /* CONFIG_INITRD_TAG */ 300ea0364f1SPeter Tyser 301ea0364f1SPeter Tyser #ifdef CONFIG_SERIAL_TAG 302ea0364f1SPeter Tyser void setup_serial_tag (struct tag **tmp) 303ea0364f1SPeter Tyser { 304ea0364f1SPeter Tyser struct tag *params = *tmp; 305ea0364f1SPeter Tyser struct tag_serialnr serialnr; 306ea0364f1SPeter Tyser void get_board_serial(struct tag_serialnr *serialnr); 307ea0364f1SPeter Tyser 308ea0364f1SPeter Tyser get_board_serial(&serialnr); 309ea0364f1SPeter Tyser params->hdr.tag = ATAG_SERIAL; 310ea0364f1SPeter Tyser params->hdr.size = tag_size (tag_serialnr); 311ea0364f1SPeter Tyser params->u.serialnr.low = serialnr.low; 312ea0364f1SPeter Tyser params->u.serialnr.high= serialnr.high; 313ea0364f1SPeter Tyser params = tag_next (params); 314ea0364f1SPeter Tyser *tmp = params; 315ea0364f1SPeter Tyser } 316ea0364f1SPeter Tyser #endif 317ea0364f1SPeter Tyser 318ea0364f1SPeter Tyser #ifdef CONFIG_REVISION_TAG 319ea0364f1SPeter Tyser void setup_revision_tag(struct tag **in_params) 320ea0364f1SPeter Tyser { 321ea0364f1SPeter Tyser u32 rev = 0; 322ea0364f1SPeter Tyser u32 get_board_rev(void); 323ea0364f1SPeter Tyser 324ea0364f1SPeter Tyser rev = get_board_rev(); 325ea0364f1SPeter Tyser params->hdr.tag = ATAG_REVISION; 326ea0364f1SPeter Tyser params->hdr.size = tag_size (tag_revision); 327ea0364f1SPeter Tyser params->u.revision.rev = rev; 328ea0364f1SPeter Tyser params = tag_next (params); 329ea0364f1SPeter Tyser } 330ea0364f1SPeter Tyser #endif /* CONFIG_REVISION_TAG */ 331ea0364f1SPeter Tyser 332ea0364f1SPeter Tyser 333ea0364f1SPeter Tyser static void setup_end_tag (bd_t *bd) 334ea0364f1SPeter Tyser { 335ea0364f1SPeter Tyser params->hdr.tag = ATAG_NONE; 336ea0364f1SPeter Tyser params->hdr.size = 0; 337ea0364f1SPeter Tyser } 338ea0364f1SPeter Tyser 339*2d1916e4SJohn Rigby static ulong get_sp(void) 340*2d1916e4SJohn Rigby { 341*2d1916e4SJohn Rigby ulong ret; 342*2d1916e4SJohn Rigby 343*2d1916e4SJohn Rigby asm("mov %0, sp" : "=r"(ret) : ); 344*2d1916e4SJohn Rigby return ret; 345*2d1916e4SJohn Rigby } 346*2d1916e4SJohn Rigby 347ea0364f1SPeter Tyser #endif /* CONFIG_SETUP_MEMORY_TAGS || CONFIG_CMDLINE_TAG || CONFIG_INITRD_TAG */ 348