[PATCH v2 0/5] bootstd: Add Android support

Android boot flow is a bit different than a regular Linux distro. Android relies on multiple partitions in order to boot.
A typical boot flow would be: 1. Parse the Bootloader Control Block (BCB, misc partition) 2. If BCB requested bootonce-bootloader, start fastboot and wait. 3. If BCB requested recovery or normal android, run the following: a. Get slot (A/B) from BCB b. Run AVB (Android Verified Boot) on boot partitions c. Load boot and vendor_boot partitions d. Load device-tree, ramdisk and boot
The AOSP documentation has more details at [1], [2], [3]
This has been implemented via complex boot scripts such as [4]. However, these boot script are neither very maintainable nor generic. Moreover, DISTRO_DEFAULTS is being deprecated [5].
Add a generic Android bootflow implementation for bootstd.
For this initial version, only boot image v4 is supported.
This has been tested on sandbox using: $ ./test/py/test.py --bd sandbox --build -k test_ut
This has also been tested on the AM62X SK EVM using TI's Android SDK[6] To test on TI board, the following (WIP) patch is needed as well: https://gitlab.baylibre.com/baylibre/ti/ti-u-boot/-/commit/84cceb912bccd7cdd...
[1] https://source.android.com/docs/core/architecture/bootloader [2] https://source.android.com/docs/core/architecture/partitions [3] https://source.android.com/docs/core/architecture/partitions/generic-boot [4] https://source.denx.de/u-boot/u-boot/-/blob/master/include/configs/meson64_a... [5] https://lore.kernel.org/r/all/20230914165615.1058529-17-sjg@chromium.org/ [6] https://software-dl.ti.com/processor-sdk-android/esd/AM62X/09_02_00/docs/and...
Signed-off-by: Mattijs Korpershoek mkorpershoek@baylibre.com --- Changes in v2: - Dropped patch 2/6 boot: android: Add image_android_get_version() (Igor) - Fixed multi-line comment style (Igor, Simon) - Added dependency on CMD_FASTBOOT for BOOTMETH_ANDROID (Igor) - Fixed various resource leaks (Igor) - Fixed bootmeth_priv dangling pointer on error cases (Igor) - Updated test instructions in commit message for patch 6/6 - Added __weak impl of get_avendor_bootimg_addr() in patch 1 (dropped Igor's review because of this change) - Added extra info in Kconfig to detail MMC limitation (Simon) - Fixed typo Bootmethod->Bootmeth (Simon) - Documented android_priv structure (Simon) - Demoted various messages from printf() to log_debug (Simon) - Fixed some lines too long (Simon) - Added function documentation to read_slotted_partition() (Simon) - Added some doc about avb extra_args being modified (Simon) - Link to v1: https://lore.kernel.org/r/20240606-bootmeth-android-v1-0-0c69d4457cc5@baylib...
--- Mattijs Korpershoek (5): boot: android: Provide vendor_bootimg_addr in boot_get_fdt() bootstd: Add bootflow_iter_check_mmc() helper android: boot: Add set_abootimg_addr() and set_avendor_bootimg_addr() bootstd: Add a bootmeth for Android bootstd: Add test for bootmeth_android
MAINTAINERS | 7 + arch/sandbox/dts/test.dts | 8 + boot/Kconfig | 16 ++ boot/Makefile | 2 + boot/bootflow.c | 12 + boot/bootmeth_android.c | 553 ++++++++++++++++++++++++++++++++++++++++++++++ boot/bootmeth_android.h | 29 +++ boot/image-android.c | 5 + boot/image-fdt.c | 2 +- cmd/abootimg.c | 10 + configs/sandbox_defconfig | 2 +- doc/develop/bootstd.rst | 6 + include/bootflow.h | 9 + include/image.h | 14 ++ test/boot/bootflow.c | 65 +++++- test/py/tests/test_ut.py | 76 +++++++ 16 files changed, 811 insertions(+), 5 deletions(-) --- base-commit: f9886bc60f42d5bcfcfa4e474af7dc230400b6be change-id: 20240605-bootmeth-android-bfc8596e9367
Best regards,

When calling android_image_get_dtb_by_index() using boot image v3+, we should also pass the vendor_boot ramdisk address.
Use get_avendor_bootimg_addr() to do so.
Notes: on boot image v2, this is harmless since get_avendor_bootimg_addr() returns -1. for legacy implementations that don't have CMD_ABOOTIMG, add a weak implementation to avoid linking errors.
Signed-off-by: Mattijs Korpershoek mkorpershoek@baylibre.com --- boot/image-android.c | 5 +++++ boot/image-fdt.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/boot/image-android.c b/boot/image-android.c index ee626972c114..09c7a44e058a 100644 --- a/boot/image-android.c +++ b/boot/image-android.c @@ -56,6 +56,11 @@ static ulong add_trailer(ulong bootconfig_start_addr, ulong bootconfig_size) return BOOTCONFIG_TRAILER_SIZE; }
+__weak ulong get_avendor_bootimg_addr(void) +{ + return -1; +} + static void android_boot_image_v3_v4_parse_hdr(const struct andr_boot_img_hdr_v3 *hdr, struct andr_image_data *data) { diff --git a/boot/image-fdt.c b/boot/image-fdt.c index 56dd7687f51c..8332792b8e80 100644 --- a/boot/image-fdt.c +++ b/boot/image-fdt.c @@ -502,7 +502,7 @@ int boot_get_fdt(void *buf, const char *select, uint arch, * Firstly check if this android boot image has dtb field. */ dtb_idx = (u32)env_get_ulong("adtb_idx", 10, 0); - if (android_image_get_dtb_by_index((ulong)hdr, 0, + if (android_image_get_dtb_by_index((ulong)hdr, get_avendor_bootimg_addr(), dtb_idx, &fdt_addr, &fdt_size)) { fdt_blob = (char *)map_sysmem(fdt_addr, 0); if (fdt_check_header(fdt_blob))

On Thu, 13 Jun 2024 at 04:13, Mattijs Korpershoek mkorpershoek@baylibre.com wrote:
When calling android_image_get_dtb_by_index() using boot image v3+, we should also pass the vendor_boot ramdisk address.
Use get_avendor_bootimg_addr() to do so.
Notes: on boot image v2, this is harmless since get_avendor_bootimg_addr() returns -1. for legacy implementations that don't have CMD_ABOOTIMG, add a weak implementation to avoid linking errors.
Signed-off-by: Mattijs Korpershoek mkorpershoek@baylibre.com
boot/image-android.c | 5 +++++ boot/image-fdt.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-)
Reviewed-by: Simon Glass sjg@chromium.org

On Thu 13 Jun 2024 at 17:42, Mattijs Korpershoek mkorpershoek@baylibre.com wrote:
When calling android_image_get_dtb_by_index() using boot image v3+, we should also pass the vendor_boot ramdisk address.
Use get_avendor_bootimg_addr() to do so.
Notes: on boot image v2, this is harmless since get_avendor_bootimg_addr() returns -1. for legacy implementations that don't have CMD_ABOOTIMG, add a weak implementation to avoid linking errors.
Signed-off-by: Mattijs Korpershoek mkorpershoek@baylibre.com
boot/image-android.c | 5 +++++ boot/image-fdt.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/boot/image-android.c b/boot/image-android.c index ee626972c114..09c7a44e058a 100644 --- a/boot/image-android.c +++ b/boot/image-android.c @@ -56,6 +56,11 @@ static ulong add_trailer(ulong bootconfig_start_addr, ulong bootconfig_size) return BOOTCONFIG_TRAILER_SIZE; }
+__weak ulong get_avendor_bootimg_addr(void) +{
- return -1;
+}
static void android_boot_image_v3_v4_parse_hdr(const struct andr_boot_img_hdr_v3 *hdr, struct andr_image_data *data) { diff --git a/boot/image-fdt.c b/boot/image-fdt.c index 56dd7687f51c..8332792b8e80 100644 --- a/boot/image-fdt.c +++ b/boot/image-fdt.c @@ -502,7 +502,7 @@ int boot_get_fdt(void *buf, const char *select, uint arch, * Firstly check if this android boot image has dtb field. */ dtb_idx = (u32)env_get_ulong("adtb_idx", 10, 0);
if (android_image_get_dtb_by_index((ulong)hdr, 0,
if (android_image_get_dtb_by_index((ulong)hdr, get_avendor_bootimg_addr(), dtb_idx, &fdt_addr, &fdt_size)) { fdt_blob = (char *)map_sysmem(fdt_addr, 0); if (fdt_check_header(fdt_blob))
-- 2.45.2
Reviewed-by: Julien Masson jmasson@baylibre.com

On Thu, Jun 13, 2024 at 12:13 PM Mattijs Korpershoek mkorpershoek@baylibre.com wrote:
When calling android_image_get_dtb_by_index() using boot image v3+, we should also pass the vendor_boot ramdisk address.
Use get_avendor_bootimg_addr() to do so.
Notes: on boot image v2, this is harmless since get_avendor_bootimg_addr() returns -1. for legacy implementations that don't have CMD_ABOOTIMG, add a weak implementation to avoid linking errors.
Signed-off-by: Mattijs Korpershoek mkorpershoek@baylibre.com
boot/image-android.c | 5 +++++ boot/image-fdt.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/boot/image-android.c b/boot/image-android.c index ee626972c114..09c7a44e058a 100644 --- a/boot/image-android.c +++ b/boot/image-android.c @@ -56,6 +56,11 @@ static ulong add_trailer(ulong bootconfig_start_addr, ulong bootconfig_size) return BOOTCONFIG_TRAILER_SIZE; }
+__weak ulong get_avendor_bootimg_addr(void) +{
return -1;
+}
static void android_boot_image_v3_v4_parse_hdr(const struct andr_boot_img_hdr_v3 *hdr, struct andr_image_data *data) { diff --git a/boot/image-fdt.c b/boot/image-fdt.c index 56dd7687f51c..8332792b8e80 100644 --- a/boot/image-fdt.c +++ b/boot/image-fdt.c @@ -502,7 +502,7 @@ int boot_get_fdt(void *buf, const char *select, uint arch, * Firstly check if this android boot image has dtb field. */ dtb_idx = (u32)env_get_ulong("adtb_idx", 10, 0);
if (android_image_get_dtb_by_index((ulong)hdr, 0,
if (android_image_get_dtb_by_index((ulong)hdr, get_avendor_bootimg_addr(), dtb_idx, &fdt_addr, &fdt_size)) { fdt_blob = (char *)map_sysmem(fdt_addr, 0); if (fdt_check_header(fdt_blob))
-- 2.45.2
Reviewed-by: Igor Opaniuk igor.opaniuk@gmail.com

Hi,
Le 13/06/2024 à 12:13, Mattijs Korpershoek a écrit :
When calling android_image_get_dtb_by_index() using boot image v3+, we should also pass the vendor_boot ramdisk address.
Use get_avendor_bootimg_addr() to do so.
Notes: on boot image v2, this is harmless since get_avendor_bootimg_addr() returns -1. for legacy implementations that don't have CMD_ABOOTIMG, add a weak implementation to avoid linking errors.
Signed-off-by: Mattijs Korpershoek mkorpershoek@baylibre.com
boot/image-android.c | 5 +++++ boot/image-fdt.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/boot/image-android.c b/boot/image-android.c index ee626972c114..09c7a44e058a 100644 --- a/boot/image-android.c +++ b/boot/image-android.c @@ -56,6 +56,11 @@ static ulong add_trailer(ulong bootconfig_start_addr, ulong bootconfig_size) return BOOTCONFIG_TRAILER_SIZE; }
+__weak ulong get_avendor_bootimg_addr(void) +{
- return -1;
+}
- static void android_boot_image_v3_v4_parse_hdr(const struct andr_boot_img_hdr_v3 *hdr, struct andr_image_data *data) {
diff --git a/boot/image-fdt.c b/boot/image-fdt.c index 56dd7687f51c..8332792b8e80 100644 --- a/boot/image-fdt.c +++ b/boot/image-fdt.c @@ -502,7 +502,7 @@ int boot_get_fdt(void *buf, const char *select, uint arch, * Firstly check if this android boot image has dtb field. */ dtb_idx = (u32)env_get_ulong("adtb_idx", 10, 0);
if (android_image_get_dtb_by_index((ulong)hdr, 0,
if (android_image_get_dtb_by_index((ulong)hdr, get_avendor_bootimg_addr(), dtb_idx, &fdt_addr, &fdt_size)) { fdt_blob = (char *)map_sysmem(fdt_addr, 0); if (fdt_check_header(fdt_blob))
Reviewed-by: Guillaume La Roque glaroque@baylibre.com

Some bootflows might be able to only boot from MMC devices.
Add a helper function these bootflows can use.
Reviewed-by: Igor Opaniuk igor.opaniuk@gmail.com Signed-off-by: Mattijs Korpershoek mkorpershoek@baylibre.com --- boot/bootflow.c | 12 ++++++++++++ include/bootflow.h | 9 +++++++++ 2 files changed, 21 insertions(+)
diff --git a/boot/bootflow.c b/boot/bootflow.c index 9aa3179c3881..59d77d2385f4 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -575,6 +575,18 @@ int bootflow_iter_check_blk(const struct bootflow_iter *iter) return -ENOTSUPP; }
+int bootflow_iter_check_mmc(const struct bootflow_iter *iter) +{ + const struct udevice *media = dev_get_parent(iter->dev); + enum uclass_id id = device_get_uclass_id(media); + + log_debug("uclass %d: %s\n", id, uclass_get_name(id)); + if (id == UCLASS_MMC) + return 0; + + return -ENOTSUPP; +} + int bootflow_iter_check_sf(const struct bootflow_iter *iter) { const struct udevice *media = dev_get_parent(iter->dev); diff --git a/include/bootflow.h b/include/bootflow.h index 080ee8501225..6058ddd89b16 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -407,6 +407,15 @@ void bootflow_remove(struct bootflow *bflow); */ int bootflow_iter_check_blk(const struct bootflow_iter *iter);
+/** + * bootflow_iter_check_mmc() - Check that a bootflow uses a MMC device + * + * This checks the bootdev in the bootflow to make sure it uses a mmc device + * + * Return: 0 if OK, -ENOTSUPP if some other device is used (e.g. ethernet) + */ +int bootflow_iter_check_mmc(const struct bootflow_iter *iter); + /** * bootflow_iter_check_sf() - Check that a bootflow uses SPI FLASH *

On Thu 13 Jun 2024 at 16:15, Mattijs Korpershoek mkorpershoek@baylibre.com wrote:
Some bootflows might be able to only boot from MMC devices.
Add a helper function these bootflows can use.
Reviewed-by: Igor Opaniuk igor.opaniuk@gmail.com Signed-off-by: Mattijs Korpershoek mkorpershoek@baylibre.com
boot/bootflow.c | 12 ++++++++++++ include/bootflow.h | 9 +++++++++ 2 files changed, 21 insertions(+)
diff --git a/boot/bootflow.c b/boot/bootflow.c index 9aa3179c3881..59d77d2385f4 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -575,6 +575,18 @@ int bootflow_iter_check_blk(const struct bootflow_iter *iter) return -ENOTSUPP; }
+int bootflow_iter_check_mmc(const struct bootflow_iter *iter) +{
- const struct udevice *media = dev_get_parent(iter->dev);
- enum uclass_id id = device_get_uclass_id(media);
- log_debug("uclass %d: %s\n", id, uclass_get_name(id));
- if (id == UCLASS_MMC)
return 0;
- return -ENOTSUPP;
+}
int bootflow_iter_check_sf(const struct bootflow_iter *iter) { const struct udevice *media = dev_get_parent(iter->dev); diff --git a/include/bootflow.h b/include/bootflow.h index 080ee8501225..6058ddd89b16 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -407,6 +407,15 @@ void bootflow_remove(struct bootflow *bflow); */ int bootflow_iter_check_blk(const struct bootflow_iter *iter);
+/**
- bootflow_iter_check_mmc() - Check that a bootflow uses a MMC device
- This checks the bootdev in the bootflow to make sure it uses a mmc device
- Return: 0 if OK, -ENOTSUPP if some other device is used (e.g. ethernet)
- */
+int bootflow_iter_check_mmc(const struct bootflow_iter *iter);
/**
- bootflow_iter_check_sf() - Check that a bootflow uses SPI FLASH
-- 2.45.2
Reviewed-by: Julien Masson jmasson@baylibre.com

Le 13/06/2024 à 12:13, Mattijs Korpershoek a écrit :
Some bootflows might be able to only boot from MMC devices.
Add a helper function these bootflows can use.
Reviewed-by: Igor Opaniuk igor.opaniuk@gmail.com Signed-off-by: Mattijs Korpershoek mkorpershoek@baylibre.com
boot/bootflow.c | 12 ++++++++++++ include/bootflow.h | 9 +++++++++ 2 files changed, 21 insertions(+)
diff --git a/boot/bootflow.c b/boot/bootflow.c index 9aa3179c3881..59d77d2385f4 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -575,6 +575,18 @@ int bootflow_iter_check_blk(const struct bootflow_iter *iter) return -ENOTSUPP; }
+int bootflow_iter_check_mmc(const struct bootflow_iter *iter) +{
- const struct udevice *media = dev_get_parent(iter->dev);
- enum uclass_id id = device_get_uclass_id(media);
- log_debug("uclass %d: %s\n", id, uclass_get_name(id));
- if (id == UCLASS_MMC)
return 0;
- return -ENOTSUPP;
+}
- int bootflow_iter_check_sf(const struct bootflow_iter *iter) { const struct udevice *media = dev_get_parent(iter->dev);
diff --git a/include/bootflow.h b/include/bootflow.h index 080ee8501225..6058ddd89b16 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -407,6 +407,15 @@ void bootflow_remove(struct bootflow *bflow); */ int bootflow_iter_check_blk(const struct bootflow_iter *iter);
+/**
- bootflow_iter_check_mmc() - Check that a bootflow uses a MMC device
- This checks the bootdev in the bootflow to make sure it uses a mmc device
- Return: 0 if OK, -ENOTSUPP if some other device is used (e.g. ethernet)
- */
+int bootflow_iter_check_mmc(const struct bootflow_iter *iter);
- /**
- bootflow_iter_check_sf() - Check that a bootflow uses SPI FLASH
Reviewed-by: Guillaume La Roque glaroque@baylibre.com

The only way to configure the load addresses for both bootimg and vendor_bootimg is by using the "abootimg" command. If we want to use the C API, there is no equivalent.
Add set_abootimg_addr() and set_avendor_bootimg_addr() so that we can specify the load address from C.
This can be useful for implementing an Android bootmethod.
Reviewed-by: Igor Opaniuk igor.opaniuk@gmail.com Signed-off-by: Mattijs Korpershoek mkorpershoek@baylibre.com --- cmd/abootimg.c | 10 ++++++++++ include/image.h | 14 ++++++++++++++ 2 files changed, 24 insertions(+)
diff --git a/cmd/abootimg.c b/cmd/abootimg.c index 327712a536c0..ae7a1a7c83b0 100644 --- a/cmd/abootimg.c +++ b/cmd/abootimg.c @@ -22,6 +22,11 @@ ulong get_abootimg_addr(void) return (_abootimg_addr == -1 ? image_load_addr : _abootimg_addr); }
+void set_abootimg_addr(ulong addr) +{ + _abootimg_addr = addr; +} + ulong get_ainit_bootimg_addr(void) { return _ainit_bootimg_addr; @@ -32,6 +37,11 @@ ulong get_avendor_bootimg_addr(void) return _avendor_bootimg_addr; }
+void set_avendor_bootimg_addr(ulong addr) +{ + _avendor_bootimg_addr = addr; +} + static int abootimg_get_ver(int argc, char *const argv[]) { const struct andr_boot_img_hdr_v0 *hdr; diff --git a/include/image.h b/include/image.h index c5b288f62b44..df2decbf5c2a 100644 --- a/include/image.h +++ b/include/image.h @@ -1970,6 +1970,13 @@ bool is_android_vendor_boot_image_header(const void *vendor_boot_img); */ ulong get_abootimg_addr(void);
+/** + * set_abootimg_addr() - Set Android boot image address + * + * Return: no returned results + */ +void set_abootimg_addr(ulong addr); + /** * get_ainit_bootimg_addr() - Get Android init boot image address * @@ -1984,6 +1991,13 @@ ulong get_ainit_bootimg_addr(void); */ ulong get_avendor_bootimg_addr(void);
+/** + * set_abootimg_addr() - Set Android vendor boot image address + * + * Return: no returned results + */ +void set_avendor_bootimg_addr(ulong addr); + /** * board_fit_config_name_match() - Check for a matching board name *

On Thu 13 Jun 2024 at 16:16, Mattijs Korpershoek mkorpershoek@baylibre.com wrote:
The only way to configure the load addresses for both bootimg and vendor_bootimg is by using the "abootimg" command. If we want to use the C API, there is no equivalent.
Add set_abootimg_addr() and set_avendor_bootimg_addr() so that we can specify the load address from C.
This can be useful for implementing an Android bootmethod.
Reviewed-by: Igor Opaniuk igor.opaniuk@gmail.com Signed-off-by: Mattijs Korpershoek mkorpershoek@baylibre.com
cmd/abootimg.c | 10 ++++++++++ include/image.h | 14 ++++++++++++++ 2 files changed, 24 insertions(+)
diff --git a/cmd/abootimg.c b/cmd/abootimg.c index 327712a536c0..ae7a1a7c83b0 100644 --- a/cmd/abootimg.c +++ b/cmd/abootimg.c @@ -22,6 +22,11 @@ ulong get_abootimg_addr(void) return (_abootimg_addr == -1 ? image_load_addr : _abootimg_addr); }
+void set_abootimg_addr(ulong addr) +{
- _abootimg_addr = addr;
+}
ulong get_ainit_bootimg_addr(void) { return _ainit_bootimg_addr; @@ -32,6 +37,11 @@ ulong get_avendor_bootimg_addr(void) return _avendor_bootimg_addr; }
+void set_avendor_bootimg_addr(ulong addr) +{
- _avendor_bootimg_addr = addr;
+}
static int abootimg_get_ver(int argc, char *const argv[]) { const struct andr_boot_img_hdr_v0 *hdr; diff --git a/include/image.h b/include/image.h index c5b288f62b44..df2decbf5c2a 100644 --- a/include/image.h +++ b/include/image.h @@ -1970,6 +1970,13 @@ bool is_android_vendor_boot_image_header(const void *vendor_boot_img); */ ulong get_abootimg_addr(void);
+/**
- set_abootimg_addr() - Set Android boot image address
- Return: no returned results
- */
+void set_abootimg_addr(ulong addr);
/**
- get_ainit_bootimg_addr() - Get Android init boot image address
@@ -1984,6 +1991,13 @@ ulong get_ainit_bootimg_addr(void); */ ulong get_avendor_bootimg_addr(void);
+/**
- set_abootimg_addr() - Set Android vendor boot image address
- Return: no returned results
- */
+void set_avendor_bootimg_addr(ulong addr);
/**
- board_fit_config_name_match() - Check for a matching board name
-- 2.45.2
Reviewed-by: Julien Masson jmasson@baylibre.com

On Thu, 13 Jun 2024 at 04:13, Mattijs Korpershoek mkorpershoek@baylibre.com wrote:
The only way to configure the load addresses for both bootimg and vendor_bootimg is by using the "abootimg" command. If we want to use the C API, there is no equivalent.
Add set_abootimg_addr() and set_avendor_bootimg_addr() so that we can specify the load address from C.
This can be useful for implementing an Android bootmethod.
Reviewed-by: Igor Opaniuk igor.opaniuk@gmail.com Signed-off-by: Mattijs Korpershoek mkorpershoek@baylibre.com
cmd/abootimg.c | 10 ++++++++++ include/image.h | 14 ++++++++++++++ 2 files changed, 24 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org

Le 13/06/2024 à 12:13, Mattijs Korpershoek a écrit :
The only way to configure the load addresses for both bootimg and vendor_bootimg is by using the "abootimg" command. If we want to use the C API, there is no equivalent.
Add set_abootimg_addr() and set_avendor_bootimg_addr() so that we can specify the load address from C.
This can be useful for implementing an Android bootmethod.
Reviewed-by: Igor Opaniuk igor.opaniuk@gmail.com Signed-off-by: Mattijs Korpershoek mkorpershoek@baylibre.com
cmd/abootimg.c | 10 ++++++++++ include/image.h | 14 ++++++++++++++ 2 files changed, 24 insertions(+)
diff --git a/cmd/abootimg.c b/cmd/abootimg.c index 327712a536c0..ae7a1a7c83b0 100644 --- a/cmd/abootimg.c +++ b/cmd/abootimg.c @@ -22,6 +22,11 @@ ulong get_abootimg_addr(void) return (_abootimg_addr == -1 ? image_load_addr : _abootimg_addr); }
+void set_abootimg_addr(ulong addr) +{
- _abootimg_addr = addr;
+}
- ulong get_ainit_bootimg_addr(void) { return _ainit_bootimg_addr;
@@ -32,6 +37,11 @@ ulong get_avendor_bootimg_addr(void) return _avendor_bootimg_addr; }
+void set_avendor_bootimg_addr(ulong addr) +{
- _avendor_bootimg_addr = addr;
+}
- static int abootimg_get_ver(int argc, char *const argv[]) { const struct andr_boot_img_hdr_v0 *hdr;
diff --git a/include/image.h b/include/image.h index c5b288f62b44..df2decbf5c2a 100644 --- a/include/image.h +++ b/include/image.h @@ -1970,6 +1970,13 @@ bool is_android_vendor_boot_image_header(const void *vendor_boot_img); */ ulong get_abootimg_addr(void);
+/**
- set_abootimg_addr() - Set Android boot image address
- Return: no returned results
- */
+void set_abootimg_addr(ulong addr);
- /**
- get_ainit_bootimg_addr() - Get Android init boot image address
@@ -1984,6 +1991,13 @@ ulong get_ainit_bootimg_addr(void); */ ulong get_avendor_bootimg_addr(void);
+/**
- set_abootimg_addr() - Set Android vendor boot image address
- Return: no returned results
- */
+void set_avendor_bootimg_addr(ulong addr);
- /**
- board_fit_config_name_match() - Check for a matching board name
Reviewed-by: Guillaume La Roque glaroque@baylibre.com

Android boot flow is a bit different than a regular Linux distro. Android relies on multiple partitions in order to boot.
A typical boot flow would be: 1. Parse the Bootloader Control Block (BCB, misc partition) 2. If BCB requested bootonce-bootloader, start fastboot and wait. 3. If BCB requested recovery or normal android, run the following: 3.a. Get slot (A/B) from BCB 3.b. Run AVB (Android Verified Boot) on boot partitions 3.c. Load boot and vendor_boot partitions 3.d. Load device-tree, ramdisk and boot
The AOSP documentation has more details at [1], [2], [3]
This has been implemented via complex boot scripts such as [4]. However, these boot script are neither very maintainable nor generic. Moreover, DISTRO_DEFAULTS is being deprecated [5].
Add a generic Android bootflow implementation for bootstd. For this initial version, only boot image v4 is supported.
[1] https://source.android.com/docs/core/architecture/bootloader [2] https://source.android.com/docs/core/architecture/partitions [3] https://source.android.com/docs/core/architecture/partitions/generic-boot [4] https://source.denx.de/u-boot/u-boot/-/blob/master/include/configs/meson64_a... [5] https://lore.kernel.org/r/all/20230914165615.1058529-17-sjg@chromium.org/
Reviewed-by: Simon Glass sjg@chromium.org Signed-off-by: Mattijs Korpershoek mkorpershoek@baylibre.com --- MAINTAINERS | 7 + boot/Kconfig | 16 ++ boot/Makefile | 2 + boot/bootmeth_android.c | 553 ++++++++++++++++++++++++++++++++++++++++++++++++ boot/bootmeth_android.h | 29 +++ doc/develop/bootstd.rst | 6 + 6 files changed, 613 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS index 66783d636e3d..6d2b87720565 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -939,6 +939,13 @@ F: include/bootstd.h F: net/eth_bootdevice.c F: test/boot/
+BOOTMETH_ANDROID +M: Mattijs Korpershoek mkorpershoek@baylibre.com +S: Maintained +T: git https://source.denx.de/u-boot/custodians/u-boot-dfu.git +F: boot/bootmeth_android.c +F: boot/bootmeth_android.h + BTRFS M: Marek Behún kabel@kernel.org R: Qu Wenruo wqu@suse.com diff --git a/boot/Kconfig b/boot/Kconfig index 6f3096c15a6f..88266c8d2ed3 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -494,6 +494,22 @@ config BOOTMETH_GLOBAL EFI bootmgr, since they take full control over which bootdevs are selected to boot.
+config BOOTMETH_ANDROID + bool "Bootdev support for Android" + depends on X86 || ARM || SANDBOX + select ANDROID_AB + select ANDROID_BOOT_IMAGE + select CMD_BCB + select CMD_FASTBOOT + select PARTITION_TYPE_GUID + select PARTITION_UUIDS + help + Enables support for booting Android using bootstd. Android requires + multiple partitions (misc, boot, vbmeta, ...) in storage for booting. + + Note that only MMC bootdevs are supported at present. This is caused + by AVB being limited to MMC devices only. + config BOOTMETH_CROS bool "Bootdev support for Chromium OS" depends on X86 || ARM || SANDBOX diff --git a/boot/Makefile b/boot/Makefile index 84ccfeaecec4..75d1cd46fabf 100644 --- a/boot/Makefile +++ b/boot/Makefile @@ -66,3 +66,5 @@ obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE_REQUEST) += vbe_request.o obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE_SIMPLE) += vbe_simple.o obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE_SIMPLE_FW) += vbe_simple_fw.o obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE_SIMPLE_OS) += vbe_simple_os.o + +obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_ANDROID) += bootmeth_android.o diff --git a/boot/bootmeth_android.c b/boot/bootmeth_android.c new file mode 100644 index 000000000000..6e8d3e615db0 --- /dev/null +++ b/boot/bootmeth_android.c @@ -0,0 +1,553 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Bootmeth for Android + * + * Copyright (C) 2024 BayLibre, SAS + * Written by Mattijs Korpershoek mkorpershoek@baylibre.com + */ +#define LOG_CATEGORY UCLASS_BOOTSTD + +#include <android_ab.h> +#include <android_image.h> +#if CONFIG_IS_ENABLED(AVB_VERIFY) +#include <avb_verify.h> +#endif +#include <bcb.h> +#include <blk.h> +#include <bootflow.h> +#include <bootm.h> +#include <bootmeth.h> +#include <dm.h> +#include <image.h> +#include <malloc.h> +#include <mapmem.h> +#include <part.h> +#include "bootmeth_android.h" + +#define BCB_FIELD_COMMAND_SZ 32 +#define BCB_PART_NAME "misc" +#define BOOT_PART_NAME "boot" +#define VENDOR_BOOT_PART_NAME "vendor_boot" + +/** + * struct android_priv - Private data + * + * This is read from the disk and recorded for use when the full Android + * kernel must be loaded and booted + * + * @boot_mode: Requested boot mode (normal, recovery, bootloader) + * @slot: Nul-terminated partition slot suffix read from BCB ("a\0" or "b\0") + * @header_version: Android boot image header version + */ +struct android_priv { + enum android_boot_mode boot_mode; + char slot[2]; + u32 header_version; +}; + +static int android_check(struct udevice *dev, struct bootflow_iter *iter) +{ + /* This only works on mmc devices */ + if (bootflow_iter_check_mmc(iter)) + return log_msg_ret("mmc", -ENOTSUPP); + + /* + * This only works on whole devices, as multiple + * partitions are needed to boot Android + */ + if (iter->part != 0) + return log_msg_ret("mmc part", -ENOTSUPP); + + return 0; +} + +static int scan_boot_part(struct udevice *blk, struct android_priv *priv) +{ + struct blk_desc *desc = dev_get_uclass_plat(blk); + struct disk_partition partition; + char partname[PART_NAME_LEN]; + ulong num_blks, bufsz; + char *buf; + int ret; + + sprintf(partname, BOOT_PART_NAME "_%s", priv->slot); + ret = part_get_info_by_name(desc, partname, &partition); + if (ret < 0) + return log_msg_ret("part info", ret); + + num_blks = DIV_ROUND_UP(sizeof(struct andr_boot_img_hdr_v0), desc->blksz); + bufsz = num_blks * desc->blksz; + buf = malloc(bufsz); + if (!buf) + return log_msg_ret("buf", -ENOMEM); + + ret = blk_read(blk, partition.start, num_blks, buf); + if (ret != num_blks) { + free(buf); + return log_msg_ret("part read", -EIO); + } + + if (!is_android_boot_image_header(buf)) { + free(buf); + return log_msg_ret("header", -ENOENT); + } + + priv->header_version = ((struct andr_boot_img_hdr_v0 *)buf)->header_version; + free(buf); + + return 0; +} + +static int scan_vendor_boot_part(struct udevice *blk, struct android_priv *priv) +{ + struct blk_desc *desc = dev_get_uclass_plat(blk); + struct disk_partition partition; + char partname[PART_NAME_LEN]; + ulong num_blks, bufsz; + char *buf; + int ret; + + sprintf(partname, VENDOR_BOOT_PART_NAME "_%s", priv->slot); + ret = part_get_info_by_name(desc, partname, &partition); + if (ret < 0) + return log_msg_ret("part info", ret); + + num_blks = DIV_ROUND_UP(sizeof(struct andr_vnd_boot_img_hdr), desc->blksz); + bufsz = num_blks * desc->blksz; + buf = malloc(bufsz); + if (!buf) + return log_msg_ret("buf", -ENOMEM); + + ret = blk_read(blk, partition.start, num_blks, buf); + if (ret != num_blks) { + free(buf); + return log_msg_ret("part read", -EIO); + } + + if (!is_android_vendor_boot_image_header(buf)) { + free(buf); + return log_msg_ret("header", -ENOENT); + } + free(buf); + + return 0; +} + +static int android_read_slot_from_bcb(struct bootflow *bflow, bool decrement) +{ + struct blk_desc *desc = dev_get_uclass_plat(bflow->blk); + struct android_priv *priv = bflow->bootmeth_priv; + struct disk_partition misc; + char slot_suffix[3]; + int ret; + + ret = part_get_info_by_name(desc, BCB_PART_NAME, &misc); + if (ret < 0) + return log_msg_ret("part", ret); + + ret = ab_select_slot(desc, &misc, decrement); + if (ret < 0) + return log_msg_ret("slot", ret); + + priv->slot[0] = BOOT_SLOT_NAME(ret); + priv->slot[1] = '\0'; + + sprintf(slot_suffix, "_%s", priv->slot); + ret = bootflow_cmdline_set_arg(bflow, "androidboot.slot_suffix", + slot_suffix, false); + if (ret < 0) + return log_msg_ret("cmdl", ret); + + return 0; +} + +static int configure_serialno(struct bootflow *bflow) +{ + char *serialno = env_get("serial#"); + + if (!serialno) + return log_msg_ret("serial", -ENOENT); + + return bootflow_cmdline_set_arg(bflow, "androidboot.serialno", serialno, false); +} + +static int android_read_bootflow(struct udevice *dev, struct bootflow *bflow) +{ + struct blk_desc *desc = dev_get_uclass_plat(bflow->blk); + struct disk_partition misc; + struct android_priv *priv; + char command[BCB_FIELD_COMMAND_SZ]; + int ret; + + bflow->state = BOOTFLOWST_MEDIA; + + /* + * bcb_find_partition_and_load() will print errors to stdout + * if BCB_PART_NAME is not found. To avoid that, check if the + * partition exists first. + */ + ret = part_get_info_by_name(desc, BCB_PART_NAME, &misc); + if (ret < 0) + return log_msg_ret("part", ret); + + ret = bcb_find_partition_and_load("mmc", desc->devnum, BCB_PART_NAME); + if (ret < 0) + return log_msg_ret("bcb load", ret); + + ret = bcb_get(BCB_FIELD_COMMAND, command, sizeof(command)); + if (ret < 0) + return log_msg_ret("bcb read", ret); + + priv = malloc(sizeof(struct android_priv)); + if (!priv) + return log_msg_ret("buf", -ENOMEM); + + if (!strcmp("bootonce-bootloader", command)) { + priv->boot_mode = ANDROID_BOOT_MODE_BOOTLOADER; + bflow->os_name = strdup("Android (bootloader)"); + } else if (!strcmp("boot-fastboot", command)) { + priv->boot_mode = ANDROID_BOOT_MODE_RECOVERY; + bflow->os_name = strdup("Android (fastbootd)"); + } else if (!strcmp("boot-recovery", command)) { + priv->boot_mode = ANDROID_BOOT_MODE_RECOVERY; + bflow->os_name = strdup("Android (recovery)"); + } else { + priv->boot_mode = ANDROID_BOOT_MODE_NORMAL; + bflow->os_name = strdup("Android"); + } + if (!bflow->os_name) + return log_msg_ret("os", -ENOMEM); + + if (priv->boot_mode == ANDROID_BOOT_MODE_BOOTLOADER) { + /* Clear BCB */ + memset(command, 0, sizeof(command)); + ret = bcb_set(BCB_FIELD_COMMAND, command); + if (ret < 0) { + free(priv); + return log_msg_ret("bcb set", ret); + } + ret = bcb_store(); + if (ret < 0) { + free(priv); + return log_msg_ret("bcb store", ret); + } + + bflow->bootmeth_priv = priv; + bflow->state = BOOTFLOWST_READY; + return 0; + } + + bflow->bootmeth_priv = priv; + + /* For recovery and normal boot, we need to scan the partitions */ + ret = android_read_slot_from_bcb(bflow, false); + if (ret < 0) { + log_err("read slot: %d", ret); + goto free_priv; + } + + ret = scan_boot_part(bflow->blk, priv); + if (ret < 0) { + log_debug("scan boot failed: err=%d\n", ret); + goto free_priv; + } + + if (priv->header_version != 4) { + log_debug("only boot.img v4 is supported %u\n", priv->header_version); + ret = -EINVAL; + goto free_priv; + } + + ret = scan_vendor_boot_part(bflow->blk, priv); + if (ret < 0) { + log_debug("scan vendor_boot failed: err=%d\n", ret); + goto free_priv; + } + + /* Ignoring return code: setting serial number is not mandatory for booting */ + configure_serialno(bflow); + + if (priv->boot_mode == ANDROID_BOOT_MODE_NORMAL) { + ret = bootflow_cmdline_set_arg(bflow, "androidboot.force_normal_boot", + "1", false); + if (ret < 0) { + log_debug("normal_boot %d", ret); + goto free_priv; + } + } + + bflow->state = BOOTFLOWST_READY; + + return 0; + + free_priv: + free(priv); + bflow->bootmeth_priv = NULL; + return ret; +} + +static int android_read_file(struct udevice *dev, struct bootflow *bflow, + const char *file_path, ulong addr, ulong *sizep) +{ + /* + * Reading individual files is not supported since we only + * operate on whole mmc devices (because we require multiple partitions) + */ + return log_msg_ret("Unsupported", -ENOSYS); +} + +/** + * read_slotted_partition() - Read a partition by appending a slot suffix + * + * Most modern Android devices use Seamless Updates, where each partition + * is duplicated. For example, the boot partition has boot_a and boot_b. + * For more information, see: + * https://source.android.com/docs/core/ota/ab + * https://source.android.com/docs/core/ota/ab/ab_implement + * + * @blk: Block device to read + * @name: Partition name to read + * @slot: Nul-terminated slot suffixed to partition name ("a\0" or "b\0") + * @addr: Address where the partition content is loaded into + * 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 addr) +{ + struct disk_partition partition; + char partname[PART_NAME_LEN]; + int ret; + u32 n; + + /* Ensure name fits in partname it should be: <name>_<slot>\0 */ + if (strlen(name) > (PART_NAME_LEN - 2 - 1)) + return log_msg_ret("name too long", -EINVAL); + + sprintf(partname, "%s_%s", name, slot); + ret = part_get_info_by_name(desc, partname, &partition); + if (ret < 0) + return log_msg_ret("part", ret); + + n = blk_dread(desc, partition.start, partition.size, map_sysmem(addr, 0)); + if (n < partition.size) + return log_msg_ret("part read", -EIO); + + return 0; +} + +#if CONFIG_IS_ENABLED(AVB_VERIFY) +static int avb_append_commandline_arg(struct bootflow *bflow, char *arg) +{ + char *key = strsep(&arg, "="); + char *value = arg; + int ret; + + ret = bootflow_cmdline_set_arg(bflow, key, value, false); + if (ret < 0) + return log_msg_ret("avb cmdline", ret); + + return 0; +} + +static int avb_append_commandline(struct bootflow *bflow, char *cmdline) +{ + char *arg = strsep(&cmdline, " "); + int ret; + + while (arg) { + ret = avb_append_commandline_arg(bflow, arg); + if (ret < 0) + return ret; + + arg = strsep(&cmdline, " "); + } + + return 0; +} + +static int run_avb_verification(struct bootflow *bflow) +{ + struct blk_desc *desc = dev_get_uclass_plat(bflow->blk); + struct android_priv *priv = bflow->bootmeth_priv; + const char * const requested_partitions[] = {"boot", "vendor_boot"}; + struct AvbOps *avb_ops; + AvbSlotVerifyResult result; + AvbSlotVerifyData *out_data; + enum avb_boot_state boot_state; + char *extra_args; + char slot_suffix[3]; + bool unlocked = false; + int ret; + + avb_ops = avb_ops_alloc(desc->devnum); + if (!avb_ops) + return log_msg_ret("avb ops", -ENOMEM); + + sprintf(slot_suffix, "_%s", priv->slot); + + ret = avb_ops->read_is_device_unlocked(avb_ops, &unlocked); + if (ret != AVB_IO_RESULT_OK) + return log_msg_ret("avb lock", -EIO); + + result = avb_slot_verify(avb_ops, + requested_partitions, + slot_suffix, + unlocked, + AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, + &out_data); + + if (result != AVB_SLOT_VERIFY_RESULT_OK) { + printf("Verification failed, reason: %s\n", + str_avb_slot_error(result)); + avb_slot_verify_data_free(out_data); + return log_msg_ret("avb verify", -EIO); + } + + if (unlocked) + boot_state = AVB_ORANGE; + else + boot_state = AVB_GREEN; + + extra_args = avb_set_state(avb_ops, boot_state); + if (extra_args) { + /* extra_args will be modified after this. This is fine */ + ret = avb_append_commandline_arg(bflow, extra_args); + if (ret < 0) + goto free_out_data; + } + + ret = avb_append_commandline(bflow, out_data->cmdline); + if (ret < 0) + goto free_out_data; + + return 0; + + free_out_data: + if (out_data) + avb_slot_verify_data_free(out_data); + + return log_msg_ret("avb cmdline", ret); +} +#else +static int run_avb_verification(struct bootflow *bflow) +{ + int ret; + + /* When AVB is unsupported, pass ORANGE state */ + ret = bootflow_cmdline_set_arg(bflow, + "androidboot.verifiedbootstate", + "orange", false); + if (ret < 0) + return log_msg_ret("avb cmdline", ret); + + return 0; +} +#endif /* AVB_VERIFY */ + +static int boot_android_normal(struct bootflow *bflow) +{ + struct blk_desc *desc = dev_get_uclass_plat(bflow->blk); + struct android_priv *priv = bflow->bootmeth_priv; + int ret; + ulong loadaddr = env_get_hex("loadaddr", 0); + ulong vloadaddr = env_get_hex("vendor_boot_comp_addr_r", 0); + + ret = run_avb_verification(bflow); + if (ret < 0) + return log_msg_ret("avb", ret); + + /* Read slot once more to decrement counter from BCB */ + ret = android_read_slot_from_bcb(bflow, true); + if (ret < 0) + return log_msg_ret("read slot", ret); + + ret = read_slotted_partition(desc, "boot", priv->slot, loadaddr); + if (ret < 0) + return log_msg_ret("read boot", ret); + + ret = read_slotted_partition(desc, "vendor_boot", priv->slot, vloadaddr); + if (ret < 0) + return log_msg_ret("read vendor_boot", ret); + + set_abootimg_addr(loadaddr); + set_avendor_bootimg_addr(vloadaddr); + + ret = bootm_boot_start(loadaddr, bflow->cmdline); + + return log_msg_ret("boot", ret); +} + +static int boot_android_recovery(struct bootflow *bflow) +{ + int ret; + + ret = boot_android_normal(bflow); + + return log_msg_ret("boot", ret); +} + +static int boot_android_bootloader(struct bootflow *bflow) +{ + int ret; + + ret = run_command("fastboot usb 0", 0); + do_reset(NULL, 0, 0, NULL); + + return log_msg_ret("boot", ret); +} + +static int android_boot(struct udevice *dev, struct bootflow *bflow) +{ + struct android_priv *priv = bflow->bootmeth_priv; + int ret; + + switch (priv->boot_mode) { + case ANDROID_BOOT_MODE_NORMAL: + ret = boot_android_normal(bflow); + break; + case ANDROID_BOOT_MODE_RECOVERY: + ret = boot_android_recovery(bflow); + break; + case ANDROID_BOOT_MODE_BOOTLOADER: + ret = boot_android_bootloader(bflow); + break; + default: + printf("ANDROID: Unknown boot mode %d. Running fastboot...\n", + priv->boot_mode); + boot_android_bootloader(bflow); + /* Tell we failed to boot since boot mode is unknown */ + ret = -EFAULT; + } + + return ret; +} + +static int android_bootmeth_bind(struct udevice *dev) +{ + struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev); + + plat->desc = "Android boot"; + plat->flags = BOOTMETHF_ANY_PART; + + return 0; +} + +static struct bootmeth_ops android_bootmeth_ops = { + .check = android_check, + .read_bootflow = android_read_bootflow, + .read_file = android_read_file, + .boot = android_boot, +}; + +static const struct udevice_id android_bootmeth_ids[] = { + { .compatible = "u-boot,android" }, + { } +}; + +U_BOOT_DRIVER(bootmeth_android) = { + .name = "bootmeth_android", + .id = UCLASS_BOOTMETH, + .of_match = android_bootmeth_ids, + .ops = &android_bootmeth_ops, + .bind = android_bootmeth_bind, +}; diff --git a/boot/bootmeth_android.h b/boot/bootmeth_android.h new file mode 100644 index 000000000000..a57fad9f0113 --- /dev/null +++ b/boot/bootmeth_android.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Bootmethod for Android + * + * Copyright (C) 2024 BayLibre, SAS + * Written by Mattijs Korpershoek mkorpershoek@baylibre.com + */ + +enum android_boot_mode { + ANDROID_BOOT_MODE_NORMAL = 0, + + /* + * Android "recovery" is a special boot mode that uses another ramdisk. + * It can be used to "factory reset" a board or to flash logical partitions + * It operates in 2 modes: adb or fastbootd + * To enter recovery from Android, we can do: + * $ adb reboot recovery + * $ adb reboot fastboot + */ + ANDROID_BOOT_MODE_RECOVERY, + + /* + * Android "bootloader" is for accessing/reflashing physical partitions + * Typically, this will launch a fastboot process in U-Boot. + * To enter "bootloader" from Android, we can do: + * $ adb reboot bootloader + */ + ANDROID_BOOT_MODE_BOOTLOADER, +}; diff --git a/doc/develop/bootstd.rst b/doc/develop/bootstd.rst index a07a72581e7a..709fa9e64ff3 100644 --- a/doc/develop/bootstd.rst +++ b/doc/develop/bootstd.rst @@ -95,6 +95,7 @@ bootflows.
Note: it is possible to have a bootmeth that uses a partition or a whole device directly, but it is more common to use a filesystem. +For example, the Android bootmeth uses a whole device.
Note that some bootmeths are 'global', meaning that they select the bootdev themselves. Examples include VBE and EFI boot manager. In this case, they @@ -277,6 +278,9 @@ script_offset_f script_size_f Size of the script to load, e.g. 0x2000
+vendor_boot_comp_addr_r + Address to which to load the vendor_boot Android image, e.g. 0xe0000000 + Some variables are set by script bootmeth:
devtype @@ -418,6 +422,7 @@ Bootmeth drivers are provided for: - EFI boot using bootefi from disk - VBE - EFI boot using boot manager + - Android bootflow (boot image v4)
Command interface @@ -786,6 +791,7 @@ To do Some things that need to be done to completely replace the distro-boot scripts:
- implement extensions (devicetree overlays with add-on boards) +- implement legacy (boot image v2) android boot flow
Other ideas:

On Thu 13 Jun 2024 at 16:18, Mattijs Korpershoek mkorpershoek@baylibre.com wrote:
Android boot flow is a bit different than a regular Linux distro. Android relies on multiple partitions in order to boot.
A typical boot flow would be:
- Parse the Bootloader Control Block (BCB, misc partition)
- If BCB requested bootonce-bootloader, start fastboot and wait.
- If BCB requested recovery or normal android, run the following:
3.a. Get slot (A/B) from BCB 3.b. Run AVB (Android Verified Boot) on boot partitions 3.c. Load boot and vendor_boot partitions 3.d. Load device-tree, ramdisk and boot
The AOSP documentation has more details at [1], [2], [3]
This has been implemented via complex boot scripts such as [4]. However, these boot script are neither very maintainable nor generic. Moreover, DISTRO_DEFAULTS is being deprecated [5].
Add a generic Android bootflow implementation for bootstd. For this initial version, only boot image v4 is supported.
[1] https://source.android.com/docs/core/architecture/bootloader [2] https://source.android.com/docs/core/architecture/partitions [3] https://source.android.com/docs/core/architecture/partitions/generic-boot [4] https://source.denx.de/u-boot/u-boot/-/blob/master/include/configs/meson64_a... [5] https://lore.kernel.org/r/all/20230914165615.1058529-17-sjg@chromium.org/
Reviewed-by: Simon Glass sjg@chromium.org Signed-off-by: Mattijs Korpershoek mkorpershoek@baylibre.com
MAINTAINERS | 7 + boot/Kconfig | 16 ++ boot/Makefile | 2 + boot/bootmeth_android.c | 553 ++++++++++++++++++++++++++++++++++++++++++++++++ boot/bootmeth_android.h | 29 +++ doc/develop/bootstd.rst | 6 + 6 files changed, 613 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS index 66783d636e3d..6d2b87720565 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -939,6 +939,13 @@ F: include/bootstd.h F: net/eth_bootdevice.c F: test/boot/
+BOOTMETH_ANDROID +M: Mattijs Korpershoek mkorpershoek@baylibre.com +S: Maintained +T: git https://source.denx.de/u-boot/custodians/u-boot-dfu.git +F: boot/bootmeth_android.c +F: boot/bootmeth_android.h
BTRFS M: Marek Beh.n kabel@kernel.org R: Qu Wenruo wqu@suse.com diff --git a/boot/Kconfig b/boot/Kconfig index 6f3096c15a6f..88266c8d2ed3 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -494,6 +494,22 @@ config BOOTMETH_GLOBAL EFI bootmgr, since they take full control over which bootdevs are selected to boot.
+config BOOTMETH_ANDROID
- bool "Bootdev support for Android"
- depends on X86 || ARM || SANDBOX
- select ANDROID_AB
- select ANDROID_BOOT_IMAGE
- select CMD_BCB
- select CMD_FASTBOOT
- select PARTITION_TYPE_GUID
- select PARTITION_UUIDS
- help
Enables support for booting Android using bootstd. Android requires
multiple partitions (misc, boot, vbmeta, ...) in storage for booting.
Note that only MMC bootdevs are supported at present. This is caused
by AVB being limited to MMC devices only.
config BOOTMETH_CROS bool "Bootdev support for Chromium OS" depends on X86 || ARM || SANDBOX diff --git a/boot/Makefile b/boot/Makefile index 84ccfeaecec4..75d1cd46fabf 100644 --- a/boot/Makefile +++ b/boot/Makefile @@ -66,3 +66,5 @@ obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE_REQUEST) += vbe_request.o obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE_SIMPLE) += vbe_simple.o obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE_SIMPLE_FW) += vbe_simple_fw.o obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE_SIMPLE_OS) += vbe_simple_os.o
+obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_ANDROID) += bootmeth_android.o diff --git a/boot/bootmeth_android.c b/boot/bootmeth_android.c new file mode 100644 index 000000000000..6e8d3e615db0 --- /dev/null +++ b/boot/bootmeth_android.c @@ -0,0 +1,553 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Bootmeth for Android
- Copyright (C) 2024 BayLibre, SAS
- Written by Mattijs Korpershoek mkorpershoek@baylibre.com
- */
+#define LOG_CATEGORY UCLASS_BOOTSTD
+#include <android_ab.h> +#include <android_image.h> +#if CONFIG_IS_ENABLED(AVB_VERIFY) +#include <avb_verify.h> +#endif +#include <bcb.h> +#include <blk.h> +#include <bootflow.h> +#include <bootm.h> +#include <bootmeth.h> +#include <dm.h> +#include <image.h> +#include <malloc.h> +#include <mapmem.h> +#include <part.h> +#include "bootmeth_android.h"
+#define BCB_FIELD_COMMAND_SZ 32 +#define BCB_PART_NAME "misc" +#define BOOT_PART_NAME "boot" +#define VENDOR_BOOT_PART_NAME "vendor_boot"
+/**
- struct android_priv - Private data
- This is read from the disk and recorded for use when the full Android
- kernel must be loaded and booted
- @boot_mode: Requested boot mode (normal, recovery, bootloader)
- @slot: Nul-terminated partition slot suffix read from BCB ("a\0" or "b\0")
- @header_version: Android boot image header version
- */
+struct android_priv {
- enum android_boot_mode boot_mode;
- char slot[2];
- u32 header_version;
+};
+static int android_check(struct udevice *dev, struct bootflow_iter *iter) +{
- /* This only works on mmc devices */
- if (bootflow_iter_check_mmc(iter))
return log_msg_ret("mmc", -ENOTSUPP);
- /*
* This only works on whole devices, as multiple
* partitions are needed to boot Android
*/
- if (iter->part != 0)
return log_msg_ret("mmc part", -ENOTSUPP);
- return 0;
+}
+static int scan_boot_part(struct udevice *blk, struct android_priv *priv) +{
- struct blk_desc *desc = dev_get_uclass_plat(blk);
- struct disk_partition partition;
- char partname[PART_NAME_LEN];
- ulong num_blks, bufsz;
- char *buf;
- int ret;
- sprintf(partname, BOOT_PART_NAME "_%s", priv->slot);
- ret = part_get_info_by_name(desc, partname, &partition);
- if (ret < 0)
return log_msg_ret("part info", ret);
- num_blks = DIV_ROUND_UP(sizeof(struct andr_boot_img_hdr_v0), desc->blksz);
- bufsz = num_blks * desc->blksz;
- buf = malloc(bufsz);
- if (!buf)
return log_msg_ret("buf", -ENOMEM);
- ret = blk_read(blk, partition.start, num_blks, buf);
- if (ret != num_blks) {
free(buf);
return log_msg_ret("part read", -EIO);
- }
- if (!is_android_boot_image_header(buf)) {
free(buf);
return log_msg_ret("header", -ENOENT);
- }
- priv->header_version = ((struct andr_boot_img_hdr_v0 *)buf)->header_version;
- free(buf);
- return 0;
+}
+static int scan_vendor_boot_part(struct udevice *blk, struct android_priv *priv) +{
- struct blk_desc *desc = dev_get_uclass_plat(blk);
- struct disk_partition partition;
- char partname[PART_NAME_LEN];
- ulong num_blks, bufsz;
- char *buf;
- int ret;
- sprintf(partname, VENDOR_BOOT_PART_NAME "_%s", priv->slot);
- ret = part_get_info_by_name(desc, partname, &partition);
- if (ret < 0)
return log_msg_ret("part info", ret);
- num_blks = DIV_ROUND_UP(sizeof(struct andr_vnd_boot_img_hdr), desc->blksz);
- bufsz = num_blks * desc->blksz;
- buf = malloc(bufsz);
- if (!buf)
return log_msg_ret("buf", -ENOMEM);
- ret = blk_read(blk, partition.start, num_blks, buf);
- if (ret != num_blks) {
free(buf);
return log_msg_ret("part read", -EIO);
- }
- if (!is_android_vendor_boot_image_header(buf)) {
free(buf);
return log_msg_ret("header", -ENOENT);
- }
- free(buf);
- return 0;
+}
+static int android_read_slot_from_bcb(struct bootflow *bflow, bool decrement) +{
- struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
- struct android_priv *priv = bflow->bootmeth_priv;
- struct disk_partition misc;
- char slot_suffix[3];
- int ret;
- ret = part_get_info_by_name(desc, BCB_PART_NAME, &misc);
- if (ret < 0)
return log_msg_ret("part", ret);
- ret = ab_select_slot(desc, &misc, decrement);
- if (ret < 0)
return log_msg_ret("slot", ret);
- priv->slot[0] = BOOT_SLOT_NAME(ret);
- priv->slot[1] = '\0';
- sprintf(slot_suffix, "_%s", priv->slot);
- ret = bootflow_cmdline_set_arg(bflow, "androidboot.slot_suffix",
slot_suffix, false);
- if (ret < 0)
return log_msg_ret("cmdl", ret);
- return 0;
+}
+static int configure_serialno(struct bootflow *bflow) +{
- char *serialno = env_get("serial#");
- if (!serialno)
return log_msg_ret("serial", -ENOENT);
- return bootflow_cmdline_set_arg(bflow, "androidboot.serialno", serialno, false);
+}
+static int android_read_bootflow(struct udevice *dev, struct bootflow *bflow) +{
- struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
- struct disk_partition misc;
- struct android_priv *priv;
- char command[BCB_FIELD_COMMAND_SZ];
- int ret;
- bflow->state = BOOTFLOWST_MEDIA;
- /*
* bcb_find_partition_and_load() will print errors to stdout
* if BCB_PART_NAME is not found. To avoid that, check if the
* partition exists first.
*/
- ret = part_get_info_by_name(desc, BCB_PART_NAME, &misc);
- if (ret < 0)
return log_msg_ret("part", ret);
- ret = bcb_find_partition_and_load("mmc", desc->devnum, BCB_PART_NAME);
- if (ret < 0)
return log_msg_ret("bcb load", ret);
- ret = bcb_get(BCB_FIELD_COMMAND, command, sizeof(command));
- if (ret < 0)
return log_msg_ret("bcb read", ret);
- priv = malloc(sizeof(struct android_priv));
- if (!priv)
return log_msg_ret("buf", -ENOMEM);
- if (!strcmp("bootonce-bootloader", command)) {
priv->boot_mode = ANDROID_BOOT_MODE_BOOTLOADER;
bflow->os_name = strdup("Android (bootloader)");
- } else if (!strcmp("boot-fastboot", command)) {
priv->boot_mode = ANDROID_BOOT_MODE_RECOVERY;
bflow->os_name = strdup("Android (fastbootd)");
- } else if (!strcmp("boot-recovery", command)) {
priv->boot_mode = ANDROID_BOOT_MODE_RECOVERY;
bflow->os_name = strdup("Android (recovery)");
- } else {
priv->boot_mode = ANDROID_BOOT_MODE_NORMAL;
bflow->os_name = strdup("Android");
- }
- if (!bflow->os_name)
return log_msg_ret("os", -ENOMEM);
- if (priv->boot_mode == ANDROID_BOOT_MODE_BOOTLOADER) {
/* Clear BCB */
memset(command, 0, sizeof(command));
ret = bcb_set(BCB_FIELD_COMMAND, command);
if (ret < 0) {
free(priv);
return log_msg_ret("bcb set", ret);
}
ret = bcb_store();
if (ret < 0) {
free(priv);
return log_msg_ret("bcb store", ret);
}
bflow->bootmeth_priv = priv;
bflow->state = BOOTFLOWST_READY;
return 0;
- }
- bflow->bootmeth_priv = priv;
- /* For recovery and normal boot, we need to scan the partitions */
- ret = android_read_slot_from_bcb(bflow, false);
- if (ret < 0) {
log_err("read slot: %d", ret);
goto free_priv;
- }
- ret = scan_boot_part(bflow->blk, priv);
- if (ret < 0) {
log_debug("scan boot failed: err=%d\n", ret);
goto free_priv;
- }
- if (priv->header_version != 4) {
log_debug("only boot.img v4 is supported %u\n", priv->header_version);
ret = -EINVAL;
goto free_priv;
- }
- ret = scan_vendor_boot_part(bflow->blk, priv);
- if (ret < 0) {
log_debug("scan vendor_boot failed: err=%d\n", ret);
goto free_priv;
- }
- /* Ignoring return code: setting serial number is not mandatory for booting */
- configure_serialno(bflow);
- if (priv->boot_mode == ANDROID_BOOT_MODE_NORMAL) {
ret = bootflow_cmdline_set_arg(bflow, "androidboot.force_normal_boot",
"1", false);
if (ret < 0) {
log_debug("normal_boot %d", ret);
goto free_priv;
}
- }
- bflow->state = BOOTFLOWST_READY;
- return 0;
- free_priv:
- free(priv);
- bflow->bootmeth_priv = NULL;
- return ret;
+}
+static int android_read_file(struct udevice *dev, struct bootflow *bflow,
const char *file_path, ulong addr, ulong *sizep)
+{
- /*
* Reading individual files is not supported since we only
* operate on whole mmc devices (because we require multiple partitions)
*/
- return log_msg_ret("Unsupported", -ENOSYS);
+}
+/**
- read_slotted_partition() - Read a partition by appending a slot suffix
- Most modern Android devices use Seamless Updates, where each partition
- is duplicated. For example, the boot partition has boot_a and boot_b.
- For more information, see:
- @blk: Block device to read
- @name: Partition name to read
- @slot: Nul-terminated slot suffixed to partition name ("a\0" or "b\0")
- @addr: Address where the partition content is loaded into
- 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 addr)
+{
- struct disk_partition partition;
- char partname[PART_NAME_LEN];
- int ret;
- u32 n;
- /* Ensure name fits in partname it should be: <name>_<slot>\0 */
- if (strlen(name) > (PART_NAME_LEN - 2 - 1))
return log_msg_ret("name too long", -EINVAL);
- sprintf(partname, "%s_%s", name, slot);
- ret = part_get_info_by_name(desc, partname, &partition);
- if (ret < 0)
return log_msg_ret("part", ret);
- n = blk_dread(desc, partition.start, partition.size, map_sysmem(addr, 0));
- if (n < partition.size)
return log_msg_ret("part read", -EIO);
- return 0;
+}
+#if CONFIG_IS_ENABLED(AVB_VERIFY) +static int avb_append_commandline_arg(struct bootflow *bflow, char *arg) +{
- char *key = strsep(&arg, "=");
- char *value = arg;
- int ret;
- ret = bootflow_cmdline_set_arg(bflow, key, value, false);
- if (ret < 0)
return log_msg_ret("avb cmdline", ret);
- return 0;
+}
+static int avb_append_commandline(struct bootflow *bflow, char *cmdline) +{
- char *arg = strsep(&cmdline, " ");
- int ret;
- while (arg) {
ret = avb_append_commandline_arg(bflow, arg);
if (ret < 0)
return ret;
arg = strsep(&cmdline, " ");
- }
- return 0;
+}
+static int run_avb_verification(struct bootflow *bflow) +{
- struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
- struct android_priv *priv = bflow->bootmeth_priv;
- const char * const requested_partitions[] = {"boot", "vendor_boot"};
- struct AvbOps *avb_ops;
- AvbSlotVerifyResult result;
- AvbSlotVerifyData *out_data;
- enum avb_boot_state boot_state;
- char *extra_args;
- char slot_suffix[3];
- bool unlocked = false;
- int ret;
- avb_ops = avb_ops_alloc(desc->devnum);
- if (!avb_ops)
return log_msg_ret("avb ops", -ENOMEM);
- sprintf(slot_suffix, "_%s", priv->slot);
- ret = avb_ops->read_is_device_unlocked(avb_ops, &unlocked);
- if (ret != AVB_IO_RESULT_OK)
return log_msg_ret("avb lock", -EIO);
- result = avb_slot_verify(avb_ops,
requested_partitions,
slot_suffix,
unlocked,
AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
&out_data);
- if (result != AVB_SLOT_VERIFY_RESULT_OK) {
printf("Verification failed, reason: %s\n",
str_avb_slot_error(result));
avb_slot_verify_data_free(out_data);
return log_msg_ret("avb verify", -EIO);
- }
- if (unlocked)
boot_state = AVB_ORANGE;
- else
boot_state = AVB_GREEN;
- extra_args = avb_set_state(avb_ops, boot_state);
- if (extra_args) {
/* extra_args will be modified after this. This is fine */
ret = avb_append_commandline_arg(bflow, extra_args);
if (ret < 0)
goto free_out_data;
- }
- ret = avb_append_commandline(bflow, out_data->cmdline);
- if (ret < 0)
goto free_out_data;
- return 0;
- free_out_data:
- if (out_data)
avb_slot_verify_data_free(out_data);
- return log_msg_ret("avb cmdline", ret);
+} +#else +static int run_avb_verification(struct bootflow *bflow) +{
- int ret;
- /* When AVB is unsupported, pass ORANGE state */
- ret = bootflow_cmdline_set_arg(bflow,
"androidboot.verifiedbootstate",
"orange", false);
- if (ret < 0)
return log_msg_ret("avb cmdline", ret);
- return 0;
+} +#endif /* AVB_VERIFY */
+static int boot_android_normal(struct bootflow *bflow) +{
- struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
- struct android_priv *priv = bflow->bootmeth_priv;
- int ret;
- ulong loadaddr = env_get_hex("loadaddr", 0);
- ulong vloadaddr = env_get_hex("vendor_boot_comp_addr_r", 0);
- ret = run_avb_verification(bflow);
- if (ret < 0)
return log_msg_ret("avb", ret);
- /* Read slot once more to decrement counter from BCB */
- ret = android_read_slot_from_bcb(bflow, true);
- if (ret < 0)
return log_msg_ret("read slot", ret);
- ret = read_slotted_partition(desc, "boot", priv->slot, loadaddr);
- if (ret < 0)
return log_msg_ret("read boot", ret);
- ret = read_slotted_partition(desc, "vendor_boot", priv->slot, vloadaddr);
- if (ret < 0)
return log_msg_ret("read vendor_boot", ret);
- set_abootimg_addr(loadaddr);
- set_avendor_bootimg_addr(vloadaddr);
- ret = bootm_boot_start(loadaddr, bflow->cmdline);
- return log_msg_ret("boot", ret);
+}
+static int boot_android_recovery(struct bootflow *bflow) +{
- int ret;
- ret = boot_android_normal(bflow);
- return log_msg_ret("boot", ret);
+}
+static int boot_android_bootloader(struct bootflow *bflow) +{
- int ret;
- ret = run_command("fastboot usb 0", 0);
- do_reset(NULL, 0, 0, NULL);
- return log_msg_ret("boot", ret);
+}
+static int android_boot(struct udevice *dev, struct bootflow *bflow) +{
- struct android_priv *priv = bflow->bootmeth_priv;
- int ret;
- switch (priv->boot_mode) {
- case ANDROID_BOOT_MODE_NORMAL:
ret = boot_android_normal(bflow);
break;
- case ANDROID_BOOT_MODE_RECOVERY:
ret = boot_android_recovery(bflow);
break;
- case ANDROID_BOOT_MODE_BOOTLOADER:
ret = boot_android_bootloader(bflow);
break;
- default:
printf("ANDROID: Unknown boot mode %d. Running fastboot...\n",
priv->boot_mode);
boot_android_bootloader(bflow);
/* Tell we failed to boot since boot mode is unknown */
ret = -EFAULT;
- }
- return ret;
+}
+static int android_bootmeth_bind(struct udevice *dev) +{
- struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
- plat->desc = "Android boot";
- plat->flags = BOOTMETHF_ANY_PART;
- return 0;
+}
+static struct bootmeth_ops android_bootmeth_ops = {
- .check = android_check,
- .read_bootflow = android_read_bootflow,
- .read_file = android_read_file,
- .boot = android_boot,
+};
+static const struct udevice_id android_bootmeth_ids[] = {
- { .compatible = "u-boot,android" },
- { }
+};
+U_BOOT_DRIVER(bootmeth_android) = {
- .name = "bootmeth_android",
- .id = UCLASS_BOOTMETH,
- .of_match = android_bootmeth_ids,
- .ops = &android_bootmeth_ops,
- .bind = android_bootmeth_bind,
+}; diff --git a/boot/bootmeth_android.h b/boot/bootmeth_android.h new file mode 100644 index 000000000000..a57fad9f0113 --- /dev/null +++ b/boot/bootmeth_android.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- Bootmethod for Android
- Copyright (C) 2024 BayLibre, SAS
- Written by Mattijs Korpershoek mkorpershoek@baylibre.com
- */
+enum android_boot_mode {
- ANDROID_BOOT_MODE_NORMAL = 0,
- /*
* Android "recovery" is a special boot mode that uses another ramdisk.
* It can be used to "factory reset" a board or to flash logical partitions
* It operates in 2 modes: adb or fastbootd
* To enter recovery from Android, we can do:
* $ adb reboot recovery
* $ adb reboot fastboot
*/
- ANDROID_BOOT_MODE_RECOVERY,
- /*
* Android "bootloader" is for accessing/reflashing physical partitions
* Typically, this will launch a fastboot process in U-Boot.
* To enter "bootloader" from Android, we can do:
* $ adb reboot bootloader
*/
- ANDROID_BOOT_MODE_BOOTLOADER,
+}; diff --git a/doc/develop/bootstd.rst b/doc/develop/bootstd.rst index a07a72581e7a..709fa9e64ff3 100644 --- a/doc/develop/bootstd.rst +++ b/doc/develop/bootstd.rst @@ -95,6 +95,7 @@ bootflows.
Note: it is possible to have a bootmeth that uses a partition or a whole device directly, but it is more common to use a filesystem. +For example, the Android bootmeth uses a whole device.
Note that some bootmeths are 'global', meaning that they select the bootdev themselves. Examples include VBE and EFI boot manager. In this case, they @@ -277,6 +278,9 @@ script_offset_f script_size_f Size of the script to load, e.g. 0x2000
+vendor_boot_comp_addr_r
- Address to which to load the vendor_boot Android image, e.g. 0xe0000000
Some variables are set by script bootmeth:
devtype @@ -418,6 +422,7 @@ Bootmeth drivers are provided for: - EFI boot using bootefi from disk - VBE - EFI boot using boot manager
- Android bootflow (boot image v4)
Command interface @@ -786,6 +791,7 @@ To do Some things that need to be done to completely replace the distro-boot scripts:
- implement extensions (devicetree overlays with add-on boards)
+- implement legacy (boot image v2) android boot flow
Other ideas:
-- 2.45.2
Reviewed-by: Julien Masson jmasson@baylibre.com

Add a unit test for testing the Android bootmethod.
This requires another mmc image (mmc7) to contain the following partitions: - misc: contains the Bootloader Control Block (BCB) - boot_a: contains a fake generic kernel image - vendor_boot_a: contains a fake vendor_boot image
Also add BOOTMETH_ANDROID as a dependency on sandbox so that we can test this with:
$ ./test/py/test.py --bd sandbox --build -k test_abootimg # build bootv4.img $ ./test/py/test.py --bd sandbox --build -k test_ut # build the mmc7.img $ ./test/py/test.py --bd sandbox --build -k bootflow_android
Reviewed-by: Simon Glass sjg@chromium.org Signed-off-by: Mattijs Korpershoek mkorpershoek@baylibre.com --- arch/sandbox/dts/test.dts | 8 +++++ configs/sandbox_defconfig | 2 +- test/boot/bootflow.c | 65 ++++++++++++++++++++++++++++++++++++++-- test/py/tests/test_ut.py | 76 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 147 insertions(+), 4 deletions(-)
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index a012f5c4c9ba..5fb5eac862ec 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -43,6 +43,7 @@ mmc4 = "/mmc4"; mmc5 = "/mmc5"; mmc6 = "/mmc6"; + mmc7 = "/mmc7"; pci0 = &pci0; pci1 = &pci1; pci2 = &pci2; @@ -1129,6 +1130,13 @@ filename = "mmc6.img"; };
+ /* This is used for Android tests */ + mmc7 { + status = "disabled"; + compatible = "sandbox,mmc"; + filename = "mmc7.img"; + }; + pch { compatible = "sandbox,pch"; }; diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 93b52f2de5cf..bc4398f101a7 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -15,6 +15,7 @@ CONFIG_FIT=y CONFIG_FIT_RSASSA_PSS=y CONFIG_FIT_CIPHER=y CONFIG_FIT_VERBOSE=y +CONFIG_BOOTMETH_ANDROID=y CONFIG_LEGACY_IMAGE_FORMAT=y CONFIG_MEASURED_BOOT=y CONFIG_BOOTSTAGE=y @@ -40,7 +41,6 @@ CONFIG_LOG_MAX_LEVEL=9 CONFIG_LOG_DEFAULT_LEVEL=6 CONFIG_DISPLAY_BOARDINFO_LATE=y CONFIG_STACKPROTECTOR=y -CONFIG_ANDROID_AB=y CONFIG_CMD_CPU=y CONFIG_CMD_LICENSE=y CONFIG_CMD_SMBIOS=y diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index 4511cfa7f9bf..934c4dcbad2b 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -27,6 +27,7 @@
DECLARE_GLOBAL_DATA_PTR;
+extern U_BOOT_DRIVER(bootmeth_android); extern U_BOOT_DRIVER(bootmeth_cros); extern U_BOOT_DRIVER(bootmeth_2script);
@@ -518,12 +519,12 @@ BOOTSTD_TEST(bootflow_cmd_boot, UT_TESTF_DM | UT_TESTF_SCAN_FDT); * @uts: Unit test state * @mmc_dev: MMC device to use, e.g. "mmc4". Note that this must remain valid * in the caller until - * @bind_cros: true to bind the ChromiumOS bootmeth + * @bind_cros: true to bind the ChromiumOS and Android bootmeths * @old_orderp: Returns the original bootdev order, which must be restored * Returns 0 on success, -ve on failure */ static int prep_mmc_bootdev(struct unit_test_state *uts, const char *mmc_dev, - bool bind_cros, const char ***old_orderp) + bool bind_cros_android, const char ***old_orderp) { static const char *order[] = {"mmc2", "mmc1", NULL, NULL}; struct udevice *dev, *bootstd; @@ -545,12 +546,19 @@ static int prep_mmc_bootdev(struct unit_test_state *uts, const char *mmc_dev, "bootmeth_script", 0, ofnode_null(), &dev));
/* Enable the cros bootmeth if needed */ - if (IS_ENABLED(CONFIG_BOOTMETH_CROS) && bind_cros) { + if (IS_ENABLED(CONFIG_BOOTMETH_CROS) && bind_cros_android) { ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd)); ut_assertok(device_bind(bootstd, DM_DRIVER_REF(bootmeth_cros), "cros", 0, ofnode_null(), &dev)); }
+ /* Enable the android bootmeths if needed */ + if (IS_ENABLED(CONFIG_BOOTMETH_ANDROID) && bind_cros_android) { + ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd)); + ut_assertok(device_bind(bootstd, DM_DRIVER_REF(bootmeth_android), + "android", 0, ofnode_null(), &dev)); + } + /* Change the order to include the device */ std = dev_get_priv(bootstd); old_order = std->bootdev_order; @@ -589,6 +597,37 @@ static int scan_mmc_bootdev(struct unit_test_state *uts, const char *mmc_dev, return 0; }
+/** + * scan_mmc_android_bootdev() - Set up an mmc bootdev so we can access other + * distros. Android bootflow might print "ANDROID:*" while scanning + * + * @uts: Unit test state + * @mmc_dev: MMC device to use, e.g. "mmc4" + * Returns 0 on success, -ve on failure + */ +static int scan_mmc_android_bootdev(struct unit_test_state *uts, const char *mmc_dev) +{ + struct bootstd_priv *std; + struct udevice *bootstd; + const char **old_order; + + ut_assertok(prep_mmc_bootdev(uts, mmc_dev, true, &old_order)); + + console_record_reset_enable(); + ut_assertok(run_command("bootflow scan", 0)); + /* Android bootflow might print one or two 'ANDROID:*' logs */ + ut_check_skipline(uts); + ut_check_skipline(uts); + ut_assert_console_end(); + + /* Restore the order used by the device tree */ + ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd)); + std = dev_get_priv(bootstd); + std->bootdev_order = old_order; + + return 0; +} + /** * scan_mmc4_bootdev() - Set up the mmc4 bootdev so we can access a fake Armbian * @@ -1160,3 +1199,23 @@ static int bootflow_cros(struct unit_test_state *uts) return 0; } BOOTSTD_TEST(bootflow_cros, 0); + +/* Test Android bootmeth */ +static int bootflow_android(struct unit_test_state *uts) +{ + ut_assertok(scan_mmc_android_bootdev(uts, "mmc7")); + ut_assertok(run_command("bootflow list", 0)); + + ut_assert_nextlinen("Showing all"); + ut_assert_nextlinen("Seq"); + ut_assert_nextlinen("---"); + ut_assert_nextlinen(" 0 extlinux"); + ut_assert_nextlinen(" 1 android ready mmc 0 mmc7.bootdev.whole "); + ut_assert_nextlinen("---"); + ut_assert_skip_to_line("(2 bootflows, 2 valid)"); + + ut_assert_console_end(); + + return 0; +} +BOOTSTD_TEST(bootflow_android, 0); diff --git a/test/py/tests/test_ut.py b/test/py/tests/test_ut.py index c169c835e38a..39e1abe02a68 100644 --- a/test/py/tests/test_ut.py +++ b/test/py/tests/test_ut.py @@ -423,6 +423,81 @@ def setup_cros_image(cons):
return fname
+def setup_android_image(cons): + """Create a 20MB disk image with Android partitions""" + Partition = collections.namedtuple('part', 'start,size,name') + parts = {} + disk_data = None + + def set_part_data(partnum, data): + """Set the contents of a disk partition + + This updates disk_data by putting data in the right place + + Args: + partnum (int): Partition number to set + data (bytes): Data for that partition + """ + nonlocal disk_data + + start = parts[partnum].start * sect_size + disk_data = disk_data[:start] + data + disk_data[start + len(data):] + + mmc_dev = 7 + fname = os.path.join(cons.config.source_dir, f'mmc{mmc_dev}.img') + u_boot_utils.run_and_log(cons, 'qemu-img create %s 20M' % fname) + u_boot_utils.run_and_log(cons, f'cgpt create {fname}') + + ptr = 40 + + # Number of sectors in 1MB + sect_size = 512 + sect_1mb = (1 << 20) // sect_size + + required_parts = [ + {'num': 1, 'label':'misc', 'size': '1M'}, + {'num': 2, 'label':'boot_a', 'size': '4M'}, + {'num': 3, 'label':'boot_b', 'size': '4M'}, + {'num': 4, 'label':'vendor_boot_a', 'size': '4M'}, + {'num': 5, 'label':'vendor_boot_b', 'size': '4M'}, + ] + + for part in required_parts: + size_str = part['size'] + if 'M' in size_str: + size = int(size_str[:-1]) * sect_1mb + else: + size = int(size_str) + u_boot_utils.run_and_log( + cons, + f"cgpt add -i {part['num']} -b {ptr} -s {size} -l {part['label']} -t basicdata {fname}") + ptr += size + + u_boot_utils.run_and_log(cons, f'cgpt boot -p {fname}') + out = u_boot_utils.run_and_log(cons, f'cgpt show -q {fname}') + + # Create a dict (indexed by partition number) containing the above info + for line in out.splitlines(): + start, size, num, name = line.split(maxsplit=3) + parts[int(num)] = Partition(int(start), int(size), name) + + with open(fname, 'rb') as inf: + disk_data = inf.read() + + boot_img = os.path.join(cons.config.result_dir, 'bootv4.img') + with open(boot_img, 'rb') as inf: + set_part_data(2, inf.read()) + + vendor_boot_img = os.path.join(cons.config.result_dir, 'vendor_boot.img') + with open(vendor_boot_img, 'rb') as inf: + set_part_data(4, inf.read()) + + with open(fname, 'wb') as outf: + outf.write(disk_data) + + print('wrote to {}'.format(fname)) + + return fname
def setup_cedit_file(cons): infname = os.path.join(cons.config.source_dir, @@ -477,6 +552,7 @@ def test_ut_dm_init_bootstd(u_boot_console): setup_bootmenu_image(u_boot_console) setup_cedit_file(u_boot_console) setup_cros_image(u_boot_console) + setup_android_image(u_boot_console)
# Restart so that the new mmc1.img is picked up u_boot_console.restart_uboot()

On Thu 13 Jun 2024 at 16:19, Mattijs Korpershoek mkorpershoek@baylibre.com wrote:
Add a unit test for testing the Android bootmethod.
This requires another mmc image (mmc7) to contain the following partitions:
- misc: contains the Bootloader Control Block (BCB)
- boot_a: contains a fake generic kernel image
- vendor_boot_a: contains a fake vendor_boot image
Also add BOOTMETH_ANDROID as a dependency on sandbox so that we can test this with:
$ ./test/py/test.py --bd sandbox --build -k test_abootimg # build bootv4.img $ ./test/py/test.py --bd sandbox --build -k test_ut # build the mmc7.img $ ./test/py/test.py --bd sandbox --build -k bootflow_android
Reviewed-by: Simon Glass sjg@chromium.org Signed-off-by: Mattijs Korpershoek mkorpershoek@baylibre.com
arch/sandbox/dts/test.dts | 8 +++++ configs/sandbox_defconfig | 2 +- test/boot/bootflow.c | 65 ++++++++++++++++++++++++++++++++++++++-- test/py/tests/test_ut.py | 76 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 147 insertions(+), 4 deletions(-)
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index a012f5c4c9ba..5fb5eac862ec 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -43,6 +43,7 @@ mmc4 = "/mmc4"; mmc5 = "/mmc5"; mmc6 = "/mmc6";
pci0 = &pci0; pci1 = &pci1; pci2 = &pci2;mmc7 = "/mmc7";
@@ -1129,6 +1130,13 @@ filename = "mmc6.img"; };
- /* This is used for Android tests */
- mmc7 {
status = "disabled";
compatible = "sandbox,mmc";
filename = "mmc7.img";
- };
- pch { compatible = "sandbox,pch"; };
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 93b52f2de5cf..bc4398f101a7 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -15,6 +15,7 @@ CONFIG_FIT=y CONFIG_FIT_RSASSA_PSS=y CONFIG_FIT_CIPHER=y CONFIG_FIT_VERBOSE=y +CONFIG_BOOTMETH_ANDROID=y CONFIG_LEGACY_IMAGE_FORMAT=y CONFIG_MEASURED_BOOT=y CONFIG_BOOTSTAGE=y @@ -40,7 +41,6 @@ CONFIG_LOG_MAX_LEVEL=9 CONFIG_LOG_DEFAULT_LEVEL=6 CONFIG_DISPLAY_BOARDINFO_LATE=y CONFIG_STACKPROTECTOR=y -CONFIG_ANDROID_AB=y CONFIG_CMD_CPU=y CONFIG_CMD_LICENSE=y CONFIG_CMD_SMBIOS=y diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index 4511cfa7f9bf..934c4dcbad2b 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -27,6 +27,7 @@
DECLARE_GLOBAL_DATA_PTR;
+extern U_BOOT_DRIVER(bootmeth_android); extern U_BOOT_DRIVER(bootmeth_cros); extern U_BOOT_DRIVER(bootmeth_2script);
@@ -518,12 +519,12 @@ BOOTSTD_TEST(bootflow_cmd_boot, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
- @uts: Unit test state
- @mmc_dev: MMC device to use, e.g. "mmc4". Note that this must remain valid
- in the caller until
- @bind_cros: true to bind the ChromiumOS bootmeth
*/
- @bind_cros: true to bind the ChromiumOS and Android bootmeths
- @old_orderp: Returns the original bootdev order, which must be restored
- Returns 0 on success, -ve on failure
static int prep_mmc_bootdev(struct unit_test_state *uts, const char *mmc_dev,
bool bind_cros, const char ***old_orderp)
bool bind_cros_android, const char ***old_orderp)
{ static const char *order[] = {"mmc2", "mmc1", NULL, NULL}; struct udevice *dev, *bootstd; @@ -545,12 +546,19 @@ static int prep_mmc_bootdev(struct unit_test_state *uts, const char *mmc_dev, "bootmeth_script", 0, ofnode_null(), &dev));
/* Enable the cros bootmeth if needed */
- if (IS_ENABLED(CONFIG_BOOTMETH_CROS) && bind_cros) {
if (IS_ENABLED(CONFIG_BOOTMETH_CROS) && bind_cros_android) { ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd)); ut_assertok(device_bind(bootstd, DM_DRIVER_REF(bootmeth_cros), "cros", 0, ofnode_null(), &dev)); }
/* Enable the android bootmeths if needed */
if (IS_ENABLED(CONFIG_BOOTMETH_ANDROID) && bind_cros_android) {
ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd));
ut_assertok(device_bind(bootstd, DM_DRIVER_REF(bootmeth_android),
"android", 0, ofnode_null(), &dev));
}
/* Change the order to include the device */ std = dev_get_priv(bootstd); old_order = std->bootdev_order;
@@ -589,6 +597,37 @@ static int scan_mmc_bootdev(struct unit_test_state *uts, const char *mmc_dev, return 0; }
+/**
- scan_mmc_android_bootdev() - Set up an mmc bootdev so we can access other
- distros. Android bootflow might print "ANDROID:*" while scanning
- @uts: Unit test state
- @mmc_dev: MMC device to use, e.g. "mmc4"
- Returns 0 on success, -ve on failure
- */
+static int scan_mmc_android_bootdev(struct unit_test_state *uts, const char *mmc_dev) +{
- struct bootstd_priv *std;
- struct udevice *bootstd;
- const char **old_order;
- ut_assertok(prep_mmc_bootdev(uts, mmc_dev, true, &old_order));
- console_record_reset_enable();
- ut_assertok(run_command("bootflow scan", 0));
- /* Android bootflow might print one or two 'ANDROID:*' logs */
- ut_check_skipline(uts);
- ut_check_skipline(uts);
- ut_assert_console_end();
- /* Restore the order used by the device tree */
- ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd));
- std = dev_get_priv(bootstd);
- std->bootdev_order = old_order;
- return 0;
+}
/**
- scan_mmc4_bootdev() - Set up the mmc4 bootdev so we can access a fake Armbian
@@ -1160,3 +1199,23 @@ static int bootflow_cros(struct unit_test_state *uts) return 0; } BOOTSTD_TEST(bootflow_cros, 0);
+/* Test Android bootmeth */ +static int bootflow_android(struct unit_test_state *uts) +{
- ut_assertok(scan_mmc_android_bootdev(uts, "mmc7"));
- ut_assertok(run_command("bootflow list", 0));
- ut_assert_nextlinen("Showing all");
- ut_assert_nextlinen("Seq");
- ut_assert_nextlinen("---");
- ut_assert_nextlinen(" 0 extlinux");
- ut_assert_nextlinen(" 1 android ready mmc 0 mmc7.bootdev.whole ");
- ut_assert_nextlinen("---");
- ut_assert_skip_to_line("(2 bootflows, 2 valid)");
- ut_assert_console_end();
- return 0;
+} +BOOTSTD_TEST(bootflow_android, 0); diff --git a/test/py/tests/test_ut.py b/test/py/tests/test_ut.py index c169c835e38a..39e1abe02a68 100644 --- a/test/py/tests/test_ut.py +++ b/test/py/tests/test_ut.py @@ -423,6 +423,81 @@ def setup_cros_image(cons):
return fname
+def setup_android_image(cons):
- """Create a 20MB disk image with Android partitions"""
- Partition = collections.namedtuple('part', 'start,size,name')
- parts = {}
- disk_data = None
- def set_part_data(partnum, data):
"""Set the contents of a disk partition
This updates disk_data by putting data in the right place
Args:
partnum (int): Partition number to set
data (bytes): Data for that partition
"""
nonlocal disk_data
start = parts[partnum].start * sect_size
disk_data = disk_data[:start] + data + disk_data[start + len(data):]
- mmc_dev = 7
- fname = os.path.join(cons.config.source_dir, f'mmc{mmc_dev}.img')
- u_boot_utils.run_and_log(cons, 'qemu-img create %s 20M' % fname)
- u_boot_utils.run_and_log(cons, f'cgpt create {fname}')
- ptr = 40
- # Number of sectors in 1MB
- sect_size = 512
- sect_1mb = (1 << 20) // sect_size
- required_parts = [
{'num': 1, 'label':'misc', 'size': '1M'},
{'num': 2, 'label':'boot_a', 'size': '4M'},
{'num': 3, 'label':'boot_b', 'size': '4M'},
{'num': 4, 'label':'vendor_boot_a', 'size': '4M'},
{'num': 5, 'label':'vendor_boot_b', 'size': '4M'},
- ]
- for part in required_parts:
size_str = part['size']
if 'M' in size_str:
size = int(size_str[:-1]) * sect_1mb
else:
size = int(size_str)
u_boot_utils.run_and_log(
cons,
f"cgpt add -i {part['num']} -b {ptr} -s {size} -l {part['label']} -t basicdata {fname}")
ptr += size
- u_boot_utils.run_and_log(cons, f'cgpt boot -p {fname}')
- out = u_boot_utils.run_and_log(cons, f'cgpt show -q {fname}')
- # Create a dict (indexed by partition number) containing the above info
- for line in out.splitlines():
start, size, num, name = line.split(maxsplit=3)
parts[int(num)] = Partition(int(start), int(size), name)
- with open(fname, 'rb') as inf:
disk_data = inf.read()
- boot_img = os.path.join(cons.config.result_dir, 'bootv4.img')
- with open(boot_img, 'rb') as inf:
set_part_data(2, inf.read())
- vendor_boot_img = os.path.join(cons.config.result_dir, 'vendor_boot.img')
- with open(vendor_boot_img, 'rb') as inf:
set_part_data(4, inf.read())
- with open(fname, 'wb') as outf:
outf.write(disk_data)
- print('wrote to {}'.format(fname))
- return fname
def setup_cedit_file(cons): infname = os.path.join(cons.config.source_dir, @@ -477,6 +552,7 @@ def test_ut_dm_init_bootstd(u_boot_console): setup_bootmenu_image(u_boot_console) setup_cedit_file(u_boot_console) setup_cros_image(u_boot_console)
setup_android_image(u_boot_console)
# Restart so that the new mmc1.img is picked up u_boot_console.restart_uboot()
-- 2.45.2
Reviewed-by: Julien Masson jmasson@baylibre.com

Le 13/06/2024 à 12:13, Mattijs Korpershoek a écrit :
Add a unit test for testing the Android bootmethod.
This requires another mmc image (mmc7) to contain the following partitions:
- misc: contains the Bootloader Control Block (BCB)
- boot_a: contains a fake generic kernel image
- vendor_boot_a: contains a fake vendor_boot image
Also add BOOTMETH_ANDROID as a dependency on sandbox so that we can test this with:
$ ./test/py/test.py --bd sandbox --build -k test_abootimg # build bootv4.img $ ./test/py/test.py --bd sandbox --build -k test_ut # build the mmc7.img $ ./test/py/test.py --bd sandbox --build -k bootflow_android
Reviewed-by: Simon Glass sjg@chromium.org Signed-off-by: Mattijs Korpershoek mkorpershoek@baylibre.com
arch/sandbox/dts/test.dts | 8 +++++ configs/sandbox_defconfig | 2 +- test/boot/bootflow.c | 65 ++++++++++++++++++++++++++++++++++++++-- test/py/tests/test_ut.py | 76 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 147 insertions(+), 4 deletions(-)
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index a012f5c4c9ba..5fb5eac862ec 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -43,6 +43,7 @@ mmc4 = "/mmc4"; mmc5 = "/mmc5"; mmc6 = "/mmc6";
pci0 = &pci0; pci1 = &pci1; pci2 = &pci2;mmc7 = "/mmc7";
@@ -1129,6 +1130,13 @@ filename = "mmc6.img"; };
- /* This is used for Android tests */
- mmc7 {
status = "disabled";
compatible = "sandbox,mmc";
filename = "mmc7.img";
- };
- pch { compatible = "sandbox,pch"; };
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 93b52f2de5cf..bc4398f101a7 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -15,6 +15,7 @@ CONFIG_FIT=y CONFIG_FIT_RSASSA_PSS=y CONFIG_FIT_CIPHER=y CONFIG_FIT_VERBOSE=y +CONFIG_BOOTMETH_ANDROID=y CONFIG_LEGACY_IMAGE_FORMAT=y CONFIG_MEASURED_BOOT=y CONFIG_BOOTSTAGE=y @@ -40,7 +41,6 @@ CONFIG_LOG_MAX_LEVEL=9 CONFIG_LOG_DEFAULT_LEVEL=6 CONFIG_DISPLAY_BOARDINFO_LATE=y CONFIG_STACKPROTECTOR=y -CONFIG_ANDROID_AB=y CONFIG_CMD_CPU=y CONFIG_CMD_LICENSE=y CONFIG_CMD_SMBIOS=y diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index 4511cfa7f9bf..934c4dcbad2b 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -27,6 +27,7 @@
DECLARE_GLOBAL_DATA_PTR;
+extern U_BOOT_DRIVER(bootmeth_android); extern U_BOOT_DRIVER(bootmeth_cros); extern U_BOOT_DRIVER(bootmeth_2script);
@@ -518,12 +519,12 @@ BOOTSTD_TEST(bootflow_cmd_boot, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
- @uts: Unit test state
- @mmc_dev: MMC device to use, e.g. "mmc4". Note that this must remain valid
- in the caller until
- @bind_cros: true to bind the ChromiumOS bootmeth
*/ static int prep_mmc_bootdev(struct unit_test_state *uts, const char *mmc_dev,
- @bind_cros: true to bind the ChromiumOS and Android bootmeths
- @old_orderp: Returns the original bootdev order, which must be restored
- Returns 0 on success, -ve on failure
bool bind_cros, const char ***old_orderp)
{ static const char *order[] = {"mmc2", "mmc1", NULL, NULL}; struct udevice *dev, *bootstd;bool bind_cros_android, const char ***old_orderp)
@@ -545,12 +546,19 @@ static int prep_mmc_bootdev(struct unit_test_state *uts, const char *mmc_dev, "bootmeth_script", 0, ofnode_null(), &dev));
/* Enable the cros bootmeth if needed */
- if (IS_ENABLED(CONFIG_BOOTMETH_CROS) && bind_cros) {
if (IS_ENABLED(CONFIG_BOOTMETH_CROS) && bind_cros_android) { ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd)); ut_assertok(device_bind(bootstd, DM_DRIVER_REF(bootmeth_cros), "cros", 0, ofnode_null(), &dev)); }
/* Enable the android bootmeths if needed */
if (IS_ENABLED(CONFIG_BOOTMETH_ANDROID) && bind_cros_android) {
ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd));
ut_assertok(device_bind(bootstd, DM_DRIVER_REF(bootmeth_android),
"android", 0, ofnode_null(), &dev));
}
/* Change the order to include the device */ std = dev_get_priv(bootstd); old_order = std->bootdev_order;
@@ -589,6 +597,37 @@ static int scan_mmc_bootdev(struct unit_test_state *uts, const char *mmc_dev, return 0; }
+/**
- scan_mmc_android_bootdev() - Set up an mmc bootdev so we can access other
- distros. Android bootflow might print "ANDROID:*" while scanning
- @uts: Unit test state
- @mmc_dev: MMC device to use, e.g. "mmc4"
- Returns 0 on success, -ve on failure
- */
+static int scan_mmc_android_bootdev(struct unit_test_state *uts, const char *mmc_dev) +{
- struct bootstd_priv *std;
- struct udevice *bootstd;
- const char **old_order;
- ut_assertok(prep_mmc_bootdev(uts, mmc_dev, true, &old_order));
- console_record_reset_enable();
- ut_assertok(run_command("bootflow scan", 0));
- /* Android bootflow might print one or two 'ANDROID:*' logs */
- ut_check_skipline(uts);
- ut_check_skipline(uts);
- ut_assert_console_end();
- /* Restore the order used by the device tree */
- ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd));
- std = dev_get_priv(bootstd);
- std->bootdev_order = old_order;
- return 0;
+}
- /**
- scan_mmc4_bootdev() - Set up the mmc4 bootdev so we can access a fake Armbian
@@ -1160,3 +1199,23 @@ static int bootflow_cros(struct unit_test_state *uts) return 0; } BOOTSTD_TEST(bootflow_cros, 0);
+/* Test Android bootmeth */ +static int bootflow_android(struct unit_test_state *uts) +{
- ut_assertok(scan_mmc_android_bootdev(uts, "mmc7"));
- ut_assertok(run_command("bootflow list", 0));
- ut_assert_nextlinen("Showing all");
- ut_assert_nextlinen("Seq");
- ut_assert_nextlinen("---");
- ut_assert_nextlinen(" 0 extlinux");
- ut_assert_nextlinen(" 1 android ready mmc 0 mmc7.bootdev.whole ");
- ut_assert_nextlinen("---");
- ut_assert_skip_to_line("(2 bootflows, 2 valid)");
- ut_assert_console_end();
- return 0;
+} +BOOTSTD_TEST(bootflow_android, 0); diff --git a/test/py/tests/test_ut.py b/test/py/tests/test_ut.py index c169c835e38a..39e1abe02a68 100644 --- a/test/py/tests/test_ut.py +++ b/test/py/tests/test_ut.py @@ -423,6 +423,81 @@ def setup_cros_image(cons):
return fname
+def setup_android_image(cons):
"""Create a 20MB disk image with Android partitions"""
Partition = collections.namedtuple('part', 'start,size,name')
parts = {}
disk_data = None
def set_part_data(partnum, data):
"""Set the contents of a disk partition
This updates disk_data by putting data in the right place
Args:
partnum (int): Partition number to set
data (bytes): Data for that partition
"""
nonlocal disk_data
start = parts[partnum].start * sect_size
disk_data = disk_data[:start] + data + disk_data[start + len(data):]
mmc_dev = 7
fname = os.path.join(cons.config.source_dir, f'mmc{mmc_dev}.img')
u_boot_utils.run_and_log(cons, 'qemu-img create %s 20M' % fname)
u_boot_utils.run_and_log(cons, f'cgpt create {fname}')
ptr = 40
# Number of sectors in 1MB
sect_size = 512
sect_1mb = (1 << 20) // sect_size
required_parts = [
{'num': 1, 'label':'misc', 'size': '1M'},
{'num': 2, 'label':'boot_a', 'size': '4M'},
{'num': 3, 'label':'boot_b', 'size': '4M'},
{'num': 4, 'label':'vendor_boot_a', 'size': '4M'},
{'num': 5, 'label':'vendor_boot_b', 'size': '4M'},
]
for part in required_parts:
size_str = part['size']
if 'M' in size_str:
size = int(size_str[:-1]) * sect_1mb
else:
size = int(size_str)
u_boot_utils.run_and_log(
cons,
f"cgpt add -i {part['num']} -b {ptr} -s {size} -l {part['label']} -t basicdata {fname}")
ptr += size
u_boot_utils.run_and_log(cons, f'cgpt boot -p {fname}')
out = u_boot_utils.run_and_log(cons, f'cgpt show -q {fname}')
# Create a dict (indexed by partition number) containing the above info
for line in out.splitlines():
start, size, num, name = line.split(maxsplit=3)
parts[int(num)] = Partition(int(start), int(size), name)
with open(fname, 'rb') as inf:
disk_data = inf.read()
boot_img = os.path.join(cons.config.result_dir, 'bootv4.img')
with open(boot_img, 'rb') as inf:
set_part_data(2, inf.read())
vendor_boot_img = os.path.join(cons.config.result_dir, 'vendor_boot.img')
with open(vendor_boot_img, 'rb') as inf:
set_part_data(4, inf.read())
with open(fname, 'wb') as outf:
outf.write(disk_data)
print('wrote to {}'.format(fname))
return fname
def setup_cedit_file(cons): infname = os.path.join(cons.config.source_dir,
@@ -477,6 +552,7 @@ def test_ut_dm_init_bootstd(u_boot_console): setup_bootmenu_image(u_boot_console) setup_cedit_file(u_boot_console) setup_cros_image(u_boot_console)
setup_android_image(u_boot_console)
# Restart so that the new mmc1.img is picked up u_boot_console.restart_uboot()
Reviewed-by: Guillaume La Roque glaroque@baylibre.com

Hi,
i apply patch series with commit you give in cover letter and test on TI AM62S-SK board. Android boot properly , just with a small changes in uboot eenv
setenv vendor_boot_comp_addr_r 0xd0000000
this changes is need link to patch done by roman on ramdisk and vendor boot loading.
so you can add for this series:
Tested-by: Guillaume La Roque glaroque@baylibre.com
Guillaume
Le 13/06/2024 à 12:13, Mattijs Korpershoek a écrit :
Android boot flow is a bit different than a regular Linux distro. Android relies on multiple partitions in order to boot.
A typical boot flow would be:
- Parse the Bootloader Control Block (BCB, misc partition)
- If BCB requested bootonce-bootloader, start fastboot and wait.
- If BCB requested recovery or normal android, run the following: a. Get slot (A/B) from BCB b. Run AVB (Android Verified Boot) on boot partitions c. Load boot and vendor_boot partitions d. Load device-tree, ramdisk and boot
The AOSP documentation has more details at [1], [2], [3]
This has been implemented via complex boot scripts such as [4]. However, these boot script are neither very maintainable nor generic. Moreover, DISTRO_DEFAULTS is being deprecated [5].
Add a generic Android bootflow implementation for bootstd.
For this initial version, only boot image v4 is supported.
This has been tested on sandbox using: $ ./test/py/test.py --bd sandbox --build -k test_ut
This has also been tested on the AM62X SK EVM using TI's Android SDK[6] To test on TI board, the following (WIP) patch is needed as well: https://gitlab.baylibre.com/baylibre/ti/ti-u-boot/-/commit/84cceb912bccd7cdd...
[1] https://source.android.com/docs/core/architecture/bootloader [2] https://source.android.com/docs/core/architecture/partitions [3] https://source.android.com/docs/core/architecture/partitions/generic-boot [4] https://source.denx.de/u-boot/u-boot/-/blob/master/include/configs/meson64_a... [5] https://lore.kernel.org/r/all/20230914165615.1058529-17-sjg@chromium.org/ [6] https://software-dl.ti.com/processor-sdk-android/esd/AM62X/09_02_00/docs/and...
Signed-off-by: Mattijs Korpershoek mkorpershoek@baylibre.com
Changes in v2:
- Dropped patch 2/6 boot: android: Add image_android_get_version() (Igor)
- Fixed multi-line comment style (Igor, Simon)
- Added dependency on CMD_FASTBOOT for BOOTMETH_ANDROID (Igor)
- Fixed various resource leaks (Igor)
- Fixed bootmeth_priv dangling pointer on error cases (Igor)
- Updated test instructions in commit message for patch 6/6
- Added __weak impl of get_avendor_bootimg_addr() in patch 1 (dropped Igor's review because of this change)
- Added extra info in Kconfig to detail MMC limitation (Simon)
- Fixed typo Bootmethod->Bootmeth (Simon)
- Documented android_priv structure (Simon)
- Demoted various messages from printf() to log_debug (Simon)
- Fixed some lines too long (Simon)
- Added function documentation to read_slotted_partition() (Simon)
- Added some doc about avb extra_args being modified (Simon)
- Link to v1: https://lore.kernel.org/r/20240606-bootmeth-android-v1-0-0c69d4457cc5@baylib...
Mattijs Korpershoek (5): boot: android: Provide vendor_bootimg_addr in boot_get_fdt() bootstd: Add bootflow_iter_check_mmc() helper android: boot: Add set_abootimg_addr() and set_avendor_bootimg_addr() bootstd: Add a bootmeth for Android bootstd: Add test for bootmeth_android
MAINTAINERS | 7 + arch/sandbox/dts/test.dts | 8 + boot/Kconfig | 16 ++ boot/Makefile | 2 + boot/bootflow.c | 12 + boot/bootmeth_android.c | 553 ++++++++++++++++++++++++++++++++++++++++++++++ boot/bootmeth_android.h | 29 +++ boot/image-android.c | 5 + boot/image-fdt.c | 2 +- cmd/abootimg.c | 10 + configs/sandbox_defconfig | 2 +- doc/develop/bootstd.rst | 6 + include/bootflow.h | 9 + include/image.h | 14 ++ test/boot/bootflow.c | 65 +++++- test/py/tests/test_ut.py | 76 +++++++ 16 files changed, 811 insertions(+), 5 deletions(-)
base-commit: f9886bc60f42d5bcfcfa4e474af7dc230400b6be change-id: 20240605-bootmeth-android-bfc8596e9367
Best regards,

Hi Guillaume,
Thank you for testing.
On ven., juin 14, 2024 at 11:53, Guillaume LA ROQUE glaroque@baylibre.com wrote:
Hi,
i apply patch series with commit you give in cover letter and test on TI AM62S-SK board. Android boot properly , just with a small changes in uboot eenv
setenv vendor_boot_comp_addr_r 0xd0000000
I see.
Thank you for sharing this.
I can confirm that this is indeed needed on next since da3447d09fa0 ("android: Fix ramdisk loading for bootimage v3+").
Will take that into account when sending board support using bootmeth_android
this changes is need link to patch done by roman on ramdisk and vendor boot loading.
so you can add for this series:
Tested-by: Guillaume La Roque glaroque@baylibre.com
[...]

Hi Mattijs,
On Thu, 13 Jun 2024 at 04:13, Mattijs Korpershoek mkorpershoek@baylibre.com wrote:
Android boot flow is a bit different than a regular Linux distro. Android relies on multiple partitions in order to boot.
A typical boot flow would be:
- Parse the Bootloader Control Block (BCB, misc partition)
- If BCB requested bootonce-bootloader, start fastboot and wait.
- If BCB requested recovery or normal android, run the following: a. Get slot (A/B) from BCB b. Run AVB (Android Verified Boot) on boot partitions c. Load boot and vendor_boot partitions d. Load device-tree, ramdisk and boot
The AOSP documentation has more details at [1], [2], [3]
This has been implemented via complex boot scripts such as [4]. However, these boot script are neither very maintainable nor generic. Moreover, DISTRO_DEFAULTS is being deprecated [5].
Add a generic Android bootflow implementation for bootstd.
For this initial version, only boot image v4 is supported.
This has been tested on sandbox using: $ ./test/py/test.py --bd sandbox --build -k test_ut
This has also been tested on the AM62X SK EVM using TI's Android SDK[6] To test on TI board, the following (WIP) patch is needed as well: https://gitlab.baylibre.com/baylibre/ti/ti-u-boot/-/commit/84cceb912bccd7cdd...
[1] https://source.android.com/docs/core/architecture/bootloader [2] https://source.android.com/docs/core/architecture/partitions [3] https://source.android.com/docs/core/architecture/partitions/generic-boot [4] https://source.denx.de/u-boot/u-boot/-/blob/master/include/configs/meson64_a... [5] https://lore.kernel.org/r/all/20230914165615.1058529-17-sjg@chromium.org/ [6] https://software-dl.ti.com/processor-sdk-android/esd/AM62X/09_02_00/docs/and...
Signed-off-by: Mattijs Korpershoek mkorpershoek@baylibre.com
Changes in v2:
- Dropped patch 2/6 boot: android: Add image_android_get_version() (Igor)
- Fixed multi-line comment style (Igor, Simon)
- Added dependency on CMD_FASTBOOT for BOOTMETH_ANDROID (Igor)
- Fixed various resource leaks (Igor)
- Fixed bootmeth_priv dangling pointer on error cases (Igor)
- Updated test instructions in commit message for patch 6/6
- Added __weak impl of get_avendor_bootimg_addr() in patch 1 (dropped Igor's review because of this change)
- Added extra info in Kconfig to detail MMC limitation (Simon)
- Fixed typo Bootmethod->Bootmeth (Simon)
- Documented android_priv structure (Simon)
- Demoted various messages from printf() to log_debug (Simon)
- Fixed some lines too long (Simon)
- Added function documentation to read_slotted_partition() (Simon)
- Added some doc about avb extra_args being modified (Simon)
- Link to v1: https://lore.kernel.org/r/20240606-bootmeth-android-v1-0-0c69d4457cc5@baylib...
Mattijs Korpershoek (5): boot: android: Provide vendor_bootimg_addr in boot_get_fdt() bootstd: Add bootflow_iter_check_mmc() helper android: boot: Add set_abootimg_addr() and set_avendor_bootimg_addr() bootstd: Add a bootmeth for Android bootstd: Add test for bootmeth_android
MAINTAINERS | 7 + arch/sandbox/dts/test.dts | 8 + boot/Kconfig | 16 ++ boot/Makefile | 2 + boot/bootflow.c | 12 + boot/bootmeth_android.c | 553 ++++++++++++++++++++++++++++++++++++++++++++++ boot/bootmeth_android.h | 29 +++ boot/image-android.c | 5 + boot/image-fdt.c | 2 +- cmd/abootimg.c | 10 + configs/sandbox_defconfig | 2 +- doc/develop/bootstd.rst | 6 + include/bootflow.h | 9 + include/image.h | 14 ++ test/boot/bootflow.c | 65 +++++- test/py/tests/test_ut.py | 76 +++++++ 16 files changed, 811 insertions(+), 5 deletions(-)
base-commit: f9886bc60f42d5bcfcfa4e474af7dc230400b6be change-id: 20240605-bootmeth-android-bfc8596e9367
Best regards,
Mattijs Korpershoek mkorpershoek@baylibre.com
Thinking about this, I believe we should start having docs about the individual bootmeths themselves.
Can you add a section about your new bootmeth? I will come up with a patch for the others that I know about. Perhaps doc/develop/bootstd.rst would be a suitable place for now?
Regards, Simon

Hi Simon,
On lun., juin 17, 2024 at 07:53, Simon Glass sjg@chromium.org wrote:
Hi Mattijs,
On Thu, 13 Jun 2024 at 04:13, Mattijs Korpershoek mkorpershoek@baylibre.com wrote:
Android boot flow is a bit different than a regular Linux distro. Android relies on multiple partitions in order to boot.
A typical boot flow would be:
- Parse the Bootloader Control Block (BCB, misc partition)
- If BCB requested bootonce-bootloader, start fastboot and wait.
- If BCB requested recovery or normal android, run the following: a. Get slot (A/B) from BCB b. Run AVB (Android Verified Boot) on boot partitions c. Load boot and vendor_boot partitions d. Load device-tree, ramdisk and boot
The AOSP documentation has more details at [1], [2], [3]
This has been implemented via complex boot scripts such as [4]. However, these boot script are neither very maintainable nor generic. Moreover, DISTRO_DEFAULTS is being deprecated [5].
Add a generic Android bootflow implementation for bootstd.
For this initial version, only boot image v4 is supported.
This has been tested on sandbox using: $ ./test/py/test.py --bd sandbox --build -k test_ut
This has also been tested on the AM62X SK EVM using TI's Android SDK[6] To test on TI board, the following (WIP) patch is needed as well: https://gitlab.baylibre.com/baylibre/ti/ti-u-boot/-/commit/84cceb912bccd7cdd...
[1] https://source.android.com/docs/core/architecture/bootloader [2] https://source.android.com/docs/core/architecture/partitions [3] https://source.android.com/docs/core/architecture/partitions/generic-boot [4] https://source.denx.de/u-boot/u-boot/-/blob/master/include/configs/meson64_a... [5] https://lore.kernel.org/r/all/20230914165615.1058529-17-sjg@chromium.org/ [6] https://software-dl.ti.com/processor-sdk-android/esd/AM62X/09_02_00/docs/and...
Signed-off-by: Mattijs Korpershoek mkorpershoek@baylibre.com
Changes in v2:
- Dropped patch 2/6 boot: android: Add image_android_get_version() (Igor)
- Fixed multi-line comment style (Igor, Simon)
- Added dependency on CMD_FASTBOOT for BOOTMETH_ANDROID (Igor)
- Fixed various resource leaks (Igor)
- Fixed bootmeth_priv dangling pointer on error cases (Igor)
- Updated test instructions in commit message for patch 6/6
- Added __weak impl of get_avendor_bootimg_addr() in patch 1 (dropped Igor's review because of this change)
- Added extra info in Kconfig to detail MMC limitation (Simon)
- Fixed typo Bootmethod->Bootmeth (Simon)
- Documented android_priv structure (Simon)
- Demoted various messages from printf() to log_debug (Simon)
- Fixed some lines too long (Simon)
- Added function documentation to read_slotted_partition() (Simon)
- Added some doc about avb extra_args being modified (Simon)
- Link to v1: https://lore.kernel.org/r/20240606-bootmeth-android-v1-0-0c69d4457cc5@baylib...
Mattijs Korpershoek (5): boot: android: Provide vendor_bootimg_addr in boot_get_fdt() bootstd: Add bootflow_iter_check_mmc() helper android: boot: Add set_abootimg_addr() and set_avendor_bootimg_addr() bootstd: Add a bootmeth for Android bootstd: Add test for bootmeth_android
MAINTAINERS | 7 + arch/sandbox/dts/test.dts | 8 + boot/Kconfig | 16 ++ boot/Makefile | 2 + boot/bootflow.c | 12 + boot/bootmeth_android.c | 553 ++++++++++++++++++++++++++++++++++++++++++++++ boot/bootmeth_android.h | 29 +++ boot/image-android.c | 5 + boot/image-fdt.c | 2 +- cmd/abootimg.c | 10 + configs/sandbox_defconfig | 2 +- doc/develop/bootstd.rst | 6 + include/bootflow.h | 9 + include/image.h | 14 ++ test/boot/bootflow.c | 65 +++++- test/py/tests/test_ut.py | 76 +++++++ 16 files changed, 811 insertions(+), 5 deletions(-)
base-commit: f9886bc60f42d5bcfcfa4e474af7dc230400b6be change-id: 20240605-bootmeth-android-bfc8596e9367
Best regards,
Mattijs Korpershoek mkorpershoek@baylibre.com
Thinking about this, I believe we should start having docs about the individual bootmeths themselves.
Yes.
Can you add a section about your new bootmeth? I will come up with a patch for the others that I know about. Perhaps doc/develop/bootstd.rst would be a suitable place for now?
Yes I can add a section. I would have preferred to have an example to work from there, but I can start writing docs as well.
I'm leaving on vacation soon (without computer), so I'll be able to send a v3 with docs included in at earliest a 2-3 weeks from now.
If you make a patch for the other bootmeths in the mean-time, please cc me so that I can help review and have an example for Android.
Thanks! Mattijs
Regards, Simon

Hi Mattijs,
On Mon, 17 Jun 2024 at 09:15, Mattijs Korpershoek mkorpershoek@baylibre.com wrote:
Hi Simon,
On lun., juin 17, 2024 at 07:53, Simon Glass sjg@chromium.org wrote:
Hi Mattijs,
On Thu, 13 Jun 2024 at 04:13, Mattijs Korpershoek mkorpershoek@baylibre.com wrote:
Android boot flow is a bit different than a regular Linux distro. Android relies on multiple partitions in order to boot.
A typical boot flow would be:
- Parse the Bootloader Control Block (BCB, misc partition)
- If BCB requested bootonce-bootloader, start fastboot and wait.
- If BCB requested recovery or normal android, run the following: a. Get slot (A/B) from BCB b. Run AVB (Android Verified Boot) on boot partitions c. Load boot and vendor_boot partitions d. Load device-tree, ramdisk and boot
The AOSP documentation has more details at [1], [2], [3]
This has been implemented via complex boot scripts such as [4]. However, these boot script are neither very maintainable nor generic. Moreover, DISTRO_DEFAULTS is being deprecated [5].
Add a generic Android bootflow implementation for bootstd.
For this initial version, only boot image v4 is supported.
This has been tested on sandbox using: $ ./test/py/test.py --bd sandbox --build -k test_ut
This has also been tested on the AM62X SK EVM using TI's Android SDK[6] To test on TI board, the following (WIP) patch is needed as well: https://gitlab.baylibre.com/baylibre/ti/ti-u-boot/-/commit/84cceb912bccd7cdd...
[1] https://source.android.com/docs/core/architecture/bootloader [2] https://source.android.com/docs/core/architecture/partitions [3] https://source.android.com/docs/core/architecture/partitions/generic-boot [4] https://source.denx.de/u-boot/u-boot/-/blob/master/include/configs/meson64_a... [5] https://lore.kernel.org/r/all/20230914165615.1058529-17-sjg@chromium.org/ [6] https://software-dl.ti.com/processor-sdk-android/esd/AM62X/09_02_00/docs/and...
Signed-off-by: Mattijs Korpershoek mkorpershoek@baylibre.com
Changes in v2:
- Dropped patch 2/6 boot: android: Add image_android_get_version() (Igor)
- Fixed multi-line comment style (Igor, Simon)
- Added dependency on CMD_FASTBOOT for BOOTMETH_ANDROID (Igor)
- Fixed various resource leaks (Igor)
- Fixed bootmeth_priv dangling pointer on error cases (Igor)
- Updated test instructions in commit message for patch 6/6
- Added __weak impl of get_avendor_bootimg_addr() in patch 1 (dropped Igor's review because of this change)
- Added extra info in Kconfig to detail MMC limitation (Simon)
- Fixed typo Bootmethod->Bootmeth (Simon)
- Documented android_priv structure (Simon)
- Demoted various messages from printf() to log_debug (Simon)
- Fixed some lines too long (Simon)
- Added function documentation to read_slotted_partition() (Simon)
- Added some doc about avb extra_args being modified (Simon)
- Link to v1: https://lore.kernel.org/r/20240606-bootmeth-android-v1-0-0c69d4457cc5@baylib...
Mattijs Korpershoek (5): boot: android: Provide vendor_bootimg_addr in boot_get_fdt() bootstd: Add bootflow_iter_check_mmc() helper android: boot: Add set_abootimg_addr() and set_avendor_bootimg_addr() bootstd: Add a bootmeth for Android bootstd: Add test for bootmeth_android
MAINTAINERS | 7 + arch/sandbox/dts/test.dts | 8 + boot/Kconfig | 16 ++ boot/Makefile | 2 + boot/bootflow.c | 12 + boot/bootmeth_android.c | 553 ++++++++++++++++++++++++++++++++++++++++++++++ boot/bootmeth_android.h | 29 +++ boot/image-android.c | 5 + boot/image-fdt.c | 2 +- cmd/abootimg.c | 10 + configs/sandbox_defconfig | 2 +- doc/develop/bootstd.rst | 6 + include/bootflow.h | 9 + include/image.h | 14 ++ test/boot/bootflow.c | 65 +++++- test/py/tests/test_ut.py | 76 +++++++ 16 files changed, 811 insertions(+), 5 deletions(-)
base-commit: f9886bc60f42d5bcfcfa4e474af7dc230400b6be change-id: 20240605-bootmeth-android-bfc8596e9367
Best regards,
Mattijs Korpershoek mkorpershoek@baylibre.com
Thinking about this, I believe we should start having docs about the individual bootmeths themselves.
Yes.
Can you add a section about your new bootmeth? I will come up with a patch for the others that I know about. Perhaps doc/develop/bootstd.rst would be a suitable place for now?
Yes I can add a section. I would have preferred to have an example to work from there, but I can start writing docs as well.
I'm leaving on vacation soon (without computer), so I'll be able to send a v3 with docs included in at earliest a 2-3 weeks from now.
It isn't a blocker for this series, just something I thought of.
If you make a patch for the other bootmeths in the mean-time, please cc me so that I can help review and have an example for Android.
Yes, will do, if I do.
Regards, Simon

Hi Simon.
On mar., juin 18, 2024 at 21:03, Simon Glass sjg@chromium.org wrote:
Hi Mattijs,
On Mon, 17 Jun 2024 at 09:15, Mattijs Korpershoek mkorpershoek@baylibre.com wrote:
Hi Simon,
On lun., juin 17, 2024 at 07:53, Simon Glass sjg@chromium.org wrote:
Hi Mattijs,
On Thu, 13 Jun 2024 at 04:13, Mattijs Korpershoek mkorpershoek@baylibre.com wrote:
Android boot flow is a bit different than a regular Linux distro. Android relies on multiple partitions in order to boot.
A typical boot flow would be:
- Parse the Bootloader Control Block (BCB, misc partition)
- If BCB requested bootonce-bootloader, start fastboot and wait.
- If BCB requested recovery or normal android, run the following: a. Get slot (A/B) from BCB b. Run AVB (Android Verified Boot) on boot partitions c. Load boot and vendor_boot partitions d. Load device-tree, ramdisk and boot
The AOSP documentation has more details at [1], [2], [3]
This has been implemented via complex boot scripts such as [4]. However, these boot script are neither very maintainable nor generic. Moreover, DISTRO_DEFAULTS is being deprecated [5].
Add a generic Android bootflow implementation for bootstd.
For this initial version, only boot image v4 is supported.
This has been tested on sandbox using: $ ./test/py/test.py --bd sandbox --build -k test_ut
This has also been tested on the AM62X SK EVM using TI's Android SDK[6] To test on TI board, the following (WIP) patch is needed as well: https://gitlab.baylibre.com/baylibre/ti/ti-u-boot/-/commit/84cceb912bccd7cdd...
[1] https://source.android.com/docs/core/architecture/bootloader [2] https://source.android.com/docs/core/architecture/partitions [3] https://source.android.com/docs/core/architecture/partitions/generic-boot [4] https://source.denx.de/u-boot/u-boot/-/blob/master/include/configs/meson64_a... [5] https://lore.kernel.org/r/all/20230914165615.1058529-17-sjg@chromium.org/ [6] https://software-dl.ti.com/processor-sdk-android/esd/AM62X/09_02_00/docs/and...
Signed-off-by: Mattijs Korpershoek mkorpershoek@baylibre.com
Changes in v2:
- Dropped patch 2/6 boot: android: Add image_android_get_version() (Igor)
- Fixed multi-line comment style (Igor, Simon)
- Added dependency on CMD_FASTBOOT for BOOTMETH_ANDROID (Igor)
- Fixed various resource leaks (Igor)
- Fixed bootmeth_priv dangling pointer on error cases (Igor)
- Updated test instructions in commit message for patch 6/6
- Added __weak impl of get_avendor_bootimg_addr() in patch 1 (dropped Igor's review because of this change)
- Added extra info in Kconfig to detail MMC limitation (Simon)
- Fixed typo Bootmethod->Bootmeth (Simon)
- Documented android_priv structure (Simon)
- Demoted various messages from printf() to log_debug (Simon)
- Fixed some lines too long (Simon)
- Added function documentation to read_slotted_partition() (Simon)
- Added some doc about avb extra_args being modified (Simon)
- Link to v1: https://lore.kernel.org/r/20240606-bootmeth-android-v1-0-0c69d4457cc5@baylib...
Mattijs Korpershoek (5): boot: android: Provide vendor_bootimg_addr in boot_get_fdt() bootstd: Add bootflow_iter_check_mmc() helper android: boot: Add set_abootimg_addr() and set_avendor_bootimg_addr() bootstd: Add a bootmeth for Android bootstd: Add test for bootmeth_android
MAINTAINERS | 7 + arch/sandbox/dts/test.dts | 8 + boot/Kconfig | 16 ++ boot/Makefile | 2 + boot/bootflow.c | 12 + boot/bootmeth_android.c | 553 ++++++++++++++++++++++++++++++++++++++++++++++ boot/bootmeth_android.h | 29 +++ boot/image-android.c | 5 + boot/image-fdt.c | 2 +- cmd/abootimg.c | 10 + configs/sandbox_defconfig | 2 +- doc/develop/bootstd.rst | 6 + include/bootflow.h | 9 + include/image.h | 14 ++ test/boot/bootflow.c | 65 +++++- test/py/tests/test_ut.py | 76 +++++++ 16 files changed, 811 insertions(+), 5 deletions(-)
base-commit: f9886bc60f42d5bcfcfa4e474af7dc230400b6be change-id: 20240605-bootmeth-android-bfc8596e9367
Best regards,
Mattijs Korpershoek mkorpershoek@baylibre.com
Thinking about this, I believe we should start having docs about the individual bootmeths themselves.
Yes.
Can you add a section about your new bootmeth? I will come up with a patch for the others that I know about. Perhaps doc/develop/bootstd.rst would be a suitable place for now?
Yes I can add a section. I would have preferred to have an example to work from there, but I can start writing docs as well.
I'm leaving on vacation soon (without computer), so I'll be able to send a v3 with docs included in at earliest a 2-3 weeks from now.
It isn't a blocker for this series, just something I thought of.
Understood. In that case if there are no other remarks on this series, I'd prefer to submit a separate documentation patch once this is applied!
If you make a patch for the other bootmeths in the mean-time, please cc me so that I can help review and have an example for Android.
Yes, will do, if I do.
Regards, Simon

On mar., juin 18, 2024 at 21:03, Simon Glass sjg@chromium.org wrote:
Hi Mattijs,
On Mon, 17 Jun 2024 at 09:15, Mattijs Korpershoek mkorpershoek@baylibre.com wrote:
Hi Simon,
On lun., juin 17, 2024 at 07:53, Simon Glass sjg@chromium.org wrote:
Hi Mattijs,
On Thu, 13 Jun 2024 at 04:13, Mattijs Korpershoek mkorpershoek@baylibre.com wrote:
Android boot flow is a bit different than a regular Linux distro. Android relies on multiple partitions in order to boot.
A typical boot flow would be:
- Parse the Bootloader Control Block (BCB, misc partition)
- If BCB requested bootonce-bootloader, start fastboot and wait.
- If BCB requested recovery or normal android, run the following: a. Get slot (A/B) from BCB b. Run AVB (Android Verified Boot) on boot partitions c. Load boot and vendor_boot partitions d. Load device-tree, ramdisk and boot
The AOSP documentation has more details at [1], [2], [3]
This has been implemented via complex boot scripts such as [4]. However, these boot script are neither very maintainable nor generic. Moreover, DISTRO_DEFAULTS is being deprecated [5].
Add a generic Android bootflow implementation for bootstd.
For this initial version, only boot image v4 is supported.
This has been tested on sandbox using: $ ./test/py/test.py --bd sandbox --build -k test_ut
This has also been tested on the AM62X SK EVM using TI's Android SDK[6] To test on TI board, the following (WIP) patch is needed as well: https://gitlab.baylibre.com/baylibre/ti/ti-u-boot/-/commit/84cceb912bccd7cdd...
[1] https://source.android.com/docs/core/architecture/bootloader [2] https://source.android.com/docs/core/architecture/partitions [3] https://source.android.com/docs/core/architecture/partitions/generic-boot [4] https://source.denx.de/u-boot/u-boot/-/blob/master/include/configs/meson64_a... [5] https://lore.kernel.org/r/all/20230914165615.1058529-17-sjg@chromium.org/ [6] https://software-dl.ti.com/processor-sdk-android/esd/AM62X/09_02_00/docs/and...
Signed-off-by: Mattijs Korpershoek mkorpershoek@baylibre.com
Changes in v2:
- Dropped patch 2/6 boot: android: Add image_android_get_version() (Igor)
- Fixed multi-line comment style (Igor, Simon)
- Added dependency on CMD_FASTBOOT for BOOTMETH_ANDROID (Igor)
- Fixed various resource leaks (Igor)
- Fixed bootmeth_priv dangling pointer on error cases (Igor)
- Updated test instructions in commit message for patch 6/6
- Added __weak impl of get_avendor_bootimg_addr() in patch 1 (dropped Igor's review because of this change)
- Added extra info in Kconfig to detail MMC limitation (Simon)
- Fixed typo Bootmethod->Bootmeth (Simon)
- Documented android_priv structure (Simon)
- Demoted various messages from printf() to log_debug (Simon)
- Fixed some lines too long (Simon)
- Added function documentation to read_slotted_partition() (Simon)
- Added some doc about avb extra_args being modified (Simon)
- Link to v1: https://lore.kernel.org/r/20240606-bootmeth-android-v1-0-0c69d4457cc5@baylib...
Mattijs Korpershoek (5): boot: android: Provide vendor_bootimg_addr in boot_get_fdt() bootstd: Add bootflow_iter_check_mmc() helper android: boot: Add set_abootimg_addr() and set_avendor_bootimg_addr() bootstd: Add a bootmeth for Android bootstd: Add test for bootmeth_android
MAINTAINERS | 7 + arch/sandbox/dts/test.dts | 8 + boot/Kconfig | 16 ++ boot/Makefile | 2 + boot/bootflow.c | 12 + boot/bootmeth_android.c | 553 ++++++++++++++++++++++++++++++++++++++++++++++ boot/bootmeth_android.h | 29 +++ boot/image-android.c | 5 + boot/image-fdt.c | 2 +- cmd/abootimg.c | 10 + configs/sandbox_defconfig | 2 +- doc/develop/bootstd.rst | 6 + include/bootflow.h | 9 + include/image.h | 14 ++ test/boot/bootflow.c | 65 +++++- test/py/tests/test_ut.py | 76 +++++++ 16 files changed, 811 insertions(+), 5 deletions(-)
base-commit: f9886bc60f42d5bcfcfa4e474af7dc230400b6be change-id: 20240605-bootmeth-android-bfc8596e9367
Best regards,
Mattijs Korpershoek mkorpershoek@baylibre.com
Thinking about this, I believe we should start having docs about the individual bootmeths themselves.
Yes.
Can you add a section about your new bootmeth? I will come up with a patch for the others that I know about. Perhaps doc/develop/bootstd.rst would be a suitable place for now?
Yes I can add a section. I would have preferred to have an example to work from there, but I can start writing docs as well.
I'm leaving on vacation soon (without computer), so I'll be able to send a v3 with docs included in at earliest a 2-3 weeks from now.
It isn't a blocker for this series, just something I thought of.
For future reference, the doc has been submitted here:
https://lore.kernel.org/all/20240724-doc-bootstd-android-v1-1-03a55b7a7c92@b...
If you make a patch for the other bootmeths in the mean-time, please cc me so that I can help review and have an example for Android.
Yes, will do, if I do.
Regards, Simon

On Thu, Jun 13, 2024 at 12:13:07PM +0200, Mattijs Korpershoek wrote:
Android boot flow is a bit different than a regular Linux distro. Android relies on multiple partitions in order to boot.
A typical boot flow would be:
- Parse the Bootloader Control Block (BCB, misc partition)
- If BCB requested bootonce-bootloader, start fastboot and wait.
- If BCB requested recovery or normal android, run the following: a. Get slot (A/B) from BCB b. Run AVB (Android Verified Boot) on boot partitions c. Load boot and vendor_boot partitions d. Load device-tree, ramdisk and boot
The AOSP documentation has more details at [1], [2], [3]
This has been implemented via complex boot scripts such as [4]. However, these boot script are neither very maintainable nor generic. Moreover, DISTRO_DEFAULTS is being deprecated [5].
Add a generic Android bootflow implementation for bootstd.
For this initial version, only boot image v4 is supported.
This has been tested on sandbox using: $ ./test/py/test.py --bd sandbox --build -k test_ut
This has also been tested on the AM62X SK EVM using TI's Android SDK[6] To test on TI board, the following (WIP) patch is needed as well: https://gitlab.baylibre.com/baylibre/ti/ti-u-boot/-/commit/84cceb912bccd7cdd...
[1] https://source.android.com/docs/core/architecture/bootloader [2] https://source.android.com/docs/core/architecture/partitions [3] https://source.android.com/docs/core/architecture/partitions/generic-boot [4] https://source.denx.de/u-boot/u-boot/-/blob/master/include/configs/meson64_a... [5] https://lore.kernel.org/r/all/20230914165615.1058529-17-sjg@chromium.org/ [6] https://software-dl.ti.com/processor-sdk-android/esd/AM62X/09_02_00/docs/and...
This leads to failures in CI such as: =================================== FAILURES =================================== ___________________________ test_ut_dm_init_bootstd ____________________________ test/py/tests/test_ut.py:555: in test_ut_dm_init_bootstd setup_android_image(u_boot_console) test/py/tests/test_ut.py:488: in setup_android_image with open(boot_img, 'rb') as inf: E FileNotFoundError: [Errno 2] No such file or directory: '/tmp/malta64el/bootv4.img' ----------------------------- Captured stdout call -----------------------------

Hi Tom,
On jeu., juin 20, 2024 at 08:23, Tom Rini trini@konsulko.com wrote:
On Thu, Jun 13, 2024 at 12:13:07PM +0200, Mattijs Korpershoek wrote:
Android boot flow is a bit different than a regular Linux distro. Android relies on multiple partitions in order to boot.
A typical boot flow would be:
- Parse the Bootloader Control Block (BCB, misc partition)
- If BCB requested bootonce-bootloader, start fastboot and wait.
- If BCB requested recovery or normal android, run the following: a. Get slot (A/B) from BCB b. Run AVB (Android Verified Boot) on boot partitions c. Load boot and vendor_boot partitions d. Load device-tree, ramdisk and boot
The AOSP documentation has more details at [1], [2], [3]
This has been implemented via complex boot scripts such as [4]. However, these boot script are neither very maintainable nor generic. Moreover, DISTRO_DEFAULTS is being deprecated [5].
Add a generic Android bootflow implementation for bootstd.
For this initial version, only boot image v4 is supported.
This has been tested on sandbox using: $ ./test/py/test.py --bd sandbox --build -k test_ut
This has also been tested on the AM62X SK EVM using TI's Android SDK[6] To test on TI board, the following (WIP) patch is needed as well: https://gitlab.baylibre.com/baylibre/ti/ti-u-boot/-/commit/84cceb912bccd7cdd...
[1] https://source.android.com/docs/core/architecture/bootloader [2] https://source.android.com/docs/core/architecture/partitions [3] https://source.android.com/docs/core/architecture/partitions/generic-boot [4] https://source.denx.de/u-boot/u-boot/-/blob/master/include/configs/meson64_a... [5] https://lore.kernel.org/r/all/20230914165615.1058529-17-sjg@chromium.org/ [6] https://software-dl.ti.com/processor-sdk-android/esd/AM62X/09_02_00/docs/and...
This leads to failures in CI such as: =================================== FAILURES =================================== ___________________________ test_ut_dm_init_bootstd ____________________________ test/py/tests/test_ut.py:555: in test_ut_dm_init_bootstd setup_android_image(u_boot_console) test/py/tests/test_ut.py:488: in setup_android_image with open(boot_img, 'rb') as inf: E FileNotFoundError: [Errno 2] No such file or directory: '/tmp/malta64el/bootv4.img' ----------------------------- Captured stdout call -----------------------------
Thank you for reporting, and sorry about the CI failure. I think I need to somehow declare a dependency on the bootv4.img file.
I will send a v3 to fix this.
-- Tom
participants (6)
-
Guillaume LA ROQUE
-
Igor Opaniuk
-
Julien Masson
-
Mattijs Korpershoek
-
Simon Glass
-
Tom Rini