diff options
Diffstat (limited to 'lib_sparc/bootm.c')
-rw-r--r-- | lib_sparc/bootm.c | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/lib_sparc/bootm.c b/lib_sparc/bootm.c new file mode 100644 index 0000000000..8900b2e581 --- /dev/null +++ b/lib_sparc/bootm.c @@ -0,0 +1,226 @@ +/* SPARC code for booting linux 2.6 + * + * (C) Copyright 2007 + * Daniel Hellstrom, Gaisler Research, daniel@gaisler.com. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <command.h> +#include <asm/byteorder.h> +#include <asm/prom.h> +#include <asm/cache.h> + +#define PRINT_KERNEL_HEADER + +extern image_header_t header; +extern void srmmu_init_cpu(unsigned int entry); +extern void prepare_bootargs(char *bootargs); +extern int do_reset(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]); + +#ifdef CONFIG_USB_UHCI +extern int usb_lowlevel_stop(void); +#endif + +/* sparc kernel argument (the ROM vector) */ +struct linux_romvec *kernel_arg_promvec; + +/* page szie is 4k */ +#define PAGE_SIZE 0x1000 +#define RAMDISK_IMAGE_START_MASK 0x07FF +#define RAMDISK_PROMPT_FLAG 0x8000 +#define RAMDISK_LOAD_FLAG 0x4000 +struct __attribute__ ((packed)) { + char traptable[PAGE_SIZE]; + char swapper_pg_dir[PAGE_SIZE]; + char pg0[PAGE_SIZE]; + char pg1[PAGE_SIZE]; + char pg2[PAGE_SIZE]; + char pg3[PAGE_SIZE]; + char empty_bad_page[PAGE_SIZE]; + char empty_bad_page_table[PAGE_SIZE]; + char empty_zero_page[PAGE_SIZE]; + unsigned char hdr[4]; /* ascii "HdrS" */ + /* 00.02.06.0b is for Linux kernel 2.6.11 */ + unsigned char linuxver_mega_major; + unsigned char linuxver_major; + unsigned char linuxver_minor; + unsigned char linuxver_revision; + /* header version 0x0203 */ + unsigned short hdr_ver; + union __attribute__ ((packed)) { + struct __attribute__ ((packed)) { + unsigned short root_flags; + unsigned short root_dev; + unsigned short ram_flags; + unsigned int sparc_ramdisk_image; + unsigned int sparc_ramdisk_size; + unsigned int reboot_command; + unsigned int resv[3]; + unsigned int end; + } ver_0203; + } hdr_input; +} *linux_hdr; + +/* temporary initrd image holder */ +image_header_t ihdr; + +/* boot the linux kernel */ +void do_bootm_linux(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[], + bootm_headers_t * images) +{ + char *bootargs; + ulong ep, load; + ulong initrd_start, initrd_end; + ulong rd_data_start, rd_data_end, rd_len; + unsigned int data, len, checksum; + unsigned int initrd_addr, kernend; + void (*kernel) (struct linux_romvec *, void *); + struct lmb *lmb = images->lmb; + int ret; + + if (images->legacy_hdr_valid) { + ep = image_get_ep(images->legacy_hdr_os); + load = image_get_load(images->legacy_hdr_os); +#if defined(CONFIG_FIT) + } else if (images->fit_uname_os) { + int ret = fit_image_get_entry(images->fit_hdr_os, + images->fit_noffset_os, &ep); + if (ret) { + puts("Can't get entry point property!\n"); + goto error; + } + + ret = fit_image_get_load(images->fit_hdr_os, + images->fit_noffset_os, &load); + if (ret) { + puts("Can't get load address property!\n"); + goto error; + } +#endif + } else { + puts("Could not find kernel entry point!\n"); + goto error; + } + + /* Get virtual address of kernel start */ + linux_hdr = (void *)load; + + /* */ + kernel = (void (*)(struct linux_romvec *, void *))ep; + + /* check for a SPARC kernel */ + if ((linux_hdr->hdr[0] != 'H') || + (linux_hdr->hdr[1] != 'd') || + (linux_hdr->hdr[2] != 'r') || (linux_hdr->hdr[3] != 'S')) { + puts("Error reading header of SPARC Linux kernel, aborting\n"); + goto error; + } +#ifdef PRINT_KERNEL_HEADER + printf("## Found SPARC Linux kernel %d.%d.%d ...\n", + linux_hdr->linuxver_major, + linux_hdr->linuxver_minor, linux_hdr->linuxver_revision); +#endif + +#ifdef CONFIG_USB_UHCI + usb_lowlevel_stop(); +#endif + + /* set basic boot params in kernel header now that it has been + * extracted and is writeable. + */ + + /* + * Are we going to use an initrd image? + */ + ret = boot_get_ramdisk(argc, argv, images, IH_ARCH_SPARC, + &rd_data_start, &rd_data_end); + if (ret) { + /* RAM disk found but was corrupt */ + puts("RAM Disk corrupt\n"); + goto error; + } + + /* Calc length of RAM disk, if zero no ramdisk available */ + rd_len = rd_data_end - rd_data_start; + + if (rd_len) { + + /* Reserve the space used by PROM and stack. This is done + * to avoid that the RAM image is copied over stack or + * PROM. + */ + lmb_reserve(lmb, CFG_RELOC_MONITOR_BASE, CFG_RAM_END); + + ret = boot_ramdisk_high(lmb, rd_data_start, rd_len, + &initrd_start, &initrd_end); + if (ret) { + puts("### Failed to relocate RAM disk\n"); + goto error; + } + + /* Update SPARC kernel header so that Linux knows + * what is going on and where to find RAM disk. + * + * Set INITRD Image address relative to RAM Start + */ + linux_hdr->hdr_input.ver_0203.sparc_ramdisk_image = + initrd_start - CFG_RAM_BASE; + linux_hdr->hdr_input.ver_0203.sparc_ramdisk_size = rd_len; + /* Clear READ ONLY flag if set to non-zero */ + linux_hdr->hdr_input.ver_0203.root_flags = 1; + /* Set root device to: Root_RAM0 */ + linux_hdr->hdr_input.ver_0203.root_dev = 0x100; + linux_hdr->hdr_input.ver_0203.ram_flags = 0; + } else { + /* NOT using RAMDISK image, overwriting kernel defaults */ + linux_hdr->hdr_input.ver_0203.sparc_ramdisk_image = 0; + linux_hdr->hdr_input.ver_0203.sparc_ramdisk_size = 0; + /* Leave to kernel defaults + linux_hdr->hdr_input.ver_0203.root_flags = 1; + linux_hdr->hdr_input.ver_0203.root_dev = 0; + linux_hdr->hdr_input.ver_0203.ram_flags = 0; + */ + } + + /* Copy bootargs from bootargs variable to kernel readable area */ + bootargs = getenv("bootargs"); + prepare_bootargs(bootargs); + + if (!images->autostart) + return; + + /* turn on mmu & setup context table & page table for process 0 (kernel) */ + srmmu_init_cpu((unsigned int)kernel); + + /* Enter SPARC Linux kernel + * From now on the only code in u-boot that will be + * executed is the PROM code. + */ + kernel(kernel_arg_promvec, (void *)ep); + + /* It will never come to this... */ + while (1) ; + + error: + if (images->autostart) + do_reset(cmdtp, flag, argc, argv); + return; +} |