• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <memory>
18 #include <vector>
19 
20 #include <getopt.h>
21 #include <inttypes.h>
22 #include <openssl/sha.h>
23 #include <stdarg.h>
24 #include <stddef.h>
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/stat.h>
30 
31 /* From Nugget OS */
32 #include <application.h>
33 #include <app_nugget.h>
34 #include <citadel_events.h>
35 #include <flash_layout.h>
36 #include <signed_header.h>
37 
38 #include <nos/AppClient.h>
39 #include <nos/NuggetClient.h>
40 #ifdef ANDROID
41 #include <nos/CitadeldProxyClient.h>
42 #endif
43 
44 namespace {
45 
46 using nos::AppClient;
47 using nos::NuggetClient;
48 using nos::NuggetClientInterface;
49 #ifdef ANDROID
50 using nos::CitadeldProxyClient;
51 #endif
52 
53 enum hdr_section {
54   SEC_BOGUS = 0,
55   SEC_RO_A,
56   SEC_RO_B,
57   SEC_RW_A,
58   SEC_RW_B,
59 };
60 
61 /* Global options */
62 struct options_s {
63   /* actions to take */
64   int version;
65   int long_version;
66   enum hdr_section section;
67   int file_version;
68   enum hdr_section file_section;
69   int id;
70   int repo_snapshot;
71   int stats;
72   int statsd;
73   int ro;
74   int rw;
75   int reboot;
76   int force_reset;
77   int enable_ro;
78   int enable_rw;
79   int change_pw;
80   uint32_t erase_code;
81   int ap_uart;
82   int selftest;
83   char **selftest_args;
84   /* generic connection options */
85   const char *device;
86   int suzyq;
87   int board_id;
88   int event;
89   char **board_id_args;
90   int console;
91 } options;
92 
93 enum no_short_opts_for_these {
94   OPT_DEVICE = 1000,
95   OPT_ID,
96   OPT_REPO_SNAPSHOT,
97   OPT_STATS,
98   OPT_STATSD,
99   OPT_RO,
100   OPT_RW,
101   OPT_REBOOT,
102   OPT_FORCE_RESET,
103   OPT_ENABLE_RO,
104   OPT_ENABLE_RW,
105   OPT_CHANGE_PW,
106   OPT_ERASE,
107   OPT_AP_UART,
108   OPT_SELFTEST,
109   OPT_SUZYQ,
110   OPT_BOARD_ID,
111   OPT_EVENT,
112 };
113 
114 const char *short_opts = ":hvlV:fF:c";
115 const struct option long_opts[] = {
116   /* name    hasarg *flag val */
117   {"version",       0, NULL, 'v'},
118   {"long_version",  0, NULL, 'l'},
119   {"long-version",  0, NULL, 'l'},
120   {"id",            0, NULL, OPT_ID},
121   {"repo_snapshot", 0, NULL, OPT_REPO_SNAPSHOT},
122   {"repo-snapshot", 0, NULL, OPT_REPO_SNAPSHOT},
123   {"stats",         0, NULL, OPT_STATS},
124   {"statsd",        0, NULL, OPT_STATSD},
125   {"ro",            0, NULL, OPT_RO},
126   {"rw",            0, NULL, OPT_RW},
127   {"reboot",        0, NULL, OPT_REBOOT},
128   {"force_reset",   0, NULL, OPT_FORCE_RESET},
129   {"force-reset",   0, NULL, OPT_FORCE_RESET},
130   {"enable_ro",     0, NULL, OPT_ENABLE_RO},
131   {"enable-ro",     0, NULL, OPT_ENABLE_RO},
132   {"enable_rw",     0, NULL, OPT_ENABLE_RW},
133   {"enable-rw",     0, NULL, OPT_ENABLE_RW},
134   {"change_pw",     0, NULL, OPT_CHANGE_PW},
135   {"change-pw",     0, NULL, OPT_CHANGE_PW},
136   {"erase",         1, NULL, OPT_ERASE},
137   {"ap_uart",       0, NULL, OPT_AP_UART},
138   {"ap-uart",       0, NULL, OPT_AP_UART},
139   {"selftest",      0, NULL, OPT_SELFTEST},
140   {"suzyq",         0, NULL, OPT_SUZYQ},
141   {"board_id",      0, NULL, OPT_BOARD_ID},
142   {"event",         0, NULL, OPT_EVENT},
143 #ifndef ANDROID
144   {"device",        1, NULL, OPT_DEVICE},
145 #endif
146   {"help",          0, NULL, 'h'},
147   {NULL, 0, NULL, 0},
148 };
149 
usage(const char * progname)150 void usage(const char *progname)
151 {
152   fprintf(stderr, "\n");
153   fprintf(stderr,
154     "Usage: %s [actions] [image.bin]\n"
155     "\n"
156     "Citadel firmware boots in two stages. The first stage\n"
157     "bootloader (aka \"RO\") is provided by the SOC hardware team\n"
158     "and seldom changes. The application image (\"RW\") is invoked\n"
159     "by the RO image. There are two copies (A/B) of each stage,\n"
160     "so that the active copy can be protected while the unused\n"
161     "copy may be updated. At boot, the newer (valid) copy of each\n"
162     "stage is selected.\n"
163     "\n"
164     "The Citadel image file is the same size of the internal\n"
165     "flash, and contains all four firmware components (RO_A,\n"
166     "RW_A, RO_B, RW_B) located at the correct offsets. Only the\n"
167     "inactive copy (A/B) of each stage (RO/RW) can be modified.\n"
168     "The tool will update the correct copies automatically.\n"
169     "\n"
170     "You must specify the actions to perform. With no actions,\n"
171     "this help message is displayed.\n"
172     "\n"
173     "Actions:\n"
174     "\n"
175     "  -v, --version        Display the running version\n"
176     "  -l, --long_version   Display the full version info\n"
177     "  --id                 Display the Citadel device ID\n"
178     "  --stats              Display Low Power stats\n"
179     "  --statsd             Display Low Power stats cached by citadeld\n"
180     "\n"
181     "  -V SECTION           Show Citadel headers for RO_A | RO_B | RW_A | RW_B\n"
182     "  -f                   Show image file version info\n"
183     "  -F SECTION           Show file headers for RO_A | RO_B | RW_A | RW_B\n"
184     "  --repo_snapshot      Show the repo sha1sums for the running image\n"
185     "\n"
186     "  --rw                 Update RW firmware from the image file\n"
187     "  --ro                 Update RO firmware from the image file\n"
188     "  --enable_ro          Mark new RO image as good (requires password)\n"
189     "  --enable_rw          Mark new RW image as good (requires password)\n"
190     "  --reboot             Tell Citadel to reboot\n"
191     "  --force_reset        Pulse Citadel's reset line\n"
192     "\n"
193     "  --change_pw          Change the update password\n"
194     "\n"
195     "  --ap_uart            Query the AP UART passthru setting\n"
196     "                       (It can only be set in the BIOS)\n"
197     "\n"
198     "  --erase=CODE         Erase all user secrets and reboot.\n"
199     "                       This skips all other actions.\n"
200     "\n"
201     "  --selftest [ARGS]    Run one or more selftests. With no ARGS, it runs\n"
202     "                       a default suite. This command will consume all\n"
203     "                       following args, so run it alone for best results.\n"
204     "\n"
205     "  --suzyq [0|1]        Set the SuzyQable detection setting\n"
206     "\n"
207     "  --board_id [TYPE FLAG]   Get/Set board ID values\n"
208     "\n"
209     "  --event [NUM]        Get NUM pending event records (default 1)\n"
210     "\n"
211 #ifndef ANDROID
212     "\n"
213     "Options:\n"
214     "\n"
215     "  --device=SN          Connect to the FDTI device with the given\n"
216     "                       serial number (try \"lsusb -v\"). A default\n"
217     "                       can be specified with the CITADEL_DEVICE\n"
218     "                       environment variable.\n"
219 #endif
220     "\n",
221     progname);
222 }
223 
224 /****************************************************************************/
225 /* Handy stuff */
226 
227 #ifndef MIN
228 #define MIN(a, b) ((a) < (b) ? (a) : (b))
229 #endif
230 
231 int errorcnt;
Error(const char * format,...)232 void Error(const char *format, ...)
233 {
234   va_list ap;
235 
236   va_start(ap, format);
237   fprintf(stderr, "ERROR: ");
238   vfprintf(stderr, format, ap);
239   fprintf(stderr, "\n");
240   va_end(ap);
241 
242   errorcnt++;
243 }
244 
245 /* Return true on APP_SUCESS, display error message if it's not */
is_app_success(uint32_t retval)246 int is_app_success(uint32_t retval)
247 {
248   if (retval == APP_SUCCESS)
249     return 1;
250 
251   errorcnt++;
252 
253   fprintf(stderr, "Error code 0x%x: ", retval);
254   switch (retval) {
255   case APP_ERROR_BOGUS_ARGS:
256     fprintf(stderr, "bogus args");
257     break;
258   case APP_ERROR_INTERNAL:
259     fprintf(stderr, "app is being stupid");
260     break;
261   case APP_ERROR_TOO_MUCH:
262     fprintf(stderr, "caller sent too much data");
263     break;
264   case APP_ERROR_IO:
265     fprintf(stderr, "problem sending or receiving data");
266     break;
267   case APP_ERROR_RPC:
268     fprintf(stderr, "problem during RPC communication");
269     break;
270   case APP_ERROR_CHECKSUM:
271     fprintf(stderr, "checksum failed");
272     break;
273   case APP_ERROR_BUSY:
274     fprintf(stderr, "the app is already working on a commnad");
275     break;
276   case APP_ERROR_TIMEOUT:
277     fprintf(stderr, "the app took too long to respond");
278     break;
279   default:
280     if (retval >= APP_SPECIFIC_ERROR &&
281        retval < APP_LINE_NUMBER_BASE) {
282       fprintf(stderr, "app-specific error #%d",
283         retval - APP_SPECIFIC_ERROR);
284     } else if (retval >= APP_LINE_NUMBER_BASE) {
285       fprintf(stderr, "error at line %d",
286         retval - APP_LINE_NUMBER_BASE);
287     } else {
288       fprintf(stderr, "unknown");
289     }
290   }
291   fprintf(stderr, "\n");
292 
293   return 0;
294 }
295 
296 /****************************************************************************/
297 
read_image_from_file(const char * name)298 std::vector<uint8_t> read_image_from_file(const char *name)
299 {
300   FILE *fp;
301   struct stat st;
302 
303   fp = fopen(name, "rb");
304   if (!fp) {
305     perror("fopen");
306     Error("Can't open file %s", name);
307     return {};
308   }
309 
310   if (fstat(fileno(fp), &st)) {
311     perror("fstat");
312     Error("Can't fstat file %s", name);
313     fclose(fp);
314     return {};
315   }
316 
317   if (st.st_size != CHIP_FLASH_SIZE) {
318     Error("The firmware image must be exactly %d bytes",
319           CHIP_FLASH_SIZE);
320     fclose(fp);
321     return {};
322   }
323 
324   std::vector<uint8_t> buf(st.st_size);
325   if (1 != fread(buf.data(), st.st_size, 1, fp)) {
326     perror("fread");
327     Error("Can't read %zd bytes", st.st_size);
328     fclose(fp);
329     return {};
330   }
331 
332   fclose(fp);
333   buf.resize(st.st_size);
334 
335   return buf;
336 }
337 
compute_digest(void * ptr,size_t len)338 uint32_t compute_digest(void *ptr, size_t len)
339 {
340   SHA_CTX ctx;
341   uint8_t digest[SHA_DIGEST_LENGTH];
342   uint32_t retval;
343 
344   SHA1_Init(&ctx);
345   SHA1_Update(&ctx, ptr, len);
346   SHA1_Final(digest, &ctx);
347 
348   memcpy(&retval, digest, sizeof(retval));
349   return retval;
350 }
351 
compute_fb_digest(struct nugget_app_flash_block * blk)352 uint32_t compute_fb_digest(struct nugget_app_flash_block *blk)
353 {
354   uint8_t *start_here = ((uint8_t *)blk) +
355     offsetof(struct nugget_app_flash_block, offset);
356   size_t size_to_hash = sizeof(*blk) -
357     offsetof(struct nugget_app_flash_block, offset);
358 
359   return compute_digest(start_here, size_to_hash);
360 }
361 
try_update(AppClient & app,const std::vector<uint8_t> & image,uint32_t offset,uint32_t imagesize)362 uint32_t try_update(AppClient &app, const std::vector<uint8_t> &image,
363         uint32_t offset, uint32_t imagesize)
364 {
365   uint32_t stop = offset + imagesize;
366   uint32_t rv;
367 
368   printf("Updating image from 0x%05x to 0x%05x, size 0x%05x\n",
369          CHIP_FLASH_BASE + offset, CHIP_FLASH_BASE + stop, imagesize);
370 
371   for (; offset < stop; offset += CHIP_FLASH_BANK_SIZE) {
372     int retries = 3;
373     std::vector<uint8_t> data(sizeof(struct nugget_app_flash_block));
374     struct nugget_app_flash_block *fb =
375       (struct nugget_app_flash_block*)data.data();
376 
377     fb->offset = offset;
378     memcpy(fb->payload, image.data() + offset, CHIP_FLASH_BANK_SIZE);
379     fb->block_digest = compute_fb_digest(fb);
380 
381     printf("writing 0x%05x / 0x%05x",
382            CHIP_FLASH_BASE + offset, CHIP_FLASH_BASE + stop);
383     do {
384       rv = app.Call(NUGGET_PARAM_FLASH_BLOCK, data, nullptr);
385       if (rv == NUGGET_ERROR_RETRY)
386         printf(" retrying");
387     } while (rv == NUGGET_ERROR_RETRY && retries--);
388     if (rv) {
389       if (rv == NUGGET_ERROR_LOCKED)
390         printf(" locked\n");
391       else
392         printf(" fail %d\n", rv);
393       break;
394     }
395     printf(" ok\n");
396   }
397 
398   return rv;
399 }
400 
do_update(AppClient & app,const std::vector<uint8_t> & image,uint32_t offset_A,uint32_t offset_B)401 uint32_t do_update(AppClient &app, const std::vector<uint8_t> &image,
402        uint32_t offset_A, uint32_t offset_B)
403 {
404   struct SignedHeader *hdr;
405   uint32_t rv_A, rv_B;
406 
407   /* Try image A first */
408   hdr = (struct SignedHeader *)(image.data() + offset_A);
409   rv_A = try_update(app, image, offset_A, hdr->image_size);
410 
411   /* If that worked, we're done */
412   if (rv_A == APP_SUCCESS) {
413     return rv_A;
414   }
415 
416   /* Else try image B */
417   hdr = (struct SignedHeader *)(image.data() + offset_B);
418   rv_B = try_update(app, image, offset_B, hdr->image_size);
419 
420   return rv_B;
421 }
422 
do_version(AppClient & app)423 uint32_t do_version(AppClient &app)
424 {
425   uint32_t retval;
426   std::vector<uint8_t> buffer;
427   buffer.reserve(512);
428 
429   retval = app.Call(NUGGET_PARAM_VERSION, buffer, &buffer);
430 
431   if (is_app_success(retval)) {
432     printf("%.*s\n", (int) buffer.size(), buffer.data());
433   }
434 
435   return retval;
436 }
437 
do_id(AppClient & app)438 uint32_t do_id(AppClient &app)
439 {
440   uint32_t retval;
441   std::vector<uint8_t> buffer;
442   buffer.reserve(32);
443 
444   retval = app.Call(NUGGET_PARAM_DEVICE_ID, buffer, &buffer);
445 
446   if (is_app_success(retval)) {
447     printf("%.*s\n", (int) buffer.size(), buffer.data());
448   }
449 
450   return retval;
451 }
452 
do_long_version(AppClient & app)453 uint32_t do_long_version(AppClient &app)
454 {
455   uint32_t retval;
456   std::vector<uint8_t> buffer;
457   buffer.reserve(1024);
458 
459   retval = app.Call(NUGGET_PARAM_LONG_VERSION, buffer, &buffer);
460 
461   if (is_app_success(retval)) {
462     printf("%.*s\n", (int)buffer.size(), buffer.data());
463   }
464 
465   return retval;
466 }
467 
parse_section(const char * str)468 static enum hdr_section parse_section(const char *str)
469 {
470   bool is_ro, is_a;
471 
472   // matching this:  /r?[ow]_?[ab]/i
473 
474   if (tolower(*str) == 'r') {
475     str++;
476   }
477 
478   if (tolower(*str) == 'o') {
479     is_ro = true;
480   } else if (tolower(*str) == 'w') {
481     is_ro = false;
482   } else {
483     Error("Invalid section \"%s\"", str);
484     return SEC_BOGUS;
485   }
486   str++;
487 
488   if (*str == '_') {
489     str++;
490   }
491 
492   if (tolower(*str) == 'a') {
493     is_a = true;
494   } else if (tolower(*str) == 'b') {
495     is_a = false;
496   } else {
497     Error("Invalid section \"%s\"", str);
498     return SEC_BOGUS;
499   }
500 
501   if (is_ro) {
502     return is_a ? SEC_RO_A : SEC_RO_B;
503   }
504 
505   return is_a ? SEC_RW_A : SEC_RW_B;
506 }
507 
show_header(const uint8_t * ptr)508 static void show_header(const uint8_t *ptr)
509 {
510   const struct SignedHeader *hdr;
511 
512   hdr = reinterpret_cast<const struct SignedHeader*>(ptr);
513   hdr->print();
514 }
515 
516 #define CROS_EC_VERSION_COOKIE1 0xce112233
517 #define CROS_EC_VERSION_COOKIE2 0xce445566
518 
519 // The start of the RW sections looks like this
520 struct compiled_version_struct {
521   // The header comes first
522   const struct SignedHeader hdr;
523   // The the vector table. Citadel has 239 entries
524   uint32_t vectors[239];
525   // A magic number to be sure we're looking at the right thing
526   uint32_t cookie1;
527   // Then the short version string
528   char version[32];
529   // And another magic number
530   uint32_t cookie2;
531 };
532 
show_ro_string(const char * name,const uint8_t * ptr)533 static void show_ro_string(const char *name, const uint8_t *ptr)
534 {
535   const struct SignedHeader *hdr;
536 
537   hdr = reinterpret_cast<const struct SignedHeader*>(ptr);
538   printf("%s:    %d.%d.%d/%08x %s\n", name,
539          hdr->epoch_, hdr->major_, hdr->minor_, be32toh(hdr->img_chk_),
540          hdr->magic == SIGNED_HEADER_MAGIC_CITADEL ? "ok" : "--");
541 }
542 
show_rw_string(const char * name,const uint8_t * ptr)543 static void show_rw_string(const char *name, const uint8_t *ptr)
544 {
545   const struct compiled_version_struct *v;
546   v = reinterpret_cast<const struct compiled_version_struct*>(ptr);
547 
548   if (v->cookie1 == CROS_EC_VERSION_COOKIE1 &&
549       v->cookie2 == CROS_EC_VERSION_COOKIE2 &&
550       (v->hdr.magic == SIGNED_HEADER_MAGIC_HAVEN ||
551        v->hdr.magic == SIGNED_HEADER_MAGIC_CITADEL)) {
552     printf("%s:    %d.%d.%d/%s %s\n", name,
553            v->hdr.epoch_, v->hdr.major_, v->hdr.minor_, v->version,
554            v->hdr.magic == SIGNED_HEADER_MAGIC_CITADEL ? "ok" : "--");
555   } else {
556     printf("<invalid>\n");
557   }
558 }
559 
do_section(AppClient & app)560 uint32_t do_section(AppClient &app __attribute__((unused)))
561 {
562   uint16_t param;
563 
564   switch (options.section) {
565   case SEC_RO_A:
566     param = NUGGET_PARAM_HEADER_RO_A;
567     break;
568   case SEC_RO_B:
569     param = NUGGET_PARAM_HEADER_RO_B;
570     break;
571   case SEC_RW_A:
572     param = NUGGET_PARAM_HEADER_RW_A;
573     break;
574   case SEC_RW_B:
575     param = NUGGET_PARAM_HEADER_RW_B;
576     break;
577   default:
578     return 1;
579   }
580 
581   uint32_t retval;
582   std::vector<uint8_t> buffer;
583   buffer.reserve(sizeof(SignedHeader));
584 
585   retval = app.Call(param, buffer, &buffer);
586 
587   if (is_app_success(retval)) {
588     show_header(buffer.data());
589   }
590 
591   return retval;
592 }
593 
do_file_version(const std::vector<uint8_t> & image)594 uint32_t do_file_version(const std::vector<uint8_t> &image)
595 {
596   show_ro_string("RO_A", image.data() + CHIP_RO_A_MEM_OFF);
597   show_ro_string("RO_B", image.data() + CHIP_RO_B_MEM_OFF);
598   show_rw_string("RW_A", image.data() + CHIP_RW_A_MEM_OFF);
599   show_rw_string("RW_B", image.data() + CHIP_RW_B_MEM_OFF);
600   return 0;
601 }
602 
do_file_section(const std::vector<uint8_t> & image)603 uint32_t do_file_section(const std::vector<uint8_t> &image)
604 {
605   switch (options.file_section) {
606   case SEC_RO_A:
607     show_header(image.data() + CHIP_RO_A_MEM_OFF);
608     break;
609   case SEC_RO_B:
610     show_header(image.data() + CHIP_RO_B_MEM_OFF);
611     break;
612   case SEC_RW_A:
613     show_header(image.data() + CHIP_RW_A_MEM_OFF);
614     break;
615   case SEC_RW_B:
616     show_header(image.data() + CHIP_RW_B_MEM_OFF);
617     break;
618   default:
619     return 1;
620   }
621 
622   return 0;
623 }
624 
do_repo_snapshot(AppClient & app)625 uint32_t do_repo_snapshot(AppClient &app)
626 {
627   uint32_t retval;
628   std::vector<uint8_t> buffer;
629   buffer.reserve(2048);
630 
631   retval = app.Call(NUGGET_PARAM_REPO_SNAPSHOT, buffer, &buffer);
632 
633   if (is_app_success(retval)) {
634     printf("%.*s\n", (int)buffer.size(), buffer.data());
635   }
636 
637   return retval;
638 }
639 
do_console(AppClient & app,int argc,char * argv[])640 uint32_t do_console(AppClient &app, int argc, char *argv[])
641 {
642   std::vector<uint8_t> buffer;
643   uint32_t rv;
644   size_t got;
645 
646   if (options.console < argc) {
647     char *s = argv[options.console];
648     char c;
649     do {
650       c = *s++;
651       buffer.push_back(c);
652     } while (c);
653   }
654 
655   do {
656     buffer.reserve(4096);
657     rv = app.Call(NUGGET_PARAM_CONSOLE, buffer, &buffer);
658     got = buffer.size();
659 
660     if (is_app_success(rv)){
661       buffer.push_back('\0');
662       printf("%s", buffer.data());
663     }
664 
665     buffer.resize(0);
666   } while (rv == APP_SUCCESS && got > 0);
667 
668   return rv;
669 }
670 
print_stats(const struct nugget_app_low_power_stats * s)671 static void print_stats(const struct nugget_app_low_power_stats *s)
672 {
673   printf("hard_reset_count            %" PRIu64 "\n", s->hard_reset_count);
674   printf("time_since_hard_reset       %" PRIu64 "\n",
675          s->time_since_hard_reset);
676   printf("wake_count                  %" PRIu64 "\n", s->wake_count);
677   printf("time_at_last_wake           %" PRIu64 "\n", s->time_at_last_wake);
678   printf("time_spent_awake            %" PRIu64 "\n", s->time_spent_awake);
679   printf("deep_sleep_count            %" PRIu64 "\n", s->deep_sleep_count);
680   printf("time_at_last_deep_sleep     %" PRIu64 "\n",
681          s->time_at_last_deep_sleep);
682   printf("time_spent_in_deep_sleep    %" PRIu64 "\n",
683          s->time_spent_in_deep_sleep);
684   if (s->time_at_ap_reset == UINT64_MAX)
685     printf("time_at_ap_reset            0x%" PRIx64 "\n", s->time_at_ap_reset);
686   else
687     printf("time_at_ap_reset            %" PRIu64 "\n", s->time_at_ap_reset);
688   if (s->time_at_ap_bootloader_done == UINT64_MAX)
689     printf("time_at_ap_bootloader_done  0x%" PRIx64 "\n",
690            s->time_at_ap_bootloader_done);
691   else
692     printf("time_at_ap_bootloader_done  %" PRIu64 "\n",
693            s->time_at_ap_bootloader_done);
694 }
695 
do_stats(AppClient & app)696 uint32_t do_stats(AppClient &app)
697 {
698   struct nugget_app_low_power_stats stats;
699   std::vector<uint8_t> buffer;
700   uint32_t retval;
701 
702   buffer.reserve(sizeof(stats));
703   retval = app.Call(NUGGET_PARAM_GET_LOW_POWER_STATS, buffer, &buffer);
704 
705   if (is_app_success(retval)) {
706     if (buffer.size() < sizeof(stats)) { // old firmware?
707       fprintf(stderr, "# only got %zd / %zd bytes back\n",
708               buffer.size(), sizeof(stats));
709       memset(&stats, 0, sizeof(stats));
710     }
711     memcpy(&stats, buffer.data(), std::min(sizeof(stats), buffer.size()));
712     print_stats(&stats);
713   }
714 
715   return retval;
716 }
717 
718 #ifdef ANDROID
do_statsd(CitadeldProxyClient & client)719 uint32_t do_statsd(CitadeldProxyClient &client)
720 {
721   struct nugget_app_low_power_stats stats;
722   std::vector<uint8_t> buffer;
723 
724   buffer.reserve(sizeof(stats));
725   ::android::binder::Status s = client.Citadeld().getCachedStats(&buffer);
726 
727   if (s.isOk()) {
728     if (buffer.size() < sizeof(stats)) { // old citadeld?
729       fprintf(stderr, "# only got %zd / %zd bytes back\n",
730               buffer.size(), sizeof(stats));
731       memset(&stats, 0, sizeof(stats));
732     }
733     memcpy(&stats, buffer.data(), std::min(sizeof(stats), buffer.size()));
734     print_stats(&stats);
735   } else {
736     printf("ERROR: binder exception %d\n", s.exceptionCode());
737     return APP_ERROR_IO;
738   }
739 
740   return 0;
741 }
742 #else
do_statsd(NuggetClient & client)743 uint32_t do_statsd(NuggetClient &client)
744 {
745   Error("citadeld isn't attached to this interface");
746   return APP_ERROR_BOGUS_ARGS;
747 }
748 #endif
749 
do_reboot(AppClient & app)750 uint32_t do_reboot(AppClient &app)
751 {
752   uint32_t retval;
753   std::vector<uint8_t> ignored = {1};           // older images need this
754 
755   retval = app.Call(NUGGET_PARAM_REBOOT, ignored, nullptr);
756 
757   if (is_app_success(retval)) {
758     printf("Citadel reboot requested\n");
759   }
760 
761   return retval;
762 }
763 
do_change_pw(AppClient & app,const char * old_pw,const char * new_pw)764 static uint32_t do_change_pw(AppClient &app,
765                              const char *old_pw, const char *new_pw)
766 {
767   std::vector<uint8_t> data(sizeof(struct nugget_app_change_update_password));
768   struct nugget_app_change_update_password *s =
769     (struct nugget_app_change_update_password*)data.data();
770 
771 
772   memset(s, 0xff, sizeof(*s));
773   if (old_pw && *old_pw) {
774     int len = strlen(old_pw);
775     memcpy(&s->old_password.password, old_pw, len);
776     s->old_password.digest =
777       compute_digest(&s->old_password.password,
778                      sizeof(s->old_password.password));
779   }
780 
781   if (new_pw && *new_pw) {
782     int len = strlen(new_pw);
783     memcpy(&s->new_password.password, new_pw, len);
784     s->new_password.digest =
785       compute_digest(&s->new_password.password,
786                      sizeof(s->new_password.password));
787   }
788 
789   uint32_t rv = app.Call(NUGGET_PARAM_CHANGE_UPDATE_PASSWORD, data, nullptr);
790 
791   if (is_app_success(rv))
792     printf("Password changed\n");
793 
794   return rv;
795 }
796 
do_enable(AppClient & app,const char * pw)797 static uint32_t do_enable(AppClient &app, const char *pw)
798 {
799   std::vector<uint8_t> data(sizeof(struct nugget_app_enable_update));
800   struct nugget_app_enable_update *s =
801     (struct nugget_app_enable_update*)data.data();
802   std::vector<uint8_t> reply;
803 
804   memset(&s->password, 0xff, sizeof(s->password));
805   if (pw && *pw) {
806     int len = strlen(pw);
807     memcpy(&s->password.password, pw, len);
808     s->password.digest =
809       compute_digest(&s->password.password,
810                      sizeof(s->password.password));
811   }
812   s->which_headers = options.enable_ro ? NUGGET_ENABLE_HEADER_RO : 0;
813   s->which_headers |= options.enable_rw ? NUGGET_ENABLE_HEADER_RW : 0;
814 
815   reply.reserve(1);
816   uint32_t rv = app.Call(NUGGET_PARAM_ENABLE_UPDATE, data, &reply);
817 
818   if (is_app_success(rv))
819     /* Reply byte is true only if header was CHANGED to valid */
820     printf("Update %s enabled\n", reply[0] ? "changed to" : "is already");
821 
822   return rv;
823 }
824 
do_ap_uart(AppClient & app)825 static uint32_t do_ap_uart(AppClient &app)
826 {
827   std::vector<uint8_t> buffer;
828   buffer.reserve(1);
829 
830   static const char * const cfgstr[] = {
831     "disabled", "USB", "enabled", "SSC", "Citadel",
832   };
833   static_assert(sizeof(cfgstr)/sizeof(cfgstr[0]) == NUGGET_AP_UART_NUM_CFGS,
834                 "Bad size of constant array");
835 
836   uint32_t rv = app.Call(NUGGET_PARAM_AP_UART_PASSTHRU, buffer, &buffer);
837 
838   if (is_app_success(rv))
839     printf("Current AP UART setting is %s\n", cfgstr[buffer[0]]);
840 
841   return rv;
842 }
843 
do_suzyq(AppClient & app,int argc,char * argv[])844 static uint32_t do_suzyq(AppClient &app, int argc, char *argv[])
845 {
846   int i, j;
847   std::vector<uint8_t> buffer;
848 
849   for (i = options.suzyq; i < argc; i++) {
850     for (j = 0; argv[i][j]; j++) {
851       buffer.push_back(strtol(argv[i], NULL, 10));
852     }
853   }
854 
855   buffer.reserve(1);
856   uint32_t rv = app.Call(NUGGET_PARAM_RDD_CFG, buffer, &buffer);
857 
858   if (is_app_success(rv))
859     printf("Current SuzyQable detection setting is %d\n", buffer[0]);
860 
861   return rv;
862 }
863 
parse_hex_value(uint32_t * val,const char * str)864 static void parse_hex_value(uint32_t *val, const char *str)
865 {
866   char *e = 0;
867   uint32_t tmp = strtoul(str, &e, 16);
868 
869   if (e && *e)
870     Error("Invalid arg: \"%s\"", str);
871   else
872     *val = tmp;
873 }
874 
show_board_id(const struct nugget_app_board_id * id)875 static void show_board_id(const struct nugget_app_board_id *id)
876 {
877   printf("0x%08x 0x%08x 0x%08x # ", id->type, id->flag, id->inv);
878 
879   if (id->type == 0xffffffff && id->flag == 0xffffffff &&
880       id->inv == 0xffffffff) {
881     printf("unset\n");
882     return;
883   }
884 
885   if (id->type ^ ~id->inv) {
886     printf("corrupted\n");
887     return;
888   }
889 
890   printf("%s, ", id->flag & 0x80 ? "MP" : "Pre-MP");
891   switch (id->flag & 0x7f) {
892   case 0x7f:
893     printf("DEVBOARD\n");
894     break;
895   case 0x7e:
896     printf("Proto1\n");
897     break;
898   case 0x7c:
899     printf("Proto2+\n");
900     break;
901   case 0x78:
902     printf("EVT1\n");
903     break;
904   case 0x70:
905     printf("EVT2+\n");
906     break;
907   case 0x60:
908     printf("DVT1\n");
909     break;
910   case 0x40:
911     printf("DVT2+\n");
912     break;
913   case 0x00:
914     printf("PVT/MP\n");
915     break;
916   default:
917     printf("(unknown)\n");
918     break;
919   }
920 }
921 
do_board_id(AppClient & app,int argc,char * argv[])922 static uint32_t do_board_id(AppClient &app, int argc, char *argv[])
923 {
924   uint32_t rv;
925   std::vector<uint8_t> request;
926   std::vector<uint8_t> response(sizeof(struct nugget_app_board_id));
927   struct nugget_app_board_id board_id;
928   char answer = 0;
929 
930   // User must input both board_type and board_flag to make a set request
931   if (argc - options.board_id >= 2) {
932     uint32_t tmp = 0;
933 
934     parse_hex_value(&tmp, argv[options.board_id]);
935     board_id.type = tmp;
936 
937     parse_hex_value(&tmp, argv[options.board_id + 1]);
938     board_id.flag = tmp;
939 
940     // optional third arg must equal ~type to avoid confirmation
941     if (argc - options.board_id > 2) {
942       parse_hex_value(&tmp, argv[options.board_id + 2]);
943       board_id.inv = tmp;
944     } else {
945       board_id.inv = ~board_id.type;
946     }
947 
948     // Any problems parsing args?
949     if (errorcnt)
950       return errorcnt;
951 
952     // Confirm unless correct type_inv arg is given
953     if (argc - options.board_id == 2 || board_id.type ^ ~board_id.inv) {
954       printf("\nWriting Board ID:  ");
955       show_board_id(&board_id);
956       printf("\nWARNING: Setting board-id is irreversible!\n");
957       printf("Are you sure? (y/n) ");
958       fflush(stdout);
959       scanf(" %c", &answer);
960       if (answer != 'y'){
961         Error("Operation cancelled");
962         return errorcnt;
963       }
964       board_id.inv = ~board_id.type;
965       printf("\n");
966     }
967 
968     request.resize(sizeof(board_id));
969     memcpy(request.data(), &board_id, sizeof(board_id));
970   }
971 
972   rv = app.Call(NUGGET_PARAM_BOARD_ID, request, &response);
973 
974   if (is_app_success(rv)) {
975     memcpy(&board_id, response.data(), sizeof(board_id));
976     show_board_id(&board_id);
977   }
978 
979   return rv;
980 }
981 
do_event(AppClient & app,int argc,char * argv[])982 static uint32_t do_event(AppClient &app, int argc, char *argv[])
983 {
984   uint32_t rv;
985   int i, num = 1;
986 
987   if (options.event < argc) {
988     num = atoi(argv[options.event]);
989   }
990 
991   for (i = 0; i < num; i++) {
992     struct event_record evt;
993     std::vector<uint8_t> buffer;
994     buffer.reserve(sizeof(evt));
995 
996     rv = app.Call(NUGGET_PARAM_GET_EVENT_RECORD, buffer, &buffer);
997 
998     if (!is_app_success(rv)) {
999       // That check also displays any errors
1000       break;
1001     }
1002 
1003     if (buffer.size() == 0) {
1004       printf("-- no event_records --\n");
1005       continue;
1006     }
1007 
1008     if (buffer.size() != sizeof(evt)) {
1009       fprintf(stderr, "Error: expected %zd bytes, got %zd instead\n",
1010              sizeof(evt), buffer.size());
1011       rv = 1;
1012       break;
1013     }
1014 
1015     /* We got an event, let's show it */
1016     memcpy(&evt, buffer.data(), sizeof(evt));
1017     uint64_t secs = evt.uptime_usecs / 1000000UL;
1018     uint64_t usecs = evt.uptime_usecs - (secs * 1000000UL);
1019     printf("event record %" PRIu64 "/%" PRIu64 ".%06" PRIu64 ": ",
1020            evt.reset_count, secs, usecs);
1021     printf("%d  0x%08x 0x%08x 0x%08x\n", evt.id,
1022            evt.u.raw.w[0], evt.u.raw.w[1], evt.u.raw.w[2]);
1023   }
1024 
1025   return rv;
1026 }
1027 
do_erase(AppClient & app)1028 static uint32_t do_erase(AppClient &app)
1029 {
1030   std::vector<uint8_t> data(sizeof(uint32_t));
1031   memcpy(data.data(), &options.erase_code, data.size());
1032 
1033   uint32_t rv = app.Call(NUGGET_PARAM_NUKE_FROM_ORBIT, data, nullptr);
1034 
1035   if (is_app_success(rv))
1036     printf("Citadel erase and reboot requested\n");
1037 
1038   return rv;
1039 }
1040 
1041 #define MAX_SELFTEST_REPLY_LEN 4096
do_selftest(AppClient & app,int argc,char * argv[])1042 static uint32_t do_selftest(AppClient &app, int argc, char *argv[])
1043 {
1044   int i, j;
1045   uint32_t rv;
1046   std::vector<uint8_t> data;
1047 
1048   /* Copy all the args to send, including their terminating '\0' */
1049   for (i = options.selftest; i < argc; i++) {
1050     for (j = 0; argv[i][j]; j++) {
1051       data.push_back(argv[i][j]);
1052     }
1053     data.push_back('\0');
1054   }
1055 
1056   /* Send args, get reply */
1057   data.reserve(MAX_SELFTEST_REPLY_LEN);
1058   rv = app.Call(NUGGET_PARAM_SELFTEST, data, &data);
1059   if (is_app_success(rv)) {
1060     /* Make SURE it's null-terminated */
1061     size_t len = data.size();
1062     if (len) {
1063       data[len - 1] = '\0';
1064       printf("%s\n", data.data());
1065     }
1066   }
1067   return rv;
1068 }
1069 
1070 // This is currently device-specific, but could be abstracted further
1071 #ifdef ANDROID
do_force_reset(CitadeldProxyClient & client)1072 static uint32_t do_force_reset(CitadeldProxyClient &client)
1073 {
1074     bool b = false;
1075 
1076     return !client.Citadeld().reset(&b).isOk();
1077 }
1078 #else
do_force_reset(NuggetClient & client)1079 static uint32_t do_force_reset(NuggetClient &client)
1080 {
1081   struct nos_device *d = client.Device();
1082 
1083   return d->ops.reset(d->ctx);
1084 }
1085 #endif
1086 
execute_commands(const std::vector<uint8_t> & image,const char * old_passwd,const char * passwd,int argc,char * argv[])1087 int execute_commands(const std::vector<uint8_t> &image,
1088                      const char *old_passwd, const char *passwd,
1089                      int argc, char *argv[])
1090 {
1091 #ifdef ANDROID
1092   CitadeldProxyClient client;
1093 #else
1094   NuggetClient client(options.device ? options.device : "");
1095 #endif
1096 
1097   client.Open();
1098   if (!client.IsOpen()) {
1099     Error("Unable to connect");
1100     return 1;
1101   }
1102   AppClient app(client, APP_ID_NUGGET);
1103 
1104   /* Try all requested actions in reasonable order, bail out on error */
1105 
1106   if (options.ap_uart &&
1107       do_ap_uart(app) != APP_SUCCESS) {
1108     return 1;
1109   }
1110 
1111   if (options.erase_code) {                     /* zero doesn't count */
1112     /* whether we succeed or not, only do this */
1113     return do_erase(app);
1114   }
1115 
1116   if (options.version &&
1117       do_version(app) != APP_SUCCESS) {
1118     return 2;
1119   }
1120 
1121   if (options.long_version &&
1122       do_long_version(app) != APP_SUCCESS) {
1123     return 2;
1124   }
1125 
1126   if (options.section &&
1127       do_section(app) != APP_SUCCESS) {
1128     return 2;
1129   }
1130 
1131   if (options.file_version &&
1132       do_file_version(image) != APP_SUCCESS) {
1133     return 2;
1134   }
1135 
1136   if (options.file_section &&
1137       do_file_section(image) != APP_SUCCESS) {
1138     return 2;
1139   }
1140 
1141   if (options.id &&
1142       do_id(app) != APP_SUCCESS) {
1143     return 2;
1144   }
1145 
1146   if (options.stats &&
1147       do_stats(app) != APP_SUCCESS) {
1148     return 2;
1149   }
1150 
1151   if (options.statsd &&
1152       do_statsd(client) != APP_SUCCESS) {
1153     return 2;
1154   }
1155 
1156   if (options.repo_snapshot &&
1157       do_repo_snapshot(app) != APP_SUCCESS) {
1158     return 2;
1159   }
1160   if (options.rw &&
1161       do_update(app, image,
1162           CHIP_RW_A_MEM_OFF, CHIP_RW_B_MEM_OFF) != APP_SUCCESS) {
1163     return 3;
1164   }
1165 
1166   if (options.ro &&
1167       do_update(app, image,
1168           CHIP_RO_A_MEM_OFF, CHIP_RO_B_MEM_OFF) != APP_SUCCESS) {
1169     return 4;
1170   }
1171 
1172   if (options.change_pw &&
1173       do_change_pw(app, old_passwd, passwd) != APP_SUCCESS)
1174     return 5;
1175 
1176   if ((options.enable_ro || options.enable_rw) &&
1177       do_enable(app, passwd) != APP_SUCCESS)
1178     return 6;
1179 
1180   if (options.reboot &&
1181       do_reboot(app) != APP_SUCCESS) {
1182     return 7;
1183   }
1184 
1185   if (options.selftest &&
1186       do_selftest(app, argc, argv) != APP_SUCCESS) {
1187     return 1;
1188   }
1189 
1190   if (options.force_reset &&
1191       do_force_reset(client) != APP_SUCCESS) {
1192     return 1;
1193   }
1194 
1195   if (options.suzyq &&
1196       do_suzyq(app, argc, argv) != APP_SUCCESS) {
1197     return 1;
1198   }
1199 
1200   if (options.board_id &&
1201       do_board_id(app, argc, argv) != APP_SUCCESS) {
1202     return 1;
1203   }
1204 
1205   if (options.console &&
1206       do_console(app, argc, argv) != APP_SUCCESS) {
1207     return 1;
1208   }
1209 
1210   if (options.event &&
1211       do_event(app, argc, argv) != APP_SUCCESS) {
1212     return 1;
1213   }
1214 
1215   return 0;
1216 }
1217 
1218 } // namespace
1219 
main(int argc,char * argv[])1220 int main(int argc, char *argv[])
1221 {
1222   int i;
1223   int idx = 0;
1224   char *this_prog;
1225   char *old_passwd = 0;
1226   char *passwd = 0;
1227   std::vector<uint8_t> image;
1228   int got_action = 0;
1229   char *e = 0;
1230   int need_file = 0;
1231 
1232   this_prog= strrchr(argv[0], '/');
1233   if (this_prog) {
1234     this_prog++;
1235   } else {
1236     this_prog = argv[0];
1237   }
1238 
1239 #ifndef ANDROID
1240   options.device = secure_getenv("CITADEL_DEVICE");
1241   if (options.device)
1242     fprintf(stderr, "-- $CITADEL_DEVICE=%s --\n", options.device);
1243 #endif
1244 
1245   opterr = 0;        /* quiet, you */
1246   while ((i = getopt_long(argc, argv,
1247         short_opts, long_opts, &idx)) != -1) {
1248     switch (i) {
1249       /* program-specific options */
1250     case 'v':
1251       options.version = 1;
1252       got_action = 1;
1253       break;
1254     case 'l':
1255       options.long_version = 1;
1256       got_action = 1;
1257       break;
1258     case 'V':
1259       options.section = parse_section(optarg);
1260       got_action = 1;
1261       break;
1262     case 'c':
1263       options.console = optind;
1264       got_action = 1;
1265       break;
1266     case 'f':
1267       options.file_version = 1;
1268       need_file = 1;
1269       got_action = 1;
1270       break;
1271     case 'F':
1272       options.file_section = parse_section(optarg);
1273       need_file = 1;
1274       got_action = 1;
1275       break;
1276     case OPT_ID:
1277       options.id = 1;
1278       got_action = 1;
1279       break;
1280     case OPT_REPO_SNAPSHOT:
1281       options.repo_snapshot = 1;
1282       got_action = 1;
1283       break;
1284     case OPT_STATS:
1285       options.stats = 1;
1286       got_action = 1;
1287       break;
1288     case OPT_STATSD:
1289       options.statsd = 1;
1290       got_action = 1;
1291       break;
1292     case OPT_RO:
1293       options.ro = 1;
1294       need_file = 1;
1295       got_action = 1;
1296       break;
1297     case OPT_RW:
1298       options.rw = 1;
1299       need_file = 1;
1300       got_action = 1;
1301       break;
1302     case OPT_REBOOT:
1303       options.reboot = 1;
1304       got_action = 1;
1305       break;
1306     case OPT_FORCE_RESET:
1307       options.force_reset = 1;
1308       got_action = 1;
1309       break;
1310     case OPT_ENABLE_RO:
1311       options.enable_ro = 1;
1312       got_action = 1;
1313       break;
1314     case OPT_ENABLE_RW:
1315       options.enable_rw = 1;
1316       got_action = 1;
1317       break;
1318     case OPT_CHANGE_PW:
1319       options.change_pw = 1;
1320       got_action = 1;
1321       break;
1322     case OPT_ERASE:
1323       options.erase_code = (uint32_t)strtoul(optarg, &e, 0);
1324       if (!*optarg || (e && *e)) {
1325         Error("Invalid argument: \"%s\"\n", optarg);
1326       }
1327       got_action = 1;
1328       break;
1329     case OPT_AP_UART:
1330       options.ap_uart = 1;
1331       got_action = 1;
1332       break;
1333     case OPT_SUZYQ:
1334       options.suzyq = optind;
1335       got_action = 1;
1336       break;
1337     case OPT_BOARD_ID:
1338       options.board_id = optind;
1339       options.board_id_args = argv;
1340       got_action = 1;
1341       break;
1342     case OPT_EVENT:
1343       options.event = optind;
1344       got_action = 1;
1345       break;
1346     case OPT_SELFTEST:
1347       options.selftest = optind;
1348       options.selftest_args = argv;
1349       got_action = 1;
1350       break;
1351 
1352       /* generic options below */
1353     case OPT_DEVICE:
1354       options.device = optarg;
1355       break;
1356     case 'h':
1357       usage(this_prog);
1358       return 0;
1359     case 0:
1360       break;
1361     case '?':
1362       if (optopt)
1363         Error("Unrecognized options: -%c", optopt);
1364       else
1365         Error("Unrecognized options: %s",
1366               argv[optind - 1]);
1367       usage(this_prog);
1368       break;
1369     case ':':
1370       Error("Missing argument to %s", argv[optind - 1]);
1371       break;
1372     default:
1373       Error("Internal error at %s:%d", __FILE__, __LINE__);
1374       exit(1);
1375     }
1376         }
1377 
1378   if (errorcnt) {
1379     goto out;
1380   }
1381 
1382   if (!got_action) {
1383     usage(this_prog);
1384     goto out;
1385   }
1386 
1387   if (need_file) {
1388     if (optind < argc) {
1389       /* Sets errorcnt on failure */
1390       image = read_image_from_file(argv[optind++]);
1391       if (errorcnt)
1392         goto out;
1393     } else {
1394       Error("Missing required image file");
1395       goto out;
1396     }
1397   }
1398 
1399   if (options.change_pw) {
1400     /* one arg provided, maybe the old password isn't set */
1401     if (optind < argc) {
1402       passwd = argv[optind++];
1403     } else {
1404       Error("Need a new password at least. Use '' to clear it.");
1405       goto out;
1406     }
1407     /* two args provided, use both old & new passwords */
1408     if (optind < argc) {
1409       old_passwd = passwd;
1410       passwd = argv[optind++];
1411     }
1412   }
1413 
1414   if ((options.enable_ro || options.enable_rw) && !passwd) {
1415     if (optind < argc)
1416       passwd = argv[optind++];
1417     else {
1418       Error("Need a password to enable images. Use '' if none.");
1419       goto out;
1420     }
1421   }
1422 
1423   /* Okay, let's do it! */
1424   (void) execute_commands(image, old_passwd, passwd, argc, argv);
1425   /* This is the last action, so fall through either way */
1426 
1427 out:
1428   return !!errorcnt;
1429 }
1430