1 /* multiboot2.c - boot a multiboot 2 OS image. */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2007 Free Software Foundation, Inc.
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.
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.
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/>.
20 #include <multiboot2.h>
21 #include <grub/loader.h>
22 #include <grub/machine/loader.h>
23 #include <grub/multiboot2.h>
24 #include <grub/elfload.h>
25 #include <grub/file.h>
29 #include <grub/misc.h>
30 #include <grub/gzio.h>
32 static grub_addr_t entry
;
33 extern grub_dl_t my_mod
;
35 static char *grub_mb2_tags
;
36 static char *grub_mb2_tags_pos
;
37 static grub_size_t grub_mb2_tags_len
;
38 static int grub_mb2_tags_count
;
41 grub_mb2_tags_free (void)
43 grub_dprintf ("loader", "Freeing all tags...\n");
44 grub_free (grub_mb2_tags
);
46 grub_mb2_tags_pos
= 0;
47 grub_mb2_tags_len
= 0;
48 grub_mb2_tags_count
= 0;
52 grub_mb2_tag_alloc (grub_addr_t
*addr
, int key
, grub_size_t len
)
54 struct multiboot_tag_header
*tag
;
58 grub_dprintf ("loader", "Allocating tag: key 0x%x, size 0x%lx.\n",
59 key
, (unsigned long) len
);
61 used
= grub_mb2_tags_pos
- grub_mb2_tags
;
62 len
= ALIGN_UP (len
, sizeof (multiboot_word
));
66 if (needed
> grub_mb2_tags_len
)
68 /* Allocate new buffer. */
69 grub_size_t newsize
= needed
* 2;
72 grub_dprintf ("loader", "Reallocating tag buffer (new size 0x%lx).\n",
73 (unsigned long) newsize
);
75 newarea
= grub_malloc (newsize
);
78 grub_memcpy (newarea
, grub_mb2_tags
, grub_mb2_tags_len
);
79 grub_free (grub_mb2_tags
);
81 grub_mb2_tags_len
= newsize
;
82 grub_mb2_tags
= newarea
;
83 grub_mb2_tags_pos
= newarea
+ used
;
86 tag
= (struct multiboot_tag_header
*) grub_mb2_tags_pos
;
87 grub_mb2_tags_pos
+= len
;
93 *addr
= (grub_addr_t
) tag
;
95 grub_mb2_tags_count
++;
97 grub_dprintf ("loader", "Allocated tag %u at %p.\n", grub_mb2_tags_count
, tag
);
103 grub_mb2_tag_start_create (void)
105 return grub_mb2_tag_alloc (0, MULTIBOOT2_TAG_START
,
106 sizeof (struct multiboot_tag_start
));
110 grub_mb2_tag_name_create (void)
112 struct multiboot_tag_name
*name
;
113 grub_addr_t name_addr
;
115 const char *grub_version
= PACKAGE_STRING
;
117 err
= grub_mb2_tag_alloc (&name_addr
, MULTIBOOT2_TAG_NAME
,
118 sizeof (struct multiboot_tag_name
) +
119 sizeof (grub_version
) + 1);
123 name
= (struct multiboot_tag_name
*) name_addr
;
124 grub_strcpy (name
->name
, grub_version
);
126 return GRUB_ERR_NONE
;
129 typedef grub_err_t (*tag_create_t
) (void);
130 static tag_create_t grub_mb2_tag_creators
[] = {
131 grub_mb2_tag_start_create
,
132 grub_mb2_tag_name_create
,
133 grub_mb2_tags_arch_create
,
138 grub_mb2_tags_create (void)
140 tag_create_t
*creator
;
143 for (creator
= grub_mb2_tag_creators
; *creator
!= 0; creator
++)
150 return GRUB_ERR_NONE
;
154 grub_mb2_tags_free ();
160 grub_mb2_tags_finish (void)
162 struct multiboot_tag_start
*start
;
165 /* Create the `end' tag. */
166 err
= grub_mb2_tag_alloc (0, MULTIBOOT2_TAG_END
,
167 sizeof (struct multiboot_tag_end
));
171 /* We created the `start' tag first. Update it now. */
172 start
= (struct multiboot_tag_start
*) grub_mb2_tags
;
173 start
->size
= grub_mb2_tags_pos
- grub_mb2_tags
;
174 return GRUB_ERR_NONE
;
178 grub_mb2_tags_free ();
186 grub_mb2_tags_finish ();
188 grub_dprintf ("loader", "Tags at %p\n", grub_mb2_tags
);
189 grub_mb2_arch_boot (entry
, grub_mb2_tags
);
192 return GRUB_ERR_NONE
;
196 grub_mb2_unload (void)
198 struct multiboot_tag_header
*tag
;
199 struct multiboot_tag_header
*tags
=
200 (struct multiboot_tag_header
*) grub_mb2_tags
;
202 /* Free all module memory in the tag list. */
203 for_each_tag (tag
, tags
)
205 if (tag
->key
== MULTIBOOT2_TAG_MODULE
)
207 struct multiboot_tag_module
*module
=
208 (struct multiboot_tag_module
*) tag
;
209 grub_free ((void *) module
->addr
);
213 /* Allow architecture to un-reserve memory. */
214 grub_mb2_arch_unload (tags
);
216 /* Free the tags themselves. */
217 grub_mb2_tags_free ();
219 grub_dl_unref (my_mod
);
221 return GRUB_ERR_NONE
;
225 grub_mb2_load_other (UNUSED grub_file_t file
, UNUSED
void *buffer
)
227 /* XXX Create module tag here. */
228 return grub_error (GRUB_ERR_UNKNOWN_OS
, "currently only ELF is supported");
231 /* Create the tag containing the cmdline and the address of the module data. */
233 grub_mb2_tag_module_create (grub_addr_t modaddr
, grub_size_t modsize
,
234 char *type
, int key
, int argc
, char *argv
[])
236 struct multiboot_tag_module
*module
;
237 grub_ssize_t argslen
= 0;
240 grub_addr_t module_addr
;
243 /* Allocate enough space for the arguments and spaces between them. */
244 for (i
= 0; i
< argc
; i
++)
245 argslen
+= grub_strlen (argv
[i
]) + 1;
247 /* Note: includes implicit 1-byte cmdline. */
248 err
= grub_mb2_tag_alloc (&module_addr
, key
,
249 sizeof (struct multiboot_tag_module
) + argslen
);
253 module
= (struct multiboot_tag_module
*) module_addr
;
254 module
->addr
= modaddr
;
255 module
->size
= modsize
;
256 grub_strcpy(module
->type
, type
);
258 /* Fill in the command line. */
260 for (i
= 0; i
< argc
; i
++)
262 p
= grub_stpcpy (p
, argv
[i
]);
265 module
->cmdline
[argslen
] = '\0';
267 return GRUB_ERR_NONE
;
270 /* Load ELF32 or ELF64. */
272 grub_mb2_load_elf (grub_elf_t elf
, int argc
, char *argv
[])
274 grub_addr_t kern_base
;
275 grub_size_t kern_size
;
278 if (grub_elf_is_elf32 (elf
))
280 entry
= elf
->ehdr
.ehdr32
.e_entry
;
281 err
= grub_elf32_load (elf
, grub_mb2_arch_elf32_hook
, &kern_base
,
284 else if (grub_elf_is_elf64 (elf
))
286 entry
= elf
->ehdr
.ehdr64
.e_entry
;
287 err
= grub_elf64_load (elf
, grub_mb2_arch_elf64_hook
, &kern_base
,
291 err
= grub_error (GRUB_ERR_UNKNOWN_OS
, "unknown ELF class");
296 grub_dprintf ("loader", "Entry point is 0x%lx.\n", (unsigned long) entry
);
298 grub_mb2_tag_module_create (kern_base
, kern_size
, "kernel",
299 MULTIBOOT2_TAG_MODULE
, argc
, argv
);
306 grub_multiboot2 (int argc
, char *argv
[])
309 grub_file_t file
= 0;
311 struct multiboot_header
*header
= 0;
315 int header_found
= 0;
317 grub_loader_unset ();
321 grub_error (GRUB_ERR_BAD_ARGUMENT
, "No kernel specified");
325 file
= grub_gzfile_open (argv
[0], 1);
328 grub_error (GRUB_ERR_BAD_ARGUMENT
, "Couldn't open file");
332 buffer
= grub_malloc (MULTIBOOT2_HEADER_SEARCH
);
336 len
= grub_file_read (file
, buffer
, MULTIBOOT2_HEADER_SEARCH
);
339 grub_error (GRUB_ERR_BAD_OS
, "File too small");
343 /* Look for the multiboot header in the buffer. The header should
344 be at least 8 bytes and aligned on a 8-byte boundary. */
345 for (p
= buffer
; p
<= buffer
+ len
- 8; p
+= 8)
347 header
= (struct multiboot_header
*) p
;
348 if (header
->magic
== MULTIBOOT2_HEADER_MAGIC
)
356 grub_dprintf ("loader", "No multiboot 2 header found.\n");
359 /* Create the basic tags. */
360 grub_dprintf ("loader", "Creating multiboot 2 tags\n");
361 grub_mb2_tags_create ();
363 /* Load the kernel and create its tag. */
364 elf
= grub_elf_file (file
);
367 grub_dprintf ("loader", "Loading ELF multiboot 2 file.\n");
368 err
= grub_mb2_load_elf (elf
, argc
-1, &argv
[1]);
369 grub_elf_close (elf
);
374 grub_dprintf ("loader", "Loading non-ELF multiboot 2 file.\n");
377 err
= grub_mb2_load_other (file
, header
);
379 err
= grub_error (GRUB_ERR_BAD_OS
,
380 "Need multiboot 2 header to load non-ELF files.");
381 grub_file_close (file
);
390 grub_loader_set (grub_mb2_boot
, grub_mb2_unload
, 1);
394 grub_mb2_tags_free ();
395 grub_dl_unref (my_mod
);
399 grub_module2 (int argc
, char *argv
[])
402 grub_addr_t modaddr
= 0;
403 grub_ssize_t modsize
= 0;
408 grub_error (GRUB_ERR_BAD_ARGUMENT
, "No module specified");
414 grub_error (GRUB_ERR_BAD_ARGUMENT
, "No module type specified");
420 grub_error (GRUB_ERR_BAD_ARGUMENT
,
421 "You need to load the multiboot kernel first");
425 /* Load module data. */
426 file
= grub_gzfile_open (argv
[0], 1);
430 modsize
= grub_file_size (file
);
431 err
= grub_mb2_arch_module_alloc (modsize
, &modaddr
);
435 grub_dprintf ("loader", "Loading module at 0x%x - 0x%x\n", modaddr
,
437 if (grub_file_read (file
, (char *) modaddr
, modsize
) != modsize
)
439 grub_error (GRUB_ERR_FILE_READ_ERROR
, "Couldn't read file");
443 /* Create the module tag. */
444 err
= grub_mb2_tag_module_create (modaddr
, modsize
,
445 argv
[1], MULTIBOOT2_TAG_MODULE
,
454 grub_file_close (file
);
457 grub_mb2_arch_module_free (modaddr
, modsize
);