[Merge] ~xypron/grub:riscv into ~ubuntu-core-dev/grub/+git/ubuntu:ubuntu
Julian Andres Klode
mp+415323 at code.launchpad.net
Wed Feb 9 16:21:10 UTC 2022
Review: Needs Fixing
Aside from the diff comments, we also concluded that we do not want to sign the grub binaries on riscv64, so the changes to debian/rules need to be reworked so that it is built unsigned as part of src:grub2 and not built in src:grub2-unsigned (which would submit it for signing).
As a guideline, the grub-efi-arm code can be used likely.
Diff comments:
> diff --git a/debian/control b/debian/control
> index 1819b2e..f96fb8b 100644
> --- a/debian/control
> +++ b/debian/control
> @@ -522,10 +522,66 @@ Description: GRand Unified Bootloader, version 2 (ARM64 UEFI version)
>
> Package: grub-efi-arm64-signed-template
> Architecture: arm64
> -Description: GRand Unified Bootloader, version 2 (ARM64 UEFI signing template)
> +Description: GRand Unified Bootloader, version 2 (arm64 UEFI signing template)
Case change is unwarranted, it is ARM after all. I'd rather change riscv64 to something nicer but there's no nice short name. RISC-V64 seems weird.
> This package contains template files for grub-efi-arm64-signed.
> This is only needed for Secure Boot signing.
>
> +Package: grub-efi-riscv64-bin
> +Architecture: any-riscv64
> +Depends: ${shlibs:Depends}, ${misc:Depends}, grub-common (>= 2.02~beta2-9)
> +Recommends: grub-efi-riscv64-signed [riscv64], efibootmgr [linux-any]
> +Multi-Arch: foreign
> +XB-Efi-Vendor: ${efi:Vendor}
> +Description: GRand Unified Bootloader, version 2 (riscv64 UEFI modules)
> + GRUB is a portable, powerful bootloader. This version of GRUB is based on a
> + cleaner design than its predecessors, and provides the following new features:
> + .
> + - Scripting in grub.cfg using BASH-like syntax.
> + - Support for modern partition maps such as GPT.
> + - Modular generation of grub.cfg via update-grub. Packages providing GRUB
> + add-ons can plug in their own script rules and trigger updates by invoking
> + update-grub.
> + .
> + This package contains GRUB modules that have been built for use on riscv64
> + systems with UEFI. It can be installed in parallel with other flavours,
> + but will not automatically install GRUB as the active boot loader nor
> + automatically update grub.cfg on upgrade unless grub-efi-riscv64 is also
> + installed.
> +
> +Package: grub-efi-riscv64-dbg
> +Section: debug
> +Architecture: any-riscv64
> +Depends: ${misc:Depends}, grub-efi-riscv64-bin (= ${binary:Version})
> +Multi-Arch: foreign
> +Description: GRand Unified Bootloader, version 2 (riscv64 UEFI debug files)
> + This package contains debugging files for grub-efi-riscv64-bin. You only
> + need these if you are trying to debug GRUB using its GDB stub.
> +
> +Package: grub-efi-riscv64
> +Architecture: any-riscv64
> +Pre-Depends: ${misc:Pre-Depends}
> +Depends: ${shlibs:Depends}, ${misc:Depends}, grub2-common (>= 2.02~beta2-9), grub-efi-riscv64-bin (= ${binary:Version}), ucf
> +Multi-Arch: foreign
> +Description: GRand Unified Bootloader, version 2 (riscv64 UEFI version)
> + GRUB is a portable, powerful bootloader. This version of GRUB is based on a
> + cleaner design than its predecessors, and provides the following new features:
> + .
> + - Scripting in grub.cfg using BASH-like syntax.
> + - Support for modern partition maps such as GPT.
> + - Modular generation of grub.cfg via update-grub. Packages providing GRUB
> + add-ons can plug in their own script rules and trigger updates by invoking
> + update-grub.
> + .
> + This is a dependency package for a version of GRUB that has been built for
> + use on RISC-V 64-bit systems with UEFI. Installing this package indicates that
> + this version of GRUB should be the active boot loader.
> +
> +Package: grub-efi-riscv64-signed-template
We should skip the signed template stuff for now if feasible as we do for grub-efi-arm
> +Architecture: riscv64
> +Description: GRand Unified Bootloader, version 2 (RISC-V 64-bit UEFI signing template)
> + This package contains template files for grub-efi-riscv64-signed.
> + This is only needed for Secure Boot signing.
> +
> Package: grub-ieee1275-bin
> Architecture: any-i386 any-amd64 any-powerpc any-ppc64 any-ppc64el any-sparc any-sparc64
> Depends: ${shlibs:Depends}, ${misc:Depends}, grub-common (= ${binary:Version})
> diff --git a/debian/patches/efi-implement-grub_efi_run_image.patch b/debian/patches/efi-implement-grub_efi_run_image.patch
> new file mode 100644
> index 0000000..fb0f0f5
> --- /dev/null
> +++ b/debian/patches/efi-implement-grub_efi_run_image.patch
> @@ -0,0 +1,880 @@
> +From: Heinrich Schuchardt <heinrich.schuchardt at canonical.com>
> +Date: Tue, 18 Jan 2022 11:55:56 +0100
> +Subject: efi: implement grub_efi_run_image
> +
> +Provide function grub_efi_run_image() which consumes a PE-COFF image
> +loaded into memory. The function
> +
> +* checks validity of header
> +* copies the sections
> +* relocates the code
> +* invalidates the instruction cache
> +* executes the image
> +* returns to caller
> +
> +Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt at canonical.com>
> +---
> + grub-core/Makefile.core.def | 4 +
> + grub-core/loader/efi/linux.c | 4 +-
> + grub-core/loader/efi/peimage.c | 765 +++++++++++++++++++++++++++++++++++++++++
> + include/grub/efi/linux.h | 3 +
> + include/grub/efi/peimage.h | 28 ++
> + 5 files changed, 803 insertions(+), 1 deletion(-)
> + create mode 100644 grub-core/loader/efi/peimage.c
> + create mode 100644 include/grub/efi/peimage.h
> +
> +diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
> +index 3b06462..2cfea95 100644
> +--- a/grub-core/Makefile.core.def
> ++++ b/grub-core/Makefile.core.def
> +@@ -1822,13 +1822,17 @@ module = {
> + arm_coreboot = loader/arm/linux.c;
> + arm_efi = loader/efi/linux.c;
> + arm_efi = loader/efi/linux_sb.c;
> ++ arm_efi = loader/efi/peimage.c;
> + arm_uboot = loader/arm/linux.c;
> + arm64 = loader/efi/linux.c;
> + arm64 = loader/efi/linux_sb.c;
> ++ arm64 = loader/efi/peimage.c;
> + riscv32 = loader/efi/linux.c;
> + riscv32 = loader/efi/linux_sb.c;
> ++ riscv32 = loader/efi/peimage.c;
> + riscv64 = loader/efi/linux.c;
> + riscv64 = loader/efi/linux_sb.c;
> ++ riscv64 = loader/efi/peimage.c;
> + cflags = '-Wno-error=cast-align';
> + common = loader/linux.c;
> + common = lib/cmdline.c;
> +diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c
> +index 56d6745..700073d 100644
> +--- a/grub-core/loader/efi/linux.c
> ++++ b/grub-core/loader/efi/linux.c
> +@@ -190,7 +190,9 @@ grub_arch_efi_linux_boot_image (grub_addr_t addr __attribute__((unused)),
> +
> + grub_dprintf ("linux", "linux command line: '%s'\n", args);
> +
> +- retval = grub_efi_linux_boot (kernel_addr, handover_offset, kernel_addr);
Per discussion, if we want to speed things up avoiding possible security regressions, we should change this to call the new code only in an #ifdef for risc-v
This will keep arm64 somewhat broken; but then we can upload first and uncouple security review from the riscv work.
> ++ //retval = grub_efi_linux_boot (kernel_addr, handover_offset, kernel_addr);
> ++
> ++ retval = grub_efi_run_image (kernel_addr, kernel_size, linux_args);
> +
> + /* When successful, not reached */
> + free_params();
> +diff --git a/grub-core/loader/efi/peimage.c b/grub-core/loader/efi/peimage.c
> +new file mode 100644
> +index 0000000..f9735ab
> +--- /dev/null
> ++++ b/grub-core/loader/efi/peimage.c
> +@@ -0,0 +1,765 @@
> ++// SPDX-License-Identifier: GPL-3.0+
> ++
> ++#include <grub/cache.h>
> ++#include <grub/cpu/efi/memory.h>
> ++#include <grub/efi/efi.h>
> ++#include <grub/efi/linux.h>
> ++#include <grub/efi/pe32.h>
> ++#include <grub/efi/peimage.h>
> ++#include <grub/env.h>
> ++#include <grub/misc.h>
> ++#include <grub/mm.h>
> ++#include <grub/setjmp.h>
> ++
> ++struct image_info {
> ++ void *data;
> ++ grub_efi_uint32_t data_size;
> ++ grub_efi_uint16_t machine;
> ++ grub_efi_uint16_t num_sections;
> ++ struct grub_pe32_section_table *section;
> ++ struct grub_pe32_data_directory *reloc;
> ++ grub_uint64_t image_base;
> ++ grub_uint32_t section_alignment;
> ++ grub_uint32_t image_size;
> ++ void *alloc_addr;
> ++ grub_uint32_t alloc_pages;
> ++ void *image_addr;
> ++ grub_efi_entry_point entry_point;
> ++};
> ++
> ++static struct {
> ++ grub_jmp_buf jmp;
> ++ grub_efi_handle_t image_handle;
> ++ grub_efi_status_t exit_status;
> ++ grub_efi_status_t EFIAPI (*exit)(grub_efi_handle_t image_handle,
> ++ grub_efi_status_t exit_status,
> ++ grub_efi_uintn_t exit_data_size,
> ++ grub_efi_char16_t *exit_data);
> ++} started_image;
> ++
> ++static int
> ++debug_enabled(const char *condition)
> ++{
> ++ const char *debug;
> ++
> ++ debug = grub_env_get ("debug");
> ++ if (!debug || !grub_strword(debug, condition))
> ++ return 0;
> ++
> ++ return 1;
> ++}
> ++
> ++static void
> ++debug(const char *constraint)
> ++{
> ++ volatile grub_uint64_t x;
> ++
> ++ if (!debug_enabled(constraint))
> ++ return;
> ++
> ++ grub_printf("Attach debugger\n");
> ++ for (x = 1; x; ++x)
> ++ ;
> ++}
> ++
> ++__attribute__((unused)) static void
> ++dump(const char *text, void *addr, unsigned int size)
> ++{
> ++ char *pos = addr;
> ++
> ++ grub_printf("%s @ 0x%lx\n", text, (unsigned long)addr);
> ++ for (unsigned int i = 0; i < size; i += 16)
> ++ grub_printf("%04x: %02x%02x %02x%02x %02x%02x %02x%02x "
> ++ "%02x%02x %02x%02x %02x%02x %02x%02x\n", i,
> ++ pos[i + 0], pos[i + 1], pos[i + 2], pos[i + 3],
> ++ pos[i + 4], pos[i + 5], pos[i + 6], pos[i + 7],
> ++ pos[i + 8], pos[i + 9], pos[i + 10], pos[i + 11],
> ++ pos[i + 12], pos[i + 13], pos[i + 14], pos[i + 15]);
> ++}
> ++
> ++static grub_uint16_t machines[] = {
> ++#if defined(__x86_64__)
> ++ GRUB_PE32_MACHINE_AMD64,
> ++#elif defined(__i386__) || defined(__i486__) || defined(__i686__)
> ++ GRUB_PE32_MACHINE_AMD64,
> ++ GRUB_PE32_MACHINE_I386,
> ++#elif defined(__aarch64__)
> ++ GRUB_PE32_MACHINE_ARM64,
> ++#elif defined(__arm__)
> ++ GRUB_PE32_MACHINE_ARMTHUMB_MIXED,
> ++#elif defined(__riscv) && __riscv_xlen == 32
> ++ GRUB_PE32_MACHINE_RISCV32,
> ++#elif defined(__riscv) && __riscv_xlen == 64
> ++ GRUB_PE32_MACHINE_RISCV64,
> ++#endif
> ++};
> ++
> ++/**
> ++ * check_machine_type() - check if the machine type matches the architecture
> ++ *
> ++ * @machine: the value of the Machine field of the COFF file header.
> ++ * Return: status code
> ++ */
> ++static grub_efi_status_t check_machine_type(grub_uint16_t machine)
> ++{
> ++ for (grub_size_t i = 0; i < sizeof(machines) / sizeof(*machines); ++i) {
> ++ if (machine == machines[i])
> ++ return GRUB_EFI_SUCCESS;
> ++ }
> ++
> ++ return GRUB_EFI_LOAD_ERROR;
> ++}
> ++
> ++/**
> ++ * check_pe_header() - check the headers of a PE-COFF image
> ++ *
> ++ * @info: information about the image
> ++ */
> ++static grub_efi_status_t check_pe_header(struct image_info *info)
> ++{
> ++ struct grub_dos_stub *dos_stub = info->data;
> ++ void *pe_magic;
> ++ struct grub_pe32_coff_header *coff_header;
> ++ struct grub_pe32_optional_header *pe32_header;
> ++ struct grub_pe64_optional_header *pe64_header;
> ++ grub_uint32_t header_size;
> ++
> ++ if (info->data_size < sizeof(struct grub_dos_stub)) {
> ++ grub_error(GRUB_ERR_BAD_OS, "truncated image");
> ++ return GRUB_EFI_LOAD_ERROR;
> ++ }
> ++ if (dos_stub->magic != GRUB_PE32_MAGIC) {
> ++ grub_error(GRUB_ERR_BAD_OS, "not a PE-COFF file");
> ++ return GRUB_EFI_UNSUPPORTED;
> ++ }
> ++ if (info->data_size < dos_stub->pe_addr + sizeof(GRUB_PE_MAGIC) +
> ++ sizeof(struct grub_pe32_coff_header) +
> ++ sizeof(struct grub_pe64_optional_header)) {
> ++ grub_error(GRUB_ERR_BAD_OS, "truncated image");
> ++ return GRUB_EFI_LOAD_ERROR;
> ++ }
> ++ pe_magic = (void *)((unsigned long)info->data + dos_stub->pe_addr);
> ++ if (grub_memcmp(pe_magic, GRUB_PE_MAGIC, sizeof(GRUB_PE_MAGIC))) {
> ++ grub_error(GRUB_ERR_BAD_OS, "not a PE-COFF file");
> ++ return GRUB_EFI_LOAD_ERROR;
> ++ }
> ++
> ++ coff_header = (void *)((unsigned long)pe_magic + sizeof(GRUB_PE_MAGIC));
> ++ info->machine = coff_header->machine;
> ++ info->num_sections = coff_header->num_sections;
> ++
> ++ if (check_machine_type(info->machine) != GRUB_EFI_SUCCESS) {
> ++ grub_error(GRUB_ERR_BAD_OS, "wrong machine type %u",
> ++ coff_header->machine);
> ++ return GRUB_EFI_LOAD_ERROR;
> ++ }
> ++
> ++ pe32_header = (void *)((unsigned long)coff_header +
> ++ sizeof(*coff_header));
> ++ pe64_header = (void *)((unsigned long)coff_header +
> ++ sizeof(*coff_header));
> ++
> ++ switch (pe32_header->magic) {
> ++ case GRUB_PE32_PE32_MAGIC:
> ++ info->section_alignment = pe32_header->section_alignment;
> ++ info->image_base = pe32_header->image_base;
> ++ info->image_size = pe32_header->image_size;
> ++ info->entry_point =
> ++ (void *)(unsigned long)pe32_header->entry_addr;
> ++ header_size = pe32_header->header_size;
> ++ if (info->data_size < header_size) {
> ++ grub_error(GRUB_ERR_BAD_OS, "truncated image");
> ++ return GRUB_EFI_LOAD_ERROR;
> ++ }
> ++
> ++ if (pe32_header->num_data_directories >= 6 &&
> ++ pe32_header->base_relocation_table.size)
> ++ info->reloc = &pe32_header->base_relocation_table;
> ++
> ++ info->section = (void *)((unsigned long)&pe32_header->export_table +
> ++ pe32_header->num_data_directories *
> ++ sizeof(struct grub_pe32_data_directory));
> ++ break;
> ++ case GRUB_PE32_PE64_MAGIC:
> ++ info->section_alignment = pe64_header->section_alignment;
> ++ info->image_base = pe64_header->image_base;
> ++ info->image_size = pe64_header->image_size;
> ++ info->entry_point =
> ++ (void *)(unsigned long)pe64_header->entry_addr;
> ++ header_size = pe64_header->header_size;
> ++ if (info->data_size < header_size) {
> ++ grub_error(GRUB_ERR_BAD_OS, "truncated image");
> ++ return GRUB_EFI_LOAD_ERROR;
> ++ }
> ++
> ++ if (pe64_header->num_data_directories >= 6 &&
> ++ pe64_header->base_relocation_table.size)
> ++ info->reloc = &pe64_header->base_relocation_table;
> ++
> ++ info->section = (void *)((unsigned long)&pe64_header->export_table +
> ++ pe64_header->num_data_directories *
> ++ sizeof(struct grub_pe32_data_directory));
> ++ break;
> ++ default:
> ++ grub_error(GRUB_ERR_BAD_OS, "not a PE-COFF file");
> ++ return GRUB_EFI_LOAD_ERROR;
> ++ }
> ++
> ++ if ((unsigned long)info->section +
> ++ info->num_sections * sizeof(*info->section) >
> ++ (unsigned long)info->data + info->data_size) {
> ++ grub_error(GRUB_ERR_BAD_OS, "truncated image");
> ++ return GRUB_EFI_LOAD_ERROR;
> ++ }
> ++
> ++ grub_dprintf("linux", "PE-COFF header checked\n");
> ++
> ++ return GRUB_EFI_SUCCESS;
> ++}
> ++
> ++/**
> ++ * load_sections() - load image sections into memory
> ++ *
> ++ * Allocate fresh memory and copy the image sections there.
> ++ *
> ++ * @info: image information
> ++ */
> ++static grub_efi_status_t load_sections(struct image_info *info)
> ++{
> ++ struct grub_pe32_section_table *section;
> ++ unsigned long align_mask = 0xfff;
> ++
> ++ /* Section alignment must be a power of two */
> ++ if (info->section_alignment & (info->section_alignment - 1)) {
> ++ grub_error(GRUB_ERR_BAD_OS, "invalid section alignment");
> ++ return GRUB_EFI_LOAD_ERROR;
> ++ }
> ++
> ++ if (info->section_alignment > align_mask)
> ++ align_mask = info->section_alignment - 1;
> ++
> ++ info->alloc_pages = GRUB_EFI_BYTES_TO_PAGES(info->image_size +
> ++ (align_mask & ~0xfffUL));
> ++
> ++ info->alloc_addr = grub_efi_allocate_pages_real(
> ++ GRUB_EFI_MAX_USABLE_ADDRESS, info->alloc_pages,
> ++ GRUB_EFI_ALLOCATE_MAX_ADDRESS, GRUB_EFI_LOADER_DATA);
> ++ if (!info->alloc_addr)
> ++ return GRUB_EFI_OUT_OF_RESOURCES;
> ++
> ++ info->image_addr =
> ++ (void *)(((unsigned long)info->alloc_addr + align_mask) &
> ++ ~align_mask);
> ++
> ++ for (section = &info->section[0];
> ++ section < &info->section[info->num_sections]; ++section) {
> ++ if (section->raw_data_offset + section->raw_data_size >
> ++ info->data_size) {
> ++ grub_error(GRUB_ERR_BAD_OS, "truncated image");
> ++ return GRUB_EFI_LOAD_ERROR;
> ++ }
> ++ if (section->virtual_address + section->virtual_size >
> ++ info->image_size) {
> ++ grub_error(GRUB_ERR_BAD_OS, "section outside image");
> ++ return GRUB_EFI_LOAD_ERROR;
> ++ }
> ++
> ++ grub_memset(
> ++ (void *)((unsigned long)info->image_addr +
> ++ section->virtual_address), 0,
> ++ section->virtual_size);
> ++ grub_memcpy(
> ++ (void *)((unsigned long)info->image_addr +
> ++ section->virtual_address),
> ++ (void *)((unsigned long)info->data +
> ++ section->raw_data_offset),
> ++ section->raw_data_size);
> ++ }
> ++
> ++ info->entry_point = (void *)((unsigned long)info->entry_point +
> ++ (unsigned long)info->image_addr);
> ++
> ++ grub_dprintf("linux", "sections loaded\n");
> ++
> ++ return GRUB_EFI_SUCCESS;
> ++}
> ++
> ++/**
> ++ * lo12i_get() - get the immediate value of a format I instruction
> ++ *
> ++ * Instruction format I::
> ++ *
> ++ * +---------------------------------------------------------------+
> ++ * |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0|
> ++ * +-----------------------+---------+-----+---------+-------------+
> ++ * | imm[11:0] | rs1 |fun3 | rd | opcode |
> ++ * +-----------------------+---------+-----+---------+-------------+
> ++ *
> ++ * @instr: pointer to instruction
> ++ * Return: immediate value
> ++ */
> ++static grub_uint16_t
> ++lo12i_get(grub_uint32_t *instr)
> ++{
> ++ return ((*instr & 0xfff00000) >> 20);
> ++}
> ++
> ++/**
> ++ * lo12i_set() - set the immediate value of a format I instruction
> ++ *
> ++ * Instruction format I::
> ++ *
> ++ * +---------------------------------------------------------------+
> ++ * |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0|
> ++ * +-----------------------+---------+-----+---------+-------------+
> ++ * | imm[11:0] | rs1 |fun3 | rd | opcode |
> ++ * +-----------------------+---------+-----+---------+-------------+
> ++ *
> ++ * @instr: pointer to instruction
> ++ * @imm: immediate value
> ++ */
> ++static void
> ++lo12i_set(grub_uint32_t *instr, grub_uint32_t imm)
> ++{
> ++ *instr = (*instr & 0x000fffff) |
> ++ (imm & 0x00000fff << 20);
> ++}
> ++
> ++/**
> ++ * hi20_get() - get the immediate value of a format I instruction
> ++ *
> ++ * Instruction format U::
> ++ *
> ++ * +---------------------------------------------------------------+
> ++ * |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0|
> ++ * +---------------------------------------+---------+-------------+
> ++ * | imm[31:12] | rd | opcode |
> ++ * +---------------------------------------+---------+-------------+
> ++ *
> ++ * @instr: pointer to instruction
> ++ * Return: immediate value
> ++ */
> ++static grub_uint16_t
> ++hi20_get(grub_uint32_t *instr)
> ++{
> ++ return *instr & 0xfffff000;
> ++}
> ++
> ++/**
> ++ * hi20_set() - set the immediate value of a format I instruction
> ++ *
> ++ * Instruction format U::
> ++ *
> ++ * +---------------------------------------------------------------+
> ++ * |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0|
> ++ * +---------------------------------------+---------+-------------+
> ++ * | imm[31:12] | rd | opcode |
> ++ * +---------------------------------------+---------+-------------+
> ++ *
> ++ * @instr: pointer to instruction
> ++ * @imm: immediate value
> ++ */
> ++static void
> ++hi20_set(grub_uint32_t *instr, grub_uint32_t imm)
> ++{
> ++ *instr = (*instr & 0x00000fff) |
> ++ (imm & 0xfffff000);
> ++}
> ++
> ++/**
> ++ * lo12s_get() - get the immediate value of a format I instruction
> ++ *
> ++ * Instruction format S::
> ++ *
> ++ * +---------------------------------------------------------------+
> ++ * |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0|
> ++ * +-------------+---------+---------+-----+----+----+-------------+
> ++ * | imm[11:5] | rs2 | rs1 |fun3 |imm[4:0] | opcode |
> ++ * +-------------+---------+---------+-----+----+----+-------------+
> ++ *
> ++ * @instr: pointer to instruction
> ++ * Return: immediate value
> ++ */
> ++static grub_uint16_t
> ++lo12s_get(grub_uint32_t *instr)
> ++{
> ++ return ((*instr & 0x00000f80) >> 7) |
> ++ ((*instr & 0xfe000000) >> 20);
> ++}
> ++
> ++/**
> ++ * lo12s_set() - set the immediate value of a format I instruction
> ++ *
> ++ * Instruction format S::
> ++ *
> ++ * +---------------------------------------------------------------+
> ++ * |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0|
> ++ * +-------------+---------+---------+-----+----+----+-------------+
> ++ * | imm[11:5] | rs2 | rs1 |fun3 |imm[4:0] | opcode |
> ++ * +-------------+---------+---------+-----+----+----+-------------+
> ++ *
> ++ * @instr: pointer to instruction
> ++ * @imm: immediate value
> ++ */
> ++static void
> ++lo12s_set(grub_uint32_t *instr, grub_uint32_t imm)
> ++{
> ++ *instr = (*instr & 0x01fff07f) |
> ++ (imm & 0x00000fe0 << 20) |
> ++ (imm & 0x0000001f << 7);
> ++}
> ++
> ++/**
> ++ * movw_get_imm() - get the immediate value of MOVT and MOVT instructions
> ++ *
> ++ * MOVT::
> ++ *
> ++ * +-------------------------------+-------------------------------+
> ++ * |f e d c b a 9 8 7 6 5 4 3 2 1 0|f e d c b a 9 8 7 6 5 4 3 2 1 0|
> ++ * +---------+-+-----------+-------+-+-----+-------+---------------+
> ++ * |1 1 1 1 0|i|1 0 1 1 0 0| imm4 |0| imm3| Rd | imm8 |
> ++ * +---------+-+-----------+-------+-+-----+-------+---------------+
> ++ *
> ++ * MOVW::
> ++ *
> ++ * +-------------------------------+-------------------------------+
> ++ * |f e d c b a 9 8 7 6 5 4 3 2 1 0|f e d c b a 9 8 7 6 5 4 3 2 1 0|
> ++ * +---------+-+-----------+-------+-+-----+-------+---------------+
> ++ * |1 1 1 1 0|i|1 0 0 1 0 0| imm4 |0| imm3| Rd | imm8 |
> ++ * +---------+-+-----------+-------+-+-----+-------+---------------+
> ++ *
> ++ * @instr: pointer to instruction
> ++ * Return: immediate value
> ++ */
> ++static grub_uint16_t
> ++movw_get_imm(grub_uint16_t *instr)
> ++{
> ++ /* imm16 = imm4:i:imm3:imm8; */
> ++ return (instr[1] & 0x00ff) |
> ++ ((instr[1] & 0x7000) >> 3) |
> ++ ((instr[0] & 0x0400) >> 8) |
> ++ ((instr[0] & 0x000f) << 12);
> ++}
> ++
> ++/**
> ++ * movw_set_imm() - set the immediate value of MOVT and MOVT instructions
> ++ *
> ++ * MOVT::
> ++ *
> ++ * +-------------------------------+-------------------------------+
> ++ * |f e d c b a 9 8 7 6 5 4 3 2 1 0|f e d c b a 9 8 7 6 5 4 3 2 1 0|
> ++ * +---------+-+-----------+-------+-+-----+-------+---------------+
> ++ * |1 1 1 1 0|i|1 0 1 1 0 0| imm4 |0| imm3| Rd | imm8 |
> ++ * +---------+-+-----------+-------+-+-----+-------+---------------+
> ++ *
> ++ * MOVW::
> ++ *
> ++ * +-------------------------------+-------------------------------+
> ++ * |f e d c b a 9 8 7 6 5 4 3 2 1 0|f e d c b a 9 8 7 6 5 4 3 2 1 0|
> ++ * +---------+-+-----------+-------+-+-----+-------+---------------+
> ++ * |1 1 1 1 0|i|1 0 0 1 0 0| imm4 |0| imm3| Rd | imm8 |
> ++ * +---------+-+-----------+-------+-+-----+-------+---------------+
> ++ *
> ++ * @instr: pointer to instruction
> ++ * @imm immediate value
> ++ */
> ++static void
> ++movw_set_imm(grub_uint16_t *instr, grub_uint16_t imm)
> ++{
> ++ /* imm16 = imm4:i:imm3:imm8; */
> ++ instr[0] = (instr[0] & 0xfbf0) |
> ++ (imm & 0xf000) >> 12 |
> ++ (imm & 0x0800) << 3;
> ++ instr[1] = (instr[0] & 0x8f00) |
> ++ (imm & 0xff) |
> ++ (imm & 0x0700) >> 4;
> ++}
> ++
> ++/**
> ++ * relocate() - apply relocations to the image
> ++ *
> ++ * @info: information about the loaded image
> ++ */
> ++static grub_efi_status_t
> ++relocate(struct image_info *info)
> ++{
> ++ struct grub_pe32_fixup_block *block, *reloc_end;
> ++ unsigned long offset;
> ++ grub_uint16_t reloc_type;
> ++ grub_uint16_t *reloc_entry;
> ++ grub_uint32_t *rvhi20_addr = NULL;
> ++
> ++ if (!info->reloc || !(info->reloc->size)) {
> ++ grub_dprintf("linux", "no relocations\n");
> ++ return GRUB_EFI_SUCCESS;
> ++ }
> ++
> ++ if (info->reloc->rva + info->reloc->size > info->image_size) {
> ++ grub_error(GRUB_ERR_BAD_OS, "relocation block outside image");
> ++ return GRUB_EFI_LOAD_ERROR;
> ++ }
> ++
> ++ /*
> ++ * The relocations are based on the difference between
> ++ * actual load address and the preferred base address.
> ++ */
> ++ offset = (unsigned long)info->image_addr - info->image_base;
> ++
> ++ block = (void *)((unsigned long)info->image_addr + info->reloc->rva);
> ++ reloc_end = (void *)((unsigned long)block + info->reloc->size);
> ++
> ++ for (; block < reloc_end;
> ++ block = (void *)((unsigned long)block + block->block_size)) {
> ++ reloc_entry = block->entries;
> ++ grub_uint16_t *block_end =
> ++ (void *)((unsigned long)block + block->block_size);
> ++
> ++ for (; reloc_entry < block_end; ++reloc_entry) {
> ++ void *addr = (void *)((unsigned long)info->image_addr +
> ++ block->page_rva +
> ++ (*reloc_entry & 0xfff));
> ++
> ++ reloc_type = *reloc_entry >> 12;
> ++
> ++ switch (reloc_type) {
> ++ case GRUB_PE32_REL_BASED_ABSOLUTE:
> ++ /* skip */
> ++ break;
> ++ case GRUB_PE32_REL_BASED_HIGH:
> ++ *(grub_uint16_t *)addr += offset >> 16;
> ++ break;
> ++ case GRUB_PE32_REL_BASED_LOW:
> ++ *(grub_uint16_t *)addr += offset;
> ++ break;
> ++ case GRUB_PE32_REL_BASED_HIGHLOW:
> ++ *(grub_uint32_t *)addr += offset;
> ++ break;
> ++ case GRUB_PE32_REL_BASED_RISCV_HI20:
> ++ switch (info->machine) {
> ++ case GRUB_PE32_MACHINE_RISCV32:
> ++ case GRUB_PE32_MACHINE_RISCV64:
> ++ rvhi20_addr = addr;
> ++ break;
> ++ default:
> ++ goto bad_reloc;
> ++ }
> ++ break;
> ++ case GRUB_PE32_REL_BASED_ARM_MOV32T:
> ++ /* = GRUB_PE32_REL_BASED_RISCV_LOW12I */
> ++ switch (info->machine) {
> ++ case GRUB_PE32_MACHINE_ARMTHUMB_MIXED: {
> ++ grub_uint16_t *instr = addr;
> ++ grub_uint32_t val;
> ++
> ++ val = movw_get_imm(&instr[0]) +
> ++ (movw_get_imm(&instr[2]) << 16) +
> ++ offset;
> ++ movw_set_imm(&instr[0], val);
> ++ movw_set_imm(&instr[2], val >> 16);
> ++ break;
> ++ }
> ++ case GRUB_PE32_MACHINE_RISCV32:
> ++ case GRUB_PE32_MACHINE_RISCV64:
> ++ if (rvhi20_addr) {
> ++ grub_uint32_t val =
> ++ hi20_get(rvhi20_addr) +
> ++ lo12i_get(addr) +
> ++ offset;
> ++ hi20_set(rvhi20_addr, val);
> ++ lo12i_set(addr, val);
> ++ rvhi20_addr = NULL;
> ++ } else {
> ++ goto bad_reloc;
> ++ }
> ++ break;
> ++ default:
> ++ goto bad_reloc;
> ++ }
> ++ break;
> ++ case GRUB_PE32_REL_BASED_RISCV_LOW12S:
> ++ switch (info->machine) {
> ++ case GRUB_PE32_MACHINE_RISCV32:
> ++ case GRUB_PE32_MACHINE_RISCV64:
> ++ if (rvhi20_addr) {
> ++ grub_uint32_t val =
> ++ hi20_get(rvhi20_addr) +
> ++ lo12s_get(addr) +
> ++ offset;
> ++ hi20_set(rvhi20_addr, val);
> ++ lo12s_set(addr, val);
> ++ rvhi20_addr = NULL;
> ++ } else {
> ++ goto bad_reloc;
> ++ }
> ++ break;
> ++ default:
> ++ goto bad_reloc;
> ++ }
> ++ break;
> ++ case GRUB_PE32_REL_BASED_DIR64:
> ++ *(grub_uint64_t *)addr += offset;
> ++ break;
> ++ default:
> ++ goto bad_reloc;
> ++ }
> ++ }
> ++ }
> ++
> ++ grub_dprintf("linux", "image relocated\n");
> ++
> ++ return GRUB_EFI_SUCCESS;
> ++
> ++bad_reloc:
> ++ grub_error(GRUB_ERR_BAD_OS,
> ++ "unsupported relocation type %d, rva 0x%08lx\n",
> ++ *reloc_entry >> 12,
> ++ (unsigned long)reloc_entry -
> ++ (unsigned long)info->image_addr);
> ++ return GRUB_EFI_LOAD_ERROR;
> ++}
> ++
> ++/**
> ++ * efi_exit() - replacement for EFI_BOOT_SERVICES.Exit()
> ++ *
> ++ * This function is inserted into system table to trap invocations of
> ++ * EFI_BOOT_SERVICES.Exit(). If Exit() is called with our handle
> ++ * return to our start routine using a long jump.
> ++ *
> ++ * @image_handle: handle of the application as passed on entry
> ++ * @exit_status: the images exit code
> ++ * @exit_data_size: size of @exit_data
> ++ * @exit_data: null terminated string followed by optional data
> ++ */
> ++static grub_efi_status_t EFIAPI
> ++efi_exit(grub_efi_handle_t image_handle,
> ++ grub_efi_status_t exit_status,
> ++ grub_efi_uintn_t exit_data_size,
> ++ grub_efi_char16_t *exit_data)
> ++{
> ++ grub_efi_system_table->boot_services->exit = started_image.exit;
> ++
> ++ if (!image_handle)
> ++ return GRUB_EFI_INVALID_PARAMETER;
> ++
> ++ if (image_handle != started_image.image_handle) {
> ++ grub_dprintf("linux", "delegating Exit()\n");
> ++ return efi_call_4(started_image.exit, image_handle,
> ++ exit_status, exit_data_size,
> ++ (grub_efi_char16_t *)exit_data);
> ++ }
> ++
> ++ started_image.exit_status = exit_status;
> ++
> ++ if (exit_status != GRUB_EFI_SUCCESS) {
> ++ grub_printf("Application failed, r = %d\n",
> ++ (int)exit_status & 0x7fffffff);
> ++ if (exit_data_size && exit_data) {
> ++ grub_printf("exit message: ");
> ++ for (grub_efi_uintn_t pos = 0;
> ++ exit_data[pos] && pos < exit_data_size / 2; ++pos)
> ++ grub_printf("%C", exit_data[pos]);
> ++ grub_printf("\n");
> ++ }
> ++ }
> ++ if (exit_data_size && exit_data) {
> ++ /* exit data must be freed by the caller */
> ++ efi_call_1(grub_efi_system_table->boot_services->free_pool,
> ++ exit_data);
> ++ }
> ++ grub_longjmp(started_image.jmp, 1);
> ++}
> ++
> ++/**
> ++ * start_image() - our implementation of StartImage()
> ++ *
> ++ * As we do not load the image via LoadImage() we need our own implementation
> ++ * of StartImage() to launch the PE-COFF image.
> ++ */
> ++static grub_efi_status_t
> ++start_image(struct image_info *info)
> ++{
> ++ int ret;
> ++ grub_efi_status_t status;
> ++ grub_efi_loaded_image_t *loaded_image;
> ++
> ++ /*
> ++ * TODO: It would better comply to the UEFI specification to
> ++ * use a separate handle for the image we are running.
> ++ */
> ++ started_image.image_handle = grub_efi_image_handle;
> ++
> ++ loaded_image = grub_efi_get_loaded_image(grub_efi_image_handle);
> ++ if (loaded_image) {
> ++ loaded_image->image_base = info->image_addr;
> ++ loaded_image->image_size = info->image_size;
> ++ } else {
> ++ grub_dprintf ("linux", "Loaded image protocol missing\n");
> ++ }
> ++
> ++ ret = grub_setjmp(started_image.jmp);
> ++ if (ret) {
> ++ started_image.image_handle = NULL;
> ++
> ++ return started_image.exit_status;
> ++ }
> ++
> ++ started_image.exit = grub_efi_system_table->boot_services->exit;
> ++ grub_efi_system_table->boot_services->exit = efi_exit;
> ++
> ++ grub_dprintf("linux",
> ++ "Executing image loaded at 0x%lx\nEntry point 0x%lx\nSize 0x%08x\n",
> ++ (unsigned long)info->image_addr,
> ++ (unsigned long)info->entry_point,
> ++ info->image_size);
> ++
> ++ /* Invalidate the instruction cache */
> ++ grub_arch_sync_caches(info->image_addr, info->image_size);
> ++
> ++ debug("pestart");
> ++ status = efi_call_2(info->entry_point, started_image.image_handle,
> ++ grub_efi_system_table);
> ++
> ++ grub_dprintf("linux", "Application returned\n");
> ++
> ++ return efi_exit(started_image.image_handle, status, 0, NULL);
> ++}
> ++
> ++/**
> ++ * grub_efi_run_image() - run a PE-COFF image from memory
> ++ *
> ++ * TODO: move the creation of the load options here
> ++ *
> ++ * @data: buffer with PE-COFF image
> ++ * @data_size: size of the buffer
> ++ * @args: command line to be passed as load options
> ++ */
> ++grub_efi_boolean_t
> ++grub_efi_run_image(void *data, grub_efi_uint32_t data_size,
> ++ const char *args __attribute__((unused)))
> ++{
> ++ struct image_info info = {
> ++ .data = data,
> ++ .data_size = data_size,
> ++ };
> ++
> ++ /* read and check header */
> ++ if (check_pe_header(&info) != GRUB_EFI_SUCCESS)
> ++ goto err;
> ++ /* load sections */
> ++ if (load_sections(&info) != GRUB_EFI_SUCCESS)
> ++ goto err;
> ++
> ++ if (relocate(&info) != GRUB_EFI_SUCCESS)
> ++ goto err;
> ++
> ++ if (start_image(&info) != GRUB_EFI_SUCCESS)
> ++ goto err;
> ++
> ++err:
> ++ /* free allocated memory */
> ++ if (info.alloc_addr)
> ++ grub_efi_free_pages((unsigned long)info.alloc_addr,
> ++ info.alloc_pages);
> ++
> ++ return 0;
> ++}
> +diff --git a/include/grub/efi/linux.h b/include/grub/efi/linux.h
> +index 0033d93..39f5a9a 100644
> +--- a/include/grub/efi/linux.h
> ++++ b/include/grub/efi/linux.h
> +@@ -27,5 +27,8 @@ EXPORT_FUNC(grub_linuxefi_secure_validate) (void *data, grub_uint32_t size);
> + grub_err_t
> + EXPORT_FUNC(grub_efi_linux_boot) (void *kernel_address, grub_off_t offset,
> + void *kernel_param);
> ++grub_efi_boolean_t
> ++EXPORT_FUNC(grub_efi_run_image) (void *data, grub_efi_uint32_t data_size,
> ++ const char *args);
> +
> + #endif /* ! GRUB_EFI_LINUX_HEADER */
> +diff --git a/include/grub/efi/peimage.h b/include/grub/efi/peimage.h
> +new file mode 100644
> +index 0000000..70bfdc7
> +--- /dev/null
> ++++ b/include/grub/efi/peimage.h
> +@@ -0,0 +1,28 @@
> ++/* SPDX-License-Identifier: GPL-3.0+ */
> ++
> ++#define GRUB_PE_MAGIC "PE\0"
> ++
> ++#ifdef __x86_64__
> ++#define EFIAPI __attribute__((ms_abi))
> ++#else
> ++#define EFIAPI
> ++#endif
> ++
> ++#define EFI_LOADED_IMAGE_PROTOCOL_REVISION (0x1000)
> ++
> ++typedef grub_efi_status_t (*grub_efi_entry_point)
> ++ (grub_efi_handle_t image_handle, grub_efi_system_table_t *system_table);
> ++
> ++struct grub_dos_stub {
> ++ grub_uint16_t magic; /* MZ magic */
> ++ grub_uint16_t lbsize; /* size of last block */
> ++ grub_uint32_t res0; /* reserved */
> ++ grub_uint64_t res1; /* reserved */
> ++ grub_uint64_t res2; /* reserved */
> ++ grub_uint64_t res3; /* reserved */
> ++ grub_uint64_t res4; /* reserved */
> ++ grub_uint64_t res5; /* reserved */
> ++ grub_uint64_t res6; /* reserved */
> ++ grub_uint32_t linux_arch; /* linux architecture */
> ++ grub_uint32_t pe_addr; /* offset of PE/COFF header */
> ++};
> diff --git a/debian/rules b/debian/rules
> index 9505998..9b7548a 100755
> --- a/debian/rules
> +++ b/debian/rules
> @@ -192,7 +211,7 @@ endif
> else
> ifeq ($(SB_SUBMIT),yes)
> # on all other arches src:grub2-unsigned FTBFS
> -ONLY_BUILD := -pnone
> +ONLY_BUILD := -pgrub
This change is wrong
> endif
> endif
> endif
--
https://code.launchpad.net/~xypron/grub/+git/grub/+merge/415323
Your team Ubuntu Core Development Team is subscribed to branch ~ubuntu-core-dev/grub/+git/ubuntu:ubuntu.
More information about the Ubuntu-reviews
mailing list