2 * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
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.
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.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
19 FILE_LICENCE ( GPL2_OR_LATER
);
24 * Linux bzImage image format
35 #include <gpxe/uaccess.h>
36 #include <gpxe/image.h>
37 #include <gpxe/segment.h>
38 #include <gpxe/init.h>
39 #include <gpxe/cpio.h>
40 #include <gpxe/features.h>
42 FEATURE ( FEATURE_IMAGE
, "bzImage", DHCP_EB_FEATURE_BZIMAGE
, 1 );
44 struct image_type bzimage_image_type
__image_type ( PROBE_NORMAL
);
49 struct bzimage_context
{
50 /** Boot protocol version */
52 /** Real-mode kernel portion load segment address */
53 unsigned int rm_kernel_seg
;
54 /** Real-mode kernel portion load address */
56 /** Real-mode kernel portion file size */
58 /** Real-mode heap top (offset from rm_kernel) */
60 /** Command line (offset from rm_kernel) */
62 /** Command line maximum length */
64 /** Real-mode kernel portion total memory size */
66 /** Non-real-mode kernel portion load address */
68 /** Non-real-mode kernel portion file and memory size */
71 unsigned int vid_mode
;
75 physaddr_t ramdisk_image
;
77 physaddr_t ramdisk_size
;
79 /** Command line magic block */
80 struct bzimage_cmdline cmdline_magic
;
82 struct bzimage_header bzhdr
;
86 * Parse bzImage header
88 * @v image bzImage file
89 * @v bzimg bzImage context
90 * @v src bzImage to parse
91 * @ret rc Return status code
93 static int bzimage_parse_header ( struct image
*image
,
94 struct bzimage_context
*bzimg
,
100 if ( image
->len
< ( BZI_HDR_OFFSET
+ sizeof ( bzimg
->bzhdr
) ) ) {
101 DBGC ( image
, "bzImage %p too short for kernel header\n",
106 /* Read in header structures */
107 memset ( bzimg
, 0, sizeof ( *bzimg
) );
108 copy_from_user ( &bzimg
->cmdline_magic
, src
, BZI_CMDLINE_OFFSET
,
109 sizeof ( bzimg
->cmdline_magic
) );
110 copy_from_user ( &bzimg
->bzhdr
, src
, BZI_HDR_OFFSET
,
111 sizeof ( bzimg
->bzhdr
) );
113 /* Calculate size of real-mode portion */
115 ( ( bzimg
->bzhdr
.setup_sects
? bzimg
->bzhdr
.setup_sects
: 4 ) + 1 ) << 9;
116 if ( bzimg
->rm_filesz
> image
->len
) {
117 DBGC ( image
, "bzImage %p too short for %zd byte of setup\n",
118 image
, bzimg
->rm_filesz
);
121 bzimg
->rm_memsz
= BZI_ASSUMED_RM_SIZE
;
123 /* Calculate size of protected-mode portion */
124 bzimg
->pm_sz
= ( image
->len
- bzimg
->rm_filesz
);
125 syssize
= ( ( bzimg
->pm_sz
+ 15 ) / 16 );
127 /* Check for signatures and determine version */
128 if ( bzimg
->bzhdr
.boot_flag
!= BZI_BOOT_FLAG
) {
129 DBGC ( image
, "bzImage %p missing 55AA signature\n", image
);
132 if ( bzimg
->bzhdr
.header
== BZI_SIGNATURE
) {
134 bzimg
->version
= bzimg
->bzhdr
.version
;
136 /* Pre-2.00. Check that the syssize field is correct,
137 * as a guard against accepting arbitrary binary data,
138 * since the 55AA check is pretty lax. Note that the
139 * syssize field is unreliable for protocols between
140 * 2.00 and 2.03 inclusive, so we should not always
143 bzimg
->version
= 0x0100;
144 if ( bzimg
->bzhdr
.syssize
!= syssize
) {
145 DBGC ( image
, "bzImage %p bad syssize %x (expected "
146 "%x)\n", image
, bzimg
->bzhdr
.syssize
, syssize
);
151 /* Determine image type */
152 is_bzimage
= ( ( bzimg
->version
>= 0x0200 ) ?
153 ( bzimg
->bzhdr
.loadflags
& BZI_LOAD_HIGH
) : 0 );
155 /* Calculate load address of real-mode portion */
156 bzimg
->rm_kernel_seg
= ( is_bzimage
? 0x1000 : 0x9000 );
157 bzimg
->rm_kernel
= real_to_user ( bzimg
->rm_kernel_seg
, 0 );
159 /* Allow space for the stack and heap */
160 bzimg
->rm_memsz
+= BZI_STACK_SIZE
;
161 bzimg
->rm_heap
= bzimg
->rm_memsz
;
163 /* Allow space for the command line */
164 bzimg
->rm_cmdline
= bzimg
->rm_memsz
;
165 bzimg
->rm_memsz
+= BZI_CMDLINE_SIZE
;
167 /* Calculate load address of protected-mode portion */
168 bzimg
->pm_kernel
= phys_to_user ( is_bzimage
? BZI_LOAD_HIGH_ADDR
169 : BZI_LOAD_LOW_ADDR
);
171 /* Extract video mode */
172 bzimg
->vid_mode
= bzimg
->bzhdr
.vid_mode
;
174 /* Extract memory limit */
175 bzimg
->mem_limit
= ( ( bzimg
->version
>= 0x0203 ) ?
176 bzimg
->bzhdr
.initrd_addr_max
: BZI_INITRD_MAX
);
178 /* Extract command line size */
179 bzimg
->cmdline_size
= ( ( bzimg
->version
>= 0x0206 ) ?
180 bzimg
->bzhdr
.cmdline_size
: BZI_CMDLINE_SIZE
);
182 DBGC ( image
, "bzImage %p version %04x RM %#lx+%#zx PM %#lx+%#zx "
183 "cmdlen %zd\n", image
, bzimg
->version
,
184 user_to_phys ( bzimg
->rm_kernel
, 0 ), bzimg
->rm_filesz
,
185 user_to_phys ( bzimg
->pm_kernel
, 0 ), bzimg
->pm_sz
,
186 bzimg
->cmdline_size
);
192 * Update bzImage header in loaded kernel
194 * @v image bzImage file
195 * @v bzimg bzImage context
196 * @v dst bzImage to update
198 static void bzimage_update_header ( struct image
*image
,
199 struct bzimage_context
*bzimg
,
202 /* Set loader type */
203 if ( bzimg
->version
>= 0x0200 )
204 bzimg
->bzhdr
.type_of_loader
= BZI_LOADER_TYPE_GPXE
;
206 /* Set heap end pointer */
207 if ( bzimg
->version
>= 0x0201 ) {
208 bzimg
->bzhdr
.heap_end_ptr
= ( bzimg
->rm_heap
- 0x200 );
209 bzimg
->bzhdr
.loadflags
|= BZI_CAN_USE_HEAP
;
212 /* Set command line */
213 if ( bzimg
->version
>= 0x0202 ) {
214 bzimg
->bzhdr
.cmd_line_ptr
= user_to_phys ( bzimg
->rm_kernel
,
217 bzimg
->cmdline_magic
.magic
= BZI_CMDLINE_MAGIC
;
218 bzimg
->cmdline_magic
.offset
= bzimg
->rm_cmdline
;
219 bzimg
->bzhdr
.setup_move_size
= bzimg
->rm_memsz
;
223 bzimg
->bzhdr
.vid_mode
= bzimg
->vid_mode
;
225 /* Set initrd address */
226 if ( bzimg
->version
>= 0x0200 ) {
227 bzimg
->bzhdr
.ramdisk_image
= bzimg
->ramdisk_image
;
228 bzimg
->bzhdr
.ramdisk_size
= bzimg
->ramdisk_size
;
231 /* Write out header structures */
232 copy_to_user ( dst
, BZI_CMDLINE_OFFSET
, &bzimg
->cmdline_magic
,
233 sizeof ( bzimg
->cmdline_magic
) );
234 copy_to_user ( dst
, BZI_HDR_OFFSET
, &bzimg
->bzhdr
,
235 sizeof ( bzimg
->bzhdr
) );
237 DBGC ( image
, "bzImage %p vidmode %d\n", image
, bzimg
->vid_mode
);
241 * Parse kernel command line for bootloader parameters
243 * @v image bzImage file
244 * @v bzimg bzImage context
245 * @v cmdline Kernel command line
246 * @ret rc Return status code
248 static int bzimage_parse_cmdline ( struct image
*image
,
249 struct bzimage_context
*bzimg
,
250 const char *cmdline
) {
254 /* Look for "vga=" */
255 if ( ( vga
= strstr ( cmdline
, "vga=" ) ) ) {
257 if ( strcmp ( vga
, "normal" ) == 0 ) {
258 bzimg
->vid_mode
= BZI_VID_MODE_NORMAL
;
259 } else if ( strcmp ( vga
, "ext" ) == 0 ) {
260 bzimg
->vid_mode
= BZI_VID_MODE_EXT
;
261 } else if ( strcmp ( vga
, "ask" ) == 0 ) {
262 bzimg
->vid_mode
= BZI_VID_MODE_ASK
;
264 bzimg
->vid_mode
= strtoul ( vga
, &vga
, 0 );
265 if ( *vga
&& ( *vga
!= ' ' ) ) {
266 DBGC ( image
, "bzImage %p strange \"vga=\""
267 "terminator '%c'\n", image
, *vga
);
272 /* Look for "mem=" */
273 if ( ( mem
= strstr ( cmdline
, "mem=" ) ) ) {
275 bzimg
->mem_limit
= strtoul ( mem
, &mem
, 0 );
279 bzimg
->mem_limit
<<= 10;
282 bzimg
->mem_limit
<<= 10;
285 bzimg
->mem_limit
<<= 10;
291 DBGC ( image
, "bzImage %p strange \"mem=\" "
292 "terminator '%c'\n", image
, *mem
);
295 bzimg
->mem_limit
-= 1;
304 * @v image bzImage image
305 * @v bzimg bzImage context
306 * @v cmdline Kernel command line
307 * @ret rc Return status code
309 static int bzimage_set_cmdline ( struct image
*image
,
310 struct bzimage_context
*bzimg
,
311 const char *cmdline
) {
314 /* Copy command line down to real-mode portion */
315 cmdline_len
= ( strlen ( cmdline
) + 1 );
316 if ( cmdline_len
> bzimg
->cmdline_size
)
317 cmdline_len
= bzimg
->cmdline_size
;
318 copy_to_user ( bzimg
->rm_kernel
, bzimg
->rm_cmdline
,
319 cmdline
, cmdline_len
);
320 DBGC ( image
, "bzImage %p command line \"%s\"\n", image
, cmdline
);
328 * @v image bzImage image
329 * @v initrd initrd image
330 * @v address Address at which to load, or UNULL
331 * @ret len Length of loaded image, rounded up to 4 bytes
333 static size_t bzimage_load_initrd ( struct image
*image
,
334 struct image
*initrd
,
335 userptr_t address
) {
336 char *filename
= initrd
->cmdline
;
337 struct cpio_header cpio
;
340 /* Do not include kernel image itself as an initrd */
341 if ( initrd
== image
)
344 /* Create cpio header before non-prebuilt images */
345 if ( filename
&& filename
[0] ) {
346 size_t name_len
= ( strlen ( filename
) + 1 );
348 DBGC ( image
, "bzImage %p inserting initrd %p as %s\n",
349 image
, initrd
, filename
);
350 memset ( &cpio
, '0', sizeof ( cpio
) );
351 memcpy ( cpio
.c_magic
, CPIO_MAGIC
, sizeof ( cpio
.c_magic
) );
352 cpio_set_field ( cpio
.c_mode
, 0100644 );
353 cpio_set_field ( cpio
.c_nlink
, 1 );
354 cpio_set_field ( cpio
.c_filesize
, initrd
->len
);
355 cpio_set_field ( cpio
.c_namesize
, name_len
);
357 copy_to_user ( address
, offset
, &cpio
,
360 offset
+= sizeof ( cpio
);
362 copy_to_user ( address
, offset
, filename
,
366 offset
= ( ( offset
+ 0x03 ) & ~0x03 );
369 /* Copy in initrd image body */
371 memcpy_user ( address
, offset
, initrd
->data
, 0, initrd
->len
);
372 offset
+= initrd
->len
;
374 DBGC ( image
, "bzImage %p has initrd %p at [%lx,%lx)\n",
375 image
, initrd
, user_to_phys ( address
, 0 ),
376 user_to_phys ( address
, offset
) );
379 /* Round up to 4-byte boundary */
380 offset
= ( ( offset
+ 0x03 ) & ~0x03 );
385 * Load initrds, if any
387 * @v image bzImage image
388 * @v bzimg bzImage context
389 * @ret rc Return status code
391 static int bzimage_load_initrds ( struct image
*image
,
392 struct bzimage_context
*bzimg
) {
393 struct image
*initrd
;
394 size_t total_len
= 0;
398 /* Add up length of all initrd images */
399 for_each_image ( initrd
)
400 total_len
+= bzimage_load_initrd ( image
, initrd
, UNULL
);
402 /* Give up if no initrd images found */
406 /* Find a suitable start address. Try 1MB boundaries,
407 * starting from the downloaded kernel image itself and
408 * working downwards until we hit an available region.
410 for ( address
= ( user_to_phys ( image
->data
, 0 ) & ~0xfffff ) ; ;
411 address
-= 0x100000 ) {
412 /* Check that we're not going to overwrite the
413 * kernel itself. This check isn't totally
414 * accurate, but errs on the side of caution.
416 if ( address
<= ( BZI_LOAD_HIGH_ADDR
+ image
->len
) ) {
417 DBGC ( image
, "bzImage %p could not find a location "
418 "for initrd\n", image
);
421 /* Check that we are within the kernel's range */
422 if ( ( address
+ total_len
- 1 ) > bzimg
->mem_limit
)
424 /* Prepare and verify segment */
425 if ( ( rc
= prep_segment ( phys_to_user ( address
), 0,
428 /* Use this address */
432 /* Record initrd location */
433 bzimg
->ramdisk_image
= address
;
434 bzimg
->ramdisk_size
= total_len
;
436 /* Construct initrd */
437 DBGC ( image
, "bzImage %p constructing initrd at [%lx,%lx)\n",
438 image
, address
, ( address
+ total_len
) );
439 for_each_image ( initrd
) {
440 address
+= bzimage_load_initrd ( image
, initrd
,
441 phys_to_user ( address
) );
448 * Execute bzImage image
450 * @v image bzImage image
451 * @ret rc Return status code
453 static int bzimage_exec ( struct image
*image
) {
454 struct bzimage_context bzimg
;
455 const char *cmdline
= ( image
->cmdline
? image
->cmdline
: "" );
458 /* Read and parse header from loaded kernel */
459 if ( ( rc
= bzimage_parse_header ( image
, &bzimg
,
460 image
->priv
.user
) ) != 0 )
462 assert ( bzimg
.rm_kernel
== image
->priv
.user
);
464 /* Parse command line for bootloader parameters */
465 if ( ( rc
= bzimage_parse_cmdline ( image
, &bzimg
, cmdline
) ) != 0)
468 /* Store command line */
469 if ( ( rc
= bzimage_set_cmdline ( image
, &bzimg
, cmdline
) ) != 0 )
472 /* Load any initrds */
473 if ( ( rc
= bzimage_load_initrds ( image
, &bzimg
) ) != 0 )
476 /* Update kernel header */
477 bzimage_update_header ( image
, &bzimg
, bzimg
.rm_kernel
);
479 /* Prepare for exiting */
480 shutdown ( SHUTDOWN_BOOT
);
482 DBGC ( image
, "bzImage %p jumping to RM kernel at %04x:0000 "
483 "(stack %04x:%04zx)\n", image
, ( bzimg
.rm_kernel_seg
+ 0x20 ),
484 bzimg
.rm_kernel_seg
, bzimg
.rm_heap
);
486 /* Jump to the kernel */
487 __asm__
__volatile__ ( REAL_CODE ( "movw %w0, %%ds\n\t"
496 : : "r" ( bzimg
.rm_kernel_seg
),
497 "r" ( bzimg
.rm_heap
),
498 "r" ( bzimg
.rm_kernel_seg
+ 0x20 ) );
500 /* There is no way for the image to return, since we provide
505 return -ECANCELED
; /* -EIMPOSSIBLE */
509 * Load bzImage image into memory
511 * @v image bzImage file
512 * @ret rc Return status code
514 int bzimage_load ( struct image
*image
) {
515 struct bzimage_context bzimg
;
518 /* Read and parse header from image */
519 if ( ( rc
= bzimage_parse_header ( image
, &bzimg
,
520 image
->data
) ) != 0 )
523 /* This is a bzImage image, valid or otherwise */
525 image
->type
= &bzimage_image_type
;
527 /* Prepare segments */
528 if ( ( rc
= prep_segment ( bzimg
.rm_kernel
, bzimg
.rm_filesz
,
529 bzimg
.rm_memsz
) ) != 0 ) {
530 DBGC ( image
, "bzImage %p could not prepare RM segment: %s\n",
531 image
, strerror ( rc
) );
534 if ( ( rc
= prep_segment ( bzimg
.pm_kernel
, bzimg
.pm_sz
,
535 bzimg
.pm_sz
) ) != 0 ) {
536 DBGC ( image
, "bzImage %p could not prepare PM segment: %s\n",
537 image
, strerror ( rc
) );
542 memcpy_user ( bzimg
.rm_kernel
, 0, image
->data
,
543 0, bzimg
.rm_filesz
);
544 memcpy_user ( bzimg
.pm_kernel
, 0, image
->data
,
545 bzimg
.rm_filesz
, bzimg
.pm_sz
);
547 /* Update and write out header */
548 bzimage_update_header ( image
, &bzimg
, bzimg
.rm_kernel
);
550 /* Record real-mode segment in image private data field */
551 image
->priv
.user
= bzimg
.rm_kernel
;
556 /** Linux bzImage image type */
557 struct image_type bzimage_image_type
__image_type ( PROBE_NORMAL
) = {
559 .load
= bzimage_load
,
560 .exec
= bzimage_exec
,