AVB assumed its partitions were on mmc: avb_ops_alloc() took a bare device
number and get_partition() called find_mmc_device(), which rules out
verified boot from anything else.
The user that benefits is "avb init", where boards already expect to name
an interface: board/armltd/total_compute/total_compute.env picks between
virtio and mmc at runtime and then runs "avb init ${blk_dev} 0", which no
release has ever accepted. bootmeth_android gains nothing yet, since
android_check() rejects non-mmc bootdevs through bootflow_iter_check_mmc().
Carry a uclass id alongside the device number and resolve the block device
with blk_get_devnum_by_uclass_id(), keeping mmc_init() and the hardware
partition switch on the mmc path only. "avb init <dev>" still means mmc,
"avb init <interface> <dev>" selects another bus, and neither touches the
device: it is still resolved on the first partition access.
Only mmc is started by AVB itself, so say that in Kconfig and avb2.rst.
Signed-off-by: Jan Kehren <[email protected]>
---
Changes in v3:
- Keep "avb init" lazy: validate the interface name only and resolve the
device on first partition access. The up-front lookup in v2 broke the
sandbox AVB tests, which run before mmc1.img exists
- Reword the motivation around "avb init" and total_compute
- Note the OP-TEE nested RPMB announcement caveat in avb2.rst
- test_avb_init_bad_device becomes test_avb_init_bad_interface, and
test_avb_init_interface no longer needs a device that exists
boot/bootmeth_android.c | 2 +-
cmd/avb.c | 25 ++++++---
common/Kconfig | 7 ++-
common/avb_verify.c | 71 +++++++++++++++++---------
doc/android/avb2.rst | 14 ++++-
include/avb_verify.h | 26 +++++++---
test/py/tests/test_android/test_avb.py | 28 ++++++++++
7 files changed, 135 insertions(+), 38 deletions(-)
diff --git a/boot/bootmeth_android.c b/boot/bootmeth_android.c
index 0db08d4f86..ae05c08488 100644
--- a/boot/bootmeth_android.c
+++ b/boot/bootmeth_android.c
@@ -443,7 +443,7 @@ static int run_avb_verification(struct bootflow *bflow)
bool unlocked = false;
int ret;
- avb_ops = avb_ops_alloc(desc->devnum);
+ avb_ops = avb_ops_alloc(desc->uclass_id, desc->devnum);
if (!avb_ops)
return log_msg_ret("avb ops", -ENOMEM);
diff --git a/cmd/avb.c b/cmd/avb.c
index 8fbd48ee5a..8a915b6583 100644
--- a/cmd/avb.c
+++ b/cmd/avb.c
@@ -16,17 +16,29 @@ static struct AvbOps *avb_ops;
int do_avb_init(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
- unsigned long mmc_dev;
+ enum uclass_id uclass_id;
+ int dev_num;
- if (argc != 2)
+ if (argc < 2 || argc > 3)
return CMD_RET_USAGE;
- mmc_dev = hextoul(argv[1], NULL);
+ /* "avb init <dev>" keeps its old meaning of mmc <dev> */
+ if (argc == 2) {
+ uclass_id = UCLASS_MMC;
+ dev_num = hextoul(argv[1], NULL);
+ } else {
+ uclass_id = blk_get_uclass_id(argv[1]);
+ if (uclass_id == UCLASS_INVALID) {
+ printf("Unknown interface '%s'\n", argv[1]);
+ return CMD_RET_FAILURE;
+ }
+ dev_num = hextoul(argv[2], NULL);
+ }
if (avb_ops)
avb_ops_free(avb_ops);
- avb_ops = avb_ops_alloc(mmc_dev);
+ avb_ops = avb_ops_alloc(uclass_id, dev_num);
if (avb_ops)
return CMD_RET_SUCCESS;
else
@@ -428,7 +440,7 @@ int do_avb_write_pvalue(struct cmd_tbl *cmdtp, int flag,
int argc,
}
static struct cmd_tbl cmd_avb[] = {
- U_BOOT_CMD_MKENT(init, 2, 0, do_avb_init, "", ""),
+ U_BOOT_CMD_MKENT(init, 3, 0, do_avb_init, "", ""),
U_BOOT_CMD_MKENT(read_rb, 2, 0, do_avb_read_rb, "", ""),
U_BOOT_CMD_MKENT(write_rb, 3, 0, do_avb_write_rb, "", ""),
U_BOOT_CMD_MKENT(is_unlocked, 1, 0, do_avb_is_unlocked, "", ""),
@@ -464,7 +476,8 @@ static int do_avb(struct cmd_tbl *cmdtp, int flag, int
argc, char *const argv[])
U_BOOT_CMD(
avb, 29, 0, do_avb,
"Provides commands for testing Android Verified Boot 2.0 functionality",
- "init <dev> - initialize avb2 for <dev>\n"
+ "init [<interface>] <dev> - initialize avb2 for hex <dev> on
<interface>\n"
+ " (mmc, nvme, usb, ...; defaults to mmc)\n"
"avb read_rb <num> - read rollback index at location <num>\n"
"avb write_rb <num> <rb> - write rollback index <rb> to <num>\n"
"avb is_unlocked - returns unlock status of the device\n"
diff --git a/common/Kconfig b/common/Kconfig
index c739225621..5590fbfdb2 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -885,9 +885,14 @@ config AVB_VERIFY
This option enables compilation of bootloader-dependent operations,
used by Android Verified Boot 2.0 library (libavb). Includes:
* Helpers to process strings in order to build OS bootargs.
- * Helpers to access MMC, similar to drivers/fastboot/fb_mmc.c.
+ * Helpers to access block devices, similar to
+ drivers/fastboot/fb_mmc.c.
* Helpers to alloc/init/free avb ops.
+ Only mmc is initialised by AVB itself. Any other interface must
+ already be enumerated when AVB runs, either by the board code or by
+ a command such as "usb start".
+
if AVB_VERIFY
config AVB_BUF_ADDR
diff --git a/common/avb_verify.c b/common/avb_verify.c
index fc29433c3c..b898ebb78f 100644
--- a/common/avb_verify.c
+++ b/common/avb_verify.c
@@ -8,6 +8,7 @@
#include <cpu_func.h>
#include <image.h>
#include <malloc.h>
+#include <mmc.h>
#include <part.h>
#include <tee.h>
#include <tee/optee_ta_avb.h>
@@ -386,44 +387,68 @@ static unsigned long avb_blk_write(struct avb_blk_part
*part, lbaint_t start,
start, sectors, tmp_buf);
}
-static struct avb_blk_part *get_partition(AvbOps *ops, const char *partition)
+static int avb_init_mmc(int dev_num)
{
+ struct mmc *mmc;
int ret;
- u8 dev_num;
- int part_num = 0;
- struct avb_blk_part *part;
- struct blk_desc *blk;
-
- part = malloc(sizeof(struct avb_blk_part));
- if (!part)
- return NULL;
- dev_num = get_boot_device(ops);
- part->mmc = find_mmc_device(dev_num);
- if (!part->mmc) {
+ mmc = find_mmc_device(dev_num);
+ if (!mmc) {
printf("%s: no MMC device at slot %x\n", __func__, dev_num);
- goto err;
+ return -ENODEV;
}
- ret = mmc_init(part->mmc);
+ ret = mmc_init(mmc);
if (ret) {
printf("%s: MMC initialization failed, err = %d\n",
__func__, ret);
- goto err;
+ return ret;
}
- if (IS_MMC(part->mmc)) {
- ret = mmc_switch_part(part->mmc, part_num);
+ if (IS_MMC(mmc)) {
+ /* Select the user area, where the GPT and our images live */
+ ret = mmc_switch_part(mmc, 0);
if (ret) {
printf("%s: MMC part switch failed, err = %d\n",
__func__, ret);
- goto err;
+ return ret;
}
}
- blk = mmc_get_blk_desc(part->mmc);
+ return 0;
+}
+
+static struct avb_blk_part *get_partition(AvbOps *ops, const char *partition)
+{
+ enum uclass_id uclass_id;
+ struct avb_blk_part *part;
+ struct blk_desc *blk;
+ int dev_num;
+ int ret;
+
+ part = malloc(sizeof(struct avb_blk_part));
+ if (!part)
+ return NULL;
+
+ dev_num = get_boot_device(ops);
+ uclass_id = get_boot_interface(ops);
+ if (uclass_id == UCLASS_INVALID || dev_num < 0) {
+ printf("%s: no storage interface, run 'avb init' first\n",
+ __func__);
+ goto err;
+ }
+
+ if (uclass_id == UCLASS_MMC) {
+ ret = avb_init_mmc(dev_num);
+ if (ret)
+ goto err;
+ }
+
+ blk = blk_get_devnum_by_uclass_id(uclass_id, dev_num);
if (!blk) {
- printf("%s: failed to obtain block descriptor\n", __func__);
+ /* Every bus but mmc must already be started when AVB runs */
+ printf("%s: no %s device %d, is the bus started?\n", __func__,
+ blk_get_uclass_name(uclass_id), dev_num);
goto err;
}
@@ -433,7 +458,6 @@ static struct avb_blk_part *get_partition(AvbOps *ops,
const char *partition)
goto err;
}
- part->dev_num = dev_num;
part->blk = blk;
return part;
@@ -1111,7 +1135,7 @@ __weak AvbIOResult avb_read_root_key_digest(AvbOps *ops,
uint8_t *digest)
* AVB2.0 AvbOps alloc/initialisation/free
* ============================================================================
*/
-AvbOps *avb_ops_alloc(int boot_device)
+AvbOps *avb_ops_alloc(enum uclass_id uclass_id, int dev_num)
{
struct AvbOpsData *ops_data;
@@ -1134,7 +1158,8 @@ AvbOps *avb_ops_alloc(int boot_device)
ops_data->ops.read_persistent_value = read_persistent_value;
#endif
ops_data->ops.get_size_of_partition = get_size_of_partition;
- ops_data->mmc_dev = boot_device;
+ ops_data->uclass_id = uclass_id;
+ ops_data->dev_num = dev_num;
return &ops_data->ops;
}
diff --git a/doc/android/avb2.rst b/doc/android/avb2.rst
index a514701241..43d5600d2d 100644
--- a/doc/android/avb2.rst
+++ b/doc/android/avb2.rst
@@ -29,6 +29,17 @@ integration in ``common/avb_verify.c`` and the platform port
under
For additional details check [1]_.
+Only mmc is started by AVB itself. Any other interface must already be
+enumerated when AVB runs, either by the board code or by a command such as
+``usb start``, or the partition lookup fails. ``avb init`` only records the
+interface and device number; the device is resolved on the first partition
+access.
+
+If the Android images live on another interface while the AVB trusted
+application keeps its state in eMMC RPMB, the trusted application's first
+RPMB request is also the first eMMC access, and it is made from within a
+supplicant RPC. That needs OP-TEE to tolerate a nested RPMB announcement.
+
AVB using OP-TEE (optional)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -80,7 +91,8 @@ AVB 2.0 U-Boot shell commands
Provides CLI interface to invoke AVB 2.0 verification + misc. commands for
different testing purposes::
- avb init <dev> - initialize avb 2 for <dev>
+ avb init [<interface>] <dev> - initialize avb 2 for hex <dev> on
+ <interface> (mmc, nvme, usb, ...; defaults to mmc)
avb read_rb <num> - read rollback index at location <num>
avb write_rb <num> <rb> - write rollback index <rb> to <num>
avb is_unlocked - returns unlock status of the device
diff --git a/include/avb_verify.h b/include/avb_verify.h
index 8c97adbfbf..6dbbb39a8a 100644
--- a/include/avb_verify.h
+++ b/include/avb_verify.h
@@ -7,8 +7,9 @@
#define _AVB_VERIFY_H
#include <../lib/libavb/libavb.h>
+#include <blk.h>
#include <mapmem.h>
-#include <mmc.h>
+#include <part.h>
#define AVB_MAX_ARGS 1024
#define VERITY_TABLE_OPT_RESTART "restart_on_corruption"
@@ -24,7 +25,8 @@ enum avb_boot_state {
struct AvbOpsData {
struct AvbOps ops;
- int mmc_dev;
+ enum uclass_id uclass_id;
+ int dev_num;
enum avb_boot_state boot_state;
#ifdef CONFIG_OPTEE_TA_AVB
struct udevice *tee;
@@ -33,8 +35,6 @@ struct AvbOpsData {
};
struct avb_blk_part {
- int dev_num;
- struct mmc *mmc;
struct blk_desc *blk;
struct disk_partition info;
};
@@ -44,7 +44,7 @@ enum avb_io_type {
AVB_IO_WRITE
};
-AvbOps *avb_ops_alloc(int boot_device);
+AvbOps *avb_ops_alloc(enum uclass_id uclass_id, int dev_num);
void avb_ops_free(AvbOps *ops);
/**
@@ -110,10 +110,24 @@ static inline int get_boot_device(AvbOps *ops)
if (ops) {
data = ops->user_data;
if (data)
- return data->mmc_dev;
+ return data->dev_num;
}
return -1;
}
+/* Mirrors get_boot_device(): no ops means no interface, not a default one */
+static inline enum uclass_id get_boot_interface(AvbOps *ops)
+{
+ struct AvbOpsData *data;
+
+ if (ops) {
+ data = ops->user_data;
+ if (data)
+ return data->uclass_id;
+ }
+
+ return UCLASS_INVALID;
+}
+
#endif /* _AVB_VERIFY_H */
diff --git a/test/py/tests/test_android/test_avb.py
b/test/py/tests/test_android/test_avb.py
index 137d83e1de..e183b3da17 100644
--- a/test/py/tests/test_android/test_avb.py
+++ b/test/py/tests/test_android/test_avb.py
@@ -135,3 +135,31 @@ def test_avb_persistent_values(ubman):
response = ubman.run_command('avb read_pvalue test 12')
assert response == 'Read 12 bytes, value = value_value'
+
+
[email protected]('cmd_avb')
[email protected]('cmd_mmc')
+def test_avb_init_interface(ubman):
+ """Check that 'avb init' accepts an explicit interface
+
+ 'avb init' only records the interface and device number, so this does
+ not need a device that exists.
+ """
+
+ response = ubman.run_command('avb init mmc 1')
+ assert response == ''
+
+ response = ubman.run_command('avb init 1')
+ assert response == ''
+
+
[email protected]('cmd_avb')
+def test_avb_init_bad_interface(ubman):
+ """Check that 'avb init' rejects an interface that does not exist
+
+ The device itself is only resolved on first access, so a device number
+ that does not exist is not rejected here.
+ """
+
+ response = ubman.run_command('avb init nonsense 0')
+ assert response.find("Unknown interface 'nonsense'") != -1
--
2.43.0