Skip to content

Commit

Permalink
Merge branch 'arm64-efi-for-linus' of git://git.kernel.org/pub/scm/li…
Browse files Browse the repository at this point in the history
…nux/kernel/git/tip/tip into next

Pull ARM64 EFI update from Peter Anvin:
 "By agreement with the ARM64 EFI maintainers, we have agreed to make
  -tip the upstream for all EFI patches.  That is why this patchset
  comes from me :)

  This patchset enables EFI stub support for ARM64, like we already have
  on x86"

* 'arm64-efi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  arm64: efi: only attempt efi map setup if booting via EFI
  efi/arm64: ignore dtb= when UEFI SecureBoot is enabled
  doc: arm64: add description of EFI stub support
  arm64: efi: add EFI stub
  doc: arm: add UEFI support documentation
  arm64: add EFI runtime services
  efi: Add shared FDT related functions for ARM/ARM64
  arm64: Add function to create identity mappings
  efi: add helper function to get UEFI params from FDT
  doc: efi-stub.txt updates for ARM
  lib: add fdt_empty_tree.c
  • Loading branch information
Linus Torvalds committed Jun 5, 2014
2 parents 046f153 + 74bcc24 commit c3c55a0
Show file tree
Hide file tree
Showing 21 changed files with 1,619 additions and 26 deletions.
2 changes: 2 additions & 0 deletions Documentation/arm/00-INDEX
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,7 @@ swp_emulation
- SWP/SWPB emulation handler/logging description
tcm.txt
- ARM Tightly Coupled Memory
uefi.txt
- [U]EFI configuration and runtime services documentation
vlocks.txt
- Voting locks, low-level mechanism relying on memory system atomic writes.
64 changes: 64 additions & 0 deletions Documentation/arm/uefi.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
UEFI, the Unified Extensible Firmware Interface, is a specification
governing the behaviours of compatible firmware interfaces. It is
maintained by the UEFI Forum - http://www.uefi.org/.

UEFI is an evolution of its predecessor 'EFI', so the terms EFI and
UEFI are used somewhat interchangeably in this document and associated
source code. As a rule, anything new uses 'UEFI', whereas 'EFI' refers
to legacy code or specifications.

UEFI support in Linux
=====================
Booting on a platform with firmware compliant with the UEFI specification
makes it possible for the kernel to support additional features:
- UEFI Runtime Services
- Retrieving various configuration information through the standardised
interface of UEFI configuration tables. (ACPI, SMBIOS, ...)

For actually enabling [U]EFI support, enable:
- CONFIG_EFI=y
- CONFIG_EFI_VARS=y or m

The implementation depends on receiving information about the UEFI environment
in a Flattened Device Tree (FDT) - so is only available with CONFIG_OF.

UEFI stub
=========
The "stub" is a feature that extends the Image/zImage into a valid UEFI
PE/COFF executable, including a loader application that makes it possible to
load the kernel directly from the UEFI shell, boot menu, or one of the
lightweight bootloaders like Gummiboot or rEFInd.

The kernel image built with stub support remains a valid kernel image for
booting in non-UEFI environments.

UEFI kernel support on ARM
==========================
UEFI kernel support on the ARM architectures (arm and arm64) is only available
when boot is performed through the stub.

When booting in UEFI mode, the stub deletes any memory nodes from a provided DT.
Instead, the kernel reads the UEFI memory map.

The stub populates the FDT /chosen node with (and the kernel scans for) the
following parameters:
________________________________________________________________________________
Name | Size | Description
================================================================================
linux,uefi-system-table | 64-bit | Physical address of the UEFI System Table.
--------------------------------------------------------------------------------
linux,uefi-mmap-start | 64-bit | Physical address of the UEFI memory map,
| | populated by the UEFI GetMemoryMap() call.
--------------------------------------------------------------------------------
linux,uefi-mmap-size | 32-bit | Size in bytes of the UEFI memory map
| | pointed to in previous entry.
--------------------------------------------------------------------------------
linux,uefi-mmap-desc-size | 32-bit | Size in bytes of each entry in the UEFI
| | memory map.
--------------------------------------------------------------------------------
linux,uefi-mmap-desc-ver | 32-bit | Version of the mmap descriptor format.
--------------------------------------------------------------------------------
linux,uefi-stub-kern-ver | string | Copy of linux_banner from build.
--------------------------------------------------------------------------------

For verbose debug messages, specify 'uefi_debug' on the kernel command line.
4 changes: 4 additions & 0 deletions Documentation/arm64/booting.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ The decompressed kernel image contains a 64-byte header as follows:
Header notes:

- code0/code1 are responsible for branching to stext.
- when booting through EFI, code0/code1 are initially skipped.
res5 is an offset to the PE header and the PE header has the EFI
entry point (efi_stub_entry). When the stub has done its work, it
jumps to code0 to resume the normal boot process.

The image must be placed at the specified offset (currently 0x80000)
from the start of the system RAM and called there. The start of the
Expand Down
33 changes: 26 additions & 7 deletions Documentation/efi-stub.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
The EFI Boot Stub
---------------------------

On the x86 platform, a bzImage can masquerade as a PE/COFF image,
thereby convincing EFI firmware loaders to load it as an EFI
executable. The code that modifies the bzImage header, along with the
EFI-specific entry point that the firmware loader jumps to are
collectively known as the "EFI boot stub", and live in
On the x86 and ARM platforms, a kernel zImage/bzImage can masquerade
as a PE/COFF image, thereby convincing EFI firmware loaders to load
it as an EFI executable. The code that modifies the bzImage header,
along with the EFI-specific entry point that the firmware loader
jumps to are collectively known as the "EFI boot stub", and live in
arch/x86/boot/header.S and arch/x86/boot/compressed/eboot.c,
respectively.
respectively. For ARM the EFI stub is implemented in
arch/arm/boot/compressed/efi-header.S and
arch/arm/boot/compressed/efi-stub.c. EFI stub code that is shared
between architectures is in drivers/firmware/efi/efi-stub-helper.c.

For arm64, there is no compressed kernel support, so the Image itself
masquerades as a PE/COFF image and the EFI stub is linked into the
kernel. The arm64 EFI stub lives in arch/arm64/kernel/efi-entry.S
and arch/arm64/kernel/efi-stub.c.

By using the EFI boot stub it's possible to boot a Linux kernel
without the use of a conventional EFI boot loader, such as grub or
Expand All @@ -23,7 +31,10 @@ The bzImage located in arch/x86/boot/bzImage must be copied to the EFI
System Partition (ESP) and renamed with the extension ".efi". Without
the extension the EFI firmware loader will refuse to execute it. It's
not possible to execute bzImage.efi from the usual Linux file systems
because EFI firmware doesn't have support for them.
because EFI firmware doesn't have support for them. For ARM the
arch/arm/boot/zImage should be copied to the system partition, and it
may not need to be renamed. Similarly for arm64, arch/arm64/boot/Image
should be copied but not necessarily renamed.


**** Passing kernel parameters from the EFI shell
Expand Down Expand Up @@ -63,3 +74,11 @@ Notice how bzImage.efi can be specified with a relative path. That's
because the image we're executing is interpreted by the EFI shell,
which understands relative paths, whereas the rest of the command line
is passed to bzImage.efi.


**** The "dtb=" option

For the ARM and arm64 architectures, we also need to be able to provide a
device tree to the kernel. This is done with the "dtb=" command line option,
and is processed in the same manner as the "initrd=" option that is
described above.
16 changes: 16 additions & 0 deletions arch/arm64/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,20 @@ config CMDLINE_FORCE
This is useful if you cannot or don't want to change the
command-line options your boot loader passes to the kernel.

config EFI
bool "UEFI runtime support"
depends on OF && !CPU_BIG_ENDIAN
select LIBFDT
select UCS2_STRING
select EFI_PARAMS_FROM_FDT
default y
help
This option provides support for runtime services provided
by UEFI firmware (such as non-volatile variables, realtime
clock, and platform reset). A UEFI stub is also provided to
allow the kernel to be booted as an EFI application. This
is only useful on systems that have UEFI firmware.

endmenu

menu "Userspace binary formats"
Expand Down Expand Up @@ -334,6 +348,8 @@ source "net/Kconfig"

source "drivers/Kconfig"

source "drivers/firmware/Kconfig"

source "fs/Kconfig"

source "arch/arm64/kvm/Kconfig"
Expand Down
14 changes: 14 additions & 0 deletions arch/arm64/include/asm/efi.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#ifndef _ASM_EFI_H
#define _ASM_EFI_H

#include <asm/io.h>

#ifdef CONFIG_EFI
extern void efi_init(void);
extern void efi_idmap_init(void);
#else
#define efi_init()
#define efi_idmap_init()
#endif

#endif /* _ASM_EFI_H */
2 changes: 2 additions & 0 deletions arch/arm64/include/asm/mmu.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,7 @@ extern void paging_init(void);
extern void setup_mm_for_reboot(void);
extern void __iomem *early_io_map(phys_addr_t phys, unsigned long virt);
extern void init_mem_pgprot(void);
/* create an identity mapping for memory (or io if map_io is true) */
extern void create_id_mapping(phys_addr_t addr, phys_addr_t size, int map_io);

#endif
3 changes: 3 additions & 0 deletions arch/arm64/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

CPPFLAGS_vmlinux.lds := -DTEXT_OFFSET=$(TEXT_OFFSET)
AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
CFLAGS_efi-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) \
-I$(src)/../../../scripts/dtc/libfdt

# Object file lists.
arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \
Expand All @@ -21,6 +23,7 @@ arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND) += sleep.o suspend.o
arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o
arm64-obj-$(CONFIG_KGDB) += kgdb.o
arm64-obj-$(CONFIG_EFI) += efi.o efi-stub.o efi-entry.o

obj-y += $(arm64-obj-y) vdso/
obj-m += $(arm64-obj-m)
Expand Down
109 changes: 109 additions & 0 deletions arch/arm64/kernel/efi-entry.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* EFI entry point.
*
* Copyright (C) 2013, 2014 Red Hat, Inc.
* Author: Mark Salter <msalter@redhat.com>
*
* 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/linkage.h>
#include <linux/init.h>

#include <asm/assembler.h>

#define EFI_LOAD_ERROR 0x8000000000000001

__INIT

/*
* We arrive here from the EFI boot manager with:
*
* * CPU in little-endian mode
* * MMU on with identity-mapped RAM
* * Icache and Dcache on
*
* We will most likely be running from some place other than where
* we want to be. The kernel image wants to be placed at TEXT_OFFSET
* from start of RAM.
*/
ENTRY(efi_stub_entry)
/*
* Create a stack frame to save FP/LR with extra space
* for image_addr variable passed to efi_entry().
*/
stp x29, x30, [sp, #-32]!

/*
* Call efi_entry to do the real work.
* x0 and x1 are already set up by firmware. Current runtime
* address of image is calculated and passed via *image_addr.
*
* unsigned long efi_entry(void *handle,
* efi_system_table_t *sys_table,
* unsigned long *image_addr) ;
*/
adrp x8, _text
add x8, x8, #:lo12:_text
add x2, sp, 16
str x8, [x2]
bl efi_entry
cmn x0, #1
b.eq efi_load_fail

/*
* efi_entry() will have relocated the kernel image if necessary
* and we return here with device tree address in x0 and the kernel
* entry point stored at *image_addr. Save those values in registers
* which are callee preserved.
*/
mov x20, x0 // DTB address
ldr x0, [sp, #16] // relocated _text address
mov x21, x0

/*
* Flush dcache covering current runtime addresses
* of kernel text/data. Then flush all of icache.
*/
adrp x1, _text
add x1, x1, #:lo12:_text
adrp x2, _edata
add x2, x2, #:lo12:_edata
sub x1, x2, x1

bl __flush_dcache_area
ic ialluis

/* Turn off Dcache and MMU */
mrs x0, CurrentEL
cmp x0, #PSR_MODE_EL2t
ccmp x0, #PSR_MODE_EL2h, #0x4, ne
b.ne 1f
mrs x0, sctlr_el2
bic x0, x0, #1 << 0 // clear SCTLR.M
bic x0, x0, #1 << 2 // clear SCTLR.C
msr sctlr_el2, x0
isb
b 2f
1:
mrs x0, sctlr_el1
bic x0, x0, #1 << 0 // clear SCTLR.M
bic x0, x0, #1 << 2 // clear SCTLR.C
msr sctlr_el1, x0
isb
2:
/* Jump to kernel entry point */
mov x0, x20
mov x1, xzr
mov x2, xzr
mov x3, xzr
br x21

efi_load_fail:
mov x0, #EFI_LOAD_ERROR
ldp x29, x30, [sp], #32
ret

ENDPROC(efi_stub_entry)
81 changes: 81 additions & 0 deletions arch/arm64/kernel/efi-stub.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright (C) 2013, 2014 Linaro Ltd; <roy.franz@linaro.org>
*
* This file implements the EFI boot stub for the arm64 kernel.
* Adapted from ARM version by Mark Salter <msalter@redhat.com>
*
* 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 <linux/libfdt.h>
#include <asm/sections.h>
#include <generated/compile.h>
#include <generated/utsrelease.h>

/*
* AArch64 requires the DTB to be 8-byte aligned in the first 512MiB from
* start of kernel and may not cross a 2MiB boundary. We set alignment to
* 2MiB so we know it won't cross a 2MiB boundary.
*/
#define EFI_FDT_ALIGN SZ_2M /* used by allocate_new_fdt_and_exit_boot() */
#define MAX_FDT_OFFSET SZ_512M

#define efi_call_early(f, ...) sys_table_arg->boottime->f(__VA_ARGS__)

static void efi_char16_printk(efi_system_table_t *sys_table_arg,
efi_char16_t *str);

static efi_status_t efi_open_volume(efi_system_table_t *sys_table,
void *__image, void **__fh);
static efi_status_t efi_file_close(void *handle);

static efi_status_t
efi_file_read(void *handle, unsigned long *size, void *addr);

static efi_status_t
efi_file_size(efi_system_table_t *sys_table, void *__fh,
efi_char16_t *filename_16, void **handle, u64 *file_sz);

/* Include shared EFI stub code */
#include "../../../drivers/firmware/efi/efi-stub-helper.c"
#include "../../../drivers/firmware/efi/fdt.c"
#include "../../../drivers/firmware/efi/arm-stub.c"


static efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
unsigned long *image_addr,
unsigned long *image_size,
unsigned long *reserve_addr,
unsigned long *reserve_size,
unsigned long dram_base,
efi_loaded_image_t *image)
{
efi_status_t status;
unsigned long kernel_size, kernel_memsize = 0;

/* Relocate the image, if required. */
kernel_size = _edata - _text;
if (*image_addr != (dram_base + TEXT_OFFSET)) {
kernel_memsize = kernel_size + (_end - _edata);
status = efi_relocate_kernel(sys_table, image_addr,
kernel_size, kernel_memsize,
dram_base + TEXT_OFFSET,
PAGE_SIZE);
if (status != EFI_SUCCESS) {
pr_efi_err(sys_table, "Failed to relocate kernel\n");
return status;
}
if (*image_addr != (dram_base + TEXT_OFFSET)) {
pr_efi_err(sys_table, "Failed to alloc kernel memory\n");
efi_free(sys_table, kernel_memsize, *image_addr);
return EFI_ERROR;
}
*image_size = kernel_memsize;
}


return EFI_SUCCESS;
}
Loading

0 comments on commit c3c55a0

Please sign in to comment.