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