[U-Boot] [PATCH v2 1/1] efi_loader: bootmgr: support BootNext and BootCurrent variable behavior

AKASHI Takahiro takahiro.akashi at linaro.org
Fri Mar 8 00:51:47 UTC 2019


On Wed, Mar 06, 2019 at 05:21:54AM +0100, Heinrich Schuchardt wrote:
> On 3/6/19 1:17 AM, AKASHI Takahiro wrote:
> > On Tue, Mar 05, 2019 at 08:38:40PM +0100, Heinrich Schuchardt wrote:
> >> On 3/5/19 2:58 AM, AKASHI Takahiro wrote:
> >>> See UEFI v2.7, section 3.1.2 for details of the specification.
> >>>
> >>> With efidebug command, you can run any EFI boot option as follows:
> >>>   => efi boot add 1 SHELL ...
> >>>   => efi boot add 2 HELLO ...
> >>>   => efi boot order 1 2
> >>>   => efi bootmgr
> >>>      (starting SHELL ...)
> >>>
> >>>   => efi boot next 2
> >>>   => efi bootmgr
> >>>      (starting HELLO ...)
> >>>   => env print -e
> >>>   <snip ...>
> >>>   BootCurrent: {boot,run}(blob)
> >>>   00000000:  02 00                    ..
> >>>   BootOrder: {boot,run}(blob)
> >>>   00000000:  01 00 02 00              ....
> >>>
> >>> Signed-off-by: AKASHI Takahiro <takahiro.akashi at linaro.org>
> >>
> >> Please, use scripts/get_maintainer.pl to determine the correct
> >> recipients. You missed Alex's new email address.
> > 
> > Okay.
> > 
> >>> ---
> >>>  lib/efi_loader/efi_bootmgr.c | 40 ++++++++++++++++++++++++++++++++----
> >>>  1 file changed, 36 insertions(+), 4 deletions(-)
> >>>
> >>> diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
> >>> index 417016102b48..1575c5c09e46 100644
> >>> --- a/lib/efi_loader/efi_bootmgr.c
> >>> +++ b/lib/efi_loader/efi_bootmgr.c
> >>> @@ -141,6 +141,7 @@ static void *try_load_entry(uint16_t n, struct efi_device_path **device_path,
> >>>  	efi_deserialize_load_option(&lo, load_option);
> >>>  
> >>>  	if (lo.attributes & LOAD_OPTION_ACTIVE) {
> >>> +		u32 attributes;
> >>>  		efi_status_t ret;
> >>>  
> >>>  		debug("%s: trying to load \"%ls\" from %pD\n",
> >>> @@ -151,6 +152,16 @@ static void *try_load_entry(uint16_t n, struct efi_device_path **device_path,
> >>>  		if (ret != EFI_SUCCESS)
> >>>  			goto error;
> >>>  
> >>> +		attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS |
> >>> +			     EFI_VARIABLE_RUNTIME_ACCESS;
> >>> +		size = sizeof(n);
> >>> +		ret = EFI_CALL(efi_set_variable(
> >>> +				L"BootCurrent",
> >>> +				(efi_guid_t *)&efi_global_variable_guid,
> >>> +				attributes, size, &n));
> >>> +		if (ret != EFI_SUCCESS)
> >>> +			goto error;
> >>> +
> >>>  		printf("Booting: %ls\n", lo.label);
> >>>  		efi_dp_split_file_path(lo.file_path, device_path, file_path);
> >>>  	}
> >>> @@ -162,21 +173,42 @@ error:
> >>>  }
> >>>  
> >>>  /*
> >>> - * Attempt to load, in the order specified by BootOrder EFI variable, the
> >>> - * available load-options, finding and returning the first one that can
> >>> - * be loaded successfully.
> >>> + * Attempt to load from BootNext or in the order specified by BootOrder
> >>> + * EFI variable, the available load-options, finding and returning
> >>> + * the first one that can be loaded successfully.
> >>>   */
> >>>  void *efi_bootmgr_load(struct efi_device_path **device_path,
> >>>  		       struct efi_device_path **file_path)
> >>>  {
> >>> -	uint16_t *bootorder;
> >>> +	u16 bootnext, *bootorder;
> >>
> >> bootnext has enough space for the terminating \n. That is way too small.
> >>
> >> You want to call efi_get_variable() twice. Get the buffer size needed in
> >> the first round. malloc() a buffer. Then actually read the variable.
> >> Finally free() the buffer.
> > 
> > No.
> > "BootNext" is always a 16-bit integer, and "bootnext" is a u16 variable,
> > not a pointer.
> > So we don't need to call get_variable() twice. That is why I didn't use
> > get_var() here. I believe that you seem to like "code efficiency."
> 
> I see.
> 
> > 
> >>>  	efi_uintn_t size;
> >>>  	void *image = NULL;
> >>>  	int i, num;
> >>> +	efi_status_t ret;
> >>>  
> >>>  	bs = systab.boottime;
> >>>  	rs = systab.runtime;
> >>>  
> >>> +	/* BootNext */
> >>> +	size = sizeof(bootnext);
> >>> +	ret = EFI_CALL(efi_get_variable(L"BootNext",
> >>> +					(efi_guid_t *)&efi_global_variable_guid,
> >>> +					NULL, &size, &bootnext));
> >>> +	if (ret == EFI_SUCCESS) {
> 
> As we expect the variable to have size 2, we should check the size field
> too.

Here we see EFI_SUCCESS only if the size of variable is 1 or 2.
In case of size of 1, it's not in correct format, but I think
that it's safe and acceptable.
So basically I don't think that we need check the size.
(except for a message below)

> >>
> >> The expected value of ret for an existing variable of size > 0 is
> >> EFI_BUFFER_TOO_SMALL.
> 
> Now let's assume that the variable has been created with the wrong size
> (e.g. using `env set -e`). In that case we should either try to delete
> it or write an error message or both.

I simply want to write a message.

> >>
> >>> +		/* delete BootNext */
> >>> +		ret = EFI_CALL(efi_set_variable(
> >>> +					L"BootNext",
> >>> +					(efi_guid_t *)&efi_global_variable_guid,
> >>> +					0, 0, &bootnext));
> >>> +		if (ret == EFI_SUCCESS) {
> >>
> >> Why would loading the boot entry depend on the return value here?
> > 
> > Deleting "BootNex" is required by UEFI spec.
> > In addition, if we fail to delete it, we will see the same application
> > start again when executing boot manager next time.
> > 
> 
> BootNext is a non-volatile variable. Once we have a non-volatile backend
> for variables the call may fail because the the NV storage is not
> writable. Another case leading to an error would be the variable having
> been created with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
> (which we do not yet support).

Since we have no idea how we will manage "non-volatile" variables
in UEFI U-Boot, it is still early to discuss any behavior based on
not-agreed assumptions.

For example, if NV storage is not writable, we will probably not be
able to define BootNext variable.

> Should this stop the boot process? If yes, we need at least an error
> message. But I would propose that if the NV storage is not writable we
> continue booting after writing a debug message.

I'm afraid that this can be used as a DoS attack.
Anyhow, we should be "conservative" :)

-Takahiro Akashi


> Best regards
> 
> Heinrich
> 
> > Thanks,
> > -Takahiro Akashi
> > 
> >>> +			image = try_load_entry(bootnext,
> >>
> >> Best regards
> >>
> >> Heinrich
> >>
> >>> +					       device_path, file_path);
> >>> +			if (image)
> >>> +				goto error;
> >>> +		}
> >>> +	}
> >>> +
> >>> +	/* BootOrder */
> >>>  	bootorder = get_var(L"BootOrder", &efi_global_variable_guid, &size);
> >>>  	if (!bootorder) {
> >>>  		printf("BootOrder not defined\n");
> >>>
> >>
> > 
> 


More information about the U-Boot mailing list