]>
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> |
21e4a6fa VS |
31 | #ifdef GRUB_USE_MULTIBOOT2 |
32 | #include <grub/multiboot2.h> | |
33 | #define GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER GRUB_MULTIBOOT2_CONSOLE_FRAMEBUFFER | |
34 | #define GRUB_MULTIBOOT_CONSOLE_EGA_TEXT GRUB_MULTIBOOT2_CONSOLE_EGA_TEXT | |
35 | #define GRUB_MULTIBOOT(x) grub_multiboot2_ ## x | |
36 | #else | |
e5dfe777 | 37 | #include <grub/multiboot.h> |
21e4a6fa VS |
38 | #define GRUB_MULTIBOOT(x) grub_multiboot_ ## x |
39 | #endif | |
c04d6e05 | 40 | #include <grub/cpu/multiboot.h> |
4b13b216 | 41 | #include <grub/elf.h> |
d38e24c2 | 42 | #include <grub/aout.h> |
4b13b216 | 43 | #include <grub/file.h> |
44 | #include <grub/err.h> | |
4b13b216 | 45 | #include <grub/dl.h> |
46 | #include <grub/mm.h> | |
47 | #include <grub/misc.h> | |
38ad2cf5 | 48 | #include <grub/env.h> |
8c46a785 | 49 | #include <grub/cpu/relocator.h> |
0a46429a | 50 | #include <grub/video.h> |
5408044f | 51 | #include <grub/memory.h> |
b1f6f35a | 52 | #include <grub/i18n.h> |
4b13b216 | 53 | |
e745cf0c VS |
54 | GRUB_MOD_LICENSE ("GPLv3+"); |
55 | ||
a0b766fc VS |
56 | #ifdef GRUB_MACHINE_EFI |
57 | #include <grub/efi/efi.h> | |
58 | #endif | |
4b13b216 | 59 | |
21e4a6fa VS |
60 | struct grub_relocator *GRUB_MULTIBOOT (relocator) = NULL; |
61 | grub_uint32_t GRUB_MULTIBOOT (payload_eip); | |
7210dca9 | 62 | #if defined (GRUB_MACHINE_PCBIOS) || defined (GRUB_MACHINE_MULTIBOOT) || defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_QEMU) |
5408044f VS |
63 | #define DEFAULT_VIDEO_MODE "text" |
64 | #else | |
65 | #define DEFAULT_VIDEO_MODE "auto" | |
66 | #endif | |
67 | ||
5408044f | 68 | static int accepts_video; |
c3a8dfc8 VS |
69 | static int accepts_ega_text; |
70 | static int console_required; | |
b1f6f35a VS |
71 | static grub_dl_t my_mod; |
72 | ||
5408044f | 73 | |
d0d4b8a0 CW |
74 | /* Helper for grub_get_multiboot_mmap_count. */ |
75 | static int | |
76 | count_hook (grub_uint64_t addr __attribute__ ((unused)), | |
77 | grub_uint64_t size __attribute__ ((unused)), | |
78 | grub_memory_type_t type __attribute__ ((unused)), void *data) | |
79 | { | |
80 | grub_size_t *count = data; | |
81 | ||
82 | (*count)++; | |
83 | return 0; | |
84 | } | |
85 | ||
5408044f VS |
86 | /* Return the length of the Multiboot mmap that will be needed to allocate |
87 | our platform's map. */ | |
88 | grub_uint32_t | |
21e4a6fa | 89 | GRUB_MULTIBOOT (get_mmap_count) (void) |
5408044f VS |
90 | { |
91 | grub_size_t count = 0; | |
92 | ||
d0d4b8a0 | 93 | grub_mmap_iterate (count_hook, &count); |
5408044f | 94 | |
8eb567e6 | 95 | return count; |
5408044f VS |
96 | } |
97 | ||
5408044f | 98 | grub_err_t |
21e4a6fa | 99 | GRUB_MULTIBOOT (set_video_mode) (void) |
5408044f VS |
100 | { |
101 | grub_err_t err; | |
102 | const char *modevar; | |
103 | ||
a4ea2dff VS |
104 | #if GRUB_MACHINE_HAS_VGA_TEXT |
105 | if (accepts_video) | |
106 | #endif | |
5408044f VS |
107 | { |
108 | modevar = grub_env_get ("gfxpayload"); | |
109 | if (! modevar || *modevar == 0) | |
3f5a90c6 | 110 | err = grub_video_set_mode (DEFAULT_VIDEO_MODE, 0, 0); |
5408044f VS |
111 | else |
112 | { | |
113 | char *tmp; | |
3f5a90c6 | 114 | tmp = grub_xasprintf ("%s;" DEFAULT_VIDEO_MODE, modevar); |
5408044f VS |
115 | if (! tmp) |
116 | return grub_errno; | |
3f5a90c6 | 117 | err = grub_video_set_mode (tmp, 0, 0); |
5408044f VS |
118 | grub_free (tmp); |
119 | } | |
120 | } | |
a4ea2dff | 121 | #if GRUB_MACHINE_HAS_VGA_TEXT |
5408044f | 122 | else |
3f5a90c6 | 123 | err = grub_video_set_mode ("text", 0, 0); |
a4ea2dff | 124 | #endif |
5408044f VS |
125 | |
126 | return err; | |
127 | } | |
636813f7 | 128 | |
9862b241 DK |
129 | #ifdef GRUB_MACHINE_EFI |
130 | #ifdef __x86_64__ | |
131 | #define grub_relocator_efi_boot grub_relocator64_efi_boot | |
132 | #define grub_relocator_efi_state grub_relocator64_efi_state | |
133 | #endif | |
134 | #endif | |
135 | ||
136 | #ifdef grub_relocator_efi_boot | |
137 | static void | |
138 | efi_boot (struct grub_relocator *rel, | |
139 | grub_uint32_t target) | |
140 | { | |
4bfd2662 VS |
141 | #ifdef GRUB_USE_MULTIBOOT2 |
142 | struct grub_relocator_efi_state state_efi = MULTIBOOT2_EFI_INITIAL_STATE; | |
143 | #else | |
9862b241 | 144 | struct grub_relocator_efi_state state_efi = MULTIBOOT_EFI_INITIAL_STATE; |
4bfd2662 | 145 | #endif |
78d2b81b | 146 | state_efi.MULTIBOOT_EFI_ENTRY_REGISTER = GRUB_MULTIBOOT (payload_eip); |
9862b241 DK |
147 | state_efi.MULTIBOOT_EFI_MBI_REGISTER = target; |
148 | ||
149 | grub_relocator_efi_boot (rel, state_efi); | |
150 | } | |
151 | #else | |
152 | #define grub_efi_is_finished 1 | |
153 | static void | |
154 | efi_boot (struct grub_relocator *rel __attribute__ ((unused)), | |
155 | grub_uint32_t target __attribute__ ((unused))) | |
156 | { | |
157 | } | |
158 | #endif | |
159 | ||
160 | #if defined (__i386__) || defined (__x86_64__) | |
161 | static void | |
162 | normal_boot (struct grub_relocator *rel, struct grub_relocator32_state state) | |
163 | { | |
164 | grub_relocator32_boot (rel, state, 0); | |
165 | } | |
166 | #else | |
167 | static void | |
168 | normal_boot (struct grub_relocator *rel, struct grub_relocator32_state state) | |
169 | { | |
170 | grub_relocator32_boot (rel, state); | |
171 | } | |
172 | #endif | |
173 | ||
4b13b216 | 174 | static grub_err_t |
175 | grub_multiboot_boot (void) | |
9a5c1ade | 176 | { |
cd051479 | 177 | grub_err_t err; |
8c46a785 | 178 | |
21e4a6fa VS |
179 | #ifdef GRUB_USE_MULTIBOOT2 |
180 | struct grub_relocator32_state state = MULTIBOOT2_INITIAL_STATE; | |
181 | #else | |
182 | struct grub_relocator32_state state = MULTIBOOT_INITIAL_STATE; | |
183 | #endif | |
184 | state.MULTIBOOT_ENTRY_REGISTER = GRUB_MULTIBOOT (payload_eip); | |
636813f7 | 185 | |
21e4a6fa | 186 | err = GRUB_MULTIBOOT (make_mbi) (&state.MULTIBOOT_MBI_REGISTER); |
cd051479 | 187 | |
cd051479 VS |
188 | if (err) |
189 | return err; | |
190 | ||
9862b241 | 191 | if (grub_efi_is_finished) |
21e4a6fa | 192 | normal_boot (GRUB_MULTIBOOT (relocator), state); |
9862b241 | 193 | else |
21e4a6fa | 194 | efi_boot (GRUB_MULTIBOOT (relocator), state.MULTIBOOT_MBI_REGISTER); |
9a5c1ade | 195 | |
196 | /* Not reached. */ | |
4b13b216 | 197 | return GRUB_ERR_NONE; |
9a5c1ade | 198 | } |
199 | ||
4b13b216 | 200 | static grub_err_t |
201 | grub_multiboot_unload (void) | |
9a5c1ade | 202 | { |
21e4a6fa | 203 | GRUB_MULTIBOOT (free_mbi) (); |
cd051479 | 204 | |
21e4a6fa VS |
205 | grub_relocator_unload (GRUB_MULTIBOOT (relocator)); |
206 | GRUB_MULTIBOOT (relocator) = NULL; | |
b39f9d20 | 207 | |
4b13b216 | 208 | grub_dl_unref (my_mod); |
9a5c1ade | 209 | |
4b13b216 | 210 | return GRUB_ERR_NONE; |
9a5c1ade | 211 | } |
212 | ||
00bfa988 VS |
213 | static grub_uint64_t highest_load; |
214 | ||
8a31787f | 215 | #define MULTIBOOT_LOAD_ELF64 |
216 | #include "multiboot_elfxx.c" | |
217 | #undef MULTIBOOT_LOAD_ELF64 | |
ea409713 | 218 | |
8a31787f | 219 | #define MULTIBOOT_LOAD_ELF32 |
220 | #include "multiboot_elfxx.c" | |
221 | #undef MULTIBOOT_LOAD_ELF32 | |
ea409713 | 222 | |
223 | /* Load ELF32 or ELF64. */ | |
b1f6f35a | 224 | grub_err_t |
21e4a6fa | 225 | GRUB_MULTIBOOT (load_elf) (mbi_load_data_t *mld) |
ea409713 | 226 | { |
a620876e DK |
227 | if (grub_multiboot_is_elf32 (mld->buffer)) |
228 | return grub_multiboot_load_elf32 (mld); | |
229 | else if (grub_multiboot_is_elf64 (mld->buffer)) | |
230 | return grub_multiboot_load_elf64 (mld); | |
d38e24c2 | 231 | |
67093bc0 | 232 | return grub_error (GRUB_ERR_UNKNOWN_OS, N_("invalid arch-dependent ELF magic")); |
ea409713 | 233 | } |
234 | ||
b1f6f35a | 235 | grub_err_t |
21e4a6fa VS |
236 | GRUB_MULTIBOOT (set_console) (int console_type, int accepted_consoles, |
237 | int width, int height, int depth, | |
238 | int console_req) | |
9a5c1ade | 239 | { |
c3a8dfc8 VS |
240 | console_required = console_req; |
241 | if (!(accepted_consoles | |
242 | & (GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER | |
243 | | (GRUB_MACHINE_HAS_VGA_TEXT ? GRUB_MULTIBOOT_CONSOLE_EGA_TEXT : 0)))) | |
9a5c1ade | 244 | { |
c3a8dfc8 VS |
245 | if (console_required) |
246 | return grub_error (GRUB_ERR_BAD_OS, | |
247 | "OS requires a console but none is available"); | |
6e0632e2 | 248 | grub_puts_ (N_("WARNING: no console will be available to OS")); |
c3a8dfc8 VS |
249 | accepts_video = 0; |
250 | accepts_ega_text = 0; | |
251 | return GRUB_ERR_NONE; | |
9a5c1ade | 252 | } |
253 | ||
b1f6f35a | 254 | if (console_type == GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER) |
9a5c1ade | 255 | { |
b1f6f35a VS |
256 | char *buf; |
257 | if (depth && width && height) | |
258 | buf = grub_xasprintf ("%dx%dx%d,%dx%d,auto", width, | |
259 | height, depth, width, height); | |
260 | else if (width && height) | |
261 | buf = grub_xasprintf ("%dx%d,auto", width, height); | |
262 | else | |
263 | buf = grub_strdup ("auto"); | |
9a5c1ade | 264 | |
b1f6f35a VS |
265 | if (!buf) |
266 | return grub_errno; | |
267 | grub_env_set ("gfxpayload", buf); | |
268 | grub_free (buf); | |
9a5c1ade | 269 | } |
71764dc8 | 270 | else |
271 | { | |
272 | #if GRUB_MACHINE_HAS_VGA_TEXT | |
273 | grub_env_set ("gfxpayload", "text"); | |
274 | #else | |
275 | /* Always use video if no VGA text is available. */ | |
276 | grub_env_set ("gfxpayload", "auto"); | |
277 | #endif | |
278 | } | |
9a5c1ade | 279 | |
b1f6f35a | 280 | accepts_video = !!(accepted_consoles & GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER); |
c3a8dfc8 | 281 | accepts_ega_text = !!(accepted_consoles & GRUB_MULTIBOOT_CONSOLE_EGA_TEXT); |
b1f6f35a VS |
282 | return GRUB_ERR_NONE; |
283 | } | |
d38e24c2 | 284 | |
b1f6f35a VS |
285 | static grub_err_t |
286 | grub_cmd_multiboot (grub_command_t cmd __attribute__ ((unused)), | |
287 | int argc, char *argv[]) | |
9a5c1ade | 288 | { |
4b13b216 | 289 | grub_file_t file = 0; |
b1f6f35a | 290 | grub_err_t err; |
9a5c1ade | 291 | |
ea409713 | 292 | grub_loader_unset (); |
c642636f | 293 | |
00bfa988 VS |
294 | highest_load = 0; |
295 | ||
296 | #ifndef GRUB_USE_MULTIBOOT2 | |
297 | grub_multiboot_quirks = GRUB_MULTIBOOT_QUIRKS_NONE; | |
e0bd66c3 | 298 | int option_found = 0; |
00bfa988 | 299 | |
e0bd66c3 | 300 | do |
00bfa988 | 301 | { |
e0bd66c3 VS |
302 | option_found = 0; |
303 | if (argc != 0 && grub_strcmp (argv[0], "--quirk-bad-kludge") == 0) | |
304 | { | |
305 | argc--; | |
306 | argv++; | |
307 | option_found = 1; | |
308 | grub_multiboot_quirks |= GRUB_MULTIBOOT_QUIRK_BAD_KLUDGE; | |
309 | } | |
00bfa988 | 310 | |
e0bd66c3 VS |
311 | if (argc != 0 && grub_strcmp (argv[0], "--quirk-modules-after-kernel") == 0) |
312 | { | |
313 | argc--; | |
314 | argv++; | |
315 | option_found = 1; | |
316 | grub_multiboot_quirks |= GRUB_MULTIBOOT_QUIRK_MODULES_AFTER_KERNEL; | |
317 | } | |
318 | } while (option_found); | |
00bfa988 VS |
319 | #endif |
320 | ||
9a5c1ade | 321 | if (argc == 0) |
9c4b5c13 | 322 | return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); |
9a5c1ade | 323 | |
ca0a4f68 | 324 | file = grub_file_open (argv[0], GRUB_FILE_TYPE_MULTIBOOT_KERNEL); |
ea409713 | 325 | if (! file) |
7a45a539 | 326 | return grub_errno; |
9a5c1ade | 327 | |
b1f6f35a | 328 | grub_dl_ref (my_mod); |
371458b5 | 329 | |
e1f39873 | 330 | /* Skip filename. */ |
21e4a6fa | 331 | GRUB_MULTIBOOT (init_mbi) (argc - 1, argv + 1); |
deceb3ec | 332 | |
21e4a6fa VS |
333 | grub_relocator_unload (GRUB_MULTIBOOT (relocator)); |
334 | GRUB_MULTIBOOT (relocator) = grub_relocator_new (); | |
14e43c6e | 335 | |
21e4a6fa | 336 | if (!GRUB_MULTIBOOT (relocator)) |
14e43c6e VS |
337 | goto fail; |
338 | ||
21e4a6fa | 339 | err = GRUB_MULTIBOOT (load) (file, argv[0]); |
b1f6f35a | 340 | if (err) |
ea409713 | 341 | goto fail; |
d38e24c2 | 342 | |
21e4a6fa | 343 | GRUB_MULTIBOOT (set_bootdev) (); |
38ad2cf5 | 344 | |
14e43c6e | 345 | grub_loader_set (grub_multiboot_boot, grub_multiboot_unload, 0); |
9a5c1ade | 346 | |
347 | fail: | |
348 | if (file) | |
4b13b216 | 349 | grub_file_close (file); |
9a5c1ade | 350 | |
4b13b216 | 351 | if (grub_errno != GRUB_ERR_NONE) |
9a5c1ade | 352 | { |
21e4a6fa VS |
353 | grub_relocator_unload (GRUB_MULTIBOOT (relocator)); |
354 | GRUB_MULTIBOOT (relocator) = NULL; | |
4b13b216 | 355 | grub_dl_unref (my_mod); |
9a5c1ade | 356 | } |
b1f6f35a VS |
357 | |
358 | return grub_errno; | |
9a5c1ade | 359 | } |
360 | ||
b1f6f35a VS |
361 | static grub_err_t |
362 | grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), | |
363 | int argc, char *argv[]) | |
9a5c1ade | 364 | { |
4b13b216 | 365 | grub_file_t file = 0; |
cd051479 | 366 | grub_ssize_t size; |
5c8e58b0 VS |
367 | void *module = NULL; |
368 | grub_addr_t target; | |
cd051479 | 369 | grub_err_t err; |
dee50575 | 370 | int nounzip = 0; |
00bfa988 | 371 | grub_uint64_t lowest_addr = 0; |
dee50575 VS |
372 | |
373 | if (argc == 0) | |
9c4b5c13 | 374 | return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); |
dee50575 VS |
375 | |
376 | if (grub_strcmp (argv[0], "--nounzip") == 0) | |
377 | { | |
378 | argv++; | |
379 | argc--; | |
380 | nounzip = 1; | |
381 | } | |
9a5c1ade | 382 | |
383 | if (argc == 0) | |
9c4b5c13 | 384 | return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); |
9a5c1ade | 385 | |
21e4a6fa | 386 | if (!GRUB_MULTIBOOT (relocator)) |
b1f6f35a | 387 | return grub_error (GRUB_ERR_BAD_ARGUMENT, |
9c4b5c13 | 388 | N_("you need to load the kernel first")); |
9a5c1ade | 389 | |
ca0a4f68 VS |
390 | file = grub_file_open (argv[0], GRUB_FILE_TYPE_MULTIBOOT_MODULE |
391 | | (nounzip ? GRUB_FILE_TYPE_NO_DECOMPRESS : GRUB_FILE_TYPE_NONE)); | |
ea409713 | 392 | if (! file) |
b1f6f35a | 393 | return grub_errno; |
9a5c1ade | 394 | |
00bfa988 | 395 | #ifndef GRUB_USE_MULTIBOOT2 |
c856be6b | 396 | lowest_addr = 0x100000; |
00bfa988 VS |
397 | if (grub_multiboot_quirks & GRUB_MULTIBOOT_QUIRK_MODULES_AFTER_KERNEL) |
398 | lowest_addr = ALIGN_UP (highest_load + 1048576, 4096); | |
399 | #endif | |
400 | ||
4b13b216 | 401 | size = grub_file_size (file); |
cb3c4a47 | 402 | if (size) |
4b2ec20b VS |
403 | { |
404 | grub_relocator_chunk_t ch; | |
21e4a6fa | 405 | err = grub_relocator_alloc_chunk_align (GRUB_MULTIBOOT (relocator), &ch, |
e39786ab | 406 | lowest_addr, UP_TO_TOP32 (size), |
4b2ec20b | 407 | size, MULTIBOOT_MOD_ALIGN, |
0df77d79 | 408 | GRUB_RELOCATOR_PREFERENCE_NONE, 1); |
4b2ec20b VS |
409 | if (err) |
410 | { | |
411 | grub_file_close (file); | |
412 | return err; | |
413 | } | |
414 | module = get_virtual_current_address (ch); | |
813c0a2b | 415 | target = get_physical_target_address (ch); |
4b2ec20b | 416 | } |
cb3c4a47 VS |
417 | else |
418 | { | |
419 | module = 0; | |
420 | target = 0; | |
421 | } | |
9a5c1ade | 422 | |
21e4a6fa | 423 | err = GRUB_MULTIBOOT (add_module) (target, size, argc - 1, argv + 1); |
cd051479 | 424 | if (err) |
b1f6f35a VS |
425 | { |
426 | grub_file_close (file); | |
427 | return err; | |
428 | } | |
9a5c1ade | 429 | |
cb3c4a47 | 430 | if (size && grub_file_read (file, module, size) != size) |
9a5c1ade | 431 | { |
b1f6f35a | 432 | grub_file_close (file); |
7a45a539 VS |
433 | if (!grub_errno) |
434 | grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), | |
435 | argv[0]); | |
436 | return grub_errno; | |
9a5c1ade | 437 | } |
d38e24c2 | 438 | |
b1f6f35a | 439 | grub_file_close (file); |
cb3c4a47 | 440 | return GRUB_ERR_NONE; |
9a5c1ade | 441 | } |
cd051479 | 442 | |
b1f6f35a VS |
443 | static grub_command_t cmd_multiboot, cmd_module; |
444 | ||
445 | GRUB_MOD_INIT(multiboot) | |
446 | { | |
447 | cmd_multiboot = | |
448 | #ifdef GRUB_USE_MULTIBOOT2 | |
449 | grub_register_command ("multiboot2", grub_cmd_multiboot, | |
450 | 0, N_("Load a multiboot 2 kernel.")); | |
daea6abd VS |
451 | cmd_module = |
452 | grub_register_command ("module2", grub_cmd_module, | |
453 | 0, N_("Load a multiboot 2 module.")); | |
b1f6f35a VS |
454 | #else |
455 | grub_register_command ("multiboot", grub_cmd_multiboot, | |
456 | 0, N_("Load a multiboot kernel.")); | |
b1f6f35a VS |
457 | cmd_module = |
458 | grub_register_command ("module", grub_cmd_module, | |
459 | 0, N_("Load a multiboot module.")); | |
daea6abd | 460 | #endif |
b1f6f35a VS |
461 | |
462 | my_mod = mod; | |
9a5c1ade | 463 | } |
cd051479 | 464 | |
b1f6f35a VS |
465 | GRUB_MOD_FINI(multiboot) |
466 | { | |
467 | grub_unregister_command (cmd_multiboot); | |
468 | grub_unregister_command (cmd_module); | |
469 | } |