|
|
1.1 root 1: /*
2: * Copyright (C) 2007 Michael Brown <[email protected]>.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
17: */
18:
19: FILE_LICENCE ( GPL2_OR_LATER );
20:
21: /**
22: * @file
23: *
24: * Linux bzImage image format
25: *
26: */
27:
28: #include <stdint.h>
29: #include <stdlib.h>
30: #include <string.h>
31: #include <errno.h>
32: #include <assert.h>
33: #include <realmode.h>
34: #include <bzimage.h>
35: #include <ipxe/uaccess.h>
36: #include <ipxe/image.h>
37: #include <ipxe/segment.h>
38: #include <ipxe/init.h>
39: #include <ipxe/cpio.h>
40: #include <ipxe/features.h>
41:
42: FEATURE ( FEATURE_IMAGE, "bzImage", DHCP_EB_FEATURE_BZIMAGE, 1 );
43:
44: /**
45: * bzImage context
46: */
47: struct bzimage_context {
48: /** Boot protocol version */
49: unsigned int version;
50: /** Real-mode kernel portion load segment address */
51: unsigned int rm_kernel_seg;
52: /** Real-mode kernel portion load address */
53: userptr_t rm_kernel;
54: /** Real-mode kernel portion file size */
55: size_t rm_filesz;
56: /** Real-mode heap top (offset from rm_kernel) */
57: size_t rm_heap;
58: /** Command line (offset from rm_kernel) */
59: size_t rm_cmdline;
60: /** Command line maximum length */
61: size_t cmdline_size;
62: /** Real-mode kernel portion total memory size */
63: size_t rm_memsz;
64: /** Non-real-mode kernel portion load address */
65: userptr_t pm_kernel;
66: /** Non-real-mode kernel portion file and memory size */
67: size_t pm_sz;
68: /** Video mode */
69: unsigned int vid_mode;
70: /** Memory limit */
71: uint64_t mem_limit;
72: /** Initrd address */
73: physaddr_t ramdisk_image;
74: /** Initrd size */
75: physaddr_t ramdisk_size;
76:
77: /** Command line magic block */
78: struct bzimage_cmdline cmdline_magic;
79: /** bzImage header */
80: struct bzimage_header bzhdr;
81: };
82:
83: /**
84: * Parse bzImage header
85: *
86: * @v image bzImage file
87: * @v bzimg bzImage context
88: * @v src bzImage to parse
89: * @ret rc Return status code
90: */
91: static int bzimage_parse_header ( struct image *image,
92: struct bzimage_context *bzimg,
93: userptr_t src ) {
94: unsigned int syssize;
95: int is_bzimage;
96:
97: /* Sanity check */
98: if ( image->len < ( BZI_HDR_OFFSET + sizeof ( bzimg->bzhdr ) ) ) {
99: DBGC ( image, "bzImage %p too short for kernel header\n",
100: image );
101: return -ENOEXEC;
102: }
103:
104: /* Read in header structures */
105: memset ( bzimg, 0, sizeof ( *bzimg ) );
106: copy_from_user ( &bzimg->cmdline_magic, src, BZI_CMDLINE_OFFSET,
107: sizeof ( bzimg->cmdline_magic ) );
108: copy_from_user ( &bzimg->bzhdr, src, BZI_HDR_OFFSET,
109: sizeof ( bzimg->bzhdr ) );
110:
111: /* Calculate size of real-mode portion */
112: bzimg->rm_filesz = ( ( ( bzimg->bzhdr.setup_sects ?
113: bzimg->bzhdr.setup_sects : 4 ) + 1 ) << 9 );
114: if ( bzimg->rm_filesz > image->len ) {
115: DBGC ( image, "bzImage %p too short for %zd byte of setup\n",
116: image, bzimg->rm_filesz );
117: return -ENOEXEC;
118: }
119: bzimg->rm_memsz = BZI_ASSUMED_RM_SIZE;
120:
121: /* Calculate size of protected-mode portion */
122: bzimg->pm_sz = ( image->len - bzimg->rm_filesz );
123: syssize = ( ( bzimg->pm_sz + 15 ) / 16 );
124:
125: /* Check for signatures and determine version */
126: if ( bzimg->bzhdr.boot_flag != BZI_BOOT_FLAG ) {
127: DBGC ( image, "bzImage %p missing 55AA signature\n", image );
128: return -ENOEXEC;
129: }
130: if ( bzimg->bzhdr.header == BZI_SIGNATURE ) {
131: /* 2.00+ */
132: bzimg->version = bzimg->bzhdr.version;
133: } else {
134: /* Pre-2.00. Check that the syssize field is correct,
135: * as a guard against accepting arbitrary binary data,
136: * since the 55AA check is pretty lax. Note that the
137: * syssize field is unreliable for protocols between
138: * 2.00 and 2.03 inclusive, so we should not always
139: * check this field.
140: */
141: bzimg->version = 0x0100;
142: if ( bzimg->bzhdr.syssize != syssize ) {
143: DBGC ( image, "bzImage %p bad syssize %x (expected "
144: "%x)\n", image, bzimg->bzhdr.syssize, syssize );
145: return -ENOEXEC;
146: }
147: }
148:
149: /* Determine image type */
150: is_bzimage = ( ( bzimg->version >= 0x0200 ) ?
151: ( bzimg->bzhdr.loadflags & BZI_LOAD_HIGH ) : 0 );
152:
153: /* Calculate load address of real-mode portion */
154: bzimg->rm_kernel_seg = ( is_bzimage ? 0x1000 : 0x9000 );
155: bzimg->rm_kernel = real_to_user ( bzimg->rm_kernel_seg, 0 );
156:
157: /* Allow space for the stack and heap */
158: bzimg->rm_memsz += BZI_STACK_SIZE;
159: bzimg->rm_heap = bzimg->rm_memsz;
160:
161: /* Allow space for the command line */
162: bzimg->rm_cmdline = bzimg->rm_memsz;
163: bzimg->rm_memsz += BZI_CMDLINE_SIZE;
164:
165: /* Calculate load address of protected-mode portion */
166: bzimg->pm_kernel = phys_to_user ( is_bzimage ? BZI_LOAD_HIGH_ADDR
167: : BZI_LOAD_LOW_ADDR );
168:
169: /* Extract video mode */
170: bzimg->vid_mode = bzimg->bzhdr.vid_mode;
171:
172: /* Extract memory limit */
173: bzimg->mem_limit = ( ( bzimg->version >= 0x0203 ) ?
174: bzimg->bzhdr.initrd_addr_max : BZI_INITRD_MAX );
175:
176: /* Extract command line size */
177: bzimg->cmdline_size = ( ( bzimg->version >= 0x0206 ) ?
178: bzimg->bzhdr.cmdline_size : BZI_CMDLINE_SIZE );
179:
180: DBGC ( image, "bzImage %p version %04x RM %#lx+%#zx PM %#lx+%#zx "
181: "cmdlen %zd\n", image, bzimg->version,
182: user_to_phys ( bzimg->rm_kernel, 0 ), bzimg->rm_filesz,
183: user_to_phys ( bzimg->pm_kernel, 0 ), bzimg->pm_sz,
184: bzimg->cmdline_size );
185:
186: return 0;
187: }
188:
189: /**
190: * Update bzImage header in loaded kernel
191: *
192: * @v image bzImage file
193: * @v bzimg bzImage context
194: * @v dst bzImage to update
195: */
196: static void bzimage_update_header ( struct image *image,
197: struct bzimage_context *bzimg,
198: userptr_t dst ) {
199:
200: /* Set loader type */
201: if ( bzimg->version >= 0x0200 )
202: bzimg->bzhdr.type_of_loader = BZI_LOADER_TYPE_IPXE;
203:
204: /* Set heap end pointer */
205: if ( bzimg->version >= 0x0201 ) {
206: bzimg->bzhdr.heap_end_ptr = ( bzimg->rm_heap - 0x200 );
207: bzimg->bzhdr.loadflags |= BZI_CAN_USE_HEAP;
208: }
209:
210: /* Set command line */
211: if ( bzimg->version >= 0x0202 ) {
212: bzimg->bzhdr.cmd_line_ptr = user_to_phys ( bzimg->rm_kernel,
213: bzimg->rm_cmdline );
214: } else {
215: bzimg->cmdline_magic.magic = BZI_CMDLINE_MAGIC;
216: bzimg->cmdline_magic.offset = bzimg->rm_cmdline;
217: bzimg->bzhdr.setup_move_size = bzimg->rm_memsz;
218: }
219:
220: /* Set video mode */
221: bzimg->bzhdr.vid_mode = bzimg->vid_mode;
222:
223: /* Set initrd address */
224: if ( bzimg->version >= 0x0200 ) {
225: bzimg->bzhdr.ramdisk_image = bzimg->ramdisk_image;
226: bzimg->bzhdr.ramdisk_size = bzimg->ramdisk_size;
227: }
228:
229: /* Write out header structures */
230: copy_to_user ( dst, BZI_CMDLINE_OFFSET, &bzimg->cmdline_magic,
231: sizeof ( bzimg->cmdline_magic ) );
232: copy_to_user ( dst, BZI_HDR_OFFSET, &bzimg->bzhdr,
233: sizeof ( bzimg->bzhdr ) );
234:
235: DBGC ( image, "bzImage %p vidmode %d\n", image, bzimg->vid_mode );
236: }
237:
238: /**
239: * Parse kernel command line for bootloader parameters
240: *
241: * @v image bzImage file
242: * @v bzimg bzImage context
243: * @v cmdline Kernel command line
244: * @ret rc Return status code
245: */
246: static int bzimage_parse_cmdline ( struct image *image,
247: struct bzimage_context *bzimg,
248: const char *cmdline ) {
249: char *vga;
250: char *mem;
251:
252: /* Look for "vga=" */
253: if ( ( vga = strstr ( cmdline, "vga=" ) ) ) {
254: vga += 4;
255: if ( strcmp ( vga, "normal" ) == 0 ) {
256: bzimg->vid_mode = BZI_VID_MODE_NORMAL;
257: } else if ( strcmp ( vga, "ext" ) == 0 ) {
258: bzimg->vid_mode = BZI_VID_MODE_EXT;
259: } else if ( strcmp ( vga, "ask" ) == 0 ) {
260: bzimg->vid_mode = BZI_VID_MODE_ASK;
261: } else {
262: bzimg->vid_mode = strtoul ( vga, &vga, 0 );
263: if ( *vga && ( *vga != ' ' ) ) {
264: DBGC ( image, "bzImage %p strange \"vga=\""
265: "terminator '%c'\n", image, *vga );
266: }
267: }
268: }
269:
270: /* Look for "mem=" */
271: if ( ( mem = strstr ( cmdline, "mem=" ) ) ) {
272: mem += 4;
273: bzimg->mem_limit = strtoul ( mem, &mem, 0 );
274: switch ( *mem ) {
275: case 'G':
276: case 'g':
277: bzimg->mem_limit <<= 10;
278: case 'M':
279: case 'm':
280: bzimg->mem_limit <<= 10;
281: case 'K':
282: case 'k':
283: bzimg->mem_limit <<= 10;
284: break;
285: case '\0':
286: case ' ':
287: break;
288: default:
289: DBGC ( image, "bzImage %p strange \"mem=\" "
290: "terminator '%c'\n", image, *mem );
291: break;
292: }
293: bzimg->mem_limit -= 1;
294: }
295:
296: return 0;
297: }
298:
299: /**
300: * Set command line
301: *
302: * @v image bzImage image
303: * @v bzimg bzImage context
304: * @v cmdline Kernel command line
305: * @ret rc Return status code
306: */
307: static int bzimage_set_cmdline ( struct image *image,
308: struct bzimage_context *bzimg,
309: const char *cmdline ) {
310: size_t cmdline_len;
311:
312: /* Copy command line down to real-mode portion */
313: cmdline_len = ( strlen ( cmdline ) + 1 );
314: if ( cmdline_len > bzimg->cmdline_size )
315: cmdline_len = bzimg->cmdline_size;
316: copy_to_user ( bzimg->rm_kernel, bzimg->rm_cmdline,
317: cmdline, cmdline_len );
318: DBGC ( image, "bzImage %p command line \"%s\"\n", image, cmdline );
319:
320: return 0;
321: }
322:
323: /**
324: * Load initrd
325: *
326: * @v image bzImage image
327: * @v initrd initrd image
328: * @v address Address at which to load, or UNULL
329: * @ret len Length of loaded image, rounded up to 4 bytes
330: */
331: static size_t bzimage_load_initrd ( struct image *image,
332: struct image *initrd,
333: userptr_t address ) {
334: char *filename = initrd->cmdline;
335: struct cpio_header cpio;
336: size_t offset = 0;
337:
338: /* Do not include kernel image itself as an initrd */
339: if ( initrd == image )
340: return 0;
341:
342: /* Create cpio header before non-prebuilt images */
343: if ( filename && filename[0] ) {
344: size_t name_len = ( strlen ( filename ) + 1 );
345:
346: DBGC ( image, "bzImage %p inserting initrd %p as %s\n",
347: image, initrd, filename );
348: memset ( &cpio, '0', sizeof ( cpio ) );
349: memcpy ( cpio.c_magic, CPIO_MAGIC, sizeof ( cpio.c_magic ) );
350: cpio_set_field ( cpio.c_mode, 0100644 );
351: cpio_set_field ( cpio.c_nlink, 1 );
352: cpio_set_field ( cpio.c_filesize, initrd->len );
353: cpio_set_field ( cpio.c_namesize, name_len );
354: if ( address ) {
355: copy_to_user ( address, offset, &cpio,
356: sizeof ( cpio ) );
357: }
358: offset += sizeof ( cpio );
359: if ( address ) {
360: copy_to_user ( address, offset, filename,
361: name_len );
362: }
363: offset += name_len;
364: offset = ( ( offset + 0x03 ) & ~0x03 );
365: }
366:
367: /* Copy in initrd image body */
368: if ( address )
369: memcpy_user ( address, offset, initrd->data, 0, initrd->len );
370: offset += initrd->len;
371: if ( address ) {
372: DBGC ( image, "bzImage %p has initrd %p at [%lx,%lx)\n",
373: image, initrd, user_to_phys ( address, 0 ),
374: user_to_phys ( address, offset ) );
375: }
376:
377: /* Round up to 4-byte boundary */
378: offset = ( ( offset + 0x03 ) & ~0x03 );
379: return offset;
380: }
381:
382: /**
383: * Load initrds, if any
384: *
385: * @v image bzImage image
386: * @v bzimg bzImage context
387: * @ret rc Return status code
388: */
389: static int bzimage_load_initrds ( struct image *image,
390: struct bzimage_context *bzimg ) {
391: struct image *initrd;
392: size_t total_len = 0;
393: physaddr_t address;
394: int rc;
395:
396: /* Add up length of all initrd images */
397: for_each_image ( initrd )
398: total_len += bzimage_load_initrd ( image, initrd, UNULL );
399:
400: /* Give up if no initrd images found */
401: if ( ! total_len )
402: return 0;
403:
404: /* Find a suitable start address. Try 1MB boundaries,
405: * starting from the downloaded kernel image itself and
406: * working downwards until we hit an available region.
407: */
408: for ( address = ( user_to_phys ( image->data, 0 ) & ~0xfffff ) ; ;
409: address -= 0x100000 ) {
410: /* Check that we're not going to overwrite the
411: * kernel itself. This check isn't totally
412: * accurate, but errs on the side of caution.
413: */
414: if ( address <= ( BZI_LOAD_HIGH_ADDR + image->len ) ) {
415: DBGC ( image, "bzImage %p could not find a location "
416: "for initrd\n", image );
417: return -ENOBUFS;
418: }
419: /* Check that we are within the kernel's range */
420: if ( ( address + total_len - 1 ) > bzimg->mem_limit )
421: continue;
422: /* Prepare and verify segment */
423: if ( ( rc = prep_segment ( phys_to_user ( address ), 0,
424: total_len ) ) != 0 )
425: continue;
426: /* Use this address */
427: break;
428: }
429:
430: /* Record initrd location */
431: bzimg->ramdisk_image = address;
432: bzimg->ramdisk_size = total_len;
433:
434: /* Construct initrd */
435: DBGC ( image, "bzImage %p constructing initrd at [%lx,%lx)\n",
436: image, address, ( address + total_len ) );
437: for_each_image ( initrd ) {
438: address += bzimage_load_initrd ( image, initrd,
439: phys_to_user ( address ) );
440: }
441:
442: return 0;
443: }
444:
445: /**
446: * Execute bzImage image
447: *
448: * @v image bzImage image
449: * @ret rc Return status code
450: */
451: static int bzimage_exec ( struct image *image ) {
452: struct bzimage_context bzimg;
453: const char *cmdline = ( image->cmdline ? image->cmdline : "" );
454: int rc;
455:
456: /* Read and parse header from image */
457: if ( ( rc = bzimage_parse_header ( image, &bzimg,
458: image->data ) ) != 0 )
459: return rc;
460:
461: /* Prepare segments */
462: if ( ( rc = prep_segment ( bzimg.rm_kernel, bzimg.rm_filesz,
463: bzimg.rm_memsz ) ) != 0 ) {
464: DBGC ( image, "bzImage %p could not prepare RM segment: %s\n",
465: image, strerror ( rc ) );
466: return rc;
467: }
468: if ( ( rc = prep_segment ( bzimg.pm_kernel, bzimg.pm_sz,
469: bzimg.pm_sz ) ) != 0 ) {
470: DBGC ( image, "bzImage %p could not prepare PM segment: %s\n",
471: image, strerror ( rc ) );
472: return rc;
473: }
474:
475: /* Load segments */
476: memcpy_user ( bzimg.rm_kernel, 0, image->data,
477: 0, bzimg.rm_filesz );
478: memcpy_user ( bzimg.pm_kernel, 0, image->data,
479: bzimg.rm_filesz, bzimg.pm_sz );
480:
481: /* Update and write out header */
482: bzimage_update_header ( image, &bzimg, bzimg.rm_kernel );
483:
484: /* Parse command line for bootloader parameters */
485: if ( ( rc = bzimage_parse_cmdline ( image, &bzimg, cmdline ) ) != 0)
486: return rc;
487:
488: /* Store command line */
489: if ( ( rc = bzimage_set_cmdline ( image, &bzimg, cmdline ) ) != 0 )
490: return rc;
491:
492: /* Load any initrds */
493: if ( ( rc = bzimage_load_initrds ( image, &bzimg ) ) != 0 )
494: return rc;
495:
496: /* Update kernel header */
497: bzimage_update_header ( image, &bzimg, bzimg.rm_kernel );
498:
499: /* Prepare for exiting */
500: shutdown_boot();
501:
502: DBGC ( image, "bzImage %p jumping to RM kernel at %04x:0000 "
503: "(stack %04x:%04zx)\n", image, ( bzimg.rm_kernel_seg + 0x20 ),
504: bzimg.rm_kernel_seg, bzimg.rm_heap );
505:
506: /* Jump to the kernel */
507: __asm__ __volatile__ ( REAL_CODE ( "movw %w0, %%ds\n\t"
508: "movw %w0, %%es\n\t"
509: "movw %w0, %%fs\n\t"
510: "movw %w0, %%gs\n\t"
511: "movw %w0, %%ss\n\t"
512: "movw %w1, %%sp\n\t"
513: "pushw %w2\n\t"
514: "pushw $0\n\t"
515: "lret\n\t" )
516: : : "r" ( bzimg.rm_kernel_seg ),
517: "r" ( bzimg.rm_heap ),
518: "r" ( bzimg.rm_kernel_seg + 0x20 ) );
519:
520: /* There is no way for the image to return, since we provide
521: * no return address.
522: */
523: assert ( 0 );
524:
525: return -ECANCELED; /* -EIMPOSSIBLE */
526: }
527:
528: /**
529: * Probe bzImage image
530: *
531: * @v image bzImage file
532: * @ret rc Return status code
533: */
534: int bzimage_probe ( struct image *image ) {
535: struct bzimage_context bzimg;
536: int rc;
537:
538: /* Read and parse header from image */
539: if ( ( rc = bzimage_parse_header ( image, &bzimg,
540: image->data ) ) != 0 )
541: return rc;
542:
543: return 0;
544: }
545:
546: /** Linux bzImage image type */
547: struct image_type bzimage_image_type __image_type ( PROBE_NORMAL ) = {
548: .name = "bzImage",
549: .probe = bzimage_probe,
550: .exec = bzimage_exec,
551: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.