Hi Mattijs,

On Sun, Oct 11, 2026 at 4:42 PM 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".
>

This exact patch ("avb: device lock state, root key provisioning and
in-place verification")
fails to compile in every configuration that has fastboot but not
FASTBOOT_CMD_FLASHING.
The extended CI job run just caught that. I’ll address it in v2.

> Signed-off-by: Igor Opaniuk <[email protected]>
> ---
>  drivers/fastboot/Kconfig      |  11 ++++
>  drivers/fastboot/fb_command.c | 115 
> ++++++++++++++++++++++++++++++++++++++++++
>  drivers/fastboot/fb_getvar.c  |  34 +++++++++++++
>  include/fastboot.h            |   3 ++
>  4 files changed, 163 insertions(+)
>
> diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig
> index 1887f78bcec..b5ca8c8dcb5 100644
> --- a/drivers/fastboot/Kconfig
> +++ b/drivers/fastboot/Kconfig
> @@ -248,6 +248,17 @@ config FASTBOOT_MBR_NAME
>           specified on the "fastboot flash" command line matches the value
>           defined here. The default target name for updating MBR is "mbr".
>
> +config FASTBOOT_CMD_FLASHING
> +       bool "Enable the 'flashing lock/unlock/get_unlock_ability' commands"
> +       depends on FASTBOOT_FLASH_MMC && AVB_VERIFY && OPTEE_TA_AVB
> +       help
> +         Add the Android "flashing lock", "flashing unlock" and "flashing
> +         get_unlock_ability" commands and the "unlocked" variable. The device
> +         lock state is the one AVB enforces, kept by the OP-TEE AVB TA. As
> +         required by AVB, the userdata (and metadata) partition is erased
> +         before the state changes in either direction, and the TA resets the
> +         rollback indexes on a state change.
> +
>  config FASTBOOT_CMD_OEM_FORMAT
>         bool "Enable the 'oem format' command"
>         depends on FASTBOOT_FLASH_MMC && CMD_GPT
> diff --git a/drivers/fastboot/fb_command.c b/drivers/fastboot/fb_command.c
> index 18d86988f4c..2e5ce981aff 100644
> --- a/drivers/fastboot/fb_command.c
> +++ b/drivers/fastboot/fb_command.c
> @@ -16,6 +16,9 @@
>  #include <stdlib.h>
>  #include <vsprintf.h>
>  #include <linux/printk.h>
> +#if CONFIG_IS_ENABLED(FASTBOOT_CMD_FLASHING)
> +#include <avb_verify.h>
> +#endif
>
>  /**
>   * image_size - final fastboot image size
> @@ -47,6 +50,9 @@ static void oem_console(char *, char *);
>  static void oem_board(char *, char *);
>  static void run_ucmd(char *, char *);
>  static void run_acmd(char *, char *);
> +static void flashing_lock(char *, char *);
> +static void flashing_unlock(char *, char *);
> +static void flashing_get_unlock_ability(char *, char *);
>
>  static const struct {
>         const char *command;
> @@ -128,6 +134,18 @@ static const struct {
>                 .command = "ACmd",
>                 .dispatch = CONFIG_IS_ENABLED(FASTBOOT_UUU_SUPPORT, 
> (run_acmd), (NULL))
>         },
> +       [FASTBOOT_COMMAND_FLASHING_LOCK] = {
> +               .command = "flashing lock",
> +               .dispatch = CONFIG_IS_ENABLED(FASTBOOT_CMD_FLASHING, 
> (flashing_lock), (NULL))
> +       },
> +       [FASTBOOT_COMMAND_FLASHING_UNLOCK] = {
> +               .command = "flashing unlock",
> +               .dispatch = CONFIG_IS_ENABLED(FASTBOOT_CMD_FLASHING, 
> (flashing_unlock), (NULL))
> +       },
> +       [FASTBOOT_COMMAND_FLASHING_GET_UNLOCK_ABILITY] = {
> +               .command = "flashing get_unlock_ability",
> +               .dispatch = CONFIG_IS_ENABLED(FASTBOOT_CMD_FLASHING, 
> (flashing_get_unlock_ability), (NULL))
> +       },
>  };
>
>  /**
> @@ -589,3 +607,100 @@ static void __maybe_unused oem_board(char 
> *cmd_parameter, char *response)
>  {
>         fastboot_oem_board(cmd_parameter, (void *)fastboot_buf_addr, 
> image_size, response);
>  }
> +
> +#if CONFIG_IS_ENABLED(FASTBOOT_CMD_FLASHING)
> +/**
> + * flashing_set_lock() - set the AVB device lock state through the AVB TA
> + *
> + * @locked: new state
> + * @response: fastboot response buffer, filled on failure
> + * Return: 0 on success, -1 otherwise
> + */
> +static int flashing_set_lock(bool locked, char *response)
> +{
> +       AvbOps *ops;
> +       AvbIOResult rc;
> +
> +       ops = avb_ops_alloc(CONFIG_FASTBOOT_FLASH_MMC_DEV);
> +       if (!ops) {
> +               fastboot_fail("cannot allocate AVB ops", response);
> +               return -1;
> +       }
> +
> +       rc = avb_write_lock_state(ops, locked);
> +       avb_ops_free(ops);
> +       if (rc != AVB_IO_RESULT_OK) {
> +               fastboot_fail("cannot change the device lock state", 
> response);
> +               return -1;
> +       }
> +
> +       return 0;
> +}
> +
> +/**
> + * flashing_wipe_userdata() - erase userdata and metadata
> + *
> + * AVB requires user data to be wiped on every change of the device state,
> + * in either direction, so userdata (and metadata, which holds the file
> + * based encryption keys) are erased before the state changes.
> + *
> + * @response: fastboot response buffer, filled on failure
> + * Return: 0 on success, -1 when userdata could not be erased
> + */
> +static int flashing_wipe_userdata(char *response)
> +{
> +       fastboot_mmc_erase("userdata", response);
> +       if (!strncmp(response, "FAIL", 4))
> +               return -1;
> +
> +       /* metadata is optional on older layouts */
> +       fastboot_mmc_erase("metadata", response);
> +
> +       return 0;
> +}
> +
> +/**
> + * flashing_lock() - lock the device
> + *
> + * Verification errors stop the boot afterwards. The AVB TA resets the
> + * rollback indexes on a state change.
> + */
> +static void flashing_lock(char *cmd_parameter, char *response)
> +{
> +       if (flashing_wipe_userdata(response))
> +               return;
> +
> +       if (flashing_set_lock(true, response))
> +               return;
> +
> +       fastboot_okay(NULL, response);
> +}
> +
> +/**
> + * flashing_unlock() - unlock the device
> + *
> + * The unlock is refused if userdata cannot be erased.
> + */
> +static void flashing_unlock(char *cmd_parameter, char *response)
> +{
> +       if (flashing_wipe_userdata(response))
> +               return;
> +
> +       if (flashing_set_lock(false, response))
> +               return;
> +
> +       fastboot_okay(NULL, response);
> +}
> +
> +/**
> + * 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)
>         }, {
>                 .variable = "is-userspace",
>                 .dispatch = getvar_is_userspace,
> @@ -346,3 +351,32 @@ void fastboot_getvar(char *cmd_parameter, char *response)
>                 fastboot_fail("Variable not implemented", response);
>         }
>  }
> +
> +#if CONFIG_IS_ENABLED(FASTBOOT_CMD_FLASHING)
> +#include <avb_verify.h>
> +
> +/* "yes" when the device is unlocked according to the AVB TA, else "no" */
> +static void getvar_unlocked(char *var_parameter, char *response)
> +{
> +       AvbOps *ops;
> +       bool unlocked = false;
> +       AvbIOResult rc;
> +
> +       ops = avb_ops_alloc(CONFIG_FASTBOOT_FLASH_MMC_DEV);
> +       if (!ops) {
> +               fastboot_fail("cannot allocate AVB ops", response);
> +               return;
> +       }
> +       rc = ops->read_is_device_unlocked(ops, &unlocked);
> +       avb_ops_free(ops);
> +       if (rc != AVB_IO_RESULT_OK) {
> +               fastboot_fail("cannot read the device lock state", response);
> +               return;
> +       }
> +       fastboot_okay(unlocked ? "yes" : "no", response);
> +}
> +#else
> +static void __maybe_unused getvar_unlocked(char *var_parameter, char 
> *response)
> +{
> +}
> +#endif
> diff --git a/include/fastboot.h b/include/fastboot.h
> index b106d617749..e8b6e293173 100644
> --- a/include/fastboot.h
> +++ b/include/fastboot.h
> @@ -53,6 +53,9 @@ enum {
>         FASTBOOT_COMMAND_OEM_BOARD,
>         FASTBOOT_COMMAND_ACMD,
>         FASTBOOT_COMMAND_UCMD,
> +       FASTBOOT_COMMAND_FLASHING_LOCK,
> +       FASTBOOT_COMMAND_FLASHING_UNLOCK,
> +       FASTBOOT_COMMAND_FLASHING_GET_UNLOCK_ABILITY,
>         FASTBOOT_COMMAND_COUNT
>  };
>
>
> --
> 2.53.0
>

Reply via email to