1 /* $NetBSD: multiboot.c,v 1.18 2008/11/30 18:21:34 martin Exp $ */
4 * Copyright (c) 2005, 2006 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Julio M. Merino Vidal.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: multiboot.c,v 1.18 2008/11/30 18:21:34 martin Exp $");
35 #include "opt_multiboot.h"
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/cdefs_elf.h>
40 #include <sys/boot_flag.h>
42 #include <sys/exec_elf.h>
43 #include <sys/optstr.h>
44 #include <sys/ksyms.h>
46 #include <machine/bootinfo.h>
47 #include <machine/multiboot.h>
49 #if !defined(MULTIBOOT)
50 # error "MULTIBOOT not defined; this cannot happen."
53 /* --------------------------------------------------------------------- */
56 * Symbol and string table for the loaded kernel.
59 struct multiboot_symbols
{
66 /* --------------------------------------------------------------------- */
69 * External variables. All of them, with the exception of 'end', must
70 * be set at some point within this file.
72 * XXX these should be found in a header file!
74 extern int biosbasemem
;
75 extern int biosextmem
;
76 extern int biosmem_implicit
;
78 extern struct bootinfo bootinfo
;
82 /* --------------------------------------------------------------------- */
85 * Copy of the Multiboot information structure passed to us by the boot
86 * loader. The Multiboot_Info structure has some pointers adjusted to the
87 * other variables -- see multiboot_pre_reloc() -- so you oughtn't access
88 * them directly. In other words, always access them through the
89 * Multiboot_Info variable.
91 static char Multiboot_Cmdline
[255];
92 static uint8_t Multiboot_Drives
[255];
93 static struct multiboot_info Multiboot_Info
;
94 static bool Multiboot_Loader
= false;
95 static char Multiboot_Loader_Name
[255];
96 static uint8_t Multiboot_Mmap
[1024];
97 static struct multiboot_symbols Multiboot_Symbols
;
99 /* --------------------------------------------------------------------- */
102 * Prototypes for private functions.
104 static void bootinfo_add(struct btinfo_common
*, int, int);
105 static void copy_syms(struct multiboot_info
*);
106 static void setup_biosgeom(struct multiboot_info
*);
107 static void setup_bootdisk(struct multiboot_info
*);
108 static void setup_bootpath(struct multiboot_info
*);
109 static void setup_console(struct multiboot_info
*);
110 static void setup_howto(struct multiboot_info
*);
111 static void setup_memory(struct multiboot_info
*);
112 static void setup_memmap(struct multiboot_info
*);
114 /* --------------------------------------------------------------------- */
117 * Sets up the kernel if it was booted by a Multiboot-compliant boot
118 * loader. This is executed before the kernel has relocated itself.
119 * The main purpose of this function is to copy all the information
120 * passed in by the boot loader to a safe place, so that it is available
121 * after it has been relocated.
123 * WARNING: Because the kernel has not yet relocated itself to KERNBASE,
124 * special care has to be taken when accessing memory because absolute
125 * addresses (referring to kernel symbols) do not work. So:
127 * 1) Avoid jumps to absolute addresses (such as gotos and switches).
128 * 2) To access global variables use their physical address, which
129 * can be obtained using the RELOC macro.
132 multiboot_pre_reloc(struct multiboot_info
*mi
)
134 #define RELOC(type, x) ((type)((vaddr_t)(x) - KERNBASE))
135 struct multiboot_info
*midest
=
136 RELOC(struct multiboot_info
*, &Multiboot_Info
);
138 *RELOC(bool *, &Multiboot_Loader
) = true;
139 memcpy(midest
, mi
, sizeof(Multiboot_Info
));
141 if (mi
->mi_flags
& MULTIBOOT_INFO_HAS_CMDLINE
) {
142 strncpy(RELOC(void *, Multiboot_Cmdline
), mi
->mi_cmdline
,
143 sizeof(Multiboot_Cmdline
));
144 midest
->mi_cmdline
= (char *)&Multiboot_Cmdline
;
147 if (mi
->mi_flags
& MULTIBOOT_INFO_HAS_LOADER_NAME
) {
148 strncpy(RELOC(void *, Multiboot_Loader_Name
),
149 mi
->mi_loader_name
, sizeof(Multiboot_Loader_Name
));
150 midest
->mi_loader_name
= (char *)&Multiboot_Loader_Name
;
153 if (mi
->mi_flags
& MULTIBOOT_INFO_HAS_MMAP
) {
154 memcpy(RELOC(void *, Multiboot_Mmap
),
155 (void *)mi
->mi_mmap_addr
, mi
->mi_mmap_length
);
156 midest
->mi_mmap_addr
= (vaddr_t
)&Multiboot_Mmap
;
159 if (mi
->mi_flags
& MULTIBOOT_INFO_HAS_DRIVES
) {
160 memcpy(RELOC(void *, Multiboot_Drives
),
161 (void *)mi
->mi_drives_addr
, mi
->mi_drives_length
);
162 midest
->mi_drives_addr
= (vaddr_t
)&Multiboot_Drives
;
169 /* --------------------------------------------------------------------- */
172 * Sets up the kernel if it was booted by a Multiboot-compliant boot
173 * loader. This is executed just after the kernel has relocated itself.
174 * At this point, executing any kind of code is safe, keeping in mind
175 * that no devices have been initialized yet (not even the console!).
178 multiboot_post_reloc(void)
180 struct multiboot_info
*mi
;
182 if (! Multiboot_Loader
)
185 mi
= &Multiboot_Info
;
186 bootinfo
.bi_nentries
= 0;
197 /* --------------------------------------------------------------------- */
200 * Prints a summary of the information collected in the Multiboot
201 * information header (if present). Done as a separate function because
202 * the console has to be available.
205 multiboot_print_info(void)
207 struct multiboot_info
*mi
= &Multiboot_Info
;
208 struct multiboot_symbols
*ms
= &Multiboot_Symbols
;
210 if (! Multiboot_Loader
)
213 printf("multiboot: Information structure flags: 0x%08x\n",
216 if (mi
->mi_flags
& MULTIBOOT_INFO_HAS_LOADER_NAME
)
217 printf("multiboot: Boot loader: %s\n", mi
->mi_loader_name
);
219 if (mi
->mi_flags
& MULTIBOOT_INFO_HAS_CMDLINE
)
220 printf("multiboot: Command line: %s\n", mi
->mi_cmdline
);
222 if (mi
->mi_flags
& MULTIBOOT_INFO_HAS_MEMORY
)
223 printf("multiboot: %u KB lower memory, %u KB upper memory\n",
224 mi
->mi_mem_lower
, mi
->mi_mem_upper
);
226 if (mi
->mi_flags
& MULTIBOOT_INFO_HAS_ELF_SYMS
) {
228 printf("multiboot: Symbol table at %p, length %d bytes\n",
229 ms
->s_symstart
, ms
->s_symsize
);
230 printf("multiboot: String table at %p, length %d bytes\n",
231 ms
->s_strstart
, ms
->s_strsize
);
235 /* --------------------------------------------------------------------- */
238 * Adds the bootinfo entry given in 'item' to the bootinfo tables.
239 * Sets the item type to 'type' and its length to 'len'.
242 bootinfo_add(struct btinfo_common
*item
, int type
, int len
)
245 struct bootinfo
*bip
= (struct bootinfo
*)&bootinfo
;
251 data
= (vaddr_t
)&bip
->bi_data
;
252 for (i
= 0; i
< bip
->bi_nentries
; i
++) {
253 struct btinfo_common
*tmp
;
255 tmp
= (struct btinfo_common
*)data
;
258 if (data
+ len
< (vaddr_t
)&bip
->bi_data
+ sizeof(bip
->bi_data
)) {
259 memcpy((void *)data
, item
, len
);
264 /* --------------------------------------------------------------------- */
267 * Copies the symbol table and the strings table passed in by the boot
268 * loader after the kernel's image, and sets up 'esym' accordingly so
269 * that this data is properly copied into upper memory during relocation.
271 * WARNING: This code runs before the kernel has relocated itself. See
272 * the note in multiboot_pre_reloc() for more information.
275 copy_syms(struct multiboot_info
*mi
)
277 #define RELOC(type, x) ((type)((vaddr_t)(x) - KERNBASE))
279 Elf32_Shdr
*symtabp
, *strtabp
;
280 struct multiboot_symbols
*ms
;
281 size_t symsize
, strsize
;
282 paddr_t symaddr
, straddr
;
283 paddr_t symstart
, strstart
;
287 * Check if the Multiboot information header has symbols or not.
289 if (!(mi
->mi_flags
& MULTIBOOT_INFO_HAS_ELF_SYMS
))
292 ms
= RELOC(struct multiboot_symbols
*, &Multiboot_Symbols
);
295 * Locate a symbol table and its matching string table in the
296 * section headers passed in by the boot loader. Set 'symtabp'
297 * and 'strtabp' with pointers to the matching entries.
299 symtabp
= strtabp
= NULL
;
300 for (i
= 0; i
< mi
->mi_elfshdr_num
&& symtabp
== NULL
&&
301 strtabp
== NULL
; i
++) {
304 shdrp
= &((Elf32_Shdr
*)mi
->mi_elfshdr_addr
)[i
];
306 if ((shdrp
->sh_type
& SHT_SYMTAB
) &&
307 shdrp
->sh_link
!= SHN_UNDEF
) {
310 shdrp2
= &((Elf32_Shdr
*)mi
->mi_elfshdr_addr
)
313 if (shdrp2
->sh_type
& SHT_STRTAB
) {
319 if (symtabp
== NULL
|| strtabp
== NULL
)
322 symaddr
= symtabp
->sh_addr
;
323 straddr
= strtabp
->sh_addr
;
324 symsize
= symtabp
->sh_size
;
325 strsize
= strtabp
->sh_size
;
328 * Copy the symbol and string tables just after the kernel's
329 * end address, in this order. Only the contents of these ELF
330 * sections are copied; headers are discarded. esym is later
331 * updated to point to the lowest "free" address after the tables
332 * so that they are mapped appropriately when enabling paging.
334 * We need to be careful to not overwrite valid data doing the
335 * copies, hence all the different cases below. We can assume
336 * that if the tables start before the kernel's end address,
337 * they will not grow over this address.
339 if ((paddr_t
)symtabp
< (paddr_t
)&end
- KERNBASE
&&
340 (paddr_t
)strtabp
< (paddr_t
)&end
- KERNBASE
) {
341 symstart
= (paddr_t
)((vaddr_t
)&end
- KERNBASE
);
342 strstart
= symstart
+ symsize
;
343 memcpy((void *)symstart
, (void *)symaddr
, symsize
);
344 memcpy((void *)strstart
, (void *)straddr
, strsize
);
345 } else if ((paddr_t
)symtabp
> (paddr_t
)&end
- KERNBASE
&&
346 (paddr_t
)strtabp
< (paddr_t
)&end
- KERNBASE
) {
347 symstart
= (paddr_t
)((vaddr_t
)&end
- KERNBASE
);
348 strstart
= symstart
+ symsize
;
349 memcpy((void *)symstart
, (void *)symaddr
, symsize
);
350 memcpy((void *)strstart
, (void *)straddr
, strsize
);
351 } else if ((paddr_t
)symtabp
< (paddr_t
)&end
- KERNBASE
&&
352 (paddr_t
)strtabp
> (paddr_t
)&end
- KERNBASE
) {
353 strstart
= (paddr_t
)((vaddr_t
)&end
- KERNBASE
);
354 symstart
= strstart
+ strsize
;
355 memcpy((void *)strstart
, (void *)straddr
, strsize
);
356 memcpy((void *)symstart
, (void *)symaddr
, symsize
);
358 /* symtabp and strtabp are both over end */
359 if ((paddr_t
)symtabp
< (paddr_t
)strtabp
) {
360 symstart
= (paddr_t
)((vaddr_t
)&end
- KERNBASE
);
361 strstart
= symstart
+ symsize
;
362 memcpy((void *)symstart
, (void *)symaddr
, symsize
);
363 memcpy((void *)strstart
, (void *)straddr
, strsize
);
365 strstart
= (paddr_t
)((vaddr_t
)&end
- KERNBASE
);
366 symstart
= strstart
+ strsize
;
367 memcpy((void *)strstart
, (void *)straddr
, strsize
);
368 memcpy((void *)symstart
, (void *)symaddr
, symsize
);
371 *RELOC(int *, &esym
) =
372 (int)(symstart
+ symsize
+ strsize
+ KERNBASE
);
374 ms
->s_symstart
= (void *)(symstart
+ KERNBASE
);
375 ms
->s_symsize
= symsize
;
376 ms
->s_strstart
= (void *)(strstart
+ KERNBASE
);
377 ms
->s_strsize
= strsize
;
381 /* --------------------------------------------------------------------- */
384 * Sets up the biosgeom bootinfo structure if the Multiboot information
385 * structure provides information about disk drives.
388 setup_biosgeom(struct multiboot_info
*mi
)
391 uint8_t bidata
[1024];
392 struct btinfo_biosgeom
*bi
;
394 if (!(mi
->mi_flags
& MULTIBOOT_INFO_HAS_DRIVES
))
397 memset(bidata
, 0, sizeof(bidata
));
398 bi
= (struct btinfo_biosgeom
*)bidata
;
401 while (pos
< mi
->mi_drives_length
) {
402 struct multiboot_drive
*md
;
403 struct bi_biosgeom_entry bbe
;
405 md
= (struct multiboot_drive
*)
406 &((uint8_t *)mi
->mi_drives_addr
)[pos
];
408 memset(&bbe
, 0, sizeof(bbe
));
409 bbe
.sec
= md
->md_sectors
;
410 bbe
.head
= md
->md_heads
;
411 bbe
.cyl
= md
->md_cylinders
;
412 bbe
.dev
= md
->md_number
;
414 memcpy(&bi
->disk
[bi
->num
], &bbe
, sizeof(bbe
));
417 pos
+= md
->md_length
;
420 bootinfo_add((struct btinfo_common
*)bi
, BTINFO_BIOSGEOM
,
421 sizeof(struct btinfo_biosgeom
) +
422 bi
->num
* sizeof(struct bi_biosgeom_entry
));
425 /* --------------------------------------------------------------------- */
428 * Sets up the default root device if the Multiboot information
429 * structure provides information about the boot drive (where the kernel
430 * image was loaded from) or if the user gave a 'root' parameter on the
434 setup_bootdisk(struct multiboot_info
*mi
)
437 struct btinfo_rootdevice bi
;
441 if (mi
->mi_flags
& MULTIBOOT_INFO_HAS_CMDLINE
)
442 found
= optstr_get(mi
->mi_cmdline
, "root", bi
.devname
,
445 if (!found
&& (mi
->mi_flags
& MULTIBOOT_INFO_HAS_BOOT_DEVICE
)) {
446 const char *devprefix
;
448 /* Attempt to match the BIOS boot disk to a device. There
449 * is not much we can do to get it right. (Well, strictly
450 * speaking, we could, but it is certainly not worth the
452 switch (mi
->mi_boot_device_drive
) {
453 case 0x00: devprefix
= "fd0"; break;
454 case 0x01: devprefix
= "fd1"; break;
455 case 0x80: devprefix
= "wd0"; break;
456 case 0x81: devprefix
= "wd1"; break;
457 case 0x82: devprefix
= "wd2"; break;
458 case 0x83: devprefix
= "wd3"; break;
459 default: devprefix
= "wd0";
462 strcpy(bi
.devname
, devprefix
);
463 if (mi
->mi_boot_device_part2
!= 0xFF)
464 bi
.devname
[3] = mi
->mi_boot_device_part2
+ 'a';
467 bi
.devname
[4] = '\0';
473 bootinfo_add((struct btinfo_common
*)&bi
, BTINFO_ROOTDEVICE
,
474 sizeof(struct btinfo_rootdevice
));
478 /* --------------------------------------------------------------------- */
481 * Sets up the bootpath bootinfo structure with an appropriate kernel
482 * name derived from the boot command line. The Multiboot information
483 * structure does not provide this detail directly, so we try to derive
484 * it from the command line setting.
487 setup_bootpath(struct multiboot_info
*mi
)
489 struct btinfo_bootpath bi
;
493 if (strncmp(Multiboot_Loader_Name
, "GNU GRUB ",
494 sizeof(Multiboot_Loader_Name
)) > 0) {
496 while (*cl
!= '\0' && *cl
!= '/')
500 while (*cl2
!= '\0' && *cl2
!= ' ') {
507 memcpy(bi
.bootpath
, cl
, MIN(sizeof(bi
.bootpath
), len
));
509 bi
.bootpath
[MIN(sizeof(bi
.bootpath
), len
)] = '\0';
511 bootinfo_add((struct btinfo_common
*)&bi
, BTINFO_BOOTPATH
,
512 sizeof(struct btinfo_bootpath
));
516 /* --------------------------------------------------------------------- */
519 * Sets up the console bootinfo structure if the user gave a 'console'
520 * argument on the boot command line. The Multiboot information
521 * structure gives no hint about this, so the only way to know where the
522 * console is is to let the user specify it.
524 * If there wasn't any 'console' argument, this does not generate any
525 * bootinfo entry, falling back to the kernel's default console.
527 * If there weren't any of 'console_speed' or 'console_addr' arguments,
528 * this falls back to the default values for the serial port.
531 setup_console(struct multiboot_info
*mi
)
533 struct btinfo_console bi
;
538 if (mi
->mi_flags
& MULTIBOOT_INFO_HAS_CMDLINE
)
539 found
= optstr_get(mi
->mi_cmdline
, "console", bi
.devname
,
545 if (strncmp(bi
.devname
, "com", sizeof(bi
.devname
)) == 0) {
548 found
= optstr_get(mi
->mi_cmdline
, "console_speed",
551 bi
.speed
= strtoul(tmp
, NULL
, 10);
553 bi
.speed
= 0; /* Use default speed. */
555 found
= optstr_get(mi
->mi_cmdline
, "console_addr",
558 if (tmp
[0] == '0' && tmp
[1] == 'x')
559 bi
.addr
= strtoul(tmp
+ 2, NULL
, 16);
561 bi
.addr
= strtoul(tmp
, NULL
, 10);
563 bi
.addr
= 0; /* Use default address. */
566 } else if (strncmp(bi
.devname
, "pc", sizeof(bi
.devname
)) == 0)
572 bootinfo_add((struct btinfo_common
*)&bi
,
573 BTINFO_CONSOLE
, sizeof(struct btinfo_console
));
577 /* --------------------------------------------------------------------- */
580 * Sets up the 'boothowto' variable based on the options given in the
581 * boot command line, if any.
584 setup_howto(struct multiboot_info
*mi
)
588 if (!(mi
->mi_flags
& MULTIBOOT_INFO_HAS_CMDLINE
))
593 /* Skip kernel file name. */
594 while (*cl
!= '\0' && *cl
!= ' ')
596 while (*cl
!= '\0' && *cl
== ' ')
599 /* Check if there are flags and set 'howto' accordingly. */
604 while (*cl
!= '\0' && *cl
!= ' ') {
605 BOOT_FLAG(*cl
, howto
);
615 /* --------------------------------------------------------------------- */
618 * Sets up the memmap bootinfo structure to describe available memory as
622 setup_memmap(struct multiboot_info
*mi
)
626 struct btinfo_memmap
*bi
;
628 if (!(mi
->mi_flags
& MULTIBOOT_INFO_HAS_MMAP
))
631 bi
= (struct btinfo_memmap
*)data
;
635 while (i
< mi
->mi_mmap_length
) {
636 struct multiboot_mmap
*mm
;
637 struct bi_memmap_entry
*bie
;
639 bie
= &bi
->entry
[bi
->num
];
641 mm
= (struct multiboot_mmap
*)(mi
->mi_mmap_addr
+ i
);
642 bie
->addr
= mm
->mm_base_addr
;
643 bie
->size
= mm
->mm_length
;
644 if (mm
->mm_type
== 1)
645 bie
->type
= BIM_Memory
;
647 bie
->type
= BIM_Reserved
;
650 i
+= mm
->mm_size
+ 4;
653 bootinfo_add((struct btinfo_common
*)bi
, BTINFO_MEMMAP
,
657 /* --------------------------------------------------------------------- */
660 * Sets up the 'biosbasemem' and 'biosextmem' variables if the
661 * Multiboot information structure provides information about memory.
664 setup_memory(struct multiboot_info
*mi
)
667 if (!(mi
->mi_flags
& MULTIBOOT_INFO_HAS_MEMORY
))
670 /* Make sure we don't override user-set variables. */
671 if (biosbasemem
== 0) {
672 biosbasemem
= mi
->mi_mem_lower
;
673 biosmem_implicit
= 1;
675 if (biosextmem
== 0) {
676 biosextmem
= mi
->mi_mem_upper
;
677 biosmem_implicit
= 1;
681 /* --------------------------------------------------------------------- */
684 * Sets up the initial kernel symbol table. Returns true if this was
685 * passed in by Multiboot; false otherwise.
688 multiboot_ksyms_addsyms_elf(void)
690 struct multiboot_info
*mi
= &Multiboot_Info
;
691 struct multiboot_symbols
*ms
= &Multiboot_Symbols
;
693 if (mi
->mi_flags
& MULTIBOOT_INFO_HAS_ELF_SYMS
) {
698 memcpy(ehdr
.e_ident
, ELFMAG
, SELFMAG
);
699 ehdr
.e_ident
[EI_CLASS
] = ELFCLASS32
;
700 ehdr
.e_ident
[EI_DATA
] = ELFDATA2LSB
;
701 ehdr
.e_ident
[EI_VERSION
] = EV_CURRENT
;
702 ehdr
.e_type
= ET_EXEC
;
703 ehdr
.e_machine
= EM_386
;
705 ehdr
.e_ehsize
= sizeof(ehdr
);
707 ksyms_addsyms_explicit((void *)&ehdr
,
708 ms
->s_symstart
, ms
->s_symsize
,
709 ms
->s_strstart
, ms
->s_strsize
);
712 return mi
->mi_flags
& MULTIBOOT_INFO_HAS_ELF_SYMS
;