diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2019-10-01 01:01:09 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2019-10-01 01:01:09 +0000 |
commit | d30532f387f68b9004fe170e63dd35d19aba28fc (patch) | |
tree | 44d800ea4bf57cbb81ce6c7a4dd42c358afe7184 | |
parent | dad46ffc7f0948fd771a87abfc2a7b70d4174d76 (diff) | |
parent | 16d4556329fc30de79387e396267c1fde7e47b5a (diff) | |
download | generic-android10-qpr1-b-s1-release.tar.gz |
Snap for 5909972 from 16d4556329fc30de79387e396267c1fde7e47b5a to qt-qpr1-releaseandroid-10.0.0_r29android-10.0.0_r28android-10.0.0_r27android-10.0.0_r26android-10.0.0_r25android-10.0.0_r24android-10.0.0_r23android-10.0.0_r22android-10.0.0_r21android-10.0.0_r20android-10.0.0_r19android-10.0.0_r18android-10.0.0_r16android-10.0.0_r15android10-qpr1-releaseandroid10-qpr1-d-releaseandroid10-qpr1-c-s1-releaseandroid10-qpr1-c-releaseandroid10-qpr1-b-s1-releaseandroid10-qpr1-b-release
Change-Id: I991d4c13ed57a89962f165a6a463f31df49c8f9a
-rw-r--r-- | .checkpatch.conf | 2 | ||||
-rw-r--r-- | Android.bp | 60 | ||||
-rw-r--r-- | citadel/updater/Android.bp | 38 | ||||
-rw-r--r-- | citadel/updater/BUILD | 12 | ||||
-rw-r--r-- | citadel/updater/NOTICE | 177 | ||||
-rw-r--r-- | citadel/updater/WORKSPACE | 1 | ||||
-rw-r--r-- | citadel/updater/updater.cpp | 1433 | ||||
-rw-r--r-- | libnos_datagram/Android.bp | 10 | ||||
-rw-r--r-- | libnos_datagram/citadel.c | 230 | ||||
-rw-r--r-- | nugget/include/citadel_events.h | 51 |
10 files changed, 333 insertions, 1681 deletions
diff --git a/.checkpatch.conf b/.checkpatch.conf index e8e9db6..f0db0f0 100644 --- a/.checkpatch.conf +++ b/.checkpatch.conf @@ -5,3 +5,5 @@ --ignore FILE_PATH_CHANGES --ignore GIT_COMMIT_ID --ignore SPLIT_STRING +--ignore CONST_STRUCT +--ignore LEADING_SPACE @@ -82,3 +82,63 @@ cc_library_static { "libbase", ], } + +// A special target to be statically linkeed into recovery which is a system +// (not vendor) component. +cc_library_static { + name: "libnos_citadel_for_recovery", + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + ], + srcs: [ + ":libnos_client", + "libnos_datagram/citadel.c", + ], + static_libs: [ + "libnos_for_recovery", + ], +} + +// Language and vendor related defaults +cc_defaults { + name: "nos_cc_defaults", + clang: true, + cflags: [ + "-pedantic", + "-Wall", + "-Wextra", + "-Werror", + "-Wno-zero-length-array", + ], + conlyflags: [ + "-std=c11", + ], + vendor: true, + owner: "google", +} + +// Defaults for components under the hw subdirectory +cc_defaults { + name: "nos_cc_hw_defaults", + defaults: ["nos_cc_defaults"], + relative_install_path: "hw", +} + +// Defaults for components shared between the host and device +cc_defaults { + name: "nos_cc_host_supported_defaults", + defaults: ["nos_cc_defaults"], + host_supported: true, +} + +cc_library { + name: "libnos_client_citadel", + srcs: [":libnos_client"], + defaults: [ + "libnos_client_defaults", + "nos_cc_defaults", + ], + shared_libs: ["libnos_datagram_citadel"], +} diff --git a/citadel/updater/Android.bp b/citadel/updater/Android.bp deleted file mode 100644 index 6b1a86b..0000000 --- a/citadel/updater/Android.bp +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -cc_binary { - name: "citadel_updater", - srcs: [ - "updater.cpp" - ], - defaults: ["nos_cc_hw_defaults"], - cflags: [ - // for openssl/sha.h - "-Wno-gnu-anonymous-struct", - "-Wno-nested-anon-types", - ], - header_libs: [ - "nos_headers", - ], - shared_libs: [ - "libcrypto", - "libnos", - "libnos_citadeld_proxy", - "libnos_client_citadel", - "libutils", - ], -} diff --git a/citadel/updater/BUILD b/citadel/updater/BUILD deleted file mode 100644 index 5daeac9..0000000 --- a/citadel/updater/BUILD +++ /dev/null @@ -1,12 +0,0 @@ -cc_library( - name = "libcitadel_updater", - srcs = [ - "updater.cpp", - ], - visibility = ["//visibility:public"], - deps = [ - "@boringssl//:ssl", - "@nugget_host_generic//:nos_headers", - "@nugget_host_generic_libnos//:libnos", - ], -) diff --git a/citadel/updater/NOTICE b/citadel/updater/NOTICE deleted file mode 100644 index f433b1a..0000000 --- a/citadel/updater/NOTICE +++ /dev/null @@ -1,177 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS diff --git a/citadel/updater/WORKSPACE b/citadel/updater/WORKSPACE deleted file mode 100644 index ea1afae..0000000 --- a/citadel/updater/WORKSPACE +++ /dev/null @@ -1 +0,0 @@ -workspace(name = "nugget_host_generic_citadel_updater") diff --git a/citadel/updater/updater.cpp b/citadel/updater/updater.cpp deleted file mode 100644 index 93c0383..0000000 --- a/citadel/updater/updater.cpp +++ /dev/null @@ -1,1433 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <memory> -#include <vector> - -#include <getopt.h> -#include <inttypes.h> -#include <openssl/sha.h> -#include <stdarg.h> -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> - -/* From Nugget OS */ -#include <application.h> -#include <app_nugget.h> -#include <citadel_events.h> -#include <flash_layout.h> -#include <signed_header.h> - -#include <nos/AppClient.h> -#include <nos/NuggetClient.h> -#ifdef ANDROID -#include <nos/CitadeldProxyClient.h> -#endif - -namespace { - -using nos::AppClient; -using nos::NuggetClient; -using nos::NuggetClientInterface; -#ifdef ANDROID -using nos::CitadeldProxyClient; -#endif - -enum hdr_section { - SEC_BOGUS = 0, - SEC_RO_A, - SEC_RO_B, - SEC_RW_A, - SEC_RW_B, -}; - -/* Global options */ -struct options_s { - /* actions to take */ - int version; - int long_version; - enum hdr_section section; - int file_version; - enum hdr_section file_section; - int id; - int repo_snapshot; - int stats; - int statsd; - int ro; - int rw; - int reboot; - int force_reset; - int enable_ro; - int enable_rw; - int change_pw; - uint32_t erase_code; - int ap_uart; - int selftest; - char **selftest_args; - /* generic connection options */ - const char *device; - int suzyq; - int board_id; - int event; - char **board_id_args; - int console; -} options; - -enum no_short_opts_for_these { - OPT_DEVICE = 1000, - OPT_ID, - OPT_REPO_SNAPSHOT, - OPT_STATS, - OPT_STATSD, - OPT_RO, - OPT_RW, - OPT_REBOOT, - OPT_FORCE_RESET, - OPT_ENABLE_RO, - OPT_ENABLE_RW, - OPT_CHANGE_PW, - OPT_ERASE, - OPT_AP_UART, - OPT_SELFTEST, - OPT_SUZYQ, - OPT_BOARD_ID, - OPT_EVENT, -}; - -const char *short_opts = ":hvlV:fF:c"; -const struct option long_opts[] = { - /* name hasarg *flag val */ - {"version", 0, NULL, 'v'}, - {"long_version", 0, NULL, 'l'}, - {"long-version", 0, NULL, 'l'}, - {"id", 0, NULL, OPT_ID}, - {"repo_snapshot", 0, NULL, OPT_REPO_SNAPSHOT}, - {"repo-snapshot", 0, NULL, OPT_REPO_SNAPSHOT}, - {"stats", 0, NULL, OPT_STATS}, - {"statsd", 0, NULL, OPT_STATSD}, - {"ro", 0, NULL, OPT_RO}, - {"rw", 0, NULL, OPT_RW}, - {"reboot", 0, NULL, OPT_REBOOT}, - {"force_reset", 0, NULL, OPT_FORCE_RESET}, - {"force-reset", 0, NULL, OPT_FORCE_RESET}, - {"enable_ro", 0, NULL, OPT_ENABLE_RO}, - {"enable-ro", 0, NULL, OPT_ENABLE_RO}, - {"enable_rw", 0, NULL, OPT_ENABLE_RW}, - {"enable-rw", 0, NULL, OPT_ENABLE_RW}, - {"change_pw", 0, NULL, OPT_CHANGE_PW}, - {"change-pw", 0, NULL, OPT_CHANGE_PW}, - {"erase", 1, NULL, OPT_ERASE}, - {"ap_uart", 0, NULL, OPT_AP_UART}, - {"ap-uart", 0, NULL, OPT_AP_UART}, - {"selftest", 0, NULL, OPT_SELFTEST}, - {"suzyq", 0, NULL, OPT_SUZYQ}, - {"board_id", 0, NULL, OPT_BOARD_ID}, - {"event", 0, NULL, OPT_EVENT}, -#ifndef ANDROID - {"device", 1, NULL, OPT_DEVICE}, -#endif - {"help", 0, NULL, 'h'}, - {NULL, 0, NULL, 0}, -}; - -void usage(const char *progname) -{ - fprintf(stderr, "\n"); - fprintf(stderr, - "Usage: %s [actions] [image.bin]\n" - "\n" - "Citadel firmware boots in two stages. The first stage\n" - "bootloader (aka \"RO\") is provided by the SOC hardware team\n" - "and seldom changes. The application image (\"RW\") is invoked\n" - "by the RO image. There are two copies (A/B) of each stage,\n" - "so that the active copy can be protected while the unused\n" - "copy may be updated. At boot, the newer (valid) copy of each\n" - "stage is selected.\n" - "\n" - "The Citadel image file is the same size of the internal\n" - "flash, and contains all four firmware components (RO_A,\n" - "RW_A, RO_B, RW_B) located at the correct offsets. Only the\n" - "inactive copy (A/B) of each stage (RO/RW) can be modified.\n" - "The tool will update the correct copies automatically.\n" - "\n" - "You must specify the actions to perform. With no actions,\n" - "this help message is displayed.\n" - "\n" - "Actions:\n" - "\n" - " -v, --version Display the running version\n" - " -l, --long_version Display the full version info\n" - " --id Display the Citadel device ID\n" - " --stats Display Low Power stats\n" - " --statsd Display Low Power stats cached by citadeld\n" - "\n" - " -V SECTION Show Citadel headers for RO_A | RO_B | RW_A | RW_B\n" - " -f Show image file version info\n" - " -F SECTION Show file headers for RO_A | RO_B | RW_A | RW_B\n" - " --repo_snapshot Show the repo sha1sums for the running image\n" - "\n" - " --rw Update RW firmware from the image file\n" - " --ro Update RO firmware from the image file\n" - " --enable_ro Mark new RO image as good (requires password)\n" - " --enable_rw Mark new RW image as good (requires password)\n" - " --reboot Tell Citadel to reboot\n" - " --force_reset Pulse Citadel's reset line\n" - "\n" - " --change_pw Change the update password\n" - "\n" - " --ap_uart Query the AP UART passthru setting\n" - " (It can only be set in the BIOS)\n" - "\n" - " --erase=CODE Erase all user secrets and reboot.\n" - " This skips all other actions.\n" - "\n" - " --selftest [ARGS] Run one or more selftests. With no ARGS, it runs\n" - " a default suite. This command will consume all\n" - " following args, so run it alone for best results.\n" - "\n" - " --suzyq [0|1] Set the SuzyQable detection setting\n" - "\n" - " --board_id [TYPE FLAG] Get/Set board ID values\n" - "\n" - " --event [NUM] Get NUM pending event records (default 1)\n" - "\n" -#ifndef ANDROID - "\n" - "Options:\n" - "\n" - " --device=SN Connect to the FDTI device with the given\n" - " serial number (try \"lsusb -v\"). A default\n" - " can be specified with the CITADEL_DEVICE\n" - " environment variable.\n" -#endif - "\n", - progname); -} - -/****************************************************************************/ -/* Handy stuff */ - -#ifndef MIN -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#endif - -int errorcnt; -void Error(const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - fprintf(stderr, "ERROR: "); - vfprintf(stderr, format, ap); - fprintf(stderr, "\n"); - va_end(ap); - - errorcnt++; -} - -/* Return true on APP_SUCESS, display error message if it's not */ -int is_app_success(uint32_t retval) -{ - if (retval == APP_SUCCESS) - return 1; - - errorcnt++; - - fprintf(stderr, "Error code 0x%x: ", retval); - switch (retval) { - case APP_ERROR_BOGUS_ARGS: - fprintf(stderr, "bogus args"); - break; - case APP_ERROR_INTERNAL: - fprintf(stderr, "app is being stupid"); - break; - case APP_ERROR_TOO_MUCH: - fprintf(stderr, "caller sent too much data"); - break; - case APP_ERROR_IO: - fprintf(stderr, "problem sending or receiving data"); - break; - case APP_ERROR_RPC: - fprintf(stderr, "problem during RPC communication"); - break; - case APP_ERROR_CHECKSUM: - fprintf(stderr, "checksum failed"); - break; - case APP_ERROR_BUSY: - fprintf(stderr, "the app is already working on a commnad"); - break; - case APP_ERROR_TIMEOUT: - fprintf(stderr, "the app took too long to respond"); - break; - default: - if (retval >= APP_SPECIFIC_ERROR && - retval < APP_LINE_NUMBER_BASE) { - fprintf(stderr, "app-specific error #%d", - retval - APP_SPECIFIC_ERROR); - } else if (retval >= APP_LINE_NUMBER_BASE) { - fprintf(stderr, "error at line %d", - retval - APP_LINE_NUMBER_BASE); - } else { - fprintf(stderr, "unknown"); - } - } - fprintf(stderr, "\n"); - - return 0; -} - -/****************************************************************************/ - -std::vector<uint8_t> read_image_from_file(const char *name) -{ - FILE *fp; - struct stat st; - - fp = fopen(name, "rb"); - if (!fp) { - perror("fopen"); - Error("Can't open file %s", name); - return {}; - } - - if (fstat(fileno(fp), &st)) { - perror("fstat"); - Error("Can't fstat file %s", name); - fclose(fp); - return {}; - } - - if (st.st_size != CHIP_FLASH_SIZE) { - Error("The firmware image must be exactly %d bytes", - CHIP_FLASH_SIZE); - fclose(fp); - return {}; - } - - std::vector<uint8_t> buf(st.st_size); - if (1 != fread(buf.data(), st.st_size, 1, fp)) { - perror("fread"); - Error("Can't read %zd bytes", st.st_size); - fclose(fp); - return {}; - } - - fclose(fp); - buf.resize(st.st_size); - - return buf; -} - -uint32_t compute_digest(void *ptr, size_t len) -{ - SHA_CTX ctx; - uint8_t digest[SHA_DIGEST_LENGTH]; - uint32_t retval; - - SHA1_Init(&ctx); - SHA1_Update(&ctx, ptr, len); - SHA1_Final(digest, &ctx); - - memcpy(&retval, digest, sizeof(retval)); - return retval; -} - -uint32_t compute_fb_digest(struct nugget_app_flash_block *blk) -{ - uint8_t *start_here = ((uint8_t *)blk) + - offsetof(struct nugget_app_flash_block, offset); - size_t size_to_hash = sizeof(*blk) - - offsetof(struct nugget_app_flash_block, offset); - - return compute_digest(start_here, size_to_hash); -} - -uint32_t try_update(AppClient &app, const std::vector<uint8_t> &image, - uint32_t offset, uint32_t imagesize) -{ - uint32_t stop = offset + imagesize; - uint32_t rv; - - printf("Updating image from 0x%05x to 0x%05x, size 0x%05x\n", - CHIP_FLASH_BASE + offset, CHIP_FLASH_BASE + stop, imagesize); - - for (; offset < stop; offset += CHIP_FLASH_BANK_SIZE) { - int retries = 3; - std::vector<uint8_t> data(sizeof(struct nugget_app_flash_block)); - struct nugget_app_flash_block *fb = - (struct nugget_app_flash_block*)data.data(); - - fb->offset = offset; - memcpy(fb->payload, image.data() + offset, CHIP_FLASH_BANK_SIZE); - fb->block_digest = compute_fb_digest(fb); - - printf("writing 0x%05x / 0x%05x", - CHIP_FLASH_BASE + offset, CHIP_FLASH_BASE + stop); - do { - rv = app.Call(NUGGET_PARAM_FLASH_BLOCK, data, nullptr); - if (rv == NUGGET_ERROR_RETRY) - printf(" retrying"); - } while (rv == NUGGET_ERROR_RETRY && retries--); - if (rv) { - if (rv == NUGGET_ERROR_LOCKED) - printf(" locked\n"); - else - printf(" fail %d\n", rv); - break; - } - printf(" ok\n"); - } - - return rv; -} - -uint32_t do_update(AppClient &app, const std::vector<uint8_t> &image, - uint32_t offset_A, uint32_t offset_B) -{ - struct SignedHeader *hdr; - uint32_t rv_A, rv_B; - - /* Try image A first */ - hdr = (struct SignedHeader *)(image.data() + offset_A); - rv_A = try_update(app, image, offset_A, hdr->image_size); - - /* If that worked, we're done */ - if (rv_A == APP_SUCCESS) { - return rv_A; - } - - /* Else try image B */ - hdr = (struct SignedHeader *)(image.data() + offset_B); - rv_B = try_update(app, image, offset_B, hdr->image_size); - - return rv_B; -} - -uint32_t do_version(AppClient &app) -{ - uint32_t retval; - std::vector<uint8_t> buffer; - buffer.reserve(512); - - retval = app.Call(NUGGET_PARAM_VERSION, buffer, &buffer); - - if (is_app_success(retval)) { - printf("%.*s\n", (int) buffer.size(), buffer.data()); - } - - return retval; -} - -uint32_t do_id(AppClient &app) -{ - uint32_t retval; - std::vector<uint8_t> buffer; - buffer.reserve(32); - - retval = app.Call(NUGGET_PARAM_DEVICE_ID, buffer, &buffer); - - if (is_app_success(retval)) { - printf("%.*s\n", (int) buffer.size(), buffer.data()); - } - - return retval; -} - -uint32_t do_long_version(AppClient &app) -{ - uint32_t retval; - std::vector<uint8_t> buffer; - buffer.reserve(1024); - - retval = app.Call(NUGGET_PARAM_LONG_VERSION, buffer, &buffer); - - if (is_app_success(retval)) { - printf("%.*s\n", (int)buffer.size(), buffer.data()); - } - - return retval; -} - -static enum hdr_section parse_section(const char *str) -{ - bool is_ro, is_a; - - // matching this: /r?[ow]_?[ab]/i - - if (tolower(*str) == 'r') { - str++; - } - - if (tolower(*str) == 'o') { - is_ro = true; - } else if (tolower(*str) == 'w') { - is_ro = false; - } else { - Error("Invalid section \"%s\"", str); - return SEC_BOGUS; - } - str++; - - if (*str == '_') { - str++; - } - - if (tolower(*str) == 'a') { - is_a = true; - } else if (tolower(*str) == 'b') { - is_a = false; - } else { - Error("Invalid section \"%s\"", str); - return SEC_BOGUS; - } - - if (is_ro) { - return is_a ? SEC_RO_A : SEC_RO_B; - } - - return is_a ? SEC_RW_A : SEC_RW_B; -} - -static void show_header(const uint8_t *ptr) -{ - const struct SignedHeader *hdr; - - hdr = reinterpret_cast<const struct SignedHeader*>(ptr); - hdr->print(); -} - -#define CROS_EC_VERSION_COOKIE1 0xce112233 -#define CROS_EC_VERSION_COOKIE2 0xce445566 - -// The start of the RW sections looks like this -struct compiled_version_struct { - // The header comes first - const struct SignedHeader hdr; - // The the vector table. Citadel has 239 entries - uint32_t vectors[239]; - // A magic number to be sure we're looking at the right thing - uint32_t cookie1; - // Then the short version string - char version[32]; - // And another magic number - uint32_t cookie2; -}; - -static void show_ro_string(const char *name, const uint8_t *ptr) -{ - const struct SignedHeader *hdr; - - hdr = reinterpret_cast<const struct SignedHeader*>(ptr); - printf("%s: %d.%d.%d/%08x %s\n", name, - hdr->epoch_, hdr->major_, hdr->minor_, be32toh(hdr->img_chk_), - hdr->magic == SIGNED_HEADER_MAGIC_CITADEL ? "ok" : "--"); -} - -static void show_rw_string(const char *name, const uint8_t *ptr) -{ - const struct compiled_version_struct *v; - v = reinterpret_cast<const struct compiled_version_struct*>(ptr); - - if (v->cookie1 == CROS_EC_VERSION_COOKIE1 && - v->cookie2 == CROS_EC_VERSION_COOKIE2 && - (v->hdr.magic == SIGNED_HEADER_MAGIC_HAVEN || - v->hdr.magic == SIGNED_HEADER_MAGIC_CITADEL)) { - printf("%s: %d.%d.%d/%s %s\n", name, - v->hdr.epoch_, v->hdr.major_, v->hdr.minor_, v->version, - v->hdr.magic == SIGNED_HEADER_MAGIC_CITADEL ? "ok" : "--"); - } else { - printf("<invalid>\n"); - } -} - -uint32_t do_section(AppClient &app __attribute__((unused))) -{ - uint16_t param; - - switch (options.section) { - case SEC_RO_A: - param = NUGGET_PARAM_HEADER_RO_A; - break; - case SEC_RO_B: - param = NUGGET_PARAM_HEADER_RO_B; - break; - case SEC_RW_A: - param = NUGGET_PARAM_HEADER_RW_A; - break; - case SEC_RW_B: - param = NUGGET_PARAM_HEADER_RW_B; - break; - default: - return 1; - } - - uint32_t retval; - std::vector<uint8_t> buffer; - buffer.reserve(sizeof(SignedHeader)); - - retval = app.Call(param, buffer, &buffer); - - if (is_app_success(retval)) { - show_header(buffer.data()); - } - - return retval; -} - -uint32_t do_file_version(const std::vector<uint8_t> &image) -{ - show_ro_string("RO_A", image.data() + CHIP_RO_A_MEM_OFF); - show_ro_string("RO_B", image.data() + CHIP_RO_B_MEM_OFF); - show_rw_string("RW_A", image.data() + CHIP_RW_A_MEM_OFF); - show_rw_string("RW_B", image.data() + CHIP_RW_B_MEM_OFF); - return 0; -} - -uint32_t do_file_section(const std::vector<uint8_t> &image) -{ - switch (options.file_section) { - case SEC_RO_A: - show_header(image.data() + CHIP_RO_A_MEM_OFF); - break; - case SEC_RO_B: - show_header(image.data() + CHIP_RO_B_MEM_OFF); - break; - case SEC_RW_A: - show_header(image.data() + CHIP_RW_A_MEM_OFF); - break; - case SEC_RW_B: - show_header(image.data() + CHIP_RW_B_MEM_OFF); - break; - default: - return 1; - } - - return 0; -} - -uint32_t do_repo_snapshot(AppClient &app) -{ - uint32_t retval; - std::vector<uint8_t> buffer; - buffer.reserve(2048); - - retval = app.Call(NUGGET_PARAM_REPO_SNAPSHOT, buffer, &buffer); - - if (is_app_success(retval)) { - printf("%.*s\n", (int)buffer.size(), buffer.data()); - } - - return retval; -} - -uint32_t do_console(AppClient &app, int argc, char *argv[]) -{ - std::vector<uint8_t> buffer; - uint32_t rv; - size_t got; - - if (options.console < argc) { - char *s = argv[options.console]; - char c; - do { - c = *s++; - buffer.push_back(c); - } while (c); - } - - do { - buffer.reserve(4096); - rv = app.Call(NUGGET_PARAM_CONSOLE, buffer, &buffer); - got = buffer.size(); - - if (is_app_success(rv)){ - buffer.push_back('\0'); - printf("%s", buffer.data()); - } - - buffer.resize(0); - } while (rv == APP_SUCCESS && got > 0); - - return rv; -} - -static void print_stats(const struct nugget_app_low_power_stats *s) -{ - printf("hard_reset_count %" PRIu64 "\n", s->hard_reset_count); - printf("time_since_hard_reset %" PRIu64 "\n", - s->time_since_hard_reset); - printf("wake_count %" PRIu64 "\n", s->wake_count); - printf("time_at_last_wake %" PRIu64 "\n", s->time_at_last_wake); - printf("time_spent_awake %" PRIu64 "\n", s->time_spent_awake); - printf("deep_sleep_count %" PRIu64 "\n", s->deep_sleep_count); - printf("time_at_last_deep_sleep %" PRIu64 "\n", - s->time_at_last_deep_sleep); - printf("time_spent_in_deep_sleep %" PRIu64 "\n", - s->time_spent_in_deep_sleep); - if (s->time_at_ap_reset == UINT64_MAX) - printf("time_at_ap_reset 0x%" PRIx64 "\n", s->time_at_ap_reset); - else - printf("time_at_ap_reset %" PRIu64 "\n", s->time_at_ap_reset); - if (s->time_at_ap_bootloader_done == UINT64_MAX) - printf("time_at_ap_bootloader_done 0x%" PRIx64 "\n", - s->time_at_ap_bootloader_done); - else - printf("time_at_ap_bootloader_done %" PRIu64 "\n", - s->time_at_ap_bootloader_done); -} - -uint32_t do_stats(AppClient &app) -{ - struct nugget_app_low_power_stats stats; - std::vector<uint8_t> buffer; - uint32_t retval; - - buffer.reserve(sizeof(stats)); - retval = app.Call(NUGGET_PARAM_GET_LOW_POWER_STATS, buffer, &buffer); - - if (is_app_success(retval)) { - if (buffer.size() < sizeof(stats)) { // old firmware? - fprintf(stderr, "# only got %zd / %zd bytes back\n", - buffer.size(), sizeof(stats)); - memset(&stats, 0, sizeof(stats)); - } - memcpy(&stats, buffer.data(), std::min(sizeof(stats), buffer.size())); - print_stats(&stats); - } - - return retval; -} - -#ifdef ANDROID -uint32_t do_statsd(CitadeldProxyClient &client) -{ - struct nugget_app_low_power_stats stats; - std::vector<uint8_t> buffer; - - buffer.reserve(sizeof(stats)); - ::android::binder::Status s = client.Citadeld().getCachedStats(&buffer); - - if (s.isOk()) { - if (buffer.size() < sizeof(stats)) { // old citadeld? - fprintf(stderr, "# only got %zd / %zd bytes back\n", - buffer.size(), sizeof(stats)); - memset(&stats, 0, sizeof(stats)); - } - memcpy(&stats, buffer.data(), std::min(sizeof(stats), buffer.size())); - print_stats(&stats); - } else { - printf("ERROR: binder exception %d\n", s.exceptionCode()); - return APP_ERROR_IO; - } - - return 0; -} -#else -uint32_t do_statsd(NuggetClient &client) -{ - Error("citadeld isn't attached to this interface"); - return APP_ERROR_BOGUS_ARGS; -} -#endif - -uint32_t do_reboot(AppClient &app) -{ - uint32_t retval; - std::vector<uint8_t> ignored = {1}; // older images need this - - retval = app.Call(NUGGET_PARAM_REBOOT, ignored, nullptr); - - if (is_app_success(retval)) { - printf("Citadel reboot requested\n"); - } - - return retval; -} - -static uint32_t do_change_pw(AppClient &app, - const char *old_pw, const char *new_pw) -{ - std::vector<uint8_t> data(sizeof(struct nugget_app_change_update_password)); - struct nugget_app_change_update_password *s = - (struct nugget_app_change_update_password*)data.data(); - - - memset(s, 0xff, sizeof(*s)); - if (old_pw && *old_pw) { - int len = strlen(old_pw); - memcpy(&s->old_password.password, old_pw, len); - s->old_password.digest = - compute_digest(&s->old_password.password, - sizeof(s->old_password.password)); - } - - if (new_pw && *new_pw) { - int len = strlen(new_pw); - memcpy(&s->new_password.password, new_pw, len); - s->new_password.digest = - compute_digest(&s->new_password.password, - sizeof(s->new_password.password)); - } - - uint32_t rv = app.Call(NUGGET_PARAM_CHANGE_UPDATE_PASSWORD, data, nullptr); - - if (is_app_success(rv)) - printf("Password changed\n"); - - return rv; -} - -static uint32_t do_enable(AppClient &app, const char *pw) -{ - std::vector<uint8_t> data(sizeof(struct nugget_app_enable_update)); - struct nugget_app_enable_update *s = - (struct nugget_app_enable_update*)data.data(); - std::vector<uint8_t> reply; - - memset(&s->password, 0xff, sizeof(s->password)); - if (pw && *pw) { - int len = strlen(pw); - memcpy(&s->password.password, pw, len); - s->password.digest = - compute_digest(&s->password.password, - sizeof(s->password.password)); - } - s->which_headers = options.enable_ro ? NUGGET_ENABLE_HEADER_RO : 0; - s->which_headers |= options.enable_rw ? NUGGET_ENABLE_HEADER_RW : 0; - - reply.reserve(1); - uint32_t rv = app.Call(NUGGET_PARAM_ENABLE_UPDATE, data, &reply); - - if (is_app_success(rv)) - /* Reply byte is true only if header was CHANGED to valid */ - printf("Update %s enabled\n", reply[0] ? "changed to" : "is already"); - - return rv; -} - -static uint32_t do_ap_uart(AppClient &app) -{ - std::vector<uint8_t> buffer; - buffer.reserve(1); - - static const char * const cfgstr[] = { - "disabled", "USB", "enabled", "SSC", "Citadel", - }; - static_assert(sizeof(cfgstr)/sizeof(cfgstr[0]) == NUGGET_AP_UART_NUM_CFGS, - "Bad size of constant array"); - - uint32_t rv = app.Call(NUGGET_PARAM_AP_UART_PASSTHRU, buffer, &buffer); - - if (is_app_success(rv)) - printf("Current AP UART setting is %s\n", cfgstr[buffer[0]]); - - return rv; -} - -static uint32_t do_suzyq(AppClient &app, int argc, char *argv[]) -{ - int i, j; - std::vector<uint8_t> buffer; - - for (i = options.suzyq; i < argc; i++) { - for (j = 0; argv[i][j]; j++) { - buffer.push_back(strtol(argv[i], NULL, 10)); - } - } - - buffer.reserve(1); - uint32_t rv = app.Call(NUGGET_PARAM_RDD_CFG, buffer, &buffer); - - if (is_app_success(rv)) - printf("Current SuzyQable detection setting is %d\n", buffer[0]); - - return rv; -} - -static void parse_hex_value(uint32_t *val, const char *str) -{ - char *e = 0; - uint32_t tmp = strtoul(str, &e, 16); - - if (e && *e) - Error("Invalid arg: \"%s\"", str); - else - *val = tmp; -} - -static void show_board_id(const struct nugget_app_board_id *id) -{ - printf("0x%08x 0x%08x 0x%08x # ", id->type, id->flag, id->inv); - - if (id->type == 0xffffffff && id->flag == 0xffffffff && - id->inv == 0xffffffff) { - printf("unset\n"); - return; - } - - if (id->type ^ ~id->inv) { - printf("corrupted\n"); - return; - } - - printf("%s, ", id->flag & 0x80 ? "MP" : "Pre-MP"); - switch (id->flag & 0x7f) { - case 0x7f: - printf("DEVBOARD\n"); - break; - case 0x7e: - printf("Proto1\n"); - break; - case 0x7c: - printf("Proto2+\n"); - break; - case 0x78: - printf("EVT1\n"); - break; - case 0x70: - printf("EVT2+\n"); - break; - case 0x60: - printf("DVT1\n"); - break; - case 0x40: - printf("DVT2+\n"); - break; - case 0x00: - printf("PVT/MP\n"); - break; - default: - printf("(unknown)\n"); - break; - } -} - -static uint32_t do_board_id(AppClient &app, int argc, char *argv[]) -{ - uint32_t rv; - std::vector<uint8_t> request; - std::vector<uint8_t> response(sizeof(struct nugget_app_board_id)); - struct nugget_app_board_id board_id; - char answer = 0; - - // User must input both board_type and board_flag to make a set request - if (argc - options.board_id >= 2) { - uint32_t tmp = 0; - - parse_hex_value(&tmp, argv[options.board_id]); - board_id.type = tmp; - - parse_hex_value(&tmp, argv[options.board_id + 1]); - board_id.flag = tmp; - - // optional third arg must equal ~type to avoid confirmation - if (argc - options.board_id > 2) { - parse_hex_value(&tmp, argv[options.board_id + 2]); - board_id.inv = tmp; - } else { - board_id.inv = ~board_id.type; - } - - // Any problems parsing args? - if (errorcnt) - return errorcnt; - - // Confirm unless correct type_inv arg is given - if (argc - options.board_id == 2 || board_id.type ^ ~board_id.inv) { - printf("\nWriting Board ID: "); - show_board_id(&board_id); - printf("\nWARNING: Setting board-id is irreversible!\n"); - printf("Are you sure? (y/n) "); - fflush(stdout); - scanf(" %c", &answer); - if (answer != 'y'){ - Error("Operation cancelled"); - return errorcnt; - } - board_id.inv = ~board_id.type; - printf("\n"); - } - - request.resize(sizeof(board_id)); - memcpy(request.data(), &board_id, sizeof(board_id)); - } - - rv = app.Call(NUGGET_PARAM_BOARD_ID, request, &response); - - if (is_app_success(rv)) { - memcpy(&board_id, response.data(), sizeof(board_id)); - show_board_id(&board_id); - } - - return rv; -} - -static uint32_t do_event(AppClient &app, int argc, char *argv[]) -{ - uint32_t rv; - int i, num = 1; - - if (options.event < argc) { - num = atoi(argv[options.event]); - } - - for (i = 0; i < num; i++) { - struct event_record evt; - std::vector<uint8_t> buffer; - buffer.reserve(sizeof(evt)); - - rv = app.Call(NUGGET_PARAM_GET_EVENT_RECORD, buffer, &buffer); - - if (!is_app_success(rv)) { - // That check also displays any errors - break; - } - - if (buffer.size() == 0) { - printf("-- no event_records --\n"); - continue; - } - - if (buffer.size() != sizeof(evt)) { - fprintf(stderr, "Error: expected %zd bytes, got %zd instead\n", - sizeof(evt), buffer.size()); - rv = 1; - break; - } - - /* We got an event, let's show it */ - memcpy(&evt, buffer.data(), sizeof(evt)); - uint64_t secs = evt.uptime_usecs / 1000000UL; - uint64_t usecs = evt.uptime_usecs - (secs * 1000000UL); - printf("event record %" PRIu64 "/%" PRIu64 ".%06" PRIu64 ": ", - evt.reset_count, secs, usecs); - printf("%d 0x%08x 0x%08x 0x%08x\n", evt.id, - evt.u.raw.w[0], evt.u.raw.w[1], evt.u.raw.w[2]); - } - - return rv; -} - -static uint32_t do_erase(AppClient &app) -{ - std::vector<uint8_t> data(sizeof(uint32_t)); - memcpy(data.data(), &options.erase_code, data.size()); - - uint32_t rv = app.Call(NUGGET_PARAM_NUKE_FROM_ORBIT, data, nullptr); - - if (is_app_success(rv)) - printf("Citadel erase and reboot requested\n"); - - return rv; -} - -#define MAX_SELFTEST_REPLY_LEN 4096 -static uint32_t do_selftest(AppClient &app, int argc, char *argv[]) -{ - int i, j; - uint32_t rv; - std::vector<uint8_t> data; - - /* Copy all the args to send, including their terminating '\0' */ - for (i = options.selftest; i < argc; i++) { - for (j = 0; argv[i][j]; j++) { - data.push_back(argv[i][j]); - } - data.push_back('\0'); - } - - /* Send args, get reply */ - data.reserve(MAX_SELFTEST_REPLY_LEN); - rv = app.Call(NUGGET_PARAM_SELFTEST, data, &data); - if (is_app_success(rv)) { - /* Make SURE it's null-terminated */ - size_t len = data.size(); - if (len) { - data[len - 1] = '\0'; - printf("%s\n", data.data()); - } - } - return rv; -} - -// This is currently device-specific, but could be abstracted further -#ifdef ANDROID -static uint32_t do_force_reset(CitadeldProxyClient &client) -{ - bool b = false; - - return !client.Citadeld().reset(&b).isOk(); -} -#else -static uint32_t do_force_reset(NuggetClient &client) -{ - struct nos_device *d = client.Device(); - - return d->ops.reset(d->ctx); -} -#endif - -int execute_commands(const std::vector<uint8_t> &image, - const char *old_passwd, const char *passwd, - int argc, char *argv[]) -{ -#ifdef ANDROID - CitadeldProxyClient client; -#else - NuggetClient client(options.device ? options.device : ""); -#endif - - client.Open(); - if (!client.IsOpen()) { - Error("Unable to connect"); - return 1; - } - AppClient app(client, APP_ID_NUGGET); - - /* Try all requested actions in reasonable order, bail out on error */ - - if (options.ap_uart && - do_ap_uart(app) != APP_SUCCESS) { - return 1; - } - - if (options.erase_code) { /* zero doesn't count */ - /* whether we succeed or not, only do this */ - return do_erase(app); - } - - if (options.version && - do_version(app) != APP_SUCCESS) { - return 2; - } - - if (options.long_version && - do_long_version(app) != APP_SUCCESS) { - return 2; - } - - if (options.section && - do_section(app) != APP_SUCCESS) { - return 2; - } - - if (options.file_version && - do_file_version(image) != APP_SUCCESS) { - return 2; - } - - if (options.file_section && - do_file_section(image) != APP_SUCCESS) { - return 2; - } - - if (options.id && - do_id(app) != APP_SUCCESS) { - return 2; - } - - if (options.stats && - do_stats(app) != APP_SUCCESS) { - return 2; - } - - if (options.statsd && - do_statsd(client) != APP_SUCCESS) { - return 2; - } - - if (options.repo_snapshot && - do_repo_snapshot(app) != APP_SUCCESS) { - return 2; - } - if (options.rw && - do_update(app, image, - CHIP_RW_A_MEM_OFF, CHIP_RW_B_MEM_OFF) != APP_SUCCESS) { - return 3; - } - - if (options.ro && - do_update(app, image, - CHIP_RO_A_MEM_OFF, CHIP_RO_B_MEM_OFF) != APP_SUCCESS) { - return 4; - } - - if (options.change_pw && - do_change_pw(app, old_passwd, passwd) != APP_SUCCESS) - return 5; - - if ((options.enable_ro || options.enable_rw) && - do_enable(app, passwd) != APP_SUCCESS) - return 6; - - if (options.reboot && - do_reboot(app) != APP_SUCCESS) { - return 7; - } - - if (options.selftest && - do_selftest(app, argc, argv) != APP_SUCCESS) { - return 1; - } - - if (options.force_reset && - do_force_reset(client) != APP_SUCCESS) { - return 1; - } - - if (options.suzyq && - do_suzyq(app, argc, argv) != APP_SUCCESS) { - return 1; - } - - if (options.board_id && - do_board_id(app, argc, argv) != APP_SUCCESS) { - return 1; - } - - if (options.console && - do_console(app, argc, argv) != APP_SUCCESS) { - return 1; - } - - if (options.event && - do_event(app, argc, argv) != APP_SUCCESS) { - return 1; - } - - return 0; -} - -} // namespace - -int main(int argc, char *argv[]) -{ - int i; - int idx = 0; - char *this_prog; - char *old_passwd = 0; - char *passwd = 0; - std::vector<uint8_t> image; - int got_action = 0; - char *e = 0; - int need_file = 0; - int status = 0; - - this_prog= strrchr(argv[0], '/'); - if (this_prog) { - this_prog++; - } else { - this_prog = argv[0]; - } - -#ifndef ANDROID - options.device = secure_getenv("CITADEL_DEVICE"); - if (options.device) - fprintf(stderr, "-- $CITADEL_DEVICE=%s --\n", options.device); -#endif - - opterr = 0; /* quiet, you */ - while ((i = getopt_long(argc, argv, - short_opts, long_opts, &idx)) != -1) { - switch (i) { - /* program-specific options */ - case 'v': - options.version = 1; - got_action = 1; - break; - case 'l': - options.long_version = 1; - got_action = 1; - break; - case 'V': - options.section = parse_section(optarg); - got_action = 1; - break; - case 'c': - options.console = optind; - got_action = 1; - break; - case 'f': - options.file_version = 1; - need_file = 1; - got_action = 1; - break; - case 'F': - options.file_section = parse_section(optarg); - need_file = 1; - got_action = 1; - break; - case OPT_ID: - options.id = 1; - got_action = 1; - break; - case OPT_REPO_SNAPSHOT: - options.repo_snapshot = 1; - got_action = 1; - break; - case OPT_STATS: - options.stats = 1; - got_action = 1; - break; - case OPT_STATSD: - options.statsd = 1; - got_action = 1; - break; - case OPT_RO: - options.ro = 1; - need_file = 1; - got_action = 1; - break; - case OPT_RW: - options.rw = 1; - need_file = 1; - got_action = 1; - break; - case OPT_REBOOT: - options.reboot = 1; - got_action = 1; - break; - case OPT_FORCE_RESET: - options.force_reset = 1; - got_action = 1; - break; - case OPT_ENABLE_RO: - options.enable_ro = 1; - got_action = 1; - break; - case OPT_ENABLE_RW: - options.enable_rw = 1; - got_action = 1; - break; - case OPT_CHANGE_PW: - options.change_pw = 1; - got_action = 1; - break; - case OPT_ERASE: - options.erase_code = (uint32_t)strtoul(optarg, &e, 0); - if (!*optarg || (e && *e)) { - Error("Invalid argument: \"%s\"\n", optarg); - } - got_action = 1; - break; - case OPT_AP_UART: - options.ap_uart = 1; - got_action = 1; - break; - case OPT_SUZYQ: - options.suzyq = optind; - got_action = 1; - break; - case OPT_BOARD_ID: - options.board_id = optind; - options.board_id_args = argv; - got_action = 1; - break; - case OPT_EVENT: - options.event = optind; - got_action = 1; - break; - case OPT_SELFTEST: - options.selftest = optind; - options.selftest_args = argv; - got_action = 1; - break; - - /* generic options below */ - case OPT_DEVICE: - options.device = optarg; - break; - case 'h': - usage(this_prog); - return 0; - case 0: - break; - case '?': - if (optopt) - Error("Unrecognized options: -%c", optopt); - else - Error("Unrecognized options: %s", - argv[optind - 1]); - usage(this_prog); - break; - case ':': - Error("Missing argument to %s", argv[optind - 1]); - break; - default: - Error("Internal error at %s:%d", __FILE__, __LINE__); - exit(1); - } - } - - if (errorcnt) { - goto out; - } - - if (!got_action) { - usage(this_prog); - goto out; - } - - if (need_file) { - if (optind < argc) { - /* Sets errorcnt on failure */ - image = read_image_from_file(argv[optind++]); - if (errorcnt) - goto out; - } else { - Error("Missing required image file"); - goto out; - } - } - - if (options.change_pw) { - /* one arg provided, maybe the old password isn't set */ - if (optind < argc) { - passwd = argv[optind++]; - } else { - Error("Need a new password at least. Use '' to clear it."); - goto out; - } - /* two args provided, use both old & new passwords */ - if (optind < argc) { - old_passwd = passwd; - passwd = argv[optind++]; - } - } - - if ((options.enable_ro || options.enable_rw) && !passwd) { - if (optind < argc) - passwd = argv[optind++]; - else { - Error("Need a password to enable images. Use '' if none."); - goto out; - } - } - - /* Okay, let's do it! */ - status = execute_commands(image, old_passwd, passwd, argc, argv); - if (status != 0) { - Error("execute_command failed(%d)!", status); - } - /* This is the last action, so fall through either way */ - -out: - return !!errorcnt; -} diff --git a/libnos_datagram/Android.bp b/libnos_datagram/Android.bp index 15d49d3..2a2b659 100644 --- a/libnos_datagram/Android.bp +++ b/libnos_datagram/Android.bp @@ -19,3 +19,13 @@ cc_library { defaults: ["nos_cc_host_supported_defaults"], export_include_dirs: ["include"], } + +cc_library { + name: "libnos_datagram_citadel", + srcs: ["citadel.c"], + defaults: ["nos_cc_defaults"], + shared_libs: [ + "liblog", + "libnos_datagram", + ], +} diff --git a/libnos_datagram/citadel.c b/libnos_datagram/citadel.c new file mode 100644 index 0000000..026224d --- /dev/null +++ b/libnos_datagram/citadel.c @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "libnos_datagram" +#include <log/log.h> +#include <nos/device.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <linux/types.h> +#include <poll.h> +#include <pthread.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <unistd.h> + +/*****************************************************************************/ +/* Ideally, this should be in <linux/citadel.h> */ +#define CITADEL_IOC_MAGIC 'c' +struct citadel_ioc_tpm_datagram { + __u64 buf; + __u32 len; + __u32 command; +}; +#define CITADEL_IOC_TPM_DATAGRAM _IOW(CITADEL_IOC_MAGIC, 1, \ + struct citadel_ioc_tpm_datagram) +#define CITADEL_IOC_RESET _IO(CITADEL_IOC_MAGIC, 2) +/*****************************************************************************/ + +#define DEV_CITADEL "/dev/citadel0" + +static pthread_mutex_t in_buf_mutex = PTHREAD_MUTEX_INITIALIZER; +static uint8_t in_buf[MAX_DEVICE_TRANSFER]; +static int read_datagram(void *ctx, uint32_t command, uint8_t *buf, uint32_t len) { + struct citadel_ioc_tpm_datagram dg = { + .buf = (unsigned long)in_buf, + .len = len, + .command = command, + }; + int ret; + int fd; + + if (!ctx) { + + ALOGE("%s: invalid (NULL) device\n", __func__); + return -ENODEV; + } + fd = *(int *)ctx; + if (fd < 0) { + ALOGE("%s: invalid device\n", __func__); + return -ENODEV; + } + + if (len > MAX_DEVICE_TRANSFER) { + ALOGE("%s: invalid len (%d > %d)\n", __func__, + len, MAX_DEVICE_TRANSFER); + return -E2BIG; + } + + /* Lock the in buffer while it is used for this transaction */ + if (pthread_mutex_lock(&in_buf_mutex) != 0) { + ALOGE("%s: failed to lock in_buf_mutex: %s", __func__, strerror(errno)); + return -errno; + } + + ret = ioctl(fd, CITADEL_IOC_TPM_DATAGRAM, &dg); + if (ret < 0) { + ALOGE("can't send spi message: %s", strerror(errno)); + ret = -errno; + goto out; + } + + memcpy(buf, in_buf, len); + +out: + if (pthread_mutex_unlock(&in_buf_mutex) != 0) { + ALOGE("%s: failed to unlock in_buf_mutex: %s", __func__, strerror(errno)); + ret = -errno; + } + return ret; +} + +static pthread_mutex_t out_buf_mutex = PTHREAD_MUTEX_INITIALIZER; +static uint8_t out_buf[MAX_DEVICE_TRANSFER]; +static int write_datagram(void *ctx, uint32_t command, const uint8_t *buf, uint32_t len) { + struct citadel_ioc_tpm_datagram dg = { + .buf = (unsigned long)out_buf, + .len = len, + .command = command, + }; + int ret; + int fd; + + if (!ctx) { + ALOGE("%s: invalid (NULL) device\n", __func__); + return -ENODEV; + } + fd = *(int *)ctx; + if (fd < 0) { + ALOGE("%s: invalid device\n", __func__); + return -ENODEV; + } + + if (len > MAX_DEVICE_TRANSFER) { + ALOGE("%s: invalid len (%d > %d)\n", __func__, len, + MAX_DEVICE_TRANSFER); + return -E2BIG; + } + + /* Lock the out buffer while it is used for this transaction */ + if (pthread_mutex_lock(&out_buf_mutex) != 0) { + ALOGE("%s: failed to lock out_buf_mutex: %s", __func__, strerror(errno)); + return -errno; + } + + memcpy(out_buf, buf, len); + + ret = ioctl(fd, CITADEL_IOC_TPM_DATAGRAM, &dg); + if (ret < 0) { + ALOGE("can't send spi message: %s", strerror(errno)); + ret = -errno; + goto out; + } + +out: + if (pthread_mutex_unlock(&out_buf_mutex) != 0) { + ALOGE("%s: failed to unlock out_buf_mutex: %s", __func__, strerror(errno)); + ret = -errno; + } + return ret; +} + +static int wait_for_interrupt(void *ctx, int msecs) { + int fd = *(int *)ctx; + struct pollfd fds = {fd, POLLIN, 0}; + int rv; + + rv = poll(&fds, 1 /*nfds*/, msecs); + if (rv < 0) { + ALOGE("poll: %s", strerror(errno)); + } + + return rv; +} + +static int reset(void *ctx) { + int ret; + int fd; + + if (!ctx) { + + ALOGE("%s: invalid (NULL) device\n", __func__); + return -ENODEV; + } + fd = *(int *)ctx; + if (fd < 0) { + ALOGE("%s: invalid device\n", __func__); + return -ENODEV; + } + + ret = ioctl(fd, CITADEL_IOC_RESET); + if (ret < 0) { + ALOGE("can't reset Citadel: %s", strerror(errno)); + return -errno; + } + return 0; +} + +static void close_device(void *ctx) { + int fd; + + if (!ctx) { + ALOGE("%s: invalid (NULL) device (ignored)\n", __func__); + return; + } + fd = *(int *)ctx; + if (fd < 0) { + ALOGE("%s: invalid device (ignored)\n", __func__); + return; + } + + if (close(fd) < 0) + ALOGE("Problem closing device (ignored): %s", strerror(errno)); + free(ctx); +} + +int nos_device_open(const char *device_name, struct nos_device *dev) { + int fd, *new_fd; + + fd = open(device_name ? device_name : DEV_CITADEL, O_RDWR); + if (fd < 0) { + ALOGE("can't open device: %s", strerror(errno)); + return -errno; + } + + new_fd = (int *)malloc(sizeof(int)); + if (!new_fd) { + ALOGE("can't malloc new fd: %s", strerror(errno)); + close(fd); + return -ENOMEM; + } + *new_fd = fd; + + dev->ctx = new_fd; + dev->ops.read = read_datagram; + dev->ops.write = write_datagram; + dev->ops.wait_for_interrupt = wait_for_interrupt; + dev->ops.reset = reset; + dev->ops.close = close_device; + return 0; +} diff --git a/nugget/include/citadel_events.h b/nugget/include/citadel_events.h index 336f3c5..154e638 100644 --- a/nugget/include/citadel_events.h +++ b/nugget/include/citadel_events.h @@ -44,47 +44,58 @@ extern "C" { * instead of changing things. */ +/* + * Event priority. Stored events of lower priority will be evicted to store + * higher-priority events if the queue is full. + */ +enum event_priority { + EVENT_PRIORITY_LOW = 0, + EVENT_PRIORITY_MEDIUM = 1, + EVENT_PRIORITY_HIGH = 2, +}; + +/* + * Event ID values live forever. + * Add to the list, but NEVER change or delete existing entries. + */ +enum event_id { + EVENT_NONE = 0, // Unused ID, used as empty marker. + EVENT_ALERT = 1, // Globalsec alert fired. + EVENT_REBOOTED = 2, // Device rebooted. + EVENT_UPGRADED = 3, // Device has upgraded. +}; + /* Please do not change the size of this struct */ #define EVENT_RECORD_SIZE 64 struct event_record { uint64_t reset_count; /* zeroed by Citadel power cycle */ uint64_t uptime_usecs; /* since last Citadel reset */ uint32_t id; + uint32_t priority; union { /* id-specific information goes here */ struct { uint32_t intr_sts[3]; } alert; struct { - uint32_t bad_thing; - } citadel; - struct { - uint32_t okay_thing; - } info; + uint32_t rstsrc; + uint32_t exitpd; + uint32_t which0; + uint32_t which1; + } rebooted; /* uninterpreted */ union { - uint32_t w[11]; - uint16_t h[22]; - uint8_t b[44]; + uint32_t w[10]; + uint16_t h[20]; + uint8_t b[40]; } raw; - } u; + } event; } __packed; /* Please do not change the size of this struct */ static_assert(sizeof(struct event_record) == EVENT_RECORD_SIZE, "Muting the Immutable"); -/* - * Event ID values live forever. - * Add to the list, but NEVER change or delete existing entries. -*/ -enum event_id { - EVENT_NONE = 0, /* No valid event exists with this ID */ - EVENT_ALERT = 1, /* Security alert reported */ - EVENT_CITADEL = 2, /* Bad: panic, stack overflow, etc. */ - EVENT_INFO = 3, /* FYI: normal reboot, etc. */ -}; - #ifdef __cplusplus } #endif |