xref: /openbmc/u-boot/arch/x86/lib/zimage.c (revision 4e3349b6)
1 /*
2  * Copyright (c) 2011 The Chromium OS Authors.
3  * (C) Copyright 2002
4  * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
5  *
6  * See file CREDITS for list of people who contributed to this
7  * project.
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 2 of
12  * the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
22  * MA 02111-1307 USA
23  */
24 
25 /*
26  * Linux x86 zImage and bzImage loading
27  *
28  * based on the procdure described in
29  * linux/Documentation/i386/boot.txt
30  */
31 
32 #include <common.h>
33 #include <asm/io.h>
34 #include <asm/ptrace.h>
35 #include <asm/zimage.h>
36 #include <asm/realmode.h>
37 #include <asm/byteorder.h>
38 #include <asm/bootparam.h>
39 
40 /*
41  * Memory lay-out:
42  *
43  * relative to setup_base (which is 0x90000 currently)
44  *
45  *	0x0000-0x7FFF	Real mode kernel
46  *	0x8000-0x8FFF	Stack and heap
47  *	0x9000-0x90FF	Kernel command line
48  */
49 #define DEFAULT_SETUP_BASE	0x90000
50 #define COMMAND_LINE_OFFSET	0x9000
51 #define HEAP_END_OFFSET		0x8e00
52 
53 #define COMMAND_LINE_SIZE	2048
54 
55 unsigned generic_install_e820_map(unsigned max_entries,
56 				  struct e820entry *entries)
57 {
58 	return 0;
59 }
60 
61 unsigned install_e820_map(unsigned max_entries,
62 			  struct e820entry *entries)
63 	__attribute__((weak, alias("generic_install_e820_map")));
64 
65 static void build_command_line(char *command_line, int auto_boot)
66 {
67 	char *env_command_line;
68 
69 	command_line[0] = '\0';
70 
71 	env_command_line =  getenv("bootargs");
72 
73 	/* set console= argument if we use a serial console */
74 	if (!strstr(env_command_line, "console=")) {
75 		if (!strcmp(getenv("stdout"), "serial")) {
76 
77 			/* We seem to use serial console */
78 			sprintf(command_line, "console=ttyS0,%s ",
79 				getenv("baudrate"));
80 		}
81 	}
82 
83 	if (auto_boot)
84 		strcat(command_line, "auto ");
85 
86 	if (env_command_line)
87 		strcat(command_line, env_command_line);
88 
89 	printf("Kernel command line: \"%s\"\n", command_line);
90 }
91 
92 static int kernel_magic_ok(struct setup_header *hdr)
93 {
94 	if (KERNEL_MAGIC != hdr->boot_flag) {
95 		printf("Error: Invalid Boot Flag "
96 			"(found 0x%04x, expected 0x%04x)\n",
97 			hdr->boot_flag, KERNEL_MAGIC);
98 		return 0;
99 	} else {
100 		printf("Valid Boot Flag\n");
101 		return 1;
102 	}
103 }
104 
105 static int get_boot_protocol(struct setup_header *hdr)
106 {
107 	if (hdr->header == KERNEL_V2_MAGIC) {
108 		printf("Magic signature found\n");
109 		return hdr->version;
110 	} else {
111 		/* Very old kernel */
112 		printf("Magic signature not found\n");
113 		return 0x0100;
114 	}
115 }
116 
117 struct boot_params *load_zimage(char *image, unsigned long kernel_size,
118 				void **load_address)
119 {
120 	struct boot_params *setup_base;
121 	int setup_size;
122 	int bootproto;
123 	int big_image;
124 
125 	struct boot_params *params = (struct boot_params *)image;
126 	struct setup_header *hdr = &params->hdr;
127 
128 	/* base address for real-mode segment */
129 	setup_base = (struct boot_params *)DEFAULT_SETUP_BASE;
130 
131 	if (!kernel_magic_ok(hdr))
132 		return 0;
133 
134 	/* determine size of setup */
135 	if (0 == hdr->setup_sects) {
136 		printf("Setup Sectors = 0 (defaulting to 4)\n");
137 		setup_size = 5 * 512;
138 	} else {
139 		setup_size = (hdr->setup_sects + 1) * 512;
140 	}
141 
142 	printf("Setup Size = 0x%8.8lx\n", (ulong)setup_size);
143 
144 	if (setup_size > SETUP_MAX_SIZE)
145 		printf("Error: Setup is too large (%d bytes)\n", setup_size);
146 
147 	/* determine boot protocol version */
148 	bootproto = get_boot_protocol(hdr);
149 
150 	printf("Using boot protocol version %x.%02x\n",
151 	       (bootproto & 0xff00) >> 8, bootproto & 0xff);
152 
153 	if (bootproto >= 0x0200) {
154 		if (hdr->setup_sects >= 15) {
155 			printf("Linux kernel version %s\n",
156 				(char *)params +
157 				hdr->kernel_version + 0x200);
158 		} else {
159 			printf("Setup Sectors < 15 - "
160 				"Cannot print kernel version.\n");
161 		}
162 	}
163 
164 	/* Determine image type */
165 	big_image = (bootproto >= 0x0200) &&
166 		    (hdr->loadflags & BIG_KERNEL_FLAG);
167 
168 	/* Determine load address */
169 	if (big_image)
170 		*load_address = (void *)BZIMAGE_LOAD_ADDR;
171 	else
172 		*load_address = (void *)ZIMAGE_LOAD_ADDR;
173 
174 #if defined CONFIG_ZBOOT_32
175 	printf("Building boot_params at 0x%8.8lx\n", (ulong)setup_base);
176 	memset(setup_base, 0, sizeof(*setup_base));
177 	setup_base->hdr = params->hdr;
178 #else
179 	/* load setup */
180 	printf("Moving Real-Mode Code to 0x%8.8lx (%d bytes)\n",
181 	       (ulong)setup_base, setup_size);
182 	memmove(setup_base, image, setup_size);
183 #endif
184 
185 	if (bootproto >= 0x0204)
186 		kernel_size = hdr->syssize * 16;
187 	else
188 		kernel_size -= setup_size;
189 
190 	if (bootproto == 0x0100) {
191 		/*
192 		 * A very old kernel MUST have its real-mode code
193 		 * loaded at 0x90000
194 		 */
195 		if ((u32)setup_base != 0x90000) {
196 			/* Copy the real-mode kernel */
197 			memmove((void *)0x90000, setup_base, setup_size);
198 
199 			/* Copy the command line */
200 			memmove((void *)0x99000,
201 				(u8 *)setup_base + COMMAND_LINE_OFFSET,
202 				COMMAND_LINE_SIZE);
203 
204 			 /* Relocated */
205 			setup_base = (struct boot_params *)0x90000;
206 		}
207 
208 		/* It is recommended to clear memory up to the 32K mark */
209 		memset((u8 *)0x90000 + setup_size, 0,
210 		       SETUP_MAX_SIZE - setup_size);
211 	}
212 
213 	if (big_image) {
214 		if (kernel_size > BZIMAGE_MAX_SIZE) {
215 			printf("Error: bzImage kernel too big! "
216 				"(size: %ld, max: %d)\n",
217 				kernel_size, BZIMAGE_MAX_SIZE);
218 			return 0;
219 		}
220 	} else if ((kernel_size) > ZIMAGE_MAX_SIZE) {
221 		printf("Error: zImage kernel too big! (size: %ld, max: %d)\n",
222 		       kernel_size, ZIMAGE_MAX_SIZE);
223 		return 0;
224 	}
225 
226 	printf("Loading %s at address %p (%ld bytes)\n",
227 		big_image ? "bzImage" : "zImage", *load_address, kernel_size);
228 
229 	memmove(*load_address, image + setup_size, kernel_size);
230 
231 	return setup_base;
232 }
233 
234 int setup_zimage(struct boot_params *setup_base, char *cmd_line, int auto_boot,
235 		 unsigned long initrd_addr, unsigned long initrd_size)
236 {
237 	struct setup_header *hdr = &setup_base->hdr;
238 	int bootproto = get_boot_protocol(hdr);
239 
240 #if defined CONFIG_ZBOOT_32
241 	setup_base->e820_entries = install_e820_map(
242 		ARRAY_SIZE(setup_base->e820_map), setup_base->e820_map);
243 #endif
244 
245 	if (bootproto == 0x0100) {
246 		setup_base->screen_info.cl_magic = COMMAND_LINE_MAGIC;
247 		setup_base->screen_info.cl_offset = COMMAND_LINE_OFFSET;
248 	}
249 	if (bootproto >= 0x0200) {
250 		hdr->type_of_loader = 8;
251 
252 		if (initrd_addr) {
253 			printf("Initial RAM disk at linear address "
254 			       "0x%08lx, size %ld bytes\n",
255 			       initrd_addr, initrd_size);
256 
257 			hdr->ramdisk_image = initrd_addr;
258 			hdr->ramdisk_size = initrd_size;
259 		}
260 	}
261 
262 	if (bootproto >= 0x0201) {
263 		hdr->heap_end_ptr = HEAP_END_OFFSET;
264 		hdr->loadflags |= HEAP_FLAG;
265 	}
266 
267 	if (bootproto >= 0x0202) {
268 		hdr->cmd_line_ptr = (uintptr_t)cmd_line;
269 	} else if (bootproto >= 0x0200) {
270 		setup_base->screen_info.cl_magic = COMMAND_LINE_MAGIC;
271 		setup_base->screen_info.cl_offset =
272 			(uintptr_t)cmd_line - (uintptr_t)setup_base;
273 
274 		hdr->setup_move_size = 0x9100;
275 	}
276 
277 	/* build command line at COMMAND_LINE_OFFSET */
278 	build_command_line(cmd_line, auto_boot);
279 	return 0;
280 }
281 
282 void boot_zimage(void *setup_base, void *load_address)
283 {
284 	printf("\nStarting kernel ...\n\n");
285 
286 #if defined CONFIG_ZBOOT_32
287 	/*
288 	 * Set %ebx, %ebp, and %edi to 0, %esi to point to the boot_params
289 	 * structure, and then jump to the kernel. We assume that %cs is
290 	 * 0x10, 4GB flat, and read/execute, and the data segments are 0x18,
291 	 * 4GB flat, and read/write. U-boot is setting them up that way for
292 	 * itself in arch/i386/cpu/cpu.c.
293 	 */
294 	__asm__ __volatile__ (
295 	"movl $0, %%ebp		\n"
296 	"cli			\n"
297 	"jmp %[kernel_entry]	\n"
298 	:: [kernel_entry]"a"(load_address),
299 	   [boot_params] "S"(setup_base),
300 	   "b"(0), "D"(0)
301 	:  "%ebp"
302 	);
303 #else
304 	struct pt_regs regs;
305 
306 	memset(&regs, 0, sizeof(struct pt_regs));
307 	regs.xds = (u32)setup_base >> 4;
308 	regs.xes = regs.xds;
309 	regs.xss = regs.xds;
310 	regs.esp = 0x9000;
311 	regs.eflags = 0;
312 	enter_realmode(((u32)setup_base + SETUP_START_OFFSET) >> 4, 0,
313 		       &regs, &regs);
314 #endif
315 }
316 
317 void setup_pcat_compatibility(void)
318 	__attribute__((weak, alias("__setup_pcat_compatibility")));
319 
320 void __setup_pcat_compatibility(void)
321 {
322 }
323 
324 int do_zboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
325 {
326 	struct boot_params *base_ptr;
327 	void *bzImage_addr = NULL;
328 	void *load_address;
329 	char *s;
330 	ulong bzImage_size = 0;
331 	ulong initrd_addr = 0;
332 	ulong initrd_size = 0;
333 
334 	disable_interrupts();
335 
336 	/* Setup board for maximum PC/AT Compatibility */
337 	setup_pcat_compatibility();
338 
339 	if (argc >= 2) {
340 		/* argv[1] holds the address of the bzImage */
341 		s = argv[1];
342 	} else {
343 		s = getenv("fileaddr");
344 	}
345 
346 	if (s)
347 		bzImage_addr = (void *)simple_strtoul(s, NULL, 16);
348 
349 	if (argc >= 3) {
350 		/* argv[2] holds the size of the bzImage */
351 		bzImage_size = simple_strtoul(argv[2], NULL, 16);
352 	}
353 
354 	if (argc >= 4)
355 		initrd_addr = simple_strtoul(argv[3], NULL, 16);
356 	if (argc >= 5)
357 		initrd_size = simple_strtoul(argv[4], NULL, 16);
358 
359 	/* Lets look for */
360 	base_ptr = load_zimage(bzImage_addr, bzImage_size, &load_address);
361 
362 	if (!base_ptr) {
363 		printf("## Kernel loading failed ...\n");
364 		return -1;
365 	}
366 	if (setup_zimage(base_ptr, (char *)base_ptr + COMMAND_LINE_OFFSET,
367 			0, initrd_addr, initrd_size)) {
368 		printf("Setting up boot parameters failed ...\n");
369 		return -1;
370 	}
371 
372 	printf("## Transferring control to Linux "
373 	       "(at address %08x) ...\n",
374 	       (u32)base_ptr);
375 
376 	/* we assume that the kernel is in place */
377 	boot_zimage(base_ptr, load_address);
378 	/* does not return */
379 
380 	return -1;
381 }
382 
383 U_BOOT_CMD(
384 	zboot, 5, 0,	do_zboot,
385 	"Boot bzImage",
386 	"[addr] [size] [initrd addr] [initrd size]\n"
387 	"      addr -        The optional starting address of the bzimage.\n"
388 	"                    If not set it defaults to the environment\n"
389 	"                    variable \"fileaddr\".\n"
390 	"      size -        The optional size of the bzimage. Defaults to\n"
391 	"                    zero.\n"
392 	"      initrd addr - The address of the initrd image to use, if any.\n"
393 	"      initrd size - The size of the initrd image to use, if any.\n"
394 );
395