|
|
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: * Multiboot image format
25: *
26: */
27:
28: #include <stdio.h>
29: #include <errno.h>
30: #include <assert.h>
31: #include <realmode.h>
32: #include <multiboot.h>
33: #include <ipxe/uaccess.h>
34: #include <ipxe/image.h>
35: #include <ipxe/segment.h>
36: #include <ipxe/io.h>
37: #include <ipxe/elf.h>
38: #include <ipxe/init.h>
39: #include <ipxe/features.h>
40:
41: FEATURE ( FEATURE_IMAGE, "MBOOT", DHCP_EB_FEATURE_MULTIBOOT, 1 );
42:
43: /**
44: * Maximum number of modules we will allow for
45: *
46: * If this has bitten you: sorry. I did have a perfect scheme with a
47: * dynamically allocated list of modules on the protected-mode stack,
48: * but it was incompatible with some broken OSes that can only access
49: * low memory at boot time (even though we kindly set up 4GB flat
50: * physical addressing as per the multiboot specification.
51: *
52: */
53: #define MAX_MODULES 8
54:
55: /**
56: * Maximum combined length of command lines
57: *
58: * Again; sorry. Some broken OSes zero out any non-base memory that
59: * isn't part of the loaded module set, so we can't just use
60: * virt_to_phys(cmdline) to point to the command lines, even though
61: * this would comply with the Multiboot spec.
62: */
63: #define MB_MAX_CMDLINE 512
64:
65: /** Multiboot flags that we support */
66: #define MB_SUPPORTED_FLAGS ( MB_FLAG_PGALIGN | MB_FLAG_MEMMAP | \
67: MB_FLAG_VIDMODE | MB_FLAG_RAW )
68:
69: /** Compulsory feature multiboot flags */
70: #define MB_COMPULSORY_FLAGS 0x0000ffff
71:
72: /** Optional feature multiboot flags */
73: #define MB_OPTIONAL_FLAGS 0xffff0000
74:
75: /**
76: * Multiboot flags that we don't support
77: *
78: * We only care about the compulsory feature flags (bits 0-15); we are
79: * allowed to ignore the optional feature flags.
80: */
81: #define MB_UNSUPPORTED_FLAGS ( MB_COMPULSORY_FLAGS & ~MB_SUPPORTED_FLAGS )
82:
83: /** A multiboot header descriptor */
84: struct multiboot_header_info {
85: /** The actual multiboot header */
86: struct multiboot_header mb;
87: /** Offset of header within the multiboot image */
88: size_t offset;
89: };
90:
91: /** Multiboot module command lines */
92: static char __bss16_array ( mb_cmdlines, [MB_MAX_CMDLINE] );
93: #define mb_cmdlines __use_data16 ( mb_cmdlines )
94:
95: /** Offset within module command lines */
96: static unsigned int mb_cmdline_offset;
97:
98: /**
99: * Build multiboot memory map
100: *
101: * @v image Multiboot image
102: * @v mbinfo Multiboot information structure
103: * @v mbmemmap Multiboot memory map
104: * @v limit Maxmimum number of memory map entries
105: */
106: static void multiboot_build_memmap ( struct image *image,
107: struct multiboot_info *mbinfo,
108: struct multiboot_memory_map *mbmemmap,
109: unsigned int limit ) {
110: struct memory_map memmap;
111: unsigned int i;
112:
113: /* Get memory map */
114: get_memmap ( &memmap );
115:
116: /* Translate into multiboot format */
117: memset ( mbmemmap, 0, sizeof ( *mbmemmap ) );
118: for ( i = 0 ; i < memmap.count ; i++ ) {
119: if ( i >= limit ) {
120: DBGC ( image, "MULTIBOOT %p limit of %d memmap "
121: "entries reached\n", image, limit );
122: break;
123: }
124: mbmemmap[i].size = ( sizeof ( mbmemmap[i] ) -
125: sizeof ( mbmemmap[i].size ) );
126: mbmemmap[i].base_addr = memmap.regions[i].start;
127: mbmemmap[i].length = ( memmap.regions[i].end -
128: memmap.regions[i].start );
129: mbmemmap[i].type = MBMEM_RAM;
130: mbinfo->mmap_length += sizeof ( mbmemmap[i] );
131: if ( memmap.regions[i].start == 0 )
132: mbinfo->mem_lower = ( memmap.regions[i].end / 1024 );
133: if ( memmap.regions[i].start == 0x100000 )
134: mbinfo->mem_upper = ( ( memmap.regions[i].end -
135: 0x100000 ) / 1024 );
136: }
137: }
138:
139: /**
140: * Add command line in base memory
141: *
142: * @v imgname Image name
143: * @v cmdline Command line
144: * @ret physaddr Physical address of command line
145: */
146: physaddr_t multiboot_add_cmdline ( const char *imgname, const char *cmdline ) {
147: char *mb_cmdline;
148:
149: if ( ! cmdline )
150: cmdline = "";
151:
152: /* Copy command line to base memory buffer */
153: mb_cmdline = ( mb_cmdlines + mb_cmdline_offset );
154: mb_cmdline_offset +=
155: ( snprintf ( mb_cmdline,
156: ( sizeof ( mb_cmdlines ) - mb_cmdline_offset ),
157: "%s %s", imgname, cmdline ) + 1 );
158:
159: /* Truncate to terminating NUL in buffer if necessary */
160: if ( mb_cmdline_offset > sizeof ( mb_cmdlines ) )
161: mb_cmdline_offset = ( sizeof ( mb_cmdlines ) - 1 );
162:
163: return virt_to_phys ( mb_cmdline );
164: }
165:
166: /**
167: * Build multiboot module list
168: *
169: * @v image Multiboot image
170: * @v modules Module list to fill, or NULL
171: * @ret count Number of modules
172: */
173: static unsigned int
174: multiboot_build_module_list ( struct image *image,
175: struct multiboot_module *modules,
176: unsigned int limit ) {
177: struct image *module_image;
178: struct multiboot_module *module;
179: unsigned int count = 0;
180: unsigned int insert;
181: physaddr_t start;
182: physaddr_t end;
183: unsigned int i;
184:
185: /* Add each image as a multiboot module */
186: for_each_image ( module_image ) {
187:
188: if ( count >= limit ) {
189: DBGC ( image, "MULTIBOOT %p limit of %d modules "
190: "reached\n", image, limit );
191: break;
192: }
193:
194: /* Do not include kernel image itself as a module */
195: if ( module_image == image )
196: continue;
197:
198: /* At least some OSes expect the multiboot modules to
199: * be in ascending order, so we have to support it.
200: */
201: start = user_to_phys ( module_image->data, 0 );
202: end = user_to_phys ( module_image->data, module_image->len );
203: for ( insert = 0 ; insert < count ; insert++ ) {
204: if ( start < modules[insert].mod_start )
205: break;
206: }
207: module = &modules[insert];
208: memmove ( ( module + 1 ), module,
209: ( ( count - insert ) * sizeof ( *module ) ) );
210: module->mod_start = start;
211: module->mod_end = end;
212: module->string = multiboot_add_cmdline ( module_image->name,
213: module_image->cmdline );
214: module->reserved = 0;
215:
216: /* We promise to page-align modules */
217: assert ( ( module->mod_start & 0xfff ) == 0 );
218:
219: count++;
220: }
221:
222: /* Dump module configuration */
223: for ( i = 0 ; i < count ; i++ ) {
224: DBGC ( image, "MULTIBOOT %p module %d is [%x,%x)\n",
225: image, i, modules[i].mod_start,
226: modules[i].mod_end );
227: }
228:
229: return count;
230: }
231:
232: /**
233: * The multiboot information structure
234: *
235: * Kept in base memory because some OSes won't find it elsewhere,
236: * along with the other structures belonging to the Multiboot
237: * information table.
238: */
239: static struct multiboot_info __bss16 ( mbinfo );
240: #define mbinfo __use_data16 ( mbinfo )
241:
242: /** The multiboot bootloader name */
243: static char __data16_array ( mb_bootloader_name, [] ) = "iPXE " VERSION;
244: #define mb_bootloader_name __use_data16 ( mb_bootloader_name )
245:
246: /** The multiboot memory map */
247: static struct multiboot_memory_map
248: __bss16_array ( mbmemmap, [MAX_MEMORY_REGIONS] );
249: #define mbmemmap __use_data16 ( mbmemmap )
250:
251: /** The multiboot module list */
252: static struct multiboot_module __bss16_array ( mbmodules, [MAX_MODULES] );
253: #define mbmodules __use_data16 ( mbmodules )
254:
255: /**
256: * Find multiboot header
257: *
258: * @v image Multiboot file
259: * @v hdr Multiboot header descriptor to fill in
260: * @ret rc Return status code
261: */
262: static int multiboot_find_header ( struct image *image,
263: struct multiboot_header_info *hdr ) {
264: uint32_t buf[64];
265: size_t offset;
266: unsigned int buf_idx;
267: uint32_t checksum;
268:
269: /* Scan through first 8kB of image file 256 bytes at a time.
270: * (Use the buffering to avoid the overhead of a
271: * copy_from_user() for every dword.)
272: */
273: for ( offset = 0 ; offset < 8192 ; offset += sizeof ( buf[0] ) ) {
274: /* Check for end of image */
275: if ( offset > image->len )
276: break;
277: /* Refill buffer if applicable */
278: buf_idx = ( ( offset % sizeof ( buf ) ) / sizeof ( buf[0] ) );
279: if ( buf_idx == 0 ) {
280: copy_from_user ( buf, image->data, offset,
281: sizeof ( buf ) );
282: }
283: /* Check signature */
284: if ( buf[buf_idx] != MULTIBOOT_HEADER_MAGIC )
285: continue;
286: /* Copy header and verify checksum */
287: copy_from_user ( &hdr->mb, image->data, offset,
288: sizeof ( hdr->mb ) );
289: checksum = ( hdr->mb.magic + hdr->mb.flags +
290: hdr->mb.checksum );
291: if ( checksum != 0 )
292: continue;
293: /* Record offset of multiboot header and return */
294: hdr->offset = offset;
295: return 0;
296: }
297:
298: /* No multiboot header found */
299: return -ENOEXEC;
300: }
301:
302: /**
303: * Load raw multiboot image into memory
304: *
305: * @v image Multiboot file
306: * @v hdr Multiboot header descriptor
307: * @ret entry Entry point
308: * @ret rc Return status code
309: */
310: static int multiboot_load_raw ( struct image *image,
311: struct multiboot_header_info *hdr,
312: physaddr_t *entry ) {
313: size_t offset;
314: size_t filesz;
315: size_t memsz;
316: userptr_t buffer;
317: int rc;
318:
319: /* Sanity check */
320: if ( ! ( hdr->mb.flags & MB_FLAG_RAW ) ) {
321: DBGC ( image, "MULTIBOOT %p is not flagged as a raw image\n",
322: image );
323: return -EINVAL;
324: }
325:
326: /* Verify and prepare segment */
327: offset = ( hdr->offset - hdr->mb.header_addr + hdr->mb.load_addr );
328: filesz = ( hdr->mb.load_end_addr ?
329: ( hdr->mb.load_end_addr - hdr->mb.load_addr ) :
330: ( image->len - offset ) );
331: memsz = ( hdr->mb.bss_end_addr ?
332: ( hdr->mb.bss_end_addr - hdr->mb.load_addr ) : filesz );
333: buffer = phys_to_user ( hdr->mb.load_addr );
334: if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) {
335: DBGC ( image, "MULTIBOOT %p could not prepare segment: %s\n",
336: image, strerror ( rc ) );
337: return rc;
338: }
339:
340: /* Copy image to segment */
341: memcpy_user ( buffer, 0, image->data, offset, filesz );
342:
343: /* Record execution entry point */
344: *entry = hdr->mb.entry_addr;
345:
346: return 0;
347: }
348:
349: /**
350: * Load ELF multiboot image into memory
351: *
352: * @v image Multiboot file
353: * @ret entry Entry point
354: * @ret rc Return status code
355: */
356: static int multiboot_load_elf ( struct image *image, physaddr_t *entry ) {
357: int rc;
358:
359: /* Load ELF image*/
360: if ( ( rc = elf_load ( image, entry ) ) != 0 ) {
361: DBGC ( image, "MULTIBOOT %p ELF image failed to load: %s\n",
362: image, strerror ( rc ) );
363: return rc;
364: }
365:
366: return 0;
367: }
368:
369: /**
370: * Execute multiboot image
371: *
372: * @v image Multiboot image
373: * @ret rc Return status code
374: */
375: static int multiboot_exec ( struct image *image ) {
376: struct multiboot_header_info hdr;
377: physaddr_t entry;
378: int rc;
379:
380: /* Locate multiboot header, if present */
381: if ( ( rc = multiboot_find_header ( image, &hdr ) ) != 0 ) {
382: DBGC ( image, "MULTIBOOT %p has no multiboot header\n",
383: image );
384: return rc;
385: }
386:
387: /* Abort if we detect flags that we cannot support */
388: if ( hdr.mb.flags & MB_UNSUPPORTED_FLAGS ) {
389: DBGC ( image, "MULTIBOOT %p flags %08x not supported\n",
390: image, ( hdr.mb.flags & MB_UNSUPPORTED_FLAGS ) );
391: return -ENOTSUP;
392: }
393:
394: /* There is technically a bit MB_FLAG_RAW to indicate whether
395: * this is an ELF or a raw image. In practice, grub will use
396: * the ELF header if present, and Solaris relies on this
397: * behaviour.
398: */
399: if ( ( ( rc = multiboot_load_elf ( image, &entry ) ) != 0 ) &&
400: ( ( rc = multiboot_load_raw ( image, &hdr, &entry ) ) != 0 ) )
401: return rc;
402:
403: /* Populate multiboot information structure */
404: memset ( &mbinfo, 0, sizeof ( mbinfo ) );
405: mbinfo.flags = ( MBI_FLAG_LOADER | MBI_FLAG_MEM | MBI_FLAG_MMAP |
406: MBI_FLAG_CMDLINE | MBI_FLAG_MODS );
407: mb_cmdline_offset = 0;
408: mbinfo.cmdline = multiboot_add_cmdline ( image->name, image->cmdline );
409: mbinfo.mods_count = multiboot_build_module_list ( image, mbmodules,
410: ( sizeof(mbmodules) / sizeof(mbmodules[0]) ) );
411: mbinfo.mods_addr = virt_to_phys ( mbmodules );
412: mbinfo.mmap_addr = virt_to_phys ( mbmemmap );
413: mbinfo.boot_loader_name = virt_to_phys ( mb_bootloader_name );
414:
415: /* Multiboot images may not return and have no callback
416: * interface, so shut everything down prior to booting the OS.
417: */
418: shutdown_boot();
419:
420: /* Build memory map after unhiding bootloader memory regions as part of
421: * shutting everything down.
422: */
423: multiboot_build_memmap ( image, &mbinfo, mbmemmap,
424: ( sizeof(mbmemmap) / sizeof(mbmemmap[0]) ) );
425:
426: /* Jump to OS with flat physical addressing */
427: DBGC ( image, "MULTIBOOT %p starting execution at %lx\n",
428: image, entry );
429: __asm__ __volatile__ ( PHYS_CODE ( "pushl %%ebp\n\t"
430: "call *%%edi\n\t"
431: "popl %%ebp\n\t" )
432: : : "a" ( MULTIBOOT_BOOTLOADER_MAGIC ),
433: "b" ( virt_to_phys ( &mbinfo ) ),
434: "D" ( entry )
435: : "ecx", "edx", "esi", "memory" );
436:
437: DBGC ( image, "MULTIBOOT %p returned\n", image );
438:
439: /* It isn't safe to continue after calling shutdown() */
440: while ( 1 ) {}
441:
442: return -ECANCELED; /* -EIMPOSSIBLE, anyone? */
443: }
444:
445: /**
446: * Probe multiboot image
447: *
448: * @v image Multiboot file
449: * @ret rc Return status code
450: */
451: static int multiboot_probe ( struct image *image ) {
452: struct multiboot_header_info hdr;
453: int rc;
454:
455: /* Locate multiboot header, if present */
456: if ( ( rc = multiboot_find_header ( image, &hdr ) ) != 0 ) {
457: DBGC ( image, "MULTIBOOT %p has no multiboot header\n",
458: image );
459: return rc;
460: }
461: DBGC ( image, "MULTIBOOT %p found header with flags %08x\n",
462: image, hdr.mb.flags );
463:
464: return 0;
465: }
466:
467: /** Multiboot image type */
468: struct image_type multiboot_image_type __image_type ( PROBE_MULTIBOOT ) = {
469: .name = "Multiboot",
470: .probe = multiboot_probe,
471: .exec = multiboot_exec,
472: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.