From nobody Tue Jan 24 22:09:41 2023 X-Original-To: dev-commits-src-branches@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4P1h3B0CrSz3bdfm; Tue, 24 Jan 2023 22:09:42 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4P1h396yMlz4GNK; Tue, 24 Jan 2023 22:09:41 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1674598181; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=N+DGHP3IdmwzjbcwTU6VZsAozb8RiNd3huzskmxzrtw=; b=xQVUnFt9QPuPzA1c/95fCXa3wGH0j1UEkgnjgC3d2A0bBHAtvGwsYYPcNQdPEUYoqXD7j7 KJhGT0hfwAzdXb+9qDF9ECoKU9X5j3Zam/fz25VEf0u3r/oXiYvxGHj0HIwjkF6zbqjKa5 6C3Nz7bzvzRUlioy/TuLs9bHZBUm4iRf9CtFZ1U2IDTyQOp9MzuJKL7jeT/JQiVPqg846T kW2hP6m6uSA/LZ+j4/F5vx5rzNIIy5cwTQw7V577kS+Eah/RliXUc+MXsOKgoFPsE9NKfJ GeM2TXDwGXZ06uwCfaJ101Pfl+cbmIXscex3OBc8l//gFuXpMboqhNjP0ej70w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1674598181; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=N+DGHP3IdmwzjbcwTU6VZsAozb8RiNd3huzskmxzrtw=; b=Lf+Rd2TcfdUVwAnOJ3bKKqOYzMQDZ4eR8s/tj0yaYWXGpnkA+dRoKSXT5IbPp1rexap9VS xlBh14u88wcTtiFfNoVQWtlSgmJKKmkTqgWeb9VpNCj4XvM8k2KbTPV8SQG8P8jNMdzB9u u6UfEekwuFRD987N7pxzHpi/o4csCb7KDoNfJ3o3HYKu+tgpuDJj+grLJ78qHU7Ryv37rM 4HT4wWYZWfbrZ7Z84mIbaL40IGOHog/xArtohwtMuN9X7NXqkyKivA2LZG/Oc0LMyo44V4 kX7P1nA9lR5T98kTO/CTqpmrINylY5PxdBZVC1xSTv2kqmjOfcgq1IBrxmZ7Jw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1674598181; a=rsa-sha256; cv=none; b=mqsZ/njEeSY/RO40Ur+hqQMeV+GyOk2MXNQ9yI+ECrFfw82aHfk9YRhUxqAB9a3brrHwWE tD9zMrvz/YQfDKCozwtjreXQ0mvkHtaORg2uGib9wNaduFHMuYaEmp/NU1GAydrosjvMSV cZ0q4cbVXzFMPCXlQfRspIfx06wHs63lFhsOj58dRa8u7KUdqd/VxjDjEZ1Ij3ebRHNPEW z17tVKpDErmW1wFZ7wEzpQ1YbVKDG2lTT19dW0SyDNJTpuBVi7p0ggjT6Ao1dEmXC9SAzb t8OOmZcE1F96gFO5b3a9mhnDFLi2JrT1UeraRiyalgOtCRyabO2t9p8RAsYSJg== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4P1h39619tzQ19; Tue, 24 Jan 2023 22:09:41 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 30OM9fiT070527; Tue, 24 Jan 2023 22:09:41 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 30OM9fbk070526; Tue, 24 Jan 2023 22:09:41 GMT (envelope-from git) Date: Tue, 24 Jan 2023 22:09:41 GMT Message-Id: <202301242209.30OM9fbk070526@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Warner Losh Subject: git: 39917afeed9d - stable/13 - stand/multiboot2: add support for booting a Xen dom0 in UEFI mode List-Id: Commits to the stable branches of the FreeBSD src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-branches@freebsd.org X-BeenThere: dev-commits-src-branches@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: imp X-Git-Repository: src X-Git-Refname: refs/heads/stable/13 X-Git-Reftype: branch X-Git-Commit: 39917afeed9de5f01445f9a08b8aec22153387a0 Auto-Submitted: auto-generated X-ThisMailContainsUnwantedMimeParts: N The branch stable/13 has been updated by imp: URL: https://cgit.FreeBSD.org/src/commit/?id=39917afeed9de5f01445f9a08b8aec22153387a0 commit 39917afeed9de5f01445f9a08b8aec22153387a0 Author: Roger Pau Monné AuthorDate: 2021-01-27 11:23:32 +0000 Commit: Warner Losh CommitDate: 2023-01-24 21:49:16 +0000 stand/multiboot2: add support for booting a Xen dom0 in UEFI mode Add some basic multiboot2 infrastructure to the EFI loader in order to be capable of booting a FreeBSD/Xen dom0 when booted from UEFI. Only a very limited subset of the multiboot2 protocol is implemented in order to support enough to boot into Xen, the implementation doesn't intend to be a full multiboot2 capable implementation. Such multiboot2 functionality is hooked up into the amd64 EFI loader, which is the only architecture that supports Xen dom0 on FreeBSD. The options to boot a FreeBSD/Xen dom0 system are exactly the same as on BIOS, and requires setting the xen_kernel and xen_cmdline options in loader.conf. Sponsored by: Citrix Systems R&D Reviewed by: tsoome, imp Differential revision: https://reviews.freebsd.org/D28497 (cherry picked from commit adda2797eb2a29487fe26640a9c990fea7e6585d) stand/multiboot2: fix error message format Add a missing space in one error message. Sponsored by: Citrix Systems R&D MFC after: 3 days Fixes: adda2797eb2a ('stand/multiboot2: add support for booting a Xen dom0 in UEFI mode') (cherry picked from commit ab379c15af6fd7c2b94f0e91769fe7e1a4102e25) stand/multiboot2: fix header length check Check whether we have reached the end of the buffer using search_size instead of MULTIBOOT_SEARCH, which is the maximum defined by the specification, but the file can be shorter than that. This prevents printing a harmless error message when loading a file that is smaller than MULTIBOOT_SEARCH. Sponsored by: Citrix Systems R&D MFC after: 3 days Fixes: adda2797eb2a ('stand/multiboot2: add support for booting a Xen dom0 in UEFI mode') (cherry picked from commit 0eaa97f0e8629bcf678ff4de2678e4cba00f1c91) --- stand/efi/loader/arch/amd64/Makefile.inc | 1 + stand/efi/loader/arch/amd64/amd64_tramp.S | 12 + stand/efi/loader/arch/amd64/elf64_freebsd.c | 5 + stand/efi/loader/arch/amd64/multiboot2.c | 567 ++++++++++++++++++++++++++++ stand/efi/loader/arch/amd64/multiboot2.h | 416 ++++++++++++++++++++ sys/sys/linker.h | 1 + 6 files changed, 1002 insertions(+) diff --git a/stand/efi/loader/arch/amd64/Makefile.inc b/stand/efi/loader/arch/amd64/Makefile.inc index f64adf08ec13..0d9e2648cb59 100644 --- a/stand/efi/loader/arch/amd64/Makefile.inc +++ b/stand/efi/loader/arch/amd64/Makefile.inc @@ -4,6 +4,7 @@ SRCS+= amd64_tramp.S \ start.S \ elf64_freebsd.c \ trap.c \ + multiboot2.c \ exc.S .PATH: ${BOOTSRC}/i386/libi386 diff --git a/stand/efi/loader/arch/amd64/amd64_tramp.S b/stand/efi/loader/arch/amd64/amd64_tramp.S index c102d9243589..877705407f92 100644 --- a/stand/efi/loader/arch/amd64/amd64_tramp.S +++ b/stand/efi/loader/arch/amd64/amd64_tramp.S @@ -30,6 +30,9 @@ #include +#define ASM_FILE +#include "multiboot2.h" + .text .globl amd64_tramp @@ -58,6 +61,15 @@ amd64_tramp: ALIGN_TEXT amd64_tramp_end: +/* void multiboot2_exec(uint64_t entry, uint64_t multiboot_info, uint64_t stack) */ + .globl multiboot2_exec +multiboot2_exec: + movq %rdx,%rsp + pushq %rdi + movq %rsi,%rbx + movq $MULTIBOOT2_BOOTLOADER_MAGIC,%rax + ret + .data .globl amd64_tramp_size amd64_tramp_size: diff --git a/stand/efi/loader/arch/amd64/elf64_freebsd.c b/stand/efi/loader/arch/amd64/elf64_freebsd.c index 354f86f090ce..4bdf675cd5a3 100644 --- a/stand/efi/loader/arch/amd64/elf64_freebsd.c +++ b/stand/efi/loader/arch/amd64/elf64_freebsd.c @@ -69,7 +69,12 @@ static struct file_format amd64_elf_obj = { .l_exec = elf64_obj_exec, }; +extern struct file_format multiboot2; +extern struct file_format multiboot2_obj; + struct file_format *file_formats[] = { + &multiboot2, + &multiboot2_obj, &amd64_elf, &amd64_elf_obj, NULL diff --git a/stand/efi/loader/arch/amd64/multiboot2.c b/stand/efi/loader/arch/amd64/multiboot2.c new file mode 100644 index 000000000000..4d7b2713685d --- /dev/null +++ b/stand/efi/loader/arch/amd64/multiboot2.c @@ -0,0 +1,567 @@ +/*- + * Copyright (c) 2021 Roger Pau Monné + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * This multiboot2 implementation only implements a subset of the full + * multiboot2 specification in order to be able to boot Xen and a + * FreeBSD Dom0. Trying to use it to boot other multiboot2 compliant + * kernels will most surely fail. + * + * The full multiboot specification can be found here: + * https://www.gnu.org/software/grub/manual/multiboot2/multiboot.html + */ + +#include + +#include +#include +#include +#include +#include +#define _MACHINE_ELF_WANT_32BIT +#include +#include +#include +#include + +#include +#include + +#include "bootstrap.h" +#include "multiboot2.h" +#include "loader_efi.h" + +extern int elf32_loadfile_raw(char *filename, uint64_t dest, + struct preloaded_file **result, int multiboot); +extern int elf64_load_modmetadata(struct preloaded_file *fp, uint64_t dest); +extern int elf64_obj_loadfile(char *filename, uint64_t dest, + struct preloaded_file **result); +extern int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp, + bool exit_bs); + +extern void multiboot2_exec(void *entry, uint64_t multiboot_info, + uint64_t stack); + +/* + * Multiboot2 header information to pass between the loading and the exec + * functions. + */ +struct mb2hdr { + uint32_t efi64_entry; +}; + +static int +loadfile(char *filename, uint64_t dest, struct preloaded_file **result) +{ + unsigned int i; + int error, fd; + void *header_search = NULL; + void *multiboot = NULL; + ssize_t search_size; + struct multiboot_header *header; + char *cmdline; + struct mb2hdr hdr; + bool keep_bs = false; + + /* + * Read MULTIBOOT_SEARCH size in order to search for the + * multiboot magic header. + */ + if (filename == NULL) + return (EFTYPE); + if ((fd = open(filename, O_RDONLY)) == -1) + return (errno); + header_search = malloc(MULTIBOOT_SEARCH); + if (header_search == NULL) { + error = ENOMEM; + goto out; + } + search_size = read(fd, header_search, MULTIBOOT_SEARCH); + + for (i = 0; i < search_size; i += MULTIBOOT_HEADER_ALIGN) { + header = header_search + i; + if (header->magic == MULTIBOOT2_HEADER_MAGIC) + break; + } + + if (i >= search_size) { + error = EFTYPE; + goto out; + } + + /* Valid multiboot header has been found, validate checksum */ + if (header->magic + header->architecture + header->header_length + + header->checksum != 0) { + printf("Multiboot checksum failed, magic: %#x " + "architecture: %#x header_length %#x checksum: %#x\n", + header->magic, header->architecture, header->header_length, + header->checksum); + error = EFTYPE; + goto out; + } + + if (header->architecture != MULTIBOOT2_ARCHITECTURE_I386) { + printf("Unsupported architecture: %#x\n", + header->architecture); + error = EFTYPE; + goto out; + } + + multiboot = malloc(header->header_length - sizeof(*header)); + error = lseek(fd, i + sizeof(*header), SEEK_SET); + if (error != i + sizeof(*header)) { + printf("Unable to set file pointer to header location: %d\n", + error); + goto out; + } + search_size = read(fd, multiboot, + header->header_length - sizeof(*header)); + + bzero(&hdr, sizeof(hdr)); + for (i = 0; i < search_size; ) { + struct multiboot_header_tag *tag; + struct multiboot_header_tag_entry_address *entry; + struct multiboot_header_tag_information_request *req; + unsigned int j; + + tag = multiboot + i; + + switch(tag->type) { + case MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST: + req = (void *)tag; + for (j = 0; + j < (tag->size - sizeof(*tag)) / sizeof(uint32_t); + j++) { + switch (req->requests[j]) { + case MULTIBOOT_TAG_TYPE_MMAP: + case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO: + /* Only applicable to BIOS. */ + break; + + case MULTIBOOT_TAG_TYPE_EFI_BS: + case MULTIBOOT_TAG_TYPE_EFI64: + case MULTIBOOT_TAG_TYPE_EFI64_IH: + /* Tags unconditionally added. */ + break; + + default: + if (req->flags & + MULTIBOOT_HEADER_TAG_OPTIONAL) + break; + + printf( + "Unknown non-optional information request %u\n", + req->requests[j]); + error = EINVAL; + goto out; + } + } + break; + + case MULTIBOOT_HEADER_TAG_EFI_BS: + /* Never shut down BS. */ + keep_bs = true; + break; + + case MULTIBOOT_HEADER_TAG_MODULE_ALIGN: + /* We will align modules by default already. */ + case MULTIBOOT_HEADER_TAG_END: + break; + + case MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64: + entry = (void *)tag; + hdr.efi64_entry = entry->entry_addr; + break; + + default: + if (tag->flags & MULTIBOOT_HEADER_TAG_OPTIONAL) + break; + printf("Unknown header tag %#x not optional\n", + tag->type); + error = EINVAL; + goto out; + } + + i += roundup2(tag->size, MULTIBOOT_TAG_ALIGN); + if (tag->type == MULTIBOOT_HEADER_TAG_END) + break; + } + + if (hdr.efi64_entry == 0) { + printf("No EFI64 entry address provided\n"); + error = EINVAL; + goto out; + } + if (!keep_bs) { + printf("Unable to boot MB2 with BS exited\n"); + error = EINVAL; + goto out; + } + + error = elf32_loadfile_raw(filename, dest, result, 1); + if (error != 0) { + printf( + "elf32_loadfile_raw failed: %d unable to load multiboot kernel\n", + error); + goto out; + } + + file_addmetadata(*result, MODINFOMD_NOCOPY | MODINFOMD_MB2HDR, + sizeof(hdr), &hdr); + + /* + * f_addr is already aligned to PAGE_SIZE, make sure + * f_size it's also aligned so when the modules are loaded + * they are aligned to PAGE_SIZE. + */ + (*result)->f_size = roundup((*result)->f_size, PAGE_SIZE); + +out: + if (header_search != NULL) + free(header_search); + if (multiboot != NULL) + free(multiboot); + close(fd); + return (error); +} + +static unsigned int add_string(void *buf, unsigned int type, const char *str) +{ + struct multiboot_tag *tag; + + tag = buf; + tag->type = type; + tag->size = sizeof(*tag) + strlen(str) + 1; + strcpy(buf + sizeof(*tag), str); + return (roundup2(tag->size, MULTIBOOT_TAG_ALIGN)); +} + +static unsigned int add_efi(void *buf) +{ + struct multiboot_tag *bs; + struct multiboot_tag_efi64 *efi64; + struct multiboot_tag_efi64_ih *ih; + unsigned int len; + + len = 0; + bs = buf; + bs->type = MULTIBOOT_TAG_TYPE_EFI_BS; + bs->size = sizeof(*bs); + len += roundup2(bs->size, MULTIBOOT_TAG_ALIGN); + + efi64 = buf + len; + efi64->type = MULTIBOOT_TAG_TYPE_EFI64; + efi64->size = sizeof(*efi64); + efi64->pointer = (uintptr_t)ST; + len += roundup2(efi64->size, MULTIBOOT_TAG_ALIGN); + + ih = buf + len; + ih->type = MULTIBOOT_TAG_TYPE_EFI64_IH; + ih->size = sizeof(*ih); + ih->pointer = (uintptr_t)IH; + + return (len + roundup2(ih->size, MULTIBOOT_TAG_ALIGN)); +} + +static unsigned int add_module(void *buf, vm_offset_t start, vm_offset_t end, + const char *cmdline) +{ + struct multiboot_tag_module *mod; + + mod = buf; + mod->type = MULTIBOOT_TAG_TYPE_MODULE; + mod->size = sizeof(*mod); + mod->mod_start = start; + mod->mod_end = end; + if (cmdline != NULL) + { + strcpy(buf + sizeof(*mod), cmdline); + mod->size += strlen(cmdline) + 1; + } + + return (roundup2(mod->size, MULTIBOOT_TAG_ALIGN)); +} + +static unsigned int add_end(void *buf) +{ + struct multiboot_tag *tag; + + tag = buf; + tag->type = MULTIBOOT_TAG_TYPE_END; + tag->size = sizeof(*tag); + + return (roundup2(tag->size, MULTIBOOT_TAG_ALIGN)); +} + +static int +exec(struct preloaded_file *fp) +{ + EFI_PHYSICAL_ADDRESS addr = 0; + EFI_PHYSICAL_ADDRESS stack = 0; + EFI_STATUS status; + void *multiboot_space; + vm_offset_t modulep, kernend, kern_base, + payload_base; + char *cmdline = NULL; + size_t len; + int error; + uint32_t *total_size; + struct file_metadata *md; + struct xen_header header; + struct mb2hdr *hdr; + + + CTASSERT(sizeof(header) <= PAGE_SIZE); + + if ((md = file_findmetadata(fp, + MODINFOMD_NOCOPY | MODINFOMD_MB2HDR)) == NULL) { + printf("Missing Multiboot2 EFI64 entry point\n"); + return(EFTYPE); + } + hdr = (void *)&md->md_data; + + status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, + EFI_SIZE_TO_PAGES(PAGE_SIZE), &addr); + if (EFI_ERROR(status)) { + printf("Failed to allocate pages for multiboot2 header: %lu\n", + EFI_ERROR_CODE(status)); + error = ENOMEM; + goto error; + } + status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, + EFI_SIZE_TO_PAGES(128 * 1024), &stack); + if (EFI_ERROR(status)) { + printf("Failed to allocate pages for Xen stack: %lu\n", + EFI_ERROR_CODE(status)); + error = ENOMEM; + goto error; + } + + /* + * Scratch space to build the multiboot2 header. Reserve the start of + * the space to place the header with the size, which we don't know + * yet. + */ + multiboot_space = (void *)(uintptr_t)(addr + sizeof(uint32_t) * 2); + + /* + * Don't pass the memory size found by the bootloader, the memory + * available to Dom0 will be lower than that. + */ + unsetenv("smbios.memory.enabled"); + + /* Set the Xen command line. */ + if (fp->f_args == NULL) { + /* Add the Xen command line if it is set. */ + cmdline = getenv("xen_cmdline"); + if (cmdline != NULL) { + fp->f_args = strdup(cmdline); + if (fp->f_args == NULL) { + error = ENOMEM; + goto error; + } + } + } + if (fp->f_args != NULL) { + len = strlen(fp->f_name) + 1 + strlen(fp->f_args) + 1; + cmdline = malloc(len); + if (cmdline == NULL) { + error = ENOMEM; + goto error; + } + snprintf(cmdline, len, "%s %s", fp->f_name, fp->f_args); + multiboot_space += add_string(multiboot_space, + MULTIBOOT_TAG_TYPE_CMDLINE, cmdline); + free(cmdline); + } + + multiboot_space += add_string(multiboot_space, + MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME, "FreeBSD Loader"); + multiboot_space += add_efi(multiboot_space); + + /* + * Prepare the multiboot module list, Xen assumes the first + * module is the Dom0 kernel, and the second one is the initramfs. + * This is not optimal for FreeBSD, that doesn't have a initramfs + * but instead loads modules dynamically and creates the metadata + * info on-the-fly. + * + * As expected, the first multiboot module is going to be the + * FreeBSD kernel loaded as a raw file. The second module is going + * to contain the metadata info and the loaded modules. + * + * There's a small header prefixed in the second module that contains + * some information required to calculate the relocated address of + * modulep based on the original offset of modulep from the start of + * the module address. Note other fields might be added to this header + * if required. + * + * Native layout: + * fp->f_addr + fp->f_size + * +---------+----------------+------------+ + * | | | | + * | Kernel | Modules | Metadata | + * | | | | + * +---------+----------------+------------+ + * fp->f_addr modulep kernend + * + * Xen dom0 layout: + * fp->f_addr fp->f_addr + fp->f_size + * +---------+------------+----------------+------------+ + * | | | | | + * | Kernel | xen_header | Modules | Metadata | + * | | | | | + * +---------+------------+----------------+------------+ + * modulep kernend + * \________/\__________________________________________/ + * module 0 module 1 + */ + + fp = file_findfile(NULL, "elf kernel"); + if (fp == NULL) { + printf("No FreeBSD kernel provided, aborting\n"); + error = EINVAL; + goto error; + } + + error = bi_load(fp->f_args, &modulep, &kernend, false); + if (error != 0) + goto error; + + /* + * Note that the Xen kernel requires to be started with BootServices + * enabled, and hence we cannot use efi_copy_finish to relocate the + * loaded data from the staging area to the expected loaded addresses. + * This is fine because the Xen kernel is relocatable, so it can boot + * fine straight from the staging area. We use efi_translate to get the + * staging addresses where the kernels and metadata are currently + * loaded. + */ + kern_base = (uintptr_t)efi_translate(fp->f_addr); + payload_base = kern_base + fp->f_size - PAGE_SIZE; + multiboot_space += add_module(multiboot_space, kern_base, payload_base, + NULL); + multiboot_space += add_module(multiboot_space, payload_base, + (uintptr_t)efi_translate(kernend), "header"); + + header.flags = XENHEADER_HAS_MODULEP_OFFSET; + header.modulep_offset = modulep - (fp->f_addr + fp->f_size - PAGE_SIZE); + archsw.arch_copyin(&header, fp->f_addr + fp->f_size - PAGE_SIZE, + sizeof(header)); + + multiboot_space += add_end(multiboot_space); + total_size = (uint32_t *)(uintptr_t)(addr); + *total_size = (uintptr_t)multiboot_space - addr; + + if (*total_size > PAGE_SIZE) + panic("Multiboot header exceeds fixed size"); + + efi_time_fini(); + dev_cleanup(); + multiboot2_exec(efi_translate(hdr->efi64_entry), addr, + stack + 128 * 1024); + + panic("exec returned"); + +error: + if (addr) + BS->FreePages(addr, EFI_SIZE_TO_PAGES(PAGE_SIZE)); + if (stack) + BS->FreePages(stack, EFI_SIZE_TO_PAGES(128 * 1024)); + return (error); +} + +static int +obj_loadfile(char *filename, uint64_t dest, struct preloaded_file **result) +{ + struct preloaded_file *mfp, *kfp, *rfp; + struct kernel_module *kmp; + int error; + + /* See if there's a multiboot kernel loaded */ + mfp = file_findfile(NULL, "elf multiboot kernel"); + if (mfp == NULL) + return (EFTYPE); + + /* + * We have a multiboot kernel loaded, see if there's a FreeBSD + * kernel loaded also. + */ + kfp = file_findfile(NULL, "elf kernel"); + if (kfp == NULL) { + /* + * No kernel loaded, this must be it. The kernel has to + * be loaded as a raw file, it will be processed by + * Xen and correctly loaded as an ELF file. + */ + rfp = file_loadraw(filename, "elf kernel", 0); + if (rfp == NULL) { + printf( + "Unable to load %s as a multiboot payload kernel\n", + filename); + return (EINVAL); + } + + /* Load kernel metadata... */ + setenv("kernelname", filename, 1); + error = elf64_load_modmetadata(rfp, rfp->f_addr + rfp->f_size); + if (error) { + printf("Unable to load kernel %s metadata error: %d\n", + rfp->f_name, error); + return (EINVAL); + } + + + /* + * Reserve one page at the end of the kernel to place some + * metadata in order to cope for Xen relocating the modules and + * the metadata information. + */ + rfp->f_size = roundup(rfp->f_size, PAGE_SIZE); + rfp->f_size += PAGE_SIZE; + *result = rfp; + } else { + /* The rest should be loaded as regular modules */ + error = elf64_obj_loadfile(filename, dest, result); + if (error != 0) { + printf("Unable to load %s as an object file, error: %d", + filename, error); + return (error); + } + } + + return (0); +} + +static int +obj_exec(struct preloaded_file *fp) +{ + + return (EFTYPE); +} + +struct file_format multiboot2 = { loadfile, exec }; +struct file_format multiboot2_obj = { obj_loadfile, obj_exec }; diff --git a/stand/efi/loader/arch/amd64/multiboot2.h b/stand/efi/loader/arch/amd64/multiboot2.h new file mode 100644 index 000000000000..5693923c014f --- /dev/null +++ b/stand/efi/loader/arch/amd64/multiboot2.h @@ -0,0 +1,416 @@ +/* multiboot2.h - Multiboot 2 header file. */ +/* Copyright (C) 1999,2003,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY + * DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR + * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef MULTIBOOT_HEADER +#define MULTIBOOT_HEADER 1 + +/* How many bytes from the start of the file we search for the header. */ +#define MULTIBOOT_SEARCH 32768 +#define MULTIBOOT_HEADER_ALIGN 8 + +/* The magic field should contain this. */ +#define MULTIBOOT2_HEADER_MAGIC 0xe85250d6 + +/* This should be in %eax. */ +#define MULTIBOOT2_BOOTLOADER_MAGIC 0x36d76289 + +/* Alignment of multiboot modules. */ +#define MULTIBOOT_MOD_ALIGN 0x00001000 + +/* Alignment of the multiboot info structure. */ +#define MULTIBOOT_INFO_ALIGN 0x00000008 + +/* Flags set in the 'flags' member of the multiboot header. */ + +#define MULTIBOOT_TAG_ALIGN 8 +#define MULTIBOOT_TAG_TYPE_END 0 +#define MULTIBOOT_TAG_TYPE_CMDLINE 1 +#define MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME 2 +#define MULTIBOOT_TAG_TYPE_MODULE 3 +#define MULTIBOOT_TAG_TYPE_BASIC_MEMINFO 4 +#define MULTIBOOT_TAG_TYPE_BOOTDEV 5 +#define MULTIBOOT_TAG_TYPE_MMAP 6 +#define MULTIBOOT_TAG_TYPE_VBE 7 +#define MULTIBOOT_TAG_TYPE_FRAMEBUFFER 8 +#define MULTIBOOT_TAG_TYPE_ELF_SECTIONS 9 +#define MULTIBOOT_TAG_TYPE_APM 10 +#define MULTIBOOT_TAG_TYPE_EFI32 11 +#define MULTIBOOT_TAG_TYPE_EFI64 12 +#define MULTIBOOT_TAG_TYPE_SMBIOS 13 +#define MULTIBOOT_TAG_TYPE_ACPI_OLD 14 +#define MULTIBOOT_TAG_TYPE_ACPI_NEW 15 +#define MULTIBOOT_TAG_TYPE_NETWORK 16 +#define MULTIBOOT_TAG_TYPE_EFI_MMAP 17 +#define MULTIBOOT_TAG_TYPE_EFI_BS 18 +#define MULTIBOOT_TAG_TYPE_EFI32_IH 19 +#define MULTIBOOT_TAG_TYPE_EFI64_IH 20 +#define MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR 21 + +#define MULTIBOOT_HEADER_TAG_END 0 +#define MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST 1 +#define MULTIBOOT_HEADER_TAG_ADDRESS 2 +#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS 3 +#define MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS 4 +#define MULTIBOOT_HEADER_TAG_FRAMEBUFFER 5 +#define MULTIBOOT_HEADER_TAG_MODULE_ALIGN 6 +#define MULTIBOOT_HEADER_TAG_EFI_BS 7 +#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64 9 +#define MULTIBOOT_HEADER_TAG_RELOCATABLE 10 + +#define MULTIBOOT2_ARCHITECTURE_I386 0 +#define MULTIBOOT2_ARCHITECTURE_MIPS32 4 +#define MULTIBOOT_HEADER_TAG_OPTIONAL 1 + +#define MULTIBOOT_LOAD_PREFERENCE_NONE 0 +#define MULTIBOOT_LOAD_PREFERENCE_LOW 1 +#define MULTIBOOT_LOAD_PREFERENCE_HIGH 2 + +#define MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED 1 +#define MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED 2 + +#ifndef ASM_FILE + +typedef unsigned char multiboot_uint8_t; +typedef unsigned short multiboot_uint16_t; +typedef unsigned int multiboot_uint32_t; +typedef unsigned long long multiboot_uint64_t; + +struct multiboot_header +{ + /* Must be MULTIBOOT_MAGIC - see above. */ + multiboot_uint32_t magic; + + /* ISA */ + multiboot_uint32_t architecture; + + /* Total header length. */ + multiboot_uint32_t header_length; + + /* The above fields plus this one must equal 0 mod 2^32. */ + multiboot_uint32_t checksum; +}; + +struct multiboot_header_tag +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; +}; + +struct multiboot_header_tag_information_request +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t requests[0]; +}; + +struct multiboot_header_tag_address +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t header_addr; + multiboot_uint32_t load_addr; + multiboot_uint32_t load_end_addr; + multiboot_uint32_t bss_end_addr; +}; + +struct multiboot_header_tag_entry_address +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t entry_addr; +}; + +struct multiboot_header_tag_console_flags +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t console_flags; +}; + +struct multiboot_header_tag_framebuffer +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t width; + multiboot_uint32_t height; + multiboot_uint32_t depth; +}; + +struct multiboot_header_tag_module_align +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; +}; + +struct multiboot_header_tag_relocatable +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t min_addr; + multiboot_uint32_t max_addr; + multiboot_uint32_t align; + multiboot_uint32_t preference; +}; + +struct multiboot_color +{ + multiboot_uint8_t red; + multiboot_uint8_t green; + multiboot_uint8_t blue; +}; + +struct multiboot_mmap_entry +{ + multiboot_uint64_t addr; + multiboot_uint64_t len; +#define MULTIBOOT_MEMORY_AVAILABLE 1 +#define MULTIBOOT_MEMORY_RESERVED 2 +#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 +#define MULTIBOOT_MEMORY_NVS 4 +#define MULTIBOOT_MEMORY_BADRAM 5 + multiboot_uint32_t type; + multiboot_uint32_t zero; +}; +typedef struct multiboot_mmap_entry multiboot_memory_map_t; + +struct multiboot_tag +{ + multiboot_uint32_t type; + multiboot_uint32_t size; +}; + +struct multiboot_tag_string +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + char string[0]; +}; + +struct multiboot_tag_module +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t mod_start; + multiboot_uint32_t mod_end; + char cmdline[0]; +}; + +struct multiboot_tag_basic_meminfo +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t mem_lower; + multiboot_uint32_t mem_upper; +}; + +struct multiboot_tag_bootdev +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t biosdev; + multiboot_uint32_t slice; + multiboot_uint32_t part; +}; + +struct multiboot_tag_mmap +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t entry_size; + multiboot_uint32_t entry_version; + struct multiboot_mmap_entry entries[0]; +}; + +struct multiboot_vbe_info_block +{ + multiboot_uint8_t external_specification[512]; +}; + +struct multiboot_vbe_mode_info_block +{ + multiboot_uint8_t external_specification[256]; +}; + +struct multiboot_tag_vbe +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + + multiboot_uint16_t vbe_mode; + multiboot_uint16_t vbe_interface_seg; + multiboot_uint16_t vbe_interface_off; + multiboot_uint16_t vbe_interface_len; + + struct multiboot_vbe_info_block vbe_control_info; + struct multiboot_vbe_mode_info_block vbe_mode_info; +}; + +struct multiboot_tag_framebuffer_common +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + + multiboot_uint64_t framebuffer_addr; + multiboot_uint32_t framebuffer_pitch; + multiboot_uint32_t framebuffer_width; + multiboot_uint32_t framebuffer_height; + multiboot_uint8_t framebuffer_bpp; +#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0 *** 144 LINES SKIPPED ***