From 80c5282b54a53702aee9078226efab55c5869861 Mon Sep 17 00:00:00 2001 From: Wei-Cheng Xiao Date: Mon, 15 Oct 2018 13:21:56 +0800 Subject: gsctool: add machine output support (-M) to FW image info (-b) Now can print out FW versions and board ID in the image in a machine-friendly way. This allows other programs (e.g., debugd) to parse the output. Add equality check to the firmware versions and board IDs in slot A and B. Now gsctool prints out an error message to stdout if the contents do not match; otherwise, it prints out only one copy of the contents instead of two. Sample runs: $ gsctool -b cr50.bin.prod RO_A:0.0.10 RW_A:0.3.10[ABCD:00000000:00000000] $ gsctool -b cr50.bin.prod -M IMAGE_RO_FW_VER=0.0.10 IMAGE_RW_FW_VER=0.3.10 IMAGE_BID_STRING=ABCD IMAGE_BID_MASK=00000000 IMAGE_BID_FLAGS=00000000 BRANCH=none BUG=None TEST=manually run gsctool -M -b cr50.bin.prod and gsctool -b cr50.bin.prod on a soraka device connected with a naultilus. Outputs are as the examples above. Signed-off-by: Wei-Cheng Xiao Change-Id: I1c4c5110fe236debb93b3db118abb4c922b98bdf Reviewed-on: https://chromium-review.googlesource.com/1278414 Commit-Ready: ChromeOS CL Exonerator Bot Tested-by: Wei-Cheng Xiao Reviewed-by: Andrey Pronin --- extra/usb_updater/gsctool.c | 249 ++++++++++++++++++++++++++++---------------- 1 file changed, 158 insertions(+), 91 deletions(-) diff --git a/extra/usb_updater/gsctool.c b/extra/usb_updater/gsctool.c index 8eac143317..e21ae305d6 100644 --- a/extra/usb_updater/gsctool.c +++ b/extra/usb_updater/gsctool.c @@ -196,6 +196,20 @@ struct upgrade_pkt { */ #define MAX_BUF_SIZE 500 +/* + * Max. length of the board ID string representation. + * + * Board ID is either a 4-character ASCII alphanumeric string or an 8-digit + * hex. + */ +#define MAX_BOARD_ID_LENGTH 9 + +/* + * Max. length of FW version in the format of .. + * (3 uint32_t string representation + 2 separators + NULL terminator). + */ +#define MAX_FW_VER_LENGTH 33 + static int verbose_mode; static uint32_t protocol_version; static char *progname; @@ -530,7 +544,7 @@ static void usage(int errs) "character string.\n" " -k,--ccd_lock Lock CCD\n" " -M,--machine Output in a machine-friendly way. " - "Effective only with -f.\n" + "Effective with -f and -b only.\n" " -m,--tpm_mode [enable|disable]\n" " Change or query tpm_mode\n" " -O,--openbox_rma \n" @@ -1246,65 +1260,165 @@ static void generate_reset_request(struct transfer_descriptor *td) printf("reboot %s\n", reset_type); } -static int show_headers_versions(const void *image) +/* + * Machine output is formatted as "key=value", one key-value pair per line, and + * parsed by other programs (e.g., debugd). The value part should be specified + * in the printf-like way. For example: + * + * print_machine_output("date", "%d/%d/%d", 2018, 1, 1), + * + * which outputs this line in console: + * + * date=2018/1/1 + * + * The key part should not contain '=' or newline. The value part may contain + * special characters like spaces, quotes, brackets, but not newlines. The + * newline character means end of value. + * + * Any output format change in this function may require similar changes on the + * programs that are using this gsctool. + */ +__attribute__((__format__(__printf__, 2, 3))) +static void print_machine_output(const char *key, const char *format, ...) { + va_list args; + + if (strchr(key, '=') != NULL || strchr(key, '\n') != NULL) { + fprintf(stderr, + "Error: key %s contains '=' or a newline character.\n", + key); + return; + } + + if (strchr(format, '\n') != NULL) { + fprintf(stderr, + "Error: value format %s contains a newline character. " + "\n", + format); + return; + } + + va_start(args, format); + + printf("%s=", key); + vprintf(format, args); + printf("\n"); + + va_end(args); +} + +/* + * Prints out the header, including FW versions and board IDs, of the given + * image. Output in a machine-friendly format if show_machine_output is true. + */ +static int show_headers_versions(const void *image, bool show_machine_output) +{ + // There are 2 FW slots in an image, and each slot has 2 sections, RO + // and RW. The 2 slots should have identical FW versions and board IDs. const struct { const char *name; - uint32_t offset; + uint32_t offset; } sections[] = { - {"RO_A", CONFIG_RO_MEM_OFF}, - {"RW_A", CONFIG_RW_MEM_OFF}, - {"RO_B", CHIP_RO_B_MEM_OFF}, - {"RW_B", CONFIG_RW_B_MEM_OFF} + // Slot A + {"RO", CONFIG_RO_MEM_OFF}, + {"RW", CONFIG_RW_MEM_OFF}, + // Slot B + {"RO", CHIP_RO_B_MEM_OFF}, + {"RW", CONFIG_RW_B_MEM_OFF} }; - size_t i; + const size_t kNumSlots = 2; + const size_t kNumSectionsPerSlot = 2; - for (i = 0; i < ARRAY_SIZE(sections); i++) { - const struct SignedHeader *h; - size_t j; - uint32_t bid; - uint32_t bid_mask; - uint32_t bid_flags; + // String representation of FW version (::), one + // string for each FW section. + char ro_fw_ver[kNumSlots][MAX_FW_VER_LENGTH]; + char rw_fw_ver[kNumSlots][MAX_FW_VER_LENGTH]; - h = (const struct SignedHeader *)((uintptr_t)image + - sections[i].offset); - printf("%s%s:%d.%d.%d", i ? " " : "", sections[i].name, - h->epoch_, h->major_, h->minor_); + struct board_id { + uint32_t id; + uint32_t mask; + uint32_t flags; + } bid[kNumSlots]; - if (sections[i].name[1] != 'W') + char bid_string[MAX_BOARD_ID_LENGTH]; + + size_t i; + + for (i = 0; i < ARRAY_SIZE(sections); i++) { + const struct SignedHeader *h = + (const struct SignedHeader *) + ((uintptr_t)image + sections[i].offset); + const size_t slot_idx = i / kNumSectionsPerSlot; + + if (sections[i].name[1] == 'O') { + // RO + snprintf(ro_fw_ver[slot_idx], MAX_FW_VER_LENGTH, + "%u.%u.%u", h->epoch_, h->major_, h->minor_); + // No need to read board ID in an RO section. continue; + } else { + // RW + snprintf(rw_fw_ver[slot_idx], MAX_FW_VER_LENGTH, + "%u.%u.%u", h->epoch_, h->major_, h->minor_); + } /* - * For read/write sections print the board ID fields' - * contents, which are stored XORed with a padding value. + * For RW sections, retrieves the board ID fields' contents, + * which are stored XORed with a padding value. */ - bid = h->board_id_type ^ SIGNED_HEADER_PADDING; - bid_mask = h->board_id_type_mask ^ SIGNED_HEADER_PADDING; - bid_flags = h->board_id_flags ^ SIGNED_HEADER_PADDING; + bid[slot_idx].id = h->board_id_type ^ SIGNED_HEADER_PADDING; + bid[slot_idx].mask = + h->board_id_type_mask ^ SIGNED_HEADER_PADDING; + bid[slot_idx].flags = h->board_id_flags ^ SIGNED_HEADER_PADDING; + } + + if (strncmp(ro_fw_ver[0], ro_fw_ver[1], MAX_FW_VER_LENGTH) != 0) { + fprintf(stderr, + "Error: RO FW versions in the 2 slots do not match.\n"); + return -1; + } - /* Beginning of a board ID section of the string. */ - printf("["); + if (strncmp(rw_fw_ver[0], rw_fw_ver[1], MAX_FW_VER_LENGTH) != 0) { + fprintf(stderr, + "Error: RW FW versions in the 2 slots do not match.\n"); + return -1; + } - /* - * If board ID is an ASCII string (as it ought to be), print - * it as 4 symbols, otherwise print it as an 8 digit hex. - */ - for (j = 0; j < sizeof(bid); j++) - if (!isalnum(((const char *)&bid)[j])) - break; + if (memcmp(&bid[0], &bid[1], sizeof(struct board_id)) != 0) { + fprintf(stderr, + "Error: board IDs in the 2 slots do not match.\n"); + return -1; + } - if (j == sizeof(bid)) { - /* Convert it for proper string representation. */ - bid = be32toh(bid); - printf("%.4s", (const char *)&bid); - } else { - printf("%08x", bid); - } + /* + * If board ID is an ASCII string (as it ought to be), print + * it as 4 symbols, otherwise print it as an 8 digit hex. + */ + for (i = 0; i < sizeof(bid[0].id); ++i) + if (!isalnum(((const char *)&bid[0].id)[i])) + break; - /* Print the rest of the board ID fields. */ - printf(":%08x:%08x]", bid_mask, bid_flags); + if (i == sizeof(bid[0].id)) { + bid[0].id = be32toh(bid[0].id); + snprintf(bid_string, MAX_BOARD_ID_LENGTH, + "%.4s", (const char *)&bid); + } else { + snprintf(bid_string, MAX_BOARD_ID_LENGTH, "%08x", bid[0].id); + } + + if (show_machine_output) { + print_machine_output("IMAGE_RO_FW_VER", "%s", ro_fw_ver[0]); + print_machine_output("IMAGE_RW_FW_VER", "%s", rw_fw_ver[0]); + print_machine_output("IMAGE_BID_STRING", "%s", bid_string); + print_machine_output("IMAGE_BID_MASK", "%08x", bid[0].mask); + print_machine_output("IMAGE_BID_FLAGS", "%08x", bid[0].flags); + } else { + // TODO(garryxiao): remove "_A" from RO and RW after updating + // scripts that use gsctool. + printf("RO_A:%s RW_A:%s[%s:%08x:%08x]\n", + ro_fw_ver[0], rw_fw_ver[0], + bid_string, bid[0].mask, bid[0].flags); } - printf("\n"); return 0; } @@ -1841,53 +1955,6 @@ static void report_version(void) exit(0); } -/* - * Machine output is formatted as "key=value", one key-value pair per line, and - * parsed by other programs (e.g., debugd). The value part should be specified - * in the printf-like way. For example: - * - * print_machine_output("date", "%d/%d/%d", 2018, 1, 1), - * - * which outputs this line in console: - * - * date=2018/1/1 - * - * The key part should not contain '=' or newline. The value part may contain - * special characters like spaces, quotes, brackets, but not newlines. The - * newline character means end of value. - * - * Any output format change in this function may require similar changes on the - * programs that are using this gsctool. - */ -__attribute__((__format__(__printf__, 2, 3))) -static void print_machine_output(const char *key, const char *format, ...) -{ - va_list args; - - if (strchr(key, '=') != NULL || strchr(key, '\n') != NULL) { - fprintf(stderr, - "Error: key %s contains '=' or a newline character.\n", - key); - return; - } - - if (strchr(format, '\n') != NULL) { - fprintf(stderr, - "Error: value format %s contains a newline character. " - "\n", - format); - return; - } - - va_start(args, format); - - printf("%s=", key); - vprintf(format, args); - printf("\n"); - - va_end(args); -} - /* * Either change or query TPM mode value. */ @@ -2161,7 +2228,7 @@ int main(int argc, char *argv[]) fetch_header_versions(data); if (binary_vers) - exit(show_headers_versions(data)); + exit(show_headers_versions(data, show_machine_output)); } else { if (optind < argc) printf("Ignoring binary image %s\n", argv[optind]); -- cgit v1.2.1