blob: 683a2851ea432af9893f2d80ba502e2b34ea7e0e [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 Richardson2917cb22018-01-26 11:59:26 -080068 int stats;
Andrew Scull43d72e12017-11-20 14:14:55 +000069 int ro;
70 int rw;
71 int reboot;
Bill Richardsonf0a6b802018-03-21 15:32:13 -070072 int force_reset;
Bill Richardson41896c72018-01-18 21:43:12 -080073 int enable_ro;
74 int enable_rw;
75 int change_pw;
76 uint32_t erase_code;
Andrew Scull43d72e12017-11-20 14:14:55 +000077 /* generic connection options */
78 const char *device;
Andrew Scull43d72e12017-11-20 14:14:55 +000079} options;
80
81enum no_short_opts_for_these {
82 OPT_DEVICE = 1000,
Andrew Sculld2c3a232018-03-29 20:24:11 +010083 OPT_ID,
Bill Richardson2917cb22018-01-26 11:59:26 -080084 OPT_STATS,
Andrew Scull43d72e12017-11-20 14:14:55 +000085 OPT_RO,
86 OPT_RW,
87 OPT_REBOOT,
Bill Richardsonf0a6b802018-03-21 15:32:13 -070088 OPT_FORCE_RESET,
Bill Richardson41896c72018-01-18 21:43:12 -080089 OPT_ENABLE_RO,
90 OPT_ENABLE_RW,
91 OPT_CHANGE_PW,
92 OPT_ERASE,
Andrew Scull43d72e12017-11-20 14:14:55 +000093};
94
Bill Richardsonb49b34d2018-04-27 10:29:38 -070095const char *short_opts = ":hvlV:fF:";
Andrew Scull43d72e12017-11-20 14:14:55 +000096const struct option long_opts[] = {
97 /* name hasarg *flag val */
Bill Richardsonb49b34d2018-04-27 10:29:38 -070098 {"version", 0, NULL, 'v'},
99 {"long_version", 0, NULL, 'l'},
100 {"id", 0, NULL, OPT_ID},
101 {"stats", 0, NULL, OPT_STATS},
102 {"ro", 0, NULL, OPT_RO},
103 {"rw", 0, NULL, OPT_RW},
104 {"reboot", 0, NULL, OPT_REBOOT},
105 {"force_reset", 0, NULL, OPT_FORCE_RESET},
106 {"enable_ro", 0, NULL, OPT_ENABLE_RO},
107 {"enable_rw", 0, NULL, OPT_ENABLE_RW},
108 {"change_pw", 0, NULL, OPT_CHANGE_PW},
109 {"erase", 1, NULL, OPT_ERASE},
Bill Richardson153fa252018-02-23 14:39:58 -0800110#ifndef ANDROID
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700111 {"device", 1, NULL, OPT_DEVICE},
Bill Richardson153fa252018-02-23 14:39:58 -0800112#endif
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700113 {"help", 0, NULL, 'h'},
Andrew Scull43d72e12017-11-20 14:14:55 +0000114 {NULL, 0, NULL, 0},
115};
116
117void usage(const char *progname)
118{
119 fprintf(stderr, "\n");
120 fprintf(stderr,
121 "Usage: %s [actions] [image.bin]\n"
122 "\n"
123 "Citadel firmware boots in two stages. The first stage\n"
124 "bootloader (aka \"RO\") is provided by the SOC hardware team\n"
125 "and seldom changes. The application image (\"RW\") is invoked\n"
126 "by the RO image. There are two copies (A/B) of each stage,\n"
127 "so that the active copy can be protected while the unused\n"
128 "copy may be updated. At boot, the newer (valid) copy of each\n"
129 "stage is selected.\n"
130 "\n"
131 "The Citadel image file is the same size of the internal\n"
132 "flash, and contains all four firmware components (RO_A,\n"
133 "RW_A, RO_B, RW_B) located at the correct offsets. Only the\n"
134 "inactive copy (A/B) of each stage (RO/RW) can be modified.\n"
135 "The tool will update the correct copies automatically.\n"
136 "\n"
Bill Richardson153fa252018-02-23 14:39:58 -0800137 "You must specify the actions to perform. With no actions,\n"
Andrew Scull43d72e12017-11-20 14:14:55 +0000138 "this help message is displayed.\n"
139 "\n"
140 "Actions:\n"
141 "\n"
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700142 " -v, --version Display the running version\n"
143 " -l, --long_version Display the full version info\n"
144 " --id Display the Citadel device ID\n"
145 " --stats Display Low Power stats\n"
146 " -V SECTION Show Citadel headers for RO_A | RO_B | RW_A | RW_B\n"
147 " -f Show image file version info\n"
148 " -F SECTION Show file headers for RO_A | RO_B | RW_A | RW_B\n"
Bill Richardson41896c72018-01-18 21:43:12 -0800149 "\n"
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700150 " --rw Update RW firmware from the image file\n"
151 " --ro Update RO firmware from the image file\n"
152 " --enable_ro Mark new RO image as good (requires password)\n"
153 " --enable_rw Mark new RW image as good (requires password)\n"
154 " --reboot Tell Citadel to reboot\n"
155 " --force_reset Pulse Citadel's reset line\n"
Bill Richardson41896c72018-01-18 21:43:12 -0800156 "\n"
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700157 " --change_pw Change the update password\n"
Bill Richardson41896c72018-01-18 21:43:12 -0800158 "\n\n"
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700159 " --erase=CODE Erase all user secrets and reboot.\n"
160 " This skips all other actions.\n"
Bill Richardson153fa252018-02-23 14:39:58 -0800161#ifndef ANDROID
162 "\n"
163 "Options:\n"
164 "\n"
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700165 " --device=SN Connect to the FDTI device with the given\n"
166 " serial number (try \"lsusb -v\"). A default\n"
167 " can be specified with the CITADEL_DEVICE\n"
168 " environment variable.\n"
Bill Richardson153fa252018-02-23 14:39:58 -0800169#endif
Andrew Scull43d72e12017-11-20 14:14:55 +0000170 "\n",
171 progname);
172}
173
174/****************************************************************************/
175/* Handy stuff */
176
177#ifndef MIN
178#define MIN(a, b) ((a) < (b) ? (a) : (b))
179#endif
180
181int errorcnt;
182void Error(const char *format, ...)
183{
184 va_list ap;
185
Bill Richardson41896c72018-01-18 21:43:12 -0800186 va_start(ap, format);
187 fprintf(stderr, "ERROR: ");
188 vfprintf(stderr, format, ap);
189 fprintf(stderr, "\n");
190 va_end(ap);
Andrew Scull43d72e12017-11-20 14:14:55 +0000191
192 errorcnt++;
193}
194
195/* Return true on APP_SUCESS, display error message if it's not */
196int is_app_success(uint32_t retval)
197{
198 if (retval == APP_SUCCESS)
199 return 1;
200
201 errorcnt++;
202
203 fprintf(stderr, "Error code 0x%x: ", retval);
204 switch (retval) {
205 case APP_ERROR_BOGUS_ARGS:
206 fprintf(stderr, "bogus args");
207 break;
208 case APP_ERROR_INTERNAL:
209 fprintf(stderr, "app is being stupid");
210 break;
211 case APP_ERROR_TOO_MUCH:
212 fprintf(stderr, "caller sent too much data");
213 break;
214 default:
215 if (retval >= APP_SPECIFIC_ERROR &&
216 retval < APP_LINE_NUMBER_BASE) {
217 fprintf(stderr, "app-specific error #%d",
218 retval - APP_SPECIFIC_ERROR);
219 } else if (retval >= APP_LINE_NUMBER_BASE) {
220 fprintf(stderr, "error at line %d",
221 retval - APP_LINE_NUMBER_BASE);
222 } else {
223 fprintf(stderr, "unknown)");
224 }
225 }
226 fprintf(stderr, "\n");
227
228 return 0;
229}
230
231/****************************************************************************/
232
233std::vector<uint8_t> read_image_from_file(const char *name)
234{
235 FILE *fp;
236 struct stat st;
237
238 fp = fopen(name, "rb");
239 if (!fp) {
240 perror("fopen");
241 Error("Can't open file %s", name);
242 return {};
243 }
244
245 if (fstat(fileno(fp), &st)) {
246 perror("fstat");
247 Error("Can't fstat file %s", name);
248 fclose(fp);
249 return {};
250 }
251
252 if (st.st_size != CHIP_FLASH_SIZE) {
253 Error("The firmware image must be exactly %d bytes",
254 CHIP_FLASH_SIZE);
255 fclose(fp);
256 return {};
257 }
258
259 std::vector<uint8_t> buf(st.st_size);
260 if (1 != fread(buf.data(), st.st_size, 1, fp)) {
261 perror("fread");
262 Error("Can't read %zd bytes", st.st_size);
263 fclose(fp);
264 return {};
265 }
266
267 fclose(fp);
268 buf.resize(st.st_size);
269
270 return buf;
271}
272
Bill Richardson41896c72018-01-18 21:43:12 -0800273uint32_t compute_digest(void *ptr, size_t len)
Andrew Scull43d72e12017-11-20 14:14:55 +0000274{
Andrew Scull43d72e12017-11-20 14:14:55 +0000275 SHA_CTX ctx;
276 uint8_t digest[SHA_DIGEST_LENGTH];
277 uint32_t retval;
278
279 SHA1_Init(&ctx);
Bill Richardson41896c72018-01-18 21:43:12 -0800280 SHA1_Update(&ctx, ptr, len);
Andrew Scull43d72e12017-11-20 14:14:55 +0000281 SHA1_Final(digest, &ctx);
282
283 memcpy(&retval, digest, sizeof(retval));
284 return retval;
285}
286
Bill Richardson41896c72018-01-18 21:43:12 -0800287uint32_t compute_fb_digest(struct nugget_app_flash_block *blk)
288{
289 uint8_t *start_here = ((uint8_t *)blk) +
290 offsetof(struct nugget_app_flash_block, offset);
291 size_t size_to_hash = sizeof(*blk) -
292 offsetof(struct nugget_app_flash_block, offset);
293
294 return compute_digest(start_here, size_to_hash);
295}
296
Andrew Scull43d72e12017-11-20 14:14:55 +0000297uint32_t try_update(AppClient &app, const std::vector<uint8_t> &image,
298 uint32_t offset, uint32_t imagesize)
299{
300 uint32_t stop = offset + imagesize;
301 uint32_t rv;
302
303 printf("Updating image from 0x%05x to 0x%05x, size 0x%05x\n",
304 CHIP_FLASH_BASE + offset, CHIP_FLASH_BASE + stop, imagesize);
305
306 for (; offset < stop; offset += CHIP_FLASH_BANK_SIZE) {
307 int retries = 3;
308 std::vector<uint8_t> data(sizeof(struct nugget_app_flash_block));
309 struct nugget_app_flash_block *fb =
310 (struct nugget_app_flash_block*)data.data();
311
312 fb->offset = offset;
313 memcpy(fb->payload, image.data() + offset, CHIP_FLASH_BANK_SIZE);
Bill Richardson41896c72018-01-18 21:43:12 -0800314 fb->block_digest = compute_fb_digest(fb);
Andrew Scull43d72e12017-11-20 14:14:55 +0000315
316 printf("writing 0x%05x / 0x%05x",
317 CHIP_FLASH_BASE + offset, CHIP_FLASH_BASE + stop);
318 do {
319 rv = app.Call(NUGGET_PARAM_FLASH_BLOCK, data, nullptr);
320 if (rv == NUGGET_ERROR_RETRY)
321 printf(" retrying");
322 } while (rv == NUGGET_ERROR_RETRY && retries--);
Bill Richardson41896c72018-01-18 21:43:12 -0800323 if (rv) {
324 if (rv == NUGGET_ERROR_LOCKED)
325 printf(" locked\n");
326 else
327 printf(" fail %d\n", rv);
Andrew Scull43d72e12017-11-20 14:14:55 +0000328 break;
Bill Richardson41896c72018-01-18 21:43:12 -0800329 }
330 printf(" ok\n");
Andrew Scull43d72e12017-11-20 14:14:55 +0000331 }
332
333 return rv;
334}
335
Andrew Scull43d72e12017-11-20 14:14:55 +0000336uint32_t do_update(AppClient &app, const std::vector<uint8_t> &image,
337 uint32_t offset_A, uint32_t offset_B)
338{
339 struct SignedHeader *hdr;
340 uint32_t rv_A, rv_B;
341
342 /* Try image A first */
343 hdr = (struct SignedHeader *)(image.data() + offset_A);
344 rv_A = try_update(app, image, offset_A, hdr->image_size);
345
346 /* If that worked, we're done */
347 if (rv_A == APP_SUCCESS) {
348 return rv_A;
349 }
350
351 /* Else try image B */
352 hdr = (struct SignedHeader *)(image.data() + offset_B);
353 rv_B = try_update(app, image, offset_B, hdr->image_size);
354
355 return rv_B;
356}
357
358uint32_t do_version(AppClient &app)
359{
360 uint32_t retval;
361 std::vector<uint8_t> buffer;
362 buffer.reserve(512);
363
364 retval = app.Call(NUGGET_PARAM_VERSION, buffer, &buffer);
365
366 if (is_app_success(retval)) {
367 printf("%.*s\n", (int) buffer.size(), buffer.data());
368 }
369
370 return retval;
371}
372
Andrew Sculld2c3a232018-03-29 20:24:11 +0100373uint32_t do_id(AppClient &app)
374{
375 uint32_t retval;
376 std::vector<uint8_t> buffer;
377 buffer.reserve(32);
378
379 retval = app.Call(NUGGET_PARAM_DEVICE_ID, buffer, &buffer);
380
381 if (is_app_success(retval)) {
382 printf("%.*s\n", (int) buffer.size(), buffer.data());
383 }
384
385 return retval;
386}
387
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700388uint32_t do_long_version(AppClient &app)
389{
390 uint32_t retval;
391 std::vector<uint8_t> buffer;
392 buffer.reserve(1024);
393
394 retval = app.Call(NUGGET_PARAM_LONG_VERSION, buffer, &buffer);
395
396 if (is_app_success(retval)) {
397 printf("%.*s\n", (int)buffer.size(), buffer.data());
398 }
399
400 return retval;
401}
402
403static enum hdr_section parse_section(const char *str)
404{
405 bool is_ro, is_a;
406
407 // matching this: /r?[ow]_?[ab]/i
408
409 if (tolower(*str) == 'r') {
410 str++;
411 }
412
413 if (tolower(*str) == 'o') {
414 is_ro = true;
415 } else if (tolower(*str) == 'w') {
416 is_ro = false;
417 } else {
418 Error("Invalid section \"%s\"", str);
419 return SEC_BOGUS;
420 }
421 str++;
422
423 if (*str == '_') {
424 str++;
425 }
426
427 if (tolower(*str) == 'a') {
428 is_a = true;
429 } else if (tolower(*str) == 'b') {
430 is_a = false;
431 } else {
432 Error("Invalid section \"%s\"", str);
433 return SEC_BOGUS;
434 }
435
436 if (is_ro) {
437 return is_a ? SEC_RO_A : SEC_RO_B;
438 }
439
440 return is_a ? SEC_RW_A : SEC_RW_B;
441}
442
443static void show_header(const uint8_t *ptr)
444{
445 const struct SignedHeader *hdr;
446
447 hdr = reinterpret_cast<const struct SignedHeader*>(ptr);
448 hdr->print();
449}
450
451#define CROS_EC_VERSION_COOKIE1 0xce112233
452#define CROS_EC_VERSION_COOKIE2 0xce445566
453
454// The start of the RW sections looks like this
455struct compiled_version_struct {
456 // The header comes first
457 const struct SignedHeader hdr;
458 // The the vector table. Citadel has 239 entries
459 uint32_t vectors[239];
460 // A magic number to be sure we're looking at the right thing
461 uint32_t cookie1;
462 // Then the short version string
463 char version[32];
464 // And another magic number
465 uint32_t cookie2;
466};
467
468static void show_ro_string(const char *name, const uint8_t *ptr)
469{
470 const struct SignedHeader *hdr;
471
472 hdr = reinterpret_cast<const struct SignedHeader*>(ptr);
473 printf("%s: %d.%d.%d/%08x %s\n", name,
474 hdr->epoch_, hdr->major_, hdr->minor_, be32toh(hdr->img_chk_),
475 hdr->magic == MAGIC_VALID ? "ok" : "--");
476}
477
478static void show_rw_string(const char *name, const uint8_t *ptr)
479{
480 const struct compiled_version_struct *v;
481 v = reinterpret_cast<const struct compiled_version_struct*>(ptr);
482
483 if (v->cookie1 == CROS_EC_VERSION_COOKIE1 &&
484 v->cookie2 == CROS_EC_VERSION_COOKIE2 &&
485 (v->hdr.magic == MAGIC_DEFAULT || v->hdr.magic == MAGIC_VALID)) {
486 printf("%s: %d.%d.%d/%s %s\n", name,
487 v->hdr.epoch_, v->hdr.major_, v->hdr.minor_, v->version,
488 v->hdr.magic == MAGIC_VALID ? "ok" : "--");
489 } else {
490 printf("<invalid>\n");
491 }
492}
493
494uint32_t do_section(AppClient &app __attribute__((unused)))
495{
496 uint16_t param;
497
498 switch (options.section) {
499 case SEC_RO_A:
500 param = NUGGET_PARAM_HEADER_RO_A;
501 break;
502 case SEC_RO_B:
503 param = NUGGET_PARAM_HEADER_RO_B;
504 break;
505 case SEC_RW_A:
506 param = NUGGET_PARAM_HEADER_RW_A;
507 break;
508 case SEC_RW_B:
509 param = NUGGET_PARAM_HEADER_RW_B;
510 break;
511 default:
512 return 1;
513 }
514
515 uint32_t retval;
516 std::vector<uint8_t> buffer;
517 buffer.reserve(sizeof(SignedHeader));
518
519 retval = app.Call(param, buffer, &buffer);
520
521 if (is_app_success(retval)) {
522 show_header(buffer.data());
523 }
524
525 return retval;
526}
527
528uint32_t do_file_version(const std::vector<uint8_t> &image)
529{
530 show_ro_string("RO_A", image.data() + CHIP_RO_A_MEM_OFF);
531 show_ro_string("RO_B", image.data() + CHIP_RO_B_MEM_OFF);
532 show_rw_string("RW_A", image.data() + CHIP_RW_A_MEM_OFF);
533 show_rw_string("RW_B", image.data() + CHIP_RW_B_MEM_OFF);
534 return 0;
535}
536
537uint32_t do_file_section(const std::vector<uint8_t> &image)
538{
539 switch (options.file_section) {
540 case SEC_RO_A:
541 show_header(image.data() + CHIP_RO_A_MEM_OFF);
542 break;
543 case SEC_RO_B:
544 show_header(image.data() + CHIP_RO_B_MEM_OFF);
545 break;
546 case SEC_RW_A:
547 show_header(image.data() + CHIP_RW_A_MEM_OFF);
548 break;
549 case SEC_RW_B:
550 show_header(image.data() + CHIP_RW_B_MEM_OFF);
551 break;
552 default:
553 return 1;
554 }
555
556 return 0;
557}
558
Bill Richardson2917cb22018-01-26 11:59:26 -0800559uint32_t do_stats(AppClient &app)
560{
561 struct nugget_app_low_power_stats stats;
562 std::vector<uint8_t> buffer;
563 uint32_t retval;
564
565 buffer.reserve(sizeof(stats));
566
567 retval = app.Call(NUGGET_PARAM_GET_LOW_POWER_STATS, buffer, &buffer);
568
569 if (is_app_success(retval)) {
570 if (buffer.size() < sizeof(stats)) {
Allen Webb2fdba9b2018-03-30 15:05:33 -0700571 fprintf(stderr, "Only got %zd / %zd bytes back",
Bill Richardson2917cb22018-01-26 11:59:26 -0800572 buffer.size(), sizeof(stats));
573 return -1;
574 }
575
576 memcpy(&stats, buffer.data(), sizeof(stats));
577
Allen Webb6f09c472018-03-30 14:55:29 -0700578 printf("hard_reset_count %" PRIu64 "\n", stats.hard_reset_count);
579 printf("time_since_hard_reset %" PRIu64 "\n",
580 stats.time_since_hard_reset);
581 printf("wake_count %" PRIu64 "\n", stats.wake_count);
582 printf("time_at_last_wake %" PRIu64 "\n", stats.time_at_last_wake);
583 printf("time_spent_awake %" PRIu64 "\n", stats.time_spent_awake);
584 printf("deep_sleep_count %" PRIu64 "\n", stats.deep_sleep_count);
585 printf("time_at_last_deep_sleep %" PRIu64 "\n",
Allen Webbc4cc3c72018-03-30 14:44:11 -0700586 stats.time_at_last_deep_sleep);
Allen Webb6f09c472018-03-30 14:55:29 -0700587 printf("time_spent_in_deep_sleep %" PRIu64 "\n",
Allen Webbc4cc3c72018-03-30 14:44:11 -0700588 stats.time_spent_in_deep_sleep);
Bill Richardson2917cb22018-01-26 11:59:26 -0800589 }
590
591 return retval;
592}
593
Andrew Scull43d72e12017-11-20 14:14:55 +0000594uint32_t do_reboot(AppClient &app)
595{
596 uint32_t retval;
Bill Richardson580e93a2018-04-25 16:43:24 -0700597 std::vector<uint8_t> ignored = {1}; // older images need this
Andrew Scull43d72e12017-11-20 14:14:55 +0000598
Bill Richardson580e93a2018-04-25 16:43:24 -0700599 retval = app.Call(NUGGET_PARAM_REBOOT, ignored, nullptr);
Andrew Scull43d72e12017-11-20 14:14:55 +0000600
601 if (is_app_success(retval)) {
602 printf("Citadel reboot requested\n");
603 }
604
605 return retval;
606}
607
Bill Richardson41896c72018-01-18 21:43:12 -0800608static uint32_t do_change_pw(AppClient &app,
609 const char *old_pw, const char *new_pw)
610{
611 std::vector<uint8_t> data(sizeof(struct nugget_app_change_update_password));
612 struct nugget_app_change_update_password *s =
613 (struct nugget_app_change_update_password*)data.data();
614
615
616 memset(s, 0xff, sizeof(*s));
617 if (old_pw && *old_pw) {
618 int len = strlen(old_pw);
619 memcpy(&s->old_password.password, old_pw, len);
620 s->old_password.digest =
621 compute_digest(&s->old_password.password,
622 sizeof(s->old_password.password));
623 }
624
625 if (new_pw && *new_pw) {
626 int len = strlen(new_pw);
627 memcpy(&s->new_password.password, new_pw, len);
628 s->new_password.digest =
629 compute_digest(&s->new_password.password,
630 sizeof(s->new_password.password));
631 }
632
633 uint32_t rv = app.Call(NUGGET_PARAM_CHANGE_UPDATE_PASSWORD, data, nullptr);
634
635 if (is_app_success(rv))
636 printf("Password changed\n");
637
638 return rv;
639}
640
641static uint32_t do_enable(AppClient &app, const char *pw)
642{
Bill Richardson41896c72018-01-18 21:43:12 -0800643 std::vector<uint8_t> data(sizeof(struct nugget_app_enable_update));
644 struct nugget_app_enable_update *s =
645 (struct nugget_app_enable_update*)data.data();
646
647 memset(&s->password, 0xff, sizeof(s->password));
648 if (pw && *pw) {
649 int len = strlen(pw);
650 memcpy(&s->password.password, pw, len);
651 s->password.digest =
652 compute_digest(&s->password.password,
653 sizeof(s->password.password));
654 }
655 s->which_headers = options.enable_ro ? NUGGET_ENABLE_HEADER_RO : 0;
656 s->which_headers |= options.enable_rw ? NUGGET_ENABLE_HEADER_RW : 0;
657
658 uint32_t rv = app.Call(NUGGET_PARAM_ENABLE_UPDATE, data, nullptr);
659
660 if (is_app_success(rv))
661 printf("Update enabled\n");
662
663 return rv;
664}
665
666static uint32_t do_erase(AppClient &app)
667{
668 std::vector<uint8_t> data(sizeof(uint32_t));
669 memcpy(data.data(), &options.erase_code, data.size());
670
671 uint32_t rv = app.Call(NUGGET_PARAM_NUKE_FROM_ORBIT, data, nullptr);
672
673 if (is_app_success(rv))
674 printf("Citadel erase and reboot requested\n");
675
676 return rv;
677}
678
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700679// This is currently device-specific, but could be abstracted further
Andrew Scull43d72e12017-11-20 14:14:55 +0000680#ifdef ANDROID
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700681static uint32_t do_force_reset(CitadeldProxyClient &client)
682{
683 bool b = false;
684
685 return !client.Citadeld().reset(&b).isOk();
Andrew Scull43d72e12017-11-20 14:14:55 +0000686}
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700687#else
688static uint32_t do_force_reset(NuggetClient &client)
689{
690 struct nos_device *d = client.Device();
691
692 return d->ops.reset(d->ctx);
693}
694#endif
Andrew Scull43d72e12017-11-20 14:14:55 +0000695
Bill Richardson41896c72018-01-18 21:43:12 -0800696int execute_commands(const std::vector<uint8_t> &image,
697 const char *old_passwd, const char *passwd)
Andrew Scull43d72e12017-11-20 14:14:55 +0000698{
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700699#ifdef ANDROID
700 CitadeldProxyClient client;
701#else
702 NuggetClient client(options.device ? options.device : "");
703#endif
704
705 client.Open();
706 if (!client.IsOpen()) {
Andrew Scull43d72e12017-11-20 14:14:55 +0000707 Error("Unable to connect");
708 return 1;
709 }
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700710 AppClient app(client, APP_ID_NUGGET);
Andrew Scull43d72e12017-11-20 14:14:55 +0000711
712 /* Try all requested actions in reasonable order, bail out on error */
713
Bill Richardson41896c72018-01-18 21:43:12 -0800714 if (options.erase_code) { /* zero doesn't count */
715 /* whether we succeed or not, only do this */
716 return do_erase(app);
717 }
718
Andrew Scull43d72e12017-11-20 14:14:55 +0000719 if (options.version &&
720 do_version(app) != APP_SUCCESS) {
721 return 2;
722 }
723
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700724 if (options.long_version &&
725 do_long_version(app) != APP_SUCCESS) {
726 return 2;
727 }
728
729 if (options.section &&
730 do_section(app) != APP_SUCCESS) {
731 return 2;
732 }
733
734 if (options.file_version &&
735 do_file_version(image) != APP_SUCCESS) {
736 return 2;
737 }
738
739 if (options.file_section &&
740 do_file_section(image) != APP_SUCCESS) {
741 return 2;
742 }
743
Andrew Sculld2c3a232018-03-29 20:24:11 +0100744 if (options.id &&
745 do_id(app) != APP_SUCCESS) {
746 return 2;
747 }
748
Bill Richardson2917cb22018-01-26 11:59:26 -0800749 if (options.stats &&
750 do_stats(app) != APP_SUCCESS) {
751 return 2;
752 }
753
Andrew Scull43d72e12017-11-20 14:14:55 +0000754 if (options.rw &&
755 do_update(app, image,
756 CHIP_RW_A_MEM_OFF, CHIP_RW_B_MEM_OFF) != APP_SUCCESS) {
757 return 3;
758 }
759
760 if (options.ro &&
761 do_update(app, image,
762 CHIP_RO_A_MEM_OFF, CHIP_RO_B_MEM_OFF) != APP_SUCCESS) {
763 return 4;
764 }
765
Bill Richardson41896c72018-01-18 21:43:12 -0800766 if (options.change_pw &&
767 do_change_pw(app, old_passwd, passwd) != APP_SUCCESS)
768 return 5;
769
770 if ((options.enable_ro || options.enable_rw) &&
771 do_enable(app, passwd) != APP_SUCCESS)
772 return 6;
773
Andrew Scull43d72e12017-11-20 14:14:55 +0000774 if (options.reboot &&
775 do_reboot(app) != APP_SUCCESS) {
Bill Richardson41896c72018-01-18 21:43:12 -0800776 return 7;
Andrew Scull43d72e12017-11-20 14:14:55 +0000777 }
Bill Richardson41896c72018-01-18 21:43:12 -0800778
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700779 if (options.force_reset &&
780 do_force_reset(client) != APP_SUCCESS) {
781 return 1;
782 }
783
Andrew Scull43d72e12017-11-20 14:14:55 +0000784 return 0;
785}
786
787} // namespace
788
789int main(int argc, char *argv[])
790{
791 int i;
792 int idx = 0;
793 char *this_prog;
Bill Richardson41896c72018-01-18 21:43:12 -0800794 char *old_passwd = 0;
795 char *passwd = 0;
Andrew Scull43d72e12017-11-20 14:14:55 +0000796 std::vector<uint8_t> image;
797 int got_action = 0;
Bill Richardson41896c72018-01-18 21:43:12 -0800798 char *e = 0;
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700799 int need_file = 0;
Andrew Scull43d72e12017-11-20 14:14:55 +0000800
801 this_prog= strrchr(argv[0], '/');
Bill Richardson153fa252018-02-23 14:39:58 -0800802 if (this_prog) {
Andrew Scull43d72e12017-11-20 14:14:55 +0000803 this_prog++;
804 } else {
805 this_prog = argv[0];
806 }
807
Bill Richardson153fa252018-02-23 14:39:58 -0800808#ifndef ANDROID
809 options.device = secure_getenv("CITADEL_DEVICE");
810 if (options.device)
811 fprintf(stderr, "-- $CITADEL_DEVICE=%s --\n", options.device);
812#endif
813
814 opterr = 0; /* quiet, you */
Andrew Scull43d72e12017-11-20 14:14:55 +0000815 while ((i = getopt_long(argc, argv,
816 short_opts, long_opts, &idx)) != -1) {
817 switch (i) {
818 /* program-specific options */
819 case 'v':
820 options.version = 1;
821 got_action = 1;
822 break;
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700823 case 'l':
824 options.long_version = 1;
825 got_action = 1;
826 break;
827 case 'V':
828 options.section = parse_section(optarg);
829 got_action = 1;
830 break;
831 case 'f':
832 options.file_version = 1;
833 need_file = 1;
834 got_action = 1;
835 break;
836 case 'F':
837 options.file_section = parse_section(optarg);
838 need_file = 1;
839 got_action = 1;
840 break;
Andrew Sculld2c3a232018-03-29 20:24:11 +0100841 case OPT_ID:
842 options.id = 1;
843 got_action = 1;
844 break;
Bill Richardson2917cb22018-01-26 11:59:26 -0800845 case OPT_STATS:
846 options.stats = 1;
847 got_action = 1;
848 break;
Andrew Scull43d72e12017-11-20 14:14:55 +0000849 case OPT_RO:
850 options.ro = 1;
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700851 need_file = 1;
Andrew Scull43d72e12017-11-20 14:14:55 +0000852 got_action = 1;
853 break;
854 case OPT_RW:
855 options.rw = 1;
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700856 need_file = 1;
Andrew Scull43d72e12017-11-20 14:14:55 +0000857 got_action = 1;
858 break;
859 case OPT_REBOOT:
860 options.reboot = 1;
861 got_action = 1;
862 break;
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700863 case OPT_FORCE_RESET:
864 options.force_reset = 1;
865 got_action = 1;
866 break;
Bill Richardson41896c72018-01-18 21:43:12 -0800867 case OPT_ENABLE_RO:
868 options.enable_ro = 1;
869 got_action = 1;
870 break;
871 case OPT_ENABLE_RW:
872 options.enable_rw = 1;
873 got_action = 1;
874 break;
875 case OPT_CHANGE_PW:
876 options.change_pw = 1;
877 got_action = 1;
878 break;
879 case OPT_ERASE:
880 options.erase_code = (uint32_t)strtoul(optarg, &e, 0);
881 if (!*optarg || (e && *e)) {
882 Error("Invalid argument: \"%s\"\n", optarg);
883 errorcnt++;
884 }
885 got_action = 1;
886 break;
Andrew Scull43d72e12017-11-20 14:14:55 +0000887
888 /* generic options below */
889 case OPT_DEVICE:
890 options.device = optarg;
891 break;
Andrew Scull43d72e12017-11-20 14:14:55 +0000892 case 'h':
893 usage(this_prog);
894 return 0;
895 case 0:
896 break;
897 case '?':
898 if (optopt)
899 Error("Unrecognized options: -%c", optopt);
900 else
901 Error("Unrecognized options: %s",
902 argv[optind - 1]);
903 usage(this_prog);
904 break;
905 case ':':
906 Error("Missing argument to %s", argv[optind - 1]);
907 break;
908 default:
909 Error("Internal error at %s:%d", __FILE__, __LINE__);
910 exit(1);
911 }
912 }
913
914 if (errorcnt) {
915 goto out;
916 }
917
918 if (!got_action) {
919 usage(this_prog);
920 goto out;
921 }
922
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700923 if (need_file) {
Andrew Scull43d72e12017-11-20 14:14:55 +0000924 if (optind < argc) {
925 /* Sets errorcnt on failure */
Bill Richardson76b40fe2018-01-22 10:28:24 -0800926 image = read_image_from_file(argv[optind++]);
Bill Richardson41896c72018-01-18 21:43:12 -0800927 if (errorcnt)
928 goto out;
Andrew Scull43d72e12017-11-20 14:14:55 +0000929 } else {
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700930 Error("Missing required image file");
Bill Richardson41896c72018-01-18 21:43:12 -0800931 goto out;
Andrew Scull43d72e12017-11-20 14:14:55 +0000932 }
933 }
934
Bill Richardson41896c72018-01-18 21:43:12 -0800935 if (options.change_pw) {
936 /* one arg provided, maybe the old password isn't set */
937 if (optind < argc) {
938 passwd = argv[optind++];
939 } else {
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700940 Error("Need a new password at least. Use '' to clear it.");
Bill Richardson41896c72018-01-18 21:43:12 -0800941 goto out;
942 }
943 /* two args provided, use both old & new passwords */
944 if (optind < argc) {
945 old_passwd = passwd;
946 passwd = argv[optind++];
947 }
948 }
Andrew Scull43d72e12017-11-20 14:14:55 +0000949
Bill Richardson41896c72018-01-18 21:43:12 -0800950 if ((options.enable_ro || options.enable_rw) && !passwd) {
951 if (optind < argc)
952 passwd = argv[optind++];
953 else {
954 Error("Need a password to enable images. Use '' if none.");
955 goto out;
956 }
957 }
958
959 /* Okay, let's do it! */
960 (void) execute_commands(image, old_passwd, passwd);
Andrew Scull43d72e12017-11-20 14:14:55 +0000961 /* This is the last action, so fall through either way */
962
963out:
964 return !!errorcnt;
965}