All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] [PATCH 00/14] EFI payload / application support v2
@ 2016-01-15  5:06 Alexander Graf
  2016-01-15  5:06 ` [U-Boot] [PATCH 01/14] disk/part.c: Expose list of available block drivers Alexander Graf
                   ` (14 more replies)
  0 siblings, 15 replies; 45+ messages in thread
From: Alexander Graf @ 2016-01-15  5:06 UTC (permalink / raw)
  To: u-boot

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

-- 
2.1.4

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 01/14] disk/part.c: Expose list of available block drivers
  2016-01-15  5:06 [U-Boot] [PATCH 00/14] EFI payload / application support v2 Alexander Graf
@ 2016-01-15  5:06 ` Alexander Graf
  2016-01-15  5:06 ` [U-Boot] [PATCH 02/14] include/efi_api.h: Add more detailed API definitions Alexander Graf
                   ` (13 subsequent siblings)
  14 siblings, 0 replies; 45+ messages in thread
From: Alexander Graf @ 2016-01-15  5:06 UTC (permalink / raw)
  To: u-boot

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.

Signed-off-by: Alexander Graf <agraf@suse.de>
---
 disk/part.c    | 7 +------
 include/part.h | 8 ++++++++
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/disk/part.c b/disk/part.c
index 909712e..7f937d9 100644
--- a/disk/part.c
+++ b/disk/part.c
@@ -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
diff --git a/include/part.h b/include/part.h
index 720a867..bc491d2 100644
--- a/include/part.h
+++ b/include/part.h
@@ -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; }
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 02/14] include/efi_api.h: Add more detailed API definitions
  2016-01-15  5:06 [U-Boot] [PATCH 00/14] EFI payload / application support v2 Alexander Graf
  2016-01-15  5:06 ` [U-Boot] [PATCH 01/14] disk/part.c: Expose list of available block drivers Alexander Graf
@ 2016-01-15  5:06 ` Alexander Graf
  2016-01-31 15:17   ` Simon Glass
  2016-01-15  5:06 ` [U-Boot] [PATCH 03/14] efi_loader: Add PE image loader Alexander Graf
                   ` (12 subsequent siblings)
  14 siblings, 1 reply; 45+ messages in thread
From: Alexander Graf @ 2016-01-15  5:06 UTC (permalink / raw)
  To: u-boot

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 <agraf@suse.de>
---
 include/efi_api.h | 197 ++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 160 insertions(+), 37 deletions(-)

diff --git a/include/efi_api.h b/include/efi_api.h
index 4fd17d6..917781f 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -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
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 03/14] efi_loader: Add PE image loader
  2016-01-15  5:06 [U-Boot] [PATCH 00/14] EFI payload / application support v2 Alexander Graf
  2016-01-15  5:06 ` [U-Boot] [PATCH 01/14] disk/part.c: Expose list of available block drivers Alexander Graf
  2016-01-15  5:06 ` [U-Boot] [PATCH 02/14] include/efi_api.h: Add more detailed API definitions Alexander Graf
@ 2016-01-15  5:06 ` Alexander Graf
  2016-01-31 15:18   ` Simon Glass
  2016-01-15  5:06 ` [U-Boot] [PATCH 04/14] efi_loader: Add boot time services Alexander Graf
                   ` (11 subsequent siblings)
  14 siblings, 1 reply; 45+ messages in thread
From: Alexander Graf @ 2016-01-15  5:06 UTC (permalink / raw)
  To: u-boot

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 <agraf@suse.de>

---

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

diff --git a/include/efi_loader.h b/include/efi_loader.h
new file mode 100644
index 0000000..bf77573
--- /dev/null
+++ b/include/efi_loader.h
@@ -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);
diff --git a/include/pe.h b/include/pe.h
new file mode 100644
index 0000000..6379ae1
--- /dev/null
+++ b/include/pe.h
@@ -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 */
diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
new file mode 100644
index 0000000..a7788bf
--- /dev/null
+++ b/lib/efi_loader/efi_image_loader.c
@@ -0,0 +1,178 @@
+/*
+ *  EFI image loader
+ *
+ *  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>
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define ROUND_UP(val, round) ((val + (round - 1)) & ~(round - 1))
+#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;
+	unsigned long rel_size;
+	int rel_idx = IMAGE_DIRECTORY_ENTRY_BASERELOC;
+	void *entry;
+	uint64_t image_size;
+	unsigned long virt_size = 0;
+	bool can_run_nt64 = true;
+	bool can_run_nt32 = true;
+
+#if defined(CONFIG_ARM64)
+	can_run_nt32 = false;
+#elif defined(CONFIG_ARM)
+	can_run_nt64 = false;
+#endif
+
+	dos = efi;
+	if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
+		printf("%s: Invalid DOS Signature\n", __func__);
+		return NULL;
+	}
+
+	nt = (void *) ((char *)efi + dos->e_lfanew);
+	if (nt->Signature != IMAGE_NT_SIGNATURE) {
+		printf("%s: Invalid NT Signature\n", __func__);
+		return NULL;
+	}
+
+	/* Calculate upper virtual address boundary */
+	num_sections = nt->FileHeader.NumberOfSections;
+	sections = (void *)&nt->OptionalHeader +
+			    nt->FileHeader.SizeOfOptionalHeader;
+
+	for (i = num_sections - 1; i >= 0; i--) {
+		IMAGE_SECTION_HEADER *sec = &sections[i];
+		virt_size = max_t(unsigned long, virt_size,
+				  sec->VirtualAddress + sec->Misc.VirtualSize);
+	}
+
+	/* Read 32/64bit specific header bits */
+	if (can_run_nt64 &&
+	    (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)) {
+		IMAGE_NT_HEADERS64 *nt64 = (void *)nt;
+		IMAGE_OPTIONAL_HEADER64 *opt = &nt64->OptionalHeader;
+		image_size = opt->SizeOfImage;
+		efi_reloc = efi_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 = &sections[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",
+				       offset, type);
+			}
+			relocs++;
+		}
+		rel = (const IMAGE_BASE_RELOCATION *)relocs;
+	}
+
+	/* Populate the loaded image interface bits */
+	loaded_image_info->image_base = efi;
+	loaded_image_info->image_size = image_size;
+
+	return entry;
+}
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 04/14] efi_loader: Add boot time services
  2016-01-15  5:06 [U-Boot] [PATCH 00/14] EFI payload / application support v2 Alexander Graf
                   ` (2 preceding siblings ...)
  2016-01-15  5:06 ` [U-Boot] [PATCH 03/14] efi_loader: Add PE image loader Alexander Graf
@ 2016-01-15  5:06 ` Alexander Graf
  2016-01-20  0:16   ` Leif Lindholm
  2016-01-31 15:19   ` Simon Glass
  2016-01-15  5:06 ` [U-Boot] [PATCH 05/14] efi_loader: Add console interface Alexander Graf
                   ` (10 subsequent siblings)
  14 siblings, 2 replies; 45+ messages in thread
From: Alexander Graf @ 2016-01-15  5:06 UTC (permalink / raw)
  To: u-boot

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 <agraf@suse.de>

---

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

diff --git a/include/efi_loader.h b/include/efi_loader.h
index bf77573..391459e 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -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
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
new file mode 100644
index 0000000..5756c9c
--- /dev/null
+++ b/lib/efi_loader/efi_boottime.c
@@ -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:
+		/* 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;
+	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);
+}
+
+/* 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);
+}
+
+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@a time */
+		return EFI_EXIT(EFI_INVALID_PARAMETER);
+	}
+
+	switch (type) {
+	case EFI_TIMER_STOP:
+		efi_event.trigger_next = -1ULL;
+		break;
+	case EFI_TIMER_PERIODIC:
+	case EFI_TIMER_RELATIVE:
+		efi_event.trigger_next = timer_get_us() + (trigger32 / 10);
+		break;
+	default:
+		return EFI_EXIT(EFI_UNSUPPORTED);
+	}
+	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__));
+}
+
+static efi_status_t efi_signal_event(void *event)
+{
+	EFI_ENTRY("%p", event);
+	return EFI_EXIT(efi_unsupported(__func__));
+}
+
+static efi_status_t efi_close_event(void *event)
+{
+	EFI_ENTRY("%p", event);
+	return EFI_EXIT(efi_unsupported(__func__));
+}
+
+static efi_status_t efi_check_event(void *event)
+{
+	EFI_ENTRY("%p", event);
+	return EFI_EXIT(efi_unsupported(__func__));
+}
+
+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__));
+}
+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__));
+}
+
+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__));
+}
+
+static efi_status_t efi_register_protocol_notify(efi_guid_t *protocol,
+					 void *event, void **registration)
+{
+	EFI_ENTRY("%p, %p, %p", protocol, event, registration);
+	return EFI_EXIT(efi_unsupported(__func__));
+}
+
+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;
+		}
+	}
+
+	*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__));
+}
+
+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)))
+		return EFI_EXIT(EFI_UNSUPPORTED);
+
+	efi_conf_table[0].table = table;
+	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);
+	if (!info->reserved) {
+		free(info);
+		free(obj);
+		return EFI_EXIT(EFI_UNSUPPORTED);
+	}
+
+	*image_handle = info;
+	list_add_tail(&obj->link, &efi_obj_list);
+
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t 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,
+		     unsigned long exit_data_size,
+		     uint16_t *exit_data)
+{
+	EFI_ENTRY("%p, %ld, %ld, %p", image_handle, exit_status,
+		  exit_data_size, exit_data);
+	return EFI_EXIT(efi_unsupported(__func__));
+}
+
+static struct efi_object *efi_search_obj(void *handle)
+{
+	struct list_head *lhandle;
+
+	list_for_each(lhandle, &efi_obj_list) {
+		struct efi_object *efiobj;
+		efiobj = list_entry(lhandle, struct efi_object, link);
+		if (efiobj->handle == handle)
+			return efiobj;
+	}
+
+	return NULL;
+}
+
+static efi_status_t efi_unload_image(void *image_handle)
+{
+	struct efi_object *efiobj;
+
+	EFI_ENTRY("%p", image_handle);
+	efiobj = efi_search_obj(image_handle);
+	if (efiobj)
+		list_del(&efiobj->link);
+
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static void efi_exit_caches(void)
+{
+#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.
+	 */
+	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();
+
+	/* 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();
+	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__));
+}
+
+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__));
+}
+
+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__));
+}
+
+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__));
+}
+
+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__));
+}
+
+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__));
+}
+
+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__));
+}
+
+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);
+		}
+	}
+
+	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__));
+}
+
+static efi_status_t efi_uninstall_multiple_protocol_interfaces(void *handle, ...)
+{
+	EFI_ENTRY("%p", handle);
+	return EFI_EXIT(efi_unsupported(__func__));
+}
+
+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:
+	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);
+}
+
+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,
+};
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 05/14] efi_loader: Add console interface
  2016-01-15  5:06 [U-Boot] [PATCH 00/14] EFI payload / application support v2 Alexander Graf
                   ` (3 preceding siblings ...)
  2016-01-15  5:06 ` [U-Boot] [PATCH 04/14] efi_loader: Add boot time services Alexander Graf
@ 2016-01-15  5:06 ` Alexander Graf
  2016-01-31 15:19   ` Simon Glass
  2016-01-15  5:06 ` [U-Boot] [PATCH 06/14] efi_loader: Add runtime services Alexander Graf
                   ` (9 subsequent siblings)
  14 siblings, 1 reply; 45+ messages in thread
From: Alexander Graf @ 2016-01-15  5:06 UTC (permalink / raw)
  To: u-boot

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

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

Signed-off-by: Alexander Graf <agraf@suse.de>

---

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

diff --git a/include/efi_loader.h b/include/efi_loader.h
index 391459e..d314002 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -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;
 
diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c
new file mode 100644
index 0000000..c1ab779
--- /dev/null
+++ b/lib/efi_loader/efi_console.c
@@ -0,0 +1,357 @@
+/*
+ *  EFI application console interface
+ *
+ *  Copyright (c) 2016 Alexander Graf
+ *
+ *  SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+
+/* If we can't determine the console size, default to 80x24 */
+static int console_columns = 80;
+static int console_rows = 24;
+static bool console_size_queried;
+
+const efi_guid_t efi_guid_console_control = CONSOLE_CONTROL_GUID;
+
+#define cESC '\x1b'
+#define ESC "\x1b"
+
+static efi_status_t efi_cin_get_mode(struct efi_console_control_protocol *this,
+				     int *mode, char *uga_exists,
+				     char *std_in_locked)
+{
+	EFI_ENTRY("%p, %p, %p, %p", this, mode, uga_exists, std_in_locked);
+
+	if (mode)
+		*mode = EFI_CONSOLE_MODE_TEXT;
+	if (uga_exists)
+		*uga_exists = 0;
+	if (std_in_locked)
+		*std_in_locked = 0;
+
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t efi_cin_set_mode(struct efi_console_control_protocol *this,
+				     int mode)
+{
+	EFI_ENTRY("%p, %d", this, mode);
+	return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+static efi_status_t efi_cin_lock_std_in(struct efi_console_control_protocol *this,
+					uint16_t *password)
+{
+	EFI_ENTRY("%p, %p", this, password);
+	return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+const struct efi_console_control_protocol efi_console_control = {
+	.get_mode = efi_cin_get_mode,
+	.set_mode = efi_cin_set_mode,
+	.lock_std_in = efi_cin_lock_std_in,
+};
+
+static struct simple_text_output_mode efi_con_mode = {
+	.max_mode = 0,
+	.mode = 0,
+	.attribute = 0,
+	.cursor_column = 0,
+	.cursor_row = 0,
+	.cursor_visible = 1,
+};
+
+static int term_read_reply(int *n, int maxnum, char end_char)
+{
+	char c;
+	int i = 0;
+
+	c = getc();
+	if (c != cESC)
+		return -1;
+	c = getc();
+	if (c != '[')
+		return -1;
+
+	n[0] = 0;
+	while (1) {
+		c = getc();
+		if (c == ';') {
+			i++;
+			if (i >= maxnum)
+				return -1;
+			n[i] = 0;
+			continue;
+		} else if (c == end_char) {
+			break;
+		} else if (c > '9' || c < '0') {
+			return -1;
+		}
+
+		/* Read one more decimal position */
+		n[i] *= 10;
+		n[i] += c - '0';
+	}
+
+	return 0;
+}
+
+static efi_status_t efi_cout_reset(struct efi_simple_text_output_protocol *this,
+				   char extended_verification)
+{
+	EFI_ENTRY("%p, %d", this, extended_verification);
+	return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+static void print_unicode_in_utf8(u16 c)
+{
+	char utf8[4] = { 0 };
+	char *b = utf8;
+
+	if (c < 0x80) {
+		*(b++) = c;
+	} else if (c < 0x800) {
+		*(b++) = 192 + c / 64;
+		*(b++) = 128 + c % 64;
+	} else {
+		*(b++) = 224 + c / 4096;
+		*(b++) = 128 + c / 64 % 64;
+		*(b++) = 128 + c % 64;
+	}
+
+	puts(utf8);
+}
+
+static efi_status_t efi_cout_output_string(
+			struct efi_simple_text_output_protocol *this,
+			const unsigned short *string)
+{
+	u16 ch;
+
+	EFI_ENTRY("%p, %p", this, string);
+	for (;(ch = *string); string++) {
+		print_unicode_in_utf8(ch);
+		efi_con_mode.cursor_column++;
+		if (ch == '\n') {
+			efi_con_mode.cursor_column = 1;
+			efi_con_mode.cursor_row++;
+		} else if (efi_con_mode.cursor_column > console_columns) {
+			efi_con_mode.cursor_column = 1;
+			efi_con_mode.cursor_row++;
+		}
+		if (efi_con_mode.cursor_row > console_rows) {
+			efi_con_mode.cursor_row = console_rows;
+		}
+	}
+
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t efi_cout_test_string(
+			struct efi_simple_text_output_protocol *this,
+			const unsigned short *string)
+{
+	EFI_ENTRY("%p, %p", this, string);
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t efi_cout_query_mode(
+			struct efi_simple_text_output_protocol *this,
+			unsigned long mode_number, unsigned long *columns,
+			unsigned long *rows)
+{
+	EFI_ENTRY("%p, %ld, %p, %p", this, mode_number, columns, rows);
+
+	if (!console_size_queried) {
+		/* Ask the terminal about its size */
+		int n[3];
+		u64 timeout;
+
+		console_size_queried = true;
+
+		/* Empty input buffer */
+		while (tstc())
+			getc();
+
+		printf(ESC"[18t");
+
+		/* Check if we have a terminal that understands */
+		timeout = timer_get_us() + 1000000;
+		while (!tstc())
+			if (timer_get_us() > timeout)
+				goto out;
+
+		/* Read {depth,rows,cols} */
+		if (term_read_reply(n, 3, 't')) {
+			goto out;
+		}
+
+		console_columns = n[2];
+		console_rows = n[1];
+	}
+
+out:
+	if (columns)
+		*columns = console_columns;
+	if (rows)
+		*rows = console_rows;
+
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t efi_cout_set_mode(
+			struct efi_simple_text_output_protocol *this,
+			unsigned long mode_number)
+{
+	EFI_ENTRY("%p, %ld", this, mode_number);
+
+	/* We only support text output for now */
+	if (mode_number == EFI_CONSOLE_MODE_TEXT)
+		return EFI_EXIT(EFI_SUCCESS);
+
+	return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+static efi_status_t efi_cout_set_attribute(
+			struct efi_simple_text_output_protocol *this,
+			unsigned long attribute)
+{
+	EFI_ENTRY("%p, %lx", this, attribute);
+
+	/* Just ignore attributes (colors) for now */
+	return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+static efi_status_t efi_cout_clear_screen(
+			struct efi_simple_text_output_protocol *this)
+{
+	EFI_ENTRY("%p", this);
+
+	printf(ESC"[2J");
+
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t efi_cout_set_cursor_position(
+			struct efi_simple_text_output_protocol *this,
+			unsigned long column, unsigned long row)
+{
+	EFI_ENTRY("%p, %ld, %ld", this, column, row);
+
+	printf(ESC"[%d;%df", (int)row, (int)column);
+	efi_con_mode.cursor_column = column;
+	efi_con_mode.cursor_row = row;
+
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t efi_cout_enable_cursor(
+			struct efi_simple_text_output_protocol *this,
+			bool enable)
+{
+	EFI_ENTRY("%p, %d", this, enable);
+
+	printf(ESC"[?25%c", enable ? 'h' : 'l');
+
+	return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+const struct efi_simple_text_output_protocol efi_con_out = {
+	.reset = efi_cout_reset,
+	.output_string = efi_cout_output_string,
+	.test_string = efi_cout_test_string,
+	.query_mode = efi_cout_query_mode,
+	.set_mode = efi_cout_set_mode,
+	.set_attribute = efi_cout_set_attribute,
+	.clear_screen = efi_cout_clear_screen,
+	.set_cursor_position = efi_cout_set_cursor_position,
+	.enable_cursor = efi_cout_enable_cursor,
+	.mode = (void*)&efi_con_mode,
+};
+
+static efi_status_t efi_cin_reset(struct efi_simple_input_interface *this,
+				  bool extended_verification)
+{
+	EFI_ENTRY("%p, %d", this, extended_verification);
+	return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+static efi_status_t efi_cin_read_key_stroke(
+			struct efi_simple_input_interface *this,
+			struct efi_input_key *key)
+{
+	struct efi_input_key pressed_key = {
+		.scan_code = 0,
+		.unicode_char = 0,
+	};
+	char ch;
+
+	EFI_ENTRY("%p, %p", this, key);
+
+	/* We don't do interrupts, so check for timers cooperatively */
+	efi_timer_check();
+
+	if (!tstc()) {
+		/* No key pressed */
+		return EFI_EXIT(EFI_NOT_READY);
+	}
+
+	ch = getc();
+	if (ch == cESC) {
+		/* Escape Sequence */
+		ch = getc();
+		switch (ch) {
+		case cESC: /* ESC */
+			pressed_key.scan_code = 23;
+			break;
+		case 'O': /* F1 - F4 */
+			pressed_key.scan_code = getc() - 'P' + 11;
+			break;
+		case 'a'...'z':
+			ch = ch - 'a';
+			break;
+		case '[':
+			ch = getc();
+			switch (ch) {
+			case 'A'...'D': /* up, down right, left */
+				pressed_key.scan_code = ch - 'A' + 1;
+				break;
+			case 'F': /* End */
+				pressed_key.scan_code = 6;
+				break;
+			case 'H': /* Home */
+				pressed_key.scan_code = 5;
+				break;
+			case '1': /* F5 - F8 */
+				pressed_key.scan_code = getc() - '0' + 11;
+				getc();
+				break;
+			case '2': /* F9 - F12 */
+				pressed_key.scan_code = getc() - '0' + 19;
+				getc();
+				break;
+			case '3': /* DEL */
+				pressed_key.scan_code = 8;
+				getc();
+				break;
+			}
+			break;
+		}
+	} else if (ch == 0x7f) {
+		/* Backspace */
+		ch = 0x08;
+	}
+	pressed_key.unicode_char = ch;
+	*key = pressed_key;
+
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+const struct efi_simple_input_interface efi_con_in = {
+	.reset = efi_cin_reset,
+	.read_key_stroke = efi_cin_read_key_stroke,
+	.wait_for_key = NULL,
+};
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 06/14] efi_loader: Add runtime services
  2016-01-15  5:06 [U-Boot] [PATCH 00/14] EFI payload / application support v2 Alexander Graf
                   ` (4 preceding siblings ...)
  2016-01-15  5:06 ` [U-Boot] [PATCH 05/14] efi_loader: Add console interface Alexander Graf
@ 2016-01-15  5:06 ` Alexander Graf
  2016-01-21 17:20   ` Leif Lindholm
  2016-01-31 15:20   ` Simon Glass
  2016-01-15  5:06 ` [U-Boot] [PATCH 07/14] efi_loader: Add disk interfaces Alexander Graf
                   ` (8 subsequent siblings)
  14 siblings, 2 replies; 45+ messages in thread
From: Alexander Graf @ 2016-01-15  5:06 UTC (permalink / raw)
  To: u-boot

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 <agraf@suse.de>

---

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

diff --git a/arch/arm/config.mk b/arch/arm/config.mk
index 0550225..ecb956d 100644
--- a/arch/arm/config.mk
+++ b/arch/arm/config.mk
@@ -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
diff --git a/arch/arm/cpu/armv8/u-boot.lds b/arch/arm/cpu/armv8/u-boot.lds
index 4c12222..fd15ad5 100644
--- a/arch/arm/cpu/armv8/u-boot.lds
+++ b/arch/arm/cpu/armv8/u-boot.lds
@@ -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)
diff --git a/arch/arm/cpu/u-boot.lds b/arch/arm/cpu/u-boot.lds
index d48a905..596a17d 100644
--- a/arch/arm/cpu/u-boot.lds
+++ b/arch/arm/cpu/u-boot.lds
@@ -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)
diff --git a/arch/arm/lib/sections.c b/arch/arm/lib/sections.c
index a1205c3..6a94522 100644
--- a/arch/arm/lib/sections.c
+++ b/arch/arm/lib/sections.c
@@ -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")));
diff --git a/board/ti/am335x/u-boot.lds b/board/ti/am335x/u-boot.lds
index 78f294a..a56cc82 100644
--- a/board/ti/am335x/u-boot.lds
+++ b/board/ti/am335x/u-boot.lds
@@ -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)
diff --git a/common/board_r.c b/common/board_r.c
index 75ee43e..420e2c8 100644
--- a/common/board_r.c
+++ b/common/board_r.c
@@ -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;
 }
diff --git a/include/efi_loader.h b/include/efi_loader.h
index d314002..0f821ff 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -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
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index 5756c9c..45217ef 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -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 */
diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
new file mode 100644
index 0000000..b7aa1e9
--- /dev/null
+++ b/lib/efi_loader/efi_runtime.c
@@ -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;
+#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);
+#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@%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,
+};
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 07/14] efi_loader: Add disk interfaces
  2016-01-15  5:06 [U-Boot] [PATCH 00/14] EFI payload / application support v2 Alexander Graf
                   ` (5 preceding siblings ...)
  2016-01-15  5:06 ` [U-Boot] [PATCH 06/14] efi_loader: Add runtime services Alexander Graf
@ 2016-01-15  5:06 ` Alexander Graf
  2016-01-31 15:23   ` Simon Glass
  2016-01-15  5:06 ` [U-Boot] [PATCH 08/14] efi_loader: Add "bootefi" command Alexander Graf
                   ` (7 subsequent siblings)
  14 siblings, 1 reply; 45+ messages in thread
From: Alexander Graf @ 2016-01-15  5:06 UTC (permalink / raw)
  To: u-boot

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 <agraf@suse.de>
Reviewed-by: Simon Glass <sjg@chromium.org>

---

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

diff --git a/include/efi_loader.h b/include/efi_loader.h
index 0f821ff..9d6c322 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -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,
diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
new file mode 100644
index 0000000..c589604
--- /dev/null
+++ b/lib/efi_loader/efi_disk.c
@@ -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
+	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);
+	else
+		n = desc->block_write(desc->dev, lba, blocks, buffer);
+
+	/* 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,
+};
+
+/*
+ * 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 gets called from do_bootefi_exec().
+ */
+int efi_disk_register(void)
+{
+	const struct block_drvr *cur_drvr;
+	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++) {
+			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;
+
+			diskobj = calloc(1, objlen);
+
+			/* Fill in object data */
+
+			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 */
+
+			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);
+			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;
+}
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 08/14] efi_loader: Add "bootefi" command
  2016-01-15  5:06 [U-Boot] [PATCH 00/14] EFI payload / application support v2 Alexander Graf
                   ` (6 preceding siblings ...)
  2016-01-15  5:06 ` [U-Boot] [PATCH 07/14] efi_loader: Add disk interfaces Alexander Graf
@ 2016-01-15  5:06 ` Alexander Graf
  2016-01-31 15:23   ` Simon Glass
  2016-01-15  5:06 ` [U-Boot] [PATCH 09/14] efi_loader: Implement memory allocation and map Alexander Graf
                   ` (6 subsequent siblings)
  14 siblings, 1 reply; 45+ messages in thread
From: Alexander Graf @ 2016-01-15  5:06 UTC (permalink / raw)
  To: u-boot

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

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

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

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

Signed-off-by: Alexander Graf <agraf@suse.de>

---

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

diff --git a/common/Makefile b/common/Makefile
index 2a1d9f8..a7a728a 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -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
diff --git a/common/cmd_bootefi.c b/common/cmd_bootefi.c
new file mode 100644
index 0000000..fde1942
--- /dev/null
+++ b/common/cmd_bootefi.c
@@ -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,
+	.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 */
+			.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;
+
+	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;
+
+	/* 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@0x%08lx ...\n", addr);
+	r = do_bootefi_exec((void *)addr);
+	printf("## Application terminated, r = %d\n", r);
+
+	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"
+);
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 09/14] efi_loader: Implement memory allocation and map
  2016-01-15  5:06 [U-Boot] [PATCH 00/14] EFI payload / application support v2 Alexander Graf
                   ` (7 preceding siblings ...)
  2016-01-15  5:06 ` [U-Boot] [PATCH 08/14] efi_loader: Add "bootefi" command Alexander Graf
@ 2016-01-15  5:06 ` Alexander Graf
  2016-01-31 15:23   ` Simon Glass
  2016-01-15  5:06 ` [U-Boot] [PATCH 10/14] arm64: Allow exceptions to return Alexander Graf
                   ` (5 subsequent siblings)
  14 siblings, 1 reply; 45+ messages in thread
From: Alexander Graf @ 2016-01-15  5:06 UTC (permalink / raw)
  To: u-boot

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 <agraf@suse.de>
---
 lib/efi_loader/efi_boottime.c     | 99 ++++++++++++++++++++++++++++++++++++++-
 lib/efi_loader/efi_image_loader.c | 28 ++++++++++-
 2 files changed, 123 insertions(+), 4 deletions(-)

diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index 45217ef..ff3f969 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -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)
diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
index a7788bf..67c4b06 100644
--- a/lib/efi_loader/efi_image_loader.c
+++ b/lib/efi_loader/efi_image_loader.c
@@ -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;
 }
 
 /*
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 10/14] arm64: Allow exceptions to return
  2016-01-15  5:06 [U-Boot] [PATCH 00/14] EFI payload / application support v2 Alexander Graf
                   ` (8 preceding siblings ...)
  2016-01-15  5:06 ` [U-Boot] [PATCH 09/14] efi_loader: Implement memory allocation and map Alexander Graf
@ 2016-01-15  5:06 ` Alexander Graf
  2016-01-15  5:06 ` [U-Boot] [PATCH 11/14] arm64: Allow EFI payload code to take exceptions Alexander Graf
                   ` (4 subsequent siblings)
  14 siblings, 0 replies; 45+ messages in thread
From: Alexander Graf @ 2016-01-15  5:06 UTC (permalink / raw)
  To: u-boot

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

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

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

Signed-off-by: Alexander Graf <agraf@suse.de>
---
 arch/arm/cpu/armv8/exceptions.S | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/arch/arm/cpu/armv8/exceptions.S b/arch/arm/cpu/armv8/exceptions.S
index baf9401..4f4f526 100644
--- a/arch/arm/cpu/armv8/exceptions.S
+++ b/arch/arm/cpu/armv8/exceptions.S
@@ -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
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 11/14] arm64: Allow EFI payload code to take exceptions
  2016-01-15  5:06 [U-Boot] [PATCH 00/14] EFI payload / application support v2 Alexander Graf
                   ` (9 preceding siblings ...)
  2016-01-15  5:06 ` [U-Boot] [PATCH 10/14] arm64: Allow exceptions to return Alexander Graf
@ 2016-01-15  5:06 ` Alexander Graf
  2016-01-15  5:06 ` [U-Boot] [PATCH 12/14] efi_loader: Add DCACHE_OFF support for arm64 Alexander Graf
                   ` (3 subsequent siblings)
  14 siblings, 0 replies; 45+ messages in thread
From: Alexander Graf @ 2016-01-15  5:06 UTC (permalink / raw)
  To: u-boot

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

  - Callback function
  - Exception

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

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

Signed-off-by: Alexander Graf <agraf@suse.de>
---
 arch/arm/lib/interrupts_64.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/arch/arm/lib/interrupts_64.c b/arch/arm/lib/interrupts_64.c
index b476722..7c9cfce 100644
--- a/arch/arm/lib/interrupts_64.c
+++ b/arch/arm/lib/interrupts_64.c
@@ -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");
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 12/14] efi_loader: Add DCACHE_OFF support for arm64
  2016-01-15  5:06 [U-Boot] [PATCH 00/14] EFI payload / application support v2 Alexander Graf
                   ` (10 preceding siblings ...)
  2016-01-15  5:06 ` [U-Boot] [PATCH 11/14] arm64: Allow EFI payload code to take exceptions Alexander Graf
@ 2016-01-15  5:06 ` Alexander Graf
  2016-01-15  5:06 ` [U-Boot] [PATCH 13/14] efi_loader: hook up in build environment Alexander Graf
                   ` (2 subsequent siblings)
  14 siblings, 0 replies; 45+ messages in thread
From: Alexander Graf @ 2016-01-15  5:06 UTC (permalink / raw)
  To: u-boot

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

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

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

Signed-off-by: Alexander Graf <agraf@suse.de>
---
 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

diff --git a/arch/arm/cpu/armv8/exceptions.S b/arch/arm/cpu/armv8/exceptions.S
index 4f4f526..97101c3 100644
--- a/arch/arm/cpu/armv8/exceptions.S
+++ b/arch/arm/cpu/armv8/exceptions.S
@@ -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
diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile
index f3db7b5..ce5ed99 100644
--- a/arch/arm/lib/Makefile
+++ b/arch/arm/lib/Makefile
@@ -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
diff --git a/arch/arm/lib/interrupts_64.c b/arch/arm/lib/interrupts_64.c
index 7c9cfce..4aa36de 100644
--- a/arch/arm/lib/interrupts_64.c
+++ b/arch/arm/lib/interrupts_64.c
@@ -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");
diff --git a/arch/arm/lib/unaligned_64.c b/arch/arm/lib/unaligned_64.c
new file mode 100644
index 0000000..b307b7e
--- /dev/null
+++ b/arch/arm/lib/unaligned_64.c
@@ -0,0 +1,284 @@
+/*
+ * (C) Copyright 2016
+ * Alexander Graf <agraf@suse.de>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <linux/compiler.h>
+#include <efi_loader.h>
+
+#define ESR_EC_MASK	0xFC000000
+#define ESR_EC_SHIFT	26
+#define ESR_IL_MASK	0x02000000
+#define ESR_IL_SHIFT	25
+#define ESR_ISS_MASK	0x01FFFFFF
+#define ESR_ISS_SHIFT	0
+
+#define EC_DATA_SL	0x25
+
+#define ISS_ISV_MASK	0x01000000
+#define ISS_ISV_SHIFT	24
+#define ISS_SAS_MASK	0x00C00000
+#define ISS_SAS_SHIFT	22
+#define ISS_SSE_MASK	0x00200000
+#define ISS_SSE_SHIFT	21
+#define ISS_SRT_MASK	0x000F0000
+#define ISS_SRT_SHIFT	16
+#define ISS_SF_MASK	0x00008000
+#define ISS_SF_SHIFT	15
+#define ISS_AR_MASK	0x00004000
+#define ISS_AR_SHIFT	14
+#define ISS_EA_MASK	0x00000200
+#define ISS_EA_SHIFT	9
+#define ISS_CM_MASK	0x00000100
+#define ISS_CM_SHIFT	8
+#define ISS_S1PTW_MASK	0x00000080
+#define ISS_S1PTW_SHIFT	7
+#define ISS_WNR_MASK	0x00000040
+#define ISS_WNR_SHIFT	6
+#define WNR_READ	0
+#define WNR_WRITE	1
+#define ISS_DFSC_MASK	0x0000003F
+#define ISS_DFSC_SHIFT	0
+
+#define ISV_VALID	1
+#define DFSC_ALIGN	0x21
+
+ulong read_far(void);
+int do_unaligned_data(struct pt_regs *pt_regs, unsigned int esr);
+
+static inline int32_t sextract32(uint32_t value, int start, int length)
+{
+	return ((int32_t)(value << (32 - length - start))) >> (32 - length);
+}
+
+static inline uint32_t extract32(uint32_t value, int start, int length)
+{
+	return (value >> start) & (~0U >> (32 - length));
+}
+
+static int insn_iss_ldst(uint32_t insn, int iss, int *wb_reg, ulong *wb_val)
+{
+	int rt = extract32(insn, 0, 5);
+	int rn = extract32(insn, 5, 5);
+	int idx = extract32(insn, 10, 2);
+	int imm9 = sextract32(insn, 12, 9);
+	int opc = extract32(insn, 22, 2);
+	int size = extract32(insn, 30, 2);
+	bool is_signed = false;
+	bool is_store = false;
+	bool is_extended = false;
+	bool is_vector = extract32(insn, 26, 1);
+
+	switch (extract32(insn, 25, 4)) {
+	case 0x4:
+	case 0x6:
+	case 0xc:
+	case 0xe:	  /* Loads and stores */
+		break;
+        default:
+		return iss;
+	}
+
+	switch (extract32(insn, 24, 6)) {
+	case 0x38: case 0x39:
+	case 0x3c: case 0x3d: /* Load/store register (all forms) */
+		break;
+	default:
+		return iss;
+	}
+
+	switch (extract32(insn, 24, 2)) {
+	case 0:
+		if (extract32(insn, 21, 1) != 1 &&
+		    extract32(insn, 10, 2) != 2) {
+			/* Write back */
+			if (idx & 1) {
+				ulong far = read_far();
+				*wb_reg = rn;
+				*wb_val = (idx & 2) ? far : far + imm9;
+				break;
+			}
+		}
+		break;
+	case 1:
+		break;
+	default:
+		return iss;
+	}
+
+	if (is_vector) {
+		return iss;
+	}
+
+	is_store = (opc == 0);
+	is_signed = extract32(opc, 1, 1);
+	is_extended = (size < 3) && extract32(opc, 0, 1);
+
+	iss |= ISS_ISV_MASK;
+	iss |= size << ISS_SAS_SHIFT;
+	iss |= (is_extended && is_signed) ? ISS_SSE_MASK : 0;
+	iss |= rt << ISS_SRT_SHIFT;
+	iss |= ISS_SF_MASK;
+	iss |= is_store ? ISS_WNR_MASK : 0;
+
+	return iss;
+}
+
+static void do_unaligned_access(struct pt_regs *pt_regs, int wnr, int rt,
+				ulong addr, int sas, int sse)
+{
+	void *rr = (void*)&pt_regs->regs[rt];
+	void *r = rr;
+	int len = 1 << sas;
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+	/* On BE registers get filled from the back */
+	r = (char*)r + (8 - len);
+#endif
+
+	if (wnr == WNR_READ) {
+		/* Read with zero pad */
+		pt_regs->regs[rt] = 0;
+		memcpy(r, (void*)addr, len);
+
+		/* Sign extend */
+		if (sse) {
+			switch (sas) {
+			case 0: *(long*)rr = *(char*)r; break;
+			case 1: *(long*)rr = *(short*)r; break;
+			case 2: *(long*)rr = *(int*)r; break;
+			}
+		}
+	} else if (wnr == WNR_WRITE) {
+		memcpy((void*)addr, r, len);
+	}
+}
+
+static int do_ldst_pair(u32 insn, struct pt_regs *pt_regs)
+{
+	int rt = extract32(insn, 0, 5);
+	int rn = extract32(insn, 5, 5);
+	int rt2 = extract32(insn, 10, 5);
+	int index = extract32(insn, 23, 2);
+	bool is_vector = extract32(insn, 26, 1);
+	bool is_load = extract32(insn, 22, 1);
+	int opc = extract32(insn, 30, 2);
+	ulong far = read_far();
+	ulong old_far = far;
+	int wnr = is_load ? WNR_READ : WNR_WRITE;
+
+	bool is_signed = extract32(opc, 0, 1);
+	bool postindex = false;
+	bool wback = false;
+
+	int size = 2 + extract32(opc, 1, 1);
+
+	switch (extract32(insn, 25, 4)) {
+	case 0x4:
+	case 0x6:
+	case 0xc:
+	case 0xe:	  /* Loads and stores */
+		break;
+        default:
+		return -1;
+	}
+
+	switch (extract32(insn, 24, 6)) {
+	case 0x28: case 0x29:
+	case 0x2c: case 0x2d: /* Load/store pair (all forms) */
+		break;
+	default:
+		return -1;
+	}
+
+	if (is_vector)
+		return -1;
+
+	switch (index) {
+	case 1: /* post-index */
+		postindex = true;
+		wback = true;
+		break;
+	case 0: /* signed offset with "non-temporal" hint. */
+		postindex = false;
+		break;
+	case 2: /* signed offset, rn not updated */
+		postindex = false;
+		break;
+	case 3: /* pre-index */
+		postindex = false;
+		wback = true;
+		break;
+	}
+
+	do_unaligned_access(pt_regs, wnr, rt, far, size, is_signed);
+	far += 1 << size;
+	do_unaligned_access(pt_regs, wnr, rt2, far, size, is_signed);
+	far += 1 << size;
+
+	if (wback)
+		pt_regs->regs[rn] = postindex ? far : old_far;
+
+	return 0;
+}
+
+int do_unaligned_data(struct pt_regs *pt_regs, unsigned int esr)
+{
+	int insn = *(u32*)(void*)pt_regs->elr;
+	int ec = (esr & ESR_EC_MASK) >> ESR_EC_SHIFT;
+	int iss = (esr & ESR_ISS_MASK) >> ESR_ISS_SHIFT;
+	int isv = (iss & ISS_ISV_MASK) >> ISS_ISV_SHIFT;
+	int dfsc = (iss & ISS_DFSC_MASK) >> ISS_DFSC_SHIFT;
+	int sas, sse, srt, sf, ar, cm, s1ptw, wnr;
+	int wb_reg = -1;
+	ulong wb_val;
+
+	/* Check whether we have an alignment fault */
+	if ((ec != EC_DATA_SL) || (dfsc != DFSC_ALIGN))
+		return -1;
+
+	/* Fix up instruction decoding */
+	if (!isv) {
+		iss = insn_iss_ldst(insn, iss, &wb_reg, &wb_val);
+	}
+
+	isv = (iss & ISS_ISV_MASK) >> ISS_ISV_SHIFT;
+	sas = (iss & ISS_SAS_MASK) >> ISS_SAS_SHIFT;
+	sse = (iss & ISS_SSE_MASK) >> ISS_SSE_SHIFT;
+	srt = (iss & ISS_SRT_MASK) >> ISS_SRT_SHIFT;
+	sf = (iss & ISS_SF_MASK) >> ISS_SF_SHIFT;
+	ar = (iss & ISS_AR_MASK) >> ISS_AR_SHIFT;
+	cm = (iss & ISS_CM_MASK) >> ISS_CM_SHIFT;
+	s1ptw = (iss & ISS_S1PTW_MASK) >> ISS_S1PTW_SHIFT;
+	wnr = (iss & ISS_WNR_MASK) >> ISS_WNR_SHIFT;
+
+	/* Check whether we have an easily fixable alignment fault */
+	if (isv && sf && !ar && !cm && !s1ptw) {
+		ulong far = read_far();
+
+		do_unaligned_access(pt_regs, wnr, srt, far, sas, sse);
+
+		/* Jump across the offending instruction */
+		pt_regs->elr += 4;
+
+		/* Do writebacks if required */
+		if (wb_reg != -1)
+			pt_regs->regs[wb_reg] = wb_val;
+
+		/* And return from the exception */
+		return 0;
+	}
+
+	if (!do_ldst_pair(insn, pt_regs)) {
+		pt_regs->elr += 4;
+		return 0;
+	}
+
+	/* Couldn't fix it, panic */
+	printf("Alignment handler couldn't decode insn %08x\n",
+	       *(u32*)(void*)pt_regs->elr);
+	return -1;
+}
diff --git a/common/cmd_bootefi.c b/common/cmd_bootefi.c
index fde1942..65d3c69 100644
--- a/common/cmd_bootefi.c
+++ b/common/cmd_bootefi.c
@@ -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)
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 13/14] efi_loader: hook up in build environment
  2016-01-15  5:06 [U-Boot] [PATCH 00/14] EFI payload / application support v2 Alexander Graf
                   ` (11 preceding siblings ...)
  2016-01-15  5:06 ` [U-Boot] [PATCH 12/14] efi_loader: Add DCACHE_OFF support for arm64 Alexander Graf
@ 2016-01-15  5:06 ` Alexander Graf
  2016-01-31 15:24   ` Simon Glass
  2016-01-15  5:06 ` [U-Boot] [PATCH 14/14] efi_loader: Add distro boot script for removable media Alexander Graf
  2016-01-31 15:17 ` [U-Boot] [PATCH 00/14] EFI payload / application support v2 Simon Glass
  14 siblings, 1 reply; 45+ messages in thread
From: Alexander Graf @ 2016-01-15  5:06 UTC (permalink / raw)
  To: u-boot

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 <agraf@suse.de>

---

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

diff --git a/lib/Kconfig b/lib/Kconfig
index 9d580e4..3efe075 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -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
diff --git a/lib/Makefile b/lib/Makefile
index dd36f25..3a9f304 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -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/
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
new file mode 100644
index 0000000..6da1c7f
--- /dev/null
+++ b/lib/efi_loader/Kconfig
@@ -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.
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
new file mode 100644
index 0000000..76596ff
--- /dev/null
+++ b/lib/efi_loader/Makefile
@@ -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
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 14/14] efi_loader: Add distro boot script for removable media
  2016-01-15  5:06 [U-Boot] [PATCH 00/14] EFI payload / application support v2 Alexander Graf
                   ` (12 preceding siblings ...)
  2016-01-15  5:06 ` [U-Boot] [PATCH 13/14] efi_loader: hook up in build environment Alexander Graf
@ 2016-01-15  5:06 ` Alexander Graf
  2016-01-31 15:24   ` Simon Glass
  2016-01-31 15:17 ` [U-Boot] [PATCH 00/14] EFI payload / application support v2 Simon Glass
  14 siblings, 1 reply; 45+ messages in thread
From: Alexander Graf @ 2016-01-15  5:06 UTC (permalink / raw)
  To: u-boot

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.

Signed-off-by: Alexander Graf <agraf@suse.de>
---
 include/config_distro_bootcmd.h | 47 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 46 insertions(+), 1 deletion(-)

diff --git a/include/config_distro_bootcmd.h b/include/config_distro_bootcmd.h
index 66264ce..bbd61f6 100644
--- a/include/config_distro_bootcmd.h
+++ b/include/config_distro_bootcmd.h
@@ -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; "     \
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 04/14] efi_loader: Add boot time services
  2016-01-15  5:06 ` [U-Boot] [PATCH 04/14] efi_loader: Add boot time services Alexander Graf
@ 2016-01-20  0:16   ` Leif Lindholm
  2016-02-02  1:52     ` Alexander Graf
  2016-01-31 15:19   ` Simon Glass
  1 sibling, 1 reply; 45+ messages in thread
From: Leif Lindholm @ 2016-01-20  0:16 UTC (permalink / raw)
  To: u-boot

On Fri, Jan 15, 2016 at 06:06:10AM +0100, Alexander Graf wrote:
> 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 <agraf@suse.de>
> 
> ---
> 
> 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
> 
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index bf77573..391459e 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -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
> diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> new file mode 100644
> index 0000000..5756c9c
> --- /dev/null
> +++ b/lib/efi_loader/efi_boottime.c
> @@ -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:
> +		/* 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.

> +	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);
> +}
> +
> +/* 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 :)

> +}
> +
> +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);
> +	}
> +
> +	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.

> +	}
> +	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.

> +}
> +
> +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.

> +}
> +
> +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.

> +}
> +
> +static efi_status_t efi_check_event(void *event)
> +{
> +	EFI_ENTRY("%p", event);
> +	return EFI_EXIT(efi_unsupported(__func__));

EFI_NOT_READY?

> +}
> +
> +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?

> +}
> +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?

> +}
> +
> +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?

> +}
> +
> +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".

> +}
> +
> +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;
> +		}
> +	}
> +
> +	*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?

> +}
> +
> +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?

> +		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).

> +
> +	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.

> +	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);
> +	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?

> +
> +	*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,
> +		     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".

> +}
> +
> +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);
> +
> +	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.

> +	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();
> +
> +	/* 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?

> +	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__));
> +}
> +
> +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?

> +}
> +
> +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?

> +}
> +
> +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?

> +}
> +
> +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?

> +}
> +
> +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)

> +}
> +
> +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?

> +}
> +
> +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);
> +		}
> +	}
> +
> +	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?

> +}
> +
> +static efi_status_t efi_uninstall_multiple_protocol_interfaces(void *handle, ...)
> +{
> +	EFI_ENTRY("%p", handle);
> +	return EFI_EXIT(efi_unsupported(__func__));

EFI_INVALID_PARAMETER?

> +}
> +
> +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:
> +	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.)

> +
> +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,
> +};
> -- 
> 2.1.4
> 

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 06/14] efi_loader: Add runtime services
  2016-01-15  5:06 ` [U-Boot] [PATCH 06/14] efi_loader: Add runtime services Alexander Graf
@ 2016-01-21 17:20   ` Leif Lindholm
  2016-01-31 15:20   ` Simon Glass
  1 sibling, 0 replies; 45+ messages in thread
From: Leif Lindholm @ 2016-01-21 17:20 UTC (permalink / raw)
  To: u-boot

On Fri, Jan 15, 2016 at 06:06:12AM +0100, Alexander Graf wrote:
> 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 <agraf@suse.de>

Just a couple of return value issues:

> ---
> 
> 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
> 

...

> diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
> new file mode 100644
> index 0000000..b7aa1e9
> --- /dev/null
> +++ b/lib/efi_loader/efi_runtime.c
> @@ -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;
> +#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?

> +#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?

> +
> +	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 */
> +}

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 00/14] EFI payload / application support v2
  2016-01-15  5:06 [U-Boot] [PATCH 00/14] EFI payload / application support v2 Alexander Graf
                   ` (13 preceding siblings ...)
  2016-01-15  5:06 ` [U-Boot] [PATCH 14/14] efi_loader: Add distro boot script for removable media Alexander Graf
@ 2016-01-31 15:17 ` Simon Glass
  2016-01-31 21:43   ` Alexander Graf
  14 siblings, 1 reply; 45+ messages in thread
From: Simon Glass @ 2016-01-31 15:17 UTC (permalink / raw)
  To: u-boot

Hi Alexander,

On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
> 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 :-)

>
> 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.

>
> 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.

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().

>
> 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_dp
efi_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_dp
efi_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.

>
> 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

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 02/14] include/efi_api.h: Add more detailed API definitions
  2016-01-15  5:06 ` [U-Boot] [PATCH 02/14] include/efi_api.h: Add more detailed API definitions Alexander Graf
@ 2016-01-31 15:17   ` Simon Glass
  2016-02-01 22:46     ` Alexander Graf
  0 siblings, 1 reply; 45+ messages in thread
From: Simon Glass @ 2016-01-31 15:17 UTC (permalink / raw)
  To: u-boot

On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
> 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 <agraf@suse.de>
> ---
>  include/efi_api.h | 197 ++++++++++++++++++++++++++++++++++++++++++++----------
>  1 file changed, 160 insertions(+), 37 deletions(-)

Reviewed-by: Simon Glass <sjg@chromium.org>

But does the notify_function parameter need EFIAPI also?

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 03/14] efi_loader: Add PE image loader
  2016-01-15  5:06 ` [U-Boot] [PATCH 03/14] efi_loader: Add PE image loader Alexander Graf
@ 2016-01-31 15:18   ` Simon Glass
  2016-02-01 22:58     ` Alexander Graf
  0 siblings, 1 reply; 45+ messages in thread
From: Simon Glass @ 2016-01-31 15:18 UTC (permalink / raw)
  To: u-boot

Hi Alexander,

On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
> 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 <agraf@suse.de>
>
> ---
>
> 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
>
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> new file mode 100644
> index 0000000..bf77573
> --- /dev/null
> +++ b/include/efi_loader.h
> @@ -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);
> diff --git a/include/pe.h b/include/pe.h
> new file mode 100644
> index 0000000..6379ae1
> --- /dev/null
> +++ b/include/pe.h
> @@ -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 */
> diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
> new file mode 100644
> index 0000000..a7788bf
> --- /dev/null
> +++ b/lib/efi_loader/efi_image_loader.c
> @@ -0,0 +1,178 @@
> +/*
> + *  EFI image loader
> + *
> + *  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.

> +#include <asm/global_data.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +#define ROUND_UP(val, round) ((val + (round - 1)) & ~(round - 1))

Can you use round_up()?

> +#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;
> +       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?

> +
> +#if defined(CONFIG_ARM64)
> +       can_run_nt32 = false;
> +#elif defined(CONFIG_ARM)
> +       can_run_nt64 = false;
> +#endif
> +
> +       dos = efi;
> +       if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
> +               printf("%s: Invalid DOS Signature\n", __func__);
> +               return NULL;
> +       }
> +
> +       nt = (void *) ((char *)efi + dos->e_lfanew);
> +       if (nt->Signature != IMAGE_NT_SIGNATURE) {
> +               printf("%s: Invalid NT Signature\n", __func__);
> +               return NULL;
> +       }
> +
> +       /* Calculate upper virtual address boundary */
> +       num_sections = nt->FileHeader.NumberOfSections;
> +       sections = (void *)&nt->OptionalHeader +
> +                           nt->FileHeader.SizeOfOptionalHeader;
> +
> +       for (i = num_sections - 1; i >= 0; i--) {
> +               IMAGE_SECTION_HEADER *sec = &sections[i];
> +               virt_size = max_t(unsigned long, virt_size,
> +                                 sec->VirtualAddress + sec->Misc.VirtualSize);
> +       }
> +
> +       /* Read 32/64bit specific header bits */
> +       if (can_run_nt64 &&
> +           (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)) {
> +               IMAGE_NT_HEADERS64 *nt64 = (void *)nt;
> +               IMAGE_OPTIONAL_HEADER64 *opt = &nt64->OptionalHeader;
> +               image_size = opt->SizeOfImage;
> +               efi_reloc = efi_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 = &sections[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",
> +                                      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.

> +
> +       /* Populate the loaded image interface bits */
> +       loaded_image_info->image_base = efi;
> +       loaded_image_info->image_size = image_size;
> +
> +       return entry;
> +}
> --
> 2.1.4
>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot

Regards,
Simon

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 04/14] efi_loader: Add boot time services
  2016-01-15  5:06 ` [U-Boot] [PATCH 04/14] efi_loader: Add boot time services Alexander Graf
  2016-01-20  0:16   ` Leif Lindholm
@ 2016-01-31 15:19   ` Simon Glass
  2016-02-01 23:45     ` Alexander Graf
  1 sibling, 1 reply; 45+ messages in thread
From: Simon Glass @ 2016-01-31 15:19 UTC (permalink / raw)
  To: u-boot

Hi Alexander,

On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
> 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 <agraf@suse.de>
>
> ---
>
> 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

Reviewed-by: Simon Glass <sjg@chromium.org>

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.

>
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index bf77573..391459e 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -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);

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.

> +};
> +
> +struct efi_object {
> +       struct list_head link;
> +       struct efi_handler protocols[4];
> +       void *handle;
> +};

Can you add comments on the above structs?

> +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) { }

And the above functions

[snip]

Regards,
Simon

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 05/14] efi_loader: Add console interface
  2016-01-15  5:06 ` [U-Boot] [PATCH 05/14] efi_loader: Add console interface Alexander Graf
@ 2016-01-31 15:19   ` Simon Glass
  0 siblings, 0 replies; 45+ messages in thread
From: Simon Glass @ 2016-01-31 15:19 UTC (permalink / raw)
  To: u-boot

On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
> One of the basic EFI interfaces is the console interface. Using it an EFI
> application can interface with the user. This patch implements an EFI console
> interface using getc() and putc().
>
> Today, we only implement text based consoles. We also convert the EFI Unicode
> characters to UTF-8 on the fly, hoping that everyone managed to jump on the
> train by now.
>
> Signed-off-by: Alexander Graf <agraf@suse.de>
>
> ---
>
> 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

Reviewed-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 06/14] efi_loader: Add runtime services
  2016-01-15  5:06 ` [U-Boot] [PATCH 06/14] efi_loader: Add runtime services Alexander Graf
  2016-01-21 17:20   ` Leif Lindholm
@ 2016-01-31 15:20   ` Simon Glass
  2016-02-01 23:57     ` Alexander Graf
  1 sibling, 1 reply; 45+ messages in thread
From: Simon Glass @ 2016-01-31 15:20 UTC (permalink / raw)
  To: u-boot

Hi Alexander,

On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
> 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 <agraf@suse.de>
>
> ---
>
> 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

Reviewed-by: Simon Glass <sjg@chromium.org>

A few nits.

>
> diff --git a/arch/arm/config.mk b/arch/arm/config.mk
> index 0550225..ecb956d 100644
> --- a/arch/arm/config.mk
> +++ b/arch/arm/config.mk
> @@ -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
> diff --git a/arch/arm/cpu/armv8/u-boot.lds b/arch/arm/cpu/armv8/u-boot.lds
> index 4c12222..fd15ad5 100644
> --- a/arch/arm/cpu/armv8/u-boot.lds
> +++ b/arch/arm/cpu/armv8/u-boot.lds
> @@ -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)
> diff --git a/arch/arm/cpu/u-boot.lds b/arch/arm/cpu/u-boot.lds
> index d48a905..596a17d 100644
> --- a/arch/arm/cpu/u-boot.lds
> +++ b/arch/arm/cpu/u-boot.lds
> @@ -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)
> diff --git a/arch/arm/lib/sections.c b/arch/arm/lib/sections.c
> index a1205c3..6a94522 100644
> --- a/arch/arm/lib/sections.c
> +++ b/arch/arm/lib/sections.c
> @@ -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")));
> diff --git a/board/ti/am335x/u-boot.lds b/board/ti/am335x/u-boot.lds
> index 78f294a..a56cc82 100644
> --- a/board/ti/am335x/u-boot.lds
> +++ b/board/ti/am335x/u-boot.lds
> @@ -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)
> diff --git a/common/board_r.c b/common/board_r.c
> index 75ee43e..420e2c8 100644
> --- a/common/board_r.c
> +++ b/common/board_r.c
> @@ -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;
>  }
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index d314002..0f821ff 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -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);

Function comment please.

>
>  #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
> diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> index 5756c9c..45217ef 100644
> --- a/lib/efi_loader/efi_boottime.c
> +++ b/lib/efi_loader/efi_boottime.c
> @@ -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 */
> diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
> new file mode 100644
> index 0000000..b7aa1e9
> --- /dev/null
> +++ b/lib/efi_loader/efi_runtime.c
> @@ -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.

> +#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;
> +};

Can you use elf.h?

> +
> +/*
> + * 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;
> +#ifdef CONFIG_DM_RTC

Since this is a new service I don't think you need to support the old RTC API.

> +       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,
> +};
> --
> 2.1.4

Regards,
Simon

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 07/14] efi_loader: Add disk interfaces
  2016-01-15  5:06 ` [U-Boot] [PATCH 07/14] efi_loader: Add disk interfaces Alexander Graf
@ 2016-01-31 15:23   ` Simon Glass
  2016-02-02  0:32     ` Alexander Graf
  0 siblings, 1 reply; 45+ messages in thread
From: Simon Glass @ 2016-01-31 15:23 UTC (permalink / raw)
  To: u-boot

On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
> 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 <agraf@suse.de>
> Reviewed-by: Simon Glass <sjg@chromium.org>
>
> ---
>
> 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
>
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index 0f821ff..9d6c322 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -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,
> diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
> new file mode 100644
> index 0000000..c589604
> --- /dev/null
> +++ b/lib/efi_loader/efi_disk.c
> @@ -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
> +       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/?

> +       else
> +               n = desc->block_write(desc->dev, lba, blocks, buffer);

Same here.

> +
> +       /* 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,
> +};
> +
> +/*
> + * 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.

> + *
> + * 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)

> +       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.

> +                       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.

> +
> +                       diskobj = calloc(1, objlen);
> +
> +                       /* Fill in object data */

drop extra blank line, and below

> +
> +                       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 */
> +
> +                       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.

> +                       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;
> +}
> --
> 2.1.4
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 08/14] efi_loader: Add "bootefi" command
  2016-01-15  5:06 ` [U-Boot] [PATCH 08/14] efi_loader: Add "bootefi" command Alexander Graf
@ 2016-01-31 15:23   ` Simon Glass
  0 siblings, 0 replies; 45+ messages in thread
From: Simon Glass @ 2016-01-31 15:23 UTC (permalink / raw)
  To: u-boot

Hi Alexander,

On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
> In order to execute an EFI application, we need to bridge the gap between
> U-Boot's notion of executing images and EFI's notion of doing the same.
>
> The best path forward IMHO here is to stick completely to the way U-Boot
> deals with payloads. You manually load them using whatever method to RAM
> and then have a simple boot command to execute them. So in our case, you
> would do
>
>   # load mmc 0:1 $loadaddr grub.efi
>   # bootefi $loadaddr
>
> which then gets you into a grub shell. Fdt information known to U-boot
> via the fdt addr command is also passed to the EFI payload.
>
> Signed-off-by: Alexander Graf <agraf@suse.de>
>
> ---
>
> 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

Reviewed-by: Simon Glass <sjg@chromium.org>

>
> diff --git a/common/Makefile b/common/Makefile
> index 2a1d9f8..a7a728a 100644
> --- a/common/Makefile
> +++ b/common/Makefile
> @@ -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.

>  obj-$(CONFIG_CMD_BOOTMENU) += cmd_bootmenu.o
>  obj-$(CONFIG_CMD_BOOTLDR) += cmd_bootldr.o
>  obj-$(CONFIG_CMD_BOOTSTAGE) += cmd_bootstage.o
> diff --git a/common/cmd_bootefi.c b/common/cmd_bootefi.c
> new file mode 100644
> index 0000000..fde1942
> --- /dev/null
> +++ b/common/cmd_bootefi.c
> @@ -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,
> +       .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

> +                       .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;
> +
> +       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?

> +
> +       /* 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);
> +
> +       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?

> +);
> --
> 2.1.4
>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot


Regards,
Simon

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 09/14] efi_loader: Implement memory allocation and map
  2016-01-15  5:06 ` [U-Boot] [PATCH 09/14] efi_loader: Implement memory allocation and map Alexander Graf
@ 2016-01-31 15:23   ` Simon Glass
  2016-02-02  0:59     ` Alexander Graf
  0 siblings, 1 reply; 45+ messages in thread
From: Simon Glass @ 2016-01-31 15:23 UTC (permalink / raw)
  To: u-boot

Hi Alexander,

On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
> 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 <agraf@suse.de>
> ---
>  lib/efi_loader/efi_boottime.c     | 99 ++++++++++++++++++++++++++++++++++++++-
>  lib/efi_loader/efi_image_loader.c | 28 ++++++++++-
>  2 files changed, 123 insertions(+), 4 deletions(-)
>
> diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> index 45217ef..ff3f969 100644
> --- a/lib/efi_loader/efi_boottime.c
> +++ b/lib/efi_loader/efi_boottime.c
> @@ -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   |

What does 'second half' mean?

> + * |____________________________|   &__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

If not defined, what happens?

> +       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)
> diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
> index a7788bf..67c4b06 100644
> --- a/lib/efi_loader/efi_image_loader.c
> +++ b/lib/efi_loader/efi_image_loader.c
> @@ -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;

I think it would be better to reserve this in board_f() with a
reserve...() function. Perhaps store the address in global_data.

> +       }
> +
> +       len = ROUND_UP(len, 4096);
> +       /* Out of memory */
> +       if ((loader_pool + len) >= (gd->relocaddr - TOTAL_MALLOC_LEN))

debug() here?

> +               return NULL;
> +
> +       r = (void *)loader_pool;
> +       loader_pool += len;
> +
> +       return r;
>  }
>
>  /*
> --
> 2.1.4

Regards,
Simon

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 13/14] efi_loader: hook up in build environment
  2016-01-15  5:06 ` [U-Boot] [PATCH 13/14] efi_loader: hook up in build environment Alexander Graf
@ 2016-01-31 15:24   ` Simon Glass
  0 siblings, 0 replies; 45+ messages in thread
From: Simon Glass @ 2016-01-31 15:24 UTC (permalink / raw)
  To: u-boot

On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
> 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 <agraf@suse.de>
>
> ---
>
> 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

Reviewed-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 14/14] efi_loader: Add distro boot script for removable media
  2016-01-15  5:06 ` [U-Boot] [PATCH 14/14] efi_loader: Add distro boot script for removable media Alexander Graf
@ 2016-01-31 15:24   ` Simon Glass
  2016-02-02  1:05     ` Alexander Graf
  0 siblings, 1 reply; 45+ messages in thread
From: Simon Glass @ 2016-01-31 15:24 UTC (permalink / raw)
  To: u-boot

Hi Alexander,

On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
> 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.
>
> Signed-off-by: Alexander Graf <agraf@suse.de>
> ---
>  include/config_distro_bootcmd.h | 47 ++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 46 insertions(+), 1 deletion(-)

Reviewed-by: Simon Glass <sjg@chromium.org>

Will this work OK with FITs?

Also, can you add an entry to MAINTAINERS?

Regards,
Simon

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 00/14] EFI payload / application support v2
  2016-01-31 15:17 ` [U-Boot] [PATCH 00/14] EFI payload / application support v2 Simon Glass
@ 2016-01-31 21:43   ` Alexander Graf
  2016-02-01  2:52     ` Simon Glass
  0 siblings, 1 reply; 45+ messages in thread
From: Alexander Graf @ 2016-01-31 21:43 UTC (permalink / raw)
  To: u-boot



On 01/31/2016 04:17 PM, Simon Glass wrote:
> Hi Alexander,
>
> On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
>> 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 ;).

>
>> 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.
>
>> 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.
> 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.

>
> Another thing you could list is efi_set_watchdog_timer().

What about it exactly? That it's not supported atm?

>
>> 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

http://download.opensuse.org/repositories/devel:/ARM:/Factory:/Contrib:/HIP04D01/images/openSUSE-Tumbleweed-ARM-JeOS-hip04d01.armv7l.install.tar.xz

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 ;).

> 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 :).

>   I'll make a few other comments on the patches. But overall
> it seems to function and I think your implementation is nice.

Thanks :)

>
> 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.

>
>
> 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_dp
> efi_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_dp
> efi_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>

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.

>
>
> 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

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 00/14] EFI payload / application support v2
  2016-01-31 21:43   ` Alexander Graf
@ 2016-02-01  2:52     ` Simon Glass
  2016-02-01  3:25       ` Simon Glass
  2016-02-01 21:38       ` Alexander Graf
  0 siblings, 2 replies; 45+ messages in thread
From: Simon Glass @ 2016-02-01  2:52 UTC (permalink / raw)
  To: u-boot

Hi Alexander,

On 31 January 2016 at 14:43, Alexander Graf <agraf@suse.de> wrote:
>
>
> On 01/31/2016 04:17 PM, Simon Glass wrote:
>>
>> Hi Alexander,
>>
>> On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
>>>
>>> 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 ;).
>
>
>>
>>> 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.
>>
>>> 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.
>>
>> 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.
>
>>
>> Another thing you could list is efi_set_watchdog_timer().
>
>
> What about it exactly? That it's not supported atm?

Yes.

>
>>
>>> 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
>
> http://download.opensuse.org/repositories/devel:/ARM:/Factory:/Contrib:/HIP04D01/images/openSUSE-Tumbleweed-ARM-JeOS-hip04d01.armv7l.install.tar.xz
>
> 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.

>
> 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?

>
>> 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. :-)

>
>>   I'll make a few other comments on the patches. But overall
>> it seems to function and I think your implementation is nice.
>
>
> Thanks :)
>
>>
>> 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.
>
>
>>
>>
>> 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_dp
>> efi_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_dp
>> efi_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>
>
>
> 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.
>
>>
>>
>> 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.

Sounds good.

>
>
> Alex
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 00/14] EFI payload / application support v2
  2016-02-01  2:52     ` Simon Glass
@ 2016-02-01  3:25       ` Simon Glass
  2016-02-01 21:38       ` Alexander Graf
  1 sibling, 0 replies; 45+ messages in thread
From: Simon Glass @ 2016-02-01  3:25 UTC (permalink / raw)
  To: u-boot

Hi Alexander,

On 31 January 2016 at 19:52, Simon Glass <sjg@chromium.org> wrote:
> Hi Alexander,
>
> On 31 January 2016 at 14:43, Alexander Graf <agraf@suse.de> wrote:
>>
>>
>> On 01/31/2016 04:17 PM, Simon Glass wrote:
>>>
>>> Hi Alexander,
>>>
>>> On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
>>>>
>>>> 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 ;).
>>
>>
>>>
>>>> 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.
>>>
>>>> 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.
>>>
>>> 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.
>>
>>>
>>> Another thing you could list is efi_set_watchdog_timer().
>>
>>
>> What about it exactly? That it's not supported atm?
>
> Yes.
>
>>
>>>
>>>> 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
>>
>> http://download.opensuse.org/repositories/devel:/ARM:/Factory:/Contrib:/HIP04D01/images/openSUSE-Tumbleweed-ARM-JeOS-hip04d01.armv7l.install.tar.xz
>>
>> 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.

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.

>
>>
>> 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?
>
>>
>>> 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. :-)
>
>>
>>>   I'll make a few other comments on the patches. But overall
>>> it seems to function and I think your implementation is nice.
>>
>>
>> Thanks :)
>>
>>>
>>> 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.
>>
>>
>>>
>>>
>>> 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_dp
>>> efi_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_dp
>>> efi_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>
>>
>>
>> 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.
>>
>>>
>>>
>>> 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.
>
> Sounds good.

Regards,
Simon

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 00/14] EFI payload / application support v2
  2016-02-01  2:52     ` Simon Glass
  2016-02-01  3:25       ` Simon Glass
@ 2016-02-01 21:38       ` Alexander Graf
  2016-02-02  0:02         ` Simon Glass
  1 sibling, 1 reply; 45+ messages in thread
From: Alexander Graf @ 2016-02-01 21:38 UTC (permalink / raw)
  To: u-boot

Hi Simon,

On 02/01/2016 03:52 AM, Simon Glass wrote:
> Hi Alexander,
>
> On 31 January 2016 at 14:43, Alexander Graf <agraf@suse.de> wrote:
>>
>> On 01/31/2016 04:17 PM, Simon Glass wrote:
>>> Hi Alexander,
>>>
>>> On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
>>>> 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 ;).
>>
>>
>>>> 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.
>>>
>>>> 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.
>>> 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.
>>
>>> Another thing you could list is efi_set_watchdog_timer().
>>
>> What about it exactly? That it's not supported atm?
> Yes.
>
>>>> 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
>>
>> http://download.opensuse.org/repositories/devel:/ARM:/Factory:/Contrib:/HIP04D01/images/openSUSE-Tumbleweed-ARM-JeOS-hip04d01.armv7l.install.tar.xz
>>
>> 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:

http://download.opensuse.org/ports/armv7hl/tumbleweed/repo/oss/suse/armv7hl/grub2-arm-efi-2.02~beta2-54.1.armv7hl.rpm

>
> 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.
>
>> 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.


>
>>> 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.

Yeah, I just didn't want to implement something I wouldn't test :).

> 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 :).

> 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

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 02/14] include/efi_api.h: Add more detailed API definitions
  2016-01-31 15:17   ` Simon Glass
@ 2016-02-01 22:46     ` Alexander Graf
  0 siblings, 0 replies; 45+ messages in thread
From: Alexander Graf @ 2016-02-01 22:46 UTC (permalink / raw)
  To: u-boot



On 01/31/2016 04:17 PM, Simon Glass wrote:
> On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
>> 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 <agraf@suse.de>
>> ---
>>   include/efi_api.h | 197 ++++++++++++++++++++++++++++++++++++++++++++----------
>>   1 file changed, 160 insertions(+), 37 deletions(-)
> Reviewed-by: Simon Glass <sjg@chromium.org>
>
> But does the notify_function parameter 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

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 03/14] efi_loader: Add PE image loader
  2016-01-31 15:18   ` Simon Glass
@ 2016-02-01 22:58     ` Alexander Graf
  0 siblings, 0 replies; 45+ messages in thread
From: Alexander Graf @ 2016-02-01 22:58 UTC (permalink / raw)
  To: u-boot



On 01/31/2016 04:18 PM, Simon Glass wrote:
> Hi Alexander,
>
> On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
>> 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 <agraf@suse.de>
>>
>> ---
>>
>> 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
>>
>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>> new file mode 100644
>> index 0000000..bf77573
>> --- /dev/null
>> +++ b/include/efi_loader.h
>> @@ -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);
>> diff --git a/include/pe.h b/include/pe.h
>> new file mode 100644
>> index 0000000..6379ae1
>> --- /dev/null
>> +++ b/include/pe.h
>> @@ -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 */
>> diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
>> new file mode 100644
>> index 0000000..a7788bf
>> --- /dev/null
>> +++ b/lib/efi_loader/efi_image_loader.c
>> @@ -0,0 +1,178 @@
>> +/*
>> + *  EFI image loader
>> + *
>> + *  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.
>
>> +#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 :).

>
>> +#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;
>> +       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.

>
>> +
>> +#if defined(CONFIG_ARM64)
>> +       can_run_nt32 = false;
>> +#elif defined(CONFIG_ARM)
>> +       can_run_nt64 = false;
>> +#endif
>> +
>> +       dos = efi;
>> +       if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
>> +               printf("%s: Invalid DOS Signature\n", __func__);
>> +               return NULL;
>> +       }
>> +
>> +       nt = (void *) ((char *)efi + dos->e_lfanew);
>> +       if (nt->Signature != IMAGE_NT_SIGNATURE) {
>> +               printf("%s: Invalid NT Signature\n", __func__);
>> +               return NULL;
>> +       }
>> +
>> +       /* Calculate upper virtual address boundary */
>> +       num_sections = nt->FileHeader.NumberOfSections;
>> +       sections = (void *)&nt->OptionalHeader +
>> +                           nt->FileHeader.SizeOfOptionalHeader;
>> +
>> +       for (i = num_sections - 1; i >= 0; i--) {
>> +               IMAGE_SECTION_HEADER *sec = &sections[i];
>> +               virt_size = max_t(unsigned long, virt_size,
>> +                                 sec->VirtualAddress + sec->Misc.VirtualSize);
>> +       }
>> +
>> +       /* Read 32/64bit specific header bits */
>> +       if (can_run_nt64 &&
>> +           (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)) {
>> +               IMAGE_NT_HEADERS64 *nt64 = (void *)nt;
>> +               IMAGE_OPTIONAL_HEADER64 *opt = &nt64->OptionalHeader;
>> +               image_size = opt->SizeOfImage;
>> +               efi_reloc = efi_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 = &sections[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",
>> +                                      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

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 04/14] efi_loader: Add boot time services
  2016-01-31 15:19   ` Simon Glass
@ 2016-02-01 23:45     ` Alexander Graf
  2016-02-01 23:54       ` Simon Glass
  0 siblings, 1 reply; 45+ messages in thread
From: Alexander Graf @ 2016-02-01 23:45 UTC (permalink / raw)
  To: u-boot



On 01/31/2016 04:19 PM, Simon Glass wrote:
> Hi Alexander,
>
> On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
>> 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 <agraf@suse.de>
>>
>> ---
>>
>> 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
> Reviewed-by: Simon Glass <sjg@chromium.org>
>
> 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.

>
>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>> index bf77573..391459e 100644
>> --- a/include/efi_loader.h
>> +++ b/include/efi_loader.h
>> @@ -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);
> 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.

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

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 04/14] efi_loader: Add boot time services
  2016-02-01 23:45     ` Alexander Graf
@ 2016-02-01 23:54       ` Simon Glass
  2016-02-02  0:02         ` Alexander Graf
  0 siblings, 1 reply; 45+ messages in thread
From: Simon Glass @ 2016-02-01 23:54 UTC (permalink / raw)
  To: u-boot

Hi Alex,

On 1 February 2016 at 16:45, Alexander Graf <agraf@suse.de> wrote:
>
>
>
> On 01/31/2016 04:19 PM, Simon Glass wrote:
>>
>> Hi Alexander,
>>
>> On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
>>>
>>> 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 <agraf@suse.de>
>>>
>>> ---
>>>
>>> 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
>>
>> Reviewed-by: Simon Glass <sjg@chromium.org>
>>
>> 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.

OK. Or maybe you need a bool to track when it is not active?

>
>
>>
>>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>>> index bf77573..391459e 100644
>>> --- a/include/efi_loader.h
>>> +++ b/include/efi_loader.h
>>> @@ -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);
>>
>> 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.
>
>
> 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

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 06/14] efi_loader: Add runtime services
  2016-01-31 15:20   ` Simon Glass
@ 2016-02-01 23:57     ` Alexander Graf
  0 siblings, 0 replies; 45+ messages in thread
From: Alexander Graf @ 2016-02-01 23:57 UTC (permalink / raw)
  To: u-boot



On 01/31/2016 04:20 PM, Simon Glass wrote:
> Hi Alexander,
>
> On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
>> 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 <agraf@suse.de>
>>
>> ---
>>
>> 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
> Reviewed-by: Simon Glass <sjg@chromium.org>
>
> A few nits.
>
>> diff --git a/arch/arm/config.mk b/arch/arm/config.mk
>> index 0550225..ecb956d 100644
>> --- a/arch/arm/config.mk
>> +++ b/arch/arm/config.mk
>> @@ -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
>> diff --git a/arch/arm/cpu/armv8/u-boot.lds b/arch/arm/cpu/armv8/u-boot.lds
>> index 4c12222..fd15ad5 100644
>> --- a/arch/arm/cpu/armv8/u-boot.lds
>> +++ b/arch/arm/cpu/armv8/u-boot.lds
>> @@ -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)
>> diff --git a/arch/arm/cpu/u-boot.lds b/arch/arm/cpu/u-boot.lds
>> index d48a905..596a17d 100644
>> --- a/arch/arm/cpu/u-boot.lds
>> +++ b/arch/arm/cpu/u-boot.lds
>> @@ -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)
>> diff --git a/arch/arm/lib/sections.c b/arch/arm/lib/sections.c
>> index a1205c3..6a94522 100644
>> --- a/arch/arm/lib/sections.c
>> +++ b/arch/arm/lib/sections.c
>> @@ -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")));
>> diff --git a/board/ti/am335x/u-boot.lds b/board/ti/am335x/u-boot.lds
>> index 78f294a..a56cc82 100644
>> --- a/board/ti/am335x/u-boot.lds
>> +++ b/board/ti/am335x/u-boot.lds
>> @@ -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)
>> diff --git a/common/board_r.c b/common/board_r.c
>> index 75ee43e..420e2c8 100644
>> --- a/common/board_r.c
>> +++ b/common/board_r.c
>> @@ -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;
>>   }
>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>> index d314002..0f821ff 100644
>> --- a/include/efi_loader.h
>> +++ b/include/efi_loader.h
>> @@ -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);
> Function comment please.
>
>>   #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
>> diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
>> index 5756c9c..45217ef 100644
>> --- a/lib/efi_loader/efi_boottime.c
>> +++ b/lib/efi_loader/efi_boottime.c
>> @@ -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 */
>> diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
>> new file mode 100644
>> index 0000000..b7aa1e9
>> --- /dev/null
>> +++ b/lib/efi_loader/efi_runtime.c
>> @@ -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.
>
>> +#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;
>> +};
> 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 ;).

>
>> +
>> +/*
>> + * 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;
>> +#ifdef CONFIG_DM_RTC
> Since this is a new service I don't think you need to support the old RTC API.

ok

Alex

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 00/14] EFI payload / application support v2
  2016-02-01 21:38       ` Alexander Graf
@ 2016-02-02  0:02         ` Simon Glass
  2016-02-02  0:16           ` Alexander Graf
  0 siblings, 1 reply; 45+ messages in thread
From: Simon Glass @ 2016-02-02  0:02 UTC (permalink / raw)
  To: u-boot

Hi Alex,

On 1 February 2016 at 14:38, Alexander Graf <agraf@suse.de> wrote:
> Hi Simon,
>
>
> On 02/01/2016 03:52 AM, Simon Glass wrote:
>>
>> Hi Alexander,
>>
>> On 31 January 2016 at 14:43, Alexander Graf <agraf@suse.de> wrote:
>>>
>>>
>>> On 01/31/2016 04:17 PM, Simon Glass wrote:
>>>>
>>>> Hi Alexander,
>>>>
>>>> On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
>>>>>
>>>>> 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 ;).
>>>
>>>
>>>>> 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.
>>>>
>>>>> 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.
>>>>
>>>> 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.
>>>
>>>> Another thing you could list is efi_set_watchdog_timer().
>>>
>>>
>>> What about it exactly? That it's not supported atm?
>>
>> Yes.
>>
>>>>> 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
>>>
>>>
>>> http://download.opensuse.org/repositories/devel:/ARM:/Factory:/Contrib:/HIP04D01/images/openSUSE-Tumbleweed-ARM-JeOS-hip04d01.armv7l.install.tar.xz
>>>
>>> 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:
>
> http://download.opensuse.org/ports/armv7hl/tumbleweed/repo/oss/suse/armv7hl/grub2-arm-efi-2.02~beta2-54.1.armv7hl.rpm

OK will try.

>
>>
>> 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.
>>
>>> 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.

Me neither, but then I turned that off a year or so ago after I had problems.

>
>
>>
>>>> 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.
>
>
> 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.

>
>> 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.

>
>> 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

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 04/14] efi_loader: Add boot time services
  2016-02-01 23:54       ` Simon Glass
@ 2016-02-02  0:02         ` Alexander Graf
  0 siblings, 0 replies; 45+ messages in thread
From: Alexander Graf @ 2016-02-02  0:02 UTC (permalink / raw)
  To: u-boot



On 02/02/2016 12:54 AM, Simon Glass wrote:
> Hi Alex,
>
> On 1 February 2016 at 16:45, Alexander Graf <agraf@suse.de> wrote:
>>
>>
>> On 01/31/2016 04:19 PM, Simon Glass wrote:
>>> Hi Alexander,
>>>
>>> On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
>>>> 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 <agraf@suse.de>
>>>>
>>>> ---
>>>>
>>>> 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
>>> Reviewed-by: Simon Glass <sjg@chromium.org>
>>>
>>> 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.
> 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.

>
>>
>>>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>>>> index bf77573..391459e 100644
>>>> --- a/include/efi_loader.h
>>>> +++ b/include/efi_loader.h
>>>> @@ -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);
>>> 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.
>>
>> 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 :-)

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

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 00/14] EFI payload / application support v2
  2016-02-02  0:02         ` Simon Glass
@ 2016-02-02  0:16           ` Alexander Graf
  2016-02-02  0:28             ` Simon Glass
  0 siblings, 1 reply; 45+ messages in thread
From: Alexander Graf @ 2016-02-02  0:16 UTC (permalink / raw)
  To: u-boot



On 02/02/2016 01:02 AM, Simon Glass wrote:
> Hi Alex,
>
> On 1 February 2016 at 14:38, Alexander Graf <agraf@suse.de> wrote:
>
>>>>> 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.
>>
>> 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

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 00/14] EFI payload / application support v2
  2016-02-02  0:16           ` Alexander Graf
@ 2016-02-02  0:28             ` Simon Glass
  0 siblings, 0 replies; 45+ messages in thread
From: Simon Glass @ 2016-02-02  0:28 UTC (permalink / raw)
  To: u-boot

Hi Alex,

On 1 February 2016 at 17:16, Alexander Graf <agraf@suse.de> wrote:
>
>
>
> On 02/02/2016 01:02 AM, Simon Glass wrote:
>>
>> Hi Alex,
>>
>> On 1 February 2016 at 14:38, Alexander Graf <agraf@suse.de> wrote:
>>
>>>>>> 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.
>>>
>>>
>>> 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 :)

Sounds good, thanks.

Regards,
Simon

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 07/14] efi_loader: Add disk interfaces
  2016-01-31 15:23   ` Simon Glass
@ 2016-02-02  0:32     ` Alexander Graf
  0 siblings, 0 replies; 45+ messages in thread
From: Alexander Graf @ 2016-02-02  0:32 UTC (permalink / raw)
  To: u-boot



On 01/31/2016 04:23 PM, Simon Glass wrote:
> On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
>> 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 <agraf@suse.de>
>> Reviewed-by: Simon Glass <sjg@chromium.org>
>>
>> ---
>>
>> 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
>>
>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>> index 0f821ff..9d6c322 100644
>> --- a/include/efi_loader.h
>> +++ b/include/efi_loader.h
>> @@ -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,
>> diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
>> new file mode 100644
>> index 0000000..c589604
>> --- /dev/null
>> +++ b/lib/efi_loader/efi_disk.c
>> @@ -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
>> +       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/?
>
>> +       else
>> +               n = desc->block_write(desc->dev, lba, blocks, buffer);
> Same here.
>
>> +
>> +       /* 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,
>> +};
>> +
>> +/*
>> + * 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.

>
>> + *
>> + * 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)

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?

>
>> +       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.

>
>> +                       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.

Ah, I didn't realize you could have DEV_TYPE_UNKNOWN.

>
>> +
>> +                       diskobj = calloc(1, objlen);
>> +
>> +                       /* Fill in object data */
> drop extra blank line, and below
>
>> +
>> +                       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 */
>> +
>> +                       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

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 09/14] efi_loader: Implement memory allocation and map
  2016-01-31 15:23   ` Simon Glass
@ 2016-02-02  0:59     ` Alexander Graf
  0 siblings, 0 replies; 45+ messages in thread
From: Alexander Graf @ 2016-02-02  0:59 UTC (permalink / raw)
  To: u-boot



On 01/31/2016 04:23 PM, Simon Glass wrote:
> Hi Alexander,
>
> On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
>> 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 <agraf@suse.de>
>> ---
>>   lib/efi_loader/efi_boottime.c     | 99 ++++++++++++++++++++++++++++++++++++++-
>>   lib/efi_loader/efi_image_loader.c | 28 ++++++++++-
>>   2 files changed, 123 insertions(+), 4 deletions(-)
>>
>> diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
>> index 45217ef..ff3f969 100644
>> --- a/lib/efi_loader/efi_boottime.c
>> +++ b/lib/efi_loader/efi_boottime.c
>> @@ -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   |
> 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.

>
>> + * |____________________________|   &__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
> If not defined, what happens?

The new code uses gd->bd->bi_dram.

>
>> +       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)
>> diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
>> index a7788bf..67c4b06 100644
>> --- a/lib/efi_loader/efi_image_loader.c
>> +++ b/lib/efi_loader/efi_image_loader.c
>> @@ -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;
> I think it would be better to reserve this in board_f() with a
> reserve...() function. Perhaps store the address in global_data.

The new code doesn't reserve anything anymore :)


Alex

>
>> +       }
>> +
>> +       len = ROUND_UP(len, 4096);
>> +       /* Out of memory */
>> +       if ((loader_pool + len) >= (gd->relocaddr - TOTAL_MALLOC_LEN))
> debug() here?
>
>> +               return NULL;
>> +
>> +       r = (void *)loader_pool;
>> +       loader_pool += len;
>> +
>> +       return r;
>>   }
>>
>>   /*
>> --
>> 2.1.4
> Regards,
> Simon

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 14/14] efi_loader: Add distro boot script for removable media
  2016-01-31 15:24   ` Simon Glass
@ 2016-02-02  1:05     ` Alexander Graf
  0 siblings, 0 replies; 45+ messages in thread
From: Alexander Graf @ 2016-02-02  1:05 UTC (permalink / raw)
  To: u-boot



On 01/31/2016 04:24 PM, Simon Glass wrote:
> Hi Alexander,
>
> On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
>> 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.
>>
>> Signed-off-by: Alexander Graf <agraf@suse.de>
>> ---
>>   include/config_distro_bootcmd.h | 47 ++++++++++++++++++++++++++++++++++++++++-
>>   1 file changed, 46 insertions(+), 1 deletion(-)
> Reviewed-by: Simon Glass <sjg@chromium.org>
>
> Will this work OK with FITs?

Uh, why would it not work ok with FITs?

> 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

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [U-Boot] [PATCH 04/14] efi_loader: Add boot time services
  2016-01-20  0:16   ` Leif Lindholm
@ 2016-02-02  1:52     ` Alexander Graf
  0 siblings, 0 replies; 45+ messages in thread
From: Alexander Graf @ 2016-02-02  1:52 UTC (permalink / raw)
  To: u-boot



On 01/20/2016 01:16 AM, Leif Lindholm wrote:
> On Fri, Jan 15, 2016 at 06:06:10AM +0100, Alexander Graf wrote:
>> 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 <agraf@suse.de>
>>
>> ---
>>
>> 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
>>
>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>> index bf77573..391459e 100644
>> --- a/include/efi_loader.h
>> +++ b/include/efi_loader.h
>> @@ -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
>> diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
>> new file mode 100644
>> index 0000000..5756c9c
>> --- /dev/null
>> +++ b/lib/efi_loader/efi_boottime.c
>> @@ -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:
>> +		/* 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.
>
>> +	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);
>> +}
>> +
>> +/* 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 :)
>
>> +}
>> +
>> +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);
>> +	}
>> +
>> +	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.
>
>> +	}
>> +	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.
>
>> +}
>> +
>> +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.
>
>> +}
>> +
>> +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.
>
>> +}
>> +
>> +static efi_status_t efi_check_event(void *event)
>> +{
>> +	EFI_ENTRY("%p", event);
>> +	return EFI_EXIT(efi_unsupported(__func__));
> EFI_NOT_READY?
>
>> +}
>> +
>> +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?
>
>> +}
>> +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?
>
>> +}
>> +
>> +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?
>
>> +}
>> +
>> +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".
>
>> +}
>> +
>> +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;
>> +		}
>> +	}
>> +
>> +	*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?
>
>> +}
>> +
>> +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?
>
>> +		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).
>
>> +
>> +	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.

I think I understood what you were saying and implemented it :). Let's 
see ;)

>
>> +	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);
>> +	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.

>
>> +
>> +	*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,
>> +		     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 :).

>
>> +}
>> +
>> +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);
>> +
>> +	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.
>
>> +	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();
>> +
>> +	/* 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?

Sure :)


Alex

^ permalink raw reply	[flat|nested] 45+ messages in thread

end of thread, other threads:[~2016-02-02  1:52 UTC | newest]

Thread overview: 45+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-01-15  5:06 [U-Boot] [PATCH 00/14] EFI payload / application support v2 Alexander Graf
2016-01-15  5:06 ` [U-Boot] [PATCH 01/14] disk/part.c: Expose list of available block drivers Alexander Graf
2016-01-15  5:06 ` [U-Boot] [PATCH 02/14] include/efi_api.h: Add more detailed API definitions Alexander Graf
2016-01-31 15:17   ` Simon Glass
2016-02-01 22:46     ` Alexander Graf
2016-01-15  5:06 ` [U-Boot] [PATCH 03/14] efi_loader: Add PE image loader Alexander Graf
2016-01-31 15:18   ` Simon Glass
2016-02-01 22:58     ` Alexander Graf
2016-01-15  5:06 ` [U-Boot] [PATCH 04/14] efi_loader: Add boot time services Alexander Graf
2016-01-20  0:16   ` Leif Lindholm
2016-02-02  1:52     ` Alexander Graf
2016-01-31 15:19   ` Simon Glass
2016-02-01 23:45     ` Alexander Graf
2016-02-01 23:54       ` Simon Glass
2016-02-02  0:02         ` Alexander Graf
2016-01-15  5:06 ` [U-Boot] [PATCH 05/14] efi_loader: Add console interface Alexander Graf
2016-01-31 15:19   ` Simon Glass
2016-01-15  5:06 ` [U-Boot] [PATCH 06/14] efi_loader: Add runtime services Alexander Graf
2016-01-21 17:20   ` Leif Lindholm
2016-01-31 15:20   ` Simon Glass
2016-02-01 23:57     ` Alexander Graf
2016-01-15  5:06 ` [U-Boot] [PATCH 07/14] efi_loader: Add disk interfaces Alexander Graf
2016-01-31 15:23   ` Simon Glass
2016-02-02  0:32     ` Alexander Graf
2016-01-15  5:06 ` [U-Boot] [PATCH 08/14] efi_loader: Add "bootefi" command Alexander Graf
2016-01-31 15:23   ` Simon Glass
2016-01-15  5:06 ` [U-Boot] [PATCH 09/14] efi_loader: Implement memory allocation and map Alexander Graf
2016-01-31 15:23   ` Simon Glass
2016-02-02  0:59     ` Alexander Graf
2016-01-15  5:06 ` [U-Boot] [PATCH 10/14] arm64: Allow exceptions to return Alexander Graf
2016-01-15  5:06 ` [U-Boot] [PATCH 11/14] arm64: Allow EFI payload code to take exceptions Alexander Graf
2016-01-15  5:06 ` [U-Boot] [PATCH 12/14] efi_loader: Add DCACHE_OFF support for arm64 Alexander Graf
2016-01-15  5:06 ` [U-Boot] [PATCH 13/14] efi_loader: hook up in build environment Alexander Graf
2016-01-31 15:24   ` Simon Glass
2016-01-15  5:06 ` [U-Boot] [PATCH 14/14] efi_loader: Add distro boot script for removable media Alexander Graf
2016-01-31 15:24   ` Simon Glass
2016-02-02  1:05     ` Alexander Graf
2016-01-31 15:17 ` [U-Boot] [PATCH 00/14] EFI payload / application support v2 Simon Glass
2016-01-31 21:43   ` Alexander Graf
2016-02-01  2:52     ` Simon Glass
2016-02-01  3:25       ` Simon Glass
2016-02-01 21:38       ` Alexander Graf
2016-02-02  0:02         ` Simon Glass
2016-02-02  0:16           ` Alexander Graf
2016-02-02  0:28             ` Simon Glass

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.