Skip to contentSkip to conversation

EFI payload / application support v3

PATCH Superseded Alexander Graf 5 participants 43 messages Feb 2, 2016 – Feb 26, 2016

Subsystems (2)

v1 of 5 Superseded Latest is v5 →

lore
Share & tools
lore
#0 lore raw

cover EFI payload / application support v3

This is my (now quite late) Christmas present for my openSUSE friends :).

U-Boot is a great project for embedded devices. However, convincing everyone involved that only for "a few oddball ARM devices" we need to support different configuration formats from grub2 when all other platforms (PPC, System Z, x86) are standardized on a single format is a nightmare.

So we started to explore alternatives. At first, people tried to get grub2 running using the u-boot api interface. However, FWIW that one doesn't support relocations, so you need to know where to link grub2 to at compile time. It also seems to be broken more often than not. And on top of it all, it's a one-off interface, so yet another thing to maintain.

That led to a nifty idea. What if we can just implement the EFI application protocol on top of U-Boot? Then we could compile a single grub2 binary for uEFI based systems and U-Boot based systems and as soon as that one's loaded, everything looks and feels (almost) the same.

This patch set is the result of pursuing this endeavor.

- I am successfully able to run grub2 and Linux EFI binaries with this code.
- When enabled, the resulting U-Boot binary only grows by ~10kb, so it's very light weight.
- It works on 32bit ARM and AArch64.
- All storage devices are directly accessible
- No EFI variables
- Removable media booting (search for /efi/boot/boota{a64,arm}.efi)
- Everything in place for runtime service support

Of course, there are still a few things one could do on top:

- Improve disk media detection (don't scan, use what information we have)
- Add EFI variable support using NVRAM
- Add GFX support
- Make EFI Shell work ;)
- Network device support
- Support for payload exit

But so far, I'm very happy with the state of the patches. They completely eliminate potential arguments against U-Boot internally and give users the chance to run with the same level of comfort on all firmware types.

Version 3 was successfully tested to boot grub2 and Linux from there on a HiKey (AArch64, dcache disabled) and on a BeagleBone Black.

If you read this far and want to try out the patches, feel free to grab the source from git at:

https://github.com/agraf/u-boot efi-v3

v1 -> v2:
- move memory allocation to separate patch
- limit 32/64 to hosts that support it
- check 32bit optional nt header magic
- switch to GPLv2+
- Fix typo s/does now/does not/
- Add #ifdefs around header to allow inclusion when efi_loader is disabled
- Add stub efi_restore_gd() function when efi_loader is disabled
- Disable debug
- Mark runtime region as such
- Fix up memory map
- Allow efi_restore_gd to be called before first efi entry
- Add 32bit arm cache workaround
- Move memory map to separate patch
- Change BTS version to 2.5
- Fix return values for a few callbacks to more EFI compliant ones
- Change vendor to "Das U-Boot"
- Add warning when truncating timer trigger
- Add runtime detach
- Enable runtime relocations
- Add get_time
- Fix relocation
- Fix 32bit
- Add am335x support
- Move section definition to header
- Add systab to runtime section
- Add self-relocation hook table
- Relocate efi_runtime section early during bootup
- Fix return values for a number of callbacks to be more UEFI compliant
- Move to block_drvr array
- Fix header order
- Document efi block object struct
- Use calloc rather than malloc & memset
- Default to y
- New patches:
- disk/part.c: Expose list of available block drivers
- arm64: Allow exceptions to return
- arm64: Allow EFI payload code to take exceptions
- efi_loader: Add DCACHE_OFF support for arm64
- efi_loader: Add distro boot script for removable media

v2 -> v3:

- Add EFIAPI to notify_function
- Add access denied code
- use efi_alloc
- add EFIAPI to function prototypes
- remove unused macros
- reorder header inclusion
- split relocation code into function
- flush cache after loading
- Use external efi_memory helpers
- Add EFIAPI to function prototypes
- Initialize event timer to -1ULL to prevent early firing
- Document header
- Move obj list to lib
- Remove implicit guid table
- Add guid compare function
- Fix return values
- Implement efi_wait_for_event
- Add EFIAPI to function prototypes
- Patch reset to NULL
- Add EFIAPI to function prototypes
- Document header
- Add dm.h include
- Remove non-dm rtc support
- Return DEVICE_ERROR in rtc path
- Adapt to newer u-boot block API
- Add EFIAPI to function prototypes
- Document header
- Check for DEV_TYPE_UNKNOWN
- Document 16byte limit for dp string
- Move to new cmd directory
- Add kconfig option
- Fix comment style
- Add help text
- s/-1/-ENOENT
- Move obj list to lib
- Rewrite memory allocation and map
- Document header
- Add memory file
- New files:
- README patch
- MAINTAINERS patch

Alex

Alexander Graf (16):
disk/part.c: Expose list of available block drivers include/efi_api.h: Add more detailed API definitions
efi_loader: Add PE image loader
efi_loader: Add boot time services
efi_loader: Add console interface
efi_loader: Add runtime services
efi_loader: Add disk interfaces
efi_loader: Add "bootefi" command
efi_loader: Implement memory allocation and map
arm64: Allow exceptions to return
arm64: Allow EFI payload code to take exceptions
efi_loader: Add DCACHE_OFF support for arm64
efi_loader: hook up in build environment
efi_loader: Add distro boot script for removable media
efi_loader: Add README section in README.efi
efi_loader: Add MAINTAINERS entry

 MAINTAINERS                       |   7 +
 arch/arm/config.mk                |   4 +
 arch/arm/cpu/armv8/exceptions.S   |  44 +++
 arch/arm/cpu/armv8/u-boot.lds     |  16 +
 arch/arm/cpu/u-boot.lds           |  30 ++
 arch/arm/lib/Makefile             |   3 +
 arch/arm/lib/interrupts_64.c      |  28 ++
 arch/arm/lib/sections.c           |   4 +
 arch/arm/lib/unaligned_64.c       | 284 ++++++++++++++
 board/ti/am335x/u-boot.lds        |  30 ++
 cmd/Kconfig                       |   7 +
 cmd/Makefile                      |   1 +
 cmd/bootefi.c                     | 172 +++++++++
 common/board_r.c                  |   7 +
 disk/part.c                       |   7 +-
 doc/README.efi                    |  69 +++-
 include/config_distro_bootcmd.h   |  47 ++-
 include/efi.h                     |   1 +
 include/efi_api.h                 | 198 ++++++++--
 include/efi_loader.h              | 147 +++++++
 include/part.h                    |   8 +
 include/pe.h                      | 263 +++++++++++++
 lib/Kconfig                       |   1 +
 lib/Makefile                      |   1 +
 lib/efi_loader/Kconfig            |   9 +
 lib/efi_loader/Makefile           |  12 +
 lib/efi_loader/efi_boottime.c     | 781 ++++++++++++++++++++++++++++++++++++++
 lib/efi_loader/efi_console.c      | 360 ++++++++++++++++++
 lib/efi_loader/efi_disk.c         | 218 +++++++++++
 lib/efi_loader/efi_image_loader.c | 182 +++++++++
 lib/efi_loader/efi_memory.c       | 314 +++++++++++++++
 lib/efi_loader/efi_runtime.c      | 290 ++++++++++++++
 32 files changed, 3500 insertions(+), 45 deletions(-)
 create mode 100644 arch/arm/lib/unaligned_64.c
 create mode 100644 cmd/bootefi.c
 create mode 100644 include/efi_loader.h
 create mode 100644 include/pe.h
 create mode 100644 lib/efi_loader/Kconfig
 create mode 100644 lib/efi_loader/Makefile
 create mode 100644 lib/efi_loader/efi_boottime.c
 create mode 100644 lib/efi_loader/efi_console.c
 create mode 100644 lib/efi_loader/efi_disk.c
 create mode 100644 lib/efi_loader/efi_image_loader.c
 create mode 100644 lib/efi_loader/efi_memory.c
 create mode 100644 lib/efi_loader/efi_runtime.c
Signature · 1 line
2.6.2
#1 lore raw

PATCH 1/16 disk/part.c: Expose list of available block drivers

We have a pretty nice and generic interface to ask for a specific block device. However, that one is still based around the magic notion that we know the driver name.

In order to be able to write fully generic disk access code, expose the currently internal list to other source files so that they can scan through all available block drivers.

---
 disk/part.c    | 7 +------
 include/part.h | 8 ++++++++
 2 files changed, 9 insertions(+), 6 deletions(-)
disk/part.c +1 -6
index 1935b28..d265c2b 100644
@@ -20,13 +20,8 @@
 #define PRINTF(fmt,args...)
 #endif
 
-struct block_drvr {
-	char *name;
-	block_dev_desc_t* (*get_dev)(int dev);
-	int (*select_hwpart)(int dev_num, int hwpart);
-};
 
-static const struct block_drvr block_drvr[] = {
+const struct block_drvr block_drvr[] = {
 #if defined(CONFIG_CMD_IDE)
 	{ .name = "ide", .get_dev = ide_get_dev, },
 #endif
include/part.h +8 -0
index dc23949..6e6205b 100644
@@ -42,6 +42,12 @@ struct block_dev_desc {
 	void		*priv;		/* driver private struct pointer */
 };
 
+struct block_drvr {
+	char *name;
+	block_dev_desc_t* (*get_dev)(int dev);
+	int (*select_hwpart)(int dev_num, int hwpart);
+};
+
 #define BLOCK_CNT(size, block_dev_desc) (PAD_COUNT(size, block_dev_desc->blksz))
 #define PAD_TO_BLOCKSIZE(size, block_dev_desc) \
 	(PAD_SIZE(size, block_dev_desc->blksz))
@@ -123,6 +129,8 @@ int get_device(const char *ifname, const char *dev_str,
 int get_device_and_partition(const char *ifname, const char *dev_part_str,
 			     block_dev_desc_t **dev_desc,
 			     disk_partition_t *info, int allow_whole_dev);
+
+extern const struct block_drvr block_drvr[];
 #else
 static inline block_dev_desc_t *get_dev(const char *ifname, int dev)
 { return NULL; }
Signature · 1 line
2.6.2
#2 lore raw

PATCH 1/16 disk/part.c: Expose list of available block drivers

Quoted 8 lines · Alexander Graf

We have a pretty nice and generic interface to ask for a specific block device. However, that one is still based around the magic notion that we know the driver name.

In order to be able to write fully generic disk access code, expose the currently internal list to other source files so that they can scan through all available block drivers.

Quoted 5 lines
---
 disk/part.c    | 7 +------
 include/part.h | 8 ++++++++
 2 files changed, 9 insertions(+), 6 deletions(-)

Tested on Beaglebone Black and Minnowboard MAX:

#3 lore raw

PATCH 2/16 include/efi_api.h: Add more detailed API definitions

In reply to Alexander Graf

The EFI API header is great, but missing a good chunk of function prototype, GUID defines and enum declarations.

This patch extends it to cover more of the EFI API. It's still not 100% complete, but sufficient enough for our EFI payload interface.

---

v2 -> v3:

- Add EFIAPI to notify_function
- Add access denied code

---
 include/efi.h     |   1 +
 include/efi_api.h | 198 ++++++++++++++++++++++++++++++++++++++++++++----------
 2 files changed, 162 insertions(+), 37 deletions(-)
include/efi.h +1 -0
index fcafda0..cce7020 100644
@@ -38,6 +38,7 @@ struct efi_device_path;
 #define EFI_WRITE_PROTECTED	(8 | (1UL << (BITS_PER_LONG - 1)))
 #define EFI_OUT_OF_RESOURCES	(9 | (1UL << (BITS_PER_LONG - 1)))
 #define EFI_NOT_FOUND		(14 | (1UL << (BITS_PER_LONG - 1)))
+#define EFI_ACCESS_DENIED	(15 | (1UL << (BITS_PER_LONG - 1)))
 #define EFI_SECURITY_VIOLATION	(26 | (1UL << (BITS_PER_LONG - 1)))
 
 typedef unsigned long efi_status_t;
include/efi_api.h +161 -37

305 lines not on this page · raw message

Signature · 1 line
2.6.2
#4 lore raw

PATCH 2/16 include/efi_api.h: Add more detailed API definitions

Quoted 10 lines · Alexander Graf

The EFI API header is great, but missing a good chunk of function prototype, GUID defines and enum declarations.

This patch extends it to cover more of the EFI API. It's still not 100% complete, but sufficient enough for our EFI payload interface.

---

Quoted 8 lines

v2 -> v3:

- Add EFIAPI to notify_function
- Add access denied code

---
 include/efi.h     |   1 +
 include/efi_api.h | 198 ++++++++++++++++++++++++++++++++++++++++++++----------
 2 files changed, 162 insertions(+), 37 deletions(-)

Tested on Beaglebone Black and Minnowboard MAX:

#5 lore raw

PATCH 3/16 efi_loader: Add PE image loader

In reply to Alexander Graf

EFI uses the PE binary format for its application images. Add support to EFI PE binaries as well as all necessary bits for the "EFI image loader" interfaces.

---

v1 -> v2:

- move memory allocation to separate patch
- limit 32/64 to hosts that support it
- check 32bit optional nt header magic
- switch to GPL2+

v2 -> v3:

- use efi_alloc
- add EFIAPI to function prototypes
- remove unused macros
- reorder header inclusion
- split relocation code into function
- flush cache after loading

---
 include/efi_loader.h              |  20 +++
 include/pe.h                      | 263 ++++++++++++++++++++++++++++++++++++++
 lib/efi_loader/efi_image_loader.c | 182 ++++++++++++++++++++++++++
 3 files changed, 465 insertions(+)
 create mode 100644 include/efi_loader.h
 create mode 100644 include/pe.h
 create mode 100644 lib/efi_loader/efi_image_loader.c
include/efi_loader.h new file +20 -0
new file mode 100644
index 0000000..5618185
@@ -0,0 +1,20 @@
+/*
+ *  EFI application loader
+ *
+ *  Copyright (c) 2016 Alexander Graf
+ *
+ *  SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <part_efi.h>
+#include <efi_api.h>
+#include <linux/list.h>
+
+extern const efi_guid_t efi_guid_device_path;
+extern const efi_guid_t efi_guid_loaded_image;
+
+efi_status_t efi_return_handle(void *handle,
+		efi_guid_t *protocol, void **protocol_interface,
+		void *agent_handle, void *controller_handle,
+		uint32_t attributes);
+void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info);
include/pe.h new file +263 -0

263 lines not on this page · raw message

lib/efi_loader/efi_image_loader.c new file +182 -0

182 lines not on this page · raw message

Signature · 1 line
2.6.2
#6 lore raw

PATCH 3/16 efi_loader: Add PE image loader

Quoted 109 lines · Alexander Graf

EFI uses the PE binary format for its application images. Add support to EFI PE binaries as well as all necessary bits for the "EFI image loader" interfaces.

---

v1 -> v2:

- move memory allocation to separate patch
- limit 32/64 to hosts that support it
- check 32bit optional nt header magic
- switch to GPL2+

v2 -> v3:

- use efi_alloc
- add EFIAPI to function prototypes
- remove unused macros
- reorder header inclusion
- split relocation code into function
- flush cache after loading

---
 include/efi_loader.h              |  20 +++
 include/pe.h                      | 263 ++++++++++++++++++++++++++++++++++++++
 lib/efi_loader/efi_image_loader.c | 182 ++++++++++++++++++++++++++
 3 files changed, 465 insertions(+)
 create mode 100644 include/efi_loader.h
 create mode 100644 include/pe.h
 create mode 100644 lib/efi_loader/efi_image_loader.c
include/efi_loader.h new file +20 -0
new file mode 100644
index 0000000..5618185
@@ -0,0 +1,20 @@
+/*
+ *  EFI application loader
+ *
+ *  Copyright (c) 2016 Alexander Graf
+ *
+ *  SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <part_efi.h>
+#include <efi_api.h>
+#include <linux/list.h>
+
+extern const efi_guid_t efi_guid_device_path;
+extern const efi_guid_t efi_guid_loaded_image;
+
+efi_status_t efi_return_handle(void *handle,
+		efi_guid_t *protocol, void **protocol_interface,
+		void *agent_handle, void *controller_handle,
+		uint32_t attributes);
+void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info);
include/pe.h new file +46 -0
new file mode 100644
index 0000000..6379ae1
@@ -0,0 +1,263 @@
+/*
+ *  Portable Executable binary format structures
+ *
+ *  Copyright (c) 2016 Alexander Graf
+ *
+ *  Based on wine code
+ *
+ *  SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#ifndef _PE_H
+#define _PE_H
+
+typedef struct _IMAGE_DOS_HEADER {
+	uint16_t e_magic;      /* 00: MZ Header signature */
+	uint16_t e_cblp;       /* 02: Bytes on last page of file */
+	uint16_t e_cp;         /* 04: Pages in file */
+	uint16_t e_crlc;       /* 06: Relocations */
+	uint16_t e_cparhdr;    /* 08: Size of header in paragraphs */
+	uint16_t e_minalloc;   /* 0a: Minimum extra paragraphs needed */
+	uint16_t e_maxalloc;   /* 0c: Maximum extra paragraphs needed */
+	uint16_t e_ss;         /* 0e: Initial (relative) SS value */
+	uint16_t e_sp;         /* 10: Initial SP value */
+	uint16_t e_csum;       /* 12: Checksum */
+	uint16_t e_ip;         /* 14: Initial IP value */
+	uint16_t e_cs;         /* 16: Initial (relative) CS value */
+	uint16_t e_lfarlc;     /* 18: File address of relocation table */
+	uint16_t e_ovno;       /* 1a: Overlay number */
+	uint16_t e_res[4];     /* 1c: Reserved words */
+	uint16_t e_oemid;      /* 24: OEM identifier (for e_oeminfo) */
+	uint16_t e_oeminfo;    /* 26: OEM information; e_oemid specific */
+	uint16_t e_res2[10];   /* 28: Reserved words */
+	uint32_t e_lfanew;     /* 3c: Offset to extended header */
+} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
+
+#define IMAGE_DOS_SIGNATURE		0x5A4D     /* MZ   */
+#define IMAGE_NT_SIGNATURE		0x00004550 /* PE00 */
+
+#define IMAGE_FILE_MACHINE_ARM		0x01c0
+#define IMAGE_FILE_MACHINE_THUMB	0x01c2
+#define IMAGE_FILE_MACHINE_ARMNT	0x01c4
+#define IMAGE_FILE_MACHINE_AMD64	0x8664
+#define IMAGE_FILE_MACHINE_ARM64	0xaa64
+#define IMAGE_NT_OPTIONAL_HDR32_MAGIC	0x10b
+#define IMAGE_NT_OPTIONAL_HDR64_MAGIC	0x20b
+#define IMAGE_SUBSYSTEM_EFI_APPLICATION	10

include/pe.h line 56

Quoted 10 lines
include/pe.h new file +10 -0
+
+typedef struct _IMAGE_FILE_HEADER {
+	uint16_t  Machine;
+	uint16_t  NumberOfSections;
+	uint32_t TimeDateStamp;
+	uint32_t PointerToSymbolTable;
+	uint32_t NumberOfSymbols;
+	uint16_t  SizeOfOptionalHeader;
+	uint16_t  Characteristics;
+} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

There is something fishy going on with tabs vs. spaces in this file, for variable name alignment. Above is one example, several more are below. No further comments on functionality.

Quoted 398 lines
include/pe.h new file +207 -0
+
+typedef struct _IMAGE_DATA_DIRECTORY {
+	uint32_t VirtualAddress;
+	uint32_t Size;
+} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
+
+#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
+
+typedef struct _IMAGE_OPTIONAL_HEADER64 {
+	uint16_t  Magic; /* 0x20b */
+	uint8_t MajorLinkerVersion;
+	uint8_t MinorLinkerVersion;
+	uint32_t SizeOfCode;
+	uint32_t SizeOfInitializedData;
+	uint32_t SizeOfUninitializedData;
+	uint32_t AddressOfEntryPoint;
+	uint32_t BaseOfCode;
+	uint64_t ImageBase;
+	uint32_t SectionAlignment;
+	uint32_t FileAlignment;
+	uint16_t MajorOperatingSystemVersion;
+	uint16_t MinorOperatingSystemVersion;
+	uint16_t MajorImageVersion;
+	uint16_t MinorImageVersion;
+	uint16_t MajorSubsystemVersion;
+	uint16_t MinorSubsystemVersion;
+	uint32_t Win32VersionValue;
+	uint32_t SizeOfImage;
+	uint32_t SizeOfHeaders;
+	uint32_t CheckSum;
+	uint16_t Subsystem;
+	uint16_t DllCharacteristics;
+	uint64_t SizeOfStackReserve;
+	uint64_t SizeOfStackCommit;
+	uint64_t SizeOfHeapReserve;
+	uint64_t SizeOfHeapCommit;
+	uint32_t LoaderFlags;
+	uint32_t NumberOfRvaAndSizes;
+	IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
+} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;
+
+typedef struct _IMAGE_NT_HEADERS64 {
+	uint32_t Signature;
+	IMAGE_FILE_HEADER FileHeader;
+	IMAGE_OPTIONAL_HEADER64 OptionalHeader;
+} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
+
+typedef struct _IMAGE_OPTIONAL_HEADER {
+
+	/* Standard fields */
+
+	uint16_t  Magic; /* 0x10b or 0x107 */     /* 0x00 */
+	uint8_t  MajorLinkerVersion;
+	uint8_t  MinorLinkerVersion;
+	uint32_t SizeOfCode;
+	uint32_t SizeOfInitializedData;
+	uint32_t SizeOfUninitializedData;
+	uint32_t AddressOfEntryPoint;            /* 0x10 */
+	uint32_t BaseOfCode;
+	uint32_t BaseOfData;
+
+	/* NT additional fields */
+
+	uint32_t ImageBase;
+	uint32_t SectionAlignment;               /* 0x20 */
+	uint32_t FileAlignment;
+	uint16_t  MajorOperatingSystemVersion;
+	uint16_t  MinorOperatingSystemVersion;
+	uint16_t  MajorImageVersion;
+	uint16_t  MinorImageVersion;
+	uint16_t  MajorSubsystemVersion;          /* 0x30 */
+	uint16_t  MinorSubsystemVersion;
+	uint32_t Win32VersionValue;
+	uint32_t SizeOfImage;
+	uint32_t SizeOfHeaders;
+	uint32_t CheckSum;                       /* 0x40 */
+	uint16_t  Subsystem;
+	uint16_t  DllCharacteristics;
+	uint32_t SizeOfStackReserve;
+	uint32_t SizeOfStackCommit;
+	uint32_t SizeOfHeapReserve;              /* 0x50 */
+	uint32_t SizeOfHeapCommit;
+	uint32_t LoaderFlags;
+	uint32_t NumberOfRvaAndSizes;
+	IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; /* 0x60 */
+	/* 0xE0 */
+} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
+
+typedef struct _IMAGE_NT_HEADERS {
+	uint32_t Signature; /* "PE"\0\0 */       /* 0x00 */
+	IMAGE_FILE_HEADER FileHeader;         /* 0x04 */
+	IMAGE_OPTIONAL_HEADER32 OptionalHeader;       /* 0x18 */
+} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
+
+#define IMAGE_SIZEOF_SHORT_NAME 8
+
+typedef struct _IMAGE_SECTION_HEADER {
+	uint8_t	Name[IMAGE_SIZEOF_SHORT_NAME];
+	union {
+		uint32_t PhysicalAddress;
+		uint32_t VirtualSize;
+	} Misc;
+	uint32_t VirtualAddress;
+	uint32_t SizeOfRawData;
+	uint32_t PointerToRawData;
+	uint32_t PointerToRelocations;
+	uint32_t PointerToLinenumbers;
+	uint16_t	NumberOfRelocations;
+	uint16_t	NumberOfLinenumbers;
+	uint32_t Characteristics;
+} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
+
+#define IMAGE_DIRECTORY_ENTRY_BASERELOC         5
+
+typedef struct _IMAGE_BASE_RELOCATION
+{
+        uint32_t VirtualAddress;
+        uint32_t SizeOfBlock;
+        /* WORD TypeOffset[1]; */
+} IMAGE_BASE_RELOCATION,*PIMAGE_BASE_RELOCATION;
+
+typedef struct _IMAGE_RELOCATION
+{
+	union {
+		uint32_t   VirtualAddress;
+		uint32_t   RelocCount;
+	} DUMMYUNIONNAME;
+	uint32_t   SymbolTableIndex;
+	uint16_t	Type;
+} IMAGE_RELOCATION, *PIMAGE_RELOCATION;
+
+#define IMAGE_SIZEOF_RELOCATION 10
+
+/* generic relocation types */
+#define IMAGE_REL_BASED_ABSOLUTE                0
+#define IMAGE_REL_BASED_HIGH                    1
+#define IMAGE_REL_BASED_LOW                     2
+#define IMAGE_REL_BASED_HIGHLOW                 3
+#define IMAGE_REL_BASED_HIGHADJ                 4
+#define IMAGE_REL_BASED_MIPS_JMPADDR            5
+#define IMAGE_REL_BASED_ARM_MOV32A              5 /* yes, 5 too */
+#define IMAGE_REL_BASED_ARM_MOV32               5 /* yes, 5 too */
+#define IMAGE_REL_BASED_SECTION                 6
+#define IMAGE_REL_BASED_REL                     7
+#define IMAGE_REL_BASED_ARM_MOV32T              7 /* yes, 7 too */
+#define IMAGE_REL_BASED_THUMB_MOV32             7 /* yes, 7 too */
+#define IMAGE_REL_BASED_MIPS_JMPADDR16          9
+#define IMAGE_REL_BASED_IA64_IMM64              9 /* yes, 9 too */
+#define IMAGE_REL_BASED_DIR64                   10
+#define IMAGE_REL_BASED_HIGH3ADJ                11
+
+/* ARM relocation types */
+#define IMAGE_REL_ARM_ABSOLUTE          0x0000
+#define IMAGE_REL_ARM_ADDR              0x0001
+#define IMAGE_REL_ARM_ADDR32NB          0x0002
+#define IMAGE_REL_ARM_BRANCH24          0x0003
+#define IMAGE_REL_ARM_BRANCH11          0x0004
+#define IMAGE_REL_ARM_TOKEN             0x0005
+#define IMAGE_REL_ARM_GPREL12           0x0006
+#define IMAGE_REL_ARM_GPREL7            0x0007
+#define IMAGE_REL_ARM_BLX24             0x0008
+#define IMAGE_REL_ARM_BLX11             0x0009
+#define IMAGE_REL_ARM_SECTION           0x000E
+#define IMAGE_REL_ARM_SECREL            0x000F
+#define IMAGE_REL_ARM_MOV32A            0x0010
+#define IMAGE_REL_ARM_MOV32T            0x0011
+#define IMAGE_REL_ARM_BRANCH20T 0x0012
+#define IMAGE_REL_ARM_BRANCH24T 0x0014
+#define IMAGE_REL_ARM_BLX23T            0x0015
+
+/* ARM64 relocation types */
+#define IMAGE_REL_ARM64_ABSOLUTE        0x0000
+#define IMAGE_REL_ARM64_ADDR32          0x0001
+#define IMAGE_REL_ARM64_ADDR32NB        0x0002
+#define IMAGE_REL_ARM64_BRANCH26        0x0003
+#define IMAGE_REL_ARM64_PAGEBASE_REL21  0x0004
+#define IMAGE_REL_ARM64_REL21           0x0005
+#define IMAGE_REL_ARM64_PAGEOFFSET_12A  0x0006
+#define IMAGE_REL_ARM64_PAGEOFFSET_12L  0x0007
+#define IMAGE_REL_ARM64_SECREL          0x0008
+#define IMAGE_REL_ARM64_SECREL_LOW12A   0x0009
+#define IMAGE_REL_ARM64_SECREL_HIGH12A  0x000A
+#define IMAGE_REL_ARM64_SECREL_LOW12L   0x000B
+#define IMAGE_REL_ARM64_TOKEN           0x000C
+#define IMAGE_REL_ARM64_SECTION         0x000D
+#define IMAGE_REL_ARM64_ADDR64          0x000E
+
+/* AMD64 relocation types */
+#define IMAGE_REL_AMD64_ABSOLUTE        0x0000
+#define IMAGE_REL_AMD64_ADDR64          0x0001
+#define IMAGE_REL_AMD64_ADDR32          0x0002
+#define IMAGE_REL_AMD64_ADDR32NB        0x0003
+#define IMAGE_REL_AMD64_REL32           0x0004
+#define IMAGE_REL_AMD64_REL32_1         0x0005
+#define IMAGE_REL_AMD64_REL32_2         0x0006
+#define IMAGE_REL_AMD64_REL32_3         0x0007
+#define IMAGE_REL_AMD64_REL32_4         0x0008
+#define IMAGE_REL_AMD64_REL32_5         0x0009
+#define IMAGE_REL_AMD64_SECTION         0x000A
+#define IMAGE_REL_AMD64_SECREL          0x000B
+#define IMAGE_REL_AMD64_SECREL7         0x000C
+#define IMAGE_REL_AMD64_TOKEN           0x000D
+#define IMAGE_REL_AMD64_SREL32          0x000E
+#define IMAGE_REL_AMD64_PAIR            0x000F
+#define IMAGE_REL_AMD64_SSPAN32         0x0010
+
+#endif /* _PE_H */
lib/efi_loader/efi_image_loader.c new file +182 -0
new file mode 100644
index 0000000..6e08099
@@ -0,0 +1,182 @@
+/*
+ *  EFI image loader
+ *
+ *  based partly on wine code
+ *
+ *  Copyright (c) 2016 Alexander Graf
+ *
+ *  SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <pe.h>
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+const efi_guid_t efi_guid_device_path = DEVICE_PATH_GUID;
+const efi_guid_t efi_guid_loaded_image = LOADED_IMAGE_GUID;
+
+efi_status_t EFIAPI efi_return_handle(void *handle, efi_guid_t *protocol,
+			void **protocol_interface, void *agent_handle,
+			void *controller_handle, uint32_t attributes)
+{
+	EFI_ENTRY("%p, %p, %p, %p, %p, 0x%x", handle, protocol,
+		  protocol_interface, agent_handle, controller_handle,
+		  attributes);
+	*protocol_interface = handle;
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static void efi_loader_relocate(const IMAGE_BASE_RELOCATION *rel,
+			unsigned long rel_size, void *efi_reloc)
+{
+	const IMAGE_BASE_RELOCATION *end;
+	int i;
+
+	end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + rel_size);
+	while (rel < end - 1 && rel->SizeOfBlock) {
+		const uint16_t *relocs = (const uint16_t *)(rel + 1);
+		i = (rel->SizeOfBlock - sizeof(*rel)) / sizeof(uint16_t);
+		while (i--) {
+			uint16_t offset = (*relocs & 0xfff) +
+					  rel->VirtualAddress;
+			int type = *relocs >> 12;
+			unsigned long delta = (unsigned long)efi_reloc;
+			uint64_t *x64 = efi_reloc + offset;
+			uint32_t *x32 = efi_reloc + offset;
+			uint16_t *x16 = efi_reloc + offset;
+
+			switch (type) {
+			case IMAGE_REL_BASED_ABSOLUTE:
+				break;
+			case IMAGE_REL_BASED_HIGH:
+				*x16 += ((uint32_t)delta) >> 16;
+				break;
+			case IMAGE_REL_BASED_LOW:
+				*x16 += (uint16_t)delta;
+				break;
+			case IMAGE_REL_BASED_HIGHLOW:
+				*x32 += (uint32_t)delta;
+				break;
+			case IMAGE_REL_BASED_DIR64:
+				*x64 += (uint64_t)delta;
+				break;
+			default:
+				printf("Unknown Relocation off %x type %x\n",
+				       offset, type);
+			}
+			relocs++;
+		}
+		rel = (const IMAGE_BASE_RELOCATION *)relocs;
+	}
+}
+
+/*
+ * This function loads all sections from a PE binary into a newly reserved
+ * piece of memory. On successful load it then returns the entry point for
+ * the binary. Otherwise NULL.
+ */
+void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
+{
+	IMAGE_NT_HEADERS32 *nt;
+	IMAGE_DOS_HEADER *dos;
+	IMAGE_SECTION_HEADER *sections;
+	int num_sections;
+	void *efi_reloc;
+	int i;
+	const IMAGE_BASE_RELOCATION *rel;
+	unsigned long rel_size;
+	int rel_idx = IMAGE_DIRECTORY_ENTRY_BASERELOC;
+	void *entry;
+	uint64_t image_size;
+	unsigned long virt_size = 0;
+	bool can_run_nt64 = true;
+	bool can_run_nt32 = true;
+
+#if defined(CONFIG_ARM64)
+	can_run_nt32 = false;
+#elif defined(CONFIG_ARM)
+	can_run_nt64 = false;
+#endif
+
+	dos = efi;
+	if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
+		printf("%s: Invalid DOS Signature\n", __func__);
+		return NULL;
+	}
+
+	nt = (void *) ((char *)efi + dos->e_lfanew);
+	if (nt->Signature != IMAGE_NT_SIGNATURE) {
+		printf("%s: Invalid NT Signature\n", __func__);
+		return NULL;
+	}
+
+	/* Calculate upper virtual address boundary */
+	num_sections = nt->FileHeader.NumberOfSections;
+	sections = (void *)&nt->OptionalHeader +
+			    nt->FileHeader.SizeOfOptionalHeader;
+
+	for (i = num_sections - 1; i >= 0; i--) {
+		IMAGE_SECTION_HEADER *sec = &sections[i];
+		virt_size = max_t(unsigned long, virt_size,
+				  sec->VirtualAddress + sec->Misc.VirtualSize);
+	}
+
+	/* Read 32/64bit specific header bits */
+	if (can_run_nt64 &&
+	    (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)) {
+		IMAGE_NT_HEADERS64 *nt64 = (void *)nt;
+		IMAGE_OPTIONAL_HEADER64 *opt = &nt64->OptionalHeader;
+		image_size = opt->SizeOfImage;
+		efi_reloc = efi_alloc(virt_size, EFI_LOADER_DATA);
+		if (!efi_reloc) {
+			printf("%s: Could not allocate %ld bytes\n",
+				__func__, virt_size);
+			return NULL;
+		}
+		entry = efi_reloc + opt->AddressOfEntryPoint;
+		rel_size = opt->DataDirectory[rel_idx].Size;
+		rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
+	} else if (can_run_nt32 &&
+		   (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)) {
+		IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader;
+		image_size = opt->SizeOfImage;
+		efi_reloc = efi_alloc(virt_size, EFI_LOADER_DATA);
+		if (!efi_reloc) {
+			printf("%s: Could not allocate %ld bytes\n",
+				__func__, virt_size);
+			return NULL;
+		}
+		entry = efi_reloc + opt->AddressOfEntryPoint;
+		rel_size = opt->DataDirectory[rel_idx].Size;
+		rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
+	} else {
+		printf("%s: Invalid optional header magic %x\n", __func__,
+		       nt->OptionalHeader.Magic);
+		return NULL;
+	}
+
+	/* Load sections into RAM */
+	for (i = num_sections - 1; i >= 0; i--) {
+		IMAGE_SECTION_HEADER *sec = &sections[i];
+		memset(efi_reloc + sec->VirtualAddress, 0,
+		       sec->Misc.VirtualSize);
+		memcpy(efi_reloc + sec->VirtualAddress,
+		       efi + sec->PointerToRawData,
+		       sec->SizeOfRawData);
+	}
+
+	/* Run through relocations */
+	efi_loader_relocate(rel, rel_size, efi_reloc);
+
+	/* Flush cache */
+	flush_cache((ulong)efi_reloc, virt_size);
+
+	/* Populate the loaded image interface bits */
+	loaded_image_info->image_base = efi;
+	loaded_image_info->image_size = image_size;
+
+	return entry;
+}
Signature · 1 line
2.6.2
#7 lore raw

PATCH 3/16 efi_loader: Add PE image loader

In reply to Alexander Graf
Quoted 30 lines · Alexander Graf

EFI uses the PE binary format for its application images. Add support to EFI PE binaries as well as all necessary bits for the "EFI image loader" interfaces.

---

v1 -> v2:

- move memory allocation to separate patch
- limit 32/64 to hosts that support it
- check 32bit optional nt header magic
- switch to GPL2+

v2 -> v3:

- use efi_alloc
- add EFIAPI to function prototypes
- remove unused macros
- reorder header inclusion
- split relocation code into function
- flush cache after loading

---
 include/efi_loader.h              |  20 +++
 include/pe.h                      | 263 ++++++++++++++++++++++++++++++++++++++
 lib/efi_loader/efi_image_loader.c | 182 ++++++++++++++++++++++++++
 3 files changed, 465 insertions(+)
 create mode 100644 include/efi_loader.h
 create mode 100644 include/pe.h
 create mode 100644 lib/efi_loader/efi_image_loader.c

[...]

Quoted 43 lines
lib/efi_loader/efi_image_loader.c new file +43 -0
+static void efi_loader_relocate(const IMAGE_BASE_RELOCATION *rel,
+			unsigned long rel_size, void *efi_reloc)
+{
+	const IMAGE_BASE_RELOCATION *end;
+	int i;
+
+	end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + rel_size);
+	while (rel < end - 1 && rel->SizeOfBlock) {
+		const uint16_t *relocs = (const uint16_t *)(rel + 1);
+		i = (rel->SizeOfBlock - sizeof(*rel)) / sizeof(uint16_t);
+		while (i--) {
+			uint16_t offset = (*relocs & 0xfff) +
+					  rel->VirtualAddress;
+			int type = *relocs >> 12;
+			unsigned long delta = (unsigned long)efi_reloc;
+			uint64_t *x64 = efi_reloc + offset;
+			uint32_t *x32 = efi_reloc + offset;
+			uint16_t *x16 = efi_reloc + offset;
+
+			switch (type) {
+			case IMAGE_REL_BASED_ABSOLUTE:
+				break;
+			case IMAGE_REL_BASED_HIGH:
+				*x16 += ((uint32_t)delta) >> 16;
+				break;
+			case IMAGE_REL_BASED_LOW:
+				*x16 += (uint16_t)delta;
+				break;
+			case IMAGE_REL_BASED_HIGHLOW:
+				*x32 += (uint32_t)delta;
+				break;
+			case IMAGE_REL_BASED_DIR64:
+				*x64 += (uint64_t)delta;
+				break;
+			default:
+				printf("Unknown Relocation off %x type %x\n",
+				       offset, type);
+			}
+			relocs++;
+		}
+		rel = (const IMAGE_BASE_RELOCATION *)relocs;
+	}
+}

[...]

Quoted 5 lines
lib/efi_loader/efi_image_loader.c new file +5 -0
+	/* Load sections into RAM */
+	for (i = num_sections - 1; i >= 0; i--) {
+		IMAGE_SECTION_HEADER *sec = &sections[i];
+		memset(efi_reloc + sec->VirtualAddress, 0,
+		       sec->Misc.VirtualSize);

lib/efi_loader/efi_image_loader.c line 175

Quoted 10 lines
lib/efi_loader/efi_image_loader.c new file +10 -0
+		memcpy(efi_reloc + sec->VirtualAddress,
+		       efi + sec->PointerToRawData,
+		       sec->SizeOfRawData);
+	}
+
+	/* Run through relocations */
+	efi_loader_relocate(rel, rel_size, efi_reloc);
+
+	/* Flush cache */
+	flush_cache((ulong)efi_reloc, virt_size);

Where's the I-cache maintenance for the image performed? I can't see it here and I didn't spot it in later patches.

Given that speculative instruction fetches can happen at any time for anything not marked NX, there may already be stale entries in the I-caches.

Also, flush_cache seems to perform DC CIVAC in a loop, which is excessively expensive. To make the instructions visible to instruction fetches you only need DC CVAU (i.e. clean by VA to the PoU), and you only need to do that for executable sections.

Mark.

Quoted 15 lines
lib/efi_loader/efi_image_loader.c new file +7 -0
+
+	/* Populate the loaded image interface bits */
+	loaded_image_info->image_base = efi;
+	loaded_image_info->image_size = image_size;
+
+	return entry;
+}
Signature · 6 lines
2.6.2

_______________________________________________
U-Boot mailing list
U-Boot at lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot
#8 lore raw

PATCH 3/16 efi_loader: Add PE image loader

Quoted 90 lines · Mark Rutland
Quoted 30 lines · Alexander Graf

EFI uses the PE binary format for its application images. Add support to EFI PE binaries as well as all necessary bits for the "EFI image loader" interfaces.

---

v1 -> v2:

- move memory allocation to separate patch
- limit 32/64 to hosts that support it
- check 32bit optional nt header magic
- switch to GPL2+

v2 -> v3:

- use efi_alloc
- add EFIAPI to function prototypes
- remove unused macros
- reorder header inclusion
- split relocation code into function
- flush cache after loading

---
 include/efi_loader.h              |  20 +++
 include/pe.h                      | 263 ++++++++++++++++++++++++++++++++++++++
 lib/efi_loader/efi_image_loader.c | 182 ++++++++++++++++++++++++++
 3 files changed, 465 insertions(+)
 create mode 100644 include/efi_loader.h
 create mode 100644 include/pe.h
 create mode 100644 lib/efi_loader/efi_image_loader.c

[...]

Quoted 43 lines
lib/efi_loader/efi_image_loader.c new file +43 -0
+static void efi_loader_relocate(const IMAGE_BASE_RELOCATION *rel,
+			unsigned long rel_size, void *efi_reloc)
+{
+	const IMAGE_BASE_RELOCATION *end;
+	int i;
+
+	end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + rel_size);
+	while (rel < end - 1 && rel->SizeOfBlock) {
+		const uint16_t *relocs = (const uint16_t *)(rel + 1);
+		i = (rel->SizeOfBlock - sizeof(*rel)) / sizeof(uint16_t);
+		while (i--) {
+			uint16_t offset = (*relocs & 0xfff) +
+					  rel->VirtualAddress;
+			int type = *relocs >> 12;
+			unsigned long delta = (unsigned long)efi_reloc;
+			uint64_t *x64 = efi_reloc + offset;
+			uint32_t *x32 = efi_reloc + offset;
+			uint16_t *x16 = efi_reloc + offset;
+
+			switch (type) {
+			case IMAGE_REL_BASED_ABSOLUTE:
+				break;
+			case IMAGE_REL_BASED_HIGH:
+				*x16 += ((uint32_t)delta) >> 16;
+				break;
+			case IMAGE_REL_BASED_LOW:
+				*x16 += (uint16_t)delta;
+				break;
+			case IMAGE_REL_BASED_HIGHLOW:
+				*x32 += (uint32_t)delta;
+				break;
+			case IMAGE_REL_BASED_DIR64:
+				*x64 += (uint64_t)delta;
+				break;
+			default:
+				printf("Unknown Relocation off %x type %x\n",
+				       offset, type);
+			}
+			relocs++;
+		}
+		rel = (const IMAGE_BASE_RELOCATION *)relocs;
+	}
+}

[...]

Quoted 10 lines
lib/efi_loader/efi_image_loader.c new file +10 -0
+	/* Load sections into RAM */
+	for (i = num_sections - 1; i >= 0; i--) {
+		IMAGE_SECTION_HEADER *sec = &sections[i];
+		memset(efi_reloc + sec->VirtualAddress, 0,
+		       sec->Misc.VirtualSize);
+		memcpy(efi_reloc + sec->VirtualAddress,
+		       efi + sec->PointerToRawData,
+		       sec->SizeOfRawData);
+	}
+
Quoted 8 lines

lib/efi_loader/efi_image_loader.c line 175

Quoted 5 lines
lib/efi_loader/efi_image_loader.c new file +5 -0
+	/* Run through relocations */
+	efi_loader_relocate(rel, rel_size, efi_reloc);
+
+	/* Flush cache */
+	flush_cache((ulong)efi_reloc, virt_size);

Where's the I-cache maintenance for the image performed? I can't see it here and I didn't spot it in later patches.

I've added a call to invalidate_icache_all(); at this spot now.

Quoted 9 lines

Given that speculative instruction fetches can happen at any time for anything not marked NX, there may already be stale entries in the I-caches.

Also, flush_cache seems to perform DC CIVAC in a loop, which is excessively expensive. To make the instructions visible to instruction fetches you only need DC CVAU (i.e. clean by VA to the PoU), and you only need to do that for executable sections.

While I see where you're trying to aim, I don't think the performance overhead will be really worth premature optimization. flush_cache is a globally available operation for all u-boot target platforms. Having something that is platform agnostic (and very simple) is worth more than a few us performance for now imho.

Alex

#9 lore raw

PATCH 3/16 efi_loader: Add PE image loader

In reply to Alexander Graf
Quoted 22 lines · Alexander Graf

EFI uses the PE binary format for its application images. Add support to EFI PE binaries as well as all necessary bits for the "EFI image loader" interfaces.

---

v1 -> v2:

- move memory allocation to separate patch
- limit 32/64 to hosts that support it
- check 32bit optional nt header magic
- switch to GPL2+

v2 -> v3:

- use efi_alloc
- add EFIAPI to function prototypes
- remove unused macros
- reorder header inclusion
- split relocation code into function
- flush cache after loading

Quoted 8 lines
---
 include/efi_loader.h              |  20 +++
 include/pe.h                      | 263 ++++++++++++++++++++++++++++++++++++++
 lib/efi_loader/efi_image_loader.c | 182 ++++++++++++++++++++++++++
 3 files changed, 465 insertions(+)
 create mode 100644 include/efi_loader.h
 create mode 100644 include/pe.h
 create mode 100644 lib/efi_loader/efi_image_loader.c

Tested on Beaglebone Black and Minnowboard MAX:

#10 lore raw

PATCH 4/16 efi_loader: Add boot time services

In reply to Alexander Graf

When an EFI application runs, it has access to a few descriptor and callback tables to instruct the EFI compliant firmware to do things for it. The bulk of those interfaces are "boot time services". They handle all object management, and memory allocation.

This patch adds support for the boot time services and also exposes a system table, which is the point of entry descriptor table for EFI payloads.

---

v1 -> v2:

- Fix typo s/does now/does not/
- Add #ifdefs around header to allow inclusion when efi_loader is disabled
- Add stub efi_restore_gd() function when efi_loader is disabled
- Disable debug
- Mark runtime region as such
- Fix up memory map
- Allow efi_restore_gd to be called before first efi entry
- Add 32bit arm cache workaround
- Move memory map to separate patch
- Change BTS version to 2.5
- Fix return values for a few callbacks to more EFI compliant ones
- Change vendor to "Das U-Boot"
- Add warning when truncating timer trigger
- Move to GPLv2+

v2 -> v3:

- Use external efi_memory helpers
- Add EFIAPI to function prototypes
- Initialize event timer to -1ULL to prevent early firing
- Document header
- Move obj list to lib
- Remove implicit guid table
- Add guid compare function
- Fix return values
- Implement efi_wait_for_event
- Implement efi_install_configuration_table

---
 include/efi_loader.h          |  84 +++++
 lib/efi_loader/efi_boottime.c | 781 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 865 insertions(+)
 create mode 100644 lib/efi_loader/efi_boottime.c
include/efi_loader.h +84 -0
index 5618185..a7f033e 100644
@@ -6,15 +6,99 @@
  *  SPDX-License-Identifier:     GPL-2.0+
  */
 
+#include <common.h>
 #include <part_efi.h>
 #include <efi_api.h>
+
+#ifdef CONFIG_EFI_LOADER
+
 #include <linux/list.h>
 
+/* #define DEBUG_EFI */
+
+#ifdef DEBUG_EFI
+#define EFI_ENTRY(format, ...) do { \
+	efi_restore_gd(); \
+	printf("EFI: Entry %s(" format ")\n", __func__, ##__VA_ARGS__); \
+	} while(0)
+#else
+#define EFI_ENTRY(format, ...) do { \
+	efi_restore_gd(); \
+	} while(0)
+#endif
+
+#define EFI_EXIT(ret) efi_exit_func(ret);
+
+extern struct efi_system_table systab;
+
 extern const efi_guid_t efi_guid_device_path;
 extern const efi_guid_t efi_guid_loaded_image;
 
+/*
+ * While UEFI objects can have callbacks, you can also call functions on
+ * protocols (classes) themselves. This struct maps a protocol GUID to its
+ * interface (usually a struct with callback functions).
+ */
+struct efi_class_map {
+	const efi_guid_t *guid;
+	const void *interface;
+};
+
+/*
+ * When the UEFI payload wants to open a protocol on an object to get its
+ * interface (usually a struct with callback functions), this struct maps the
+ * protocol GUID to the respective protocol handler open function for that
+ * object protocol combination.
+ */
+struct efi_handler {
+	const efi_guid_t *guid;
+	efi_status_t (EFIAPI *open)(void *handle,
+			efi_guid_t *protocol, void **protocol_interface,
+			void *agent_handle, void *controller_handle,
+			uint32_t attributes);
+};
+
+/*
+ * UEFI has a poor man's OO model where one "object" can be polimorphic and have
+ * multiple different protocols (classes) attached to it.
+ *
+ * This struct is the parent struct for all of our actual implementation objects
+ * that can include it to make themselves an EFI object
+ */
+struct efi_object {
+	/* Every UEFI object is part of a global object list */
+	struct list_head link;
+	/* We support up to 4 "protocols" an object can be accessed through */
+	struct efi_handler protocols[4];
+	/* The object spawner can either use this for data or as identifier */
+	void *handle;
+};
+
+/* This list contains all UEFI objects we know of */
+extern struct list_head efi_obj_list;
+
+/*
+ * Stub implementation for a protocol opener that just returns the handle as
+ * interface
+ */
 efi_status_t efi_return_handle(void *handle,
 		efi_guid_t *protocol, void **protocol_interface,
 		void *agent_handle, void *controller_handle,
 		uint32_t attributes);
+/* Called from places to check whether a timer expired */
+void efi_timer_check(void);
+/* PE loader implementation */
 void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info);
+/* Called once to store the pristine gd pointer */
+void efi_save_gd(void);
+/* Called from EFI_ENTRY on callback entry to put gd into the gd register */
+void efi_restore_gd(void);
+/* Called from EFI_EXIT on callback exit to restore the gd register */
+efi_status_t efi_exit_func(efi_status_t ret);
+
+#else /* defined(EFI_LOADER) */
+
+/* No loader configured, stub out EFI_ENTRY */
+static inline void efi_restore_gd(void) { }
+
+#endif
lib/efi_loader/efi_boottime.c new file +781 -0

781 lines not on this page · raw message

Signature · 1 line
2.6.2
#11 lore raw

PATCH 4/16 efi_loader: Add boot time services

Quoted 100 lines · Alexander Graf

When an EFI application runs, it has access to a few descriptor and callback tables to instruct the EFI compliant firmware to do things for it. The bulk of those interfaces are "boot time services". They handle all object management, and memory allocation.

This patch adds support for the boot time services and also exposes a system table, which is the point of entry descriptor table for EFI payloads.

---

v1 -> v2:

- Fix typo s/does now/does not/
- Add #ifdefs around header to allow inclusion when efi_loader is disabled
- Add stub efi_restore_gd() function when efi_loader is disabled
- Disable debug
- Mark runtime region as such
- Fix up memory map
- Allow efi_restore_gd to be called before first efi entry
- Add 32bit arm cache workaround
- Move memory map to separate patch
- Change BTS version to 2.5
- Fix return values for a few callbacks to more EFI compliant ones
- Change vendor to "Das U-Boot"
- Add warning when truncating timer trigger
- Move to GPLv2+

v2 -> v3:

- Use external efi_memory helpers
- Add EFIAPI to function prototypes
- Initialize event timer to -1ULL to prevent early firing
- Document header
- Move obj list to lib
- Remove implicit guid table
- Add guid compare function
- Fix return values
- Implement efi_wait_for_event
- Implement efi_install_configuration_table

---
 include/efi_loader.h          |  84 +++++
 lib/efi_loader/efi_boottime.c | 781 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 865 insertions(+)
 create mode 100644 lib/efi_loader/efi_boottime.c
include/efi_loader.h +37 -0
index 5618185..a7f033e 100644
@@ -6,15 +6,99 @@
  *  SPDX-License-Identifier:     GPL-2.0+
  */
 
+#include <common.h>
 #include <part_efi.h>
 #include <efi_api.h>
+
+#ifdef CONFIG_EFI_LOADER
+
 #include <linux/list.h>
 
+/* #define DEBUG_EFI */
+
+#ifdef DEBUG_EFI
+#define EFI_ENTRY(format, ...) do { \
+	efi_restore_gd(); \
+	printf("EFI: Entry %s(" format ")\n", __func__, ##__VA_ARGS__); \
+	} while(0)
+#else
+#define EFI_ENTRY(format, ...) do { \
+	efi_restore_gd(); \
+	} while(0)
+#endif
+
+#define EFI_EXIT(ret) efi_exit_func(ret);
+
+extern struct efi_system_table systab;
+
 extern const efi_guid_t efi_guid_device_path;
 extern const efi_guid_t efi_guid_loaded_image;
 
+/*
+ * While UEFI objects can have callbacks, you can also call functions on
+ * protocols (classes) themselves. This struct maps a protocol GUID to its
+ * interface (usually a struct with callback functions).
+ */
+struct efi_class_map {
+	const efi_guid_t *guid;
+	const void *interface;
+};
+
+/*
+ * When the UEFI payload wants to open a protocol on an object to get its
+ * interface (usually a struct with callback functions), this struct maps the
+ * protocol GUID to the respective protocol handler open function for that
+ * object protocol combination.
+ */

include/efi_loader.h line 62

Quoted 10 lines
include/efi_loader.h +10 -0
+struct efi_handler {
+	const efi_guid_t *guid;
+	efi_status_t (EFIAPI *open)(void *handle,
+			efi_guid_t *protocol, void **protocol_interface,
+			void *agent_handle, void *controller_handle,
+			uint32_t attributes);
+};
+
+/*
+ * UEFI has a poor man's OO model where one "object" can be polimorphic and have

Polymorphic.
Also, somewhat long lines in this comment block?

Quoted 70 lines
include/efi_loader.h +37 -0
+ * multiple different protocols (classes) attached to it.
+ *
+ * This struct is the parent struct for all of our actual implementation objects
+ * that can include it to make themselves an EFI object
+ */
+struct efi_object {
+	/* Every UEFI object is part of a global object list */
+	struct list_head link;
+	/* We support up to 4 "protocols" an object can be accessed through */
+	struct efi_handler protocols[4];
+	/* The object spawner can either use this for data or as identifier */
+	void *handle;
+};
+
+/* This list contains all UEFI objects we know of */
+extern struct list_head efi_obj_list;
+
+/*
+ * Stub implementation for a protocol opener that just returns the handle as
+ * interface
+ */
 efi_status_t efi_return_handle(void *handle,
 		efi_guid_t *protocol, void **protocol_interface,
 		void *agent_handle, void *controller_handle,
 		uint32_t attributes);
+/* Called from places to check whether a timer expired */
+void efi_timer_check(void);
+/* PE loader implementation */
 void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info);
+/* Called once to store the pristine gd pointer */
+void efi_save_gd(void);
+/* Called from EFI_ENTRY on callback entry to put gd into the gd register */
+void efi_restore_gd(void);
+/* Called from EFI_EXIT on callback exit to restore the gd register */
+efi_status_t efi_exit_func(efi_status_t ret);
+
+#else /* defined(EFI_LOADER) */
+
+/* No loader configured, stub out EFI_ENTRY */
+static inline void efi_restore_gd(void) { }
+
+#endif
lib/efi_loader/efi_boottime.c new file +22 -0
new file mode 100644
index 0000000..e60fae9
@@ -0,0 +1,781 @@
+/*
+ *  EFI application boot time services
+ *
+ *  Copyright (c) 2016 Alexander Graf
+ *
+ *  SPDX-License-Identifier:     GPL-2.0+
+ */
+
+/* #define DEBUG_EFI */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <malloc.h>
+#include <asm/global_data.h>
+#include <libfdt_env.h>
+#include <u-boot/crc.h>
+#include <bootm.h>
+#include <inttypes.h>
+#include <watchdog.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+

lib/efi_loader/efi_boottime.c line 32

Quoted 10 lines
lib/efi_loader/efi_boottime.c new file +10 -0
+/* This list contains all the EFI objects our payload has access to */
+LIST_HEAD(efi_obj_list);
+
+/*
+ * If we're running on nasty systems (32bit ARM booting into non-EFI Linux)
+ * we need to do trickery with caches. Since we don't want to break the EFI
+ * aware boot path, only apply hacks when loading exiting directly (breaking
+ * direct Linux EFI booting along the way - oh well).
+ */
+static bool efi_is_direct_boot = true;

Is this a case worth supporting?

Quoted 46 lines
lib/efi_loader/efi_boottime.c new file +46 -0
+
+/*
+ * EFI can pass arbitrary additional "tables" containing vendor specific
+ * information to the payload. One such table is the FDT table which contains
+ * a pointer to a flattened device tree blob.
+ *
+ * In most cases we want to pass an FDT to the payload, so reserve one slot of
+ * config table space for it. The pointer gets populated by do_bootefi_exec().
+ */
+static struct efi_configuration_table efi_conf_table[1];
+
+/*
+ * The "gd" pointer lives in a register on ARM and AArch64 that we declare
+ * fixed when compiling U-Boot. However, the payload does not know about that
+ * restriction so we need to manually swap its and our view of that register on
+ * EFI callback entry/exit.
+ */
+static volatile void *efi_gd, *app_gd;
+
+/* Called from do_bootefi_exec() */
+void efi_save_gd(void)
+{
+	efi_gd = gd;
+}
+
+/* Called on every callback entry */
+void efi_restore_gd(void)
+{
+	/* Only restore if we're already in EFI context */
+	if (!efi_gd)
+		return;
+
+	if (gd != efi_gd)
+		app_gd = gd;
+	gd = efi_gd;
+}
+
+/* Called on every callback exit */
+efi_status_t efi_exit_func(efi_status_t ret)
+{
+	gd = app_gd;
+	return ret;
+}
+
+static efi_status_t efi_unsupported(const char *funcname)
+{

lib/efi_loader/efi_boottime.c line 88

Quoted 10 lines
lib/efi_loader/efi_boottime.c new file +10 -0
+#ifdef DEBUG_EFI
+	printf("EFI: App called into unimplemented function %s\n", funcname);
+#endif
+	return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+static int guidcmp(const efi_guid_t *g1, const efi_guid_t *g2)
+{
+	return memcmp(g1, g2, sizeof(efi_guid_t));
+}

Not necessary for this set, but this could be useful as a generic function. (at least part_efi.c could be another consumer)

Quoted 402 lines
lib/efi_loader/efi_boottime.c new file +402 -0
+
+static unsigned long EFIAPI efi_raise_tpl(unsigned long new_tpl)
+{
+	EFI_ENTRY("0x%lx", new_tpl);
+	return EFI_EXIT(0);
+}
+
+static void EFIAPI efi_restore_tpl(unsigned long old_tpl)
+{
+	EFI_ENTRY("0x%lx", old_tpl);
+	EFI_EXIT(efi_unsupported(__func__));
+}
+
+efi_status_t EFIAPI efi_allocate_pages_ext(int type, int memory_type,
+					   unsigned long pages,
+					   uint64_t *memory)
+{
+	efi_status_t r;
+
+	EFI_ENTRY("%d, %d, 0x%lx, %p", type, memory_type, pages, memory);
+	r = efi_allocate_pages(type, memory_type, pages, memory);
+	return EFI_EXIT(r);
+}
+
+efi_status_t EFIAPI efi_free_pages_ext(uint64_t memory, unsigned long pages)
+{
+	efi_status_t r;
+
+	EFI_ENTRY("%"PRIx64", 0x%lx", memory, pages);
+	r = efi_free_pages(memory, pages);
+	return EFI_EXIT(r);
+}
+
+efi_status_t EFIAPI efi_get_memory_map_ext(unsigned long *memory_map_size,
+					   struct efi_mem_desc *memory_map,
+					   unsigned long *map_key,
+					   unsigned long *descriptor_size,
+					   uint32_t *descriptor_version)
+{
+	efi_status_t r;
+
+	EFI_ENTRY("%p, %p, %p, %p, %p", memory_map_size, memory_map,
+		  map_key, descriptor_size, descriptor_version);
+	r = efi_get_memory_map(memory_map_size, memory_map, map_key,
+			       descriptor_size, descriptor_version);
+	return EFI_EXIT(r);
+}
+
+static efi_status_t EFIAPI efi_allocate_pool(int pool_type, unsigned long size,
+					     void **buffer)
+{
+	return efi_allocate_pages(0, pool_type, (size + 0xfff) >> 12, (void*)buffer);
+}
+
+static efi_status_t EFIAPI efi_free_pool(void *buffer)
+{
+	return efi_free_pages((ulong)buffer, 0);
+}
+
+/*
+ * Our event capabilities are very limited. Only support a single
+ * event to exist, so we don't need to maintain lists.
+ */
+static struct {
+	enum efi_event_type type;
+	u32 trigger_type;
+	u32 trigger_time;
+	u64 trigger_next;
+	unsigned long notify_tpl;
+	void (*notify_function) (void *event, void *context);
+	void *notify_context;
+} efi_event = {
+	/* Disable timers on bootup */
+	.trigger_next = -1ULL,
+};
+
+static efi_status_t EFIAPI efi_create_event(
+			enum efi_event_type type, ulong notify_tpl,
+			void (*notify_function) (void *event, void *context),
+			void *notify_context, void **event)
+{
+	EFI_ENTRY("%d, 0x%lx, %p, %p", type, notify_tpl, notify_function,
+		  notify_context);
+	if (efi_event.notify_function) {
+		/* We only support one event at a time */
+		return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+	}
+
+	efi_event.type = type;
+	efi_event.notify_tpl = notify_tpl;
+	efi_event.notify_function = notify_function;
+	efi_event.notify_context = notify_context;
+	*event = &efi_event;
+
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+/*
+ * Our timers have to work without interrupts, so we check whenever keyboard
+ * input or disk accesses happen if enough time elapsed for it to fire.
+ */
+void efi_timer_check(void)
+{
+	u64 now = timer_get_us();
+
+	if (now >= efi_event.trigger_next) {
+		/* Triggering! */
+		if (efi_event.trigger_type == EFI_TIMER_PERIODIC)
+			efi_event.trigger_next += efi_event.trigger_time / 10;
+		efi_event.notify_function(&efi_event, efi_event.notify_context);
+	}
+
+	WATCHDOG_RESET();
+}
+
+static efi_status_t EFIAPI efi_set_timer(void *event, int type,
+					 uint64_t trigger_time)
+{
+	/* We don't have 64bit division available everywhere, so limit timer
+	 * distances to 32bit bits. */
+	u32 trigger32 = trigger_time;
+
+	EFI_ENTRY("%p, %d, %"PRIx64, event, type, trigger_time);
+
+	if (trigger32 < trigger_time) {
+		printf("WARNING: Truncating timer from %"PRIx64" to %x\n",
+		       trigger_time, trigger32);
+	}
+
+	if (event != &efi_event) {
+		/* We only support one event at a time */
+		return EFI_EXIT(EFI_INVALID_PARAMETER);
+	}
+
+	switch (type) {
+	case EFI_TIMER_STOP:
+		efi_event.trigger_next = -1ULL;
+		break;
+	case EFI_TIMER_PERIODIC:
+	case EFI_TIMER_RELATIVE:
+		efi_event.trigger_next = timer_get_us() + (trigger32 / 10);
+		break;
+	default:
+		return EFI_EXIT(EFI_INVALID_PARAMETER);
+	}
+	efi_event.trigger_type = type;
+	efi_event.trigger_time = trigger_time;
+
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_wait_for_event(unsigned long num_events,
+					      void *event, unsigned long *index)
+{
+	u64 now;
+
+	EFI_ENTRY("%ld, %p, %p", num_events, event, index);
+
+	now = timer_get_us();
+	while (now < efi_event.trigger_next) { }
+	efi_timer_check();
+
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_signal_event(void *event)
+{
+	EFI_ENTRY("%p", event);
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_close_event(void *event)
+{
+	EFI_ENTRY("%p", event);
+	efi_event.trigger_next = -1ULL;
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_check_event(void *event)
+{
+	EFI_ENTRY("%p", event);
+	return EFI_EXIT(EFI_NOT_READY);
+}
+
+static efi_status_t EFIAPI efi_install_protocol_interface(void **handle,
+			efi_guid_t *protocol, int protocol_interface_type,
+			void *protocol_interface)
+{
+	EFI_ENTRY("%p, %p, %d, %p", handle, protocol, protocol_interface_type,
+		  protocol_interface);
+	return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+}
+static efi_status_t EFIAPI efi_reinstall_protocol_interface(void *handle,
+			efi_guid_t *protocol, void *old_interface,
+			void *new_interface)
+{
+	EFI_ENTRY("%p, %p, %p, %p", handle, protocol, old_interface,
+		  new_interface);
+	return EFI_EXIT(EFI_ACCESS_DENIED);
+}
+
+static efi_status_t EFIAPI efi_uninstall_protocol_interface(void *handle,
+			efi_guid_t *protocol, void *protocol_interface)
+{
+	EFI_ENTRY("%p, %p, %p", handle, protocol, protocol_interface);
+	return EFI_EXIT(EFI_NOT_FOUND);
+}
+
+static efi_status_t EFIAPI efi_register_protocol_notify(efi_guid_t *protocol,
+							void *event,
+							void **registration)
+{
+	EFI_ENTRY("%p, %p, %p", protocol, event, registration);
+	return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+}
+
+static int efi_search(enum efi_locate_search_type search_type,
+		      efi_guid_t *protocol, void *search_key,
+		      struct efi_object *efiobj)
+{
+	int i;
+
+	switch (search_type) {
+	case all_handles:
+		return 0;
+	case by_register_notify:
+		return -1;
+	case by_protocol:
+		for (i = 0; i < ARRAY_SIZE(efiobj->protocols); i++) {
+			const efi_guid_t *guid = efiobj->protocols[i].guid;
+			if (guid && !guidcmp(guid, protocol))
+				return 0;
+		}
+		return -1;
+	}
+
+	return -1;
+}
+
+static efi_status_t EFIAPI efi_locate_handle(
+			enum efi_locate_search_type search_type,
+			efi_guid_t *protocol, void *search_key,
+			unsigned long *buffer_size, efi_handle_t *buffer)
+{
+	struct list_head *lhandle;
+	unsigned long size = 0;
+
+	EFI_ENTRY("%d, %p, %p, %p, %p", search_type, protocol, search_key,
+		  buffer_size, buffer);
+
+	/* Count how much space we need */
+	list_for_each(lhandle, &efi_obj_list) {
+		struct efi_object *efiobj;
+		efiobj = list_entry(lhandle, struct efi_object, link);
+		if (!efi_search(search_type, protocol, search_key, efiobj)) {
+			size += sizeof(void*);
+		}
+	}
+
+	if (*buffer_size < size) {
+		*buffer_size = size;
+		return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
+	}
+
+	/* Then fill the array */
+	list_for_each(lhandle, &efi_obj_list) {
+		struct efi_object *efiobj;
+		efiobj = list_entry(lhandle, struct efi_object, link);
+		if (!efi_search(search_type, protocol, search_key, efiobj)) {
+			*(buffer++) = efiobj->handle;
+		}
+	}
+
+	*buffer_size = size;
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_locate_device_path(efi_guid_t *protocol,
+			struct efi_device_path **device_path,
+			efi_handle_t *device)
+{
+	EFI_ENTRY("%p, %p, %p", protocol, device_path, device);
+	return EFI_EXIT(EFI_NOT_FOUND);
+}
+
+static efi_status_t EFIAPI efi_install_configuration_table(efi_guid_t *guid,
+							   void *table)
+{
+	int i;
+
+	EFI_ENTRY("%p, %p", guid, table);
+
+	/* Check for guid override */
+	for (i = 0; i < systab.nr_tables; i++) {
+		if (!guidcmp(guid, &efi_conf_table[i].guid)) {
+			efi_conf_table[i].table = table;
+			return EFI_EXIT(EFI_SUCCESS);
+		}
+	}
+
+	/* No override, check for overflow */
+	if (i >= ARRAY_SIZE(efi_conf_table))
+		return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+
+	/* Add a new entry */
+	memcpy(&efi_conf_table[i].guid, guid, sizeof(*guid));
+	efi_conf_table[i].table = table;
+	systab.nr_tables = i;
+
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_load_image(bool boot_policy,
+					  efi_handle_t parent_image,
+					  struct efi_device_path *file_path,
+					  void *source_buffer,
+					  unsigned long source_size,
+					  efi_handle_t *image_handle)
+{
+	static struct efi_object loaded_image_info_obj = {
+		.protocols = {
+			{
+				.guid = &efi_guid_loaded_image,
+				.open = &efi_return_handle,
+			},
+		},
+	};
+	struct efi_loaded_image *info;
+	struct efi_object *obj;
+
+	EFI_ENTRY("%d, %p, %p, %p, %ld, %p", boot_policy, parent_image,
+		  file_path, source_buffer, source_size, image_handle);
+	info = malloc(sizeof(*info));
+	obj = malloc(sizeof(loaded_image_info_obj));
+	memset(info, 0, sizeof(*info));
+	memcpy(obj, &loaded_image_info_obj, sizeof(loaded_image_info_obj));
+	obj->handle = info;
+	info->file_path = file_path;
+	info->reserved = efi_load_pe(source_buffer, info);
+	if (!info->reserved) {
+		free(info);
+		free(obj);
+		return EFI_EXIT(EFI_UNSUPPORTED);
+	}
+
+	*image_handle = info;
+	list_add_tail(&obj->link, &efi_obj_list);
+
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
+					   unsigned long *exit_data_size,
+					   s16 **exit_data)
+{
+	ulong (*entry)(void *image_handle, struct efi_system_table *st);
+	struct efi_loaded_image *info = image_handle;
+
+	EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data);
+	entry = info->reserved;
+
+	efi_is_direct_boot = false;
+
+	/* call the image! */
+	entry(image_handle, &systab);
+
+	/* Should usually never get here */
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_exit(void *image_handle, long exit_status,
+				    unsigned long exit_data_size,
+				    uint16_t *exit_data)
+{
+	EFI_ENTRY("%p, %ld, %ld, %p", image_handle, exit_status,
+		  exit_data_size, exit_data);
+	return EFI_EXIT(efi_unsupported(__func__));
+}
+
+static struct efi_object *efi_search_obj(void *handle)
+{
+	struct list_head *lhandle;
+
+	list_for_each(lhandle, &efi_obj_list) {
+		struct efi_object *efiobj;
+		efiobj = list_entry(lhandle, struct efi_object, link);
+		if (efiobj->handle == handle)
+			return efiobj;
+	}
+
+	return NULL;
+}
+
+static efi_status_t EFIAPI efi_unload_image(void *image_handle)
+{
+	struct efi_object *efiobj;
+
+	EFI_ENTRY("%p", image_handle);
+	efiobj = efi_search_obj(image_handle);
+	if (efiobj)
+		list_del(&efiobj->link);
+

lib/efi_loader/efi_boottime.c line 500

Quoted 10 lines
lib/efi_loader/efi_boottime.c new file +10 -0
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static void efi_exit_caches(void)
+{
+#if defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
+	/*
+	 * Grub on 32bit ARM needs to have caches disabled before jumping into
+	 * a zImage, but does not know of all cache layers. Give it a hand.
+	 */

Should we even be doing this hack?
EFI stub support for 32-bit ARM is now (finally) upstream.

Quoted 284 lines
lib/efi_loader/efi_boottime.c new file +281 -0
+	if (efi_is_direct_boot)
+		cleanup_before_linux();
+#endif
+}
+
+static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle,
+						  unsigned long map_key)
+{
+	EFI_ENTRY("%p, %ld", image_handle, map_key);
+
+	/* Fix up caches for EFI payloads if necessary */
+	efi_exit_caches();
+
+	/* This stops all lingering devices */
+	bootm_disable_interrupts();
+
+	/* Give the payload some time to boot */
+	WATCHDOG_RESET();
+
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_get_next_monotonic_count(uint64_t *count)
+{
+	static uint64_t mono = 0;
+	EFI_ENTRY("%p", count);
+	*count = mono++;
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_stall(unsigned long microseconds)
+{
+	EFI_ENTRY("%ld", microseconds);
+	udelay(microseconds);
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout,
+						  uint64_t watchdog_code,
+						  unsigned long data_size,
+						  uint16_t *watchdog_data)
+{
+	EFI_ENTRY("%ld, 0x%"PRIx64", %ld, %p", timeout, watchdog_code,
+		  data_size, watchdog_data);
+	return EFI_EXIT(efi_unsupported(__func__));
+}
+
+static efi_status_t EFIAPI efi_connect_controller(
+			efi_handle_t controller_handle,
+			efi_handle_t *driver_image_handle,
+			struct efi_device_path *remain_device_path,
+			bool recursive)
+{
+	EFI_ENTRY("%p, %p, %p, %d", controller_handle, driver_image_handle,
+		  remain_device_path, recursive);
+	return EFI_EXIT(EFI_NOT_FOUND);
+}
+
+static efi_status_t EFIAPI efi_disconnect_controller(void *controller_handle,
+						     void *driver_image_handle,
+						     void *child_handle)
+{
+	EFI_ENTRY("%p, %p, %p", controller_handle, driver_image_handle,
+		  child_handle);
+	return EFI_EXIT(EFI_INVALID_PARAMETER);
+}
+
+static efi_status_t EFIAPI efi_close_protocol(void *handle,
+					      efi_guid_t *protocol,
+					      void *agent_handle,
+					      void *controller_handle)
+{
+	EFI_ENTRY("%p, %p, %p, %p", handle, protocol, agent_handle,
+		  controller_handle);
+	return EFI_EXIT(EFI_NOT_FOUND);
+}
+
+static efi_status_t EFIAPI efi_open_protocol_information(efi_handle_t handle,
+			efi_guid_t *protocol,
+			struct efi_open_protocol_info_entry **entry_buffer,
+			unsigned long *entry_count)
+{
+	EFI_ENTRY("%p, %p, %p, %p", handle, protocol, entry_buffer,
+		  entry_count);
+	return EFI_EXIT(EFI_NOT_FOUND);
+}
+
+static efi_status_t EFIAPI efi_protocols_per_handle(void *handle,
+			efi_guid_t ***protocol_buffer,
+			unsigned long *protocol_buffer_count)
+{
+	EFI_ENTRY("%p, %p, %p", handle, protocol_buffer,
+		  protocol_buffer_count);
+	return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+}
+
+static efi_status_t EFIAPI efi_locate_handle_buffer(
+			enum efi_locate_search_type search_type,
+			efi_guid_t *protocol, void *search_key,
+			unsigned long *no_handles, efi_handle_t **buffer)
+{
+	EFI_ENTRY("%d, %p, %p, %p, %p", search_type, protocol, search_key,
+		  no_handles, buffer);
+	return EFI_EXIT(EFI_NOT_FOUND);
+}
+
+static struct efi_class_map efi_class_maps[] = {
+	{
+		.guid = &efi_guid_console_control,
+		.interface = &efi_console_control
+	},
+};
+
+static efi_status_t EFIAPI efi_locate_protocol(efi_guid_t *protocol,
+					       void *registration,
+					       void **protocol_interface)
+{
+	int i;
+
+	EFI_ENTRY("%p, %p, %p", protocol, registration, protocol_interface);
+	for (i = 0; i < ARRAY_SIZE(efi_class_maps); i++) {
+		struct efi_class_map *curmap = &efi_class_maps[i];
+		if (!guidcmp(protocol, curmap->guid)) {
+			*protocol_interface = (void*)curmap->interface;
+			return EFI_EXIT(EFI_SUCCESS);
+		}
+	}
+
+	return EFI_EXIT(EFI_NOT_FOUND);
+}
+
+static efi_status_t EFIAPI efi_install_multiple_protocol_interfaces(
+			void **handle, ...)
+{
+	EFI_ENTRY("%p", handle);
+	return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+}
+
+static efi_status_t EFIAPI efi_uninstall_multiple_protocol_interfaces(
+			void *handle, ...)
+{
+	EFI_ENTRY("%p", handle);
+	return EFI_EXIT(EFI_INVALID_PARAMETER);
+}
+
+static efi_status_t EFIAPI efi_calculate_crc32(void *data,
+					       unsigned long data_size,
+					       uint32_t *crc32_p)
+{
+	EFI_ENTRY("%p, %ld", data, data_size);
+	*crc32_p = crc32(0, data, data_size);
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static void EFIAPI efi_copy_mem(void *destination, void *source,
+				unsigned long length)
+{
+	EFI_ENTRY("%p, %p, %ld", destination, source, length);
+	memcpy(destination, source, length);
+}
+
+static void EFIAPI efi_set_mem(void *buffer, unsigned long size, uint8_t value)
+{
+	EFI_ENTRY("%p, %ld, 0x%x", buffer, size, value);
+	memset(buffer, value, size);
+}
+
+static efi_status_t EFIAPI efi_open_protocol(
+			void *handle, efi_guid_t *protocol,
+			void **protocol_interface, void *agent_handle,
+			void *controller_handle, uint32_t attributes)
+{
+	struct list_head *lhandle;
+	int i;
+	efi_status_t r = EFI_UNSUPPORTED;
+
+	EFI_ENTRY("%p, %p, %p, %p, %p, 0x%x", handle, protocol,
+		  protocol_interface, agent_handle, controller_handle,
+		  attributes);
+	list_for_each(lhandle, &efi_obj_list) {
+		struct efi_object *efiobj;
+		efiobj = list_entry(lhandle, struct efi_object, link);
+
+		if (efiobj->handle != handle)
+			continue;
+
+		for (i = 0; i < ARRAY_SIZE(efiobj->protocols); i++) {
+			struct efi_handler *handler = &efiobj->protocols[i];
+			const efi_guid_t *hprotocol = handler->guid;
+			if (!hprotocol)
+				break;
+			if (!guidcmp(hprotocol, protocol)) {
+				r = handler->open(handle, protocol,
+				    protocol_interface, agent_handle,
+				    controller_handle, attributes);
+				goto out;
+			}
+		}
+	}
+
+out:
+	return EFI_EXIT(r);
+}
+
+static efi_status_t EFIAPI efi_handle_protocol(void *handle,
+					       efi_guid_t *protocol,
+					       void **protocol_interface)
+{
+	EFI_ENTRY("%p, %p, %p", handle, protocol, protocol_interface);
+	return efi_open_protocol(handle, protocol, protocol_interface,
+				 NULL, NULL, 0);
+}
+
+static const struct efi_boot_services efi_boot_services = {
+	.hdr = {
+		.headersize = sizeof(struct efi_table_hdr),
+	},
+	.raise_tpl = efi_raise_tpl,
+	.restore_tpl = efi_restore_tpl,
+	.allocate_pages = efi_allocate_pages_ext,
+	.free_pages = efi_free_pages_ext,
+	.get_memory_map = efi_get_memory_map_ext,
+	.allocate_pool = efi_allocate_pool,
+	.free_pool = efi_free_pool,
+	.create_event = efi_create_event,
+	.set_timer = efi_set_timer,
+	.wait_for_event = efi_wait_for_event,
+	.signal_event = efi_signal_event,
+	.close_event = efi_close_event,
+	.check_event = efi_check_event,
+	.install_protocol_interface = efi_install_protocol_interface,
+	.reinstall_protocol_interface = efi_reinstall_protocol_interface,
+	.uninstall_protocol_interface = efi_uninstall_protocol_interface,
+	.handle_protocol = efi_handle_protocol,
+	.reserved = NULL,
+	.register_protocol_notify = efi_register_protocol_notify,
+	.locate_handle = efi_locate_handle,
+	.locate_device_path = efi_locate_device_path,
+	.install_configuration_table = efi_install_configuration_table,
+	.load_image = efi_load_image,
+	.start_image = efi_start_image,
+	.exit = (void*)efi_exit,
+	.unload_image = efi_unload_image,
+	.exit_boot_services = efi_exit_boot_services,
+	.get_next_monotonic_count = efi_get_next_monotonic_count,
+	.stall = efi_stall,
+	.set_watchdog_timer = efi_set_watchdog_timer,
+	.connect_controller = efi_connect_controller,
+	.disconnect_controller = efi_disconnect_controller,
+	.open_protocol = efi_open_protocol,
+	.close_protocol = efi_close_protocol,
+	.open_protocol_information = efi_open_protocol_information,
+	.protocols_per_handle = efi_protocols_per_handle,
+	.locate_handle_buffer = efi_locate_handle_buffer,
+	.locate_protocol = efi_locate_protocol,
+	.install_multiple_protocol_interfaces = efi_install_multiple_protocol_interfaces,
+	.uninstall_multiple_protocol_interfaces = efi_uninstall_multiple_protocol_interfaces,
+	.calculate_crc32 = efi_calculate_crc32,
+	.copy_mem = efi_copy_mem,
+	.set_mem = efi_set_mem,
+};
+
+
+static uint16_t firmware_vendor[] =
+	{ 'D','a','s',' ','U','-','b','o','o','t',0 };
+
+struct efi_system_table systab = {
+	.hdr = {
+		.signature = EFI_SYSTEM_TABLE_SIGNATURE,
+		.revision = 0x20005, /* 2.5 */
+		.headersize = sizeof(struct efi_table_hdr),
+	},
+	.fw_vendor = (long)firmware_vendor,
+	.con_in = (void*)&efi_con_in,
+	.con_out = (void*)&efi_con_out,
+	.std_err = (void*)&efi_con_out,
+	.runtime = (void*)&efi_runtime_services,
+	.boottime = (void*)&efi_boot_services,
+	.nr_tables = 0,
+	.tables = (void*)efi_conf_table,
+};
Signature · 1 line
2.6.2

No further technical comments on this patch.

#12 lore raw

PATCH 4/16 efi_loader: Add boot time services

Quoted 106 lines · Leif Lindholm
Quoted 105 lines · Alexander Graf

When an EFI application runs, it has access to a few descriptor and callback tables to instruct the EFI compliant firmware to do things for it. The bulk of those interfaces are "boot time services". They handle all object management, and memory allocation.

This patch adds support for the boot time services and also exposes a system table, which is the point of entry descriptor table for EFI payloads.

---

v1 -> v2:

- Fix typo s/does now/does not/
- Add #ifdefs around header to allow inclusion when efi_loader is disabled
- Add stub efi_restore_gd() function when efi_loader is disabled
- Disable debug
- Mark runtime region as such
- Fix up memory map
- Allow efi_restore_gd to be called before first efi entry
- Add 32bit arm cache workaround
- Move memory map to separate patch
- Change BTS version to 2.5
- Fix return values for a few callbacks to more EFI compliant ones
- Change vendor to "Das U-Boot"
- Add warning when truncating timer trigger
- Move to GPLv2+

v2 -> v3:

- Use external efi_memory helpers
- Add EFIAPI to function prototypes
- Initialize event timer to -1ULL to prevent early firing
- Document header
- Move obj list to lib
- Remove implicit guid table
- Add guid compare function
- Fix return values
- Implement efi_wait_for_event
- Implement efi_install_configuration_table

---
 include/efi_loader.h          |  84 +++++
 lib/efi_loader/efi_boottime.c | 781 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 865 insertions(+)
 create mode 100644 lib/efi_loader/efi_boottime.c
include/efi_loader.h +42 -0
index 5618185..a7f033e 100644
@@ -6,15 +6,99 @@
  *  SPDX-License-Identifier:     GPL-2.0+
  */
 
+#include <common.h>
 #include <part_efi.h>
 #include <efi_api.h>
+
+#ifdef CONFIG_EFI_LOADER
+
 #include <linux/list.h>
 
+/* #define DEBUG_EFI */
+
+#ifdef DEBUG_EFI
+#define EFI_ENTRY(format, ...) do { \
+	efi_restore_gd(); \
+	printf("EFI: Entry %s(" format ")\n", __func__, ##__VA_ARGS__); \
+	} while(0)
+#else
+#define EFI_ENTRY(format, ...) do { \
+	efi_restore_gd(); \
+	} while(0)
+#endif
+
+#define EFI_EXIT(ret) efi_exit_func(ret);
+
+extern struct efi_system_table systab;
+
 extern const efi_guid_t efi_guid_device_path;
 extern const efi_guid_t efi_guid_loaded_image;
 
+/*
+ * While UEFI objects can have callbacks, you can also call functions on
+ * protocols (classes) themselves. This struct maps a protocol GUID to its
+ * interface (usually a struct with callback functions).
+ */
+struct efi_class_map {
+	const efi_guid_t *guid;
+	const void *interface;
+};
+
+/*
+ * When the UEFI payload wants to open a protocol on an object to get its
+ * interface (usually a struct with callback functions), this struct maps the
+ * protocol GUID to the respective protocol handler open function for that
+ * object protocol combination.
+ */
+struct efi_handler {
+	const efi_guid_t *guid;
+	efi_status_t (EFIAPI *open)(void *handle,
+			efi_guid_t *protocol, void **protocol_interface,
+			void *agent_handle, void *controller_handle,
Quoted 8 lines

include/efi_loader.h line 62

Quoted 5 lines
include/efi_loader.h +5 -0
+			uint32_t attributes);
+};
+
+/*
+ * UEFI has a poor man's OO model where one "object" can be polimorphic and have

Polymorphic.
Also, somewhat long lines in this comment block?

Exactly 80 characters long :).

Quoted 75 lines
Quoted 75 lines
include/efi_loader.h +37 -0
+ * multiple different protocols (classes) attached to it.
+ *
+ * This struct is the parent struct for all of our actual implementation objects
+ * that can include it to make themselves an EFI object
+ */
+struct efi_object {
+	/* Every UEFI object is part of a global object list */
+	struct list_head link;
+	/* We support up to 4 "protocols" an object can be accessed through */
+	struct efi_handler protocols[4];
+	/* The object spawner can either use this for data or as identifier */
+	void *handle;
+};
+
+/* This list contains all UEFI objects we know of */
+extern struct list_head efi_obj_list;
+
+/*
+ * Stub implementation for a protocol opener that just returns the handle as
+ * interface
+ */
 efi_status_t efi_return_handle(void *handle,
 		efi_guid_t *protocol, void **protocol_interface,
 		void *agent_handle, void *controller_handle,
 		uint32_t attributes);
+/* Called from places to check whether a timer expired */
+void efi_timer_check(void);
+/* PE loader implementation */
 void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info);
+/* Called once to store the pristine gd pointer */
+void efi_save_gd(void);
+/* Called from EFI_ENTRY on callback entry to put gd into the gd register */
+void efi_restore_gd(void);
+/* Called from EFI_EXIT on callback exit to restore the gd register */
+efi_status_t efi_exit_func(efi_status_t ret);
+
+#else /* defined(EFI_LOADER) */
+
+/* No loader configured, stub out EFI_ENTRY */
+static inline void efi_restore_gd(void) { }
+
+#endif
lib/efi_loader/efi_boottime.c new file +26 -0
new file mode 100644
index 0000000..e60fae9
@@ -0,0 +1,781 @@
+/*
+ *  EFI application boot time services
+ *
+ *  Copyright (c) 2016 Alexander Graf
+ *
+ *  SPDX-License-Identifier:     GPL-2.0+
+ */
+
+/* #define DEBUG_EFI */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <malloc.h>
+#include <asm/global_data.h>
+#include <libfdt_env.h>
+#include <u-boot/crc.h>
+#include <bootm.h>
+#include <inttypes.h>
+#include <watchdog.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* This list contains all the EFI objects our payload has access to */
+LIST_HEAD(efi_obj_list);
+
+/*
Quoted 8 lines

lib/efi_loader/efi_boottime.c line 32

Quoted 6 lines
lib/efi_loader/efi_boottime.c new file +6 -0
+ * If we're running on nasty systems (32bit ARM booting into non-EFI Linux)
+ * we need to do trickery with caches. Since we don't want to break the EFI
+ * aware boot path, only apply hacks when loading exiting directly (breaking
+ * direct Linux EFI booting along the way - oh well).
+ */
+static bool efi_is_direct_boot = true;

Is this a case worth supporting?

Which one? :)

Without this hack we can't run current Linux kernels. Without the check we break the grub2 -> Linux case with EFI enabled Linux.

Both are IMHO the two most important use cases. I want to have grub2 be able to boot today's kernels and tomorrows just as well.

Quoted 469 lines
Quoted 48 lines
lib/efi_loader/efi_boottime.c new file +46 -0
+
+/*
+ * EFI can pass arbitrary additional "tables" containing vendor specific
+ * information to the payload. One such table is the FDT table which contains
+ * a pointer to a flattened device tree blob.
+ *
+ * In most cases we want to pass an FDT to the payload, so reserve one slot of
+ * config table space for it. The pointer gets populated by do_bootefi_exec().
+ */
+static struct efi_configuration_table efi_conf_table[1];
+
+/*
+ * The "gd" pointer lives in a register on ARM and AArch64 that we declare
+ * fixed when compiling U-Boot. However, the payload does not know about that
+ * restriction so we need to manually swap its and our view of that register on
+ * EFI callback entry/exit.
+ */
+static volatile void *efi_gd, *app_gd;
+
+/* Called from do_bootefi_exec() */
+void efi_save_gd(void)
+{
+	efi_gd = gd;
+}
+
+/* Called on every callback entry */
+void efi_restore_gd(void)
+{
+	/* Only restore if we're already in EFI context */
+	if (!efi_gd)
+		return;
+
+	if (gd != efi_gd)
+		app_gd = gd;
+	gd = efi_gd;
+}
+
+/* Called on every callback exit */
+efi_status_t efi_exit_func(efi_status_t ret)
+{
+	gd = app_gd;
+	return ret;
+}
+
+static efi_status_t efi_unsupported(const char *funcname)
+{

lib/efi_loader/efi_boottime.c line 88

Quoted 10 lines
lib/efi_loader/efi_boottime.c new file +10 -0
+#ifdef DEBUG_EFI
+	printf("EFI: App called into unimplemented function %s\n", funcname);
+#endif
+	return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+static int guidcmp(const efi_guid_t *g1, const efi_guid_t *g2)
+{
+	return memcmp(g1, g2, sizeof(efi_guid_t));
+}

Not necessary for this set, but this could be useful as a generic function. (at least part_efi.c could be another consumer)

Quoted 407 lines
lib/efi_loader/efi_boottime.c new file +407 -0
+
+static unsigned long EFIAPI efi_raise_tpl(unsigned long new_tpl)
+{
+	EFI_ENTRY("0x%lx", new_tpl);
+	return EFI_EXIT(0);
+}
+
+static void EFIAPI efi_restore_tpl(unsigned long old_tpl)
+{
+	EFI_ENTRY("0x%lx", old_tpl);
+	EFI_EXIT(efi_unsupported(__func__));
+}
+
+efi_status_t EFIAPI efi_allocate_pages_ext(int type, int memory_type,
+					   unsigned long pages,
+					   uint64_t *memory)
+{
+	efi_status_t r;
+
+	EFI_ENTRY("%d, %d, 0x%lx, %p", type, memory_type, pages, memory);
+	r = efi_allocate_pages(type, memory_type, pages, memory);
+	return EFI_EXIT(r);
+}
+
+efi_status_t EFIAPI efi_free_pages_ext(uint64_t memory, unsigned long pages)
+{
+	efi_status_t r;
+
+	EFI_ENTRY("%"PRIx64", 0x%lx", memory, pages);
+	r = efi_free_pages(memory, pages);
+	return EFI_EXIT(r);
+}
+
+efi_status_t EFIAPI efi_get_memory_map_ext(unsigned long *memory_map_size,
+					   struct efi_mem_desc *memory_map,
+					   unsigned long *map_key,
+					   unsigned long *descriptor_size,
+					   uint32_t *descriptor_version)
+{
+	efi_status_t r;
+
+	EFI_ENTRY("%p, %p, %p, %p, %p", memory_map_size, memory_map,
+		  map_key, descriptor_size, descriptor_version);
+	r = efi_get_memory_map(memory_map_size, memory_map, map_key,
+			       descriptor_size, descriptor_version);
+	return EFI_EXIT(r);
+}
+
+static efi_status_t EFIAPI efi_allocate_pool(int pool_type, unsigned long size,
+					     void **buffer)
+{
+	return efi_allocate_pages(0, pool_type, (size + 0xfff) >> 12, (void*)buffer);
+}
+
+static efi_status_t EFIAPI efi_free_pool(void *buffer)
+{
+	return efi_free_pages((ulong)buffer, 0);
+}
+
+/*
+ * Our event capabilities are very limited. Only support a single
+ * event to exist, so we don't need to maintain lists.
+ */
+static struct {
+	enum efi_event_type type;
+	u32 trigger_type;
+	u32 trigger_time;
+	u64 trigger_next;
+	unsigned long notify_tpl;
+	void (*notify_function) (void *event, void *context);
+	void *notify_context;
+} efi_event = {
+	/* Disable timers on bootup */
+	.trigger_next = -1ULL,
+};
+
+static efi_status_t EFIAPI efi_create_event(
+			enum efi_event_type type, ulong notify_tpl,
+			void (*notify_function) (void *event, void *context),
+			void *notify_context, void **event)
+{
+	EFI_ENTRY("%d, 0x%lx, %p, %p", type, notify_tpl, notify_function,
+		  notify_context);
+	if (efi_event.notify_function) {
+		/* We only support one event at a time */
+		return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+	}
+
+	efi_event.type = type;
+	efi_event.notify_tpl = notify_tpl;
+	efi_event.notify_function = notify_function;
+	efi_event.notify_context = notify_context;
+	*event = &efi_event;
+
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+/*
+ * Our timers have to work without interrupts, so we check whenever keyboard
+ * input or disk accesses happen if enough time elapsed for it to fire.
+ */
+void efi_timer_check(void)
+{
+	u64 now = timer_get_us();
+
+	if (now >= efi_event.trigger_next) {
+		/* Triggering! */
+		if (efi_event.trigger_type == EFI_TIMER_PERIODIC)
+			efi_event.trigger_next += efi_event.trigger_time / 10;
+		efi_event.notify_function(&efi_event, efi_event.notify_context);
+	}
+
+	WATCHDOG_RESET();
+}
+
+static efi_status_t EFIAPI efi_set_timer(void *event, int type,
+					 uint64_t trigger_time)
+{
+	/* We don't have 64bit division available everywhere, so limit timer
+	 * distances to 32bit bits. */
+	u32 trigger32 = trigger_time;
+
+	EFI_ENTRY("%p, %d, %"PRIx64, event, type, trigger_time);
+
+	if (trigger32 < trigger_time) {
+		printf("WARNING: Truncating timer from %"PRIx64" to %x\n",
+		       trigger_time, trigger32);
+	}
+
+	if (event != &efi_event) {
+		/* We only support one event at a time */
+		return EFI_EXIT(EFI_INVALID_PARAMETER);
+	}
+
+	switch (type) {
+	case EFI_TIMER_STOP:
+		efi_event.trigger_next = -1ULL;
+		break;
+	case EFI_TIMER_PERIODIC:
+	case EFI_TIMER_RELATIVE:
+		efi_event.trigger_next = timer_get_us() + (trigger32 / 10);
+		break;
+	default:
+		return EFI_EXIT(EFI_INVALID_PARAMETER);
+	}
+	efi_event.trigger_type = type;
+	efi_event.trigger_time = trigger_time;
+
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_wait_for_event(unsigned long num_events,
+					      void *event, unsigned long *index)
+{
+	u64 now;
+
+	EFI_ENTRY("%ld, %p, %p", num_events, event, index);
+
+	now = timer_get_us();
+	while (now < efi_event.trigger_next) { }
+	efi_timer_check();
+
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_signal_event(void *event)
+{
+	EFI_ENTRY("%p", event);
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_close_event(void *event)
+{
+	EFI_ENTRY("%p", event);
+	efi_event.trigger_next = -1ULL;
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_check_event(void *event)
+{
+	EFI_ENTRY("%p", event);
+	return EFI_EXIT(EFI_NOT_READY);
+}
+
+static efi_status_t EFIAPI efi_install_protocol_interface(void **handle,
+			efi_guid_t *protocol, int protocol_interface_type,
+			void *protocol_interface)
+{
+	EFI_ENTRY("%p, %p, %d, %p", handle, protocol, protocol_interface_type,
+		  protocol_interface);
+	return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+}
+static efi_status_t EFIAPI efi_reinstall_protocol_interface(void *handle,
+			efi_guid_t *protocol, void *old_interface,
+			void *new_interface)
+{
+	EFI_ENTRY("%p, %p, %p, %p", handle, protocol, old_interface,
+		  new_interface);
+	return EFI_EXIT(EFI_ACCESS_DENIED);
+}
+
+static efi_status_t EFIAPI efi_uninstall_protocol_interface(void *handle,
+			efi_guid_t *protocol, void *protocol_interface)
+{
+	EFI_ENTRY("%p, %p, %p", handle, protocol, protocol_interface);
+	return EFI_EXIT(EFI_NOT_FOUND);
+}
+
+static efi_status_t EFIAPI efi_register_protocol_notify(efi_guid_t *protocol,
+							void *event,
+							void **registration)
+{
+	EFI_ENTRY("%p, %p, %p", protocol, event, registration);
+	return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+}
+
+static int efi_search(enum efi_locate_search_type search_type,
+		      efi_guid_t *protocol, void *search_key,
+		      struct efi_object *efiobj)
+{
+	int i;
+
+	switch (search_type) {
+	case all_handles:
+		return 0;
+	case by_register_notify:
+		return -1;
+	case by_protocol:
+		for (i = 0; i < ARRAY_SIZE(efiobj->protocols); i++) {
+			const efi_guid_t *guid = efiobj->protocols[i].guid;
+			if (guid && !guidcmp(guid, protocol))
+				return 0;
+		}
+		return -1;
+	}
+
+	return -1;
+}
+
+static efi_status_t EFIAPI efi_locate_handle(
+			enum efi_locate_search_type search_type,
+			efi_guid_t *protocol, void *search_key,
+			unsigned long *buffer_size, efi_handle_t *buffer)
+{
+	struct list_head *lhandle;
+	unsigned long size = 0;
+
+	EFI_ENTRY("%d, %p, %p, %p, %p", search_type, protocol, search_key,
+		  buffer_size, buffer);
+
+	/* Count how much space we need */
+	list_for_each(lhandle, &efi_obj_list) {
+		struct efi_object *efiobj;
+		efiobj = list_entry(lhandle, struct efi_object, link);
+		if (!efi_search(search_type, protocol, search_key, efiobj)) {
+			size += sizeof(void*);
+		}
+	}
+
+	if (*buffer_size < size) {
+		*buffer_size = size;
+		return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
+	}
+
+	/* Then fill the array */
+	list_for_each(lhandle, &efi_obj_list) {
+		struct efi_object *efiobj;
+		efiobj = list_entry(lhandle, struct efi_object, link);
+		if (!efi_search(search_type, protocol, search_key, efiobj)) {
+			*(buffer++) = efiobj->handle;
+		}
+	}
+
+	*buffer_size = size;
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_locate_device_path(efi_guid_t *protocol,
+			struct efi_device_path **device_path,
+			efi_handle_t *device)
+{
+	EFI_ENTRY("%p, %p, %p", protocol, device_path, device);
+	return EFI_EXIT(EFI_NOT_FOUND);
+}
+
+static efi_status_t EFIAPI efi_install_configuration_table(efi_guid_t *guid,
+							   void *table)
+{
+	int i;
+
+	EFI_ENTRY("%p, %p", guid, table);
+
+	/* Check for guid override */
+	for (i = 0; i < systab.nr_tables; i++) {
+		if (!guidcmp(guid, &efi_conf_table[i].guid)) {
+			efi_conf_table[i].table = table;
+			return EFI_EXIT(EFI_SUCCESS);
+		}
+	}
+
+	/* No override, check for overflow */
+	if (i >= ARRAY_SIZE(efi_conf_table))
+		return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+
+	/* Add a new entry */
+	memcpy(&efi_conf_table[i].guid, guid, sizeof(*guid));
+	efi_conf_table[i].table = table;
+	systab.nr_tables = i;
+
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_load_image(bool boot_policy,
+					  efi_handle_t parent_image,
+					  struct efi_device_path *file_path,
+					  void *source_buffer,
+					  unsigned long source_size,
+					  efi_handle_t *image_handle)
+{
+	static struct efi_object loaded_image_info_obj = {
+		.protocols = {
+			{
+				.guid = &efi_guid_loaded_image,
+				.open = &efi_return_handle,
+			},
+		},
+	};
+	struct efi_loaded_image *info;
+	struct efi_object *obj;
+
+	EFI_ENTRY("%d, %p, %p, %p, %ld, %p", boot_policy, parent_image,
+		  file_path, source_buffer, source_size, image_handle);
+	info = malloc(sizeof(*info));
+	obj = malloc(sizeof(loaded_image_info_obj));
+	memset(info, 0, sizeof(*info));
+	memcpy(obj, &loaded_image_info_obj, sizeof(loaded_image_info_obj));
+	obj->handle = info;
+	info->file_path = file_path;
+	info->reserved = efi_load_pe(source_buffer, info);
+	if (!info->reserved) {
+		free(info);
+		free(obj);
+		return EFI_EXIT(EFI_UNSUPPORTED);
+	}
+
+	*image_handle = info;
+	list_add_tail(&obj->link, &efi_obj_list);
+
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
+					   unsigned long *exit_data_size,
+					   s16 **exit_data)
+{
+	ulong (*entry)(void *image_handle, struct efi_system_table *st);
+	struct efi_loaded_image *info = image_handle;
+
+	EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data);
+	entry = info->reserved;
+
+	efi_is_direct_boot = false;
+
+	/* call the image! */
+	entry(image_handle, &systab);
+
+	/* Should usually never get here */
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_exit(void *image_handle, long exit_status,
+				    unsigned long exit_data_size,
+				    uint16_t *exit_data)
+{
+	EFI_ENTRY("%p, %ld, %ld, %p", image_handle, exit_status,
+		  exit_data_size, exit_data);
+	return EFI_EXIT(efi_unsupported(__func__));
+}
+
+static struct efi_object *efi_search_obj(void *handle)
+{
+	struct list_head *lhandle;
+
+	list_for_each(lhandle, &efi_obj_list) {
+		struct efi_object *efiobj;
+		efiobj = list_entry(lhandle, struct efi_object, link);
+		if (efiobj->handle == handle)
+			return efiobj;
+	}
+
+	return NULL;
+}
+
+static efi_status_t EFIAPI efi_unload_image(void *image_handle)
+{
+	struct efi_object *efiobj;
+
+	EFI_ENTRY("%p", image_handle);
+	efiobj = efi_search_obj(image_handle);
+	if (efiobj)
+		list_del(&efiobj->link);
+
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static void efi_exit_caches(void)
+{
Quoted 8 lines

lib/efi_loader/efi_boottime.c line 500

Quoted 5 lines
lib/efi_loader/efi_boottime.c new file +5 -0
+#if defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
+	/*
+	 * Grub on 32bit ARM needs to have caches disabled before jumping into
+	 * a zImage, but does not know of all cache layers. Give it a hand.
+	 */

Should we even be doing this hack?
EFI stub support for 32-bit ARM is now (finally) upstream.

See above :). We can talk about removing the hack a few years down the road, when all kernels out in the wild have an efi stub.

Alex

#13 lore raw

PATCH 4/16 efi_loader: Add boot time services

In reply to Alexander Graf
Quoted 42 lines · Alexander Graf

When an EFI application runs, it has access to a few descriptor and callback tables to instruct the EFI compliant firmware to do things for it. The bulk of those interfaces are "boot time services". They handle all object management, and memory allocation.

This patch adds support for the boot time services and also exposes a system table, which is the point of entry descriptor table for EFI payloads.

---

v1 -> v2:

- Fix typo s/does now/does not/
- Add #ifdefs around header to allow inclusion when efi_loader is disabled
- Add stub efi_restore_gd() function when efi_loader is disabled
- Disable debug
- Mark runtime region as such
- Fix up memory map
- Allow efi_restore_gd to be called before first efi entry
- Add 32bit arm cache workaround
- Move memory map to separate patch
- Change BTS version to 2.5
- Fix return values for a few callbacks to more EFI compliant ones
- Change vendor to "Das U-Boot"
- Add warning when truncating timer trigger
- Move to GPLv2+

v2 -> v3:

- Use external efi_memory helpers
- Add EFIAPI to function prototypes
- Initialize event timer to -1ULL to prevent early firing
- Document header
- Move obj list to lib
- Remove implicit guid table
- Add guid compare function
- Fix return values
- Implement efi_wait_for_event
- Implement efi_install_configuration_table

Quoted 5 lines
---
 include/efi_loader.h          |  84 +++++
 lib/efi_loader/efi_boottime.c | 781 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 865 insertions(+)
 create mode 100644 lib/efi_loader/efi_boottime.c

Tested on Beaglebone Black and Minnowboard MAX:

#14 lore raw

PATCH 5/16 efi_loader: Add console interface

In reply to Alexander Graf

One of the basic EFI interfaces is the console interface. Using it an EFI application can interface with the user. This patch implements an EFI console interface using getc() and putc().

Today, we only implement text based consoles. We also convert the EFI Unicode characters to UTF-8 on the fly, hoping that everyone managed to jump on the train by now.

---

v1 -> v2:

- Move to GPLv2+

v2 -> v3:

- Add EFIAPI to function prototypes

---
 include/efi_loader.h         |   5 +
 lib/efi_loader/efi_console.c | 360 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 365 insertions(+)
 create mode 100644 lib/efi_loader/efi_console.c
include/efi_loader.h +5 -0
index a7f033e..46cd714 100644
@@ -31,6 +31,11 @@
 
 extern struct efi_system_table systab;
 
+extern const struct efi_simple_text_output_protocol efi_con_out;
+extern const struct efi_simple_input_interface efi_con_in;
+extern const struct efi_console_control_protocol efi_console_control;
+
+extern const efi_guid_t efi_guid_console_control;
 extern const efi_guid_t efi_guid_device_path;
 extern const efi_guid_t efi_guid_loaded_image;
 
lib/efi_loader/efi_console.c new file +360 -0

360 lines not on this page · raw message

Signature · 1 line
2.6.2
#15 lore raw

PATCH 5/16 efi_loader: Add console interface

Quoted 17 lines · Alexander Graf

One of the basic EFI interfaces is the console interface. Using it an EFI application can interface with the user. This patch implements an EFI console interface using getc() and putc().

Today, we only implement text based consoles. We also convert the EFI Unicode characters to UTF-8 on the fly, hoping that everyone managed to jump on the train by now.

---

v1 -> v2:

- Move to GPLv2+

Quoted 8 lines

v2 -> v3:

- Add EFIAPI to function prototypes

---
 include/efi_loader.h         |   5 +
 lib/efi_loader/efi_console.c | 360 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 365 insertions(+)
 create mode 100644 lib/efi_loader/efi_console.c

Tested on Beaglebone Black and Minnowboard MAX:

I note that cout_enable_cursor() seems to be implemented but returns unimplemented. Is that right?

- Simon

#16 lore raw

PATCH 6/16 efi_loader: Add runtime services

In reply to Alexander Graf

After booting has finished, EFI allows firmware to still interact with the OS using the "runtime services". These callbacks live in a separate address space, since they are available long after U-Boot has been overwritten by the OS.

This patch adds enough framework for arbitrary code inside of U-Boot to become a runtime service with the right section attributes set. For now, we don't make use of it yet though.

We could maybe in the future map U-boot environment variables to EFI variables here.

---

v1 -> v2:

- Fix runtime service sections
- Add runtime detach
- Enable runtime relocations
- Add get_time
- Fix relocation
- Fix 32bit
- Add am335x support
- Move section definition to header
- Add systab to runtime section
- Add self-relocation hook table
- Fix self-relocation
- Relocate efi_runtime section early during bootup
- Fix return values for a number of callbacks to be more UEFI compliant
- Move to GPLv2+

v2 -> v3:

- Patch reset to NULL
- Add EFIAPI to function prototypes
- Document header
- Add dm.h include
- Remove non-dm rtc support
- Return DEVICE_ERROR in rtc path

---
 arch/arm/config.mk            |   4 +
 arch/arm/cpu/armv8/u-boot.lds |  16 +++
 arch/arm/cpu/u-boot.lds       |  30 +++++
 arch/arm/lib/sections.c       |   4 +
 board/ti/am335x/u-boot.lds    |  30 +++++
 common/board_r.c              |   4 +
 include/efi_loader.h          |  17 +++
 lib/efi_loader/efi_boottime.c |   6 +-
 lib/efi_loader/efi_runtime.c  | 290 ++++++++++++++++++++++++++++++++++++++++++
 9 files changed, 398 insertions(+), 3 deletions(-)
 create mode 100644 lib/efi_loader/efi_runtime.c
arch/arm/config.mk +4 -0
index a3e14a8..99c5187 100644
@@ -121,6 +121,10 @@ ifdef CONFIG_OF_EMBED
 OBJCOPYFLAGS += -j .dtb.init.rodata
 endif
 
+ifdef CONFIG_EFI_LOADER
+OBJCOPYFLAGS += -j .efi_runtime -j .efi_runtime_rel
+endif
+
 ifneq ($(CONFIG_IMX_CONFIG),)
 ifdef CONFIG_SPL
 ifndef CONFIG_SPL_BUILD
arch/arm/cpu/armv8/u-boot.lds +16 -0
index 4c12222..fd15ad5 100644
@@ -42,6 +42,22 @@ SECTIONS
 
 	. = ALIGN(8);
 
+	.efi_runtime : {
+                __efi_runtime_start = .;
+		*(efi_runtime_text)
+		*(efi_runtime_data)
+                __efi_runtime_stop = .;
+	}
+
+	.efi_runtime_rel : {
+                __efi_runtime_rel_start = .;
+		*(.relaefi_runtime_text)
+		*(.relaefi_runtime_data)
+                __efi_runtime_rel_stop = .;
+	}
+
+	. = ALIGN(8);
+
 	.image_copy_end :
 	{
 		*(.__image_copy_end)
arch/arm/cpu/u-boot.lds +30 -0
index d48a905..596a17d 100644
@@ -89,6 +89,36 @@ SECTIONS
 
 	. = ALIGN(4);
 
+	.__efi_runtime_start : {
+		*(.__efi_runtime_start)
+	}
+
+	.efi_runtime : {
+		*(efi_runtime_text)
+		*(efi_runtime_data)
+	}
+
+	.__efi_runtime_stop : {
+		*(.__efi_runtime_stop)
+	}
+
+	.efi_runtime_rel_start :
+	{
+		*(.__efi_runtime_rel_start)
+	}
+
+	.efi_runtime_rel : {
+		*(.relefi_runtime_text)
+		*(.relefi_runtime_data)
+	}
+
+	.efi_runtime_rel_stop :
+	{
+		*(.__efi_runtime_rel_stop)
+	}
+
+	. = ALIGN(4);
+
 	.image_copy_end :
 	{
 		*(.__image_copy_end)
arch/arm/lib/sections.c +4 -0
index a1205c3..6a94522 100644
@@ -27,4 +27,8 @@ char __rel_dyn_start[0] __attribute__((section(".__rel_dyn_start")));
 char __rel_dyn_end[0] __attribute__((section(".__rel_dyn_end")));
 char __secure_start[0] __attribute__((section(".__secure_start")));
 char __secure_end[0] __attribute__((section(".__secure_end")));
+char __efi_runtime_start[0] __attribute__((section(".__efi_runtime_start")));
+char __efi_runtime_stop[0] __attribute__((section(".__efi_runtime_stop")));
+char __efi_runtime_rel_start[0] __attribute__((section(".__efi_runtime_rel_start")));
+char __efi_runtime_rel_stop[0] __attribute__((section(".__efi_runtime_rel_stop")));
 char _end[0] __attribute__((section(".__end")));
board/ti/am335x/u-boot.lds +30 -0
index 78f294a..a56cc82 100644
@@ -59,6 +59,36 @@ SECTIONS
 
 	. = ALIGN(4);
 
+	.__efi_runtime_start : {
+		*(.__efi_runtime_start)
+	}
+
+	.efi_runtime : {
+		*(efi_runtime_text)
+		*(efi_runtime_data)
+	}
+
+	.__efi_runtime_stop : {
+		*(.__efi_runtime_stop)
+	}
+
+	.efi_runtime_rel_start :
+	{
+		*(.__efi_runtime_rel_start)
+	}
+
+	.efi_runtime_rel : {
+		*(.relefi_runtime_text)
+		*(.relefi_runtime_data)
+	}
+
+	.efi_runtime_rel_stop :
+	{
+		*(.__efi_runtime_rel_stop)
+	}
+
+	. = ALIGN(4);
+
 	.image_copy_end :
 	{
 		*(.__image_copy_end)
common/board_r.c +4 -0
index 75ee43e..420e2c8 100644
@@ -64,6 +64,7 @@
 #ifdef CONFIG_AVR32
 #include <asm/arch/mmu.h>
 #endif
+#include <efi_loader.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -176,6 +177,9 @@ static int initr_reloc_global_data(void)
 	*/
 	gd->fdt_blob += gd->reloc_off;
 #endif
+#ifdef CONFIG_EFI_LOADER
+	efi_runtime_relocate(gd->relocaddr, NULL);
+#endif
 
 	return 0;
 }
include/efi_loader.h +17 -0
index 46cd714..640e703 100644
@@ -29,6 +29,7 @@
 
 #define EFI_EXIT(ret) efi_exit_func(ret);
 
+extern struct efi_runtime_services efi_runtime_services;
 extern struct efi_system_table systab;
 
 extern const struct efi_simple_text_output_protocol efi_con_out;
@@ -39,6 +40,9 @@ extern const efi_guid_t efi_guid_console_control;
 extern const efi_guid_t efi_guid_device_path;
 extern const efi_guid_t efi_guid_loaded_image;
 
+extern unsigned int __efi_runtime_start, __efi_runtime_stop;
+extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
+
 /*
  * While UEFI objects can have callbacks, you can also call functions on
  * protocols (classes) themselves. This struct maps a protocol GUID to its
@@ -100,9 +104,22 @@ void efi_save_gd(void);
 void efi_restore_gd(void);
 /* Called from EFI_EXIT on callback exit to restore the gd register */
 efi_status_t efi_exit_func(efi_status_t ret);
+/* Call this to relocate the runtime section to an address space */
+void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map);
+
+/*
+ * Use these to indicate that your code / data should go into the EFI runtime
+ * section and thus still be available when the OS is running
+ */
+#define EFI_RUNTIME_DATA __attribute__ ((section ("efi_runtime_data")))
+#define EFI_RUNTIME_TEXT __attribute__ ((section ("efi_runtime_text")))
 
 #else /* defined(EFI_LOADER) */
 
+/* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */
+#define EFI_RUNTIME_DATA
+#define EFI_RUNTIME_TEXT
+
 /* No loader configured, stub out EFI_ENTRY */
 static inline void efi_restore_gd(void) { }
 
lib/efi_loader/efi_boottime.c +3 -3
index e60fae9..87400de 100644
@@ -39,7 +39,7 @@ static bool efi_is_direct_boot = true;
  * In most cases we want to pass an FDT to the payload, so reserve one slot of
  * config table space for it. The pointer gets populated by do_bootefi_exec().
  */
-static struct efi_configuration_table efi_conf_table[1];
+static struct efi_configuration_table EFI_RUNTIME_DATA efi_conf_table[1];
 
 /*
  * The "gd" pointer lives in a register on ARM and AArch64 that we declare
@@ -761,10 +761,10 @@ static const struct efi_boot_services efi_boot_services = {
 };
 
 
-static uint16_t firmware_vendor[] =
+static uint16_t EFI_RUNTIME_DATA firmware_vendor[] =
 	{ 'D','a','s',' ','U','-','b','o','o','t',0 };
 
-struct efi_system_table systab = {
+struct efi_system_table EFI_RUNTIME_DATA systab = {
 	.hdr = {
 		.signature = EFI_SYSTEM_TABLE_SIGNATURE,
 		.revision = 0x20005, /* 2.5 */
lib/efi_loader/efi_runtime.c new file +290 -0

290 lines not on this page · raw message

Signature · 1 line
2.6.2
#17 lore raw

PATCH 6/16 efi_loader: Add runtime services

Quoted 40 lines · Alexander Graf

After booting has finished, EFI allows firmware to still interact with the OS using the "runtime services". These callbacks live in a separate address space, since they are available long after U-Boot has been overwritten by the OS.

This patch adds enough framework for arbitrary code inside of U-Boot to become a runtime service with the right section attributes set. For now, we don't make use of it yet though.

We could maybe in the future map U-boot environment variables to EFI variables here.

---

v1 -> v2:

- Fix runtime service sections
- Add runtime detach
- Enable runtime relocations
- Add get_time
- Fix relocation
- Fix 32bit
- Add am335x support
- Move section definition to header
- Add systab to runtime section
- Add self-relocation hook table
- Fix self-relocation
- Relocate efi_runtime section early during bootup
- Fix return values for a number of callbacks to be more UEFI compliant
- Move to GPLv2+

v2 -> v3:

- Patch reset to NULL
- Add EFIAPI to function prototypes
- Document header
- Add dm.h include
- Remove non-dm rtc support
- Return DEVICE_ERROR in rtc path

Quoted 12 lines
---
 arch/arm/config.mk            |   4 +
 arch/arm/cpu/armv8/u-boot.lds |  16 +++
 arch/arm/cpu/u-boot.lds       |  30 +++++
 arch/arm/lib/sections.c       |   4 +
 board/ti/am335x/u-boot.lds    |  30 +++++
 common/board_r.c              |   4 +
 include/efi_loader.h          |  17 +++
 lib/efi_loader/efi_boottime.c |   6 +-
 lib/efi_loader/efi_runtime.c  | 290 ++++++++++++++++++++++++++++++++++++++++++
 9 files changed, 398 insertions(+), 3 deletions(-)
 create mode 100644 lib/efi_loader/efi_runtime.c

Tested on Beaglebone Black and Minnowboard MAX:

#18 lore raw

PATCH 7/16 efi_loader: Add disk interfaces

In reply to Alexander Graf

A EFI applications usually want to access storage devices to load data from.

This patch adds support for EFI disk interfaces. It loops through all block storage interfaces known to U-Boot and creates an EFI object for each existing one. EFI applications can then through these objects call U-Boot's read and write functions.

---

v1 -> v2:

- Move to block_drvr array
- Move to GPLv2+
- Fix header order
- Document efi block object struct
- Use calloc rather than malloc & memset

v2 -> v3:

- Adapt to newer u-boot block API
- Add EFIAPI to function prototypes
- Document header
- Check for DEV_TYPE_UNKNOWN
- Document 16byte limit for dp string

---
 include/efi_loader.h      |   2 +
 lib/efi_loader/efi_disk.c | 218 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 220 insertions(+)
 create mode 100644 lib/efi_loader/efi_disk.c
include/efi_loader.h +2 -0
index 640e703..91ab6cb 100644
@@ -86,6 +86,8 @@ struct efi_object {
 /* This list contains all UEFI objects we know of */
 extern struct list_head efi_obj_list;
 
+/* Called by bootefi to make all disk storage accessible as EFI objects */
+int efi_disk_register(void);
 /*
  * Stub implementation for a protocol opener that just returns the handle as
  * interface
lib/efi_loader/efi_disk.c new file +218 -0

218 lines not on this page · raw message

Signature · 1 line
2.6.2
#19 lore raw

PATCH 7/16 efi_loader: Add disk interfaces

Quoted 27 lines · Alexander Graf

A EFI applications usually want to access storage devices to load data from.

This patch adds support for EFI disk interfaces. It loops through all block storage interfaces known to U-Boot and creates an EFI object for each existing one. EFI applications can then through these objects call U-Boot's read and write functions.

---

v1 -> v2:

- Move to block_drvr array
- Move to GPLv2+
- Fix header order
- Document efi block object struct
- Use calloc rather than malloc & memset

v2 -> v3:

- Adapt to newer u-boot block API
- Add EFIAPI to function prototypes
- Document header
- Check for DEV_TYPE_UNKNOWN
- Document 16byte limit for dp string

Quoted 5 lines
---
 include/efi_loader.h      |   2 +
 lib/efi_loader/efi_disk.c | 218 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 220 insertions(+)
 create mode 100644 lib/efi_loader/efi_disk.c

Tested on Beaglebone Black and Minnowboard MAX:

#20 lore raw

PATCH 8/16 efi_loader: Add "bootefi" command

In reply to Alexander Graf

In order to execute an EFI application, we need to bridge the gap between U-Boot's notion of executing images and EFI's notion of doing the same.

The best path forward IMHO here is to stick completely to the way U-Boot deals with payloads. You manually load them using whatever method to RAM and then have a simple boot command to execute them. So in our case, you would do

  # load mmc 0:1 $loadaddr grub.efi
  # bootefi $loadaddr

which then gets you into a grub shell. Fdt information known to U-boot via the fdt addr command is also passed to the EFI payload.

---

v1 -> v2:

- Move to GPLv2+

v2 -> v3:

- Move to new cmd directory
- Add kconfig option
- Fix comment style
- Add help text
- s/-1/-ENOENT
- Move obj list to lib

XXX bootefi: make dtb conf table more explicit

---
 cmd/Kconfig   |   7 +++
 cmd/Makefile  |   1 +
 cmd/bootefi.c | 167 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 175 insertions(+)
 create mode 100644 cmd/bootefi.c
cmd/Kconfig +7 -0
index 2ed0263..7cdff04 100644
@@ -148,6 +148,13 @@ config CMD_BOOTM
 	help
 	  Boot an application image from the memory.
 
+config CMD_BOOTEFI
+	bool "bootefi"
+	depends on EFI_LOADER
+	default y
+	help
+	  Boot an EFI image from memory.
+
 config CMD_ELF
 	bool "bootelf, bootvx"
 	default y
cmd/Makefile +1 -0
index 03f7e0a..7604621 100644
@@ -21,6 +21,7 @@ obj-$(CONFIG_CMD_SOURCE) += source.o
 obj-$(CONFIG_CMD_BDI) += bdinfo.o
 obj-$(CONFIG_CMD_BEDBUG) += bedbug.o
 obj-$(CONFIG_CMD_BMP) += bmp.o
+obj-$(CONFIG_CMD_BOOTEFI) += bootefi.o
 obj-$(CONFIG_CMD_BOOTMENU) += bootmenu.o
 obj-$(CONFIG_CMD_BOOTLDR) += bootldr.o
 obj-$(CONFIG_CMD_BOOTSTAGE) += bootstage.o
cmd/bootefi.c new file +167 -0

167 lines not on this page · raw message

Signature · 1 line
2.6.2
#21 lore raw

PATCH 8/16 efi_loader: Add "bootefi" command

Quoted 32 lines · Alexander Graf

In order to execute an EFI application, we need to bridge the gap between U-Boot's notion of executing images and EFI's notion of doing the same.

The best path forward IMHO here is to stick completely to the way U-Boot deals with payloads. You manually load them using whatever method to RAM and then have a simple boot command to execute them. So in our case, you would do

  # load mmc 0:1 $loadaddr grub.efi
  # bootefi $loadaddr

which then gets you into a grub shell. Fdt information known to U-boot via the fdt addr command is also passed to the EFI payload.

---

v1 -> v2:

- Move to GPLv2+

v2 -> v3:

- Move to new cmd directory
- Add kconfig option
- Fix comment style
- Add help text
- s/-1/-ENOENT
- Move obj list to lib

Quoted 7 lines

XXX bootefi: make dtb conf table more explicit

---
 cmd/Kconfig   |   7 +++
 cmd/Makefile  |   1 +
 cmd/bootefi.c | 167 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 175 insertions(+)
 create mode 100644 cmd/bootefi.c

Tested on Beaglebone Black and Minnowboard MAX:

#22 lore raw

PATCH 9/16 efi_loader: Implement memory allocation and map

In reply to Alexander Graf

The EFI loader needs to maintain views of memory - general system memory windows as well as used locations inside those and potential runtime service MMIO windows.

To manage all of these, add a few helpers that maintain an internal representation of the map the similar to how the EFI API later on reports it to the application.

For allocations, the scheme is very simple. We basically allow allocations to replace chunks of previously done maps, so that a new LOADER_DATA allocation for example can remove a piece of the RAM map. When no specific address is given, we just take the highest possible address in the lowest RAM map that fits the allocation size.

---

v2 -> v3:

- Rewrite memory allocation and map
- Document header

---
 common/board_r.c            |   3 +
 include/efi_loader.h        |  19 +++
 lib/efi_loader/efi_memory.c | 314 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 336 insertions(+)
 create mode 100644 lib/efi_loader/efi_memory.c
common/board_r.c +3 -0
index 420e2c8..2ed8e4b 100644
@@ -785,6 +785,9 @@ init_fnc_t init_sequence_r[] = {
 #ifdef CONFIG_CLOCKS
 	set_cpu_clk_info, /* Setup clock information */
 #endif
+#ifdef CONFIG_EFI_LOADER
+	efi_memory_init,
+#endif
 	stdio_init_tables,
 	initr_serial,
 	initr_announce,
include/efi_loader.h +19 -0
index 91ab6cb..e38be12 100644
@@ -109,6 +109,25 @@ efi_status_t efi_exit_func(efi_status_t ret);
 /* Call this to relocate the runtime section to an address space */
 void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map);
 
+/* Generic EFI memory allocator, call this to get memory */
+void *efi_alloc(uint64_t len, int memory_type);
+/* More specific EFI memory allocator, called by EFI payloads */
+efi_status_t efi_allocate_pages(int type, int memory_type, unsigned long pages,
+				uint64_t *memory);
+/* EFI memory free function. Not implemented today */
+efi_status_t efi_free_pages(uint64_t memory, unsigned long pages);
+/* Returns the EFI memory map */
+efi_status_t efi_get_memory_map(unsigned long *memory_map_size,
+				struct efi_mem_desc *memory_map,
+				unsigned long *map_key,
+				unsigned long *descriptor_size,
+				uint32_t *descriptor_version);
+/* Adds a range into the EFI memory map */
+uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type,
+			    bool overlap_only_ram);
+/* Called by board init to initialize the EFI memory map */
+int efi_memory_init(void);
+
 /*
  * Use these to indicate that your code / data should go into the EFI runtime
  * section and thus still be available when the OS is running
lib/efi_loader/efi_memory.c new file +314 -0

314 lines not on this page · raw message

Signature · 1 line
2.6.2
#23 lore raw

PATCH 9/16 efi_loader: Implement memory allocation and map

Quoted 113 lines · Alexander Graf

The EFI loader needs to maintain views of memory - general system memory windows as well as used locations inside those and potential runtime service MMIO windows.

To manage all of these, add a few helpers that maintain an internal representation of the map the similar to how the EFI API later on reports it to the application.

For allocations, the scheme is very simple. We basically allow allocations to replace chunks of previously done maps, so that a new LOADER_DATA allocation for example can remove a piece of the RAM map. When no specific address is given, we just take the highest possible address in the lowest RAM map that fits the allocation size.

---

v2 -> v3:

- Rewrite memory allocation and map
- Document header

---
 common/board_r.c            |   3 +
 include/efi_loader.h        |  19 +++
 lib/efi_loader/efi_memory.c | 314 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 336 insertions(+)
 create mode 100644 lib/efi_loader/efi_memory.c
common/board_r.c +3 -0
index 420e2c8..2ed8e4b 100644
@@ -785,6 +785,9 @@ init_fnc_t init_sequence_r[] = {
 #ifdef CONFIG_CLOCKS
 	set_cpu_clk_info, /* Setup clock information */
 #endif
+#ifdef CONFIG_EFI_LOADER
+	efi_memory_init,
+#endif
 	stdio_init_tables,
 	initr_serial,
 	initr_announce,
include/efi_loader.h +19 -0
index 91ab6cb..e38be12 100644
@@ -109,6 +109,25 @@ efi_status_t efi_exit_func(efi_status_t ret);
 /* Call this to relocate the runtime section to an address space */
 void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map);
 
+/* Generic EFI memory allocator, call this to get memory */
+void *efi_alloc(uint64_t len, int memory_type);
+/* More specific EFI memory allocator, called by EFI payloads */
+efi_status_t efi_allocate_pages(int type, int memory_type, unsigned long pages,
+				uint64_t *memory);
+/* EFI memory free function. Not implemented today */
+efi_status_t efi_free_pages(uint64_t memory, unsigned long pages);
+/* Returns the EFI memory map */
+efi_status_t efi_get_memory_map(unsigned long *memory_map_size,
+				struct efi_mem_desc *memory_map,
+				unsigned long *map_key,
+				unsigned long *descriptor_size,
+				uint32_t *descriptor_version);
+/* Adds a range into the EFI memory map */
+uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type,
+			    bool overlap_only_ram);
+/* Called by board init to initialize the EFI memory map */
+int efi_memory_init(void);
+
 /*
  * Use these to indicate that your code / data should go into the EFI runtime
  * section and thus still be available when the OS is running
lib/efi_loader/efi_memory.c new file +34 -0
new file mode 100644
index 0000000..e58d190
@@ -0,0 +1,314 @@
+/*
+ *  EFI application memory management
+ *
+ *  Copyright (c) 2016 Alexander Graf
+ *
+ *  SPDX-License-Identifier:     GPL-2.0+
+ */
+
+/* #define DEBUG_EFI */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <malloc.h>
+#include <asm/global_data.h>
+#include <libfdt_env.h>
+#include <inttypes.h>
+#include <watchdog.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct efi_mem_list {
+	struct list_head link;
+	struct efi_mem_desc desc;
+};
+
+/* This list contains all memory map items */
+LIST_HEAD(efi_mem);
+
+/*
+ * Unmaps all memory occupied by the carve_desc region from the
+ * list entry pointed to by map.
+ *
+ * Returns 1 if carving was performed or 0 if the regions don't overlap.
+ * Returns -1 if it would affect non-RAM regions but overlap_only_ram is set.

lib/efi_loader/efi_memory.c line 44

Quoted 10 lines
lib/efi_loader/efi_memory.c new file +10 -0
+ * Carving is only guaranteed to complete when all regions return 0.
+ */
+static int efi_mem_carve_out(struct efi_mem_list *map,
+			     struct efi_mem_desc *carve_desc,
+			     bool overlap_only_ram)
+{
+	struct efi_mem_list *newmap;
+	struct efi_mem_desc *map_desc = &map->desc;
+	uint64_t map_start = map_desc->physical_start;
+	uint64_t map_end = map_start + (map_desc->num_pages << 12);

Can we replace the magic 12 with an EFI_PAGE_SHIFT define?

Quoted 156 lines
lib/efi_loader/efi_memory.c new file +156 -0
+	uint64_t carve_start = carve_desc->physical_start;
+	uint64_t carve_end = carve_start + (carve_desc->num_pages << 12);
+
+	/* check whether we're overlapping */
+	if ((carve_end <= map_start) || (carve_start >= map_end))
+		return 0;
+
+	/* We're overlapping with non-RAM, warn the caller if desired */
+	if (overlap_only_ram && (map_desc->type != EFI_CONVENTIONAL_MEMORY))
+		return -1;
+
+	/* Sanitize carve_start and carve_end to lie within our bounds */
+	carve_start = max(carve_start, map_start);
+	carve_end = min(carve_end, map_end);
+
+	/* Carving at the beginning of our map? Just move it! */
+	if (carve_start == map_start) {
+		if (map_end == carve_end) {
+			/* Full overlap, just remove map */
+			list_del(&map->link);
+		}
+
+		map_desc->physical_start = carve_end;
+		map_desc->num_pages = (map_end - carve_end) >> 12;
+		return 1;
+	}
+
+	/*
+	 * Overlapping maps, just split the list map at carve_start,
+	 * it will get moved or removed in the next iteration.
+	 *
+	 * [ map_desc |__carve_start__| newmap ]
+	 */
+
+	/* Create a new map from [ carve_start ... map_end ] */
+	newmap = calloc(1, sizeof(*newmap));
+	newmap->desc = map->desc;
+	newmap->desc.physical_start = carve_start;
+	newmap->desc.num_pages = (map_end - carve_start) >> 12;
+        list_add_tail(&newmap->link, &efi_mem);
+
+	/* Shrink the map to [ map_start ... carve_start ] */
+	map_desc->num_pages = (carve_start - map_start) >> 12;
+
+	return 1;
+}
+
+uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type,
+			    bool overlap_only_ram)
+{
+	struct list_head *lhandle;
+	struct efi_mem_list *newlist;
+	bool do_carving;
+
+	if (!pages)
+		return start;
+
+	newlist = calloc(1, sizeof(*newlist));
+	newlist->desc.type = memory_type;
+	newlist->desc.physical_start = start;
+	newlist->desc.virtual_start = start;
+	newlist->desc.num_pages = pages;
+
+	switch (memory_type) {
+	case EFI_RUNTIME_SERVICES_CODE:
+	case EFI_RUNTIME_SERVICES_DATA:
+		newlist->desc.attribute = (1 << EFI_MEMORY_WB_SHIFT) |
+					  (1ULL << EFI_MEMORY_RUNTIME_SHIFT);
+		break;
+	case EFI_MMAP_IO:
+		newlist->desc.attribute = 1ULL << EFI_MEMORY_RUNTIME_SHIFT;
+		break;
+	default:
+		newlist->desc.attribute = 1 << EFI_MEMORY_WB_SHIFT;
+		break;
+	}
+
+	/* Add our new map */
+	do {
+		do_carving = false;
+		list_for_each(lhandle, &efi_mem) {
+			struct efi_mem_list *lmem;
+			int r;
+
+			lmem = list_entry(lhandle, struct efi_mem_list, link);
+			r = efi_mem_carve_out(lmem, &newlist->desc,
+					      overlap_only_ram);
+			if (r < 0) {
+				return 0;
+			} else if (r) {
+				do_carving = true;
+				break;
+			}
+		}
+	} while (do_carving);
+
+	/* Add our new map */
+        list_add_tail(&newlist->link, &efi_mem);
+
+	return start;
+}
+
+static uint64_t efi_find_free_memory(uint64_t len, uint64_t max_addr)
+{
+	struct list_head *lhandle;
+
+	list_for_each(lhandle, &efi_mem) {
+		struct efi_mem_list *lmem = list_entry(lhandle,
+			struct efi_mem_list, link);
+		struct efi_mem_desc *desc = &lmem->desc;
+		uint64_t desc_len = desc->num_pages << 12;
+		uint64_t desc_end = desc->physical_start + desc_len;
+		uint64_t curmax = min(max_addr, desc_end);
+		uint64_t ret = curmax - len;
+
+		/* We only take memory from free RAM */
+		if (desc->type != EFI_CONVENTIONAL_MEMORY)
+			continue;
+
+		/* Out of bounds for max_addr */
+		if ((ret + len) > max_addr)
+			continue;
+
+		/* Out of bounds for upper map limit */
+		if ((ret + len) > desc_end)
+			continue;
+
+		/* Out of bounds for lower map limit */
+		if (ret < desc->physical_start)
+			continue;
+
+		/* Return the highest address in this map within bounds */
+		return ret;
+	}
+
+	return 0;
+}
+
+efi_status_t efi_allocate_pages(int type, int memory_type,
+				unsigned long pages, uint64_t *memory)
+{
+	u64 len = pages << 12;
+	efi_status_t r = EFI_SUCCESS;
+	uint64_t addr;
+
+	switch (type) {
+	case 0:
+		/* Any page */
+		addr = efi_find_free_memory(len, gd->ram_top);
+		if (!addr) {
+			r = EFI_NOT_FOUND;
+			break;
+		}
+		break;
+	case 1:
+		/* Max address */

lib/efi_loader/efi_memory.c line 210

Quoted 10 lines
lib/efi_loader/efi_memory.c new file +10 -0
+		addr = efi_find_free_memory(len, *memory);
+		if (!addr) {
+			r = EFI_NOT_FOUND;
+			break;
+		}
+		break;
+	case 2:
+		/* Exact address, reserve it. The addr is already in *memory. */
+		addr = *memory;
+		break;

Still concerned over the lack of checking for clashes with existing allocations. Ah, no, you trap that below. Sneaky :)

Quoted 6 lines
lib/efi_loader/efi_memory.c new file +6 -0
+	default:
+		/* UEFI doesn't specify other allocation types */
+		r = EFI_INVALID_PARAMETER;
+		break;
+	}
+

lib/efi_loader/efi_memory.c line 226

Quoted 10 lines
lib/efi_loader/efi_memory.c new file +10 -0
+	if (r == EFI_SUCCESS) {
+		uint64_t ret;
+
+		/* Reserve that map in our memory maps */
+		ret = efi_add_memory_map(addr, pages, memory_type, true);
+		if (ret == addr) {
+			*memory = addr;
+		} else {
+			/* Map would overlap, bail out */
+			r = EFI_NOT_FOUND;

EFI_OUT_OF_RESOURCES?

lib/efi_loader/efi_memory.c line 238

Quoted 12 lines
lib/efi_loader/efi_memory.c new file +12 -0
+		}
+	}
+
+	return r;
+}
+
+void *efi_alloc(uint64_t len, int memory_type)
+{
+	uint64_t ret = 0;
+	efi_status_t r;
+
+	r = efi_allocate_pages(0, memory_type, (len + 0xfff) >> 12, &ret);

A define for the 0xfff value as well? (EFI_PAGE_SIZE - 1)?

lib/efi_loader/efi_memory.c line 249

Quoted 11 lines
lib/efi_loader/efi_memory.c new file +11 -0
+	if (r == EFI_SUCCESS)
+		return (void*)(uintptr_t)ret;
+
+	return NULL;
+}
+
+efi_status_t efi_free_pages(uint64_t memory, unsigned long pages)
+{
+	/* We don't free, let's cross our fingers we have plenty RAM */
+	return EFI_SUCCESS;
+}

This would actually be quite easy to implement correctly now with your latest changes :)

But I won't insist it needs to be fixed for initial inclusion.

Quoted 36 lines
lib/efi_loader/efi_memory.c new file +36 -0
+
+efi_status_t efi_get_memory_map(unsigned long *memory_map_size,
+			       struct efi_mem_desc *memory_map,
+			       unsigned long *map_key,
+			       unsigned long *descriptor_size,
+			       uint32_t *descriptor_version)
+{
+	ulong map_size = 0;
+	struct list_head *lhandle;
+
+	list_for_each(lhandle, &efi_mem)
+		map_size += sizeof(struct efi_mem_desc);
+
+	*memory_map_size = map_size;
+
+	if (descriptor_size)
+		*descriptor_size = sizeof(struct efi_mem_desc);
+
+	if (*memory_map_size < map_size)
+		return EFI_BUFFER_TOO_SMALL;
+
+	/* Copy list into array */
+	if (memory_map) {
+		list_for_each(lhandle, &efi_mem) {
+			struct efi_mem_list *lmem;
+
+			lmem = list_entry(lhandle, struct efi_mem_list, link);
+			*memory_map = lmem->desc;
+			memory_map++;
+		}
+	}
+
+	return EFI_SUCCESS;
+}
+
+int efi_memory_init(void)

lib/efi_loader/efi_memory.c line 295

Quoted 10 lines
lib/efi_loader/efi_memory.c new file +10 -0
+{
+	uint64_t runtime_start, runtime_end, runtime_pages;
+	uint64_t uboot_start, uboot_pages;
+	uint64_t uboot_stack_size = 16 * 1024 * 1024;
+	int i;
+
+	/* Add RAM */
+	for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
+		ulong start = (gd->bd->bi_dram[i].start + 0xfff) & ~0xfffULL;
+		ulong pages = (gd->bd->bi_dram[i].size + 0xfff) >> 12;

(EFI_PAGE_SIZE -1)

lib/efi_loader/efi_memory.c line 299

Quoted 4 lines
lib/efi_loader/efi_memory.c new file +4 -0
+
+		efi_add_memory_map(start, pages, EFI_CONVENTIONAL_MEMORY,
+				   false);
+	}

Much prettier with the multi-bank handling. Thanks.

Quoted 18 lines
lib/efi_loader/efi_memory.c new file +15 -0
+
+	/* Add U-Boot */
+	uboot_start = (gd->start_addr_sp - uboot_stack_size) & ~0xfffULL;
+	uboot_pages = (gd->ram_top - uboot_start) >> 12;
+	efi_add_memory_map(uboot_start, uboot_pages, EFI_LOADER_DATA, false);
+
+	/* Add Runtime Services */
+	runtime_start = (ulong)&__efi_runtime_start & ~0xfffULL;
+	runtime_end = ((ulong)&__efi_runtime_stop + 0xfff) & ~0xfffULL;
+	runtime_pages = (runtime_end - runtime_start) >> 12;
+	efi_add_memory_map(runtime_start, runtime_pages,
+			   EFI_RUNTIME_SERVICES_CODE, false);
+
+	return 0;
+}
Signature · 1 line
2.6.2
#24 lore raw

PATCH 9/16 efi_loader: Implement memory allocation and map

In reply to Alexander Graf
Quoted 20 lines · Alexander Graf

The EFI loader needs to maintain views of memory - general system memory windows as well as used locations inside those and potential runtime service MMIO windows.

To manage all of these, add a few helpers that maintain an internal representation of the map the similar to how the EFI API later on reports it to the application.

For allocations, the scheme is very simple. We basically allow allocations to replace chunks of previously done maps, so that a new LOADER_DATA allocation for example can remove a piece of the RAM map. When no specific address is given, we just take the highest possible address in the lowest RAM map that fits the allocation size.

---

v2 -> v3:

Quoted 8 lines

- Rewrite memory allocation and map
- Document header

---
 common/board_r.c            |   3 +
 include/efi_loader.h        |  19 +++
 lib/efi_loader/efi_memory.c | 314 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 336 insertions(+)
 create mode 100644 lib/efi_loader/efi_memory.c

Tested on Beaglebone Black and Minnowboard MAX:

#25 lore raw

PATCH 10/16 arm64: Allow exceptions to return

In reply to Alexander Graf

Our current arm64 exception handlers all panic and never return to the exception triggering code.

But if any handler wanted to continue execution after fixups, it would need help from the exception handling code to restore all registers.

This patch implements that help. With this code, exception handlers on aarch64 can successfully return to the place the exception happened (or somewhere else if they modify elr).

---
 arch/arm/cpu/armv8/exceptions.S | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)
arch/arm/cpu/armv8/exceptions.S +34 -0
index baf9401..4f4f526 100644
@@ -82,31 +82,65 @@ vectors:
 _do_bad_sync:
 	exception_entry
 	bl	do_bad_sync
+	b	exception_exit
 
 _do_bad_irq:
 	exception_entry
 	bl	do_bad_irq
+	b	exception_exit
 
 _do_bad_fiq:
 	exception_entry
 	bl	do_bad_fiq
+	b	exception_exit
 
 _do_bad_error:
 	exception_entry
 	bl	do_bad_error
+	b	exception_exit
 
 _do_sync:
 	exception_entry
 	bl	do_sync
+	b	exception_exit
 
 _do_irq:
 	exception_entry
 	bl	do_irq
+	b	exception_exit
 
 _do_fiq:
 	exception_entry
 	bl	do_fiq
+	b	exception_exit
 
 _do_error:
 	exception_entry
 	bl	do_error
+	b	exception_exit
+
+exception_exit:
+	ldp	x2, x0, [sp],#16
+	switch_el x11, 3f, 2f, 1f
+3:	msr	elr_el3, x2
+	b	0f
+2:	msr	elr_el2, x2
+	b	0f
+1:	msr	elr_el1, x2
+0:
+	ldp	x1, x2, [sp],#16
+	ldp	x3, x4, [sp],#16
+	ldp	x5, x6, [sp],#16
+	ldp	x7, x8, [sp],#16
+	ldp	x9, x10, [sp],#16
+	ldp	x11, x12, [sp],#16
+	ldp	x13, x14, [sp],#16
+	ldp	x15, x16, [sp],#16
+	ldp	x17, x18, [sp],#16
+	ldp	x19, x20, [sp],#16
+	ldp	x21, x22, [sp],#16
+	ldp	x23, x24, [sp],#16
+	ldp	x25, x26, [sp],#16
+	ldp	x27, x28, [sp],#16
+	ldp	x29, x30, [sp],#16
+	eret
Signature · 1 line
2.6.2
#26 lore raw

PATCH 11/16 arm64: Allow EFI payload code to take exceptions

In reply to Alexander Graf

There are 2 ways an EFI payload could return into u-boot:

- Callback function
- Exception

While in EFI payload mode, x18 is owned by the payload and may not contain a valid pointer to gd, so we need to fix it up. We do that properly for the payload to callback path already.

This patch also adds gd pointer restoral for the exception path.

---
 arch/arm/lib/interrupts_64.c | 9 +++++++++
 1 file changed, 9 insertions(+)
arch/arm/lib/interrupts_64.c +9 -0
index b476722..7c9cfce 100644
@@ -7,6 +7,7 @@
 
 #include <common.h>
 #include <linux/compiler.h>
+#include <efi_loader.h>
 
 
 int interrupt_init(void)
@@ -41,6 +42,7 @@ void show_regs(struct pt_regs *regs)
  */
 void do_bad_sync(struct pt_regs *pt_regs, unsigned int esr)
 {
+	efi_restore_gd();
 	printf("Bad mode in \"Synchronous Abort\" handler, esr 0x%08x\n", esr);
 	show_regs(pt_regs);
 	panic("Resetting CPU ...\n");
@@ -51,6 +53,7 @@ void do_bad_sync(struct pt_regs *pt_regs, unsigned int esr)
  */
 void do_bad_irq(struct pt_regs *pt_regs, unsigned int esr)
 {
+	efi_restore_gd();
 	printf("Bad mode in \"Irq\" handler, esr 0x%08x\n", esr);
 	show_regs(pt_regs);
 	panic("Resetting CPU ...\n");
@@ -61,6 +64,7 @@ void do_bad_irq(struct pt_regs *pt_regs, unsigned int esr)
  */
 void do_bad_fiq(struct pt_regs *pt_regs, unsigned int esr)
 {
+	efi_restore_gd();
 	printf("Bad mode in \"Fiq\" handler, esr 0x%08x\n", esr);
 	show_regs(pt_regs);
 	panic("Resetting CPU ...\n");
@@ -71,6 +75,7 @@ void do_bad_fiq(struct pt_regs *pt_regs, unsigned int esr)
  */
 void do_bad_error(struct pt_regs *pt_regs, unsigned int esr)
 {
+	efi_restore_gd();
 	printf("Bad mode in \"Error\" handler, esr 0x%08x\n", esr);
 	show_regs(pt_regs);
 	panic("Resetting CPU ...\n");
@@ -81,6 +86,7 @@ void do_bad_error(struct pt_regs *pt_regs, unsigned int esr)
  */
 void do_sync(struct pt_regs *pt_regs, unsigned int esr)
 {
+	efi_restore_gd();
 	printf("\"Synchronous Abort\" handler, esr 0x%08x\n", esr);
 	show_regs(pt_regs);
 	panic("Resetting CPU ...\n");
@@ -91,6 +97,7 @@ void do_sync(struct pt_regs *pt_regs, unsigned int esr)
  */
 void do_irq(struct pt_regs *pt_regs, unsigned int esr)
 {
+	efi_restore_gd();
 	printf("\"Irq\" handler, esr 0x%08x\n", esr);
 	show_regs(pt_regs);
 	panic("Resetting CPU ...\n");
@@ -101,6 +108,7 @@ void do_irq(struct pt_regs *pt_regs, unsigned int esr)
  */
 void do_fiq(struct pt_regs *pt_regs, unsigned int esr)
 {
+	efi_restore_gd();
 	printf("\"Fiq\" handler, esr 0x%08x\n", esr);
 	show_regs(pt_regs);
 	panic("Resetting CPU ...\n");
@@ -114,6 +122,7 @@ void do_fiq(struct pt_regs *pt_regs, unsigned int esr)
  */
 void __weak do_error(struct pt_regs *pt_regs, unsigned int esr)
 {
+	efi_restore_gd();
 	printf("\"Error\" handler, esr 0x%08x\n", esr);
 	show_regs(pt_regs);
 	panic("Resetting CPU ...\n");
Signature · 1 line
2.6.2
#27 lore raw

PATCH 12/16 efi_loader: Add DCACHE_OFF support for arm64

In reply to Alexander Graf

On arm64, boards can declare that they want to run with dcache disabled.

However, uEFI guarantees to payloads that they're running with the dcache enabled which on arm64 means that they can do unaligned accesses.

To not leave those systems out of the door, let's handle the unaligned traps. In the typical boot case, the OS will set up page tables and dcache itself early on anyway once it's done talking with uEFI.

---
 arch/arm/cpu/armv8/exceptions.S |  10 ++
 arch/arm/lib/Makefile           |   3 +
 arch/arm/lib/interrupts_64.c    |  19 +++
 arch/arm/lib/unaligned_64.c     | 284 ++++++++++++++++++++++++++++++++++++++++
 cmd/bootefi.c                   |   5 +
 5 files changed, 321 insertions(+)
 create mode 100644 arch/arm/lib/unaligned_64.c
arch/arm/cpu/armv8/exceptions.S +10 -0
index 4f4f526..97101c3 100644
@@ -144,3 +144,13 @@ exception_exit:
 	ldp	x27, x28, [sp],#16
 	ldp	x29, x30, [sp],#16
 	eret
+
+.global read_far
+read_far:
+	switch_el x1, 3f, 2f, 1f
+3:	mrs	x0, far_el3
+	ret
+2:	mrs	x0, far_el2
+	ret
+1:	mrs	x0, far_el1
+	ret
arch/arm/lib/Makefile +3 -0
index f3db7b5..ce5ed99 100644
@@ -42,6 +42,9 @@ else ifdef CONFIG_ARM64
 obj-y	+= ccn504.o
 obj-y	+= gic_64.o
 obj-y	+= interrupts_64.o
+ifeq ($(CONFIG_SYS_DCACHE_OFF),y)
+obj-$(CONFIG_EFI_LOADER)	+= unaligned_64.o
+endif
 else
 obj-y	+= interrupts.o
 endif
arch/arm/lib/interrupts_64.c +19 -0
index 7c9cfce..4aa36de 100644
@@ -81,12 +81,31 @@ void do_bad_error(struct pt_regs *pt_regs, unsigned int esr)
 	panic("Resetting CPU ...\n");
 }
 
+#if defined(CONFIG_EFI_LOADER) && defined(CONFIG_SYS_DCACHE_OFF)
+int do_unaligned_data(struct pt_regs *pt_regs, unsigned int esr);
+#else
+static int do_unaligned_data(struct pt_regs *pt_regs, unsigned int esr)
+{
+	return -1;
+}
+#endif
+
 /*
  * do_sync handles the Synchronous Abort exception.
  */
 void do_sync(struct pt_regs *pt_regs, unsigned int esr)
 {
 	efi_restore_gd();
+
+	/*
+	 * EFI guarantees that unaligned accesses do succeed, so while we
+	 * still need hardware access and thus are unsure whether we can
+	 * enable the dcache to have the CPU deal with them, we fix unaligned
+	 * accesses up ourselves.
+	 */
+	if (!do_unaligned_data(pt_regs, esr))
+		return;
+
 	printf("\"Synchronous Abort\" handler, esr 0x%08x\n", esr);
 	show_regs(pt_regs);
 	panic("Resetting CPU ...\n");
arch/arm/lib/unaligned_64.c new file +284 -0

284 lines not on this page · raw message

cmd/bootefi.c +5 -0
index e3e51d4..5968ecc 100644
@@ -108,6 +108,11 @@ static unsigned long do_bootefi_exec(void *efi)
 		systab.nr_tables = 0;
 	}
 
+#ifdef CONFIG_SYS_DCACHE_OFF
+	printf("WARNING: You're running with MMU disabled, expect crashes\n"
+	       "         and send bug reports to your firmware vendor!\n");
+#endif
+
 	/* Load the EFI payload */
 	entry = efi_load_pe(efi, &loaded_image_info);
 	if (!entry)
Signature · 1 line
2.6.2
#28 lore raw

PATCH 12/16 efi_loader: Add DCACHE_OFF support for arm64

Quoted 8 lines · Alexander Graf

On arm64, boards can declare that they want to run with dcache disabled.

However, uEFI guarantees to payloads that they're running with the dcache enabled which on arm64 means that they can do unaligned accesses.

To not leave those systems out of the door, let's handle the unaligned traps. In the typical boot case, the OS will set up page tables and dcache itself early on anyway once it's done talking with uEFI.

This is not sufficient to emulate having caches enabled.

There are other things which operate differently with the caches on (e.g. exclusives and/or atomics, which a compiler might generate implicitly).

Likewise, cache-maintenance by Va (which you may require from the I-side) implicitly hazards against cacheable accesses, but not against non-cacheable accesses.

There are almsot certainly other differences.

Due to that, I don't think this is a good approach.

Why can we not map memory using cacheable attributes in all cases?

Mark.

Quoted 392 lines
---
 arch/arm/cpu/armv8/exceptions.S |  10 ++
 arch/arm/lib/Makefile           |   3 +
 arch/arm/lib/interrupts_64.c    |  19 +++
 arch/arm/lib/unaligned_64.c     | 284 ++++++++++++++++++++++++++++++++++++++++
 cmd/bootefi.c                   |   5 +
 5 files changed, 321 insertions(+)
 create mode 100644 arch/arm/lib/unaligned_64.c
arch/arm/cpu/armv8/exceptions.S +10 -0
index 4f4f526..97101c3 100644
@@ -144,3 +144,13 @@ exception_exit:
 	ldp	x27, x28, [sp],#16
 	ldp	x29, x30, [sp],#16
 	eret
+
+.global read_far
+read_far:
+	switch_el x1, 3f, 2f, 1f
+3:	mrs	x0, far_el3
+	ret
+2:	mrs	x0, far_el2
+	ret
+1:	mrs	x0, far_el1
+	ret
arch/arm/lib/Makefile +3 -0
index f3db7b5..ce5ed99 100644
@@ -42,6 +42,9 @@ else ifdef CONFIG_ARM64
 obj-y	+= ccn504.o
 obj-y	+= gic_64.o
 obj-y	+= interrupts_64.o
+ifeq ($(CONFIG_SYS_DCACHE_OFF),y)
+obj-$(CONFIG_EFI_LOADER)	+= unaligned_64.o
+endif
 else
 obj-y	+= interrupts.o
 endif
arch/arm/lib/interrupts_64.c +19 -0
index 7c9cfce..4aa36de 100644
@@ -81,12 +81,31 @@ void do_bad_error(struct pt_regs *pt_regs, unsigned int esr)
 	panic("Resetting CPU ...\n");
 }
 
+#if defined(CONFIG_EFI_LOADER) && defined(CONFIG_SYS_DCACHE_OFF)
+int do_unaligned_data(struct pt_regs *pt_regs, unsigned int esr);
+#else
+static int do_unaligned_data(struct pt_regs *pt_regs, unsigned int esr)
+{
+	return -1;
+}
+#endif
+
 /*
  * do_sync handles the Synchronous Abort exception.
  */
 void do_sync(struct pt_regs *pt_regs, unsigned int esr)
 {
 	efi_restore_gd();
+
+	/*
+	 * EFI guarantees that unaligned accesses do succeed, so while we
+	 * still need hardware access and thus are unsure whether we can
+	 * enable the dcache to have the CPU deal with them, we fix unaligned
+	 * accesses up ourselves.
+	 */
+	if (!do_unaligned_data(pt_regs, esr))
+		return;
+
 	printf("\"Synchronous Abort\" handler, esr 0x%08x\n", esr);
 	show_regs(pt_regs);
 	panic("Resetting CPU ...\n");
arch/arm/lib/unaligned_64.c new file +284 -0
new file mode 100644
index 0000000..b307b7e
@@ -0,0 +1,284 @@
+/*
+ * (C) Copyright 2016
+ * Alexander Graf <[email protected]>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <linux/compiler.h>
+#include <efi_loader.h>
+
+#define ESR_EC_MASK	0xFC000000
+#define ESR_EC_SHIFT	26
+#define ESR_IL_MASK	0x02000000
+#define ESR_IL_SHIFT	25
+#define ESR_ISS_MASK	0x01FFFFFF
+#define ESR_ISS_SHIFT	0
+
+#define EC_DATA_SL	0x25
+
+#define ISS_ISV_MASK	0x01000000
+#define ISS_ISV_SHIFT	24
+#define ISS_SAS_MASK	0x00C00000
+#define ISS_SAS_SHIFT	22
+#define ISS_SSE_MASK	0x00200000
+#define ISS_SSE_SHIFT	21
+#define ISS_SRT_MASK	0x000F0000
+#define ISS_SRT_SHIFT	16
+#define ISS_SF_MASK	0x00008000
+#define ISS_SF_SHIFT	15
+#define ISS_AR_MASK	0x00004000
+#define ISS_AR_SHIFT	14
+#define ISS_EA_MASK	0x00000200
+#define ISS_EA_SHIFT	9
+#define ISS_CM_MASK	0x00000100
+#define ISS_CM_SHIFT	8
+#define ISS_S1PTW_MASK	0x00000080
+#define ISS_S1PTW_SHIFT	7
+#define ISS_WNR_MASK	0x00000040
+#define ISS_WNR_SHIFT	6
+#define WNR_READ	0
+#define WNR_WRITE	1
+#define ISS_DFSC_MASK	0x0000003F
+#define ISS_DFSC_SHIFT	0
+
+#define ISV_VALID	1
+#define DFSC_ALIGN	0x21
+
+ulong read_far(void);
+int do_unaligned_data(struct pt_regs *pt_regs, unsigned int esr);
+
+static inline int32_t sextract32(uint32_t value, int start, int length)
+{
+	return ((int32_t)(value << (32 - length - start))) >> (32 - length);
+}
+
+static inline uint32_t extract32(uint32_t value, int start, int length)
+{
+	return (value >> start) & (~0U >> (32 - length));
+}
+
+static int insn_iss_ldst(uint32_t insn, int iss, int *wb_reg, ulong *wb_val)
+{
+	int rt = extract32(insn, 0, 5);
+	int rn = extract32(insn, 5, 5);
+	int idx = extract32(insn, 10, 2);
+	int imm9 = sextract32(insn, 12, 9);
+	int opc = extract32(insn, 22, 2);
+	int size = extract32(insn, 30, 2);
+	bool is_signed = false;
+	bool is_store = false;
+	bool is_extended = false;
+	bool is_vector = extract32(insn, 26, 1);
+
+	switch (extract32(insn, 25, 4)) {
+	case 0x4:
+	case 0x6:
+	case 0xc:
+	case 0xe:	  /* Loads and stores */
+		break;
+        default:
+		return iss;
+	}
+
+	switch (extract32(insn, 24, 6)) {
+	case 0x38: case 0x39:
+	case 0x3c: case 0x3d: /* Load/store register (all forms) */
+		break;
+	default:
+		return iss;
+	}
+
+	switch (extract32(insn, 24, 2)) {
+	case 0:
+		if (extract32(insn, 21, 1) != 1 &&
+		    extract32(insn, 10, 2) != 2) {
+			/* Write back */
+			if (idx & 1) {
+				ulong far = read_far();
+				*wb_reg = rn;
+				*wb_val = (idx & 2) ? far : far + imm9;
+				break;
+			}
+		}
+		break;
+	case 1:
+		break;
+	default:
+		return iss;
+	}
+
+	if (is_vector) {
+		return iss;
+	}
+
+	is_store = (opc == 0);
+	is_signed = extract32(opc, 1, 1);
+	is_extended = (size < 3) && extract32(opc, 0, 1);
+
+	iss |= ISS_ISV_MASK;
+	iss |= size << ISS_SAS_SHIFT;
+	iss |= (is_extended && is_signed) ? ISS_SSE_MASK : 0;
+	iss |= rt << ISS_SRT_SHIFT;
+	iss |= ISS_SF_MASK;
+	iss |= is_store ? ISS_WNR_MASK : 0;
+
+	return iss;
+}
+
+static void do_unaligned_access(struct pt_regs *pt_regs, int wnr, int rt,
+				ulong addr, int sas, int sse)
+{
+	void *rr = (void*)&pt_regs->regs[rt];
+	void *r = rr;
+	int len = 1 << sas;
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+	/* On BE registers get filled from the back */
+	r = (char*)r + (8 - len);
+#endif
+
+	if (wnr == WNR_READ) {
+		/* Read with zero pad */
+		pt_regs->regs[rt] = 0;
+		memcpy(r, (void*)addr, len);
+
+		/* Sign extend */
+		if (sse) {
+			switch (sas) {
+			case 0: *(long*)rr = *(char*)r; break;
+			case 1: *(long*)rr = *(short*)r; break;
+			case 2: *(long*)rr = *(int*)r; break;
+			}
+		}
+	} else if (wnr == WNR_WRITE) {
+		memcpy((void*)addr, r, len);
+	}
+}
+
+static int do_ldst_pair(u32 insn, struct pt_regs *pt_regs)
+{
+	int rt = extract32(insn, 0, 5);
+	int rn = extract32(insn, 5, 5);
+	int rt2 = extract32(insn, 10, 5);
+	int index = extract32(insn, 23, 2);
+	bool is_vector = extract32(insn, 26, 1);
+	bool is_load = extract32(insn, 22, 1);
+	int opc = extract32(insn, 30, 2);
+	ulong far = read_far();
+	ulong old_far = far;
+	int wnr = is_load ? WNR_READ : WNR_WRITE;
+
+	bool is_signed = extract32(opc, 0, 1);
+	bool postindex = false;
+	bool wback = false;
+
+	int size = 2 + extract32(opc, 1, 1);
+
+	switch (extract32(insn, 25, 4)) {
+	case 0x4:
+	case 0x6:
+	case 0xc:
+	case 0xe:	  /* Loads and stores */
+		break;
+        default:
+		return -1;
+	}
+
+	switch (extract32(insn, 24, 6)) {
+	case 0x28: case 0x29:
+	case 0x2c: case 0x2d: /* Load/store pair (all forms) */
+		break;
+	default:
+		return -1;
+	}
+
+	if (is_vector)
+		return -1;
+
+	switch (index) {
+	case 1: /* post-index */
+		postindex = true;
+		wback = true;
+		break;
+	case 0: /* signed offset with "non-temporal" hint. */
+		postindex = false;
+		break;
+	case 2: /* signed offset, rn not updated */
+		postindex = false;
+		break;
+	case 3: /* pre-index */
+		postindex = false;
+		wback = true;
+		break;
+	}
+
+	do_unaligned_access(pt_regs, wnr, rt, far, size, is_signed);
+	far += 1 << size;
+	do_unaligned_access(pt_regs, wnr, rt2, far, size, is_signed);
+	far += 1 << size;
+
+	if (wback)
+		pt_regs->regs[rn] = postindex ? far : old_far;
+
+	return 0;
+}
+
+int do_unaligned_data(struct pt_regs *pt_regs, unsigned int esr)
+{
+	int insn = *(u32*)(void*)pt_regs->elr;
+	int ec = (esr & ESR_EC_MASK) >> ESR_EC_SHIFT;
+	int iss = (esr & ESR_ISS_MASK) >> ESR_ISS_SHIFT;
+	int isv = (iss & ISS_ISV_MASK) >> ISS_ISV_SHIFT;
+	int dfsc = (iss & ISS_DFSC_MASK) >> ISS_DFSC_SHIFT;
+	int sas, sse, srt, sf, ar, cm, s1ptw, wnr;
+	int wb_reg = -1;
+	ulong wb_val;
+
+	/* Check whether we have an alignment fault */
+	if ((ec != EC_DATA_SL) || (dfsc != DFSC_ALIGN))
+		return -1;
+
+	/* Fix up instruction decoding */
+	if (!isv) {
+		iss = insn_iss_ldst(insn, iss, &wb_reg, &wb_val);
+	}
+
+	isv = (iss & ISS_ISV_MASK) >> ISS_ISV_SHIFT;
+	sas = (iss & ISS_SAS_MASK) >> ISS_SAS_SHIFT;
+	sse = (iss & ISS_SSE_MASK) >> ISS_SSE_SHIFT;
+	srt = (iss & ISS_SRT_MASK) >> ISS_SRT_SHIFT;
+	sf = (iss & ISS_SF_MASK) >> ISS_SF_SHIFT;
+	ar = (iss & ISS_AR_MASK) >> ISS_AR_SHIFT;
+	cm = (iss & ISS_CM_MASK) >> ISS_CM_SHIFT;
+	s1ptw = (iss & ISS_S1PTW_MASK) >> ISS_S1PTW_SHIFT;
+	wnr = (iss & ISS_WNR_MASK) >> ISS_WNR_SHIFT;
+
+	/* Check whether we have an easily fixable alignment fault */
+	if (isv && sf && !ar && !cm && !s1ptw) {
+		ulong far = read_far();
+
+		do_unaligned_access(pt_regs, wnr, srt, far, sas, sse);
+
+		/* Jump across the offending instruction */
+		pt_regs->elr += 4;
+
+		/* Do writebacks if required */
+		if (wb_reg != -1)
+			pt_regs->regs[wb_reg] = wb_val;
+
+		/* And return from the exception */
+		return 0;
+	}
+
+	if (!do_ldst_pair(insn, pt_regs)) {
+		pt_regs->elr += 4;
+		return 0;
+	}
+
+	/* Couldn't fix it, panic */
+	printf("Alignment handler couldn't decode insn %08x\n",
+	       *(u32*)(void*)pt_regs->elr);
+	return -1;
+}
cmd/bootefi.c +5 -0
index e3e51d4..5968ecc 100644
@@ -108,6 +108,11 @@ static unsigned long do_bootefi_exec(void *efi)
 		systab.nr_tables = 0;
 	}
 
+#ifdef CONFIG_SYS_DCACHE_OFF
+	printf("WARNING: You're running with MMU disabled, expect crashes\n"
+	       "         and send bug reports to your firmware vendor!\n");
+#endif
+
 	/* Load the EFI payload */
 	entry = efi_load_pe(efi, &loaded_image_info);
 	if (!entry)
Signature · 6 lines
2.6.2

_______________________________________________
U-Boot mailing list
U-Boot at lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot
#29 lore raw

PATCH 12/16 efi_loader: Add DCACHE_OFF support for arm64

Quoted 20 lines · Mark Rutland
Quoted 8 lines · Alexander Graf

On arm64, boards can declare that they want to run with dcache disabled.

However, uEFI guarantees to payloads that they're running with the dcache enabled which on arm64 means that they can do unaligned accesses.

To not leave those systems out of the door, let's handle the unaligned traps. In the typical boot case, the OS will set up page tables and dcache itself early on anyway once it's done talking with uEFI.

This is not sufficient to emulate having caches enabled.

There are other things which operate differently with the caches on (e.g. exclusives and/or atomics, which a compiler might generate implicitly).

Likewise, cache-maintenance by Va (which you may require from the I-side) implicitly hazards against cacheable accesses, but not against non-cacheable accesses.

Quoted 5 lines

There are almsot certainly other differences.

Due to that, I don't think this is a good approach.

Why can we not map memory using cacheable attributes in all cases?

I have a simpiler question perhaps, why can we not just say that this support (in Kconfig that is) depends on !SYS_DACHE_OFF ? Yes, only ARC as moved that symbol over to Kconfig but that's a relatively easy thing to remedy. I assume that it being off on some systems is more of a debug / bringup thing that needs to be circled back to rather than a good hard requirement of the platform.

Signature · 8 lines
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20160202/b47fca0b/attachment.sig>
#30 lore raw

PATCH 12/16 efi_loader: Add DCACHE_OFF support for arm64

Quoted 29 lines

Am 02.02.2016 um 18:28 schrieb Tom Rini <[email protected]>:

Quoted 25 lines · Mark Rutland
Quoted 9 lines · Alexander Graf

On arm64, boards can declare that they want to run with dcache disabled.

However, uEFI guarantees to payloads that they're running with the dcache enabled which on arm64 means that they can do unaligned accesses.

To not leave those systems out of the door, let's handle the unaligned traps. In the typical boot case, the OS will set up page tables and dcache itself early on anyway once it's done talking with uEFI.

This is not sufficient to emulate having caches enabled.

There are other things which operate differently with the caches on (e.g. exclusives and/or atomics, which a compiler might generate implicitly).

Likewise, cache-maintenance by Va (which you may require from the I-side) implicitly hazards against cacheable accesses, but not against non-cacheable accesses.

There are almsot certainly other differences.

Due to that, I don't think this is a good approach.

Quoted 8 lines
Quoted 1 line

Why can we not map memory using cacheable attributes in all cases?

I have a simpiler question perhaps, why can we not just say that this support (in Kconfig that is) depends on !SYS_DACHE_OFF ? Yes, only ARC as moved that symbol over to Kconfig but that's a relatively easy thing to remedy. I assume that it being off on some systems is more of a debug / bringup thing that needs to be circled back to rather than a good hard requirement of the platform.

How about we just remove cache disabled mode for AArch64? I really don't understand why it's there in the first place.

Alex

#31 lore raw

PATCH 13/16 efi_loader: hook up in build environment

In reply to Alexander Graf

Now that we have all the bits and pieces ready for EFI payload loading support, hook them up in Makefiles and KConfigs so that we can build.

---

v1 -> v2:

- Move to GPLv2+
- Default to y

v2 -> v3:

- Add memory file

---
 lib/Kconfig             |  1 +
 lib/Makefile            |  1 +
 lib/efi_loader/Kconfig  |  9 +++++++++
 lib/efi_loader/Makefile | 12 ++++++++++++
 4 files changed, 23 insertions(+)
 create mode 100644 lib/efi_loader/Kconfig
 create mode 100644 lib/efi_loader/Makefile
lib/Kconfig +1 -0
index 46d7034..f3cdd98 100644
@@ -129,5 +129,6 @@ config ERRNO_STR
 	  - if errno is negative - a pointer to errno related message
 
 source lib/efi/Kconfig
+source lib/efi_loader/Kconfig
 
 endmenu
lib/Makefile +1 -0
index dd36f25..3a9f304 100644
@@ -8,6 +8,7 @@
 ifndef CONFIG_SPL_BUILD
 
 obj-$(CONFIG_EFI) += efi/
+obj-$(CONFIG_EFI_LOADER) += efi_loader/
 obj-$(CONFIG_RSA) += rsa/
 obj-$(CONFIG_LZMA) += lzma/
 obj-$(CONFIG_LZO) += lzo/
lib/efi_loader/Kconfig new file +9 -0
new file mode 100644
index 0000000..6da1c7f
@@ -0,0 +1,9 @@
+config EFI_LOADER
+	bool "Support running EFI Applications in U-Boot"
+	depends on ARM64 ||?ARM
+	default y
+	help
+	  Select this option if you want to run EFI applications (like grub2)
+	  on top of U-Boot. If this option is enabled, U-Boot will expose EFI
+	  interfaces to a loaded EFI application, enabling it to reuse U-Boot's
+	  device drivers.
lib/efi_loader/Makefile new file +12 -0
new file mode 100644
index 0000000..28725a2
@@ -0,0 +1,12 @@
+#
+# (C) Copyright 2016 Alexander Graf
+#
+#  SPDX-License-Identifier:     GPL-2.0+
+#
+
+# This file only gets included with CONFIG_EFI_LOADER set, so all
+# object inclusion implicitly depends on it
+
+obj-y += efi_image_loader.o efi_boottime.o efi_runtime.o efi_console.o
+obj-y += efi_memory.o
+obj-$(CONFIG_PARTITIONS) += efi_disk.o
Signature · 1 line
2.6.2
#32 lore raw

PATCH 13/16 efi_loader: hook up in build environment

Quoted 16 lines · Alexander Graf

Now that we have all the bits and pieces ready for EFI payload loading support, hook them up in Makefiles and KConfigs so that we can build.

---

v1 -> v2:

- Move to GPLv2+
- Default to y

v2 -> v3:

- Add memory file

Quoted 8 lines
---
 lib/Kconfig             |  1 +
 lib/Makefile            |  1 +
 lib/efi_loader/Kconfig  |  9 +++++++++
 lib/efi_loader/Makefile | 12 ++++++++++++
 4 files changed, 23 insertions(+)
 create mode 100644 lib/efi_loader/Kconfig
 create mode 100644 lib/efi_loader/Makefile

Tested on Beaglebone Black and Minnowboard MAX:

#33 lore raw

PATCH 14/16 efi_loader: Add distro boot script for removable media

In reply to Alexander Graf

UEFI defines a simple boot protocol for removable media. There we should look at the EFI (first GPT FAT) partition and search for /efi/boot/bootXXX.efi with XXX being different between different platforms (x86, x64, arm, aa64, ...).

This patch implements a simple version of that protocol for the default distro boot script. With this we can automatically boot from valid UEFI enabled removable media.

Because from all I could see U-Boot by default doesn't deliver device tree blobs with its firmware, we also need to load the dtb from somewhere. Traverse the same EFI partition for an fdt file that fits our current board so that an OS receives a valid device tree when booted automatically.

---
 include/config_distro_bootcmd.h | 47 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 46 insertions(+), 1 deletion(-)
include/config_distro_bootcmd.h +46 -1
index 37c6b43..c19f1b0 100644
@@ -90,6 +90,48 @@
 	BOOT_TARGET_DEVICES_references_UBIFS_without_CONFIG_CMD_UBIFS
 #endif
 
+#ifdef CONFIG_EFI_LOADER
+#if defined(CONFIG_ARM64)
+#define BOOTEFI_NAME "bootaa64.efi"
+#elif defined(CONFIG_ARM)
+#define BOOTEFI_NAME "bootarm.efi"
+#endif
+#endif
+
+#ifdef BOOTEFI_NAME
+#define BOOTENV_SHARED_EFI                                                \
+	"boot_efi_binary="                                                \
+		"load ${devtype} ${devnum}:${distro_bootpart} "           \
+			"${kernel_addr_r} efi/boot/"BOOTEFI_NAME"; "      \
+		"bootefi ${kernel_addr_r}\0"                              \
+	\
+	"load_efi_dtb="                                                   \
+		"load ${devtype} ${devnum}:${distro_bootpart} "           \
+			"${fdt_addr_r} ${prefix}${fdt_name}; "           \
+		"fdt addr ${fdt_addr_r}\0"                                \
+	\
+	"efi_dtb_prefixes=/ /dtb/ /dtb/current/\0"                        \
+	"scan_dev_for_efi="                                               \
+		"for prefix in ${efi_dtb_prefixes}; do "                  \
+			"if test -e ${devtype} "                          \
+					"${devnum}:${distro_bootpart} "   \
+					"${prefix}${fdt_name}; then "     \
+				"run load_efi_dtb; "                      \
+			"fi;"                                             \
+		"done;"                                                   \
+		"if test -e ${devtype} ${devnum}:${distro_bootpart} "     \
+					"efi/boot/"BOOTEFI_NAME"; then "  \
+				"echo Found EFI removable media binary "  \
+					"efi/boot/"BOOTEFI_NAME"; "       \
+				"run boot_efi_binary; "                   \
+				"echo EFI LOAD FAILED: continuing...; "   \
+		"fi; "
+#define SCAN_DEV_FOR_EFI "run scan_dev_for_efi;"
+#else
+#define BOOTENV_SHARED_EFI
+#define SCAN_DEV_FOR_EFI
+#endif
+
 #ifdef CONFIG_CMD_SATA
 #define BOOTENV_SHARED_SATA	BOOTENV_SHARED_BLKDEV(sata)
 #define BOOTENV_DEV_SATA	BOOTENV_DEV_BLKDEV
@@ -217,6 +259,7 @@
 	BOOTENV_SHARED_SCSI \
 	BOOTENV_SHARED_IDE \
 	BOOTENV_SHARED_UBIFS \
+	BOOTENV_SHARED_EFI \
 	"boot_prefixes=/ /boot/\0" \
 	"boot_scripts=boot.scr.uimg boot.scr\0" \
 	"boot_script_dhcp=boot.scr.uimg\0" \
@@ -258,7 +301,9 @@
 		"for prefix in ${boot_prefixes}; do "                     \
 			"run scan_dev_for_extlinux; "                     \
 			"run scan_dev_for_scripts; "                      \
-		"done\0"                                                  \
+		"done;"                                                   \
+		SCAN_DEV_FOR_EFI                                          \
+		"\0"                                                      \
 	\
 	"scan_dev_for_boot_part="                                         \
 		"part list ${devtype} ${devnum} -bootable devplist; "     \
Signature · 1 line
2.6.2
#34 lore raw

PATCH 14/16 efi_loader: Add distro boot script for removable media

Quoted 3 lines · Alexander Graf

UEFI defines a simple boot protocol for removable media. There we should look at the EFI (first GPT FAT) partition and search for /efi/boot/bootXXX.efi with XXX being different between different platforms (x86, x64, arm, aa64, ...).

One comment, one question.
Comment:
It's not really "the first GPT FAT" - it's the partition marked with the EFI System Partition type (containing a FAT filesystem). This is actually defined for MBR partition tables too, but I'd be quite happy for that bit of legacy to be left out.

Question:
Does U-Boot support El Torito for iso images?

Quoted 91 lines

This patch implements a simple version of that protocol for the default distro boot script. With this we can automatically boot from valid UEFI enabled removable media.

Because from all I could see U-Boot by default doesn't deliver device tree blobs with its firmware, we also need to load the dtb from somewhere. Traverse the same EFI partition for an fdt file that fits our current board so that an OS receives a valid device tree when booted automatically.

---
 include/config_distro_bootcmd.h | 47 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 46 insertions(+), 1 deletion(-)
include/config_distro_bootcmd.h +46 -1
index 37c6b43..c19f1b0 100644
@@ -90,6 +90,48 @@
 	BOOT_TARGET_DEVICES_references_UBIFS_without_CONFIG_CMD_UBIFS
 #endif
 
+#ifdef CONFIG_EFI_LOADER
+#if defined(CONFIG_ARM64)
+#define BOOTEFI_NAME "bootaa64.efi"
+#elif defined(CONFIG_ARM)
+#define BOOTEFI_NAME "bootarm.efi"
+#endif
+#endif
+
+#ifdef BOOTEFI_NAME
+#define BOOTENV_SHARED_EFI                                                \
+	"boot_efi_binary="                                                \
+		"load ${devtype} ${devnum}:${distro_bootpart} "           \
+			"${kernel_addr_r} efi/boot/"BOOTEFI_NAME"; "      \
+		"bootefi ${kernel_addr_r}\0"                              \
+	\
+	"load_efi_dtb="                                                   \
+		"load ${devtype} ${devnum}:${distro_bootpart} "           \
+			"${fdt_addr_r} ${prefix}${fdt_name}; "           \
+		"fdt addr ${fdt_addr_r}\0"                                \
+	\
+	"efi_dtb_prefixes=/ /dtb/ /dtb/current/\0"                        \
+	"scan_dev_for_efi="                                               \
+		"for prefix in ${efi_dtb_prefixes}; do "                  \
+			"if test -e ${devtype} "                          \
+					"${devnum}:${distro_bootpart} "   \
+					"${prefix}${fdt_name}; then "     \
+				"run load_efi_dtb; "                      \
+			"fi;"                                             \
+		"done;"                                                   \
+		"if test -e ${devtype} ${devnum}:${distro_bootpart} "     \
+					"efi/boot/"BOOTEFI_NAME"; then "  \
+				"echo Found EFI removable media binary "  \
+					"efi/boot/"BOOTEFI_NAME"; "       \
+				"run boot_efi_binary; "                   \
+				"echo EFI LOAD FAILED: continuing...; "   \
+		"fi; "
+#define SCAN_DEV_FOR_EFI "run scan_dev_for_efi;"
+#else
+#define BOOTENV_SHARED_EFI
+#define SCAN_DEV_FOR_EFI
+#endif
+
 #ifdef CONFIG_CMD_SATA
 #define BOOTENV_SHARED_SATA	BOOTENV_SHARED_BLKDEV(sata)
 #define BOOTENV_DEV_SATA	BOOTENV_DEV_BLKDEV
@@ -217,6 +259,7 @@
 	BOOTENV_SHARED_SCSI \
 	BOOTENV_SHARED_IDE \
 	BOOTENV_SHARED_UBIFS \
+	BOOTENV_SHARED_EFI \
 	"boot_prefixes=/ /boot/\0" \
 	"boot_scripts=boot.scr.uimg boot.scr\0" \
 	"boot_script_dhcp=boot.scr.uimg\0" \
@@ -258,7 +301,9 @@
 		"for prefix in ${boot_prefixes}; do "                     \
 			"run scan_dev_for_extlinux; "                     \
 			"run scan_dev_for_scripts; "                      \
-		"done\0"                                                  \
+		"done;"                                                   \
+		SCAN_DEV_FOR_EFI                                          \
+		"\0"                                                      \
 	\
 	"scan_dev_for_boot_part="                                         \
 		"part list ${devtype} ${devnum} -bootable devplist; "     \
Signature · 1 line
2.6.2
#35 lore raw

PATCH 14/16 efi_loader: Add distro boot script for removable media

Quoted 11 lines · Leif Lindholm
Quoted 3 lines · Alexander Graf

UEFI defines a simple boot protocol for removable media. There we should look at the EFI (first GPT FAT) partition and search for /efi/boot/bootXXX.efi with XXX being different between different platforms (x86, x64, arm, aa64, ...).

One comment, one question.
Comment:
It's not really "the first GPT FAT" - it's the partition marked with the EFI System Partition type (containing a FAT filesystem). This is actually defined for MBR partition tables too, but I'd be quite happy for that bit of legacy to be left out.

Have you ever seen it as not the first partition? :)

Quoted 3 lines

Question:
Does U-Boot support El Torito for iso images?

There is some code for el torito in the iso driver. But it looks more like output for an info command rather than actual boot logic.

Alex

#36 lore raw

PATCH 14/16 efi_loader: Add distro boot script for removable media

In reply to Alexander Graf
Quoted 24 lines · Alexander Graf

UEFI defines a simple boot protocol for removable media. There we should look at the EFI (first GPT FAT) partition and search for /efi/boot/bootXXX.efi with XXX being different between different platforms (x86, x64, arm, aa64, ...).

This patch implements a simple version of that protocol for the default distro boot script. With this we can automatically boot from valid UEFI enabled removable media.

Because from all I could see U-Boot by default doesn't deliver device tree blobs with its firmware, we also need to load the dtb from somewhere. Traverse the same EFI partition for an fdt file that fits our current board so that an OS receives a valid device tree when booted automatically.

---
 include/config_distro_bootcmd.h | 47 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 46 insertions(+), 1 deletion(-)
include/config_distro_bootcmd.h +0 -0
index 37c6b43..c19f1b0 100644

include/config_distro_bootcmd.h line 99

Quoted 10 lines
include/config_distro_bootcmd.h +7 -0
@@ -90,6 +90,48 @@
 	BOOT_TARGET_DEVICES_references_UBIFS_without_CONFIG_CMD_UBIFS
 #endif
 
+#ifdef CONFIG_EFI_LOADER
+#if defined(CONFIG_ARM64)
+#define BOOTEFI_NAME "bootaa64.efi"
+#elif defined(CONFIG_ARM)
+#define BOOTEFI_NAME "bootarm.efi"
+#endif
+#endif

Actually, since Simon is testing this series also on Minnowboard (at least MAX), maybe add bootx64.efi and/or bootia32.efi as well?

I presume U-Boot for ia64 is not a thing? ;)

Quoted 60 lines
include/config_distro_bootcmd.h +39 -1
+
+#ifdef BOOTEFI_NAME
+#define BOOTENV_SHARED_EFI                                                \
+	"boot_efi_binary="                                                \
+		"load ${devtype} ${devnum}:${distro_bootpart} "           \
+			"${kernel_addr_r} efi/boot/"BOOTEFI_NAME"; "      \
+		"bootefi ${kernel_addr_r}\0"                              \
+	\
+	"load_efi_dtb="                                                   \
+		"load ${devtype} ${devnum}:${distro_bootpart} "           \
+			"${fdt_addr_r} ${prefix}${fdt_name}; "           \
+		"fdt addr ${fdt_addr_r}\0"                                \
+	\
+	"efi_dtb_prefixes=/ /dtb/ /dtb/current/\0"                        \
+	"scan_dev_for_efi="                                               \
+		"for prefix in ${efi_dtb_prefixes}; do "                  \
+			"if test -e ${devtype} "                          \
+					"${devnum}:${distro_bootpart} "   \
+					"${prefix}${fdt_name}; then "     \
+				"run load_efi_dtb; "                      \
+			"fi;"                                             \
+		"done;"                                                   \
+		"if test -e ${devtype} ${devnum}:${distro_bootpart} "     \
+					"efi/boot/"BOOTEFI_NAME"; then "  \
+				"echo Found EFI removable media binary "  \
+					"efi/boot/"BOOTEFI_NAME"; "       \
+				"run boot_efi_binary; "                   \
+				"echo EFI LOAD FAILED: continuing...; "   \
+		"fi; "
+#define SCAN_DEV_FOR_EFI "run scan_dev_for_efi;"
+#else
+#define BOOTENV_SHARED_EFI
+#define SCAN_DEV_FOR_EFI
+#endif
+
 #ifdef CONFIG_CMD_SATA
 #define BOOTENV_SHARED_SATA	BOOTENV_SHARED_BLKDEV(sata)
 #define BOOTENV_DEV_SATA	BOOTENV_DEV_BLKDEV
@@ -217,6 +259,7 @@
 	BOOTENV_SHARED_SCSI \
 	BOOTENV_SHARED_IDE \
 	BOOTENV_SHARED_UBIFS \
+	BOOTENV_SHARED_EFI \
 	"boot_prefixes=/ /boot/\0" \
 	"boot_scripts=boot.scr.uimg boot.scr\0" \
 	"boot_script_dhcp=boot.scr.uimg\0" \
@@ -258,7 +301,9 @@
 		"for prefix in ${boot_prefixes}; do "                     \
 			"run scan_dev_for_extlinux; "                     \
 			"run scan_dev_for_scripts; "                      \
-		"done\0"                                                  \
+		"done;"                                                   \
+		SCAN_DEV_FOR_EFI                                          \
+		"\0"                                                      \
 	\
 	"scan_dev_for_boot_part="                                         \
 		"part list ${devtype} ${devnum} -bootable devplist; "     \
Signature · 1 line
2.6.2
#37 lore raw

PATCH 14/16 efi_loader: Add distro boot script for removable media

Quoted 32 lines

Am 03.02.2016 um 12:36 schrieb Leif Lindholm <[email protected]>:

Quoted 28 lines · Alexander Graf

UEFI defines a simple boot protocol for removable media. There we should look at the EFI (first GPT FAT) partition and search for /efi/boot/bootXXX.efi with XXX being different between different platforms (x86, x64, arm, aa64, ...).

This patch implements a simple version of that protocol for the default distro boot script. With this we can automatically boot from valid UEFI enabled removable media.

Because from all I could see U-Boot by default doesn't deliver device tree blobs with its firmware, we also need to load the dtb from somewhere. Traverse the same EFI partition for an fdt file that fits our current board so that an OS receives a valid device tree when booted automatically.

---
include/config_distro_bootcmd.h | 47 ++++++++++++++++++++++++++++++++++++++++-

1 file changed, 46 insertions(+), 1 deletion(-)
include/config_distro_bootcmd.h +0 -0
index 37c6b43..c19f1b0 100644
@@ -90,6 +90,48 @@
   BOOT_TARGET_DEVICES_references_UBIFS_without_CONFIG_CMD_UBIFS
 #endif
 
Quoted 10 lines

include/config_distro_bootcmd.h line 99

Quoted 7 lines
include/config_distro_bootcmd.h +7 -0
+#ifdef CONFIG_EFI_LOADER
+#if defined(CONFIG_ARM64)
+#define BOOTEFI_NAME "bootaa64.efi"
+#elif defined(CONFIG_ARM)
+#define BOOTEFI_NAME "bootarm.efi"
+#endif
+#endif

Actually, since Simon is testing this series also on Minnowboard (at least MAX), maybe add bootx64.efi and/or bootia32.efi as well?

We need more to support a new architecture - relocations for pe and u-boot for example. So adding the bootia32.efi bits in an x86 enable patch set makes the most sense imho.

Quoted 2 lines

I presume U-Boot for ia64 is not a thing? ;)

s/U-Boot for // :). Don't ride dead horses.

Alex

#38 lore raw

PATCH 14/16 efi_loader: Add distro boot script for removable media

Hi,

Quoted 43 lines · Alexander Graf
Quoted 32 lines

Am 03.02.2016 um 12:36 schrieb Leif Lindholm <[email protected]>:

Quoted 28 lines · Alexander Graf

UEFI defines a simple boot protocol for removable media. There we should look at the EFI (first GPT FAT) partition and search for /efi/boot/bootXXX.efi with XXX being different between different platforms (x86, x64, arm, aa64, ...).

This patch implements a simple version of that protocol for the default distro boot script. With this we can automatically boot from valid UEFI enabled removable media.

Because from all I could see U-Boot by default doesn't deliver device tree blobs with its firmware, we also need to load the dtb from somewhere. Traverse the same EFI partition for an fdt file that fits our current board so that an OS receives a valid device tree when booted automatically.

---
include/config_distro_bootcmd.h | 47 ++++++++++++++++++++++++++++++++++++++++-

1 file changed, 46 insertions(+), 1 deletion(-)
include/config_distro_bootcmd.h +0 -0
index 37c6b43..c19f1b0 100644
@@ -90,6 +90,48 @@
   BOOT_TARGET_DEVICES_references_UBIFS_without_CONFIG_CMD_UBIFS
 #endif
 
Quoted 10 lines

include/config_distro_bootcmd.h line 99

Quoted 7 lines
include/config_distro_bootcmd.h +7 -0
+#ifdef CONFIG_EFI_LOADER
+#if defined(CONFIG_ARM64)
+#define BOOTEFI_NAME "bootaa64.efi"
+#elif defined(CONFIG_ARM)
+#define BOOTEFI_NAME "bootarm.efi"
+#endif
+#endif

Actually, since Simon is testing this series also on Minnowboard (at least MAX), maybe add bootx64.efi and/or bootia32.efi as well?

Quoted 1 line

We need more to support a new architecture - relocations for pe and u-boot for example. So adding the bootia32.efi bits in an x86 enable patch set makes the most sense imho.

Yes, let's worry about that later.

Quoted 10 lines
Quoted 4 lines

I presume U-Boot for ia64 is not a thing? ;)

s/U-Boot for // :). Don't ride dead horses.

Alex

Regards,
Simon

#39 lore raw

PATCH 15/16 efi_loader: Add README section in README.efi

In reply to Alexander Graf

To preserve all cover letter knowledge of the status on UEFI payload support, let's add some sections to README.efi.

---
 doc/README.efi | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 68 insertions(+), 1 deletion(-)
doc/README.efi +68 -1
index 23a3cdd..5834bcc 100644
@@ -4,6 +4,27 @@
 # SPDX-License-Identifier:	GPL-2.0+
 #
 
+=========== Table of Contents ===========
+
+  1  U-Boot on EFI
+  1.1  In God's Name, Why?
+  1.2  Status
+  1.3  Build Instructions
+  1.4  Trying it out
+  1.5  Inner workings
+  1.6  EFI Application
+  1.7  EFI Payload
+  1.8  Tables
+  1.9  Interrupts
+  1.10 32/64-bit
+  1.11 Future work
+  1.12 Where is the code?
+
+  2  EFI on U-Boot
+  2.1  In God's Name, Why?
+  2.2  Status
+  1.3  Future work
+
 U-Boot on EFI
 =============
 This document provides information about U-Boot running on top of EFI, either
@@ -234,7 +255,6 @@ board/efi/efi-x86/efi.c
 common/cmd_efi.c
 	the 'efi' command
 
-
 --
 Ben Stoltz, Simon Glass
 Google, Inc
@@ -242,3 +262,50 @@ July 2015
 
 [1] http://www.qemu.org
 [2] http://www.tianocore.org/ovmf/
+
+-------------------------------------------------------------------------------
+
+EFI on U-Boot
+=============
+
+In addition to support for running U-Boot as a UEFI application, U-Boot itself
+can also expose the UEFI interfaces and thus allow UEFI payloads to run under
+it.
+
+In God's Name, Why?
+-------------------
+
+With this support in place, you can run any UEFI payload (such as the Linux
+kernel, grub2 or gummiboot) on U-Boot. This dramatically simplifies boot loader
+configuration, as U-Boot based systems now look and feel (almost) the same way
+as TianoCore based systems.
+
+Status
+------
+
+I am successfully able to run grub2 and Linux EFI binaries with this code on
+ARMv7 as well as AArch64 systems.
+
+When enabled, the resulting U-Boot binary only grows by ~10kb, so it's very
+light weight.
+
+All storage devices are directly accessible from the uEFI payload
+
+Removable media booting (search for /efi/boot/boota{a64,arm}.efi) is supported.
+
+Simple use cases like "Plug this SD card into my ARM device and it just
+boots into grub which boots into Linux", work very well.
+
+Future work
+-----------
+
+Of course, there are still a few things one could do on top:
+
+   - Improve disk media detection (don't scan, use what information we
+have)
+   - Add EFI variable support using NVRAM
+   - Add GFX support
+   - Make EFI Shell work
+   - Network device support
+   - Support for payload exit
+   - Payload Watchdog support
Signature · 1 line
2.6.2
#40 lore raw

PATCH 15/16 efi_loader: Add README section in README.efi

Quoted 7 lines · Alexander Graf

To preserve all cover letter knowledge of the status on UEFI payload support, let's add some sections to README.efi.

---
 doc/README.efi | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 68 insertions(+), 1 deletion(-)

This is fine, but if you are respinning for any reason, please add mention of the Kconfig options to enable to get this feature. And for 10kb, do you mean 10KB? A little 'b' always makes me thing of 'bit'.

Regards,
Simon

#41 lore raw

PATCH 16/16 efi_loader: Add MAINTAINERS entry

In reply to Alexander Graf

Now that everything's in place, let's add myself as the maintainer for the efi payload support.

---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)
MAINTAINERS +7 -0
index b387207..b3b0ab8 100644
@@ -230,6 +230,13 @@ F:	drivers/core/
 F:	include/dm/
 F:	test/dm/
 
+EFI PAYLOAD
+M:	Alexander Graf <[email protected]>
+S:	Maintained
+F:	include/efi_loader.h
+F:	lib/efi_loader/
+F:	cmd/bootefi.c
+
 FLATTENED DEVICE TREE
 M:	Simon Glass <[email protected]>
 S:	Maintained
Signature · 1 line
2.6.2