blob: 3d7fd8c7a8374229ab219b61f30d4bebff79e0cc [file] [log] [blame]
Andrew Scull43d72e12017-11-20 14:14:55 +00001/*
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 <openssl/sha.h>
22#include <stdarg.h>
23#include <stddef.h>
24#include <stdint.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <sys/stat.h>
29
30/* From Nugget OS */
31#include <application.h>
32#include <app_nugget.h>
33#include <flash_layout.h>
34#include <signed_header.h>
35
36#include <nos/AppClient.h>
37#include <nos/NuggetClient.h>
38#ifdef ANDROID
39#include <nos/CitadeldProxyClient.h>
40#endif
41
42namespace {
43
44using nos::AppClient;
45using nos::NuggetClient;
46using nos::NuggetClientInterface;
47#ifdef ANDROID
48using nos::CitadeldProxyClient;
49#endif
50
Bill Richardsonb49b34d2018-04-27 10:29:38 -070051enum hdr_section {
52 SEC_BOGUS = 0,
53 SEC_RO_A,
54 SEC_RO_B,
55 SEC_RW_A,
56 SEC_RW_B,
57};
58
Andrew Scull43d72e12017-11-20 14:14:55 +000059/* Global options */
60struct options_s {
61 /* actions to take */
62 int version;
Bill Richardsonb49b34d2018-04-27 10:29:38 -070063 int long_version;
64 enum hdr_section section;
65 int file_version;
66 enum hdr_section file_section;
Andrew Sculld2c3a232018-03-29 20:24:11 +010067 int id;
Bill Richardson30558862018-04-27 16:11:12 -070068 int repo_snapshot;
Bill Richardson2917cb22018-01-26 11:59:26 -080069 int stats;
Andrew Scull43d72e12017-11-20 14:14:55 +000070 int ro;
71 int rw;
72 int reboot;
Bill Richardsonf0a6b802018-03-21 15:32:13 -070073 int force_reset;
Bill Richardson41896c72018-01-18 21:43:12 -080074 int enable_ro;
75 int enable_rw;
76 int change_pw;
77 uint32_t erase_code;
Bill Richardsond3eaf972018-05-23 15:23:33 -070078 int ap_uart;
Andrew Scull43d72e12017-11-20 14:14:55 +000079 /* generic connection options */
80 const char *device;
Andrew Scull43d72e12017-11-20 14:14:55 +000081} options;
82
83enum no_short_opts_for_these {
84 OPT_DEVICE = 1000,
Andrew Sculld2c3a232018-03-29 20:24:11 +010085 OPT_ID,
Bill Richardson30558862018-04-27 16:11:12 -070086 OPT_REPO_SNAPSHOT,
Bill Richardson2917cb22018-01-26 11:59:26 -080087 OPT_STATS,
Andrew Scull43d72e12017-11-20 14:14:55 +000088 OPT_RO,
89 OPT_RW,
90 OPT_REBOOT,
Bill Richardsonf0a6b802018-03-21 15:32:13 -070091 OPT_FORCE_RESET,
Bill Richardson41896c72018-01-18 21:43:12 -080092 OPT_ENABLE_RO,
93 OPT_ENABLE_RW,
94 OPT_CHANGE_PW,
95 OPT_ERASE,
Bill Richardsond3eaf972018-05-23 15:23:33 -070096 OPT_AP_UART,
Andrew Scull43d72e12017-11-20 14:14:55 +000097};
98
Bill Richardsonb49b34d2018-04-27 10:29:38 -070099const char *short_opts = ":hvlV:fF:";
Andrew Scull43d72e12017-11-20 14:14:55 +0000100const struct option long_opts[] = {
101 /* name hasarg *flag val */
Bill Richardson30558862018-04-27 16:11:12 -0700102 {"version", 0, NULL, 'v'},
103 {"long_version", 0, NULL, 'l'},
Bill Richardsond3eaf972018-05-23 15:23:33 -0700104 {"long-version", 0, NULL, 'l'},
Bill Richardson30558862018-04-27 16:11:12 -0700105 {"id", 0, NULL, OPT_ID},
106 {"repo_snapshot", 0, NULL, OPT_REPO_SNAPSHOT},
Bill Richardsond3eaf972018-05-23 15:23:33 -0700107 {"repo-snapshot", 0, NULL, OPT_REPO_SNAPSHOT},
Bill Richardson30558862018-04-27 16:11:12 -0700108 {"stats", 0, NULL, OPT_STATS},
109 {"ro", 0, NULL, OPT_RO},
110 {"rw", 0, NULL, OPT_RW},
111 {"reboot", 0, NULL, OPT_REBOOT},
112 {"force_reset", 0, NULL, OPT_FORCE_RESET},
Bill Richardsond3eaf972018-05-23 15:23:33 -0700113 {"force-reset", 0, NULL, OPT_FORCE_RESET},
Bill Richardson30558862018-04-27 16:11:12 -0700114 {"enable_ro", 0, NULL, OPT_ENABLE_RO},
Bill Richardsond3eaf972018-05-23 15:23:33 -0700115 {"enable-ro", 0, NULL, OPT_ENABLE_RO},
Bill Richardson30558862018-04-27 16:11:12 -0700116 {"enable_rw", 0, NULL, OPT_ENABLE_RW},
Bill Richardsond3eaf972018-05-23 15:23:33 -0700117 {"enable-rw", 0, NULL, OPT_ENABLE_RW},
Bill Richardson30558862018-04-27 16:11:12 -0700118 {"change_pw", 0, NULL, OPT_CHANGE_PW},
Bill Richardsond3eaf972018-05-23 15:23:33 -0700119 {"change-pw", 0, NULL, OPT_CHANGE_PW},
Bill Richardson30558862018-04-27 16:11:12 -0700120 {"erase", 1, NULL, OPT_ERASE},
Bill Richardsond3eaf972018-05-23 15:23:33 -0700121 {"ap_uart", 0, NULL, OPT_AP_UART},
122 {"ap-uart", 0, NULL, OPT_AP_UART},
Bill Richardson153fa252018-02-23 14:39:58 -0800123#ifndef ANDROID
Bill Richardson30558862018-04-27 16:11:12 -0700124 {"device", 1, NULL, OPT_DEVICE},
Bill Richardson153fa252018-02-23 14:39:58 -0800125#endif
Bill Richardson30558862018-04-27 16:11:12 -0700126 {"help", 0, NULL, 'h'},
Andrew Scull43d72e12017-11-20 14:14:55 +0000127 {NULL, 0, NULL, 0},
128};
129
130void usage(const char *progname)
131{
132 fprintf(stderr, "\n");
133 fprintf(stderr,
134 "Usage: %s [actions] [image.bin]\n"
135 "\n"
136 "Citadel firmware boots in two stages. The first stage\n"
137 "bootloader (aka \"RO\") is provided by the SOC hardware team\n"
138 "and seldom changes. The application image (\"RW\") is invoked\n"
139 "by the RO image. There are two copies (A/B) of each stage,\n"
140 "so that the active copy can be protected while the unused\n"
141 "copy may be updated. At boot, the newer (valid) copy of each\n"
142 "stage is selected.\n"
143 "\n"
144 "The Citadel image file is the same size of the internal\n"
145 "flash, and contains all four firmware components (RO_A,\n"
146 "RW_A, RO_B, RW_B) located at the correct offsets. Only the\n"
147 "inactive copy (A/B) of each stage (RO/RW) can be modified.\n"
148 "The tool will update the correct copies automatically.\n"
149 "\n"
Bill Richardson153fa252018-02-23 14:39:58 -0800150 "You must specify the actions to perform. With no actions,\n"
Andrew Scull43d72e12017-11-20 14:14:55 +0000151 "this help message is displayed.\n"
152 "\n"
153 "Actions:\n"
154 "\n"
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700155 " -v, --version Display the running version\n"
156 " -l, --long_version Display the full version info\n"
157 " --id Display the Citadel device ID\n"
158 " --stats Display Low Power stats\n"
159 " -V SECTION Show Citadel headers for RO_A | RO_B | RW_A | RW_B\n"
160 " -f Show image file version info\n"
161 " -F SECTION Show file headers for RO_A | RO_B | RW_A | RW_B\n"
Bill Richardson30558862018-04-27 16:11:12 -0700162 " --repo_snapshot Show the repo sha1sums for the running image\n"
Bill Richardson41896c72018-01-18 21:43:12 -0800163 "\n"
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700164 " --rw Update RW firmware from the image file\n"
165 " --ro Update RO firmware from the image file\n"
166 " --enable_ro Mark new RO image as good (requires password)\n"
167 " --enable_rw Mark new RW image as good (requires password)\n"
168 " --reboot Tell Citadel to reboot\n"
169 " --force_reset Pulse Citadel's reset line\n"
Bill Richardson41896c72018-01-18 21:43:12 -0800170 "\n"
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700171 " --change_pw Change the update password\n"
Bill Richardsond3eaf972018-05-23 15:23:33 -0700172 "\n"
173 " --ap_uart Query the AP UART passthru setting\n"
174 " (It can only be set in the BIOS)\n"
Bill Richardson41896c72018-01-18 21:43:12 -0800175 "\n\n"
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700176 " --erase=CODE Erase all user secrets and reboot.\n"
177 " This skips all other actions.\n"
Bill Richardson153fa252018-02-23 14:39:58 -0800178#ifndef ANDROID
179 "\n"
180 "Options:\n"
181 "\n"
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700182 " --device=SN Connect to the FDTI device with the given\n"
183 " serial number (try \"lsusb -v\"). A default\n"
184 " can be specified with the CITADEL_DEVICE\n"
185 " environment variable.\n"
Bill Richardson153fa252018-02-23 14:39:58 -0800186#endif
Andrew Scull43d72e12017-11-20 14:14:55 +0000187 "\n",
188 progname);
189}
190
191/****************************************************************************/
192/* Handy stuff */
193
194#ifndef MIN
195#define MIN(a, b) ((a) < (b) ? (a) : (b))
196#endif
197
198int errorcnt;
199void Error(const char *format, ...)
200{
201 va_list ap;
202
Bill Richardson41896c72018-01-18 21:43:12 -0800203 va_start(ap, format);
204 fprintf(stderr, "ERROR: ");
205 vfprintf(stderr, format, ap);
206 fprintf(stderr, "\n");
207 va_end(ap);
Andrew Scull43d72e12017-11-20 14:14:55 +0000208
209 errorcnt++;
210}
211
212/* Return true on APP_SUCESS, display error message if it's not */
213int is_app_success(uint32_t retval)
214{
215 if (retval == APP_SUCCESS)
216 return 1;
217
218 errorcnt++;
219
220 fprintf(stderr, "Error code 0x%x: ", retval);
221 switch (retval) {
222 case APP_ERROR_BOGUS_ARGS:
223 fprintf(stderr, "bogus args");
224 break;
225 case APP_ERROR_INTERNAL:
226 fprintf(stderr, "app is being stupid");
227 break;
228 case APP_ERROR_TOO_MUCH:
229 fprintf(stderr, "caller sent too much data");
230 break;
231 default:
232 if (retval >= APP_SPECIFIC_ERROR &&
233 retval < APP_LINE_NUMBER_BASE) {
234 fprintf(stderr, "app-specific error #%d",
235 retval - APP_SPECIFIC_ERROR);
236 } else if (retval >= APP_LINE_NUMBER_BASE) {
237 fprintf(stderr, "error at line %d",
238 retval - APP_LINE_NUMBER_BASE);
239 } else {
240 fprintf(stderr, "unknown)");
241 }
242 }
243 fprintf(stderr, "\n");
244
245 return 0;
246}
247
248/****************************************************************************/
249
250std::vector<uint8_t> read_image_from_file(const char *name)
251{
252 FILE *fp;
253 struct stat st;
254
255 fp = fopen(name, "rb");
256 if (!fp) {
257 perror("fopen");
258 Error("Can't open file %s", name);
259 return {};
260 }
261
262 if (fstat(fileno(fp), &st)) {
263 perror("fstat");
264 Error("Can't fstat file %s", name);
265 fclose(fp);
266 return {};
267 }
268
269 if (st.st_size != CHIP_FLASH_SIZE) {
270 Error("The firmware image must be exactly %d bytes",
271 CHIP_FLASH_SIZE);
272 fclose(fp);
273 return {};
274 }
275
276 std::vector<uint8_t> buf(st.st_size);
277 if (1 != fread(buf.data(), st.st_size, 1, fp)) {
278 perror("fread");
279 Error("Can't read %zd bytes", st.st_size);
280 fclose(fp);
281 return {};
282 }
283
284 fclose(fp);
285 buf.resize(st.st_size);
286
287 return buf;
288}
289
Bill Richardson41896c72018-01-18 21:43:12 -0800290uint32_t compute_digest(void *ptr, size_t len)
Andrew Scull43d72e12017-11-20 14:14:55 +0000291{
Andrew Scull43d72e12017-11-20 14:14:55 +0000292 SHA_CTX ctx;
293 uint8_t digest[SHA_DIGEST_LENGTH];
294 uint32_t retval;
295
296 SHA1_Init(&ctx);
Bill Richardson41896c72018-01-18 21:43:12 -0800297 SHA1_Update(&ctx, ptr, len);
Andrew Scull43d72e12017-11-20 14:14:55 +0000298 SHA1_Final(digest, &ctx);
299
300 memcpy(&retval, digest, sizeof(retval));
301 return retval;
302}
303
Bill Richardson41896c72018-01-18 21:43:12 -0800304uint32_t compute_fb_digest(struct nugget_app_flash_block *blk)
305{
306 uint8_t *start_here = ((uint8_t *)blk) +
307 offsetof(struct nugget_app_flash_block, offset);
308 size_t size_to_hash = sizeof(*blk) -
309 offsetof(struct nugget_app_flash_block, offset);
310
311 return compute_digest(start_here, size_to_hash);
312}
313
Andrew Scull43d72e12017-11-20 14:14:55 +0000314uint32_t try_update(AppClient &app, const std::vector<uint8_t> &image,
315 uint32_t offset, uint32_t imagesize)
316{
317 uint32_t stop = offset + imagesize;
318 uint32_t rv;
319
320 printf("Updating image from 0x%05x to 0x%05x, size 0x%05x\n",
321 CHIP_FLASH_BASE + offset, CHIP_FLASH_BASE + stop, imagesize);
322
323 for (; offset < stop; offset += CHIP_FLASH_BANK_SIZE) {
324 int retries = 3;
325 std::vector<uint8_t> data(sizeof(struct nugget_app_flash_block));
326 struct nugget_app_flash_block *fb =
327 (struct nugget_app_flash_block*)data.data();
328
329 fb->offset = offset;
330 memcpy(fb->payload, image.data() + offset, CHIP_FLASH_BANK_SIZE);
Bill Richardson41896c72018-01-18 21:43:12 -0800331 fb->block_digest = compute_fb_digest(fb);
Andrew Scull43d72e12017-11-20 14:14:55 +0000332
333 printf("writing 0x%05x / 0x%05x",
334 CHIP_FLASH_BASE + offset, CHIP_FLASH_BASE + stop);
335 do {
336 rv = app.Call(NUGGET_PARAM_FLASH_BLOCK, data, nullptr);
337 if (rv == NUGGET_ERROR_RETRY)
338 printf(" retrying");
339 } while (rv == NUGGET_ERROR_RETRY && retries--);
Bill Richardson41896c72018-01-18 21:43:12 -0800340 if (rv) {
341 if (rv == NUGGET_ERROR_LOCKED)
342 printf(" locked\n");
343 else
344 printf(" fail %d\n", rv);
Andrew Scull43d72e12017-11-20 14:14:55 +0000345 break;
Bill Richardson41896c72018-01-18 21:43:12 -0800346 }
347 printf(" ok\n");
Andrew Scull43d72e12017-11-20 14:14:55 +0000348 }
349
350 return rv;
351}
352
Andrew Scull43d72e12017-11-20 14:14:55 +0000353uint32_t do_update(AppClient &app, const std::vector<uint8_t> &image,
354 uint32_t offset_A, uint32_t offset_B)
355{
356 struct SignedHeader *hdr;
357 uint32_t rv_A, rv_B;
358
359 /* Try image A first */
360 hdr = (struct SignedHeader *)(image.data() + offset_A);
361 rv_A = try_update(app, image, offset_A, hdr->image_size);
362
363 /* If that worked, we're done */
364 if (rv_A == APP_SUCCESS) {
365 return rv_A;
366 }
367
368 /* Else try image B */
369 hdr = (struct SignedHeader *)(image.data() + offset_B);
370 rv_B = try_update(app, image, offset_B, hdr->image_size);
371
372 return rv_B;
373}
374
375uint32_t do_version(AppClient &app)
376{
377 uint32_t retval;
378 std::vector<uint8_t> buffer;
379 buffer.reserve(512);
380
381 retval = app.Call(NUGGET_PARAM_VERSION, buffer, &buffer);
382
383 if (is_app_success(retval)) {
384 printf("%.*s\n", (int) buffer.size(), buffer.data());
385 }
386
387 return retval;
388}
389
Andrew Sculld2c3a232018-03-29 20:24:11 +0100390uint32_t do_id(AppClient &app)
391{
392 uint32_t retval;
393 std::vector<uint8_t> buffer;
394 buffer.reserve(32);
395
396 retval = app.Call(NUGGET_PARAM_DEVICE_ID, buffer, &buffer);
397
398 if (is_app_success(retval)) {
399 printf("%.*s\n", (int) buffer.size(), buffer.data());
400 }
401
402 return retval;
403}
404
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700405uint32_t do_long_version(AppClient &app)
406{
407 uint32_t retval;
408 std::vector<uint8_t> buffer;
409 buffer.reserve(1024);
410
411 retval = app.Call(NUGGET_PARAM_LONG_VERSION, buffer, &buffer);
412
413 if (is_app_success(retval)) {
414 printf("%.*s\n", (int)buffer.size(), buffer.data());
415 }
416
417 return retval;
418}
419
420static enum hdr_section parse_section(const char *str)
421{
422 bool is_ro, is_a;
423
424 // matching this: /r?[ow]_?[ab]/i
425
426 if (tolower(*str) == 'r') {
427 str++;
428 }
429
430 if (tolower(*str) == 'o') {
431 is_ro = true;
432 } else if (tolower(*str) == 'w') {
433 is_ro = false;
434 } else {
435 Error("Invalid section \"%s\"", str);
436 return SEC_BOGUS;
437 }
438 str++;
439
440 if (*str == '_') {
441 str++;
442 }
443
444 if (tolower(*str) == 'a') {
445 is_a = true;
446 } else if (tolower(*str) == 'b') {
447 is_a = false;
448 } else {
449 Error("Invalid section \"%s\"", str);
450 return SEC_BOGUS;
451 }
452
453 if (is_ro) {
454 return is_a ? SEC_RO_A : SEC_RO_B;
455 }
456
457 return is_a ? SEC_RW_A : SEC_RW_B;
458}
459
460static void show_header(const uint8_t *ptr)
461{
462 const struct SignedHeader *hdr;
463
464 hdr = reinterpret_cast<const struct SignedHeader*>(ptr);
465 hdr->print();
466}
467
468#define CROS_EC_VERSION_COOKIE1 0xce112233
469#define CROS_EC_VERSION_COOKIE2 0xce445566
470
471// The start of the RW sections looks like this
472struct compiled_version_struct {
473 // The header comes first
474 const struct SignedHeader hdr;
475 // The the vector table. Citadel has 239 entries
476 uint32_t vectors[239];
477 // A magic number to be sure we're looking at the right thing
478 uint32_t cookie1;
479 // Then the short version string
480 char version[32];
481 // And another magic number
482 uint32_t cookie2;
483};
484
485static void show_ro_string(const char *name, const uint8_t *ptr)
486{
487 const struct SignedHeader *hdr;
488
489 hdr = reinterpret_cast<const struct SignedHeader*>(ptr);
490 printf("%s: %d.%d.%d/%08x %s\n", name,
491 hdr->epoch_, hdr->major_, hdr->minor_, be32toh(hdr->img_chk_),
492 hdr->magic == MAGIC_VALID ? "ok" : "--");
493}
494
495static void show_rw_string(const char *name, const uint8_t *ptr)
496{
497 const struct compiled_version_struct *v;
498 v = reinterpret_cast<const struct compiled_version_struct*>(ptr);
499
500 if (v->cookie1 == CROS_EC_VERSION_COOKIE1 &&
501 v->cookie2 == CROS_EC_VERSION_COOKIE2 &&
502 (v->hdr.magic == MAGIC_DEFAULT || v->hdr.magic == MAGIC_VALID)) {
503 printf("%s: %d.%d.%d/%s %s\n", name,
504 v->hdr.epoch_, v->hdr.major_, v->hdr.minor_, v->version,
505 v->hdr.magic == MAGIC_VALID ? "ok" : "--");
506 } else {
507 printf("<invalid>\n");
508 }
509}
510
511uint32_t do_section(AppClient &app __attribute__((unused)))
512{
513 uint16_t param;
514
515 switch (options.section) {
516 case SEC_RO_A:
517 param = NUGGET_PARAM_HEADER_RO_A;
518 break;
519 case SEC_RO_B:
520 param = NUGGET_PARAM_HEADER_RO_B;
521 break;
522 case SEC_RW_A:
523 param = NUGGET_PARAM_HEADER_RW_A;
524 break;
525 case SEC_RW_B:
526 param = NUGGET_PARAM_HEADER_RW_B;
527 break;
528 default:
529 return 1;
530 }
531
532 uint32_t retval;
533 std::vector<uint8_t> buffer;
534 buffer.reserve(sizeof(SignedHeader));
535
536 retval = app.Call(param, buffer, &buffer);
537
538 if (is_app_success(retval)) {
539 show_header(buffer.data());
540 }
541
542 return retval;
543}
544
545uint32_t do_file_version(const std::vector<uint8_t> &image)
546{
547 show_ro_string("RO_A", image.data() + CHIP_RO_A_MEM_OFF);
548 show_ro_string("RO_B", image.data() + CHIP_RO_B_MEM_OFF);
549 show_rw_string("RW_A", image.data() + CHIP_RW_A_MEM_OFF);
550 show_rw_string("RW_B", image.data() + CHIP_RW_B_MEM_OFF);
551 return 0;
552}
553
554uint32_t do_file_section(const std::vector<uint8_t> &image)
555{
556 switch (options.file_section) {
557 case SEC_RO_A:
558 show_header(image.data() + CHIP_RO_A_MEM_OFF);
559 break;
560 case SEC_RO_B:
561 show_header(image.data() + CHIP_RO_B_MEM_OFF);
562 break;
563 case SEC_RW_A:
564 show_header(image.data() + CHIP_RW_A_MEM_OFF);
565 break;
566 case SEC_RW_B:
567 show_header(image.data() + CHIP_RW_B_MEM_OFF);
568 break;
569 default:
570 return 1;
571 }
572
573 return 0;
574}
575
Bill Richardson30558862018-04-27 16:11:12 -0700576uint32_t do_repo_snapshot(AppClient &app)
577{
578 uint32_t retval;
579 std::vector<uint8_t> buffer;
580 buffer.reserve(1200);
581
582 retval = app.Call(NUGGET_PARAM_REPO_SNAPSHOT, buffer, &buffer);
583
584 if (is_app_success(retval)) {
585 printf("%.*s\n", (int)buffer.size(), buffer.data());
586 }
587
588 return retval;
589}
590
Bill Richardson2917cb22018-01-26 11:59:26 -0800591uint32_t do_stats(AppClient &app)
592{
593 struct nugget_app_low_power_stats stats;
594 std::vector<uint8_t> buffer;
595 uint32_t retval;
596
597 buffer.reserve(sizeof(stats));
598
599 retval = app.Call(NUGGET_PARAM_GET_LOW_POWER_STATS, buffer, &buffer);
600
601 if (is_app_success(retval)) {
602 if (buffer.size() < sizeof(stats)) {
Allen Webb2fdba9b2018-03-30 15:05:33 -0700603 fprintf(stderr, "Only got %zd / %zd bytes back",
Bill Richardson2917cb22018-01-26 11:59:26 -0800604 buffer.size(), sizeof(stats));
605 return -1;
606 }
607
608 memcpy(&stats, buffer.data(), sizeof(stats));
609
Allen Webb6f09c472018-03-30 14:55:29 -0700610 printf("hard_reset_count %" PRIu64 "\n", stats.hard_reset_count);
611 printf("time_since_hard_reset %" PRIu64 "\n",
612 stats.time_since_hard_reset);
613 printf("wake_count %" PRIu64 "\n", stats.wake_count);
614 printf("time_at_last_wake %" PRIu64 "\n", stats.time_at_last_wake);
615 printf("time_spent_awake %" PRIu64 "\n", stats.time_spent_awake);
616 printf("deep_sleep_count %" PRIu64 "\n", stats.deep_sleep_count);
617 printf("time_at_last_deep_sleep %" PRIu64 "\n",
Allen Webbc4cc3c72018-03-30 14:44:11 -0700618 stats.time_at_last_deep_sleep);
Allen Webb6f09c472018-03-30 14:55:29 -0700619 printf("time_spent_in_deep_sleep %" PRIu64 "\n",
Allen Webbc4cc3c72018-03-30 14:44:11 -0700620 stats.time_spent_in_deep_sleep);
Bill Richardson2917cb22018-01-26 11:59:26 -0800621 }
622
623 return retval;
624}
625
Andrew Scull43d72e12017-11-20 14:14:55 +0000626uint32_t do_reboot(AppClient &app)
627{
628 uint32_t retval;
Bill Richardson580e93a2018-04-25 16:43:24 -0700629 std::vector<uint8_t> ignored = {1}; // older images need this
Andrew Scull43d72e12017-11-20 14:14:55 +0000630
Bill Richardson580e93a2018-04-25 16:43:24 -0700631 retval = app.Call(NUGGET_PARAM_REBOOT, ignored, nullptr);
Andrew Scull43d72e12017-11-20 14:14:55 +0000632
633 if (is_app_success(retval)) {
634 printf("Citadel reboot requested\n");
635 }
636
637 return retval;
638}
639
Bill Richardson41896c72018-01-18 21:43:12 -0800640static uint32_t do_change_pw(AppClient &app,
641 const char *old_pw, const char *new_pw)
642{
643 std::vector<uint8_t> data(sizeof(struct nugget_app_change_update_password));
644 struct nugget_app_change_update_password *s =
645 (struct nugget_app_change_update_password*)data.data();
646
647
648 memset(s, 0xff, sizeof(*s));
649 if (old_pw && *old_pw) {
650 int len = strlen(old_pw);
651 memcpy(&s->old_password.password, old_pw, len);
652 s->old_password.digest =
653 compute_digest(&s->old_password.password,
654 sizeof(s->old_password.password));
655 }
656
657 if (new_pw && *new_pw) {
658 int len = strlen(new_pw);
659 memcpy(&s->new_password.password, new_pw, len);
660 s->new_password.digest =
661 compute_digest(&s->new_password.password,
662 sizeof(s->new_password.password));
663 }
664
665 uint32_t rv = app.Call(NUGGET_PARAM_CHANGE_UPDATE_PASSWORD, data, nullptr);
666
667 if (is_app_success(rv))
668 printf("Password changed\n");
669
670 return rv;
671}
672
673static uint32_t do_enable(AppClient &app, const char *pw)
674{
Bill Richardson41896c72018-01-18 21:43:12 -0800675 std::vector<uint8_t> data(sizeof(struct nugget_app_enable_update));
676 struct nugget_app_enable_update *s =
677 (struct nugget_app_enable_update*)data.data();
Bill Richardson9e3ce2e2018-06-04 11:44:51 -0700678 std::vector<uint8_t> reply;
Bill Richardson41896c72018-01-18 21:43:12 -0800679
680 memset(&s->password, 0xff, sizeof(s->password));
681 if (pw && *pw) {
682 int len = strlen(pw);
683 memcpy(&s->password.password, pw, len);
684 s->password.digest =
685 compute_digest(&s->password.password,
686 sizeof(s->password.password));
687 }
688 s->which_headers = options.enable_ro ? NUGGET_ENABLE_HEADER_RO : 0;
689 s->which_headers |= options.enable_rw ? NUGGET_ENABLE_HEADER_RW : 0;
690
Bill Richardson9e3ce2e2018-06-04 11:44:51 -0700691 reply.reserve(1);
692 uint32_t rv = app.Call(NUGGET_PARAM_ENABLE_UPDATE, data, &reply);
Bill Richardson41896c72018-01-18 21:43:12 -0800693
694 if (is_app_success(rv))
Bill Richardson9e3ce2e2018-06-04 11:44:51 -0700695 /* Reply byte is true only if header was CHANGED to valid */
696 printf("Update %s enabled\n", reply[0] ? "changed to" : "is already");
Bill Richardson41896c72018-01-18 21:43:12 -0800697
698 return rv;
699}
700
Bill Richardsond3eaf972018-05-23 15:23:33 -0700701static uint32_t do_ap_uart(AppClient &app)
702{
703 std::vector<uint8_t> buffer;
704 buffer.reserve(1);
705
706 static const char * const cfgstr[] = {
707 "disabled", "USB", "enabled", "SSC", "Citadel",
708 };
709 static_assert(sizeof(cfgstr)/sizeof(cfgstr[0]) == NUGGET_AP_UART_NUM_CFGS,
710 "Bad size of constant array");
711
712 uint32_t rv = app.Call(NUGGET_PARAM_AP_UART_PASSTHRU, buffer, &buffer);
713
714 if (is_app_success(rv))
715 printf("Current AP UART setting is %s\n", cfgstr[buffer[0]]);
716
717 return rv;
718}
719
720
Bill Richardson41896c72018-01-18 21:43:12 -0800721static uint32_t do_erase(AppClient &app)
722{
723 std::vector<uint8_t> data(sizeof(uint32_t));
724 memcpy(data.data(), &options.erase_code, data.size());
725
726 uint32_t rv = app.Call(NUGGET_PARAM_NUKE_FROM_ORBIT, data, nullptr);
727
728 if (is_app_success(rv))
729 printf("Citadel erase and reboot requested\n");
730
731 return rv;
732}
733
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700734// This is currently device-specific, but could be abstracted further
Andrew Scull43d72e12017-11-20 14:14:55 +0000735#ifdef ANDROID
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700736static uint32_t do_force_reset(CitadeldProxyClient &client)
737{
738 bool b = false;
739
740 return !client.Citadeld().reset(&b).isOk();
Andrew Scull43d72e12017-11-20 14:14:55 +0000741}
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700742#else
743static uint32_t do_force_reset(NuggetClient &client)
744{
745 struct nos_device *d = client.Device();
746
747 return d->ops.reset(d->ctx);
748}
749#endif
Andrew Scull43d72e12017-11-20 14:14:55 +0000750
Bill Richardson41896c72018-01-18 21:43:12 -0800751int execute_commands(const std::vector<uint8_t> &image,
752 const char *old_passwd, const char *passwd)
Andrew Scull43d72e12017-11-20 14:14:55 +0000753{
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700754#ifdef ANDROID
755 CitadeldProxyClient client;
756#else
757 NuggetClient client(options.device ? options.device : "");
758#endif
759
760 client.Open();
761 if (!client.IsOpen()) {
Andrew Scull43d72e12017-11-20 14:14:55 +0000762 Error("Unable to connect");
763 return 1;
764 }
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700765 AppClient app(client, APP_ID_NUGGET);
Andrew Scull43d72e12017-11-20 14:14:55 +0000766
767 /* Try all requested actions in reasonable order, bail out on error */
768
Bill Richardsond3eaf972018-05-23 15:23:33 -0700769 if (options.ap_uart &&
770 do_ap_uart(app) != APP_SUCCESS) {
771 return 1;
772 }
773
Bill Richardson41896c72018-01-18 21:43:12 -0800774 if (options.erase_code) { /* zero doesn't count */
775 /* whether we succeed or not, only do this */
776 return do_erase(app);
777 }
778
Andrew Scull43d72e12017-11-20 14:14:55 +0000779 if (options.version &&
780 do_version(app) != APP_SUCCESS) {
781 return 2;
782 }
783
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700784 if (options.long_version &&
785 do_long_version(app) != APP_SUCCESS) {
786 return 2;
787 }
788
789 if (options.section &&
790 do_section(app) != APP_SUCCESS) {
791 return 2;
792 }
793
794 if (options.file_version &&
795 do_file_version(image) != APP_SUCCESS) {
796 return 2;
797 }
798
799 if (options.file_section &&
800 do_file_section(image) != APP_SUCCESS) {
801 return 2;
802 }
803
Andrew Sculld2c3a232018-03-29 20:24:11 +0100804 if (options.id &&
805 do_id(app) != APP_SUCCESS) {
806 return 2;
807 }
808
Bill Richardson2917cb22018-01-26 11:59:26 -0800809 if (options.stats &&
810 do_stats(app) != APP_SUCCESS) {
811 return 2;
812 }
813
Bill Richardson30558862018-04-27 16:11:12 -0700814 if (options.repo_snapshot &&
815 do_repo_snapshot(app) != APP_SUCCESS) {
816 return 2;
817 }
Andrew Scull43d72e12017-11-20 14:14:55 +0000818 if (options.rw &&
819 do_update(app, image,
820 CHIP_RW_A_MEM_OFF, CHIP_RW_B_MEM_OFF) != APP_SUCCESS) {
821 return 3;
822 }
823
824 if (options.ro &&
825 do_update(app, image,
826 CHIP_RO_A_MEM_OFF, CHIP_RO_B_MEM_OFF) != APP_SUCCESS) {
827 return 4;
828 }
829
Bill Richardson41896c72018-01-18 21:43:12 -0800830 if (options.change_pw &&
831 do_change_pw(app, old_passwd, passwd) != APP_SUCCESS)
832 return 5;
833
834 if ((options.enable_ro || options.enable_rw) &&
835 do_enable(app, passwd) != APP_SUCCESS)
836 return 6;
837
Andrew Scull43d72e12017-11-20 14:14:55 +0000838 if (options.reboot &&
839 do_reboot(app) != APP_SUCCESS) {
Bill Richardson41896c72018-01-18 21:43:12 -0800840 return 7;
Andrew Scull43d72e12017-11-20 14:14:55 +0000841 }
Bill Richardson41896c72018-01-18 21:43:12 -0800842
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700843 if (options.force_reset &&
844 do_force_reset(client) != APP_SUCCESS) {
845 return 1;
846 }
847
Andrew Scull43d72e12017-11-20 14:14:55 +0000848 return 0;
849}
850
851} // namespace
852
853int main(int argc, char *argv[])
854{
855 int i;
856 int idx = 0;
857 char *this_prog;
Bill Richardson41896c72018-01-18 21:43:12 -0800858 char *old_passwd = 0;
859 char *passwd = 0;
Andrew Scull43d72e12017-11-20 14:14:55 +0000860 std::vector<uint8_t> image;
861 int got_action = 0;
Bill Richardson41896c72018-01-18 21:43:12 -0800862 char *e = 0;
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700863 int need_file = 0;
Andrew Scull43d72e12017-11-20 14:14:55 +0000864
865 this_prog= strrchr(argv[0], '/');
Bill Richardson153fa252018-02-23 14:39:58 -0800866 if (this_prog) {
Andrew Scull43d72e12017-11-20 14:14:55 +0000867 this_prog++;
868 } else {
869 this_prog = argv[0];
870 }
871
Bill Richardson153fa252018-02-23 14:39:58 -0800872#ifndef ANDROID
873 options.device = secure_getenv("CITADEL_DEVICE");
874 if (options.device)
875 fprintf(stderr, "-- $CITADEL_DEVICE=%s --\n", options.device);
876#endif
877
878 opterr = 0; /* quiet, you */
Andrew Scull43d72e12017-11-20 14:14:55 +0000879 while ((i = getopt_long(argc, argv,
880 short_opts, long_opts, &idx)) != -1) {
881 switch (i) {
882 /* program-specific options */
883 case 'v':
884 options.version = 1;
885 got_action = 1;
886 break;
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700887 case 'l':
888 options.long_version = 1;
889 got_action = 1;
890 break;
891 case 'V':
892 options.section = parse_section(optarg);
893 got_action = 1;
894 break;
895 case 'f':
896 options.file_version = 1;
897 need_file = 1;
898 got_action = 1;
899 break;
900 case 'F':
901 options.file_section = parse_section(optarg);
902 need_file = 1;
903 got_action = 1;
904 break;
Andrew Sculld2c3a232018-03-29 20:24:11 +0100905 case OPT_ID:
906 options.id = 1;
907 got_action = 1;
908 break;
Bill Richardson30558862018-04-27 16:11:12 -0700909 case OPT_REPO_SNAPSHOT:
910 options.repo_snapshot = 1;
911 got_action = 1;
912 break;
Bill Richardson2917cb22018-01-26 11:59:26 -0800913 case OPT_STATS:
914 options.stats = 1;
915 got_action = 1;
916 break;
Andrew Scull43d72e12017-11-20 14:14:55 +0000917 case OPT_RO:
918 options.ro = 1;
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700919 need_file = 1;
Andrew Scull43d72e12017-11-20 14:14:55 +0000920 got_action = 1;
921 break;
922 case OPT_RW:
923 options.rw = 1;
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700924 need_file = 1;
Andrew Scull43d72e12017-11-20 14:14:55 +0000925 got_action = 1;
926 break;
927 case OPT_REBOOT:
928 options.reboot = 1;
929 got_action = 1;
930 break;
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700931 case OPT_FORCE_RESET:
932 options.force_reset = 1;
933 got_action = 1;
934 break;
Bill Richardson41896c72018-01-18 21:43:12 -0800935 case OPT_ENABLE_RO:
936 options.enable_ro = 1;
937 got_action = 1;
938 break;
939 case OPT_ENABLE_RW:
940 options.enable_rw = 1;
941 got_action = 1;
942 break;
943 case OPT_CHANGE_PW:
944 options.change_pw = 1;
945 got_action = 1;
946 break;
947 case OPT_ERASE:
948 options.erase_code = (uint32_t)strtoul(optarg, &e, 0);
949 if (!*optarg || (e && *e)) {
950 Error("Invalid argument: \"%s\"\n", optarg);
951 errorcnt++;
952 }
953 got_action = 1;
954 break;
Bill Richardsond3eaf972018-05-23 15:23:33 -0700955 case OPT_AP_UART:
956 options.ap_uart = 1;
957 got_action = 1;
958 break;
Andrew Scull43d72e12017-11-20 14:14:55 +0000959
960 /* generic options below */
961 case OPT_DEVICE:
962 options.device = optarg;
963 break;
Andrew Scull43d72e12017-11-20 14:14:55 +0000964 case 'h':
965 usage(this_prog);
966 return 0;
967 case 0:
968 break;
969 case '?':
970 if (optopt)
971 Error("Unrecognized options: -%c", optopt);
972 else
973 Error("Unrecognized options: %s",
974 argv[optind - 1]);
975 usage(this_prog);
976 break;
977 case ':':
978 Error("Missing argument to %s", argv[optind - 1]);
979 break;
980 default:
981 Error("Internal error at %s:%d", __FILE__, __LINE__);
982 exit(1);
983 }
984 }
985
986 if (errorcnt) {
987 goto out;
988 }
989
990 if (!got_action) {
991 usage(this_prog);
992 goto out;
993 }
994
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700995 if (need_file) {
Andrew Scull43d72e12017-11-20 14:14:55 +0000996 if (optind < argc) {
997 /* Sets errorcnt on failure */
Bill Richardson76b40fe2018-01-22 10:28:24 -0800998 image = read_image_from_file(argv[optind++]);
Bill Richardson41896c72018-01-18 21:43:12 -0800999 if (errorcnt)
1000 goto out;
Andrew Scull43d72e12017-11-20 14:14:55 +00001001 } else {
Bill Richardsonb49b34d2018-04-27 10:29:38 -07001002 Error("Missing required image file");
Bill Richardson41896c72018-01-18 21:43:12 -08001003 goto out;
Andrew Scull43d72e12017-11-20 14:14:55 +00001004 }
1005 }
1006
Bill Richardson41896c72018-01-18 21:43:12 -08001007 if (options.change_pw) {
1008 /* one arg provided, maybe the old password isn't set */
1009 if (optind < argc) {
1010 passwd = argv[optind++];
1011 } else {
Bill Richardsonb49b34d2018-04-27 10:29:38 -07001012 Error("Need a new password at least. Use '' to clear it.");
Bill Richardson41896c72018-01-18 21:43:12 -08001013 goto out;
1014 }
1015 /* two args provided, use both old & new passwords */
1016 if (optind < argc) {
1017 old_passwd = passwd;
1018 passwd = argv[optind++];
1019 }
1020 }
Andrew Scull43d72e12017-11-20 14:14:55 +00001021
Bill Richardson41896c72018-01-18 21:43:12 -08001022 if ((options.enable_ro || options.enable_rw) && !passwd) {
1023 if (optind < argc)
1024 passwd = argv[optind++];
1025 else {
1026 Error("Need a password to enable images. Use '' if none.");
1027 goto out;
1028 }
1029 }
1030
1031 /* Okay, let's do it! */
1032 (void) execute_commands(image, old_passwd, passwd);
Andrew Scull43d72e12017-11-20 14:14:55 +00001033 /* This is the last action, so fall through either way */
1034
1035out:
1036 return !!errorcnt;
1037}