xref: /titanic_51/usr/src/boot/sys/boot/common/multiboot2.c (revision 8ede96251e3dca657102f3a32187d4382bda2ba4)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2017 Toomas Soome <tsoome@me.com>
14  */
15 
16 /*
17  * This module adds support for loading and booting illumos multiboot2
18  * kernel. This code is only built to support the illumos kernel, it does
19  * not support xen.
20  */
21 #include <sys/cdefs.h>
22 #include <sys/stddef.h>
23 
24 #include <sys/param.h>
25 #include <sys/exec.h>
26 #include <sys/linker.h>
27 #include <sys/module.h>
28 #include <sys/stdint.h>
29 #include <sys/multiboot2.h>
30 #include <stand.h>
31 #include <stdbool.h>
32 #include "libzfs.h"
33 
34 #include "bootstrap.h"
35 
36 #include <machine/metadata.h>
37 #include <machine/pc/bios.h>
38 
39 #include "../i386/libi386/libi386.h"
40 #include "../i386/btx/lib/btxv86.h"
41 #include "pxe.h"
42 
43 extern BOOTPLAYER bootplayer;	/* dhcp info */
44 extern void multiboot_tramp();
45 
46 #include "platform/acfreebsd.h"
47 #include "acconfig.h"
48 #define ACPI_SYSTEM_XFACE
49 #include "actypes.h"
50 #include "actbl.h"
51 
52 extern ACPI_TABLE_RSDP *rsdp;
53 
54 /* MB data heap pointer. */
55 static vm_offset_t last_addr;
56 extern char bootprog_info[];
57 
58 extern int elf32_loadfile_raw(char *filename, u_int64_t dest,
59     struct preloaded_file **result, int multiboot);
60 static int multiboot2_loadfile(char *, u_int64_t, struct preloaded_file **);
61 static int multiboot2_exec(struct preloaded_file *);
62 
63 struct file_format multiboot2 = { multiboot2_loadfile, multiboot2_exec };
64 static bool keep_bs = false;
65 static bool have_framebuffer = false;
66 static vm_offset_t load_addr;
67 static vm_offset_t entry_addr;
68 
69 /*
70  * Validate tags in info request. This function is provided just to
71  * recognize the current tag list and only serves as a limited
72  * safe guard against possibly corrupt information.
73  */
74 static bool
75 is_info_request_valid(multiboot_header_tag_information_request_t *rtag)
76 {
77 	int i;
78 
79 	/*
80 	 * If the tag is optional and we do not support it, we do not
81 	 * have to do anything special, so we skip optional tags.
82 	 */
83 	if (rtag->mbh_flags & MULTIBOOT_HEADER_TAG_OPTIONAL)
84 		return (true);
85 
86 	for (i = 0; i < (rtag->mbh_size - sizeof (*rtag)) /
87 	    sizeof (rtag->mbh_requests[0]); i++)
88 		switch (rtag->mbh_requests[i]) {
89 		case MULTIBOOT_TAG_TYPE_END:
90 		case MULTIBOOT_TAG_TYPE_CMDLINE:
91 		case MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME:
92 		case MULTIBOOT_TAG_TYPE_MODULE:
93 		case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO:
94 		case MULTIBOOT_TAG_TYPE_BOOTDEV:
95 		case MULTIBOOT_TAG_TYPE_MMAP:
96 		case MULTIBOOT_TAG_TYPE_FRAMEBUFFER:
97 		case MULTIBOOT_TAG_TYPE_VBE:
98 		case MULTIBOOT_TAG_TYPE_ELF_SECTIONS:
99 		case MULTIBOOT_TAG_TYPE_APM:
100 		case MULTIBOOT_TAG_TYPE_EFI32:
101 		case MULTIBOOT_TAG_TYPE_EFI64:
102 		case MULTIBOOT_TAG_TYPE_ACPI_OLD:
103 		case MULTIBOOT_TAG_TYPE_ACPI_NEW:
104 		case MULTIBOOT_TAG_TYPE_NETWORK:
105 		case MULTIBOOT_TAG_TYPE_EFI_MMAP:
106 		case MULTIBOOT_TAG_TYPE_EFI_BS:
107 		case MULTIBOOT_TAG_TYPE_EFI32_IH:
108 		case MULTIBOOT_TAG_TYPE_EFI64_IH:
109 		case MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR:
110 			break;
111 		default:
112 			printf("unsupported information tag: 0x%x\n",
113 			    rtag->mbh_requests[i]);
114 			return (false);
115 		}
116 	return (true);
117 }
118 
119 static int
120 multiboot2_loadfile(char *filename, u_int64_t dest,
121     struct preloaded_file **result)
122 {
123 	int fd, error;
124 	uint32_t i;
125 	struct stat st;
126 	caddr_t header_search;
127 	multiboot2_header_t *header;
128 	multiboot_header_tag_t *tag;
129 	multiboot_header_tag_address_t *addr_tag = NULL;
130 	multiboot_header_tag_entry_address_t *entry_tag = NULL;
131 	struct preloaded_file *fp;
132 
133 	/* This allows to check other file formats from file_formats array. */
134 	error = EFTYPE;
135 	if (filename == NULL)
136 		return (error);
137 
138 	/* is kernel already loaded? */
139 	fp = file_findfile(NULL, NULL);
140 	if (fp != NULL)
141 		return (error);
142 
143 	if ((fd = open(filename, O_RDONLY)) == -1)
144 		return (errno);
145 
146 	/*
147 	 * Read MULTIBOOT_SEARCH size in order to search for the
148 	 * multiboot magic header.
149 	 */
150 	header_search = malloc(MULTIBOOT_SEARCH);
151 	if (header_search == NULL) {
152 		close(fd);
153 		return (ENOMEM);
154 	}
155 
156 	if (read(fd, header_search, MULTIBOOT_SEARCH) != MULTIBOOT_SEARCH)
157 		goto out;
158 
159 	header = NULL;
160 	for (i = 0; i <= (MULTIBOOT_SEARCH - sizeof (multiboot2_header_t));
161 	    i += MULTIBOOT_HEADER_ALIGN) {
162 		header = (multiboot2_header_t *)(header_search + i);
163 
164 		/* Do we have match on magic? */
165 		if (header->mb2_magic != MULTIBOOT2_HEADER_MAGIC) {
166 			header = NULL;
167 			continue;
168 		}
169 		/*
170 		 * Validate checksum, the sum of magic + architecture +
171 		 * header_length + checksum must equal 0.
172 		 */
173 		if (header->mb2_magic + header->mb2_architecture +
174 		    header->mb2_header_length + header->mb2_checksum != 0) {
175 			header = NULL;
176 			continue;
177 		}
178 		/*
179 		 * Finally, the entire header must fit within MULTIBOOT_SEARCH.
180 		 */
181 		if (i + header->mb2_header_length > MULTIBOOT_SEARCH) {
182 			header = NULL;
183 			continue;
184 		}
185 		break;
186 	}
187 
188 	if (header == NULL)
189 		goto out;
190 
191 	for (tag = header->mb2_tags; tag->mbh_type != MULTIBOOT_TAG_TYPE_END;
192 	    tag = (multiboot_header_tag_t *)((uintptr_t)tag +
193 	    roundup2(tag->mbh_size, MULTIBOOT_TAG_ALIGN))) {
194 		switch (tag->mbh_type) {
195 		case MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST:
196 			if (is_info_request_valid((void*)tag) == false)
197 				goto out;
198 			break;
199 		case MULTIBOOT_HEADER_TAG_ADDRESS:
200 			addr_tag = (multiboot_header_tag_address_t *)tag;
201 			break;
202 		case MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS:
203 			entry_tag =
204 			    (multiboot_header_tag_entry_address_t *)tag;
205 			break;
206 		case MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS:
207 			break;
208 		case MULTIBOOT_HEADER_TAG_FRAMEBUFFER:
209 			have_framebuffer = true;
210 			break;
211 		case MULTIBOOT_HEADER_TAG_MODULE_ALIGN:
212 			/* we always align modules */
213 			break;
214 		case MULTIBOOT_HEADER_TAG_EFI_BS:
215 			keep_bs = true;
216 			break;
217 		default:
218 			if (!(tag->mbh_flags & MULTIBOOT_HEADER_TAG_OPTIONAL)) {
219 				printf("unsupported tag: 0x%x\n",
220 				    tag->mbh_type);
221 				goto out;
222 			}
223 		}
224 	}
225 
226 	/*
227 	 * We must have addr_tag and entry_tag to load a 64-bit kernel.
228 	 * If these tags are missing, we either have a 32-bit kernel, or
229 	 * this is not our kernel at all.
230 	 */
231 	if (addr_tag != NULL && entry_tag != NULL) {
232 		fp = file_alloc();
233 		if (fp == NULL) {
234 			error = ENOMEM;
235 			goto out;
236 		}
237 		if (lseek(fd, 0, SEEK_SET) == -1) {
238 			printf("lseek failed\n");
239 			error = EIO;
240 			file_discard(fp);
241 			goto out;
242 		}
243 		if (fstat(fd, &st) < 0) {
244 			printf("fstat failed\n");
245 			error = EIO;
246 			file_discard(fp);
247 			goto out;
248 		}
249 
250 		load_addr = addr_tag->mbh_load_addr;
251 		entry_addr = entry_tag->mbh_entry_addr;
252 		fp->f_addr = archsw.arch_loadaddr(LOAD_KERN, filename,
253 		    addr_tag->mbh_load_addr);
254 		if (fp->f_addr == 0) {
255 			error = ENOMEM;
256 			file_discard(fp);
257 			goto out;
258 		}
259 		fp->f_size = archsw.arch_readin(fd, fp->f_addr, st.st_size);
260 
261 		if (fp->f_size != st.st_size) {
262 			printf("error reading: %s", strerror(errno));
263 			file_discard(fp);
264 			error = EIO;
265 			goto out;
266 		}
267 
268 		fp->f_name = strdup(filename);
269 		fp->f_type = strdup("aout multiboot2 kernel");
270 		if (fp->f_name == NULL || fp->f_type == NULL) {
271 			error = ENOMEM;
272 			file_discard(fp);
273 			goto out;
274 		}
275 
276 		fp->f_metadata = NULL;
277 		error = 0;
278 	} else {
279 		/* elf32_loadfile_raw will fill the attributes in fp. */
280 		error = elf32_loadfile_raw(filename, dest, &fp, 2);
281 		if (error != 0) {
282 			printf("elf32_loadfile_raw failed: %d unable to "
283 			    "load multiboot2 kernel\n", error);
284 			goto out;
285 		}
286 		entry_addr = fp->f_addr;
287 		/*
288 		 * We want the load_addr to have some legal value,
289 		 * so we set it same as the entry_addr.
290 		 * The distinction is important with UEFI, but not
291 		 * with BIOS version, because BIOS version does not use
292 		 * staging area.
293 		 */
294 		load_addr = fp->f_addr;
295 	}
296 
297 	setenv("kernelname", fp->f_name, 1);
298 	bios_addsmapdata(fp);
299 	*result = fp;
300 out:
301 	free(header_search);
302 	close(fd);
303 	return (error);
304 }
305 
306 /*
307  * Search the command line for named property.
308  *
309  * Return codes:
310  *	0	The name is found, we return the data in value and len.
311  *	ENOENT	The name is not found.
312  *	EINVAL	The provided command line is badly formed.
313  */
314 static int
315 find_property_value(const char *cmd, const char *name, const char **value,
316     size_t *len)
317 {
318 	const char *namep, *valuep;
319 	size_t name_len, value_len;
320 	int quoted;
321 
322 	*value = NULL;
323 	*len = 0;
324 
325 	if (cmd == NULL)
326 		return (ENOENT);
327 
328 	while (*cmd != '\0') {
329 		if (cmd[0] != '-' || cmd[1] != 'B') {
330 			cmd++;
331 			continue;
332 		}
333 		cmd += 2;	/* Skip -B */
334 		while (cmd[0] == ' ' || cmd[0] == '\t')
335 			cmd++;	/* Skip whitespaces. */
336 		while (*cmd != '\0' && cmd[0] != ' ' && cmd[0] != '\t') {
337 			namep = cmd;
338 			valuep = strchr(cmd, '=');
339 			if (valuep == NULL)
340 				break;
341 			name_len = valuep - namep;
342 			valuep++;
343 			value_len = 0;
344 			quoted = 0;
345 			for (; ; ++value_len) {
346 				if (valuep[value_len] == '\0')
347 					break;
348 
349 				/* Is this value quoted? */
350 				if (value_len == 0 &&
351 				    (valuep[0] == '\'' || valuep[0] == '"')) {
352 					quoted = valuep[0];
353 					++value_len;
354 				}
355 
356 				/*
357 				 * In the quote accept any character,
358 				 * but look for ending quote.
359 				 */
360 				if (quoted != 0) {
361 					if (valuep[value_len] == quoted)
362 						quoted = 0;
363 					continue;
364 				}
365 
366 				/* A comma or white space ends the value. */
367 				if (valuep[value_len] == ',' ||
368 				    valuep[value_len] == ' ' ||
369 				    valuep[value_len] == '\t')
370 					break;
371 			}
372 			if (quoted != 0) {
373 				printf("Missing closing '%c' in \"%s\"\n",
374 				    quoted, valuep);
375 				return (EINVAL);
376 			}
377 			if (value_len != 0) {
378 				if (strncmp(namep, name, name_len) == 0) {
379 					*value = valuep;
380 					*len = value_len;
381 					return (0);
382 				}
383 			}
384 			cmd = valuep + value_len;
385 			while (*cmd == ',')
386 				cmd++;
387 		}
388 	}
389 	return (ENOENT);
390 }
391 
392 /*
393  * If command line has " -B ", insert property after "-B ", otherwise
394  * append to command line.
395  */
396 static char *
397 insert_cmdline(const char *head, const char *prop)
398 {
399 	const char *prop_opt = " -B ";
400 	char *cmdline, *tail;
401 	int len = 0;
402 
403 	tail = strstr(head, prop_opt);
404 	if (tail != NULL) {
405 		ptrdiff_t diff;
406 		tail += strlen(prop_opt);
407 		diff = tail - head;
408 		if (diff >= INT_MAX)
409 			return (NULL);
410 		len = (int)diff;
411 	}
412 
413 	if (tail == NULL)
414 		asprintf(&cmdline, "%s%s%s", head, prop_opt, prop);
415 	else
416 		asprintf(&cmdline, "%.*s%s,%s", len, head, prop, tail);
417 
418 	return (cmdline);
419 }
420 
421 /*
422  * Since we have no way to pass the environment to the mb1 kernel other than
423  * through arguments, we need to take care of console setup.
424  *
425  * If the console is in mirror mode, set the kernel console from $os_console.
426  * If it's unset, use first item from $console.
427  * If $console is "ttyX", also pass $ttyX-mode, since it may have been set by
428  * the user.
429  *
430  * In case of memory allocation errors, just return the original command line
431  * so we have a chance of booting.
432  *
433  * On success, cl will be freed and a new, allocated command line string is
434  * returned.
435  *
436  * For the mb2 kernel, we only set command line console if os_console is set.
437  * We can not overwrite console in the environment, as it can disrupt the
438  * loader console messages, and we do not want to deal with the os_console
439  * in the kernel.
440  */
441 static char *
442 update_cmdline(char *cl, bool mb2)
443 {
444 	char *os_console = getenv("os_console");
445 	char *ttymode = NULL;
446 	char mode[10];
447 	char *tmp;
448 	const char *prop;
449 	size_t plen;
450 	int rv;
451 
452 	if (mb2 == true && os_console == NULL)
453 		return (cl);
454 
455 	if (os_console == NULL) {
456 		tmp = strdup(getenv("console"));
457 		os_console = strsep(&tmp, ", ");
458 	} else {
459 		os_console = strdup(os_console);
460 	}
461 
462 	if (os_console == NULL)
463 		return (cl);
464 
465 	if (mb2 == false && strncmp(os_console, "tty", 3) == 0) {
466 		snprintf(mode, sizeof (mode), "%s-mode", os_console);
467 		/*
468 		 * The ttyX-mode variable is set by our serial console
469 		 * driver for ttya-ttyd. However, since the os_console
470 		 * values are not verified, it is possible we get bogus
471 		 * name and no mode variable. If so, we do not set console
472 		 * property and let the kernel use defaults.
473 		 */
474 		if ((ttymode = getenv(mode)) == NULL)
475 			return (cl);
476 	}
477 
478 	rv = find_property_value(cl, "console", &prop, &plen);
479 	if (rv != 0 && rv != ENOENT) {
480 		free(os_console);
481 		return (cl);
482 	}
483 
484 	/* If console is set and this is MB2 boot, we are done. */
485 	if (rv == 0 && mb2 == true) {
486 		free(os_console);
487 		return (cl);
488 	}
489 
490 	/* If console is set, do we need to set tty mode? */
491 	if (rv == 0) {
492 		const char *ttyp = NULL;
493 		size_t ttylen;
494 
495 		free(os_console);
496 		os_console = NULL;
497 		*mode = '\0';
498 		if (strncmp(prop, "tty", 3) == 0 && plen == 4) {
499 			strncpy(mode, prop, plen);
500 			mode[plen] = '\0';
501 			strncat(mode, "-mode", 5);
502 			find_property_value(cl, mode, &ttyp, &ttylen);
503 		}
504 
505 		if (*mode != '\0' && ttyp == NULL)
506 			ttymode = getenv(mode);
507 		else
508 			return (cl);
509 	}
510 
511 	/* Build updated command line. */
512 	if (os_console != NULL) {
513 		char *propstr;
514 
515 		asprintf(&propstr, "console=%s", os_console);
516 		free(os_console);
517 		if (propstr == NULL) {
518 			return (cl);
519 		}
520 
521 		tmp = insert_cmdline(cl, propstr);
522                 free(propstr);
523                 if (tmp == NULL)
524 			return (cl);
525 
526                 free(cl);
527                 cl = tmp;
528 	}
529 	if (ttymode != NULL) {
530 		char *propstr;
531 
532 		asprintf(&propstr, "%s=\"%s\"", mode, ttymode);
533 		if (propstr == NULL)
534 			return (cl);
535 
536 		tmp = insert_cmdline(cl, propstr);
537                 free(propstr);
538                 if (tmp == NULL)
539 			return (cl);
540                 free(cl);
541                 cl = tmp;
542 	}
543 
544 	return (cl);
545 }
546 
547 /*
548  * Build the kernel command line. Shared function between MB1 and MB2.
549  *
550  * In both cases, if fstype is set and is not zfs, we do not set up
551  * zfs-bootfs property. But we set kernel file name and options.
552  *
553  * For the MB1, we only can pass properties on command line, so
554  * we will set console, ttyX-mode (for serial console) and zfs-bootfs.
555  *
556  * For the MB2, we can pass properties in environment, but if os_console
557  * is set in environment, we need to add console property on the kernel
558  * command line.
559  *
560  * The console properties are managed in update_cmdline().
561  */
562 int
563 mb_kernel_cmdline(struct preloaded_file *fp, struct devdesc *rootdev,
564     char **line)
565 {
566 	const char *fs = getenv("fstype");
567 	char *cmdline;
568 	size_t len;
569 	bool zfs_root = false;
570 	bool mb2;
571 	int rv;
572 
573 	/*
574 	 * 64-bit kernel has aout header, 32-bit kernel is elf, and the
575 	 * type strings are different. Lets just search for "multiboot2".
576 	 */
577 	if (strstr(fp->f_type, "multiboot2") == NULL)
578 		mb2 = false;
579 	else
580 		mb2 = true;
581 
582 	if (rootdev->d_type == DEVT_ZFS)
583 		zfs_root = true;
584 
585 	/* If we have fstype set in env, reset zfs_root if needed. */
586 	if (fs != NULL && strcmp(fs, "zfs") != 0)
587 		zfs_root = false;
588 
589 	/*
590 	 * If we have fstype set on the command line,
591 	 * reset zfs_root if needed.
592 	 */
593 	rv = find_property_value(fp->f_args, "fstype", &fs, &len);
594 	if (rv != 0 && rv != ENOENT)
595 		return (rv);
596 
597 	if (fs != NULL && strncmp(fs, "zfs", len) != 0)
598 		zfs_root = false;
599 
600 	/* zfs_bootfs() will set the environment, it must be called. */
601 	if (zfs_root == true)
602 		fs = zfs_bootfs(rootdev);
603 
604 	if (fp->f_args == NULL)
605 		cmdline = strdup(fp->f_name);
606 	else
607 		asprintf(&cmdline, "%s %s", fp->f_name, fp->f_args);
608 
609 	if (cmdline == NULL)
610 		return (ENOMEM);
611 
612 	/* Append zfs-bootfs for MB1 command line. */
613 	if (mb2 == false && zfs_root == true) {
614 		char *tmp;
615 
616 		tmp = insert_cmdline(cmdline, fs);
617 		free(cmdline);
618 		if (tmp == NULL)
619 			return (ENOMEM);
620 		cmdline = tmp;
621 	}
622 
623 	*line = update_cmdline(cmdline, mb2);
624 	return (0);
625 }
626 
627 /*
628  * Returns allocated virtual address from MB info area.
629  */
630 static vm_offset_t
631 mb_malloc(size_t n)
632 {
633 	vm_offset_t ptr = last_addr;
634 	last_addr = roundup(last_addr + n, MULTIBOOT_TAG_ALIGN);
635 	return (ptr);
636 }
637 
638 /*
639  * Calculate size for module tag list.
640  */
641 static size_t
642 module_size(struct preloaded_file *fp)
643 {
644 	size_t len, size;
645 	struct preloaded_file *mfp;
646 
647 	size = 0;
648 	for (mfp = fp->f_next; mfp != NULL; mfp = mfp->f_next) {
649 		len = strlen(mfp->f_name) + 1;
650 		len += strlen(mfp->f_type) + 5 + 1; /* 5 is for "type=" */
651 		if (mfp->f_args != NULL)
652 			len += strlen(mfp->f_args) + 1;
653 		size += sizeof (multiboot_tag_module_t) + len;
654 		size = roundup(size, MULTIBOOT_TAG_ALIGN);
655 	}
656 	return (size);
657 }
658 
659 /*
660  * Calculate size for bios smap tag.
661  */
662 static size_t
663 biossmap_size(struct preloaded_file *fp)
664 {
665 	int num;
666 	struct file_metadata *md;
667 
668 	md = file_findmetadata(fp, MODINFOMD_SMAP);
669 	if (md == NULL)
670 		return (0);
671 
672 	num = md->md_size / sizeof(struct bios_smap); /* number of entries */
673 	return (sizeof (multiboot_tag_mmap_t) +
674 	    num * sizeof (multiboot_mmap_entry_t));
675 }
676 
677 static size_t
678 mbi_size(struct preloaded_file *fp, char *cmdline)
679 {
680 	size_t size;
681 
682 	size = sizeof (uint32_t) * 2; /* first 2 fields from MBI header */
683 	size += sizeof (multiboot_tag_string_t) + strlen(cmdline) + 1;
684 	size = roundup2(size, MULTIBOOT_TAG_ALIGN);
685 	size += sizeof (multiboot_tag_string_t) + strlen(bootprog_info) + 1;
686 	size = roundup2(size, MULTIBOOT_TAG_ALIGN);
687 	size += sizeof (multiboot_tag_basic_meminfo_t);
688 	size = roundup2(size, MULTIBOOT_TAG_ALIGN);
689 	size += module_size(fp);
690 	size = roundup2(size, MULTIBOOT_TAG_ALIGN);
691 	size += biossmap_size(fp);
692 	size = roundup2(size, MULTIBOOT_TAG_ALIGN);
693 
694 	if (strstr(getenv("loaddev"), "pxe") != NULL) {
695 		size += sizeof(multiboot_tag_network_t) + sizeof (BOOTPLAYER);
696 		size = roundup2(size, MULTIBOOT_TAG_ALIGN);
697 	}
698 
699 	if (rsdp != NULL) {
700 		if (rsdp->Revision == 0) {
701 			size += sizeof (multiboot_tag_old_acpi_t) +
702 			    sizeof(ACPI_RSDP_COMMON);
703 		} else {
704 			size += sizeof (multiboot_tag_new_acpi_t) +
705 			    rsdp->Length;
706 		}
707 		size = roundup2(size, MULTIBOOT_TAG_ALIGN);
708 	}
709 	size += sizeof(multiboot_tag_t);
710 
711 	return (size);
712 }
713 
714 static int
715 multiboot2_exec(struct preloaded_file *fp)
716 {
717 	struct preloaded_file *mfp;
718 	multiboot2_info_header_t *mbi;
719 	char *cmdline = NULL;
720 	struct devdesc *rootdev;
721 	struct file_metadata *md;
722 	int i, error, num;
723 	int rootfs = 0;
724 	size_t size;
725 	struct bios_smap *smap;
726 	vm_offset_t tmp;
727 	i386_getdev((void **)(&rootdev), NULL, NULL);
728 
729 	error = EINVAL;
730 	if (rootdev == NULL) {
731 		printf("can't determine root device\n");
732 		goto error;
733 	}
734 
735 	/*
736 	 * Set the image command line.
737 	 */
738 	if (fp->f_args == NULL) {
739 		cmdline = getenv("boot-args");
740 		if (cmdline != NULL) {
741 			fp->f_args = strdup(cmdline);
742 			if (fp->f_args == NULL) {
743 				error = ENOMEM;
744 				goto error;
745 			}
746 		}
747 	}
748 
749 	error = mb_kernel_cmdline(fp, rootdev, &cmdline);
750 	if (error != 0)
751 		goto error;
752 
753 	/* mb_kernel_cmdline() updates the environment. */
754 	build_environment_module();
755 
756 	size = mbi_size(fp, cmdline);	/* Get the size for MBI. */
757 
758 	/* Set up the base for mb_malloc. */
759 	for (mfp = fp; mfp->f_next != NULL; mfp = mfp->f_next);
760 
761 	/* Start info block from the new page. */
762 	last_addr = roundup(mfp->f_addr + mfp->f_size, MULTIBOOT_MOD_ALIGN);
763 
764 	/* Do we have space for multiboot info? */
765 	if (last_addr + size >= memtop_copyin) {
766 		error = ENOMEM;
767 		goto error;
768 	}
769 
770 	mbi = (multiboot2_info_header_t *)PTOV(last_addr);
771 	last_addr = (vm_offset_t)mbi->mbi_tags;
772 
773 	{
774 		multiboot_tag_string_t *tag;
775 		i = sizeof (multiboot_tag_string_t) + strlen(cmdline) + 1;
776 		tag = (multiboot_tag_string_t *) mb_malloc(i);
777 
778 		tag->mb_type = MULTIBOOT_TAG_TYPE_CMDLINE;
779 		tag->mb_size = i;
780 		memcpy(tag->mb_string, cmdline, strlen(cmdline) + 1);
781 		free(cmdline);
782 		cmdline = NULL;
783 	}
784 
785 	{
786 		multiboot_tag_string_t *tag;
787 		i = sizeof (multiboot_tag_string_t) + strlen(bootprog_info) + 1;
788 		tag = (multiboot_tag_string_t *) mb_malloc(i);
789 
790 		tag->mb_type = MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME;
791 		tag->mb_size = i;
792 		memcpy(tag->mb_string, bootprog_info,
793 		    strlen(bootprog_info) + 1);
794 	}
795 
796 	{
797 		multiboot_tag_basic_meminfo_t *tag;
798 		tag = (multiboot_tag_basic_meminfo_t *)
799 		    mb_malloc(sizeof (*tag));
800 
801 		tag->mb_type = MULTIBOOT_TAG_TYPE_BASIC_MEMINFO;
802 		tag->mb_size = sizeof (*tag);
803 		tag->mb_mem_lower = bios_basemem / 1024;
804 		tag->mb_mem_upper = bios_extmem / 1024;
805 	}
806 
807 	num = 0;
808 	for (mfp = fp->f_next; mfp != NULL; mfp = mfp->f_next) {
809 		num++;
810 		if (mfp->f_type != NULL && strcmp(mfp->f_type, "rootfs") == 0)
811 			rootfs++;
812 	}
813 
814 	if (num == 0 || rootfs == 0) {
815 		/* We need at least one module - rootfs. */
816 		printf("No rootfs module provided, aborting\n");
817 		error = EINVAL;
818 		goto error;
819 	}
820 
821 	/*
822 	 * Set the stage for physical memory layout:
823 	 * - We have kernel at load_addr.
824 	 * - Modules are aligned to page boundary.
825 	 * - MBI is aligned to page boundary.
826 	 * - Set the tmp to point to physical address of the first module.
827 	 */
828 	tmp = roundup2(load_addr + fp->f_size, MULTIBOOT_MOD_ALIGN);
829 
830 	for (mfp = fp->f_next; mfp != NULL; mfp = mfp->f_next) {
831 		multiboot_tag_module_t *tag;
832 
833 		num = strlen(mfp->f_name) + 1;
834 		num += strlen(mfp->f_type) + 5 + 1;
835 		if (mfp->f_args != NULL) {
836 			num += strlen(mfp->f_args) + 1;
837 		}
838 		cmdline = malloc(num);
839 		if (cmdline == NULL) {
840 			error = ENOMEM;
841 			goto error;
842 		}
843 
844 		if (mfp->f_args != NULL)
845 			snprintf(cmdline, num, "%s type=%s %s",
846 			    mfp->f_name, mfp->f_type, mfp->f_args);
847 		else
848 			snprintf(cmdline, num, "%s type=%s",
849 			    mfp->f_name, mfp->f_type);
850 
851 		tag = (multiboot_tag_module_t *)mb_malloc(sizeof (*tag) + num);
852 
853 		tag->mb_type = MULTIBOOT_TAG_TYPE_MODULE;
854 		tag->mb_size = sizeof (*tag) + num;
855 		tag->mb_mod_start = tmp;
856 		tag->mb_mod_end = tmp + mfp->f_size;
857 		tmp = roundup2(tag->mb_mod_end, MULTIBOOT_MOD_ALIGN);
858 		memcpy(tag->mb_cmdline, cmdline, num);
859 		free(cmdline);
860 		cmdline = NULL;
861 	}
862 
863 	md = file_findmetadata(fp, MODINFOMD_SMAP);
864 	if (md == NULL) {
865 		printf("no memory smap\n");
866 		error = EINVAL;
867 		goto error;
868 	}
869 
870 	smap = (struct bios_smap *)md->md_data;
871 	num = md->md_size / sizeof(struct bios_smap); /* number of entries */
872 
873 	{
874 		multiboot_tag_mmap_t *tag;
875 		multiboot_mmap_entry_t *mmap_entry;
876 
877 		tag = (multiboot_tag_mmap_t *)
878 		    mb_malloc(sizeof (*tag) +
879 		    num * sizeof (multiboot_mmap_entry_t));
880 
881 		tag->mb_type = MULTIBOOT_TAG_TYPE_MMAP;
882 		tag->mb_size = sizeof (*tag) +
883 		    num * sizeof (multiboot_mmap_entry_t);
884 		tag->mb_entry_size = sizeof (multiboot_mmap_entry_t);
885 		tag->mb_entry_version = 0;
886 		mmap_entry = (multiboot_mmap_entry_t *)tag->mb_entries;
887 
888 		for (i = 0; i < num; i++) {
889 			mmap_entry[i].mmap_addr = smap[i].base;
890 			mmap_entry[i].mmap_len = smap[i].length;
891 			mmap_entry[i].mmap_type = smap[i].type;
892 			mmap_entry[i].mmap_reserved = 0;
893 		}
894 	}
895 
896 	if (strstr(getenv("loaddev"), "pxe") != NULL) {
897 		multiboot_tag_network_t *tag;
898 		tag = (multiboot_tag_network_t *)
899 		    mb_malloc(sizeof(*tag) + sizeof (BOOTPLAYER));
900 
901 		tag->mb_type = MULTIBOOT_TAG_TYPE_NETWORK;
902 		tag->mb_size = sizeof(*tag) + sizeof (BOOTPLAYER);
903 		memcpy(tag->mb_dhcpack, &bootplayer, sizeof (BOOTPLAYER));
904 	}
905 
906 	if (rsdp != NULL) {
907 		multiboot_tag_new_acpi_t *ntag;
908 		multiboot_tag_old_acpi_t *otag;
909 		uint32_t tsize;
910 
911 		if (rsdp->Revision == 0) {
912 			tsize = sizeof (*otag) + sizeof (ACPI_RSDP_COMMON);
913 			otag = (multiboot_tag_old_acpi_t *)mb_malloc(tsize);
914 			otag->mb_type = MULTIBOOT_TAG_TYPE_ACPI_OLD;
915 			otag->mb_size = tsize;
916 			memcpy(otag->mb_rsdp, rsdp, sizeof (ACPI_RSDP_COMMON));
917 		} else {
918 			tsize = sizeof (*ntag) + rsdp->Length;
919 			ntag = (multiboot_tag_new_acpi_t *)mb_malloc(tsize);
920 			ntag->mb_type = MULTIBOOT_TAG_TYPE_ACPI_NEW;
921 			ntag->mb_size = tsize;
922 			memcpy(ntag->mb_rsdp, rsdp, rsdp->Length);
923 		}
924 	}
925 
926 	/*
927 	 * MB tag list end marker.
928 	 */
929 	{
930 		multiboot_tag_t *tag = (multiboot_tag_t *)
931 		    mb_malloc(sizeof(*tag));
932 		tag->mb_type = MULTIBOOT_TAG_TYPE_END;
933 		tag->mb_size = sizeof(*tag);
934 	}
935 
936 	mbi->mbi_total_size = last_addr - (vm_offset_t)mbi;
937 	mbi->mbi_reserved = 0;
938 
939 	dev_cleanup();
940 	__exec((void *)VTOP(multiboot_tramp), MULTIBOOT2_BOOTLOADER_MAGIC,
941 	    (void *)entry_addr, (void *)VTOP(mbi));
942 	panic("exec returned");
943 
944 error:
945 	if (cmdline != NULL)
946 		free(cmdline);
947 	return (error);
948 }
949