xref: /netbsd/sys/arch/i386/i386/multiboot.c (revision 43108a21)
1*43108a21Smanu /*	$NetBSD: multiboot.c,v 1.26 2019/10/18 01:38:28 manu Exp $	*/
20b879f1cSjmmv 
30b879f1cSjmmv /*-
40b879f1cSjmmv  * Copyright (c) 2005, 2006 The NetBSD Foundation, Inc.
50b879f1cSjmmv  * All rights reserved.
60b879f1cSjmmv  *
70b879f1cSjmmv  * This code is derived from software contributed to The NetBSD Foundation
80b879f1cSjmmv  * by Julio M. Merino Vidal.
90b879f1cSjmmv  *
100b879f1cSjmmv  * Redistribution and use in source and binary forms, with or without
110b879f1cSjmmv  * modification, are permitted provided that the following conditions
120b879f1cSjmmv  * are met:
130b879f1cSjmmv  * 1. Redistributions of source code must retain the above copyright
140b879f1cSjmmv  *    notice, this list of conditions and the following disclaimer.
150b879f1cSjmmv  * 2. Redistributions in binary form must reproduce the above copyright
160b879f1cSjmmv  *    notice, this list of conditions and the following disclaimer in the
170b879f1cSjmmv  *    documentation and/or other materials provided with the distribution.
180b879f1cSjmmv  *
190b879f1cSjmmv  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
200b879f1cSjmmv  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
210b879f1cSjmmv  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
220b879f1cSjmmv  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
230b879f1cSjmmv  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
240b879f1cSjmmv  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
250b879f1cSjmmv  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
260b879f1cSjmmv  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
270b879f1cSjmmv  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
280b879f1cSjmmv  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
290b879f1cSjmmv  * POSSIBILITY OF SUCH DAMAGE.
300b879f1cSjmmv  */
310b879f1cSjmmv 
320b879f1cSjmmv #include <sys/cdefs.h>
33*43108a21Smanu __KERNEL_RCSID(0, "$NetBSD: multiboot.c,v 1.26 2019/10/18 01:38:28 manu Exp $");
349bb39d02Sjoerg 
359bb39d02Sjoerg #include "opt_multiboot.h"
360b879f1cSjmmv 
370b879f1cSjmmv #include <sys/param.h>
380b879f1cSjmmv #include <sys/systm.h>
390b879f1cSjmmv #include <sys/cdefs_elf.h>
400b879f1cSjmmv #include <sys/boot_flag.h>
410b879f1cSjmmv #include <sys/exec.h>
420b879f1cSjmmv #include <sys/exec_elf.h>
430b879f1cSjmmv #include <sys/optstr.h>
448d30a464Sjmmv #include <sys/ksyms.h>
450b879f1cSjmmv 
460b879f1cSjmmv #include <machine/bootinfo.h>
470b879f1cSjmmv #include <machine/multiboot.h>
480b879f1cSjmmv 
490b879f1cSjmmv #if !defined(MULTIBOOT)
500b879f1cSjmmv #  error "MULTIBOOT not defined; this cannot happen."
510b879f1cSjmmv #endif
520b879f1cSjmmv 
530b879f1cSjmmv /* --------------------------------------------------------------------- */
540b879f1cSjmmv 
550b879f1cSjmmv /*
568d30a464Sjmmv  * Symbol and string table for the loaded kernel.
578d30a464Sjmmv  */
588d30a464Sjmmv 
598d30a464Sjmmv struct multiboot_symbols {
6053524e44Schristos 	void *		s_symstart;
618d30a464Sjmmv 	size_t		s_symsize;
6253524e44Schristos 	void *		s_strstart;
638d30a464Sjmmv 	size_t		s_strsize;
648d30a464Sjmmv };
658d30a464Sjmmv 
668d30a464Sjmmv /* --------------------------------------------------------------------- */
678d30a464Sjmmv 
688d30a464Sjmmv /*
690b879f1cSjmmv  * External variables.  All of them, with the exception of 'end', must
700b879f1cSjmmv  * be set at some point within this file.
7195543a8eSmrg  *
7295543a8eSmrg  * XXX these should be found in a header file!
730b879f1cSjmmv  */
740b879f1cSjmmv extern int		biosbasemem;
750b879f1cSjmmv extern int		biosextmem;
7695543a8eSmrg extern int		biosmem_implicit;
770b879f1cSjmmv extern int		boothowto;
780b879f1cSjmmv extern struct bootinfo	bootinfo;
790b879f1cSjmmv extern int		end;
800b879f1cSjmmv extern int *		esym;
810b879f1cSjmmv 
820b879f1cSjmmv /* --------------------------------------------------------------------- */
830b879f1cSjmmv 
840b879f1cSjmmv /*
850b879f1cSjmmv  * Copy of the Multiboot information structure passed to us by the boot
860b879f1cSjmmv  * loader.  The Multiboot_Info structure has some pointers adjusted to the
87*43108a21Smanu  * other variables -- see multiboot1_pre_reloc() -- so you oughtn't access
880b879f1cSjmmv  * them directly.  In other words, always access them through the
890b879f1cSjmmv  * Multiboot_Info variable.
900b879f1cSjmmv  */
910b879f1cSjmmv static char			Multiboot_Cmdline[255];
920b879f1cSjmmv static uint8_t			Multiboot_Drives[255];
930b879f1cSjmmv static struct multiboot_info	Multiboot_Info;
94f00c5400Sthorpej static bool			Multiboot_Loader = false;
950b879f1cSjmmv static char			Multiboot_Loader_Name[255];
960b879f1cSjmmv static uint8_t			Multiboot_Mmap[1024];
978d30a464Sjmmv static struct multiboot_symbols	Multiboot_Symbols;
980b879f1cSjmmv 
990b879f1cSjmmv /* --------------------------------------------------------------------- */
1000b879f1cSjmmv 
1010b879f1cSjmmv /*
1020b879f1cSjmmv  * Prototypes for private functions.
1030b879f1cSjmmv  */
1040b879f1cSjmmv static void	bootinfo_add(struct btinfo_common *, int, int);
1050b879f1cSjmmv static void	copy_syms(struct multiboot_info *);
1060b879f1cSjmmv static void	setup_biosgeom(struct multiboot_info *);
1070b879f1cSjmmv static void	setup_bootdisk(struct multiboot_info *);
1080b879f1cSjmmv static void	setup_bootpath(struct multiboot_info *);
1090b879f1cSjmmv static void	setup_console(struct multiboot_info *);
1100b879f1cSjmmv static void	setup_howto(struct multiboot_info *);
1110b879f1cSjmmv static void	setup_memory(struct multiboot_info *);
1120b879f1cSjmmv static void	setup_memmap(struct multiboot_info *);
1130b879f1cSjmmv 
1140b879f1cSjmmv /* --------------------------------------------------------------------- */
1150b879f1cSjmmv 
1160b879f1cSjmmv /*
1170b879f1cSjmmv  * Sets up the kernel if it was booted by a Multiboot-compliant boot
1180b879f1cSjmmv  * loader.  This is executed before the kernel has relocated itself.
1190b879f1cSjmmv  * The main purpose of this function is to copy all the information
1200b879f1cSjmmv  * passed in by the boot loader to a safe place, so that it is available
1210b879f1cSjmmv  * after it has been relocated.
1220b879f1cSjmmv  *
1230b879f1cSjmmv  * WARNING: Because the kernel has not yet relocated itself to KERNBASE,
1240b879f1cSjmmv  * special care has to be taken when accessing memory because absolute
1250b879f1cSjmmv  * addresses (referring to kernel symbols) do not work.  So:
1260b879f1cSjmmv  *
1270b879f1cSjmmv  *     1) Avoid jumps to absolute addresses (such as gotos and switches).
1280b879f1cSjmmv  *     2) To access global variables use their physical address, which
1290b879f1cSjmmv  *        can be obtained using the RELOC macro.
1300b879f1cSjmmv  */
1310b879f1cSjmmv void
multiboot1_pre_reloc(struct multiboot_info * mi)132*43108a21Smanu multiboot1_pre_reloc(struct multiboot_info *mi)
1330b879f1cSjmmv {
1340b879f1cSjmmv #define RELOC(type, x) ((type)((vaddr_t)(x) - KERNBASE))
1350b879f1cSjmmv 	struct multiboot_info *midest =
1360b879f1cSjmmv 	    RELOC(struct multiboot_info *, &Multiboot_Info);
1370b879f1cSjmmv 
138f00c5400Sthorpej 	*RELOC(bool *, &Multiboot_Loader) = true;
1390b879f1cSjmmv 	memcpy(midest, mi, sizeof(Multiboot_Info));
1400b879f1cSjmmv 
1410b879f1cSjmmv 	if (mi->mi_flags & MULTIBOOT_INFO_HAS_CMDLINE) {
1420b879f1cSjmmv 		strncpy(RELOC(void *, Multiboot_Cmdline), mi->mi_cmdline,
1430b879f1cSjmmv 		    sizeof(Multiboot_Cmdline));
1440b879f1cSjmmv 		midest->mi_cmdline = (char *)&Multiboot_Cmdline;
1450b879f1cSjmmv 	}
1460b879f1cSjmmv 
1470b879f1cSjmmv 	if (mi->mi_flags & MULTIBOOT_INFO_HAS_LOADER_NAME) {
1480b879f1cSjmmv 		strncpy(RELOC(void *, Multiboot_Loader_Name),
1490b879f1cSjmmv 		    mi->mi_loader_name, sizeof(Multiboot_Loader_Name));
1500b879f1cSjmmv 		midest->mi_loader_name = (char *)&Multiboot_Loader_Name;
1510b879f1cSjmmv 	}
1520b879f1cSjmmv 
1530b879f1cSjmmv 	if (mi->mi_flags & MULTIBOOT_INFO_HAS_MMAP) {
1540b879f1cSjmmv 		memcpy(RELOC(void *, Multiboot_Mmap),
1550b879f1cSjmmv 		    (void *)mi->mi_mmap_addr, mi->mi_mmap_length);
1560b879f1cSjmmv 		midest->mi_mmap_addr = (vaddr_t)&Multiboot_Mmap;
1570b879f1cSjmmv 	}
1580b879f1cSjmmv 
1590b879f1cSjmmv 	if (mi->mi_flags & MULTIBOOT_INFO_HAS_DRIVES) {
1600b879f1cSjmmv 		memcpy(RELOC(void *, Multiboot_Drives),
1610b879f1cSjmmv 		    (void *)mi->mi_drives_addr, mi->mi_drives_length);
1620b879f1cSjmmv 		midest->mi_drives_addr = (vaddr_t)&Multiboot_Drives;
1630b879f1cSjmmv 	}
1640b879f1cSjmmv 
1650b879f1cSjmmv 	copy_syms(mi);
1660b879f1cSjmmv #undef RELOC
1670b879f1cSjmmv }
1680b879f1cSjmmv 
1690b879f1cSjmmv /* --------------------------------------------------------------------- */
1700b879f1cSjmmv 
1710b879f1cSjmmv /*
1720b879f1cSjmmv  * Sets up the kernel if it was booted by a Multiboot-compliant boot
1730b879f1cSjmmv  * loader.  This is executed just after the kernel has relocated itself.
1740b879f1cSjmmv  * At this point, executing any kind of code is safe, keeping in mind
1750b879f1cSjmmv  * that no devices have been initialized yet (not even the console!).
1760b879f1cSjmmv  */
1770b879f1cSjmmv void
multiboot1_post_reloc(void)178*43108a21Smanu multiboot1_post_reloc(void)
1790b879f1cSjmmv {
1800b879f1cSjmmv 	struct multiboot_info *mi;
1810b879f1cSjmmv 
1820b879f1cSjmmv 	if (! Multiboot_Loader)
1830b879f1cSjmmv 		return;
1840b879f1cSjmmv 
1850b879f1cSjmmv 	mi = &Multiboot_Info;
1860b879f1cSjmmv 	bootinfo.bi_nentries = 0;
1870b879f1cSjmmv 
1880b879f1cSjmmv 	setup_memory(mi);
1890b879f1cSjmmv 	setup_console(mi);
1900b879f1cSjmmv 	setup_howto(mi);
1910b879f1cSjmmv 	setup_bootpath(mi);
1920b879f1cSjmmv 	setup_biosgeom(mi);
1936cf8f1a5Sjmmv 	setup_bootdisk(mi);
1940b879f1cSjmmv 	setup_memmap(mi);
1950b879f1cSjmmv }
1960b879f1cSjmmv 
1970b879f1cSjmmv /* --------------------------------------------------------------------- */
1980b879f1cSjmmv 
1990b879f1cSjmmv /*
2000b879f1cSjmmv  * Prints a summary of the information collected in the Multiboot
2010b879f1cSjmmv  * information header (if present).  Done as a separate function because
2020b879f1cSjmmv  * the console has to be available.
2030b879f1cSjmmv  */
2040b879f1cSjmmv void
multiboot1_print_info(void)205*43108a21Smanu multiboot1_print_info(void)
2060b879f1cSjmmv {
2070b879f1cSjmmv 	struct multiboot_info *mi = &Multiboot_Info;
2088d30a464Sjmmv 	struct multiboot_symbols *ms = &Multiboot_Symbols;
2090b879f1cSjmmv 
2100b879f1cSjmmv 	if (! Multiboot_Loader)
2110b879f1cSjmmv 		return;
2120b879f1cSjmmv 
2130b879f1cSjmmv 	printf("multiboot: Information structure flags: 0x%08x\n",
2140b879f1cSjmmv 	    mi->mi_flags);
2150b879f1cSjmmv 
2160b879f1cSjmmv 	if (mi->mi_flags & MULTIBOOT_INFO_HAS_LOADER_NAME)
2170b879f1cSjmmv 		printf("multiboot: Boot loader: %s\n", mi->mi_loader_name);
2180b879f1cSjmmv 
2190b879f1cSjmmv 	if (mi->mi_flags & MULTIBOOT_INFO_HAS_CMDLINE)
2200b879f1cSjmmv 		printf("multiboot: Command line: %s\n", mi->mi_cmdline);
2210b879f1cSjmmv 
2220b879f1cSjmmv 	if (mi->mi_flags & MULTIBOOT_INFO_HAS_MEMORY)
2230b879f1cSjmmv 		printf("multiboot: %u KB lower memory, %u KB upper memory\n",
2240b879f1cSjmmv 		    mi->mi_mem_lower, mi->mi_mem_upper);
2250b879f1cSjmmv 
2268d30a464Sjmmv 	if (mi->mi_flags & MULTIBOOT_INFO_HAS_ELF_SYMS) {
2278d30a464Sjmmv 		KASSERT(esym != 0);
2288d30a464Sjmmv 		printf("multiboot: Symbol table at %p, length %d bytes\n",
2298d30a464Sjmmv 		    ms->s_symstart, ms->s_symsize);
2308d30a464Sjmmv 		printf("multiboot: String table at %p, length %d bytes\n",
2318d30a464Sjmmv 		    ms->s_strstart, ms->s_strsize);
2328d30a464Sjmmv 	}
2330b879f1cSjmmv }
2340b879f1cSjmmv 
2350b879f1cSjmmv /* --------------------------------------------------------------------- */
2360b879f1cSjmmv 
2370b879f1cSjmmv /*
2380b879f1cSjmmv  * Adds the bootinfo entry given in 'item' to the bootinfo tables.
2390b879f1cSjmmv  * Sets the item type to 'type' and its length to 'len'.
2400b879f1cSjmmv  */
2410b879f1cSjmmv static void
bootinfo_add(struct btinfo_common * item,int type,int len)2420b879f1cSjmmv bootinfo_add(struct btinfo_common *item, int type, int len)
2430b879f1cSjmmv {
2440b879f1cSjmmv 	int i;
2450b879f1cSjmmv 	struct bootinfo *bip = (struct bootinfo *)&bootinfo;
2460b879f1cSjmmv 	vaddr_t data;
2470b879f1cSjmmv 
2480b879f1cSjmmv 	item->type = type;
2490b879f1cSjmmv 	item->len = len;
2500b879f1cSjmmv 
2510b879f1cSjmmv 	data = (vaddr_t)&bip->bi_data;
2520b879f1cSjmmv 	for (i = 0; i < bip->bi_nentries; i++) {
2530b879f1cSjmmv 		struct btinfo_common *tmp;
2540b879f1cSjmmv 
2550b879f1cSjmmv 		tmp = (struct btinfo_common *)data;
2560b879f1cSjmmv 		data += tmp->len;
2570b879f1cSjmmv 	}
2580b879f1cSjmmv 	if (data + len < (vaddr_t)&bip->bi_data + sizeof(bip->bi_data)) {
2590b879f1cSjmmv 		memcpy((void *)data, item, len);
2600b879f1cSjmmv 		bip->bi_nentries++;
2610b879f1cSjmmv 	}
2620b879f1cSjmmv }
2630b879f1cSjmmv 
2640b879f1cSjmmv /* --------------------------------------------------------------------- */
2650b879f1cSjmmv 
2660b879f1cSjmmv /*
2670b879f1cSjmmv  * Copies the symbol table and the strings table passed in by the boot
2680b879f1cSjmmv  * loader after the kernel's image, and sets up 'esym' accordingly so
2690b879f1cSjmmv  * that this data is properly copied into upper memory during relocation.
2700b879f1cSjmmv  *
2710b879f1cSjmmv  * WARNING: This code runs before the kernel has relocated itself.  See
272*43108a21Smanu  * the note in multiboot1_pre_reloc() for more information.
2730b879f1cSjmmv  */
2740b879f1cSjmmv static void
copy_syms(struct multiboot_info * mi)2750b879f1cSjmmv copy_syms(struct multiboot_info *mi)
2760b879f1cSjmmv {
2770b879f1cSjmmv #define RELOC(type, x) ((type)((vaddr_t)(x) - KERNBASE))
2780b879f1cSjmmv 	int i;
2798d30a464Sjmmv 	struct multiboot_symbols *ms;
280d9474223Sjym 	Elf32_Shdr *symtabp, *strtabp;
281d9474223Sjym 	Elf32_Word symsize, strsize;
282d9474223Sjym 	Elf32_Addr symaddr, straddr;
283d9474223Sjym 	Elf32_Addr symstart, strstart;
2840b879f1cSjmmv 
2850b879f1cSjmmv 	/*
2860b879f1cSjmmv 	 * Check if the Multiboot information header has symbols or not.
2870b879f1cSjmmv 	 */
2880b879f1cSjmmv 	if (!(mi->mi_flags & MULTIBOOT_INFO_HAS_ELF_SYMS))
2890b879f1cSjmmv 		return;
2900b879f1cSjmmv 
2918d30a464Sjmmv 	ms = RELOC(struct multiboot_symbols *, &Multiboot_Symbols);
2928d30a464Sjmmv 
2930b879f1cSjmmv 	/*
2940b879f1cSjmmv 	 * Locate a symbol table and its matching string table in the
2950b879f1cSjmmv 	 * section headers passed in by the boot loader.  Set 'symtabp'
2960b879f1cSjmmv 	 * and 'strtabp' with pointers to the matching entries.
2970b879f1cSjmmv 	 */
2980b879f1cSjmmv 	symtabp = strtabp = NULL;
2990b879f1cSjmmv 	for (i = 0; i < mi->mi_elfshdr_num && symtabp == NULL &&
3000b879f1cSjmmv 	    strtabp == NULL; i++) {
3010b879f1cSjmmv 		Elf32_Shdr *shdrp;
3020b879f1cSjmmv 
3030b879f1cSjmmv 		shdrp = &((Elf32_Shdr *)mi->mi_elfshdr_addr)[i];
3040b879f1cSjmmv 
30548082eb6Smanu 		if ((shdrp->sh_type == SHT_SYMTAB) &&
3060b879f1cSjmmv 		    shdrp->sh_link != SHN_UNDEF) {
3070b879f1cSjmmv 			Elf32_Shdr *shdrp2;
3080b879f1cSjmmv 
3090b879f1cSjmmv 			shdrp2 = &((Elf32_Shdr *)mi->mi_elfshdr_addr)
3100b879f1cSjmmv 			    [shdrp->sh_link];
3110b879f1cSjmmv 
31248082eb6Smanu 			if (shdrp2->sh_type == SHT_STRTAB) {
3130b879f1cSjmmv 				symtabp = shdrp;
3140b879f1cSjmmv 				strtabp = shdrp2;
3150b879f1cSjmmv 			}
3160b879f1cSjmmv 		}
3170b879f1cSjmmv 	}
3180b879f1cSjmmv 	if (symtabp == NULL || strtabp == NULL)
3190b879f1cSjmmv 		return;
3200b879f1cSjmmv 
3218d30a464Sjmmv 	symaddr = symtabp->sh_addr;
3228d30a464Sjmmv 	straddr = strtabp->sh_addr;
3238d30a464Sjmmv 	symsize = symtabp->sh_size;
3248d30a464Sjmmv 	strsize = strtabp->sh_size;
3250b879f1cSjmmv 
3260b879f1cSjmmv 	/*
3278d30a464Sjmmv 	 * Copy the symbol and string tables just after the kernel's
3288d30a464Sjmmv 	 * end address, in this order.  Only the contents of these ELF
3298d30a464Sjmmv 	 * sections are copied; headers are discarded.  esym is later
3308d30a464Sjmmv 	 * updated to point to the lowest "free" address after the tables
3318d30a464Sjmmv 	 * so that they are mapped appropriately when enabling paging.
3320b879f1cSjmmv 	 *
3338d30a464Sjmmv 	 * We need to be careful to not overwrite valid data doing the
3348d30a464Sjmmv 	 * copies, hence all the different cases below.  We can assume
3358d30a464Sjmmv 	 * that if the tables start before the kernel's end address,
3368d30a464Sjmmv 	 * they will not grow over this address.
3370b879f1cSjmmv 	 */
338d9474223Sjym         if ((void *)symtabp < RELOC(void *, &end) &&
339d9474223Sjym 	    (void *)strtabp < RELOC(void *, &end)) {
340d9474223Sjym 		symstart = RELOC(Elf32_Addr, &end);
3418d30a464Sjmmv 		strstart = symstart + symsize;
342542717e7Sgsutre 		memcpy((void *)symstart, (void *)symaddr, symsize);
343542717e7Sgsutre 		memcpy((void *)strstart, (void *)straddr, strsize);
344d9474223Sjym         } else if ((void *)symtabp > RELOC(void *, &end) &&
345d9474223Sjym 	           (void *)strtabp < RELOC(void *, &end)) {
346d9474223Sjym 		symstart = RELOC(Elf32_Addr, &end);
3478d30a464Sjmmv 		strstart = symstart + symsize;
348542717e7Sgsutre 		memcpy((void *)symstart, (void *)symaddr, symsize);
349542717e7Sgsutre 		memcpy((void *)strstart, (void *)straddr, strsize);
350d9474223Sjym         } else if ((void *)symtabp < RELOC(void *, &end) &&
351d9474223Sjym 	           (void *)strtabp > RELOC(void *, &end)) {
352d9474223Sjym 		strstart = RELOC(Elf32_Addr, &end);
3538d30a464Sjmmv 		symstart = strstart + strsize;
354542717e7Sgsutre 		memcpy((void *)strstart, (void *)straddr, strsize);
355542717e7Sgsutre 		memcpy((void *)symstart, (void *)symaddr, symsize);
3568d30a464Sjmmv 	} else {
3578d30a464Sjmmv 		/* symtabp and strtabp are both over end */
358d9474223Sjym 		if (symtabp < strtabp) {
359d9474223Sjym 			symstart = RELOC(Elf32_Addr, &end);
3608d30a464Sjmmv 			strstart = symstart + symsize;
361542717e7Sgsutre 			memcpy((void *)symstart, (void *)symaddr, symsize);
362542717e7Sgsutre 			memcpy((void *)strstart, (void *)straddr, strsize);
3638d30a464Sjmmv 		} else {
364d9474223Sjym 			strstart = RELOC(Elf32_Addr, &end);
3658d30a464Sjmmv 			symstart = strstart + strsize;
3668d30a464Sjmmv 			memcpy((void *)strstart, (void *)straddr, strsize);
3678d30a464Sjmmv 			memcpy((void *)symstart, (void *)symaddr, symsize);
368542717e7Sgsutre 		}
369542717e7Sgsutre 	}
370d9474223Sjym 
371bf266e55Sjmmv 	*RELOC(int *, &esym) =
372bf266e55Sjmmv 	    (int)(symstart + symsize + strsize + KERNBASE);
3730b879f1cSjmmv 
37453524e44Schristos 	ms->s_symstart = (void *)(symstart + KERNBASE);
3758d30a464Sjmmv 	ms->s_symsize  = symsize;
37653524e44Schristos 	ms->s_strstart = (void *)(strstart + KERNBASE);
3778d30a464Sjmmv 	ms->s_strsize  = strsize;
3780b879f1cSjmmv #undef RELOC
3790b879f1cSjmmv }
3800b879f1cSjmmv 
3810b879f1cSjmmv /* --------------------------------------------------------------------- */
3820b879f1cSjmmv 
3830b879f1cSjmmv /*
3840b879f1cSjmmv  * Sets up the biosgeom bootinfo structure if the Multiboot information
3850b879f1cSjmmv  * structure provides information about disk drives.
3860b879f1cSjmmv  */
3870b879f1cSjmmv static void
setup_biosgeom(struct multiboot_info * mi)3880b879f1cSjmmv setup_biosgeom(struct multiboot_info *mi)
3890b879f1cSjmmv {
3900b879f1cSjmmv 	size_t pos;
3910b879f1cSjmmv 	uint8_t bidata[1024];
3920b879f1cSjmmv 	struct btinfo_biosgeom *bi;
3930b879f1cSjmmv 
3940b879f1cSjmmv 	if (!(mi->mi_flags & MULTIBOOT_INFO_HAS_DRIVES))
3950b879f1cSjmmv 		return;
3960b879f1cSjmmv 
3970b879f1cSjmmv 	memset(bidata, 0, sizeof(bidata));
3980b879f1cSjmmv 	bi = (struct btinfo_biosgeom *)bidata;
3990b879f1cSjmmv 	pos = 0;
4000b879f1cSjmmv 
4010b879f1cSjmmv 	while (pos < mi->mi_drives_length) {
4020b879f1cSjmmv 		struct multiboot_drive *md;
4030b879f1cSjmmv 		struct bi_biosgeom_entry bbe;
4040b879f1cSjmmv 
4050b879f1cSjmmv 		md = (struct multiboot_drive *)
4060b879f1cSjmmv 		    &((uint8_t *)mi->mi_drives_addr)[pos];
4070b879f1cSjmmv 
4080b879f1cSjmmv 		memset(&bbe, 0, sizeof(bbe));
4090b879f1cSjmmv 		bbe.sec = md->md_sectors;
4100b879f1cSjmmv 		bbe.head = md->md_heads;
4110b879f1cSjmmv 		bbe.cyl = md->md_cylinders;
4120b879f1cSjmmv 		bbe.dev = md->md_number;
4130b879f1cSjmmv 
4140b879f1cSjmmv 		memcpy(&bi->disk[bi->num], &bbe, sizeof(bbe));
4150b879f1cSjmmv 		bi->num++;
4160b879f1cSjmmv 
4170b879f1cSjmmv 		pos += md->md_length;
4180b879f1cSjmmv 	}
4190b879f1cSjmmv 
4200b879f1cSjmmv 	bootinfo_add((struct btinfo_common *)bi, BTINFO_BIOSGEOM,
4210b879f1cSjmmv 	    sizeof(struct btinfo_biosgeom) +
4220b879f1cSjmmv 	    bi->num * sizeof(struct bi_biosgeom_entry));
4230b879f1cSjmmv }
4240b879f1cSjmmv 
4250b879f1cSjmmv /* --------------------------------------------------------------------- */
4260b879f1cSjmmv 
4270b879f1cSjmmv /*
4280b879f1cSjmmv  * Sets up the default root device if the Multiboot information
4290b879f1cSjmmv  * structure provides information about the boot drive (where the kernel
4300b879f1cSjmmv  * image was loaded from) or if the user gave a 'root' parameter on the
4310b879f1cSjmmv  * boot command line.
4320b879f1cSjmmv  */
4330b879f1cSjmmv static void
setup_bootdisk(struct multiboot_info * mi)4340b879f1cSjmmv setup_bootdisk(struct multiboot_info *mi)
4350b879f1cSjmmv {
436712239e3Sthorpej 	bool found;
4370b879f1cSjmmv 	struct btinfo_rootdevice bi;
4380b879f1cSjmmv 
439f00c5400Sthorpej 	found = false;
4400b879f1cSjmmv 
4410b879f1cSjmmv 	if (mi->mi_flags & MULTIBOOT_INFO_HAS_CMDLINE)
4420b879f1cSjmmv 		found = optstr_get(mi->mi_cmdline, "root", bi.devname,
4430b879f1cSjmmv 		    sizeof(bi.devname));
4440b879f1cSjmmv 
4450b879f1cSjmmv 	if (!found && (mi->mi_flags & MULTIBOOT_INFO_HAS_BOOT_DEVICE)) {
4460b879f1cSjmmv 		const char *devprefix;
4470b879f1cSjmmv 
4489485c8d2Sjmmv 		/* Attempt to match the BIOS boot disk to a device.  There
4499485c8d2Sjmmv 		 * is not much we can do to get it right.  (Well, strictly
4509485c8d2Sjmmv 		 * speaking, we could, but it is certainly not worth the
4519485c8d2Sjmmv 		 * extra effort.) */
4520b879f1cSjmmv 		switch (mi->mi_boot_device_drive) {
4530b879f1cSjmmv 		case 0x00:	devprefix = "fd0";	break;
4540b879f1cSjmmv 		case 0x01:	devprefix = "fd1";	break;
4556cf8f1a5Sjmmv 		case 0x80:	devprefix = "wd0";	break;
4566cf8f1a5Sjmmv 		case 0x81:	devprefix = "wd1";	break;
4576cf8f1a5Sjmmv 		case 0x82:	devprefix = "wd2";	break;
4586cf8f1a5Sjmmv 		case 0x83:	devprefix = "wd3";	break;
4599485c8d2Sjmmv 		default:	devprefix = "wd0";
4600b879f1cSjmmv 		}
4610b879f1cSjmmv 
4620b879f1cSjmmv 		strcpy(bi.devname, devprefix);
4630b879f1cSjmmv 		if (mi->mi_boot_device_part2 != 0xFF)
4640b879f1cSjmmv 			bi.devname[3] = mi->mi_boot_device_part2 + 'a';
4650b879f1cSjmmv 		else
4660b879f1cSjmmv 			bi.devname[3] = 'a';
4670b879f1cSjmmv 		bi.devname[4] = '\0';
4680b879f1cSjmmv 
469f00c5400Sthorpej 		found = true;
4700b879f1cSjmmv 	}
4710b879f1cSjmmv 
4720b879f1cSjmmv 	if (found) {
4730b879f1cSjmmv 		bootinfo_add((struct btinfo_common *)&bi, BTINFO_ROOTDEVICE,
4740b879f1cSjmmv 		    sizeof(struct btinfo_rootdevice));
4750b879f1cSjmmv 	}
4760b879f1cSjmmv }
4770b879f1cSjmmv 
4780b879f1cSjmmv /* --------------------------------------------------------------------- */
4790b879f1cSjmmv 
4800b879f1cSjmmv /*
4810b879f1cSjmmv  * Sets up the bootpath bootinfo structure with an appropriate kernel
4820b879f1cSjmmv  * name derived from the boot command line.  The Multiboot information
4830b879f1cSjmmv  * structure does not provide this detail directly, so we try to derive
4840b879f1cSjmmv  * it from the command line setting.
4850b879f1cSjmmv  */
4860b879f1cSjmmv static void
setup_bootpath(struct multiboot_info * mi)4870b879f1cSjmmv setup_bootpath(struct multiboot_info *mi)
4880b879f1cSjmmv {
4890b879f1cSjmmv 	struct btinfo_bootpath bi;
4900b879f1cSjmmv 	char *cl, *cl2, old;
4910b879f1cSjmmv 	int len;
4920b879f1cSjmmv 
4930b879f1cSjmmv 	if (strncmp(Multiboot_Loader_Name, "GNU GRUB ",
4940b879f1cSjmmv 	    sizeof(Multiboot_Loader_Name)) > 0) {
4950b879f1cSjmmv 		cl = mi->mi_cmdline;
4960b879f1cSjmmv 		while (*cl != '\0' && *cl != '/')
4970b879f1cSjmmv 			cl++;
4980b879f1cSjmmv 		cl2 = cl;
4990b879f1cSjmmv 		len = 0;
5000b879f1cSjmmv 		while (*cl2 != '\0' && *cl2 != ' ') {
5010b879f1cSjmmv 			len++;
5020b879f1cSjmmv 			cl2++;
5030b879f1cSjmmv 		}
5040b879f1cSjmmv 
5050b879f1cSjmmv 		old = *cl2;
5060b879f1cSjmmv 		*cl2 = '\0';
5070b879f1cSjmmv 		memcpy(bi.bootpath, cl, MIN(sizeof(bi.bootpath), len));
5080b879f1cSjmmv 		*cl2 = old;
5093c18863aSmsaitoh 		bi.bootpath[MIN(sizeof(bi.bootpath) - 1, len)] = '\0';
5100b879f1cSjmmv 
5110b879f1cSjmmv 		bootinfo_add((struct btinfo_common *)&bi, BTINFO_BOOTPATH,
5120b879f1cSjmmv 		    sizeof(struct btinfo_bootpath));
5130b879f1cSjmmv 	}
5140b879f1cSjmmv }
5150b879f1cSjmmv 
5160b879f1cSjmmv /* --------------------------------------------------------------------- */
5170b879f1cSjmmv 
5180b879f1cSjmmv /*
5190b879f1cSjmmv  * Sets up the console bootinfo structure if the user gave a 'console'
5200b879f1cSjmmv  * argument on the boot command line.  The Multiboot information
5210b879f1cSjmmv  * structure gives no hint about this, so the only way to know where the
52238106bafSmsaitoh  * console is to let the user specify it.
5230b879f1cSjmmv  *
5240b879f1cSjmmv  * If there wasn't any 'console' argument, this does not generate any
5250b879f1cSjmmv  * bootinfo entry, falling back to the kernel's default console.
5260b879f1cSjmmv  *
5270b879f1cSjmmv  * If there weren't any of 'console_speed' or 'console_addr' arguments,
5280b879f1cSjmmv  * this falls back to the default values for the serial port.
5290b879f1cSjmmv  */
5300b879f1cSjmmv static void
setup_console(struct multiboot_info * mi)5310b879f1cSjmmv setup_console(struct multiboot_info *mi)
5320b879f1cSjmmv {
5330b879f1cSjmmv 	struct btinfo_console bi;
534712239e3Sthorpej 	bool found;
5350b879f1cSjmmv 
536f00c5400Sthorpej 	found = false;
5370b879f1cSjmmv 
5380b879f1cSjmmv 	if (mi->mi_flags & MULTIBOOT_INFO_HAS_CMDLINE)
5390b879f1cSjmmv 		found = optstr_get(mi->mi_cmdline, "console", bi.devname,
5400b879f1cSjmmv 		    sizeof(bi.devname));
5410b879f1cSjmmv 
5420b879f1cSjmmv 	if (found) {
543a0228be4Sjmmv 		bool valid;
544a0228be4Sjmmv 
5450b879f1cSjmmv 		if (strncmp(bi.devname, "com", sizeof(bi.devname)) == 0) {
5460b879f1cSjmmv 			char tmp[10];
5470b879f1cSjmmv 
5480b879f1cSjmmv 			found = optstr_get(mi->mi_cmdline, "console_speed",
5490b879f1cSjmmv 			    tmp, sizeof(tmp));
5500b879f1cSjmmv 			if (found)
5510b879f1cSjmmv 				bi.speed = strtoul(tmp, NULL, 10);
5520b879f1cSjmmv 			else
5530b879f1cSjmmv 				bi.speed = 0; /* Use default speed. */
5540b879f1cSjmmv 
5550b879f1cSjmmv 			found = optstr_get(mi->mi_cmdline, "console_addr",
5560b879f1cSjmmv 			    tmp, sizeof(tmp));
5570b879f1cSjmmv 			if (found) {
5580b879f1cSjmmv 				if (tmp[0] == '0' && tmp[1] == 'x')
5590b879f1cSjmmv 					bi.addr = strtoul(tmp + 2, NULL, 16);
5600b879f1cSjmmv 				else
5610b879f1cSjmmv 					bi.addr = strtoul(tmp, NULL, 10);
5620b879f1cSjmmv 			} else
5630b879f1cSjmmv 				bi.addr = 0; /* Use default address. */
5640b879f1cSjmmv 
565a0228be4Sjmmv 			valid = true;
566a0228be4Sjmmv 		} else if (strncmp(bi.devname, "pc", sizeof(bi.devname)) == 0)
567a0228be4Sjmmv 			valid = true;
568a0228be4Sjmmv 		else
569a0228be4Sjmmv 			valid = false;
570a0228be4Sjmmv 
571a0228be4Sjmmv 		if (valid)
572a0228be4Sjmmv 			bootinfo_add((struct btinfo_common *)&bi,
573a0228be4Sjmmv 			    BTINFO_CONSOLE, sizeof(struct btinfo_console));
5740b879f1cSjmmv 	}
5750b879f1cSjmmv }
5760b879f1cSjmmv 
5770b879f1cSjmmv /* --------------------------------------------------------------------- */
5780b879f1cSjmmv 
5790b879f1cSjmmv /*
5800b879f1cSjmmv  * Sets up the 'boothowto' variable based on the options given in the
5810b879f1cSjmmv  * boot command line, if any.
5820b879f1cSjmmv  */
5830b879f1cSjmmv static void
setup_howto(struct multiboot_info * mi)5840b879f1cSjmmv setup_howto(struct multiboot_info *mi)
5850b879f1cSjmmv {
5860b879f1cSjmmv 	char *cl;
5870b879f1cSjmmv 
5880b879f1cSjmmv 	if (!(mi->mi_flags & MULTIBOOT_INFO_HAS_CMDLINE))
5890b879f1cSjmmv 		return;
5900b879f1cSjmmv 
5910b879f1cSjmmv 	cl = mi->mi_cmdline;
5920b879f1cSjmmv 
5930b879f1cSjmmv 	/* Skip kernel file name. */
5940b879f1cSjmmv 	while (*cl != '\0' && *cl != ' ')
5950b879f1cSjmmv 		cl++;
5965959d669Sdholland 	while (*cl == ' ')
5970b879f1cSjmmv 		cl++;
5980b879f1cSjmmv 
5990b879f1cSjmmv 	/* Check if there are flags and set 'howto' accordingly. */
6000b879f1cSjmmv 	if (*cl == '-') {
6010b879f1cSjmmv 		int howto = 0;
6020b879f1cSjmmv 
6030b879f1cSjmmv 		cl++;
6040b879f1cSjmmv 		while (*cl != '\0' && *cl != ' ') {
6050b879f1cSjmmv 			BOOT_FLAG(*cl, howto);
6060b879f1cSjmmv 			cl++;
6070b879f1cSjmmv 		}
6080b879f1cSjmmv 		if (*cl == ' ')
6090b879f1cSjmmv 			cl++;
6100b879f1cSjmmv 
6110b879f1cSjmmv 		boothowto = howto;
6120b879f1cSjmmv 	}
6130b879f1cSjmmv }
6140b879f1cSjmmv 
6150b879f1cSjmmv /* --------------------------------------------------------------------- */
6160b879f1cSjmmv 
6170b879f1cSjmmv /*
6180b879f1cSjmmv  * Sets up the memmap bootinfo structure to describe available memory as
6190b879f1cSjmmv  * given by the BIOS.
6200b879f1cSjmmv  */
6210b879f1cSjmmv static void
setup_memmap(struct multiboot_info * mi)6220b879f1cSjmmv setup_memmap(struct multiboot_info *mi)
6230b879f1cSjmmv {
6240b879f1cSjmmv 	char data[1024];
6250b879f1cSjmmv 	size_t i;
6260b879f1cSjmmv 	struct btinfo_memmap *bi;
6270b879f1cSjmmv 
6280b879f1cSjmmv 	if (!(mi->mi_flags & MULTIBOOT_INFO_HAS_MMAP))
6290b879f1cSjmmv 		return;
6300b879f1cSjmmv 
6310b879f1cSjmmv 	bi = (struct btinfo_memmap *)data;
6320b879f1cSjmmv 	bi->num = 0;
6330b879f1cSjmmv 
6340b879f1cSjmmv 	i = 0;
6350b879f1cSjmmv 	while (i < mi->mi_mmap_length) {
6360b879f1cSjmmv 		struct multiboot_mmap *mm;
6370b879f1cSjmmv 		struct bi_memmap_entry *bie;
6380b879f1cSjmmv 
6390b879f1cSjmmv 		bie = &bi->entry[bi->num];
6400b879f1cSjmmv 
6410b879f1cSjmmv 		mm = (struct multiboot_mmap *)(mi->mi_mmap_addr + i);
6420b879f1cSjmmv 		bie->addr = mm->mm_base_addr;
6430b879f1cSjmmv 		bie->size = mm->mm_length;
6440b879f1cSjmmv 		if (mm->mm_type == 1)
6450b879f1cSjmmv 			bie->type = BIM_Memory;
6460b879f1cSjmmv 		else
6470b879f1cSjmmv 			bie->type = BIM_Reserved;
6480b879f1cSjmmv 
6490b879f1cSjmmv 		bi->num++;
6500b879f1cSjmmv 		i += mm->mm_size + 4;
6510b879f1cSjmmv 	}
6520b879f1cSjmmv 
6530b879f1cSjmmv 	bootinfo_add((struct btinfo_common *)bi, BTINFO_MEMMAP,
6540b879f1cSjmmv 	    sizeof(data));
6550b879f1cSjmmv }
6560b879f1cSjmmv 
6570b879f1cSjmmv /* --------------------------------------------------------------------- */
6580b879f1cSjmmv 
6590b879f1cSjmmv /*
6600b879f1cSjmmv  * Sets up the 'biosbasemem' and 'biosextmem' variables if the
6610b879f1cSjmmv  * Multiboot information structure provides information about memory.
6620b879f1cSjmmv  */
6630b879f1cSjmmv static void
setup_memory(struct multiboot_info * mi)6640b879f1cSjmmv setup_memory(struct multiboot_info *mi)
6650b879f1cSjmmv {
66695543a8eSmrg 
6670b879f1cSjmmv 	if (!(mi->mi_flags & MULTIBOOT_INFO_HAS_MEMORY))
6680b879f1cSjmmv 		return;
6690b879f1cSjmmv 
67095543a8eSmrg 	/* Make sure we don't override user-set variables. */
67195543a8eSmrg 	if (biosbasemem == 0) {
6720b879f1cSjmmv 		biosbasemem = mi->mi_mem_lower;
67395543a8eSmrg 		biosmem_implicit = 1;
67495543a8eSmrg 	}
67595543a8eSmrg 	if (biosextmem == 0) {
6760b879f1cSjmmv 		biosextmem = mi->mi_mem_upper;
67795543a8eSmrg 		biosmem_implicit = 1;
67895543a8eSmrg 	}
6790b879f1cSjmmv }
6800b879f1cSjmmv 
6810b879f1cSjmmv /* --------------------------------------------------------------------- */
6820b879f1cSjmmv 
6830b879f1cSjmmv /*
6848d30a464Sjmmv  * Sets up the initial kernel symbol table.  Returns true if this was
6858d30a464Sjmmv  * passed in by Multiboot; false otherwise.
6860b879f1cSjmmv  */
687712239e3Sthorpej bool
multiboot1_ksyms_addsyms_elf(void)688*43108a21Smanu multiboot1_ksyms_addsyms_elf(void)
6890b879f1cSjmmv {
6908d30a464Sjmmv 	struct multiboot_info *mi = &Multiboot_Info;
6918d30a464Sjmmv 	struct multiboot_symbols *ms = &Multiboot_Symbols;
6920b879f1cSjmmv 
693*43108a21Smanu 	if (! Multiboot_Loader)
694*43108a21Smanu 		return false;
695*43108a21Smanu 
6968d30a464Sjmmv 	if (mi->mi_flags & MULTIBOOT_INFO_HAS_ELF_SYMS) {
697c3429f80Sjmmv 		Elf32_Ehdr ehdr;
698c3429f80Sjmmv 
6998d30a464Sjmmv 		KASSERT(esym != 0);
7000b879f1cSjmmv 
701c3429f80Sjmmv 		memcpy(ehdr.e_ident, ELFMAG, SELFMAG);
702c3429f80Sjmmv 		ehdr.e_ident[EI_CLASS] = ELFCLASS32;
703c3429f80Sjmmv 		ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
704c3429f80Sjmmv 		ehdr.e_ident[EI_VERSION] = EV_CURRENT;
705c3429f80Sjmmv 		ehdr.e_type = ET_EXEC;
706c3429f80Sjmmv 		ehdr.e_machine = EM_386;
707c3429f80Sjmmv 		ehdr.e_version = 1;
708c3429f80Sjmmv 		ehdr.e_ehsize = sizeof(ehdr);
709c3429f80Sjmmv 
710719a906eSmartin 		ksyms_addsyms_explicit((void *)&ehdr,
711c3429f80Sjmmv 		    ms->s_symstart, ms->s_symsize,
7128d30a464Sjmmv 		    ms->s_strstart, ms->s_strsize);
7138d30a464Sjmmv 	}
7140b879f1cSjmmv 
7158d30a464Sjmmv 	return mi->mi_flags & MULTIBOOT_INFO_HAS_ELF_SYMS;
7160b879f1cSjmmv }
717