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(+) 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..9bf012645ef 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,11 @@ static void oem_console(char *, char *); static void oem_board(char *, char *); static void run_ucmd(char *, char *); static void run_acmd(char *, char *); +#if CONFIG_IS_ENABLED(FASTBOOT_CMD_FLASHING) +static void flashing_lock(char *, char *); +static void flashing_unlock(char *, char *); +static void flashing_get_unlock_ability(char *, char *); +#endif static const struct { const char *command; @@ -128,6 +136,20 @@ 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 +611,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
