xref: /openbmc/u-boot/arch/x86/lib/zimage.c (revision a2ee7f07)
1 /*
2  * (C) Copyright 2002
3  * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  */
23 
24 /*
25  * Linux x86 zImage and bzImage loading
26  *
27  * based on the procdure described in
28  * linux/Documentation/i386/boot.txt
29  */
30 
31 #include <common.h>
32 #include <asm/io.h>
33 #include <asm/ptrace.h>
34 #include <asm/zimage.h>
35 #include <asm/realmode.h>
36 #include <asm/byteorder.h>
37 #include <asm/bootparam.h>
38 
39 /*
40  * Memory lay-out:
41  *
42  * relative to setup_base (which is 0x90000 currently)
43  *
44  *	0x0000-0x7FFF	Real mode kernel
45  *	0x8000-0x8FFF	Stack and heap
46  *	0x9000-0x90FF	Kernel command line
47  */
48 #define DEFAULT_SETUP_BASE  0x90000
49 #define COMMAND_LINE_OFFSET 0x9000
50 #define HEAP_END_OFFSET     0x8e00
51 
52 #define COMMAND_LINE_SIZE   2048
53 
54 static void build_command_line(char *command_line, int auto_boot)
55 {
56 	char *env_command_line;
57 
58 	command_line[0] = '\0';
59 
60 	env_command_line =  getenv("bootargs");
61 
62 	/* set console= argument if we use a serial console */
63 	if (NULL == strstr(env_command_line, "console=")) {
64 		if (0==strcmp(getenv("stdout"), "serial")) {
65 
66 			/* We seem to use serial console */
67 			sprintf(command_line, "console=ttyS0,%s ",
68 				 getenv("baudrate"));
69 		}
70 	}
71 
72 	if (auto_boot) {
73 		strcat(command_line, "auto ");
74 	}
75 
76 	if (NULL != env_command_line) {
77 		strcat(command_line, env_command_line);
78 	}
79 
80 
81 	printf("Kernel command line: \"%s\"\n", command_line);
82 }
83 
84 void *load_zimage(char *image, unsigned long kernel_size,
85 		  unsigned long initrd_addr, unsigned long initrd_size,
86 		  int auto_boot)
87 {
88 	void *setup_base;
89 	int setup_size;
90 	int bootproto;
91 	int big_image;
92 	void *load_address;
93 
94 	struct setup_header *hdr = (struct setup_header *)(image + SETUP_SECTS_OFF);
95 
96 	setup_base = (void*)DEFAULT_SETUP_BASE;	/* base address for real-mode segment */
97 
98 	if (KERNEL_MAGIC != hdr->boot_flag) {
99 		printf("Error: Invalid Boot Flag (found 0x%04x, expected 0x%04x)\n",
100 				hdr->boot_flag, KERNEL_MAGIC);
101 		return 0;
102 	} else {
103 		printf("Valid Boot Flag\n");
104 	}
105 
106 	/* determine boot protocol version */
107 	if (KERNEL_V2_MAGIC == hdr->header) {
108 		printf("Magic signature found\n");
109 
110 		bootproto = hdr->version;
111 	} else {
112 		/* Very old kernel */
113 		printf("Magic signature not found\n");
114 		bootproto = 0x0100;
115 	}
116 
117 	/* determine size of setup */
118 	if (0 == hdr->setup_sects) {
119 		printf("Setup Sectors = 0 (defaulting to 4)\n");
120 		setup_size = 5 * 512;
121 	} else {
122 		setup_size = (hdr->setup_sects + 1) * 512;
123 	}
124 
125 	printf("Setup Size = 0x%8.8lx\n", (ulong)setup_size);
126 
127 	if (setup_size > SETUP_MAX_SIZE) {
128 		printf("Error: Setup is too large (%d bytes)\n", setup_size);
129 	}
130 
131 	/* Determine image type */
132 	big_image = (bootproto >= 0x0200) && (hdr->loadflags & BIG_KERNEL_FLAG);
133 
134 	/* Determine load address */
135 	load_address = (void*)(big_image ? BZIMAGE_LOAD_ADDR : ZIMAGE_LOAD_ADDR);
136 
137 	/* load setup */
138 	printf("Moving Real-Mode Code to 0x%8.8lx (%d bytes)\n", (ulong)setup_base, setup_size);
139 	memmove(setup_base, image, setup_size);
140 
141 	printf("Using boot protocol version %x.%02x\n",
142 	       (bootproto & 0xff00) >> 8, bootproto & 0xff);
143 
144 	if (bootproto == 0x0100) {
145 
146 		*(u16*)(setup_base + CMD_LINE_MAGIC_OFF) = COMMAND_LINE_MAGIC;
147 		*(u16*)(setup_base + CMD_LINE_OFFSET_OFF) = COMMAND_LINE_OFFSET;
148 
149 		/* A very old kernel MUST have its real-mode code
150 		 * loaded at 0x90000 */
151 
152 		if ((u32)setup_base != 0x90000) {
153 			/* Copy the real-mode kernel */
154 			memmove((void*)0x90000, setup_base, setup_size);
155 			/* Copy the command line */
156 			memmove((void*)0x99000, setup_base+COMMAND_LINE_OFFSET,
157 			       COMMAND_LINE_SIZE);
158 
159 			setup_base = (void*)0x90000;		 /* Relocated */
160 		}
161 
162 		/* It is recommended to clear memory up to the 32K mark */
163 		memset((void*)0x90000 + setup_size, 0, SETUP_MAX_SIZE-setup_size);
164 	}
165 
166 	/* We are now setting up the real-mode version of the header */
167 	hdr = (struct setup_header *)(setup_base + SETUP_SECTS_OFF);
168 
169 	if (bootproto >= 0x0200) {
170 		hdr->type_of_loader = 8;
171 
172 		if (hdr->setup_sects >= 15)
173 			printf("Linux kernel version %s\n", (char *)
174 					(setup_base + (hdr->kernel_version + 0x200)));
175 		else
176 			printf("Setup Sectors < 15 - Cannot print kernel version.\n");
177 
178 		if (initrd_addr) {
179 			printf("Initial RAM disk at linear address 0x%08lx, size %ld bytes\n",
180 			       initrd_addr, initrd_size);
181 
182 			hdr->ramdisk_image = initrd_addr;
183 			hdr->ramdisk_size = initrd_size;
184 		}
185 	}
186 
187 	if (bootproto >= 0x0201) {
188 		hdr->heap_end_ptr = HEAP_END_OFFSET;
189 		hdr->loadflags |= HEAP_FLAG;
190 	}
191 
192 	if (bootproto >= 0x0202) {
193 		hdr->cmd_line_ptr = (u32)setup_base + COMMAND_LINE_OFFSET;
194 	} else if (bootproto >= 0x0200) {
195 
196 		*(u16*)(setup_base + CMD_LINE_MAGIC_OFF) = COMMAND_LINE_MAGIC;
197 		*(u16*)(setup_base + CMD_LINE_OFFSET_OFF) = COMMAND_LINE_OFFSET;
198 
199 		hdr->setup_move_size = 0x9100;
200 	}
201 
202 	if (bootproto >= 0x0204)
203 		kernel_size = hdr->syssize * 16;
204 	else
205 		kernel_size -= setup_size;
206 
207 
208 	if (big_image) {
209 		if ((kernel_size) > BZIMAGE_MAX_SIZE) {
210 			printf("Error: bzImage kernel too big! (size: %ld, max: %d)\n",
211 			       kernel_size, BZIMAGE_MAX_SIZE);
212 			return 0;
213 		}
214 
215 	} else if ((kernel_size) > ZIMAGE_MAX_SIZE) {
216 		printf("Error: zImage kernel too big! (size: %ld, max: %d)\n",
217 		       kernel_size, ZIMAGE_MAX_SIZE);
218 		return 0;
219 	}
220 
221 	/* build command line at COMMAND_LINE_OFFSET */
222 	build_command_line(setup_base + COMMAND_LINE_OFFSET, auto_boot);
223 
224 	printf("Loading %czImage at address 0x%08x (%ld bytes)\n", big_image ? 'b' : ' ',
225 	       (u32)load_address, kernel_size);
226 
227 
228 	memmove(load_address, image + setup_size, kernel_size);
229 
230 	/* ready for booting */
231 	return setup_base;
232 }
233 
234 void boot_zimage(void *setup_base)
235 {
236 	struct pt_regs regs;
237 
238 	memset(&regs, 0, sizeof(struct pt_regs));
239 	regs.xds = (u32)setup_base >> 4;
240 	regs.xes = regs.xds;
241 	regs.xss = regs.xds;
242 	regs.esp = 0x9000;
243 	regs.eflags = 0;
244 	enter_realmode(((u32)setup_base+SETUP_START_OFFSET)>>4, 0, &regs, &regs);
245 }
246 
247 int do_zboot (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
248 {
249 	void *base_ptr;
250 	void *bzImage_addr = NULL;
251 	char *s;
252 	ulong bzImage_size = 0;
253 
254 	disable_interrupts();
255 
256 	/* Setup board for maximum PC/AT Compatibility */
257 	setup_pcat_compatibility();
258 
259 	if (argc >= 2)
260 		/* argv[1] holds the address of the bzImage */
261 		s = argv[1];
262 	else
263 		s = getenv("fileaddr");
264 
265 	if (s)
266 		bzImage_addr = (void *)simple_strtoul(s, NULL, 16);
267 
268 	if (argc >= 3)
269 		/* argv[2] holds the size of the bzImage */
270 		bzImage_size = simple_strtoul(argv[2], NULL, 16);
271 
272 	/* Lets look for*/
273 	base_ptr = load_zimage (bzImage_addr, bzImage_size, 0, 0, 0);
274 
275 	if (NULL == base_ptr) {
276 		printf ("## Kernel loading failed ...\n");
277 	} else {
278 		printf ("## Transferring control to Linux (at address %08x) ...\n",
279 			(u32)base_ptr);
280 
281 		/* we assume that the kernel is in place */
282 		printf("\nStarting kernel ...\n\n");
283 
284 		boot_zimage(base_ptr);
285 		/* does not return */
286 	}
287 
288 	return -1;
289 }
290 
291 U_BOOT_CMD(
292 	zboot, 2, 0,	do_zboot,
293 	"Boot bzImage",
294 	""
295 );
296