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