summaryrefslogtreecommitdiff
path: root/lib/efi_loader
diff options
context:
space:
mode:
Diffstat (limited to 'lib/efi_loader')
-rw-r--r--lib/efi_loader/Kconfig26
-rw-r--r--lib/efi_loader/Makefile2
-rw-r--r--lib/efi_loader/efi_bootmgr.c19
-rw-r--r--lib/efi_loader/efi_boottime.c23
-rw-r--r--lib/efi_loader/efi_capsule.c12
-rw-r--r--lib/efi_loader/efi_console.c51
-rw-r--r--lib/efi_loader/efi_device_path.c151
-rw-r--r--lib/efi_loader/efi_device_path_to_text.c13
-rw-r--r--lib/efi_loader/efi_dt_fixup.c2
-rw-r--r--lib/efi_loader/efi_esrt.c510
-rw-r--r--lib/efi_loader/efi_file.c39
-rw-r--r--lib/efi_loader/efi_firmware.c4
-rw-r--r--lib/efi_loader/efi_helper.c98
-rw-r--r--lib/efi_loader/efi_load_initrd.c202
-rw-r--r--lib/efi_loader/efi_setup.c6
-rw-r--r--lib/efi_loader/efi_tcg2.c75
-rw-r--r--lib/efi_loader/efi_unicode_collation.c21
-rw-r--r--lib/efi_loader/efi_var_common.c33
18 files changed, 1112 insertions, 175 deletions
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index e729f727df1..e44f004f3f8 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -281,7 +281,7 @@ config EFI_HAVE_RUNTIME_RESET
config EFI_GRUB_ARM32_WORKAROUND
bool "Workaround for GRUB on 32bit ARM"
- default n if ARCH_QEMU
+ default n if ARCH_BCM283X || ARCH_SUNXI || ARCH_QEMU
default y
depends on ARM && !ARM64
help
@@ -315,18 +315,13 @@ config EFI_TCG2_PROTOCOL_EVENTLOG_SIZE
config EFI_LOAD_FILE2_INITRD
bool "EFI_FILE_LOAD2_PROTOCOL for Linux initial ramdisk"
- default n
- help
- Expose a EFI_FILE_LOAD2_PROTOCOL that the Linux UEFI stub can
- use to load the initial ramdisk. Once this is enabled using
- initrd=<ramdisk> will stop working.
-
-config EFI_INITRD_FILESPEC
- string "initramfs path"
- default "host 0:1 initrd"
- depends on EFI_LOAD_FILE2_INITRD
+ default y
help
- Full path of the initramfs file, e.g. mmc 0:2 initramfs.cpio.gz.
+ Linux v5.7 and later can make use of this option. If the boot option
+ selected by the UEFI boot manager specifies an existing file to be used
+ as initial RAM disk, a Linux specific Load File2 protocol will be
+ installed and Linux 5.7+ will ignore any initrd=<ramdisk> command line
+ argument.
config EFI_SECURE_BOOT
bool "Enable EFI secure boot support"
@@ -347,4 +342,11 @@ config EFI_SECURE_BOOT
it is signed with a trusted key. To do that, you need to install,
at least, PK, KEK and db.
+config EFI_ESRT
+ bool "Enable the UEFI ESRT generation"
+ depends on EFI_CAPSULE_FIRMWARE_MANAGEMENT
+ default y
+ help
+ Enabling this option creates the ESRT UEFI system table.
+
endif
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
index 10b42e8847b..8bd343e258a 100644
--- a/lib/efi_loader/Makefile
+++ b/lib/efi_loader/Makefile
@@ -23,6 +23,7 @@ endif
obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o
obj-$(CONFIG_CMD_BOOTEFI_BOOTMGR) += efi_bootmgr.o
obj-y += efi_boottime.o
+obj-y += efi_helper.o
obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += efi_capsule.o
obj-$(CONFIG_EFI_CAPSULE_FIRMWARE) += efi_firmware.o
obj-y += efi_console.o
@@ -52,6 +53,7 @@ obj-y += efi_variable.o
obj-$(CONFIG_EFI_VARIABLES_PRESEED) += efi_var_seed.o
endif
obj-y += efi_watchdog.o
+obj-$(CONFIG_EFI_ESRT) += efi_esrt.o
obj-$(CONFIG_LCD) += efi_gop.o
obj-$(CONFIG_DM_VIDEO) += efi_gop.o
obj-$(CONFIG_PARTITIONS) += efi_disk.o
diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
index 25f5cebfdb6..46c8011344b 100644
--- a/lib/efi_loader/efi_bootmgr.c
+++ b/lib/efi_loader/efi_bootmgr.c
@@ -118,11 +118,13 @@ static efi_status_t try_load_entry(u16 n, efi_handle_t *handle,
ret = efi_set_variable_int(L"BootCurrent",
&efi_global_variable_guid,
attributes, sizeof(n), &n, false);
- if (ret != EFI_SUCCESS) {
- if (EFI_CALL(efi_unload_image(*handle))
- != EFI_SUCCESS)
- log_err("Unloading image failed\n");
- goto error;
+ if (ret != EFI_SUCCESS)
+ goto unload;
+ /* try to register load file2 for initrd's */
+ if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD)) {
+ ret = efi_initrd_register();
+ if (ret != EFI_SUCCESS)
+ goto unload;
}
log_info("Booting: %ls\n", lo.label);
@@ -147,6 +149,13 @@ error:
free(load_option);
return ret;
+
+unload:
+ if (EFI_CALL(efi_unload_image(*handle)) != EFI_SUCCESS)
+ log_err("Unloading image failed\n");
+ free(load_option);
+
+ return ret;
}
/**
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index 41b8949b042..4777b35fd4f 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -1406,10 +1406,9 @@ out:
*
* Return: status code
*/
-static efi_status_t EFIAPI efi_register_protocol_notify(
- const efi_guid_t *protocol,
- struct efi_event *event,
- void **registration)
+efi_status_t EFIAPI efi_register_protocol_notify(const efi_guid_t *protocol,
+ struct efi_event *event,
+ void **registration)
{
struct efi_register_notify_event *item;
efi_status_t ret = EFI_SUCCESS;
@@ -1877,7 +1876,6 @@ static
efi_status_t efi_load_image_from_file(struct efi_device_path *file_path,
void **buffer, efi_uintn_t *size)
{
- struct efi_file_info *info = NULL;
struct efi_file_handle *f;
efi_status_t ret;
u64 addr;
@@ -1888,18 +1886,7 @@ efi_status_t efi_load_image_from_file(struct efi_device_path *file_path,
if (!f)
return EFI_NOT_FOUND;
- /* Get file size */
- bs = 0;
- EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid,
- &bs, info));
- if (ret != EFI_BUFFER_TOO_SMALL) {
- ret = EFI_DEVICE_ERROR;
- goto error;
- }
-
- info = malloc(bs);
- EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid, &bs,
- info));
+ ret = efi_file_size(f, &bs);
if (ret != EFI_SUCCESS)
goto error;
@@ -1909,7 +1896,6 @@ efi_status_t efi_load_image_from_file(struct efi_device_path *file_path,
* allocate a buffer as EFI_BOOT_SERVICES_DATA. The caller has to
* update the reservation according to the image type.
*/
- bs = info->file_size;
ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
EFI_BOOT_SERVICES_DATA,
efi_size_in_pages(bs), &addr);
@@ -1926,7 +1912,6 @@ efi_status_t efi_load_image_from_file(struct efi_device_path *file_path,
*size = bs;
error:
EFI_CALL(f->close(f));
- free(info);
return ret;
}
diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
index b57f0302c59..9df9c35084c 100644
--- a/lib/efi_loader/efi_capsule.c
+++ b/lib/efi_loader/efi_capsule.c
@@ -449,7 +449,7 @@ efi_status_t EFIAPI efi_update_capsule(
unsigned int i;
efi_status_t ret;
- EFI_ENTRY("%p, %lu, %llu\n", capsule_header_array, capsule_count,
+ EFI_ENTRY("%p, %zu, %llu\n", capsule_header_array, capsule_count,
scatter_gather_list);
if (!capsule_count) {
@@ -482,6 +482,14 @@ efi_status_t EFIAPI efi_update_capsule(
goto out;
}
out:
+
+ if (IS_ENABLED(CONFIG_EFI_ESRT)) {
+ /* Rebuild the ESRT to reflect any updated FW images. */
+ ret = efi_esrt_populate();
+ if (ret != EFI_SUCCESS)
+ log_warning("EFI Capsule: failed to update ESRT\n");
+ }
+
return EFI_EXIT(ret);
}
@@ -509,7 +517,7 @@ efi_status_t EFIAPI efi_query_capsule_caps(
unsigned int i;
efi_status_t ret;
- EFI_ENTRY("%p, %lu, %p, %p\n", capsule_header_array, capsule_count,
+ EFI_ENTRY("%p, %zu, %p, %p\n", capsule_header_array, capsule_count,
maximum_capsule_size, reset_type);
if (!maximum_capsule_size) {
diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c
index edcfce7becb..6040f3a99ab 100644
--- a/lib/efi_loader/efi_console.c
+++ b/lib/efi_loader/efi_console.c
@@ -254,7 +254,7 @@ static bool cout_mode_matches(struct cout_mode *mode, int rows, int cols)
}
/**
- * query_console_serial() - query console size
+ * query_console_serial() - query serial console size
*
* When using a serial console or the net console we can only devise the
* terminal size by querying the terminal using ECMA-48 control sequences.
@@ -300,6 +300,37 @@ out:
}
/**
+ * query_vidconsole() - query video console size
+ *
+ *
+ * @rows: pointer to return number of rows
+ * @cols: pointer to return number of columns
+ * Returns: 0 on success
+ */
+static int __maybe_unused query_vidconsole(int *rows, int *cols)
+{
+ const char *stdout_name = env_get("stdout");
+ struct stdio_dev *stdout_dev;
+ struct udevice *dev;
+ struct vidconsole_priv *priv;
+
+ if (!stdout_name || strncmp(stdout_name, "vidconsole", 10))
+ return -ENODEV;
+ stdout_dev = stdio_get_by_name("vidconsole");
+ if (!stdout_dev)
+ return -ENODEV;
+ dev = stdout_dev->priv;
+ if (!dev)
+ return -ENODEV;
+ priv = dev_get_uclass_priv(dev);
+ if (!priv)
+ return -ENODEV;
+ *rows = priv->rows;
+ *cols = priv->cols;
+ return 0;
+}
+
+/**
* query_console_size() - update the mode table.
*
* By default the only mode available is 80x25. If the console has at least 50
@@ -308,21 +339,15 @@ out:
*/
static void query_console_size(void)
{
- const char *stdout_name = env_get("stdout");
int rows = 25, cols = 80;
+ int ret = -ENODEV;
- if (stdout_name && !strcmp(stdout_name, "vidconsole") &&
- IS_ENABLED(CONFIG_DM_VIDEO)) {
- struct stdio_dev *stdout_dev =
- stdio_get_by_name("vidconsole");
- struct udevice *dev = stdout_dev->priv;
- struct vidconsole_priv *priv =
- dev_get_uclass_priv(dev);
- rows = priv->rows;
- cols = priv->cols;
- } else if (query_console_serial(&rows, &cols)) {
+ if IS_ENABLED(CONFIG_DM_VIDEO)
+ ret = query_vidconsole(&rows, &cols);
+ if (ret)
+ ret = query_console_serial(&rows, &cols);
+ if (ret)
return;
- }
/* Test if we can have Mode 1 */
if (cols >= 80 && rows >= 50) {
diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c
index c9315dd4585..4b20859b255 100644
--- a/lib/efi_loader/efi_device_path.c
+++ b/lib/efi_loader/efi_device_path.c
@@ -282,11 +282,31 @@ struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp)
return ndp;
}
-struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
- const struct efi_device_path *dp2)
+/**
+ * efi_dp_append_or_concatenate() - Append or concatenate two device paths.
+ * Concatenated device path will be separated
+ * by a sub-type 0xff end node
+ *
+ * @dp1: First device path
+ * @dp2: Second device path
+ * @concat: If true the two device paths will be concatenated and separated
+ * by an end of entrire device path sub-type 0xff end node.
+ * If true the second device path will be appended to the first and
+ * terminated by an end node
+ *
+ * Return:
+ * concatenated device path or NULL. Caller must free the returned value
+ */
+static struct
+efi_device_path *efi_dp_append_or_concatenate(const struct efi_device_path *dp1,
+ const struct efi_device_path *dp2,
+ bool concat)
{
struct efi_device_path *ret;
+ size_t end_size = sizeof(END);
+ if (concat)
+ end_size = 2 * sizeof(END);
if (!dp1 && !dp2) {
/* return an end node */
ret = efi_dp_dup(&END);
@@ -298,18 +318,58 @@ struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
/* both dp1 and dp2 are non-null */
unsigned sz1 = efi_dp_size(dp1);
unsigned sz2 = efi_dp_size(dp2);
- void *p = dp_alloc(sz1 + sz2 + sizeof(END));
+ void *p = dp_alloc(sz1 + sz2 + end_size);
if (!p)
return NULL;
+ ret = p;
memcpy(p, dp1, sz1);
+ p += sz1;
+
+ if (concat) {
+ memcpy(p, &END, sizeof(END));
+ p += sizeof(END);
+ }
+
/* the end node of the second device path has to be retained */
- memcpy(p + sz1, dp2, sz2 + sizeof(END));
- ret = p;
+ memcpy(p, dp2, sz2);
+ p += sz2;
+ memcpy(p, &END, sizeof(END));
}
return ret;
}
+/**
+ * efi_dp_append() - Append a device to an existing device path.
+ *
+ * @dp1: First device path
+ * @dp2: Second device path
+ *
+ * Return:
+ * concatenated device path or NULL. Caller must free the returned value
+ */
+struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
+ const struct efi_device_path *dp2)
+{
+ return efi_dp_append_or_concatenate(dp1, dp2, false);
+}
+
+/**
+ * efi_dp_concat() - Concatenate 2 device paths. The final device path will
+ * contain two device paths separated by and end node (0xff).
+ *
+ * @dp1: First device path
+ * @dp2: Second device path
+ *
+ * Return:
+ * concatenated device path or NULL. Caller must free the returned value
+ */
+struct efi_device_path *efi_dp_concat(const struct efi_device_path *dp1,
+ const struct efi_device_path *dp2)
+{
+ return efi_dp_append_or_concatenate(dp1, dp2, true);
+}
+
struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
const struct efi_device_path *node)
{
@@ -960,6 +1020,28 @@ struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
return start;
}
+struct efi_device_path *efi_dp_from_uart(void)
+{
+ void *buf, *pos;
+ struct efi_device_path_uart *uart;
+ size_t dpsize = sizeof(ROOT) + sizeof(*uart) + sizeof(END);
+
+ buf = dp_alloc(dpsize);
+ if (!buf)
+ return NULL;
+ pos = buf;
+ memcpy(pos, &ROOT, sizeof(ROOT));
+ pos += sizeof(ROOT);
+ uart = pos;
+ uart->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+ uart->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_UART;
+ uart->dp.length = sizeof(*uart);
+ pos += sizeof(*uart);
+ memcpy(pos, &END, sizeof(END));
+
+ return buf;
+}
+
#ifdef CONFIG_NET
struct efi_device_path *efi_dp_from_eth(void)
{
@@ -1086,7 +1168,6 @@ efi_status_t efi_dp_from_name(const char *dev, const char *devnr,
struct efi_device_path **device,
struct efi_device_path **file)
{
- int is_net;
struct blk_desc *desc = NULL;
struct disk_partition fs_partition;
int part = 0;
@@ -1096,8 +1177,15 @@ efi_status_t efi_dp_from_name(const char *dev, const char *devnr,
if (path && !file)
return EFI_INVALID_PARAMETER;
- is_net = !strcmp(dev, "Net");
- if (!is_net) {
+ if (!strcmp(dev, "Net")) {
+#ifdef CONFIG_NET
+ if (device)
+ *device = efi_dp_from_eth();
+#endif
+ } else if (!strcmp(dev, "Uart")) {
+ if (device)
+ *device = efi_dp_from_uart();
+ } else {
part = blk_get_device_part_str(dev, devnr, &desc, &fs_partition,
1);
if (part < 0 || !desc)
@@ -1105,11 +1193,6 @@ efi_status_t efi_dp_from_name(const char *dev, const char *devnr,
if (device)
*device = efi_dp_from_part(desc, part);
- } else {
-#ifdef CONFIG_NET
- if (device)
- *device = efi_dp_from_eth();
-#endif
}
if (!path)
@@ -1120,7 +1203,7 @@ efi_status_t efi_dp_from_name(const char *dev, const char *devnr,
s = filename;
while ((s = strchr(s, '/')))
*s++ = '\\';
- *file = efi_dp_from_file(is_net ? NULL : desc, part, filename);
+ *file = efi_dp_from_file(desc, part, filename);
if (!*file)
return EFI_INVALID_PARAMETER;
@@ -1160,3 +1243,43 @@ ssize_t efi_dp_check_length(const struct efi_device_path *dp,
dp = (const struct efi_device_path *)((const u8 *)dp + len);
}
}
+
+/**
+ * efi_dp_from_lo() - Get the instance of a VenMedia node in a
+ * multi-instance device path that matches
+ * a specific GUID. This kind of device paths
+ * is found in Boot#### options describing an
+ * initrd location
+ *
+ * @lo: EFI_LOAD_OPTION containing a valid device path
+ * @size: size of the discovered device path
+ * @guid: guid to search for
+ *
+ * Return:
+ * device path including the VenMedia node or NULL.
+ * Caller must free the returned value.
+ */
+struct
+efi_device_path *efi_dp_from_lo(struct efi_load_option *lo,
+ efi_uintn_t *size, efi_guid_t guid)
+{
+ struct efi_device_path *fp = lo->file_path;
+ struct efi_device_path_vendor *vendor;
+ int lo_len = lo->file_path_length;
+
+ for (; lo_len >= sizeof(struct efi_device_path);
+ lo_len -= fp->length, fp = (void *)fp + fp->length) {
+ if (lo_len < 0 || efi_dp_check_length(fp, lo_len) < 0)
+ break;
+ if (fp->type != DEVICE_PATH_TYPE_MEDIA_DEVICE ||
+ fp->sub_type != DEVICE_PATH_SUB_TYPE_VENDOR_PATH)
+ continue;
+
+ vendor = (struct efi_device_path_vendor *)fp;
+ if (!guidcmp(&vendor->guid, &guid))
+ return efi_dp_dup(fp);
+ }
+ log_debug("VenMedia(%pUl) not found in %ls\n", &guid, lo->label);
+
+ return NULL;
+}
diff --git a/lib/efi_loader/efi_device_path_to_text.c b/lib/efi_loader/efi_device_path_to_text.c
index edc9fdc3870..43554cd771b 100644
--- a/lib/efi_loader/efi_device_path_to_text.c
+++ b/lib/efi_loader/efi_device_path_to_text.c
@@ -118,6 +118,19 @@ static char *dp_msging(char *s, struct efi_device_path *dp)
ide->logical_unit_number);
break;
}
+ case DEVICE_PATH_SUB_TYPE_MSG_UART: {
+ struct efi_device_path_uart *uart =
+ (struct efi_device_path_uart *)dp;
+ s += sprintf(s, "Uart(%lld,%d,%d,", uart->baud_rate,
+ uart->data_bits, uart->parity);
+ switch (uart->stop_bits) {
+ case 2:
+ s += sprintf(s, "1.5)");
+ default:
+ s += sprintf(s, "%d)", uart->stop_bits);
+ }
+ break;
+ }
case DEVICE_PATH_SUB_TYPE_MSG_USB: {
struct efi_device_path_usb *udp =
(struct efi_device_path_usb *)dp;
diff --git a/lib/efi_loader/efi_dt_fixup.c b/lib/efi_loader/efi_dt_fixup.c
index a4529ee3ef2..b6fe5d2e5a3 100644
--- a/lib/efi_loader/efi_dt_fixup.c
+++ b/lib/efi_loader/efi_dt_fixup.c
@@ -61,7 +61,7 @@ void efi_carve_out_dt_rsv(void *fdt)
for (i = 0; i < nr_rsv; i++) {
if (fdt_get_mem_rsv(fdt, i, &addr, &size) != 0)
continue;
- efi_reserve_memory(addr, size, false);
+ efi_reserve_memory(addr, size, true);
}
/* process reserved-memory */
diff --git a/lib/efi_loader/efi_esrt.c b/lib/efi_loader/efi_esrt.c
new file mode 100644
index 00000000000..947bdb5e95f
--- /dev/null
+++ b/lib/efi_loader/efi_esrt.c
@@ -0,0 +1,510 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * EFI application ESRT tables support
+ *
+ * Copyright (C) 2021 Arm Ltd.
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <log.h>
+#include <efi_api.h>
+#include <malloc.h>
+
+const efi_guid_t efi_esrt_guid = EFI_SYSTEM_RESOURCE_TABLE_GUID;
+
+static struct efi_system_resource_table *esrt;
+
+#define EFI_ESRT_VERSION 1
+
+/**
+ * efi_esrt_image_info_to_entry() - copy the information present in a fw image
+ * descriptor to a ESRT entry.
+ * The function ensures the ESRT entry matches the image_type_id in @img_info.
+ * In case of a mismatch we leave the entry unchanged.
+ *
+ * @img_info: the source image info descriptor
+ * @entry: pointer to the ESRT entry to be filled
+ * @desc_version: the version of the elements in img_info
+ * @image_type: the image type value to be set in the ESRT entry
+ * @flags: the capsule flags value to be set in the ESRT entry
+ *
+ * Return:
+ * - EFI_SUCCESS if the entry is correctly updated
+ * - EFI_INVALID_PARAMETER if entry does not match image_type_id in @img_info.
+ */
+static efi_status_t
+efi_esrt_image_info_to_entry(struct efi_firmware_image_descriptor *img_info,
+ struct efi_system_resource_entry *entry,
+ u32 desc_version, u32 image_type, u32 flags)
+{
+ if (guidcmp(&entry->fw_class, &img_info->image_type_id)) {
+ EFI_PRINT("ESRT entry %pUL mismatches img_type_id %pUL\n",
+ &entry->fw_class, &img_info->image_type_id);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ entry->fw_version = img_info->version;
+
+ entry->fw_type = image_type;
+ entry->capsule_flags = flags;
+
+ /*
+ * The field lowest_supported_image_version is only present
+ * on image info structure of version 2 or greater.
+ * See the EFI_FIRMWARE_IMAGE_DESCRIPTOR definition in UEFI.
+ */
+ if (desc_version >= 2)
+ entry->lowest_supported_fw_version =
+ img_info->lowest_supported_image_version;
+ else
+ entry->lowest_supported_fw_version = 0;
+
+ /*
+ * The fields last_attempt_version and last_attempt_status
+ * are only present on image info structure of version 3 or
+ * greater.
+ * See the EFI_FIRMWARE_IMAGE_DESCRIPTOR definition in UEFI.
+ */
+ if (desc_version >= 3) {
+ entry->last_attempt_version =
+ img_info->last_attempt_version;
+
+ entry->last_attempt_status =
+ img_info->last_attempt_status;
+ } else {
+ entry->last_attempt_version = 0;
+ entry->last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_esrt_entries_to_size() - Obtain the bytes used by an ESRT
+ * datastructure with @num_entries.
+ *
+ * @num_entries: the number of entries in the ESRT.
+ *
+ * Return: the number of bytes an ESRT with @num_entries occupies in memory.
+ */
+static
+inline u32 efi_esrt_entries_to_size(u32 num_entries)
+{
+ u32 esrt_size = sizeof(struct efi_system_resource_table) +
+ num_entries * sizeof(struct efi_system_resource_entry);
+
+ return esrt_size;
+}
+
+/**
+ * efi_esrt_allocate_install() - Allocates @num_entries for the ESRT and
+ * performs basic ESRT initialization.
+ *
+ * @num_entries: the number of entries that the ESRT will hold.
+ *
+ * Return:
+ * - pointer to the ESRT if successful.
+ * - NULL otherwise.
+ */
+static
+efi_status_t efi_esrt_allocate_install(u32 num_entries)
+{
+ efi_status_t ret;
+ struct efi_system_resource_table *new_esrt;
+ u32 size = efi_esrt_entries_to_size(num_entries);
+ efi_guid_t esrt_guid = efi_esrt_guid;
+
+ /* Reserve num_pages for ESRT */
+ ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, size,
+ (void **)&new_esrt);
+
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT cannot allocate memory for %d entries (%d bytes)\n",
+ num_entries, efi_esrt_entries_to_size(num_entries));
+
+ return ret;
+ }
+
+ new_esrt->fw_resource_count_max = num_entries;
+ new_esrt->fw_resource_count = 0;
+ new_esrt->fw_resource_version = EFI_ESRT_VERSION;
+
+ /* Install the ESRT in the system configuration table. */
+ ret = efi_install_configuration_table(&esrt_guid, (void *)new_esrt);
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to install the ESRT in the system table\n");
+ return ret;
+ }
+
+ /* If there was a previous ESRT, deallocate its memory now. */
+ if (esrt)
+ ret = EFI_CALL(efi_free_pool(esrt));
+
+ esrt = new_esrt;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * esrt_find_entry() - Obtain the ESRT entry for the image with GUID
+ * @img_fw_class.
+ *
+ * If the img_fw_class is not yet present in the ESRT, this function
+ * reserves the tail element of the current ESRT as the entry for that fw_class.
+ * The number of elements in the ESRT is updated in that case.
+ *
+ * @img_fw_class: the GUID of the FW image which ESRT entry we want to obtain.
+ *
+ * Return:
+ * - A pointer to the ESRT entry for the image with GUID img_fw_class,
+ * - NULL if:
+ * - there is no more space in the ESRT,
+ * - ESRT is not initialized,
+ */
+static
+struct efi_system_resource_entry *esrt_find_entry(efi_guid_t *img_fw_class)
+{
+ u32 filled_entries;
+ u32 max_entries;
+ struct efi_system_resource_entry *entry;
+
+ if (!esrt) {
+ EFI_PRINT("ESRT access before initialized\n");
+ return NULL;
+ }
+
+ filled_entries = esrt->fw_resource_count;
+ entry = esrt->entries;
+
+ /* Check if the image with img_fw_class is already in the ESRT. */
+ for (u32 idx = 0; idx < filled_entries; idx++) {
+ if (!guidcmp(&entry[idx].fw_class, img_fw_class)) {
+ EFI_PRINT("ESRT found entry for image %pUl at index %d\n",
+ img_fw_class, idx);
+ return &entry[idx];
+ }
+ }
+
+ max_entries = esrt->fw_resource_count_max;
+ /*
+ * Since the image with img_fw_class is not present in the ESRT, check
+ * if ESRT is full before appending the new entry to it.
+ */
+ if (filled_entries == max_entries) {
+ EFI_PRINT("ESRT full, this should not happen\n");
+ return NULL;
+ }
+
+ /*
+ * This is a new entry for a fw image, increment the element
+ * number in the table and set the fw_class field.
+ */
+ esrt->fw_resource_count++;
+ entry[filled_entries].fw_class = *img_fw_class;
+ EFI_PRINT("ESRT allocated new entry for image %pUl at index %d\n",
+ img_fw_class, filled_entries);
+
+ return &entry[filled_entries];
+}
+
+/**
+ * efi_esrt_add_from_fmp() - Populates a sequence of ESRT entries from the FW
+ * images in the FMP.
+ *
+ * @fmp: the FMP instance from which FW images are added to the ESRT
+ *
+ * Return:
+ * - EFI_SUCCESS if all the FW images in the FMP are added to the ESRT
+ * - Error status otherwise
+ */
+static
+efi_status_t efi_esrt_add_from_fmp(struct efi_firmware_management_protocol *fmp)
+{
+ struct efi_system_resource_entry *entry = NULL;
+ size_t info_size = 0;
+ struct efi_firmware_image_descriptor *img_info = NULL;
+ u32 desc_version;
+ u8 desc_count;
+ size_t desc_size;
+ u32 package_version;
+ u16 *package_version_name;
+ efi_status_t ret = EFI_SUCCESS;
+
+ /*
+ * TODO: set the field image_type depending on the FW image type
+ * defined in a platform basis.
+ */
+ u32 image_type = ESRT_FW_TYPE_UNKNOWN;
+
+ /* TODO: set the capsule flags as a function of the FW image type. */
+ u32 flags = 0;
+
+ ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, img_info,
+ &desc_version, &desc_count,
+ &desc_size, NULL, NULL));
+
+ if (ret != EFI_BUFFER_TOO_SMALL) {
+ /*
+ * An input of info_size=0 should always lead
+ * fmp->get_image_info to return BUFFER_TO_SMALL.
+ */
+ EFI_PRINT("Erroneous FMP implementation\n");
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ret = EFI_CALL(efi_allocate_pool(EFI_BOOT_SERVICES_DATA, info_size,
+ (void **)&img_info));
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to allocate memory for image info.\n");
+ return ret;
+ }
+
+ ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, img_info,
+ &desc_version, &desc_count,
+ &desc_size, &package_version,
+ &package_version_name));
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to obtain the FMP image info\n");
+ goto out;
+ }
+
+ /*
+ * Iterate over all the FW images in the FMP.
+ */
+ for (u32 desc_idx = 0; desc_idx < desc_count; desc_idx++) {
+ struct efi_firmware_image_descriptor *cur_img_info =
+ (struct efi_firmware_image_descriptor *)
+ ((uintptr_t)img_info + desc_idx * desc_size);
+
+ /*
+ * Obtain the ESRT entry for the FW image with fw_class
+ * equal to cur_img_info->image_type_id.
+ */
+ entry = esrt_find_entry(&cur_img_info->image_type_id);
+
+ if (entry) {
+ ret = efi_esrt_image_info_to_entry(cur_img_info, entry,
+ desc_version,
+ image_type, flags);
+ if (ret != EFI_SUCCESS)
+ EFI_PRINT("ESRT entry mismatches image_type\n");
+
+ } else {
+ EFI_PRINT("ESRT failed to add entry for %pUl\n",
+ &cur_img_info->image_type_id);
+ continue;
+ }
+ }
+
+out:
+ EFI_CALL(efi_free_pool(img_info));
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_esrt_populate() - Populates the ESRT entries from the FMP instances
+ * present in the system.
+ * If an ESRT already exists, the old ESRT is replaced in the system table.
+ * The memory of the old ESRT is deallocated.
+ *
+ * Return:
+ * - EFI_SUCCESS if the ESRT is correctly created
+ * - error code otherwise.
+ */
+efi_status_t efi_esrt_populate(void)
+{
+ efi_handle_t *base_handle = NULL;
+ efi_handle_t *it_handle;
+ size_t no_handles = 0;
+ struct efi_firmware_management_protocol *fmp;
+ efi_status_t ret;
+ u32 num_entries = 0;
+ struct efi_handler *handler;
+
+ /*
+ * Obtain the number of registered FMP handles.
+ */
+ ret = EFI_CALL(efi_locate_handle_buffer(BY_PROTOCOL,
+ &efi_guid_firmware_management_protocol,
+ NULL, &no_handles,
+ (efi_handle_t **)&base_handle));
+
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT There are no FMP instances\n");
+
+ ret = efi_esrt_allocate_install(0);
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to create table with 0 entries\n");
+ return ret;
+ }
+ return EFI_SUCCESS;
+ }
+
+ EFI_PRINT("ESRT populate esrt from (%ld) available FMP handles\n",
+ no_handles);
+
+ /*
+ * Iterate over all FMPs to determine an upper bound on the number of
+ * ESRT entries.
+ */
+ it_handle = base_handle;
+ for (u32 idx = 0; idx < no_handles; idx++, it_handle++) {
+ struct efi_firmware_image_descriptor *img_info = NULL;
+ size_t info_size = 0;
+ u32 desc_version = 0;
+ u8 desc_count = 0;
+ size_t desc_size = 0;
+ u32 package_version;
+ u16 *package_version_name;
+
+ ret = efi_search_protocol(*it_handle,
+ &efi_guid_firmware_management_protocol,
+ &handler);
+
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT Unable to find FMP handle (%d)\n",
+ idx);
+ goto out;
+ }
+ fmp = handler->protocol_interface;
+
+ ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, NULL,
+ &desc_version, &desc_count,
+ &desc_size, &package_version,
+ &package_version_name));
+
+ if (ret != EFI_BUFFER_TOO_SMALL) {
+ /*
+ * An input of info_size=0 should always lead
+ * fmp->get_image_info to return BUFFER_TO_SMALL.
+ */
+ EFI_PRINT("ESRT erroneous FMP implementation\n");
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ ret = EFI_CALL(efi_allocate_pool(EFI_BOOT_SERVICES_DATA, info_size,
+ (void **)&img_info));
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to allocate memory for image info\n");
+ goto out;
+ }
+
+ /*
+ * Calls to a FMP get_image_info method do not return the
+ * desc_count value if the return status differs from EFI_SUCCESS.
+ * We need to repeat the call to get_image_info with a properly
+ * sized buffer in order to obtain the real number of images
+ * handled by the FMP.
+ */
+ ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, img_info,
+ &desc_version, &desc_count,
+ &desc_size, &package_version,
+ &package_version_name));
+
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to obtain image info from FMP\n");
+ EFI_CALL(efi_free_pool(img_info));
+ goto out;
+ }
+
+ num_entries += desc_count;
+
+ EFI_CALL(efi_free_pool(img_info));
+ }
+
+ EFI_PRINT("ESRT create table with %d entries\n", num_entries);
+ /*
+ * Allocate an ESRT with the sufficient number of entries to accommodate
+ * all the FMPs in the system.
+ */
+ ret = efi_esrt_allocate_install(num_entries);
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to initialize table\n");
+ goto out;
+ }
+
+ /*
+ * Populate the ESRT entries with all existing FMP.
+ */
+ it_handle = base_handle;
+ for (u32 idx = 0; idx < no_handles; idx++, it_handle++) {
+ ret = EFI_CALL(efi_search_protocol(*it_handle,
+ &efi_guid_firmware_management_protocol,
+ &handler));
+
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT unable to find FMP handle (%d)\n",
+ idx);
+ break;
+ }
+ fmp = handler->protocol_interface;
+
+ ret = efi_esrt_add_from_fmp(fmp);
+ if (ret != EFI_SUCCESS)
+ EFI_PRINT("ESRT failed to add FMP to the table\n");
+ }
+
+out:
+
+ EFI_CALL(efi_free_pool(base_handle));
+
+ return ret;
+}
+
+/**
+ * efi_esrt_new_fmp_notify() - Callback for the EVT_NOTIFY_SIGNAL event raised
+ * when a new FMP protocol instance is registered in the system.
+ */
+static void EFIAPI efi_esrt_new_fmp_notify(struct efi_event *event,
+ void *context)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY();
+
+ ret = efi_esrt_populate();
+ if (ret != EFI_SUCCESS)
+ EFI_PRINT("ESRT failed to populate ESRT entry\n");
+
+ EFI_EXIT(ret);
+}
+
+/**
+ * efi_esrt_register() - Install the ESRT system table.
+ *
+ * Return: status code
+ */
+efi_status_t efi_esrt_register(void)
+{
+ struct efi_event *ev = NULL;
+ void *registration;
+ efi_status_t ret;
+
+ EFI_PRINT("ESRT creation start\n");
+
+ ret = efi_esrt_populate();
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to initiate the table\n");
+ return ret;
+ }
+
+ ret = EFI_CALL(efi_create_event(EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
+ efi_esrt_new_fmp_notify, NULL, NULL, &ev));
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to create event\n");
+ return ret;
+ }
+
+ ret = EFI_CALL(efi_register_protocol_notify(&efi_guid_firmware_management_protocol,
+ ev, &registration));
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to register FMP callback\n");
+ return ret;
+ }
+
+ EFI_PRINT("ESRT table created\n");
+
+ return ret;
+}
diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c
index 8ece8e71ee1..204105e25af 100644
--- a/lib/efi_loader/efi_file.c
+++ b/lib/efi_loader/efi_file.c
@@ -409,6 +409,45 @@ static efi_status_t efi_get_file_size(struct file_handle *fh,
return EFI_SUCCESS;
}
+/**
+ * efi_file_size() - Get the size of a file using an EFI file handle
+ *
+ * @fh: EFI file handle
+ * @size: buffer to fill in the discovered size
+ *
+ * Return: size of the file
+ */
+efi_status_t efi_file_size(struct efi_file_handle *fh, efi_uintn_t *size)
+{
+ struct efi_file_info *info = NULL;
+ efi_uintn_t bs = 0;
+ efi_status_t ret;
+
+ *size = 0;
+ ret = EFI_CALL(fh->getinfo(fh, (efi_guid_t *)&efi_file_info_guid, &bs,
+ info));
+ if (ret != EFI_BUFFER_TOO_SMALL) {
+ ret = EFI_DEVICE_ERROR;
+ goto out;
+ }
+
+ info = malloc(bs);
+ if (!info) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+ ret = EFI_CALL(fh->getinfo(fh, (efi_guid_t *)&efi_file_info_guid, &bs,
+ info));
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ *size = info->file_size;
+
+out:
+ free(info);
+ return ret;
+}
+
static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size,
void *buffer)
{
diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c
index 5e401bbca2b..7a3cca27936 100644
--- a/lib/efi_loader/efi_firmware.c
+++ b/lib/efi_loader/efi_firmware.c
@@ -299,7 +299,7 @@ efi_status_t EFIAPI efi_firmware_fit_set_image(
efi_status_t (*progress)(efi_uintn_t completion),
u16 **abort_reason)
{
- EFI_ENTRY("%p %d %p %ld %p %p %p\n", this, image_index, image,
+ EFI_ENTRY("%p %d %p %zd %p %p %p\n", this, image_index, image,
image_size, vendor_code, progress, abort_reason);
if (!image || image_index != 1)
@@ -414,7 +414,7 @@ efi_status_t EFIAPI efi_firmware_raw_set_image(
efi_status_t status;
efi_uintn_t capsule_payload_size;
- EFI_ENTRY("%p %d %p %ld %p %p %p\n", this, image_index, image,
+ EFI_ENTRY("%p %d %p %zd %p %p %p\n", this, image_index, image,
image_size, vendor_code, progress, abort_reason);
if (!image)
diff --git a/lib/efi_loader/efi_helper.c b/lib/efi_loader/efi_helper.c
new file mode 100644
index 00000000000..d03a7364615
--- /dev/null
+++ b/lib/efi_loader/efi_helper.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2020, Linaro Limited
+ */
+
+#define LOG_CATEGORY LOGC_EFI
+#include <common.h>
+#include <env.h>
+#include <malloc.h>
+#include <dm.h>
+#include <fs.h>
+#include <efi_load_initrd.h>
+#include <efi_loader.h>
+#include <efi_variable.h>
+
+/**
+ * efi_create_current_boot_var() - Return Boot#### name were #### is replaced by
+ * the value of BootCurrent
+ *
+ * @var_name: variable name
+ * @var_name_size: size of var_name
+ *
+ * Return: Status code
+ */
+static efi_status_t efi_create_current_boot_var(u16 var_name[],
+ size_t var_name_size)
+{
+ efi_uintn_t boot_current_size;
+ efi_status_t ret;
+ u16 boot_current;
+ u16 *pos;
+
+ boot_current_size = sizeof(boot_current);
+ ret = efi_get_variable_int(L"BootCurrent",
+ &efi_global_variable_guid, NULL,
+ &boot_current_size, &boot_current, NULL);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ pos = efi_create_indexed_name(var_name, var_name_size, "Boot",
+ boot_current);
+ if (!pos) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+/**
+ * efi_get_dp_from_boot() - Retrieve and return a device path from an EFI
+ * Boot### variable.
+ * A boot option may contain an array of device paths.
+ * We use a VenMedia() with a specific GUID to identify
+ * the usage of the array members. This function is
+ * used to extract a specific device path
+ *
+ * @guid: vendor GUID of the VenMedia() device path node identifying the
+ * device path
+ *
+ * Return: device path or NULL. Caller must free the returned value
+ */
+struct efi_device_path *efi_get_dp_from_boot(const efi_guid_t guid)
+{
+ struct efi_device_path *file_path = NULL;
+ struct efi_device_path *tmp = NULL;
+ struct efi_load_option lo;
+ void *var_value = NULL;
+ efi_uintn_t size;
+ efi_status_t ret;
+ u16 var_name[16];
+
+ ret = efi_create_current_boot_var(var_name, sizeof(var_name));
+ if (ret != EFI_SUCCESS)
+ return NULL;
+
+ var_value = efi_get_var(var_name, &efi_global_variable_guid, &size);
+ if (!var_value)
+ return NULL;
+
+ ret = efi_deserialize_load_option(&lo, var_value, &size);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ tmp = efi_dp_from_lo(&lo, &size, guid);
+ if (!tmp)
+ goto out;
+
+ /* efi_dp_dup will just return NULL if efi_dp_next is NULL */
+ file_path = efi_dp_dup(efi_dp_next(tmp));
+
+out:
+ efi_free_pool(tmp);
+ free(var_value);
+
+ return file_path;
+}
diff --git a/lib/efi_loader/efi_load_initrd.c b/lib/efi_loader/efi_load_initrd.c
index b9ee8839054..e2a80630230 100644
--- a/lib/efi_loader/efi_load_initrd.c
+++ b/lib/efi_loader/efi_load_initrd.c
@@ -3,9 +3,11 @@
* Copyright (c) 2020, Linaro Limited
*/
+#define LOG_CATEGORY LOGC_EFI
#include <common.h>
#include <efi_loader.h>
#include <efi_load_initrd.h>
+#include <efi_variable.h>
#include <fs.h>
#include <malloc.h>
#include <mapmem.h>
@@ -23,57 +25,56 @@ static const struct efi_load_file_protocol efi_lf2_protocol = {
* Device path defined by Linux to identify the handle providing the
* EFI_LOAD_FILE2_PROTOCOL used for loading the initial ramdisk.
*/
-static const struct efi_initrd_dp dp = {
+static const struct efi_initrd_dp dp_lf2_handle = {
.vendor = {
{
DEVICE_PATH_TYPE_MEDIA_DEVICE,
DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
- sizeof(dp.vendor),
+ sizeof(dp_lf2_handle.vendor),
},
EFI_INITRD_MEDIA_GUID,
},
.end = {
DEVICE_PATH_TYPE_END,
DEVICE_PATH_SUB_TYPE_END,
- sizeof(dp.end),
+ sizeof(dp_lf2_handle.end),
}
};
+static efi_handle_t efi_initrd_handle;
+
/**
- * get_file_size() - retrieve the size of initramfs, set efi status on error
+ * get_initrd_fp() - Get initrd device path from a FilePathList device path
*
- * @dev: device to read from, e.g. "mmc"
- * @part: device partition, e.g. "0:1"
- * @file: name of file
- * @status: EFI exit code in case of failure
+ * @initrd_fp: the final initrd filepath
*
- * Return: size of file
+ * Return: status code. Caller must free initrd_fp
*/
-static loff_t get_file_size(const char *dev, const char *part, const char *file,
- efi_status_t *status)
+static efi_status_t get_initrd_fp(struct efi_device_path **initrd_fp)
{
- loff_t sz = 0;
- int ret;
-
- ret = fs_set_blk_dev(dev, part, FS_TYPE_ANY);
- if (ret) {
- *status = EFI_NO_MEDIA;
- goto out;
- }
+ const efi_guid_t lf2_initrd_guid = EFI_INITRD_MEDIA_GUID;
+ struct efi_device_path *dp = NULL;
- ret = fs_size(file, &sz);
- if (ret) {
- sz = 0;
- *status = EFI_NOT_FOUND;
- goto out;
- }
+ /*
+ * if bootmgr is setup with and initrd, the device path will be
+ * in the FilePathList[] of our load options in Boot####.
+ * The first device path of the multi instance device path will
+ * start with a VenMedia and the initrds will follow.
+ *
+ * If the device path is not found return EFI_INVALID_PARAMETER.
+ * We can then use this specific return value and not install the
+ * protocol, while allowing the boot to continue
+ */
+ dp = efi_get_dp_from_boot(lf2_initrd_guid);
+ if (!dp)
+ return EFI_INVALID_PARAMETER;
-out:
- return sz;
+ *initrd_fp = dp;
+ return EFI_SUCCESS;
}
/**
- * efi_load_file2initrd() - load initial RAM disk
+ * efi_load_file2_initrd() - load initial RAM disk
*
* This function implements the LoadFile service of the EFI_LOAD_FILE2_PROTOCOL
* in order to load an initial RAM disk requested by the Linux kernel stub.
@@ -93,102 +94,125 @@ efi_load_file2_initrd(struct efi_load_file_protocol *this,
struct efi_device_path *file_path, bool boot_policy,
efi_uintn_t *buffer_size, void *buffer)
{
- char *filespec;
- efi_status_t status = EFI_NOT_FOUND;
- loff_t file_sz = 0, read_sz = 0;
- char *dev, *part, *file;
- char *pos;
- int ret;
+ struct efi_device_path *initrd_fp = NULL;
+ efi_status_t ret = EFI_NOT_FOUND;
+ struct efi_file_handle *f = NULL;
+ efi_uintn_t bs;
EFI_ENTRY("%p, %p, %d, %p, %p", this, file_path, boot_policy,
buffer_size, buffer);
- filespec = strdup(CONFIG_EFI_INITRD_FILESPEC);
- if (!filespec)
- goto out;
- pos = filespec;
-
if (!this || this != &efi_lf2_protocol ||
!buffer_size) {
- status = EFI_INVALID_PARAMETER;
+ ret = EFI_INVALID_PARAMETER;
goto out;
}
- if (file_path->type != dp.end.type ||
- file_path->sub_type != dp.end.sub_type) {
- status = EFI_INVALID_PARAMETER;
+ if (file_path->type != dp_lf2_handle.end.type ||
+ file_path->sub_type != dp_lf2_handle.end.sub_type) {
+ ret = EFI_INVALID_PARAMETER;
goto out;
}
if (boot_policy) {
- status = EFI_UNSUPPORTED;
+ ret = EFI_UNSUPPORTED;
goto out;
}
- /*
- * expect a string with three space separated parts:
- *
- * * a block device type, e.g. "mmc"
- * * a device and partition identifier, e.g. "0:1"
- * * a file path on the block device, e.g. "/boot/initrd.cpio.gz"
- */
- dev = strsep(&pos, " ");
- if (!dev)
+ ret = get_initrd_fp(&initrd_fp);
+ if (ret != EFI_SUCCESS)
goto out;
- part = strsep(&pos, " ");
- if (!part)
- goto out;
- file = strsep(&pos, " ");
- if (!file)
+
+ /* Open file */
+ f = efi_file_from_path(initrd_fp);
+ if (!f) {
+ log_err("Can't find initrd specified in Boot####\n");
+ ret = EFI_NOT_FOUND;
goto out;
+ }
- file_sz = get_file_size(dev, part, file, &status);
- if (!file_sz)
+ /* Get file size */
+ ret = efi_file_size(f, &bs);
+ if (ret != EFI_SUCCESS)
goto out;
- if (!buffer || *buffer_size < file_sz) {
- status = EFI_BUFFER_TOO_SMALL;
- *buffer_size = file_sz;
+ if (!buffer || *buffer_size < bs) {
+ ret = EFI_BUFFER_TOO_SMALL;
+ *buffer_size = bs;
} else {
- ret = fs_set_blk_dev(dev, part, FS_TYPE_ANY);
- if (ret) {
- status = EFI_NO_MEDIA;
- goto out;
- }
-
- ret = fs_read(file, map_to_sysmem(buffer), 0, *buffer_size,
- &read_sz);
- if (ret || read_sz != file_sz)
- goto out;
- *buffer_size = read_sz;
-
- status = EFI_SUCCESS;
+ ret = EFI_CALL(f->read(f, &bs, (void *)(uintptr_t)buffer));
+ *buffer_size = bs;
+ }
+
+out:
+ efi_free_pool(initrd_fp);
+ if (f)
+ EFI_CALL(f->close(f));
+ return EFI_EXIT(ret);
+}
+
+/**
+ * check_initrd() - Determine if the file defined as an initrd in Boot####
+ * load_options device path is present
+ *
+ * Return: status code
+ */
+static efi_status_t check_initrd(void)
+{
+ struct efi_device_path *initrd_fp = NULL;
+ struct efi_file_handle *f;
+ efi_status_t ret;
+
+ ret = get_initrd_fp(&initrd_fp);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /*
+ * If the file is not found, but the file path is set, return an error
+ * and trigger the bootmgr fallback
+ */
+ f = efi_file_from_path(initrd_fp);
+ if (!f) {
+ log_err("Can't find initrd specified in Boot####\n");
+ ret = EFI_NOT_FOUND;
+ goto out;
}
+ EFI_CALL(f->close(f));
+
out:
- free(filespec);
- return EFI_EXIT(status);
+ efi_free_pool(initrd_fp);
+ return ret;
}
/**
* efi_initrd_register() - create handle for loading initial RAM disk
*
* This function creates a new handle and installs a Linux specific vendor
- * device path and an EFI_LOAD_FILE_2_PROTOCOL. Linux uses the device path
+ * device path and an EFI_LOAD_FILE2_PROTOCOL. Linux uses the device path
* to identify the handle and then calls the LoadFile service of the
- * EFI_LOAD_FILE_2_PROTOCOL to read the initial RAM disk.
+ * EFI_LOAD_FILE2_PROTOCOL to read the initial RAM disk.
*
* Return: status code
*/
efi_status_t efi_initrd_register(void)
{
- efi_handle_t efi_initrd_handle = NULL;
efi_status_t ret;
+ /*
+ * Allow the user to continue if Boot#### file path is not set for
+ * an initrd
+ */
+ ret = check_initrd();
+ if (ret == EFI_INVALID_PARAMETER)
+ return EFI_SUCCESS;
+ if (ret != EFI_SUCCESS)
+ return ret;
+
ret = EFI_CALL(efi_install_multiple_protocol_interfaces
(&efi_initrd_handle,
/* initramfs */
- &efi_guid_device_path, &dp,
+ &efi_guid_device_path, &dp_lf2_handle,
/* LOAD_FILE2 */
&efi_guid_load_file2_protocol,
(void *)&efi_lf2_protocol,
@@ -196,3 +220,17 @@ efi_status_t efi_initrd_register(void)
return ret;
}
+
+/**
+ * efi_initrd_deregister() - delete the handle for loading initial RAM disk
+ *
+ * This will delete the handle containing the Linux specific vendor device
+ * path and EFI_LOAD_FILE2_PROTOCOL for loading an initrd
+ *
+ * Return: status code
+ */
+void efi_initrd_deregister(void)
+{
+ efi_delete_handle(efi_initrd_handle);
+ efi_initrd_handle = NULL;
+}
diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
index b1c5125032b..3c5cf9a4357 100644
--- a/lib/efi_loader/efi_setup.c
+++ b/lib/efi_loader/efi_setup.c
@@ -227,6 +227,12 @@ efi_status_t efi_init_obj_list(void)
if (ret != EFI_SUCCESS)
goto out;
+ if (IS_ENABLED(CONFIG_EFI_ESRT)) {
+ ret = efi_esrt_register();
+ if (ret != EFI_SUCCESS)
+ goto out;
+ }
+
if (IS_ENABLED(CONFIG_EFI_TCG2_PROTOCOL)) {
ret = efi_tcg2_register();
if (ret != EFI_SUCCESS)
diff --git a/lib/efi_loader/efi_tcg2.c b/lib/efi_loader/efi_tcg2.c
index 797d6eb134f..09046844c72 100644
--- a/lib/efi_loader/efi_tcg2.c
+++ b/lib/efi_loader/efi_tcg2.c
@@ -13,6 +13,7 @@
#include <efi_loader.h>
#include <efi_tcg2.h>
#include <log.h>
+#include <version.h>
#include <tpm-v2.h>
#include <u-boot/sha1.h>
#include <u-boot/sha256.h>
@@ -958,6 +959,23 @@ out:
}
/**
+ * tcg2_uninit - remove the final event table and free efi memory on failures
+ */
+void tcg2_uninit(void)
+{
+ efi_status_t ret;
+
+ ret = efi_install_configuration_table(&efi_guid_final_events, NULL);
+ if (ret != EFI_SUCCESS)
+ log_err("Failed to delete final events config table\n");
+
+ efi_free_pool(event_log.buffer);
+ event_log.buffer = NULL;
+ efi_free_pool(event_log.final_buffer);
+ event_log.final_buffer = NULL;
+}
+
+/**
* create_final_event() - Create the final event and install the config
* defined by the TCG EFI spec
*/
@@ -983,10 +1001,6 @@ static efi_status_t create_final_event(void)
event_log.final_pos = sizeof(*final_event);
ret = efi_install_configuration_table(&efi_guid_final_events,
final_event);
- if (ret != EFI_SUCCESS)
- goto out;
-
- return EFI_SUCCESS;
out:
return ret;
}
@@ -1041,6 +1055,40 @@ static efi_status_t efi_init_event_log(void)
event_log.last_event_size = event_log.pos;
ret = create_final_event();
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ return EFI_SUCCESS;
+out:
+ tcg2_uninit();
+ return ret;
+}
+
+/**
+ * efi_append_scrtm_version - Append an S-CRTM EV_S_CRTM_VERSION event on the
+ * eventlog and extend the PCRs
+ *
+ * @dev: TPM device
+ *
+ * @Return: status code
+ */
+static efi_status_t efi_append_scrtm_version(struct udevice *dev)
+{
+ struct tpml_digest_values digest_list;
+ u8 ver[] = U_BOOT_VERSION_STRING;
+ const int pcr_index = 0;
+ efi_status_t ret;
+
+ ret = tcg2_create_digest(ver, sizeof(ver), &digest_list);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ ret = tcg2_pcr_extend(dev, pcr_index, &digest_list);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ ret = tcg2_agile_log_append(pcr_index, EV_S_CRTM_VERSION, &digest_list,
+ sizeof(ver), ver);
out:
return ret;
@@ -1055,23 +1103,34 @@ out:
*/
efi_status_t efi_tcg2_register(void)
{
- efi_status_t ret;
+ efi_status_t ret = EFI_SUCCESS;
struct udevice *dev;
ret = platform_get_tpm2_device(&dev);
if (ret != EFI_SUCCESS) {
log_warning("Unable to find TPMv2 device\n");
- return EFI_SUCCESS;
+ ret = EFI_SUCCESS;
+ goto out;
}
ret = efi_init_event_log();
if (ret != EFI_SUCCESS)
- return ret;
+ goto fail;
+
+ ret = efi_append_scrtm_version(dev);
+ if (ret != EFI_SUCCESS)
+ goto out;
ret = efi_add_protocol(efi_root, &efi_guid_tcg2_protocol,
(void *)&efi_tcg2_protocol);
- if (ret != EFI_SUCCESS)
+ if (ret != EFI_SUCCESS) {
log_err("Cannot install EFI_TCG2_PROTOCOL\n");
+ goto fail;
+ }
+out:
+ return ret;
+fail:
+ tcg2_uninit();
return ret;
}
diff --git a/lib/efi_loader/efi_unicode_collation.c b/lib/efi_loader/efi_unicode_collation.c
index f6c875bc33c..36be798f64b 100644
--- a/lib/efi_loader/efi_unicode_collation.c
+++ b/lib/efi_loader/efi_unicode_collation.c
@@ -23,7 +23,7 @@ static const char illegal[] = "+,<=>:;\"/\\|?*[]\x7f";
static const u16 codepage[] = CP1250;
#else
/* Unicode code points for code page 437 characters 0x80 - 0xff */
-static const u16 codepage[] = CP437;
+static const u16 *codepage = codepage_437;
#endif
/* GUID of the EFI_UNICODE_COLLATION_PROTOCOL2 */
@@ -300,23 +300,10 @@ static bool EFIAPI efi_str_to_fat(struct efi_unicode_collation_protocol *this,
break;
}
c = utf_to_upper(c);
- if (c >= 0x80) {
- int j;
-
- /* Look for codepage translation */
- for (j = 0; j < 0x80; ++j) {
- if (c == codepage[j]) {
- c = j + 0x80;
- break;
- }
- }
- if (j >= 0x80) {
- c = '_';
- ret = true;
- }
- } else if (c && (c < 0x20 || strchr(illegal, c))) {
- c = '_';
+ if (utf_to_cp(&c, codepage) ||
+ (c && (c < 0x20 || strchr(illegal, c)))) {
ret = true;
+ c = '_';
}
fat[i] = c;
diff --git a/lib/efi_loader/efi_var_common.c b/lib/efi_loader/efi_var_common.c
index 1c7459266a3..b11ed91a74a 100644
--- a/lib/efi_loader/efi_var_common.c
+++ b/lib/efi_loader/efi_var_common.c
@@ -9,6 +9,7 @@
#include <common.h>
#include <efi_loader.h>
#include <efi_variable.h>
+#include <stdlib.h>
enum efi_secure_mode {
EFI_MODE_SETUP,
@@ -343,3 +344,35 @@ enum efi_auth_var_type efi_auth_var_get_type(u16 *name, const efi_guid_t *guid)
}
return EFI_AUTH_VAR_NONE;
}
+
+/**
+ * efi_get_var() - read value of an EFI variable
+ *
+ * @name: variable name
+ * @start: vendor GUID
+ * @size: size of allocated buffer
+ *
+ * Return: buffer with variable data or NULL
+ */
+void *efi_get_var(u16 *name, const efi_guid_t *vendor, efi_uintn_t *size)
+{
+ efi_status_t ret;
+ void *buf = NULL;
+
+ *size = 0;
+ ret = efi_get_variable_int(name, vendor, NULL, size, buf, NULL);
+ if (ret == EFI_BUFFER_TOO_SMALL) {
+ buf = malloc(*size);
+ if (!buf)
+ return NULL;
+ ret = efi_get_variable_int(name, vendor, NULL, size, buf, NULL);
+ }
+
+ if (ret != EFI_SUCCESS) {
+ free(buf);
+ *size = 0;
+ return NULL;
+ }
+
+ return buf;
+}