]>
Commit | Line | Data |
---|---|---|
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 |
46 | GRUB_MOD_LICENSE ("GPLv3+"); |
47 | ||
a0b766fc VS |
48 | #ifdef GRUB_MACHINE_EFI |
49 | #include <grub/efi/efi.h> | |
50 | #endif | |
4b13b216 | 51 | |
14e43c6e | 52 | struct grub_relocator *grub_multiboot_relocator = NULL; |
8b0800f6 | 53 | grub_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 | 60 | static int accepts_video; |
c3a8dfc8 VS |
61 | static int accepts_ega_text; |
62 | static int console_required; | |
b1f6f35a VS |
63 | static grub_dl_t my_mod; |
64 | ||
5408044f | 65 | |
d0d4b8a0 CW |
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 | ||
5408044f VS |
78 | /* Return the length of the Multiboot mmap that will be needed to allocate |
79 | our platform's map. */ | |
80 | grub_uint32_t | |
8eb567e6 | 81 | grub_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 |
90 | grub_err_t |
91 | grub_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 | 121 | static grub_err_t |
122 | grub_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 | 144 | static grub_err_t |
145 | grub_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 |
157 | static 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 | 168 | grub_err_t |
9c4b5c13 VS |
169 | grub_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 |
180 | grub_err_t |
181 | grub_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 |
230 | static grub_err_t |
231 | grub_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 |
299 | static grub_err_t |
300 | grub_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 |
382 | static grub_command_t cmd_multiboot, cmd_module; |
383 | ||
384 | GRUB_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 |
404 | GRUB_MOD_FINI(multiboot) |
405 | { | |
406 | grub_unregister_command (cmd_multiboot); | |
407 | grub_unregister_command (cmd_module); | |
408 | } |