xref: /titanic_51/usr/src/boot/sys/boot/i386/libi386/multiboot.c (revision 546a5316dc64f8723d8e04d43ca4cd2503252fd8)
1 /*-
2  * Copyright (c) 2014 Roger Pau Monné <royger@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 /*
28  * This multiboot implementation only implements a subset of the full
29  * multiboot specification in order to be able to boot Xen and a
30  * FreeBSD Dom0. Trying to use it to boot other multiboot compliant
31  * kernels will most surely fail.
32  *
33  * The full multiboot specification can be found here:
34  * http://www.gnu.org/software/grub/manual/multiboot/multiboot.html
35  */
36 
37 #include <sys/cdefs.h>
38 
39 #include <sys/param.h>
40 #include <sys/exec.h>
41 #include <sys/linker.h>
42 #include <sys/module.h>
43 #include <sys/stdint.h>
44 #include <stdbool.h>
45 #define _MACHINE_ELF_WANT_32BIT
46 #include <machine/elf.h>
47 #include <machine/metadata.h>
48 #include <machine/pc/bios.h>
49 #include <string.h>
50 #include <stand.h>
51 
52 #include "bootstrap.h"
53 #include "multiboot.h"
54 #include "pxe.h"
55 #include "../zfs/libzfs.h"
56 #include "../i386/libi386/libi386.h"
57 #include "../i386/btx/lib/btxv86.h"
58 
59 #define MULTIBOOT_SUPPORTED_FLAGS \
60 	(MULTIBOOT_AOUT_KLUDGE|MULTIBOOT_PAGE_ALIGN|MULTIBOOT_MEMORY_INFO)
61 #define NUM_MODULES		2
62 #define METADATA_FIXED_SIZE	(PAGE_SIZE*4)
63 #define METADATA_MODULE_SIZE	PAGE_SIZE
64 
65 #define METADATA_RESV_SIZE(mod_num) \
66 	roundup(METADATA_FIXED_SIZE + METADATA_MODULE_SIZE * mod_num, PAGE_SIZE)
67 
68 /* MB data heap pointer */
69 static vm_offset_t last_addr;
70 
71 extern int elf32_loadfile_raw(char *filename, u_int64_t dest,
72     struct preloaded_file **result, int multiboot);
73 extern int elf64_load_modmetadata(struct preloaded_file *fp, u_int64_t dest);
74 extern int elf64_obj_loadfile(char *filename, u_int64_t dest,
75     struct preloaded_file **result);
76 
77 static int multiboot_loadfile(char *, u_int64_t, struct preloaded_file **);
78 static int multiboot_exec(struct preloaded_file *);
79 
80 static int multiboot_obj_loadfile(char *, u_int64_t, struct preloaded_file **);
81 static int multiboot_obj_exec(struct preloaded_file *fp);
82 
83 struct file_format multiboot = { multiboot_loadfile, multiboot_exec };
84 struct file_format multiboot_obj =
85     { multiboot_obj_loadfile, multiboot_obj_exec };
86 
87 extern void multiboot_tramp();
88 
89 static const char mbl_name[] = "illumos Loader";
90 
91 static int
92 num_modules(struct preloaded_file *kfp)
93 {
94 	struct kernel_module	*kmp;
95 	int			 mod_num = 0;
96 
97 	for (kmp = kfp->f_modules; kmp != NULL; kmp = kmp->m_next)
98 		mod_num++;
99 
100 	return (mod_num);
101 }
102 
103 static vm_offset_t
104 max_addr(void)
105 {
106 	struct preloaded_file	*fp;
107 	vm_offset_t		 addr = 0;
108 
109 	for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
110 		if (addr < (fp->f_addr + fp->f_size))
111 			addr = fp->f_addr + fp->f_size;
112 	}
113 
114 	return (addr);
115 }
116 
117 static int
118 multiboot_loadfile(char *filename, u_int64_t dest,
119     struct preloaded_file **result)
120 {
121 	uint32_t		*magic;
122 	int			 i, error;
123 	caddr_t			 header_search;
124 	ssize_t			 search_size;
125 	int			 fd;
126 	struct multiboot_header	*header;
127 	char			*cmdline;
128 	struct preloaded_file	*fp;
129 
130 	if (filename == NULL)
131 		return (EFTYPE);
132 
133 	/* is kernel already loaded? */
134 	fp = file_findfile(NULL, NULL);
135 	if (fp != NULL) {
136 		return (EFTYPE);
137 	}
138 
139 	if ((fd = open(filename, O_RDONLY)) == -1)
140 		return (errno);
141 
142 	/*
143 	 * Read MULTIBOOT_SEARCH size in order to search for the
144 	 * multiboot magic header.
145 	 */
146 	header_search = malloc(MULTIBOOT_SEARCH);
147 	if (header_search == NULL) {
148 		close(fd);
149 		return (ENOMEM);
150 	}
151 
152 	search_size = read(fd, header_search, MULTIBOOT_SEARCH);
153 	magic = (uint32_t *)header_search;
154 
155 	header = NULL;
156 	for (i = 0; i < (search_size / sizeof(uint32_t)); i++) {
157 		if (magic[i] == MULTIBOOT_HEADER_MAGIC) {
158 			header = (struct multiboot_header *)&magic[i];
159 			break;
160 		}
161 	}
162 
163 	if (header == NULL) {
164 		error = EFTYPE;
165 		goto out;
166 	}
167 
168 	/* Valid multiboot header has been found, validate checksum */
169 	if (header->magic + header->flags + header->checksum != 0) {
170 		printf(
171 	"Multiboot checksum failed, magic: 0x%x flags: 0x%x checksum: 0x%x\n",
172 	header->magic, header->flags, header->checksum);
173 		error = EFTYPE;
174 		goto out;
175 	}
176 
177 	if ((header->flags & ~MULTIBOOT_SUPPORTED_FLAGS) != 0) {
178 		printf("Unsupported multiboot flags found: 0x%x\n",
179 		    header->flags);
180 		error = EFTYPE;
181 		goto out;
182 	}
183 	/* AOUT KLUDGE means we just load entire flat file as blob */
184 	if (header->flags & MULTIBOOT_AOUT_KLUDGE) {
185 		vm_offset_t laddr;
186 		int got;
187 
188 		dest = header->load_addr;
189 		if (lseek(fd, 0, SEEK_SET) == -1) {
190 			printf("lseek failed\n");
191 			error = EIO;
192 			goto out;
193 		}
194 		laddr = dest;
195 		for (;;) {
196 			got = archsw.arch_readin(fd, laddr, 4096);
197 			if (got == 0)
198 				break;
199 			if (got < 0) {
200 				printf("error reading: %s", strerror(errno));
201 				error = EIO;
202 				goto out;
203 			}
204 			laddr += got;
205 		}
206 
207 		fp = file_alloc();
208 		if (fp == NULL) {
209 			error = ENOMEM;
210 			goto out;
211 		}
212 		fp->f_name = strdup(filename);
213 		fp->f_type = strdup("aout multiboot kernel");
214 		fp->f_addr = header->entry_addr;
215 		fp->f_size = laddr - dest;
216 		if (fp->f_size == 0) {
217 			file_discard(fp);
218 			error = EIO;
219 			goto out;
220 		}
221 		fp->f_metadata = NULL;
222 
223 		*result = fp;
224 		error = 0;
225 	} else {
226 
227 		error = elf32_loadfile_raw(filename, dest, result, 1);
228 		if (error != 0) {
229 			printf("elf32_loadfile_raw failed: %d unable to "
230 			    "load multiboot kernel\n", error);
231 			goto out;
232 		}
233 	}
234 
235 	setenv("kernelname", (*result)->f_name, 1);
236 	bios_addsmapdata(*result);
237 out:
238 	free(header_search);
239 	close(fd);
240 	return (error);
241 }
242 
243 /*
244  * returns allocated virtual address from MB info area
245  */
246 static vm_offset_t
247 mb_malloc(size_t n)
248 {
249 	vm_offset_t ptr = last_addr;
250 	if (ptr + n >= high_heap_base)
251 		return (0);
252 	last_addr = roundup(last_addr + n, MULTIBOOT_INFO_ALIGN);
253 	return (ptr);
254 }
255 
256 /*
257  * Since for now we have no way to pass the environment to the kernel other than
258  * through arguments, we need to take care of console setup.
259  *
260  * If the console is in mirror mode, set the kernel console from $os_console.
261  * If it's unset, use first item from $console.
262  * If $console is "ttyX", also pass $ttyX-mode, since it may have been set by
263  * the user.
264  *
265  * In case of memory allocation errors, just return original command line,
266  * so we have chance of booting.
267  *
268  * On success, cl will be freed and a new, allocated command line string is
269  * returned.
270  */
271 static char *
272 update_cmdline(char *cl)
273 {
274 	char *os_console = getenv("os_console");
275 	char *ttymode = NULL;
276 	char mode[10];
277 	char *tmp;
278 	int len;
279 
280 	if (os_console == NULL) {
281 		tmp = strdup(getenv("console"));
282 		os_console = strsep(&tmp, ", ");
283 	} else
284 		os_console = strdup(os_console);
285 
286 	if (os_console == NULL)
287 		return (cl);
288 
289 	if (strncmp(os_console, "tty", 3) == 0) {
290 		snprintf(mode, sizeof (mode), "%s-mode", os_console);
291 		ttymode = getenv(mode);	/* never NULL */
292 	}
293 
294 	if (strstr(cl, "-B") != NULL) {
295 		len = strlen(cl) + 1;
296 		/*
297 		 * if console is not present, add it
298 		 * if console is ttyX, add ttymode
299 		 */
300 		tmp = strstr(cl, "console");
301 		if (tmp == NULL) {
302 			len += 12;	/* " -B console=" */
303 			len += strlen(os_console);
304 			if (ttymode != NULL) {
305 				len += 13;	/* ",ttyX-mode=\"\"" */
306 				len += strlen(ttymode);
307 			}
308 			tmp = malloc(len);
309 			if (tmp == NULL) {
310 				free(os_console);
311 				return (cl);
312 			}
313 			if (ttymode != NULL)
314 				sprintf(tmp,
315 				    "%s -B console=%s,%s-mode=\"%s\"",
316 				    cl, os_console, os_console, ttymode);
317 			else
318 				sprintf(tmp, "%s -B console=%s",
319 				    cl, os_console);
320 		} else {
321 			/* console is set, do we need tty mode? */
322 			tmp += 8;
323 			if (strstr(tmp, "tty") == tmp) {
324 				strncpy(mode, tmp, 4);
325 				mode[4] = '\0';
326 				strcat(mode, "-mode");
327 				ttymode = getenv(mode);	/* never NULL */
328 			} else { /* nope */
329 				free(os_console);
330 				return (cl);
331 			}
332 			len = strlen(cl) + 1;
333 			len += 13;	/* ",ttyX-mode=\"\"" */
334 			len += strlen(ttymode);
335 			tmp = malloc(len);
336 			if (tmp == NULL) {
337 				free(os_console);
338 				return (cl);
339 			}
340 			sprintf(tmp, "%s,%s=\"%s\"", cl, mode, ttymode);
341 		}
342 	} else {
343 		/*
344 		 * no -B, so we need to add " -B console=%s[,ttyX-mode=\"%s\"]"
345 		 */
346 		len = strlen(cl) + 1;
347 		len += 12;		/* " -B console=" */
348 		len += strlen(os_console);
349 		if (ttymode != NULL) {
350 			len += 13;	/* ",ttyX-mode=\"\"" */
351 			len += strlen(ttymode);
352 		}
353 		tmp = malloc(len);
354 		if (tmp == NULL) {
355 			free(os_console);
356 			return (cl);
357 		}
358 		if (ttymode != NULL)
359 			sprintf(tmp, "%s -B console=%s,%s-mode=\"%s\"", cl,
360 			    os_console, os_console, ttymode);
361 		else
362 			sprintf(tmp, "%s -B console=%s", cl, os_console);
363 	}
364 	free(os_console);
365 	free(cl);
366 	return (tmp);
367 }
368 
369 /*
370  * Search the command line for named property.
371  *
372  * Return codes:
373  *	0	The name is found, we return the data in value and len.
374  *	ENOENT	The name is not found.
375  *	EINVAL	The provided command line is badly formed.
376  */
377 static int
378 find_property_value(const char *cmd, const char *name, const char **value,
379     size_t *len)
380 {
381 	const char *namep, *valuep;
382 	size_t name_len, value_len;
383 	int quoted;
384 
385 	*value = NULL;
386 	*len = 0;
387 
388 	if (cmd == NULL)
389 		return (ENOENT);
390 
391 	while (*cmd != '\0') {
392 		if (cmd[0] != '-' || cmd[1] != 'B') {
393 			cmd++;
394 			continue;
395 		}
396 		cmd += 2;	/* Skip -B */
397 		while (cmd[0] == ' ' || cmd[0] == '\t')
398 			cmd++;	/* Skip whitespaces. */
399 		while (*cmd != '\0' && cmd[0] != ' ' && cmd[0] != '\t') {
400 			namep = cmd;
401 			valuep = strchr(cmd, '=');
402 			if (valuep == NULL)
403 				break;
404 			name_len = valuep - namep;
405 			valuep++;
406 			value_len = 0;
407 			quoted = 0;
408 			for (; ; ++value_len) {
409 				if (valuep[value_len] == '\0')
410 					break;
411 
412 				/* Is this value quoted? */
413 				if (value_len == 0 &&
414 				    (valuep[0] == '\'' || valuep[0] == '"')) {
415 					quoted = valuep[0];
416 					++value_len;
417 				}
418 
419 				/*
420 				 * In the quote accept any character,
421 				 * but look for ending quote.
422 				 */
423 				if (quoted != 0) {
424 					if (valuep[value_len] == quoted)
425 						quoted = 0;
426 					continue;
427 				}
428 
429 				/* A comma or white space ends the value. */
430 				if (valuep[value_len] == ',' ||
431 				    valuep[value_len] == ' ' ||
432 				    valuep[value_len] == '\t')
433 					break;
434 			}
435 			if (quoted != 0) {
436 				printf("Missing closing '%c' in \"%s\"\n",
437 				    quoted, valuep);
438 				return (EINVAL);
439 			}
440 
441 			if (value_len != 0) {
442 				if (strncmp(namep, name, name_len) == 0) {
443 					*value = valuep;
444 					*len = value_len;
445 					return (0);
446 				}
447 			}
448 			cmd = valuep + value_len;
449 			while (*cmd == ',')
450 				cmd++;
451 		}
452 	}
453 	return (ENOENT);
454 }
455 
456 static int
457 kernel_cmdline(struct preloaded_file *fp, struct i386_devdesc *rootdev,
458     char **line)
459 {
460 	const char *fs = getenv("fstype");
461 	char *cmdline = NULL;
462 	size_t len;
463 	bool zfs_root = false;
464 	int rv = 0;
465 
466 	if (rootdev->d_type == DEVT_ZFS)
467 		zfs_root = true;
468 
469 	/* If we have fstype set in env, reset zfs_root if needed. */
470 	if (fs != NULL && strcmp(fs, "zfs") != 0)
471 		zfs_root = false;
472 
473 	/*
474 	 * If we have fstype set on the command line,
475 	 * reset zfs_root if needed.
476 	 */
477 	rv = find_property_value(fp->f_args, "fstype", &fs, &len);
478 	switch (rv) {
479 	case EINVAL:		/* invalid command line */
480 		return (rv);
481 	case ENOENT:		/* fall through */
482 	case 0:
483 		break;
484 	}
485 
486 	if (fs != NULL && strncmp(fs, "zfs", len) != 0)
487 		zfs_root = false;
488 
489 	len = strlen(fp->f_name) + 1;
490 
491 	if (fp->f_args != NULL)
492 		len += strlen(fp->f_args) + 1;
493 
494 	if (zfs_root == true)
495 		len += 3 + strlen(zfs_bootfs(rootdev)) + 1;
496 
497 	cmdline = malloc(len);
498 	if (cmdline == NULL)
499 		return (ENOMEM);
500 
501 	if (zfs_root == true) {
502 		if (fp->f_args != NULL)
503 			snprintf(cmdline, len, "%s %s -B %s", fp->f_name,
504 			    fp->f_args, zfs_bootfs(rootdev));
505 		else
506 			snprintf(cmdline, len, "%s -B %s", fp->f_name,
507 			    zfs_bootfs(rootdev));
508 	} else if (fp->f_args != NULL)
509 		snprintf(cmdline, len, "%s %s", fp->f_name, fp->f_args);
510 	else
511 		snprintf(cmdline, len, "%s", fp->f_name);
512 
513 	*line = update_cmdline(cmdline);
514 	return (0);
515 }
516 
517 static int
518 multiboot_exec(struct preloaded_file *fp)
519 {
520 	struct preloaded_file		*mfp;
521 	vm_offset_t			 module_start, metadata_size;
522 	vm_offset_t			 modulep, kernend, entry;
523 	struct file_metadata		*md;
524 	Elf_Ehdr			*ehdr;
525 	struct multiboot_info		*mb_info = NULL;
526 	struct multiboot_mod_list	*mb_mod = NULL;
527 	multiboot_memory_map_t		*mmap;
528 	struct bios_smap		*smap;
529 	struct i386_devdesc		*rootdev;
530 	extern BOOTPLAYER		bootplayer; 	/* dhcp info */
531 	char				*cmdline = NULL;
532 	size_t				 len;
533 	int				 error, num, i;
534 	int				 rootfs = 0;	/* flag for rootfs */
535 	int				 xen = 0;	/* flag for xen */
536 	int				 kernel = 0;	/* flag for kernel */
537 
538 	/* set up base for mb_malloc */
539 	for (mfp = fp; mfp->f_next != NULL; mfp = mfp->f_next);
540 
541 	/* start info block from new page */
542 	last_addr = roundup(mfp->f_addr + mfp->f_size, MULTIBOOT_MOD_ALIGN);
543 
544 	/* Allocate the multiboot struct and fill the basic details. */
545 	mb_info = (struct multiboot_info *)PTOV(mb_malloc(sizeof (*mb_info)));
546 
547 	bzero(mb_info, sizeof(struct multiboot_info));
548 	mb_info->flags = MULTIBOOT_INFO_MEMORY|MULTIBOOT_INFO_BOOT_LOADER_NAME;
549 	mb_info->mem_lower = bios_basemem / 1024;
550 	mb_info->mem_upper = bios_extmem / 1024;
551 	mb_info->boot_loader_name = mb_malloc(strlen(mbl_name) + 1);
552 
553 	i386_copyin(mbl_name, mb_info->boot_loader_name, strlen(mbl_name)+1);
554 
555 	i386_getdev((void **)(&rootdev), NULL, NULL);
556 	if (rootdev == NULL) {
557 		printf("can't determine root device\n");
558 		error = EINVAL;
559 		goto error;
560 	}
561 
562 	/*
563 	 * boot image command line. if args were not provided, we need to set
564 	 * args here, and that depends on image type...
565 	 * fortunately we only have following options:
566 	 * 64 or 32 bit unix or xen. so we just check if f_name has unix.
567 	 */
568 	/* do we boot xen? */
569 	if (strstr(fp->f_name, "unix") == NULL)
570 		xen = 1;
571 
572 	entry = fp->f_addr;
573 
574 	num = 0;
575 	for (mfp = fp->f_next; mfp != NULL; mfp = mfp->f_next) {
576 		num++;
577 		if (mfp->f_type != NULL && strcmp(mfp->f_type, "rootfs") == 0)
578 			rootfs++;
579 		if (mfp->f_type != NULL && strcmp(mfp->f_type, "kernel") == 0)
580 			kernel++;
581 	}
582 
583 	if (num == 0 || rootfs == 0) {
584 		/* need at least one module - rootfs */
585 		printf("No rootfs module provided, aborting\n");
586 		error = EINVAL;
587 		goto error;
588 	}
589 	if (xen == 1 && kernel == 0) {
590 		printf("No kernel module provided for xen, aborting\n");
591 		error = EINVAL;
592 		goto error;
593 	}
594 	mb_mod = (struct multiboot_mod_list *) PTOV(last_addr);
595 	last_addr += roundup(sizeof(*mb_mod) * num, MULTIBOOT_INFO_ALIGN);
596 
597 	bzero(mb_mod, sizeof(*mb_mod) * num);
598 
599 	num = 0;
600 	for (mfp = fp->f_next; mfp != NULL; mfp = mfp->f_next) {
601 		mb_mod[num].mod_start = mfp->f_addr;
602 		mb_mod[num].mod_end = mfp->f_addr + mfp->f_size;
603 
604 		if (strcmp(mfp->f_type, "kernel") == 0) {
605 			cmdline = NULL;
606 			error = kernel_cmdline(mfp, rootdev, &cmdline);
607 			if (error != 0)
608 				goto error;
609 		} else {
610 			len = strlen(mfp->f_name) + 1;
611 			len += strlen(mfp->f_type) + 5 + 1;
612 			if (mfp->f_args != NULL) {
613 				len += strlen(mfp->f_args) + 1;
614 			}
615 			cmdline = malloc(len);
616 			if (cmdline == NULL) {
617 				error = ENOMEM;
618 				goto error;
619 			}
620 
621 			if (mfp->f_args != NULL)
622 				snprintf(cmdline, len, "%s type=%s %s",
623 				    mfp->f_name, mfp->f_type, mfp->f_args);
624 			else
625 				snprintf(cmdline, len, "%s type=%s",
626 				    mfp->f_name, mfp->f_type);
627 		}
628 
629 		mb_mod[num].cmdline = mb_malloc(strlen(cmdline)+1);
630 		i386_copyin(cmdline, mb_mod[num].cmdline, strlen(cmdline)+1);
631 		free(cmdline);
632 		num++;
633 	}
634 
635 	mb_info->mods_count = num;
636 	mb_info->mods_addr = VTOP(mb_mod);
637 	mb_info->flags |= MULTIBOOT_INFO_MODS;
638 
639 	md = file_findmetadata(fp, MODINFOMD_SMAP);
640 	if (md == NULL) {
641 		printf("no memory smap\n");
642 		error = EINVAL;
643 		goto error;
644 	}
645 
646 	num = md->md_size / sizeof(struct bios_smap); /* number of entries */
647 	mmap = (multiboot_memory_map_t *)PTOV(mb_malloc(sizeof(*mmap) * num));
648 
649 	mb_info->mmap_length = num * sizeof(*mmap);
650 	smap = (struct bios_smap *)md->md_data;
651 
652 	for (i = 0; i < num; i++) {
653 		mmap[i].size = sizeof(*smap);
654 		mmap[i].addr = smap[i].base;
655 		mmap[i].len = smap[i].length;
656 		mmap[i].type = smap[i].type;
657 	}
658 	mb_info->mmap_addr = VTOP(mmap);
659 	mb_info->flags |= MULTIBOOT_INFO_MEM_MAP;
660 
661 	if (strstr(getenv("loaddev"), "pxe") != NULL) {
662 		mb_info->drives_length = sizeof (BOOTPLAYER);
663 		mb_info->drives_addr = mb_malloc(mb_info->drives_length);
664 		i386_copyin(&bootplayer, mb_info->drives_addr,
665 		    mb_info->drives_length);
666 		mb_info->flags &= ~MULTIBOOT_INFO_DRIVE_INFO;
667 	}
668 	/*
669 	 * Set the image command line. Need to do this as last thing,
670 	 * as Illumos kernel dboot_startkern will check cmdline
671 	 * address as last check to find first free address.
672 	 */
673 	if (fp->f_args == NULL) {
674 		if (xen)
675 			cmdline = getenv("xen_cmdline");
676 		else
677 			cmdline = getenv("boot-args");
678 		if (cmdline != NULL) {
679 			fp->f_args = strdup(cmdline);
680 			if (fp->f_args == NULL) {
681 				error = ENOMEM;
682 				goto error;
683 			}
684 		}
685 	}
686 
687 	/*
688 	 * if image is xen, we just use f_name + f_args for commandline
689 	 * for unix, we need to add zfs-bootfs.
690 	 */
691 	if (xen) {
692 		len = strlen(fp->f_name) + 1;
693 		if (fp->f_args != NULL)
694 			len += strlen(fp->f_args) + 1;
695 
696 		if (fp->f_args != NULL) {
697 			if((cmdline = malloc(len)) == NULL) {
698 				error = ENOMEM;
699 				goto error;
700 			}
701 			snprintf(cmdline, len, "%s %s", fp->f_name, fp->f_args);
702 		} else {
703 			cmdline = strdup(fp->f_name);
704 			if (cmdline == NULL) {
705 				error = ENOMEM;
706 				goto error;
707 			}
708 		}
709 	} else {
710 		cmdline = NULL;
711 		if ((error = kernel_cmdline(fp, rootdev, &cmdline)) != 0)
712 			goto error;
713 	}
714 
715 	mb_info->cmdline = mb_malloc(strlen(cmdline)+1);
716 	i386_copyin(cmdline, mb_info->cmdline, strlen(cmdline)+1);
717 	mb_info->flags |= MULTIBOOT_INFO_CMDLINE;
718 	free(cmdline);
719 	cmdline = NULL;
720 
721 	dev_cleanup();
722 	__exec((void *)VTOP(multiboot_tramp), (void *)entry,
723 	    (void *)VTOP(mb_info));
724 
725 	panic("exec returned");
726 
727 error:
728 	free(cmdline);
729 	return (error);
730 }
731 
732 static int
733 multiboot_obj_loadfile(char *filename, u_int64_t dest,
734     struct preloaded_file **result)
735 {
736 	struct preloaded_file	*mfp, *kfp, *rfp;
737 	struct kernel_module	*kmp;
738 	int			 error, mod_num;
739 
740 	/* See if there's a aout multiboot kernel loaded */
741 	mfp = file_findfile(NULL, "aout multiboot kernel");
742 	if (mfp != NULL) {
743 		/* we have normal kernel loaded, add module */
744 		rfp = file_loadraw(filename, "module", 0, NULL, 0);
745 		if (rfp == NULL) {
746 			printf(
747 			"Unable to load %s as a multiboot payload module\n",
748 			filename);
749 			return (EINVAL);
750 		}
751 		rfp->f_size = roundup(rfp->f_size, PAGE_SIZE);
752 		*result = rfp;
753 		return (0);
754 	}
755 
756 	/* See if there's a multiboot kernel loaded */
757 	mfp = file_findfile(NULL, "elf multiboot kernel");
758 	if (mfp == NULL) {
759 		return (EFTYPE);	/* this allows to check other methods */
760 	}
761 
762 	/*
763 	 * We have a multiboot kernel loaded, see if there's a
764 	 * kernel loaded also.
765 	 */
766 	kfp = file_findfile(NULL, "elf kernel");
767 	if (kfp == NULL) {
768 		/*
769 		 * No kernel loaded, this must be it. The kernel has to
770 		 * be loaded as a raw file, it will be processed by
771 		 * Xen and correctly loaded as an ELF file.
772 		 */
773 		rfp = file_loadraw(filename, "elf kernel", 0, NULL, 0);
774 		if (rfp == NULL) {
775 			printf(
776 			"Unable to load %s as a multiboot payload kernel\n",
777 			filename);
778 			return (EINVAL);
779 		}
780 
781 		/* Load kernel metadata... */
782 		setenv("kernelname", filename, 1);
783 		error = elf64_load_modmetadata(rfp, rfp->f_addr + rfp->f_size);
784 		if (error) {
785 			printf("Unable to load kernel %s metadata error: %d\n",
786 			    rfp->f_name, error);
787 			return (EINVAL);
788 		}
789 
790 		/*
791 		 * Save space at the end of the kernel in order to place
792 		 * the metadata information. We do an approximation of the
793 		 * max metadata size, this is not optimal but it's probably
794 		 * the best we can do at this point. Once all modules are
795 		 * loaded and the size of the metadata is known this
796 		 * space will be recovered if not used.
797 		 */
798 		mod_num = num_modules(rfp);
799 		rfp->f_size = roundup(rfp->f_size, PAGE_SIZE);
800 		rfp->f_size += METADATA_RESV_SIZE(mod_num);
801 		*result = rfp;
802 	} else {
803 		/* The rest should be loaded as regular modules */
804 		error = elf64_obj_loadfile(filename, dest, result);
805 		if (error != 0) {
806 			printf("Unable to load %s as an object file, error: %d",
807 			    filename, error);
808 			return (error);
809 		}
810 	}
811 
812 	return (0);
813 }
814 
815 static int
816 multiboot_obj_exec(struct preloaded_file *fp)
817 {
818 
819 	return (EFTYPE);
820 }
821