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