[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