Discussion:
[U-Boot] [PATCH 7/9] efi_loader: Add disk interfaces
Alexander Graf
2015-12-22 13:57:54 UTC
Permalink
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 <***@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;
+}
--
2.1.4
Alexander Graf
2015-12-22 13:57:48 UTC
Permalink
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 <***@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; }
--
2.1.4
Tom Rini
2016-01-14 19:18:41 UTC
Permalink
Post by Alexander Graf
We have a pretty nice and generic interface to ask for a specific block
device. However, that one is still based around the magic notion that
we know the driver name.
In order to be able to write fully generic disk access code, expose a list
of all available block drivers.
Reviewed-by: Tom Rini <***@konsulko.com>
--
Tom
Simon Glass
2016-01-14 23:11:44 UTC
Permalink
Hi Alexander,
Post by Alexander Graf
We have a pretty nice and generic interface to ask for a specific block
device. However, that one is still based around the magic notion that
we know the driver name.
In order to be able to write fully generic disk access code, expose a list
of all available block drivers.
---
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?
Post by Alexander Graf
+
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
Alexander Graf
2016-01-14 23:33:03 UTC
Permalink
Post by Simon Glass
Hi Alexander,
Post by Alexander Graf
We have a pretty nice and generic interface to ask for a specific block
device. However, that one is still based around the magic notion that
we know the driver name.
In order to be able to write fully generic disk access code, expose a list
of all available block drivers.
---
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
Post by Simon Glass
Post by Alexander Graf
+
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
Simon Glass
2016-01-15 00:46:53 UTC
Permalink
Hi Alex,
Post by Alexander Graf
Post by Simon Glass
Hi Alexander,
Post by Alexander Graf
We have a pretty nice and generic interface to ask for a specific block
device. However, that one is still based around the magic notion that
we know the driver name.
In order to be able to write fully generic disk access code, expose a list
of all available block drivers.
---
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!
Post by Alexander Graf
Alex
Post by Simon Glass
Post by Alexander Graf
+
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
Alexander Graf
2016-01-15 01:04:40 UTC
Permalink
Post by Simon Glass
Hi Alex,
Post by Alexander Graf
Post by Simon Glass
Hi Alexander,
Post by Alexander Graf
We have a pretty nice and generic interface to ask for a specific block
device. However, that one is still based around the magic notion that
we know the driver name.
In order to be able to write fully generic disk access code, expose a list
of all available block drivers.
---
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? :)
Post by Simon Glass
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
Alexander Graf
2015-12-22 13:57:49 UTC
Permalink
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 <***@suse.de>
---
include/efi_api.h | 197 ++++++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 160 insertions(+), 37 deletions(-)

diff --git a/include/efi_api.h b/include/efi_api.h
index 4fd17d6..917781f 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -17,11 +17,18 @@

#include <efi.h>

+/* Types and defines for EFI CreateEvent */
+enum efi_event_type {
+ EFI_TIMER_STOP = 0,
+ EFI_TIMER_PERIODIC = 1,
+ EFI_TIMER_RELATIVE = 2
+};
+
/* EFI Boot Services table */
struct efi_boot_services {
struct efi_table_hdr hdr;
- void *raise_tpl;
- void *restore_tpl;
+ efi_status_t (EFIAPI *raise_tpl)(unsigned long new_tpl);
+ void (EFIAPI *restore_tpl)(unsigned long old_tpl);

efi_status_t (EFIAPI *allocate_pages)(int, int, unsigned long,
efi_physical_addr_t *);
@@ -32,21 +39,32 @@ struct efi_boot_services {
efi_status_t (EFIAPI *allocate_pool)(int, unsigned long, void **);
efi_status_t (EFIAPI *free_pool)(void *);

- void *create_event;
- void *set_timer;
- efi_status_t(EFIAPI *wait_for_event)(unsigned long number_of_events,
- void *event, unsigned long *index);
- void *signal_event;
- void *close_event;
- void *check_event;
-
- void *install_protocol_interface;
- void *reinstall_protocol_interface;
- void *uninstall_protocol_interface;
+ efi_status_t (EFIAPI *create_event)(enum efi_event_type type,
+ unsigned long notify_tpl,
+ void (*notify_function) (void *event, void *context),
+ void *notify_context, void **event);
+ efi_status_t (EFIAPI *set_timer)(void *event, int type,
+ uint64_t trigger_time);
+ efi_status_t (EFIAPI *wait_for_event)(unsigned long number_of_events,
+ void *event, unsigned long *index);
+ efi_status_t (EFIAPI *signal_event)(void *event);
+ efi_status_t (EFIAPI *close_event)(void *event);
+ efi_status_t (EFIAPI *check_event)(void *event);
+
+ efi_status_t (EFIAPI *install_protocol_interface)(
+ void **handle, efi_guid_t *protocol,
+ int protocol_interface_type, void *protocol_interface);
+ efi_status_t (EFIAPI *reinstall_protocol_interface)(
+ void *handle, efi_guid_t *protocol,
+ void *old_interface, void *new_interface);
+ efi_status_t (EFIAPI *uninstall_protocol_interface)(void *handle,
+ efi_guid_t *protocol, void *protocol_interface);
efi_status_t (EFIAPI *handle_protocol)(efi_handle_t, efi_guid_t *,
void **);
void *reserved;
- void *register_protocol_notify;
+ efi_status_t (EFIAPI *register_protocol_notify)(
+ efi_guid_t *protocol, void *event,
+ void **registration);
efi_status_t (EFIAPI *locate_handle)(
enum efi_locate_search_type search_type,
efi_guid_t *protocol, void *search_key,
@@ -54,7 +72,8 @@ struct efi_boot_services {
efi_status_t (EFIAPI *locate_device_path)(efi_guid_t *protocol,
struct efi_device_path **device_path,
efi_handle_t *device);
- void *install_configuration_table;
+ efi_status_t (EFIAPI *install_configuration_table)(
+ efi_guid_t *guid, void *table);

efi_status_t (EFIAPI *load_image)(bool boot_policiy,
efi_handle_t parent_image,
@@ -66,17 +85,20 @@ struct efi_boot_services {
efi_status_t (EFIAPI *exit)(efi_handle_t handle,
efi_status_t exit_status,
unsigned long exitdata_size, s16 *exitdata);
- void *unload_image;
+ efi_status_t (EFIAPI *unload_image)(void *image_handle);
efi_status_t (EFIAPI *exit_boot_services)(efi_handle_t, unsigned long);

efi_status_t (EFIAPI *get_next_monotonic_count)(u64 *count);
efi_status_t (EFIAPI *stall)(unsigned long usecs);
- void *set_watchdog_timer;
+ efi_status_t (EFIAPI *set_watchdog_timer)(unsigned long timeout,
+ uint64_t watchdog_code, unsigned long data_size,
+ uint16_t *watchdog_data);
efi_status_t(EFIAPI *connect_controller)(efi_handle_t controller_handle,
efi_handle_t *driver_image_handle,
struct efi_device_path *remaining_device_path,
bool recursive);
- void *disconnect_controller;
+ efi_status_t (EFIAPI *disconnect_controller)(void *controller_handle,
+ void *driver_image_handle, void *child_handle);
#define EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL 0x00000001
#define EFI_OPEN_PROTOCOL_GET_PROTOCOL 0x00000002
#define EFI_OPEN_PROTOCOL_TEST_PROTOCOL 0x00000004
@@ -87,7 +109,9 @@ struct efi_boot_services {
efi_guid_t *protocol, void **interface,
efi_handle_t agent_handle,
efi_handle_t controller_handle, u32 attributes);
- void *close_protocol;
+ efi_status_t (EFIAPI *close_protocol)(void *handle,
+ efi_guid_t *protocol, void *agent_handle,
+ void *controller_handle);
efi_status_t(EFIAPI *open_protocol_information)(efi_handle_t handle,
efi_guid_t *protocol,
struct efi_open_protocol_info_entry **entry_buffer,
@@ -99,12 +123,18 @@ struct efi_boot_services {
enum efi_locate_search_type search_type,
efi_guid_t *protocol, void *search_key,
unsigned long *no_handles, efi_handle_t **buffer);
- void *locate_protocol;
- void *install_multiple_protocol_interfaces;
- void *uninstall_multiple_protocol_interfaces;
- void *calculate_crc32;
- void *copy_mem;
- void *set_mem;
+ efi_status_t (EFIAPI *locate_protocol)(efi_guid_t *protocol,
+ void *registration, void **protocol_interface);
+ efi_status_t (EFIAPI *install_multiple_protocol_interfaces)(
+ void **handle, ...);
+ efi_status_t (EFIAPI *uninstall_multiple_protocol_interfaces)(
+ void *handle, ...);
+ efi_status_t (EFIAPI *calculate_crc32)(void *data,
+ unsigned long data_size, uint32_t *crc32);
+ void (EFIAPI *copy_mem)(void *destination, void *source,
+ unsigned long length);
+ void (EFIAPI *set_mem)(void *buffer, unsigned long size,
+ uint8_t value);
void *create_event_ex;
};

@@ -121,12 +151,19 @@ enum efi_reset_type {

struct efi_runtime_services {
struct efi_table_hdr hdr;
- void *get_time;
- void *set_time;
- void *get_wakeup_time;
- void *set_wakeup_time;
- void *set_virtual_address_map;
- void *convert_pointer;
+ efi_status_t (EFIAPI *get_time)(struct efi_time *time,
+ struct efi_time_cap *capabilities);
+ efi_status_t (EFIAPI *set_time)(struct efi_time *time);
+ efi_status_t (EFIAPI *get_wakeup_time)(char *enabled, char *pending,
+ struct efi_time *time);
+ efi_status_t (EFIAPI *set_wakeup_time)(char enabled,
+ struct efi_time *time);
+ efi_status_t (EFIAPI *set_virtual_address_map)(
+ unsigned long memory_map_size,
+ unsigned long descriptor_size,
+ uint32_t descriptor_version,
+ struct efi_mem_desc *virtmap);
+ efi_status_t (*convert_pointer)(unsigned long dbg, void **address);
efi_status_t (EFIAPI *get_variable)(s16 *variable_name,
efi_guid_t *vendor, u32 *attributes,
unsigned long *data_size, void *data);
@@ -136,7 +173,8 @@ struct efi_runtime_services {
efi_status_t (EFIAPI *set_variable)(s16 *variable_name,
efi_guid_t *vendor, u32 attributes,
unsigned long data_size, void *data);
- void *get_next_high_mono_count;
+ efi_status_t (EFIAPI *get_next_high_mono_count)(
+ uint32_t *high_count);
void (EFIAPI *reset_system)(enum efi_reset_type reset_type,
efi_status_t reset_status,
unsigned long data_size, void *reset_data);
@@ -154,6 +192,18 @@ struct efi_runtime_services {
EFI_GUID(0x5b1b31a1, 0x9562, 0x11d2, 0x8e, 0x3f, \
0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b)

+#define EFI_FDT_GUID \
+ EFI_GUID(0xb1b621d5, 0xf19c, 0x41a5, \
+ 0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0)
+
+struct efi_configuration_table
+{
+ efi_guid_t guid;
+ void *table;
+};
+
+#define EFI_SYSTEM_TABLE_SIGNATURE ((u64)0x5453595320494249ULL)
+
struct efi_system_table {
struct efi_table_hdr hdr;
unsigned long fw_vendor; /* physical addr of wchar_t vendor string */
@@ -163,13 +213,17 @@ struct efi_system_table {
unsigned long con_out_handle;
struct efi_simple_text_output_protocol *con_out;
unsigned long stderr_handle;
- unsigned long std_err;
+ struct efi_simple_text_output_protocol *std_err;
struct efi_runtime_services *runtime;
struct efi_boot_services *boottime;
unsigned long nr_tables;
- unsigned long tables;
+ struct efi_configuration_table *tables;
};

+#define LOADED_IMAGE_GUID \
+ EFI_GUID(0x5b1b31a1, 0x9562, 0x11d2, \
+ 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
+
struct efi_loaded_image {
u32 revision;
void *parent_handle;
@@ -186,12 +240,60 @@ struct efi_loaded_image {
unsigned long unload;
};

+#define DEVICE_PATH_GUID \
+ EFI_GUID(0x09576e91, 0x6d3f, 0x11d2, \
+ 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b )
+
+#define DEVICE_PATH_TYPE_END 0x7f
+# define DEVICE_PATH_SUB_TYPE_END 0xff
+
struct efi_device_path {
u8 type;
u8 sub_type;
u16 length;
};

+#define DEVICE_PATH_TYPE_MEDIA_DEVICE 0x04
+# define DEVICE_PATH_SUB_TYPE_FILE_PATH 0x04
+
+struct efi_device_path_file_path {
+ struct efi_device_path dp;
+ u16 str[16];
+};
+
+#define BLOCK_IO_GUID \
+ EFI_GUID(0x964e5b21, 0x6459, 0x11d2, \
+ 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
+
+struct efi_block_io_media
+{
+ u32 media_id;
+ char removable_media;
+ char media_present;
+ char logical_partition;
+ char read_only;
+ char write_caching;
+ u8 pad[3];
+ u32 block_size;
+ u32 io_align;
+ u8 pad2[4];
+ u64 last_block;
+};
+
+struct efi_block_io {
+ u64 revision;
+ struct efi_block_io_media *media;
+ efi_status_t (EFIAPI *reset)(struct efi_block_io *this,
+ char extended_verification);
+ efi_status_t (EFIAPI *read_blocks)(struct efi_block_io *this,
+ u32 media_id, u64 lba, unsigned long buffer_size,
+ void *buffer);
+ efi_status_t (EFIAPI *write_blocks)(struct efi_block_io *this,
+ u32 media_id, u64 lba, unsigned long buffer_size,
+ void *buffer);
+ efi_status_t (EFIAPI *flush_blocks)(struct efi_block_io *this);
+};
+
struct simple_text_output_mode {
s32 max_mode;
s32 mode;
@@ -206,8 +308,9 @@ struct efi_simple_text_output_protocol {
efi_status_t (EFIAPI *output_string)(
struct efi_simple_text_output_protocol *this,
const unsigned short *str);
- void *test_string;
-
+ efi_status_t (EFIAPI *test_string)(
+ struct efi_simple_text_output_protocol *this,
+ const unsigned short *str);
efi_status_t(EFIAPI *query_mode)(
struct efi_simple_text_output_protocol *this,
unsigned long mode_number, unsigned long *columns,
@@ -223,7 +326,9 @@ struct efi_simple_text_output_protocol {
efi_status_t(EFIAPI *set_cursor_position) (
struct efi_simple_text_output_protocol *this,
unsigned long column, unsigned long row);
- efi_status_t(EFIAPI *enable_cursor)(void *, bool enable);
+ efi_status_t(EFIAPI *enable_cursor)(
+ struct efi_simple_text_output_protocol *this,
+ bool enable);
struct simple_text_output_mode *mode;
};

@@ -241,4 +346,22 @@ struct efi_simple_input_interface {
void *wait_for_key;
};

+#define CONSOLE_CONTROL_GUID \
+ EFI_GUID(0xf42f7782, 0x12e, 0x4c12, \
+ 0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21)
+#define EFI_CONSOLE_MODE_TEXT 0
+#define EFI_CONSOLE_MODE_GFX 1
+
+struct efi_console_control_protocol
+{
+ efi_status_t (EFIAPI *get_mode)(
+ struct efi_console_control_protocol *this, int *mode,
+ char *uga_exists, char *std_in_locked);
+ efi_status_t (EFIAPI *set_mode)(
+ struct efi_console_control_protocol *this, int mode);
+ efi_status_t (EFIAPI *lock_std_in)(
+ struct efi_console_control_protocol *this,
+ uint16_t *password);
+};
+
#endif
--
2.1.4
Alexander Graf
2015-12-22 13:57:55 UTC
Permalink
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 <***@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
Matwey V. Kornilov
2015-12-24 11:15:52 UTC
Permalink
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.
Post by Alexander Graf
In order to execute an EFI application, we need to bridge the gap between
U-Boot's notion of executing images and EFI's notion of doing the same.
The best path forward IMHO here is to stick completely to the way U-Boot
deals with payloads. You manually load them using whatever method to RAM
and then have a simple boot command to execute them. So in our case, you
would do
# load mmc 0:1 $loadaddr grub.efi
# bootefi $loadaddr
which then gets you into a grub shell. Fdt information known to U-boot
via the fdt addr command is also passed to the EFI payload.
---
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
--
With best regards,
Matwey V. Kornilov
http://blog.matwey.name
xmpp://***@jabber.ru
Andreas Färber
2015-12-25 09:25:22 UTC
Permalink
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
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton; HRB 21284 (AG Nürnberg)
Matwey V. Kornilov
2015-12-25 09:40:25 UTC
Permalink
Post by Andreas Färber
[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.
Post by Andreas Färber
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
Post by Andreas Färber
Cheers,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton; HRB 21284 (AG Nürnberg)
--
With best regards,
Matwey V. Kornilov
http://blog.matwey.name
xmpp://***@jabber.ru
Tom Rini
2015-12-25 17:04:54 UTC
Permalink
Post by Matwey V. Kornilov
Post by Andreas Färber
[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.
Post by Andreas Färber
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.
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.
--
Tom
Leif Lindholm
2015-12-26 18:55:12 UTC
Permalink
Post by Andreas Färber
[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.
Post by Andreas Färber
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
Alexander Graf
2015-12-27 15:33:36 UTC
Permalink
Post by Leif Lindholm
Post by Andreas Färber
[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
Tom Rini
2015-12-25 16:58:28 UTC
Permalink
Post by Matwey V. Kornilov
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.
--
Tom
Leif Lindholm
2015-12-26 18:45:24 UTC
Permalink
Post by Matwey V. Kornilov
Why just not to implement standard EFI behaviour when EFI looks for
boot-efi partition and proceed?
Well, what is "standard EFI behavior"?
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.
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.
Post by Matwey V. Kornilov
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
Alexander Graf
2015-12-22 13:57:51 UTC
Permalink
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 <***@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,
+};
--
2.1.4
Andreas Färber
2015-12-22 14:15:32 UTC
Permalink
Post by 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
[...]
Post by Alexander Graf
+/*
+ * 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"?
Post by Alexander Graf
+ * 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
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton; HRB 21284 (AG Nürnberg)
Alexander Graf
2015-12-22 14:31:21 UTC
Permalink
Post by Andreas Färber
Post by 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
[...]
Post by Alexander Graf
+/*
+ * 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
Leif Lindholm
2015-12-26 18:09:01 UTC
Permalink
Post by Alexander Graf
When an EFI application runs, it has access to a few descriptor and callback
tables to instruct the EFI compliant firmware to do things for it. The bulk
of those interfaces are "boot time services". They handle all object management,
and memory allocation.
This patch adds support for the boot time services and also exposes a system
table, which is the point of entry descriptor table for EFI payloads.
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...
Post by Alexander Graf
---
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.
Post by Alexander Graf
+}
+
+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()."
Post by Alexander Graf
+}
+
+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)
Post by Alexander Graf
+}
+
+static void *efi_alloc(uint64_t len, int memory_type)
+{
+ switch (memory_type) {
+ return efi_loader_alloc(len);
+ 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) {
+ /* Any page means we can go to efi_alloc */
+ *memory = (unsigned long)efi_alloc(len, memory_type);
+ break;
+ /* 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?
Post by Alexander Graf
+ break;
+ /* 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.
Post by Alexander Graf
+ break;
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.
Post by Alexander Graf
+ r = EFI_UNSUPPORTED;
Actually, not a valid return value.
EFI_INVALID_PARAMETER
Post by Alexander Graf
+ 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);
+}
+
+/*
+ *
+ * ____________________________ 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?
Post by Alexander Graf
+#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 "..."
?
Post by Alexander Graf
+#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.
Post by Alexander Graf
+ }
+
+ 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?
Post by Alexander Graf
+
+ 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.
Post by Alexander Graf
+ }
+
+ switch (type) {
+ efi_event.trigger_next = -1ULL;
+ break;
+ efi_event.trigger_next = timer_get_us() + (trigger32 / 10);
+ break;
+ 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) {
+ return 0;
+ return -1;
+ 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
Post by Alexander Graf
+ 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.)
Post by Alexander Graf
+
+ 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;
+ }
+ }
+ }
+
+ 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', ' ',
... :)
Post by Alexander Graf
+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.
Post by Alexander Graf
+ .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
Alexander Graf
2016-01-15 00:13:15 UTC
Permalink
Post by Leif Lindholm
Post by Alexander Graf
When an EFI application runs, it has access to a few descriptor and callback
tables to instruct the EFI compliant firmware to do things for it. The bulk
of those interfaces are "boot time services". They handle all object management,
and memory allocation.
This patch adds support for the boot time services and also exposes a system
table, which is the point of entry descriptor table for EFI payloads.
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...
Post by Alexander Graf
---
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.
Post by Alexander Graf
+}
+
+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.
Post by Leif Lindholm
Post by Alexander Graf
+}
+
+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 :).
Post by Leif Lindholm
Post by Alexander Graf
+}
+
+static void *efi_alloc(uint64_t len, int memory_type)
+{
+ switch (memory_type) {
+ return efi_loader_alloc(len);
+ 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) {
+ /* Any page means we can go to efi_alloc */
+ *memory = (unsigned long)efi_alloc(len, memory_type);
+ break;
+ /* 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?
Post by Alexander Graf
+ break;
+ /* 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.
Post by Leif Lindholm
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?
Post by Leif Lindholm
Post by Alexander Graf
+ break;
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.
Post by Alexander Graf
+ r = EFI_UNSUPPORTED;
Actually, not a valid return value.
EFI_INVALID_PARAMETER
Post by Alexander Graf
+ 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);
+}
+
+/*
+ *
+ * ____________________________ 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.
Post by Leif Lindholm
Post by Alexander Graf
+#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 :).
Post by Leif Lindholm
Post by Alexander Graf
+#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.
Post by Leif Lindholm
Post by Alexander Graf
+ }
+
+ 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
Post by Leif Lindholm
Post by Alexander Graf
+
+ 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.
Post by Leif Lindholm
Post by Alexander Graf
+ }
+
+ switch (type) {
+ efi_event.trigger_next = -1ULL;
+ break;
+ efi_event.trigger_next = timer_get_us() + (trigger32 / 10);
+ break;
+ return EFI_EXIT(EFI_UNSUPPORTED);
+ }
+ efi_event.trigger_type = type;
+ efi_event.trigger_time = trigger_time;
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
[...]
Post by Leif Lindholm
Post by Alexander Graf
+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!
Post by Leif Lindholm
Post by Alexander Graf
+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
Leif Lindholm
2016-01-15 13:02:01 UTC
Permalink
Post by Alexander Graf
Post by Leif Lindholm
Post by 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
@@ -0,0 +1,838 @@
+/*
+ * EFI application boot time services
+ *
...
Post by Alexander Graf
Post by Leif Lindholm
Post by Alexander Graf
+
+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.
Post by Alexander Graf
Post by Leif Lindholm
Post by Alexander Graf
+}
+
+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.
Post by Alexander Graf
Post by Leif Lindholm
Post by Alexander Graf
+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) {
+ /* Any page means we can go to efi_alloc */
+ *memory = (unsigned long)efi_alloc(len, memory_type);
+ break;
+ /* 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?
Post by Alexander Graf
+ break;
+ /* 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.
Post by Leif Lindholm
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.
Post by Alexander Graf
Post by Leif Lindholm
Post by Alexander Graf
+ 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).
Post by Alexander Graf
Post by Leif Lindholm
Post by Alexander Graf
+#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
Alexander Graf
2016-01-15 14:14:54 UTC
Permalink
Post by Leif Lindholm
Post by Alexander Graf
Post by Leif Lindholm
Post by 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
@@ -0,0 +1,838 @@
+/*
+ * EFI application boot time services
+ *
...
Post by Alexander Graf
Post by Leif Lindholm
Post by Alexander Graf
+
+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.
Post by Alexander Graf
Post by Leif Lindholm
Post by Alexander Graf
+}
+
+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.
Post by Alexander Graf
Post by Leif Lindholm
Post by Alexander Graf
+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) {
+ /* Any page means we can go to efi_alloc */
+ *memory = (unsigned long)efi_alloc(len, memory_type);
+ break;
+ /* 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?
Post by Alexander Graf
+ break;
+ /* 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.
Post by Leif Lindholm
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.
Post by Alexander Graf
Post by Leif Lindholm
Post by Alexander Graf
+ 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?
Post by Leif Lindholm
Post by Alexander Graf
Post by Leif Lindholm
Post by Alexander Graf
+#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
Leif Lindholm
2016-01-15 14:21:41 UTC
Permalink
Post by Alexander Graf
Post by Leif Lindholm
Post by Alexander Graf
Post by Leif Lindholm
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.
Post by Alexander Graf
With the UEFI entry path, Linux will ignore memory nodes in dt, right?
Yep.
Post by Alexander Graf
Post by Leif Lindholm
Post by Alexander Graf
Post by Leif Lindholm
Post by Alexander Graf
+#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
Alexander Graf
2016-01-15 17:04:33 UTC
Permalink
Post by Leif Lindholm
Post by Alexander Graf
Post by Leif Lindholm
Post by Alexander Graf
Post by Leif Lindholm
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.
Post by Alexander Graf
With the UEFI entry path, Linux will ignore memory nodes in dt, right?
Yep.
Post by Alexander Graf
Post by Leif Lindholm
Post by Alexander Graf
Post by Leif Lindholm
Post by Alexander Graf
+#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

Alexander Graf
2016-01-15 03:40:55 UTC
Permalink
[...]
Post by Leif Lindholm
Post by Alexander Graf
+ break;
+ /* 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
Alexander Graf
2015-12-22 13:57:53 UTC
Permalink
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 <***@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,
+};
--
2.1.4
Leif Lindholm
2015-12-26 18:33:04 UTC
Permalink
Post by Alexander Graf
After booting has finished, EFI allows firmware to still interact with the OS
using the "runtime services". These callbacks live in a separate address space,
since they are available long after U-Boot has been overwritten by the OS.
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.
---
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)
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)
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.
Post by Alexander Graf
+}
+
+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
Post by Alexander Graf
+ .set_time = (void *)&efi_unimplemented,
EFI_DEVICE_ERROR
Post by Alexander Graf
+ .get_wakeup_time = (void *)&efi_unimplemented,
+ .set_wakeup_time = (void *)&efi_unimplemented,
Both of these are fine, and correct, to return EFI_UNSUPPORTED.
Post by Alexander Graf
+ .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.
Post by Alexander Graf
+ .get_variable = (void *)&efi_unimplemented,
EFI_DEVICE_ERROR would probably be the closest thing to a correct
return code in this instance.
Post by Alexander Graf
+ .get_next_variable = (void *)&efi_unimplemented,
(get_next_variable_name?)
Again, EFI_DEVICE_ERROR, is probably the least wrong return value.
Post by Alexander Graf
+ .set_variable = (void *)&efi_unimplemented,
EFI_DEVICE_ERROR
Post by Alexander Graf
+ .get_next_high_mono_count = (void *)&efi_unimplemented,
EFI_DEVICE_ERROR
Post by Alexander Graf
+ .reset_system = (void *)&efi_unimplemented,
"The ResetSystem() function does not return."
Post by Alexander Graf
+};
--
2.1.4
Alexander Graf
2016-01-15 00:26:42 UTC
Permalink
Post by Leif Lindholm
Post by Alexander Graf
After booting has finished, EFI allows firmware to still interact with the OS
using the "runtime services". These callbacks live in a separate address space,
since they are available long after U-Boot has been overwritten by the OS.
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.
---
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)
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)
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.
Post by Alexander Graf
+}
+
+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
Post by Alexander Graf
+ .set_time = (void *)&efi_unimplemented,
EFI_DEVICE_ERROR
Post by Alexander Graf
+ .get_wakeup_time = (void *)&efi_unimplemented,
+ .set_wakeup_time = (void *)&efi_unimplemented,
Both of these are fine, and correct, to return EFI_UNSUPPORTED.
Post by Alexander Graf
+ .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 :).
Post by Leif Lindholm
Post by Alexander Graf
+ .get_variable = (void *)&efi_unimplemented,
EFI_DEVICE_ERROR would probably be the closest thing to a correct
return code in this instance.
Post by Alexander Graf
+ .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);
Post by Leif Lindholm
Again, EFI_DEVICE_ERROR, is probably the least wrong return value.
Post by Alexander Graf
+ .set_variable = (void *)&efi_unimplemented,
EFI_DEVICE_ERROR
Post by Alexander Graf
+ .get_next_high_mono_count = (void *)&efi_unimplemented,
EFI_DEVICE_ERROR
Ok, fixed all of the above.
Post by Leif Lindholm
Post by Alexander Graf
+ .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
Leif Lindholm
2016-01-15 13:52:14 UTC
Permalink
Post by Alexander Graf
Post by Leif Lindholm
Post by Alexander Graf
+ .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
Alexander Graf
2016-01-15 14:15:37 UTC
Permalink
Post by Leif Lindholm
Post by Alexander Graf
Post by Leif Lindholm
Post by Alexander Graf
+ .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
Leif Lindholm
2016-01-15 14:22:27 UTC
Permalink
Post by Alexander Graf
Post by Leif Lindholm
Post by Alexander Graf
Post by Leif Lindholm
"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
Alexander Graf
2015-12-22 13:57:56 UTC
Permalink
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 <***@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
--
2.1.4
Alexander Graf
2015-12-22 13:57:50 UTC
Permalink
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 <***@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 = &sections[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 = &sections[i];
+ memset(efi_reloc + sec->VirtualAddress, 0,
+ sec->Misc.VirtualSize);
+ memcpy(efi_reloc + sec->VirtualAddress,
+ efi + sec->PointerToRawData,
+ sec->SizeOfRawData);
+ }
+
+
+ /* Run through relocations */
+ end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + rel_size);
+
+ while (rel < end - 1 && rel->SizeOfBlock) {
+ relocs = (const uint16_t *)(rel + 1);
+ i = (rel->SizeOfBlock - sizeof(*rel)) / sizeof(uint16_t);
+ while (i--) {
+ uint16_t offset = (*relocs & 0xfff) + rel->VirtualAddress;
+ int type = *relocs >> 12;
+ unsigned long delta = (unsigned long)efi_reloc;
+ uint64_t *x64 = efi_reloc + offset;
+ uint32_t *x32 = efi_reloc + offset;
+ uint16_t *x16 = efi_reloc + offset;
+
+ switch (type) {
+ case IMAGE_REL_BASED_ABSOLUTE:
+ break;
+ case IMAGE_REL_BASED_HIGH:
+ *x16 += ((uint32_t)delta) >> 16;
+ break;
+ case IMAGE_REL_BASED_LOW:
+ *x16 += (uint16_t)delta;
+ break;
+ case IMAGE_REL_BASED_HIGHLOW:
+ *x32 += (uint32_t)delta;
+ break;
+ case IMAGE_REL_BASED_DIR64:
+ *x64 += (uint64_t)delta;
+ break;
+ default:
+ printf("Unknown Relocation off %x type %x\n",
+ offset, type);
+ }
+ relocs++;
+ }
+ rel = (const IMAGE_BASE_RELOCATION *)relocs;
+ }
+
+ /* Populate the loaded image interface bits */
+ loaded_image_info->image_base = efi;
+ loaded_image_info->image_size = image_size;
+
+ return entry;
+}
--
2.1.4
Leif Lindholm
2015-12-26 16:26:33 UTC
Permalink
Post by Alexander Graf
EFI uses the PE binary format for its application images. Add support to EFI PE
binaries as well as all necessary bits for the "EFI image loader" interfaces.
---
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.
Post by Alexander Graf
+ * 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.
Post by Alexander Graf
+ */
+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 = &sections[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)
Post by Alexander Graf
+ 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?
Post by Alexander Graf
+ 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 = &sections[i];
+ memset(efi_reloc + sec->VirtualAddress, 0,
+ sec->Misc.VirtualSize);
+ memcpy(efi_reloc + sec->VirtualAddress,
+ efi + sec->PointerToRawData,
+ sec->SizeOfRawData);
+ }
+
+
+ /* Run through relocations */
+ end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + rel_size);
+
+ while (rel < end - 1 && rel->SizeOfBlock) {
+ relocs = (const uint16_t *)(rel + 1);
+ i = (rel->SizeOfBlock - sizeof(*rel)) / sizeof(uint16_t);
+ while (i--) {
+ uint16_t offset = (*relocs & 0xfff) + rel->VirtualAddress;
+ int type = *relocs >> 12;
+ unsigned long delta = (unsigned long)efi_reloc;
+ uint64_t *x64 = efi_reloc + offset;
+ uint32_t *x32 = efi_reloc + offset;
+ uint16_t *x16 = efi_reloc + offset;
+
+ switch (type) {
+ break;
+ *x16 += ((uint32_t)delta) >> 16;
+ break;
+ *x16 += (uint16_t)delta;
+ break;
+ *x32 += (uint32_t)delta;
+ break;
+ *x64 += (uint64_t)delta;
+ break;
+ 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
Alexander Graf
2016-01-14 23:45:46 UTC
Permalink
Post by Leif Lindholm
Post by Alexander Graf
EFI uses the PE binary format for its application images. Add support to EFI PE
binaries as well as all necessary bits for the "EFI image loader" interfaces.
---
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 :).
Post by Leif Lindholm
Assigning a specific 128MB pool for LOADER_DATA memory can have
unexpected side effects.
Like for example?
Post by Leif Lindholm
Post by Alexander Graf
+ * 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 :).
Post by Leif Lindholm
Post by Alexander Graf
+ */
+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 = &sections[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.
Post by Leif Lindholm
Post by Alexander Graf
+ 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
Leif Lindholm
2016-01-15 12:29:53 UTC
Permalink
Post by Alexander Graf
Post by Leif Lindholm
Post by Alexander Graf
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.
Post by Alexander Graf
Post by Leif Lindholm
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
Alexander Graf
2015-12-22 13:57:52 UTC
Permalink
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 <***@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,
+};
--
2.1.4
Matwey V. Kornilov
2015-12-22 18:28:02 UTC
Permalink
This is my Christmas present for my openSUSE friends :).
Santa, do you have u-boot rpm packed with the patches to test?
--
With best regards,
Matwey V. Kornilov
http://blog.matwey.name
xmpp://***@jabber.ru
Alexander Graf
2015-12-22 20:32:24 UTC
Permalink
Post by Matwey V. Kornilov
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
Tom Rini
2015-12-25 03:29:39 UTC
Permalink
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?
--
Tom
Tom Rini
2015-12-25 16:50:18 UTC
Permalink
Post by Tom Rini
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!
--
Tom
Matwey V. Kornilov
2015-12-25 16:53:24 UTC
Permalink
Post by Tom Rini
Post by Tom Rini
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?
--
With best regards,
Matwey V. Kornilov
http://blog.matwey.name
xmpp://***@jabber.ru
Tom Rini
2015-12-25 17:00:40 UTC
Permalink
Post by Matwey V. Kornilov
Post by Tom Rini
Post by Tom Rini
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.
--
Tom
Alexander Graf
2016-01-15 03:00:21 UTC
Permalink
Post by Tom Rini
Post by Tom Rini
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
Tom Rini
2016-01-15 03:06:36 UTC
Permalink
Post by Alexander Graf
Post by Tom Rini
Post by Tom Rini
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 :)
Post by Alexander Graf
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!
--
Tom
Blibbet
2015-12-25 19:34:13 UTC
Permalink
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
Leif Lindholm
2015-12-26 15:31:03 UTC
Permalink
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.
- 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
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
Alexander Graf
2015-12-26 16:27:48 UTC
Permalink
Post by Leif Lindholm
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?
Post by Leif Lindholm
- 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.
Post by Leif Lindholm
- 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.
Post by Leif Lindholm
- 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.
Post by Leif Lindholm
- Make EFI Shell work ;)
- Network device support.
Oh, good point. I forgot about that one.
Post by Leif Lindholm
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
Leif Lindholm
2015-12-26 19:34:50 UTC
Permalink
Post by Alexander Graf
Post by Leif Lindholm
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.
Post by Alexander Graf
Post by Leif Lindholm
- 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.
Post by Alexander Graf
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.
Post by Alexander Graf
Post by Leif Lindholm
- 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.
Post by Alexander Graf
Post by Leif Lindholm
- 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".
Post by Alexander Graf
Post by Leif Lindholm
- Make EFI Shell work ;)
- Network device support.
Oh, good point. I forgot about that one.
Post by Leif Lindholm
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
Tom Rini
2016-01-04 16:56:45 UTC
Permalink
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 :)
--
Tom
Andreas Färber
2016-01-04 18:03:33 UTC
Permalink
Post by 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
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton; HRB 21284 (AG Nürnberg)
Andreas Färber
2016-01-04 18:41:42 UTC
Permalink
Post by 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
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton; HRB 21284 (AG Nürnberg)
Tom Rini
2016-01-04 19:54:40 UTC
Permalink
Post by Andreas Färber
Post by 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?
--
Tom
Dennis Gilmore
2016-01-04 22:37:54 UTC
Permalink
Post by Tom Rini
Post by Andreas Färber
Post by 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
Post by Tom Rini
Post by Andreas Färber
Post by Tom Rini
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
Alexander Graf
2016-01-04 22:48:26 UTC
Permalink
Post by Dennis Gilmore
Post by Tom Rini
Post by Andreas Färber
Post by 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
Post by Tom Rini
Post by Andreas Färber
Post by Tom Rini
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.
Post by Dennis Gilmore
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.
Post by Dennis Gilmore
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
Peter Robinson
2016-01-15 03:40:35 UTC
Permalink
Post by Andreas Färber
Post by 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.
Post by Andreas Färber
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.
Post by Andreas Färber
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.
Matwey V. Kornilov
2016-01-04 20:11:55 UTC
Permalink
Post by Andreas Färber
[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?
Post by Andreas Färber
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
--
With best regards,
Matwey V. Kornilov
http://blog.matwey.name
xmpp://***@jabber.ru
Peter Robinson
2016-01-15 03:32:53 UTC
Permalink
Post by Tom Rini
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
Post by Tom Rini
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.
Tom Rini
2015-12-27 18:10:39 UTC
Permalink
Post by Leif Lindholm
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.
- 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!
--
Tom
Leif Lindholm
2015-12-27 18:39:16 UTC
Permalink
Post by Tom Rini
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.pdf

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
Tom Rini
2015-12-27 19:48:21 UTC
Permalink
Post by Leif Lindholm
Post by Tom Rini
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!
1) 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.
Post by Leif Lindholm
2) The potential complication would be with regards to implementing
http://uefi.org/sites/default/files/resources/UEFI_Adopters_Agreement_100907.pdf
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!
--
Tom
Tom Rini
2016-01-05 20:18:56 UTC
Permalink
Post by Leif Lindholm
Post by Tom Rini
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!
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
http://uefi.org/sites/default/files/resources/UEFI_Adopters_Agreement_100907.pdf
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.
--
Tom
Simon Glass
2016-01-15 01:37:42 UTC
Permalink
Hi Alexander,
Post by Alexander Graf
A EFI applications usually want to access storage devices to load data from.
This patch adds support for EFI disk interfaces. It loops through all block
storage interfaces known to U-Boot and creates an EFI object for each existing
one. EFI applications can then through these objects call U-Boot's read and
write functions.
---
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 <***@chromium.org>

Some nits below.
Post by Alexander Graf
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?
Post by Alexander Graf
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <part.h>
+#include <malloc.h>
+#include <inttypes.h>
inttypes.h should go above part.h
Post by Alexander Graf
+
+static const efi_guid_t efi_block_io_guid = BLOCK_IO_GUID;
+
+struct efi_disk_obj {
Could use comments on these members
Post by Alexander Graf
+ 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?
Post by Alexander Graf
+ 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.
Post by Alexander Graf
+ 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
Alexander Graf
2016-01-15 02:40:53 UTC
Permalink
Post by Simon Glass
Hi Alexander,
Post by Alexander Graf
A EFI applications usually want to access storage devices to load data from.
This patch adds support for EFI disk interfaces. It loops through all block
storage interfaces known to U-Boot and creates an EFI object for each existing
one. EFI applications can then through these objects call U-Boot's read and
write functions.
---
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
Some nits below.
Post by Alexander Graf
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?
Post by Alexander Graf
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <part.h>
+#include <malloc.h>
+#include <inttypes.h>
inttypes.h should go above part.h
Post by Alexander Graf
+
+static const efi_guid_t efi_block_io_guid = BLOCK_IO_GUID;
+
+struct efi_disk_obj {
Could use comments on these members
Post by Alexander Graf
+ 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
Loading...