blob: 49931594e092d4ceff7d8e3d46f1d29025ad4450 [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
51/* Global options */
52struct options_s {
53 /* actions to take */
54 int version;
Andrew Sculld2c3a232018-03-29 20:24:11 +010055 int id;
Bill Richardson2917cb22018-01-26 11:59:26 -080056 int stats;
Andrew Scull43d72e12017-11-20 14:14:55 +000057 int ro;
58 int rw;
59 int reboot;
Bill Richardsonf0a6b802018-03-21 15:32:13 -070060 int force_reset;
Bill Richardson41896c72018-01-18 21:43:12 -080061 int enable_ro;
62 int enable_rw;
63 int change_pw;
64 uint32_t erase_code;
Andrew Scull43d72e12017-11-20 14:14:55 +000065 /* generic connection options */
66 const char *device;
Andrew Scull43d72e12017-11-20 14:14:55 +000067} options;
68
69enum no_short_opts_for_these {
70 OPT_DEVICE = 1000,
Andrew Sculld2c3a232018-03-29 20:24:11 +010071 OPT_ID,
Bill Richardson2917cb22018-01-26 11:59:26 -080072 OPT_STATS,
Andrew Scull43d72e12017-11-20 14:14:55 +000073 OPT_RO,
74 OPT_RW,
75 OPT_REBOOT,
Bill Richardsonf0a6b802018-03-21 15:32:13 -070076 OPT_FORCE_RESET,
Bill Richardson41896c72018-01-18 21:43:12 -080077 OPT_ENABLE_RO,
78 OPT_ENABLE_RW,
79 OPT_CHANGE_PW,
80 OPT_ERASE,
Andrew Scull43d72e12017-11-20 14:14:55 +000081};
82
83const char *short_opts = ":hv";
84const struct option long_opts[] = {
85 /* name hasarg *flag val */
86 {"version", 0, NULL, 'v'},
Andrew Sculld2c3a232018-03-29 20:24:11 +010087 {"id", 0, NULL, OPT_ID},
Bill Richardson2917cb22018-01-26 11:59:26 -080088 {"stats", 0, NULL, OPT_STATS},
Andrew Scull43d72e12017-11-20 14:14:55 +000089 {"ro", 0, NULL, OPT_RO},
90 {"rw", 0, NULL, OPT_RW},
91 {"reboot", 0, NULL, OPT_REBOOT},
Bill Richardsonf0a6b802018-03-21 15:32:13 -070092 {"force_reset", 0, NULL, OPT_FORCE_RESET},
Bill Richardson41896c72018-01-18 21:43:12 -080093 {"enable_ro", 0, NULL, OPT_ENABLE_RO},
94 {"enable_rw", 0, NULL, OPT_ENABLE_RW},
95 {"change_pw", 0, NULL, OPT_CHANGE_PW},
96 {"erase", 1, NULL, OPT_ERASE},
Bill Richardson153fa252018-02-23 14:39:58 -080097#ifndef ANDROID
Andrew Scull43d72e12017-11-20 14:14:55 +000098 {"device", 1, NULL, OPT_DEVICE},
Bill Richardson153fa252018-02-23 14:39:58 -080099#endif
Andrew Scull43d72e12017-11-20 14:14:55 +0000100 {"help", 0, NULL, 'h'},
101 {NULL, 0, NULL, 0},
102};
103
104void usage(const char *progname)
105{
106 fprintf(stderr, "\n");
107 fprintf(stderr,
108 "Usage: %s [actions] [image.bin]\n"
109 "\n"
110 "Citadel firmware boots in two stages. The first stage\n"
111 "bootloader (aka \"RO\") is provided by the SOC hardware team\n"
112 "and seldom changes. The application image (\"RW\") is invoked\n"
113 "by the RO image. There are two copies (A/B) of each stage,\n"
114 "so that the active copy can be protected while the unused\n"
115 "copy may be updated. At boot, the newer (valid) copy of each\n"
116 "stage is selected.\n"
117 "\n"
118 "The Citadel image file is the same size of the internal\n"
119 "flash, and contains all four firmware components (RO_A,\n"
120 "RW_A, RO_B, RW_B) located at the correct offsets. Only the\n"
121 "inactive copy (A/B) of each stage (RO/RW) can be modified.\n"
122 "The tool will update the correct copies automatically.\n"
123 "\n"
Bill Richardson153fa252018-02-23 14:39:58 -0800124 "You must specify the actions to perform. With no actions,\n"
Andrew Scull43d72e12017-11-20 14:14:55 +0000125 "this help message is displayed.\n"
126 "\n"
127 "Actions:\n"
128 "\n"
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700129 " -v, --version Display the Citadel version info\n"
Andrew Sculld2c3a232018-03-29 20:24:11 +0100130 " --id Display the Citadel device ID\n"
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700131 " --stats Display Low Power stats\n"
132 " --rw Update RW firmware from the image file\n"
133 " --ro Update RO firmware from the image file\n"
134 " --reboot Tell Citadel to reboot\n"
135 " --force_reset Pulse Citadel's reset line\n"
Bill Richardson41896c72018-01-18 21:43:12 -0800136 "\n"
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700137 " --enable_ro Mark new RO image as good\n"
138 " --enable_rw Mark new RW image as good\n"
Bill Richardson41896c72018-01-18 21:43:12 -0800139 "\n"
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700140 " --change_pw Change update password\n"
Bill Richardson41896c72018-01-18 21:43:12 -0800141 "\n\n"
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700142 " --erase=CODE Erase all user secrets and reboot.\n"
143 " This skips all other actions.\n"
Bill Richardson153fa252018-02-23 14:39:58 -0800144#ifndef ANDROID
145 "\n"
146 "Options:\n"
147 "\n"
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700148 " --device=SN Connect to the FDTI device with the given\n"
149 " serial number (try \"lsusb -v\"). A default\n"
150 " can be specified with the CITADEL_DEVICE\n"
151 " environment variable.\n"
Bill Richardson153fa252018-02-23 14:39:58 -0800152#endif
Andrew Scull43d72e12017-11-20 14:14:55 +0000153 "\n",
154 progname);
155}
156
157/****************************************************************************/
158/* Handy stuff */
159
160#ifndef MIN
161#define MIN(a, b) ((a) < (b) ? (a) : (b))
162#endif
163
164int errorcnt;
165void Error(const char *format, ...)
166{
167 va_list ap;
168
Bill Richardson41896c72018-01-18 21:43:12 -0800169 va_start(ap, format);
170 fprintf(stderr, "ERROR: ");
171 vfprintf(stderr, format, ap);
172 fprintf(stderr, "\n");
173 va_end(ap);
Andrew Scull43d72e12017-11-20 14:14:55 +0000174
175 errorcnt++;
176}
177
178/* Return true on APP_SUCESS, display error message if it's not */
179int is_app_success(uint32_t retval)
180{
181 if (retval == APP_SUCCESS)
182 return 1;
183
184 errorcnt++;
185
186 fprintf(stderr, "Error code 0x%x: ", retval);
187 switch (retval) {
188 case APP_ERROR_BOGUS_ARGS:
189 fprintf(stderr, "bogus args");
190 break;
191 case APP_ERROR_INTERNAL:
192 fprintf(stderr, "app is being stupid");
193 break;
194 case APP_ERROR_TOO_MUCH:
195 fprintf(stderr, "caller sent too much data");
196 break;
197 default:
198 if (retval >= APP_SPECIFIC_ERROR &&
199 retval < APP_LINE_NUMBER_BASE) {
200 fprintf(stderr, "app-specific error #%d",
201 retval - APP_SPECIFIC_ERROR);
202 } else if (retval >= APP_LINE_NUMBER_BASE) {
203 fprintf(stderr, "error at line %d",
204 retval - APP_LINE_NUMBER_BASE);
205 } else {
206 fprintf(stderr, "unknown)");
207 }
208 }
209 fprintf(stderr, "\n");
210
211 return 0;
212}
213
214/****************************************************************************/
215
216std::vector<uint8_t> read_image_from_file(const char *name)
217{
218 FILE *fp;
219 struct stat st;
220
221 fp = fopen(name, "rb");
222 if (!fp) {
223 perror("fopen");
224 Error("Can't open file %s", name);
225 return {};
226 }
227
228 if (fstat(fileno(fp), &st)) {
229 perror("fstat");
230 Error("Can't fstat file %s", name);
231 fclose(fp);
232 return {};
233 }
234
235 if (st.st_size != CHIP_FLASH_SIZE) {
236 Error("The firmware image must be exactly %d bytes",
237 CHIP_FLASH_SIZE);
238 fclose(fp);
239 return {};
240 }
241
242 std::vector<uint8_t> buf(st.st_size);
243 if (1 != fread(buf.data(), st.st_size, 1, fp)) {
244 perror("fread");
245 Error("Can't read %zd bytes", st.st_size);
246 fclose(fp);
247 return {};
248 }
249
250 fclose(fp);
251 buf.resize(st.st_size);
252
253 return buf;
254}
255
Bill Richardson41896c72018-01-18 21:43:12 -0800256uint32_t compute_digest(void *ptr, size_t len)
Andrew Scull43d72e12017-11-20 14:14:55 +0000257{
Andrew Scull43d72e12017-11-20 14:14:55 +0000258 SHA_CTX ctx;
259 uint8_t digest[SHA_DIGEST_LENGTH];
260 uint32_t retval;
261
262 SHA1_Init(&ctx);
Bill Richardson41896c72018-01-18 21:43:12 -0800263 SHA1_Update(&ctx, ptr, len);
Andrew Scull43d72e12017-11-20 14:14:55 +0000264 SHA1_Final(digest, &ctx);
265
266 memcpy(&retval, digest, sizeof(retval));
267 return retval;
268}
269
Bill Richardson41896c72018-01-18 21:43:12 -0800270uint32_t compute_fb_digest(struct nugget_app_flash_block *blk)
271{
272 uint8_t *start_here = ((uint8_t *)blk) +
273 offsetof(struct nugget_app_flash_block, offset);
274 size_t size_to_hash = sizeof(*blk) -
275 offsetof(struct nugget_app_flash_block, offset);
276
277 return compute_digest(start_here, size_to_hash);
278}
279
Andrew Scull43d72e12017-11-20 14:14:55 +0000280uint32_t try_update(AppClient &app, const std::vector<uint8_t> &image,
281 uint32_t offset, uint32_t imagesize)
282{
283 uint32_t stop = offset + imagesize;
284 uint32_t rv;
285
286 printf("Updating image from 0x%05x to 0x%05x, size 0x%05x\n",
287 CHIP_FLASH_BASE + offset, CHIP_FLASH_BASE + stop, imagesize);
288
289 for (; offset < stop; offset += CHIP_FLASH_BANK_SIZE) {
290 int retries = 3;
291 std::vector<uint8_t> data(sizeof(struct nugget_app_flash_block));
292 struct nugget_app_flash_block *fb =
293 (struct nugget_app_flash_block*)data.data();
294
295 fb->offset = offset;
296 memcpy(fb->payload, image.data() + offset, CHIP_FLASH_BANK_SIZE);
Bill Richardson41896c72018-01-18 21:43:12 -0800297 fb->block_digest = compute_fb_digest(fb);
Andrew Scull43d72e12017-11-20 14:14:55 +0000298
299 printf("writing 0x%05x / 0x%05x",
300 CHIP_FLASH_BASE + offset, CHIP_FLASH_BASE + stop);
301 do {
302 rv = app.Call(NUGGET_PARAM_FLASH_BLOCK, data, nullptr);
303 if (rv == NUGGET_ERROR_RETRY)
304 printf(" retrying");
305 } while (rv == NUGGET_ERROR_RETRY && retries--);
Bill Richardson41896c72018-01-18 21:43:12 -0800306 if (rv) {
307 if (rv == NUGGET_ERROR_LOCKED)
308 printf(" locked\n");
309 else
310 printf(" fail %d\n", rv);
Andrew Scull43d72e12017-11-20 14:14:55 +0000311 break;
Bill Richardson41896c72018-01-18 21:43:12 -0800312 }
313 printf(" ok\n");
Andrew Scull43d72e12017-11-20 14:14:55 +0000314 }
315
316 return rv;
317}
318
Andrew Scull43d72e12017-11-20 14:14:55 +0000319uint32_t do_update(AppClient &app, const std::vector<uint8_t> &image,
320 uint32_t offset_A, uint32_t offset_B)
321{
322 struct SignedHeader *hdr;
323 uint32_t rv_A, rv_B;
324
325 /* Try image A first */
326 hdr = (struct SignedHeader *)(image.data() + offset_A);
327 rv_A = try_update(app, image, offset_A, hdr->image_size);
328
329 /* If that worked, we're done */
330 if (rv_A == APP_SUCCESS) {
331 return rv_A;
332 }
333
334 /* Else try image B */
335 hdr = (struct SignedHeader *)(image.data() + offset_B);
336 rv_B = try_update(app, image, offset_B, hdr->image_size);
337
338 return rv_B;
339}
340
341uint32_t do_version(AppClient &app)
342{
343 uint32_t retval;
344 std::vector<uint8_t> buffer;
345 buffer.reserve(512);
346
347 retval = app.Call(NUGGET_PARAM_VERSION, buffer, &buffer);
348
349 if (is_app_success(retval)) {
350 printf("%.*s\n", (int) buffer.size(), buffer.data());
351 }
352
353 return retval;
354}
355
Andrew Sculld2c3a232018-03-29 20:24:11 +0100356uint32_t do_id(AppClient &app)
357{
358 uint32_t retval;
359 std::vector<uint8_t> buffer;
360 buffer.reserve(32);
361
362 retval = app.Call(NUGGET_PARAM_DEVICE_ID, buffer, &buffer);
363
364 if (is_app_success(retval)) {
365 printf("%.*s\n", (int) buffer.size(), buffer.data());
366 }
367
368 return retval;
369}
370
Bill Richardson2917cb22018-01-26 11:59:26 -0800371uint32_t do_stats(AppClient &app)
372{
373 struct nugget_app_low_power_stats stats;
374 std::vector<uint8_t> buffer;
375 uint32_t retval;
376
377 buffer.reserve(sizeof(stats));
378
379 retval = app.Call(NUGGET_PARAM_GET_LOW_POWER_STATS, buffer, &buffer);
380
381 if (is_app_success(retval)) {
382 if (buffer.size() < sizeof(stats)) {
Allen Webb2fdba9b2018-03-30 15:05:33 -0700383 fprintf(stderr, "Only got %zd / %zd bytes back",
Bill Richardson2917cb22018-01-26 11:59:26 -0800384 buffer.size(), sizeof(stats));
385 return -1;
386 }
387
388 memcpy(&stats, buffer.data(), sizeof(stats));
389
Allen Webb6f09c472018-03-30 14:55:29 -0700390 printf("hard_reset_count %" PRIu64 "\n", stats.hard_reset_count);
391 printf("time_since_hard_reset %" PRIu64 "\n",
392 stats.time_since_hard_reset);
393 printf("wake_count %" PRIu64 "\n", stats.wake_count);
394 printf("time_at_last_wake %" PRIu64 "\n", stats.time_at_last_wake);
395 printf("time_spent_awake %" PRIu64 "\n", stats.time_spent_awake);
396 printf("deep_sleep_count %" PRIu64 "\n", stats.deep_sleep_count);
397 printf("time_at_last_deep_sleep %" PRIu64 "\n",
Allen Webbc4cc3c72018-03-30 14:44:11 -0700398 stats.time_at_last_deep_sleep);
Allen Webb6f09c472018-03-30 14:55:29 -0700399 printf("time_spent_in_deep_sleep %" PRIu64 "\n",
Allen Webbc4cc3c72018-03-30 14:44:11 -0700400 stats.time_spent_in_deep_sleep);
Bill Richardson2917cb22018-01-26 11:59:26 -0800401 }
402
403 return retval;
404}
405
Andrew Scull43d72e12017-11-20 14:14:55 +0000406uint32_t do_reboot(AppClient &app)
407{
408 uint32_t retval;
Bill Richardson580e93a2018-04-25 16:43:24 -0700409 std::vector<uint8_t> ignored = {1}; // older images need this
Andrew Scull43d72e12017-11-20 14:14:55 +0000410
Bill Richardson580e93a2018-04-25 16:43:24 -0700411 retval = app.Call(NUGGET_PARAM_REBOOT, ignored, nullptr);
Andrew Scull43d72e12017-11-20 14:14:55 +0000412
413 if (is_app_success(retval)) {
414 printf("Citadel reboot requested\n");
415 }
416
417 return retval;
418}
419
Bill Richardson41896c72018-01-18 21:43:12 -0800420static uint32_t do_change_pw(AppClient &app,
421 const char *old_pw, const char *new_pw)
422{
423 std::vector<uint8_t> data(sizeof(struct nugget_app_change_update_password));
424 struct nugget_app_change_update_password *s =
425 (struct nugget_app_change_update_password*)data.data();
426
427
428 memset(s, 0xff, sizeof(*s));
429 if (old_pw && *old_pw) {
430 int len = strlen(old_pw);
431 memcpy(&s->old_password.password, old_pw, len);
432 s->old_password.digest =
433 compute_digest(&s->old_password.password,
434 sizeof(s->old_password.password));
435 }
436
437 if (new_pw && *new_pw) {
438 int len = strlen(new_pw);
439 memcpy(&s->new_password.password, new_pw, len);
440 s->new_password.digest =
441 compute_digest(&s->new_password.password,
442 sizeof(s->new_password.password));
443 }
444
445 uint32_t rv = app.Call(NUGGET_PARAM_CHANGE_UPDATE_PASSWORD, data, nullptr);
446
447 if (is_app_success(rv))
448 printf("Password changed\n");
449
450 return rv;
451}
452
453static uint32_t do_enable(AppClient &app, const char *pw)
454{
Bill Richardson41896c72018-01-18 21:43:12 -0800455 std::vector<uint8_t> data(sizeof(struct nugget_app_enable_update));
456 struct nugget_app_enable_update *s =
457 (struct nugget_app_enable_update*)data.data();
458
459 memset(&s->password, 0xff, sizeof(s->password));
460 if (pw && *pw) {
461 int len = strlen(pw);
462 memcpy(&s->password.password, pw, len);
463 s->password.digest =
464 compute_digest(&s->password.password,
465 sizeof(s->password.password));
466 }
467 s->which_headers = options.enable_ro ? NUGGET_ENABLE_HEADER_RO : 0;
468 s->which_headers |= options.enable_rw ? NUGGET_ENABLE_HEADER_RW : 0;
469
470 uint32_t rv = app.Call(NUGGET_PARAM_ENABLE_UPDATE, data, nullptr);
471
472 if (is_app_success(rv))
473 printf("Update enabled\n");
474
475 return rv;
476}
477
478static uint32_t do_erase(AppClient &app)
479{
480 std::vector<uint8_t> data(sizeof(uint32_t));
481 memcpy(data.data(), &options.erase_code, data.size());
482
483 uint32_t rv = app.Call(NUGGET_PARAM_NUKE_FROM_ORBIT, data, nullptr);
484
485 if (is_app_success(rv))
486 printf("Citadel erase and reboot requested\n");
487
488 return rv;
489}
490
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700491// This is currently device-specific, but could be abstracted further
Andrew Scull43d72e12017-11-20 14:14:55 +0000492#ifdef ANDROID
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700493static uint32_t do_force_reset(CitadeldProxyClient &client)
494{
495 bool b = false;
496
497 return !client.Citadeld().reset(&b).isOk();
Andrew Scull43d72e12017-11-20 14:14:55 +0000498}
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700499#else
500static uint32_t do_force_reset(NuggetClient &client)
501{
502 struct nos_device *d = client.Device();
503
504 return d->ops.reset(d->ctx);
505}
506#endif
Andrew Scull43d72e12017-11-20 14:14:55 +0000507
Bill Richardson41896c72018-01-18 21:43:12 -0800508int execute_commands(const std::vector<uint8_t> &image,
509 const char *old_passwd, const char *passwd)
Andrew Scull43d72e12017-11-20 14:14:55 +0000510{
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700511#ifdef ANDROID
512 CitadeldProxyClient client;
513#else
514 NuggetClient client(options.device ? options.device : "");
515#endif
516
517 client.Open();
518 if (!client.IsOpen()) {
Andrew Scull43d72e12017-11-20 14:14:55 +0000519 Error("Unable to connect");
520 return 1;
521 }
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700522 AppClient app(client, APP_ID_NUGGET);
Andrew Scull43d72e12017-11-20 14:14:55 +0000523
524 /* Try all requested actions in reasonable order, bail out on error */
525
Bill Richardson41896c72018-01-18 21:43:12 -0800526 if (options.erase_code) { /* zero doesn't count */
527 /* whether we succeed or not, only do this */
528 return do_erase(app);
529 }
530
Andrew Scull43d72e12017-11-20 14:14:55 +0000531 if (options.version &&
532 do_version(app) != APP_SUCCESS) {
533 return 2;
534 }
535
Andrew Sculld2c3a232018-03-29 20:24:11 +0100536 if (options.id &&
537 do_id(app) != APP_SUCCESS) {
538 return 2;
539 }
540
Bill Richardson2917cb22018-01-26 11:59:26 -0800541 if (options.stats &&
542 do_stats(app) != APP_SUCCESS) {
543 return 2;
544 }
545
Andrew Scull43d72e12017-11-20 14:14:55 +0000546 if (options.rw &&
547 do_update(app, image,
548 CHIP_RW_A_MEM_OFF, CHIP_RW_B_MEM_OFF) != APP_SUCCESS) {
549 return 3;
550 }
551
552 if (options.ro &&
553 do_update(app, image,
554 CHIP_RO_A_MEM_OFF, CHIP_RO_B_MEM_OFF) != APP_SUCCESS) {
555 return 4;
556 }
557
Bill Richardson41896c72018-01-18 21:43:12 -0800558 if (options.change_pw &&
559 do_change_pw(app, old_passwd, passwd) != APP_SUCCESS)
560 return 5;
561
562 if ((options.enable_ro || options.enable_rw) &&
563 do_enable(app, passwd) != APP_SUCCESS)
564 return 6;
565
Andrew Scull43d72e12017-11-20 14:14:55 +0000566 if (options.reboot &&
567 do_reboot(app) != APP_SUCCESS) {
Bill Richardson41896c72018-01-18 21:43:12 -0800568 return 7;
Andrew Scull43d72e12017-11-20 14:14:55 +0000569 }
Bill Richardson41896c72018-01-18 21:43:12 -0800570
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700571 if (options.force_reset &&
572 do_force_reset(client) != APP_SUCCESS) {
573 return 1;
574 }
575
Andrew Scull43d72e12017-11-20 14:14:55 +0000576 return 0;
577}
578
579} // namespace
580
581int main(int argc, char *argv[])
582{
583 int i;
584 int idx = 0;
585 char *this_prog;
Bill Richardson41896c72018-01-18 21:43:12 -0800586 char *old_passwd = 0;
587 char *passwd = 0;
Andrew Scull43d72e12017-11-20 14:14:55 +0000588 std::vector<uint8_t> image;
589 int got_action = 0;
Bill Richardson41896c72018-01-18 21:43:12 -0800590 char *e = 0;
Andrew Scull43d72e12017-11-20 14:14:55 +0000591
592 this_prog= strrchr(argv[0], '/');
Bill Richardson153fa252018-02-23 14:39:58 -0800593 if (this_prog) {
Andrew Scull43d72e12017-11-20 14:14:55 +0000594 this_prog++;
595 } else {
596 this_prog = argv[0];
597 }
598
Bill Richardson153fa252018-02-23 14:39:58 -0800599#ifndef ANDROID
600 options.device = secure_getenv("CITADEL_DEVICE");
601 if (options.device)
602 fprintf(stderr, "-- $CITADEL_DEVICE=%s --\n", options.device);
603#endif
604
605 opterr = 0; /* quiet, you */
Andrew Scull43d72e12017-11-20 14:14:55 +0000606 while ((i = getopt_long(argc, argv,
607 short_opts, long_opts, &idx)) != -1) {
608 switch (i) {
609 /* program-specific options */
610 case 'v':
611 options.version = 1;
612 got_action = 1;
613 break;
Andrew Sculld2c3a232018-03-29 20:24:11 +0100614 case OPT_ID:
615 options.id = 1;
616 got_action = 1;
617 break;
Bill Richardson2917cb22018-01-26 11:59:26 -0800618 case OPT_STATS:
619 options.stats = 1;
620 got_action = 1;
621 break;
Andrew Scull43d72e12017-11-20 14:14:55 +0000622 case OPT_RO:
623 options.ro = 1;
624 got_action = 1;
625 break;
626 case OPT_RW:
627 options.rw = 1;
628 got_action = 1;
629 break;
630 case OPT_REBOOT:
631 options.reboot = 1;
632 got_action = 1;
633 break;
Bill Richardsonf0a6b802018-03-21 15:32:13 -0700634 case OPT_FORCE_RESET:
635 options.force_reset = 1;
636 got_action = 1;
637 break;
Bill Richardson41896c72018-01-18 21:43:12 -0800638 case OPT_ENABLE_RO:
639 options.enable_ro = 1;
640 got_action = 1;
641 break;
642 case OPT_ENABLE_RW:
643 options.enable_rw = 1;
644 got_action = 1;
645 break;
646 case OPT_CHANGE_PW:
647 options.change_pw = 1;
648 got_action = 1;
649 break;
650 case OPT_ERASE:
651 options.erase_code = (uint32_t)strtoul(optarg, &e, 0);
652 if (!*optarg || (e && *e)) {
653 Error("Invalid argument: \"%s\"\n", optarg);
654 errorcnt++;
655 }
656 got_action = 1;
657 break;
Andrew Scull43d72e12017-11-20 14:14:55 +0000658
659 /* generic options below */
660 case OPT_DEVICE:
661 options.device = optarg;
662 break;
Andrew Scull43d72e12017-11-20 14:14:55 +0000663 case 'h':
664 usage(this_prog);
665 return 0;
666 case 0:
667 break;
668 case '?':
669 if (optopt)
670 Error("Unrecognized options: -%c", optopt);
671 else
672 Error("Unrecognized options: %s",
673 argv[optind - 1]);
674 usage(this_prog);
675 break;
676 case ':':
677 Error("Missing argument to %s", argv[optind - 1]);
678 break;
679 default:
680 Error("Internal error at %s:%d", __FILE__, __LINE__);
681 exit(1);
682 }
683 }
684
685 if (errorcnt) {
686 goto out;
687 }
688
689 if (!got_action) {
690 usage(this_prog);
691 goto out;
692 }
693
694 if (options.ro || options.rw) {
695 if (optind < argc) {
696 /* Sets errorcnt on failure */
Bill Richardson76b40fe2018-01-22 10:28:24 -0800697 image = read_image_from_file(argv[optind++]);
Bill Richardson41896c72018-01-18 21:43:12 -0800698 if (errorcnt)
699 goto out;
Andrew Scull43d72e12017-11-20 14:14:55 +0000700 } else {
701 Error("An image file is required with --ro and --rw");
Bill Richardson41896c72018-01-18 21:43:12 -0800702 goto out;
Andrew Scull43d72e12017-11-20 14:14:55 +0000703 }
704 }
705
Bill Richardson41896c72018-01-18 21:43:12 -0800706 if (options.change_pw) {
707 /* one arg provided, maybe the old password isn't set */
708 if (optind < argc) {
709 passwd = argv[optind++];
710 } else {
711 Error("Need a new password at least."
712 " Use '' to clear it.");
713 goto out;
714 }
715 /* two args provided, use both old & new passwords */
716 if (optind < argc) {
717 old_passwd = passwd;
718 passwd = argv[optind++];
719 }
720 }
Andrew Scull43d72e12017-11-20 14:14:55 +0000721
Bill Richardson41896c72018-01-18 21:43:12 -0800722 if ((options.enable_ro || options.enable_rw) && !passwd) {
723 if (optind < argc)
724 passwd = argv[optind++];
725 else {
726 Error("Need a password to enable images. Use '' if none.");
727 goto out;
728 }
729 }
730
731 /* Okay, let's do it! */
732 (void) execute_commands(image, old_passwd, passwd);
Andrew Scull43d72e12017-11-20 14:14:55 +0000733 /* This is the last action, so fall through either way */
734
735out:
736 return !!errorcnt;
737}