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>
27 #include <grub/rescue.h>
30 #include <grub/misc.h>
31 #include <grub/gzio.h>
33 static grub_addr_t entry
;
34 extern grub_dl_t my_mod
;
36 static char *grub_mb2_tags
;
37 static char *grub_mb2_tags_pos
;
38 static grub_size_t grub_mb2_tags_len
;
39 static int grub_mb2_tags_count
;
42 grub_mb2_tags_free (void)
44 grub_dprintf ("loader", "Freeing all tags...\n");
45 grub_free (grub_mb2_tags
);
47 grub_mb2_tags_pos
= 0;
48 grub_mb2_tags_len
= 0;
49 grub_mb2_tags_count
= 0;
53 grub_mb2_tag_alloc (grub_addr_t
*addr
, int key
, grub_size_t len
)
55 struct multiboot_tag_header
*tag
;
59 grub_dprintf ("loader", "Allocating tag: key 0x%x, size 0x%lx.\n",
60 key
, (unsigned long) len
);
62 used
= grub_mb2_tags_pos
- grub_mb2_tags
;
63 len
= ALIGN_UP (len
, sizeof (multiboot_word
));
67 if (needed
> grub_mb2_tags_len
)
69 /* Allocate new buffer. */
70 grub_size_t newsize
= needed
* 2;
73 grub_dprintf ("loader", "Reallocating tag buffer (new size 0x%lx).\n",
74 (unsigned long) newsize
);
76 newarea
= grub_malloc (newsize
);
79 grub_memcpy (newarea
, grub_mb2_tags
, grub_mb2_tags_len
);
80 grub_free (grub_mb2_tags
);
82 grub_mb2_tags_len
= newsize
;
83 grub_mb2_tags
= newarea
;
84 grub_mb2_tags_pos
= newarea
+ used
;
87 tag
= (struct multiboot_tag_header
*) grub_mb2_tags_pos
;
88 grub_mb2_tags_pos
+= len
;
94 *addr
= (grub_addr_t
) tag
;
96 grub_mb2_tags_count
++;
98 grub_dprintf ("loader", "Allocated tag %u at %p.\n", grub_mb2_tags_count
, tag
);
104 grub_mb2_tag_start_create (void)
106 return grub_mb2_tag_alloc (0, MULTIBOOT2_TAG_START
,
107 sizeof (struct multiboot_tag_start
));
111 grub_mb2_tag_name_create (void)
113 struct multiboot_tag_name
*name
;
114 grub_addr_t name_addr
;
116 const char *grub_version
= PACKAGE_STRING
;
118 err
= grub_mb2_tag_alloc (&name_addr
, MULTIBOOT2_TAG_NAME
,
119 sizeof (struct multiboot_tag_name
) +
120 sizeof (grub_version
) + 1);
124 name
= (struct multiboot_tag_name
*) name_addr
;
125 grub_strcpy (name
->name
, grub_version
);
127 return GRUB_ERR_NONE
;
130 typedef grub_err_t (*tag_create_t
) (void);
131 static tag_create_t grub_mb2_tag_creators
[] = {
132 grub_mb2_tag_start_create
,
133 grub_mb2_tag_name_create
,
134 grub_mb2_tags_arch_create
,
139 grub_mb2_tags_create (void)
141 tag_create_t
*creator
;
144 for (creator
= grub_mb2_tag_creators
; *creator
!= 0; creator
++)
151 return GRUB_ERR_NONE
;
155 grub_mb2_tags_free ();
161 grub_mb2_tags_finish (void)
163 struct multiboot_tag_start
*start
;
166 /* Create the `end' tag. */
167 err
= grub_mb2_tag_alloc (0, MULTIBOOT2_TAG_END
,
168 sizeof (struct multiboot_tag_end
));
172 /* We created the `start' tag first. Update it now. */
173 start
= (struct multiboot_tag_start
*) grub_mb2_tags
;
174 start
->size
= grub_mb2_tags_pos
- grub_mb2_tags
;
175 return GRUB_ERR_NONE
;
179 grub_mb2_tags_free ();
187 grub_mb2_tags_finish ();
189 grub_dprintf ("loader", "Tags at %p\n", grub_mb2_tags
);
190 grub_mb2_arch_boot (entry
, grub_mb2_tags
);
193 return GRUB_ERR_NONE
;
197 grub_mb2_unload (void)
199 struct multiboot_tag_header
*tag
;
200 struct multiboot_tag_header
*tags
=
201 (struct multiboot_tag_header
*) grub_mb2_tags
;
203 /* Free all module memory in the tag list. */
204 for_each_tag (tag
, tags
)
206 if (tag
->key
== MULTIBOOT2_TAG_MODULE
)
208 struct multiboot_tag_module
*module
=
209 (struct multiboot_tag_module
*) tag
;
210 grub_free ((void *) module
->addr
);
214 /* Allow architecture to un-reserve memory. */
215 grub_mb2_arch_unload (tags
);
217 /* Free the tags themselves. */
218 grub_mb2_tags_free ();
220 grub_dl_unref (my_mod
);
222 return GRUB_ERR_NONE
;
226 grub_mb2_load_other (UNUSED grub_file_t file
, UNUSED
void *buffer
)
228 /* XXX Create module tag here. */
229 return grub_error (GRUB_ERR_UNKNOWN_OS
, "currently only ELF is supported");
232 /* Create the tag containing the cmdline and the address of the module data. */
234 grub_mb2_tag_module_create (grub_addr_t modaddr
, grub_size_t modsize
,
235 char *type
, int key
, int argc
, char *argv
[])
237 struct multiboot_tag_module
*module
;
238 grub_ssize_t argslen
= 0;
241 grub_addr_t module_addr
;
244 /* Allocate enough space for the arguments and spaces between them. */
245 for (i
= 0; i
< argc
; i
++)
246 argslen
+= grub_strlen (argv
[i
]) + 1;
248 /* Note: includes implicit 1-byte cmdline. */
249 err
= grub_mb2_tag_alloc (&module_addr
, key
,
250 sizeof (struct multiboot_tag_module
) + argslen
);
254 module
= (struct multiboot_tag_module
*) module_addr
;
255 module
->addr
= modaddr
;
256 module
->size
= modsize
;
257 grub_strcpy(module
->type
, type
);
259 /* Fill in the command line. */
261 for (i
= 0; i
< argc
; i
++)
263 p
= grub_stpcpy (p
, argv
[i
]);
266 module
->cmdline
[argslen
] = '\0';
268 return GRUB_ERR_NONE
;
271 /* Load ELF32 or ELF64. */
273 grub_mb2_load_elf (grub_elf_t elf
, int argc
, char *argv
[])
275 grub_addr_t kern_base
;
276 grub_size_t kern_size
;
279 if (grub_elf_is_elf32 (elf
))
281 entry
= elf
->ehdr
.ehdr32
.e_entry
;
282 err
= grub_elf32_load (elf
, grub_mb2_arch_elf32_hook
, &kern_base
,
285 else if (grub_elf_is_elf64 (elf
))
287 entry
= elf
->ehdr
.ehdr64
.e_entry
;
288 err
= grub_elf64_load (elf
, grub_mb2_arch_elf64_hook
, &kern_base
,
292 err
= grub_error (GRUB_ERR_UNKNOWN_OS
, "unknown ELF class");
297 grub_dprintf ("loader", "Entry point is 0x%lx.\n", (unsigned long) entry
);
299 grub_mb2_tag_module_create (kern_base
, kern_size
, "kernel",
300 MULTIBOOT2_TAG_MODULE
, argc
, argv
);
307 grub_multiboot2 (int argc
, char *argv
[])
310 grub_file_t file
= 0;
312 struct multiboot_header
*header
= 0;
316 int header_found
= 0;
318 grub_loader_unset ();
322 grub_error (GRUB_ERR_BAD_ARGUMENT
, "No kernel specified");
326 file
= grub_gzfile_open (argv
[0], 1);
329 grub_error (GRUB_ERR_BAD_ARGUMENT
, "Couldn't open file");
333 buffer
= grub_malloc (MULTIBOOT2_HEADER_SEARCH
);
337 len
= grub_file_read (file
, buffer
, MULTIBOOT2_HEADER_SEARCH
);
340 grub_error (GRUB_ERR_BAD_OS
, "File too small");
344 /* Look for the multiboot header in the buffer. The header should
345 be at least 8 bytes and aligned on a 8-byte boundary. */
346 for (p
= buffer
; p
<= buffer
+ len
- 8; p
+= 8)
348 header
= (struct multiboot_header
*) p
;
349 if (header
->magic
== MULTIBOOT2_HEADER_MAGIC
)
357 grub_dprintf ("loader", "No multiboot 2 header found.\n");
360 /* Create the basic tags. */
361 grub_dprintf ("loader", "Creating multiboot 2 tags\n");
362 grub_mb2_tags_create ();
364 /* Load the kernel and create its tag. */
365 elf
= grub_elf_file (file
);
368 grub_dprintf ("loader", "Loading ELF multiboot 2 file.\n");
369 err
= grub_mb2_load_elf (elf
, argc
-1, &argv
[1]);
370 grub_elf_close (elf
);
375 grub_dprintf ("loader", "Loading non-ELF multiboot 2 file.\n");
378 err
= grub_mb2_load_other (file
, header
);
380 err
= grub_error (GRUB_ERR_BAD_OS
,
381 "Need multiboot 2 header to load non-ELF files.");
382 grub_file_close (file
);
391 grub_loader_set (grub_mb2_boot
, grub_mb2_unload
, 1);
395 grub_mb2_tags_free ();
396 grub_dl_unref (my_mod
);
400 grub_module2 (int argc
, char *argv
[])
403 grub_addr_t modaddr
= 0;
404 grub_ssize_t modsize
= 0;
409 grub_error (GRUB_ERR_BAD_ARGUMENT
, "No module specified");
415 grub_error (GRUB_ERR_BAD_ARGUMENT
, "No module type specified");
421 grub_error (GRUB_ERR_BAD_ARGUMENT
,
422 "You need to load the multiboot kernel first");
426 /* Load module data. */
427 file
= grub_gzfile_open (argv
[0], 1);
431 modsize
= grub_file_size (file
);
432 err
= grub_mb2_arch_module_alloc (modsize
, &modaddr
);
436 grub_dprintf ("loader", "Loading module at 0x%x - 0x%x\n", modaddr
,
438 if (grub_file_read (file
, (char *) modaddr
, modsize
) != modsize
)
440 grub_error (GRUB_ERR_FILE_READ_ERROR
, "Couldn't read file");
444 /* Create the module tag. */
445 err
= grub_mb2_tag_module_create (modaddr
, modsize
,
446 argv
[1], MULTIBOOT2_TAG_MODULE
,
455 grub_file_close (file
);
458 grub_mb2_arch_module_free (modaddr
, modsize
);