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