blob: ecc8c355462114e5109b21f323fda95e2304d86b [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();
678
679 memset(&s->password, 0xff, sizeof(s->password));
680 if (pw && *pw) {
681 int len = strlen(pw);
682 memcpy(&s->password.password, pw, len);
683 s->password.digest =
684 compute_digest(&s->password.password,
685 sizeof(s->password.password));
686 }
687 s->which_headers = options.enable_ro ? NUGGET_ENABLE_HEADER_RO : 0;
688 s->which_headers |= options.enable_rw ? NUGGET_ENABLE_HEADER_RW : 0;
689
690 uint32_t rv = app.Call(NUGGET_PARAM_ENABLE_UPDATE, data, nullptr);
691
692 if (is_app_success(rv))
693 printf("Update enabled\n");
694
695 return rv;
696}
697
Bill Richardsond3eaf972018-05-23 15:23:33 -0700698static uint32_t do_ap_uart(AppClient &app)
699{
700 std::vector<uint8_t> buffer;
701 buffer.reserve(1);
702
703 static const char * const cfgstr[] = {
704 "disabled", "USB", "enabled", "SSC", "Citadel",
705 };
706 static_assert(sizeof(cfgstr)/sizeof(cfgstr[0]) == NUGGET_AP_UART_NUM_CFGS,
707 "Bad size of constant array");
708
709 uint32_t rv = app.Call(NUGGET_PARAM_AP_UART_PASSTHRU, buffer, &buffer);
710
711 if (is_app_success(rv))
712 printf("Current AP UART setting is %s\n", cfgstr[buffer[0]]);
713
714 return rv;
715}
716
717
Bill Richardson41896c72018-01-18 21:43:12 -0800718static uint32_t do_erase(AppClient &app)
719{
720 std::vector<uint8_t> data(sizeof(uint32_t));
721 memcpy(data.data(), &options.erase_code, data.size());
722
723 uint32_t rv = app.Call(NUGGET_PARAM_NUKE_FROM_ORBIT, data, nullptr);
724
725 if (is_app_success(rv))
726 printf("Citadel erase and reboot requested\n");
727
728 return rv;
729}
730
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700731// This is currently device-specific, but could be abstracted further
Andrew Scull43d72e12017-11-20 14:14:55 +0000732#ifdef ANDROID
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700733static uint32_t do_force_reset(CitadeldProxyClient &client)
734{
735 bool b = false;
736
737 return !client.Citadeld().reset(&b).isOk();
Andrew Scull43d72e12017-11-20 14:14:55 +0000738}
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700739#else
740static uint32_t do_force_reset(NuggetClient &client)
741{
742 struct nos_device *d = client.Device();
743
744 return d->ops.reset(d->ctx);
745}
746#endif
Andrew Scull43d72e12017-11-20 14:14:55 +0000747
Bill Richardson41896c72018-01-18 21:43:12 -0800748int execute_commands(const std::vector<uint8_t> &image,
749 const char *old_passwd, const char *passwd)
Andrew Scull43d72e12017-11-20 14:14:55 +0000750{
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700751#ifdef ANDROID
752 CitadeldProxyClient client;
753#else
754 NuggetClient client(options.device ? options.device : "");
755#endif
756
757 client.Open();
758 if (!client.IsOpen()) {
Andrew Scull43d72e12017-11-20 14:14:55 +0000759 Error("Unable to connect");
760 return 1;
761 }
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700762 AppClient app(client, APP_ID_NUGGET);
Andrew Scull43d72e12017-11-20 14:14:55 +0000763
764 /* Try all requested actions in reasonable order, bail out on error */
765
Bill Richardsond3eaf972018-05-23 15:23:33 -0700766 if (options.ap_uart &&
767 do_ap_uart(app) != APP_SUCCESS) {
768 return 1;
769 }
770
Bill Richardson41896c72018-01-18 21:43:12 -0800771 if (options.erase_code) { /* zero doesn't count */
772 /* whether we succeed or not, only do this */
773 return do_erase(app);
774 }
775
Andrew Scull43d72e12017-11-20 14:14:55 +0000776 if (options.version &&
777 do_version(app) != APP_SUCCESS) {
778 return 2;
779 }
780
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700781 if (options.long_version &&
782 do_long_version(app) != APP_SUCCESS) {
783 return 2;
784 }
785
786 if (options.section &&
787 do_section(app) != APP_SUCCESS) {
788 return 2;
789 }
790
791 if (options.file_version &&
792 do_file_version(image) != APP_SUCCESS) {
793 return 2;
794 }
795
796 if (options.file_section &&
797 do_file_section(image) != APP_SUCCESS) {
798 return 2;
799 }
800
Andrew Sculld2c3a232018-03-29 20:24:11 +0100801 if (options.id &&
802 do_id(app) != APP_SUCCESS) {
803 return 2;
804 }
805
Bill Richardson2917cb22018-01-26 11:59:26 -0800806 if (options.stats &&
807 do_stats(app) != APP_SUCCESS) {
808 return 2;
809 }
810
Bill Richardson30558862018-04-27 16:11:12 -0700811 if (options.repo_snapshot &&
812 do_repo_snapshot(app) != APP_SUCCESS) {
813 return 2;
814 }
Andrew Scull43d72e12017-11-20 14:14:55 +0000815 if (options.rw &&
816 do_update(app, image,
817 CHIP_RW_A_MEM_OFF, CHIP_RW_B_MEM_OFF) != APP_SUCCESS) {
818 return 3;
819 }
820
821 if (options.ro &&
822 do_update(app, image,
823 CHIP_RO_A_MEM_OFF, CHIP_RO_B_MEM_OFF) != APP_SUCCESS) {
824 return 4;
825 }
826
Bill Richardson41896c72018-01-18 21:43:12 -0800827 if (options.change_pw &&
828 do_change_pw(app, old_passwd, passwd) != APP_SUCCESS)
829 return 5;
830
831 if ((options.enable_ro || options.enable_rw) &&
832 do_enable(app, passwd) != APP_SUCCESS)
833 return 6;
834
Andrew Scull43d72e12017-11-20 14:14:55 +0000835 if (options.reboot &&
836 do_reboot(app) != APP_SUCCESS) {
Bill Richardson41896c72018-01-18 21:43:12 -0800837 return 7;
Andrew Scull43d72e12017-11-20 14:14:55 +0000838 }
Bill Richardson41896c72018-01-18 21:43:12 -0800839
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700840 if (options.force_reset &&
841 do_force_reset(client) != APP_SUCCESS) {
842 return 1;
843 }
844
Andrew Scull43d72e12017-11-20 14:14:55 +0000845 return 0;
846}
847
848} // namespace
849
850int main(int argc, char *argv[])
851{
852 int i;
853 int idx = 0;
854 char *this_prog;
Bill Richardson41896c72018-01-18 21:43:12 -0800855 char *old_passwd = 0;
856 char *passwd = 0;
Andrew Scull43d72e12017-11-20 14:14:55 +0000857 std::vector<uint8_t> image;
858 int got_action = 0;
Bill Richardson41896c72018-01-18 21:43:12 -0800859 char *e = 0;
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700860 int need_file = 0;
Andrew Scull43d72e12017-11-20 14:14:55 +0000861
862 this_prog= strrchr(argv[0], '/');
Bill Richardson153fa252018-02-23 14:39:58 -0800863 if (this_prog) {
Andrew Scull43d72e12017-11-20 14:14:55 +0000864 this_prog++;
865 } else {
866 this_prog = argv[0];
867 }
868
Bill Richardson153fa252018-02-23 14:39:58 -0800869#ifndef ANDROID
870 options.device = secure_getenv("CITADEL_DEVICE");
871 if (options.device)
872 fprintf(stderr, "-- $CITADEL_DEVICE=%s --\n", options.device);
873#endif
874
875 opterr = 0; /* quiet, you */
Andrew Scull43d72e12017-11-20 14:14:55 +0000876 while ((i = getopt_long(argc, argv,
877 short_opts, long_opts, &idx)) != -1) {
878 switch (i) {
879 /* program-specific options */
880 case 'v':
881 options.version = 1;
882 got_action = 1;
883 break;
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700884 case 'l':
885 options.long_version = 1;
886 got_action = 1;
887 break;
888 case 'V':
889 options.section = parse_section(optarg);
890 got_action = 1;
891 break;
892 case 'f':
893 options.file_version = 1;
894 need_file = 1;
895 got_action = 1;
896 break;
897 case 'F':
898 options.file_section = parse_section(optarg);
899 need_file = 1;
900 got_action = 1;
901 break;
Andrew Sculld2c3a232018-03-29 20:24:11 +0100902 case OPT_ID:
903 options.id = 1;
904 got_action = 1;
905 break;
Bill Richardson30558862018-04-27 16:11:12 -0700906 case OPT_REPO_SNAPSHOT:
907 options.repo_snapshot = 1;
908 got_action = 1;
909 break;
Bill Richardson2917cb22018-01-26 11:59:26 -0800910 case OPT_STATS:
911 options.stats = 1;
912 got_action = 1;
913 break;
Andrew Scull43d72e12017-11-20 14:14:55 +0000914 case OPT_RO:
915 options.ro = 1;
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700916 need_file = 1;
Andrew Scull43d72e12017-11-20 14:14:55 +0000917 got_action = 1;
918 break;
919 case OPT_RW:
920 options.rw = 1;
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700921 need_file = 1;
Andrew Scull43d72e12017-11-20 14:14:55 +0000922 got_action = 1;
923 break;
924 case OPT_REBOOT:
925 options.reboot = 1;
926 got_action = 1;
927 break;
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700928 case OPT_FORCE_RESET:
929 options.force_reset = 1;
930 got_action = 1;
931 break;
Bill Richardson41896c72018-01-18 21:43:12 -0800932 case OPT_ENABLE_RO:
933 options.enable_ro = 1;
934 got_action = 1;
935 break;
936 case OPT_ENABLE_RW:
937 options.enable_rw = 1;
938 got_action = 1;
939 break;
940 case OPT_CHANGE_PW:
941 options.change_pw = 1;
942 got_action = 1;
943 break;
944 case OPT_ERASE:
945 options.erase_code = (uint32_t)strtoul(optarg, &e, 0);
946 if (!*optarg || (e && *e)) {
947 Error("Invalid argument: \"%s\"\n", optarg);
948 errorcnt++;
949 }
950 got_action = 1;
951 break;
Bill Richardsond3eaf972018-05-23 15:23:33 -0700952 case OPT_AP_UART:
953 options.ap_uart = 1;
954 got_action = 1;
955 break;
Andrew Scull43d72e12017-11-20 14:14:55 +0000956
957 /* generic options below */
958 case OPT_DEVICE:
959 options.device = optarg;
960 break;
Andrew Scull43d72e12017-11-20 14:14:55 +0000961 case 'h':
962 usage(this_prog);
963 return 0;
964 case 0:
965 break;
966 case '?':
967 if (optopt)
968 Error("Unrecognized options: -%c", optopt);
969 else
970 Error("Unrecognized options: %s",
971 argv[optind - 1]);
972 usage(this_prog);
973 break;
974 case ':':
975 Error("Missing argument to %s", argv[optind - 1]);
976 break;
977 default:
978 Error("Internal error at %s:%d", __FILE__, __LINE__);
979 exit(1);
980 }
981 }
982
983 if (errorcnt) {
984 goto out;
985 }
986
987 if (!got_action) {
988 usage(this_prog);
989 goto out;
990 }
991
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700992 if (need_file) {
Andrew Scull43d72e12017-11-20 14:14:55 +0000993 if (optind < argc) {
994 /* Sets errorcnt on failure */
Bill Richardson76b40fe2018-01-22 10:28:24 -0800995 image = read_image_from_file(argv[optind++]);
Bill Richardson41896c72018-01-18 21:43:12 -0800996 if (errorcnt)
997 goto out;
Andrew Scull43d72e12017-11-20 14:14:55 +0000998 } else {
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700999 Error("Missing required image file");
Bill Richardson41896c72018-01-18 21:43:12 -08001000 goto out;
Andrew Scull43d72e12017-11-20 14:14:55 +00001001 }
1002 }
1003
Bill Richardson41896c72018-01-18 21:43:12 -08001004 if (options.change_pw) {
1005 /* one arg provided, maybe the old password isn't set */
1006 if (optind < argc) {
1007 passwd = argv[optind++];
1008 } else {
Bill Richardsonb49b34d2018-04-27 10:29:38 -07001009 Error("Need a new password at least. Use '' to clear it.");
Bill Richardson41896c72018-01-18 21:43:12 -08001010 goto out;
1011 }
1012 /* two args provided, use both old & new passwords */
1013 if (optind < argc) {
1014 old_passwd = passwd;
1015 passwd = argv[optind++];
1016 }
1017 }
Andrew Scull43d72e12017-11-20 14:14:55 +00001018
Bill Richardson41896c72018-01-18 21:43:12 -08001019 if ((options.enable_ro || options.enable_rw) && !passwd) {
1020 if (optind < argc)
1021 passwd = argv[optind++];
1022 else {
1023 Error("Need a password to enable images. Use '' if none.");
1024 goto out;
1025 }
1026 }
1027
1028 /* Okay, let's do it! */
1029 (void) execute_commands(image, old_passwd, passwd);
Andrew Scull43d72e12017-11-20 14:14:55 +00001030 /* This is the last action, so fall through either way */
1031
1032out:
1033 return !!errorcnt;
1034}