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