bd9d5b3e698588db594a2c334d715e0e883bab92
[grub.git] / grub-core / loader / multiboot.c
1 /* multiboot.c - boot a multiboot OS image. */
2 /*
3  *  GRUB  --  GRand Unified Bootloader
4  *  Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,2009,2010  Free Software Foundation, Inc.
5  *
6  *  GRUB is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  GRUB is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 /*
21  *  FIXME: The following features from the Multiboot specification still
22  *         need to be implemented:
23  *  - drives table
24  *  - ROM configuration table
25  *  - SMBIOS tables
26  *  - Networking information
27  */
28
29 #include <grub/loader.h>
30 #include <grub/command.h>
31 #include <grub/multiboot.h>
32 #include <grub/cpu/multiboot.h>
33 #include <grub/elf.h>
34 #include <grub/aout.h>
35 #include <grub/file.h>
36 #include <grub/err.h>
37 #include <grub/dl.h>
38 #include <grub/mm.h>
39 #include <grub/misc.h>
40 #include <grub/env.h>
41 #include <grub/cpu/relocator.h>
42 #include <grub/video.h>
43 #include <grub/memory.h>
44 #include <grub/i18n.h>
45
46 GRUB_MOD_LICENSE ("GPLv3+");
47
48 #ifdef GRUB_MACHINE_EFI
49 #include <grub/efi/efi.h>
50 #endif
51
52 struct grub_relocator *grub_multiboot_relocator = NULL;
53 grub_uint32_t grub_multiboot_payload_eip;
54 #if defined (GRUB_MACHINE_PCBIOS) || defined (GRUB_MACHINE_MULTIBOOT) || defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_QEMU)
55 #define DEFAULT_VIDEO_MODE "text"
56 #else
57 #define DEFAULT_VIDEO_MODE "auto"
58 #endif
59
60 static int accepts_video;
61 static int accepts_ega_text;
62 static int console_required;
63 static grub_dl_t my_mod;
64
65
66 /* Helper for grub_get_multiboot_mmap_count.  */
67 static int
68 count_hook (grub_uint64_t addr __attribute__ ((unused)),
69             grub_uint64_t size __attribute__ ((unused)),
70             grub_memory_type_t type __attribute__ ((unused)), void *data)
71 {
72   grub_size_t *count = data;
73
74   (*count)++;
75   return 0;
76 }
77
78 /* Return the length of the Multiboot mmap that will be needed to allocate
79    our platform's map.  */
80 grub_uint32_t
81 grub_get_multiboot_mmap_count (void)
82 {
83   grub_size_t count = 0;
84
85   grub_mmap_iterate (count_hook, &count);
86
87   return count;
88 }
89
90 grub_err_t
91 grub_multiboot_set_video_mode (void)
92 {
93   grub_err_t err;
94   const char *modevar;
95
96 #if GRUB_MACHINE_HAS_VGA_TEXT
97   if (accepts_video)
98 #endif
99     {
100       modevar = grub_env_get ("gfxpayload");
101       if (! modevar || *modevar == 0)
102         err = grub_video_set_mode (DEFAULT_VIDEO_MODE, 0, 0);
103       else
104         {
105           char *tmp;
106           tmp = grub_xasprintf ("%s;" DEFAULT_VIDEO_MODE, modevar);
107           if (! tmp)
108             return grub_errno;
109           err = grub_video_set_mode (tmp, 0, 0);
110           grub_free (tmp);
111         }
112     }
113 #if GRUB_MACHINE_HAS_VGA_TEXT
114   else
115     err = grub_video_set_mode ("text", 0, 0);
116 #endif
117
118   return err;
119 }
120
121 #ifdef GRUB_MACHINE_EFI
122 #ifdef __x86_64__
123 #define grub_relocator_efi_boot         grub_relocator64_efi_boot
124 #define grub_relocator_efi_state        grub_relocator64_efi_state
125 #endif
126 #endif
127
128 #ifdef grub_relocator_efi_boot
129 static void
130 efi_boot (struct grub_relocator *rel,
131           grub_uint32_t target)
132 {
133   struct grub_relocator_efi_state state_efi = MULTIBOOT_EFI_INITIAL_STATE;
134
135   state_efi.MULTIBOOT_EFI_ENTRY_REGISTER = grub_multiboot_payload_eip;
136   state_efi.MULTIBOOT_EFI_MBI_REGISTER = target;
137
138   grub_relocator_efi_boot (rel, state_efi);
139 }
140 #else
141 #define grub_efi_is_finished    1
142 static void
143 efi_boot (struct grub_relocator *rel __attribute__ ((unused)),
144           grub_uint32_t target __attribute__ ((unused)))
145 {
146 }
147 #endif
148
149 #if defined (__i386__) || defined (__x86_64__)
150 static void
151 normal_boot (struct grub_relocator *rel, struct grub_relocator32_state state)
152 {
153   grub_relocator32_boot (rel, state, 0);
154 }
155 #else
156 static void
157 normal_boot (struct grub_relocator *rel, struct grub_relocator32_state state)
158 {
159   grub_relocator32_boot (rel, state);
160 }
161 #endif
162
163 static grub_err_t
164 grub_multiboot_boot (void)
165 {
166   grub_err_t err;
167   struct grub_relocator32_state state = MULTIBOOT_INITIAL_STATE;
168
169   state.MULTIBOOT_ENTRY_REGISTER = grub_multiboot_payload_eip;
170
171   err = grub_multiboot_make_mbi (&state.MULTIBOOT_MBI_REGISTER);
172
173   if (err)
174     return err;
175
176   if (grub_efi_is_finished)
177     normal_boot (grub_multiboot_relocator, state);
178   else
179     efi_boot (grub_multiboot_relocator, state.MULTIBOOT_MBI_REGISTER);
180
181   /* Not reached.  */
182   return GRUB_ERR_NONE;
183 }
184
185 static grub_err_t
186 grub_multiboot_unload (void)
187 {
188   grub_multiboot_free_mbi ();
189
190   grub_relocator_unload (grub_multiboot_relocator);
191   grub_multiboot_relocator = NULL;
192
193   grub_dl_unref (my_mod);
194
195   return GRUB_ERR_NONE;
196 }
197
198 static grub_uint64_t highest_load;
199
200 #define MULTIBOOT_LOAD_ELF64
201 #include "multiboot_elfxx.c"
202 #undef MULTIBOOT_LOAD_ELF64
203
204 #define MULTIBOOT_LOAD_ELF32
205 #include "multiboot_elfxx.c"
206 #undef MULTIBOOT_LOAD_ELF32
207
208 /* Load ELF32 or ELF64.  */
209 grub_err_t
210 grub_multiboot_load_elf (mbi_load_data_t *mld)
211 {
212   if (grub_multiboot_is_elf32 (mld->buffer))
213     return grub_multiboot_load_elf32 (mld);
214   else if (grub_multiboot_is_elf64 (mld->buffer))
215     return grub_multiboot_load_elf64 (mld);
216
217   return grub_error (GRUB_ERR_UNKNOWN_OS, N_("invalid arch-dependent ELF magic"));
218 }
219
220 grub_err_t
221 grub_multiboot_set_console (int console_type, int accepted_consoles,
222                             int width, int height, int depth,
223                             int console_req)
224 {
225   console_required = console_req;
226   if (!(accepted_consoles 
227         & (GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER
228            | (GRUB_MACHINE_HAS_VGA_TEXT ? GRUB_MULTIBOOT_CONSOLE_EGA_TEXT : 0))))
229     {
230       if (console_required)
231         return grub_error (GRUB_ERR_BAD_OS,
232                            "OS requires a console but none is available");
233       grub_puts_ (N_("WARNING: no console will be available to OS"));
234       accepts_video = 0;
235       accepts_ega_text = 0;
236       return GRUB_ERR_NONE;
237     }
238
239   if (console_type == GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER)
240     {
241       char *buf;
242       if (depth && width && height)
243         buf = grub_xasprintf ("%dx%dx%d,%dx%d,auto", width,
244                               height, depth, width, height);
245       else if (width && height)
246         buf = grub_xasprintf ("%dx%d,auto", width, height);
247       else
248         buf = grub_strdup ("auto");
249
250       if (!buf)
251         return grub_errno;
252       grub_env_set ("gfxpayload", buf);
253       grub_free (buf);
254     }
255   else
256     {
257 #if GRUB_MACHINE_HAS_VGA_TEXT
258       grub_env_set ("gfxpayload", "text");
259 #else
260       /* Always use video if no VGA text is available.  */
261       grub_env_set ("gfxpayload", "auto");
262 #endif
263     }
264
265   accepts_video = !!(accepted_consoles & GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER);
266   accepts_ega_text = !!(accepted_consoles & GRUB_MULTIBOOT_CONSOLE_EGA_TEXT);
267   return GRUB_ERR_NONE;
268 }
269
270 static grub_err_t
271 grub_cmd_multiboot (grub_command_t cmd __attribute__ ((unused)),
272                     int argc, char *argv[])
273 {
274   grub_file_t file = 0;
275   grub_err_t err;
276
277   grub_loader_unset ();
278
279   highest_load = 0;
280
281 #ifndef GRUB_USE_MULTIBOOT2
282   grub_multiboot_quirks = GRUB_MULTIBOOT_QUIRKS_NONE;
283   int option_found = 0;
284
285   do
286     {
287       option_found = 0;
288       if (argc != 0 && grub_strcmp (argv[0], "--quirk-bad-kludge") == 0)
289         {
290           argc--;
291           argv++;
292           option_found = 1;
293           grub_multiboot_quirks |= GRUB_MULTIBOOT_QUIRK_BAD_KLUDGE;
294         }
295
296       if (argc != 0 && grub_strcmp (argv[0], "--quirk-modules-after-kernel") == 0)
297         {
298           argc--;
299           argv++;
300           option_found = 1;
301           grub_multiboot_quirks |= GRUB_MULTIBOOT_QUIRK_MODULES_AFTER_KERNEL;
302         }
303     } while (option_found);
304 #endif
305
306   if (argc == 0)
307     return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
308
309   file = grub_file_open (argv[0]);
310   if (! file)
311     return grub_errno;
312
313   grub_dl_ref (my_mod);
314
315   /* Skip filename.  */
316   grub_multiboot_init_mbi (argc - 1, argv + 1);
317
318   grub_relocator_unload (grub_multiboot_relocator);
319   grub_multiboot_relocator = grub_relocator_new ();
320
321   if (!grub_multiboot_relocator)
322     goto fail;
323
324   err = grub_multiboot_load (file, argv[0]);
325   if (err)
326     goto fail;
327
328   grub_multiboot_set_bootdev ();
329
330   grub_loader_set (grub_multiboot_boot, grub_multiboot_unload, 0);
331
332  fail:
333   if (file)
334     grub_file_close (file);
335
336   if (grub_errno != GRUB_ERR_NONE)
337     {
338       grub_relocator_unload (grub_multiboot_relocator);
339       grub_multiboot_relocator = NULL;
340       grub_dl_unref (my_mod);
341     }
342
343   return grub_errno;
344 }
345
346 static grub_err_t
347 grub_cmd_module (grub_command_t cmd __attribute__ ((unused)),
348                  int argc, char *argv[])
349 {
350   grub_file_t file = 0;
351   grub_ssize_t size;
352   void *module = NULL;
353   grub_addr_t target;
354   grub_err_t err;
355   int nounzip = 0;
356   grub_uint64_t lowest_addr = 0;
357
358   if (argc == 0)
359     return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
360
361   if (grub_strcmp (argv[0], "--nounzip") == 0)
362     {
363       argv++;
364       argc--;
365       nounzip = 1;
366     }
367
368   if (argc == 0)
369     return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
370
371   if (!grub_multiboot_relocator)
372     return grub_error (GRUB_ERR_BAD_ARGUMENT,
373                        N_("you need to load the kernel first"));
374
375   if (nounzip)
376     grub_file_filter_disable_compression ();
377
378   file = grub_file_open (argv[0]);
379   if (! file)
380     return grub_errno;
381
382 #ifndef GRUB_USE_MULTIBOOT2
383   lowest_addr = 0x100000;
384   if (grub_multiboot_quirks & GRUB_MULTIBOOT_QUIRK_MODULES_AFTER_KERNEL)
385     lowest_addr = ALIGN_UP (highest_load + 1048576, 4096);
386 #endif
387
388   size = grub_file_size (file);
389   if (size)
390   {
391     grub_relocator_chunk_t ch;
392     err = grub_relocator_alloc_chunk_align (grub_multiboot_relocator, &ch,
393                                             lowest_addr, (0xffffffff - size) + 1,
394                                             size, MULTIBOOT_MOD_ALIGN,
395                                             GRUB_RELOCATOR_PREFERENCE_NONE, 1);
396     if (err)
397       {
398         grub_file_close (file);
399         return err;
400       }
401     module = get_virtual_current_address (ch);
402     target = get_physical_target_address (ch);
403   }
404   else
405     {
406       module = 0;
407       target = 0;
408     }
409
410   err = grub_multiboot_add_module (target, size, argc - 1, argv + 1);
411   if (err)
412     {
413       grub_file_close (file);
414       return err;
415     }
416
417   if (size && grub_file_read (file, module, size) != size)
418     {
419       grub_file_close (file);
420       if (!grub_errno)
421         grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
422                     argv[0]);
423       return grub_errno;
424     }
425
426   grub_file_close (file);
427   return GRUB_ERR_NONE;
428 }
429
430 static grub_command_t cmd_multiboot, cmd_module;
431
432 GRUB_MOD_INIT(multiboot)
433 {
434   cmd_multiboot =
435 #ifdef GRUB_USE_MULTIBOOT2
436     grub_register_command ("multiboot2", grub_cmd_multiboot,
437                            0, N_("Load a multiboot 2 kernel."));
438   cmd_module =
439     grub_register_command ("module2", grub_cmd_module,
440                            0, N_("Load a multiboot 2 module."));
441 #else
442     grub_register_command ("multiboot", grub_cmd_multiboot,
443                            0, N_("Load a multiboot kernel."));
444   cmd_module =
445     grub_register_command ("module", grub_cmd_module,
446                            0, N_("Load a multiboot module."));
447 #endif
448
449   my_mod = mod;
450 }
451
452 GRUB_MOD_FINI(multiboot)
453 {
454   grub_unregister_command (cmd_multiboot);
455   grub_unregister_command (cmd_module);
456 }