Hi Igor, Thank you for the patch.
On Mon, Oct 12, 2026 at 22:25, Igor Opaniuk <[email protected]> wrote: > Android's fastboot client changes the device lock state with > "fastboot flashing lock|unlock" and asks "flashing get_unlock_ability" > first. U-Boot has the state in the AVB TA (avb_write_lock_state(), used by > the "avb lock/unlock" shell commands) but no fastboot front end for it, so > a device could only be unlocked from the serial console. > > Add the three commands and the "unlocked" variable behind > CONFIG_FASTBOOT_CMD_FLASHING. As AVB requires, both transitions erase > userdata (and metadata, which holds the file based encryption keys) before > the state changes and are refused when userdata cannot be erased; the TA > resets the rollback indexes on a state change. get_unlock_ability always > reports 1 since U-Boot does not evaluate the "OEM unlocking" setting > Android keeps in the frp partition. > > Tested on QEMU virt with OP-TEE over "fastboot tcp": unlock and lock > erase the partitions and flip the state reported by getvar unlocked and > "avb is_unlocked". > > Signed-off-by: Igor Opaniuk <[email protected]> > --- > drivers/fastboot/Kconfig | 11 ++++ > drivers/fastboot/fb_command.c | 119 > ++++++++++++++++++++++++++++++++++++++++++ > drivers/fastboot/fb_getvar.c | 34 ++++++++++++ > include/fastboot.h | 3 ++ > 4 files changed, 167 insertions(+) > [...] > +} > + > +/** > + * flashing_get_unlock_ability() - report whether unlocking is allowed > + * > + * Android gates this on the "OEM unlocking" developer setting, stored by > + * the OS in the frp partition; there is no policy for it in U-Boot, so > + * unlocking is always possible. > + */ > +static void flashing_get_unlock_ability(char *cmd_parameter, char *response) > +{ > + fastboot_okay("1", response); > +} > +#endif /* FASTBOOT_CMD_FLASHING */ > diff --git a/drivers/fastboot/fb_getvar.c b/drivers/fastboot/fb_getvar.c > index e8aa0e09aa6..92c8e702a0b 100644 > --- a/drivers/fastboot/fb_getvar.c > +++ b/drivers/fastboot/fb_getvar.c > @@ -28,6 +28,7 @@ static void getvar_has_slot(char *var_parameter, char > *response); > static void getvar_partition_type(char *part_name, char *response); > static void getvar_partition_size(char *part_name, char *response); > static void getvar_is_userspace(char *var_parameter, char *response); > +static void getvar_unlocked(char *var_parameter, char *response); > > static const struct { > const char *variable; > @@ -88,6 +89,10 @@ static const struct { > .dispatch = getvar_partition_size, > .list = false > #endif > + }, { > + .variable = "unlocked", > + .dispatch = CONFIG_IS_ENABLED(FASTBOOT_CMD_FLASHING, > (getvar_unlocked), (NULL)), > + .list = CONFIG_IS_ENABLED(FASTBOOT_CMD_FLASHING) .list is only to decide if we need to display the unlocked variable when a user runs 'fastboot getvar all' In the case of FASTBOOT_CMD_FLASHING not being set, the "unlocked" variable will still be available, but .dispatch will be a NULL function. Thus, `fastboot getvar unlocked` can still be called and in fastboot_getvar() we have: getvar_dispatch[i].dispatch(var_parameter, response); Which will call a NULL function pointer. To avoid this issue, can we guard the unlocked section similarly to other variables, such as 'has-slot' ? Thanks Mattijs
