1*0a672d49SSimon Schwarz /* Copyright (C) 2011 2*0a672d49SSimon Schwarz * Corscience GmbH & Co. KG - Simon Schwarz <schwarz@corscience.de> 3*0a672d49SSimon Schwarz * - Added prep subcommand support 4*0a672d49SSimon Schwarz * - Reorganized source - modeled after powerpc version 5*0a672d49SSimon Schwarz * 6ea0364f1SPeter Tyser * (C) Copyright 2002 7ea0364f1SPeter Tyser * Sysgo Real-Time Solutions, GmbH <www.elinos.com> 8ea0364f1SPeter Tyser * Marius Groeger <mgroeger@sysgo.de> 9ea0364f1SPeter Tyser * 10ea0364f1SPeter Tyser * Copyright (C) 2001 Erik Mouw (J.A.K.Mouw@its.tudelft.nl) 11ea0364f1SPeter Tyser * 12ea0364f1SPeter Tyser * This program is free software; you can redistribute it and/or modify 13ea0364f1SPeter Tyser * it under the terms of the GNU General Public License as published by 14ea0364f1SPeter Tyser * the Free Software Foundation; either version 2 of the License, or 15ea0364f1SPeter Tyser * (at your option) any later version. 16ea0364f1SPeter Tyser * 17ea0364f1SPeter Tyser * This program is distributed in the hope that it will be useful, 18ea0364f1SPeter Tyser * but WITHOUT ANY WARRANTY; without even the implied warranty of 19ea0364f1SPeter Tyser * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20ea0364f1SPeter Tyser * GNU General Public License for more details. 21ea0364f1SPeter Tyser * 22ea0364f1SPeter Tyser * You should have received a copy of the GNU General Public License 23ea0364f1SPeter Tyser * along with this program; if not, write to the Free Software 24ea0364f1SPeter Tyser * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 25ea0364f1SPeter Tyser * 26ea0364f1SPeter Tyser */ 27ea0364f1SPeter Tyser 28ea0364f1SPeter Tyser #include <common.h> 29ea0364f1SPeter Tyser #include <command.h> 30ea0364f1SPeter Tyser #include <image.h> 31ea0364f1SPeter Tyser #include <u-boot/zlib.h> 32ea0364f1SPeter Tyser #include <asm/byteorder.h> 332d1916e4SJohn Rigby #include <fdt.h> 342d1916e4SJohn Rigby #include <libfdt.h> 352d1916e4SJohn Rigby #include <fdt_support.h> 36*0a672d49SSimon Schwarz #include <asm/bootm.h> 37ea0364f1SPeter Tyser 38ea0364f1SPeter Tyser DECLARE_GLOBAL_DATA_PTR; 39ea0364f1SPeter Tyser 40ea0364f1SPeter Tyser #if defined(CONFIG_SETUP_MEMORY_TAGS) || \ 41ea0364f1SPeter Tyser defined(CONFIG_CMDLINE_TAG) || \ 42ea0364f1SPeter Tyser defined(CONFIG_INITRD_TAG) || \ 43ea0364f1SPeter Tyser defined(CONFIG_SERIAL_TAG) || \ 4447ea6edfSMinkyu Kang defined(CONFIG_REVISION_TAG) 45ea0364f1SPeter Tyser static struct tag *params; 462d1916e4SJohn Rigby #endif 472d1916e4SJohn Rigby 48*0a672d49SSimon Schwarz static ulong get_sp(void) 49*0a672d49SSimon Schwarz { 50*0a672d49SSimon Schwarz ulong ret; 51*0a672d49SSimon Schwarz 52*0a672d49SSimon Schwarz asm("mov %0, sp" : "=r"(ret) : ); 53*0a672d49SSimon Schwarz return ret; 54*0a672d49SSimon Schwarz } 55*0a672d49SSimon Schwarz 562d1916e4SJohn Rigby void arch_lmb_reserve(struct lmb *lmb) 572d1916e4SJohn Rigby { 582d1916e4SJohn Rigby ulong sp; 592d1916e4SJohn Rigby 602d1916e4SJohn Rigby /* 612d1916e4SJohn Rigby * Booting a (Linux) kernel image 622d1916e4SJohn Rigby * 632d1916e4SJohn Rigby * Allocate space for command line and board info - the 642d1916e4SJohn Rigby * address should be as high as possible within the reach of 652d1916e4SJohn Rigby * the kernel (see CONFIG_SYS_BOOTMAPSZ settings), but in unused 662d1916e4SJohn Rigby * memory, which means far enough below the current stack 672d1916e4SJohn Rigby * pointer. 682d1916e4SJohn Rigby */ 692d1916e4SJohn Rigby sp = get_sp(); 702d1916e4SJohn Rigby debug("## Current stack ends at 0x%08lx ", sp); 712d1916e4SJohn Rigby 722d1916e4SJohn Rigby /* adjust sp by 1K to be safe */ 732d1916e4SJohn Rigby sp -= 1024; 742d1916e4SJohn Rigby lmb_reserve(lmb, sp, 752d1916e4SJohn Rigby gd->bd->bi_dram[0].start + gd->bd->bi_dram[0].size - sp); 762d1916e4SJohn Rigby } 772d1916e4SJohn Rigby 782d1916e4SJohn Rigby #ifdef CONFIG_OF_LIBFDT 792d1916e4SJohn Rigby static int fixup_memory_node(void *blob) 802d1916e4SJohn Rigby { 812d1916e4SJohn Rigby bd_t *bd = gd->bd; 822d1916e4SJohn Rigby int bank; 832d1916e4SJohn Rigby u64 start[CONFIG_NR_DRAM_BANKS]; 842d1916e4SJohn Rigby u64 size[CONFIG_NR_DRAM_BANKS]; 852d1916e4SJohn Rigby 862d1916e4SJohn Rigby for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) { 872d1916e4SJohn Rigby start[bank] = bd->bi_dram[bank].start; 882d1916e4SJohn Rigby size[bank] = bd->bi_dram[bank].size; 892d1916e4SJohn Rigby } 902d1916e4SJohn Rigby 912d1916e4SJohn Rigby return fdt_fixup_memory_banks(blob, start, size, CONFIG_NR_DRAM_BANKS); 922d1916e4SJohn Rigby } 932d1916e4SJohn Rigby #endif 94ea0364f1SPeter Tyser 95*0a672d49SSimon Schwarz static void announce_and_cleanup(void) 96*0a672d49SSimon Schwarz { 97*0a672d49SSimon Schwarz printf("\nStarting kernel ...\n\n"); 98*0a672d49SSimon Schwarz bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel"); 99*0a672d49SSimon Schwarz #ifdef CONFIG_BOOTSTAGE_REPORT 100*0a672d49SSimon Schwarz bootstage_report(); 101*0a672d49SSimon Schwarz #endif 102*0a672d49SSimon Schwarz 103*0a672d49SSimon Schwarz #ifdef CONFIG_USB_DEVICE 104*0a672d49SSimon Schwarz udc_disconnect(); 105*0a672d49SSimon Schwarz #endif 106*0a672d49SSimon Schwarz cleanup_before_linux(); 107*0a672d49SSimon Schwarz } 108*0a672d49SSimon Schwarz 109ea0364f1SPeter Tyser #if defined(CONFIG_SETUP_MEMORY_TAGS) || \ 110ea0364f1SPeter Tyser defined(CONFIG_CMDLINE_TAG) || \ 111ea0364f1SPeter Tyser defined(CONFIG_INITRD_TAG) || \ 112ea0364f1SPeter Tyser defined(CONFIG_SERIAL_TAG) || \ 11347ea6edfSMinkyu Kang defined(CONFIG_REVISION_TAG) 114ea0364f1SPeter Tyser static void setup_start_tag (bd_t *bd) 115ea0364f1SPeter Tyser { 116ea0364f1SPeter Tyser params = (struct tag *)bd->bi_boot_params; 117ea0364f1SPeter Tyser 118ea0364f1SPeter Tyser params->hdr.tag = ATAG_CORE; 119ea0364f1SPeter Tyser params->hdr.size = tag_size (tag_core); 120ea0364f1SPeter Tyser 121ea0364f1SPeter Tyser params->u.core.flags = 0; 122ea0364f1SPeter Tyser params->u.core.pagesize = 0; 123ea0364f1SPeter Tyser params->u.core.rootdev = 0; 124ea0364f1SPeter Tyser 125ea0364f1SPeter Tyser params = tag_next (params); 126ea0364f1SPeter Tyser } 127*0a672d49SSimon Schwarz #endif 128ea0364f1SPeter Tyser 129ea0364f1SPeter Tyser #ifdef CONFIG_SETUP_MEMORY_TAGS 130ea0364f1SPeter Tyser static void setup_memory_tags(bd_t *bd) 131ea0364f1SPeter Tyser { 132ea0364f1SPeter Tyser int i; 133ea0364f1SPeter Tyser 134ea0364f1SPeter Tyser for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { 135ea0364f1SPeter Tyser params->hdr.tag = ATAG_MEM; 136ea0364f1SPeter Tyser params->hdr.size = tag_size (tag_mem32); 137ea0364f1SPeter Tyser 138ea0364f1SPeter Tyser params->u.mem.start = bd->bi_dram[i].start; 139ea0364f1SPeter Tyser params->u.mem.size = bd->bi_dram[i].size; 140ea0364f1SPeter Tyser 141ea0364f1SPeter Tyser params = tag_next (params); 142ea0364f1SPeter Tyser } 143ea0364f1SPeter Tyser } 144*0a672d49SSimon Schwarz #endif 145ea0364f1SPeter Tyser 146*0a672d49SSimon Schwarz #ifdef CONFIG_CMDLINE_TAG 147ea0364f1SPeter Tyser static void setup_commandline_tag(bd_t *bd, char *commandline) 148ea0364f1SPeter Tyser { 149ea0364f1SPeter Tyser char *p; 150ea0364f1SPeter Tyser 151ea0364f1SPeter Tyser if (!commandline) 152ea0364f1SPeter Tyser return; 153ea0364f1SPeter Tyser 154ea0364f1SPeter Tyser /* eat leading white space */ 155ea0364f1SPeter Tyser for (p = commandline; *p == ' '; p++); 156ea0364f1SPeter Tyser 157ea0364f1SPeter Tyser /* skip non-existent command lines so the kernel will still 158ea0364f1SPeter Tyser * use its default command line. 159ea0364f1SPeter Tyser */ 160ea0364f1SPeter Tyser if (*p == '\0') 161ea0364f1SPeter Tyser return; 162ea0364f1SPeter Tyser 163ea0364f1SPeter Tyser params->hdr.tag = ATAG_CMDLINE; 164ea0364f1SPeter Tyser params->hdr.size = 165ea0364f1SPeter Tyser (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2; 166ea0364f1SPeter Tyser 167ea0364f1SPeter Tyser strcpy (params->u.cmdline.cmdline, p); 168ea0364f1SPeter Tyser 169ea0364f1SPeter Tyser params = tag_next (params); 170ea0364f1SPeter Tyser } 171*0a672d49SSimon Schwarz #endif 172ea0364f1SPeter Tyser 173ea0364f1SPeter Tyser #ifdef CONFIG_INITRD_TAG 174ea0364f1SPeter Tyser static void setup_initrd_tag(bd_t *bd, ulong initrd_start, ulong initrd_end) 175ea0364f1SPeter Tyser { 176ea0364f1SPeter Tyser /* an ATAG_INITRD node tells the kernel where the compressed 177ea0364f1SPeter Tyser * ramdisk can be found. ATAG_RDIMG is a better name, actually. 178ea0364f1SPeter Tyser */ 179ea0364f1SPeter Tyser params->hdr.tag = ATAG_INITRD2; 180ea0364f1SPeter Tyser params->hdr.size = tag_size (tag_initrd); 181ea0364f1SPeter Tyser 182ea0364f1SPeter Tyser params->u.initrd.start = initrd_start; 183ea0364f1SPeter Tyser params->u.initrd.size = initrd_end - initrd_start; 184ea0364f1SPeter Tyser 185ea0364f1SPeter Tyser params = tag_next (params); 186ea0364f1SPeter Tyser } 187*0a672d49SSimon Schwarz #endif 188ea0364f1SPeter Tyser 189ea0364f1SPeter Tyser #ifdef CONFIG_SERIAL_TAG 190ea0364f1SPeter Tyser void setup_serial_tag(struct tag **tmp) 191ea0364f1SPeter Tyser { 192ea0364f1SPeter Tyser struct tag *params = *tmp; 193ea0364f1SPeter Tyser struct tag_serialnr serialnr; 194ea0364f1SPeter Tyser void get_board_serial(struct tag_serialnr *serialnr); 195ea0364f1SPeter Tyser 196ea0364f1SPeter Tyser get_board_serial(&serialnr); 197ea0364f1SPeter Tyser params->hdr.tag = ATAG_SERIAL; 198ea0364f1SPeter Tyser params->hdr.size = tag_size (tag_serialnr); 199ea0364f1SPeter Tyser params->u.serialnr.low = serialnr.low; 200ea0364f1SPeter Tyser params->u.serialnr.high= serialnr.high; 201ea0364f1SPeter Tyser params = tag_next (params); 202ea0364f1SPeter Tyser *tmp = params; 203ea0364f1SPeter Tyser } 204ea0364f1SPeter Tyser #endif 205ea0364f1SPeter Tyser 206ea0364f1SPeter Tyser #ifdef CONFIG_REVISION_TAG 207ea0364f1SPeter Tyser void setup_revision_tag(struct tag **in_params) 208ea0364f1SPeter Tyser { 209ea0364f1SPeter Tyser u32 rev = 0; 210ea0364f1SPeter Tyser u32 get_board_rev(void); 211ea0364f1SPeter Tyser 212ea0364f1SPeter Tyser rev = get_board_rev(); 213ea0364f1SPeter Tyser params->hdr.tag = ATAG_REVISION; 214ea0364f1SPeter Tyser params->hdr.size = tag_size (tag_revision); 215ea0364f1SPeter Tyser params->u.revision.rev = rev; 216ea0364f1SPeter Tyser params = tag_next (params); 217ea0364f1SPeter Tyser } 218*0a672d49SSimon Schwarz #endif 219ea0364f1SPeter Tyser 220*0a672d49SSimon Schwarz #if defined(CONFIG_SETUP_MEMORY_TAGS) || \ 221*0a672d49SSimon Schwarz defined(CONFIG_CMDLINE_TAG) || \ 222*0a672d49SSimon Schwarz defined(CONFIG_INITRD_TAG) || \ 223*0a672d49SSimon Schwarz defined(CONFIG_SERIAL_TAG) || \ 224*0a672d49SSimon Schwarz defined(CONFIG_REVISION_TAG) 225ea0364f1SPeter Tyser static void setup_end_tag(bd_t *bd) 226ea0364f1SPeter Tyser { 227ea0364f1SPeter Tyser params->hdr.tag = ATAG_NONE; 228ea0364f1SPeter Tyser params->hdr.size = 0; 229ea0364f1SPeter Tyser } 230*0a672d49SSimon Schwarz #endif 231ea0364f1SPeter Tyser 232*0a672d49SSimon Schwarz #ifdef CONFIG_OF_LIBFDT 233*0a672d49SSimon Schwarz static int create_fdt(bootm_headers_t *images) 2342d1916e4SJohn Rigby { 235*0a672d49SSimon Schwarz ulong of_size = images->ft_len; 236*0a672d49SSimon Schwarz char **of_flat_tree = &images->ft_addr; 237*0a672d49SSimon Schwarz ulong *initrd_start = &images->initrd_start; 238*0a672d49SSimon Schwarz ulong *initrd_end = &images->initrd_end; 239*0a672d49SSimon Schwarz struct lmb *lmb = &images->lmb; 240*0a672d49SSimon Schwarz ulong rd_len; 241*0a672d49SSimon Schwarz int ret; 2422d1916e4SJohn Rigby 243*0a672d49SSimon Schwarz debug("using: FDT\n"); 244*0a672d49SSimon Schwarz 245*0a672d49SSimon Schwarz boot_fdt_add_mem_rsv_regions(lmb, *of_flat_tree); 246*0a672d49SSimon Schwarz 247*0a672d49SSimon Schwarz rd_len = images->rd_end - images->rd_start; 248*0a672d49SSimon Schwarz ret = boot_ramdisk_high(lmb, images->rd_start, rd_len, 249*0a672d49SSimon Schwarz initrd_start, initrd_end); 250*0a672d49SSimon Schwarz if (ret) 2512d1916e4SJohn Rigby return ret; 252*0a672d49SSimon Schwarz 253*0a672d49SSimon Schwarz ret = boot_relocate_fdt(lmb, of_flat_tree, &of_size); 254*0a672d49SSimon Schwarz if (ret) 255*0a672d49SSimon Schwarz return ret; 256*0a672d49SSimon Schwarz 257*0a672d49SSimon Schwarz fdt_chosen(*of_flat_tree, 1); 258*0a672d49SSimon Schwarz fixup_memory_node(*of_flat_tree); 259*0a672d49SSimon Schwarz fdt_initrd(*of_flat_tree, *initrd_start, *initrd_end, 1); 260*0a672d49SSimon Schwarz 261*0a672d49SSimon Schwarz return 0; 262*0a672d49SSimon Schwarz } 263*0a672d49SSimon Schwarz #endif 264*0a672d49SSimon Schwarz 265*0a672d49SSimon Schwarz /* Subcommand: PREP */ 266*0a672d49SSimon Schwarz static void boot_prep_linux(bootm_headers_t *images) 267*0a672d49SSimon Schwarz { 268*0a672d49SSimon Schwarz #ifdef CONFIG_CMDLINE_TAG 269*0a672d49SSimon Schwarz char *commandline = getenv("bootargs"); 270*0a672d49SSimon Schwarz #endif 271*0a672d49SSimon Schwarz 272*0a672d49SSimon Schwarz #ifdef CONFIG_OF_LIBFDT 273*0a672d49SSimon Schwarz if (images->ft_len) { 274*0a672d49SSimon Schwarz debug("using: FDT\n"); 275*0a672d49SSimon Schwarz if (create_fdt(images)) { 276*0a672d49SSimon Schwarz printf("FDT creation failed! hanging..."); 277*0a672d49SSimon Schwarz hang(); 278*0a672d49SSimon Schwarz } 279*0a672d49SSimon Schwarz } else 280*0a672d49SSimon Schwarz #endif 281*0a672d49SSimon Schwarz { 282*0a672d49SSimon Schwarz #if defined(CONFIG_SETUP_MEMORY_TAGS) || \ 283*0a672d49SSimon Schwarz defined(CONFIG_CMDLINE_TAG) || \ 284*0a672d49SSimon Schwarz defined(CONFIG_INITRD_TAG) || \ 285*0a672d49SSimon Schwarz defined(CONFIG_SERIAL_TAG) || \ 286*0a672d49SSimon Schwarz defined(CONFIG_REVISION_TAG) 287*0a672d49SSimon Schwarz debug("using: ATAGS\n"); 288*0a672d49SSimon Schwarz setup_start_tag(gd->bd); 289*0a672d49SSimon Schwarz #ifdef CONFIG_SERIAL_TAG 290*0a672d49SSimon Schwarz setup_serial_tag(¶ms); 291*0a672d49SSimon Schwarz #endif 292*0a672d49SSimon Schwarz #ifdef CONFIG_CMDLINE_TAG 293*0a672d49SSimon Schwarz setup_commandline_tag(gd->bd, commandline); 294*0a672d49SSimon Schwarz #endif 295*0a672d49SSimon Schwarz #ifdef CONFIG_REVISION_TAG 296*0a672d49SSimon Schwarz setup_revision_tag(¶ms); 297*0a672d49SSimon Schwarz #endif 298*0a672d49SSimon Schwarz #ifdef CONFIG_SETUP_MEMORY_TAGS 299*0a672d49SSimon Schwarz setup_memory_tags(gd->bd); 300*0a672d49SSimon Schwarz #endif 301*0a672d49SSimon Schwarz #ifdef CONFIG_INITRD_TAG 302*0a672d49SSimon Schwarz if (images->rd_start && images->rd_end) 303*0a672d49SSimon Schwarz setup_initrd_tag(gd->bd, images->rd_start, 304*0a672d49SSimon Schwarz images->rd_end); 305*0a672d49SSimon Schwarz #endif 306*0a672d49SSimon Schwarz setup_end_tag(gd->bd); 307*0a672d49SSimon Schwarz #else /* all tags */ 308*0a672d49SSimon Schwarz printf("FDT and ATAGS support not compiled in - hanging\n"); 309*0a672d49SSimon Schwarz hang(); 310*0a672d49SSimon Schwarz #endif /* all tags */ 311*0a672d49SSimon Schwarz } 312*0a672d49SSimon Schwarz } 313*0a672d49SSimon Schwarz 314*0a672d49SSimon Schwarz /* Subcommand: GO */ 315*0a672d49SSimon Schwarz static void boot_jump_linux(bootm_headers_t *images) 316*0a672d49SSimon Schwarz { 317*0a672d49SSimon Schwarz unsigned long machid = gd->bd->bi_arch_number; 318*0a672d49SSimon Schwarz char *s; 319*0a672d49SSimon Schwarz void (*kernel_entry)(int zero, int arch, uint params); 320*0a672d49SSimon Schwarz 321*0a672d49SSimon Schwarz kernel_entry = (void (*)(int, int, uint))images->ep; 322*0a672d49SSimon Schwarz 323*0a672d49SSimon Schwarz s = getenv("machid"); 324*0a672d49SSimon Schwarz if (s) { 325*0a672d49SSimon Schwarz strict_strtoul(s, 16, &machid); 326*0a672d49SSimon Schwarz printf("Using machid 0x%lx from environment\n", machid); 327*0a672d49SSimon Schwarz } 328*0a672d49SSimon Schwarz 329*0a672d49SSimon Schwarz debug("## Transferring control to Linux (at address %08lx)" \ 330*0a672d49SSimon Schwarz "...\n", (ulong) kernel_entry); 331*0a672d49SSimon Schwarz bootstage_mark(BOOTSTAGE_ID_RUN_OS); 332*0a672d49SSimon Schwarz announce_and_cleanup(); 333*0a672d49SSimon Schwarz kernel_entry(0, machid, gd->bd->bi_boot_params); 334*0a672d49SSimon Schwarz } 335*0a672d49SSimon Schwarz 336*0a672d49SSimon Schwarz /* Main Entry point for arm bootm implementation 337*0a672d49SSimon Schwarz * 338*0a672d49SSimon Schwarz * Modeled after the powerpc implementation 339*0a672d49SSimon Schwarz * DIFFERENCE: Instead of calling prep and go at the end 340*0a672d49SSimon Schwarz * they are called if subcommand is equal 0. 341*0a672d49SSimon Schwarz */ 342*0a672d49SSimon Schwarz int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images) 343*0a672d49SSimon Schwarz { 344*0a672d49SSimon Schwarz /* No need for those on ARM */ 345*0a672d49SSimon Schwarz if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE) 346*0a672d49SSimon Schwarz return -1; 347*0a672d49SSimon Schwarz 348*0a672d49SSimon Schwarz if (flag & BOOTM_STATE_OS_PREP) { 349*0a672d49SSimon Schwarz boot_prep_linux(images); 350*0a672d49SSimon Schwarz return 0; 351*0a672d49SSimon Schwarz } 352*0a672d49SSimon Schwarz 353*0a672d49SSimon Schwarz if (flag & BOOTM_STATE_OS_GO) { 354*0a672d49SSimon Schwarz boot_jump_linux(images); 355*0a672d49SSimon Schwarz return 0; 356*0a672d49SSimon Schwarz } 357*0a672d49SSimon Schwarz 358*0a672d49SSimon Schwarz boot_prep_linux(images); 359*0a672d49SSimon Schwarz boot_jump_linux(images); 360*0a672d49SSimon Schwarz return 0; 3612d1916e4SJohn Rigby } 362