diff mbox series

[v3,2/3] efi_firmware: set EFI capsule dfu_alt_info env explicitly

Message ID 20250213195351.3518305-3-j-humphreys@ti.com
State Changes Requested
Delegated to: Ilias Apalodimas
Headers show
Series EFI Capsule update explicitly sets dfu_alt_info | expand

Commit Message

Jon Humphreys Feb. 13, 2025, 7:53 p.m. UTC
The current implementation of EFI capsule update uses set_dfu_alt_info() to
set the dfu_alt_info environment variable with the settings it requires.
However, set_dfu_alt_info() is doing this for all DFU operations, even
those unrelated to capsule update.

Thus other uses of DFU, such as DFU boot which sets its own value for the
dfu_alt_info environment variable, will have that setting overwritten with
the capsule update setting. Similarly, any user defined value for the
dfu_alt_info environment variable would get overwritten when any DFU
operation was performed, including simply performing a "dfu 0 list"
command.

The solution is stop using the set_dfu_alt_info() mechanism to set the
dfu_alt_info environment variable and instead explicitly set it to the
capsule update's setting just before performing the capsule update's DFU
operation, and then restore the environment variable back to its original
value.

This patch implements the explicit setting and restoring of the
dfu_alt_info environment variable as part of the EFI capsule update
operation.

The fix is fully implemented in a subsequent patch that removes the capsule
update dfu_alt_info support in set_dfu_alt_info().

Signed-off-by: Jonathan Humphreys <j-humphreys@ti.com>
---
 lib/efi_loader/efi_firmware.c | 39 ++++++++++++++++++++++++++++++++---
 1 file changed, 36 insertions(+), 3 deletions(-)

Comments

Mattijs Korpershoek Feb. 14, 2025, 4:54 p.m. UTC | #1
Hi Jon,

Thank you for the patch.

On jeu., févr. 13, 2025 at 13:53, Jonathan Humphreys <j-humphreys@ti.com> wrote:

> The current implementation of EFI capsule update uses set_dfu_alt_info() to
> set the dfu_alt_info environment variable with the settings it requires.
> However, set_dfu_alt_info() is doing this for all DFU operations, even
> those unrelated to capsule update.
>
> Thus other uses of DFU, such as DFU boot which sets its own value for the
> dfu_alt_info environment variable, will have that setting overwritten with
> the capsule update setting. Similarly, any user defined value for the
> dfu_alt_info environment variable would get overwritten when any DFU
> operation was performed, including simply performing a "dfu 0 list"
> command.
>
> The solution is stop using the set_dfu_alt_info() mechanism to set the
> dfu_alt_info environment variable and instead explicitly set it to the
> capsule update's setting just before performing the capsule update's DFU
> operation, and then restore the environment variable back to its original
> value.
>
> This patch implements the explicit setting and restoring of the
> dfu_alt_info environment variable as part of the EFI capsule update
> operation.
>
> The fix is fully implemented in a subsequent patch that removes the capsule
> update dfu_alt_info support in set_dfu_alt_info().
>
> Signed-off-by: Jonathan Humphreys <j-humphreys@ti.com>

Reviewed-by: Mattijs Korpershoek <mkorpershoek@baylibre.com>

> ---
Ilias Apalodimas Feb. 18, 2025, 7:54 a.m. UTC | #2
Hi Jonathan, 

The patch looks correct but there's a few more things to address.

On Thu, Feb 13, 2025 at 01:53:50PM -0600, Jonathan Humphreys wrote:
> The current implementation of EFI capsule update uses set_dfu_alt_info() to
> set the dfu_alt_info environment variable with the settings it requires.
> However, set_dfu_alt_info() is doing this for all DFU operations, even
> those unrelated to capsule update.
> 
> Thus other uses of DFU, such as DFU boot which sets its own value for the
> dfu_alt_info environment variable, will have that setting overwritten with
> the capsule update setting. Similarly, any user defined value for the
> dfu_alt_info environment variable would get overwritten when any DFU
> operation was performed, including simply performing a "dfu 0 list"
> command.
> 
> The solution is stop using the set_dfu_alt_info() mechanism to set the
> dfu_alt_info environment variable and instead explicitly set it to the
> capsule update's setting just before performing the capsule update's DFU
> operation, and then restore the environment variable back to its original
> value.
> 
> This patch implements the explicit setting and restoring of the
> dfu_alt_info environment variable as part of the EFI capsule update
> operation.
> 
> The fix is fully implemented in a subsequent patch that removes the capsule
> update dfu_alt_info support in set_dfu_alt_info().
> 
> Signed-off-by: Jonathan Humphreys <j-humphreys@ti.com>
> ---
>  lib/efi_loader/efi_firmware.c | 39 ++++++++++++++++++++++++++++++++---
>  1 file changed, 36 insertions(+), 3 deletions(-)
> 
> diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c
> index 5a754c9cd03..1a1cf3b55e1 100644
> --- a/lib/efi_loader/efi_firmware.c
> +++ b/lib/efi_loader/efi_firmware.c
> @@ -649,8 +649,10 @@ efi_status_t EFIAPI efi_firmware_fit_set_image(
>  	efi_status_t (*progress)(efi_uintn_t completion),
>  	u16 **abort_reason)
>  {
> +	int ret;
>  	efi_status_t status;
>  	struct fmp_state state = { 0 };
> +	char *orig_dfu_env;
>  
>  	EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image,
>  		  image_size, vendor_code, progress, abort_reason);
> @@ -663,7 +665,22 @@ efi_status_t EFIAPI efi_firmware_fit_set_image(
>  	if (status != EFI_SUCCESS)
>  		return EFI_EXIT(status);
>  
> -	if (fit_update(image))
> +	orig_dfu_env = strdup(env_get("dfu_alt_info"));

strdup might fail, we need to check that the orig_dfu_env is !NULL

> +	if (env_set("dfu_alt_info", update_info.dfu_string)) {
> +		log_err("unable to set env variable \"dfu_alt_info\"!\n");
> +		free(orig_dfu_env);
> +		return EFI_EXIT(EFI_DEVICE_ERROR);
> +	}
> +
> +	ret = fit_update(image);

> +
> +	if (env_set("dfu_alt_info", orig_dfu_env)) {
> +		log_err("unable to set env variable \"dfu_alt_info\"!\n");
> +		ret = 1;

This will work but for consistency just use an EFI defined error e.g 
ret = EFI_DEVICE_ERROR;

> +	}
> +	free(orig_dfu_env);
> +
> +	if (ret)

I am not 100% sure this exiting here is useful. If fit_update has succeeded
we have updated our firmware. So I think we should just add a warning here,
that resetting the dfu to its original value failed, and any further dfu
ops will fail. 

>  		return EFI_EXIT(EFI_DEVICE_ERROR);
>  
>  	efi_firmware_set_fmp_state_var(&state, image_index);
> @@ -717,6 +734,7 @@ efi_status_t EFIAPI efi_firmware_raw_set_image(
>  	u8 dfu_alt_num;
>  	efi_status_t status;
>  	struct fmp_state state = { 0 };
> +	char *orig_dfu_env;
>  
>  	EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image,
>  		  image_size, vendor_code, progress, abort_reason);
> @@ -747,8 +765,23 @@ efi_status_t EFIAPI efi_firmware_raw_set_image(
>  		}
>  	}
>  
> -	if (dfu_write_by_alt(dfu_alt_num, (void *)image, image_size,
> -			     NULL, NULL))
> +	orig_dfu_env = strdup(env_get("dfu_alt_info"));
> +	if (env_set("dfu_alt_info", update_info.dfu_string)) {
> +		log_err("unable to set env variable \"dfu_alt_info\"!\n");
> +		free(orig_dfu_env);
> +		return EFI_EXIT(EFI_DEVICE_ERROR);
> +	}
> +
> +	ret = dfu_write_by_alt(dfu_alt_num, (void *)image, image_size,
> +			       NULL, NULL);
> +
> +	if (env_set("dfu_alt_info", orig_dfu_env)) {
> +		log_err("unable to set env variable \"dfu_alt_info\"!\n");
> +		ret = 1;
> +	}
> +	free(orig_dfu_env);
> +
> +	if (ret)
>  		return EFI_EXIT(EFI_DEVICE_ERROR);
>  
>  	efi_firmware_set_fmp_state_var(&state, image_index);
> -- 
> 2.34.1
> 

Thanks
/Ilias
Jon Humphreys Feb. 24, 2025, 2:38 a.m. UTC | #3
Ilias Apalodimas <ilias.apalodimas@linaro.org> writes:

> Hi Jonathan, 
>
> The patch looks correct but there's a few more things to address.
>
> On Thu, Feb 13, 2025 at 01:53:50PM -0600, Jonathan Humphreys wrote:
>> The current implementation of EFI capsule update uses set_dfu_alt_info() to
>> set the dfu_alt_info environment variable with the settings it requires.
>> However, set_dfu_alt_info() is doing this for all DFU operations, even
>> those unrelated to capsule update.
>> 
>> Thus other uses of DFU, such as DFU boot which sets its own value for the
>> dfu_alt_info environment variable, will have that setting overwritten with
>> the capsule update setting. Similarly, any user defined value for the
>> dfu_alt_info environment variable would get overwritten when any DFU
>> operation was performed, including simply performing a "dfu 0 list"
>> command.
>> 
>> The solution is stop using the set_dfu_alt_info() mechanism to set the
>> dfu_alt_info environment variable and instead explicitly set it to the
>> capsule update's setting just before performing the capsule update's DFU
>> operation, and then restore the environment variable back to its original
>> value.
>> 
>> This patch implements the explicit setting and restoring of the
>> dfu_alt_info environment variable as part of the EFI capsule update
>> operation.
>> 
>> The fix is fully implemented in a subsequent patch that removes the capsule
>> update dfu_alt_info support in set_dfu_alt_info().
>> 
>> Signed-off-by: Jonathan Humphreys <j-humphreys@ti.com>
>> ---
>>  lib/efi_loader/efi_firmware.c | 39 ++++++++++++++++++++++++++++++++---
>>  1 file changed, 36 insertions(+), 3 deletions(-)
>> 
>> diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c
>> index 5a754c9cd03..1a1cf3b55e1 100644
>> --- a/lib/efi_loader/efi_firmware.c
>> +++ b/lib/efi_loader/efi_firmware.c
>> @@ -649,8 +649,10 @@ efi_status_t EFIAPI efi_firmware_fit_set_image(
>>  	efi_status_t (*progress)(efi_uintn_t completion),
>>  	u16 **abort_reason)
>>  {
>> +	int ret;
>>  	efi_status_t status;
>>  	struct fmp_state state = { 0 };
>> +	char *orig_dfu_env;
>>  
>>  	EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image,
>>  		  image_size, vendor_code, progress, abort_reason);
>> @@ -663,7 +665,22 @@ efi_status_t EFIAPI efi_firmware_fit_set_image(
>>  	if (status != EFI_SUCCESS)
>>  		return EFI_EXIT(status);
>>  
>> -	if (fit_update(image))
>> +	orig_dfu_env = strdup(env_get("dfu_alt_info"));
>
> strdup might fail, we need to check that the orig_dfu_env is !NULL
>
>> +	if (env_set("dfu_alt_info", update_info.dfu_string)) {
>> +		log_err("unable to set env variable \"dfu_alt_info\"!\n");
>> +		free(orig_dfu_env);
>> +		return EFI_EXIT(EFI_DEVICE_ERROR);
>> +	}
>> +
>> +	ret = fit_update(image);
>
>> +
>> +	if (env_set("dfu_alt_info", orig_dfu_env)) {
>> +		log_err("unable to set env variable \"dfu_alt_info\"!\n");
>> +		ret = 1;
>
> This will work but for consistency just use an EFI defined error e.g 
> ret = EFI_DEVICE_ERROR;
>
>> +	}
>> +	free(orig_dfu_env);
>> +
>> +	if (ret)
>
> I am not 100% sure this exiting here is useful. If fit_update has succeeded
> we have updated our firmware. So I think we should just add a warning here,
> that resetting the dfu to its original value failed, and any further dfu
> ops will fail. 
>

Hi Ilias, thanks for the review.

The exit here is for either fit_update() failing OR we cannot restore the
original value for the dfu_alt_info variable, so it is needed, at least in
the fit_update() failure case.

I was trying to keep the control flow simpler by handling the 2 failure
events together. Are you wanting to separate the 2 failure conditions, and
return EFI_DEVICE_ERROR if fit_update() fails, and emit a warning and
return EFI_SUCCESS if restoring the env variable fails?

Jon

>>  		return EFI_EXIT(EFI_DEVICE_ERROR);
>>  
>>  	efi_firmware_set_fmp_state_var(&state, image_index);
>> @@ -717,6 +734,7 @@ efi_status_t EFIAPI efi_firmware_raw_set_image(
>>  	u8 dfu_alt_num;
>>  	efi_status_t status;
>>  	struct fmp_state state = { 0 };
>> +	char *orig_dfu_env;
>>  
>>  	EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image,
>>  		  image_size, vendor_code, progress, abort_reason);
>> @@ -747,8 +765,23 @@ efi_status_t EFIAPI efi_firmware_raw_set_image(
>>  		}
>>  	}
>>  
>> -	if (dfu_write_by_alt(dfu_alt_num, (void *)image, image_size,
>> -			     NULL, NULL))
>> +	orig_dfu_env = strdup(env_get("dfu_alt_info"));
>> +	if (env_set("dfu_alt_info", update_info.dfu_string)) {
>> +		log_err("unable to set env variable \"dfu_alt_info\"!\n");
>> +		free(orig_dfu_env);
>> +		return EFI_EXIT(EFI_DEVICE_ERROR);
>> +	}
>> +
>> +	ret = dfu_write_by_alt(dfu_alt_num, (void *)image, image_size,
>> +			       NULL, NULL);
>> +
>> +	if (env_set("dfu_alt_info", orig_dfu_env)) {
>> +		log_err("unable to set env variable \"dfu_alt_info\"!\n");
>> +		ret = 1;
>> +	}
>> +	free(orig_dfu_env);
>> +
>> +	if (ret)
>>  		return EFI_EXIT(EFI_DEVICE_ERROR);
>>  
>>  	efi_firmware_set_fmp_state_var(&state, image_index);
>> -- 
>> 2.34.1
>> 
>
> Thanks
> /Ilias
Ilias Apalodimas Feb. 26, 2025, 8:54 a.m. UTC | #4
Hi Jonathan,

Apologies for the late reply

On Mon, 24 Feb 2025 at 04:38, Jon Humphreys <j-humphreys@ti.com> wrote:

[...]
> >> +
> >> +    ret = fit_update(image);
> >
> >> +
> >> +    if (env_set("dfu_alt_info", orig_dfu_env)) {
> >> +            log_err("unable to set env variable \"dfu_alt_info\"!\n");
> >> +            ret = 1;
> >
> > This will work but for consistency just use an EFI defined error e.g
> > ret = EFI_DEVICE_ERROR;
> >
> >> +    }
> >> +    free(orig_dfu_env);
> >> +
> >> +    if (ret)
> >
> > I am not 100% sure this exiting here is useful. If fit_update has succeeded
> > we have updated our firmware. So I think we should just add a warning here,
> > that resetting the dfu to its original value failed, and any further dfu
> > ops will fail.
> >
>
> Hi Ilias, thanks for the review.
>
> The exit here is for either fit_update() failing OR we cannot restore the
> original value for the dfu_alt_info variable, so it is needed, at least in
> the fit_update() failure case.
>
> I was trying to keep the control flow simpler by handling the 2 failure
> events together. Are you wanting to separate the 2 failure conditions, and
> return EFI_DEVICE_ERROR if fit_update() fails, and emit a warning and
> return EFI_SUCCESS if restoring the env variable fails?

Yes, I think it's better

Thanks
/Ilias
>
> Jon
>
> >>              return EFI_EXIT(EFI_DEVICE_ERROR);
> >>
> >>      efi_firmware_set_fmp_state_var(&state, image_index);
> >> @@ -717,6 +734,7 @@ efi_status_t EFIAPI efi_firmware_raw_set_image(
> >>      u8 dfu_alt_num;
> >>      efi_status_t status;
> >>      struct fmp_state state = { 0 };
> >> +    char *orig_dfu_env;
> >>
> >>      EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image,
> >>                image_size, vendor_code, progress, abort_reason);
> >> @@ -747,8 +765,23 @@ efi_status_t EFIAPI efi_firmware_raw_set_image(
> >>              }
> >>      }
> >>
> >> -    if (dfu_write_by_alt(dfu_alt_num, (void *)image, image_size,
> >> -                         NULL, NULL))
> >> +    orig_dfu_env = strdup(env_get("dfu_alt_info"));
> >> +    if (env_set("dfu_alt_info", update_info.dfu_string)) {
> >> +            log_err("unable to set env variable \"dfu_alt_info\"!\n");
> >> +            free(orig_dfu_env);
> >> +            return EFI_EXIT(EFI_DEVICE_ERROR);
> >> +    }
> >> +
> >> +    ret = dfu_write_by_alt(dfu_alt_num, (void *)image, image_size,
> >> +                           NULL, NULL);
> >> +
> >> +    if (env_set("dfu_alt_info", orig_dfu_env)) {
> >> +            log_err("unable to set env variable \"dfu_alt_info\"!\n");
> >> +            ret = 1;
> >> +    }
> >> +    free(orig_dfu_env);
> >> +
> >> +    if (ret)
> >>              return EFI_EXIT(EFI_DEVICE_ERROR);
> >>
> >>      efi_firmware_set_fmp_state_var(&state, image_index);
> >> --
> >> 2.34.1
> >>
> >
> > Thanks
> > /Ilias
diff mbox series

Patch

diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c
index 5a754c9cd03..1a1cf3b55e1 100644
--- a/lib/efi_loader/efi_firmware.c
+++ b/lib/efi_loader/efi_firmware.c
@@ -649,8 +649,10 @@  efi_status_t EFIAPI efi_firmware_fit_set_image(
 	efi_status_t (*progress)(efi_uintn_t completion),
 	u16 **abort_reason)
 {
+	int ret;
 	efi_status_t status;
 	struct fmp_state state = { 0 };
+	char *orig_dfu_env;
 
 	EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image,
 		  image_size, vendor_code, progress, abort_reason);
@@ -663,7 +665,22 @@  efi_status_t EFIAPI efi_firmware_fit_set_image(
 	if (status != EFI_SUCCESS)
 		return EFI_EXIT(status);
 
-	if (fit_update(image))
+	orig_dfu_env = strdup(env_get("dfu_alt_info"));
+	if (env_set("dfu_alt_info", update_info.dfu_string)) {
+		log_err("unable to set env variable \"dfu_alt_info\"!\n");
+		free(orig_dfu_env);
+		return EFI_EXIT(EFI_DEVICE_ERROR);
+	}
+
+	ret = fit_update(image);
+
+	if (env_set("dfu_alt_info", orig_dfu_env)) {
+		log_err("unable to set env variable \"dfu_alt_info\"!\n");
+		ret = 1;
+	}
+	free(orig_dfu_env);
+
+	if (ret)
 		return EFI_EXIT(EFI_DEVICE_ERROR);
 
 	efi_firmware_set_fmp_state_var(&state, image_index);
@@ -717,6 +734,7 @@  efi_status_t EFIAPI efi_firmware_raw_set_image(
 	u8 dfu_alt_num;
 	efi_status_t status;
 	struct fmp_state state = { 0 };
+	char *orig_dfu_env;
 
 	EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image,
 		  image_size, vendor_code, progress, abort_reason);
@@ -747,8 +765,23 @@  efi_status_t EFIAPI efi_firmware_raw_set_image(
 		}
 	}
 
-	if (dfu_write_by_alt(dfu_alt_num, (void *)image, image_size,
-			     NULL, NULL))
+	orig_dfu_env = strdup(env_get("dfu_alt_info"));
+	if (env_set("dfu_alt_info", update_info.dfu_string)) {
+		log_err("unable to set env variable \"dfu_alt_info\"!\n");
+		free(orig_dfu_env);
+		return EFI_EXIT(EFI_DEVICE_ERROR);
+	}
+
+	ret = dfu_write_by_alt(dfu_alt_num, (void *)image, image_size,
+			       NULL, NULL);
+
+	if (env_set("dfu_alt_info", orig_dfu_env)) {
+		log_err("unable to set env variable \"dfu_alt_info\"!\n");
+		ret = 1;
+	}
+	free(orig_dfu_env);
+
+	if (ret)
 		return EFI_EXIT(EFI_DEVICE_ERROR);
 
 	efi_firmware_set_fmp_state_var(&state, image_index);