1 /*-
2 * Copyright (c) 2021 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 multiboot2 implementation only implements a subset of the full
29 * multiboot2 specification in order to be able to boot Xen and a
30 * FreeBSD Dom0. Trying to use it to boot other multiboot2 compliant
31 * kernels will most surely fail.
32 *
33 * The full multiboot specification can be found here:
34 * https://www.gnu.org/software/grub/manual/multiboot2/multiboot.html
35 */
36
37 #include <sys/param.h>
38 #include <sys/exec.h>
39 #include <sys/linker.h>
40 #include <sys/module.h>
41 #include <sys/stdint.h>
42 #define _MACHINE_ELF_WANT_32BIT
43 #include <machine/elf.h>
44 #include <machine/metadata.h>
45 #include <string.h>
46 #include <stand.h>
47
48 #include <efi.h>
49 #include <efilib.h>
50
51 #include "bootstrap.h"
52 #include "multiboot2.h"
53 #include "loader_efi.h"
54
55 extern int elf32_loadfile_raw(char *filename, uint64_t dest,
56 struct preloaded_file **result, int multiboot);
57 extern int elf64_load_modmetadata(struct preloaded_file *fp, uint64_t dest);
58 extern int elf64_obj_loadfile(char *filename, uint64_t dest,
59 struct preloaded_file **result);
60 extern int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp,
61 bool exit_bs);
62
63 extern void multiboot2_exec(void *entry, uint64_t multiboot_info,
64 uint64_t stack);
65
66 /*
67 * Multiboot2 header information to pass between the loading and the exec
68 * functions.
69 */
70 struct mb2hdr {
71 uint32_t efi64_entry;
72 };
73
74 static int
loadfile(char * filename,uint64_t dest,struct preloaded_file ** result)75 loadfile(char *filename, uint64_t dest, struct preloaded_file **result)
76 {
77 unsigned int i;
78 int error, fd;
79 void *header_search = NULL;
80 void *multiboot = NULL;
81 ssize_t search_size;
82 struct multiboot_header *header;
83 char *cmdline;
84 struct mb2hdr hdr;
85 bool keep_bs = false;
86
87 /*
88 * Read MULTIBOOT_SEARCH size in order to search for the
89 * multiboot magic header.
90 */
91 if (filename == NULL)
92 return (EFTYPE);
93 if ((fd = open(filename, O_RDONLY)) == -1)
94 return (errno);
95 header_search = malloc(MULTIBOOT_SEARCH);
96 if (header_search == NULL) {
97 error = ENOMEM;
98 goto out;
99 }
100 search_size = read(fd, header_search, MULTIBOOT_SEARCH);
101
102 for (i = 0; i < search_size; i += MULTIBOOT_HEADER_ALIGN) {
103 header = header_search + i;
104 if (header->magic == MULTIBOOT2_HEADER_MAGIC)
105 break;
106 }
107
108 if (i >= search_size) {
109 error = EFTYPE;
110 goto out;
111 }
112
113 /* Valid multiboot header has been found, validate checksum */
114 if (header->magic + header->architecture + header->header_length +
115 header->checksum != 0) {
116 printf("Multiboot checksum failed, magic: %#x "
117 "architecture: %#x header_length %#x checksum: %#x\n",
118 header->magic, header->architecture, header->header_length,
119 header->checksum);
120 error = EFTYPE;
121 goto out;
122 }
123
124 if (header->architecture != MULTIBOOT2_ARCHITECTURE_I386) {
125 printf("Unsupported architecture: %#x\n",
126 header->architecture);
127 error = EFTYPE;
128 goto out;
129 }
130
131 multiboot = malloc(header->header_length - sizeof(*header));
132 error = lseek(fd, i + sizeof(*header), SEEK_SET);
133 if (error != i + sizeof(*header)) {
134 printf("Unable to set file pointer to header location: %d\n",
135 error);
136 goto out;
137 }
138 search_size = read(fd, multiboot,
139 header->header_length - sizeof(*header));
140
141 bzero(&hdr, sizeof(hdr));
142 for (i = 0; i < search_size; ) {
143 struct multiboot_header_tag *tag;
144 struct multiboot_header_tag_entry_address *entry;
145 struct multiboot_header_tag_information_request *req;
146 unsigned int j;
147
148 tag = multiboot + i;
149
150 switch(tag->type) {
151 case MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST:
152 req = (void *)tag;
153 for (j = 0;
154 j < (tag->size - sizeof(*tag)) / sizeof(uint32_t);
155 j++) {
156 switch (req->requests[j]) {
157 case MULTIBOOT_TAG_TYPE_MMAP:
158 case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO:
159 /* Only applicable to BIOS. */
160 break;
161
162 case MULTIBOOT_TAG_TYPE_EFI_BS:
163 case MULTIBOOT_TAG_TYPE_EFI64:
164 case MULTIBOOT_TAG_TYPE_EFI64_IH:
165 /* Tags unconditionally added. */
166 break;
167
168 default:
169 if (req->flags &
170 MULTIBOOT_HEADER_TAG_OPTIONAL)
171 break;
172
173 printf(
174 "Unknown non-optional information request %u\n",
175 req->requests[j]);
176 error = EINVAL;
177 goto out;
178 }
179 }
180 break;
181
182 case MULTIBOOT_HEADER_TAG_EFI_BS:
183 /* Never shut down BS. */
184 keep_bs = true;
185 break;
186
187 case MULTIBOOT_HEADER_TAG_MODULE_ALIGN:
188 /* We will align modules by default already. */
189 case MULTIBOOT_HEADER_TAG_END:
190 break;
191
192 case MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64:
193 entry = (void *)tag;
194 hdr.efi64_entry = entry->entry_addr;
195 break;
196
197 default:
198 if (tag->flags & MULTIBOOT_HEADER_TAG_OPTIONAL)
199 break;
200 printf("Unknown header tag %#x not optional\n",
201 tag->type);
202 error = EINVAL;
203 goto out;
204 }
205
206 i += roundup2(tag->size, MULTIBOOT_TAG_ALIGN);
207 if (tag->type == MULTIBOOT_HEADER_TAG_END)
208 break;
209 }
210
211 if (hdr.efi64_entry == 0) {
212 printf("No EFI64 entry address provided\n");
213 error = EINVAL;
214 goto out;
215 }
216 if (!keep_bs) {
217 printf("Unable to boot MB2 with BS exited\n");
218 error = EINVAL;
219 goto out;
220 }
221
222 error = elf32_loadfile_raw(filename, dest, result, 1);
223 if (error != 0) {
224 printf(
225 "elf32_loadfile_raw failed: %d unable to load multiboot kernel\n",
226 error);
227 goto out;
228 }
229
230 file_addmetadata(*result, MODINFOMD_NOCOPY | MODINFOMD_MB2HDR,
231 sizeof(hdr), &hdr);
232
233 /*
234 * f_addr is already aligned to PAGE_SIZE, make sure
235 * f_size it's also aligned so when the modules are loaded
236 * they are aligned to PAGE_SIZE.
237 */
238 (*result)->f_size = roundup((*result)->f_size, PAGE_SIZE);
239
240 out:
241 if (header_search != NULL)
242 free(header_search);
243 if (multiboot != NULL)
244 free(multiboot);
245 close(fd);
246 return (error);
247 }
248
add_string(void * buf,unsigned int type,const char * str)249 static unsigned int add_string(void *buf, unsigned int type, const char *str)
250 {
251 struct multiboot_tag *tag;
252
253 tag = buf;
254 tag->type = type;
255 tag->size = sizeof(*tag) + strlen(str) + 1;
256 strcpy(buf + sizeof(*tag), str);
257 return (roundup2(tag->size, MULTIBOOT_TAG_ALIGN));
258 }
259
add_efi(void * buf)260 static unsigned int add_efi(void *buf)
261 {
262 struct multiboot_tag *bs;
263 struct multiboot_tag_efi64 *efi64;
264 struct multiboot_tag_efi64_ih *ih;
265 unsigned int len;
266
267 len = 0;
268 bs = buf;
269 bs->type = MULTIBOOT_TAG_TYPE_EFI_BS;
270 bs->size = sizeof(*bs);
271 len += roundup2(bs->size, MULTIBOOT_TAG_ALIGN);
272
273 efi64 = buf + len;
274 efi64->type = MULTIBOOT_TAG_TYPE_EFI64;
275 efi64->size = sizeof(*efi64);
276 efi64->pointer = (uintptr_t)ST;
277 len += roundup2(efi64->size, MULTIBOOT_TAG_ALIGN);
278
279 ih = buf + len;
280 ih->type = MULTIBOOT_TAG_TYPE_EFI64_IH;
281 ih->size = sizeof(*ih);
282 ih->pointer = (uintptr_t)IH;
283
284 return (len + roundup2(ih->size, MULTIBOOT_TAG_ALIGN));
285 }
286
add_module(void * buf,vm_offset_t start,vm_offset_t end,const char * cmdline)287 static unsigned int add_module(void *buf, vm_offset_t start, vm_offset_t end,
288 const char *cmdline)
289 {
290 struct multiboot_tag_module *mod;
291
292 mod = buf;
293 mod->type = MULTIBOOT_TAG_TYPE_MODULE;
294 mod->size = sizeof(*mod);
295 mod->mod_start = start;
296 mod->mod_end = end;
297 if (cmdline != NULL)
298 {
299 strcpy(buf + sizeof(*mod), cmdline);
300 mod->size += strlen(cmdline) + 1;
301 }
302
303 return (roundup2(mod->size, MULTIBOOT_TAG_ALIGN));
304 }
305
add_end(void * buf)306 static unsigned int add_end(void *buf)
307 {
308 struct multiboot_tag *tag;
309
310 tag = buf;
311 tag->type = MULTIBOOT_TAG_TYPE_END;
312 tag->size = sizeof(*tag);
313
314 return (roundup2(tag->size, MULTIBOOT_TAG_ALIGN));
315 }
316
317 static int
exec(struct preloaded_file * fp)318 exec(struct preloaded_file *fp)
319 {
320 EFI_PHYSICAL_ADDRESS addr = 0;
321 EFI_PHYSICAL_ADDRESS stack = 0;
322 EFI_STATUS status;
323 void *multiboot_space;
324 vm_offset_t modulep, kernend, kern_base,
325 payload_base;
326 char *cmdline = NULL;
327 size_t len;
328 int error;
329 uint32_t *total_size;
330 struct file_metadata *md;
331 struct xen_header header;
332 struct mb2hdr *hdr;
333
334
335 _Static_assert(sizeof(header) <= PAGE_SIZE, "header too big");
336
337 if ((md = file_findmetadata(fp,
338 MODINFOMD_NOCOPY | MODINFOMD_MB2HDR)) == NULL) {
339 printf("Missing Multiboot2 EFI64 entry point\n");
340 return(EFTYPE);
341 }
342 hdr = (void *)&md->md_data;
343
344 status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
345 EFI_SIZE_TO_PAGES(PAGE_SIZE), &addr);
346 if (EFI_ERROR(status)) {
347 printf("Failed to allocate pages for multiboot2 header: %lu\n",
348 EFI_ERROR_CODE(status));
349 error = ENOMEM;
350 goto error;
351 }
352 status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
353 EFI_SIZE_TO_PAGES(128 * 1024), &stack);
354 if (EFI_ERROR(status)) {
355 printf("Failed to allocate pages for Xen stack: %lu\n",
356 EFI_ERROR_CODE(status));
357 error = ENOMEM;
358 goto error;
359 }
360
361 /*
362 * Scratch space to build the multiboot2 header. Reserve the start of
363 * the space to place the header with the size, which we don't know
364 * yet.
365 */
366 multiboot_space = (void *)(uintptr_t)(addr + sizeof(uint32_t) * 2);
367
368 /*
369 * Don't pass the memory size found by the bootloader, the memory
370 * available to Dom0 will be lower than that.
371 */
372 unsetenv("smbios.memory.enabled");
373
374 /* Set the Xen command line. */
375 if (fp->f_args == NULL) {
376 /* Add the Xen command line if it is set. */
377 cmdline = getenv("xen_cmdline");
378 if (cmdline != NULL) {
379 fp->f_args = strdup(cmdline);
380 if (fp->f_args == NULL) {
381 error = ENOMEM;
382 goto error;
383 }
384 }
385 }
386 if (fp->f_args != NULL) {
387 len = strlen(fp->f_name) + 1 + strlen(fp->f_args) + 1;
388 cmdline = malloc(len);
389 if (cmdline == NULL) {
390 error = ENOMEM;
391 goto error;
392 }
393 snprintf(cmdline, len, "%s %s", fp->f_name, fp->f_args);
394 multiboot_space += add_string(multiboot_space,
395 MULTIBOOT_TAG_TYPE_CMDLINE, cmdline);
396 free(cmdline);
397 }
398
399 multiboot_space += add_string(multiboot_space,
400 MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME, "FreeBSD Loader");
401 multiboot_space += add_efi(multiboot_space);
402
403 /*
404 * Prepare the multiboot module list, Xen assumes the first
405 * module is the Dom0 kernel, and the second one is the initramfs.
406 * This is not optimal for FreeBSD, that doesn't have a initramfs
407 * but instead loads modules dynamically and creates the metadata
408 * info on-the-fly.
409 *
410 * As expected, the first multiboot module is going to be the
411 * FreeBSD kernel loaded as a raw file. The second module is going
412 * to contain the metadata info and the loaded modules.
413 *
414 * There's a small header prefixed in the second module that contains
415 * some information required to calculate the relocated address of
416 * modulep based on the original offset of modulep from the start of
417 * the module address. Note other fields might be added to this header
418 * if required.
419 *
420 * Native layout:
421 * fp->f_addr + fp->f_size
422 * +---------+----------------+------------+
423 * | | | |
424 * | Kernel | Modules | Metadata |
425 * | | | |
426 * +---------+----------------+------------+
427 * fp->f_addr modulep kernend
428 *
429 * Xen dom0 layout:
430 * fp->f_addr fp->f_addr + fp->f_size
431 * +---------+------------+----------------+------------+
432 * | | | | |
433 * | Kernel | xen_header | Modules | Metadata |
434 * | | | | |
435 * +---------+------------+----------------+------------+
436 * modulep kernend
437 * \________/\__________________________________________/
438 * module 0 module 1
439 */
440
441 fp = file_findfile(NULL, "elf kernel");
442 if (fp == NULL) {
443 printf("No FreeBSD kernel provided, aborting\n");
444 error = EINVAL;
445 goto error;
446 }
447
448 error = bi_load(fp->f_args, &modulep, &kernend, false);
449 if (error != 0)
450 goto error;
451
452 /*
453 * Note that the Xen kernel requires to be started with BootServices
454 * enabled, and hence we cannot use efi_copy_finish to relocate the
455 * loaded data from the staging area to the expected loaded addresses.
456 * This is fine because the Xen kernel is relocatable, so it can boot
457 * fine straight from the staging area. We use efi_translate to get the
458 * staging addresses where the kernels and metadata are currently
459 * loaded.
460 */
461 kern_base = (uintptr_t)efi_translate(fp->f_addr);
462 payload_base = kern_base + fp->f_size - PAGE_SIZE;
463 multiboot_space += add_module(multiboot_space, kern_base, payload_base,
464 NULL);
465 multiboot_space += add_module(multiboot_space, payload_base,
466 (uintptr_t)efi_translate(kernend), "header");
467
468 header.flags = XENHEADER_HAS_MODULEP_OFFSET;
469 header.modulep_offset = modulep - (fp->f_addr + fp->f_size - PAGE_SIZE);
470 archsw.arch_copyin(&header, fp->f_addr + fp->f_size - PAGE_SIZE,
471 sizeof(header));
472
473 multiboot_space += add_end(multiboot_space);
474 total_size = (uint32_t *)(uintptr_t)(addr);
475 *total_size = (uintptr_t)multiboot_space - addr;
476
477 if (*total_size > PAGE_SIZE)
478 panic("Multiboot header exceeds fixed size");
479
480 efi_time_fini();
481 dev_cleanup();
482 multiboot2_exec(efi_translate(hdr->efi64_entry), addr,
483 stack + 128 * 1024);
484
485 panic("exec returned");
486
487 error:
488 if (addr)
489 BS->FreePages(addr, EFI_SIZE_TO_PAGES(PAGE_SIZE));
490 if (stack)
491 BS->FreePages(stack, EFI_SIZE_TO_PAGES(128 * 1024));
492 return (error);
493 }
494
495 static int
obj_loadfile(char * filename,uint64_t dest,struct preloaded_file ** result)496 obj_loadfile(char *filename, uint64_t dest, struct preloaded_file **result)
497 {
498 struct preloaded_file *mfp, *kfp, *rfp;
499 struct kernel_module *kmp;
500 int error;
501
502 /* See if there's a multiboot kernel loaded */
503 mfp = file_findfile(NULL, "elf multiboot kernel");
504 if (mfp == NULL)
505 return (EFTYPE);
506
507 /*
508 * We have a multiboot kernel loaded, see if there's a FreeBSD
509 * kernel loaded also.
510 */
511 kfp = file_findfile(NULL, "elf kernel");
512 if (kfp == NULL) {
513 /*
514 * No kernel loaded, this must be it. The kernel has to
515 * be loaded as a raw file, it will be processed by
516 * Xen and correctly loaded as an ELF file.
517 */
518 rfp = file_loadraw(filename, "elf kernel", 0);
519 if (rfp == NULL) {
520 printf(
521 "Unable to load %s as a multiboot payload kernel\n",
522 filename);
523 return (EINVAL);
524 }
525
526 /* Load kernel metadata... */
527 setenv("kernelname", filename, 1);
528 error = elf64_load_modmetadata(rfp, rfp->f_addr + rfp->f_size);
529 if (error) {
530 printf("Unable to load kernel %s metadata error: %d\n",
531 rfp->f_name, error);
532 return (EINVAL);
533 }
534
535
536 /*
537 * Reserve one page at the end of the kernel to place some
538 * metadata in order to cope for Xen relocating the modules and
539 * the metadata information.
540 */
541 rfp->f_size = roundup(rfp->f_size, PAGE_SIZE);
542 rfp->f_size += PAGE_SIZE;
543 *result = rfp;
544 } else {
545 /* The rest should be loaded as regular modules */
546 error = elf64_obj_loadfile(filename, dest, result);
547 if (error != 0) {
548 printf("Unable to load %s as an object file, error: %d",
549 filename, error);
550 return (error);
551 }
552 }
553
554 return (0);
555 }
556
557 static int
obj_exec(struct preloaded_file * fp)558 obj_exec(struct preloaded_file *fp)
559 {
560
561 return (EFTYPE);
562 }
563
564 struct file_format multiboot2 = { loadfile, exec };
565 struct file_format multiboot2_obj = { obj_loadfile, obj_exec };
566