diff mbox series

[1/5,v4] efi_loader: Add device path related functions for initrd via Boot####

Message ID 20210317195505.337105-2-ilias.apalodimas@linaro.org
State Accepted
Commit 76e8acce12fe6e914fdab422c8af44956c1dac04
Headers show
Series Loadfile2 for initrd loading | expand

Commit Message

Ilias Apalodimas March 17, 2021, 7:54 p.m. UTC
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 | 106 +++++++++++++++++++++++++++++--
 2 files changed, 105 insertions(+), 5 deletions(-)

-- 
2.31.0

Comments

Heinrich Schuchardt March 17, 2021, 8:32 p.m. UTC | #1
On 3/17/21 8:54 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 | 106 +++++++++++++++++++++++++++++--

>   2 files changed, 105 insertions(+), 5 deletions(-)

>

> diff --git a/include/efi_loader.h b/include/efi_loader.h

> index 68daa1a4a9dc..5d534e69bb59 100644

> --- a/include/efi_loader.h

> +++ b/include/efi_loader.h

> @@ -744,6 +744,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..1491251e224b 100644

> --- a/lib/efi_loader/efi_device_path.c

> +++ b/lib/efi_loader/efi_device_path.c

> @@ -282,11 +282,30 @@ 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_or_concatenate() - Append or concatenate two device paths.


Wrong line. You can't put his on the line with /**.

> + *				     Concatenated device path will be separated

> + *				     by a sub-type 0xff end node

> + *

> + * @dp1:	First device path

> + * @dp2:	Second device path

> + * @concat:	If true the two device paths will be concatenated and separated

> + *		by an end of entrire device path sub-type 0xff end node.

> + *		If true the second device path will be appended to the first and

> + *		terminated by an end node

> + *

> + * Return:	concatenated device path or NULL. Caller must free the returned

> + *              value


Multiple line comments for Return: have to start on a new line or
formatting by Sphinx will fail.

> + */

> +static struct

> +efi_device_path *efi_dp_append_or_concatenate(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 +317,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_or_concatenate(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_or_concatenate(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 +1217,42 @@ 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


@lo:

I will correct the function comments when merging.

Reviewed-by: Heinrich Schuchardt <xypron.glpk@gmx.de>


> + * @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 (lo_len < 0 || efi_dp_check_length(fp, lo_len) < 0)

> +			break;

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

> +}

>
diff mbox series

Patch

diff --git a/include/efi_loader.h b/include/efi_loader.h
index 68daa1a4a9dc..5d534e69bb59 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -744,6 +744,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..1491251e224b 100644
--- a/lib/efi_loader/efi_device_path.c
+++ b/lib/efi_loader/efi_device_path.c
@@ -282,11 +282,30 @@  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_or_concatenate() - Append or concatenate two device paths.
+ *				     Concatenated device path will be separated
+ *				     by a sub-type 0xff end node
+ *
+ * @dp1:	First device path
+ * @dp2:	Second device path
+ * @concat:	If true the two device paths will be concatenated and separated
+ *		by an end of entrire device path sub-type 0xff end node.
+ *		If true the second device path will be appended to the first and
+ *		terminated by an end node
+ *
+ * Return:	concatenated device path or NULL. Caller must free the returned
+ *              value
+ */
+static struct
+efi_device_path *efi_dp_append_or_concatenate(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 +317,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_or_concatenate(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_or_concatenate(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 +1217,42 @@  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 (lo_len < 0 || efi_dp_check_length(fp, lo_len) < 0)
+			break;
+		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;
+}