Capable of loading a multiboot image into memory
[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 __unused ) {
78         return -ENOTSUP;
79 }
80
81 /**
82  * Find multiboot header
83  *
84  * @v image             Multiboot file
85  * @v hdr               Multiboot header descriptor to fill in
86  * @ret rc              Return status code
87  */
88 static int multiboot_find_header ( struct image *image,
89                                    struct multiboot_header_info *hdr ) {
90         uint32_t buf[64];
91         size_t offset;
92         unsigned int buf_idx;
93         uint32_t checksum;
94
95         /* Scan through first 8kB of image file 256 bytes at a time.
96          * (Use the buffering to avoid the overhead of a
97          * copy_from_user() for every dword.)
98          */
99         for ( offset = 0 ; offset < 8192 ; offset += sizeof ( buf[0] ) ) {
100                 /* Check for end of image */
101                 if ( offset > image->len )
102                         break;
103                 /* Refill buffer if applicable */
104                 buf_idx = ( ( offset % sizeof ( buf ) ) / sizeof ( buf[0] ) );
105                 if ( buf_idx == 0 ) {
106                         copy_from_user ( buf, image->data, offset,
107                                          sizeof ( buf ) );
108                 }
109                 /* Check signature */
110                 if ( buf[buf_idx] != MULTIBOOT_HEADER_MAGIC )
111                         continue;
112                 /* Copy header and verify checksum */
113                 copy_from_user ( &hdr->mb, image->data, offset,
114                                  sizeof ( hdr->mb ) );
115                 checksum = ( hdr->mb.magic + hdr->mb.flags +
116                              hdr->mb.checksum );
117                 if ( checksum != 0 )
118                         continue;
119                 /* Record offset of multiboot header and return */
120                 hdr->offset = offset;
121                 return 0;
122         }
123
124         /* No multiboot header found */
125         return -ENOEXEC;
126 }
127
128 /**
129  * Load raw multiboot image into memory
130  *
131  * @v image             Multiboot file
132  * @v hdr               Multiboot header descriptor
133  * @ret rc              Return status code
134  */
135 static int multiboot_load_raw ( struct image *image,
136                                 struct multiboot_header_info *hdr ) {
137         size_t offset;
138         size_t filesz;
139         size_t memsz;
140         userptr_t buffer;
141         int rc;
142
143         /* Verify and prepare segment */
144         offset = ( hdr->offset - hdr->mb.header_addr + hdr->mb.load_addr );
145         filesz = ( hdr->mb.load_end_addr - hdr->mb.load_addr );
146         memsz = ( hdr->mb.bss_end_addr - hdr->mb.load_addr );
147         buffer = phys_to_user ( hdr->mb.load_addr );
148         if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) {
149                 DBG ( "Multiboot could not prepare segment: %s\n",
150                       strerror ( rc ) );
151                 return rc;
152         }
153
154         /* Copy image to segment */
155         copy_user ( buffer, 0, image->data, offset, filesz );
156
157         /* Record execution entry point */
158         image->entry = hdr->mb.entry_addr;
159         image->execute = multiboot_execute;
160
161         return 0;
162 }
163
164 /**
165  * Load ELF multiboot image into memory
166  *
167  * @v image             Multiboot file
168  * @ret rc              Return status code
169  */
170 static int multiboot_load_elf ( struct image *image ) {
171         int rc;
172
173         /* Load ELF image*/
174         if ( ( rc = elf_load ( image ) ) != 0 ) {
175                 DBG ( "Multiboot ELF image failed to load: %s\n",
176                       strerror ( rc ) );
177                 /* We must translate "not an ELF image" (i.e. ENOEXEC)
178                  * into "invalid multiboot image", to avoid screwing
179                  * up the image probing logic.
180                  */
181                 if ( rc == -ENOEXEC ) {
182                         return -ENOTSUP;
183                 } else {
184                         return rc;
185                 }
186         }
187
188         /* Capture execution method */
189         if ( image->execute )
190                 image->execute = multiboot_execute;
191
192         return 0;
193 }
194
195 /**
196  * Load multiboot image into memory
197  *
198  * @v image             Multiboot file
199  * @ret rc              Return status code
200  */
201 int multiboot_load ( struct image *image ) {
202         struct multiboot_header_info hdr;
203         int rc;
204
205         /* Locate multiboot header, if present */
206         if ( ( rc = multiboot_find_header ( image, &hdr ) ) != 0 ) {
207                 DBG ( "No multiboot header\n" );
208                 return rc;
209         }
210         DBG ( "Found multiboot header with flags %08lx\n", hdr.mb.flags );
211
212         /* Abort if we detect flags that we cannot support */
213         if ( hdr.mb.flags & MB_UNSUPPORTED_FLAGS ) {
214                 DBG ( "Multiboot flags %08lx not supported\n",
215                       ( hdr.mb.flags & MB_UNSUPPORTED_FLAGS ) );
216                 return -ENOTSUP;
217         }
218
219         /* Load the actual image */
220         if ( hdr.mb.flags & MB_FLAG_RAW ) {
221                 if ( ( rc = multiboot_load_raw ( image, &hdr ) ) != 0 )
222                         return rc;
223         } else {
224                 if ( ( rc = multiboot_load_elf ( image ) ) != 0 )
225                         return rc;
226         }
227
228         return 0;
229 }
230
231 /** Multiboot image type */
232 struct image_type multiboot_image_type __image_type = {
233         .name = "Multiboot",
234         .load = multiboot_load,
235 };