1 /*
2  * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 /**
27  * @file
28  *
29  * Linux bzImage image format
30  *
31  */
32 
33 #include <stdint.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <errno.h>
37 #include <assert.h>
38 #include <realmode.h>
39 #include <bzimage.h>
40 #include <initrd.h>
41 #include <ipxe/uaccess.h>
42 #include <ipxe/image.h>
43 #include <ipxe/segment.h>
44 #include <ipxe/init.h>
45 #include <ipxe/cpio.h>
46 #include <ipxe/features.h>
47 
48 FEATURE ( FEATURE_IMAGE, "bzImage", DHCP_EB_FEATURE_BZIMAGE, 1 );
49 
50 /**
51  * bzImage context
52  */
53 struct bzimage_context {
54 	/** Boot protocol version */
55 	unsigned int version;
56 	/** Real-mode kernel portion load segment address */
57 	unsigned int rm_kernel_seg;
58 	/** Real-mode kernel portion load address */
59 	userptr_t rm_kernel;
60 	/** Real-mode kernel portion file size */
61 	size_t rm_filesz;
62 	/** Real-mode heap top (offset from rm_kernel) */
63 	size_t rm_heap;
64 	/** Command line (offset from rm_kernel) */
65 	size_t rm_cmdline;
66 	/** Command line maximum length */
67 	size_t cmdline_size;
68 	/** Real-mode kernel portion total memory size */
69 	size_t rm_memsz;
70 	/** Non-real-mode kernel portion load address */
71 	userptr_t pm_kernel;
72 	/** Non-real-mode kernel portion file and memory size */
73 	size_t pm_sz;
74 	/** Video mode */
75 	unsigned int vid_mode;
76 	/** Memory limit */
77 	uint64_t mem_limit;
78 	/** Initrd address */
79 	physaddr_t ramdisk_image;
80 	/** Initrd size */
81 	physaddr_t ramdisk_size;
82 
83 	/** Command line magic block */
84 	struct bzimage_cmdline cmdline_magic;
85 	/** bzImage header */
86 	struct bzimage_header bzhdr;
87 };
88 
89 /**
90  * Parse bzImage header
91  *
92  * @v image		bzImage file
93  * @v bzimg		bzImage context
94  * @v src		bzImage to parse
95  * @ret rc		Return status code
96  */
bzimage_parse_header(struct image * image,struct bzimage_context * bzimg,userptr_t src)97 static int bzimage_parse_header ( struct image *image,
98 				  struct bzimage_context *bzimg,
99 				  userptr_t src ) {
100 	unsigned int syssize;
101 	int is_bzimage;
102 
103 	/* Sanity check */
104 	if ( image->len < ( BZI_HDR_OFFSET + sizeof ( bzimg->bzhdr ) ) ) {
105 		DBGC ( image, "bzImage %p too short for kernel header\n",
106 		       image );
107 		return -ENOEXEC;
108 	}
109 
110 	/* Read in header structures */
111 	memset ( bzimg, 0, sizeof ( *bzimg ) );
112 	copy_from_user ( &bzimg->cmdline_magic, src, BZI_CMDLINE_OFFSET,
113 			 sizeof ( bzimg->cmdline_magic ) );
114 	copy_from_user ( &bzimg->bzhdr, src, BZI_HDR_OFFSET,
115 			 sizeof ( bzimg->bzhdr ) );
116 
117 	/* Calculate size of real-mode portion */
118 	bzimg->rm_filesz = ( ( ( bzimg->bzhdr.setup_sects ?
119 				 bzimg->bzhdr.setup_sects : 4 ) + 1 ) << 9 );
120 	if ( bzimg->rm_filesz > image->len ) {
121 		DBGC ( image, "bzImage %p too short for %zd byte of setup\n",
122 		       image, bzimg->rm_filesz );
123 		return -ENOEXEC;
124 	}
125 	bzimg->rm_memsz = BZI_ASSUMED_RM_SIZE;
126 
127 	/* Calculate size of protected-mode portion */
128 	bzimg->pm_sz = ( image->len - bzimg->rm_filesz );
129 	syssize = ( ( bzimg->pm_sz + 15 ) / 16 );
130 
131 	/* Check for signatures and determine version */
132 	if ( bzimg->bzhdr.boot_flag != BZI_BOOT_FLAG ) {
133 		DBGC ( image, "bzImage %p missing 55AA signature\n", image );
134 		return -ENOEXEC;
135 	}
136 	if ( bzimg->bzhdr.header == BZI_SIGNATURE ) {
137 		/* 2.00+ */
138 		bzimg->version = bzimg->bzhdr.version;
139 	} else {
140 		/* Pre-2.00.  Check that the syssize field is correct,
141 		 * as a guard against accepting arbitrary binary data,
142 		 * since the 55AA check is pretty lax.  Note that the
143 		 * syssize field is unreliable for protocols between
144 		 * 2.00 and 2.03 inclusive, so we should not always
145 		 * check this field.
146 		 */
147 		bzimg->version = 0x0100;
148 		if ( bzimg->bzhdr.syssize != syssize ) {
149 			DBGC ( image, "bzImage %p bad syssize %x (expected "
150 			       "%x)\n", image, bzimg->bzhdr.syssize, syssize );
151 			return -ENOEXEC;
152 		}
153 	}
154 
155 	/* Determine image type */
156 	is_bzimage = ( ( bzimg->version >= 0x0200 ) ?
157 		       ( bzimg->bzhdr.loadflags & BZI_LOAD_HIGH ) : 0 );
158 
159 	/* Calculate load address of real-mode portion */
160 	bzimg->rm_kernel_seg = ( is_bzimage ? 0x1000 : 0x9000 );
161 	bzimg->rm_kernel = real_to_user ( bzimg->rm_kernel_seg, 0 );
162 
163 	/* Allow space for the stack and heap */
164 	bzimg->rm_memsz += BZI_STACK_SIZE;
165 	bzimg->rm_heap = bzimg->rm_memsz;
166 
167 	/* Allow space for the command line */
168 	bzimg->rm_cmdline = bzimg->rm_memsz;
169 	bzimg->rm_memsz += BZI_CMDLINE_SIZE;
170 
171 	/* Calculate load address of protected-mode portion */
172 	bzimg->pm_kernel = phys_to_user ( is_bzimage ? BZI_LOAD_HIGH_ADDR
173 					: BZI_LOAD_LOW_ADDR );
174 
175 	/* Extract video mode */
176 	bzimg->vid_mode = bzimg->bzhdr.vid_mode;
177 
178 	/* Extract memory limit */
179 	bzimg->mem_limit = ( ( bzimg->version >= 0x0203 ) ?
180 			     bzimg->bzhdr.initrd_addr_max : BZI_INITRD_MAX );
181 
182 	/* Extract command line size */
183 	bzimg->cmdline_size = ( ( bzimg->version >= 0x0206 ) ?
184 				bzimg->bzhdr.cmdline_size : BZI_CMDLINE_SIZE );
185 
186 	DBGC ( image, "bzImage %p version %04x RM %#lx+%#zx PM %#lx+%#zx "
187 	       "cmdlen %zd\n", image, bzimg->version,
188 	       user_to_phys ( bzimg->rm_kernel, 0 ), bzimg->rm_filesz,
189 	       user_to_phys ( bzimg->pm_kernel, 0 ), bzimg->pm_sz,
190 	       bzimg->cmdline_size );
191 
192 	return 0;
193 }
194 
195 /**
196  * Update bzImage header in loaded kernel
197  *
198  * @v image		bzImage file
199  * @v bzimg		bzImage context
200  * @v dst		bzImage to update
201  */
bzimage_update_header(struct image * image,struct bzimage_context * bzimg,userptr_t dst)202 static void bzimage_update_header ( struct image *image,
203 				    struct bzimage_context *bzimg,
204 				    userptr_t dst ) {
205 
206 	/* Set loader type */
207 	if ( bzimg->version >= 0x0200 )
208 		bzimg->bzhdr.type_of_loader = BZI_LOADER_TYPE_IPXE;
209 
210 	/* Set heap end pointer */
211 	if ( bzimg->version >= 0x0201 ) {
212 		bzimg->bzhdr.heap_end_ptr = ( bzimg->rm_heap - 0x200 );
213 		bzimg->bzhdr.loadflags |= BZI_CAN_USE_HEAP;
214 	}
215 
216 	/* Set command line */
217 	if ( bzimg->version >= 0x0202 ) {
218 		bzimg->bzhdr.cmd_line_ptr = user_to_phys ( bzimg->rm_kernel,
219 							   bzimg->rm_cmdline );
220 	} else {
221 		bzimg->cmdline_magic.magic = BZI_CMDLINE_MAGIC;
222 		bzimg->cmdline_magic.offset = bzimg->rm_cmdline;
223 		if ( bzimg->version >= 0x0200 )
224 			bzimg->bzhdr.setup_move_size = bzimg->rm_memsz;
225 	}
226 
227 	/* Set video mode */
228 	bzimg->bzhdr.vid_mode = bzimg->vid_mode;
229 
230 	/* Set initrd address */
231 	if ( bzimg->version >= 0x0200 ) {
232 		bzimg->bzhdr.ramdisk_image = bzimg->ramdisk_image;
233 		bzimg->bzhdr.ramdisk_size = bzimg->ramdisk_size;
234 	}
235 
236 	/* Write out header structures */
237 	copy_to_user ( dst, BZI_CMDLINE_OFFSET, &bzimg->cmdline_magic,
238 		       sizeof ( bzimg->cmdline_magic ) );
239 	copy_to_user ( dst, BZI_HDR_OFFSET, &bzimg->bzhdr,
240 		       sizeof ( bzimg->bzhdr ) );
241 
242 	DBGC ( image, "bzImage %p vidmode %d\n", image, bzimg->vid_mode );
243 }
244 
245 /**
246  * Parse kernel command line for bootloader parameters
247  *
248  * @v image		bzImage file
249  * @v bzimg		bzImage context
250  * @v cmdline		Kernel command line
251  * @ret rc		Return status code
252  */
bzimage_parse_cmdline(struct image * image,struct bzimage_context * bzimg,const char * cmdline)253 static int bzimage_parse_cmdline ( struct image *image,
254 				   struct bzimage_context *bzimg,
255 				   const char *cmdline ) {
256 	char *vga;
257 	char *mem;
258 
259 	/* Look for "vga=" */
260 	if ( ( vga = strstr ( cmdline, "vga=" ) ) ) {
261 		vga += 4;
262 		if ( strcmp ( vga, "normal" ) == 0 ) {
263 			bzimg->vid_mode = BZI_VID_MODE_NORMAL;
264 		} else if ( strcmp ( vga, "ext" ) == 0 ) {
265 			bzimg->vid_mode = BZI_VID_MODE_EXT;
266 		} else if ( strcmp ( vga, "ask" ) == 0 ) {
267 			bzimg->vid_mode = BZI_VID_MODE_ASK;
268 		} else {
269 			bzimg->vid_mode = strtoul ( vga, &vga, 0 );
270 			if ( *vga && ( *vga != ' ' ) ) {
271 				DBGC ( image, "bzImage %p strange \"vga=\""
272 				       "terminator '%c'\n", image, *vga );
273 			}
274 		}
275 	}
276 
277 	/* Look for "mem=" */
278 	if ( ( mem = strstr ( cmdline, "mem=" ) ) ) {
279 		mem += 4;
280 		bzimg->mem_limit = strtoul ( mem, &mem, 0 );
281 		switch ( *mem ) {
282 		case 'G':
283 		case 'g':
284 			bzimg->mem_limit <<= 10;
285 			/* Fall through */
286 		case 'M':
287 		case 'm':
288 			bzimg->mem_limit <<= 10;
289 			/* Fall through */
290 		case 'K':
291 		case 'k':
292 			bzimg->mem_limit <<= 10;
293 			break;
294 		case '\0':
295 		case ' ':
296 			break;
297 		default:
298 			DBGC ( image, "bzImage %p strange \"mem=\" "
299 			       "terminator '%c'\n", image, *mem );
300 			break;
301 		}
302 		bzimg->mem_limit -= 1;
303 	}
304 
305 	return 0;
306 }
307 
308 /**
309  * Set command line
310  *
311  * @v image		bzImage image
312  * @v bzimg		bzImage context
313  * @v cmdline		Kernel command line
314  */
bzimage_set_cmdline(struct image * image,struct bzimage_context * bzimg,const char * cmdline)315 static void bzimage_set_cmdline ( struct image *image,
316 				  struct bzimage_context *bzimg,
317 				  const char *cmdline ) {
318 	size_t cmdline_len;
319 
320 	/* Copy command line down to real-mode portion */
321 	cmdline_len = ( strlen ( cmdline ) + 1 );
322 	if ( cmdline_len > bzimg->cmdline_size )
323 		cmdline_len = bzimg->cmdline_size;
324 	copy_to_user ( bzimg->rm_kernel, bzimg->rm_cmdline,
325 		       cmdline, cmdline_len );
326 	DBGC ( image, "bzImage %p command line \"%s\"\n", image, cmdline );
327 }
328 
329 /**
330  * Parse standalone image command line for cpio parameters
331  *
332  * @v image		bzImage file
333  * @v cpio		CPIO header
334  * @v cmdline		Command line
335  */
bzimage_parse_cpio_cmdline(struct image * image,struct cpio_header * cpio,const char * cmdline)336 static void bzimage_parse_cpio_cmdline ( struct image *image,
337 					 struct cpio_header *cpio,
338 					 const char *cmdline ) {
339 	char *arg;
340 	char *end;
341 	unsigned int mode;
342 
343 	/* Look for "mode=" */
344 	if ( ( arg = strstr ( cmdline, "mode=" ) ) ) {
345 		arg += 5;
346 		mode = strtoul ( arg, &end, 8 /* Octal for file mode */ );
347 		if ( *end && ( *end != ' ' ) ) {
348 			DBGC ( image, "bzImage %p strange \"mode=\""
349 			       "terminator '%c'\n", image, *end );
350 		}
351 		cpio_set_field ( cpio->c_mode, ( 0100000 | mode ) );
352 	}
353 }
354 
355 /**
356  * Align initrd length
357  *
358  * @v len		Length
359  * @ret len		Length rounded up to INITRD_ALIGN
360  */
bzimage_align(size_t len)361 static inline size_t bzimage_align ( size_t len ) {
362 
363 	return ( ( len + INITRD_ALIGN - 1 ) & ~( INITRD_ALIGN - 1 ) );
364 }
365 
366 /**
367  * Load initrd
368  *
369  * @v image		bzImage image
370  * @v initrd		initrd image
371  * @v address		Address at which to load, or UNULL
372  * @ret len		Length of loaded image, excluding zero-padding
373  */
bzimage_load_initrd(struct image * image,struct image * initrd,userptr_t address)374 static size_t bzimage_load_initrd ( struct image *image,
375 				    struct image *initrd,
376 				    userptr_t address ) {
377 	char *filename = initrd->cmdline;
378 	char *cmdline;
379 	struct cpio_header cpio;
380 	size_t offset;
381 	size_t name_len;
382 	size_t pad_len;
383 
384 	/* Do not include kernel image itself as an initrd */
385 	if ( initrd == image )
386 		return 0;
387 
388 	/* Create cpio header for non-prebuilt images */
389 	if ( filename && filename[0] ) {
390 		cmdline = strchr ( filename, ' ' );
391 		name_len = ( ( cmdline ? ( ( size_t ) ( cmdline - filename ) )
392 			       : strlen ( filename ) ) + 1 /* NUL */ );
393 		memset ( &cpio, '0', sizeof ( cpio ) );
394 		memcpy ( cpio.c_magic, CPIO_MAGIC, sizeof ( cpio.c_magic ) );
395 		cpio_set_field ( cpio.c_mode, 0100644 );
396 		cpio_set_field ( cpio.c_nlink, 1 );
397 		cpio_set_field ( cpio.c_filesize, initrd->len );
398 		cpio_set_field ( cpio.c_namesize, name_len );
399 		if ( cmdline ) {
400 			bzimage_parse_cpio_cmdline ( image, &cpio,
401 						     ( cmdline + 1 /* ' ' */ ));
402 		}
403 		offset = ( ( sizeof ( cpio ) + name_len + 0x03 ) & ~0x03 );
404 	} else {
405 		offset = 0;
406 		name_len = 0;
407 	}
408 
409 	/* Copy in initrd image body (and cpio header if applicable) */
410 	if ( address ) {
411 		memmove_user ( address, offset, initrd->data, 0, initrd->len );
412 		if ( offset ) {
413 			memset_user ( address, 0, 0, offset );
414 			copy_to_user ( address, 0, &cpio, sizeof ( cpio ) );
415 			copy_to_user ( address, sizeof ( cpio ), filename,
416 				       ( name_len - 1 /* NUL (or space) */ ) );
417 		}
418 		DBGC ( image, "bzImage %p initrd %p [%#08lx,%#08lx,%#08lx)"
419 		       "%s%s\n", image, initrd, user_to_phys ( address, 0 ),
420 		       user_to_phys ( address, offset ),
421 		       user_to_phys ( address, ( offset + initrd->len ) ),
422 		       ( filename ? " " : "" ), ( filename ? filename : "" ) );
423 		DBGC2_MD5A ( image, user_to_phys ( address, offset ),
424 			     user_to_virt ( address, offset ), initrd->len );
425 	}
426 	offset += initrd->len;
427 
428 	/* Zero-pad to next INITRD_ALIGN boundary */
429 	pad_len = ( ( -offset ) & ( INITRD_ALIGN - 1 ) );
430 	if ( address )
431 		memset_user ( address, offset, 0, pad_len );
432 
433 	return offset;
434 }
435 
436 /**
437  * Check that initrds can be loaded
438  *
439  * @v image		bzImage image
440  * @v bzimg		bzImage context
441  * @ret rc		Return status code
442  */
bzimage_check_initrds(struct image * image,struct bzimage_context * bzimg)443 static int bzimage_check_initrds ( struct image *image,
444 				   struct bzimage_context *bzimg ) {
445 	struct image *initrd;
446 	userptr_t bottom;
447 	size_t len = 0;
448 	int rc;
449 
450 	/* Calculate total loaded length of initrds */
451 	for_each_image ( initrd ) {
452 
453 		/* Skip kernel */
454 		if ( initrd == image )
455 			continue;
456 
457 		/* Calculate length */
458 		len += bzimage_load_initrd ( image, initrd, UNULL );
459 		len = bzimage_align ( len );
460 
461 		DBGC ( image, "bzImage %p initrd %p from [%#08lx,%#08lx)%s%s\n",
462 		       image, initrd, user_to_phys ( initrd->data, 0 ),
463 		       user_to_phys ( initrd->data, initrd->len ),
464 		       ( initrd->cmdline ? " " : "" ),
465 		       ( initrd->cmdline ? initrd->cmdline : "" ) );
466 		DBGC2_MD5A ( image, user_to_phys ( initrd->data, 0 ),
467 			     user_to_virt ( initrd->data, 0 ), initrd->len );
468 	}
469 
470 	/* Calculate lowest usable address */
471 	bottom = userptr_add ( bzimg->pm_kernel, bzimg->pm_sz );
472 
473 	/* Check that total length fits within space available for
474 	 * reshuffling.  This is a conservative check, since CPIO
475 	 * headers are not present during reshuffling, but this
476 	 * doesn't hurt and keeps the code simple.
477 	 */
478 	if ( ( rc = initrd_reshuffle_check ( len, bottom ) ) != 0 ) {
479 		DBGC ( image, "bzImage %p failed reshuffle check: %s\n",
480 		       image, strerror ( rc ) );
481 		return rc;
482 	}
483 
484 	/* Check that total length fits within kernel's memory limit */
485 	if ( user_to_phys ( bottom, len ) > bzimg->mem_limit ) {
486 		DBGC ( image, "bzImage %p not enough space for initrds\n",
487 		       image );
488 		return -ENOBUFS;
489 	}
490 
491 	return 0;
492 }
493 
494 /**
495  * Load initrds, if any
496  *
497  * @v image		bzImage image
498  * @v bzimg		bzImage context
499  */
bzimage_load_initrds(struct image * image,struct bzimage_context * bzimg)500 static void bzimage_load_initrds ( struct image *image,
501 				   struct bzimage_context *bzimg ) {
502 	struct image *initrd;
503 	struct image *highest = NULL;
504 	struct image *other;
505 	userptr_t top;
506 	userptr_t dest;
507 	size_t offset;
508 	size_t len;
509 
510 	/* Reshuffle initrds into desired order */
511 	initrd_reshuffle ( userptr_add ( bzimg->pm_kernel, bzimg->pm_sz ) );
512 
513 	/* Find highest initrd */
514 	for_each_image ( initrd ) {
515 		if ( ( highest == NULL ) ||
516 		     ( userptr_sub ( initrd->data, highest->data ) > 0 ) ) {
517 			highest = initrd;
518 		}
519 	}
520 
521 	/* Do nothing if there are no initrds */
522 	if ( ! highest )
523 		return;
524 
525 	/* Find highest usable address */
526 	top = userptr_add ( highest->data, bzimage_align ( highest->len ) );
527 	if ( user_to_phys ( top, -1 ) > bzimg->mem_limit ) {
528 		top = phys_to_user ( ( bzimg->mem_limit + 1 ) &
529 				     ~( INITRD_ALIGN - 1 ) );
530 	}
531 	DBGC ( image, "bzImage %p loading initrds from %#08lx downwards\n",
532 	       image, user_to_phys ( top, -1 ) );
533 
534 	/* Load initrds in order */
535 	for_each_image ( initrd ) {
536 
537 		/* Calculate cumulative length of following
538 		 * initrds (including padding).
539 		 */
540 		offset = 0;
541 		for_each_image ( other ) {
542 			if ( other == initrd )
543 				offset = 0;
544 			offset += bzimage_load_initrd ( image, other, UNULL );
545 			offset = bzimage_align ( offset );
546 		}
547 
548 		/* Load initrd at this address */
549 		dest = userptr_add ( top, -offset );
550 		len = bzimage_load_initrd ( image, initrd, dest );
551 
552 		/* Record initrd location */
553 		if ( ! bzimg->ramdisk_image )
554 			bzimg->ramdisk_image = user_to_phys ( dest, 0 );
555 		bzimg->ramdisk_size = ( user_to_phys ( dest, len ) -
556 					bzimg->ramdisk_image );
557 	}
558 	DBGC ( image, "bzImage %p initrds at [%#08lx,%#08lx)\n",
559 	       image, bzimg->ramdisk_image,
560 	       ( bzimg->ramdisk_image + bzimg->ramdisk_size ) );
561 }
562 
563 /**
564  * Execute bzImage image
565  *
566  * @v image		bzImage image
567  * @ret rc		Return status code
568  */
bzimage_exec(struct image * image)569 static int bzimage_exec ( struct image *image ) {
570 	struct bzimage_context bzimg;
571 	const char *cmdline = ( image->cmdline ? image->cmdline : "" );
572 	int rc;
573 
574 	/* Read and parse header from image */
575 	if ( ( rc = bzimage_parse_header ( image, &bzimg,
576 					   image->data ) ) != 0 )
577 		return rc;
578 
579 	/* Prepare segments */
580 	if ( ( rc = prep_segment ( bzimg.rm_kernel, bzimg.rm_filesz,
581 				   bzimg.rm_memsz ) ) != 0 ) {
582 		DBGC ( image, "bzImage %p could not prepare RM segment: %s\n",
583 		       image, strerror ( rc ) );
584 		return rc;
585 	}
586 	if ( ( rc = prep_segment ( bzimg.pm_kernel, bzimg.pm_sz,
587 				   bzimg.pm_sz ) ) != 0 ) {
588 		DBGC ( image, "bzImage %p could not prepare PM segment: %s\n",
589 		       image, strerror ( rc ) );
590 		return rc;
591 	}
592 
593 	/* Parse command line for bootloader parameters */
594 	if ( ( rc = bzimage_parse_cmdline ( image, &bzimg, cmdline ) ) != 0)
595 		return rc;
596 
597 	/* Check that initrds can be loaded */
598 	if ( ( rc = bzimage_check_initrds ( image, &bzimg ) ) != 0 )
599 		return rc;
600 
601 	/* Remove kernel from image list (without invalidating image pointer) */
602 	unregister_image ( image_get ( image ) );
603 
604 	/* Load segments */
605 	memcpy_user ( bzimg.rm_kernel, 0, image->data,
606 		      0, bzimg.rm_filesz );
607 	memcpy_user ( bzimg.pm_kernel, 0, image->data,
608 		      bzimg.rm_filesz, bzimg.pm_sz );
609 
610 	/* Store command line */
611 	bzimage_set_cmdline ( image, &bzimg, cmdline );
612 
613 	/* Prepare for exiting.  Must do this before loading initrds,
614 	 * since loading the initrds will corrupt the external heap.
615 	 */
616 	shutdown_boot();
617 
618 	/* Load any initrds */
619 	bzimage_load_initrds ( image, &bzimg );
620 
621 	/* Update kernel header */
622 	bzimage_update_header ( image, &bzimg, bzimg.rm_kernel );
623 
624 	DBGC ( image, "bzImage %p jumping to RM kernel at %04x:0000 "
625 	       "(stack %04x:%04zx)\n", image, ( bzimg.rm_kernel_seg + 0x20 ),
626 	       bzimg.rm_kernel_seg, bzimg.rm_heap );
627 
628 	/* Jump to the kernel */
629 	__asm__ __volatile__ ( REAL_CODE ( "movw %w0, %%ds\n\t"
630 					   "movw %w0, %%es\n\t"
631 					   "movw %w0, %%fs\n\t"
632 					   "movw %w0, %%gs\n\t"
633 					   "movw %w0, %%ss\n\t"
634 					   "movw %w1, %%sp\n\t"
635 					   "pushw %w2\n\t"
636 					   "pushw $0\n\t"
637 					   "lret\n\t" )
638 			       : : "R" ( bzimg.rm_kernel_seg ),
639 			           "R" ( bzimg.rm_heap ),
640 			           "R" ( bzimg.rm_kernel_seg + 0x20 ) );
641 
642 	/* There is no way for the image to return, since we provide
643 	 * no return address.
644 	 */
645 	assert ( 0 );
646 
647 	return -ECANCELED; /* -EIMPOSSIBLE */
648 }
649 
650 /**
651  * Probe bzImage image
652  *
653  * @v image		bzImage file
654  * @ret rc		Return status code
655  */
bzimage_probe(struct image * image)656 int bzimage_probe ( struct image *image ) {
657 	struct bzimage_context bzimg;
658 	int rc;
659 
660 	/* Read and parse header from image */
661 	if ( ( rc = bzimage_parse_header ( image, &bzimg,
662 					   image->data ) ) != 0 )
663 		return rc;
664 
665 	return 0;
666 }
667 
668 /** Linux bzImage image type */
669 struct image_type bzimage_image_type __image_type ( PROBE_NORMAL ) = {
670 	.name = "bzImage",
671 	.probe = bzimage_probe,
672 	.exec = bzimage_exec,
673 };
674