cfe4589a21fdda7d409f669cdd4f6d5c30c5d834
[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 <multiboot.h>
28 #include <gpxe/uaccess.h>
29 #include <gpxe/image.h>
30 #include <gpxe/segment.h>
31 #include <gpxe/elf.h>
32
33 /** Boot modules must be page aligned */
34 #define MB_FLAG_PGALIGN 0x00000001
35
36 /** Memory map must be provided */
37 #define MB_FLAG_MEMMAP 0x00000002
38
39 /** Video mode information must be provided */
40 #define MB_FLAG_VIDMODE 0x00000004
41
42 /** Image is a raw multiboot image (not ELF) */
43 #define MB_FLAG_RAW 0x00010000
44
45 /** Multiboot flags that we support */
46 #define MB_SUPPORTED_FLAGS ( MB_FLAG_PGALIGN | MB_FLAG_MEMMAP | \
47                              MB_FLAG_VIDMODE | MB_FLAG_RAW )
48
49 /** Compulsory feature multiboot flags */
50 #define MB_COMPULSORY_FLAGS 0x0000ffff
51
52 /** Optional feature multiboot flags */
53 #define MB_OPTIONAL_FLAGS 0xffff0000
54
55 /**
56  * Multiboot flags that we don't support
57  *
58  * We only care about the compulsory feature flags (bits 0-15); we are
59  * allowed to ignore the optional feature flags.
60  */
61 #define MB_UNSUPPORTED_FLAGS ( MB_COMPULSORY_FLAGS & ~MB_SUPPORTED_FLAGS )
62
63 /** A multiboot header descriptor */
64 struct multiboot_header_info {
65         /** The actual multiboot header */
66         struct multiboot_header mb;
67         /** Offset of header within the multiboot image */
68         size_t offset;
69 };
70
71 /**
72  * Execute multiboot image
73  *
74  * @v image             ELF file
75  * @ret rc              Return status code
76  */
77 static int multiboot_execute ( struct image *image ) {
78         struct multiboot_info mbinfo;
79
80         /* Populate multiboot information structure */
81         memset ( &mbinfo, 0, sizeof ( mbinfo ) );
82         
83
84         /* Jump to OS with flat physical addressing */
85         __asm__ ( PHYS_CODE ( "call *%%edi\n\t" )
86                   : : "a" ( MULTIBOOT_BOOTLOADER_MAGIC ),
87                       "b" ( virt_to_phys ( &mbinfo ) ),
88                       "D" ( image->entry )
89                   : "ecx", "edx", "esi", "ebp" );
90
91         return -ECANCELED;
92 }
93
94 /**
95  * Find multiboot header
96  *
97  * @v image             Multiboot file
98  * @v hdr               Multiboot header descriptor to fill in
99  * @ret rc              Return status code
100  */
101 static int multiboot_find_header ( struct image *image,
102                                    struct multiboot_header_info *hdr ) {
103         uint32_t buf[64];
104         size_t offset;
105         unsigned int buf_idx;
106         uint32_t checksum;
107
108         /* Scan through first 8kB of image file 256 bytes at a time.
109          * (Use the buffering to avoid the overhead of a
110          * copy_from_user() for every dword.)
111          */
112         for ( offset = 0 ; offset < 8192 ; offset += sizeof ( buf[0] ) ) {
113                 /* Check for end of image */
114                 if ( offset > image->len )
115                         break;
116                 /* Refill buffer if applicable */
117                 buf_idx = ( ( offset % sizeof ( buf ) ) / sizeof ( buf[0] ) );
118                 if ( buf_idx == 0 ) {
119                         copy_from_user ( buf, image->data, offset,
120                                          sizeof ( buf ) );
121                 }
122                 /* Check signature */
123                 if ( buf[buf_idx] != MULTIBOOT_HEADER_MAGIC )
124                         continue;
125                 /* Copy header and verify checksum */
126                 copy_from_user ( &hdr->mb, image->data, offset,
127                                  sizeof ( hdr->mb ) );
128                 checksum = ( hdr->mb.magic + hdr->mb.flags +
129                              hdr->mb.checksum );
130                 if ( checksum != 0 )
131                         continue;
132                 /* Record offset of multiboot header and return */
133                 hdr->offset = offset;
134                 return 0;
135         }
136
137         /* No multiboot header found */
138         return -ENOEXEC;
139 }
140
141 /**
142  * Load raw multiboot image into memory
143  *
144  * @v image             Multiboot file
145  * @v hdr               Multiboot header descriptor
146  * @ret rc              Return status code
147  */
148 static int multiboot_load_raw ( struct image *image,
149                                 struct multiboot_header_info *hdr ) {
150         size_t offset;
151         size_t filesz;
152         size_t memsz;
153         userptr_t buffer;
154         int rc;
155
156         /* Verify and prepare segment */
157         offset = ( hdr->offset - hdr->mb.header_addr + hdr->mb.load_addr );
158         filesz = ( hdr->mb.load_end_addr - hdr->mb.load_addr );
159         memsz = ( hdr->mb.bss_end_addr - hdr->mb.load_addr );
160         buffer = phys_to_user ( hdr->mb.load_addr );
161         if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) {
162                 DBG ( "Multiboot could not prepare segment: %s\n",
163                       strerror ( rc ) );
164                 return rc;
165         }
166
167         /* Copy image to segment */
168         copy_user ( buffer, 0, image->data, offset, filesz );
169
170         /* Record execution entry point */
171         image->entry = hdr->mb.entry_addr;
172         image->execute = multiboot_execute;
173
174         return 0;
175 }
176
177 /**
178  * Load ELF multiboot image into memory
179  *
180  * @v image             Multiboot file
181  * @ret rc              Return status code
182  */
183 static int multiboot_load_elf ( struct image *image ) {
184         int rc;
185
186         /* Load ELF image*/
187         if ( ( rc = elf_load ( image ) ) != 0 ) {
188                 DBG ( "Multiboot ELF image failed to load: %s\n",
189                       strerror ( rc ) );
190                 /* We must translate "not an ELF image" (i.e. ENOEXEC)
191                  * into "invalid multiboot image", to avoid screwing
192                  * up the image probing logic.
193                  */
194                 if ( rc == -ENOEXEC ) {
195                         return -ENOTSUP;
196                 } else {
197                         return rc;
198                 }
199         }
200
201         /* Capture execution method */
202         if ( image->execute )
203                 image->execute = multiboot_execute;
204
205         return 0;
206 }
207
208 /**
209  * Load multiboot image into memory
210  *
211  * @v image             Multiboot file
212  * @ret rc              Return status code
213  */
214 int multiboot_load ( struct image *image ) {
215         struct multiboot_header_info hdr;
216         int rc;
217
218         /* Locate multiboot header, if present */
219         if ( ( rc = multiboot_find_header ( image, &hdr ) ) != 0 ) {
220                 DBG ( "No multiboot header\n" );
221                 return rc;
222         }
223         DBG ( "Found multiboot header with flags %08lx\n", hdr.mb.flags );
224
225         /* Abort if we detect flags that we cannot support */
226         if ( hdr.mb.flags & MB_UNSUPPORTED_FLAGS ) {
227                 DBG ( "Multiboot flags %08lx not supported\n",
228                       ( hdr.mb.flags & MB_UNSUPPORTED_FLAGS ) );
229                 return -ENOTSUP;
230         }
231
232         /* Load the actual image */
233         if ( hdr.mb.flags & MB_FLAG_RAW ) {
234                 if ( ( rc = multiboot_load_raw ( image, &hdr ) ) != 0 )
235                         return rc;
236         } else {
237                 if ( ( rc = multiboot_load_elf ( image ) ) != 0 )
238                         return rc;
239         }
240
241         return 0;
242 }
243
244 /** Multiboot image type */
245 struct image_type multiboot_image_type __image_type = {
246         .name = "Multiboot",
247         .load = multiboot_load,
248 };