diff --git a/xen/common/efi/boot.c b/xen/common/efi/boot.c index 5a520bf21d3a..fa922c63514d 100644 --- a/xen/common/efi/boot.c +++ b/xen/common/efi/boot.c @@ -102,6 +102,7 @@ union string { struct file { UINTN size; + bool need_to_free; union { EFI_PHYSICAL_ADDRESS addr; void *ptr; @@ -330,13 +331,13 @@ static void __init noreturn blexit(const CHAR16 *str) if ( !efi_bs ) efi_arch_halt(); - if ( cfg.addr ) + if ( cfg.addr && cfg.need_to_free) efi_bs->FreePages(cfg.addr, PFN_UP(cfg.size)); - if ( kernel.addr ) + if ( kernel.addr && kernel.need_to_free) efi_bs->FreePages(kernel.addr, PFN_UP(kernel.size)); - if ( ramdisk.addr ) + if ( ramdisk.addr && ramdisk.need_to_free) efi_bs->FreePages(ramdisk.addr, PFN_UP(ramdisk.size)); - if ( xsm.addr ) + if ( xsm.addr && xsm.need_to_free) efi_bs->FreePages(xsm.addr, PFN_UP(xsm.size)); efi_arch_blexit(); @@ -619,6 +620,7 @@ static bool __init read_file(EFI_FILE_HANDLE dir_handle, CHAR16 *name, what = what ?: L"Seek"; else { + file->need_to_free = true; file->addr = min(1UL << (32 + PAGE_SHIFT), HYPERVISOR_VIRT_END - DIRECTMAP_VIRT_START); ret = efi_bs->AllocatePages(AllocateMaxAddress, EfiLoaderData, @@ -665,6 +667,135 @@ static bool __init read_file(EFI_FILE_HANDLE dir_handle, CHAR16 *name, return true; } + +struct DosFileHeader { + UINT8 Magic[2]; + UINT16 LastSize; + UINT16 nBlocks; + UINT16 nReloc; + UINT16 HdrSize; + UINT16 MinAlloc; + UINT16 MaxAlloc; + UINT16 ss; + UINT16 sp; + UINT16 Checksum; + UINT16 ip; + UINT16 cs; + UINT16 RelocPos; + UINT16 nOverlay; + UINT16 reserved[4]; + UINT16 OEMId; + UINT16 OEMInfo; + UINT16 reserved2[10]; + UINT32 ExeHeader; +} __attribute__((packed)); + +#define PE_HEADER_MACHINE_I386 0x014c +#define PE_HEADER_MACHINE_X64 0x8664 +#define PE_HEADER_MACHINE_ARM64 0xaa64 + +struct PeFileHeader { + UINT16 Machine; + UINT16 NumberOfSections; + UINT32 TimeDateStamp; + UINT32 PointerToSymbolTable; + UINT32 NumberOfSymbols; + UINT16 SizeOfOptionalHeader; + UINT16 Characteristics; +} __attribute__((packed)); + +struct PeHeader { + UINT8 Magic[4]; + struct PeFileHeader FileHeader; +} __attribute__((packed)); + +struct PeSectionHeader { + UINT8 Name[8]; + UINT32 VirtualSize; + UINT32 VirtualAddress; + UINT32 SizeOfRawData; + UINT32 PointerToRawData; + UINT32 PointerToRelocations; + UINT32 PointerToLinenumbers; + UINT16 NumberOfRelocations; + UINT16 NumberOfLinenumbers; + UINT32 Characteristics; +} __attribute__((packed)); + +static void * __init pe_find_section(const void * const image_base, + const char * section_name, UINTN * size_out) +{ + const CHAR8 * const base = image_base; + const struct DosFileHeader * dos = (const void*) base; + const struct PeHeader * pe; + const UINTN name_len = strlen(section_name); + UINTN offset; + + if (base == NULL) + return NULL; + + if (memcmp(dos->Magic, "MZ", 2) != 0) + return NULL; + + pe = (const void *) &base[dos->ExeHeader]; + if (memcmp(pe->Magic, "PE\0\0", 4) != 0) + return NULL; + + /* PE32+ Subsystem type */ + if (pe->FileHeader.Machine != PE_HEADER_MACHINE_X64 + && pe->FileHeader.Machine != PE_HEADER_MACHINE_ARM64 + && pe->FileHeader.Machine != PE_HEADER_MACHINE_I386) + return NULL; + + if (pe->FileHeader.NumberOfSections > 96) + return NULL; + + offset = dos->ExeHeader + sizeof(*pe) + pe->FileHeader.SizeOfOptionalHeader; + + for (UINTN i = 0; i < pe->FileHeader.NumberOfSections; i++) + { + const struct PeSectionHeader *const sect = (const struct PeSectionHeader *)&base[offset]; + if (memcmp(sect->Name, section_name, name_len) == 0) + { + if (size_out) + *size_out = sect->VirtualSize; + return (void*)(sect->VirtualAddress + (uintptr_t) image_base); + } + + offset += sizeof(*sect); + } + + return NULL; +} + +static bool __init read_section(const void * const image_base, char * const name, + struct file *file, char *options) +{ + union string name_string = { .s = name + 1 }; + if ( !image_base ) + return false; + + file->ptr = pe_find_section(image_base, name, &file->size); + if ( !file->ptr ) + return false; + + file->need_to_free = false; + + if ( file == &cfg ) + return true; + + s2w(&name_string); + PrintStr(name_string.w); + PrintStr(L": "); + DisplayUint(file->addr, 2 * sizeof(file->addr)); + PrintStr(L"-"); + DisplayUint(file->addr + file->size, 2 * sizeof(file->addr)); + PrintStr(newline); + efi_arch_handle_module(file, name_string.w, options); + + return true; +} + static void __init pre_parse(const struct file *cfg) { char *ptr = cfg->ptr, *end = ptr + cfg->size; @@ -1143,6 +1274,7 @@ efi_start(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) static EFI_GUID __initdata loaded_image_guid = LOADED_IMAGE_PROTOCOL; static EFI_GUID __initdata shim_lock_guid = SHIM_LOCK_PROTOCOL_GUID; EFI_LOADED_IMAGE *loaded_image; + void * image_base = NULL; EFI_STATUS status; unsigned int i, argc; CHAR16 **argv, *file_name, *cfg_file_name = NULL, *options = NULL; @@ -1171,6 +1303,8 @@ efi_start(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) PrintErrMesg(L"No Loaded Image Protocol", status); efi_arch_load_addr_check(loaded_image); + if (loaded_image) + image_base = loaded_image->ImageBase; if ( use_cfg_file ) { @@ -1249,9 +1383,13 @@ efi_start(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) /* Get the file system interface. */ dir_handle = get_parent_handle(loaded_image, &file_name); - /* Read and parse the config file. */ - if ( !cfg_file_name ) + if ( read_section(image_base, ".config", &cfg, NULL) ) + { + PrintStr(L"Using unified config file\r\n"); + } + else if ( !cfg_file_name ) { + /* Read and parse the config file. */ CHAR16 *tail; while ( (tail = point_tail(file_name)) != NULL ) @@ -1269,6 +1407,7 @@ efi_start(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) } else if ( !read_file(dir_handle, cfg_file_name, &cfg, NULL) ) blexit(L"Configuration file not found."); + pre_parse(&cfg); if ( section.w ) @@ -1303,26 +1442,37 @@ efi_start(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) efi_arch_cfg_file_early(dir_handle, section.s); option_str = split_string(name.s); - read_file(dir_handle, s2w(&name), &kernel, option_str); - efi_bs->FreePool(name.w); - - if ( !EFI_ERROR(efi_bs->LocateProtocol(&shim_lock_guid, NULL, - (void **)&shim_lock)) && - (status = shim_lock->Verify(kernel.ptr, kernel.size)) != EFI_SUCCESS ) - PrintErrMesg(L"Dom0 kernel image could not be verified", status); - name.s = get_value(&cfg, section.s, "ramdisk"); - if ( name.s ) + if ( !read_section(image_base, ".kernel", &kernel, option_str) ) { - read_file(dir_handle, s2w(&name), &ramdisk, NULL); + read_file(dir_handle, s2w(&name), &kernel, option_str); efi_bs->FreePool(name.w); + + if ( !EFI_ERROR(efi_bs->LocateProtocol(&shim_lock_guid, NULL, + (void **)&shim_lock)) && + (status = shim_lock->Verify(kernel.ptr, kernel.size)) != EFI_SUCCESS ) + PrintErrMesg(L"Dom0 kernel image could not be verified", status); } - name.s = get_value(&cfg, section.s, "xsm"); - if ( name.s ) + if ( !read_section(image_base, ".ramdisk", &ramdisk, NULL) ) { - read_file(dir_handle, s2w(&name), &xsm, NULL); - efi_bs->FreePool(name.w); + name.s = get_value(&cfg, section.s, "ramdisk"); + if ( name.s ) + { + read_file(dir_handle, s2w(&name), &ramdisk, NULL); + efi_bs->FreePool(name.w); + } + } + + if ( !read_section(image_base, ".xsm", &xsm, NULL) ) + { + name.s = get_value(&cfg, section.s, "xsm"); + if ( name.s ) + { + if (!xsm.ptr) + read_file(dir_handle, s2w(&name), &xsm, NULL); + efi_bs->FreePool(name.w); + } } /* diff --git a/xen/scripts/unify-xen b/xen/scripts/unify-xen new file mode 100755 index 000000000000..b6072b1f59b7 --- /dev/null +++ b/xen/scripts/unify-xen @@ -0,0 +1,68 @@ +#!/bin/bash +# Merge a Linux kernel, initrd and commandline into xen.efi to produce a single signed +# EFI executable. +# +# turn off "expressions don't expand in single quotes" +# and "can't follow non-constant sources" +# shellcheck disable=SC2016 disable=SC1090 +set -e -o pipefail +export LC_ALL=C + +die() { echo "$@" >&2 ; exit 1 ; } +warn() { echo "$@" >&2 ; } +debug() { [ "$VERBOSE" == 1 ] && echo "$@" >&2 ; } + +cleanup() { + rm -rf "$TMP" +} + +TMP=$(mktemp -d) +TMP_MOUNT=n +trap cleanup EXIT + +######################################## + +# Usage +# unify xen.efi xen.cfg bzimage initrd +# Xen goes up to a pad at 00400000 + +XEN="$1" +CONFIG="$2" +KERNEL="$3" +RAMDISK="$4" +# --change-section-vma .config=0x0500000 \ +# --change-section-vma .kernel=0x0510000 \ +# --change-section-vma .ramdisk=0x3000000 \ + +objcopy \ + --add-section .kernel="$KERNEL" \ + --add-section .ramdisk="$RAMDISK" \ + --add-section .config="$CONFIG" \ + --change-section-vma .config=0xffff82d041000000 \ + --change-section-vma .kernel=0xffff82d041010000 \ + --change-section-vma .ramdisk=0xffff82d042000000 \ + "$XEN" \ + "$TMP/xen.efi" \ +|| die "$TMP/xen.efi: unable to create" + +KEY_ENGINE="" +KEY="/etc/safeboot/signing.key" +CERT="/etc/safeboot/cert.pem" + +for try in 1 2 3 ; do + warn "$TMP/xen.efi: Signing (ignore warnings about gaps)" + sbsign.safeboot \ + $KEY_ENGINE \ + --key "$KEY" \ + --cert "$CERT" \ + --output "xen.signed.efi" \ + "$TMP/xen.efi" \ + && break + + if [ "$try" == 3 ]; then + die "xen.signed.efi: failed after $try tries" + fi + + warn "$OUTDIR/linux.efi: signature failed! Try $try." +done +