[PATCH 2/6] efi_loader: Introduce helper functions for EFI

Ilias Apalodimas ilias.apalodimas at linaro.org
Mon Dec 28 13:24:34 CET 2020


A following patch introduces a different logic for loading initrd's
based on the EFI_LOAD_FILE2_PROTOCOL.
Since similar logic can be applied in the future for other system files
(i.e DTBs). Let's add some helper functions which will retrieve and
parse device paths via EFI variables.

Signed-off-by: Ilias Apalodimas <ilias.apalodimas at linaro.org>
---
 include/efi_helper.h        |  29 ++++++
 lib/efi_loader/efi_helper.c | 189 ++++++++++++++++++++++++++++++++++++
 2 files changed, 218 insertions(+)
 create mode 100644 include/efi_helper.h
 create mode 100644 lib/efi_loader/efi_helper.c

diff --git a/include/efi_helper.h b/include/efi_helper.h
new file mode 100644
index 000000000000..d76e24e0f57d
--- /dev/null
+++ b/include/efi_helper.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2020, Linaro Limited
+ */
+
+#if !defined _EFI_HELPER_H_
+#define _EFI_HELPER_H
+
+#include <efi.h>
+#include <efi_api.h>
+
+/*
+ * @dev:	device string i.e 'mmc'
+ * @part:	partition string i.e '0:2'
+ * @filename:	name of the file
+ */
+struct load_file_info {
+	char dev[32];
+	char part[16];
+	char filename[256];
+};
+
+loff_t get_file_size(const struct load_file_info *file_loc,
+		     efi_status_t *status);
+efi_status_t efi_get_fp_from_var(const u16 *name, u16 start,
+				 struct load_file_info *loc);
+void *get_var(u16 *name, const efi_guid_t *vendor, efi_uintn_t *size);
+
+#endif
diff --git a/lib/efi_loader/efi_helper.c b/lib/efi_loader/efi_helper.c
new file mode 100644
index 000000000000..4cf1f8abed30
--- /dev/null
+++ b/lib/efi_loader/efi_helper.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2020, Linaro Limited
+ */
+
+#include <common.h>
+#include <env.h>
+#include <malloc.h>
+#include <dm.h>
+#include <fs.h>
+#include <efi_helper.h>
+#include <efi_loader.h>
+#include <efi_variable.h>
+
+/**
+ * get_file_size() - retrieve the size of initramfs, set efi status on error
+ *
+ * @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
+ *
+ * Return:			size of file
+ */
+loff_t get_file_size(const struct load_file_info *info, efi_status_t *status)
+{
+	loff_t sz = 0;
+	int ret;
+
+	ret = fs_set_blk_dev(info->dev, info->part, FS_TYPE_ANY);
+	if (ret) {
+		*status = EFI_NO_MEDIA;
+		goto out;
+	}
+
+	ret = fs_size(info->filename, &sz);
+	if (ret) {
+		sz = 0;
+		*status = EFI_NOT_FOUND;
+		goto out;
+	}
+
+out:
+	return sz;
+}
+
+/*
+ * string_to_load_args() - Fill in a struct load_file_info with the file info
+ *			   parsed from an EFI variable
+ *
+ * @args:	value of the EFI variable i.e "mmc 0 initrd"
+ * @info:	struct to fill in with file specific info
+ *
+ * Return:	Status code
+ */
+static efi_status_t string_to_load_args(char *args, struct load_file_info *info)
+{
+	efi_status_t status = EFI_SUCCESS;
+	char *p;
+
+	/*
+	 * expect a string with three space separated parts:
+	 * - block device type, e.g. "mmc"
+	 * - device and partition identifier, e.g. "0:1"
+	 * - file path on the block device, e.g. "/boot/initrd.cpio.gz"
+	 */
+	p = strsep(&args, " ");
+	if (!p) {
+		status = EFI_NO_MEDIA;
+		goto out;
+	}
+	strncpy(info->dev, p, sizeof(info->dev));
+
+	p = strsep(&args, " ");
+	if (!p) {
+		status = EFI_NO_MEDIA;
+		goto out;
+	}
+	strncpy(info->part, p, sizeof(info->part));
+
+	p = strsep(&args, " ");
+	if (!p) {
+		status = EFI_NOT_FOUND;
+		goto out;
+	}
+	strncpy(info->filename, p, sizeof(info->filename));
+
+out:
+	return status;
+}
+
+/**
+ * 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 *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);
+		ret = efi_get_variable_int(name, vendor, NULL, size, buf, NULL);
+	}
+
+	if (ret != EFI_SUCCESS) {
+		free(buf);
+		*size = 0;
+		return NULL;
+	}
+
+	return buf;
+}
+
+/**
+ * efi_get_fp_from_var() - Retrieve a file path from an EFI variable
+ *
+ * @name:	variable name
+ * @start:	start replacing from
+ * @info:	struct to fill in with file specific info
+ */
+efi_status_t efi_get_fp_from_var(const u16 *name, u16 start,
+				 struct load_file_info *info)
+{
+	u16 hexmap[] = L"0123456789ABCDEF";
+	efi_uintn_t boot_order_size;
+	void *var_value = NULL;
+	u16 *name_dup = NULL;
+	efi_uintn_t size;
+	efi_status_t ret;
+	u16 boot_order;
+
+	memset(info, 0, sizeof(*info));
+
+	/* make sure we have enough space for replacements */
+	if (u16_strsize(name) < sizeof(*name) * start + u16_strsize(L"####")) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+	boot_order_size = sizeof(boot_order);
+	ret = efi_get_variable_int(L"BootCurrent",
+				   &efi_global_variable_guid, NULL,
+				   &boot_order_size, &boot_order, NULL);
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+	name_dup = u16_strdup(name);
+	if (!name_dup) {
+		ret = EFI_OUT_OF_RESOURCES;
+		goto out;
+	}
+	/* Match name variable to BootCurrent */
+	name_dup[start] = hexmap[(boot_order & 0xf000) >> 12];
+	name_dup[start + 1] = hexmap[(boot_order & 0x0f00) >> 8];
+	name_dup[start + 2] = hexmap[(boot_order & 0x00f0) >> 4];
+	name_dup[start + 3] = hexmap[(boot_order & 0x000f) >> 0];
+
+	var_value = get_var(name_dup, &efi_global_variable_guid, &size);
+	if (!var_value) {
+		ret = EFI_NOT_FOUND;
+		goto out;
+	}
+
+	ret = string_to_load_args(var_value, info);
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+	if (fs_set_blk_dev(info->dev, info->part, FS_TYPE_ANY)) {
+		ret = EFI_NO_MEDIA;
+		goto out;
+	}
+
+	if (!fs_exists(info->filename)) {
+		ret = EFI_NOT_FOUND;
+		goto out;
+	}
+
+out:
+	free(var_value);
+	free(name_dup);
+	return ret;
+}
-- 
2.30.0.rc2



More information about the U-Boot mailing list