EFI payload / application support v2
Subsystems (2)
cover EFI payload / application support v2
This is my 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)
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 2 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-v2
or just use the readily compiled version from
https://build.opensuse.org/project/show/home:algraf:branches:Base:System
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
Alex
Alexander Graf (14):
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
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 ++ common/Makefile | 1 + common/board_r.c | 4 + common/cmd_bootefi.c | 159 +++++++ disk/part.c | 7 +- include/config_distro_bootcmd.h | 47 ++- include/efi_api.h | 197 +++++++-- include/efi_loader.h | 90 ++++ include/part.h | 8 + include/pe.h | 263 ++++++++++++ lib/Kconfig | 1 + lib/Makefile | 1 + lib/efi_loader/Kconfig | 9 + lib/efi_loader/Makefile | 11 + lib/efi_loader/efi_boottime.c | 856 ++++++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_console.c | 357 ++++++++++++++++ lib/efi_loader/efi_disk.c | 218 ++++++++++ lib/efi_loader/efi_image_loader.c | 202 +++++++++ lib/efi_loader/efi_runtime.c | 300 +++++++++++++ 27 files changed, 3130 insertions(+), 44 deletions(-) create mode 100644 arch/arm/lib/unaligned_64.c create mode 100644 common/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_runtime.c
Signature · 1 line
2.1.4
PATCH 1/14 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
@@ -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
@@ -41,6 +41,12 @@ typedef struct block_dev_desc {void *priv;/* driver private struct pointer */ }block_dev_desc_t; +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))
@@ -122,6 +128,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.1.4
PATCH 2/14 include/efi_api.h: Add more detailed API definitions
In reply to Alexander GrafThe 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.
--- include/efi_api.h | 197 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 160 insertions(+), 37 deletions(-)
include/efi_api.h +160 -37
@@ -17,11 +17,18 @@#include <efi.h> +/* Types and defines for EFI CreateEvent */ +enum efi_event_type { +EFI_TIMER_STOP =0 , +EFI_TIMER_PERIODIC =1 , +EFI_TIMER_RELATIVE =2 +}; +/* EFI Boot Services table */ struct efi_boot_services {struct efi_table_hdr hdr; -void *raise_tpl; -void *restore_tpl; + efi_status_t (EFIAPI *raise_tpl)(unsigned long new_tpl); + void (EFIAPI *restore_tpl)(unsigned long old_tpl); efi_status_t (EFIAPI *allocate_pages)(int, int, unsigned long, efi_physical_addr_t *);
@@ -32,21 +39,32 @@ struct efi_boot_services { efi_status_t (EFIAPI *allocate_pool)(int, unsigned long, void **); efi_status_t (EFIAPI *free_pool)(void *); -void *create_event; -void *set_timer; - efi_status_t(EFIAPI *wait_for_event)(unsigned long number_of_events, - void *event, unsigned long *index); -void *signal_event; -void *close_event; -void *check_event; - -void *install_protocol_interface; -void *reinstall_protocol_interface; -void *uninstall_protocol_interface; + efi_status_t (EFIAPI *create_event)(enum efi_event_type type, + unsigned long notify_tpl, +void (*notify_function) (void *event, void *context), + void *notify_context, void **event); + efi_status_t (EFIAPI *set_timer)(void *event, int type, + uint64_t trigger_time); + efi_status_t (EFIAPI *wait_for_event)(unsigned long number_of_events, + void *event, unsigned long *index); +efi_status_t (EFIAPI *signal_event)(void *event); +efi_status_t (EFIAPI *close_event)(void *event); +efi_status_t (EFIAPI *check_event)(void *event); + +efi_status_t (EFIAPI *install_protocol_interface)( + void **handle, efi_guid_t *protocol, + int protocol_interface_type, void *protocol_interface); +efi_status_t (EFIAPI *reinstall_protocol_interface)( + void *handle, efi_guid_t *protocol, + void *old_interface, void *new_interface); +efi_status_t (EFIAPI *uninstall_protocol_interface)(void *handle, + efi_guid_t *protocol, void *protocol_interface); efi_status_t (EFIAPI *handle_protocol)(efi_handle_t, efi_guid_t *, void **);void *reserved; -void *register_protocol_notify; +efi_status_t (EFIAPI *register_protocol_notify)( + efi_guid_t *protocol, void *event, + void **registration); efi_status_t (EFIAPI *locate_handle)( enum efi_locate_search_type search_type, efi_guid_t *protocol, void *search_key,
@@ -54,7 +72,8 @@ struct efi_boot_services {efi_status_t (EFIAPI *locate_device_path)(efi_guid_t *protocol, struct efi_device_path **device_path, efi_handle_t *device); -void *install_configuration_table; +efi_status_t (EFIAPI *install_configuration_table)( + efi_guid_t *guid, void *table); efi_status_t (EFIAPI *load_image)(bool boot_policiy, efi_handle_t parent_image,
@@ -66,17 +85,20 @@ struct efi_boot_services { efi_status_t (EFIAPI *exit)(efi_handle_t handle, efi_status_t exit_status, unsigned long exitdata_size, s16 *exitdata); -void *unload_image; +efi_status_t (EFIAPI *unload_image)(void *image_handle); efi_status_t (EFIAPI *exit_boot_services)(efi_handle_t, unsigned long);efi_status_t (EFIAPI *get_next_monotonic_count)(u64 *count); efi_status_t (EFIAPI *stall)(unsigned long usecs); -void *set_watchdog_timer; + efi_status_t (EFIAPI *set_watchdog_timer)(unsigned long timeout, + uint64_t watchdog_code, unsigned long data_size, + uint16_t *watchdog_data); efi_status_t(EFIAPI *connect_controller)(efi_handle_t controller_handle, efi_handle_t *driver_image_handle, struct efi_device_path *remaining_device_path, bool recursive); -void *disconnect_controller; +efi_status_t (EFIAPI *disconnect_controller)(void *controller_handle, + void *driver_image_handle, void *child_handle);#define EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL 0x00000001#define EFI_OPEN_PROTOCOL_GET_PROTOCOL 0x00000002#define EFI_OPEN_PROTOCOL_TEST_PROTOCOL 0x00000004
@@ -87,7 +109,9 @@ struct efi_boot_services { efi_guid_t *protocol, void **interface, efi_handle_t agent_handle, efi_handle_t controller_handle, u32 attributes); -void *close_protocol; +efi_status_t (EFIAPI *close_protocol)(void *handle, + efi_guid_t *protocol, void *agent_handle, + void *controller_handle); efi_status_t(EFIAPI *open_protocol_information)(efi_handle_t handle, efi_guid_t *protocol, struct efi_open_protocol_info_entry **entry_buffer,
@@ -99,12 +123,18 @@ struct efi_boot_services {enum efi_locate_search_type search_type, efi_guid_t *protocol, void *search_key, unsigned long *no_handles, efi_handle_t **buffer); -void *locate_protocol; -void *install_multiple_protocol_interfaces; -void *uninstall_multiple_protocol_interfaces; -void *calculate_crc32; -void *copy_mem; -void *set_mem; +efi_status_t (EFIAPI *locate_protocol)(efi_guid_t *protocol, + void *registration, void **protocol_interface); +efi_status_t (EFIAPI *install_multiple_protocol_interfaces)( + void **handle, ...); +efi_status_t (EFIAPI *uninstall_multiple_protocol_interfaces)( + void *handle, ...); +efi_status_t (EFIAPI *calculate_crc32)(void *data, + unsigned long data_size, uint32_t *crc32); + void (EFIAPI *copy_mem)(void *destination, void *source, +unsigned long length); + void (EFIAPI *set_mem)(void *buffer, unsigned long size, +uint8_t value);void *create_event_ex; };
@@ -121,12 +151,19 @@ enum efi_reset_type {struct efi_runtime_services {struct efi_table_hdr hdr; -void *get_time; -void *set_time; -void *get_wakeup_time; -void *set_wakeup_time; -void *set_virtual_address_map; -void *convert_pointer; + efi_status_t (EFIAPI *get_time)(struct efi_time *time, + struct efi_time_cap *capabilities); + efi_status_t (EFIAPI *set_time)(struct efi_time *time); + efi_status_t (EFIAPI *get_wakeup_time)(char *enabled, char *pending, + struct efi_time *time); + efi_status_t (EFIAPI *set_wakeup_time)(char enabled, + struct efi_time *time); + efi_status_t (EFIAPI *set_virtual_address_map)( + unsigned long memory_map_size, + unsigned long descriptor_size, + uint32_t descriptor_version, + struct efi_mem_desc *virtmap); +efi_status_t (*convert_pointer)(unsigned long dbg,void **address); efi_status_t (EFIAPI *get_variable)(s16 *variable_name, efi_guid_t *vendor, u32 *attributes, unsigned long *data_size, void *data);
@@ -136,7 +173,8 @@ struct efi_runtime_services {efi_status_t (EFIAPI *set_variable)(s16 *variable_name, efi_guid_t *vendor, u32 attributes, unsigned long data_size, void *data); -void *get_next_high_mono_count; +efi_status_t (EFIAPI *get_next_high_mono_count)( + uint32_t *high_count); void (EFIAPI *reset_system)(enum efi_reset_type reset_type,efi_status_t reset_status,unsigned long data_size,void *reset_data);
@@ -154,6 +192,18 @@ struct efi_runtime_services {EFI_GUID (0x5b1b31a1 ,0x9562 ,0x11d2 ,0x8e ,0x3f , \ 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b) +#define EFI_FDT_GUID \ + EFI_GUID(0xb1b621d5, 0xf19c, 0x41a5, \ + 0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0) + +struct efi_configuration_table +{ +efi_guid_t guid; +void *table; +}; + +#define EFI_SYSTEM_TABLE_SIGNATURE ((u64)0x5453595320494249ULL) +struct efi_system_table {struct efi_table_hdr hdr;unsigned long fw_vendor;/* physical addr of wchar_t vendor string */
@@ -163,13 +213,17 @@ struct efi_system_table {unsigned long con_out_handle;struct efi_simple_text_output_protocol *con_out;unsigned long stderr_handle; -unsigned long std_err; +struct efi_simple_text_output_protocol *std_err;struct efi_runtime_services *runtime;struct efi_boot_services *boottime;unsigned long nr_tables; -unsigned long tables; +struct efi_configuration_table *tables; }; +#define LOADED_IMAGE_GUID \ + EFI_GUID(0x5b1b31a1, 0x9562, 0x11d2, \ + 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b) +struct efi_loaded_image {u32 revision;void *parent_handle;
@@ -186,12 +240,60 @@ struct efi_loaded_image {unsigned long unload; }; +#define DEVICE_PATH_GUID \ + EFI_GUID(0x09576e91, 0x6d3f, 0x11d2, \ + 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b ) + +#define DEVICE_PATH_TYPE_END 0x7f +# define DEVICE_PATH_SUB_TYPE_END 0xff +struct efi_device_path {u8 type;u8 sub_type;u16 length; }; +#define DEVICE_PATH_TYPE_MEDIA_DEVICE 0x04 +# define DEVICE_PATH_SUB_TYPE_FILE_PATH 0x04 + +struct efi_device_path_file_path { +struct efi_device_path dp; +u16 str[16 ]; +}; + +#define BLOCK_IO_GUID \ + EFI_GUID(0x964e5b21, 0x6459, 0x11d2, \ + 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b) + +struct efi_block_io_media +{ +u32 media_id; +char removable_media; +char media_present; +char logical_partition; +char read_only; +char write_caching; +u8 pad[3 ]; +u32 block_size; +u32 io_align; +u8 pad2[4 ]; +u64 last_block; +}; + +struct efi_block_io { +u64 revision; +struct efi_block_io_media *media; + efi_status_t (EFIAPI *reset)(struct efi_block_io *this, + char extended_verification); + efi_status_t (EFIAPI *read_blocks)(struct efi_block_io *this, + u32 media_id, u64 lba, unsigned long buffer_size, + void *buffer); + efi_status_t (EFIAPI *write_blocks)(struct efi_block_io *this, + u32 media_id, u64 lba, unsigned long buffer_size, + void *buffer); + efi_status_t (EFIAPI *flush_blocks)(struct efi_block_io *this); +}; +struct simple_text_output_mode {s32 max_mode;s32 mode;
@@ -206,8 +308,9 @@ struct efi_simple_text_output_protocol {efi_status_t (EFIAPI *output_string)( struct efi_simple_text_output_protocol *this, const unsigned short *str); -void *test_string; - +efi_status_t (EFIAPI *test_string)( + struct efi_simple_text_output_protocol *this, + const unsigned short *str); efi_status_t(EFIAPI *query_mode)( struct efi_simple_text_output_protocol *this, unsigned long mode_number, unsigned long *columns,
@@ -223,7 +326,9 @@ struct efi_simple_text_output_protocol {efi_status_t (EFIAPI *set_cursor_position) ( struct efi_simple_text_output_protocol *this, unsigned long column, unsigned long row); - efi_status_t(EFIAPI *enable_cursor)(void *, bool enable); +efi_status_t (EFIAPI *enable_cursor)( + struct efi_simple_text_output_protocol *this, + bool enable);struct simple_text_output_mode *mode; };
@@ -241,4 +346,22 @@ struct efi_simple_input_interface {void *wait_for_key; }; +#define CONSOLE_CONTROL_GUID \ + EFI_GUID(0xf42f7782, 0x12e, 0x4c12, \ + 0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21) +#define EFI_CONSOLE_MODE_TEXT 0 +#define EFI_CONSOLE_MODE_GFX 1 + +struct efi_console_control_protocol +{ + efi_status_t (EFIAPI *get_mode)( + struct efi_console_control_protocol *this, int *mode, + char *uga_exists, char *std_in_locked); + efi_status_t (EFIAPI *set_mode)( + struct efi_console_control_protocol *this, int mode); + efi_status_t (EFIAPI *lock_std_in)( + struct efi_console_control_protocol *this, + uint16_t *password); +}; +#endif
Signature · 1 line
2.1.4
PATCH 2/14 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.
Signed-off-by Alexander Graf <[email protected]>--- include/efi_api.h | 197 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 160 insertions(+), 37 deletions(-)
But does the notify_function parameter need EFIAPI also?
PATCH 2/14 include/efi_api.h: Add more detailed API definitions
Quoted 7 lines · Simon Glass
Quoted 6 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 7 lines
Quoted 4 lines
Signed-off-by Alexander Graf <[email protected]>--- include/efi_api.h | 197 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 160 insertions(+), 37 deletions(-)Reviewed-by Simon Glass <[email protected]>But does the
notify_functionparameter need EFIAPI also?
Yes, probably. The reason nobody realized it is that there's nobody calling it today :). Only 32bit arm and ia64 grub2 make use of it. Linux doesn't use the callback at all.
But I'll change it.
Alex
PATCH 3/14 efi_loader: Add PE image loader
In reply to Alexander GrafEFI 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+
--- include/efi_loader.h | 23 ++++ include/pe.h | 263 ++++++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_image_loader.c | 178 ++++++++++++++++++++++++++ 3 files changed, 464 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 +23 -0
@@ -0,0 +1,23 @@ +/* +* 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); + +#define EFI_LOADER_POOL_SIZE (128 * 1024 * 1024) +void *efi_loader_alloc (uint64_t len);
include/pe.h new file +263 -0
263 lines not on this page · raw message
lib/efi_loader/efi_image_loader.c new file +178 -0
178 lines not on this page · raw message
Signature · 1 line
2.1.4
PATCH 3/14 efi_loader: Add PE image loader
Hi Alexander,
Quoted 329 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.
Signed-off-by Alexander Graf <[email protected]>---
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+--- include/efi_loader.h | 23 ++++ include/pe.h | 263 ++++++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_image_loader.c | 178 ++++++++++++++++++++++++++ 3 files changed, 464 insertions(+) create mode 100644 include/efi_loader.h create mode 100644 include/pe.h create mode 100644 lib/efi_loader/efi_image_loader.cinclude/efi_loader.h new file +23
-0@@ -0,0 +1,23 @@ +/* +* 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); + +#define EFI_LOADER_POOL_SIZE (128 * 1024 * 1024) +void *efi_loader_alloc (uint64_t len);include/pe.h new file +263
-0@@ -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 + +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 ; + +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 +3
-0@@ -0,0 +1,178 @@ +/* +* EFI image loader +*
lib/efi_loader/efi_image_loader.c line 13
Quoted 10 lines
lib/efi_loader/efi_image_loader.c new file +10
-0+* based partly on wine code +* +* Copyright (c) 2016 Alexander Graf +* +* SPDX-License-Identifier: GPL-2.0+ +*/ + +#include <common.h> +#include <pe.h> +#include <efi_loader.h>
This should go above pe.h to keep alpha ordering.
lib/efi_loader/efi_image_loader.c line 18
Quoted 5 lines
lib/efi_loader/efi_image_loader.c new file +5
-0+#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR ; + +#define ROUND_UP (val, round) ((val + (round - 1)) & ~(round - 1))
Can you use round_up()?
Quoted 32 lines
lib/efi_loader/efi_image_loader.c new file +32
-0+#define MB (1024 * 1024) + +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 efi_return_handle (void *handle,efi_guid_t *protocol, +void **protocol_interface,void *agent_handle, +void *controller_handle,uint32_t attributes) +{ + *protocol_interface = handle; +return EFI_SUCCESS ; +} + +/* Will be implemented in a later patch */ +void *efi_loader_alloc (uint64_t len) +{ +return NULL ; +} + +/* +* 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;
lib/efi_loader/efi_image_loader.c line 60
Quoted 10 lines
lib/efi_loader/efi_image_loader.c new file +10
-0+const uint16_t *relocs; +const IMAGE_BASE_RELOCATION *rel; +const IMAGE_BASE_RELOCATION *end; +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;
What does nt stand for?
Quoted 101 lines
lib/efi_loader/efi_image_loader.c new file +101
-0+ +#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 = §ions[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_loader_alloc (virt_size); +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_loader_alloc (virt_size); +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 = §ions[i]; +memset (efi_reloc + sec->VirtualAddress,0 , + sec->Misc.VirtualSize); +memcpy (efi_reloc + sec->VirtualAddress, + efi + sec->PointerToRawData, + sec->SizeOfRawData); + } + +/* Run through relocations */ + end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + rel_size); + +while (rel < end -1 && rel->SizeOfBlock) { + 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 :
lib/efi_loader/efi_image_loader.c line 171
Quoted 10 lines
lib/efi_loader/efi_image_loader.c new file +10
-0+ *x64 += (uint64_t )delta; +break ; +default : +printf ("Unknown Relocation off %x type %x\n" , + offset, type); + } + relocs++; + } + rel = (const IMAGE_BASE_RELOCATION *)relocs; + }
How about putting the relocation code in a separate function, as this one is getting large.
Quoted 10 lines
lib/efi_loader/efi_image_loader.c new file +7
-1+ +/* Populate the loaded image interface bits */ + loaded_image_info->image_base = efi; + loaded_image_info->image_size = image_size; + +return entry; +} --2.1.4
Quoted 4 lines
_______________________________________________
U-Boot mailing list
U-Boot at lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot
Regards,
Simon
PATCH 3/14 efi_loader: Add PE image loader
Quoted 344 lines · Simon Glass
Hi Alexander,
Quoted 329 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.
Signed-off-by Alexander Graf <[email protected]>---
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+--- include/efi_loader.h | 23 ++++ include/pe.h | 263 ++++++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_image_loader.c | 178 ++++++++++++++++++++++++++ 3 files changed, 464 insertions(+)create mode 100644
include/efi_loader.hcreate mode 100644include/pe.h
create mode 100644lib/efi_loader/efi_image_loader.cinclude/efi_loader.h new file +23
-0@@ -0,0 +1,23 @@ +/* +* 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); + +#define EFI_LOADER_POOL_SIZE (128 * 1024 * 1024) +void *efi_loader_alloc (uint64_t len);include/pe.h new file +263
-0@@ -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 + +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 ; + +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 +3
-0@@ -0,0 +1,178 @@ +/* +* EFI image loader +* lib/efi_loader/efi_image_loader.c line 13
Quoted 10 lines
lib/efi_loader/efi_image_loader.c new file +10
-0+* based partly on wine code +* +* Copyright (c) 2016 Alexander Graf +* +* SPDX-License-Identifier: GPL-2.0+ +*/ + +#include <common.h> +#include <pe.h> +#include <efi_loader.h> This should go above
pe.hto keep alpha ordering.
Quoted 6 lines
lib/efi_loader/efi_image_loader.c line 18
Quoted 5 lines
lib/efi_loader/efi_image_loader.c new file +5
-0+#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR ; + +#define ROUND_UP (val, round) ((val + (round - 1)) & ~(round - 1))Can you use
round_up()?
Turns out this just disappears with the new memory code :).
Quoted 36 lines
Quoted 36 lines
lib/efi_loader/efi_image_loader.c new file +35
-0+#define MB (1024 * 1024) + +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 efi_return_handle (void *handle,efi_guid_t *protocol, +void **protocol_interface,void *agent_handle, +void *controller_handle,uint32_t attributes) +{ + *protocol_interface = handle; +return EFI_SUCCESS ; +} + +/* Will be implemented in a later patch */ +void *efi_loader_alloc (uint64_t len) +{ +return NULL ; +} + +/* +* 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 uint16_t *relocs; +const IMAGE_BASE_RELOCATION *rel; +const IMAGE_BASE_RELOCATION *end;
Quoted 8 lines
lib/efi_loader/efi_image_loader.c line 60
Quoted 7 lines
lib/efi_loader/efi_image_loader.c new file +7
-0+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;What does nt stand for?
This is PE speech :). These headers probably got introduced back with Windows NT, so that's why they're called that way.
Quoted 106 lines
Quoted 106 lines
lib/efi_loader/efi_image_loader.c new file +105
-0+ +#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 = §ions[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_loader_alloc (virt_size); +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_loader_alloc (virt_size); +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 = §ions[i]; +memset (efi_reloc + sec->VirtualAddress,0 , + sec->Misc.VirtualSize); +memcpy (efi_reloc + sec->VirtualAddress, + efi + sec->PointerToRawData, + sec->SizeOfRawData); + } + +/* Run through relocations */ + end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + rel_size); + +while (rel < end -1 && rel->SizeOfBlock) { + 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",
Quoted 8 lines
lib/efi_loader/efi_image_loader.c line 171
Quoted 6 lines
lib/efi_loader/efi_image_loader.c new file +6
-0+ offset, type); + } + relocs++; + } + rel = (const IMAGE_BASE_RELOCATION *)relocs; + }How about putting the relocation code in a separate function, as this one is getting large.
Great idea.
Alex
PATCH 4/14 efi_loader: Add boot time services
In reply to Alexander GrafWhen 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+
--- include/efi_loader.h | 51 +++ lib/efi_loader/efi_boottime.c | 761 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 812 insertions(+) create mode 100644 lib/efi_loader/efi_boottime.c
include/efi_loader.h +51 -0
@@ -6,18 +6,69 @@* 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; +struct efi_class_map { +const efi_guid_t *guid; +const void *interface; +}; + +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); +}; + +struct efi_object { +struct list_head link; +struct efi_handler protocols[4 ]; +void *handle; +}; +extern struct list_head efi_obj_list; +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_timer_check (void );void *efi_load_pe (void *efi,struct efi_loaded_image *loaded_image_info); +void efi_save_gd (void ); +void efi_restore_gd (void ); +efi_status_t efi_exit_func (efi_status_t ret);#define EFI_LOADER_POOL_SIZE (128 * 1024 * 1024)void *efi_loader_alloc (uint64_t len); + +#else /* defined(EFI_LOADER) */ + +static inline void efi_restore_gd (void ) { } + +#endif
lib/efi_loader/efi_boottime.c new file +761 -0
761 lines not on this page · raw message
Signature · 1 line
2.1.4
PATCH 4/14 efi_loader: Add boot time services
Quoted 233 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.
Signed-off-by Alexander Graf <[email protected]>---
v1 -> v2:
- Fix typo s/does now/does not/
- Add #ifdefs around header to allow inclusion whenefi_loaderis disabled
- Add stubefi_restore_gd()function whenefi_loaderis disabled
- Disable debug
- Mark runtime region as such
- Fix up memory map
- Allowefi_restore_gdto 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+--- include/efi_loader.h | 51 +++ lib/efi_loader/efi_boottime.c | 761 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 812 insertions(+) create mode 100644 lib/efi_loader/efi_boottime.cinclude/efi_loader.h +51
-0@@ -6,18 +6,69 @@* 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; +struct efi_class_map { +const efi_guid_t *guid; +const void *interface; +}; + +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); +}; + +struct efi_object { +struct list_head link; +struct efi_handler protocols[4 ]; +void *handle; +}; +extern struct list_head efi_obj_list; +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_timer_check (void );void *efi_load_pe (void *efi,struct efi_loaded_image *loaded_image_info); +void efi_save_gd (void ); +void efi_restore_gd (void ); +efi_status_t efi_exit_func (efi_status_t ret);#define EFI_LOADER_POOL_SIZE (128 * 1024 * 1024)void *efi_loader_alloc (uint64_t len); + +#else /* defined(EFI_LOADER) */ + +static inline void efi_restore_gd (void ) { } + +#endif lib/efi_loader/efi_boottime.c new file +119
-0@@ -0,0 +1,761 @@ +/* +* 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 ; + +/* +* 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; + +/* +* 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[] = { + { + .guid =EFI_FDT_GUID , + }, +}; + +/* +* 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) +{ +#ifdef DEBUG_EFI +printf ("EFI: App called into unimplemented function %s\n" , funcname); +#endif +return EFI_EXIT (EFI_UNSUPPORTED ); +} + +static unsigned long efi_raise_tpl (unsigned long new_tpl) +{ +EFI_ENTRY ("0x%lx" , new_tpl); +return EFI_EXIT (0 ); +} + +static void efi_restore_tpl (unsigned long old_tpl) +{ +EFI_ENTRY ("0x%lx" , old_tpl); +EFI_EXIT (efi_unsupported (__func__)); +} + +static void *efi_alloc (uint64_t len,int memory_type) +{ +switch (memory_type) { +default : +return malloc (len); + } +} + +static 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 ; + +EFI_ENTRY ("%d, %d, 0x%lx, %p" , type, memory_type, pages, memory); + +switch (type) { +case 0 : +/* Any page means we can go to efi_alloc */ + *memory = (unsigned long )efi_alloc (len, memory_type); +break ; +case 1 :
lib/efi_loader/efi_boottime.c line 129
Quoted 10 lines
lib/efi_loader/efi_boottime.c new file +10
-0+/* Max address */ +if (gd->relocaddr < *memory) { + *memory = (unsigned long )efi_alloc (len, memory_type); +break ; + } + r =EFI_NOT_FOUND ; +break ; +case 2 : +/* Exact address, grant it. The addr is already in *memory. */ +break ;
While potentially not necessary for the basic grub->linux flow, for compliance this one should really:
- Register a memory map entry.
- Check for clashes with existing memory map entries.
Quoted 16 lines
lib/efi_loader/efi_boottime.c new file +16
-0+default : +/* UEFI doesn't specify other allocation types */ + r =EFI_INVALID_PARAMETER ; +break ; + } + +return EFI_EXIT (r); +} + +static 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 */ +EFI_ENTRY ("%" PRIx64", 0x%lx" , memory, pages); +return EFI_EXIT (EFI_SUCCESS ); +} +
lib/efi_loader/efi_boottime.c line 155
Quoted 10 lines
lib/efi_loader/efi_boottime.c new file +10
-0+/* Will be implemented in a later patch */ +static 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) +{ +EFI_ENTRY ("%p, %p, %p, %p, %p" , memory_map_size, memory_map, map_key, + descriptor_size, descriptor_version); +return EFI_EXIT (EFI_UNSUPPORTED );
(Invalid, but will be overridden in later patch :)
Quoted 82 lines
lib/efi_loader/efi_boottime.c new file +82
-0+} + +static efi_status_t 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 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; + +static efi_status_t 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 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; + +if (trigger32 < trigger_time) { +printf ("WARNING: Truncating timer from %" PRIx64" to %x\n" , + trigger_time, trigger32); + } + +EFI_ENTRY ("%p, %d, %" PRIx64, event, type, trigger_time); +if (event != &efi_event) { +/* We only support one event at a time */ +return EFI_EXIT (EFI_INVALID_PARAMETER ); + } +
lib/efi_loader/efi_boottime.c line 247
Quoted 10 lines
lib/efi_loader/efi_boottime.c new file +10
-0+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_UNSUPPORTED );
Not a valid return code. EFI_INVALID_PARAMETER is a better choice.
lib/efi_loader/efi_boottime.c line 259
Quoted 12 lines
lib/efi_loader/efi_boottime.c new file +12
-0+ } + efi_event.trigger_type = type; + efi_event.trigger_time = trigger_time; + +return EFI_EXIT (EFI_SUCCESS ); +} + +static efi_status_t efi_wait_for_event (unsigned long num_events,void *event, +unsigned long *index) +{ +EFI_ENTRY ("%ld, %p, %p" , num_events, event, index); +return EFI_EXIT (efi_unsupported (__func__));
I can't see a way this function can really be left out. None of the valid return values make sense otherwise.
lib/efi_loader/efi_boottime.c line 265
Quoted 6 lines
lib/efi_loader/efi_boottime.c new file +6
-0+} + +static efi_status_t efi_signal_event (void *event) +{ +EFI_ENTRY ("%p" , event); +return EFI_EXIT (efi_unsupported (__func__));
Only valid return is EFI_SUCCESS.
lib/efi_loader/efi_boottime.c line 271
Quoted 6 lines
lib/efi_loader/efi_boottime.c new file +6
-0+} + +static efi_status_t efi_close_event (void *event) +{ +EFI_ENTRY ("%p" , event); +return EFI_EXIT (efi_unsupported (__func__));
Only valid return is EFI_SUCCESS.
lib/efi_loader/efi_boottime.c line 277
Quoted 6 lines
lib/efi_loader/efi_boottime.c new file +6
-0+} + +static efi_status_t efi_check_event (void *event) +{ +EFI_ENTRY ("%p" , event); +return EFI_EXIT (efi_unsupported (__func__));
EFI_NOT_READY?
lib/efi_loader/efi_boottime.c line 287
Quoted 10 lines
lib/efi_loader/efi_boottime.c new file +10
-0+} + +static efi_status_t 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_unsupported (__func__));
EFI_OUT_OF_RESOURCES?
lib/efi_loader/efi_boottime.c line 296
Quoted 9 lines
lib/efi_loader/efi_boottime.c new file +9
-0+} +static efi_status_t 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_unsupported (__func__));
EFI_NOT_FOUND/EFI_ACCESS_DENIED?
lib/efi_loader/efi_boottime.c line 304
Quoted 8 lines
lib/efi_loader/efi_boottime.c new file +8
-0+} + +static efi_status_t 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_unsupported (__func__));
EFI_NOT_FOUND?
lib/efi_loader/efi_boottime.c line 311
Quoted 7 lines
lib/efi_loader/efi_boottime.c new file +7
-0+} + +static efi_status_t efi_register_protocol_notify (efi_guid_t *protocol, +void *event,void **registration) +{e + EFI_ENTRY("%p, %p, %p", protocol, event, registration); +return EFI_EXIT (efi_unsupported (__func__));
EFI_OUT_OF_RESOURCES is the only valid return value that doesn't mean "one of the inputs is NULL".
Quoted 58 lines
lib/efi_loader/efi_boottime.c new file +58
-0+} + +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 && !memcmp (guid, protocol,sizeof (efi_guid_t))) +return 0 ; + } +return -1 ; + } + +return -1 ; +} + +static efi_status_t 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; + } + } +
lib/efi_loader/efi_boottime.c line 379
Quoted 10 lines
lib/efi_loader/efi_boottime.c new file +10
-0+ *buffer_size = size; +return EFI_EXIT (EFI_SUCCESS ); +} + +static efi_status_t 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_unsupported (__func__));
EFI_NOT_FOUND?
lib/efi_loader/efi_boottime.c line 386
Quoted 7 lines
lib/efi_loader/efi_boottime.c new file +7
-0+} + +static efi_status_t efi_install_configuration_table (efi_guid_t *guid,void *table) +{ +EFI_ENTRY ("%p, %p" , guid, table); +/* Only allow overriding of the FDT */ +if (memcmp (guid, &efi_conf_table[0 ].guid,sizeof (efi_guid_t)))
Worth creating a guidcmp() macro?
lib/efi_loader/efi_boottime.c line 387
Quoted 1 line
lib/efi_loader/efi_boottime.c new file +1
-0+return EFI_EXIT (EFI_UNSUPPORTED );
Not a valid return value.EFI_OUT_OF_RESOURCES would sort of be correct in this scenario (and my proposed variation below).
lib/efi_loader/efi_boottime.c line 389
Quoted 2 lines
lib/efi_loader/efi_boottime.c new file +2
-0+ + efi_conf_table[0 ].table = table;
The config table infrastructure should also be possible to make more extensible without really increasing the code complexity. Could just loop over NumberOfTableEntries in the system table, and check against a hardcoded max value set by the size of the efi_conf_table struct.
Quoted 23 lines
lib/efi_loader/efi_boottime.c new file +23
-0+return EFI_EXIT (EFI_SUCCESS ); +} + +static efi_status_t 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));
lib/efi_loader/efi_boottime.c line 422
Quoted 10 lines
lib/efi_loader/efi_boottime.c new file +10
-0+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 ); + }
Do we need a Dcache clean to PoU at this point (in ARM terms, whatever that means in core code), and Icache invalidation? Or should that be part of efi_load_pe?
Quoted 23 lines
lib/efi_loader/efi_boottime.c new file +23
-0+ + *image_handle = info; +list_add_tail (&obj->link, &efi_obj_list); + +return EFI_EXIT (EFI_SUCCESS ); +} + +static efi_status_t 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 */
lib/efi_loader/efi_boottime.c line 455
Quoted 10 lines
lib/efi_loader/efi_boottime.c new file +10
-0+return EFI_EXIT (EFI_SUCCESS ); +} + +static efi_status_t 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__));
There's not really a valid "no" return value here other than "that ain't me, mate".
Quoted 25 lines
lib/efi_loader/efi_boottime.c new file +25
-0+} + +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 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 490
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. +*/
Just a note: the UEFI support in the 32bit Linux loader in GRUB (written by me) is a bit crap. Now we actually have the kernel UEFI support merged, I should just unify it with the 64bit one, at which point this requirement goes away.
Quoted 16 lines
lib/efi_loader/efi_boottime.c new file +16
-0+if (efi_is_direct_boot) +cleanup_before_linux (); +#endif +} + +static efi_status_t 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 (); +
lib/efi_loader/efi_boottime.c line 516
Quoted 10 lines
lib/efi_loader/efi_boottime.c new file +10
-0+/* Give the payload some time to boot */ +WATCHDOG_RESET (); + +return EFI_EXIT (EFI_SUCCESS ); +} + +static efi_status_t efi_get_next_monotonic_count (uint64_t *count) +{ +EFI_ENTRY ("%p" , count); + *count =timer_get_us ();
This doesn't actually conform to the spec.
While we may not explicitly care enough to implement the full defined behaviour in this emulation (persistent top 32 bits), we should still make sure the lower 32 bits are increment by explicitly 1 every time it's called.
Basically, could you just turn this into a static incrementing by 1 on every call?
Quoted 18 lines
lib/efi_loader/efi_boottime.c new file +18
-0+return EFI_EXIT (EFI_SUCCESS ); +} + +static efi_status_t efi_stall (unsigned long microseconds) +{ +EFI_ENTRY ("%ld" , microseconds); +udelay (microseconds); +return EFI_EXIT (EFI_SUCCESS ); +} + +static efi_status_t 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__));
lib/efi_loader/efi_boottime.c line 544
Quoted 10 lines
lib/efi_loader/efi_boottime.c new file +10
-0+} + +static efi_status_t 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_unsupported (__func__));
EFI_NOT_FOUND?
lib/efi_loader/efi_boottime.c line 553
Quoted 9 lines
lib/efi_loader/efi_boottime.c new file +9
-0+} + +static efi_status_t 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_unsupported (__func__));
EFI_INVALID_PARAMETER?
lib/efi_loader/efi_boottime.c line 561
Quoted 8 lines
lib/efi_loader/efi_boottime.c new file +8
-0+} + +static efi_status_t 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_unsupported (__func__));
EFI_NOT_FOUND?
lib/efi_loader/efi_boottime.c line 571
Quoted 10 lines
lib/efi_loader/efi_boottime.c new file +10
-0+} + +static efi_status_t 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_unsupported (__func__));
EFI_NOT_FOUND?
lib/efi_loader/efi_boottime.c line 580
Quoted 9 lines
lib/efi_loader/efi_boottime.c new file +9
-0+} + +static efi_status_t 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_unsupported (__func__));
EFI_OUT_OF_RESOURCES? (only supported return code not based on one of the inputs being NULL)
lib/efi_loader/efi_boottime.c line 590
Quoted 10 lines
lib/efi_loader/efi_boottime.c new file +10
-0+} + +static efi_status_t 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_unsupported (__func__));
EFI_NOT_FOUND?
Quoted 21 lines
lib/efi_loader/efi_boottime.c new file +21
-0+} + +static struct efi_class_map efi_class_maps[] = { + { + .guid = &efi_guid_console_control, + .interface = &efi_console_control + }, +}; + +static efi_status_t efi_locate_protocol (efi_guid_t *protocol,void *registration, +void **protocol_interface) +{ +efi_status_t r =EFI_NOT_FOUND ; +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 (!memcmp (protocol, curmap->guid,sizeof (efi_guid_t))) { + *protocol_interface = (void *)curmap->interface; +return EFI_EXIT (EFI_SUCCESS );
lib/efi_loader/efi_boottime.c line 621
Quoted 10 lines
lib/efi_loader/efi_boottime.c new file +10
-0+ } + } + +return EFI_EXIT (r); +} + +static efi_status_t efi_install_multiple_protocol_interfaces (void **handle, ...) +{ +EFI_ENTRY ("%p" , handle); +return EFI_EXIT (efi_unsupported (__func__));
EFI_OUT_OF_RESOURCES?
lib/efi_loader/efi_boottime.c line 627
Quoted 6 lines
lib/efi_loader/efi_boottime.c new file +6
-0+} + +static efi_status_t efi_uninstall_multiple_protocol_interfaces (void *handle, ...) +{ +EFI_ENTRY ("%p" , handle); +return EFI_EXIT (efi_unsupported (__func__));
EFI_INVALID_PARAMETER?
Quoted 55 lines
lib/efi_loader/efi_boottime.c new file +55
-0+} + +static efi_status_t 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 efi_copy_mem (void *destination,void *source,unsigned long length) +{ +EFI_ENTRY ("%p, %p, %ld" , destination, source, length); +memcpy (destination, source, length); +} + +static void 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 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 (!memcmp (hprotocol, protocol,sizeof (efi_guid_t))) { + r = handler->open (handle, protocol, + protocol_interface, agent_handle, + controller_handle, attributes); + goto out; + } + } + } + +out:
lib/efi_loader/efi_boottime.c line 692
Quoted 10 lines
lib/efi_loader/efi_boottime.c new file +10
-0+return EFI_EXIT (r); +} + +static efi_status_t 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 ); +}
(Remaining status codes above all fine.)
Quoted 72 lines
lib/efi_loader/efi_boottime.c new file +69
-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, + .free_pages = efi_free_pages, + .get_memory_map = efi_get_memory_map, + .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 =1 , + .tables = (void *)efi_conf_table, +};Signature · 1 line
2.1.4
PATCH 4/14 efi_loader: Add boot time services
Quoted 537 lines · Leif Lindholm
Quoted 233 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.
Signed-off-by Alexander Graf <[email protected]>---
v1 -> v2:
- Fix typo s/does now/does not/
- Add #ifdefs around header to allow inclusion whenefi_loaderis disabled
- Add stubefi_restore_gd()function whenefi_loaderis disabled
- Disable debug
- Mark runtime region as such
- Fix up memory map
- Allowefi_restore_gdto 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+--- include/efi_loader.h | 51 +++ lib/efi_loader/efi_boottime.c | 761 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 812 insertions(+)create mode 100644
lib/efi_loader/efi_boottime.cinclude/efi_loader.h +51
-0@@ -6,18 +6,69 @@* 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; +struct efi_class_map { +const efi_guid_t *guid; +const void *interface; +}; + +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); +}; + +struct efi_object { +struct list_head link; +struct efi_handler protocols[4 ]; +void *handle; +}; +extern struct list_head efi_obj_list; +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_timer_check (void );void *efi_load_pe (void *efi,struct efi_loaded_image *loaded_image_info); +void efi_save_gd (void ); +void efi_restore_gd (void ); +efi_status_t efi_exit_func (efi_status_t ret);#define EFI_LOADER_POOL_SIZE (128 * 1024 * 1024)void *efi_loader_alloc (uint64_t len); + +#else /* defined(EFI_LOADER) */ + +static inline void efi_restore_gd (void ) { } + +#endif lib/efi_loader/efi_boottime.c new file +119
-0@@ -0,0 +1,761 @@ +/* +* 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 ; + +/* +* 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; + +/* +* 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[] = { + { + .guid =EFI_FDT_GUID , + }, +}; + +/* +* 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) +{ +#ifdef DEBUG_EFI +printf ("EFI: App called into unimplemented function %s\n" , funcname); +#endif +return EFI_EXIT (EFI_UNSUPPORTED ); +} + +static unsigned long efi_raise_tpl (unsigned long new_tpl) +{ +EFI_ENTRY ("0x%lx" , new_tpl); +return EFI_EXIT (0 ); +} + +static void efi_restore_tpl (unsigned long old_tpl) +{ +EFI_ENTRY ("0x%lx" , old_tpl); +EFI_EXIT (efi_unsupported (__func__)); +} + +static void *efi_alloc (uint64_t len,int memory_type) +{ +switch (memory_type) { +default : +return malloc (len); + } +} + +static 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 ; + +EFI_ENTRY ("%d, %d, 0x%lx, %p" , type, memory_type, pages, memory); + +switch (type) { +case 0 : +/* Any page means we can go to efi_alloc */ + *memory = (unsigned long )efi_alloc (len, memory_type); +break ; +case 1 :lib/efi_loader/efi_boottime.c line 129
Quoted 10 lines
lib/efi_loader/efi_boottime.c new file +10
-0+/* Max address */ +if (gd->relocaddr < *memory) { + *memory = (unsigned long )efi_alloc (len, memory_type); +break ; + } + r =EFI_NOT_FOUND ; +break ; +case 2 : +/* Exact address, grant it. The addr is already in *memory. */ +break ;While potentially not necessary for the basic
grub->linuxflow, for compliance this one should really:
- Register a memory map entry.
- Check for clashes with existing memory map entries.Quoted 16 lines
lib/efi_loader/efi_boottime.c new file +16
-0+default : +/* UEFI doesn't specify other allocation types */ + r =EFI_INVALID_PARAMETER ; +break ; + } + +return EFI_EXIT (r); +} + +static 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 */ +EFI_ENTRY ("%" PRIx64", 0x%lx" , memory, pages); +return EFI_EXIT (EFI_SUCCESS ); +} +lib/efi_loader/efi_boottime.c line 155
Quoted 10 lines
lib/efi_loader/efi_boottime.c new file +10
-0+/* Will be implemented in a later patch */ +static 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) +{ +EFI_ENTRY ("%p, %p, %p, %p, %p" , memory_map_size, memory_map, map_key, + descriptor_size, descriptor_version); +return EFI_EXIT (EFI_UNSUPPORTED );(Invalid, but will be overridden in later patch :)
Quoted 82 lines
lib/efi_loader/efi_boottime.c new file +82
-0+} + +static efi_status_t 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 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; + +static efi_status_t 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 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; + +if (trigger32 < trigger_time) { +printf ("WARNING: Truncating timer from %" PRIx64" to %x\n" , + trigger_time, trigger32); + } + +EFI_ENTRY ("%p, %d, %" PRIx64, event, type, trigger_time); +if (event != &efi_event) { +/* We only support one event at a time */ +return EFI_EXIT (EFI_INVALID_PARAMETER ); + } +lib/efi_loader/efi_boottime.c line 247
Quoted 10 lines
lib/efi_loader/efi_boottime.c new file +10
-0+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_UNSUPPORTED );Not a valid return code.
EFI_INVALID_PARAMETERis a better choice.lib/efi_loader/efi_boottime.c line 259
Quoted 12 lines
lib/efi_loader/efi_boottime.c new file +12
-0+ } + efi_event.trigger_type = type; + efi_event.trigger_time = trigger_time; + +return EFI_EXIT (EFI_SUCCESS ); +} + +static efi_status_t efi_wait_for_event (unsigned long num_events,void *event, +unsigned long *index) +{ +EFI_ENTRY ("%ld, %p, %p" , num_events, event, index); +return EFI_EXIT (efi_unsupported (__func__));I can't see a way this function can really be left out. None of the valid return values make sense otherwise.
lib/efi_loader/efi_boottime.c line 265
Quoted 6 lines
lib/efi_loader/efi_boottime.c new file +6
-0+} + +static efi_status_t efi_signal_event (void *event) +{ +EFI_ENTRY ("%p" , event); +return EFI_EXIT (efi_unsupported (__func__));Only valid return is
EFI_SUCCESS.lib/efi_loader/efi_boottime.c line 271
Quoted 6 lines
lib/efi_loader/efi_boottime.c new file +6
-0+} + +static efi_status_t efi_close_event (void *event) +{ +EFI_ENTRY ("%p" , event); +return EFI_EXIT (efi_unsupported (__func__));Only valid return is
EFI_SUCCESS.lib/efi_loader/efi_boottime.c line 277
Quoted 6 lines
lib/efi_loader/efi_boottime.c new file +6
-0+} + +static efi_status_t efi_check_event (void *event) +{ +EFI_ENTRY ("%p" , event); +return EFI_EXIT (efi_unsupported (__func__));
EFI_NOT_READY?lib/efi_loader/efi_boottime.c line 287
Quoted 10 lines
lib/efi_loader/efi_boottime.c new file +10
-0+} + +static efi_status_t 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_unsupported (__func__));
EFI_OUT_OF_RESOURCES?lib/efi_loader/efi_boottime.c line 296
Quoted 9 lines
lib/efi_loader/efi_boottime.c new file +9
-0+} +static efi_status_t 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_unsupported (__func__));EFI_NOT_FOUND/EFI_ACCESS_DENIED?
lib/efi_loader/efi_boottime.c line 304
Quoted 8 lines
lib/efi_loader/efi_boottime.c new file +8
-0+} + +static efi_status_t 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_unsupported (__func__));
EFI_NOT_FOUND?lib/efi_loader/efi_boottime.c line 311
Quoted 7 lines
lib/efi_loader/efi_boottime.c new file +7
-0+} + +static efi_status_t efi_register_protocol_notify (efi_guid_t *protocol, +void *event,void **registration) +{e + EFI_ENTRY("%p, %p, %p", protocol, event, registration); +return EFI_EXIT (efi_unsupported (__func__));
EFI_OUT_OF_RESOURCESis the only valid return value that doesn't mean "one of the inputs is NULL".Quoted 58 lines
lib/efi_loader/efi_boottime.c new file +58
-0+} + +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 && !memcmp (guid, protocol,sizeof (efi_guid_t))) +return 0 ; + } +return -1 ; + } + +return -1 ; +} + +static efi_status_t 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; + } + } +lib/efi_loader/efi_boottime.c line 379
Quoted 10 lines
lib/efi_loader/efi_boottime.c new file +10
-0+ *buffer_size = size; +return EFI_EXIT (EFI_SUCCESS ); +} + +static efi_status_t 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_unsupported (__func__));
EFI_NOT_FOUND?lib/efi_loader/efi_boottime.c line 386
Quoted 7 lines
lib/efi_loader/efi_boottime.c new file +7
-0+} + +static efi_status_t efi_install_configuration_table (efi_guid_t *guid,void *table) +{ +EFI_ENTRY ("%p, %p" , guid, table); +/* Only allow overriding of the FDT */ +if (memcmp (guid, &efi_conf_table[0 ].guid,sizeof (efi_guid_t)))Worth creating a
guidcmp()macro?lib/efi_loader/efi_boottime.c line 387
Quoted 1 line
lib/efi_loader/efi_boottime.c new file +1
-0+return EFI_EXIT (EFI_UNSUPPORTED );Not a valid return value.
EFI_OUT_OF_RESOURCESwould sort of be correct in this scenario (and my proposed variation below).
Quoted 7 lines
lib/efi_loader/efi_boottime.c line 389
Quoted 2 lines
lib/efi_loader/efi_boottime.c new file +2
-0+ + efi_conf_table[0 ].table = table;The config table infrastructure should also be possible to make more extensible without really increasing the code complexity. Could just loop over NumberOfTableEntries in the system table, and check against a hardcoded max value set by the size of the
efi_conf_tablestruct.
I think I understood what you were saying and implemented it :). Let's see ;)
Quoted 29 lines
Quoted 29 lines
lib/efi_loader/efi_boottime.c new file +28
-0+return EFI_EXIT (EFI_SUCCESS ); +} + +static efi_status_t 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);
Quoted 8 lines
lib/efi_loader/efi_boottime.c line 422
Quoted 5 lines
lib/efi_loader/efi_boottime.c new file +5
-0+if (!info->reserved) { +free (info); +free (obj); +return EFI_EXIT (EFI_UNSUPPORTED ); + }Do we need a Dcache clean to PoU at this point (in ARM terms, whatever that means in core code), and Icache invalidation? Or should that be part of
efi_load_pe?
dcache clean makes a lot of sense, though it really belongs into efi_load_pe. We don't need an icache invalidate FWIW because the icache should be empty for the newly allocated region.
Quoted 28 lines
Quoted 28 lines
lib/efi_loader/efi_boottime.c new file +27
-0+ + *image_handle = info; +list_add_tail (&obj->link, &efi_obj_list); + +return EFI_EXIT (EFI_SUCCESS ); +} + +static efi_status_t 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 efi_exit(void *image_handle, long exit_status,
Quoted 8 lines
lib/efi_loader/efi_boottime.c line 455
Quoted 6 lines
lib/efi_loader/efi_boottime.c new file +6
-0+ 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__));There's not really a valid "no" return value here other than "that ain't me, mate".
I think it makes a lot of sense to support exiting, but I can't quite figure out how to do that without nasty longjmp() style asm code yet. So I'd say let's leave it at unsupported and think hard how we can actually implement it :).
Quoted 73 lines
Quoted 26 lines
lib/efi_loader/efi_boottime.c new file +25
-0+} + +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 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 490
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. +*/ Just a note: the UEFI support in the 32bit Linux loader in GRUB (written by me) is a bit crap. Now we actually have the kernel UEFI support merged, I should just unify it with the 64bit one, at which point this requirement goes away.
Quoted 16 lines
lib/efi_loader/efi_boottime.c new file +16
-0+if (efi_is_direct_boot) +cleanup_before_linux (); +#endif +} + +static efi_status_t 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 (); +lib/efi_loader/efi_boottime.c line 516
Quoted 10 lines
lib/efi_loader/efi_boottime.c new file +10
-0+/* Give the payload some time to boot */ +WATCHDOG_RESET (); + +return EFI_EXIT (EFI_SUCCESS ); +} + +static efi_status_t efi_get_next_monotonic_count (uint64_t *count) +{ +EFI_ENTRY ("%p" , count); + *count =timer_get_us ();This doesn't actually conform to the spec.
While we may not explicitly care enough to implement the full defined behaviour in this emulation (persistent top 32 bits), we should still make sure the lower 32 bits are increment by explicitly 1 every time it's called.
Quoted 2 lines
Basically, could you just turn this into a static incrementing by 1 on every call?
Sure :)
Alex
PATCH 4/14 efi_loader: Add boot time services
In reply to Alexander GrafHi Alexander,
Quoted 28 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.
Signed-off-by Alexander Graf <[email protected]>---
v1 -> v2:
- Fix typo s/does now/does not/
- Add #ifdefs around header to allow inclusion whenefi_loaderis disabled
- Add stubefi_restore_gd()function whenefi_loaderis disabled
- Disable debug
- Mark runtime region as such
- Fix up memory map
- Allowefi_restore_gdto 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+
Quoted 5 lines
--- include/efi_loader.h | 51 +++ lib/efi_loader/efi_boottime.c | 761 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 812 insertions(+) create mode 100644 lib/efi_loader/efi_boottime.c
But please see below.
Also, how does the timer get disabled? For me it seems to be enabled on boot so it always calls the trigger function.
Quoted 38 lines
include/efi_loader.h +22
-0@@ -6,18 +6,69 @@* 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; +struct efi_class_map {
include/efi_loader.h line 47
Quoted 10 lines
include/efi_loader.h +10
-0+const efi_guid_t *guid; +const void *interface; +}; + +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);
Can we add a name in here? It would be good to name each of these things so that we can print it out for debugging, etc.
Same also for efi_object. The guids are an unfortunate but effective obfuscation for people trying to figure out what is going on. I think we should try to keep things human-friendly.
include/efi_loader.h line 54
Quoted 7 lines
include/efi_loader.h +7
-0+}; + +struct efi_object { +struct list_head link; +struct efi_handler protocols[4 ]; +void *handle; +};
Can you add comments on the above structs?
Quoted 8 lines
include/efi_loader.h +3
-0+extern struct list_head efi_obj_list; +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_timer_check (void );void *efi_load_pe (void *efi,struct efi_loaded_image *loaded_image_info);
include/efi_loader.h line 72
Quoted 10 lines
include/efi_loader.h +7
-0+void efi_save_gd (void ); +void efi_restore_gd (void ); +efi_status_t efi_exit_func (efi_status_t ret);#define EFI_LOADER_POOL_SIZE (128 * 1024 * 1024)void *efi_loader_alloc (uint64_t len); + +#else /* defined(EFI_LOADER) */ + +static inline void efi_restore_gd (void ) { }
And the above functions
[snip]
Regards,
Simon
PATCH 4/14 efi_loader: Add boot time services
Quoted 36 lines · Simon Glass
Hi Alexander,
Quoted 28 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.
Signed-off-by Alexander Graf <[email protected]>---
v1 -> v2:
- Fix typo s/does now/does not/
- Add #ifdefs around header to allow inclusion whenefi_loaderis disabled
- Add stubefi_restore_gd()function whenefi_loaderis disabled
- Disable debug
- Mark runtime region as such
- Fix up memory map
- Allowefi_restore_gdto 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+Quoted 5 lines
--- include/efi_loader.h | 51 +++ lib/efi_loader/efi_boottime.c | 761 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 812 insertions(+)create mode 100644
lib/efi_loader/efi_boottime.c
Quoted 6 lines
Reviewed-by Simon Glass <[email protected]>But please see below.
Also, how does the timer get disabled? For me it seems to be enabled on boot so it always calls the trigger function.
I think I see what you're running into. The ARM code runs set_timer as one of its first actions. On x86 it probably doesn't, so that's why you see the timer firing off, since it's initialized to 0 and now >= 0 always.
I'll change the initialization for timer_next to -1ULL. That way you should only get to see events when they're there.
Quoted 51 lines
Quoted 38 lines
include/efi_loader.h +22
-0@@ -6,18 +6,69 @@* 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; +struct efi_class_map {include/efi_loader.h line 47
Quoted 10 lines
include/efi_loader.h +10
-0+const efi_guid_t *guid; +const void *interface; +}; + +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);Can we add a name in here? It would be good to name each of these things so that we can print it out for debugging, etc.
Quoted 3 lines
Same also for
efi_object. The guids are an unfortunate but effective obfuscation for people trying to figure out what is going on. I think we should try to keep things human-friendly.
While I agree, I'm not sure this is the correct layer to do this in. As soon as the open callback gets called, you should be able to see which function this is, no?
Do you have any concrete case where the current debugging wasn't enough?
Alex
PATCH 4/14 efi_loader: Add boot time services
Hi Alex,
Quoted 47 lines · Alexander Graf
Quoted 43 lines · Simon Glass
Hi Alexander,
Quoted 29 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.
Signed-off-by Alexander Graf <[email protected]>---
v1 -> v2:
- Fix typo s/does now/does not/
- Add #ifdefs around header to allow inclusion whenefi_loaderis disabled
- Add stubefi_restore_gd()function whenefi_loaderis disabled
- Disable debug
- Mark runtime region as such
- Fix up memory map
- Allowefi_restore_gdto 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+Quoted 5 lines
--- include/efi_loader.h | 51 +++ lib/efi_loader/efi_boottime.c | 761 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 812 insertions(+)create mode 100644
lib/efi_loader/efi_boottime.cReviewed-by Simon Glass <[email protected]>But please see below.
Quoted 7 lines
Quoted 2 lines
Also, how does the timer get disabled? For me it seems to be enabled on boot so it always calls the trigger function.
I think I see what you're running into. The ARM code runs
set_timeras one of its first actions. On x86 it probably doesn't, so that's why you see the timer firing off, since it's initialized to 0 and now >= 0 always.I'll change the initialization for
timer_nextto -1ULL. That way you should only get to see events when they're there.
OK. Or maybe you need a bool to track when it is not active?
Quoted 54 lines
Quoted 54 lines
Quoted 40 lines
include/efi_loader.h +22
-0@@ -6,18 +6,69 @@* 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; +struct efi_class_map {include/efi_loader.h line 47
Quoted 10 lines
include/efi_loader.h +10
-0+const efi_guid_t *guid; +const void *interface; +}; + +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);Can we add a name in here? It would be good to name each of these things so that we can print it out for debugging, etc.
Quoted 8 lines
Quoted 3 lines
Same also for
efi_object. The guids are an unfortunate but effective obfuscation for people trying to figure out what is going on. I think we should try to keep things human-friendly.While I agree, I'm not sure this is the correct layer to do this in. As soon as the open callback gets called, you should be able to see which function this is, no?
Do you have any concrete case where the current debugging wasn't enough?
In my case I was trying to work out whether the call was getting through, and where it was ending up. It was really hard to know whether something was just not getting through. Putting a name in the dispatching helped a lot with that. It's all very well to send a message when you have arrived, but it's more useful I think to send a message when you leave :-)
Regards,
Simon
PATCH 4/14 efi_loader: Add boot time services
Quoted 46 lines · Simon Glass
Hi Alex,
Quoted 43 lines · Alexander Graf
Quoted 40 lines · Simon Glass
Hi Alexander,
Quoted 28 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.
Signed-off-by Alexander Graf <[email protected]>---
v1 -> v2:
- Fix typo s/does now/does not/
- Add #ifdefs around header to allow inclusion whenefi_loaderis disabled
- Add stubefi_restore_gd()function whenefi_loaderis disabled
- Disable debug
- Mark runtime region as such
- Fix up memory map
- Allowefi_restore_gdto 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+Quoted 5 lines
--- include/efi_loader.h | 51 +++ lib/efi_loader/efi_boottime.c | 761 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 812 insertions(+)create mode 100644
lib/efi_loader/efi_boottime.cReviewed-by Simon Glass <[email protected]>But please see below.
Quoted 7 lines
Quoted 6 lines
Quoted 2 lines
Also, how does the timer get disabled? For me it seems to be enabled on boot so it always calls the trigger function.
I think I see what you're running into. The ARM code runs
set_timeras one of its first actions. On x86 it probably doesn't, so that's why you see the timer firing off, since it's initialized to 0 and now >= 0 always.I'll change the initialization for
timer_nextto -1ULL. That way you should only get to see events when they're there.OK. Or maybe you need a bool to track when it is not active?
Setting the value to -1ULL is almost the same, as it would only ever match if timer_get_us() returns exactly 0xffffffffffffffff.
Quoted 58 lines
Quoted 58 lines
Quoted 52 lines
Quoted 39 lines
include/efi_loader.h +22
-0@@ -6,18 +6,69 @@* 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; +struct efi_class_map {include/efi_loader.h line 47
Quoted 10 lines
include/efi_loader.h +10
-0+const efi_guid_t *guid; +const void *interface; +}; + +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);Can we add a name in here? It would be good to name each of these things so that we can print it out for debugging, etc.
Quoted 3 lines
Same also for
efi_object. The guids are an unfortunate but effective obfuscation for people trying to figure out what is going on. I think we should try to keep things human-friendly.While I agree, I'm not sure this is the correct layer to do this in. As soon as the open callback gets called, you should be able to see which function this is, no?
Quoted 7 lines
Quoted 1 line
Do you have any concrete case where the current debugging wasn't enough?
In my case I was trying to work out whether the call was getting through, and where it was ending up. It was really hard to know whether something was just not getting through. Putting a name in the dispatching helped a lot with that. It's all very well to send a message when you have arrived, but it's more useful I think to send a message when you leave :-)
I can see why it would help for that specific case, but I'd really put this into the "apply locally, debug, throw away" patch category. I'm sure once we have the support going well on x86, there won't be any need to dig out names from the dispatcher.
Alex
PATCH 5/14 efi_loader: Add console interface
In reply to Alexander GrafOne 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+
--- include/efi_loader.h | 5 + lib/efi_loader/efi_console.c | 357 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 362 insertions(+) create mode 100644 lib/efi_loader/efi_console.c
include/efi_loader.h +5 -0
@@ -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 +357 -0
357 lines not on this page · raw message
Signature · 1 line
2.1.4
PATCH 5/14 efi_loader: Add console interface
Quoted 12 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()andputc().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.
Signed-off-by Alexander Graf <[email protected]>---
Quoted 8 lines
v1 -> v2:
- Move to GPLv2+
--- include/efi_loader.h | 5 + lib/efi_loader/efi_console.c | 357 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 362 insertions(+) create mode 100644 lib/efi_loader/efi_console.c
PATCH 6/14 efi_loader: Add runtime services
In reply to Alexander GrafAfter 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+
--- 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 | 10 ++ lib/efi_loader/efi_boottime.c | 6 +- lib/efi_loader/efi_runtime.c | 300 ++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 401 insertions(+), 3 deletions(-) create mode 100644 lib/efi_loader/efi_runtime.c
arch/arm/config.mk +4 -0
@@ -120,6 +120,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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 +10 -0
@@ -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; +struct efi_class_map {const efi_guid_t *guid;const void *interface;
@@ -68,12 +72,18 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info);void efi_save_gd (void );void efi_restore_gd (void );efi_status_t efi_exit_func (efi_status_t ret); +void efi_runtime_relocate (ulong offset,struct efi_mem_desc *map);#define EFI_LOADER_POOL_SIZE (128 * 1024 * 1024)void *efi_loader_alloc (uint64_t len); +#define EFI_RUNTIME_DATA __attribute__ ((section ("efi_runtime_data"))) +#define EFI_RUNTIME_TEXT __attribute__ ((section ("efi_runtime_text"))) +#else /* defined(EFI_LOADER) */ +#define EFI_RUNTIME_DATA +#define EFI_RUNTIME_TEXT static inline void efi_restore_gd (void ) { }#endif
lib/efi_loader/efi_boottime.c +3 -3
@@ -36,7 +36,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[] = { +static struct efi_configuration_table EFI_RUNTIME_DATA efi_conf_table[] = { { .guid =EFI_FDT_GUID , },
@@ -741,10 +741,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 +300 -0
300 lines not on this page · raw message
Signature · 1 line
2.1.4
PATCH 6/14 efi_loader: Add runtime services
Quoted 4 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.
Quoted 8 lines
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.
Signed-off-by Alexander Graf <[email protected]>
Just a couple of return value issues:
Quoted 31 lines
---
v1 -> v2:
- Fix runtime service sections
- Add runtime detach
- Enable runtime relocations
- Addget_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
- Relocateefi_runtimesection early during bootup
- Fix return values for a number of callbacks to be more UEFI compliant
- Move to GPLv2+--- 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 | 10 ++ lib/efi_loader/efi_boottime.c | 6 +- lib/efi_loader/efi_runtime.c | 300 ++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 401 insertions(+), 3 deletions(-) create mode 100644 lib/efi_loader/efi_runtime.c
...
Quoted 81 lines
lib/efi_loader/efi_runtime.c new file +75
-0@@ -0,0 +1,300 @@ +/* +* EFI application runtime services +* +* Copyright (c) 2016 Alexander Graf +* +* SPDX-License-Identifier: GPL-2.0+ +*/ + +#include <common.h> +#include <efi_loader.h> +#include <command.h> +#include <asm/global_data.h> +#include <rtc.h> + +/* For manual relocation support */ +DECLARE_GLOBAL_DATA_PTR ; + +static efi_status_t EFI_RUNTIME_TEXT efi_unimplemented(void); +static efi_status_t EFI_RUNTIME_TEXT efi_device_error(void); +static efi_status_t EFI_RUNTIME_TEXT efi_invalid_parameter(void); + +#if defined(CONFIG_ARM64 ) +#define R_RELATIVE 1027 +#define R_MASK 0xffffffffULL +#define IS_RELA 1 +#elif defined(CONFIG_ARM ) +#define R_RELATIVE 23 +#define R_MASK 0xffULL +#else +#error Need to add relocation awareness +#endif + +struct elf_rel { +ulong *offset; +ulong info; +}; + +struct elf_rela { +ulong *offset; +ulong info; +long addend; +}; + +/* +* EFI Runtime code lives in 2 stages. In the first stage, U-Boot and an EFI +* payload are running concurrently at the same time. In this mode, we can +* handle a good number of runtime callbacks +*/ + +static void efi_reset_system (enum efi_reset_type reset_type, +efi_status_t reset_status,unsigned long data_size, +void *reset_data) +{ +EFI_ENTRY ("%d %lx %lx %p" , reset_type, reset_status, data_size, reset_data); + +switch (reset_type) { +case EFI_RESET_COLD : +case EFI_RESET_WARM : +do_reset (NULL ,0 ,0 ,NULL ); +break ; +case EFI_RESET_SHUTDOWN : +/* We don't have anything to map this to */ +break ; + } + +EFI_EXIT (EFI_SUCCESS ); +} + +static efi_status_t efi_get_time (struct efi_time *time, +struct efi_time_cap *capabilities) +{ +#ifdef CONFIG_CMD_DATE + +struct rtc_time tm; +int r;
lib/efi_loader/efi_runtime.c line 85
Quoted 10 lines
lib/efi_loader/efi_runtime.c new file +10
-0+#ifdef CONFIG_DM_RTC +struct udevice *dev; +#endif + +EFI_ENTRY ("%p %p" , time, capabilities); + +#ifdef CONFIG_DM_RTC + r =uclass_get_device (UCLASS_RTC ,0 , &dev); +if (r) +return EFI_EXIT (EFI_UNSUPPORTED );
EFI_DEVICE_ERROR?
lib/efi_loader/efi_runtime.c line 94
Quoted 9 lines
lib/efi_loader/efi_runtime.c new file +9
-0+#endif + +#ifdef CONFIG_DM_RTC + r =dm_rtc_get (dev, &tm); +#else + r =rtc_get (&tm); +#endif +if (r) +return EFI_EXIT (EFI_UNSUPPORTED );
EFI_DEVICE_ERROR?
Quoted 17 lines
lib/efi_loader/efi_runtime.c new file +17
-0+ +memset (time,0 ,sizeof (*time)); + time->year = tm.tm_year; + time->month = tm.tm_mon; + time->day = tm.tm_mday; + time->hour = tm.tm_hour; + time->minute = tm.tm_min; + time->daylight = tm.tm_isdst; + +return EFI_EXIT (EFI_SUCCESS ); + +#else /* CONFIG_CMD_DATE */ + +return EFI_DEVICE_ERROR ; + +#endif /* CONFIG_CMD_DATE */ +}
PATCH 6/14 efi_loader: Add runtime services
In reply to Alexander GrafHi Alexander,
Quoted 31 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.
Signed-off-by Alexander Graf <[email protected]>---
v1 -> v2:
- Fix runtime service sections
- Add runtime detach
- Enable runtime relocations
- Addget_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
- Relocateefi_runtimesection early during bootup
- Fix return values for a number of callbacks to be more UEFI compliant
- Move to GPLv2+
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 | 10 ++ lib/efi_loader/efi_boottime.c | 6 +- lib/efi_loader/efi_runtime.c | 300 ++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 401 insertions(+), 3 deletions(-) create mode 100644 lib/efi_loader/efi_runtime.c
A few nits.
Quoted 183 lines
arch/arm/config.mk +4
-0@@ -120,6 +120,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_BUILDarch/arm/cpu/armv8/u-boot.lds +16
-0@@ -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@@ -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@@ -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@@ -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@@ -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 +4
-0@@ -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; +struct efi_class_map {const efi_guid_t *guid;const void *interface;
include/efi_loader.h void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info); · line 75
Quoted 4 lines
include/efi_loader.h +1
-0@@ -68,12 +72,18 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info);void efi_save_gd (void );void efi_restore_gd (void );efi_status_t efi_exit_func (efi_status_t ret); +void efi_runtime_relocate (ulong offset,struct efi_mem_desc *map);
Function comment please.
Quoted 46 lines
include/efi_loader.h +5
-0#define EFI_LOADER_POOL_SIZE (128 * 1024 * 1024)void *efi_loader_alloc (uint64_t len); +#define EFI_RUNTIME_DATA __attribute__ ((section ("efi_runtime_data"))) +#define EFI_RUNTIME_TEXT __attribute__ ((section ("efi_runtime_text"))) +#else /* defined(EFI_LOADER) */ +#define EFI_RUNTIME_DATA +#define EFI_RUNTIME_TEXT static inline void efi_restore_gd (void ) { }#endif lib/efi_loader/efi_boottime.c +3
-3@@ -36,7 +36,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[] = { +static struct efi_configuration_table EFI_RUNTIME_DATA efi_conf_table[] = { { .guid =EFI_FDT_GUID , },@@ -741,10 +741,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 +0
-0
lib/efi_loader/efi_runtime.c line 9
Quoted 9 lines
lib/efi_loader/efi_runtime.c new file +9
-0@@ -0,0 +1,300 @@ +/* +* EFI application runtime services +* +* Copyright (c) 2016 Alexander Graf +* +* SPDX-License-Identifier: GPL-2.0+ +*/ + +#include <common.h>
You use driver model so need <dm.h> too.
Quoted 23 lines
lib/efi_loader/efi_runtime.c new file +23
-0+#include <efi_loader.h> +#include <command.h> +#include <asm/global_data.h> +#include <rtc.h> + +/* For manual relocation support */ +DECLARE_GLOBAL_DATA_PTR ; + +static efi_status_t EFI_RUNTIME_TEXT efi_unimplemented(void); +static efi_status_t EFI_RUNTIME_TEXT efi_device_error(void); +static efi_status_t EFI_RUNTIME_TEXT efi_invalid_parameter(void); + +#if defined(CONFIG_ARM64 ) +#define R_RELATIVE 1027 +#define R_MASK 0xffffffffULL +#define IS_RELA 1 +#elif defined(CONFIG_ARM ) +#define R_RELATIVE 23 +#define R_MASK 0xffULL +#else +#error Need to add relocation awareness +#endif +
lib/efi_loader/efi_runtime.c line 42
Quoted 10 lines
lib/efi_loader/efi_runtime.c new file +10
-0+struct elf_rel { +ulong *offset; +ulong info; +}; + +struct elf_rela { +ulong *offset; +ulong info; +long addend; +};
Can you use elf.h?
Quoted 24 lines
lib/efi_loader/efi_runtime.c new file +24
-0+ +/* +* EFI Runtime code lives in 2 stages. In the first stage, U-Boot and an EFI +* payload are running concurrently at the same time. In this mode, we can +* handle a good number of runtime callbacks +*/ + +static void efi_reset_system (enum efi_reset_type reset_type, +efi_status_t reset_status,unsigned long data_size, +void *reset_data) +{ +EFI_ENTRY ("%d %lx %lx %p" , reset_type, reset_status, data_size, reset_data); + +switch (reset_type) { +case EFI_RESET_COLD : +case EFI_RESET_WARM : +do_reset (NULL ,0 ,0 ,NULL ); +break ; +case EFI_RESET_SHUTDOWN : +/* We don't have anything to map this to */ +break ; + } + +EFI_EXIT (EFI_SUCCESS );
lib/efi_loader/efi_runtime.c line 76
Quoted 10 lines
lib/efi_loader/efi_runtime.c new file +10
-0+} + +static efi_status_t efi_get_time (struct efi_time *time, +struct efi_time_cap *capabilities) +{ +#ifdef CONFIG_CMD_DATE + +struct rtc_time tm; +int r; +#ifdef CONFIG_DM_RTC
Since this is a new service I don't think you need to support the old RTC API.
Quoted 225 lines
lib/efi_loader/efi_runtime.c new file +224
-1+struct udevice *dev; +#endif + +EFI_ENTRY ("%p %p" , time, capabilities); + +#ifdef CONFIG_DM_RTC + r =uclass_get_device (UCLASS_RTC ,0 , &dev); +if (r) +return EFI_EXIT (EFI_UNSUPPORTED ); +#endif + +#ifdef CONFIG_DM_RTC + r =dm_rtc_get (dev, &tm); +#else + r =rtc_get (&tm); +#endif +if (r) +return EFI_EXIT (EFI_UNSUPPORTED ); + +memset (time,0 ,sizeof (*time)); + time->year = tm.tm_year; + time->month = tm.tm_mon; + time->day = tm.tm_mday; + time->hour = tm.tm_hour; + time->minute = tm.tm_min; + time->daylight = tm.tm_isdst; + +return EFI_EXIT (EFI_SUCCESS ); + +#else /* CONFIG_CMD_DATE */ + +return EFI_DEVICE_ERROR ; + +#endif /* CONFIG_CMD_DATE */ +} + +struct efi_runtime_detach_list_struct { +void *ptr; +void *patchto; +}; + +static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = { + { +/* do_reset is gone */ + .ptr = &efi_runtime_services.reset_system, + .patchto = &efi_unimplemented, + }, { +/* invalidate_*cache_all are gone */ + .ptr = &efi_runtime_services.set_virtual_address_map, + .patchto = &efi_invalid_parameter, + }, { +/* RTC accessors are gone */ + .ptr = &efi_runtime_services.get_time, + .patchto = &efi_device_error, + }, +}; + +static bool efi_runtime_tobedetached (void *p) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (efi_runtime_detach_list); i++) +if (efi_runtime_detach_list[i].ptr == p) +return true; + +return false; +} + +static void efi_runtime_detach (ulong offset) +{ +int i; +ulong patchoff = offset - (ulong )gd->relocaddr; + +for (i =0 ; i <ARRAY_SIZE (efi_runtime_detach_list); i++) { +ulong patchto = (ulong )efi_runtime_detach_list[i].patchto; +ulong *p = efi_runtime_detach_list[i].ptr; +ulong newaddr = patchto + patchoff; + +#ifdef DEBUG_EFI +printf ("%s: Setting %p to %lx\n" , __func__, p, newaddr); +#endif + *p = newaddr; + } +} + +/* Relocate EFI runtime to uboot_reloc_base = offset */ +void efi_runtime_relocate (ulong offset,struct efi_mem_desc *map) +{ +#ifdef IS_RELA +struct elf_rela *rel = (void *)&__efi_runtime_rel_start; +#else +struct elf_rel *rel = (void *)&__efi_runtime_rel_start; +static ulong lastoff =CONFIG_SYS_TEXT_BASE ; +#endif + +#ifdef DEBUG_EFI +printf ("%s: Relocating to offset=%lx\n" , __func__, offset); +#endif + +for (; (ulong )rel < (ulong)&__efi_runtime_rel_stop; rel++) { +ulong base =CONFIG_SYS_TEXT_BASE ; +ulong *p; +ulong newaddr; + + p = (void *)((ulong )rel->offset - base) + gd->relocaddr; + +if ((rel->info &R_MASK ) !=R_RELATIVE ) { +continue ; + } + +#ifdef IS_RELA + newaddr = rel->addend + offset -CONFIG_SYS_TEXT_BASE ; +#else + newaddr = *p - lastoff + offset; +#endif + +/* Check if the relocation is inside bounds */ +if (map && ((newaddr < map->virtual_start) || + newaddr > (map->virtual_start + (map->num_pages <<12 )))) { +if (!efi_runtime_tobedetached (p)) +printf ("U-Boot EFI: Relocation at %p is out of " +"range (%lx)\n" , p, newaddr); +continue ; + } + +#ifdef DEBUG_EFI +printf ("%s: Setting %p to %lx\n" , __func__, p, newaddr); +#endif + + *p = newaddr; +flush_dcache_range ((ulong )p, (ulong)&p[1 ]); + } + +#ifndef IS_RELA + lastoff = offset; +#endif + +invalidate_icache_all (); +} + +static efi_status_t efi_set_virtual_address_map ( +unsigned long memory_map_size, +unsigned long descriptor_size, +uint32_t descriptor_version, +struct efi_mem_desc *virtmap) +{ +ulong runtime_start = (ulong)&__efi_runtime_start & ~0xfffULL ; +int n = memory_map_size / descriptor_size; +int i; + +EFI_ENTRY ("%lx %lx %x %p" , memory_map_size, descriptor_size, + descriptor_version, virtmap); + +for (i =0 ; i < n; i++) { +struct efi_mem_desc *map; + + map = (void *)virtmap + (descriptor_size * i); +if (map->type ==EFI_RUNTIME_SERVICES_CODE ) { +ulong new_offset = map->virtual_start - (runtime_start - gd->relocaddr); + +efi_runtime_relocate (new_offset, map); +/* Once we're virtual, we can no longer handle +complex callbacks */ +efi_runtime_detach (new_offset); +return EFI_EXIT (EFI_SUCCESS ); + } + } + +return EFI_EXIT (EFI_INVALID_PARAMETER ); +} + +/* +* In the second stage, U-Boot has disappeared. To isolate our runtime code +* that at this point still exists from the rest, we put it into a special +* section. +* +* !!WARNING!! +* +* This means that we can not rely on any code outside of this file in any +* function or variable below this line. +* +* Please keep everything fully self-contained and annotated with +* EFI_RUNTIME_TEXT and EFI_RUNTIME_DATA markers. +*/ + +/* +* Relocate the EFI runtime stub to a different place. We need to call this +* the first time we expose the runtime interface to a user and on set virtual +* address map calls. +*/ + +static efi_status_t EFI_RUNTIME_TEXT efi_unimplemented(void) +{ +return EFI_UNSUPPORTED ; +} + +static efi_status_t EFI_RUNTIME_TEXT efi_device_error(void) +{ +return EFI_DEVICE_ERROR ; +} + +static efi_status_t EFI_RUNTIME_TEXT efi_invalid_parameter(void) +{ +return EFI_INVALID_PARAMETER ; +} + +struct efi_runtime_services EFI_RUNTIME_DATA efi_runtime_services = { + .hdr = { + .signature =EFI_RUNTIME_SERVICES_SIGNATURE , + .revision =EFI_RUNTIME_SERVICES_REVISION , + .headersize =sizeof (struct efi_table_hdr ), + }, + .get_time = &efi_get_time, + .set_time = (void *)&efi_device_error, + .get_wakeup_time = (void *)&efi_unimplemented, + .set_wakeup_time = (void *)&efi_unimplemented, + .set_virtual_address_map = &efi_set_virtual_address_map, + .convert_pointer = (void *)&efi_invalid_parameter, + .get_variable = (void *)&efi_device_error, + .get_next_variable = (void *)&efi_device_error, + .set_variable = (void *)&efi_device_error, + .get_next_high_mono_count = (void *)&efi_device_error, + .reset_system = &efi_reset_system, +}; --
Quoted 1 line
2.1.4
Regards,
Simon
PATCH 6/14 efi_loader: Add runtime services
Quoted 320 lines · Simon Glass
Hi Alexander,
Quoted 42 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.
Signed-off-by Alexander Graf <[email protected]>---
v1 -> v2:
- Fix runtime service sections
- Add runtime detach
- Enable runtime relocations
- Addget_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
- Relocateefi_runtimesection early during bootup
- Fix return values for a number of callbacks to be more UEFI compliant
- Move to GPLv2+--- 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 | 10 ++ lib/efi_loader/efi_boottime.c | 6 +- lib/efi_loader/efi_runtime.c | 300 ++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 401 insertions(+), 3 deletions(-)Quoted 1 line
create mode 100644
lib/efi_loader/efi_runtime.cReviewed-by Simon Glass <[email protected]>A few nits.
Quoted 182 lines
arch/arm/config.mk +4
-0@@ -120,6 +120,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_BUILDarch/arm/cpu/armv8/u-boot.lds +16
-0@@ -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@@ -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@@ -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@@ -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@@ -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 +4
-0@@ -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; +struct efi_class_map {const efi_guid_t *guid;const void *interface;include/efi_loader.h void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info); · line 75
Quoted 4 lines
include/efi_loader.h +1
-0@@ -68,12 +72,18 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info);void efi_save_gd (void );void efi_restore_gd (void );efi_status_t efi_exit_func (efi_status_t ret); +void efi_runtime_relocate (ulong offset,struct efi_mem_desc *map);Function comment please.
Quoted 45 lines
include/efi_loader.h +5
-0#define EFI_LOADER_POOL_SIZE (128 * 1024 * 1024)void *efi_loader_alloc (uint64_t len); +#define EFI_RUNTIME_DATA __attribute__ ((section ("efi_runtime_data"))) +#define EFI_RUNTIME_TEXT __attribute__ ((section ("efi_runtime_text"))) +#else /* defined(EFI_LOADER) */ +#define EFI_RUNTIME_DATA +#define EFI_RUNTIME_TEXT static inline void efi_restore_gd (void ) { }#endif lib/efi_loader/efi_boottime.c +3
-3@@ -36,7 +36,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[] = { +static struct efi_configuration_table EFI_RUNTIME_DATA efi_conf_table[] = { { .guid =EFI_FDT_GUID , },@@ -741,10 +741,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 +0
-0lib/efi_loader/efi_runtime.c line 9
Quoted 9 lines
lib/efi_loader/efi_runtime.c new file +9
-0@@ -0,0 +1,300 @@ +/* +* EFI application runtime services +* +* Copyright (c) 2016 Alexander Graf +* +* SPDX-License-Identifier: GPL-2.0+ +*/ + +#include <common.h> You use driver model so need <
dm.h> too.Quoted 26 lines
lib/efi_loader/efi_runtime.c new file +26
-0+#include <efi_loader.h> +#include <command.h> +#include <asm/global_data.h> +#include <rtc.h> + +/* For manual relocation support */ +DECLARE_GLOBAL_DATA_PTR ; + +static efi_status_t EFI_RUNTIME_TEXT efi_unimplemented(void); +static efi_status_t EFI_RUNTIME_TEXT efi_device_error(void); +static efi_status_t EFI_RUNTIME_TEXT efi_invalid_parameter(void); + +#if defined(CONFIG_ARM64 ) +#define R_RELATIVE 1027 +#define R_MASK 0xffffffffULL +#define IS_RELA 1 +#elif defined(CONFIG_ARM ) +#define R_RELATIVE 23 +#define R_MASK 0xffULL +#else +#error Need to add relocation awareness +#endif + +struct elf_rel { +ulong *offset; +ulong info;
Quoted 8 lines
lib/efi_loader/efi_runtime.c line 42
Quoted 7 lines
lib/efi_loader/efi_runtime.c new file +7
-0+}; + +struct elf_rela { +ulong *offset; +ulong info; +long addend; +};Can you use
elf.h?
I could, but elf.h has a hard distinction between 32bit and 64bit binaries while I really just want the one the binary is actually using at this specific moment.
So I could add #ifdefs on 32bit vs 64bit and then use the elf.h defines instead, but I'm not sure the code would look any more readable than it is now after that ;).
Quoted 28 lines
Quoted 28 lines
lib/efi_loader/efi_runtime.c new file +27
-0+ +/* +* EFI Runtime code lives in 2 stages. In the first stage, U-Boot and an EFI +* payload are running concurrently at the same time. In this mode, we can +* handle a good number of runtime callbacks +*/ + +static void efi_reset_system (enum efi_reset_type reset_type, +efi_status_t reset_status,unsigned long data_size, +void *reset_data) +{ +EFI_ENTRY ("%d %lx %lx %p" , reset_type, reset_status, data_size, reset_data); + +switch (reset_type) { +case EFI_RESET_COLD : +case EFI_RESET_WARM : +do_reset (NULL ,0 ,0 ,NULL ); +break ; +case EFI_RESET_SHUTDOWN : +/* We don't have anything to map this to */ +break ; + } + +EFI_EXIT (EFI_SUCCESS ); +} + +static efi_status_t efi_get_time(struct efi_time *time,
Quoted 8 lines
lib/efi_loader/efi_runtime.c line 76
Quoted 7 lines
lib/efi_loader/efi_runtime.c new file +7
-0+ struct efi_time_cap *capabilities) +{ +#ifdef CONFIG_CMD_DATE + +struct rtc_time tm; +int r; +#ifdef CONFIG_DM_RTC Since this is a new service I don't think you need to support the old RTC API.
ok
Alex
PATCH 7/14 efi_loader: Add disk interfaces
In reply to Alexander GrafA 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
--- include/efi_loader.h | 1 + lib/efi_loader/efi_disk.c | 218 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 219 insertions(+) create mode 100644 lib/efi_loader/efi_disk.c
include/efi_loader.h +1 -0
@@ -63,6 +63,7 @@ struct efi_object { };extern struct list_head efi_obj_list; +int efi_disk_register (void ); efi_status_t efi_return_handle(void *handle, efi_guid_t *protocol, void **protocol_interface, void *agent_handle, void *controller_handle,
lib/efi_loader/efi_disk.c new file +218 -0
218 lines not on this page · raw message
Signature · 1 line
2.1.4
PATCH 7/14 efi_loader: Add disk interfaces
Quoted 134 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.
Signed-off-by Alexander Graf <[email protected]>Reviewed-by Simon Glass <[email protected]>---
v1 -> v2:
- Move to
block_drvrarray
- Move to GPLv2+
- Fix header order
- Document efi block object struct
- Use calloc rather than malloc & memset--- include/efi_loader.h | 1 + lib/efi_loader/efi_disk.c | 218 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 219 insertions(+) create mode 100644 lib/efi_loader/efi_disk.cinclude/efi_loader.h +1
-0@@ -63,6 +63,7 @@ struct efi_object { };extern struct list_head efi_obj_list; +int efi_disk_register (void ); efi_status_t efi_return_handle(void *handle, efi_guid_t *protocol, void **protocol_interface, void *agent_handle, void *controller_handle,lib/efi_loader/efi_disk.c new file +91
-0@@ -0,0 +1,218 @@ +/* +* EFI application disk support +* +* Copyright (c) 2016 Alexander Graf +* +* SPDX-License-Identifier: GPL-2.0+ +*/ + +#include <common.h> +#include <efi_loader.h> +#include <inttypes.h> +#include <part.h> +#include <malloc.h> + +static const efi_guid_t efi_block_io_guid =BLOCK_IO_GUID ; + +struct efi_disk_obj { +/* Generic EFI object parent class data */ +struct efi_object parent; +/* EFI Interface callback struct for block I/O */ +struct efi_block_io ops; +/* U-Boot ifname for block device */ +const char *ifname; +/* U-Boot dev_index for block device */ +int dev_index; +/* EFI Interface Media descriptor struct, referenced by ops */ +struct efi_block_io_media media; +/* EFI device path to this block device */ +struct efi_device_path_file_path *dp; +}; + +static void ascii2unicode (u16 *unicode,char *ascii) +{ +while (*ascii) + *(unicode++) = *(ascii++); +} + +static efi_status_t efi_disk_open_block (void *handle,efi_guid_t *protocol, +void **protocol_interface,void *agent_handle, +void *controller_handle,uint32_t attributes) +{ +struct efi_disk_obj *diskobj = handle; + + *protocol_interface = &diskobj->ops; + +return EFI_SUCCESS ; +} + +static efi_status_t efi_disk_open_dp (void *handle,efi_guid_t *protocol, +void **protocol_interface,void *agent_handle, +void *controller_handle,uint32_t attributes) +{ +struct efi_disk_obj *diskobj = handle; + + *protocol_interface = diskobj->dp; + +return EFI_SUCCESS ; +} + +static efi_status_t efi_disk_reset (struct efi_block_io *this, +char extended_verification) +{ +EFI_ENTRY ("%p, %x" , this, extended_verification); +return EFI_EXIT (EFI_DEVICE_ERROR ); +} + +enum efi_disk_direction { +EFI_DISK_READ , +EFI_DISK_WRITE , +}; + +static efi_status_t efi_disk_rw_blocks (struct efi_block_io *this, +u32 media_id,u64 lba,unsigned long buffer_size, +void *buffer,enum efi_disk_direction direction) +{ +struct efi_disk_obj *diskobj; +struct block_dev_desc *desc; +int blksz; +int blocks; +unsigned long n; + +EFI_ENTRY ("%p, %x, %" PRIx64", %lx, %p" , this, media_id, lba, + buffer_size, buffer); + + diskobj = container_of(this, struct efi_disk_obj, ops); +if (!(desc =get_dev (diskobj->ifname, diskobj->dev_index))) +return EFI_EXIT (EFI_DEVICE_ERROR ); + blksz = desc->blksz; + blocks = buffer_size / blksz; + +#ifdef DEBUG_EFI
lib/efi_loader/efi_disk.c line 101
Quoted 10 lines
lib/efi_loader/efi_disk.c new file +10
-0+printf ("EFI: %s:%d blocks=%x lba=%" PRIx64" blksz=%x dir=%d\n" , __func__, + __LINE__, blocks, lba, blksz, direction); +#endif + +/* We only support full block access */ +if (buffer_size & (blksz -1 )) +return EFI_EXIT (EFI_DEVICE_ERROR ); + +if (direction ==EFI_DISK_READ ) + n = desc->block_read (desc->dev, lba, blocks, buffer);
Shouldn't this be s/desc->dev/desc/?
lib/efi_loader/efi_disk.c line 103
Quoted 2 lines
lib/efi_loader/efi_disk.c new file +2
-0+else + n = desc->block_write (desc->dev, lba, blocks, buffer);
Same here.
Quoted 36 lines
lib/efi_loader/efi_disk.c new file +36
-0+ +/* We don't do interrupts, so check for timers cooperatively */ +efi_timer_check (); + +#ifdef DEBUG_EFI +printf ("EFI: %s:%d n=%lx blocks=%x\n" , __func__, __LINE__, n, blocks); +#endif +if (n != blocks) +return EFI_EXIT (EFI_DEVICE_ERROR ); + +return EFI_EXIT (EFI_SUCCESS ); +} + +static efi_status_t efi_disk_read_blocks (struct efi_block_io *this, +u32 media_id,u64 lba,unsigned long buffer_size, +void *buffer) +{ +return efi_disk_rw_blocks (this, media_id, lba, buffer_size, buffer, +EFI_DISK_READ ); +} + +static efi_status_t efi_disk_write_blocks (struct efi_block_io *this, +u32 media_id,u64 lba,unsigned long buffer_size, +void *buffer) +{ +return efi_disk_rw_blocks (this, media_id, lba, buffer_size, buffer, +EFI_DISK_WRITE ); +} + +static efi_status_t efi_disk_flush_blocks (struct efi_block_io *this) +{ +/* We always write synchronously */ +return EFI_SUCCESS ; +} + +static const struct efi_block_io block_io_disk_template = {
lib/efi_loader/efi_disk.c line 149
Quoted 10 lines
lib/efi_loader/efi_disk.c new file +10
-0+ .reset = &efi_disk_reset, + .read_blocks = &efi_disk_read_blocks, + .write_blocks = &efi_disk_write_blocks, + .flush_blocks = &efi_disk_flush_blocks, +}; + +/* +* U-Boot doesn't have a list of all online disk devices. So when running our +* EFI payload, we scan through all of the potentially available ones and +* store them in our object pool.
This isn't quite true - you are actually using its list below. You could modify this to work without the table I suspect. But the way you have it seems good to me.
lib/efi_loader/efi_disk.c line 155
Quoted 6 lines
lib/efi_loader/efi_disk.c new file +6
-0+* +* This gets called from do_bootefi_exec(). +*/ +int efi_disk_register (void ) +{ +const struct block_drvr *cur_drvr;
I think it would be better to use a function call to return the driver. Then we can implement it with driver model later.
How about:
block_dev_first(&cur_drvr)block_dev_next(&cur_drvr)
lib/efi_loader/efi_disk.c line 162
Quoted 7 lines
lib/efi_loader/efi_disk.c new file +7
-0+int i; +int disks =0 ; + +/* Search for all available disk devices */ +for (cur_drvr = block_drvr; cur_drvr->name; cur_drvr++) { +printf ("Scanning disks on %s...\n" , cur_drvr->name); +for (i =0 ; i <4 ; i++) {
Please add a comment about the 4, or an enum/#define.
lib/efi_loader/efi_disk.c line 171
Quoted 9 lines
lib/efi_loader/efi_disk.c new file +9
-0+block_dev_desc_t *desc; +struct efi_disk_obj *diskobj; +struct efi_device_path_file_path *dp; +int objlen =sizeof (*diskobj) + (sizeof (*dp) *2 ); +char devname[16 ]; + + desc =get_dev (cur_drvr->name, i); +if (!desc) +continue ;
It's not clear how this checks that the device is actually valid. I think if desc->type == DEV_TYPE_UNKNOWN then you should ignore the device. For example an MMC card slot might be empty.
lib/efi_loader/efi_disk.c line 175
Quoted 4 lines
lib/efi_loader/efi_disk.c new file +4
-0+ + diskobj =calloc (1 , objlen); + +/* Fill in object data */
drop extra blank line, and below
Quoted 17 lines
lib/efi_loader/efi_disk.c new file +17
-0+ + diskobj->parent.protocols[0 ].guid = &efi_block_io_guid; + diskobj->parent.protocols[0 ].open = efi_disk_open_block; + diskobj->parent.protocols[1 ].guid = &efi_guid_device_path; + diskobj->parent.protocols[1 ].open = efi_disk_open_dp; + diskobj->parent.handle = diskobj; + diskobj->ops = block_io_disk_template; + diskobj->ifname = cur_drvr->name; + diskobj->dev_index = i; + +/* Fill in EFI IO Media info (for read/write callbacks) */ + + diskobj->media.removable_media = desc->removable; + diskobj->media.media_present =1 ; + diskobj->media.block_size = desc->blksz; + diskobj->media.io_align = desc->blksz; + diskobj->media.last_block = desc->lba;
lib/efi_loader/efi_disk.c line 202
Quoted 10 lines
lib/efi_loader/efi_disk.c new file +10
-0+ diskobj->ops.media = &diskobj->media; + +/* Fill in device path */ + + dp = (void *)&diskobj[1 ]; + diskobj->dp = dp; + dp[0 ].dp.type =DEVICE_PATH_TYPE_MEDIA_DEVICE ; + dp[0 ].dp.sub_type =DEVICE_PATH_SUB_TYPE_FILE_PATH ; + dp[0 ].dp.length =sizeof (*dp); +sprintf (devname,"%s%d" , cur_drvr->name, i);
I'd suggest snprintf(devname, ARRAY_SIZE(dp[0].str), ...) or perhaps a check.
Quoted 17 lines
lib/efi_loader/efi_disk.c new file +16
-1+ascii2unicode (dp[0 ].str, devname); + + dp[1 ].dp.type =DEVICE_PATH_TYPE_END ; + dp[1 ].dp.sub_type =DEVICE_PATH_SUB_TYPE_END ; + dp[1 ].dp.length =sizeof (*dp); + +/* Hook up to the device list */ + +list_add_tail (&diskobj->parent.link, &efi_obj_list); + disks++; + } + } +printf ("Found %d disks\n" , disks); + +return 0 ; +} --
Quoted 2 lines
2.1.4
Regards,
Simon
PATCH 7/14 efi_loader: Add disk interfaces
Quoted 192 lines · Simon Glass
Quoted 134 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.
Signed-off-by Alexander Graf <[email protected]>Reviewed-by Simon Glass <[email protected]>---
v1 -> v2:
- Move to
block_drvrarray
- Move to GPLv2+
- Fix header order
- Document efi block object struct
- Use calloc rather than malloc & memset--- include/efi_loader.h | 1 + lib/efi_loader/efi_disk.c | 218 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 219 insertions(+)create mode 100644
lib/efi_loader/efi_disk.cinclude/efi_loader.h +1
-0@@ -63,6 +63,7 @@ struct efi_object { };extern struct list_head efi_obj_list; +int efi_disk_register (void ); efi_status_t efi_return_handle(void *handle, efi_guid_t *protocol, void **protocol_interface, void *agent_handle, void *controller_handle,lib/efi_loader/efi_disk.c new file +91
-0@@ -0,0 +1,218 @@ +/* +* EFI application disk support +* +* Copyright (c) 2016 Alexander Graf +* +* SPDX-License-Identifier: GPL-2.0+ +*/ + +#include <common.h> +#include <efi_loader.h> +#include <inttypes.h> +#include <part.h> +#include <malloc.h> + +static const efi_guid_t efi_block_io_guid =BLOCK_IO_GUID ; + +struct efi_disk_obj { +/* Generic EFI object parent class data */ +struct efi_object parent; +/* EFI Interface callback struct for block I/O */ +struct efi_block_io ops; +/* U-Boot ifname for block device */ +const char *ifname; +/* U-Boot dev_index for block device */ +int dev_index; +/* EFI Interface Media descriptor struct, referenced by ops */ +struct efi_block_io_media media; +/* EFI device path to this block device */ +struct efi_device_path_file_path *dp; +}; + +static void ascii2unicode (u16 *unicode,char *ascii) +{ +while (*ascii) + *(unicode++) = *(ascii++); +} + +static efi_status_t efi_disk_open_block (void *handle,efi_guid_t *protocol, +void **protocol_interface,void *agent_handle, +void *controller_handle,uint32_t attributes) +{ +struct efi_disk_obj *diskobj = handle; + + *protocol_interface = &diskobj->ops; + +return EFI_SUCCESS ; +} + +static efi_status_t efi_disk_open_dp (void *handle,efi_guid_t *protocol, +void **protocol_interface,void *agent_handle, +void *controller_handle,uint32_t attributes) +{ +struct efi_disk_obj *diskobj = handle; + + *protocol_interface = diskobj->dp; + +return EFI_SUCCESS ; +} + +static efi_status_t efi_disk_reset (struct efi_block_io *this, +char extended_verification) +{ +EFI_ENTRY ("%p, %x" , this, extended_verification); +return EFI_EXIT (EFI_DEVICE_ERROR ); +} + +enum efi_disk_direction { +EFI_DISK_READ , +EFI_DISK_WRITE , +}; + +static efi_status_t efi_disk_rw_blocks (struct efi_block_io *this, +u32 media_id,u64 lba,unsigned long buffer_size, +void *buffer,enum efi_disk_direction direction) +{ +struct efi_disk_obj *diskobj; +struct block_dev_desc *desc; +int blksz; +int blocks; +unsigned long n; + +EFI_ENTRY ("%p, %x, %" PRIx64", %lx, %p" , this, media_id, lba, + buffer_size, buffer); + + diskobj = container_of(this, struct efi_disk_obj, ops); +if (!(desc =get_dev (diskobj->ifname, diskobj->dev_index))) +return EFI_EXIT (EFI_DEVICE_ERROR ); + blksz = desc->blksz; + blocks = buffer_size / blksz; + +#ifdef DEBUG_EFI lib/efi_loader/efi_disk.c line 101
Quoted 10 lines
lib/efi_loader/efi_disk.c new file +10
-0+printf ("EFI: %s:%d blocks=%x lba=%" PRIx64" blksz=%x dir=%d\n" , __func__, + __LINE__, blocks, lba, blksz, direction); +#endif + +/* We only support full block access */ +if (buffer_size & (blksz -1 )) +return EFI_EXIT (EFI_DEVICE_ERROR ); + +if (direction ==EFI_DISK_READ ) + n = desc->block_read (desc->dev, lba, blocks, buffer);Shouldn't this be s/desc->dev/desc/?
lib/efi_loader/efi_disk.c line 103
Quoted 2 lines
lib/efi_loader/efi_disk.c new file +2
-0+else + n = desc->block_write (desc->dev, lba, blocks, buffer);Same here.
Quoted 41 lines
lib/efi_loader/efi_disk.c new file +41
-0+ +/* We don't do interrupts, so check for timers cooperatively */ +efi_timer_check (); + +#ifdef DEBUG_EFI +printf ("EFI: %s:%d n=%lx blocks=%x\n" , __func__, __LINE__, n, blocks); +#endif +if (n != blocks) +return EFI_EXIT (EFI_DEVICE_ERROR ); + +return EFI_EXIT (EFI_SUCCESS ); +} + +static efi_status_t efi_disk_read_blocks (struct efi_block_io *this, +u32 media_id,u64 lba,unsigned long buffer_size, +void *buffer) +{ +return efi_disk_rw_blocks (this, media_id, lba, buffer_size, buffer, +EFI_DISK_READ ); +} + +static efi_status_t efi_disk_write_blocks (struct efi_block_io *this, +u32 media_id,u64 lba,unsigned long buffer_size, +void *buffer) +{ +return efi_disk_rw_blocks (this, media_id, lba, buffer_size, buffer, +EFI_DISK_WRITE ); +} + +static efi_status_t efi_disk_flush_blocks (struct efi_block_io *this) +{ +/* We always write synchronously */ +return EFI_SUCCESS ; +} + +static const struct efi_block_io block_io_disk_template = { + .reset = &efi_disk_reset, + .read_blocks = &efi_disk_read_blocks, + .write_blocks = &efi_disk_write_blocks, + .flush_blocks = &efi_disk_flush_blocks, +};
Quoted 8 lines
lib/efi_loader/efi_disk.c line 149
Quoted 5 lines
lib/efi_loader/efi_disk.c new file +5
-0+ +/* +* U-Boot doesn't have a list of all online disk devices. So when running our +* EFI payload, we scan through all of the potentially available ones and +* store them in our object pool. This isn't quite true - you are actually using its list below. You could modify this to work without the table I suspect. But the way you have it seems good to me.
What we have is a list of possible drivers. We don't know the list of online devices. That's why we're looping from 0-3 to see whether behind any of those doors there's an online device hiding :).
In an ideal world, we could just say "loop me through all storage devices" without me caring about device drivers or numbering.
Quoted 10 lines
lib/efi_loader/efi_disk.c line 155
Quoted 7 lines
lib/efi_loader/efi_disk.c new file +6
-0+* +* This gets called from do_bootefi_exec(). +*/ +int efi_disk_register (void ) +{ +const struct block_drvr *cur_drvr;I think it would be better to use a function call to return the driver. Then we can implement it with driver model later.
Quoted 4 lines
How about:
block_dev_first(&cur_drvr)block_dev_next(&cur_drvr)
As mentioned above, I'd like to see this generalized even more. Do you think we can postpone the exact way this loops to the point in time when that is sorted out?
Quoted 9 lines
lib/efi_loader/efi_disk.c line 162
Quoted 8 lines
lib/efi_loader/efi_disk.c new file +7
-0+int i; +int disks =0 ; + +/* Search for all available disk devices */ +for (cur_drvr = block_drvr; cur_drvr->name; cur_drvr++) { +printf ("Scanning disks on %s...\n" , cur_drvr->name); +for (i =0 ; i <4 ; i++) {Please add a comment about the 4, or an enum/#define.
Sure. It's a define if anything, since it's really just an arbitrary MAX_DISKS_PER_DRIVER.
Quoted 10 lines
lib/efi_loader/efi_disk.c line 171
Quoted 10 lines
lib/efi_loader/efi_disk.c new file +9
-0+block_dev_desc_t *desc; +struct efi_disk_obj *diskobj; +struct efi_device_path_file_path *dp; +int objlen =sizeof (*diskobj) + (sizeof (*dp) *2 ); +char devname[16 ]; + + desc =get_dev (cur_drvr->name, i); +if (!desc) +continue ;
Quoted 3 lines
It's not clear how this checks that the device is actually valid. I think if
desc->type==DEV_TYPE_UNKNOWNthen you should ignore the device. For example an MMC card slot might be empty.
Ah, I didn't realize you could have DEV_TYPE_UNKNOWN.
Quoted 27 lines
lib/efi_loader/efi_disk.c line 175
Quoted 5 lines
lib/efi_loader/efi_disk.c new file +4
-0+ + diskobj =calloc (1 , objlen); + +/* Fill in object data */ drop extra blank line, and below
Quoted 20 lines
lib/efi_loader/efi_disk.c new file +20
-0+ + diskobj->parent.protocols[0 ].guid = &efi_block_io_guid; + diskobj->parent.protocols[0 ].open = efi_disk_open_block; + diskobj->parent.protocols[1 ].guid = &efi_guid_device_path; + diskobj->parent.protocols[1 ].open = efi_disk_open_dp; + diskobj->parent.handle = diskobj; + diskobj->ops = block_io_disk_template; + diskobj->ifname = cur_drvr->name; + diskobj->dev_index = i; + +/* Fill in EFI IO Media info (for read/write callbacks) */ + + diskobj->media.removable_media = desc->removable; + diskobj->media.media_present =1 ; + diskobj->media.block_size = desc->blksz; + diskobj->media.io_align = desc->blksz; + diskobj->media.last_block = desc->lba; + diskobj->ops.media = &diskobj->media; + +/* Fill in device path */
Quoted 8 lines
lib/efi_loader/efi_disk.c line 202
Quoted 7 lines
lib/efi_loader/efi_disk.c new file +7
-0+ + dp = (void *)&diskobj[1 ]; + diskobj->dp = dp; + dp[0 ].dp.type =DEVICE_PATH_TYPE_MEDIA_DEVICE ; + dp[0 ].dp.sub_type =DEVICE_PATH_SUB_TYPE_FILE_PATH ; + dp[0 ].dp.length =sizeof (*dp); +sprintf (devname,"%s%d" , cur_drvr->name, i);I'd suggest
snprintf(devname, ARRAY_SIZE(dp[0].str), ...)or perhaps a check.
I'll add a comment in the array definition of devname. The str array size is also 16.
Alex
PATCH 8/14 efi_loader: Add "bootefi" command
In reply to Alexander GrafIn 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+
--- common/Makefile | 1 + common/cmd_bootefi.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 common/cmd_bootefi.c
common/Makefile +1 -0
@@ -67,6 +67,7 @@ obj-$(CONFIG_CMD_SOURCE) += cmd_source.o obj-$(CONFIG_CMD_BDI) += cmd_bdinfo.o obj-$(CONFIG_CMD_BEDBUG) += bedbug.o cmd_bedbug.o obj-$(CONFIG_CMD_BMP) += cmd_bmp.o +obj-$(CONFIG_EFI_LOADER) += cmd_bootefi.o obj-$(CONFIG_CMD_BOOTMENU) += cmd_bootmenu.o obj-$(CONFIG_CMD_BOOTLDR) += cmd_bootldr.o obj-$(CONFIG_CMD_BOOTSTAGE) += cmd_bootstage.o
common/cmd_bootefi.c new file +154 -0
154 lines not on this page · raw message
Signature · 1 line
2.1.4
PATCH 8/14 efi_loader: Add "bootefi" command
Hi Alexander,
Quoted 18 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 $loadaddrwhich 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.
Signed-off-by Alexander Graf <[email protected]>---
Quoted 8 lines
v1 -> v2:
- Move to GPLv2+
--- common/Makefile | 1 + common/cmd_bootefi.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 common/cmd_bootefi.c
common/Makefile obj-$(CONFIG_CMD_SOURCE) += cmd_source.o · line 70
Quoted 10 lines
common/Makefile +1
-0@@ -67,6 +67,7 @@ obj-$(CONFIG_CMD_SOURCE) += cmd_source.o obj-$(CONFIG_CMD_BDI) += cmd_bdinfo.o obj-$(CONFIG_CMD_BEDBUG) += bedbug.o cmd_bedbug.o obj-$(CONFIG_CMD_BMP) += cmd_bmp.o +obj-$(CONFIG_EFI_LOADER) += cmd_bootefi.o
For mainline this should move to cmd/bootefi.o. Also please add a Kconfig item for it.
Quoted 57 lines
common/Makefile +0
-0obj-$(CONFIG_CMD_BOOTMENU) += cmd_bootmenu.o obj-$(CONFIG_CMD_BOOTLDR) += cmd_bootldr.o obj-$(CONFIG_CMD_BOOTSTAGE) += cmd_bootstage.ocommon/cmd_bootefi.c new file +48
-0@@ -0,0 +1,154 @@ +/* +* EFI application loader +* +* Copyright (c) 2016 Alexander Graf +* +* SPDX-License-Identifier: GPL-2.0+ +*/ + +#include <common.h> +#include <command.h> +#include <efi_loader.h> +#include <libfdt_env.h> + +/* This list contains all the EFI objects our payload has access to */ +LIST_HEAD (efi_obj_list); + +/* +* When booting using the "bootefi" command, we don't know which +* physical device the file came from. So we create a pseudo-device +* called "bootefi" with the device path /bootefi. +* +* In addition to the originating device we also declare the file path +* of "bootefi" based loads to be /bootefi. +*/ +static struct efi_device_path_file_path bootefi_dummy_path[] = { + { + .dp.type =DEVICE_PATH_TYPE_MEDIA_DEVICE , + .dp.sub_type =DEVICE_PATH_SUB_TYPE_FILE_PATH , + .dp.length =sizeof (bootefi_dummy_path[0 ]), + .str = {'b' ,'o' ,'o' ,'t' ,'e' ,'f' ,'i' }, + }, { + .dp.type =DEVICE_PATH_TYPE_END , + .dp.sub_type =DEVICE_PATH_SUB_TYPE_END , + .dp.length =sizeof (bootefi_dummy_path[0 ]), + } +}; + +static efi_status_t bootefi_open_dp (void *handle,efi_guid_t *protocol, +void **protocol_interface,void *agent_handle, +void *controller_handle,uint32_t attributes) +{ + *protocol_interface = bootefi_dummy_path; +return EFI_SUCCESS ; +} + +/* The EFI loaded_image interface for the image executed via "bootefi" */ +static struct efi_loaded_image loaded_image_info = { + .device_handle = bootefi_dummy_path,
common/cmd_bootefi.c line 58
Quoted 10 lines
common/cmd_bootefi.c new file +10
-0+ .file_path = bootefi_dummy_path, +}; + +/* The EFI object struct for the image executed via "bootefi" */ +static struct efi_object loaded_image_info_obj = { + .handle = &loaded_image_info, + .protocols = { + { +/* When asking for the loaded_image interface, just +* return handle which points to loaded_image_info */
nit: comment style
Quoted 41 lines
common/cmd_bootefi.c new file +41
-0+ .guid = &efi_guid_loaded_image, + .open = &efi_return_handle, + }, + { +/* When asking for the device path interface, return +* bootefi_dummy_path */ + .guid = &efi_guid_device_path, + .open = &bootefi_open_dp, + }, + }, +}; + +/* The EFI object struct for the device the "bootefi" image was loaded from */ +static struct efi_object bootefi_device_obj = { + .handle = bootefi_dummy_path, + .protocols = { + { +/* When asking for the device path interface, return +* bootefi_dummy_path */ + .guid = &efi_guid_device_path, + .open = &bootefi_open_dp, + } + }, +}; + +/* +* Load an EFI payload into a newly allocated piece of memory, register all +* EFI objects it would want to access and jump to it. +*/ +static unsigned long do_bootefi_exec (void *efi) +{ +ulong (*entry)(void *image_handle,struct efi_system_table *st); + +/* +* gd lives in a fixed register which may get clobbered while we execute +* the payload. So save it here and restore it on every callback entry +*/ +efi_save_gd (); + +/* Update system table to point to our currently loaded FDT */ + systab.tables[0 ].table = working_fdt;
common/cmd_bootefi.c line 109
Quoted 10 lines
common/cmd_bootefi.c new file +10
-0+ +if (!working_fdt) { +printf ("WARNING: No device tree loaded, expect boot to fail\n" ); + systab.nr_tables =0 ; + } + +/* Load the EFI payload */ + entry =efi_load_pe (efi, &loaded_image_info); +if (!entry) +return -1 ;
-ENOENT?
Quoted 34 lines
common/cmd_bootefi.c new file +34
-0+ +/* Initialize and populate EFI object list */ +INIT_LIST_HEAD (&efi_obj_list); +list_add_tail (&loaded_image_info_obj.link, &efi_obj_list); +list_add_tail (&bootefi_device_obj.link, &efi_obj_list); +#ifdef CONFIG_PARTITIONS +efi_disk_register (); +#endif + +/* Call our payload! */ +#ifdef DEBUG_EFI +printf ("%s:%d Jumping to 0x%lx\n" , __func__, __LINE__, (long )entry); +#endif +return entry (&loaded_image_info, &systab); +} + + +/* Interpreter command to boot an arbitrary EFI image from memory */ +static int do_bootefi (cmd_tbl_t *cmdtp,int flag,int argc,char *const argv[]) +{ +char *saddr; +unsigned long addr; +int r =0 ; + +if (argc <2 ) +return 1 ; + saddr = argv[1 ]; + + addr =simple_strtoul (saddr,NULL ,16 ); + +printf ("## Starting EFI application at 0x%08lx ...\n" , addr); + r =do_bootefi_exec ((void *)addr); +printf ("## Application terminated, r = %d\n" , r); +
common/cmd_bootefi.c line 153
Quoted 10 lines
common/cmd_bootefi.c new file +10
-0+if (r !=0 ) + r =1 ; + +return r; +} + +U_BOOT_CMD ( + bootefi,2 ,0 , do_bootefi, +"Boot from an EFI image in memory" , + "<image address>\n"
Could you add any more help here?
Quoted 8 lines
common/cmd_bootefi.c new file +1
-1+); --2.1.4
_______________________________________________
U-Boot mailing list
U-Boot at lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot
Regards,
Simon
PATCH 9/14 efi_loader: Implement memory allocation and map
In reply to Alexander GrafDue to popular request, this is a separate patch implementing all of the memory allocation and memory mapping bits.
We assume we always have a linear RAM map. At TOM U-Boot resides. Inside of U-Boot there is the runtime region that we need to explicitly expose via the EFI memory map. Below U-Boot, we reserve 128MB of RAM for LOADER_DATA.
--- lib/efi_loader/efi_boottime.c | 99 ++++++++++++++++++++++++++++++++++++++- lib/efi_loader/efi_image_loader.c | 28 ++++++++++- 2 files changed, 123 insertions(+), 4 deletions(-)
lib/efi_loader/efi_boottime.c +97 -2
@@ -98,6 +98,8 @@ static void efi_restore_tpl(unsigned long old_tpl)static void *efi_alloc (uint64_t len,int memory_type) {switch (memory_type) { +case EFI_LOADER_DATA : +return efi_loader_alloc (len);default :return malloc (len); }
@@ -143,16 +145,109 @@ static efi_status_t efi_free_pages(uint64_t memory, unsigned long pages)return EFI_EXIT (EFI_SUCCESS ); } -/* Will be implemented in a later patch */ +/* +* Returns the EFI memory map. In our case, this looks pretty simple: +* +* ____________________________ TOM +* | | +* | Second half of U-Boot | +* |____________________________| &__efi_runtime_stop +* | | +* | EFI Runtime Services | +* |____________________________| &__efi_runtime_start +* | | +* | First half of U-Boot | +* |____________________________| start of EFI loader allocation space +* | | +* | Free RAM | +* |____________________________| CONFIG_SYS_SDRAM_BASE +* +* All pointers are extended to live on a 4k boundary. After exiting the boot +* services, only the EFI Runtime Services chunk of memory stays alive. +*/ static 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) { +struct efi_mem_desc efi_memory_map[] = { + { +/* RAM before U-Boot */ + .type =EFI_CONVENTIONAL_MEMORY , + .attribute =1 <<EFI_MEMORY_WB_SHIFT , + }, + { +/* First half of U-Boot */ + .type =EFI_LOADER_DATA , + .attribute =1 <<EFI_MEMORY_WB_SHIFT , + }, + { +/* EFI Runtime Services */ + .type =EFI_RUNTIME_SERVICES_CODE , + .attribute = (1 <<EFI_MEMORY_WB_SHIFT ) | + (1ULL <<EFI_MEMORY_RUNTIME_SHIFT ), + }, + { +/* Second half of U-Boot */ + .type =EFI_LOADER_DATA , + .attribute =1 <<EFI_MEMORY_WB_SHIFT , + }, + }; +ulong runtime_start, runtime_end, runtime_len_pages, runtime_len; +EFI_ENTRY ("%p, %p, %p, %p, %p" , memory_map_size, memory_map, map_key, descriptor_size, descriptor_version); -return EFI_EXIT (EFI_UNSUPPORTED ); + + runtime_start = (ulong)&__efi_runtime_start & ~0xfffULL ; + runtime_end = ((ulong )&__efi_runtime_stop +0xfff ) & ~0xfffULL ; + runtime_len_pages = (runtime_end - runtime_start) >>12 ; + runtime_len = runtime_len_pages <<12 ; + +/* Fill in where normal RAM is (up to U-Boot's top of stack) */ + efi_memory_map[0 ].num_pages = gd->start_addr_sp >>12 ; +#ifdef CONFIG_SYS_SDRAM_BASE + efi_memory_map[0 ].physical_start =CONFIG_SYS_SDRAM_BASE ; + efi_memory_map[0 ].virtual_start =CONFIG_SYS_SDRAM_BASE ; + efi_memory_map[0 ].num_pages -=CONFIG_SYS_SDRAM_BASE >>12 ; +#endif + +/* Give us some space for the stack */ + efi_memory_map[0 ].num_pages -= (16 *1024 *1024 ) >>12 ; + +/* Reserve the EFI loader pool */ + efi_memory_map[0 ].num_pages -=EFI_LOADER_POOL_SIZE >>12 ; + +/* Cut out the runtime services */ + efi_memory_map[2 ].physical_start = runtime_start; + efi_memory_map[2 ].virtual_start = efi_memory_map[2 ].physical_start; + efi_memory_map[2 ].num_pages = runtime_len_pages; + +/* Allocate the rest to U-Boot */ + efi_memory_map[1 ].physical_start = efi_memory_map[0 ].physical_start + + (efi_memory_map[0 ].num_pages <<12 ); + efi_memory_map[1 ].virtual_start = efi_memory_map[1 ].physical_start; + efi_memory_map[1 ].num_pages = (runtime_start - + efi_memory_map[1 ].physical_start) >>12 ; + + efi_memory_map[3 ].physical_start = runtime_start + runtime_len; + efi_memory_map[3 ].virtual_start = efi_memory_map[3 ].physical_start; + efi_memory_map[3 ].num_pages = (gd->ram_top - + efi_memory_map[3 ].physical_start) >>12 ; + + *memory_map_size =sizeof (efi_memory_map); + +if (descriptor_size) + *descriptor_size =sizeof (struct efi_mem_desc ); + +if (*memory_map_size <sizeof (efi_memory_map)) { +return EFI_EXIT (EFI_BUFFER_TOO_SMALL ); + } + +if (memory_map) +memcpy (memory_map, efi_memory_map,sizeof (efi_memory_map)); + +return EFI_EXIT (EFI_SUCCESS ); }static efi_status_t efi_allocate_pool (int pool_type,unsigned long size,void **buffer)
lib/efi_loader/efi_image_loader.c +26 -2
@@ -29,10 +29,34 @@ efi_status_t efi_return_handle(void *handle, efi_guid_t *protocol,return EFI_SUCCESS ; } -/* Will be implemented in a later patch */ +/* +* EFI payloads potentially want to load pretty big images into memory, +* so our small malloc region isn't enough for them. However, they usually +* don't need a smart allocator either. +* +* So instead give them a really dumb one. We just reserve EFI_LOADER_POOL_SIZE +* bytes from 16MB below the stack start to give the stack some space. +* Then every allocation gets a 4k aligned chunk from it. We never free. +*/ void *efi_loader_alloc (uint64_t len) { -return NULL ; +static unsigned long loader_pool; +void *r; + +if (!loader_pool) { + loader_pool = ((gd->start_addr_sp >>12 ) <<12 ) - + (16 *MB ) -EFI_LOADER_POOL_SIZE ; + } + + len =ROUND_UP (len,4096 ); +/* Out of memory */ +if ((loader_pool + len) >= (gd->relocaddr -TOTAL_MALLOC_LEN )) +return NULL ; + + r = (void *)loader_pool; + loader_pool += len; + +return r; }/*
Signature · 1 line
2.1.4
PATCH 9/14 efi_loader: Implement memory allocation and map
Hi Alexander,
Quoted 27 lines · Alexander Graf
Due to popular request, this is a separate patch implementing all of the memory allocation and memory mapping bits.
We assume we always have a linear RAM map. At TOM U-Boot resides. Inside of U-Boot there is the runtime region that we need to explicitly expose via the EFI memory map. Below U-Boot, we reserve 128MB of RAM for
LOADER_DATA.Signed-off-by Alexander Graf <[email protected]>--- lib/efi_loader/efi_boottime.c | 99 ++++++++++++++++++++++++++++++++++++++- lib/efi_loader/efi_image_loader.c | 28 ++++++++++- 2 files changed, 123 insertions(+), 4 deletions(-)lib/efi_loader/efi_boottime.c +2
-0@@ -98,6 +98,8 @@ static void efi_restore_tpl(unsigned long old_tpl)static void *efi_alloc (uint64_t len,int memory_type) {switch (memory_type) { +case EFI_LOADER_DATA : +return efi_loader_alloc (len);default :return malloc (len); }
lib/efi_loader/efi_boottime.c static efi_status_t efi_free_pages(uint64_t memory, unsigned long pages) · line 153
Quoted 10 lines
lib/efi_loader/efi_boottime.c +6
-1@@ -143,16 +145,109 @@ static efi_status_t efi_free_pages(uint64_t memory, unsigned long pages)return EFI_EXIT (EFI_SUCCESS ); } -/* Will be implemented in a later patch */ +/* +* Returns the EFI memory map. In our case, this looks pretty simple: +* +* ____________________________ TOM +* | | +* | Second half of U-Boot |
What does 'second half' mean?
Quoted 47 lines
lib/efi_loader/efi_boottime.c +39
-0+* |____________________________| &__efi_runtime_stop +* | | +* | EFI Runtime Services | +* |____________________________| &__efi_runtime_start +* | | +* | First half of U-Boot | +* |____________________________| start of EFI loader allocation space +* | | +* | Free RAM | +* |____________________________| CONFIG_SYS_SDRAM_BASE +* +* All pointers are extended to live on a 4k boundary. After exiting the boot +* services, only the EFI Runtime Services chunk of memory stays alive. +*/ static 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) { +struct efi_mem_desc efi_memory_map[] = { + { +/* RAM before U-Boot */ + .type =EFI_CONVENTIONAL_MEMORY , + .attribute =1 <<EFI_MEMORY_WB_SHIFT , + }, + { +/* First half of U-Boot */ + .type =EFI_LOADER_DATA , + .attribute =1 <<EFI_MEMORY_WB_SHIFT , + }, + { +/* EFI Runtime Services */ + .type =EFI_RUNTIME_SERVICES_CODE , + .attribute = (1 <<EFI_MEMORY_WB_SHIFT ) | + (1ULL <<EFI_MEMORY_RUNTIME_SHIFT ), + }, + { +/* Second half of U-Boot */ + .type =EFI_LOADER_DATA , + .attribute =1 <<EFI_MEMORY_WB_SHIFT , + }, + }; +ulong runtime_start, runtime_end, runtime_len_pages, runtime_len; +EFI_ENTRY ("%p, %p, %p, %p, %p" , memory_map_size, memory_map, map_key, descriptor_size, descriptor_version);
lib/efi_loader/efi_boottime.c static efi_status_t efi_free_pages(uint64_t memory, unsigned long pages) · line 209
Quoted 10 lines
lib/efi_loader/efi_boottime.c +9
-1-return EFI_EXIT (EFI_UNSUPPORTED ); + + runtime_start = (ulong)&__efi_runtime_start & ~0xfffULL ; + runtime_end = ((ulong )&__efi_runtime_stop +0xfff ) & ~0xfffULL ; + runtime_len_pages = (runtime_end - runtime_start) >>12 ; + runtime_len = runtime_len_pages <<12 ; + +/* Fill in where normal RAM is (up to U-Boot's top of stack) */ + efi_memory_map[0 ].num_pages = gd->start_addr_sp >>12 ; +#ifdef CONFIG_SYS_SDRAM_BASE
If not defined, what happens?
Quoted 61 lines
lib/efi_loader/efi_boottime.c +41
-0+ efi_memory_map[0 ].physical_start =CONFIG_SYS_SDRAM_BASE ; + efi_memory_map[0 ].virtual_start =CONFIG_SYS_SDRAM_BASE ; + efi_memory_map[0 ].num_pages -=CONFIG_SYS_SDRAM_BASE >>12 ; +#endif + +/* Give us some space for the stack */ + efi_memory_map[0 ].num_pages -= (16 *1024 *1024 ) >>12 ; + +/* Reserve the EFI loader pool */ + efi_memory_map[0 ].num_pages -=EFI_LOADER_POOL_SIZE >>12 ; + +/* Cut out the runtime services */ + efi_memory_map[2 ].physical_start = runtime_start; + efi_memory_map[2 ].virtual_start = efi_memory_map[2 ].physical_start; + efi_memory_map[2 ].num_pages = runtime_len_pages; + +/* Allocate the rest to U-Boot */ + efi_memory_map[1 ].physical_start = efi_memory_map[0 ].physical_start + + (efi_memory_map[0 ].num_pages <<12 ); + efi_memory_map[1 ].virtual_start = efi_memory_map[1 ].physical_start; + efi_memory_map[1 ].num_pages = (runtime_start - + efi_memory_map[1 ].physical_start) >>12 ; + + efi_memory_map[3 ].physical_start = runtime_start + runtime_len; + efi_memory_map[3 ].virtual_start = efi_memory_map[3 ].physical_start; + efi_memory_map[3 ].num_pages = (gd->ram_top - + efi_memory_map[3 ].physical_start) >>12 ; + + *memory_map_size =sizeof (efi_memory_map); + +if (descriptor_size) + *descriptor_size =sizeof (struct efi_mem_desc ); + +if (*memory_map_size <sizeof (efi_memory_map)) { +return EFI_EXIT (EFI_BUFFER_TOO_SMALL ); + } + +if (memory_map) +memcpy (memory_map, efi_memory_map,sizeof (efi_memory_map)); + +return EFI_EXIT (EFI_SUCCESS ); }static efi_status_t efi_allocate_pool (int pool_type,unsigned long size,void **buffer)lib/efi_loader/efi_image_loader.c +8
-1@@ -29,10 +29,34 @@ efi_status_t efi_return_handle(void *handle, efi_guid_t *protocol,return EFI_SUCCESS ; } -/* Will be implemented in a later patch */ +/* +* EFI payloads potentially want to load pretty big images into memory, +* so our small malloc region isn't enough for them. However, they usually +* don't need a smart allocator either. +* +* So instead give them a really dumb one. We just reserve EFI_LOADER_POOL_SIZE +* bytes from 16MB below the stack start to give the stack some space. +* Then every allocation gets a 4k aligned chunk from it. We never free.
lib/efi_loader/efi_image_loader.c efi_status_t efi_return_handle(void *handle, efi_guid_t *protocol, · line 48
Quoted 10 lines
lib/efi_loader/efi_image_loader.c +7
-1+*/ void *efi_loader_alloc (uint64_t len) { -return NULL ; +static unsigned long loader_pool; +void *r; + +if (!loader_pool) { + loader_pool = ((gd->start_addr_sp >>12 ) <<12 ) - + (16 *MB ) -EFI_LOADER_POOL_SIZE ;
I think it would be better to reserve this in board_f() with a reserve...() function. Perhaps store the address in global_data.
lib/efi_loader/efi_image_loader.c efi_status_t efi_return_handle(void *handle, efi_guid_t *protocol, · line 53
Quoted 5 lines
lib/efi_loader/efi_image_loader.c +5
-0+ } + + len =ROUND_UP (len,4096 ); +/* Out of memory */ +if ((loader_pool + len) >= (gd->relocaddr -TOTAL_MALLOC_LEN ))
debug() here?
Quoted 11 lines
lib/efi_loader/efi_image_loader.c +6
-1+return NULL ; + + r = (void *)loader_pool; + loader_pool += len; + +return r; }/* -- 2.1.4
Regards,
Simon
PATCH 9/14 efi_loader: Implement memory allocation and map
Quoted 33 lines · Simon Glass
Hi Alexander,
Quoted 30 lines · Alexander Graf
Due to popular request, this is a separate patch implementing all of the memory allocation and memory mapping bits.
We assume we always have a linear RAM map. At TOM U-Boot resides. Inside of U-Boot there is the runtime region that we need to explicitly expose via the EFI memory map. Below U-Boot, we reserve 128MB of RAM for
LOADER_DATA.Signed-off-by Alexander Graf <[email protected]>--- lib/efi_loader/efi_boottime.c | 99 ++++++++++++++++++++++++++++++++++++++- lib/efi_loader/efi_image_loader.c | 28 ++++++++++- 2 files changed, 123 insertions(+), 4 deletions(-)lib/efi_loader/efi_boottime.c +2
-0@@ -98,6 +98,8 @@ static void efi_restore_tpl(unsigned long old_tpl)static void *efi_alloc (uint64_t len,int memory_type) {switch (memory_type) { +case EFI_LOADER_DATA : +return efi_loader_alloc (len);default :return malloc (len); }@@ -143,16 +145,109 @@ static efi_status_t efi_free_pages(uint64_t memory, unsigned long pages)return EFI_EXIT (EFI_SUCCESS ); }
Quoted 8 lines
lib/efi_loader/efi_boottime.c static efi_status_t efi_free_pages(uint64_t memory, unsigned long pages) · line 153
Quoted 7 lines
lib/efi_loader/efi_boottime.c +6
-1-/* Will be implemented in a later patch */ +/* +* Returns the EFI memory map. In our case, this looks pretty simple: +* +* ____________________________ TOM +* | | +* | Second half of U-Boot | What does 'second half' mean?
It means that there's one big chunk of "U-Boot" with the RTS sitting in the middle, dividing it into 2 halves :).
I hope my new memory allocation code makes this more obvious.
Quoted 51 lines
Quoted 51 lines
lib/efi_loader/efi_boottime.c +41
-1+* |____________________________| &__efi_runtime_stop +* | | +* | EFI Runtime Services | +* |____________________________| &__efi_runtime_start +* | | +* | First half of U-Boot | +* |____________________________| start of EFI loader allocation space +* | | +* | Free RAM | +* |____________________________| CONFIG_SYS_SDRAM_BASE +* +* All pointers are extended to live on a 4k boundary. After exiting the boot +* services, only the EFI Runtime Services chunk of memory stays alive. +*/ static 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) { +struct efi_mem_desc efi_memory_map[] = { + { +/* RAM before U-Boot */ + .type =EFI_CONVENTIONAL_MEMORY , + .attribute =1 <<EFI_MEMORY_WB_SHIFT , + }, + { +/* First half of U-Boot */ + .type =EFI_LOADER_DATA , + .attribute =1 <<EFI_MEMORY_WB_SHIFT , + }, + { +/* EFI Runtime Services */ + .type =EFI_RUNTIME_SERVICES_CODE , + .attribute = (1 <<EFI_MEMORY_WB_SHIFT ) | + (1ULL <<EFI_MEMORY_RUNTIME_SHIFT ), + }, + { +/* Second half of U-Boot */ + .type =EFI_LOADER_DATA , + .attribute =1 <<EFI_MEMORY_WB_SHIFT , + }, + }; +ulong runtime_start, runtime_end, runtime_len_pages, runtime_len; +EFI_ENTRY ("%p, %p, %p, %p, %p" , memory_map_size, memory_map, map_key, descriptor_size, descriptor_version); -return EFI_EXIT (EFI_UNSUPPORTED ); + + runtime_start = (ulong)&__efi_runtime_start & ~0xfffULL ;
Quoted 8 lines
lib/efi_loader/efi_boottime.c static efi_status_t efi_free_pages(uint64_t memory, unsigned long pages) · line 209
Quoted 7 lines
lib/efi_loader/efi_boottime.c +7
-0+ runtime_end = ((ulong )&__efi_runtime_stop +0xfff ) & ~0xfffULL ; + runtime_len_pages = (runtime_end - runtime_start) >>12 ; + runtime_len = runtime_len_pages <<12 ; + +/* Fill in where normal RAM is (up to U-Boot's top of stack) */ + efi_memory_map[0 ].num_pages = gd->start_addr_sp >>12 ; +#ifdef CONFIG_SYS_SDRAM_BASE If not defined, what happens?
The new code uses gd->bd->bi_dram.
Quoted 66 lines
Quoted 66 lines
lib/efi_loader/efi_boottime.c +41
-0+ efi_memory_map[0 ].physical_start =CONFIG_SYS_SDRAM_BASE ; + efi_memory_map[0 ].virtual_start =CONFIG_SYS_SDRAM_BASE ; + efi_memory_map[0 ].num_pages -=CONFIG_SYS_SDRAM_BASE >>12 ; +#endif + +/* Give us some space for the stack */ + efi_memory_map[0 ].num_pages -= (16 *1024 *1024 ) >>12 ; + +/* Reserve the EFI loader pool */ + efi_memory_map[0 ].num_pages -=EFI_LOADER_POOL_SIZE >>12 ; + +/* Cut out the runtime services */ + efi_memory_map[2 ].physical_start = runtime_start; + efi_memory_map[2 ].virtual_start = efi_memory_map[2 ].physical_start; + efi_memory_map[2 ].num_pages = runtime_len_pages; + +/* Allocate the rest to U-Boot */ + efi_memory_map[1 ].physical_start = efi_memory_map[0 ].physical_start + + (efi_memory_map[0 ].num_pages <<12 ); + efi_memory_map[1 ].virtual_start = efi_memory_map[1 ].physical_start; + efi_memory_map[1 ].num_pages = (runtime_start - + efi_memory_map[1 ].physical_start) >>12 ; + + efi_memory_map[3 ].physical_start = runtime_start + runtime_len; + efi_memory_map[3 ].virtual_start = efi_memory_map[3 ].physical_start; + efi_memory_map[3 ].num_pages = (gd->ram_top - + efi_memory_map[3 ].physical_start) >>12 ; + + *memory_map_size =sizeof (efi_memory_map); + +if (descriptor_size) + *descriptor_size =sizeof (struct efi_mem_desc ); + +if (*memory_map_size <sizeof (efi_memory_map)) { +return EFI_EXIT (EFI_BUFFER_TOO_SMALL ); + } + +if (memory_map) +memcpy (memory_map, efi_memory_map,sizeof (efi_memory_map)); + +return EFI_EXIT (EFI_SUCCESS ); }static efi_status_t efi_allocate_pool (int pool_type,unsigned long size,void **buffer)lib/efi_loader/efi_image_loader.c +9
-2@@ -29,10 +29,34 @@ efi_status_t efi_return_handle(void *handle, efi_guid_t *protocol,return EFI_SUCCESS ; } -/* Will be implemented in a later patch */ +/* +* EFI payloads potentially want to load pretty big images into memory, +* so our small malloc region isn't enough for them. However, they usually +* don't need a smart allocator either. +* +* So instead give them a really dumb one. We just reserve EFI_LOADER_POOL_SIZE +* bytes from 16MB below the stack start to give the stack some space. +* Then every allocation gets a 4k aligned chunk from it. We never free. +*/ void *efi_loader_alloc (uint64_t len) { -return NULL ;
Quoted 8 lines
lib/efi_loader/efi_image_loader.c efi_status_t efi_return_handle(void *handle, efi_guid_t *protocol, · line 48
Quoted 6 lines
lib/efi_loader/efi_image_loader.c +6
-0+static unsigned long loader_pool; +void *r; + +if (!loader_pool) { + loader_pool = ((gd->start_addr_sp >>12 ) <<12 ) - + (16 *MB ) -EFI_LOADER_POOL_SIZE ;I think it would be better to reserve this in
board_f()with a reserve...() function. Perhaps store the address inglobal_data.
The new code doesn't reserve anything anymore :)
Alex
Quoted 21 lines
lib/efi_loader/efi_image_loader.c efi_status_t efi_return_handle(void *handle, efi_guid_t *protocol, · line 53
Quoted 6 lines
lib/efi_loader/efi_image_loader.c +5
-0+ } + + len =ROUND_UP (len,4096 ); +/* Out of memory */ +if ((loader_pool + len) >= (gd->relocaddr -TOTAL_MALLOC_LEN ))
debug()here?Quoted 11 lines
lib/efi_loader/efi_image_loader.c +6
-1+return NULL ; + + r = (void *)loader_pool; + loader_pool += len; + +return r; }/* -- 2.1.4
Regards,
Simon
PATCH 10/14 arm64: Allow exceptions to return
In reply to Alexander GrafOur 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
@@ -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.1.4
PATCH 11/14 arm64: Allow EFI payload code to take exceptions
In reply to Alexander GrafThere 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
@@ -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.1.4
PATCH 12/14 efi_loader: Add DCACHE_OFF support for arm64
In reply to Alexander GrafOn 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 ++++++++++++++++++++++++++++++++++++++++ common/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
@@ -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
@@ -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
@@ -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
common/cmd_bootefi.c +5 -0
@@ -103,6 +103,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.1.4
PATCH 13/14 efi_loader: hook up in build environment
In reply to Alexander GrafNow 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
--- lib/Kconfig | 1 + lib/Makefile | 1 + lib/efi_loader/Kconfig | 9 +++++++++ lib/efi_loader/Makefile | 11 +++++++++++ 4 files changed, 22 insertions(+) create mode 100644 lib/efi_loader/Kconfig create mode 100644 lib/efi_loader/Makefile
lib/Kconfig +1 -0
@@ -138,5 +138,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
@@ -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
@@ -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 +11 -0
@@ -0,0 +1,11 @@ +# +# (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-$(CONFIG_PARTITIONS) += efi_disk.o
Signature · 1 line
2.1.4
PATCH 13/14 efi_loader: hook up in build environment
Quoted 11 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.
Signed-off-by Alexander Graf <[email protected]>---
v1 -> v2:
- Move to GPLv2+
- Default to y
Quoted 8 lines
--- lib/Kconfig | 1 + lib/Makefile | 1 + lib/efi_loader/Kconfig | 9 +++++++++ lib/efi_loader/Makefile | 11 +++++++++++ 4 files changed, 22 insertions(+) create mode 100644 lib/efi_loader/Kconfig create mode 100644 lib/efi_loader/Makefile
PATCH 14/14 efi_loader: Add distro boot script for removable media
In reply to Alexander GrafUEFI 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
@@ -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
@@ -204,6 +246,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" \
@@ -245,7 +288,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.1.4
PATCH 14/14 efi_loader: Add distro boot script for removable media
Hi Alexander,
Quoted 13 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.
Quoted 4 lines
Signed-off-by Alexander Graf <[email protected]>--- include/config_distro_bootcmd.h | 47 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-)
Will this work OK with FITs?
Also, can you add an entry to MAINTAINERS?
Regards,
Simon
PATCH 14/14 efi_loader: Add distro boot script for removable media
Quoted 16 lines · Simon Glass
Hi Alexander,
Quoted 13 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.
Quoted 7 lines
Quoted 4 lines
Signed-off-by Alexander Graf <[email protected]>--- include/config_distro_bootcmd.h | 47 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-)Reviewed-by Simon Glass <[email protected]>Will this work OK with FITs?
Uh, why would it not work ok with FITs?
Quoted 1 line
Also, can you add an entry to
MAINTAINERS?
Sure :)
Btw, in your move of all cmd files into their own directory you forgot to update the MAINTAINERS file ;)
Alex
cover EFI payload / application support v2
In reply to Alexander GrafHi Alexander,
Quoted 6 lines · Alexander Graf
This is my 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.
Well some might argue that grub2 and UEFI are their own nightmares :-)
Quoted 6 lines
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.
The API interface is mostly for closed-source work I think.
Quoted 25 lines
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)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
Quoted 3 lines
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.
I'd suggest creating a README with the above info. The cover letter will vanish pretty fast. Perhaps also update README.efi with this new option.
Another thing you could list is efi_set_watchdog_timer().
Quoted 3 lines
Version 2 was successfully tested to boot grub2 and Linux from there on a HiKey (AArch64, dcache disabled) and on a BeagleBone Black.
Do you have a UEFI image for BBB that I can put on an SD card or otherwise boot?
For now I've had a play with Minnowboard, which is x86. The main thing I noticed is that the API function implements should have EFIAPI on them also. I'll make a few other comments on the patches. But overall it seems to function and I think your implementation is nice.
I was able to get grub to boot but it just says 'Welcome to GRUB!' and then 'error: disk ',gpt4' not found'. I'm not sure what that means.
U-Boot 2016.01-00860-geb4b602-dirty (Jan 31 2016 - 08:02:54 -0700)
CPU: x86_64, vendor Intel, device 30673h DRAM: 2 GiB efi_runtime_relocate: Relocating to offset=7ba5d000 MMC: ValleyView SDHCI: 0, ValleyView SDHCI: 1 SF: Detected W25Q64DW with page size 256 Bytes, erase size 4 KiB, total 8 MiB
*** Warning - bad CRC, using default environment
Video: 1280x1024x16
Model: Intel Minnowboard Max
SF: Detected W25Q64DW with page size 256 Bytes, erase size 4 KiB, total 8 MiB
SCSI: SATA link 0 timeout.
Target spinup took 0 ms.
AHCI 0001.0300 32 slots 2 ports 3 Gbps 0x3 impl SATA mode
flags: 64bit ncq stag pm led clo pio slum part sxs
scanning bus for devices...
Device 0: (1:0) Vendor: ATA Prod.: ADATA SP310 Rev: 5.2
Type: Hard Disk
Capacity: 30533.8 MB = 29.8 GB (62533296 x 512)
Found 1 device(s).
Net:
Warning: eth_rtl8169 using MAC address from ROM
eth0: eth_rtl8169
Hit any key to stop autoboot: 0
reading grubia32.efi
85504 bytes read in 16 ms (5.1 MiB/s)
## Starting EFI application at 0x00010000 ...
WARNING: No device tree loaded, expect boot to fail
Scanning disks on scsi...
Scanning disks on usb...
Scanning disks on mmc...
Card did not respond to voltage select!
MMC Device 2 not found
MMC Device 3 not found
Found 2 disks
do_bootefi_exec:134 Jumping to 0x72857400
EFI: Entry efi_open_protocol(7bab8c18, 728609a0, 7b857ab8, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handler loaded_image0
EFI: Exit 0
EFI: Entry efi_locate_protocol(72860990, 00000000, 7b857ac8)
EFI: Exit 0
EFI: Entry efi_cin_get_mode(7baa79cc, 7b857af8, 00000000, 00000000)
EFI: Exit 0
EFI: Entry efi_locate_protocol(72860990, 00000000, 7b857aa8)
EFI: Exit 0
EFI: Entry efi_cin_get_mode(7baa79cc, 7b857ad8, 00000000, 00000000)
EFI: Exit 0
EFI: Entry efi_cout_enable_cursor(7baa79d8, 1)
EFI: Exit 80000003
EFI: Entry efi_allocate_pages(1, 2, 0x6, 7b857a74)
EFI: Exit 0
EFI: Entry efi_get_memory_map(7b857b04, 7286c000, 7b857a64, 7b857b08, 7b857a68)
EFI: Exit 0
EFI: Entry efi_allocate_pages(2, 2, 0x1ca15, 7b857a74)
EFI: Exit 0
EFI: Entry efi_free_pages(7286c000, 0x6)
EFI: Exit 0
EFI: Entry efi_set_watchdog_timer(0, 0x0, 0, 00000000)
EFI: App called into unimplemented function efi_set_watchdog_timer
EFI: Exit 80000003
EFI: Exit 80000003
EFI: App called into unimplemented function efi_set_watchdog_timer
EFI: Exit 80000003
EFI: Entry efi_locate_handle(2, 72860980, 00000000, 7b857a88, 72856fe0)
EFI: Exit 0
EFI: Entry efi_open_protocol(7b863108, 728609b0, 7b857a78, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handler open_dpefi_disk_open_dp
EFI: Exit 0
EFI: Entry efi_open_protocol(7b863108, 72860980, 7b857a98, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handler open_block efi_disk_open_block
EFI: Exit 0
EFI: Entry efi_open_protocol(7b8631d8, 728609b0, 7b857a78, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handler open_dpefi_disk_open_dp
EFI: Exit 0
EFI: Entry efi_open_protocol(7b8631d8, 72860980, 7b857a98, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handler open_block efi_disk_open_block
EFI: Exit 0
EFI: Entry efi_cout_set_attribute(7baa79d8, 70)
EFI: Exit 80000003
Welcome to GRUB!
EFI: Entry efi_cout_set_attribute(7baa79d8, 7)
EFI: Exit 80000003
EFI: Entry efi_open_protocol(7bab8c18, 728609a0, 7b857ad8, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handler loaded_image0
EFI: Exit 0
EFI: Entry efi_open_protocol(7bab8bd0, 728609b0, 7b857a78, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handler bootefi0
EFI: Exit 0
error: disk `,gpt4' not found.
Entering rescue mode...
grub rescue>
I suppose my grub could be wrong. If you can point me to one that I should use I could try again. I pushed your tree (rebased to mainline) plus my messing-around patch to u-boot-x86/efi-working.
It would be good to get this series applied soon.
Quoted 10 lines
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-v2
or just use the readily compiled version from
https://build.opensuse.org/project/show/home:algraf:branches:Base:System
Regards,
Simon
cover EFI payload / application support v2
Quoted 10 lines · Simon Glass
Hi Alexander,
Quoted 6 lines · Alexander Graf
This is my 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.
Well some might argue that grub2 and UEFI are their own nightmares :-)
They are, but they are the same nightmare everyone else is dreaming ;).
Quoted 32 lines
Quoted 6 lines
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.
The API interface is mostly for closed-source work I think.
Quoted 24 lines
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)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
Quoted 6 lines
Quoted 3 lines
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.
I'd suggest creating a README with the above info. The cover letter will vanish pretty fast. Perhaps also update README.efi with this new option.
Good idea.
Quoted 2 lines
Another thing you could list is
efi_set_watchdog_timer().
What about it exactly? That it's not supported atm?
Quoted 4 lines
Quoted 3 lines
Version 2 was successfully tested to boot grub2 and Linux from there on a HiKey (AArch64, dcache disabled) and on a BeagleBone Black.
Do you have a UEFI image for BBB that I can put on an SD card or otherwise boot?
Phew, I hand-crafted one to play around with. You can use the hip04d01 image from
Just extract the tar.xz, to get to the actual image .xz file.
That image obviously has an incorrect kernel for the BBB, but everything up to grub2 should work with it.
My plan was to slowly move all openSUSE arm targets that use our own u-boot binaries to EFI once this patch set goes in ;).
Quoted 3 lines
For now I've had a play with Minnowboard, which is x86. The main thing I noticed is that the API function implements should have EFIAPI on them also.
Yes :). I didn't expect anyone to actually care about running this on x86 which is the only architecture that has different calling conventions for EFI. I'm very pleasantly surprised that you are interested and since you already have a patch to add them, I guess you can as well just post that once the base support is in :).
Quoted 2 lines
I'll make a few other comments on the patches. But overall
it seems to function and I think your implementation is nice.
Thanks :)
Quoted 4 lines
I was able to get grub to boot but it just says 'Welcome to GRUB!' and then 'error: disk ',gpt4' not found'. I'm not sure what that means.
It might mean memory corruption. I'm not sure where from though :). It could also mean register clobbering.
Quoted 93 lines
U-Boot 2016.01-00860-geb4b602-dirty (Jan 31 2016 - 08:02:54 -0700)
CPU: x86_64, vendor Intel, device 30673h DRAM: 2 GiB efi_runtime_relocate: Relocating to offset=7ba5d000 MMC: ValleyView SDHCI: 0, ValleyView SDHCI: 1 SF: Detected W25Q64DW with page size 256 Bytes, erase size 4 KiB, total 8 MiB*** Warning - bad CRC, using default environment
Video: 1280x1024x16
Model: Intel Minnowboard Max
SF: Detected W25Q64DW with page size 256 Bytes, erase size 4 KiB, total 8 MiB
SCSI: SATA link 0 timeout.
Target spinup took 0 ms.
AHCI 0001.0300 32 slots 2 ports 3 Gbps0x3impl SATA mode
flags: 64bit ncq stag pm led clo pio slum part sxs
scanning bus for devices...
Device 0: (1:0) Vendor: ATA Prod.: ADATA SP310 Rev: 5.2
Type: Hard Disk
Capacity: 30533.8 MB = 29.8 GB (62533296 x 512)
Found 1 device(s).
Net:
Warning:eth_rtl8169using MAC address from ROM
eth0:eth_rtl8169
Hit any key to stop autoboot: 0
reading grubia32.efi
85504 bytes read in 16 ms (5.1 MiB/s)
## Starting EFI application at0x00010000...
WARNING: No device tree loaded, expect boot to fail
Scanning disks on scsi...
Scanning disks on usb...
Scanning disks on mmc...
Card did not respond to voltage select!
MMC Device 2 not found
MMC Device 3 not found
Found 2 disks
do_bootefi_exec:134 Jumping to0x72857400
EFI: Entryefi_open_protocol(7bab8c18, 728609a0, 7b857ab8, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handlerloaded_image0
EFI: Exit 0
EFI: Entryefi_locate_protocol(72860990, 00000000, 7b857ac8)
EFI: Exit 0
EFI: Entryefi_cin_get_mode(7baa79cc, 7b857af8, 00000000, 00000000)
EFI: Exit 0
EFI: Entryefi_locate_protocol(72860990, 00000000, 7b857aa8)
EFI: Exit 0
EFI: Entryefi_cin_get_mode(7baa79cc, 7b857ad8, 00000000, 00000000)
EFI: Exit 0
EFI: Entryefi_cout_enable_cursor(7baa79d8, 1)
EFI: Exit 80000003
EFI: Entryefi_allocate_pages(1, 2, 0x6, 7b857a74)
EFI: Exit 0
EFI: Entryefi_get_memory_map(7b857b04, 7286c000, 7b857a64, 7b857b08, 7b857a68)
EFI: Exit 0
EFI: Entryefi_allocate_pages(2, 2, 0x1ca15, 7b857a74)
EFI: Exit 0
EFI: Entryefi_free_pages(7286c000, 0x6)
EFI: Exit 0
EFI: Entryefi_set_watchdog_timer(0, 0x0, 0, 00000000)
EFI: App called into unimplemented functionefi_set_watchdog_timer
EFI: Exit 80000003
EFI: Exit 80000003
EFI: App called into unimplemented functionefi_set_watchdog_timer
EFI: Exit 80000003
EFI: Entryefi_locate_handle(2, 72860980, 00000000, 7b857a88, 72856fe0)
EFI: Exit 0
EFI: Entryefi_open_protocol(7b863108, 728609b0, 7b857a78, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handleropen_dpefi_disk_open_dp
EFI: Exit 0
EFI: Entryefi_open_protocol(7b863108, 72860980, 7b857a98, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handleropen_blockefi_disk_open_block
EFI: Exit 0
EFI: Entryefi_open_protocol(7b8631d8, 728609b0, 7b857a78, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handleropen_dpefi_disk_open_dp
EFI: Exit 0
EFI: Entryefi_open_protocol(7b8631d8, 72860980, 7b857a98, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handleropen_blockefi_disk_open_block
EFI: Exit 0
EFI: Entryefi_cout_set_attribute(7baa79d8, 70)
EFI: Exit 80000003
Welcome to GRUB!
Quoted 13 lines
EFI: Entry
efi_cout_set_attribute(7baa79d8, 7)
EFI: Exit 80000003
EFI: Entryefi_open_protocol(7bab8c18, 728609a0, 7b857ad8, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handlerloaded_image0
EFI: Exit 0
EFI: Entryefi_open_protocol(7bab8bd0, 728609b0, 7b857a78, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handler bootefi0
EFI: Exit 0
error: disk `,gpt4' not found.
Entering rescue mode...
grub rescue>
Does grub see the hard disks? What happens when you do ls (hd,<TAB>? Also, try the ls command but first do set debug=all to get grub internal debugging enabled too.
Quoted 7 lines
I suppose my grub could be wrong. If you can point me to one that I should use I could try again. I pushed your tree (rebased to mainline) plus my messing-around patch to u-boot-x86/efi-working.
It would be good to get this series applied soon.
Awesome.
I'm currently rewriting the memory map code, making it more robust, easy to understand and extensible for modules that may want to register runtime service mmio regions.
Once that works and I have all of Leif's comments sorted out, I'll post v3.
Alex
cover EFI payload / application support v2
Hi Alexander,
Quoted 71 lines · Alexander Graf
Quoted 7 lines · Simon Glass
Hi Alexander,
Quoted 3 lines · Alexander Graf
This is my Christmas present for my openSUSE friends :).
Quoted 7 lines
Quoted 5 lines
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.Well some might argue that grub2 and UEFI are their own nightmares :-)
They are, but they are the same nightmare everyone else is dreaming ;).
Quoted 39 lines
Quoted 7 lines
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.
The API interface is mostly for closed-source work I think.
Quoted 29 lines
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)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 exitQuoted 8 lines
Quoted 4 lines
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.I'd suggest creating a README with the above info. The cover letter will vanish pretty fast. Perhaps also update README.efi with this new option.
Quoted 7 lines
Good idea.
Quoted 2 lines
Another thing you could list is
efi_set_watchdog_timer().What about it exactly? That it's not supported atm?
Yes.
Quoted 12 lines
Quoted 7 lines
Quoted 4 lines
Version 2 was successfully tested to boot grub2 and Linux from there on a HiKey (AArch64, dcache disabled) and on a BeagleBone Black.
Do you have a UEFI image for BBB that I can put on an SD card or otherwise boot?
Phew, I hand-crafted one to play around with. You can use the hip04d01 image from
Quoted 6 lines
Just extract the tar.xz, to get to the actual image .xz file.
That image obviously has an incorrect kernel for the BBB, but everything up to grub2 should work with it.
I'm not sure what file to use. I found a file with a .12.1 extension which I tried to dd to a uSD card but it did not boot.
Do you have an ARM grub binary (in EFI format)? II could test with just that piece. tried building grub but it says cross-compiling isn't recommended, and I got an error about a missing ../include/grub/machine/kernel.h.
Quoted 3 lines
My plan was to slowly move all openSUSE arm targets that use our own u-boot binaries to EFI once this patch set goes in ;).
BTW out of interest how come SUSE doesn't use the same distro boot thing as other distributions?
Quoted 11 lines
Quoted 4 lines
For now I've had a play with Minnowboard, which is x86. The main thing I noticed is that the API function implements should have EFIAPI on them also.
Yes :). I didn't expect anyone to actually care about running this on x86 which is the only architecture that has different calling conventions for EFI. I'm very pleasantly surprised that you are interested and since you already have a patch to add them, I guess you can as well just post that once the base support is in :).
OK. I suppose because EFIAPI is empty on ARM it doesn't matter. But strictly speaking the declaration should match the implementation.
U-Boot mostly uses FIT which provides secure boot and it can boot both 32- and 64-bit kernels directly. But there is still the odd setup.bin thing Linux uses.
Perhaps my main interest is fiddling with UEFI without having to use its code base. :-)
Quoted 141 lines
Quoted 3 lines
I'll make a few other comments on the patches. But overall
it seems to function and I think your implementation is nice.Thanks :)
Quoted 4 lines
I was able to get grub to boot but it just says 'Welcome to GRUB!' and then 'error: disk ',gpt4' not found'. I'm not sure what that means.
It might mean memory corruption. I'm not sure where from though :). It could also mean register clobbering.
Quoted 96 lines
U-Boot 2016.01-00860-geb4b602-dirty (Jan 31 2016 - 08:02:54 -0700)
CPU: x86_64, vendor Intel, device 30673h DRAM: 2 GiB efi_runtime_relocate: Relocating to offset=7ba5d000 MMC: ValleyView SDHCI: 0, ValleyView SDHCI: 1 SF: Detected W25Q64DW with page size 256 Bytes, erase size 4 KiB, total 8MiB
*** Warning - bad CRC, using default environmentVideo: 1280x1024x16
Model: Intel Minnowboard Max
SF: Detected W25Q64DW with page size 256 Bytes, erase size 4 KiB, total 8 MiB
SCSI: SATA link 0 timeout.
Target spinup took 0 ms.
AHCI 0001.0300 32 slots 2 ports 3 Gbps0x3impl SATA mode
flags: 64bit ncq stag pm led clo pio slum part sxs
scanning bus for devices...
Device 0: (1:0) Vendor: ATA Prod.: ADATA SP310 Rev: 5.2
Type: Hard Disk
Capacity: 30533.8 MB = 29.8 GB (62533296 x 512)
Found 1 device(s).
Net:
Warning:eth_rtl8169using MAC address from ROM
eth0:eth_rtl8169
Hit any key to stop autoboot: 0
reading grubia32.efi
85504 bytes read in 16 ms (5.1 MiB/s)
## Starting EFI application at0x00010000...
WARNING: No device tree loaded, expect boot to fail
Scanning disks on scsi...
Scanning disks on usb...
Scanning disks on mmc...
Card did not respond to voltage select!
MMC Device 2 not found
MMC Device 3 not found
Found 2 disks
do_bootefi_exec:134 Jumping to0x72857400
EFI: Entryefi_open_protocol(7bab8c18, 728609a0, 7b857ab8, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handlerloaded_image0
EFI: Exit 0
EFI: Entryefi_locate_protocol(72860990, 00000000, 7b857ac8)
EFI: Exit 0
EFI: Entryefi_cin_get_mode(7baa79cc, 7b857af8, 00000000, 00000000)
EFI: Exit 0
EFI: Entryefi_locate_protocol(72860990, 00000000, 7b857aa8)
EFI: Exit 0
EFI: Entryefi_cin_get_mode(7baa79cc, 7b857ad8, 00000000, 00000000)
EFI: Exit 0
EFI: Entryefi_cout_enable_cursor(7baa79d8, 1)
EFI: Exit 80000003
EFI: Entryefi_allocate_pages(1, 2, 0x6, 7b857a74)
EFI: Exit 0
EFI: Entryefi_get_memory_map(7b857b04, 7286c000, 7b857a64, 7b857b08, 7b857a68)
EFI: Exit 0
EFI: Entryefi_allocate_pages(2, 2, 0x1ca15, 7b857a74)
EFI: Exit 0
EFI: Entryefi_free_pages(7286c000, 0x6)
EFI: Exit 0
EFI: Entryefi_set_watchdog_timer(0, 0x0, 0, 00000000)
EFI: App called into unimplemented functionefi_set_watchdog_timer
EFI: Exit 80000003
EFI: Exit 80000003
EFI: App called into unimplemented functionefi_set_watchdog_timer
EFI: Exit 80000003
EFI: Entryefi_locate_handle(2, 72860980, 00000000, 7b857a88, 72856fe0)
EFI: Exit 0
EFI: Entryefi_open_protocol(7b863108, 728609b0, 7b857a78, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handleropen_dpefi_disk_open_dp
EFI: Exit 0
EFI: Entryefi_open_protocol(7b863108, 72860980, 7b857a98, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handleropen_blockefi_disk_open_block
EFI: Exit 0
EFI: Entryefi_open_protocol(7b8631d8, 728609b0, 7b857a78, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handleropen_dpefi_disk_open_dp
EFI: Exit 0
EFI: Entryefi_open_protocol(7b8631d8, 72860980, 7b857a98, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handleropen_blockefi_disk_open_block
EFI: Exit 0
EFI: Entryefi_cout_set_attribute(7baa79d8, 70)
EFI: Exit 80000003
Welcome to GRUB!Quoted 13 lines
EFI: Entry
efi_cout_set_attribute(7baa79d8, 7)
EFI: Exit 80000003
EFI: Entryefi_open_protocol(7bab8c18, 728609a0, 7b857ad8, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handlerloaded_image0
EFI: Exit 0
EFI: Entryefi_open_protocol(7bab8bd0, 728609b0, 7b857a78, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handler bootefi0
EFI: Exit 0
error: disk `,gpt4' not found.
Entering rescue mode...
grub rescue>Does grub see the hard disks? What happens when you do ls (hd,<TAB>? Also, try the ls command but first do set debug=all to get grub internal debugging enabled too.
Quoted 7 lines
I suppose my grub could be wrong. If you can point me to one that I should use I could try again. I pushed your tree (rebased to mainline) plus my messing-around patch to u-boot-x86/efi-working.
It would be good to get this series applied soon.
Quoted 7 lines
Awesome.
I'm currently rewriting the memory map code, making it more robust, easy to understand and extensible for modules that may want to register runtime service mmio regions.
Once that works and I have all of Leif's comments sorted out, I'll post v3.
Sounds good.
Quoted 4 lines
Alex
Regards,
Simon
cover EFI payload / application support v2
Hi Alexander,
Quoted 103 lines · Simon Glass
Hi Alexander,
Quoted 71 lines · Alexander Graf
Quoted 7 lines · Simon Glass
Hi Alexander,
Quoted 3 lines · Alexander Graf
This is my Christmas present for my openSUSE friends :).
Quoted 7 lines
Quoted 5 lines
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.Well some might argue that grub2 and UEFI are their own nightmares :-)
They are, but they are the same nightmare everyone else is dreaming ;).
Quoted 39 lines
Quoted 7 lines
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.
The API interface is mostly for closed-source work I think.
Quoted 29 lines
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)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 exitQuoted 8 lines
Quoted 4 lines
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.I'd suggest creating a README with the above info. The cover letter will vanish pretty fast. Perhaps also update README.efi with this new option.
Quoted 7 lines
Good idea.
Quoted 2 lines
Another thing you could list is
efi_set_watchdog_timer().What about it exactly? That it's not supported atm?
Yes.
Quoted 12 lines
Quoted 7 lines
Quoted 4 lines
Version 2 was successfully tested to boot grub2 and Linux from there on a HiKey (AArch64, dcache disabled) and on a BeagleBone Black.
Do you have a UEFI image for BBB that I can put on an SD card or otherwise boot?
Phew, I hand-crafted one to play around with. You can use the hip04d01 image from
Quoted 6 lines
Just extract the tar.xz, to get to the actual image .xz file.
That image obviously has an incorrect kernel for the BBB, but everything up to grub2 should work with it.
Quoted 7 lines
I'm not sure what file to use. I found a file with a .12.1 extension which I tried to dd to a uSD card but it did not boot.
Do you have an ARM grub binary (in EFI format)? II could test with just that piece. tried building grub but it says cross-compiling isn't recommended, and I got an error about a missing
../include/grub/machine/kernel.h.
Sorry, scratch that. I moved it to a different directory which must have made the build system unhappy. I re-ran autogen.sh and it worked.
Quoted 173 lines
Quoted 4 lines
My plan was to slowly move all openSUSE arm targets that use our own u-boot binaries to EFI once this patch set goes in ;).
BTW out of interest how come SUSE doesn't use the same distro boot thing as other distributions?
Quoted 11 lines
Quoted 4 lines
For now I've had a play with Minnowboard, which is x86. The main thing I noticed is that the API function implements should have EFIAPI on them also.
Yes :). I didn't expect anyone to actually care about running this on x86 which is the only architecture that has different calling conventions for EFI. I'm very pleasantly surprised that you are interested and since you already have a patch to add them, I guess you can as well just post that once the base support is in :).
OK. I suppose because EFIAPI is empty on ARM it doesn't matter. But strictly speaking the declaration should match the implementation.
U-Boot mostly uses FIT which provides secure boot and it can boot both 32- and 64-bit kernels directly. But there is still the odd setup.bin thing Linux uses.
Perhaps my main interest is fiddling with UEFI without having to use its code base. :-)
Quoted 143 lines
Quoted 3 lines
I'll make a few other comments on the patches. But overall
it seems to function and I think your implementation is nice.Thanks :)
Quoted 4 lines
I was able to get grub to boot but it just says 'Welcome to GRUB!' and then 'error: disk ',gpt4' not found'. I'm not sure what that means.
It might mean memory corruption. I'm not sure where from though :). It could also mean register clobbering.
Quoted 96 lines
U-Boot 2016.01-00860-geb4b602-dirty (Jan 31 2016 - 08:02:54 -0700)
CPU: x86_64, vendor Intel, device 30673h DRAM: 2 GiB efi_runtime_relocate: Relocating to offset=7ba5d000 MMC: ValleyView SDHCI: 0, ValleyView SDHCI: 1 SF: Detected W25Q64DW with page size 256 Bytes, erase size 4 KiB, total 8MiB
*** Warning - bad CRC, using default environmentVideo: 1280x1024x16
Model: Intel Minnowboard Max
SF: Detected W25Q64DW with page size 256 Bytes, erase size 4 KiB, total 8 MiB
SCSI: SATA link 0 timeout.
Target spinup took 0 ms.
AHCI 0001.0300 32 slots 2 ports 3 Gbps0x3impl SATA mode
flags: 64bit ncq stag pm led clo pio slum part sxs
scanning bus for devices...
Device 0: (1:0) Vendor: ATA Prod.: ADATA SP310 Rev: 5.2
Type: Hard Disk
Capacity: 30533.8 MB = 29.8 GB (62533296 x 512)
Found 1 device(s).
Net:
Warning:eth_rtl8169using MAC address from ROM
eth0:eth_rtl8169
Hit any key to stop autoboot: 0
reading grubia32.efi
85504 bytes read in 16 ms (5.1 MiB/s)
## Starting EFI application at0x00010000...
WARNING: No device tree loaded, expect boot to fail
Scanning disks on scsi...
Scanning disks on usb...
Scanning disks on mmc...
Card did not respond to voltage select!
MMC Device 2 not found
MMC Device 3 not found
Found 2 disks
do_bootefi_exec:134 Jumping to0x72857400
EFI: Entryefi_open_protocol(7bab8c18, 728609a0, 7b857ab8, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handlerloaded_image0
EFI: Exit 0
EFI: Entryefi_locate_protocol(72860990, 00000000, 7b857ac8)
EFI: Exit 0
EFI: Entryefi_cin_get_mode(7baa79cc, 7b857af8, 00000000, 00000000)
EFI: Exit 0
EFI: Entryefi_locate_protocol(72860990, 00000000, 7b857aa8)
EFI: Exit 0
EFI: Entryefi_cin_get_mode(7baa79cc, 7b857ad8, 00000000, 00000000)
EFI: Exit 0
EFI: Entryefi_cout_enable_cursor(7baa79d8, 1)
EFI: Exit 80000003
EFI: Entryefi_allocate_pages(1, 2, 0x6, 7b857a74)
EFI: Exit 0
EFI: Entryefi_get_memory_map(7b857b04, 7286c000, 7b857a64, 7b857b08, 7b857a68)
EFI: Exit 0
EFI: Entryefi_allocate_pages(2, 2, 0x1ca15, 7b857a74)
EFI: Exit 0
EFI: Entryefi_free_pages(7286c000, 0x6)
EFI: Exit 0
EFI: Entryefi_set_watchdog_timer(0, 0x0, 0, 00000000)
EFI: App called into unimplemented functionefi_set_watchdog_timer
EFI: Exit 80000003
EFI: Exit 80000003
EFI: App called into unimplemented functionefi_set_watchdog_timer
EFI: Exit 80000003
EFI: Entryefi_locate_handle(2, 72860980, 00000000, 7b857a88, 72856fe0)
EFI: Exit 0
EFI: Entryefi_open_protocol(7b863108, 728609b0, 7b857a78, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handleropen_dpefi_disk_open_dp
EFI: Exit 0
EFI: Entryefi_open_protocol(7b863108, 72860980, 7b857a98, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handleropen_blockefi_disk_open_block
EFI: Exit 0
EFI: Entryefi_open_protocol(7b8631d8, 728609b0, 7b857a78, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handleropen_dpefi_disk_open_dp
EFI: Exit 0
EFI: Entryefi_open_protocol(7b8631d8, 72860980, 7b857a98, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handleropen_blockefi_disk_open_block
EFI: Exit 0
EFI: Entryefi_cout_set_attribute(7baa79d8, 70)
EFI: Exit 80000003
Welcome to GRUB!Quoted 13 lines
EFI: Entry
efi_cout_set_attribute(7baa79d8, 7)
EFI: Exit 80000003
EFI: Entryefi_open_protocol(7bab8c18, 728609a0, 7b857ad8, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handlerloaded_image0
EFI: Exit 0
EFI: Entryefi_open_protocol(7bab8bd0, 728609b0, 7b857a78, 7bab8c18, 00000000, 0x2)efi_open_protocol: Found protocol handler bootefi0
EFI: Exit 0
error: disk `,gpt4' not found.
Entering rescue mode...
grub rescue>Does grub see the hard disks? What happens when you do ls (hd,<TAB>? Also, try the ls command but first do set debug=all to get grub internal debugging enabled too.
Quoted 7 lines
I suppose my grub could be wrong. If you can point me to one that I should use I could try again. I pushed your tree (rebased to mainline) plus my messing-around patch to u-boot-x86/efi-working.
It would be good to get this series applied soon.
Awesome.
Quoted 7 lines
Quoted 5 lines
I'm currently rewriting the memory map code, making it more robust, easy to understand and extensible for modules that may want to register runtime service mmio regions.
Once that works and I have all of Leif's comments sorted out, I'll post v3.
Sounds good.
Regards,
Simon
cover EFI payload / application support v2
In reply to Simon GlassHi Simon,
Quoted 80 lines · Simon Glass
Hi Alexander,
Quoted 62 lines · Alexander Graf
Quoted 11 lines · Simon Glass
Hi Alexander,
Quoted 7 lines · Alexander Graf
This is my 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.Well some might argue that grub2 and UEFI are their own nightmares :-)
They are, but they are the same nightmare everyone else is dreaming ;).
Quoted 37 lines
Quoted 6 lines
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.
The API interface is mostly for closed-source work I think.
Quoted 29 lines
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)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 exitQuoted 7 lines
Quoted 4 lines
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.I'd suggest creating a README with the above info. The cover letter will vanish pretty fast. Perhaps also update README.efi with this new option.
Quoted 5 lines
Good idea.
Quoted 1 line
Another thing you could list is
efi_set_watchdog_timer().What about it exactly? That it's not supported atm?
Yes.
Quoted 8 lines
Quoted 4 lines
Quoted 2 lines
Version 2 was successfully tested to boot grub2 and Linux from there on a HiKey (AArch64, dcache disabled) and on a BeagleBone Black.
Do you have a UEFI image for BBB that I can put on an SD card or otherwise boot?
Phew, I hand-crafted one to play around with. You can use the hip04d01 image from
Quoted 8 lines
Quoted 6 lines
Just extract the tar.xz, to get to the actual image .xz file.
That image obviously has an incorrect kernel for the BBB, but everything up to grub2 should work with it.
I'm not sure what file to use. I found a file with a .12.1 extension which I tried to dd to a uSD card but it did not boot.
Yeah, BBB is difficult. You need to have a working MLO (SPL) binary that chain-loads into a u-boot binary. The MLO can be either on a FAT partition or at hard coded offsets on the card. The same applies to the u-boot binary.
Some genius changed the way SPL works to only restrict you to either one path or the other - so if you started SPL from a raw offset, you need to have u-boot at a raw offset. If you started from FAT, u-boot needs to be in FAT.
So you'd have to take the hip04d01 image and manually try to dd MLO and u-boot binaries into it. I'd love to just give you the image I have, but I'm not sure bandwidth is great enough for that here :).
Alternatively you could use a working BBB image and copy grub from the hip04d01 image. Or use the rpm from here:
Quoted 10 lines
Do you have an ARM grub binary (in EFI format)? II could test with just that piece. tried building grub but it says cross-compiling isn't recommended, and I got an error about a missing
../include/grub/machine/kernel.h.Quoted 2 lines
My plan was to slowly move all openSUSE arm targets that use our own u-boot binaries to EFI once this patch set goes in ;).
BTW out of interest how come SUSE doesn't use the same distro boot thing as other distributions?
Imagine you had a team full of people who constantly try to fight off additional work because their support matrix is already insanely big (Yast in our case). And now try to convince those guys to maintain additional code just for this one random architecture that doesn't even pay bills for anyone (32bit arm) :).
We went through great pain with SLE12 to move all targets to grub2. On s390x for example, we even chain load a small Linux kernel that runs grub2 as user space payload on top just to ensure that configuration is *always* the same.
The nice thing that gives you is feature parity across architectures. For example, we can easily boot from btrfs snapshots with this approach. I'm not sure how you'd do that otherwise.
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
For now I've had a play with Minnowboard, which is x86. The main thing I noticed is that the API function implements should have EFIAPI on them also.
Quoted 7 lines
Quoted 5 lines
Yes :). I didn't expect anyone to actually care about running this on x86 which is the only architecture that has different calling conventions for EFI. I'm very pleasantly surprised that you are interested and since you already have a patch to add them, I guess you can as well just post that once the base support is in :).
OK. I suppose because EFIAPI is empty on ARM it doesn't matter. But strictly speaking the declaration should match the implementation.
Yeah, I just didn't want to implement something I wouldn't test :).
Quoted 3 lines
U-Boot mostly uses FIT which provides secure boot and it can boot both 32- and 64-bit kernels directly. But there is still the odd setup.bin thing Linux uses.
I haven't looked at FIT at all so far - and I have no idea what setup.bin is :).
Quoted 2 lines
Perhaps my main interest is fiddling with UEFI without having to use its code base. :-)
That's a great goal! To be quite honest, the more I work with the EFI API, the more I think it's actually a pretty reasonable API for what it's trying to do.
The problem is just that every time I try to look at TianoCore code, my eyes get sore.
Alex
cover EFI payload / application support v2
Hi Alex,
Quoted 125 lines · Alexander Graf
Hi Simon,
Quoted 102 lines · Simon Glass
Hi Alexander,
Quoted 73 lines · Alexander Graf
Quoted 7 lines · Simon Glass
Hi Alexander,
Quoted 3 lines · Alexander Graf
This is my Christmas present for my openSUSE friends :).
Quoted 8 lines
Quoted 6 lines
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.Well some might argue that grub2 and UEFI are their own nightmares :-)
They are, but they are the same nightmare everyone else is dreaming ;).
Quoted 45 lines
Quoted 6 lines
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.
The API interface is mostly for closed-source work I think.
Quoted 30 lines
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)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 exitQuoted 5 lines
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.Quoted 3 lines
I'd suggest creating a README with the above info. The cover letter will vanish pretty fast. Perhaps also update README.efi with this new option.
Quoted 6 lines
Good idea.
Quoted 1 line
Another thing you could list is
efi_set_watchdog_timer().What about it exactly? That it's not supported atm?
Yes.
Quoted 16 lines
Quoted 7 lines
Quoted 3 lines
Version 2 was successfully tested to boot grub2 and Linux from there on a
HiKey (AArch64, dcache disabled) and on a BeagleBone Black.Do you have a UEFI image for BBB that I can put on an SD card or otherwise
boot?Phew, I hand-crafted one to play around with. You can use the hip04d01 image
fromQuoted 8 lines
Quoted 5 lines
Just extract the tar.xz, to get to the actual image .xz file.
That image obviously has an incorrect kernel for the BBB, but everything up
to grub2 should work with it.I'm not sure what file to use. I found a file with a .12.1 extension which I tried to dd to a uSD card but it did not boot.
Yeah, BBB is difficult. You need to have a working MLO (SPL) binary that chain-loads into a u-boot binary. The MLO can be either on a FAT partition or at hard coded offsets on the card. The same applies to the u-boot binary.
Some genius changed the way SPL works to only restrict you to either one path or the other - so if you started SPL from a raw offset, you need to have u-boot at a raw offset. If you started from FAT, u-boot needs to be in FAT.
Quoted 8 lines
So you'd have to take the hip04d01 image and manually try to dd MLO and u-boot binaries into it. I'd love to just give you the image I have, but I'm not sure bandwidth is great enough for that here :).
Alternatively you could use a working BBB image and copy grub from the hip04d01 image. Or use the rpm from here:
OK will try.
Quoted 21 lines
Quoted 7 lines
Do you have an ARM grub binary (in EFI format)? II could test with just that piece. tried building grub but it says cross-compiling isn't recommended, and I got an error about a missing
../include/grub/machine/kernel.h.Quoted 6 lines
Quoted 3 lines
My plan was to slowly move all openSUSE arm targets that use our own u-boot
binaries to EFI once this patch set goes in ;).BTW out of interest how come SUSE doesn't use the same distro boot thing as other distributions?
Imagine you had a team full of people who constantly try to fight off additional work because their support matrix is already insanely big (Yast in our case). And now try to convince those guys to maintain additional code just for this one random architecture that doesn't even pay bills for anyone (32bit arm) :).
Quoted 7 lines
We went through great pain with SLE12 to move all targets to grub2. On s390x for example, we even chain load a small Linux kernel that runs grub2 as user space payload on top just to ensure that configuration is *always* the same.
The nice thing that gives you is feature parity across architectures. For example, we can easily boot from btrfs snapshots with this approach. I'm not sure how you'd do that otherwise.
Me neither, but then I turned that off a year or so ago after I had problems.
Quoted 14 lines
Quoted 14 lines
Quoted 13 lines
Quoted 6 lines
For now I've had a play with Minnowboard, which is x86. The main thing I noticed is that the API function implements should have EFIAPI on them also.
Yes :). I didn't expect anyone to actually care about running this on x86 which is the only architecture that has different calling conventions for EFI. I'm very pleasantly surprised that you are interested and since you already have a patch to add them, I guess you can as well just post that once the base support is in :).
Quoted 5 lines
Quoted 2 lines
OK. I suppose because EFIAPI is empty on ARM it doesn't matter. But strictly speaking the declaration should match the implementation.
Yeah, I just didn't want to implement something I wouldn't test :).
OK. You could add a regparm(0) or something like that to be sure it is working. Or just bring in that part of my patch. But I'm OK if you'd rather I send a follow-up.
Quoted 8 lines
Quoted 4 lines
U-Boot mostly uses FIT which provides secure boot and it can boot both 32- and 64-bit kernels directly. But there is still the odd setup.bin thing Linux uses.
I haven't looked at FIT at all so far - and I have no idea what setup.bin is :).
FIT is probably what everyone should be using. It always amazes me the lengths to which people go to do get a kernel and a selected device tree. All those hacks at the start of the kernel for decompression, early serial, device tree appending, etc. What a mess.
Anyway I commend FIT to your attention :-)
setup.bin is created on x86 only - I believe it is a few boot sectors so it contains executable code as well as settings. Really ugly.
Quoted 11 lines
Quoted 3 lines
Perhaps my main interest is fiddling with UEFI without having to use its code base. :-)
That's a great goal! To be quite honest, the more I work with the EFI API, the more I think it's actually a pretty reasonable API for what it's trying to do.
The problem is just that every time I try to look at TianoCore code, my eyes get sore.
Agreed. I'm quite happy with the EFI API too, and particularly since we can write code that uses it in our own style (e.g. linux kernel style). But I have quite limited experience with it so far (just the U-Boot feature to run as a payload and an application).
It must cost a lot more to write code in TianoCore than in U-Boot. I'm surprised that companies have that much money to waste :-)
Regards,
Simon
cover EFI payload / application support v2
Quoted 13 lines · Simon Glass
Hi Alex,
Quoted 9 lines · Alexander Graf
Quoted 9 lines
Quoted 9 lines
Quoted 3 lines
For now I've had a play with Minnowboard, which is x86. The main thing I noticed is that the API function implements should have EFIAPI on them also.
Yes :). I didn't expect anyone to actually care about running this on x86 which is the only architecture that has different calling conventions for EFI. I'm very pleasantly surprised that you are interested and since you already have a patch to add them, I guess you can as well just post that once the base support is in :).
Quoted 7 lines
Quoted 4 lines
Quoted 2 lines
OK. I suppose because EFIAPI is empty on ARM it doesn't matter. But strictly speaking the declaration should match the implementation.
Yeah, I just didn't want to implement something I wouldn't test :).
OK. You could add a regparm(0) or something like that to be sure it is working. Or just bring in that part of my patch. But I'm OK if you'd rather I send a follow-up.
My AArch64 compiler isn't really happy about that:
include/efi_loader.h:67:4: warning: 'regparm' attribute directive ignored [-Wattributes]
But I've fixed up everything I could find manually and just leave the remaining 1 or 2 occurrences to you :)
Alex
cover EFI payload / application support v2
Hi Alex,
Quoted 32 lines · Alexander Graf
Quoted 20 lines · Simon Glass
Hi Alex,
Quoted 15 lines · Alexander Graf
Quoted 13 lines
Quoted 10 lines
Quoted 3 lines
For now I've had a play with Minnowboard, which is x86. The main thing I noticed is that the API function implements should have EFIAPI on them also.
Yes :). I didn't expect anyone to actually care about running this on x86 which is the only architecture that has different calling conventions for EFI. I'm very pleasantly surprised that you are interested and since you already have a patch to add them, I guess you can as well just post that once the base support is in :).
OK. I suppose because EFIAPI is empty on ARM it doesn't matter. But strictly speaking the declaration should match the implementation.
Quoted 5 lines
Quoted 1 line
Yeah, I just didn't want to implement something I wouldn't test :).
OK. You could add a regparm(0) or something like that to be sure it is working. Or just bring in that part of my patch. But I'm OK if you'd rather I send a follow-up.
Quoted 5 lines
My AArch64 compiler isn't really happy about that:
include/efi_loader.h:67:4: warning: 'regparm' attribute directive ignored [-Wattributes]But I've fixed up everything I could find manually and just leave the remaining 1 or 2 occurrences to you :)
Sounds good, thanks.
Regards,
Simon