[U-Boot] [PATCH 0/9] EFI payload / application support

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 runtime services (all calls return unimplemented) - No EFI variables
Of course, there are still a few things one could do on top:
- Implement removable media booting (search for /efi/boot/boota{a64,rm}.efi) - 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 ;)
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.
Alex
Alexander Graf (9): disk/part.c: Expose a 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: hook up in build environment
arch/arm/cpu/armv8/u-boot.lds | 8 + arch/arm/cpu/u-boot.lds | 13 + arch/arm/lib/sections.c | 2 + common/Makefile | 1 + common/cmd_bootefi.c | 168 ++++++++ disk/part.c | 25 ++ include/efi_api.h | 197 +++++++-- include/efi_loader.h | 87 ++++ include/part.h | 2 + include/pe.h | 277 +++++++++++++ lib/Kconfig | 1 + lib/Makefile | 1 + lib/efi_loader/Kconfig | 8 + lib/efi_loader/Makefile | 11 + lib/efi_loader/efi_boottime.c | 838 ++++++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_console.c | 371 +++++++++++++++++ lib/efi_loader/efi_disk.c | 227 +++++++++++ lib/efi_loader/efi_image_loader.c | 203 +++++++++ lib/efi_loader/efi_runtime.c | 59 +++ 19 files changed, 2462 insertions(+), 37 deletions(-) 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

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 a list of all available block drivers.
Signed-off-by: Alexander Graf agraf@suse.de --- disk/part.c | 25 +++++++++++++++++++++++++ include/part.h | 2 ++ 2 files changed, 27 insertions(+)
diff --git a/disk/part.c b/disk/part.c index 909712e..5bc64c7 100644 --- a/disk/part.c +++ b/disk/part.c @@ -26,6 +26,31 @@ struct block_drvr { int (*select_hwpart)(int dev_num, int hwpart); };
+const char *available_block_drvrs[] = { +#if defined(CONFIG_CMD_IDE) + "ide", +#endif +#if defined(CONFIG_CMD_SATA) + "sata", +#endif +#if defined(CONFIG_CMD_SCSI) + "scsi", +#endif +#if defined(CONFIG_CMD_USB) && defined(CONFIG_USB_STORAGE) + "usb", +#endif +#if defined(CONFIG_MMC) + "mmc", +#endif +#if defined(CONFIG_SYSTEMACE) + "ace", +#endif +#if defined(CONFIG_SANDBOX) + "host", +#endif + NULL, +}; + static const struct block_drvr block_drvr[] = { #if defined(CONFIG_CMD_IDE) { .name = "ide", .get_dev = ide_get_dev, }, diff --git a/include/part.h b/include/part.h index 720a867..dc2a78b 100644 --- a/include/part.h +++ b/include/part.h @@ -122,6 +122,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 char *available_block_drvrs[]; #else static inline block_dev_desc_t *get_dev(const char *ifname, int dev) { return NULL; }

On Tue, Dec 22, 2015 at 02:57:48PM +0100, Alexander Graf wrote:
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 a list of all available block drivers.
Signed-off-by: Alexander Graf agraf@suse.de
Reviewed-by: Tom Rini trini@konsulko.com

Hi Alexander,
On 22 December 2015 at 06:57, Alexander Graf agraf@suse.de wrote:
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 a list of all available block drivers.
Signed-off-by: Alexander Graf agraf@suse.de
disk/part.c | 25 +++++++++++++++++++++++++ include/part.h | 2 ++ 2 files changed, 27 insertions(+)
diff --git a/disk/part.c b/disk/part.c index 909712e..5bc64c7 100644 --- a/disk/part.c +++ b/disk/part.c @@ -26,6 +26,31 @@ struct block_drvr { int (*select_hwpart)(int dev_num, int hwpart); };
+const char *available_block_drvrs[] = { +#if defined(CONFIG_CMD_IDE)
"ide",
+#endif +#if defined(CONFIG_CMD_SATA)
"sata",
+#endif +#if defined(CONFIG_CMD_SCSI)
"scsi",
+#endif +#if defined(CONFIG_CMD_USB) && defined(CONFIG_USB_STORAGE)
"usb",
+#endif +#if defined(CONFIG_MMC)
"mmc",
+#endif +#if defined(CONFIG_SYSTEMACE)
"ace",
+#endif +#if defined(CONFIG_SANDBOX)
"host",
+#endif
NULL,
+};
You seem to be duplicating block_drvr[]. Can we not just use that?
static const struct block_drvr block_drvr[] = { #if defined(CONFIG_CMD_IDE) { .name = "ide", .get_dev = ide_get_dev, }, diff --git a/include/part.h b/include/part.h index 720a867..dc2a78b 100644 --- a/include/part.h +++ b/include/part.h @@ -122,6 +122,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 char *available_block_drvrs[]; #else static inline block_dev_desc_t *get_dev(const char *ifname, int dev) { return NULL; } -- 2.1.4
Regards, Simon

On 15.01.16 00:11, Simon Glass wrote:
Hi Alexander,
On 22 December 2015 at 06:57, Alexander Graf agraf@suse.de wrote:
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 a list of all available block drivers.
Signed-off-by: Alexander Graf agraf@suse.de
disk/part.c | 25 +++++++++++++++++++++++++ include/part.h | 2 ++ 2 files changed, 27 insertions(+)
diff --git a/disk/part.c b/disk/part.c index 909712e..5bc64c7 100644 --- a/disk/part.c +++ b/disk/part.c @@ -26,6 +26,31 @@ struct block_drvr { int (*select_hwpart)(int dev_num, int hwpart); };
+const char *available_block_drvrs[] = { +#if defined(CONFIG_CMD_IDE)
"ide",
+#endif +#if defined(CONFIG_CMD_SATA)
"sata",
+#endif +#if defined(CONFIG_CMD_SCSI)
"scsi",
+#endif +#if defined(CONFIG_CMD_USB) && defined(CONFIG_USB_STORAGE)
"usb",
+#endif +#if defined(CONFIG_MMC)
"mmc",
+#endif +#if defined(CONFIG_SYSTEMACE)
"ace",
+#endif +#if defined(CONFIG_SANDBOX)
"host",
+#endif
NULL,
+};
You seem to be duplicating block_drvr[]. Can we not just use that?
It would mean that we'd have to make it public then - to me it looked like people kept it static for a reason.
However if everyone's happy if I expose it (and the struct definition behind it), I'm certainly more than happy to move to that one instead :).
Alex
static const struct block_drvr block_drvr[] = { #if defined(CONFIG_CMD_IDE) { .name = "ide", .get_dev = ide_get_dev, }, diff --git a/include/part.h b/include/part.h index 720a867..dc2a78b 100644 --- a/include/part.h +++ b/include/part.h @@ -122,6 +122,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 char *available_block_drvrs[]; #else static inline block_dev_desc_t *get_dev(const char *ifname, int dev) { return NULL; } -- 2.1.4
Regards, Simon

Hi Alex,
On 14 January 2016 at 16:33, Alexander Graf agraf@suse.de wrote:
On 15.01.16 00:11, Simon Glass wrote:
Hi Alexander,
On 22 December 2015 at 06:57, Alexander Graf agraf@suse.de wrote:
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 a list of all available block drivers.
Signed-off-by: Alexander Graf agraf@suse.de
disk/part.c | 25 +++++++++++++++++++++++++ include/part.h | 2 ++ 2 files changed, 27 insertions(+)
diff --git a/disk/part.c b/disk/part.c index 909712e..5bc64c7 100644 --- a/disk/part.c +++ b/disk/part.c @@ -26,6 +26,31 @@ struct block_drvr { int (*select_hwpart)(int dev_num, int hwpart); };
+const char *available_block_drvrs[] = { +#if defined(CONFIG_CMD_IDE)
"ide",
+#endif +#if defined(CONFIG_CMD_SATA)
"sata",
+#endif +#if defined(CONFIG_CMD_SCSI)
"scsi",
+#endif +#if defined(CONFIG_CMD_USB) && defined(CONFIG_USB_STORAGE)
"usb",
+#endif +#if defined(CONFIG_MMC)
"mmc",
+#endif +#if defined(CONFIG_SYSTEMACE)
"ace",
+#endif +#if defined(CONFIG_SANDBOX)
"host",
+#endif
NULL,
+};
You seem to be duplicating block_drvr[]. Can we not just use that?
It would mean that we'd have to make it public then - to me it looked like people kept it static for a reason.
However if everyone's happy if I expose it (and the struct definition behind it), I'm certainly more than happy to move to that one instead :).
But you have created a public one which is a copy of part of it, so it doesn't seem like much of a benefit :-)
How about adding a function to return the device name of a member? Then you should be able to avoid copying it, and avoid making it global.
I'm very interested in your series, and hope to review the rest of it (and try it out) soon!
Alex
static const struct block_drvr block_drvr[] = { #if defined(CONFIG_CMD_IDE) { .name = "ide", .get_dev = ide_get_dev, }, diff --git a/include/part.h b/include/part.h index 720a867..dc2a78b 100644 --- a/include/part.h +++ b/include/part.h @@ -122,6 +122,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 char *available_block_drvrs[]; #else static inline block_dev_desc_t *get_dev(const char *ifname, int dev) { return NULL; } -- 2.1.4
Regards, Simon
Regards, Simon

On 15.01.16 01:46, Simon Glass wrote:
Hi Alex,
On 14 January 2016 at 16:33, Alexander Graf agraf@suse.de wrote:
On 15.01.16 00:11, Simon Glass wrote:
Hi Alexander,
On 22 December 2015 at 06:57, Alexander Graf agraf@suse.de wrote:
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 a list of all available block drivers.
Signed-off-by: Alexander Graf agraf@suse.de
disk/part.c | 25 +++++++++++++++++++++++++ include/part.h | 2 ++ 2 files changed, 27 insertions(+)
diff --git a/disk/part.c b/disk/part.c index 909712e..5bc64c7 100644 --- a/disk/part.c +++ b/disk/part.c @@ -26,6 +26,31 @@ struct block_drvr { int (*select_hwpart)(int dev_num, int hwpart); };
+const char *available_block_drvrs[] = { +#if defined(CONFIG_CMD_IDE)
"ide",
+#endif +#if defined(CONFIG_CMD_SATA)
"sata",
+#endif +#if defined(CONFIG_CMD_SCSI)
"scsi",
+#endif +#if defined(CONFIG_CMD_USB) && defined(CONFIG_USB_STORAGE)
"usb",
+#endif +#if defined(CONFIG_MMC)
"mmc",
+#endif +#if defined(CONFIG_SYSTEMACE)
"ace",
+#endif +#if defined(CONFIG_SANDBOX)
"host",
+#endif
NULL,
+};
You seem to be duplicating block_drvr[]. Can we not just use that?
It would mean that we'd have to make it public then - to me it looked like people kept it static for a reason.
However if everyone's happy if I expose it (and the struct definition behind it), I'm certainly more than happy to move to that one instead :).
But you have created a public one which is a copy of part of it, so it doesn't seem like much of a benefit :-)
How about adding a function to return the device name of a member?
I wouldn't know the struct size, so I couldn't walk the array, right? :)
Then you should be able to avoid copying it, and avoid making it global.
I'm very interested in your series, and hope to review the rest of it (and try it out) soon!
Wait for v2, I'm just polishing it up :). It fixes a good number of issues on real hardware. I have the code running well on BBB and HiKey now.
Alex

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

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 --- include/efi_loader.h | 37 +++++ include/pe.h | 277 ++++++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_image_loader.c | 203 ++++++++++++++++++++++++++++ 3 files changed, 517 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..da82354 --- /dev/null +++ b/include/efi_loader.h @@ -0,0 +1,37 @@ +/* + * EFI application loader + * + * Copyright (c) 2015 Alexander Graf + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#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..009b0c5 --- /dev/null +++ b/include/pe.h @@ -0,0 +1,277 @@ +/* + * Portable Executable binary format structures + * + * Copyright (c) 2015 Alexander Graf + * + * Based on wine code + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#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..688e177 --- /dev/null +++ b/lib/efi_loader/efi_image_loader.c @@ -0,0 +1,203 @@ +/* + * EFI image loader + * + * based partly on wine code + * + * Copyright (c) 2015 Alexander Graf + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#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; +} + +/* + * 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 malloc region 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) +{ + static unsigned long loader_pool; + void *r; + + if (!loader_pool) { + loader_pool = gd->relocaddr - TOTAL_MALLOC_LEN - + EFI_LOADER_POOL_SIZE - (16 * MB); + } + + 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; +} + +/* + * 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; + + dos = efi; + if (dos->e_magic != IMAGE_DOS_SIGNATURE) { + printf("%s: Invalid DOS Signature\n", __func__); + return NULL; + } + + nt = (void *) ((char *)efi + dos->e_lfanew); + if (nt->Signature != IMAGE_NT_SIGNATURE) { + printf("%s: Invalid NT Signature\n", __func__); + return NULL; + } + + /* Calculate upper virtual address boundary */ + num_sections = nt->FileHeader.NumberOfSections; + sections = (void *)&nt->OptionalHeader + + nt->FileHeader.SizeOfOptionalHeader; + + for (i = num_sections - 1; i >= 0; i--) { + IMAGE_SECTION_HEADER *sec = §ions[i]; + virt_size = max_t(unsigned long, virt_size, + sec->VirtualAddress + sec->Misc.VirtualSize); + } + + /* Read 32/64bit specific header bits */ + if (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 { + 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; + } + + /* Load sections into RAM */ + for (i = num_sections - 1; i >= 0; i--) { + IMAGE_SECTION_HEADER *sec = §ions[i]; + memset(efi_reloc + sec->VirtualAddress, 0, + sec->Misc.VirtualSize); + memcpy(efi_reloc + sec->VirtualAddress, + efi + sec->PointerToRawData, + sec->SizeOfRawData); + } + + + /* Run through relocations */ + end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + rel_size); + + while (rel < end - 1 && rel->SizeOfBlock) { + relocs = (const uint16_t *)(rel + 1); + i = (rel->SizeOfBlock - sizeof(*rel)) / sizeof(uint16_t); + while (i--) { + uint16_t offset = (*relocs & 0xfff) + rel->VirtualAddress; + int type = *relocs >> 12; + unsigned long delta = (unsigned long)efi_reloc; + uint64_t *x64 = efi_reloc + offset; + uint32_t *x32 = efi_reloc + offset; + uint16_t *x16 = efi_reloc + offset; + + switch (type) { + case IMAGE_REL_BASED_ABSOLUTE: + break; + case IMAGE_REL_BASED_HIGH: + *x16 += ((uint32_t)delta) >> 16; + break; + case IMAGE_REL_BASED_LOW: + *x16 += (uint16_t)delta; + break; + case IMAGE_REL_BASED_HIGHLOW: + *x32 += (uint32_t)delta; + break; + case IMAGE_REL_BASED_DIR64: + *x64 += (uint64_t)delta; + break; + default: + printf("Unknown Relocation off %x type %x\n", + 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; +}

On Tue, Dec 22, 2015 at 02:57:50PM +0100, Alexander Graf 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
include/efi_loader.h | 37 +++++ include/pe.h | 277 ++++++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_image_loader.c | 203 ++++++++++++++++++++++++++++ 3 files changed, 517 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..da82354 --- /dev/null +++ b/include/efi_loader.h @@ -0,0 +1,37 @@ +/*
- EFI application loader
- Copyright (c) 2015 Alexander Graf
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- SPDX-License-Identifier: LGPL-2.1+
- */
+#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..009b0c5 --- /dev/null +++ b/include/pe.h @@ -0,0 +1,277 @@ +/*
- Portable Executable binary format structures
- Copyright (c) 2015 Alexander Graf
- Based on wine code
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- SPDX-License-Identifier: LGPL-2.1+
- */
+#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..688e177 --- /dev/null +++ b/lib/efi_loader/efi_image_loader.c @@ -0,0 +1,203 @@ +/*
- EFI image loader
- based partly on wine code
- Copyright (c) 2015 Alexander Graf
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- SPDX-License-Identifier: LGPL-2.1+
- */
+#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;
+}
+/*
- 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 malloc region start to give the stack some space.
Is there any chance you could break out the memory allocation and memory map management as a separate patch, rather than spread across 3,4 and 6?
Assigning a specific 128MB pool for LOADER_DATA memory can have unexpected side effects.
- Then every allocation gets a 4k aligned chunk from it. We never free.
Never freeing LOADER_DATA should not be an issue in and of itself. It all gets zapped by the OS anyway.
- */
+void *efi_loader_alloc(uint64_t len) +{
- static unsigned long loader_pool;
- void *r;
- if (!loader_pool) {
loader_pool = gd->relocaddr - TOTAL_MALLOC_LEN -
EFI_LOADER_POOL_SIZE - (16 * MB);
- }
- 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;
+}
+/*
- 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;
- dos = efi;
- if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
printf("%s: Invalid DOS Signature\n", __func__);
return NULL;
- }
- nt = (void *) ((char *)efi + dos->e_lfanew);
- if (nt->Signature != IMAGE_NT_SIGNATURE) {
printf("%s: Invalid NT Signature\n", __func__);
return NULL;
- }
- /* Calculate upper virtual address boundary */
- num_sections = nt->FileHeader.NumberOfSections;
- sections = (void *)&nt->OptionalHeader +
nt->FileHeader.SizeOfOptionalHeader;
- for (i = num_sections - 1; i >= 0; i--) {
IMAGE_SECTION_HEADER *sec = §ions[i];
virt_size = max_t(unsigned long, virt_size,
sec->VirtualAddress + sec->Misc.VirtualSize);
- }
- /* Read 32/64bit specific header bits */
I guess x86 may support 32-on-64 or 64-on-32, but ARM won't - so could probably be compile-time conditionalised somehow. (CONFIG_EFI_SUPPORTS_PE32/CONFIG_EFI_SUPPORTS_PE64)
- if (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 {
...and should still check the magic value for 32-bit?
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;
- }
- /* Load sections into RAM */
- for (i = num_sections - 1; i >= 0; i--) {
IMAGE_SECTION_HEADER *sec = §ions[i];
memset(efi_reloc + sec->VirtualAddress, 0,
sec->Misc.VirtualSize);
memcpy(efi_reloc + sec->VirtualAddress,
efi + sec->PointerToRawData,
sec->SizeOfRawData);
- }
- /* Run through relocations */
- end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + rel_size);
- while (rel < end - 1 && rel->SizeOfBlock) {
relocs = (const uint16_t *)(rel + 1);
i = (rel->SizeOfBlock - sizeof(*rel)) / sizeof(uint16_t);
while (i--) {
uint16_t offset = (*relocs & 0xfff) + rel->VirtualAddress;
int type = *relocs >> 12;
unsigned long delta = (unsigned long)efi_reloc;
uint64_t *x64 = efi_reloc + offset;
uint32_t *x32 = efi_reloc + offset;
uint16_t *x16 = efi_reloc + offset;
switch (type) {
case IMAGE_REL_BASED_ABSOLUTE:
break;
case IMAGE_REL_BASED_HIGH:
*x16 += ((uint32_t)delta) >> 16;
break;
case IMAGE_REL_BASED_LOW:
*x16 += (uint16_t)delta;
break;
case IMAGE_REL_BASED_HIGHLOW:
*x32 += (uint32_t)delta;
break;
case IMAGE_REL_BASED_DIR64:
*x64 += (uint64_t)delta;
break;
default:
printf("Unknown Relocation off %x type %x\n",
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

On 26.12.15 17:26, Leif Lindholm wrote:
On Tue, Dec 22, 2015 at 02:57:50PM +0100, Alexander Graf 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
include/efi_loader.h | 37 +++++ include/pe.h | 277 ++++++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_image_loader.c | 203 ++++++++++++++++++++++++++++ 3 files changed, 517 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..da82354 --- /dev/null +++ b/include/efi_loader.h @@ -0,0 +1,37 @@ +/*
- EFI application loader
- Copyright (c) 2015 Alexander Graf
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- SPDX-License-Identifier: LGPL-2.1+
- */
+#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..009b0c5 --- /dev/null +++ b/include/pe.h @@ -0,0 +1,277 @@ +/*
- Portable Executable binary format structures
- Copyright (c) 2015 Alexander Graf
- Based on wine code
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- SPDX-License-Identifier: LGPL-2.1+
- */
+#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..688e177 --- /dev/null +++ b/lib/efi_loader/efi_image_loader.c @@ -0,0 +1,203 @@ +/*
- EFI image loader
- based partly on wine code
- Copyright (c) 2015 Alexander Graf
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- SPDX-License-Identifier: LGPL-2.1+
- */
+#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;
+}
+/*
- 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 malloc region start to give the stack some space.
Is there any chance you could break out the memory allocation and memory map management as a separate patch, rather than spread across 3,4 and 6?
Uh, sure, I can try. I'm not sure it'll end up more readable than now though :).
Assigning a specific 128MB pool for LOADER_DATA memory can have unexpected side effects.
Like for example?
- Then every allocation gets a 4k aligned chunk from it. We never free.
Never freeing LOADER_DATA should not be an issue in and of itself. It all gets zapped by the OS anyway.
That's the idea, yes :).
- */
+void *efi_loader_alloc(uint64_t len) +{
- static unsigned long loader_pool;
- void *r;
- if (!loader_pool) {
loader_pool = gd->relocaddr - TOTAL_MALLOC_LEN -
EFI_LOADER_POOL_SIZE - (16 * MB);
- }
- 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;
+}
+/*
- 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;
- dos = efi;
- if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
printf("%s: Invalid DOS Signature\n", __func__);
return NULL;
- }
- nt = (void *) ((char *)efi + dos->e_lfanew);
- if (nt->Signature != IMAGE_NT_SIGNATURE) {
printf("%s: Invalid NT Signature\n", __func__);
return NULL;
- }
- /* Calculate upper virtual address boundary */
- num_sections = nt->FileHeader.NumberOfSections;
- sections = (void *)&nt->OptionalHeader +
nt->FileHeader.SizeOfOptionalHeader;
- for (i = num_sections - 1; i >= 0; i--) {
IMAGE_SECTION_HEADER *sec = §ions[i];
virt_size = max_t(unsigned long, virt_size,
sec->VirtualAddress + sec->Misc.VirtualSize);
- }
- /* Read 32/64bit specific header bits */
I guess x86 may support 32-on-64 or 64-on-32, but ARM won't - so could probably be compile-time conditionalised somehow. (CONFIG_EFI_SUPPORTS_PE32/CONFIG_EFI_SUPPORTS_PE64)
I think I have a reasonably elegant solution now. I hope.
- if (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 {
...and should still check the magic value for 32-bit?
True.
Alex

On Fri, Jan 15, 2016 at 12:45:46AM +0100, Alexander Graf wrote:
On 26.12.15 17:26, Leif Lindholm wrote:
new file mode 100644 index 0000000..688e177 --- /dev/null +++ b/lib/efi_loader/efi_image_loader.c @@ -0,0 +1,203 @@ +/*
- EFI image loader
- based partly on wine code
- Copyright (c) 2015 Alexander Graf
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- SPDX-License-Identifier: LGPL-2.1+
- */
+#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;
+}
+/*
- 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 malloc region start to give the stack some space.
Is there any chance you could break out the memory allocation and memory map management as a separate patch, rather than spread across 3,4 and 6?
Uh, sure, I can try. I'm not sure it'll end up more readable than now though :).
Heh, ok. It was a theory.
Assigning a specific 128MB pool for LOADER_DATA memory can have unexpected side effects.
Like for example?
Like applications running out of memory when there's plenty left in the system. OK, so not "unexpected" unexpected, but "may cause valid programs to break". My initial comment was because I was scratching my head over how GRUB managed to allocate its heap at all.
/ Leif

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 --- include/efi_loader.h | 41 +++ lib/efi_loader/efi_boottime.c | 838 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 879 insertions(+) create mode 100644 lib/efi_loader/efi_boottime.c
diff --git a/include/efi_loader.h b/include/efi_loader.h index da82354..ed7c389 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -24,14 +24,55 @@ #include <efi_api.h> #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); diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c new file mode 100644 index 0000000..ed95962 --- /dev/null +++ b/lib/efi_loader/efi_boottime.c @@ -0,0 +1,838 @@ +/* + * EFI application boot time services + * + * Copyright (c) 2015 Alexander Graf + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#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; + +/* + * 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 now 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) +{ + 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(efi_unsupported(__func__)); +} + +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) { + case EFI_LOADER_DATA: + return efi_loader_alloc(len); + 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_UNSUPPORTED; + break; + case 2: + /* Exact address, grant it. The addr is already in *memory. */ + break; + default: + r = EFI_UNSUPPORTED; + 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); +} + +/* + * 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, + }, + { + /* 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); + + 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) */ + efi_memory_map[0].num_pages = gd->relocaddr >> 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 + + /* Remove U-Boot from the available RAM view */ + efi_memory_map[0].num_pages -= gd->mon_len >> 12; + + /* Remove the malloc area from the available RAM view */ + efi_memory_map[0].num_pages -= TOTAL_MALLOC_LEN >> 12; + + /* 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) +{ + 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_UNSUPPORTED); + } + + 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; + + 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_UNSUPPORTED); + } + + 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; + + /* 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 efi_status_t efi_exit_boot_services(void *image_handle, + unsigned long map_key) +{ + EFI_ENTRY("%p, %ld", image_handle, map_key); + + /* 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_UNSUPPORTED; + 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[] = { 'U','-','b','o','o','t',0 }; +struct efi_system_table systab = { + .hdr = { + .signature = EFI_SYSTEM_TABLE_SIGNATURE, + .revision = 0x20000, /* 2.0 */ + .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, +};

Am 22.12.2015 um 14:57 schrieb Alexander Graf:
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c new file mode 100644 index 0000000..ed95962 --- /dev/null +++ b/lib/efi_loader/efi_boottime.c
[...]
+/*
- The "gd" pointer lives in a register on ARM and AArch64 that we declare
- fixed when compiling U-Boot. However, the payload does now know about that
"does not"?
- 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;
[snip]
Cheers, Andreas

On 22.12.15 15:15, Andreas Färber wrote:
Am 22.12.2015 um 14:57 schrieb Alexander Graf:
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c new file mode 100644 index 0000000..ed95962 --- /dev/null +++ b/lib/efi_loader/efi_boottime.c
[...]
+/*
- The "gd" pointer lives in a register on ARM and AArch64 that we declare
- fixed when compiling U-Boot. However, the payload does now know about that
"does not"?
Yup, fixed for v2, thanks :).
Alex

On Tue, Dec 22, 2015 at 02:57:51PM +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.
One overall observation, and I may help track these down - but not all for this review: this code uses EFI_UNSUPPORTED as a default "something went wrong" error code, but this is not actually supported by the specification. I'm pointing out a few of these, but it would be preferable if we could crowdsource this a bit since there are quire a few instances...
Signed-off-by: Alexander Graf agraf@suse.de
include/efi_loader.h | 41 +++ lib/efi_loader/efi_boottime.c | 838 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 879 insertions(+) create mode 100644 lib/efi_loader/efi_boottime.c
diff --git a/include/efi_loader.h b/include/efi_loader.h index da82354..ed7c389 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -24,14 +24,55 @@ #include <efi_api.h> #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); diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c new file mode 100644 index 0000000..ed95962 --- /dev/null +++ b/lib/efi_loader/efi_boottime.c @@ -0,0 +1,838 @@ +/*
- EFI application boot time services
- Copyright (c) 2015 Alexander Graf
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- SPDX-License-Identifier: LGPL-2.1+
- */
+#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;
+/*
- 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 now 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) +{
- 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);
Not always a legal return status.
+}
+static unsigned long efi_raise_tpl(unsigned long new_tpl) +{
- EFI_ENTRY("0x%lx", new_tpl);
- return EFI_EXIT(efi_unsupported(__func__));
"Unlike other UEFI interface functions, EFI_BOOT_SERVICES.RaiseTPL() does not return a status code. Instead, it returns the previous task priority level, which is to be restored later with a matching call to RestoreTPL()."
+}
+static void efi_restore_tpl(unsigned long old_tpl) +{
- EFI_ENTRY("0x%lx", old_tpl);
- EFI_EXIT(efi_unsupported(__func__));
(void function, nothing to return)
+}
+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);
- }
+}
+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_UNSUPPORTED;
EFI_OUT_OF_RESOURCES/EFI_NOT_FOUND?
break;
- case 2:
/* Exact address, grant it. The addr is already in *memory. */
As far as I can tell, this is why GRUB works. Because it filters through the memory map manually, requesting to allocate its heap at an exact address in a region of free memory in the UEFI memory map.
The key is that EFI_LOADER_MEMORY will be used by applications loaded as well as by U-Boot to load applications into. A simple example where this could be problematic would be a large(ish) initrd loaded via initrd= on kernel (stub loader) command line rather than via GRUB.
break;
- default:
It would actually be fair here to state that the above are the only types supported by the UEFI specification, as opposed to not being implemented.
r = EFI_UNSUPPORTED;
Actually, not a valid return value. 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);
+}
+/*
- 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,
},
{
/* 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);
- 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) */
- efi_memory_map[0].num_pages = gd->relocaddr >> 12;
U-Boot question: is gd->relocaddr always the offset from start of RAM? How does this work with gaps in memory map?
+#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;
#else #error "..." ?
+#endif
- /* Remove U-Boot from the available RAM view */
- efi_memory_map[0].num_pages -= gd->mon_len >> 12;
- /* Remove the malloc area from the available RAM view */
- efi_memory_map[0].num_pages -= TOTAL_MALLOC_LEN >> 12;
- /* 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) +{
- 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_UNSUPPORTED);
EFI_OUT_OF_RESOURCES would be a better return value here.
- }
- 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;
Add a warning message if this limit is exceeded?
- 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_UNSUPPORTED);
This function should only ever be called with an event successfully created via create_event (and stored into efi_event). If we're called with another event handle, EFI_INVALID_PARAMETER is the appropriate error code.
- }
- 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;
- /* 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 efi_status_t efi_exit_boot_services(void *image_handle,
unsigned long map_key)
+{
- EFI_ENTRY("%p, %ld", image_handle, map_key);
- /* 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_UNSUPPORTED;
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;
(Correct use of 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[] = { 'U','-','b','o','o','t',0 };
Surely, if we're being formal, that should be 'D', 'a', 's', ' ', ... :)
+struct efi_system_table systab = {
- .hdr = {
.signature = EFI_SYSTEM_TABLE_SIGNATURE,
.revision = 0x20000, /* 2.0 */
Really, this should claim to support revision 2.5, if not 2.6 (soon to be released). AArch64 support was only introduced in 2.4.
.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

On 26.12.15 19:09, Leif Lindholm wrote:
On Tue, Dec 22, 2015 at 02:57:51PM +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.
One overall observation, and I may help track these down - but not all for this review: this code uses EFI_UNSUPPORTED as a default "something went wrong" error code, but this is not actually supported by the specification. I'm pointing out a few of these, but it would be preferable if we could crowdsource this a bit since there are quire a few instances...
Signed-off-by: Alexander Graf agraf@suse.de
include/efi_loader.h | 41 +++ lib/efi_loader/efi_boottime.c | 838 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 879 insertions(+) create mode 100644 lib/efi_loader/efi_boottime.c
diff --git a/include/efi_loader.h b/include/efi_loader.h index da82354..ed7c389 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -24,14 +24,55 @@ #include <efi_api.h> #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); diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c new file mode 100644 index 0000000..ed95962 --- /dev/null +++ b/lib/efi_loader/efi_boottime.c @@ -0,0 +1,838 @@ +/*
- EFI application boot time services
- Copyright (c) 2015 Alexander Graf
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- SPDX-License-Identifier: LGPL-2.1+
- */
+#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;
+/*
- 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 now 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) +{
- 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);
Not always a legal return status.
+}
+static unsigned long efi_raise_tpl(unsigned long new_tpl) +{
- EFI_ENTRY("0x%lx", new_tpl);
- return EFI_EXIT(efi_unsupported(__func__));
"Unlike other UEFI interface functions, EFI_BOOT_SERVICES.RaiseTPL() does not return a status code. Instead, it returns the previous task priority level, which is to be restored later with a matching call to RestoreTPL()."
Since we don't do TPLs (or IRQs for that matter), I'll just return 0 here.
+}
+static void efi_restore_tpl(unsigned long old_tpl) +{
- EFI_ENTRY("0x%lx", old_tpl);
- EFI_EXIT(efi_unsupported(__func__));
(void function, nothing to return)
Yes, hence no return. EFI_EXIT deals with the gd swapping and efi_unsupported() gives me a nice debug message :).
+}
+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);
- }
+}
+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_UNSUPPORTED;
EFI_OUT_OF_RESOURCES/EFI_NOT_FOUND?
break;
- case 2:
/* Exact address, grant it. The addr is already in *memory. */
As far as I can tell, this is why GRUB works. Because it filters through the memory map manually, requesting to allocate its heap at an exact address in a region of free memory in the UEFI memory map.
Yes.
The key is that EFI_LOADER_MEMORY will be used by applications loaded as well as by U-Boot to load applications into. A simple example where this could be problematic would be a large(ish) initrd loaded via initrd= on kernel (stub loader) command line rather than via GRUB.
Ah, so here the 128MB limit on the LOADER_DATA section might bite us?
break;
- default:
It would actually be fair here to state that the above are the only types supported by the UEFI specification, as opposed to not being implemented.
r = EFI_UNSUPPORTED;
Actually, not a valid return value. 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);
+}
+/*
- 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,
},
{
/* 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);
- 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) */
- efi_memory_map[0].num_pages = gd->relocaddr >> 12;
U-Boot question: is gd->relocaddr always the offset from start of RAM? How does this work with gaps in memory map?
U-Boot always relocates itself at TOM (or at least what we consider TOM here). gd->relocaddr is the physical address of the start of U-Boot which is right below TOM.
+#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;
#else #error "..." ?
If it's not defined, it's 0 :).
+#endif
- /* Remove U-Boot from the available RAM view */
- efi_memory_map[0].num_pages -= gd->mon_len >> 12;
- /* Remove the malloc area from the available RAM view */
- efi_memory_map[0].num_pages -= TOTAL_MALLOC_LEN >> 12;
- /* 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) +{
- 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_UNSUPPORTED);
EFI_OUT_OF_RESOURCES would be a better return value here.
Yup.
- }
- 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;
Add a warning message if this limit is exceeded?
ok
- 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_UNSUPPORTED);
This function should only ever be called with an event successfully created via create_event (and stored into efi_event). If we're called with another event handle, EFI_INVALID_PARAMETER is the appropriate error code.
Sounds reasonable.
- }
- 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 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[] = { 'U','-','b','o','o','t',0 };
Surely, if we're being formal, that should be 'D', 'a', 's', ' ', ... :)
Heh :) Sure!
+struct efi_system_table systab = {
- .hdr = {
.signature = EFI_SYSTEM_TABLE_SIGNATURE,
.revision = 0x20000, /* 2.0 */
Really, this should claim to support revision 2.5, if not 2.6 (soon to be released). AArch64 support was only introduced in 2.4.
Works for me :).
Alex

On Fri, Jan 15, 2016 at 01:13:15AM +0100, Alexander Graf wrote:
On 26.12.15 19:09, Leif Lindholm wrote:
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c new file mode 100644 index 0000000..ed95962 --- /dev/null +++ b/lib/efi_loader/efi_boottime.c @@ -0,0 +1,838 @@ +/*
- EFI application boot time services
...
+static unsigned long efi_raise_tpl(unsigned long new_tpl) +{
- EFI_ENTRY("0x%lx", new_tpl);
- return EFI_EXIT(efi_unsupported(__func__));
"Unlike other UEFI interface functions, EFI_BOOT_SERVICES.RaiseTPL() does not return a status code. Instead, it returns the previous task priority level, which is to be restored later with a matching call to RestoreTPL()."
Since we don't do TPLs (or IRQs for that matter), I'll just return 0 here.
Sure.
+}
+static void efi_restore_tpl(unsigned long old_tpl) +{
- EFI_ENTRY("0x%lx", old_tpl);
- EFI_EXIT(efi_unsupported(__func__));
(void function, nothing to return)
Yes, hence no return. EFI_EXIT deals with the gd swapping and efi_unsupported() gives me a nice debug message :).
Ah, ok.
+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_UNSUPPORTED;
EFI_OUT_OF_RESOURCES/EFI_NOT_FOUND?
break;
- case 2:
/* Exact address, grant it. The addr is already in *memory. */
As far as I can tell, this is why GRUB works. Because it filters through the memory map manually, requesting to allocate its heap at an exact address in a region of free memory in the UEFI memory map.
Yes.
The key is that EFI_LOADER_MEMORY will be used by applications loaded as well as by U-Boot to load applications into. A simple example where this could be problematic would be a large(ish) initrd loaded via initrd= on kernel (stub loader) command line rather than via GRUB.
Ah, so here the 128MB limit on the LOADER_DATA section might bite us?
Yeah, I think so.
- 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) */
- efi_memory_map[0].num_pages = gd->relocaddr >> 12;
U-Boot question: is gd->relocaddr always the offset from start of RAM? How does this work with gaps in memory map?
U-Boot always relocates itself at TOM (or at least what we consider TOM here). gd->relocaddr is the physical address of the start of U-Boot which is right below TOM.
Still ... would need additional handling if memory has holes (like, fitted with multiple DIMMs smaller than the individual memory window reserved for them). Or even on something like Juno, which has 2GB of RAM at 0x00_8000_0000, and a further 6GB at 0x08_8000_0000 (I think).
+#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;
#else #error "..." ?
If it's not defined, it's 0 :).
Make it #if CONFIG_SYS_SDRAM_BASE != 0 for clarity?
/ Leif

On 15.01.16 14:02, Leif Lindholm wrote:
On Fri, Jan 15, 2016 at 01:13:15AM +0100, Alexander Graf wrote:
On 26.12.15 19:09, Leif Lindholm wrote:
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c new file mode 100644 index 0000000..ed95962 --- /dev/null +++ b/lib/efi_loader/efi_boottime.c @@ -0,0 +1,838 @@ +/*
- EFI application boot time services
...
+static unsigned long efi_raise_tpl(unsigned long new_tpl) +{
- EFI_ENTRY("0x%lx", new_tpl);
- return EFI_EXIT(efi_unsupported(__func__));
"Unlike other UEFI interface functions, EFI_BOOT_SERVICES.RaiseTPL() does not return a status code. Instead, it returns the previous task priority level, which is to be restored later with a matching call to RestoreTPL()."
Since we don't do TPLs (or IRQs for that matter), I'll just return 0 here.
Sure.
+}
+static void efi_restore_tpl(unsigned long old_tpl) +{
- EFI_ENTRY("0x%lx", old_tpl);
- EFI_EXIT(efi_unsupported(__func__));
(void function, nothing to return)
Yes, hence no return. EFI_EXIT deals with the gd swapping and efi_unsupported() gives me a nice debug message :).
Ah, ok.
+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_UNSUPPORTED;
EFI_OUT_OF_RESOURCES/EFI_NOT_FOUND?
break;
- case 2:
/* Exact address, grant it. The addr is already in *memory. */
As far as I can tell, this is why GRUB works. Because it filters through the memory map manually, requesting to allocate its heap at an exact address in a region of free memory in the UEFI memory map.
Yes.
The key is that EFI_LOADER_MEMORY will be used by applications loaded as well as by U-Boot to load applications into. A simple example where this could be problematic would be a large(ish) initrd loaded via initrd= on kernel (stub loader) command line rather than via GRUB.
Ah, so here the 128MB limit on the LOADER_DATA section might bite us?
Yeah, I think so.
- 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) */
- efi_memory_map[0].num_pages = gd->relocaddr >> 12;
U-Boot question: is gd->relocaddr always the offset from start of RAM? How does this work with gaps in memory map?
U-Boot always relocates itself at TOM (or at least what we consider TOM here). gd->relocaddr is the physical address of the start of U-Boot which is right below TOM.
Still ... would need additional handling if memory has holes (like, fitted with multiple DIMMs smaller than the individual memory window reserved for them). Or even on something like Juno, which has 2GB of RAM at 0x00_8000_0000, and a further 6GB at 0x08_8000_0000 (I think).
Yes. I think we'll have to just implement that when we hit the first board that does this (and has awareness in U-Boot for it).
With the UEFI entry path, Linux will ignore memory nodes in dt, right?
+#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;
#else #error "..." ?
If it's not defined, it's 0 :).
Make it #if CONFIG_SYS_SDRAM_BASE != 0 for clarity?
I really don't think we'll make it more readable with more #iffery here :).
Alex

On Fri, Jan 15, 2016 at 03:14:54PM +0100, Alexander Graf wrote:
On 15.01.16 14:02, Leif Lindholm wrote:
U-Boot question: is gd->relocaddr always the offset from start of RAM? How does this work with gaps in memory map?
U-Boot always relocates itself at TOM (or at least what we consider TOM here). gd->relocaddr is the physical address of the start of U-Boot which is right below TOM.
Still ... would need additional handling if memory has holes (like, fitted with multiple DIMMs smaller than the individual memory window reserved for them). Or even on something like Juno, which has 2GB of RAM at 0x00_8000_0000, and a further 6GB at 0x08_8000_0000 (I think).
Yes. I think we'll have to just implement that when we hit the first board that does this (and has awareness in U-Boot for it).
Yeah, sure.
With the UEFI entry path, Linux will ignore memory nodes in dt, right?
Yep.
+#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;
#else #error "..." ?
If it's not defined, it's 0 :).
Make it #if CONFIG_SYS_SDRAM_BASE != 0 for clarity?
I really don't think we'll make it more readable with more #iffery here :).
It isn't really the kind of thing I'm here to review, but ... Current version requires knowledge of state of certain macros, the alternative is explicit without familiarity with the codebase.
/ Leif

On 15.01.16 15:21, Leif Lindholm wrote:
On Fri, Jan 15, 2016 at 03:14:54PM +0100, Alexander Graf wrote:
On 15.01.16 14:02, Leif Lindholm wrote:
U-Boot question: is gd->relocaddr always the offset from start of RAM? How does this work with gaps in memory map?
U-Boot always relocates itself at TOM (or at least what we consider TOM here). gd->relocaddr is the physical address of the start of U-Boot which is right below TOM.
Still ... would need additional handling if memory has holes (like, fitted with multiple DIMMs smaller than the individual memory window reserved for them). Or even on something like Juno, which has 2GB of RAM at 0x00_8000_0000, and a further 6GB at 0x08_8000_0000 (I think).
Yes. I think we'll have to just implement that when we hit the first board that does this (and has awareness in U-Boot for it).
Yeah, sure.
With the UEFI entry path, Linux will ignore memory nodes in dt, right?
Yep.
+#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;
#else #error "..." ?
If it's not defined, it's 0 :).
Make it #if CONFIG_SYS_SDRAM_BASE != 0 for clarity?
I really don't think we'll make it more readable with more #iffery here :).
It isn't really the kind of thing I'm here to review, but ... Current version requires knowledge of state of certain macros, the alternative is explicit without familiarity with the codebase.
Hrm. So how about a comment like this:
/* RAM starts at an offset above 0 */
right before the #ifdef?
Alex

On 26.12.15 19:09, Leif Lindholm wrote:
On Tue, Dec 22, 2015 at 02:57:51PM +0100, Alexander Graf wrote:
[...]
break;
- case 2:
/* Exact address, grant it. The addr is already in *memory. */
As far as I can tell, this is why GRUB works. Because it filters through the memory map manually, requesting to allocate its heap at an exact address in a region of free memory in the UEFI memory map.
The key is that EFI_LOADER_MEMORY will be used by applications loaded as well as by U-Boot to load applications into. A simple example where this could be problematic would be a large(ish) initrd loaded via initrd= on kernel (stub loader) command line rather than via GRUB.
Thinking about this once more, we don't expose any file system interfaces to EFI applications, so initrd= won't work anyway. That means the only viable path to go right now is via grub which means we shouldn't get into memory contention with 128MB LOADER_DATA.
Alex

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 --- include/efi_loader.h | 5 + lib/efi_loader/efi_console.c | 371 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 376 insertions(+) create mode 100644 lib/efi_loader/efi_console.c
diff --git a/include/efi_loader.h b/include/efi_loader.h index ed7c389..7fb2106 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -41,6 +41,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..45112e7 --- /dev/null +++ b/lib/efi_loader/efi_console.c @@ -0,0 +1,371 @@ +/* + * EFI application console interface + * + * Copyright (c) 2015 Alexander Graf + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#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, +};

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.
However, since U-Boot has no notion of RTS, we just create an extremely minimal RTS stub that just declares all functions as unsupported. We could in the future map U-boot environment variables to EFI variables here.
Signed-off-by: Alexander Graf agraf@suse.de --- arch/arm/cpu/armv8/u-boot.lds | 8 ++++++ arch/arm/cpu/u-boot.lds | 13 ++++++++++ arch/arm/lib/sections.c | 2 ++ include/efi_loader.h | 3 +++ lib/efi_loader/efi_runtime.c | 59 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 85 insertions(+) create mode 100644 lib/efi_loader/efi_runtime.c
diff --git a/arch/arm/cpu/armv8/u-boot.lds b/arch/arm/cpu/armv8/u-boot.lds index 4c12222..7c5b032 100644 --- a/arch/arm/cpu/armv8/u-boot.lds +++ b/arch/arm/cpu/armv8/u-boot.lds @@ -42,6 +42,14 @@ SECTIONS
. = ALIGN(8);
+ .efi_runtime : { + __efi_runtime_start = .; + *(efi_runtime) + __efi_runtime_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..b5198d0 100644 --- a/arch/arm/cpu/u-boot.lds +++ b/arch/arm/cpu/u-boot.lds @@ -89,6 +89,19 @@ SECTIONS
. = ALIGN(4);
+ .__efi_runtime_start : { + *(.__efi_runtime_start) + } + + .efi_runtime : { + *(efi_runtime) + } + + .__efi_runtime_stop : { + *(.__efi_runtime_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..21b3066 100644 --- a/arch/arm/lib/sections.c +++ b/arch/arm/lib/sections.c @@ -27,4 +27,6 @@ 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 _end[0] __attribute__((section(".__end"))); diff --git a/include/efi_loader.h b/include/efi_loader.h index 7fb2106..af1c88f 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -39,6 +39,7 @@
#define EFI_EXIT(ret) efi_exit_func(ret);
+extern const struct efi_runtime_services efi_runtime_services; extern struct efi_system_table systab;
extern const struct efi_simple_text_output_protocol efi_con_out; @@ -49,6 +50,8 @@ 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; + struct efi_class_map { const efi_guid_t *guid; const void *interface; diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c new file mode 100644 index 0000000..214e1f5 --- /dev/null +++ b/lib/efi_loader/efi_runtime.c @@ -0,0 +1,59 @@ +/* + * EFI application runtime services + * + * Copyright (c) 2015 Alexander Graf + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include <common.h> +#include <efi_loader.h> + +/* + * EFI Runtime code is still alive when U-Boot is long overwritten. To isolate + * this code 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 at runtime. + * Please keep it fully self-contained. + */ +asm(".section efi_runtime,"a""); + +static efi_status_t efi_unimplemented(void) +{ + return EFI_UNSUPPORTED; +} + +const struct efi_runtime_services efi_runtime_services = { + .hdr = { + .signature = EFI_RUNTIME_SERVICES_SIGNATURE, + .revision = EFI_RUNTIME_SERVICES_REVISION, + .headersize = sizeof(struct efi_table_hdr), + }, + .get_time = (void *)&efi_unimplemented, + .set_time = (void *)&efi_unimplemented, + .get_wakeup_time = (void *)&efi_unimplemented, + .set_wakeup_time = (void *)&efi_unimplemented, + .set_virtual_address_map = (void *)&efi_unimplemented, + .convert_pointer = (void *)&efi_unimplemented, + .get_variable = (void *)&efi_unimplemented, + .get_next_variable = (void *)&efi_unimplemented, + .set_variable = (void *)&efi_unimplemented, + .get_next_high_mono_count = (void *)&efi_unimplemented, + .reset_system = (void *)&efi_unimplemented, +};

On Tue, Dec 22, 2015 at 02:57:53PM +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.
However, since U-Boot has no notion of RTS, we just create an extremely minimal RTS stub that just declares all functions as unsupported. We could in the future map U-boot environment variables to EFI variables here.
Signed-off-by: Alexander Graf agraf@suse.de
arch/arm/cpu/armv8/u-boot.lds | 8 ++++++ arch/arm/cpu/u-boot.lds | 13 ++++++++++ arch/arm/lib/sections.c | 2 ++ include/efi_loader.h | 3 +++ lib/efi_loader/efi_runtime.c | 59 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 85 insertions(+) create mode 100644 lib/efi_loader/efi_runtime.c
diff --git a/arch/arm/cpu/armv8/u-boot.lds b/arch/arm/cpu/armv8/u-boot.lds index 4c12222..7c5b032 100644 --- a/arch/arm/cpu/armv8/u-boot.lds +++ b/arch/arm/cpu/armv8/u-boot.lds @@ -42,6 +42,14 @@ SECTIONS
. = ALIGN(8);
- .efi_runtime : {
__efi_runtime_start = .;
*(efi_runtime)
__efi_runtime_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..b5198d0 100644 --- a/arch/arm/cpu/u-boot.lds +++ b/arch/arm/cpu/u-boot.lds @@ -89,6 +89,19 @@ SECTIONS
. = ALIGN(4);
- .__efi_runtime_start : {
*(.__efi_runtime_start)
- }
- .efi_runtime : {
*(efi_runtime)
- }
- .__efi_runtime_stop : {
*(.__efi_runtime_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..21b3066 100644 --- a/arch/arm/lib/sections.c +++ b/arch/arm/lib/sections.c @@ -27,4 +27,6 @@ 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 _end[0] __attribute__((section(".__end"))); diff --git a/include/efi_loader.h b/include/efi_loader.h index 7fb2106..af1c88f 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -39,6 +39,7 @@
#define EFI_EXIT(ret) efi_exit_func(ret);
+extern const struct efi_runtime_services efi_runtime_services; extern struct efi_system_table systab;
extern const struct efi_simple_text_output_protocol efi_con_out; @@ -49,6 +50,8 @@ 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;
struct efi_class_map { const efi_guid_t *guid; const void *interface; diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c new file mode 100644 index 0000000..214e1f5 --- /dev/null +++ b/lib/efi_loader/efi_runtime.c @@ -0,0 +1,59 @@ +/*
- EFI application runtime services
- Copyright (c) 2015 Alexander Graf
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- SPDX-License-Identifier: LGPL-2.1+
- */
+#include <common.h> +#include <efi_loader.h>
+/*
- EFI Runtime code is still alive when U-Boot is long overwritten. To isolate
- this code 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 at runtime.
- Please keep it fully self-contained.
- */
+asm(".section efi_runtime,"a"");
+static efi_status_t efi_unimplemented(void) +{
- return EFI_UNSUPPORTED;
Again, EFI_UNSUPPORTED is not necessarily a valid return value for all runtime services.
+}
+const struct efi_runtime_services efi_runtime_services = {
- .hdr = {
.signature = EFI_RUNTIME_SERVICES_SIGNATURE,
.revision = EFI_RUNTIME_SERVICES_REVISION,
.headersize = sizeof(struct efi_table_hdr),
- },
- .get_time = (void *)&efi_unimplemented,
EFI_DEVICE_ERROR
- .set_time = (void *)&efi_unimplemented,
EFI_DEVICE_ERROR
- .get_wakeup_time = (void *)&efi_unimplemented,
- .set_wakeup_time = (void *)&efi_unimplemented,
Both of these are fine, and correct, to return EFI_UNSUPPORTED.
- .set_virtual_address_map = (void *)&efi_unimplemented,
- .convert_pointer = (void *)&efi_unimplemented,
There really isn't a way to gracefully decline these two functions. All valid error codes refer to invalid inputs.
- .get_variable = (void *)&efi_unimplemented,
EFI_DEVICE_ERROR would probably be the closest thing to a correct return code in this instance.
- .get_next_variable = (void *)&efi_unimplemented,
(get_next_variable_name?) Again, EFI_DEVICE_ERROR, is probably the least wrong return value.
- .set_variable = (void *)&efi_unimplemented,
EFI_DEVICE_ERROR
- .get_next_high_mono_count = (void *)&efi_unimplemented,
EFI_DEVICE_ERROR
- .reset_system = (void *)&efi_unimplemented,
"The ResetSystem() function does not return."
+};
2.1.4

On 26.12.15 19:33, Leif Lindholm wrote:
On Tue, Dec 22, 2015 at 02:57:53PM +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.
However, since U-Boot has no notion of RTS, we just create an extremely minimal RTS stub that just declares all functions as unsupported. We could in the future map U-boot environment variables to EFI variables here.
Signed-off-by: Alexander Graf agraf@suse.de
arch/arm/cpu/armv8/u-boot.lds | 8 ++++++ arch/arm/cpu/u-boot.lds | 13 ++++++++++ arch/arm/lib/sections.c | 2 ++ include/efi_loader.h | 3 +++ lib/efi_loader/efi_runtime.c | 59 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 85 insertions(+) create mode 100644 lib/efi_loader/efi_runtime.c
diff --git a/arch/arm/cpu/armv8/u-boot.lds b/arch/arm/cpu/armv8/u-boot.lds index 4c12222..7c5b032 100644 --- a/arch/arm/cpu/armv8/u-boot.lds +++ b/arch/arm/cpu/armv8/u-boot.lds @@ -42,6 +42,14 @@ SECTIONS
. = ALIGN(8);
- .efi_runtime : {
__efi_runtime_start = .;
*(efi_runtime)
__efi_runtime_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..b5198d0 100644 --- a/arch/arm/cpu/u-boot.lds +++ b/arch/arm/cpu/u-boot.lds @@ -89,6 +89,19 @@ SECTIONS
. = ALIGN(4);
- .__efi_runtime_start : {
*(.__efi_runtime_start)
- }
- .efi_runtime : {
*(efi_runtime)
- }
- .__efi_runtime_stop : {
*(.__efi_runtime_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..21b3066 100644 --- a/arch/arm/lib/sections.c +++ b/arch/arm/lib/sections.c @@ -27,4 +27,6 @@ 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 _end[0] __attribute__((section(".__end"))); diff --git a/include/efi_loader.h b/include/efi_loader.h index 7fb2106..af1c88f 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -39,6 +39,7 @@
#define EFI_EXIT(ret) efi_exit_func(ret);
+extern const struct efi_runtime_services efi_runtime_services; extern struct efi_system_table systab;
extern const struct efi_simple_text_output_protocol efi_con_out; @@ -49,6 +50,8 @@ 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;
struct efi_class_map { const efi_guid_t *guid; const void *interface; diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c new file mode 100644 index 0000000..214e1f5 --- /dev/null +++ b/lib/efi_loader/efi_runtime.c @@ -0,0 +1,59 @@ +/*
- EFI application runtime services
- Copyright (c) 2015 Alexander Graf
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- SPDX-License-Identifier: LGPL-2.1+
- */
+#include <common.h> +#include <efi_loader.h>
+/*
- EFI Runtime code is still alive when U-Boot is long overwritten. To isolate
- this code 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 at runtime.
- Please keep it fully self-contained.
- */
+asm(".section efi_runtime,"a"");
+static efi_status_t efi_unimplemented(void) +{
- return EFI_UNSUPPORTED;
Again, EFI_UNSUPPORTED is not necessarily a valid return value for all runtime services.
+}
+const struct efi_runtime_services efi_runtime_services = {
- .hdr = {
.signature = EFI_RUNTIME_SERVICES_SIGNATURE,
.revision = EFI_RUNTIME_SERVICES_REVISION,
.headersize = sizeof(struct efi_table_hdr),
- },
- .get_time = (void *)&efi_unimplemented,
EFI_DEVICE_ERROR
- .set_time = (void *)&efi_unimplemented,
EFI_DEVICE_ERROR
- .get_wakeup_time = (void *)&efi_unimplemented,
- .set_wakeup_time = (void *)&efi_unimplemented,
Both of these are fine, and correct, to return EFI_UNSUPPORTED.
- .set_virtual_address_map = (void *)&efi_unimplemented,
- .convert_pointer = (void *)&efi_unimplemented,
There really isn't a way to gracefully decline these two functions. All valid error codes refer to invalid inputs.
Ok, changing to EFI_INVALID_PARAMETER then :).
- .get_variable = (void *)&efi_unimplemented,
EFI_DEVICE_ERROR would probably be the closest thing to a correct return code in this instance.
- .get_next_variable = (void *)&efi_unimplemented,
(get_next_variable_name?)
git blame says it's Simon's fault :).
867a6ac8 (Simon Glass 2015-07-31 09:31:36 -0600 170) efi_status_t (EFIAPI *get_next_variable)( 867a6ac8 (Simon Glass 2015-07-31 09:31:36 -0600 171) unsigned long *variable_name_size, 867a6ac8 (Simon Glass 2015-07-31 09:31:36 -0600 172) s16 *variable_name, efi_guid_t *vendor);
Again, EFI_DEVICE_ERROR, is probably the least wrong return value.
- .set_variable = (void *)&efi_unimplemented,
EFI_DEVICE_ERROR
- .get_next_high_mono_count = (void *)&efi_unimplemented,
EFI_DEVICE_ERROR
Ok, fixed all of the above.
- .reset_system = (void *)&efi_unimplemented,
"The ResetSystem() function does not return."
Hrm, I think returning EFI_UNSUPPORTED is still better than while(1) { }. With the return an OS at least has the chance to fix things up itself.
Alex

On Fri, Jan 15, 2016 at 01:26:42AM +0100, Alexander Graf wrote:
On 26.12.15 19:33, Leif Lindholm wrote:
- .reset_system = (void *)&efi_unimplemented,
"The ResetSystem() function does not return."
Hrm, I think returning EFI_UNSUPPORTED is still better than while(1) { }. With the return an OS at least has the chance to fix things up itself.
I'm not saying it isn't better, I'm saying it's not compliant - there is no valid return value. I would prefer simply having the pointer set to NULL.
/ Leif

On 15.01.16 14:52, Leif Lindholm wrote:
On Fri, Jan 15, 2016 at 01:26:42AM +0100, Alexander Graf wrote:
On 26.12.15 19:33, Leif Lindholm wrote:
- .reset_system = (void *)&efi_unimplemented,
"The ResetSystem() function does not return."
Hrm, I think returning EFI_UNSUPPORTED is still better than while(1) { }. With the return an OS at least has the chance to fix things up itself.
I'm not saying it isn't better, I'm saying it's not compliant - there is no valid return value. I would prefer simply having the pointer set to NULL.
Is a NULL function pointer valid here?
Alex

On Fri, Jan 15, 2016 at 03:15:37PM +0100, Alexander Graf wrote:
On 15.01.16 14:52, Leif Lindholm wrote:
"The ResetSystem() function does not return."
Hrm, I think returning EFI_UNSUPPORTED is still better than while(1) { }. With the return an OS at least has the chance to fix things up itself.
I'm not saying it isn't better, I'm saying it's not compliant - there is no valid return value. I would prefer simply having the pointer set to NULL.
Is a NULL function pointer valid here?
More than returning is.
/ Leif

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 --- include/efi_loader.h | 1 + lib/efi_loader/efi_disk.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 228 insertions(+) create mode 100644 lib/efi_loader/efi_disk.c
diff --git a/include/efi_loader.h b/include/efi_loader.h index af1c88f..7e821e5 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -72,6 +72,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..1804e3e --- /dev/null +++ b/lib/efi_loader/efi_disk.c @@ -0,0 +1,227 @@ +/* + * EFI application disk support + * + * Copyright (c) 2015 Alexander Graf + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include <common.h> +#include <efi_loader.h> +#include <part.h> +#include <malloc.h> +#include <inttypes.h> + +static const efi_guid_t efi_block_io_guid = BLOCK_IO_GUID; + +struct efi_disk_obj { + struct efi_object parent; + struct efi_block_io ops; + const char *ifname; + int dev_index; + struct efi_block_io_media media; + 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 char **cur_drvr; + int i; + int disks = 0; + + /* Search for all available disk devices */ + for (cur_drvr = available_block_drvrs; *cur_drvr; cur_drvr++) { + printf("Scanning disks on %s...\n", *cur_drvr); + 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, i); + if (!desc) + continue; + + diskobj = malloc(objlen); + memset(diskobj, 0, 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; + 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, 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; +}

Hi Alexander,
On 22 December 2015 at 06:57, 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
include/efi_loader.h | 1 + lib/efi_loader/efi_disk.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 228 insertions(+) create mode 100644 lib/efi_loader/efi_disk.c
Reviewed-by: Simon Glass sjg@chromium.org
Some nits below.
diff --git a/include/efi_loader.h b/include/efi_loader.h index af1c88f..7e821e5 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -72,6 +72,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..1804e3e --- /dev/null +++ b/lib/efi_loader/efi_disk.c @@ -0,0 +1,227 @@ +/*
- EFI application disk support
- Copyright (c) 2015 Alexander Graf
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- SPDX-License-Identifier: LGPL-2.1+
Can we have GPL 2 / 2+? Also drop the text?
- */
+#include <common.h> +#include <efi_loader.h> +#include <part.h> +#include <malloc.h> +#include <inttypes.h>
inttypes.h should go above part.h
+static const efi_guid_t efi_block_io_guid = BLOCK_IO_GUID;
+struct efi_disk_obj {
Could use comments on these members
struct efi_object parent;
struct efi_block_io ops;
const char *ifname;
int dev_index;
struct efi_block_io_media media;
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 char **cur_drvr;
int i;
int disks = 0;
/* Search for all available disk devices */
for (cur_drvr = available_block_drvrs; *cur_drvr; cur_drvr++) {
printf("Scanning disks on %s...\n", *cur_drvr);
for (i = 0; i < 4; i++) {
What is 4 for?
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, i);
if (!desc)
continue;
diskobj = malloc(objlen);
Could use calloc() to avoid memset() if you like.
memset(diskobj, 0, 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;
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, 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
Regards, Simon

On 15.01.16 02:37, Simon Glass wrote:
Hi Alexander,
On 22 December 2015 at 06:57, 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
include/efi_loader.h | 1 + lib/efi_loader/efi_disk.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 228 insertions(+) create mode 100644 lib/efi_loader/efi_disk.c
Reviewed-by: Simon Glass sjg@chromium.org
Some nits below.
diff --git a/include/efi_loader.h b/include/efi_loader.h index af1c88f..7e821e5 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -72,6 +72,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..1804e3e --- /dev/null +++ b/lib/efi_loader/efi_disk.c @@ -0,0 +1,227 @@ +/*
- EFI application disk support
- Copyright (c) 2015 Alexander Graf
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- SPDX-License-Identifier: LGPL-2.1+
Can we have GPL 2 / 2+? Also drop the text?
- */
+#include <common.h> +#include <efi_loader.h> +#include <part.h> +#include <malloc.h> +#include <inttypes.h>
inttypes.h should go above part.h
+static const efi_guid_t efi_block_io_guid = BLOCK_IO_GUID;
+struct efi_disk_obj {
Could use comments on these members
struct efi_object parent;
struct efi_block_io ops;
const char *ifname;
int dev_index;
struct efi_block_io_media media;
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 char **cur_drvr;
int i;
int disks = 0;
/* Search for all available disk devices */
for (cur_drvr = available_block_drvrs; *cur_drvr; cur_drvr++) {
printf("Scanning disks on %s...\n", *cur_drvr);
for (i = 0; i < 4; i++) {
What is 4 for?
4 disks should be enough for everyone ;).
I couldn't find an easy way to limit the number of devices we have to scan. You may have 4 MMC slots, but only slot 3 is actually populated. How do you know how many there really are?
So we have to use some upper bound for the scan. The 4 is simply because most devices I'm aware of that U-Boot runs on don't have much more than 4 devices per block bus attached to them. I had 128 in here first, but then I ran into endless timeouts with MMC scans.
Thanks a bunch for the review :)
Alex

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 --- common/Makefile | 1 + common/cmd_bootefi.c | 168 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 169 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..8d872d0 --- /dev/null +++ b/common/cmd_bootefi.c @@ -0,0 +1,168 @@ +/* + * EFI application loader + * + * Copyright (c) 2015 Alexander Graf + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#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 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" +);

Why just not to implement standard EFI behaviour when EFI looks for boot-efi partition and proceed?
If ARM board developers will enable EFI support in the future, we can have single one JeOS having all possible dtb in KIWI image. BeagleBone Black has its own u-boot on eMMC, and the user need to push S2 button to force hardware to use our openSUSE u-boot from SD card. Maybe something like that is for other boards. If the single one required u-boot feature is to run EFI grub, then we can even don't touch preinstalled bootloader, that is not possible now, because we need our openSUSE boot scripts.
2015-12-22 16:57 GMT+03:00 Alexander Graf agraf@suse.de:
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
common/Makefile | 1 + common/cmd_bootefi.c | 168 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 169 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..8d872d0 --- /dev/null +++ b/common/cmd_bootefi.c @@ -0,0 +1,168 @@ +/*
- EFI application loader
- Copyright (c) 2015 Alexander Graf
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- SPDX-License-Identifier: LGPL-2.1+
- */
+#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 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"
+);
2.1.4

On 24.12.15 12:15, Matwey V. Kornilov wrote:
Why just not to implement standard EFI behaviour when EFI looks for boot-efi partition and proceed?
Well, what is "standard EFI behavior"?
There are 2 standard ways I'm aware of:
1) NVRAM
The default case for 99.9% of the boots on normal EFI systems is based on variables in NVRAM that tell EFI which boot device to boot from. Since we don't implement EFI variables today, we can't really make use of this feature. And because you want to change the default boot device at runtime, we'd have to have runtime services be able to modify them after exiting boot services.
2) Removable Media
There is another way implemented for "Removable Media" - mostly intended for USB sticks and the likes. Here EFI searches for a defined file name (EFI/boot/boot{arm,a64,x64}.efi) on the ESP partition and boots it.
Part 1 is very difficult to do without major rework of a few U-Boot components. If EFI becomes the de-facto standard way of booting with U-Boot, I think we'll walk down that road, but it's nothing I want to have to deal with in the initial enablement discussion.
Part 2 is easy to do. But then again it's also easy to do it using a boot script. Or a compiled in bootcmd. If it's really desired.
Which brings me to the next idea. What if we just implement exlinux.conf support for EFI binaries? Then all you need to do is have an extlinux.conf available on your generic EFI media that tells U-Boot where to load the grub binary from.
That way we wouldn't bend U-Boot completely away from its heritage, make use of its flexibility and all distributions that actually care about booting from U-Boot would easily be able to just put such a file in an rpm an install it always.
If ARM board developers will enable EFI support in the future, we can have single one JeOS having all possible dtb in KIWI image. BeagleBone Black has its own u-boot on eMMC, and the user need to push S2 button to force hardware to use our openSUSE u-boot from SD card. Maybe something like that is for other boards. If the single one required u-boot feature is to run EFI grub, then we can even don't touch preinstalled bootloader, that is not possible now, because we need our openSUSE boot scripts.
There are more things we need to solve before we can truly get to a universal booting solution. But one step at a time :).
The reason I implemented "bootefi" was really because it's the natural fit into how U-Boot handles all other formats today. I don't think this is going to be the last patch set around EFI support.
Alex

Am 25.12.2015 um 10:02 schrieb Alexander Graf: [snip]
The reason I implemented "bootefi" was really because it's the natural fit into how U-Boot handles all other formats today. I don't think this is going to be the last patch set around EFI support.
I think what Matwey was suggesting is integrating your "bootefi" into the standard "distro" boot sequence environment, so that it probes each device for an EFI binary and if it finds one runs load and bootefi, without the need for any boot.scr.
That would be a follow-up patch.
It however conflicts with your idea of having some potentially board-specific code mess with "fdt addr" command before running "bootefi".
My solution would be to give boot.scr preference over *.efi, so that the user has a way to load dtb and run "bootefi" on his own, and otherwise fall back to just "bootefi" which'll spit a warning about lack of fdt if I read that correctly.
Cheers, Andreas

2015-12-25 12:25 GMT+03:00 Andreas Färber afaerber@suse.de:
Am 25.12.2015 um 10:02 schrieb Alexander Graf: [snip]
The reason I implemented "bootefi" was really because it's the natural fit into how U-Boot handles all other formats today. I don't think this is going to be the last patch set around EFI support.
I think what Matwey was suggesting is integrating your "bootefi" into the standard "distro" boot sequence environment, so that it probes each device for an EFI binary and if it finds one runs load and bootefi, without the need for any boot.scr.
I have no problem if boot{arm,a64,x64}.efi search is implemented via standard bootscript. But I like idea about extlinux.conf too.
That would be a follow-up patch.
It however conflicts with your idea of having some potentially board-specific code mess with "fdt addr" command before running "bootefi".
My solution would be to give boot.scr preference over *.efi, so that the user has a way to load dtb and run "bootefi" on his own, and otherwise fall back to just "bootefi" which'll spit a warning about lack of fdt if I read that correctly.
Yeah, dtb should be anyhow controlled by the user to make workarounds possible. Funny story about how u-boot detects dtb: http://lists.denx.de/pipermail/u-boot/2015-December/237604.html
Cheers, Andreas
-- SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany GF: Felix Imendörffer, Jane Smithard, Graham Norton; HRB 21284 (AG Nürnberg)

On Fri, Dec 25, 2015 at 12:40:25PM +0300, Matwey V. Kornilov wrote:
2015-12-25 12:25 GMT+03:00 Andreas Färber afaerber@suse.de:
Am 25.12.2015 um 10:02 schrieb Alexander Graf: [snip]
The reason I implemented "bootefi" was really because it's the natural fit into how U-Boot handles all other formats today. I don't think this is going to be the last patch set around EFI support.
I think what Matwey was suggesting is integrating your "bootefi" into the standard "distro" boot sequence environment, so that it probes each device for an EFI binary and if it finds one runs load and bootefi, without the need for any boot.scr.
I have no problem if boot{arm,a64,x64}.efi search is implemented via standard bootscript. But I like idea about extlinux.conf too.
That would be a follow-up patch.
It however conflicts with your idea of having some potentially board-specific code mess with "fdt addr" command before running "bootefi".
My solution would be to give boot.scr preference over *.efi, so that the user has a way to load dtb and run "bootefi" on his own, and otherwise fall back to just "bootefi" which'll spit a warning about lack of fdt if I read that correctly.
Yeah, dtb should be anyhow controlled by the user to make workarounds possible. Funny story about how u-boot detects dtb: http://lists.denx.de/pipermail/u-boot/2015-December/237604.html
Yeah. And frankly that's about how it's supposed to work too. That's a very "funny" Beaglbone Black clone you found there (they neither verbatim copied the EEPROM contents nor followed the guidelines on how to get their own name in there). :)
But, in the end it's all a solvable set of problems, after the initial work is in.

On Fri, Dec 25, 2015 at 10:25:22AM +0100, Andreas Färber wrote:
Am 25.12.2015 um 10:02 schrieb Alexander Graf: [snip]
The reason I implemented "bootefi" was really because it's the natural fit into how U-Boot handles all other formats today. I don't think this is going to be the last patch set around EFI support.
I think what Matwey was suggesting is integrating your "bootefi" into the standard "distro" boot sequence environment, so that it probes each device for an EFI binary and if it finds one runs load and bootefi, without the need for any boot.scr.
That would be a follow-up patch.
It however conflicts with your idea of having some potentially board-specific code mess with "fdt addr" command before running "bootefi".
This could however be resolved by moving to a model where the device-tree was considered a component of the firmware (which is how we treat it in UEFI). If U-Boot had an awareness (if it does not already) of an FDT being something it should have and know about, then this could trivially be passed onto bootefi. This could means a "default_fdt" environment variable which is loaded automatically before bootcmd is executed.
My solution would be to give boot.scr preference over *.efi, so that the user has a way to load dtb and run "bootefi" on his own, and otherwise fall back to just "bootefi" which'll spit a warning about lack of fdt if I read that correctly.
An explicit bootscript should always override a fallback boot option. And booting the "removable boot path" is a fallback boot option.
Regards,
Leif

On 26.12.15 19:55, Leif Lindholm wrote:
On Fri, Dec 25, 2015 at 10:25:22AM +0100, Andreas Färber wrote:
Am 25.12.2015 um 10:02 schrieb Alexander Graf: [snip]
The reason I implemented "bootefi" was really because it's the natural fit into how U-Boot handles all other formats today. I don't think this is going to be the last patch set around EFI support.
I think what Matwey was suggesting is integrating your "bootefi" into the standard "distro" boot sequence environment, so that it probes each device for an EFI binary and if it finds one runs load and bootefi, without the need for any boot.scr.
That would be a follow-up patch.
It however conflicts with your idea of having some potentially board-specific code mess with "fdt addr" command before running "bootefi".
This could however be resolved by moving to a model where the device-tree was considered a component of the firmware (which is how we treat it in UEFI). If U-Boot had an awareness (if it does not already) of an FDT being something it should have and know about, then this could trivially be passed onto bootefi. This could means a "default_fdt" environment variable which is loaded automatically before bootcmd is executed.
That part is already the case in my patches :).
The difficult bit is what to do when it doesn't work, because the kernel guys consider a dtb to be a "Linux configuration" framework rather than the "hardware description" framework it's supposed to be.
Alex

On Fri, Dec 25, 2015 at 10:02:44AM +0100, Alexander Graf wrote:
On 24.12.15 12:15, Matwey V. Kornilov wrote:
Why just not to implement standard EFI behaviour when EFI looks for boot-efi partition and proceed?
Well, what is "standard EFI behavior"?
There are 2 standard ways I'm aware of:
- NVRAM
The default case for 99.9% of the boots on normal EFI systems is based on variables in NVRAM that tell EFI which boot device to boot from. Since we don't implement EFI variables today, we can't really make use of this feature. And because you want to change the default boot device at runtime, we'd have to have runtime services be able to modify them after exiting boot services.
- Removable Media
There is another way implemented for "Removable Media" - mostly intended for USB sticks and the likes. Here EFI searches for a defined file name (EFI/boot/boot{arm,a64,x64}.efi) on the ESP partition and boots it.
Part 1 is very difficult to do without major rework of a few U-Boot components. If EFI becomes the de-facto standard way of booting with U-Boot, I think we'll walk down that road, but it's nothing I want to have to deal with in the initial enablement discussion.
Part 2 is easy to do. But then again it's also easy to do it using a boot script. Or a compiled in bootcmd. If it's really desired.
Sure, I've seen a lot more complicated boot operations than that shipped in the default environment of many platforms.
Which brings me to the next idea. What if we just implement exlinux.conf support for EFI binaries? Then all you need to do is have an extlinux.conf available on your generic EFI media that tells U-Boot where to load the grub binary from.
The problem with this is that you're adding things unrelated to UEFI into a UEFI boot scenario. Now, depending on what you're looking for, this may be fine - but it will not leave you with the ability to rely on being able to "just work" with the UEFI support provided by UEFI-aware operating systems and their installation media.
Basically, it comes down to preference - is this about: - Leveraging the benefits of UEFI while staying with the U-Boot codebase. or - Leveraging some of the benefits of UEFI in order to give distros a more device-independent way of supporting U-Boot platforms.
That way we wouldn't bend U-Boot completely away from its heritage, make use of its flexibility and all distributions that actually care about booting from U-Boot would easily be able to just put such a file in an rpm an install it always.
You would also need a way to distinguish whether booting with UEFI or U-Boot efiload.
Sure, compared to stuff like flash-kernel, this is hardly rocket science, but would be worth standardising early and hard.
If ARM board developers will enable EFI support in the future, we can have single one JeOS having all possible dtb in KIWI image. BeagleBone Black has its own u-boot on eMMC, and the user need to push S2 button to force hardware to use our openSUSE u-boot from SD card. Maybe something like that is for other boards. If the single one required u-boot feature is to run EFI grub, then we can even don't touch preinstalled bootloader, that is not possible now, because we need our openSUSE boot scripts.
There are more things we need to solve before we can truly get to a universal booting solution. But one step at a time :).
The reason I implemented "bootefi" was really because it's the natural fit into how U-Boot handles all other formats today. I don't think this is going to be the last patch set around EFI support.
Alex

On Thu, Dec 24, 2015 at 02:15:52PM +0300, Matwey V. Kornilov wrote:
Why just not to implement standard EFI behaviour when EFI looks for boot-efi partition and proceed?
If ARM board developers will enable EFI support in the future, we can have single one JeOS having all possible dtb in KIWI image. BeagleBone Black has its own u-boot on eMMC, and the user need to push S2 button to force hardware to use our openSUSE u-boot from SD card. Maybe something like that is for other boards. If the single one required u-boot feature is to run EFI grub, then we can even don't touch preinstalled bootloader, that is not possible now, because we need our openSUSE boot scripts.
AFAICT once we have the general support in we can simply extend the current generic distro framework to have an iteration on "does the device have the expected EFI boot stuff?" and if so, boot it. Nothing we need to do in C code here.

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 --- lib/Kconfig | 1 + lib/Makefile | 1 + lib/efi_loader/Kconfig | 8 ++++++++ lib/efi_loader/Makefile | 11 +++++++++++ 4 files changed, 21 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..870f1f7 --- /dev/null +++ b/lib/efi_loader/Kconfig @@ -0,0 +1,8 @@ +config EFI_LOADER + bool "Support running EFI Applications in U-Boot" + depends on ARM64 || ARM + 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..1df595b --- /dev/null +++ b/lib/efi_loader/Makefile @@ -0,0 +1,11 @@ +# +# (C) Copyright 2015 Alexander Graf +# +# SPDX-License-Identifier: LGPL-2.1+ +# + +# 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

2015-12-22 16:57 GMT+03:00 Alexander Graf agraf@suse.de:
This is my Christmas present for my openSUSE friends :).
Santa, do you have u-boot rpm packed with the patches to test?

On 22.12.15 19:28, Matwey V. Kornilov wrote:
2015-12-22 16:57 GMT+03:00 Alexander Graf agraf@suse.de:
This is my Christmas present for my openSUSE friends :).
Santa, do you have u-boot rpm packed with the patches to test?
Once OBS has finished compiling, they should be available here:
https://build.opensuse.org/project/show/home:algraf:branches:Base:System
If you also want a 32bit ARM grub2 binary, check out
https://build.opensuse.org/project/show/devel:ARM:Factory:Contrib:HIP04D01
Merry Christmas ;)
Alex

On Tue, Dec 22, 2015 at 02:57:47PM +0100, Alexander Graf 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.
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.
So, I owe the whole codebase a real review. My very quick question however is, aside from what you had to borrow from wine, can you license everything else as GPL v2 or later rather than LGPL?

On 25.12.15 04:29, Tom Rini wrote:
On Tue, Dec 22, 2015 at 02:57:47PM +0100, Alexander Graf 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.
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.
So, I owe the whole codebase a real review. My very quick question however is, aside from what you had to borrow from wine, can you license everything else as GPL v2 or later rather than LGPL?
I'm personally a pretty big fan of the LGPL, since it's a very reasonable compromise between closed and open source IMHO ;).
Is there a particular reason you're asking for this? LGPL code is fully compatible with GPL code and the resulting binary would be GPL anyway because FWIW you can't compile U-Boot without GPL code inside.
Alex

On Fri, Dec 25, 2015 at 09:53:22AM +0100, Alexander Graf wrote:
On 25.12.15 04:29, Tom Rini wrote:
On Tue, Dec 22, 2015 at 02:57:47PM +0100, Alexander Graf 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.
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.
So, I owe the whole codebase a real review. My very quick question however is, aside from what you had to borrow from wine, can you license everything else as GPL v2 or later rather than LGPL?
I'm personally a pretty big fan of the LGPL, since it's a very reasonable compromise between closed and open source IMHO ;).
Is there a particular reason you're asking for this? LGPL code is fully compatible with GPL code and the resulting binary would be GPL anyway because FWIW you can't compile U-Boot without GPL code inside.
The general rules for U-Boot code are to be GPL v2 or later. U-Boot is (and always will be) a GPL v2 only project as there's simply too much Linux kernel code that we want to leverage. We do make special exceptions at times for very good reasons (like include/android_image.h is the authorative BSD-2 clause copy of that information) and I've even told some companies that for crypto-auth-sensitive stuff they can do GPL v2 only in their submission (again, due to U-Boot always being a v2 only project).
So, I'm not gonig to reject the EFI loader code if you say no, you won't re-license it as GPL v2 (or v2 and later) but I'd really appreciate it. Thanks!

2015-12-25 19:50 GMT+03:00 Tom Rini trini@konsulko.com:
On Fri, Dec 25, 2015 at 09:53:22AM +0100, Alexander Graf wrote:
On 25.12.15 04:29, Tom Rini wrote:
On Tue, Dec 22, 2015 at 02:57:47PM +0100, Alexander Graf 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.
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.
So, I owe the whole codebase a real review. My very quick question however is, aside from what you had to borrow from wine, can you license everything else as GPL v2 or later rather than LGPL?
I'm personally a pretty big fan of the LGPL, since it's a very reasonable compromise between closed and open source IMHO ;).
Is there a particular reason you're asking for this? LGPL code is fully compatible with GPL code and the resulting binary would be GPL anyway because FWIW you can't compile U-Boot without GPL code inside.
The general rules for U-Boot code are to be GPL v2 or later. U-Boot is (and always will be) a GPL v2 only project as there's simply too much Linux kernel code that we want to leverage. We do make special exceptions at times for very good reasons (like include/android_image.h is the authorative BSD-2 clause copy of that information) and I've even told some companies that for crypto-auth-sensitive stuff they can do GPL v2 only in their submission (again, due to U-Boot always being a v2 only project).
So, I'm not gonig to reject the EFI loader code if you say no, you won't re-license it as GPL v2 (or v2 and later) but I'd really appreciate it. Thanks!
If EFI loader is GPLed, then is it possible to use it to run non-GPLed (proprietary) EFI applications?

On Fri, Dec 25, 2015 at 07:53:24PM +0300, Matwey V. Kornilov wrote:
2015-12-25 19:50 GMT+03:00 Tom Rini trini@konsulko.com:
On Fri, Dec 25, 2015 at 09:53:22AM +0100, Alexander Graf wrote:
On 25.12.15 04:29, Tom Rini wrote:
On Tue, Dec 22, 2015 at 02:57:47PM +0100, Alexander Graf 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.
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.
So, I owe the whole codebase a real review. My very quick question however is, aside from what you had to borrow from wine, can you license everything else as GPL v2 or later rather than LGPL?
I'm personally a pretty big fan of the LGPL, since it's a very reasonable compromise between closed and open source IMHO ;).
Is there a particular reason you're asking for this? LGPL code is fully compatible with GPL code and the resulting binary would be GPL anyway because FWIW you can't compile U-Boot without GPL code inside.
The general rules for U-Boot code are to be GPL v2 or later. U-Boot is (and always will be) a GPL v2 only project as there's simply too much Linux kernel code that we want to leverage. We do make special exceptions at times for very good reasons (like include/android_image.h is the authorative BSD-2 clause copy of that information) and I've even told some companies that for crypto-auth-sensitive stuff they can do GPL v2 only in their submission (again, due to U-Boot always being a v2 only project).
So, I'm not gonig to reject the EFI loader code if you say no, you won't re-license it as GPL v2 (or v2 and later) but I'd really appreciate it. Thanks!
If EFI loader is GPLed, then is it possible to use it to run non-GPLed (proprietary) EFI applications?
Yes. Absolutely. We've (pratically) always supported running non-GPL payloads. VxWorks has been supported for ages and ages and ages for example. There may be a thought experiment or two required about callbacks but that's part of why we've had CONFIG_API, iirc.

On 25.12.15 17:50, Tom Rini wrote:
On Fri, Dec 25, 2015 at 09:53:22AM +0100, Alexander Graf wrote:
On 25.12.15 04:29, Tom Rini wrote:
On Tue, Dec 22, 2015 at 02:57:47PM +0100, Alexander Graf 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.
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.
So, I owe the whole codebase a real review. My very quick question however is, aside from what you had to borrow from wine, can you license everything else as GPL v2 or later rather than LGPL?
I'm personally a pretty big fan of the LGPL, since it's a very reasonable compromise between closed and open source IMHO ;).
Is there a particular reason you're asking for this? LGPL code is fully compatible with GPL code and the resulting binary would be GPL anyway because FWIW you can't compile U-Boot without GPL code inside.
The general rules for U-Boot code are to be GPL v2 or later. U-Boot is (and always will be) a GPL v2 only project as there's simply too much Linux kernel code that we want to leverage. We do make special exceptions at times for very good reasons (like include/android_image.h is the authorative BSD-2 clause copy of that information) and I've even told some companies that for crypto-auth-sensitive stuff they can do GPL v2 only in their submission (again, due to U-Boot always being a v2 only project).
So, I'm not gonig to reject the EFI loader code if you say no, you won't re-license it as GPL v2 (or v2 and later) but I'd really appreciate it. Thanks!
I've just read up and apparently it's completely legal and allowed to simply remove the LGPL (2.1+) boilerplate from a file and instead put a GPL (2.0+) one on it, even if you didn't write the code.
So even if I had insisted to stick to LGPL v2.1+, you could've just written a patch to change it after the fact ;).
But since everyone seems to be far more happy with GPL rather than LGPL, I've spared you that patch and changed the headers myself now.
Alex

On Fri, Jan 15, 2016 at 04:00:21AM +0100, Alexander Graf wrote:
On 25.12.15 17:50, Tom Rini wrote:
On Fri, Dec 25, 2015 at 09:53:22AM +0100, Alexander Graf wrote:
On 25.12.15 04:29, Tom Rini wrote:
On Tue, Dec 22, 2015 at 02:57:47PM +0100, Alexander Graf 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.
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.
So, I owe the whole codebase a real review. My very quick question however is, aside from what you had to borrow from wine, can you license everything else as GPL v2 or later rather than LGPL?
I'm personally a pretty big fan of the LGPL, since it's a very reasonable compromise between closed and open source IMHO ;).
Is there a particular reason you're asking for this? LGPL code is fully compatible with GPL code and the resulting binary would be GPL anyway because FWIW you can't compile U-Boot without GPL code inside.
The general rules for U-Boot code are to be GPL v2 or later. U-Boot is (and always will be) a GPL v2 only project as there's simply too much Linux kernel code that we want to leverage. We do make special exceptions at times for very good reasons (like include/android_image.h is the authorative BSD-2 clause copy of that information) and I've even told some companies that for crypto-auth-sensitive stuff they can do GPL v2 only in their submission (again, due to U-Boot always being a v2 only project).
So, I'm not gonig to reject the EFI loader code if you say no, you won't re-license it as GPL v2 (or v2 and later) but I'd really appreciate it. Thanks!
I've just read up and apparently it's completely legal and allowed to simply remove the LGPL (2.1+) boilerplate from a file and instead put a GPL (2.0+) one on it, even if you didn't write the code.
Legal and good idea don't always match up :)
So even if I had insisted to stick to LGPL v2.1+, you could've just written a patch to change it after the fact ;).
But since everyone seems to be far more happy with GPL rather than LGPL, I've spared you that patch and changed the headers myself now.
Thanks, I appreciate it!

On 12/22/2015 05:57 AM, Alexander Graf 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.
This is a very exciting patch!
The potential to one day run CHIPSEC on U-Boot systems is VERY EXCITING! https://github.com/chipsec/chipsec CHIPSEC is a UEFI-centric. Though I've heard someone got it to work on coreboot-based Intel-based Android system, not sure how.
After UEFI Shell works, I hope a goal is to get UEFI port of CPython 2.7x working, and Intel CHIPSEC running. CHIPSEC is a hardware/firmware vulnerability detection tool, GPL open source. As I've heard them say, the Intel CHIPSEC team is open to patches from all architectures for all firmware targets, not just Intel x86/x64.
Linaro has started to investigate port of CHIPSEC from x86/x64 to AArch64, as part of port of LUV (Linux UEFI Validation) project. Once CPython and CHIPSEC run on U-Boot, this enables a whole new level of hardware/firmware security detection! Once ported to AArch64, the ARM security teams needs to add some AArch64-centric security test modules to CHIPSEC, as it'll do little good on ARM, except for a few portable UEFI variable and SPI tests, otherwise. https://wiki.linaro.org/LEG/Engineering/luvOS Hopefully ARM can fund Linaro to also port LUV/CHIPSEC to AArch32, all of their products need hardware/firmware vulnerability detection software, not just the latest 64-bit ones.
For U-Boot on MIPS, there is an unofficial UEFI MIPS port, but nobody has touched it in a while, and CHIPSEC hasn't yet been ported there. https://github.com/kontais/EFI-MIPS
I didn't think U-Boot ran on OpenPOWER, if it does, I missed that, sorry. If so, there are two ports of UEFI to OpenPOWER by different developers at IBM, but (AFAIK) no official OpenPOWER interest in UEFI, and no CHIPSEC port to OpenPOWER yet. And no OpenPOWER-centric security modules. http://firmwaresecurity.com/2015/10/12/tianocore-for-openpower/ http://firmwaresecurity.com/2015/10/12/second-port-of-tianocore-to-openpower...
For other architectures that U-Boot runs on, I'm afraid porting UEFI will be necessary before CHIPSEC can be attempted. :-( Is there any marketshare data that shows which architectures coverage by U-Boot?
Dumb question: it appears Intel is not involved in U-Boot's x86/x64 port, or maybe I've just missed their involvement. I see Intel very involved with coreboot and UEFI, but not U-Boot, even though U-Boot is targetting Intel platforms. Can someone explain that to me? :-)
Thanks, Lee Fisher RSS: http://firmwaresecurity.com/feed

On Tue, Dec 22, 2015 at 02:57:47PM +0100, Alexander Graf 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.
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.
Thanks, this is a very cool thing. I meant to reply sooner, but Christmas got in the way.
- 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 runtime services (all calls return unimplemented)
Yeah, this is a bit of a pain point. The time services, virtual memory services and reset being the key ones.
- No EFI variables
This would obviously (from my point of view) be desirable, but at least initially, we can do most things without persistent variables.
Of course, there are still a few things one could do on top:
- Implement removable media booting (search for /efi/boot/boota{a64,rm}.efi)
Yeah, that would be top of my wishlist.
- Improve disk media detection (don't scan, use what information we have)
- Add EFI variable support using NVRAM
- Add GFX support
GFX support was actually never implemented for U-Boot GRUB, so from this p.o.v. it is not a shortcoming over the existing impementation.
- Make EFI Shell work ;)
- Network device support.
I also spotted a couple of minor things while playing around (things like image exit being missing), but these will be easy to flush out.
I'll leave reviewing of the u-boot side of things to people who know the codebase better, and restrict myself to commenting on the UEFIness.
/ Leif
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.
Alex
Alexander Graf (9): disk/part.c: Expose a 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: hook up in build environment
arch/arm/cpu/armv8/u-boot.lds | 8 + arch/arm/cpu/u-boot.lds | 13 + arch/arm/lib/sections.c | 2 + common/Makefile | 1 + common/cmd_bootefi.c | 168 ++++++++ disk/part.c | 25 ++ include/efi_api.h | 197 +++++++-- include/efi_loader.h | 87 ++++ include/part.h | 2 + include/pe.h | 277 +++++++++++++ lib/Kconfig | 1 + lib/Makefile | 1 + lib/efi_loader/Kconfig | 8 + lib/efi_loader/Makefile | 11 + lib/efi_loader/efi_boottime.c | 838 ++++++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_console.c | 371 +++++++++++++++++ lib/efi_loader/efi_disk.c | 227 +++++++++++ lib/efi_loader/efi_image_loader.c | 203 +++++++++ lib/efi_loader/efi_runtime.c | 59 +++ 19 files changed, 2462 insertions(+), 37 deletions(-) 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

On 26.12.15 16:31, Leif Lindholm wrote:
On Tue, Dec 22, 2015 at 02:57:47PM +0100, Alexander Graf 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.
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.
Thanks, this is a very cool thing. I meant to reply sooner, but Christmas got in the way.
- 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 runtime services (all calls return unimplemented)
Yeah, this is a bit of a pain point. The time services, virtual memory services and reset being the key ones.
I guess reset should be pretty well doable. What are virtual memory services? The bits that translate RTS code to run in virtual address space somewhere else?
- No EFI variables
This would obviously (from my point of view) be desirable, but at least initially, we can do most things without persistent variables.
Doing EFI variables before exiting boot services is easy - we could even just map U-Boot variables to EFI variables. That could come in handy for cases where you want U-Boot to tell you which board you're on so you can refer to different dtb files in your grub.cfg (if you need to override the dtb).
Making them persistent is another difficult question - U-Boot usually splits "modify variable" from "store variable pool to nvram".
And the hardest part would obviously be to make all of this work while Linux is running ;). I'd definitely prefer to defer that bit to later.
Of course, there are still a few things one could do on top:
- Implement removable media booting (search for /efi/boot/boota{a64,rm}.efi)
Yeah, that would be top of my wishlist.
That should be pretty simple to do - maybe we can even get away without any C code for it.
- Improve disk media detection (don't scan, use what information we have)
- Add EFI variable support using NVRAM
- Add GFX support
GFX support was actually never implemented for U-Boot GRUB, so from this p.o.v. it is not a shortcoming over the existing impementation.
Heh ;). My goal here really is to make U-Boot based systems be en par with TianoCore based ones in terms of usability.
- Make EFI Shell work ;)
- Network device support.
Oh, good point. I forgot about that one.
I also spotted a couple of minor things while playing around (things like image exit being missing), but these will be easy to flush out.
Awesome, looking forward to the review! :)
Thanks a lot for taking the time to look at this patch set.
Alex

On Sat, Dec 26, 2015 at 05:27:48PM +0100, Alexander Graf wrote:
This patch set is the result of pursuing this endeavor.
Thanks, this is a very cool thing. I meant to reply sooner, but Christmas got in the way.
- 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 runtime services (all calls return unimplemented)
Yeah, this is a bit of a pain point. The time services, virtual memory services and reset being the key ones.
I guess reset should be pretty well doable. What are virtual memory services? The bits that translate RTS code to run in virtual address space somewhere else?
Yup.
- No EFI variables
This would obviously (from my point of view) be desirable, but at least initially, we can do most things without persistent variables.
Doing EFI variables before exiting boot services is easy - we could even just map U-Boot variables to EFI variables. That could come in handy for cases where you want U-Boot to tell you which board you're on so you can refer to different dtb files in your grub.cfg (if you need to override the dtb).
Making them persistent is another difficult question - U-Boot usually splits "modify variable" from "store variable pool to nvram".
Indeed. UEFI might as well, but in more convoluted ways.
And the hardest part would obviously be to make all of this work while Linux is running ;). I'd definitely prefer to defer that bit to later.
Of course.
And again - depending on ambition levels, implementing a version of efibootmgr that ended up simply tweaking a /boot/uEnv.txt (or similar) if U-Boot boot environment was detected would be entirely achievable.
Of course, there are still a few things one could do on top:
- Implement removable media booting (search for /efi/boot/boota{a64,rm}.efi)
Yeah, that would be top of my wishlist.
That should be pretty simple to do - maybe we can even get away without any C code for it.
Should be possible.
- Improve disk media detection (don't scan, use what information we have)
- Add EFI variable support using NVRAM
- Add GFX support
GFX support was actually never implemented for U-Boot GRUB, so from this p.o.v. it is not a shortcoming over the existing impementation.
Heh ;). My goal here really is to make U-Boot based systems be en par with TianoCore based ones in terms of usability.
Sure. Just setting the nearer goal post of "where can we replace U-Boot API in GRUB".
- Make EFI Shell work ;)
- Network device support.
Oh, good point. I forgot about that one.
I also spotted a couple of minor things while playing around (things like image exit being missing), but these will be easy to flush out.
Awesome, looking forward to the review! :)
Thanks a lot for taking the time to look at this patch set.
Sure, this is a very useful thing.
Even at the most cynical possible position (which isn't where I am), this means additional interface testing using a completely separate codebase.
/ Leif

On 26.12.15 20:34, Leif Lindholm wrote:
On Sat, Dec 26, 2015 at 05:27:48PM +0100, Alexander Graf wrote:
This patch set is the result of pursuing this endeavor.
Thanks, this is a very cool thing. I meant to reply sooner, but Christmas got in the way.
- 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 runtime services (all calls return unimplemented)
Yeah, this is a bit of a pain point. The time services, virtual memory services and reset being the key ones.
I guess reset should be pretty well doable. What are virtual memory services? The bits that translate RTS code to run in virtual address space somewhere else?
Yup.
Can you point me to code that uses the time services?
- No EFI variables
This would obviously (from my point of view) be desirable, but at least initially, we can do most things without persistent variables.
Doing EFI variables before exiting boot services is easy - we could even just map U-Boot variables to EFI variables. That could come in handy for cases where you want U-Boot to tell you which board you're on so you can refer to different dtb files in your grub.cfg (if you need to override the dtb).
Making them persistent is another difficult question - U-Boot usually splits "modify variable" from "store variable pool to nvram".
Indeed. UEFI might as well, but in more convoluted ways.
And the hardest part would obviously be to make all of this work while Linux is running ;). I'd definitely prefer to defer that bit to later.
Of course.
And again - depending on ambition levels, implementing a version of efibootmgr that ended up simply tweaking a /boot/uEnv.txt (or similar) if U-Boot boot environment was detected would be entirely achievable.
Of course, there are still a few things one could do on top:
- Implement removable media booting (search for /efi/boot/boota{a64,rm}.efi)
Yeah, that would be top of my wishlist.
That should be pretty simple to do - maybe we can even get away without any C code for it.
Should be possible.
- Improve disk media detection (don't scan, use what information we have)
- Add EFI variable support using NVRAM
- Add GFX support
GFX support was actually never implemented for U-Boot GRUB, so from this p.o.v. it is not a shortcoming over the existing impementation.
Heh ;). My goal here really is to make U-Boot based systems be en par with TianoCore based ones in terms of usability.
Sure. Just setting the nearer goal post of "where can we replace U-Boot API in GRUB".
I am not aware of anyone using the U-Boot API for grub these days, so I'm not sure it's an incredibly useful goal. The main pain point distros seem to have is to make something that "just works" on all systems out there. Moving into that direction should be our ultimate goal.
Alex

On Mon, Jan 04, 2016 at 05:25:44PM +0100, Alexander Graf wrote:
[snip]
I am not aware of anyone using the U-Boot API for grub these days, so I'm not sure it's an incredibly useful goal. The main pain point distros seem to have is to make something that "just works" on all systems out there. Moving into that direction should be our ultimate goal.
Please note that with the generic distro framework U-Boot will grok https://wiki.freedesktop.org/www/Specifications/BootLoaderSpec/ and things Just Work. I setup a bunch of SD cards with Debian and Fedora over holiday so I can drop them in whatever board and boot up Linux as a sanity test.
I certainly can see a usecase for kicking off an EFI binary as part of fitting into existing work-flows. But we do already have a something for getting rid of that particular pain-point and it's working :)

Am 04.01.2016 um 17:56 schrieb Tom Rini:
Please note that with the generic distro framework U-Boot will grok https://wiki.freedesktop.org/www/Specifications/BootLoaderSpec/ and things Just Work. I setup a bunch of SD cards with Debian and Fedora over holiday so I can drop them in whatever board and boot up Linux as a sanity test.
I certainly can see a usecase for kicking off an EFI binary as part of fitting into existing work-flows. But we do already have a something for getting rid of that particular pain-point and it's working :)
No, as I explained before, you are not addressing that particular pain-point: extlinux is still something new to implement for us as distro, you provide no tools to help us, while on x86, ppc, s390 and some aarch64 we have converged on grub2 as a standard, and just recently the YaST devs decided to only support grub2 going forward.
For extlinux (which BTW to my eye looked slightly different from the freedesktop.org spec that you guys keep referencing?!), distro-specific code needs to be written [1] so that on kernel installation the /boot/extlinux/extlinux.conf file is regenerated - for grub2 such tools simply exist as part of GRUB and this proposed EFI interface for U-Boot will avoid having to implement any new, e.g., perl-Bootloader code.
So the open conflict is that you tell us that extlinux.conf is your "distro" mechanism that we should be using, and our distro people are telling us that grub2 is their preferred solution after having accumulated bootloader code for some two decades and just got rid of it.
Standards are not created through publishing some spec, they are created through adoption, and today I don't see anyone at SUSE moving an inch towards adopting extlinux.conf as a generic boot mechanism for all architectures. That leaves our ARM community at a loss, booting a single kernel through a symlink.
No one has suggested to dump extlinux.conf or boot.scr, they can all simply co-exist, with the difference that, from the looks of it, Alex' EFI code could get enabled by default to allow users to choose using it, unlike the disabled CONFIG_API code that I reported got broken by DM migration and for many other boards was lacking defines and is in need of a board-specific rather than generic second bootloader on the distro side.
This patchset is a cute middle ground where for U-Boot it's mostly just an additional command, our distro people will be content, and our ARM users will be happy too, not having to handcraft extlinux.conf files and benefiting from the vibrant U-Boot community as opposed to the much more fragmented Tianocore forks out there. Thus I'm hoping we can sort out some of the technical issues Leif pointed out and stop circling back to this unhelpful oh-but-extlinux.conf-is-the-mechanism point.
Regards, Andreas
[1] https://github.com/openSUSE/perl-bootloader/pull/81

Am 04.01.2016 um 19:03 schrieb Andreas Färber:
Am 04.01.2016 um 17:56 schrieb Tom Rini:
Please note that with the generic distro framework U-Boot will grok https://wiki.freedesktop.org/www/Specifications/BootLoaderSpec/ and things Just Work. I setup a bunch of SD cards with Debian and Fedora over holiday so I can drop them in whatever board and boot up Linux as a sanity test.
I certainly can see a usecase for kicking off an EFI binary as part of fitting into existing work-flows. But we do already have a something for getting rid of that particular pain-point and it's working :)
[snip]
Executive summary: https://xkcd.com/927/
Cheers, Andreas

On Mon, Jan 04, 2016 at 07:41:42PM +0100, Andreas Färber wrote:
Am 04.01.2016 um 19:03 schrieb Andreas Färber:
Am 04.01.2016 um 17:56 schrieb Tom Rini:
Please note that with the generic distro framework U-Boot will grok https://wiki.freedesktop.org/www/Specifications/BootLoaderSpec/ and things Just Work. I setup a bunch of SD cards with Debian and Fedora over holiday so I can drop them in whatever board and boot up Linux as a sanity test.
I certainly can see a usecase for kicking off an EFI binary as part of fitting into existing work-flows. But we do already have a something for getting rid of that particular pain-point and it's working :)
[snip]
Executive summary: https://xkcd.com/927/
Oh pretty much. I guess the point I am driving at here is that EFI loading (to kick off GRUB2) needs to fit in with the framework that other distros have already adapted to. Or heck, maybe you can convince them to switch over to this instead? Hans or Dennis, what do you think?

On Monday, January 04, 2016 02:54:40 PM Tom Rini wrote:
On Mon, Jan 04, 2016 at 07:41:42PM +0100, Andreas Färber wrote:
Am 04.01.2016 um 19:03 schrieb Andreas Färber:
Am 04.01.2016 um 17:56 schrieb Tom Rini:
Please note that with the generic distro framework U-Boot will grok https://wiki.freedesktop.org/www/Specifications/BootLoaderSpec/ and things Just Work. I setup a bunch of SD cards with Debian and Fedora over holiday so I can drop them in whatever board and boot up Linux as a sanity test.
We do not fully support bootloader spec in u-boot today. but I know that we want to one day
I certainly can see a usecase for kicking off an EFI binary as part of fitting into existing work-flows. But we do already have a something for getting rid of that particular pain-point and it's working :)
[snip]
Executive summary: https://xkcd.com/927/
Oh pretty much. I guess the point I am driving at here is that EFI loading (to kick off GRUB2) needs to fit in with the framework that other distros have already adapted to. Or heck, maybe you can convince them to switch over to this instead? Hans or Dennis, what do you think?
not opposed to it, but it is not something that we have evaluated, I know debian have done a lot of work to ensure that their systems support extlinux.conf also. which is the same syslinux format as used by extlinux/syslinux/isolinux on x86, the user experience is somewhat similiar to that of grub on other arches. Long term I have planed to wire up menu support so you get a menu to interact with rather than a list of boot options, as well as the ability to edit the commandline arguments. I would not say we have perfect support today for extlinux. so far SuSE is the only one saying no to what has been proposed. It was brought up on both the u-boot and linaro cross distro list back in 2013[1][2] with no one saying it was not a good idea.while there was less feedback than I would have liked it was positive.
Anyway my main question is how dtb support would work. As that really is the trickiest part that I can think of. Something that is gracefully dealt with in the extlinux support, regardless of distro. Going this approach to me feels like trying to put a Ford engine in a GM car by adding a volkswagon gearbox. can we make grub a u-boot application? that is not using CONFIG_API or does not need to have hard coded memory locations in it? we looked at grub2 support years ago as we felt that it would be the way to go as it seemed people were standardising on it. and decided that there were too many issues with the implementation for it to be viable. so we went the route of proposing the extlinux.conf file option.
Dennis
[1] https://lists.linaro.org/pipermail/cross-distro/2013-August/000439.html [2] http://lists.denx.de/pipermail/u-boot/2013-August/160080.html

On 04.01.16 23:37, Dennis Gilmore wrote:
On Monday, January 04, 2016 02:54:40 PM Tom Rini wrote:
On Mon, Jan 04, 2016 at 07:41:42PM +0100, Andreas Färber wrote:
Am 04.01.2016 um 19:03 schrieb Andreas Färber:
Am 04.01.2016 um 17:56 schrieb Tom Rini:
Please note that with the generic distro framework U-Boot will grok https://wiki.freedesktop.org/www/Specifications/BootLoaderSpec/ and things Just Work. I setup a bunch of SD cards with Debian and Fedora over holiday so I can drop them in whatever board and boot up Linux as a sanity test.
We do not fully support bootloader spec in u-boot today. but I know that we want to one day
I certainly can see a usecase for kicking off an EFI binary as part of fitting into existing work-flows. But we do already have a something for getting rid of that particular pain-point and it's working :)
[snip]
Executive summary: https://xkcd.com/927/
Oh pretty much. I guess the point I am driving at here is that EFI loading (to kick off GRUB2) needs to fit in with the framework that other distros have already adapted to. Or heck, maybe you can convince them to switch over to this instead? Hans or Dennis, what do you think?
not opposed to it, but it is not something that we have evaluated, I know debian have done a lot of work to ensure that their systems support extlinux.conf also. which is the same syslinux format as used by extlinux/syslinux/isolinux on x86, the user experience is somewhat similiar to that of grub on other arches. Long term I have planed to wire up menu support so you get a menu to interact with rather than a list of boot options, as well as the ability to edit the commandline arguments. I would not say we have perfect support today for extlinux. so far SuSE is the only one saying no to what has been proposed. It was brought up on both the u-boot and linaro cross distro list back in 2013[1][2] with no one saying it was not a good idea.while there was less feedback than I would have liked it was positive.
Anyway my main question is how dtb support would work. As that really is the
Ideally the same way as on existing uEFI based AArch64 machines: Firmware passes it to grub, grub reformats it and passes it on to Linux.
However, as people keep pointing out, we don't live in an ideal device tree world - especially when thinking about boards that are running U-Boot today. Chances are pretty good that a device tree that works for your kernel from today won't run on tomorrow's kernel - or will lack features.
So we still need to support the devicetree option in grub2 - and it does work today. The only convenience missing from this patch set (and I'll look into it once I have runtime service relocation working on 32bit) is variable export. That way you'd be able to write
devicetree (hd0,1)/dtb-4.10.2/$fdtfile
in your grub config and u-boot would magically tell you which dtb file to load. Then - in theory - you wouldn't need any platform specific logic in your bootloader configuration generation anymore.
trickiest part that I can think of. Something that is gracefully dealt with in the extlinux support, regardless of distro. Going this approach to me feels like trying to put a Ford engine in a GM car by adding a volkswagon gearbox. can we make grub a u-boot application? that is not using CONFIG_API or does not need to have hard coded memory locations in it? we looked at
That's what this patch set is about, yes. It'd be the exact same grub2 binary that runs on all other EFI enabled systems. So if you run on HIP04D01 or AMD Seattle today, chances are pretty good you have everything in place already.
grub2 support years ago as we felt that it would be the way to go as it seemed people were standardising on it. and decided that there were too many issues with the implementation for it to be viable. so we went the route of proposing the extlinux.conf file option.
I'm no evangelist - if the extlinux.conf solution works for people, I'm happy if they stick with it. It just simply doesn't work well for us ;).
Alex

On Mon, Jan 4, 2016 at 6:03 PM, Andreas Färber afaerber@suse.de wrote:
Am 04.01.2016 um 17:56 schrieb Tom Rini:
Please note that with the generic distro framework U-Boot will grok https://wiki.freedesktop.org/www/Specifications/BootLoaderSpec/ and things Just Work. I setup a bunch of SD cards with Debian and Fedora over holiday so I can drop them in whatever board and boot up Linux as a sanity test.
I certainly can see a usecase for kicking off an EFI binary as part of fitting into existing work-flows. But we do already have a something for getting rid of that particular pain-point and it's working :)
No, as I explained before, you are not addressing that particular pain-point: extlinux is still something new to implement for us as distro, you provide no tools to help us, while on x86, ppc, s390 and some aarch64 we have converged on grub2 as a standard, and just recently the YaST devs decided to only support grub2 going forward.
For extlinux (which BTW to my eye looked slightly different from the freedesktop.org spec that you guys keep referencing?!), distro-specific code needs to be written [1] so that on kernel installation the /boot/extlinux/extlinux.conf file is regenerated - for grub2 such tools simply exist as part of GRUB and this proposed EFI interface for U-Boot will avoid having to implement any new, e.g., perl-Bootloader code.
Yes, that is true, but it only has to be done once.
So the open conflict is that you tell us that extlinux.conf is your "distro" mechanism that we should be using, and our distro people are telling us that grub2 is their preferred solution after having accumulated bootloader code for some two decades and just got rid of it.
Standards are not created through publishing some spec, they are created through adoption, and today I don't see anyone at SUSE moving an inch towards adopting extlinux.conf as a generic boot mechanism for all architectures. That leaves our ARM community at a loss, booting a single kernel through a symlink.
It's certainly not ideal but in Fedora (and I believe in Debian too) we use the distro support in u-boot to boot dozens of devices with multiple kernels and roll backs.
No one has suggested to dump extlinux.conf or boot.scr, they can all simply co-exist, with the difference that, from the looks of it, Alex' EFI code could get enabled by default to allow users to choose using it, unlike the disabled CONFIG_API code that I reported got broken by DM migration and for many other boards was lacking defines and is in need of a board-specific rather than generic second bootloader on the distro side.
This patchset is a cute middle ground where for U-Boot it's mostly just an additional command, our distro people will be content, and our ARM users will be happy too, not having to handcraft extlinux.conf files and benefiting from the vibrant U-Boot community as opposed to the much more fragmented Tianocore forks out there. Thus I'm hoping we can sort out some of the technical issues Leif pointed out and stop circling back to this unhelpful oh-but-extlinux.conf-is-the-mechanism point.
Yes, I think it's definitely good value, especially for aarch64 devices where uEFI is currently a much wider tested option due to the current available devices.

2016-01-04 19:56 GMT+03:00 Tom Rini trini@konsulko.com:
On Mon, Jan 04, 2016 at 05:25:44PM +0100, Alexander Graf wrote:
[snip]
I am not aware of anyone using the U-Boot API for grub these days, so I'm not sure it's an incredibly useful goal. The main pain point distros seem to have is to make something that "just works" on all systems out there. Moving into that direction should be our ultimate goal.
Please note that with the generic distro framework U-Boot will grok https://wiki.freedesktop.org/www/Specifications/BootLoaderSpec/ and
I would say this is pretty alpha spec. How should I specify fallback booting with it? Not to mention that currently grub2 perfectly boots from LVM and MD partitions in my installations and this is not supported in spec (from what I see).
p.s. > Currently there's little cooperation between multiple distributions in dual-boot (or triple, ... multi-boot) setups
Have you ever seen anybody really needed it?
things Just Work. I setup a bunch of SD cards with Debian and Fedora over holiday so I can drop them in whatever board and boot up Linux as a sanity test.
I certainly can see a usecase for kicking off an EFI binary as part of fitting into existing work-flows. But we do already have a something for getting rid of that particular pain-point and it's working :)
-- Tom

I am not aware of anyone using the U-Boot API for grub these days, so I'm not sure it's an incredibly useful goal. The main pain point distros seem to have is to make something that "just works" on all systems out there. Moving into that direction should be our ultimate goal.
Please note that with the generic distro framework U-Boot will grok https://wiki.freedesktop.org/www/Specifications/BootLoaderSpec/ and things Just Work. I setup a bunch of SD cards with Debian and Fedora over holiday so I can drop them in whatever board and boot up Linux as a sanity test.
This is certtainly Fedora's preferred method for u-boot based devices
I certainly can see a usecase for kicking off an EFI binary as part of fitting into existing work-flows. But we do already have a something for getting rid of that particular pain-point and it's working :)
It would certainly be a useful option to have.

On Sat, Dec 26, 2015 at 03:31:03PM +0000, Leif Lindholm wrote:
On Tue, Dec 22, 2015 at 02:57:47PM +0100, Alexander Graf 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.
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.
Thanks, this is a very cool thing. I meant to reply sooner, but Christmas got in the way.
- 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 runtime services (all calls return unimplemented)
Yeah, this is a bit of a pain point. The time services, virtual memory services and reset being the key ones.
- No EFI variables
This would obviously (from my point of view) be desirable, but at least initially, we can do most things without persistent variables.
Of course, there are still a few things one could do on top:
- Implement removable media booting (search for /efi/boot/boota{a64,rm}.efi)
Yeah, that would be top of my wishlist.
- Improve disk media detection (don't scan, use what information we have)
- Add EFI variable support using NVRAM
- Add GFX support
GFX support was actually never implemented for U-Boot GRUB, so from this p.o.v. it is not a shortcoming over the existing impementation.
- Make EFI Shell work ;)
- Network device support.
I also spotted a couple of minor things while playing around (things like image exit being missing), but these will be easy to flush out.
I'll leave reviewing of the u-boot side of things to people who know the codebase better, and restrict myself to commenting on the UEFIness.
So, my only "big" concern here is, are we as a community able to view and implement the relevant parts of the UEFI spec (without having to agree to a potentially complicated enough license to have to bug a lawyer)? It's been a while since I tried to view a copy so I'm hoping the answer is now yes. Thanks!

On Sun, Dec 27, 2015 at 01:10:39PM -0500, Tom Rini wrote:
So, my only "big" concern here is, are we as a community able to view and implement the relevant parts of the UEFI spec (without having to agree to a potentially complicated enough license to have to bug a lawyer)? It's been a while since I tried to view a copy so I'm hoping the answer is now yes. Thanks!
So: 1) You can certainly read the specification without agreeing to anything (click-through is now gone - docs accessible straight from http://uefi.org/specifications/). 2) The potential complication would be with regards to implementing and distributing - which requires signing the Adopters Agreement: http://uefi.org/sites/default/files/resources/UEFI_Adopters_Agreement_100907...
I actually think the implications of 2) for a project like U-Boot would be a useful thing to bring up for discussion on the FW/OS forum mailing list at http://uefi.org/FWOSForum.
/ Leif

On Sun, Dec 27, 2015 at 06:39:16PM +0000, Leif Lindholm wrote:
On Sun, Dec 27, 2015 at 01:10:39PM -0500, Tom Rini wrote:
So, my only "big" concern here is, are we as a community able to view and implement the relevant parts of the UEFI spec (without having to agree to a potentially complicated enough license to have to bug a lawyer)? It's been a while since I tried to view a copy so I'm hoping the answer is now yes. Thanks!
So:
- You can certainly read the specification without agreeing to anything (click-through is now gone - docs accessible straight from http://uefi.org/specifications/).
Thats good.
- The potential complication would be with regards to implementing and distributing - which requires signing the Adopters Agreement: http://uefi.org/sites/default/files/resources/UEFI_Adopters_Agreement_100907...
I actually think the implications of 2) for a project like U-Boot would be a useful thing to bring up for discussion on the FW/OS forum mailing list at http://uefi.org/FWOSForum.
OK, I'll bring it up over there, thanks!

On Sun, Dec 27, 2015 at 06:39:16PM +0000, Leif Lindholm wrote:
On Sun, Dec 27, 2015 at 01:10:39PM -0500, Tom Rini wrote:
So, my only "big" concern here is, are we as a community able to view and implement the relevant parts of the UEFI spec (without having to agree to a potentially complicated enough license to have to bug a lawyer)? It's been a while since I tried to view a copy so I'm hoping the answer is now yes. Thanks!
So:
- You can certainly read the specification without agreeing to anything (click-through is now gone - docs accessible straight from http://uefi.org/specifications/).
- The potential complication would be with regards to implementing and distributing - which requires signing the Adopters Agreement: http://uefi.org/sites/default/files/resources/UEFI_Adopters_Agreement_100907...
I actually think the implications of 2) for a project like U-Boot would be a useful thing to bring up for discussion on the FW/OS forum mailing list at http://uefi.org/FWOSForum.
The answer I got back is http://lists.mailman.uefi.org/pipermail/fw_os_forum/20160105/000004.html and in short, individuals are welcome to (and apparently many have) sign and execute the agreement. People that work for companies that have already signed the agreement are good to go. To me, this is reasonable for the goals U-Boot has with respect to UEFI implementation.
participants (9)
-
Alexander Graf
-
Andreas Färber
-
Blibbet
-
Dennis Gilmore
-
Leif Lindholm
-
Matwey V. Kornilov
-
Peter Robinson
-
Simon Glass
-
Tom Rini