[U-Boot] [PATCH v4 0/4] Add 'bcb' command to read/modify/write Android BCB

The first patch does the necessary fixing and polishing of include/android_bootloader_message.h and is a hard prerequisite for this series.
The second patch performs the implementation of the 'bcb' command. The third patch relocates the Android README to doc/android. The fourth patch describes the motivation and foreseeable use-cases behind the 'bcb' command in the newly created doc/android/bcb.txt.
v4: - [Simon Glass] Non-functional refinements in cmd/bcb.c - [Sam Protsenko] Fix stale references of Android docs - Place detailed stats in each patch. Full v3 to v4 series interdiff: https://pastebin.ubuntu.com/p/zD66FwBznt/ - Retest on Renesas R-Car H3-ES20 ULCB-KF-M06 using U-Boot v2019.07-rc4-358-g1f83431f0053 as base
v3: - [Simon Glass] Lots of review comments handled in cmd/bcb.c. - [Simon Glass, Sam Protsenko] Renamed and enriched android docs. - Placed detailed stats in each patch. - https://patchwork.ozlabs.org/cover/1104242/
v2: - [Heinrich Schuchardt] Implement sub-commands via U_BOOT_CMD_MKENT. - Polished the code. Ensured no warnings returned by sparse, smatch, `cppcheck --force --enable=all --inconclusive`, make W=1. - Tested on R-Car-H3-ES20 ULCB-KF. - https://patchwork.ozlabs.org/cover/1101106/
v1: - https://patchwork.ozlabs.org/cover/1080393/
Eugeniu Rosca (4): include: android_bootloader_message.h: Minimize the diff to AOSP cmd: Add 'bcb' command to read/modify/write BCB fields doc: android: relocate/rename README files doc: android: add BCB overview and usage
cmd/Kconfig | 19 +- cmd/Makefile | 1 + cmd/bcb.c | 338 ++++++++++++++++++ doc/{README.avb2 => android/avb2.txt} | 0 doc/android/bcb.txt | 89 +++++ .../fastboot-protocol.txt} | 0 .../fastboot.txt} | 2 +- include/android_bootloader_message.h | 126 ++++--- test/py/tests/test_avb.py | 2 +- 9 files changed, 516 insertions(+), 61 deletions(-) create mode 100644 cmd/bcb.c rename doc/{README.avb2 => android/avb2.txt} (100%) create mode 100644 doc/android/bcb.txt rename doc/{README.android-fastboot-protocol => android/fastboot-protocol.txt} (100%) rename doc/{README.android-fastboot => android/fastboot.txt} (98%)

Perform the following updates: - Relocate the commit id from the file to the description of U-Boot commit. The AOSP commit is c784ce50e8c10eaf70e1f97e24e8324aef45faf5. This is done to avoid stale references in the file itself. The reasoning is in https://patchwork.ozlabs.org/patch/1098056/#2170209. - Minimize the diff to AOSP, to decrease the effort of the next AOSP backports. The background can be found in: https://patchwork.ozlabs.org/patch/1080394/#2168454. - Guard the static_assert() calls by #ifndef __UBOOT__ ... #endif, to avoid compilation failures of files including the header.
Signed-off-by: Eugeniu Rosca erosca@de.adit-jv.com Reviewed-by: Sam Protsenko semen.protsenko@linaro.org Reviewed-by: Simon Glass sjg@chromium.org --- v4: - No changes in the contents. - Picked up 'Reviewed-by: Simon Glass' from: https://patchwork.ozlabs.org/patch/1104243/#2200278 v3: - Added 'Reviewed-by: Sam Protsenko' - https://patchwork.ozlabs.org/patch/1104243/ v2: - Newly pushed. No changes. - https://patchwork.ozlabs.org/patch/1101108/ --- include/android_bootloader_message.h | 126 +++++++++++++++------------ 1 file changed, 68 insertions(+), 58 deletions(-)
diff --git a/include/android_bootloader_message.h b/include/android_bootloader_message.h index b84789f02227..286d7ab0f31e 100644 --- a/include/android_bootloader_message.h +++ b/include/android_bootloader_message.h @@ -2,7 +2,7 @@ * This is from the Android Project, * Repository: https://android.googlesource.com/platform/bootable/recovery * File: bootloader_message/include/bootloader_message/bootloader_message.h - * Commit: c784ce50e8c10eaf70e1f97e24e8324aef45faf5 + * Commit: See U-Boot commit description * * Copyright (C) 2008 The Android Open Source Project * @@ -12,18 +12,24 @@ #ifndef __ANDROID_BOOTLOADER_MESSAGE_H #define __ANDROID_BOOTLOADER_MESSAGE_H
+#ifndef __UBOOT__ +#include <assert.h> +#include <stddef.h> +#include <stdint.h> +#else /* compiler.h defines the types that otherwise are included from stdint.h and * stddef.h */ #include <compiler.h> +#endif
-/* Spaces used by misc partition are as below: - * 0 - 2K For bootloader_message - * 2K - 16K Used by Vendor's bootloader (the 2K - 4K range may be optionally used - * as bootloader_message_ab struct) - * 16K - 64K Used by uncrypt and recovery to store wipe_package for A/B devices - * Note that these offsets are admitted by bootloader,recovery and uncrypt, so they - * are not configurable without changing all of them. */ +// Spaces used by misc partition are as below: +// 0 - 2K For bootloader_message +// 2K - 16K Used by Vendor's bootloader (the 2K - 4K range may be optionally used +// as bootloader_message_ab struct) +// 16K - 64K Used by uncrypt and recovery to store wipe_package for A/B devices +// Note that these offsets are admitted by bootloader,recovery and uncrypt, so they +// are not configurable without changing all of them. static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0; static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024;
@@ -61,17 +67,17 @@ struct bootloader_message { char status[32]; char recovery[768];
- /* The 'recovery' field used to be 1024 bytes. It has only ever - * been used to store the recovery command line, so 768 bytes - * should be plenty. We carve off the last 256 bytes to store the - * stage string (for multistage packages) and possible future - * expansion. */ + // The 'recovery' field used to be 1024 bytes. It has only ever + // been used to store the recovery command line, so 768 bytes + // should be plenty. We carve off the last 256 bytes to store the + // stage string (for multistage packages) and possible future + // expansion. char stage[32];
- /* The 'reserved' field used to be 224 bytes when it was initially - * carved off from the 1024-byte recovery field. Bump it up to - * 1184-byte so that the entire bootloader_message struct rounds up - * to 2048-byte. */ + // The 'reserved' field used to be 224 bytes when it was initially + // carved off from the 1024-byte recovery field. Bump it up to + // 1184-byte so that the entire bootloader_message struct rounds up + // to 2048-byte. char reserved[1184]; };
@@ -79,10 +85,12 @@ struct bootloader_message { * We must be cautious when changing the bootloader_message struct size, * because A/B-specific fields may end up with different offsets. */ +#ifndef __UBOOT__ #if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus) static_assert(sizeof(struct bootloader_message) == 2048, "struct bootloader_message size changes, which may break A/B devices"); #endif +#endif /* __UBOOT__ */
/** * The A/B-specific bootloader message structure (4-KiB). @@ -108,7 +116,7 @@ struct bootloader_message_ab { char slot_suffix[32]; char update_channel[128];
- /* Round up the entire struct to 4096-byte. */ + // Round up the entire struct to 4096-byte. char reserved[1888]; };
@@ -116,26 +124,28 @@ struct bootloader_message_ab { * Be cautious about the struct size change, in case we put anything post * bootloader_message_ab struct (b/29159185). */ +#ifndef __UBOOT__ #if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus) static_assert(sizeof(struct bootloader_message_ab) == 4096, "struct bootloader_message_ab size changes"); #endif +#endif /* __UBOOT__ */
#define BOOT_CTRL_MAGIC 0x42414342 /* Bootloader Control AB */ #define BOOT_CTRL_VERSION 1
struct slot_metadata { - /* Slot priority with 15 meaning highest priority, 1 lowest - * priority and 0 the slot is unbootable. */ + // Slot priority with 15 meaning highest priority, 1 lowest + // priority and 0 the slot is unbootable. uint8_t priority : 4; - /* Number of times left attempting to boot this slot. */ + // Number of times left attempting to boot this slot. uint8_t tries_remaining : 3; - /* 1 if this slot has booted successfully, 0 otherwise. */ + // 1 if this slot has booted successfully, 0 otherwise. uint8_t successful_boot : 1; - /* 1 if this slot is corrupted from a dm-verity corruption, 0 - * otherwise. */ + // 1 if this slot is corrupted from a dm-verity corruption, 0 + // otherwise. uint8_t verity_corrupted : 1; - /* Reserved for further use. */ + // Reserved for further use. uint8_t reserved : 7; } __attribute__((packed));
@@ -148,99 +158,99 @@ struct slot_metadata { * mandatory. */ struct bootloader_control { - /* NUL terminated active slot suffix. */ + // NUL terminated active slot suffix. char slot_suffix[4]; - /* Bootloader Control AB magic number (see BOOT_CTRL_MAGIC). */ + // Bootloader Control AB magic number (see BOOT_CTRL_MAGIC). uint32_t magic; - /* Version of struct being used (see BOOT_CTRL_VERSION). */ + // Version of struct being used (see BOOT_CTRL_VERSION). uint8_t version; - /* Number of slots being managed. */ + // Number of slots being managed. uint8_t nb_slot : 3; - /* Number of times left attempting to boot recovery. */ + // Number of times left attempting to boot recovery. uint8_t recovery_tries_remaining : 3; - /* Ensure 4-bytes alignment for slot_info field. */ + // Ensure 4-bytes alignment for slot_info field. uint8_t reserved0[2]; - /* Per-slot information. Up to 4 slots. */ + // Per-slot information. Up to 4 slots. struct slot_metadata slot_info[4]; - /* Reserved for further use. */ + // Reserved for further use. uint8_t reserved1[8]; - /* CRC32 of all 28 bytes preceding this field (little endian - * format). */ + // CRC32 of all 28 bytes preceding this field (little endian + // format). uint32_t crc32_le; } __attribute__((packed));
+#ifndef __UBOOT__ #if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus) static_assert(sizeof(struct bootloader_control) == sizeof(((struct bootloader_message_ab *)0)->slot_suffix), "struct bootloader_control has wrong size"); #endif +#endif /* __UBOOT__ */
#ifndef __UBOOT__ - #ifdef __cplusplus
#include <string> #include <vector>
-/* Return the block device name for the bootloader message partition and waits - * for the device for up to 10 seconds. In case of error returns the empty - * string. */ +// Return the block device name for the bootloader message partition and waits +// for the device for up to 10 seconds. In case of error returns the empty +// string. std::string get_bootloader_message_blk_device(std::string* err);
-/* Read bootloader message into boot. Error message will be set in err. */ +// Read bootloader message into boot. Error message will be set in err. bool read_bootloader_message(bootloader_message* boot, std::string* err);
-/* Read bootloader message from the specified misc device into boot. */ +// Read bootloader message from the specified misc device into boot. bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device, std::string* err);
-/* Write bootloader message to BCB. */ +// Write bootloader message to BCB. bool write_bootloader_message(const bootloader_message& boot, std::string* err);
-/* Write bootloader message to the specified BCB device. */ +// Write bootloader message to the specified BCB device. bool write_bootloader_message_to(const bootloader_message& boot, const std::string& misc_blk_device, std::string* err);
-/* Write bootloader message (boots into recovery with the options) to BCB. Will - * set the command and recovery fields, and reset the rest. */ +// Write bootloader message (boots into recovery with the options) to BCB. Will +// set the command and recovery fields, and reset the rest. bool write_bootloader_message(const std::vectorstd::string& options, std::string* err);
-/* Write bootloader message (boots into recovery with the options) to the specific BCB device. Will - * set the command and recovery fields, and reset the rest. */ +// Write bootloader message (boots into recovery with the options) to the specific BCB device. Will +// set the command and recovery fields, and reset the rest. bool write_bootloader_message_to(const std::vectorstd::string& options, const std::string& misc_blk_device, std::string* err);
-/* Update bootloader message (boots into recovery with the options) to BCB. Will - * only update the command and recovery fields. */ +// Update bootloader message (boots into recovery with the options) to BCB. Will +// only update the command and recovery fields. bool update_bootloader_message(const std::vectorstd::string& options, std::string* err);
-/* Update bootloader message (boots into recovery with the |options|) in |boot|. Will only update - * the command and recovery fields. */ +// Update bootloader message (boots into recovery with the |options|) in |boot|. Will only update +// the command and recovery fields. bool update_bootloader_message_in_struct(bootloader_message* boot, const std::vectorstd::string& options);
-/* Clear BCB. */ +// Clear BCB. bool clear_bootloader_message(std::string* err);
-/* Writes the reboot-bootloader reboot reason to the bootloader_message. */ +// Writes the reboot-bootloader reboot reason to the bootloader_message. bool write_reboot_bootloader(std::string* err);
-/* Read the wipe package from BCB (from offset WIPE_PACKAGE_OFFSET_IN_MISC). */ +// Read the wipe package from BCB (from offset WIPE_PACKAGE_OFFSET_IN_MISC). bool read_wipe_package(std::string* package_data, size_t size, std::string* err);
-/* Write the wipe package into BCB (to offset WIPE_PACKAGE_OFFSET_IN_MISC). */ +// Write the wipe package into BCB (to offset WIPE_PACKAGE_OFFSET_IN_MISC). bool write_wipe_package(const std::string& package_data, std::string* err);
#else
#include <stdbool.h>
-/* C Interface. */ +// C Interface. bool write_bootloader_message(const char* options); bool write_reboot_bootloader(void);
-#endif /* ifdef __cplusplus */ - +#endif // ifdef __cplusplus #endif /* __UBOOT__ */
#endif /* __ANDROID_BOOTLOADER_MESSAGE_H */

'Bootloader Control Block' (BCB) is a well established term/acronym in the Android namespace which refers to a location in a dedicated raw (i.e. FS-unaware) flash (e.g. eMMC) partition, usually called "misc", which is used as media for exchanging messages between Android userspace (particularly recovery [1]) and an Android-capable bootloader.
On higher level, this allows implementing a subset of Android Bootloader Requirements [2], amongst which is the Android-specific bootloader flow [3]. Regardless how the latter is implemented in U-Boot ([3] being the most memorable example), reading/writing/dumping the BCB fields in the development process from inside the U-Boot is a convenient feature. Hence, make it available to the users.
Some usage examples of the new command recorded on R-Car H3ULCB-KF ('>>>' is an overlay on top of the original console output):
=> bcb bcb - Load/set/clear/test/dump/store Android BCB fields
Usage: bcb load <dev> <part> - load BCB from mmc <dev>:<part> bcb set <field> <val> - set BCB <field> to <val> bcb clear [<field>] - clear BCB <field> or all fields bcb test <field> <op> <val> - test BCB <field> against <val> bcb dump <field> - dump BCB <field> bcb store - store BCB back to mmc
Legend: <dev> - MMC device index containing the BCB partition <part> - MMC partition index or name containing the BCB <field> - one of {command,status,recovery,stage,reserved} <op> - the binary operator used in 'bcb test': '=' returns true if <val> matches the string stored in <field> '~' returns true if <val> matches a subset of <field>'s string <val> - string/text provided as input to bcb {set,test} NOTE: any ':' character in <val> will be replaced by line feed during 'bcb set' and used as separator by upper layers
=> bcb dump command Error: Please, load BCB first!
Users must specify mmc device and partition before any other call
=> bcb load 1 misc => bcb load 1 1
The two calls are equivalent (assuming "misc" has index 1)
=> bcb dump command 00000000: 62 6f 6f 74 6f 6e 63 65 2d 73 68 65 6c 6c 00 72 bootonce-shell.r 00000010: 79 00 72 00 00 00 00 00 00 00 00 00 00 00 00 00 y.r.............
The output is in binary/string format for convenience The output size matches the size of inspected BCB field (32 bytes in case of 'command')
=> bcb test command = bootonce-shell && echo true true => bcb test command = bootonce-shell- && echo true => bcb test command = bootonce-shel && echo true
The '=' operator returns 'true' on perfect match
=> bcb test command ~ bootonce-shel && echo true true => bcb test command ~ bootonce-shell && echo true true
The '~' operator returns 'true' on substring match
=> bcb set command recovery => bcb dump command 00000000: 72 65 63 6f 76 65 72 79 00 73 68 65 6c 6c 00 72 recovery.shell.r 00000010: 79 00 72 00 00 00 00 00 00 00 00 00 00 00 00 00 y.r.............
The new value is NULL-terminated and stored in the BCB field
=> bcb set recovery "msg1:msg2:msg3" => bcb dump recovery 00000040: 6d 73 67 31 0a 6d 73 67 32 0a 6d 73 67 33 00 00 msg1.msg2.msg3.. 00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
--- snip --- Every ':' is replaced by line-feed '\n' (0xA). The latter is used as separator between individual commands by Android userspace
=> bcb store
Flush/store the BCB structure to MMC
[1] https://android.googlesource.com/platform/bootable/recovery [2] https://source.android.com/devices/bootloader [3] https://patchwork.ozlabs.org/patch/746835/ ("[U-Boot,5/6] Initial support for the Android Bootloader flow")
Signed-off-by: Eugeniu Rosca erosca@de.adit-jv.com --- v4: - [Simon Glass] Use strcmp instead of strncmp whenever one of the arguments is a string literal - [Simon Glass] s/field/fieldp/, s/size/sizep/ in bcb_field_get() - [Simon Glass] Place 'static int do_bcb_load' on one line - [Simon Glass] Refine the goto labels in do_bcb_load() - [Simon Glass] Refine the comment in do_bcb() - Merge two identical case-branches in bcb_is_misused() - Rerun static analysis (cppcheck, sparse, smatch, make W=1) - Retest on Renesas R-Car H3-ES2.0-ULCB-KF-M06 - Compile/boot/smoke-test on sandbox v3: - [Simon Glass] Allow 'strsep' to modify the argv[n] string in-place rather than duplicating it with 'strdup'. Remove #include <malloc.h> - [Simon Glass] Call bcb_is_misused() _once_ in do_bcb() - [Simon Glass] Report error codes if IO fails in do_bcb_{load,store} - [Simon Glass] Leave one empty line above top-level returns (note: checkpatch is indifferent about this) - [Simon Glass] Replace strncmp with '==' operator for single-character strings in do_bcb_test(), which reduces compiled object size - Reorder the functions to match the cmd_bcb_sub table - Improve error reporting: - s/Error: Unknown field/Error: Unknown bcb field/ - s/debug Error: Unexpected BCB command/ printf Error: 'bcb %s' not supported/ - s/Error: BCB not loaded/Error: Please, load BCB first/ - Make sure static analysis is clean: - cppcheck --force --enable=all --inconclusive - sparse/make C=2 - make W=1 - smatch - Re-test on R-Car H3ULCB-KF - Compile/boot/smoke-test on sandbox - https://patchwork.ozlabs.org/patch/1104244/ v2: - [Heinrich Schuchardt] Implement sub-commands via U_BOOT_CMD_MKENT. - Polished the code. Ensured no warnings returned by sparse, smatch, `cppcheck --force --enable=all --inconclusive`, make W=1. - Tested on R-Car-H3-ES20 ULCB-KF. - https://patchwork.ozlabs.org/patch/1101107/
v1: - https://patchwork.ozlabs.org/patch/1080395/ --- cmd/Kconfig | 17 +++ cmd/Makefile | 1 + cmd/bcb.c | 338 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 356 insertions(+) create mode 100644 cmd/bcb.c
diff --git a/cmd/Kconfig b/cmd/Kconfig index cda7931fe360..3afb760a816f 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -631,6 +631,23 @@ config CMD_ADC Shows ADC device info and permit printing one-shot analog converted data from a named Analog to Digital Converter.
+config CMD_BCB + bool "bcb" + depends on MMC + depends on PARTITIONS + help + Read/modify/write the fields of Bootloader Control Block, usually + stored on the flash "misc" partition with its structure defined in: + https://android.googlesource.com/platform/bootable/recovery/+/master/ + bootloader_message/include/bootloader_message/bootloader_message.h + + Some real-life use-cases include (but are not limited to): + - Determine the "boot reason" (and act accordingly): + https://source.android.com/devices/bootloader/boot-reason + - Get/pass a list of commands from/to recovery: + https://android.googlesource.com/platform/bootable/recovery + - Inspect/dump the contents of the BCB fields + config CMD_BIND bool "bind/unbind - Bind or unbind a device to/from a driver" depends on DM diff --git a/cmd/Makefile b/cmd/Makefile index f982564ab9f5..49e64cde1d07 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_CMD_ADC) += adc.o obj-$(CONFIG_CMD_ARMFLASH) += armflash.o obj-y += blk_common.o obj-$(CONFIG_CMD_SOURCE) += source.o +obj-$(CONFIG_CMD_BCB) += bcb.o obj-$(CONFIG_CMD_BDI) += bdinfo.o obj-$(CONFIG_CMD_BEDBUG) += bedbug.o obj-$(CONFIG_CMD_BIND) += bind.o diff --git a/cmd/bcb.c b/cmd/bcb.c new file mode 100644 index 000000000000..9626f2c69e34 --- /dev/null +++ b/cmd/bcb.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Eugeniu Rosca rosca.eugeniu@gmail.com + * + * Command to read/modify/write Android BCB fields + */ + +#include <android_bootloader_message.h> +#include <command.h> +#include <common.h> + +enum bcb_cmd { + BCB_CMD_LOAD, + BCB_CMD_FIELD_SET, + BCB_CMD_FIELD_CLEAR, + BCB_CMD_FIELD_TEST, + BCB_CMD_FIELD_DUMP, + BCB_CMD_STORE, +}; + +static int bcb_dev = -1; +static int bcb_part = -1; +static struct bootloader_message bcb = { { 0 } }; + +static int bcb_cmd_get(char *cmd) +{ + if (!strcmp(cmd, "load")) + return BCB_CMD_LOAD; + if (!strcmp(cmd, "set")) + return BCB_CMD_FIELD_SET; + if (!strcmp(cmd, "clear")) + return BCB_CMD_FIELD_CLEAR; + if (!strcmp(cmd, "test")) + return BCB_CMD_FIELD_TEST; + if (!strcmp(cmd, "store")) + return BCB_CMD_STORE; + if (!strcmp(cmd, "dump")) + return BCB_CMD_FIELD_DUMP; + else + return -1; +} + +static int bcb_is_misused(int argc, char *const argv[]) +{ + int cmd = bcb_cmd_get(argv[0]); + + switch (cmd) { + case BCB_CMD_LOAD: + case BCB_CMD_FIELD_SET: + if (argc != 3) + goto err; + break; + case BCB_CMD_FIELD_TEST: + if (argc != 4) + goto err; + break; + case BCB_CMD_FIELD_CLEAR: + if (argc != 1 && argc != 2) + goto err; + break; + case BCB_CMD_STORE: + if (argc != 1) + goto err; + break; + case BCB_CMD_FIELD_DUMP: + if (argc != 2) + goto err; + break; + default: + printf("Error: 'bcb %s' not supported\n", argv[0]); + return -1; + } + + if (cmd != BCB_CMD_LOAD && (bcb_dev < 0 || bcb_part < 0)) { + printf("Error: Please, load BCB first!\n"); + return -1; + } + + return 0; +err: + printf("Error: Bad usage of 'bcb %s'\n", argv[0]); + + return -1; +} + +static int bcb_field_get(char *name, char **fieldp, int *sizep) +{ + if (!strcmp(name, "command")) { + *fieldp = bcb.command; + *sizep = sizeof(bcb.command); + } else if (!strcmp(name, "status")) { + *fieldp = bcb.status; + *sizep = sizeof(bcb.status); + } else if (!strcmp(name, "recovery")) { + *fieldp = bcb.recovery; + *sizep = sizeof(bcb.recovery); + } else if (!strcmp(name, "stage")) { + *fieldp = bcb.stage; + *sizep = sizeof(bcb.stage); + } else if (!strcmp(name, "reserved")) { + *fieldp = bcb.reserved; + *sizep = sizeof(bcb.reserved); + } else { + printf("Error: Unknown bcb field '%s'\n", name); + return -1; + } + + return 0; +} + +static int do_bcb_load(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + struct blk_desc *desc; + disk_partition_t info; + u64 cnt; + char *endp; + int part, ret; + + ret = blk_get_device_by_str("mmc", argv[1], &desc); + if (ret < 0) + goto err_read_fail; + + part = simple_strtoul(argv[2], &endp, 0); + if (*endp == '\0') { + ret = part_get_info(desc, part, &info); + if (ret) + goto err_read_fail; + } else { + part = part_get_info_by_name(desc, argv[2], &info); + if (part < 0) { + ret = part; + goto err_read_fail; + } + } + + cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), info.blksz); + if (cnt > info.size) + goto err_too_small; + + if (blk_dread(desc, info.start, cnt, &bcb) != cnt) { + ret = -EIO; + goto err_read_fail; + } + + bcb_dev = desc->devnum; + bcb_part = part; + debug("%s: Loaded from mmc %d:%d\n", __func__, bcb_dev, bcb_part); + + return CMD_RET_SUCCESS; +err_read_fail: + printf("Error: mmc %s:%s read failed (%d)\n", argv[1], argv[2], ret); + goto err; +err_too_small: + printf("Error: mmc %s:%s too small!", argv[1], argv[2]); + goto err; +err: + bcb_dev = -1; + bcb_part = -1; + + return CMD_RET_FAILURE; +} + +static int do_bcb_set(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + int size, len; + char *field, *str, *found; + + if (bcb_field_get(argv[1], &field, &size)) + return CMD_RET_FAILURE; + + len = strlen(argv[2]); + if (len >= size) { + printf("Error: sizeof('%s') = %d >= %d = sizeof(bcb.%s)\n", + argv[2], len, size, argv[1]); + return CMD_RET_FAILURE; + } + str = argv[2]; + + field[0] = '\0'; + while ((found = strsep(&str, ":"))) { + if (field[0] != '\0') + strcat(field, "\n"); + strcat(field, found); + } + + return CMD_RET_SUCCESS; +} + +static int do_bcb_clear(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + int size; + char *field; + + if (argc == 1) { + memset(&bcb, 0, sizeof(bcb)); + return CMD_RET_SUCCESS; + } + + if (bcb_field_get(argv[1], &field, &size)) + return CMD_RET_FAILURE; + + memset(field, 0, size); + + return CMD_RET_SUCCESS; +} + +static int do_bcb_test(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + int size; + char *field; + char *op = argv[2]; + + if (bcb_field_get(argv[1], &field, &size)) + return CMD_RET_FAILURE; + + if (*op == '=' && *(op + 1) == '\0') { + if (!strncmp(argv[3], field, size)) + return CMD_RET_SUCCESS; + else + return CMD_RET_FAILURE; + } else if (*op == '~' && *(op + 1) == '\0') { + if (!strstr(field, argv[3])) + return CMD_RET_FAILURE; + else + return CMD_RET_SUCCESS; + } else { + printf("Error: Unknown operator '%s'\n", op); + } + + return CMD_RET_FAILURE; +} + +static int do_bcb_dump(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + int size; + char *field; + + if (bcb_field_get(argv[1], &field, &size)) + return CMD_RET_FAILURE; + + print_buffer((ulong)field - (ulong)&bcb, (void *)field, 1, size, 16); + + return CMD_RET_SUCCESS; +} + +static int do_bcb_store(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + struct blk_desc *desc; + disk_partition_t info; + u64 cnt; + int ret; + + desc = blk_get_devnum_by_type(IF_TYPE_MMC, bcb_dev); + if (!desc) { + ret = -ENODEV; + goto err; + } + + ret = part_get_info(desc, bcb_part, &info); + if (ret) + goto err; + + cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), info.blksz); + + if (blk_dwrite(desc, info.start, cnt, &bcb) != cnt) { + ret = -EIO; + goto err; + } + + return CMD_RET_SUCCESS; +err: + printf("Error: mmc %d:%d write failed (%d)\n", bcb_dev, bcb_part, ret); + + return CMD_RET_FAILURE; +} + +static cmd_tbl_t cmd_bcb_sub[] = { + U_BOOT_CMD_MKENT(load, CONFIG_SYS_MAXARGS, 1, do_bcb_load, "", ""), + U_BOOT_CMD_MKENT(set, CONFIG_SYS_MAXARGS, 1, do_bcb_set, "", ""), + U_BOOT_CMD_MKENT(clear, CONFIG_SYS_MAXARGS, 1, do_bcb_clear, "", ""), + U_BOOT_CMD_MKENT(test, CONFIG_SYS_MAXARGS, 1, do_bcb_test, "", ""), + U_BOOT_CMD_MKENT(dump, CONFIG_SYS_MAXARGS, 1, do_bcb_dump, "", ""), + U_BOOT_CMD_MKENT(store, CONFIG_SYS_MAXARGS, 1, do_bcb_store, "", ""), +}; + +static int do_bcb(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + cmd_tbl_t *c; + + if (argc < 2) + return CMD_RET_USAGE; + + argc--; + argv++; + + c = find_cmd_tbl(argv[0], cmd_bcb_sub, ARRAY_SIZE(cmd_bcb_sub)); + if (!c) + return CMD_RET_USAGE; + + if (bcb_is_misused(argc, argv)) { + /* + * We try to improve the user experience by reporting the + * root-cause of misusage, so don't return CMD_RET_USAGE, + * since the latter prints out the full-blown help text + */ + return CMD_RET_FAILURE; + } + + return c->cmd(cmdtp, flag, argc, argv); +} + +U_BOOT_CMD( + bcb, CONFIG_SYS_MAXARGS, 1, do_bcb, + "Load/set/clear/test/dump/store Android BCB fields", + "load <dev> <part> - load BCB from mmc <dev>:<part>\n" + "bcb set <field> <val> - set BCB <field> to <val>\n" + "bcb clear [<field>] - clear BCB <field> or all fields\n" + "bcb test <field> <op> <val> - test BCB <field> against <val>\n" + "bcb dump <field> - dump BCB <field>\n" + "bcb store - store BCB back to mmc\n" + "\n" + "Legend:\n" + "<dev> - MMC device index containing the BCB partition\n" + "<part> - MMC partition index or name containing the BCB\n" + "<field> - one of {command,status,recovery,stage,reserved}\n" + "<op> - the binary operator used in 'bcb test':\n" + " '=' returns true if <val> matches the string stored in <field>\n" + " '~' returns true if <val> matches a subset of <field>'s string\n" + "<val> - string/text provided as input to bcb {set,test}\n" + " NOTE: any ':' character in <val> will be replaced by line feed\n" + " during 'bcb set' and used as separator by upper layers\n" +);

Rename: - doc/{README.avb2 => android/avb2.txt} - doc/{README.android-fastboot => android/fastboot.txt} - doc/{README.android-fastboot-protocol => android/fastboot-protocol.txt}
The new directory structure has been reviewed by Simon [0].
Thanks to Sam [1], update a number of stale references in: - cmd/Kconfig - test/py/tests/test_avb.py
As per Simon [2], any additions to doc/android/ (e.g. bcb.txt) should come as standalone patches.
[0] https://patchwork.ozlabs.org/patch/1101107/#2176031 [1] https://patchwork.ozlabs.org/patch/1104245/#2208134 [2] https://patchwork.ozlabs.org/patch/1104245/#2200256
Signed-off-by: Eugeniu Rosca erosca@de.adit-jv.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Sam Protsenko semen.protsenko@linaro.org --- v4: - [Simon Glass] Defer bcb.txt addition to a separate patch - [Sam Protsenko] Update stale doc references - Rename one more lost doc: doc/{README.android-fastboot-protocol => android/fastboot-protocol.txt} - Refine summary line - Pick up 'Reviewed-by: Simon Glass' from: https://patchwork.ozlabs.org/patch/1104245/#2200256 - Pick up 'Reviewed-by: Sam Protsenko' from: https://patchwork.ozlabs.org/cover/1104242/#2177898 v3: - Newly pushed. - https://patchwork.ozlabs.org/patch/1104245/ --- cmd/Kconfig | 2 +- doc/{README.avb2 => android/avb2.txt} | 0 .../fastboot-protocol.txt} | 0 doc/{README.android-fastboot => android/fastboot.txt} | 2 +- test/py/tests/test_avb.py | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) rename doc/{README.avb2 => android/avb2.txt} (100%) rename doc/{README.android-fastboot-protocol => android/fastboot-protocol.txt} (100%) rename doc/{README.android-fastboot => android/fastboot.txt} (98%)
diff --git a/cmd/Kconfig b/cmd/Kconfig index 3afb760a816f..3cf8233df62e 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -707,7 +707,7 @@ config CMD_FASTBOOT Android devices. Fastboot requires either the network stack enabled or support for acting as a USB device.
- See doc/README.android-fastboot for more information. + See doc/android/fastboot.txt for more information.
config CMD_FDC bool "fdcboot - Boot from floppy device" diff --git a/doc/README.avb2 b/doc/android/avb2.txt similarity index 100% rename from doc/README.avb2 rename to doc/android/avb2.txt diff --git a/doc/README.android-fastboot-protocol b/doc/android/fastboot-protocol.txt similarity index 100% rename from doc/README.android-fastboot-protocol rename to doc/android/fastboot-protocol.txt diff --git a/doc/README.android-fastboot b/doc/android/fastboot.txt similarity index 98% rename from doc/README.android-fastboot rename to doc/android/fastboot.txt index 431191c473f2..dcf824713f94 100644 --- a/doc/README.android-fastboot +++ b/doc/android/fastboot.txt @@ -6,7 +6,7 @@ Overview ========
The protocol that is used over USB and UDP is described in the -``README.android-fastboot-protocol`` file in the same directory. +``fastboot-protocol.txt`` file in the same directory.
The current implementation supports the following standard commands:
diff --git a/test/py/tests/test_avb.py b/test/py/tests/test_avb.py index 2bb75ed6e2a2..813242343555 100644 --- a/test/py/tests/test_avb.py +++ b/test/py/tests/test_avb.py @@ -8,7 +8,7 @@ This tests Android Verified Boot 2.0 support in U-boot:
For additional details about how to build proper vbmeta partition -check doc/README.avb2 +check doc/android/avb2.txt
For configuration verification: - Corrupt boot partition and check for failure

Document the BCB concept and U-Boot command, touching below aspects: - give an overview of BCB w/o duplicating public documentation - describe the main BCB use-cases which concern U-Boot - reflect current support status in U-Boot - mention any relevant U-Boot build-time tunables - precisely exemplify one or more use-cases
Signed-off-by: Eugeniu Rosca erosca@de.adit-jv.com --- v4: - This is carved out from https://patchwork.ozlabs.org/patch/1104245/ ("[v3,3/3] doc: relocate/rename Android README and add BCB overview") with no modifications. --- doc/android/bcb.txt | 89 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 doc/android/bcb.txt
diff --git a/doc/android/bcb.txt b/doc/android/bcb.txt new file mode 100644 index 000000000000..7b7177cacf21 --- /dev/null +++ b/doc/android/bcb.txt @@ -0,0 +1,89 @@ +Android Bootloader Control Block (BCB) + +The purpose behind this file is to: + - give an overview of BCB w/o duplicating public documentation + - describe the main BCB use-cases which concern U-Boot + - reflect current support status in U-Boot + - mention any relevant U-Boot build-time tunables + - precisely exemplify one or more use-cases + +Additions and fixes are welcome! + + +1. OVERVIEW +--------------------------------- +Bootloader Control Block (BCB) is a well established term/acronym in +the Android namespace which refers to a location in a dedicated raw +(i.e. FS-unaware) flash (e.g. eMMC) partition, usually called "misc", +which is used as media for exchanging messages between Android userspace +(particularly recovery [1]) and an Android-capable bootloader. + +On higher level, BCB provides a way to implement a subset of Android +Bootloader Requirements [2], amongst which are: + - Android-specific bootloader flow [3] + - Get the "reboot reason" (and act accordingly) [4] + - Get/pass a list of commands from/to recovery [1] + - TODO + + +2. 'BCB'. SHELL COMMAND OVERVIEW +----------------------------------- +The 'bcb' command provides a CLI to facilitate the development of the +requirements enumerated above. Below is the command's help message: + +=> bcb +bcb - Load/set/clear/test/dump/store Android BCB fields + +Usage: +bcb load <dev> <part> - load BCB from mmc <dev>:<part> +bcb set <field> <val> - set BCB <field> to <val> +bcb clear [<field>] - clear BCB <field> or all fields +bcb test <field> <op> <val> - test BCB <field> against <val> +bcb dump <field> - dump BCB <field> +bcb store - store BCB back to mmc + +Legend: +<dev> - MMC device index containing the BCB partition +<part> - MMC partition index or name containing the BCB +<field> - one of {command,status,recovery,stage,reserved} +<op> - the binary operator used in 'bcb test': + '=' returns true if <val> matches the string stored in <field> + '~' returns true if <val> matches a subset of <field>'s string +<val> - string/text provided as input to bcb {set,test} + NOTE: any ':' character in <val> will be replaced by line feed + during 'bcb set' and used as separator by upper layers + + +3. 'BCB'. EXAMPLE OF GETTING REBOOT REASON +----------------------------------- +if bcb load 1 misc; then + # valid BCB found + if bcb test command = bootonce-bootloader; then + bcb clear command; bcb store; + # do the equivalent of AOSP ${fastbootcmd} + # i.e. call fastboot + else if bcb test command = boot-recovery; then + bcb clear command; bcb store; + # do the equivalent of AOSP ${recoverycmd} + # i.e. do anything required for booting into recovery + else + # boot Android OS normally + fi +else + # corrupted/non-existent BCB + # report error or boot non-Android OS (platform-specific) +fi + + +4. ENABLE ON YOUR BOARD +----------------------------------- +The following Kconfig options must be enabled: +CONFIG_PARTITIONS=y +CONFIG_MMC=y +CONFIG_BCB=y + +[1] https://android.googlesource.com/platform/bootable/recovery +[2] https://source.android.com/devices/bootloader +[3] https://patchwork.ozlabs.org/patch/746835/ + ("[U-Boot,5/6] Initial support for the Android Bootloader flow") +[4] https://source.android.com/devices/bootloader/boot-reason

Tom, Simon,
Can we please handle this series and apply it to master? A lot of Android-related patches depend on this, so if all comments are addressed in v4 and there are no further concerns, it would be great to have it merged ASAP so we can handle more patches while merge window is still open.
Thanks!
On Sun, Jul 7, 2019 at 4:34 PM Eugeniu Rosca roscaeugeniu@gmail.com wrote:
The first patch does the necessary fixing and polishing of include/android_bootloader_message.h and is a hard prerequisite for this series.
The second patch performs the implementation of the 'bcb' command. The third patch relocates the Android README to doc/android. The fourth patch describes the motivation and foreseeable use-cases behind the 'bcb' command in the newly created doc/android/bcb.txt.
v4:
- [Simon Glass] Non-functional refinements in cmd/bcb.c
- [Sam Protsenko] Fix stale references of Android docs
- Place detailed stats in each patch. Full v3 to v4 series interdiff: https://pastebin.ubuntu.com/p/zD66FwBznt/
- Retest on Renesas R-Car H3-ES20 ULCB-KF-M06 using U-Boot v2019.07-rc4-358-g1f83431f0053 as base
v3:
- [Simon Glass] Lots of review comments handled in cmd/bcb.c.
- [Simon Glass, Sam Protsenko] Renamed and enriched android docs.
- Placed detailed stats in each patch.
- https://patchwork.ozlabs.org/cover/1104242/
v2:
- [Heinrich Schuchardt] Implement sub-commands via U_BOOT_CMD_MKENT.
- Polished the code. Ensured no warnings returned by sparse, smatch, `cppcheck --force --enable=all --inconclusive`, make W=1.
- Tested on R-Car-H3-ES20 ULCB-KF.
- https://patchwork.ozlabs.org/cover/1101106/
v1:
Eugeniu Rosca (4): include: android_bootloader_message.h: Minimize the diff to AOSP cmd: Add 'bcb' command to read/modify/write BCB fields doc: android: relocate/rename README files doc: android: add BCB overview and usage
cmd/Kconfig | 19 +- cmd/Makefile | 1 + cmd/bcb.c | 338 ++++++++++++++++++ doc/{README.avb2 => android/avb2.txt} | 0 doc/android/bcb.txt | 89 +++++ .../fastboot-protocol.txt} | 0 .../fastboot.txt} | 2 +- include/android_bootloader_message.h | 126 ++++--- test/py/tests/test_avb.py | 2 +- 9 files changed, 516 insertions(+), 61 deletions(-) create mode 100644 cmd/bcb.c rename doc/{README.avb2 => android/avb2.txt} (100%) create mode 100644 doc/android/bcb.txt rename doc/{README.android-fastboot-protocol => android/fastboot-protocol.txt} (100%) rename doc/{README.android-fastboot => android/fastboot.txt} (98%)
-- 2.22.0

Hi Sam,
On Tue, Jul 09, 2019 at 03:20:01PM +0300, Sam Protsenko wrote:
Tom, Simon,
Can we please handle this series and apply it to master? A lot of Android-related patches depend on this, so if all comments are addressed in v4 and there are no further concerns, it would be great to have it merged ASAP so we can handle more patches while merge window is still open.
Thanks!
Due to whatever reason, v3 has been pushed to master instead of v4: https://gitlab.denx.de/u-boot/u-boot/commit/0381b713d1c3 ("include: android_bootloader_message.h: Minimize the diff to AOSP") https://gitlab.denx.de/u-boot/u-boot/commit/db7b7a05b267 ("cmd: Add 'bcb' command to read/modify/write BCB fields") https://gitlab.denx.de/u-boot/u-boot/commit/9bdf0e8fef86 ("doc: relocate/rename Android README and add BCB overview")
I will submit the v3-to-v4 diff in a separate patch. I hope we can avoid unnecessary work like this in future.

Hi Eugeniu,
On Fri, Jul 12, 2019 at 12:20 PM Eugeniu Rosca erosca@de.adit-jv.com wrote:
Hi Sam,
On Tue, Jul 09, 2019 at 03:20:01PM +0300, Sam Protsenko wrote:
Tom, Simon,
Can we please handle this series and apply it to master? A lot of Android-related patches depend on this, so if all comments are addressed in v4 and there are no further concerns, it would be great to have it merged ASAP so we can handle more patches while merge window is still open.
Thanks!
Due to whatever reason, v3 has been pushed to master instead of v4: https://gitlab.denx.de/u-boot/u-boot/commit/0381b713d1c3 ("include: android_bootloader_message.h: Minimize the diff to AOSP") https://gitlab.denx.de/u-boot/u-boot/commit/db7b7a05b267 ("cmd: Add 'bcb' command to read/modify/write BCB fields") https://gitlab.denx.de/u-boot/u-boot/commit/9bdf0e8fef86 ("doc: relocate/rename Android README and add BCB overview")
As patches number has increased recently, I believe maintainers are under big workload right now, so such things can happen, nothing dramatic. But it's good you noticed that. Thanks for handling this patch series, now we are almost ready for merging the A/B.
I will submit the v3-to-v4 diff in a separate patch. I hope we can avoid unnecessary work like this in future.
-- Best Regards, Eugeniu.
participants (3)
-
Eugeniu Rosca
-
Eugeniu Rosca
-
Sam Protsenko