Message ID | 20210313214738.3257922-3-ilias.apalodimas@linaro.org |
---|---|
State | Superseded |
Headers | show |
Series | Loadfile2 for initrd loading | expand |
On 3/13/21 10:47 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) - 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@linaro.org> > --- > include/efi_loader.h | 4 ++ > lib/efi_loader/efi_device_path.c | 99 ++++++++++++++++++++++++++++++-- > 2 files changed, 98 insertions(+), 5 deletions(-) > > 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..1f55c772dc83 100644 > --- a/lib/efi_loader/efi_device_path.c > +++ b/lib/efi_loader/efi_device_path.c > @@ -282,11 +282,25 @@ struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp) > return ndp; > } > > -struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1, > - const struct efi_device_path *dp2) > +/** _efi_dp_append() - Append or concatenate two device paths. Please, add an empty line. > + * Concatenated device path will be separated by a 0xff end ... by a sub-type 0xff end node. > + * node. > + * > + * @dp1: First device path > + * @dp2: Second device path Please, describe argument @concat. > + * > + * Return: concatenated device path or NULL. Caller must free the returned > + * value > + */ > +static struct efi_device_path *_efi_dp_append(const struct efi_device_path *dp1, This function name is very close to efi_dp_append. Maybe efi_dp_append_or_concatenate() would be a better function name. > + const struct efi_device_path *dp2, > + bool concat) > { > struct efi_device_path *ret; > + size_t end_size = sizeof(END); > > + if (concat) > + end_size = 2 * sizeof(END); > if (!dp1 && !dp2) { > /* return an end node */ > ret = efi_dp_dup(&END); > @@ -298,18 +312,56 @@ struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1, > /* both dp1 and dp2 are non-null */ > unsigned sz1 = efi_dp_size(dp1); > unsigned sz2 = efi_dp_size(dp2); > - void *p = dp_alloc(sz1 + sz2 + sizeof(END)); > + void *p = dp_alloc(sz1 + sz2 + end_size); > if (!p) > return NULL; > + ret = p; > memcpy(p, dp1, sz1); > + p += sz1; > + > + if (concat) { > + memcpy(p, &END, sizeof(END)); > + p += sizeof(END); > + } > + > /* the end node of the second device path has to be retained */ > - memcpy(p + sz1, dp2, sz2 + sizeof(END)); > - ret = p; > + memcpy(p, dp2, sz2); > + p += sz2; > + memcpy(p, &END, sizeof(END)); > } > > return ret; > } > > +/** efi_dp_append() - Append a device to an existing device path. > + * > + * @dp1: First device path > + * @dp2: Second device path > + * > + * Return: concatenated device path or NULL. Caller must free the returned > + * value > + */ > +struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1, > + const struct efi_device_path *dp2) > +{ > + return _efi_dp_append(dp1, dp2, false); > +} > + > +/** 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 > + * @dp2: 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) > +{ > + return _efi_dp_append(dp1, dp2, true); > +} > + > struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp, > const struct efi_device_path *node) > { > @@ -1160,3 +1212,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 VenMedia node in a > + * multi-instance device path that matches > + * a specific GUID. This kind of device paths > + * is found in Boot#### options describing an > + * initrd location > + * > + * @load_option: EFI_LOAD_OPTION containing a valid device path > + * @size: size of the discovered device path > + * @guid: guid to search for > + * > + * Return: device path including the VenMedia node or NULL. > + * Caller must free the returned value > + */ > +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; > + > + for (; lo_len >= sizeof(struct efi_device_path); > + lo_len -= fp->length, fp = (void *)fp + fp->length) { > + if (fp->type != DEVICE_PATH_TYPE_MEDIA_DEVICE || > + fp->sub_type != DEVICE_PATH_SUB_TYPE_VENDOR_PATH) > + continue; The device path is provided by the user and may be constructed incorrectly. lo_len might be negative here. Or the remaining device path might not fit into lo_len. Function efi_dp_check_length() can be used to check the size but it currently accepts only positive values of maxlen. Maybe we should change the type of maxlen to ssize() in that function. Best regards Heinrich > + > + vendor = (struct efi_device_path_vendor *)fp; > + if (!guidcmp(&vendor->guid, &guid)) > + return efi_dp_dup(fp); > + } > + log_debug("VenMedia(%pUl) not found in %ls\n", &guid, lo->label); > + > + return NULL; > +} >
On Sun, Mar 14, 2021 at 08:19:49AM +0100, Heinrich Schuchardt wrote: > > + * Caller must free the returned value [...] > > + */ > > +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; > > + > > + for (; lo_len >= sizeof(struct efi_device_path); > > + lo_len -= fp->length, fp = (void *)fp + fp->length) { > > + if (fp->type != DEVICE_PATH_TYPE_MEDIA_DEVICE || > > + fp->sub_type != DEVICE_PATH_SUB_TYPE_VENDOR_PATH) > > + continue; > > The device path is provided by the user and may be constructed incorrectly. > > lo_len might be negative here. Or the remaining device path might not > fit into lo_len. > > Function efi_dp_check_length() can be used to check the size but it > currently accepts only positive values of maxlen. Maybe we should change > the type of maxlen to ssize() in that function. > Yea, I forgot to fix this one. Regards /Ilias > Best regards > > Heinrich > > > + > > + vendor = (struct efi_device_path_vendor *)fp; > > + if (!guidcmp(&vendor->guid, &guid)) > > + return efi_dp_dup(fp); > > + } > > + log_debug("VenMedia(%pUl) not found in %ls\n", &guid, lo->label); > > + > > + return NULL; > > +} > > >
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..1f55c772dc83 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -282,11 +282,25 @@ struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp) return ndp; } -struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1, - const struct efi_device_path *dp2) +/** _efi_dp_append() - Append or concatenate two device paths. + * Concatenated device path will be separated by a 0xff end + * node. + * + * @dp1: First device path + * @dp2: Second device path + * + * Return: concatenated device path or NULL. Caller must free the returned + * value + */ +static struct efi_device_path *_efi_dp_append(const struct efi_device_path *dp1, + const struct efi_device_path *dp2, + bool concat) { struct efi_device_path *ret; + size_t end_size = sizeof(END); + if (concat) + end_size = 2 * sizeof(END); if (!dp1 && !dp2) { /* return an end node */ ret = efi_dp_dup(&END); @@ -298,18 +312,56 @@ struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1, /* both dp1 and dp2 are non-null */ unsigned sz1 = efi_dp_size(dp1); unsigned sz2 = efi_dp_size(dp2); - void *p = dp_alloc(sz1 + sz2 + sizeof(END)); + void *p = dp_alloc(sz1 + sz2 + end_size); if (!p) return NULL; + ret = p; memcpy(p, dp1, sz1); + p += sz1; + + if (concat) { + memcpy(p, &END, sizeof(END)); + p += sizeof(END); + } + /* the end node of the second device path has to be retained */ - memcpy(p + sz1, dp2, sz2 + sizeof(END)); - ret = p; + memcpy(p, dp2, sz2); + p += sz2; + memcpy(p, &END, sizeof(END)); } return ret; } +/** efi_dp_append() - Append a device to an existing device path. + * + * @dp1: First device path + * @dp2: Second device path + * + * Return: concatenated device path or NULL. Caller must free the returned + * value + */ +struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1, + const struct efi_device_path *dp2) +{ + return _efi_dp_append(dp1, dp2, false); +} + +/** 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 + * @dp2: 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) +{ + return _efi_dp_append(dp1, dp2, true); +} + struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp, const struct efi_device_path *node) { @@ -1160,3 +1212,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 VenMedia node in a + * multi-instance device path that matches + * a specific GUID. This kind of device paths + * is found in Boot#### options describing an + * initrd location + * + * @load_option: EFI_LOAD_OPTION containing a valid device path + * @size: size of the discovered device path + * @guid: guid to search for + * + * Return: device path including the VenMedia node or NULL. + * Caller must free the returned value + */ +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; + + for (; lo_len >= sizeof(struct efi_device_path); + lo_len -= fp->length, fp = (void *)fp + fp->length) { + if (fp->type != DEVICE_PATH_TYPE_MEDIA_DEVICE || + fp->sub_type != DEVICE_PATH_SUB_TYPE_VENDOR_PATH) + continue; + + vendor = (struct efi_device_path_vendor *)fp; + if (!guidcmp(&vendor->guid, &guid)) + return efi_dp_dup(fp); + } + log_debug("VenMedia(%pUl) not found in %ls\n", &guid, lo->label); + + return NULL; +}
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) - 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@linaro.org> --- include/efi_loader.h | 4 ++ lib/efi_loader/efi_device_path.c | 99 ++++++++++++++++++++++++++++++-- 2 files changed, 98 insertions(+), 5 deletions(-) -- 2.30.1