bootm.c 5.46 KB
Newer Older
Alexander Yurtsev's avatar
Alexander Yurtsev committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
/* 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>
#include <image.h>

#define PRINT_KERNEL_HEADER

extern image_header_t header;
extern void srmmu_init_cpu(unsigned int entry);
extern void prepare_bootargs(char *bootargs);

#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;

void arch_lmb_reserve(struct lmb *lmb)
{
	/* 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, CONFIG_SYS_RELOC_MONITOR_BASE, CONFIG_SYS_RAM_END);
}

/* boot the linux kernel */
int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t * images)
{
	char *bootargs;
	ulong initrd_start, initrd_end;
	ulong 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 ((flag != 0) && (flag != BOOTM_STATE_OS_GO))
		return 1;

	/* Get virtual address of kernel start */
	linux_hdr = (void *)images->os.load;

	/* */
	kernel = (void (*)(struct linux_romvec *, void *))images->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.
	 */

	/* Calc length of RAM disk, if zero no ramdisk available */
	rd_len = images->rd_end - images->rd_start;

	if (rd_len) {
		ret = boot_ramdisk_high(lmb, images->rd_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 - CONFIG_SYS_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);

	/* 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 *)images->ep);

	/* It will never come to this... */
	while (1) ;

      error:
	return 1;
}