blob: a4250efed8f865226979c6946f14f764be5c38ad [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;
Andrew Scull43d72e12017-11-20 14:14:55 +000078 /* generic connection options */
79 const char *device;
Andrew Scull43d72e12017-11-20 14:14:55 +000080} options;
81
82enum no_short_opts_for_these {
83 OPT_DEVICE = 1000,
Andrew Sculld2c3a232018-03-29 20:24:11 +010084 OPT_ID,
Bill Richardson30558862018-04-27 16:11:12 -070085 OPT_REPO_SNAPSHOT,
Bill Richardson2917cb22018-01-26 11:59:26 -080086 OPT_STATS,
Andrew Scull43d72e12017-11-20 14:14:55 +000087 OPT_RO,
88 OPT_RW,
89 OPT_REBOOT,
Bill Richardsonf0a6b802018-03-21 15:32:13 -070090 OPT_FORCE_RESET,
Bill Richardson41896c72018-01-18 21:43:12 -080091 OPT_ENABLE_RO,
92 OPT_ENABLE_RW,
93 OPT_CHANGE_PW,
94 OPT_ERASE,
Andrew Scull43d72e12017-11-20 14:14:55 +000095};
96
Bill Richardsonb49b34d2018-04-27 10:29:38 -070097const char *short_opts = ":hvlV:fF:";
Andrew Scull43d72e12017-11-20 14:14:55 +000098const struct option long_opts[] = {
99 /* name hasarg *flag val */
Bill Richardson30558862018-04-27 16:11:12 -0700100 {"version", 0, NULL, 'v'},
101 {"long_version", 0, NULL, 'l'},
102 {"id", 0, NULL, OPT_ID},
103 {"repo_snapshot", 0, NULL, OPT_REPO_SNAPSHOT},
104 {"stats", 0, NULL, OPT_STATS},
105 {"ro", 0, NULL, OPT_RO},
106 {"rw", 0, NULL, OPT_RW},
107 {"reboot", 0, NULL, OPT_REBOOT},
108 {"force_reset", 0, NULL, OPT_FORCE_RESET},
109 {"enable_ro", 0, NULL, OPT_ENABLE_RO},
110 {"enable_rw", 0, NULL, OPT_ENABLE_RW},
111 {"change_pw", 0, NULL, OPT_CHANGE_PW},
112 {"erase", 1, NULL, OPT_ERASE},
Bill Richardson153fa252018-02-23 14:39:58 -0800113#ifndef ANDROID
Bill Richardson30558862018-04-27 16:11:12 -0700114 {"device", 1, NULL, OPT_DEVICE},
Bill Richardson153fa252018-02-23 14:39:58 -0800115#endif
Bill Richardson30558862018-04-27 16:11:12 -0700116 {"help", 0, NULL, 'h'},
Andrew Scull43d72e12017-11-20 14:14:55 +0000117 {NULL, 0, NULL, 0},
118};
119
120void usage(const char *progname)
121{
122 fprintf(stderr, "\n");
123 fprintf(stderr,
124 "Usage: %s [actions] [image.bin]\n"
125 "\n"
126 "Citadel firmware boots in two stages. The first stage\n"
127 "bootloader (aka \"RO\") is provided by the SOC hardware team\n"
128 "and seldom changes. The application image (\"RW\") is invoked\n"
129 "by the RO image. There are two copies (A/B) of each stage,\n"
130 "so that the active copy can be protected while the unused\n"
131 "copy may be updated. At boot, the newer (valid) copy of each\n"
132 "stage is selected.\n"
133 "\n"
134 "The Citadel image file is the same size of the internal\n"
135 "flash, and contains all four firmware components (RO_A,\n"
136 "RW_A, RO_B, RW_B) located at the correct offsets. Only the\n"
137 "inactive copy (A/B) of each stage (RO/RW) can be modified.\n"
138 "The tool will update the correct copies automatically.\n"
139 "\n"
Bill Richardson153fa252018-02-23 14:39:58 -0800140 "You must specify the actions to perform. With no actions,\n"
Andrew Scull43d72e12017-11-20 14:14:55 +0000141 "this help message is displayed.\n"
142 "\n"
143 "Actions:\n"
144 "\n"
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700145 " -v, --version Display the running version\n"
146 " -l, --long_version Display the full version info\n"
147 " --id Display the Citadel device ID\n"
148 " --stats Display Low Power stats\n"
149 " -V SECTION Show Citadel headers for RO_A | RO_B | RW_A | RW_B\n"
150 " -f Show image file version info\n"
151 " -F SECTION Show file headers for RO_A | RO_B | RW_A | RW_B\n"
Bill Richardson30558862018-04-27 16:11:12 -0700152 " --repo_snapshot Show the repo sha1sums for the running image\n"
Bill Richardson41896c72018-01-18 21:43:12 -0800153 "\n"
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700154 " --rw Update RW firmware from the image file\n"
155 " --ro Update RO firmware from the image file\n"
156 " --enable_ro Mark new RO image as good (requires password)\n"
157 " --enable_rw Mark new RW image as good (requires password)\n"
158 " --reboot Tell Citadel to reboot\n"
159 " --force_reset Pulse Citadel's reset line\n"
Bill Richardson41896c72018-01-18 21:43:12 -0800160 "\n"
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700161 " --change_pw Change the update password\n"
Bill Richardson41896c72018-01-18 21:43:12 -0800162 "\n\n"
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700163 " --erase=CODE Erase all user secrets and reboot.\n"
164 " This skips all other actions.\n"
Bill Richardson153fa252018-02-23 14:39:58 -0800165#ifndef ANDROID
166 "\n"
167 "Options:\n"
168 "\n"
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700169 " --device=SN Connect to the FDTI device with the given\n"
170 " serial number (try \"lsusb -v\"). A default\n"
171 " can be specified with the CITADEL_DEVICE\n"
172 " environment variable.\n"
Bill Richardson153fa252018-02-23 14:39:58 -0800173#endif
Andrew Scull43d72e12017-11-20 14:14:55 +0000174 "\n",
175 progname);
176}
177
178/****************************************************************************/
179/* Handy stuff */
180
181#ifndef MIN
182#define MIN(a, b) ((a) < (b) ? (a) : (b))
183#endif
184
185int errorcnt;
186void Error(const char *format, ...)
187{
188 va_list ap;
189
Bill Richardson41896c72018-01-18 21:43:12 -0800190 va_start(ap, format);
191 fprintf(stderr, "ERROR: ");
192 vfprintf(stderr, format, ap);
193 fprintf(stderr, "\n");
194 va_end(ap);
Andrew Scull43d72e12017-11-20 14:14:55 +0000195
196 errorcnt++;
197}
198
199/* Return true on APP_SUCESS, display error message if it's not */
200int is_app_success(uint32_t retval)
201{
202 if (retval == APP_SUCCESS)
203 return 1;
204
205 errorcnt++;
206
207 fprintf(stderr, "Error code 0x%x: ", retval);
208 switch (retval) {
209 case APP_ERROR_BOGUS_ARGS:
210 fprintf(stderr, "bogus args");
211 break;
212 case APP_ERROR_INTERNAL:
213 fprintf(stderr, "app is being stupid");
214 break;
215 case APP_ERROR_TOO_MUCH:
216 fprintf(stderr, "caller sent too much data");
217 break;
218 default:
219 if (retval >= APP_SPECIFIC_ERROR &&
220 retval < APP_LINE_NUMBER_BASE) {
221 fprintf(stderr, "app-specific error #%d",
222 retval - APP_SPECIFIC_ERROR);
223 } else if (retval >= APP_LINE_NUMBER_BASE) {
224 fprintf(stderr, "error at line %d",
225 retval - APP_LINE_NUMBER_BASE);
226 } else {
227 fprintf(stderr, "unknown)");
228 }
229 }
230 fprintf(stderr, "\n");
231
232 return 0;
233}
234
235/****************************************************************************/
236
237std::vector<uint8_t> read_image_from_file(const char *name)
238{
239 FILE *fp;
240 struct stat st;
241
242 fp = fopen(name, "rb");
243 if (!fp) {
244 perror("fopen");
245 Error("Can't open file %s", name);
246 return {};
247 }
248
249 if (fstat(fileno(fp), &st)) {
250 perror("fstat");
251 Error("Can't fstat file %s", name);
252 fclose(fp);
253 return {};
254 }
255
256 if (st.st_size != CHIP_FLASH_SIZE) {
257 Error("The firmware image must be exactly %d bytes",
258 CHIP_FLASH_SIZE);
259 fclose(fp);
260 return {};
261 }
262
263 std::vector<uint8_t> buf(st.st_size);
264 if (1 != fread(buf.data(), st.st_size, 1, fp)) {
265 perror("fread");
266 Error("Can't read %zd bytes", st.st_size);
267 fclose(fp);
268 return {};
269 }
270
271 fclose(fp);
272 buf.resize(st.st_size);
273
274 return buf;
275}
276
Bill Richardson41896c72018-01-18 21:43:12 -0800277uint32_t compute_digest(void *ptr, size_t len)
Andrew Scull43d72e12017-11-20 14:14:55 +0000278{
Andrew Scull43d72e12017-11-20 14:14:55 +0000279 SHA_CTX ctx;
280 uint8_t digest[SHA_DIGEST_LENGTH];
281 uint32_t retval;
282
283 SHA1_Init(&ctx);
Bill Richardson41896c72018-01-18 21:43:12 -0800284 SHA1_Update(&ctx, ptr, len);
Andrew Scull43d72e12017-11-20 14:14:55 +0000285 SHA1_Final(digest, &ctx);
286
287 memcpy(&retval, digest, sizeof(retval));
288 return retval;
289}
290
Bill Richardson41896c72018-01-18 21:43:12 -0800291uint32_t compute_fb_digest(struct nugget_app_flash_block *blk)
292{
293 uint8_t *start_here = ((uint8_t *)blk) +
294 offsetof(struct nugget_app_flash_block, offset);
295 size_t size_to_hash = sizeof(*blk) -
296 offsetof(struct nugget_app_flash_block, offset);
297
298 return compute_digest(start_here, size_to_hash);
299}
300
Andrew Scull43d72e12017-11-20 14:14:55 +0000301uint32_t try_update(AppClient &app, const std::vector<uint8_t> &image,
302 uint32_t offset, uint32_t imagesize)
303{
304 uint32_t stop = offset + imagesize;
305 uint32_t rv;
306
307 printf("Updating image from 0x%05x to 0x%05x, size 0x%05x\n",
308 CHIP_FLASH_BASE + offset, CHIP_FLASH_BASE + stop, imagesize);
309
310 for (; offset < stop; offset += CHIP_FLASH_BANK_SIZE) {
311 int retries = 3;
312 std::vector<uint8_t> data(sizeof(struct nugget_app_flash_block));
313 struct nugget_app_flash_block *fb =
314 (struct nugget_app_flash_block*)data.data();
315
316 fb->offset = offset;
317 memcpy(fb->payload, image.data() + offset, CHIP_FLASH_BANK_SIZE);
Bill Richardson41896c72018-01-18 21:43:12 -0800318 fb->block_digest = compute_fb_digest(fb);
Andrew Scull43d72e12017-11-20 14:14:55 +0000319
320 printf("writing 0x%05x / 0x%05x",
321 CHIP_FLASH_BASE + offset, CHIP_FLASH_BASE + stop);
322 do {
323 rv = app.Call(NUGGET_PARAM_FLASH_BLOCK, data, nullptr);
324 if (rv == NUGGET_ERROR_RETRY)
325 printf(" retrying");
326 } while (rv == NUGGET_ERROR_RETRY && retries--);
Bill Richardson41896c72018-01-18 21:43:12 -0800327 if (rv) {
328 if (rv == NUGGET_ERROR_LOCKED)
329 printf(" locked\n");
330 else
331 printf(" fail %d\n", rv);
Andrew Scull43d72e12017-11-20 14:14:55 +0000332 break;
Bill Richardson41896c72018-01-18 21:43:12 -0800333 }
334 printf(" ok\n");
Andrew Scull43d72e12017-11-20 14:14:55 +0000335 }
336
337 return rv;
338}
339
Andrew Scull43d72e12017-11-20 14:14:55 +0000340uint32_t do_update(AppClient &app, const std::vector<uint8_t> &image,
341 uint32_t offset_A, uint32_t offset_B)
342{
343 struct SignedHeader *hdr;
344 uint32_t rv_A, rv_B;
345
346 /* Try image A first */
347 hdr = (struct SignedHeader *)(image.data() + offset_A);
348 rv_A = try_update(app, image, offset_A, hdr->image_size);
349
350 /* If that worked, we're done */
351 if (rv_A == APP_SUCCESS) {
352 return rv_A;
353 }
354
355 /* Else try image B */
356 hdr = (struct SignedHeader *)(image.data() + offset_B);
357 rv_B = try_update(app, image, offset_B, hdr->image_size);
358
359 return rv_B;
360}
361
362uint32_t do_version(AppClient &app)
363{
364 uint32_t retval;
365 std::vector<uint8_t> buffer;
366 buffer.reserve(512);
367
368 retval = app.Call(NUGGET_PARAM_VERSION, buffer, &buffer);
369
370 if (is_app_success(retval)) {
371 printf("%.*s\n", (int) buffer.size(), buffer.data());
372 }
373
374 return retval;
375}
376
Andrew Sculld2c3a232018-03-29 20:24:11 +0100377uint32_t do_id(AppClient &app)
378{
379 uint32_t retval;
380 std::vector<uint8_t> buffer;
381 buffer.reserve(32);
382
383 retval = app.Call(NUGGET_PARAM_DEVICE_ID, buffer, &buffer);
384
385 if (is_app_success(retval)) {
386 printf("%.*s\n", (int) buffer.size(), buffer.data());
387 }
388
389 return retval;
390}
391
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700392uint32_t do_long_version(AppClient &app)
393{
394 uint32_t retval;
395 std::vector<uint8_t> buffer;
396 buffer.reserve(1024);
397
398 retval = app.Call(NUGGET_PARAM_LONG_VERSION, buffer, &buffer);
399
400 if (is_app_success(retval)) {
401 printf("%.*s\n", (int)buffer.size(), buffer.data());
402 }
403
404 return retval;
405}
406
407static enum hdr_section parse_section(const char *str)
408{
409 bool is_ro, is_a;
410
411 // matching this: /r?[ow]_?[ab]/i
412
413 if (tolower(*str) == 'r') {
414 str++;
415 }
416
417 if (tolower(*str) == 'o') {
418 is_ro = true;
419 } else if (tolower(*str) == 'w') {
420 is_ro = false;
421 } else {
422 Error("Invalid section \"%s\"", str);
423 return SEC_BOGUS;
424 }
425 str++;
426
427 if (*str == '_') {
428 str++;
429 }
430
431 if (tolower(*str) == 'a') {
432 is_a = true;
433 } else if (tolower(*str) == 'b') {
434 is_a = false;
435 } else {
436 Error("Invalid section \"%s\"", str);
437 return SEC_BOGUS;
438 }
439
440 if (is_ro) {
441 return is_a ? SEC_RO_A : SEC_RO_B;
442 }
443
444 return is_a ? SEC_RW_A : SEC_RW_B;
445}
446
447static void show_header(const uint8_t *ptr)
448{
449 const struct SignedHeader *hdr;
450
451 hdr = reinterpret_cast<const struct SignedHeader*>(ptr);
452 hdr->print();
453}
454
455#define CROS_EC_VERSION_COOKIE1 0xce112233
456#define CROS_EC_VERSION_COOKIE2 0xce445566
457
458// The start of the RW sections looks like this
459struct compiled_version_struct {
460 // The header comes first
461 const struct SignedHeader hdr;
462 // The the vector table. Citadel has 239 entries
463 uint32_t vectors[239];
464 // A magic number to be sure we're looking at the right thing
465 uint32_t cookie1;
466 // Then the short version string
467 char version[32];
468 // And another magic number
469 uint32_t cookie2;
470};
471
472static void show_ro_string(const char *name, const uint8_t *ptr)
473{
474 const struct SignedHeader *hdr;
475
476 hdr = reinterpret_cast<const struct SignedHeader*>(ptr);
477 printf("%s: %d.%d.%d/%08x %s\n", name,
478 hdr->epoch_, hdr->major_, hdr->minor_, be32toh(hdr->img_chk_),
479 hdr->magic == MAGIC_VALID ? "ok" : "--");
480}
481
482static void show_rw_string(const char *name, const uint8_t *ptr)
483{
484 const struct compiled_version_struct *v;
485 v = reinterpret_cast<const struct compiled_version_struct*>(ptr);
486
487 if (v->cookie1 == CROS_EC_VERSION_COOKIE1 &&
488 v->cookie2 == CROS_EC_VERSION_COOKIE2 &&
489 (v->hdr.magic == MAGIC_DEFAULT || v->hdr.magic == MAGIC_VALID)) {
490 printf("%s: %d.%d.%d/%s %s\n", name,
491 v->hdr.epoch_, v->hdr.major_, v->hdr.minor_, v->version,
492 v->hdr.magic == MAGIC_VALID ? "ok" : "--");
493 } else {
494 printf("<invalid>\n");
495 }
496}
497
498uint32_t do_section(AppClient &app __attribute__((unused)))
499{
500 uint16_t param;
501
502 switch (options.section) {
503 case SEC_RO_A:
504 param = NUGGET_PARAM_HEADER_RO_A;
505 break;
506 case SEC_RO_B:
507 param = NUGGET_PARAM_HEADER_RO_B;
508 break;
509 case SEC_RW_A:
510 param = NUGGET_PARAM_HEADER_RW_A;
511 break;
512 case SEC_RW_B:
513 param = NUGGET_PARAM_HEADER_RW_B;
514 break;
515 default:
516 return 1;
517 }
518
519 uint32_t retval;
520 std::vector<uint8_t> buffer;
521 buffer.reserve(sizeof(SignedHeader));
522
523 retval = app.Call(param, buffer, &buffer);
524
525 if (is_app_success(retval)) {
526 show_header(buffer.data());
527 }
528
529 return retval;
530}
531
532uint32_t do_file_version(const std::vector<uint8_t> &image)
533{
534 show_ro_string("RO_A", image.data() + CHIP_RO_A_MEM_OFF);
535 show_ro_string("RO_B", image.data() + CHIP_RO_B_MEM_OFF);
536 show_rw_string("RW_A", image.data() + CHIP_RW_A_MEM_OFF);
537 show_rw_string("RW_B", image.data() + CHIP_RW_B_MEM_OFF);
538 return 0;
539}
540
541uint32_t do_file_section(const std::vector<uint8_t> &image)
542{
543 switch (options.file_section) {
544 case SEC_RO_A:
545 show_header(image.data() + CHIP_RO_A_MEM_OFF);
546 break;
547 case SEC_RO_B:
548 show_header(image.data() + CHIP_RO_B_MEM_OFF);
549 break;
550 case SEC_RW_A:
551 show_header(image.data() + CHIP_RW_A_MEM_OFF);
552 break;
553 case SEC_RW_B:
554 show_header(image.data() + CHIP_RW_B_MEM_OFF);
555 break;
556 default:
557 return 1;
558 }
559
560 return 0;
561}
562
Bill Richardson30558862018-04-27 16:11:12 -0700563uint32_t do_repo_snapshot(AppClient &app)
564{
565 uint32_t retval;
566 std::vector<uint8_t> buffer;
567 buffer.reserve(1200);
568
569 retval = app.Call(NUGGET_PARAM_REPO_SNAPSHOT, buffer, &buffer);
570
571 if (is_app_success(retval)) {
572 printf("%.*s\n", (int)buffer.size(), buffer.data());
573 }
574
575 return retval;
576}
577
Bill Richardson2917cb22018-01-26 11:59:26 -0800578uint32_t do_stats(AppClient &app)
579{
580 struct nugget_app_low_power_stats stats;
581 std::vector<uint8_t> buffer;
582 uint32_t retval;
583
584 buffer.reserve(sizeof(stats));
585
586 retval = app.Call(NUGGET_PARAM_GET_LOW_POWER_STATS, buffer, &buffer);
587
588 if (is_app_success(retval)) {
589 if (buffer.size() < sizeof(stats)) {
Allen Webb2fdba9b2018-03-30 15:05:33 -0700590 fprintf(stderr, "Only got %zd / %zd bytes back",
Bill Richardson2917cb22018-01-26 11:59:26 -0800591 buffer.size(), sizeof(stats));
592 return -1;
593 }
594
595 memcpy(&stats, buffer.data(), sizeof(stats));
596
Allen Webb6f09c472018-03-30 14:55:29 -0700597 printf("hard_reset_count %" PRIu64 "\n", stats.hard_reset_count);
598 printf("time_since_hard_reset %" PRIu64 "\n",
599 stats.time_since_hard_reset);
600 printf("wake_count %" PRIu64 "\n", stats.wake_count);
601 printf("time_at_last_wake %" PRIu64 "\n", stats.time_at_last_wake);
602 printf("time_spent_awake %" PRIu64 "\n", stats.time_spent_awake);
603 printf("deep_sleep_count %" PRIu64 "\n", stats.deep_sleep_count);
604 printf("time_at_last_deep_sleep %" PRIu64 "\n",
Allen Webbc4cc3c72018-03-30 14:44:11 -0700605 stats.time_at_last_deep_sleep);
Allen Webb6f09c472018-03-30 14:55:29 -0700606 printf("time_spent_in_deep_sleep %" PRIu64 "\n",
Allen Webbc4cc3c72018-03-30 14:44:11 -0700607 stats.time_spent_in_deep_sleep);
Bill Richardson2917cb22018-01-26 11:59:26 -0800608 }
609
610 return retval;
611}
612
Andrew Scull43d72e12017-11-20 14:14:55 +0000613uint32_t do_reboot(AppClient &app)
614{
615 uint32_t retval;
Bill Richardson580e93a2018-04-25 16:43:24 -0700616 std::vector<uint8_t> ignored = {1}; // older images need this
Andrew Scull43d72e12017-11-20 14:14:55 +0000617
Bill Richardson580e93a2018-04-25 16:43:24 -0700618 retval = app.Call(NUGGET_PARAM_REBOOT, ignored, nullptr);
Andrew Scull43d72e12017-11-20 14:14:55 +0000619
620 if (is_app_success(retval)) {
621 printf("Citadel reboot requested\n");
622 }
623
624 return retval;
625}
626
Bill Richardson41896c72018-01-18 21:43:12 -0800627static uint32_t do_change_pw(AppClient &app,
628 const char *old_pw, const char *new_pw)
629{
630 std::vector<uint8_t> data(sizeof(struct nugget_app_change_update_password));
631 struct nugget_app_change_update_password *s =
632 (struct nugget_app_change_update_password*)data.data();
633
634
635 memset(s, 0xff, sizeof(*s));
636 if (old_pw && *old_pw) {
637 int len = strlen(old_pw);
638 memcpy(&s->old_password.password, old_pw, len);
639 s->old_password.digest =
640 compute_digest(&s->old_password.password,
641 sizeof(s->old_password.password));
642 }
643
644 if (new_pw && *new_pw) {
645 int len = strlen(new_pw);
646 memcpy(&s->new_password.password, new_pw, len);
647 s->new_password.digest =
648 compute_digest(&s->new_password.password,
649 sizeof(s->new_password.password));
650 }
651
652 uint32_t rv = app.Call(NUGGET_PARAM_CHANGE_UPDATE_PASSWORD, data, nullptr);
653
654 if (is_app_success(rv))
655 printf("Password changed\n");
656
657 return rv;
658}
659
660static uint32_t do_enable(AppClient &app, const char *pw)
661{
Bill Richardson41896c72018-01-18 21:43:12 -0800662 std::vector<uint8_t> data(sizeof(struct nugget_app_enable_update));
663 struct nugget_app_enable_update *s =
664 (struct nugget_app_enable_update*)data.data();
665
666 memset(&s->password, 0xff, sizeof(s->password));
667 if (pw && *pw) {
668 int len = strlen(pw);
669 memcpy(&s->password.password, pw, len);
670 s->password.digest =
671 compute_digest(&s->password.password,
672 sizeof(s->password.password));
673 }
674 s->which_headers = options.enable_ro ? NUGGET_ENABLE_HEADER_RO : 0;
675 s->which_headers |= options.enable_rw ? NUGGET_ENABLE_HEADER_RW : 0;
676
677 uint32_t rv = app.Call(NUGGET_PARAM_ENABLE_UPDATE, data, nullptr);
678
679 if (is_app_success(rv))
680 printf("Update enabled\n");
681
682 return rv;
683}
684
685static uint32_t do_erase(AppClient &app)
686{
687 std::vector<uint8_t> data(sizeof(uint32_t));
688 memcpy(data.data(), &options.erase_code, data.size());
689
690 uint32_t rv = app.Call(NUGGET_PARAM_NUKE_FROM_ORBIT, data, nullptr);
691
692 if (is_app_success(rv))
693 printf("Citadel erase and reboot requested\n");
694
695 return rv;
696}
697
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700698// This is currently device-specific, but could be abstracted further
Andrew Scull43d72e12017-11-20 14:14:55 +0000699#ifdef ANDROID
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700700static uint32_t do_force_reset(CitadeldProxyClient &client)
701{
702 bool b = false;
703
704 return !client.Citadeld().reset(&b).isOk();
Andrew Scull43d72e12017-11-20 14:14:55 +0000705}
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700706#else
707static uint32_t do_force_reset(NuggetClient &client)
708{
709 struct nos_device *d = client.Device();
710
711 return d->ops.reset(d->ctx);
712}
713#endif
Andrew Scull43d72e12017-11-20 14:14:55 +0000714
Bill Richardson41896c72018-01-18 21:43:12 -0800715int execute_commands(const std::vector<uint8_t> &image,
716 const char *old_passwd, const char *passwd)
Andrew Scull43d72e12017-11-20 14:14:55 +0000717{
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700718#ifdef ANDROID
719 CitadeldProxyClient client;
720#else
721 NuggetClient client(options.device ? options.device : "");
722#endif
723
724 client.Open();
725 if (!client.IsOpen()) {
Andrew Scull43d72e12017-11-20 14:14:55 +0000726 Error("Unable to connect");
727 return 1;
728 }
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700729 AppClient app(client, APP_ID_NUGGET);
Andrew Scull43d72e12017-11-20 14:14:55 +0000730
731 /* Try all requested actions in reasonable order, bail out on error */
732
Bill Richardson41896c72018-01-18 21:43:12 -0800733 if (options.erase_code) { /* zero doesn't count */
734 /* whether we succeed or not, only do this */
735 return do_erase(app);
736 }
737
Andrew Scull43d72e12017-11-20 14:14:55 +0000738 if (options.version &&
739 do_version(app) != APP_SUCCESS) {
740 return 2;
741 }
742
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700743 if (options.long_version &&
744 do_long_version(app) != APP_SUCCESS) {
745 return 2;
746 }
747
748 if (options.section &&
749 do_section(app) != APP_SUCCESS) {
750 return 2;
751 }
752
753 if (options.file_version &&
754 do_file_version(image) != APP_SUCCESS) {
755 return 2;
756 }
757
758 if (options.file_section &&
759 do_file_section(image) != APP_SUCCESS) {
760 return 2;
761 }
762
Andrew Sculld2c3a232018-03-29 20:24:11 +0100763 if (options.id &&
764 do_id(app) != APP_SUCCESS) {
765 return 2;
766 }
767
Bill Richardson2917cb22018-01-26 11:59:26 -0800768 if (options.stats &&
769 do_stats(app) != APP_SUCCESS) {
770 return 2;
771 }
772
Bill Richardson30558862018-04-27 16:11:12 -0700773 if (options.repo_snapshot &&
774 do_repo_snapshot(app) != APP_SUCCESS) {
775 return 2;
776 }
Andrew Scull43d72e12017-11-20 14:14:55 +0000777 if (options.rw &&
778 do_update(app, image,
779 CHIP_RW_A_MEM_OFF, CHIP_RW_B_MEM_OFF) != APP_SUCCESS) {
780 return 3;
781 }
782
783 if (options.ro &&
784 do_update(app, image,
785 CHIP_RO_A_MEM_OFF, CHIP_RO_B_MEM_OFF) != APP_SUCCESS) {
786 return 4;
787 }
788
Bill Richardson41896c72018-01-18 21:43:12 -0800789 if (options.change_pw &&
790 do_change_pw(app, old_passwd, passwd) != APP_SUCCESS)
791 return 5;
792
793 if ((options.enable_ro || options.enable_rw) &&
794 do_enable(app, passwd) != APP_SUCCESS)
795 return 6;
796
Andrew Scull43d72e12017-11-20 14:14:55 +0000797 if (options.reboot &&
798 do_reboot(app) != APP_SUCCESS) {
Bill Richardson41896c72018-01-18 21:43:12 -0800799 return 7;
Andrew Scull43d72e12017-11-20 14:14:55 +0000800 }
Bill Richardson41896c72018-01-18 21:43:12 -0800801
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700802 if (options.force_reset &&
803 do_force_reset(client) != APP_SUCCESS) {
804 return 1;
805 }
806
Andrew Scull43d72e12017-11-20 14:14:55 +0000807 return 0;
808}
809
810} // namespace
811
812int main(int argc, char *argv[])
813{
814 int i;
815 int idx = 0;
816 char *this_prog;
Bill Richardson41896c72018-01-18 21:43:12 -0800817 char *old_passwd = 0;
818 char *passwd = 0;
Andrew Scull43d72e12017-11-20 14:14:55 +0000819 std::vector<uint8_t> image;
820 int got_action = 0;
Bill Richardson41896c72018-01-18 21:43:12 -0800821 char *e = 0;
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700822 int need_file = 0;
Andrew Scull43d72e12017-11-20 14:14:55 +0000823
824 this_prog= strrchr(argv[0], '/');
Bill Richardson153fa252018-02-23 14:39:58 -0800825 if (this_prog) {
Andrew Scull43d72e12017-11-20 14:14:55 +0000826 this_prog++;
827 } else {
828 this_prog = argv[0];
829 }
830
Bill Richardson153fa252018-02-23 14:39:58 -0800831#ifndef ANDROID
832 options.device = secure_getenv("CITADEL_DEVICE");
833 if (options.device)
834 fprintf(stderr, "-- $CITADEL_DEVICE=%s --\n", options.device);
835#endif
836
837 opterr = 0; /* quiet, you */
Andrew Scull43d72e12017-11-20 14:14:55 +0000838 while ((i = getopt_long(argc, argv,
839 short_opts, long_opts, &idx)) != -1) {
840 switch (i) {
841 /* program-specific options */
842 case 'v':
843 options.version = 1;
844 got_action = 1;
845 break;
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700846 case 'l':
847 options.long_version = 1;
848 got_action = 1;
849 break;
850 case 'V':
851 options.section = parse_section(optarg);
852 got_action = 1;
853 break;
854 case 'f':
855 options.file_version = 1;
856 need_file = 1;
857 got_action = 1;
858 break;
859 case 'F':
860 options.file_section = parse_section(optarg);
861 need_file = 1;
862 got_action = 1;
863 break;
Andrew Sculld2c3a232018-03-29 20:24:11 +0100864 case OPT_ID:
865 options.id = 1;
866 got_action = 1;
867 break;
Bill Richardson30558862018-04-27 16:11:12 -0700868 case OPT_REPO_SNAPSHOT:
869 options.repo_snapshot = 1;
870 got_action = 1;
871 break;
Bill Richardson2917cb22018-01-26 11:59:26 -0800872 case OPT_STATS:
873 options.stats = 1;
874 got_action = 1;
875 break;
Andrew Scull43d72e12017-11-20 14:14:55 +0000876 case OPT_RO:
877 options.ro = 1;
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700878 need_file = 1;
Andrew Scull43d72e12017-11-20 14:14:55 +0000879 got_action = 1;
880 break;
881 case OPT_RW:
882 options.rw = 1;
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700883 need_file = 1;
Andrew Scull43d72e12017-11-20 14:14:55 +0000884 got_action = 1;
885 break;
886 case OPT_REBOOT:
887 options.reboot = 1;
888 got_action = 1;
889 break;
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700890 case OPT_FORCE_RESET:
891 options.force_reset = 1;
892 got_action = 1;
893 break;
Bill Richardson41896c72018-01-18 21:43:12 -0800894 case OPT_ENABLE_RO:
895 options.enable_ro = 1;
896 got_action = 1;
897 break;
898 case OPT_ENABLE_RW:
899 options.enable_rw = 1;
900 got_action = 1;
901 break;
902 case OPT_CHANGE_PW:
903 options.change_pw = 1;
904 got_action = 1;
905 break;
906 case OPT_ERASE:
907 options.erase_code = (uint32_t)strtoul(optarg, &e, 0);
908 if (!*optarg || (e && *e)) {
909 Error("Invalid argument: \"%s\"\n", optarg);
910 errorcnt++;
911 }
912 got_action = 1;
913 break;
Andrew Scull43d72e12017-11-20 14:14:55 +0000914
915 /* generic options below */
916 case OPT_DEVICE:
917 options.device = optarg;
918 break;
Andrew Scull43d72e12017-11-20 14:14:55 +0000919 case 'h':
920 usage(this_prog);
921 return 0;
922 case 0:
923 break;
924 case '?':
925 if (optopt)
926 Error("Unrecognized options: -%c", optopt);
927 else
928 Error("Unrecognized options: %s",
929 argv[optind - 1]);
930 usage(this_prog);
931 break;
932 case ':':
933 Error("Missing argument to %s", argv[optind - 1]);
934 break;
935 default:
936 Error("Internal error at %s:%d", __FILE__, __LINE__);
937 exit(1);
938 }
939 }
940
941 if (errorcnt) {
942 goto out;
943 }
944
945 if (!got_action) {
946 usage(this_prog);
947 goto out;
948 }
949
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700950 if (need_file) {
Andrew Scull43d72e12017-11-20 14:14:55 +0000951 if (optind < argc) {
952 /* Sets errorcnt on failure */
Bill Richardson76b40fe2018-01-22 10:28:24 -0800953 image = read_image_from_file(argv[optind++]);
Bill Richardson41896c72018-01-18 21:43:12 -0800954 if (errorcnt)
955 goto out;
Andrew Scull43d72e12017-11-20 14:14:55 +0000956 } else {
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700957 Error("Missing required image file");
Bill Richardson41896c72018-01-18 21:43:12 -0800958 goto out;
Andrew Scull43d72e12017-11-20 14:14:55 +0000959 }
960 }
961
Bill Richardson41896c72018-01-18 21:43:12 -0800962 if (options.change_pw) {
963 /* one arg provided, maybe the old password isn't set */
964 if (optind < argc) {
965 passwd = argv[optind++];
966 } else {
Bill Richardsonb49b34d2018-04-27 10:29:38 -0700967 Error("Need a new password at least. Use '' to clear it.");
Bill Richardson41896c72018-01-18 21:43:12 -0800968 goto out;
969 }
970 /* two args provided, use both old & new passwords */
971 if (optind < argc) {
972 old_passwd = passwd;
973 passwd = argv[optind++];
974 }
975 }
Andrew Scull43d72e12017-11-20 14:14:55 +0000976
Bill Richardson41896c72018-01-18 21:43:12 -0800977 if ((options.enable_ro || options.enable_rw) && !passwd) {
978 if (optind < argc)
979 passwd = argv[optind++];
980 else {
981 Error("Need a password to enable images. Use '' if none.");
982 goto out;
983 }
984 }
985
986 /* Okay, let's do it! */
987 (void) execute_commands(image, old_passwd, passwd);
Andrew Scull43d72e12017-11-20 14:14:55 +0000988 /* This is the last action, so fall through either way */
989
990out:
991 return !!errorcnt;
992}