blob: fbbcd8dc6c4d5afe063a26bc7a1295992289b441 [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;
55 int ro;
56 int rw;
57 int reboot;
Bill Richardson41896c72018-01-18 21:43:12 -080058 int enable_ro;
59 int enable_rw;
60 int change_pw;
61 uint32_t erase_code;
Andrew Scull43d72e12017-11-20 14:14:55 +000062 /* generic connection options */
63 const char *device;
Andrew Scull43d72e12017-11-20 14:14:55 +000064} options;
65
66enum no_short_opts_for_these {
67 OPT_DEVICE = 1000,
68 OPT_RO,
69 OPT_RW,
70 OPT_REBOOT,
Bill Richardson41896c72018-01-18 21:43:12 -080071 OPT_ENABLE_RO,
72 OPT_ENABLE_RW,
73 OPT_CHANGE_PW,
74 OPT_ERASE,
Andrew Scull43d72e12017-11-20 14:14:55 +000075};
76
77const char *short_opts = ":hv";
78const struct option long_opts[] = {
79 /* name hasarg *flag val */
80 {"version", 0, NULL, 'v'},
81 {"ro", 0, NULL, OPT_RO},
82 {"rw", 0, NULL, OPT_RW},
83 {"reboot", 0, NULL, OPT_REBOOT},
Bill Richardson41896c72018-01-18 21:43:12 -080084 {"enable_ro", 0, NULL, OPT_ENABLE_RO},
85 {"enable_rw", 0, NULL, OPT_ENABLE_RW},
86 {"change_pw", 0, NULL, OPT_CHANGE_PW},
87 {"erase", 1, NULL, OPT_ERASE},
Andrew Scull43d72e12017-11-20 14:14:55 +000088 {"device", 1, NULL, OPT_DEVICE},
Andrew Scull43d72e12017-11-20 14:14:55 +000089 {"help", 0, NULL, 'h'},
90 {NULL, 0, NULL, 0},
91};
92
93void usage(const char *progname)
94{
95 fprintf(stderr, "\n");
96 fprintf(stderr,
97 "Usage: %s [actions] [image.bin]\n"
98 "\n"
99 "Citadel firmware boots in two stages. The first stage\n"
100 "bootloader (aka \"RO\") is provided by the SOC hardware team\n"
101 "and seldom changes. The application image (\"RW\") is invoked\n"
102 "by the RO image. There are two copies (A/B) of each stage,\n"
103 "so that the active copy can be protected while the unused\n"
104 "copy may be updated. At boot, the newer (valid) copy of each\n"
105 "stage is selected.\n"
106 "\n"
107 "The Citadel image file is the same size of the internal\n"
108 "flash, and contains all four firmware components (RO_A,\n"
109 "RW_A, RO_B, RW_B) located at the correct offsets. Only the\n"
110 "inactive copy (A/B) of each stage (RO/RW) can be modified.\n"
111 "The tool will update the correct copies automatically.\n"
112 "\n"
113 "You must specify the actions to perform. With no options,\n"
114 "this help message is displayed.\n"
115 "\n"
116 "Actions:\n"
117 "\n"
118 " -v, --version Display the Citadel version info\n"
119 " --rw Update RW firmware from the image file\n"
120 " --ro Update RO firmware from the image file\n"
121 " --reboot Tell Citadel to reboot\n"
Bill Richardson41896c72018-01-18 21:43:12 -0800122 "\n"
123 " --enable_ro Mark new RO image as good\n"
124 " --enable_rw Mark new RW image as good\n"
125 "\n"
126 " --change_pw Change update password\n"
127 "\n\n"
128 " --erase=CODE Erase all user secrets and reboot.\n"
129 " This skips all other actions.\n"
Andrew Scull43d72e12017-11-20 14:14:55 +0000130 "\n",
131 progname);
132}
133
134/****************************************************************************/
135/* Handy stuff */
136
137#ifndef MIN
138#define MIN(a, b) ((a) < (b) ? (a) : (b))
139#endif
140
141int errorcnt;
142void Error(const char *format, ...)
143{
144 va_list ap;
145
Bill Richardson41896c72018-01-18 21:43:12 -0800146 va_start(ap, format);
147 fprintf(stderr, "ERROR: ");
148 vfprintf(stderr, format, ap);
149 fprintf(stderr, "\n");
150 va_end(ap);
Andrew Scull43d72e12017-11-20 14:14:55 +0000151
152 errorcnt++;
153}
154
155/* Return true on APP_SUCESS, display error message if it's not */
156int is_app_success(uint32_t retval)
157{
158 if (retval == APP_SUCCESS)
159 return 1;
160
161 errorcnt++;
162
163 fprintf(stderr, "Error code 0x%x: ", retval);
164 switch (retval) {
165 case APP_ERROR_BOGUS_ARGS:
166 fprintf(stderr, "bogus args");
167 break;
168 case APP_ERROR_INTERNAL:
169 fprintf(stderr, "app is being stupid");
170 break;
171 case APP_ERROR_TOO_MUCH:
172 fprintf(stderr, "caller sent too much data");
173 break;
174 default:
175 if (retval >= APP_SPECIFIC_ERROR &&
176 retval < APP_LINE_NUMBER_BASE) {
177 fprintf(stderr, "app-specific error #%d",
178 retval - APP_SPECIFIC_ERROR);
179 } else if (retval >= APP_LINE_NUMBER_BASE) {
180 fprintf(stderr, "error at line %d",
181 retval - APP_LINE_NUMBER_BASE);
182 } else {
183 fprintf(stderr, "unknown)");
184 }
185 }
186 fprintf(stderr, "\n");
187
188 return 0;
189}
190
191/****************************************************************************/
192
193std::vector<uint8_t> read_image_from_file(const char *name)
194{
195 FILE *fp;
196 struct stat st;
197
198 fp = fopen(name, "rb");
199 if (!fp) {
200 perror("fopen");
201 Error("Can't open file %s", name);
202 return {};
203 }
204
205 if (fstat(fileno(fp), &st)) {
206 perror("fstat");
207 Error("Can't fstat file %s", name);
208 fclose(fp);
209 return {};
210 }
211
212 if (st.st_size != CHIP_FLASH_SIZE) {
213 Error("The firmware image must be exactly %d bytes",
214 CHIP_FLASH_SIZE);
215 fclose(fp);
216 return {};
217 }
218
219 std::vector<uint8_t> buf(st.st_size);
220 if (1 != fread(buf.data(), st.st_size, 1, fp)) {
221 perror("fread");
222 Error("Can't read %zd bytes", st.st_size);
223 fclose(fp);
224 return {};
225 }
226
227 fclose(fp);
228 buf.resize(st.st_size);
229
230 return buf;
231}
232
Bill Richardson41896c72018-01-18 21:43:12 -0800233uint32_t compute_digest(void *ptr, size_t len)
Andrew Scull43d72e12017-11-20 14:14:55 +0000234{
Andrew Scull43d72e12017-11-20 14:14:55 +0000235 SHA_CTX ctx;
236 uint8_t digest[SHA_DIGEST_LENGTH];
237 uint32_t retval;
238
239 SHA1_Init(&ctx);
Bill Richardson41896c72018-01-18 21:43:12 -0800240 SHA1_Update(&ctx, ptr, len);
Andrew Scull43d72e12017-11-20 14:14:55 +0000241 SHA1_Final(digest, &ctx);
242
243 memcpy(&retval, digest, sizeof(retval));
244 return retval;
245}
246
Bill Richardson41896c72018-01-18 21:43:12 -0800247uint32_t compute_fb_digest(struct nugget_app_flash_block *blk)
248{
249 uint8_t *start_here = ((uint8_t *)blk) +
250 offsetof(struct nugget_app_flash_block, offset);
251 size_t size_to_hash = sizeof(*blk) -
252 offsetof(struct nugget_app_flash_block, offset);
253
254 return compute_digest(start_here, size_to_hash);
255}
256
Andrew Scull43d72e12017-11-20 14:14:55 +0000257uint32_t try_update(AppClient &app, const std::vector<uint8_t> &image,
258 uint32_t offset, uint32_t imagesize)
259{
260 uint32_t stop = offset + imagesize;
261 uint32_t rv;
262
263 printf("Updating image from 0x%05x to 0x%05x, size 0x%05x\n",
264 CHIP_FLASH_BASE + offset, CHIP_FLASH_BASE + stop, imagesize);
265
266 for (; offset < stop; offset += CHIP_FLASH_BANK_SIZE) {
267 int retries = 3;
268 std::vector<uint8_t> data(sizeof(struct nugget_app_flash_block));
269 struct nugget_app_flash_block *fb =
270 (struct nugget_app_flash_block*)data.data();
271
272 fb->offset = offset;
273 memcpy(fb->payload, image.data() + offset, CHIP_FLASH_BANK_SIZE);
Bill Richardson41896c72018-01-18 21:43:12 -0800274 fb->block_digest = compute_fb_digest(fb);
Andrew Scull43d72e12017-11-20 14:14:55 +0000275
276 printf("writing 0x%05x / 0x%05x",
277 CHIP_FLASH_BASE + offset, CHIP_FLASH_BASE + stop);
278 do {
279 rv = app.Call(NUGGET_PARAM_FLASH_BLOCK, data, nullptr);
280 if (rv == NUGGET_ERROR_RETRY)
281 printf(" retrying");
282 } while (rv == NUGGET_ERROR_RETRY && retries--);
Bill Richardson41896c72018-01-18 21:43:12 -0800283 if (rv) {
284 if (rv == NUGGET_ERROR_LOCKED)
285 printf(" locked\n");
286 else
287 printf(" fail %d\n", rv);
Andrew Scull43d72e12017-11-20 14:14:55 +0000288 break;
Bill Richardson41896c72018-01-18 21:43:12 -0800289 }
290 printf(" ok\n");
Andrew Scull43d72e12017-11-20 14:14:55 +0000291 }
292
293 return rv;
294}
295
Andrew Scull43d72e12017-11-20 14:14:55 +0000296uint32_t do_update(AppClient &app, const std::vector<uint8_t> &image,
297 uint32_t offset_A, uint32_t offset_B)
298{
299 struct SignedHeader *hdr;
300 uint32_t rv_A, rv_B;
301
302 /* Try image A first */
303 hdr = (struct SignedHeader *)(image.data() + offset_A);
304 rv_A = try_update(app, image, offset_A, hdr->image_size);
305
306 /* If that worked, we're done */
307 if (rv_A == APP_SUCCESS) {
308 return rv_A;
309 }
310
311 /* Else try image B */
312 hdr = (struct SignedHeader *)(image.data() + offset_B);
313 rv_B = try_update(app, image, offset_B, hdr->image_size);
314
315 return rv_B;
316}
317
318uint32_t do_version(AppClient &app)
319{
320 uint32_t retval;
321 std::vector<uint8_t> buffer;
322 buffer.reserve(512);
323
324 retval = app.Call(NUGGET_PARAM_VERSION, buffer, &buffer);
325
326 if (is_app_success(retval)) {
327 printf("%.*s\n", (int) buffer.size(), buffer.data());
328 }
329
330 return retval;
331}
332
333uint32_t do_reboot(AppClient &app)
334{
335 uint32_t retval;
Bill Richardson10629c62018-01-24 10:28:35 -0800336 std::vector<uint8_t> data = {NUGGET_REBOOT_HARD};
Andrew Scull43d72e12017-11-20 14:14:55 +0000337
338 retval = app.Call(NUGGET_PARAM_REBOOT, data, nullptr);
339
340 if (is_app_success(retval)) {
341 printf("Citadel reboot requested\n");
342 }
343
344 return retval;
345}
346
Bill Richardson41896c72018-01-18 21:43:12 -0800347static uint32_t do_change_pw(AppClient &app,
348 const char *old_pw, const char *new_pw)
349{
350 std::vector<uint8_t> data(sizeof(struct nugget_app_change_update_password));
351 struct nugget_app_change_update_password *s =
352 (struct nugget_app_change_update_password*)data.data();
353
354
355 memset(s, 0xff, sizeof(*s));
356 if (old_pw && *old_pw) {
357 int len = strlen(old_pw);
358 memcpy(&s->old_password.password, old_pw, len);
359 s->old_password.digest =
360 compute_digest(&s->old_password.password,
361 sizeof(s->old_password.password));
362 }
363
364 if (new_pw && *new_pw) {
365 int len = strlen(new_pw);
366 memcpy(&s->new_password.password, new_pw, len);
367 s->new_password.digest =
368 compute_digest(&s->new_password.password,
369 sizeof(s->new_password.password));
370 }
371
372 uint32_t rv = app.Call(NUGGET_PARAM_CHANGE_UPDATE_PASSWORD, data, nullptr);
373
374 if (is_app_success(rv))
375 printf("Password changed\n");
376
377 return rv;
378}
379
380static uint32_t do_enable(AppClient &app, const char *pw)
381{
Bill Richardson41896c72018-01-18 21:43:12 -0800382 std::vector<uint8_t> data(sizeof(struct nugget_app_enable_update));
383 struct nugget_app_enable_update *s =
384 (struct nugget_app_enable_update*)data.data();
385
386 memset(&s->password, 0xff, sizeof(s->password));
387 if (pw && *pw) {
388 int len = strlen(pw);
389 memcpy(&s->password.password, pw, len);
390 s->password.digest =
391 compute_digest(&s->password.password,
392 sizeof(s->password.password));
393 }
394 s->which_headers = options.enable_ro ? NUGGET_ENABLE_HEADER_RO : 0;
395 s->which_headers |= options.enable_rw ? NUGGET_ENABLE_HEADER_RW : 0;
396
397 uint32_t rv = app.Call(NUGGET_PARAM_ENABLE_UPDATE, data, nullptr);
398
399 if (is_app_success(rv))
400 printf("Update enabled\n");
401
402 return rv;
403}
404
405static uint32_t do_erase(AppClient &app)
406{
407 std::vector<uint8_t> data(sizeof(uint32_t));
408 memcpy(data.data(), &options.erase_code, data.size());
409
410 uint32_t rv = app.Call(NUGGET_PARAM_NUKE_FROM_ORBIT, data, nullptr);
411
412 if (is_app_success(rv))
413 printf("Citadel erase and reboot requested\n");
414
415 return rv;
416}
417
Andrew Scull43d72e12017-11-20 14:14:55 +0000418std::unique_ptr<NuggetClientInterface> select_client()
419{
420#ifdef ANDROID
Andrew Sculle0b457e2018-01-13 16:03:50 +0000421 return std::unique_ptr<NuggetClientInterface>(new CitadeldProxyClient());
422#else
Andrew Scull43d72e12017-11-20 14:14:55 +0000423 return std::unique_ptr<NuggetClientInterface>(
424 new NuggetClient(options.device ? options.device : ""));
Andrew Sculle0b457e2018-01-13 16:03:50 +0000425#endif
Andrew Scull43d72e12017-11-20 14:14:55 +0000426}
427
Bill Richardson41896c72018-01-18 21:43:12 -0800428int execute_commands(const std::vector<uint8_t> &image,
429 const char *old_passwd, const char *passwd)
Andrew Scull43d72e12017-11-20 14:14:55 +0000430{
431 auto client = select_client();
432 client->Open();
433 if (!client->IsOpen()) {
434 Error("Unable to connect");
435 return 1;
436 }
437 AppClient app(*client, APP_ID_NUGGET);
438
439 /* Try all requested actions in reasonable order, bail out on error */
440
Bill Richardson41896c72018-01-18 21:43:12 -0800441 if (options.erase_code) { /* zero doesn't count */
442 /* whether we succeed or not, only do this */
443 return do_erase(app);
444 }
445
Andrew Scull43d72e12017-11-20 14:14:55 +0000446 if (options.version &&
447 do_version(app) != APP_SUCCESS) {
448 return 2;
449 }
450
451 if (options.rw &&
452 do_update(app, image,
453 CHIP_RW_A_MEM_OFF, CHIP_RW_B_MEM_OFF) != APP_SUCCESS) {
454 return 3;
455 }
456
457 if (options.ro &&
458 do_update(app, image,
459 CHIP_RO_A_MEM_OFF, CHIP_RO_B_MEM_OFF) != APP_SUCCESS) {
460 return 4;
461 }
462
Bill Richardson41896c72018-01-18 21:43:12 -0800463 if (options.change_pw &&
464 do_change_pw(app, old_passwd, passwd) != APP_SUCCESS)
465 return 5;
466
467 if ((options.enable_ro || options.enable_rw) &&
468 do_enable(app, passwd) != APP_SUCCESS)
469 return 6;
470
Andrew Scull43d72e12017-11-20 14:14:55 +0000471 if (options.reboot &&
472 do_reboot(app) != APP_SUCCESS) {
Bill Richardson41896c72018-01-18 21:43:12 -0800473 return 7;
Andrew Scull43d72e12017-11-20 14:14:55 +0000474 }
Bill Richardson41896c72018-01-18 21:43:12 -0800475
Andrew Scull43d72e12017-11-20 14:14:55 +0000476 return 0;
477}
478
479} // namespace
480
481int main(int argc, char *argv[])
482{
483 int i;
484 int idx = 0;
485 char *this_prog;
Bill Richardson41896c72018-01-18 21:43:12 -0800486 char *old_passwd = 0;
487 char *passwd = 0;
Andrew Scull43d72e12017-11-20 14:14:55 +0000488 std::vector<uint8_t> image;
489 int got_action = 0;
Bill Richardson41896c72018-01-18 21:43:12 -0800490 char *e = 0;
Andrew Scull43d72e12017-11-20 14:14:55 +0000491
492 this_prog= strrchr(argv[0], '/');
493 if (this_prog) {
494 this_prog++;
495 } else {
496 this_prog = argv[0];
497 }
498
499 opterr = 0; /* quiet, you */
500 while ((i = getopt_long(argc, argv,
501 short_opts, long_opts, &idx)) != -1) {
502 switch (i) {
503 /* program-specific options */
504 case 'v':
505 options.version = 1;
506 got_action = 1;
507 break;
508 case OPT_RO:
509 options.ro = 1;
510 got_action = 1;
511 break;
512 case OPT_RW:
513 options.rw = 1;
514 got_action = 1;
515 break;
516 case OPT_REBOOT:
517 options.reboot = 1;
518 got_action = 1;
519 break;
Bill Richardson41896c72018-01-18 21:43:12 -0800520 case OPT_ENABLE_RO:
521 options.enable_ro = 1;
522 got_action = 1;
523 break;
524 case OPT_ENABLE_RW:
525 options.enable_rw = 1;
526 got_action = 1;
527 break;
528 case OPT_CHANGE_PW:
529 options.change_pw = 1;
530 got_action = 1;
531 break;
532 case OPT_ERASE:
533 options.erase_code = (uint32_t)strtoul(optarg, &e, 0);
534 if (!*optarg || (e && *e)) {
535 Error("Invalid argument: \"%s\"\n", optarg);
536 errorcnt++;
537 }
538 got_action = 1;
539 break;
Andrew Scull43d72e12017-11-20 14:14:55 +0000540
541 /* generic options below */
542 case OPT_DEVICE:
543 options.device = optarg;
544 break;
Andrew Scull43d72e12017-11-20 14:14:55 +0000545 case 'h':
546 usage(this_prog);
547 return 0;
548 case 0:
549 break;
550 case '?':
551 if (optopt)
552 Error("Unrecognized options: -%c", optopt);
553 else
554 Error("Unrecognized options: %s",
555 argv[optind - 1]);
556 usage(this_prog);
557 break;
558 case ':':
559 Error("Missing argument to %s", argv[optind - 1]);
560 break;
561 default:
562 Error("Internal error at %s:%d", __FILE__, __LINE__);
563 exit(1);
564 }
565 }
566
567 if (errorcnt) {
568 goto out;
569 }
570
571 if (!got_action) {
572 usage(this_prog);
573 goto out;
574 }
575
576 if (options.ro || options.rw) {
577 if (optind < argc) {
578 /* Sets errorcnt on failure */
Bill Richardson76b40fe2018-01-22 10:28:24 -0800579 image = read_image_from_file(argv[optind++]);
Bill Richardson41896c72018-01-18 21:43:12 -0800580 if (errorcnt)
581 goto out;
Andrew Scull43d72e12017-11-20 14:14:55 +0000582 } else {
583 Error("An image file is required with --ro and --rw");
Bill Richardson41896c72018-01-18 21:43:12 -0800584 goto out;
Andrew Scull43d72e12017-11-20 14:14:55 +0000585 }
586 }
587
Bill Richardson41896c72018-01-18 21:43:12 -0800588 if (options.change_pw) {
589 /* one arg provided, maybe the old password isn't set */
590 if (optind < argc) {
591 passwd = argv[optind++];
592 } else {
593 Error("Need a new password at least."
594 " Use '' to clear it.");
595 goto out;
596 }
597 /* two args provided, use both old & new passwords */
598 if (optind < argc) {
599 old_passwd = passwd;
600 passwd = argv[optind++];
601 }
602 }
Andrew Scull43d72e12017-11-20 14:14:55 +0000603
Bill Richardson41896c72018-01-18 21:43:12 -0800604 if ((options.enable_ro || options.enable_rw) && !passwd) {
605 if (optind < argc)
606 passwd = argv[optind++];
607 else {
608 Error("Need a password to enable images. Use '' if none.");
609 goto out;
610 }
611 }
612
613 /* Okay, let's do it! */
614 (void) execute_commands(image, old_passwd, passwd);
Andrew Scull43d72e12017-11-20 14:14:55 +0000615 /* This is the last action, so fall through either way */
616
617out:
618 return !!errorcnt;
619}