zimage.c 7.3 KB
Newer Older
1 2 3
/*
 * (C) Copyright 2002
 * Daniel Engstrm, Omicron Ceti AB, daniel@omicron.se
wdenk's avatar
wdenk committed
4
 *
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 * 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
 */

wdenk's avatar
wdenk committed
24
/*
25
 * Linux i386 zImage and bzImage loading
wdenk's avatar
wdenk committed
26 27
 *
 * based on the procdure described in
28 29 30 31 32 33 34 35 36
 * linux/Documentation/i386/boot.txt
 */

#include <common.h>
#include <asm/io.h>
#include <asm/ptrace.h>
#include <asm/zimage.h>
#include <asm/realmode.h>
#include <asm/byteorder.h>
37 38
#include <asm/bootparam.h>
#include <asm/ic/sc520.h>
39 40 41

/*
 * Memory lay-out:
wdenk's avatar
wdenk committed
42
 *
43
 * relative to setup_base (which is 0x90000 currently)
wdenk's avatar
wdenk committed
44 45
 *
 *	0x0000-0x7FFF	Real mode kernel
46 47 48 49 50 51 52 53 54 55 56 57
 *	0x8000-0x8FFF	Stack and heap
 *	0x9000-0x90FF	Kernel command line
 */
#define DEFAULT_SETUP_BASE  0x90000
#define COMMAND_LINE_OFFSET 0x9000
#define HEAP_END_OFFSET     0x8e00

#define COMMAND_LINE_SIZE   2048

static void build_command_line(char *command_line, int auto_boot)
{
	char *env_command_line;
wdenk's avatar
wdenk committed
58

59
	command_line[0] = '\0';
wdenk's avatar
wdenk committed
60

61
	env_command_line =  getenv("bootargs");
wdenk's avatar
wdenk committed
62

63 64 65
	/* set console= argument if we use a serial console */
	if (NULL == strstr(env_command_line, "console=")) {
		if (0==strcmp(getenv("stdout"), "serial")) {
wdenk's avatar
wdenk committed
66

67
			/* We seem to use serial console */
wdenk's avatar
wdenk committed
68
			sprintf(command_line, "console=ttyS0,%s ",
69 70 71
				 getenv("baudrate"));
		}
	}
wdenk's avatar
wdenk committed
72

73 74 75
	if (auto_boot) {
		strcat(command_line, "auto ");
	}
wdenk's avatar
wdenk committed
76

77 78
	if (NULL != env_command_line) {
		strcat(command_line, env_command_line);
wdenk's avatar
wdenk committed
79 80 81
	}


82 83 84
	printf("Kernel command line: \"%s\"\n", command_line);
}

wdenk's avatar
wdenk committed
85
void *load_zimage(char *image, unsigned long kernel_size,
86 87 88
		  unsigned long initrd_addr, unsigned long initrd_size,
		  int auto_boot)
{
wdenk's avatar
wdenk committed
89
	void *setup_base;
90 91 92 93
	int setup_size;
	int bootproto;
	int big_image;
	void *load_address;
wdenk's avatar
wdenk committed
94

95
	struct setup_header *hdr = (struct setup_header *)(image + SETUP_SECTS_OFF);
wdenk's avatar
wdenk committed
96

97
	setup_base = (void*)DEFAULT_SETUP_BASE;	/* base address for real-mode segment */
wdenk's avatar
wdenk committed
98

99 100 101
	if (KERNEL_MAGIC != hdr->boot_flag) {
		printf("Error: Invalid Boot Flag (found 0x%04x, expected 0x%04x)\n",
				hdr->boot_flag, KERNEL_MAGIC);
102
		return 0;
103 104
	} else {
		printf("Valid Boot Flag\n");
105
	}
wdenk's avatar
wdenk committed
106

107
	/* determine boot protocol version */
108 109 110 111
	if (KERNEL_V2_MAGIC == hdr->header) {
		printf("Magic signature found\n");

		bootproto = hdr->version;
112 113
	} else {
		/* Very old kernel */
114
		printf("Magic signature not found\n");
115 116
		bootproto = 0x0100;
	}
wdenk's avatar
wdenk committed
117

118
	/* determine size of setup */
119 120
	if (0 == hdr->setup_sects) {
		printf("Setup Sectors = 0 (defaulting to 4)\n");
121 122
		setup_size = 5 * 512;
	} else {
123
		setup_size = (hdr->setup_sects + 1) * 512;
124
	}
wdenk's avatar
wdenk committed
125

126 127
	printf("Setup Size = 0x%8.8lx\n", (ulong)setup_size);

128 129 130
	if (setup_size > SETUP_MAX_SIZE) {
		printf("Error: Setup is too large (%d bytes)\n", setup_size);
	}
wdenk's avatar
wdenk committed
131

132
	/* Determine image type */
133
	big_image = (bootproto >= 0x0200) && (hdr->loadflags & BIG_KERNEL_FLAG);
wdenk's avatar
wdenk committed
134

135 136
	/* Determine load address */
	load_address = (void*)(big_image ? BZIMAGE_LOAD_ADDR : ZIMAGE_LOAD_ADDR);
wdenk's avatar
wdenk committed
137

138
	/* load setup */
139
	printf("Moving Real-Mode Code to 0x%8.8lx (%d bytes)\n", (ulong)setup_base, setup_size);
140
	memmove(setup_base, image, setup_size);
wdenk's avatar
wdenk committed
141 142

	printf("Using boot protocol version %x.%02x\n",
143
	       (bootproto & 0xff00) >> 8, bootproto & 0xff);
wdenk's avatar
wdenk committed
144 145 146

	if (bootproto == 0x0100) {

147
		*(u16*)(setup_base + CMD_LINE_MAGIC_OFF) = COMMAND_LINE_MAGIC;
wdenk's avatar
wdenk committed
148 149
		*(u16*)(setup_base + CMD_LINE_OFFSET_OFF) = COMMAND_LINE_OFFSET;

150 151
		/* A very old kernel MUST have its real-mode code
		 * loaded at 0x90000 */
wdenk's avatar
wdenk committed
152

153 154 155 156
		if ((u32)setup_base != 0x90000) {
			/* Copy the real-mode kernel */
			memmove((void*)0x90000, setup_base, setup_size);
			/* Copy the command line */
wdenk's avatar
wdenk committed
157
			memmove((void*)0x99000, setup_base+COMMAND_LINE_OFFSET,
158
			       COMMAND_LINE_SIZE);
wdenk's avatar
wdenk committed
159

160 161
			setup_base = (void*)0x90000;		 /* Relocated */
		}
wdenk's avatar
wdenk committed
162

163 164 165
		/* It is recommended to clear memory up to the 32K mark */
		memset((void*)0x90000 + setup_size, 0, SETUP_MAX_SIZE-setup_size);
	}
wdenk's avatar
wdenk committed
166

167 168 169
	/* We are now setting up the real-mode version of the header */
	hdr = (struct setup_header *)(setup_base + SETUP_SECTS_OFF);

170
	if (bootproto >= 0x0200) {
171 172 173 174 175 176 177
		hdr->type_of_loader = 8;

		if (hdr->setup_sects >= 15)
			printf("Linux kernel version %s\n", (char *)
					(setup_base + (hdr->kernel_version + 0x200)));
		else
			printf("Setup Sectors < 15 - Cannot print kernel version.\n");
wdenk's avatar
wdenk committed
178

179 180 181
		if (initrd_addr) {
			printf("Initial RAM disk at linear address 0x%08lx, size %ld bytes\n",
			       initrd_addr, initrd_size);
wdenk's avatar
wdenk committed
182

183 184
			hdr->ramdisk_image = initrd_addr;
			hdr->ramdisk_size = initrd_size;
185 186
		}
	}
wdenk's avatar
wdenk committed
187

188
	if (bootproto >= 0x0201) {
189 190
		hdr->heap_end_ptr = HEAP_END_OFFSET;
		hdr->loadflags |= HEAP_FLAG;
191
	}
wdenk's avatar
wdenk committed
192

193
	if (bootproto >= 0x0202) {
194
		hdr->cmd_line_ptr = (u32)setup_base + COMMAND_LINE_OFFSET;
195
	} else if (bootproto >= 0x0200) {
196

197
		*(u16*)(setup_base + CMD_LINE_MAGIC_OFF) = COMMAND_LINE_MAGIC;
wdenk's avatar
wdenk committed
198
		*(u16*)(setup_base + CMD_LINE_OFFSET_OFF) = COMMAND_LINE_OFFSET;
199 200

		hdr->setup_move_size = 0x9100;
201 202
	}

203 204 205 206 207
	if (bootproto >= 0x0204)
		kernel_size = hdr->syssize * 16;
	else
		kernel_size -= setup_size;

wdenk's avatar
wdenk committed
208

209
	if (big_image) {
210
		if ((kernel_size) > BZIMAGE_MAX_SIZE) {
211
			printf("Error: bzImage kernel too big! (size: %ld, max: %d)\n",
212
			       kernel_size, BZIMAGE_MAX_SIZE);
213 214
			return 0;
		}
wdenk's avatar
wdenk committed
215

216
	} else if ((kernel_size) > ZIMAGE_MAX_SIZE) {
217
		printf("Error: zImage kernel too big! (size: %ld, max: %d)\n",
218
		       kernel_size, ZIMAGE_MAX_SIZE);
219 220
		return 0;
	}
wdenk's avatar
wdenk committed
221

222 223
	/* build command line at COMMAND_LINE_OFFSET */
	build_command_line(setup_base + COMMAND_LINE_OFFSET, auto_boot);
wdenk's avatar
wdenk committed
224 225

	printf("Loading %czImage at address 0x%08x (%ld bytes)\n", big_image ? 'b' : ' ',
226
	       (u32)load_address, kernel_size);
227

wdenk's avatar
wdenk committed
228

229
	memmove(load_address, image + setup_size, kernel_size);
wdenk's avatar
wdenk committed
230

231 232 233 234 235 236 237
	/* ready for booting */
	return setup_base;
}

void boot_zimage(void *setup_base)
{
	struct pt_regs regs;
wdenk's avatar
wdenk committed
238

239 240
	memset(&regs, 0, sizeof(struct pt_regs));
	regs.xds = (u32)setup_base >> 4;
241 242
	regs.xes = regs.xds;
	regs.xss = regs.xds;
wdenk's avatar
wdenk committed
243
	regs.esp = 0x9000;
244 245 246
	regs.eflags = 0;
	enter_realmode(((u32)setup_base+SETUP_START_OFFSET)>>4, 0, &regs, &regs);
}
247

248
int do_zboot (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
{
	void *base_ptr;
	void *bzImage_addr;
	ulong bzImage_size = 0;

	disable_interrupts();

	/* Setup board for maximum PC/AT Compatibility */
	setup_pcat_compatibility();

	/* argv[1] holds the address of the bzImage */
	bzImage_addr = (void *)simple_strtoul(argv[1], NULL, 16);

	if (argc == 3)
		bzImage_size = simple_strtoul(argv[2], NULL, 16);

	/* Lets look for*/
	base_ptr = load_zimage (bzImage_addr, bzImage_size, 0, 0, 0);

	if (NULL == base_ptr) {
		printf ("## Kernel loading failed ...\n");
	} else {
		printf ("## Transferring control to Linux (at address %08x) ...\n",
			(u32)base_ptr);

		/* we assume that the kernel is in place */
		printf("\nStarting kernel ...\n\n");

		boot_zimage(base_ptr);
		/* does not return */
	}

	return -1;
}

U_BOOT_CMD(
	zboot, 3, 0,	do_zboot,
	"Boot bzImage",
	""
);