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