[PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot####
Heinrich Schuchardt
xypron.glpk at gmx.de
Thu Mar 11 12:00:32 CET 2021
On 11.03.21 10:10, Ilias Apalodimas wrote:
> On Thu, Mar 11, 2021 at 08:50:22AM +0100, Heinrich Schuchardt wrote:
>> On 3/5/21 11:22 PM, Ilias Apalodimas wrote:
>>> On the following patches we allow for an initrd path to be stored in
>>> Boot#### variables. Specifically we encode in the FIlePathList[] of
>>> the EFI_LOAD_OPTIONS for each Boot#### variable.
>>>
>>> The FilePathList[] array looks like this:
>>> kernel - 0xff - VenMedia(initrd GUID) - 0x01 - initrd1 - 0x01 initrd2 -0xff
>>> So let's add the relevant functions to concatenate and retrieve a device
>>> path based on a Vendor GUID.
>>>
>>> Signed-off-by: Ilias Apalodimas <ilias.apalodimas at linaro.org>
>>> ---
>>> include/efi_loader.h | 4 ++
>>> lib/efi_loader/efi_device_path.c | 72 ++++++++++++++++++++++++++++++++
>>> 2 files changed, 76 insertions(+)
>>>
>>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>>> index f470bbd636f4..eb11a8c7d4b1 100644
>>> --- a/include/efi_loader.h
>>> +++ b/include/efi_loader.h
>>> @@ -738,6 +738,10 @@ struct efi_load_option {
>>> const u8 *optional_data;
>>> };
>>>
>>> +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 *efi_dp_concat(const struct efi_device_path *dp1,
>>> + const struct efi_device_path *dp2);
>>> efi_status_t efi_deserialize_load_option(struct efi_load_option *lo, u8 *data,
>>> efi_uintn_t *size);
>>> unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data);
>>> diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c
>>> index c9315dd45857..cf1321828e87 100644
>>> --- a/lib/efi_loader/efi_device_path.c
>>> +++ b/lib/efi_loader/efi_device_path.c
>>> @@ -310,6 +310,41 @@ struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
>>> return ret;
>>> }
>>>
>>> +/** 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
>>> + * @size: 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)
>>> +{
>>> + struct efi_device_path *ret;
>>> + efi_uintn_t sz1, sz2;
>>> + void *p;
>>> +
>>> + if (!dp1 || !dp2)
>>> + return NULL;
>>> + sz1 = efi_dp_size(dp1);
>>> + sz2 = efi_dp_size(dp2);
>>> + p = dp_alloc(sz1 + sz2 + (2 * sizeof(END)));
>>> + /* both dp1 and dp2 are non-null */
>>> + if (!p)
>>> + return NULL;
>>> + ret = p;
>>> + memcpy(p, dp1, sz1);
>>> + p += sz1;
>>> + memcpy(p, &END, sizeof(END));
>>> + p += sizeof(END);
>>> + memcpy(p, dp2, sz2);
>>> + p += sz2;
>>> + memcpy(p, &END, sizeof(END));
>>> +
>>> + return ret;
>>> +}
>>> +
>>> struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
>>> const struct efi_device_path *node)
>>> {
>>> @@ -1160,3 +1195,40 @@ 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 Vendor Device Path
>>> + * in a multi-instance device path that matches
>>> + * a specific GUID
>>
>> The description does make it clear that you are looking for a VenMedia()
>> node.
>>
>> Please, describe what the function is good for (find the device path for
>> an initrd or dtb in a load option).
>>
>
> Ok
>
>>> + *
>>> + * @load_option: device paths to search
>>> + * @size: size of the discovered device path
>>> + * @guid: guid to search for
>>> + *
>>> + * Return: device path or NULL. Caller must free the returned value
>>
>> Please, keep the text aligned.
>>
>> Do we need a copy? Isn't a pointer good enough?
>
> A pointer to what?
> I think it's better to a copy here. This is a device path that might be used
> out of a stack context were the load option might disappear.
> Look at how the function is used in efi_get_dp_from_boot().
You are duplicating in get_initrd_fp(). Why should we duplicate twice?
>
>>
>>> + */
>>> +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;
>>> +
>>> + while (lo_len) {
>>
>> lo_len must be at least sizeof(struct efi_device_path).
>>
>>> + if (fp->type != DEVICE_PATH_TYPE_MEDIA_DEVICE ||
>>> + fp->sub_type != DEVICE_PATH_SUB_TYPE_VENDOR_PATH) {
>>> + lo_len -= fp->length;
>>
>> Could the last device path in the array be followed by zero bytes for
>> padding?
>
> How? Device paths are packed aren't they ?
>
>> Should we check that fp->length >= sizeof(struct efi_device_path)?
>
> Yea probably a good idea
The content of the boot option comes from the user. Just assume that it
can contain malicious content.
We should also check that the identified device-path starting at
VenMedia() ends within fp->length using efi_dp_check_length().
>
>>
>>> + fp = (void *)fp + fp->length;
>>
>> Please, avoid code duplication.
>>
>> E.g.
>>
>> for (; lo_len >= sizeof(struct efi_device_path);
>> lo_len -= fp->length, fp = (void *)fp + fp->length) {
>
> I can an switch to that, but I really never liked this format.
> It always seemed way less readable to me for some reason. Maybe because I
> never got used to it ...
Using "for" is only one option. You could use "goto next;" instead.
>
>>
>>> + continue;
>>> + }
>>> +
>>> + vendor = (struct efi_device_path_vendor *)fp;
>>> + if (!guidcmp(&vendor->guid, &guid))
>>> + return efi_dp_dup(fp);
>>
>> Should we strip of the VenMedia() node here?
>
> Why? This is not supposed to get the file path. The function says "get device
> path from load option" and that device includes the VenMedia node.
> It would make more sense for me to strip in efi_get_dp_from_boot() for
> example, if you want a helper function to get the initrd path *only*.
The VenMedia() node is not needed anymore once you have found the entry.
>
> But really it's just one invocation of efi_dp_get_next_instance() after
> whatever device path you get. Which also modifies the device path pointer, so
> I'd really prefer keeping that call in a local context.
Why next instance? I thought next node.
My understanding is that we have:
kernel path,end(0xff),
VenMedia(), /* no end node here */
initrd1, end(0x01),
initrd2, end(0xff)
Please, document the structure.
Best regards
Heinrich
More information about the U-Boot
mailing list