Snap for 5909972 from 16d4556329fc30de79387e396267c1fde7e47b5a to qt-qpr1-release

Change-Id: I991d4c13ed57a89962f165a6a463f31df49c8f9a
diff --git a/.checkpatch.conf b/.checkpatch.conf
index e8e9db6..f0db0f0 100644
--- a/.checkpatch.conf
+++ b/.checkpatch.conf
@@ -5,3 +5,5 @@
 --ignore FILE_PATH_CHANGES
 --ignore GIT_COMMIT_ID
 --ignore SPLIT_STRING
+--ignore CONST_STRUCT
+--ignore LEADING_SPACE
diff --git a/Android.bp b/Android.bp
index 7bf412d..074b9b5 100644
--- a/Android.bp
+++ b/Android.bp
@@ -82,3 +82,63 @@
         "libbase",
     ],
 }
+
+// A special target to be statically linkeed into recovery which is a system
+// (not vendor) component.
+cc_library_static {
+    name: "libnos_citadel_for_recovery",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    srcs: [
+        ":libnos_client",
+        "libnos_datagram/citadel.c",
+    ],
+    static_libs: [
+        "libnos_for_recovery",
+    ],
+}
+
+// Language and vendor related defaults
+cc_defaults {
+    name: "nos_cc_defaults",
+    clang: true,
+    cflags: [
+        "-pedantic",
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wno-zero-length-array",
+    ],
+    conlyflags: [
+        "-std=c11",
+    ],
+    vendor: true,
+    owner: "google",
+}
+
+// Defaults for components under the hw subdirectory
+cc_defaults {
+    name: "nos_cc_hw_defaults",
+    defaults: ["nos_cc_defaults"],
+    relative_install_path: "hw",
+}
+
+// Defaults for components shared between the host and device
+cc_defaults {
+    name: "nos_cc_host_supported_defaults",
+    defaults: ["nos_cc_defaults"],
+    host_supported: true,
+}
+
+cc_library {
+    name: "libnos_client_citadel",
+    srcs: [":libnos_client"],
+    defaults: [
+        "libnos_client_defaults",
+        "nos_cc_defaults",
+    ],
+    shared_libs: ["libnos_datagram_citadel"],
+}
diff --git a/citadel/updater/Android.bp b/citadel/updater/Android.bp
deleted file mode 100644
index 6b1a86b..0000000
--- a/citadel/updater/Android.bp
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-cc_binary {
-    name: "citadel_updater",
-    srcs: [
-        "updater.cpp"
-    ],
-    defaults: ["nos_cc_hw_defaults"],
-    cflags: [
-        // for openssl/sha.h
-        "-Wno-gnu-anonymous-struct",
-        "-Wno-nested-anon-types",
-    ],
-    header_libs: [
-        "nos_headers",
-    ],
-    shared_libs: [
-        "libcrypto",
-        "libnos",
-        "libnos_citadeld_proxy",
-        "libnos_client_citadel",
-        "libutils",
-    ],
-}
diff --git a/citadel/updater/BUILD b/citadel/updater/BUILD
deleted file mode 100644
index 5daeac9..0000000
--- a/citadel/updater/BUILD
+++ /dev/null
@@ -1,12 +0,0 @@
-cc_library(
-    name = "libcitadel_updater",
-    srcs = [
-        "updater.cpp",
-    ],
-    visibility = ["//visibility:public"],
-    deps = [
-        "@boringssl//:ssl",
-        "@nugget_host_generic//:nos_headers",
-        "@nugget_host_generic_libnos//:libnos",
-    ],
-)
diff --git a/citadel/updater/NOTICE b/citadel/updater/NOTICE
deleted file mode 100644
index f433b1a..0000000
--- a/citadel/updater/NOTICE
+++ /dev/null
@@ -1,177 +0,0 @@
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
diff --git a/citadel/updater/WORKSPACE b/citadel/updater/WORKSPACE
deleted file mode 100644
index ea1afae..0000000
--- a/citadel/updater/WORKSPACE
+++ /dev/null
@@ -1 +0,0 @@
-workspace(name = "nugget_host_generic_citadel_updater")
diff --git a/citadel/updater/updater.cpp b/citadel/updater/updater.cpp
deleted file mode 100644
index 93c0383..0000000
--- a/citadel/updater/updater.cpp
+++ /dev/null
@@ -1,1433 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <memory>
-#include <vector>
-
-#include <getopt.h>
-#include <inttypes.h>
-#include <openssl/sha.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-
-/* From Nugget OS */
-#include <application.h>
-#include <app_nugget.h>
-#include <citadel_events.h>
-#include <flash_layout.h>
-#include <signed_header.h>
-
-#include <nos/AppClient.h>
-#include <nos/NuggetClient.h>
-#ifdef ANDROID
-#include <nos/CitadeldProxyClient.h>
-#endif
-
-namespace {
-
-using nos::AppClient;
-using nos::NuggetClient;
-using nos::NuggetClientInterface;
-#ifdef ANDROID
-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 repo_snapshot;
-  int stats;
-  int statsd;
-  int ro;
-  int rw;
-  int reboot;
-  int force_reset;
-  int enable_ro;
-  int enable_rw;
-  int change_pw;
-  uint32_t erase_code;
-  int ap_uart;
-  int selftest;
-  char **selftest_args;
-  /* generic connection options */
-  const char *device;
-  int suzyq;
-  int board_id;
-  int event;
-  char **board_id_args;
-  int console;
-} options;
-
-enum no_short_opts_for_these {
-  OPT_DEVICE = 1000,
-  OPT_ID,
-  OPT_REPO_SNAPSHOT,
-  OPT_STATS,
-  OPT_STATSD,
-  OPT_RO,
-  OPT_RW,
-  OPT_REBOOT,
-  OPT_FORCE_RESET,
-  OPT_ENABLE_RO,
-  OPT_ENABLE_RW,
-  OPT_CHANGE_PW,
-  OPT_ERASE,
-  OPT_AP_UART,
-  OPT_SELFTEST,
-  OPT_SUZYQ,
-  OPT_BOARD_ID,
-  OPT_EVENT,
-};
-
-const char *short_opts = ":hvlV:fF:c";
-const struct option long_opts[] = {
-  /* name    hasarg *flag val */
-  {"version",       0, NULL, 'v'},
-  {"long_version",  0, NULL, 'l'},
-  {"long-version",  0, NULL, 'l'},
-  {"id",            0, NULL, OPT_ID},
-  {"repo_snapshot", 0, NULL, OPT_REPO_SNAPSHOT},
-  {"repo-snapshot", 0, NULL, OPT_REPO_SNAPSHOT},
-  {"stats",         0, NULL, OPT_STATS},
-  {"statsd",        0, NULL, OPT_STATSD},
-  {"ro",            0, NULL, OPT_RO},
-  {"rw",            0, NULL, OPT_RW},
-  {"reboot",        0, NULL, OPT_REBOOT},
-  {"force_reset",   0, NULL, OPT_FORCE_RESET},
-  {"force-reset",   0, NULL, OPT_FORCE_RESET},
-  {"enable_ro",     0, NULL, OPT_ENABLE_RO},
-  {"enable-ro",     0, NULL, OPT_ENABLE_RO},
-  {"enable_rw",     0, NULL, OPT_ENABLE_RW},
-  {"enable-rw",     0, NULL, OPT_ENABLE_RW},
-  {"change_pw",     0, NULL, OPT_CHANGE_PW},
-  {"change-pw",     0, NULL, OPT_CHANGE_PW},
-  {"erase",         1, NULL, OPT_ERASE},
-  {"ap_uart",       0, NULL, OPT_AP_UART},
-  {"ap-uart",       0, NULL, OPT_AP_UART},
-  {"selftest",      0, NULL, OPT_SELFTEST},
-  {"suzyq",         0, NULL, OPT_SUZYQ},
-  {"board_id",      0, NULL, OPT_BOARD_ID},
-  {"event",         0, NULL, OPT_EVENT},
-#ifndef ANDROID
-  {"device",        1, NULL, OPT_DEVICE},
-#endif
-  {"help",          0, NULL, 'h'},
-  {NULL, 0, NULL, 0},
-};
-
-void usage(const char *progname)
-{
-  fprintf(stderr, "\n");
-  fprintf(stderr,
-    "Usage: %s [actions] [image.bin]\n"
-    "\n"
-    "Citadel firmware boots in two stages. The first stage\n"
-    "bootloader (aka \"RO\") is provided by the SOC hardware team\n"
-    "and seldom changes. The application image (\"RW\") is invoked\n"
-    "by the RO image. There are two copies (A/B) of each stage,\n"
-    "so that the active copy can be protected while the unused\n"
-    "copy may be updated. At boot, the newer (valid) copy of each\n"
-    "stage is selected.\n"
-    "\n"
-    "The Citadel image file is the same size of the internal\n"
-    "flash, and contains all four firmware components (RO_A,\n"
-    "RW_A, RO_B, RW_B) located at the correct offsets. Only the\n"
-    "inactive copy (A/B) of each stage (RO/RW) can be modified.\n"
-    "The tool will update the correct copies automatically.\n"
-    "\n"
-    "You must specify the actions to perform. With no actions,\n"
-    "this help message is displayed.\n"
-    "\n"
-    "Actions:\n"
-    "\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"
-    "  --statsd             Display Low Power stats cached by citadeld\n"
-    "\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"
-    "  --repo_snapshot      Show the repo sha1sums for the running image\n"
-    "\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 the update password\n"
-    "\n"
-    "  --ap_uart            Query the AP UART passthru setting\n"
-    "                       (It can only be set in the BIOS)\n"
-    "\n"
-    "  --erase=CODE         Erase all user secrets and reboot.\n"
-    "                       This skips all other actions.\n"
-    "\n"
-    "  --selftest [ARGS]    Run one or more selftests. With no ARGS, it runs\n"
-    "                       a default suite. This command will consume all\n"
-    "                       following args, so run it alone for best results.\n"
-    "\n"
-    "  --suzyq [0|1]        Set the SuzyQable detection setting\n"
-    "\n"
-    "  --board_id [TYPE FLAG]   Get/Set board ID values\n"
-    "\n"
-    "  --event [NUM]        Get NUM pending event records (default 1)\n"
-    "\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"
-#endif
-    "\n",
-    progname);
-}
-
-/****************************************************************************/
-/* Handy stuff */
-
-#ifndef MIN
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
-#endif
-
-int errorcnt;
-void Error(const char *format, ...)
-{
-  va_list ap;
-
-  va_start(ap, format);
-  fprintf(stderr, "ERROR: ");
-  vfprintf(stderr, format, ap);
-  fprintf(stderr, "\n");
-  va_end(ap);
-
-  errorcnt++;
-}
-
-/* Return true on APP_SUCESS, display error message if it's not */
-int is_app_success(uint32_t retval)
-{
-  if (retval == APP_SUCCESS)
-    return 1;
-
-  errorcnt++;
-
-  fprintf(stderr, "Error code 0x%x: ", retval);
-  switch (retval) {
-  case APP_ERROR_BOGUS_ARGS:
-    fprintf(stderr, "bogus args");
-    break;
-  case APP_ERROR_INTERNAL:
-    fprintf(stderr, "app is being stupid");
-    break;
-  case APP_ERROR_TOO_MUCH:
-    fprintf(stderr, "caller sent too much data");
-    break;
-  case APP_ERROR_IO:
-    fprintf(stderr, "problem sending or receiving data");
-    break;
-  case APP_ERROR_RPC:
-    fprintf(stderr, "problem during RPC communication");
-    break;
-  case APP_ERROR_CHECKSUM:
-    fprintf(stderr, "checksum failed");
-    break;
-  case APP_ERROR_BUSY:
-    fprintf(stderr, "the app is already working on a commnad");
-    break;
-  case APP_ERROR_TIMEOUT:
-    fprintf(stderr, "the app took too long to respond");
-    break;
-  default:
-    if (retval >= APP_SPECIFIC_ERROR &&
-       retval < APP_LINE_NUMBER_BASE) {
-      fprintf(stderr, "app-specific error #%d",
-        retval - APP_SPECIFIC_ERROR);
-    } else if (retval >= APP_LINE_NUMBER_BASE) {
-      fprintf(stderr, "error at line %d",
-        retval - APP_LINE_NUMBER_BASE);
-    } else {
-      fprintf(stderr, "unknown");
-    }
-  }
-  fprintf(stderr, "\n");
-
-  return 0;
-}
-
-/****************************************************************************/
-
-std::vector<uint8_t> read_image_from_file(const char *name)
-{
-  FILE *fp;
-  struct stat st;
-
-  fp = fopen(name, "rb");
-  if (!fp) {
-    perror("fopen");
-    Error("Can't open file %s", name);
-    return {};
-  }
-
-  if (fstat(fileno(fp), &st)) {
-    perror("fstat");
-    Error("Can't fstat file %s", name);
-    fclose(fp);
-    return {};
-  }
-
-  if (st.st_size != CHIP_FLASH_SIZE) {
-    Error("The firmware image must be exactly %d bytes",
-          CHIP_FLASH_SIZE);
-    fclose(fp);
-    return {};
-  }
-
-  std::vector<uint8_t> buf(st.st_size);
-  if (1 != fread(buf.data(), st.st_size, 1, fp)) {
-    perror("fread");
-    Error("Can't read %zd bytes", st.st_size);
-    fclose(fp);
-    return {};
-  }
-
-  fclose(fp);
-  buf.resize(st.st_size);
-
-  return buf;
-}
-
-uint32_t compute_digest(void *ptr, size_t len)
-{
-  SHA_CTX ctx;
-  uint8_t digest[SHA_DIGEST_LENGTH];
-  uint32_t retval;
-
-  SHA1_Init(&ctx);
-  SHA1_Update(&ctx, ptr, len);
-  SHA1_Final(digest, &ctx);
-
-  memcpy(&retval, digest, sizeof(retval));
-  return retval;
-}
-
-uint32_t compute_fb_digest(struct nugget_app_flash_block *blk)
-{
-  uint8_t *start_here = ((uint8_t *)blk) +
-    offsetof(struct nugget_app_flash_block, offset);
-  size_t size_to_hash = sizeof(*blk) -
-    offsetof(struct nugget_app_flash_block, offset);
-
-  return compute_digest(start_here, size_to_hash);
-}
-
-uint32_t try_update(AppClient &app, const std::vector<uint8_t> &image,
-        uint32_t offset, uint32_t imagesize)
-{
-  uint32_t stop = offset + imagesize;
-  uint32_t rv;
-
-  printf("Updating image from 0x%05x to 0x%05x, size 0x%05x\n",
-         CHIP_FLASH_BASE + offset, CHIP_FLASH_BASE + stop, imagesize);
-
-  for (; offset < stop; offset += CHIP_FLASH_BANK_SIZE) {
-    int retries = 3;
-    std::vector<uint8_t> data(sizeof(struct nugget_app_flash_block));
-    struct nugget_app_flash_block *fb =
-      (struct nugget_app_flash_block*)data.data();
-
-    fb->offset = offset;
-    memcpy(fb->payload, image.data() + offset, CHIP_FLASH_BANK_SIZE);
-    fb->block_digest = compute_fb_digest(fb);
-
-    printf("writing 0x%05x / 0x%05x",
-           CHIP_FLASH_BASE + offset, CHIP_FLASH_BASE + stop);
-    do {
-      rv = app.Call(NUGGET_PARAM_FLASH_BLOCK, data, nullptr);
-      if (rv == NUGGET_ERROR_RETRY)
-        printf(" retrying");
-    } while (rv == NUGGET_ERROR_RETRY && retries--);
-    if (rv) {
-      if (rv == NUGGET_ERROR_LOCKED)
-        printf(" locked\n");
-      else
-        printf(" fail %d\n", rv);
-      break;
-    }
-    printf(" ok\n");
-  }
-
-  return rv;
-}
-
-uint32_t do_update(AppClient &app, const std::vector<uint8_t> &image,
-       uint32_t offset_A, uint32_t offset_B)
-{
-  struct SignedHeader *hdr;
-  uint32_t rv_A, rv_B;
-
-  /* Try image A first */
-  hdr = (struct SignedHeader *)(image.data() + offset_A);
-  rv_A = try_update(app, image, offset_A, hdr->image_size);
-
-  /* If that worked, we're done */
-  if (rv_A == APP_SUCCESS) {
-    return rv_A;
-  }
-
-  /* Else try image B */
-  hdr = (struct SignedHeader *)(image.data() + offset_B);
-  rv_B = try_update(app, image, offset_B, hdr->image_size);
-
-  return rv_B;
-}
-
-uint32_t do_version(AppClient &app)
-{
-  uint32_t retval;
-  std::vector<uint8_t> buffer;
-  buffer.reserve(512);
-
-  retval = app.Call(NUGGET_PARAM_VERSION, buffer, &buffer);
-
-  if (is_app_success(retval)) {
-    printf("%.*s\n", (int) buffer.size(), buffer.data());
-  }
-
-  return retval;
-}
-
-uint32_t do_id(AppClient &app)
-{
-  uint32_t retval;
-  std::vector<uint8_t> buffer;
-  buffer.reserve(32);
-
-  retval = app.Call(NUGGET_PARAM_DEVICE_ID, buffer, &buffer);
-
-  if (is_app_success(retval)) {
-    printf("%.*s\n", (int) buffer.size(), buffer.data());
-  }
-
-  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 == SIGNED_HEADER_MAGIC_CITADEL ? "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 == SIGNED_HEADER_MAGIC_HAVEN ||
-       v->hdr.magic == SIGNED_HEADER_MAGIC_CITADEL)) {
-    printf("%s:    %d.%d.%d/%s %s\n", name,
-           v->hdr.epoch_, v->hdr.major_, v->hdr.minor_, v->version,
-           v->hdr.magic == SIGNED_HEADER_MAGIC_CITADEL ? "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_repo_snapshot(AppClient &app)
-{
-  uint32_t retval;
-  std::vector<uint8_t> buffer;
-  buffer.reserve(2048);
-
-  retval = app.Call(NUGGET_PARAM_REPO_SNAPSHOT, buffer, &buffer);
-
-  if (is_app_success(retval)) {
-    printf("%.*s\n", (int)buffer.size(), buffer.data());
-  }
-
-  return retval;
-}
-
-uint32_t do_console(AppClient &app, int argc, char *argv[])
-{
-  std::vector<uint8_t> buffer;
-  uint32_t rv;
-  size_t got;
-
-  if (options.console < argc) {
-    char *s = argv[options.console];
-    char c;
-    do {
-      c = *s++;
-      buffer.push_back(c);
-    } while (c);
-  }
-
-  do {
-    buffer.reserve(4096);
-    rv = app.Call(NUGGET_PARAM_CONSOLE, buffer, &buffer);
-    got = buffer.size();
-
-    if (is_app_success(rv)){
-      buffer.push_back('\0');
-      printf("%s", buffer.data());
-    }
-
-    buffer.resize(0);
-  } while (rv == APP_SUCCESS && got > 0);
-
-  return rv;
-}
-
-static void print_stats(const struct nugget_app_low_power_stats *s)
-{
-  printf("hard_reset_count            %" PRIu64 "\n", s->hard_reset_count);
-  printf("time_since_hard_reset       %" PRIu64 "\n",
-         s->time_since_hard_reset);
-  printf("wake_count                  %" PRIu64 "\n", s->wake_count);
-  printf("time_at_last_wake           %" PRIu64 "\n", s->time_at_last_wake);
-  printf("time_spent_awake            %" PRIu64 "\n", s->time_spent_awake);
-  printf("deep_sleep_count            %" PRIu64 "\n", s->deep_sleep_count);
-  printf("time_at_last_deep_sleep     %" PRIu64 "\n",
-         s->time_at_last_deep_sleep);
-  printf("time_spent_in_deep_sleep    %" PRIu64 "\n",
-         s->time_spent_in_deep_sleep);
-  if (s->time_at_ap_reset == UINT64_MAX)
-    printf("time_at_ap_reset            0x%" PRIx64 "\n", s->time_at_ap_reset);
-  else
-    printf("time_at_ap_reset            %" PRIu64 "\n", s->time_at_ap_reset);
-  if (s->time_at_ap_bootloader_done == UINT64_MAX)
-    printf("time_at_ap_bootloader_done  0x%" PRIx64 "\n",
-           s->time_at_ap_bootloader_done);
-  else
-    printf("time_at_ap_bootloader_done  %" PRIu64 "\n",
-           s->time_at_ap_bootloader_done);
-}
-
-uint32_t do_stats(AppClient &app)
-{
-  struct nugget_app_low_power_stats stats;
-  std::vector<uint8_t> buffer;
-  uint32_t retval;
-
-  buffer.reserve(sizeof(stats));
-  retval = app.Call(NUGGET_PARAM_GET_LOW_POWER_STATS, buffer, &buffer);
-
-  if (is_app_success(retval)) {
-    if (buffer.size() < sizeof(stats)) { // old firmware?
-      fprintf(stderr, "# only got %zd / %zd bytes back\n",
-              buffer.size(), sizeof(stats));
-      memset(&stats, 0, sizeof(stats));
-    }
-    memcpy(&stats, buffer.data(), std::min(sizeof(stats), buffer.size()));
-    print_stats(&stats);
-  }
-
-  return retval;
-}
-
-#ifdef ANDROID
-uint32_t do_statsd(CitadeldProxyClient &client)
-{
-  struct nugget_app_low_power_stats stats;
-  std::vector<uint8_t> buffer;
-
-  buffer.reserve(sizeof(stats));
-  ::android::binder::Status s = client.Citadeld().getCachedStats(&buffer);
-
-  if (s.isOk()) {
-    if (buffer.size() < sizeof(stats)) { // old citadeld?
-      fprintf(stderr, "# only got %zd / %zd bytes back\n",
-              buffer.size(), sizeof(stats));
-      memset(&stats, 0, sizeof(stats));
-    }
-    memcpy(&stats, buffer.data(), std::min(sizeof(stats), buffer.size()));
-    print_stats(&stats);
-  } else {
-    printf("ERROR: binder exception %d\n", s.exceptionCode());
-    return APP_ERROR_IO;
-  }
-
-  return 0;
-}
-#else
-uint32_t do_statsd(NuggetClient &client)
-{
-  Error("citadeld isn't attached to this interface");
-  return APP_ERROR_BOGUS_ARGS;
-}
-#endif
-
-uint32_t do_reboot(AppClient &app)
-{
-  uint32_t retval;
-  std::vector<uint8_t> ignored = {1};           // older images need this
-
-  retval = app.Call(NUGGET_PARAM_REBOOT, ignored, nullptr);
-
-  if (is_app_success(retval)) {
-    printf("Citadel reboot requested\n");
-  }
-
-  return retval;
-}
-
-static uint32_t do_change_pw(AppClient &app,
-                             const char *old_pw, const char *new_pw)
-{
-  std::vector<uint8_t> data(sizeof(struct nugget_app_change_update_password));
-  struct nugget_app_change_update_password *s =
-    (struct nugget_app_change_update_password*)data.data();
-
-
-  memset(s, 0xff, sizeof(*s));
-  if (old_pw && *old_pw) {
-    int len = strlen(old_pw);
-    memcpy(&s->old_password.password, old_pw, len);
-    s->old_password.digest =
-      compute_digest(&s->old_password.password,
-                     sizeof(s->old_password.password));
-  }
-
-  if (new_pw && *new_pw) {
-    int len = strlen(new_pw);
-    memcpy(&s->new_password.password, new_pw, len);
-    s->new_password.digest =
-      compute_digest(&s->new_password.password,
-                     sizeof(s->new_password.password));
-  }
-
-  uint32_t rv = app.Call(NUGGET_PARAM_CHANGE_UPDATE_PASSWORD, data, nullptr);
-
-  if (is_app_success(rv))
-    printf("Password changed\n");
-
-  return rv;
-}
-
-static uint32_t do_enable(AppClient &app, const char *pw)
-{
-  std::vector<uint8_t> data(sizeof(struct nugget_app_enable_update));
-  struct nugget_app_enable_update *s =
-    (struct nugget_app_enable_update*)data.data();
-  std::vector<uint8_t> reply;
-
-  memset(&s->password, 0xff, sizeof(s->password));
-  if (pw && *pw) {
-    int len = strlen(pw);
-    memcpy(&s->password.password, pw, len);
-    s->password.digest =
-      compute_digest(&s->password.password,
-                     sizeof(s->password.password));
-  }
-  s->which_headers = options.enable_ro ? NUGGET_ENABLE_HEADER_RO : 0;
-  s->which_headers |= options.enable_rw ? NUGGET_ENABLE_HEADER_RW : 0;
-
-  reply.reserve(1);
-  uint32_t rv = app.Call(NUGGET_PARAM_ENABLE_UPDATE, data, &reply);
-
-  if (is_app_success(rv))
-    /* Reply byte is true only if header was CHANGED to valid */
-    printf("Update %s enabled\n", reply[0] ? "changed to" : "is already");
-
-  return rv;
-}
-
-static uint32_t do_ap_uart(AppClient &app)
-{
-  std::vector<uint8_t> buffer;
-  buffer.reserve(1);
-
-  static const char * const cfgstr[] = {
-    "disabled", "USB", "enabled", "SSC", "Citadel",
-  };
-  static_assert(sizeof(cfgstr)/sizeof(cfgstr[0]) == NUGGET_AP_UART_NUM_CFGS,
-                "Bad size of constant array");
-
-  uint32_t rv = app.Call(NUGGET_PARAM_AP_UART_PASSTHRU, buffer, &buffer);
-
-  if (is_app_success(rv))
-    printf("Current AP UART setting is %s\n", cfgstr[buffer[0]]);
-
-  return rv;
-}
-
-static uint32_t do_suzyq(AppClient &app, int argc, char *argv[])
-{
-  int i, j;
-  std::vector<uint8_t> buffer;
-
-  for (i = options.suzyq; i < argc; i++) {
-    for (j = 0; argv[i][j]; j++) {
-      buffer.push_back(strtol(argv[i], NULL, 10));
-    }
-  }
-
-  buffer.reserve(1);
-  uint32_t rv = app.Call(NUGGET_PARAM_RDD_CFG, buffer, &buffer);
-
-  if (is_app_success(rv))
-    printf("Current SuzyQable detection setting is %d\n", buffer[0]);
-
-  return rv;
-}
-
-static void parse_hex_value(uint32_t *val, const char *str)
-{
-  char *e = 0;
-  uint32_t tmp = strtoul(str, &e, 16);
-
-  if (e && *e)
-    Error("Invalid arg: \"%s\"", str);
-  else
-    *val = tmp;
-}
-
-static void show_board_id(const struct nugget_app_board_id *id)
-{
-  printf("0x%08x 0x%08x 0x%08x # ", id->type, id->flag, id->inv);
-
-  if (id->type == 0xffffffff && id->flag == 0xffffffff &&
-      id->inv == 0xffffffff) {
-    printf("unset\n");
-    return;
-  }
-
-  if (id->type ^ ~id->inv) {
-    printf("corrupted\n");
-    return;
-  }
-
-  printf("%s, ", id->flag & 0x80 ? "MP" : "Pre-MP");
-  switch (id->flag & 0x7f) {
-  case 0x7f:
-    printf("DEVBOARD\n");
-    break;
-  case 0x7e:
-    printf("Proto1\n");
-    break;
-  case 0x7c:
-    printf("Proto2+\n");
-    break;
-  case 0x78:
-    printf("EVT1\n");
-    break;
-  case 0x70:
-    printf("EVT2+\n");
-    break;
-  case 0x60:
-    printf("DVT1\n");
-    break;
-  case 0x40:
-    printf("DVT2+\n");
-    break;
-  case 0x00:
-    printf("PVT/MP\n");
-    break;
-  default:
-    printf("(unknown)\n");
-    break;
-  }
-}
-
-static uint32_t do_board_id(AppClient &app, int argc, char *argv[])
-{
-  uint32_t rv;
-  std::vector<uint8_t> request;
-  std::vector<uint8_t> response(sizeof(struct nugget_app_board_id));
-  struct nugget_app_board_id board_id;
-  char answer = 0;
-
-  // User must input both board_type and board_flag to make a set request
-  if (argc - options.board_id >= 2) {
-    uint32_t tmp = 0;
-
-    parse_hex_value(&tmp, argv[options.board_id]);
-    board_id.type = tmp;
-
-    parse_hex_value(&tmp, argv[options.board_id + 1]);
-    board_id.flag = tmp;
-
-    // optional third arg must equal ~type to avoid confirmation
-    if (argc - options.board_id > 2) {
-      parse_hex_value(&tmp, argv[options.board_id + 2]);
-      board_id.inv = tmp;
-    } else {
-      board_id.inv = ~board_id.type;
-    }
-
-    // Any problems parsing args?
-    if (errorcnt)
-      return errorcnt;
-
-    // Confirm unless correct type_inv arg is given
-    if (argc - options.board_id == 2 || board_id.type ^ ~board_id.inv) {
-      printf("\nWriting Board ID:  ");
-      show_board_id(&board_id);
-      printf("\nWARNING: Setting board-id is irreversible!\n");
-      printf("Are you sure? (y/n) ");
-      fflush(stdout);
-      scanf(" %c", &answer);
-      if (answer != 'y'){
-        Error("Operation cancelled");
-        return errorcnt;
-      }
-      board_id.inv = ~board_id.type;
-      printf("\n");
-    }
-
-    request.resize(sizeof(board_id));
-    memcpy(request.data(), &board_id, sizeof(board_id));
-  }
-
-  rv = app.Call(NUGGET_PARAM_BOARD_ID, request, &response);
-
-  if (is_app_success(rv)) {
-    memcpy(&board_id, response.data(), sizeof(board_id));
-    show_board_id(&board_id);
-  }
-
-  return rv;
-}
-
-static uint32_t do_event(AppClient &app, int argc, char *argv[])
-{
-  uint32_t rv;
-  int i, num = 1;
-
-  if (options.event < argc) {
-    num = atoi(argv[options.event]);
-  }
-
-  for (i = 0; i < num; i++) {
-    struct event_record evt;
-    std::vector<uint8_t> buffer;
-    buffer.reserve(sizeof(evt));
-
-    rv = app.Call(NUGGET_PARAM_GET_EVENT_RECORD, buffer, &buffer);
-
-    if (!is_app_success(rv)) {
-      // That check also displays any errors
-      break;
-    }
-
-    if (buffer.size() == 0) {
-      printf("-- no event_records --\n");
-      continue;
-    }
-
-    if (buffer.size() != sizeof(evt)) {
-      fprintf(stderr, "Error: expected %zd bytes, got %zd instead\n",
-             sizeof(evt), buffer.size());
-      rv = 1;
-      break;
-    }
-
-    /* We got an event, let's show it */
-    memcpy(&evt, buffer.data(), sizeof(evt));
-    uint64_t secs = evt.uptime_usecs / 1000000UL;
-    uint64_t usecs = evt.uptime_usecs - (secs * 1000000UL);
-    printf("event record %" PRIu64 "/%" PRIu64 ".%06" PRIu64 ": ",
-           evt.reset_count, secs, usecs);
-    printf("%d  0x%08x 0x%08x 0x%08x\n", evt.id,
-           evt.u.raw.w[0], evt.u.raw.w[1], evt.u.raw.w[2]);
-  }
-
-  return rv;
-}
-
-static uint32_t do_erase(AppClient &app)
-{
-  std::vector<uint8_t> data(sizeof(uint32_t));
-  memcpy(data.data(), &options.erase_code, data.size());
-
-  uint32_t rv = app.Call(NUGGET_PARAM_NUKE_FROM_ORBIT, data, nullptr);
-
-  if (is_app_success(rv))
-    printf("Citadel erase and reboot requested\n");
-
-  return rv;
-}
-
-#define MAX_SELFTEST_REPLY_LEN 4096
-static uint32_t do_selftest(AppClient &app, int argc, char *argv[])
-{
-  int i, j;
-  uint32_t rv;
-  std::vector<uint8_t> data;
-
-  /* Copy all the args to send, including their terminating '\0' */
-  for (i = options.selftest; i < argc; i++) {
-    for (j = 0; argv[i][j]; j++) {
-      data.push_back(argv[i][j]);
-    }
-    data.push_back('\0');
-  }
-
-  /* Send args, get reply */
-  data.reserve(MAX_SELFTEST_REPLY_LEN);
-  rv = app.Call(NUGGET_PARAM_SELFTEST, data, &data);
-  if (is_app_success(rv)) {
-    /* Make SURE it's null-terminated */
-    size_t len = data.size();
-    if (len) {
-      data[len - 1] = '\0';
-      printf("%s\n", data.data());
-    }
-  }
-  return rv;
-}
-
-// This is currently device-specific, but could be abstracted further
-#ifdef ANDROID
-static uint32_t do_force_reset(CitadeldProxyClient &client)
-{
-    bool b = false;
-
-    return !client.Citadeld().reset(&b).isOk();
-}
-#else
-static uint32_t do_force_reset(NuggetClient &client)
-{
-  struct nos_device *d = client.Device();
-
-  return d->ops.reset(d->ctx);
-}
-#endif
-
-int execute_commands(const std::vector<uint8_t> &image,
-                     const char *old_passwd, const char *passwd,
-                     int argc, char *argv[])
-{
-#ifdef ANDROID
-  CitadeldProxyClient client;
-#else
-  NuggetClient client(options.device ? options.device : "");
-#endif
-
-  client.Open();
-  if (!client.IsOpen()) {
-    Error("Unable to connect");
-    return 1;
-  }
-  AppClient app(client, APP_ID_NUGGET);
-
-  /* Try all requested actions in reasonable order, bail out on error */
-
-  if (options.ap_uart &&
-      do_ap_uart(app) != APP_SUCCESS) {
-    return 1;
-  }
-
-  if (options.erase_code) {                     /* zero doesn't count */
-    /* whether we succeed or not, only do this */
-    return do_erase(app);
-  }
-
-  if (options.version &&
-      do_version(app) != APP_SUCCESS) {
-    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;
-  }
-
-  if (options.stats &&
-      do_stats(app) != APP_SUCCESS) {
-    return 2;
-  }
-
-  if (options.statsd &&
-      do_statsd(client) != APP_SUCCESS) {
-    return 2;
-  }
-
-  if (options.repo_snapshot &&
-      do_repo_snapshot(app) != APP_SUCCESS) {
-    return 2;
-  }
-  if (options.rw &&
-      do_update(app, image,
-          CHIP_RW_A_MEM_OFF, CHIP_RW_B_MEM_OFF) != APP_SUCCESS) {
-    return 3;
-  }
-
-  if (options.ro &&
-      do_update(app, image,
-          CHIP_RO_A_MEM_OFF, CHIP_RO_B_MEM_OFF) != APP_SUCCESS) {
-    return 4;
-  }
-
-  if (options.change_pw &&
-      do_change_pw(app, old_passwd, passwd) != APP_SUCCESS)
-    return 5;
-
-  if ((options.enable_ro || options.enable_rw) &&
-      do_enable(app, passwd) != APP_SUCCESS)
-    return 6;
-
-  if (options.reboot &&
-      do_reboot(app) != APP_SUCCESS) {
-    return 7;
-  }
-
-  if (options.selftest &&
-      do_selftest(app, argc, argv) != APP_SUCCESS) {
-    return 1;
-  }
-
-  if (options.force_reset &&
-      do_force_reset(client) != APP_SUCCESS) {
-    return 1;
-  }
-
-  if (options.suzyq &&
-      do_suzyq(app, argc, argv) != APP_SUCCESS) {
-    return 1;
-  }
-
-  if (options.board_id &&
-      do_board_id(app, argc, argv) != APP_SUCCESS) {
-    return 1;
-  }
-
-  if (options.console &&
-      do_console(app, argc, argv) != APP_SUCCESS) {
-    return 1;
-  }
-
-  if (options.event &&
-      do_event(app, argc, argv) != APP_SUCCESS) {
-    return 1;
-  }
-
-  return 0;
-}
-
-} // namespace
-
-int main(int argc, char *argv[])
-{
-  int i;
-  int idx = 0;
-  char *this_prog;
-  char *old_passwd = 0;
-  char *passwd = 0;
-  std::vector<uint8_t> image;
-  int got_action = 0;
-  char *e = 0;
-  int need_file = 0;
-  int status = 0;
-
-  this_prog= strrchr(argv[0], '/');
-  if (this_prog) {
-    this_prog++;
-  } else {
-    this_prog = argv[0];
-  }
-
-#ifndef ANDROID
-  options.device = secure_getenv("CITADEL_DEVICE");
-  if (options.device)
-    fprintf(stderr, "-- $CITADEL_DEVICE=%s --\n", options.device);
-#endif
-
-  opterr = 0;        /* quiet, you */
-  while ((i = getopt_long(argc, argv,
-        short_opts, long_opts, &idx)) != -1) {
-    switch (i) {
-      /* program-specific options */
-    case 'v':
-      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 'c':
-      options.console = optind;
-      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;
-      break;
-    case OPT_REPO_SNAPSHOT:
-      options.repo_snapshot = 1;
-      got_action = 1;
-      break;
-    case OPT_STATS:
-      options.stats = 1;
-      got_action = 1;
-      break;
-    case OPT_STATSD:
-      options.statsd = 1;
-      got_action = 1;
-      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:
-      options.reboot = 1;
-      got_action = 1;
-      break;
-    case OPT_FORCE_RESET:
-      options.force_reset = 1;
-      got_action = 1;
-      break;
-    case OPT_ENABLE_RO:
-      options.enable_ro = 1;
-      got_action = 1;
-      break;
-    case OPT_ENABLE_RW:
-      options.enable_rw = 1;
-      got_action = 1;
-      break;
-    case OPT_CHANGE_PW:
-      options.change_pw = 1;
-      got_action = 1;
-      break;
-    case OPT_ERASE:
-      options.erase_code = (uint32_t)strtoul(optarg, &e, 0);
-      if (!*optarg || (e && *e)) {
-        Error("Invalid argument: \"%s\"\n", optarg);
-      }
-      got_action = 1;
-      break;
-    case OPT_AP_UART:
-      options.ap_uart = 1;
-      got_action = 1;
-      break;
-    case OPT_SUZYQ:
-      options.suzyq = optind;
-      got_action = 1;
-      break;
-    case OPT_BOARD_ID:
-      options.board_id = optind;
-      options.board_id_args = argv;
-      got_action = 1;
-      break;
-    case OPT_EVENT:
-      options.event = optind;
-      got_action = 1;
-      break;
-    case OPT_SELFTEST:
-      options.selftest = optind;
-      options.selftest_args = argv;
-      got_action = 1;
-      break;
-
-      /* generic options below */
-    case OPT_DEVICE:
-      options.device = optarg;
-      break;
-    case 'h':
-      usage(this_prog);
-      return 0;
-    case 0:
-      break;
-    case '?':
-      if (optopt)
-        Error("Unrecognized options: -%c", optopt);
-      else
-        Error("Unrecognized options: %s",
-              argv[optind - 1]);
-      usage(this_prog);
-      break;
-    case ':':
-      Error("Missing argument to %s", argv[optind - 1]);
-      break;
-    default:
-      Error("Internal error at %s:%d", __FILE__, __LINE__);
-      exit(1);
-    }
-        }
-
-  if (errorcnt) {
-    goto out;
-  }
-
-  if (!got_action) {
-    usage(this_prog);
-    goto out;
-  }
-
-  if (need_file) {
-    if (optind < argc) {
-      /* Sets errorcnt on failure */
-      image = read_image_from_file(argv[optind++]);
-      if (errorcnt)
-        goto out;
-    } else {
-      Error("Missing required image file");
-      goto out;
-    }
-  }
-
-  if (options.change_pw) {
-    /* one arg provided, maybe the old password isn't set */
-    if (optind < argc) {
-      passwd = argv[optind++];
-    } else {
-      Error("Need a new password at least. Use '' to clear it.");
-      goto out;
-    }
-    /* two args provided, use both old & new passwords */
-    if (optind < argc) {
-      old_passwd = passwd;
-      passwd = argv[optind++];
-    }
-  }
-
-  if ((options.enable_ro || options.enable_rw) && !passwd) {
-    if (optind < argc)
-      passwd = argv[optind++];
-    else {
-      Error("Need a password to enable images. Use '' if none.");
-      goto out;
-    }
-  }
-
-  /* Okay, let's do it! */
-  status = execute_commands(image, old_passwd, passwd, argc, argv);
-  if (status != 0) {
-    Error("execute_command failed(%d)!", status);
-  }
-  /* This is the last action, so fall through either way */
-
-out:
-  return !!errorcnt;
-}
diff --git a/libnos_datagram/Android.bp b/libnos_datagram/Android.bp
index 15d49d3..2a2b659 100644
--- a/libnos_datagram/Android.bp
+++ b/libnos_datagram/Android.bp
@@ -19,3 +19,13 @@
     defaults: ["nos_cc_host_supported_defaults"],
     export_include_dirs: ["include"],
 }
+
+cc_library {
+    name: "libnos_datagram_citadel",
+    srcs: ["citadel.c"],
+    defaults: ["nos_cc_defaults"],
+    shared_libs: [
+        "liblog",
+        "libnos_datagram",
+    ],
+}
diff --git a/libnos_datagram/citadel.c b/libnos_datagram/citadel.c
new file mode 100644
index 0000000..026224d
--- /dev/null
+++ b/libnos_datagram/citadel.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "libnos_datagram"
+#include <log/log.h>
+#include <nos/device.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <linux/types.h>
+#include <poll.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+/*****************************************************************************/
+/* Ideally, this should be in <linux/citadel.h> */
+#define CITADEL_IOC_MAGIC               'c'
+struct citadel_ioc_tpm_datagram {
+    __u64        buf;
+    __u32        len;
+    __u32        command;
+};
+#define CITADEL_IOC_TPM_DATAGRAM    _IOW(CITADEL_IOC_MAGIC, 1, \
+                         struct citadel_ioc_tpm_datagram)
+#define CITADEL_IOC_RESET           _IO(CITADEL_IOC_MAGIC, 2)
+/*****************************************************************************/
+
+#define DEV_CITADEL "/dev/citadel0"
+
+static pthread_mutex_t in_buf_mutex = PTHREAD_MUTEX_INITIALIZER;
+static uint8_t in_buf[MAX_DEVICE_TRANSFER];
+static int read_datagram(void *ctx, uint32_t command, uint8_t *buf, uint32_t len) {
+    struct citadel_ioc_tpm_datagram dg = {
+        .buf = (unsigned long)in_buf,
+        .len = len,
+        .command = command,
+    };
+    int ret;
+    int fd;
+
+    if (!ctx) {
+
+        ALOGE("%s: invalid (NULL) device\n", __func__);
+        return -ENODEV;
+    }
+    fd = *(int *)ctx;
+    if (fd < 0) {
+        ALOGE("%s: invalid device\n", __func__);
+        return -ENODEV;
+    }
+
+    if (len > MAX_DEVICE_TRANSFER) {
+        ALOGE("%s: invalid len (%d > %d)\n", __func__,
+            len, MAX_DEVICE_TRANSFER);
+        return -E2BIG;
+    }
+
+    /* Lock the in buffer while it is used for this transaction */
+    if (pthread_mutex_lock(&in_buf_mutex) != 0) {
+        ALOGE("%s: failed to lock in_buf_mutex: %s", __func__, strerror(errno));
+        return -errno;
+    }
+
+    ret = ioctl(fd, CITADEL_IOC_TPM_DATAGRAM, &dg);
+    if (ret < 0) {
+        ALOGE("can't send spi message: %s", strerror(errno));
+        ret = -errno;
+        goto out;
+    }
+
+    memcpy(buf, in_buf, len);
+
+out:
+    if (pthread_mutex_unlock(&in_buf_mutex) != 0) {
+        ALOGE("%s: failed to unlock in_buf_mutex: %s", __func__, strerror(errno));
+        ret = -errno;
+    }
+    return ret;
+}
+
+static pthread_mutex_t out_buf_mutex = PTHREAD_MUTEX_INITIALIZER;
+static uint8_t out_buf[MAX_DEVICE_TRANSFER];
+static int write_datagram(void *ctx, uint32_t command, const uint8_t *buf, uint32_t len) {
+    struct citadel_ioc_tpm_datagram dg = {
+        .buf = (unsigned long)out_buf,
+        .len = len,
+        .command = command,
+    };
+    int ret;
+    int fd;
+
+    if (!ctx) {
+        ALOGE("%s: invalid (NULL) device\n", __func__);
+        return -ENODEV;
+    }
+    fd = *(int *)ctx;
+    if (fd < 0) {
+        ALOGE("%s: invalid device\n", __func__);
+        return -ENODEV;
+    }
+
+    if (len > MAX_DEVICE_TRANSFER) {
+        ALOGE("%s: invalid len (%d > %d)\n", __func__, len,
+            MAX_DEVICE_TRANSFER);
+        return -E2BIG;
+    }
+
+    /* Lock the out buffer while it is used for this transaction */
+    if (pthread_mutex_lock(&out_buf_mutex) != 0) {
+        ALOGE("%s: failed to lock out_buf_mutex: %s", __func__, strerror(errno));
+        return -errno;
+    }
+
+    memcpy(out_buf, buf, len);
+
+    ret = ioctl(fd, CITADEL_IOC_TPM_DATAGRAM, &dg);
+    if (ret < 0) {
+        ALOGE("can't send spi message: %s", strerror(errno));
+        ret = -errno;
+        goto out;
+    }
+
+out:
+    if (pthread_mutex_unlock(&out_buf_mutex) != 0) {
+        ALOGE("%s: failed to unlock out_buf_mutex: %s", __func__, strerror(errno));
+        ret = -errno;
+    }
+    return ret;
+}
+
+static int wait_for_interrupt(void *ctx, int msecs) {
+    int fd = *(int *)ctx;
+    struct pollfd fds = {fd, POLLIN, 0};
+    int rv;
+
+    rv = poll(&fds, 1 /*nfds*/, msecs);
+    if (rv < 0) {
+        ALOGE("poll: %s", strerror(errno));
+    }
+
+    return rv;
+}
+
+static int reset(void *ctx) {
+    int ret;
+    int fd;
+
+    if (!ctx) {
+
+        ALOGE("%s: invalid (NULL) device\n", __func__);
+        return -ENODEV;
+    }
+    fd = *(int *)ctx;
+    if (fd < 0) {
+        ALOGE("%s: invalid device\n", __func__);
+        return -ENODEV;
+    }
+
+    ret = ioctl(fd, CITADEL_IOC_RESET);
+    if (ret < 0) {
+        ALOGE("can't reset Citadel: %s", strerror(errno));
+        return -errno;
+    }
+    return 0;
+}
+
+static void close_device(void *ctx) {
+    int fd;
+
+    if (!ctx) {
+        ALOGE("%s: invalid (NULL) device (ignored)\n", __func__);
+        return;
+    }
+    fd = *(int *)ctx;
+    if (fd < 0) {
+        ALOGE("%s: invalid device (ignored)\n", __func__);
+        return;
+    }
+
+    if (close(fd) < 0)
+        ALOGE("Problem closing device (ignored): %s", strerror(errno));
+    free(ctx);
+}
+
+int nos_device_open(const char *device_name, struct nos_device *dev) {
+    int fd, *new_fd;
+
+    fd = open(device_name ? device_name : DEV_CITADEL, O_RDWR);
+    if (fd < 0) {
+        ALOGE("can't open device: %s", strerror(errno));
+        return -errno;
+    }
+
+    new_fd = (int *)malloc(sizeof(int));
+    if (!new_fd) {
+        ALOGE("can't malloc new fd: %s", strerror(errno));
+        close(fd);
+        return -ENOMEM;
+    }
+    *new_fd = fd;
+
+    dev->ctx = new_fd;
+    dev->ops.read = read_datagram;
+    dev->ops.write = write_datagram;
+    dev->ops.wait_for_interrupt = wait_for_interrupt;
+    dev->ops.reset = reset;
+    dev->ops.close = close_device;
+    return 0;
+}
diff --git a/nugget/include/citadel_events.h b/nugget/include/citadel_events.h
index 336f3c5..154e638 100644
--- a/nugget/include/citadel_events.h
+++ b/nugget/include/citadel_events.h
@@ -44,47 +44,58 @@
  * instead of changing things.
  */
 
+/*
+ * Event priority.  Stored events of lower priority will be evicted to store
+ * higher-priority events if the queue is full.
+ */
+enum event_priority {
+  EVENT_PRIORITY_LOW = 0,
+  EVENT_PRIORITY_MEDIUM = 1,
+  EVENT_PRIORITY_HIGH = 2,
+};
+
+/*
+ * Event ID values live forever.
+ * Add to the list, but NEVER change or delete existing entries.
+ */
+enum event_id {
+  EVENT_NONE = 0,      // Unused ID, used as empty marker.
+  EVENT_ALERT = 1,     // Globalsec alert fired.
+  EVENT_REBOOTED = 2,  // Device rebooted.
+  EVENT_UPGRADED = 3,  // Device has upgraded.
+};
+
 /* Please do not change the size of this struct */
 #define EVENT_RECORD_SIZE 64
 struct event_record {
   uint64_t reset_count;                 /* zeroed by Citadel power cycle */
   uint64_t uptime_usecs;                /* since last Citadel reset */
   uint32_t id;
+  uint32_t priority;
   union {
     /* id-specific information goes here */
     struct {
       uint32_t intr_sts[3];
     } alert;
     struct {
-      uint32_t bad_thing;
-    } citadel;
-    struct {
-      uint32_t okay_thing;
-    } info;
+      uint32_t rstsrc;
+      uint32_t exitpd;
+      uint32_t which0;
+      uint32_t which1;
+    } rebooted;
 
     /* uninterpreted */
     union {
-      uint32_t w[11];
-      uint16_t h[22];
-      uint8_t  b[44];
+      uint32_t w[10];
+      uint16_t h[20];
+      uint8_t b[40];
     } raw;
-  } u;
+  } event;
 } __packed;
 /* Please do not change the size of this struct */
 static_assert(sizeof(struct event_record) == EVENT_RECORD_SIZE,
               "Muting the Immutable");
 
-/*
- * Event ID values live forever.
- * Add to the list, but NEVER change or delete existing entries.
-*/
-enum event_id {
-  EVENT_NONE = 0,                       /* No valid event exists with this ID */
-  EVENT_ALERT = 1,                      /* Security alert reported */
-  EVENT_CITADEL = 2,                    /* Bad: panic, stack overflow, etc. */
-  EVENT_INFO = 3,                       /* FYI: normal reboot, etc. */
-};
-
 #ifdef __cplusplus
 }
 #endif