diff mbox series

[v5,2/4] efi_loader: check lowest supported version

Message ID 20230410090732.1676-3-masahisa.kojima@linaro.org
State New
Headers show
Series FMP versioning support | expand

Commit Message

Masahisa Kojima April 10, 2023, 9:07 a.m. UTC
The FMP Payload Header which EDK II capsule generation scripts
insert has a firmware version.
This commit reads the lowest supported version stored in the
device tree, then check if the firmware version in FMP payload header
of the ongoing capsule is equal or greater than the
lowest supported version. If the firmware version is lower than
lowest supported version, capsule update fails.

Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
Changes in v5:
- newly implement the device tree based versioning

Changes in v4:
- use log_err() instead of printf()

Changes in v2:
- add error message when the firmware version is lower than
  lowest supported version

 lib/efi_loader/efi_firmware.c | 124 ++++++++++++++++++++++++++++------
 1 file changed, 103 insertions(+), 21 deletions(-)
diff mbox series

Patch

diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c
index 1c6ef468bf..c88c1bb364 100644
--- a/lib/efi_loader/efi_firmware.c
+++ b/lib/efi_loader/efi_firmware.c
@@ -33,7 +33,7 @@  struct fmp_payload_header {
 	u32 signature;
 	u32 header_size;
 	u32 fw_version;
-	u32 lowest_supported_version;
+	u32 lowest_supported_version; /* not used */
 };
 
 __weak void set_dfu_alt_info(char *interface, char *devstr)
@@ -250,8 +250,6 @@  efi_status_t efi_firmware_capsule_authenticate(const void **p_image,
 {
 	const void *image = *p_image;
 	efi_uintn_t image_size = *p_image_size;
-	u32 fmp_hdr_signature;
-	struct fmp_payload_header *header;
 	void *capsule_payload;
 	efi_status_t status;
 	efi_uintn_t capsule_payload_size;
@@ -264,7 +262,7 @@  efi_status_t efi_firmware_capsule_authenticate(const void **p_image,
 						  &capsule_payload_size);
 
 		if (status == EFI_SECURITY_VIOLATION) {
-			printf("Capsule authentication check failed. Aborting update\n");
+			log_err("Capsule authentication check failed. Aborting update\n");
 			return status;
 		} else if (status != EFI_SUCCESS) {
 			return status;
@@ -278,21 +276,6 @@  efi_status_t efi_firmware_capsule_authenticate(const void **p_image,
 		debug("Updating capsule without authenticating.\n");
 	}
 
-	fmp_hdr_signature = FMP_PAYLOAD_HDR_SIGNATURE;
-	header = (void *)image;
-
-	if (!memcmp(&header->signature, &fmp_hdr_signature,
-		    sizeof(fmp_hdr_signature))) {
-		/*
-		 * When building the capsule with the scripts in
-		 * edk2, a FMP header is inserted above the capsule
-		 * payload. Compensate for this header to get the
-		 * actual payload that is to be updated.
-		 */
-		image += header->header_size;
-		image_size -= header->header_size;
-	}
-
 	*p_image = image;
 	*p_image_size = image_size;
 	return EFI_SUCCESS;
@@ -349,6 +332,105 @@  efi_status_t EFIAPI efi_firmware_get_image_info(
 	return EFI_EXIT(ret);
 }
 
+/**
+ * efi_firmware_get_image_type_id - get image_type_id
+ * @image_index:	image index
+ *
+ * Return the image_type_id identified by the image index.
+ *
+ * Return:		pointer to the image_type_id, NULL if image_index is invalid
+ */
+static
+efi_guid_t *efi_firmware_get_image_type_id(u8 image_index)
+{
+	int i;
+	struct efi_fw_image *fw_array;
+
+	fw_array = update_info.images;
+	for (i = 0; i < num_image_type_guids; i++) {
+		if (fw_array[i].image_index == image_index)
+			return &fw_array[i].image_type_id;
+	}
+
+	return NULL;
+}
+
+/**
+ * efi_firmware_check_lowest_supported_version - check the lowest supported version
+ * @p_image:		Pointer to new image
+ * @p_image_size:	Pointer to size of new image
+ * @image_index:	image_index
+ *
+ * Check the fw_version in FMP payload header is equal to or greather than the
+ * lowest_supported_version stored in the device tree
+ *
+ * Return:		status code
+ */
+static
+efi_status_t efi_firmware_check_lowest_supported_version(const void **p_image,
+							 efi_uintn_t *p_image_size,
+							 u8 image_index)
+{
+	const void *image = *p_image;
+	efi_uintn_t image_size = *p_image_size;
+	const struct fmp_payload_header *header;
+	u32 fmp_hdr_signature = FMP_PAYLOAD_HDR_SIGNATURE;
+
+	/* FMP header is inserted above the capsule payload */
+	header = image;
+	if (header->signature == fmp_hdr_signature) {
+		efi_guid_t *image_type_id;
+		u32 version = 0, lsv = 0;
+
+		image_type_id = efi_firmware_get_image_type_id(image_index);
+		if (!image_type_id)
+			return EFI_INVALID_PARAMETER;
+
+		efi_firmware_get_firmware_version(image_index, image_type_id,
+						  &version, &lsv);
+
+		if (header->fw_version >= lsv) {
+			image += header->header_size;
+			image_size -= header->header_size;
+		} else {
+			log_err("Firmware version %u too low. Expecting >= %u. Aborting update\n",
+				header->fw_version, lsv);
+			return EFI_INCOMPATIBLE_VERSION;
+		}
+	}
+
+	*p_image = image;
+	*p_image_size = image_size;
+
+	return EFI_SUCCESS;
+}
+
+/**
+ * efi_firmware_verify_image - verify image
+ * @p_image:		Pointer to new image
+ * @p_image_size:	Pointer to size of new image
+ * @image_index		Image index
+ *
+ * Verify the capsule file
+ *
+ * Return:		status code
+ */
+static
+efi_status_t efi_firmware_verify_image(const void **p_image,
+				       efi_uintn_t *p_image_size,
+				       u8 image_index)
+{
+	efi_status_t ret;
+
+	ret = efi_firmware_capsule_authenticate(p_image, p_image_size);
+	if (ret != EFI_SUCCESS)
+		return ret;
+
+	ret = efi_firmware_check_lowest_supported_version(p_image, p_image_size, image_index);
+
+	return ret;
+}
+
 #ifdef CONFIG_EFI_CAPSULE_FIRMWARE_FIT
 /*
  * This FIRMWARE_MANAGEMENT_PROTOCOL driver provides a firmware update
@@ -393,7 +475,7 @@  efi_status_t EFIAPI efi_firmware_fit_set_image(
 	if (!image || image_index != 1)
 		return EFI_EXIT(EFI_INVALID_PARAMETER);
 
-	status = efi_firmware_capsule_authenticate(&image, &image_size);
+	status = efi_firmware_verify_image(&image, &image_size, image_index);
 	if (status != EFI_SUCCESS)
 		return EFI_EXIT(status);
 
@@ -454,7 +536,7 @@  efi_status_t EFIAPI efi_firmware_raw_set_image(
 	if (!image)
 		return EFI_EXIT(EFI_INVALID_PARAMETER);
 
-	status = efi_firmware_capsule_authenticate(&image, &image_size);
+	status = efi_firmware_verify_image(&image, &image_size, image_index);
 	if (status != EFI_SUCCESS)
 		return EFI_EXIT(status);