[PATCH V5 4/6] Add EFI stub for ARM
Grant Likely
grant.likely at linaro.org
Thu Dec 5 15:00:54 EST 2013
On Wed, 27 Nov 2013 15:31:53 -0800, Roy Franz <roy.franz at linaro.org> wrote:
> This patch adds EFI stub support for the ARM Linux kernel. The EFI stub
> operates similarly to the x86 stub: it is a shim between the EFI firmware
> and the normal zImage entry point, and sets up the environment that the
> zImage is expecting. This includes loading the initrd (optionaly) and
> device tree from the system partition based on the kernel command line.
> The stub updates the device tree as necessary, adding entries for EFI
> runtime services. The PE/COFF "MZ" header at offset 0 results in the
> first instruction being an add that corrupts r5, which is not used by
> the zImage interface.
>
> Signed-off-by: Roy Franz <roy.franz at linaro.org>
I'm going to review this patch side-by-side with the ARM64 patch written
by Mark. There are things that can still be consolidated. Otherwise the
patch looks good.
> ---
> diff --git a/arch/arm/boot/compressed/efi-stub.c b/arch/arm/boot/compressed/efi-stub.c
> new file mode 100644
> index 0000000..2bd559d
> --- /dev/null
> +++ b/arch/arm/boot/compressed/efi-stub.c
> @@ -0,0 +1,291 @@
> +/*
> + * linux/arch/arm/boot/compressed/efi-stub.c
> + *
> + * Copyright (C) 2013 Linaro Ltd; <roy.franz at linaro.org>
> + *
> + * This file implements the EFI boot stub for the ARM kernel
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +#include <linux/efi.h>
> +#include <libfdt.h>
> +#include "efi-stub.h"
> +
> +/* EFI function call wrappers. These are not required for
> + * ARM, but wrappers are required for X86 to convert between
> + * ABIs. These wrappers are provided to allow code sharing
> + * between X86 and ARM. Since these wrappers directly invoke the
> + * EFI function pointer, the function pointer type must be properly
> + * defined, which is not the case for X86 One advantage of this is
> + * it allows for type checking of arguments, which is not
> + * possible with the X86 wrappers.
> + */
> +#define efi_call_phys0(f) f()
> +#define efi_call_phys1(f, a1) f(a1)
> +#define efi_call_phys2(f, a1, a2) f(a1, a2)
> +#define efi_call_phys3(f, a1, a2, a3) f(a1, a2, a3)
> +#define efi_call_phys4(f, a1, a2, a3, a4) f(a1, a2, a3, a4)
> +#define efi_call_phys5(f, a1, a2, a3, a4, a5) f(a1, a2, a3, a4, a5)
Identical to ARM64. I would put this into a common header usable by
platforms that can call the functions directly.
> +
> +/* The maximum uncompressed kernel size is 32 MBytes, so we will reserve
> + * that for the decompressed kernel. We have no easy way to tell what
> + * the actuall size of code + data the uncompressed kernel will use.
> + */
> +#define MAX_UNCOMP_KERNEL_SIZE 0x02000000
> +
> +/* The kernel zImage should be located between 32 Mbytes
> + * and 128 MBytes from the base of DRAM. The min
> + * address leaves space for a maximal size uncompressed image,
> + * and the max address is due to how the zImage decompressor
> + * picks a destination address.
> + */
> +#define ZIMAGE_OFFSET_LIMIT 0x08000000
> +#define MIN_ZIMAGE_OFFSET MAX_UNCOMP_KERNEL_SIZE
> +
> +#define PRINTK_PREFIX "EFIstub: "
> +
> +struct fdt_region {
> + u64 base;
> + u64 size;
> +};
Identical to ARM64; move to header.
> +
> +
> +/* Include shared EFI stub code, and required headers. */
> +#include "../../../../include/generated/compile.h"
> +#include "../../../../include/generated/utsrelease.h"
> +#include "../../../../drivers/firmware/efi/efi-stub-helper.c"
> +#include "../../../../drivers/firmware/efi/fdt.c"
> +
> +
> +int efi_entry(void *handle, efi_system_table_t *sys_table,
> + unsigned long *zimage_addr)
> +{
> + efi_loaded_image_t *image;
> + int status;
> + unsigned long nr_pages;
> + const struct fdt_region *region;
> +
> + void *fdt;
> + int err;
> + int node;
> + unsigned long zimage_size = 0;
> + unsigned long dram_base;
> + /* addr/point and size pairs for memory management*/
> + unsigned long initrd_addr;
> + unsigned long initrd_size = 0;
> + unsigned long fdt_addr;
> + unsigned long fdt_size = 0;
> + efi_physical_addr_t kernel_reserve_addr;
> + unsigned long kernel_reserve_size = 0;
> + char *cmdline_ptr;
> + int cmdline_size = 0;
> +
> + unsigned long map_size, desc_size;
> + u32 desc_ver;
> + unsigned long mmap_key;
> + efi_memory_desc_t *memory_map;
> +
> + unsigned long new_fdt_size;
> + unsigned long new_fdt_addr;
> +
> + efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
> +
> + /* Check if we were booted by the EFI firmware */
> + if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
> + goto fail;
> +
> + efi_printk(sys_table, PRINTK_PREFIX"Booting Linux using EFI stub.\n");
Gratuitously different between ARM and ARM64. efi_printk() vs. pr_efi()
and the string is different.
> + /* get the command line from EFI, using the LOADED_IMAGE protocol */
> + status = efi_call_phys3(sys_table->boottime->handle_protocol,
> + handle, &proto, (void *)&image);
> + if (status != EFI_SUCCESS) {
> + efi_printk(sys_table, PRINTK_PREFIX"ERROR: Failed to get handle for LOADED_IMAGE_PROTOCOL.\n");
> + goto fail;
> + }
> +
> + /* We are going to copy the command line into the device tree,
> + * so this memory just needs to not conflict with boot protocol
> + * requirements.
> + */
> + cmdline_ptr = efi_convert_cmdline_to_ascii(sys_table, image,
> + &cmdline_size);
> + if (!cmdline_ptr) {
> + efi_printk(sys_table, PRINTK_PREFIX"ERROR: Unable to allocate memory for command line.\n");
> + goto fail;
> + }
> +
> + /* We first load the device tree, as we need to get the base address of
> + * DRAM from the device tree. The zImage, device tree, and initrd
> + * have address restrictions that are relative to the base of DRAM.
> + */
> + status = handle_cmdline_files(sys_table, image, cmdline_ptr, "dtb=",
> + 0xffffffff, &fdt_addr, &fdt_size);
> + if (status != EFI_SUCCESS) {
> + efi_printk(sys_table, PRINTK_PREFIX"ERROR: Unable to load device tree blob.\n");
> + goto fail_free_cmdline;
> + }
> +
> + err = fdt_check_header((void *)fdt_addr);
> + if (err != 0) {
> + efi_printk(sys_table, PRINTK_PREFIX"ERROR: Device tree header not valid.\n");
> + goto fail_free_fdt;
> + }
> + if (fdt_totalsize((void *)fdt_addr) > fdt_size) {
> + efi_printk(sys_table, PRINTK_PREFIX"ERROR: Incomplete device tree.\n");
> + goto fail_free_fdt;
> +
> + }
All of the above is basically identical.
> + /* Look up the base of DRAM from the device tree. */
> + fdt = (void *)fdt_addr;
> + node = fdt_subnode_offset(fdt, 0, "memory");
> + region = fdt_getprop(fdt, node, "reg", NULL);
> + if (region) {
> + dram_base = fdt64_to_cpu(region->base);
> + } else {
> + /* There is no way to get amount or addresses of physical
> + * memory installed using EFI calls. If the device tree
> + * we read from disk doesn't have this, there is no way
> + * for us to construct this informaion.
> + */
> + efi_printk(sys_table, PRINTK_PREFIX"ERROR: No 'memory' node in device tree.\n");
> + goto fail_free_fdt;
> + }
ARM and ARM64 differ here. ARM64 uses get_dram_base(), this open codes
some stuff.
> +
> + /* Reserve memory for the uncompressed kernel image. This is
> + * all that prevents any future allocations from conflicting
> + * with the kernel. Since we can't tell from the compressed
> + * image how much DRAM the kernel actually uses (due to BSS
> + * size uncertainty) we allocate the maximum possible size.
> + */
> + kernel_reserve_addr = dram_base;
> + kernel_reserve_size = MAX_UNCOMP_KERNEL_SIZE;
> + nr_pages = round_up(kernel_reserve_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
> + status = efi_call_phys4(sys_table->boottime->allocate_pages,
> + EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
> + nr_pages, &kernel_reserve_addr);
> + if (status != EFI_SUCCESS) {
> + efi_printk(sys_table, PRINTK_PREFIX"ERROR: Unable to allocate memory for uncompressed kernel.\n");
> + goto fail_free_fdt;
> + }
This is ARM only since arm64 doesn't have a compressed version. It would
be possible to stub out.
> +
> + /* Relocate the zImage, if required. ARM doesn't have a
> + * preferred address, so we set it to 0, as we want to allocate
> + * as low in memory as possible.
> + */
> + zimage_size = image->image_size;
> + status = efi_relocate_kernel(sys_table, zimage_addr, zimage_size,
> + zimage_size, 0, 0);
> + if (status != EFI_SUCCESS) {
> + efi_printk(sys_table, PRINTK_PREFIX"ERROR: Failed to relocate kernel.\n");
> + goto fail_free_kernel_reserve;
> + }
Slight differences here.
> +
> + /* Check to see if we were able to allocate memory low enough
> + * in memory.
> + */
> + if (*zimage_addr + zimage_size > dram_base + ZIMAGE_OFFSET_LIMIT) {
> + efi_printk(sys_table, PRINTK_PREFIX"ERROR: Failed to relocate kernel, no low memory available.\n");
> + goto fail_free_zimage;
> + }
Back to same code below this point, and is the same through to the
bottom of the function. That is a lot of identical code. I think you can
generalize the whole function with arch specific callouts in a couple of
places.
I really don't want to hold up the merging of this series. Aside from
the duplication I think it is ready to be merged. However, the
duplication is so obvious that I'm not comfortable with it. If I were
an ARM or ARM64 core maintainer then I would push back on it.
Go ahead an add my Acked-by for this patch. I'll support merging it as
is provided that you promise me to merge the two versions with a
follow-up patch ASAP. However, if you can I would still recommend
respinning the series with the common bits split out right away since
there's a much greater chance of getting the arm and arm64 maintainers
to accept them.
Acked-by: Grant Likely <grant.likely at linaro.org>
g.
More information about the linux-arm-kernel
mailing list