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.
22 * Multiboot image format
27 #include <multiboot.h>
28 #include <gpxe/uaccess.h>
29 #include <gpxe/image.h>
30 #include <gpxe/segment.h>
31 #include <gpxe/memmap.h>
34 /** Multiboot flags that we support */
35 #define MB_SUPPORTED_FLAGS ( MB_FLAG_PGALIGN | MB_FLAG_MEMMAP | \
36 MB_FLAG_VIDMODE | MB_FLAG_RAW )
38 /** Compulsory feature multiboot flags */
39 #define MB_COMPULSORY_FLAGS 0x0000ffff
41 /** Optional feature multiboot flags */
42 #define MB_OPTIONAL_FLAGS 0xffff0000
45 * Multiboot flags that we don't support
47 * We only care about the compulsory feature flags (bits 0-15); we are
48 * allowed to ignore the optional feature flags.
50 #define MB_UNSUPPORTED_FLAGS ( MB_COMPULSORY_FLAGS & ~MB_SUPPORTED_FLAGS )
52 /** A multiboot header descriptor */
53 struct multiboot_header_info {
54 /** The actual multiboot header */
55 struct multiboot_header mb;
56 /** Offset of header within the multiboot image */
61 * Execute multiboot image
64 * @ret rc Return status code
66 static int multiboot_execute ( struct image *image ) {
67 static const char *bootloader_name = "gPXE " VERSION;
68 struct multiboot_info mbinfo;
69 struct memory_map memmap;
70 struct multiboot_memory_map mbmemmap[ sizeof ( memmap.regions ) /
71 sizeof ( memmap.regions[0] ) ];
74 /* Populate multiboot information structure */
75 memset ( &mbinfo, 0, sizeof ( mbinfo ) );
77 /* Set boot loader name */
78 mbinfo.flags |= MBI_FLAG_LOADER;
79 mbinfo.boot_loader_name = virt_to_phys ( bootloader_name );
82 get_memmap ( &memmap );
83 memset ( mbmemmap, 0, sizeof ( mbmemmap ) );
84 for ( i = 0 ; i < memmap.count ; i++ ) {
85 mbmemmap[i].size = sizeof ( mbmemmap[i] );
86 mbmemmap[i].base_addr = memmap.regions[i].start;
87 mbmemmap[i].length = ( memmap.regions[i].end -
88 memmap.regions[i].start );
89 mbmemmap[i].type = MBMEM_RAM;
90 mbinfo.mmap_length += sizeof ( mbmemmap[i] );
91 if ( memmap.regions[i].start == 0 )
92 mbinfo.mem_lower = memmap.regions[i].end;
93 if ( memmap.regions[i].start == 0x100000 )
94 mbinfo.mem_upper = ( memmap.regions[i].end - 0x100000);
96 mbinfo.flags |= ( MBI_FLAG_MEM | MBI_FLAG_MMAP );
97 mbinfo.mmap_addr = virt_to_phys ( &mbmemmap[0].base_addr );
99 /* Set command line, if present */
100 if ( image->cmdline ) {
101 mbinfo.flags |= MBI_FLAG_CMDLINE;
102 mbinfo.cmdline = virt_to_phys ( image->cmdline );
105 /* Jump to OS with flat physical addressing */
106 __asm__ __volatile__ ( PHYS_CODE ( "xchgw %%bx,%%bx\n\t"
108 : : "a" ( MULTIBOOT_BOOTLOADER_MAGIC ),
109 "b" ( virt_to_phys ( &mbinfo ) ),
111 : "ecx", "edx", "esi", "ebp" );
117 * Find multiboot header
119 * @v image Multiboot file
120 * @v hdr Multiboot header descriptor to fill in
121 * @ret rc Return status code
123 static int multiboot_find_header ( struct image *image,
124 struct multiboot_header_info *hdr ) {
127 unsigned int buf_idx;
130 /* Scan through first 8kB of image file 256 bytes at a time.
131 * (Use the buffering to avoid the overhead of a
132 * copy_from_user() for every dword.)
134 for ( offset = 0 ; offset < 8192 ; offset += sizeof ( buf[0] ) ) {
135 /* Check for end of image */
136 if ( offset > image->len )
138 /* Refill buffer if applicable */
139 buf_idx = ( ( offset % sizeof ( buf ) ) / sizeof ( buf[0] ) );
140 if ( buf_idx == 0 ) {
141 copy_from_user ( buf, image->data, offset,
144 /* Check signature */
145 if ( buf[buf_idx] != MULTIBOOT_HEADER_MAGIC )
147 /* Copy header and verify checksum */
148 copy_from_user ( &hdr->mb, image->data, offset,
149 sizeof ( hdr->mb ) );
150 checksum = ( hdr->mb.magic + hdr->mb.flags +
154 /* Record offset of multiboot header and return */
155 hdr->offset = offset;
159 /* No multiboot header found */
164 * Load raw multiboot image into memory
166 * @v image Multiboot file
167 * @v hdr Multiboot header descriptor
168 * @ret rc Return status code
170 static int multiboot_load_raw ( struct image *image,
171 struct multiboot_header_info *hdr ) {
178 /* Verify and prepare segment */
179 offset = ( hdr->offset - hdr->mb.header_addr + hdr->mb.load_addr );
180 filesz = ( hdr->mb.load_end_addr - hdr->mb.load_addr );
181 memsz = ( hdr->mb.bss_end_addr - hdr->mb.load_addr );
182 buffer = phys_to_user ( hdr->mb.load_addr );
183 if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) {
184 DBG ( "Multiboot could not prepare segment: %s\n",
189 /* Copy image to segment */
190 copy_user ( buffer, 0, image->data, offset, filesz );
192 /* Record execution entry point */
193 image->entry = hdr->mb.entry_addr;
194 image->execute = multiboot_execute;
200 * Load ELF multiboot image into memory
202 * @v image Multiboot file
203 * @ret rc Return status code
205 static int multiboot_load_elf ( struct image *image ) {
209 if ( ( rc = elf_load ( image ) ) != 0 ) {
210 DBG ( "Multiboot ELF image failed to load: %s\n",
212 /* We must translate "not an ELF image" (i.e. ENOEXEC)
213 * into "invalid multiboot image", to avoid screwing
214 * up the image probing logic.
216 if ( rc == -ENOEXEC ) {
223 /* Capture execution method */
224 if ( image->execute )
225 image->execute = multiboot_execute;
231 * Load multiboot image into memory
233 * @v image Multiboot file
234 * @ret rc Return status code
236 int multiboot_load ( struct image *image ) {
237 struct multiboot_header_info hdr;
240 /* Locate multiboot header, if present */
241 if ( ( rc = multiboot_find_header ( image, &hdr ) ) != 0 ) {
242 DBG ( "No multiboot header\n" );
245 DBG ( "Found multiboot header with flags %08lx\n", hdr.mb.flags );
247 /* Abort if we detect flags that we cannot support */
248 if ( hdr.mb.flags & MB_UNSUPPORTED_FLAGS ) {
249 DBG ( "Multiboot flags %08lx not supported\n",
250 ( hdr.mb.flags & MB_UNSUPPORTED_FLAGS ) );
254 /* Load the actual image */
255 if ( hdr.mb.flags & MB_FLAG_RAW ) {
256 if ( ( rc = multiboot_load_raw ( image, &hdr ) ) != 0 )
259 if ( ( rc = multiboot_load_elf ( image ) ) != 0 )
266 /** Multiboot image type */
267 struct image_type multiboot_image_type __image_type = {
269 .load = multiboot_load,