diff mbox series

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

Message ID 20201228122440.316403-3-ilias.apalodimas@linaro.org
State New
Headers show
Series Change logic of EFI LoadFile2 protocol for initrd loading | expand

Commit Message

Ilias Apalodimas Dec. 28, 2020, 12:24 p.m. UTC
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@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

-- 
2.30.0.rc2

Comments

Heinrich Schuchardt Dec. 28, 2020, 2:49 p.m. UTC | #1
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@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;

> +}

>
Ilias Apalodimas Dec. 28, 2020, 10:06 p.m. UTC | #2
Hi Heinrich, 

> > +

> > +	/* 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().


That one doesn't check any input variables and just asks for the user to
provide sufficient buffers for the output. 
I am explicitly checking the sizes here. I guess I can add similar checks to 
efi_create_indexed_name() and use it, instead of open coding again.
Just a note here, there's similar code to the efi bootmgr, so we should
probably start replacing all of the functions.

Regards
/Ilias
> 

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

> > +}

> > 

>
diff mbox series

Patch

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