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