xref: /titanic_51/usr/src/boot/sys/boot/i386/libi386/multiboot.c (revision 8ede96251e3dca657102f3a32187d4382bda2ba4)
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 #define _MACHINE_ELF_WANT_32BIT
45 #include <machine/elf.h>
46 #include <machine/metadata.h>
47 #include <machine/pc/bios.h>
48 #include <string.h>
49 #include <stand.h>
50 
51 #include "bootstrap.h"
52 #include "multiboot.h"
53 #include "pxe.h"
54 #include "../zfs/libzfs.h"
55 #include "../i386/libi386/libi386.h"
56 #include "../i386/btx/lib/btxv86.h"
57 
58 #define MULTIBOOT_SUPPORTED_FLAGS \
59 	(MULTIBOOT_AOUT_KLUDGE|MULTIBOOT_PAGE_ALIGN|MULTIBOOT_MEMORY_INFO)
60 #define METADATA_FIXED_SIZE	(PAGE_SIZE*4)
61 #define METADATA_MODULE_SIZE	PAGE_SIZE
62 
63 #define METADATA_RESV_SIZE(mod_num) \
64 	roundup(METADATA_FIXED_SIZE + METADATA_MODULE_SIZE * mod_num, PAGE_SIZE)
65 
66 /* MB data heap pointer */
67 static vm_offset_t last_addr;
68 extern char bootprog_info[];
69 
70 extern int elf32_loadfile_raw(char *filename, u_int64_t dest,
71     struct preloaded_file **result, int multiboot);
72 extern int elf64_load_modmetadata(struct preloaded_file *fp, u_int64_t dest);
73 extern int elf64_obj_loadfile(char *filename, u_int64_t dest,
74     struct preloaded_file **result);
75 extern int mb_kernel_cmdline(struct preloaded_file *, struct devdesc *,
76     char **);
77 
78 extern void multiboot_tramp();
79 
80 static int multiboot_loadfile(char *, u_int64_t, struct preloaded_file **);
81 static int multiboot_exec(struct preloaded_file *);
82 
83 static int multiboot_obj_loadfile(char *, u_int64_t, struct preloaded_file **);
84 static int multiboot_obj_exec(struct preloaded_file *fp);
85 
86 struct file_format multiboot = { multiboot_loadfile, multiboot_exec };
87 struct file_format multiboot_obj =
88     { multiboot_obj_loadfile, multiboot_obj_exec };
89 
90 static int
91 num_modules(struct preloaded_file *kfp)
92 {
93 	struct kernel_module	*kmp;
94 	int			 mod_num = 0;
95 
96 	for (kmp = kfp->f_modules; kmp != NULL; kmp = kmp->m_next)
97 		mod_num++;
98 
99 	return (mod_num);
100 }
101 
102 static vm_offset_t
103 max_addr(void)
104 {
105 	struct preloaded_file	*fp;
106 	vm_offset_t		 addr = 0;
107 
108 	for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
109 		if (addr < (fp->f_addr + fp->f_size))
110 			addr = fp->f_addr + fp->f_size;
111 	}
112 
113 	return (addr);
114 }
115 
116 static int
117 multiboot_loadfile(char *filename, u_int64_t dest,
118     struct preloaded_file **result)
119 {
120 	uint32_t		*magic;
121 	int			 i, error;
122 	caddr_t			 header_search;
123 	ssize_t			 search_size;
124 	int			 fd;
125 	struct multiboot_header	*header;
126 	struct preloaded_file	*fp;
127 
128 	if (filename == NULL)
129 		return (EFTYPE);
130 
131 	/* is kernel already loaded? */
132 	fp = file_findfile(NULL, NULL);
133 	if (fp != NULL) {
134 		return (EFTYPE);
135 	}
136 
137 	if ((fd = open(filename, O_RDONLY)) == -1)
138 		return (errno);
139 
140 	/*
141 	 * Read MULTIBOOT_SEARCH size in order to search for the
142 	 * multiboot magic header.
143 	 */
144 	header_search = malloc(MULTIBOOT_SEARCH);
145 	if (header_search == NULL) {
146 		close(fd);
147 		return (ENOMEM);
148 	}
149 
150 	search_size = read(fd, header_search, MULTIBOOT_SEARCH);
151 	magic = (uint32_t *)header_search;
152 
153 	header = NULL;
154 	for (i = 0; i < (search_size / sizeof(uint32_t)); i++) {
155 		if (magic[i] == MULTIBOOT_HEADER_MAGIC) {
156 			header = (struct multiboot_header *)&magic[i];
157 			break;
158 		}
159 	}
160 
161 	if (header == NULL) {
162 		error = EFTYPE;
163 		goto out;
164 	}
165 
166 	/* Valid multiboot header has been found, validate checksum */
167 	if (header->magic + header->flags + header->checksum != 0) {
168 		printf(
169 	"Multiboot checksum failed, magic: 0x%x flags: 0x%x checksum: 0x%x\n",
170 	header->magic, header->flags, header->checksum);
171 		error = EFTYPE;
172 		goto out;
173 	}
174 
175 	if ((header->flags & ~MULTIBOOT_SUPPORTED_FLAGS) != 0) {
176 		printf("Unsupported multiboot flags found: 0x%x\n",
177 		    header->flags);
178 		error = EFTYPE;
179 		goto out;
180 	}
181 	/* AOUT KLUDGE means we just load entire flat file as blob */
182 	if (header->flags & MULTIBOOT_AOUT_KLUDGE) {
183 		vm_offset_t laddr;
184 		int got;
185 
186 		dest = header->load_addr;
187 		if (lseek(fd, 0, SEEK_SET) == -1) {
188 			printf("lseek failed\n");
189 			error = EIO;
190 			goto out;
191 		}
192 		laddr = dest;
193 		for (;;) {
194 			got = archsw.arch_readin(fd, laddr, 4096);
195 			if (got == 0)
196 				break;
197 			if (got < 0) {
198 				printf("error reading: %s", strerror(errno));
199 				error = EIO;
200 				goto out;
201 			}
202 			laddr += got;
203 		}
204 
205 		fp = file_alloc();
206 		if (fp == NULL) {
207 			error = ENOMEM;
208 			goto out;
209 		}
210 		fp->f_name = strdup(filename);
211 		fp->f_type = strdup("aout multiboot kernel");
212 		fp->f_addr = header->entry_addr;
213 		fp->f_size = laddr - dest;
214 		if (fp->f_size == 0) {
215 			file_discard(fp);
216 			error = EIO;
217 			goto out;
218 		}
219 		fp->f_metadata = NULL;
220 		error = 0;
221 	} else {
222 		error = elf32_loadfile_raw(filename, dest, &fp, 1);
223 		if (error != 0) {
224 			printf("elf32_loadfile_raw failed: %d unable to "
225 			    "load multiboot kernel\n", error);
226 			goto out;
227 		}
228 	}
229 
230 	setenv("kernelname", fp->f_name, 1);
231 	bios_addsmapdata(fp);
232 	*result = fp;
233 out:
234 	free(header_search);
235 	close(fd);
236 	return (error);
237 }
238 
239 /*
240  * returns allocated virtual address from MB info area
241  */
242 static vm_offset_t
243 mb_malloc(size_t n)
244 {
245 	vm_offset_t ptr = last_addr;
246 	if (ptr + n >= high_heap_base)
247 		return (0);
248 	last_addr = roundup(last_addr + n, MULTIBOOT_INFO_ALIGN);
249 	return (ptr);
250 }
251 
252 static int
253 multiboot_exec(struct preloaded_file *fp)
254 {
255 	struct preloaded_file		*mfp;
256 	vm_offset_t			 module_start, metadata_size;
257 	vm_offset_t			 modulep, kernend, entry;
258 	struct file_metadata		*md;
259 	struct multiboot_info		*mb_info = NULL;
260 	struct multiboot_mod_list	*mb_mod = NULL;
261 	multiboot_memory_map_t		*mmap;
262 	struct bios_smap		*smap;
263 	struct devdesc			*rootdev;
264 	extern BOOTPLAYER		bootplayer;	/* dhcp info */
265 	char				*cmdline = NULL;
266 	size_t				 len;
267 	int				 error, num, i;
268 	int				 rootfs = 0;	/* flag for rootfs */
269 	int				 xen = 0;	/* flag for xen */
270 	int				 kernel = 0;	/* flag for kernel */
271 
272 	/* Set up base for mb_malloc. */
273 	for (mfp = fp; mfp->f_next != NULL; mfp = mfp->f_next);
274 
275 	/* Start info block from new page. */
276 	last_addr = roundup(mfp->f_addr + mfp->f_size, MULTIBOOT_MOD_ALIGN);
277 
278 	/* Allocate the multiboot struct and fill the basic details. */
279 	mb_info = (struct multiboot_info *)PTOV(mb_malloc(sizeof (*mb_info)));
280 
281 	bzero(mb_info, sizeof(struct multiboot_info));
282 	mb_info->flags = MULTIBOOT_INFO_MEMORY|MULTIBOOT_INFO_BOOT_LOADER_NAME;
283 	mb_info->mem_lower = bios_basemem / 1024;
284 	mb_info->mem_upper = bios_extmem / 1024;
285 	mb_info->boot_loader_name = mb_malloc(strlen(bootprog_info) + 1);
286 
287 	i386_copyin(bootprog_info, mb_info->boot_loader_name,
288 	    strlen(bootprog_info) + 1);
289 
290 	i386_getdev((void **)(&rootdev), NULL, NULL);
291 	if (rootdev == NULL) {
292 		printf("can't determine root device\n");
293 		error = EINVAL;
294 		goto error;
295 	}
296 
297 	/*
298 	 * Boot image command line. If args were not provided, we need to set
299 	 * args here, and that depends on image type...
300 	 * Fortunately we only have following options:
301 	 * 64 or 32 bit unix or xen. So we just check if f_name has unix.
302 	 */
303 	/* Do we boot xen? */
304 	if (strstr(fp->f_name, "unix") == NULL)
305 		xen = 1;
306 
307 	entry = fp->f_addr;
308 
309 	num = 0;
310 	for (mfp = fp->f_next; mfp != NULL; mfp = mfp->f_next) {
311 		num++;
312 		if (mfp->f_type != NULL && strcmp(mfp->f_type, "rootfs") == 0)
313 			rootfs++;
314 		if (mfp->f_type != NULL && strcmp(mfp->f_type, "kernel") == 0)
315 			kernel++;
316 	}
317 
318 	if (num == 0 || rootfs == 0) {
319 		/* We need at least one module - rootfs. */
320 		printf("No rootfs module provided, aborting\n");
321 		error = EINVAL;
322 		goto error;
323 	}
324 	if (xen == 1 && kernel == 0) {
325 		printf("No kernel module provided for xen, aborting\n");
326 		error = EINVAL;
327 		goto error;
328 	}
329 	mb_mod = (struct multiboot_mod_list *) PTOV(last_addr);
330 	last_addr += roundup(sizeof(*mb_mod) * num, MULTIBOOT_INFO_ALIGN);
331 
332 	bzero(mb_mod, sizeof(*mb_mod) * num);
333 
334 	num = 0;
335 	for (mfp = fp->f_next; mfp != NULL; mfp = mfp->f_next) {
336 		mb_mod[num].mod_start = mfp->f_addr;
337 		mb_mod[num].mod_end = mfp->f_addr + mfp->f_size;
338 
339 		if (strcmp(mfp->f_type, "kernel") == 0) {
340 			cmdline = NULL;
341 			error = mb_kernel_cmdline(mfp, rootdev, &cmdline);
342 			if (error != 0)
343 				goto error;
344 		} else {
345 			len = strlen(mfp->f_name) + 1;
346 			len += strlen(mfp->f_type) + 5 + 1;
347 			if (mfp->f_args != NULL) {
348 				len += strlen(mfp->f_args) + 1;
349 			}
350 			cmdline = malloc(len);
351 			if (cmdline == NULL) {
352 				error = ENOMEM;
353 				goto error;
354 			}
355 
356 			if (mfp->f_args != NULL)
357 				snprintf(cmdline, len, "%s type=%s %s",
358 				    mfp->f_name, mfp->f_type, mfp->f_args);
359 			else
360 				snprintf(cmdline, len, "%s type=%s",
361 				    mfp->f_name, mfp->f_type);
362 		}
363 
364 		mb_mod[num].cmdline = mb_malloc(strlen(cmdline)+1);
365 		i386_copyin(cmdline, mb_mod[num].cmdline, strlen(cmdline)+1);
366 		free(cmdline);
367 		num++;
368 	}
369 
370 	mb_info->mods_count = num;
371 	mb_info->mods_addr = VTOP(mb_mod);
372 	mb_info->flags |= MULTIBOOT_INFO_MODS;
373 
374 	md = file_findmetadata(fp, MODINFOMD_SMAP);
375 	if (md == NULL) {
376 		printf("no memory smap\n");
377 		error = EINVAL;
378 		goto error;
379 	}
380 
381 	num = md->md_size / sizeof(struct bios_smap); /* number of entries */
382 	mmap = (multiboot_memory_map_t *)PTOV(mb_malloc(sizeof(*mmap) * num));
383 
384 	mb_info->mmap_length = num * sizeof(*mmap);
385 	smap = (struct bios_smap *)md->md_data;
386 
387 	for (i = 0; i < num; i++) {
388 		mmap[i].size = sizeof(*smap);
389 		mmap[i].addr = smap[i].base;
390 		mmap[i].len = smap[i].length;
391 		mmap[i].type = smap[i].type;
392 	}
393 	mb_info->mmap_addr = VTOP(mmap);
394 	mb_info->flags |= MULTIBOOT_INFO_MEM_MAP;
395 
396 	if (strstr(getenv("loaddev"), "pxe") != NULL) {
397 		mb_info->drives_length = sizeof (BOOTPLAYER);
398 		mb_info->drives_addr = mb_malloc(mb_info->drives_length);
399 		i386_copyin(&bootplayer, mb_info->drives_addr,
400 		    mb_info->drives_length);
401 		mb_info->flags &= ~MULTIBOOT_INFO_DRIVE_INFO;
402 	}
403 	/*
404 	 * Set the image command line. Need to do this as last thing,
405 	 * as illumos kernel dboot_startkern will check cmdline
406 	 * address as last check to find first free address.
407 	 */
408 	if (fp->f_args == NULL) {
409 		if (xen)
410 			cmdline = getenv("xen_cmdline");
411 		else
412 			cmdline = getenv("boot-args");
413 		if (cmdline != NULL) {
414 			fp->f_args = strdup(cmdline);
415 			if (fp->f_args == NULL) {
416 				error = ENOMEM;
417 				goto error;
418 			}
419 		}
420 	}
421 
422 	/*
423 	 * If the image is xen, we just use f_name + f_args for commandline
424 	 * for unix, we need to add zfs-bootfs.
425 	 */
426 	if (xen) {
427 		len = strlen(fp->f_name) + 1;
428 		if (fp->f_args != NULL)
429 			len += strlen(fp->f_args) + 1;
430 
431 		if (fp->f_args != NULL) {
432 			if((cmdline = malloc(len)) == NULL) {
433 				error = ENOMEM;
434 				goto error;
435 			}
436 			snprintf(cmdline, len, "%s %s", fp->f_name, fp->f_args);
437 		} else {
438 			cmdline = strdup(fp->f_name);
439 			if (cmdline == NULL) {
440 				error = ENOMEM;
441 				goto error;
442 			}
443 		}
444 	} else {
445 		cmdline = NULL;
446 		if ((error = mb_kernel_cmdline(fp, rootdev, &cmdline)) != 0)
447 			goto error;
448 	}
449 
450 	mb_info->cmdline = mb_malloc(strlen(cmdline)+1);
451 	i386_copyin(cmdline, mb_info->cmdline, strlen(cmdline)+1);
452 	mb_info->flags |= MULTIBOOT_INFO_CMDLINE;
453 	free(cmdline);
454 	cmdline = NULL;
455 
456 	dev_cleanup();
457 	__exec((void *)VTOP(multiboot_tramp), MULTIBOOT_BOOTLOADER_MAGIC,
458 	    (void *)entry, (void *)VTOP(mb_info));
459 
460 	panic("exec returned");
461 
462 error:
463 	free(cmdline);
464 	return (error);
465 }
466 
467 static int
468 multiboot_obj_loadfile(char *filename, u_int64_t dest,
469     struct preloaded_file **result)
470 {
471 	struct preloaded_file	*mfp, *kfp, *rfp;
472 	struct kernel_module	*kmp;
473 	int			 error, mod_num;
474 
475 	/* See if there's a aout multiboot kernel loaded */
476 	mfp = file_findfile(NULL, "aout multiboot kernel");
477 	if (mfp != NULL) {
478 		/* we have normal kernel loaded, add module */
479 		rfp = file_loadraw(filename, "module", 0, NULL, 0);
480 		if (rfp == NULL) {
481 			printf(
482 			"Unable to load %s as a multiboot payload module\n",
483 			filename);
484 			return (EINVAL);
485 		}
486 		rfp->f_size = roundup(rfp->f_size, PAGE_SIZE);
487 		*result = rfp;
488 		return (0);
489 	}
490 
491 	/* See if there's a multiboot kernel loaded */
492 	mfp = file_findfile(NULL, "elf multiboot kernel");
493 	if (mfp == NULL) {
494 		return (EFTYPE);	/* this allows to check other methods */
495 	}
496 
497 	/*
498 	 * We have a multiboot kernel loaded, see if there's a
499 	 * kernel loaded also.
500 	 */
501 	kfp = file_findfile(NULL, "elf kernel");
502 	if (kfp == NULL) {
503 		/*
504 		 * No kernel loaded, this must be it. The kernel has to
505 		 * be loaded as a raw file, it will be processed by
506 		 * Xen and correctly loaded as an ELF file.
507 		 */
508 		rfp = file_loadraw(filename, "elf kernel", 0, NULL, 0);
509 		if (rfp == NULL) {
510 			printf(
511 			"Unable to load %s as a multiboot payload kernel\n",
512 			filename);
513 			return (EINVAL);
514 		}
515 
516 		/* Load kernel metadata... */
517 		setenv("kernelname", filename, 1);
518 		error = elf64_load_modmetadata(rfp, rfp->f_addr + rfp->f_size);
519 		if (error) {
520 			printf("Unable to load kernel %s metadata error: %d\n",
521 			    rfp->f_name, error);
522 			return (EINVAL);
523 		}
524 
525 		/*
526 		 * Save space at the end of the kernel in order to place
527 		 * the metadata information. We do an approximation of the
528 		 * max metadata size, this is not optimal but it's probably
529 		 * the best we can do at this point. Once all modules are
530 		 * loaded and the size of the metadata is known this
531 		 * space will be recovered if not used.
532 		 */
533 		mod_num = num_modules(rfp);
534 		rfp->f_size = roundup(rfp->f_size, PAGE_SIZE);
535 		rfp->f_size += METADATA_RESV_SIZE(mod_num);
536 		*result = rfp;
537 	} else {
538 		/* The rest should be loaded as regular modules */
539 		error = elf64_obj_loadfile(filename, dest, result);
540 		if (error != 0) {
541 			printf("Unable to load %s as an object file, error: %d",
542 			    filename, error);
543 			return (error);
544 		}
545 	}
546 
547 	return (0);
548 }
549 
550 static int
551 multiboot_obj_exec(struct preloaded_file *fp)
552 {
553 
554 	return (EFTYPE);
555 }
556