* [PATCH v2 0/5] avb: device lock state, root key provisioning and in-place verification
@ 2026-10-12 20:25 Igor Opaniuk
2026-10-12 20:25 ` [PATCH v2 1/5] cmd: avb: add read_pvalue_hex/write_pvalue_hex Igor Opaniuk
` (6 more replies)
0 siblings, 7 replies; 18+ messages in thread
From: Igor Opaniuk @ 2026-10-12 20:25 UTC (permalink / raw)
To: Mattijs Korpershoek, u-boot
Cc: Tom Rini, Simon Glass, Shahriyar Jalayeri, Quentin Schulz,
Pengpeng Hou, Marek Vasut, Ahmad Fatoum, Igor Opaniuk
This series completes the device state handling of the AVB 2.0
implementation when the state is kept by the OP-TEE AVB TA, and fixes
the way the Android bootmeth hands the boot images to libavb.
Patches 1 and 2 add the shell side: "avb read_pvalue_hex" and
"avb write_pvalue_hex" provision binary persistent values such as the
SHA-256 digest of the vbmeta signing key that CONFIG_AVB_ROOT_KEY_TEE
consumes, and "avb lock"/"avb unlock" set the lock state through a new
avb_write_lock_state() helper. Until now U-Boot could only read the
lock state, so a device backed by the TA could never leave the unlocked
(orange) state.
Patch 3 makes bootmeth_android verify the boot images in the buffers it
boots them from, via AvbOps.get_preloaded_partition(), instead of
letting libavb read, hash and free its own copies and then reading the
partitions a second time for booting. This closes the TOCTOU window
reported on the list and halves the amount of data read from storage.
Patch 4 adds "fastboot flashing lock|unlock|get_unlock_ability" and the
"unlocked" variable, so the state can be changed with the Android
fastboot tool. Both transitions erase userdata and metadata before the
state changes, as AVB requires.
Patch 5 documents the root key sources, the provisioning of the TEE
digest and the lock state in doc/android/avb2.rst.
Tested on QEMU virt with OP-TEE 4.11.0-rc1 and the Cuttlefish Android
17 images: the chain boots locked (green) to sys.boot_completed, a
modified kernel is refused, "fastboot flashing unlock/lock" over
"fastboot tcp" erase the partitions and flip the state reported by
"getvar unlocked" and "avb is_unlocked", and QEMU's SD card block trace
shows every sector of boot and vendor_boot read exactly once.
The lock state is stored in RPMB by the TA; on boards where U-Boot
serves the RPMB requests of OP-TEE itself, the two fixes in "optee: two
RPMB fixes for RPMB requests served by U-Boot" are needed at run time.
There is no build dependency.
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
---
Changes in v2:
- fastboot: guard the flashing_lock/unlock/get_unlock_ability prototypes
with FASTBOOT_CMD_FLASHING. They were declared unconditionally while
their definitions sit under that option, so every configuration with
fastboot but without the flashing commands (sandbox, sei510, ...)
failed with "declared 'static' but never defined" under -Werror. v1
was only build-tested on configurations that enable the option; the
sandbox CI jobs caught it. The series is now also build-tested with
sandbox, sandbox64 (out of tree) and the arm64/arm32 boards that
enable AVB or fastboot flashing, and the 107-column dispatch line is
wrapped.
- Link to v1: https://patch.msgid.link/20261011-upstream-avb_lock_unlock_b4-v1-0-a45aeca54016@oss.qualcomm.com
---
Igor Opaniuk (5):
cmd: avb: add read_pvalue_hex/write_pvalue_hex
cmd: avb: add lock/unlock commands
avb: verify the boot images in the buffers they are booted from
fastboot: add flashing lock/unlock/get_unlock_ability
doc: android/avb2: document the root key provisioning and lock state
boot/bootmeth_android.c | 73 ++++++++++++++++++----
cmd/avb.c | 141 ++++++++++++++++++++++++++++++++++++++++++
common/avb_verify.c | 103 ++++++++++++++++++++++++++++++
doc/android/avb2.rst | 69 +++++++++++++++++++++
drivers/fastboot/Kconfig | 11 ++++
drivers/fastboot/fb_command.c | 119 +++++++++++++++++++++++++++++++++++
drivers/fastboot/fb_getvar.c | 34 ++++++++++
include/avb_verify.h | 39 ++++++++++++
include/fastboot.h | 3 +
9 files changed, 580 insertions(+), 12 deletions(-)
---
base-commit: ed869863c7e2aca50d57a05d843830dd843138c4
change-id: 20261011-upstream-avb_lock_unlock_b4-d3a266c809d4
Best regards,
--
Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v2 1/5] cmd: avb: add read_pvalue_hex/write_pvalue_hex
2026-10-12 20:25 [PATCH v2 0/5] avb: device lock state, root key provisioning and in-place verification Igor Opaniuk
@ 2026-10-12 20:25 ` Igor Opaniuk
2026-10-20 7:38 ` Mattijs Korpershoek
2026-10-20 10:33 ` Guillaume La Roque
2026-10-12 20:25 ` [PATCH v2 2/5] cmd: avb: add lock/unlock commands Igor Opaniuk
` (5 subsequent siblings)
6 siblings, 2 replies; 18+ messages in thread
From: Igor Opaniuk @ 2026-10-12 20:25 UTC (permalink / raw)
To: Mattijs Korpershoek, u-boot
Cc: Tom Rini, Simon Glass, Shahriyar Jalayeri, Quentin Schulz,
Pengpeng Hou, Marek Vasut, Ahmad Fatoum, Igor Opaniuk
write_pvalue stores its argument as a NUL-terminated string, which cannot
carry binary data such as the 32-byte SHA-256 root key digest consumed by
CONFIG_AVB_ROOT_KEY_TEE (persistent value avb.root_pub_digest). Add
hex-string variants to provision and dump raw persistent values from the
U-Boot shell.
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
---
cmd/avb.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 101 insertions(+)
diff --git a/cmd/avb.c b/cmd/avb.c
index 8fbd48ee5a2..6f8df7ecb6d 100644
--- a/cmd/avb.c
+++ b/cmd/avb.c
@@ -6,6 +6,7 @@
#include <avb_verify.h>
#include <command.h>
#include <env.h>
+#include <hexdump.h>
#include <image.h>
#include <malloc.h>
#include <mmc.h>
@@ -427,6 +428,100 @@ int do_avb_write_pvalue(struct cmd_tbl *cmdtp, int flag, int argc,
return CMD_RET_FAILURE;
}
+int do_avb_read_pvalue_hex(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *name;
+ size_t bytes;
+ size_t bytes_read;
+ size_t i;
+ u8 *buffer;
+ char *endp;
+ int ret;
+
+ if (!avb_ops) {
+ printf("AVB is not initialized, please run 'avb init <id>'\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ name = argv[1];
+ bytes = dectoul(argv[2], &endp);
+ if (*endp && *endp != '\n')
+ return CMD_RET_USAGE;
+
+ buffer = malloc(bytes);
+ if (!buffer)
+ return CMD_RET_FAILURE;
+
+ ret = avb_ops->read_persistent_value(avb_ops, name, bytes,
+ buffer, &bytes_read);
+ if (ret == AVB_IO_RESULT_OK) {
+ printf("Read %zu bytes, value = ", bytes_read);
+ for (i = 0; i < bytes_read; i++)
+ printf("%02x", buffer[i]);
+ printf("\n");
+ free(buffer);
+ return CMD_RET_SUCCESS;
+ }
+
+ printf("Failed to read persistent value, err = %d\n", ret);
+
+ free(buffer);
+
+ return CMD_RET_FAILURE;
+}
+
+int do_avb_write_pvalue_hex(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *name;
+ const char *hex;
+ size_t len, bytes;
+ u8 *buffer;
+ int ret;
+
+ if (!avb_ops) {
+ printf("AVB is not initialized, please run 'avb init <id>'\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ name = argv[1];
+ hex = argv[2];
+ len = strlen(hex);
+ if (!len || len % 2) {
+ printf("Value must be an even number of hex digits\n");
+ return CMD_RET_USAGE;
+ }
+ bytes = len / 2;
+
+ buffer = malloc(bytes);
+ if (!buffer)
+ return CMD_RET_FAILURE;
+
+ if (hex2bin(buffer, hex, bytes)) {
+ printf("Value is not a valid hex string\n");
+ free(buffer);
+ return CMD_RET_USAGE;
+ }
+
+ ret = avb_ops->write_persistent_value(avb_ops, name, bytes, buffer);
+ free(buffer);
+ if (ret == AVB_IO_RESULT_OK) {
+ printf("Wrote %zu bytes\n", bytes);
+ return CMD_RET_SUCCESS;
+ }
+
+ printf("Failed to write persistent value `%s`, err = %d\n", name, ret);
+
+ return CMD_RET_FAILURE;
+}
+
static struct cmd_tbl cmd_avb[] = {
U_BOOT_CMD_MKENT(init, 2, 0, do_avb_init, "", ""),
U_BOOT_CMD_MKENT(read_rb, 2, 0, do_avb_read_rb, "", ""),
@@ -440,6 +535,8 @@ static struct cmd_tbl cmd_avb[] = {
#ifdef CONFIG_OPTEE_TA_AVB
U_BOOT_CMD_MKENT(read_pvalue, 3, 0, do_avb_read_pvalue, "", ""),
U_BOOT_CMD_MKENT(write_pvalue, 3, 0, do_avb_write_pvalue, "", ""),
+ U_BOOT_CMD_MKENT(read_pvalue_hex, 3, 0, do_avb_read_pvalue_hex, "", ""),
+ U_BOOT_CMD_MKENT(write_pvalue_hex, 3, 0, do_avb_write_pvalue_hex, "", ""),
#endif
};
@@ -478,6 +575,10 @@ U_BOOT_CMD(
#ifdef CONFIG_OPTEE_TA_AVB
"avb read_pvalue <name> <bytes> - read a persistent value <name>\n"
"avb write_pvalue <name> <value> - write a persistent value <name>\n"
+ "avb read_pvalue_hex <name> <bytes> - read a persistent value <name>\n"
+ " and print it as hex\n"
+ "avb write_pvalue_hex <name> <hex> - write the bytes given as a hex\n"
+ " string to persistent value <name>\n"
#endif
"avb verify [slot_suffix] - run verification process using hash data\n"
" from vbmeta structure\n"
--
2.53.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v2 2/5] cmd: avb: add lock/unlock commands
2026-10-12 20:25 [PATCH v2 0/5] avb: device lock state, root key provisioning and in-place verification Igor Opaniuk
2026-10-12 20:25 ` [PATCH v2 1/5] cmd: avb: add read_pvalue_hex/write_pvalue_hex Igor Opaniuk
@ 2026-10-12 20:25 ` Igor Opaniuk
2026-10-20 7:41 ` Mattijs Korpershoek
2026-10-20 10:33 ` Guillaume La Roque
2026-10-12 20:25 ` [PATCH v2 3/5] avb: verify the boot images in the buffers they are booted from Igor Opaniuk
` (4 subsequent siblings)
6 siblings, 2 replies; 18+ messages in thread
From: Igor Opaniuk @ 2026-10-12 20:25 UTC (permalink / raw)
To: Mattijs Korpershoek, u-boot
Cc: Tom Rini, Simon Glass, Shahriyar Jalayeri, Quentin Schulz,
Pengpeng Hou, Marek Vasut, Ahmad Fatoum, Igor Opaniuk
The OP-TEE AVB TA stores the device lock state
(TA_AVB_CMD_READ/WRITE_LOCK_STATE) but U-Boot only ever read it, so a
device backed by the TA could never leave the unlocked (orange) state from
the bootloader. Add avb lock and avb unlock, which set the state through a
new avb_write_lock_state() helper. As documented by the TA, changing the
state resets all rollback indexes.
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
---
cmd/avb.c | 40 ++++++++++++++++++++++++++++++++++++++++
common/avb_verify.c | 23 +++++++++++++++++++++++
include/avb_verify.h | 3 +++
3 files changed, 66 insertions(+)
diff --git a/cmd/avb.c b/cmd/avb.c
index 6f8df7ecb6d..5e45975d349 100644
--- a/cmd/avb.c
+++ b/cmd/avb.c
@@ -522,6 +522,41 @@ int do_avb_write_pvalue_hex(struct cmd_tbl *cmdtp, int flag, int argc,
return CMD_RET_FAILURE;
}
+#ifdef CONFIG_OPTEE_TA_AVB
+static int avb_set_lock(bool locked)
+{
+ int ret;
+
+ if (!avb_ops) {
+ printf("AVB is not initialized, please run 'avb init <id>'\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = avb_write_lock_state(avb_ops, locked);
+ if (ret != AVB_IO_RESULT_OK) {
+ printf("Failed to %s the device, err = %d\n",
+ locked ? "lock" : "unlock", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ printf("Device %s, rollback indexes reset\n",
+ locked ? "locked" : "unlocked");
+
+ return CMD_RET_SUCCESS;
+}
+
+int do_avb_lock(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ return avb_set_lock(true);
+}
+
+int do_avb_unlock(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return avb_set_lock(false);
+}
+#endif
+
static struct cmd_tbl cmd_avb[] = {
U_BOOT_CMD_MKENT(init, 2, 0, do_avb_init, "", ""),
U_BOOT_CMD_MKENT(read_rb, 2, 0, do_avb_read_rb, "", ""),
@@ -537,6 +572,8 @@ static struct cmd_tbl cmd_avb[] = {
U_BOOT_CMD_MKENT(write_pvalue, 3, 0, do_avb_write_pvalue, "", ""),
U_BOOT_CMD_MKENT(read_pvalue_hex, 3, 0, do_avb_read_pvalue_hex, "", ""),
U_BOOT_CMD_MKENT(write_pvalue_hex, 3, 0, do_avb_write_pvalue_hex, "", ""),
+ U_BOOT_CMD_MKENT(lock, 1, 0, do_avb_lock, "", ""),
+ U_BOOT_CMD_MKENT(unlock, 1, 0, do_avb_unlock, "", ""),
#endif
};
@@ -579,6 +616,9 @@ U_BOOT_CMD(
" and print it as hex\n"
"avb write_pvalue_hex <name> <hex> - write the bytes given as a hex\n"
" string to persistent value <name>\n"
+ "avb lock - lock the device (verification errors stop the boot);\n"
+ " resets all rollback indexes\n"
+ "avb unlock - unlock the device; resets all rollback indexes\n"
#endif
"avb verify [slot_suffix] - run verification process using hash data\n"
" from vbmeta structure\n"
diff --git a/common/avb_verify.c b/common/avb_verify.c
index 09f1534c3cc..9231366d5fa 100644
--- a/common/avb_verify.c
+++ b/common/avb_verify.c
@@ -1056,6 +1056,29 @@ free_name:
}
#endif
+#ifdef CONFIG_OPTEE_TA_AVB
+/**
+ * avb_write_lock_state() - sets the device lock state in the AVB TA
+ *
+ * @ops: contains AvbOps handlers
+ * @locked: true to lock the device, false to unlock it
+ *
+ * Changing the lock state resets all rollback indexes in the TA.
+ *
+ * Return: AVB_IO_RESULT_OK or an AvbIOResult error
+ */
+AvbIOResult avb_write_lock_state(AvbOps *ops, bool locked)
+{
+ struct tee_param param = {
+ .attr = TEE_PARAM_ATTR_TYPE_VALUE_INPUT,
+ .u.value.a = locked,
+ };
+
+ return invoke_func(ops->user_data, TA_AVB_CMD_WRITE_LOCK_STATE, 1,
+ ¶m);
+}
+#endif
+
/**
* ============================================================================
* AVB root key digest providers (selected via CONFIG_AVB_ROOT_KEY_*)
diff --git a/include/avb_verify.h b/include/avb_verify.h
index 66655aab871..d2136b2ce02 100644
--- a/include/avb_verify.h
+++ b/include/avb_verify.h
@@ -45,6 +45,9 @@ enum mmc_io_type {
};
AvbOps *avb_ops_alloc(int boot_device);
+#ifdef CONFIG_OPTEE_TA_AVB
+AvbIOResult avb_write_lock_state(AvbOps *ops, bool locked);
+#endif
void avb_ops_free(AvbOps *ops);
/**
--
2.53.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v2 3/5] avb: verify the boot images in the buffers they are booted from
2026-10-12 20:25 [PATCH v2 0/5] avb: device lock state, root key provisioning and in-place verification Igor Opaniuk
2026-10-12 20:25 ` [PATCH v2 1/5] cmd: avb: add read_pvalue_hex/write_pvalue_hex Igor Opaniuk
2026-10-12 20:25 ` [PATCH v2 2/5] cmd: avb: add lock/unlock commands Igor Opaniuk
@ 2026-10-12 20:25 ` Igor Opaniuk
2026-10-20 7:53 ` Mattijs Korpershoek
2026-10-20 10:33 ` Guillaume La Roque
2026-10-12 20:25 ` [PATCH v2 4/5] fastboot: add flashing lock/unlock/get_unlock_ability Igor Opaniuk
` (3 subsequent siblings)
6 siblings, 2 replies; 18+ messages in thread
From: Igor Opaniuk @ 2026-10-12 20:25 UTC (permalink / raw)
To: Mattijs Korpershoek, u-boot
Cc: Tom Rini, Simon Glass, Shahriyar Jalayeri, Quentin Schulz,
Pengpeng Hou, Marek Vasut, Ahmad Fatoum, Igor Opaniuk
bootmeth_android let libavb read boot and vendor_boot into its own buffers,
hash them and free them, and then read both partitions again to boot them.
The bytes that were verified and the bytes that were booted were therefore
two separate reads of the storage device, which an attacker able to
interpose the device can tell apart (reported on the list as a TOCTOU
issue). It also read every image twice.
Load boot and vendor_boot first and hand the buffers to libavb through
AvbOps.get_preloaded_partition(), so the hashes are computed over the
memory that bootm consumes afterwards. If a hash descriptor covers more
bytes than the boot image header announced, the tail is read into the same
buffer, bounded by the partition size; a descriptor larger than the buffer
fails verification.
Reported-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
---
boot/bootmeth_android.c | 73 ++++++++++++++++++++++++++++++++++++--------
common/avb_verify.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++
include/avb_verify.h | 36 ++++++++++++++++++++++
3 files changed, 177 insertions(+), 12 deletions(-)
diff --git a/boot/bootmeth_android.c b/boot/bootmeth_android.c
index 0db08d4f861..862d0d67a2f 100644
--- a/boot/bootmeth_android.c
+++ b/boot/bootmeth_android.c
@@ -48,6 +48,11 @@ struct android_priv {
u32 header_version;
u32 boot_img_size;
u32 vendor_boot_img_size;
+ /* Where boot and vendor_boot were loaded, and the partition sizes */
+ ulong boot_addr;
+ ulong boot_part_size;
+ ulong vendor_boot_addr;
+ ulong vendor_boot_part_size;
};
static int android_check(struct udevice *dev, struct bootflow_iter *iter)
@@ -353,7 +358,8 @@ static int android_read_file(struct udevice *dev, struct bootflow *bflow,
* Return: 0 if OK, negative errno on failure.
*/
static int read_slotted_partition(struct blk_desc *desc, const char *const name,
- const char slot[2], ulong image_size, ulong addr)
+ const char slot[2], ulong image_size, ulong addr,
+ ulong *part_size)
{
struct disk_partition partition;
char partname[PART_NAME_LEN];
@@ -396,6 +402,9 @@ static int read_slotted_partition(struct blk_desc *desc, const char *const name,
if (n < num_blks)
return log_msg_ret("part read", -EIO);
+ if (part_size)
+ *part_size = (ulong)partition.size * desc->blksz;
+
return 0;
}
@@ -440,6 +449,7 @@ static int run_avb_verification(struct bootflow *bflow)
enum avb_boot_state boot_state;
char *extra_args;
char slot_suffix[3] = "";
+ char partname[PART_NAME_LEN];
bool unlocked = false;
int ret;
@@ -450,6 +460,35 @@ static int run_avb_verification(struct bootflow *bflow)
if (priv->slot)
sprintf(slot_suffix, "_%s", priv->slot);
+ /*
+ * Hand libavb the buffers boot and vendor_boot were loaded into, so the
+ * bytes it hashes are the bytes that get booted and the partitions are
+ * read only once.
+ */
+ if (priv->boot_addr) {
+ snprintf(partname, sizeof(partname), "boot%s", slot_suffix);
+ ret = avb_ops_add_preloaded(avb_ops, partname,
+ map_sysmem(priv->boot_addr, 0),
+ priv->boot_img_size,
+ priv->boot_part_size);
+ if (ret) {
+ ret = log_msg_ret("avb preload boot", ret);
+ goto out;
+ }
+ }
+ if (priv->vendor_boot_addr) {
+ snprintf(partname, sizeof(partname), "vendor_boot%s",
+ slot_suffix);
+ ret = avb_ops_add_preloaded(avb_ops, partname,
+ map_sysmem(priv->vendor_boot_addr, 0),
+ priv->vendor_boot_img_size,
+ priv->vendor_boot_part_size);
+ if (ret) {
+ ret = log_msg_ret("avb preload vendor_boot", ret);
+ goto out;
+ }
+ }
+
ret = avb_ops->read_is_device_unlocked(avb_ops, &unlocked);
if (ret != AVB_IO_RESULT_OK) {
ret = log_msg_ret("avb lock", -EIO);
@@ -566,6 +605,26 @@ static int boot_android_normal(struct bootflow *bflow)
ulong loadaddr = env_get_hex("loadaddr", 0);
ulong vloadaddr = env_get_hex("vendor_boot_comp_addr_r", 0);
+ /*
+ * Load the images first and verify them in place: libavb hashes these
+ * buffers (see run_avb_verification()), so a storage device that
+ * returns different data on a second read cannot slip past the check.
+ */
+ ret = read_slotted_partition(desc, "boot", priv->slot, priv->boot_img_size,
+ loadaddr, &priv->boot_part_size);
+ if (ret < 0)
+ return log_msg_ret("read boot", ret);
+ priv->boot_addr = loadaddr;
+
+ if (priv->header_version >= 3) {
+ ret = read_slotted_partition(desc, "vendor_boot", priv->slot,
+ priv->vendor_boot_img_size, vloadaddr,
+ &priv->vendor_boot_part_size);
+ if (ret < 0)
+ return log_msg_ret("read vendor_boot", ret);
+ priv->vendor_boot_addr = vloadaddr;
+ }
+
ret = run_avb_verification(bflow);
if (ret < 0)
return log_msg_ret("avb", ret);
@@ -575,18 +634,8 @@ static int boot_android_normal(struct bootflow *bflow)
if (ret < 0)
return log_msg_ret("read slot", ret);
- ret = read_slotted_partition(desc, "boot", priv->slot, priv->boot_img_size,
- loadaddr);
- if (ret < 0)
- return log_msg_ret("read boot", ret);
-
- if (priv->header_version >= 3) {
- ret = read_slotted_partition(desc, "vendor_boot", priv->slot,
- priv->vendor_boot_img_size, vloadaddr);
- if (ret < 0)
- return log_msg_ret("read vendor_boot", ret);
+ if (priv->header_version >= 3)
set_avendor_bootimg_addr(vloadaddr);
- }
set_abootimg_addr(loadaddr);
if (priv->slot)
diff --git a/common/avb_verify.c b/common/avb_verify.c
index 9231366d5fa..e7685c29d65 100644
--- a/common/avb_verify.c
+++ b/common/avb_verify.c
@@ -1079,6 +1079,85 @@ AvbIOResult avb_write_lock_state(AvbOps *ops, bool locked)
}
#endif
+/**
+ * get_preloaded_partition() - hand libavb a partition the caller loaded
+ *
+ * Without this callback libavb reads every requested partition into its own
+ * buffer, hashes that copy and frees it, and the bootloader then reads the
+ * partition a second time to boot it: the verified bytes and the booted bytes
+ * are different reads of the storage device. Serving the caller's buffer makes
+ * libavb hash the memory that is booted afterwards.
+ *
+ * The boot image header may describe fewer bytes than the hash descriptor
+ * covers; the missing tail is then read into the same buffer, up to its
+ * capacity, before it is handed out.
+ */
+static AvbIOResult get_preloaded_partition(AvbOps *ops, const char *partition,
+ size_t num_bytes,
+ uint8_t **out_pointer,
+ size_t *out_num_bytes_preloaded)
+{
+ struct AvbOpsData *ops_data = ops->user_data;
+ struct avb_preloaded *p = NULL;
+ size_t num_read = 0;
+ AvbIOResult rc;
+ int i;
+
+ *out_pointer = NULL;
+ *out_num_bytes_preloaded = 0;
+
+ for (i = 0; i < ops_data->num_preloaded; i++) {
+ if (!strcmp(ops_data->preloaded[i].name, partition)) {
+ p = &ops_data->preloaded[i];
+ break;
+ }
+ }
+ if (!p)
+ return AVB_IO_RESULT_OK; /* not preloaded, libavb reads it */
+
+ if (num_bytes > p->max_size) {
+ printf("%s: %s: %zu bytes to verify, buffer holds %zu\n",
+ __func__, partition, num_bytes, p->max_size);
+ return AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE;
+ }
+
+ if (num_bytes > p->size) {
+ rc = read_from_partition(ops, partition, p->size,
+ num_bytes - p->size,
+ (u8 *)p->addr + p->size, &num_read);
+ if (rc != AVB_IO_RESULT_OK)
+ return rc;
+ if (num_read != num_bytes - p->size)
+ return AVB_IO_RESULT_ERROR_IO;
+ p->size = num_bytes;
+ }
+
+ *out_pointer = p->addr;
+ *out_num_bytes_preloaded = num_bytes;
+
+ return AVB_IO_RESULT_OK;
+}
+
+int avb_ops_add_preloaded(AvbOps *ops, const char *partition, void *addr,
+ size_t size, size_t max_size)
+{
+ struct AvbOpsData *ops_data = ops->user_data;
+ struct avb_preloaded *p;
+
+ if (ops_data->num_preloaded >= AVB_MAX_PRELOADED)
+ return -ENOSPC;
+ if (strlen(partition) >= sizeof(p->name) || size > max_size)
+ return -EINVAL;
+
+ p = &ops_data->preloaded[ops_data->num_preloaded++];
+ strcpy(p->name, partition);
+ p->addr = addr;
+ p->size = size;
+ p->max_size = max_size;
+
+ return 0;
+}
+
/**
* ============================================================================
* AVB root key digest providers (selected via CONFIG_AVB_ROOT_KEY_*)
@@ -1146,6 +1225,7 @@ AvbOps *avb_ops_alloc(int boot_device)
ops_data->ops.user_data = ops_data;
ops_data->ops.read_from_partition = read_from_partition;
+ ops_data->ops.get_preloaded_partition = get_preloaded_partition;
ops_data->ops.write_to_partition = write_to_partition;
ops_data->ops.validate_vbmeta_public_key = validate_vbmeta_public_key;
ops_data->ops.read_rollback_index = read_rollback_index;
diff --git a/include/avb_verify.h b/include/avb_verify.h
index d2136b2ce02..cac8b816c03 100644
--- a/include/avb_verify.h
+++ b/include/avb_verify.h
@@ -22,6 +22,26 @@ enum avb_boot_state {
AVB_RED,
};
+#define AVB_MAX_PRELOADED 4
+
+/**
+ * struct avb_preloaded - a partition already loaded into memory by the caller
+ *
+ * libavb hashes the preloaded bytes instead of reading the partition again,
+ * so what gets verified is exactly what the bootloader goes on to boot.
+ *
+ * @name: partition name including the slot suffix, e.g. "boot_a"
+ * @addr: start of the buffer holding the partition data
+ * @size: number of bytes loaded so far
+ * @max_size: capacity of the buffer (at most the partition size)
+ */
+struct avb_preloaded {
+ char name[32];
+ void *addr;
+ size_t size;
+ size_t max_size;
+};
+
struct AvbOpsData {
struct AvbOps ops;
int mmc_dev;
@@ -30,6 +50,8 @@ struct AvbOpsData {
struct udevice *tee;
u32 session;
#endif
+ struct avb_preloaded preloaded[AVB_MAX_PRELOADED];
+ int num_preloaded;
};
struct mmc_part {
@@ -45,6 +67,20 @@ enum mmc_io_type {
};
AvbOps *avb_ops_alloc(int boot_device);
+
+/**
+ * avb_ops_add_preloaded() - register a partition that is already in memory
+ *
+ * @ops: AvbOps from avb_ops_alloc()
+ * @partition: partition name including the slot suffix, e.g. "boot_a"
+ * @addr: buffer holding the first @size bytes of the partition
+ * @size: bytes loaded in @addr
+ * @max_size: capacity of @addr; a hash descriptor covering more than this
+ * fails verification instead of overflowing the buffer
+ * Return: 0 on success, -ENOSPC when no slot is left, -EINVAL on a bad name
+ */
+int avb_ops_add_preloaded(AvbOps *ops, const char *partition, void *addr,
+ size_t size, size_t max_size);
#ifdef CONFIG_OPTEE_TA_AVB
AvbIOResult avb_write_lock_state(AvbOps *ops, bool locked);
#endif
--
2.53.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v2 4/5] fastboot: add flashing lock/unlock/get_unlock_ability
2026-10-12 20:25 [PATCH v2 0/5] avb: device lock state, root key provisioning and in-place verification Igor Opaniuk
` (2 preceding siblings ...)
2026-10-12 20:25 ` [PATCH v2 3/5] avb: verify the boot images in the buffers they are booted from Igor Opaniuk
@ 2026-10-12 20:25 ` Igor Opaniuk
2026-10-20 8:23 ` Mattijs Korpershoek
2026-10-20 10:33 ` Guillaume La Roque
2026-10-12 20:25 ` [PATCH v2 5/5] doc: android/avb2: document the root key provisioning and lock state Igor Opaniuk
` (2 subsequent siblings)
6 siblings, 2 replies; 18+ messages in thread
From: Igor Opaniuk @ 2026-10-12 20:25 UTC (permalink / raw)
To: Mattijs Korpershoek, u-boot
Cc: Tom Rini, Simon Glass, Shahriyar Jalayeri, Quentin Schulz,
Pengpeng Hou, Marek Vasut, Ahmad Fatoum, Igor Opaniuk
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 <igor.opaniuk@oss.qualcomm.com>
---
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
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v2 5/5] doc: android/avb2: document the root key provisioning and lock state
2026-10-12 20:25 [PATCH v2 0/5] avb: device lock state, root key provisioning and in-place verification Igor Opaniuk
` (3 preceding siblings ...)
2026-10-12 20:25 ` [PATCH v2 4/5] fastboot: add flashing lock/unlock/get_unlock_ability Igor Opaniuk
@ 2026-10-12 20:25 ` Igor Opaniuk
2026-10-20 8:27 ` Mattijs Korpershoek
2026-10-20 10:33 ` Guillaume La Roque
2026-10-19 6:51 ` [PATCH v2 0/5] avb: device lock state, root key provisioning and in-place verification Igor Opaniuk
2026-10-20 7:21 ` Mattijs Korpershoek
6 siblings, 2 replies; 18+ messages in thread
From: Igor Opaniuk @ 2026-10-12 20:25 UTC (permalink / raw)
To: Mattijs Korpershoek, u-boot
Cc: Tom Rini, Simon Glass, Shahriyar Jalayeri, Quentin Schulz,
Pengpeng Hou, Marek Vasut, Ahmad Fatoum, Igor Opaniuk
Describe the three root key sources (AVB_ROOT_KEY_BUILTIN, _TEE, _BOARD),
how to derive the SHA-256 digest of the vbmeta signing key with avbtool
and provision it into the TEE persistent value with "avb write_pvalue_hex",
the fail-closed behaviour when it is missing, and the device lock state as
seen from the U-Boot shell and from fastboot (flashing lock/unlock with the
mandatory userdata erase).
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
---
doc/android/avb2.rst | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 69 insertions(+)
diff --git a/doc/android/avb2.rst b/doc/android/avb2.rst
index a5147012413..b43b58a07a6 100644
--- a/doc/android/avb2.rst
+++ b/doc/android/avb2.rst
@@ -74,6 +74,62 @@ Note that device lock state and rollback protection are only enforced when
backed by OP-TEE (see `AVB using OP-TEE (optional)`_); without it,
verification is advisory regardless of the root key source.
+Provisioning the root key digest in the TEE
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+With ``AVB_ROOT_KEY_TEE`` the trusted digest is the SHA-256 of the public key
+in the AVB key format (the 1032-byte blob for a 4096-bit RSA key, the same
+encoding that vbmeta embeds), not of the PEM file. Compute it on the host from
+the private key used to sign vbmeta::
+
+ $ avbtool extract_public_key --key key.pem --output key.avbpubkey
+ $ sha256sum key.avbpubkey
+ 7728e30f50bfa5cea165f473175a08803f6a8346642b5aa10913e9d9e6defef6 key.avbpubkey
+
+and store it from the U-Boot shell as the named persistent value, which the
+AVB TA keeps in RPMB-backed secure storage::
+
+ => avb init 0
+ => avb write_pvalue_hex avb.root_pub_digest 7728e30f...defef6
+ => avb read_pvalue_hex avb.root_pub_digest 32
+
+Until the value exists, ``avb verify`` and the Android bootmeth fail with
+"Public keys are not accepted"; a wrong digest is rejected the same way; the
+value persists across reboots. Provisioning is a one-time factory step and
+should happen before the device is locked; see the note above on protecting
+the value from normal-world writes.
+
+Device lock state
+-----------------
+
+The AVB TA stores the device lock state next to the rollback indexes. A
+locked device refuses to boot on any verification error and reports
+``androidboot.verifiedbootstate=green``; an unlocked device boots with
+verification errors tolerated (``orange``) and rollback protection off.
+Changing the state resets all rollback indexes in the TA.
+
+From the U-Boot shell::
+
+ => avb init 0
+ => avb lock
+ => avb unlock
+ => avb is_unlocked
+
+Over fastboot, with ``CONFIG_FASTBOOT_CMD_FLASHING``, using the standard
+Android commands::
+
+ $ fastboot flashing get_unlock_ability # OKAY 1
+ $ fastboot flashing unlock # erases userdata and metadata first
+ $ fastboot flashing lock # likewise
+ $ fastboot getvar unlocked # yes / no
+
+As AVB requires, both transitions erase userdata (and metadata) before the
+state changes and are refused if userdata cannot be erased. On a slow
+(emulated) eMMC the erase can take minutes; a TCP client may time out
+meanwhile, the device still completes the operation.
+``get_unlock_ability`` always reports 1: the "OEM unlocking" setting Android
+keeps in the frp partition is not evaluated.
+
AVB 2.0 U-Boot shell commands
-----------------------------
@@ -93,6 +149,13 @@ different testing purposes::
<partname> by <offset> using data from <addr>
avb read_pvalue <name> <bytes> - read a persistent value <name>
avb write_pvalue <name> <value> - write a persistent value <name>
+ avb read_pvalue_hex <name> <bytes> - read a persistent value <name>
+ and print it as hex
+ avb write_pvalue_hex <name> <hex> - write the bytes given as a hex
+ string to persistent value <name>
+ avb lock - lock the device (verification errors stop the boot);
+ resets all rollback indexes
+ avb unlock - unlock the device; resets all rollback indexes
avb verify [slot_suffix] - run verification process using hash data
from vbmeta structure
[slot_suffix] - _a, _b, etc (if vbmeta partition is slotted)
@@ -134,6 +197,12 @@ OP-TEE::
CONFIG_OPTEE_TA_AVB=y
CONFIG_SUPPORT_EMMC_RPMB=y
+and, to take the root key digest from the TEE and to lock/unlock over
+fastboot::
+
+ CONFIG_AVB_ROOT_KEY_TEE=y
+ CONFIG_FASTBOOT_CMD_FLASHING=y
+
Then add ``avb verify`` invocation to your android boot sequence of commands,
e.g.::
--
2.53.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH v2 0/5] avb: device lock state, root key provisioning and in-place verification
2026-10-12 20:25 [PATCH v2 0/5] avb: device lock state, root key provisioning and in-place verification Igor Opaniuk
` (4 preceding siblings ...)
2026-10-12 20:25 ` [PATCH v2 5/5] doc: android/avb2: document the root key provisioning and lock state Igor Opaniuk
@ 2026-10-19 6:51 ` Igor Opaniuk
2026-10-20 7:21 ` Mattijs Korpershoek
6 siblings, 0 replies; 18+ messages in thread
From: Igor Opaniuk @ 2026-10-19 6:51 UTC (permalink / raw)
To: Mattijs Korpershoek, u-boot
Cc: Tom Rini, Simon Glass, Shahriyar Jalayeri, Quentin Schulz,
Pengpeng Hou, Marek Vasut, Ahmad Fatoum
Hi,
On Mon, Oct 12, 2026 at 10:25 PM Igor Opaniuk
<igor.opaniuk@oss.qualcomm.com> wrote:
>
> This series completes the device state handling of the AVB 2.0
> implementation when the state is kept by the OP-TEE AVB TA, and fixes
> the way the Android bootmeth hands the boot images to libavb.
>
> Patches 1 and 2 add the shell side: "avb read_pvalue_hex" and
> "avb write_pvalue_hex" provision binary persistent values such as the
> SHA-256 digest of the vbmeta signing key that CONFIG_AVB_ROOT_KEY_TEE
> consumes, and "avb lock"/"avb unlock" set the lock state through a new
> avb_write_lock_state() helper. Until now U-Boot could only read the
> lock state, so a device backed by the TA could never leave the unlocked
> (orange) state.
>
> Patch 3 makes bootmeth_android verify the boot images in the buffers it
> boots them from, via AvbOps.get_preloaded_partition(), instead of
> letting libavb read, hash and free its own copies and then reading the
> partitions a second time for booting. This closes the TOCTOU window
> reported on the list and halves the amount of data read from storage.
>
> Patch 4 adds "fastboot flashing lock|unlock|get_unlock_ability" and the
> "unlocked" variable, so the state can be changed with the Android
> fastboot tool. Both transitions erase userdata and metadata before the
> state changes, as AVB requires.
>
> Patch 5 documents the root key sources, the provisioning of the TEE
> digest and the lock state in doc/android/avb2.rst.
>
> Tested on QEMU virt with OP-TEE 4.11.0-rc1 and the Cuttlefish Android
> 17 images: the chain boots locked (green) to sys.boot_completed, a
> modified kernel is refused, "fastboot flashing unlock/lock" over
> "fastboot tcp" erase the partitions and flip the state reported by
> "getvar unlocked" and "avb is_unlocked", and QEMU's SD card block trace
> shows every sector of boot and vendor_boot read exactly once.
>
> The lock state is stored in RPMB by the TA; on boards where U-Boot
> serves the RPMB requests of OP-TEE itself, the two fixes in "optee: two
> RPMB fixes for RPMB requests served by U-Boot" are needed at run time.
> There is no build dependency.
>
> Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
> ---
> Changes in v2:
> - fastboot: guard the flashing_lock/unlock/get_unlock_ability prototypes
> with FASTBOOT_CMD_FLASHING. They were declared unconditionally while
> their definitions sit under that option, so every configuration with
> fastboot but without the flashing commands (sandbox, sei510, ...)
> failed with "declared 'static' but never defined" under -Werror. v1
> was only build-tested on configurations that enable the option; the
> sandbox CI jobs caught it. The series is now also build-tested with
> sandbox, sandbox64 (out of tree) and the arm64/arm32 boards that
> enable AVB or fastboot flashing, and the 107-column dispatch line is
> wrapped.
> - Link to v1: https://patch.msgid.link/20261011-upstream-avb_lock_unlock_b4-v1-0-a45aeca54016@oss.qualcomm.com
>
> ---
> Igor Opaniuk (5):
> cmd: avb: add read_pvalue_hex/write_pvalue_hex
> cmd: avb: add lock/unlock commands
> avb: verify the boot images in the buffers they are booted from
> fastboot: add flashing lock/unlock/get_unlock_ability
> doc: android/avb2: document the root key provisioning and lock state
>
> boot/bootmeth_android.c | 73 ++++++++++++++++++----
> cmd/avb.c | 141 ++++++++++++++++++++++++++++++++++++++++++
> common/avb_verify.c | 103 ++++++++++++++++++++++++++++++
> doc/android/avb2.rst | 69 +++++++++++++++++++++
> drivers/fastboot/Kconfig | 11 ++++
> drivers/fastboot/fb_command.c | 119 +++++++++++++++++++++++++++++++++++
> drivers/fastboot/fb_getvar.c | 34 ++++++++++
> include/avb_verify.h | 39 ++++++++++++
> include/fastboot.h | 3 +
> 9 files changed, 580 insertions(+), 12 deletions(-)
> ---
> base-commit: ed869863c7e2aca50d57a05d843830dd843138c4
> change-id: 20261011-upstream-avb_lock_unlock_b4-d3a266c809d4
>
> Best regards,
> --
> Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
>
Gentle ping on this patch series. Please let me know if anyone has any
feedback or objections.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 0/5] avb: device lock state, root key provisioning and in-place verification
2026-10-12 20:25 [PATCH v2 0/5] avb: device lock state, root key provisioning and in-place verification Igor Opaniuk
` (5 preceding siblings ...)
2026-10-19 6:51 ` [PATCH v2 0/5] avb: device lock state, root key provisioning and in-place verification Igor Opaniuk
@ 2026-10-20 7:21 ` Mattijs Korpershoek
6 siblings, 0 replies; 18+ messages in thread
From: Mattijs Korpershoek @ 2026-10-20 7:21 UTC (permalink / raw)
To: Igor Opaniuk, u-boot
Cc: Tom Rini, Simon Glass, Shahriyar Jalayeri, Quentin Schulz,
Pengpeng Hou, Marek Vasut, Ahmad Fatoum, Igor Opaniuk
Hi Igor,
Thank you for the series
On Mon, Oct 12, 2026 at 22:25, Igor Opaniuk <igor.opaniuk@oss.qualcomm.com> wrote:
> This series completes the device state handling of the AVB 2.0
> implementation when the state is kept by the OP-TEE AVB TA, and fixes
> the way the Android bootmeth hands the boot images to libavb.
>
> Patches 1 and 2 add the shell side: "avb read_pvalue_hex" and
> "avb write_pvalue_hex" provision binary persistent values such as the
> SHA-256 digest of the vbmeta signing key that CONFIG_AVB_ROOT_KEY_TEE
> consumes, and "avb lock"/"avb unlock" set the lock state through a new
> avb_write_lock_state() helper. Until now U-Boot could only read the
> lock state, so a device backed by the TA could never leave the unlocked
> (orange) state.
>
> Patch 3 makes bootmeth_android verify the boot images in the buffers it
> boots them from, via AvbOps.get_preloaded_partition(), instead of
> letting libavb read, hash and free its own copies and then reading the
> partitions a second time for booting. This closes the TOCTOU window
> reported on the list and halves the amount of data read from storage.
>
> Patch 4 adds "fastboot flashing lock|unlock|get_unlock_ability" and the
> "unlocked" variable, so the state can be changed with the Android
> fastboot tool. Both transitions erase userdata and metadata before the
> state changes, as AVB requires.
>
> Patch 5 documents the root key sources, the provisioning of the TEE
> digest and the lock state in doc/android/avb2.rst.
>
> Tested on QEMU virt with OP-TEE 4.11.0-rc1 and the Cuttlefish Android
> 17 images: the chain boots locked (green) to sys.boot_completed, a
> modified kernel is refused, "fastboot flashing unlock/lock" over
> "fastboot tcp" erase the partitions and flip the state reported by
> "getvar unlocked" and "avb is_unlocked", and QEMU's SD card block trace
> shows every sector of boot and vendor_boot read exactly once.
>
> The lock state is stored in RPMB by the TA; on boards where U-Boot
> serves the RPMB requests of OP-TEE itself, the two fixes in "optee: two
> RPMB fixes for RPMB requests served by U-Boot" are needed at run time.
> There is no build dependency.
>
> Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
Boot tested this on khadas-vim3_android_ab_defconfig using the following
AOSP images:
http://public.amlogic.binaries.baylibre.com/ci/build/baylibre/2026-09-17/2026-09-17_yukawa-bp2a-userdebug_vim3_yukawa-android-16_mainline_b25.tar.gz
Tested-by: Mattijs Korpershoek <mkorpershoek@kernel.org>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 1/5] cmd: avb: add read_pvalue_hex/write_pvalue_hex
2026-10-12 20:25 ` [PATCH v2 1/5] cmd: avb: add read_pvalue_hex/write_pvalue_hex Igor Opaniuk
@ 2026-10-20 7:38 ` Mattijs Korpershoek
2026-10-20 10:33 ` Guillaume La Roque
1 sibling, 0 replies; 18+ messages in thread
From: Mattijs Korpershoek @ 2026-10-20 7:38 UTC (permalink / raw)
To: Igor Opaniuk, u-boot
Cc: Tom Rini, Simon Glass, Shahriyar Jalayeri, Quentin Schulz,
Pengpeng Hou, Marek Vasut, Ahmad Fatoum, Igor Opaniuk
Hi Igor,
Thank you for the patch.
On Mon, Oct 12, 2026 at 22:25, Igor Opaniuk <igor.opaniuk@oss.qualcomm.com> wrote:
> write_pvalue stores its argument as a NUL-terminated string, which cannot
> carry binary data such as the 32-byte SHA-256 root key digest consumed by
> CONFIG_AVB_ROOT_KEY_TEE (persistent value avb.root_pub_digest). Add
> hex-string variants to provision and dump raw persistent values from the
> U-Boot shell.
>
> Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
Reviewed-by: Mattijs Korpershoek <mkorpershoek@kernel.org>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 2/5] cmd: avb: add lock/unlock commands
2026-10-12 20:25 ` [PATCH v2 2/5] cmd: avb: add lock/unlock commands Igor Opaniuk
@ 2026-10-20 7:41 ` Mattijs Korpershoek
2026-10-20 10:33 ` Guillaume La Roque
1 sibling, 0 replies; 18+ messages in thread
From: Mattijs Korpershoek @ 2026-10-20 7:41 UTC (permalink / raw)
To: Igor Opaniuk, u-boot
Cc: Tom Rini, Simon Glass, Shahriyar Jalayeri, Quentin Schulz,
Pengpeng Hou, Marek Vasut, Ahmad Fatoum, Igor Opaniuk
Hi Igor,
Thank you for the patch.
On Mon, Oct 12, 2026 at 22:25, Igor Opaniuk <igor.opaniuk@oss.qualcomm.com> wrote:
> The OP-TEE AVB TA stores the device lock state
> (TA_AVB_CMD_READ/WRITE_LOCK_STATE) but U-Boot only ever read it, so a
> device backed by the TA could never leave the unlocked (orange) state from
> the bootloader. Add avb lock and avb unlock, which set the state through a
> new avb_write_lock_state() helper. As documented by the TA, changing the
> state resets all rollback indexes.
>
> Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
Reviewed-by: Mattijs Korpershoek <mkorpershoek@kernel.org>
> ---
> cmd/avb.c | 40 ++++++++++++++++++++++++++++++++++++++++
> common/avb_verify.c | 23 +++++++++++++++++++++++
> include/avb_verify.h | 3 +++
> 3 files changed, 66 insertions(+)
>
> diff --git a/cmd/avb.c b/cmd/avb.c
> index 6f8df7ecb6d..5e45975d349 100644
> --- a/cmd/avb.c
> +++ b/cmd/avb.c
> @@ -522,6 +522,41 @@ int do_avb_write_pvalue_hex(struct cmd_tbl *cmdtp, int flag, int argc,
> return CMD_RET_FAILURE;
> }
>
> +#ifdef CONFIG_OPTEE_TA_AVB
> +static int avb_set_lock(bool locked)
> +{
> + int ret;
> +
> + if (!avb_ops) {
> + printf("AVB is not initialized, please run 'avb init <id>'\n");
> + return CMD_RET_FAILURE;
> + }
> +
> + ret = avb_write_lock_state(avb_ops, locked);
> + if (ret != AVB_IO_RESULT_OK) {
> + printf("Failed to %s the device, err = %d\n",
> + locked ? "lock" : "unlock", ret);
> + return CMD_RET_FAILURE;
> + }
> +
> + printf("Device %s, rollback indexes reset\n",
> + locked ? "locked" : "unlocked");
> +
> + return CMD_RET_SUCCESS;
> +}
> +
> +int do_avb_lock(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
> +{
> + return avb_set_lock(true);
> +}
> +
> +int do_avb_unlock(struct cmd_tbl *cmdtp, int flag, int argc,
> + char *const argv[])
> +{
> + return avb_set_lock(false);
> +}
> +#endif
> +
> static struct cmd_tbl cmd_avb[] = {
> U_BOOT_CMD_MKENT(init, 2, 0, do_avb_init, "", ""),
> U_BOOT_CMD_MKENT(read_rb, 2, 0, do_avb_read_rb, "", ""),
> @@ -537,6 +572,8 @@ static struct cmd_tbl cmd_avb[] = {
> U_BOOT_CMD_MKENT(write_pvalue, 3, 0, do_avb_write_pvalue, "", ""),
> U_BOOT_CMD_MKENT(read_pvalue_hex, 3, 0, do_avb_read_pvalue_hex, "", ""),
> U_BOOT_CMD_MKENT(write_pvalue_hex, 3, 0, do_avb_write_pvalue_hex, "", ""),
> + U_BOOT_CMD_MKENT(lock, 1, 0, do_avb_lock, "", ""),
> + U_BOOT_CMD_MKENT(unlock, 1, 0, do_avb_unlock, "", ""),
> #endif
> };
>
> @@ -579,6 +616,9 @@ U_BOOT_CMD(
> " and print it as hex\n"
> "avb write_pvalue_hex <name> <hex> - write the bytes given as a hex\n"
> " string to persistent value <name>\n"
> + "avb lock - lock the device (verification errors stop the boot);\n"
> + " resets all rollback indexes\n"
> + "avb unlock - unlock the device; resets all rollback indexes\n"
> #endif
> "avb verify [slot_suffix] - run verification process using hash data\n"
> " from vbmeta structure\n"
> diff --git a/common/avb_verify.c b/common/avb_verify.c
> index 09f1534c3cc..9231366d5fa 100644
> --- a/common/avb_verify.c
> +++ b/common/avb_verify.c
> @@ -1056,6 +1056,29 @@ free_name:
> }
> #endif
>
> +#ifdef CONFIG_OPTEE_TA_AVB
> +/**
> + * avb_write_lock_state() - sets the device lock state in the AVB TA
> + *
> + * @ops: contains AvbOps handlers
> + * @locked: true to lock the device, false to unlock it
> + *
> + * Changing the lock state resets all rollback indexes in the TA.
> + *
> + * Return: AVB_IO_RESULT_OK or an AvbIOResult error
> + */
> +AvbIOResult avb_write_lock_state(AvbOps *ops, bool locked)
> +{
> + struct tee_param param = {
> + .attr = TEE_PARAM_ATTR_TYPE_VALUE_INPUT,
> + .u.value.a = locked,
> + };
> +
> + return invoke_func(ops->user_data, TA_AVB_CMD_WRITE_LOCK_STATE, 1,
> + ¶m);
> +}
> +#endif
> +
> /**
> * ============================================================================
> * AVB root key digest providers (selected via CONFIG_AVB_ROOT_KEY_*)
> diff --git a/include/avb_verify.h b/include/avb_verify.h
> index 66655aab871..d2136b2ce02 100644
> --- a/include/avb_verify.h
> +++ b/include/avb_verify.h
> @@ -45,6 +45,9 @@ enum mmc_io_type {
> };
>
> AvbOps *avb_ops_alloc(int boot_device);
> +#ifdef CONFIG_OPTEE_TA_AVB
> +AvbIOResult avb_write_lock_state(AvbOps *ops, bool locked);
> +#endif
> void avb_ops_free(AvbOps *ops);
>
> /**
>
> --
> 2.53.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 3/5] avb: verify the boot images in the buffers they are booted from
2026-10-12 20:25 ` [PATCH v2 3/5] avb: verify the boot images in the buffers they are booted from Igor Opaniuk
@ 2026-10-20 7:53 ` Mattijs Korpershoek
2026-10-20 10:33 ` Guillaume La Roque
1 sibling, 0 replies; 18+ messages in thread
From: Mattijs Korpershoek @ 2026-10-20 7:53 UTC (permalink / raw)
To: Igor Opaniuk, u-boot
Cc: Tom Rini, Simon Glass, Shahriyar Jalayeri, Quentin Schulz,
Pengpeng Hou, Marek Vasut, Ahmad Fatoum, Igor Opaniuk
Hi Igor,
Thank you for the patch.
On Mon, Oct 12, 2026 at 22:25, Igor Opaniuk <igor.opaniuk@oss.qualcomm.com> wrote:
> bootmeth_android let libavb read boot and vendor_boot into its own buffers,
> hash them and free them, and then read both partitions again to boot them.
> The bytes that were verified and the bytes that were booted were therefore
> two separate reads of the storage device, which an attacker able to
> interpose the device can tell apart (reported on the list as a TOCTOU
> issue). It also read every image twice.
>
> Load boot and vendor_boot first and hand the buffers to libavb through
> AvbOps.get_preloaded_partition(), so the hashes are computed over the
> memory that bootm consumes afterwards. If a hash descriptor covers more
> bytes than the boot image header announced, the tail is read into the same
> buffer, bounded by the partition size; a descriptor larger than the buffer
> fails verification.
>
> Reported-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
Fixes: 125d9f3306ea ("bootstd: Add a bootmeth for Android")
> Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
Reviewed-by: Mattijs Korpershoek <mkorpershoek@kernel.org>
> ---
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 4/5] fastboot: add flashing lock/unlock/get_unlock_ability
2026-10-12 20:25 ` [PATCH v2 4/5] fastboot: add flashing lock/unlock/get_unlock_ability Igor Opaniuk
@ 2026-10-20 8:23 ` Mattijs Korpershoek
2026-10-20 10:33 ` Guillaume La Roque
1 sibling, 0 replies; 18+ messages in thread
From: Mattijs Korpershoek @ 2026-10-20 8:23 UTC (permalink / raw)
To: Igor Opaniuk, u-boot
Cc: Tom Rini, Simon Glass, Shahriyar Jalayeri, Quentin Schulz,
Pengpeng Hou, Marek Vasut, Ahmad Fatoum, Igor Opaniuk
Hi Igor,
Thank you for the patch.
On Mon, Oct 12, 2026 at 22:25, Igor Opaniuk <igor.opaniuk@oss.qualcomm.com> 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 <igor.opaniuk@oss.qualcomm.com>
> ---
> 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
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 5/5] doc: android/avb2: document the root key provisioning and lock state
2026-10-12 20:25 ` [PATCH v2 5/5] doc: android/avb2: document the root key provisioning and lock state Igor Opaniuk
@ 2026-10-20 8:27 ` Mattijs Korpershoek
2026-10-20 10:33 ` Guillaume La Roque
1 sibling, 0 replies; 18+ messages in thread
From: Mattijs Korpershoek @ 2026-10-20 8:27 UTC (permalink / raw)
To: Igor Opaniuk, u-boot
Cc: Tom Rini, Simon Glass, Shahriyar Jalayeri, Quentin Schulz,
Pengpeng Hou, Marek Vasut, Ahmad Fatoum, Igor Opaniuk
Hi Igor,
Thank you for the patch.
On Mon, Oct 12, 2026 at 22:25, Igor Opaniuk <igor.opaniuk@oss.qualcomm.com> wrote:
> Describe the three root key sources (AVB_ROOT_KEY_BUILTIN, _TEE, _BOARD),
> how to derive the SHA-256 digest of the vbmeta signing key with avbtool
> and provision it into the TEE persistent value with "avb write_pvalue_hex",
> the fail-closed behaviour when it is missing, and the device lock state as
> seen from the U-Boot shell and from fastboot (flashing lock/unlock with the
> mandatory userdata erase).
>
> Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
Reviewed-by: Mattijs Korpershoek <mkorpershoek@kernel.org>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 1/5] cmd: avb: add read_pvalue_hex/write_pvalue_hex
2026-10-12 20:25 ` [PATCH v2 1/5] cmd: avb: add read_pvalue_hex/write_pvalue_hex Igor Opaniuk
2026-10-20 7:38 ` Mattijs Korpershoek
@ 2026-10-20 10:33 ` Guillaume La Roque
1 sibling, 0 replies; 18+ messages in thread
From: Guillaume La Roque @ 2026-10-20 10:33 UTC (permalink / raw)
To: Igor Opaniuk
Cc: Mattijs Korpershoek, u-boot, Tom Rini, Simon Glass,
Shahriyar Jalayeri, Quentin Schulz, Pengpeng Hou, Marek Vasut,
Ahmad Fatoum
On Mon, 12 Oct 2026 22:25:03 +0200, Igor Opaniuk <igor.opaniuk@oss.qualcomm.com> wrote:
> write_pvalue stores its argument as a NUL-terminated string, which cannot
> carry binary data such as the 32-byte SHA-256 root key digest consumed by
> CONFIG_AVB_ROOT_KEY_TEE (persistent value avb.root_pub_digest). Add
> hex-string variants to provision and dump raw persistent values from the
> U-Boot shell.
>
>
> [...]
Reviewed-by: Guillaume La Roque <glaroque@baylibre.com>
--
Guillaume La Roque <glaroque@baylibre.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 3/5] avb: verify the boot images in the buffers they are booted from
2026-10-12 20:25 ` [PATCH v2 3/5] avb: verify the boot images in the buffers they are booted from Igor Opaniuk
2026-10-20 7:53 ` Mattijs Korpershoek
@ 2026-10-20 10:33 ` Guillaume La Roque
1 sibling, 0 replies; 18+ messages in thread
From: Guillaume La Roque @ 2026-10-20 10:33 UTC (permalink / raw)
To: Igor Opaniuk
Cc: Mattijs Korpershoek, u-boot, Tom Rini, Simon Glass,
Shahriyar Jalayeri, Quentin Schulz, Pengpeng Hou, Marek Vasut,
Ahmad Fatoum
On Mon, 12 Oct 2026 22:25:05 +0200, Igor Opaniuk <igor.opaniuk@oss.qualcomm.com> wrote:
Hi Igor,
>
> diff --git a/boot/bootmeth_android.c b/boot/bootmeth_android.c
> index 0db08d4f861c..862d0d67a2fe 100644
> --- a/boot/bootmeth_android.c
> +++ b/boot/bootmeth_android.c
> @@ -450,6 +460,35 @@ static int run_avb_verification(struct bootflow *bflow)
> if (priv->slot)
> sprintf(slot_suffix, "_%s", priv->slot);
>
> + /*
> + * Hand libavb the buffers boot and vendor_boot were loaded into, so the
> + * bytes it hashes are the bytes that get booted and the partitions are
> + * read only once.
> + */
> + if (priv->boot_addr) {
Not sure it's needed boot_addr come from boot_android_normal , in fact a check of
loadaddr in boot_android_normal could be better than here or we consider
user should wt properly loadaddr in uboot env
> + snprintf(partname, sizeof(partname), "boot%s", slot_suffix);
> + ret = avb_ops_add_preloaded(avb_ops, partname,
> + map_sysmem(priv->boot_addr, 0),
> + priv->boot_img_size,
> + priv->boot_part_size);
> + if (ret) {
> + ret = log_msg_ret("avb preload boot", ret);
> + goto out;
> + }
> + }
> + if (priv->vendor_boot_addr) {
missing check of android image version, if android image v2 this value
was not set and come from malloc struct android_priv , we can have any other value than 0.
--
Guillaume La Roque <glaroque@baylibre.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 2/5] cmd: avb: add lock/unlock commands
2026-10-12 20:25 ` [PATCH v2 2/5] cmd: avb: add lock/unlock commands Igor Opaniuk
2026-10-20 7:41 ` Mattijs Korpershoek
@ 2026-10-20 10:33 ` Guillaume La Roque
1 sibling, 0 replies; 18+ messages in thread
From: Guillaume La Roque @ 2026-10-20 10:33 UTC (permalink / raw)
To: Igor Opaniuk
Cc: Mattijs Korpershoek, u-boot, Tom Rini, Simon Glass,
Shahriyar Jalayeri, Quentin Schulz, Pengpeng Hou, Marek Vasut,
Ahmad Fatoum
On Mon, 12 Oct 2026 22:25:04 +0200, Igor Opaniuk <igor.opaniuk@oss.qualcomm.com> wrote:
> The OP-TEE AVB TA stores the device lock state
> (TA_AVB_CMD_READ/WRITE_LOCK_STATE) but U-Boot only ever read it, so a
> device backed by the TA could never leave the unlocked (orange) state from
> the bootloader. Add avb lock and avb unlock, which set the state through a
> new avb_write_lock_state() helper. As documented by the TA, changing the
> state resets all rollback indexes.
>
> [...]
Reviewed-by: Guillaume La Roque <glaroque@baylibre.com>
--
Guillaume La Roque <glaroque@baylibre.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 4/5] fastboot: add flashing lock/unlock/get_unlock_ability
2026-10-12 20:25 ` [PATCH v2 4/5] fastboot: add flashing lock/unlock/get_unlock_ability Igor Opaniuk
2026-10-20 8:23 ` Mattijs Korpershoek
@ 2026-10-20 10:33 ` Guillaume La Roque
1 sibling, 0 replies; 18+ messages in thread
From: Guillaume La Roque @ 2026-10-20 10:33 UTC (permalink / raw)
To: Igor Opaniuk
Cc: Mattijs Korpershoek, u-boot, Tom Rini, Simon Glass,
Shahriyar Jalayeri, Quentin Schulz, Pengpeng Hou, Marek Vasut,
Ahmad Fatoum
On Mon, 12 Oct 2026 22:25:06 +0200, Igor Opaniuk <igor.opaniuk@oss.qualcomm.com> wrote:
Hi Igor,
general comment , perhaps i miss somehting but if i do a fastboot flash
userdate or anyother partition with lock at true fastboot flash function
don't check lock state.
>
> diff --git a/drivers/fastboot/fb_command.c b/drivers/fastboot/fb_command.c
> index 18d86988f4c9..9bf012645ef2 100644
> --- a/drivers/fastboot/fb_command.c
> +++ b/drivers/fastboot/fb_command.c
> @@ -589,3 +611,100 @@ static void __maybe_unused oem_board(char *cmd_parameter, char *response)
> [ ... skip 40 lines ... ]
> + * @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);
what append if we use CONFIG_FASTBOOT_FLASH_NAND or
CONFIG_FASTBOOT_FLASH_BLOCK ?
it could be better to call generic function which are backend support
> [ ... skip 11 lines ... ]
> + *
> + * 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)
> +{
you don't check unlock_ability
> [ ... skip 10 lines ... ]
> + * flashing_unlock() - unlock the device
> + *
> + * The unlock is refused if userdata cannot be erased.
> + */
> +static void flashing_unlock(char *cmd_parameter, char *response)
> +{
same here
> [ ... skip 14 lines ... ]
> + * unlocking is always possible.
> + */
> +static void flashing_get_unlock_ability(char *cmd_parameter, char *response)
> +{
> + fastboot_okay("1", response);
> +}
this ability should not be common with android status ?
if we boot in bootloader fastboot mode we can disable lock mode without
action from developer setting in android no ?
>
> diff --git a/drivers/fastboot/fb_getvar.c b/drivers/fastboot/fb_getvar.c
> index e8aa0e09aa63..92c8e702a0b5 100644
> --- a/drivers/fastboot/fb_getvar.c
> +++ b/drivers/fastboot/fb_getvar.c
> @@ -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)
with this when FASTBOOT_CMD_FLASHING not defined unlocked value not return on fastboot getvar
all but if we do a fastboot getvar unlocked dispatch will be null.
do same as other or do a getvar_unlocked to return unsupported or
something like that
> @@ -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>
include should be on top of file .
> [ ... skip 20 lines ... ]
> +}
> +#else
> +static void __maybe_unused getvar_unlocked(char *var_parameter, char *response)
> +{
> +}
> +#endif
/> @@ -53,6 +53,9 @@ enum {
--
Guillaume La Roque <glaroque@baylibre.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 5/5] doc: android/avb2: document the root key provisioning and lock state
2026-10-12 20:25 ` [PATCH v2 5/5] doc: android/avb2: document the root key provisioning and lock state Igor Opaniuk
2026-10-20 8:27 ` Mattijs Korpershoek
@ 2026-10-20 10:33 ` Guillaume La Roque
1 sibling, 0 replies; 18+ messages in thread
From: Guillaume La Roque @ 2026-10-20 10:33 UTC (permalink / raw)
To: Igor Opaniuk
Cc: Mattijs Korpershoek, u-boot, Tom Rini, Simon Glass,
Shahriyar Jalayeri, Quentin Schulz, Pengpeng Hou, Marek Vasut,
Ahmad Fatoum
On Mon, 12 Oct 2026 22:25:07 +0200, Igor Opaniuk <igor.opaniuk@oss.qualcomm.com> wrote:
> Describe the three root key sources (AVB_ROOT_KEY_BUILTIN, _TEE, _BOARD),
> how to derive the SHA-256 digest of the vbmeta signing key with avbtool
> and provision it into the TEE persistent value with "avb write_pvalue_hex",
> the fail-closed behaviour when it is missing, and the device lock state as
> seen from the U-Boot shell and from fastboot (flashing lock/unlock with the
> mandatory userdata erase).
>
> [...]
Reviewed-by: Guillaume La Roque <glaroque@baylibre.com>
--
Guillaume La Roque <glaroque@baylibre.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2026-10-20 10:34 UTC | newest]
Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-12 20:25 [PATCH v2 0/5] avb: device lock state, root key provisioning and in-place verification Igor Opaniuk
2026-10-12 20:25 ` [PATCH v2 1/5] cmd: avb: add read_pvalue_hex/write_pvalue_hex Igor Opaniuk
2026-10-20 7:38 ` Mattijs Korpershoek
2026-10-20 10:33 ` Guillaume La Roque
2026-10-12 20:25 ` [PATCH v2 2/5] cmd: avb: add lock/unlock commands Igor Opaniuk
2026-10-20 7:41 ` Mattijs Korpershoek
2026-10-20 10:33 ` Guillaume La Roque
2026-10-12 20:25 ` [PATCH v2 3/5] avb: verify the boot images in the buffers they are booted from Igor Opaniuk
2026-10-20 7:53 ` Mattijs Korpershoek
2026-10-20 10:33 ` Guillaume La Roque
2026-10-12 20:25 ` [PATCH v2 4/5] fastboot: add flashing lock/unlock/get_unlock_ability Igor Opaniuk
2026-10-20 8:23 ` Mattijs Korpershoek
2026-10-20 10:33 ` Guillaume La Roque
2026-10-12 20:25 ` [PATCH v2 5/5] doc: android/avb2: document the root key provisioning and lock state Igor Opaniuk
2026-10-20 8:27 ` Mattijs Korpershoek
2026-10-20 10:33 ` Guillaume La Roque
2026-10-19 6:51 ` [PATCH v2 0/5] avb: device lock state, root key provisioning and in-place verification Igor Opaniuk
2026-10-20 7:21 ` Mattijs Korpershoek
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.