On 13.01.21 12:11, 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 file paths stored in EFI load options.
>
> Signed-off-by: Ilias Apalodimas <ilias.apalodi...@linaro.org>
> ---
>  include/efi_helper.h        |  23 ++++++
>  lib/efi_loader/efi_helper.c | 146 ++++++++++++++++++++++++++++++++++++
>  2 files changed, 169 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..4e6bd2f036df
> --- /dev/null
> +++ b/include/efi_helper.h
> @@ -0,0 +1,23 @@
> +/* 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>
> +
> +enum load_option_dp_type {
> +     BOOT_IMAGE_DP,
> +     INITRD_DP,
> +     DTB_DP,
> +};
> +
> +void *efi_get_var(u16 *name, const efi_guid_t *vendor, efi_uintn_t *size);
> +struct efi_device_path *efi_get_fp_from_boot(int idx);
> +struct efi_device_path *efi_dp_instance_by_idx(struct efi_device_path *dp,
> +                                            efi_uintn_t *size, int idx);
> +
> +#endif
> diff --git a/lib/efi_loader/efi_helper.c b/lib/efi_loader/efi_helper.c
> new file mode 100644
> index 000000000000..e2437a4f698b
> --- /dev/null
> +++ b/lib/efi_loader/efi_helper.c
> @@ -0,0 +1,146 @@
> +// 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>
> +
> +/**
> + * 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);

Please, always check the output of malloc(), e.g.

                if (!buf)
                        ret = EFI_OUT_OF_RESOURCES;
                else

> +             ret = efi_get_variable_int(name, vendor, NULL, size, buf, NULL);
> +     }
> +
> +     if (ret != EFI_SUCCESS) {
> +             free(buf);
> +             *size = 0;
> +             return NULL;
> +     }
> +
> +     return buf;
> +}
> +
> +/**
> + * efi_dp_instance_by_idx() - Get a file path with a specific index
> + *
> + * @name:    device path array
> + * @size:    size of the discovered device path
> + * @idx:     index of the device path
> + *
> + * Return:   device path or NULL. Caller must free the returned value
> + */
> +
> +struct
> +efi_device_path *efi_dp_instance_by_idx(struct efi_device_path *dp,
> +                                     efi_uintn_t *size, int idx)
> +{

idx should be of an unsigned type as we cannot have negative indices.

> +     struct efi_device_path *instance = NULL;
> +     efi_uintn_t instance_size = 0;
> +
> +     if (!efi_dp_is_multi_instance(dp))

Given a device path with one instance, why don't you allow me to read
index 0? I assume this check can be removed.

> +             return NULL;
> +
> +     while (idx >= 0 && dp) {
> +             instance = efi_dp_get_next_instance(&dp, &instance_size);
> +             if (idx && instance) {
> +                     efi_free_pool(instance);
> +                     instance_size = 0;
> +                     instance = NULL;
> +             }
> +             idx--;
> +     }
> +     *size = instance_size;
> +
> +     return instance;

This can be simplified with unsigned idx:

for (; dp; --idx) {
        instance = efi_dp_get_next_instance(&dp, size);
        if (!idx)  {
                return instance;
        efi_free_pool(instance);
}
return NULL;

> +}
> +
> +/**
> + * create_boot_var_indexed() - 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 create_boot_var_indexed(u16 var_name[], size_t 
> var_name_size)
> +{
> +     efi_uintn_t boot_order_size;

You are reading BootCurrent, not BootOrder.

> +     efi_status_t ret;
> +     u16 boot_order;

Same here.

> +     u16 *pos;
> +
> +     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;
> +
> +     pos = efi_create_indexed_name(var_name, var_name_size, "Boot",
> +                                   boot_order);
> +     if (!pos) {
> +             ret = EFI_OUT_OF_RESOURCES;
> +             goto out;
> +     }
> +
> +out:
> +     return ret;
> +}
> +
> +/**
> + * efi_get_fp_from_var() - Retrieve and return a device path from an EFI
> + *                      Boot### variable
> + *
> + * @idx:     index of the instance to retrieve
> + *
> + * Return:   device path of NULL. Caller must free the returned value

%s/of/or/

> + */
> +struct efi_device_path *efi_get_fp_from_boot(int idx)
> +{
> +     struct efi_device_path *file_path;
> +     struct efi_load_option lo;
> +     void *var_value = NULL;
> +     efi_uintn_t size;
> +     efi_status_t ret;
> +     u16 var_name[16];
> +
> +     ret = create_boot_var_indexed(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;
> +
> +     efi_deserialize_load_option(&lo, var_value, &size);
> +     file_path = efi_dp_instance_by_idx(lo.file_path, &size, idx);
> +     if (!file_path) {
> +             printf("Instance not found\n");

This message is not enough for a user to take action. I suggest

("Missing file path with index %d in %ls", idx, var_name)

We want to use logging. I assume this is not an error. Can we use
log_debug() here?

> +             return NULL;
> +     }
> +
> +     return file_path;
> +}
>

Some other functions would fit into the same C module. Candidates are:

* efi_create_indexed_name()
* efi_deserialize_load_option()
* efi_serialize_load_option()

But that can be done in a separate patch.

Best regards

Heinrich

Reply via email to