Improve Nugget OS version reporting
This adds commands to Nugget OS and citadel_updater to display
additional version information about both Citadel's flash and the
binary image files.
BUG: 77160892
Change-Id: I940df4d47571fa2249b0c71486f91ddaeaa57192
Signed-off-by: Bill Richardson <wfrichar@google.com>
diff --git a/citadel/updater/updater.cpp b/citadel/updater/updater.cpp
index 4993159..683a285 100644
--- a/citadel/updater/updater.cpp
+++ b/citadel/updater/updater.cpp
@@ -48,10 +48,22 @@
using nos::CitadeldProxyClient;
#endif
+enum hdr_section {
+ SEC_BOGUS = 0,
+ SEC_RO_A,
+ SEC_RO_B,
+ SEC_RW_A,
+ SEC_RW_B,
+};
+
/* Global options */
struct options_s {
/* actions to take */
int version;
+ int long_version;
+ enum hdr_section section;
+ int file_version;
+ enum hdr_section file_section;
int id;
int stats;
int ro;
@@ -80,24 +92,25 @@
OPT_ERASE,
};
-const char *short_opts = ":hv";
+const char *short_opts = ":hvlV:fF:";
const struct option long_opts[] = {
/* name hasarg *flag val */
- {"version", 0, NULL, 'v'},
- {"id", 0, NULL, OPT_ID},
- {"stats", 0, NULL, OPT_STATS},
- {"ro", 0, NULL, OPT_RO},
- {"rw", 0, NULL, OPT_RW},
- {"reboot", 0, NULL, OPT_REBOOT},
- {"force_reset", 0, NULL, OPT_FORCE_RESET},
- {"enable_ro", 0, NULL, OPT_ENABLE_RO},
- {"enable_rw", 0, NULL, OPT_ENABLE_RW},
- {"change_pw", 0, NULL, OPT_CHANGE_PW},
- {"erase", 1, NULL, OPT_ERASE},
+ {"version", 0, NULL, 'v'},
+ {"long_version", 0, NULL, 'l'},
+ {"id", 0, NULL, OPT_ID},
+ {"stats", 0, NULL, OPT_STATS},
+ {"ro", 0, NULL, OPT_RO},
+ {"rw", 0, NULL, OPT_RW},
+ {"reboot", 0, NULL, OPT_REBOOT},
+ {"force_reset", 0, NULL, OPT_FORCE_RESET},
+ {"enable_ro", 0, NULL, OPT_ENABLE_RO},
+ {"enable_rw", 0, NULL, OPT_ENABLE_RW},
+ {"change_pw", 0, NULL, OPT_CHANGE_PW},
+ {"erase", 1, NULL, OPT_ERASE},
#ifndef ANDROID
- {"device", 1, NULL, OPT_DEVICE},
+ {"device", 1, NULL, OPT_DEVICE},
#endif
- {"help", 0, NULL, 'h'},
+ {"help", 0, NULL, 'h'},
{NULL, 0, NULL, 0},
};
@@ -126,29 +139,33 @@
"\n"
"Actions:\n"
"\n"
- " -v, --version Display the Citadel version info\n"
- " --id Display the Citadel device ID\n"
- " --stats Display Low Power stats\n"
- " --rw Update RW firmware from the image file\n"
- " --ro Update RO firmware from the image file\n"
- " --reboot Tell Citadel to reboot\n"
- " --force_reset Pulse Citadel's reset line\n"
+ " -v, --version Display the running version\n"
+ " -l, --long_version Display the full version info\n"
+ " --id Display the Citadel device ID\n"
+ " --stats Display Low Power stats\n"
+ " -V SECTION Show Citadel headers for RO_A | RO_B | RW_A | RW_B\n"
+ " -f Show image file version info\n"
+ " -F SECTION Show file headers for RO_A | RO_B | RW_A | RW_B\n"
"\n"
- " --enable_ro Mark new RO image as good\n"
- " --enable_rw Mark new RW image as good\n"
+ " --rw Update RW firmware from the image file\n"
+ " --ro Update RO firmware from the image file\n"
+ " --enable_ro Mark new RO image as good (requires password)\n"
+ " --enable_rw Mark new RW image as good (requires password)\n"
+ " --reboot Tell Citadel to reboot\n"
+ " --force_reset Pulse Citadel's reset line\n"
"\n"
- " --change_pw Change update password\n"
+ " --change_pw Change the update password\n"
"\n\n"
- " --erase=CODE Erase all user secrets and reboot.\n"
- " This skips all other actions.\n"
+ " --erase=CODE Erase all user secrets and reboot.\n"
+ " This skips all other actions.\n"
#ifndef ANDROID
"\n"
"Options:\n"
"\n"
- " --device=SN Connect to the FDTI device with the given\n"
- " serial number (try \"lsusb -v\"). A default\n"
- " can be specified with the CITADEL_DEVICE\n"
- " environment variable.\n"
+ " --device=SN Connect to the FDTI device with the given\n"
+ " serial number (try \"lsusb -v\"). A default\n"
+ " can be specified with the CITADEL_DEVICE\n"
+ " environment variable.\n"
#endif
"\n",
progname);
@@ -368,6 +385,177 @@
return retval;
}
+uint32_t do_long_version(AppClient &app)
+{
+ uint32_t retval;
+ std::vector<uint8_t> buffer;
+ buffer.reserve(1024);
+
+ retval = app.Call(NUGGET_PARAM_LONG_VERSION, buffer, &buffer);
+
+ if (is_app_success(retval)) {
+ printf("%.*s\n", (int)buffer.size(), buffer.data());
+ }
+
+ return retval;
+}
+
+static enum hdr_section parse_section(const char *str)
+{
+ bool is_ro, is_a;
+
+ // matching this: /r?[ow]_?[ab]/i
+
+ if (tolower(*str) == 'r') {
+ str++;
+ }
+
+ if (tolower(*str) == 'o') {
+ is_ro = true;
+ } else if (tolower(*str) == 'w') {
+ is_ro = false;
+ } else {
+ Error("Invalid section \"%s\"", str);
+ return SEC_BOGUS;
+ }
+ str++;
+
+ if (*str == '_') {
+ str++;
+ }
+
+ if (tolower(*str) == 'a') {
+ is_a = true;
+ } else if (tolower(*str) == 'b') {
+ is_a = false;
+ } else {
+ Error("Invalid section \"%s\"", str);
+ return SEC_BOGUS;
+ }
+
+ if (is_ro) {
+ return is_a ? SEC_RO_A : SEC_RO_B;
+ }
+
+ return is_a ? SEC_RW_A : SEC_RW_B;
+}
+
+static void show_header(const uint8_t *ptr)
+{
+ const struct SignedHeader *hdr;
+
+ hdr = reinterpret_cast<const struct SignedHeader*>(ptr);
+ hdr->print();
+}
+
+#define CROS_EC_VERSION_COOKIE1 0xce112233
+#define CROS_EC_VERSION_COOKIE2 0xce445566
+
+// The start of the RW sections looks like this
+struct compiled_version_struct {
+ // The header comes first
+ const struct SignedHeader hdr;
+ // The the vector table. Citadel has 239 entries
+ uint32_t vectors[239];
+ // A magic number to be sure we're looking at the right thing
+ uint32_t cookie1;
+ // Then the short version string
+ char version[32];
+ // And another magic number
+ uint32_t cookie2;
+};
+
+static void show_ro_string(const char *name, const uint8_t *ptr)
+{
+ const struct SignedHeader *hdr;
+
+ hdr = reinterpret_cast<const struct SignedHeader*>(ptr);
+ printf("%s: %d.%d.%d/%08x %s\n", name,
+ hdr->epoch_, hdr->major_, hdr->minor_, be32toh(hdr->img_chk_),
+ hdr->magic == MAGIC_VALID ? "ok" : "--");
+}
+
+static void show_rw_string(const char *name, const uint8_t *ptr)
+{
+ const struct compiled_version_struct *v;
+ v = reinterpret_cast<const struct compiled_version_struct*>(ptr);
+
+ if (v->cookie1 == CROS_EC_VERSION_COOKIE1 &&
+ v->cookie2 == CROS_EC_VERSION_COOKIE2 &&
+ (v->hdr.magic == MAGIC_DEFAULT || v->hdr.magic == MAGIC_VALID)) {
+ printf("%s: %d.%d.%d/%s %s\n", name,
+ v->hdr.epoch_, v->hdr.major_, v->hdr.minor_, v->version,
+ v->hdr.magic == MAGIC_VALID ? "ok" : "--");
+ } else {
+ printf("<invalid>\n");
+ }
+}
+
+uint32_t do_section(AppClient &app __attribute__((unused)))
+{
+ uint16_t param;
+
+ switch (options.section) {
+ case SEC_RO_A:
+ param = NUGGET_PARAM_HEADER_RO_A;
+ break;
+ case SEC_RO_B:
+ param = NUGGET_PARAM_HEADER_RO_B;
+ break;
+ case SEC_RW_A:
+ param = NUGGET_PARAM_HEADER_RW_A;
+ break;
+ case SEC_RW_B:
+ param = NUGGET_PARAM_HEADER_RW_B;
+ break;
+ default:
+ return 1;
+ }
+
+ uint32_t retval;
+ std::vector<uint8_t> buffer;
+ buffer.reserve(sizeof(SignedHeader));
+
+ retval = app.Call(param, buffer, &buffer);
+
+ if (is_app_success(retval)) {
+ show_header(buffer.data());
+ }
+
+ return retval;
+}
+
+uint32_t do_file_version(const std::vector<uint8_t> &image)
+{
+ show_ro_string("RO_A", image.data() + CHIP_RO_A_MEM_OFF);
+ show_ro_string("RO_B", image.data() + CHIP_RO_B_MEM_OFF);
+ show_rw_string("RW_A", image.data() + CHIP_RW_A_MEM_OFF);
+ show_rw_string("RW_B", image.data() + CHIP_RW_B_MEM_OFF);
+ return 0;
+}
+
+uint32_t do_file_section(const std::vector<uint8_t> &image)
+{
+ switch (options.file_section) {
+ case SEC_RO_A:
+ show_header(image.data() + CHIP_RO_A_MEM_OFF);
+ break;
+ case SEC_RO_B:
+ show_header(image.data() + CHIP_RO_B_MEM_OFF);
+ break;
+ case SEC_RW_A:
+ show_header(image.data() + CHIP_RW_A_MEM_OFF);
+ break;
+ case SEC_RW_B:
+ show_header(image.data() + CHIP_RW_B_MEM_OFF);
+ break;
+ default:
+ return 1;
+ }
+
+ return 0;
+}
+
uint32_t do_stats(AppClient &app)
{
struct nugget_app_low_power_stats stats;
@@ -533,6 +721,26 @@
return 2;
}
+ if (options.long_version &&
+ do_long_version(app) != APP_SUCCESS) {
+ return 2;
+ }
+
+ if (options.section &&
+ do_section(app) != APP_SUCCESS) {
+ return 2;
+ }
+
+ if (options.file_version &&
+ do_file_version(image) != APP_SUCCESS) {
+ return 2;
+ }
+
+ if (options.file_section &&
+ do_file_section(image) != APP_SUCCESS) {
+ return 2;
+ }
+
if (options.id &&
do_id(app) != APP_SUCCESS) {
return 2;
@@ -588,6 +796,7 @@
std::vector<uint8_t> image;
int got_action = 0;
char *e = 0;
+ int need_file = 0;
this_prog= strrchr(argv[0], '/');
if (this_prog) {
@@ -611,6 +820,24 @@
options.version = 1;
got_action = 1;
break;
+ case 'l':
+ options.long_version = 1;
+ got_action = 1;
+ break;
+ case 'V':
+ options.section = parse_section(optarg);
+ got_action = 1;
+ break;
+ case 'f':
+ options.file_version = 1;
+ need_file = 1;
+ got_action = 1;
+ break;
+ case 'F':
+ options.file_section = parse_section(optarg);
+ need_file = 1;
+ got_action = 1;
+ break;
case OPT_ID:
options.id = 1;
got_action = 1;
@@ -621,10 +848,12 @@
break;
case OPT_RO:
options.ro = 1;
+ need_file = 1;
got_action = 1;
break;
case OPT_RW:
options.rw = 1;
+ need_file = 1;
got_action = 1;
break;
case OPT_REBOOT:
@@ -691,14 +920,14 @@
goto out;
}
- if (options.ro || options.rw) {
+ if (need_file) {
if (optind < argc) {
/* Sets errorcnt on failure */
image = read_image_from_file(argv[optind++]);
if (errorcnt)
goto out;
} else {
- Error("An image file is required with --ro and --rw");
+ Error("Missing required image file");
goto out;
}
}
@@ -708,8 +937,7 @@
if (optind < argc) {
passwd = argv[optind++];
} else {
- Error("Need a new password at least."
- " Use '' to clear it.");
+ Error("Need a new password at least. Use '' to clear it.");
goto out;
}
/* two args provided, use both old & new passwords */