xref: /illumos-gate/usr/src/boot/common/multiboot2.c (revision b72c8d00)
114ee0d29SToomas Soome /*
214ee0d29SToomas Soome  * This file and its contents are supplied under the terms of the
314ee0d29SToomas Soome  * Common Development and Distribution License ("CDDL"), version 1.0.
414ee0d29SToomas Soome  * You may only use this file in accordance with the terms of version
514ee0d29SToomas Soome  * 1.0 of the CDDL.
614ee0d29SToomas Soome  *
714ee0d29SToomas Soome  * A full copy of the text of the CDDL should have accompanied this
814ee0d29SToomas Soome  * source.  A copy of the CDDL is also available via the Internet at
914ee0d29SToomas Soome  * http://www.illumos.org/license/CDDL.
1014ee0d29SToomas Soome  */
1114ee0d29SToomas Soome 
1214ee0d29SToomas Soome /*
1314ee0d29SToomas Soome  * Copyright 2017 Toomas Soome <tsoome@me.com>
14287d0250SJohn Levon  * Copyright 2019, Joyent, Inc.
1514ee0d29SToomas Soome  */
1614ee0d29SToomas Soome 
1714ee0d29SToomas Soome /*
1814ee0d29SToomas Soome  * This module adds support for loading and booting illumos multiboot2
1914ee0d29SToomas Soome  * kernel. This code is only built to support the illumos kernel, it does
2014ee0d29SToomas Soome  * not support xen.
2114ee0d29SToomas Soome  */
22f9feecc1SToomas Soome 
2314ee0d29SToomas Soome #include <sys/cdefs.h>
24ade2ca5bSToomas Soome #include <sys/stddef.h>
2514ee0d29SToomas Soome 
2614ee0d29SToomas Soome #include <sys/param.h>
2714ee0d29SToomas Soome #include <sys/exec.h>
2814ee0d29SToomas Soome #include <sys/linker.h>
2914ee0d29SToomas Soome #include <sys/module.h>
3014ee0d29SToomas Soome #include <sys/stdint.h>
3114ee0d29SToomas Soome #include <sys/multiboot2.h>
3214ee0d29SToomas Soome #include <stand.h>
3314ee0d29SToomas Soome #include <stdbool.h>
34f9feecc1SToomas Soome #include <machine/elf.h>
3514ee0d29SToomas Soome #include "libzfs.h"
3614ee0d29SToomas Soome 
3714ee0d29SToomas Soome #include "bootstrap.h"
389890ff83SToomas Soome #include <sys/consplat.h>
3914ee0d29SToomas Soome 
4014ee0d29SToomas Soome #include <machine/metadata.h>
4114ee0d29SToomas Soome #include <machine/pc/bios.h>
4214ee0d29SToomas Soome 
43f9feecc1SToomas Soome #define	SUPPORT_DHCP
44f9feecc1SToomas Soome #include <bootp.h>
45f9feecc1SToomas Soome 
46f9feecc1SToomas Soome #if !defined(EFI)
4714ee0d29SToomas Soome #include "../i386/btx/lib/btxv86.h"
489890ff83SToomas Soome #include "libi386.h"
499890ff83SToomas Soome #include "vbe.h"
5014ee0d29SToomas Soome 
51f9feecc1SToomas Soome #else
52f9feecc1SToomas Soome #include <efi.h>
53f9feecc1SToomas Soome #include <efilib.h>
54f9feecc1SToomas Soome #include "loader_efi.h"
55f9feecc1SToomas Soome 
56f9feecc1SToomas Soome static void (*trampoline)(uint32_t, struct relocator *, uint64_t);
5715ab2c88SToomas Soome static UINTN efi_map_size;		/* size of efi memory map */
58f9feecc1SToomas Soome #endif
5914ee0d29SToomas Soome 
6014ee0d29SToomas Soome #include "platform/acfreebsd.h"
6114ee0d29SToomas Soome #include "acconfig.h"
6286759c82SToomas Soome #define	ACPI_SYSTEM_XFACE
6314ee0d29SToomas Soome #include "actypes.h"
6414ee0d29SToomas Soome #include "actbl.h"
6514ee0d29SToomas Soome 
6614ee0d29SToomas Soome extern ACPI_TABLE_RSDP *rsdp;
6714ee0d29SToomas Soome 
6814ee0d29SToomas Soome /* MB data heap pointer. */
6914ee0d29SToomas Soome static vm_offset_t last_addr;
7014ee0d29SToomas Soome 
7186759c82SToomas Soome static int multiboot2_loadfile(char *, uint64_t, struct preloaded_file **);
7214ee0d29SToomas Soome static int multiboot2_exec(struct preloaded_file *);
7314ee0d29SToomas Soome 
7414ee0d29SToomas Soome struct file_format multiboot2 = { multiboot2_loadfile, multiboot2_exec };
7514ee0d29SToomas Soome static bool keep_bs = false;
7614ee0d29SToomas Soome static bool have_framebuffer = false;
7714ee0d29SToomas Soome static vm_offset_t load_addr;
7814ee0d29SToomas Soome static vm_offset_t entry_addr;
79190f051bSToomas Soome bool has_boot_services = true;
8014ee0d29SToomas Soome 
8114ee0d29SToomas Soome /*
8214ee0d29SToomas Soome  * Validate tags in info request. This function is provided just to
8314ee0d29SToomas Soome  * recognize the current tag list and only serves as a limited
8414ee0d29SToomas Soome  * safe guard against possibly corrupt information.
8514ee0d29SToomas Soome  */
8614ee0d29SToomas Soome static bool
is_info_request_valid(multiboot_header_tag_information_request_t * rtag)8714ee0d29SToomas Soome is_info_request_valid(multiboot_header_tag_information_request_t *rtag)
8814ee0d29SToomas Soome {
8914ee0d29SToomas Soome 	int i;
9014ee0d29SToomas Soome 
9114ee0d29SToomas Soome 	/*
9214ee0d29SToomas Soome 	 * If the tag is optional and we do not support it, we do not
9314ee0d29SToomas Soome 	 * have to do anything special, so we skip optional tags.
9414ee0d29SToomas Soome 	 */
9514ee0d29SToomas Soome 	if (rtag->mbh_flags & MULTIBOOT_HEADER_TAG_OPTIONAL)
9614ee0d29SToomas Soome 		return (true);
9714ee0d29SToomas Soome 
9814ee0d29SToomas Soome 	for (i = 0; i < (rtag->mbh_size - sizeof (*rtag)) /
9914ee0d29SToomas Soome 	    sizeof (rtag->mbh_requests[0]); i++)
10014ee0d29SToomas Soome 		switch (rtag->mbh_requests[i]) {
10114ee0d29SToomas Soome 		case MULTIBOOT_TAG_TYPE_END:
10214ee0d29SToomas Soome 		case MULTIBOOT_TAG_TYPE_CMDLINE:
10314ee0d29SToomas Soome 		case MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME:
10414ee0d29SToomas Soome 		case MULTIBOOT_TAG_TYPE_MODULE:
10514ee0d29SToomas Soome 		case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO:
10614ee0d29SToomas Soome 		case MULTIBOOT_TAG_TYPE_BOOTDEV:
10714ee0d29SToomas Soome 		case MULTIBOOT_TAG_TYPE_MMAP:
10814ee0d29SToomas Soome 		case MULTIBOOT_TAG_TYPE_FRAMEBUFFER:
10914ee0d29SToomas Soome 		case MULTIBOOT_TAG_TYPE_VBE:
11014ee0d29SToomas Soome 		case MULTIBOOT_TAG_TYPE_ELF_SECTIONS:
11114ee0d29SToomas Soome 		case MULTIBOOT_TAG_TYPE_APM:
11214ee0d29SToomas Soome 		case MULTIBOOT_TAG_TYPE_EFI32:
11314ee0d29SToomas Soome 		case MULTIBOOT_TAG_TYPE_EFI64:
11414ee0d29SToomas Soome 		case MULTIBOOT_TAG_TYPE_ACPI_OLD:
11514ee0d29SToomas Soome 		case MULTIBOOT_TAG_TYPE_ACPI_NEW:
11614ee0d29SToomas Soome 		case MULTIBOOT_TAG_TYPE_NETWORK:
11714ee0d29SToomas Soome 		case MULTIBOOT_TAG_TYPE_EFI_MMAP:
11814ee0d29SToomas Soome 		case MULTIBOOT_TAG_TYPE_EFI_BS:
11914ee0d29SToomas Soome 		case MULTIBOOT_TAG_TYPE_EFI32_IH:
12014ee0d29SToomas Soome 		case MULTIBOOT_TAG_TYPE_EFI64_IH:
12114ee0d29SToomas Soome 		case MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR:
12214ee0d29SToomas Soome 			break;
12314ee0d29SToomas Soome 		default:
12414ee0d29SToomas Soome 			printf("unsupported information tag: 0x%x\n",
12514ee0d29SToomas Soome 			    rtag->mbh_requests[i]);
12614ee0d29SToomas Soome 			return (false);
12714ee0d29SToomas Soome 		}
12814ee0d29SToomas Soome 	return (true);
12914ee0d29SToomas Soome }
13014ee0d29SToomas Soome 
13114ee0d29SToomas Soome static int
multiboot2_loadfile(char * filename,uint64_t dest,struct preloaded_file ** result)13286759c82SToomas Soome multiboot2_loadfile(char *filename, uint64_t dest,
13314ee0d29SToomas Soome     struct preloaded_file **result)
13414ee0d29SToomas Soome {
13514ee0d29SToomas Soome 	int fd, error;
13614ee0d29SToomas Soome 	uint32_t i;
13714ee0d29SToomas Soome 	struct stat st;
13814ee0d29SToomas Soome 	caddr_t header_search;
13914ee0d29SToomas Soome 	multiboot2_header_t *header;
14014ee0d29SToomas Soome 	multiboot_header_tag_t *tag;
14114ee0d29SToomas Soome 	multiboot_header_tag_address_t *addr_tag = NULL;
14214ee0d29SToomas Soome 	multiboot_header_tag_entry_address_t *entry_tag = NULL;
14314ee0d29SToomas Soome 	struct preloaded_file *fp;
14414ee0d29SToomas Soome 
14514ee0d29SToomas Soome 	/* This allows to check other file formats from file_formats array. */
14614ee0d29SToomas Soome 	error = EFTYPE;
14714ee0d29SToomas Soome 	if (filename == NULL)
14814ee0d29SToomas Soome 		return (error);
14914ee0d29SToomas Soome 
15014ee0d29SToomas Soome 	/* is kernel already loaded? */
15114ee0d29SToomas Soome 	fp = file_findfile(NULL, NULL);
15214ee0d29SToomas Soome 	if (fp != NULL)
15314ee0d29SToomas Soome 		return (error);
15414ee0d29SToomas Soome 
15514ee0d29SToomas Soome 	if ((fd = open(filename, O_RDONLY)) == -1)
15614ee0d29SToomas Soome 		return (errno);
15714ee0d29SToomas Soome 
15814ee0d29SToomas Soome 	/*
15914ee0d29SToomas Soome 	 * Read MULTIBOOT_SEARCH size in order to search for the
16014ee0d29SToomas Soome 	 * multiboot magic header.
16114ee0d29SToomas Soome 	 */
16214ee0d29SToomas Soome 	header_search = malloc(MULTIBOOT_SEARCH);
16314ee0d29SToomas Soome 	if (header_search == NULL) {
16414ee0d29SToomas Soome 		close(fd);
16514ee0d29SToomas Soome 		return (ENOMEM);
16614ee0d29SToomas Soome 	}
16714ee0d29SToomas Soome 
16814ee0d29SToomas Soome 	if (read(fd, header_search, MULTIBOOT_SEARCH) != MULTIBOOT_SEARCH)
16914ee0d29SToomas Soome 		goto out;
17014ee0d29SToomas Soome 
17114ee0d29SToomas Soome 	header = NULL;
17214ee0d29SToomas Soome 	for (i = 0; i <= (MULTIBOOT_SEARCH - sizeof (multiboot2_header_t));
17314ee0d29SToomas Soome 	    i += MULTIBOOT_HEADER_ALIGN) {
17414ee0d29SToomas Soome 		header = (multiboot2_header_t *)(header_search + i);
17514ee0d29SToomas Soome 
17614ee0d29SToomas Soome 		/* Do we have match on magic? */
17714ee0d29SToomas Soome 		if (header->mb2_magic != MULTIBOOT2_HEADER_MAGIC) {
17814ee0d29SToomas Soome 			header = NULL;
17914ee0d29SToomas Soome 			continue;
18014ee0d29SToomas Soome 		}
18114ee0d29SToomas Soome 		/*
18214ee0d29SToomas Soome 		 * Validate checksum, the sum of magic + architecture +
18314ee0d29SToomas Soome 		 * header_length + checksum must equal 0.
18414ee0d29SToomas Soome 		 */
18514ee0d29SToomas Soome 		if (header->mb2_magic + header->mb2_architecture +
18614ee0d29SToomas Soome 		    header->mb2_header_length + header->mb2_checksum != 0) {
18714ee0d29SToomas Soome 			header = NULL;
18814ee0d29SToomas Soome 			continue;
18914ee0d29SToomas Soome 		}
19014ee0d29SToomas Soome 		/*
19114ee0d29SToomas Soome 		 * Finally, the entire header must fit within MULTIBOOT_SEARCH.
19214ee0d29SToomas Soome 		 */
19314ee0d29SToomas Soome 		if (i + header->mb2_header_length > MULTIBOOT_SEARCH) {
19414ee0d29SToomas Soome 			header = NULL;
19514ee0d29SToomas Soome 			continue;
19614ee0d29SToomas Soome 		}
19714ee0d29SToomas Soome 		break;
19814ee0d29SToomas Soome 	}
19914ee0d29SToomas Soome 
20014ee0d29SToomas Soome 	if (header == NULL)
20114ee0d29SToomas Soome 		goto out;
20214ee0d29SToomas Soome 
2039890ff83SToomas Soome 	have_framebuffer = false;
20414ee0d29SToomas Soome 	for (tag = header->mb2_tags; tag->mbh_type != MULTIBOOT_TAG_TYPE_END;
20514ee0d29SToomas Soome 	    tag = (multiboot_header_tag_t *)((uintptr_t)tag +
20614ee0d29SToomas Soome 	    roundup2(tag->mbh_size, MULTIBOOT_TAG_ALIGN))) {
20714ee0d29SToomas Soome 		switch (tag->mbh_type) {
20814ee0d29SToomas Soome 		case MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST:
20914ee0d29SToomas Soome 			if (is_info_request_valid((void*)tag) == false)
21014ee0d29SToomas Soome 				goto out;
21114ee0d29SToomas Soome 			break;
21214ee0d29SToomas Soome 		case MULTIBOOT_HEADER_TAG_ADDRESS:
21314ee0d29SToomas Soome 			addr_tag = (multiboot_header_tag_address_t *)tag;
21414ee0d29SToomas Soome 			break;
21514ee0d29SToomas Soome 		case MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS:
21614ee0d29SToomas Soome 			entry_tag =
21714ee0d29SToomas Soome 			    (multiboot_header_tag_entry_address_t *)tag;
21814ee0d29SToomas Soome 			break;
21914ee0d29SToomas Soome 		case MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS:
22014ee0d29SToomas Soome 			break;
22114ee0d29SToomas Soome 		case MULTIBOOT_HEADER_TAG_FRAMEBUFFER:
22214ee0d29SToomas Soome 			have_framebuffer = true;
22314ee0d29SToomas Soome 			break;
22414ee0d29SToomas Soome 		case MULTIBOOT_HEADER_TAG_MODULE_ALIGN:
22514ee0d29SToomas Soome 			/* we always align modules */
22614ee0d29SToomas Soome 			break;
22714ee0d29SToomas Soome 		case MULTIBOOT_HEADER_TAG_EFI_BS:
22814ee0d29SToomas Soome 			keep_bs = true;
22914ee0d29SToomas Soome 			break;
23014ee0d29SToomas Soome 		default:
23114ee0d29SToomas Soome 			if (!(tag->mbh_flags & MULTIBOOT_HEADER_TAG_OPTIONAL)) {
23214ee0d29SToomas Soome 				printf("unsupported tag: 0x%x\n",
23314ee0d29SToomas Soome 				    tag->mbh_type);
23414ee0d29SToomas Soome 				goto out;
23514ee0d29SToomas Soome 			}
23614ee0d29SToomas Soome 		}
23714ee0d29SToomas Soome 	}
23814ee0d29SToomas Soome 
23914ee0d29SToomas Soome 	/*
24014ee0d29SToomas Soome 	 * We must have addr_tag and entry_tag to load a 64-bit kernel.
24114ee0d29SToomas Soome 	 * If these tags are missing, we either have a 32-bit kernel, or
24214ee0d29SToomas Soome 	 * this is not our kernel at all.
24314ee0d29SToomas Soome 	 */
24414ee0d29SToomas Soome 	if (addr_tag != NULL && entry_tag != NULL) {
24514ee0d29SToomas Soome 		fp = file_alloc();
24614ee0d29SToomas Soome 		if (fp == NULL) {
24714ee0d29SToomas Soome 			error = ENOMEM;
24814ee0d29SToomas Soome 			goto out;
24914ee0d29SToomas Soome 		}
25014ee0d29SToomas Soome 		if (lseek(fd, 0, SEEK_SET) == -1) {
25114ee0d29SToomas Soome 			printf("lseek failed\n");
25214ee0d29SToomas Soome 			error = EIO;
25314ee0d29SToomas Soome 			file_discard(fp);
25414ee0d29SToomas Soome 			goto out;
25514ee0d29SToomas Soome 		}
25614ee0d29SToomas Soome 		if (fstat(fd, &st) < 0) {
25714ee0d29SToomas Soome 			printf("fstat failed\n");
25814ee0d29SToomas Soome 			error = EIO;
25914ee0d29SToomas Soome 			file_discard(fp);
26014ee0d29SToomas Soome 			goto out;
26114ee0d29SToomas Soome 		}
26214ee0d29SToomas Soome 
26314ee0d29SToomas Soome 		load_addr = addr_tag->mbh_load_addr;
26414ee0d29SToomas Soome 		entry_addr = entry_tag->mbh_entry_addr;
26514ee0d29SToomas Soome 		fp->f_addr = archsw.arch_loadaddr(LOAD_KERN, filename,
26614ee0d29SToomas Soome 		    addr_tag->mbh_load_addr);
26714ee0d29SToomas Soome 		if (fp->f_addr == 0) {
26814ee0d29SToomas Soome 			error = ENOMEM;
26914ee0d29SToomas Soome 			file_discard(fp);
27014ee0d29SToomas Soome 			goto out;
27114ee0d29SToomas Soome 		}
27214ee0d29SToomas Soome 		fp->f_size = archsw.arch_readin(fd, fp->f_addr, st.st_size);
27314ee0d29SToomas Soome 
27414ee0d29SToomas Soome 		if (fp->f_size != st.st_size) {
275eddd1134SToomas Soome 			printf("error reading %s: %s\n", filename,
276eddd1134SToomas Soome 			    strerror(errno));
27714ee0d29SToomas Soome 			file_discard(fp);
27814ee0d29SToomas Soome 			error = EIO;
27914ee0d29SToomas Soome 			goto out;
28014ee0d29SToomas Soome 		}
28114ee0d29SToomas Soome 
28214ee0d29SToomas Soome 		fp->f_name = strdup(filename);
28314ee0d29SToomas Soome 		fp->f_type = strdup("aout multiboot2 kernel");
28414ee0d29SToomas Soome 		if (fp->f_name == NULL || fp->f_type == NULL) {
28514ee0d29SToomas Soome 			error = ENOMEM;
28614ee0d29SToomas Soome 			file_discard(fp);
28714ee0d29SToomas Soome 			goto out;
28814ee0d29SToomas Soome 		}
28914ee0d29SToomas Soome 
29014ee0d29SToomas Soome 		fp->f_metadata = NULL;
29114ee0d29SToomas Soome 		error = 0;
29214ee0d29SToomas Soome 	} else {
293f9feecc1SToomas Soome #if defined(EFI)
294f9feecc1SToomas Soome 		/* 32-bit kernel is not yet supported for EFI */
295f9feecc1SToomas Soome 		printf("32-bit kernel is not supported by UEFI loader\n");
296f9feecc1SToomas Soome 		error = ENOTSUP;
297f9feecc1SToomas Soome 		goto out;
298f9feecc1SToomas Soome #endif
29914ee0d29SToomas Soome 		/* elf32_loadfile_raw will fill the attributes in fp. */
30014ee0d29SToomas Soome 		error = elf32_loadfile_raw(filename, dest, &fp, 2);
30114ee0d29SToomas Soome 		if (error != 0) {
30214ee0d29SToomas Soome 			printf("elf32_loadfile_raw failed: %d unable to "
30314ee0d29SToomas Soome 			    "load multiboot2 kernel\n", error);
30414ee0d29SToomas Soome 			goto out;
30514ee0d29SToomas Soome 		}
30614ee0d29SToomas Soome 		entry_addr = fp->f_addr;
30714ee0d29SToomas Soome 		/*
30814ee0d29SToomas Soome 		 * We want the load_addr to have some legal value,
30914ee0d29SToomas Soome 		 * so we set it same as the entry_addr.
31014ee0d29SToomas Soome 		 * The distinction is important with UEFI, but not
31114ee0d29SToomas Soome 		 * with BIOS version, because BIOS version does not use
31214ee0d29SToomas Soome 		 * staging area.
31314ee0d29SToomas Soome 		 */
31414ee0d29SToomas Soome 		load_addr = fp->f_addr;
31514ee0d29SToomas Soome 	}
31614ee0d29SToomas Soome 
31714ee0d29SToomas Soome 	setenv("kernelname", fp->f_name, 1);
318f9feecc1SToomas Soome #if defined(EFI)
319f9feecc1SToomas Soome 	efi_addsmapdata(fp);
320f9feecc1SToomas Soome #else
32114ee0d29SToomas Soome 	bios_addsmapdata(fp);
322f9feecc1SToomas Soome #endif
32314ee0d29SToomas Soome 	*result = fp;
32414ee0d29SToomas Soome out:
32514ee0d29SToomas Soome 	free(header_search);
32614ee0d29SToomas Soome 	close(fd);
32714ee0d29SToomas Soome 	return (error);
32814ee0d29SToomas Soome }
32914ee0d29SToomas Soome 
33014ee0d29SToomas Soome /*
33114ee0d29SToomas Soome  * Search the command line for named property.
33214ee0d29SToomas Soome  *
33314ee0d29SToomas Soome  * Return codes:
33414ee0d29SToomas Soome  *	0	The name is found, we return the data in value and len.
33514ee0d29SToomas Soome  *	ENOENT	The name is not found.
33614ee0d29SToomas Soome  *	EINVAL	The provided command line is badly formed.
33714ee0d29SToomas Soome  */
33814ee0d29SToomas Soome static int
find_property_value(const char * cmd,const char * name,const char ** value,size_t * len)33914ee0d29SToomas Soome find_property_value(const char *cmd, const char *name, const char **value,
34014ee0d29SToomas Soome     size_t *len)
34114ee0d29SToomas Soome {
34214ee0d29SToomas Soome 	const char *namep, *valuep;
34314ee0d29SToomas Soome 	size_t name_len, value_len;
34414ee0d29SToomas Soome 	int quoted;
34514ee0d29SToomas Soome 
34614ee0d29SToomas Soome 	*value = NULL;
34714ee0d29SToomas Soome 	*len = 0;
34814ee0d29SToomas Soome 
34914ee0d29SToomas Soome 	if (cmd == NULL)
35014ee0d29SToomas Soome 		return (ENOENT);
35114ee0d29SToomas Soome 
35214ee0d29SToomas Soome 	while (*cmd != '\0') {
35314ee0d29SToomas Soome 		if (cmd[0] != '-' || cmd[1] != 'B') {
35414ee0d29SToomas Soome 			cmd++;
35514ee0d29SToomas Soome 			continue;
35614ee0d29SToomas Soome 		}
35714ee0d29SToomas Soome 		cmd += 2;	/* Skip -B */
35814ee0d29SToomas Soome 		while (cmd[0] == ' ' || cmd[0] == '\t')
35914ee0d29SToomas Soome 			cmd++;	/* Skip whitespaces. */
36014ee0d29SToomas Soome 		while (*cmd != '\0' && cmd[0] != ' ' && cmd[0] != '\t') {
36114ee0d29SToomas Soome 			namep = cmd;
36214ee0d29SToomas Soome 			valuep = strchr(cmd, '=');
36314ee0d29SToomas Soome 			if (valuep == NULL)
36414ee0d29SToomas Soome 				break;
36514ee0d29SToomas Soome 			name_len = valuep - namep;
36614ee0d29SToomas Soome 			valuep++;
36714ee0d29SToomas Soome 			value_len = 0;
36814ee0d29SToomas Soome 			quoted = 0;
369*b72c8d00SToomas Soome 			for (;; ++value_len) {
37014ee0d29SToomas Soome 				if (valuep[value_len] == '\0')
37114ee0d29SToomas Soome 					break;
37214ee0d29SToomas Soome 
37314ee0d29SToomas Soome 				/* Is this value quoted? */
37414ee0d29SToomas Soome 				if (value_len == 0 &&
37514ee0d29SToomas Soome 				    (valuep[0] == '\'' || valuep[0] == '"')) {
37614ee0d29SToomas Soome 					quoted = valuep[0];
37714ee0d29SToomas Soome 					++value_len;
37814ee0d29SToomas Soome 				}
37914ee0d29SToomas Soome 
38014ee0d29SToomas Soome 				/*
38114ee0d29SToomas Soome 				 * In the quote accept any character,
38214ee0d29SToomas Soome 				 * but look for ending quote.
38314ee0d29SToomas Soome 				 */
38414ee0d29SToomas Soome 				if (quoted != 0) {
38514ee0d29SToomas Soome 					if (valuep[value_len] == quoted)
38614ee0d29SToomas Soome 						quoted = 0;
38714ee0d29SToomas Soome 					continue;
38814ee0d29SToomas Soome 				}
38914ee0d29SToomas Soome 
39014ee0d29SToomas Soome 				/* A comma or white space ends the value. */
39114ee0d29SToomas Soome 				if (valuep[value_len] == ',' ||
39214ee0d29SToomas Soome 				    valuep[value_len] == ' ' ||
39314ee0d29SToomas Soome 				    valuep[value_len] == '\t')
39414ee0d29SToomas Soome 					break;
39514ee0d29SToomas Soome 			}
39614ee0d29SToomas Soome 			if (quoted != 0) {
39714ee0d29SToomas Soome 				printf("Missing closing '%c' in \"%s\"\n",
39814ee0d29SToomas Soome 				    quoted, valuep);
39914ee0d29SToomas Soome 				return (EINVAL);
40014ee0d29SToomas Soome 			}
40114ee0d29SToomas Soome 			if (value_len != 0) {
40214ee0d29SToomas Soome 				if (strncmp(namep, name, name_len) == 0) {
40314ee0d29SToomas Soome 					*value = valuep;
40414ee0d29SToomas Soome 					*len = value_len;
40514ee0d29SToomas Soome 					return (0);
40614ee0d29SToomas Soome 				}
40714ee0d29SToomas Soome 			}
40814ee0d29SToomas Soome 			cmd = valuep + value_len;
40914ee0d29SToomas Soome 			while (*cmd == ',')
41014ee0d29SToomas Soome 				cmd++;
41114ee0d29SToomas Soome 		}
41214ee0d29SToomas Soome 	}
41314ee0d29SToomas Soome 	return (ENOENT);
41414ee0d29SToomas Soome }
41514ee0d29SToomas Soome 
416ade2ca5bSToomas Soome /*
417ade2ca5bSToomas Soome  * If command line has " -B ", insert property after "-B ", otherwise
418ade2ca5bSToomas Soome  * append to command line.
419ade2ca5bSToomas Soome  */
420ade2ca5bSToomas Soome static char *
insert_cmdline(const char * head,const char * prop)421ade2ca5bSToomas Soome insert_cmdline(const char *head, const char *prop)
422ade2ca5bSToomas Soome {
423ade2ca5bSToomas Soome 	const char *prop_opt = " -B ";
424ade2ca5bSToomas Soome 	char *cmdline, *tail;
425ade2ca5bSToomas Soome 	int len = 0;
426ade2ca5bSToomas Soome 
427ade2ca5bSToomas Soome 	tail = strstr(head, prop_opt);
428ade2ca5bSToomas Soome 	if (tail != NULL) {
429ade2ca5bSToomas Soome 		ptrdiff_t diff;
430ade2ca5bSToomas Soome 		tail += strlen(prop_opt);
431ade2ca5bSToomas Soome 		diff = tail - head;
432ade2ca5bSToomas Soome 		if (diff >= INT_MAX)
433ade2ca5bSToomas Soome 			return (NULL);
434ade2ca5bSToomas Soome 		len = (int)diff;
435ade2ca5bSToomas Soome 	}
436ade2ca5bSToomas Soome 
437ade2ca5bSToomas Soome 	if (tail == NULL)
438ade2ca5bSToomas Soome 		asprintf(&cmdline, "%s%s%s", head, prop_opt, prop);
439ade2ca5bSToomas Soome 	else
440ade2ca5bSToomas Soome 		asprintf(&cmdline, "%.*s%s,%s", len, head, prop, tail);
441ade2ca5bSToomas Soome 
442ade2ca5bSToomas Soome 	return (cmdline);
443ade2ca5bSToomas Soome }
444ade2ca5bSToomas Soome 
445ade2ca5bSToomas Soome /*
446ade2ca5bSToomas Soome  * Since we have no way to pass the environment to the mb1 kernel other than
447ade2ca5bSToomas Soome  * through arguments, we need to take care of console setup.
448ade2ca5bSToomas Soome  *
449ade2ca5bSToomas Soome  * If the console is in mirror mode, set the kernel console from $os_console.
450ade2ca5bSToomas Soome  * If it's unset, use first item from $console.
451ade2ca5bSToomas Soome  * If $console is "ttyX", also pass $ttyX-mode, since it may have been set by
452ade2ca5bSToomas Soome  * the user.
453ade2ca5bSToomas Soome  *
454ade2ca5bSToomas Soome  * In case of memory allocation errors, just return the original command line
455ade2ca5bSToomas Soome  * so we have a chance of booting.
456ade2ca5bSToomas Soome  *
457ade2ca5bSToomas Soome  * On success, cl will be freed and a new, allocated command line string is
458ade2ca5bSToomas Soome  * returned.
459ade2ca5bSToomas Soome  *
460ade2ca5bSToomas Soome  * For the mb2 kernel, we only set command line console if os_console is set.
461ade2ca5bSToomas Soome  * We can not overwrite console in the environment, as it can disrupt the
462ade2ca5bSToomas Soome  * loader console messages, and we do not want to deal with the os_console
463ade2ca5bSToomas Soome  * in the kernel.
464ade2ca5bSToomas Soome  */
465ade2ca5bSToomas Soome static char *
update_cmdline(char * cl,bool mb2)466ade2ca5bSToomas Soome update_cmdline(char *cl, bool mb2)
467ade2ca5bSToomas Soome {
468ade2ca5bSToomas Soome 	char *os_console = getenv("os_console");
469ade2ca5bSToomas Soome 	char *ttymode = NULL;
470ade2ca5bSToomas Soome 	char mode[10];
471ade2ca5bSToomas Soome 	char *tmp;
472ade2ca5bSToomas Soome 	const char *prop;
473ade2ca5bSToomas Soome 	size_t plen;
474ade2ca5bSToomas Soome 	int rv;
475ade2ca5bSToomas Soome 
476ade2ca5bSToomas Soome 	if (mb2 == true && os_console == NULL)
477ade2ca5bSToomas Soome 		return (cl);
478ade2ca5bSToomas Soome 
479ade2ca5bSToomas Soome 	if (os_console == NULL) {
480ade2ca5bSToomas Soome 		tmp = strdup(getenv("console"));
481ade2ca5bSToomas Soome 		os_console = strsep(&tmp, ", ");
482ade2ca5bSToomas Soome 	} else {
483ade2ca5bSToomas Soome 		os_console = strdup(os_console);
484ade2ca5bSToomas Soome 	}
485ade2ca5bSToomas Soome 
486ade2ca5bSToomas Soome 	if (os_console == NULL)
487ade2ca5bSToomas Soome 		return (cl);
488ade2ca5bSToomas Soome 
489ade2ca5bSToomas Soome 	if (mb2 == false && strncmp(os_console, "tty", 3) == 0) {
490ade2ca5bSToomas Soome 		snprintf(mode, sizeof (mode), "%s-mode", os_console);
491ade2ca5bSToomas Soome 		/*
492ade2ca5bSToomas Soome 		 * The ttyX-mode variable is set by our serial console
493ade2ca5bSToomas Soome 		 * driver for ttya-ttyd. However, since the os_console
494ade2ca5bSToomas Soome 		 * values are not verified, it is possible we get bogus
495ade2ca5bSToomas Soome 		 * name and no mode variable. If so, we do not set console
496ade2ca5bSToomas Soome 		 * property and let the kernel use defaults.
497ade2ca5bSToomas Soome 		 */
498ade2ca5bSToomas Soome 		if ((ttymode = getenv(mode)) == NULL)
499ade2ca5bSToomas Soome 			return (cl);
500ade2ca5bSToomas Soome 	}
501ade2ca5bSToomas Soome 
502ade2ca5bSToomas Soome 	rv = find_property_value(cl, "console", &prop, &plen);
503ade2ca5bSToomas Soome 	if (rv != 0 && rv != ENOENT) {
504ade2ca5bSToomas Soome 		free(os_console);
505ade2ca5bSToomas Soome 		return (cl);
506ade2ca5bSToomas Soome 	}
507ade2ca5bSToomas Soome 
508ade2ca5bSToomas Soome 	/* If console is set and this is MB2 boot, we are done. */
509ade2ca5bSToomas Soome 	if (rv == 0 && mb2 == true) {
510ade2ca5bSToomas Soome 		free(os_console);
511ade2ca5bSToomas Soome 		return (cl);
512ade2ca5bSToomas Soome 	}
513ade2ca5bSToomas Soome 
514ade2ca5bSToomas Soome 	/* If console is set, do we need to set tty mode? */
515ade2ca5bSToomas Soome 	if (rv == 0) {
516ade2ca5bSToomas Soome 		const char *ttyp = NULL;
517ade2ca5bSToomas Soome 		size_t ttylen;
518ade2ca5bSToomas Soome 
519ade2ca5bSToomas Soome 		free(os_console);
520ade2ca5bSToomas Soome 		os_console = NULL;
521ade2ca5bSToomas Soome 		*mode = '\0';
522ade2ca5bSToomas Soome 		if (strncmp(prop, "tty", 3) == 0 && plen == 4) {
523ade2ca5bSToomas Soome 			strncpy(mode, prop, plen);
524ade2ca5bSToomas Soome 			mode[plen] = '\0';
525ade2ca5bSToomas Soome 			strncat(mode, "-mode", 5);
526ade2ca5bSToomas Soome 			find_property_value(cl, mode, &ttyp, &ttylen);
527ade2ca5bSToomas Soome 		}
528ade2ca5bSToomas Soome 
529ade2ca5bSToomas Soome 		if (*mode != '\0' && ttyp == NULL)
530ade2ca5bSToomas Soome 			ttymode = getenv(mode);
531ade2ca5bSToomas Soome 		else
532ade2ca5bSToomas Soome 			return (cl);
533ade2ca5bSToomas Soome 	}
534ade2ca5bSToomas Soome 
535ade2ca5bSToomas Soome 	/* Build updated command line. */
536ade2ca5bSToomas Soome 	if (os_console != NULL) {
537ade2ca5bSToomas Soome 		char *propstr;
538ade2ca5bSToomas Soome 
539ade2ca5bSToomas Soome 		asprintf(&propstr, "console=%s", os_console);
540ade2ca5bSToomas Soome 		free(os_console);
541ade2ca5bSToomas Soome 		if (propstr == NULL) {
542ade2ca5bSToomas Soome 			return (cl);
543ade2ca5bSToomas Soome 		}
544ade2ca5bSToomas Soome 
545ade2ca5bSToomas Soome 		tmp = insert_cmdline(cl, propstr);
54686759c82SToomas Soome 		free(propstr);
54786759c82SToomas Soome 		if (tmp == NULL)
548ade2ca5bSToomas Soome 			return (cl);
549ade2ca5bSToomas Soome 
55086759c82SToomas Soome 		free(cl);
55186759c82SToomas Soome 		cl = tmp;
552ade2ca5bSToomas Soome 	}
553ade2ca5bSToomas Soome 	if (ttymode != NULL) {
554ade2ca5bSToomas Soome 		char *propstr;
555ade2ca5bSToomas Soome 
556ade2ca5bSToomas Soome 		asprintf(&propstr, "%s=\"%s\"", mode, ttymode);
557ade2ca5bSToomas Soome 		if (propstr == NULL)
558ade2ca5bSToomas Soome 			return (cl);
559ade2ca5bSToomas Soome 
560ade2ca5bSToomas Soome 		tmp = insert_cmdline(cl, propstr);
56186759c82SToomas Soome 		free(propstr);
56286759c82SToomas Soome 		if (tmp == NULL)
563ade2ca5bSToomas Soome 			return (cl);
56486759c82SToomas Soome 		free(cl);
56586759c82SToomas Soome 		cl = tmp;
566ade2ca5bSToomas Soome 	}
567ade2ca5bSToomas Soome 
568ade2ca5bSToomas Soome 	return (cl);
569ade2ca5bSToomas Soome }
570ade2ca5bSToomas Soome 
57114ee0d29SToomas Soome /*
57214ee0d29SToomas Soome  * Build the kernel command line. Shared function between MB1 and MB2.
573ade2ca5bSToomas Soome  *
574ade2ca5bSToomas Soome  * In both cases, if fstype is set and is not zfs, we do not set up
575ade2ca5bSToomas Soome  * zfs-bootfs property. But we set kernel file name and options.
576ade2ca5bSToomas Soome  *
577ade2ca5bSToomas Soome  * For the MB1, we only can pass properties on command line, so
578ade2ca5bSToomas Soome  * we will set console, ttyX-mode (for serial console) and zfs-bootfs.
579ade2ca5bSToomas Soome  *
580ade2ca5bSToomas Soome  * For the MB2, we can pass properties in environment, but if os_console
581ade2ca5bSToomas Soome  * is set in environment, we need to add console property on the kernel
582ade2ca5bSToomas Soome  * command line.
583ade2ca5bSToomas Soome  *
584ade2ca5bSToomas Soome  * The console properties are managed in update_cmdline().
58514ee0d29SToomas Soome  */
58614ee0d29SToomas Soome int
mb_kernel_cmdline(struct preloaded_file * fp,struct devdesc * rootdev,char ** line)58714ee0d29SToomas Soome mb_kernel_cmdline(struct preloaded_file *fp, struct devdesc *rootdev,
58814ee0d29SToomas Soome     char **line)
58914ee0d29SToomas Soome {
59014ee0d29SToomas Soome 	const char *fs = getenv("fstype");
591ade2ca5bSToomas Soome 	char *cmdline;
59214ee0d29SToomas Soome 	size_t len;
59314ee0d29SToomas Soome 	bool zfs_root = false;
594ade2ca5bSToomas Soome 	bool mb2;
595ade2ca5bSToomas Soome 	int rv;
596ade2ca5bSToomas Soome 
597*b72c8d00SToomas Soome 	/*
598*b72c8d00SToomas Soome 	 * With multiple console devices and "os_console" variable not
599*b72c8d00SToomas Soome 	 * set, set os_console to last input device.
600*b72c8d00SToomas Soome 	 */
601*b72c8d00SToomas Soome 	rv = cons_inputdev();
602*b72c8d00SToomas Soome 	if (rv != -1)
603*b72c8d00SToomas Soome 		(void) setenv("os_console", consoles[rv]->c_name, 0);
604*b72c8d00SToomas Soome 
605ade2ca5bSToomas Soome 	/*
606ade2ca5bSToomas Soome 	 * 64-bit kernel has aout header, 32-bit kernel is elf, and the
607ade2ca5bSToomas Soome 	 * type strings are different. Lets just search for "multiboot2".
608ade2ca5bSToomas Soome 	 */
609ade2ca5bSToomas Soome 	if (strstr(fp->f_type, "multiboot2") == NULL)
610ade2ca5bSToomas Soome 		mb2 = false;
611ade2ca5bSToomas Soome 	else
612ade2ca5bSToomas Soome 		mb2 = true;
61314ee0d29SToomas Soome 
614c142ce19SToomas Soome 	if (rootdev->d_dev->dv_type == DEVT_ZFS)
61514ee0d29SToomas Soome 		zfs_root = true;
61614ee0d29SToomas Soome 
61714ee0d29SToomas Soome 	/* If we have fstype set in env, reset zfs_root if needed. */
61814ee0d29SToomas Soome 	if (fs != NULL && strcmp(fs, "zfs") != 0)
61914ee0d29SToomas Soome 		zfs_root = false;
62014ee0d29SToomas Soome 
62114ee0d29SToomas Soome 	/*
62214ee0d29SToomas Soome 	 * If we have fstype set on the command line,
62314ee0d29SToomas Soome 	 * reset zfs_root if needed.
62414ee0d29SToomas Soome 	 */
62514ee0d29SToomas Soome 	rv = find_property_value(fp->f_args, "fstype", &fs, &len);
626ade2ca5bSToomas Soome 	if (rv != 0 && rv != ENOENT)
62714ee0d29SToomas Soome 		return (rv);
62814ee0d29SToomas Soome 
62914ee0d29SToomas Soome 	if (fs != NULL && strncmp(fs, "zfs", len) != 0)
63014ee0d29SToomas Soome 		zfs_root = false;
63114ee0d29SToomas Soome 
632ade2ca5bSToomas Soome 	/* zfs_bootfs() will set the environment, it must be called. */
63314ee0d29SToomas Soome 	if (zfs_root == true)
634ade2ca5bSToomas Soome 		fs = zfs_bootfs(rootdev);
635ade2ca5bSToomas Soome 
636ade2ca5bSToomas Soome 	if (fp->f_args == NULL)
637ade2ca5bSToomas Soome 		cmdline = strdup(fp->f_name);
638ade2ca5bSToomas Soome 	else
639ade2ca5bSToomas Soome 		asprintf(&cmdline, "%s %s", fp->f_name, fp->f_args);
64014ee0d29SToomas Soome 
64114ee0d29SToomas Soome 	if (cmdline == NULL)
64214ee0d29SToomas Soome 		return (ENOMEM);
64314ee0d29SToomas Soome 
644ade2ca5bSToomas Soome 	/* Append zfs-bootfs for MB1 command line. */
645ade2ca5bSToomas Soome 	if (mb2 == false && zfs_root == true) {
646ade2ca5bSToomas Soome 		char *tmp;
647ade2ca5bSToomas Soome 
648ade2ca5bSToomas Soome 		tmp = insert_cmdline(cmdline, fs);
649ade2ca5bSToomas Soome 		free(cmdline);
650ade2ca5bSToomas Soome 		if (tmp == NULL)
651ade2ca5bSToomas Soome 			return (ENOMEM);
652ade2ca5bSToomas Soome 		cmdline = tmp;
653ade2ca5bSToomas Soome 	}
65414ee0d29SToomas Soome 
655ade2ca5bSToomas Soome 	*line = update_cmdline(cmdline, mb2);
65614ee0d29SToomas Soome 	return (0);
65714ee0d29SToomas Soome }
65814ee0d29SToomas Soome 
65914ee0d29SToomas Soome /*
66014ee0d29SToomas Soome  * Returns allocated virtual address from MB info area.
66114ee0d29SToomas Soome  */
66214ee0d29SToomas Soome static vm_offset_t
mb_malloc(size_t n)66314ee0d29SToomas Soome mb_malloc(size_t n)
66414ee0d29SToomas Soome {
66514ee0d29SToomas Soome 	vm_offset_t ptr = last_addr;
66614ee0d29SToomas Soome 	last_addr = roundup(last_addr + n, MULTIBOOT_TAG_ALIGN);
66714ee0d29SToomas Soome 	return (ptr);
66814ee0d29SToomas Soome }
66914ee0d29SToomas Soome 
67014ee0d29SToomas Soome /*
67114ee0d29SToomas Soome  * Calculate size for module tag list.
67214ee0d29SToomas Soome  */
67314ee0d29SToomas Soome static size_t
module_size(struct preloaded_file * fp)67414ee0d29SToomas Soome module_size(struct preloaded_file *fp)
67514ee0d29SToomas Soome {
67614ee0d29SToomas Soome 	size_t len, size;
67714ee0d29SToomas Soome 	struct preloaded_file *mfp;
67814ee0d29SToomas Soome 
67914ee0d29SToomas Soome 	size = 0;
68014ee0d29SToomas Soome 	for (mfp = fp->f_next; mfp != NULL; mfp = mfp->f_next) {
68114ee0d29SToomas Soome 		len = strlen(mfp->f_name) + 1;
68214ee0d29SToomas Soome 		len += strlen(mfp->f_type) + 5 + 1; /* 5 is for "type=" */
68314ee0d29SToomas Soome 		if (mfp->f_args != NULL)
68414ee0d29SToomas Soome 			len += strlen(mfp->f_args) + 1;
68514ee0d29SToomas Soome 		size += sizeof (multiboot_tag_module_t) + len;
68614ee0d29SToomas Soome 		size = roundup(size, MULTIBOOT_TAG_ALIGN);
68714ee0d29SToomas Soome 	}
68814ee0d29SToomas Soome 	return (size);
68914ee0d29SToomas Soome }
69014ee0d29SToomas Soome 
69186759c82SToomas Soome #if defined(EFI)
692f9feecc1SToomas Soome /*
693f9feecc1SToomas Soome  * Calculate size for UEFI memory map tag.
694f9feecc1SToomas Soome  */
69515ab2c88SToomas Soome #define	EFI_EXTRA_PAGES	3
69615ab2c88SToomas Soome 
697f9feecc1SToomas Soome static int
efimemmap_size(void)698f9feecc1SToomas Soome efimemmap_size(void)
699f9feecc1SToomas Soome {
700f9feecc1SToomas Soome 	UINTN size, cur_size, desc_size;
701f9feecc1SToomas Soome 	EFI_MEMORY_DESCRIPTOR *mmap;
702f9feecc1SToomas Soome 	EFI_STATUS ret;
703f9feecc1SToomas Soome 
704f9feecc1SToomas Soome 	size = EFI_PAGE_SIZE;		/* Start with 4k. */
705f9feecc1SToomas Soome 	while (1) {
706f9feecc1SToomas Soome 		cur_size = size;
707f9feecc1SToomas Soome 		mmap = malloc(cur_size);
708f9feecc1SToomas Soome 		if (mmap == NULL)
709f9feecc1SToomas Soome 			return (0);
710f9feecc1SToomas Soome 		ret = BS->GetMemoryMap(&cur_size, mmap, NULL, &desc_size, NULL);
711f9feecc1SToomas Soome 		free(mmap);
712f9feecc1SToomas Soome 		if (ret == EFI_SUCCESS)
713f9feecc1SToomas Soome 			break;
714f9feecc1SToomas Soome 		if (ret == EFI_BUFFER_TOO_SMALL) {
715f9feecc1SToomas Soome 			if (size < cur_size)
716f9feecc1SToomas Soome 				size = cur_size;
717f9feecc1SToomas Soome 			size += (EFI_PAGE_SIZE);
718f9feecc1SToomas Soome 		} else
719f9feecc1SToomas Soome 			return (0);
720f9feecc1SToomas Soome 	}
721f9feecc1SToomas Soome 
722f9feecc1SToomas Soome 	/* EFI MMAP will grow when we allocate MBI, set some buffer. */
72315ab2c88SToomas Soome 	size += (EFI_EXTRA_PAGES << EFI_PAGE_SHIFT);
72415ab2c88SToomas Soome 	size = roundup2(size, EFI_PAGE_SIZE);
72515ab2c88SToomas Soome 	efi_map_size = size;	/* Record the calculated size. */
726f9feecc1SToomas Soome 	return (sizeof (multiboot_tag_efi_mmap_t) + size);
727f9feecc1SToomas Soome }
728f9feecc1SToomas Soome #endif
729f9feecc1SToomas Soome 
73014ee0d29SToomas Soome /*
73114ee0d29SToomas Soome  * Calculate size for bios smap tag.
73214ee0d29SToomas Soome  */
73314ee0d29SToomas Soome static size_t
biossmap_size(struct preloaded_file * fp)73414ee0d29SToomas Soome biossmap_size(struct preloaded_file *fp)
73514ee0d29SToomas Soome {
73614ee0d29SToomas Soome 	int num;
73714ee0d29SToomas Soome 	struct file_metadata *md;
73814ee0d29SToomas Soome 
73914ee0d29SToomas Soome 	md = file_findmetadata(fp, MODINFOMD_SMAP);
74014ee0d29SToomas Soome 	if (md == NULL)
74114ee0d29SToomas Soome 		return (0);
74214ee0d29SToomas Soome 
74386759c82SToomas Soome 	num = md->md_size / sizeof (struct bios_smap); /* number of entries */
74414ee0d29SToomas Soome 	return (sizeof (multiboot_tag_mmap_t) +
74514ee0d29SToomas Soome 	    num * sizeof (multiboot_mmap_entry_t));
74614ee0d29SToomas Soome }
74714ee0d29SToomas Soome 
74814ee0d29SToomas Soome static size_t
mbi_size(struct preloaded_file * fp,char * cmdline)74914ee0d29SToomas Soome mbi_size(struct preloaded_file *fp, char *cmdline)
75014ee0d29SToomas Soome {
75114ee0d29SToomas Soome 	size_t size;
75286759c82SToomas Soome #if !defined(EFI)
7539890ff83SToomas Soome 	extern multiboot_tag_framebuffer_t gfx_fb;
7549890ff83SToomas Soome #endif
75514ee0d29SToomas Soome 
75614ee0d29SToomas Soome 	size = sizeof (uint32_t) * 2; /* first 2 fields from MBI header */
75714ee0d29SToomas Soome 	size += sizeof (multiboot_tag_string_t) + strlen(cmdline) + 1;
75814ee0d29SToomas Soome 	size = roundup2(size, MULTIBOOT_TAG_ALIGN);
75914ee0d29SToomas Soome 	size += sizeof (multiboot_tag_string_t) + strlen(bootprog_info) + 1;
76014ee0d29SToomas Soome 	size = roundup2(size, MULTIBOOT_TAG_ALIGN);
76186759c82SToomas Soome #if !defined(EFI)
76214ee0d29SToomas Soome 	size += sizeof (multiboot_tag_basic_meminfo_t);
76314ee0d29SToomas Soome 	size = roundup2(size, MULTIBOOT_TAG_ALIGN);
764f9feecc1SToomas Soome #endif
76514ee0d29SToomas Soome 	size += module_size(fp);
76614ee0d29SToomas Soome 	size = roundup2(size, MULTIBOOT_TAG_ALIGN);
76786759c82SToomas Soome #if defined(EFI)
768f9feecc1SToomas Soome 	size += sizeof (multiboot_tag_efi64_t);
769f9feecc1SToomas Soome 	size = roundup2(size, MULTIBOOT_TAG_ALIGN);
770f9feecc1SToomas Soome 	size += efimemmap_size();
771f9feecc1SToomas Soome 	size = roundup2(size, MULTIBOOT_TAG_ALIGN);
772f9feecc1SToomas Soome 
773f9feecc1SToomas Soome 	if (have_framebuffer == true) {
774f9feecc1SToomas Soome 		size += sizeof (multiboot_tag_framebuffer_t);
775f9feecc1SToomas Soome 		size = roundup2(size, MULTIBOOT_TAG_ALIGN);
776f9feecc1SToomas Soome 	}
777f9feecc1SToomas Soome #endif
7789890ff83SToomas Soome 
77914ee0d29SToomas Soome 	size += biossmap_size(fp);
78014ee0d29SToomas Soome 	size = roundup2(size, MULTIBOOT_TAG_ALIGN);
78114ee0d29SToomas Soome 
78286759c82SToomas Soome #if !defined(EFI)
7839890ff83SToomas Soome 	if (gfx_fb.framebuffer_common.framebuffer_type ==
7849890ff83SToomas Soome 	    MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED) {
7859890ff83SToomas Soome 		size += sizeof (struct multiboot_tag_framebuffer_common);
78691061836SToomas Soome 		size += CMAP_SIZE * sizeof (multiboot_color_t);
7879890ff83SToomas Soome 	} else {
7889890ff83SToomas Soome 		size += sizeof (multiboot_tag_framebuffer_t);
7899890ff83SToomas Soome 	}
7909890ff83SToomas Soome 	size = roundup2(size, MULTIBOOT_TAG_ALIGN);
7919890ff83SToomas Soome 
7929890ff83SToomas Soome 	size += sizeof (multiboot_tag_vbe_t);
7939890ff83SToomas Soome 	size = roundup2(size, MULTIBOOT_TAG_ALIGN);
7949890ff83SToomas Soome #endif
7959890ff83SToomas Soome 
796f9feecc1SToomas Soome 	if (bootp_response != NULL) {
79786759c82SToomas Soome 		size += sizeof (multiboot_tag_network_t) + bootp_response_size;
79814ee0d29SToomas Soome 		size = roundup2(size, MULTIBOOT_TAG_ALIGN);
79914ee0d29SToomas Soome 	}
80014ee0d29SToomas Soome 
80114ee0d29SToomas Soome 	if (rsdp != NULL) {
80214ee0d29SToomas Soome 		if (rsdp->Revision == 0) {
80314ee0d29SToomas Soome 			size += sizeof (multiboot_tag_old_acpi_t) +
80486759c82SToomas Soome 			    sizeof (ACPI_RSDP_COMMON);
80514ee0d29SToomas Soome 		} else {
80614ee0d29SToomas Soome 			size += sizeof (multiboot_tag_new_acpi_t) +
80714ee0d29SToomas Soome 			    rsdp->Length;
80814ee0d29SToomas Soome 		}
80914ee0d29SToomas Soome 		size = roundup2(size, MULTIBOOT_TAG_ALIGN);
81014ee0d29SToomas Soome 	}
81186759c82SToomas Soome 	size += sizeof (multiboot_tag_t);
81214ee0d29SToomas Soome 
81314ee0d29SToomas Soome 	return (size);
81414ee0d29SToomas Soome }
81514ee0d29SToomas Soome 
816287d0250SJohn Levon #if defined(EFI)
817287d0250SJohn Levon static bool
overlaps(uintptr_t start1,size_t size1,uintptr_t start2,size_t size2)818287d0250SJohn Levon overlaps(uintptr_t start1, size_t size1, uintptr_t start2, size_t size2)
819287d0250SJohn Levon {
820287d0250SJohn Levon 	if (start1 < start2 + size2 &&
821287d0250SJohn Levon 	    start1 + size1 >= start2) {
822287d0250SJohn Levon 		printf("overlaps: %zx-%zx, %zx-%zx\n",
823287d0250SJohn Levon 		    start1, start1 + size1, start2, start2 + size2);
824287d0250SJohn Levon 		return (true);
825287d0250SJohn Levon 	}
826287d0250SJohn Levon 
827287d0250SJohn Levon 	return (false);
828287d0250SJohn Levon }
829287d0250SJohn Levon #endif
830287d0250SJohn Levon 
83114ee0d29SToomas Soome static int
multiboot2_exec(struct preloaded_file * fp)83214ee0d29SToomas Soome multiboot2_exec(struct preloaded_file *fp)
83314ee0d29SToomas Soome {
834287d0250SJohn Levon 	multiboot2_info_header_t *mbi = NULL;
83514ee0d29SToomas Soome 	struct preloaded_file *mfp;
83614ee0d29SToomas Soome 	char *cmdline = NULL;
83714ee0d29SToomas Soome 	struct devdesc *rootdev;
83814ee0d29SToomas Soome 	struct file_metadata *md;
83914ee0d29SToomas Soome 	int i, error, num;
84014ee0d29SToomas Soome 	int rootfs = 0;
84114ee0d29SToomas Soome 	size_t size;
84214ee0d29SToomas Soome 	struct bios_smap *smap;
84386759c82SToomas Soome #if defined(EFI)
844af8443c4SToomas Soome 	multiboot_tag_module_t *module, *mp;
845287d0250SJohn Levon 	struct relocator *relocator = NULL;
846f9feecc1SToomas Soome 	EFI_MEMORY_DESCRIPTOR *map;
847af8443c4SToomas Soome 	UINTN map_size, desc_size;
848f9feecc1SToomas Soome 	struct chunk_head *head;
849f9feecc1SToomas Soome 	struct chunk *chunk;
8508600fd4dSToomas Soome 	vm_offset_t tmp;
851f9feecc1SToomas Soome 
852f9feecc1SToomas Soome 	efi_getdev((void **)(&rootdev), NULL, NULL);
853287d0250SJohn Levon 
854287d0250SJohn Levon 	/*
855287d0250SJohn Levon 	 * We need 5 pages for relocation. We'll allocate from the heap: while
856287d0250SJohn Levon 	 * it's possible that our heap got placed low down enough to be in the
857287d0250SJohn Levon 	 * way of where we're going to relocate our kernel, it's hopefully not
858287d0250SJohn Levon 	 * likely.
859287d0250SJohn Levon 	 */
860287d0250SJohn Levon 	if ((relocator = malloc(EFI_PAGE_SIZE * 5)) == NULL) {
861287d0250SJohn Levon 		printf("relocator malloc failed!\n");
862287d0250SJohn Levon 		error = ENOMEM;
863287d0250SJohn Levon 		goto error;
864287d0250SJohn Levon 	}
865287d0250SJohn Levon 
866287d0250SJohn Levon 	if (overlaps((uintptr_t)relocator, EFI_PAGE_SIZE * 5,
867287d0250SJohn Levon 	    load_addr, fp->f_size)) {
868287d0250SJohn Levon 		printf("relocator pages overlap the kernel!\n");
869287d0250SJohn Levon 		error = EINVAL;
870287d0250SJohn Levon 		goto error;
871287d0250SJohn Levon 	}
872287d0250SJohn Levon 
873f9feecc1SToomas Soome #else
87414ee0d29SToomas Soome 	i386_getdev((void **)(&rootdev), NULL, NULL);
8759890ff83SToomas Soome 
8769890ff83SToomas Soome 	if (have_framebuffer == false) {
8779890ff83SToomas Soome 		/* make sure we have text mode */
8789890ff83SToomas Soome 		bios_set_text_mode(VGA_TEXT_MODE);
8799890ff83SToomas Soome 	}
880f9feecc1SToomas Soome #endif
88114ee0d29SToomas Soome 
88214ee0d29SToomas Soome 	error = EINVAL;
88314ee0d29SToomas Soome 	if (rootdev == NULL) {
88414ee0d29SToomas Soome 		printf("can't determine root device\n");
88514ee0d29SToomas Soome 		goto error;
88614ee0d29SToomas Soome 	}
88714ee0d29SToomas Soome 
88814ee0d29SToomas Soome 	/*
88914ee0d29SToomas Soome 	 * Set the image command line.
89014ee0d29SToomas Soome 	 */
89114ee0d29SToomas Soome 	if (fp->f_args == NULL) {
89214ee0d29SToomas Soome 		cmdline = getenv("boot-args");
89314ee0d29SToomas Soome 		if (cmdline != NULL) {
89414ee0d29SToomas Soome 			fp->f_args = strdup(cmdline);
89514ee0d29SToomas Soome 			if (fp->f_args == NULL) {
89614ee0d29SToomas Soome 				error = ENOMEM;
89714ee0d29SToomas Soome 				goto error;
89814ee0d29SToomas Soome 			}
89914ee0d29SToomas Soome 		}
90014ee0d29SToomas Soome 	}
90114ee0d29SToomas Soome 
90214ee0d29SToomas Soome 	error = mb_kernel_cmdline(fp, rootdev, &cmdline);
90314ee0d29SToomas Soome 	if (error != 0)
90414ee0d29SToomas Soome 		goto error;
90514ee0d29SToomas Soome 
90676608ff7SToomas Soome 	/* mb_kernel_cmdline() updates the environment. */
90776608ff7SToomas Soome 	build_environment_module();
90876608ff7SToomas Soome 
90945cf6faaSToomas Soome 	/* Pass the loaded console font for kernel. */
91045cf6faaSToomas Soome 	build_font_module();
9119890ff83SToomas Soome 
91214ee0d29SToomas Soome 	size = mbi_size(fp, cmdline);	/* Get the size for MBI. */
91314ee0d29SToomas Soome 
91414ee0d29SToomas Soome 	/* Set up the base for mb_malloc. */
915f9feecc1SToomas Soome 	i = 0;
916f9feecc1SToomas Soome 	for (mfp = fp; mfp->f_next != NULL; mfp = mfp->f_next)
917f9feecc1SToomas Soome 		i++;
918f9feecc1SToomas Soome 
91986759c82SToomas Soome #if defined(EFI)
920f9feecc1SToomas Soome 	/* We need space for kernel + MBI + # modules */
921f9feecc1SToomas Soome 	num = (EFI_PAGE_SIZE - offsetof(struct relocator, rel_chunklist)) /
922f9feecc1SToomas Soome 	    sizeof (struct chunk);
923f9feecc1SToomas Soome 	if (i + 2 >= num) {
924f9feecc1SToomas Soome 		printf("Too many modules, do not have space for relocator.\n");
925f9feecc1SToomas Soome 		error = ENOMEM;
926f9feecc1SToomas Soome 		goto error;
927f9feecc1SToomas Soome 	}
92814ee0d29SToomas Soome 
929f9feecc1SToomas Soome 	last_addr = efi_loadaddr(LOAD_MEM, &size, mfp->f_addr + mfp->f_size);
930f9feecc1SToomas Soome 	mbi = (multiboot2_info_header_t *)last_addr;
931f9feecc1SToomas Soome 	if (mbi == NULL) {
932f9feecc1SToomas Soome 		error = ENOMEM;
933f9feecc1SToomas Soome 		goto error;
934f9feecc1SToomas Soome 	}
935f9feecc1SToomas Soome 	last_addr = (vm_offset_t)mbi->mbi_tags;
936f9feecc1SToomas Soome #else
93714ee0d29SToomas Soome 	/* Start info block from the new page. */
9388600fd4dSToomas Soome 	last_addr = i386_loadaddr(LOAD_MEM, &size, mfp->f_addr + mfp->f_size);
93914ee0d29SToomas Soome 
94014ee0d29SToomas Soome 	/* Do we have space for multiboot info? */
94114ee0d29SToomas Soome 	if (last_addr + size >= memtop_copyin) {
94214ee0d29SToomas Soome 		error = ENOMEM;
94314ee0d29SToomas Soome 		goto error;
94414ee0d29SToomas Soome 	}
94514ee0d29SToomas Soome 
94614ee0d29SToomas Soome 	mbi = (multiboot2_info_header_t *)PTOV(last_addr);
94714ee0d29SToomas Soome 	last_addr = (vm_offset_t)mbi->mbi_tags;
948f9feecc1SToomas Soome #endif	/* EFI */
94914ee0d29SToomas Soome 
95014ee0d29SToomas Soome 	{
95114ee0d29SToomas Soome 		multiboot_tag_string_t *tag;
95214ee0d29SToomas Soome 		i = sizeof (multiboot_tag_string_t) + strlen(cmdline) + 1;
95386759c82SToomas Soome 		tag = (multiboot_tag_string_t *)mb_malloc(i);
95414ee0d29SToomas Soome 
95514ee0d29SToomas Soome 		tag->mb_type = MULTIBOOT_TAG_TYPE_CMDLINE;
95614ee0d29SToomas Soome 		tag->mb_size = i;
95714ee0d29SToomas Soome 		memcpy(tag->mb_string, cmdline, strlen(cmdline) + 1);
95814ee0d29SToomas Soome 		free(cmdline);
95914ee0d29SToomas Soome 		cmdline = NULL;
96014ee0d29SToomas Soome 	}
96114ee0d29SToomas Soome 
96214ee0d29SToomas Soome 	{
96314ee0d29SToomas Soome 		multiboot_tag_string_t *tag;
96414ee0d29SToomas Soome 		i = sizeof (multiboot_tag_string_t) + strlen(bootprog_info) + 1;
96586759c82SToomas Soome 		tag = (multiboot_tag_string_t *)mb_malloc(i);
96614ee0d29SToomas Soome 
96714ee0d29SToomas Soome 		tag->mb_type = MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME;
96814ee0d29SToomas Soome 		tag->mb_size = i;
96914ee0d29SToomas Soome 		memcpy(tag->mb_string, bootprog_info,
97014ee0d29SToomas Soome 		    strlen(bootprog_info) + 1);
97114ee0d29SToomas Soome 	}
97214ee0d29SToomas Soome 
97386759c82SToomas Soome #if !defined(EFI)
974f9feecc1SToomas Soome 	/* Only set in case of BIOS. */
97514ee0d29SToomas Soome 	{
97614ee0d29SToomas Soome 		multiboot_tag_basic_meminfo_t *tag;
97714ee0d29SToomas Soome 		tag = (multiboot_tag_basic_meminfo_t *)
97814ee0d29SToomas Soome 		    mb_malloc(sizeof (*tag));
97914ee0d29SToomas Soome 
98014ee0d29SToomas Soome 		tag->mb_type = MULTIBOOT_TAG_TYPE_BASIC_MEMINFO;
98114ee0d29SToomas Soome 		tag->mb_size = sizeof (*tag);
98214ee0d29SToomas Soome 		tag->mb_mem_lower = bios_basemem / 1024;
98314ee0d29SToomas Soome 		tag->mb_mem_upper = bios_extmem / 1024;
98414ee0d29SToomas Soome 	}
985f9feecc1SToomas Soome #endif
98614ee0d29SToomas Soome 
98714ee0d29SToomas Soome 	num = 0;
98814ee0d29SToomas Soome 	for (mfp = fp->f_next; mfp != NULL; mfp = mfp->f_next) {
98914ee0d29SToomas Soome 		num++;
99014ee0d29SToomas Soome 		if (mfp->f_type != NULL && strcmp(mfp->f_type, "rootfs") == 0)
99114ee0d29SToomas Soome 			rootfs++;
99214ee0d29SToomas Soome 	}
99314ee0d29SToomas Soome 
99414ee0d29SToomas Soome 	if (num == 0 || rootfs == 0) {
99514ee0d29SToomas Soome 		/* We need at least one module - rootfs. */
99614ee0d29SToomas Soome 		printf("No rootfs module provided, aborting\n");
99714ee0d29SToomas Soome 		error = EINVAL;
99814ee0d29SToomas Soome 		goto error;
99914ee0d29SToomas Soome 	}
100014ee0d29SToomas Soome 
100114ee0d29SToomas Soome 	/*
100214ee0d29SToomas Soome 	 * Set the stage for physical memory layout:
100314ee0d29SToomas Soome 	 * - We have kernel at load_addr.
100414ee0d29SToomas Soome 	 * - Modules are aligned to page boundary.
100514ee0d29SToomas Soome 	 * - MBI is aligned to page boundary.
100614ee0d29SToomas Soome 	 * - Set the tmp to point to physical address of the first module.
1007f9feecc1SToomas Soome 	 * - tmp != mfp->f_addr only in case of EFI.
100814ee0d29SToomas Soome 	 */
100986759c82SToomas Soome #if defined(EFI)
1010af8443c4SToomas Soome 	tmp = roundup2(load_addr + fp->f_size + 1, MULTIBOOT_MOD_ALIGN);
1011f9feecc1SToomas Soome 	module = (multiboot_tag_module_t *)last_addr;
1012f9feecc1SToomas Soome #endif
101314ee0d29SToomas Soome 
101414ee0d29SToomas Soome 	for (mfp = fp->f_next; mfp != NULL; mfp = mfp->f_next) {
101514ee0d29SToomas Soome 		multiboot_tag_module_t *tag;
101614ee0d29SToomas Soome 
101714ee0d29SToomas Soome 		num = strlen(mfp->f_name) + 1;
101814ee0d29SToomas Soome 		num += strlen(mfp->f_type) + 5 + 1;
101914ee0d29SToomas Soome 		if (mfp->f_args != NULL) {
102014ee0d29SToomas Soome 			num += strlen(mfp->f_args) + 1;
102114ee0d29SToomas Soome 		}
102214ee0d29SToomas Soome 		cmdline = malloc(num);
102314ee0d29SToomas Soome 		if (cmdline == NULL) {
102414ee0d29SToomas Soome 			error = ENOMEM;
102514ee0d29SToomas Soome 			goto error;
102614ee0d29SToomas Soome 		}
102714ee0d29SToomas Soome 
102814ee0d29SToomas Soome 		if (mfp->f_args != NULL)
102914ee0d29SToomas Soome 			snprintf(cmdline, num, "%s type=%s %s",
103014ee0d29SToomas Soome 			    mfp->f_name, mfp->f_type, mfp->f_args);
103114ee0d29SToomas Soome 		else
103214ee0d29SToomas Soome 			snprintf(cmdline, num, "%s type=%s",
103314ee0d29SToomas Soome 			    mfp->f_name, mfp->f_type);
103414ee0d29SToomas Soome 
103514ee0d29SToomas Soome 		tag = (multiboot_tag_module_t *)mb_malloc(sizeof (*tag) + num);
103614ee0d29SToomas Soome 
103714ee0d29SToomas Soome 		tag->mb_type = MULTIBOOT_TAG_TYPE_MODULE;
103814ee0d29SToomas Soome 		tag->mb_size = sizeof (*tag) + num;
103986759c82SToomas Soome #if defined(EFI)
1040af8443c4SToomas Soome 		/*
1041af8443c4SToomas Soome 		 * We can assign module addresses only after BS have been
1042af8443c4SToomas Soome 		 * switched off.
1043af8443c4SToomas Soome 		 */
1044af8443c4SToomas Soome 		tag->mb_mod_start = 0;
1045af8443c4SToomas Soome 		tag->mb_mod_end = mfp->f_size;
10468600fd4dSToomas Soome #else
10478600fd4dSToomas Soome 		tag->mb_mod_start = mfp->f_addr;
10488600fd4dSToomas Soome 		tag->mb_mod_end = mfp->f_addr + mfp->f_size;
10498600fd4dSToomas Soome #endif
105014ee0d29SToomas Soome 		memcpy(tag->mb_cmdline, cmdline, num);
105114ee0d29SToomas Soome 		free(cmdline);
105214ee0d29SToomas Soome 		cmdline = NULL;
105314ee0d29SToomas Soome 	}
105414ee0d29SToomas Soome 
105514ee0d29SToomas Soome 	md = file_findmetadata(fp, MODINFOMD_SMAP);
105614ee0d29SToomas Soome 	if (md == NULL) {
105714ee0d29SToomas Soome 		printf("no memory smap\n");
105814ee0d29SToomas Soome 		error = EINVAL;
105914ee0d29SToomas Soome 		goto error;
106014ee0d29SToomas Soome 	}
106114ee0d29SToomas Soome 
106214ee0d29SToomas Soome 	smap = (struct bios_smap *)md->md_data;
106386759c82SToomas Soome 	num = md->md_size / sizeof (struct bios_smap); /* number of entries */
106414ee0d29SToomas Soome 
106514ee0d29SToomas Soome 	{
106614ee0d29SToomas Soome 		multiboot_tag_mmap_t *tag;
106714ee0d29SToomas Soome 		multiboot_mmap_entry_t *mmap_entry;
106814ee0d29SToomas Soome 
106914ee0d29SToomas Soome 		tag = (multiboot_tag_mmap_t *)
107014ee0d29SToomas Soome 		    mb_malloc(sizeof (*tag) +
107114ee0d29SToomas Soome 		    num * sizeof (multiboot_mmap_entry_t));
107214ee0d29SToomas Soome 
107314ee0d29SToomas Soome 		tag->mb_type = MULTIBOOT_TAG_TYPE_MMAP;
107414ee0d29SToomas Soome 		tag->mb_size = sizeof (*tag) +
107514ee0d29SToomas Soome 		    num * sizeof (multiboot_mmap_entry_t);
107614ee0d29SToomas Soome 		tag->mb_entry_size = sizeof (multiboot_mmap_entry_t);
107714ee0d29SToomas Soome 		tag->mb_entry_version = 0;
107814ee0d29SToomas Soome 		mmap_entry = (multiboot_mmap_entry_t *)tag->mb_entries;
107914ee0d29SToomas Soome 
108014ee0d29SToomas Soome 		for (i = 0; i < num; i++) {
108114ee0d29SToomas Soome 			mmap_entry[i].mmap_addr = smap[i].base;
108214ee0d29SToomas Soome 			mmap_entry[i].mmap_len = smap[i].length;
108314ee0d29SToomas Soome 			mmap_entry[i].mmap_type = smap[i].type;
108414ee0d29SToomas Soome 			mmap_entry[i].mmap_reserved = 0;
108514ee0d29SToomas Soome 		}
108614ee0d29SToomas Soome 	}
108714ee0d29SToomas Soome 
1088f9feecc1SToomas Soome 	if (bootp_response != NULL) {
108914ee0d29SToomas Soome 		multiboot_tag_network_t *tag;
109014ee0d29SToomas Soome 		tag = (multiboot_tag_network_t *)
109186759c82SToomas Soome 		    mb_malloc(sizeof (*tag) + bootp_response_size);
109214ee0d29SToomas Soome 
109314ee0d29SToomas Soome 		tag->mb_type = MULTIBOOT_TAG_TYPE_NETWORK;
109486759c82SToomas Soome 		tag->mb_size = sizeof (*tag) + bootp_response_size;
1095859472daSToomas Soome 		memcpy(tag->mb_dhcpack, bootp_response, bootp_response_size);
109614ee0d29SToomas Soome 	}
109714ee0d29SToomas Soome 
109886759c82SToomas Soome #if !defined(EFI)
109986759c82SToomas Soome 	multiboot_tag_vbe_t *tag;
110086759c82SToomas Soome 	extern multiboot_tag_vbe_t vbestate;
110186759c82SToomas Soome 
110286759c82SToomas Soome 	if (VBE_VALID_MODE(vbestate.vbe_mode)) {
110386759c82SToomas Soome 		tag = (multiboot_tag_vbe_t *)mb_malloc(sizeof (*tag));
110486759c82SToomas Soome 		memcpy(tag, &vbestate, sizeof (*tag));
110586759c82SToomas Soome 		tag->mb_type = MULTIBOOT_TAG_TYPE_VBE;
110686759c82SToomas Soome 		tag->mb_size = sizeof (*tag);
11079890ff83SToomas Soome 	}
11089890ff83SToomas Soome #endif
11099890ff83SToomas Soome 
111014ee0d29SToomas Soome 	if (rsdp != NULL) {
111114ee0d29SToomas Soome 		multiboot_tag_new_acpi_t *ntag;
111214ee0d29SToomas Soome 		multiboot_tag_old_acpi_t *otag;
1113d9e07fb5SToomas Soome 		uint32_t tsize;
111414ee0d29SToomas Soome 
111514ee0d29SToomas Soome 		if (rsdp->Revision == 0) {
1116d9e07fb5SToomas Soome 			tsize = sizeof (*otag) + sizeof (ACPI_RSDP_COMMON);
1117d9e07fb5SToomas Soome 			otag = (multiboot_tag_old_acpi_t *)mb_malloc(tsize);
111814ee0d29SToomas Soome 			otag->mb_type = MULTIBOOT_TAG_TYPE_ACPI_OLD;
1119d9e07fb5SToomas Soome 			otag->mb_size = tsize;
112014ee0d29SToomas Soome 			memcpy(otag->mb_rsdp, rsdp, sizeof (ACPI_RSDP_COMMON));
112114ee0d29SToomas Soome 		} else {
1122d9e07fb5SToomas Soome 			tsize = sizeof (*ntag) + rsdp->Length;
1123d9e07fb5SToomas Soome 			ntag = (multiboot_tag_new_acpi_t *)mb_malloc(tsize);
112414ee0d29SToomas Soome 			ntag->mb_type = MULTIBOOT_TAG_TYPE_ACPI_NEW;
1125d9e07fb5SToomas Soome 			ntag->mb_size = tsize;
112614ee0d29SToomas Soome 			memcpy(ntag->mb_rsdp, rsdp, rsdp->Length);
112714ee0d29SToomas Soome 		}
112814ee0d29SToomas Soome 	}
112914ee0d29SToomas Soome 
113086759c82SToomas Soome #if defined(EFI)
113183b4671eSToomas Soome #ifdef  __LP64__
1132f9feecc1SToomas Soome 	{
1133f9feecc1SToomas Soome 		multiboot_tag_efi64_t *tag;
1134f9feecc1SToomas Soome 		tag = (multiboot_tag_efi64_t *)
1135f9feecc1SToomas Soome 		    mb_malloc(sizeof (*tag));
1136f9feecc1SToomas Soome 
1137f9feecc1SToomas Soome 		tag->mb_type = MULTIBOOT_TAG_TYPE_EFI64;
1138f9feecc1SToomas Soome 		tag->mb_size = sizeof (*tag);
1139f9feecc1SToomas Soome 		tag->mb_pointer = (uint64_t)(uintptr_t)ST;
1140f9feecc1SToomas Soome 	}
114183b4671eSToomas Soome #else
114283b4671eSToomas Soome 	{
114383b4671eSToomas Soome 		multiboot_tag_efi32_t *tag;
114483b4671eSToomas Soome 		tag = (multiboot_tag_efi32_t *)
114583b4671eSToomas Soome 		    mb_malloc(sizeof (*tag));
114683b4671eSToomas Soome 
114783b4671eSToomas Soome 		tag->mb_type = MULTIBOOT_TAG_TYPE_EFI32;
114883b4671eSToomas Soome 		tag->mb_size = sizeof (*tag);
114983b4671eSToomas Soome 		tag->mb_pointer = (uint32_t)ST;
115083b4671eSToomas Soome 	}
115183b4671eSToomas Soome #endif /* __LP64__ */
11529890ff83SToomas Soome #endif /* EFI */
1153f9feecc1SToomas Soome 
1154f9feecc1SToomas Soome 	if (have_framebuffer == true) {
1155f9feecc1SToomas Soome 		multiboot_tag_framebuffer_t *tag;
11569890ff83SToomas Soome 		extern multiboot_tag_framebuffer_t gfx_fb;
115786759c82SToomas Soome #if defined(EFI)
11589890ff83SToomas Soome 
115986759c82SToomas Soome 		tag = (multiboot_tag_framebuffer_t *)mb_malloc(sizeof (*tag));
11609890ff83SToomas Soome 		memcpy(tag, &gfx_fb, sizeof (*tag));
11619890ff83SToomas Soome 		tag->framebuffer_common.mb_type =
11629890ff83SToomas Soome 		    MULTIBOOT_TAG_TYPE_FRAMEBUFFER;
11639890ff83SToomas Soome 		tag->framebuffer_common.mb_size = sizeof (*tag);
11649890ff83SToomas Soome #else
11659890ff83SToomas Soome 		extern multiboot_color_t *cmap;
11669890ff83SToomas Soome 		uint32_t size;
11679890ff83SToomas Soome 
11689890ff83SToomas Soome 		if (gfx_fb.framebuffer_common.framebuffer_type ==
11699890ff83SToomas Soome 		    MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED) {
11709890ff83SToomas Soome 			uint16_t nc;
11719890ff83SToomas Soome 			nc = gfx_fb.u.fb1.framebuffer_palette_num_colors;
11729890ff83SToomas Soome 			size = sizeof (struct multiboot_tag_framebuffer_common)
11739890ff83SToomas Soome 			    + sizeof (nc)
11749890ff83SToomas Soome 			    + nc * sizeof (multiboot_color_t);
11759890ff83SToomas Soome 		} else {
11769890ff83SToomas Soome 			size = sizeof (gfx_fb);
11779890ff83SToomas Soome 		}
11789890ff83SToomas Soome 
117986759c82SToomas Soome 		tag = (multiboot_tag_framebuffer_t *)mb_malloc(size);
11809890ff83SToomas Soome 		memcpy(tag, &gfx_fb, sizeof (*tag));
11819890ff83SToomas Soome 
11829890ff83SToomas Soome 		tag->framebuffer_common.mb_type =
11839890ff83SToomas Soome 		    MULTIBOOT_TAG_TYPE_FRAMEBUFFER;
11849890ff83SToomas Soome 		tag->framebuffer_common.mb_size = size;
11859890ff83SToomas Soome 
11869890ff83SToomas Soome 		if (gfx_fb.framebuffer_common.framebuffer_type ==
11879890ff83SToomas Soome 		    MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED) {
118891061836SToomas Soome 			gfx_fb.u.fb1.framebuffer_palette_num_colors = CMAP_SIZE;
118991061836SToomas Soome 
11909890ff83SToomas Soome 			memcpy(tag->u.fb1.framebuffer_palette, cmap,
119191061836SToomas Soome 			    sizeof (multiboot_color_t) * CMAP_SIZE);
1192f9feecc1SToomas Soome 		}
11939890ff83SToomas Soome #endif /* EFI */
1194f9feecc1SToomas Soome 	}
1195f9feecc1SToomas Soome 
119686759c82SToomas Soome #if defined(EFI)
1197f9feecc1SToomas Soome 	/* Leave EFI memmap last as we will also switch off the BS. */
1198f9feecc1SToomas Soome 	{
1199f9feecc1SToomas Soome 		multiboot_tag_efi_mmap_t *tag;
1200af8443c4SToomas Soome 		UINTN key;
1201f9feecc1SToomas Soome 		EFI_STATUS status;
1202f9feecc1SToomas Soome 
1203f9feecc1SToomas Soome 		tag = (multiboot_tag_efi_mmap_t *)
1204f9feecc1SToomas Soome 		    mb_malloc(sizeof (*tag));
1205f9feecc1SToomas Soome 
1206af8443c4SToomas Soome 		map_size = 0;
1207af8443c4SToomas Soome 		status = BS->GetMemoryMap(&map_size,
1208f9feecc1SToomas Soome 		    (EFI_MEMORY_DESCRIPTOR *)tag->mb_efi_mmap, &key,
1209f9feecc1SToomas Soome 		    &desc_size, &tag->mb_descr_vers);
1210f9feecc1SToomas Soome 		if (status != EFI_BUFFER_TOO_SMALL) {
1211f9feecc1SToomas Soome 			error = EINVAL;
1212f9feecc1SToomas Soome 			goto error;
1213f9feecc1SToomas Soome 		}
121415ab2c88SToomas Soome 		map_size = roundup2(map_size, EFI_PAGE_SIZE);
121515ab2c88SToomas Soome 
121615ab2c88SToomas Soome 		i = 2;	/* Attempts to ExitBootServices() */
121715ab2c88SToomas Soome 		while (map_size <= efi_map_size && i > 0) {
121815ab2c88SToomas Soome 			status = BS->GetMemoryMap(&map_size,
121915ab2c88SToomas Soome 			    (EFI_MEMORY_DESCRIPTOR *)tag->mb_efi_mmap, &key,
122015ab2c88SToomas Soome 			    &desc_size, &tag->mb_descr_vers);
122115ab2c88SToomas Soome 			if (status == EFI_BUFFER_TOO_SMALL) {
122215ab2c88SToomas Soome 				/* Still too small? */
122315ab2c88SToomas Soome 				map_size += EFI_PAGE_SIZE;
122415ab2c88SToomas Soome 				continue;
122515ab2c88SToomas Soome 			}
122615ab2c88SToomas Soome 			if (EFI_ERROR(status)) {
122715ab2c88SToomas Soome 				error = EINVAL;
122815ab2c88SToomas Soome 				goto error;
122915ab2c88SToomas Soome 			}
123015ab2c88SToomas Soome 
123115ab2c88SToomas Soome 			if (keep_bs != 0)
123215ab2c88SToomas Soome 				break;
123315ab2c88SToomas Soome 
123415ab2c88SToomas Soome 			status = BS->ExitBootServices(IH, key);
1235190f051bSToomas Soome 			if (status == EFI_SUCCESS) {
1236190f051bSToomas Soome 				has_boot_services = false;
123715ab2c88SToomas Soome 				break;
1238190f051bSToomas Soome 			}
123915ab2c88SToomas Soome 			i--;
124015ab2c88SToomas Soome 		}
124115ab2c88SToomas Soome 		if (status != EFI_SUCCESS) {
1242f9feecc1SToomas Soome 			error = EINVAL;
1243f9feecc1SToomas Soome 			goto error;
1244f9feecc1SToomas Soome 		}
124515ab2c88SToomas Soome 
1246f9feecc1SToomas Soome 		tag->mb_type = MULTIBOOT_TAG_TYPE_EFI_MMAP;
1247af8443c4SToomas Soome 		tag->mb_size = sizeof (*tag) + map_size;
124886759c82SToomas Soome 		tag->mb_descr_size = (uint32_t)desc_size;
1249f9feecc1SToomas Soome 
1250287d0250SJohn Levon 		map = (EFI_MEMORY_DESCRIPTOR *)tag->mb_efi_mmap;
1251f9feecc1SToomas Soome 
1252af8443c4SToomas Soome 		last_addr += map_size;
1253f9feecc1SToomas Soome 		last_addr = roundup2(last_addr, MULTIBOOT_TAG_ALIGN);
1254f9feecc1SToomas Soome 	}
12559890ff83SToomas Soome #endif /* EFI */
1256f9feecc1SToomas Soome 
125714ee0d29SToomas Soome 	/*
125814ee0d29SToomas Soome 	 * MB tag list end marker.
125914ee0d29SToomas Soome 	 */
126014ee0d29SToomas Soome 	{
126114ee0d29SToomas Soome 		multiboot_tag_t *tag = (multiboot_tag_t *)
126286759c82SToomas Soome 		    mb_malloc(sizeof (*tag));
126314ee0d29SToomas Soome 		tag->mb_type = MULTIBOOT_TAG_TYPE_END;
126486759c82SToomas Soome 		tag->mb_size = sizeof (*tag);
126514ee0d29SToomas Soome 	}
126614ee0d29SToomas Soome 
126714ee0d29SToomas Soome 	mbi->mbi_total_size = last_addr - (vm_offset_t)mbi;
126814ee0d29SToomas Soome 	mbi->mbi_reserved = 0;
126914ee0d29SToomas Soome 
127086759c82SToomas Soome #if defined(EFI)
127186759c82SToomas Soome 	/*
127286759c82SToomas Soome 	 * At this point we have load_addr pointing to kernel load
1273f9feecc1SToomas Soome 	 * address, module list in MBI having physical addresses,
1274f9feecc1SToomas Soome 	 * module list in fp having logical addresses and tmp pointing to
1275f9feecc1SToomas Soome 	 * physical address for MBI.
1276f9feecc1SToomas Soome 	 * Now we must move all pieces to place and start the kernel.
1277f9feecc1SToomas Soome 	 */
1278f9feecc1SToomas Soome 	head = &relocator->rel_chunk_head;
1279f9feecc1SToomas Soome 	STAILQ_INIT(head);
1280f9feecc1SToomas Soome 
1281f9feecc1SToomas Soome 	i = 0;
1282f9feecc1SToomas Soome 	chunk = &relocator->rel_chunklist[i++];
1283f9feecc1SToomas Soome 	chunk->chunk_vaddr = fp->f_addr;
1284f9feecc1SToomas Soome 	chunk->chunk_paddr = load_addr;
1285f9feecc1SToomas Soome 	chunk->chunk_size = fp->f_size;
1286f9feecc1SToomas Soome 
1287f9feecc1SToomas Soome 	STAILQ_INSERT_TAIL(head, chunk, chunk_next);
1288f9feecc1SToomas Soome 
1289af8443c4SToomas Soome 	mp = module;
1290f9feecc1SToomas Soome 	for (mfp = fp->f_next; mfp != NULL; mfp = mfp->f_next) {
1291f9feecc1SToomas Soome 		chunk = &relocator->rel_chunklist[i++];
1292f9feecc1SToomas Soome 		chunk->chunk_vaddr = mfp->f_addr;
1293af8443c4SToomas Soome 
1294af8443c4SToomas Soome 		/*
1295af8443c4SToomas Soome 		 * fix the mb_mod_start and mb_mod_end.
1296af8443c4SToomas Soome 		 */
1297af8443c4SToomas Soome 		mp->mb_mod_start = efi_physaddr(module, tmp, map,
12985388916eSToomas Soome 		    map_size / desc_size, desc_size, mfp->f_addr,
12995388916eSToomas Soome 		    mp->mb_mod_end);
1300af8443c4SToomas Soome 		if (mp->mb_mod_start == 0)
130196cf0467SToomas Soome 			panic("Could not find memory for module");
1302af8443c4SToomas Soome 
1303af8443c4SToomas Soome 		mp->mb_mod_end += mp->mb_mod_start;
1304af8443c4SToomas Soome 		chunk->chunk_paddr = mp->mb_mod_start;
1305f9feecc1SToomas Soome 		chunk->chunk_size = mfp->f_size;
1306f9feecc1SToomas Soome 		STAILQ_INSERT_TAIL(head, chunk, chunk_next);
1307f9feecc1SToomas Soome 
1308af8443c4SToomas Soome 		mp = (multiboot_tag_module_t *)
1309af8443c4SToomas Soome 		    roundup2((uintptr_t)mp + mp->mb_size,
1310f9feecc1SToomas Soome 		    MULTIBOOT_TAG_ALIGN);
1311f9feecc1SToomas Soome 	}
1312f9feecc1SToomas Soome 	chunk = &relocator->rel_chunklist[i++];
131383b4671eSToomas Soome 	chunk->chunk_vaddr = (EFI_VIRTUAL_ADDRESS)(uintptr_t)mbi;
1314af8443c4SToomas Soome 	chunk->chunk_paddr = efi_physaddr(module, tmp, map,
13155388916eSToomas Soome 	    map_size / desc_size, desc_size, (uintptr_t)mbi,
13165388916eSToomas Soome 	    mbi->mbi_total_size);
1317f9feecc1SToomas Soome 	chunk->chunk_size = mbi->mbi_total_size;
1318f9feecc1SToomas Soome 	STAILQ_INSERT_TAIL(head, chunk, chunk_next);
1319f9feecc1SToomas Soome 
1320f9feecc1SToomas Soome 	trampoline = (void *)(uintptr_t)relocator + EFI_PAGE_SIZE;
1321f9feecc1SToomas Soome 	memmove(trampoline, multiboot_tramp, EFI_PAGE_SIZE);
1322f9feecc1SToomas Soome 
1323f9feecc1SToomas Soome 	relocator->rel_copy = (uintptr_t)trampoline + EFI_PAGE_SIZE;
1324f9feecc1SToomas Soome 	memmove((void *)relocator->rel_copy, efi_copy_finish, EFI_PAGE_SIZE);
1325f9feecc1SToomas Soome 
1326f9feecc1SToomas Soome 	relocator->rel_memmove = (uintptr_t)relocator->rel_copy + EFI_PAGE_SIZE;
1327f9feecc1SToomas Soome 	memmove((void *)relocator->rel_memmove, memmove, EFI_PAGE_SIZE);
1328f9feecc1SToomas Soome 	relocator->rel_stack = relocator->rel_memmove + EFI_PAGE_SIZE - 8;
1329f9feecc1SToomas Soome 
1330f9feecc1SToomas Soome 	trampoline(MULTIBOOT2_BOOTLOADER_MAGIC, relocator, entry_addr);
1331f9feecc1SToomas Soome #else
133214ee0d29SToomas Soome 	dev_cleanup();
133314ee0d29SToomas Soome 	__exec((void *)VTOP(multiboot_tramp), MULTIBOOT2_BOOTLOADER_MAGIC,
133414ee0d29SToomas Soome 	    (void *)entry_addr, (void *)VTOP(mbi));
13359890ff83SToomas Soome #endif /* EFI */
133614ee0d29SToomas Soome 	panic("exec returned");
133714ee0d29SToomas Soome 
133814ee0d29SToomas Soome error:
1339287d0250SJohn Levon 	free(cmdline);
1340287d0250SJohn Levon 
134186759c82SToomas Soome #if defined(EFI)
1342287d0250SJohn Levon 	free(relocator);
1343287d0250SJohn Levon 
1344f9feecc1SToomas Soome 	if (mbi != NULL)
134583b4671eSToomas Soome 		efi_free_loadaddr((vm_offset_t)mbi, EFI_SIZE_TO_PAGES(size));
1346f9feecc1SToomas Soome #endif
1347287d0250SJohn Levon 
134814ee0d29SToomas Soome 	return (error);
134914ee0d29SToomas Soome }
1350