Snap for 4955197 from 4cd75cff377c91eb9ff62b6508b0a15503cab2a7 to pi-b4s4-release

Change-Id: I51a0cf0ee206c3ebf9b1fc969deb09a4de5dd109
diff --git a/citadel/updater/updater.cpp b/citadel/updater/updater.cpp
index 3d7fd8c..4a8549e 100644
--- a/citadel/updater/updater.cpp
+++ b/citadel/updater/updater.cpp
@@ -76,6 +76,8 @@
   int change_pw;
   uint32_t erase_code;
   int ap_uart;
+  int selftest;
+  char **selftest_args;
   /* generic connection options */
   const char *device;
 } options;
@@ -94,6 +96,7 @@
   OPT_CHANGE_PW,
   OPT_ERASE,
   OPT_AP_UART,
+  OPT_SELFTEST,
 };
 
 const char *short_opts = ":hvlV:fF:";
@@ -120,6 +123,7 @@
   {"erase",         1, NULL, OPT_ERASE},
   {"ap_uart",       0, NULL, OPT_AP_UART},
   {"ap-uart",       0, NULL, OPT_AP_UART},
+  {"selftest",      0, NULL, OPT_SELFTEST},
 #ifndef ANDROID
   {"device",        1, NULL, OPT_DEVICE},
 #endif
@@ -172,9 +176,13 @@
     "\n"
     "  --ap_uart            Query the AP UART passthru setting\n"
     "                       (It can only be set in the BIOS)\n"
-    "\n\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"
 #ifndef ANDROID
     "\n"
     "Options:\n"
@@ -228,6 +236,21 @@
   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) {
@@ -237,7 +260,7 @@
       fprintf(stderr, "error at line %d",
         retval - APP_LINE_NUMBER_BASE);
     } else {
-      fprintf(stderr, "unknown)");
+      fprintf(stderr, "unknown");
     }
   }
   fprintf(stderr, "\n");
@@ -717,7 +740,6 @@
   return rv;
 }
 
-
 static uint32_t do_erase(AppClient &app)
 {
   std::vector<uint8_t> data(sizeof(uint32_t));
@@ -731,6 +753,35 @@
   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)
@@ -749,7 +800,8 @@
 #endif
 
 int execute_commands(const std::vector<uint8_t> &image,
-                     const char *old_passwd, const char *passwd)
+                     const char *old_passwd, const char *passwd,
+                     int argc, char *argv[])
 {
 #ifdef ANDROID
   CitadeldProxyClient client;
@@ -840,6 +892,11 @@
     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;
@@ -948,7 +1005,6 @@
       options.erase_code = (uint32_t)strtoul(optarg, &e, 0);
       if (!*optarg || (e && *e)) {
         Error("Invalid argument: \"%s\"\n", optarg);
-        errorcnt++;
       }
       got_action = 1;
       break;
@@ -956,6 +1012,11 @@
       options.ap_uart = 1;
       got_action = 1;
       break;
+    case OPT_SELFTEST:
+      options.selftest = optind;
+      options.selftest_args = argv;
+      got_action = 1;
+      break;
 
       /* generic options below */
     case OPT_DEVICE:
@@ -1029,7 +1090,7 @@
   }
 
   /* Okay, let's do it! */
-  (void) execute_commands(image, old_passwd, passwd);
+  (void) execute_commands(image, old_passwd, passwd, argc, argv);
   /* This is the last action, so fall through either way */
 
 out:
diff --git a/nugget/include/app_nugget.h b/nugget/include/app_nugget.h
index 72572b9..2c3d980 100644
--- a/nugget/include/app_nugget.h
+++ b/nugget/include/app_nugget.h
@@ -275,6 +275,21 @@
  * @param reply_len    sizeof(uint32_t)
  */
 
+#define NUGGET_PARAM_SELFTEST 0x0101
+/*
+ * Run an intentionally vaguely specified internal test.
+ *
+ * This accepts arbitrary args and returns arbitrary results, as defined by the
+ * Citadel firmware. To allow changes to Nugget OS without requiring
+ * simultaneous changes to the AP, calling this with no args will run a default
+ * set of tests and return a null-terminated string with the result.
+ *
+ * @param args         zero or more null-terminated strings, concatenated
+ * @param arg_len      number of bytes in args
+ * @param reply        null-terminated string (usually)
+ * @param reply_len    number of bytes in reply (including trailing '\0')
+ */
+
 /****************************************************************************/
 /* Support for Power 1.1 HAL */