Merge remote-tracking branch 'goog/upstream-master' into nos-merge-upstream
* goog/upstream-master:
avb.proto: add resetchallenge and getproduction
flash: update of cert & provisioning data offsets
Suppress clang 7.0 warnings.
Add retry logic to get_status() function
Define static lib for 'recovery' system module
flash_layout: Add manufacturing related config
updater: Add option to wiggle Citadel's reset line
updater: Add command to read Low Power Stats
Reserve a fake AVB_TEST app ID just for testing
keymaster: add factory-setup methods
Bug: 74946926
Change-Id: Ic4bf32b5994a0f159ce4d0efaab6e1427ae1142e
diff --git a/Android.bp b/Android.bp
index 6350262..a07fdbb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -23,7 +23,6 @@
cc_defaults {
name: "nos_proto_defaults",
cflags: [
- "-Wno-extended-offsetof",
"-Wno-unused-parameter",
],
}
diff --git a/citadel/updater/Android.bp b/citadel/updater/Android.bp
index 8ea42a4..6b1a86b 100644
--- a/citadel/updater/Android.bp
+++ b/citadel/updater/Android.bp
@@ -33,5 +33,6 @@
"libnos",
"libnos_citadeld_proxy",
"libnos_client_citadel",
+ "libutils",
],
}
diff --git a/citadel/updater/updater.cpp b/citadel/updater/updater.cpp
index 1cff116..4f70ae2 100644
--- a/citadel/updater/updater.cpp
+++ b/citadel/updater/updater.cpp
@@ -52,9 +52,11 @@
struct options_s {
/* actions to take */
int version;
+ int stats;
int ro;
int rw;
int reboot;
+ int force_reset;
int enable_ro;
int enable_rw;
int change_pw;
@@ -65,9 +67,11 @@
enum no_short_opts_for_these {
OPT_DEVICE = 1000,
+ OPT_STATS,
OPT_RO,
OPT_RW,
OPT_REBOOT,
+ OPT_FORCE_RESET,
OPT_ENABLE_RO,
OPT_ENABLE_RW,
OPT_CHANGE_PW,
@@ -78,9 +82,11 @@
const struct option long_opts[] = {
/* name hasarg *flag val */
{"version", 0, NULL, 'v'},
+ {"stats", 0, NULL, OPT_STATS},
{"ro", 0, NULL, OPT_RO},
{"rw", 0, NULL, OPT_RW},
{"reboot", 0, NULL, OPT_REBOOT},
+ {"force_reset", 0, NULL, OPT_FORCE_RESET},
{"enable_ro", 0, NULL, OPT_ENABLE_RO},
{"enable_rw", 0, NULL, OPT_ENABLE_RW},
{"change_pw", 0, NULL, OPT_CHANGE_PW},
@@ -117,26 +123,28 @@
"\n"
"Actions:\n"
"\n"
- " -v, --version Display the Citadel version info\n"
- " --rw Update RW firmware from the image file\n"
- " --ro Update RO firmware from the image file\n"
- " --reboot Tell Citadel to reboot\n"
+ " -v, --version Display the Citadel version info\n"
+ " --stats Display Low Power stats\n"
+ " --rw Update RW firmware from the image file\n"
+ " --ro Update RO firmware from the image file\n"
+ " --reboot Tell Citadel to reboot\n"
+ " --force_reset Pulse Citadel's reset line\n"
"\n"
- " --enable_ro Mark new RO image as good\n"
- " --enable_rw Mark new RW image as good\n"
+ " --enable_ro Mark new RO image as good\n"
+ " --enable_rw Mark new RW image as good\n"
"\n"
- " --change_pw Change update password\n"
+ " --change_pw Change update password\n"
"\n\n"
- " --erase=CODE Erase all user secrets and reboot.\n"
- " This skips all other actions.\n"
+ " --erase=CODE Erase all user secrets and reboot.\n"
+ " This skips all other actions.\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"
+ " --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);
@@ -341,6 +349,38 @@
return retval;
}
+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)) {
+ fprintf(stderr, "Only got %lud / %lud bytes back",
+ buffer.size(), sizeof(stats));
+ return -1;
+ }
+
+ memcpy(&stats, buffer.data(), sizeof(stats));
+
+ printf("hard_reset_count %lu\n", stats.hard_reset_count);
+ printf("time_since_hard_reset %lu\n", stats.time_since_hard_reset);
+ printf("wake_count %lu\n", stats.wake_count);
+ printf("time_at_last_wake %lu\n", stats.time_at_last_wake);
+ printf("time_spent_awake %lu\n", stats.time_spent_awake);
+ printf("deep_sleep_count %lu\n", stats.deep_sleep_count);
+ printf("time_at_last_deep_sleep %lu\n", stats.time_at_last_deep_sleep);
+ printf("time_spent_in_deep_sleep %lu\n", stats.time_spent_in_deep_sleep);
+ }
+
+ return retval;
+}
+
uint32_t do_reboot(AppClient &app)
{
uint32_t retval;
@@ -426,26 +466,38 @@
return rv;
}
-std::unique_ptr<NuggetClientInterface> select_client()
-{
+// This is currently device-specific, but could be abstracted further
#ifdef ANDROID
- return std::unique_ptr<NuggetClientInterface>(new CitadeldProxyClient());
-#else
- return std::unique_ptr<NuggetClientInterface>(
- new NuggetClient(options.device ? options.device : ""));
-#endif
+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)
{
- auto client = select_client();
- client->Open();
- if (!client->IsOpen()) {
+#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);
+ AppClient app(client, APP_ID_NUGGET);
/* Try all requested actions in reasonable order, bail out on error */
@@ -459,6 +511,11 @@
return 2;
}
+ if (options.stats &&
+ do_stats(app) != APP_SUCCESS) {
+ return 2;
+ }
+
if (options.rw &&
do_update(app, image,
CHIP_RW_A_MEM_OFF, CHIP_RW_B_MEM_OFF) != APP_SUCCESS) {
@@ -484,6 +541,11 @@
return 7;
}
+ if (options.force_reset &&
+ do_force_reset(client) != APP_SUCCESS) {
+ return 1;
+ }
+
return 0;
}
@@ -522,6 +584,10 @@
options.version = 1;
got_action = 1;
break;
+ case OPT_STATS:
+ options.stats = 1;
+ got_action = 1;
+ break;
case OPT_RO:
options.ro = 1;
got_action = 1;
@@ -534,6 +600,10 @@
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;
diff --git a/libnos_transport/transport.c b/libnos_transport/transport.c
index c328c18..140d257 100644
--- a/libnos_transport/transport.c
+++ b/libnos_transport/transport.c
@@ -16,6 +16,7 @@
#include <nos/transport.h>
+#include <errno.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
@@ -62,14 +63,33 @@
uint8_t buf[6];
uint32_t command = CMD_ID(app_id) | CMD_IS_READ | CMD_TRANSPORT;
- if (0 != dev->ops.read(dev->ctx, command, buf, sizeof(buf))) {
- NLOGV("Failed to read device status");
+ uint16_t retries = 10;
+ while (retries--) {
+ int err = dev->ops.read(dev->ctx, command, buf, sizeof(buf));
+
+ if (err == 0) {
+ /* The read operation is successful */
+ *status = *(uint32_t *)buf;
+ *ulen = *(uint16_t *)(buf + 4);
+
+ return 0;
+ }
+
+ if (err == -EAGAIN) {
+ /* Linux driver returns EAGAIN error if Citadel chip is asleep.
+ * Give to the chip a little bit of time to awake and retry reading
+ * status again. */
+ usleep(5000);
+ continue;
+ }
+
+ /* Citadel driver returned an error */
+ NLOGE("Failed to read device status: %d", err);
return -1;
}
- *status = *(uint32_t *)buf;
- *ulen = *(uint16_t *)(buf + 4);
- return 0;
+ NLOGE("Failed to read device status");
+ return -1;
}
static int clear_status(const struct nos_device *dev, uint8_t app_id)
@@ -94,18 +114,8 @@
uint32_t status;
uint16_t ulen;
uint32_t poll_count = 0;
- uint16_t retries = 10;
- /* Make sure it's idle */
- while (retries) {
- if (get_status(dev, app_id, &status, &ulen) == 0) {
- break;
- }
- --retries;
- usleep(5000);
- }
- if (!retries) {
- NLOGE("Failed to read device status");
+ if (get_status(dev, app_id, &status, &ulen) != 0) {
return APP_ERROR_IO;
}
NLOGV("%d: query status 0x%08x ulen 0x%04x", __LINE__, status, ulen);
diff --git a/nugget/include/application.h b/nugget/include/application.h
index f7e2142..45824b3 100644
--- a/nugget/include/application.h
+++ b/nugget/include/application.h
@@ -73,6 +73,9 @@
#define APP_ID_WEAVER 0x03
#define APP_ID_PROTOBUF 0x04
+/* Fake apps used only for testing */
+#define APP_ID_AVB_TEST 0x11
+
/* This app ID should only be used by tests. */
#define APP_ID_TEST 0xff
diff --git a/nugget/include/flash_layout.h b/nugget/include/flash_layout.h
index 5049a85..580970d 100644
--- a/nugget/include/flash_layout.h
+++ b/nugget/include/flash_layout.h
@@ -69,4 +69,18 @@
/* The flash controller prevents bulk writes that cross row boundaries */
#define CHIP_FLASH_ROW_SIZE 256 /* row size */
+/* Manufacturing related data. */
+/* Certs in the RO region are written as 4-kB + 3-kB blocks to the A &
+ * B banks respectively.
+ */
+#define RO_CERTS_A_OFF (CHIP_RO_A_MEM_OFF + 0x2800)
+#define RO_CERTS_B_OFF (CHIP_RO_B_MEM_OFF + 0x2800)
+#define RO_CERTS_A_SIZE 0x01000
+#define RO_CERTS_B_SIZE 0x00c00
+/* We have an unused 3-kB region in the B bank, for future proofing. */
+#define RO_CERTS_PAD_B_SIZE 0x00c00
+/* Factory provision data is written as a 2-kB block to the A bank. */
+#define RO_PROVISION_DATA_A_OFF 0x3800
+#define RO_PROVISION_DATA_A_SIZE 0x0800
+
#endif /* __CROS_EC_FLASH_LAYOUT_H */
diff --git a/nugget/proto/nugget/app/avb/avb.proto b/nugget/proto/nugget/app/avb/avb.proto
index b9422a2..bc0c700 100644
--- a/nugget/proto/nugget/app/avb/avb.proto
+++ b/nugget/proto/nugget/app/avb/avb.proto
@@ -41,6 +41,8 @@
rpc Reset (ResetRequest) returns (ResetResponse);
rpc BootloaderDone (BootloaderDoneRequest) returns (BootloaderDoneResponse);
rpc GetOwnerKey (GetOwnerKeyRequest) returns (GetOwnerKeyResponse);
+ rpc GetResetChallenge (GetResetChallengeRequest) returns (GetResetChallengeResponse);
+ rpc ProductionResetTest (ProductionResetTestRequest) returns (ProductionResetTestResponse);
}
enum LockIndex {
@@ -131,6 +133,7 @@
// SetProduction
message SetProductionRequest {
bool production = 1;
+ bytes device_data = 2;
}
message SetProductionResponse {}
@@ -145,16 +148,43 @@
message CarrierLockTestResponse {}
// Reset
+message ResetToken {
+ enum Selectors {
+ INVALID = 0;
+ CURRENT = 1;
+ };
+ uint32 selector = 1;
+ bytes signature = 2;
+}
+
message ResetRequest {
enum ResetKind {
- FACTORY = 0;
+ PRODUCTION = 0;
LOCKS = 1;
- }
+ };
ResetKind kind = 1;
+ ResetToken token = 2; // optional
}
message ResetResponse {}
+// GetResetChallenge
+message GetResetChallengeRequest {}
+message GetResetChallengeResponse {
+ uint32 selector = 1;
+ uint64 nonce = 2;
+ bytes device_data = 3;
+}
+
+// ProductionResetTest
+message ProductionResetTestRequest {
+ uint32 selector = 1;
+ uint64 nonce = 2;
+ bytes device_data = 3;
+ bytes signature = 4;
+}
+message ProductionResetTestResponse {}
+
// BootloaderDone
message BootloaderDoneRequest {}
diff --git a/nugget/proto/nugget/app/keymaster/keymaster.proto b/nugget/proto/nugget/app/keymaster/keymaster.proto
index d20d4c5..603e041 100644
--- a/nugget/proto/nugget/app/keymaster/keymaster.proto
+++ b/nugget/proto/nugget/app/keymaster/keymaster.proto
@@ -75,6 +75,10 @@
rpc SetRootOfTrust (SetRootOfTrustRequest) returns (SetRootOfTrustResponse);
// Only callable by the Bootloader.
rpc SetBootState (SetBootStateRequest) returns (SetBootStateResponse);
+ // Only callable at the Device Factory.
+ rpc ProvisionDeviceIds (ProvisionDeviceIdsRequest) returns (ProvisionDeviceIdsResponse);
+ // Only callable at the Device Factory.
+ rpc ReadTeeBatchCertificate (ReadTeeBatchCertificateRequest) returns (ReadTeeBatchCertificateResponse);
}
/*
@@ -267,4 +271,33 @@
message SetBootStateResponse {
// Specified in keymaster_defs.proto:ErrorCode
ErrorCode error_code = 1;
-}
\ No newline at end of file
+}
+
+// ProvisionDeviceIds
+// Only callable at the Device Factory
+message ProvisionDeviceIdsRequest {
+ bytes product_brand = 1;
+ bytes product_device = 2;
+ bytes product_name = 3;
+ bytes serialno = 4;
+ bytes product_manufacturer = 5;
+ bytes product_model = 6;
+ bytes imei = 7;
+ bytes meid = 8;
+}
+message ProvisionDeviceIdsResponse {
+ // Specified in keymaster_defs.proto:ErrorCode
+ ErrorCode error_code = 1;
+}
+
+// ReadTeeBatchCertificate
+// Only callable at the Device Factory
+message ReadTeeBatchCertificateRequest {
+ Algorithm algorithm = 1;
+}
+message ReadTeeBatchCertificateResponse {
+ ErrorCode error_code = 1;
+ RSAKey rsa = 2; // rsa or ec set based on request algorithm selector.
+ ECKey ec = 3;
+ bytes batch_cert = 4;
+}