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

Heinrich Schuchardt xypron.glpk at gmx.de
Mon Dec 28 15:49:28 CET 2020


On 12/28/20 1:24 PM, Ilias Apalodimas wrote:
> 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];

Please, consider using  efi_create_indexed_name().

Best regards

Heinrich

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



More information about the U-Boot mailing list