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