2 * Copyright (c) 2014 Roger Pau Monné <royger@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * This multiboot implementation only implements a subset of the full
29 * multiboot specification in order to be able to boot Xen and a
30 * FreeBSD Dom0. Trying to use it to boot other multiboot compliant
31 * kernels will most surely fail.
33 * The full multiboot specification can be found here:
34 * http://www.gnu.org/software/grub/manual/multiboot/multiboot.html
37 #include <sys/cdefs.h>
39 #include <sys/param.h>
41 #include <sys/linker.h>
42 #include <sys/module.h>
43 #include <sys/stdint.h>
44 #define _MACHINE_ELF_WANT_32BIT
45 #include <machine/elf.h>
46 #include <machine/metadata.h>
47 #include <machine/pc/bios.h>
51 #include "bootstrap.h"
52 #include "multiboot.h"
54 #include "../zfs/libzfs.h"
55 #include "../i386/libi386/libi386.h"
56 #include "../i386/btx/lib/btxv86.h"
58 #define MULTIBOOT_SUPPORTED_FLAGS \
59 (MULTIBOOT_AOUT_KLUDGE|MULTIBOOT_PAGE_ALIGN|MULTIBOOT_MEMORY_INFO)
61 #define METADATA_FIXED_SIZE (PAGE_SIZE*4)
62 #define METADATA_MODULE_SIZE PAGE_SIZE
64 #define METADATA_RESV_SIZE(mod_num) \
65 roundup(METADATA_FIXED_SIZE + METADATA_MODULE_SIZE * mod_num, PAGE_SIZE)
67 /* MB data heap pointer */
68 static vm_offset_t last_addr
;
70 extern int elf32_loadfile_raw(char *filename
, u_int64_t dest
,
71 struct preloaded_file
**result
, int multiboot
);
72 extern int elf64_load_modmetadata(struct preloaded_file
*fp
, u_int64_t dest
);
73 extern int elf64_obj_loadfile(char *filename
, u_int64_t dest
,
74 struct preloaded_file
**result
);
76 static int multiboot_loadfile(char *, u_int64_t
, struct preloaded_file
**);
77 static int multiboot_exec(struct preloaded_file
*);
79 static int multiboot_obj_loadfile(char *, u_int64_t
, struct preloaded_file
**);
80 static int multiboot_obj_exec(struct preloaded_file
*fp
);
82 struct file_format multiboot
= { multiboot_loadfile
, multiboot_exec
};
83 struct file_format multiboot_obj
=
84 { multiboot_obj_loadfile
, multiboot_obj_exec
};
86 extern void multiboot_tramp();
88 static const char mbl_name
[] = "illumos Loader";
91 num_modules(struct preloaded_file
*kfp
)
93 struct kernel_module
*kmp
;
96 for (kmp
= kfp
->f_modules
; kmp
!= NULL
; kmp
= kmp
->m_next
)
105 struct preloaded_file
*fp
;
106 vm_offset_t addr
= 0;
108 for (fp
= file_findfile(NULL
, NULL
); fp
!= NULL
; fp
= fp
->f_next
) {
109 if (addr
< (fp
->f_addr
+ fp
->f_size
))
110 addr
= fp
->f_addr
+ fp
->f_size
;
117 multiboot_loadfile(char *filename
, u_int64_t dest
,
118 struct preloaded_file
**result
)
122 caddr_t header_search
;
125 struct multiboot_header
*header
;
127 struct preloaded_file
*fp
;
129 if (filename
== NULL
)
132 /* is kernel already loaded? */
133 fp
= file_findfile(NULL
, NULL
);
138 if ((fd
= open(filename
, O_RDONLY
)) == -1)
142 * Read MULTIBOOT_SEARCH size in order to search for the
143 * multiboot magic header.
145 header_search
= malloc(MULTIBOOT_SEARCH
);
146 if (header_search
== NULL
) {
151 search_size
= read(fd
, header_search
, MULTIBOOT_SEARCH
);
152 magic
= (uint32_t *)header_search
;
155 for (i
= 0; i
< (search_size
/ sizeof(uint32_t)); i
++) {
156 if (magic
[i
] == MULTIBOOT_HEADER_MAGIC
) {
157 header
= (struct multiboot_header
*)&magic
[i
];
162 if (header
== NULL
) {
167 /* Valid multiboot header has been found, validate checksum */
168 if (header
->magic
+ header
->flags
+ header
->checksum
!= 0) {
170 "Multiboot checksum failed, magic: 0x%x flags: 0x%x checksum: 0x%x\n",
171 header
->magic
, header
->flags
, header
->checksum
);
176 if ((header
->flags
& ~MULTIBOOT_SUPPORTED_FLAGS
) != 0) {
177 printf("Unsupported multiboot flags found: 0x%x\n",
182 /* AOUT KLUDGE means we just load entire flat file as blob */
183 if (header
->flags
& MULTIBOOT_AOUT_KLUDGE
) {
187 dest
= header
->load_addr
;
188 if (lseek(fd
, 0, SEEK_SET
) == -1) {
189 printf("lseek failed\n");
195 got
= archsw
.arch_readin(fd
, laddr
, 4096);
199 printf("error reading: %s", strerror(errno
));
211 fp
->f_name
= strdup(filename
);
212 fp
->f_type
= strdup("aout multiboot kernel");
213 fp
->f_addr
= header
->entry_addr
;
214 fp
->f_size
= laddr
- dest
;
215 if (fp
->f_size
== 0) {
220 fp
->f_metadata
= NULL
;
226 error
= elf32_loadfile_raw(filename
, dest
, result
, 1);
228 printf("elf32_loadfile_raw failed: %d unable to "
229 "load multiboot kernel\n", error
);
234 setenv("kernelname", (*result
)->f_name
, 1);
235 bios_addsmapdata(*result
);
243 * returns allocated virtual address from MB info area
248 vm_offset_t ptr
= last_addr
;
249 if (ptr
+ n
>= high_heap_base
)
251 last_addr
= roundup(last_addr
+ n
, MULTIBOOT_INFO_ALIGN
);
256 * Since for now we have no way to pass the environment to the kernel other than
257 * through arguments, we need to take care of console setup.
259 * If the console is in mirror mode, set the kernel console from $os_console.
260 * If it's unset, use first item from $console.
261 * If $console is "ttyX", also pass $ttyX-mode, since it may have been set by
264 * In case of memory allocation errors, just return original command line,
265 * so we have chance of booting.
267 * On success, cl will be freed and a new, allocated command line string is
271 update_cmdline(char *cl
)
273 char *os_console
= getenv("os_console");
274 char *ttymode
= NULL
;
279 if (os_console
== NULL
) {
280 tmp
= strdup(getenv("console"));
281 os_console
= strsep(&tmp
, ", ");
283 os_console
= strdup(os_console
);
285 if (os_console
== NULL
)
288 if (strncmp(os_console
, "tty", 3) == 0) {
289 snprintf(mode
, sizeof (mode
), "%s-mode", os_console
);
290 ttymode
= getenv(mode
); /* never NULL */
293 if (strstr(cl
, "-B") != NULL
) {
294 len
= strlen(cl
) + 1;
296 * if console is not present, add it
297 * if console is ttyX, add ttymode
299 tmp
= strstr(cl
, "console");
301 len
+= 12; /* " -B console=" */
302 len
+= strlen(os_console
);
303 if (ttymode
!= NULL
) {
304 len
+= 13; /* ",ttyX-mode=\"\"" */
305 len
+= strlen(ttymode
);
314 "%s -B console=%s,%s-mode=\"%s\"",
315 cl
, os_console
, os_console
, ttymode
);
317 sprintf(tmp
, "%s -B console=%s",
320 /* console is set, do we need tty mode? */
322 if (strstr(tmp
, "tty") == tmp
) {
323 strncpy(mode
, tmp
, 4);
325 strcat(mode
, "-mode");
326 ttymode
= getenv(mode
); /* never NULL */
331 len
= strlen(cl
) + 1;
332 len
+= 13; /* ",ttyX-mode=\"\"" */
333 len
+= strlen(ttymode
);
339 sprintf(tmp
, "%s,%s=\"%s\"", cl
, mode
, ttymode
);
343 * no -B, so we need to add " -B console=%s[,ttyX-mode=\"%s\"]"
345 len
= strlen(cl
) + 1;
346 len
+= 12; /* " -B console=" */
347 len
+= strlen(os_console
);
348 if (ttymode
!= NULL
) {
349 len
+= 13; /* ",ttyX-mode=\"\"" */
350 len
+= strlen(ttymode
);
358 sprintf(tmp
, "%s -B console=%s,%s-mode=\"%s\"", cl
,
359 os_console
, os_console
, ttymode
);
361 sprintf(tmp
, "%s -B console=%s", cl
, os_console
);
369 kernel_cmdline(struct preloaded_file
*fp
, struct i386_devdesc
*rootdev
)
371 char *cmdline
= NULL
;
374 if (fp
->f_args
== NULL
)
375 fp
->f_args
= getenv("boot-args");
377 len
= strlen(fp
->f_name
) + 1;
379 if (fp
->f_args
!= NULL
)
380 len
+= strlen(fp
->f_args
) + 1;
382 if (rootdev
->d_type
== DEVT_ZFS
)
383 len
+= 3 + strlen(zfs_bootfs(rootdev
)) + 1;
385 cmdline
= malloc(len
);
389 if (rootdev
->d_type
== DEVT_ZFS
) {
390 if (fp
->f_args
!= NULL
)
391 snprintf(cmdline
, len
, "%s %s -B %s", fp
->f_name
,
392 fp
->f_args
, zfs_bootfs(rootdev
));
394 snprintf(cmdline
, len
, "%s -B %s", fp
->f_name
,
395 zfs_bootfs(rootdev
));
396 } else if (fp
->f_args
!= NULL
)
397 snprintf(cmdline
, len
, "%s %s", fp
->f_name
, fp
->f_args
);
399 snprintf(cmdline
, len
, "%s", fp
->f_name
);
401 return (update_cmdline(cmdline
));
405 multiboot_exec(struct preloaded_file
*fp
)
407 struct preloaded_file
*mfp
;
408 vm_offset_t module_start
, metadata_size
;
409 vm_offset_t modulep
, kernend
, entry
;
410 struct file_metadata
*md
;
412 struct multiboot_info
*mb_info
= NULL
;
413 struct multiboot_mod_list
*mb_mod
= NULL
;
414 multiboot_memory_map_t
*mmap
;
415 struct bios_smap
*smap
;
416 struct i386_devdesc
*rootdev
;
417 extern BOOTPLAYER bootplayer
; /* dhcp info */
418 char *cmdline
= NULL
;
421 int rootfs
= 0; /* flag for rootfs */
422 int xen
= 0; /* flag for xen */
423 int kernel
= 0; /* flag for kernel */
425 /* set up base for mb_malloc */
426 for (mfp
= fp
; mfp
->f_next
!= NULL
; mfp
= mfp
->f_next
);
428 /* start info block from new page */
429 last_addr
= roundup(mfp
->f_addr
+ mfp
->f_size
, MULTIBOOT_MOD_ALIGN
);
431 /* Allocate the multiboot struct and fill the basic details. */
432 mb_info
= (struct multiboot_info
*)PTOV(mb_malloc(sizeof (*mb_info
)));
434 bzero(mb_info
, sizeof(struct multiboot_info
));
435 mb_info
->flags
= MULTIBOOT_INFO_MEMORY
|MULTIBOOT_INFO_BOOT_LOADER_NAME
;
436 mb_info
->mem_lower
= bios_basemem
/ 1024;
437 mb_info
->mem_upper
= bios_extmem
/ 1024;
438 mb_info
->boot_loader_name
= mb_malloc(strlen(mbl_name
) + 1);
440 i386_copyin(mbl_name
, mb_info
->boot_loader_name
, strlen(mbl_name
)+1);
442 i386_getdev((void **)(&rootdev
), NULL
, NULL
);
443 if (rootdev
== NULL
) {
444 printf("can't determine root device\n");
450 * boot image command line. if args were not provided, we need to set
451 * args here, and that depends on image type...
452 * fortunately we only have following options:
453 * 64 or 32 bit unix or xen. so we just check if f_name has unix.
455 /* do we boot xen? */
456 if (strstr(fp
->f_name
, "unix") == NULL
)
462 for (mfp
= fp
->f_next
; mfp
!= NULL
; mfp
= mfp
->f_next
) {
464 if (mfp
->f_type
!= NULL
&& strcmp(mfp
->f_type
, "rootfs") == 0)
466 if (mfp
->f_type
!= NULL
&& strcmp(mfp
->f_type
, "kernel") == 0)
470 if (num
== 0 || rootfs
== 0) {
471 /* need at least one module - rootfs */
472 printf("No rootfs module provided, aborting\n");
476 if (xen
== 1 && kernel
== 0) {
477 printf("No kernel module provided for xen, aborting\n");
481 mb_mod
= (struct multiboot_mod_list
*) PTOV(last_addr
);
482 last_addr
+= roundup(sizeof(*mb_mod
) * num
, MULTIBOOT_INFO_ALIGN
);
484 bzero(mb_mod
, sizeof(*mb_mod
) * num
);
487 for (mfp
= fp
->f_next
; mfp
!= NULL
; mfp
= mfp
->f_next
) {
488 mb_mod
[num
].mod_start
= mfp
->f_addr
;
489 mb_mod
[num
].mod_end
= mfp
->f_addr
+ mfp
->f_size
;
491 if (strcmp(mfp
->f_type
, "kernel") == 0) {
492 cmdline
= kernel_cmdline(mfp
, rootdev
);
493 if (cmdline
== NULL
) {
498 len
= strlen(mfp
->f_name
) + 1;
499 len
+= strlen(mfp
->f_type
) + 5 + 1;
500 if (mfp
->f_args
!= NULL
) {
501 len
+= strlen(mfp
->f_args
) + 1;
503 cmdline
= malloc(len
);
504 if (cmdline
== NULL
) {
509 if (mfp
->f_args
!= NULL
)
510 snprintf(cmdline
, len
, "%s type=%s %s",
511 mfp
->f_name
, mfp
->f_type
, mfp
->f_args
);
513 snprintf(cmdline
, len
, "%s type=%s",
514 mfp
->f_name
, mfp
->f_type
);
517 mb_mod
[num
].cmdline
= mb_malloc(strlen(cmdline
)+1);
518 i386_copyin(cmdline
, mb_mod
[num
].cmdline
, strlen(cmdline
)+1);
523 mb_info
->mods_count
= num
;
524 mb_info
->mods_addr
= VTOP(mb_mod
);
525 mb_info
->flags
|= MULTIBOOT_INFO_MODS
;
527 md
= file_findmetadata(fp
, MODINFOMD_SMAP
);
529 printf("no memory smap\n");
534 num
= md
->md_size
/ sizeof(struct bios_smap
); /* number of entries */
535 mmap
= (multiboot_memory_map_t
*)PTOV(mb_malloc(sizeof(*mmap
) * num
));
537 mb_info
->mmap_length
= num
* sizeof(*mmap
);
538 smap
= (struct bios_smap
*)md
->md_data
;
540 for (i
= 0; i
< num
; i
++) {
541 mmap
[i
].size
= sizeof(*smap
);
542 mmap
[i
].addr
= smap
[i
].base
;
543 mmap
[i
].len
= smap
[i
].length
;
544 mmap
[i
].type
= smap
[i
].type
;
546 mb_info
->mmap_addr
= VTOP(mmap
);
547 mb_info
->flags
|= MULTIBOOT_INFO_MEM_MAP
;
549 if (strstr(getenv("loaddev"), "pxe") != NULL
) {
550 mb_info
->drives_length
= sizeof (BOOTPLAYER
);
551 mb_info
->drives_addr
= mb_malloc(mb_info
->drives_length
);
552 i386_copyin(&bootplayer
, mb_info
->drives_addr
,
553 mb_info
->drives_length
);
554 mb_info
->flags
&= ~MULTIBOOT_INFO_DRIVE_INFO
;
557 * Set the image command line. Need to do this as last thing,
558 * as Illumos kernel dboot_startkern will check cmdline
559 * address as last check to find first free address.
561 if (fp
->f_args
== NULL
) {
563 cmdline
= getenv("xen_cmdline");
565 cmdline
= getenv("boot-args");
566 if (cmdline
!= NULL
) {
567 fp
->f_args
= strdup(cmdline
);
568 if (fp
->f_args
== NULL
) {
576 * if image is xen, we just use f_name + f_args for commandline
577 * for unix, we need to add zfs-bootfs.
580 len
= strlen(fp
->f_name
) + 1;
581 if (fp
->f_args
!= NULL
)
582 len
+= strlen(fp
->f_args
) + 1;
584 if (fp
->f_args
!= NULL
) {
585 if((cmdline
= malloc(len
)) == NULL
) {
589 snprintf(cmdline
, len
, "%s %s", fp
->f_name
, fp
->f_args
);
591 cmdline
= strdup(fp
->f_name
);
594 cmdline
= kernel_cmdline(fp
, rootdev
);
597 if (cmdline
== NULL
) {
602 mb_info
->cmdline
= mb_malloc(strlen(cmdline
)+1);
603 i386_copyin(cmdline
, mb_info
->cmdline
, strlen(cmdline
)+1);
604 mb_info
->flags
|= MULTIBOOT_INFO_CMDLINE
;
609 __exec((void *)VTOP(multiboot_tramp
), (void *)entry
,
610 (void *)VTOP(mb_info
));
612 panic("exec returned");
619 multiboot_obj_loadfile(char *filename
, u_int64_t dest
,
620 struct preloaded_file
**result
)
622 struct preloaded_file
*mfp
, *kfp
, *rfp
;
623 struct kernel_module
*kmp
;
626 /* See if there's a aout multiboot kernel loaded */
627 mfp
= file_findfile(NULL
, "aout multiboot kernel");
629 /* we have normal kernel loaded, add module */
630 rfp
= file_loadraw(filename
, "module", 0, NULL
, 0);
633 "Unable to load %s as a multiboot payload module\n",
637 rfp
->f_size
= roundup(rfp
->f_size
, PAGE_SIZE
);
642 /* See if there's a multiboot kernel loaded */
643 mfp
= file_findfile(NULL
, "elf multiboot kernel");
645 return (EFTYPE
); /* this allows to check other methods */
649 * We have a multiboot kernel loaded, see if there's a
650 * kernel loaded also.
652 kfp
= file_findfile(NULL
, "elf kernel");
655 * No kernel loaded, this must be it. The kernel has to
656 * be loaded as a raw file, it will be processed by
657 * Xen and correctly loaded as an ELF file.
659 rfp
= file_loadraw(filename
, "elf kernel", 0, NULL
, 0);
662 "Unable to load %s as a multiboot payload kernel\n",
667 /* Load kernel metadata... */
668 setenv("kernelname", filename
, 1);
669 error
= elf64_load_modmetadata(rfp
, rfp
->f_addr
+ rfp
->f_size
);
671 printf("Unable to load kernel %s metadata error: %d\n",
677 * Save space at the end of the kernel in order to place
678 * the metadata information. We do an approximation of the
679 * max metadata size, this is not optimal but it's probably
680 * the best we can do at this point. Once all modules are
681 * loaded and the size of the metadata is known this
682 * space will be recovered if not used.
684 mod_num
= num_modules(rfp
);
685 rfp
->f_size
= roundup(rfp
->f_size
, PAGE_SIZE
);
686 rfp
->f_size
+= METADATA_RESV_SIZE(mod_num
);
689 /* The rest should be loaded as regular modules */
690 error
= elf64_obj_loadfile(filename
, dest
, result
);
692 printf("Unable to load %s as an object file, error: %d",
702 multiboot_obj_exec(struct preloaded_file
*fp
)