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
num_modules(struct preloaded_file * kfp)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
max_addr(void)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
multiboot_loadfile(char * filename,u_int64_t dest,struct preloaded_file ** result)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
mb_malloc(size_t n)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
multiboot_exec(struct preloaded_file * fp)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
multiboot_obj_loadfile(char * filename,u_int64_t dest,struct preloaded_file ** result)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
multiboot_obj_exec(struct preloaded_file * fp)543 multiboot_obj_exec(struct preloaded_file *fp)
544 {
545
546 return (EFTYPE);
547 }
548