summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHenry Sun <henrysun@google.com>2019-12-17 13:59:47 +0800
committerCommit Bot <commit-bot@chromium.org>2020-05-27 07:17:02 +0000
commit05763ab160f1fa395d80b7159b3d71e30e6045c5 (patch)
treeed02b006498b0eaaa0743f92ca8328b2641c2ed4
parentb3af8a7125cef37701af5466fd30064b1bf4ee02 (diff)
downloadchrome-ec-05763ab160f1fa395d80b7159b3d71e30e6045c5.tar.gz
gsctool: fast forward to cros/factory-coral-10122.B
Checkout gsctool related files from coral factory branch git checkout cros/factory-coral-10122.B -- \ include/ util/ test/ board/cr50/ chip/g BRANCH=reef BUG=b:145473707 TEST=emerge-reef ec-utils TEST=build test image with dependent changes, check gsctool was compiled out. Cq-Depend: chromium:1970774 Cq-Depend: chromium:1970516 Cq-Depend: chromium:1970517 Cq-Depend: chromium:1970518 Cq-Depend: chromium:1970519 Change-Id: I1b602d65b662ad25e4f46ab285894052e99949ca Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1970673 Reviewed-by: Henry Sun <henrysun@google.com> Tested-by: Henry Sun <henrysun@google.com> Commit-Queue: Henry Sun <henrysun@google.com>
-rw-r--r--board/cr50/ap_state.c212
-rw-r--r--board/cr50/board.c1469
-rw-r--r--board/cr50/board.h271
-rw-r--r--board/cr50/build.mk21
-rw-r--r--board/cr50/ec.tasklist2
-rw-r--r--board/cr50/ec_state.c139
-rw-r--r--board/cr50/gpio.inc139
-rw-r--r--board/cr50/pinweaver_tpm_imports.c20
-rw-r--r--board/cr50/power_button.c118
-rw-r--r--board/cr50/rdd.c499
-rw-r--r--board/cr50/recovery_button.c73
-rw-r--r--board/cr50/scratch_reg1.h55
-rw-r--r--board/cr50/servo_state.c207
-rw-r--r--board/cr50/tpm2/NVMem.c45
-rw-r--r--board/cr50/tpm2/aes.c84
-rw-r--r--board/cr50/tpm2/ecc.c49
-rw-r--r--board/cr50/tpm2/endorsement.c428
-rw-r--r--board/cr50/tpm2/hash.c64
-rw-r--r--board/cr50/tpm2/platform.c52
-rw-r--r--board/cr50/tpm2/rsa.c173
-rw-r--r--board/cr50/tpm2/stubs.c1
-rw-r--r--board/cr50/tpm2/tpm_state.c6
-rw-r--r--board/cr50/tpm_nvmem_read.c55
-rw-r--r--board/cr50/tpm_nvmem_read.h20
-rw-r--r--board/cr50/u2f.c226
-rw-r--r--board/cr50/usb_i2c.c98
-rw-r--r--board/cr50/usb_spi.c699
-rw-r--r--board/cr50/wp.c430
-rw-r--r--board/cr50/wp.h24
-rw-r--r--board/reef/board.h3
-rw-r--r--chip/g/alerts.c364
-rw-r--r--chip/g/board_id.c271
-rw-r--r--chip/g/board_id.h68
-rw-r--r--chip/g/build.mk94
-rw-r--r--chip/g/config_chip.h10
-rw-r--r--chip/g/crypto_api.c31
-rw-r--r--chip/g/dcrypto/aes.c10
-rw-r--r--chip/g/dcrypto/app_cipher.c452
-rw-r--r--chip/g/dcrypto/app_key.c91
-rw-r--r--chip/g/dcrypto/bn.c1034
-rw-r--r--chip/g/dcrypto/compare.c20
-rw-r--r--chip/g/dcrypto/dcrypto.h156
-rw-r--r--chip/g/dcrypto/dcrypto_bn.c733
-rw-r--r--chip/g/dcrypto/dcrypto_p256.c941
-rw-r--r--chip/g/dcrypto/dcrypto_runtime.c46
-rw-r--r--chip/g/dcrypto/dcrypto_sha512.c772
-rw-r--r--chip/g/dcrypto/drbg_rfc6979.c166
-rw-r--r--chip/g/dcrypto/gcm.c345
-rw-r--r--chip/g/dcrypto/hkdf.c3
-rw-r--r--chip/g/dcrypto/hmac.c4
-rw-r--r--chip/g/dcrypto/internal.h101
-rw-r--r--chip/g/dcrypto/key_ladder.c289
-rw-r--r--chip/g/dcrypto/p256.c8
-rw-r--r--chip/g/dcrypto/p256_ec.c7
-rw-r--r--chip/g/dcrypto/p256_ecies.c11
-rw-r--r--chip/g/dcrypto/rsa.c236
-rw-r--r--chip/g/dcrypto/sha256.c7
-rw-r--r--chip/g/dcrypto/sha384.c20
-rw-r--r--chip/g/dcrypto/sha512.c20
-rw-r--r--chip/g/dcrypto/x509.c341
-rw-r--r--chip/g/flash.c139
-rw-r--r--chip/g/flash_config.h2
-rw-r--r--chip/g/flash_info.h2
-rw-r--r--chip/g/gpio.c113
-rw-r--r--chip/g/hwtimer.c94
-rw-r--r--chip/g/i2cm.c485
-rw-r--r--chip/g/i2cs.c144
-rw-r--r--chip/g/i2cs.h20
-rw-r--r--chip/g/idle.c97
-rw-r--r--chip/g/init_chip.h32
-rw-r--r--chip/g/jitter.c22
-rw-r--r--chip/g/loader/main.c2
-rw-r--r--chip/g/pmu.c5
-rw-r--r--chip/g/post_reset.c39
-rw-r--r--chip/g/pre_init.c28
-rw-r--r--chip/g/rbox.c43
-rw-r--r--chip/g/rbox.h28
-rw-r--r--chip/g/rdd.c224
-rw-r--r--chip/g/rdd.h18
-rw-r--r--chip/g/registers.h7
-rw-r--r--chip/g/runlevel.c49
-rw-r--r--chip/g/spi_master.c34
-rw-r--r--chip/g/sps.c110
-rw-r--r--chip/g/sps.h2
-rw-r--r--chip/g/sps_tpm.c29
-rw-r--r--chip/g/system.c469
-rw-r--r--chip/g/system_chip.h93
-rw-r--r--chip/g/trng.c34
-rw-r--r--chip/g/uart_bitbang.c541
-rw-r--r--chip/g/uart_bitbang.h130
-rw-r--r--chip/g/uartn.c97
-rw-r--r--chip/g/uartn.h42
-rw-r--r--chip/g/upgrade.c150
-rw-r--r--chip/g/upgrade_fw.c260
-rw-r--r--chip/g/upgrade_fw.h14
-rw-r--r--chip/g/usart.c83
-rw-r--r--chip/g/usart.h18
-rw-r--r--chip/g/usb-stream.c4
-rw-r--r--chip/g/usb-stream.h1
-rw-r--r--chip/g/usb.c219
-rw-r--r--chip/g/usb_console.c19
-rw-r--r--chip/g/usb_hid_keyboard.c159
-rw-r--r--chip/g/usb_spi.c111
-rw-r--r--chip/g/usb_spi.h33
-rw-r--r--chip/g/usb_upgrade.c121
-rw-r--r--chip/g/watchdog.c4
-rw-r--r--extra/usb_updater/Makefile45
-rw-r--r--extra/usb_updater/desc_parser.c377
-rw-r--r--extra/usb_updater/desc_parser.d1
-rw-r--r--extra/usb_updater/desc_parser.h58
-rw-r--r--extra/usb_updater/desc_parser.obin0 -> 16040 bytes
l---------extra/usb_updater/ecusb1
-rwxr-xr-xextra/usb_updater/fw_update.py34
-rw-r--r--extra/usb_updater/generated_version.h11
-rw-r--r--extra/usb_updater/gsctool.c2400
-rw-r--r--extra/usb_updater/gsctool.h116
-rw-r--r--extra/usb_updater/sample_descriptor87
-rwxr-xr-xextra/usb_updater/servo_updater.py85
-rw-r--r--extra/usb_updater/usb_updater2.c1046
-rw-r--r--extra/usb_updater/verify_ro.c342
-rw-r--r--extra/usb_updater/verify_ro.h24
-rw-r--r--include/2id.h32
-rw-r--r--include/accelgyro.h20
-rw-r--r--include/base32.h73
-rw-r--r--include/battery.h24
-rw-r--r--include/battery_smart.h15
-rw-r--r--include/board_config.h35
-rw-r--r--include/btle_hci_int.h12
-rw-r--r--include/button.h12
-rw-r--r--include/case_closed_debug.h31
-rw-r--r--include/ccd_config.h292
-rw-r--r--include/charge_manager.h132
-rw-r--r--include/charge_ramp.h33
-rw-r--r--include/charge_state.h27
-rw-r--r--include/charge_state_v2.h44
-rw-r--r--include/charger.h26
-rw-r--r--include/charger_profile_override.h86
-rw-r--r--include/chipset.h6
-rw-r--r--include/common.h45
-rw-r--r--include/compile_time_macros.h2
-rw-r--r--include/config.h1001
-rw-r--r--include/console.h5
-rw-r--r--include/console_channel.inc9
-rw-r--r--include/crypto_api.h55
-rw-r--r--include/curve25519.h70
-rw-r--r--include/device_event.h44
-rw-r--r--include/device_state.h87
-rw-r--r--include/ec_commands.h826
-rw-r--r--include/event_log.h35
-rw-r--r--include/extension.h12
-rw-r--r--include/flash.h110
-rw-r--r--include/gpio.h21
-rw-r--r--include/hooks.h26
-rw-r--r--include/host_command.h87
-rw-r--r--include/i2c.h85
-rw-r--r--include/keyboard_8042.h7
-rw-r--r--include/keyboard_8042_sharedlib.h5
-rw-r--r--include/keyboard_config.h3
-rw-r--r--include/keyboard_protocol.h12
-rw-r--r--include/keyboard_scan.h32
-rw-r--r--include/led_common.h18
-rw-r--r--include/link_defs.h12
-rw-r--r--include/lpc.h49
-rw-r--r--include/mkbp_event.h3
-rw-r--r--include/motion_lid.h6
-rw-r--r--include/motion_sense.h50
-rw-r--r--include/nvcounter.h20
-rw-r--r--include/nvmem.h103
-rw-r--r--include/nvmem_vars.h87
-rw-r--r--include/otp.h32
-rw-r--r--include/panic.h14
-rw-r--r--include/physical_presence.h76
-rw-r--r--include/pinweaver.h142
-rw-r--r--include/pinweaver_tpm_imports.h27
-rw-r--r--include/pinweaver_types.h289
-rw-r--r--include/power.h95
-rw-r--r--include/power_button.h8
-rw-r--r--include/pwm.h10
-rw-r--r--include/rma_auth.h69
-rw-r--r--include/rollback.h72
-rw-r--r--include/rsa.h44
-rw-r--r--include/rtc.h45
-rw-r--r--include/rwsig.h126
-rw-r--r--include/sha256.h3
-rw-r--r--include/shared_mem.h41
-rw-r--r--include/smbus.h4
-rw-r--r--include/software_panic.h1
-rw-r--r--include/spi.h10
-rw-r--r--include/spi_flash.h22
-rw-r--r--include/spi_nor.h12
-rw-r--r--include/system.h182
-rw-r--r--include/tablet_mode.h12
-rw-r--r--include/task.h4
-rw-r--r--include/task_filter.h50
-rw-r--r--include/task_id.h15
-rw-r--r--include/tpm_log.h19
-rw-r--r--include/tpm_manufacture.h19
-rw-r--r--include/tpm_registers.h58
-rw-r--r--include/tpm_vendor_cmds.h70
-rw-r--r--include/trng.h11
-rw-r--r--include/u2f.h106
-rw-r--r--include/u2f_impl.h111
-rw-r--r--include/uart.h43
-rw-r--r--include/update_fw.h245
-rw-r--r--include/usb_api.h33
-rw-r--r--include/usb_charge.h26
-rw-r--r--include/usb_descriptor.h69
-rw-r--r--include/usb_hid.h3
-rw-r--r--include/usb_hid_touchpad.h33
-rw-r--r--include/usb_i2c.h177
-rw-r--r--include/usb_mux.h6
-rw-r--r--include/usb_pd.h286
-rw-r--r--include/usb_pd_tcpm.h39
-rw-r--r--include/util.h10
-rw-r--r--include/vb21_struct.h346
-rw-r--r--include/vboot.h48
-rw-r--r--include/version.h13
-rw-r--r--include/virtual_battery.h49
-rw-r--r--include/watchdog.h4
-rw-r--r--test/base32.c210
-rw-r--r--test/base32.tasklist17
-rw-r--r--test/build.mk90
-rw-r--r--test/button.c14
-rw-r--r--test/charge_manager.c55
-rw-r--r--test/charge_ramp.c64
-rw-r--r--test/entropy.c92
-rw-r--r--test/entropy.tasklist17
-rw-r--r--test/flash.c7
-rw-r--r--test/kb_scan.c4
-rw-r--r--test/motion_lid.c56
-rw-r--r--test/nvmem.c263
-rw-r--r--test/nvmem_vars.c538
-rw-r--r--test/nvmem_vars.tasklist17
-rw-r--r--test/pinweaver.c1739
-rw-r--r--test/pinweaver.tasklist17
-rw-r--r--test/rma_auth.c200
-rw-r--r--test/rma_auth.tasklist17
-rw-r--r--test/rsa.c53
-rw-r--r--test/rsa.tasklist17
-rw-r--r--test/rsa2048-3.h111
-rw-r--r--test/rsa2048-3.pem27
-rw-r--r--test/rsa2048-F4.h111
-rw-r--r--test/rsa2048-F4.pem27
l---------test/rsa3.tasklist1
-rw-r--r--test/rtc.c104
-rw-r--r--test/rtc.tasklist17
-rw-r--r--test/sbs_charging_v2.c2
-rw-r--r--test/sha256.c220
-rw-r--r--test/sha256.tasklist17
l---------test/sha256_unrolled.tasklist1
-rw-r--r--test/shmalloc.c305
-rw-r--r--test/shmalloc.tasklist19
-rw-r--r--test/stress.c7
-rw-r--r--test/test_config.h151
-rw-r--r--test/tpm_test/Makefile25
-rw-r--r--test/tpm_test/bn_test.c10
-rw-r--r--test/tpm_test/crypto_test.py33
-rw-r--r--test/tpm_test/crypto_test.xml336
-rw-r--r--test/tpm_test/hash_test.py2
-rw-r--r--test/tpm_test/rsa1024.pem15
-rw-r--r--test/tpm_test/rsa2048.pem27
-rw-r--r--test/tpm_test/rsa4096.pem51
-rw-r--r--test/tpm_test/rsa768.pem12
-rw-r--r--test/tpm_test/rsa_test.py95
-rwxr-xr-xtest/tpm_test/tpmtest.py15
-rw-r--r--test/tpm_test/upgrade_test.py4
-rw-r--r--test/usb_pd.c673
l---------test/usb_pd_giveback.tasklist1
l---------test/usb_pd_rev30.tasklist1
-rw-r--r--test/utils.c108
-rw-r--r--test/utils_str.c162
-rw-r--r--test/utils_str.tasklist17
-rw-r--r--test/vboot.c142
-rw-r--r--test/vboot.tasklist17
-rw-r--r--test/x25519.c197
-rw-r--r--test/x25519.tasklist17
-rw-r--r--util/build.mk41
-rw-r--r--util/comm-host.c7
-rwxr-xr-xutil/ec3po/console.py2
-rwxr-xr-xutil/ec3po/console_unittest.py2
-rw-r--r--util/ec3po/interpreter.py14
-rw-r--r--util/ec_sb_firmware_update.c22
-rwxr-xr-xutil/ecst.c287
-rwxr-xr-xutil/ecst.h265
-rw-r--r--util/ectool.c469
-rw-r--r--util/export_taskinfo.c43
-rwxr-xr-xutil/flash_ec281
-rw-r--r--util/gen_touchpad_hash.c174
-rw-r--r--util/genvif.c551
-rwxr-xr-xutil/getversion.sh27
-rwxr-xr-xutil/host_command_check.sh128
-rw-r--r--util/iteflash.c36
-rw-r--r--util/misc_util.c7
-rw-r--r--util/openocd/npcx.cfg6
-rw-r--r--util/openocd/npcx_cmds.tcl27
-rwxr-xr-xutil/presubmit_check.sh43
-rw-r--r--util/run_ects.py92
-rwxr-xr-xutil/signer/bs224
-rw-r--r--util/signer/build.mk11
-rw-r--r--util/signer/codesigner.cc12
-rw-r--r--util/signer/cr50_RW-prod.pem.pub9
-rwxr-xr-xutil/signer/create_released_image.sh222
-rw-r--r--util/signer/ec_RW-manifest-dev.json48
-rw-r--r--util/signer/ec_RW-manifest-prod.json52
-rw-r--r--util/signer/gnubby.cc3
-rw-r--r--util/signer/loader-testkey-A.pem62
-rwxr-xr-xutil/signer/pmjp.py53
-rw-r--r--util/stm32mon.c228
-rwxr-xr-xutil/tagbranch.sh84
309 files changed, 34949 insertions, 3536 deletions
diff --git a/board/cr50/ap_state.c b/board/cr50/ap_state.c
new file mode 100644
index 0000000000..e9ddea5640
--- /dev/null
+++ b/board/cr50/ap_state.c
@@ -0,0 +1,212 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * AP state machine
+ */
+#include "ccd_config.h"
+#include "common.h"
+#include "console.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "system.h"
+
+#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args)
+
+static enum device_state state = DEVICE_STATE_INIT;
+
+void print_ap_state(void)
+{
+ ccprintf("AP: %s\n", device_state_name(state));
+}
+
+int ap_is_on(void)
+{
+ /* Debouncing and on are both still on */
+ return (state == DEVICE_STATE_DEBOUNCING || state == DEVICE_STATE_ON);
+}
+
+/**
+ * Set the AP state.
+ *
+ * Done as a function to make it easier to debug state transitions. Note that
+ * this ONLY sets the state (and possibly prints debug info), and doesn't do
+ * all the additional transition work that set_ap_on(), etc. do.
+ *
+ * @param new_state State to set.
+ */
+static void set_state(enum device_state new_state)
+{
+#ifdef CR50_DEBUG_AP_STATE
+ /* Print all state transitions. May spam the console. */
+ if (state != new_state)
+ CPRINTS("AP %s -> %s",
+ device_state_name(state), device_state_name(new_state));
+#endif
+ state = new_state;
+}
+
+/**
+ * Set AP to the off state
+ */
+static void set_ap_off(void)
+{
+ CPRINTS("AP off");
+ set_state(DEVICE_STATE_OFF);
+
+ /*
+ * If TPM is configured then the INT_AP_L signal is used as a low pulse
+ * trigger to sync transactions with the host. By default Cr50 is
+ * driving this line high, but when the AP powers off, the 1.8V rail
+ * that it's pulled up to will be off and cause excessive power to be
+ * consumed by the Cr50. Set INT_AP_L as an input while the AP is
+ * powered off.
+ */
+ gpio_set_flags(GPIO_INT_AP_L, GPIO_INPUT);
+
+ ccd_update_state();
+
+ /*
+ * We don't enable deep sleep on ARM devices yet, as its processing
+ * there will require more support on the AP side than is available
+ * now.
+ *
+ * Note: Presence of platform reset is a poor indicator of deep sleep
+ * support. It happens to be correlated with ARM vs x86 at present.
+ */
+ if (board_deep_sleep_allowed())
+ enable_deep_sleep();
+}
+
+/**
+ * Move the AP to the ON state
+ */
+static void set_ap_on(void)
+{
+ CPRINTS("AP on");
+ set_state(DEVICE_STATE_ON);
+
+ /*
+ * AP is powering up, set the host sync signal to output and set it
+ * high which is the default level.
+ */
+ gpio_set_flags(GPIO_INT_AP_L, GPIO_OUT_HIGH);
+ gpio_set_level(GPIO_INT_AP_L, 1);
+
+ ccd_update_state();
+
+ if (board_deep_sleep_allowed())
+ disable_deep_sleep();
+}
+
+/**
+ * Handle moving the AP to the OFF state from a deferred interrupt handler.
+ *
+ * Needs to make additional state checks to avoid double-on in case ap_detect()
+ * has run in the meantime.
+ */
+void set_ap_on_deferred(void)
+{
+ /* If we were debouncing ON->OFF, cancel it because we're still on */
+ if (state == DEVICE_STATE_DEBOUNCING)
+ set_state(DEVICE_STATE_ON);
+
+ /* If AP isn't already on, make it so */
+ if (state != DEVICE_STATE_ON)
+ set_ap_on();
+}
+DECLARE_DEFERRED(set_ap_on_deferred);
+
+/**
+ * Interrupt handler for AP detect asserted
+ */
+void ap_detect_asserted(enum gpio_signal signal)
+{
+ gpio_disable_interrupt(GPIO_DETECT_AP);
+ hook_call_deferred(&set_ap_on_deferred_data, 0);
+}
+
+/**
+ * Detect state machine
+ */
+static void ap_detect(void)
+{
+ int detect;
+
+ if (board_detect_ap_with_tpm_rst()) {
+ /* AP is detected if platform reset is deasserted */
+ detect = gpio_get_level(GPIO_TPM_RST_L);
+ } else {
+ /* Disable interrupts if we had them on for debouncing */
+ gpio_disable_interrupt(GPIO_DETECT_AP);
+
+ /* AP is detected if it's driving its UART TX signal */
+ detect = gpio_get_level(GPIO_DETECT_AP);
+ }
+
+ /* Handle detecting device */
+ if (detect) {
+ /*
+ * If we were debouncing ON->OFF, cancel debouncing and go back
+ * to the ON state.
+ */
+ if (state == DEVICE_STATE_DEBOUNCING)
+ set_state(DEVICE_STATE_ON);
+
+ /* If we're already ON, done */
+ if (state == DEVICE_STATE_ON)
+ return;
+
+ if (board_detect_ap_with_tpm_rst()) {
+ /*
+ * The platform reset handler has not run yet;
+ * otherwise, it would have already turned the AP on
+ * and we wouldn't get here.
+ *
+ * This can happen if the hook task calls ap_detect()
+ * before deferred_tpm_rst_isr(). In this case, the
+ * deferred handler is already pending so calling the
+ * ISR has no effect.
+ *
+ * But we may actually have missed the edge. In that
+ * case, calling the ISR makes sure we don't miss the
+ * reset. It will call set_ap_on_deferred() to move
+ * the AP to the ON state.
+ */
+ CPRINTS("AP detect calling tpm_rst_deasserted()");
+ tpm_rst_deasserted(GPIO_TPM_RST_L);
+ } else {
+ /* We're responsible for setting the AP state to ON */
+ set_ap_on();
+ }
+
+ return;
+ }
+
+ /* AP wasn't detected. If we're already off, done. */
+ if (state == DEVICE_STATE_OFF)
+ return;
+
+ /* If we were debouncing, we're now sure we're off */
+ if (state == DEVICE_STATE_DEBOUNCING ||
+ state == DEVICE_STATE_INIT_DEBOUNCING) {
+ set_ap_off();
+ return;
+ }
+
+ /*
+ * Otherwise, we were on before and haven't detected the AP. But we
+ * don't know if that's because the AP is actually off, or because the
+ * AP UART is sending a 0-bit or temporarily asserting platform reset.
+ * So start debouncing.
+ */
+ if (state == DEVICE_STATE_INIT)
+ set_state(DEVICE_STATE_INIT_DEBOUNCING);
+ else
+ set_state(DEVICE_STATE_DEBOUNCING);
+
+ /* If we're using AP UART RX for detect, enable its interrupt */
+ if (!board_detect_ap_with_tpm_rst())
+ gpio_enable_interrupt(GPIO_DETECT_AP);
+}
+DECLARE_HOOK(HOOK_SECOND, ap_detect, HOOK_PRIO_DEFAULT);
diff --git a/board/cr50/board.c b/board/cr50/board.c
index c80c2f3930..932fddaf9b 100644
--- a/board/cr50/board.c
+++ b/board/cr50/board.c
@@ -2,30 +2,42 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
-
+#include "board_id.h"
+#include "ccd_config.h"
#include "clock.h"
#include "common.h"
#include "console.h"
#include "dcrypto/dcrypto.h"
-#include "device_state.h"
#include "ec_version.h"
+#include "endian.h"
+#include "extension.h"
+#include "flash.h"
#include "flash_config.h"
#include "gpio.h"
#include "hooks.h"
+#include "i2c.h"
#include "i2cs.h"
#include "init_chip.h"
#include "nvmem.h"
+#include "nvmem_vars.h"
+#include "rdd.h"
#include "registers.h"
+#include "scratch_reg1.h"
+#include "signed_header.h"
#include "spi.h"
#include "system.h"
+#include "system_chip.h"
#include "task.h"
#include "tpm_registers.h"
#include "trng.h"
+#include "uart_bitbang.h"
#include "uartn.h"
#include "usb_descriptor.h"
#include "usb_hid.h"
+#include "usb_i2c.h"
#include "usb_spi.h"
#include "util.h"
+#include "wp.h"
/* Define interrupt and gpio structs */
#include "gpio_list.h"
@@ -50,7 +62,8 @@
#undef SHA_DIGEST_SIZE
#include "Implementation.h"
-#define NVMEM_CR50_SIZE 300
+#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args)
+
#define NVMEM_TPM_SIZE ((sizeof((struct nvmem_partition *)0)->buffer) \
- NVMEM_CR50_SIZE)
@@ -59,6 +72,8 @@
* should be set to
*
* NVMEM_PARTITION_SIZE - NVMEM_CR50_SIZE - 8
+ *
+ * Both of these macros are defined in board.h.
*/
BUILD_ASSERT(NVMEM_TPM_SIZE == NV_MEMORY_SIZE);
@@ -69,7 +84,297 @@ uint32_t nvmem_user_sizes[NVMEM_NUM_USERS] = {
};
/* Board specific configuration settings */
-static uint32_t board_properties;
+static uint32_t board_properties; /* Mainly used as a cache for strap config. */
+static uint8_t reboot_request_posted;
+
+/* Which UARTs we'd like to be able to bitbang. */
+struct uart_bitbang_properties bitbang_config = {
+ .uart = UART_EC,
+ .tx_gpio = GPIO_DETECT_SERVO, /* This is TX to EC console. */
+ .rx_gpio = GPIO_EC_TX_CR50_RX,
+ /*
+ * The rx/tx_pinmux_regval values MUST agree with the pin config for
+ * both the TX and RX GPIOs in gpio.inc. Don't change one without
+ * changing the other.
+ */
+ .tx_pinmux_reg = GBASE(PINMUX) + GOFFSET(PINMUX, DIOB5_SEL),
+ .tx_pinmux_regval = GC_PINMUX_GPIO1_GPIO3_SEL,
+ .rx_pinmux_reg = GBASE(PINMUX) + GOFFSET(PINMUX, DIOB6_SEL),
+ .rx_pinmux_regval = GC_PINMUX_GPIO1_GPIO4_SEL,
+};
+
+extern struct deferred_data ec_uart_deferred__data;
+void ec_tx_cr50_rx(enum gpio_signal signal)
+{
+ uart_bitbang_receive_char(UART_EC);
+ /* Let the USART module know that there's new bits to consume. */
+ hook_call_deferred(&ec_uart_deferred__data, 0);
+}
+
+const char *device_state_names[] = {
+ "init",
+ "init_debouncing",
+ "init_rx_only",
+ "disconnected",
+ "off",
+ "undetectable",
+ "connected",
+ "on",
+ "debouncing",
+ "unknown"
+};
+BUILD_ASSERT(ARRAY_SIZE(device_state_names) == DEVICE_STATE_COUNT);
+
+const char *device_state_name(enum device_state state)
+{
+ if (state >= 0 && state < DEVICE_STATE_COUNT)
+ return device_state_names[state];
+ else
+ return "?";
+}
+
+int board_use_plt_rst(void)
+{
+ return !!(board_properties & BOARD_USE_PLT_RESET);
+}
+
+int board_deep_sleep_allowed(void)
+{
+ return board_use_plt_rst();
+}
+
+int board_detect_ap_with_tpm_rst(void)
+{
+ return board_use_plt_rst();
+}
+
+int board_rst_pullup_needed(void)
+{
+ return !!(board_properties & BOARD_NEEDS_SYS_RST_PULL_UP);
+}
+
+int board_tpm_uses_i2c(void)
+{
+ return !!(board_properties & BOARD_SLAVE_CONFIG_I2C);
+}
+
+int board_tpm_uses_spi(void)
+{
+ return !!(board_properties & BOARD_SLAVE_CONFIG_SPI);
+}
+
+/* Get header address of the backup RW copy. */
+const struct SignedHeader *get_other_rw_addr(void)
+{
+ if (system_get_image_copy() == SYSTEM_IMAGE_RW)
+ return (const struct SignedHeader *)
+ get_program_memory_addr(SYSTEM_IMAGE_RW_B);
+
+ return (const struct SignedHeader *)
+ get_program_memory_addr(SYSTEM_IMAGE_RW);
+}
+
+/* Return true if the other RW is not ready to run. */
+static int other_rw_is_inactive(void)
+{
+ const struct SignedHeader *header = get_other_rw_addr();
+
+ return !!(header->image_size & TOP_IMAGE_SIZE_BIT);
+}
+
+/* I2C Port definition */
+const struct i2c_port_t i2c_ports[] = {
+ {"master", I2C_PORT_MASTER, 100,
+ GPIO_I2C_SCL_INA, GPIO_I2C_SDA_INA},
+};
+const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports);
+
+/* Strapping pin info structure */
+#define STRAP_PIN_DELAY_USEC 100
+enum strap_list {
+ a0,
+ a1,
+ b0,
+ b1,
+};
+
+struct strap_desc {
+ /* GPIO enum from gpio.inc for the strap pin */
+ uint8_t gpio_signal;
+ /* Offset into pinmux register section for pad SEL register */
+ uint8_t sel_offset;
+ /* Entry in the pinmux peripheral selector table for pad */
+ uint8_t pad_select;
+ const char *pad_name;
+};
+
+struct board_cfg {
+ /* Value the strap pins should read for a given board */
+ uint8_t strap_cfg;
+ /* Properties required for a given board */
+ uint32_t board_properties;
+};
+
+/*
+ * This table contains both the GPIO and pad specific information required to
+ * configure each strapping pin to be either a GPIO input or output.
+ */
+const struct strap_desc strap_regs[] = {
+ {GPIO_STRAP_A0, GOFFSET(PINMUX, DIOA1_SEL), GC_PINMUX_DIOA1_SEL, "a1"},
+ {GPIO_STRAP_A1, GOFFSET(PINMUX, DIOA9_SEL), GC_PINMUX_DIOA9_SEL, "a9"},
+ {GPIO_STRAP_B0, GOFFSET(PINMUX, DIOA6_SEL), GC_PINMUX_DIOA6_SEL, "a6"},
+ {GPIO_STRAP_B1, GOFFSET(PINMUX, DIOA12_SEL), GC_PINMUX_DIOA12_SEL,
+ "a12"},
+};
+
+#define BOARD_PROPERTIES_DEFAULT (BOARD_SLAVE_CONFIG_I2C | BOARD_USE_PLT_RESET)
+static struct board_cfg board_cfg_table[] = {
+ /* SPI Variants: DIOA12 = 1M PD, DIOA6 = 1M PD */
+ /* Kevin/Gru: DI0A9 = 5k PD, DIOA1 = 1M PU */
+ { 0x02, BOARD_SLAVE_CONFIG_SPI | BOARD_NEEDS_SYS_RST_PULL_UP },
+ /* Poppy: DI0A9 = 1M PU, DIOA1 = 1M PU */
+ { 0x0A, BOARD_SLAVE_CONFIG_SPI | BOARD_USE_PLT_RESET },
+
+ /* I2C Variants: DIOA9 = 1M PD, DIOA1 = 1M PD */
+ /* Reef/Eve: DIOA12 = 5k PD, DIOA6 = 1M PU */
+ { 0x20, BOARD_SLAVE_CONFIG_I2C | BOARD_USE_PLT_RESET },
+ /* Rowan: DIOA12 = 5k PD, DIOA6 = 5k PU */
+ { 0x30, BOARD_SLAVE_CONFIG_I2C },
+};
+
+void post_reboot_request(void)
+{
+ /* Reboot the device next time TPM reset is requested. */
+ reboot_request_posted = 1;
+}
+
+/*****************************************************************************/
+/* */
+
+/*
+ * Battery cutoff monitor is needed on the devices where hardware alone does
+ * not provide proper battery cutoff functionality.
+ *
+ * The sequence is as follows: set up an interrupt to react to the charger
+ * disconnect event. When the interrupt happens observe status of the buttons
+ * connected to PWRB_IN and KEY0_IN.
+ *
+ * If both are pressed, start the 5 second timeout, while keeping monitoring
+ * the charger connection state. If it remains disconnected for the entire
+ * duration - generate 5 second pulses on EC_RST_L and BAT_EN outputs.
+ *
+ * In reality the BAT_EN output pulse will cause the complete power cut off,
+ * so strictly speaking the code does not need to do anything once BAT_EN
+ * output is deasserted.
+ */
+
+/* Time to wait before initiating battery cutoff procedure. */
+#define CUTOFF_TIMEOUT_US (5 * SECOND)
+
+/* A timeout hook to run in the end of the 5 s interval. */
+static void ac_stayed_disconnected(void)
+{
+ uint32_t saved_override_state;
+
+ CPRINTS("%s", __func__);
+
+ /* assert EC_RST_L and deassert BAT_EN */
+ GREG32(RBOX, ASSERT_EC_RST) = 1;
+
+ /*
+ * BAT_EN needs to use the RBOX override ability, bit 1 is battery
+ * disable bit.
+ */
+ saved_override_state = GREG32(RBOX, OVERRIDE_OUTPUT);
+ GWRITE_FIELD(RBOX, OVERRIDE_OUTPUT, VAL, 0); /* Setting it to zero. */
+ GWRITE_FIELD(RBOX, OVERRIDE_OUTPUT, OEN, 1);
+ GWRITE_FIELD(RBOX, OVERRIDE_OUTPUT, EN, 1);
+
+
+ msleep(5000);
+
+ /*
+ * The system was supposed to be shut down the moment battery
+ * disconnect was asserted, but if we made it here we might as well
+ * restore the original state.
+ */
+ GREG32(RBOX, OVERRIDE_OUTPUT) = saved_override_state;
+ GREG32(RBOX, ASSERT_EC_RST) = 0;
+}
+DECLARE_DEFERRED(ac_stayed_disconnected);
+
+/*
+ * Just a shortcut to make use of these AC power interrupt states better
+ * readable. RED means rising edge and FED means falling edge.
+ */
+enum {
+ ac_pres_red = GC_RBOX_INT_STATE_INTR_AC_PRESENT_RED_MASK,
+ ac_pres_fed = GC_RBOX_INT_STATE_INTR_AC_PRESENT_FED_MASK,
+ buttons_not_pressed = GC_RBOX_CHECK_INPUT_KEY0_IN_MASK |
+ GC_RBOX_CHECK_INPUT_PWRB_IN_MASK
+};
+
+/*
+ * ISR reacting to both falling and raising edges of the AC_PRESENT signal.
+ * Falling edge indicates AC no longer present (removal of the charger cable)
+ * and rising edge indicates AP present (insertion of charger cable).
+ */
+static void ac_power_state_changed(void)
+{
+ uint32_t req;
+
+ /* Get current status and clear it. */
+ req = GREG32(RBOX, INT_STATE) & (ac_pres_red | ac_pres_fed);
+ GREG32(RBOX, INT_STATE) = req;
+
+ CPRINTS("AC: %c%c",
+ req & ac_pres_red ? 'R' : '-',
+ req & ac_pres_fed ? 'F' : '-');
+
+ /* Delay sleep so RDD state machines can stabilize */
+ delay_sleep_by(5 * SECOND);
+
+ /* The remaining code is only used for battery cutoff */
+ if (!system_battery_cutoff_support_required())
+ return;
+
+ /* Raising edge gets priority, stop timeout timer and go. */
+ if (req & ac_pres_red) {
+ hook_call_deferred(&ac_stayed_disconnected_data, -1);
+ return;
+ }
+
+ /*
+ * If this is not a falling edge, or either of the buttons is not
+ * pressed - bail out.
+ */
+ if (!(req & ac_pres_fed) ||
+ (GREG32(RBOX, CHECK_INPUT) & buttons_not_pressed))
+ return;
+
+ /*
+ * Charger cable was yanked while the power and key0 buttons were kept
+ * pressed - user wants a battery cut off.
+ */
+ hook_call_deferred(&ac_stayed_disconnected_data, CUTOFF_TIMEOUT_US);
+}
+DECLARE_IRQ(GC_IRQNUM_RBOX0_INTR_AC_PRESENT_RED_INT, ac_power_state_changed, 1);
+DECLARE_IRQ(GC_IRQNUM_RBOX0_INTR_AC_PRESENT_FED_INT, ac_power_state_changed, 1);
+
+/* Enable interrupts on plugging in and yanking out of the charger cable. */
+static void init_ac_detect(void)
+{
+ /* It is set in idle.c also. */
+ GWRITE_FIELD(RBOX, WAKEUP, ENABLE, 1);
+
+ GWRITE_FIELD(RBOX, INT_ENABLE, INTR_AC_PRESENT_RED, 1);
+ GWRITE_FIELD(RBOX, INT_ENABLE, INTR_AC_PRESENT_FED, 1);
+
+ task_enable_irq(GC_IRQNUM_RBOX0_INTR_AC_PRESENT_RED_INT);
+ task_enable_irq(GC_IRQNUM_RBOX0_INTR_AC_PRESENT_FED_INT);
+}
+/* */
+/*****************************************************************************/
/*
* There's no way to trigger on both rising and falling edges, so force a
@@ -80,6 +385,20 @@ static uint32_t board_properties;
BUILD_ASSERT(((flags) & GPIO_INT_BOTH) != GPIO_INT_BOTH);
#include "gpio.wrap"
+/**
+ * Reset wake logic
+ *
+ * If any wake pins are edge triggered, the pad logic latches the wakeup. Clear
+ * and restore EXITEN0 to reset the wakeup logic.
+ */
+static void reset_wake_logic(void)
+{
+ uint32_t exiten = GREG32(PINMUX, EXITEN0);
+
+ GREG32(PINMUX, EXITEN0) = 0;
+ GREG32(PINMUX, EXITEN0) = exiten;
+}
+
static void init_pmu(void)
{
clock_enable_module(MODULE_PMU, 1);
@@ -96,7 +415,11 @@ static void init_pmu(void)
void pmu_wakeup_interrupt(void)
{
- int exiten, wakeup_src;
+ int wakeup_src;
+ static uint8_t count;
+ static uint8_t ws;
+ static uint8_t line_length;
+ static const char wheel[] = { '|', '/', '-', '\\' };
delay_sleep_by(1 * MSEC);
@@ -108,28 +431,37 @@ void pmu_wakeup_interrupt(void)
/* Clear pmu reset */
GWRITE(PMU, CLRRST, 1);
+ /*
+ * This will print the next state of the "rotating wheel" every time
+ * cr50 resumes from regular sleep (8 is the ASCII code for
+ * 'backspace'). Each time wake source changes, its hex value is
+ * printed out preceded by a space.
+ *
+ * In steady state when there is no other activity Cr50 wakes up every
+ * half second for HOOK_TICK, so that is the rate the wheel will be
+ * spinning at when device is idle.
+ */
+ if (ws == wakeup_src) {
+ ccprintf("%c%c%c%2x%c", 8, 8, 8, ws,
+ wheel[count++ % sizeof(wheel)]);
+ } else {
+ ws = wakeup_src;
+ line_length += 3;
+ if (line_length > 50) {
+ ccprintf("\n");
+ line_length = 0;
+ }
+ ccprintf(" %2x ", wakeup_src);
+ }
+
if (wakeup_src & GC_PMU_EXITPD_SRC_PIN_PD_EXIT_MASK) {
- /*
- * If any wake pins are edge triggered, the pad logic latches
- * the wakeup. Clear EXITEN0 to reset the wakeup logic.
- */
- exiten = GREG32(PINMUX, EXITEN0);
- GREG32(PINMUX, EXITEN0) = 0;
- GREG32(PINMUX, EXITEN0) = exiten;
+ reset_wake_logic();
/*
* Delay sleep long enough for a SPI slave transaction to start
* or for the system to be reset.
*/
- delay_sleep_by(3 * MINUTE);
-
- /*
- * If sys_rst_l is configured to wake on low and the signal is
- * low then call sys_rst_asserted
- */
- if (!gpio_get_level(GPIO_SYS_RST_L_IN) &&
- GREAD_FIELD(PINMUX, EXITINV0, DIOM0))
- sys_rst_asserted(GPIO_SYS_RST_L_IN);
+ delay_sleep_by(5 * SECOND);
}
/* Trigger timer0 interrupt */
@@ -139,7 +471,6 @@ void pmu_wakeup_interrupt(void)
/* Trigger timer1 interrupt */
if (wakeup_src & GC_PMU_EXITPD_SRC_TIMELS0_PD_EXIT_TIMER1_MASK)
task_trigger_irq(GC_IRQNUM_TIMELS0_TIMINT1);
-
}
DECLARE_IRQ(GC_IRQNUM_PMU_INTR_WAKEUP_INT, pmu_wakeup_interrupt, 1);
@@ -151,107 +482,272 @@ void board_configure_deep_sleep_wakepins(void)
* resume.
*/
GWRITE_FIELD(PINMUX, EXITEN0, DIOA12, 0); /* SPS_CS_L */
- /* TODO remove i2cs wake event */
+ GWRITE_FIELD(PINMUX, EXITEN0, DIOA1, 0); /* I2CS_SDA */
+ GWRITE_FIELD(PINMUX, EXITEN0, DIOA9, 0); /* I2CS_SCL */
+
+ /* Remove the pulldown on EC uart tx and disable the input */
+ GWRITE_FIELD(PINMUX, DIOB5_CTL, PD, 0);
+ GWRITE_FIELD(PINMUX, DIOB5_CTL, IE, 0);
/*
- * Whether it is a short pulse or long one waking on the rising edge is
- * fine because the goal of sys_rst is to reset the TPM and after
- * resuming from deep sleep the TPM will be reset. Cr50 doesn't need to
- * read the low value and then reset.
- *
- * Configure cr50 to resume on the rising edge of sys_rst_l
+ * Whether it is a short pulse or long one waking on the high level is
+ * fine because the goal of the system reset signal is to reset the
+ * TPM and after resuming from deep sleep the TPM will be reset. Cr50
+ * doesn't need to read the low value and then reset.
*/
- /* Disable sys_rst_l as a wake pin */
- GWRITE_FIELD(PINMUX, EXITEN0, DIOM0, 0);
- /* Reconfigure and reenable it. */
- GWRITE_FIELD(PINMUX, EXITEDGE0, DIOM0, 1); /* edge sensitive */
- GWRITE_FIELD(PINMUX, EXITINV0, DIOM0, 0); /* wake on high */
- GWRITE_FIELD(PINMUX, EXITEN0, DIOM0, 1); /* enable powerdown exit */
+ if (board_use_plt_rst()) {
+ /*
+ * If the board includes plt_rst_l, configure Cr50 to resume on
+ * the rising edge of this signal.
+ */
+ /* Disable plt_rst_l as a wake pin */
+ GWRITE_FIELD(PINMUX, EXITEN0, DIOM3, 0);
+ /*
+ * Reconfigure it to be level sensitive so that we are
+ * guaranteed to wake up if the level turns up, no need to
+ * worry about missing the rising edge.
+ */
+ GWRITE_FIELD(PINMUX, EXITEDGE0, DIOM3, 0);
+ GWRITE_FIELD(PINMUX, EXITINV0, DIOM3, 0); /* wake on high */
+ /* enable powerdown exit */
+ GWRITE_FIELD(PINMUX, EXITEN0, DIOM3, 1);
+ } else {
+ /*
+ * Configure cr50 to wake when sys_rst_l is asserted. It is
+ * wake on low to make sure that Cr50 is awake to detect the
+ * rising edge of sys_rst_l. This will keep Cr50 awake the
+ * entire time sys_rst_l is asserted.
+ */
+ /* Disable sys_rst_l as a wake pin */
+ GWRITE_FIELD(PINMUX, EXITEN0, DIOM0, 0);
+ /* Reconfigure and reenable it. */
+ GWRITE_FIELD(PINMUX, EXITEDGE0, DIOM0, 0); /* level sensitive */
+ GWRITE_FIELD(PINMUX, EXITINV0, DIOM0, 1); /* wake on low */
+ /* enable powerdown exit */
+ GWRITE_FIELD(PINMUX, EXITEN0, DIOM0, 1);
+ }
+
+ if (!board_detect_ap_with_tpm_rst()) {
+ /*
+ * DIOA3 is GPIO_DETECT_AP which is used to detect if the AP
+ * is in S0. If the AP is in s0, cr50 should not be in deep
+ * sleep so wake up.
+ */
+ GWRITE_FIELD(PINMUX, EXITEDGE0, DIOA3, 0); /* level sensitive */
+ GWRITE_FIELD(PINMUX, EXITINV0, DIOA3, 0); /* wake on high */
+ GWRITE_FIELD(PINMUX, EXITEN0, DIOA3, 1);
+ }
}
-static void init_interrupts(void)
+static void deferred_tpm_rst_isr(void);
+DECLARE_DEFERRED(deferred_tpm_rst_isr);
+
+static void configure_board_specific_gpios(void)
{
- int i;
- uint32_t exiten = GREG32(PINMUX, EXITEN0);
+ /* Add a pullup to sys_rst_l */
+ if (board_rst_pullup_needed())
+ GWRITE_FIELD(PINMUX, DIOM0_CTL, PU, 1);
- /* Clear wake pin interrupts */
- GREG32(PINMUX, EXITEN0) = 0;
- GREG32(PINMUX, EXITEN0) = exiten;
+ /*
+ * Connect either plt_rst_l or sys_rst_l to GPIO_TPM_RST_L based on the
+ * board type. This signal is used to monitor AP resets and reset the
+ * TPM.
+ *
+ * Also configure these pins to be wake triggers on the rising edge,
+ * this will apply to regular sleep only, entering deep sleep would
+ * reconfigure this.
+ *
+ * plt_rst_l is on diom3, and sys_rst_l is on diom0.
+ */
+ if (board_use_plt_rst()) {
+ /* Use plt_rst_l as the tpm reset signal. */
+ GWRITE(PINMUX, GPIO1_GPIO0_SEL, GC_PINMUX_DIOM3_SEL);
- /* Enable all GPIO interrupts */
- for (i = 0; i < gpio_ih_count; i++)
- if (gpio_list[i].flags & GPIO_INT_ANY)
- gpio_enable_interrupt(i);
-}
+ /* Enable the input */
+ GWRITE_FIELD(PINMUX, DIOM3_CTL, IE, 1);
-enum permission_level {
- PERMISSION_LOW = 0x00,
- PERMISSION_MEDIUM = 0x33, /* APPS run at medium */
- PERMISSION_HIGH = 0x3C,
- PERMISSION_HIGHEST = 0x55
-};
+ /*
+ * Make plt_rst_l routed to DIOM3 a low level sensitive wake
+ * source. This way when a plt_rst_l pulse comes along while
+ * H1 is in sleep, the H1 wakes from sleep first, enabling all
+ * necessary clocks, and becomes ready to generate an
+ * interrupt on the rising edge of plt_rst_l.
+ *
+ * It takes at most 150 us to wake up, and the pulse is at
+ * least 1ms long.
+ */
+ GWRITE_FIELD(PINMUX, EXITEDGE0, DIOM3, 0);
+ GWRITE_FIELD(PINMUX, EXITINV0, DIOM3, 1);
+
+ /* Enable powerdown exit on DIOM3 */
+ GWRITE_FIELD(PINMUX, EXITEN0, DIOM3, 1);
+ } else {
+ /* Use sys_rst_l as the tpm reset signal. */
+ GWRITE(PINMUX, GPIO1_GPIO0_SEL, GC_PINMUX_DIOM0_SEL);
+ /* Enable the input */
+ GWRITE_FIELD(PINMUX, DIOM0_CTL, IE, 1);
+
+ /* Set to be level sensitive */
+ GWRITE_FIELD(PINMUX, EXITEDGE0, DIOM0, 0);
+ /* wake on low */
+ GWRITE_FIELD(PINMUX, EXITINV0, DIOM0, 1);
+ /* Enable powerdown exit on DIOM0 */
+ GWRITE_FIELD(PINMUX, EXITEN0, DIOM0, 1);
+ }
+ if (!board_detect_ap_with_tpm_rst()) {
+ /* Use AP UART TX as the DETECT AP signal. */
+ GWRITE(PINMUX, GPIO1_GPIO1_SEL, GC_PINMUX_DIOA3_SEL);
+ /* Enable the input */
+ GWRITE_FIELD(PINMUX, DIOA3_CTL, IE, 1);
+ }
+}
-/* Drop run level to at least medium. */
-static void init_runlevel(const enum permission_level desired_level)
-{
- volatile uint32_t *const reg_addrs[] = {
- /* CPU's use of the system peripheral bus */
- GREG32_ADDR(GLOBALSEC, CPU0_S_PERMISSION),
- /* CPU's use of the system bus via the debug access port */
- GREG32_ADDR(GLOBALSEC, CPU0_S_DAP_PERMISSION),
- /* DMA's use of the system peripheral bus */
- GREG32_ADDR(GLOBALSEC, DDMA0_PERMISSION),
- /* Current software level affects which (if any) scratch
- * registers can be used for a warm boot hardware-verified
- * jump. */
- GREG32_ADDR(GLOBALSEC, SOFTWARE_LVL),
- };
- int i;
+void decrement_retry_counter(void)
+{
+ uint32_t counter = GREG32(PMU, LONG_LIFE_SCRATCH0);
- /* Permission registers drop by 1 level (e.g. HIGHEST -> HIGH)
- * each time a write is encountered (the value written does
- * not matter). So we repeat writes and reads, until the
- * desired level is reached.
- */
- for (i = 0; i < ARRAY_SIZE(reg_addrs); i++) {
- uint32_t current_level;
-
- while (1) {
- current_level = *reg_addrs[i];
- if (current_level <= desired_level)
- break;
- *reg_addrs[i] = desired_level;
- }
+ if (counter) {
+ GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG0, 1);
+ GREG32(PMU, LONG_LIFE_SCRATCH0) = counter - 1;
+ GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG0, 0);
}
}
-static void configure_board_specific_gpios(void)
+static uint8_t mismatched_board_id;
+
+int board_id_is_mismatched(void)
{
- /* Add a pullup to sys_rst_l */
- if (system_get_board_properties() & BOARD_NEEDS_SYS_RST_PULL_UP)
- GWRITE_FIELD(PINMUX, DIOM0_CTL, PU, 1);
+ return !!mismatched_board_id;
+}
+
+static void check_board_id_mismatch(void)
+{
+ if (!board_id_mismatch(NULL))
+ return;
+
+ if (system_rollback_detected()) {
+ /*
+ * We are in a rollback, the other image must be no good.
+ * Let's keep going with the TPM disabled, only updates will
+ * be allowed.
+ */
+ mismatched_board_id = 1;
+ ccprintf("Board ID mismatched, but can not reboot.\n");
+
+ /* Force CCD disabled */
+ ccd_disable();
+
+ return;
+ }
+
+ system_ensure_rollback();
+ ccprintf("Rebooting due to board ID mismatch\n");
+ cflush();
+ system_reset(0);
}
/* Initialize board. */
static void board_init(void)
{
+#ifdef CR50_DEV
+ static enum ccd_state ccd_init_state = CCD_STATE_OPENED;
+#else
+ static enum ccd_state ccd_init_state = CCD_STATE_LOCKED;
+#endif
+
+ /*
+ * Deep sleep resets should be considered valid and should not impact
+ * the rolling reboot count.
+ */
+ if (system_get_reset_flags() & RESET_FLAG_HIBERNATE)
+ decrement_retry_counter();
configure_board_specific_gpios();
init_pmu();
- init_interrupts();
+ reset_wake_logic();
init_trng();
init_jittery_clock(1);
init_runlevel(PERMISSION_MEDIUM);
/* Initialize NvMem partitions */
nvmem_init();
+ /* Initialize the persistent storage. */
+ initvars();
- /* TODO(crosbug.com/p/49959): For now, leave flash WP unlocked */
- GREG32(RBOX, EC_WP_L) = 1;
+ /*
+ * If this was a low power wake and not a rollback, restore the ccd
+ * state from the long-life register.
+ */
+ if ((system_get_reset_flags() & RESET_FLAG_HIBERNATE) &&
+ !system_rollback_detected()) {
+ ccd_init_state = (GREG32(PMU, LONG_LIFE_SCRATCH1) &
+ BOARD_CCD_STATE) >> BOARD_CCD_SHIFT;
+ }
+
+ /* Load case-closed debugging config. Must be after initvars(). */
+ ccd_config_init(ccd_init_state);
+
+ system_update_rollback_mask_with_both_imgs();
/* Indication that firmware is running, for debug purposes. */
GREG32(PMU, PWRDN_SCRATCH16) = 0xCAFECAFE;
+
+ /*
+ * Call the function twice to make it harder to glitch execution into
+ * passing the check when not supposed to.
+ */
+ check_board_id_mismatch();
+ check_board_id_mismatch();
+
+ /*
+ * Enable TPM reset GPIO interrupt.
+ *
+ * If the TPM_RST_L signal is already high when cr50 wakes up or
+ * transitions to high before we are able to configure the gpio then we
+ * will have missed the edge and the tpm reset isr will not get
+ * called. Check that we haven't already missed the rising edge. If we
+ * have alert tpm_rst_isr.
+ */
+ gpio_enable_interrupt(GPIO_TPM_RST_L);
+ if (gpio_get_level(GPIO_TPM_RST_L))
+ hook_call_deferred(&deferred_tpm_rst_isr_data, 0);
+
+ /*
+ * Start monitoring AC detect to wake Cr50 from deep sleep. This is
+ * needed to detect RDD cable changes in deep sleep. AC detect is also
+ * used for battery cutoff software support on detachable devices.
+ */
+ init_ac_detect();
+ init_rdd_state();
+
+ /* Initialize write protect. Must be after CCD config init. */
+ init_wp_state();
+
+ /*
+ * Note that the AP, EC, and servo state machines do not have explicit
+ * init_xxx_state() functions, because they don't need to configure
+ * registers prior to starting their state machines. Their state
+ * machines run in HOOK_SECOND, which first triggers right after
+ * HOOK_INIT, not at +1.0 seconds.
+ */
}
DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT);
+/**
+ * Hook for CCD config loaded/changed.
+ */
+static void board_ccd_config_changed(void)
+{
+ /* Store the current CCD state so we can restore it after deep sleep */
+ GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG1, 1);
+ GREG32(PMU, LONG_LIFE_SCRATCH1) &= ~BOARD_CCD_STATE;
+ GREG32(PMU, LONG_LIFE_SCRATCH1) |= (ccd_get_state() << BOARD_CCD_SHIFT)
+ & BOARD_CCD_STATE;
+ GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG1, 0);
+
+ /* Update CCD state */
+ ccd_update_state();
+}
+DECLARE_HOOK(HOOK_CCD_CHANGE, board_ccd_config_changed, HOOK_PRIO_DEFAULT);
+
#if defined(CONFIG_USB)
const void * const usb_strings[] = {
[USB_STR_DESC] = usb_string_desc,
@@ -260,11 +756,13 @@ const void * const usb_strings[] = {
[USB_STR_VERSION] = USB_STRING_DESC(CROS_EC_VERSION32),
[USB_STR_CONSOLE_NAME] = USB_STRING_DESC("Shell"),
[USB_STR_BLOB_NAME] = USB_STRING_DESC("Blob"),
- [USB_STR_HID_NAME] = USB_STRING_DESC("PokeyPokey"),
+ [USB_STR_HID_KEYBOARD_NAME] = USB_STRING_DESC("PokeyPokey"),
[USB_STR_AP_NAME] = USB_STRING_DESC("AP"),
[USB_STR_EC_NAME] = USB_STRING_DESC("EC"),
[USB_STR_UPGRADE_NAME] = USB_STRING_DESC("Firmware upgrade"),
[USB_STR_SPI_NAME] = USB_STRING_DESC("AP EC upgrade"),
+ [USB_STR_SERIALNO] = USB_STRING_DESC(DEFAULT_SERIALNO),
+ [USB_STR_I2C_NAME] = USB_STRING_DESC("I2C"),
};
BUILD_ASSERT(ARRAY_SIZE(usb_strings) == USB_STR_COUNT);
#endif
@@ -305,34 +803,68 @@ int flash_regions_to_enable(struct g_flash_region *regions,
/* Enable access to the NVRAM partition A region */
regions[1].reg_base = CONFIG_MAPPED_STORAGE_BASE +
- CONFIG_FLASH_NVMEM_OFFSET_A;
- regions[1].reg_size = NVMEM_PARTITION_SIZE;
+ CFG_TOP_A_OFF;
+ regions[1].reg_size = CFG_TOP_SIZE;
regions[1].reg_perms = FLASH_REGION_EN_ALL;
/* Enable access to the NVRAM partition B region */
regions[2].reg_base = CONFIG_MAPPED_STORAGE_BASE +
- CONFIG_FLASH_NVMEM_OFFSET_B;
- regions[2].reg_size = NVMEM_PARTITION_SIZE;
+ CFG_TOP_B_OFF;
+ regions[2].reg_size = CFG_TOP_SIZE;
regions[2].reg_perms = FLASH_REGION_EN_ALL;
return 3;
}
-#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args)
-
-/* This is the interrupt handler to react to SYS_RST_L_IN */
-void sys_rst_asserted(enum gpio_signal signal)
+/**
+ * Deferred TPM reset interrupt handling
+ *
+ * This is always called from the HOOK task.
+ */
+static void deferred_tpm_rst_isr(void)
{
+ CPRINTS("%s", __func__);
+
/*
- * Cr50 drives SYS_RST_L in certain scenarios, in those cases
- * this signal's assertion should be ignored here.
+ * If the board uses TPM reset to detect the AP, connect AP. This is
+ * the only way those boards connect; they don't examine AP UART TX.
*/
- CPRINTS("%s", __func__);
- if (usb_spi_update_in_progress() || is_sys_rst_asserted())
+ if (board_detect_ap_with_tpm_rst())
+ set_ap_on_deferred();
+
+ /*
+ * If no reboot request is posted, OR if the other RW's header is not
+ * ready to run - do not try rebooting the device, just reset the
+ * TPM.
+ *
+ * The inactive header will have to be restored by the appropriate
+ * vendor command, the device will be rebooted then.
+ */
+ if (!reboot_request_posted || other_rw_is_inactive()) {
+ /* Reset TPM, no need to wait for completion. */
+ tpm_reset_request(0, 0);
return;
+ }
- cflush();
- system_reset(0);
+ /*
+ * Reset TPM and wait to completion to make sure nvmem is
+ * committed before reboot.
+ */
+ tpm_reset_request(1, 0);
+
+ /* This will never return. */
+ system_reset(SYSTEM_RESET_MANUALLY_TRIGGERED | SYSTEM_RESET_HARD);
+}
+
+/**
+ * Handle TPM_RST_L deasserting
+ *
+ * This can also be called explicitly from AP detection, if it thinks the
+ * interrupt handler missed the rising edge.
+ */
+void tpm_rst_deasserted(enum gpio_signal signal)
+{
+ hook_call_deferred(&deferred_tpm_rst_isr_data, 0);
}
void assert_sys_rst(void)
@@ -373,6 +905,58 @@ int is_sys_rst_asserted(void)
&& (gpio_get_level(GPIO_SYS_RST_L_OUT) == 0);
}
+/**
+ * Reboot the AP
+ */
+void board_reboot_ap(void)
+{
+ assert_sys_rst();
+ msleep(20);
+ deassert_sys_rst();
+}
+
+/**
+ * Console command to toggle system (AP) reset
+ */
+static int command_sys_rst(int argc, char **argv)
+{
+ int val;
+ char *e;
+ int ms = 20;
+
+ if (argc > 1) {
+ if (!ccd_is_cap_enabled(CCD_CAP_REBOOT_EC_AP))
+ return EC_ERROR_ACCESS_DENIED;
+
+ if (!strcasecmp("pulse", argv[1])) {
+ if (argc == 3) {
+ ms = strtoi(argv[2], &e, 0);
+ if (*e)
+ return EC_ERROR_PARAM2;
+ }
+ ccprintf("Pulsing AP reset for %dms\n", ms);
+ assert_sys_rst();
+ msleep(ms);
+ deassert_sys_rst();
+ } else if (parse_bool(argv[1], &val)) {
+ if (val)
+ assert_sys_rst();
+ else
+ deassert_sys_rst();
+ } else
+ return EC_ERROR_PARAM1;
+ }
+
+ ccprintf("SYS_RST_L is %s\n", is_sys_rst_asserted() ?
+ "asserted" : "deasserted");
+
+ return EC_SUCCESS;
+
+}
+DECLARE_SAFE_CONSOLE_COMMAND(sysrst, command_sys_rst,
+ "[pulse [time] | <BOOLEAN>]",
+ "Assert/deassert SYS_RST_L to reset the AP");
+
void assert_ec_rst(void)
{
GWRITE(RBOX, ASSERT_EC_RST, 1);
@@ -387,205 +971,268 @@ int is_ec_rst_asserted(void)
return GREAD(RBOX, ASSERT_EC_RST);
}
-void nvmem_compute_sha(uint8_t *p_buf, int num_bytes,
- uint8_t *p_sha, int sha_len)
-{
- uint8_t sha1_digest[SHA_DIGEST_SIZE];
- /*
- * Taking advantage of the built in dcrypto engine to generate
- * a CRC-like value that can be used to validate contents of an
- * NvMem partition. Only using the lower 4 bytes of the sha1 hash.
- */
- DCRYPTO_SHA1_hash((uint8_t *)p_buf,
- num_bytes,
- sha1_digest);
- memcpy(p_sha, sha1_digest, sha_len);
-}
-
-static void device_state_changed(enum device_type device,
- enum device_state state)
+/**
+ * Console command to toggle EC reset
+ */
+static int command_ec_rst(int argc, char **argv)
{
- device_set_state(device, state);
+ int val;
+
+ if (argc > 1) {
+ if (!ccd_is_cap_enabled(CCD_CAP_REBOOT_EC_AP))
+ return EC_ERROR_ACCESS_DENIED;
+
+ if (!strcasecmp("pulse", argv[1])) {
+ ccprintf("Pulsing EC reset\n");
+ assert_ec_rst();
+ usleep(200);
+ deassert_ec_rst();
+ } else if (parse_bool(argv[1], &val)) {
+ if (val)
+ assert_ec_rst();
+ else
+ deassert_ec_rst();
+ } else
+ return EC_ERROR_PARAM1;
+ }
- /* Disable interrupts */
- gpio_disable_interrupt(device_states[device].detect_on);
- gpio_disable_interrupt(device_states[device].detect_off);
+ ccprintf("EC_RST_L is %s\n", is_ec_rst_asserted() ?
+ "asserted" : "deasserted");
- /*
- * We've determined the device state, so cancel any deferred callbacks.
- */
- hook_call_deferred(device_states[device].deferred, -1);
+ return EC_SUCCESS;
}
+DECLARE_SAFE_CONSOLE_COMMAND(ecrst, command_ec_rst,
+ "[pulse | <BOOLEAN>]",
+ "Assert/deassert EC_RST_L to reset the EC (and AP)");
/*
- * If the UART is enabled we cant tell anything about the
- * servo state, so disable servo detection.
+ * This function duplicates some of the functionality in chip/g/gpio.c in order
+ * to configure a given strap pin to be either a low gpio output, a gpio input
+ * with or without an internal pull resistor, or disconnect the gpio signal
+ * from the pin pad.
+ *
+ * The desired gpio functionality is contained in the input parameter flags,
+ * while the strap parameter is an index into the array strap_regs.
*/
-static int servo_state_unknown(void)
+static void strap_config_pin(enum strap_list strap, int flags)
{
- if (uartn_enabled(UART_EC)) {
- device_set_state(DEVICE_SERVO, DEVICE_STATE_UNKNOWN);
- return 1;
- }
- return 0;
-}
-
-static void device_powered_off(enum device_type device, int uart)
-{
- if (device_get_state(device) == DEVICE_STATE_ON)
+ const struct gpio_info *g = gpio_list + strap_regs[strap].gpio_signal;
+ int bitnum = GPIO_MASK_TO_NUM(g->mask);
+ int mask = DIO_CTL_IE_MASK | DIO_CTL_PD_MASK | DIO_CTL_PU_MASK;
+ int val;
+
+ if (!flags) {
+ /* Reset strap pins, disconnect output and clear pull up/dn */
+ /* Disconnect gpio from pin mux */
+ DIO_SEL_REG(strap_regs[strap].sel_offset) = 0;
+ /* Clear input enable and pulldown/pullup in pinmux */
+ REG_WRITE_MLV(DIO_CTL_REG(strap_regs[strap].sel_offset),
+ mask, 0, 0);
return;
-
- device_state_changed(device, DEVICE_STATE_OFF);
-
- if (uart) {
- /* Disable RX and TX on the UART peripheral */
- uartn_disable(uart);
-
- /* Disconnect the TX pin from the UART peripheral */
- uartn_tx_disconnect(uart);
}
- gpio_enable_interrupt(device_states[device].detect_on);
-}
-
-static void servo_deferred(void)
-{
- if (servo_state_unknown())
+ if (flags & GPIO_OUT_LOW) {
+ /* Config gpio to output and drive low */
+ gpio_set_flags(strap_regs[strap].gpio_signal, GPIO_OUT_LOW);
+ /* connect pin mux to gpio */
+ DIO_SEL_REG(strap_regs[strap].sel_offset) =
+ GET_GPIO_FUNC(g->port, bitnum);
return;
+ }
- device_powered_off(DEVICE_SERVO, 0);
-}
-DECLARE_DEFERRED(servo_deferred);
+ if (flags & GPIO_INPUT) {
+ /* Configure gpio pin to be an input */
+ gpio_set_flags(strap_regs[strap].gpio_signal, GPIO_INPUT);
+ /* Connect pad to gpio */
+ GET_GPIO_SEL_REG(g->port, bitnum) =
+ strap_regs[strap].pad_select;
-static void ap_deferred(void)
-{
- device_powered_off(DEVICE_AP, UART_AP);
+ /*
+ * Input enable is bit 2 of the CTL register. Pulldown enable is
+ * bit 3, and pullup enable is bit 4. Always set input enable
+ * and clear the pullup/pulldown bits unless the flags variable
+ * specifies that pulldown or pullup should be enabled.
+ */
+ val = DIO_CTL_IE_MASK;
+ if (flags & GPIO_PULL_DOWN)
+ val |= DIO_CTL_PD_MASK;
+ if (flags & GPIO_PULL_UP)
+ val |= DIO_CTL_PU_MASK;
+ /* Set input enable and pulldown/pullup in pinmux */
+ REG_WRITE_MLV(DIO_CTL_REG(strap_regs[strap].sel_offset),
+ mask, 0, val);
+ }
}
-DECLARE_DEFERRED(ap_deferred);
-static void ec_deferred(void)
+static int get_strap_config(uint8_t *config)
{
- device_powered_off(DEVICE_EC, UART_EC);
-}
-DECLARE_DEFERRED(ec_deferred);
+ enum strap_list s0;
+ int lvl;
+ int flags;
+ uint8_t pull_a;
+ uint8_t pull_b;
-struct device_config device_states[] = {
- [DEVICE_SERVO] = {
- .deferred = &servo_deferred_data,
- .detect_on = GPIO_SERVO_UART2_ON,
- .detect_off = GPIO_SERVO_UART2_OFF,
- .name = "Servo"
- },
- [DEVICE_AP] = {
- .deferred = &ap_deferred_data,
- .detect_on = GPIO_AP_ON,
- .detect_off = GPIO_AP_OFF,
- .name = "AP"
- },
- [DEVICE_EC] = {
- .deferred = &ec_deferred_data,
- .detect_on = GPIO_EC_ON,
- .detect_off = GPIO_EC_OFF,
- .name = "EC"
- },
-};
-BUILD_ASSERT(ARRAY_SIZE(device_states) == DEVICE_COUNT);
+ /*
+ * There are 4 pins that are used to determine Cr50 board strapping
+ * options. These pins are:
+ * 1. DIOA1 -> I2CS_SDA
+ * 2. DI0A9 -> I2CS_SCL
+ * 3. DIOA6 -> SPS_CLK
+ * 4. DIOA12 -> SPS_CS_L
+ * There are two main configuration options based on whether I2C or SPI
+ * is used for TPM2 communication to/from the host AP. If SPI is the
+ * TPM2 bus, then the pair of pins DIOA9|DIOA1 are used to designate
+ * strapping options. If TPM uses I2C, then DIOA12|DIOA6 are the
+ * strapping pins.
+ *
+ * Each strapping pin will have either an external pullup or pulldown
+ * resistor. The external pull resistors have two levels, 5k for strong
+ * and 1M for weak. Cr50 has internal pullup/pulldown 50k resistors that
+ * can be configured via pinmux register settings. This combination of
+ * external and internal pullup/pulldown resistors allows for 4 possible
+ * states per strapping pin. The following table shows the different
+ * combinations. Note that when a strong external pull down/up resistor
+ * is used, the internal resistor is a don't care and those cases are
+ * marked by n/a. The bits column represents the signal level read on
+ * the gpio pin. Bit 1 of this field is the value read with the internal
+ * pull down/up resistors disabled, and bit 0 is the gpio signal level
+ * of the same pin when the internal pull resistor is selected as shown
+ * in the 'internal' column.
+ * external internal bits
+ * -------- -------- ----
+ * 5K PD n/a 00
+ * 1M PD 50k PU 01
+ * 1M PU 50k PD 10
+ * 5K PU n/a 11
+ *
+ * To determine the bits associated with each strapping pin, the
+ * following method is used.
+ * 1. Set all 4 pins as inputs with internal pulls disabled.
+ * 2. For each pin do the following to encode 2 bits b1:b0
+ * a. b1 = gpio_get_level(pin)
+ * b. If b1 == 1, then enable internal pulldown, else enable
+ * internal pullup resistor.
+ * c. b0 = gpio_get_level(pin)
+ *
+ * To be considered a valid strap configuraiton, the upper 4 bits must
+ * have no pullups and at least one pullup in the lower 4 bits or vice
+ * versa. So can use 0xA0 and 0x0A as masks to check for each
+ * condition. Once this check is passed, the 4 bits which are used to
+ * distinguish between SPI vs I2C are masked since reading them as weak
+ * pulldowns is not being explicitly required due to concerns that the
+ * AP could prevent accurate differentiation between strong and weak
+ * pull down cases.
+ */
-static void device_powered_on(enum device_type device, int uart)
-{
- /* Update the device state */
- device_state_changed(device, DEVICE_STATE_ON);
+ /* Drive all 4 strap pins low to discharge caps. */
+ for (s0 = a0; s0 < ARRAY_SIZE(strap_regs); s0++)
+ strap_config_pin(s0, GPIO_OUT_LOW);
+ /* Delay long enough to discharge any caps. */
+ udelay(STRAP_PIN_DELAY_USEC);
+
+ /* Set all 4 strap pins as inputs with pull resistors disabled. */
+ for (s0 = a0; s0 < ARRAY_SIZE(strap_regs); s0++)
+ strap_config_pin(s0, GPIO_INPUT);
+ /* Delay so voltage levels can settle. */
+ udelay(STRAP_PIN_DELAY_USEC);
+
+ *config = 0;
+ /* Read 2 bit value of each strapping pin. */
+ ccprintf("strap pin readings:");
+ for (s0 = a0; s0 < ARRAY_SIZE(strap_regs); s0++) {
+ lvl = gpio_get_level(strap_regs[s0].gpio_signal);
+ flags = GPIO_INPUT;
+ if (lvl)
+ flags |= GPIO_PULL_DOWN;
+ else
+ flags |= GPIO_PULL_UP;
+ /* Enable internal pull down/up resistor. */
+ strap_config_pin(s0, flags);
+ udelay(STRAP_PIN_DELAY_USEC);
+ lvl = (lvl << 1) |
+ gpio_get_level(strap_regs[s0].gpio_signal);
+ ccprintf(" %s:%d", strap_regs[s0].pad_name, lvl);
+ *config |= lvl << s0 * 2;
- /* Enable RX and TX on the UART peripheral */
- uartn_enable(uart);
+ /*
+ * Finished with this pin. Disable internal pull up/dn resistor
+ * and disconnect gpio from pin mux. The pins used for straps
+ * are configured for their desired role when either the SPI or
+ * I2C interfaces are initialized.
+ */
+ strap_config_pin(s0, 0);
+ }
+ ccprintf("\n");
- /* Connect the TX pin to the UART TX Signal */
- if (device_get_state(DEVICE_SERVO) != DEVICE_STATE_ON &&
- !uartn_enabled(uart))
- uartn_tx_connect(uart);
-}
+ /*
+ * The strap bits for DIOA12|DIOA6 are in the upper 4 bits of 'config'
+ * while the strap bits for DIOA9|DIOA1 are in the lower 4 bits. Check
+ * for SPI vs I2C config by checking for presence of external pullups in
+ * one group of 4 bits and confirming no external pullups in the other
+ * group. For SPI config the weak pulldowns may not be accurately read
+ * on DIOA12|DIOA6 and similarly for I2C config on
+ * DIOA9|DIOA1. Therefore, only requiring that there be no external
+ * pullups on these pins and will mask the bits so they will match the
+ * config table entries.
+ */
-static void servo_attached(void)
-{
- if (servo_state_unknown())
- return;
+ pull_a = *config & 0xa0;
+ pull_b = *config & 0xa;
+ if ((!pull_a && !pull_b) || (pull_a && pull_b))
+ return EC_ERROR_INVAL;
- /* Update the device state */
- device_state_changed(DEVICE_SERVO, DEVICE_STATE_ON);
+ /* Now that I2C vs SPI is known, mask the unused strap bits. */
+ *config &= *config & 0xa ? 0xf : 0xf0;
- /* Disconnect AP and EC UART when servo is attached */
- uartn_tx_disconnect(UART_AP);
- uartn_tx_disconnect(UART_EC);
+ return EC_SUCCESS;
}
-void device_state_on(enum gpio_signal signal)
+static uint32_t get_properties(void)
{
- switch (signal) {
- case GPIO_AP_ON:
- device_powered_on(DEVICE_AP, UART_AP);
- break;
- case GPIO_EC_ON:
- device_powered_on(DEVICE_EC, UART_EC);
- break;
- case GPIO_SERVO_UART2_ON:
- servo_attached();
- break;
- default:
- CPRINTS("Device not supported");
- return;
- }
-}
+ int i;
+ uint8_t config;
+ uint32_t properties;
-void device_state_off(enum gpio_signal signal)
-{
- switch (signal) {
- case GPIO_AP_OFF:
- board_update_device_state(DEVICE_AP);
- break;
- case GPIO_EC_OFF:
- board_update_device_state(DEVICE_EC);
- break;
- case GPIO_SERVO_UART2_OFF:
- board_update_device_state(DEVICE_SERVO);
- break;
- default:
- CPRINTS("Device not supported");
+ if (chip_factory_mode()) {
+ CPRINTS("Chip factory mode, short circuit to SPI");
+ return BOARD_SLAVE_CONFIG_SPI;
}
-}
-void board_update_device_state(enum device_type device)
-{
- int state;
-
- if (device == DEVICE_SERVO) {
+ if (get_strap_config(&config) != EC_SUCCESS) {
/*
- * If EC UART TX is pulled high when EC UART is not enabled,
- * then servo is attached.
+ * No pullups were detected on any of the strap pins so there
+ * is no point in checking for a matching config table entry.
+ * For this case use default properties.
*/
- state = (!uartn_enabled(UART_EC) &&
- gpio_get_level(GPIO_SERVO_UART2_ON));
- } else
- state = gpio_get_level(device_states[device].detect_on);
+ CPRINTS("Invalid strap pins! Default properties = 0x%x",
+ BOARD_PROPERTIES_DEFAULT);
+ return BOARD_PROPERTIES_DEFAULT;
+ }
+
+ /* Search board config table to find a matching entry */
+ for (i = 0; i < ARRAY_SIZE(board_cfg_table); i++) {
+ if (board_cfg_table[i].strap_cfg == config) {
+ properties = board_cfg_table[i].board_properties;
+ CPRINTS("Valid strap: 0x%x properties: 0x%x",
+ config, properties);
+ /* Read board properties for this config */
+ return properties;
+ }
+ }
/*
- * If the device is currently on set its state immediately. If it
- * thinks the device is powered off debounce the signal.
+ * Reached the end of the table and didn't find a matching config entry.
+ * However, the SPI vs I2C determination can still be made as
+ *get_strap_config() returned EC_SUCCESS.
*/
- if (state)
- device_state_on(device_states[device].detect_on);
- else {
- device_set_state(device, DEVICE_STATE_UNKNOWN);
-
- gpio_enable_interrupt(device_states[device].detect_on);
- /*
- * Wait a bit. If cr50 detects this device is ever powered on
- * during this time then the status wont be set to powered off.
- */
- hook_call_deferred(device_states[device].deferred, 50);
- }
+ properties = config & 0xa ? BOARD_SLAVE_CONFIG_SPI :
+ BOARD_PROPERTIES_DEFAULT;
+ CPRINTS("strap_cfg 0x%x has no table entry, prop = 0x%x",
+ config, properties);
+ return properties;
}
-void system_init_board_properties(void)
+static void init_board_properties(void)
{
uint32_t properties;
@@ -595,61 +1242,29 @@ void system_init_board_properties(void)
* This must be a power on reset or maybe restart due to a software
* update from a version not setting the register.
*/
- if (!properties || system_get_reset_flags() & RESET_FLAG_HARD) {
+ if (!(properties & BOARD_ALL_PROPERTIES) || (system_get_reset_flags() &
+ RESET_FLAG_HARD)) {
/*
- * Reset the properties, because after a hard reset the register
+ * Mask board properties because following hard reset, they
* won't be cleared.
*/
- properties = 0;
-
- /* Read DIOA1 strap pin */
- if (gpio_get_level(GPIO_STRAP0)) {
- /* Strap is pulled high -> Kevin SPI TPM option */
- properties |= BOARD_SLAVE_CONFIG_SPI;
- /* Add an internal pull up on sys_rst_l */
- /*
- * TODO(crosbug.com/p/56945): Remove once SYS_RST_L can
- * be pulled up externally.
- */
- properties |= BOARD_NEEDS_SYS_RST_PULL_UP;
- } else {
- /* Strap is low -> Reef I2C TPM option */
- properties |= BOARD_SLAVE_CONFIG_I2C;
- /* One PHY is connected to the AP */
- properties |= BOARD_USB_AP;
- /*
- * TODO(crosbug.com/p/56540): enable UART0 RX on Reef.
- * Early reef boards dont have the necessary pullups on
- * UART0RX so disable it until that is fixed.
- */
- properties |= BOARD_DISABLE_UART0_RX;
- /*
- * Use receiving a usb set address request as a
- * benchmark for marking the updated image as good.
- */
- properties |= BOARD_MARK_UPDATE_ON_USB_REQ;
- }
-
+ properties &= ~BOARD_ALL_PROPERTIES;
+ properties |= get_properties();
/*
* Now save the properties value for future use.
*
- * First enable write access to the LONG_LIFE_SCRATCH1 register.
+ * Enable access to LONG_LIFE_SCRATCH1 reg.
*/
GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG1, 1);
/* Save properties in LONG_LIFE register */
GREG32(PMU, LONG_LIFE_SCRATCH1) = properties;
- /* Disabel write access to the LONG_LIFE_SCRATCH1 register */
+ /* Disable access to LONG_LIFE_SCRATCH1 reg */
GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG1, 0);
}
-
/* Save this configuration setting */
board_properties = properties;
}
-
-uint32_t system_board_properties_callback(void)
-{
- return board_properties;
-}
+DECLARE_HOOK(HOOK_INIT, init_board_properties, HOOK_PRIO_FIRST);
void i2cs_set_pinmux(void)
{
@@ -662,12 +1277,244 @@ void i2cs_set_pinmux(void)
/* Enable SDA/SCL inputs from A1/A9 pads */
GWRITE_FIELD(PINMUX, DIOA1_CTL, IE, 1); /* I2CS_SDA */
GWRITE_FIELD(PINMUX, DIOA9_CTL, IE, 1); /* I2CS_SCL */
+
/*
- * Enable pull ups on both signals. TODO(vbendeb): consider
- * adjusting pull strength.
+ * Provide access to the SDA line to be able to detect 'hosed i2c
+ * slave' condition.
*/
- GWRITE_FIELD(PINMUX, DIOA1_CTL, PU, 1);
- GWRITE_FIELD(PINMUX, DIOA9_CTL, PU, 1);
- /* TODO(scollyer): Do we need to add wake on SCL activity here? */
+ GWRITE(PINMUX, GPIO0_GPIO14_SEL, GC_PINMUX_DIOA1_SEL);
+
+ /* Allow I2CS_SCL to wake from sleep */
+ GWRITE_FIELD(PINMUX, EXITEDGE0, DIOA9, 1); /* edge sensitive */
+ GWRITE_FIELD(PINMUX, EXITINV0, DIOA9, 1); /* wake on low */
+ GWRITE_FIELD(PINMUX, EXITEN0, DIOA9, 1); /* enable powerdown exit */
+ /* Allow I2CS_SDA to wake from sleep */
+ GWRITE_FIELD(PINMUX, EXITEDGE0, DIOA1, 1); /* edge sensitive */
+ GWRITE_FIELD(PINMUX, EXITINV0, DIOA1, 1); /* wake on low */
+ GWRITE_FIELD(PINMUX, EXITEN0, DIOA1, 1); /* enable powerdown exit */
+}
+
+/**
+ * Return non-zero if this is the first boot of a board in the factory.
+ *
+ * This is used to determine whether the default CCD configuration will be RMA
+ * (things are unlocked for factory) or normal (things locked down because not
+ * in factory).
+ *
+ * Suggested checks:
+ * - If the board ID exists, this is not the first boot
+ * - If the TPM is not blank, this is not the first boot
+ */
+int board_is_first_factory_boot(void)
+{
+ /*
+ * TODO(rspangler): Add checks for factory boot. For now, always
+ * return 0 so we're safely locked by default.
+ */
+ return 0;
+}
+
+/* Determine key type based on the key ID. */
+static const char *key_type(uint32_t key_id)
+{
+
+ /*
+ * It is a mere convention, but all prod keys are required to have key
+ * IDs such, that bit D2 is set, and all dev keys are required to have
+ * key IDs such, that bit D2 is not set.
+ *
+ * This convention is enforced at the key generation time.
+ */
+ if (key_id & (1 << 2))
+ return "prod";
+ else
+ return "dev";
}
+
+static int command_sysinfo(int argc, char **argv)
+{
+ enum system_image_copy_t active;
+ uintptr_t vaddr;
+ const struct SignedHeader *h;
+ int reset_count = GREG32(PMU, LONG_LIFE_SCRATCH0);
+ char rollback_str[15];
+
+ ccprintf("Reset flags: 0x%08x (", system_get_reset_flags());
+ system_print_reset_flags();
+ ccprintf(")\n");
+ if (reset_count > 6)
+ ccprintf("Rollback detected\n");
+ ccprintf("Reset count: %d\n", reset_count);
+
+ ccprintf("Chip: %s %s %s\n", system_get_chip_vendor(),
+ system_get_chip_name(), system_get_chip_revision());
+
+ active = system_get_ro_image_copy();
+ vaddr = get_program_memory_addr(active);
+ h = (const struct SignedHeader *)vaddr;
+ ccprintf("RO keyid: 0x%08x(%s)\n", h->keyid, key_type(h->keyid));
+
+ active = system_get_image_copy();
+ vaddr = get_program_memory_addr(active);
+ h = (const struct SignedHeader *)vaddr;
+ ccprintf("RW keyid: 0x%08x(%s)\n", h->keyid, key_type(h->keyid));
+
+ ccprintf("DEV_ID: 0x%08x 0x%08x\n",
+ GREG32(FUSE, DEV_ID0), GREG32(FUSE, DEV_ID1));
+
+ system_get_rollback_bits(rollback_str, sizeof(rollback_str));
+ ccprintf("Rollback: %s\n", rollback_str);
+
+ return EC_SUCCESS;
+}
+DECLARE_SAFE_CONSOLE_COMMAND(sysinfo, command_sysinfo,
+ NULL,
+ "Print system info");
+
+/*
+ * SysInfo command:
+ * There are no input args.
+ * Output is this struct, all fields in network order.
+ */
+struct sysinfo_s {
+ uint32_t ro_keyid;
+ uint32_t rw_keyid;
+ uint32_t dev_id0;
+ uint32_t dev_id1;
+} __packed;
+
+static enum vendor_cmd_rc vc_sysinfo(enum vendor_cmd_cc code,
+ void *buf,
+ size_t input_size,
+ size_t *response_size)
+{
+ enum system_image_copy_t active;
+ uintptr_t vaddr;
+ const struct SignedHeader *h;
+ struct sysinfo_s *sysinfo = buf;
+
+ active = system_get_ro_image_copy();
+ vaddr = get_program_memory_addr(active);
+ h = (const struct SignedHeader *)vaddr;
+ sysinfo->ro_keyid = htobe32(h->keyid);
+
+ active = system_get_image_copy();
+ vaddr = get_program_memory_addr(active);
+ h = (const struct SignedHeader *)vaddr;
+ sysinfo->rw_keyid = htobe32(h->keyid);
+
+ sysinfo->dev_id0 = htobe32(GREG32(FUSE, DEV_ID0));
+ sysinfo->dev_id1 = htobe32(GREG32(FUSE, DEV_ID1));
+
+ *response_size = sizeof(*sysinfo);
+ return VENDOR_RC_SUCCESS;
+}
+DECLARE_VENDOR_COMMAND(VENDOR_CC_SYSINFO, vc_sysinfo);
+
+static enum vendor_cmd_rc vc_invalidate_inactive_rw(enum vendor_cmd_cc code,
+ void *buf,
+ size_t input_size,
+ size_t *response_size)
+{
+ const struct SignedHeader *header;
+ uint32_t ctrl;
+ uint32_t base_addr;
+ uint32_t size;
+ const char zero[4] = {}; /* value to write to magic. */
+
+ *response_size = 0;
+
+ /* Update INFO1 mask based on the currently active image. */
+ system_update_rollback_mask_with_active_img();
+
+ if (other_rw_is_inactive()) {
+ CPRINTS("%s: Inactive region is disabled", __func__);
+ return VENDOR_RC_SUCCESS;
+ }
+
+ /* save the original flash region6 register values */
+ ctrl = GREAD(GLOBALSEC, FLASH_REGION6_CTRL);
+ base_addr = GREG32(GLOBALSEC, FLASH_REGION6_BASE_ADDR);
+ size = GREG32(GLOBALSEC, FLASH_REGION6_SIZE);
+
+ header = get_other_rw_addr();
+
+ /* Enable RW access to the other header. */
+ GREG32(GLOBALSEC, FLASH_REGION6_BASE_ADDR) = (uint32_t) header;
+ GREG32(GLOBALSEC, FLASH_REGION6_SIZE) = 1023;
+ GWRITE_FIELD(GLOBALSEC, FLASH_REGION6_CTRL, EN, 1);
+ GWRITE_FIELD(GLOBALSEC, FLASH_REGION6_CTRL, RD_EN, 1);
+ GWRITE_FIELD(GLOBALSEC, FLASH_REGION6_CTRL, WR_EN, 1);
+
+ CPRINTS("%s: TPM verified corrupting inactive image, magic before %x",
+ __func__, header->magic);
+
+ flash_physical_write((intptr_t)&header->magic -
+ CONFIG_PROGRAM_MEMORY_BASE,
+ sizeof(zero), zero);
+
+ CPRINTS("%s: magic after: %x", __func__, header->magic);
+
+ /* Restore original values */
+ GREG32(GLOBALSEC, FLASH_REGION6_BASE_ADDR) = base_addr;
+ GREG32(GLOBALSEC, FLASH_REGION6_SIZE) = size;
+ GREG32(GLOBALSEC, FLASH_REGION6_CTRL) = ctrl;
+
+ return VENDOR_RC_SUCCESS;
+}
+DECLARE_VENDOR_COMMAND(VENDOR_CC_INVALIDATE_INACTIVE_RW,
+ vc_invalidate_inactive_rw);
+
+static enum vendor_cmd_rc vc_commit_nvmem(enum vendor_cmd_cc code,
+ void *buf,
+ size_t input_size,
+ size_t *response_size)
+{
+ nvmem_enable_commits();
+ *response_size = 0;
+ return VENDOR_RC_SUCCESS;
+}
+DECLARE_VENDOR_COMMAND(VENDOR_CC_COMMIT_NVMEM, vc_commit_nvmem);
+
+static int command_board_properties(int argc, char **argv)
+{
+ /*
+ * The board properties are stored in LONG_LIFE_SCRATCH1. Note that we
+ * don't just simply return board_properties here since that's just a
+ * cached value from init time.
+ */
+ ccprintf("properties = 0x%x\n", GREG32(PMU, LONG_LIFE_SCRATCH1));
+
+ return EC_SUCCESS;
+}
+DECLARE_SAFE_CONSOLE_COMMAND(brdprop, command_board_properties,
+ NULL, "Display board properties");
+
+int chip_factory_mode(void)
+{
+ static uint8_t mode_set;
+
+ /*
+ * Bit 0x2 used to indicate that mode has been set, bit 0x1 is the
+ * actual indicator of the chip factory mode.
+ */
+ if (!mode_set)
+ mode_set = 2 | !!gpio_get_level(GPIO_DIOB4);
+
+ return mode_set & 1;
+}
+
+#ifdef CR50_DEV
+static int command_rollback(int argc, char **argv)
+{
+ system_ensure_rollback();
+ ccprintf("Rebooting to alternate RW due to manual request\n");
+ cflush();
+ system_reset(0);
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(rollback, command_rollback,
+ "", "Force rollback to escape DEV image.");
+#endif
diff --git a/board/cr50/board.h b/board/cr50/board.h
index c7814e5b36..ed2e2facb2 100644
--- a/board/cr50/board.h
+++ b/board/cr50/board.h
@@ -6,6 +6,15 @@
#ifndef __CROS_EC_BOARD_H
#define __CROS_EC_BOARD_H
+/*
+ * The default watchdog timeout is 1.6 seconds, but there are some legitimate
+ * flash-intensive TPM operations that actually take close to that long to
+ * complete. Make sure we don't trigger the watchdog accidentally if the timing
+ * is just a little off.
+ */
+#undef CONFIG_WATCHDOG_PERIOD_MS
+#define CONFIG_WATCHDOG_PERIOD_MS 5000
+
/* Features that we don't want */
#undef CONFIG_CMD_LID_ANGLE
#undef CONFIG_CMD_POWERINDEBUG
@@ -13,56 +22,99 @@
#undef CONFIG_FMAP
#undef CONFIG_HIBERNATE
#undef CONFIG_LID_SWITCH
+#undef CONFIG_CMD_SYSINFO
+#undef CONFIG_CMD_SYSJUMP
+#undef CONFIG_CMD_SYSLOCK
+
+#ifndef CR50_DEV
+/* Disable stuff that should only be in debug builds */
+#undef CONFIG_CMD_CRASH
+#undef CONFIG_CMD_MD
+#undef CONFIG_CMD_RW
+#undef CONFIG_CMD_SLEEPMASK
+#undef CONFIG_CMD_WAITMS
+#undef CONFIG_FLASH
+#endif
/* Flash configuration */
#undef CONFIG_FLASH_PSTATE
-/* TODO(crosbug.com/p/44745): Bringup only! Do the right thing for real! */
#define CONFIG_WP_ALWAYS
-/* TODO(crosbug.com/p/44745): For debugging only */
#define CONFIG_CMD_FLASH
+#define CONFIG_CRC8
+
+/* Non-volatile counter storage for U2F */
+#define CONFIG_FLASH_NVCOUNTER
+#define CONFIG_FLASH_NVCTR_SIZE CONFIG_FLASH_BANK_SIZE
+#define CONFIG_FLASH_NVCTR_BASE_A (CONFIG_PROGRAM_MEMORY_BASE + \
+ CFG_TOP_A_OFF)
+#define CONFIG_FLASH_NVCTR_BASE_B (CONFIG_PROGRAM_MEMORY_BASE + \
+ CFG_TOP_B_OFF)
/* We're using TOP_A for partition 0, TOP_B for partition 1 */
#define CONFIG_FLASH_NVMEM
/* Offset to start of NvMem area from base of flash */
-#define CONFIG_FLASH_NVMEM_OFFSET_A (CFG_TOP_A_OFF)
-#define CONFIG_FLASH_NVMEM_OFFSET_B (CFG_TOP_B_OFF)
+#define CONFIG_FLASH_NVMEM_OFFSET_A (CFG_TOP_A_OFF + CONFIG_FLASH_NVCTR_SIZE)
+#define CONFIG_FLASH_NVMEM_OFFSET_B (CFG_TOP_B_OFF + CONFIG_FLASH_NVCTR_SIZE)
/* Address of start of Nvmem area */
#define CONFIG_FLASH_NVMEM_BASE_A (CONFIG_PROGRAM_MEMORY_BASE + \
CONFIG_FLASH_NVMEM_OFFSET_A)
#define CONFIG_FLASH_NVMEM_BASE_B (CONFIG_PROGRAM_MEMORY_BASE + \
CONFIG_FLASH_NVMEM_OFFSET_B)
/* Size partition in NvMem */
-#define NVMEM_PARTITION_SIZE CFG_TOP_SIZE
+#define NVMEM_PARTITION_SIZE (CFG_TOP_SIZE - CONFIG_FLASH_NVCTR_SIZE)
/* Size in bytes of NvMem area */
-#define CONFIG_FLASH_NVMEM_SIZE (CFG_TOP_SIZE * NVMEM_NUM_PARTITIONS)
+#define CONFIG_FLASH_NVMEM_SIZE (NVMEM_PARTITION_SIZE * NVMEM_NUM_PARTITIONS)
+/* Enable <key, value> variable support. */
+#define CONFIG_FLASH_NVMEM_VARS
+#define NVMEM_CR50_SIZE 272
+#define CONFIG_FLASH_NVMEM_VARS_USER_SIZE NVMEM_CR50_SIZE
/* Go to sleep when nothing else is happening */
#define CONFIG_LOW_POWER_IDLE
-/* Detect the states of other devices */
-#define CONFIG_DEVICE_STATE
+/* Allow multiple concurrent memory allocations. */
+#define CONFIG_MALLOC
/* Enable debug cable detection */
#define CONFIG_RDD
+/* Also use the cr50 as a second factor authentication */
+#define CONFIG_U2F
+
/* USB configuration */
#define CONFIG_USB
-#define CONFIG_USB_HID
#define CONFIG_USB_CONSOLE
+#define CONFIG_USB_I2C
#define CONFIG_USB_INHIBIT_INIT
-#define CONFIG_USB_SELECT_PHY
#define CONFIG_USB_SPI
+#define CONFIG_USB_SERIALNO
+#define DEFAULT_SERIALNO "0"
#define CONFIG_STREAM_USART
#define CONFIG_STREAM_USB
+#define CONFIG_STREAM_USART1
+#define CONFIG_STREAM_USART2
/* Enable Case Closed Debugging */
-#define CONFIG_CASE_CLOSED_DEBUG
+#define CONFIG_CASE_CLOSED_DEBUG_V1
+#define CONFIG_PHYSICAL_PRESENCE
+
+#ifdef CR50_DEV
+/* Enable unsafe dev features for CCD in dev builds */
+#define CONFIG_CASE_CLOSED_DEBUG_V1_UNSAFE
+#define CONFIG_PHYSICAL_PRESENCE_DEBUG_UNSAFE
+#endif
#define CONFIG_USB_PID 0x5014
#define CONFIG_USB_SELF_POWERED
+#undef CONFIG_USB_MAXPOWER_MA
+#define CONFIG_USB_MAXPOWER_MA 0
+
+/* Need to be able to bitbang the EC UART for updates through CCD. */
+#define CONFIG_UART_BITBANG
+
/* Enable SPI Master (SPI) module */
#define CONFIG_SPI_MASTER
#define CONFIG_SPI_MASTER_NO_CS_GPIOS
@@ -85,6 +137,12 @@
/* Include crypto stuff, both software and hardware. */
#define CONFIG_DCRYPTO
+#define CONFIG_UPTO_SHA512
+
+/* Implement custom udelay, due to usec hwtimer imprecision. */
+#define CONFIG_HW_SPECIFIC_UDELAY
+
+#define CONFIG_TPM_LOGGING
#ifndef __ASSEMBLER__
@@ -98,36 +156,89 @@ enum usb_strings {
USB_STR_VERSION,
USB_STR_CONSOLE_NAME,
USB_STR_BLOB_NAME,
- USB_STR_HID_NAME,
+ USB_STR_HID_KEYBOARD_NAME,
USB_STR_AP_NAME,
USB_STR_EC_NAME,
USB_STR_UPGRADE_NAME,
USB_STR_SPI_NAME,
+ USB_STR_SERIALNO,
+ USB_STR_I2C_NAME,
USB_STR_COUNT
};
-/* Device indexes */
-enum device_type {
- DEVICE_AP = 0,
- DEVICE_EC,
- DEVICE_SERVO,
-
- DEVICE_COUNT
+/*
+ * Device states
+ *
+ * Note that not all states are used by all devices.
+ */
+enum device_state {
+ /* Initial state at boot */
+ DEVICE_STATE_INIT = 0,
+
+ /*
+ * Detect was not asserted at boot, but we're not willing to give up on
+ * the device right away so we're debouncing to see if it shows up.
+ */
+ DEVICE_STATE_INIT_DEBOUNCING,
+
+ /*
+ * Device was detected at boot, but we can't enable transmit yet
+ * because that would interfere with detection of another device.
+ */
+ DEVICE_STATE_INIT_RX_ONLY,
+
+ /* Disconnected or off, because detect is deasserted */
+ DEVICE_STATE_DISCONNECTED,
+ DEVICE_STATE_OFF,
+
+ /* Device state is not knowable because we're driving detect */
+ DEVICE_STATE_UNDETECTABLE,
+
+ /* Connected or on, because detect is asserted */
+ DEVICE_STATE_CONNECTED,
+ DEVICE_STATE_ON,
+
+ /*
+ * Device was connected, but we saw detect deasserted and are
+ * debouncing to see if it stays deasserted - at which point we'll
+ * decide that it's disconnected.
+ */
+ DEVICE_STATE_DEBOUNCING,
+
+ /* Device state is unknown. Used only by legacy device_state code. */
+ DEVICE_STATE_UNKNOWN,
+
+ /* Number of device states */
+ DEVICE_STATE_COUNT
};
-/* USB SPI device indexes */
-enum usb_spi {
- USB_SPI_DISABLE = 0,
- USB_SPI_AP,
- USB_SPI_EC,
+/**
+ * Return the name of the device state as as string.
+ *
+ * @param state State to look up
+ * @return Name of the state, or "?" if no match.
+ */
+const char *device_state_name(enum device_state state);
+
+/* NVMem variables. */
+enum nvmem_vars {
+ NVMEM_VAR_CONSOLE_LOCKED = 0,
+ NVMEM_VAR_TEST_VAR,
+ NVMEM_VAR_U2F_SALT,
+ NVMEM_VAR_CCD_CONFIG,
+
+ NVMEM_VARS_COUNT
};
void board_configure_deep_sleep_wakepins(void);
-/* Interrupt handler */
-void sys_rst_asserted(enum gpio_signal signal);
-void device_state_on(enum gpio_signal signal);
-void device_state_off(enum gpio_signal signal);
+void ap_detect_asserted(enum gpio_signal signal);
+void ec_detect_asserted(enum gpio_signal signal);
+void ec_tx_cr50_rx(enum gpio_signal signal);
+void servo_detect_asserted(enum gpio_signal signal);
+void tpm_rst_deasserted(enum gpio_signal signal);
+
+void post_reboot_request(void);
/* Special controls over EC and AP */
void assert_sys_rst(void);
@@ -137,25 +248,65 @@ void assert_ec_rst(void);
void deassert_ec_rst(void);
int is_ec_rst_asserted(void);
+/**
+ * Set up a deferred call to update CCD state.
+ *
+ * This will enable/disable UARTs, SPI, I2C, etc. as needed.
+ */
+void ccd_update_state(void);
+
+int board_use_plt_rst(void);
+int board_rst_pullup_needed(void);
+int board_tpm_uses_i2c(void);
+int board_tpm_uses_spi(void);
+int board_id_is_mismatched(void);
+/* Use TPM_RST_L to detect the AP state instead of the uart */
+int board_detect_ap_with_tpm_rst(void);
+/* Allow for deep sleep to be enabled on AP shutdown */
+int board_deep_sleep_allowed(void);
+
+void power_button_record(void);
+
+/* Functions needed by CCD config */
+int board_battery_is_present(void);
+int board_fwmp_allows_unlock(void);
+void board_reboot_ap(void);
+int board_wipe_tpm(void);
+int board_is_first_factory_boot(void);
+
+void print_ap_state(void);
+void print_ec_state(void);
+void print_servo_state(void);
+
+int ap_is_on(void);
+int ec_is_on(void);
+int ec_is_rx_allowed(void);
+int servo_is_connected(void);
+
+void set_ap_on_deferred(void);
+
+/* Returns True if chip is brought up in a factory test harness. */
+int chip_factory_mode(void);
+
#endif /* !__ASSEMBLER__ */
/* USB interface indexes (use define rather than enum to expand them) */
#define USB_IFACE_CONSOLE 0
-#define USB_IFACE_HID 1
-#define USB_IFACE_AP 2
-#define USB_IFACE_EC 3
-#define USB_IFACE_UPGRADE 4
-#define USB_IFACE_SPI 5
+#define USB_IFACE_AP 1
+#define USB_IFACE_EC 2
+#define USB_IFACE_UPGRADE 3
+#define USB_IFACE_SPI 4
+#define USB_IFACE_I2C 5
#define USB_IFACE_COUNT 6
/* USB endpoint indexes (use define rather than enum to expand them) */
#define USB_EP_CONTROL 0
#define USB_EP_CONSOLE 1
-#define USB_EP_HID 2
-#define USB_EP_AP 3
-#define USB_EP_EC 4
-#define USB_EP_UPGRADE 5
-#define USB_EP_SPI 6
+#define USB_EP_AP 2
+#define USB_EP_EC 3
+#define USB_EP_UPGRADE 4
+#define USB_EP_SPI 5
+#define USB_EP_I2C 6
#define USB_EP_COUNT 7
/* UART indexes (use define rather than enum to expand them) */
@@ -165,18 +316,6 @@ int is_ec_rst_asserted(void);
#define UARTN UART_CR50
-/* TODO(crosbug.com/p/56540): Remove this when UART0_RX works everywhere */
-#define GC_UART0_RX_DISABLE
-
-/*
- * This would be a low hanging fruit if there is a need to reduce memory
- * footprint. Having a large buffer helps not to drop debug outputs generated
- * before console is initialized, but this is not really necessary in a
- * production device.
- */
-#undef CONFIG_UART_TX_BUF_SIZE
-#define CONFIG_UART_TX_BUF_SIZE 4096
-
#define CC_DEFAULT (CC_ALL & ~CC_MASK(CC_TPM))
/* Nv Memory users */
@@ -188,12 +327,7 @@ enum nvmem_users {
};
#endif
-/*
- * Let's be on the lookout for stack overflow, while debugging.
- *
- * TODO(vbendeb): remove this before finalizing the code.
- */
-#define CONFIG_DEBUG_STACK_OVERFLOW
+#define CONFIG_FLASH_NVMEM_VARS_USER_NUM NVMEM_CR50
#define CONFIG_RW_B
/* Firmware upgrade options. */
@@ -201,7 +335,32 @@ enum nvmem_users {
#define CONFIG_USB_FW_UPDATE
#define CONFIG_I2C
+#define CONFIG_I2C_MASTER
#define CONFIG_I2C_SLAVE
#define CONFIG_TPM_I2CS
+#define CONFIG_BOARD_ID_SUPPORT
+#define CONFIG_EXTENDED_VERSION_INFO
+
+#define I2C_PORT_MASTER 0
+
+#define CONFIG_BASE32
+#define CONFIG_CURVE25519
+#define CONFIG_RMA_AUTH
+#define CONFIG_FACTORY_MODE
+#define CONFIG_RNG
+
+/* Dummy values to be replaced with real ones. */
+#define CONFIG_RMA_AUTH_SERVER_PUBLIC_KEY { \
+ 0x03, 0xae, 0x2d, 0x2c, 0x06, 0x23, 0xe0, 0x73, \
+ 0x0d, 0xd3, 0xb7, 0x92, 0xac, 0x54, 0xc5, 0xfd, \
+ 0x7e, 0x9c, 0xf0, 0xa8, 0xeb, 0x7e, 0x2a, 0xb5, \
+ 0xdb, 0xf4, 0x79, 0x5f, 0x8a, 0x0f, 0x28, 0x3f}
+#define CONFIG_RMA_AUTH_SERVER_KEY_ID 0x10
+
+#define CONFIG_ENABLE_H1_ALERTS
+
+/* Enable hardware backed brute force resistance feature */
+#define CONFIG_PINWEAVER
+
#endif /* __CROS_EC_BOARD_H */
diff --git a/board/cr50/build.mk b/board/cr50/build.mk
index 639743f0b7..72c34af809 100644
--- a/board/cr50/build.mk
+++ b/board/cr50/build.mk
@@ -29,10 +29,11 @@ dirs-y += chip/$(CHIP)/dcrypto
dirs-y += $(BDIR)/tpm2
# Objects that we need to build
-board-y = board.o
-board-y += board_id.o
+board-y = board.o ap_state.o ec_state.o power_button.o servo_state.o
board-${CONFIG_RDD} += rdd.o
board-${CONFIG_USB_SPI} += usb_spi.o
+board-${CONFIG_USB_I2C} += usb_i2c.o
+board-y += recovery_button.o
board-y += tpm2/NVMem.o
board-y += tpm2/aes.o
board-y += tpm2/ecc.o
@@ -47,8 +48,9 @@ board-y += tpm2/rsa.o
board-y += tpm2/stubs.o
board-y += tpm2/tpm_state.o
board-y += tpm2/trng.o
-board-y += tpm2/upgrade.o
+board-y += tpm_nvmem_read.o
board-y += wp.o
+board-$(CONFIG_U2F) += u2f.o
# Build and link with an external library
EXTLIB := $(realpath ../../third_party/tpm2)
@@ -63,10 +65,17 @@ CPPFLAGS += -I$(abspath ./chip/$(CHIP))
CPPFLAGS += -I$(abspath .)
CPPFLAGS += -I$(abspath $(BDIR))
CPPFLAGS += -I$(abspath ./test)
+ifeq ($(CONFIG_UPTO_SHA512),y)
+CPPFLAGS += -DSHA512_SUPPORT
+endif
-# Make sure the context of the software sha256 implementation fits. If it ever
+# Make sure the context of the software sha512 implementation fits. If it ever
# increases, a compile time assert will fire in tpm2/hash.c.
+ifeq ($(CONFIG_UPTO_SHA512),y)
+CFLAGS += -DUSER_MIN_HASH_STATE_SIZE=208
+else
CFLAGS += -DUSER_MIN_HASH_STATE_SIZE=112
+endif
# Configure TPM2 headers accordingly.
CFLAGS += -DEMBEDDED_MODE=1
# Configure cryptoc headers to handle unaligned accesses.
@@ -81,6 +90,8 @@ $(out)/RW/ec.RW.elf $(out)/RW/ec.RW_B.elf: $(out)/tpm2/libtpm2.a
# Force the external build each time, so it can look for changed sources.
.PHONY: $(out)/tpm2/libtpm2.a
$(out)/tpm2/libtpm2.a:
- $(MAKE) obj=$(realpath $(out))/tpm2 EMBEDDED_MODE=1 -C $(EXTLIB)
+ $(MAKE) obj=$(realpath $(out))/tpm2 EMBEDDED_MODE=1 OBJ_PREFIX=Tpm2_ -C $(EXTLIB)
endif # BOARD_MK_INCLUDED_ONCE is nonempty
+
+board-$(CONFIG_PINWEAVER)+=pinweaver_tpm_imports.o
diff --git a/board/cr50/ec.tasklist b/board/cr50/ec.tasklist
index 3d2a4ab1ad..b22bc4e158 100644
--- a/board/cr50/ec.tasklist
+++ b/board/cr50/ec.tasklist
@@ -17,6 +17,6 @@
* 's' is the stack size in bytes; must be a multiple of 8
*/
#define CONFIG_TASK_LIST \
- TASK_ALWAYS(HOOKS, hook_task, NULL, LARGER_TASK_STACK_SIZE) \
+ TASK_ALWAYS(HOOKS, hook_task, NULL, CONFIG_STACK_SIZE) \
TASK_NOTEST(TPM, tpm_task, NULL, 8192) \
TASK_ALWAYS(CONSOLE, console_task, NULL, TASK_STACK_SIZE)
diff --git a/board/cr50/ec_state.c b/board/cr50/ec_state.c
new file mode 100644
index 0000000000..a208cbfd2c
--- /dev/null
+++ b/board/cr50/ec_state.c
@@ -0,0 +1,139 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * EC state machine
+ */
+#include "ccd_config.h"
+#include "common.h"
+#include "console.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "uart_bitbang.h"
+#include "uartn.h"
+
+#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args)
+
+static enum device_state state = DEVICE_STATE_INIT;
+
+void print_ec_state(void)
+{
+ ccprintf("EC: %s\n", device_state_name(state));
+}
+
+int ec_is_on(void)
+{
+ /* Debouncing and on are both still on */
+ return (state == DEVICE_STATE_DEBOUNCING || state == DEVICE_STATE_ON);
+}
+
+int ec_is_rx_allowed(void)
+{
+ return ec_is_on() || state == DEVICE_STATE_INIT_RX_ONLY;
+}
+
+/**
+ * Set the EC state.
+ *
+ * Done as a function to make it easier to debug state transitions. Note that
+ * this ONLY sets the state (and possibly prints debug info), and doesn't do
+ * all the additional transition work that set_ec_on(), etc. do.
+ *
+ * @param new_state State to set.
+ */
+static void set_state(enum device_state new_state)
+{
+#ifdef CR50_DEBUG_EC_STATE
+ /* Print all state transitions. May spam the console. */
+ if (state != new_state)
+ CPRINTS("EC %s -> %s",
+ device_state_name(state), device_state_name(new_state));
+#endif
+ state = new_state;
+}
+
+/**
+ * Move the EC to the ON state.
+ *
+ * This can be deferred from the interrupt handler, or called from the state
+ * machine which also runs in HOOK task, so it needs to check the current state
+ * to determine whether we're already on.
+ */
+static void set_ec_on(void)
+{
+ if (state == DEVICE_STATE_INIT ||
+ state == DEVICE_STATE_INIT_DEBOUNCING) {
+ /*
+ * Enable the UART peripheral so we start receiving on EC RX,
+ * but do not call uartn_tx_connect() to connect EC TX yet. We
+ * need to be able to use EC TX to detect servo, so if we drive
+ * it right away that blocks us from detecting servo.
+ */
+ CPRINTS("EC RX only");
+ set_state(DEVICE_STATE_INIT_RX_ONLY);
+ ccd_update_state();
+ return;
+ }
+
+ /* If we were debouncing ON->OFF, cancel it because we're still on */
+ if (state == DEVICE_STATE_DEBOUNCING)
+ set_state(DEVICE_STATE_ON);
+
+ /* If we're already on, done */
+ if (state == DEVICE_STATE_ON)
+ return;
+
+ /* We were previously off */
+ CPRINTS("EC on");
+ set_state(DEVICE_STATE_ON);
+ ccd_update_state();
+}
+DECLARE_DEFERRED(set_ec_on);
+
+/**
+ * Interrupt handler for EC detect asserted.
+ */
+void ec_detect_asserted(enum gpio_signal signal)
+{
+ gpio_disable_interrupt(GPIO_DETECT_EC);
+ hook_call_deferred(&set_ec_on_data, 0);
+}
+
+/**
+ * Detect state machine
+ */
+static void ec_detect(void)
+{
+ /* Disable interrupts if we had them on for debouncing */
+ gpio_disable_interrupt(GPIO_DETECT_EC);
+
+ /* If we detect the EC, make sure it's on */
+ if (gpio_get_level(GPIO_DETECT_EC)) {
+ set_ec_on();
+ return;
+ }
+
+ /* EC wasn't detected. If we're already off, done. */
+ if (state == DEVICE_STATE_OFF)
+ return;
+
+ /* If we were debouncing, we're now sure we're off */
+ if (state == DEVICE_STATE_DEBOUNCING ||
+ state == DEVICE_STATE_INIT_DEBOUNCING) {
+ CPRINTS("EC off");
+ set_state(DEVICE_STATE_OFF);
+ ccd_update_state();
+ return;
+ }
+
+ /*
+ * Otherwise, we were on or initializing, and we're not sure if the EC
+ * is actually off or just sending a 0-bit. So start debouncing.
+ */
+ if (state == DEVICE_STATE_INIT)
+ set_state(DEVICE_STATE_INIT_DEBOUNCING);
+ else
+ set_state(DEVICE_STATE_DEBOUNCING);
+ gpio_enable_interrupt(GPIO_DETECT_EC);
+}
+DECLARE_HOOK(HOOK_SECOND, ec_detect, HOOK_PRIO_DEFAULT);
diff --git a/board/cr50/gpio.inc b/board/cr50/gpio.inc
index 3c40599034..8288735a3f 100644
--- a/board/cr50/gpio.inc
+++ b/board/cr50/gpio.inc
@@ -4,23 +4,68 @@
* found in the LICENSE file.
*/
+/*
+ * This file describes GPIO mapping for the cr50 code running on the H1 chip.
+ *
+ * For the purposes of this file H1 core has the following logical and
+ * physical items and properties:
+ *
+ * - 32 internal GPIOs, which are split into two ports of 16 bits each.
+ * Ports' architecture and programmig is described in "ARM Cortex-M System
+ * Design Kit TRM" DDIO47B.
+ *
+ * - a set of peripherals - slave and master SPI and I2C controllers, UARTs,
+ * interrupt controller, etc.
+ *
+ * - 28 pins on the package named DIOA0..14, DIOB0..7 and DIOM0..4
+ *
+ * - a PINMUX - a unit which allows to interconnect objects from the three
+ * groups listed above. Note that some peripherals are attached to some
+ * pins directly, so in case those peripherals are used the pins should
+ * not be connected by PINMUX to any other outputs.
+ *
+ * The below macros are somewhat misleading (apparently for historical
+ * reasons), as PIN(p, b) component in fact refers not to the external pin,
+ * but to the GPIO (bit b on port p), where bit is in 0..15 range, and port is
+ * in 0..1 range.
+ *
+ * To describe routing of an external signal two macro instantiations are
+ * required:
+ *
+ * The GPIO_INT() or GPIO() macro assigns the signal a name and assigns it to
+ * the internal GPIO port, (again, defining the port using the PIN(port, bit)
+ * component of the macro invocation). GPIO_INT definitions assign their
+ * respective signals to interrupts and ISRs.
+ *
+ * The PINMUX macro assigns the previously defined GPIO to another object,
+ * most commonly to an external pin, but possibly to some internal component.
+ */
+
/* Declare symbolic names for all the GPIOs that we care about.
* Note: Those with interrupt handlers must be declared first. */
/*
- * We can assert SYS_RST_L but so can the EC, so we need react if it's pulled
- * low. The ARM core can't trigger an interrupt if it's driving it as an output
- * so we attach two internal GPIOs to the same pad.
+ * The system reset signal can be from two different pins depending on what the
+ * board type is. One board uses plt_rst_l (diom3) and the other board type uses
+ * sys_rst_l (diom0) to detect a warm reset. The pin is selected based on the
+ * board properties in board.c
+ *
+ * On both boards sys_rst_l is used as an output to trigger warm resets. The ARM
+ * core can't trigger an interrupt if it's driving it as an output so we attach
+ * two internal GPIOs to the same pad if sys_rst_l is also being used to detect
+ * system resets.
+ */
+GPIO_INT(TPM_RST_L, PIN(1, 0), GPIO_INT_RISING, tpm_rst_deasserted)
+GPIO_INT(DETECT_AP, PIN(1, 1), GPIO_INT_HIGH, ap_detect_asserted)
+GPIO_INT(DETECT_EC, PIN(1, 2), GPIO_INT_HIGH, ec_detect_asserted)
+/*
+ * DETECT_SERVO and EC_TX_CR50_RX pins must NOT be changed without also changing
+ * the pinmux_regval fields in the bitbang_config in board.c. The pinmux values
+ * in the config assume the pin definitions here.
*/
-GPIO_INT(SYS_RST_L_IN, PIN(1, 0), GPIO_INT_FALLING, sys_rst_asserted)
-GPIO_INT(AP_ON, PIN(1, 1), GPIO_INT_RISING, device_state_on)
-GPIO_INT(EC_ON, PIN(1, 2), GPIO_INT_RISING, device_state_on)
-GPIO_INT(SERVO_UART2_ON, PIN(1, 3), GPIO_INT_RISING | GPIO_PULL_DOWN,
- device_state_on)
-GPIO_INT(AP_OFF, PIN(1, 4), GPIO_INT_FALLING, device_state_off)
-GPIO_INT(EC_OFF, PIN(1, 5), GPIO_INT_FALLING, device_state_off)
-GPIO_INT(SERVO_UART2_OFF, PIN(1, 6), GPIO_INT_FALLING | GPIO_PULL_DOWN,
- device_state_off)
+GPIO_INT(DETECT_SERVO, PIN(1, 3), GPIO_INT_HIGH | GPIO_PULL_DOWN,
+ servo_detect_asserted)
+GPIO_INT(EC_TX_CR50_RX, PIN(1, 4), GPIO_INT_FALLING, ec_tx_cr50_rx)
/* Pull this low to interrupt the AP */
GPIO(INT_AP_L, PIN(0, 0), GPIO_OUT_HIGH)
@@ -32,23 +77,45 @@ GPIO(AP_FLASH_SELECT, PIN(0, 2), GPIO_OUT_LOW)
/* Pull this low to reset the AP. (We reset the EC with the RBOX.) */
GPIO(SYS_RST_L_OUT, PIN(0, 4), GPIO_INPUT)
-/* Indicate to EC when CCD is enabled. EC can pull this down too, to tell us if
- * it decided instead. The pullup is on the EC's side. */
-GPIO(CCD_MODE_L, PIN(0, 5), GPIO_ODR_HIGH)
+/*
+ * Indicate to EC when CCD is enabled. EC can pull this down too, to tell us if
+ * it decided instead.
+ */
+GPIO(CCD_MODE_L, PIN(0, 5), GPIO_INPUT | GPIO_PULL_UP)
/* Battery present signal is active low */
GPIO(BATT_PRES_L, PIN(0, 6), GPIO_INPUT)
/* GPIOs used to tristate the SPI bus */
-GPIO(SPI_MOSI, PIN(0, 7), GPIO_INPUT)
-GPIO(SPI_CLK, PIN(0, 8), GPIO_INPUT)
+GPIO(SPI_MOSI, PIN(0, 7), GPIO_INPUT | GPIO_PULL_DOWN)
+GPIO(SPI_CLK, PIN(0, 8), GPIO_INPUT | GPIO_PULL_DOWN)
GPIO(SPI_CS_L, PIN(0, 9), GPIO_INPUT)
-/* Control the load switch powering the INA 3.3V rail */
-GPIO(EN_PP3300_INA_L, PIN(1, 9), GPIO_ODR_HIGH)
+/* Used during *chip* factory process. */
+GPIO(DIOB4, PIN(0, 10), GPIO_INPUT | GPIO_PULL_DOWN)
/* GPIOs used for Cr50 strapping options */
-GPIO(STRAP0, PIN(0, 10), GPIO_INPUT)
+GPIO(STRAP_A0, PIN(1, 12), GPIO_INPUT)
+GPIO(STRAP_A1, PIN(1, 13), GPIO_INPUT)
+GPIO(STRAP_B0, PIN(1, 14), GPIO_INPUT)
+GPIO(STRAP_B1, PIN(1, 15), GPIO_INPUT)
+
+/* Control the load switch powering the INA 3.3V rail */
+GPIO(EN_PP3300_INA_L, PIN(0, 11), GPIO_ODR_HIGH)
+/* GPIOs used for I2CM pins for INAs */
+GPIO(I2C_SCL_INA, PIN(0, 12), GPIO_INPUT)
+GPIO(I2C_SDA_INA, PIN(0, 13), GPIO_INPUT)
+
+/*
+ * Use this to poll the state of the I2CS SDA line. Note that this is not
+ * necessary if SPI interface is used to communicate with the AP, if needed,
+ * this GPIO could be reclaimed in that case.
+ *
+ * Actual attachment of this GPIO to the SDA line happens in board.c only when
+ * I2CS interface is required. Should this GPIO ever change, the code setting
+ * up the pinmux in board.c will have to change as well.
+ */
+GPIO(I2CS_SDA, PIN(0, 14), GPIO_INPUT)
/* Unimplemented signals which we need to emulate for now */
/* TODO(wfrichar): Half the boards don't use this signal. Take it out. */
@@ -69,15 +136,24 @@ PINMUX(GPIO(INT_AP_L), A5, DIO_INPUT) /* DIOB7 is p_digitial_od */
PINMUX(GPIO(EC_FLASH_SELECT), B2, DIO_INPUT)
PINMUX(GPIO(AP_FLASH_SELECT), B3, DIO_INPUT)
PINMUX(GPIO(EN_PP3300_INA_L), B7, DIO_INPUT)
-PINMUX(GPIO(SYS_RST_L_IN), M0, DIO_WAKE_FALLING)
+/*
+ * To allow the EC to drive the signal we set sys_rst_l_out as an input here and
+ * only change it to an output when we want to assert the signal.
+ */
PINMUX(GPIO(SYS_RST_L_OUT), M0, DIO_INPUT)
-PINMUX(GPIO(CCD_MODE_L), M1, DIO_INPUT)
+/*
+ * CCD_MODE_L is an input above, but we need to be able to drive it as
+ * an output if Rdd detects the debug cable. So set DIO_OUTPUT as
+ * well. It will be tristated initially, because we don't set
+ * GPIO_OUTPUT above.
+ */
+PINMUX(GPIO(CCD_MODE_L), M1, DIO_INPUT | DIO_OUTPUT)
PINMUX(GPIO(BATT_PRES_L), M2, 0)
-PINMUX(GPIO(STRAP0), A1, DIO_INPUT)
-
+PINMUX(GPIO(I2C_SCL_INA), B0, DIO_INPUT)
+PINMUX(GPIO(I2C_SDA_INA), B1, DIO_INPUT)
/* UARTs */
PINMUX(FUNC(UART0_TX), A0, DIO_OUTPUT) /* Cr50 console */
-PINMUX(FUNC(UART0_RX), A13, DIO_INPUT | DIO_WAKE_FALLING)
+PINMUX(FUNC(UART0_RX), A13, DIO_INPUT | DIO_WAKE_LOW)
/*
* UART1_TX and UART2_TX are configured in usart.c. They are not set as outputs
* here in order to avoid interfering with servo. They can be controlled using
@@ -97,12 +173,9 @@ PINMUX(FUNC(UART2_RX), B6, DIO_INPUT) /* EC console */
* driving the cr50 uart TX at the same time as servo is driving those pins may
* damage both servo and cr50.
*/
-PINMUX(GPIO(AP_ON), A3, DIO_INPUT)
-PINMUX(GPIO(AP_OFF), A3, DIO_INPUT)
-PINMUX(GPIO(EC_ON), B6, DIO_INPUT)
-PINMUX(GPIO(EC_OFF), B6, DIO_INPUT)
-PINMUX(GPIO(SERVO_UART2_ON), B5, DIO_INPUT)
-PINMUX(GPIO(SERVO_UART2_OFF), B5, DIO_INPUT)
+PINMUX(GPIO(DETECT_EC), B6, DIO_INPUT)
+PINMUX(GPIO(EC_TX_CR50_RX), B6, DIO_INPUT)
+PINMUX(GPIO(DETECT_SERVO), B5, DIO_INPUT)
/*
* I2CS pins are bi-directional and would be configured here as shown. However,
@@ -137,4 +210,8 @@ PINMUX(GPIO(SERVO_UART2_OFF), B5, DIO_INPUT)
PINMUX(GPIO(SPI_MOSI), A4, DIO_OUTPUT)
PINMUX(GPIO(SPI_CLK), A8, DIO_OUTPUT)
PINMUX(GPIO(SPI_CS_L), A14, DIO_OUTPUT)
+
+PINMUX(GPIO(DIOB4), B4, DIO_INPUT)
+
+
#undef PINMUX
diff --git a/board/cr50/pinweaver_tpm_imports.c b/board/cr50/pinweaver_tpm_imports.c
new file mode 100644
index 0000000000..eec2284dfd
--- /dev/null
+++ b/board/cr50/pinweaver_tpm_imports.c
@@ -0,0 +1,20 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <pinweaver_tpm_imports.h>
+
+#include <Global.h>
+#include <util.h>
+
+uint32_t get_restart_count(void)
+{
+ return gp.resetCount;
+}
+
+void get_storage_seed(void *buf, size_t *len)
+{
+ *len = MIN(*len, sizeof(gp.SPSeed));
+ memcpy(buf, &gp.SPSeed, *len);
+}
diff --git a/board/cr50/power_button.c b/board/cr50/power_button.c
new file mode 100644
index 0000000000..bf178e893f
--- /dev/null
+++ b/board/cr50/power_button.c
@@ -0,0 +1,118 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "console.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "physical_presence.h"
+#include "rbox.h"
+#include "registers.h"
+#include "system.h"
+#include "system_chip.h"
+#include "task.h"
+#include "timer.h"
+
+#define CPRINTS(format, args...) cprints(CC_RBOX, format, ## args)
+#define CPRINTF(format, args...) cprintf(CC_RBOX, format, ## args)
+
+/**
+ * Enable/disable power button interrupt.
+ *
+ * @param enable Enable (!=0) or disable (==0)
+ */
+static void power_button_enable_interrupt(int enable)
+{
+ if (enable) {
+ /* Clear any leftover power button interrupts */
+ GWRITE_FIELD(RBOX, INT_STATE, INTR_PWRB_IN_FED, 1);
+
+ /* Enable power button interrupt */
+ GWRITE_FIELD(RBOX, INT_ENABLE, INTR_PWRB_IN_FED, 1);
+ task_enable_irq(GC_IRQNUM_RBOX0_INTR_PWRB_IN_FED_INT);
+ } else {
+ GWRITE_FIELD(RBOX, INT_ENABLE, INTR_PWRB_IN_FED, 0);
+ task_disable_irq(GC_IRQNUM_RBOX0_INTR_PWRB_IN_FED_INT);
+ }
+}
+
+static void power_button_handler(void)
+{
+ CPRINTS("power button pressed");
+
+ if (physical_detect_press() != EC_SUCCESS) {
+ /* Not consumed by physical detect */
+#ifdef CONFIG_U2F
+ /* Track last power button press for U2F */
+ power_button_record();
+#endif
+ }
+
+ GWRITE_FIELD(RBOX, INT_STATE, INTR_PWRB_IN_FED, 1);
+}
+DECLARE_IRQ(GC_IRQNUM_RBOX0_INTR_PWRB_IN_FED_INT, power_button_handler, 1);
+
+#ifdef CONFIG_U2F
+static void power_button_init(void)
+{
+ /*
+ * Enable power button interrupts all the time for U2F.
+ *
+ * Ideally U2F should only enable physical presence after the start of
+ * a U2F request (using atomic operations for the PP enable mask so it
+ * plays nicely with CCD config), but that doesn't happen yet.
+ */
+ power_button_enable_interrupt(1);
+}
+DECLARE_HOOK(HOOK_INIT, power_button_init, HOOK_PRIO_DEFAULT);
+#endif /* CONFIG_U2F */
+
+void board_physical_presence_enable(int enable)
+{
+#ifndef CONFIG_U2F
+ /* Enable/disable power button interrupts */
+ power_button_enable_interrupt(enable);
+#endif
+
+ /* Stay awake while we're doing this, just in case. */
+ if (enable)
+ disable_sleep(SLEEP_MASK_PHYSICAL_PRESENCE);
+ else
+ enable_sleep(SLEEP_MASK_PHYSICAL_PRESENCE);
+}
+
+static int command_powerbtn(int argc, char **argv)
+{
+ char *e;
+ int ms = 200;
+
+ if (argc > 1) {
+ if (!strcasecmp("pulse", argv[1])) {
+ if (argc == 3) {
+ ms = strtoi(argv[2], &e, 0);
+ if (*e)
+ return EC_ERROR_PARAM2;
+ }
+
+ ccprintf("Force %dms power button press\n", ms);
+
+ rbox_powerbtn_press();
+ msleep(ms);
+ rbox_powerbtn_release();
+ } else if (!strcasecmp("press", argv[1])) {
+ rbox_powerbtn_press();
+ } else if (!strcasecmp("release", argv[1])) {
+ rbox_powerbtn_release();
+ } else
+ return EC_ERROR_PARAM1;
+ }
+
+ ccprintf("powerbtn: %s\n",
+ rbox_powerbtn_override_is_enabled() ? "forced press" :
+ rbox_powerbtn_is_pressed() ? "pressed\n" : "released\n");
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(powerbtn, command_powerbtn,
+ "[pulse [ms] | press | release]",
+ "get/set the state of the power button");
diff --git a/board/cr50/rdd.c b/board/cr50/rdd.c
index a01c35cea7..2f12db089f 100644
--- a/board/cr50/rdd.c
+++ b/board/cr50/rdd.c
@@ -3,223 +3,450 @@
* found in the LICENSE file.
*/
-#include "case_closed_debug.h"
+#include "case_closed_debug.h" /* For ccd_ext_is_enabled() */
+#include "ccd_config.h"
#include "console.h"
-#include "device_state.h"
#include "gpio.h"
#include "hooks.h"
+#include "i2c.h"
+#include "rbox.h"
#include "rdd.h"
#include "registers.h"
#include "system.h"
+#include "uart_bitbang.h"
#include "uartn.h"
#include "usb_api.h"
+#include "usb_console.h"
+#include "usb_i2c.h"
+#include "usb_spi.h"
+
+/* Include the dazzlingly complex macro to instantiate the USB SPI config */
+USB_SPI_CONFIG(ccd_usb_spi, USB_IFACE_SPI, USB_EP_SPI);
#define CPRINTS(format, args...) cprints(CC_USB, format, ## args)
+#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args)
-static int ec_uart_enabled, enable_usb_wakeup;
-static int usb_is_initialized;
+static enum device_state state = DEVICE_STATE_INIT;
-struct uart_config {
- const char *name;
- enum device_type device;
- int tx_signal;
-};
+/* Flags for CCD blocking */
+enum ccd_block_flags {
+ /*
+ * UARTs. Disabling these can be helpful if the AP or EC is doing
+ * something which creates an interrupt storm on these ports.
+ */
+ CCD_BLOCK_AP_UART = (1 << 0),
+ CCD_BLOCK_EC_UART = (1 << 1),
-static struct uart_config uarts[] = {
- [UART_AP] = {"AP", DEVICE_AP, GC_PINMUX_UART1_TX_SEL},
- [UART_EC] = {"EC", DEVICE_EC, GC_PINMUX_UART2_TX_SEL},
+ /*
+ * Any ports shared with servo. Disabling these will stop CCD from
+ * interfering with servo, in the case where both CCD and servo is
+ * connected but servo isn't properly detected.
+ */
+ CCD_BLOCK_SERVO_SHARED = (1 << 2)
};
-static int ccd_is_enabled(void)
-{
- return !gpio_get_level(GPIO_CCD_MODE_L);
-}
+/* Which UARTs are blocked by console command */
+static uint8_t ccd_block;
-int is_utmi_wakeup_allowed(void)
+int ccd_ext_is_enabled(void)
{
- return enable_usb_wakeup;
+ return state == DEVICE_STATE_CONNECTED;
}
-
-/* If the UART TX is enabled the pinmux select will have a non-zero value */
-int uartn_enabled(int uart)
+/* If the UART TX is connected the pinmux select will have a non-zero value */
+int uart_tx_is_connected(int uart)
{
if (uart == UART_AP)
return GREAD(PINMUX, DIOA7_SEL);
return GREAD(PINMUX, DIOB5_SEL);
}
-/* Connect the UART pin to the given signal */
+/**
+ * Connect the UART pin to the given signal
+ *
+ * @param uart the uart peripheral number
+ * @param signal the pinmux selector value for the gpio or peripheral
+ * function. 0 to disable the output.
+ */
static void uart_select_tx(int uart, int signal)
{
- if (uart == UART_AP)
+ if (uart == UART_AP) {
GWRITE(PINMUX, DIOA7_SEL, signal);
- else
+ } else {
GWRITE(PINMUX, DIOB5_SEL, signal);
-}
-static int servo_is_connected(void)
-{
- return device_get_state(DEVICE_SERVO) == DEVICE_STATE_ON;
+ /* Remove the pulldown when we are driving the signal */
+ GWRITE_FIELD(PINMUX, DIOB5_CTL, PD, signal ? 0 : 1);
+ }
}
void uartn_tx_connect(int uart)
{
- if (uart == UART_EC && !ec_uart_enabled)
+ /*
+ * Don't drive TX unless the debug cable is connected (we have
+ * something to transmit) and servo is disconnected (we won't be
+ * drive-fighting with servo).
+ */
+ if (servo_is_connected() || !ccd_ext_is_enabled())
return;
- if (!ccd_is_enabled())
- return;
+ if (uart == UART_AP) {
+ if (!ccd_is_cap_enabled(CCD_CAP_GSC_TX_AP_RX))
+ return;
- if (servo_is_connected()) {
- CPRINTS("Servo is attached cannot enable %s UART",
- uarts[uart].name);
- return;
- }
+ if (!ap_is_on())
+ return;
+
+ uart_select_tx(UART_AP, GC_PINMUX_UART1_TX_SEL);
+ } else {
+ if (!ccd_is_cap_enabled(CCD_CAP_GSC_TX_EC_RX))
+ return;
+
+ if (!ec_is_on())
+ return;
- if (device_get_state(uarts[uart].device) == DEVICE_STATE_ON)
- uart_select_tx(uart, uarts[uart].tx_signal);
- else if (!uartn_enabled(uart))
- CPRINTS("%s is powered off", uarts[uart].name);
+ uart_select_tx(UART_EC, GC_PINMUX_UART2_TX_SEL);
+ }
}
void uartn_tx_disconnect(int uart)
{
- /* If servo is connected disable UART */
- if (servo_is_connected())
- ec_uart_enabled = 0;
-
/* Disconnect the TX pin from UART peripheral */
uart_select_tx(uart, 0);
}
-void rdd_attached(void)
-{
- /* Indicate case-closed debug mode (active low) */
- gpio_set_level(GPIO_CCD_MODE_L, 0);
+/*
+ * Flags for the current CCD device state. This is used for determining what
+ * hardware devices we've enabled now, and which we want enabled.
+ */
+enum ccd_state_flag {
+ /* Flags for individual devices/ports */
+
+ /* AP UART is enabled. RX-only, unless TX is also enabled. */
+ CCD_ENABLE_UART_AP = (1 << 0),
+
+ /* AP UART transmit is enabled. Requires AP UART enabled. */
+ CCD_ENABLE_UART_AP_TX = (1 << 1),
- /* Enable CCD */
- ccd_set_mode(CCD_MODE_ENABLED);
+ /* EC UART is enabled. RX-only, unless TX is also enabled. */
+ CCD_ENABLE_UART_EC = (1 << 2),
- enable_usb_wakeup = 1;
- uartn_tx_connect(UART_AP);
+ /* EC UART transmit is enabled. Requires EC UART enabled. */
+ CCD_ENABLE_UART_EC_TX = (1 << 3),
- /* Enable device state monitoring */
- device_detect_state_enable(1);
+ /*
+ * EC UART bit-banging is enabled. Requires EC UART enabled, and
+ * blocks EC UART transmit.
+ */
+ CCD_ENABLE_UART_EC_BITBANG = (1 << 4),
+
+ /* I2C port is enabled */
+ CCD_ENABLE_I2C = (1 << 5),
+
+ /* SPI port is enabled for AP and/or EC flash */
+ CCD_ENABLE_SPI = (1 << 6),
+};
+
+int console_is_restricted(void)
+{
+ return !ccd_is_cap_enabled(CCD_CAP_GSC_RESTRICTED_CONSOLE);
}
-void rdd_detached(void)
+/**
+ * Return the currently enabled state flags (see enum ccd_state_flag).
+ */
+static uint32_t get_state_flags(void)
{
- /* Disable device state monitoring */
- device_detect_state_enable(0);
+ uint32_t flags_now = 0;
- /* Disconnect from AP and EC UART TX peripheral from gpios */
- uartn_tx_disconnect(UART_EC);
- uartn_tx_disconnect(UART_AP);
+ if (uartn_is_enabled(UART_AP))
+ flags_now |= CCD_ENABLE_UART_AP;
+ if (uart_tx_is_connected(UART_AP))
+ flags_now |= CCD_ENABLE_UART_AP_TX;
+ if (uartn_is_enabled(UART_EC))
+ flags_now |= CCD_ENABLE_UART_EC;
+ if (uart_tx_is_connected(UART_EC))
+ flags_now |= CCD_ENABLE_UART_EC_TX;
- /* Disable the AP and EC UART peripheral */
- uartn_disable(UART_AP);
- uartn_disable(UART_EC);
+#ifdef CONFIG_UART_BITBANG
+ if (uart_bitbang_is_enabled(UART_EC))
+ flags_now |= CCD_ENABLE_UART_EC_BITBANG;
+#endif
- /* Done with case-closed debug mode */
- gpio_set_level(GPIO_CCD_MODE_L, 1);
+ if (usb_i2c_board_is_enabled())
+ flags_now |= CCD_ENABLE_I2C;
- enable_usb_wakeup = 0;
- ec_uart_enabled = 0;
+ if (ccd_usb_spi.state->enabled_device)
+ flags_now |= CCD_ENABLE_SPI;
- /* Disable CCD */
- ccd_set_mode(CCD_MODE_DISABLED);
+ return flags_now;
}
-void ccd_phy_init(int enable_ccd)
+/**
+ * Print the state flags to the specified output channel
+ *
+ * @param channel Console channel
+ * @param flags Flags to print
+ */
+static void print_state_flags(enum console_channel channel, uint32_t flags)
{
- uint32_t properties = system_get_board_properties();
- /*
- * For boards that have one phy connected to the AP and one to the
- * external port PHY0 is for the AP and PHY1 is for CCD.
- */
- uint32_t which_phy = enable_ccd ? USB_SEL_PHY1 : USB_SEL_PHY0;
+ if (flags & CCD_ENABLE_UART_AP)
+ cprintf(channel, " UARTAP");
+ if (flags & CCD_ENABLE_UART_AP_TX)
+ cprintf(channel, "+TX");
+ if (flags & CCD_ENABLE_UART_EC)
+ cprintf(channel, " UARTEC");
+ if (flags & CCD_ENABLE_UART_EC_TX)
+ cprintf(channel, "+TX");
+ if (flags & CCD_ENABLE_UART_EC_BITBANG)
+ cprintf(channel, "+BB");
+ if (flags & CCD_ENABLE_I2C)
+ cprintf(channel, " I2C");
+ if (flags & CCD_ENABLE_SPI)
+ cprintf(channel, " SPI");
+}
- /*
- * TODO: if both PHYs are connected to the external port select the
- * PHY based on the detected polarity
- */
- usb_select_phy(which_phy);
+static void ccd_state_change_hook(void)
+{
+ uint32_t flags_now;
+ uint32_t flags_want = 0;
+ uint32_t delta;
+
+ /* Check what's enabled now */
+ flags_now = get_state_flags();
+
+ /* Start out by figuring what flags we might want enabled */
+
+ /* Enable EC/AP UART RX if that device is on */
+ if (ap_is_on())
+ flags_want |= CCD_ENABLE_UART_AP;
+ if (ec_is_rx_allowed())
+ flags_want |= CCD_ENABLE_UART_EC;
+
+#ifdef CONFIG_UART_BITBANG
+ /* EC must be all the way on for bit-banging the EC UART */
+ if (ec_is_on() && uart_bitbang_is_wanted(UART_EC))
+ flags_want |= CCD_ENABLE_UART_EC_BITBANG;
+#endif
+
+ /* External CCD will try to enable all the ports */
+ if (ccd_ext_is_enabled())
+ flags_want |= (CCD_ENABLE_UART_AP_TX | CCD_ENABLE_UART_EC_TX |
+ CCD_ENABLE_I2C | CCD_ENABLE_SPI);
+
+ /* Then disable flags we can't have */
+
+ /* Servo takes over UART TX, I2C, and SPI */
+ if (servo_is_connected() || (ccd_block & CCD_BLOCK_SERVO_SHARED))
+ flags_want &= ~(CCD_ENABLE_UART_AP_TX | CCD_ENABLE_UART_EC_TX |
+ CCD_ENABLE_UART_EC_BITBANG | CCD_ENABLE_I2C |
+ CCD_ENABLE_SPI);
+
+ /* Disable based on capabilities */
+ if (!ccd_is_cap_enabled(CCD_CAP_GSC_RX_AP_TX))
+ flags_want &= ~CCD_ENABLE_UART_AP;
+ if (!ccd_is_cap_enabled(CCD_CAP_GSC_TX_AP_RX))
+ flags_want &= ~CCD_ENABLE_UART_AP_TX;
+ if (!ccd_is_cap_enabled(CCD_CAP_GSC_RX_EC_TX))
+ flags_want &= ~CCD_ENABLE_UART_EC;
+ if (!ccd_is_cap_enabled(CCD_CAP_GSC_TX_EC_RX))
+ flags_want &= ~(CCD_ENABLE_UART_EC_TX |
+ CCD_ENABLE_UART_EC_BITBANG);
+ if (!ccd_is_cap_enabled(CCD_CAP_I2C))
+ flags_want &= ~CCD_ENABLE_I2C;
/*
- * If the usb is going to be initialized on the AP PHY, but the AP is
- * off, wait until HOOK_CHIPSET_RESUME to initialize usb.
+ * EC and AP flash block on a per-packet basis, but if we don't have
+ * access to either one, turn off SPI.
*/
- if (!enable_ccd && device_get_state(DEVICE_AP) != DEVICE_STATE_ON) {
- usb_is_initialized = 0;
+ if (!ccd_is_cap_enabled(CCD_CAP_AP_FLASH) &&
+ !ccd_is_cap_enabled(CCD_CAP_EC_FLASH))
+ flags_want &= ~CCD_ENABLE_SPI;
+
+ /* EC UART TX blocked by bit-banging */
+ if (flags_want & CCD_ENABLE_UART_EC_BITBANG)
+ flags_want &= ~CCD_ENABLE_UART_EC_TX;
+
+ /* UARTs can be specifically blocked by console command */
+ if (ccd_block & CCD_BLOCK_AP_UART)
+ flags_want &= ~CCD_ENABLE_UART_AP;
+ if (ccd_block & CCD_BLOCK_EC_UART)
+ flags_want &= ~CCD_ENABLE_UART_EC;
+
+ /* UARTs are either RX-only or RX+TX, so no RX implies no TX */
+ if (!(flags_want & CCD_ENABLE_UART_AP))
+ flags_want &= ~CCD_ENABLE_UART_AP_TX;
+ if (!(flags_want & CCD_ENABLE_UART_EC))
+ flags_want &= ~CCD_ENABLE_UART_EC_TX;
+
+ /* If no change, we're done */
+ if (flags_now == flags_want)
return;
- }
+ CPRINTF("[%T CCD state:");
+ print_state_flags(CC_USB, flags_want);
+ CPRINTF("]\n");
+
+ /* Handle turning things off */
+ delta = flags_now & ~flags_want;
+ if (delta & CCD_ENABLE_UART_AP)
+ uartn_disable(UART_AP);
+ if (delta & CCD_ENABLE_UART_AP_TX)
+ uartn_tx_disconnect(UART_AP);
+ if (delta & CCD_ENABLE_UART_EC)
+ uartn_disable(UART_EC);
+ if (delta & CCD_ENABLE_UART_EC_TX)
+ uartn_tx_disconnect(UART_EC);
+#ifdef CONFIG_UART_BITBANG
+ if (delta & CCD_ENABLE_UART_EC_BITBANG)
+ uart_bitbang_disable(UART_EC);
+#endif
+ if (delta & CCD_ENABLE_I2C)
+ usb_i2c_board_disable();
+ if (delta & CCD_ENABLE_SPI)
+ usb_spi_enable(&ccd_usb_spi, 0);
+
+ /* Handle turning things on */
+ delta = flags_want & ~flags_now;
+ if (delta & CCD_ENABLE_UART_AP)
+ uartn_enable(UART_AP);
+ if (delta & CCD_ENABLE_UART_AP_TX)
+ uartn_tx_connect(UART_AP);
+ if (delta & CCD_ENABLE_UART_EC)
+ uartn_enable(UART_EC);
+ if (delta & CCD_ENABLE_UART_EC_TX)
+ uartn_tx_connect(UART_EC);
+#ifdef CONFIG_UART_BITBANG
+ if (delta & CCD_ENABLE_UART_EC_BITBANG)
+ uart_bitbang_enable(UART_EC);
+#endif
+ if (delta & CCD_ENABLE_I2C)
+ usb_i2c_board_enable();
+ if (delta & CCD_ENABLE_SPI)
+ usb_spi_enable(&ccd_usb_spi, 1);
+}
+DECLARE_DEFERRED(ccd_state_change_hook);
+
+void ccd_update_state(void)
+{
/*
- * If the board has the non-ccd phy connected to the AP initialize the
- * phy no matter what. Otherwise only initialized the phy if ccd is
- * enabled.
+ * Use a deferred call to serialize changes from CCD config, RDD
+ * attach/detach, EC/AP startup or shutdown, etc.
*/
- if ((properties & BOARD_USB_AP) || enable_ccd) {
- usb_init();
- usb_is_initialized = 1;
- }
+ hook_call_deferred(&ccd_state_change_hook_data, 0);
}
-void disable_ap_usb(void)
+/*****************************************************************************/
+
+static void ccd_ext_detect(void)
{
- if ((system_get_board_properties() & BOARD_USB_AP) &&
- !ccd_is_enabled() && usb_is_initialized) {
+ /* The CCD mode pin is active low. */
+ int enable = !gpio_get_level(GPIO_CCD_MODE_L);
+
+ if (enable == ccd_ext_is_enabled())
+ return;
+
+ if (enable) {
+ /*
+ * If we're not disconnected, release USB to ensure it's in a
+ * good state before we usb_init(). This matches what
+ * common/case_closed_debug.c does.
+ *
+ * Not sure exactly why this is necessary. It could be because
+ * that also has CCD_MODE_PARTIAL, and the only way to go
+ * cleanly between ENABLED and PARTIAL is to disable things and
+ * then re-enable only what's needed?
+ *
+ * TODO(rspangler): Figure out whether we can delete this.
+ */
+ if (state != DEVICE_STATE_DISCONNECTED)
+ usb_release();
+
+ CPRINTS("CCD EXT enable");
+ state = DEVICE_STATE_CONNECTED;
+
+ usb_init();
+ usb_console_enable(1, 0);
+ } else {
+ CPRINTS("CCD EXT disable");
+ state = DEVICE_STATE_DISCONNECTED;
+
usb_release();
- usb_is_initialized = 0;
+ usb_console_enable(0, 0);
}
+
+ ccd_update_state();
}
-DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, disable_ap_usb, HOOK_PRIO_DEFAULT);
+DECLARE_HOOK(HOOK_SECOND, ccd_ext_detect, HOOK_PRIO_DEFAULT);
-void enable_ap_usb(void)
+static void print_ccd_ports_blocked(void)
{
- if ((system_get_board_properties() & BOARD_USB_AP) &&
- !ccd_is_enabled() && !usb_is_initialized) {
- usb_is_initialized = 1;
- usb_init();
- }
+ /* Regardless, print current state */
+ ccputs("CCD ports blocked:");
+ if (ccd_block & CCD_BLOCK_AP_UART)
+ ccputs(" AP");
+ if (ccd_block & CCD_BLOCK_EC_UART)
+ ccputs(" EC");
+ if (ccd_block & CCD_BLOCK_SERVO_SHARED)
+ ccputs(" SERVO");
+ if (!ccd_block)
+ ccputs(" (none)");
+ ccputs("\n");
}
-DECLARE_HOOK(HOOK_CHIPSET_RESUME, enable_ap_usb, HOOK_PRIO_DEFAULT);
-static int command_ccd(int argc, char **argv)
+static int command_ccd_state(int argc, char **argv)
{
- int val;
-
- if (argc > 1) {
- if (!strcasecmp("uart", argv[1]) && argc > 2) {
- if (!parse_bool(argv[2], &val))
- return EC_ERROR_PARAM2;
-
- if (val) {
- ec_uart_enabled = 1;
- uartn_tx_connect(UART_EC);
- } else {
- ec_uart_enabled = 0;
- uartn_tx_disconnect(UART_EC);
- }
- } else if (argc == 2) {
- if (!parse_bool(argv[1], &val))
- return EC_ERROR_PARAM1;
-
- if (val)
- rdd_attached();
- else
- rdd_detached();
- } else
+ print_ap_state();
+ print_ec_state();
+ print_rdd_state();
+ print_servo_state();
+
+ ccprintf("CCD EXT: %s\n",
+ ccd_ext_is_enabled() ? "enabled" : "disabled");
+
+ ccprintf("State flags:");
+ print_state_flags(CC_COMMAND, get_state_flags());
+ ccprintf("\n");
+
+ print_ccd_ports_blocked();
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(ccdstate, command_ccd_state,
+ "",
+ "Print the case closed debug device state");
+
+static int command_ccd_block(int argc, char **argv)
+{
+ uint8_t block_flag = 0;
+ int new_state;
+
+ if (argc == 3) {
+ if (!strcasecmp(argv[1], "AP"))
+ block_flag = CCD_BLOCK_AP_UART;
+ else if (!strcasecmp(argv[1], "EC"))
+ block_flag = CCD_BLOCK_EC_UART;
+ else if (!strcasecmp(argv[1], "SERVO"))
+ block_flag = CCD_BLOCK_SERVO_SHARED;
+ else
return EC_ERROR_PARAM1;
+
+ if (!parse_bool(argv[2], &new_state))
+ return EC_ERROR_PARAM2;
+
+ if (new_state)
+ ccd_block |= block_flag;
+ else
+ ccd_block &= ~block_flag;
+
+ /* Update blocked state in deferred function */
+ ccd_update_state();
}
- ccprintf("CCD: %s\nAP UART: %s\nEC UART: %s\n",
- ccd_is_enabled() ? " enabled" : "disabled",
- uartn_enabled(UART_AP) ? " enabled" : "disabled",
- uartn_enabled(UART_EC) ? " enabled" : "disabled");
+ print_ccd_ports_blocked();
+
return EC_SUCCESS;
}
-DECLARE_CONSOLE_COMMAND(ccd, command_ccd,
- "[uart] [<BOOLEAN>]",
- "Get/set the case closed debug state");
+DECLARE_CONSOLE_COMMAND(ccdblock, command_ccd_block,
+ "[<AP | EC | SERVO> [BOOLEAN]]",
+ "Force CCD ports disabled");
diff --git a/board/cr50/recovery_button.c b/board/cr50/recovery_button.c
new file mode 100644
index 0000000000..af4d4d7c87
--- /dev/null
+++ b/board/cr50/recovery_button.c
@@ -0,0 +1,73 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Recovery button override module. */
+
+#include "common.h"
+#include "console.h"
+#include "extension.h"
+#include "registers.h"
+#include "util.h"
+
+/*
+ * The recovery button, on some systems only, is wired to KEY0 in rbox. For
+ * testing, we need to be able override the value. We'll have a vendor command
+ * such that the AP can query the state of the recovery button. However, the
+ * reported state can only be overridden with a console command given sufficient
+ * privileges.
+ */
+static uint8_t rec_btn_force_pressed;
+
+static uint8_t is_rec_btn_pressed(void)
+{
+ if (rec_btn_force_pressed)
+ return 1;
+
+ /*
+ * If not force pressed, check the actual state of button. Note,
+ * the value is inverted because the button is active low.
+ */
+ return !GREAD_FIELD(RBOX, CHECK_INPUT, KEY0_IN);
+}
+
+static int command_recbtnforce(int argc, char **argv)
+{
+ int val;
+
+ if (argc > 2)
+ return EC_ERROR_PARAM_COUNT;
+
+ if (argc == 2) {
+ /* Make sure we're allowed to override the recovery button. */
+ if (console_is_restricted())
+ return EC_ERROR_ACCESS_DENIED;
+
+ if (!parse_bool(argv[1], &val))
+ return EC_ERROR_PARAM1;
+
+ rec_btn_force_pressed = val;
+ }
+
+ ccprintf("RecBtn: %s pressed\n",
+ rec_btn_force_pressed ? "forced" :
+ is_rec_btn_pressed() ? "" : "not");
+
+ return EC_SUCCESS;
+}
+DECLARE_SAFE_CONSOLE_COMMAND(recbtnforce, command_recbtnforce,
+ "[enable | disable]",
+ "Force enable the reported recbtn state.");
+
+static enum vendor_cmd_rc vc_get_rec_btn(enum vendor_cmd_cc code,
+ void *buf,
+ size_t input_size,
+ size_t *response_size)
+{
+ *(uint8_t *)buf = is_rec_btn_pressed();
+ *response_size = 1;
+
+ return VENDOR_RC_SUCCESS;
+}
+DECLARE_VENDOR_COMMAND(VENDOR_CC_GET_REC_BTN, vc_get_rec_btn);
diff --git a/board/cr50/scratch_reg1.h b/board/cr50/scratch_reg1.h
new file mode 100644
index 0000000000..86dc8d7e4b
--- /dev/null
+++ b/board/cr50/scratch_reg1.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __EC_BOARD_CR50_SCRATCH_REG1_H
+#define __EC_BOARD_CR50_SCRATCH_REG1_H
+
+/*
+ * Bit assignments of the LONG_LIFE_SCRATCH1 register. This register survives
+ * all kinds of resets, it is cleared only on the Power ON event.
+ */
+#define BOARD_SLAVE_CONFIG_SPI (1 << 0) /* TPM uses SPI interface */
+#define BOARD_SLAVE_CONFIG_I2C (1 << 1) /* TPM uses I2C interface */
+
+/*
+ * The gaps are left to enusre backwards compatibility with the earliest cr50
+ * code releases. It will be possible to safely reuse these gaps if and when the
+ * rest of the bits are taken.
+ */
+
+/* TODO(crosbug.com/p/56945): Remove when sys_rst_l has an external pullup */
+#define BOARD_NEEDS_SYS_RST_PULL_UP (1 << 5) /* Add a pullup to sys_rst_l */
+#define BOARD_USE_PLT_RESET (1 << 6) /* Use plt_rst_l instead of */
+ /* sys_rst_l to monitor the */
+ /* system resets */
+
+/* Bits to store write protect bit state across deep sleep and resets. */
+#define BOARD_WP_ASSERTED (1 << 8)
+#define BOARD_FORCING_WP (1 << 9)
+
+/*
+ * Bit to signal to compatible RO to suppress its uart output.
+ * Helps to reduce time to resume from deep sleep.
+ */
+#define BOARD_NO_RO_UART (1 << 10)
+
+/*
+ * Bits to store current case-closed debug state across deep sleep.
+ *
+ * DO NOT examine these bits to determine the current CCD state. Call methods
+ * from case_closed_debug.h instead.
+ */
+#define BOARD_CCD_SHIFT 11
+#define BOARD_CCD_STATE (3 << BOARD_CCD_SHIFT)
+
+/*
+ * Macro to capture all properties related to board strapping pins. This must be
+ * updated if additional strap related properties are added.
+ */
+#define BOARD_ALL_PROPERTIES (BOARD_SLAVE_CONFIG_SPI | BOARD_SLAVE_CONFIG_I2C \
+ | BOARD_NEEDS_SYS_RST_PULL_UP | BOARD_USE_PLT_RESET)
+
+#endif /* ! __EC_BOARD_CR50_SCRATCH_REG1_H */
diff --git a/board/cr50/servo_state.c b/board/cr50/servo_state.c
new file mode 100644
index 0000000000..8c574f5a4e
--- /dev/null
+++ b/board/cr50/servo_state.c
@@ -0,0 +1,207 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Servo state machine.
+ */
+#include "common.h"
+#include "console.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "uart_bitbang.h"
+#include "uartn.h"
+#include "usb_i2c.h"
+
+#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args)
+
+static enum device_state state = DEVICE_STATE_INIT;
+
+void print_servo_state(void)
+{
+ ccprintf("Servo: %s\n", device_state_name(state));
+}
+
+int servo_is_connected(void)
+{
+ /*
+ * If we're connected, we definitely know we are. If we're debouncing,
+ * then we were connected and might still be. If we haven't
+ * initialized yet, we'd bettter assume we're connected until we prove
+ * otherwise. In any of these cases, it's not safe to allow ports to
+ * be connected because that would block detecting servo.
+ */
+ return (state == DEVICE_STATE_CONNECTED ||
+ state == DEVICE_STATE_DEBOUNCING ||
+ state == DEVICE_STATE_INIT ||
+ state == DEVICE_STATE_INIT_DEBOUNCING);
+}
+
+/**
+ * Set the servo state.
+ *
+ * Done as a function to make it easier to debug state transitions. Note that
+ * this ONLY sets the state (and possibly prints debug info), and doesn't do
+ * all the additional transition work that servo_disconnect(), etc. do.
+ *
+ * @param new_state State to set.
+ */
+static void set_state(enum device_state new_state)
+{
+#ifdef CR50_DEBUG_SERVO_STATE
+ /* Print all state transitions. May spam the console. */
+ if (state != new_state)
+ CPRINTS("Servo %s -> %s",
+ device_state_name(state), device_state_name(new_state));
+#endif
+ state = new_state;
+}
+
+/**
+ * Check if we can tell servo is connected.
+ *
+ * @return 1 if we can tell if servo is connected, 0 if we can't tell.
+ */
+static int servo_detectable(void)
+{
+ /*
+ * If we are driving the UART transmit line to the EC, then we can't
+ * check to see if servo is also doing so.
+ *
+ * We also need to check if we're bit-banging the EC UART, because in
+ * that case, the UART transmit line is directly controlled as a GPIO
+ * and can be high even if UART TX is disconnected.
+ */
+ return !(uart_tx_is_connected(UART_EC) ||
+ uart_bitbang_is_enabled(UART_EC));
+}
+
+/**
+ * Handle servo being disconnected
+ */
+static void servo_disconnect(void)
+{
+ if (!servo_is_connected())
+ return;
+
+ CPRINTS("Servo disconnect");
+ set_state(DEVICE_STATE_DISCONNECTED);
+ ccd_update_state();
+}
+
+/**
+ * Handle servo being connected.
+ *
+ * This can be called directly by servo_detect(), or as a deferred function.
+ * Both are in the HOOK task, so can't preempt each other.
+ */
+static void servo_connect(void)
+{
+ /*
+ * If we were debouncing disconnect, go back to connected. We never
+ * finished disconnecting, so nothing else is necessary.
+ */
+ if (state == DEVICE_STATE_DEBOUNCING)
+ set_state(DEVICE_STATE_CONNECTED);
+
+ /* If we're already connected, nothing else needs to be done */
+ if (state == DEVICE_STATE_CONNECTED)
+ return;
+
+ /*
+ * If we're still here, this is a real transition from a disconnected
+ * state, so we need to configure ports.
+ */
+ CPRINTS("Servo connect");
+ set_state(DEVICE_STATE_CONNECTED);
+ ccd_update_state();
+}
+DECLARE_DEFERRED(servo_connect);
+
+/**
+ * Servo state machine
+ */
+static void servo_detect(void)
+{
+ /* Disable interrupts if we had them on for debouncing */
+ gpio_disable_interrupt(GPIO_DETECT_SERVO);
+
+ /* If we're driving EC UART TX, we can't detect servo */
+ if (!servo_detectable()) {
+ /* We're driving one port; might as well drive them all */
+ servo_disconnect();
+
+ set_state(DEVICE_STATE_UNDETECTABLE);
+ return;
+ }
+
+ /* Handle detecting servo */
+ if (gpio_get_level(GPIO_DETECT_SERVO)) {
+ servo_connect();
+ return;
+ }
+
+ /*
+ * If servo has become detectable but wasn't detected above, assume
+ * it's disconnected.
+ *
+ * We know we were driving EC UART TX, so we want to give priority to
+ * our ability to drive it again. If we went to the debouncing state
+ * here, then we'd need to wait a second before we could drive it.
+ *
+ * This is similar to how if servo was driving EC UART TX, we go to the
+ * debouncing state below, because we want to give priority to servo
+ * being able to drive it again.
+ */
+ if (state == DEVICE_STATE_UNDETECTABLE)
+ set_state(DEVICE_STATE_DISCONNECTED);
+
+ /* Servo wasn't detected. If we're already disconnected, done. */
+ if (state == DEVICE_STATE_DISCONNECTED)
+ return;
+
+ /* If we were debouncing, we're now sure we're disconnected */
+ if (state == DEVICE_STATE_DEBOUNCING ||
+ state == DEVICE_STATE_INIT_DEBOUNCING) {
+ servo_disconnect();
+ return;
+ }
+
+ /*
+ * Otherwise, we were connected or initializing, and we're not sure if
+ * we're now disconnected or just sending a 0-bit. So start
+ * debouncing.
+ *
+ * During debouncing, servo_is_connected() will still return true, so
+ * that if both CCD and servo cables are connected, we won't start
+ * driving EC UART TX and become unable to determine the servo connect
+ * state.
+ */
+ if (state == DEVICE_STATE_INIT)
+ set_state(DEVICE_STATE_INIT_DEBOUNCING);
+ else
+ set_state(DEVICE_STATE_DEBOUNCING);
+ gpio_enable_interrupt(GPIO_DETECT_SERVO);
+}
+/*
+ * Do this at slightly elevated priority so it runs before rdd_check_pin() and
+ * ec_detect(). This increases the odds that we'll detect servo before
+ * detecting the EC. If ec_detect() ran first, it could turn on TX to the EC
+ * UART before we had a chance to detect servo. This is still a little bit of
+ * a race condition.
+ */
+DECLARE_HOOK(HOOK_SECOND, servo_detect, HOOK_PRIO_DEFAULT - 1);
+
+/**
+ * Interrupt handler for servo detect asserted
+ */
+void servo_detect_asserted(enum gpio_signal signal)
+{
+ gpio_disable_interrupt(GPIO_DETECT_SERVO);
+
+ /*
+ * If this interrupt is because servo is actually detectable (vs. we're
+ * driving the detect pin now), queue a transition back to connected.
+ */
+ if (servo_detectable())
+ hook_call_deferred(&servo_connect_data, 0);
+}
diff --git a/board/cr50/tpm2/NVMem.c b/board/cr50/tpm2/NVMem.c
index f78308033c..7921270fb2 100644
--- a/board/cr50/tpm2/NVMem.c
+++ b/board/cr50/tpm2/NVMem.c
@@ -18,21 +18,22 @@
#include "nvmem.h"
/* Local state */
+static struct {
#ifndef CONFIG_FLASH_NVMEM
-static unsigned char s_NV[NV_MEMORY_SIZE];
+ uint8_t s_NV[NV_MEMORY_SIZE];
#endif
-static BOOL s_NvIsAvailable;
-static BOOL s_NV_unrecoverable;
-static BOOL s_NV_recoverable;
-
+ BOOL s_NvIsAvailable;
+ BOOL s_NV_unrecoverable;
+ BOOL s_NV_recoverable;
+} local_state __attribute__((section("Tpm2_common.bss")));
/*
* This function is used by the simulator to set the error flags in the NV
* subsystem to simulate an error in the NV loading process.
*/
void _plat__NvErrors(BOOL recoverable, BOOL unrecoverable)
{
- s_NV_unrecoverable = unrecoverable;
- s_NV_recoverable = recoverable;
+ local_state.s_NV_unrecoverable = unrecoverable;
+ local_state.s_NV_recoverable = recoverable;
}
/*
@@ -52,8 +53,8 @@ void _plat__NvErrors(BOOL recoverable, BOOL unrecoverable)
*/
int _plat__NVEnable(void *platParameter)
{
- s_NV_unrecoverable = FALSE;
- s_NV_recoverable = FALSE;
+ local_state.s_NV_unrecoverable = FALSE;
+ local_state.s_NV_recoverable = FALSE;
#ifdef CONFIG_FLASH_NVMEM
/* TODO: Need to define what is recoverable and unrecoverable
@@ -63,12 +64,12 @@ int _plat__NVEnable(void *platParameter)
* determines that NvMem is fully erased and configures a valid
* partition. Setting both variables TRUE if NvMem is not available
*/
- s_NV_recoverable = nvmem_get_error_state() != 0;
- s_NV_unrecoverable = s_NV_recoverable;
+ local_state.s_NV_recoverable = nvmem_get_error_state() != 0;
+ local_state.s_NV_unrecoverable = local_state.s_NV_recoverable;
#endif
- if (s_NV_unrecoverable)
+ if (local_state.s_NV_unrecoverable)
return -1;
- return s_NV_recoverable;
+ return local_state.s_NV_recoverable;
}
void _plat__NVDisable(void)
@@ -96,10 +97,10 @@ int _plat__IsNvAvailable(void)
* the on chip NvMem area must be in the correct state for NvMem
* to be in 'NV is available' state.
*/
- rv = !s_NvIsAvailable || nvmem_get_error_state();
+ rv = !local_state.s_NvIsAvailable || nvmem_get_error_state();
return rv;
#else
- if (!s_NvIsAvailable)
+ if (!local_state.s_NvIsAvailable)
return 1;
return 0;
@@ -118,7 +119,7 @@ void _plat__NvMemoryRead(unsigned int startOffset,
#ifdef CONFIG_FLASH_NVMEM
nvmem_read(startOffset, size, data, NVMEM_TPM);
#else
- memcpy(data, &s_NV[startOffset], size);
+ memcpy(data, &local_state.s_NV[startOffset], size);
#endif
return;
}
@@ -135,8 +136,7 @@ _plat__NvIsDifferent(unsigned int startOffset,
#ifdef CONFIG_FLASH_NVMEM
return (nvmem_is_different(startOffset, size, data, NVMEM_TPM) != 0);
#else
- /* Do we need a safe memcmp here? */
- return (memcmp(&s_NV[startOffset], data, size) != 0);
+ return !DCRYPTO_equals(&local_state.s_NV[startOffset], data, size);
#endif
}
@@ -154,7 +154,7 @@ void _plat__NvMemoryWrite(unsigned int startOffset,
#ifdef CONFIG_FLASH_NVMEM
nvmem_write(startOffset, size, data, NVMEM_TPM);
#else
- memcpy(&s_NV[startOffset], data, size);
+ memcpy(&local_state.s_NV[startOffset], data, size);
#endif
}
@@ -172,7 +172,8 @@ void _plat__NvMemoryMove(unsigned int sourceOffset,
nvmem_move(sourceOffset, destOffset, size, NVMEM_TPM);
#else
/* Move data in RAM */
- memmove(&s_NV[destOffset], &s_NV[sourceOffset], size);
+ memmove(&local_state.s_NV[destOffset],
+ &local_state.s_NV[sourceOffset], size);
#endif
return;
}
@@ -200,7 +201,7 @@ int _plat__NvCommit(void)
*/
void _plat__SetNvAvail(void)
{
- s_NvIsAvailable = TRUE;
+ local_state.s_NvIsAvailable = TRUE;
return;
}
@@ -210,6 +211,6 @@ void _plat__SetNvAvail(void)
*/
void _plat__ClearNvAvail(void)
{
- s_NvIsAvailable = FALSE;
+ local_state.s_NvIsAvailable = FALSE;
return;
}
diff --git a/board/cr50/tpm2/aes.c b/board/cr50/tpm2/aes.c
index 164f08aeb5..72148fb0ec 100644
--- a/board/cr50/tpm2/aes.c
+++ b/board/cr50/tpm2/aes.c
@@ -228,6 +228,8 @@ static void aes_command_handler(void *cmd_body,
uint16_t key_len;
uint8_t iv_len;
uint8_t *iv;
+ uint8_t aad_len;
+ const uint8_t *aad;
enum aes_test_cipher_mode c_mode;
enum encrypt_mode e_mode;
uint8_t *cmd = (uint8_t *)cmd_body;
@@ -262,6 +264,8 @@ static void aes_command_handler(void *cmd_body,
* key | key len | key to use
* iv_len | 1 | either 0 or 16
* iv | 0 or 16 | as defined by iv_len
+ * aad_len | <= 127 | additional authentication data length
+ * aad | aad_len | additional authentication data
* text_len | 2 | size of the text to process, big endian
* text | text_len | text to encrypt/decrypt
*/
@@ -277,12 +281,16 @@ static void aes_command_handler(void *cmd_body,
cmd += key_len;
key_len *= 8;
iv_len = *cmd++;
- if (iv_len && (iv_len != 16)) {
+ if ((c_mode == TEST_MODE_GCM && iv_len == 0) ||
+ (c_mode != TEST_MODE_GCM && iv_len && iv_len != 16)) {
CPRINTF("Invalid vector len %d\n", iv_len);
return;
}
iv = cmd;
cmd += iv_len;
+ aad_len = *cmd++;
+ aad = cmd;
+ cmd += aad_len;
data_len = *cmd++;
data_len = data_len * 256 + *cmd++;
@@ -388,6 +396,80 @@ static void aes_command_handler(void *cmd_body,
}
break;
}
+ case TEST_MODE_GCM:
+ {
+ if (e_mode == 0) {
+ size_t total;
+ size_t count;
+ struct GCM_CTX ctx;
+
+ DCRYPTO_gcm_init(&ctx, key_local.b, iv_local.b, iv_len);
+ DCRYPTO_gcm_aad(&ctx, aad, aad_len);
+ count = DCRYPTO_gcm_decrypt(
+ &ctx, out_local.b, sizeof(out_local.b),
+ data_local.b, data_len);
+ if (count < 0) {
+ CPRINTF(
+ "%s: gcm decrypt failed\n", __func__);
+ break;
+ }
+ total = count;
+ count = DCRYPTO_gcm_decrypt_final(
+ &ctx, out_local.b + total,
+ sizeof(out_local.b) - total);
+ if (count < 0) {
+ CPRINTF(
+ "%s: gcm decrypt_final failed\n",
+ __func__);
+ break;
+ }
+ total += count;
+ count = DCRYPTO_gcm_tag(&ctx, out_local.b + total,
+ sizeof(out_local.b) - total);
+ if (count == 0) {
+ CPRINTF("%s: gcm tag failed\n", __func__);
+ break;
+ }
+ total += count;
+ *response_size = total;
+ } else if (e_mode == 1) {
+ size_t total;
+ size_t count;
+ struct GCM_CTX ctx;
+
+ DCRYPTO_gcm_init(&ctx, key_local.b, iv_local.b, iv_len);
+ DCRYPTO_gcm_aad(&ctx, aad, aad_len);
+ count = DCRYPTO_gcm_encrypt(
+ &ctx, out_local.b, sizeof(out_local.b),
+ data_local.b, data_len);
+ if (count < 0) {
+ CPRINTF(
+ "%s: gcm encrypt failed\n");
+ break;
+ }
+ total = count;
+ count = DCRYPTO_gcm_encrypt_final(
+ &ctx, out_local.b + total,
+ sizeof(out_local.b) - total);
+ if (count < 0) {
+ CPRINTF(
+ "%s: gcm encrypt_final failed\n",
+ __func__);
+ break;
+ }
+ total += count;
+ count = DCRYPTO_gcm_tag(
+ &ctx, out_local.b + total,
+ sizeof(out_local.b) - total);
+ if (count == 0) {
+ CPRINTF("%s: gcm tag failed\n", __func__);
+ break;
+ }
+ total += count;
+ *response_size = total;
+ }
+ break;
+ }
case TEST_MODE_OFB:
if (e_mode == 0) {
if (_cpri__AESDecryptOFB(
diff --git a/board/cr50/tpm2/ecc.c b/board/cr50/tpm2/ecc.c
index 9f61ab86a9..25ed169832 100644
--- a/board/cr50/tpm2/ecc.c
+++ b/board/cr50/tpm2/ecc.c
@@ -15,6 +15,7 @@
#include "cryptoc/p256.h"
#include "cryptoc/p256_ecdsa.h"
+#include "cryptoc/util.h"
static void reverse_tpm2b(TPM2B *b)
{
@@ -47,7 +48,7 @@ BOOL _cpri__EccIsPointOnCurve(TPM_ECC_CURVE curve_id, TPMS_ECC_POINT *q)
reverse_tpm2b(&q->x.b);
reverse_tpm2b(&q->y.b);
- result = p256_is_valid_point((p256_int *) q->x.b.buffer,
+ result = dcrypto_p256_is_valid_point((p256_int *) q->x.b.buffer,
(p256_int *) q->y.b.buffer);
reverse_tpm2b(&q->x.b);
@@ -171,6 +172,7 @@ CRYPT_RESULT _cpri__GenerateKeyEcc(
HASH_update(&hmac.hash, "ECC", 4);
memcpy(local_seed.t.buffer, DCRYPTO_HMAC_final(&hmac),
local_seed.t.size);
+ always_memset(&hmac, 0, sizeof(hmac));
/* TODO(ngm): CRBUG/P/55260: the personalize code uses only
* the first 4 bytes of extra.
*/
@@ -204,8 +206,9 @@ CRYPT_RESULT _cpri__GenerateKeyEcc(
break;
}
}
- /* TODO(ngm): implement secure memset. */
- memset(local_seed.t.buffer, 0, local_seed.t.size);
+
+ always_memset(local_seed.t.buffer, 0, local_seed.t.size);
+ always_memset(key_bytes, 0, sizeof(key_bytes));
if (count == 0)
FAIL(FATAL_ERROR_INTERNAL);
@@ -223,6 +226,8 @@ CRYPT_RESULT _cpri__SignEcc(
uint8_t digest_local[sizeof(p256_int)];
const size_t digest_len = MIN(digest->size, sizeof(digest_local));
p256_int p256_digest;
+ int result;
+ struct drbg_ctx drbg;
if (curve_id != TPM_ECC_NIST_P256)
return CRYPT_PARAMETER;
@@ -239,7 +244,9 @@ CRYPT_RESULT _cpri__SignEcc(
reverse_tpm2b(&d->b);
- p256_ecdsa_sign((p256_int *) d->b.buffer,
+ drbg_rand_init(&drbg);
+ result = dcrypto_p256_ecdsa_sign(&drbg,
+ (p256_int *) d->b.buffer,
&p256_digest,
(p256_int *) r->b.buffer,
(p256_int *) s->b.buffer);
@@ -250,7 +257,10 @@ CRYPT_RESULT _cpri__SignEcc(
reverse_tpm2b(&r->b);
reverse_tpm2b(&s->b);
- return CRYPT_SUCCESS;
+ if (result)
+ return CRYPT_SUCCESS;
+ else
+ return CRYPT_FAIL;
default:
return CRYPT_PARAMETER;
}
@@ -283,7 +293,7 @@ CRYPT_RESULT _cpri__ValidateSignatureEcc(
reverse_tpm2b(&r->b);
reverse_tpm2b(&s->b);
- result = p256_ecdsa_verify(
+ result = dcrypto_p256_ecdsa_verify(
(p256_int *) q->x.b.buffer,
(p256_int *) q->y.b.buffer,
&p256_digest,
@@ -308,6 +318,7 @@ CRYPT_RESULT _cpri__ValidateSignatureEcc(
CRYPT_RESULT _cpri__GetEphemeralEcc(TPMS_ECC_POINT *q, TPM2B_ECC_PARAMETER *d,
TPM_ECC_CURVE curve_id)
{
+ int result;
uint8_t key_bytes[P256_NBYTES] __aligned(4);
if (curve_id != TPM_ECC_NIST_P256)
@@ -315,10 +326,13 @@ CRYPT_RESULT _cpri__GetEphemeralEcc(TPMS_ECC_POINT *q, TPM2B_ECC_PARAMETER *d,
rand_bytes(key_bytes, sizeof(key_bytes));
- if (DCRYPTO_p256_key_from_bytes((p256_int *) q->x.b.buffer,
- (p256_int *) q->y.b.buffer,
- (p256_int *) d->b.buffer,
- key_bytes)) {
+ result = DCRYPTO_p256_key_from_bytes((p256_int *) q->x.b.buffer,
+ (p256_int *) q->y.b.buffer,
+ (p256_int *) d->b.buffer,
+ key_bytes);
+ always_memset(key_bytes, 0, sizeof(key_bytes));
+
+ if (result) {
q->x.b.size = sizeof(p256_int);
q->y.b.size = sizeof(p256_int);
reverse_tpm2b(&q->x.b);
@@ -394,11 +408,18 @@ static const struct TPM2B_ECC_PARAMETER_aligned NIST_P256_qy = {
static int point_equals(const TPMS_ECC_POINT *a, const TPMS_ECC_POINT *b)
{
- return a->x.b.size == b->x.b.size &&
- a->y.b.size == b->y.b.size &&
- memcmp(a->x.b.buffer, b->x.b.buffer, a->x.b.size) == 0 &&
- memcmp(a->y.b.buffer, b->y.b.buffer, a->y.b.size) == 0;
+ int diff = 0;
+
+ diff = a->x.b.size != b->x.b.size;
+ diff |= a->y.b.size != b->y.b.size;
+ if (!diff) {
+ diff |= !DCRYPTO_equals(
+ a->x.b.buffer, b->x.b.buffer, a->x.b.size);
+ diff |= !DCRYPTO_equals(
+ a->y.b.buffer, b->y.b.buffer, a->y.b.size);
+ }
+ return !diff;
}
static void ecc_command_handler(void *cmd_body, size_t cmd_size,
diff --git a/board/cr50/tpm2/endorsement.c b/board/cr50/tpm2/endorsement.c
index c3e818501c..23a9f3539a 100644
--- a/board/cr50/tpm2/endorsement.c
+++ b/board/cr50/tpm2/endorsement.c
@@ -30,6 +30,7 @@
#include "dcrypto.h"
#include <cryptoc/sha256.h>
+#include <cryptoc/util.h>
#include <endian.h>
#include <string.h>
@@ -39,7 +40,6 @@
#define EK_CERT_NV_START_INDEX 0x01C00000
#define INFO1_EPS_SIZE PRIMARY_SEED_SIZE
#define INFO1_EPS_OFFSET FLASH_INFO_MANUFACTURE_STATE_OFFSET
-#define AES256_BLOCK_CIPHER_KEY_SIZE 32
#define RO_CERTS_START_ADDR 0x43800
#define RO_CERTS_REGION_SIZE 0x0800
@@ -69,7 +69,186 @@ struct cros_perso_certificate_response_v0 {
BUILD_ASSERT(sizeof(struct cros_perso_response_component_info_v0) == 8);
BUILD_ASSERT(sizeof(struct cros_perso_certificate_response_v0) == 8);
-/* TODO(ngm): replace with real pub key. */
+/* This is a fixed seed (and corresponding certificates) for use in a
+ * developer environment. Use of this fixed seed will be triggered if
+ * the HMAC on the certificate region (i.e. read-only certificates
+ * written at manufacture) fails to verify.
+ *
+ * The HMAC verification failure itself only occurs in the event that
+ * RO & RW are signed in a mode that does correspond to the
+ * manufacture process, i.e. a PRODUCTION mode chip installed with DEV
+ * signed RO/RW (or vice-versa) or a PRODUCTION signed RO and DEV
+ * signed RW (or vice-versa).
+ *
+ * The fixed seed and its corresponding certificates are not trusted
+ * by production infrastructure, and are hence useful for development
+ * and testing.
+ */
+const uint8_t FIXED_ENDORSEMENT_SEED[PRIMARY_SEED_SIZE] = {
+ 0x1c, 0xb0, 0xde, 0x0e, 0x96, 0xe5, 0x58, 0xb0,
+ 0xad, 0x1d, 0x3a, 0x08, 0x22, 0x41, 0x7f, 0x45,
+ 0x37, 0xe7, 0x17, 0x42, 0x5d, 0x87, 0xc4, 0x77,
+ 0xf2, 0x97, 0xf8, 0xdd, 0xb9, 0xa0, 0xe5, 0x3a
+};
+
+const uint8_t FIXED_RSA_ENDORSEMENT_CERT[1007] = {
+ 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82, 0x02, 0xd3, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x57, 0xd7, 0x5a, 0xbc, 0x74, 0xa8, 0x2e, 0x11, 0x9c,
+ 0x73, 0x70, 0x2d, 0x3e, 0x15, 0xdf, 0x4e, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
+ 0x80, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c,
+ 0x0a, 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x31,
+ 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0b, 0x47, 0x6f,
+ 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x24, 0x30,
+ 0x22, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x1b, 0x45, 0x6e, 0x67, 0x69,
+ 0x6e, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x64, 0x20,
+ 0x44, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x6d, 0x65, 0x6e, 0x74, 0x31,
+ 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x17, 0x43, 0x52,
+ 0x4f, 0x53, 0x20, 0x54, 0x50, 0x4d, 0x20, 0x44, 0x45, 0x56, 0x20, 0x45,
+ 0x4b, 0x20, 0x52, 0x4f, 0x4f, 0x54, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x36, 0x31, 0x30, 0x32, 0x30, 0x30, 0x30, 0x34, 0x39, 0x33,
+ 0x36, 0x5a, 0x17, 0x0d, 0x32, 0x36, 0x31, 0x30, 0x31, 0x38, 0x30, 0x30,
+ 0x34, 0x39, 0x33, 0x36, 0x5a, 0x30, 0x00, 0x30, 0x82, 0x01, 0x22, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02,
+ 0x82, 0x01, 0x01, 0x00, 0xae, 0x3f, 0x7e, 0x66, 0x78, 0x26, 0x7a, 0x38,
+ 0x93, 0xaf, 0x9c, 0xe4, 0x2c, 0x3c, 0x9e, 0x11, 0xb7, 0xae, 0x2f, 0x71,
+ 0x8d, 0x4f, 0x2e, 0x3f, 0xd2, 0x35, 0x18, 0xb0, 0x27, 0x04, 0x4e, 0x04,
+ 0x66, 0xb2, 0x16, 0xd4, 0xa8, 0xfc, 0x51, 0x60, 0x1b, 0x05, 0x1c, 0x02,
+ 0xb5, 0x77, 0x1b, 0xf6, 0x40, 0xc4, 0x0e, 0x01, 0xbf, 0x70, 0xc1, 0x68,
+ 0x53, 0x8b, 0x20, 0x4c, 0xa3, 0x39, 0x09, 0xd4, 0x4e, 0x28, 0x7c, 0x1d,
+ 0xda, 0x57, 0x5c, 0x41, 0xae, 0x9b, 0xf3, 0xd5, 0xd3, 0x46, 0x12, 0x3d,
+ 0x43, 0xcc, 0x39, 0x29, 0x79, 0x9d, 0xe5, 0x87, 0x84, 0x22, 0x85, 0x4b,
+ 0x49, 0x35, 0x16, 0x4f, 0x3b, 0xdd, 0xd8, 0xaf, 0xe3, 0x99, 0xfa, 0x37,
+ 0xaf, 0xbd, 0xa9, 0x38, 0xb4, 0x47, 0x58, 0x1e, 0x71, 0xb2, 0x46, 0xf2,
+ 0x14, 0x85, 0x43, 0x12, 0x55, 0x8b, 0xc3, 0x5b, 0x78, 0x86, 0xd0, 0x0b,
+ 0x08, 0x87, 0x1d, 0xf7, 0x4c, 0x69, 0x47, 0x91, 0xd1, 0x16, 0x5c, 0x0e,
+ 0xf7, 0x0d, 0xad, 0x4a, 0x2d, 0xd8, 0x74, 0xe2, 0x89, 0xe1, 0xaf, 0xd7,
+ 0x54, 0xb6, 0xe0, 0x36, 0x76, 0x7b, 0xd4, 0x6d, 0x50, 0x64, 0x13, 0x5b,
+ 0x86, 0xa8, 0xa7, 0xee, 0xed, 0xf9, 0x50, 0x4d, 0xac, 0x1d, 0x1f, 0x9c,
+ 0x1b, 0x58, 0x19, 0xa5, 0x20, 0x19, 0x75, 0xb7, 0xcf, 0xf6, 0x37, 0x59,
+ 0x2a, 0xc7, 0x5b, 0x14, 0x51, 0xe6, 0x64, 0x70, 0xcc, 0x0e, 0x90, 0x9f,
+ 0xe8, 0xf3, 0xc5, 0x95, 0x41, 0x74, 0x24, 0xb4, 0x6d, 0x37, 0x4a, 0x90,
+ 0x17, 0x0e, 0x11, 0xea, 0xde, 0x74, 0x0e, 0x05, 0x4d, 0x1f, 0x9c, 0x11,
+ 0xea, 0x06, 0xbd, 0x90, 0x9a, 0x9f, 0x44, 0x55, 0x0f, 0x93, 0x82, 0x96,
+ 0xfc, 0x29, 0xb7, 0x26, 0x5e, 0x01, 0x25, 0x55, 0x4b, 0x80, 0xda, 0xd6,
+ 0x2d, 0xe0, 0xd9, 0x65, 0xcf, 0xcb, 0x7a, 0x2b, 0x02, 0x03, 0x01, 0x00,
+ 0x01, 0xa3, 0x81, 0xdf, 0x30, 0x81, 0xdc, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x00, 0x20, 0x30,
+ 0x51, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x01, 0x01, 0xff, 0x04, 0x47, 0x30,
+ 0x45, 0xa4, 0x43, 0x30, 0x41, 0x31, 0x16, 0x30, 0x14, 0x06, 0x05, 0x67,
+ 0x81, 0x05, 0x02, 0x01, 0x0c, 0x0b, 0x69, 0x64, 0x3a, 0x34, 0x37, 0x34,
+ 0x46, 0x34, 0x46, 0x34, 0x37, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x05, 0x67,
+ 0x81, 0x05, 0x02, 0x02, 0x0c, 0x04, 0x48, 0x31, 0x42, 0x32, 0x31, 0x16,
+ 0x30, 0x14, 0x06, 0x05, 0x67, 0x81, 0x05, 0x02, 0x03, 0x0c, 0x0b, 0x69,
+ 0x64, 0x3a, 0x30, 0x30, 0x31, 0x33, 0x30, 0x30, 0x33, 0x37, 0x30, 0x0c,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00,
+ 0x30, 0x13, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x0c, 0x30, 0x0a, 0x30,
+ 0x08, 0x06, 0x06, 0x67, 0x81, 0x0c, 0x01, 0x02, 0x02, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xd5, 0xfd,
+ 0x4b, 0xf1, 0xbe, 0x05, 0xfb, 0x13, 0x28, 0xe2, 0x5f, 0x39, 0xd3, 0x9d,
+ 0x70, 0x4a, 0x48, 0x91, 0x6b, 0xb0, 0x30, 0x10, 0x06, 0x03, 0x55, 0x1d,
+ 0x25, 0x04, 0x09, 0x30, 0x07, 0x06, 0x05, 0x67, 0x81, 0x05, 0x08, 0x01,
+ 0x30, 0x21, 0x06, 0x03, 0x55, 0x1d, 0x09, 0x04, 0x1a, 0x30, 0x18, 0x30,
+ 0x16, 0x06, 0x05, 0x67, 0x81, 0x05, 0x02, 0x10, 0x31, 0x0d, 0x30, 0x0b,
+ 0x0c, 0x03, 0x32, 0x2e, 0x30, 0x02, 0x01, 0x00, 0x02, 0x01, 0x10, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x4c, 0x65, 0x3f, 0x58, 0x73,
+ 0xb6, 0x21, 0x72, 0xb3, 0x2c, 0xc3, 0x94, 0xf4, 0xb3, 0xe0, 0x74, 0xa3,
+ 0x2e, 0x47, 0xa7, 0x63, 0x12, 0xa3, 0x0f, 0xc5, 0x18, 0x45, 0x06, 0xab,
+ 0xa9, 0xba, 0x64, 0xf0, 0xeb, 0x18, 0x7c, 0xba, 0x57, 0x09, 0xd0, 0x11,
+ 0x60, 0x6f, 0xbd, 0x52, 0x73, 0xab, 0x39, 0x81, 0x29, 0xab, 0x78, 0x84,
+ 0xec, 0x00, 0xe3, 0x87, 0xec, 0xf1, 0x7d, 0x2e, 0x15, 0x3f, 0xad, 0x1b,
+ 0x3a, 0x3f, 0x03, 0x53, 0x91, 0xee, 0x72, 0x7a, 0x87, 0x74, 0xa8, 0x09,
+ 0x7d, 0x83, 0x37, 0x0d, 0x46, 0x22, 0x12, 0xf3, 0x79, 0x61, 0xaf, 0x80,
+ 0xf3, 0xf4, 0x76, 0x7d, 0xbd, 0xb3, 0x1f, 0x87, 0xb8, 0x66, 0xc9, 0x24,
+ 0x15, 0xe9, 0xc7, 0x5b, 0x19, 0xdf, 0x04, 0x0a, 0x47, 0xec, 0x88, 0x46,
+ 0x7f, 0x20, 0x6c, 0x4b, 0x23, 0xdb, 0x65, 0x67, 0x54, 0xde, 0x3a, 0xc3,
+ 0x64, 0xbb, 0x77, 0x4d, 0x6d, 0x4b, 0x1e, 0x43, 0x9a, 0x35, 0x20, 0x7e,
+ 0x28, 0xce, 0x4e, 0xe5, 0xb7, 0x0b, 0xae, 0xd0, 0x26, 0xc0, 0xac, 0x2f,
+ 0x79, 0x35, 0x71, 0xbd, 0x74, 0x68, 0x8d, 0x51, 0x6f, 0x84, 0x4d, 0xaa,
+ 0xca, 0x0d, 0xf0, 0xa8, 0x41, 0x5c, 0xa9, 0x6e, 0x3b, 0x70, 0x15, 0x73,
+ 0x8d, 0xf0, 0x70, 0xd3, 0xb3, 0x0e, 0xa7, 0x3a, 0x34, 0x12, 0xd2, 0x1e,
+ 0xa4, 0x18, 0x4c, 0x31, 0xee, 0x26, 0x44, 0x24, 0xe0, 0xa5, 0xca, 0x56,
+ 0x5d, 0x76, 0x9e, 0xf4, 0x9a, 0x6e, 0x2b, 0xd6, 0x4a, 0xe9, 0x47, 0xd9,
+ 0x29, 0x94, 0x2d, 0x23, 0xf7, 0xbb, 0x13, 0x0c, 0x48, 0x73, 0x93, 0xe3,
+ 0x49, 0xc7, 0xd8, 0xca, 0x5d, 0x63, 0xf5, 0x68, 0xb2, 0xe9, 0x1a, 0xe6,
+ 0x87, 0x39, 0xf8, 0x12, 0xa7, 0x5c, 0xb2, 0x6e, 0x04, 0xd0, 0x73, 0x3a,
+ 0x05, 0x77, 0xc0, 0x9f, 0x23, 0xa7, 0x1a, 0x71, 0x38, 0x55, 0x70
+};
+
+const uint8_t FIXED_ECC_ENDORSEMENT_CERT[804] = {
+ 0x30, 0x82, 0x03, 0x20, 0x30, 0x82, 0x02, 0x08, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x67, 0x02, 0x3f, 0x35, 0xc3, 0x17, 0xad, 0xcf, 0x0a,
+ 0x76, 0xed, 0x50, 0x17, 0xd8, 0x4e, 0x50, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
+ 0x80, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c,
+ 0x0a, 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x31,
+ 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0b, 0x47, 0x6f,
+ 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x24, 0x30,
+ 0x22, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x1b, 0x45, 0x6e, 0x67, 0x69,
+ 0x6e, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x64, 0x20,
+ 0x44, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x6d, 0x65, 0x6e, 0x74, 0x31,
+ 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x17, 0x43, 0x52,
+ 0x4f, 0x53, 0x20, 0x54, 0x50, 0x4d, 0x20, 0x44, 0x45, 0x56, 0x20, 0x45,
+ 0x4b, 0x20, 0x52, 0x4f, 0x4f, 0x54, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x36, 0x31, 0x30, 0x32, 0x30, 0x30, 0x30, 0x34, 0x39, 0x33,
+ 0x36, 0x5a, 0x17, 0x0d, 0x32, 0x36, 0x31, 0x30, 0x31, 0x38, 0x30, 0x30,
+ 0x34, 0x39, 0x33, 0x36, 0x5a, 0x30, 0x00, 0x30, 0x59, 0x30, 0x13, 0x06,
+ 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86,
+ 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x6e, 0xcc,
+ 0xf0, 0x96, 0x69, 0x9b, 0x3f, 0xea, 0x95, 0xb7, 0xd5, 0x00, 0x27, 0x20,
+ 0x81, 0x8e, 0x57, 0x00, 0x6f, 0x67, 0x98, 0xce, 0x8e, 0xdf, 0xc7, 0xda,
+ 0xae, 0xa8, 0xa3, 0xed, 0x3e, 0x7a, 0xb3, 0x27, 0xbf, 0x92, 0xee, 0xb2,
+ 0xa2, 0x76, 0x81, 0xc1, 0x71, 0x4d, 0x8c, 0xa8, 0x9d, 0xfd, 0x8e, 0xd0,
+ 0x29, 0xb5, 0x01, 0x20, 0xec, 0x78, 0xc0, 0x17, 0x8f, 0xf6, 0xf8, 0x67,
+ 0x5f, 0xe8, 0xa3, 0x81, 0xdf, 0x30, 0x81, 0xdc, 0x30, 0x0e, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x00, 0x20,
+ 0x30, 0x51, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x01, 0x01, 0xff, 0x04, 0x47,
+ 0x30, 0x45, 0xa4, 0x43, 0x30, 0x41, 0x31, 0x16, 0x30, 0x14, 0x06, 0x05,
+ 0x67, 0x81, 0x05, 0x02, 0x01, 0x0c, 0x0b, 0x69, 0x64, 0x3a, 0x34, 0x37,
+ 0x34, 0x46, 0x34, 0x46, 0x34, 0x37, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x05,
+ 0x67, 0x81, 0x05, 0x02, 0x02, 0x0c, 0x04, 0x48, 0x31, 0x42, 0x32, 0x31,
+ 0x16, 0x30, 0x14, 0x06, 0x05, 0x67, 0x81, 0x05, 0x02, 0x03, 0x0c, 0x0b,
+ 0x69, 0x64, 0x3a, 0x30, 0x30, 0x31, 0x33, 0x30, 0x30, 0x33, 0x37, 0x30,
+ 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30,
+ 0x00, 0x30, 0x13, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x0c, 0x30, 0x0a,
+ 0x30, 0x08, 0x06, 0x06, 0x67, 0x81, 0x0c, 0x01, 0x02, 0x02, 0x30, 0x1f,
+ 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xd5,
+ 0xfd, 0x4b, 0xf1, 0xbe, 0x05, 0xfb, 0x13, 0x28, 0xe2, 0x5f, 0x39, 0xd3,
+ 0x9d, 0x70, 0x4a, 0x48, 0x91, 0x6b, 0xb0, 0x30, 0x10, 0x06, 0x03, 0x55,
+ 0x1d, 0x25, 0x04, 0x09, 0x30, 0x07, 0x06, 0x05, 0x67, 0x81, 0x05, 0x08,
+ 0x01, 0x30, 0x21, 0x06, 0x03, 0x55, 0x1d, 0x09, 0x04, 0x1a, 0x30, 0x18,
+ 0x30, 0x16, 0x06, 0x05, 0x67, 0x81, 0x05, 0x02, 0x10, 0x31, 0x0d, 0x30,
+ 0x0b, 0x0c, 0x03, 0x32, 0x2e, 0x30, 0x02, 0x01, 0x00, 0x02, 0x01, 0x10,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x21, 0xab, 0x9e, 0x92,
+ 0x4d, 0xb0, 0x50, 0x04, 0xeb, 0x2b, 0xb6, 0xcc, 0x87, 0x8c, 0xa8, 0x27,
+ 0xe3, 0x5a, 0xbf, 0x03, 0x5d, 0xb1, 0x4d, 0x24, 0xda, 0xdf, 0x44, 0xdb,
+ 0x4a, 0x37, 0x5c, 0x3e, 0x70, 0xf3, 0x35, 0x5d, 0x26, 0x2e, 0xaa, 0x85,
+ 0xc6, 0xbe, 0x1c, 0x9d, 0x1e, 0x5f, 0xf6, 0x6c, 0xb8, 0x94, 0x41, 0x25,
+ 0x20, 0x55, 0x28, 0x53, 0x55, 0x67, 0x9a, 0xb5, 0xfb, 0x6b, 0x57, 0x09,
+ 0xf0, 0x5b, 0xe2, 0x66, 0xc5, 0xe8, 0xd1, 0x9e, 0xb8, 0xb7, 0xed, 0xd8,
+ 0x41, 0xb5, 0xbd, 0x44, 0xd9, 0x53, 0xab, 0x2d, 0x17, 0x4c, 0x73, 0x05,
+ 0x19, 0x2c, 0x9d, 0x18, 0x98, 0xd8, 0x55, 0xbe, 0xbd, 0xb6, 0xa5, 0xf6,
+ 0x5f, 0x3d, 0x70, 0x98, 0xd6, 0xd0, 0xcf, 0x1c, 0x0d, 0xc6, 0x78, 0x6d,
+ 0x2e, 0x9c, 0x44, 0xf6, 0x9e, 0x0a, 0x80, 0x12, 0xcd, 0x9b, 0x4b, 0x1f,
+ 0xbc, 0xfe, 0xe7, 0x3f, 0x45, 0x81, 0x78, 0x43, 0x40, 0xf2, 0xb0, 0x6b,
+ 0x2c, 0x23, 0xc8, 0xc8, 0x57, 0xc6, 0x33, 0x08, 0x3e, 0x17, 0x43, 0x16,
+ 0xf0, 0x3f, 0xbf, 0x24, 0x54, 0xba, 0xe6, 0x85, 0x4c, 0xc8, 0x2e, 0x7f,
+ 0x88, 0x41, 0x6c, 0x4e, 0x03, 0xa6, 0x35, 0x00, 0x4d, 0xdb, 0x65, 0x68,
+ 0x78, 0x01, 0x40, 0xc6, 0xa0, 0x95, 0xd9, 0xe9, 0x27, 0xe1, 0x90, 0x20,
+ 0xc8, 0xe6, 0xa7, 0x7c, 0x4d, 0x9c, 0x1c, 0x44, 0x47, 0xfe, 0x9e, 0xc9,
+ 0x25, 0x7a, 0x07, 0xa9, 0x86, 0x60, 0x58, 0x18, 0x1c, 0x16, 0x18, 0x7e,
+ 0x04, 0xd6, 0x5a, 0xb6, 0xcb, 0xb6, 0xa6, 0x0f, 0xd9, 0x42, 0xf3, 0x19,
+ 0x8c, 0xbe, 0x26, 0x98, 0xdd, 0x07, 0x05, 0x76, 0xc0, 0xf9, 0xa4, 0xeb,
+ 0x53, 0xff, 0x13, 0x27, 0x61, 0x87, 0x66, 0x99, 0x76, 0x9c, 0x5f, 0x03,
+ 0x52, 0x95, 0x13, 0x6e, 0xb7, 0x33, 0x1f, 0x8d, 0xc6, 0x22, 0xd8, 0xe4
+};
+
+/* Test endorsement CA root. */
static const uint32_t TEST_ENDORSEMENT_CA_RSA_N[64] = {
0xfa3b34ed, 0x3c59ad05, 0x912d6623, 0x83302402,
0xd43b6755, 0x5777021a, 0xaf37e9a1, 0x45c0e8ad,
@@ -89,6 +268,26 @@ static const uint32_t TEST_ENDORSEMENT_CA_RSA_N[64] = {
0x486fb315, 0xa1098c31, 0x5dc50dd6, 0xcdc10874
};
+/* Production endorsement CA root. */
+static const uint32_t PROD_ENDORSEMENT_CA_RSA_N[64] = {
+ 0xeb6a07bf, 0x6cf8eca6, 0x4756e85e, 0x2fc3874c,
+ 0xa4c23e87, 0xc364dffe, 0x2a2ddb95, 0x2f7f0e1e,
+ 0xdb485bd8, 0xce8aa808, 0xe062001b, 0x187811c3,
+ 0x0e400462, 0xb7097a01, 0xb988152b, 0xba9d058a,
+ 0x814b6691, 0xc70a694f, 0x8108c7f0, 0x4c7a1f33,
+ 0x5cfda48e, 0xef303dbc, 0x84f5a3ea, 0x14607435,
+ 0xc72f1e60, 0x345d0b38, 0x0ac16927, 0xbdf903c7,
+ 0x11b660ed, 0x21ebfe0e, 0x8c8b303c, 0xd6eff6cb,
+ 0x76156bf7, 0x57735ce4, 0x8b7a87ed, 0x7a757188,
+ 0xd4fb3eb0, 0xc67fa05d, 0x163f0cf5, 0x69d8abf3,
+ 0xec105749, 0x1de78f37, 0xb885a62f, 0x81344a82,
+ 0x390df2b7, 0x58a7c56a, 0xa938f471, 0x506ee7d4,
+ 0x2ca0f2a3, 0x2aa5392c, 0x39052797, 0x199e837c,
+ 0x0d367b81, 0xb7bbff6f, 0x0ea99f5f, 0xfbac0d2a,
+ 0x7bbe018d, 0x265fc995, 0x34f73008, 0x5e2cd747,
+ 0x42096e33, 0x0c15f816, 0xffa7f7d2, 0xbd6f0198
+};
+
static const struct RSA TEST_ENDORSEMENT_CA_RSA_PUB = {
.e = RSA_F4,
.N = {
@@ -101,6 +300,18 @@ static const struct RSA TEST_ENDORSEMENT_CA_RSA_PUB = {
},
};
+static const struct RSA PROD_ENDORSEMENT_CA_RSA_PUB = {
+ .e = RSA_F4,
+ .N = {
+ .dmax = sizeof(PROD_ENDORSEMENT_CA_RSA_N) / sizeof(uint32_t),
+ .d = (struct access_helper *) PROD_ENDORSEMENT_CA_RSA_N,
+ },
+ .d = {
+ .dmax = 0,
+ .d = NULL,
+ },
+};
+
static int validate_cert(
const struct cros_perso_response_component_info_v0 *cert_info,
const struct cros_perso_certificate_response_v0 *cert,
@@ -115,13 +326,20 @@ static int validate_cert(
if (cert->cert_len > MAX_NV_BUFFER_SIZE)
return 0;
- /* Verify certificate signature. */
+ /* Verify certificate signature; accept either root CA.
+ * Getting here implies that the previous mac check on the
+ * endorsement seed passed, and that one of these two CA
+ * certificates serve as roots for the installed endorsement
+ * certificate.
+ */
return DCRYPTO_x509_verify(cert->cert, cert->cert_len,
+ &PROD_ENDORSEMENT_CA_RSA_PUB) ||
+ DCRYPTO_x509_verify(cert->cert, cert->cert_len,
&TEST_ENDORSEMENT_CA_RSA_PUB);
}
static int store_cert(enum cros_perso_component_type component_type,
- const struct cros_perso_certificate_response_v0 *cert)
+ const uint8_t *cert, size_t cert_len)
{
const uint32_t rsa_ek_nv_index = EK_CERT_NV_START_INDEX;
const uint32_t ecc_ek_nv_index = EK_CERT_NV_START_INDEX + 1;
@@ -167,7 +385,7 @@ static int store_cert(enum cros_perso_component_type component_type,
define_space.publicInfo.t.nvPublic.nameAlg = TPM_ALG_SHA256;
define_space.publicInfo.t.nvPublic.attributes = space_attributes;
define_space.publicInfo.t.nvPublic.authPolicy.t.size = 0;
- define_space.publicInfo.t.nvPublic.dataSize = cert->cert_len;
+ define_space.publicInfo.t.nvPublic.dataSize = cert_len;
/* Define the required space first. */
if (TPM2_NV_DefineSpace(&define_space) != TPM_RC_SUCCESS)
@@ -179,8 +397,8 @@ static int store_cert(enum cros_perso_component_type component_type,
in.nvIndex = nv_index;
in.authHandle = TPM_RH_PLATFORM;
- in.data.t.size = cert->cert_len;
- memcpy(in.data.t.buffer, cert->cert, cert->cert_len);
+ in.data.t.size = cert_len;
+ memcpy(in.data.t.buffer, cert, cert_len);
in.offset = 0;
if (TPM2_NV_Write(&in) != TPM_RC_SUCCESS)
@@ -190,105 +408,6 @@ static int store_cert(enum cros_perso_component_type component_type,
return 0;
}
-static uint32_t hw_key_ladder_step(uint32_t cert)
-{
- uint32_t itop;
-
- GREG32(KEYMGR, SHA_ITOP) = 0; /* clear status */
-
- GREG32(KEYMGR, SHA_USE_CERT_INDEX) =
- (cert << GC_KEYMGR_SHA_USE_CERT_INDEX_LSB) |
- GC_KEYMGR_SHA_USE_CERT_ENABLE_MASK;
-
- GREG32(KEYMGR, SHA_CFG_EN) =
- GC_KEYMGR_SHA_CFG_EN_INT_EN_DONE_MASK;
- GREG32(KEYMGR, SHA_TRIG) =
- GC_KEYMGR_SHA_TRIG_TRIG_GO_MASK;
-
- do {
- itop = GREG32(KEYMGR, SHA_ITOP);
- } while (!itop);
-
- GREG32(KEYMGR, SHA_ITOP) = 0; /* clear status */
-
- return !!GREG32(KEYMGR, HKEY_ERR_FLAGS);
-}
-
-
-#define KEYMGR_CERT_0 0
-#define KEYMGR_CERT_3 3
-#define KEYMGR_CERT_4 4
-#define KEYMGR_CERT_5 5
-#define KEYMGR_CERT_7 7
-#define KEYMGR_CERT_15 15
-#define KEYMGR_CERT_20 20
-#define KEYMGR_CERT_25 25
-#define KEYMGR_CERT_26 26
-
-#define K_CROS_FW_MAJOR_VERSION 0
-static const uint8_t k_cr50_max_fw_major_version = 254;
-
-static int compute_frk2(uint8_t frk2[AES256_BLOCK_CIPHER_KEY_SIZE])
-{
- int i;
-
- /* TODO(ngm): reading ITOP in hw_key_ladder_step hangs on
- * second run of this function (i.e. install of ECC cert,
- * which re-generates FRK2) unless the SHA engine is reset.
- */
- GREG32(KEYMGR, SHA_TRIG) =
- GC_KEYMGR_SHA_TRIG_TRIG_RESET_MASK;
-
- if (hw_key_ladder_step(KEYMGR_CERT_0))
- return 0;
-
- /* Derive HC_PHIK --> Deposited into ISR0 */
- if (hw_key_ladder_step(KEYMGR_CERT_3))
- return 0;
-
- /* Cryptographically mix OBS-FBS --> Deposited into ISR1 */
- if (hw_key_ladder_step(KEYMGR_CERT_4))
- return 0;
-
- /* Derive HIK_RT --> Deposited into ISR0 */
- if (hw_key_ladder_step(KEYMGR_CERT_5))
- return 0;
-
- /* Derive BL_HIK --> Deposited into ISR0 */
- if (hw_key_ladder_step(KEYMGR_CERT_7))
- return 0;
-
- /* Generate FRK2 by executing certs 15, 20, 25, and 26 */
- if (hw_key_ladder_step(KEYMGR_CERT_15))
- return 0;
-
- if (hw_key_ladder_step(KEYMGR_CERT_20))
- return 0;
-
- for (i = 0; i < k_cr50_max_fw_major_version -
- K_CROS_FW_MAJOR_VERSION; i++) {
- if (hw_key_ladder_step(KEYMGR_CERT_25))
- return 0;
- }
- if (hw_key_ladder_step(KEYMGR_CERT_26))
- return 0;
- memcpy(frk2, (void *) GREG32_ADDR(KEYMGR, HKEY_FRR0),
- AES256_BLOCK_CIPHER_KEY_SIZE);
- return 1;
-}
-
-static void flash_info_read_enable(void)
-{
- /* Enable R access to INFO. */
- GREG32(GLOBALSEC, FLASH_REGION7_BASE_ADDR) = FLASH_INFO_MEMORY_BASE +
- FLASH_INFO_MANUFACTURE_STATE_OFFSET;
- GREG32(GLOBALSEC, FLASH_REGION7_SIZE) =
- FLASH_INFO_MANUFACTURE_STATE_SIZE - 1;
- GREG32(GLOBALSEC, FLASH_REGION7_CTRL) =
- GC_GLOBALSEC_FLASH_REGION7_CTRL_EN_MASK |
- GC_GLOBALSEC_FLASH_REGION7_CTRL_RD_EN_MASK;
-}
-
static void flash_info_read_disable(void)
{
GREG32(GLOBALSEC, FLASH_REGION7_CTRL) = 0;
@@ -305,6 +424,8 @@ static void flash_cert_region_enable(void)
GC_GLOBALSEC_FLASH_REGION6_CTRL_RD_EN_MASK;
}
+#define K_CROS_FW_MAJOR_VERSION 0
+
/* EPS is stored XOR'd with FRK2, so make sure that the sizes match. */
BUILD_ASSERT(AES256_BLOCK_CIPHER_KEY_SIZE == PRIMARY_SEED_SIZE);
static int get_decrypted_eps(uint8_t eps[PRIMARY_SEED_SIZE])
@@ -313,18 +434,19 @@ static int get_decrypted_eps(uint8_t eps[PRIMARY_SEED_SIZE])
uint8_t frk2[AES256_BLOCK_CIPHER_KEY_SIZE];
CPRINTF("%s: getting eps\n", __func__);
- if (!compute_frk2(frk2))
+ if (!DCRYPTO_ladder_compute_frk2(K_CROS_FW_MAJOR_VERSION, frk2))
return 0;
/* Setup flash region mapping. */
- flash_info_read_enable();
+ flash_info_read_enable(FLASH_INFO_MANUFACTURE_STATE_OFFSET,
+ FLASH_INFO_MANUFACTURE_STATE_SIZE);
for (i = 0; i < INFO1_EPS_SIZE; i += sizeof(uint32_t)) {
uint32_t word;
if (flash_physical_info_read_word(
INFO1_EPS_OFFSET + i, &word) != EC_SUCCESS) {
- memset(frk2, 0, sizeof(frk2));
+ always_memset(frk2, 0, sizeof(frk2));
return 0; /* Flash read INFO1 failed. */
}
memcpy(eps + i, &word, sizeof(word));
@@ -337,7 +459,7 @@ static int get_decrypted_eps(uint8_t eps[PRIMARY_SEED_SIZE])
for (i = 0; i < PRIMARY_SEED_SIZE; i++)
eps[i] ^= frk2[i];
- memset(frk2, 0, sizeof(frk2));
+ always_memset(frk2, 0, sizeof(frk2));
return 1;
}
@@ -356,6 +478,24 @@ static void endorsement_complete(void)
CPRINTF("%s(): SUCCESS\n", __func__);
}
+static int install_fixed_certs(void)
+{
+ if (!store_eps(FIXED_ENDORSEMENT_SEED))
+ return 0;
+
+ if (!store_cert(CROS_PERSO_COMPONENT_TYPE_RSA_CERT,
+ FIXED_RSA_ENDORSEMENT_CERT,
+ sizeof(FIXED_RSA_ENDORSEMENT_CERT)))
+ return 0;
+
+ if (!store_cert(CROS_PERSO_COMPONENT_TYPE_P256_CERT,
+ FIXED_ECC_ENDORSEMENT_CERT,
+ sizeof(FIXED_ECC_ENDORSEMENT_CERT)))
+ return 0;
+
+ return 1;
+}
+
static int handle_cert(
const struct cros_perso_response_component_info_v0 *cert_info,
const struct cros_perso_certificate_response_v0 *cert,
@@ -367,7 +507,8 @@ static int handle_cert(
return 0;
/* TODO(ngm): verify that storage succeeded. */
- if (!store_cert(cert_info->component_type, cert)) {
+ if (!store_cert(cert_info->component_type, cert->cert,
+ cert->cert_len)) {
CPRINTF("%s(): cert storage failed, type: %d\n", __func__,
cert_info->component_type);
return 0; /* Internal failure. */
@@ -376,7 +517,7 @@ static int handle_cert(
return 1;
}
-int tpm_endorse(void)
+enum manufacturing_status tpm_endorse(void)
{
struct ro_cert_response {
uint8_t key_id[4];
@@ -392,23 +533,28 @@ int tpm_endorse(void)
/* 2-kB RO cert region is setup like so:
*
* | struct ro_cert | rsa_cert | struct ro_cert | ecc_cert |
+ *
+ * last 32 bytes is hmac over (2048 - 32) preceding bytes.
+ * using hmac(eps, "RSA", 4) as key
*/
const uint8_t *p = (const uint8_t *) RO_CERTS_START_ADDR;
const uint32_t *c = (const uint32_t *) RO_CERTS_START_ADDR;
const struct ro_cert *rsa_cert;
const struct ro_cert *ecc_cert;
- int result = 0;
+ enum manufacturing_status result;
uint8_t eps[PRIMARY_SEED_SIZE];
+ LITE_HMAC_CTX hmac;
+
flash_cert_region_enable();
/* First boot, certs not yet installed. */
if (*c == 0xFFFFFFFF)
- return 0;
+ return mnf_no_certs;
if (!get_decrypted_eps(eps)) {
CPRINTF("%s(): failed to read eps\n", __func__);
- return 0;
+ return mnf_eps_decr;
}
/* Unpack rsa cert struct. */
@@ -416,7 +562,7 @@ int tpm_endorse(void)
/* Sanity check cert region contents. */
if ((2 * sizeof(struct ro_cert)) +
rsa_cert->cert_response.cert_len > RO_CERTS_REGION_SIZE)
- return 0;
+ return mnf_bad_rsa_size;
/* Unpack ecc cert struct. */
ecc_cert = (const struct ro_cert *) (p + sizeof(struct ro_cert) +
@@ -425,24 +571,62 @@ int tpm_endorse(void)
if ((2 * sizeof(struct ro_cert)) +
rsa_cert->cert_response.cert_len +
ecc_cert->cert_response.cert_len > RO_CERTS_REGION_SIZE)
- return 0;
+ return mnf_bad_total_size;
/* Verify expected component types. */
if (rsa_cert->cert_info.component_type !=
CROS_PERSO_COMPONENT_TYPE_RSA_CERT) {
- return 0;
+ return mnf_bad_rsa_type;
}
if (ecc_cert->cert_info.component_type !=
CROS_PERSO_COMPONENT_TYPE_P256_CERT) {
- return 0;
+ return mnf_bad_ecc_type;
}
do {
+ /* Check cert region hmac.
+ *
+ * This will fail if we are not running w/ expected keyladder.
+ */
+ DCRYPTO_HMAC_SHA256_init(&hmac, eps, sizeof(eps));
+ HASH_update(&hmac.hash, "RSA", 4);
+ DCRYPTO_HMAC_SHA256_init(&hmac, DCRYPTO_HMAC_final(&hmac), 32);
+ HASH_update(&hmac.hash, p, RO_CERTS_REGION_SIZE - 32);
+ if (!DCRYPTO_equals(p + RO_CERTS_REGION_SIZE - 32,
+ DCRYPTO_HMAC_final(&hmac), 32)) {
+ CPRINTF("%s: bad cert region hmac; falling back\n"
+ " to fixed endorsement\n", __func__);
+
+ /* HMAC verification failure indicates either
+ * a manufacture fault, or mis-match in
+ * production mode and currently running
+ * firmware (e.g. PRODUCTION mode chip, now
+ * flashed with DEV mode firmware.
+ *
+ * In either case, fall back to a fixed
+ * endorsement seed, which will not be trusted
+ * by production infrastructure.
+ */
+ if (!install_fixed_certs()) {
+ CPRINTF("%s: failed to install fixed "
+ "endorsement certs; \n"
+ " unknown endorsement state\n",
+ __func__);
+ }
+
+ /* TODO(ngm): is this state considered
+ * endorsement failure?
+ */
+ result = mnf_hmac_mismatch;
+ break;
+ }
+
if (!handle_cert(
&rsa_cert->cert_info,
(struct cros_perso_certificate_response_v0 *)
&rsa_cert->cert_response, eps)) {
CPRINTF("%s: Failed to process RSA cert\n", __func__);
+ result = mnf_rsa_proc;
break;
}
CPRINTF("%s: RSA cert install success\n", __func__);
@@ -452,6 +636,7 @@ int tpm_endorse(void)
(struct cros_perso_certificate_response_v0 *)
&ecc_cert->cert_response, eps)) {
CPRINTF("%s: Failed to process ECC cert\n", __func__);
+ result = mnf_ecc_proc;
break;
}
CPRINTF("%s: ECC cert install success\n", __func__);
@@ -459,6 +644,7 @@ int tpm_endorse(void)
/* Copy EPS from INFO1 to flash data region. */
if (!store_eps(eps)) {
CPRINTF("%s(): eps storage failed\n", __func__);
+ result = mnf_store;
break;
}
@@ -466,9 +652,9 @@ int tpm_endorse(void)
endorsement_complete();
/* Chip has been marked as manufactured. */
- result = 1;
+ result = mnf_success;
} while (0);
- memset(eps, 0, sizeof(eps));
+ always_memset(eps, 0, sizeof(eps));
return result;
}
diff --git a/board/cr50/tpm2/hash.c b/board/cr50/tpm2/hash.c
index a11fb9e450..157100fd96 100644
--- a/board/cr50/tpm2/hash.c
+++ b/board/cr50/tpm2/hash.c
@@ -71,14 +71,12 @@ uint16_t _cpri__HashBlock(TPM_ALG_ID alg, uint32_t in_len, uint8_t *in,
case TPM_ALG_SHA256:
DCRYPTO_SHA256_hash(in, in_len, digest);
break;
-/* TODO: add support for SHA384 and SHA512
- *
- * case TPM_ALG_SHA384:
- * DCRYPTO_SHA384_hash(in, in_len, p);
- * break;
- * case TPM_ALG_SHA512:
- * DCRYPTO_SHA512_hash(in, in_len, p);
- * break; */
+ case TPM_ALG_SHA384:
+ DCRYPTO_SHA384_hash(in, in_len, digest);
+ break;
+ case TPM_ALG_SHA512:
+ DCRYPTO_SHA512_hash(in, in_len, digest);
+ break;
default:
FAIL(FATAL_ERROR_INTERNAL);
break;
@@ -97,22 +95,30 @@ uint16_t _cpri__StartHash(TPM_ALG_ID alg, BOOL sequence,
struct HASH_CTX *ctx = (struct HASH_CTX *) state->state;
uint16_t result;
+ /* NOTE: as per bug http://crosbug.com/p/55331#26 (NVMEM
+ * encryption), always use the software hash implementation
+ * for TPM related calculations, since we have no guarantee
+ * that the key-ladder will not be used between SHA_init() and
+ * final().
+ */
switch (alg) {
case TPM_ALG_SHA1:
- DCRYPTO_SHA1_init(ctx, sequence);
+ DCRYPTO_SHA1_init(ctx, 1);
result = HASH_size(ctx);
break;
case TPM_ALG_SHA256:
- DCRYPTO_SHA256_init(ctx, sequence);
+ DCRYPTO_SHA256_init(ctx, 1);
+ result = HASH_size(ctx);
+ break;
+
+ case TPM_ALG_SHA384:
+ DCRYPTO_SHA384_init(ctx);
+ result = HASH_size(ctx);
+ break;
+ case TPM_ALG_SHA512:
+ DCRYPTO_SHA512_init(ctx);
result = HASH_size(ctx);
break;
-/* TODO: add support for SHA384 and SHA512
- * case TPM_ALG_SHA384:
- * DCRYPTO_SHA384_init(in, in_len, p);
- * break;
- * case TPM_ALG_SHA512:
- * DCRYPTO_SHA512_init(in, in_len, p);
- * break; */
default:
result = 0;
break;
@@ -184,17 +190,28 @@ static void process_start(TPM_ALG_ID alg, int handle, void *response_body,
}
if (!hash_test_db.max_contexts) {
+ size_t buffer_size;
+
/* Check how many contexts could possible fit. */
hash_test_db.max_contexts = shared_mem_size() /
sizeof(struct test_context);
- }
- if (!hash_test_db.contexts)
- shared_mem_acquire(shared_mem_size(),
- (char **)&hash_test_db.contexts);
+ buffer_size = sizeof(struct test_context) *
+ hash_test_db.max_contexts;
+
+ if (shared_mem_acquire(buffer_size,
+ (char **)&hash_test_db.contexts) !=
+ EC_SUCCESS) {
+ /* Must be out of memory. */
+ hash_test_db.max_contexts = 0;
+ *response = EXC_HASH_TOO_MANY_HANDLES;
+ *response_size = 1;
+ return;
+ }
+ memset(hash_test_db.contexts, 0, buffer_size);
+ }
- if (!hash_test_db.contexts ||
- (hash_test_db.current_context_count == hash_test_db.max_contexts)) {
+ if (hash_test_db.current_context_count == hash_test_db.max_contexts) {
*response = EXC_HASH_TOO_MANY_HANDLES;
*response_size = 1;
return;
@@ -240,6 +257,7 @@ static void process_finish(int handle, void *response_body,
hash_test_db.current_context_count--;
if (!hash_test_db.current_context_count) {
shared_mem_release(hash_test_db.contexts);
+ hash_test_db.max_contexts = 0;
return;
}
diff --git a/board/cr50/tpm2/platform.c b/board/cr50/tpm2/platform.c
index 20b5854625..e380d8afe1 100644
--- a/board/cr50/tpm2/platform.c
+++ b/board/cr50/tpm2/platform.c
@@ -6,7 +6,10 @@
#include "Platform.h"
#include "TPM_Types.h"
+#include "ccd_config.h"
#include "trng.h"
+#include "util.h"
+#include "version.h"
uint16_t _cpri__GenerateRandom(size_t random_size,
uint8_t *buffer)
@@ -14,3 +17,52 @@ uint16_t _cpri__GenerateRandom(size_t random_size,
rand_bytes(buffer, random_size);
return random_size;
}
+
+/*
+ * Return the pointer to the character immediately after the first dash
+ * encountered in the passed in string, or NULL if there is no dashes in the
+ * string.
+ */
+static const char *char_after_dash(const char *str)
+{
+ char c;
+
+ do {
+ c = *str++;
+
+ if (c == '-')
+ return str;
+ } while (c);
+
+ return NULL;
+}
+
+/*
+ * The properly formatted build_info string has the ec code SHA1 after the
+ * first dash, and tpm2 code sha1 after the second dash.
+ */
+
+void _plat__GetFwVersion(uint32_t *firmwareV1, uint32_t *firmwareV2)
+{
+ const char *ver_str = char_after_dash(build_info);
+
+ /* Just in case the build_info string is misformatted. */
+ *firmwareV1 = 0;
+ *firmwareV2 = 0;
+
+ if (!ver_str)
+ return;
+
+ *firmwareV1 = strtoi(ver_str, NULL, 16);
+
+ ver_str = char_after_dash(ver_str);
+ if (!ver_str)
+ return;
+
+ *firmwareV2 = strtoi(ver_str, NULL, 16);
+}
+
+void _plat__ResetCallback(void)
+{
+ ccd_tpm_reset_callback();
+}
diff --git a/board/cr50/tpm2/rsa.c b/board/cr50/tpm2/rsa.c
index 290ad9cfe5..70e14fba97 100644
--- a/board/cr50/tpm2/rsa.c
+++ b/board/cr50/tpm2/rsa.c
@@ -10,6 +10,8 @@
#include "dcrypto.h"
#include "trng.h"
+#include "cryptoc/util.h"
+
#include <assert.h>
TPM2B_BYTE_VALUE(4);
@@ -32,15 +34,16 @@ static int check_encrypt_params(TPM_ALG_ID padding_alg, TPM_ALG_ID hash_alg,
enum padding_mode *padding,
enum hashing_mode *hashing)
{
+ /* Initialize hashing for all padding types */
+ *hashing = HASH_SHA1;
+
if (padding_alg == TPM_ALG_RSAES) {
*padding = PADDING_MODE_PKCS1;
} else if (padding_alg == TPM_ALG_OAEP) {
/* Only SHA1 and SHA256 supported with OAEP. */
- if (hash_alg == TPM_ALG_SHA1)
- *hashing = HASH_SHA1;
- else if (hash_alg == TPM_ALG_SHA256)
+ if (hash_alg == TPM_ALG_SHA256)
*hashing = HASH_SHA256;
- else
+ else if (hash_alg != TPM_ALG_SHA1)
/* Unsupported hash algorithm. */
return 0;
*padding = PADDING_MODE_OAEP;
@@ -62,6 +65,17 @@ static int check_sign_params(TPM_ALG_ID padding_alg, TPM_ALG_ID hash_alg,
*hashing = HASH_SHA1;
else if (hash_alg == TPM_ALG_SHA256)
*hashing = HASH_SHA256;
+ else if (hash_alg == ALG_SHA384_VALUE &&
+ padding_alg == TPM_ALG_RSASSA)
+ *hashing = HASH_SHA384;
+ else if (hash_alg == ALG_SHA512_VALUE &&
+ padding_alg == TPM_ALG_RSASSA)
+ *hashing = HASH_SHA512;
+#if defined(SUPPORT_PADDING_ONLY_RSASSA) && SUPPORT_PADDING_ONLY_RSASSA == YES
+ else if (hash_alg == TPM_ALG_NULL &&
+ padding_alg == TPM_ALG_RSASSA)
+ *hashing = HASH_NULL;
+#endif
else
return 0;
if (padding_alg == TPM_ALG_RSASSA)
@@ -363,8 +377,8 @@ CRYPT_RESULT _cpri__GenerateKeyRSA(
* template instead.
*/
if (extra->size == sizeof(TPM2_RSA_EK_NAME_TEMPLATE) &&
- memcmp(extra->buffer, TPM2_RSA_EK_NAME_TEMPLATE,
- sizeof(TPM2_RSA_EK_NAME_TEMPLATE)) == 0 &&
+ DCRYPTO_equals(extra->buffer, TPM2_RSA_EK_NAME_TEMPLATE,
+ sizeof(TPM2_RSA_EK_NAME_TEMPLATE)) &&
seed == &endorsement_seed->b) {
memcpy(local_extra.b.buffer, TPM2_RSA_EK_NAME_CR50,
sizeof(TPM2_RSA_EK_NAME_CR50));
@@ -376,7 +390,7 @@ CRYPT_RESULT _cpri__GenerateKeyRSA(
*/
#ifdef CRYPTO_TEST_SETUP
if (seed->size == sizeof(VERIFY_SEED) &&
- memcmp(seed->buffer, VERIFY_SEED, seed->size) == 0) {
+ DCRYPTO_equals(seed->buffer, VERIFY_SEED, seed->size)) {
/* Test seed has already been hashed down. */
memcpy(local_seed.t.buffer, seed->buffer, seed->size);
} else
@@ -415,8 +429,7 @@ CRYPT_RESULT _cpri__GenerateKeyRSA(
&counter)) {
if (counter_in != NULL)
*counter_in = counter;
- /* TODO(ngm): implement secure memset. */
- memset(local_seed.t.buffer, 0, local_seed.t.size);
+ always_memset(local_seed.t.buffer, 0, local_seed.t.size);
return CRYPT_FAIL;
}
@@ -426,8 +439,7 @@ CRYPT_RESULT _cpri__GenerateKeyRSA(
&counter)) {
if (counter_in != NULL)
*counter_in = counter;
- /* TODO(ngm): implement secure memset. */
- memset(local_seed.t.buffer, 0, local_seed.t.size);
+ always_memset(local_seed.t.buffer, 0, local_seed.t.size);
return CRYPT_FAIL;
}
@@ -439,9 +451,8 @@ CRYPT_RESULT _cpri__GenerateKeyRSA(
DCRYPTO_bn_mul(&N, &p, &q);
reverse_tpm2b(N_buf);
reverse_tpm2b(p_buf);
- /* TODO(ngm): replace with secure memset. */
- memset(q_buf, 0, sizeof(q_buf));
- memset(local_seed.t.buffer, 0, local_seed.t.size);
+ always_memset(q_buf, 0, sizeof(q_buf));
+ always_memset(local_seed.t.buffer, 0, local_seed.t.size);
return CRYPT_SUCCESS;
}
@@ -460,7 +471,14 @@ enum {
TEST_X509_VERIFY = 7,
};
-static const TPM2B_PUBLIC_KEY_RSA RSA_768_N = {
+/* Test support for RSA 2k (signing / encryption) and RSA 4k
+ * (verification) without changing the default value for
+ * MAX_RSA_KEY_BYTES (which corresponds to RSA 2k) in
+ * tpm2/tpm_types.h.
+ */
+TPM2B_BYTE_VALUE(512);
+
+static const TPM2B_512_BYTE_VALUE RSA_768_N = {
.t = {96, {
0xb0, 0xdb, 0xed, 0x46, 0xd9, 0x32, 0xf0, 0x7c,
0xd4, 0x20, 0x23, 0xd2, 0x35, 0x5a, 0x86, 0x17,
@@ -478,7 +496,7 @@ static const TPM2B_PUBLIC_KEY_RSA RSA_768_N = {
}
};
-static const TPM2B_PUBLIC_KEY_RSA RSA_768_D = {
+static const TPM2B_512_BYTE_VALUE RSA_768_D = {
.t = {96, {
0xae, 0xad, 0xb9, 0x50, 0x25, 0x8c, 0x1b, 0x5c,
0x9f, 0x42, 0xd3, 0x3e, 0x76, 0x75, 0xdf, 0x45,
@@ -496,7 +514,7 @@ static const TPM2B_PUBLIC_KEY_RSA RSA_768_D = {
}
};
-static const TPM2B_PUBLIC_KEY_RSA RSA_768_P = {
+static const TPM2B_512_BYTE_VALUE RSA_768_P = {
.t = {48, {
0xd6, 0x09, 0x64, 0xc8, 0xf3, 0x5c, 0x02, 0xc7,
0xc6, 0x47, 0x4e, 0x7f, 0x43, 0x9d, 0x31, 0x46,
@@ -508,7 +526,7 @@ static const TPM2B_PUBLIC_KEY_RSA RSA_768_P = {
}
};
-static const TPM2B_PUBLIC_KEY_RSA RSA_768_Q = {
+static const TPM2B_512_BYTE_VALUE RSA_768_Q = {
.t = {48, {
0xd3, 0x88, 0x92, 0x2d, 0xd5, 0xc6, 0x29, 0xf4,
0xf0, 0x2e, 0x61, 0xf0, 0x60, 0xad, 0xa9, 0x46,
@@ -520,7 +538,7 @@ static const TPM2B_PUBLIC_KEY_RSA RSA_768_Q = {
}
};
-static const TPM2B_PUBLIC_KEY_RSA RSA_1024_N = {
+static const TPM2B_512_BYTE_VALUE RSA_1024_N = {
.t = {128, {
0xdf, 0x4e, 0xaf, 0x73, 0x45, 0x94, 0x98, 0x34,
0x30, 0x7e, 0x26, 0xad, 0x40, 0x83, 0xf9, 0x17,
@@ -542,7 +560,7 @@ static const TPM2B_PUBLIC_KEY_RSA RSA_1024_N = {
}
};
-static const TPM2B_PUBLIC_KEY_RSA RSA_1024_D = {
+static const TPM2B_512_BYTE_VALUE RSA_1024_D = {
.t = {128, {
0x9a, 0x6d, 0x85, 0xf4, 0x07, 0xa8, 0x6d, 0x61,
0x9a, 0x2f, 0x83, 0x7b, 0xc8, 0xe3, 0xfb, 0x7c,
@@ -564,7 +582,7 @@ static const TPM2B_PUBLIC_KEY_RSA RSA_1024_D = {
}
};
-static const TPM2B_PUBLIC_KEY_RSA RSA_1024_P = {
+static const TPM2B_512_BYTE_VALUE RSA_1024_P = {
.t = {64, {
0xf9, 0x5e, 0x79, 0x65, 0x43, 0x70, 0x40, 0x83,
0x50, 0x0a, 0xbb, 0x61, 0xb3, 0x87, 0x7b, 0x24,
@@ -578,7 +596,7 @@ static const TPM2B_PUBLIC_KEY_RSA RSA_1024_P = {
}
};
-static const TPM2B_PUBLIC_KEY_RSA RSA_1024_Q = {
+static const TPM2B_512_BYTE_VALUE RSA_1024_Q = {
.t = {64, {
0xe5, 0x3e, 0xcd, 0x4b, 0x97, 0xc5, 0x96, 0x39,
0x70, 0x97, 0x3a, 0x10, 0xa9, 0xc3, 0x35, 0x0a,
@@ -592,7 +610,7 @@ static const TPM2B_PUBLIC_KEY_RSA RSA_1024_Q = {
}
};
-static const TPM2B_PUBLIC_KEY_RSA RSA_2048_N = {
+static const TPM2B_512_BYTE_VALUE RSA_2048_N = {
.t = {256, {
0x9c, 0xd7, 0x61, 0x2e, 0x43, 0x8e, 0x15, 0xbe,
0xcd, 0x73, 0x9f, 0xb7, 0xf5, 0x86, 0x4b, 0xe3,
@@ -742,7 +760,7 @@ static const uint8_t RSA_2048_CERT[] = {
0x57
};
-static const TPM2B_PUBLIC_KEY_RSA RSA_2048_D = {
+static const TPM2B_512_BYTE_VALUE RSA_2048_D = {
.t = {256, {
0x4e, 0x9d, 0x02, 0x1f, 0xdf, 0x4a, 0x8b, 0x89,
0xbc, 0x8f, 0x14, 0xe2, 0x6f, 0x15, 0x66, 0x5a,
@@ -780,7 +798,7 @@ static const TPM2B_PUBLIC_KEY_RSA RSA_2048_D = {
}
};
-static const TPM2B_PUBLIC_KEY_RSA RSA_2048_P = {
+static const TPM2B_512_BYTE_VALUE RSA_2048_P = {
.t = {128, {
0xc8, 0x80, 0x6f, 0xf6, 0x2f, 0xfb, 0x49, 0x8b,
0x77, 0x39, 0xe2, 0x3d, 0x3d, 0x1f, 0x4d, 0xf9,
@@ -802,7 +820,7 @@ static const TPM2B_PUBLIC_KEY_RSA RSA_2048_P = {
}
};
-static const TPM2B_PUBLIC_KEY_RSA RSA_2048_Q = {
+static const TPM2B_512_BYTE_VALUE RSA_2048_Q = {
.t = {128, {
0xc8, 0x41, 0x2a, 0x42, 0xf1, 0x6a, 0x81, 0xac,
0x06, 0xab, 0xd0, 0xb7, 0xc0, 0xbb, 0xc6, 0x13,
@@ -824,7 +842,77 @@ static const TPM2B_PUBLIC_KEY_RSA RSA_2048_Q = {
}
};
-#define MAX_MSG_BYTES RSA_MAX_BYTES
+static const TPM2B_512_BYTE_VALUE RSA_4096_N = {
+ .t = {512, {
+ 0xB4, 0xD2, 0xAB, 0x9C, 0xFA, 0x42, 0x9A, 0x37,
+ 0x70, 0x2A, 0xE4, 0x2D, 0x87, 0x67, 0x7F, 0x15,
+ 0xB6, 0x31, 0x06, 0x06, 0xD5, 0x5A, 0xE9, 0x8E,
+ 0xA9, 0xD4, 0x6D, 0x6B, 0x92, 0xB9, 0x63, 0x37,
+ 0x7D, 0x9C, 0xF0, 0xCA, 0x0A, 0x44, 0x19, 0xF0,
+ 0x71, 0xA7, 0x4C, 0xE0, 0x90, 0x9C, 0xBE, 0x69,
+ 0x4A, 0x9C, 0x55, 0x18, 0xB3, 0x42, 0x43, 0xD9,
+ 0x6C, 0x65, 0xFD, 0xD1, 0xCF, 0x20, 0x0C, 0x92,
+ 0x34, 0x27, 0x46, 0x48, 0x58, 0x0B, 0x13, 0xCD,
+ 0xF1, 0x67, 0x70, 0x02, 0x7E, 0xE7, 0x4C, 0xA9,
+ 0xD8, 0xFD, 0x48, 0x17, 0x80, 0xF6, 0x01, 0x0E,
+ 0x61, 0xB0, 0xBE, 0xEF, 0x87, 0x2B, 0x71, 0x0E,
+ 0x65, 0x81, 0xC1, 0x50, 0xCE, 0xB8, 0x65, 0xD2,
+ 0xD7, 0xFA, 0x76, 0xC4, 0xB7, 0x13, 0xD9, 0xED,
+ 0xDA, 0xD6, 0x9F, 0x9E, 0x10, 0xB2, 0x50, 0xF4,
+ 0xFD, 0xF4, 0xF9, 0xEC, 0xD8, 0x33, 0x56, 0xEF,
+ 0xF4, 0x22, 0xD3, 0xC2, 0xBA, 0x49, 0xD5, 0xA9,
+ 0x0B, 0xB0, 0x18, 0xA3, 0xD9, 0x26, 0xCD, 0x27,
+ 0x76, 0x6A, 0x72, 0x1B, 0x31, 0xAC, 0x3D, 0x42,
+ 0xE8, 0xD9, 0x26, 0xD3, 0x90, 0xBB, 0x39, 0x64,
+ 0xBB, 0xEA, 0x89, 0x0B, 0x7A, 0xB4, 0x88, 0x39,
+ 0xEA, 0xE9, 0xCA, 0x19, 0x8C, 0x42, 0xFB, 0x69,
+ 0x22, 0xCA, 0x8E, 0xBD, 0x6C, 0x73, 0xB7, 0x88,
+ 0xA0, 0xA1, 0x19, 0xA8, 0xFA, 0x94, 0x55, 0x20,
+ 0x6F, 0x80, 0xBA, 0xD7, 0x69, 0x12, 0x1F, 0x74,
+ 0x82, 0xEC, 0xAF, 0xEE, 0x28, 0xAF, 0xCD, 0xDA,
+ 0xAD, 0x1B, 0x5B, 0x54, 0xE4, 0x2B, 0xEF, 0x0C,
+ 0x4D, 0xD1, 0xAB, 0xD7, 0x03, 0xC4, 0xA4, 0x99,
+ 0xA0, 0xBC, 0x7E, 0x9D, 0x3C, 0x2F, 0x34, 0x3B,
+ 0xD5, 0xDD, 0x6A, 0xED, 0xA6, 0x19, 0x93, 0x83,
+ 0xBE, 0xE2, 0xD4, 0x6F, 0x92, 0xFF, 0x55, 0xA7,
+ 0xF7, 0x67, 0xEB, 0xE3, 0x46, 0xD3, 0xE7, 0x6D,
+ 0xC0, 0x29, 0x95, 0x2B, 0xBF, 0xDD, 0xB4, 0x93,
+ 0xBB, 0x45, 0x01, 0xFC, 0x53, 0x21, 0xCE, 0x9F,
+ 0x8B, 0x90, 0x80, 0x09, 0xBA, 0x1A, 0x6E, 0x08,
+ 0x87, 0x15, 0xB4, 0x74, 0xA3, 0xDC, 0xBB, 0xDB,
+ 0x45, 0xF2, 0x38, 0x20, 0x85, 0xC3, 0x9E, 0x4A,
+ 0x45, 0x1E, 0x75, 0x18, 0x05, 0x42, 0x86, 0xAD,
+ 0x47, 0xE6, 0xAA, 0xED, 0x88, 0x9A, 0x9F, 0x37,
+ 0xA3, 0xB8, 0xC7, 0x99, 0x35, 0xE2, 0xA8, 0x8E,
+ 0x9D, 0x2E, 0xBC, 0xF2, 0x64, 0x48, 0xC3, 0x63,
+ 0x4F, 0x1C, 0xD0, 0x45, 0xC3, 0x41, 0xA1, 0xBE,
+ 0xE5, 0xC2, 0xA4, 0x5D, 0x45, 0xF0, 0x03, 0x83,
+ 0xC9, 0x80, 0x4C, 0x68, 0xF0, 0xD1, 0xDA, 0xF1,
+ 0x51, 0x81, 0xD5, 0xFE, 0xC4, 0xE8, 0x8A, 0xD6,
+ 0x32, 0x95, 0x38, 0xD8, 0x3B, 0xDA, 0xF7, 0x8C,
+ 0x5C, 0x6B, 0x01, 0xF4, 0x6E, 0x12, 0xB5, 0xB6,
+ 0x36, 0xE3, 0xD1, 0xAA, 0x49, 0x44, 0x6A, 0xF3,
+ 0xE6, 0x7B, 0x19, 0x7D, 0xF8, 0x35, 0xCB, 0x2D,
+ 0xB8, 0x62, 0x54, 0xCF, 0x2D, 0x66, 0xB3, 0x7F,
+ 0xE0, 0x37, 0x65, 0x19, 0x82, 0xD6, 0x88, 0x24,
+ 0xC2, 0x31, 0x7E, 0x1F, 0x6E, 0xDA, 0x4B, 0x28,
+ 0xAF, 0x5E, 0x2B, 0xE8, 0x34, 0xB2, 0xDC, 0xC7,
+ 0xD1, 0x95, 0x0C, 0x94, 0xE7, 0x09, 0xA0, 0xED,
+ 0xAA, 0x91, 0x2F, 0x91, 0x6F, 0xF0, 0x00, 0xDF,
+ 0x0D, 0x46, 0xEF, 0xD2, 0x59, 0x7B, 0x65, 0xCE,
+ 0xA2, 0xED, 0x8A, 0xE2, 0x1E, 0xD0, 0xE1, 0x93,
+ 0x72, 0x7F, 0x18, 0x79, 0x18, 0x35, 0xF3, 0xCA,
+ 0xDB, 0x01, 0x5F, 0x09, 0x48, 0x21, 0x45, 0xFE,
+ 0x89, 0x44, 0xED, 0x07, 0x79, 0x68, 0x32, 0xD8,
+ 0xD6, 0x8B, 0xA0, 0xC6, 0xDC, 0xF9, 0x56, 0x89,
+ 0x5B, 0xCE, 0x35, 0x05, 0xE9, 0xC1, 0x4A, 0x1E,
+ 0x24, 0x0B, 0xC8, 0x73, 0x18, 0x19, 0x6B, 0xED,
+ 0xF9, 0x3E, 0x92, 0x92, 0xF2, 0x0B, 0x75, 0x25,
+ }
+ }
+};
+
+#define MAX_MSG_BYTES 512
#define MAX_LABEL_LEN 32
/* 128-byte buffer to hold entropy for generating a
@@ -846,10 +934,10 @@ static void rsa_command_handler(void *cmd_body,
uint16_t digest_len;
uint8_t digest[SHA_DIGEST_MAX_BYTES];
uint8_t *out = (uint8_t *) cmd_body;
- TPM2B_PUBLIC_KEY_RSA N;
- TPM2B_PUBLIC_KEY_RSA d;
- TPM2B_PUBLIC_KEY_RSA p;
- TPM2B_PUBLIC_KEY_RSA q;
+ TPM2B_512_BYTE_VALUE N;
+ TPM2B_512_BYTE_VALUE d;
+ TPM2B_512_BYTE_VALUE p;
+ TPM2B_512_BYTE_VALUE q;
RSA_KEY key;
uint32_t *response_size = (uint32_t *) response_size_out;
TPM2B_PUBLIC_KEY_RSA rsa_d;
@@ -903,16 +991,6 @@ static void rsa_command_handler(void *cmd_body,
return;
}
memcpy(in, cmd, in_len);
- if (op == TEST_RSA_VERIFY) {
- cmd += in_len;
- digest_len = ((uint16_t) (cmd[0] << 8)) | cmd[1];
- cmd += 2;
- if (digest_len > sizeof(digest)) {
- *response_size = 0;
- return;
- }
- memcpy(digest, cmd, digest_len);
- }
/* Make copies of N, and d, as const data is immutable. */
switch (key_len) {
@@ -940,6 +1018,10 @@ static void rsa_command_handler(void *cmd_body,
rsa_n.b.size = RSA_2048_N.b.size;
rsa_d.b.size = RSA_2048_D.b.size;
break;
+ case 4096:
+ N = RSA_4096_N;
+ rsa_n.b.size = RSA_4096_N.b.size;
+ break;
default:
*response_size = 0;
return;
@@ -971,6 +1053,15 @@ static void rsa_command_handler(void *cmd_body,
*response_size = 0;
return;
case TEST_RSA_VERIFY:
+ cmd += in_len;
+ digest_len = ((uint16_t) (cmd[0] << 8)) | cmd[1];
+ cmd += 2;
+ if (digest_len > sizeof(digest)) {
+ *response_size = 0;
+ return;
+ }
+ memcpy(digest, cmd, digest_len);
+
if (_cpri__ValidateSignatureRSA(
&key, padding_alg, hashing_alg, digest_len,
digest, in_len, in, 0)
diff --git a/board/cr50/tpm2/stubs.c b/board/cr50/tpm2/stubs.c
index d3e8710604..2d38b2c904 100644
--- a/board/cr50/tpm2/stubs.c
+++ b/board/cr50/tpm2/stubs.c
@@ -52,7 +52,6 @@ BOOL _cpri__Startup(
/*
* Below is the list of functions called by the TPM2 library from
* _cpri__Startup().
- * TODO(vbendeb): verify proper initialization.
*
* _cpri__HashStartup() - not doing anything for now, maybe hw
* reinitialization is required?
diff --git a/board/cr50/tpm2/tpm_state.c b/board/cr50/tpm2/tpm_state.c
index a9b9fdd8f8..43b28ea7b2 100644
--- a/board/cr50/tpm2/tpm_state.c
+++ b/board/cr50/tpm2/tpm_state.c
@@ -52,6 +52,12 @@ static enum vendor_cmd_rc report_tpm_state(enum vendor_cmd_cc code,
memset(state, 0, sizeof(*state));
+ if (board_id_is_mismatched()) {
+ s_failCode = 0xbadc0de;
+ s_failLine = __LINE__;
+ memcpy(&s_failFunction, __func__, sizeof(s_failFunction));
+ }
+
serialize_u32(&state->version, TPM_STATE_VERSION);
serialize_u32(&state->fail_code, s_failCode);
serialize_u32(&state->fail_line, s_failLine);
diff --git a/board/cr50/tpm_nvmem_read.c b/board/cr50/tpm_nvmem_read.c
new file mode 100644
index 0000000000..c71c7cce0c
--- /dev/null
+++ b/board/cr50/tpm_nvmem_read.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "common.h"
+#include "console.h"
+#include "tpm_nvmem_read.h"
+
+/* These come from the tpm2 tree. */
+#include "Global.h"
+#include "Implementation.h"
+#include "NV_fp.h"
+#include "tpm_types.h"
+
+#define CPRINTF(format, args...) cprintf(CC_TASK, format, ## args)
+
+enum tpm_read_rv read_tpm_nvmem(uint16_t obj_index,
+ uint16_t obj_size, void *obj_value)
+{
+ TPMI_RH_NV_INDEX object_handle;
+ NV_INDEX nvIndex;
+
+ object_handle = HR_NV_INDEX + obj_index;
+ if (NvIndexIsAccessible(object_handle,
+ TPM_CC_NV_Read) != TPM_RC_SUCCESS) {
+ CPRINTF("%s: object at 0x%x not found\n", __func__, obj_index);
+ return tpm_read_not_found;
+ }
+
+ /* Get properties of this index as stored in nvmem. */
+ NvGetIndexInfo(object_handle, &nvIndex);
+
+ /*
+ * We presume it is readable and are not checking the access
+ * limitations.
+ */
+
+ /*
+ * Does the caller ask for too much? Note that we always read from the
+ * beginning of the space, unlike the actual TPM2_NV_Read command
+ * which can start at an offset.
+ */
+ if (obj_size > nvIndex.publicArea.dataSize) {
+ CPRINTF("%s: object at 0x%x is smaller than %d\n",
+ __func__, obj_index, obj_size);
+ return tpm_read_too_small;
+ }
+
+ /* Perform the read. */
+ NvGetIndexData(object_handle, &nvIndex, 0, obj_size, obj_value);
+
+ return tpm_read_success;
+}
diff --git a/board/cr50/tpm_nvmem_read.h b/board/cr50/tpm_nvmem_read.h
new file mode 100644
index 0000000000..83d3a415be
--- /dev/null
+++ b/board/cr50/tpm_nvmem_read.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __EC_BOARD_CR50_TPM_NVMEM_READ_H
+#define __EC_BOARD_CR50_TPM_NVMEM_READ_H
+
+enum tpm_read_rv {
+ tpm_read_success,
+ tpm_read_not_found,
+ tpm_read_too_small
+};
+
+enum tpm_read_rv read_tpm_nvmem(uint16_t object_index,
+ uint16_t object_size,
+ void *obj_value);
+
+#endif /* ! __EC_BOARD_CR50_TPM_NVMEM_READ_H */
diff --git a/board/cr50/u2f.c b/board/cr50/u2f.c
new file mode 100644
index 0000000000..f379e66566
--- /dev/null
+++ b/board/cr50/u2f.c
@@ -0,0 +1,226 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Helpers to emulate a U2F HID dongle over the TPM transport */
+
+#include "console.h"
+#include "dcrypto.h"
+#include "extension.h"
+#include "nvmem_vars.h"
+#include "rbox.h"
+#include "registers.h"
+#include "signed_header.h"
+#include "system.h"
+#include "tpm_vendor_cmds.h"
+#include "u2f.h"
+#include "u2f_impl.h"
+#include "util.h"
+
+#define CPRINTS(format, args...) cprints(CC_EXTENSION, format, ## args)
+
+/* ---- physical presence (using the laptop power button) ---- */
+
+static timestamp_t last_press;
+
+/* how long do we keep the last button press as valid presence */
+#define PRESENCE_TIMEOUT (10 * SECOND)
+
+void power_button_record(void)
+{
+ if (ap_is_on() && rbox_powerbtn_is_pressed())
+ last_press = get_time();
+}
+
+enum touch_state pop_check_presence(int consume)
+{
+ int recent = (get_time().val - PRESENCE_TIMEOUT) < last_press.val;
+
+ CPRINTS("Presence:%d", recent);
+ if (consume)
+ last_press.val = 0;
+
+ /* user physical presence on the power button */
+ return recent ? POP_TOUCH_YES : POP_TOUCH_NO;
+}
+
+/* ---- non-volatile U2F parameters ---- */
+
+/*
+ * Current mode defining the behavior of the U2F feature.
+ * Identical to the one defined on the host side by the enum U2fMode
+ * in the chrome_device_policy.proto protobuf.
+ */
+enum u2f_mode {
+ MODE_UNSET = 0,
+ /* Feature disabled */
+ MODE_DISABLED = 1,
+ /* U2F as defined by the FIDO Alliance specification */
+ MODE_U2F = 2,
+ /* U2F plus extensions for individual attestation certificate */
+ MODE_U2F_EXTENDED = 3,
+};
+
+static uint32_t salt[8];
+static uint8_t u2f_mode = MODE_UNSET;
+static const uint8_t k_salt = NVMEM_VAR_U2F_SALT;
+
+static int load_state(void)
+{
+ const struct tuple *t_salt = getvar(&k_salt, sizeof(k_salt));
+
+ if (!t_salt) {
+ /* create random salt */
+ if (!DCRYPTO_ladder_random(salt))
+ return 0;
+ if (setvar(&k_salt, sizeof(k_salt),
+ (const uint8_t *)salt, sizeof(salt)))
+ return 0;
+ /* really save the new variable to flash */
+ writevars();
+ } else {
+ memcpy(salt, tuple_val(t_salt), sizeof(salt));
+ }
+
+ return 1;
+}
+
+static int use_u2f(void)
+{
+ /*
+ * TODO(b/62294740): Put board ID check here if needed
+ * if (!board_id_we_want)
+ * return 0;
+ */
+
+ if (u2f_mode == MODE_UNSET) {
+ if (load_state())
+ /* Start without extension enabled, host will set it */
+ u2f_mode = MODE_U2F;
+ }
+
+ return u2f_mode >= MODE_U2F;
+}
+
+int use_g2f(void)
+{
+ return use_u2f() && u2f_mode == MODE_U2F_EXTENDED;
+}
+
+unsigned u2f_custom_dispatch(uint8_t ins, struct apdu apdu,
+ uint8_t *buf, unsigned *ret_len)
+{
+ if (ins == U2F_VENDOR_MODE) {
+ if (apdu.p1) { /* Set mode */
+ u2f_mode = apdu.p2;
+ }
+ /* return the current mode */
+ buf[0] = use_u2f() ? u2f_mode : 0;
+ *ret_len = 1;
+ return U2F_SW_NO_ERROR;
+ }
+ return U2F_SW_INS_NOT_SUPPORTED;
+}
+
+/* ---- chip-specific U2F crypto ---- */
+
+static int _derive_key(enum dcrypto_appid appid, const uint32_t input[8],
+ uint32_t output[8])
+{
+ struct APPKEY_CTX ctx;
+ int result;
+
+ /* Setup USR-based application key. */
+ if (!DCRYPTO_appkey_init(appid, &ctx))
+ return 0;
+ result = DCRYPTO_appkey_derive(appid, input, output);
+
+ DCRYPTO_appkey_finish(&ctx);
+ return result;
+}
+
+int u2f_origin_keypair(uint8_t *seed, p256_int *d,
+ p256_int *pk_x, p256_int *pk_y)
+{
+ uint32_t tmp[P256_NDIGITS];
+
+ do {
+ if (!DCRYPTO_ladder_random(seed))
+ return EC_ERROR_UNKNOWN;
+ memcpy(tmp, seed, sizeof(tmp));
+ if (!_derive_key(U2F_ORIGIN, tmp, tmp))
+ return EC_ERROR_UNKNOWN;
+ } while (
+ !DCRYPTO_p256_key_from_bytes(pk_x, pk_y, d, (const uint8_t *)tmp));
+
+ return EC_SUCCESS;
+}
+
+int u2f_origin_key(const uint8_t *seed, p256_int *d)
+{
+ uint32_t tmp[P256_NDIGITS];
+
+ memcpy(tmp, seed, sizeof(tmp));
+ if (!_derive_key(U2F_ORIGIN, tmp, tmp))
+ return EC_ERROR_UNKNOWN;
+ return DCRYPTO_p256_key_from_bytes(NULL, NULL, d,
+ (const uint8_t *)tmp) == 0;
+}
+
+int u2f_gen_kek(const uint8_t *origin, uint8_t *kek, size_t key_len)
+{
+ uint32_t buf[P256_NDIGITS];
+
+ if (key_len != sizeof(buf))
+ return EC_ERROR_UNKNOWN;
+ if (!_derive_key(U2F_WRAP, salt, buf))
+ return EC_ERROR_UNKNOWN;
+ memcpy(kek, buf, key_len);
+
+ return EC_SUCCESS;
+}
+
+int g2f_individual_keypair(p256_int *d, p256_int *pk_x, p256_int *pk_y)
+{
+ uint8_t buf[SHA256_DIGEST_SIZE];
+
+ /* Incorporate HIK & diversification constant */
+ if (!_derive_key(U2F_ATTEST, salt, (uint32_t *)buf))
+ return EC_ERROR_UNKNOWN;
+
+ /* Generate unbiased private key */
+ while (!DCRYPTO_p256_key_from_bytes(pk_x, pk_y, d, buf)) {
+ HASH_CTX sha;
+
+ DCRYPTO_SHA256_init(&sha, 0);
+ HASH_update(&sha, buf, sizeof(buf));
+ memcpy(buf, HASH_final(&sha), sizeof(buf));
+ }
+
+ return EC_SUCCESS;
+}
+
+/* ---- Send/receive U2F APDU over TPM vendor commands ---- */
+
+enum vendor_cmd_rc vc_u2f_apdu(enum vendor_cmd_cc code, void *body,
+ size_t cmd_size, size_t *response_size)
+{
+ unsigned retlen;
+
+ if (!use_u2f()) { /* the feature is disabled */
+ uint8_t *cmd = body;
+ /* process it only if the host tries to enable the feature */
+ if (cmd_size < 2 || cmd[1] != U2F_VENDOR_MODE) {
+ *response_size = 0;
+ return VENDOR_RC_NO_SUCH_COMMAND;
+ }
+ }
+
+ /* Process U2F APDU */
+ retlen = u2f_apdu_rcv(body, cmd_size, *response_size);
+
+ *response_size = retlen;
+ return VENDOR_RC_SUCCESS;
+}
+DECLARE_VENDOR_COMMAND(VENDOR_CC_U2F_APDU, vc_u2f_apdu);
diff --git a/board/cr50/usb_i2c.c b/board/cr50/usb_i2c.c
new file mode 100644
index 0000000000..1aa18012c3
--- /dev/null
+++ b/board/cr50/usb_i2c.c
@@ -0,0 +1,98 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "case_closed_debug.h"
+#include "ccd_config.h"
+#include "console.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "i2c.h"
+#include "rdd.h"
+#include "registers.h"
+#include "system.h"
+#include "timer.h"
+#include "usb_i2c.h"
+
+#define CPRINTS(format, args...) cprints(CC_USB, format, ## args)
+
+int usb_i2c_board_is_enabled(void)
+{
+ /*
+ * Note that this signal requires an external pullup, because this is
+ * one of the real open drain pins; we cannot pull it up or drive it
+ * high. On test boards without the pullup, this will mis-detect as
+ * enabled.
+ */
+ return !gpio_get_level(GPIO_EN_PP3300_INA_L);
+}
+
+static void ina_disconnect(void)
+{
+ CPRINTS("I2C disconnect");
+
+ /* Disonnect I2C0 SDA/SCL output to B1/B0 pads */
+ GWRITE(PINMUX, DIOB1_SEL, 0);
+ GWRITE(PINMUX, DIOB0_SEL, 0);
+ /* Disconnect B1/B0 pads to I2C0 input SDA/SCL */
+ GWRITE(PINMUX, I2C0_SDA_SEL, 0);
+ GWRITE(PINMUX, I2C0_SCL_SEL, 0);
+
+ /* Disable power to INA chips */
+ gpio_set_level(GPIO_EN_PP3300_INA_L, 1);
+}
+
+static void ina_connect(void)
+{
+ CPRINTS("I2C connect");
+
+ /* Apply power to INA chips */
+ gpio_set_level(GPIO_EN_PP3300_INA_L, 0);
+
+ /*
+ * Connect B0/B1 pads to I2C0 input SDA/SCL. Note, that the inputs
+ * for these pads are already enabled for the gpio signals I2C_SCL_INA
+ * and I2C_SDA_INA in gpio.inc.
+ */
+ GWRITE(PINMUX, I2C0_SDA_SEL, GC_PINMUX_DIOB1_SEL);
+ GWRITE(PINMUX, I2C0_SCL_SEL, GC_PINMUX_DIOB0_SEL);
+
+ /* Connect I2CS SDA/SCL output to B1/B0 pads */
+ GWRITE(PINMUX, DIOB1_SEL, GC_PINMUX_I2C0_SDA_SEL);
+ GWRITE(PINMUX, DIOB0_SEL, GC_PINMUX_I2C0_SCL_SEL);
+
+ /*
+ * Initialize the i2cm module after the INAs are powered and the signal
+ * lines are connected.
+ */
+ i2cm_init();
+}
+
+void usb_i2c_board_disable(void)
+{
+ if (!usb_i2c_board_is_enabled())
+ return;
+
+ ina_disconnect();
+}
+
+int usb_i2c_board_enable(void)
+{
+ if (servo_is_connected()) {
+ CPRINTS("Servo attached; cannot enable I2C");
+ usb_i2c_board_disable();
+ return EC_ERROR_BUSY;
+ }
+
+ if (!ccd_ext_is_enabled())
+ return EC_ERROR_BUSY;
+
+ if (!ccd_is_cap_enabled(CCD_CAP_I2C))
+ return EC_ERROR_ACCESS_DENIED;
+
+ if (!usb_i2c_board_is_enabled())
+ ina_connect();
+
+ return EC_SUCCESS;
+}
diff --git a/board/cr50/usb_spi.c b/board/cr50/usb_spi.c
index 9ec9c0e7ad..9392bec8f7 100644
--- a/board/cr50/usb_spi.c
+++ b/board/cr50/usb_spi.c
@@ -3,28 +3,156 @@
* found in the LICENSE file.
*/
+#include "byteorder.h"
+#include "ccd_config.h"
+#include "cryptoc/sha256.h"
#include "console.h"
+#include "dcrypto.h"
+#include "extension.h"
#include "gpio.h"
#include "hooks.h"
+#include "physical_presence.h"
#include "registers.h"
#include "spi.h"
+#include "spi_flash.h"
#include "system.h"
+#include "task.h"
#include "timer.h"
+#include "tpm_registers.h"
+#include "tpm_vendor_cmds.h"
#include "usb_spi.h"
#define CPRINTS(format, args...) cprints(CC_USB, format, ## args)
-static uint8_t update_in_progress;
+/* Don't hash more than this at once */
+#define MAX_SPI_HASH_SIZE (4 * 1024 * 1024)
+
+/*
+ * Buffer size to use for reading and hashing. This must be a multiple of the
+ * SHA256 block size (64 bytes) and at least 4 less than the maximum SPI
+ * transaction size for H1 (0x80 bytes). So, 64.
+ */
+#define SPI_HASH_CHUNK_SIZE 64
+
+/* Timeout for auto-disabling SPI hash device, in microseconds */
+#define SPI_HASH_TIMEOUT_US (60 * SECOND)
+
+/* Current device for SPI hashing */
+static uint8_t spi_hash_device = USB_SPI_DISABLE;
+
+/*
+ * Do we need to use NPCX7 gang programming mode?
+ *
+ * If 0, then we hold the EC in reset the whole time we've acquired the SPI
+ * bus, to keep the EC from accessing it.
+ *
+ * If 1, then:
+ *
+ * When we acquire the EC SPI bus, we need to reset the EC, assert the
+ * gang programmer enable, then take the EC out of reset so its boot ROM
+ * can map the EC's internal SPI bus to the EC gang programmer pins.
+ *
+ * When we relinquish the EC SPI bus, we need to reset the EC again while
+ * keeping gang programmer deasserted, then take the EC out of reset. The
+ * EC will then boot normally.
+ */
+static uint8_t use_npcx_gang_mode;
+
+/*
+ * Device and gang mode selected by last spihash command, for use by
+ * spi_hash_pp_done().
+ */
+static uint8_t new_device;
+static uint8_t new_gang_mode;
+
+static void spi_hash_inactive_timeout(void);
+DECLARE_DEFERRED(spi_hash_inactive_timeout);
+
+/*****************************************************************************/
+/*
+ * Mutex and variable for tracking whether the SPI bus is used by the USB
+ * connection or hashing commands.
+ *
+ * Access these ONLY through set_spi_bus_user() and get_spi_bus_user(), to
+ * ensure thread-safe access to the SPI bus.
+ */
+static struct mutex spi_bus_user_mutex;
+static enum spi_bus_user_t {
+ SPI_BUS_USER_NONE = 0,
+ SPI_BUS_USER_USB,
+ SPI_BUS_USER_HASH
+} spi_bus_user = SPI_BUS_USER_NONE;
+
+/**
+ * Set who's using the SPI bus.
+ *
+ * This is thread-safe and will not block if someone owns the bus. You can't
+ * take the bus if someone else has it, and you can only free it if you hold
+ * it. It has no extra effect if you already own the bus.
+ *
+ * @param user What bus user is asking?
+ * @param want_bus Do we want the bus (!=0) or no longer want it (==0)?
+ *
+ * @return EC_SUCCESS, or non-zero error code.
+ */
+static int set_spi_bus_user(enum spi_bus_user_t user, int want_bus)
+{
+ int rv = EC_SUCCESS;
+
+ /*
+ * Serialize access to bus user variable, but don't mutex lock the
+ * entire bus because that would freeze USB or the console instead of
+ * just failing.
+ */
+ mutex_lock(&spi_bus_user_mutex);
+
+ if (want_bus) {
+ /* Can only take the bus if it's free or we already own it */
+ if (spi_bus_user == SPI_BUS_USER_NONE)
+ spi_bus_user = user;
+ else if (spi_bus_user != user)
+ rv = EC_ERROR_BUSY;
+ } else {
+ /* Can only free the bus if it was ours */
+ if (spi_bus_user == user)
+ spi_bus_user = SPI_BUS_USER_NONE;
+ else
+ rv = EC_ERROR_BUSY;
+ }
+
+ mutex_unlock(&spi_bus_user_mutex);
+
+ return rv;
+}
+
+/**
+ * Get the current SPI bus user.
+ */
+static enum spi_bus_user_t get_spi_bus_user(void)
+{
+ return spi_bus_user;
+}
+
+/*****************************************************************************/
+/* Methods to enable / disable the SPI bus and pin mux */
static void disable_ec_ap_spi(void)
{
- /* Configure SPI GPIOs */
- gpio_set_level(GPIO_AP_FLASH_SELECT, 0);
+ int was_ap_spi_en = gpio_get_level(GPIO_AP_FLASH_SELECT);
+
+ /* Disable EC SPI access. */
gpio_set_level(GPIO_EC_FLASH_SELECT, 0);
- /* Release AP and EC */
- deassert_ec_rst();
- deassert_sys_rst();
+ /* Disable AP SPI access. */
+ if (was_ap_spi_en) {
+ /*
+ * The fact that AP SPI access was enabled means that the EC was
+ * held in reset. Therefore, it needs to be released here.
+ */
+ gpio_set_level(GPIO_AP_FLASH_SELECT, 0);
+ deassert_ec_rst();
+ deassert_sys_rst();
+ }
}
static void enable_ec_spi(void)
@@ -33,8 +161,11 @@ static void enable_ec_spi(void)
gpio_set_level(GPIO_AP_FLASH_SELECT, 0);
gpio_set_level(GPIO_EC_FLASH_SELECT, 1);
- /* Hold EC in reset. This will also hold the AP in reset. */
- assert_ec_rst();
+ /*
+ * Note that we don't hold the EC in reset here. This is because some
+ * ECs with internal SPI flash cannot be held in reset in order to
+ * access the flash.
+ */
}
static void enable_ap_spi(void)
@@ -50,38 +181,13 @@ static void enable_ap_spi(void)
assert_ec_rst();
}
-int usb_spi_update_in_progress(void)
-{
- return update_in_progress;
-}
-
-static void update_finished(void)
-{
- update_in_progress = 0;
-
- /*
- * The AP and EC are reset in usb_spi_enable so the TPM is in a bad
- * state. Do a hard reset to reset the entire system.
- */
- system_reset(SYSTEM_RESET_HARD);
-}
-DECLARE_DEFERRED(update_finished);
-
-void usb_spi_board_enable(struct usb_spi_config const *config)
+/**
+ * Enable the pin mux to the SPI master port.
+ */
+static void enable_spi_pinmux(void)
{
- hook_call_deferred(&update_finished_data, -1);
- update_in_progress = 1;
-
- disable_ec_ap_spi();
-
- if (config->state->enabled_host == USB_SPI_EC)
- enable_ec_spi();
- else if (config->state->enabled_host == USB_SPI_AP)
- enable_ap_spi();
- else {
- CPRINTS("DEVICE NOT SUPPORTED");
- return;
- }
+ GWRITE_FIELD(PINMUX, DIOA4_CTL, PD, 0); /* SPI_MOSI */
+ GWRITE_FIELD(PINMUX, DIOA8_CTL, PD, 0); /* SPI_CLK */
/* Connect DIO A4, A8, and A14 to the SPI peripheral */
GWRITE(PINMUX, DIOA4_SEL, 0); /* SPI_MOSI */
@@ -90,17 +196,18 @@ void usb_spi_board_enable(struct usb_spi_config const *config)
/* Set SPI_CS to be an internal pull up */
GWRITE_FIELD(PINMUX, DIOA14_CTL, PU, 1);
- CPRINTS("usb_spi enable %s",
+ CPRINTS("%s: %s", __func__,
gpio_get_level(GPIO_AP_FLASH_SELECT) ? "AP" : "EC");
spi_enable(CONFIG_SPI_FLASH_PORT, 1);
}
-void usb_spi_board_disable(struct usb_spi_config const *config)
+/**
+ * Disable the pin mux to the SPI master port.
+ */
+static void disable_spi_pinmux(void)
{
- CPRINTS("usb_spi disable");
spi_enable(CONFIG_SPI_FLASH_PORT, 0);
- disable_ec_ap_spi();
/* Disconnect SPI peripheral to tri-state pads */
/* Disable internal pull up */
@@ -110,19 +217,69 @@ void usb_spi_board_disable(struct usb_spi_config const *config)
ASSERT(GREAD(PINMUX, GPIO0_GPIO8_SEL) == GC_PINMUX_DIOA8_SEL);
ASSERT(GREAD(PINMUX, GPIO0_GPIO9_SEL) == GC_PINMUX_DIOA14_SEL);
+ GWRITE_FIELD(PINMUX, DIOA4_CTL, PD, 1); /* SPI_MOSI */
+ GWRITE_FIELD(PINMUX, DIOA8_CTL, PD, 1); /* SPI_CLK */
+
/* Set SPI MOSI, CLK, and CS_L as inputs */
GWRITE(PINMUX, DIOA4_SEL, GC_PINMUX_GPIO0_GPIO7_SEL);
GWRITE(PINMUX, DIOA8_SEL, GC_PINMUX_GPIO0_GPIO8_SEL);
GWRITE(PINMUX, DIOA14_SEL, GC_PINMUX_GPIO0_GPIO9_SEL);
+}
+
+/*****************************************************************************/
+/* USB SPI methods */
+
+int usb_spi_board_enable(struct usb_spi_config const *config)
+{
+ int host = config->state->enabled_host;
+
+ /* Make sure we're allowed to enable the requested device */
+ if (host == USB_SPI_EC) {
+ if (!ccd_is_cap_enabled(CCD_CAP_EC_FLASH)) {
+ CPRINTS("%s: EC access denied", __func__);
+ return EC_ERROR_ACCESS_DENIED;
+ }
+ } else if (host == USB_SPI_AP) {
+ if (!ccd_is_cap_enabled(CCD_CAP_AP_FLASH)) {
+ CPRINTS("%s: AP access denied", __func__);
+ return EC_ERROR_ACCESS_DENIED;
+ }
+ } else {
+ CPRINTS("%s: device %d not supported", __func__, host);
+ return EC_ERROR_INVAL;
+ }
+
+ if (set_spi_bus_user(SPI_BUS_USER_USB, 1) != EC_SUCCESS) {
+ CPRINTS("%s: bus in use", __func__);
+ return EC_ERROR_BUSY;
+ }
+
+ disable_ec_ap_spi();
/*
- * TODO(crosbug.com/p/52366): remove once sys_rst just resets the TPM
- * instead of cr50.
- * Resetting the EC and AP cause sys_rst to be asserted currently that
- * will cause cr50 to do a soft reset. Delay the end of the transaction
- * to prevent cr50 from resetting during a series of usb_spi calls.
+ * Only need to check EC vs. AP, because other hosts were ruled out
+ * above.
*/
- hook_call_deferred(&update_finished_data, 1 * SECOND);
+ if (host == USB_SPI_EC)
+ enable_ec_spi();
+ else
+ enable_ap_spi();
+
+ enable_spi_pinmux();
+ return EC_SUCCESS;
+}
+
+void usb_spi_board_disable(struct usb_spi_config const *config)
+{
+ CPRINTS("%s", __func__);
+
+ /* Only disable the SPI bus if we own it */
+ if (get_spi_bus_user() != SPI_BUS_USER_USB)
+ return;
+
+ disable_spi_pinmux();
+ disable_ec_ap_spi();
+ set_spi_bus_user(SPI_BUS_USER_USB, 0);
}
int usb_spi_interface(struct usb_spi_config const *config,
@@ -149,7 +306,8 @@ int usb_spi_interface(struct usb_spi_config const *config,
config->state->enabled_host = USB_SPI_EC;
break;
case USB_SPI_REQ_ENABLE:
- CPRINTS("ERROR: Must specify target");
+ CPRINTS("%s: Must specify target", __func__);
+ /* Fall through... */
case USB_SPI_REQ_DISABLE:
config->state->enabled_host = USB_SPI_DISABLE;
break;
@@ -165,3 +323,444 @@ int usb_spi_interface(struct usb_spi_config const *config,
hook_call_deferred(config->deferred, 0);
return 0;
}
+
+/*****************************************************************************/
+/* Hashing support */
+
+/**
+ * Returns the content of SPI flash
+ *
+ * @param buf_usr Buffer to write flash contents
+ * @param offset Flash offset to start reading from
+ * @param bytes Number of bytes to read.
+ *
+ * @return EC_SUCCESS, or non-zero if any error.
+ */
+int spi_read_chunk(uint8_t *buf_usr, unsigned int offset, unsigned int bytes)
+{
+ uint8_t cmd[4];
+
+ if (bytes > SPI_HASH_CHUNK_SIZE)
+ return EC_ERROR_INVAL;
+
+ cmd[0] = SPI_FLASH_READ;
+ cmd[1] = (offset >> 16) & 0xFF;
+ cmd[2] = (offset >> 8) & 0xFF;
+ cmd[3] = offset & 0xFF;
+
+ return spi_transaction(SPI_FLASH_DEVICE, cmd, 4, buf_usr, bytes);
+}
+
+/**
+ * Reset EC out of gang programming mode if needed.
+ */
+static void spi_hash_stop_ec_device(void)
+{
+ /* If device is not currently EC, nothing to do */
+ if (spi_hash_device != USB_SPI_EC)
+ return;
+
+ if (use_npcx_gang_mode) {
+ /*
+ * EC was in gang mode. Pulse reset without asserting gang
+ * programmer enable, so that when we take the EC out of reset
+ * it will boot normally.
+ */
+ assert_ec_rst();
+ usleep(200);
+ use_npcx_gang_mode = 0;
+ }
+
+ /*
+ * Release EC from reset (either from above, or because gang progamming
+ * mode was disabled so the EC was held in reset during SPI access).
+ */
+ deassert_ec_rst();
+}
+
+/**
+ * Disable SPI hashing mode.
+ *
+ * @return Vendor command return code.
+ */
+static enum vendor_cmd_rc spi_hash_disable(void)
+{
+ if (spi_hash_device == USB_SPI_DISABLE)
+ return VENDOR_RC_SUCCESS;
+
+ /* Can't disable SPI if we don't own it */
+ if (get_spi_bus_user() != SPI_BUS_USER_HASH)
+ return VENDOR_RC_NOT_ALLOWED;
+
+ /* Disable the SPI bus and chip select */
+ disable_spi_pinmux();
+ disable_ec_ap_spi();
+
+ /* Stop the EC device, if it was active */
+ spi_hash_stop_ec_device();
+
+ /* Release the bus */
+ spi_hash_device = USB_SPI_DISABLE;
+ new_device = USB_SPI_DISABLE;
+ new_gang_mode = 0;
+ set_spi_bus_user(SPI_BUS_USER_HASH, 0);
+
+ /* Disable inactivity timer to turn hashing mode off */
+ hook_call_deferred(&spi_hash_inactive_timeout_data, -1);
+
+ CPRINTS("%s", __func__);
+ return VENDOR_RC_SUCCESS;
+}
+
+/**
+ * Deferred function to disable SPI hash mode on inactivity.
+ */
+static void spi_hash_inactive_timeout(void)
+{
+ spi_hash_disable();
+}
+
+/**
+ * Callback to set up the new SPI device after physical presence check.
+ */
+static void spi_hash_pp_done(void)
+{
+ /* Acquire the bus */
+ if (set_spi_bus_user(SPI_BUS_USER_HASH, 1)) {
+ CPRINTS("%s: bus busy", __func__);
+ return;
+ }
+
+ /* Clear previous enable if needed */
+ if (spi_hash_device != USB_SPI_DISABLE)
+ disable_ec_ap_spi();
+
+ /* Set up new device */
+ if (new_device == USB_SPI_AP) {
+ /* Stop the EC device, if it was previously active */
+ spi_hash_stop_ec_device();
+
+ enable_ap_spi();
+ } else {
+ /* Force the EC into reset and enable EC SPI bus */
+ assert_ec_rst();
+ enable_ec_spi();
+
+ /*
+ * If EC is headed into gang programmer mode, need to release
+ * EC from reset after acquiring the bus. EC_FLASH_SELECT runs
+ * to the EC's GP_SEL_ODL signal, which is what enables gang
+ * programmer mode.
+ */
+ if (new_gang_mode) {
+ usleep(200);
+ deassert_ec_rst();
+ use_npcx_gang_mode = 1;
+ }
+ }
+
+ enable_spi_pinmux();
+ spi_hash_device = new_device;
+
+ /* Start inactivity timer to turn hashing mode off */
+ hook_call_deferred(&spi_hash_inactive_timeout_data,
+ SPI_HASH_TIMEOUT_US);
+
+ CPRINTS("%s: %s", __func__,
+ (spi_hash_device == USB_SPI_AP ? "AP" : "EC"));
+}
+
+/* Process vendor subcommand dealing with Physical presence polling. */
+static enum vendor_cmd_rc spihash_pp_poll(void *buf,
+ size_t input_size,
+ size_t *response_size)
+{
+ char *buffer = buf;
+
+ if (spi_hash_device != USB_SPI_DISABLE) {
+ buffer[0] = CCD_PP_DONE;
+ } else {
+ switch (physical_presense_fsm_state()) {
+ case PP_AWAITING_PRESS:
+ buffer[0] = CCD_PP_AWAITING_PRESS;
+ break;
+ case PP_BETWEEN_PRESSES:
+ buffer[0] = CCD_PP_BETWEEN_PRESSES;
+ break;
+ default:
+ buffer[0] = CCD_PP_CLOSED;
+ break;
+ }
+ }
+ *response_size = 1;
+ return VENDOR_RC_SUCCESS;
+}
+
+/**
+ * Set the SPI hashing device.
+ *
+ * @param dev Device (enum usb_spi)
+ * @param gang_mode If non-zero, EC uses gang mode
+ *
+ * @return Vendor command return code
+ */
+static enum vendor_cmd_rc spi_hash_set_device(int dev, int gang_mode,
+ uint8_t *response_buf,
+ size_t *response_size)
+{
+ *response_size = 0;
+
+ if (dev == spi_hash_device)
+ return VENDOR_RC_SUCCESS;
+
+ /* Enabling requires permission */
+ if (!(ccd_is_cap_enabled(CCD_CAP_FLASH_READ)))
+ return VENDOR_RC_NOT_ALLOWED;
+
+ new_device = dev;
+ new_gang_mode = gang_mode;
+
+ /* Handle enabling */
+ if (spi_hash_device == USB_SPI_DISABLE &&
+ !(ccd_is_cap_enabled(CCD_CAP_AP_FLASH) &&
+ ccd_is_cap_enabled(CCD_CAP_EC_FLASH))) {
+ /*
+ * We were disabled, and CCD does not grant permission
+ * to both flash chips. So we need physical presence
+ * to take the SPI bus. That prevents a malicious
+ * peripheral from using this to reset the device.
+ *
+ * Technically, we could track the chips separately,
+ * and only require physical presence the first time we
+ * check a chip which CCD doesn't grant access to. But
+ * that's more bookkeeping, so for now the only way to
+ * skip physical presence is to have access to both.
+ */
+ int rv = physical_detect_start(0, spi_hash_pp_done);
+
+ if (rv == EC_SUCCESS)
+ return VENDOR_RC_IN_PROGRESS;
+
+ *response_size = 1;
+ response_buf[0] = rv;
+
+ return VENDOR_RC_INTERNAL_ERROR;
+ }
+
+ /*
+ * If we're still here, we already own the SPI bus, and are
+ * changing which chip we're looking at. Update hash device
+ * directly; no new physical presence required.
+ */
+ spi_hash_pp_done();
+ return VENDOR_RC_SUCCESS;
+}
+
+static enum vendor_cmd_rc spi_hash_dump(uint8_t *dest, uint32_t offset,
+ uint32_t size)
+{
+ /* Fail if we don't own the bus */
+ if (get_spi_bus_user() != SPI_BUS_USER_HASH) {
+ CPRINTS("%s: not enabled", __func__);
+ return VENDOR_RC_NOT_ALLOWED;
+ }
+
+ /* Bump inactivity timer to turn hashing mode off */
+ hook_call_deferred(&spi_hash_inactive_timeout_data,
+ SPI_HASH_TIMEOUT_US);
+
+ if (size > SPI_HASH_MAX_RESPONSE_BYTES)
+ return VENDOR_RC_BOGUS_ARGS;
+
+ if (spi_read_chunk(dest, offset, size) != EC_SUCCESS) {
+ CPRINTS("%s: read error at 0x%x", __func__, offset);
+ return VENDOR_RC_READ_FLASH_FAIL;
+ }
+
+ return VENDOR_RC_SUCCESS;
+}
+
+static enum vendor_cmd_rc spi_hash_sha256(uint8_t *dest, uint32_t offset,
+ uint32_t size)
+{
+ HASH_CTX sha;
+ uint8_t data[SPI_HASH_CHUNK_SIZE];
+ int chunk_size = SPI_HASH_CHUNK_SIZE;
+ int chunks = 0;
+
+ /* Fail if we don't own the bus */
+ if (get_spi_bus_user() != SPI_BUS_USER_HASH) {
+ CPRINTS("%s: not enabled", __func__);
+ return VENDOR_RC_NOT_ALLOWED;
+ }
+
+ /* Bump inactivity timer to turn hashing mode off */
+ hook_call_deferred(&spi_hash_inactive_timeout_data,
+ SPI_HASH_TIMEOUT_US);
+
+ if (size > MAX_SPI_HASH_SIZE)
+ return VENDOR_RC_BOGUS_ARGS;
+
+ CPRINTS("%s: 0x%x 0x%x", __func__, offset, size);
+
+ DCRYPTO_SHA256_init(&sha, 0);
+
+ for (chunks = 0; size > 0; chunks++) {
+ int this_chunk = MIN(size, chunk_size);
+ /* Read the data */
+ if (spi_read_chunk(data, offset, this_chunk) != EC_SUCCESS) {
+ CPRINTS("%s: read error at 0x%x", __func__, offset);
+ return VENDOR_RC_READ_FLASH_FAIL;
+ }
+
+ /* Update hash */
+ HASH_update(&sha, data, this_chunk);
+
+ /* Give other things a chance to happen */
+ if (!(chunks % 128))
+ msleep(1);
+
+ size -= this_chunk;
+ offset += this_chunk;
+ }
+
+ memcpy(dest, HASH_final(&sha), SHA256_DIGEST_SIZE);
+
+ CPRINTS("%s: done", __func__);
+ return VENDOR_RC_SUCCESS;
+}
+
+/*
+ * TPM Vendor command handler for SPI hash commands which need to be available
+ * both through CLI and over /dev/tpm0.
+ */
+static enum vendor_cmd_rc spi_hash_vendor(enum vendor_cmd_cc code,
+ void *buf,
+ size_t input_size,
+ size_t *response_size)
+{
+ const struct vendor_cc_spi_hash_request *req = buf;
+ enum vendor_cmd_rc rc;
+
+ /* Default to no response data */
+ *response_size = 0;
+
+ /* Pick what to do based on subcommand. */
+ switch (req->subcmd) {
+ case SPI_HASH_SUBCMD_DISABLE:
+ /* Handle disabling */
+ return spi_hash_disable();
+ case SPI_HASH_SUBCMD_AP:
+ return spi_hash_set_device(USB_SPI_AP, 0, buf, response_size);
+ case SPI_HASH_SUBCMD_EC:
+ return spi_hash_set_device(USB_SPI_EC,
+ !!(req->flags &
+ SPI_HASH_FLAG_EC_GANG),
+ buf, response_size);
+ case SPI_HASH_SUBCMD_SHA256:
+ *response_size = SHA256_DIGEST_SIZE;
+ rc = spi_hash_sha256(buf, req->offset, req->size);
+ if (rc != VENDOR_RC_SUCCESS)
+ *response_size = 0;
+ return rc;
+ case SPI_HASH_SUBCMD_DUMP:
+ /* Save size before we overwrite it with data */
+ *response_size = req->size;
+ rc = spi_hash_dump(buf, req->offset, req->size);
+ if (rc != VENDOR_RC_SUCCESS)
+ *response_size = 0;
+ return rc;
+ case SPI_HASH_PP_POLL:
+ return spihash_pp_poll(buf, input_size, response_size);
+
+ default:
+ CPRINTS("%s:%d - unknown subcommand %d",
+ __func__, __LINE__, req->subcmd);
+ *response_size = 0;
+ return VENDOR_RC_NO_SUCH_SUBCOMMAND;
+ }
+}
+DECLARE_VENDOR_COMMAND(VENDOR_CC_SPI_HASH, spi_hash_vendor);
+
+/**
+ * Wrapper for hash commands which are passed through the TPM task context.
+ */
+static int hash_command_wrapper(int argc, char *argv[])
+{
+ int rv;
+ struct vendor_cc_spi_hash_request req;
+ struct tpm_cmd_header *tpm_header;
+ const size_t command_size = sizeof(*tpm_header) +
+ MAX(sizeof(req), SPI_HASH_MAX_RESPONSE_BYTES);
+ uint8_t buf[command_size];
+ uint8_t *p;
+ uint32_t return_code;
+
+ /* If no args, just return */
+ if (argc < 2) {
+ ccprintf("SPI hash device: %s\n",
+ (spi_hash_device ?
+ (spi_hash_device == USB_SPI_AP ? "AP" : "EC") :
+ "disable"));
+ return EC_SUCCESS;
+ }
+
+ /* Parse args into stack-based struct */
+ memset(&req, 0, sizeof(req));
+ if (!strcasecmp(argv[1], "AP")) {
+ req.subcmd = SPI_HASH_SUBCMD_AP;
+ } else if (!strcasecmp(argv[1], "EC")) {
+ req.subcmd = SPI_HASH_SUBCMD_EC;
+ if (argc > 2 && !strcasecmp(argv[2], "gang"))
+ req.flags |= SPI_HASH_FLAG_EC_GANG;
+ } else if (!strcasecmp(argv[1], "disable")) {
+ req.subcmd = SPI_HASH_SUBCMD_DISABLE;
+ } else if (argc == 3) {
+ req.subcmd = SPI_HASH_SUBCMD_SHA256;
+ rv = parse_offset_size(argc, argv, 1, &req.offset, &req.size);
+ if (rv)
+ return rv;
+ } else if (argc == 4 && !strcasecmp(argv[1], "dump")) {
+ req.subcmd = SPI_HASH_SUBCMD_DUMP;
+ rv = parse_offset_size(argc, argv, 2, &req.offset, &req.size);
+ if (rv)
+ return rv;
+ } else {
+ return EC_ERROR_PARAM1;
+ }
+
+ /* Build the extension command */
+ tpm_header = (struct tpm_cmd_header *)buf;
+ tpm_header->tag = htobe16(0x8001); /* TPM_ST_NO_SESSIONS */
+ tpm_header->size = htobe32(command_size);
+ tpm_header->command_code = htobe32(TPM_CC_VENDOR_BIT_MASK);
+ tpm_header->subcommand_code = htobe16(VENDOR_CC_SPI_HASH);
+ /* Copy request data */
+ p = (uint8_t *)(tpm_header + 1);
+ memcpy(p, &req, sizeof(req));
+
+ tpm_alt_extension(tpm_header, command_size);
+
+ /*
+ * Return status in the command code field now, in case of error,
+ * error code is the first byte after the header.
+ */
+ return_code = be32toh(tpm_header->command_code);
+
+ if ((return_code != EC_SUCCESS) &&
+ ((return_code - VENDOR_RC_ERR) != VENDOR_RC_IN_PROGRESS)) {
+ rv = p[0];
+ } else {
+ rv = EC_SUCCESS;
+
+ if (req.subcmd == SPI_HASH_SUBCMD_DUMP)
+ ccprintf("data: %.*h\n", req.size, p);
+ else if (req.subcmd == SPI_HASH_SUBCMD_SHA256)
+ ccprintf("hash: %.32h\n", p);
+ }
+
+ return rv;
+}
+DECLARE_SAFE_CONSOLE_COMMAND(spihash, hash_command_wrapper,
+ "ap | ec [gang] | disable | [dump] <offset> <size>",
+ "Hash SPI flash via TPM vendor command");
diff --git a/board/cr50/wp.c b/board/cr50/wp.c
index b2afc10f85..e4fc54c630 100644
--- a/board/cr50/wp.c
+++ b/board/cr50/wp.c
@@ -3,184 +3,368 @@
* found in the LICENSE file.
*/
-#include "common.h"
+#include "ccd_config.h"
#include "console.h"
+#include "crc8.h"
+#include "extension.h"
+#include "gpio.h"
#include "hooks.h"
#include "registers.h"
+#include "scratch_reg1.h"
#include "system.h"
-#include "task.h"
-#include "timer.h"
+#include "system_chip.h"
+#include "tpm_nvmem_read.h"
+#include "tpm_registers.h"
+#include "util.h"
#define CPRINTS(format, args...) cprints(CC_RBOX, format, ## args)
#define CPRINTF(format, args...) cprintf(CC_RBOX, format, ## args)
-static int command_wp(int argc, char **argv)
+/**
+ * Return non-zero if battery is present
+ */
+int board_battery_is_present(void)
{
- int val;
+ /* Invert because battery-present signal is active low */
+ return !gpio_get_level(GPIO_BATT_PRES_L);
+}
- if (argc > 1) {
- if (!parse_bool(argv[1], &val))
- return EC_ERROR_PARAM1;
+/**
+ * Set the current write protect state in RBOX and long life scratch register.
+ *
+ * @param asserted: 0 to disable write protect, otherwise enable write protect.
+ */
+static void set_wp_state(int asserted)
+{
+ /* Enable writing to the long life register */
+ GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG1, 1);
- /* Invert, because active low */
- GREG32(RBOX, EC_WP_L) = !val;
+ if (asserted) {
+ GREG32(PMU, LONG_LIFE_SCRATCH1) |= BOARD_WP_ASSERTED;
+ GREG32(RBOX, EC_WP_L) = 0;
+ } else {
+ GREG32(PMU, LONG_LIFE_SCRATCH1) &= ~BOARD_WP_ASSERTED;
+ GREG32(RBOX, EC_WP_L) = 1;
}
- /* Invert, because active low */
- val = !GREG32(RBOX, EC_WP_L);
-
- ccprintf("Flash WP is %s\n", val ? "enabled" : "disabled");
+ /* Disable writing to the long life register */
+ GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG1, 0);
+}
- return EC_SUCCESS;
+/**
+ * Return the current WP state
+ *
+ * @return 0 if WP deasserted, 1 if WP asserted
+ */
+static int get_wp_state(void)
+{
+ /* Signal is active low, so invert */
+ return !GREG32(RBOX, EC_WP_L);
}
-DECLARE_CONSOLE_COMMAND(wp, command_wp,
- "[<BOOLEAN>]",
- "Get/set the flash HW write-protect signal");
-/* When the system is locked down, provide a means to unlock it */
-#ifdef CONFIG_RESTRICTED_CONSOLE_COMMANDS
+static void check_wp_battery_presence(void)
+{
+ int bp = board_battery_is_present();
-/* TODO(crosbug.com/p/55510): It should be locked by default */
-static int console_restricted_state;
+ /* If we're forcing WP, ignore battery detect */
+ if (GREG32(PMU, LONG_LIFE_SCRATCH1) & BOARD_FORCING_WP)
+ return;
-int console_is_restricted(void)
-{
- return console_restricted_state;
+ /* Otherwise, mirror battery */
+ if (bp != get_wp_state()) {
+ CPRINTS("WP %d", bp);
+ set_wp_state(bp);
+ }
}
+DECLARE_HOOK(HOOK_SECOND, check_wp_battery_presence, HOOK_PRIO_DEFAULT);
+
+/**
+ * Force write protect state or follow battery presence.
+ *
+ * @param force: Force write protect to wp_en if non-zero, otherwise use battery
+ * presence as the source.
+ * @param wp_en: 0: Deassert WP. 1: Assert WP.
+ */
+static void force_write_protect(int force, int wp_en)
+{
+ /* Enable writing to the long life register */
+ GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG1, 1);
-/****************************************************************************/
-/* Stuff for the unlock dance */
+ if (force) {
+ /* Force WP regardless of battery presence. */
+ GREG32(PMU, LONG_LIFE_SCRATCH1) |= BOARD_FORCING_WP;
+ } else {
+ /* Stop forcing write protect. */
+ GREG32(PMU, LONG_LIFE_SCRATCH1) &= ~BOARD_FORCING_WP;
+ /* Use battery presence as the value for write protect. */
+ wp_en = board_battery_is_present();
+ }
-/* Total time to spend poking the power button */
-#define DANCE_TIME (10 * SECOND)
-/* Max time between pokes */
-#define DANCE_BEAT (2 * SECOND)
+ /* Disable writing to the long life register */
+ GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG1, 0);
-static timestamp_t dance_deadline;
-static int dance_in_progress;
+ /* Update the WP state. */
+ set_wp_state(wp_en);
+}
-/* This will only be invoked when the dance is done, either good or bad. */
-static void dance_is_over(void)
+static int command_wp(int argc, char **argv)
{
- if (dance_in_progress) {
- CPRINTS("Unlock dance failed");
- } else {
- CPRINTS("Unlock dance completed successfully");
- console_restricted_state = 0;
+ int val = 1;
+ int forced = 1;
+
+ if (argc > 1) {
+ /* Make sure we're allowed to override WP settings */
+ if (!ccd_is_cap_enabled(CCD_CAP_OVERRIDE_WP))
+ return EC_ERROR_ACCESS_DENIED;
+
+ /* Update WP */
+ if (strncasecmp(argv[1], "follow_batt_pres", 16) == 0)
+ forced = 0;
+ else if (parse_bool(argv[1], &val))
+ forced = 1;
+ else
+ return EC_ERROR_PARAM1;
+
+ force_write_protect(forced, val);
+
+ if (argc > 2 && !strcasecmp(argv[2], "atboot")) {
+ /* Change override at boot to match */
+ ccd_set_flag(CCD_FLAG_OVERRIDE_WP_AT_BOOT, forced);
+ ccd_set_flag(CCD_FLAG_OVERRIDE_WP_STATE_ENABLED, val);
+ }
}
- dance_in_progress = 0;
+ forced = GREG32(PMU, LONG_LIFE_SCRATCH1) & BOARD_FORCING_WP;
+ ccprintf("Flash WP: %s%s\n", forced ? "forced " : "",
+ get_wp_state() ? "enabled" : "disabled");
- /* Disable power button interrupt */
- GWRITE_FIELD(RBOX, INT_ENABLE, INTR_PWRB_IN_FED, 0);
- task_disable_irq(GC_IRQNUM_RBOX0_INTR_PWRB_IN_FED_INT);
+ ccprintf(" at boot: ");
+ if (ccd_get_flag(CCD_FLAG_OVERRIDE_WP_AT_BOOT))
+ ccprintf("forced %s\n",
+ ccd_get_flag(CCD_FLAG_OVERRIDE_WP_STATE_ENABLED)
+ ? "enabled" : "disabled");
+ else
+ ccprintf("follow_batt_pres\n");
- /* Allow sleeping again */
- enable_sleep(SLEEP_MASK_FORCE_NO_DSLEEP);
+ return EC_SUCCESS;
}
-DECLARE_DEFERRED(dance_is_over);
+DECLARE_SAFE_CONSOLE_COMMAND(wp, command_wp,
+ "[<BOOLEAN>/follow_batt_pres [atboot]]",
+ "Get/set the flash HW write-protect signal");
-static void power_button_poked(void)
+void init_wp_state(void)
{
- if (timestamp_expired(dance_deadline, NULL)) {
- /* We've been poking for long enough */
- dance_in_progress = 0;
- hook_call_deferred(&dance_is_over_data, 0);
- CPRINTS("poke: enough already", __func__);
+ /* Check system reset flags after CCD config is initially loaded */
+ if ((system_get_reset_flags() & RESET_FLAG_HIBERNATE) &&
+ !system_rollback_detected()) {
+ /*
+ * Deep sleep resume without rollback, so reload the WP state
+ * that was saved to the long-life registers before the deep
+ * sleep instead of going back to the at-boot default.
+ */
+ if (GREG32(PMU, LONG_LIFE_SCRATCH1) & BOARD_FORCING_WP) {
+ /* Temporarily forcing WP */
+ set_wp_state(GREG32(PMU, LONG_LIFE_SCRATCH1) &
+ BOARD_WP_ASSERTED);
+ } else {
+ /* Write protected if battery is present */
+ set_wp_state(board_battery_is_present());
+ }
+ } else if (ccd_get_flag(CCD_FLAG_OVERRIDE_WP_AT_BOOT)) {
+ /* Reset to at-boot state specified by CCD */
+ force_write_protect(1, ccd_get_flag(
+ CCD_FLAG_OVERRIDE_WP_STATE_ENABLED));
} else {
- /* Wait for the next poke */
- hook_call_deferred(&dance_is_over_data, DANCE_BEAT);
- CPRINTS("poke");
+ /* Reset to WP based on battery-present (val is ignored) */
+ force_write_protect(0, 1);
}
-
- GWRITE_FIELD(RBOX, INT_STATE, INTR_PWRB_IN_FED, 1);
}
-DECLARE_IRQ(GC_IRQNUM_RBOX0_INTR_PWRB_IN_FED_INT, power_button_poked, 1);
-
-static int start_the_dance(void)
+/**
+ * Wipe the TPM
+ *
+ * @return EC_SUCCESS, or non-zero if error.
+ */
+int board_wipe_tpm(void)
{
- /* Don't invoke more than one at a time */
- if (dance_in_progress)
- return EC_ERROR_BUSY;
-
- dance_in_progress = 1;
-
- /* Clear any leftover power button interrupts */
- GWRITE_FIELD(RBOX, INT_STATE, INTR_PWRB_IN_FED, 1);
-
- /* Enable power button interrupt */
- GWRITE_FIELD(RBOX, INT_ENABLE, INTR_PWRB_IN_FED, 1);
- task_enable_irq(GC_IRQNUM_RBOX0_INTR_PWRB_IN_FED_INT);
+ int rc;
+
+ /*
+ * Blindly zapping the TPM space while the AP is awake and poking at
+ * it will bork the TPM task and the AP itself, so force the whole
+ * system off by holding the EC in reset.
+ */
+ CPRINTS("%s: force EC off", __func__);
+ assert_ec_rst();
+
+ /* Wipe the TPM's memory and reset the TPM task. */
+ rc = tpm_reset_request(1, 1);
+ if (rc != EC_SUCCESS) {
+ /*
+ * If anything goes wrong (which is unlikely), we REALLY don't
+ * want to unlock the console. It's possible to fail without
+ * the TPM task ever running, so rebooting is probably our best
+ * bet for fixing the problem.
+ */
+ CPRINTS("%s: Couldn't wipe nvmem! (rc %d)", __func__, rc);
+ cflush();
+ system_reset(SYSTEM_RESET_MANUALLY_TRIGGERED |
+ SYSTEM_RESET_HARD);
- /* Keep dancing until it's been long enough */
- dance_deadline = get_time();
- dance_deadline.val += DANCE_TIME;
+ /*
+ * That should never return, but if it did, release EC reset
+ * and pass through the error we got.
+ */
+ deassert_ec_rst();
+ return rc;
+ }
- /* Stay awake while we're doing this, just in case. */
- disable_sleep(SLEEP_MASK_FORCE_NO_DSLEEP);
+ CPRINTS("TPM is erased");
- /* Check progress after waiting long enough for one button press */
- hook_call_deferred(&dance_is_over_data, DANCE_BEAT);
+ /* Tell the TPM task to re-enable NvMem commits. */
+ tpm_reinstate_nvmem_commits();
- CPRINTS("Unlock dance starting. Dance until %.6ld", dance_deadline);
+ /* Let the rest of the system boot. */
+ CPRINTS("%s: release EC reset", __func__);
+ deassert_ec_rst();
return EC_SUCCESS;
}
/****************************************************************************/
+/* FWMP TPM NVRAM space support */
-static int command_lock(int argc, char **argv)
+/*
+ * These definitions and the structure layout were manually copied from
+ * src/platform/vboot_reference/firmware/lib/include/rollback_index.h. at
+ * git sha c7282f6.
+ */
+#define FWMP_NV_INDEX 0x100a
+#define FWMP_HASH_SIZE 32
+#define FWMP_DEV_DISABLE_CCD_UNLOCK (1 << 6)
+
+/* Firmware management parameters */
+struct RollbackSpaceFwmp {
+ /* CRC-8 of fields following struct_size */
+ uint8_t crc;
+ /* Structure size in bytes */
+ uint8_t struct_size;
+ /* Structure version */
+ uint8_t struct_version;
+ /* Reserved; ignored by current reader */
+ uint8_t reserved0;
+ /* Flags; see enum fwmp_flags */
+ uint32_t flags;
+ /* Hash of developer kernel key */
+ uint8_t dev_key_hash[FWMP_HASH_SIZE];
+} __packed;
+
+static int lock_enforced(const struct RollbackSpaceFwmp *fwmp)
{
- int enabled;
- int i;
+ uint8_t crc;
- if (argc > 1) {
- if (!parse_bool(argv[1], &enabled))
- return EC_ERROR_PARAM1;
+ /* Let's verify that the FWMP structure makes sense. */
+ if (fwmp->struct_size != sizeof(*fwmp)) {
+ CPRINTS("%s: fwmp size mismatch (%d)\n", __func__,
+ fwmp->struct_size);
+ return 1;
+ }
- /* Changing nothing does nothing */
- if (enabled == console_restricted_state)
- goto out;
+ crc = crc8(&fwmp->struct_version, sizeof(struct RollbackSpaceFwmp) -
+ offsetof(struct RollbackSpaceFwmp, struct_version));
+ if (fwmp->crc != crc) {
+ CPRINTS("%s: fwmp crc mismatch\n", __func__);
+ return 1;
+ }
- /* Locking the console is always allowed */
- if (enabled) {
- console_restricted_state = 1;
- goto out;
- }
+ return !!(fwmp->flags & FWMP_DEV_DISABLE_CCD_UNLOCK);
+}
- /*
- * TODO(crosbug.com/p/55322, crosbug.com/p/55728): There may be
- * other preconditions which must be satisified before
- * continuing. We can return EC_ERROR_ACCESS_DENIED if those
- * aren't met.
- */
+static int fwmp_allows_unlock;
+void read_fwmp(void)
+{
+ /* Let's see if FWMP disables console activation. */
+ struct RollbackSpaceFwmp fwmp;
+
+ switch (read_tpm_nvmem(FWMP_NV_INDEX,
+ sizeof(struct RollbackSpaceFwmp), &fwmp)) {
+ default:
+ /* Something is messed up, let's not allow console unlock. */
+ fwmp_allows_unlock = 0;
+ break;
+
+ case tpm_read_not_found:
+ fwmp_allows_unlock = 1;
+ break;
+
+ case tpm_read_success:
+ fwmp_allows_unlock = !lock_enforced(&fwmp);
+ break;
+ }
- /* Don't count down if we know it's likely to fail */
- if (dance_in_progress) {
- ccprintf("An unlock dance is already in progress\n");
- return EC_ERROR_BUSY;
- }
+ CPRINTS("Console unlock %sallowed", fwmp_allows_unlock ? "" : "not ");
+}
+
+/**
+ * Return non-zero if FWMP allows unlock
+ */
+int board_fwmp_allows_unlock(void)
+{
+ /*
+ * TODO(rspangler): This doesn't work right for CCD config unlock and
+ * open, because read_fwmp() isn't called until TPM2_Startup is sent by
+ * the AP. But that means if the AP can't boot, it's not possible to
+ * unlock or open CCD.
+ *
+ * CCD config isn't connected to anything else yet, so let's bypass
+ * the fwmp check for now. But we need to fix this before we make
+ * a Cr50 release that could run on a MP device.
+ */
+#ifdef CR50_DEV
+ return 1;
+#else
+ return fwmp_allows_unlock;
+#endif
+}
+
+/****************************************************************************/
+/* TPM vendor-specific commands */
+
+static enum vendor_cmd_rc vc_lock(enum vendor_cmd_cc code,
+ void *buf,
+ size_t input_size,
+ size_t *response_size)
+{
+ uint8_t *buffer = buf;
- /* Now the user has to sit there and poke the button */
- ccprintf("Start poking the power button in ");
- for (i = 5; i; i--) {
- ccprintf("%d ", i);
- sleep(1);
+ if (code == VENDOR_CC_GET_LOCK) {
+ /*
+ * Get the state of the console lock.
+ *
+ * Args: none
+ * Returns: one byte; true (locked) or false (unlocked)
+ */
+ if (input_size != 0) {
+ *response_size = 0;
+ return VENDOR_RC_BOGUS_ARGS;
}
- ccprintf("go!\n");
- return start_the_dance();
+ buffer[0] = console_is_restricted() ? 0x01 : 0x00;
+ *response_size = 1;
+ return VENDOR_RC_SUCCESS;
}
-out:
- ccprintf("The restricted console lock is %s\n",
- console_is_restricted() ? "enabled" : "disabled");
-
- return EC_SUCCESS;
+ /* I have no idea what you're talking about */
+ *response_size = 0;
+ return VENDOR_RC_NO_SUCH_COMMAND;
}
-DECLARE_SAFE_CONSOLE_COMMAND(lock, command_lock,
- "[<BOOLEAN>]",
- "Get/Set the restricted console lock");
-
-#endif /* CONFIG_RESTRICTED_CONSOLE_COMMANDS */
+DECLARE_VENDOR_COMMAND(VENDOR_CC_GET_LOCK, vc_lock);
+
+/*
+ * TODO(rspangler): The old concept of 'lock the console' really meant
+ * something closer to 'reset CCD config', not the CCD V1 meaning of 'ccdlock'.
+ * This command is no longer supported, so will fail. It was defined this
+ * way:
+ *
+ * DECLARE_VENDOR_COMMAND(VENDOR_CC_SET_LOCK, vc_lock);
+ */
diff --git a/board/cr50/wp.h b/board/cr50/wp.h
new file mode 100644
index 0000000000..6d93145ba6
--- /dev/null
+++ b/board/cr50/wp.h
@@ -0,0 +1,24 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __EC_BOARD_CR50_WP_H
+#define __EC_BOARD_CR50_WP_H
+
+#include "common.h"
+
+/**
+ * Initialize write protect state.
+ *
+ * Must be called after case-closed debugging is initialized.
+ */
+void init_wp_state(void);
+
+/**
+ * Read the FWMP value from TPM NVMEM and set the console restriction
+ * appropriately.
+ */
+void read_fwmp(void);
+
+#endif /* ! __EC_BOARD_CR50_WP_H */
diff --git a/board/reef/board.h b/board/reef/board.h
index 9420e1caf1..48d7502762 100644
--- a/board/reef/board.h
+++ b/board/reef/board.h
@@ -254,9 +254,6 @@ enum reef_board_version {
BOARD_VERSION_COUNT,
};
-/* start as a sink in case we have no other power supply/battery */
-#define PD_DEFAULT_STATE PD_STATE_SNK_DISCONNECTED
-
/* TODO: determine the following board specific type-C power constants */
/* FIXME(dhendrix): verify all of the below PD_* numbers */
/*
diff --git a/chip/g/alerts.c b/chip/g/alerts.c
new file mode 100644
index 0000000000..6cfb936b9c
--- /dev/null
+++ b/chip/g/alerts.c
@@ -0,0 +1,364 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "board_id.h"
+#include "common.h"
+#include "console.h"
+#include "endian.h"
+#include "extension.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "registers.h"
+#include "signed_header.h"
+#include "task.h"
+#include "tpm_vendor_cmds.h"
+
+#define BROM_FWBIT_APPLYSEC_SC300 0
+#define BROM_FWBIT_APPLYSEC_CAMO 1
+#define BROM_FWBIT_APPLYSEC_BUSERR 2
+#define BROM_FWBIT_APPLYSEC_BUSOBF 3
+#define BROM_FWBIT_APPLYSEC_HEARTBEAT 4
+#define BROM_FWBIT_APPLYSEC_BATMON 5
+#define BROM_FWBIT_APPLYSEC_RTCCHECK 6
+#define BROM_FWBIT_APPLYSEC_JITTERY 7
+#define BROM_FWBIT_APPLYSEC_TRNG 8
+#define BROM_FWBIT_APPLYSEC_VOLT 9
+#define BROM_FWBIT_APPLYSEC_NOB5 10
+#define BROM_FWBIT_APPLYSEC_UNKNOWN 11
+
+struct alert_desc {
+ const char *name;
+ const uint8_t fuse; // BROM_FWBIT_APPLYSEC_* fuse that gates the alert
+};
+
+// These numbers correspond to index at 'alert_counters/alert_descs' arrays
+#define ALERT_NUM_CAMO0_BREACH 0
+#define ALERT_NUM_CRYPTO0_DMEM_PARITY 1
+#define ALERT_NUM_CRYPTO0_DRF_PARITY 2
+#define ALERT_NUM_CRYPTO0_IMEM_PARITY 3
+#define ALERT_NUM_CRYPTO0_PGM_FAULT 4
+#define ALERT_NUM_DBCTRL_CPU0_D_IF_BUS_ERR 5
+#define ALERT_NUM_DBCTRL_CPU0_D_IF_UPDATE_WATCHDOG 6
+#define ALERT_NUM_DBCTRL_CPU0_I_IF_BUS_ERR 7
+#define ALERT_NUM_DBCTRL_CPU0_I_IF_UPDATE_WATCHDOG 8
+#define ALERT_NUM_DBCTRL_CPU0_S_IF_BUS_ERR 9
+#define ALERT_NUM_DBCTRL_CPU0_S_IF_UPDATE_WATCHDOG 10
+#define ALERT_NUM_DBCTRL_DDMA0_IF_BUS_ERR 11
+#define ALERT_NUM_DBCTRL_DDMA0_IF_UPDATE_WATCHDOG 12
+#define ALERT_NUM_DBCTRL_DSPS0_IF_BUS_ERR 13
+#define ALERT_NUM_DBCTRL_DSPS0_IF_UPDATE_WATCHDOG 14
+#define ALERT_NUM_DBCTRL_DUSB0_IF_BUS_ERR 15
+#define ALERT_NUM_DBCTRL_DUSB0_IF_UPDATE_WATCHDOG 16
+#define ALERT_NUM_FUSE0_FUSE_DEFAULTS 17
+#define ALERT_NUM_GLOBALSEC_DIFF_FAIL 18
+#define ALERT_NUM_GLOBALSEC_FW0 19
+#define ALERT_NUM_GLOBALSEC_FW1 20
+#define ALERT_NUM_GLOBALSEC_FW2 21
+#define ALERT_NUM_GLOBALSEC_FW3 22
+#define ALERT_NUM_GLOBALSEC_HEARTBEAT_FAIL 23
+#define ALERT_NUM_GLOBALSEC_PROC_OPCODE_HASH 24
+#define ALERT_NUM_GLOBALSEC_SRAM_PARITY_SCRUB 25
+#define ALERT_NUM_KEYMGR0_AES_EXEC_CTR_MAX 26
+#define ALERT_NUM_KEYMGR0_AES_HKEY 27
+#define ALERT_NUM_KEYMGR0_CERT_LOOKUP 28
+#define ALERT_NUM_KEYMGR0_FLASH_ENTRY 29
+#define ALERT_NUM_KEYMGR0_PW 30
+#define ALERT_NUM_KEYMGR0_SHA_EXEC_CTR_MAX 31
+#define ALERT_NUM_KEYMGR0_SHA_FAULT 32
+#define ALERT_NUM_KEYMGR0_SHA_HKEY 33
+#define ALERT_NUM_PMU_BATTERY_MON 34
+#define ALERT_NUM_PMU_PMU_WDOG 35
+#define ALERT_NUM_RTC0_RTC_DEAD 36
+#define ALERT_NUM_TEMP0_MAX_TEMP 37
+#define ALERT_NUM_TEMP0_MAX_TEMP_DIFF 38
+#define ALERT_NUM_TEMP0_MIN_TEMP 39
+#define ALERT_NUM_TRNG0_OUT_OF_SPEC 40
+#define ALERT_NUM_TRNG0_TIMEOUT 41
+#define ALERT_NUM_VOLT0_VOLT_ERR 42
+#define ALERT_NUM_XO0_JITTERY_TRIM_DIS 43
+
+#define ALERTS_NUM 44
+
+uint16_t alert_counters[ALERTS_NUM];
+
+static void alerts_init(void)
+{
+ int irq;
+
+ // enable every single IRQ for globalsec alerts
+ for (irq = GC_IRQNUM_GLOBALSEC_CAMO0_BREACH_ALERT_INT;
+ irq <= GC_IRQNUM_GLOBALSEC_XO0_JITTERY_TRIM_DIS_ALERT_INT;
+ irq++) {
+ task_enable_irq(irq);
+ }
+}
+DECLARE_HOOK(HOOK_INIT, alerts_init, HOOK_PRIO_DEFAULT);
+
+volatile uint32_t *INTR_STATUS_ADDR[] = {
+ GREG32_ADDR(GLOBALSEC, ALERT_INTR_STS0),
+ GREG32_ADDR(GLOBALSEC, ALERT_INTR_STS1),
+};
+BUILD_ASSERT(ARRAY_SIZE(INTR_STATUS_ADDR) * 32 >= ALERTS_NUM);
+
+static void alert_intr_clear(int alert)
+{
+ int reg = alert / 32;
+ int offset = alert % 32;
+
+ *INTR_STATUS_ADDR[reg] = 1 << offset;
+}
+
+static void alert_interrupt_process(int alert)
+{
+ alert_counters[alert]++;
+ alert_intr_clear(alert);
+}
+
+#define GLOBALSEC_ALERT_COUNTER(name) \
+ DECLARE_IRQ(GC_IRQNUM_GLOBALSEC_##name##_ALERT_INT, handler_##name, 1); \
+ void handler_##name(void) \
+ { \
+ alert_interrupt_process(ALERT_NUM_##name); \
+ }
+
+GLOBALSEC_ALERT_COUNTER(CAMO0_BREACH);
+GLOBALSEC_ALERT_COUNTER(CRYPTO0_DMEM_PARITY);
+GLOBALSEC_ALERT_COUNTER(CRYPTO0_DRF_PARITY);
+GLOBALSEC_ALERT_COUNTER(CRYPTO0_IMEM_PARITY);
+GLOBALSEC_ALERT_COUNTER(CRYPTO0_PGM_FAULT);
+GLOBALSEC_ALERT_COUNTER(DBCTRL_CPU0_D_IF_BUS_ERR);
+GLOBALSEC_ALERT_COUNTER(DBCTRL_CPU0_D_IF_UPDATE_WATCHDOG);
+GLOBALSEC_ALERT_COUNTER(DBCTRL_CPU0_I_IF_BUS_ERR);
+GLOBALSEC_ALERT_COUNTER(DBCTRL_CPU0_I_IF_UPDATE_WATCHDOG);
+GLOBALSEC_ALERT_COUNTER(DBCTRL_CPU0_S_IF_BUS_ERR);
+GLOBALSEC_ALERT_COUNTER(DBCTRL_CPU0_S_IF_UPDATE_WATCHDOG);
+GLOBALSEC_ALERT_COUNTER(DBCTRL_DDMA0_IF_BUS_ERR);
+GLOBALSEC_ALERT_COUNTER(DBCTRL_DDMA0_IF_UPDATE_WATCHDOG);
+GLOBALSEC_ALERT_COUNTER(DBCTRL_DSPS0_IF_BUS_ERR);
+GLOBALSEC_ALERT_COUNTER(DBCTRL_DSPS0_IF_UPDATE_WATCHDOG);
+GLOBALSEC_ALERT_COUNTER(DBCTRL_DUSB0_IF_BUS_ERR);
+GLOBALSEC_ALERT_COUNTER(DBCTRL_DUSB0_IF_UPDATE_WATCHDOG);
+GLOBALSEC_ALERT_COUNTER(FUSE0_FUSE_DEFAULTS);
+GLOBALSEC_ALERT_COUNTER(GLOBALSEC_DIFF_FAIL);
+GLOBALSEC_ALERT_COUNTER(GLOBALSEC_FW0);
+GLOBALSEC_ALERT_COUNTER(GLOBALSEC_FW1);
+GLOBALSEC_ALERT_COUNTER(GLOBALSEC_FW2);
+GLOBALSEC_ALERT_COUNTER(GLOBALSEC_FW3);
+GLOBALSEC_ALERT_COUNTER(GLOBALSEC_HEARTBEAT_FAIL);
+GLOBALSEC_ALERT_COUNTER(GLOBALSEC_PROC_OPCODE_HASH);
+GLOBALSEC_ALERT_COUNTER(GLOBALSEC_SRAM_PARITY_SCRUB);
+GLOBALSEC_ALERT_COUNTER(KEYMGR0_AES_EXEC_CTR_MAX);
+GLOBALSEC_ALERT_COUNTER(KEYMGR0_AES_HKEY);
+GLOBALSEC_ALERT_COUNTER(KEYMGR0_CERT_LOOKUP);
+GLOBALSEC_ALERT_COUNTER(KEYMGR0_FLASH_ENTRY);
+GLOBALSEC_ALERT_COUNTER(KEYMGR0_PW);
+GLOBALSEC_ALERT_COUNTER(KEYMGR0_SHA_EXEC_CTR_MAX);
+GLOBALSEC_ALERT_COUNTER(KEYMGR0_SHA_FAULT);
+GLOBALSEC_ALERT_COUNTER(KEYMGR0_SHA_HKEY);
+GLOBALSEC_ALERT_COUNTER(PMU_BATTERY_MON);
+GLOBALSEC_ALERT_COUNTER(PMU_PMU_WDOG);
+GLOBALSEC_ALERT_COUNTER(RTC0_RTC_DEAD);
+GLOBALSEC_ALERT_COUNTER(TEMP0_MAX_TEMP);
+GLOBALSEC_ALERT_COUNTER(TEMP0_MAX_TEMP_DIFF);
+GLOBALSEC_ALERT_COUNTER(TEMP0_MIN_TEMP);
+GLOBALSEC_ALERT_COUNTER(TRNG0_OUT_OF_SPEC);
+GLOBALSEC_ALERT_COUNTER(TRNG0_TIMEOUT);
+GLOBALSEC_ALERT_COUNTER(VOLT0_VOLT_ERR);
+GLOBALSEC_ALERT_COUNTER(XO0_JITTERY_TRIM_DIS);
+
+#define ALERTS_FORMAT_HAVEN 1
+
+struct vc_alerts_data {
+ uint16_t version_id;
+ uint16_t alerts_num;
+ uint16_t counters[ALERTS_NUM];
+} __packed;
+
+static enum vendor_cmd_rc vc_get_alerts_data(enum vendor_cmd_cc code,
+ void *buf, size_t input_size, size_t *response_size)
+{
+ int i;
+ struct vc_alerts_data *resp = buf;
+
+ if (sizeof(struct vc_alerts_data) > *response_size)
+ return VENDOR_RC_RESPONSE_TOO_BIG;
+
+ memset(resp, 0, sizeof(struct vc_alerts_data));
+ resp->version_id = htobe16(ALERTS_FORMAT_HAVEN);
+ resp->alerts_num = htobe16(ALERTS_NUM);
+ for (i = 0; i < ALERTS_NUM; i++) {
+ // Most of alert_counters[i] will be zero. We want to avoid
+ // disabling IRQ thus check counters with IRQ enabled.
+ if (alert_counters[i]) {
+ interrupt_disable();
+ resp->counters[i] = htobe16(alert_counters[i]);
+ alert_counters[i] = 0;
+ interrupt_enable();
+ }
+ }
+
+ *response_size = sizeof(struct vc_alerts_data);
+
+ return VENDOR_RC_SUCCESS;
+}
+DECLARE_VENDOR_COMMAND(VENDOR_CC_GET_ALERTS_DATA, vc_get_alerts_data);
+
+#ifdef CONFIG_ENABLE_H1_ALERTS_CONSOLE
+
+const struct alert_desc alert_descs[] = {
+ { "camo0/breach", BROM_FWBIT_APPLYSEC_CAMO },
+ { "crypto0/dmem_parity", BROM_FWBIT_APPLYSEC_UNKNOWN },
+ { "crypto0/drf_parity", BROM_FWBIT_APPLYSEC_UNKNOWN },
+ { "crypto0/imem_parity", BROM_FWBIT_APPLYSEC_UNKNOWN },
+ { "crypto0/pgm_fault", BROM_FWBIT_APPLYSEC_UNKNOWN },
+ { "dbctrl_cpu0_D_if/bus_err", BROM_FWBIT_APPLYSEC_BUSERR },
+ { "dbctrl_cpu0_D_if/update_watchdog", BROM_FWBIT_APPLYSEC_BUSOBF },
+ { "dbctrl_cpu0_I_if/bus_err", BROM_FWBIT_APPLYSEC_BUSERR },
+ { "dbctrl_cpu0_I_if/update_watchdog", BROM_FWBIT_APPLYSEC_BUSOBF },
+ { "dbctrl_cpu0_S_if/bus_err", BROM_FWBIT_APPLYSEC_BUSERR },
+ { "dbctrl_cpu0_S_if/update_watchdog", BROM_FWBIT_APPLYSEC_BUSOBF },
+ { "dbctrl_ddma0_if/bus_err", BROM_FWBIT_APPLYSEC_BUSERR },
+ { "dbctrl_ddma0_if/update_watchdog", BROM_FWBIT_APPLYSEC_BUSOBF },
+ { "dbctrl_dsps0_if/bus_err", BROM_FWBIT_APPLYSEC_BUSERR },
+ { "dbctrl_dsps0_if/update_watchdog", BROM_FWBIT_APPLYSEC_BUSOBF },
+ { "dbctrl_dusb0_if/bus_err", BROM_FWBIT_APPLYSEC_BUSERR },
+ { "dbctrl_dusb0_if/update_watchdog", BROM_FWBIT_APPLYSEC_BUSOBF },
+ { "fuse0/fuse_defaults", BROM_FWBIT_APPLYSEC_UNKNOWN },
+ { "globalsec/diff_fail", BROM_FWBIT_APPLYSEC_HEARTBEAT },
+ { "globalsec/fw0", BROM_FWBIT_APPLYSEC_UNKNOWN },
+ { "globalsec/fw1", BROM_FWBIT_APPLYSEC_UNKNOWN },
+ { "globalsec/fw2", BROM_FWBIT_APPLYSEC_UNKNOWN },
+ { "globalsec/fw3", BROM_FWBIT_APPLYSEC_UNKNOWN },
+ { "globalsec/heartbeat_fail", BROM_FWBIT_APPLYSEC_HEARTBEAT },
+ { "globalsec/proc_opcode_hash", BROM_FWBIT_APPLYSEC_UNKNOWN },
+ { "globalsec/sram_parity_scrub", BROM_FWBIT_APPLYSEC_UNKNOWN },
+ { "keymgr0/aes_exec_ctr_max", BROM_FWBIT_APPLYSEC_UNKNOWN },
+ { "keymgr0/aes_hkey", BROM_FWBIT_APPLYSEC_UNKNOWN },
+ { "keymgr0/cert_lookup", BROM_FWBIT_APPLYSEC_UNKNOWN },
+ { "keymgr0/flash_entry", BROM_FWBIT_APPLYSEC_UNKNOWN },
+ { "keymgr0/pw", BROM_FWBIT_APPLYSEC_UNKNOWN },
+ { "keymgr0/sha_exec_ctr_max", BROM_FWBIT_APPLYSEC_UNKNOWN },
+ { "keymgr0/sha_fault", BROM_FWBIT_APPLYSEC_UNKNOWN },
+ { "keymgr0/sha_hkey", BROM_FWBIT_APPLYSEC_UNKNOWN },
+ { "pmu/battery_mon", BROM_FWBIT_APPLYSEC_BATMON },
+ { "pmu/pmu_wdog", BROM_FWBIT_APPLYSEC_HEARTBEAT },
+ { "rtc0/rtc_dead", BROM_FWBIT_APPLYSEC_RTCCHECK },
+ { "temp0/max_temp", BROM_FWBIT_APPLYSEC_UNKNOWN },
+ { "temp0/max_temp_diff", BROM_FWBIT_APPLYSEC_UNKNOWN },
+ { "temp0/min_temp", BROM_FWBIT_APPLYSEC_UNKNOWN },
+ { "trng0/out_of_spec", BROM_FWBIT_APPLYSEC_TRNG },
+ { "trng0/timeout", BROM_FWBIT_APPLYSEC_TRNG },
+ { "volt0/volt_err", BROM_FWBIT_APPLYSEC_VOLT },
+ { "xo0/jittery_trim_dis", BROM_FWBIT_APPLYSEC_JITTERY },
+};
+BUILD_ASSERT(ARRAY_SIZE(alert_descs) == ALERTS_NUM);
+
+static int alert_intr_status(int alert)
+{
+ int reg = alert / 32;
+ int offset = alert % 32;
+
+ return !!(*INTR_STATUS_ADDR[reg] & (1 << offset));
+}
+
+#ifdef CONFIG_BOARD_ID_SUPPORT
+static uint32_t fuse_enabled(void)
+{
+ uint32_t fuses = GR_FUSE(FW_DEFINED_BROM_APPLYSEC);
+ // get_current_image_header() is defined in board_id.c and available
+ // only when CONFIG_BOARD_ID_SUPPORT is enabled
+ const struct SignedHeader *hdr = get_current_image_header();
+
+ return fuses & hdr->applysec_;
+}
+#else /* CONFIG_BOARD_ID_SUPPORT */
+static uint32_t fuse_enabled(void)
+{
+ return GR_FUSE(FW_DEFINED_BROM_APPLYSEC);
+}
+#endif /* CONFIG_BOARD_ID_SUPPORT */
+
+static void command_alerts_list(void)
+{
+ int i;
+ uint32_t fuses = fuse_enabled();
+
+ ccprintf("Globalsec alerts status\nColumns:\n"
+ " * name\n"
+ " * fuse state: '?' - not defined, '#' disabled, '+' enabled\n"
+ " * interrupt state\n"
+ " * alert counter\n");
+
+ for (i = 0; i < ALERTS_NUM; i++) {
+ const char *name = alert_descs[i].name;
+ char fuse_status;
+
+ int status = alert_intr_status(i);
+ int8_t fuse = alert_descs[i].fuse;
+
+ if (fuse == BROM_FWBIT_APPLYSEC_UNKNOWN)
+ fuse_status = '?';
+ else if (fuses & (1 << fuse))
+ fuse_status = '+';
+ else
+ fuse_status = '#';
+
+ ccprintf("%32s %c %d %d\n", name, fuse_status, status,
+ alert_counters[i]);
+ cflush();
+ }
+}
+
+/* Fire a software enabled alert */
+static void command_alerts_fire(int interrupt)
+{
+ int i = 0;
+ int value = 0;
+
+ for (i = 3; i >= 0; i--) {
+ /* Trigger register consists of four 2-bit fields.
+ * pair 01 triggers the alerts, pair 10 does not trigger
+ */
+ value <<= 2;
+ value |= (i == interrupt) ? 1 : 2;
+ }
+ GWRITE(GLOBALSEC, ALERT_FW_TRIGGER, value); // firing FW-N irq
+ GWRITE(GLOBALSEC, ALERT_FW_TRIGGER, 0xaa); // back to normal
+}
+
+static int command_alerts(int argc, char **argv)
+{
+ char *e;
+
+ if (argc == 1) {
+ command_alerts_list();
+ return EC_SUCCESS;
+ }
+
+ if (argc == 3) {
+ if (!strcasecmp(argv[1], "fire")) {
+ int alert = strtoi(argv[2], &e, 10);
+
+ if (*e || alert < 0 || alert > 3) {
+ ccprintf("interrupt number must be in range "
+ "[0..3]\n");
+ return EC_ERROR_PARAM2;
+ }
+
+ command_alerts_fire(alert);
+ return EC_SUCCESS;
+ }
+
+ return EC_ERROR_PARAM1;
+ }
+
+ return EC_ERROR_PARAM_COUNT;
+}
+
+DECLARE_CONSOLE_COMMAND(alerts, command_alerts,
+ "<|fire [INT]>",
+ "View/change alerts status");
+
+#endif /* CONFIG_ENABLE_H1_ALERTS_CONSOLE */
diff --git a/chip/g/board_id.c b/chip/g/board_id.c
new file mode 100644
index 0000000000..569540eb62
--- /dev/null
+++ b/chip/g/board_id.c
@@ -0,0 +1,271 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "common.h"
+#include "board_id.h"
+#include "endian.h"
+#include "extension.h"
+#include "flash_info.h"
+#include "system.h"
+#include "util.h"
+
+#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args)
+#define CPRINTF(format, args...) cprintf(CC_SYSTEM, format, ## args)
+
+/**
+ * Return the image header for the current image copy
+ */
+const struct SignedHeader *get_current_image_header(void)
+{
+ return (const struct SignedHeader *)
+ get_program_memory_addr(system_get_image_copy());
+}
+
+uint32_t check_board_id_vs_header(const struct board_id *id,
+ const struct SignedHeader *h)
+{
+ uint32_t mismatch;
+ uint32_t header_board_id_type;
+ uint32_t header_board_id_mask;
+ uint32_t header_board_id_flags;
+
+ /* Blank Board ID matches all headers */
+ if (~(id->type & id->type_inv & id->flags) == 0)
+ return 0;
+
+ header_board_id_type = SIGNED_HEADER_PADDING ^ h->board_id_type;
+ header_board_id_mask = SIGNED_HEADER_PADDING ^ h->board_id_type_mask;
+ header_board_id_flags = SIGNED_HEADER_PADDING ^ h->board_id_flags;
+
+ /*
+ * Masked bits in header Board ID type must match type and inverse from
+ * flash.
+ */
+ mismatch = header_board_id_type ^ id->type;
+ mismatch |= header_board_id_type ^ ~id->type_inv;
+ mismatch &= header_board_id_mask;
+
+ /*
+ * All 1-bits in header Board ID flags must be present in flags from
+ * flash
+ */
+ mismatch |=
+ ((header_board_id_flags & id->flags) != header_board_id_flags);
+
+ return mismatch;
+}
+
+int read_board_id(struct board_id *id)
+{
+ uint32_t *id_p;
+ int i;
+
+ /*
+ * Board ID structure size is guaranteed to be divisible by 4, and it
+ * is guaranteed to be aligned at 4 bytes.
+ */
+
+ id_p = (uint32_t *)id;
+
+ /* Make sure INFO1 board ID space is readable */
+ if (flash_info_read_enable(INFO_BOARD_SPACE_OFFSET,
+ INFO_BOARD_SPACE_PROTECT_SIZE) !=
+ EC_SUCCESS) {
+ CPRINTS("%s: failed to enable read access to info", __func__);
+ return EC_ERROR_ACCESS_DENIED;
+ }
+
+ for (i = 0; i < sizeof(*id); i += sizeof(uint32_t)) {
+ int rv;
+
+ rv = flash_physical_info_read_word
+ (INFO_BOARD_SPACE_OFFSET +
+ offsetof(struct info1_board_space, bid) + i,
+ id_p);
+ if (rv != EC_SUCCESS) {
+ CPRINTF("%s: failed to read word %d, error %d\n",
+ __func__, i, rv);
+ return rv;
+ }
+ id_p++;
+ }
+ return EC_SUCCESS;
+}
+
+uint32_t board_id_mismatch(const struct SignedHeader *sh)
+{
+ struct board_id id;
+
+ if (!sh)
+ /* Get header of the currently running image. */
+ sh = get_current_image_header();
+
+ /* Get Board ID from INFO1. */
+ if (read_board_id(&id) != EC_SUCCESS) {
+ /*
+ * On failure, set id fields to 0. This will only match an
+ * unrestricted image header (board_id_mask=board_id_flags=0),
+ * which would run on any Board ID.
+ *
+ * Don't return error, because that would prevent all images
+ * from running.
+ */
+ id.type = id.type_inv = id.flags = 0;
+ }
+
+ return check_board_id_vs_header(&id, sh);
+}
+
+/**
+ * Write board ID into the flash INFO1 space.
+ *
+ * @param id Pointer to a Board ID structure to copy into INFO1
+ *
+ * @return EC_SUCCESS or an error code in cases of various failures to read or
+ * if the space has been already initialized.
+ */
+static int write_board_id(const struct board_id *id)
+{
+ struct board_id id_test;
+ uint32_t rv;
+
+ /*
+ * Make sure the current header will still validate against the
+ * proposed values. If it doesn't, then programming these values
+ * would cause the next boot to fail.
+ */
+ if (check_board_id_vs_header(id, get_current_image_header()) != 0) {
+ CPRINTS("%s: Board ID wouldn't allow current header", __func__);
+ return EC_ERROR_INVAL;
+ }
+
+ /* Fail if Board ID is already programmed */
+ rv = read_board_id(&id_test);
+ if (rv != EC_SUCCESS) {
+ CPRINTS("%s: error reading Board ID", __func__);
+ return rv;
+ }
+
+ if (~(id_test.type & id_test.type_inv & id_test.flags) != 0) {
+ CPRINTS("%s: Board ID already programmed", __func__);
+ return EC_ERROR_ACCESS_DENIED;
+ }
+
+ /* Enable write access */
+ if (flash_info_write_enable(INFO_BOARD_SPACE_OFFSET,
+ INFO_BOARD_SPACE_PROTECT_SIZE) !=
+ EC_SUCCESS) {
+ CPRINTS("%s: failed to enable write access", __func__);
+ return EC_ERROR_ACCESS_DENIED;
+ }
+
+ /* Write Board ID */
+ rv = flash_info_physical_write(INFO_BOARD_SPACE_OFFSET +
+ offsetof(struct info1_board_space, bid),
+ sizeof(*id), (const char *)id);
+ if (rv != EC_SUCCESS)
+ CPRINTS("%s: write failed", __func__);
+
+ /* Disable write access */
+ flash_info_write_disable();
+
+ return rv;
+}
+
+static enum vendor_cmd_rc vc_set_board_id(enum vendor_cmd_cc code,
+ void *buf,
+ size_t input_size,
+ size_t *response_size)
+{
+ struct board_id id;
+ uint8_t *pbuf = buf;
+
+ *response_size = 1;
+
+ /* Exactly two fields are expected. */
+ if (input_size != sizeof(id.type) + sizeof(id.flags)) {
+ *pbuf = VENDOR_RC_BOGUS_ARGS;
+ return VENDOR_RC_BOGUS_ARGS;
+ }
+
+ memcpy(&id.type, pbuf, sizeof(id.type));
+ id.type = be32toh(id.type);
+ id.type_inv = ~id.type;
+
+ memcpy(&id.flags, pbuf + sizeof(id.type), sizeof(id.flags));
+ id.flags = be32toh(id.flags);
+
+ /* We care about the LSB only. */
+ *pbuf = write_board_id(&id);
+
+ return *pbuf;
+}
+DECLARE_VENDOR_COMMAND(VENDOR_CC_SET_BOARD_ID, vc_set_board_id);
+
+static int command_board_id(int argc, char **argv)
+{
+ struct board_id id;
+ int rv = EC_ERROR_PARAM_COUNT;
+
+ if (argc == 1) {
+ rv = read_board_id(&id);
+
+ if (rv != EC_SUCCESS) {
+ ccprintf("Failed to read board ID space\n");
+ return rv;
+ }
+ ccprintf("Board ID: %08x, flags %08x\n", id.type, id.flags);
+
+ if ((~id.type | ~id.type_inv | ~id.flags) == 0)
+ return rv; /* The space is not initialized. */
+
+ if (id.type != ~id.type_inv)
+ ccprintf("Inv Type Mismatch (%08x instead of %08x)!\n",
+ id.type_inv, ~id.type);
+ }
+#ifdef CR50_DEV
+ else if (argc == 3) {
+ char *e;
+
+ id.type = strtoi(argv[1], &e, 0);
+ if (*e)
+ return EC_ERROR_PARAM1;
+ id.type_inv = ~id.type;
+ id.flags = strtoi(argv[2], &e, 0);
+ if (*e)
+ return EC_ERROR_PARAM2;
+
+ rv = write_board_id(&id);
+ } else {
+ ccprintf("specify board type and flags\n");
+ rv = EC_ERROR_PARAM_COUNT;
+ }
+#endif
+ return rv;
+}
+DECLARE_SAFE_CONSOLE_COMMAND(bid,
+ command_board_id, NULL, "Set/Get Board ID");
+
+static enum vendor_cmd_rc vc_get_board_id(enum vendor_cmd_cc code,
+ void *buf,
+ size_t input_size,
+ size_t *response_size)
+{
+ struct board_id id;
+
+ if (read_board_id(&id))
+ return VENDOR_RC_READ_FLASH_FAIL;
+
+ /* Convert to line representation. */
+ id.type = htobe32(id.type);
+ id.type_inv = htobe32(id.type_inv);
+ id.flags = htobe32(id.flags);
+
+ memcpy(buf, &id, sizeof(id));
+ *response_size = sizeof(id);
+
+ return VENDOR_RC_SUCCESS;
+}
+DECLARE_VENDOR_COMMAND(VENDOR_CC_GET_BOARD_ID, vc_get_board_id);
diff --git a/chip/g/board_id.h b/chip/g/board_id.h
new file mode 100644
index 0000000000..dda2302c14
--- /dev/null
+++ b/chip/g/board_id.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __EC_CHIP_G_BOARD_ID_H
+#define __EC_CHIP_G_BOARD_ID_H
+
+#include "common.h"
+#include "signed_header.h"
+#include "util.h"
+
+/* Structure holding Board ID */
+struct board_id {
+ uint32_t type; /* Board type */
+ uint32_t type_inv; /* Board type (inverted) */
+ uint32_t flags; /* Flags */
+};
+
+/* Info1 Board space contents. */
+struct info1_board_space {
+ struct board_id bid;
+};
+
+#define INFO_BOARD_ID_SIZE sizeof(struct board_id)
+#define INFO_BOARD_SPACE_PROTECT_SIZE 16
+
+/**
+ * Check the current header vs. the supplied Board ID
+ *
+ * @param board_id Pointer to a Board ID structure to check
+ * @param h Pointer to the currently running image's header
+ *
+ * @return 0 if no mismatch, non-zero if mismatch
+ */
+uint32_t check_board_id_vs_header(const struct board_id *id,
+ const struct SignedHeader *h);
+
+/**
+ * Check board ID from the flash INFO1 space.
+ *
+ * @param id Pointer to a Board ID structure to fill
+ *
+ * @return EC_SUCCESS of an error code in cases of vairous failures to read.
+ */
+int read_board_id(struct board_id *id);
+
+/**
+ * Return the image header for the current image copy
+ */
+const struct SignedHeader *get_current_image_header(void);
+
+/**
+ * Check if board ID in the image matches board ID field in the INFO1.
+ *
+ * Pass the pointer to the image header to check. If the pointer is set to
+ * NULL, check board ID against the currently running image's header.
+ *
+ * Return true if there is a mismatch (the code should not run).
+ */
+uint32_t board_id_mismatch(const struct SignedHeader *h);
+
+BUILD_ASSERT((offsetof(struct info1_board_space, bid) & 3) == 0);
+BUILD_ASSERT((INFO_BOARD_ID_SIZE & 3) == 0);
+BUILD_ASSERT(sizeof(struct info1_board_space) <= INFO_BOARD_SPACE_PROTECT_SIZE);
+
+#endif /* ! __EC_CHIP_G_BOARD_ID_H */
diff --git a/chip/g/build.mk b/chip/g/build.mk
index 184184abea..c2a5bd360c 100644
--- a/chip/g/build.mk
+++ b/chip/g/build.mk
@@ -20,26 +20,45 @@ CPPFLAGS += -I$(CRYPTOCLIB)/include
endif
# Required chip modules
-chip-y=clock.o gpio.o hwtimer.o jtag.o system.o
+chip-y = clock.o gpio.o hwtimer.o pre_init.o system.o
+chip-$(CONFIG_BOARD_ID_SUPPORT) += board_id.o
ifeq ($(CONFIG_POLLING_UART),y)
chip-y += polling_uart.o
else
chip-y += uart.o
chip-y += uartn.o
-endif
+chip-$(CONFIG_UART_BITBANG)+= uart_bitbang.o
+endif # undef CONFIG_POLLING_UART
+
+chip-$(CONFIG_DCRYPTO)+= crypto_api.o
chip-$(CONFIG_DCRYPTO)+= dcrypto/aes.o
+chip-$(CONFIG_DCRYPTO)+= dcrypto/app_cipher.o
+chip-$(CONFIG_DCRYPTO)+= dcrypto/app_key.o
chip-$(CONFIG_DCRYPTO)+= dcrypto/bn.o
-chip-$(CONFIG_DCRYPTO)+= dcrypto/bn_hw.o
+chip-$(CONFIG_DCRYPTO)+= dcrypto/dcrypto_bn.o
+chip-$(CONFIG_DCRYPTO)+= dcrypto/dcrypto_p256.o
+chip-$(CONFIG_DCRYPTO)+= dcrypto/compare.o
chip-$(CONFIG_DCRYPTO)+= dcrypto/dcrypto_runtime.o
+chip-$(CONFIG_DCRYPTO)+= dcrypto/drbg_rfc6979.o
+chip-$(CONFIG_DCRYPTO)+= dcrypto/gcm.o
chip-$(CONFIG_DCRYPTO)+= dcrypto/hkdf.o
chip-$(CONFIG_DCRYPTO)+= dcrypto/hmac.o
+chip-$(CONFIG_DCRYPTO)+= dcrypto/key_ladder.o
chip-$(CONFIG_DCRYPTO)+= dcrypto/p256.o
chip-$(CONFIG_DCRYPTO)+= dcrypto/p256_ec.o
chip-$(CONFIG_DCRYPTO)+= dcrypto/p256_ecies.o
chip-$(CONFIG_DCRYPTO)+= dcrypto/rsa.o
chip-$(CONFIG_DCRYPTO)+= dcrypto/sha1.o
chip-$(CONFIG_DCRYPTO)+= dcrypto/sha256.o
+ifeq ($(CONFIG_UPTO_SHA512),y)
+chip-$(CONFIG_DCRYPTO)+= dcrypto/sha384.o
+ifeq ($(CONFIG_DCRYPTO_SHA512),y)
+chip-$(CONFIG_DCRYPTO)+= dcrypto/dcrypto_sha512.o
+else
+chip-$(CONFIG_DCRYPTO)+= dcrypto/sha512.o
+endif
+endif
chip-$(CONFIG_DCRYPTO)+= dcrypto/x509.o
chip-$(CONFIG_SPI_MASTER)+=spi_master.o
@@ -47,26 +66,29 @@ chip-$(CONFIG_SPI_MASTER)+=spi_master.o
chip-y+= jitter.o
chip-y+= pmu.o
chip-y+= trng.o
+chip-y+= runlevel.o
+chip-$(CONFIG_ENABLE_H1_ALERTS)+= alerts.o
chip-$(CONFIG_USB_FW_UPDATE)+= usb_upgrade.o
-chip-$(CONFIG_NON_HC_FW_UPDATE)+= upgrade_fw.o
+chip-$(CONFIG_NON_HC_FW_UPDATE)+= upgrade_fw.o post_reset.o upgrade.o
chip-$(CONFIG_SPS)+= sps.o
chip-$(CONFIG_TPM_SPS)+=sps_tpm.o
chip-$(CONFIG_WATCHDOG)+=watchdog.o
chip-$(CONFIG_USB)+=usb.o usb_endpoints.o
chip-$(CONFIG_USB_CONSOLE)+=usb_console.o
-chip-$(CONFIG_USB_HID)+=usb_hid.o
+chip-$(CONFIG_USB_HID_KEYBOARD)+=usb_hid_keyboard.o
chip-$(CONFIG_USB_BLOB)+=blob.o
chip-$(CONFIG_USB_SPI)+=usb_spi.o
chip-$(CONFIG_RDD)+=rdd.o
chip-$(CONFIG_RBOX)+=rbox.o
chip-$(CONFIG_STREAM_USB)+=usb-stream.o
chip-$(CONFIG_STREAM_USART)+=usart.o
+chip-$(CONFIG_I2C_MASTER)+= i2cm.o
chip-$(CONFIG_I2C_SLAVE)+= i2cs.o
chip-$(CONFIG_LOW_POWER_IDLE)+=idle.o
-chip-$(CONFIG_FLASH)+=flash.o
+chip-$(CONFIG_FLASH_PHYSICAL) += flash.o
dirs-y += chip/g/dcrypto
ifneq ($(CONFIG_CUSTOMIZED_RO),)
@@ -102,20 +124,63 @@ $(out)/$(PROJECT).obj: $(out)/RW/ec.RW_B.flat
$(out)/RW/ec.RW_B.flat: $(out)/util/signer
endif
+ifneq ($(CR50_DEV),)
+CPPFLAGS += -DCR50_DEV=$(CR50_DEV)
+endif
+
+MANIFEST := util/signer/ec_RW-manifest-dev.json
CR50_RO_KEY ?= rom-testkey-A.pem
-ifeq ($(CR50_DEV),)
+ifeq ($(H1_DEVIDS),)
CR50_RW_KEY = loader-testkey-A.pem
SIGNER = $(out)/util/signer
SIGNER_EXTRAS =
+SIGNER_MANIFEST := $(MANIFEST)
else
-CFLAGS += -DCR50_DEV=1
-SIGNER = $(HOME)/bin/codesigner
+SIGNER = sudo $(HOME)/bin/codesigner
CR50_RW_KEY = cr50_rom0-dev-blsign.pem.pub
RW_SIGNER_EXTRAS = -x util/signer/fuses.xml
-RW_SIGNER_EXTRAS += -j util/signer/ec_RW-manifest-kevin_evt_1.json
-$(out)/RW/ec.RW_B.flat: $(out)/RW/ec.RW.flat
-$(out)/RW/ec.RW.flat $(out)/RW/ec.RW_B.flat: SIGNER_EXTRAS = $(RW_SIGNER_EXTRAS)
+
+ifneq ($(CHIP_MK_INCLUDED_ONCE),)
+#
+# When building a node locked cr50 image for an H1 device with prod RO, the
+# manifest needs to be modifed to include the device ID of the chip the image
+# is built for.
+#
+# The device ID consists of two 32 bit numbers which can be retrieved by
+# running the 'sysinfo' command on the cr50 console. These two numbers
+# need to be spliced into the signer manifest after the '"fuses": {' line
+# for the signer to pick them up. Pass the numbers on the make command line
+# like this:
+#
+# H1_DEVIDS='<num 1> <num 2>' make ...
+#
+ifeq ($(SIGNER_MANIFEST),)
+SIGNER_MANIFEST := $(shell mktemp /tmp/h1.signer.XXXXXX)
+endif
+ifneq ($(CR50_DEV),)
+
+#
+# When building a debug image, we don't want rollback protection to be in the
+# way - a debug image, which is guaranteed to be node locked should run on any
+# H1, whatever its info mask state is. The awk script below clears out the
+# info {} section of the manifest.
+#
+DUMMY := $(shell /usr/bin/awk 'BEGIN {skip = 0}; \
+ /^},/ {skip = 0}; \
+ {if (!skip) {print };} \
+ /\"info\": {/ {skip = 1};' $(MANIFEST) > $(SIGNER_MANIFEST))
+else
+DUMMY := $(shell /bin/cp $(MANIFEST) $(SIGNER_MANIFEST))
endif
+REPLACEMENT := $(shell printf \
+ '\\n \\"DEV_ID0\\": %s,\\n \\"DEV_ID1\\": %s,' $(H1_DEVIDS))
+NODE_JSON := $(shell sed -i \
+ "s/\"fuses\": {/\"fuses\": {$(REPLACEMENT)/" $(SIGNER_MANIFEST))
+
+RW_SIGNER_EXTRAS += -j $(SIGNER_MANIFEST)
+endif # CHIP_MK_INCLUDED_ONCE defined
+endif # H1_DEVIDS defined
+
# This file is included twice by the Makefile, once to determine the CHIP info
# # and then again after defining all the CONFIG_ and HAS_TASK variables. We use
@@ -125,6 +190,9 @@ ifeq ($(CHIP_MK_INCLUDED_ONCE),)
CHIP_MK_INCLUDED_ONCE=1
else
+$(out)/RW/ec.RW_B.flat: $(out)/RW/ec.RW.flat
+$(out)/RW/ec.RW.flat $(out)/RW/ec.RW_B.flat: SIGNER_EXTRAS = $(RW_SIGNER_EXTRAS)
+
ifeq ($(CONFIG_DCRYPTO),y)
$(out)/RW/ec.RW.elf $(out)/RW/ec.RW_B.elf: LDFLAGS_EXTRA += -L$(out)/cryptoc \
-lcryptoc
@@ -134,7 +202,7 @@ $(out)/RW/ec.RW.elf $(out)/RW/ec.RW_B.elf: $(out)/cryptoc/libcryptoc.a
.PHONY: $(out)/cryptoc/libcryptoc.a
$(out)/cryptoc/libcryptoc.a:
$(MAKE) obj=$(realpath $(out))/cryptoc SUPPORT_UNALIGNED=1 \
- -C $(CRYPTOCLIB)
+ CONFIG_UPTO_SHA512=$(CONFIG_UPTO_SHA512) -C $(CRYPTOCLIB)
endif # end CONFIG_DCRYPTO
endif # CHIP_MK_INCLUDED_ONCE is nonempty
diff --git a/chip/g/config_chip.h b/chip/g/config_chip.h
index ed909fc3e5..0e6e319007 100644
--- a/chip/g/config_chip.h
+++ b/chip/g/config_chip.h
@@ -46,7 +46,7 @@
#define CONFIG_STACK_SIZE 1024
/* Idle task stack size */
-#define IDLE_TASK_STACK_SIZE 256
+#define IDLE_TASK_STACK_SIZE 512
/* Default task stack size */
#define TASK_STACK_SIZE 488
@@ -67,6 +67,9 @@
/* We'll have some special commands of our own */
#define CONFIG_EXTENSION_COMMAND 0xbaccd00a
+/* Chip needs to do custom pre-init */
+#define CONFIG_CHIP_PRE_INIT
+
/*
* The flash memory is implemented in two halves. The SoC bootrom will look for
* the first-stage bootloader at the beginning of each of the two halves and
@@ -95,7 +98,7 @@
* use these two areas for the same thing, it's just more convenient to make
* them the same size.
*/
-#define CFG_TOP_SIZE 0x4000
+#define CFG_TOP_SIZE 0x3800
#define CFG_TOP_A_OFF (CFG_FLASH_HALF - CFG_TOP_SIZE)
#define CFG_TOP_B_OFF (CONFIG_FLASH_SIZE - CFG_TOP_SIZE)
@@ -140,4 +143,7 @@
*/
#define CONFIG_CUSTOMIZED_RO
+/* Number of I2C ports */
+#define I2C_PORT_COUNT 2
+
#endif /* __CROS_EC_CONFIG_CHIP_H */
diff --git a/chip/g/crypto_api.c b/chip/g/crypto_api.c
new file mode 100644
index 0000000000..267bb31eb6
--- /dev/null
+++ b/chip/g/crypto_api.c
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "crypto_api.h"
+#include "dcrypto.h"
+
+void app_compute_hash(uint8_t *p_buf, size_t num_bytes,
+ uint8_t *p_hash, size_t hash_len)
+{
+ uint8_t sha1_digest[SHA_DIGEST_SIZE];
+
+ /*
+ * Use the built in dcrypto engine to generate the sha1 hash of the
+ * buffer.
+ */
+ DCRYPTO_SHA1_hash((uint8_t *)p_buf, num_bytes, sha1_digest);
+
+ memcpy(p_hash, sha1_digest, MIN(hash_len, sizeof(sha1_digest)));
+
+ if (hash_len > sizeof(sha1_digest))
+ memset(p_hash + sizeof(sha1_digest), 0,
+ hash_len - sizeof(sha1_digest));
+}
+
+int app_cipher(const void *salt, void *out, const void *in, size_t size)
+{
+ return DCRYPTO_app_cipher(NVMEM, salt, out, in, size);
+}
diff --git a/chip/g/dcrypto/aes.c b/chip/g/dcrypto/aes.c
index c610745a0a..f5cc0e6d8f 100644
--- a/chip/g/dcrypto/aes.c
+++ b/chip/g/dcrypto/aes.c
@@ -16,6 +16,13 @@ static void set_control_register(
GWRITE_FIELD(KEYMGR, AES_CTRL, ENC_MODE, encrypt);
GWRITE_FIELD(KEYMGR, AES_CTRL, CTR_ENDIAN, CTRL_CTR_BIG_ENDIAN);
GWRITE_FIELD(KEYMGR, AES_CTRL, ENABLE, CTRL_ENABLE);
+
+ /* Turn off random nops (which are enabled by default). */
+ GWRITE_FIELD(KEYMGR, AES_RAND_STALL_CTL, STALL_EN, 0);
+ /* Configure random nop percentage at 25%. */
+ GWRITE_FIELD(KEYMGR, AES_RAND_STALL_CTL, FREQ, 1);
+ /* Now turn on random nops. */
+ GWRITE_FIELD(KEYMGR, AES_RAND_STALL_CTL, STALL_EN, 1);
}
static int wait_read_data(volatile uint32_t *addr)
@@ -140,7 +147,8 @@ int DCRYPTO_aes_ctr(uint8_t *out, const uint8_t *key, uint32_t key_bits,
inp = in;
outp = out;
}
- DCRYPTO_aes_block(inp, outp);
+ if (!DCRYPTO_aes_block(inp, outp))
+ return 0;
if (outp != out)
memcpy(out, outp, count);
diff --git a/chip/g/dcrypto/app_cipher.c b/chip/g/dcrypto/app_cipher.c
new file mode 100644
index 0000000000..a416720633
--- /dev/null
+++ b/chip/g/dcrypto/app_cipher.c
@@ -0,0 +1,452 @@
+/*
+ * Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "dcrypto.h"
+#include "registers.h"
+
+/* The default build options compile for size (-Os); instruct the
+ * compiler to optimize for speed here. Incidentally -O produces
+ * faster code than -O2!
+ */
+static int __attribute__((optimize("O")))
+inner_loop(uint32_t **out, const uint32_t **in, size_t len)
+{
+ uint32_t *outw = *out;
+ const uint32_t *inw = *in;
+
+ while (len >= 16) {
+ uint32_t w0, w1, w2, w3;
+
+ w0 = inw[0];
+ w1 = inw[1];
+ w2 = inw[2];
+ w3 = inw[3];
+ GREG32(KEYMGR, AES_WFIFO_DATA) = w0;
+ GREG32(KEYMGR, AES_WFIFO_DATA) = w1;
+ GREG32(KEYMGR, AES_WFIFO_DATA) = w2;
+ GREG32(KEYMGR, AES_WFIFO_DATA) = w3;
+
+ while (GREG32(KEYMGR, AES_RFIFO_EMPTY))
+ ;
+
+ w0 = GREG32(KEYMGR, AES_RFIFO_DATA);
+ w1 = GREG32(KEYMGR, AES_RFIFO_DATA);
+ w2 = GREG32(KEYMGR, AES_RFIFO_DATA);
+ w3 = GREG32(KEYMGR, AES_RFIFO_DATA);
+ outw[0] = w0;
+ outw[1] = w1;
+ outw[2] = w2;
+ outw[3] = w3;
+
+ inw += 4;
+ outw += 4;
+ len -= 16;
+ }
+
+ *in = inw;
+ *out = outw;
+ return len;
+}
+
+static int outer_loop(uint32_t **out, const uint32_t **in, size_t len)
+{
+ uint32_t *outw = *out;
+ const uint32_t *inw = *in;
+
+ if (len >= 16) {
+ GREG32(KEYMGR, AES_WFIFO_DATA) = inw[0];
+ GREG32(KEYMGR, AES_WFIFO_DATA) = inw[1];
+ GREG32(KEYMGR, AES_WFIFO_DATA) = inw[2];
+ GREG32(KEYMGR, AES_WFIFO_DATA) = inw[3];
+ inw += 4;
+ len -= 16;
+
+ len = inner_loop(&outw, &inw, len);
+
+ while (GREG32(KEYMGR, AES_RFIFO_EMPTY))
+ ;
+
+ outw[0] = GREG32(KEYMGR, AES_RFIFO_DATA);
+ outw[1] = GREG32(KEYMGR, AES_RFIFO_DATA);
+ outw[2] = GREG32(KEYMGR, AES_RFIFO_DATA);
+ outw[3] = GREG32(KEYMGR, AES_RFIFO_DATA);
+ outw += 4;
+ }
+
+ *in = inw;
+ *out = outw;
+ return len;
+}
+
+static int aes_init(struct APPKEY_CTX *ctx, enum dcrypto_appid appid,
+ const uint32_t iv[4])
+{
+ /* Setup USR-based application key. */
+ if (!DCRYPTO_appkey_init(appid, ctx))
+ return 0;
+
+ /* Configure AES engine. */
+ GWRITE_FIELD(KEYMGR, AES_CTRL, RESET, CTRL_NO_SOFT_RESET);
+ GWRITE_FIELD(KEYMGR, AES_CTRL, KEYSIZE, 2 /* AES-256 */);
+ GWRITE_FIELD(KEYMGR, AES_CTRL, CIPHER_MODE, CIPHER_MODE_CTR);
+ GWRITE_FIELD(KEYMGR, AES_CTRL, ENC_MODE, ENCRYPT_MODE);
+ GWRITE_FIELD(KEYMGR, AES_CTRL, CTR_ENDIAN, CTRL_CTR_BIG_ENDIAN);
+
+ /*
+ * For fixed-key, bulk ciphering, turn off random nops (which
+ * are enabled by default).
+ */
+ GWRITE_FIELD(KEYMGR, AES_RAND_STALL_CTL, STALL_EN, 0);
+
+ /* Enable hidden key usage, each appid gets its own
+ * USR, with USR0 starting at 0x2a0.
+ */
+ GWRITE_FIELD(KEYMGR, AES_USE_HIDDEN_KEY, INDEX,
+ 0x2a0 + (appid * 2));
+ GWRITE_FIELD(KEYMGR, AES_USE_HIDDEN_KEY, ENABLE, 1);
+ GWRITE_FIELD(KEYMGR, AES_CTRL, ENABLE, CTRL_ENABLE);
+
+ /* Wait for key-expansion. */
+ GREG32(KEYMGR, AES_KEY_START) = 1;
+ while (GREG32(KEYMGR, AES_KEY_START))
+ ;
+
+ /* Check for errors (e.g. USR not correctly setup. */
+ if (GREG32(KEYMGR, HKEY_ERR_FLAGS))
+ return 0;
+
+ /* Set IV. */
+ GR_KEYMGR_AES_CTR(0) = iv[0];
+ GR_KEYMGR_AES_CTR(1) = iv[1];
+ GR_KEYMGR_AES_CTR(2) = iv[2];
+ GR_KEYMGR_AES_CTR(3) = iv[3];
+
+ return 1;
+}
+
+int DCRYPTO_app_cipher(enum dcrypto_appid appid, const void *salt,
+ void *out, const void *in, size_t len)
+{
+ struct APPKEY_CTX ctx;
+ const uint32_t *inw = in;
+ uint32_t *outw = out;
+
+ /* Test pointers for word alignment. */
+ if (((uintptr_t) in & 0x03) || ((uintptr_t) out & 0x03))
+ return 0;
+
+ {
+ /* Initialize key, and AES engine. */
+ uint32_t iv[4];
+
+ memcpy(iv, salt, sizeof(iv));
+ if (!aes_init(&ctx, appid, iv))
+ return 0;
+ }
+
+ len = outer_loop(&outw, &inw, len);
+
+ if (len) {
+ /* Cipher the final partial block */
+ uint32_t tmpin[4];
+ uint32_t tmpout[4];
+ const uint32_t *tmpinw;
+ uint32_t *tmpoutw;
+
+ tmpinw = tmpin;
+ tmpoutw = tmpout;
+
+ memcpy(tmpin, inw, len);
+ outer_loop(&tmpoutw, &tmpinw, 16);
+ memcpy(outw, tmpout, len);
+ }
+
+ DCRYPTO_appkey_finish(&ctx);
+ return 1;
+}
+
+#ifdef CRYPTO_TEST_SETUP
+
+#include "common.h"
+#include "console.h"
+#include "hooks.h"
+#include "shared_mem.h"
+#include "task.h"
+#include "timer.h"
+#include "watchdog.h"
+
+#define HEAP_HEAD_ROOM 0x400
+static uint32_t number_of_iterations;
+static uint8_t result;
+
+/* Staticstics for ecrypt and decryp passes. */
+struct ciph_stats {
+ uint16_t min_time;
+ uint16_t max_time;
+ uint32_t total_time;
+} __packed; /* Just in case. */
+
+/* A common structure to contain information about the test run. */
+struct test_info {
+ size_t test_blob_size;
+ struct ciph_stats enc_stats;
+ struct ciph_stats dec_stats;
+ char *p; /* Pointer to an allcoated buffer of test_blob_size bytes. */
+};
+
+static void init_stats(struct ciph_stats *stats)
+{
+ stats->min_time = ~0;
+ stats->max_time = 0;
+ stats->total_time = 0;
+}
+
+static void update_stats(struct ciph_stats *stats, uint32_t time)
+{
+ if (time < stats->min_time)
+ stats->min_time = time;
+
+ if (time > stats->max_time)
+ stats->max_time = time;
+
+ stats->total_time += time;
+}
+
+static void report_stats(const char *direction, struct ciph_stats *stats)
+{
+ ccprintf("%s results: min %d us, max %d us, average %d us\n",
+ direction, stats->min_time, stats->max_time,
+ stats->total_time / number_of_iterations);
+}
+
+/*
+ * Prepare to run the test: allocate memory, initialize stats structures.
+ *
+ * Returns EC_SUCCESS if everything is fine, EC_ERROR_OVERFLOW on malloc
+ * failures.
+ */
+static int prepare_running(struct test_info *pinfo)
+{
+ memset(pinfo, 0, sizeof(*pinfo));
+
+
+ pinfo->test_blob_size = shared_mem_size();
+ /*
+ * Leave some room for crypto functions if they need to allocate
+ * something, just in case. 0x20 extra bytes are needed to be able to
+ * modify size alignment of the allocated buffer.
+ */
+ if (pinfo->test_blob_size < (HEAP_HEAD_ROOM + 0x20)) {
+ ccprintf("Not enough memory to run the test\n");
+ return EC_ERROR_OVERFLOW;
+ }
+ pinfo->test_blob_size = (pinfo->test_blob_size - HEAP_HEAD_ROOM);
+
+ if (shared_mem_acquire(pinfo->test_blob_size,
+ (char **)&(pinfo->p)) != EC_SUCCESS) {
+ ccprintf("Failed to allocate %d bytes\n",
+ pinfo->test_blob_size);
+ return EC_ERROR_OVERFLOW;
+ }
+
+ /*
+ * Use odd block size to make sure unaligned length blocks are handled
+ * properly. This leaves room in the end of the buffer to check if the
+ * decryption routine scratches it.
+ */
+ pinfo->test_blob_size &= ~0x1f;
+ pinfo->test_blob_size |= 7;
+
+ ccprintf("running %d iterations\n", number_of_iterations);
+ ccprintf("blob size %d at %p\n", pinfo->test_blob_size, pinfo->p);
+
+ init_stats(&(pinfo->enc_stats));
+ init_stats(&(pinfo->dec_stats));
+
+ return EC_SUCCESS;
+}
+
+/*
+ * Let's split the buffer in two equal halves, encrypt the lower half into the
+ * upper half and compare them word by word. There should be no repetitions.
+ *
+ * The side effect of this is starting the test with random clear text data.
+ *
+ * The first 16 bytes of the allocated buffer are used as the encryption IV.
+ */
+static int basic_check(struct test_info *pinfo)
+{
+ size_t half;
+ int i;
+ uint32_t *p;
+
+ ccprintf("original data %.16h\n", pinfo->p);
+
+ half = (pinfo->test_blob_size/2) & ~3;
+ if (!DCRYPTO_app_cipher(NVMEM, pinfo->p, pinfo->p,
+ pinfo->p + half, half)) {
+ ccprintf("first ecnryption run failed\n");
+ return EC_ERROR_UNKNOWN;
+ }
+
+ p = (uint32_t *)pinfo->p;
+ half /= sizeof(*p);
+
+ for (i = 0; i < half; i++)
+ if (p[i] == p[i + half]) {
+ ccprintf("repeating 32 bit word detected"
+ " at offset 0x%x!\n", i * 4);
+ return EC_ERROR_UNKNOWN;
+ }
+
+ ccprintf("hashed data %.16h\n", pinfo->p);
+
+ return EC_SUCCESS;
+}
+
+/*
+ * Main iteration of the console command, runs ecnryption/decryption cycles,
+ * vefifying that decrypted text's hash matches the original, and accumulating
+ * timing statistics.
+ */
+static int command_loop(struct test_info *pinfo)
+{
+ uint8_t sha[SHA_DIGEST_SIZE];
+ uint8_t sha_after[SHA_DIGEST_SIZE];
+ uint32_t iteration;
+ uint8_t *p_last_byte;
+ int rv;
+
+ /*
+ * Prepare the hash of the original data to be able to verify
+ * results.
+ */
+ DCRYPTO_SHA1_hash((uint8_t *)(pinfo->p), pinfo->test_blob_size, sha);
+
+ /* Use the hash as an IV for the cipher. */
+ memcpy(sha_after, sha, sizeof(sha_after));
+
+ iteration = number_of_iterations;
+ p_last_byte = pinfo->p + pinfo->test_blob_size;
+
+ while (iteration--) {
+ char last_byte = (char) iteration;
+ uint32_t tstamp;
+
+ *p_last_byte = last_byte;
+
+ if (!(iteration % 500))
+ watchdog_reload();
+
+ tstamp = get_time().val;
+ rv = DCRYPTO_app_cipher(NVMEM, sha_after, pinfo->p,
+ pinfo->p, pinfo->test_blob_size);
+ tstamp = get_time().val - tstamp;
+
+ if (!rv) {
+ ccprintf("encryption failed\n");
+ return EC_ERROR_UNKNOWN;
+ }
+ if (*p_last_byte != last_byte) {
+ ccprintf("encryption overflowed\n");
+ return EC_ERROR_UNKNOWN;
+ }
+ update_stats(&pinfo->enc_stats, tstamp);
+
+ tstamp = get_time().val;
+ rv = DCRYPTO_app_cipher(NVMEM, sha_after, pinfo->p,
+ pinfo->p, pinfo->test_blob_size);
+ tstamp = get_time().val - tstamp;
+
+ if (!rv) {
+ ccprintf("decryption failed\n");
+ return EC_ERROR_UNKNOWN;
+ }
+ if (*p_last_byte != last_byte) {
+ ccprintf("decryption overflowed\n");
+ return EC_ERROR_UNKNOWN;
+ }
+
+ DCRYPTO_SHA1_hash((uint8_t *)(pinfo->p),
+ pinfo->test_blob_size, sha_after);
+ if (memcmp(sha, sha_after, sizeof(sha))) {
+ ccprintf("\n"
+ "sha1 before and after mismatch, %d to go!\n",
+ iteration);
+ return EC_ERROR_UNKNOWN;
+ }
+
+ update_stats(&pinfo->dec_stats, tstamp);
+
+ /* get a new IV */
+ DCRYPTO_SHA1_hash(sha_after, sizeof(sha), sha_after);
+ }
+
+ return EC_SUCCESS;
+}
+
+/*
+ * Run cipher command on the hooks task context, as dcrypto's stack
+ * requirements exceed console tasks' allowance.
+ */
+static void run_cipher_cmd(void)
+{
+ struct test_info info;
+
+ result = prepare_running(&info);
+
+ if (result == EC_SUCCESS)
+ result = basic_check(&info);
+
+ if (result == EC_SUCCESS)
+ result = command_loop(&info);
+
+ if (result == EC_SUCCESS) {
+ report_stats("Encryption", &info.enc_stats);
+ report_stats("Decryption", &info.dec_stats);
+ } else if (info.p) {
+ ccprintf("current data %.16h\n", info.p);
+ }
+
+ if (info.p)
+ shared_mem_release(info.p);
+
+ task_set_event(TASK_ID_CONSOLE, TASK_EVENT_CUSTOM(1), 0);
+}
+DECLARE_DEFERRED(run_cipher_cmd);
+
+static int cmd_cipher(int argc, char **argv)
+{
+ uint32_t events;
+ uint32_t max_time;
+
+ /* Ignore potential input errors, let the user handle them. */
+ if (argc > 1)
+ number_of_iterations = strtoi(argv[1], NULL, 0);
+ else
+ number_of_iterations = 1000;
+
+ if (!number_of_iterations) {
+ ccprintf("not running zero iterations\n");
+ return EC_ERROR_PARAM1;
+ }
+
+ hook_call_deferred(&run_cipher_cmd_data, 0);
+
+ /* Roughly, .5 us per byte should be more than enough. */
+ max_time = number_of_iterations * shared_mem_size() / 2;
+
+ ccprintf("Will wait up to %d ms\n", (max_time + 500)/1000);
+
+ events = task_wait_event_mask(TASK_EVENT_CUSTOM(1), max_time);
+ if (!(events & TASK_EVENT_CUSTOM(1))) {
+ ccprintf("Timed out, you might want to reboot...\n");
+ return EC_ERROR_TIMEOUT;
+ }
+
+ return result;
+}
+DECLARE_SAFE_CONSOLE_COMMAND(cipher, cmd_cipher, NULL, NULL);
+#endif
diff --git a/chip/g/dcrypto/app_key.c b/chip/g/dcrypto/app_key.c
new file mode 100644
index 0000000000..d10dd46014
--- /dev/null
+++ b/chip/g/dcrypto/app_key.c
@@ -0,0 +1,91 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "dcrypto.h"
+#include "internal.h"
+#include "endian.h"
+#include "registers.h"
+
+#include "cryptoc/util.h"
+
+const struct {
+ const char *name;
+ /* SHA256(name) */
+ const uint32_t digest[SHA256_DIGEST_WORDS];
+} dcrypto_app_names[] = {
+ {
+ "RESERVED",
+ {
+ 0x89ef2e22, 0x0032b61a, 0x7b349ab1, 0x3f512449,
+ 0x4cd161dd, 0x2a6cac94, 0x109a045a, 0x23d669ea
+ }
+ },
+ {
+ "NVMEM",
+ {
+ 0xd137e92f, 0x0f39686e, 0xd663f548, 0x9b570397,
+ 0x5801c4ce, 0x8e7c7654, 0xa2a13c85, 0x875779b6
+ }
+ },
+ {
+ "U2F_ATTEST",
+ {
+ 0xe108bde1, 0xb87820a9, 0x8b4b943a, 0xc7c1dbc4,
+ 0xa027d3f1, 0x96538c5f, 0x49a07d16, 0xd0c8e1da
+ }
+ },
+ {
+ "U2F_ORIGIN",
+ {
+ 0xeb4ba9f1, 0x12b0ec6c, 0xd0791cd4, 0x4a1f4e6d,
+ 0x51e60c00, 0xad84c2c0, 0x38b78b24, 0x1ded57ea
+ }
+ },
+ {
+ "U2F_WRAP",
+ {
+ 0xa013e112, 0x4cb0134c, 0x1cab1edf, 0xbd741b61,
+ 0xcd375bcd, 0x8065e8cc, 0xc892ed69, 0x72436c7d
+ }
+ },
+ {
+ /* This key signs data from H1's configured by mn50/scribe. */
+ "PERSO_AUTH",
+ {
+ 0x2019da34, 0xf1a01a13, 0x0fb9f73f, 0xf2e85f76,
+ 0x5ecb7690, 0x09f732c9, 0xe540bf14, 0xcc46799a
+ }
+ },
+ {
+ "PINWEAVER",
+ {
+ 0x51cd9166, 0x911a7460, 0x96aeaf06, 0xa9d0371c,
+ 0xfa08a500, 0xfe4e04a1, 0xe0a36b57, 0x0418c429
+ }
+ },
+};
+
+int DCRYPTO_appkey_init(enum dcrypto_appid appid, struct APPKEY_CTX *ctx)
+{
+ memset(ctx, 0, sizeof(*ctx));
+
+ if (!dcrypto_ladder_compute_usr(
+ appid, dcrypto_app_names[appid].digest))
+ return 0;
+
+ return 1;
+}
+
+void DCRYPTO_appkey_finish(struct APPKEY_CTX *ctx)
+{
+ always_memset(ctx, 0, sizeof(struct APPKEY_CTX));
+ GREG32(KEYMGR, AES_WIPE_SECRETS) = 1;
+}
+
+int DCRYPTO_appkey_derive(enum dcrypto_appid appid, const uint32_t input[8],
+ uint32_t output[8])
+{
+ return !!dcrypto_ladder_derive(appid, dcrypto_app_names[appid].digest,
+ input, output);
+}
diff --git a/chip/g/dcrypto/bn.c b/chip/g/dcrypto/bn.c
index c876b5d868..5c92f82fda 100644
--- a/chip/g/dcrypto/bn.c
+++ b/chip/g/dcrypto/bn.c
@@ -3,11 +3,17 @@
* found in the LICENSE file.
*/
+#ifdef PRINT_PRIMES
+#include "console.h"
+#endif
+
#include "dcrypto.h"
#include "internal.h"
#include "trng.h"
+#include "cryptoc/util.h"
+
#include <assert.h>
#ifdef CONFIG_WATCHDOG
@@ -19,7 +25,7 @@ static inline void watchdog_reload(void) { }
void bn_init(struct LITE_BIGNUM *b, void *buf, size_t len)
{
DCRYPTO_bn_wrap(b, buf, len);
- dcrypto_memset(buf, 0x00, len);
+ always_memset(buf, 0x00, len);
}
void DCRYPTO_bn_wrap(struct LITE_BIGNUM *b, void *buf, size_t len)
@@ -241,7 +247,7 @@ static void bn_rshift(struct LITE_BIGNUM *r, uint32_t carry, uint32_t neg)
/* Montgomery c[] += a * b[] / R % N. */
/* TODO(ngm): constant time. */
static void bn_mont_mul_add(struct LITE_BIGNUM *c, const uint32_t a,
- const struct LITE_BIGNUM *b, const uint32_t nprime,
+ const struct LITE_BIGNUM *b, const uint32_t nprime,
const struct LITE_BIGNUM *N)
{
uint32_t A, B, d0;
@@ -324,11 +330,12 @@ static uint32_t bn_compute_nprime(const uint32_t n0)
return ~ninv + 1; /* Two's complement. */
}
-/* Montgomery output = input ^ exp % N. */
/* TODO(ngm): this implementation not timing or side-channel safe by
* any measure. */
-void bn_mont_modexp(struct LITE_BIGNUM *output, const struct LITE_BIGNUM *input,
- const struct LITE_BIGNUM *exp, const struct LITE_BIGNUM *N)
+static void bn_modexp_internal(struct LITE_BIGNUM *output,
+ const struct LITE_BIGNUM *input,
+ const struct LITE_BIGNUM *exp,
+ const struct LITE_BIGNUM *N)
{
int i;
uint32_t nprime;
@@ -340,18 +347,6 @@ void bn_mont_modexp(struct LITE_BIGNUM *output, const struct LITE_BIGNUM *input,
struct LITE_BIGNUM acc;
struct LITE_BIGNUM aR;
-#ifndef CR50_NO_BN_ASM
- if (bn_bits(N) == 2048 || bn_bits(N) == 1024) {
- /* TODO(ngm): add hardware support for standard key sizes. */
- bn_mont_modexp_asm(output, input, exp, N);
- /* Final reduce. */
- /* TODO(ngm): constant time. */
- if (bn_sub(output, N))
- bn_add(output, N);
- return;
- }
-#endif
-
bn_init(&RR, RR_buf, bn_size(N));
bn_init(&acc, acc_buf, bn_size(N));
bn_init(&aR, aR_buf, bn_size(N));
@@ -391,14 +386,64 @@ void bn_mont_modexp(struct LITE_BIGNUM *output, const struct LITE_BIGNUM *input,
bn_add(output, N); /* Final reduce. */
output->dmax = N->dmax;
- dcrypto_memset(RR_buf, 0, sizeof(RR_buf));
- dcrypto_memset(acc_buf, 0, sizeof(acc_buf));
- dcrypto_memset(aR_buf, 0, sizeof(aR_buf));
+ always_memset(RR_buf, 0, sizeof(RR_buf));
+ always_memset(acc_buf, 0, sizeof(acc_buf));
+ always_memset(aR_buf, 0, sizeof(aR_buf));
+}
+
+/* output = input ^ exp % N */
+int bn_modexp(struct LITE_BIGNUM *output, const struct LITE_BIGNUM *input,
+ const struct LITE_BIGNUM *exp, const struct LITE_BIGNUM *N)
+{
+#ifndef CR50_NO_BN_ASM
+ if ((bn_bits(N) & 255) == 0) {
+ /* Use hardware support for standard key sizes. */
+ return dcrypto_modexp(output, input, exp, N);
+ }
+#endif
+ bn_modexp_internal(output, input, exp, N);
+ return 1;
+}
+
+/* output = input ^ exp % N */
+int bn_modexp_word(struct LITE_BIGNUM *output, const struct LITE_BIGNUM *input,
+ uint32_t exp, const struct LITE_BIGNUM *N)
+{
+#ifndef CR50_NO_BN_ASM
+ if ((bn_bits(N) & 255) == 0) {
+ /* Use hardware support for standard key sizes. */
+ return dcrypto_modexp_word(output, input, exp, N);
+ }
+#endif
+ {
+ struct LITE_BIGNUM pubexp;
+
+ DCRYPTO_bn_wrap(&pubexp, &exp, sizeof(exp));
+ bn_modexp_internal(output, input, &pubexp, N);
+ return 1;
+ }
+}
+
+/* output = input ^ exp % N */
+int bn_modexp_blinded(struct LITE_BIGNUM *output,
+ const struct LITE_BIGNUM *input,
+ const struct LITE_BIGNUM *exp,
+ const struct LITE_BIGNUM *N,
+ uint32_t pubexp)
+{
+#ifndef CR50_NO_BN_ASM
+ if ((bn_bits(N) & 255) == 0) {
+ /* Use hardware support for standard key sizes. */
+ return dcrypto_modexp_blinded(output, input, exp, N, pubexp);
+ }
+#endif
+ bn_modexp_internal(output, input, exp, N);
+ return 1;
}
/* c[] += a * b[] */
static uint32_t bn_mul_add(struct LITE_BIGNUM *c, uint32_t a,
- const struct LITE_BIGNUM *b, uint32_t offset)
+ const struct LITE_BIGNUM *b, uint32_t offset)
{
int i;
uint64_t carry = 0;
@@ -429,386 +474,549 @@ void DCRYPTO_bn_mul(struct LITE_BIGNUM *c, const struct LITE_BIGNUM *a,
BN_DIGIT(c, i + b->dmax - 1) = carry;
}
-#define bn_is_even(b) !bn_is_bit_set((b), 0)
-#define bn_is_odd(b) bn_is_bit_set((b), 0)
-
-static int bn_is_zero(const struct LITE_BIGNUM *a)
-{
- int i, result = 0;
-
- for (i = 0; i < a->dmax; ++i)
- result |= BN_DIGIT(a, i);
- return !result;
-}
-
-/* d = (e ^ -1) mod MOD */
-/* TODO(ngm): this method is used in place of division to calculate
- * q = N/p, i.e. q = p^-1 mod (N-1). The buffer e may be
- * resized to uint32_t once division is implemented. */
-int bn_modinv_vartime(struct LITE_BIGNUM *d, const struct LITE_BIGNUM *e,
- const struct LITE_BIGNUM *MOD)
-{
- /* Buffers for B, D, and U must be as large as e. */
- uint32_t A_buf[RSA_MAX_WORDS + 1];
- uint32_t B_buf[RSA_MAX_WORDS + 1];
- uint32_t C_buf[RSA_MAX_WORDS + 1];
- uint32_t D_buf[RSA_MAX_WORDS + 1];
- uint32_t U_buf[RSA_MAX_WORDS];
- uint32_t V_buf[RSA_MAX_WORDS];
- int a_neg = 0;
- int b_neg = 0;
- int c_neg = 0;
- int d_neg = 0;
- int carry1;
- int carry2;
- int i = 0;
+/* c[] = a[] * b[] */
+static void bn_mul_ex(struct LITE_BIGNUM *c,
+ const struct LITE_BIGNUM *a, int a_len,
+ const struct LITE_BIGNUM *b)
+{
+ int i;
+ uint32_t carry = 0;
- struct LITE_BIGNUM A;
- struct LITE_BIGNUM B;
- struct LITE_BIGNUM C;
- struct LITE_BIGNUM D;
- struct LITE_BIGNUM U;
- struct LITE_BIGNUM V;
+ memset(c->d, 0, bn_size(c));
+ for (i = 0; i < a_len; i++) {
+ BN_DIGIT(c, i + b->dmax - 1) = carry;
+ carry = bn_mul_add(c, BN_DIGIT(a, i), b, i);
+ }
+
+ BN_DIGIT(c, i + b->dmax - 1) = carry;
+}
+
+static int bn_div_word_ex(struct LITE_BIGNUM *q,
+ struct LITE_BIGNUM *r,
+ const struct LITE_BIGNUM *u, int m,
+ uint32_t div)
+{
+ uint32_t rem = 0;
+ int i;
+
+ for (i = m - 1; i >= 0; --i) {
+ uint64_t tmp = ((uint64_t)rem << 32) + BN_DIGIT(u, i);
+ uint32_t qd = tmp / div;
- if (bn_size(e) > sizeof(U_buf))
+ BN_DIGIT(q, i) = qd;
+ rem = tmp - (uint64_t)qd * div;
+ }
+
+ if (r != NULL)
+ BN_DIGIT(r, 0) = rem;
+
+ return 1;
+}
+
+/*
+ * Knuth's long division.
+ *
+ * Returns 0 on error.
+ * |u| >= |v|
+ * v[n-1] must not be 0
+ * r gets |v| digits written to.
+ * q gets |u| - |v| + 1 digits written to.
+ */
+static int bn_div_ex(struct LITE_BIGNUM *q,
+ struct LITE_BIGNUM *r,
+ const struct LITE_BIGNUM *u, int m,
+ const struct LITE_BIGNUM *v, int n)
+{
+ uint32_t vtop;
+ int s, i, j;
+ uint32_t vn[RSA_MAX_WORDS]; /* Normalized v */
+ uint32_t un[RSA_MAX_WORDS + 1]; /* Normalized u */
+
+ if (m < n || n <= 0)
return 0;
- if (bn_is_even(e) && bn_is_even(MOD))
+ vtop = BN_DIGIT(v, n - 1);
+
+ if (vtop == 0)
return 0;
- bn_init(&A, A_buf, bn_size(MOD) + sizeof(uint32_t));
- bn_init(&B, B_buf, bn_size(MOD) + sizeof(uint32_t));
- bn_init(&C, C_buf, bn_size(MOD) + sizeof(uint32_t));
- bn_init(&D, D_buf, bn_size(MOD) + sizeof(uint32_t));
- bn_init(&U, U_buf, bn_size(MOD));
- bn_init(&V, V_buf, bn_size(MOD));
-
- BN_DIGIT(&A, 0) = 1;
- BN_DIGIT(&D, 0) = 1;
- memcpy(U_buf, e->d, bn_size(e));
- memcpy(V_buf, MOD->d, bn_size(MOD));
-
- /* Binary extended GCD, as per Handbook of Applied
- * Cryptography, 14.61. */
- for (i = 0;; i++) {
- carry1 = 0;
- carry2 = 0;
- if (bn_is_even(&U)) {
- bn_rshift(&U, 0, 0);
- if (bn_is_odd(&A) || bn_is_odd(&B)) {
- carry1 = bn_signed_add(&A, &a_neg, MOD, 0);
- carry2 = bn_signed_sub(&B, &b_neg, e, 0);
+ if (n == 1)
+ return bn_div_word_ex(q, r, u, m, vtop);
+
+ /* Compute shift factor to make v have high bit set */
+ s = 0;
+ while ((vtop & 0x80000000) == 0) {
+ s = s + 1;
+ vtop = vtop << 1;
+ }
+
+ /* Normalize u and v into un and vn.
+ * Note un always gains a leading digit
+ */
+ if (s != 0) {
+ for (i = n - 1; i > 0; i--)
+ vn[i] = (BN_DIGIT(v, i) << s) |
+ (BN_DIGIT(v, i - 1) >> (32 - s));
+ vn[0] = BN_DIGIT(v, 0) << s;
+
+ un[m] = BN_DIGIT(u, m - 1) >> (32 - s);
+ for (i = m - 1; i > 0; i--)
+ un[i] = (BN_DIGIT(u, i) << s) |
+ (BN_DIGIT(u, i - 1) >> (32 - s));
+ un[0] = BN_DIGIT(u, 0) << s;
+ } else {
+ for (i = 0; i < n; ++i)
+ vn[i] = BN_DIGIT(v, i);
+ for (i = 0; i < m; ++i)
+ un[i] = BN_DIGIT(u, i);
+ un[m] = 0;
+ }
+
+ /* Main loop, reducing un digit by digit */
+ for (j = m - n; j >= 0; j--) {
+ uint32_t qd;
+ int64_t t, k;
+
+ /* Estimate quotient digit */
+ if (un[j + n] == vn[n - 1]) {
+ /* Maxed out */
+ qd = 0xFFFFFFFF;
+ } else {
+ /* Fine tune estimate */
+ uint64_t rhat = ((uint64_t)un[j + n] << 32) +
+ un[j + n - 1];
+
+ qd = rhat / vn[n - 1];
+ rhat = rhat - (uint64_t)qd * vn[n - 1];
+ while ((rhat >> 32) == 0 &&
+ (uint64_t)qd * vn[n - 2] >
+ (rhat << 32) + un[j + n - 2]) {
+ qd = qd - 1;
+ rhat = rhat + vn[n - 1];
}
- bn_rshift(&A, carry1, a_neg);
- bn_rshift(&B, carry2, b_neg);
- } else if (bn_is_even(&V)) {
- bn_rshift(&V, 0, 0);
- if (bn_is_odd(&C) || bn_is_odd(&D)) {
- carry1 = bn_signed_add(&C, &c_neg, MOD, 0);
- carry2 = bn_signed_sub(&D, &d_neg, e, 0);
+ }
+
+ /* Multiply and subtract */
+ k = 0;
+ for (i = 0; i < n; i++) {
+ uint64_t p = (uint64_t)qd * vn[i];
+
+ t = un[i + j] - k - (p & 0xFFFFFFFF);
+ un[i + j] = t;
+ k = (p >> 32) - (t >> 32);
+ }
+ t = un[j + n] - k;
+ un[j + n] = t;
+
+ /* If borrowed, add one back and adjust estimate */
+ if (t < 0) {
+ qd = qd - 1;
+ for (i = 0; i < n; i++) {
+ t = (uint64_t)un[i + j] + vn[i] + k;
+ un[i + j] = t;
+ k = t >> 32;
}
- bn_rshift(&C, carry1, c_neg);
- bn_rshift(&D, carry2, d_neg);
- } else { /* U, V both odd. */
- if (bn_gte(&U, &V)) {
- assert(!bn_sub(&U, &V));
- bn_signed_sub(&A, &a_neg, &C, c_neg);
- bn_signed_sub(&B, &b_neg, &D, d_neg);
- if (bn_is_zero(&U))
- break; /* done. */
+ un[j + n] = un[j + n] + k;
+ }
+
+ BN_DIGIT(q, j) = qd;
+ }
+
+ if (r != NULL) {
+ /* Denormalize un into r */
+ if (s != 0) {
+ for (i = 0; i < n - 1; i++)
+ BN_DIGIT(r, i) = (un[i] >> s) |
+ (un[i + 1] << (32 - s));
+ BN_DIGIT(r, n - 1) = un[n - 1] >> s;
+ } else {
+ for (i = 0; i < n; i++)
+ BN_DIGIT(r, i) = un[i];
+ }
+ }
+
+ return 1;
+}
+
+static void bn_set_bn(struct LITE_BIGNUM *d, const struct LITE_BIGNUM *src,
+ size_t n)
+{
+ size_t i = 0;
+
+ for (; i < n && i < d->dmax; ++i)
+ BN_DIGIT(d, i) = BN_DIGIT(src, i);
+ for (; i < d->dmax; ++i)
+ BN_DIGIT(d, i) = 0;
+}
+
+static size_t bn_digits(const struct LITE_BIGNUM *a)
+{
+ size_t n = a->dmax - 1;
+
+ while (BN_DIGIT(a, n) == 0 && n)
+ --n;
+ return n + 1;
+}
+
+int DCRYPTO_bn_div(struct LITE_BIGNUM *quotient,
+ struct LITE_BIGNUM *remainder,
+ const struct LITE_BIGNUM *src,
+ const struct LITE_BIGNUM *divisor)
+{
+ int src_len = bn_digits(src);
+ int div_len = bn_digits(divisor);
+ int i, result;
+
+ if (src_len < div_len)
+ return 0;
+
+ result = bn_div_ex(quotient, remainder,
+ src, src_len,
+ divisor, div_len);
+
+ if (!result)
+ return 0;
+
+ /* 0-pad the destinations. */
+ for (i = src_len - div_len + 1; i < quotient->dmax; ++i)
+ BN_DIGIT(quotient, i) = 0;
+ if (remainder) {
+ for (i = div_len; i < remainder->dmax; ++i)
+ BN_DIGIT(remainder, i) = 0;
+ }
+
+ return result;
+}
+
+/*
+ * Extended Euclid modular inverse.
+ *
+ * https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm
+ * #Computing_multiplicative_inverses_in_modular_structures:
+
+ * function inverse(a, n)
+ * t := 0; newt := 1;
+ * r := n; newr := a;
+ * while newr ≠ 0
+ * quotient := r div newr
+ * (t, newt) := (newt, t - quotient * newt)
+ * (r, newr) := (newr, r - quotient * newr)
+ * if r > 1 then return "a is not invertible"
+ * if t < 0 then t := t + n
+ * return t
+ */
+int bn_modinv_vartime(struct LITE_BIGNUM *dst, const struct LITE_BIGNUM *src,
+ const struct LITE_BIGNUM *mod)
+{
+ uint32_t R_buf[RSA_MAX_WORDS];
+ uint32_t nR_buf[RSA_MAX_WORDS];
+ uint32_t Q_buf[RSA_MAX_WORDS];
+
+ uint32_t nT_buf[RSA_MAX_WORDS + 1]; /* Can go negative, hence +1 */
+ uint32_t T_buf[RSA_MAX_WORDS + 1]; /* Can go negative */
+ uint32_t tmp_buf[2 * RSA_MAX_WORDS + 1]; /* needs to hold Q*nT */
+
+ struct LITE_BIGNUM R;
+ struct LITE_BIGNUM nR;
+ struct LITE_BIGNUM Q;
+ struct LITE_BIGNUM T;
+ struct LITE_BIGNUM nT;
+ struct LITE_BIGNUM tmp;
+
+ struct LITE_BIGNUM *pT = &T;
+ struct LITE_BIGNUM *pnT = &nT;
+ struct LITE_BIGNUM *pR = &R;
+ struct LITE_BIGNUM *pnR = &nR;
+ struct LITE_BIGNUM *bnswap;
+
+ int t_neg = 0;
+ int nt_neg = 0;
+ int iswap;
+
+ size_t r_len, nr_len;
+
+ bn_init(&R, R_buf, bn_size(mod));
+ bn_init(&nR, nR_buf, bn_size(mod));
+ bn_init(&Q, Q_buf, bn_size(mod));
+ bn_init(&T, T_buf, bn_size(mod) + sizeof(uint32_t));
+ bn_init(&nT, nT_buf, bn_size(mod) + sizeof(uint32_t));
+ bn_init(&tmp, tmp_buf, bn_size(mod) + sizeof(uint32_t));
+
+ r_len = bn_digits(mod);
+ nr_len = bn_digits(src);
+
+ BN_DIGIT(&nT, 0) = 1; /* T = 0, nT = 1 */
+ bn_set_bn(&R, mod, r_len); /* R = n */
+ bn_set_bn(&nR, src, nr_len); /* nR = input */
+
+ /* Trim nR */
+ while (nr_len && BN_DIGIT(&nR, nr_len - 1) == 0)
+ --nr_len;
+
+ while (nr_len) {
+ size_t q_len = r_len - nr_len + 1;
+
+ /* (r, nr) = (nr, r % nr), q = r / nr */
+ if (!bn_div_ex(&Q, pR, pR, r_len, pnR, nr_len))
+ return 0;
+
+ /* swap R and nR */
+ r_len = nr_len;
+ bnswap = pR; pR = pnR; pnR = bnswap;
+
+ /* trim nR and Q */
+ while (nr_len && BN_DIGIT(pnR, nr_len - 1) == 0)
+ --nr_len;
+ while (q_len && BN_DIGIT(&Q, q_len - 1) == 0)
+ --q_len;
+
+ Q.dmax = q_len;
+
+ /* compute t - q*nt */
+ if (q_len == 1 && BN_DIGIT(&Q, 0) <= 2) {
+ /* Doing few direct subs is faster than mul + sub */
+ uint32_t n = BN_DIGIT(&Q, 0);
+
+ while (n--)
+ bn_signed_sub(pT, &t_neg, pnT, nt_neg);
+ } else {
+ /* Call bn_mul_ex with smallest operand first */
+ if (nt_neg) {
+ /* Negative numbers use all digits,
+ * thus pnT is large
+ */
+ bn_mul_ex(&tmp, &Q, q_len, pnT);
} else {
- assert(!bn_sub(&V, &U));
- bn_signed_sub(&C, &c_neg, &A, a_neg);
- bn_signed_sub(&D, &d_neg, &B, b_neg);
+ int nt_len = bn_digits(pnT);
+
+ if (q_len < nt_len)
+ bn_mul_ex(&tmp, &Q, q_len, pnT);
+ else
+ bn_mul_ex(&tmp, pnT, nt_len, &Q);
}
+ bn_signed_sub(pT, &t_neg, &tmp, nt_neg);
}
- if ((i + 1) % 1000 == 0)
- /* TODO(ngm): Poke the watchdog (only
- * necessary for q = N/p). Remove once
- * division is implemented. */
- watchdog_reload();
- }
- BN_DIGIT(&V, 0) ^= 0x01;
- if (bn_is_zero(&V)) {
- while (c_neg)
- bn_signed_add(&C, &c_neg, MOD, 0);
- while (bn_gte(&C, MOD))
- bn_sub(&C, MOD);
+ /* swap T and nT */
+ bnswap = pT; pT = pnT; pnT = bnswap;
+ iswap = t_neg; t_neg = nt_neg; nt_neg = iswap;
+ }
- memcpy(d->d, C.d, bn_size(d));
- return 1;
- } else {
- return 0; /* Inverse not found. */
+ if (r_len != 1 || BN_DIGIT(pR, 0) != 1) {
+ /* gcd not 1; no direct inverse */
+ return 0;
}
+
+ if (t_neg)
+ bn_signed_add(pT, &t_neg, mod, 0);
+
+ bn_set_bn(dst, pT, bn_digits(pT));
+
+ return 1;
}
-#define NUM_PRIMES 4095
#define PRIME1 3
-/* First NUM_PRIMES worth of primes starting with PRIME1. The entries
- * are a delta / 2 encoding, i.e.:
- * prime(x) = prime(x - 1) + (PRIME_DELTAS[x] * 2)
+/*
+ * The array below is an encoding of the first 4096 primes, starting with
+ * PRIME1. Using 4096 of the first primes results in at least 5% improvement
+ * in running time over using the first 2048.
+ *
+ * Most byte entries in the array contain two sequential differentials between
+ * two adjacent prime numbers, each differential halved (as the difference is
+ * always even) and packed into 4 bits.
+ *
+ * If a halved differential value exceeds 0xf (and as such does not fit into 4
+ * bits), a zero is placed in the array followed by the value literal (no
+ * halving).
*
- * Using 4096 of the first primes results in a 5-10% improvement in
- * running time over using the first 2048. */
-const uint8_t PRIME_DELTAS[NUM_PRIMES] = {
- 0, 1, 1, 2, 1, 2, 1, 2, 3, 1, 3, 2, 1, 2, 3,
- 3, 1, 3, 2, 1, 3, 2, 3, 4, 2, 1, 2, 1, 2, 7, 2,
- 3, 1, 5, 1, 3, 3, 2, 3, 3, 1, 5, 1, 2, 1, 6, 6,
- 2, 1, 2, 3, 1, 5, 3, 3, 3, 1, 3, 2, 1, 5, 7, 2,
- 1, 2, 7, 3, 5, 1, 2, 3, 4, 3, 3, 2, 3, 4, 2, 4,
- 5, 1, 5, 1, 3, 2, 3, 4, 2, 1, 2, 6, 4, 2, 4, 2,
- 3, 6, 1, 9, 3, 5, 3, 3, 1, 3, 5, 3, 3, 1, 3, 3,
- 2, 1, 6, 5, 1, 2, 3, 3, 1, 6, 2, 3, 4, 5, 4, 5,
- 4, 3, 3, 2, 4, 3, 2, 4, 2, 7, 5, 6, 1, 5, 1, 2,
- 1, 5, 7, 2, 1, 2, 7, 2, 1, 2, 10, 2, 4, 5, 4, 2,
- 3, 3, 7, 2, 3, 3, 4, 3, 6, 2, 3, 1, 5, 1, 3, 5,
- 1, 5, 1, 3, 9, 2, 1, 2, 3, 3, 4, 3, 3, 11, 1, 5,
- 4, 5, 3, 3, 4, 6, 2, 3, 3, 1, 3, 6, 5, 9, 1, 2,
- 3, 1, 3, 2, 1, 2, 6, 1, 3, 17, 3, 3, 4, 9, 5, 7,
- 2, 1, 2, 3, 4, 2, 1, 3, 6, 5, 1, 2, 1, 2, 3, 6,
- 6, 4, 6, 3, 2, 3, 4, 2, 4, 2, 7, 2, 3, 1, 2, 3,
- 1, 3, 5, 10, 3, 2, 1, 12, 2, 1, 5, 6, 1, 5, 4, 3,
- 3, 3, 9, 3, 2, 1, 6, 5, 6, 4, 8, 7, 3, 2, 1, 2,
- 1, 5, 6, 3, 3, 9, 1, 8, 1, 11, 3, 4, 3, 2, 1, 2,
- 4, 3, 5, 1, 5, 7, 5, 3, 6, 1, 2, 1, 5, 6, 1, 8,
- 1, 3, 2, 1, 5, 4, 9, 12, 2, 3, 4, 8, 1, 2, 4, 8,
- 1, 2, 4, 3, 3, 2, 6, 1, 11, 3, 1, 3, 2, 3, 7, 3,
- 2, 1, 3, 2, 3, 6, 3, 3, 7, 2, 3, 6, 4, 3, 2, 13,
- 9, 5, 4, 2, 3, 1, 3, 11, 6, 1, 8, 4, 2, 6, 7, 5,
- 1, 2, 4, 3, 3, 2, 1, 2, 3, 4, 2, 1, 3, 5, 1, 5,
- 4, 2, 7, 5, 6, 1, 3, 2, 1, 8, 7, 2, 3, 4, 3, 2,
- 9, 4, 5, 3, 3, 4, 5, 6, 7, 2, 3, 3, 1, 14, 1, 5,
- 4, 2, 7, 2, 4, 6, 3, 6, 2, 3, 10, 5, 1, 8, 13, 2,
- 1, 6, 3, 2, 6, 3, 4, 2, 4, 11, 1, 2, 1, 6, 14, 1,
- 3, 3, 3, 2, 3, 1, 6, 2, 6, 1, 5, 1, 8, 1, 8, 3,
- 10, 8, 4, 2, 1, 2, 1, 11, 4, 6, 3, 5, 1, 2, 3, 1,
- 3, 5, 1, 6, 5, 1, 5, 7, 3, 2, 3, 4, 3, 3, 8, 6,
- 1, 2, 7, 3, 2, 4, 5, 4, 3, 3, 11, 3, 1, 5, 7, 2,
- 3, 9, 1, 5, 7, 2, 1, 5, 7, 2, 4, 9, 2, 3, 1, 2,
- 3, 1, 6, 2, 10, 11, 6, 1, 2, 3, 3, 1, 3, 11, 1, 3,
- 8, 3, 6, 1, 3, 6, 8, 1, 2, 3, 7, 2, 1, 9, 12, 5,
- 3, 1, 5, 1, 5, 1, 5, 3, 1, 5, 1, 5, 3, 4, 15, 5,
- 1, 5, 4, 3, 5, 9, 3, 6, 6, 1, 9, 3, 2, 3, 3, 9,
- 1, 5, 7, 3, 2, 1, 2, 12, 1, 6, 3, 8, 4, 3, 3, 9,
- 8, 1, 2, 3, 1, 3, 3, 5, 3, 6, 6, 9, 1, 3, 2, 9,
- 4, 12, 2, 1, 2, 3, 1, 6, 2, 7, 15, 5, 3, 6, 7, 3,
- 5, 6, 1, 2, 3, 4, 3, 5, 1, 2, 7, 3, 3, 2, 3, 1,
- 5, 1, 8, 6, 4, 9, 2, 3, 6, 1, 3, 3, 3, 14, 3, 7,
- 2, 4, 5, 4, 6, 9, 2, 1, 2, 12, 6, 3, 1, 8, 3, 3,
- 7, 5, 7, 2, 15, 3, 3, 3, 4, 3, 2, 1, 6, 3, 2, 1,
- 3, 11, 3, 1, 2, 9, 1, 2, 6, 1, 3, 2, 13, 3, 3, 2,
- 4, 5, 16, 8, 1, 3, 2, 1, 2, 1, 5, 7, 3, 2, 4, 5,
- 3, 10, 2, 1, 3, 15, 2, 4, 5, 3, 3, 4, 3, 6, 2, 3,
- 1, 3, 2, 3, 1, 5, 1, 8, 3, 10, 2, 6, 7, 14, 3, 10,
- 2, 9, 4, 3, 2, 3, 7, 3, 3, 5, 1, 5, 6, 4, 5, 1,
- 5, 4, 6, 5, 12, 1, 2, 4, 3, 2, 4, 9, 5, 3, 3, 1,
- 3, 5, 6, 1, 5, 3, 3, 3, 4, 3, 5, 3, 1, 3, 3, 3,
- 5, 4, 12, 3, 11, 1, 9, 2, 4, 5, 15, 4, 9, 2, 1, 5,
- 3, 1, 3, 2, 9, 4, 6, 9, 8, 3, 1, 6, 3, 5, 1, 5,
- 1, 3, 5, 7, 2, 12, 1, 8, 1, 5, 1, 5, 10, 2, 1, 2,
- 4, 8, 3, 3, 1, 6, 8, 4, 2, 3, 15, 1, 5, 1, 3, 2,
- 3, 3, 4, 3, 2, 6, 3, 4, 6, 2, 7, 6, 5, 12, 3, 6,
- 3, 1, 11, 4, 9, 5, 3, 7, 2, 1, 3, 5, 4, 3, 2, 3,
- 15, 7, 5, 1, 6, 5, 1, 8, 1, 9, 12, 9, 3, 8, 9, 3,
- 1, 9, 2, 3, 1, 5, 4, 5, 3, 3, 4, 2, 3, 1, 5, 1,
- 6, 2, 3, 3, 1, 6, 2, 7, 9, 2, 3, 10, 2, 4, 3, 2,
- 4, 2, 7, 3, 2, 7, 6, 2, 1, 15, 2, 12, 3, 3, 6, 6,
- 7, 3, 2, 1, 2, 9, 3, 6, 4, 3, 2, 6, 1, 6, 15, 8,
- 1, 3, 11, 7, 3, 5, 6, 3, 1, 2, 4, 5, 3, 3, 12, 7,
- 3, 2, 4, 6, 9, 5, 1, 5, 1, 2, 3, 10, 3, 2, 7, 2,
- 1, 2, 7, 3, 6, 12, 5, 3, 4, 5, 1, 15, 2, 3, 1, 6,
- 2, 7, 3, 17, 6, 4, 3, 5, 1, 2, 10, 5, 4, 8, 1, 5,
- 7, 2, 1, 6, 3, 8, 3, 4, 2, 4, 2, 3, 4, 3, 3, 6,
- 3, 2, 3, 3, 4, 9, 2, 10, 2, 6, 1, 5, 3, 1, 5, 6,
- 1, 2, 10, 3, 15, 3, 2, 4, 5, 6, 3, 1, 14, 1, 3, 2,
- 1, 8, 6, 1, 3, 5, 4, 12, 6, 3, 9, 3, 2, 7, 3, 2,
- 6, 4, 3, 6, 2, 3, 6, 3, 6, 1, 8, 10, 2, 1, 5, 9,
- 4, 2, 7, 2, 1, 3, 11, 3, 7, 3, 3, 5, 3, 1, 5, 1,
- 2, 1, 11, 1, 2, 3, 3, 6, 3, 7, 5, 6, 3, 4, 2, 18,
- 7, 6, 3, 2, 3, 1, 6, 3, 6, 8, 1, 5, 4, 11, 1, 6,
- 3, 2, 3, 9, 1, 6, 3, 2, 6, 4, 3, 6, 2, 3, 6, 3,
- 1, 6, 6, 2, 7, 3, 8, 3, 1, 5, 4, 9, 3, 17, 1, 14,
- 1, 11, 3, 1, 5, 6, 1, 3, 2, 4, 11, 3, 1, 5, 4, 2,
- 3, 4, 2, 6, 9, 6, 10, 2, 3, 3, 4, 2, 1, 8, 6, 1,
- 5, 4, 5, 1, 2, 3, 7, 6, 11, 4, 14, 1, 2, 10, 2, 1,
- 2, 7, 5, 6, 1, 6, 8, 1, 14, 4, 11, 4, 2, 3, 3, 7,
- 2, 4, 6, 3, 3, 2, 10, 2, 9, 1, 6, 3, 2, 3, 7, 9,
- 5, 4, 5, 16, 3, 5, 3, 3, 1, 3, 8, 3, 1, 6, 3, 14,
- 1, 5, 4, 8, 3, 4, 3, 5, 12, 10, 5, 1, 5, 1, 6, 2,
- 3, 10, 2, 1, 6, 9, 5, 1, 5, 1, 2, 10, 8, 13, 2, 4,
- 3, 2, 6, 3, 4, 6, 6, 3, 2, 4, 11, 1, 8, 7, 5, 3,
- 6, 6, 7, 3, 2, 10, 2, 6, 3, 1, 3, 3, 8, 4, 11, 1,
- 14, 4, 3, 2, 10, 2, 6, 12, 10, 2, 4, 5, 1, 8, 1, 6,
- 6, 17, 1, 2, 3, 6, 3, 3, 4, 3, 2, 1, 3, 12, 2, 10,
- 5, 3, 3, 7, 2, 3, 3, 1, 6, 3, 5, 1, 5, 3, 10, 2,
- 13, 2, 1, 3, 11, 1, 12, 2, 3, 1, 2, 3, 12, 3, 4, 2,
- 1, 17, 3, 4, 8, 6, 1, 5, 1, 5, 3, 4, 2, 4, 6, 11,
- 3, 7, 2, 13, 2, 1, 6, 5, 4, 2, 4, 6, 2, 7, 3, 8,
- 3, 4, 2, 3, 3, 4, 3, 5, 6, 1, 3, 3, 8, 4, 3, 3,
- 6, 5, 1, 3, 9, 2, 3, 3, 3, 6, 9, 4, 3, 5, 4, 9,
- 2, 7, 3, 9, 5, 4, 5, 6, 1, 3, 6, 6, 18, 2, 3, 4,
- 2, 3, 1, 2, 9, 6, 3, 4, 3, 3, 2, 9, 1, 2, 1, 12,
- 2, 3, 3, 7, 15, 3, 2, 3, 6, 3, 10, 2, 4, 2, 4, 3,
- 3, 2, 15, 1, 5, 6, 4, 5, 4, 12, 3, 6, 2, 7, 2, 3,
- 1, 14, 7, 8, 1, 6, 3, 2, 10, 5, 3, 3, 3, 4, 5, 6,
- 7, 5, 7, 8, 7, 5, 7, 3, 8, 3, 4, 3, 8, 10, 5, 1,
- 3, 2, 1, 2, 6, 1, 5, 1, 3, 11, 3, 1, 2, 9, 4, 5,
- 4, 11, 1, 5, 9, 7, 2, 1, 2, 9, 1, 2, 3, 4, 5, 1,
- 15, 2, 15, 1, 5, 1, 9, 2, 9, 3, 7, 5, 1, 2, 10, 18,
- 3, 2, 3, 7, 2, 10, 5, 7, 11, 3, 1, 15, 6, 5, 9, 1,
- 2, 7, 3, 11, 9, 1, 6, 3, 2, 4, 2, 4, 3, 5, 1, 6,
- 9, 5, 7, 8, 7, 2, 3, 3, 1, 3, 2, 1, 14, 1, 14, 3,
- 1, 2, 3, 7, 2, 6, 7, 8, 7, 2, 3, 4, 3, 2, 3, 3,
- 3, 4, 2, 4, 2, 7, 8, 4, 3, 2, 6, 4, 8, 1, 5, 4,
- 2, 3, 13, 3, 5, 4, 2, 3, 6, 7, 15, 2, 7, 11, 4, 6,
- 2, 3, 4, 5, 3, 7, 5, 3, 1, 5, 6, 6, 7, 3, 3, 9,
- 5, 3, 4, 9, 2, 3, 1, 3, 5, 1, 5, 4, 3, 3, 5, 1,
- 9, 5, 1, 6, 2, 3, 4, 5, 6, 7, 6, 2, 4, 5, 3, 3,
- 10, 2, 7, 8, 7, 5, 4, 5, 6, 1, 9, 3, 6, 5, 6, 1,
- 2, 1, 6, 3, 2, 4, 2, 22, 2, 1, 2, 1, 5, 6, 3, 3,
- 7, 2, 3, 3, 3, 4, 3, 18, 9, 2, 3, 1, 6, 3, 3, 3,
- 2, 7, 11, 6, 1, 9, 5, 3, 13, 12, 2, 1, 2, 1, 2, 7,
- 2, 3, 3, 4, 8, 6, 1, 21, 2, 1, 2, 12, 3, 3, 1, 9,
- 2, 7, 3, 14, 9, 7, 3, 5, 6, 1, 3, 6, 15, 3, 2, 3,
- 3, 7, 2, 1, 12, 2, 3, 3, 13, 5, 9, 3, 4, 3, 3, 15,
- 2, 6, 6, 1, 8, 1, 3, 2, 6, 9, 1, 3, 2, 13, 6, 3,
- 6, 2, 12, 12, 6, 3, 1, 6, 14, 4, 2, 3, 6, 1, 9, 3,
- 2, 3, 3, 10, 8, 1, 3, 3, 9, 5, 3, 1, 2, 4, 3, 3,
- 12, 8, 3, 4, 5, 3, 7, 11, 4, 8, 3, 1, 6, 2, 1, 11,
- 4, 9, 17, 1, 3, 9, 2, 3, 3, 4, 5, 4, 9, 3, 2, 1,
- 2, 4, 8, 1, 6, 6, 3, 9, 2, 3, 3, 3, 1, 3, 6, 5,
- 10, 6, 9, 2, 3, 1, 8, 1, 5, 7, 2, 15, 1, 5, 6, 1,
- 12, 3, 8, 4, 5, 1, 6, 11, 3, 1, 8, 10, 5, 1, 6, 6,
- 9, 5, 6, 3, 1, 5, 1, 3, 5, 9, 1, 6, 3, 2, 3, 1,
- 12, 14, 1, 2, 1, 5, 1, 8, 6, 4, 11, 1, 3, 2, 1, 5,
- 3, 10, 6, 5, 4, 6, 3, 3, 3, 2, 9, 1, 2, 6, 9, 1,
- 6, 3, 2, 1, 8, 6, 6, 7, 2, 4, 9, 2, 6, 7, 3, 3,
- 2, 4, 3, 2, 10, 6, 5, 7, 2, 1, 8, 1, 6, 15, 2, 3,
- 12, 10, 12, 5, 4, 6, 5, 6, 3, 6, 6, 3, 4, 8, 7, 3,
- 2, 3, 18, 10, 5, 15, 6, 1, 2, 1, 14, 6, 7, 3, 11, 4,
- 2, 9, 3, 7, 9, 2, 3, 1, 3, 17, 9, 1, 8, 3, 9, 1,
- 12, 2, 1, 3, 6, 3, 6, 5, 4, 3, 8, 6, 4, 5, 7, 20,
- 3, 1, 3, 2, 6, 7, 2, 1, 2, 1, 2, 4, 3, 5, 3, 3,
- 1, 3, 3, 3, 6, 3, 12, 5, 1, 5, 3, 6, 3, 3, 7, 3,
- 3, 26, 10, 3, 5, 1, 5, 4, 5, 6, 6, 1, 3, 2, 7, 8,
- 4, 6, 3, 11, 1, 5, 4, 3, 11, 1, 11, 3, 4, 5, 6, 6,
- 1, 5, 3, 6, 1, 2, 7, 5, 1, 3, 9, 2, 6, 4, 9, 6,
- 3, 3, 2, 3, 3, 7, 2, 1, 6, 6, 2, 3, 9, 9, 6, 1,
- 8, 6, 4, 9, 5, 13, 2, 3, 4, 3, 3, 2, 1, 5, 10, 2,
- 3, 4, 2, 10, 5, 1, 17, 1, 2, 12, 1, 6, 6, 5, 3, 1,
- 6, 15, 3, 6, 8, 6, 1, 11, 9, 6, 7, 5, 1, 6, 6, 2,
- 1, 2, 3, 6, 1, 8, 9, 1, 20, 4, 8, 3, 4, 5, 1, 2,
- 9, 4, 5, 4, 6, 2, 9, 1, 9, 5, 1, 2, 1, 2, 4, 14,
- 1, 3, 11, 6, 3, 7, 9, 2, 3, 4, 3, 3, 5, 4, 2, 1,
- 9, 5, 3, 10, 11, 4, 3, 15, 2, 1, 2, 9, 3, 15, 1, 2,
- 4, 3, 2, 3, 6, 7, 17, 7, 3, 2, 1, 3, 2, 7, 2, 1,
- 3, 14, 1, 2, 3, 4, 5, 1, 5, 1, 5, 1, 2, 15, 1, 6,
- 6, 5, 9, 6, 7, 5, 1, 6, 3, 5, 3, 7, 6, 2, 7, 2,
- 9, 1, 5, 4, 2, 4, 5, 6, 9, 9, 4, 3, 9, 8, 7, 3,
- 3, 5, 7, 2, 3, 1, 6, 6, 2, 3, 3, 6, 1, 8, 1, 6,
- 3, 2, 7, 3, 2, 1, 6, 9, 2, 18, 9, 6, 6, 1, 2, 1,
- 2, 4, 6, 2, 18, 3, 9, 1, 6, 5, 3, 6, 12, 4, 3, 3,
- 8, 6, 1, 9, 5, 10, 5, 1, 3, 9, 2, 1, 20, 3, 1, 8,
- 1, 2, 4, 9, 5, 6, 3, 1, 5, 4, 2, 3, 6, 1, 5, 9,
- 4, 3, 2, 10, 2, 3, 18, 3, 1, 5, 3, 12, 3, 7, 8, 3,
- 9, 1, 5, 10, 5, 4, 3, 2, 3, 1, 5, 1, 6, 2, 1, 2,
- 4, 5, 3, 6, 9, 7, 6, 8, 4, 3, 8, 4, 2, 1, 3, 9,
- 12, 9, 5, 6, 1, 2, 7, 5, 3, 3, 3, 9, 6, 1, 14, 9,
- 7, 8, 6, 7, 12, 6, 11, 3, 1, 5, 4, 2, 1, 2, 7, 6,
- 3, 2, 3, 7, 2, 1, 2, 15, 3, 1, 3, 5, 1, 15, 11, 1,
- 2, 3, 4, 3, 3, 8, 6, 6, 3, 4, 2, 1, 12, 6, 2, 3,
- 4, 3, 3, 5, 1, 3, 6, 14, 7, 3, 2, 6, 4, 3, 6, 2,
- 3, 7, 3, 6, 5, 3, 3, 4, 3, 3, 2, 1, 2, 4, 6, 2,
- 7, 9, 5, 1, 8, 3, 10, 3, 5, 4, 2, 15, 18, 6, 4, 11,
- 6, 1, 3, 6, 8, 3, 3, 1, 9, 2, 13, 2, 4, 9, 5, 4,
- 5, 3, 7, 2, 10, 11, 9, 6, 4, 14, 6, 3, 3, 4, 3, 6,
- 12, 8, 7, 2, 7, 6, 3, 5, 6, 10, 3, 2, 4, 9, 6, 9,
- 5, 1, 2, 10, 5, 7, 2, 3, 1, 5, 12, 9, 1, 2, 10, 8,
- 7, 5, 7, 3, 2, 3, 10, 3, 5, 3, 1, 6, 3, 15, 5, 4,
- 3, 2, 3, 4, 20, 1, 2, 1, 6, 9, 2, 3, 4, 5, 3, 9,
- 9, 1, 6, 8, 4, 3, 2, 3, 3, 1, 26, 7, 2, 10, 8, 1,
- 2, 3, 6, 1, 3, 6, 6, 3, 2, 7, 5, 3, 3, 7, 5, 7,
- 8, 4, 3, 6, 2, 4, 11, 3, 1, 9, 11, 3, 1, 9, 3, 8,
- 7, 5, 3, 6, 1, 3, 2, 4, 9, 6, 8, 1, 2, 7, 2, 4,
- 6, 6, 15, 8, 4, 2, 1, 3, 11, 6, 4, 5, 3, 3, 3, 7,
- 3, 9, 5, 6, 1, 5, 1, 2, 13, 2, 6, 4, 2, 9, 4, 5,
- 7, 8, 3, 3, 4, 5, 3, 4, 3, 6, 5, 10, 5, 4, 2, 6,
- 13, 9, 2, 6, 9, 3, 15, 3, 4, 3, 11, 6, 1, 2, 3, 3,
- 1, 5, 1, 2, 3, 3, 1, 3, 11, 9, 3, 9, 6, 4, 6, 3,
- 5, 6, 1, 8, 1, 5, 1, 5, 9, 3, 10, 2, 1, 3, 11, 3,
- 3, 9, 3, 7, 6, 8, 1, 3, 3, 2, 7, 6, 2, 1, 9, 8,
- 18, 6, 3, 7, 14, 1, 6, 3, 6, 3, 2, 1, 8, 15, 4, 12,
- 3, 15, 5, 1, 9, 2, 3, 6, 4, 11, 1, 3, 11, 9, 1, 5,
- 1, 5, 15, 1, 14, 3, 7, 8, 3, 10, 8, 1, 3, 2, 16, 2,
- 1, 2, 3, 1, 6, 2, 3, 3, 6, 1, 3, 2, 3, 4, 3, 2,
- 10, 2, 16, 5, 4, 8, 1, 11, 1, 2, 3, 4, 3, 8, 7, 2,
- 9, 4, 2, 10, 3, 6, 6, 3, 5, 1, 5, 1, 6, 14, 6, 9,
- 1, 9, 5, 4, 5, 24, 1, 2, 3, 4, 5, 1, 5, 15, 1, 18,
- 3, 5, 3, 1, 9, 2, 3, 4, 8, 7, 8, 3, 7, 2, 10, 2,
- 3, 1, 5, 6, 1, 3, 6, 3, 3, 2, 6, 1, 3, 2, 6, 3,
- 4, 2, 1, 3, 9, 5, 3, 4, 6, 3, 11, 1, 3, 6, 9, 2,
- 7, 3, 2, 10, 3, 8, 4, 2, 4, 11, 4, 6, 3, 3, 8, 6,
- 9, 15, 4, 2, 1, 2, 3, 13, 2, 7, 12, 11, 3, 1, 3, 5,
- 3, 7, 3, 3, 6, 5, 3, 1, 6, 5, 6, 4, 9, 9, 5, 3,
- 4, 8, 3, 3, 4, 8, 10, 2, 1, 5, 1, 5, 6, 3, 4, 3,
- 5, 10, 5, 9, 13, 2, 3, 15, 1, 2, 4, 3, 6, 6, 9, 2,
- 4, 11, 3, 1, 6, 17, 3, 9, 6, 3, 1, 14, 7, 8, 7, 2,
- 7, 6, 2, 3, 3, 1, 18, 2, 3, 10, 6, 12, 3, 11, 1, 8,
- 9, 6, 6, 9, 1, 3, 3, 3, 2, 3, 7, 2, 1, 11, 4, 6,
- 3, 5, 3, 4, 6, 9, 6, 3, 5, 1, 11, 7, 3, 3, 2, 9,
- 3, 10, 11, 1, 6, 12, 2, 9, 9, 1, 11, 1, 2, 6, 4, 6,
- 5, 7, 2, 1, 9, 8, 19, 3, 3, 3, 6, 5, 3, 6, 4, 3,
- 2, 3, 7, 15, 3, 5, 4, 11, 3, 4, 6, 5, 1, 5, 1, 3,
- 5, 1, 5, 6, 9, 10, 3, 2, 4, 11, 3, 3, 15, 3, 7, 3,
- 6, 6, 3, 5, 1, 5, 15, 1, 8, 4, 2, 1, 3, 9, 2, 1,
- 3, 2, 13, 2, 4, 3, 5, 1, 2, 3, 4, 2, 3, 15, 6, 1,
- 3, 3, 2, 10, 11, 4, 2, 1, 2, 36, 4, 2, 4, 11, 1, 2,
- 7, 5, 1, 2, 10, 3, 5, 9, 3, 10, 8, 3, 4, 3, 2, 10,
- 6, 11, 1, 2, 1, 6, 5, 9, 1, 11, 3, 9, 15, 1, 5, 7,
- 5, 4, 8, 25, 3, 5, 4, 5, 6, 3, 9, 1, 11, 3, 1, 2,
- 3, 4, 3, 3, 5, 9, 1, 11, 1, 8, 7, 5, 3, 1, 6, 5,
- 10, 2, 7, 3, 2, 18, 1, 2, 3, 6, 1, 2, 7, 6, 3, 2,
- 3, 1, 3, 2, 10, 5, 1, 5, 3, 6, 1, 12, 6, 6, 3, 3,
- 2, 12, 1, 2, 12, 1, 3, 2, 3, 4, 8, 3, 1, 5, 6, 7,
- 3, 17, 3, 7, 3, 2, 1, 15, 11, 4, 2, 3, 4, 2, 1, 14,
- 1, 3, 2, 13, 9, 11, 1, 3, 8, 3, 1, 8, 6, 1, 6, 2,
- 3, 3, 7, 5, 3, 4, 6, 2, 9, 1, 5, 4, 8, 3, 3, 15,
- 1, 5, 9, 1, 5, 4, 2, 4, 6, 12, 20, 1, 6, 5, 3, 6,
- 1, 6, 2, 1, 2, 3, 9, 7, 6, 3, 2, 7, 15, 2, 4, 5,
- 4, 3, 5, 9, 4, 2, 7, 8, 3, 4, 2, 3, 1, 5, 1, 6,
- 2, 1, 2, 3, 4, 2, 3, 16, 12, 5, 4, 9, 5, 1, 3, 5,
- 1, 2, 9, 3, 6, 1, 8, 1, 11, 3, 3, 4, 9, 2, 9, 6,
- 4, 3, 2, 10, 3, 15, 11, 6, 1, 3, 9, 2, 31, 2, 1, 6,
- 3, 5, 1, 6, 6, 14, 1, 2, 7, 11, 3, 1, 3, 3, 5, 7,
- 2, 1, 5, 3, 4, 5, 7, 5, 3, 1, 6, 11, 9, 4, 5, 9,
- 6, 1, 6, 2, 6, 1, 5, 1, 3, 9, 3, 3, 17, 3, 1, 6,
- 2, 3, 9, 9, 1, 8, 3, 3, 4, 3, 5, 9, 4, 5, 4, 5,
- 1, 2, 9, 13, 6, 11, 1, 2, 1, 11, 3, 3, 7, 8, 3, 10,
- 5, 6, 1, 9, 21, 2, 12, 1, 3, 5, 6, 1, 3, 5, 4, 2,
- 3, 6, 6, 4, 2, 3, 6, 15, 10, 3, 12, 3, 5, 6, 1, 5,
- 10, 3, 3, 2, 6, 7, 5, 9, 6, 4, 3, 6, 2, 7, 5, 1,
- 6, 15, 8, 1, 6, 3, 2, 1, 2, 3, 13, 2, 9, 1, 2, 3,
- 7, 27, 3, 26, 1, 8, 3, 3, 6, 13, 2, 1, 3, 11, 3, 1,
- 6, 6, 3, 5, 9, 1, 6, 6, 5, 9, 6, 3, 4, 3, 5, 3,
- 4, 2, 1, 2, 10, 12, 3, 3, 5, 7, 5, 1, 11, 3, 7, 5,
- 13, 2, 9, 4, 6, 6, 5, 6, 3, 4, 8, 3, 4, 3, 3, 11,
- 1, 5, 10, 5, 3, 22, 9, 3, 5, 1, 2, 3, 7, 2, 13, 2,
- 1, 6, 5, 4, 2, 4, 6, 2, 6, 4, 11, 4, 3, 5, 9, 3,
- 3, 4, 3, 6, 2, 4, 9, 5, 6, 3, 6, 1, 3, 2, 1, 8,
- 6, 6, 7, 5, 7, 3, 5, 6, 1, 6, 3, 2, 3, 1, 6, 2,
- 13, 3, 9, 3, 5, 3, 1, 9, 5, 4, 2, 13, 5, 10, 3, 8,
- 10, 6, 5, 4, 5, 1, 8, 3, 10, 5, 10, 2, 15, 1, 2, 4,
- 8, 1, 9, 2, 1, 3, 5, 9, 6, 7, 9, 3, 8, 10, 3, 2,
- 4, 3, 2, 3, 6, 4, 5, 1, 6, 3, 2, 1, 3, 5, 1, 8,
- 6, 7, 5, 3, 4, 3, 14, 1, 3, 9, 15, 17, 1, 8, 6, 1,
- 9, 8, 3, 4, 5, 4, 5, 4, 5, 22, 3, 3, 2, 10, 2, 1,
- 2, 7, 14, 4, 3, 8, 7, 15, 3, 15, 2, 7, 5, 3, 3, 4,
- 2, 9, 6, 3, 1, 11, 6, 4, 3, 6, 2, 7, 2, 3, 1, 2,
- 9, 10, 3, 8, 19, 8, 1, 2, 3, 1, 20, 21, 7, 2, 3, 1,
- 12, 5, 3, 1, 9, 5, 6, 1, 8, 1, 3, 8, 3, 4, 2, 1,
- 5, 3, 4, 5, 1, 9, 8, 4, 6, 9, 6, 3, 6, 5, 3, 3
+ * If out of two consecutive differencials only the second one exceeds 0xf,
+ * the first one still is put into the array in its own byte prepended by a
+ * zero.
+ */
+const uint8_t PRIME_DELTAS[] = {
+ 1, 18, 18, 18, 49, 50, 18, 51, 19, 33, 50, 52,
+ 33, 33, 39, 35, 21, 19, 50, 51, 21, 18, 22, 98,
+ 18, 49, 83, 51, 19, 33, 87, 33, 39, 53, 18, 52,
+ 51, 35, 66, 69, 21, 19, 35, 66, 18, 100, 36, 35,
+ 97, 147, 83, 49, 53, 51, 19, 50, 22, 81, 35, 49,
+ 98, 52, 84, 84, 51, 36, 50, 66, 117, 97, 81, 33,
+ 87, 33, 39, 33, 42, 36, 84, 35, 55, 35, 52, 54,
+ 35, 21, 19, 81, 81, 57, 33, 35, 52, 51, 177, 84,
+ 83, 52, 98, 51, 19, 101, 145, 35, 19, 33, 38, 19,
+ 0, 34, 51, 73, 87, 33, 35, 66, 19, 101, 18, 18,
+ 54, 100, 99, 35, 66, 66, 114, 49, 35, 19, 90, 50,
+ 28, 33, 86, 21, 67, 51, 147, 33, 101, 100, 135, 50,
+ 18, 21, 99, 57, 24, 27, 52, 50, 18, 67, 81, 87,
+ 83, 97, 33, 86, 24, 19, 33, 84, 156, 35, 72, 18,
+ 72, 18, 67, 50, 97, 179, 19, 35, 115, 33, 50, 54,
+ 51, 114, 54, 67, 45, 149, 66, 49, 59, 97, 132, 38,
+ 117, 18, 67, 50, 18, 52, 33, 53, 21, 66, 117, 97,
+ 50, 24, 114, 52, 50, 148, 83, 52, 86, 114, 51, 30,
+ 21, 66, 114, 70, 54, 35, 165, 24, 210, 22, 50, 99,
+ 66, 75, 18, 22, 225, 51, 50, 49, 98, 97, 81, 129,
+ 131, 168, 66, 18, 27, 70, 53, 18, 49, 53, 22, 81,
+ 87, 50, 52, 51, 134, 18, 115, 36, 84, 51, 179, 21,
+ 114, 57, 21, 114, 21, 114, 73, 35, 18, 49, 98, 171,
+ 97, 35, 49, 59, 19, 131, 97, 54, 129, 35, 114, 25,
+ 197, 49, 81, 81, 83, 21, 21, 52, 245, 21, 67, 89,
+ 54, 97, 147, 35, 57, 21, 115, 33, 44, 22, 56, 67,
+ 57, 129, 35, 19, 53, 54, 105, 19, 41, 76, 33, 35,
+ 22, 39, 245, 54, 115, 86, 18, 52, 53, 18, 115, 50,
+ 49, 81, 134, 73, 35, 97, 51, 62, 55, 36, 84, 105,
+ 33, 44, 99, 24, 51, 117, 114, 243, 51, 67, 33, 99,
+ 33, 59, 49, 41, 18, 97, 50, 211, 50, 69, 0, 32,
+ 129, 50, 18, 21, 115, 36, 83, 162, 19, 242, 69, 51,
+ 67, 98, 49, 50, 49, 81, 131, 162, 103, 227, 162, 148,
+ 50, 55, 51, 81, 86, 69, 21, 70, 92, 18, 67, 36,
+ 149, 51, 19, 86, 21, 51, 52, 53, 49, 51, 53, 76,
+ 59, 25, 36, 95, 73, 33, 83, 19, 41, 70, 152, 49,
+ 99, 81, 81, 53, 114, 193, 129, 81, 90, 33, 36, 131,
+ 49, 104, 66, 63, 21, 19, 35, 52, 50, 99, 70, 39,
+ 101, 195, 99, 27, 73, 83, 114, 19, 84, 50, 63, 117,
+ 22, 81, 129, 156, 147, 137, 49, 146, 49, 84, 83, 52,
+ 35, 21, 22, 35, 49, 98, 121, 35, 162, 67, 36, 39,
+ 50, 118, 33, 242, 195, 54, 103, 50, 18, 147, 100, 50,
+ 97, 111, 129, 59, 115, 86, 49, 36, 83, 60, 115, 36,
+ 105, 81, 81, 35, 163, 39, 33, 39, 54, 197, 52, 81,
+ 242, 49, 98, 115, 0, 34, 100, 53, 18, 165, 72, 21,
+ 114, 22, 56, 52, 36, 35, 67, 54, 50, 51, 73, 42,
+ 38, 21, 49, 86, 18, 163, 243, 36, 86, 49, 225, 50,
+ 24, 97, 53, 76, 99, 147, 39, 50, 100, 54, 35, 99,
+ 97, 138, 33, 89, 66, 114, 19, 179, 115, 53, 49, 81,
+ 33, 177, 35, 54, 55, 86, 52, 0, 4, 0, 36, 118,
+ 50, 49, 99, 104, 21, 75, 22, 50, 57, 22, 50, 100,
+ 54, 35, 99, 22, 98, 115, 131, 21, 73, 0, 6, 0,
+ 34, 30, 27, 49, 86, 19, 36, 179, 21, 66, 52, 38,
+ 150, 162, 51, 66, 24, 97, 84, 81, 35, 118, 180, 225,
+ 42, 33, 39, 86, 22, 129, 228, 180, 35, 55, 36, 99,
+ 50, 162, 145, 99, 35, 121, 84, 0, 10, 0, 32, 53,
+ 51, 19, 131, 22, 62, 21, 72, 52, 53, 202, 81, 81,
+ 98, 58, 33, 105, 81, 81, 42, 141, 36, 50, 99, 70,
+ 99, 36, 177, 135, 83, 102, 115, 42, 38, 49, 51, 132,
+ 177, 228, 50, 162, 108, 162, 69, 24, 22, 0, 12, 0,
+ 34, 18, 54, 51, 67, 33, 60, 42, 83, 55, 35, 49,
+ 99, 81, 83, 162, 210, 19, 177, 194, 49, 35, 195, 66,
+ 0, 2, 0, 34, 52, 134, 21, 21, 52, 36, 107, 55,
+ 45, 33, 101, 66, 70, 39, 56, 52, 35, 52, 53, 97,
+ 51, 132, 51, 101, 19, 146, 51, 54, 148, 53, 73, 39,
+ 57, 84, 86, 19, 102, 0, 36, 35, 66, 49, 41, 99,
+ 67, 50, 145, 33, 194, 51, 127, 50, 54, 58, 36, 36,
+ 51, 47, 21, 100, 84, 195, 98, 114, 49, 231, 129, 99,
+ 42, 83, 51, 69, 103, 87, 135, 87, 56, 52, 56, 165,
+ 19, 33, 38, 21, 19, 179, 18, 148, 84, 177, 89, 114,
+ 18, 145, 35, 69, 31, 47, 21, 25, 41, 55, 81, 42,
+ 0, 36, 50, 55, 42, 87, 179, 31, 101, 145, 39, 59,
+ 145, 99, 36, 36, 53, 22, 149, 120, 114, 51, 19, 33,
+ 225, 227, 18, 55, 38, 120, 114, 52, 50, 51, 52, 36,
+ 39, 132, 50, 100, 129, 84, 35, 211, 84, 35, 103, 242,
+ 123, 70, 35, 69, 55, 83, 21, 102, 115, 57, 83, 73,
+ 35, 19, 81, 84, 51, 81, 149, 22, 35, 69, 103, 98,
+ 69, 51, 162, 120, 117, 69, 97, 147, 101, 97, 33, 99,
+ 36, 0, 4, 0, 44, 33, 33, 86, 51, 114, 51, 52,
+ 0, 6, 0, 36, 146, 49, 99, 51, 39, 182, 25, 83,
+ 220, 33, 33, 39, 35, 52, 134, 0, 2, 0, 42, 33,
+ 44, 51, 25, 39, 62, 151, 53, 97, 54, 243, 35, 55,
+ 33, 194, 51, 213, 147, 67, 63, 38, 97, 129, 50, 105,
+ 19, 45, 99, 98, 204, 99, 22, 228, 35, 97, 147, 35,
+ 58, 129, 51, 149, 49, 36, 51, 200, 52, 83, 123, 72,
+ 49, 98, 27, 73, 0, 34, 19, 146, 51, 69, 73, 50,
+ 18, 72, 22, 99, 146, 51, 49, 54, 90, 105, 35, 24,
+ 21, 114, 241, 86, 28, 56, 69, 22, 179, 24, 165, 22,
+ 105, 86, 49, 81, 53, 145, 99, 35, 28, 225, 33, 81,
+ 134, 75, 19, 33, 83, 166, 84, 99, 51, 41, 18, 105,
+ 22, 50, 24, 102, 114, 73, 38, 115, 50, 67, 42, 101,
+ 114, 24, 22, 242, 60, 172, 84, 101, 99, 102, 52, 135,
+ 50, 0, 6, 0, 36, 165, 246, 18, 30, 103, 59, 66,
+ 147, 121, 35, 19, 0, 34, 145, 131, 145, 194, 19, 99,
+ 101, 67, 134, 69, 0, 14, 0, 40, 49, 50, 103, 33,
+ 33, 36, 53, 51, 19, 51, 99, 197, 21, 54, 51, 115,
+ 0, 6, 0, 52, 163, 81, 84, 86, 97, 50, 120, 70,
+ 59, 21, 67, 177, 179, 69, 102, 21, 54, 18, 117, 19,
+ 146, 100, 150, 51, 35, 55, 33, 102, 35, 153, 97, 134,
+ 73, 93, 35, 67, 50, 21, 162, 52, 42, 81, 0, 34,
+ 18, 193, 102, 83, 22, 243, 104, 97, 185, 103, 81, 102,
+ 33, 35, 97, 137, 0, 2, 0, 40, 72, 52, 81, 41,
+ 69, 70, 41, 25, 81, 33, 36, 225, 59, 99, 121, 35,
+ 67, 53, 66, 25, 83, 171, 67, 242, 18, 147, 241, 36,
+ 50, 54, 0, 14, 0, 34, 115, 33, 50, 114, 19, 225,
+ 35, 69, 21, 21, 18, 241, 102, 89, 103, 81, 99, 83,
+ 118, 39, 41, 21, 66, 69, 105, 148, 57, 135, 51, 87,
+ 35, 22, 98, 51, 97, 129, 99, 39, 50, 22, 146, 0,
+ 36, 150, 97, 33, 36, 98, 0, 36, 57, 22, 83, 108,
+ 67, 56, 97, 149, 165, 19, 146, 0, 2, 0, 40, 49,
+ 129, 36, 149, 99, 21, 66, 54, 21, 148, 50, 162, 0,
+ 6, 0, 36, 49, 83, 195, 120, 57, 21, 165, 67, 35,
+ 21, 22, 33, 36, 83, 105, 118, 132, 56, 66, 19, 156,
+ 149, 97, 39, 83, 51, 150, 30, 151, 134, 124, 107, 49,
+ 84, 33, 39, 99, 35, 114, 18, 243, 19, 81, 251, 18,
+ 52, 51, 134, 99, 66, 28, 98, 52, 51, 81, 54, 231,
+ 50, 100, 54, 35, 115, 101, 51, 67, 50, 18, 70, 39,
+ 149, 24, 58, 53, 66, 0, 30, 0, 36, 100, 182, 19,
+ 104, 51, 25, 45, 36, 149, 69, 55, 42, 185, 100, 230,
+ 51, 67, 108, 135, 39, 99, 86, 163, 36, 150, 149, 18,
+ 165, 114, 49, 92, 145, 42, 135, 87, 50, 58, 53, 49,
+ 99, 245, 67, 35, 0, 8, 0, 40, 18, 22, 146, 52,
+ 83, 153, 22, 132, 50, 51, 0, 2, 0, 52, 114, 168,
+ 18, 54, 19, 102, 50, 117, 51, 117, 120, 67, 98, 75,
+ 49, 155, 49, 147, 135, 83, 97, 50, 73, 104, 18, 114,
+ 70, 111, 132, 33, 59, 100, 83, 51, 115, 149, 97, 81,
+ 45, 38, 66, 148, 87, 131, 52, 83, 67, 101, 165, 66,
+ 109, 146, 105, 63, 52, 59, 97, 35, 49, 81, 35, 49,
+ 59, 147, 150, 70, 53, 97, 129, 81, 89, 58, 33, 59,
+ 51, 147, 118, 129, 51, 39, 98, 25, 0, 16, 0, 36,
+ 99, 126, 22, 54, 50, 24, 244, 195, 245, 25, 35, 100,
+ 177, 59, 145, 81, 95, 30, 55, 131, 168, 19, 0, 4,
+ 0, 32, 33, 35, 22, 35, 54, 19, 35, 67, 42, 0,
+ 4, 0, 32, 84, 129, 177, 35, 67, 135, 41, 66, 163,
+ 102, 53, 21, 22, 230, 145, 149, 69, 0, 48, 18, 52,
+ 81, 95, 0, 2, 0, 36, 53, 49, 146, 52, 135, 131,
+ 114, 162, 49, 86, 19, 99, 50, 97, 50, 99, 66, 19,
+ 149, 52, 99, 177, 54, 146, 115, 42, 56, 66, 75, 70,
+ 51, 134, 159, 66, 18, 61, 39, 203, 49, 53, 55, 51,
+ 101, 49, 101, 100, 153, 83, 72, 51, 72, 162, 21, 21,
+ 99, 67, 90, 89, 210, 63, 18, 67, 102, 146, 75, 49,
+ 0, 12, 0, 34, 57, 99, 30, 120, 114, 118, 35, 49,
+ 0, 36, 35, 166, 195, 177, 137, 102, 145, 51, 50, 55,
+ 33, 180, 99, 83, 70, 150, 53, 27, 115, 50, 147, 171,
+ 22, 194, 153, 27, 18, 100, 101, 114, 25, 0, 16, 0,
+ 38, 51, 54, 83, 100, 50, 55, 243, 84, 179, 70, 81,
+ 81, 53, 21, 105, 163, 36, 179, 63, 55, 54, 99, 81,
+ 95, 24, 66, 19, 146, 19, 45, 36, 53, 18, 52, 35,
+ 246, 19, 50, 171, 66, 18, 0, 72, 66, 75, 18, 117,
+ 18, 163, 89, 58, 131, 67, 42, 107, 18, 22, 89, 27,
+ 57, 241, 87, 84, 0, 16, 0, 50, 53, 69, 99, 145,
+ 179, 18, 52, 51, 89, 27, 24, 117, 49, 101, 162, 115,
+ 0, 4, 0, 36, 18, 54, 18, 118, 50, 49, 50, 165,
+ 21, 54, 28, 102, 51, 44, 18, 193, 50, 52, 131, 21,
+ 103, 0, 6, 0, 34, 55, 50, 31, 180, 35, 66, 30,
+ 19, 45, 155, 19, 131, 24, 97, 98, 51, 117, 52, 98,
+ 145, 84, 131, 63, 21, 145, 84, 36, 108, 0, 40, 22,
+ 83, 97, 98, 18, 57, 118, 50, 127, 36, 84, 53, 148,
+ 39, 131, 66, 49, 81, 98, 18, 52, 35, 0, 32, 197,
+ 73, 81, 53, 18, 147, 97, 129, 179, 52, 146, 150, 67,
+ 42, 63, 182, 19, 146, 0, 62, 33, 99, 81, 102, 225,
+ 39, 179, 19, 53, 114, 21, 52, 87, 83, 22, 185, 69,
+ 150, 22, 38, 21, 19, 147, 0, 6, 0, 34, 49, 98,
+ 57, 145, 131, 52, 53, 148, 84, 81, 41, 214, 177, 33,
+ 179, 55, 131, 165, 97, 0, 18, 0, 42, 44, 19, 86,
+ 19, 84, 35, 102, 66, 54, 250, 60, 53, 97, 90, 51,
+ 38, 117, 150, 67, 98, 117, 22, 248, 22, 50, 18, 61,
+ 41, 18, 55, 0, 54, 0, 6, 0, 52, 24, 51, 109,
+ 33, 59, 49, 102, 53, 145, 102, 89, 99, 67, 83, 66,
+ 18, 172, 51, 87, 81, 179, 117, 210, 148, 102, 86, 52,
+ 131, 67, 59, 21, 165, 0, 6, 0, 44, 147, 81, 35,
+ 114, 210, 22, 84, 36, 98, 100, 180, 53, 147, 52, 54,
+ 36, 149, 99, 97, 50, 24, 102, 117, 115, 86, 22, 50,
+ 49, 98, 211, 147, 83, 25, 84, 45, 90, 56, 166, 84,
+ 81, 131, 165, 162, 241, 36, 129, 146, 19, 89, 103, 147,
+ 138, 50, 67, 35, 100, 81, 99, 33, 53, 24, 103, 83,
+ 67, 225, 57, 0, 30, 0, 34, 24, 97, 152, 52, 84,
+ 84, 0, 10, 0, 44, 51, 42, 33, 39, 228, 56, 127,
+ 63, 39, 83, 52, 41, 99, 27, 100, 54, 39, 35, 18,
+ 154, 56, 0, 38, 129, 35, 0, 2, 0, 40, 0, 42,
+ 114, 49, 197, 49, 149, 97, 129, 56, 52, 33, 83, 69,
+ 25, 132, 105, 99, 101, 51,
};
static uint32_t bn_mod_word16(const struct LITE_BIGNUM *p, uint16_t word)
@@ -911,7 +1119,7 @@ static int bn_probable_prime(const struct LITE_BIGNUM *p)
}
/* y = a ^ r mod p */
- bn_mont_modexp(&y, &A, &r, p);
+ bn_modexp(&y, &A, &r, p);
if (bn_eq(&y, &ONE))
continue;
bn_add(&y, &ONE);
@@ -922,7 +1130,7 @@ static int bn_probable_prime(const struct LITE_BIGNUM *p)
/* y = y ^ 2 mod p */
for (i = 0; i < s - 1; i++) {
bn_copy(&A, &y);
- bn_mont_modexp(&y, &A, &TWO, p);
+ bn_modexp(&y, &A, &TWO, p);
if (bn_eq(&y, &ONE))
return 0;
@@ -942,6 +1150,27 @@ static int bn_probable_prime(const struct LITE_BIGNUM *p)
return 1;
}
+/* #define PRINT_PRIMES to enable printing predefined prime numbers' set. */
+static void print_primes(uint16_t prime)
+{
+#ifdef PRINT_PRIMES
+ static uint16_t num_per_line;
+ static uint16_t max_printed;
+
+ if (prime <= max_printed)
+ return;
+
+ if (!(num_per_line++ % 8)) {
+ if (num_per_line == 1)
+ ccprintf("Prime numbers:");
+ ccprintf("\n");
+ cflush();
+ }
+ max_printed = prime;
+ ccprintf(" %6d", prime);
+#endif
+}
+
int DCRYPTO_bn_generate_prime(struct LITE_BIGNUM *p)
{
int i;
@@ -960,17 +1189,34 @@ int DCRYPTO_bn_generate_prime(struct LITE_BIGNUM *p)
/* Save on trial division by marking known composites. */
bn_init(&composites, composites_buf, sizeof(composites_buf));
- for (i = 0; i < sizeof(PRIME_DELTAS) / sizeof(PRIME_DELTAS[0]); i++) {
+ for (i = 0; i < ARRAY_SIZE(PRIME_DELTAS); i++) {
uint16_t rem;
+ uint8_t unpacked_deltas[2];
+ uint8_t packed_deltas = PRIME_DELTAS[i];
+ int k;
+ int m;
+
+ if (packed_deltas) {
+ unpacked_deltas[0] = (packed_deltas >> 4) << 1;
+ unpacked_deltas[1] = (packed_deltas & 0xf) << 1;
+ m = 2;
+ } else {
+ i += 1;
+ unpacked_deltas[0] = PRIME_DELTAS[i];
+ m = 1;
+ }
- prime += (PRIME_DELTAS[i] << 1);
- rem = bn_mod_word16(p, prime);
- /* Skip marking odd offsets (i.e. even candidates). */
- for (j = (rem == 0) ? 0 : prime - rem;
- j < bn_bits(&composites) << 1;
- j += prime) {
- if ((j & 1) == 0)
- bn_set_bit(&composites, j >> 1);
+ for (k = 0; k < m; k++) {
+ prime += unpacked_deltas[k];
+ print_primes(prime);
+ rem = bn_mod_word16(p, prime);
+ /* Skip marking odd offsets (i.e. even candidates). */
+ for (j = (rem == 0) ? 0 : prime - rem;
+ j < bn_bits(&composites) << 1;
+ j += prime) {
+ if ((j & 1) == 0)
+ bn_set_bit(&composites, j >> 1);
+ }
}
}
@@ -995,6 +1241,6 @@ int DCRYPTO_bn_generate_prime(struct LITE_BIGNUM *p)
}
}
- memset(composites_buf, 0, sizeof(composites_buf));
+ always_memset(composites_buf, 0, sizeof(composites_buf));
return 0;
}
diff --git a/chip/g/dcrypto/compare.c b/chip/g/dcrypto/compare.c
new file mode 100644
index 0000000000..db6193752b
--- /dev/null
+++ b/chip/g/dcrypto/compare.c
@@ -0,0 +1,20 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "dcrypto.h"
+
+/* Constant time comparator. */
+int DCRYPTO_equals(const void *a, const void *b, size_t len)
+{
+ size_t i;
+ const uint8_t *pa = a;
+ const uint8_t *pb = b;
+ uint8_t diff = 0;
+
+ for (i = 0; i < len; i++)
+ diff |= pa[i] ^ pb[i];
+
+ return !diff;
+}
diff --git a/chip/g/dcrypto/dcrypto.h b/chip/g/dcrypto/dcrypto.h
index aaa4b9d0cf..51c142fa92 100644
--- a/chip/g/dcrypto/dcrypto.h
+++ b/chip/g/dcrypto/dcrypto.h
@@ -9,11 +9,14 @@
#ifndef __EC_CHIP_G_DCRYPTO_DCRYPTO_H
#define __EC_CHIP_G_DCRYPTO_DCRYPTO_H
-/* TODO(vbendeb) don't forget to disable this for prod builds. */
+#if defined(CR50_DEV) && (CR50_DEV) > 1
#define CRYPTO_TEST_SETUP
+#endif
#include "internal.h"
+#include "crypto_api.h"
+
#include <stddef.h>
#include "cryptoc/hmac.h"
@@ -32,12 +35,17 @@ enum encrypt_mode {
enum hashing_mode {
HASH_SHA1 = 0,
- HASH_SHA256 = 1
+ HASH_SHA256 = 1,
+ HASH_SHA384 = 2, /* Only supported for PKCS#1 signing */
+ HASH_SHA512 = 3, /* Only supported for PKCS#1 signing */
+ HASH_NULL = 4 /* Only supported for PKCS#1 signing */
};
/*
* AES implementation, based on a hardware AES block.
*/
+#define AES256_BLOCK_CIPHER_KEY_SIZE 32
+
int DCRYPTO_aes_init(const uint8_t *key, uint32_t key_len, const uint8_t *iv,
enum cipher_mode c_mode, enum encrypt_mode e_mode);
int DCRYPTO_aes_block(const uint8_t *in, uint8_t *out);
@@ -47,6 +55,48 @@ void DCRYPTO_aes_read_iv(uint8_t *iv);
int DCRYPTO_aes_ctr(uint8_t *out, const uint8_t *key, uint32_t key_bits,
const uint8_t *iv, const uint8_t *in, size_t in_len);
+/* AES-GCM-128 */
+struct GCM_CTX {
+ union {
+ uint32_t d[4];
+ uint8_t c[16];
+ } block, Ej0;
+
+ uint64_t aad_len;
+ uint64_t count;
+ size_t remainder;
+};
+
+/* Initialize the GCM context structure. */
+void DCRYPTO_gcm_init(struct GCM_CTX *ctx, const uint8_t *key,
+ const uint8_t *iv, size_t iv_len);
+/* Additional authentication data to include in the tag calculation. */
+void DCRYPTO_gcm_aad(struct GCM_CTX *ctx, const uint8_t *aad_data, size_t len);
+/* Encrypt & decrypt return the number of bytes written to out
+ * (always an integral multiple of 16), or -1 on error. These functions
+ * may be called repeatedly with incremental data.
+ *
+ * NOTE: if in_len is not a integral multiple of 16, then out_len must
+ * be atleast in_len - (in_len % 16) + 16 bytes.
+ */
+int DCRYPTO_gcm_encrypt(struct GCM_CTX *ctx, uint8_t *out, size_t out_len,
+ const uint8_t *in, size_t in_len);
+int DCRYPTO_gcm_decrypt(struct GCM_CTX *ctx, uint8_t *out, size_t out_len,
+ const uint8_t *in, size_t in_len);
+/* Encrypt & decrypt a partial final block, if any. These functions
+ * return the number of bytes written to out (<= 15), or -1 on error.
+ */
+int DCRYPTO_gcm_encrypt_final(struct GCM_CTX *ctx,
+ uint8_t *out, size_t out_len);
+int DCRYPTO_gcm_decrypt_final(struct GCM_CTX *ctx,
+ uint8_t *out, size_t out_len);
+/* Compute the tag over AAD + encrypt or decrypt data, and return the
+ * number of bytes written to tag. Returns -1 on error.
+ */
+int DCRYPTO_gcm_tag(struct GCM_CTX *ctx, uint8_t *tag, size_t tag_len);
+/* Cleanup secrets. */
+void DCRYPTO_gcm_finish(struct GCM_CTX *ctx);
+
/*
* SHA implementation. This abstraction is backed by either a
* software or hardware implementation.
@@ -58,11 +108,16 @@ int DCRYPTO_aes_ctr(uint8_t *out, const uint8_t *key, uint32_t key_bits,
*/
void DCRYPTO_SHA1_init(SHA_CTX *ctx, uint32_t sw_required);
void DCRYPTO_SHA256_init(LITE_SHA256_CTX *ctx, uint32_t sw_required);
+void DCRYPTO_SHA384_init(LITE_SHA384_CTX *ctx);
+void DCRYPTO_SHA512_init(LITE_SHA512_CTX *ctx);
const uint8_t *DCRYPTO_SHA1_hash(const void *data, uint32_t n,
uint8_t *digest);
const uint8_t *DCRYPTO_SHA256_hash(const void *data, uint32_t n,
- uint8_t *digest);
-
+ uint8_t *digest);
+const uint8_t *DCRYPTO_SHA384_hash(const void *data, uint32_t n,
+ uint8_t *digest);
+const uint8_t *DCRYPTO_SHA512_hash(const void *data, uint32_t n,
+ uint8_t *digest);
/*
* HMAC.
*/
@@ -79,8 +134,17 @@ void DCRYPTO_bn_wrap(struct LITE_BIGNUM *b, void *buf, size_t len);
* RSA.
*/
-/* Largest supported key size, 2048-bits. */
-#define RSA_MAX_BYTES 256
+/* Largest supported key size for signing / encryption: 2048-bits.
+ * Verification is a special case and supports 4096-bits (signing /
+ * decryption could also support 4k-RSA, but is disabled since support
+ * is not required, and enabling support would result in increased
+ * stack usage for all key sizes.)
+ */
+#define RSA_BYTES_2K 256
+#define RSA_BYTES_4K 512
+#define RSA_WORDS_2K (RSA_BYTES_2K / sizeof(uint32_t))
+#define RSA_WORDS_4K (RSA_BYTES_4K / sizeof(uint32_t))
+#define RSA_MAX_BYTES RSA_BYTES_2K
#define RSA_MAX_WORDS (RSA_MAX_BYTES / sizeof(uint32_t))
#define RSA_F4 65537
@@ -135,8 +199,13 @@ int DCRYPTO_p256_base_point_mul(p256_int *out_x, p256_int *out_y,
int DCRYPTO_p256_point_mul(p256_int *out_x, p256_int *out_y,
const p256_int *n, const p256_int *in_x,
const p256_int *in_y);
+/*
+ * Produce uniform private key from seed.
+ * If x or y is NULL, the public key part is not computed.
+ * Returns !0 on success.
+ */
int DCRYPTO_p256_key_from_bytes(p256_int *x, p256_int *y, p256_int *d,
- const uint8_t key_bytes[P256_NBYTES]);
+ const uint8_t bytes[P256_NBYTES]);
/* P256 based integration encryption (DH+AES128+SHA256). */
/* Authenticated data may be provided, where the first auth_data_len
* bytes of in will be authenticated but not encrypted. */
@@ -169,11 +238,84 @@ int DCRYPTO_bn_generate_prime(struct LITE_BIGNUM *p);
void DCRYPTO_bn_wrap(struct LITE_BIGNUM *b, void *buf, size_t len);
void DCRYPTO_bn_mul(struct LITE_BIGNUM *c, const struct LITE_BIGNUM *a,
const struct LITE_BIGNUM *b);
+int DCRYPTO_bn_div(struct LITE_BIGNUM *quotient, struct LITE_BIGNUM *remainder,
+ const struct LITE_BIGNUM *input,
+ const struct LITE_BIGNUM *divisor);
+
+/*
+ * ASN.1 DER
+ */
+size_t DCRYPTO_asn1_sigp(uint8_t *buf, const p256_int *r, const p256_int *s);
+size_t DCRYPTO_asn1_pubp(uint8_t *buf, const p256_int *x, const p256_int *y);
/*
* X509.
*/
int DCRYPTO_x509_verify(const uint8_t *cert, size_t len,
const struct RSA *ca_pub_key);
+int DCRYPTO_x509_gen_u2f_cert(const p256_int *d, const p256_int *pk_x,
+ const p256_int *pk_y, const p256_int *serial,
+ uint8_t *cert, const int n);
+
+/*
+ * Memory related functions.
+ */
+int DCRYPTO_equals(const void *a, const void *b, size_t len);
+
+/*
+ * Key-ladder and application key related functions.
+ */
+enum dcrypto_appid {
+ RESERVED = 0,
+ NVMEM = 1,
+ U2F_ATTEST = 2,
+ U2F_ORIGIN = 3,
+ U2F_WRAP = 4,
+ PERSO_AUTH = 5,
+ PINWEAVER = 6,
+ /* This enum value should not exceed 7. */
+};
+
+struct APPKEY_CTX {
+};
+
+int DCRYPTO_ladder_compute_frk2(size_t major_fw_version, uint8_t *frk2);
+int DCRYPTO_ladder_random(void *output);
+
+int DCRYPTO_appkey_init(enum dcrypto_appid id, struct APPKEY_CTX *ctx);
+void DCRYPTO_appkey_finish(struct APPKEY_CTX *ctx);
+int DCRYPTO_appkey_derive(enum dcrypto_appid appid, const uint32_t input[8],
+ uint32_t output[8]);
+
+/* Number of bytes in the salt object. */
+#define DCRYPTO_CIPHER_SALT_SIZE 16
+BUILD_ASSERT(DCRYPTO_CIPHER_SALT_SIZE == CIPHER_SALT_SIZE);
+
+/*
+ * Encrypt/decrypt a flat blob.
+ *
+ * Encrypt or decrypt the input buffer, and write the correspondingly
+ * ciphered output to out. The number of bytes produced is equal to
+ * the number of input bytes. Note that the input and output pointers
+ * MUST be word-aligned.
+ *
+ * This API is expected to be applied to a single contiguous region.
+
+ * WARNING: A given salt/"in" pair MUST be unique, i.e. re-using a
+ * salt with a logically different input buffer is catastrophic. An
+ * example of a suitable salt is one that is derived from "in", e.g. a
+ * digest of the input data.
+ *
+ * @param appid the application-id of the calling context.
+ * @param salt pointer to a unique value to be associated with this blob,
+ * used for derivation of the proper IV, the size of the value
+ * is as defined by DCRYPTO_CIPHER_SALT_SIZE above.
+ * @param out Destination pointer where to write plaintext / ciphertext.
+ * @param in Source pointer where to read ciphertext / plaintext.
+ * @param len Number of bytes to read from in / write to out.
+ * @return non-zero on success, and zero otherwise.
+ */
+int DCRYPTO_app_cipher(enum dcrypto_appid appid, const void *salt,
+ void *out, const void *in, size_t len);
#endif /* ! __EC_CHIP_G_DCRYPTO_DCRYPTO_H */
diff --git a/chip/g/dcrypto/dcrypto_bn.c b/chip/g/dcrypto/dcrypto_bn.c
new file mode 100644
index 0000000000..5b6bbf8451
--- /dev/null
+++ b/chip/g/dcrypto/dcrypto_bn.c
@@ -0,0 +1,733 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "dcrypto.h"
+#include "internal.h"
+#include "registers.h"
+#include "trng.h"
+
+/* Firmware blob for crypto accelerator */
+
+/* AUTO-GENERATED. DO NOT MODIFY. */
+/* clang-format off */
+static const uint32_t IMEM_dcrypto[] = {
+/* @0x0: function tag[1] { */
+#define CF_tag_adr 0
+ 0xf8000001, /* sigini #1 */
+/* } */
+/* @0x1: function d0inv[14] { */
+#define CF_d0inv_adr 1
+ 0x4c000000, /* xor r0, r0, r0 */
+ 0x80000001, /* movi r0.0l, #1 */
+ 0x7c740000, /* mov r29, r0 */
+ 0x05100008, /* loop #256 ( */
+ 0x5807bc00, /* mul128 r1, r28l, r29l */
+ 0x588bbc00, /* mul128 r2, r28u, r29l */
+ 0x50044110, /* add r1, r1, r2 << 128 */
+ 0x590bbc00, /* mul128 r2, r28l, r29u */
+ 0x50044110, /* add r1, r1, r2 << 128 */
+ 0x40040100, /* and r1, r1, r0 */
+ 0x44743d00, /* or r29, r29, r1 */
+ 0x50000000, /* add r0, r0, r0 */
+ /* ) */
+ 0x5477bf00, /* sub r29, r31, r29 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0xf: function selcxSub[10] { */
+#define CF_selcxSub_adr 15
+ 0x97800100, /* ldrfp r1 */
+ 0x95800000, /* lddmp r0 */
+ 0x540c6300, /* sub r3, r3, r3 */
+ 0x0600c005, /* loop *6 ( */
+ 0x8c081800, /* ld *2, *0++ */
+ 0x7c8c0000, /* ldr *3, *0 */
+ 0x54906200, /* subb r4, r2, r3 */
+ 0x66084408, /* selcx r2, r4, r2 */
+ 0x7ca00300, /* ldr *0++, *3 */
+ /* ) */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x19: function computeRR[41] { */
+#define CF_computeRR_adr 25
+ 0x4c7fff00, /* xor r31, r31, r31 */
+ 0x84004000, /* ldi r0, [#0] */
+ 0x95800000, /* lddmp r0 */
+ 0x4c0c6300, /* xor r3, r3, r3 */
+ 0x800cffff, /* movi r3.0l, #65535 */
+ 0x40040398, /* and r1, r3, r0 >> 192 */
+ 0x480c6000, /* not r3, r3 */
+ 0x400c0300, /* and r3, r3, r0 */
+ 0x500c2301, /* add r3, r3, r1 << 8 */
+ 0x94800300, /* ldlc r3 */
+ 0x80040005, /* movi r1.0l, #5 */
+ 0x81040003, /* movi r1.2l, #3 */
+ 0x81840002, /* movi r1.3l, #2 */
+ 0x82040004, /* movi r1.4l, #4 */
+ 0x97800100, /* ldrfp r1 */
+ 0x4c0c6300, /* xor r3, r3, r3 */
+ 0x0600c001, /* loop *6 ( */
+ 0x7ca00200, /* ldr *0++, *2 */
+ /* ) */
+ 0x560c1f00, /* subx r3, r31, r0 */
+ 0x0800000f, /* call &selcxSub */
+ 0x06000010, /* loop *0 ( */
+ 0x97800100, /* ldrfp r1 */
+ 0x560c6300, /* subx r3, r3, r3 */
+ 0x0600c003, /* loop *6 ( */
+ 0x7c8c0000, /* ldr *3, *0 */
+ 0x52884200, /* addcx r2, r2, r2 */
+ 0x7ca00300, /* ldr *0++, *3 */
+ /* ) */
+ 0x0800000f, /* call &selcxSub */
+ 0x97800100, /* ldrfp r1 */
+ 0x95800000, /* lddmp r0 */
+ 0x560c6300, /* subx r3, r3, r3 */
+ 0x0600c003, /* loop *6 ( */
+ 0x8c081800, /* ld *2, *0++ */
+ 0x7c8c0800, /* ldr *3, *0++ */
+ 0x5e804300, /* cmpbx r3, r2 */
+ /* ) */
+ 0x0800000f, /* call &selcxSub */
+ 0xfc000000, /* nop */
+ /* ) */
+ 0x97800100, /* ldrfp r1 */
+ 0x0600c001, /* loop *6 ( */
+ 0x90680800, /* st *0++, *2++ */
+ /* ) */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x42: function dmXd0[9] { */
+#define CF_dmXd0_adr 66
+ 0x586f3e00, /* mul128 r27, r30l, r25l */
+ 0x59eb3e00, /* mul128 r26, r30u, r25u */
+ 0x58df3e00, /* mul128 r23, r30u, r25l */
+ 0x506efb10, /* add r27, r27, r23 << 128 */
+ 0x50eafa90, /* addc r26, r26, r23 >> 128 */
+ 0x595f3e00, /* mul128 r23, r30l, r25u */
+ 0x506efb10, /* add r27, r27, r23 << 128 */
+ 0x50eafa90, /* addc r26, r26, r23 >> 128 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x4b: function dmXa[9] { */
+#define CF_dmXa_adr 75
+ 0x586c5e00, /* mul128 r27, r30l, r2l */
+ 0x59e85e00, /* mul128 r26, r30u, r2u */
+ 0x58dc5e00, /* mul128 r23, r30u, r2l */
+ 0x506efb10, /* add r27, r27, r23 << 128 */
+ 0x50eafa90, /* addc r26, r26, r23 >> 128 */
+ 0x595c5e00, /* mul128 r23, r30l, r2u */
+ 0x506efb10, /* add r27, r27, r23 << 128 */
+ 0x50eafa90, /* addc r26, r26, r23 >> 128 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x54: function mma[46] { */
+#define CF_mma_adr 84
+ 0x8204001e, /* movi r1.4l, #30 */
+ 0x82840018, /* movi r1.5l, #24 */
+ 0x97800100, /* ldrfp r1 */
+ 0x8c101b00, /* ld *4, *3++ */
+ 0x0800004b, /* call &dmXa */
+ 0x7c940800, /* ldr *5, *0++ */
+ 0x507b1b00, /* add r30, r27, r24 */
+ 0x50f7fa00, /* addc r29, r26, r31 */
+ 0x7c640300, /* mov r25, r3 */
+ 0x08000042, /* call &dmXd0 */
+ 0x7c641b00, /* mov r25, r27 */
+ 0x7c701a00, /* mov r28, r26 */
+ 0x7c601e00, /* mov r24, r30 */
+ 0x8c101800, /* ld *4, *0++ */
+ 0x08000042, /* call &dmXd0 */
+ 0x506f1b00, /* add r27, r27, r24 */
+ 0x50f3fa00, /* addc r28, r26, r31 */
+ 0x0600e00e, /* loop *7 ( */
+ 0x8c101b00, /* ld *4, *3++ */
+ 0x0800004b, /* call &dmXa */
+ 0x7c940800, /* ldr *5, *0++ */
+ 0x506f1b00, /* add r27, r27, r24 */
+ 0x50ebfa00, /* addc r26, r26, r31 */
+ 0x5063bb00, /* add r24, r27, r29 */
+ 0x50f7fa00, /* addc r29, r26, r31 */
+ 0x8c101800, /* ld *4, *0++ */
+ 0x08000042, /* call &dmXd0 */
+ 0x506f1b00, /* add r27, r27, r24 */
+ 0x50ebfa00, /* addc r26, r26, r31 */
+ 0x52639b00, /* addx r24, r27, r28 */
+ 0x7ca80500, /* ldr *2++, *5 */
+ 0x52f3fa00, /* addcx r28, r26, r31 */
+ /* ) */
+ 0x52e39d00, /* addcx r24, r29, r28 */
+ 0x7ca80500, /* ldr *2++, *5 */
+ 0x95800000, /* lddmp r0 */
+ 0x97800100, /* ldrfp r1 */
+ 0x54739c00, /* sub r28, r28, r28 */
+ 0x0600c007, /* loop *6 ( */
+ 0x8c141800, /* ld *5, *0++ */
+ 0x7c900000, /* ldr *4, *0 */
+ 0x54f71e00, /* subb r29, r30, r24 */
+ 0x99600000, /* strnd r24 */
+ 0x7c800500, /* ldr *0, *5 */
+ 0x6663dd08, /* selcx r24, r29, r30 */
+ 0x7ca00500, /* ldr *0++, *5 */
+ /* ) */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x82: function setupPtrs[11] { */
+#define CF_setupPtrs_adr 130
+ 0x847c4000, /* ldi r31, [#0] */
+ 0x4c7fff00, /* xor r31, r31, r31 */
+ 0x95800000, /* lddmp r0 */
+ 0x94800000, /* ldlc r0 */
+ 0x7c041f00, /* mov r1, r31 */
+ 0x80040004, /* movi r1.0l, #4 */
+ 0x80840003, /* movi r1.1l, #3 */
+ 0x81040004, /* movi r1.2l, #4 */
+ 0x81840002, /* movi r1.3l, #2 */
+ 0x97800100, /* ldrfp r1 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x8d: function mulx[19] { */
+#define CF_mulx_adr 141
+ 0x84004000, /* ldi r0, [#0] */
+ 0x08000082, /* call &setupPtrs */
+ 0x8c041100, /* ld *1, *1 */
+ 0x7c081f00, /* mov r2, r31 */
+ 0x0600c001, /* loop *6 ( */
+ 0x7ca80300, /* ldr *2++, *3 */
+ /* ) */
+ 0x97800100, /* ldrfp r1 */
+ 0x0600c004, /* loop *6 ( */
+ 0x8c0c1c00, /* ld *3, *4++ */
+ 0x95000000, /* stdmp r0 */
+ 0x08000054, /* call &mma */
+ 0x95800000, /* lddmp r0 */
+ /* ) */
+ 0x97800100, /* ldrfp r1 */
+ 0x95800000, /* lddmp r0 */
+ 0x0600c001, /* loop *6 ( */
+ 0x90740800, /* st *0++, *5++ */
+ /* ) */
+ 0x97800100, /* ldrfp r1 */
+ 0x95800000, /* lddmp r0 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0xa0: function mul1_exp[30] { */
+#define CF_mul1_exp_adr 160
+ 0x8c041100, /* ld *1, *1 */
+ 0x7c081f00, /* mov r2, r31 */
+ 0x0600c001, /* loop *6 ( */
+ 0x7ca80300, /* ldr *2++, *3 */
+ /* ) */
+ 0x97800100, /* ldrfp r1 */
+ 0x80080001, /* movi r2.0l, #1 */
+ 0x0600c003, /* loop *6 ( */
+ 0x95800000, /* lddmp r0 */
+ 0x08000054, /* call &mma */
+ 0x7c081f00, /* mov r2, r31 */
+ /* ) */
+ 0x97800100, /* ldrfp r1 */
+ 0x95800000, /* lddmp r0 */
+ 0x56084200, /* subx r2, r2, r2 */
+ 0x0600c003, /* loop *6 ( */
+ 0x8c041800, /* ld *1, *0++ */
+ 0x7c8c0800, /* ldr *3, *0++ */
+ 0x5e804300, /* cmpbx r3, r2 */
+ /* ) */
+ 0x97800100, /* ldrfp r1 */
+ 0x95800000, /* lddmp r0 */
+ 0x540c6300, /* sub r3, r3, r3 */
+ 0x0600c006, /* loop *6 ( */
+ 0x8c041800, /* ld *1, *0++ */
+ 0x7c8c0800, /* ldr *3, *0++ */
+ 0x548c6200, /* subb r3, r2, r3 */
+ 0x66084308, /* selcx r2, r3, r2 */
+ 0x90740300, /* st *3, *5++ */
+ 0xfc000000, /* nop */
+ /* ) */
+ 0x97800100, /* ldrfp r1 */
+ 0x95800000, /* lddmp r0 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0xbe: function mul1[4] { */
+#define CF_mul1_adr 190
+ 0x84004000, /* ldi r0, [#0] */
+ 0x08000082, /* call &setupPtrs */
+ 0x080000a0, /* call &mul1_exp */
+ 0x0c000000, /* ret */
+/* } */
+/* @0xc2: function sqrx_exp[19] { */
+#define CF_sqrx_exp_adr 194
+ 0x84004020, /* ldi r0, [#1] */
+ 0x95800000, /* lddmp r0 */
+ 0x8c041100, /* ld *1, *1 */
+ 0x7c081f00, /* mov r2, r31 */
+ 0x0600c001, /* loop *6 ( */
+ 0x7ca80300, /* ldr *2++, *3 */
+ /* ) */
+ 0x97800100, /* ldrfp r1 */
+ 0x0600c004, /* loop *6 ( */
+ 0x8c0c1c00, /* ld *3, *4++ */
+ 0x95000000, /* stdmp r0 */
+ 0x08000054, /* call &mma */
+ 0x95800000, /* lddmp r0 */
+ /* ) */
+ 0x97800100, /* ldrfp r1 */
+ 0x95800000, /* lddmp r0 */
+ 0x0600c001, /* loop *6 ( */
+ 0x90740800, /* st *0++, *5++ */
+ /* ) */
+ 0x97800100, /* ldrfp r1 */
+ 0x95800000, /* lddmp r0 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0xd5: function mulx_exp[14] { */
+#define CF_mulx_exp_adr 213
+ 0x84004040, /* ldi r0, [#2] */
+ 0x95800000, /* lddmp r0 */
+ 0x8c041100, /* ld *1, *1 */
+ 0x7c081f00, /* mov r2, r31 */
+ 0x0600c001, /* loop *6 ( */
+ 0x7ca80300, /* ldr *2++, *3 */
+ /* ) */
+ 0x97800100, /* ldrfp r1 */
+ 0x0600c004, /* loop *6 ( */
+ 0x8c0c1c00, /* ld *3, *4++ */
+ 0x95000000, /* stdmp r0 */
+ 0x08000054, /* call &mma */
+ 0x95800000, /* lddmp r0 */
+ /* ) */
+ 0x97800100, /* ldrfp r1 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0xe3: function modexp[43] { */
+#define CF_modexp_adr 227
+ 0x0800008d, /* call &mulx */
+ 0x84004060, /* ldi r0, [#3] */
+ 0x95800000, /* lddmp r0 */
+ 0x54084200, /* sub r2, r2, r2 */
+ 0x0600c004, /* loop *6 ( */
+ 0xfc000000, /* nop */
+ 0x8c0c1800, /* ld *3, *0++ */
+ 0x54885f00, /* subb r2, r31, r2 */
+ 0x90740300, /* st *3, *5++ */
+ /* ) */
+ 0xfc000000, /* nop */
+ 0x7c081f00, /* mov r2, r31 */
+ 0x8008ffff, /* movi r2.0l, #65535 */
+ 0x400c0298, /* and r3, r2, r0 >> 192 */
+ 0x48084000, /* not r2, r2 */
+ 0x40080200, /* and r2, r2, r0 */
+ 0x50086201, /* add r2, r2, r3 << 8 */
+ 0x94800200, /* ldlc r2 */
+ 0x06000015, /* loop *0 ( */
+ 0x080000c2, /* call &sqrx_exp */
+ 0x080000d5, /* call &mulx_exp */
+ 0x84004060, /* ldi r0, [#3] */
+ 0x95800000, /* lddmp r0 */
+ 0x99080000, /* strnd r2 */
+ 0x54084200, /* sub r2, r2, r2 */
+ 0x0600c004, /* loop *6 ( */
+ 0x99080000, /* strnd r2 */
+ 0x8c0c1400, /* ld *3, *4 */
+ 0x50884200, /* addc r2, r2, r2 */
+ 0x90700300, /* st *3, *4++ */
+ /* ) */
+ 0x0600c008, /* loop *6 ( */
+ 0x99080000, /* strnd r2 */
+ 0x8c041500, /* ld *1, *5 */
+ 0x90540300, /* st *3, *5 */
+ 0x7c8c0800, /* ldr *3, *0++ */
+ 0x7c000200, /* mov r0, r2 */
+ 0x99080000, /* strnd r2 */
+ 0x64086008, /* selc r2, r0, r3 */
+ 0x90740300, /* st *3, *5++ */
+ /* ) */
+ 0xfc000000, /* nop */
+ /* ) */
+ 0x84004060, /* ldi r0, [#3] */
+ 0x95800000, /* lddmp r0 */
+ 0x080000a0, /* call &mul1_exp */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x10e: function modexp_blinded[76] { */
+#define CF_modexp_blinded_adr 270
+ 0x0800008d, /* call &mulx */
+ 0x84004060, /* ldi r0, [#3] */
+ 0x95800000, /* lddmp r0 */
+ 0x54084200, /* sub r2, r2, r2 */
+ 0x0600c004, /* loop *6 ( */
+ 0xfc000000, /* nop */
+ 0x8c0c1800, /* ld *3, *0++ */
+ 0x54885f00, /* subb r2, r31, r2 */
+ 0x90740300, /* st *3, *5++ */
+ /* ) */
+ 0xfc000000, /* nop */
+ 0x8c0c1900, /* ld *3, *1++ */
+ 0x8c0c1100, /* ld *3, *1 */
+ 0x521c5f90, /* addx r7, r31, r2 >> 128 */
+ 0x590c4200, /* mul128 r3, r2l, r2u */
+ 0x7c181f00, /* mov r6, r31 */
+ 0x0600c011, /* loop *6 ( */
+ 0x99080000, /* strnd r2 */
+ 0x8c0c1400, /* ld *3, *4 */
+ 0x58106200, /* mul128 r4, r2l, r3l */
+ 0x59946200, /* mul128 r5, r2u, r3u */
+ 0x58806200, /* mul128 r0, r2u, r3l */
+ 0x50100410, /* add r4, r4, r0 << 128 */
+ 0x50940590, /* addc r5, r5, r0 >> 128 */
+ 0x59006200, /* mul128 r0, r2l, r3u */
+ 0x50100410, /* add r4, r4, r0 << 128 */
+ 0x50940590, /* addc r5, r5, r0 >> 128 */
+ 0x5010c400, /* add r4, r4, r6 */
+ 0x5097e500, /* addc r5, r5, r31 */
+ 0x50088200, /* add r2, r2, r4 */
+ 0x509be500, /* addc r6, r5, r31 */
+ 0x5688e200, /* subbx r2, r2, r7 */
+ 0x90700300, /* st *3, *4++ */
+ 0x541ce700, /* sub r7, r7, r7 */
+ /* ) */
+ 0x7c080600, /* mov r2, r6 */
+ 0x5688e200, /* subbx r2, r2, r7 */
+ 0x90500300, /* st *3, *4 */
+ 0xfc000000, /* nop */
+ 0x84004060, /* ldi r0, [#3] */
+ 0x7c081f00, /* mov r2, r31 */
+ 0x8008ffff, /* movi r2.0l, #65535 */
+ 0x400c0298, /* and r3, r2, r0 >> 192 */
+ 0x48084000, /* not r2, r2 */
+ 0x40080200, /* and r2, r2, r0 */
+ 0x510c0301, /* addi r3, r3, #1 */
+ 0x50086201, /* add r2, r2, r3 << 8 */
+ 0x94800200, /* ldlc r2 */
+ 0x06000019, /* loop *0 ( */
+ 0x080000c2, /* call &sqrx_exp */
+ 0x080000d5, /* call &mulx_exp */
+ 0x84004060, /* ldi r0, [#3] */
+ 0x95800000, /* lddmp r0 */
+ 0x99080000, /* strnd r2 */
+ 0x54084200, /* sub r2, r2, r2 */
+ 0x0600c004, /* loop *6 ( */
+ 0x99080000, /* strnd r2 */
+ 0x8c0c1400, /* ld *3, *4 */
+ 0x50884200, /* addc r2, r2, r2 */
+ 0x90700300, /* st *3, *4++ */
+ /* ) */
+ 0x99080000, /* strnd r2 */
+ 0x8c0c1400, /* ld *3, *4 */
+ 0x50884200, /* addc r2, r2, r2 */
+ 0x90700300, /* st *3, *4++ */
+ 0x0600c008, /* loop *6 ( */
+ 0x99080000, /* strnd r2 */
+ 0x8c041500, /* ld *1, *5 */
+ 0x90540300, /* st *3, *5 */
+ 0x7c8c0800, /* ldr *3, *0++ */
+ 0x7c000200, /* mov r0, r2 */
+ 0x99080000, /* strnd r2 */
+ 0x64086008, /* selc r2, r0, r3 */
+ 0x90740300, /* st *3, *5++ */
+ /* ) */
+ 0xfc000000, /* nop */
+ /* ) */
+ 0x84004060, /* ldi r0, [#3] */
+ 0x95800000, /* lddmp r0 */
+ 0x080000a0, /* call &mul1_exp */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x15a: function modload[12] { */
+#define CF_modload_adr 346
+ 0x4c7fff00, /* xor r31, r31, r31 */
+ 0x84004000, /* ldi r0, [#0] */
+ 0x95800000, /* lddmp r0 */
+ 0x94800000, /* ldlc r0 */
+ 0x8000001c, /* movi r0.0l, #28 */
+ 0x8080001d, /* movi r0.1l, #29 */
+ 0x97800000, /* ldrfp r0 */
+ 0x8c001000, /* ld *0, *0 */
+ 0x08000001, /* call &d0inv */
+ 0x90440100, /* st *1, *1 */
+ 0x08000019, /* call &computeRR */
+ 0x0c000000, /* ret */
+ /* } */
+};
+/* clang-format on */
+
+struct DMEM_ctx_ptrs {
+ uint32_t pMod;
+ uint32_t pDinv;
+ uint32_t pRR;
+ uint32_t pA;
+ uint32_t pB;
+ uint32_t pC;
+ uint32_t n;
+ uint32_t n1;
+};
+
+/*
+ * This struct is "calling convention" for passing parameters into the
+ * code block above for RSA operations. Parameters start at &DMEM[0].
+ */
+struct DMEM_ctx {
+ struct DMEM_ctx_ptrs in_ptrs;
+ struct DMEM_ctx_ptrs sqr_ptrs;
+ struct DMEM_ctx_ptrs mul_ptrs;
+ struct DMEM_ctx_ptrs out_ptrs;
+ uint32_t mod[RSA_WORDS_4K];
+ uint32_t dInv[8];
+ uint32_t pubexp;
+ uint32_t _pad1[3];
+ uint32_t rnd[2];
+ uint32_t _pad2[2];
+ uint32_t RR[RSA_WORDS_4K];
+ uint32_t in[RSA_WORDS_4K];
+ uint32_t exp[RSA_WORDS_4K + 8]; /* extra word for randomization */
+ uint32_t out[RSA_WORDS_4K];
+ uint32_t bin[RSA_WORDS_4K];
+ uint32_t bout[RSA_WORDS_4K];
+};
+
+#define DMEM_CELL_SIZE 32
+#define DMEM_INDEX(p, f) \
+ (((const uint8_t *) &(p)->f - (const uint8_t *) (p)) / DMEM_CELL_SIZE)
+
+/* Get non-0 64 bit random */
+static void rand64(uint32_t dst[2])
+{
+ do {
+ dst[0] = rand();
+ dst[1] = rand();
+ } while ((dst[0] | dst[1]) == 0);
+}
+
+/* Grab dcrypto lock and set things up for modulus and input */
+static int setup_and_lock(const struct LITE_BIGNUM *N,
+ const struct LITE_BIGNUM *input)
+{
+ struct DMEM_ctx *ctx =
+ (struct DMEM_ctx *) GREG32_ADDR(CRYPTO, DMEM_DUMMY);
+
+ /* Initialize hardware; load code page. */
+ dcrypto_init_and_lock();
+ dcrypto_imem_load(0, IMEM_dcrypto, ARRAY_SIZE(IMEM_dcrypto));
+
+ /* Setup DMEM pointers (as indices into DMEM which are 256-bit cells).
+ */
+ ctx->in_ptrs.pMod = DMEM_INDEX(ctx, mod);
+ ctx->in_ptrs.pDinv = DMEM_INDEX(ctx, dInv);
+ ctx->in_ptrs.pRR = DMEM_INDEX(ctx, RR);
+ ctx->in_ptrs.pA = DMEM_INDEX(ctx, in);
+ ctx->in_ptrs.pB = DMEM_INDEX(ctx, exp);
+ ctx->in_ptrs.pC = DMEM_INDEX(ctx, out);
+ ctx->in_ptrs.n = bn_bits(N) / (DMEM_CELL_SIZE * 8);
+ ctx->in_ptrs.n1 = ctx->in_ptrs.n - 1;
+
+ ctx->sqr_ptrs = ctx->in_ptrs;
+ ctx->mul_ptrs = ctx->in_ptrs;
+ ctx->out_ptrs = ctx->in_ptrs;
+
+ dcrypto_dmem_load(DMEM_INDEX(ctx, mod), N->d, bn_words(N));
+ dcrypto_dmem_load(DMEM_INDEX(ctx, in), input->d, bn_words(input));
+
+ /* Calculate RR and d0inv. */
+ return dcrypto_call(CF_modload_adr);
+}
+
+#define MONTMUL(ctx, a, b, c) \
+ montmul(ctx, DMEM_INDEX(ctx, a), DMEM_INDEX(ctx, b), DMEM_INDEX(ctx, c))
+
+static int montmul(struct DMEM_ctx *ctx, uint32_t pA, uint32_t pB,
+ uint32_t pOut)
+{
+
+ ctx->in_ptrs.pA = pA;
+ ctx->in_ptrs.pB = pB;
+ ctx->in_ptrs.pC = pOut;
+
+ return dcrypto_call(CF_mulx_adr);
+}
+
+#define MONTOUT(ctx, a, b) montout(ctx, DMEM_INDEX(ctx, a), DMEM_INDEX(ctx, b))
+
+static int montout(struct DMEM_ctx *ctx, uint32_t pA, uint32_t pOut)
+{
+
+ ctx->in_ptrs.pA = pA;
+ ctx->in_ptrs.pB = 0;
+ ctx->in_ptrs.pC = pOut;
+
+ return dcrypto_call(CF_mul1_adr);
+}
+
+#define MODEXP(ctx, in, exp, out) \
+ modexp(ctx, CF_modexp_adr, DMEM_INDEX(ctx, RR), DMEM_INDEX(ctx, in), \
+ DMEM_INDEX(ctx, exp), DMEM_INDEX(ctx, out))
+
+#define MODEXP_BLINDED(ctx, in, exp, out) \
+ modexp(ctx, CF_modexp_blinded_adr, DMEM_INDEX(ctx, RR), \
+ DMEM_INDEX(ctx, in), DMEM_INDEX(ctx, exp), \
+ DMEM_INDEX(ctx, out))
+
+static int modexp(struct DMEM_ctx *ctx, uint32_t adr, uint32_t rr, uint32_t pIn,
+ uint32_t pExp, uint32_t pOut)
+{
+ /* in = in * RR */
+ ctx->in_ptrs.pA = pIn;
+ ctx->in_ptrs.pB = rr;
+ ctx->in_ptrs.pC = pIn;
+
+ /* out = out * out */
+ ctx->sqr_ptrs.pA = pOut;
+ ctx->sqr_ptrs.pB = pOut;
+ ctx->sqr_ptrs.pC = pOut;
+
+ /* out = out * in */
+ ctx->mul_ptrs.pA = pIn;
+ ctx->mul_ptrs.pB = pOut;
+ ctx->mul_ptrs.pC = pOut;
+
+ /* out = out / R */
+ ctx->out_ptrs.pA = pOut;
+ ctx->out_ptrs.pB = pExp;
+ ctx->out_ptrs.pC = pOut;
+
+ return dcrypto_call(adr);
+}
+
+/* output = input ** exp % N. */
+int dcrypto_modexp_blinded(struct LITE_BIGNUM *output,
+ const struct LITE_BIGNUM *input,
+ const struct LITE_BIGNUM *exp,
+ const struct LITE_BIGNUM *N, uint32_t pubexp)
+{
+ int i, result;
+ struct DMEM_ctx *ctx =
+ (struct DMEM_ctx *) GREG32_ADDR(CRYPTO, DMEM_DUMMY);
+
+ uint32_t r_buf[RSA_MAX_WORDS];
+ uint32_t rinv_buf[RSA_MAX_WORDS];
+
+ struct LITE_BIGNUM r;
+ struct LITE_BIGNUM rinv;
+
+ bn_init(&r, r_buf, bn_size(N));
+ bn_init(&rinv, rinv_buf, bn_size(N));
+
+ /*
+ * pick 64 bit r != 0
+ * We cannot tolerate risk of 0 since 0 breaks computation.
+ */
+ rand64(r_buf);
+
+ /*
+ * compute 1/r mod N
+ * Note this cannot fail since N is product of two large primes
+ * and r != 0, so we can ignore return value.
+ */
+ bn_modinv_vartime(&rinv, &r, N);
+
+ /*
+ * compute r^pubexp mod N
+ */
+ dcrypto_modexp_word(&r, &r, pubexp, N);
+
+ result = setup_and_lock(N, input);
+
+ /* Pick !0 64-bit random for exponent blinding */
+ rand64(ctx->rnd);
+ ctx->pubexp = pubexp;
+
+ ctx->_pad1[0] = ctx->_pad1[1] = ctx->_pad1[2] = 0;
+ ctx->_pad2[0] = ctx->_pad2[1] = 0;
+
+ dcrypto_dmem_load(DMEM_INDEX(ctx, bin), r.d, bn_words(&r));
+ dcrypto_dmem_load(DMEM_INDEX(ctx, bout), rinv.d, bn_words(&rinv));
+ dcrypto_dmem_load(DMEM_INDEX(ctx, exp), exp->d, bn_words(exp));
+
+ /* 0 pad the exponent to full size + 8 */
+ for (i = bn_words(exp); i < bn_words(N) + 8; ++i)
+ ctx->exp[i] = 0;
+
+ /* Blind input */
+ result |= MONTMUL(ctx, in, RR, in);
+ result |= MONTMUL(ctx, in, bin, in);
+
+ result |= MODEXP_BLINDED(ctx, in, exp, out);
+
+ /* remove blinding factor */
+ result |= MONTMUL(ctx, out, RR, out);
+ result |= MONTMUL(ctx, out, bout, out);
+ /* fully reduce out */
+ result |= MONTMUL(ctx, out, RR, out);
+ result |= MONTOUT(ctx, out, out);
+
+ memcpy(output->d, ctx->out, bn_size(output));
+
+ dcrypto_unlock();
+ return result == 0;
+}
+
+/* output = input ** exp % N. */
+int dcrypto_modexp(struct LITE_BIGNUM *output, const struct LITE_BIGNUM *input,
+ const struct LITE_BIGNUM *exp, const struct LITE_BIGNUM *N)
+{
+ int i, result;
+ struct DMEM_ctx *ctx =
+ (struct DMEM_ctx *) GREG32_ADDR(CRYPTO, DMEM_DUMMY);
+
+ result = setup_and_lock(N, input);
+
+ dcrypto_dmem_load(DMEM_INDEX(ctx, exp), exp->d, bn_words(exp));
+
+ /* 0 pad the exponent to full size */
+ for (i = bn_words(exp); i < bn_words(N); ++i)
+ ctx->exp[i] = 0;
+
+ result |= MODEXP(ctx, in, exp, out);
+
+ memcpy(output->d, ctx->out, bn_size(output));
+
+ dcrypto_unlock();
+ return result == 0;
+}
+
+/* output = input ** exp % N. */
+int dcrypto_modexp_word(struct LITE_BIGNUM *output,
+ const struct LITE_BIGNUM *input, uint32_t exp,
+ const struct LITE_BIGNUM *N)
+{
+ int result;
+ uint32_t e = exp;
+ uint32_t b = 0x80000000;
+ struct DMEM_ctx *ctx =
+ (struct DMEM_ctx *) GREG32_ADDR(CRYPTO, DMEM_DUMMY);
+
+ result = setup_and_lock(N, input);
+
+ /* Find top bit */
+ while (b != 0 && !(b & e))
+ b >>= 1;
+
+ /* out = in * RR */
+ result |= MONTMUL(ctx, in, RR, out);
+ /* in = in * RR */
+ result |= MONTMUL(ctx, in, RR, in);
+
+ while (b > 1) {
+ b >>= 1;
+
+ /* out = out * out */
+ result |= MONTMUL(ctx, out, out, out);
+
+ if ((b & e) != 0) {
+ /* out = out * in */
+ result |= MONTMUL(ctx, in, out, out);
+ }
+ }
+
+ /* out = out / R */
+ result |= MONTOUT(ctx, out, out);
+
+ memcpy(output->d, ctx->out, bn_size(output));
+
+ dcrypto_unlock();
+ return result == 0;
+}
diff --git a/chip/g/dcrypto/dcrypto_p256.c b/chip/g/dcrypto/dcrypto_p256.c
new file mode 100644
index 0000000000..3b30d702ca
--- /dev/null
+++ b/chip/g/dcrypto/dcrypto_p256.c
@@ -0,0 +1,941 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "dcrypto.h"
+#include "internal.h"
+#include "registers.h"
+#include "trng.h"
+
+/* Firmware blob for crypto accelerator */
+
+/* AUTO-GENERATED. DO NOT MODIFY. */
+/* clang-format off */
+static const uint32_t IMEM_dcrypto[] = {
+/* @0x0: function tag[1] { */
+#define CF_tag_adr 0
+ 0xf8000002, /* sigini #2 */
+/* } */
+/* @0x1: function SetupP256PandMuLow[21] { */
+#define CF_SetupP256PandMuLow_adr 1
+ 0x55741f01, /* subi r29, r31, #1 */
+ 0x83750000, /* movi r29.6h, #0 */
+ 0x83740001, /* movi r29.6l, #1 */
+ 0x82f50000, /* movi r29.5h, #0 */
+ 0x82f40000, /* movi r29.5l, #0 */
+ 0x82750000, /* movi r29.4h, #0 */
+ 0x82740000, /* movi r29.4l, #0 */
+ 0x81f50000, /* movi r29.3h, #0 */
+ 0x81f40000, /* movi r29.3l, #0 */
+ 0x98801d00, /* ldmod r29 */
+ 0x55701f01, /* subi r28, r31, #1 */
+ 0x83f10000, /* movi r28.7h, #0 */
+ 0x83f00000, /* movi r28.7l, #0 */
+ 0x82f0fffe, /* movi r28.5l, #65534 */
+ 0x8270fffe, /* movi r28.4l, #65534 */
+ 0x81f0fffe, /* movi r28.3l, #65534 */
+ 0x80f10000, /* movi r28.1h, #0 */
+ 0x80f00000, /* movi r28.1l, #0 */
+ 0x80710000, /* movi r28.0h, #0 */
+ 0x80700003, /* movi r28.0l, #3 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x16: function p256init[22] { */
+#define CF_p256init_adr 22
+ 0x847c4000, /* ldi r31, [#0] */
+ 0x4c7fff00, /* xor r31, r31, r31 */
+ 0x51781f01, /* addi r30, r31, #1 */
+ 0x08000001, /* call &SetupP256PandMuLow */
+ 0x7c6c1f00, /* mov r27, r31 */
+ 0x83ed5ac6, /* movi r27.7h, #23238 */
+ 0x83ec35d8, /* movi r27.7l, #13784 */
+ 0x836daa3a, /* movi r27.6h, #43578 */
+ 0x836c93e7, /* movi r27.6l, #37863 */
+ 0x82edb3eb, /* movi r27.5h, #46059 */
+ 0x82ecbd55, /* movi r27.5l, #48469 */
+ 0x826d7698, /* movi r27.4h, #30360 */
+ 0x826c86bc, /* movi r27.4l, #34492 */
+ 0x81ed651d, /* movi r27.3h, #25885 */
+ 0x81ec06b0, /* movi r27.3l, #1712 */
+ 0x816dcc53, /* movi r27.2h, #52307 */
+ 0x816cb0f6, /* movi r27.2l, #45302 */
+ 0x80ed3bce, /* movi r27.1h, #15310 */
+ 0x80ec3c3e, /* movi r27.1l, #15422 */
+ 0x806d27d2, /* movi r27.0h, #10194 */
+ 0x806c604b, /* movi r27.0l, #24651 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x2c: function MulMod[38] { */
+#define CF_MulMod_adr 44
+ 0x584f3800, /* mul128 r19, r24l, r25l */
+ 0x59d33800, /* mul128 r20, r24u, r25u */
+ 0x58d73800, /* mul128 r21, r24u, r25l */
+ 0x504eb310, /* add r19, r19, r21 << 128 */
+ 0x50d2b490, /* addc r20, r20, r21 >> 128 */
+ 0x59573800, /* mul128 r21, r24l, r25u */
+ 0x504eb310, /* add r19, r19, r21 << 128 */
+ 0x50d2b490, /* addc r20, r20, r21 >> 128 */
+ 0x645bfc02, /* selm r22, r28, r31 */
+ 0x685693ff, /* rshi r21, r19, r20 >> 255 */
+ 0x585f9500, /* mul128 r23, r21l, r28l */
+ 0x59e39500, /* mul128 r24, r21u, r28u */
+ 0x58e79500, /* mul128 r25, r21u, r28l */
+ 0x505f3710, /* add r23, r23, r25 << 128 */
+ 0x50e33890, /* addc r24, r24, r25 >> 128 */
+ 0x59679500, /* mul128 r25, r21l, r28u */
+ 0x505f3710, /* add r23, r23, r25 << 128 */
+ 0x50e33890, /* addc r24, r24, r25 >> 128 */
+ 0x6867f4ff, /* rshi r25, r20, r31 >> 255 */
+ 0x5062b800, /* add r24, r24, r21 */
+ 0x50e7f900, /* addc r25, r25, r31 */
+ 0x5062d800, /* add r24, r24, r22 */
+ 0x50e7f900, /* addc r25, r25, r31 */
+ 0x68573801, /* rshi r21, r24, r25 >> 1 */
+ 0x585abd00, /* mul128 r22, r29l, r21l */
+ 0x59debd00, /* mul128 r23, r29u, r21u */
+ 0x58e2bd00, /* mul128 r24, r29u, r21l */
+ 0x505b1610, /* add r22, r22, r24 << 128 */
+ 0x50df1790, /* addc r23, r23, r24 >> 128 */
+ 0x5962bd00, /* mul128 r24, r29l, r21u */
+ 0x505b1610, /* add r22, r22, r24 << 128 */
+ 0x50df1790, /* addc r23, r23, r24 >> 128 */
+ 0x545ad300, /* sub r22, r19, r22 */
+ 0x54d2f400, /* subb r20, r20, r23 */
+ 0x6457fd01, /* sell r21, r29, r31 */
+ 0x5456b600, /* sub r21, r22, r21 */
+ 0x9c4ff500, /* addm r19, r21, r31 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x52: function p256isoncurve[24] { */
+#define CF_p256isoncurve_adr 82
+ 0x84004000, /* ldi r0, [#0] */
+ 0x95800000, /* lddmp r0 */
+ 0x82800018, /* movi r0.5l, #24 */
+ 0x83000018, /* movi r0.6l, #24 */
+ 0x80000000, /* movi r0.0l, #0 */
+ 0x97800000, /* ldrfp r0 */
+ 0x8c181600, /* ld *6, *6 */
+ 0x7c641800, /* mov r25, r24 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c001300, /* mov r0, r19 */
+ 0x8c141500, /* ld *5, *5 */
+ 0x7c641800, /* mov r25, r24 */
+ 0x0800002c, /* call &MulMod */
+ 0x8c141500, /* ld *5, *5 */
+ 0x7c641300, /* mov r25, r19 */
+ 0x0800002c, /* call &MulMod */
+ 0x8c141500, /* ld *5, *5 */
+ 0xa04f1300, /* subm r19, r19, r24 */
+ 0xa04f1300, /* subm r19, r19, r24 */
+ 0xa04f1300, /* subm r19, r19, r24 */
+ 0x9c637300, /* addm r24, r19, r27 */
+ 0x904c0500, /* st *5, *3 */
+ 0x90500000, /* st *0, *4 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x6a: function ProjAdd[80] { */
+#define CF_ProjAdd_adr 106
+ 0x7c600b00, /* mov r24, r11 */
+ 0x7c640800, /* mov r25, r8 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c381300, /* mov r14, r19 */
+ 0x7c600c00, /* mov r24, r12 */
+ 0x7c640900, /* mov r25, r9 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c3c1300, /* mov r15, r19 */
+ 0x7c600d00, /* mov r24, r13 */
+ 0x7c640a00, /* mov r25, r10 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c401300, /* mov r16, r19 */
+ 0x9c458b00, /* addm r17, r11, r12 */
+ 0x9c492800, /* addm r18, r8, r9 */
+ 0x7c601100, /* mov r24, r17 */
+ 0x7c641200, /* mov r25, r18 */
+ 0x0800002c, /* call &MulMod */
+ 0x9c49ee00, /* addm r18, r14, r15 */
+ 0xa0465300, /* subm r17, r19, r18 */
+ 0x9c49ac00, /* addm r18, r12, r13 */
+ 0x9c4d4900, /* addm r19, r9, r10 */
+ 0x7c601200, /* mov r24, r18 */
+ 0x7c641300, /* mov r25, r19 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c481300, /* mov r18, r19 */
+ 0x9c4e0f00, /* addm r19, r15, r16 */
+ 0xa04a7200, /* subm r18, r18, r19 */
+ 0x9c4dab00, /* addm r19, r11, r13 */
+ 0x9c314800, /* addm r12, r8, r10 */
+ 0x7c601300, /* mov r24, r19 */
+ 0x7c640c00, /* mov r25, r12 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c2c1300, /* mov r11, r19 */
+ 0x9c320e00, /* addm r12, r14, r16 */
+ 0xa0318b00, /* subm r12, r11, r12 */
+ 0x7c601b00, /* mov r24, r27 */
+ 0x7c641000, /* mov r25, r16 */
+ 0x0800002c, /* call &MulMod */
+ 0xa02e6c00, /* subm r11, r12, r19 */
+ 0x9c356b00, /* addm r13, r11, r11 */
+ 0x9c2dab00, /* addm r11, r11, r13 */
+ 0xa0356f00, /* subm r13, r15, r11 */
+ 0x9c2d6f00, /* addm r11, r15, r11 */
+ 0x7c601b00, /* mov r24, r27 */
+ 0x7c640c00, /* mov r25, r12 */
+ 0x0800002c, /* call &MulMod */
+ 0x9c3e1000, /* addm r15, r16, r16 */
+ 0x9c420f00, /* addm r16, r15, r16 */
+ 0xa0321300, /* subm r12, r19, r16 */
+ 0xa031cc00, /* subm r12, r12, r14 */
+ 0x9c3d8c00, /* addm r15, r12, r12 */
+ 0x9c318f00, /* addm r12, r15, r12 */
+ 0x9c3dce00, /* addm r15, r14, r14 */
+ 0x9c39cf00, /* addm r14, r15, r14 */
+ 0xa03a0e00, /* subm r14, r14, r16 */
+ 0x7c601200, /* mov r24, r18 */
+ 0x7c640c00, /* mov r25, r12 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c3c1300, /* mov r15, r19 */
+ 0x7c600e00, /* mov r24, r14 */
+ 0x7c640c00, /* mov r25, r12 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c401300, /* mov r16, r19 */
+ 0x7c600b00, /* mov r24, r11 */
+ 0x7c640d00, /* mov r25, r13 */
+ 0x0800002c, /* call &MulMod */
+ 0x9c321300, /* addm r12, r19, r16 */
+ 0x7c601100, /* mov r24, r17 */
+ 0x7c640b00, /* mov r25, r11 */
+ 0x0800002c, /* call &MulMod */
+ 0xa02df300, /* subm r11, r19, r15 */
+ 0x7c601200, /* mov r24, r18 */
+ 0x7c640d00, /* mov r25, r13 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c341300, /* mov r13, r19 */
+ 0x7c601100, /* mov r24, r17 */
+ 0x7c640e00, /* mov r25, r14 */
+ 0x0800002c, /* call &MulMod */
+ 0x9c366d00, /* addm r13, r13, r19 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0xba: function ProjToAffine[116] { */
+#define CF_ProjToAffine_adr 186
+ 0x9c2bea00, /* addm r10, r10, r31 */
+ 0x7c600a00, /* mov r24, r10 */
+ 0x7c640a00, /* mov r25, r10 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c601300, /* mov r24, r19 */
+ 0x7c640a00, /* mov r25, r10 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c301300, /* mov r12, r19 */
+ 0x7c601300, /* mov r24, r19 */
+ 0x7c641300, /* mov r25, r19 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c601300, /* mov r24, r19 */
+ 0x7c641300, /* mov r25, r19 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c601300, /* mov r24, r19 */
+ 0x7c640c00, /* mov r25, r12 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c341300, /* mov r13, r19 */
+ 0x05004004, /* loop #4 ( */
+ 0x7c601300, /* mov r24, r19 */
+ 0x7c641300, /* mov r25, r19 */
+ 0x0800002c, /* call &MulMod */
+ 0xfc000000, /* nop */
+ /* ) */
+ 0x7c601300, /* mov r24, r19 */
+ 0x7c640d00, /* mov r25, r13 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c381300, /* mov r14, r19 */
+ 0x05008004, /* loop #8 ( */
+ 0x7c601300, /* mov r24, r19 */
+ 0x7c641300, /* mov r25, r19 */
+ 0x0800002c, /* call &MulMod */
+ 0xfc000000, /* nop */
+ /* ) */
+ 0x7c601300, /* mov r24, r19 */
+ 0x7c640e00, /* mov r25, r14 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c3c1300, /* mov r15, r19 */
+ 0x05010004, /* loop #16 ( */
+ 0x7c601300, /* mov r24, r19 */
+ 0x7c641300, /* mov r25, r19 */
+ 0x0800002c, /* call &MulMod */
+ 0xfc000000, /* nop */
+ /* ) */
+ 0x7c601300, /* mov r24, r19 */
+ 0x7c640f00, /* mov r25, r15 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c401300, /* mov r16, r19 */
+ 0x05020004, /* loop #32 ( */
+ 0x7c601300, /* mov r24, r19 */
+ 0x7c641300, /* mov r25, r19 */
+ 0x0800002c, /* call &MulMod */
+ 0xfc000000, /* nop */
+ /* ) */
+ 0x7c441300, /* mov r17, r19 */
+ 0x7c600a00, /* mov r24, r10 */
+ 0x7c641300, /* mov r25, r19 */
+ 0x0800002c, /* call &MulMod */
+ 0x050c0004, /* loop #192 ( */
+ 0x7c601300, /* mov r24, r19 */
+ 0x7c641300, /* mov r25, r19 */
+ 0x0800002c, /* call &MulMod */
+ 0xfc000000, /* nop */
+ /* ) */
+ 0x7c481300, /* mov r18, r19 */
+ 0x7c601100, /* mov r24, r17 */
+ 0x7c641000, /* mov r25, r16 */
+ 0x0800002c, /* call &MulMod */
+ 0x05010004, /* loop #16 ( */
+ 0x7c601300, /* mov r24, r19 */
+ 0x7c641300, /* mov r25, r19 */
+ 0x0800002c, /* call &MulMod */
+ 0xfc000000, /* nop */
+ /* ) */
+ 0x7c600f00, /* mov r24, r15 */
+ 0x7c641300, /* mov r25, r19 */
+ 0x0800002c, /* call &MulMod */
+ 0x05008004, /* loop #8 ( */
+ 0x7c601300, /* mov r24, r19 */
+ 0x7c641300, /* mov r25, r19 */
+ 0x0800002c, /* call &MulMod */
+ 0xfc000000, /* nop */
+ /* ) */
+ 0x7c600e00, /* mov r24, r14 */
+ 0x7c641300, /* mov r25, r19 */
+ 0x0800002c, /* call &MulMod */
+ 0x05004004, /* loop #4 ( */
+ 0x7c601300, /* mov r24, r19 */
+ 0x7c641300, /* mov r25, r19 */
+ 0x0800002c, /* call &MulMod */
+ 0xfc000000, /* nop */
+ /* ) */
+ 0x7c600d00, /* mov r24, r13 */
+ 0x7c641300, /* mov r25, r19 */
+ 0x0800002c, /* call &MulMod */
+ 0x05002004, /* loop #2 ( */
+ 0x7c601300, /* mov r24, r19 */
+ 0x7c641300, /* mov r25, r19 */
+ 0x0800002c, /* call &MulMod */
+ 0xfc000000, /* nop */
+ /* ) */
+ 0x7c600c00, /* mov r24, r12 */
+ 0x7c641300, /* mov r25, r19 */
+ 0x0800002c, /* call &MulMod */
+ 0x05002004, /* loop #2 ( */
+ 0x7c601300, /* mov r24, r19 */
+ 0x7c641300, /* mov r25, r19 */
+ 0x0800002c, /* call &MulMod */
+ 0xfc000000, /* nop */
+ /* ) */
+ 0x7c600a00, /* mov r24, r10 */
+ 0x7c641300, /* mov r25, r19 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c601300, /* mov r24, r19 */
+ 0x7c641200, /* mov r25, r18 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c381300, /* mov r14, r19 */
+ 0x7c600800, /* mov r24, r8 */
+ 0x7c640e00, /* mov r25, r14 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c2c1300, /* mov r11, r19 */
+ 0x7c600900, /* mov r24, r9 */
+ 0x7c640e00, /* mov r25, r14 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c301300, /* mov r12, r19 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x12e: function ModInv[17] { */
+#define CF_ModInv_adr 302
+ 0x98080000, /* stmod r2 */
+ 0x55080202, /* subi r2, r2, #2 */
+ 0x7c041e00, /* mov r1, r30 */
+ 0x0510000c, /* loop #256 ( */
+ 0x7c600100, /* mov r24, r1 */
+ 0x7c640100, /* mov r25, r1 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c0c1300, /* mov r3, r19 */
+ 0x50084200, /* add r2, r2, r2 */
+ 0x64046108, /* selc r1, r1, r3 */
+ 0x1008813d, /* bnc nomul */
+ 0x7c600300, /* mov r24, r3 */
+ 0x7c640000, /* mov r25, r0 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c041300, /* mov r1, r19 */
+ /*nomul: */
+ 0xfc000000, /* nop */
+ /* ) */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x13f: function FetchBandRandomize[11] { */
+#define CF_FetchBandRandomize_adr 319
+ 0x99080000, /* strnd r2 */
+ 0x9c6be200, /* addm r26, r2, r31 */
+ 0x8c081500, /* ld *2, *5 */
+ 0x7c641a00, /* mov r25, r26 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c181300, /* mov r6, r19 */
+ 0x8c081600, /* ld *2, *6 */
+ 0x7c641a00, /* mov r25, r26 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c1c1300, /* mov r7, r19 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x14a: function ProjDouble[5] { */
+#define CF_ProjDouble_adr 330
+ 0x7c2c0800, /* mov r11, r8 */
+ 0x7c300900, /* mov r12, r9 */
+ 0x7c340a00, /* mov r13, r10 */
+ 0x0800006a, /* call &ProjAdd */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x14f: function SetupP256NandMuLow[25] { */
+#define CF_SetupP256NandMuLow_adr 335
+ 0x55741f01, /* subi r29, r31, #1 */
+ 0x83750000, /* movi r29.6h, #0 */
+ 0x83740000, /* movi r29.6l, #0 */
+ 0x81f5bce6, /* movi r29.3h, #48358 */
+ 0x81f4faad, /* movi r29.3l, #64173 */
+ 0x8175a717, /* movi r29.2h, #42775 */
+ 0x81749e84, /* movi r29.2l, #40580 */
+ 0x80f5f3b9, /* movi r29.1h, #62393 */
+ 0x80f4cac2, /* movi r29.1l, #51906 */
+ 0x8075fc63, /* movi r29.0h, #64611 */
+ 0x80742551, /* movi r29.0l, #9553 */
+ 0x55701f01, /* subi r28, r31, #1 */
+ 0x83f10000, /* movi r28.7h, #0 */
+ 0x83f00000, /* movi r28.7l, #0 */
+ 0x82f0fffe, /* movi r28.5l, #65534 */
+ 0x81f14319, /* movi r28.3h, #17177 */
+ 0x81f00552, /* movi r28.3l, #1362 */
+ 0x8171df1a, /* movi r28.2h, #57114 */
+ 0x81706c21, /* movi r28.2l, #27681 */
+ 0x80f1012f, /* movi r28.1h, #303 */
+ 0x80f0fd85, /* movi r28.1l, #64901 */
+ 0x8071eedf, /* movi r28.0h, #61151 */
+ 0x80709bfe, /* movi r28.0l, #39934 */
+ 0x98801d00, /* ldmod r29 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x168: function ScalarMult_internal[51] { */
+#define CF_ScalarMult_internal_adr 360
+ 0x0800014f, /* call &SetupP256NandMuLow */
+ 0x8c041100, /* ld *1, *1 */
+ 0x9c07e100, /* addm r1, r1, r31 */
+ 0xa0002000, /* subm r0, r0, r1 */
+ 0x08000001, /* call &SetupP256PandMuLow */
+ 0x0800013f, /* call &FetchBandRandomize */
+ 0x7c200600, /* mov r8, r6 */
+ 0x7c240700, /* mov r9, r7 */
+ 0x7c281a00, /* mov r10, r26 */
+ 0x0800014a, /* call &ProjDouble */
+ 0x7c0c0b00, /* mov r3, r11 */
+ 0x7c100c00, /* mov r4, r12 */
+ 0x7c140d00, /* mov r5, r13 */
+ 0x7c201f00, /* mov r8, r31 */
+ 0x7c241e00, /* mov r9, r30 */
+ 0x7c281f00, /* mov r10, r31 */
+ 0x05100020, /* loop #256 ( */
+ 0x0800014a, /* call &ProjDouble */
+ 0x0800013f, /* call &FetchBandRandomize */
+ 0x4c202000, /* xor r8, r0, r1 */
+ 0x64206602, /* selm r8, r6, r3 */
+ 0x64248702, /* selm r9, r7, r4 */
+ 0x6428ba02, /* selm r10, r26, r5 */
+ 0x7c080b00, /* mov r2, r11 */
+ 0x7c180c00, /* mov r6, r12 */
+ 0x7c1c0d00, /* mov r7, r13 */
+ 0x0800006a, /* call &ProjAdd */
+ 0x44202000, /* or r8, r0, r1 */
+ 0x64204b02, /* selm r8, r11, r2 */
+ 0x6424cc02, /* selm r9, r12, r6 */
+ 0x6428ed02, /* selm r10, r13, r7 */
+ 0x680000ff, /* rshi r0, r0, r0 >> 255 */
+ 0x680421ff, /* rshi r1, r1, r1 >> 255 */
+ 0x992c0000, /* strnd r11 */
+ 0x99300000, /* strnd r12 */
+ 0x99340000, /* strnd r13 */
+ 0x99080000, /* strnd r2 */
+ 0x7c600300, /* mov r24, r3 */
+ 0x7c640200, /* mov r25, r2 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c0c1300, /* mov r3, r19 */
+ 0x7c600400, /* mov r24, r4 */
+ 0x7c640200, /* mov r25, r2 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c101300, /* mov r4, r19 */
+ 0x7c600500, /* mov r24, r5 */
+ 0x7c640200, /* mov r25, r2 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c141300, /* mov r5, r19 */
+ /* ) */
+ 0x080000ba, /* call &ProjToAffine */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x19b: function get_P256B[35] { */
+#define CF_get_P256B_adr 411
+ 0x7c201f00, /* mov r8, r31 */
+ 0x83a16b17, /* movi r8.7h, #27415 */
+ 0x83a0d1f2, /* movi r8.7l, #53746 */
+ 0x8321e12c, /* movi r8.6h, #57644 */
+ 0x83204247, /* movi r8.6l, #16967 */
+ 0x82a1f8bc, /* movi r8.5h, #63676 */
+ 0x82a0e6e5, /* movi r8.5l, #59109 */
+ 0x822163a4, /* movi r8.4h, #25508 */
+ 0x822040f2, /* movi r8.4l, #16626 */
+ 0x81a17703, /* movi r8.3h, #30467 */
+ 0x81a07d81, /* movi r8.3l, #32129 */
+ 0x81212deb, /* movi r8.2h, #11755 */
+ 0x812033a0, /* movi r8.2l, #13216 */
+ 0x80a1f4a1, /* movi r8.1h, #62625 */
+ 0x80a03945, /* movi r8.1l, #14661 */
+ 0x8021d898, /* movi r8.0h, #55448 */
+ 0x8020c296, /* movi r8.0l, #49814 */
+ 0x7c241f00, /* mov r9, r31 */
+ 0x83a54fe3, /* movi r9.7h, #20451 */
+ 0x83a442e2, /* movi r9.7l, #17122 */
+ 0x8325fe1a, /* movi r9.6h, #65050 */
+ 0x83247f9b, /* movi r9.6l, #32667 */
+ 0x82a58ee7, /* movi r9.5h, #36583 */
+ 0x82a4eb4a, /* movi r9.5l, #60234 */
+ 0x82257c0f, /* movi r9.4h, #31759 */
+ 0x82249e16, /* movi r9.4l, #40470 */
+ 0x81a52bce, /* movi r9.3h, #11214 */
+ 0x81a43357, /* movi r9.3l, #13143 */
+ 0x81256b31, /* movi r9.2h, #27441 */
+ 0x81245ece, /* movi r9.2l, #24270 */
+ 0x80a5cbb6, /* movi r9.1h, #52150 */
+ 0x80a44068, /* movi r9.1l, #16488 */
+ 0x802537bf, /* movi r9.0h, #14271 */
+ 0x802451f5, /* movi r9.0l, #20981 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x1be: function p256sign[34] { */
+#define CF_p256sign_adr 446
+ 0xfc000000, /* nop */
+ 0x84004000, /* ldi r0, [#0] */
+ 0x95800000, /* lddmp r0 */
+ 0x80000000, /* movi r0.0l, #0 */
+ 0x80800001, /* movi r0.1l, #1 */
+ 0x81000018, /* movi r0.2l, #24 */
+ 0x82000008, /* movi r0.4l, #8 */
+ 0x82800009, /* movi r0.5l, #9 */
+ 0x97800000, /* ldrfp r0 */
+ 0x0800019b, /* call &get_P256B */
+ 0x90540400, /* st *4, *5 */
+ 0x90580500, /* st *5, *6 */
+ 0xfc000000, /* nop */
+ 0x8c001000, /* ld *0, *0 */
+ 0x08000168, /* call &ScalarMult_internal */
+ 0x0800014f, /* call &SetupP256NandMuLow */
+ 0x8c001000, /* ld *0, *0 */
+ 0x0800012e, /* call &ModInv */
+ 0x8c081700, /* ld *2, *7 */
+ 0x7c640100, /* mov r25, r1 */
+ 0x0800002c, /* call &MulMod */
+ 0x9c63eb00, /* addm r24, r11, r31 */
+ 0x904c0200, /* st *2, *3 */
+ 0xfc000000, /* nop */
+ 0x7c641300, /* mov r25, r19 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c001300, /* mov r0, r19 */
+ 0x8c081200, /* ld *2, *2 */
+ 0x7c640100, /* mov r25, r1 */
+ 0x0800002c, /* call &MulMod */
+ 0x9c001300, /* addm r0, r19, r0 */
+ 0x90500000, /* st *0, *4 */
+ 0x08000001, /* call &SetupP256PandMuLow */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x1e0: function p256scalarbasemult[21] { */
+#define CF_p256scalarbasemult_adr 480
+ 0xfc000000, /* nop */
+ 0x84004000, /* ldi r0, [#0] */
+ 0x95800000, /* lddmp r0 */
+ 0x80000000, /* movi r0.0l, #0 */
+ 0x80800001, /* movi r0.1l, #1 */
+ 0x81000018, /* movi r0.2l, #24 */
+ 0x8180000b, /* movi r0.3l, #11 */
+ 0x82000008, /* movi r0.4l, #8 */
+ 0x82800009, /* movi r0.5l, #9 */
+ 0x97800000, /* ldrfp r0 */
+ 0x8c001100, /* ld *0, *1 */
+ 0x99800000, /* ldrnd r0 */
+ 0x0800019b, /* call &get_P256B */
+ 0x90540400, /* st *4, *5 */
+ 0x90580500, /* st *5, *6 */
+ 0xfc000000, /* nop */
+ 0x8c001700, /* ld *0, *7 */
+ 0x08000168, /* call &ScalarMult_internal */
+ 0x90540b00, /* st *3++, *5 */
+ 0x90580b00, /* st *3++, *6 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x1f5: function ModInvVar[37] { */
+#define CF_ModInvVar_adr 501
+ 0x7c081f00, /* mov r2, r31 */
+ 0x7c0c1e00, /* mov r3, r30 */
+ 0x98100000, /* stmod r4 */
+ 0x981c0000, /* stmod r7 */
+ 0x7c140000, /* mov r5, r0 */
+ /*impvt_Loop: */
+ 0x44108400, /* or r4, r4, r4 */
+ 0x10001205, /* bl impvt_Uodd */
+ 0x6813e401, /* rshi r4, r4, r31 >> 1 */
+ 0x44084200, /* or r2, r2, r2 */
+ 0x10001201, /* bl impvt_Rodd */
+ 0x680be201, /* rshi r2, r2, r31 >> 1 */
+ 0x100801fa, /* b impvt_Loop */
+ /*impvt_Rodd: */
+ 0x50084700, /* add r2, r7, r2 */
+ 0x509bff00, /* addc r6, r31, r31 */
+ 0x6808c201, /* rshi r2, r2, r6 >> 1 */
+ 0x100801fa, /* b impvt_Loop */
+ /*impvt_Uodd: */
+ 0x4414a500, /* or r5, r5, r5 */
+ 0x10001210, /* bl impvt_UVodd */
+ 0x6817e501, /* rshi r5, r5, r31 >> 1 */
+ 0x440c6300, /* or r3, r3, r3 */
+ 0x1000120c, /* bl impvt_Sodd */
+ 0x680fe301, /* rshi r3, r3, r31 >> 1 */
+ 0x100801fa, /* b impvt_Loop */
+ /*impvt_Sodd: */
+ 0x500c6700, /* add r3, r7, r3 */
+ 0x509bff00, /* addc r6, r31, r31 */
+ 0x680cc301, /* rshi r3, r3, r6 >> 1 */
+ 0x100801fa, /* b impvt_Loop */
+ /*impvt_UVodd: */
+ 0x5c008500, /* cmp r5, r4 */
+ 0x10088215, /* bnc impvt_V>=U */
+ 0xa0086200, /* subm r2, r2, r3 */
+ 0x5410a400, /* sub r4, r4, r5 */
+ 0x100801fa, /* b impvt_Loop */
+ /*impvt_V>=U: */
+ 0xa00c4300, /* subm r3, r3, r2 */
+ 0x54148500, /* sub r5, r5, r4 */
+ 0x100841fa, /* bnz impvt_Loop */
+ 0x9c07e200, /* addm r1, r2, r31 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x21a: function p256verify[80] { */
+#define CF_p256verify_adr 538
+ 0x84184000, /* ldi r6, [#0] */
+ 0x95800600, /* lddmp r6 */
+ 0x81980018, /* movi r6.3l, #24 */
+ 0x82180000, /* movi r6.4l, #0 */
+ 0x82980008, /* movi r6.5l, #8 */
+ 0x83180009, /* movi r6.6l, #9 */
+ 0x8018000b, /* movi r6.0l, #11 */
+ 0x8398000c, /* movi r6.7l, #12 */
+ 0x81180018, /* movi r6.2l, #24 */
+ 0x97800600, /* ldrfp r6 */
+ 0x8c0c1300, /* ld *3, *3 */
+ 0x7c600600, /* mov r24, r6 */
+ 0x48630000, /* not r24, r24 */
+ 0x0800014f, /* call &SetupP256NandMuLow */
+ 0x5c03e600, /* cmp r6, r31 */
+ 0x10004268, /* bz fail */
+ 0x5c03a600, /* cmp r6, r29 */
+ 0x10088268, /* bnc fail */
+ 0x8c101400, /* ld *4, *4 */
+ 0x5c03e000, /* cmp r0, r31 */
+ 0x10004268, /* bz fail */
+ 0x5c03a000, /* cmp r0, r29 */
+ 0x10088268, /* bnc fail */
+ 0x080001f5, /* call &ModInvVar */
+ 0x8c0c1300, /* ld *3, *3 */
+ 0x7c640100, /* mov r25, r1 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c001300, /* mov r0, r19 */
+ 0x8c081200, /* ld *2, *2 */
+ 0x7c640100, /* mov r25, r1 */
+ 0x0800002c, /* call &MulMod */
+ 0x7c041300, /* mov r1, r19 */
+ 0x08000001, /* call &SetupP256PandMuLow */
+ 0x8c001500, /* ld *0, *5 */
+ 0x8c1c1600, /* ld *7, *6 */
+ 0x7c341e00, /* mov r13, r30 */
+ 0x0800019b, /* call &get_P256B */
+ 0x7c281e00, /* mov r10, r30 */
+ 0x0800006a, /* call &ProjAdd */
+ 0x7c0c0b00, /* mov r3, r11 */
+ 0x7c100c00, /* mov r4, r12 */
+ 0x7c140d00, /* mov r5, r13 */
+ 0x40082000, /* and r2, r0, r1 */
+ 0x7c2c1f00, /* mov r11, r31 */
+ 0x7c301e00, /* mov r12, r30 */
+ 0x7c341f00, /* mov r13, r31 */
+ 0x05100018, /* loop #256 ( */
+ 0x7c200b00, /* mov r8, r11 */
+ 0x7c240c00, /* mov r9, r12 */
+ 0x7c280d00, /* mov r10, r13 */
+ 0x0800006a, /* call &ProjAdd */
+ 0x50084200, /* add r2, r2, r2 */
+ 0x10088254, /* bnc noBoth */
+ 0x7c200300, /* mov r8, r3 */
+ 0x7c240400, /* mov r9, r4 */
+ 0x7c280500, /* mov r10, r5 */
+ 0x0800006a, /* call &ProjAdd */
+ 0x1008025f, /* b noY */
+ /*noBoth: */
+ 0x50180000, /* add r6, r0, r0 */
+ 0x1008825a, /* bnc noG */
+ 0x8c141500, /* ld *5, *5 */
+ 0x8c181600, /* ld *6, *6 */
+ 0x7c281e00, /* mov r10, r30 */
+ 0x0800006a, /* call &ProjAdd */
+ /*noG: */
+ 0x50182100, /* add r6, r1, r1 */
+ 0x1008825f, /* bnc noY */
+ 0x0800019b, /* call &get_P256B */
+ 0x7c281e00, /* mov r10, r30 */
+ 0x0800006a, /* call &ProjAdd */
+ /*noY: */
+ 0x50000000, /* add r0, r0, r0 */
+ 0x50042100, /* add r1, r1, r1 */
+ /* ) */
+ 0x7c000d00, /* mov r0, r13 */
+ 0x080001f5, /* call &ModInvVar */
+ 0x7c600100, /* mov r24, r1 */
+ 0x7c640b00, /* mov r25, r11 */
+ 0x0800002c, /* call &MulMod */
+ 0x0800014f, /* call &SetupP256NandMuLow */
+ 0xa063f300, /* subm r24, r19, r31 */
+ /*fail: */
+ 0x90440300, /* st *3, *1 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x26a: function p256scalarmult[12] { */
+#define CF_p256scalarmult_adr 618
+ 0x84004000, /* ldi r0, [#0] */
+ 0x95800000, /* lddmp r0 */
+ 0x80000000, /* movi r0.0l, #0 */
+ 0x80800001, /* movi r0.1l, #1 */
+ 0x81000018, /* movi r0.2l, #24 */
+ 0x8180000b, /* movi r0.3l, #11 */
+ 0x97800000, /* ldrfp r0 */
+ 0x8c001000, /* ld *0, *0 */
+ 0x08000168, /* call &ScalarMult_internal */
+ 0x90540b00, /* st *3++, *5 */
+ 0x90580b00, /* st *3++, *6 */
+ 0x0c000000, /* ret */
+ /* } */
+};
+/* clang-format on */
+
+#define DMEM_CELL_SIZE 32
+#define DMEM_INDEX(p, f) \
+ (((const uint8_t *) &(p)->f - (const uint8_t *) (p)) / DMEM_CELL_SIZE)
+
+/*
+ * This struct is "calling convention" for passing parameters into the
+ * code block above for ecc operations. Parameters start at &DMEM[0].
+ */
+struct DMEM_ecc {
+ uint32_t pK;
+ uint32_t pRnd;
+ uint32_t pMsg;
+ uint32_t pR;
+ uint32_t pS;
+ uint32_t pX;
+ uint32_t pY;
+ uint32_t pD;
+ p256_int k;
+ p256_int rnd;
+ p256_int msg;
+ p256_int r;
+ p256_int s;
+ p256_int x;
+ p256_int y;
+ p256_int d;
+};
+
+static void dcrypto_ecc_init(void)
+{
+ struct DMEM_ecc *pEcc =
+ (struct DMEM_ecc *) GREG32_ADDR(CRYPTO, DMEM_DUMMY);
+
+ dcrypto_imem_load(0, IMEM_dcrypto, ARRAY_SIZE(IMEM_dcrypto));
+
+ pEcc->pK = DMEM_INDEX(pEcc, k);
+ pEcc->pRnd = DMEM_INDEX(pEcc, rnd);
+ pEcc->pMsg = DMEM_INDEX(pEcc, msg);
+ pEcc->pR = DMEM_INDEX(pEcc, r);
+ pEcc->pS = DMEM_INDEX(pEcc, s);
+ pEcc->pX = DMEM_INDEX(pEcc, x);
+ pEcc->pY = DMEM_INDEX(pEcc, y);
+ pEcc->pD = DMEM_INDEX(pEcc, d);
+
+ /* (over)write first words to ensure pairwise mismatch. */
+ pEcc->k.a[0] = 1;
+ pEcc->rnd.a[0] = 2;
+ pEcc->msg.a[0] = 3;
+ pEcc->r.a[0] = 4;
+ pEcc->s.a[0] = 5;
+ pEcc->x.a[0] = 6;
+ pEcc->y.a[0] = 7;
+ pEcc->d.a[0] = 8;
+}
+
+/*
+ * Local copy function since for some reason we have p256_int as
+ * packed structs.
+ * This causes wrong writes (bytes vs. words) to the peripheral with
+ * struct copies in case the src operand is unaligned.
+ *
+ * Our peripheral dst are always aligned correctly.
+ * By making sure the src is aligned too, we get word copy behavior.
+ */
+static inline void cp8w(p256_int *dst, const p256_int *src)
+{
+ p256_int tmp;
+
+ tmp = *src;
+ *dst = tmp;
+}
+
+int dcrypto_p256_ecdsa_sign(struct drbg_ctx *drbg, const p256_int *key,
+ const p256_int *message, p256_int *r, p256_int *s)
+{
+ int i, result;
+ struct DMEM_ecc *pEcc =
+ (struct DMEM_ecc *) GREG32_ADDR(CRYPTO, DMEM_DUMMY);
+
+ dcrypto_init_and_lock();
+ dcrypto_ecc_init();
+ result = dcrypto_call(CF_p256init_adr);
+
+ /* Pick uniform 0 < k < R */
+ do {
+ drbg_generate(drbg, &pEcc->rnd);
+ } while (p256_cmp(&SECP256r1_nMin2, &pEcc->rnd) < 0);
+ drbg_exit(drbg);
+
+ p256_add_d(&pEcc->rnd, 1, &pEcc->k);
+
+ for (i = 0; i < 8; ++i)
+ pEcc->rnd.a[i] = rand();
+
+ cp8w(&pEcc->msg, message);
+ cp8w(&pEcc->d, key);
+
+ result |= dcrypto_call(CF_p256sign_adr);
+
+ cp8w(r, &pEcc->r);
+ cp8w(s, &pEcc->s);
+
+ /* Wipe d,k */
+ cp8w(&pEcc->d, &pEcc->rnd);
+ cp8w(&pEcc->k, &pEcc->rnd);
+
+ dcrypto_unlock();
+ return result == 0;
+}
+
+int dcrypto_p256_base_point_mul(const p256_int *k, p256_int *x, p256_int *y)
+{
+ int i, result;
+ struct DMEM_ecc *pEcc =
+ (struct DMEM_ecc *) GREG32_ADDR(CRYPTO, DMEM_DUMMY);
+
+ dcrypto_init_and_lock();
+ dcrypto_ecc_init();
+ result = dcrypto_call(CF_p256init_adr);
+
+ for (i = 0; i < 8; ++i)
+ pEcc->rnd.a[i] ^= rand();
+
+ cp8w(&pEcc->d, k);
+
+ result |= dcrypto_call(CF_p256scalarbasemult_adr);
+
+ cp8w(x, &pEcc->x);
+ cp8w(y, &pEcc->y);
+
+ /* Wipe d */
+ cp8w(&pEcc->d, &pEcc->rnd);
+
+ dcrypto_unlock();
+ return result == 0;
+}
+
+int dcrypto_p256_point_mul(const p256_int *k, const p256_int *in_x,
+ const p256_int *in_y, p256_int *x, p256_int *y)
+{
+ int i, result;
+ struct DMEM_ecc *pEcc =
+ (struct DMEM_ecc *) GREG32_ADDR(CRYPTO, DMEM_DUMMY);
+
+ dcrypto_init_and_lock();
+ dcrypto_ecc_init();
+ result = dcrypto_call(CF_p256init_adr);
+
+ for (i = 0; i < 8; ++i)
+ pEcc->rnd.a[i] ^= rand();
+
+ cp8w(&pEcc->k, k);
+ cp8w(&pEcc->x, in_x);
+ cp8w(&pEcc->y, in_y);
+
+ result |= dcrypto_call(CF_p256scalarmult_adr);
+
+ cp8w(x, &pEcc->x);
+ cp8w(y, &pEcc->y);
+
+ /* Wipe k,x,y */
+ cp8w(&pEcc->k, &pEcc->rnd);
+ cp8w(&pEcc->x, &pEcc->rnd);
+ cp8w(&pEcc->y, &pEcc->rnd);
+
+ dcrypto_unlock();
+ return result == 0;
+}
+
+int dcrypto_p256_ecdsa_verify(const p256_int *key_x, const p256_int *key_y,
+ const p256_int *message, const p256_int *r,
+ const p256_int *s)
+{
+ int i, result;
+ struct DMEM_ecc *pEcc =
+ (struct DMEM_ecc *) GREG32_ADDR(CRYPTO, DMEM_DUMMY);
+
+ dcrypto_init_and_lock();
+ dcrypto_ecc_init();
+ result = dcrypto_call(CF_p256init_adr);
+
+ cp8w(&pEcc->msg, message);
+ cp8w(&pEcc->r, r);
+ cp8w(&pEcc->s, s);
+ cp8w(&pEcc->x, key_x);
+ cp8w(&pEcc->y, key_y);
+
+ result |= dcrypto_call(CF_p256verify_adr);
+
+ for (i = 0; i < 8; ++i)
+ result |= (pEcc->rnd.a[i] ^ r->a[i]);
+
+ dcrypto_unlock();
+ return result == 0;
+}
+
+int dcrypto_p256_is_valid_point(const p256_int *x, const p256_int *y)
+{
+ int i, result;
+ struct DMEM_ecc *pEcc =
+ (struct DMEM_ecc *) GREG32_ADDR(CRYPTO, DMEM_DUMMY);
+
+ dcrypto_init_and_lock();
+ dcrypto_ecc_init();
+ result = dcrypto_call(CF_p256init_adr);
+
+ cp8w(&pEcc->x, x);
+ cp8w(&pEcc->y, y);
+
+ result |= dcrypto_call(CF_p256isoncurve_adr);
+
+ for (i = 0; i < 8; ++i)
+ result |= (pEcc->r.a[i] ^ pEcc->s.a[i]);
+
+ dcrypto_unlock();
+ return result == 0;
+}
diff --git a/chip/g/dcrypto/dcrypto_runtime.c b/chip/g/dcrypto/dcrypto_runtime.c
index 1c739fb0c6..e182ca5c3c 100644
--- a/chip/g/dcrypto/dcrypto_runtime.c
+++ b/chip/g/dcrypto/dcrypto_runtime.c
@@ -10,13 +10,21 @@
#define DMEM_NUM_WORDS 1024
#define IMEM_NUM_WORDS 1024
-static task_id_t my_task_id;
+static struct mutex dcrypto_mutex;
+static volatile task_id_t my_task_id;
+static int dcrypto_is_initialized;
-void dcrypto_init(void)
+void dcrypto_init_and_lock(void)
{
int i;
volatile uint32_t *ptr;
+ mutex_lock(&dcrypto_mutex);
+ my_task_id = task_get_current();
+
+ if (dcrypto_is_initialized)
+ return;
+
/* Enable PMU. */
REG_WRITE_MLV(GR_PMU_PERICLKSET0, GC_PMU_PERICLKSET0_DCRYPTO0_CLK_MASK,
GC_PMU_PERICLKSET0_DCRYPTO0_CLK_LSB, 1);
@@ -25,9 +33,12 @@ void dcrypto_init(void)
REG_WRITE_MLV(GR_PMU_RST0, GC_PMU_RST0_DCRYPTO0_MASK,
GC_PMU_RST0_DCRYPTO0_LSB, 0);
- /* Turn off random nops (for accurate measuring here). */
- /* TODO(ngm): enable for production. */
- GREG32(CRYPTO, RAND_STALL_CTL) = 0;
+ /* Turn off random nops (which are enabled by default). */
+ GWRITE_FIELD(CRYPTO, RAND_STALL_CTL, STALL_EN, 0);
+ /* Configure random nop percentage at 6%. */
+ GWRITE_FIELD(CRYPTO, RAND_STALL_CTL, FREQ, 3);
+ /* Now turn on random nops. */
+ GWRITE_FIELD(CRYPTO, RAND_STALL_CTL, STALL_EN, 1);
/* Initialize DMEM. */
ptr = GREG32_ADDR(CRYPTO, DMEM_DUMMY);
@@ -42,15 +53,25 @@ void dcrypto_init(void)
GREG32(CRYPTO, INT_STATE) = -1; /* Reset all the status bits. */
GREG32(CRYPTO, INT_ENABLE) = -1; /* Enable all status bits. */
- my_task_id = task_get_current();
task_enable_irq(GC_IRQNUM_CRYPTO0_HOST_CMD_DONE_INT);
/* Reset. */
GREG32(CRYPTO, CONTROL) = 1;
GREG32(CRYPTO, CONTROL) = 0;
+
+ dcrypto_is_initialized = 1;
+}
+
+void dcrypto_unlock(void)
+{
+ mutex_unlock(&dcrypto_mutex);
}
#define DCRYPTO_CALL_TIMEOUT_US (700 * 1000)
+/*
+ * When running on Cr50 this event belongs in the TPM task event space. Make
+ * sure there is no collision with events defined in ./common/tpm_regsters.c.
+ */
#define TASK_EVENT_DCRYPTO_DONE TASK_EVENT_CUSTOM(1)
uint32_t dcrypto_call(uint32_t adr)
@@ -65,13 +86,13 @@ uint32_t dcrypto_call(uint32_t adr)
GREG32(CRYPTO, HOST_CMD) = 0x08000000 + adr; /* Call imem:adr. */
event = task_wait_event_mask(TASK_EVENT_DCRYPTO_DONE,
- DCRYPTO_CALL_TIMEOUT_US);
+ DCRYPTO_CALL_TIMEOUT_US);
/* TODO(ngm): switch return value to an enum. */
switch (event) {
case TASK_EVENT_DCRYPTO_DONE:
- return 1;
- default:
return 0;
+ default:
+ return 1;
}
}
@@ -90,8 +111,11 @@ void dcrypto_imem_load(size_t offset, const uint32_t *opcodes,
volatile uint32_t *ptr = GREG32_ADDR(CRYPTO, IMEM_DUMMY);
ptr += offset;
- for (i = 0; i < n_opcodes; ++i)
- ptr[i] = opcodes[i];
+ /* Check first word and copy all only if different. */
+ if (ptr[0] != opcodes[0]) {
+ for (i = 0; i < n_opcodes; ++i)
+ ptr[i] = opcodes[i];
+ }
}
void dcrypto_dmem_load(size_t offset, const void *words, size_t n_words)
diff --git a/chip/g/dcrypto/dcrypto_sha512.c b/chip/g/dcrypto/dcrypto_sha512.c
new file mode 100644
index 0000000000..cdaff6853b
--- /dev/null
+++ b/chip/g/dcrypto/dcrypto_sha512.c
@@ -0,0 +1,772 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "dcrypto.h"
+#include "internal.h"
+#include "registers.h"
+
+#include "cryptoc/sha512.h"
+
+#ifdef CRYPTO_TEST_SETUP
+
+/* test and benchmark */
+#include "common.h"
+#include "console.h"
+#include "hooks.h"
+#include "task.h"
+
+#define cyclecounter() GREG32(M3, DWT_CYCCNT)
+#define START_PROFILE(x) \
+ { \
+ x -= cyclecounter(); \
+ }
+#define END_PROFILE(x) \
+ { \
+ x += cyclecounter(); \
+ }
+static uint32_t t_sw;
+static uint32_t t_hw;
+static uint32_t t_transform;
+static uint32_t t_dcrypto;
+
+#else /* CRYPTO_TEST_SETUP */
+
+#define START_PROFILE(x)
+#define END_PROFILE(x)
+
+#endif /* CRYPTO_TEST_SETUP */
+
+/* auto-generated from go test haven -test.run=TestSha512 -test.v */
+/* clang-format off */
+static const uint32_t IMEM_dcrypto[] = {
+/* @0x0: function tag[1] { */
+#define CF_tag_adr 0
+ 0xf8000003, /* sigini #3 */
+/* } */
+/* @0x1: function expandw[84] { */
+#define CF_expandw_adr 1
+ 0x4c3def00, /* xor r15, r15, r15 */
+ 0x803c0013, /* movi r15.0l, #19 */
+ 0x80bc0016, /* movi r15.1l, #22 */
+ 0x97800f00, /* ldrfp r15 */
+ 0x05004003, /* loop #4 ( */
+ 0x8c001800, /* ld *0, *0++ */
+ 0x906c0800, /* st *0++, *3++ */
+ 0xfc000000, /* nop */
+ /* ) */
+ 0x0501004a, /* loop #16 ( */
+ 0x684a6080, /* rshi r18, r0, r19 >> 128 */
+ 0x68443340, /* rshi r17, r19, r1 >> 64 */
+ 0x683e3201, /* rshi r15, r18, r17 >> 1 */
+ 0x68423208, /* rshi r16, r18, r17 >> 8 */
+ 0x4c3e0f00, /* xor r15, r15, r16 */
+ 0x6843f207, /* rshi r16, r18, r31 >> 7 */
+ 0x4c3e0f00, /* xor r15, r15, r16 */
+ 0x505df398, /* add r23, r19, r15 >> 192 */
+ 0x505eb788, /* add r23, r23, r21 >> 64 */
+ 0x684ac0c0, /* rshi r18, r0, r22 >> 192 */
+ 0x68443680, /* rshi r17, r22, r1 >> 128 */
+ 0x683e3213, /* rshi r15, r18, r17 >> 19 */
+ 0x6842323d, /* rshi r16, r18, r17 >> 61 */
+ 0x4c3e0f00, /* xor r15, r15, r16 */
+ 0x6843f206, /* rshi r16, r18, r31 >> 6 */
+ 0x4c3e0f00, /* xor r15, r15, r16 */
+ 0x505df798, /* add r23, r23, r15 >> 192 */
+ 0x684a60c0, /* rshi r18, r0, r19 >> 192 */
+ 0x68443380, /* rshi r17, r19, r1 >> 128 */
+ 0x683e3201, /* rshi r15, r18, r17 >> 1 */
+ 0x68423208, /* rshi r16, r18, r17 >> 8 */
+ 0x4c3e0f00, /* xor r15, r15, r16 */
+ 0x6843f207, /* rshi r16, r18, r31 >> 7 */
+ 0x4c3e0f00, /* xor r15, r15, r16 */
+ 0x50627f88, /* add r24, r31, r19 >> 64 */
+ 0x5061f898, /* add r24, r24, r15 >> 192 */
+ 0x5062b890, /* add r24, r24, r21 >> 128 */
+ 0x684416c0, /* rshi r17, r22, r0 >> 192 */
+ 0x683e3613, /* rshi r15, r22, r17 >> 19 */
+ 0x6842363d, /* rshi r16, r22, r17 >> 61 */
+ 0x4c3e0f00, /* xor r15, r15, r16 */
+ 0x6843f606, /* rshi r16, r22, r31 >> 6 */
+ 0x4c3e0f00, /* xor r15, r15, r16 */
+ 0x5061f898, /* add r24, r24, r15 >> 192 */
+ 0x684433c0, /* rshi r17, r19, r1 >> 192 */
+ 0x683e3301, /* rshi r15, r19, r17 >> 1 */
+ 0x68423308, /* rshi r16, r19, r17 >> 8 */
+ 0x4c3e0f00, /* xor r15, r15, r16 */
+ 0x6843f307, /* rshi r16, r19, r31 >> 7 */
+ 0x4c3e0f00, /* xor r15, r15, r16 */
+ 0x50667f90, /* add r25, r31, r19 >> 128 */
+ 0x5065f998, /* add r25, r25, r15 >> 192 */
+ 0x5066b998, /* add r25, r25, r21 >> 192 */
+ 0x684ae040, /* rshi r18, r0, r23 >> 64 */
+ 0x683ef213, /* rshi r15, r18, r23 >> 19 */
+ 0x6842f23d, /* rshi r16, r18, r23 >> 61 */
+ 0x4c3e0f00, /* xor r15, r15, r16 */
+ 0x6843f206, /* rshi r16, r18, r31 >> 6 */
+ 0x4c3e0f00, /* xor r15, r15, r16 */
+ 0x5065f998, /* add r25, r25, r15 >> 192 */
+ 0x684a8040, /* rshi r18, r0, r20 >> 64 */
+ 0x683e9201, /* rshi r15, r18, r20 >> 1 */
+ 0x68429208, /* rshi r16, r18, r20 >> 8 */
+ 0x4c3e0f00, /* xor r15, r15, r16 */
+ 0x6843f207, /* rshi r16, r18, r31 >> 7 */
+ 0x4c3e0f00, /* xor r15, r15, r16 */
+ 0x506a7f98, /* add r26, r31, r19 >> 192 */
+ 0x5069fa98, /* add r26, r26, r15 >> 192 */
+ 0x506ada00, /* add r26, r26, r22 */
+ 0x684b0040, /* rshi r18, r0, r24 >> 64 */
+ 0x683f1213, /* rshi r15, r18, r24 >> 19 */
+ 0x6843123d, /* rshi r16, r18, r24 >> 61 */
+ 0x4c3e0f00, /* xor r15, r15, r16 */
+ 0x6843f206, /* rshi r16, r18, r31 >> 6 */
+ 0x4c3e0f00, /* xor r15, r15, r16 */
+ 0x5069fa98, /* add r26, r26, r15 >> 192 */
+ 0x7c4c1400, /* mov r19, r20 */
+ 0x7c501500, /* mov r20, r21 */
+ 0x7c541600, /* mov r21, r22 */
+ 0x685af640, /* rshi r22, r22, r23 >> 64 */
+ 0x685b1640, /* rshi r22, r22, r24 >> 64 */
+ 0x685b3640, /* rshi r22, r22, r25 >> 64 */
+ 0x685b5640, /* rshi r22, r22, r26 >> 64 */
+ 0x906c0100, /* st *1, *3++ */
+ /* ) */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x55: function Sha512_a[125] { */
+#define CF_Sha512_a_adr 85
+ 0x68580c40, /* rshi r22, r12, r0 >> 64 */
+ 0x683c161c, /* rshi r15, r22, r0 >> 28 */
+ 0x68541622, /* rshi r21, r22, r0 >> 34 */
+ 0x4c3eaf00, /* xor r15, r15, r21 */
+ 0x68541627, /* rshi r21, r22, r0 >> 39 */
+ 0x4c3eaf00, /* xor r15, r15, r21 */
+ 0x40402000, /* and r16, r0, r1 */
+ 0x40544000, /* and r21, r0, r2 */
+ 0x4c42b000, /* xor r16, r16, r21 */
+ 0x40544100, /* and r21, r1, r2 */
+ 0x4c42b000, /* xor r16, r16, r21 */
+ 0x68458fc0, /* rshi r17, r15, r12 >> 192 */
+ 0x50461100, /* add r17, r17, r16 */
+ 0x68588d40, /* rshi r22, r13, r4 >> 64 */
+ 0x6848960e, /* rshi r18, r22, r4 >> 14 */
+ 0x68549612, /* rshi r21, r22, r4 >> 18 */
+ 0x4c4ab200, /* xor r18, r18, r21 */
+ 0x684c9629, /* rshi r19, r22, r4 >> 41 */
+ 0x4c4a7200, /* xor r18, r18, r19 */
+ 0x404ca400, /* and r19, r4, r5 */
+ 0x48548000, /* not r21, r4 */
+ 0x4054d500, /* and r21, r21, r6 */
+ 0x4c4eb300, /* xor r19, r19, r21 */
+ 0x6851b2c0, /* rshi r20, r18, r13 >> 192 */
+ 0x5050f400, /* add r20, r20, r7 */
+ 0x50515480, /* add r20, r20, r10 >> 0 */
+ 0x68558b00, /* rshi r21, r11, r12 >> 0 */
+ 0x50567500, /* add r21, r21, r19 */
+ 0x5052b400, /* add r20, r20, r21 */
+ 0x500e8300, /* add r3, r3, r20 */
+ 0x501e3400, /* add r7, r20, r17 */
+ 0x6858ec40, /* rshi r22, r12, r7 >> 64 */
+ 0x683cf61c, /* rshi r15, r22, r7 >> 28 */
+ 0x6854f622, /* rshi r21, r22, r7 >> 34 */
+ 0x4c3eaf00, /* xor r15, r15, r21 */
+ 0x6854f627, /* rshi r21, r22, r7 >> 39 */
+ 0x4c3eaf00, /* xor r15, r15, r21 */
+ 0x40400700, /* and r16, r7, r0 */
+ 0x40542700, /* and r21, r7, r1 */
+ 0x4c42b000, /* xor r16, r16, r21 */
+ 0x40542000, /* and r21, r0, r1 */
+ 0x4c42b000, /* xor r16, r16, r21 */
+ 0x68458fc0, /* rshi r17, r15, r12 >> 192 */
+ 0x50461100, /* add r17, r17, r16 */
+ 0x68586d40, /* rshi r22, r13, r3 >> 64 */
+ 0x6848760e, /* rshi r18, r22, r3 >> 14 */
+ 0x68547612, /* rshi r21, r22, r3 >> 18 */
+ 0x4c4ab200, /* xor r18, r18, r21 */
+ 0x684c7629, /* rshi r19, r22, r3 >> 41 */
+ 0x4c4a7200, /* xor r18, r18, r19 */
+ 0x404c8300, /* and r19, r3, r4 */
+ 0x48546000, /* not r21, r3 */
+ 0x4054b500, /* and r21, r21, r5 */
+ 0x4c4eb300, /* xor r19, r19, r21 */
+ 0x6851b2c0, /* rshi r20, r18, r13 >> 192 */
+ 0x5050d400, /* add r20, r20, r6 */
+ 0x50515488, /* add r20, r20, r10 >> 64 */
+ 0x68558b40, /* rshi r21, r11, r12 >> 64 */
+ 0x50567500, /* add r21, r21, r19 */
+ 0x5052b400, /* add r20, r20, r21 */
+ 0x500a8200, /* add r2, r2, r20 */
+ 0x501a3400, /* add r6, r20, r17 */
+ 0x6858cc40, /* rshi r22, r12, r6 >> 64 */
+ 0x683cd61c, /* rshi r15, r22, r6 >> 28 */
+ 0x6854d622, /* rshi r21, r22, r6 >> 34 */
+ 0x4c3eaf00, /* xor r15, r15, r21 */
+ 0x6854d627, /* rshi r21, r22, r6 >> 39 */
+ 0x4c3eaf00, /* xor r15, r15, r21 */
+ 0x4040e600, /* and r16, r6, r7 */
+ 0x40540600, /* and r21, r6, r0 */
+ 0x4c42b000, /* xor r16, r16, r21 */
+ 0x40540700, /* and r21, r7, r0 */
+ 0x4c42b000, /* xor r16, r16, r21 */
+ 0x68458fc0, /* rshi r17, r15, r12 >> 192 */
+ 0x50461100, /* add r17, r17, r16 */
+ 0x68584d40, /* rshi r22, r13, r2 >> 64 */
+ 0x6848560e, /* rshi r18, r22, r2 >> 14 */
+ 0x68545612, /* rshi r21, r22, r2 >> 18 */
+ 0x4c4ab200, /* xor r18, r18, r21 */
+ 0x684c5629, /* rshi r19, r22, r2 >> 41 */
+ 0x4c4a7200, /* xor r18, r18, r19 */
+ 0x404c6200, /* and r19, r2, r3 */
+ 0x48544000, /* not r21, r2 */
+ 0x40549500, /* and r21, r21, r4 */
+ 0x4c4eb300, /* xor r19, r19, r21 */
+ 0x6851b2c0, /* rshi r20, r18, r13 >> 192 */
+ 0x5050b400, /* add r20, r20, r5 */
+ 0x50515490, /* add r20, r20, r10 >> 128 */
+ 0x68558b80, /* rshi r21, r11, r12 >> 128 */
+ 0x50567500, /* add r21, r21, r19 */
+ 0x5052b400, /* add r20, r20, r21 */
+ 0x50068100, /* add r1, r1, r20 */
+ 0x50163400, /* add r5, r20, r17 */
+ 0x6858ac40, /* rshi r22, r12, r5 >> 64 */
+ 0x683cb61c, /* rshi r15, r22, r5 >> 28 */
+ 0x6854b622, /* rshi r21, r22, r5 >> 34 */
+ 0x4c3eaf00, /* xor r15, r15, r21 */
+ 0x6854b627, /* rshi r21, r22, r5 >> 39 */
+ 0x4c3eaf00, /* xor r15, r15, r21 */
+ 0x4040c500, /* and r16, r5, r6 */
+ 0x4054e500, /* and r21, r5, r7 */
+ 0x4c42b000, /* xor r16, r16, r21 */
+ 0x4054e600, /* and r21, r6, r7 */
+ 0x4c42b000, /* xor r16, r16, r21 */
+ 0x68458fc0, /* rshi r17, r15, r12 >> 192 */
+ 0x50461100, /* add r17, r17, r16 */
+ 0x68582d40, /* rshi r22, r13, r1 >> 64 */
+ 0x6848360e, /* rshi r18, r22, r1 >> 14 */
+ 0x68543612, /* rshi r21, r22, r1 >> 18 */
+ 0x4c4ab200, /* xor r18, r18, r21 */
+ 0x684c3629, /* rshi r19, r22, r1 >> 41 */
+ 0x4c4a7200, /* xor r18, r18, r19 */
+ 0x404c4100, /* and r19, r1, r2 */
+ 0x48542000, /* not r21, r1 */
+ 0x40547500, /* and r21, r21, r3 */
+ 0x4c4eb300, /* xor r19, r19, r21 */
+ 0x6851b2c0, /* rshi r20, r18, r13 >> 192 */
+ 0x50509400, /* add r20, r20, r4 */
+ 0x50515498, /* add r20, r20, r10 >> 192 */
+ 0x68558bc0, /* rshi r21, r11, r12 >> 192 */
+ 0x50567500, /* add r21, r21, r19 */
+ 0x5052b400, /* add r20, r20, r21 */
+ 0x50028000, /* add r0, r0, r20 */
+ 0x50123400, /* add r4, r20, r17 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0xd2: function Sha512_b[125] { */
+#define CF_Sha512_b_adr 210
+ 0x68588d40, /* rshi r22, r13, r4 >> 64 */
+ 0x683c961c, /* rshi r15, r22, r4 >> 28 */
+ 0x68549622, /* rshi r21, r22, r4 >> 34 */
+ 0x4c3eaf00, /* xor r15, r15, r21 */
+ 0x68549627, /* rshi r21, r22, r4 >> 39 */
+ 0x4c3eaf00, /* xor r15, r15, r21 */
+ 0x4040a400, /* and r16, r4, r5 */
+ 0x4054c400, /* and r21, r4, r6 */
+ 0x4c42b000, /* xor r16, r16, r21 */
+ 0x4054c500, /* and r21, r5, r6 */
+ 0x4c42b000, /* xor r16, r16, r21 */
+ 0x6845afc0, /* rshi r17, r15, r13 >> 192 */
+ 0x50461100, /* add r17, r17, r16 */
+ 0x68580c40, /* rshi r22, r12, r0 >> 64 */
+ 0x6848160e, /* rshi r18, r22, r0 >> 14 */
+ 0x68541612, /* rshi r21, r22, r0 >> 18 */
+ 0x4c4ab200, /* xor r18, r18, r21 */
+ 0x684c1629, /* rshi r19, r22, r0 >> 41 */
+ 0x4c4a7200, /* xor r18, r18, r19 */
+ 0x404c2000, /* and r19, r0, r1 */
+ 0x48540000, /* not r21, r0 */
+ 0x40545500, /* and r21, r21, r2 */
+ 0x4c4eb300, /* xor r19, r19, r21 */
+ 0x685192c0, /* rshi r20, r18, r12 >> 192 */
+ 0x50507400, /* add r20, r20, r3 */
+ 0x50515480, /* add r20, r20, r10 >> 0 */
+ 0x6855ab00, /* rshi r21, r11, r13 >> 0 */
+ 0x50567500, /* add r21, r21, r19 */
+ 0x5052b400, /* add r20, r20, r21 */
+ 0x501e8700, /* add r7, r7, r20 */
+ 0x500e3400, /* add r3, r20, r17 */
+ 0x68586d40, /* rshi r22, r13, r3 >> 64 */
+ 0x683c761c, /* rshi r15, r22, r3 >> 28 */
+ 0x68547622, /* rshi r21, r22, r3 >> 34 */
+ 0x4c3eaf00, /* xor r15, r15, r21 */
+ 0x68547627, /* rshi r21, r22, r3 >> 39 */
+ 0x4c3eaf00, /* xor r15, r15, r21 */
+ 0x40408300, /* and r16, r3, r4 */
+ 0x4054a300, /* and r21, r3, r5 */
+ 0x4c42b000, /* xor r16, r16, r21 */
+ 0x4054a400, /* and r21, r4, r5 */
+ 0x4c42b000, /* xor r16, r16, r21 */
+ 0x6845afc0, /* rshi r17, r15, r13 >> 192 */
+ 0x50461100, /* add r17, r17, r16 */
+ 0x6858ec40, /* rshi r22, r12, r7 >> 64 */
+ 0x6848f60e, /* rshi r18, r22, r7 >> 14 */
+ 0x6854f612, /* rshi r21, r22, r7 >> 18 */
+ 0x4c4ab200, /* xor r18, r18, r21 */
+ 0x684cf629, /* rshi r19, r22, r7 >> 41 */
+ 0x4c4a7200, /* xor r18, r18, r19 */
+ 0x404c0700, /* and r19, r7, r0 */
+ 0x4854e000, /* not r21, r7 */
+ 0x40543500, /* and r21, r21, r1 */
+ 0x4c4eb300, /* xor r19, r19, r21 */
+ 0x685192c0, /* rshi r20, r18, r12 >> 192 */
+ 0x50505400, /* add r20, r20, r2 */
+ 0x50515488, /* add r20, r20, r10 >> 64 */
+ 0x6855ab40, /* rshi r21, r11, r13 >> 64 */
+ 0x50567500, /* add r21, r21, r19 */
+ 0x5052b400, /* add r20, r20, r21 */
+ 0x501a8600, /* add r6, r6, r20 */
+ 0x500a3400, /* add r2, r20, r17 */
+ 0x68584d40, /* rshi r22, r13, r2 >> 64 */
+ 0x683c561c, /* rshi r15, r22, r2 >> 28 */
+ 0x68545622, /* rshi r21, r22, r2 >> 34 */
+ 0x4c3eaf00, /* xor r15, r15, r21 */
+ 0x68545627, /* rshi r21, r22, r2 >> 39 */
+ 0x4c3eaf00, /* xor r15, r15, r21 */
+ 0x40406200, /* and r16, r2, r3 */
+ 0x40548200, /* and r21, r2, r4 */
+ 0x4c42b000, /* xor r16, r16, r21 */
+ 0x40548300, /* and r21, r3, r4 */
+ 0x4c42b000, /* xor r16, r16, r21 */
+ 0x6845afc0, /* rshi r17, r15, r13 >> 192 */
+ 0x50461100, /* add r17, r17, r16 */
+ 0x6858cc40, /* rshi r22, r12, r6 >> 64 */
+ 0x6848d60e, /* rshi r18, r22, r6 >> 14 */
+ 0x6854d612, /* rshi r21, r22, r6 >> 18 */
+ 0x4c4ab200, /* xor r18, r18, r21 */
+ 0x684cd629, /* rshi r19, r22, r6 >> 41 */
+ 0x4c4a7200, /* xor r18, r18, r19 */
+ 0x404ce600, /* and r19, r6, r7 */
+ 0x4854c000, /* not r21, r6 */
+ 0x40541500, /* and r21, r21, r0 */
+ 0x4c4eb300, /* xor r19, r19, r21 */
+ 0x685192c0, /* rshi r20, r18, r12 >> 192 */
+ 0x50503400, /* add r20, r20, r1 */
+ 0x50515490, /* add r20, r20, r10 >> 128 */
+ 0x6855ab80, /* rshi r21, r11, r13 >> 128 */
+ 0x50567500, /* add r21, r21, r19 */
+ 0x5052b400, /* add r20, r20, r21 */
+ 0x50168500, /* add r5, r5, r20 */
+ 0x50063400, /* add r1, r20, r17 */
+ 0x68582d40, /* rshi r22, r13, r1 >> 64 */
+ 0x683c361c, /* rshi r15, r22, r1 >> 28 */
+ 0x68543622, /* rshi r21, r22, r1 >> 34 */
+ 0x4c3eaf00, /* xor r15, r15, r21 */
+ 0x68543627, /* rshi r21, r22, r1 >> 39 */
+ 0x4c3eaf00, /* xor r15, r15, r21 */
+ 0x40404100, /* and r16, r1, r2 */
+ 0x40546100, /* and r21, r1, r3 */
+ 0x4c42b000, /* xor r16, r16, r21 */
+ 0x40546200, /* and r21, r2, r3 */
+ 0x4c42b000, /* xor r16, r16, r21 */
+ 0x6845afc0, /* rshi r17, r15, r13 >> 192 */
+ 0x50461100, /* add r17, r17, r16 */
+ 0x6858ac40, /* rshi r22, r12, r5 >> 64 */
+ 0x6848b60e, /* rshi r18, r22, r5 >> 14 */
+ 0x6854b612, /* rshi r21, r22, r5 >> 18 */
+ 0x4c4ab200, /* xor r18, r18, r21 */
+ 0x684cb629, /* rshi r19, r22, r5 >> 41 */
+ 0x4c4a7200, /* xor r18, r18, r19 */
+ 0x404cc500, /* and r19, r5, r6 */
+ 0x4854a000, /* not r21, r5 */
+ 0x4054f500, /* and r21, r21, r7 */
+ 0x4c4eb300, /* xor r19, r19, r21 */
+ 0x685192c0, /* rshi r20, r18, r12 >> 192 */
+ 0x50501400, /* add r20, r20, r0 */
+ 0x50515498, /* add r20, r20, r10 >> 192 */
+ 0x6855abc0, /* rshi r21, r11, r13 >> 192 */
+ 0x50567500, /* add r21, r21, r19 */
+ 0x5052b400, /* add r20, r20, r21 */
+ 0x50128400, /* add r4, r4, r20 */
+ 0x50023400, /* add r0, r20, r17 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x14f: function compress[70] { */
+#define CF_compress_adr 335
+ 0xfc000000, /* nop */
+ 0x4c7fff00, /* xor r31, r31, r31 */
+ 0x4c000000, /* xor r0, r0, r0 */
+ 0x4c042100, /* xor r1, r1, r1 */
+ 0x55000001, /* subi r0, r0, #1 */
+ 0x55040101, /* subi r1, r1, #1 */
+ 0x84204100, /* ldi r8, [#8] */
+ 0x94800800, /* ldlc r8 */
+ 0x4c3def00, /* xor r15, r15, r15 */
+ 0x803c000a, /* movi r15.0l, #10 */
+ 0x95800f00, /* lddmp r15 */
+ 0x06000039, /* loop *0 ( */
+ 0x953c0000, /* stdmp r15 */
+ 0x81bc002a, /* movi r15.3l, #42 */
+ 0x95800f00, /* lddmp r15 */
+ 0x08000001, /* call &expandw */
+ 0x84004000, /* ldi r0, [#0] */
+ 0x84044020, /* ldi r1, [#1] */
+ 0x84084040, /* ldi r2, [#2] */
+ 0x840c4060, /* ldi r3, [#3] */
+ 0x84104080, /* ldi r4, [#4] */
+ 0x841440a0, /* ldi r5, [#5] */
+ 0x841840c0, /* ldi r6, [#6] */
+ 0x841c40e0, /* ldi r7, [#7] */
+ 0x4c3def00, /* xor r15, r15, r15 */
+ 0x803c0060, /* movi r15.0l, #96 */
+ 0x80bc000a, /* movi r15.1l, #10 */
+ 0x813c000b, /* movi r15.2l, #11 */
+ 0x96800f00, /* lddrp r15 */
+ 0x97800f00, /* ldrfp r15 */
+ 0x953c0000, /* stdmp r15 */
+ 0x81bc002a, /* movi r15.3l, #42 */
+ 0x95800f00, /* lddmp r15 */
+ 0x4c318c00, /* xor r12, r12, r12 */
+ 0x4c35ad00, /* xor r13, r13, r13 */
+ 0x55300c01, /* subi r12, r12, #1 */
+ 0x55340d01, /* subi r13, r13, #1 */
+ 0x0500a007, /* loop #10 ( */
+ 0x8c440800, /* ldc *1, *0++ */
+ 0x8c081b00, /* ld *2, *3++ */
+ 0x08000055, /* call &Sha512_a */
+ 0x8c440800, /* ldc *1, *0++ */
+ 0x8c081b00, /* ld *2, *3++ */
+ 0x080000d2, /* call &Sha512_b */
+ 0xfc000000, /* nop */
+ /* ) */
+ 0x843c4000, /* ldi r15, [#0] */
+ 0x5001e000, /* add r0, r0, r15 */
+ 0x843c4020, /* ldi r15, [#1] */
+ 0x5005e100, /* add r1, r1, r15 */
+ 0x843c4040, /* ldi r15, [#2] */
+ 0x5009e200, /* add r2, r2, r15 */
+ 0x843c4060, /* ldi r15, [#3] */
+ 0x500de300, /* add r3, r3, r15 */
+ 0x843c4080, /* ldi r15, [#4] */
+ 0x5011e400, /* add r4, r4, r15 */
+ 0x843c40a0, /* ldi r15, [#5] */
+ 0x5015e500, /* add r5, r5, r15 */
+ 0x843c40c0, /* ldi r15, [#6] */
+ 0x5019e600, /* add r6, r6, r15 */
+ 0x843c40e0, /* ldi r15, [#7] */
+ 0x501de700, /* add r7, r7, r15 */
+ 0x88004000, /* sti r0, [#0] */
+ 0x88044020, /* sti r1, [#1] */
+ 0x88084040, /* sti r2, [#2] */
+ 0x880c4060, /* sti r3, [#3] */
+ 0x88104080, /* sti r4, [#4] */
+ 0x881440a0, /* sti r5, [#5] */
+ 0x881840c0, /* sti r6, [#6] */
+ 0x881c40e0, /* sti r7, [#7] */
+ /* ) */
+ 0x0c000000, /* ret */
+ /* } */
+};
+/* clang-format on */
+
+struct DMEM_sha512 {
+ uint64_t H0[4];
+ uint64_t H1[4];
+ uint64_t H2[4];
+ uint64_t H3[4];
+ uint64_t H4[4];
+ uint64_t H5[4];
+ uint64_t H6[4];
+ uint64_t H7[4];
+ uint32_t nblocks;
+ uint32_t unused[2 * 8 - 1];
+ uint32_t input[4 * 8 * 8]; // dmem[10..41]
+};
+
+static void copy_words(const void *in, uint32_t *dst, size_t nwords)
+{
+ const uint32_t *src = (const uint32_t *) in;
+
+ do {
+ uint32_t w1 = __builtin_bswap32(*src++);
+ uint32_t w2 = __builtin_bswap32(*src++);
+ *dst++ = w2;
+ *dst++ = w1;
+ } while (nwords -= 2);
+}
+
+static void dcrypto_SHA512_setup(void)
+{
+ dcrypto_imem_load(0, IMEM_dcrypto, ARRAY_SIZE(IMEM_dcrypto));
+}
+
+static void dcrypto_SHA512_Transform(LITE_SHA512_CTX *ctx, const uint32_t *buf,
+ size_t nwords)
+{
+ int result = 0;
+ struct DMEM_sha512 *p512 =
+ (struct DMEM_sha512 *) GREG32_ADDR(CRYPTO, DMEM_DUMMY);
+
+ START_PROFILE(t_transform)
+
+ /* Pass in H[] */
+ p512->H0[0] = ctx->state[0];
+ p512->H1[0] = ctx->state[1];
+ p512->H2[0] = ctx->state[2];
+ p512->H3[0] = ctx->state[3];
+ p512->H4[0] = ctx->state[4];
+ p512->H5[0] = ctx->state[5];
+ p512->H6[0] = ctx->state[6];
+ p512->H7[0] = ctx->state[7];
+
+ p512->nblocks = nwords / 32;
+
+ /* Pass in buf[] */
+ copy_words(buf, p512->input, nwords);
+
+ START_PROFILE(t_dcrypto)
+ result |= dcrypto_call(CF_compress_adr);
+ END_PROFILE(t_dcrypto)
+
+ /* Retrieve new H[] */
+ ctx->state[0] = p512->H0[0];
+ ctx->state[1] = p512->H1[0];
+ ctx->state[2] = p512->H2[0];
+ ctx->state[3] = p512->H3[0];
+ ctx->state[4] = p512->H4[0];
+ ctx->state[5] = p512->H5[0];
+ ctx->state[6] = p512->H6[0];
+ ctx->state[7] = p512->H7[0];
+
+ /* TODO: errno or such to capture errors */
+ (void) (result == 0);
+
+ END_PROFILE(t_transform)
+}
+
+static void dcrypto_SHA512_update(LITE_SHA512_CTX *ctx, const void *data,
+ size_t len)
+{
+ int i = (int) (ctx->count & (sizeof(ctx->buf) - 1));
+ const uint8_t *p = (const uint8_t *) data;
+ uint8_t *d = &ctx->buf[i];
+
+ ctx->count += len;
+
+ dcrypto_init_and_lock();
+ dcrypto_SHA512_setup();
+
+ /* Take fast path for 32-bit aligned 1KB inputs */
+ if (i == 0 && len == 1024 && (((intptr_t) data) & 3) == 0) {
+ dcrypto_SHA512_Transform(ctx, (const uint32_t *) data, 8 * 32);
+ } else {
+ if (len <= sizeof(ctx->buf) - i) {
+ memcpy(d, p, len);
+ if (len == sizeof(ctx->buf) - i) {
+ dcrypto_SHA512_Transform(
+ ctx, (uint32_t *) (ctx->buf), 32);
+ }
+ } else {
+ memcpy(d, p, sizeof(ctx->buf) - i);
+ dcrypto_SHA512_Transform(ctx, (uint32_t *) (ctx->buf),
+ 32);
+ d = ctx->buf;
+ len -= (sizeof(ctx->buf) - i);
+ p += (sizeof(ctx->buf) - i);
+ while (len >= sizeof(ctx->buf)) {
+ memcpy(d, p, sizeof(ctx->buf));
+ p += sizeof(ctx->buf);
+ len -= sizeof(ctx->buf);
+ dcrypto_SHA512_Transform(
+ ctx, (uint32_t *) (ctx->buf), 32);
+ }
+ /* Leave remainder in ctx->buf */
+ memcpy(d, p, len);
+ }
+ }
+ dcrypto_unlock();
+}
+
+static const uint8_t *dcrypto_SHA512_final(LITE_SHA512_CTX *ctx)
+{
+ uint64_t cnt = ctx->count * 8;
+ int i = (int) (ctx->count & (sizeof(ctx->buf) - 1));
+ uint8_t *p = &ctx->buf[i];
+
+ *p++ = 0x80;
+ i++;
+
+ dcrypto_init_and_lock();
+ dcrypto_SHA512_setup();
+
+ if (i > sizeof(ctx->buf) - 16) {
+ memset(p, 0, sizeof(ctx->buf) - i);
+ dcrypto_SHA512_Transform(ctx, (uint32_t *) (ctx->buf), 32);
+ i = 0;
+ p = ctx->buf;
+ }
+
+ memset(p, 0, sizeof(ctx->buf) - 8 - i);
+ p += sizeof(ctx->buf) - 8 - i;
+
+ for (i = 0; i < 8; ++i) {
+ uint8_t tmp = (uint8_t)(cnt >> 56);
+ cnt <<= 8;
+ *p++ = tmp;
+ }
+
+ dcrypto_SHA512_Transform(ctx, (uint32_t *) (ctx->buf), 32);
+
+ p = ctx->buf;
+ for (i = 0; i < 8; i++) {
+ uint64_t tmp = ctx->state[i];
+ *p++ = (uint8_t)(tmp >> 56);
+ *p++ = (uint8_t)(tmp >> 48);
+ *p++ = (uint8_t)(tmp >> 40);
+ *p++ = (uint8_t)(tmp >> 32);
+ *p++ = (uint8_t)(tmp >> 24);
+ *p++ = (uint8_t)(tmp >> 16);
+ *p++ = (uint8_t)(tmp >> 8);
+ *p++ = (uint8_t)(tmp >> 0);
+ }
+
+ dcrypto_unlock();
+ return ctx->buf;
+}
+
+const uint8_t *DCRYPTO_SHA512_hash(const void *data, size_t len,
+ uint8_t *digest)
+{
+ LITE_SHA512_CTX ctx;
+
+ DCRYPTO_SHA512_init(&ctx);
+ dcrypto_SHA512_update(&ctx, data, len);
+ memcpy(digest, dcrypto_SHA512_final(&ctx), SHA512_DIGEST_SIZE);
+
+ return digest;
+}
+
+static const HASH_VTAB dcrypto_SHA512_VTAB = {
+ DCRYPTO_SHA512_init, dcrypto_SHA512_update, dcrypto_SHA512_final,
+ DCRYPTO_SHA512_hash, SHA512_DIGEST_SIZE};
+
+void DCRYPTO_SHA512_init(LITE_SHA512_CTX *ctx)
+{
+ SHA512_init(ctx);
+ ctx->f = &dcrypto_SHA512_VTAB;
+}
+
+#ifdef CRYPTO_TEST_SETUP
+
+static uint32_t msg[256]; // 1KB
+static int msg_len;
+static int msg_loops;
+static LITE_SHA512_CTX sw;
+static LITE_SHA512_CTX hw;
+static const uint8_t *sw_digest;
+static const uint8_t *hw_digest;
+static uint32_t t_sw;
+static uint32_t t_hw;
+
+static void run_sha512_cmd(void)
+{
+ int i;
+
+ t_transform = 0;
+ t_dcrypto = 0;
+ t_sw = 0;
+ t_hw = 0;
+
+ START_PROFILE(t_sw)
+ SHA512_init(&sw);
+ for (i = 0; i < msg_loops; ++i) {
+ HASH_update(&sw, msg, msg_len);
+ }
+ sw_digest = HASH_final(&sw);
+ END_PROFILE(t_sw)
+
+ START_PROFILE(t_hw)
+ DCRYPTO_SHA512_init(&hw);
+ for (i = 0; i < msg_loops; ++i) {
+ HASH_update(&hw, msg, msg_len);
+ }
+ hw_digest = HASH_final(&hw);
+ END_PROFILE(t_hw)
+
+ ccprintf("sw(%u):\n", t_sw);
+ for (i = 0; i < 64; ++i)
+ ccprintf("%02x", sw_digest[i]);
+ ccprintf("\n");
+
+ ccprintf("hw(%u/%u/%u):\n", t_hw, t_transform, t_dcrypto);
+ for (i = 0; i < 64; ++i)
+ ccprintf("%02x", hw_digest[i]);
+ ccprintf("\n");
+
+ task_set_event(TASK_ID_CONSOLE, TASK_EVENT_CUSTOM(1), 0);
+}
+DECLARE_DEFERRED(run_sha512_cmd);
+
+static int cmd_sha512_bench(int argc, char *argv[])
+{
+ const int max_time = 1000000;
+ uint32_t events;
+
+ memset(msg, '!', sizeof(msg));
+
+ if (argc > 1) {
+ msg_loops = 1;
+ msg_len = strlen(argv[1]);
+ memcpy(msg, argv[1], msg_len);
+ } else {
+ msg_loops = 64; // benchmark 64K
+ msg_len = sizeof(msg);
+ }
+
+ hook_call_deferred(&run_sha512_cmd_data, 0);
+ ccprintf("Will wait up to %d ms\n", (max_time + 500) / 1000);
+
+ events = task_wait_event_mask(TASK_EVENT_CUSTOM(1), max_time);
+ if (!(events & TASK_EVENT_CUSTOM(1))) {
+ ccprintf("Timed out, you might want to reboot...\n");
+ return EC_ERROR_TIMEOUT;
+ }
+
+ return EC_SUCCESS;
+}
+DECLARE_SAFE_CONSOLE_COMMAND(sha512_bench, cmd_sha512_bench, NULL, NULL);
+
+static void run_sha512_test(void)
+{
+ int i;
+
+ for (i = 0; i < 129; ++i) {
+ memset(msg, i, i);
+
+ SHA512_init(&sw);
+ HASH_update(&sw, msg, i);
+ sw_digest = HASH_final(&sw);
+
+ DCRYPTO_SHA512_init(&hw);
+ HASH_update(&hw, msg, i);
+ hw_digest = HASH_final(&hw);
+
+ if (memcmp(sw_digest, hw_digest, SHA512_DIGEST_SIZE) != 0) {
+ ccprintf("sha512 self-test fail at %d!\n", i);
+ cflush();
+ }
+ }
+
+ ccprintf("sha512 self-test PASS!\n");
+ task_set_event(TASK_ID_CONSOLE, TASK_EVENT_CUSTOM(1), 0);
+}
+DECLARE_DEFERRED(run_sha512_test);
+
+static int cmd_sha512_test(int argc, char *argv[])
+{
+ hook_call_deferred(&run_sha512_test_data, 0);
+ task_wait_event_mask(TASK_EVENT_CUSTOM(1), 1000000);
+ return EC_SUCCESS;
+}
+DECLARE_SAFE_CONSOLE_COMMAND(sha512_test, cmd_sha512_test, NULL, NULL);
+
+#endif /* CRYPTO_TEST_SETUP */
diff --git a/chip/g/dcrypto/drbg_rfc6979.c b/chip/g/dcrypto/drbg_rfc6979.c
new file mode 100644
index 0000000000..ce9953ce9a
--- /dev/null
+++ b/chip/g/dcrypto/drbg_rfc6979.c
@@ -0,0 +1,166 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "console.h"
+#include "cryptoc/util.h"
+#include "dcrypto.h"
+#include "internal.h"
+#include "trng.h"
+
+/* V = HMAC_K(V) */
+static void update_v(const uint32_t *k, uint32_t *v)
+{
+ LITE_HMAC_CTX ctx;
+
+ DCRYPTO_HMAC_SHA256_init(&ctx, k, SHA256_DIGEST_SIZE);
+ HASH_update(&ctx.hash, v, SHA256_DIGEST_SIZE);
+ memcpy(v, DCRYPTO_HMAC_final(&ctx), SHA256_DIGEST_SIZE);
+}
+
+/* K = HMAC_K(V || tag || x || h1) */
+static void update_k(uint32_t *k, const uint32_t *v, uint8_t tag,
+ const uint32_t *x, const uint32_t *h1)
+{
+ LITE_HMAC_CTX ctx;
+
+ DCRYPTO_HMAC_SHA256_init(&ctx, k, SHA256_DIGEST_SIZE);
+ HASH_update(&ctx.hash, v, SHA256_DIGEST_SIZE);
+ HASH_update(&ctx.hash, &tag, 1);
+ HASH_update(&ctx.hash, x, SHA256_DIGEST_SIZE);
+ HASH_update(&ctx.hash, h1, SHA256_DIGEST_SIZE);
+ memcpy(k, DCRYPTO_HMAC_final(&ctx), SHA256_DIGEST_SIZE);
+}
+
+/* K = HMAC_K(V || 0x00) */
+static void append_0(uint32_t *k, const uint32_t *v)
+{
+ LITE_HMAC_CTX ctx;
+ uint8_t zero = 0;
+
+ DCRYPTO_HMAC_SHA256_init(&ctx, k, SHA256_DIGEST_SIZE);
+ HASH_update(&ctx.hash, v, SHA256_DIGEST_SIZE);
+ HASH_update(&ctx.hash, &zero, 1);
+ memcpy(k, DCRYPTO_HMAC_final(&ctx), SHA256_DIGEST_SIZE);
+}
+
+/* Deterministic generation of k as per RFC 6979 */
+void drbg_rfc6979_init(struct drbg_ctx *ctx, const p256_int *key,
+ const p256_int *message)
+{
+ const uint32_t *x = key->a;
+ const uint32_t *h1 = message->a;
+
+ /* V = 0x01 0x01 0x01 ... 0x01 */
+ always_memset(ctx->v, 0x01, sizeof(ctx->v));
+ /* K = 0x00 0x00 0x00 ... 0x00 */
+ always_memset(ctx->k, 0x00, sizeof(ctx->k));
+ /* K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1)) */
+ update_k(ctx->k, ctx->v, 0x00, x, h1);
+ /* V = HMAC_K(V) */
+ update_v(ctx->k, ctx->v);
+ /* K = HMAC_K(V || 0x01 || int2octets(x) || bits2octets(h1)) */
+ update_k(ctx->k, ctx->v, 0x01, x, h1);
+ /* V = HMAC_K(V) */
+ update_v(ctx->k, ctx->v);
+}
+
+void drbg_rand_init(struct drbg_ctx *ctx)
+{
+ int i;
+ p256_int x, h1;
+
+ for (i = 0; i < P256_NDIGITS; ++i) {
+ x.a[i] = rand();
+ h1.a[i] = rand();
+ }
+
+ drbg_rfc6979_init(ctx, &x, &h1);
+}
+
+void drbg_generate(struct drbg_ctx *ctx, p256_int *k_out)
+{
+ int i;
+
+ /* V = HMAC_K(V) */
+ update_v(ctx->k, ctx->v);
+ /* get the current candidate K, then prepare for the next one */
+ for (i = 0; i < P256_NDIGITS; ++i)
+ k_out->a[i] = ctx->v[i];
+ /* K = HMAC_K(V || 0x00) */
+ append_0(ctx->k, ctx->v);
+ /* V = HMAC_K(V) */
+ update_v(ctx->k, ctx->v);
+}
+
+void drbg_exit(struct drbg_ctx *ctx)
+{
+ always_memset(ctx->k, 0x00, sizeof(ctx->k));
+ always_memset(ctx->v, 0x00, sizeof(ctx->v));
+}
+
+#ifdef CRYPTO_TEST_SETUP
+
+/*
+ * from the RFC 6979 A.2.5 example:
+ *
+ * curve: NIST P-256
+ *
+ * q = FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551
+ * (qlen = 256 bits)
+ *
+ * private key:
+ * x = C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721
+ *
+ * public key: U = xG
+ * Ux = 60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6
+ * Uy = 7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299
+ *
+ * Signature:
+ * With SHA-256, message = "sample":
+ * k = A6E3C57DD01ABE90086538398355DD4C3B17AA873382B0F24D6129493D8AAD60
+ * r = EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716
+ * s = F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8
+ */
+static int cmd_rfc6979(int argc, char **argv)
+{
+ static p256_int h1;
+ static p256_int k;
+ static const char message[] = "sample";
+ static struct drbg_ctx drbg;
+ static HASH_CTX ctx;
+ int result;
+ static const uint8_t priv_from_rfc[] = {
+ 0xC9, 0xAF, 0xA9, 0xD8, 0x45, 0xBA, 0x75, 0x16,
+ 0x6B, 0x5C, 0x21, 0x57, 0x67, 0xB1, 0xD6, 0x93,
+ 0x4E, 0x50, 0xC3, 0xDB, 0x36, 0xE8, 0x9B, 0x12,
+ 0x7B, 0x8A, 0x62, 0x2B, 0x12, 0x0F, 0x67, 0x21
+ };
+ static const uint8_t k_from_rfc[] = {
+ 0xA6, 0xE3, 0xC5, 0x7D, 0xD0, 0x1A, 0xBE, 0x90,
+ 0x08, 0x65, 0x38, 0x39, 0x83, 0x55, 0xDD, 0x4C,
+ 0x3B, 0x17, 0xAA, 0x87, 0x33, 0x82, 0xB0, 0xF2,
+ 0x4D, 0x61, 0x29, 0x49, 0x3D, 0x8A, 0xAD, 0x60
+ };
+ p256_int *x = (p256_int *)priv_from_rfc;
+ p256_int *reference_k = (p256_int *)k_from_rfc;
+
+ /* h1 = H(m) */
+ DCRYPTO_SHA256_init(&ctx, 1);
+ HASH_update(&ctx, message, sizeof(message) - 1);
+ memcpy(&h1, HASH_final(&ctx), SHA256_DIGEST_SIZE);
+
+ drbg_rfc6979_init(&drbg, x, &h1);
+ do {
+ drbg_generate(&drbg, &k);
+ ccprintf("K = %.32h\n", &k);
+ } while (p256_cmp(&SECP256r1_nMin2, &k) < 0);
+ drbg_exit(&drbg);
+ result = p256_cmp(&k, reference_k);
+ ccprintf("K generation: %s\n", result ? "FAIL" : "PASS");
+
+ return result ? EC_ERROR_INVAL : EC_SUCCESS;
+}
+DECLARE_SAFE_CONSOLE_COMMAND(rfc6979, cmd_rfc6979, NULL, NULL);
+#endif /* CRYPTO_TEST_SETUP */
diff --git a/chip/g/dcrypto/gcm.c b/chip/g/dcrypto/gcm.c
new file mode 100644
index 0000000000..18016de612
--- /dev/null
+++ b/chip/g/dcrypto/gcm.c
@@ -0,0 +1,345 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "dcrypto.h"
+#include "internal.h"
+#include "registers.h"
+
+#include "endian.h"
+
+#include "cryptoc/util.h"
+
+static void gcm_mul(uint32_t *counter)
+{
+ int i;
+ volatile uint32_t *p;
+
+ /* Set HASH to zero. */
+ p = GREG32_ADDR(KEYMGR, GCM_HASH_IN0);
+ for (i = 0; i < 4; i++)
+ *p++ = 0;
+
+ /* Initialize GMAC. */
+ p = GREG32_ADDR(KEYMGR, GCM_MAC0);
+ for (i = 0; i < 4; i++)
+ *p++ = counter[i];
+
+ /* Crank GMAC. */
+ GREG32(KEYMGR, GCM_DO_ACC) = 1;
+
+ /* Read GMAC. */
+ p = GREG32_ADDR(KEYMGR, GCM_MAC0);
+ for (i = 0; i < 4; i++)
+ counter[i] = *p++;
+
+ /* Reset GMAC. */
+ p = GREG32_ADDR(KEYMGR, GCM_MAC0);
+ for (i = 0; i < 4; ++i)
+ *p++ = 0;
+}
+
+static void gcm_init_iv(
+ const uint8_t *iv, uint32_t iv_len, uint32_t *counter)
+{
+
+ if (iv_len == 12) {
+ memcpy(counter, iv, 12);
+ counter[3] = 1 << 24;
+ } else {
+ size_t i;
+ uint32_t len = iv_len;
+ uint64_t len0 = len;
+ uint8_t *ctr = (uint8_t *) counter;
+
+ memset(ctr, 0, 16);
+ while (len >= 16) {
+ for (i = 0; i < 16; ++i)
+ ctr[i] ^= iv[i];
+
+ gcm_mul(counter);
+ iv += 16;
+ len -= 16;
+ }
+ if (len) {
+ for (i = 0; i < len; ++i)
+ ctr[i] ^= iv[i];
+
+ gcm_mul(counter);
+ }
+ len0 <<= 3;
+ ctr[8] ^= (uint8_t)(len0 >> 56);
+ ctr[9] ^= (uint8_t)(len0 >> 48);
+ ctr[10] ^= (uint8_t)(len0 >> 40);
+ ctr[11] ^= (uint8_t)(len0 >> 32);
+ ctr[12] ^= (uint8_t)(len0 >> 24);
+ ctr[13] ^= (uint8_t)(len0 >> 16);
+ ctr[14] ^= (uint8_t)(len0 >> 8);
+ ctr[15] ^= (uint8_t)(len0);
+
+ gcm_mul(counter);
+ }
+}
+
+void DCRYPTO_gcm_init(struct GCM_CTX *ctx, const uint8_t *key,
+ const uint8_t *iv, size_t iv_len)
+{
+ int i;
+ const uint32_t zero[4] = {0, 0, 0, 0};
+ uint32_t H[4];
+ uint32_t counter[4];
+
+ memset(ctx, 0, sizeof(struct GCM_CTX));
+
+ /* Initialize AES engine in CTR mode, and set the counter to 0. */
+ DCRYPTO_aes_init(key, 128, (const uint8_t *) zero,
+ CIPHER_MODE_CTR, ENCRYPT_MODE);
+ /* Set H to AES(ZERO). */
+ DCRYPTO_aes_block((const uint8_t *) zero, (uint8_t *) H);
+
+ /* Initialize the GMAC accumulator to ZERO. */
+ for (i = 0; i < 4; i++)
+ GR_KEYMGR_GCM_MAC(i) = zero[i];
+
+ /* Initialize H. */
+ for (i = 0; i < 4; i++)
+ GR_KEYMGR_GCM_H(i) = H[i];
+
+ /* Map the IV to a 128-bit counter. */
+ gcm_init_iv(iv, iv_len, counter);
+
+ /* Re-initialize the IV counter. */
+ for (i = 0; i < 4; i++)
+ GR_KEYMGR_AES_CTR(i) = counter[i];
+
+ /* Calculate Ej0: encrypt IV counter XOR ZERO. */
+ DCRYPTO_aes_block((const uint8_t *) zero, ctx->Ej0.c);
+}
+
+static void gcm_aad_block(const struct GCM_CTX *ctx, const uint32_t *block)
+{
+ int i;
+ const struct access_helper *p = (struct access_helper *) block;
+
+ if (ctx->aad_len == 0 && ctx->count <= 16) {
+ /* Update GMAC. */
+ for (i = 0; i < 4; i++)
+ GR_KEYMGR_GCM_MAC(i) = p[i].udata;
+ } else {
+ for (i = 0; i < 4; i++)
+ GR_KEYMGR_GCM_HASH_IN(i) = p[i].udata;
+
+ /* Crank GMAC. */
+ GREG32(KEYMGR, GCM_DO_ACC) = 1;
+ }
+}
+
+void DCRYPTO_gcm_aad(struct GCM_CTX *ctx, const uint8_t *aad_data, size_t len)
+{
+ uint32_t block[4];
+
+ while (len) {
+ size_t count;
+
+ memset(block, 0, sizeof(block));
+ count = MIN(16, len);
+ memcpy(block, aad_data, count);
+
+ gcm_aad_block(ctx, block);
+ ctx->aad_len += count;
+
+ len -= count;
+ aad_data += count;
+ }
+
+ always_memset(block, 0, sizeof(block));
+}
+
+int DCRYPTO_gcm_encrypt(struct GCM_CTX *ctx, uint8_t *out, size_t out_len,
+ const uint8_t *in, size_t in_len)
+{
+ uint8_t *outp = out;
+
+ if (out_len < (in_len & ~0x0F) + ((in_len & 0x0F) ? 16 : 0))
+ return -1;
+
+ /* Process a previous partial block, if any. */
+ if (ctx->remainder) {
+ size_t count = MIN(in_len, 16 - ctx->remainder);
+
+ memcpy(ctx->block.c + ctx->remainder, in, count);
+ ctx->remainder += count;
+ if (ctx->remainder < 16)
+ return 0;
+
+ DCRYPTO_aes_block(ctx->block.c, outp);
+ ctx->count += 16;
+ gcm_aad_block(ctx, (uint32_t *) outp);
+ ctx->remainder = 0;
+ in += count;
+ in_len -= count;
+ outp += 16;
+ }
+
+ while (in_len >= 16) {
+ DCRYPTO_aes_block(in, outp);
+ ctx->count += 16;
+
+ gcm_aad_block(ctx, (uint32_t *) outp);
+
+ in_len -= 16;
+ in += 16;
+ outp += 16;
+ }
+
+ if (in_len) {
+ memcpy(ctx->block.c, in, in_len);
+ ctx->remainder = in_len;
+ }
+
+ return outp - out;
+}
+
+int DCRYPTO_gcm_encrypt_final(struct GCM_CTX *ctx, uint8_t *out, size_t out_len)
+{
+ if (out_len < ctx->remainder)
+ return -1;
+
+ if (ctx->remainder) {
+ size_t remainder = ctx->remainder;
+ uint8_t out_block[16];
+
+ DCRYPTO_aes_block(ctx->block.c, out_block);
+ ctx->count += ctx->remainder;
+ memcpy(out, out_block, ctx->remainder);
+
+ memset(out_block + ctx->remainder, 0, 16 - ctx->remainder);
+ gcm_aad_block(ctx, (uint32_t *) out_block);
+ ctx->remainder = 0;
+ return remainder;
+ }
+
+ return 0;
+}
+
+int DCRYPTO_gcm_decrypt(struct GCM_CTX *ctx, uint8_t *out, size_t out_len,
+ const uint8_t *in, size_t in_len)
+{
+ uint8_t *outp = out;
+
+ if (out_len < (in_len & ~0x0F) + ((in_len & 0x0F) ? 16 : 0))
+ return -1;
+
+ if (ctx->remainder) {
+ size_t count = MIN(in_len, 16 - ctx->remainder);
+
+ memcpy(ctx->block.c + ctx->remainder, in, count);
+ ctx->remainder += count;
+
+ if (ctx->remainder < 16)
+ return 0;
+
+ DCRYPTO_aes_block(ctx->block.c, outp);
+ ctx->remainder = 0;
+ ctx->count += 16;
+ gcm_aad_block(ctx, ctx->block.d);
+ in += count;
+ in_len -= count;
+ outp += count;
+ }
+
+ while (in_len >= 16) {
+ DCRYPTO_aes_block(in, outp);
+ ctx->count += 16;
+ gcm_aad_block(ctx, (uint32_t *) in);
+ in += 16;
+ in_len -= 16;
+ outp += 16;
+ }
+
+ if (in_len) {
+ memcpy(ctx->block.c, in, in_len);
+ ctx->remainder = in_len;
+ }
+
+ return outp - out;
+}
+
+int DCRYPTO_gcm_decrypt_final(struct GCM_CTX *ctx,
+ uint8_t *out, size_t out_len)
+{
+ if (out_len < ctx->remainder)
+ return -1;
+
+ if (ctx->remainder) {
+ size_t remainder = ctx->remainder;
+ uint8_t out_block[16];
+
+ DCRYPTO_aes_block(ctx->block.c, out_block);
+ ctx->count += ctx->remainder;
+ memcpy(out, out_block, ctx->remainder);
+
+ memset(ctx->block.c + ctx->remainder, 0, 16 - ctx->remainder);
+ gcm_aad_block(ctx, ctx->block.d);
+ ctx->remainder = 0;
+ return remainder;
+ }
+
+ return 0;
+}
+
+static void dcrypto_gcm_len_vector(
+ const struct GCM_CTX *ctx, void *len_vector) {
+ uint64_t aad_be;
+ uint64_t count_be;
+
+ /* Serialize counters to bit-count (big-endian). */
+ aad_be = ctx->aad_len * 8;
+ aad_be = htobe64(aad_be);
+ count_be = ctx->count * 8;
+ count_be = htobe64(count_be);
+
+ memcpy(len_vector, &aad_be, 8);
+ memcpy(((uint8_t *)len_vector) + 8, &count_be, 8);
+}
+
+static void dcrypto_gcm_tag(const struct GCM_CTX *ctx,
+ const uint32_t *len_vector, uint32_t *tag) {
+ int i;
+
+ for (i = 0; i < 4; i++)
+ GR_KEYMGR_GCM_HASH_IN(i) = len_vector[i];
+
+ /* Crank GMAC. */
+ GREG32(KEYMGR, GCM_DO_ACC) = 1;
+
+ for (i = 0; i < 4; i++)
+ GR_KEYMGR_GCM_HASH_IN(i) = ctx->Ej0.d[i];
+
+ /* Crank GMAC. */
+ GREG32(KEYMGR, GCM_DO_ACC) = 1;
+
+ /* Read tag. */
+ for (i = 0; i < 4; i++)
+ tag[i] = GR_KEYMGR_GCM_MAC(i);
+}
+
+int DCRYPTO_gcm_tag(struct GCM_CTX *ctx, uint8_t *tag, size_t tag_len)
+{
+ uint32_t len_vector[4];
+ uint32_t local_tag[4];
+ size_t count = MIN(tag_len, sizeof(local_tag));
+
+ dcrypto_gcm_len_vector(ctx, len_vector);
+ dcrypto_gcm_tag(ctx, len_vector, local_tag);
+
+ memcpy(tag, local_tag, count);
+ return count;
+}
+
+void DCRYPTO_gcm_finish(struct GCM_CTX *ctx)
+{
+ always_memset(ctx, 0, sizeof(struct GCM_CTX));
+ GREG32(KEYMGR, AES_WIPE_SECRETS) = 1;
+}
diff --git a/chip/g/dcrypto/hkdf.c b/chip/g/dcrypto/hkdf.c
index 9a647361ce..3afdc6b2eb 100644
--- a/chip/g/dcrypto/hkdf.c
+++ b/chip/g/dcrypto/hkdf.c
@@ -8,6 +8,7 @@
#include "internal.h"
#include "cryptoc/sha256.h"
+#include "cryptoc/util.h"
static int hkdf_extract(uint8_t *PRK, const uint8_t *salt, size_t salt_len,
const uint8_t *IKM, size_t IKM_len)
@@ -77,6 +78,6 @@ int DCRYPTO_hkdf(uint8_t *OKM, size_t OKM_len,
return 0;
result = hkdf_expand(OKM, OKM_len, PRK, info, info_len);
- memset(PRK, 0, sizeof(PRK));
+ always_memset(PRK, 0, sizeof(PRK));
return result;
}
diff --git a/chip/g/dcrypto/hmac.c b/chip/g/dcrypto/hmac.c
index 1c34ddfd96..d6f2d4e775 100644
--- a/chip/g/dcrypto/hmac.c
+++ b/chip/g/dcrypto/hmac.c
@@ -9,6 +9,7 @@
#include <stdint.h>
#include "cryptoc/sha256.h"
+#include "cryptoc/util.h"
/* TODO(ngm): add support for hardware hmac. */
static void HMAC_init(LITE_HMAC_CTX *ctx, const void *key, unsigned int len)
@@ -40,7 +41,6 @@ static void HMAC_init(LITE_HMAC_CTX *ctx, const void *key, unsigned int len)
void DCRYPTO_HMAC_SHA256_init(LITE_HMAC_CTX *ctx, const void *key,
unsigned int len)
{
- DCRYPTO_SHA256_init(&ctx->hash, 0);
HMAC_init(ctx, key, len);
}
@@ -54,6 +54,6 @@ const uint8_t *DCRYPTO_HMAC_final(LITE_HMAC_CTX *ctx)
DCRYPTO_SHA256_init(&ctx->hash, 0);
HASH_update(&ctx->hash, ctx->opad, sizeof(ctx->opad));
HASH_update(&ctx->hash, digest, HASH_size(&ctx->hash));
- memset(&ctx->opad[0], 0, sizeof(ctx->opad)); /* wipe key */
+ always_memset(&ctx->opad[0], 0, sizeof(ctx->opad)); /* wipe key */
return HASH_final(&ctx->hash);
}
diff --git a/chip/g/dcrypto/internal.h b/chip/g/dcrypto/internal.h
index 7be2140ac4..b97cde9b03 100644
--- a/chip/g/dcrypto/internal.h
+++ b/chip/g/dcrypto/internal.h
@@ -15,6 +15,8 @@
#include "cryptoc/p256.h"
#include "cryptoc/sha.h"
#include "cryptoc/sha256.h"
+#include "cryptoc/sha384.h"
+#include "cryptoc/sha512.h"
/*
* SHA.
@@ -27,7 +29,11 @@
#define SHA_DIGEST_WORDS (SHA_DIGEST_SIZE / sizeof(uint32_t))
#define SHA256_DIGEST_WORDS (SHA256_DIGEST_SIZE / sizeof(uint32_t))
+#ifdef SHA512_SUPPORT
+#define SHA_DIGEST_MAX_BYTES SHA512_DIGEST_SIZE
+#else
#define SHA_DIGEST_MAX_BYTES SHA256_DIGEST_SIZE
+#endif
enum sha_mode {
SHA1_MODE = 0,
@@ -72,31 +78,100 @@ void bn_init(struct LITE_BIGNUM *bn, void *buf, size_t len);
#define bn_bits(b) ((b)->dmax * LITE_BN_BITS2)
int bn_eq(const struct LITE_BIGNUM *a, const struct LITE_BIGNUM *b);
int bn_check_topbit(const struct LITE_BIGNUM *N);
-void bn_mont_modexp(struct LITE_BIGNUM *output, const struct LITE_BIGNUM *input,
- const struct LITE_BIGNUM *exp, const struct LITE_BIGNUM *N);
-void bn_mont_modexp_asm(struct LITE_BIGNUM *output,
+int bn_modexp(struct LITE_BIGNUM *output,
const struct LITE_BIGNUM *input,
const struct LITE_BIGNUM *exp,
const struct LITE_BIGNUM *N);
-uint32_t bn_add(struct LITE_BIGNUM *c, const struct LITE_BIGNUM *a);
-uint32_t bn_sub(struct LITE_BIGNUM *c, const struct LITE_BIGNUM *a);
-int bn_modinv_vartime(struct LITE_BIGNUM *r, const struct LITE_BIGNUM *e,
- const struct LITE_BIGNUM *MOD);
+int bn_modexp_word(struct LITE_BIGNUM *output,
+ const struct LITE_BIGNUM *input,
+ uint32_t pubexp,
+ const struct LITE_BIGNUM *N);
+int bn_modexp_blinded(struct LITE_BIGNUM *output,
+ const struct LITE_BIGNUM *input,
+ const struct LITE_BIGNUM *exp,
+ const struct LITE_BIGNUM *N,
+ uint32_t pubexp);
+uint32_t bn_add(struct LITE_BIGNUM *c,
+ const struct LITE_BIGNUM *a);
+uint32_t bn_sub(struct LITE_BIGNUM *c,
+ const struct LITE_BIGNUM *a);
+int bn_modinv_vartime(struct LITE_BIGNUM *r,
+ const struct LITE_BIGNUM *e,
+ const struct LITE_BIGNUM *MOD);
int bn_is_bit_set(const struct LITE_BIGNUM *a, int n);
/*
- * Runtime.
+ * Accelerated bn.
+ */
+int dcrypto_modexp(struct LITE_BIGNUM *output,
+ const struct LITE_BIGNUM *input,
+ const struct LITE_BIGNUM *exp,
+ const struct LITE_BIGNUM *N);
+int dcrypto_modexp_word(struct LITE_BIGNUM *output,
+ const struct LITE_BIGNUM *input,
+ uint32_t pubexp,
+ const struct LITE_BIGNUM *N);
+int dcrypto_modexp_blinded(struct LITE_BIGNUM *output,
+ const struct LITE_BIGNUM *input,
+ const struct LITE_BIGNUM *exp,
+ const struct LITE_BIGNUM *N,
+ uint32_t pubexp);
+
+/*
+ * RFC6979 based DRBG for ECDSA signature.
+ */
+struct drbg_ctx {
+ uint32_t k[SHA256_DIGEST_WORDS];
+ uint32_t v[SHA256_DIGEST_WORDS];
+};
+void drbg_rfc6979_init(struct drbg_ctx *ctx, const p256_int *key,
+ const p256_int *message);
+void drbg_rand_init(struct drbg_ctx *ctx);
+void drbg_generate(struct drbg_ctx *ctx, p256_int *k_out);
+void drbg_exit(struct drbg_ctx *ctx);
+
+/*
+ * Accelerated p256.
+ */
+int dcrypto_p256_ecdsa_sign(struct drbg_ctx *drbg, const p256_int *key,
+ const p256_int *message, p256_int *r, p256_int *s)
+ __attribute__((warn_unused_result));
+int dcrypto_p256_base_point_mul(const p256_int *k, p256_int *x, p256_int *y)
+ __attribute__((warn_unused_result));
+int dcrypto_p256_point_mul(const p256_int *k,
+ const p256_int *in_x, const p256_int *in_y,
+ p256_int *x, p256_int *y)
+ __attribute__((warn_unused_result));
+int dcrypto_p256_ecdsa_verify(const p256_int *key_x, const p256_int *key_y,
+ const p256_int *message, const p256_int *r,
+ const p256_int *s)
+ __attribute__((warn_unused_result));
+int dcrypto_p256_is_valid_point(const p256_int *x, const p256_int *y)
+ __attribute__((warn_unused_result));
+
+/*
+ * Accelerator runtime.
+ *
+ * Note dcrypto_init_and_lock grabs a mutex and dcrypto_unlock releases it.
+ * Do not use dcrypto_call, dcrypto_imem_load or dcrypto_dmem_load w/o holding
+ * the mutex.
*/
-void dcrypto_init(void);
-uint32_t dcrypto_call(uint32_t adr);
+void dcrypto_init_and_lock(void);
+void dcrypto_unlock(void);
+uint32_t dcrypto_call(uint32_t adr) __attribute__((warn_unused_result));
void dcrypto_imem_load(size_t offset, const uint32_t *opcodes,
size_t n_opcodes);
void dcrypto_dmem_load(size_t offset, const void *words, size_t n_words);
/*
- * Utility functions.
+ * Key ladder.
*/
-/* TODO(ngm): memset that doesn't get optimized out. */
-#define dcrypto_memset(p, b, len) memset((p), (b), (len))
+enum dcrypto_appid; /* Forward declaration. */
+
+int dcrypto_ladder_compute_usr(enum dcrypto_appid id,
+ const uint32_t usr_salt[8]);
+int dcrypto_ladder_derive(enum dcrypto_appid appid, const uint32_t salt[8],
+ const uint32_t input[8], uint32_t output[8]);
+
#endif /* ! __EC_CHIP_G_DCRYPTO_INTERNAL_H */
diff --git a/chip/g/dcrypto/key_ladder.c b/chip/g/dcrypto/key_ladder.c
new file mode 100644
index 0000000000..913a667417
--- /dev/null
+++ b/chip/g/dcrypto/key_ladder.c
@@ -0,0 +1,289 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "dcrypto.h"
+#include "internal.h"
+#include "endian.h"
+#include "registers.h"
+#include "trng.h"
+
+static void ladder_init(void)
+{
+ /* Do not reset keyladder engine here, as before.
+ *
+ * Should not be needed and if it is, it is indicative
+ * of sync error between this and sha engine usage.
+ * Reset will make this flow work, but will have broken
+ * the other pending sha flow.
+ * Hence leave as is and observe the error.
+ */
+
+ /* Enable random stalls for key-ladder usage. Note that
+ * the stall rate used for key-ladder operations is
+ * 25% (vs. 12% for generic SHA operations). This distinction
+ * is made so as to increase the difficulty in characterizng
+ * the key-ladder engine via random inputs provided over the
+ * generic SHA interface.
+ */
+ /* Turn off random nops (which are enabled by default). */
+ GWRITE_FIELD(KEYMGR, SHA_RAND_STALL_CTL, STALL_EN, 0);
+ /* Configure random nop percentage at 25%. */
+ GWRITE_FIELD(KEYMGR, SHA_RAND_STALL_CTL, FREQ, 1);
+ /* Now turn on random nops. */
+ GWRITE_FIELD(KEYMGR, SHA_RAND_STALL_CTL, STALL_EN, 1);
+}
+
+static int ladder_step(uint32_t cert, const uint32_t input[8])
+{
+ GREG32(KEYMGR, SHA_ITOP) = 0; /* clear status */
+
+ GREG32(KEYMGR, SHA_USE_CERT_INDEX) =
+ (cert << GC_KEYMGR_SHA_USE_CERT_INDEX_LSB) |
+ GC_KEYMGR_SHA_USE_CERT_ENABLE_MASK;
+
+ GREG32(KEYMGR, SHA_CFG_EN) =
+ GC_KEYMGR_SHA_CFG_EN_INT_EN_DONE_MASK;
+ GREG32(KEYMGR, SHA_TRIG) =
+ GC_KEYMGR_SHA_TRIG_TRIG_GO_MASK;
+
+ if (input) {
+ GREG32(KEYMGR, SHA_INPUT_FIFO) = input[0];
+ GREG32(KEYMGR, SHA_INPUT_FIFO) = input[1];
+ GREG32(KEYMGR, SHA_INPUT_FIFO) = input[2];
+ GREG32(KEYMGR, SHA_INPUT_FIFO) = input[3];
+ GREG32(KEYMGR, SHA_INPUT_FIFO) = input[4];
+ GREG32(KEYMGR, SHA_INPUT_FIFO) = input[5];
+ GREG32(KEYMGR, SHA_INPUT_FIFO) = input[6];
+ GREG32(KEYMGR, SHA_INPUT_FIFO) = input[7];
+
+ GREG32(KEYMGR, SHA_TRIG) = GC_KEYMGR_SHA_TRIG_TRIG_STOP_MASK;
+ }
+
+ while (!GREG32(KEYMGR, SHA_ITOP))
+ ;
+
+ GREG32(KEYMGR, SHA_ITOP) = 0; /* clear status */
+
+ return !!GREG32(KEYMGR, HKEY_ERR_FLAGS);
+}
+
+static int compute_certs(const uint32_t *certs, size_t num_certs)
+{
+ int i;
+
+ for (i = 0; i < num_certs; i++) {
+ if (ladder_step(certs[i], NULL))
+ return 0;
+ }
+
+ return 1;
+}
+
+#define KEYMGR_CERT_0 0
+#define KEYMGR_CERT_3 3
+#define KEYMGR_CERT_4 4
+#define KEYMGR_CERT_5 5
+#define KEYMGR_CERT_7 7
+#define KEYMGR_CERT_15 15
+#define KEYMGR_CERT_20 20
+#define KEYMGR_CERT_25 25
+#define KEYMGR_CERT_26 26
+#define KEYMGR_CERT_27 27
+#define KEYMGR_CERT_28 28
+#define KEYMGR_CERT_34 34
+#define KEYMGR_CERT_35 35
+#define KEYMGR_CERT_38 38
+
+static const uint32_t FRK2_CERTS_PREFIX[] = {
+ KEYMGR_CERT_0,
+ KEYMGR_CERT_3,
+ KEYMGR_CERT_4,
+ KEYMGR_CERT_5,
+ KEYMGR_CERT_7,
+ KEYMGR_CERT_15,
+ KEYMGR_CERT_20,
+};
+
+static const uint32_t FRK2_CERTS_POSTFIX[] = {
+ KEYMGR_CERT_26,
+};
+
+#define MAX_MAJOR_FW_VERSION 254
+
+int DCRYPTO_ladder_compute_frk2(size_t fw_version, uint8_t *frk2)
+{
+ int result = 0;
+
+ if (fw_version > MAX_MAJOR_FW_VERSION)
+ return 0;
+
+ if (!dcrypto_grab_sha_hw())
+ return 0;
+
+ do {
+ int i;
+
+ ladder_init();
+
+ if (!compute_certs(FRK2_CERTS_PREFIX,
+ ARRAY_SIZE(FRK2_CERTS_PREFIX)))
+ break;
+
+ for (i = 0; i < MAX_MAJOR_FW_VERSION - fw_version; i++) {
+ if (ladder_step(KEYMGR_CERT_25, NULL))
+ break;
+ }
+
+ if (!compute_certs(FRK2_CERTS_POSTFIX,
+ ARRAY_SIZE(FRK2_CERTS_POSTFIX)))
+ break;
+
+ memcpy(frk2, (void *) GREG32_ADDR(KEYMGR, HKEY_FRR0),
+ AES256_BLOCK_CIPHER_KEY_SIZE);
+
+ result = 1;
+ } while (0);
+
+ dcrypto_release_sha_hw();
+ return result;
+}
+
+/* ISR salt (SHA256("ISR_SALT")) to use for USR generation. */
+static const uint32_t ISR_SALT[8] = {
+ 0x6ba1b495, 0x4b7ca214, 0xfe07e922, 0x09735185,
+ 0xfcca43ca, 0xc6d4dfd9, 0x5fc2fcca, 0xaa45400b
+};
+
+/* Map of populated USR registers. */
+static int usr_ready[8] = {};
+
+int dcrypto_ladder_compute_usr(enum dcrypto_appid id,
+ const uint32_t usr_salt[8])
+{
+ int result = 0;
+
+ /* Check for USR readiness. */
+ if (usr_ready[id])
+ return 1;
+
+ if (!dcrypto_grab_sha_hw())
+ return 0;
+
+ do {
+ int i;
+
+ /* The previous check performed without lock acquisition. */
+ if (usr_ready[id]) {
+ result = 1;
+ break;
+ }
+
+ ladder_init();
+
+ if (!compute_certs(FRK2_CERTS_PREFIX,
+ ARRAY_SIZE(FRK2_CERTS_PREFIX)))
+ break;
+
+ /* USR generation requires running the key-ladder till
+ * the end (version 0), plus one additional iteration.
+ */
+ for (i = 0; i < MAX_MAJOR_FW_VERSION - 0 + 1; i++) {
+ if (ladder_step(KEYMGR_CERT_25, NULL))
+ break;
+ }
+ if (i != MAX_MAJOR_FW_VERSION - 0 + 1)
+ break;
+
+ if (ladder_step(KEYMGR_CERT_34, ISR_SALT))
+ break;
+
+ /* Output goes to USR[appid] (the multiply by 2 is an
+ * artifact of slot addressing).
+ */
+ GWRITE_FIELD(KEYMGR, SHA_CERT_OVERRIDE, DIGEST_PTR, 2 * id);
+ if (ladder_step(KEYMGR_CERT_35, usr_salt))
+ break;
+
+ /* Check for key-ladder errors. */
+ if (GREG32(KEYMGR, HKEY_ERR_FLAGS))
+ break;
+
+ /* Key deposited in USR[id], and ready to use. */
+ usr_ready[id] = 1;
+
+ result = 1;
+ } while (0);
+
+ dcrypto_release_sha_hw();
+ return result;
+}
+
+static void ladder_out(uint32_t output[8])
+{
+ output[0] = GREG32(KEYMGR, SHA_STS_H0);
+ output[1] = GREG32(KEYMGR, SHA_STS_H1);
+ output[2] = GREG32(KEYMGR, SHA_STS_H2);
+ output[3] = GREG32(KEYMGR, SHA_STS_H3);
+ output[4] = GREG32(KEYMGR, SHA_STS_H4);
+ output[5] = GREG32(KEYMGR, SHA_STS_H5);
+ output[6] = GREG32(KEYMGR, SHA_STS_H6);
+ output[7] = GREG32(KEYMGR, SHA_STS_H7);
+}
+
+/*
+ * Stir TRNG entropy into RSR and pull some out.
+ */
+int DCRYPTO_ladder_random(void *output)
+{
+ int error = 1;
+ uint32_t tmp[8];
+ int i;
+
+ if (!dcrypto_grab_sha_hw())
+ goto fail;
+
+ rand_bytes(tmp, sizeof(tmp));
+ error = ladder_step(KEYMGR_CERT_28, tmp);
+ if (error)
+ goto fail;
+
+ if (!compute_certs(FRK2_CERTS_PREFIX, ARRAY_SIZE(FRK2_CERTS_PREFIX)))
+ goto fail;
+ /* USR generation requires running the key-ladder till
+ * the end (version 0), plus one additional iteration.
+ */
+ for (i = 0; i < MAX_MAJOR_FW_VERSION - 0 + 1; i++)
+ if (ladder_step(KEYMGR_CERT_25, NULL))
+ goto fail;
+ if (i != MAX_MAJOR_FW_VERSION - 0 + 1)
+ goto fail;
+ if (ladder_step(KEYMGR_CERT_34, ISR_SALT))
+ goto fail;
+
+ rand_bytes(tmp, sizeof(tmp));
+ error = ladder_step(KEYMGR_CERT_27, tmp);
+ if (!error)
+ ladder_out(output);
+
+fail:
+ dcrypto_release_sha_hw();
+ return !error;
+}
+
+int dcrypto_ladder_derive(enum dcrypto_appid appid, const uint32_t salt[8],
+ const uint32_t input[8], uint32_t output[8])
+{
+ int error;
+
+ if (!dcrypto_grab_sha_hw())
+ return 0;
+
+ GWRITE_FIELD(KEYMGR, SHA_CERT_OVERRIDE, KEY_PTR, 2 * appid);
+ error = ladder_step(KEYMGR_CERT_38, input); /* HMAC */
+ if (!error)
+ ladder_out(output);
+
+ dcrypto_release_sha_hw();
+ return !error;
+}
diff --git a/chip/g/dcrypto/p256.c b/chip/g/dcrypto/p256.c
index cb963bb669..665144e31b 100644
--- a/chip/g/dcrypto/p256.c
+++ b/chip/g/dcrypto/p256.c
@@ -6,6 +6,7 @@
#include "dcrypto.h"
#include "cryptoc/p256.h"
+#include "cryptoc/util.h"
static const p256_int p256_one = P256_ONE;
@@ -22,7 +23,8 @@ int DCRYPTO_p256_key_from_bytes(p256_int *x, p256_int *y, p256_int *d,
if (p256_cmp(&SECP256r1_nMin2, &key) < 0)
return 0;
p256_add(&key, &p256_one, d);
- p256_base_point_mul(d, x, y);
- dcrypto_memset(&key, 0, sizeof(key));
- return 1;
+ always_memset(&key, 0, sizeof(key));
+ if (x == NULL || y == NULL)
+ return 1;
+ return dcrypto_p256_base_point_mul(d, x, y);
}
diff --git a/chip/g/dcrypto/p256_ec.c b/chip/g/dcrypto/p256_ec.c
index b298493037..cb33a15774 100644
--- a/chip/g/dcrypto/p256_ec.c
+++ b/chip/g/dcrypto/p256_ec.c
@@ -20,8 +20,7 @@ int DCRYPTO_p256_base_point_mul(p256_int *out_x, p256_int *out_y,
return 0;
}
- p256_base_point_mul(n, out_x, out_y);
- return 1;
+ return dcrypto_p256_base_point_mul(n, out_x, out_y);
}
/* DCRYPTO_p256_point_mul sets {out_x,out_y} = n*{in_x,in_y}, where n is <
@@ -35,6 +34,6 @@ int DCRYPTO_p256_point_mul(p256_int *out_x, p256_int *out_y,
p256_clear(out_y);
return 0;
}
- p256_point_mul(n, in_x, in_y, out_x, out_y);
- return 1;
+
+ return dcrypto_p256_point_mul(n, in_x, in_y, out_x, out_y);
}
diff --git a/chip/g/dcrypto/p256_ecies.c b/chip/g/dcrypto/p256_ecies.c
index 8272014495..30a410d828 100644
--- a/chip/g/dcrypto/p256_ecies.c
+++ b/chip/g/dcrypto/p256_ecies.c
@@ -58,7 +58,7 @@ size_t DCRYPTO_ecies_encrypt(
&eph_d, pub_x, pub_y))
return 0;
/* Check for computational errors. */
- if (!p256_is_valid_point(&secret_x, &secret_y))
+ if (!dcrypto_p256_is_valid_point(&secret_x, &secret_y))
return 0;
/* Convert secret to big-endian. */
reverse(&secret_x, sizeof(secret_x));
@@ -140,14 +140,14 @@ size_t DCRYPTO_ecies_decrypt(
inp += P256_NBYTES;
/* Verify that the public point is on the curve. */
- if (!p256_is_valid_point(&eph_x, &eph_y))
+ if (!dcrypto_p256_is_valid_point(&eph_x, &eph_y))
return 0;
/* Compute the DH point. */
if (!DCRYPTO_p256_point_mul(&secret_x, &secret_y,
d, &eph_x, &eph_y))
return 0;
/* Check for computational errors. */
- if (!p256_is_valid_point(&secret_x, &secret_y))
+ if (!dcrypto_p256_is_valid_point(&secret_x, &secret_y))
return 0;
/* Convert secret to big-endian. */
reverse(&secret_x, sizeof(secret_x));
@@ -161,9 +161,8 @@ size_t DCRYPTO_ecies_decrypt(
hmac_key = &key[AES_KEY_BYTES];
DCRYPTO_HMAC_SHA256_init(&ctx, hmac_key, HMAC_KEY_BYTES);
HASH_update(&ctx.hash, inp, in_len);
- /* TODO(ngm): replace with constant time verify. */
- if (memcmp(inp + in_len, DCRYPTO_HMAC_final(&ctx),
- SHA256_DIGEST_SIZE) != 0)
+ if (!DCRYPTO_equals(inp + in_len, DCRYPTO_HMAC_final(&ctx),
+ SHA256_DIGEST_SIZE))
return 0;
memmove(outp, inp, auth_data_len);
diff --git a/chip/g/dcrypto/rsa.c b/chip/g/dcrypto/rsa.c
index 359565d118..8a4115398d 100644
--- a/chip/g/dcrypto/rsa.c
+++ b/chip/g/dcrypto/rsa.c
@@ -13,6 +13,27 @@
#include "cryptoc/sha.h"
#include "cryptoc/sha256.h"
+#include "cryptoc/sha384.h"
+#include "cryptoc/sha512.h"
+#include "cryptoc/util.h"
+
+/* Extend the MSB throughout the word. */
+static uint32_t msb_extend(uint32_t a)
+{
+ return 0u - (a >> 31);
+}
+
+/* Return 0xFF..FF if a is zero, and zero otherwise. */
+static uint32_t is_zero(uint32_t a)
+{
+ return msb_extend(~a & (a - 1));
+}
+
+/* Select a or b based on mask. Mask expected to be 0xFF..FF or 0. */
+static uint32_t select(uint32_t mask, uint32_t a, uint32_t b)
+{
+ return (mask & a) | (~mask & b);
+}
static void MGF1_xor(uint8_t *dst, uint32_t dst_len,
const uint8_t *seed, uint32_t seed_len,
@@ -80,7 +101,7 @@ static int oaep_pad(uint8_t *output, uint32_t output_len,
if (msg_len > output_len - 2 - 2 * hash_size)
return 0; /* Input message too large for key size. */
- dcrypto_memset(output, 0, output_len);
+ always_memset(output, 0, output_len);
for (i = 0; i < hash_size;) {
uint32_t r = rand();
@@ -107,7 +128,6 @@ static int oaep_pad(uint8_t *output, uint32_t output_len,
}
/* decrypt */
-/* TODO(ngm): constant time. */
static int check_oaep_pad(uint8_t *out, uint32_t *out_len,
uint8_t *padded, uint32_t padded_len,
enum hashing_mode hashing, const char *label)
@@ -119,7 +139,8 @@ static int check_oaep_pad(uint8_t *out, uint32_t *out_len,
uint8_t *PS = phash + hash_size;
const uint32_t max_msg_len = padded_len - 2 - 2 * hash_size;
struct HASH_CTX ctx;
- int one_index = -1;
+ size_t one_index = 0;
+ uint32_t looking_for_one_byte = ~0;
int bad;
int i;
@@ -137,21 +158,26 @@ static int check_oaep_pad(uint8_t *out, uint32_t *out_len,
DCRYPTO_SHA256_init(&ctx, 0);
HASH_update(&ctx, label, label ? strlen(label) + 1 : 0);
- bad = memcmp(phash, HASH_final(&ctx), hash_size);
+ bad = !DCRYPTO_equals(phash, HASH_final(&ctx), hash_size);
bad |= padded[0];
for (i = PS - padded; i < padded_len; i++) {
- if (padded[i] == 1) {
- one_index = i;
- break;
- } else if (padded[i] != 0) {
- bad = 1;
- break;
- }
+ uint32_t equals0 = is_zero(padded[i]);
+ uint32_t equals1 = is_zero(padded[i] ^ 1);
+
+ one_index = select(looking_for_one_byte & equals1,
+ i, one_index);
+ looking_for_one_byte = select(equals1, 0, looking_for_one_byte);
+
+ /* Bad padding if padded[i] is neither 1 nor 0. */
+ bad |= looking_for_one_byte & ~equals0;
}
- if (one_index < 0 || bad)
+ bad |= looking_for_one_byte;
+
+ if (bad)
return 0;
+
one_index++;
if (*out_len < padded_len - one_index)
return 0;
@@ -196,30 +222,38 @@ static int pkcs1_type2_pad(uint8_t *padded, uint32_t padded_len,
}
/* decrypt */
-/* TODO(ngm): constant time */
static int check_pkcs1_type2_pad(uint8_t *out, uint32_t *out_len,
const uint8_t *padded, uint32_t padded_len)
{
int i;
+ int valid;
+ uint32_t zero_index = 0;
+ uint32_t looking_for_index = ~0;
if (padded_len < RSA_PKCS1_PADDING_SIZE)
return 0;
- if (padded[0] != 0 || padded[1] != 2)
- return 0;
+
+ valid = (padded[0] == 0);
+ valid &= (padded[1] == 2);
+
for (i = 2; i < padded_len; i++) {
- if (padded[i] == 0)
- break;
+ uint32_t found = is_zero(padded[i]);
+
+ zero_index = select(looking_for_index & found, i, zero_index);
+ looking_for_index = select(found, 0, looking_for_index);
}
- if (i == padded_len)
- return 0;
- i++;
- if (i < RSA_PKCS1_PADDING_SIZE)
+ zero_index++;
+
+ valid &= ~looking_for_index;
+ valid &= (zero_index >= RSA_PKCS1_PADDING_SIZE);
+ if (!valid)
return 0;
- if (*out_len < padded_len - i)
+
+ if (*out_len < padded_len - zero_index)
return 0;
- memcpy(out, &padded[i], padded_len - i);
- *out_len = padded_len - i;
+ memcpy(out, &padded[zero_index], padded_len - zero_index);
+ *out_len = padded_len - zero_index;
return 1;
}
@@ -232,23 +266,68 @@ static const uint8_t SHA256_DER[] = {
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
0x00, 0x04, 0x20
};
+static const uint8_t SHA384_DER[] = {
+ 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05,
+ 0x00, 0x04, 0x30
+};
+static const uint8_t SHA512_DER[] = {
+ 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
+ 0x00, 0x04, 0x40
+};
+
+static int pkcs1_get_der(enum hashing_mode hashing, const uint8_t **der,
+ uint32_t *der_size, uint32_t *hash_size)
+{
+ switch (hashing) {
+ case HASH_SHA1:
+ *der = &SHA1_DER[0];
+ *der_size = sizeof(SHA1_DER);
+ *hash_size = SHA_DIGEST_SIZE;
+ break;
+ case HASH_SHA256:
+ *der = &SHA256_DER[0];
+ *der_size = sizeof(SHA256_DER);
+ *hash_size = SHA256_DIGEST_SIZE;
+ break;
+ case HASH_SHA384:
+ *der = &SHA384_DER[0];
+ *der_size = sizeof(SHA384_DER);
+ *hash_size = SHA384_DIGEST_SIZE;
+ break;
+ case HASH_SHA512:
+ *der = &SHA512_DER[0];
+ *der_size = sizeof(SHA512_DER);
+ *hash_size = SHA512_DIGEST_SIZE;
+ break;
+ case HASH_NULL:
+ *der = NULL;
+ *der_size = 0;
+ *hash_size = 0; /* any size allowed */
+ break;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
/* sign */
static int pkcs1_type1_pad(uint8_t *padded, uint32_t padded_len,
const uint8_t *in, uint32_t in_len,
enum hashing_mode hashing)
{
- const uint8_t *der = (hashing == HASH_SHA1) ? &SHA1_DER[0]
- : &SHA256_DER[0];
- const uint32_t der_size = (hashing == HASH_SHA1) ? sizeof(SHA1_DER)
- : sizeof(SHA256_DER);
- const uint32_t hash_size = (hashing == HASH_SHA1) ? SHA_DIGEST_SIZE
- : SHA256_DIGEST_SIZE;
+ const uint8_t *der;
+ uint32_t der_size;
+ uint32_t hash_size;
uint32_t ps_len;
+ if (!pkcs1_get_der(hashing, &der, &der_size, &hash_size))
+ return 0;
if (padded_len < RSA_PKCS1_PADDING_SIZE + der_size)
return 0;
- if (in_len != hash_size)
+ if (!in_len || (hash_size && in_len != hash_size))
return 0;
if (in_len > padded_len - RSA_PKCS1_PADDING_SIZE - der_size)
return 0;
@@ -256,7 +335,7 @@ static int pkcs1_type1_pad(uint8_t *padded, uint32_t padded_len,
*(padded++) = 0;
*(padded++) = 1;
- dcrypto_memset(padded, 0xFF, ps_len);
+ always_memset(padded, 0xFF, ps_len);
padded += ps_len;
*(padded++) = 0;
memcpy(padded, der, der_size);
@@ -266,20 +345,18 @@ static int pkcs1_type1_pad(uint8_t *padded, uint32_t padded_len,
}
/* verify */
-/* TODO(ngm): constant time */
static int check_pkcs1_type1_pad(const uint8_t *msg, uint32_t msg_len,
const uint8_t *padded, uint32_t padded_len,
enum hashing_mode hashing)
{
int i;
- const uint8_t *der = (hashing == HASH_SHA1) ? &SHA1_DER[0]
- : &SHA256_DER[0];
- const uint32_t der_size = (hashing == HASH_SHA1) ? sizeof(SHA1_DER)
- : sizeof(SHA256_DER);
- const uint32_t hash_size = (hashing == HASH_SHA1) ? SHA_DIGEST_SIZE
- : SHA256_DIGEST_SIZE;
+ const uint8_t *der;
+ uint32_t der_size;
+ uint32_t hash_size;
uint32_t ps_len;
+ if (!pkcs1_get_der(hashing, &der, &der_size, &hash_size))
+ return 0;
if (msg_len != hash_size)
return 0;
if (padded_len < RSA_PKCS1_PADDING_SIZE + der_size + hash_size)
@@ -295,10 +372,10 @@ static int check_pkcs1_type1_pad(const uint8_t *msg, uint32_t msg_len,
if (padded[i++] != 0)
return 0;
- if (memcmp(&padded[i], der, der_size) != 0)
+ if (!DCRYPTO_equals(&padded[i], der, der_size))
return 0;
i += der_size;
- return memcmp(msg, &padded[i], hash_size) == 0;
+ return DCRYPTO_equals(msg, &padded[i], hash_size);
}
/* sign */
@@ -398,13 +475,14 @@ static int check_pkcs1_pss_pad(const uint8_t *in, uint32_t in_len,
HASH_update(&ctx, zeros, sizeof(zeros));
HASH_update(&ctx, in, in_len);
HASH_update(&ctx, padded + db_len - salt_len, salt_len);
- bad |= memcmp(padded + db_len, HASH_final(&ctx), hash_size);
+ bad |= !DCRYPTO_equals(padded + db_len, HASH_final(&ctx), hash_size);
return !bad;
}
-static int check_modulus_params(const struct LITE_BIGNUM *N, uint32_t *out_len)
+static int check_modulus_params(
+ const struct LITE_BIGNUM *N, size_t rsa_max_bytes, uint32_t *out_len)
{
- if (bn_size(N) > RSA_MAX_BYTES)
+ if (bn_size(N) > rsa_max_bytes)
return 0; /* Unsupported key size. */
if (!bn_check_topbit(N)) /* Check that top bit is set. */
return 0;
@@ -423,16 +501,14 @@ int DCRYPTO_rsa_encrypt(struct RSA *rsa, uint8_t *out, uint32_t *out_len,
uint32_t e_buf[LITE_BN_BYTES / sizeof(uint32_t)];
struct LITE_BIGNUM padded;
- struct LITE_BIGNUM e;
struct LITE_BIGNUM encrypted;
+ int ret;
- if (!check_modulus_params(&rsa->N, out_len))
+ if (!check_modulus_params(&rsa->N, sizeof(padded_buf), out_len))
return 0;
bn_init(&padded, padded_buf, bn_size(&rsa->N));
bn_init(&encrypted, out, bn_size(&rsa->N));
- bn_init(&e, e_buf, sizeof(e_buf));
- BN_DIGIT(&e, 0) = rsa->e;
switch (padding) {
case PADDING_MODE_OAEP:
@@ -455,7 +531,7 @@ int DCRYPTO_rsa_encrypt(struct RSA *rsa, uint8_t *out, uint32_t *out_len,
/* If in_len < bn_size(&padded), padded will
* have leading zero bytes. */
memcpy(&p[bn_size(&padded) - in_len], in, in_len);
- /* TODO(ngm): in may be > N, bn_mont_mod_exp() should
+ /* TODO(ngm): in may be > N, bn_mod_exp() should
* handle this case. */
break;
default:
@@ -464,14 +540,14 @@ int DCRYPTO_rsa_encrypt(struct RSA *rsa, uint8_t *out, uint32_t *out_len,
/* Reverse from big-endian to little-endian notation. */
reverse((uint8_t *) padded.d, bn_size(&padded));
- bn_mont_modexp(&encrypted, &padded, &e, &rsa->N);
+ ret = bn_modexp_word(&encrypted, &padded, rsa->e, &rsa->N);
/* Back to big-endian notation. */
reverse((uint8_t *) encrypted.d, bn_size(&encrypted));
*out_len = bn_size(&encrypted);
- dcrypto_memset(padded_buf, 0, sizeof(padded_buf));
- dcrypto_memset(e_buf, 0, sizeof(e_buf));
- return 1;
+ always_memset(padded_buf, 0, sizeof(padded_buf));
+ always_memset(e_buf, 0, sizeof(e_buf));
+ return ret;
}
int DCRYPTO_rsa_decrypt(struct RSA *rsa, uint8_t *out, uint32_t *out_len,
@@ -484,9 +560,9 @@ int DCRYPTO_rsa_decrypt(struct RSA *rsa, uint8_t *out, uint32_t *out_len,
struct LITE_BIGNUM encrypted;
struct LITE_BIGNUM padded;
- int ret = 1;
+ int ret;
- if (!check_modulus_params(&rsa->N, NULL))
+ if (!check_modulus_params(&rsa->N, sizeof(padded_buf), NULL))
return 0;
if (in_len != bn_size(&rsa->N))
return 0; /* Invalid input length. */
@@ -498,7 +574,7 @@ int DCRYPTO_rsa_decrypt(struct RSA *rsa, uint8_t *out, uint32_t *out_len,
/* Reverse from big-endian to little-endian notation. */
reverse((uint8_t *) encrypted.d, encrypted.dmax * LITE_BN_BYTES);
- bn_mont_modexp(&padded, &encrypted, &rsa->d, &rsa->N);
+ ret = bn_modexp_blinded(&padded, &encrypted, &rsa->d, &rsa->N, rsa->e);
/* Back to big-endian notation. */
reverse((uint8_t *) padded.d, padded.dmax * LITE_BN_BYTES);
@@ -528,8 +604,8 @@ int DCRYPTO_rsa_decrypt(struct RSA *rsa, uint8_t *out, uint32_t *out_len,
break;
}
- dcrypto_memset(encrypted_buf, 0, sizeof(encrypted_buf));
- dcrypto_memset(padded_buf, 0, sizeof(padded_buf));
+ always_memset(encrypted_buf, 0, sizeof(encrypted_buf));
+ always_memset(padded_buf, 0, sizeof(padded_buf));
return ret;
}
@@ -541,8 +617,9 @@ int DCRYPTO_rsa_sign(struct RSA *rsa, uint8_t *out, uint32_t *out_len,
struct LITE_BIGNUM padded;
struct LITE_BIGNUM signature;
+ int ret;
- if (!check_modulus_params(&rsa->N, out_len))
+ if (!check_modulus_params(&rsa->N, sizeof(padded_buf), out_len))
return 0;
bn_init(&padded, padded_buf, bn_size(&rsa->N));
@@ -565,13 +642,13 @@ int DCRYPTO_rsa_sign(struct RSA *rsa, uint8_t *out, uint32_t *out_len,
/* Reverse from big-endian to little-endian notation. */
reverse((uint8_t *) padded.d, bn_size(&padded));
- bn_mont_modexp(&signature, &padded, &rsa->d, &rsa->N);
+ ret = bn_modexp_blinded(&signature, &padded, &rsa->d, &rsa->N, rsa->e);
/* Back to big-endian notation. */
reverse((uint8_t *) signature.d, bn_size(&signature));
*out_len = bn_size(&rsa->N);
- dcrypto_memset(padded_buf, 0, sizeof(padded_buf));
- return 1;
+ always_memset(padded_buf, 0, sizeof(padded_buf));
+ return ret;
}
int DCRYPTO_rsa_verify(const struct RSA *rsa, const uint8_t *digest,
@@ -579,16 +656,14 @@ int DCRYPTO_rsa_verify(const struct RSA *rsa, const uint8_t *digest,
const uint32_t sig_len, enum padding_mode padding,
enum hashing_mode hashing)
{
- uint32_t padded_buf[RSA_MAX_WORDS];
- uint32_t signature_buf[RSA_MAX_WORDS];
- uint32_t e_buf[LITE_BN_BYTES / sizeof(uint32_t)];
+ uint32_t padded_buf[RSA_WORDS_4K];
+ uint32_t signature_buf[RSA_WORDS_4K];
struct LITE_BIGNUM padded;
struct LITE_BIGNUM signature;
- struct LITE_BIGNUM e;
- int ret = 1;
+ int ret;
- if (!check_modulus_params(&rsa->N, NULL))
+ if (!check_modulus_params(&rsa->N, sizeof(padded_buf), NULL))
return 0;
if (sig_len != bn_size(&rsa->N))
return 0; /* Invalid input length. */
@@ -596,14 +671,12 @@ int DCRYPTO_rsa_verify(const struct RSA *rsa, const uint8_t *digest,
bn_init(&signature, signature_buf, bn_size(&rsa->N));
memcpy(signature_buf, sig, bn_size(&rsa->N));
bn_init(&padded, padded_buf, bn_size(&rsa->N));
- bn_init(&e, e_buf, sizeof(e_buf));
- BN_DIGIT(&e, 0) = rsa->e;
/* Reverse from big-endian to little-endian notation. */
- reverse((uint8_t *) signature.d, signature.dmax * LITE_BN_BYTES);
- bn_mont_modexp(&padded, &signature, &e, &rsa->N);
+ reverse((uint8_t *) signature.d, bn_size(&signature));
+ ret = bn_modexp_word(&padded, &signature, rsa->e, &rsa->N);
/* Back to big-endian notation. */
- reverse((uint8_t *) padded.d, padded.dmax * LITE_BN_BYTES);
+ reverse((uint8_t *) padded.d, bn_size(&padded));
switch (padding) {
case PADDING_MODE_PKCS1:
@@ -616,7 +689,7 @@ int DCRYPTO_rsa_verify(const struct RSA *rsa, const uint8_t *digest,
if (!check_pkcs1_pss_pad(
digest, digest_len, (uint8_t *) padded.d,
bn_size(&padded), hashing))
- return 0;
+ ret = 0;
break;
default:
/* Unsupported padding mode. */
@@ -624,8 +697,8 @@ int DCRYPTO_rsa_verify(const struct RSA *rsa, const uint8_t *digest,
break;
}
- dcrypto_memset(padded_buf, 0, sizeof(padded_buf));
- dcrypto_memset(signature_buf, 0, sizeof(signature_buf));
+ always_memset(padded_buf, 0, sizeof(padded_buf));
+ always_memset(signature_buf, 0, sizeof(signature_buf));
return ret;
}
@@ -635,7 +708,7 @@ int DCRYPTO_rsa_key_compute(struct LITE_BIGNUM *N, struct LITE_BIGNUM *d,
{
uint32_t ONE_buf = 1;
uint32_t phi_buf[RSA_MAX_WORDS];
- uint32_t q_buf[RSA_MAX_WORDS / 2];
+ uint32_t q_buf[RSA_MAX_WORDS / 2 + 1];
struct LITE_BIGNUM ONE;
struct LITE_BIGNUM e;
@@ -648,14 +721,15 @@ int DCRYPTO_rsa_key_compute(struct LITE_BIGNUM *N, struct LITE_BIGNUM *d,
/* q not provided, calculate it. */
memcpy(phi_buf, N->d, bn_size(N));
bn_init(&q_local, q_buf, bn_size(p));
- bn_sub(&phi, &ONE);
- if (!bn_modinv_vartime(&q_local, p, &phi))
+ q = &q_local;
+
+ if (!DCRYPTO_bn_div(q, NULL, &phi, p))
return 0;
+
/* Check that p * q == N */
- DCRYPTO_bn_mul(&phi, p, &q_local);
+ DCRYPTO_bn_mul(&phi, p, q);
if (!bn_eq(N, &phi))
return 0;
- q = &q_local;
} else {
DCRYPTO_bn_mul(N, p, q);
memcpy(phi_buf, N->d, bn_size(N));
diff --git a/chip/g/dcrypto/sha256.c b/chip/g/dcrypto/sha256.c
index e21d54b1d6..f127ab445a 100644
--- a/chip/g/dcrypto/sha256.c
+++ b/chip/g/dcrypto/sha256.c
@@ -147,6 +147,13 @@ void dcrypto_sha_init(enum sha_mode mode)
val |= GC_KEYMGR_SHA_CFG_EN_SHA1_MASK;
GREG32(KEYMGR, SHA_CFG_EN) = val;
+ /* Turn off random nops (which are enabled by default). */
+ GWRITE_FIELD(KEYMGR, SHA_RAND_STALL_CTL, STALL_EN, 0);
+ /* Configure random nop percentage at 12%. */
+ GWRITE_FIELD(KEYMGR, SHA_RAND_STALL_CTL, FREQ, 2);
+ /* Now turn on random nops. */
+ GWRITE_FIELD(KEYMGR, SHA_RAND_STALL_CTL, STALL_EN, 1);
+
/* Start SHA engine. */
GREG32(KEYMGR, SHA_TRIG) = GC_KEYMGR_SHA_TRIG_TRIG_GO_MASK;
}
diff --git a/chip/g/dcrypto/sha384.c b/chip/g/dcrypto/sha384.c
new file mode 100644
index 0000000000..6f3c6ca096
--- /dev/null
+++ b/chip/g/dcrypto/sha384.c
@@ -0,0 +1,20 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "dcrypto.h"
+#include "internal.h"
+
+#include "cryptoc/sha384.h"
+
+void DCRYPTO_SHA384_init(LITE_SHA512_CTX *ctx)
+{
+ SHA384_init(ctx);
+}
+
+const uint8_t *DCRYPTO_SHA384_hash(const void *data, uint32_t n,
+ uint8_t *digest)
+{
+ return SHA384_hash(data, n, digest);
+}
diff --git a/chip/g/dcrypto/sha512.c b/chip/g/dcrypto/sha512.c
new file mode 100644
index 0000000000..1446970174
--- /dev/null
+++ b/chip/g/dcrypto/sha512.c
@@ -0,0 +1,20 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "dcrypto.h"
+#include "internal.h"
+
+#include "cryptoc/sha512.h"
+
+void DCRYPTO_SHA512_init(LITE_SHA512_CTX *ctx)
+{
+ SHA512_init(ctx);
+}
+
+const uint8_t *DCRYPTO_SHA512_hash(const void *data, uint32_t n,
+ uint8_t *digest)
+{
+ return SHA512_hash(data, n, digest);
+}
diff --git a/chip/g/dcrypto/x509.c b/chip/g/dcrypto/x509.c
index c33a809e0b..b733f91b5e 100644
--- a/chip/g/dcrypto/x509.c
+++ b/chip/g/dcrypto/x509.c
@@ -10,10 +10,45 @@
/* Limit the size of long form encoded objects to < 64 kB. */
#define MAX_ASN1_OBJ_LEN_BYTES 3
+/* Reserve space for TLV encoding */
+#define SEQ_SMALL 2 /* < 128 bytes (1B type, 1B 7-bit length) */
+#define SEQ_MEDIUM 3 /* < 256 bytes (1B type, 1B length size, 1B length) */
+#define SEQ_LARGE 4 /* < 65536 bytes (1B type, 1B length size, 2B length) */
+
/* Tag related constants. */
-#define V_ASN1_CONSTRUCTED 0x20
-#define V_ASN1_SEQUENCE 0x10
-#define V_ASN1_BIT_STRING 0x03
+enum {
+ V_ASN1_INT = 0x02,
+ V_ASN1_BIT_STRING = 0x03,
+ V_ASN1_BYTES = 0x04,
+ V_ASN1_OBJ = 0x06,
+ V_ASN1_UTF8 = 0x0c,
+ V_ASN1_SEQUENCE = 0x10,
+ V_ASN1_SET = 0x11,
+ V_ASN1_ASCII = 0x13,
+ V_ASN1_TIME = 0x18,
+ V_ASN1_CONSTRUCTED = 0x20,
+ /* short helpers */
+ V_BITS = V_ASN1_BIT_STRING,
+ V_SEQ = V_ASN1_CONSTRUCTED | V_ASN1_SEQUENCE,
+ V_SET = V_ASN1_CONSTRUCTED | V_ASN1_SET,
+};
+
+struct asn1 {
+ uint8_t *p;
+ size_t n;
+};
+
+
+#define SEQ_START(X, T, L) \
+ do { \
+ int __old = (X).n; \
+ uint8_t __t = (T); \
+ int __l = (L); \
+ (X).n += __l;
+#define SEQ_END(X) \
+ (X).n = asn1_seq((X).p + __old, __t, __l, (X).n - __old - __l) + __old;\
+ } \
+ while (0)
/* The SHA256 OID, from https://tools.ietf.org/html/rfc5754#section-3.2
* Only the object bytes below, the DER encoding header ([0x30 0x0d])
@@ -22,7 +57,173 @@ static const uint8_t OID_SHA256_WITH_RSA_ENCRYPTION[13] = {
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
0x01, 0x01, 0x0b, 0x05, 0x00
};
+static const uint8_t OID_commonName[3] = {0x55, 0x04, 0x03};
+static const uint8_t OID_ecdsa_with_SHA256[8] = {0x2A, 0x86, 0x48, 0xCE,
+ 0x3D, 0x04, 0x03, 0x02};
+static const uint8_t OID_id_ecPublicKey[7] = {0x2A, 0x86, 0x48, 0xCE, 0x3D,
+ 0x02, 0x01};
+static const uint8_t OID_prime256v1[8] = {0x2A, 0x86, 0x48, 0xCE,
+ 0x3D, 0x03, 0x01, 0x07};
+static const uint8_t OID_fido_u2f[11] = {0x2B, 0x06, 0x01, 0x04, 0x01, 0x82,
+ 0xE5, 0x1C, 0x02, 0x01, 0x01};
+#define OID(X) sizeof(OID_##X), OID_##X
+
+/* ---- ASN.1 Generation ---- */
+
+/* start a tag and return write ptr */
+static uint8_t *asn1_tag(struct asn1 *ctx, uint8_t tag)
+{
+ ctx->p[(ctx->n)++] = tag;
+ return ctx->p + ctx->n;
+}
+
+/* DER encode length and return encoded size thereof */
+static int asn1_len(uint8_t *p, size_t size)
+{
+ if (size < 128) {
+ p[0] = size;
+ return 1;
+ } else if (size < 256) {
+ p[0] = 0x81;
+ p[1] = size;
+ return 2;
+ } else {
+ p[0] = 0x82;
+ p[1] = size >> 8;
+ p[2] = size;
+ return 3;
+ }
+}
+
+/*
+ * close sequence and move encapsulated data if needed
+ * return total length.
+ */
+static size_t asn1_seq(uint8_t *p, uint8_t tag, size_t l, size_t size)
+{
+ size_t tl;
+
+ p[0] = tag;
+ tl = asn1_len(p + 1, size) + 1;
+ /* TODO: tl > l fail */
+ if (tl < l)
+ memmove(p + tl, p + l, size);
+
+ return tl + size;
+}
+
+/* DER encode (small positive) integer */
+static void asn1_int(struct asn1 *ctx, uint32_t val)
+{
+ uint8_t *p = asn1_tag(ctx, V_ASN1_INT);
+
+ if (!val) {
+ *p++ = 1;
+ *p++ = 0;
+ } else {
+ int nbits = 32 - __builtin_clz(val);
+ int nbytes = (nbits + 7) / 8;
+
+ if ((nbits & 7) == 0) {
+ *p++ = nbytes + 1;
+ *p++ = 0;
+ } else {
+ *p++ = nbytes;
+ }
+ while (nbytes--)
+ *p++ = val >> (nbytes * 8);
+ }
+
+ ctx->n = p - ctx->p;
+}
+
+/* DER encode positive p256_int */
+static void asn1_p256_int(struct asn1 *ctx, const p256_int *n)
+{
+ uint8_t *p = asn1_tag(ctx, V_ASN1_INT);
+ uint8_t bn[P256_NBYTES];
+ int i;
+
+ p256_to_bin(n, bn);
+ for (i = 0; i < P256_NBYTES; ++i) {
+ if (bn[i] != 0)
+ break;
+ }
+ if (bn[i] & 0x80) {
+ *p++ = P256_NBYTES - i + 1;
+ *p++ = 0;
+ } else {
+ *p++ = P256_NBYTES - i;
+ }
+ for (; i < P256_NBYTES; ++i)
+ *p++ = bn[i];
+
+ ctx->n = p - ctx->p;
+}
+
+/* DER encode p256 signature */
+static void asn1_sig(struct asn1 *ctx, const p256_int *r, const p256_int *s)
+{
+ SEQ_START(*ctx, V_SEQ, SEQ_SMALL) {
+ asn1_p256_int(ctx, r);
+ asn1_p256_int(ctx, s);
+ }
+ SEQ_END(*ctx);
+}
+
+/* DER encode printable string */
+static void asn1_string(struct asn1 *ctx, uint8_t tag, const char *s)
+{
+ uint8_t *p = asn1_tag(ctx, tag);
+ size_t n = strlen(s);
+
+ p += asn1_len(p, n);
+ while (n--)
+ *p++ = *s++;
+
+ ctx->n = p - ctx->p;
+}
+/* DER encode bytes */
+static void asn1_object(struct asn1 *ctx, size_t n, const uint8_t *b)
+{
+ uint8_t *p = asn1_tag(ctx, V_ASN1_OBJ);
+
+ p += asn1_len(p, n);
+ while (n--)
+ *p++ = *b++;
+
+ ctx->n = p - ctx->p;
+}
+
+/* DER encode p256 pk */
+static void asn1_pub(struct asn1 *ctx, const p256_int *x, const p256_int *y)
+{
+ uint8_t *p = asn1_tag(ctx, 4); /* uncompressed format */
+
+ p256_to_bin(x, p); p += P256_NBYTES;
+ p256_to_bin(y, p); p += P256_NBYTES;
+
+ ctx->n = p - ctx->p;
+}
+
+size_t DCRYPTO_asn1_sigp(uint8_t *buf, const p256_int *r, const p256_int *s)
+{
+ struct asn1 asn1 = {buf, 0};
+
+ asn1_sig(&asn1, r, s);
+ return asn1.n;
+}
+
+size_t DCRYPTO_asn1_pubp(uint8_t *buf, const p256_int *x, const p256_int *y)
+{
+ struct asn1 asn1 = {buf, 0};
+
+ asn1_pub(&asn1, x, y);
+ return asn1.n;
+}
+
+/* ---- ASN.1 Parsing ---- */
/*
* An ASN.1 DER (Definite Encoding Rules) parser.
@@ -201,3 +402,137 @@ int DCRYPTO_x509_verify(const uint8_t *cert, size_t len,
return DCRYPTO_rsa_verify(ca_pub_key, digest, sizeof(digest),
sig, sig_len, PADDING_MODE_PKCS1, HASH_SHA256);
}
+
+/* ---- Certificate generation ---- */
+
+static void add_common_name(struct asn1 *ctx, int unique)
+{
+ const char *cname = unique ? STRINGIFY(BOARD) : "U2F";
+
+ SEQ_START(*ctx, V_SEQ, SEQ_SMALL) {
+ SEQ_START(*ctx, V_SET, SEQ_SMALL) {
+ SEQ_START(*ctx, V_SEQ, SEQ_SMALL) {
+ asn1_object(ctx, OID(commonName));
+ asn1_string(ctx, V_ASN1_ASCII, cname);
+ }
+ SEQ_END(*ctx);
+ }
+ SEQ_END(*ctx);
+ }
+ SEQ_END(*ctx);
+}
+
+int DCRYPTO_x509_gen_u2f_cert(const p256_int *d, const p256_int *pk_x,
+ const p256_int *pk_y, const p256_int *serial,
+ uint8_t *cert, const int n)
+{
+ struct asn1 ctx = {cert, 0};
+ HASH_CTX sha;
+ p256_int h, r, s;
+ struct drbg_ctx drbg;
+
+ SEQ_START(ctx, V_SEQ, SEQ_LARGE) { /* outer seq */
+ /*
+ * Grab current pointer to data to hash later.
+ * Note this will fail if cert body + cert sign is less
+ * than 256 bytes (SEQ_MEDIUM) -- not likely.
+ */
+ uint8_t *body = ctx.p + ctx.n;
+
+ /* Cert body seq */
+ SEQ_START(ctx, V_SEQ, SEQ_MEDIUM) {
+ /* X509 v3 */
+ SEQ_START(ctx, 0xa0, SEQ_SMALL) {
+ asn1_int(&ctx, 2);
+ }
+ SEQ_END(ctx);
+
+ /* Serial number */
+ if (serial)
+ asn1_p256_int(&ctx, serial);
+ else
+ asn1_int(&ctx, 1);
+
+ /* Signature algo */
+ SEQ_START(ctx, V_SEQ, SEQ_SMALL) {
+ asn1_object(&ctx, OID(ecdsa_with_SHA256));
+ }
+ SEQ_END(ctx);
+
+ /* Issuer */
+ add_common_name(&ctx, !!serial);
+
+ /* Expiry */
+ SEQ_START(ctx, V_SEQ, SEQ_SMALL) {
+ asn1_string(&ctx, V_ASN1_TIME, "20000101000000Z");
+ asn1_string(&ctx, V_ASN1_TIME, "20991231235959Z");
+ }
+ SEQ_END(ctx);
+
+ /* Subject */
+ add_common_name(&ctx, !!serial);
+
+ /* Subject pk */
+ SEQ_START(ctx, V_SEQ, SEQ_SMALL) {
+ /* pk parameters */
+ SEQ_START(ctx, V_SEQ, SEQ_SMALL) {
+ asn1_object(&ctx, OID(id_ecPublicKey));
+ asn1_object(&ctx, OID(prime256v1));
+ }
+ SEQ_END(ctx);
+ /* pk bits */
+ SEQ_START(ctx, V_BITS, SEQ_SMALL) {
+ /* No unused bit at the end */
+ asn1_tag(&ctx, 0);
+ asn1_pub(&ctx, pk_x, pk_y);
+ }
+ SEQ_END(ctx);
+ }
+ SEQ_END(ctx);
+
+ /* U2F transports indicator extension */
+ SEQ_START(ctx, 0xa3, SEQ_SMALL) {
+ SEQ_START(ctx, V_SEQ, SEQ_SMALL) {
+ SEQ_START(ctx, V_SEQ, SEQ_SMALL) {
+ asn1_object(&ctx, OID(fido_u2f));
+ SEQ_START(ctx, V_ASN1_BYTES, SEQ_SMALL) {
+ SEQ_START(ctx, V_BITS, SEQ_SMALL) {
+ /* 3 zero bits */
+ asn1_tag(&ctx, 3);
+ /* usb-internal transport */
+ asn1_tag(&ctx, 0x08);
+ }
+ SEQ_END(ctx);
+ }
+ SEQ_END(ctx);
+ }
+ SEQ_END(ctx);
+ }
+ SEQ_END(ctx);
+ }
+ SEQ_END(ctx);
+ }
+ SEQ_END(ctx); /* Cert body */
+
+ /* Sign all of cert body */
+ DCRYPTO_SHA256_init(&sha, 0);
+ HASH_update(&sha, body, (ctx.p + ctx.n) - body);
+ p256_from_bin(HASH_final(&sha), &h);
+ drbg_rfc6979_init(&drbg, d, &h);
+ if (!dcrypto_p256_ecdsa_sign(&drbg, d, &h, &r, &s))
+ return 0;
+
+ /* Append X509 signature */
+ SEQ_START(ctx, V_SEQ, SEQ_SMALL);
+ asn1_object(&ctx, OID(ecdsa_with_SHA256));
+ SEQ_END(ctx);
+ SEQ_START(ctx, V_BITS, SEQ_SMALL) {
+ /* no unused/zero bit at the end */
+ asn1_tag(&ctx, 0);
+ asn1_sig(&ctx, &r, &s);
+ } SEQ_END(ctx);
+
+ } SEQ_END(ctx); /* end of outer seq */
+
+ return ctx.n;
+}
diff --git a/chip/g/flash.c b/chip/g/flash.c
index 870f76ef34..6fd262a31d 100644
--- a/chip/g/flash.c
+++ b/chip/g/flash.c
@@ -40,10 +40,12 @@
#include "common.h"
#include "console.h"
+#include "cryptoc/util.h"
#include "flash.h"
#include "flash_config.h"
#include "flash_info.h"
#include "registers.h"
+#include "shared_mem.h"
#include "timer.h"
#include "watchdog.h"
@@ -105,7 +107,7 @@ uint32_t flash_physical_get_writable_flags(uint32_t cur_flags)
return 0; /* no flags writable */
}
-int flash_physical_protect_at_boot(enum flash_wp_range range)
+int flash_physical_protect_at_boot(uint32_t new_flags)
{
return EC_SUCCESS; /* yeah, I did it. */
}
@@ -166,10 +168,12 @@ static int do_flash_op(enum flash_op op, int is_info_bank,
/* What are we doing? */
switch (op) {
case OP_ERASE_BLOCK:
+#ifndef CR50_DEV
if (is_info_bank)
/* Erasing the INFO bank from the RW section is
* unsupported. */
return EC_ERROR_INVAL;
+#endif
opcode = 0x31415927;
words = 0; /* don't care, really */
/* This number is based on the TSMC spec Nme=Terase/Tsme */
@@ -191,6 +195,8 @@ static int do_flash_op(enum flash_op op, int is_info_bank,
words = 1;
max_attempts = 9;
break;
+ default:
+ return EC_ERROR_INVAL;
}
/*
@@ -350,22 +356,62 @@ int flash_physical_info_read_word(int byte_offset, uint32_t *dst)
return EC_SUCCESS;
}
-void flash_info_write_enable(void)
+/*
+ * Verify that the range's size is power of 2, the range offset is aligned by
+ * size, and the range does not cross the INFO space boundary.
+ */
+static int valid_info_range(uint32_t offset, size_t size)
+{
+ if (!size || (size & (size - 1)))
+ return 0;
+
+ if (offset & (size - 1))
+ return 0;
+
+ if ((offset + size) > FLASH_INFO_SIZE)
+ return 0;
+
+ return 1;
+
+}
+
+/* Write access is a superset of read access. */
+static int flash_info_configure_access(uint32_t offset,
+ size_t size, int write_mode)
+{
+ int mask;
+
+ if (!valid_info_range(offset, size))
+ return EC_ERROR_INVAL;
+
+ mask = GREG32(GLOBALSEC, FLASH_REGION6_CTRL);
+ mask |= GC_GLOBALSEC_FLASH_REGION6_CTRL_EN_MASK |
+ GC_GLOBALSEC_FLASH_REGION6_CTRL_RD_EN_MASK;
+ if (write_mode)
+ mask |= GC_GLOBALSEC_FLASH_REGION6_CTRL_WR_EN_MASK;
+
+ GREG32(GLOBALSEC, FLASH_REGION6_BASE_ADDR) =
+ FLASH_INFO_MEMORY_BASE + offset;
+
+ GREG32(GLOBALSEC, FLASH_REGION6_SIZE) = size - 1;
+ GREG32(GLOBALSEC, FLASH_REGION6_CTRL) = mask;
+
+ return EC_SUCCESS;
+}
+
+int flash_info_read_enable(uint32_t offset, size_t size)
+{
+ return flash_info_configure_access(offset, size, 0);
+}
+
+int flash_info_write_enable(uint32_t offset, size_t size)
{
- /* Enable R/W access to INFO. */
- GREG32(GLOBALSEC, FLASH_REGION3_BASE_ADDR) = FLASH_INFO_MEMORY_BASE +
- FLASH_INFO_MANUFACTURE_STATE_OFFSET;
- GREG32(GLOBALSEC, FLASH_REGION3_SIZE) =
- FLASH_INFO_MANUFACTURE_STATE_SIZE - 1;
- GREG32(GLOBALSEC, FLASH_REGION3_CTRL) =
- GC_GLOBALSEC_FLASH_REGION3_CTRL_EN_MASK |
- GC_GLOBALSEC_FLASH_REGION3_CTRL_WR_EN_MASK |
- GC_GLOBALSEC_FLASH_REGION3_CTRL_RD_EN_MASK;
+ return flash_info_configure_access(offset, size, 1);
}
void flash_info_write_disable(void)
{
- GREG32(GLOBALSEC, FLASH_REGION3_CTRL) = 0;
+ GWRITE_FIELD(GLOBALSEC, FLASH_REGION6_CTRL, WR_EN, 0);
}
int flash_info_physical_write(int byte_offset, int num_bytes, const char *data)
@@ -404,3 +450,72 @@ int flash_physical_erase(int byte_offset, int num_bytes)
return EC_SUCCESS;
}
+
+
+/* Enable write access to the backup RO section. */
+void flash_open_ro_window(uint32_t offset, size_t size_b)
+{
+ GREG32(GLOBALSEC, FLASH_REGION6_BASE_ADDR) =
+ offset + CONFIG_PROGRAM_MEMORY_BASE;
+ GREG32(GLOBALSEC, FLASH_REGION6_SIZE) = size_b - 1;
+ GWRITE_FIELD(GLOBALSEC, FLASH_REGION6_CTRL, EN, 1);
+ GWRITE_FIELD(GLOBALSEC, FLASH_REGION6_CTRL, RD_EN, 1);
+ GWRITE_FIELD(GLOBALSEC, FLASH_REGION6_CTRL, WR_EN, 1);
+}
+
+#ifdef CR50_DEV
+
+static int command_erase_flash_info(int argc, char **argv)
+{
+ uint32_t *preserved_manufacture_state;
+ const size_t manuf_word_count = FLASH_INFO_MANUFACTURE_STATE_SIZE /
+ sizeof(uint32_t);
+ int i;
+ int rv = EC_ERROR_BUSY;
+
+ if (shared_mem_acquire(FLASH_INFO_MANUFACTURE_STATE_SIZE,
+ (char **)&preserved_manufacture_state) !=
+ EC_SUCCESS) {
+ ccprintf("Failed to allocate memory for manufacture state!\n");
+ return rv;
+ }
+
+ flash_info_read_enable(0, 2048);
+ flash_info_write_enable(0, 2048);
+
+ /* Preserve manufacturing information. */
+ for (i = 0; i < manuf_word_count; i++) {
+ if (flash_physical_info_read_word
+ (FLASH_INFO_MANUFACTURE_STATE_OFFSET +
+ i * sizeof(uint32_t),
+ preserved_manufacture_state + i) != EC_SUCCESS) {
+ ccprintf("Failed to read word %d!\n", i);
+ goto exit;
+ }
+ }
+
+ if (do_flash_op(OP_ERASE_BLOCK, 1, 0, 512) != EC_SUCCESS) {
+ ccprintf("Failed to erase info space!\n");
+ goto exit;
+ }
+
+ if (flash_info_physical_write
+ (FLASH_INFO_MANUFACTURE_STATE_OFFSET,
+ FLASH_INFO_MANUFACTURE_STATE_SIZE,
+ (char *)preserved_manufacture_state) != EC_SUCCESS) {
+ ccprintf("Failed to restore manufacture state!\n");
+ goto exit;
+ }
+
+ rv = EC_SUCCESS;
+ exit:
+ always_memset(preserved_manufacture_state, 0,
+ FLASH_INFO_MANUFACTURE_STATE_SIZE);
+ shared_mem_release(preserved_manufacture_state);
+ flash_info_write_disable();
+ return rv;
+}
+DECLARE_CONSOLE_COMMAND(eraseflashinfo, command_erase_flash_info,
+ "",
+ "Erase INFO1 flash space");
+#endif
diff --git a/chip/g/flash_config.h b/chip/g/flash_config.h
index 40633e2281..09ddd872d5 100644
--- a/chip/g/flash_config.h
+++ b/chip/g/flash_config.h
@@ -37,7 +37,7 @@ struct g_flash_region {
* properly.
*
* The function is passed an array of the g_flash_region structures of the
- * max_regions size, it fills as many entties as necessary and returns the
+ * max_regions size, it fills as many entries as necessary and returns the
* number of set up entries.
*/
int flash_regions_to_enable(struct g_flash_region *regions,
diff --git a/chip/g/flash_info.h b/chip/g/flash_info.h
index dec14042a0..e07fac1ed7 100644
--- a/chip/g/flash_info.h
+++ b/chip/g/flash_info.h
@@ -37,4 +37,6 @@ void flash_info_write_disable(void);
int flash_info_physical_write(int byte_offset, int num_bytes, const char *data);
int flash_physical_info_read_word(int byte_offset, uint32_t *dst);
+void flash_open_ro_window(uint32_t offset, size_t size_b);
+
#endif /* ! __EC_CHIP_G_FLASH_INFO_H */
diff --git a/chip/g/gpio.c b/chip/g/gpio.c
index 0ec603d18b..c8399f01ce 100644
--- a/chip/g/gpio.c
+++ b/chip/g/gpio.c
@@ -41,6 +41,31 @@ void gpio_set_level(enum gpio_signal signal, int value)
set_one_gpio_bit(g->port, g->mask, value);
}
+int gpio_get_flags_by_mask(uint32_t port, uint32_t mask)
+{
+ uint32_t flags = 0;
+ uint32_t val = 0;
+
+ /* Only one bit must be set. */
+ if ((mask != (mask & -mask)) || (mask == 0))
+ return 0;
+
+ /* Check mode. */
+ /* ARM DDI 0479B: 3.5.2 */
+ val = GR_GPIO_SETDOUTEN(port) & mask;
+ if (val) {
+ flags |= GPIO_OUTPUT;
+ val = GR_GPIO_DOUT(port) & mask;
+ if (val)
+ flags |= GPIO_HIGH;
+ else
+ flags |= GPIO_LOW;
+ } else
+ flags |= GPIO_INPUT;
+
+ return flags;
+}
+
void gpio_set_flags_by_mask(uint32_t port, uint32_t mask, uint32_t flags)
{
/* Only matters for outputs */
@@ -59,24 +84,19 @@ void gpio_set_flags_by_mask(uint32_t port, uint32_t mask, uint32_t flags)
if (flags & GPIO_INT_F_LOW) {
GR_GPIO_CLRINTTYPE(port) = mask;
GR_GPIO_CLRINTPOL(port) = mask;
- GR_GPIO_SETINTEN(port) = mask;
}
if (flags & GPIO_INT_F_HIGH) {
GR_GPIO_CLRINTTYPE(port) = mask;
GR_GPIO_SETINTPOL(port) = mask;
- GR_GPIO_SETINTEN(port) = mask;
}
if (flags & GPIO_INT_F_FALLING) {
GR_GPIO_SETINTTYPE(port) = mask;
GR_GPIO_CLRINTPOL(port) = mask;
- GR_GPIO_SETINTEN(port) = mask;
}
if (flags & GPIO_INT_F_RISING) {
GR_GPIO_SETINTTYPE(port) = mask;
GR_GPIO_SETINTPOL(port) = mask;
- GR_GPIO_SETINTEN(port) = mask;
}
-
/* No way to trigger on both rising and falling edges, darn it. */
}
@@ -256,6 +276,13 @@ int gpio_disable_interrupt(enum gpio_signal signal)
return EC_SUCCESS;
}
+int gpio_clear_pending_interrupt(enum gpio_signal signal)
+{
+ const struct gpio_info *g = gpio_list + signal;
+ GR_GPIO_CLRINTSTAT(g->port) = g->mask;
+ return EC_SUCCESS;
+}
+
void gpio_pre_init(void)
{
const struct gpio_info *g = gpio_list;
@@ -325,12 +352,43 @@ void _gpio1_interrupt(void)
DECLARE_IRQ(GC_IRQNUM_GPIO0_GPIOCOMBINT, _gpio0_interrupt, 1);
DECLARE_IRQ(GC_IRQNUM_GPIO1_GPIOCOMBINT, _gpio1_interrupt, 1);
+/*
+ * The uart, i2c, and spi suffix arrays must match the order of the pinmux
+ * select registers in chip/g/hw_regdefs.h. If the order is incorrect, the
+ * pinmux command output will be wrong.
+ */
static const char * const uart_str[] = {
"0_CTS", "0_RTS", "0_RX", "0_TX",
"1_CTS", "1_RTS", "1_RX", "1_TX",
"2_CTS", "2_RTS", "2_RX", "2_TX",
};
+static const char * const i2c_str[] = {
+ "0_SCL", "0_SDA",
+ "1_SCL", "1_SDA",
+ "S0_SCL", "S0_SDA",
+};
+
+static const char * const spi_str[] = {
+ "SPICLK", "SPICSB", "SPIMISO", "SPIMOSI",
+};
+
+static void print_periph(int sel)
+{
+ if (sel >= 1 && sel <= 16)
+ ccprintf("GPIO0_GPIO%d", sel - 1);
+ else if (sel >= 17 && sel <= 32)
+ ccprintf("GPIO1_GPIO%d", sel - 17);
+ else if (sel >= 33 && sel <= 38)
+ ccprintf("I2C%s", i2c_str[sel - 33]);
+ else if (sel >= 49 && sel <= 52)
+ ccprintf("SPI1_%s", spi_str[sel - 49]);
+ else if (sel >= 67 && sel <= 78)
+ ccprintf("UART%s", uart_str[sel - 67]);
+ else if (sel)
+ ccprintf("UNDEF");
+}
+
static void show_pinmux(const char *name, int i, int ofs)
{
uint32_t sel = DIO_SEL_REG(i * 8 + ofs);
@@ -342,7 +400,7 @@ static void show_pinmux(const char *name, int i, int ofs)
if (!sel && !(ctl & (0xf << 2)) && !(GREG32(PINMUX, EXITEN0) & bitmask))
return;
- ccprintf("%08x: %s%-2d %2d %s%s%s%s",
+ ccprintf("%08x: %s%-2d %2d %s%s%s%s ",
GC_PINMUX_BASE_ADDR + i * 8 + ofs,
name, i, sel,
(ctl & (1<<2)) ? " IN" : "",
@@ -350,12 +408,7 @@ static void show_pinmux(const char *name, int i, int ofs)
(ctl & (1<<4)) ? " PU" : "",
(ctl & (1<<5)) ? " INV" : "");
- if (sel >= 1 && sel <= 16)
- ccprintf(" GPIO0_GPIO%d", sel - 1);
- else if (sel >= 17 && sel <= 32)
- ccprintf(" GPIO1_GPIO%d", sel - 17);
- else if (sel >= 67 && sel <= 78)
- ccprintf(" UART%s", uart_str[sel - 67]);
+ print_periph(sel);
if (GREG32(PINMUX, EXITEN0) & bitmask) {
ccprintf(" WAKE_");
@@ -365,6 +418,7 @@ static void show_pinmux(const char *name, int i, int ofs)
ccprintf("%s", edge ? "RISING" : "HIGH");
}
ccprintf("\n");
+ cflush();
}
static void print_dio_str(uint32_t sel)
@@ -379,34 +433,21 @@ static void print_dio_str(uint32_t sel)
ccprintf(" DIOM%d\n", 30 - sel);
else
ccprintf("\n");
+ cflush();
}
-static void show_pinmux_gpio(const char *name, int i, int ofs)
-{
- uint32_t sel = DIO_SEL_REG(i * 4 + ofs);
-
- if (sel == 0)
- return;
-
- ccprintf("%08x: %s%-2d %2d",
- GC_PINMUX_BASE_ADDR + i * 4 + ofs,
- name, i, sel);
- print_dio_str(sel);
-}
-
-static void show_pinmux_uart(int i)
+static void show_pinmux_periph(int i)
{
-
- uint32_t ofs = GC_PINMUX_UART0_CTS_SEL_OFFSET + i * 4;
+ uint32_t ofs = GC_PINMUX_GPIO0_GPIO0_SEL_OFFSET + i * 4;
uint32_t sel = DIO_SEL_REG(ofs);
if (sel == 0)
return;
- ccprintf("%08x: UART%s %2d",
- GC_PINMUX_BASE_ADDR + ofs,
- uart_str[i], sel);
+ ccprintf("%08x: ", GC_PINMUX_BASE_ADDR + ofs);
+ print_periph(i + 1);
+ ccprintf("\t%2d", sel);
print_dio_str(sel);
}
@@ -425,14 +466,10 @@ static int command_pinmux(int argc, char **argv)
ccprintf("\n");
/* GPIO & Peripheral sources */
- for (i = 0; i <= 15; i++)
- show_pinmux_gpio("GPIO0_GPIO", i, 0xf8);
- for (i = 0; i <= 15; i++)
- show_pinmux_gpio("GPIO1_GPIO", i, 0x134);
-
- for (i = 0; i <= 11; i++)
- show_pinmux_uart(i);
+ for (i = 0; i <= 98; i++)
+ show_pinmux_periph(i);
+ ccprintf("\n");
return EC_SUCCESS;
}
DECLARE_SAFE_CONSOLE_COMMAND(pinmux, command_pinmux,
diff --git a/chip/g/hwtimer.c b/chip/g/hwtimer.c
index 22d43775c4..91dba78c40 100644
--- a/chip/g/hwtimer.c
+++ b/chip/g/hwtimer.c
@@ -6,29 +6,52 @@
#include "common.h"
#include "hooks.h"
#include "hwtimer.h"
+#include "init_chip.h"
#include "registers.h"
#include "task.h"
+#include "timer.h"
#include "util.h"
-/* The frequency of timerls is 256k so there are about 4usec/tick */
-#define USEC_PER_TICK 4
+#define SOURCE(field) TIMER0_##field
+#define EVENT(field) TIMER1_##field
+
+/* The frequency of timerls is 8 * 32768 Hz. */
+#define TIMER_FREQ_HZ (8 * 32768)
+
/*
- * Scale the maximum number of ticks so that it will only count up to the
- * equivalent of 0xffffffff usecs.
+ * GCD(SECOND, TIMER_FREQ_HZ) = 64. We'll need to use reduced terms to prevent
+ * overflow of our intermediate uint32_t type in some calculations.
*/
-#define TIMELS_MAX (0xffffffff / USEC_PER_TICK)
+#define GCD 64
+#define TIMER_FREQ_GCD (TIMER_FREQ_HZ / GCD)
+#define TIME_GCD (SECOND / GCD)
-#define SOURCE(field) TIMER0_##field
-#define EVENT(field) TIMER1_##field
+/*
+ * Scale the maximum number of ticks so that it will only count up to the
+ * equivalent of approximately 0xffffffff usecs. Note that we lose 3us on
+ * timer wrap due to loss of precision during division.
+ */
+#define TIMELS_MAX (usecs_to_ticks(0xffffffff))
+/*
+ * The below calculation is lightweight and can be implemented using
+ * umull + shift on 32-bit ARM.
+ */
static inline uint32_t ticks_to_usecs(uint32_t ticks)
{
- return ticks * USEC_PER_TICK;
+ return (uint64_t)ticks * SECOND / TIMER_FREQ_HZ;
}
-static inline uint32_t usec_to_ticks(uint32_t next_evt_us)
+/*
+ * The below calulation is more tricky, this is very inefficient and requires
+ * 64-bit division:
+ * return ((uint64_t)(usecs) * TIMER_FREQ_HZ / SECOND);
+ * Instead use 32 bit vals, divide first, and add back the loss of precision.
+ */
+static inline uint32_t usecs_to_ticks(uint32_t usecs)
{
- return next_evt_us / USEC_PER_TICK;
+ return (usecs / TIME_GCD * TIMER_FREQ_GCD) +
+ ((usecs % TIME_GCD) * TIMER_FREQ_GCD / TIME_GCD);
}
uint32_t __hw_clock_event_get(void)
@@ -61,8 +84,7 @@ void __hw_clock_event_set(uint32_t deadline)
event_time = (deadline - __hw_clock_source_read());
/* Convert event_time to ticks rounding up */
- GREG32(TIMELS, EVENT(LOAD)) =
- ((uint64_t)(event_time + USEC_PER_TICK - 1) / USEC_PER_TICK);
+ GREG32(TIMELS, EVENT(LOAD)) = usecs_to_ticks(event_time) + 1;
/* Enable the timer & interrupts */
GWRITE(TIMELS, EVENT(IER), 1);
@@ -92,7 +114,7 @@ uint32_t __hw_clock_source_read(void)
void __hw_clock_source_set(uint32_t ts)
{
- GREG32(TIMELS, SOURCE(LOAD)) = (0xffffffff - ts) / USEC_PER_TICK;
+ GREG32(TIMELS, SOURCE(LOAD)) = usecs_to_ticks(0xffffffff - ts);
}
/* This handles rollover in the HW timer */
@@ -112,12 +134,14 @@ DECLARE_IRQ(GC_IRQNUM_TIMELS0_TIMINT0, __hw_clock_source_irq, 1);
int __hw_clock_source_init(uint32_t start_t)
{
- /* Verify the contents of CC_TRIM are valid */
- ASSERT(GR_FUSE(RC_RTC_OSC256K_CC_EN) == 0x5);
+ if (runlevel_is_high()) {
+ /* Verify the contents of CC_TRIM are valid */
+ ASSERT(GR_FUSE(RC_RTC_OSC256K_CC_EN) == 0x5);
- /* Initialize RTC to 256kHz */
- GWRITE_FIELD(RTC, CTRL, X_RTC_RC_CTRL,
- GR_FUSE(RC_RTC_OSC256K_CC_TRIM));
+ /* Initialize RTC to 256kHz */
+ GWRITE_FIELD(RTC, CTRL, X_RTC_RC_CTRL,
+ GR_FUSE(RC_RTC_OSC256K_CC_TRIM));
+ }
/* Configure timer1 */
GREG32(TIMELS, EVENT(LOAD)) = TIMELS_MAX;
@@ -153,3 +177,37 @@ int __hw_clock_source_init(uint32_t start_t)
/* Return the Event timer IRQ number (NOT the HW timer IRQ) */
return GC_IRQNUM_TIMELS0_TIMINT1;
}
+
+#ifdef CONFIG_HW_SPECIFIC_UDELAY
+/*
+ * Custom chip/g udelay(), guaranteed to delay for at least us microseconds.
+ *
+ * Lost time during timer wrap is not taken into account since interrupt latency
+ * and __hw_clock_source_irq() execution time likely exceeds the lost 3us.
+ */
+void udelay(unsigned us)
+{
+ unsigned t0 = __hw_clock_source_read();
+
+ /*
+ * The timer will tick either 3 us or 4 us, every ~3.8us in realtime.
+ * To ensure we meet the minimum delay, we must wait out a full
+ * longest-case timer tick (4 us), since a tick may have occurred
+ * immediately after sampling t0.
+ */
+ us += ticks_to_usecs(1) + 1;
+
+ /*
+ * udelay() may be called with interrupts disabled, so we can't rely on
+ * process_timers() updating the top 32 bits. So handle wraparound
+ * ourselves rather than calling get_time() and comparing with a
+ * deadline.
+ *
+ * This may fail for delays close to 2^32 us (~4000 sec), because the
+ * subtraction below can overflow. That's acceptable, because the
+ * watchdog timer would have tripped long before that anyway.
+ */
+ while (__hw_clock_source_read() - t0 <= us)
+ ;
+}
+#endif /* CONFIG_HW_SPECIFIC_UDELAY */
diff --git a/chip/g/i2cm.c b/chip/g/i2cm.c
new file mode 100644
index 0000000000..b77bd91935
--- /dev/null
+++ b/chip/g/i2cm.c
@@ -0,0 +1,485 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * This is a driver for the I2C Master controller (i2cm) of the g chip.
+ *
+ * The g chip i2cm module supports 3 modes of operation, disabled, bit-banging,
+ * and instruction based. These modes are selected via the I2C_CTRL
+ * register. Selecting disabled mode can be used as a soft reset where the i2cm
+ * hw state machine is reset, but the register values remain unchanged. In
+ * bit-banging mode the signals SDA/SCL are controlled by the lower two bits of
+ * the INST register. I2C_INST[1:0] = SCL|SDA. In this mode the value of SDA is
+ * read every clock cycle.
+ *
+ * The main operation mode is instruction mode. A 32 bit instruction register
+ * (I2C_INST) is used to describe a sequence of operations. The I2C transaction
+ * is initiated when this register is written. The I2C module contains a status
+ * register which in real-time tracks the progress of the I2C sequence that was
+ * configured in the INST register. If enabled, an interrupt is generated when
+ * the transaction is completed. If not using interrupts then bit 24 (INTB) of
+ * the status register can be polled for 0. INTB is the inverse of the i2cm
+ * interrupt status.
+ *
+ * The i2cm module provides a 64 byte fifo (RWBYTES) for both write and read
+ * transactions. In addition there is a 4 byte fifo (FWBYTES) that can be used
+ * for writes, for the register write of portion of a read transaction. By
+ * default the pointer to RWBYTES fifo resets back 0 following each
+ * transaction.
+ *
+ * As mentioned, i2c transactions are configured via the I2C_INST register.
+ * A 2 byte register write would create the following bitmap to define the
+ * compound instruction for the transaction:
+ *
+ * I2C_INST_START = 1 -> send start bit
+ * I2C_INST_FWDEVADDR = 1 -> first send the slave device address
+ * I2C_INST_FWBYTESCOUNT = 3 -> 3 bytes in FWBYTES (register + 16 bit value)
+ * I2C_INST_FINALSTOP = 1 -> send stop bit
+ * I2C_INST_DEVADDRVAL = slave address
+ *
+ * I2C_FWBYTES[b7:b0] = out[0] -> register address
+ * I2C_FWBYTES[b15:b8] = out[1] -> first byte of value
+ * I2C_FWBYTES[b23:b16] = out[2] -> 2nd byte of value
+ *
+ * A 2 byte register read would create the following bitmap to define the
+ * compound instruction for the transaction:
+ *
+ * I2C_INST_START = 1 -> send start bit
+ * I2C_INST_FWDEVADDR = 1 -> first send the slave device address
+ * I2C_INST_FWBYTESCOUNT = 1 -> 1 byte in FWBYTES (register address)
+ * I2C_INST_REPEATEDSTART = 1 -> send start bit following write
+ * I2C_INST_RWDEVADDR = 1 -> send slave address in read mode
+ * I2C_INST_RWDEVADDR_RWB = 1 -> read bytes following slave address
+ * I2C_INST_FINALNA = 1 -> ACK read bytes, NACK last byte read
+ * I2C_INST_FINALSTOP = 1 -> send stop bit
+ * I2C_INST_DEVADDRVAL = slave address
+ * I2C_FWBYTES[b7:b0] = out[0] -> register address byte
+ *
+ * Once transaction is complete:
+ * in[0] = I2C_RW0[b7:b0] -> copy first byte of read into destination
+ * in[1] = I2C_RW0[b15:b8] -> copy 2nd byte of read into destination
+ *
+ * Once the register I2C_INST is written with the instruction words constructed
+ * as shown, the transaction on the bus will commence. When I2C_INST is written,
+ * I2C_STATUS[b23:b0] is updated to reflect the transaction
+ * details and I2C_STATUS[b24] is set to 1. The transaction is complete when
+ * I2C_STATUS[b24] is 0. If interrupts are enabled, then an interrupt would be
+ * generated at this same point. The values of I2C_STATUS[b23:b0] are updated as
+ * the transaction progresses. Upon a completion of a successful transaction
+ * I2C_STATUS will be 0. If there was an error, the error details are contained
+ * in the upper bits of of I2C_STATUS, specifically [b31:b25].
+ */
+
+#include "common.h"
+#include "console.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "i2c.h"
+#include "pmu.h"
+#include "registers.h"
+#include "system.h"
+#include "timer.h"
+
+#define CPRINTS(format, args...) cprints(CC_I2C, format, ## args)
+
+/*
+ * Limits for polling I2C transaction. The time limit of 25 msec is a
+ * conservative value for the worst case (68 byte transfer) at 100 kHz clock
+ * speed.
+ */
+#define I2CM_POLL_WAIT_US 25
+#define I2CM_MAX_POLL_ITERATIONS (25000 / I2CM_POLL_WAIT_US)
+
+/* Sizes for first write (FW) and read/write (RW) fifos */
+#define I2CM_FW_BYTES_MAX 4
+#define I2CM_RW_BYTES_MAX 64
+
+/* Macros to set bits/fields of the INST word for sequences*/
+#define INST_START GFIELD_MASK(I2C, INST, START)
+#define INST_STOP GFIELD_MASK(I2C, INST, FINALSTOP)
+#define INST_RPT_START GFIELD_MASK(I2C, INST, REPEATEDSTART)
+#define INST_FWDEVADDR GFIELD_MASK(I2C, INST, FWDEVADDR)
+#define INST_DEVADDRVAL(addr) (addr << GFIELD_LSB(I2C, INST, \
+ DEVADDRVAL))
+#define INST_RWDEVADDR GFIELD_MASK(I2C, INST, RWDEVADDR)
+#define INST_RWDEVADDR_RWB GFIELD_MASK(I2C, INST, RWDEVADDR_RWB)
+#define INST_NA GFIELD_MASK(I2C, INST, FINALNA)
+#define INST_RWBYTES(size) (size << GFIELD_LSB(I2C, INST, \
+ RWBYTESCOUNT))
+
+/* Mask for b31:INTB of STATUS register */
+#define I2CM_ERROR_MASK (~((1 << GFIELD_LSB(I2C, STATUS, INTB)) - 1))
+
+enum i2cm_control_mode {
+ i2c_mode_disabled = 0,
+ i2c_mode_bit_bang = 1,
+ i2c_mode_instruction = 2,
+ i2c_mode_reserved = 3,
+};
+
+#define I2C_NUM_PHASESTEPS 4
+struct i2c_xfer_mode {
+ uint8_t clk_div;
+ uint8_t phase_steps[I2C_NUM_PHASESTEPS];
+};
+
+/*
+ * TODO (crosbug.com/p/58355): For 100 and 400 kHz speed, phasestep0 has been
+ * adjusted longer that what should be required due to slow rise times on both
+ * Reef and Gru boards. In addition, the suggested values from the H1 chip spec
+ * were based off a 26 MHz clock. Have an ask to get suggested values for the
+ * actual 24 MHz bus speed.
+ */
+const struct i2c_xfer_mode i2c_timing[I2C_FREQ_COUNT] = {
+ /* 1000 kHz */
+ {
+ .clk_div = 1,
+ .phase_steps = {5, 5, 5, 11},
+ },
+ /* 400 kHz */
+ {
+ .clk_div = 1,
+ .phase_steps = {15, 12, 12, 21},
+ },
+ /* 100 kHz */
+ {
+ .clk_div = 10,
+ .phase_steps = {9, 6, 5, 4},
+ },
+};
+
+static void i2cm_config_xfer_mode(int port, enum i2c_freq freq)
+{
+ /* Set the control mode to disabled (soft reset) */
+ GWRITE_I(I2C, port, CTRL_MODE, i2c_mode_disabled);
+
+ /* Set the phasesteps register for the requested bus frequency */
+ GWRITE_FIELD_I(I2C, port, CTRL_PHASESTEPS, P0,
+ i2c_timing[freq].phase_steps[0]);
+ GWRITE_FIELD_I(I2C, port, CTRL_PHASESTEPS, P1,
+ i2c_timing[freq].phase_steps[1]);
+ GWRITE_FIELD_I(I2C, port, CTRL_PHASESTEPS, P2,
+ i2c_timing[freq].phase_steps[2]);
+ GWRITE_FIELD_I(I2C, port, CTRL_PHASESTEPS, P3,
+ i2c_timing[freq].phase_steps[3]);
+
+ /* Set the clock divide control register */
+ GWRITE_I(I2C, port, CTRL_CLKDIV, i2c_timing[freq].clk_div);
+ /* Ensure that INST register is reset */
+ GWRITE_I(I2C, port, INST, 0);
+ /* Set the control mode register to instruction */
+ GWRITE_I(I2C, port, CTRL_MODE, i2c_mode_instruction);
+}
+
+static void i2cm_set_fwbytes(int port, uint32_t *inst, const uint8_t *data,
+ int size)
+{
+ int i;
+ uint32_t fwbytes = 0;
+
+ /* Indicate that first write bytes field will be used */
+ *inst |= size << GFIELD_LSB(I2C, INST, FWBYTESCOUNT);
+
+ /* Now write data to FWBYTES register */
+ for (i = 0; i < size; i++)
+ fwbytes |= data[i] << (i * 8);
+ GWRITE_I(I2C, port, FW, fwbytes);
+}
+
+static void i2cm_write_rwbytes(int port, const uint8_t *out, int size)
+{
+ volatile uint32_t *rw_ptr;
+ int rw_count;
+ int i;
+
+ /* Calculate number of RW register writes required */
+ rw_count = (size + 3) >> 2;
+ /* Get pointer to RW0 register (start of fifo) */
+ rw_ptr = GREG32_ADDR_I(I2C, port, RW0);
+
+ /*
+ * Get write data from source buffer one byte at a time and write up to
+ * 4 bytes at a time in to the RW fifo.
+ */
+ for (i = 0; i < rw_count; i++) {
+ int byte_count;
+ int j;
+ uint32_t rw_data = 0;
+
+ byte_count = MIN(4, size);
+ for (j = 0; j < byte_count; j++)
+ rw_data |= *out++ << (j * 8);
+ size -= byte_count;
+ *rw_ptr++ = rw_data;
+ }
+}
+
+static void i2cm_read_rwbytes(int port, uint8_t *in, int size)
+{
+ int rw_count;
+ int i;
+ volatile uint32_t *rw_ptr;
+
+ /* Calculate number of RW register writes required */
+ rw_count = (size + 3) >> 2;
+ /* Get pointer to RW0 register (start of fifo) */
+ rw_ptr = GREG32_ADDR_I(I2C, port, RW0);
+
+ /*
+ * Read data from fifo up to 4 bytes at a time and copy into
+ * destination buffer 1 byte at a time.
+ */
+ for (i = 0; i < rw_count; i++) {
+ int byte_count;
+ int j;
+ uint32_t rw_data;
+
+ rw_data = *rw_ptr++;
+ byte_count = MIN(4, size);
+ for (j = 0; j < byte_count; j++) {
+ *in++ = rw_data;
+ rw_data >>= 8;
+ }
+ size -= byte_count;
+ }
+}
+
+static int i2cm_poll_for_complete(int port)
+{
+ int poll_count = 0;
+
+ while (poll_count < I2CM_MAX_POLL_ITERATIONS) {
+ /* Check if the sequence is complete */
+ if (!GREAD_FIELD_I(I2C, port, STATUS, INTB))
+ return EC_SUCCESS;
+ /* Not done yet, sleep */
+ usleep(I2CM_POLL_WAIT_US);
+ poll_count++;
+ };
+
+ return EC_ERROR_TIMEOUT;
+}
+
+static uint32_t i2cm_build_sequence(int port, int slave_addr,
+ const uint8_t *out, int out_size,
+ uint8_t *in, int in_size, int flags)
+{
+ int bytes_consumed;
+ uint32_t inst = 0;
+
+ if (flags & I2C_XFER_START)
+ inst |= INST_START;
+
+ /*
+ * Setup slave device address. Calls to chip_i2c_xfer assume an 8 bit
+ * slave address. Need to shift right by 1 bit.
+ */
+ inst |= INST_DEVADDRVAL(slave_addr >> 1);
+
+ if (out_size) {
+ /* Send slave addr byte if this is start of I2C transaction */
+ if (flags & I2C_XFER_START)
+ inst |= INST_FWDEVADDR;
+ bytes_consumed = MIN(I2CM_FW_BYTES_MAX, out_size);
+ /* Setup first write bytes */
+ i2cm_set_fwbytes(port, &inst, out, bytes_consumed);
+ out_size -= bytes_consumed;
+ /* If write data remains, then put the rest in RW fifo */
+ if (out_size) {
+ out += bytes_consumed;
+ inst |= INST_RWBYTES(out_size);
+ i2cm_write_rwbytes(port, out, out_size);
+ }
+ }
+
+ if (in_size) {
+ /*
+ * If I2C_XFER_START is marked, then send slave address and
+ * indicate it's a read transaction.
+ */
+ if (flags & I2C_XFER_START) {
+ inst |= INST_RWDEVADDR;
+ inst |= INST_RWDEVADDR_RWB;
+ inst |= INST_RPT_START;
+ }
+ /* Setup number of bytes to read */
+ inst |= INST_RWBYTES(in_size);
+
+ /* NACK the last byte read */
+ if (flags & I2C_XFER_STOP)
+ inst |= INST_NA;
+ }
+
+ if (flags & I2C_XFER_STOP)
+ inst |= INST_STOP;
+
+ return inst;
+}
+
+static int i2cm_execute_sequence(int port, int slave_addr, const uint8_t *out,
+ int out_size, uint8_t *in, int in_size,
+ int flags)
+{
+ int rv;
+ uint32_t inst;
+
+ /* Build sequence instruction */
+ inst = i2cm_build_sequence(port, slave_addr, out, out_size, in,
+ in_size, flags);
+ /* Start transaction */
+ GWRITE_I(I2C, port, INST, inst);
+
+ /* Wait for transaction to be complete */
+ rv = i2cm_poll_for_complete(port);
+ /* Handle timeout case */
+ if (rv)
+ return rv;
+
+ /* Check status value for errors */
+ if (GREAD_I(I2C, port, STATUS) & I2CM_ERROR_MASK) {
+ /* If failed, then clear INST register */
+ GWRITE_I(I2C, port, INST, 0);
+ return EC_ERROR_UNKNOWN;
+ }
+
+ return EC_SUCCESS;
+}
+
+
+/* Perform an i2c transaction. */
+int chip_i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_size,
+ uint8_t *in, int in_size, int flags)
+{
+ int rv;
+ int sequence_flags;
+ int num_out, num_in;
+
+ if (!in_size && !out_size)
+ /* Nothing to do */
+ return EC_SUCCESS;
+
+ /*
+ * Cr50 can do sequences of up to 64 write or read bytes. In addition it
+ * can accommodate up to 4 write bytes and up to 64 read bytes in a
+ * sequence set up. However, if the number of write bytes is > 4, then
+ * the write and read must be done in separate sequences.
+ */
+
+ while (out_size > I2CM_FW_BYTES_MAX) {
+ /* number of bytes that can handed in 1 sequence */
+ num_out = MIN(I2CM_RW_BYTES_MAX + I2CM_FW_BYTES_MAX, out_size);
+ sequence_flags = flags;
+ /* If more than 1 sequence remaining, mask stop bit flag */
+ if ((out_size - num_out) || in_size)
+ sequence_flags &= ~I2C_XFER_STOP;
+ /* Execute transaction */
+ rv = i2cm_execute_sequence(port, slave_addr, out, num_out, in,
+ 0, sequence_flags);
+ if (rv)
+ return rv;
+ /* Update counts and flags */
+ out += num_out;
+ out_size -= num_out;
+ flags &= ~sequence_flags;
+ }
+
+ /* At this point out_size <= 4 */
+ while (out_size || in_size) {
+ num_in = MIN(I2CM_RW_BYTES_MAX, in_size);
+ num_out = out_size;
+ sequence_flags = flags;
+ /* If more than 1 sequence remaining, mask stop bit flag */
+ if (in_size - num_in)
+ sequence_flags &= ~I2C_XFER_STOP;
+
+ rv = i2cm_execute_sequence(port, slave_addr, out, num_out, in,
+ num_in, sequence_flags);
+ if (rv)
+ return rv;
+
+ /* If bytes were read, copy to destination buffer */
+ if (num_in) {
+ i2cm_read_rwbytes(port, in, num_in);
+ in += num_in;
+ in_size -= num_in;
+ }
+ out_size = 0;
+ flags &= ~sequence_flags;
+ }
+
+ return EC_SUCCESS;
+}
+
+int i2c_raw_get_scl(int port)
+{
+ enum gpio_signal pin;
+
+ if (get_scl_from_i2c_port(port, &pin) == EC_SUCCESS)
+ return gpio_get_level(pin);
+
+ /* If no SCL pin defined for this port, then return 1 to appear idle. */
+ return 1;
+}
+
+int i2c_raw_get_sda(int port)
+{
+ enum gpio_signal pin;
+
+ if (get_sda_from_i2c_port(port, &pin) == EC_SUCCESS)
+ return gpio_get_level(pin);
+
+ /* If no SDA pin defined for this port, then return 1 to appear idle. */
+ return 1;
+}
+
+int i2c_get_line_levels(int port)
+{
+ return (i2c_raw_get_sda(port) ? I2C_LINE_SDA_HIGH : 0) |
+ (i2c_raw_get_scl(port) ? I2C_LINE_SCL_HIGH : 0);
+
+}
+
+static void i2cm_init_port(const struct i2c_port_t *p)
+{
+ enum i2c_freq freq;
+
+ /* Enable clock for I2C Master */
+ pmu_clock_en(p->port ? PERIPH_I2C1 : PERIPH_I2C0);
+
+ /* Set operation speed. */
+ switch (p->kbps) {
+ case 1000: /* Fast-mode Plus */
+ freq = I2C_FREQ_1000KHZ;
+ break;
+ case 400: /* Fast-mode */
+ freq = I2C_FREQ_400KHZ;
+ break;
+ case 100: /* Standard-mode */
+ freq = I2C_FREQ_100KHZ;
+ break;
+ default: /* unknown speed, default to 100kBps */
+ CPRINTS("I2C bad speed %d kBps. Defaulting to 100kbps.",
+ p->kbps);
+ freq = I2C_FREQ_100KHZ;
+ }
+
+ /* Configure the transfer clocks and mode */
+ i2cm_config_xfer_mode(p->port, freq);
+
+ CPRINTS("Initalized I2C port %d, freq = %d kHz", p->port, p->kbps);
+}
+
+/**
+ * Initialize the i2c module for all supported ports.
+ */
+void i2cm_init(void)
+{
+ const struct i2c_port_t *p = i2c_ports;
+ int i;
+
+ for (i = 0; i < i2c_ports_used; i++, p++)
+ i2cm_init_port(p);
+
+}
diff --git a/chip/g/i2cs.c b/chip/g/i2cs.c
index 77a39a5a1e..0cd5abf13f 100644
--- a/chip/g/i2cs.c
+++ b/chip/g/i2cs.c
@@ -14,10 +14,10 @@
*
* The file holding data written by the master has associated with it a
* register showing where the controller accessed the file last, comparing it
- * with its pervious value tells the driver how many bytes recently written by
+ * with its previous value tells the driver how many bytes recently written by
* the master are there.
*
- * The file holding data to be read by the master has a register associtated
+ * The file holding data to be read by the master has a register associated
* with it showing where was the latest BIT the controller transmitted.
*
* The controller can generate interrupts on three different conditions:
@@ -64,12 +64,14 @@
#include "common.h"
#include "console.h"
+#include "gpio.h"
#include "hooks.h"
#include "i2cs.h"
#include "pmu.h"
#include "registers.h"
#include "system.h"
#include "task.h"
+#include "tpm_log.h"
#define REGISTER_FILE_SIZE (1 << 6) /* 64 bytes. */
#define REGISTER_FILE_MASK (REGISTER_FILE_SIZE - 1)
@@ -96,15 +98,40 @@ static uint16_t last_write_pointer;
*/
static uint16_t last_read_pointer;
+/*
+ * Keep track of i2c interrupts and the number of times the "hosed slave"
+ * condition was encountered.
+ */
+static uint16_t i2cs_read_irq_count;
+static uint16_t i2cs_read_recovery_count;
+
static void i2cs_init(void)
{
/* First decide if i2c is even needed for this platform. */
/* if (i2cs is not needed) return; */
- if (!(system_get_board_properties() & BOARD_SLAVE_CONFIG_I2C))
+ if (!board_tpm_uses_i2c())
return;
pmu_clock_en(PERIPH_I2CS);
+ /*
+ * Toggle the reset register to make sure i2cs interface is in the
+ * initial state even if it is mid transaction at this time.
+ */
+ GWRITE_FIELD(PMU, RST0, DI2CS0, 1);
+
+ /*
+ * This initialization is guraranteed to take way more than enough
+ * time for the reset to kick in.
+ */
+ memset(i2cs_buffer, 0, sizeof(i2cs_buffer));
+ last_write_pointer = 0;
+ last_read_pointer = 0;
+ i2cs_read_irq_count = 0;
+
+ GWRITE_FIELD(PMU, RST0, DI2CS0, 0);
+
+
/* Set pinmux registers for I2CS interface */
i2cs_set_pinmux();
@@ -114,7 +141,61 @@ static void i2cs_init(void)
/* Slave address is hardcoded to 0x50. */
GWRITE(I2CS, SLAVE_DEVADDRVAL, 0x50);
}
-DECLARE_HOOK(HOOK_INIT, i2cs_init, HOOK_PRIO_DEFAULT);
+
+/* Forward declaration of the hook function. */
+static void poll_read_state(void);
+DECLARE_DEFERRED(poll_read_state);
+
+/* Poll SDA line to detect the "hosed" condition. */
+#define READ_STATUS_CHECK_INTERVAL (500 * MSEC)
+
+/*
+ * Check for receive problems, if found - reinitialize the i2c slave
+ * interface.
+ */
+static void poll_read_state(void)
+{
+ /*
+ * Make sure there is no accidental match between
+ * last_i2cs_read_irq_count and i2cs_read_irq_count if the first run
+ * of this function happens when SDA is low.
+ */
+ static uint16_t last_i2cs_read_irq_count = ~0;
+
+ if (ap_is_on()) {
+ if (!gpio_get_level(GPIO_I2CS_SDA)) {
+ if (last_i2cs_read_irq_count == i2cs_read_irq_count) {
+ /*
+ * SDA line is low and number of RX interrupts
+ * has not changed since last poll when it was
+ * low, it must be hosed. Reinitialize the i2c
+ * interface (which will also restart this
+ * polling function).
+ */
+ last_i2cs_read_irq_count = ~0;
+ i2cs_read_recovery_count++;
+ i2cs_register_write_complete_handler
+ (write_complete_handler_);
+
+#ifdef CONFIG_TPM_LOGGING
+ tpm_log_event(TPM_I2C_RESET,
+ i2cs_read_recovery_count);
+#endif
+ return;
+ }
+ last_i2cs_read_irq_count = i2cs_read_irq_count;
+ }
+ } else {
+ /*
+ * AP is off, let's make sure that in case this function
+ * happens to run right after AP wakes up and i2c is active,
+ * there is no false positive 'hosed' condition detection.
+ */
+ if (last_i2cs_read_irq_count == i2cs_read_irq_count)
+ last_i2cs_read_irq_count -= 1;
+ }
+ hook_call_deferred(&poll_read_state_data, READ_STATUS_CHECK_INTERVAL);
+}
/* Process the 'end of a write cycle' interrupt. */
static void _i2cs_write_complete_int(void)
@@ -122,10 +203,13 @@ static void _i2cs_write_complete_int(void)
/* Reset the IRQ condition. */
GWRITE_FIELD(I2CS, INT_STATE, INTR_WRITE_COMPLETE, 1);
+ /* We're receiving some bytes, so don't sleep */
+ disable_sleep(SLEEP_MASK_I2C_SLAVE);
+
if (write_complete_handler_) {
uint16_t bytes_written;
uint16_t bytes_processed;
- uint32_t word_in_value;
+ uint32_t word_in_value = 0;
/* How many bytes has the master just written. */
bytes_written = ((uint16_t)GREAD(I2CS, WRITE_PTR) -
@@ -166,7 +250,18 @@ static void _i2cs_write_complete_int(void)
/* Invoke the callback to process the message. */
write_complete_handler_(i2cs_buffer, bytes_processed);
+
+ if (bytes_processed == 1)
+ i2cs_read_irq_count++;
}
+
+ /*
+ * Could be the end of a TPM trasaction. Set sleep to be reenabled in 1
+ * second. If this is not the end of a TPM response, then sleep will be
+ * disabled again in the next I2CS interrupt.
+ */
+ delay_sleep_by(1 * SECOND);
+ enable_sleep(SLEEP_MASK_I2C_SLAVE);
}
DECLARE_IRQ(GC_IRQNUM_I2CS0_INTR_WRITE_COMPLETE_INT,
_i2cs_write_complete_int, 1);
@@ -216,12 +311,12 @@ void i2cs_post_read_fill_fifo(uint8_t *buffer, size_t len)
/* Insert bytes until fifo is word aligned */
if (remainder_bytes) {
/* mask the bytes to be kept */
- word_out_value = *value_addr;
+ word_out_value = value_addr[addr_offset];
word_out_value &= (1 << (8 * start_offset)) - 1;
/* Write in remainder bytes */
for (i = 0; i < remainder_bytes; i++)
word_out_value |= *buffer++ << (8 * (start_offset + i));
- /* Write to fifo regsiter */
+ /* Write to fifo register */
value_addr[addr_offset] = word_out_value;
addr_offset = (addr_offset + 1) & (REGISTER_FILE_MASK >> 2);
/* Account for bytes consumed */
@@ -240,11 +335,11 @@ void i2cs_post_read_fill_fifo(uint8_t *buffer, size_t len)
}
len -= (num_words << 2);
- /* Now proccess remaining bytes (if any), will be <= 3 at this point */
+ /* Now process remaining bytes (if any), will be <= 3 at this point */
remainder_bytes = len;
if (remainder_bytes) {
/* read from HW fifo */
- word_out_value = *value_addr;
+ word_out_value = value_addr[addr_offset];
/* Mask bytes that need to be kept */
word_out_value &= (0xffffffff << (8 * remainder_bytes));
for (i = 0; i < remainder_bytes; i++)
@@ -255,16 +350,25 @@ void i2cs_post_read_fill_fifo(uint8_t *buffer, size_t len)
int i2cs_register_write_complete_handler(wr_complete_handler_f wc_handler)
{
- if (write_complete_handler_)
- return -1;
+ task_disable_irq(GC_IRQNUM_I2CS0_INTR_WRITE_COMPLETE_INT);
+
+ if (!wc_handler)
+ return 0;
+ i2cs_init();
write_complete_handler_ = wc_handler;
task_enable_irq(GC_IRQNUM_I2CS0_INTR_WRITE_COMPLETE_INT);
+ /*
+ * Start a self perpetuating polling function to check for 'hosed'
+ * condition periodically.
+ */
+ hook_call_deferred(&poll_read_state_data, READ_STATUS_CHECK_INTERVAL);
+
return 0;
}
-size_t i2cs_get_read_fifo_buffer_depth(void)
+size_t i2cs_zero_read_fifo_buffer_depth(void)
{
uint32_t hw_read_pointer;
size_t depth;
@@ -277,6 +381,20 @@ size_t i2cs_get_read_fifo_buffer_depth(void)
hw_read_pointer = GREAD(I2CS, READ_PTR) >> 3;
/* Determine the number of bytes buffered in the HW fifo */
depth = (last_read_pointer - hw_read_pointer) & REGISTER_FILE_MASK;
-
+ /*
+ * If queue depth is not zero, force it to 0 by adjusting
+ * last_read_pointer to where the hw read pointer is.
+ */
+ if (depth)
+ last_read_pointer = (uint16_t)hw_read_pointer;
+ /*
+ * Return number of bytes queued when this funciton is called so it can
+ * be tracked or logged by caller if desired.
+ */
return depth;
}
+
+void i2cs_get_status(struct i2cs_status *status)
+{
+ status->read_recovery_count = i2cs_read_recovery_count;
+}
diff --git a/chip/g/i2cs.h b/chip/g/i2cs.h
index 76e8117be0..8fbc28187f 100644
--- a/chip/g/i2cs.h
+++ b/chip/g/i2cs.h
@@ -31,11 +31,14 @@ void i2cs_post_read_data(uint8_t byte_to_read);
void i2cs_set_pinmux(void);
/*
- * Determine the number of bytes currently buffered in the I2CS READ fifo. This
+ * Ensure no bytes are currently buffered in the I2CS READ fifo. This
* value is calculated by finding the difference between read pointer that's
- * used by FW to add bytes to the HW fifo and the HW's read pointer.
+ * used by FW to add bytes to the HW fifo and the current value of the
+ * I2CS_READ_PTR register.
+ *
+ * @returns: the number of bytes buffered when the function is called
*/
-size_t i2cs_get_read_fifo_buffer_depth(void);
+size_t i2cs_zero_read_fifo_buffer_depth(void);
/*
* Write buffer of data into the I2CS HW read fifo. The function will operate a
@@ -47,4 +50,15 @@ size_t i2cs_get_read_fifo_buffer_depth(void);
*/
void i2cs_post_read_fill_fifo(uint8_t *buffer, size_t len);
+/*
+ * Provide upper layers with information with the I2CS interface
+ * status/statistics. The only piece of information currently provided is the
+ * counter of "hosed" i2c interface occurences, where i2c clocking stopped
+ * while slave was transmitting a zero.
+ */
+struct i2cs_status {
+ uint16_t read_recovery_count;
+};
+void i2cs_get_status(struct i2cs_status *status);
+
#endif /* ! __CHIP_G_I2CS_H */
diff --git a/chip/g/idle.c b/chip/g/idle.c
index 5486cebaa9..0bff45da84 100644
--- a/chip/g/idle.c
+++ b/chip/g/idle.c
@@ -3,17 +3,23 @@
* found in the LICENSE file.
*/
+#include "case_closed_debug.h"
+#include "clock.h"
#include "common.h"
#include "console.h"
#include "hooks.h"
#include "hwtimer.h"
+#include "init_chip.h"
#include "rdd.h"
#include "registers.h"
#include "system.h"
#include "task.h"
#include "timer.h"
+#include "usb_api.h"
#include "util.h"
+#define CPRINTS(format, args...) cprints(CC_USB, format, ## args)
+
/* What to do when we're just waiting */
static enum {
DONT_KNOW,
@@ -23,9 +29,10 @@ static enum {
NUM_CHOICES
} idle_action;
-#define IDLE_DEFAULT IDLE_SLEEP
#define EVENT_MIN 500
+static int idle_default;
+
static const char const *idle_name[] = {
"invalid",
"wfi",
@@ -36,31 +43,45 @@ BUILD_ASSERT(ARRAY_SIZE(idle_name) == NUM_CHOICES);
static int command_idle(int argc, char **argv)
{
- int c, i;
+ int i;
if (argc > 1) {
- c = tolower(argv[1][0]);
- for (i = 1; i < ARRAY_SIZE(idle_name); i++)
- if (idle_name[i][0] == c) {
- idle_action = i;
- break;
- }
+ if (!strncasecmp("c", argv[1], 1)) {
+ GREG32(PMU, PWRDN_SCRATCH17) = 0;
+ } else if (console_is_restricted()) {
+ ccprintf("Console is locked, cannot set idle state\n");
+ return EC_ERROR_INVAL;
+ } else {
+ for (i = 1; i < ARRAY_SIZE(idle_name); i++)
+ if (!strncasecmp(idle_name[i], argv[1], 1)) {
+ idle_action = i;
+ break;
+ }
+ }
}
ccprintf("idle action: %s\n", idle_name[idle_action]);
+ ccprintf("deep sleep count: %u\n", GREG32(PMU, PWRDN_SCRATCH17));
return EC_SUCCESS;
}
-DECLARE_CONSOLE_COMMAND(idle, command_idle,
- "[w|s|d]",
- "Set or show the idle action: wfi, sleep, deep sleep");
+DECLARE_SAFE_CONSOLE_COMMAND(idle, command_idle,
+ "[w|s|d|c]",
+ "Set idle action: wfi, sleep, deep sleep or "
+ "Clear the deep sleep count");
static int utmi_wakeup_is_enabled(void)
{
#ifdef CONFIG_RDD
- return is_utmi_wakeup_allowed();
-#endif
+ /*
+ * USB is only used for CCD, so only enable UTMI wakeups when RDD
+ * detects that a debug accessory is attached.
+ */
+ return ccd_ext_is_enabled();
+#else
+ /* USB is used for the host interface, so always enable UTMI wakeups */
return 1;
+#endif
}
static void prepare_to_sleep(void)
@@ -81,7 +102,7 @@ static void prepare_to_sleep(void)
/* Wake on RBOX interrupts */
GREG32(RBOX, WAKEUP) = GC_RBOX_WAKEUP_ENABLE_MASK;
- if (utmi_wakeup_is_enabled())
+ if (utmi_wakeup_is_enabled() && idle_action != IDLE_DEEP_SLEEP)
GR_PMU_EXITPD_MASK |=
GC_PMU_EXITPD_MASK_UTMI_SUSPEND_N_MASK;
@@ -94,9 +115,6 @@ static void prepare_to_sleep(void)
/*
* Deep sleep should only be enabled when the AP is off otherwise the
* TPM state will lost.
- *
- * TODO(crosbug.com/p/55747): Enable deep sleep when the AP is shut
- * down. Currently deep sleep is only enabled through the console.
*/
if (idle_action == IDLE_DEEP_SLEEP) {
/* Clear upcoming events. They don't matter in deep sleep */
@@ -105,15 +123,20 @@ static void prepare_to_sleep(void)
/* Configure pins for deep sleep */
board_configure_deep_sleep_wakepins();
- /*
- * Preserve some state prior to deep sleep. Pretty much all we
- * need is the device address, since everything else can be
- * reinitialized on resume.
- */
- GREG32(PMU, PWRDN_SCRATCH18) = GR_USB_DCFG;
+ /* Make sure the usb clock is enabled */
+ clock_enable_module(MODULE_USB, 1);
+ /* Preserve some state from USB hardware prior to deep sleep. */
+ if (!GREAD_FIELD(USB, PCGCCTL, RSTPDWNMODULE))
+ usb_save_suspended_state();
+ /* Increment the deep sleep count */
+ GREG32(PMU, PWRDN_SCRATCH17) =
+ GREG32(PMU, PWRDN_SCRATCH17) + 1;
+
+#ifndef CONFIG_NO_PINHOLD
/* Latch the pinmux values */
GREG32(PINMUX, HOLD) = 1;
+#endif
/* Clamp the USB pins and shut the PHY down. We have to do this
* in three separate steps, or Bad Things happen. */
@@ -162,15 +185,29 @@ void clock_refresh_console_in_use(void)
void disable_deep_sleep(void)
{
- idle_action = IDLE_DEFAULT;
+ idle_action = idle_default;
}
-DECLARE_HOOK(HOOK_CHIPSET_RESUME, disable_deep_sleep, HOOK_PRIO_DEFAULT);
void enable_deep_sleep(void)
{
idle_action = IDLE_DEEP_SLEEP;
}
-DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, enable_deep_sleep, HOOK_PRIO_DEFAULT);
+
+static void idle_init(void)
+{
+ /*
+ * If bus obfuscation is enabled disable sleep.
+ */
+ if ((GR_FUSE(OBFUSCATION_EN) == 5) ||
+ (GR_FUSE(FW_DEFINED_BROM_APPLYSEC) & (1 << 3)) ||
+ (runlevel_is_high() && GREAD(GLOBALSEC, OBFS_SW_EN))) {
+ CPRINTS("bus obfuscation enabled disabling sleep");
+ idle_default = IDLE_WFI;
+ } else {
+ idle_default = IDLE_SLEEP;
+ }
+}
+DECLARE_HOOK(HOOK_INIT, idle_init, HOOK_PRIO_DEFAULT - 1);
/* Custom idle task, executed when no tasks are ready to be scheduled. */
void __idle(void)
@@ -186,14 +223,14 @@ void __idle(void)
* this and set the idle_action.
*/
if (!idle_action)
- idle_action = IDLE_DEFAULT;
+ idle_action = idle_default;
- /* Disable sleep until 3 minutes after init */
- delay_sleep_by(3 * MINUTE);
+ /* Disable sleep for 20 seconds after init */
+ delay_sleep_by(20 * SECOND);
while (1) {
- /* Anyone still busy? */
+ /* Anyone still busy? (this checks sleep_mask) */
sleep_ok = DEEP_SLEEP_ALLOWED;
/* Wait a bit, just in case */
diff --git a/chip/g/init_chip.h b/chip/g/init_chip.h
index 090ac7b730..0daff279b7 100644
--- a/chip/g/init_chip.h
+++ b/chip/g/init_chip.h
@@ -6,6 +6,38 @@
#ifndef __CROS_EC_INIT_CHIP_H
#define __CROS_EC_INIT_CHIP_H
+/**
+ * This is the current state of the PMU persistent registers. There are two
+ * types: long life and pwrdn scratch. Long life will persist through any
+ * reset other than POR. PWRDN scratch only survives deep sleep.
+ *
+ * LONG_LIFE_SCRATCH 0 - 2
+ * SCRATCH0 - Rollback counter
+ * SCRATCH1 - Board properties
+ * SCRATCH2
+ *
+ * PWRDN_SCRATCH 0 - 15 - Locked
+ *
+ * PWRDN_SCRATCH 16 - 27 - Can be used by RW
+ * SCRATCH16 - Indicator that firmware is running for debug purposes
+ * SCRATCH17 - deep sleep count
+ * SCRATCH18 - Preserving USB_DCFG through deep sleep
+ * SCRATCH19 - Preserving USB data sequencing PID through deep sleep
+ *
+ * PWRDN_SCRATCH 28 - 31 - Reserved for boot rom
+ */
+
+
+enum permission_level {
+ PERMISSION_LOW = 0x00,
+ PERMISSION_MEDIUM = 0x33, /* APPS run at medium */
+ PERMISSION_HIGH = 0x3C,
+ PERMISSION_HIGHEST = 0x55
+};
+
+int runlevel_is_high(void);
+void init_runlevel(const enum permission_level desired_level);
+
void init_jittery_clock(int highsec);
void init_sof_clock(void);
diff --git a/chip/g/jitter.c b/chip/g/jitter.c
index 3a485d3d84..4715972497 100644
--- a/chip/g/jitter.c
+++ b/chip/g/jitter.c
@@ -35,7 +35,8 @@ void init_jittery_clock(int highsec)
/* saturate at 0xff */
bankval = (setting > 0xfff) ? 0xff : (setting >> 4);
- GR_XO_JTR_JITTERY_TRIM_BANK(bank) = bankval;
+ if (runlevel_is_high())
+ GR_XO_JTR_JITTERY_TRIM_BANK(bank) = bankval;
setting += stepx16;
if ((setting > skiplow) && (setting < skiphigh))
@@ -136,10 +137,10 @@ static void timer_sof_calibration_underrun_int(void)
{
unsigned coarseTrimValue = GREG32(XO, CLK_TIMER_RC_COARSE_ATE_TRIM);
- CPRINTS("%s: 0x%02x", __func__, coarseTrimValue);
-
- if (coarseTrimValue > 0x00)
+ if (coarseTrimValue > 0x00) {
+ CPRINTS("%s: 0x%02x", __func__, coarseTrimValue);
GREG32(XO, CLK_TIMER_RC_COARSE_ATE_TRIM) = coarseTrimValue - 1;
+ }
GREG32(XO, DXO_INT_STATE) =
GC_XO_DXO_INT_STATE_SLOW_CALIB_UNDERRUN_MASK;
@@ -153,17 +154,12 @@ DECLARE_IRQ(GC_IRQNUM_XO0_SLOW_CALIB_UNDERRUN_INT,
static void timer_sof_calibration_overflow_int(void)
{
unsigned coarseTrimValue = GREG32(XO, CLK_TIMER_RC_COARSE_ATE_TRIM);
- unsigned max;
-
- CPRINTS("%s: 0x%02x", __func__, coarseTrimValue);
- if (GREAD_FIELD(XO, CLK_TIMER_CALIB_TRIM_CTRL, MAX_TRIM_SEL))
- max = 0x1f;
- else
- max = 0xff;
-
- if (coarseTrimValue < max)
+ /* Coarse trim range is 0..0xff. */
+ if (coarseTrimValue < 0xff) {
+ CPRINTS("%s: 0x%02x", __func__, coarseTrimValue);
GREG32(XO, CLK_TIMER_RC_COARSE_ATE_TRIM) = coarseTrimValue + 1;
+ }
GREG32(XO, DXO_INT_STATE) =
GC_XO_DXO_INT_STATE_SLOW_CALIB_OVERFLOW_MASK;
diff --git a/chip/g/loader/main.c b/chip/g/loader/main.c
index e560c9b8b5..e304c30a49 100644
--- a/chip/g/loader/main.c
+++ b/chip/g/loader/main.c
@@ -15,7 +15,7 @@
/*
* This file is a proof of concept stub which will be extended and split into
- * appropriate pieces sortly, when full blown support for cr50 bootrom is
+ * appropriate pieces shortly, when full blown support for cr50 bootrom is
* introduced.
*/
uint32_t sleep_mask;
diff --git a/chip/g/pmu.c b/chip/g/pmu.c
index f6ec4d164d..47b8341d5a 100644
--- a/chip/g/pmu.c
+++ b/chip/g/pmu.c
@@ -7,11 +7,6 @@
#include "task.h"
/*
- * TODO_FPGA this file should be thoroughly reworked when actual support is
- * introduced.
- */
-
-/*
* RC Trim constants
*/
#define RCTRIM_RESOLUTION (12)
diff --git a/chip/g/post_reset.c b/chip/g/post_reset.c
new file mode 100644
index 0000000000..24a98a9470
--- /dev/null
+++ b/chip/g/post_reset.c
@@ -0,0 +1,39 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "config.h"
+#include "board.h"
+#include "console.h"
+#include "extension.h"
+#include "system.h"
+
+#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args)
+
+void post_reset_command_handler(void *body,
+ size_t cmd_size,
+ size_t *response_size)
+{
+ *response_size = 1;
+ ((uint8_t *)body)[0] = 0;
+ post_reboot_request();
+}
+
+DECLARE_EXTENSION_COMMAND(EXTENSION_POST_RESET, post_reset_command_handler);
+
+static enum vendor_cmd_rc immediate_reset(enum vendor_cmd_cc code,
+ void *buf,
+ size_t input_size,
+ size_t *response_size)
+{
+ CPRINTS("%s: rebooting on host's request", __func__);
+ cflush(); /* Let the console drain. */
+ /* This will never return. */
+ system_reset(SYSTEM_RESET_MANUALLY_TRIGGERED | SYSTEM_RESET_HARD);
+
+ /* Never reached. */
+ return VENDOR_RC_SUCCESS;
+}
+DECLARE_VENDOR_COMMAND(VENDOR_CC_IMMEDIATE_RESET, immediate_reset);
diff --git a/chip/g/pre_init.c b/chip/g/pre_init.c
new file mode 100644
index 0000000000..0fe7a7dbe8
--- /dev/null
+++ b/chip/g/pre_init.c
@@ -0,0 +1,28 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "board_config.h"
+#include "registers.h"
+
+void chip_pre_init(void)
+{
+ /*
+ * If we're resuming from deep sleep we need to undo some stuff as soon
+ * as possible and this is the first init function that's called.
+ *
+ * It doesn't hurt anything if this setup is not needed, but we don't
+ * investigate the reset cause until much later (and doing so is
+ * destructive), so we'll just do the post-deep-sleep setup every time.
+ */
+
+ /* Disable the deep sleep triggers */
+ GR_PMU_LOW_POWER_DIS = 0;
+ GR_PMU_EXITPD_MASK = 0;
+
+ /* Unfreeze the USB module */
+ GWRITE_FIELD(USB, PCGCCTL, STOPPCLK, 0);
+ GWRITE_FIELD(USB, PCGCCTL, RSTPDWNMODULE, 0);
+ GWRITE_FIELD(USB, PCGCCTL, PWRCLMP, 0);
+}
diff --git a/chip/g/rbox.c b/chip/g/rbox.c
index 627c587563..2be1e19c11 100644
--- a/chip/g/rbox.c
+++ b/chip/g/rbox.c
@@ -6,14 +6,51 @@
#include "clock.h"
#include "hooks.h"
#include "registers.h"
+#include "timer.h"
+
+#define POWER_BUTTON 2
+
+static uint8_t val;
+
+int rbox_powerbtn_is_pressed(void)
+{
+ return !GREAD_FIELD(RBOX, CHECK_OUTPUT, PWRB_OUT);
+}
+
+int rbox_powerbtn_override_is_enabled(void)
+{
+ return GREAD_FIELD(RBOX, OVERRIDE_OUTPUT, EN) & (1 << POWER_BUTTON);
+}
+
+void rbox_powerbtn_release(void)
+{
+ GWRITE_FIELD(RBOX, OVERRIDE_OUTPUT, EN, 0);
+ GWRITE_FIELD(RBOX, OVERRIDE_OUTPUT, OEN, 0);
+ GWRITE_FIELD(RBOX, OVERRIDE_OUTPUT, VAL, val);
+}
+
+void rbox_powerbtn_press(void)
+{
+ if (rbox_powerbtn_override_is_enabled())
+ return;
+
+ val = GREAD_FIELD(RBOX, OVERRIDE_OUTPUT, VAL);
+ GWRITE_FIELD(RBOX, OVERRIDE_OUTPUT, VAL, ~(1 << POWER_BUTTON) & val);
+ GWRITE_FIELD(RBOX, OVERRIDE_OUTPUT, OEN, 1 << POWER_BUTTON);
+ GWRITE_FIELD(RBOX, OVERRIDE_OUTPUT, EN, 1 << POWER_BUTTON);
+}
static void rbox_release_ec_reset(void)
{
+ /* Unfreeze the PINMUX */
+ GREG32(PINMUX, HOLD) = 0;
+
+ /* Allow some time for outputs to stabilize. */
+ usleep(500);
+
/* Let the EC go (the RO bootloader asserts it ASAP after POR) */
GREG32(RBOX, ASSERT_EC_RST) = 0;
- /* And unfreeze the PINMUX */
- GREG32(PINMUX, HOLD) = 0;
}
DECLARE_HOOK(HOOK_INIT, rbox_release_ec_reset, HOOK_PRIO_LAST);
@@ -62,7 +99,7 @@ static void rbox_init(void)
0x2 << GC_RBOX_DEBUG_TERM_KEY0_IN_LSB |
0x0 << GC_RBOX_DEBUG_TERM_KEY0_OUT_LSB |
0x1 << GC_RBOX_DEBUG_TERM_KEY1_IN_LSB |
- 0x0 << GC_RBOX_DEBUG_TERM_KEY1_IN_LSB);
+ 0x0 << GC_RBOX_DEBUG_TERM_KEY1_OUT_LSB);
/* DEBUG_BLOCK_OUTPUT value should be 0x157 */
GWRITE(RBOX, DEBUG_DRIVE,
0x3 << GC_RBOX_DEBUG_DRIVE_PWRB_OUT_LSB |
diff --git a/chip/g/rbox.h b/chip/g/rbox.h
new file mode 100644
index 0000000000..e327faaf8e
--- /dev/null
+++ b/chip/g/rbox.h
@@ -0,0 +1,28 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __CROS_RBOX_H
+#define __CROS_RBOX_H
+
+/**
+ * Return true if the power button output shows it is pressed
+ */
+int rbox_powerbtn_is_pressed(void);
+
+/**
+ * Return true if power button rbox output override is enabled
+ */
+int rbox_powerbtn_override_is_enabled(void);
+
+/**
+ * Disable the output override
+ */
+void rbox_powerbtn_release(void);
+
+/**
+ * Override power button output to force a power button press
+ */
+void rbox_powerbtn_press(void);
+#endif /* __CROS_RBOX_H */
diff --git a/chip/g/rdd.c b/chip/g/rdd.c
index d4592b9358..57c4bd96b9 100644
--- a/chip/g/rdd.c
+++ b/chip/g/rdd.c
@@ -5,6 +5,7 @@
#include "clock.h"
#include "console.h"
+#include "gpio.h"
#include "hooks.h"
#include "rdd.h"
#include "registers.h"
@@ -21,9 +22,26 @@
* debug accessory.
*/
#define DETECT_DEBUG 0x420
+
+/*
+ * The interrupt only triggers when the debug state is detected. If we want to
+ * trigger an interrupt when the debug state is *not* detected, we need to
+ * program the bit-inverse.
+ */
#define DETECT_DISCONNECT (~DETECT_DEBUG & 0xffff)
-int debug_cable_is_attached(void)
+/* State of RDD CC detection */
+static enum device_state state = DEVICE_STATE_DISCONNECTED;
+
+/* Force detecting a debug accessory (ignore RDD CC detect hardware) */
+static int force_detected;
+
+/**
+ * Get instantaneous cable detect state
+ *
+ * @return 1 if debug accessory is detected, 0 if not detected
+ */
+static int rdd_is_detected(void)
{
uint8_t cc1 = GREAD_FIELD(RDD, INPUT_PIN_VALUES, CC1);
uint8_t cc2 = GREAD_FIELD(RDD, INPUT_PIN_VALUES, CC2);
@@ -31,61 +49,205 @@ int debug_cable_is_attached(void)
return (cc1 == cc2 && (cc1 == 3 || cc1 == 1));
}
-void rdd_interrupt(void)
+void print_rdd_state(void)
{
- int is_debug;
+ ccprintf("Rdd: %s\n",
+ force_detected ? "keepalive" : device_state_name(state));
+}
- delay_sleep_by(1 * SECOND);
+/**
+ * Handle debug accessory disconnecting
+ */
+static void rdd_disconnect(void)
+{
+ CPRINTS("Rdd disconnect");
+ state = DEVICE_STATE_DISCONNECTED;
+
+ /*
+ * Stop pulling CCD_MODE_L low. The internal pullup configured in the
+ * pinmux will pull the signal back high, unless the EC is also pulling
+ * it low.
+ *
+ * This disables the SBUx muxes, if we were the only one driving
+ * CCD_MODE_L.
+ */
+ gpio_set_flags(GPIO_CCD_MODE_L, GPIO_INPUT);
+}
- is_debug = debug_cable_is_attached();
+/**
+ * Handle debug accessory connecting
+ *
+ * This can be deferred from both rdd_detect() and the interrupt handler, so
+ * it needs to check the current state to determine whether we're already
+ * connected.
+ */
+static void rdd_connect(void)
+{
+ /* If we were debouncing, we're done, and still connected */
+ if (state == DEVICE_STATE_DEBOUNCING)
+ state = DEVICE_STATE_CONNECTED;
- if (is_debug) {
- disable_sleep(SLEEP_MASK_RDD);
+ /* If we're already connected, done */
+ if (state == DEVICE_STATE_CONNECTED)
+ return;
- CPRINTS("Debug Accessory connected");
+ /* We were previously disconnected, so connect */
+ CPRINTS("Rdd connect");
+ state = DEVICE_STATE_CONNECTED;
- /* Detect when debug cable is disconnected */
- GWRITE(RDD, PROG_DEBUG_STATE_MAP, DETECT_DISCONNECT);
+ /* Start pulling CCD_MODE_L low to enable the SBUx muxes */
+ gpio_set_flags(GPIO_CCD_MODE_L, GPIO_OUT_LOW);
+}
+DECLARE_DEFERRED(rdd_connect);
- rdd_attached();
- } else if (!is_debug) {
- CPRINTS("Debug Accessory disconnected");
+/**
+ * Debug accessory detect interrupt
+ */
+static void rdd_interrupt(void)
+{
+ /*
+ * The Rdd detector is level-sensitive with debounce. That is, it
+ * samples the RDCCx pin states. If they're different, it resets the
+ * wait counter. If they're the same, it decrements the wait counter.
+ * Then if the counter is zero, and the state we're looking for matches
+ * the map, it fires the interrupt.
+ *
+ * Note that the counter *remains* zero until the pin states change.
+ *
+ * If we want to be able to wake on Rdd change, then interrupts need to
+ * remain enabled. Each time we get an interrupt, we'll toggle the map
+ * we're looking for to the opposite state. That stops the interrupt
+ * from continuing to fire on the current state. When the pins settle
+ * into a new state, we'll fire the interrupt again.
+ *
+ * Even with that, we can still get a double interrupt now and then,
+ * because the Rdd module runs on a different clock than we do. So the
+ * write we do to change the state map may not be picked up until the
+ * next clock, when the Rdd module has already generated its next
+ * interrupt based on the old map. This is harmless, because we're
+ * unlikely to actually trigger the deferred function twice, and it
+ * doesn't care if we do anyway because on the second call it'll
+ * already be in the connected state.
+ *
+ */
+ if (rdd_is_detected()) {
+ /* Accessory detected; toggle to looking for disconnect */
+ GWRITE(RDD, PROG_DEBUG_STATE_MAP, DETECT_DISCONNECT);
- /* Detect when debug cable is connected */
+ /*
+ * Trigger the deferred handler so that we move back into the
+ * connected state before our debounce interval expires.
+ */
+ hook_call_deferred(&rdd_connect_data, 0);
+ } else {
+ /*
+ * Not detected; toggle to looking for connect. We'll start
+ * debouncing disconnect the next time HOOK_SECOND triggers
+ * rdd_detect() below.
+ */
GWRITE(RDD, PROG_DEBUG_STATE_MAP, DETECT_DEBUG);
-
- rdd_detached();
-
- cflush();
- enable_sleep(SLEEP_MASK_RDD);
}
- /* Clear interrupt */
+ /* Make sure we stay awake long enough to advance the state machine */
+ delay_sleep_by(1 * SECOND);
+
+ /* Clear the interrupt */
GWRITE_FIELD(RDD, INT_STATE, INTR_DEBUG_STATE_DETECTED, 1);
}
DECLARE_IRQ(GC_IRQNUM_RDD0_INTR_DEBUG_STATE_DETECTED_INT, rdd_interrupt, 1);
-void rdd_init(void)
+/**
+ * RDD CC detect state machine
+ */
+static void rdd_detect(void)
+{
+ /* Handle detecting device */
+ if (force_detected || rdd_is_detected()) {
+ rdd_connect();
+ return;
+ }
+
+ /* CC wasn't detected. If we're already disconnected, done. */
+ if (state == DEVICE_STATE_DISCONNECTED)
+ return;
+
+ /* If we were debouncing, we're now sure we're disconnected */
+ if (state == DEVICE_STATE_DEBOUNCING) {
+ rdd_disconnect();
+ return;
+ }
+
+ /*
+ * Otherwise, we were connected but the accessory seems to be
+ * disconnected right now. PD negotiation (e.g. during EC reset or
+ * sysjump) can alter the RDCCx voltages, so we need to debounce this
+ * signal for longer than the Rdd hardware does to make sure it's
+ * really disconnected before we deassert CCD_MODE_L.
+ */
+ state = DEVICE_STATE_DEBOUNCING;
+}
+/*
+ * Bump up priority so this runs before the CCD_MODE_L state machine, because
+ * we can change CCD_MODE_L.
+ */
+DECLARE_HOOK(HOOK_SECOND, rdd_detect, HOOK_PRIO_DEFAULT - 1);
+
+void init_rdd_state(void)
{
- /* Enable RDD */
+ /* Enable RDD hardware */
clock_enable_module(MODULE_RDD, 1);
GWRITE(RDD, POWER_DOWN_B, 1);
- GWRITE(RDD, PROG_DEBUG_STATE_MAP, DETECT_DEBUG);
+ /*
+ * Note that there is currently (ha, see what I did there) a leakage
+ * path out of Cr50 into the CC lines. On some systems, this can cause
+ * false Rdd detection when the TCPCs are turned off. This may require
+ * a software workaround where RDD hardware must be powered down
+ * whenever the TCPCs are off, and can only be powered up for brief
+ * periods to do a quick check. See b/38019839 and b/64582597.
+ */
- /* Initialize the debug state based on the current cc values */
- rdd_interrupt();
+ /* Configure to detect accessory connected */
+ GWRITE(RDD, PROG_DEBUG_STATE_MAP, DETECT_DEBUG);
- /* Enable RDD interrupts */
+ /*
+ * Set the 0.4V comparator reference to 0.3V instead. The voltage is
+ * marginal near 0.4V for example with VBUS at 4.75V and a SuzyQable See
+ * b/64847312.
+ */
+ GWRITE_FIELD(RDD, REF_ADJ, LVL0P4V, 0x2);
+
+ /*
+ * Enable interrupt for detecting CC. This minimizes the time before
+ * we transition to cable-detected at boot, and will cause us to wake
+ * from deep sleep if a cable is plugged in.
+ */
task_enable_irq(GC_IRQNUM_RDD0_INTR_DEBUG_STATE_DETECTED_INT);
+ GWRITE_FIELD(RDD, INT_STATE, INTR_DEBUG_STATE_DETECTED, 1);
GWRITE_FIELD(RDD, INT_ENABLE, INTR_DEBUG_STATE_DETECTED, 1);
}
-DECLARE_HOOK(HOOK_INIT, rdd_init, HOOK_PRIO_DEFAULT);
-static int command_test_rdd(int argc, char **argv)
+static int command_rdd_keepalive(int argc, char **argv)
{
- GWRITE_FIELD(RDD, INT_TEST, INTR_DEBUG_STATE_DETECTED, 1);
+ if (argc == 1) {
+ print_rdd_state();
+ return EC_SUCCESS;
+ }
+
+ if (!parse_bool(argv[1], &force_detected))
+ return EC_ERROR_PARAM1;
+
+ if (force_detected) {
+ /* Force Rdd detect */
+ ccprintf("Forcing Rdd detect keepalive\n");
+ hook_call_deferred(&rdd_connect_data, 0);
+ } else {
+ /* Go back to actual hardware state */
+ ccprintf("Using actual Rdd state\n");
+ }
+
return EC_SUCCESS;
}
-DECLARE_CONSOLE_COMMAND(test_rdd, command_test_rdd, NULL,
- "Fake an RDD-detected interrupt");
+DECLARE_CONSOLE_COMMAND(rddkeepalive, command_rdd_keepalive,
+ "[BOOLEAN]",
+ "Get Rdd state or force keepalive");
diff --git a/chip/g/rdd.h b/chip/g/rdd.h
index 1f19ee9b3e..1fd4f89152 100644
--- a/chip/g/rdd.h
+++ b/chip/g/rdd.h
@@ -6,16 +6,14 @@
#ifndef __CROS_RDD_H
#define __CROS_RDD_H
-/* Detach from debug cable */
-void rdd_detached(void);
-
-/* Attach to debug cable */
-void rdd_attached(void);
+/**
+ * Initialize RDD module
+ */
+void init_rdd_state(void);
-/*
- * USB is only used for CCD, so only enable UTMI wakeups when RDD detects that
- * a debug accessory is attached and disable it as a wakeup source when the
- * cable is detached.
+/**
+ * Print debug accessory detect state
*/
-int is_utmi_wakeup_allowed(void);
+void print_rdd_state(void);
+
#endif /* __CROS_RDD_H */
diff --git a/chip/g/registers.h b/chip/g/registers.h
index 86f7a4ce81..de80896b70 100644
--- a/chip/g/registers.h
+++ b/chip/g/registers.h
@@ -316,6 +316,9 @@ static inline int x_timehs_addr(unsigned int module, unsigned int timer,
/* Key manager */
#define GR_KEYMGR_AES_KEY(n) REG32(GREG32_ADDR(KEYMGR, AES_KEY0) + (n))
#define GR_KEYMGR_AES_CTR(n) REG32(GREG32_ADDR(KEYMGR, AES_CTR0) + (n))
+#define GR_KEYMGR_GCM_H(n) REG32(GREG32_ADDR(KEYMGR, GCM_H0) + (n))
+#define GR_KEYMGR_GCM_HASH_IN(n) REG32(GREG32_ADDR(KEYMGR, GCM_HASH_IN0) + (n))
+#define GR_KEYMGR_GCM_MAC(n) REG32(GREG32_ADDR(KEYMGR, GCM_MAC0) + (n))
#define GR_KEYMGR_SHA_HASH(n) REG32(GREG32_ADDR(KEYMGR, SHA_STS_H0) + (n))
#define GR_KEYMGR_HKEY_FRR(n) REG32(GREG32_ADDR(KEYMGR, HKEY_FRR0) + (n))
@@ -503,7 +506,7 @@ static inline int x_timehs_addr(unsigned int module, unsigned int timer,
#define DXEPCTL_TXFNUM(n) ((n) << GC_USB_DIEPCTL1_TXFNUM_LSB)
#define DXEPCTL_STALL (1 << GC_USB_DIEPCTL0_STALL_LSB)
#define DXEPCTL_CNAK (1 << GC_USB_DIEPCTL0_CNAK_LSB)
-#define DXEPCTL_DPID (1 << GC_USB_DIEPCTL0_DPID_LSB)
+#define DXEPCTL_DPID (1 << GC_USB_DIEPCTL1_DPID_LSB)
#define DXEPCTL_SNAK (1 << GC_USB_DIEPCTL0_SNAK_LSB)
#define DXEPCTL_NAKSTS (1 << GC_USB_DIEPCTL0_NAKSTS_LSB)
#define DXEPCTL_EPENA (1 << GC_USB_DIEPCTL0_EPENA_LSB)
@@ -511,6 +514,8 @@ static inline int x_timehs_addr(unsigned int module, unsigned int timer,
#define DXEPCTL_USBACTEP (1 << GC_USB_DIEPCTL0_USBACTEP_LSB)
#define DXEPCTL_MPS64 (0 << GC_USB_DIEPCTL0_MPS_LSB)
#define DXEPCTL_MPS(cnt) ((cnt) << GC_USB_DIEPCTL1_MPS_LSB)
+#define DXEPCTL_SET_D0PID (1 << 28)
+#define DXEPCTL_SET_D1PID (1 << 29)
#define DXEPTSIZ_SUPCNT(n) ((n) << GC_USB_DOEPTSIZ0_SUPCNT_LSB)
#define DXEPTSIZ_PKTCNT(n) ((n) << GC_USB_DIEPTSIZ0_PKTCNT_LSB)
diff --git a/chip/g/runlevel.c b/chip/g/runlevel.c
new file mode 100644
index 0000000000..13e215e3de
--- /dev/null
+++ b/chip/g/runlevel.c
@@ -0,0 +1,49 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "init_chip.h"
+#include "registers.h"
+
+/* Drop run level to at least medium. */
+void init_runlevel(const enum permission_level desired_level)
+{
+ volatile uint32_t *const reg_addrs[] = {
+ /* CPU's use of the system peripheral bus */
+ GREG32_ADDR(GLOBALSEC, CPU0_S_PERMISSION),
+ /* CPU's use of the system bus via the debug access port */
+ GREG32_ADDR(GLOBALSEC, CPU0_S_DAP_PERMISSION),
+ /* DMA's use of the system peripheral bus */
+ GREG32_ADDR(GLOBALSEC, DDMA0_PERMISSION),
+ /*
+ * Current software level affects which (if any) scratch
+ * registers can be used for a warm boot hardware-verified
+ * jump.
+ */
+ GREG32_ADDR(GLOBALSEC, SOFTWARE_LVL),
+ };
+ int i;
+
+ /* Permission registers drop by 1 level (e.g. HIGHEST -> HIGH)
+ * each time a write is encountered (the value written does
+ * not matter). So we repeat writes and reads, until the
+ * desired level is reached.
+ */
+ for (i = 0; i < ARRAY_SIZE(reg_addrs); i++) {
+ uint32_t current_level;
+
+ while (1) {
+ current_level = *reg_addrs[i];
+ if (current_level <= desired_level)
+ break;
+ *reg_addrs[i] = desired_level;
+ }
+ }
+}
+
+int runlevel_is_high(void)
+{
+ return ((GREAD(GLOBALSEC, CPU0_S_PERMISSION) == PERMISSION_HIGH) ||
+ (GREAD(GLOBALSEC, CPU0_S_PERMISSION) == PERMISSION_HIGHEST));
+}
diff --git a/chip/g/spi_master.c b/chip/g/spi_master.c
index f3ddbd1bdb..13d8e15b75 100644
--- a/chip/g/spi_master.c
+++ b/chip/g/spi_master.c
@@ -34,6 +34,8 @@ int spi_transaction(const struct spi_device_t *spi_device,
int port = spi_device->port;
int rv = EC_SUCCESS;
timestamp_t timeout;
+ int transaction_size = 0;
+ int rxoffset = 0;
/* If SPI0's passthrough is enabled, SPI0 is not available unless the
* SPS's BUSY bit is set. */
@@ -43,11 +45,26 @@ int spi_transaction(const struct spi_device_t *spi_device,
return EC_ERROR_BUSY;
}
- /* Ensure it'll fit inside of the RX and TX buffers. Note that although
- * the buffers are separate, the total transmission size must fit in
- * the rx buffer. */
- if (txlen + rxlen > SPI_BUF_SIZE)
- return EC_ERROR_INVAL;
+ if (rxlen == SPI_READBACK_ALL) {
+ /* Bidirectional SPI sends and receives a bit for each clock.
+ * We'll need to make sure the buffers for RX and TX are equal
+ * and return a bit received for every bit sent.
+ */
+ if (txlen > SPI_BUF_SIZE)
+ return EC_ERROR_INVAL;
+ rxlen = txlen;
+ transaction_size = txlen;
+ rxoffset = 0;
+ } else {
+ /* Ensure it'll fit inside of the RX and TX buffers. Note that
+ * although the buffers are separate, the total transmission
+ * size must fit in the rx buffer.
+ */
+ if (txlen + rxlen > SPI_BUF_SIZE)
+ return EC_ERROR_INVAL;
+ transaction_size = rxlen + txlen;
+ rxoffset = txlen;
+ }
/* Grab the port's mutex. */
mutex_lock(&spi_mutex[port]);
@@ -62,7 +79,7 @@ int spi_transaction(const struct spi_device_t *spi_device,
#endif /* CONFIG_SPI_MASTER_NO_CS_GPIOS */
/* Initiate the transaction. */
- GWRITE_FIELD_I(SPI, port, XACT, SIZE, rxlen + txlen - 1);
+ GWRITE_FIELD_I(SPI, port, XACT, SIZE, transaction_size - 1);
GWRITE_FIELD_I(SPI, port, XACT, START, 1);
/* Wait for the SPI master to finish the transaction. */
@@ -77,7 +94,8 @@ int spi_transaction(const struct spi_device_t *spi_device,
GWRITE_FIELD_I(SPI, port, ISTATE_CLR, TXDONE, 1);
/* Copy the result. */
- memmove(rxdata, &((uint8_t *)GREG32_ADDR_I(SPI, port, RX_DATA))[txlen],
+ memmove(rxdata,
+ &((uint8_t *)GREG32_ADDR_I(SPI, port, RX_DATA))[rxoffset],
rxlen);
err_cs_high:
@@ -188,7 +206,7 @@ int spi_enable(int port, int enable)
continue;
#ifndef CONFIG_SPI_MASTER_NO_CS_GPIOS
- /* Make sure CS# is deaserted and disabled. */
+ /* Make sure CS# is deasserted and disabled. */
gpio_set_level(spi_devices[i].gpio_cs, 1);
gpio_set_flags(spi_devices[i].gpio_cs, GPIO_ODR_HIGH);
#endif /* CONFIG_SPI_MASTER_NO_CS_GPIOS */
diff --git a/chip/g/sps.c b/chip/g/sps.c
index a25e379bb6..76b4d4d462 100644
--- a/chip/g/sps.c
+++ b/chip/g/sps.c
@@ -5,6 +5,7 @@
#include "common.h"
#include "console.h"
+#include "gpio.h"
#include "hooks.h"
#include "pmu.h"
#include "registers.h"
@@ -44,7 +45,7 @@
/*
* Hardware pointers use one extra bit, which means that indexing FIFO and
- * values written into the pointers have to have dfferent sizes. Tracked under
+ * values written into the pointers have to have different sizes. Tracked under
* http://b/20894690
*/
#define SPS_FIFO_PTR_MASK ((SPS_FIFO_MASK << 1) | 1)
@@ -59,6 +60,9 @@ static uint32_t sps_tx_count, sps_rx_count, tx_empty_count, max_rx_batch;
#define CPUTS(outstr) cputs(CC_SPS, outstr)
#define CPRINTS(format, args...) cprints(CC_SPS, format, ## args)
+/* Flag indicating if there has been any data received while CS was asserted. */
+static uint8_t seen_data;
+
void sps_tx_status(uint8_t byte)
{
GREG32(SPS, DUMMY_WORD) = byte;
@@ -118,7 +122,7 @@ int sps_transmit(uint8_t *data, size_t data_size)
/*
* CR50 SPS controller does not allow byte
* accesses for writes into the FIFO, so read
- * modify/write is requred. Tracked uder
+ * modify/write is required. Tracked under
* http://b/20894727
*/
bit_shift = 8 * (wptr & 3);
@@ -151,7 +155,7 @@ int sps_transmit(uint8_t *data, size_t data_size)
/*
* Start TX if necessary. This happens after FIFO is primed, which
- * helps aleviate TX underrun problems but introduces delay before
+ * helps alleviate TX underrun problems but introduces delay before
* data starts coming out.
*/
if (!GREAD_FIELD(SPS, FIFO_CTRL, TXFIFO_EN))
@@ -161,6 +165,15 @@ int sps_transmit(uint8_t *data, size_t data_size)
return bytes_sent;
}
+static int sps_cs_asserted(void)
+{
+ /*
+ * Read the current value on the SPS CS line and return the iversion
+ * of it (CS is active low).
+ */
+ return !GREAD_FIELD(SPS, VAL, CSB);
+}
+
/** Configure the data transmission format
*
* @param mode Clock polarity and phase mode (0 - 3)
@@ -181,6 +194,20 @@ static void sps_configure(enum sps_mode mode, enum spi_clock_mode clk_mode,
/* xfer 0xff when tx fifo is empty */
GREG32(SPS, DUMMY_WORD) = GC_SPS_DUMMY_WORD_DEFAULT;
+ if (sps_cs_asserted()) {
+ /*
+ * Reset while the external controller is mid SPI
+ * transaction.
+ */
+ ccprintf("%s: reset while CS active\n", __func__);
+ /*
+ * Wait for external controller to deassert CS before
+ * continuing.
+ */
+ while (sps_cs_asserted())
+ ;
+ }
+
/* [5,4,3] [2,1,0]
* RX{DIS, EN, RST} TX{DIS, EN, RST}
*/
@@ -197,6 +224,8 @@ static void sps_configure(enum sps_mode mode, enum spi_clock_mode clk_mode,
GWRITE_FIELD(SPS, ICTRL, RXFIFO_LVL, 1);
+ seen_data = 0;
+
/* Use CS_DEASSERT to retrieve all remaining bytes from RX FIFO. */
GWRITE_FIELD(SPS, ISTATE_CLR, CS_DEASSERT, 1);
GWRITE_FIELD(SPS, ICTRL, CS_DEASSERT, 1);
@@ -211,8 +240,11 @@ static rx_handler_f sps_rx_handler;
int sps_register_rx_handler(enum sps_mode mode, rx_handler_f rx_handler,
unsigned rx_fifo_threshold)
{
- if (sps_rx_handler)
- return -1;
+ task_disable_irq(GC_IRQNUM_SPS0_RXFIFO_LVL_INTR);
+ task_disable_irq(GC_IRQNUM_SPS0_CS_DEASSERT_INTR);
+
+ if (!rx_handler)
+ return 0;
if (!rx_fifo_threshold)
rx_fifo_threshold = 8; /* This is a sensible default. */
@@ -225,25 +257,13 @@ int sps_register_rx_handler(enum sps_mode mode, rx_handler_f rx_handler,
return 0;
}
-int sps_unregister_rx_handler(void)
-{
- if (!sps_rx_handler)
- return -1;
-
- task_disable_irq(GC_IRQNUM_SPS0_RXFIFO_LVL_INTR);
- task_disable_irq(GC_IRQNUM_SPS0_CS_DEASSERT_INTR);
-
- sps_rx_handler = NULL;
- return 0;
-}
-
static void sps_init(void)
{
/*
* Check to see if slave SPI interface is required by the board before
* initializing it. If SPI option is not set, then just return.
*/
- if (!(system_get_board_properties() & BOARD_SLAVE_CONFIG_SPI))
+ if (!board_tpm_uses_spi())
return;
pmu_clock_en(PERIPH_SPS);
@@ -302,7 +322,7 @@ static void sps_advance_rx(int port, int data_size)
/*
* Actual receive interrupt processing function. Invokes the callback passing
* it a pointer to the linear space in the RX FIFO and the number of bytes
- * availabe at that address.
+ * available at that address.
*
* If RX fifo is wrapping around, the callback will be called twice with two
* flat pointers.
@@ -322,6 +342,7 @@ static void sps_rx_interrupt(uint32_t port, int cs_deasserted)
if (!data_size)
break;
+ seen_data = 1;
sps_rx_count += data_size;
if (sps_rx_handler)
@@ -333,13 +354,52 @@ static void sps_rx_interrupt(uint32_t port, int cs_deasserted)
sps_advance_rx(port, data_size);
}
- if (cs_deasserted)
- sps_rx_handler(NULL, 0, 1);
+ if (cs_deasserted) {
+ if (seen_data) {
+ /*
+ * SPI does not provide inherent flow control. Let's
+ * use this pin to signal the AP that the device has
+ * finished processing received data.
+ */
+
+ sps_rx_handler(NULL, 0, 1);
+ gpio_set_level(GPIO_INT_AP_L, 0);
+ gpio_set_level(GPIO_INT_AP_L, 1);
+ seen_data = 0;
+ }
+ }
}
static void sps_cs_deassert_interrupt(uint32_t port)
{
/* Make sure the receive FIFO is drained. */
+
+ if (sps_cs_asserted()) {
+ /*
+ * we must have been slow, this is the next CS assertion after
+ * the 'wake up' pulse, but we have not processed the wake up
+ * interrupt yet.
+ *
+ * There would be no other out of order CS assertions, as all
+ * the 'real' ones (as opposed to the wake up pulses) are
+ * confirmed by the H1 pulsing the AP interrupt line
+ */
+
+ /*
+ * Make sure we react to the next deassertion when it
+ * happens.
+ */
+ GWRITE_FIELD(SPS, ISTATE_CLR, CS_DEASSERT, 1);
+ GWRITE_FIELD(SPS, FIFO_CTRL, TXFIFO_EN, 0);
+ if (sps_cs_asserted())
+ return;
+
+ /*
+ * The CS went away while we were processing this interrupt,
+ * this was the 'real' CS, need to process data.
+ */
+ }
+
sps_rx_interrupt(port, 1);
GWRITE_FIELD(SPS, ISTATE_CLR, CS_DEASSERT, 1);
GWRITE_FIELD(SPS, FIFO_CTRL, TXFIFO_EN, 0);
@@ -375,7 +435,7 @@ DECLARE_IRQ(GC_IRQNUM_SPS0_RXFIFO_LVL_INTR, _sps0_interrupt, 1);
*/
/*
- * Receive callback implemets a simple state machine, it could be in one of
+ * Receive callback implements a simple state machine, it could be in one of
* three states: not started, receiving frame, frame finished.
*/
@@ -416,7 +476,7 @@ static void sps_receive_callback(uint8_t *data, size_t data_size, int cs_status)
rx_state = spstrx_receiving;
else
/*
- * If we won't be able to receve this much, enter the
+ * If we won't be able to receive this much, enter the
* 'frame finished' state.
*/
rx_state = spstrx_finished;
@@ -495,7 +555,7 @@ static int command_sps(int argc, char **argv)
/*
* Wait for receive state machine to transition out of 'frame
- * finised' state.
+ * finished' state.
*/
while (rx_state == spstrx_finished) {
watchdog_reload();
@@ -503,8 +563,6 @@ static int command_sps(int argc, char **argv)
}
}
- sps_unregister_rx_handler();
-
ccprintf("Processed %d frames\n", count - 1);
ccprintf("rx count %d, tx count %d, tx_empty %d, max rx batch %d\n",
sps_rx_count, sps_tx_count,
diff --git a/chip/g/sps.h b/chip/g/sps.h
index f7889b82bd..5f49d8d092 100644
--- a/chip/g/sps.h
+++ b/chip/g/sps.h
@@ -41,7 +41,7 @@ int sps_transmit(uint8_t *data, size_t data_size);
/*
* These functions return zero on success or non-zero on failure (attempt to
* register a callback on top of existing one, or attempt to unregister
- * non-exitisng callback.
+ * non-existing callback.
*
* rx_fifo_threshold value of zero means 'default'.
*/
diff --git a/chip/g/sps_tpm.c b/chip/g/sps_tpm.c
index 2c3f6876e7..5d5073bb6f 100644
--- a/chip/g/sps_tpm.c
+++ b/chip/g/sps_tpm.c
@@ -131,7 +131,7 @@ static void init_new_cycle(void)
enable_sleep(SLEEP_MASK_SPI);
}
-/* Extract R/W bit, register addresss, and data count from 4-byte header */
+/* Extract R/W bit, register address, and data count from 4-byte header */
static int header_says_to_read(uint8_t *data, uint32_t *reg, uint32_t *count)
{
uint32_t addr = data[1]; /* reg address is MSB first */
@@ -254,6 +254,9 @@ static void process_rx_data(uint8_t *data, size_t data_size)
static void tpm_rx_handler(uint8_t *data, size_t data_size, int cs_disabled)
{
+ if (chip_factory_mode())
+ return; /* Ignore TPM traffic in factory mode. */
+
if ((sps_tpm_state == SPS_TPM_STATE_RECEIVING_HEADER) ||
(sps_tpm_state == SPS_TPM_STATE_RECEIVING_WRITE_DATA))
process_rx_data(data, data_size);
@@ -262,12 +265,28 @@ static void tpm_rx_handler(uint8_t *data, size_t data_size, int cs_disabled)
init_new_cycle();
}
-void sps_tpm_enable(void)
+static void sps_if_stop(void)
+{
+ /* Let's shut down the interface while TPM is being reset. */
+ sps_register_rx_handler(0, NULL, 0);
+}
+
+static void sps_if_start(void)
{
/*
- * Let's make sure we get an interrupt as soon as the header is
- * received.
+ * Threshold of 3 makes sure we get an interrupt as soon as the header
+ * is received.
*/
- sps_register_rx_handler(SPS_GENERIC_MODE, tpm_rx_handler, 3);
init_new_cycle();
+ sps_register_rx_handler(SPS_GENERIC_MODE, tpm_rx_handler, 3);
+}
+
+
+static void sps_if_register(void)
+{
+ if (!board_tpm_uses_spi())
+ return;
+
+ tpm_register_interface(sps_if_start, sps_if_stop);
}
+DECLARE_HOOK(HOOK_INIT, sps_if_register, HOOK_PRIO_LAST);
diff --git a/chip/g/system.c b/chip/g/system.c
index b79af123ce..012d5372cb 100644
--- a/chip/g/system.c
+++ b/chip/g/system.c
@@ -3,18 +3,21 @@
* found in the LICENSE file.
*/
-#include "config.h"
+#include "board_id.h"
#include "console.h"
#include "cpu.h"
#include "cpu.h"
#include "flash.h"
+#include "flash_info.h"
#include "printf.h"
#include "registers.h"
-#include "signed_header.h"
#include "system.h"
+#include "system_chip.h"
#include "task.h"
#include "version.h"
+static uint8_t pinhold_on_reset;
+
static void check_reset_cause(void)
{
uint32_t g_rstsrc = GR_PMU_RSTSRC;
@@ -34,6 +37,8 @@ static void check_reset_cause(void)
/* This register is cleared by reading it */
uint32_t g_exitpd = GR_PMU_EXITPD_SRC;
+ flags |= RESET_FLAG_HIBERNATE;
+
if (g_exitpd & GC_PMU_EXITPD_SRC_PIN_PD_EXIT_MASK)
flags |= RESET_FLAG_WAKE_PIN;
if (g_exitpd & GC_PMU_EXITPD_SRC_UTMI_SUSPEND_N_MASK)
@@ -81,20 +86,43 @@ void system_pre_init(void)
* no effect.
*/
GREG32(GLOBALSEC, FLASH_REGION0_CTRL_CFG_EN) = 0;
+}
-#ifdef BOARD_CR50
- system_init_board_properties();
-#endif
+void system_pinhold_disengage(void)
+{
+ GREG32(PINMUX, HOLD) = 0;
}
-void system_reset(int flags)
+void system_pinhold_on_reset_enable(void)
{
- /* TODO: Do we need to handle SYSTEM_RESET_PRESERVE_FLAGS? Doubtful. */
- /* TODO(crosbug.com/p/47289): handle RESET_FLAG_WATCHDOG */
+ pinhold_on_reset = 1;
+}
+void system_pinhold_on_reset_disable(void)
+{
+ pinhold_on_reset = 0;
+}
+
+void system_reset(int flags)
+{
/* Disable interrupts to avoid task swaps during reboot */
interrupt_disable();
+#if defined(CHIP_FAMILY_CR50)
+ /*
+ * Decrement the retry counter on manually triggered reboots. We were
+ * able to process the console command, therefore we're probably okay.
+ */
+ if (flags & SYSTEM_RESET_MANUALLY_TRIGGERED)
+ system_decrement_retry_counter();
+
+ /*
+ * On CR50 we want every reset be hard reset, causing the entire
+ * chromebook to reboot: we don't want the TPM reset while the AP
+ * stays up.
+ */
+ GR_PMU_GLOBAL_RESET = GC_PMU_GLOBAL_RESET_KEY;
+#else
if (flags & SYSTEM_RESET_HARD) {
/* Reset the full microcontroller */
GR_PMU_GLOBAL_RESET = GC_PMU_GLOBAL_RESET_KEY;
@@ -104,6 +132,9 @@ void system_reset(int flags)
* state. To accomplish this, first register a wakeup
* timer and then enter lower power mode. */
+ if (pinhold_on_reset)
+ GREG32(PINMUX, HOLD) = 1;
+
/* Low speed timers continue to run in low power mode. */
GREG32(TIMELS, TIMER1_CONTROL) = 0x1;
/* Wait for this long. */
@@ -124,9 +155,11 @@ void system_reset(int flags)
GC_PMU_LOW_POWER_DIS_START_LSB,
1);
}
+#endif /* ^^^^^^^ CHIP_FAMILY_CR50 Not defined */
/* Wait for reboot; should never return */
- asm("wfi");
+ while (1)
+ asm("wfi");
}
const char *system_get_chip_vendor(void)
@@ -139,10 +172,41 @@ const char *system_get_chip_name(void)
return "cr50";
}
-const char *system_get_chip_revision(void)
+/*
+ * There are three versions of B2 H1s outhere in the wild so far: chromebook,
+ * poppy and detachable. The following registers are different in those
+ * three versions in the following way:
+ *
+ * register chromebook poppy detachable
+ *--------------------------------------------------------------------
+ * RBOX_KEY_COMBO0_VAL 0xc0 0x80 0xc0
+ * RBOX_POL_KEY1_IN 0x01 0x00 0x00
+ * RBOX_KEY_COMBO0_HOLD 0x00 0x00 0x59
+ */
+
+static const struct {
+ uint32_t register_values;
+ const char *revision_str;
+} rev_map[] = {
+ {0xc00100, "B2-C"}, /* Chromebook. */
+ {0x800000, "B2-P"}, /* Poppy (a one off actually). */
+ {0xc00059, "B2-D"}, /* Detachable. */
+};
+
+/* Return a value which allows to identify the fuse setting of this chip. */
+static uint32_t get_fuse_set_id(void)
+{
+ return (GREAD_FIELD(FUSE, RBOX_KEY_COMBO0_VAL, VAL) << 16) |
+ (GREAD_FIELD(FUSE, RBOX_POL_KEY1_IN, VAL) << 8) |
+ GREAD_FIELD(FUSE, RBOX_KEY_COMBO0_HOLD, VAL);
+}
+
+static const char *get_revision_str(void)
{
int build_date = GR_SWDP_BUILD_DATE;
int build_time = GR_SWDP_BUILD_TIME;
+ uint32_t register_vals;
+ int i;
if ((build_date != GC_SWDP_BUILD_DATE_DEFAULT) ||
(build_time != GC_SWDP_BUILD_TIME_DEFAULT))
@@ -151,20 +215,62 @@ const char *system_get_chip_revision(void)
switch (GREAD_FIELD(PMU, CHIP_ID, REVISION)) {
case 3:
return "B1";
+
case 4:
- return "B2";
+ register_vals = get_fuse_set_id();
+ for (i = 0; i < ARRAY_SIZE(rev_map); i++)
+ if (rev_map[i].register_values == register_vals)
+ return rev_map[i].revision_str;
+
+ return "B2-?";
}
return "B?";
}
-/* TODO(crosbug.com/p/33822): Where can we store stuff persistently? */
-int system_get_vbnvcontext(uint8_t *block)
+const char *system_get_chip_revision(void)
+{
+ static const char *revision_str;
+
+ if (!revision_str)
+ revision_str = get_revision_str();
+
+ return revision_str;
+}
+
+int system_get_chip_unique_id(uint8_t **id)
+{
+ static uint32_t cached[8];
+
+ if (!cached[3]) { /* generate it if it doesn't exist yet */
+ const struct SignedHeader *ro_hdr = (const void *)
+ get_program_memory_addr(system_get_ro_image_copy());
+ const char *rev = get_revision_str();
+
+ cached[0] = ro_hdr->keyid;
+ cached[1] = GREG32(FUSE, DEV_ID0);
+ cached[2] = GREG32(FUSE, DEV_ID1);
+ strncpy((char *)&cached[3], rev, sizeof(cached[3]));
+ }
+ *id = (uint8_t *)cached;
+ return sizeof(cached);
+}
+
+int system_battery_cutoff_support_required(void)
+{
+ switch (get_fuse_set_id())
+ case 0xc00059:
+ return 1;
+
+ return 0;
+}
+
+int system_get_bbram(enum system_bbram_idx idx, uint8_t *value)
{
return 0;
}
-int system_set_vbnvcontext(const uint8_t *block)
+int system_set_bbram(enum system_bbram_idx idx, uint8_t value)
{
return 0;
}
@@ -187,6 +293,19 @@ enum system_image_copy_t system_get_ro_image_copy(void)
}
/*
+ * TODO(crbug.com/698882): Remove support for version_struct_deprecated once
+ * we no longer care about supporting legacy RO.
+ */
+struct version_struct_deprecated {
+ uint32_t cookie1;
+ char version[32];
+ uint32_t cookie2;
+};
+
+#define CROS_EC_IMAGE_DATA_COOKIE1_DEPRECATED 0xce112233
+#define CROS_EC_IMAGE_DATA_COOKIE2_DEPRECATED 0xce445566
+
+/*
* The RW images contain version strings. The RO images don't, so we'll make
* some here.
*/
@@ -195,7 +314,10 @@ static char vers_str[MAX_RO_VER_LEN];
const char *system_get_version(enum system_image_copy_t copy)
{
- const struct version_struct *v;
+ const struct image_data *data;
+ const struct version_struct_deprecated *data_deprecated;
+ const char *version;
+
const struct SignedHeader *h;
enum system_image_copy_t this_copy;
uintptr_t vaddr, delta;
@@ -226,7 +348,7 @@ const char *system_get_version(enum system_image_copy_t copy)
if (copy == this_copy) {
snprintf(vers_str, sizeof(vers_str), "%d.%d.%d/%s",
h->epoch_, h->major_, h->minor_,
- version_data.version);
+ current_image_data.version);
return vers_str;
}
@@ -235,26 +357,42 @@ const char *system_get_version(enum system_image_copy_t copy)
* puts the version string right after the reset vectors, so
* it's at the same relative offset. Measure that offset here.
*/
- delta = (uintptr_t)&version_data - vaddr;
+ delta = (uintptr_t)&current_image_data - vaddr;
/* Now look at that offset in the requested image */
vaddr = get_program_memory_addr(copy);
if (vaddr == INVALID_ADDR)
break;
h = (const struct SignedHeader *)vaddr;
+ /* Corrupted header's magic is set to zero. */
+ if (!h->magic)
+ break;
+
vaddr += delta;
- v = (const struct version_struct *)vaddr;
+ data = (const struct image_data *)vaddr;
+ data_deprecated = (const struct version_struct_deprecated *)
+ vaddr;
/*
* Make sure the version struct cookies match before returning
* the version string.
*/
- if (v->cookie1 == version_data.cookie1 &&
- v->cookie2 == version_data.cookie2) {
- snprintf(vers_str, sizeof(vers_str), "%d.%d.%d/%s",
- h->epoch_, h->major_, h->minor_, v->version);
- return vers_str;
- }
+ if (data->cookie1 == current_image_data.cookie1 &&
+ data->cookie2 == current_image_data.cookie2)
+ version = data->version;
+ /* Check for old / deprecated structure. */
+ else if (data_deprecated->cookie1 ==
+ CROS_EC_IMAGE_DATA_COOKIE1_DEPRECATED &&
+ data_deprecated->cookie2 ==
+ CROS_EC_IMAGE_DATA_COOKIE2_DEPRECATED)
+ version = data_deprecated->version;
+ else
+ break;
+
+ snprintf(vers_str, sizeof(vers_str), "%d.%d.%d/%s",
+ h->epoch_, h->major_, h->minor_, version);
+ return vers_str;
+
default:
break;
}
@@ -262,8 +400,7 @@ const char *system_get_version(enum system_image_copy_t copy)
return "Error";
}
-#ifdef BOARD_CR50
-
+#if defined(CHIP_FAMILY_CR50)
void system_clear_retry_counter(void)
{
GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG0, 1);
@@ -271,8 +408,19 @@ void system_clear_retry_counter(void)
GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG0, 0);
}
+void system_decrement_retry_counter(void)
+{
+ uint32_t val = GREG32(PMU, LONG_LIFE_SCRATCH0);
+
+ if (val != 0) {
+ GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG0, 1);
+ GREG32(PMU, LONG_LIFE_SCRATCH0) = val - 1;
+ GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG0, 0);
+ }
+}
+
/*
- * Check wich of the two cr50 RW images is newer, return true if the first
+ * Check which of the two cr50 RW images is newer, return true if the first
* image is no older than the second one.
*
* Note that RO and RW images use the same header structure. When deciding
@@ -301,7 +449,7 @@ static int a_is_newer_than_b(const struct SignedHeader *a,
* apparently failing image from being considered as a candidate to load and
* run on the following reboots.
*/
-static int corrupt_other_header(volatile struct SignedHeader *header)
+static int corrupt_header(volatile struct SignedHeader *header)
{
int rv;
const char zero[4] = {}; /* value to write to magic. */
@@ -335,19 +483,15 @@ static int corrupt_other_header(volatile struct SignedHeader *header)
*/
#define RW_BOOT_MAX_RETRY_COUNT 5
-int system_process_retry_counter(void)
+/*
+ * Check if the current running image is newer. Set the passed in pointer, if
+ * supplied, to point to the newer image in case the running image is the
+ * older one.
+ */
+static int current_image_is_newer(struct SignedHeader **newer_image)
{
- unsigned retry_counter;
struct SignedHeader *me, *other;
- retry_counter = GREG32(PMU, LONG_LIFE_SCRATCH0);
- system_clear_retry_counter();
-
- ccprintf("%s:retry counter %d\n", __func__, retry_counter);
-
- if (retry_counter <= RW_BOOT_MAX_RETRY_COUNT)
- return EC_SUCCESS;
-
if (system_get_image_copy() == SYSTEM_IMAGE_RW) {
me = (struct SignedHeader *)
get_program_memory_addr(SYSTEM_IMAGE_RW);
@@ -360,17 +504,50 @@ int system_process_retry_counter(void)
get_program_memory_addr(SYSTEM_IMAGE_RW);
}
- if (a_is_newer_than_b(me, other)) {
+ if (a_is_newer_than_b(me, other))
+ return 1;
+
+ if (newer_image)
+ *newer_image = other;
+ return 0;
+}
+
+int system_rollback_detected(void)
+{
+ return !current_image_is_newer(NULL);
+}
+
+int system_process_retry_counter(void)
+{
+ unsigned retry_counter;
+ struct SignedHeader *newer_image;
+
+ retry_counter = GREG32(PMU, LONG_LIFE_SCRATCH0);
+ system_clear_retry_counter();
+
+ ccprintf("%s:retry counter %d\n", __func__, retry_counter);
+
+ if (retry_counter <= RW_BOOT_MAX_RETRY_COUNT)
+ return EC_SUCCESS;
+
+ if (current_image_is_newer(&newer_image)) {
ccprintf("%s: "
"this is odd, I am newer, but retry counter was %d\n",
__func__, retry_counter);
return EC_SUCCESS;
}
/*
- * let's corrupt the "other" guy so that the next restart is happening
- * straight into this version.
+ * let's corrupt the newer image so that the next restart is happening
+ * straight into the current version.
*/
- return corrupt_other_header(other);
+ return corrupt_header(newer_image);
+}
+
+void system_ensure_rollback(void)
+{
+ GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG0, 1);
+ GREG32(PMU, LONG_LIFE_SCRATCH0) = RW_BOOT_MAX_RETRY_COUNT + 1;
+ GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG0, 0);
}
int system_rolling_reboot_suspected(void)
@@ -390,16 +567,6 @@ int system_rolling_reboot_suspected(void)
}
#endif
-uint32_t system_get_board_properties(void)
-{
- uint32_t properties = 0;
-
-#ifdef BOARD_CR50
- properties = system_board_properties_callback();
-#endif
- return properties;
-}
-
/* Prepend header version to the current image's build info. */
const char *system_get_build_info(void)
{
@@ -417,3 +584,201 @@ const char *system_get_build_info(void)
return combined_build_info;
}
+
+/**
+ * Modify info1 RW rollback mask to match the passed in header(s).
+ *
+ * If both headers' addressses are passed in, the INFO1 rollback mask field is
+ * erased in case both headers have a zero in the appropriate bit. If only one
+ * header address is passed (the other one is set to zero), only the valid
+ * header is considered when updating INFO1.
+ */
+static void update_rollback_mask(const struct SignedHeader *header_a,
+ const struct SignedHeader *header_b)
+{
+#ifndef CR50_DEV
+ int updated_words_count = 0;
+ int i;
+ int write_enabled = 0;
+ uint32_t header_mask = 0;
+
+ /*
+ * Make sure INFO1 RW map space is readable.
+ */
+ if (flash_info_read_enable(INFO_RW_MAP_OFFSET, INFO_RW_MAP_SIZE) !=
+ EC_SUCCESS) {
+ ccprintf("%s: failed to enable read access to info\n",
+ __func__);
+ return;
+ }
+
+ /*
+ * The infomap field in the image header has a matching space in the
+ * flash INFO1 section.
+ *
+ * The INFO1 space words which map into zeroed bits in the infomap
+ * header are ignored by the RO.
+ *
+ * Let's make sure that those words in the INFO1 space are erased.
+ * This in turn makes sure that attempts to load earlier RW images
+ * (where those bits in the header are not zeroed) will fail, thus
+ * ensuring rollback protection.
+ */
+ /* For each bit in the header infomap field of the running image. */
+ for (i = 0; i < INFO_MAX; i++) {
+ uint32_t bit;
+ uint32_t word;
+ int byte_offset;
+
+ /* Read the next infomap word when done with the current one. */
+ if (!(i % 32)) {
+ /*
+ * Not to shoot ourselves in the foot, let's zero only
+ * those words in the INFO1 space which are set to
+ * zero in all headers we are supposed to look at.
+ */
+ header_mask = 0;
+
+ if (header_a)
+ header_mask |= header_a->infomap[i/32];
+
+ if (header_b)
+ header_mask |= header_b->infomap[i/32];
+ }
+
+ /* Get the next bit value. */
+ bit = !!(header_mask & (1 << (i % 32)));
+ if (bit) {
+ /*
+ * By convention zeroed bits are expected to be
+ * adjacent at the LSB of the info mask field. Stop as
+ * soon as a non-zeroed bit is encountered.
+ */
+ ccprintf("%s: bailing out at bit %d\n", __func__, i);
+ break;
+ }
+
+ byte_offset = (INFO_MAX + i) * sizeof(uint32_t);
+
+ if (flash_physical_info_read_word(byte_offset, &word) !=
+ EC_SUCCESS) {
+ ccprintf("failed to read info mask word %d\n", i);
+ continue;
+ }
+
+ if (!word)
+ continue; /* This word has been zeroed already. */
+
+ if (!write_enabled) {
+ if (flash_info_write_enable(
+ INFO_RW_MAP_OFFSET,
+ INFO_RW_MAP_SIZE) != EC_SUCCESS) {
+ ccprintf("%s: failed to enable write access to"
+ " info\n", __func__);
+ return;
+ }
+ write_enabled = 1;
+ }
+
+ word = 0;
+ if (flash_info_physical_write(byte_offset,
+ sizeof(word),
+ (const char *) &word) !=
+ EC_SUCCESS) {
+ ccprintf("failed to write info mask word %d\n", i);
+ continue;
+ }
+ updated_words_count++;
+
+ }
+ if (!write_enabled)
+ return;
+
+ flash_info_write_disable();
+ ccprintf("updated %d info map words\n", updated_words_count);
+#endif /* CR50_DEV ^^^^^^^^ NOT defined. */
+}
+
+void system_update_rollback_mask_with_active_img(void)
+{
+ update_rollback_mask((const struct SignedHeader *)
+ get_program_memory_addr(system_get_image_copy()),
+ 0);
+}
+
+void system_update_rollback_mask_with_both_imgs(void)
+{
+ update_rollback_mask((const struct SignedHeader *)
+ get_program_memory_addr(SYSTEM_IMAGE_RW),
+ (const struct SignedHeader *)
+ get_program_memory_addr(SYSTEM_IMAGE_RW_B));
+}
+
+void system_get_rollback_bits(char *value, size_t value_size)
+{
+ int info_count;
+ int i;
+ struct {
+ int count;
+ const struct SignedHeader *h;
+ } headers[] = {
+ {.h = (const struct SignedHeader *)
+ get_program_memory_addr(SYSTEM_IMAGE_RW)},
+
+ {.h = (const struct SignedHeader *)
+ get_program_memory_addr(SYSTEM_IMAGE_RW_B)},
+ };
+
+ flash_info_read_enable(INFO_RW_MAP_OFFSET, INFO_RW_MAP_SIZE);
+ for (i = 0; i < INFO_MAX; i++) {
+ uint32_t w;
+
+ flash_physical_info_read_word(INFO_RW_MAP_OFFSET +
+ i * sizeof(uint32_t),
+ &w);
+ if (w)
+ break;
+ }
+ info_count = i;
+
+ for (i = 0; i < ARRAY_SIZE(headers); i++) {
+ int j;
+
+ for (j = 0; j < INFO_MAX; j++)
+ if (headers[i].h->infomap[j/32] & (1 << (j%32)))
+ break;
+ headers[i].count = j;
+ }
+
+ snprintf(value, value_size, "%d/%d/%d", info_count,
+ headers[0].count, headers[1].count);
+}
+
+#ifdef CONFIG_EXTENDED_VERSION_INFO
+
+void system_print_extended_version_info(void)
+{
+ int i;
+ struct board_id bid;
+ enum system_image_copy_t rw_images[] = {
+ SYSTEM_IMAGE_RW, SYSTEM_IMAGE_RW_B
+ };
+
+ if (read_board_id(&bid) != EC_SUCCESS) {
+ ccprintf("Board ID read failure!\n");
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(rw_images); i++) {
+ struct SignedHeader *ss = (struct SignedHeader *)
+ get_program_memory_addr(rw_images[i]);
+
+ ccprintf("BID %c: %08x:%08x:%08x %s\n", 'A' + i,
+ ss->board_id_type ^ SIGNED_HEADER_PADDING,
+ ss->board_id_type_mask ^ SIGNED_HEADER_PADDING,
+ ss->board_id_flags ^ SIGNED_HEADER_PADDING,
+ check_board_id_vs_header(&bid, ss) ? " No" : "Yes");
+ }
+}
+
+#endif /* CONFIG_EXTENDED_VERSION_INFO */
diff --git a/chip/g/system_chip.h b/chip/g/system_chip.h
new file mode 100644
index 0000000000..b5b9f4cbea
--- /dev/null
+++ b/chip/g/system_chip.h
@@ -0,0 +1,93 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* chip/g-specific system function prototypes */
+
+#ifndef __CROS_EC_SYSTEM_CHIP_H
+#define __CROS_EC_SYSTEM_CHIP_H
+
+/**
+ * On systems with protection from a failing RW update: read the retry counter
+ * and act on it.
+ *
+ * @return EC_SUCCESS if no flash write errors were encounterd.
+ */
+int system_process_retry_counter(void);
+
+/**
+ * On systems with protection from a failing RW update: reset retry
+ * counter, this is used after a new image upload is finished, to make
+ * sure that the new image has a chance to run.
+ */
+void system_clear_retry_counter(void);
+
+/**
+ * A function provided by some platforms to decrement a retry counter.
+ *
+ * This should be used whenever a system reset is manually triggered.
+ */
+void system_decrement_retry_counter(void);
+
+/**
+ * A function provided by some platforms to hint that something is going
+ * wrong.
+ *
+ * @return a boolean, set to True if rolling reboot condition is suspected.
+ */
+int system_rolling_reboot_suspected(void);
+
+/**
+ * Compare the rw headers to check if there was a rollback.
+ *
+ * @return a boolean, set to True if a rollback is detected.
+ */
+int system_rollback_detected(void);
+
+/**
+ * Returns non-zero value when firmware is expected to be abe to detect user
+ * request to cut off battery supply.
+ */
+int system_battery_cutoff_support_required(void);
+
+/**
+ * Functions to update INFO1 rollback mask based on one or both RW image
+ * headers.
+ */
+void system_update_rollback_mask_with_active_img(void);
+void system_update_rollback_mask_with_both_imgs(void);
+
+/**
+ * Scan INFO1 rollback map and infomap fields of both RW and RW_B image
+ * headers, and return a string showing how many zeros are there at the base
+ * of in each of these objects.
+ *
+ * The passed in parameters are the memory area to put the string in and the
+ * size of this memory area.
+ */
+void system_get_rollback_bits(char *value, size_t value_size);
+
+/**
+ * Set the rollback counter to a value which would ensure a rollback on the
+ * next boot.
+ */
+void system_ensure_rollback(void);
+
+/**
+ * Enables holding external pins across soft chip resets. Application firmware
+ * is responsible for disengaging pinhold upon reset.
+ */
+void system_pinhold_on_reset_enable(void);
+
+/**
+ * Disables holding external pins across soft chip resets.
+ */
+void system_pinhold_on_reset_disable(void);
+
+/**
+ * Disengages pinhold if engaged.
+ */
+void system_pinhold_disengage(void);
+
+#endif /* __CROS_EC_SYSTEM_CHIP_H */
diff --git a/chip/g/trng.c b/chip/g/trng.c
index a7dbcf4455..f715a1f833 100644
--- a/chip/g/trng.c
+++ b/chip/g/trng.c
@@ -3,26 +3,42 @@
* found in the LICENSE file.
*/
+#include "init_chip.h"
#include "registers.h"
+#include "trng.h"
void init_trng(void)
{
+#if (!(defined(CONFIG_CUSTOMIZED_RO) && defined(SECTION_IS_RO)))
+ /*
+ * Most of the trng initialization requires high permissions. If RO has
+ * dropped the permission level, dont try to read or write these high
+ * permission registers because it will cause rolling reboots. RO
+ * should do the TRNG initialization before dropping the level.
+ */
+ if (!runlevel_is_high())
+ return;
+#endif
+
+ GWRITE(TRNG, POST_PROCESSING_CTRL,
+ GC_TRNG_POST_PROCESSING_CTRL_SHUFFLE_BITS_MASK |
+ GC_TRNG_POST_PROCESSING_CTRL_CHURN_MODE_MASK);
+ GWRITE(TRNG, SLICE_MAX_UPPER_LIMIT, 1);
+ GWRITE(TRNG, SLICE_MIN_LOWER_LIMIT, 0);
+ GWRITE(TRNG, TIMEOUT_COUNTER, 0x7ff);
+ GWRITE(TRNG, TIMEOUT_MAX_TRY_NUM, 4);
GWRITE(TRNG, POWER_DOWN_B, 1);
GWRITE(TRNG, GO_EVENT, 1);
- while (GREAD(TRNG, EMPTY))
- ;
- GREAD(TRNG, READ_DATA);
}
uint32_t rand(void)
{
while (GREAD(TRNG, EMPTY)) {
- if (!GREAD_FIELD(TRNG, FSM_STATE, FSM_IDLE))
- continue;
-
- /* TRNG must have stopped, needs to be restarted. */
- GWRITE(TRNG, STOP_WORK, 1);
- init_trng();
+ if (GREAD_FIELD(TRNG, FSM_STATE, FSM_TIMEOUT)) {
+ /* TRNG timed out, restart */
+ GWRITE(TRNG, STOP_WORK, 1);
+ GWRITE(TRNG, GO_EVENT, 1);
+ }
}
return GREAD(TRNG, READ_DATA);
}
diff --git a/chip/g/uart_bitbang.c b/chip/g/uart_bitbang.c
new file mode 100644
index 0000000000..6d38626b68
--- /dev/null
+++ b/chip/g/uart_bitbang.c
@@ -0,0 +1,541 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "common.h"
+#include "compile_time_macros.h"
+#include "console.h"
+#include "gpio.h"
+#include "pmu.h"
+#include "registers.h"
+#include "task.h"
+#include "timer.h"
+#include "uart_bitbang.h"
+#include "uartn.h"
+
+#define CPRINTF(format, args...) cprintf(CC_SYSTEM, format, ##args)
+#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ##args)
+
+#define BITBANG_DEBUG 0 /* Set to 1 to enable debug counters and logs. */
+
+/* Support the "standard" baud rates. */
+#define IS_BAUD_RATE_SUPPORTED(rate) \
+ ((rate == 1200) || (rate == 2400) || (rate == 4800) || (rate == 9600) \
+ || (rate == 19200) || (rate == 38400) || (rate == 57600) || \
+ (rate == 115200))
+
+#define RX_BUF_SIZE 8
+#define BUF_NEXT(idx) ((idx+1) % RX_BUF_SIZE)
+
+#define TIMEUS_CLK_FREQ 24 /* units: MHz */
+
+/* Flag indicating whether bit banging is enabled or not. */
+static uint8_t bitbang_enabled;
+/* Flag indicating bit banging is desired. Allows async enable/disable. */
+static uint8_t bitbang_wanted;
+
+static int rx_buf[RX_BUF_SIZE];
+
+/* Current bitbang context */
+static int tx_pin;
+static int rx_pin;
+static uint32_t bit_period_ticks;
+static uint8_t set_parity;
+
+#if BITBANG_DEBUG
+/* debug counters and log */
+#define DISCARD_LOG 8
+static int read_char_cnt;
+static int rx_buff_inserted_cnt;
+static int rx_buff_rx_char_cnt;
+static int stop_bit_err_cnt;
+static int parity_err_cnt;
+static int parity_err_discard[DISCARD_LOG];
+static int parity_discard_idx;
+static int stop_bit_discard[DISCARD_LOG];
+static int stop_bit_discard_idx;
+#endif /* BITBANG_DEBUG */
+
+static int is_uart_allowed(int uart)
+{
+ return uart == bitbang_config.uart;
+}
+
+int uart_bitbang_is_enabled(int uart)
+{
+ return (is_uart_allowed(uart) && bitbang_enabled);
+}
+
+int uart_bitbang_is_wanted(int uart)
+{
+ return (is_uart_allowed(uart) && bitbang_wanted);
+}
+
+int uart_bitbang_config(int uart, int baud_rate, int parity)
+{
+ /* Can't configure when enabled */
+ if (bitbang_enabled)
+ return EC_ERROR_BUSY;
+
+ if (!is_uart_allowed(uart)) {
+ CPRINTF("bit bang config not found for UART%d\n", uart);
+ return EC_ERROR_INVAL;
+ }
+
+ /* Check desired properties. */
+ if (!IS_BAUD_RATE_SUPPORTED(baud_rate)) {
+ CPRINTF("Err: invalid baud rate (%d)\n", baud_rate);
+ return EC_ERROR_INVAL;
+ }
+ bitbang_config.baud_rate = baud_rate;
+
+ switch (parity) {
+ case 0:
+ case 1:
+ case 2:
+ break;
+
+ default:
+ CPRINTF("Err: invalid parity '%d'. (0:N, 1:O, 2:E)\n", parity);
+ return EC_ERROR_INVAL;
+ };
+ bitbang_config.htp.parity = parity;
+
+ return EC_SUCCESS;
+}
+
+int uart_bitbang_enable(int uart)
+{
+ /* We only want to bit bang 1 UART at a time */
+ if (bitbang_enabled)
+ return EC_ERROR_BUSY;
+
+ /* UART TX must be disconnected first */
+ if (uart_tx_is_connected(uart))
+ return EC_ERROR_BUSY;
+
+ if (!is_uart_allowed(uart)) {
+ CPRINTS("bit bang config not found for UART%d", uart);
+ return EC_ERROR_INVAL;
+ }
+
+ /* Select the GPIOs instead of the UART block */
+ REG32(bitbang_config.tx_pinmux_reg) =
+ bitbang_config.tx_pinmux_regval;
+ gpio_set_flags(bitbang_config.tx_gpio, GPIO_OUT_HIGH);
+ REG32(bitbang_config.rx_pinmux_reg) =
+ bitbang_config.rx_pinmux_regval;
+ gpio_set_flags(bitbang_config.rx_gpio, GPIO_INPUT);
+
+ /*
+ * Ungate the microsecond timer so that we can use it. This is needed
+ * for accurate framing if using faster baud rates.
+ */
+ pmu_clock_en(PERIPH_TIMEUS);
+ GR_TIMEUS_EN(0) = 0;
+ GR_TIMEUS_MAXVAL(0) = 0xFFFFFFFF;
+ GR_TIMEUS_EN(0) = 1;
+
+ /* Save context information. */
+ tx_pin = bitbang_config.tx_gpio;
+ rx_pin = bitbang_config.rx_gpio;
+ bit_period_ticks = TIMEUS_CLK_FREQ *
+ ((1 * SECOND) / bitbang_config.baud_rate);
+ set_parity = bitbang_config.htp.parity;
+
+ /* Register the function pointers. */
+ uartn_funcs[uart]._rx_available = _uart_bitbang_rx_available;
+ uartn_funcs[uart]._write_char = _uart_bitbang_write_char;
+ uartn_funcs[uart]._read_char = _uart_bitbang_read_char;
+
+ bitbang_enabled = 1;
+ gpio_enable_interrupt(bitbang_config.rx_gpio);
+ CPRINTS("Bit bang enabled");
+ return EC_SUCCESS;
+}
+
+int uart_bitbang_disable(int uart)
+{
+ if (!uart_bitbang_is_enabled(uart))
+ return EC_SUCCESS;
+
+ /*
+ * This is safe because if the UART was not specified in the config, we
+ * would have already returned.
+ */
+ bitbang_enabled = 0;
+ gpio_reset(bitbang_config.tx_gpio);
+ gpio_reset(bitbang_config.rx_gpio);
+
+ /* Unregister the function pointers. */
+ uartn_funcs[uart]._rx_available = _uartn_rx_available;
+ uartn_funcs[uart]._write_char = _uartn_write_char;
+ uartn_funcs[uart]._read_char = _uartn_read_char;
+
+ /* Gate the microsecond timer since we're done with it. */
+ pmu_clock_dis(PERIPH_TIMEUS);
+
+ /* Don't need to watch RX */
+ gpio_disable_interrupt(bitbang_config.rx_gpio);
+ CPRINTS("Bit bang disabled");
+ return EC_SUCCESS;
+}
+
+static void wait_ticks(uint32_t ticks)
+{
+ uint32_t t0 = GR_TIMEUS_CUR_MAJOR(0);
+
+ while ((GR_TIMEUS_CUR_MAJOR(0) - t0) < ticks)
+ ;
+}
+
+void uart_bitbang_write_char(int uart, char c)
+{
+ int val;
+ int ones;
+ int i;
+
+ if (!uart_bitbang_is_enabled(uart))
+ return;
+
+ interrupt_disable();
+
+ /* Start bit. */
+ gpio_set_level(tx_pin, 0);
+ wait_ticks(bit_period_ticks);
+
+ /* 8 data bits. */
+ ones = 0;
+ for (i = 0; i < 8; i++) {
+ val = (c & (1 << i));
+ /* Count 1's in order to handle parity bit. */
+ if (val)
+ ones++;
+ gpio_set_level(tx_pin, val);
+ wait_ticks(bit_period_ticks);
+ }
+
+ /* Optional parity. */
+ switch (set_parity) {
+ case 1: /* odd parity */
+ if (ones & 0x1)
+ gpio_set_level(tx_pin, 0);
+ else
+ gpio_set_level(tx_pin, 1);
+ wait_ticks(bit_period_ticks);
+ break;
+
+ case 2: /* even parity */
+ if (ones & 0x1)
+ gpio_set_level(tx_pin, 1);
+ else
+ gpio_set_level(tx_pin, 0);
+ wait_ticks(bit_period_ticks);
+ break;
+
+ case 0: /* no parity */
+ default:
+ break;
+ };
+
+ /* 1 stop bit. */
+ gpio_set_level(tx_pin, 1);
+ wait_ticks(bit_period_ticks);
+ interrupt_enable();
+}
+
+int uart_bitbang_receive_char(int uart)
+{
+ uint8_t rx_char;
+ int i;
+ int rv;
+ int ones;
+ int parity_bit;
+ int stop_bit;
+ uint8_t head;
+ uint8_t tail;
+
+ /* Disable interrupts so that we aren't interrupted. */
+ interrupt_disable();
+#if BITBANG_DEBUG
+ rx_buff_rx_char_cnt++;
+#endif /* BITBANG_DEBUG */
+ rv = EC_SUCCESS;
+
+ rx_char = 0;
+
+ /* Wait 1 bit period for the start bit. */
+ wait_ticks(bit_period_ticks);
+
+ /* 8 data bits. */
+ ones = 0;
+ for (i = 0; i < 8; i++) {
+ if (gpio_get_level(rx_pin)) {
+ ones++;
+ rx_char |= (1 << i);
+ }
+ wait_ticks(bit_period_ticks);
+ }
+
+ /* optional parity or stop bit. */
+ parity_bit = gpio_get_level(rx_pin);
+ if (set_parity) {
+ wait_ticks(bit_period_ticks);
+ stop_bit = gpio_get_level(rx_pin);
+ } else {
+ /* If there's no parity, that _was_ the stop bit. */
+ stop_bit = parity_bit;
+ }
+
+ /* Check the parity if necessary. */
+ switch (set_parity) {
+ case 2: /* even parity */
+ if (ones & 0x1)
+ rv = parity_bit ? EC_SUCCESS : EC_ERROR_CRC;
+ else
+ rv = parity_bit ? EC_ERROR_CRC : EC_SUCCESS;
+ break;
+
+ case 1: /* odd parity */
+ if (ones & 0x1)
+ rv = parity_bit ? EC_ERROR_CRC : EC_SUCCESS;
+ else
+ rv = parity_bit ? EC_SUCCESS : EC_ERROR_CRC;
+ break;
+
+ case 0:
+ default:
+ break;
+ }
+
+#if BITBANG_DEBUG
+ if (rv != EC_SUCCESS) {
+ parity_err_cnt++;
+ parity_err_discard[parity_discard_idx] = rx_char;
+ parity_discard_idx = (parity_discard_idx + 1) % DISCARD_LOG;
+ }
+#endif /* BITBANG_DEBUG */
+
+ /* Check that the stop bit is valid. */
+ if (stop_bit != 1) {
+ rv = EC_ERROR_CRC;
+#if BITBANG_DEBUG
+ stop_bit_err_cnt++;
+ stop_bit_discard[stop_bit_discard_idx] = rx_char;
+ stop_bit_discard_idx = (stop_bit_discard_idx + 1) % DISCARD_LOG;
+#endif /* BITBANG_DEBUG */
+ }
+
+ if (rv != EC_SUCCESS) {
+ interrupt_enable();
+ return rv;
+ }
+
+ /* Place the received char in the RX buffer. */
+ head = bitbang_config.htp.head;
+ tail = bitbang_config.htp.tail;
+ if (BUF_NEXT(tail) != head) {
+ rx_buf[tail] = rx_char;
+ bitbang_config.htp.tail = BUF_NEXT(tail);
+#if BITBANG_DEBUG
+ rx_buff_inserted_cnt++;
+#endif /* BITBANG_DEBUG */
+ }
+
+ interrupt_enable();
+ return EC_SUCCESS;
+}
+
+int uart_bitbang_read_char(int uart)
+{
+ int c;
+ uint8_t head;
+
+ if (!is_uart_allowed(uart))
+ return 0;
+
+ head = bitbang_config.htp.head;
+ c = rx_buf[head];
+
+ if (head != bitbang_config.htp.tail)
+ bitbang_config.htp.head = BUF_NEXT(head);
+
+#if BITBANG_DEBUG
+ read_char_cnt++;
+#endif /* BITBANG_DEBUG */
+ return c;
+}
+
+int uart_bitbang_is_char_available(int uart)
+{
+ if (!is_uart_allowed(uart))
+ return 0;
+
+ return bitbang_config.htp.head != bitbang_config.htp.tail;
+}
+
+#if BITBANG_DEBUG
+static int write_test_pattern(int uart, int pattern_idx)
+{
+ if (!uart_bitbang_is_enabled(uart)) {
+ ccprintf("bit banging mode not enabled for UART%d\n", uart);
+ return EC_ERROR_INVAL;
+ }
+
+ switch (pattern_idx) {
+ case 0:
+ uartn_write_char(uart, 'a');
+ uartn_write_char(uart, 'b');
+ uartn_write_char(uart, 'c');
+ uartn_write_char(uart, '\n');
+ ccprintf("wrote: 'abc\\n'\n");
+ break;
+
+ case 1:
+ uartn_write_char(uart, 0xAA);
+ uartn_write_char(uart, 0xCC);
+ uartn_write_char(uart, 0x55);
+ ccprintf("wrote: '0xAA 0xCC 0x55'\n");
+ break;
+
+ default:
+ ccprintf("unknown test pattern\n");
+ return EC_ERROR_INVAL;
+ };
+
+ return EC_SUCCESS;
+}
+#endif /* BITBANG_DEBUG */
+
+static int command_bitbang(int argc, char **argv)
+{
+ int uart;
+ int baud_rate;
+ int parity;
+ int rv;
+
+ if (argc > 1) {
+ uart = atoi(argv[1]);
+ if (argc == 3) {
+ if (!strcasecmp("disable", argv[2])) {
+ bitbang_wanted = 0;
+ ccd_update_state();
+ return EC_SUCCESS;
+ }
+ return EC_ERROR_PARAM2;
+ }
+
+ if (argc == 4) {
+#if BITBANG_DEBUG
+ if (!strncasecmp("test", argv[2], 4))
+ return write_test_pattern(uart, atoi(argv[3]));
+#endif /* BITBANG_DEBUG */
+
+ baud_rate = atoi(argv[2]);
+ if (!strcasecmp("odd", argv[3]))
+ parity = 1;
+ else if (!strcasecmp("even", argv[3]))
+ parity = 2;
+ else if (!strcasecmp("none", argv[3]))
+ parity = 0;
+ else
+ return EC_ERROR_PARAM3;
+
+ rv = uart_bitbang_config(uart, baud_rate, parity);
+ if (rv)
+ return rv;
+
+ if (servo_is_connected())
+ ccprintf("Bit banging superseded by servo\n");
+
+ bitbang_wanted = 1;
+ ccd_update_state();
+ return EC_SUCCESS;
+ }
+
+ return EC_ERROR_PARAM_COUNT;
+ }
+
+ if (!uart_bitbang_is_enabled(bitbang_config.uart)) {
+ ccprintf("bit banging mode disabled.\n");
+ } else {
+ ccprintf("baud rate - parity\n");
+ ccprintf(" %6d ", bitbang_config.baud_rate);
+ switch (bitbang_config.htp.parity) {
+ case 1:
+ ccprintf("odd\n");
+ break;
+
+ case 2:
+ ccprintf("even\n");
+ break;
+
+ case 0:
+ default:
+ ccprintf("none\n");
+ break;
+ };
+ }
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(bitbang, command_bitbang,
+ "<uart> <baud_rate> <odd,even,none> | <uart> disable "
+#if BITBANG_DEBUG
+ "| <uart> test <0, 1>"
+#endif /* BITBANG_DEBUG */
+ , "set bit bang mode");
+
+#if BITBANG_DEBUG
+static int command_bitbang_dump_stats(int argc, char **argv)
+{
+ int i;
+
+ if (argc == 2) {
+ /* Clear the counters. */
+ if (!strncasecmp(argv[1], "clear", 5)) {
+ parity_err_cnt = 0;
+ stop_bit_err_cnt = 0;
+ rx_buff_rx_char_cnt = 0;
+ read_char_cnt = 0;
+ rx_buff_inserted_cnt = 0;
+ return EC_SUCCESS;
+ }
+ return EC_ERROR_PARAM1;
+ }
+
+ ccprintf("Errors:\n");
+ ccprintf("%d Parity Errors\n", parity_err_cnt);
+ ccprintf("%d Stop Bit Errors\n", stop_bit_err_cnt);
+ ccprintf("Buffer info\n");
+ ccprintf("%d received\n", rx_buff_rx_char_cnt);
+ ccprintf("%d chars inserted\n", rx_buff_inserted_cnt);
+ ccprintf("%d chars read\n", read_char_cnt);
+ ccprintf("Contents\n");
+ ccprintf("[");
+ for (i = 0; i < RX_BUF_SIZE; i++)
+ ccprintf(" %02x ", rx_buf[i] & 0xFF);
+ ccprintf("]\n");
+ ccprintf("head: %d\ntail: %d\n",
+ bitbang_config.htp.head,
+ bitbang_config.htp.tail);
+ ccprintf("Discards\nparity: ");
+ ccprintf("[");
+ for (i = 0; i < DISCARD_LOG; i++)
+ ccprintf(" %02x ", parity_err_discard[i] & 0xFF);
+ ccprintf("]\n");
+ ccprintf("idx: %d\n", parity_discard_idx);
+ ccprintf("stop bit: ");
+ ccprintf("[");
+ for (i = 0; i < DISCARD_LOG; i++)
+ ccprintf(" %02x ", stop_bit_discard[i] & 0xFF);
+ ccprintf("]\n");
+ ccprintf("idx: %d\n", stop_bit_discard_idx);
+ cflush();
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(bbstats, command_bitbang_dump_stats,
+ "",
+ "dumps bitbang stats");
+#endif /* BITBANG_DEBUG */
diff --git a/chip/g/uart_bitbang.h b/chip/g/uart_bitbang.h
new file mode 100644
index 0000000000..7a8a33e923
--- /dev/null
+++ b/chip/g/uart_bitbang.h
@@ -0,0 +1,130 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __CROS_EC_CHIP_G_UART_BITBANG_H
+#define __CROS_EC_CHIP_G_UART_BITBANG_H
+
+/* UART Bit Banging */
+
+#include "common.h"
+#include "gpio.h"
+
+/* These are functions that we'll have to replace. */
+struct uartn_function_ptrs {
+ int (*_rx_available)(int uart);
+ void (*_write_char)(int uart, char c);
+ int (*_read_char)(int uart);
+};
+
+/*
+ * And these are the function definitions. The functions live in
+ * chip/g/uartn.c.
+ */
+extern int _uartn_rx_available(int uart);
+extern void _uartn_write_char(int uart, char c);
+extern int _uartn_read_char(int uart);
+extern int _uart_bitbang_rx_available(int uart);
+extern void _uart_bitbang_write_char(int uart, char c);
+extern int _uart_bitbang_read_char(int uart);
+extern struct uartn_function_ptrs uartn_funcs[];
+
+struct uart_bitbang_properties {
+ enum gpio_signal tx_gpio;
+ enum gpio_signal rx_gpio;
+ uint32_t tx_pinmux_reg;
+ uint32_t tx_pinmux_regval;
+ uint32_t rx_pinmux_reg;
+ uint32_t rx_pinmux_regval;
+ int baud_rate;
+ uint8_t uart;
+ struct {
+ unsigned int head : 3;
+ unsigned int tail : 3;
+ unsigned int parity : 2;
+ } htp __packed;
+};
+
+/* In order to bitbang a UART, a board must define a bitbang_config. */
+extern struct uart_bitbang_properties bitbang_config;
+
+/**
+ * Configure bit banging mode for a UART.
+ *
+ * If configuration succeeds, then call uart_bitbang_enable() on the port.
+ *
+ * @param uart: Index of UART to enable bit banging mode.
+ * @param baud_rate: desired baud rate.
+ * @param parity: 0: no parity, 1: odd parity, 2: even parity.
+ *
+ * @returns EC_SUCCESS on success, otherwise an error.
+ */
+int uart_bitbang_config(int uart, int baud_rate, int parity);
+
+/**
+ * Enable bit banging mode for a UART.
+ *
+ * The UART must have been configured first.
+ *
+ * @param uart: Index of UART to disable bit banging mode.
+ */
+int uart_bitbang_enable(int uart);
+
+/**
+ * Disable bit banging mode for a UART.
+ *
+ * @param uart: Index of UART to disable bit banging mode.
+ */
+int uart_bitbang_disable(int uart);
+
+/**
+ * Returns 1 if bit banging mode is enabled for the UART.
+ *
+ * @param uart: Index of UART to query.
+ */
+int uart_bitbang_is_enabled(int uart);
+
+/**
+ * Returns 1 if bit banging mode is wanted for the UART.
+ *
+ * @param uart: Index of UART to query.
+ */
+int uart_bitbang_is_wanted(int uart);
+
+/**
+ * TX a character on a UART configured for bit banging mode.
+ *
+ * @param uart: Index of UART to use.
+ * @param c: Character to send out.
+ */
+void uart_bitbang_write_char(int uart, char c);
+
+/**
+ * Sample the RX line on a UART configured for bit banging mode.
+ *
+ * This is called when a falling edge is seen on the RX line and will attempt to
+ * receive a character. Incoming data with framing errors or parity errors will
+ * be discarded.
+ *
+ * @param uart: Index of UART to use.
+ * @returns EC_SUCCESS if a character was successfully received, EC_ERROR_CRC if
+ * there was a framing or parity issue.
+ */
+int uart_bitbang_receive_char(int uart);
+
+/**
+ * Returns 1 if there are characters available for consumption, otherwise 0.
+ *
+ * @param uart: Index of UART to check.
+ */
+int uart_bitbang_is_char_available(int uart);
+
+/**
+ * Retrieve a character from the bit bang RX buffer.
+ *
+ * @param uart: Index of UART to use.
+ */
+int uart_bitbang_read_char(int uart);
+
+#endif /* __CROS_EC_CHIP_G_UART_BITBANG_H */
diff --git a/chip/g/uartn.c b/chip/g/uartn.c
index c534705eb3..b9d08d4375 100644
--- a/chip/g/uartn.c
+++ b/chip/g/uartn.c
@@ -10,6 +10,7 @@
#include "system.h"
#include "task.h"
#include "uart.h"
+#include "uart_bitbang.h"
#include "util.h"
#define USE_UART_INTERRUPTS (!(defined(CONFIG_CUSTOMIZED_RO) && \
@@ -25,6 +26,26 @@ static struct uartn_interrupts interrupt[] = {
{GC_IRQNUM_UART2_TXINT, GC_IRQNUM_UART2_RXINT},
};
+struct uartn_function_ptrs uartn_funcs[3] = {
+ {
+ _uartn_rx_available,
+ _uartn_write_char,
+ _uartn_read_char,
+ },
+
+ {
+ _uartn_rx_available,
+ _uartn_write_char,
+ _uartn_read_char,
+ },
+
+ {
+ _uartn_rx_available,
+ _uartn_write_char,
+ _uartn_read_char,
+ },
+};
+
void uartn_tx_start(int uart)
{
if (!uart_init_done())
@@ -68,9 +89,22 @@ int uartn_tx_in_progress(int uart)
void uartn_tx_flush(int uart)
{
+ timestamp_t ts;
+ int i;
+
/* Wait until TX FIFO is idle. */
while (uartn_tx_in_progress(uart))
;
+ /*
+ * Even when uartn_tx_in_progress() returns false, the chip seems to
+ * be still trasmitting, resetting at this point results in an eaten
+ * last symbol. Let's just wait some time (required to transmit 10
+ * bits at 115200 baud).
+ */
+ ts = get_time(); /* Start time. */
+ for (i = 0; i < 1000; i++) /* Limit it in case timer is not running. */
+ if ((get_time().val - ts.val) > ((1000000 * 10) / 115200))
+ return;
}
int uartn_tx_ready(int uart)
@@ -79,13 +113,18 @@ int uartn_tx_ready(int uart)
return !(GR_UART_STATE(uart) & GC_UART_STATE_TX_MASK);
}
-int uartn_rx_available(int uart)
+int _uartn_rx_available(int uart)
{
/* True if the RX buffer is not completely empty. */
return !(GR_UART_STATE(uart) & GC_UART_STATE_RXEMPTY_MASK);
}
-void uartn_write_char(int uart, char c)
+int uartn_rx_available(int uart)
+{
+ return uartn_funcs[uart]._rx_available(uart);
+}
+
+void _uartn_write_char(int uart, char c)
{
/* Wait for space in transmit FIFO. */
while (!uartn_tx_ready(uart))
@@ -94,11 +133,45 @@ void uartn_write_char(int uart, char c)
GR_UART_WDATA(uart) = c;
}
-int uartn_read_char(int uart)
+void uartn_write_char(int uart, char c)
+{
+ uartn_funcs[uart]._write_char(uart, c);
+}
+
+int _uartn_read_char(int uart)
{
return GR_UART_RDATA(uart);
}
+int uartn_read_char(int uart)
+{
+ return uartn_funcs[uart]._read_char(uart);
+}
+
+#ifdef CONFIG_UART_BITBANG
+int _uart_bitbang_rx_available(int uart)
+{
+ if (uart_bitbang_is_enabled(uart))
+ return uart_bitbang_is_char_available(uart);
+
+ return 0;
+}
+
+void _uart_bitbang_write_char(int uart, char c)
+{
+ if (uart_bitbang_is_enabled(uart))
+ uart_bitbang_write_char(uart, c);
+}
+
+int _uart_bitbang_read_char(int uart)
+{
+ if (uart_bitbang_is_enabled(uart))
+ return uart_bitbang_read_char(uart);
+
+ return 0;
+}
+#endif /* defined(CONFIG_UART_BITBANG) */
+
void uartn_disable_interrupt(int uart)
{
task_disable_irq(interrupt[uart].tx_int);
@@ -112,19 +185,10 @@ void uartn_enable_interrupt(int uart)
}
-/* Enable TX and RX. Disable HW flow control and loopback */
void uartn_enable(int uart)
{
- /* Enable UART TX */
- GR_UART_CTRL(uart) = 0x01;
-
-/* TODO(crosbug.com/p/56540): Remove this when UART0_RX works everywhere */
-#if defined(BOARD_CR50) && !defined(SECTION_IS_RO)
- if (!uart && (system_get_board_properties() & BOARD_DISABLE_UART0_RX))
- return;
-#endif
-
- GR_UART_CTRL(uart) |= 0x02;
+ /* Enable TX and RX. Disable HW flow control and loopback. */
+ GR_UART_CTRL(uart) = 0x03;
}
/* Disable TX, RX, HW flow control, and loopback */
@@ -133,6 +197,11 @@ void uartn_disable(int uart)
GR_UART_CTRL(uart) = 0;
}
+int uartn_is_enabled(int uart)
+{
+ return !!(GR_UART_CTRL(uart) & 0x03);
+}
+
void uartn_init(int uart)
{
long long setting = (16 * (1 << UART_NCO_WIDTH) *
diff --git a/chip/g/uartn.h b/chip/g/uartn.h
index e13c2f6552..bfb7772518 100644
--- a/chip/g/uartn.h
+++ b/chip/g/uartn.h
@@ -74,18 +74,48 @@ void uartn_tx_start(int uart);
*/
void uartn_tx_stop(int uart);
-/* Get UART output status */
-int uartn_enabled(int uart);
+/**
+ * Return non-zero if TX is connected for the UART.
+ *
+ * @param uart UART to check
+ * @return 1 if TX pin is connected, 0 if disconnected.
+ */
+int uart_tx_is_connected(int uart);
-/* Enable UART output */
+/* Connect TX pin for the UART */
void uartn_tx_connect(int uart);
-/* Disable UART output */
+/* Disconnect TX pin for the UART */
void uartn_tx_disconnect(int uart);
-/* Enable TX and RX. Disable HW flow control and loopback */
+/**
+ * Return non-zero if TX and RX are enabled for the UART.
+ *
+ * Note that TX won't be connected unless uart_tx_is_connected() is also
+ * non-zero.
+ *
+ * @param uart UART to check
+ * @return 1 if UART is enabled, 0 if disabled.
+ */
+int uartn_is_enabled(int uart);
+
+/**
+ * Enable TX and RX for the UART. Disable HW flow control and loopback.
+ *
+ * @param uart UART to enable
+ *
+ * Note that this does NOT connect the TX pin for the UART; that must be done
+ * explicitly via uartn_tx_connect().
+ */
void uartn_enable(int uart);
-/* Disable TX, RX, HW flow control, and loopback */
+/**
+ * Disable TX, RX, HW flow control, and loopback.
+ *
+ * @param uart UART to disable
+ *
+ * Note that this does NOT disconnect the TX pin for the UART; that must be
+ * done explicitly via uartn_tx_disconnect().
+ */
void uartn_disable(int uart);
#endif /* __CROS_EC_UARTN_H */
diff --git a/chip/g/upgrade.c b/chip/g/upgrade.c
new file mode 100644
index 0000000000..c51faf401e
--- /dev/null
+++ b/chip/g/upgrade.c
@@ -0,0 +1,150 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "config.h"
+#include "console.h"
+#include "endian.h"
+#include "extension.h"
+#include "flash.h"
+#include "flash_info.h"
+#include "hooks.h"
+#include "signed_header.h"
+#include "system.h"
+#include "upgrade_fw.h"
+#include "util.h"
+
+#define CPRINTF(format, args...) cprintf(CC_EXTENSION, format, ## args)
+
+static void deferred_reboot(void)
+{
+ system_reset(SYSTEM_RESET_MANUALLY_TRIGGERED | SYSTEM_RESET_HARD);
+}
+DECLARE_DEFERRED(deferred_reboot);
+
+#define MAX_REBOOT_TIMEOUT_MS 1000
+
+/*
+ * Verify if the header at the passed in flash offset needs to be restored,
+ * and restore it if so. If this is an RO header - enable writing into that RO
+ * section (the currently active RO writes can not be enabled).
+ *
+ * Return true if a corruption was detected and restored.
+ */
+static int header_restored(uint32_t offset)
+{
+ struct SignedHeader *header;
+ uint32_t new_size;
+
+ header = (struct SignedHeader *)(CONFIG_PROGRAM_MEMORY_BASE + offset);
+
+ new_size = header->image_size;
+ if (!(new_size & TOP_IMAGE_SIZE_BIT))
+ return 0;
+
+ new_size &= ~TOP_IMAGE_SIZE_BIT;
+ /*
+ * Clear only in case the size is sensible (i.e. not set to all
+ * ones).
+ */
+ if (new_size > CONFIG_RW_SIZE)
+ return 0;
+
+ if ((offset == CONFIG_RO_MEM_OFF) || (offset == CHIP_RO_B_MEM_OFF))
+ flash_open_ro_window(offset, sizeof(struct SignedHeader));
+
+ return flash_physical_write(offset + offsetof(struct SignedHeader,
+ image_size),
+ sizeof(header->image_size),
+ (char *)&new_size) == EC_SUCCESS;
+}
+
+/*
+ * Try restoring inactive RO and RW headers, Return the number of restored
+ * headers.
+ */
+static uint8_t headers_restored(void)
+{
+ uint8_t total_restored;
+
+ /* Examine the RO first. */
+ if (system_get_ro_image_copy() == SYSTEM_IMAGE_RO)
+ total_restored = header_restored(CHIP_RO_B_MEM_OFF);
+ else
+ total_restored = header_restored(CONFIG_RO_MEM_OFF);
+
+ /* Now the RW */
+ if (system_get_image_copy() == SYSTEM_IMAGE_RW)
+ total_restored += header_restored(CONFIG_RW_B_MEM_OFF);
+ else
+ total_restored += header_restored(CONFIG_RW_MEM_OFF);
+
+ return total_restored;
+}
+
+/*
+ * The TURN_UPDATE_ON command comes with a single parameter, which is a 16 bit
+ * integer value of the number of milliseconds to wait before reboot in case
+ * there has been an update waiting.
+ *
+ * Maximum wait time is 1000 ms.
+ *
+ * If the requested timeout exceeds the allowed maximum, or the command is
+ * malformed, a protocol error is returned.
+ *
+ * If there was no errors, the number of restored headers is returned to the
+ * host in a single byte.
+ *
+ * If at least one header was restored AND the host supplied a nonzero timeout
+ * value, the H1 will be reset after this many milliseconds.
+ *
+ * Sending this command with the zero timeout value results in reporting to
+ * the host how many headers were restored, the reset is not triggered.
+ */
+static enum vendor_cmd_rc turn_update_on(enum vendor_cmd_cc code,
+ void *buf,
+ size_t input_size,
+ size_t *response_size)
+{
+ uint16_t timeout;
+ uint8_t *response;
+
+ /* Just in case. */
+ *response_size = 0;
+
+ if (input_size < sizeof(uint16_t)) {
+ CPRINTF("%s: incorrect request size %d\n",
+ __func__, input_size);
+ return VENDOR_RC_BOGUS_ARGS;
+ }
+
+ /* Retrieve the requested timeout. */
+ memcpy(&timeout, buf, sizeof(timeout));
+ timeout = be16toh(timeout);
+
+ if (timeout > MAX_REBOOT_TIMEOUT_MS) {
+ CPRINTF("%s: incorrect timeout value %d\n",
+ __func__, timeout);
+ return VENDOR_RC_BOGUS_ARGS;
+ }
+
+ *response_size = 1;
+ response = buf;
+
+ *response = headers_restored();
+ if (*response && timeout) {
+ /*
+ * At least one header was restored, and timeout is not zero,
+ * set up the reboot.
+ */
+ CPRINTF("%s: rebooting in %d ms\n", __func__, timeout);
+ hook_call_deferred(&deferred_reboot_data, timeout * MSEC);
+ }
+
+ return VENDOR_RC_SUCCESS;
+}
+DECLARE_VENDOR_COMMAND(VENDOR_CC_TURN_UPDATE_ON, turn_update_on);
+
+/* This command's implementation is shared with USB updater. */
+DECLARE_EXTENSION_COMMAND(EXTENSION_FW_UPGRADE, fw_upgrade_command_handler);
diff --git a/chip/g/upgrade_fw.c b/chip/g/upgrade_fw.c
index cbbfdc36d1..caa5b88818 100644
--- a/chip/g/upgrade_fw.c
+++ b/chip/g/upgrade_fw.c
@@ -3,20 +3,24 @@
* found in the LICENSE file.
*/
+#include "config.h"
+
+#include "board_id.h"
#include "byteorder.h"
+#include "compile_time_macros.h"
#include "console.h"
+#include "cryptoc/sha.h"
#include "dcrypto/dcrypto.h"
#include "extension.h"
#include "flash.h"
+#include "flash_info.h"
#include "hooks.h"
-#include "include/compile_time_macros.h"
-#include "system.h"
#include "registers.h"
-#include "uart.h"
-
#include "signed_header.h"
+#include "system.h"
+#include "system_chip.h"
+#include "uart.h"
#include "upgrade_fw.h"
-#include "cryptoc/sha.h"
#define CPRINTF(format, args...) cprintf(CC_EXTENSION, format, ## args)
@@ -65,17 +69,6 @@ static void set_valid_sections(void)
CONFIG_RW_SIZE;
}
-/* Enable write access to the backup RO section. */
-static void open_ro_window(uint32_t offset, size_t size_b)
-{
- GREG32(GLOBALSEC, FLASH_REGION6_BASE_ADDR) =
- offset + CONFIG_PROGRAM_MEMORY_BASE;
- GREG32(GLOBALSEC, FLASH_REGION6_SIZE) = size_b - 1;
- GWRITE_FIELD(GLOBALSEC, FLASH_REGION6_CTRL, EN, 1);
- GWRITE_FIELD(GLOBALSEC, FLASH_REGION6_CTRL, RD_EN, 1);
- GWRITE_FIELD(GLOBALSEC, FLASH_REGION6_CTRL, WR_EN, 1);
-}
-
/*
* Verify that the passed in block fits into the valid area. If it does, and
* is destined to the base address of the area - erase the area contents.
@@ -101,7 +94,7 @@ static uint8_t check_update_chunk(uint32_t block_offset, size_t body_size)
* be erased.
*/
if (block_offset == valid_sections.rw_base_offset) {
- if (flash_erase(base, size) != EC_SUCCESS) {
+ if (flash_physical_erase(base, size) != EC_SUCCESS) {
CPRINTF("%s:%d erase failure of 0x%x..+0x%x\n",
__func__, __LINE__, base, size);
return UPGRADE_ERASE_FAILURE;
@@ -127,8 +120,8 @@ static uint8_t check_update_chunk(uint32_t block_offset, size_t body_size)
size = valid_sections.ro_top_offset -
valid_sections.ro_base_offset;
/* backup RO area write access needs to be enabled. */
- open_ro_window(base, size);
- if (flash_erase(base, size) != EC_SUCCESS) {
+ flash_open_ro_window(base, size);
+ if (flash_physical_erase(base, size) != EC_SUCCESS) {
CPRINTF("%s:%d erase failure of 0x%x..+0x%x\n",
__func__, __LINE__, base, size);
return UPGRADE_ERASE_FAILURE;
@@ -148,6 +141,187 @@ static uint8_t check_update_chunk(uint32_t block_offset, size_t body_size)
return UPGRADE_BAD_ADDR;
}
+int usb_pdu_valid(struct upgrade_command *cmd_body, size_t cmd_size)
+{
+ uint8_t sha1_digest[SHA_DIGEST_SIZE];
+ size_t body_size = cmd_size - offsetof(struct update_frame_header,
+ cmd.block_base);
+
+ /* Check if the block was received properly. */
+ DCRYPTO_SHA1_hash((uint8_t *)&cmd_body->block_base,
+ body_size + sizeof(cmd_body->block_base),
+ sha1_digest);
+ if (memcmp(sha1_digest, &cmd_body->block_digest,
+ sizeof(cmd_body->block_digest))) {
+ CPRINTF("%s:%d sha1 %x not equal received %x\n",
+ __func__, __LINE__,
+ *(uint32_t *)sha1_digest, cmd_body->block_digest);
+ return 0;
+ }
+
+ return 1;
+}
+
+#ifdef CR50_DEV
+#ifndef CONFIG_IGNORE_G_UPDATE_CHECKS
+#define CONFIG_IGNORE_G_UPDATE_CHECKS
+#endif
+#endif
+
+#ifndef CONFIG_IGNORE_G_UPDATE_CHECKS
+/* Compare two versions, return True if the new version is older. */
+static int new_is_older(const struct SignedHeader *new,
+ const struct SignedHeader *old)
+{
+ if (new->epoch_ != old->epoch_)
+ return new->epoch_ < old->epoch_;
+
+ if (new->major_ != old->major_)
+ return new->major_ < old->major_;
+
+
+ return new->minor_ < old->minor_;
+}
+
+/*
+ * Check if this chunk of data is a rollback attempt, or is unaligned,
+ * overlaps RO or RW header, or would cause a board ID mismatch if attempted
+ * to run.
+ *
+ * Return False if there is any of the above problems and set the passed in
+ * error_code pointer to the proper error_code.
+ */
+static int contents_allowed(uint32_t block_offset,
+ size_t body_size, void *upgrade_data,
+ uint8_t *error_code)
+{
+ /* Pointer to RO or RW header in flash, to compare against. */
+ const struct SignedHeader *header;
+ int is_rw_header = 0;
+
+ if (block_offset == valid_sections.ro_base_offset) {
+ header = (const struct SignedHeader *)
+ get_program_memory_addr(system_get_ro_image_copy());
+ } else if (block_offset == valid_sections.rw_base_offset) {
+ header = (const struct SignedHeader *)
+ get_program_memory_addr(system_get_image_copy());
+ is_rw_header = 1;
+ } else {
+
+ /*
+ * The received block is not destined to a header directly,
+ * but does it overlap with a header by any chance?
+ */
+ int i;
+ /* Base offsets of valid headers in flash. */
+ uint32_t bases[] = { valid_sections.ro_base_offset,
+ valid_sections.rw_base_offset };
+ /* Range of offsets this block is covering. */
+ uint32_t range[] = { block_offset, block_offset + body_size };
+
+ for (i = 0; i < ARRAY_SIZE(bases); i++) {
+ int j;
+
+ for (j = 0; j < ARRAY_SIZE(range); j++) {
+ if ((range[j] >= bases[i]) &&
+ (range[j] <
+ (bases[i] +
+ sizeof(struct SignedHeader)))) {
+ CPRINTF("%s:"
+ " unaligned block overlaps\n",
+ __func__);
+ *error_code =
+ UPGRADE_UNALIGNED_BLOCK_ERROR;
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+ }
+
+ /* This block is a header (ro or rw) of the new image. */
+ if (body_size < sizeof(struct SignedHeader)) {
+ CPRINTF("%s: block too short\n", __func__);
+ *error_code = UPGRADE_TRUNCATED_HEADER_ERROR;
+ return 0;
+ }
+
+ /* upgrade_data is the new header. */
+ if (new_is_older(upgrade_data, header)) {
+ CPRINTF("%s: rejecting an older header.\n", __func__);
+ *error_code = UPGRADE_ROLLBACK_ERROR;
+ return 0;
+ }
+
+ if (is_rw_header && board_id_mismatch(upgrade_data)) {
+ CPRINTF("%s: rejecting Board ID mismatch.\n", __func__);
+ *error_code = UPGRADE_BOARD_ID_ERROR;
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static uint32_t prev_offset;
+static uint64_t prev_timestamp;
+#define BACKOFF_TIME (60 * SECOND)
+
+static int chunk_came_too_soon(uint32_t block_offset)
+{
+ int hard_reset = system_get_reset_flags() & RESET_FLAG_HARD;
+
+ /*
+ * If it has been BACKOFF_TIME since the last time we wrote to a block
+ * or since the last boot, the write is ok.
+ */
+ if ((get_time().val - prev_timestamp) > BACKOFF_TIME)
+ return 0;
+
+ if (!prev_timestamp) {
+ /*
+ * If we just recovered from a hard reset, we have to wait until
+ * backoff time to accept an update. All other resets can accept
+ * updates immediately.
+ */
+ if (hard_reset)
+ CPRINTF("%s: rejecting a write after hard reset\n",
+ __func__);
+ return hard_reset;
+ }
+
+ if (!prev_offset ||
+ (block_offset >= (prev_offset + SIGNED_TRANSFER_SIZE)))
+ return 0;
+
+ CPRINTF("%s: rejecting a write to the same block\n", __func__);
+ return 1;
+}
+
+static void new_chunk_written(uint32_t block_offset)
+{
+ prev_timestamp = get_time().val;
+ prev_offset = block_offset;
+}
+#else
+static int chunk_came_too_soon(uint32_t block_offset)
+{
+ return 0;
+}
+
+static void new_chunk_written(uint32_t block_offset)
+{
+}
+
+static int contents_allowed(uint32_t block_offset,
+ size_t body_size, void *upgrade_data,
+ uint8_t *error_code)
+{
+ return 1;
+}
+#endif
+
void fw_upgrade_command_handler(void *body,
size_t cmd_size,
size_t *response_size)
@@ -155,7 +329,6 @@ void fw_upgrade_command_handler(void *body,
struct upgrade_command *cmd_body = body;
void *upgrade_data;
uint8_t *error_code = body; /* Cache the address for code clarity. */
- uint8_t sha1_digest[SHA_DIGEST_SIZE];
size_t body_size;
uint32_t block_offset;
@@ -190,7 +363,7 @@ void fw_upgrade_command_handler(void *body,
/*
* If there have been any problems when determining the valid
- * secitons offsets/sizes - return an error code.
+ * Sections offsets/sizes - return an error code.
*/
if (!valid_sections.ro_top_offset ||
!valid_sections.rw_top_offset) {
@@ -225,29 +398,47 @@ void fw_upgrade_command_handler(void *body,
return;
}
- /* Check if the block was received properly. */
- DCRYPTO_SHA1_hash((uint8_t *)&cmd_body->block_base,
- body_size + sizeof(cmd_body->block_base),
- sha1_digest);
- if (memcmp(sha1_digest, &cmd_body->block_digest,
- sizeof(cmd_body->block_digest))) {
+ block_offset = be32toh(cmd_body->block_base);
+
+ if (!usb_pdu_valid(cmd_body, cmd_size)) {
*error_code = UPGRADE_DATA_ERROR;
- CPRINTF("%s:%d sha1 %x not equal received %x at offs. 0x%x\n",
- __func__, __LINE__,
- *(uint32_t *)sha1_digest, cmd_body->block_digest,
- block_offset);
return;
}
+ upgrade_data = cmd_body + 1;
+ if (!contents_allowed(block_offset, body_size,
+ upgrade_data, error_code))
+ return;
+
/* Check if the block will fit into the valid area. */
- block_offset = be32toh(cmd_body->block_base);
*error_code = check_update_chunk(block_offset, body_size);
if (*error_code)
return;
+ if (chunk_came_too_soon(block_offset)) {
+ *error_code = UPGRADE_RATE_LIMIT_ERROR;
+ return;
+ }
+
+ if ((block_offset == valid_sections.ro_base_offset) ||
+ (block_offset == valid_sections.rw_base_offset)) {
+ /*
+ * This is the header coming, let's corrupt it so that it does
+ * not run until it's time to switch.
+ */
+ struct SignedHeader *header;
+
+ header = (struct SignedHeader *) upgrade_data;
+
+ /*
+ * Set the top bit of the size field. It will be impossible to
+ * run this image until this bit is erased.
+ */
+ header->image_size |= TOP_IMAGE_SIZE_BIT;
+ }
+
CPRINTF("%s: programming at address 0x%x\n", __func__,
block_offset + CONFIG_PROGRAM_MEMORY_BASE);
- upgrade_data = cmd_body + 1;
if (flash_physical_write(block_offset, body_size, upgrade_data)
!= EC_SUCCESS) {
*error_code = UPGRADE_WRITE_FAILURE;
@@ -255,6 +446,8 @@ void fw_upgrade_command_handler(void *body,
return;
}
+ new_chunk_written(block_offset);
+
/* Verify that data was written properly. */
if (memcmp(upgrade_data, (void *)
(block_offset + CONFIG_PROGRAM_MEMORY_BASE),
@@ -272,4 +465,3 @@ void fw_upgrade_complete(void)
{
system_clear_retry_counter();
}
-
diff --git a/chip/g/upgrade_fw.h b/chip/g/upgrade_fw.h
index 3833d26036..d3414b103e 100644
--- a/chip/g/upgrade_fw.h
+++ b/chip/g/upgrade_fw.h
@@ -8,6 +8,7 @@
#include <stddef.h>
+#include "common.h" /* For __packed. */
/*
* This file contains structures used to facilitate cr50 firmware updates,
@@ -81,7 +82,7 @@ struct signed_header_version {
* just as to any other block of the transfer sequence.
*
* It became clear that there is a need to be able to enhance the upgrade
- * protocol, while stayng backwards compatible.
+ * protocol, while staying backwards compatible.
*
* All newer protocol versions (starting with version 2) respond to the very
* first packet with an 8 byte or larger response, where the first 4 bytes are
@@ -112,7 +113,6 @@ struct first_response_pdu {
uint32_t keyid[2];
};
-/* TODO: Handle this in upgrade_fw.c, not usb_upgrade.c */
#define UPGRADE_DONE 0xB007AB1E
void fw_upgrade_command_handler(void *body,
@@ -122,6 +122,10 @@ void fw_upgrade_command_handler(void *body,
/* Used to tell fw upgrade the update ran successfully and is finished */
void fw_upgrade_complete(void);
+/* Verify integrity of the PDU received over USB. */
+int usb_pdu_valid(struct upgrade_command *cmd_body,
+ size_t cmd_size);
+
/* Various upgrade command return values. */
enum return_value {
UPGRADE_SUCCESS = 0,
@@ -132,6 +136,11 @@ enum return_value {
UPGRADE_VERIFY_ERROR = 5,
UPGRADE_GEN_ERROR = 6,
UPGRADE_MALLOC_ERROR = 7,
+ UPGRADE_ROLLBACK_ERROR = 8,
+ UPGRADE_RATE_LIMIT_ERROR = 9,
+ UPGRADE_UNALIGNED_BLOCK_ERROR = 10,
+ UPGRADE_TRUNCATED_HEADER_ERROR = 11,
+ UPGRADE_BOARD_ID_ERROR = 12,
};
/*
@@ -139,4 +148,5 @@ enum return_value {
* of the image.
*/
#define SIGNED_TRANSFER_SIZE 1024
+
#endif /* ! __EC_CHIP_G_UPGRADE_FW_H */
diff --git a/chip/g/usart.c b/chip/g/usart.c
index 0d22b94bfc..598b4d4ed9 100644
--- a/chip/g/usart.c
+++ b/chip/g/usart.c
@@ -8,33 +8,63 @@
#include "uartn.h"
#include "usart.h"
#include "usb-stream.h"
+#ifdef CONFIG_STREAM_SIGNATURE
+#include "signing.h"
+#endif
#define USE_UART_INTERRUPTS (!(defined(CONFIG_CUSTOMIZED_RO) && \
defined(SECTION_IS_RO)))
#define QUEUE_SIZE 64
+
+#ifdef CONFIG_STREAM_USART1
struct usb_stream_config const ap_usb;
struct usart_config const ap_uart;
-struct usb_stream_config const ec_usb;
-struct usart_config const ec_uart;
+#ifdef CONFIG_STREAM_SIGNATURE
+/*
+ * This code adds the ability to capture UART data received, and
+ * sign it with H1's key. This allows the log output to be verified
+ * as actual UART output from this board.
+ *
+ * This functionality is enabled by redirecting the UART receive queue
+ * to feed into the signing module rather than the usb tx. After being
+ * added to the running hash, the data is then pushed by the signer
+ * into the usb tx queue.
+ */
+struct signer_config const sig;
+static struct queue const ap_uart_output =
+ QUEUE_DIRECT(QUEUE_SIZE, uint8_t, ap_uart.producer, sig.consumer);
+static struct queue const sig_to_usb =
+ QUEUE_DIRECT(QUEUE_SIZE, uint8_t, sig.producer, ap_usb.consumer);
-static struct queue const ap_uart_to_usb =
+SIGNER_CONFIG(sig, stream_uart, sig_to_usb, ap_uart_output);
+
+#else /* Not CONFIG_STREAM_SIGNATURE */
+static struct queue const ap_uart_output =
QUEUE_DIRECT(QUEUE_SIZE, uint8_t, ap_uart.producer, ap_usb.consumer);
+#endif
+
static struct queue const ap_usb_to_uart =
QUEUE_DIRECT(QUEUE_SIZE, uint8_t, ap_usb.producer, ap_uart.consumer);
-static struct queue const ec_uart_to_usb =
- QUEUE_DIRECT(QUEUE_SIZE, uint8_t, ec_uart.producer, ec_usb.consumer);
-static struct queue const ec_usb_to_uart =
- QUEUE_DIRECT(QUEUE_SIZE, uint8_t, ec_usb.producer, ec_uart.consumer);
+/*
+ * AP UART data is sent to the ap_uart_output queue, and received from
+ * the ap_usb_to_uart queue. The ap_uart_output queue is received by the
+ * USB bridge, or if a signer is enabled, received by the signer, which then
+ * passes the data to the USB bridge after processing it.
+ */
+USART_CONFIG(ap_uart,
+ UART_AP,
+ ap_uart_output,
+ ap_usb_to_uart);
-struct usart_config const ap_uart = USART_CONFIG(UART_AP,
- ap_uart_to_usb,
- ap_usb_to_uart);
-struct usart_config const ec_uart = USART_CONFIG(UART_EC,
- ec_uart_to_usb,
- ec_usb_to_uart);
+/*
+ * The UART USB bridge receives character data from the UART's queue,
+ * unless signing is enabled, in which case it receives data from the
+ * signer's queue, after the signer has received it from the UART and
+ * processed it.
+ */
USB_STREAM_CONFIG(ap_usb,
USB_IFACE_AP,
USB_STR_AP_NAME,
@@ -42,7 +72,27 @@ USB_STREAM_CONFIG(ap_usb,
USB_MAX_PACKET_SIZE,
USB_MAX_PACKET_SIZE,
ap_usb_to_uart,
- ap_uart_to_usb)
+#ifdef CONFIG_STREAM_SIGNATURE
+ sig_to_usb)
+#else
+ ap_uart_output)
+#endif
+#endif /* CONFIG_STREAM_USART1 */
+
+#ifdef CONFIG_STREAM_USART2
+struct usb_stream_config const ec_usb;
+struct usart_config const ec_uart;
+
+static struct queue const ec_uart_to_usb =
+ QUEUE_DIRECT(QUEUE_SIZE, uint8_t, ec_uart.producer, ec_usb.consumer);
+static struct queue const ec_usb_to_uart =
+ QUEUE_DIRECT(QUEUE_SIZE, uint8_t, ec_usb.producer, ec_uart.consumer);
+
+USART_CONFIG(ec_uart,
+ UART_EC,
+ ec_uart_to_usb,
+ ec_usb_to_uart);
+
USB_STREAM_CONFIG(ec_usb,
USB_IFACE_EC,
USB_STR_EC_NAME,
@@ -51,6 +101,7 @@ USB_STREAM_CONFIG(ec_usb,
USB_MAX_PACKET_SIZE,
ec_usb_to_uart,
ec_uart_to_usb)
+#endif
void get_data_from_usb(struct usart_config const *config)
{
@@ -109,13 +160,16 @@ struct consumer_ops const uart_consumer_ops = {
};
#if USE_UART_INTERRUPTS
+#ifdef CONFIG_STREAM_USART1
/*
* Interrupt handlers for UART1
*/
CONFIGURE_INTERRUPTS(ap_uart,
GC_IRQNUM_UART1_RXINT,
GC_IRQNUM_UART1_TXINT)
+#endif
+#ifdef CONFIG_STREAM_USART2
/*
* Interrupt handlers for UART2
*/
@@ -123,3 +177,4 @@ CONFIGURE_INTERRUPTS(ec_uart,
GC_IRQNUM_UART2_RXINT,
GC_IRQNUM_UART2_TXINT)
#endif
+#endif
diff --git a/chip/g/usart.h b/chip/g/usart.h
index 79616a48e0..133dc7a3af 100644
--- a/chip/g/usart.h
+++ b/chip/g/usart.h
@@ -16,6 +16,8 @@ struct usart_config {
struct producer const producer;
struct consumer const consumer;
+
+ const struct deferred_data *deferred;
};
extern struct consumer_ops const uart_consumer_ops;
@@ -41,14 +43,17 @@ extern struct producer_ops const uart_producer_ops;
GR_UART_ISTATECLR(NAME.uart) = \
GC_UART_ISTATECLR_RX_MASK; \
/* Read input FIFO until empty */ \
- send_data_to_usb(&NAME); \
+ hook_call_deferred(NAME.deferred, 0); \
}
-#define USART_CONFIG(UART, \
+#define USART_CONFIG(NAME, \
+ UART, \
RX_QUEUE, \
TX_QUEUE) \
- ((struct usart_config const) { \
+ static void CONCAT2(NAME, _deferred_)(void); \
+ DECLARE_DEFERRED(CONCAT2(NAME, _deferred_)); \
+ struct usart_config const NAME = { \
.uart = UART, \
.consumer = { \
.queue = &TX_QUEUE, \
@@ -58,7 +63,12 @@ extern struct producer_ops const uart_producer_ops;
.queue = &RX_QUEUE, \
.ops = &uart_producer_ops, \
}, \
- })
+ .deferred = &CONCAT2(NAME, _deferred__data), \
+ }; \
+ static void CONCAT2(NAME, _deferred_)(void) \
+ { \
+ send_data_to_usb(&NAME); \
+ } \
/* Read data from UART and add it to the producer queue */
diff --git a/chip/g/usb-stream.c b/chip/g/usb-stream.c
index 21a06c94e3..025f71e7fc 100644
--- a/chip/g/usb-stream.c
+++ b/chip/g/usb-stream.c
@@ -57,6 +57,10 @@ int rx_stream_handler(struct usb_stream_config const *config)
*/
static int rx_handled;
+ /* If the HW FIFO isn't ready, then we're waiting for more bytes */
+ if (!rx_fifo_is_ready(config))
+ return 0;
+
/*
* How many of the HW FIFO bytes have we not yet handled? We need to
* know both where we are in the buffer and how many bytes we haven't
diff --git a/chip/g/usb-stream.h b/chip/g/usb-stream.h
index b789c65925..5eb03865b0 100644
--- a/chip/g/usb-stream.h
+++ b/chip/g/usb-stream.h
@@ -14,6 +14,7 @@
#include "producer.h"
#include "queue.h"
#include "usb_descriptor.h"
+#include "usb_hw.h"
/*
* Compile time Per-USB stream configuration stored in flash. Instances of this
diff --git a/chip/g/usb.c b/chip/g/usb.c
index 932081f363..37fef3179d 100644
--- a/chip/g/usb.c
+++ b/chip/g/usb.c
@@ -3,6 +3,7 @@
* found in the LICENSE file.
*/
+#include "case_closed_debug.h"
#include "clock.h"
#include "common.h"
#include "config.h"
@@ -11,12 +12,14 @@
#include "hooks.h"
#include "init_chip.h"
#include "link_defs.h"
+#include "printf.h"
#include "registers.h"
#include "system.h"
#include "task.h"
#include "timer.h"
#include "util.h"
#include "usb_descriptor.h"
+#include "usb_hw.h"
#include "watchdog.h"
/****************************************************************************/
@@ -26,6 +29,10 @@
#define CPRINTS(format, args...) cprints(CC_USB, format, ## args)
#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args)
+#ifndef CONFIG_USB_SERIALNO
+#define USB_STR_SERIALNO 0
+#endif
+
/* This is not defined anywhere else. Change it here to debug. */
#undef DEBUG_ME
#ifdef DEBUG_ME
@@ -177,8 +184,8 @@ static void showregs(void)
/* Standard USB stuff */
#ifdef CONFIG_USB_BOS
-/* v2.01 (vs 2.00) BOS Descriptor provided */
-#define USB_DEV_BCDUSB 0x0201
+/* v2.10 (vs 2.00) BOS Descriptor provided */
+#define USB_DEV_BCDUSB 0x0210
#else
#define USB_DEV_BCDUSB 0x0200
#endif
@@ -191,14 +198,6 @@ static void showregs(void)
#define CONFIG_USB_BCD_DEV 0x0100 /* 1.00 */
#endif
-#ifndef USB_BMATTRIBUTES
-#ifdef CONFIG_USB_SELF_POWERED
-#define USB_BMATTRIBUTES 0xc0 /* Self powered. */
-#else
-#define USB_BMATTRIBUTES 0x80 /* Bus powered. */
-#endif
-#endif
-
/* USB Standard Device Descriptor */
static const struct usb_device_descriptor dev_desc = {
.bLength = USB_DT_DEVICE_SIZE,
@@ -213,7 +212,7 @@ static const struct usb_device_descriptor dev_desc = {
.bcdDevice = CONFIG_USB_BCD_DEV,
.iManufacturer = USB_STR_VENDOR,
.iProduct = USB_STR_PRODUCT,
- .iSerialNumber = 0,
+ .iSerialNumber = USB_STR_SERIALNO,
.bNumConfigurations = 1
};
@@ -225,7 +224,14 @@ const struct usb_config_descriptor USB_CONF_DESC(conf) = {
.bNumInterfaces = USB_IFACE_COUNT,
.bConfigurationValue = 1, /* Caution: hard-coded value */
.iConfiguration = USB_STR_VERSION,
- .bmAttributes = USB_BMATTRIBUTES, /* bus or self powered */
+ .bmAttributes = 0x80 /* Reserved bit */
+#ifdef CONFIG_USB_SELF_POWERED /* bus or self powered */
+ | 0x40
+#endif
+#ifdef CONFIG_USB_REMOTE_WAKEUP
+ | 0x20
+#endif
+ ,
.bMaxPower = (CONFIG_USB_MAXPOWER_MA / 2),
};
@@ -322,8 +328,12 @@ static enum {
} device_state;
static uint8_t configuration_value;
+#ifndef CONFIG_USB_SELECT_PHY_DEFAULT
+#define CONFIG_USB_SELECT_PHY_DEFAULT USB_SEL_PHY1
+#endif
+
/* Default PHY to use */
-static uint32_t which_phy = USB_SEL_PHY1;
+static uint32_t which_phy = CONFIG_USB_SELECT_PHY_DEFAULT;
void usb_select_phy(uint32_t phy)
{
@@ -378,7 +388,7 @@ static void got_RX_packet(void)
int load_in_fifo(const void *source, uint32_t len)
{
uint8_t *buffer = ep0_in_buf;
- int zero_packet = !len;
+ int zero_packet = (len % USB_MAX_PACKET_SIZE) == 0;
int d, l;
/* Copy the data into our FIFO buffer */
@@ -598,12 +608,19 @@ static int handle_setup_with_in_stage(enum table_case tc,
case USB_DT_STRING:
if (idx >= USB_STR_COUNT)
return -1;
- data = usb_strings[idx];
+#ifdef CONFIG_USB_SERIALNO
+ if (idx == USB_STR_SERIALNO && ccd_ext_is_enabled())
+ data = usb_serialno_desc;
+ else
+#endif
+ data = usb_strings[idx];
len = *(uint8_t *)data;
break;
case USB_DT_DEVICE_QUALIFIER:
/* We're not high speed */
return -1;
+ case USB_DT_DEBUG:
+ return -1;
default:
report_error(type);
return -1;
@@ -732,12 +749,6 @@ static int handle_setup_with_no_data_stage(enum table_case tc,
CPRINTS("SETAD 0x%02x (%d)", set_addr, set_addr);
print_later("SETAD 0x%02x (%d)", set_addr, set_addr, 0, 0, 0);
device_state = DS_ADDRESS;
-#ifdef BOARD_CR50
- /* TODO(crosbug.com/p/56540): Remove when no longer needed */
- if (!processed_update_counter && system_get_board_properties() &
- BOARD_MARK_UPDATE_ON_USB_REQ)
- system_process_retry_counter();
-#endif
processed_update_counter = 1;
break;
@@ -807,6 +818,20 @@ static void handle_setup(enum table_case tc)
print_later(" iface returned %d", bytes, 0, 0, 0, 0);
}
} else {
+#ifdef CONFIG_WEBUSB_URL
+ if (data_phase_in &&
+ ((req->bmRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR)) {
+ if (req->bRequest == 0x01 &&
+ req->wIndex == WEBUSB_REQ_GET_URL) {
+ bytes = *(uint8_t *)webusb_url;
+ bytes = MIN(req->wLength, bytes);
+ if (load_in_fifo(webusb_url, bytes) < 0)
+ bytes = -1;
+ } else {
+ report_error(-1);
+ }
+ } else
+#endif
/* Something we need to add support for? */
report_error(-1);
}
@@ -898,7 +923,7 @@ static void ep0_interrupt(uint32_t intr_on_out, uint32_t intr_on_in)
/*
* The Programmer's Guide says (p291) to stall any
* further INs, but that's stupid because it'll destroy
- * the packet we just tranferred to SPRAM, so don't do
+ * the packet we just transferred to SPRAM, so don't do
* that (we tried it anyway, and Bad Things happened).
* Also don't stop here, but keep looking at stuff.
*/
@@ -1078,7 +1103,7 @@ static void usb_init_endpoints(void)
static void usb_reset(void)
{
- CPRINTS("%s", __func__);
+ CPRINTS("%s, status %x", __func__, GR_USB_GINTSTS);
print_later("usb_reset()", 0, 0, 0, 0, 0);
/* Clear our internal state */
@@ -1092,14 +1117,6 @@ static void usb_reset(void)
usb_init_endpoints();
}
-static void usb_resetdet(void)
-{
- /* TODO: Same as normal reset, right? I think we only get this if we're
- * suspended (sleeping) and the host resets us. Try it and see. */
- print_later("usb_resetdet()", 0, 0, 0, 0, 0);
- usb_reset();
-}
-
void usb_interrupt(void)
{
uint32_t status = GR_USB_GINTSTS;
@@ -1130,10 +1147,7 @@ void usb_interrupt(void)
print_later("usb_enumdone()", 0, 0, 0, 0, 0);
#endif
- if (status & GINTSTS(RESETDET))
- usb_resetdet();
-
- if (status & GINTSTS(USBRST))
+ if (status & (GINTSTS(RESETDET) | GINTSTS(USBRST)))
usb_reset();
/* Initialize the SOF clock calibrator only on the first SOF */
@@ -1207,6 +1221,15 @@ static void usb_softreset(void)
return;
}
/* TODO: Wait 3 PHY clocks before returning */
+
+#ifdef BOARD_CR50
+ /*
+ * TODO(b/63867566): This delay is added to get usb to suspend after
+ * resume from deep sleep. Find out what the root cause is and add a
+ * fix.
+ */
+ usleep(100);
+#endif
}
void usb_connect(void)
@@ -1224,6 +1247,41 @@ void usb_disconnect(void)
configuration_value = 0;
}
+void usb_save_suspended_state(void)
+{
+ int i;
+ uint32_t pid = 0;
+
+ /* Record the state the DATA PIDs toggling on each endpoint. */
+ for (i = 1; i < USB_EP_COUNT; i++) {
+ if (GR_USB_DOEPCTL(i) & DXEPCTL_DPID)
+ pid |= (1 << i);
+ if (GR_USB_DIEPCTL(i) & DXEPCTL_DPID)
+ pid |= (1 << (i + 16));
+ }
+ /* Save the USB device address */
+ GREG32(PMU, PWRDN_SCRATCH18) = GR_USB_DCFG;
+ GREG32(PMU, PWRDN_SCRATCH19) = pid;
+
+}
+
+void usb_restore_suspended_state(void)
+{
+ int i;
+ uint32_t pid;
+
+ /* restore the USB device address (the DEVADDR field). */
+ GR_USB_DCFG = GREG32(PMU, PWRDN_SCRATCH18);
+ /* Restore the DATA PIDs on endpoints. */
+ pid = GREG32(PMU, PWRDN_SCRATCH19);
+ for (i = 1; i < USB_EP_COUNT; i++) {
+ GR_USB_DOEPCTL(i) = pid & (1 << i) ?
+ DXEPCTL_SET_D1PID : DXEPCTL_SET_D0PID;
+ GR_USB_DIEPCTL(i) = pid & (1 << (i + 16)) ?
+ DXEPCTL_SET_D1PID : DXEPCTL_SET_D0PID;
+ }
+}
+
void usb_init(void)
{
int i, resume;
@@ -1264,6 +1322,9 @@ void usb_init(void)
GR_USB_DIEPMSK = 0;
GR_USB_DOEPMSK = 0;
+ /* Disable the PHY clock whenever usb suspend is detected */
+ GWRITE_FIELD(USB, PCGCCTL, STOPPCLK, 1);
+
/* Select the correct PHY */
usb_select_phy(which_phy);
@@ -1293,10 +1354,7 @@ void usb_init(void)
usb_disconnect();
if (resume)
- /* DEVADDR is preserved in the USB module during deep sleep,
- * but it doesn't show up in USB_DCFG on resume. If we don't
- * restore it manually too, it doesn't work. */
- GR_USB_DCFG = GREG32(PMU, PWRDN_SCRATCH18);
+ usb_restore_suspended_state();
else
/* Init: USB2 FS, Scatter/Gather DMA, DEVADDR = 0x00 */
GR_USB_DCFG |= DCFG_DEVSPD_FS48 | DCFG_DESCDMA;
@@ -1347,8 +1405,8 @@ void usb_init(void)
GINTMSK(RESETDET) | /* TODO: Do we need this? */
/* Idle, Suspend detected. Should go to sleep. */
GINTMSK(ERLYSUSP) | GINTMSK(USBSUSP) |
- /* Watch for first SOF */
- GINTMSK(SOF);
+ /* Watch for first SOF and usb wakeup */
+ GINTMSK(SOF) | GINTMSK(WKUPINT);
/* Device registers have been setup */
GR_USB_DCTL |= DCTL_PWRONPRGDONE;
@@ -1365,7 +1423,7 @@ void usb_init(void)
#endif
}
#ifndef CONFIG_USB_INHIBIT_INIT
-DECLARE_HOOK(HOOK_INIT, usb_init, HOOK_PRIO_DEFAULT);
+DECLARE_HOOK(HOOK_INIT, usb_init, HOOK_PRIO_DEFAULT - 2);
#endif
void usb_release(void)
@@ -1392,15 +1450,17 @@ static int command_usb(int argc, char **argv)
int val;
if (argc > 1) {
- if (!strcasecmp("a", argv[1]))
- usb_select_phy(USB_SEL_PHY0);
- else if (!strcasecmp("b", argv[1]))
- usb_select_phy(USB_SEL_PHY1);
- else if (parse_bool(argv[1], &val)) {
+ if (parse_bool(argv[1], &val)) {
if (val)
usb_init();
else
usb_release();
+#ifdef CONFIG_USB_SELECT_PHY
+ } else if (!strcasecmp("a", argv[1])) {
+ usb_select_phy(USB_SEL_PHY0);
+ } else if (!strcasecmp("b", argv[1])) {
+ usb_select_phy(USB_SEL_PHY1);
+#endif
} else
return EC_ERROR_PARAM1;
}
@@ -1411,5 +1471,70 @@ static int command_usb(int argc, char **argv)
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(usb, command_usb,
+#ifdef CONFIG_USB_SELECT_PHY
"[<BOOLEAN> | a | b]",
+#else
+ "<BOOLEAN>",
+#endif
"Get/set the USB connection state and PHY selection");
+
+#ifdef CONFIG_USB_SERIALNO
+/* This will be subbed into USB_STR_SERIALNO. */
+struct usb_string_desc *usb_serialno_desc =
+ USB_WR_STRING_DESC(DEFAULT_SERIALNO);
+
+/* Update serial number */
+static int usb_set_serial(const char *serialno)
+{
+ struct usb_string_desc *sd = usb_serialno_desc;
+ int i;
+
+ if (!serialno)
+ return EC_ERROR_INVAL;
+
+ /* Convert into unicode usb string desc. */
+ for (i = 0; i < CONFIG_SERIALNO_LEN; i++) {
+ sd->_data[i] = serialno[i];
+ if (serialno[i] == 0)
+ break;
+ }
+ /* Count wchars (w/o null terminator) plus size & type bytes. */
+ sd->_len = (i * 2) + 2;
+ sd->_type = USB_DT_STRING;
+
+ return EC_SUCCESS;
+}
+
+static void usb_load_serialno(void)
+{
+ char devid_str[20];
+
+ snprintf(devid_str, 20, "%08X-%08X", GREG32(FUSE, DEV_ID0),
+ GREG32(FUSE, DEV_ID1));
+
+ usb_set_serial(devid_str);
+}
+DECLARE_HOOK(HOOK_INIT, usb_load_serialno, HOOK_PRIO_DEFAULT - 1);
+
+static int command_serialno(int argc, char **argv)
+{
+ struct usb_string_desc *sd = usb_serialno_desc;
+ char buf[CONFIG_SERIALNO_LEN];
+ int rv = EC_SUCCESS;
+ int i;
+
+ if (argc != 1) {
+ ccprintf("Setting serial number\n");
+ rv = usb_set_serial(argv[1]);
+ }
+
+ for (i = 0; i < CONFIG_SERIALNO_LEN; i++)
+ buf[i] = sd->_data[i];
+ ccprintf("Serial number: %s\n", buf);
+ return rv;
+}
+
+DECLARE_CONSOLE_COMMAND(serialno, command_serialno,
+ "[value]",
+ "Read and write USB serial number");
+#endif
diff --git a/chip/g/usb_console.c b/chip/g/usb_console.c
index b9c35a53f2..f77c34a638 100644
--- a/chip/g/usb_console.c
+++ b/chip/g/usb_console.c
@@ -14,6 +14,7 @@
#include "timer.h"
#include "util.h"
#include "usb_descriptor.h"
+#include "usb_hw.h"
/* Console output macro */
#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args)
@@ -22,8 +23,18 @@
static int last_tx_ok = 1;
static int is_reset;
+
+/*
+ * Start enabled, so we can queue early debug output before the board gets
+ * around to calling usb_console_enable().
+ */
static int is_enabled = 1;
-static int is_readonly;
+
+/*
+ * But start read-only, so we don't accept console input until we explicitly
+ * decide that we're ready for it.
+ */
+static int is_readonly = 1;
/* USB-Serial descriptors */
const struct usb_interface_descriptor USB_IFACE_DESC(USB_IFACE_CONSOLE) =
@@ -286,7 +297,7 @@ int usb_getc(void)
{
int c;
- if (!is_enabled)
+ if (is_readonly || !is_enabled)
return -1;
if (QUEUE_REMOVE_UNITS(&rx_q, &c, 1))
@@ -299,7 +310,7 @@ int usb_puts(const char *outstr)
int ret;
struct queue state;
- if (is_readonly)
+ if (!is_enabled)
return EC_SUCCESS;
ret = usb_wait_console();
@@ -331,7 +342,7 @@ int usb_vprintf(const char *format, va_list args)
int ret;
struct queue state;
- if (is_readonly)
+ if (!is_enabled)
return EC_SUCCESS;
ret = usb_wait_console();
diff --git a/chip/g/usb_hid_keyboard.c b/chip/g/usb_hid_keyboard.c
new file mode 100644
index 0000000000..62167d93ca
--- /dev/null
+++ b/chip/g/usb_hid_keyboard.c
@@ -0,0 +1,159 @@
+/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "clock.h"
+#include "common.h"
+#include "config.h"
+#include "console.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "link_defs.h"
+#include "registers.h"
+#include "task.h"
+#include "timer.h"
+#include "util.h"
+#include "usb_descriptor.h"
+#include "usb_hid.h"
+
+/* Console output macro */
+#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args)
+
+#define HID_REPORT_SIZE 8
+
+/* HID descriptors */
+const struct usb_interface_descriptor USB_IFACE_DESC(USB_IFACE_HID) = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = USB_IFACE_HID_KEYBOARD,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_HID,
+ .bInterfaceSubClass = USB_HID_SUBCLASS_BOOT,
+ .bInterfaceProtocol = USB_HID_PROTOCOL_KEYBOARD,
+ .iInterface = USB_STR_HID_KEYBOARD_NAME,
+};
+const struct usb_endpoint_descriptor USB_EP_DESC(USB_IFACE_HID, 81) = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0x80 | USB_EP_HID_KEYBOARD,
+ .bmAttributes = 0x03 /* Interrupt endpoint */,
+ .wMaxPacketSize = HID_REPORT_SIZE,
+ .bInterval = 32 /* ms polling interval */
+};
+const struct usb_hid_descriptor USB_CUSTOM_DESC(USB_IFACE_HID, hid) = {
+ .bLength = 9,
+ .bDescriptorType = USB_HID_DT_HID,
+ .bcdHID = 0x0100,
+ .bCountryCode = 0x00, /* Hardware target country */
+ .bNumDescriptors = 1,
+ .desc = {
+ {.bDescriptorType = USB_HID_DT_REPORT,
+ .wDescriptorLength = 45}
+ }
+};
+
+/* HID : Report Descriptor */
+static const uint8_t report_desc[] = {
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x06, /* Usage (Keyboard) */
+ 0xA1, 0x01, /* Collection (Application) */
+ 0x05, 0x07, /* Usage Page (Key Codes) */
+ 0x19, 0xE0, /* Usage Minimum (224) */
+ 0x29, 0xE7, /* Usage Maximum (231) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x95, 0x08, /* Report Count (8) */
+ 0x81, 0x02, /* Input (Data, Variable, Absolute), ;Modifier byte */
+
+ 0x95, 0x01, /* Report Count (1) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x81, 0x01, /* Input (Constant), ;Reserved byte */
+
+ 0x95, 0x06, /* Report Count (6) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0x65, /* Logical Maximum(101) */
+ 0x05, 0x07, /* Usage Page (Key Codes) */
+ 0x19, 0x00, /* Usage Minimum (0) */
+ 0x29, 0x65, /* Usage Maximum (101) */
+ 0x81, 0x00, /* Input (Data, Array), ;Key arrays (6 bytes) */
+ 0xC0, /* End Collection */
+ 0x00 /* Padding */
+};
+
+static uint8_t hid_ep_buf[HID_REPORT_SIZE];
+static struct g_usb_desc hid_ep_desc;
+
+void set_keyboard_report(uint64_t rpt)
+{
+ memcpy(hid_ep_buf, &rpt, sizeof(rpt));
+ hid_ep_desc.flags = DIEPDMA_LAST | DIEPDMA_BS_HOST_RDY | DIEPDMA_IOC |
+ DIEPDMA_TXBYTES(HID_REPORT_SIZE);
+ /* enable TX */
+ GR_USB_DIEPCTL(USB_EP_HID_KEYBOARD) |= DXEPCTL_CNAK | DXEPCTL_EPENA;
+}
+
+static void hid_tx(void)
+{
+ /* clear IT */
+ GR_USB_DIEPINT(USB_EP_HID_KEYBOARD) = 0xffffffff;
+ return;
+}
+
+static void hid_reset(void)
+{
+ hid_ep_desc.flags = DIEPDMA_LAST | DIEPDMA_BS_HOST_BSY | DIEPDMA_IOC;
+ hid_ep_desc.addr = hid_ep_buf;
+ GR_USB_DIEPDMA(USB_EP_HID_KEYBOARD) = (uint32_t)&hid_ep_desc;
+ GR_USB_DIEPCTL(USB_EP_HID_KEYBOARD) = DXEPCTL_MPS(HID_REPORT_SIZE) |
+ DXEPCTL_USBACTEP | DXEPCTL_EPTYPE_INT |
+ DXEPCTL_TXFNUM(USB_EP_HID_KEYBOARD);
+ GR_USB_DAINTMSK |= DAINT_INEP(USB_EP_HID_KEYBOARD);
+}
+
+USB_DECLARE_EP(USB_EP_HID_KEYBOARD, hid_tx, hid_tx, hid_reset);
+
+static int hid_iface_request(struct usb_setup_packet *req)
+{
+ if ((req->bmRequestType & USB_DIR_IN) &&
+ req->bRequest == USB_REQ_GET_DESCRIPTOR &&
+ req->wValue == (USB_HID_DT_REPORT << 8)) {
+ /* Setup : HID specific : Get Report descriptor */
+ return load_in_fifo(report_desc,
+ MIN(req->wLength,
+ sizeof(report_desc)));
+ }
+
+ /* Anything else we'll stall */
+ return -1;
+}
+USB_DECLARE_IFACE(USB_IFACE_HID_KEYBOARD, hid_iface_request);
+
+#ifdef CR50_DEV
+/* Just for debugging */
+static int command_hid(int argc, char **argv)
+{
+ uint8_t keycode = 0x0a; /* 'G' key */
+
+ if (argc >= 2) {
+ char *e;
+
+ keycode = strtoi(argv[1], &e, 16);
+ if (*e)
+ return EC_ERROR_PARAM1;
+ }
+
+ /* press then release the key */
+ set_keyboard_report((uint32_t)keycode << 16);
+ udelay(50 * MSEC);
+ set_keyboard_report(0x000000);
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(hid, command_hid,
+ "[<HID keycode>]",
+ "test USB HID driver");
+#endif
diff --git a/chip/g/usb_spi.c b/chip/g/usb_spi.c
index bab7bae6c4..a5f6ddd175 100644
--- a/chip/g/usb_spi.c
+++ b/chip/g/usb_spi.c
@@ -3,6 +3,7 @@
* found in the LICENSE file.
*/
+#include "ccd_config.h"
#include "common.h"
#include "link_defs.h"
#include "gpio.h"
@@ -13,6 +14,10 @@
#include "usb_spi.h"
#include "util.h"
+#ifdef CONFIG_STREAM_SIGNATURE
+#include "signing.h"
+#endif
+
#define CPUTS(outstr) cputs(CC_USB, outstr)
#define CPRINTS(format, args...) cprints(CC_USB, format, ## args)
@@ -39,65 +44,85 @@ static uint16_t usb_spi_read_packet(struct usb_spi_config const *config)
static void usb_spi_write_packet(struct usb_spi_config const *config,
uint8_t count)
{
- QUEUE_ADD_UNITS(config->tx_queue, config->buffer, count);
-}
+#ifdef CONFIG_STREAM_SIGNATURE
+ /*
+ * This hook allows mn50 to sign SPI data read from newly
+ * manufactured H1 devieces. The data is added to a running
+ * hash until a completion message is received.
+ */
+ sig_append(stream_spi, config->buffer, count);
+#endif
-static int rx_valid(struct usb_spi_config const *config)
-{
- return (config->usb->out_desc->flags & DOEPDMA_BS_MASK) ==
- DOEPDMA_BS_DMA_DONE;
+ QUEUE_ADD_UNITS(config->tx_queue, config->buffer, count);
}
void usb_spi_deferred(struct usb_spi_config const *config)
{
+ uint16_t count;
+ int write_count;
+ int read_count;
+ int read_length;
+ uint16_t res;
+ int rv = EC_SUCCESS;
+
/*
* If our overall enabled state has changed we call the board specific
* enable or disable routines and save our new state.
*/
- int enabled = (config->state->enabled_host &
- config->state->enabled_device);
+ int enabled = !!(config->state->enabled_host &
+ config->state->enabled_device);
if (enabled ^ config->state->enabled) {
if (enabled)
- usb_spi_board_enable(config);
+ rv = usb_spi_board_enable(config);
else
usb_spi_board_disable(config);
- config->state->enabled = enabled;
+ /* Only update our state if we were successful. */
+ if (rv == EC_SUCCESS)
+ config->state->enabled = enabled;
}
/*
* And if there is a USB packet waiting we process it and generate a
* response.
*/
- if (!rx_valid(config)) {
- uint16_t count = usb_spi_read_packet(config);
- uint8_t write_count = config->buffer[0];
- uint8_t read_count = config->buffer[1];
- uint16_t res;
-
- if (!read_count && !write_count)
- return;
-
- if (!config->state->enabled) {
- res = USB_SPI_DISABLED;
- } else if (write_count > USB_SPI_MAX_WRITE_COUNT ||
- write_count != (count - HEADER_SIZE)) {
- res = USB_SPI_WRITE_COUNT_INVALID;
- } else if (read_count > USB_SPI_MAX_READ_COUNT) {
- res = USB_SPI_READ_COUNT_INVALID;
- } else {
- res = usb_spi_map_error(
- spi_transaction(SPI_FLASH_DEVICE,
- config->buffer + HEADER_SIZE,
- write_count,
- config->buffer + HEADER_SIZE,
- read_count));
- }
-
- memcpy(config->buffer, &res, HEADER_SIZE);
- usb_spi_write_packet(config, read_count + HEADER_SIZE);
+ count = usb_spi_read_packet(config);
+ write_count = config->buffer[0];
+ read_count = config->buffer[1];
+
+ /* Handle SPI_READBACK_ALL case */
+ if (read_count == 255) {
+ /* Handle simultaneously clocked RX and TX */
+ read_count = SPI_READBACK_ALL;
+ read_length = write_count;
+ } else {
+ /* Normal case */
+ read_length = read_count;
+ }
+
+ if (!count || (!read_count && !write_count) ||
+ (!write_count && read_count == (uint8_t)SPI_READBACK_ALL))
+ return;
+
+ if (!config->state->enabled) {
+ res = USB_SPI_DISABLED;
+ } else if (write_count > USB_SPI_MAX_WRITE_COUNT ||
+ write_count != (count - HEADER_SIZE)) {
+ res = USB_SPI_WRITE_COUNT_INVALID;
+ } else if (read_length > USB_SPI_MAX_READ_COUNT) {
+ res = USB_SPI_READ_COUNT_INVALID;
+ } else {
+ res = usb_spi_map_error(
+ spi_transaction(SPI_FLASH_DEVICE,
+ config->buffer + HEADER_SIZE,
+ write_count,
+ config->buffer + HEADER_SIZE,
+ read_count));
}
+
+ memcpy(config->buffer, &res, HEADER_SIZE);
+ usb_spi_write_packet(config, read_length + HEADER_SIZE);
}
static void usb_spi_written(struct consumer const *consumer, size_t count)
@@ -119,7 +144,17 @@ struct consumer_ops const usb_spi_consumer_ops = {
void usb_spi_enable(struct usb_spi_config const *config, int enabled)
{
- config->state->enabled_device = enabled ? 0xf : 0;
+ config->state->enabled_device = 0;
+ if (enabled) {
+#ifdef CONFIG_CASE_CLOSED_DEBUG_V1
+ if (ccd_is_cap_enabled(CCD_CAP_AP_FLASH))
+ config->state->enabled_device |= USB_SPI_AP;
+ if (ccd_is_cap_enabled(CCD_CAP_EC_FLASH))
+ config->state->enabled_device |= USB_SPI_EC;
+#else
+ config->state->enabled_device = USB_SPI_ALL;
+#endif
+ }
hook_call_deferred(config->deferred, 0);
}
diff --git a/chip/g/usb_spi.h b/chip/g/usb_spi.h
index 7a31e41d81..72364ab469 100644
--- a/chip/g/usb_spi.h
+++ b/chip/g/usb_spi.h
@@ -66,8 +66,24 @@ enum usb_spi_request {
USB_SPI_REQ_DISABLE = 0x0001,
USB_SPI_REQ_ENABLE_AP = 0x0002,
USB_SPI_REQ_ENABLE_EC = 0x0003,
+ USB_SPI_REQ_ENABLE_H1 = 0x0004,
+ USB_SPI_REQ_RESET = 0x0005,
+ USB_SPI_REQ_BOOT_CFG = 0x0006,
+ USB_SPI_REQ_SOCKET = 0x0007,
+ USB_SPI_REQ_SIGNING_START = 0x0008,
+ USB_SPI_REQ_SIGNING_SIGN = 0x0009,
};
+/* USB SPI device bitmasks */
+enum usb_spi {
+ USB_SPI_DISABLE = 0,
+ USB_SPI_AP = (1 << 0),
+ USB_SPI_EC = (1 << 1),
+ USB_SPI_H1 = (1 << 2),
+ USB_SPI_ALL = USB_SPI_AP | USB_SPI_EC | USB_SPI_H1
+};
+
+
#define USB_SPI_MAX_WRITE_COUNT 62
#define USB_SPI_MAX_READ_COUNT 62
@@ -109,10 +125,8 @@ struct usb_spi_config {
*/
struct usb_spi_state *state;
- struct usb_stream_config const *usb;
-
/*
- * Interface and endpoint indicies.
+ * Interface and endpoint indices.
*/
int interface;
int endpoint;
@@ -125,7 +139,7 @@ struct usb_spi_config {
/*
- * Pointer to tx and rx queus and bounce buffer.
+ * Pointer to tx and rx queues and bounce buffer.
*/
uint8_t *buffer;
struct consumer const consumer;
@@ -170,7 +184,6 @@ extern struct consumer_ops const usb_spi_consumer_ops;
static struct usb_spi_state CONCAT2(NAME, _state_); \
struct usb_spi_config const NAME = { \
.state = &CONCAT2(NAME, _state_), \
- .usb = &CONCAT2(NAME, _usb_), \
.interface = INTERFACE, \
.endpoint = ENDPOINT, \
.deferred = &CONCAT2(NAME, _deferred__data), \
@@ -223,14 +236,10 @@ int usb_spi_interface(struct usb_spi_config const *config,
/*
* These functions should be implemented by the board to provide any board
* specific operations required to enable or disable access to the SPI device.
+ * usb_spi_board_enable should return EC_SUCCESS on success or an error
+ * otherwise.
*/
-void usb_spi_board_enable(struct usb_spi_config const *config);
+int usb_spi_board_enable(struct usb_spi_config const *config);
void usb_spi_board_disable(struct usb_spi_config const *config);
-/*
- * Returns true if SPI update is running, needed to properly handle SYS_RST_L
- * input state changes.
- */
-int usb_spi_update_in_progress(void);
-
#endif /* __CROS_EC_USB_SPI_H */
diff --git a/chip/g/usb_upgrade.c b/chip/g/usb_upgrade.c
index 46e88949fb..acbec230b7 100644
--- a/chip/g/usb_upgrade.c
+++ b/chip/g/usb_upgrade.c
@@ -7,6 +7,7 @@
#include "common.h"
#include "console.h"
#include "consumer.h"
+#include "extension.h"
#include "queue_policies.h"
#include "shared_mem.h"
#include "system.h"
@@ -65,7 +66,6 @@ enum rx_state {
rx_inside_block, /* Assembling a block to pass to the programmer. */
rx_outside_block, /* Waiting for the next block to start or for the
reset command. */
- rx_awaiting_reset /* Waiting for reset confirmation. */
};
enum rx_state rx_state_ = rx_idle;
@@ -74,7 +74,7 @@ static uint32_t block_size;
static uint32_t block_index;
/*
- * Verify that the contens of the USB rx queue is a valid transfer start
+ * Verify that the contents of the USB rx queue is a valid transfer start
* message from host, and if so - save its contents in the passed in
* update_frame_header structure.
*/
@@ -106,6 +106,82 @@ static int valid_transfer_start(struct consumer const *consumer, size_t count,
return 0;
return 1;
}
+static int try_vendor_command(struct consumer const *consumer, size_t count)
+{
+ struct update_frame_header ufh;
+ struct update_frame_header *cmd_buffer;
+ int rv = 0;
+
+ if (count < sizeof(ufh))
+ return 0; /* Too short to be a valid vendor command. */
+
+ /*
+ * Let's copy off the queue the upgrade frame header, to see if this
+ * is a channeled vendor command.
+ */
+ queue_peek_units(consumer->queue, &ufh, 0, sizeof(ufh));
+ if (be32toh(ufh.cmd.block_base) != CONFIG_EXTENSION_COMMAND)
+ return 0;
+
+ if (be32toh(ufh.block_size) != count) {
+ CPRINTS("%s: problem: block size and count mismatch (%d != %d)",
+ __func__, be32toh(ufh.block_size), count);
+ return 0;
+ }
+
+ if (shared_mem_acquire(count, (char **)&cmd_buffer)
+ != EC_SUCCESS) {
+ CPRINTS("%s: problem: failed to allocate block of %d",
+ __func__, count);
+ return 0;
+ }
+
+ /* Get the entire command, don't remove it from the queue just yet. */
+ queue_peek_units(consumer->queue, cmd_buffer, 0, count);
+
+ /* Looks like this is a vendor command, let's verify it. */
+ if (usb_pdu_valid(&cmd_buffer->cmd,
+ count - offsetof(struct update_frame_header, cmd))) {
+ uint16_t *subcommand;
+ size_t response_size;
+ size_t request_size;
+ /*
+ * Should be enough for any vendor command/response. We'll
+ * generate an error if it is not.
+ */
+ uint8_t subcommand_body[32];
+
+ /* looks good, let's process it. */
+ rv = 1;
+
+ /* Now remove if from the queue. */
+ queue_advance_head(consumer->queue, count);
+
+ subcommand = (uint16_t *)(cmd_buffer + 1);
+ request_size = count - sizeof(struct update_frame_header) -
+ sizeof(*subcommand);
+
+ if (request_size > sizeof(subcommand_body)) {
+ CPRINTS("%s: vendor command payload too big (%d)",
+ __func__, request_size);
+ subcommand_body[0] = VENDOR_RC_REQUEST_TOO_BIG;
+ response_size = 1;
+ } else {
+ memcpy(subcommand_body, subcommand + 1, request_size);
+ response_size = sizeof(subcommand_body);
+ usb_extension_route_command(be16toh(*subcommand),
+ subcommand_body,
+ request_size,
+ &response_size);
+ }
+
+ QUEUE_ADD_UNITS(&upgrade_to_usb,
+ subcommand_body, response_size);
+ }
+ shared_mem_release(cmd_buffer);
+
+ return rv;
+}
/*
* When was last time a USB callback was called, in microseconds, free running
@@ -113,6 +189,12 @@ static int valid_transfer_start(struct consumer const *consumer, size_t count,
*/
static uint64_t prev_activity_timestamp;
+/*
+ * A flag indicating that at least one valid PDU containing flash update block
+ * has been received in the current transfer session.
+ */
+static uint8_t data_was_transferred;
+
/* Called to deal with data from the host */
static void upgrade_out_handler(struct consumer const *consumer, size_t count)
{
@@ -155,9 +237,13 @@ static void upgrade_out_handler(struct consumer const *consumer, size_t count)
};
} u;
+ /* Check is this is a channeled TPM extension command. */
+ if (try_vendor_command(consumer, count))
+ return;
+
if (!valid_transfer_start(consumer, count, &u.upfr)) {
/*
- * Someting is wrong, this payload is not a valid
+ * Something is wrong, this payload is not a valid
* update start PDU. Let'w indicate this by returning
* a single byte error code.
*/
@@ -173,26 +259,16 @@ static void upgrade_out_handler(struct consumer const *consumer, size_t count)
cmd),
&resp_size);
- if (!u.startup_resp.return_value)
+ if (!u.startup_resp.return_value) {
rx_state_ = rx_outside_block; /* We're in business. */
+ data_was_transferred = 0; /* No data received yet. */
+ }
/* Let the host know what upgrader had to say. */
QUEUE_ADD_UNITS(&upgrade_to_usb, &u.startup_resp, resp_size);
return;
}
- if (rx_state_ == rx_awaiting_reset) {
- /*
- * Any USB data received in this state triggers reset, no
- * response required.
- */
- CPRINTS("reboot hard");
- cflush();
- system_reset(SYSTEM_RESET_HARD);
- while (1)
- ;
- }
-
if (rx_state_ == rx_outside_block) {
/*
* Expecting to receive the beginning of the block or the
@@ -207,11 +283,15 @@ static void upgrade_out_handler(struct consumer const *consumer, size_t count)
if (command == UPGRADE_DONE) {
CPRINTS("FW update: done");
- fw_upgrade_complete();
+ if (data_was_transferred) {
+ fw_upgrade_complete();
+ data_was_transferred = 0;
+ }
+
resp_value = 0;
QUEUE_ADD_UNITS(&upgrade_to_usb,
&resp_value, 1);
- rx_state_ = rx_awaiting_reset;
+ rx_state_ = rx_idle;
return;
}
}
@@ -308,6 +388,11 @@ static void upgrade_out_handler(struct consumer const *consumer, size_t count)
*/
fw_upgrade_command_handler(block_buffer, block_index, &resp_size);
+ /*
+ * There was at least an attempt to program the flash, set the
+ * flag.
+ */
+ data_was_transferred = 1;
resp_value = block_buffer[0];
QUEUE_ADD_UNITS(&upgrade_to_usb, &resp_value, sizeof(resp_value));
rx_state_ = rx_outside_block;
diff --git a/chip/g/watchdog.c b/chip/g/watchdog.c
index 0c565f3850..aa78fa675c 100644
--- a/chip/g/watchdog.c
+++ b/chip/g/watchdog.c
@@ -33,7 +33,7 @@ void IRQ_HANDLER(GC_IRQNUM_WATCHDOG0_WDOGINT)(void)
asm volatile("mov r0, lr\n"
"mov r1, sp\n"
/* Must push registers in pairs to keep 64-bit aligned
- * stack for ARM EABI. This also conveninently saves
+ * stack for ARM EABI. This also conveniently saves
* R0=LR so we can pass it to task_resched_if_needed. */
"push {r0, lr}\n"
/* We've lowered our runlevel, so just rebooting the ARM
@@ -52,7 +52,7 @@ void IRQ_HANDLER(GC_IRQNUM_WATCHDOG0_WDOGINT)(void)
"b task_resched_if_needed\n"
: : [irq] "i" (GC_IRQNUM_WATCHDOG0_WDOGINT));
}
-const struct irq_priority IRQ_PRIORITY(GC_IRQNUM_WATCHDOG0_WDOGINT)
+const struct irq_priority __keep IRQ_PRIORITY(GC_IRQNUM_WATCHDOG0_WDOGINT)
__attribute__((section(".rodata.irqprio")))
= {GC_IRQNUM_WATCHDOG0_WDOGINT, 0};
/* put the watchdog at the highest priority */
diff --git a/extra/usb_updater/Makefile b/extra/usb_updater/Makefile
index 9120b7d4dc..42ff2bfdb2 100644
--- a/extra/usb_updater/Makefile
+++ b/extra/usb_updater/Makefile
@@ -4,8 +4,7 @@
CC ?= gcc
PKG_CONFIG ?= pkg-config
-PROGRAM := usb_updater
-SOURCE := $(PROGRAM).c
+PROGRAMS := gsctool usb_updater2
LIBS :=
LFLAGS :=
CFLAGS := -std=gnu99 \
@@ -29,17 +28,45 @@ endif
#
# Add libusb-1.0 required flags
#
-LIBS += $(shell $(PKG_CONFIG) --libs libusb-1.0 libcrypto)
-CFLAGS += $(shell $(PKG_CONFIG) --cflags libusb-1.0 libcrypto)
+LIBS += $(shell $(PKG_CONFIG) --libs libusb-1.0)
+CFLAGS += $(shell $(PKG_CONFIG) --cflags libusb-1.0)
+CFLAGS += -I../../include -I../../util -I../../test
-# NOTE: This may be board-specific
BOARD ?= cr50
-CFLAGS += -I../../include -I../../board/$(BOARD) -I ../../chip/g -I../../util
+LIBS_g = $(shell $(PKG_CONFIG) --libs libcrypto)
+CFLAGS_g = $(shell $(PKG_CONFIG) --cflags libcrypto)
+CFLAGS_g += -I../../board/$(BOARD) -I ../../chip/g
-$(PROGRAM): $(SOURCE) Makefile
- $(CC) $(CFLAGS) $(SOURCE) $(LFLAGS) $(LIBS) -o $@
+LIBS_common = -lfmap
+
+all: $(PROGRAMS)
+
+GSCTOOL_SOURCES := gsctool.c desc_parser.c verify_ro.c
+GSCTOOL_OBJS := $(patsubst %.c,%.o,$(GSCTOOL_SOURCES))
+DEPS := $(patsubst %.c,%.d,$(GSCTOOL_SOURCES))
+
+# chip/g updater
+gsctool: $(GSCTOOL_OBJS) Makefile
+ $(CC) $(GSCTOOL_OBJS) $(LFLAGS) $(LIBS) $(LIBS_g) -o $@
+
+%.o: %.c
+ $(CC) $(CFLAGS) $(CFLAGS_g) -c -MMD -MF $(basename $@).d -o $@ $<
+
+gsctool.o: generated_version.h
+
+# common EC code USB updater
+usb_updater2: usb_updater2.c Makefile
+ $(CC) $(CFLAGS) $< $(LFLAGS) $(LIBS) $(LIBS_common) -o $@
.PHONY: clean
+generated_version.h: $(GSCTOOL_SOURCES)
+ @../../util/getversion.sh > $@
+
clean:
- rm -rf $(PROGRAM) *~
+ rm -rf $(PROGRAMS) *~ *.o *.d dp generated_version.h
+
+parser_debug: desc_parser.c
+ gcc -g -O0 -DTEST_PARSER desc_parser.c -o dp
+
+-include $(DEPS)
diff --git a/extra/usb_updater/desc_parser.c b/extra/usb_updater/desc_parser.c
new file mode 100644
index 0000000000..04f144457c
--- /dev/null
+++ b/extra/usb_updater/desc_parser.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <malloc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "desc_parser.h"
+
+static FILE *hash_file_;
+static int line_count_;
+static int section_count_;
+
+/* Size of the retrieved string or negative OS error value. */
+static ssize_t get_next_line(char *next_line, size_t line_size)
+{
+ size_t index = 0;
+
+ while (fgets(next_line + index, line_size - index, hash_file_)) {
+ line_count_++;
+
+ if (next_line[index] == '#')
+ continue; /* Skip the comment */
+
+ if (next_line[index] == '\n') {
+ /*
+ * This is an empty line, return all collected data,
+ * pontintially an array of size zero if this is a
+ * repeated empty line.
+ */
+ next_line[index] = '\0';
+ return index;
+ }
+
+ /* Make sure next string overwrites this string's newline. */
+ index += strlen(next_line + index) - 1;
+
+ if (index >= (line_size - 1)) {
+ fprintf(stderr, "%s: Input overflow in line %d\n",
+ __func__, line_count_);
+ return -EOVERFLOW;
+ }
+ }
+
+ if (index) {
+ /*
+ * This must be the last line in the file with no empty line
+ * after it. Drop the closing newline, if it is there.
+ */
+ if (next_line[index] == '\n')
+ next_line[index--] = '\0';
+
+ return index;
+ }
+ return errno ? -errno : -ENODATA;
+}
+
+static int get_next_token(char *input, size_t expected_size, char **output)
+{
+ char *next_colon;
+
+ next_colon = strchr(input, ':');
+ if (next_colon)
+ *next_colon = '\0';
+ if (!next_colon || (expected_size &&
+ strlen(input) != expected_size)) {
+ fprintf(stderr, "Invalid entry in section %d\n",
+ section_count_);
+ return -EINVAL;
+ }
+
+ *output = next_colon + 1;
+ return 0;
+}
+
+static int get_hex_value(char *input, char **output)
+{
+ char *e;
+ long int value;
+
+ if (strchr(input, ':'))
+ get_next_token(input, 0, output);
+ else
+ *output = NULL;
+
+ value = strtol(input, &e, 16);
+ if (e && *e) {
+ fprintf(stderr, "Invalid hex value %s in section %d\n",
+ input, section_count_);
+ return -EINVAL;
+ }
+
+ return value;
+}
+
+static int parse_range(char *next_line,
+ size_t line_len,
+ struct addr_range *parsed_range)
+{
+ char *line_cursor;
+ char *next_token;
+ int is_a_hash_range;
+ struct result_node *node;
+ int value;
+
+ section_count_++;
+ line_cursor = next_line;
+
+ /* Range type. */
+ if (get_next_token(line_cursor, 1, &next_token))
+ return -EINVAL;
+
+ switch (*line_cursor) {
+ case 'a':
+ parsed_range->range_type = AP_RANGE;
+ break;
+ case 'e':
+ parsed_range->range_type = EC_RANGE;
+ break;
+ case 'g':
+ parsed_range->range_type = EC_GANG_RANGE;
+ break;
+ default:
+ fprintf(stderr, "Invalid range type %c in section %d\n",
+ *line_cursor, section_count_);
+ return -EINVAL;
+ }
+ line_cursor = next_token;
+
+ /* Hash or dump? */
+ if (get_next_token(line_cursor, 1, &next_token))
+ return -EINVAL;
+
+ switch (*line_cursor) {
+ case 'd':
+ is_a_hash_range = 0;
+ break;
+ case 'h':
+ is_a_hash_range = 1;
+ break;
+ default:
+ fprintf(stderr, "Invalid entry kind %c in section %d\n",
+ *line_cursor, section_count_);
+ return -EINVAL;
+ }
+ line_cursor = next_token;
+
+ /* Range base address. */
+ value = get_hex_value(line_cursor, &next_token);
+ if (value < 0)
+ return -EINVAL;
+ parsed_range->base_addr = value;
+
+ /* Range size. */
+ line_cursor = next_token;
+ value = get_hex_value(line_cursor, &next_token);
+ if (value < 0)
+ return -EINVAL;
+ parsed_range->range_size = value;
+
+ if (!next_token && is_a_hash_range) {
+ fprintf(stderr, "Missing hash in section %d\n", section_count_);
+ return -EINVAL;
+ }
+
+ if (next_token && !is_a_hash_range) {
+ fprintf(stderr, "Unexpected data in section %d\n",
+ section_count_);
+ return -EINVAL;
+ }
+
+ parsed_range->variant_count = 0;
+ if (!is_a_hash_range)
+ return 0; /* No more input for dump ranges. */
+
+ node = parsed_range->variants;
+ do { /* While line is not over. */
+ char c;
+ int i = 0;
+
+ line_cursor = next_token;
+ next_token = strchr(line_cursor, ':');
+ if (next_token)
+ *next_token++ = '\0';
+ if (strlen(line_cursor) != (2 * sizeof(*node))) {
+ fprintf(stderr,
+ "Invalid hash %zd size %zd in section %d\n",
+ parsed_range->variant_count + 1,
+ strlen(line_cursor), section_count_);
+ return -EINVAL;
+ }
+
+ while ((c = *line_cursor++) != 0) {
+ uint8_t nibble;
+
+ if (!isxdigit(c)) {
+ fprintf(stderr,
+ "Invalid hash %zd value in section %d\n",
+ parsed_range->variant_count + 1,
+ section_count_);
+ return -EINVAL;
+ }
+
+ if (c <= '9')
+ nibble = c - '0';
+ else if (c >= 'a')
+ nibble = c - 'a' + 10;
+ else
+ nibble = c - 'A' + 10;
+
+ if (i & 1)
+ node->expected_result[i / 2] |= nibble;
+ else
+ node->expected_result[i / 2] = nibble << 4;
+
+ i++;
+ }
+
+ node++;
+ parsed_range->variant_count++;
+
+ } while (next_token);
+
+ return 0;
+}
+
+int parser_get_next_range(struct addr_range **range)
+{
+ char next_line[1000]; /* Should be enough for the largest descriptor. */
+ ssize_t entry_size;
+ struct addr_range *new_range;
+ int rv;
+
+ /*
+ * This is used to verify consistency of the description database,
+ * namely that all hash sections include the same numger of hash
+ * variants.
+ */
+ static size_t variant_count;
+
+ /*
+ * We come here after hash descriptor database file was opened and the
+ * current board's section has been found. Just in case check if the
+ * file has been opened.
+ */
+ if (!hash_file_ || !range)
+ return -EIO;
+
+ *range = NULL;
+ do {
+ entry_size = get_next_line(next_line, sizeof(next_line));
+ if (entry_size < 0)
+ return entry_size;
+ } while (!entry_size); /* Skip empty lines. */
+
+ if (entry_size == 4) /* Next board's entry must have been reached. */
+ return -ENODATA;
+
+ /* This sure will be enough to fit parsed structure contents. */
+ new_range = malloc(sizeof(*new_range) + entry_size);
+ if (!new_range) {
+ fprintf(stderr, "Failed to allocate %zd bytes\n",
+ sizeof(*new_range) + entry_size);
+ return -ENOMEM;
+ }
+
+ /* This must be a new descriptor section, lets parse it. */
+ rv = parse_range(next_line, entry_size, new_range);
+
+ if (rv) {
+ free(new_range);
+ return rv;
+ }
+
+ if (new_range->variant_count) {
+ /*
+ * A new range was found, if this is the first hash range we
+ * encountered, save its dimensions for future reference.
+ *
+ * If this is not the first one - verify that it has the same
+ * number of hash variants as all previous hash blocks.
+ */
+ if (!variant_count) {
+ variant_count = new_range->variant_count;
+ } else if (variant_count != new_range->variant_count) {
+ fprintf(stderr,
+ "Unexpected number of variants in section %d\n",
+ section_count_);
+ free(new_range);
+ return -EINVAL;
+ }
+ }
+
+ *range = new_range;
+ return 0;
+
+}
+
+int parser_find_board(const char *hash_file_name, const char *board_id)
+{
+ char next_line[1000]; /* Should be enough for the largest descriptor. */
+ ssize_t id_len = strlen(board_id);
+
+ hash_file_ = fopen(hash_file_name, "r");
+ if (!hash_file_) {
+ fprintf(stderr, "Error:%s can not open file '%s'\n",
+ strerror(errno), hash_file_name);
+ return errno;
+ }
+
+ while (1) {
+ ssize_t entry_size;
+
+ entry_size = get_next_line(next_line, sizeof(next_line));
+ if (entry_size < 0) {
+ fclose(hash_file_);
+ return entry_size;
+ }
+
+ if ((entry_size == id_len) &&
+ !memcmp(next_line, board_id, id_len))
+ return 0;
+ }
+
+ fclose(hash_file_);
+ hash_file_ = NULL;
+ return errno;
+}
+
+void parser_done(void)
+{
+ if (!hash_file_)
+ return;
+
+ fclose(hash_file_);
+ hash_file_ = NULL;
+}
+
+#ifdef TEST_PARSER
+int main(int argc, char **argv)
+{
+ const char *board_name = "QZUX";
+ char next_line[1000]; /* Should be enough for the largest descriptor. */
+ int rv;
+ int count;
+
+ if (argc < 2) {
+ fprintf(stderr, "Name of the file to parse is required.\n");
+ return -1;
+ }
+
+ if (parser_find_board(argv[1], board_name)) {
+ fprintf(stderr, "Board %s NOT found\n", board_name);
+ return -1;
+ }
+
+ count = 0;
+ do {
+ struct addr_range *range;
+
+ rv = parser_get_next_range(&range);
+ count++;
+ printf("Section %d, rv %d\n", count, rv);
+ free(range); /* Freeing NULL is OK. */
+
+ } while (rv != -ENODATA);
+
+ return 0;
+}
+#endif
diff --git a/extra/usb_updater/desc_parser.d b/extra/usb_updater/desc_parser.d
new file mode 100644
index 0000000000..6b996e0c08
--- /dev/null
+++ b/extra/usb_updater/desc_parser.d
@@ -0,0 +1 @@
+desc_parser.o: desc_parser.c desc_parser.h
diff --git a/extra/usb_updater/desc_parser.h b/extra/usb_updater/desc_parser.h
new file mode 100644
index 0000000000..faa80d1a63
--- /dev/null
+++ b/extra/usb_updater/desc_parser.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef __EXTRA_USB_UPDATER_DESC_PARSER_H
+#define __EXTRA_USB_UPDATER_DESC_PARSER_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+struct result_node {
+ uint8_t expected_result[32];
+};
+
+enum range_type_t {
+ NOT_A_RANGE,
+ AP_RANGE,
+ EC_RANGE,
+ EC_GANG_RANGE,
+};
+
+struct addr_range {
+ enum range_type_t range_type;
+ uint32_t base_addr;
+ uint32_t range_size;
+ size_t variant_count; /* Set to zero for dump ranges. */
+ struct result_node variants[0];
+};
+
+/* Board description retrieval API includes the following functions. */
+
+/*
+ * In the given hash database file find board by its ID. Return zero on
+ * success, or OS error of error. In particular ENODATA is returned if the
+ * section for the required board ID is not found in the file.
+ */
+int parser_find_board(const char *hash_file_name, const char board_id[4]);
+
+/*
+ * Find next range for the previousely defined board, parse it into the
+ * addr_range structure and return pointer to the parsed structure to the
+ * caller, set pointer to NULL if no more entries are available or in case of
+ * error.
+ *
+ * Caller of this function is responsible for returning memory allocated for
+ * the entry.
+ *
+ * Return value set to zero on success, or to OS error if one occurs. EIO is
+ * used if an attmept to get next range is made before hash database file was
+ * opened and board entry in it was found.
+ */
+int parser_get_next_range(struct addr_range **range);
+
+/* Close the hash database file. */
+void parser_done(void);
+
+#endif // __EXTRA_USB_UPDATER_DESC_PARSER_H
diff --git a/extra/usb_updater/desc_parser.o b/extra/usb_updater/desc_parser.o
new file mode 100644
index 0000000000..ea9f6a9a2d
--- /dev/null
+++ b/extra/usb_updater/desc_parser.o
Binary files differ
diff --git a/extra/usb_updater/ecusb b/extra/usb_updater/ecusb
new file mode 120000
index 0000000000..c06ee0f51b
--- /dev/null
+++ b/extra/usb_updater/ecusb
@@ -0,0 +1 @@
+../tigertool/ecusb/ \ No newline at end of file
diff --git a/extra/usb_updater/fw_update.py b/extra/usb_updater/fw_update.py
index 73393c8fb2..367c0cad14 100755
--- a/extra/usb_updater/fw_update.py
+++ b/extra/usb_updater/fw_update.py
@@ -5,6 +5,8 @@
# Upload firmware over USB
+from __future__ import print_function
+
import argparse
import array
import json
@@ -19,10 +21,10 @@ import usb
debug = False
def debuglog(msg):
if debug:
- print msg
+ print(msg)
-def logoutput(msg):
- print msg
+def log(msg):
+ print(msg)
sys.stdout.flush()
@@ -189,7 +191,7 @@ class Supdate(object):
read = self.wr_command(cmd, read_count=4)
if len(read) == 4:
- print "Finished flashing"
+ log("Finished flashing")
return
raise Exception("Update", "Stop failed [%s]" % read)
@@ -212,7 +214,7 @@ class Supdate(object):
region, self._brdcfg['regions'][region][0], offset))
length = self._brdcfg['regions'][region][1]
- print "Sending"
+ log("Sending")
# Go to the correct region in the ec.bin file.
self._binfile.seek(offset)
@@ -246,7 +248,7 @@ class Supdate(object):
self.wr_command(data, read_count=0)
break
except:
- print "Timeout fail"
+ log("Timeout fail")
todo -= packetsize
# Done with this packet, move to the next one.
length -= pagesize
@@ -285,8 +287,8 @@ class Supdate(object):
raise Exception("Update", "Protocol version 0 not supported")
elif len(read) == expected:
base, version = struct.unpack(">II", read)
- print "Update protocol v. %d" % version
- print "Available flash region base: %x" % base
+ log("Update protocol v. %d" % version)
+ log("Available flash region base: %x" % base)
else:
raise Exception("Update", "Start command returned %d bytes" % len(read))
@@ -302,7 +304,7 @@ class Supdate(object):
if (self._offset >= self._brdcfg['regions'][region][0]) and \
(self._offset < (self._brdcfg['regions'][region][0] + \
self._brdcfg['regions'][region][1])):
- print "Active region: %s" % region
+ log("Active region: %s" % region)
self._region = region
@@ -333,26 +335,26 @@ class Supdate(object):
if debug:
pprint(data)
- print "Board is %s" % self._brdcfg['board']
+ log("Board is %s" % self._brdcfg['board'])
# Cast hex strings to int.
self._brdcfg['flash'] = int(self._brdcfg['flash'], 0)
self._brdcfg['vid'] = int(self._brdcfg['vid'], 0)
self._brdcfg['pid'] = int(self._brdcfg['pid'], 0)
- print "Flash Base is %x" % self._brdcfg['flash']
+ log("Flash Base is %x" % self._brdcfg['flash'])
self._flashsize = 0
for region in self._brdcfg['regions']:
base = int(self._brdcfg['regions'][region][0], 0)
length = int(self._brdcfg['regions'][region][1], 0)
- print "region %s\tbase:0x%08x size:0x%08x" % (
- region, base, length)
+ log("region %s\tbase:0x%08x size:0x%08x" % (
+ region, base, length))
self._flashsize += length
# Convert these to int because json doesn't support hex.
self._brdcfg['regions'][region][0] = base
self._brdcfg['regions'][region][1] = length
- print "Flash Size: 0x%x" % self._flashsize
+ log("Flash Size: 0x%x" % self._flashsize)
def load_file(self, binfile):
"""Open and verify size of the target ec.bin file.
@@ -405,11 +407,11 @@ def main():
# Start transfer and erase.
p.start()
# Upload the bin file
- print "Uploading %s" % binfile
+ log("Uploading %s" % binfile)
p.write_file()
# Finalize
- print "Done. Finalizing."
+ log("Done. Finalizing.")
p.stop()
if __name__ == "__main__":
diff --git a/extra/usb_updater/generated_version.h b/extra/usb_updater/generated_version.h
new file mode 100644
index 0000000000..8d4ebedc11
--- /dev/null
+++ b/extra/usb_updater/generated_version.h
@@ -0,0 +1,11 @@
+/* This file is generated by util/getversion.sh */
+/* Version string for use by common/version.c */
+#define CROS_EC_VERSION "bob_v1.1.5705-45cf222"
+/* Version string, truncated to 31 chars (+ terminating null = 32) */
+#define CROS_EC_VERSION32 "bob_v1.1.5705-45cf222"
+/* Sub-fields for use in Makefile.rules and to form build info string
+ * in common/version.c. */
+#define VERSION "bob_v1.1.5705-45cf222"
+#define BUILDER "henrysun@henrysun.sha.corp.google.com"
+/* Repo is clean, use the commit date of the last commit */
+#define DATE "2019-12-16 09:58:20"
diff --git a/extra/usb_updater/gsctool.c b/extra/usb_updater/gsctool.c
new file mode 100644
index 0000000000..66bf91156c
--- /dev/null
+++ b/extra/usb_updater/gsctool.c
@@ -0,0 +1,2400 @@
+/*
+ * Copyright 2015 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <asm/byteorder.h>
+#include <ctype.h>
+#include <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <libusb.h>
+#include <openssl/sha.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "config.h"
+
+#include "ccd_config.h"
+#include "compile_time_macros.h"
+#include "generated_version.h"
+#include "gsctool.h"
+#include "misc_util.h"
+#include "signed_header.h"
+#include "tpm_vendor_cmds.h"
+#include "upgrade_fw.h"
+#include "usb_descriptor.h"
+#include "verify_ro.h"
+
+#ifdef DEBUG
+#define debug printf
+#else
+#define debug(fmt, args...)
+#endif
+
+/*
+ * This file contains the source code of a Linux application used to update
+ * CR50 device firmware.
+ *
+ * The CR50 firmware image consists of multiple sections, of interest to this
+ * app are the RO and RW code sections, two of each. When firmware update
+ * session is established, the CR50 device reports locations of backup RW and RO
+ * sections (those not used by the device at the time of transfer).
+ *
+ * Based on this information this app carves out the appropriate sections form
+ * the full CR50 firmware binary image and sends them to the device for
+ * programming into flash. Once the new sections are programmed and the device
+ * is restarted, the new RO and RW are used if they pass verification and are
+ * logically newer than the existing sections.
+ *
+ * There are two ways to communicate with the CR50 device: USB and /dev/tpm0
+ * (when this app is running on a chromebook with the CR50 device). Originally
+ * different protocols were used to communicate over different channels,
+ * starting with version 3 the same protocol is used.
+ *
+ * This app provides backwards compatibility to ensure that earlier CR50
+ * devices still can be updated.
+ *
+ *
+ * The host (either a local AP or a workstation) is the master of the firmware
+ * update protocol, it sends data to the cr50 device, which proceeses it and
+ * responds.
+ *
+ * The encapsultation format is different between the /dev/tpm0 and USB cases:
+ *
+ * 4 bytes 4 bytes 4 bytes variable size
+ * +-----------+--------------+---------------+----------~~--------------+
+ * + total size| block digest | dest address | data |
+ * +-----------+--------------+---------------+----------~~--------------+
+ * \ \ /
+ * \ \ /
+ * \ +----- FW update PDU sent over /dev/tpm0 -----------+
+ * \ /
+ * +--------- USB frame, requires total size field ------------+
+ *
+ * The update protocol data unints (PDUs) are passed over /dev/tpm0, the
+ * encapsulation includes integritiy verification and destination address of
+ * the data (more of this later). /dev/tpm0 transactions pretty much do not
+ * have size limits, whereas the USB data is sent in chunks of the size
+ * determined when the USB connestion is set up. This is why USB requires an
+ * additional encapsulation into frames to communicate the PDU size to the
+ * client side so that the PDU can be reassembled before passing to the
+ * programming function.
+ *
+ * In general, the protocol consists of two phases: connection establishment
+ * and actual image transfer.
+ *
+ * The very first PDU of the transfer session is used to establish the
+ * connection. The first PDU does not have any data, and the dest. address
+ * field is set to zero. Receiving such a PDU signals the programming function
+ * that the host intends to transfer a new image.
+ *
+ * The response to the first PDU varies depending on the protocol version.
+ *
+ * Note that protocol versions before 5 are described here for completeness,
+ * but are not supported any more by this utility.
+ *
+ * Version 1 is used over /dev/tpm0. The response is either 4 or 1 bytes in
+ * size. The 4 byte response is the *base address* of the backup RW section,
+ * no support for RO updates. The one byte response is an error indication,
+ * possibly reporting flash erase failure, command format error, etc.
+ *
+ * Version 2 is used over USB. The response is 8 bytes in size. The first four
+ * bytes are either the *base address* of the backup RW section (still no RO
+ * updates), or an error code, the same as in Version 1. The second 4 bytes
+ * are the protocol version number (set to 2).
+ *
+ * All versions above 2 behave the same over /dev/tpm0 and USB.
+ *
+ * Version 3 response is 16 bytes in size. The first 4 bytes are the error code
+ * the second 4 bytes are the protocol version (set to 3) and then 4 byte
+ * *offset* of the RO section followed by the 4 byte *offset* of the RW section.
+ *
+ * Version 4 response in addition to version 3 provides header revision fields
+ * for active RO and RW images running on the target.
+ *
+ * Once the connection is established, the image to be programmed into flash
+ * is transferred to the CR50 in 1K PDUs. In versions 1 and 2 the address in
+ * the header is the absolute address to place the block to, in version 3 and
+ * later it is the offset into the flash.
+ *
+ * Protocol version 5 includes RO and RW key ID information into the first PDU
+ * response. The key ID could be used to tell between prod and dev signing
+ * modes, among other things.
+ *
+ * Protocol version 6 does not change the format of the first PDU response,
+ * but it indicates the target's ablitiy to channel TPM vendor commands
+ * through USB connection.
+ *
+ * When channeling TPM vendor commands the USB frame looks as follows:
+ *
+ * 4 bytes 4 bytes 4 bytes 2 bytes variable size
+ * +-----------+--------------+---------------+-----------+------~~~-------+
+ * + total size| block digest | EXT_CMD | Vend. sub.| data |
+ * +-----------+--------------+---------------+-----------+------~~~-------+
+ *
+ * Where 'Vend. sub' is the vendor subcommand, and data field is subcommand
+ * dependent. The target tells between update PDUs and encapsulated vendor
+ * subcommands by looking at the EXT_CMD value - it is set to 0xbaccd00a and
+ * as such is guaranteed not to be a valid update PDU destination address.
+ *
+ * The vendor command response size is not fixed, it is subcommand dependent.
+ *
+ * The CR50 device responds to each update PDU with a confirmation which is 4
+ * bytes in size in protocol version 2, and 1 byte in size in all other
+ * versions. Zero value means success, non zero value is the error code
+ * reported by CR50.
+ *
+ * Again, vendor command responses are subcommand specific.
+ */
+
+/* Look for Cr50 FW update interface */
+#define VID USB_VID_GOOGLE
+#define PID CONFIG_USB_PID
+#define SUBCLASS USB_SUBCLASS_GOOGLE_CR50
+#define PROTOCOL USB_PROTOCOL_GOOGLE_CR50_NON_HC_FW_UPDATE
+
+/*
+ * Need to create an entire TPM PDU when upgrading over /dev/tpm0 and need to
+ * have space to prepare the entire PDU.
+ */
+struct upgrade_pkt {
+ __be16 tag;
+ __be32 length;
+ __be32 ordinal;
+ __be16 subcmd;
+ union {
+ /*
+ * Upgrade PDUs as opposed to all other vendor and extension
+ * commands include two additional fields in the header.
+ */
+ struct {
+ __be32 digest;
+ __be32 address;
+ char data[0];
+ } upgrade;
+ struct {
+ char data[0];
+ } command;
+ };
+} __packed;
+
+
+/*
+ * This by far exceeds the largest vendor command response size we ever
+ * expect.
+ */
+#define MAX_BUF_SIZE 500
+
+/*
+ * Max. length of the board ID string representation.
+ *
+ * Board ID is either a 4-character ASCII alphanumeric string or an 8-digit
+ * hex.
+ */
+#define MAX_BOARD_ID_LENGTH 9
+
+/*
+ * Max. length of FW version in the format of <epoch>.<major>.<minor>
+ * (3 uint32_t string representation + 2 separators + NULL terminator).
+ */
+#define MAX_FW_VER_LENGTH 33
+
+static int verbose_mode;
+static uint32_t protocol_version;
+static char *progname;
+static char *short_opts = "abcd:F:fhIikMO:oPprstUuVv";
+static const struct option long_opts[] = {
+ /* name hasarg *flag val */
+ {"any", 0, NULL, 'a'},
+ {"binvers", 0, NULL, 'b'},
+ {"board_id", 2, NULL, 'i'},
+ {"ccd_info", 0, NULL, 'I'},
+ {"ccd_lock", 0, NULL, 'k'},
+ {"ccd_open", 0, NULL, 'o'},
+ {"ccd_unlock", 0, NULL, 'U'},
+ {"corrupt", 0, NULL, 'c'},
+ {"device", 1, NULL, 'd'},
+ {"factory", 1, NULL, 'F'},
+ {"fwver", 0, NULL, 'f'},
+ {"help", 0, NULL, 'h'},
+ {"machine", 0, NULL, 'M'},
+ {"openbox_rma", 1, NULL, 'O'},
+ {"password", 0, NULL, 'P'},
+ {"post_reset", 0, NULL, 'p'},
+ {"rma_auth", 2, NULL, 'r'},
+ {"systemdev", 0, NULL, 's'},
+ {"trunks_send", 0, NULL, 't'},
+ {"verbose", 0, NULL, 'V'},
+ {"version", 0, NULL, 'v'},
+ {"upstart", 0, NULL, 'u'},
+ {},
+};
+
+
+
+/* Helpers to convert between binary and hex ascii and back. */
+static char to_hexascii(uint8_t c)
+{
+ if (c <= 9)
+ return '0' + c;
+ return 'a' + c - 10;
+}
+
+static int from_hexascii(char c)
+{
+ /* convert to lower case. */
+ c = tolower(c);
+
+ if (c < '0' || c > 'f' || ((c > '9') && (c < 'a')))
+ return -1; /* Not an ascii character. */
+
+ if (c <= '9')
+ return c - '0';
+
+ return c - 'a' + 10;
+}
+
+/* Functions to communicate with the TPM over the trunks_send --raw channel. */
+
+/* File handle to share between write and read sides. */
+static FILE *tpm_output;
+static int ts_write(const void *out, size_t len)
+{
+ const char *cmd_head = "PATH=\"${PATH}:/usr/sbin\" trunks_send --raw ";
+ size_t head_size = strlen(cmd_head);
+ char full_command[head_size + 2 * len + 1];
+ size_t i;
+
+ strcpy(full_command, cmd_head);
+ /*
+ * Need to convert binary input into hex ascii output to pass to the
+ * trunks_send command.
+ */
+ for (i = 0; i < len; i++) {
+ uint8_t c = ((const uint8_t *)out)[i];
+
+ full_command[head_size + 2 * i] = to_hexascii(c >> 4);
+ full_command[head_size + 2 * i + 1] = to_hexascii(c & 0xf);
+ }
+
+ /* Make it a proper zero terminated string. */
+ full_command[sizeof(full_command) - 1] = 0;
+ debug("cmd: %s\n", full_command);
+ tpm_output = popen(full_command, "r");
+ if (tpm_output)
+ return len;
+
+ fprintf(stderr, "Error: failed to launch trunks_send --raw\n");
+ return -1;
+}
+
+static int ts_read(void *buf, size_t max_rx_size)
+{
+ int i;
+ int pclose_rv;
+ int rv;
+ char response[max_rx_size * 2];
+
+ if (!tpm_output) {
+ fprintf(stderr, "Error: attempt to read empty output\n");
+ return -1;
+ }
+
+ rv = fread(response, 1, sizeof(response), tpm_output);
+ if (rv > 0)
+ rv -= 1; /* Discard the \n character added by trunks_send. */
+
+ debug("response of size %d, max rx size %zd: %s\n",
+ rv, max_rx_size, response);
+
+ pclose_rv = pclose(tpm_output);
+ if (pclose_rv < 0) {
+ fprintf(stderr,
+ "Error: pclose failed: error %d (%s)\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ tpm_output = NULL;
+
+ if (rv & 1) {
+ fprintf(stderr,
+ "Error: trunks_send returned odd number of bytes: %s\n",
+ response);
+ return -1;
+ }
+
+ for (i = 0; i < rv/2; i++) {
+ uint8_t byte;
+ char c;
+ int nibble;
+
+ c = response[2 * i];
+ nibble = from_hexascii(c);
+ if (nibble < 0) {
+ fprintf(stderr, "Error: "
+ "trunks_send returned non hex character %c\n",
+ c);
+ return -1;
+ }
+ byte = nibble << 4;
+
+ c = response[2 * i + 1];
+ nibble = from_hexascii(c);
+ if (nibble < 0) {
+ fprintf(stderr, "Error: "
+ "trunks_send returned non hex character %c\n",
+ c);
+ return -1;
+ }
+ byte |= nibble;
+
+ ((uint8_t *)buf)[i] = byte;
+ }
+
+ return rv/2;
+}
+
+/*
+ * Prepare and transfer a block to either to /dev/tpm0 or through trunks_send
+ * --raw, get a reply.
+ */
+static int tpm_send_pkt(struct transfer_descriptor *td, unsigned int digest,
+ unsigned int addr, const void *data, int size,
+ void *response, size_t *response_size,
+ uint16_t subcmd)
+{
+ /* Used by transfer to /dev/tpm0 */
+ static uint8_t outbuf[MAX_BUF_SIZE];
+ struct upgrade_pkt *out = (struct upgrade_pkt *)outbuf;
+ int len, done;
+ int response_offset = offsetof(struct upgrade_pkt, command.data);
+ void *payload;
+ size_t header_size;
+ uint32_t rv;
+ const size_t rx_size = sizeof(outbuf);
+
+ debug("%s: sending to %#x %d bytes\n", __func__, addr, size);
+
+ out->tag = htobe16(0x8001);
+ out->subcmd = htobe16(subcmd);
+
+ if (subcmd <= LAST_EXTENSION_COMMAND)
+ out->ordinal = htobe32(CONFIG_EXTENSION_COMMAND);
+ else
+ out->ordinal = htobe32(TPM_CC_VENDOR_BIT_MASK);
+
+ if (subcmd == EXTENSION_FW_UPGRADE) {
+ /* FW Upgrade PDU header includes a couple of extra fields. */
+ out->upgrade.digest = digest;
+ out->upgrade.address = htobe32(addr);
+ header_size = offsetof(struct upgrade_pkt, upgrade.data);
+ } else {
+ header_size = offsetof(struct upgrade_pkt, command.data);
+ }
+
+ payload = outbuf + header_size;
+ len = size + header_size;
+
+ out->length = htobe32(len);
+ memcpy(payload, data, size);
+#ifdef DEBUG
+ {
+ int i;
+
+ debug("Writing %d bytes to TPM at %x\n", len, addr);
+ for (i = 0; i < 20; i++)
+ debug("%2.2x ", outbuf[i]);
+ debug("\n");
+ }
+#endif
+ switch (td->ep_type) {
+ case dev_xfer:
+ done = write(td->tpm_fd, out, len);
+ break;
+ case ts_xfer:
+ done = ts_write(out, len);
+ break;
+ default:
+ fprintf(stderr, "Error: %s:%d: unknown transfer type %d\n",
+ __func__, __LINE__, td->ep_type);
+ return -1;
+ }
+
+ if (done < 0) {
+ perror("Could not write to TPM");
+ return -1;
+ } else if (done != len) {
+ fprintf(stderr, "Error: Wrote %d bytes, expected to write %d\n",
+ done, len);
+ return -1;
+ }
+
+ switch (td->ep_type) {
+ case dev_xfer: {
+ int read_count;
+
+ len = 0;
+ do {
+ uint8_t *rx_buf = outbuf + len;
+ size_t rx_to_go = rx_size - len;
+
+ read_count = read(td->tpm_fd, rx_buf, rx_to_go);
+
+ len += read_count;
+ } while (read_count);
+ break;
+ }
+ case ts_xfer:
+ len = ts_read(outbuf, rx_size);
+ break;
+ default:
+ /*
+ * This sure will never happen, type is verifed in the
+ * previous switch statement.
+ */
+ len = -1;
+ break;
+ }
+
+#ifdef DEBUG
+ debug("Read %d bytes from TPM\n", len);
+ if (len > 0) {
+ int i;
+
+ for (i = 0; i < len; i++)
+ debug("%2.2x ", outbuf[i]);
+ debug("\n");
+ }
+#endif
+ len = len - response_offset;
+ if (len < 0) {
+ fprintf(stderr, "Problems reading from TPM, got %d bytes\n",
+ len + response_offset);
+ return -1;
+ }
+
+ if (response && response_size) {
+ len = MIN(len, *response_size);
+ memcpy(response, outbuf + response_offset, len);
+ *response_size = len;
+ }
+
+ /* Return the actual return code from the TPM response header. */
+ memcpy(&rv, &((struct upgrade_pkt *)outbuf)->ordinal, sizeof(rv));
+ rv = be32toh(rv);
+
+ /* Clear out vendor command return value offset.*/
+ if ((rv & VENDOR_RC_ERR) == VENDOR_RC_ERR)
+ rv &= ~VENDOR_RC_ERR;
+
+ return rv;
+}
+
+/* Release USB device and return error to the OS. */
+static void shut_down(struct usb_endpoint *uep)
+{
+ libusb_close(uep->devh);
+ libusb_exit(NULL);
+ exit(update_error);
+}
+
+static void usage(int errs)
+{
+ printf("\nUsage: %s [options] [<binary image>]\n"
+ "\n"
+ "This utility allows to update Cr50 RW firmware, configure\n"
+ "various aspects of Cr50 operation, analyze Cr50 binary\n"
+ "images, etc.\n"
+ "The required argument is the file name of a full RO+RW\n"
+ "binary image.\n"
+ "A typical Chromebook use would exepect -s -t options\n"
+ "included in the command line.\n"
+ "\n"
+ "Options:\n"
+ "\n"
+ " -a,--any Try any interfaces to find Cr50"
+ " (-d, -s, -t are all ignored)\n"
+ " -b,--binvers Report versions of Cr50 image's "
+ "RW and RO headers, do not update\n"
+ " -c,--corrupt Corrupt the inactive rw\n"
+ " -d,--device VID:PID USB device (default %04x:%04x)\n"
+ " -f,--fwver "
+ "Report running Cr50 firmware versions\n"
+ " -F,--factory [enable|disable]\n"
+ " Control factory mode\n"
+ " -h,--help Show this message\n"
+ " -I,--ccd_info Get information about CCD state\n"
+ " -i,--board_id [ID[:FLAGS]]\n"
+ " Get or set Info1 board ID fields\n"
+ " ID could be 32 bit hex or 4 "
+ "character string.\n"
+ " -k,--ccd_lock Lock CCD\n"
+ " -M,--machine Output in a machine-friendly way. "
+ "Effective with -b, -f, -i, and -O.\n"
+ " -O,--openbox_rma <desc_file>\n"
+ " Verify other device's RO integrity\n"
+ " using information provided in "
+ "<desc file>\n"
+ " -o,--ccd_open Start CCD open sequence\n"
+ " -P,--password <password>\n"
+ " Set or clear CCD password. Use\n"
+ " 'clear:<cur password>' to clear it\n"
+ " -p,--post_reset Request post reset after transfer\n"
+ " -r,--rma_auth [auth_code]\n"
+ " Request RMA challenge, process "
+ "RMA authentication code\n"
+ " -s,--systemdev Use /dev/tpm0 (-d is ignored)\n"
+ " -t,--trunks_send Use `trunks_send --raw' "
+ "(-d is ignored)\n"
+ " -U,--ccd_unlock Start CCD unlock sequence\n"
+ " -u,--upstart "
+ "Upstart mode (strict header checks)\n"
+ " -V,--verbose Enable debug messages\n"
+ " -v,--version Report this utility version\n"
+ "\n", progname, VID, PID);
+
+ exit(errs ? update_error : noop);
+}
+
+/* Read file into buffer */
+static uint8_t *get_file_or_die(const char *filename, size_t *len_ptr)
+{
+ FILE *fp;
+ struct stat st;
+ uint8_t *data;
+ size_t len;
+
+ fp = fopen(filename, "rb");
+ if (!fp) {
+ perror(filename);
+ exit(update_error);
+ }
+ if (fstat(fileno(fp), &st)) {
+ perror("stat");
+ exit(update_error);
+ }
+
+ len = st.st_size;
+
+ data = malloc(len);
+ if (!data) {
+ perror("malloc");
+ exit(update_error);
+ }
+
+ if (1 != fread(data, st.st_size, 1, fp)) {
+ perror("fread");
+ exit(update_error);
+ }
+
+ fclose(fp);
+
+ *len_ptr = len;
+ return data;
+}
+
+#define USB_ERROR(m, r) \
+ fprintf(stderr, "%s:%d, %s returned %d (%s)\n", __FILE__, __LINE__, \
+ m, r, libusb_strerror(r))
+
+/*
+ * Actual USB transfer function, the 'allow_less' flag indicates that the
+ * valid response could be shortef than allotted memory, the 'rxed_count'
+ * pointer, if provided along with 'allow_less' lets the caller know how mavy
+ * bytes were received.
+ */
+static void do_xfer(struct usb_endpoint *uep, void *outbuf, int outlen,
+ void *inbuf, int inlen, int allow_less,
+ size_t *rxed_count)
+{
+
+ int r, actual;
+
+ /* Send data out */
+ if (outbuf && outlen) {
+ actual = 0;
+ r = libusb_bulk_transfer(uep->devh, uep->ep_num,
+ outbuf, outlen,
+ &actual, 1000);
+ if (r < 0) {
+ USB_ERROR("libusb_bulk_transfer", r);
+ exit(update_error);
+ }
+ if (actual != outlen) {
+ fprintf(stderr, "%s:%d, only sent %d/%d bytes\n",
+ __FILE__, __LINE__, actual, outlen);
+ shut_down(uep);
+ }
+ }
+
+ /* Read reply back */
+ if (inbuf && inlen) {
+
+ actual = 0;
+ r = libusb_bulk_transfer(uep->devh, uep->ep_num | 0x80,
+ inbuf, inlen,
+ &actual, 1000);
+ if (r < 0) {
+ USB_ERROR("libusb_bulk_transfer", r);
+ exit(update_error);
+ }
+ if ((actual != inlen) && !allow_less) {
+ fprintf(stderr, "%s:%d, only received %d/%d bytes\n",
+ __FILE__, __LINE__, actual, inlen);
+ shut_down(uep);
+ }
+
+ if (rxed_count)
+ *rxed_count = actual;
+ }
+}
+
+static void xfer(struct usb_endpoint *uep, void *outbuf,
+ size_t outlen, void *inbuf, size_t inlen)
+{
+ do_xfer(uep, outbuf, outlen, inbuf, inlen, 0, NULL);
+}
+
+/* Return 0 on error, since it's never gonna be EP 0 */
+static int find_endpoint(const struct libusb_interface_descriptor *iface,
+ struct usb_endpoint *uep)
+{
+ const struct libusb_endpoint_descriptor *ep;
+
+ if (iface->bInterfaceClass == 255 &&
+ iface->bInterfaceSubClass == SUBCLASS &&
+ iface->bInterfaceProtocol == PROTOCOL &&
+ iface->bNumEndpoints) {
+ ep = &iface->endpoint[0];
+ uep->ep_num = ep->bEndpointAddress & 0x7f;
+ uep->chunk_len = ep->wMaxPacketSize;
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Return -1 on error */
+static int find_interface(struct usb_endpoint *uep)
+{
+ int iface_num = -1;
+ int r, i, j;
+ struct libusb_device *dev;
+ struct libusb_config_descriptor *conf = 0;
+ const struct libusb_interface *iface0;
+ const struct libusb_interface_descriptor *iface;
+
+ dev = libusb_get_device(uep->devh);
+ r = libusb_get_active_config_descriptor(dev, &conf);
+ if (r < 0) {
+ USB_ERROR("libusb_get_active_config_descriptor", r);
+ goto out;
+ }
+
+ for (i = 0; i < conf->bNumInterfaces; i++) {
+ iface0 = &conf->interface[i];
+ for (j = 0; j < iface0->num_altsetting; j++) {
+ iface = &iface0->altsetting[j];
+ if (find_endpoint(iface, uep)) {
+ iface_num = i;
+ goto out;
+ }
+ }
+ }
+
+out:
+ libusb_free_config_descriptor(conf);
+ return iface_num;
+}
+
+/* Returns true if parsed. */
+static int parse_vidpid(const char *input, uint16_t *vid_ptr, uint16_t *pid_ptr)
+{
+ char *copy, *s, *e = 0;
+
+ copy = strdup(input);
+
+ s = strchr(copy, ':');
+ if (!s)
+ return 0;
+ *s++ = '\0';
+
+ *vid_ptr = (uint16_t) strtoul(copy, &e, 16);
+ if (!*optarg || (e && *e))
+ return 0;
+
+ *pid_ptr = (uint16_t) strtoul(s, &e, 16);
+ if (!*optarg || (e && *e))
+ return 0;
+
+ return 1;
+}
+
+
+static void usb_findit(uint16_t vid, uint16_t pid, struct usb_endpoint *uep)
+{
+ int iface_num, r;
+
+ memset(uep, 0, sizeof(*uep));
+
+ r = libusb_init(NULL);
+ if (r < 0) {
+ USB_ERROR("libusb_init", r);
+ exit(update_error);
+ }
+
+ printf("open_device %04x:%04x\n", vid, pid);
+ /* NOTE: This doesn't handle multiple matches! */
+ uep->devh = libusb_open_device_with_vid_pid(NULL, vid, pid);
+ if (!uep->devh) {
+ fprintf(stderr, "Can't find device\n");
+ exit(update_error);
+ }
+
+ iface_num = find_interface(uep);
+ if (iface_num < 0) {
+ fprintf(stderr, "USB FW update not supported by that device\n");
+ shut_down(uep);
+ }
+ if (!uep->chunk_len) {
+ fprintf(stderr, "wMaxPacketSize isn't valid\n");
+ shut_down(uep);
+ }
+
+ printf("found interface %d endpoint %d, chunk_len %d\n",
+ iface_num, uep->ep_num, uep->chunk_len);
+
+ libusb_set_auto_detach_kernel_driver(uep->devh, 1);
+ r = libusb_claim_interface(uep->devh, iface_num);
+ if (r < 0) {
+ USB_ERROR("libusb_claim_interface", r);
+ shut_down(uep);
+ }
+
+ printf("READY\n-------\n");
+}
+
+struct update_pdu {
+ uint32_t block_size; /* Total block size, include this field's size. */
+ struct upgrade_command cmd;
+ /* The actual payload goes here. */
+};
+
+static int transfer_block(struct usb_endpoint *uep, struct update_pdu *updu,
+ uint8_t *transfer_data_ptr, size_t payload_size)
+{
+ size_t transfer_size;
+ uint32_t reply;
+ int actual;
+ int r;
+
+ /* First send the header. */
+ xfer(uep, updu, sizeof(*updu), NULL, 0);
+
+ /* Now send the block, chunk by chunk. */
+ for (transfer_size = 0; transfer_size < payload_size;) {
+ int chunk_size;
+
+ chunk_size = MIN(uep->chunk_len, payload_size - transfer_size);
+ xfer(uep, transfer_data_ptr, chunk_size, NULL, 0);
+ transfer_data_ptr += chunk_size;
+ transfer_size += chunk_size;
+ }
+
+ /* Now get the reply. */
+ r = libusb_bulk_transfer(uep->devh, uep->ep_num | 0x80,
+ (void *) &reply, sizeof(reply),
+ &actual, 1000);
+ if (r) {
+ if (r == -7) {
+ fprintf(stderr, "Timeout!\n");
+ return r;
+ }
+ USB_ERROR("libusb_bulk_transfer", r);
+ shut_down(uep);
+ }
+
+ reply = *((uint8_t *)&reply);
+ if (reply) {
+ fprintf(stderr, "Error: status %#x\n", reply);
+ exit(update_error);
+ }
+
+ return 0;
+}
+
+/**
+ * Transfer an image section (typically RW or RO).
+ *
+ * td - transfer descriptor to use to communicate with the target
+ * data_ptr - pointer at the section base in the image
+ * section_addr - address of the section in the target memory space
+ * data_len - section size
+ */
+static void transfer_section(struct transfer_descriptor *td,
+ uint8_t *data_ptr,
+ uint32_t section_addr,
+ size_t data_len)
+{
+ /*
+ * Actually, we can skip trailing chunks of 0xff, as the entire
+ * section space must be erased before the update is attempted.
+ */
+ while (data_len && (data_ptr[data_len - 1] == 0xff))
+ data_len--;
+
+ printf("sending 0x%zx bytes to %#x\n", data_len, section_addr);
+ while (data_len) {
+ size_t payload_size;
+ SHA_CTX ctx;
+ uint8_t digest[SHA_DIGEST_LENGTH];
+ int max_retries;
+ struct update_pdu updu;
+
+ /* prepare the header to prepend to the block. */
+ payload_size = MIN(data_len, SIGNED_TRANSFER_SIZE);
+ updu.block_size = htobe32(payload_size +
+ sizeof(struct update_pdu));
+
+ updu.cmd.block_base = htobe32(section_addr);
+
+ /* Calculate the digest. */
+ SHA1_Init(&ctx);
+ SHA1_Update(&ctx, &updu.cmd.block_base,
+ sizeof(updu.cmd.block_base));
+ SHA1_Update(&ctx, data_ptr, payload_size);
+ SHA1_Final(digest, &ctx);
+
+ /* Copy the first few bytes. */
+ memcpy(&updu.cmd.block_digest, digest,
+ sizeof(updu.cmd.block_digest));
+ if (td->ep_type == usb_xfer) {
+ for (max_retries = 10; max_retries; max_retries--)
+ if (!transfer_block(&td->uep, &updu,
+ data_ptr, payload_size))
+ break;
+
+ if (!max_retries) {
+ fprintf(stderr,
+ "Failed to transfer block, %zd to go\n",
+ data_len);
+ exit(update_error);
+ }
+ } else {
+ uint8_t error_code[4];
+ size_t rxed_size = sizeof(error_code);
+ uint32_t block_addr;
+
+ block_addr = section_addr;
+
+ /*
+ * A single byte response is expected, but let's give
+ * the driver a few extra bytes to catch cases when a
+ * different amount of data is transferred (which
+ * would indicate a synchronization problem).
+ */
+ if (tpm_send_pkt(td,
+ updu.cmd.block_digest,
+ block_addr,
+ data_ptr,
+ payload_size, error_code,
+ &rxed_size,
+ EXTENSION_FW_UPGRADE) < 0) {
+ fprintf(stderr,
+ "Failed to trasfer block, %zd to go\n",
+ data_len);
+ exit(update_error);
+ }
+ if (rxed_size != 1) {
+ fprintf(stderr, "Unexpected return size %zd\n",
+ rxed_size);
+ exit(update_error);
+ }
+
+ if (error_code[0]) {
+ fprintf(stderr, "Error %d\n", error_code[0]);
+ exit(update_error);
+ }
+ }
+ data_len -= payload_size;
+ data_ptr += payload_size;
+ section_addr += payload_size;
+ }
+}
+
+/* Information about the target */
+static struct first_response_pdu targ;
+
+/*
+ * Each RO or RW section of the new image can be in one of the following
+ * states.
+ */
+enum upgrade_status {
+ not_needed = 0, /* Version below or equal that on the target. */
+ not_possible, /*
+ * RO is newer, but can't be transferred due to
+ * target RW shortcomings.
+ */
+ needed /*
+ * This section needs to be transferred to the
+ * target.
+ */
+};
+
+/* This array describes all four sections of the new image. */
+static struct {
+ const char *name;
+ uint32_t offset;
+ uint32_t size;
+ enum upgrade_status ustatus;
+ struct signed_header_version shv;
+ uint32_t keyid;
+} sections[] = {
+ {"RO_A", CONFIG_RO_MEM_OFF, CONFIG_RO_SIZE},
+ {"RW_A", CONFIG_RW_MEM_OFF, CONFIG_RW_SIZE},
+ {"RO_B", CHIP_RO_B_MEM_OFF, CONFIG_RO_SIZE},
+ {"RW_B", CONFIG_RW_B_MEM_OFF, CONFIG_RW_SIZE}
+};
+
+/*
+ * Scan the new image and retrieve versions of all four sections, two RO and
+ * two RW.
+ */
+static void fetch_header_versions(const void *image)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(sections); i++) {
+ const struct SignedHeader *h;
+
+ h = (const struct SignedHeader *)((uintptr_t)image +
+ sections[i].offset);
+ sections[i].shv.epoch = h->epoch_;
+ sections[i].shv.major = h->major_;
+ sections[i].shv.minor = h->minor_;
+ sections[i].keyid = h->keyid;
+ }
+}
+
+
+/* Compare to signer headers and determine which one is newer. */
+static int a_newer_than_b(struct signed_header_version *a,
+ struct signed_header_version *b)
+{
+ uint32_t fields[][3] = {
+ {a->epoch, a->major, a->minor},
+ {b->epoch, b->major, b->minor},
+ };
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(fields[0]); i++) {
+ uint32_t a_value;
+ uint32_t b_value;
+
+ a_value = fields[0][i];
+ b_value = fields[1][i];
+
+ /*
+ * Let's filter out images where the section is not
+ * initialized and the version field value is set to all ones.
+ */
+ if (a_value == 0xffffffff)
+ a_value = 0;
+
+ if (b_value == 0xffffffff)
+ b_value = 0;
+
+ if (a_value != b_value)
+ return a_value > b_value;
+ }
+
+ return 0; /* All else being equal A is no newer than B. */
+}
+/*
+ * Pick sections to transfer based on information retrieved from the target,
+ * the new image, and the protocol version the target is running.
+ */
+static void pick_sections(struct transfer_descriptor *td)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(sections); i++) {
+ uint32_t offset = sections[i].offset;
+
+ if ((offset == CONFIG_RW_MEM_OFF) ||
+ (offset == CONFIG_RW_B_MEM_OFF)) {
+
+ /* Skip currently active section. */
+ if (offset != td->rw_offset)
+ continue;
+ /*
+ * Ok, this would be the RW section to transfer to the
+ * device. Is it newer in the new image than the
+ * running RW section on the device?
+ *
+ * If not in 'upstart' mode - transfer even if
+ * versions are the same, timestamps could be
+ * different.
+ */
+
+ if (a_newer_than_b(&sections[i].shv, &targ.shv[1]) ||
+ !td->upstart_mode)
+ sections[i].ustatus = needed;
+ continue;
+ }
+
+ /* Skip currently active section. */
+ if (offset != td->ro_offset)
+ continue;
+ /*
+ * Ok, this would be the RO section to transfer to the device.
+ * Is it newer in the new image than the running RO section on
+ * the device?
+ */
+ if (a_newer_than_b(&sections[i].shv, &targ.shv[0]))
+ sections[i].ustatus = needed;
+ }
+}
+
+static void setup_connection(struct transfer_descriptor *td)
+{
+ size_t rxed_size;
+ size_t i;
+ uint32_t error_code;
+
+ /*
+ * Need to be backwards compatible, communicate with targets running
+ * different protocol versions.
+ */
+ union {
+ struct first_response_pdu rpdu;
+ uint32_t legacy_resp;
+ } start_resp;
+
+ /* Send start request. */
+ printf("start\n");
+
+ if (td->ep_type == usb_xfer) {
+ struct update_pdu updu;
+
+ memset(&updu, 0, sizeof(updu));
+ updu.block_size = htobe32(sizeof(updu));
+ do_xfer(&td->uep, &updu, sizeof(updu), &start_resp,
+ sizeof(start_resp), 1, &rxed_size);
+ } else {
+ rxed_size = sizeof(start_resp);
+ if (tpm_send_pkt(td, 0, 0, NULL, 0,
+ &start_resp, &rxed_size,
+ EXTENSION_FW_UPGRADE) < 0) {
+ fprintf(stderr, "Failed to start transfer\n");
+ exit(update_error);
+ }
+ }
+
+ /* We got something. Check for errors in response */
+ if (rxed_size < 8) {
+ fprintf(stderr, "Unexpected response size %zd: ", rxed_size);
+ for (i = 0; i < rxed_size; i++)
+ fprintf(stderr, " %02x", ((uint8_t *)&start_resp)[i]);
+ fprintf(stderr, "\n");
+ exit(update_error);
+ }
+
+ protocol_version = be32toh(start_resp.rpdu.protocol_version);
+ if (protocol_version < 5) {
+ fprintf(stderr, "Unsupported protocol version %d\n",
+ protocol_version);
+ exit(update_error);
+ }
+
+ printf("target running protocol version %d\n", protocol_version);
+
+ error_code = be32toh(start_resp.rpdu.return_value);
+
+ if (error_code) {
+ fprintf(stderr, "Target reporting error %d\n", error_code);
+ if (td->ep_type == usb_xfer)
+ shut_down(&td->uep);
+ exit(update_error);
+ }
+
+ td->rw_offset = be32toh(start_resp.rpdu.backup_rw_offset);
+ td->ro_offset = be32toh(start_resp.rpdu.backup_ro_offset);
+
+ /* Running header versions. */
+ for (i = 0; i < ARRAY_SIZE(targ.shv); i++) {
+ targ.shv[i].minor = be32toh(start_resp.rpdu.shv[i].minor);
+ targ.shv[i].major = be32toh(start_resp.rpdu.shv[i].major);
+ targ.shv[i].epoch = be32toh(start_resp.rpdu.shv[i].epoch);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(targ.keyid); i++)
+ targ.keyid[i] = be32toh(start_resp.rpdu.keyid[i]);
+
+ printf("keyids: RO 0x%08x, RW 0x%08x\n", targ.keyid[0], targ.keyid[1]);
+ printf("offsets: backup RO at %#x, backup RW at %#x\n",
+ td->ro_offset, td->rw_offset);
+
+ pick_sections(td);
+}
+
+/*
+ * Channel TPM extension/vendor command over USB. The payload of the USB frame
+ * in this case consists of the 2 byte subcommand code concatenated with the
+ * command body. The caller needs to indicate if a response is expected, and
+ * if it is - of what maximum size.
+ */
+static int ext_cmd_over_usb(struct usb_endpoint *uep, uint16_t subcommand,
+ const void *cmd_body, size_t body_size,
+ void *resp, size_t *resp_size)
+{
+ struct update_frame_header *ufh;
+ uint16_t *frame_ptr;
+ size_t usb_msg_size;
+ SHA_CTX ctx;
+ uint8_t digest[SHA_DIGEST_LENGTH];
+
+ usb_msg_size = sizeof(struct update_frame_header) +
+ sizeof(subcommand) + body_size;
+
+ ufh = malloc(usb_msg_size);
+ if (!ufh) {
+ fprintf(stderr, "%s: failed to allocate %zd bytes\n",
+ __func__, usb_msg_size);
+ return -1;
+ }
+
+ ufh->block_size = htobe32(usb_msg_size);
+ ufh->cmd.block_base = htobe32(CONFIG_EXTENSION_COMMAND);
+ frame_ptr = (uint16_t *)(ufh + 1);
+ *frame_ptr = htobe16(subcommand);
+
+ if (body_size)
+ memcpy(frame_ptr + 1, cmd_body, body_size);
+
+ /* Calculate the digest. */
+ SHA1_Init(&ctx);
+ SHA1_Update(&ctx, &ufh->cmd.block_base,
+ usb_msg_size -
+ offsetof(struct update_frame_header, cmd.block_base));
+ SHA1_Final(digest, &ctx);
+ memcpy(&ufh->cmd.block_digest, digest, sizeof(ufh->cmd.block_digest));
+
+ do_xfer(uep, ufh, usb_msg_size, resp,
+ resp_size ? *resp_size : 0, 1, resp_size);
+
+ free(ufh);
+ return 0;
+}
+
+/*
+ * Indicate to the target that update image transfer has been completed. Upon
+ * receiveing of this message the target state machine transitions into the
+ * 'rx_idle' state. The host may send an extension command to reset the target
+ * after this.
+ */
+static void send_done(struct usb_endpoint *uep)
+{
+ uint32_t out;
+
+ /* Send stop request, ignoring reply. */
+ out = htobe32(UPGRADE_DONE);
+ xfer(uep, &out, sizeof(out), &out, 1);
+}
+
+/* Returns number of successfully transmitted image sections. */
+static int transfer_image(struct transfer_descriptor *td,
+ uint8_t *data, size_t data_len)
+{
+ size_t i;
+ int num_txed_sections = 0;
+
+ for (i = 0; i < ARRAY_SIZE(sections); i++)
+ if (sections[i].ustatus == needed) {
+ transfer_section(td,
+ data + sections[i].offset,
+ sections[i].offset,
+ sections[i].size);
+ num_txed_sections++;
+ }
+
+ if (!num_txed_sections)
+ printf("nothing to do\n");
+ else
+ printf("-------\nupdate complete\n");
+ return num_txed_sections;
+}
+
+uint32_t send_vendor_command(struct transfer_descriptor *td,
+ uint16_t subcommand,
+ const void *command_body,
+ size_t command_body_size,
+ void *response,
+ size_t *response_size)
+{
+ int32_t rv;
+
+ if (td->ep_type == usb_xfer) {
+ /*
+ * When communicating over USB the response is always supposed
+ * to have the result code in the first byte of the response,
+ * to be stripped from the actual response body by this
+ * function.
+ */
+ uint8_t temp_response[MAX_BUF_SIZE];
+ size_t max_response_size;
+
+ if (!response_size) {
+ max_response_size = 1;
+ } else if (*response_size < (sizeof(temp_response))) {
+ max_response_size = *response_size + 1;
+ } else {
+ fprintf(stderr,
+ "Error: Expected response too large (%zd)\n",
+ *response_size);
+ /* Should happen only when debugging. */
+ exit(update_error);
+ }
+
+ ext_cmd_over_usb(&td->uep, subcommand,
+ command_body, command_body_size,
+ temp_response, &max_response_size);
+ if (!max_response_size) {
+ /*
+ * we must be talking to an older Cr50 firmware, which
+ * does not return the result code in the first byte
+ * on success, nothing to do.
+ */
+ if (response_size)
+ *response_size = 0;
+ rv = 0;
+ } else {
+ rv = temp_response[0];
+ if (response_size) {
+ *response_size = max_response_size - 1;
+ memcpy(response,
+ temp_response + 1, *response_size);
+ }
+ }
+ } else {
+
+ rv = tpm_send_pkt(td, 0, 0,
+ command_body, command_body_size,
+ response, response_size, subcommand);
+
+ if (rv == -1) {
+ fprintf(stderr,
+ "Error: Failed to send vendor command %d\n",
+ subcommand);
+ exit(update_error);
+ }
+ }
+
+ return rv; /* This will be converted into uint32_t */
+}
+
+/*
+ * Corrupt the header of the inactive rw image to make sure the system can't
+ * rollback
+ */
+static void invalidate_inactive_rw(struct transfer_descriptor *td)
+{
+ /* Corrupt the rw image that is not running. */
+ uint32_t rv;
+
+ rv = send_vendor_command(td, VENDOR_CC_INVALIDATE_INACTIVE_RW,
+ NULL, 0, NULL, NULL);
+ if (!rv) {
+ printf("Inactive header invalidated\n");
+ return;
+ }
+
+ fprintf(stderr, "*%s: Error %#x\n", __func__, rv);
+ exit(update_error);
+}
+
+static struct signed_header_version ver19 = {
+ .epoch = 0,
+ .major = 0,
+ .minor = 19,
+};
+
+static void generate_reset_request(struct transfer_descriptor *td)
+{
+ size_t response_size;
+ uint8_t response;
+ uint16_t subcommand;
+ uint8_t command_body[2]; /* Max command body size. */
+ size_t command_body_size;
+ uint32_t background_update_supported;
+ const char *reset_type;
+ int rv;
+
+ if (protocol_version < 6) {
+ if (td->ep_type == usb_xfer) {
+ /*
+ * Send a second stop request, which should reboot
+ * without replying.
+ */
+ send_done(&td->uep);
+ }
+ /* Nothing we can do over /dev/tpm0 running versions below 6. */
+ return;
+ }
+
+ /* RW version 0.0.19 and above has support for background updates. */
+ background_update_supported = !a_newer_than_b(&ver19, &targ.shv[1]);
+
+ /*
+ * If this is an upstart request and there is support for background
+ * updates, don't post a request now. The target should handle it on
+ * the next reboot.
+ */
+ if (td->upstart_mode && background_update_supported)
+ return;
+
+ /*
+ * If the user explicitly wants it or a reset is needed because h1
+ * does not support background updates, request post reset instead of
+ * immediate reset. In this case next time the target reboots, the h1
+ * will reboot as well, and will consider running the uploaded code.
+ *
+ * In case target RW version is 19 or above, to reset the target the
+ * host is supposed to send the command to enable the uploaded image
+ * disabled by default.
+ *
+ * Otherwise the immediate reset command would suffice.
+ */
+ /* Most common case. */
+ command_body_size = 0;
+ response_size = 1;
+ if (td->post_reset || td->upstart_mode) {
+ subcommand = EXTENSION_POST_RESET;
+ reset_type = "posted";
+ } else if (background_update_supported) {
+ subcommand = VENDOR_CC_TURN_UPDATE_ON;
+ command_body_size = sizeof(command_body);
+ command_body[0] = 0;
+ command_body[1] = 100; /* Reset in 100 ms. */
+ reset_type = "requested";
+ } else {
+ response_size = 0;
+ subcommand = VENDOR_CC_IMMEDIATE_RESET;
+ reset_type = "triggered";
+ }
+
+ rv = send_vendor_command(td, subcommand, command_body,
+ command_body_size, &response, &response_size);
+
+ if (rv) {
+ fprintf(stderr, "*%s: Error %#x\n", __func__, rv);
+ exit(update_error);
+ }
+ printf("reboot %s\n", reset_type);
+}
+
+/*
+ * Machine output is formatted as "key=value", one key-value pair per line, and
+ * parsed by other programs (e.g., debugd). The value part should be specified
+ * in the printf-like way. For example:
+ *
+ * print_machine_output("date", "%d/%d/%d", 2018, 1, 1),
+ *
+ * which outputs this line in console:
+ *
+ * date=2018/1/1
+ *
+ * The key part should not contain '=' or newline. The value part may contain
+ * special characters like spaces, quotes, brackets, but not newlines. The
+ * newline character means end of value.
+ *
+ * Any output format change in this function may require similar changes on the
+ * programs that are using this gsctool.
+ */
+__attribute__((__format__(__printf__, 2, 3)))
+static void print_machine_output(const char *key, const char *format, ...)
+{
+ va_list args;
+
+ if (strchr(key, '=') != NULL || strchr(key, '\n') != NULL) {
+ fprintf(stderr,
+ "Error: key %s contains '=' or a newline character.\n",
+ key);
+ return;
+ }
+
+ if (strchr(format, '\n') != NULL) {
+ fprintf(stderr,
+ "Error: value format %s contains a newline character. "
+ "\n",
+ format);
+ return;
+ }
+
+ va_start(args, format);
+
+ printf("%s=", key);
+ vprintf(format, args);
+ printf("\n");
+
+ va_end(args);
+}
+
+/*
+ * Prints out the header, including FW versions and board IDs, of the given
+ * image. Output in a machine-friendly format if show_machine_output is true.
+ */
+static int show_headers_versions(const void *image, bool show_machine_output)
+{
+ // There are 2 FW slots in an image, and each slot has 2 sections, RO
+ // and RW. The 2 slots should have identical FW versions and board IDs.
+ const struct {
+ const char *name;
+ uint32_t offset;
+ } sections[] = {
+ // Slot A
+ {"RO", CONFIG_RO_MEM_OFF},
+ {"RW", CONFIG_RW_MEM_OFF},
+ // Slot B
+ {"RO", CHIP_RO_B_MEM_OFF},
+ {"RW", CONFIG_RW_B_MEM_OFF}
+ };
+ const size_t kNumSlots = 2;
+ const size_t kNumSectionsPerSlot = 2;
+
+ // String representation of FW version (<epoch>:<major>:<minor>), one
+ // string for each FW section.
+ char ro_fw_ver[kNumSlots][MAX_FW_VER_LENGTH];
+ char rw_fw_ver[kNumSlots][MAX_FW_VER_LENGTH];
+
+ struct board_id {
+ uint32_t id;
+ uint32_t mask;
+ uint32_t flags;
+ } bid[kNumSlots];
+
+ char bid_string[MAX_BOARD_ID_LENGTH];
+
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(sections); i++) {
+ const struct SignedHeader *h =
+ (const struct SignedHeader *)
+ ((uintptr_t)image + sections[i].offset);
+ const size_t slot_idx = i / kNumSectionsPerSlot;
+
+ if (sections[i].name[1] == 'O') {
+ // RO
+ snprintf(ro_fw_ver[slot_idx], MAX_FW_VER_LENGTH,
+ "%u.%u.%u", h->epoch_, h->major_, h->minor_);
+ // No need to read board ID in an RO section.
+ continue;
+ } else {
+ // RW
+ snprintf(rw_fw_ver[slot_idx], MAX_FW_VER_LENGTH,
+ "%u.%u.%u", h->epoch_, h->major_, h->minor_);
+ }
+
+ /*
+ * For RW sections, retrieves the board ID fields' contents,
+ * which are stored XORed with a padding value.
+ */
+ bid[slot_idx].id = h->board_id_type ^ SIGNED_HEADER_PADDING;
+ bid[slot_idx].mask =
+ h->board_id_type_mask ^ SIGNED_HEADER_PADDING;
+ bid[slot_idx].flags = h->board_id_flags ^ SIGNED_HEADER_PADDING;
+ }
+
+ if (strncmp(ro_fw_ver[0], ro_fw_ver[1], MAX_FW_VER_LENGTH) != 0) {
+ fprintf(stderr,
+ "Error: RO FW versions in the 2 slots do not match.\n");
+ return -1;
+ }
+
+ if (strncmp(rw_fw_ver[0], rw_fw_ver[1], MAX_FW_VER_LENGTH) != 0) {
+ fprintf(stderr,
+ "Error: RW FW versions in the 2 slots do not match.\n");
+ return -1;
+ }
+
+ if (memcmp(&bid[0], &bid[1], sizeof(struct board_id)) != 0) {
+ fprintf(stderr,
+ "Error: board IDs in the 2 slots do not match.\n");
+ return -1;
+ }
+
+ /*
+ * If board ID is an ASCII string (as it ought to be), print
+ * it as 4 symbols, otherwise print it as an 8 digit hex.
+ */
+ for (i = 0; i < sizeof(bid[0].id); ++i)
+ if (!isalnum(((const char *)&bid[0].id)[i]))
+ break;
+
+ if (i == sizeof(bid[0].id)) {
+ bid[0].id = be32toh(bid[0].id);
+ snprintf(bid_string, MAX_BOARD_ID_LENGTH,
+ "%.4s", (const char *)&bid);
+ } else {
+ snprintf(bid_string, MAX_BOARD_ID_LENGTH, "%08x", bid[0].id);
+ }
+
+ if (show_machine_output) {
+ print_machine_output("IMAGE_RO_FW_VER", "%s", ro_fw_ver[0]);
+ print_machine_output("IMAGE_RW_FW_VER", "%s", rw_fw_ver[0]);
+ print_machine_output("IMAGE_BID_STRING", "%s", bid_string);
+ print_machine_output("IMAGE_BID_MASK", "%08x", bid[0].mask);
+ print_machine_output("IMAGE_BID_FLAGS", "%08x", bid[0].flags);
+ } else {
+ // TODO(garryxiao): remove "_A" from RO and RW after updating
+ // scripts that use gsctool.
+ printf("RO_A:%s RW_A:%s[%s:%08x:%08x]\n",
+ ro_fw_ver[0], rw_fw_ver[0],
+ bid_string, bid[0].mask, bid[0].flags);
+ }
+
+ return 0;
+}
+
+/*
+ * The default flag value will allow to run images built for any hardware
+ * generation of a particular board ID.
+ */
+#define DEFAULT_BOARD_ID_FLAG 0xff00
+static int parse_bid(const char *opt,
+ struct board_id *bid,
+ enum board_id_action *bid_action)
+{
+ char *e;
+ const char *param2;
+ size_t param1_length;
+
+ if (!opt) {
+ *bid_action = bid_get;
+ return 1;
+ }
+
+ /* Set it here to make bailing out easier later. */
+ bid->flags = DEFAULT_BOARD_ID_FLAG;
+
+ *bid_action = bid_set; /* Ignored by caller on errors. */
+
+ /*
+ * Pointer to the optional second component of the command line
+ * parameter, when present - separated by a colon.
+ */
+ param2 = strchr(opt, ':');
+ if (param2) {
+ param1_length = param2 - opt;
+ param2++;
+ if (!*param2)
+ return 0; /* Empty second parameter. */
+ } else {
+ param1_length = strlen(opt);
+ }
+
+ if (!param1_length)
+ return 0; /* Colon is the first character of the string? */
+
+ if (param1_length <= 4) {
+ unsigned i;
+
+ /* Input must be a symbolic board name. */
+ bid->type = 0;
+ for (i = 0; i < param1_length; i++)
+ bid->type = (bid->type << 8) | opt[i];
+ } else {
+ bid->type = (uint32_t)strtoul(opt, &e, 0);
+ if ((param2 && (*e != ':')) || (!param2 && *e))
+ return 0;
+ }
+
+ if (param2) {
+ bid->flags = (uint32_t)strtoul(param2, &e, 0);
+ if (*e)
+ return 0;
+ }
+
+ return 1;
+}
+
+static uint32_t common_process_password(struct transfer_descriptor *td,
+ enum ccd_vendor_subcommands subcmd)
+{
+ size_t response_size;
+ uint8_t response;
+ uint32_t rv;
+ char *password = NULL;
+ char *password_copy = NULL;
+ size_t copy_len = 0;
+ size_t len = 0;
+ struct termios oldattr, newattr;
+
+ /* Suppress command line echo while password is being entered. */
+ tcgetattr(STDIN_FILENO, &oldattr);
+ newattr = oldattr;
+ newattr.c_lflag &= ~ECHO;
+ newattr.c_lflag |= (ICANON | ECHONL);
+ tcsetattr(STDIN_FILENO, TCSANOW, &newattr);
+
+ /* With command line echo suppressed request password entry twice. */
+ printf("Enter password:");
+ len = getline(&password, &len, stdin);
+ printf("Re-enter password:");
+ getline(&password_copy, &copy_len, stdin);
+
+ /* Restore command line echo. */
+ tcsetattr(STDIN_FILENO, TCSANOW, &oldattr);
+
+ /* Empty password will still have the newline. */
+ if ((len <= 1) || !password_copy) {
+ if (password)
+ free(password);
+ if (password_copy)
+ free(password_copy);
+ fprintf(stderr, "Error reading password\n");
+ exit(update_error);
+ }
+
+ /* Compare the two inputs. */
+ if (strcmp(password, password_copy)) {
+ fprintf(stderr, "Entered passwords don't match\n");
+ free(password);
+ free(password_copy);
+ exit(update_error);
+ }
+
+ /*
+ * Ok, we have a password, let's move it in the buffer to overwrite
+ * the newline and free a byte to prepend the subcommand code.
+ */
+ memmove(password + 1, password, len - 1);
+ password[0] = subcmd;
+ response_size = sizeof(response);
+ rv = send_vendor_command(td, VENDOR_CC_CCD,
+ password, len,
+ &response, &response_size);
+ free(password);
+ free(password_copy);
+
+ if ((rv != VENDOR_RC_SUCCESS) && (rv != VENDOR_RC_IN_PROGRESS))
+ fprintf(stderr, "Error sending password: rv %d, response %d\n",
+ rv, response_size ? response : 0);
+
+ return rv;
+}
+
+static void process_password(struct transfer_descriptor *td)
+{
+ if (common_process_password(td, CCDV_PASSWORD) == VENDOR_RC_SUCCESS)
+ return;
+
+ exit(update_error);
+}
+
+void poll_for_pp(struct transfer_descriptor *td,
+ uint16_t command,
+ uint8_t poll_type)
+{
+ uint8_t response;
+ uint8_t prev_response;
+ size_t response_size;
+ int rv;
+
+ prev_response = ~0; /* Guaranteed invalid value. */
+
+ while (1) {
+ response_size = sizeof(response);
+ rv = send_vendor_command(td, command,
+ &poll_type, sizeof(poll_type),
+ &response, &response_size);
+
+ if (((rv != VENDOR_RC_SUCCESS) && (rv != VENDOR_RC_IN_PROGRESS))
+ || (response_size != 1)) {
+ fprintf(stderr, "Error: rv %d, response %d\n",
+ rv, response_size ? response : 0);
+ exit(update_error);
+ }
+
+ if (response == CCD_PP_DONE) {
+ printf("PP Done!\n");
+ return;
+ }
+
+ if (response == CCD_PP_CLOSED) {
+ fprintf(stderr,
+ "Error: Physical presence check timeout!\n");
+ exit(update_error);
+ }
+
+
+ if (response == CCD_PP_AWAITING_PRESS) {
+ printf("Press PP button now!\n");
+ } else if (response == CCD_PP_BETWEEN_PRESSES) {
+ if (prev_response != response)
+ printf("Another press will be required!\n");
+ } else {
+ fprintf(stderr, "Error: unknown poll result %d\n",
+ response);
+ exit(update_error);
+ }
+ prev_response = response;
+
+ usleep(500 * 1000); /* Poll every half a second. */
+ }
+
+}
+
+static void print_ccd_info(void *response, size_t response_size)
+{
+ struct ccd_info_response ccd_info;
+ size_t i;
+ const struct ccd_capability_info cap_info[] = CAP_INFO_DATA;
+ const char *state_names[] = CCD_STATE_NAMES;
+ const char *cap_state_names[] = CCD_CAP_STATE_NAMES;
+ uint32_t caps_bitmap = 0;
+
+ if (response_size != sizeof(ccd_info)) {
+ fprintf(stderr, "Unexpected CCD info response size %zd\n",
+ response_size);
+ exit(update_error);
+ }
+
+ memcpy(&ccd_info, response, sizeof(ccd_info));
+
+ /* Convert it back to host endian format. */
+ ccd_info.ccd_flags = be32toh(ccd_info.ccd_flags);
+ for (i = 0; i < ARRAY_SIZE(ccd_info.ccd_caps_current); i++) {
+ ccd_info.ccd_caps_current[i] =
+ be32toh(ccd_info.ccd_caps_current[i]);
+ ccd_info.ccd_caps_defaults[i] =
+ be32toh(ccd_info.ccd_caps_defaults[i]);
+ }
+
+ /* Now report CCD state on the console. */
+ printf("State: %s\n", ccd_info.ccd_state > ARRAY_SIZE(state_names) ?
+ "Error" : state_names[ccd_info.ccd_state]);
+ printf("Password: %s\n", ccd_info.ccd_has_password ? "Set" : "None");
+ printf("Flags: %#06x\n", ccd_info.ccd_flags);
+ printf("Capabilities, current and default:\n");
+ for (i = 0; i < CCD_CAP_COUNT; i++) {
+ int is_enabled;
+ int index;
+ int shift;
+ int cap_current;
+ int cap_default;
+
+ index = i / (32/2);
+ shift = (i % (32/2)) * 2;
+
+ cap_current = (ccd_info.ccd_caps_current[index] >> shift) & 3;
+ cap_default = (ccd_info.ccd_caps_defaults[index] >> shift) & 3;
+
+ if (ccd_info.ccd_force_disabled) {
+ is_enabled = 0;
+ } else {
+ switch (cap_current) {
+ case CCD_CAP_STATE_ALWAYS:
+ is_enabled = 1;
+ break;
+ case CCD_CAP_STATE_UNLESS_LOCKED:
+ is_enabled = (ccd_info.ccd_state !=
+ CCD_STATE_LOCKED);
+ break;
+ default:
+ is_enabled = (ccd_info.ccd_state ==
+ CCD_STATE_OPENED);
+ break;
+ }
+ }
+
+ printf(" %-15s %c %s",
+ cap_info[i].name,
+ is_enabled ? 'Y' : '-',
+ cap_state_names[cap_current]);
+
+ if (cap_current != cap_default)
+ printf(" (%s)", cap_state_names[cap_default]);
+
+ printf("\n");
+
+ if (is_enabled)
+ caps_bitmap |= (1 << i);
+ }
+ printf("CCD caps bitmap: %#x\n", caps_bitmap);
+}
+
+static void process_ccd_state(struct transfer_descriptor *td, int ccd_unlock,
+ int ccd_open, int ccd_lock, int ccd_info)
+{
+ uint8_t payload;
+ /* Max possible response size is when ccd_info is requested. */
+ uint8_t response[sizeof(struct ccd_info_response)];
+ size_t response_size;
+ int rv;
+
+ if (ccd_unlock)
+ payload = CCDV_UNLOCK;
+ else if (ccd_open)
+ payload = CCDV_OPEN;
+ else if (ccd_lock)
+ payload = CCDV_LOCK;
+ else
+ payload = CCDV_GET_INFO;
+
+ response_size = sizeof(response);
+ rv = send_vendor_command(td, VENDOR_CC_CCD,
+ &payload, sizeof(payload),
+ &response, &response_size);
+
+ /*
+ * If password is required - try sending the same subcommand
+ * accompanied by user password.
+ */
+ if (rv == VENDOR_RC_PASSWORD_REQUIRED)
+ rv = common_process_password(td, payload);
+
+ if (rv == VENDOR_RC_SUCCESS) {
+ if (ccd_info)
+ print_ccd_info(response, response_size);
+ return;
+ }
+
+ if (rv != VENDOR_RC_IN_PROGRESS) {
+ fprintf(stderr, "Error: rv %d, response %d\n",
+ rv, response_size ? response[0] : 0);
+ exit(update_error);
+ }
+
+ /*
+ * Physical presence process started, poll for the state the user
+ * asked for. Only two subcommands would return 'IN_PROGRESS'.
+ */
+ if (ccd_unlock)
+ poll_for_pp(td, VENDOR_CC_CCD, CCDV_PP_POLL_UNLOCK);
+ else
+ poll_for_pp(td, VENDOR_CC_CCD, CCDV_PP_POLL_OPEN);
+}
+
+void process_bid(struct transfer_descriptor *td,
+ enum board_id_action bid_action,
+ struct board_id *bid,
+ bool show_machine_output)
+{
+ size_t response_size;
+
+ if (bid_action == bid_get) {
+
+ response_size = sizeof(*bid);
+ send_vendor_command(td, VENDOR_CC_GET_BOARD_ID,
+ bid, sizeof(*bid),
+ bid, &response_size);
+
+ if (response_size != sizeof(*bid)) {
+ fprintf(stderr,
+ "Error reading board ID: response size %zd, "
+ "first byte %#02x\n",
+ response_size,
+ response_size ? *(uint8_t *)&bid : -1);
+ exit(update_error);
+ }
+
+ if (show_machine_output) {
+ print_machine_output(
+ "BID_TYPE", "%08x", be32toh(bid->type));
+ print_machine_output(
+ "BID_TYPE_INV", "%08x", be32toh(bid->type_inv));
+ print_machine_output(
+ "BID_FLAGS", "%08x", be32toh(bid->flags));
+
+ } else {
+ printf("Board ID space: %08x:%08x:%08x\n",
+ be32toh(bid->type),
+ be32toh(bid->type_inv),
+ be32toh(bid->flags));
+ }
+
+ return;
+ }
+
+ if (bid_action == bid_set) {
+ /* Sending just two fields: type and flags. */
+ uint32_t command_body[2];
+ uint8_t response;
+
+ command_body[0] = htobe32(bid->type);
+ command_body[1] = htobe32(bid->flags);
+
+ response_size = sizeof(command_body);
+ send_vendor_command(td, VENDOR_CC_SET_BOARD_ID,
+ command_body, sizeof(command_body),
+ command_body, &response_size);
+
+ /*
+ * Speculative assignment: the response is expected to be one
+ * byte in size and be placed in the first byte of the buffer.
+ */
+ response = *((uint8_t *)command_body);
+
+ if (response_size == 1) {
+ if (!response)
+ return; /* Success! */
+
+ fprintf(stderr, "Error %d while setting board id\n",
+ response);
+ } else {
+ fprintf(stderr, "Unexpected response size %zd"
+ " while setting board id\n",
+ response_size);
+ }
+ exit(update_error);
+ }
+}
+
+/*
+ * Retrieve the RMA authentication challenge from the Cr50, print out the
+ * challenge on the console, then prompt the user for the authentication code,
+ * and send the code back to Cr50. The Cr50 would report if the code matched
+ * its expectations or not.
+ */
+static void process_rma(struct transfer_descriptor *td, const char *authcode)
+{
+ char rma_response[81];
+ size_t response_size = sizeof(rma_response);
+ size_t i;
+ size_t auth_size = 0;
+
+ if (!authcode) {
+ send_vendor_command(td, VENDOR_CC_RMA_CHALLENGE_RESPONSE,
+ NULL, 0, rma_response, &response_size);
+
+ if (response_size == 1) {
+ fprintf(stderr, "error %d\n", rma_response[0]);
+ if (td->ep_type == usb_xfer)
+ shut_down(&td->uep);
+ exit(update_error);
+ }
+
+ printf("Challenge:");
+ for (i = 0; i < response_size; i++) {
+ if (!(i % 5)) {
+ if (!(i % 40))
+ printf("\n");
+ printf(" ");
+ }
+ printf("%c", rma_response[i]);
+ }
+ printf("\n");
+ return;
+ }
+
+ if (!*authcode) {
+ printf("Empty response.\n");
+ exit(update_error);
+ return;
+ }
+
+ if (!strcmp(authcode, "disable")) {
+ printf("Invalid arg. Try using 'gsctool -F disable'\n");
+ exit(update_error);
+ return;
+ }
+
+ printf("Processing response...\n");
+ auth_size = strlen(authcode);
+ response_size = sizeof(rma_response);
+
+ send_vendor_command(td, VENDOR_CC_RMA_CHALLENGE_RESPONSE,
+ authcode, auth_size,
+ rma_response, &response_size);
+
+ if (response_size == 1) {
+ fprintf(stderr, "\nrma unlock failed, code %d ",
+ *rma_response);
+ switch (*rma_response) {
+ case VENDOR_RC_BOGUS_ARGS:
+ fprintf(stderr, "(wrong authcode size)\n");
+ break;
+ case VENDOR_RC_INTERNAL_ERROR:
+ fprintf(stderr, "(authcode mismatch)\n");
+ break;
+ default:
+ fprintf(stderr, "(unknown error)\n");
+ break;
+ }
+ if (td->ep_type == usb_xfer)
+ shut_down(&td->uep);
+ exit(update_error);
+ }
+ printf("RMA unlock succeeded.\n");
+}
+
+/*
+ * Enable or disable factory mode. Factory mode will only be enabled if HW
+ * write protect is removed.
+ */
+static void process_factory_mode(struct transfer_descriptor *td,
+ const char *arg)
+{
+ char *cmd_str;
+ int rv;
+ uint16_t subcommand;
+
+ if (!strcasecmp(arg, "disable")) {
+ subcommand = VENDOR_CC_DISABLE_FACTORY;
+ cmd_str = "dis";
+ } else if (!strcasecmp(arg, "enable")) {
+ subcommand = VENDOR_CC_RESET_FACTORY;
+ cmd_str = "en";
+
+ } else {
+ fprintf(stderr, "Invalid factory mode arg %s", arg);
+ exit(update_error);
+ }
+
+ printf("%sabling factory mode\n", cmd_str);
+ rv = send_vendor_command(td, subcommand, NULL, 0, NULL, NULL);
+ if (rv) {
+ fprintf(stderr, "Failed %sabling factory mode, error "
+ "%d\n", cmd_str, rv);
+ exit(update_error);
+ }
+ printf("Factory %sable succeeded.\n", cmd_str);
+}
+
+static void report_version(void)
+{
+ /* Get version from the generated file, ignore the underscore prefix. */
+ const char *v = VERSION + 1;
+
+ printf("Version: %s, built on %s by %s\n", v, DATE, BUILDER);
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ struct transfer_descriptor td;
+ int errorcnt;
+ uint8_t *data = 0;
+ size_t data_len = 0;
+ uint16_t vid = VID, pid = PID;
+ int i;
+ size_t j;
+ int transferred_sections = 0;
+ int binary_vers = 0;
+ int show_fw_ver = 0;
+ int rma = 0;
+ const char *rma_auth_code;
+ int corrupt_inactive_rw = 0;
+ struct board_id bid;
+ enum board_id_action bid_action;
+ int password = 0;
+ int ccd_open = 0;
+ int ccd_unlock = 0;
+ int ccd_lock = 0;
+ int ccd_info = 0;
+ int try_all_transfer = 0;
+ bool show_machine_output = false;
+
+ const char *exclusive_opt_error =
+ "Options -a, -s and -t are mutually exclusive\n";
+ const char *openbox_desc_file = NULL;
+ int factory_mode = 0;
+ char *factory_mode_arg;
+
+ progname = strrchr(argv[0], '/');
+ if (progname)
+ progname++;
+ else
+ progname = argv[0];
+
+ /* Usb transfer - default mode. */
+ memset(&td, 0, sizeof(td));
+ td.ep_type = usb_xfer;
+
+ bid_action = bid_none;
+ errorcnt = 0;
+ opterr = 0; /* quiet, you */
+ while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) {
+ switch (i) {
+ case 'a':
+ if (td.ep_type) {
+ errorcnt++;
+ fprintf(stderr, "%s", exclusive_opt_error);
+ break;
+ }
+ try_all_transfer = 1;
+ /* Try dev_xfer first. */
+ td.ep_type = dev_xfer;
+ break;
+ case 'b':
+ binary_vers = 1;
+ break;
+ case 'd':
+ if (!parse_vidpid(optarg, &vid, &pid)) {
+ fprintf(stderr,
+ "Invalid device argument: \"%s\"\n",
+ optarg);
+ errorcnt++;
+ }
+ break;
+ case 'c':
+ corrupt_inactive_rw = 1;
+ break;
+ case 'f':
+ show_fw_ver = 1;
+ break;
+ case 'h':
+ usage(errorcnt);
+ break;
+ case 'I':
+ ccd_info = 1;
+ break;
+ case 'i':
+ if (!optarg && argv[optind] && argv[optind][0] != '-')
+ /* optional argument present. */
+ optarg = argv[optind++];
+
+ if (!parse_bid(optarg, &bid, &bid_action)) {
+ fprintf(stderr,
+ "Invalid board id argument: \"%s\"\n",
+ optarg);
+ errorcnt++;
+ }
+ break;
+ case 'k':
+ ccd_lock = 1;
+ break;
+ case 'M':
+ show_machine_output = true;
+ break;
+ case 'O':
+ openbox_desc_file = optarg;
+ break;
+ case 'o':
+ ccd_open = 1;
+ break;
+ case 'p':
+ td.post_reset = 1;
+ break;
+ case 'P':
+ password = 1;
+ break;
+ case 'F':
+ factory_mode = 1;
+ factory_mode_arg = optarg;
+ break;
+ case 'r':
+ rma = 1;
+
+ if (!optarg && argv[optind] && argv[optind][0] != '-')
+ /* optional argument present. */
+ optarg = argv[optind++];
+
+ rma_auth_code = optarg;
+ break;
+ case 's':
+ if (td.ep_type || try_all_transfer) {
+ errorcnt++;
+ fprintf(stderr, "%s", exclusive_opt_error);
+ break;
+ }
+ td.ep_type = dev_xfer;
+ break;
+ case 't':
+ if (td.ep_type || try_all_transfer) {
+ errorcnt++;
+ fprintf(stderr, "%s", exclusive_opt_error);
+ break;
+ }
+ td.ep_type = ts_xfer;
+ break;
+ case 'U':
+ ccd_unlock = 1;
+ break;
+ case 'u':
+ td.upstart_mode = 1;
+ break;
+ case 'V':
+ verbose_mode = 1;
+ break;
+ case 'v':
+ report_version(); /* This will call exit(). */
+ break;
+ case 0: /* auto-handled option */
+ break;
+ case '?':
+ if (optopt)
+ fprintf(stderr, "Unrecognized option: -%c\n",
+ optopt);
+ else
+ fprintf(stderr, "Unrecognized option: %s\n",
+ argv[optind - 1]);
+ errorcnt++;
+ break;
+ case ':':
+ fprintf(stderr, "Missing argument to %s\n",
+ argv[optind - 1]);
+ errorcnt++;
+ break;
+ default:
+ fprintf(stderr, "Internal error at %s:%d\n",
+ __FILE__, __LINE__);
+ exit(update_error);
+ }
+ }
+
+ if (errorcnt)
+ usage(errorcnt);
+
+ if ((bid_action == bid_none) &&
+ !ccd_info &&
+ !ccd_lock &&
+ !ccd_open &&
+ !ccd_unlock &&
+ !corrupt_inactive_rw &&
+ !factory_mode &&
+ !password &&
+ !rma &&
+ !show_fw_ver &&
+ !openbox_desc_file) {
+ if (optind >= argc) {
+ fprintf(stderr,
+ "\nERROR: Missing required <binary image>\n\n");
+ usage(1);
+ }
+
+ data = get_file_or_die(argv[optind], &data_len);
+ printf("read %zd(%#zx) bytes from %s\n",
+ data_len, data_len, argv[optind]);
+ if (data_len != CONFIG_FLASH_SIZE) {
+ fprintf(stderr, "Image file is not %d bytes\n",
+ CONFIG_FLASH_SIZE);
+ exit(update_error);
+ }
+
+ fetch_header_versions(data);
+
+ if (binary_vers)
+ exit(show_headers_versions(data, show_machine_output));
+ } else {
+ if (optind < argc)
+ printf("Ignoring binary image %s\n", argv[optind]);
+ }
+
+ if (((bid_action != bid_none) + !!rma + !!password +
+ !!ccd_open + !!ccd_unlock + !!ccd_lock + !!ccd_info +
+ !!openbox_desc_file + !!factory_mode) > 2) {
+ fprintf(stderr, "ERROR: "
+ "options -F, -I, -i, -k, -O, -o, -P, -r, and -u "
+ "are mutually exclusive\n");
+ exit(update_error);
+ }
+
+ if (td.ep_type == usb_xfer) {
+ usb_findit(vid, pid, &td.uep);
+ } else if (td.ep_type == dev_xfer) {
+ td.tpm_fd = open("/dev/tpm0", O_RDWR);
+ if (td.tpm_fd < 0) {
+ if (!try_all_transfer) {
+ perror("Could not open TPM");
+ exit(update_error);
+ }
+ td.ep_type = ts_xfer;
+ }
+ }
+
+ if (openbox_desc_file)
+ return verify_ro(&td, openbox_desc_file, show_machine_output);
+
+ if (ccd_unlock || ccd_open || ccd_lock || ccd_info)
+ process_ccd_state(&td, ccd_unlock, ccd_open,
+ ccd_lock, ccd_info);
+
+ if (password)
+ process_password(&td);
+
+ if (bid_action != bid_none)
+ process_bid(&td, bid_action, &bid, show_machine_output);
+
+ if (rma)
+ process_rma(&td, rma_auth_code);
+
+ if (factory_mode)
+ process_factory_mode(&td, factory_mode_arg);
+
+ if (corrupt_inactive_rw)
+ invalidate_inactive_rw(&td);
+
+ if (data || show_fw_ver) {
+
+ setup_connection(&td);
+
+ if (data) {
+ transferred_sections = transfer_image(&td,
+ data, data_len);
+ free(data);
+ }
+
+ /*
+ * Move USB updater sate machine to idle state so that vendor
+ * commands can be processed later, if any.
+ */
+ if (td.ep_type == usb_xfer)
+ send_done(&td.uep);
+
+ if (transferred_sections)
+ generate_reset_request(&td);
+
+ if (show_fw_ver) {
+ if (show_machine_output) {
+ print_machine_output("RO_FW_VER", "%d.%d.%d",
+ targ.shv[0].epoch,
+ targ.shv[0].major,
+ targ.shv[0].minor);
+ print_machine_output("RW_FW_VER", "%d.%d.%d",
+ targ.shv[1].epoch,
+ targ.shv[1].major,
+ targ.shv[1].minor);
+ } else {
+ printf("Current versions:\n");
+ printf("RO %d.%d.%d\n", targ.shv[0].epoch,
+ targ.shv[0].major, targ.shv[0].minor);
+ printf("RW %d.%d.%d\n", targ.shv[1].epoch,
+ targ.shv[1].major, targ.shv[1].minor);
+ }
+ }
+ }
+
+ if (td.ep_type == usb_xfer) {
+ libusb_close(td.uep.devh);
+ libusb_exit(NULL);
+ }
+
+ if (!transferred_sections)
+ return noop;
+ /*
+ * We should indicate if RO update was not done because of the
+ * insufficient RW version.
+ */
+ for (j = 0; j < ARRAY_SIZE(sections); j++)
+ if (sections[j].ustatus == not_possible) {
+ /* This will allow scripting repeat attempts. */
+ printf("Failed to update RO, run the command again\n");
+ return rw_updated;
+ }
+
+ printf("image updated\n");
+ return all_updated;
+}
diff --git a/extra/usb_updater/gsctool.h b/extra/usb_updater/gsctool.h
new file mode 100644
index 0000000000..9b3660ca65
--- /dev/null
+++ b/extra/usb_updater/gsctool.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __EXTRA_USB_UPDATER_GSCTOOL_H
+#define __EXTRA_USB_UPDATER_GSCTOOL_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+/* This describes USB endpoint used to communicate with Cr50. */
+struct usb_endpoint {
+ struct libusb_device_handle *devh;
+ uint8_t ep_num;
+ int chunk_len;
+};
+
+/*
+ * gsctool uses this structure to keep information about the communications
+ * channel used to talk to the Cr50, and about the state of the Cr50 image.
+ */
+struct transfer_descriptor {
+ /*
+ * Set to true for use in an upstart script. Do not reboot after
+ * transfer, and do not transfer RW if versions are the same.
+ *
+ * When using in development environment it is beneficial to transfer
+ * RW images with the same version, as they get started based on the
+ * header timestamp.
+ */
+ uint32_t upstart_mode;
+
+ /*
+ * offsets of RO and WR sections available for update (not currently
+ * active).
+ */
+ uint32_t ro_offset;
+ uint32_t rw_offset;
+ uint32_t post_reset;
+
+ /* Type of channel used to communicate with Cr50. */
+ enum transfer_type {
+ usb_xfer = 0, /* usb interface. */
+ dev_xfer = 1, /* /dev/tpm0 */
+ ts_xfer = 2 /* trunks_send */
+ } ep_type;
+ union {
+ struct usb_endpoint uep;
+ int tpm_fd;
+ };
+};
+
+/*
+ * These are values returned by the gsctool utility, they are interpreted by
+ * the startup files to decide how to proceed (try to update to a new Cr50
+ * image or not).
+ */
+enum exit_values {
+ noop = 0, /* All up to date, no update needed. */
+ all_updated = 1, /* Update completed, reboot required. */
+ rw_updated = 2, /* RO was not updated, reboot required. */
+ update_error = 3 /* Something went wrong. */
+};
+
+
+struct board_id {
+ uint32_t type; /* Board type */
+ uint32_t type_inv; /* Board type (inverted) */
+ uint32_t flags; /* Flags */
+};
+
+enum board_id_action {
+ bid_none,
+ bid_get,
+ bid_set
+};
+
+/*
+ * This function allows to retrieve or set (if not initialized) board ID of
+ * the H1 chip. If bid_action is bid_get and show_machine_output is set,
+ * prints out board ID in a machine-friendly format.
+ */
+void process_bid(struct transfer_descriptor *td,
+ enum board_id_action bid_action,
+ struct board_id *bid,
+ bool show_machine_output);
+
+/*
+ * This function can be used to retrieve the current PP status from Cr50 and
+ * prompt the user when a PP press is required.
+ *
+ * Physical presence can be required by different gsctool options, for which
+ * Cr50 behavior also differs. The 'command' and 'poll_type' parameters are
+ * used by Cr50 to tell what the host is polling for.
+ */
+void poll_for_pp(struct transfer_descriptor *td,
+ uint16_t command,
+ uint8_t poll_type);
+
+/*
+ * Function used to send vendor command to the Cr50 and receive a response.
+ * Returns the error code from TPM response header, which is set to zero on
+ * success.
+ */
+uint32_t send_vendor_command(struct transfer_descriptor *td,
+ uint16_t subcommand,
+ const void *command_body,
+ size_t command_body_size,
+ void *response,
+ size_t *response_size);
+
+
+#endif // __EXTRA_USB_UPDATER_GSCTOOL_H
diff --git a/extra/usb_updater/sample_descriptor b/extra/usb_updater/sample_descriptor
new file mode 100644
index 0000000000..1566e9e2e1
--- /dev/null
+++ b/extra/usb_updater/sample_descriptor
@@ -0,0 +1,87 @@
+# Copyright 2018 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# Hash descriptor database file consists of sections for various Chrome OS
+# boards. Each board description section starts with a line of 4 characters
+# which is the board ID (the same as the board's RLZ code).
+#
+# Each board description section includes variable number of range
+# descriptor entries, each entry consisting of semicolon separated fields:
+#
+# {a|e|g}:{h|d}:base_addr:size[:value[:value[:value...]]]]
+#
+# Where
+#
+# - the first sindgle character field defines the way the range is accessed:
+# a - AP flash
+# e - EC flash
+# g - EC flash requiring gang programming mode
+# - the second single character field defines the range type
+# h - Cr50 returns the hash of the range
+# d - Cr50 returns actual contents of the range (hex dump)
+# - the third and and forth fields are base address and size of the range
+# - ranges of type 'h' include one or more values for the hash of the range.
+#
+# Descriptor entries can be split along multiple lines. Each entry is
+# terminated by an empty line. Board description section is completed when
+# another board ID or end of file is encountered.
+#
+# All values are expressed in hex. Repeating empty lines and lines starting
+# with '#' are ignored.
+#
+
+QZUX
+
+# 1: Valid hash section.
+a:h:0:10000:
+756c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf3503:
+336c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf3503:
+446c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf3503
+
+# 2: Valid dump section.
+a:d:10:10
+
+# 3: Valid hash section.
+e:h:0:100:
+55d262badc1116520a7ae1d3fda380c0382b4b87f0db10de6495053ba3aadb87:
+444442badc1116520a7ae1d3fda380c0382b4b87f0db10de6495053ba3aadb87:
+443322badc1116520a7ae1d3fda380c0382b4b87f0db10de6495053ba3aadb87
+
+# 4: Invalid dump section (includes hash)
+a:d:20:10:55d262badc1116520a7ae1d3fda380c0382b4b87f0db10de6495053ba3aadb87
+
+# 5: Invalid hash section (does not include hash)
+e:h:0:100:
+
+# 6: Another invalid hash section (does not include hash)
+e:h:0:100:
+
+# extra empty lines
+
+
+# 7: Invalid hash section (hash too short)
+e:h:0:100:
+55d262badc1116520a7ae1d3fda380c0382b4b87f0db10de6495053ba3aadb8
+
+# 8: Invalid hash section (hash too long)
+a:h:0:10000:
+756c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf35034:
+336c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf3503
+
+# 9: Invalid hash section (hash includes non-hex value)
+a:h:0:10000:
+756c41b90ac9aa23a6c98ce13549dccd7xe0a83f8537eb834d9cfc3d12bf3503:
+336c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf3505
+
+# 10: Invalid hash section (hash does not include 3 variants)
+a:h:0:10000:
+756c41b90ac9aa23a6c98ce13549dccd75e0a83f8537eb834d9cfc3d12bf3503:
+336c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf3505
+
+# 11: Invalid dump section (size includes non hex character)
+a:d:10:10x
+
+ABCD
+
+a:d:10:10
diff --git a/extra/usb_updater/servo_updater.py b/extra/usb_updater/servo_updater.py
new file mode 100755
index 0000000000..0f7d640c39
--- /dev/null
+++ b/extra/usb_updater/servo_updater.py
@@ -0,0 +1,85 @@
+#!/usr/bin/python
+# Copyright 2016 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from __future__ import print_function
+
+import argparse
+import errno
+import os
+import subprocess
+import time
+
+import json
+import fw_update
+import ecusb.tiny_servo_common as c
+
+
+def flash(brdfile, serialno, binfile):
+ p = fw_update.Supdate()
+ p.load_board(brdfile)
+ p.connect_usb(serialname=serialno)
+ p.load_file(binfile)
+
+ # Start transfer and erase.
+ p.start()
+ # Upload the bin file
+ print("Uploading %s" % binfile)
+ p.write_file()
+
+ # Finalize
+ print("Done. Finalizing.")
+ p.stop()
+
+
+def select(vidpid, serialno, region, debuglog=False):
+ if region not in ["rw", "ro"]:
+ raise Exception("Region must be ro or rw")
+
+ # Make sure device is up
+ c.wait_for_usb(vidpid, serialname=serialno)
+
+ # make a console
+ pty = c.setup_tinyservod(vidpid, 0, serialname=serialno, debuglog=debuglog)
+
+ cmd = "sysjump %s\nreboot" % region
+ pty._issue_cmd(cmd)
+ time.sleep(1)
+ pty.close()
+
+
+def main():
+ parser = argparse.ArgumentParser(description="Image a servo micro device")
+ parser.add_argument('-s', '--serialno', type=str,
+ help="serial number to program", default=None)
+ parser.add_argument('-b', '--board', type=str,
+ help="Board configuration json file", default="servo_v4.json")
+ parser.add_argument('-f', '--file', type=str,
+ help="Complete ec.bin file", default="servo_v4.bin")
+ parser.add_argument('-v', '--verbose', action="store_true",
+ help="Chatty output")
+
+ args = parser.parse_args()
+
+ brdfile = args.board
+ binfile = args.file
+ serialno = args.serialno
+ debuglog = (args.verbose is True)
+
+ with open(brdfile) as data_file:
+ data = json.load(data_file)
+
+ vidpid = "%04x:%04x" % (int(data['vid'], 0), int(data['pid'], 0))
+
+ select(vidpid, serialno, "ro", debuglog=debuglog)
+
+ flash(brdfile, serialno, binfile)
+
+ select(vidpid, serialno, "rw", debuglog=debuglog)
+
+ flash(brdfile, serialno, binfile)
+
+if __name__ == "__main__":
+ main()
+
diff --git a/extra/usb_updater/usb_updater2.c b/extra/usb_updater/usb_updater2.c
new file mode 100644
index 0000000000..a9901d177a
--- /dev/null
+++ b/extra/usb_updater/usb_updater2.c
@@ -0,0 +1,1046 @@
+/*
+ * Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <asm/byteorder.h>
+#include <endian.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <libusb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <fmap.h>
+
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+
+#include "compile_time_macros.h"
+#include "misc_util.h"
+#include "usb_descriptor.h"
+#include "update_fw.h"
+#include "vb21_struct.h"
+
+#ifdef DEBUG
+#define debug printf
+#else
+#define debug(fmt, args...)
+#endif
+
+/*
+ * This file contains the source code of a Linux application used to update
+ * EC device firmware (common code only, gsctool takes care of cr50).
+ */
+
+#define VID USB_VID_GOOGLE
+#define PID 0x5022
+#define SUBCLASS USB_SUBCLASS_GOOGLE_UPDATE
+#define PROTOCOL USB_PROTOCOL_GOOGLE_UPDATE
+
+enum exit_values {
+ noop = 0, /* All up to date, no update needed. */
+ all_updated = 1, /* Update completed, reboot required. */
+ rw_updated = 2, /* RO was not updated, reboot required. */
+ update_error = 3 /* Something went wrong. */
+};
+
+struct usb_endpoint {
+ struct libusb_device_handle *devh;
+ uint8_t ep_num;
+ int chunk_len;
+};
+
+struct transfer_descriptor {
+ /*
+ * offsets of section available for update (not currently active).
+ */
+ uint32_t offset;
+
+ struct usb_endpoint uep;
+};
+
+/* Information about the target */
+static struct first_response_pdu targ;
+
+static uint16_t protocol_version;
+static uint16_t header_type;
+static char *progname;
+static char *short_opts = "bd:efhjp:rstuw";
+static const struct option long_opts[] = {
+ /* name hasarg *flag val */
+ {"binvers", 1, NULL, 'b'},
+ {"device", 1, NULL, 'd'},
+ {"entropy", 0, NULL, 'e'},
+ {"fwver", 0, NULL, 'f'},
+ {"help", 0, NULL, 'h'},
+ {"jump_to_rw", 0, NULL, 'j'},
+ {"tp_update", 1, NULL, 'p'},
+ {"reboot", 0, NULL, 'r'},
+ {"stay_in_ro", 0, NULL, 's'},
+ {"tp_info", 0, NULL, 't'},
+ {"unlock_rollback", 0, NULL, 'u'},
+ {"unlock_rw", 0, NULL, 'w'},
+ {},
+};
+
+/* Release USB device and return error to the OS. */
+static void shut_down(struct usb_endpoint *uep)
+{
+ libusb_close(uep->devh);
+ libusb_exit(NULL);
+ exit(update_error);
+}
+
+static void usage(int errs)
+{
+ printf("\nUsage: %s [options] <binary image>\n"
+ "\n"
+ "This updates EC firmware over USB (common code EC, no cr50).\n"
+ "The required argument is the full RO+RW image.\n"
+ "\n"
+ "Options:\n"
+ "\n"
+ " -b,--binvers Report versions of image's "
+ "RW and RO, do not update\n"
+ " -d,--device VID:PID USB device (default %04x:%04x)\n"
+ " -f,--fwver Report running firmware versions.\n"
+ " -h,--help Show this message\n"
+ " -e,--entropy Add entropy to device secret\n"
+ " -j,--jump_to_rw Tell EC to jump to RW\n"
+ " -p,--tp_update file Update touchpad FW\n"
+ " -r,--reboot Tell EC to reboot\n"
+ " -s,--stay_in_ro Tell EC to stay in RO\n"
+ " -t,--tp_info Get touchpad information\n"
+ " -u,--unlock_rollback Tell EC to unlock the rollback region\n"
+ " -w,--unlock_rw Tell EC to unlock the RW region\n"
+ "\n", progname, VID, PID);
+
+ exit(errs ? update_error : noop);
+}
+
+/* Read file into buffer */
+static uint8_t *get_file_or_die(const char *filename, size_t *len_ptr)
+{
+ FILE *fp;
+ struct stat st;
+ uint8_t *data;
+ size_t len;
+
+ fp = fopen(filename, "rb");
+ if (!fp) {
+ perror(filename);
+ exit(update_error);
+ }
+ if (fstat(fileno(fp), &st)) {
+ perror("stat");
+ exit(update_error);
+ }
+
+ len = st.st_size;
+
+ data = malloc(len);
+ if (!data) {
+ perror("malloc");
+ exit(update_error);
+ }
+
+ if (fread(data, st.st_size, 1, fp) != 1) {
+ perror("fread");
+ exit(update_error);
+ }
+
+ fclose(fp);
+
+ *len_ptr = len;
+ return data;
+}
+
+#define USB_ERROR(m, r) \
+ fprintf(stderr, "%s:%d, %s returned %d (%s)\n", __FILE__, __LINE__, \
+ m, r, libusb_strerror(r))
+
+/*
+ * Actual USB transfer function, the 'allow_less' flag indicates that the
+ * valid response could be shortef than allotted memory, the 'rxed_count'
+ * pointer, if provided along with 'allow_less' lets the caller know how mavy
+ * bytes were received.
+ */
+static void do_xfer(struct usb_endpoint *uep, void *outbuf, int outlen,
+ void *inbuf, int inlen, int allow_less,
+ size_t *rxed_count)
+{
+
+ int r, actual;
+
+ /* Send data out */
+ if (outbuf && outlen) {
+ actual = 0;
+ r = libusb_bulk_transfer(uep->devh, uep->ep_num,
+ outbuf, outlen,
+ &actual, 1000);
+ if (r < 0) {
+ USB_ERROR("libusb_bulk_transfer", r);
+ exit(update_error);
+ }
+ if (actual != outlen) {
+ fprintf(stderr, "%s:%d, only sent %d/%d bytes\n",
+ __FILE__, __LINE__, actual, outlen);
+ shut_down(uep);
+ }
+ }
+
+ /* Read reply back */
+ if (inbuf && inlen) {
+
+ actual = 0;
+ r = libusb_bulk_transfer(uep->devh, uep->ep_num | 0x80,
+ inbuf, inlen,
+ &actual, 1000);
+ if (r < 0) {
+ USB_ERROR("libusb_bulk_transfer", r);
+ exit(update_error);
+ }
+ if ((actual != inlen) && !allow_less) {
+ fprintf(stderr, "%s:%d, only received %d/%d bytes\n",
+ __FILE__, __LINE__, actual, inlen);
+ shut_down(uep);
+ }
+
+ if (rxed_count)
+ *rxed_count = actual;
+ }
+}
+
+static void xfer(struct usb_endpoint *uep, void *outbuf,
+ size_t outlen, void *inbuf, size_t inlen)
+{
+ do_xfer(uep, outbuf, outlen, inbuf, inlen, 0, NULL);
+}
+
+/* Return 0 on error, since it's never gonna be EP 0 */
+static int find_endpoint(const struct libusb_interface_descriptor *iface,
+ struct usb_endpoint *uep)
+{
+ const struct libusb_endpoint_descriptor *ep;
+
+ if (iface->bInterfaceClass == 255 &&
+ iface->bInterfaceSubClass == SUBCLASS &&
+ iface->bInterfaceProtocol == PROTOCOL &&
+ iface->bNumEndpoints) {
+ ep = &iface->endpoint[0];
+ uep->ep_num = ep->bEndpointAddress & 0x7f;
+ uep->chunk_len = ep->wMaxPacketSize;
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Return -1 on error */
+static int find_interface(struct usb_endpoint *uep)
+{
+ int iface_num = -1;
+ int r, i, j;
+ struct libusb_device *dev;
+ struct libusb_config_descriptor *conf = 0;
+ const struct libusb_interface *iface0;
+ const struct libusb_interface_descriptor *iface;
+
+ dev = libusb_get_device(uep->devh);
+ r = libusb_get_active_config_descriptor(dev, &conf);
+ if (r < 0) {
+ USB_ERROR("libusb_get_active_config_descriptor", r);
+ goto out;
+ }
+
+ for (i = 0; i < conf->bNumInterfaces; i++) {
+ iface0 = &conf->interface[i];
+ for (j = 0; j < iface0->num_altsetting; j++) {
+ iface = &iface0->altsetting[j];
+ if (find_endpoint(iface, uep)) {
+ iface_num = i;
+ goto out;
+ }
+ }
+ }
+
+out:
+ libusb_free_config_descriptor(conf);
+ return iface_num;
+}
+
+/* Returns true if parsed. */
+static int parse_vidpid(const char *input, uint16_t *vid_ptr, uint16_t *pid_ptr)
+{
+ char *copy, *s, *e = 0;
+
+ copy = strdup(input);
+
+ s = strchr(copy, ':');
+ if (!s)
+ return 0;
+ *s++ = '\0';
+
+ *vid_ptr = (uint16_t) strtoul(copy, &e, 16);
+ if (!*optarg || (e && *e))
+ return 0;
+
+ *pid_ptr = (uint16_t) strtoul(s, &e, 16);
+ if (!*optarg || (e && *e))
+ return 0;
+
+ return 1;
+}
+
+
+static void usb_findit(uint16_t vid, uint16_t pid, struct usb_endpoint *uep)
+{
+ int iface_num, r;
+
+ memset(uep, 0, sizeof(*uep));
+
+ r = libusb_init(NULL);
+ if (r < 0) {
+ USB_ERROR("libusb_init", r);
+ exit(update_error);
+ }
+
+ printf("open_device %04x:%04x\n", vid, pid);
+ /* NOTE: This doesn't handle multiple matches! */
+ uep->devh = libusb_open_device_with_vid_pid(NULL, vid, pid);
+ if (!uep->devh) {
+ fprintf(stderr, "Can't find device\n");
+ exit(update_error);
+ }
+
+ iface_num = find_interface(uep);
+ if (iface_num < 0) {
+ fprintf(stderr, "USB FW update not supported by that device\n");
+ shut_down(uep);
+ }
+ if (!uep->chunk_len) {
+ fprintf(stderr, "wMaxPacketSize isn't valid\n");
+ shut_down(uep);
+ }
+
+ printf("found interface %d endpoint %d, chunk_len %d\n",
+ iface_num, uep->ep_num, uep->chunk_len);
+
+ libusb_set_auto_detach_kernel_driver(uep->devh, 1);
+ r = libusb_claim_interface(uep->devh, iface_num);
+ if (r < 0) {
+ USB_ERROR("libusb_claim_interface", r);
+ shut_down(uep);
+ }
+
+ printf("READY\n-------\n");
+}
+
+static int transfer_block(struct usb_endpoint *uep,
+ struct update_frame_header *ufh,
+ uint8_t *transfer_data_ptr, size_t payload_size)
+{
+ size_t transfer_size;
+ uint32_t reply;
+ int actual;
+ int r;
+
+ /* First send the header. */
+ xfer(uep, ufh, sizeof(*ufh), NULL, 0);
+
+ /* Now send the block, chunk by chunk. */
+ for (transfer_size = 0; transfer_size < payload_size;) {
+ int chunk_size;
+
+ chunk_size = MIN(uep->chunk_len, payload_size - transfer_size);
+ xfer(uep, transfer_data_ptr, chunk_size, NULL, 0);
+ transfer_data_ptr += chunk_size;
+ transfer_size += chunk_size;
+ }
+
+ /* Now get the reply. */
+ r = libusb_bulk_transfer(uep->devh, uep->ep_num | 0x80,
+ (void *) &reply, sizeof(reply),
+ &actual, 1000);
+ if (r) {
+ if (r == -7) {
+ fprintf(stderr, "Timeout!\n");
+ return r;
+ }
+ USB_ERROR("libusb_bulk_transfer", r);
+ shut_down(uep);
+ }
+
+ reply = *((uint8_t *)&reply);
+ if (reply) {
+ fprintf(stderr, "Error: status %#x\n", reply);
+ exit(update_error);
+ }
+
+ return 0;
+}
+
+/**
+ * Transfer an image section (typically RW or RO).
+ *
+ * td - transfer descriptor to use to communicate with the target
+ * data_ptr - pointer at the section base in the image
+ * section_addr - address of the section in the target memory space
+ * data_len - section size
+ * smart_update - non-zero to enable the smart trailing of 0xff.
+ */
+static void transfer_section(struct transfer_descriptor *td,
+ uint8_t *data_ptr,
+ uint32_t section_addr,
+ size_t data_len,
+ uint8_t smart_update)
+{
+ /*
+ * Actually, we can skip trailing chunks of 0xff, as the entire
+ * section space must be erased before the update is attempted.
+ *
+ * FIXME: We can be smarter than this and skip blocks within the image.
+ */
+ if (smart_update)
+ while (data_len && (data_ptr[data_len - 1] == 0xff))
+ data_len--;
+
+ printf("sending 0x%zx bytes to %#x\n", data_len, section_addr);
+ while (data_len) {
+ size_t payload_size;
+ uint32_t block_base;
+ int max_retries;
+
+ /* prepare the header to prepend to the block. */
+ payload_size = MIN(data_len, targ.common.maximum_pdu_size);
+
+ block_base = htobe32(section_addr);
+
+ struct update_frame_header ufh;
+
+ ufh.block_size = htobe32(payload_size +
+ sizeof(struct update_frame_header));
+ ufh.cmd.block_base = block_base;
+ ufh.cmd.block_digest = 0;
+ for (max_retries = 10; max_retries; max_retries--)
+ if (!transfer_block(&td->uep, &ufh,
+ data_ptr, payload_size))
+ break;
+
+ if (!max_retries) {
+ fprintf(stderr,
+ "Failed to transfer block, %zd to go\n",
+ data_len);
+ exit(update_error);
+ }
+ data_len -= payload_size;
+ data_ptr += payload_size;
+ section_addr += payload_size;
+ }
+}
+
+/*
+ * Each RO or RW section of the new image can be in one of the following
+ * states.
+ */
+enum upgrade_status {
+ not_needed = 0, /* Version below or equal that on the target. */
+ not_possible, /*
+ * RO is newer, but can't be transferred due to
+ * target RW shortcomings.
+ */
+ needed /*
+ * This section needs to be transferred to the
+ * target.
+ */
+};
+
+/* This array describes all sections of the new image. */
+static struct {
+ const char *name;
+ uint32_t offset;
+ uint32_t size;
+ enum upgrade_status ustatus;
+ char version[32];
+ int32_t rollback;
+ uint32_t key_version;
+} sections[] = {
+ {"RO"},
+ {"RW"}
+};
+
+static const struct fmap_area *fmap_find_area_or_die(const struct fmap *fmap,
+ const char *name)
+{
+ const struct fmap_area *fmaparea;
+
+ fmaparea = fmap_find_area(fmap, name);
+ if (!fmaparea) {
+ fprintf(stderr, "Cannot find FMAP area %s\n", name);
+ exit(update_error);
+ }
+
+ return fmaparea;
+}
+
+/*
+ * Scan the new image and retrieve versions of all sections.
+ */
+static void fetch_header_versions(const uint8_t *image, size_t len)
+{
+ const struct fmap *fmap;
+ const struct fmap_area *fmaparea;
+ long int offset;
+ size_t i;
+
+ offset = fmap_find(image, len);
+ if (offset < 0) {
+ fprintf(stderr, "Cannot find FMAP in image\n");
+ exit(update_error);
+ }
+ fmap = (const struct fmap *)(image+offset);
+
+ /* FIXME: validate fmap struct more than this? */
+ if (fmap->size != len) {
+ fprintf(stderr, "Mismatch between FMAP size and image size\n");
+ exit(update_error);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sections); i++) {
+ const char *fmap_name;
+ const char *fmap_fwid_name;
+ const char *fmap_rollback_name = NULL;
+ const char *fmap_key_name = NULL;
+
+ if (!strcmp(sections[i].name, "RO")) {
+ fmap_name = "EC_RO";
+ fmap_fwid_name = "RO_FRID";
+ } else if (!strcmp(sections[i].name, "RW")) {
+ fmap_name = "EC_RW";
+ fmap_fwid_name = "RW_FWID";
+ fmap_rollback_name = "RW_RBVER";
+ /*
+ * Key version comes from key RO (RW signature does not
+ * contain the key version.
+ */
+ fmap_key_name = "KEY_RO";
+ } else {
+ fprintf(stderr, "Invalid section name\n");
+ exit(update_error);
+ }
+
+ fmaparea = fmap_find_area_or_die(fmap, fmap_name);
+
+ /* FIXME: endianness? */
+ sections[i].offset = fmaparea->offset;
+ sections[i].size = fmaparea->size;
+
+ fmaparea = fmap_find_area_or_die(fmap, fmap_fwid_name);
+
+ if (fmaparea->size != sizeof(sections[i].version)) {
+ fprintf(stderr, "Invalid fwid size\n");
+ exit(update_error);
+ }
+ memcpy(sections[i].version, image+fmaparea->offset,
+ fmaparea->size);
+
+ sections[i].rollback = -1;
+ if (fmap_rollback_name) {
+ fmaparea = fmap_find_area(fmap, fmap_rollback_name);
+ if (fmaparea)
+ memcpy(&sections[i].rollback,
+ image+fmaparea->offset,
+ sizeof(sections[i].rollback));
+ }
+
+ sections[i].key_version = -1;
+ if (fmap_key_name) {
+ fmaparea = fmap_find_area(fmap, fmap_key_name);
+ if (fmaparea) {
+ const struct vb21_packed_key *key =
+ (const void *)(image+fmaparea->offset);
+ sections[i].key_version = key->key_version;
+ }
+ }
+ }
+}
+
+static int show_headers_versions(const void *image)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(sections); i++) {
+ printf("%s off=%08x/%08x v=%.32s rb=%d kv=%d\n",
+ sections[i].name, sections[i].offset, sections[i].size,
+ sections[i].version, sections[i].rollback,
+ sections[i].key_version);
+ }
+ return 0;
+}
+
+/*
+ * Pick sections to transfer based on information retrieved from the target,
+ * the new image, and the protocol version the target is running.
+ */
+static void pick_sections(struct transfer_descriptor *td)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(sections); i++) {
+ uint32_t offset = sections[i].offset;
+
+ /* Skip currently active section. */
+ if (offset != td->offset)
+ continue;
+
+ sections[i].ustatus = needed;
+ }
+}
+
+static void setup_connection(struct transfer_descriptor *td)
+{
+ size_t rxed_size;
+ size_t i;
+ uint32_t error_code;
+
+ /*
+ * Need to be backwards compatible, communicate with targets running
+ * different protocol versions.
+ */
+ union {
+ struct first_response_pdu rpdu;
+ uint32_t legacy_resp;
+ } start_resp;
+
+ /* Send start request. */
+ printf("start\n");
+
+ struct update_frame_header ufh;
+ uint8_t inbuf[td->uep.chunk_len];
+ int actual = 0;
+
+ /* Flush all data from endpoint to recover in case of error. */
+ while (!libusb_bulk_transfer(td->uep.devh,
+ td->uep.ep_num | 0x80,
+ (void *)&inbuf, td->uep.chunk_len,
+ &actual, 10)) {
+ printf("flush\n");
+ }
+
+ memset(&ufh, 0, sizeof(ufh));
+ ufh.block_size = htobe32(sizeof(ufh));
+ do_xfer(&td->uep, &ufh, sizeof(ufh), &start_resp,
+ sizeof(start_resp), 1, &rxed_size);
+
+ /* We got something. Check for errors in response */
+ if (rxed_size < 8) {
+ fprintf(stderr, "Unexpected response size %zd: ", rxed_size);
+ for (i = 0; i < rxed_size; i++)
+ fprintf(stderr, " %02x", ((uint8_t *)&start_resp)[i]);
+ fprintf(stderr, "\n");
+ exit(update_error);
+ }
+
+ protocol_version = be16toh(start_resp.rpdu.protocol_version);
+ if (protocol_version < 5 || protocol_version > 6) {
+ fprintf(stderr, "Unsupported protocol version %d\n",
+ protocol_version);
+ exit(update_error);
+ }
+
+ header_type = be16toh(start_resp.rpdu.header_type);
+
+ printf("target running protocol version %d (type %d)\n",
+ protocol_version, header_type);
+ if (header_type != UPDATE_HEADER_TYPE_COMMON) {
+ fprintf(stderr, "Unsupported header type %d\n",
+ header_type);
+ exit(update_error);
+ }
+
+ error_code = be32toh(start_resp.rpdu.return_value);
+
+ if (error_code) {
+ fprintf(stderr, "Target reporting error %d\n", error_code);
+ shut_down(&td->uep);
+ exit(update_error);
+ }
+
+ td->offset = be32toh(start_resp.rpdu.common.offset);
+ memcpy(targ.common.version, start_resp.rpdu.common.version,
+ sizeof(start_resp.rpdu.common.version));
+ targ.common.maximum_pdu_size =
+ be32toh(start_resp.rpdu.common.maximum_pdu_size);
+ targ.common.flash_protection =
+ be32toh(start_resp.rpdu.common.flash_protection);
+ targ.common.min_rollback = be32toh(start_resp.rpdu.common.min_rollback);
+ targ.common.key_version = be32toh(start_resp.rpdu.common.key_version);
+
+ printf("maximum PDU size: %d\n", targ.common.maximum_pdu_size);
+ printf("Flash protection status: %04x\n", targ.common.flash_protection);
+ printf("version: %32s\n", targ.common.version);
+ printf("key_version: %d\n", targ.common.key_version);
+ printf("min_rollback: %d\n", targ.common.min_rollback);
+ printf("offset: writable at %#x\n", td->offset);
+
+ pick_sections(td);
+}
+
+/*
+ * Channel TPM extension/vendor command over USB. The payload of the USB frame
+ * in this case consists of the 2 byte subcommand code concatenated with the
+ * command body. The caller needs to indicate if a response is expected, and
+ * if it is - of what maximum size.
+ */
+static int ext_cmd_over_usb(struct usb_endpoint *uep, uint16_t subcommand,
+ void *cmd_body, size_t body_size,
+ void *resp, size_t *resp_size)
+{
+ struct update_frame_header *ufh;
+ uint16_t *frame_ptr;
+ size_t usb_msg_size;
+
+ usb_msg_size = sizeof(struct update_frame_header) +
+ sizeof(subcommand) + body_size;
+
+ ufh = malloc(usb_msg_size);
+ if (!ufh) {
+ printf("%s: failed to allocate %zd bytes\n",
+ __func__, usb_msg_size);
+ return -1;
+ }
+
+ ufh->block_size = htobe32(usb_msg_size);
+ ufh->cmd.block_digest = 0;
+ ufh->cmd.block_base = htobe32(UPDATE_EXTRA_CMD);
+ frame_ptr = (uint16_t *)(ufh + 1);
+ *frame_ptr = htobe16(subcommand);
+
+ if (body_size)
+ memcpy(frame_ptr + 1, cmd_body, body_size);
+
+ xfer(uep, ufh, usb_msg_size, resp, resp_size ? *resp_size : 0);
+
+ free(ufh);
+ return 0;
+}
+
+/*
+ * Indicate to the target that update image transfer has been completed. Upon
+ * receiveing of this message the target state machine transitions into the
+ * 'rx_idle' state. The host may send an extension command to reset the target
+ * after this.
+ */
+static void send_done(struct usb_endpoint *uep)
+{
+ uint32_t out;
+
+ /* Send stop request, ignoring reply. */
+ out = htobe32(UPDATE_DONE);
+ xfer(uep, &out, sizeof(out), &out, 1);
+}
+
+static void send_subcommand(struct transfer_descriptor *td, uint16_t subcommand,
+ void *cmd_body, size_t body_size,
+ uint8_t *response, size_t response_size)
+{
+ send_done(&td->uep);
+
+ ext_cmd_over_usb(&td->uep, subcommand,
+ cmd_body, body_size,
+ response, &response_size);
+ printf("sent command %x, resp %x\n", subcommand, response[0]);
+}
+
+/* Returns number of successfully transmitted image sections. */
+static int transfer_image(struct transfer_descriptor *td,
+ uint8_t *data, size_t data_len)
+{
+ size_t i;
+ int num_txed_sections = 0;
+
+ for (i = 0; i < ARRAY_SIZE(sections); i++)
+ if (sections[i].ustatus == needed) {
+ transfer_section(td,
+ data + sections[i].offset,
+ sections[i].offset,
+ sections[i].size, 1);
+ num_txed_sections++;
+ }
+
+ /*
+ * Move USB receiver sate machine to idle state so that vendor
+ * commands can be processed later, if any.
+ */
+ send_done(&td->uep);
+
+ if (!num_txed_sections)
+ printf("nothing to do\n");
+ else
+ printf("-------\nupdate complete\n");
+ return num_txed_sections;
+}
+
+static void generate_reset_request(struct transfer_descriptor *td)
+{
+ size_t response_size;
+ uint8_t response;
+ uint16_t subcommand;
+ uint8_t command_body[2]; /* Max command body size. */
+ size_t command_body_size;
+
+ if (protocol_version < 6) {
+ /*
+ * Send a second stop request, which should reboot
+ * without replying.
+ */
+ send_done(&td->uep);
+ /* Nothing we can do over /dev/tpm0 running versions below 6. */
+ return;
+ }
+
+ /*
+ * If the user explicitly wants it, request post reset instead of
+ * immediate reset. In this case next time the target reboots, the h1
+ * will reboot as well, and will consider running the uploaded code.
+ *
+ * In case target RW version is 19 or above, to reset the target the
+ * host is supposed to send the command to enable the uploaded image
+ * disabled by default.
+ *
+ * Otherwise the immediate reset command would suffice.
+ */
+ /* Most common case. */
+ command_body_size = 0;
+ response_size = 1;
+ subcommand = UPDATE_EXTRA_CMD_IMMEDIATE_RESET;
+ ext_cmd_over_usb(&td->uep, subcommand,
+ command_body, command_body_size,
+ &response, &response_size);
+
+ printf("reboot not triggered\n");
+}
+
+static void get_random(uint8_t *data, int len)
+{
+ FILE *fp;
+ int i = 0;
+
+ fp = fopen("/dev/random", "rb");
+ if (!fp) {
+ perror("Can't open /dev/random");
+ exit(update_error);
+ }
+
+ while (i < len) {
+ int ret = fread(data+i, len-i, 1, fp);
+
+ if (ret < 0) {
+ perror("fread");
+ exit(update_error);
+ }
+
+ i += ret;
+ }
+
+ fclose(fp);
+}
+
+static void hexdump(const uint8_t *data, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ printf("%02x", data[i]);
+ if ((i % 16) == 15)
+ printf("\n");
+ }
+
+ if ((len % 16) != 0)
+ printf("\n");
+}
+
+int main(int argc, char *argv[])
+{
+ struct transfer_descriptor td;
+ int errorcnt;
+ uint8_t *data = 0;
+ size_t data_len = 0;
+ uint16_t vid = VID, pid = PID;
+ int i;
+ size_t j;
+ int transferred_sections = 0;
+ int binary_vers = 0;
+ int show_fw_ver = 0;
+ int touchpad_update = 0;
+ int extra_command = -1;
+ uint8_t extra_command_data[32];
+ int extra_command_data_len = 0;
+ uint8_t extra_command_answer[64];
+ int extra_command_answer_len = 1;
+
+ progname = strrchr(argv[0], '/');
+ if (progname)
+ progname++;
+ else
+ progname = argv[0];
+
+ /* Usb transfer - default mode. */
+ memset(&td, 0, sizeof(td));
+
+ errorcnt = 0;
+ opterr = 0; /* quiet, you */
+ while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) {
+ switch (i) {
+ case 'b':
+ binary_vers = 1;
+ break;
+ case 'd':
+ if (!parse_vidpid(optarg, &vid, &pid)) {
+ printf("Invalid argument: \"%s\"\n", optarg);
+ errorcnt++;
+ }
+ break;
+ case 'f':
+ show_fw_ver = 1;
+ break;
+ case 'h':
+ usage(errorcnt);
+ break;
+ case 'e':
+ get_random(extra_command_data, 32);
+ extra_command_data_len = 32;
+ extra_command = UPDATE_EXTRA_CMD_INJECT_ENTROPY;
+ break;
+ case 'j':
+ extra_command = UPDATE_EXTRA_CMD_JUMP_TO_RW;
+ break;
+ case 'p':
+ touchpad_update = 1;
+
+ data = get_file_or_die(optarg, &data_len);
+ printf("read %zd(%#zx) bytes from %s\n",
+ data_len, data_len, argv[optind]);
+
+ break;
+ case 'r':
+ extra_command = UPDATE_EXTRA_CMD_IMMEDIATE_RESET;
+ break;
+ case 's':
+ extra_command = UPDATE_EXTRA_CMD_STAY_IN_RO;
+ break;
+ case 't':
+ extra_command = UPDATE_EXTRA_CMD_TOUCHPAD_INFO;
+ extra_command_answer_len =
+ sizeof(struct touchpad_info);
+ break;
+ case 'u':
+ extra_command = UPDATE_EXTRA_CMD_UNLOCK_ROLLBACK;
+ break;
+ case 'w':
+ extra_command = UPDATE_EXTRA_CMD_UNLOCK_RW;
+ break;
+ case 0: /* auto-handled option */
+ break;
+ case '?':
+ if (optopt)
+ printf("Unrecognized option: -%c\n", optopt);
+ else
+ printf("Unrecognized option: %s\n",
+ argv[optind - 1]);
+ errorcnt++;
+ break;
+ case ':':
+ printf("Missing argument to %s\n", argv[optind - 1]);
+ errorcnt++;
+ break;
+ default:
+ printf("Internal error at %s:%d\n", __FILE__, __LINE__);
+ exit(update_error);
+ }
+ }
+
+ if (errorcnt)
+ usage(errorcnt);
+
+ if (!show_fw_ver && extra_command == -1 && !touchpad_update) {
+ if (optind >= argc) {
+ fprintf(stderr,
+ "\nERROR: Missing required <binary image>\n\n");
+ usage(1);
+ }
+
+ data = get_file_or_die(argv[optind], &data_len);
+ printf("read %zd(%#zx) bytes from %s\n",
+ data_len, data_len, argv[optind]);
+
+ fetch_header_versions(data, data_len);
+
+ if (binary_vers)
+ exit(show_headers_versions(data));
+ } else {
+ if (optind < argc)
+ printf("Ignoring binary image %s\n", argv[optind]);
+ }
+
+ usb_findit(vid, pid, &td.uep);
+
+ setup_connection(&td);
+
+ if (show_fw_ver) {
+ printf("Current versions:\n");
+ printf("Writable %32s\n", targ.common.version);
+ }
+
+ if (data) {
+ if (touchpad_update) {
+ transfer_section(&td,
+ data,
+ 0x80000000,
+ data_len, 0);
+ free(data);
+
+ send_done(&td.uep);
+ } else {
+ transferred_sections = transfer_image(&td,
+ data, data_len);
+ free(data);
+
+ if (transferred_sections)
+ generate_reset_request(&td);
+ }
+ } else if (extra_command > -1) {
+ send_subcommand(&td, extra_command,
+ extra_command_data, extra_command_data_len,
+ extra_command_answer, extra_command_answer_len);
+
+ if (extra_command == UPDATE_EXTRA_CMD_TOUCHPAD_INFO)
+ hexdump(extra_command_answer, extra_command_answer_len);
+ }
+
+ libusb_close(td.uep.devh);
+ libusb_exit(NULL);
+
+ if (!transferred_sections)
+ return noop;
+ /*
+ * We should indicate if RO update was not done because of the
+ * insufficient RW version.
+ */
+ for (j = 0; j < ARRAY_SIZE(sections); j++)
+ if (sections[j].ustatus == not_possible) {
+ /* This will allow scripting repeat attempts. */
+ printf("Failed to update RO, run the command again\n");
+ return rw_updated;
+ }
+
+ printf("image updated\n");
+ return all_updated;
+}
diff --git a/extra/usb_updater/verify_ro.c b/extra/usb_updater/verify_ro.c
new file mode 100644
index 0000000000..20354d2692
--- /dev/null
+++ b/extra/usb_updater/verify_ro.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "config.h"
+#include "desc_parser.h"
+#include "gsctool.h"
+#include "tpm_vendor_cmds.h"
+#include "verify_ro.h"
+
+
+/*
+ * Print out passed in buffer contents in hex, 16 bytes per line, each line
+ * starting with the base address value.
+ *
+ * If the passed in base address is not aligned at 16 byte boundary, skip
+ * positions in the dump line so that the address is displayed rounded down to
+ * the closest lower 16 byte boundary.
+ *
+ * For instance passing base of 0x4007 and size of 20 will result in a
+ * printout like:
+ *
+ * 004000 e0 00 00 00 00 66 c7 05 04
+ * 004010 80 06 e0 06 00 66 c7 05 20 90 06
+ *
+ * If title is nonzero - print out the string it points to before printing
+ * out buffer contents.
+ */
+static void print_buffer_aligned(const char *title, uint32_t base,
+ size_t size, const void *data)
+{
+ const uint8_t *bytes = data;
+ size_t i;
+ uint8_t alignment;
+
+ /*
+ * Calculate how many characters we need to skip in the first dump
+ * line.
+ */
+ alignment = base % 16;
+ if (alignment) {
+ size += alignment;
+ base &= ~0xf;
+ }
+
+ if (title)
+ printf("%s\n", title);
+
+ /* Let's print data space separated 16 bytes per line. */
+ for (i = 0; i < size; i++) {
+ if (!(i % 16))
+ printf("\n%06zx", base + i);
+
+ if (i < alignment)
+ printf(" ");
+ else
+ printf(" %02x", bytes[i - alignment]);
+ }
+}
+
+/* Change the DUT spihash range to the new_type value. */
+static int set_new_range(struct transfer_descriptor *td,
+ enum range_type_t new_type)
+{
+ uint32_t rv;
+ struct vendor_cc_spi_hash_request req;
+
+ memset(&req, 0, sizeof(req));
+
+ /* Need to send command to change spihash mode. */
+ switch (new_type) {
+ case AP_RANGE:
+ req.subcmd = SPI_HASH_SUBCMD_AP;
+ break;
+ case EC_RANGE:
+ req.subcmd = SPI_HASH_SUBCMD_EC;
+ break;
+ case EC_GANG_RANGE:
+ req.subcmd = SPI_HASH_SUBCMD_EC;
+ req.flags = SPI_HASH_FLAG_EC_GANG;
+ break;
+ default: /* Should never happen. */
+ return -EINVAL;
+ }
+
+ rv = send_vendor_command(td, VENDOR_CC_SPI_HASH, &req, sizeof(req),
+ 0, NULL);
+
+ if (!rv)
+ return 0;
+
+ if (rv == VENDOR_RC_IN_PROGRESS) {
+ /* This will exit() on error. */
+ poll_for_pp(td, VENDOR_CC_SPI_HASH, SPI_HASH_PP_POLL);
+ } else {
+ fprintf(stderr,
+ "%s: failed setting range type %d, error %d\n",
+ __func__, new_type, rv);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Verify a dump descriptor hash section defined by 'range'. The passed in by
+ * pointer structure req has the range offset and size already initialized.
+ *
+ * Make sure that matching hashes are at the same index in the hash variants
+ * array in all hash sections.
+ */
+static int verify_hash_section(struct transfer_descriptor *td,
+ struct vendor_cc_spi_hash_request *req,
+ struct addr_range *range)
+{
+ static ssize_t matching_range = -1;
+ size_t i;
+ uint8_t response[sizeof(range->variants->expected_result)];
+ size_t response_size;
+ int rv;
+
+ /* First retrieve hash from the DUT. */
+ response_size = sizeof(response);
+ req->subcmd = SPI_HASH_SUBCMD_SHA256;
+ rv = send_vendor_command(td, VENDOR_CC_SPI_HASH,
+ req, sizeof(*req), response, &response_size);
+
+ if (rv) {
+ fprintf(stderr,
+ "%s: failed retrieving hash at %x, tpm error %d\n",
+ __func__, req->offset, rv);
+ return -EINVAL;
+ }
+
+ if (response_size != sizeof(response)) {
+ fprintf(stderr, "got %zd bytes in response for range %x:%x\n",
+ response_size, req->offset, req->size);
+ return -EINVAL;
+ }
+
+ if (matching_range < 0) {
+ /* This is the first hash range to be processed. */
+ struct result_node *variant = range->variants;
+
+ for (i = 0; i < range->variant_count; i++) {
+ if (!memcmp(variant->expected_result,
+ response, response_size)) {
+ matching_range = i;
+ return 0;
+ }
+ variant++;
+ }
+
+ fprintf(stderr, "no matching hash found for range %x:%x\n",
+ req->offset, req->size);
+ return -EINVAL;
+ }
+
+ if (!memcmp(range->variants[matching_range].expected_result,
+ response, response_size))
+ return 0;
+
+ fprintf(stderr, "hash mismatch for range %x:%x\n",
+ req->offset, req->size);
+
+ return -EINVAL;
+}
+
+/*
+ * Dump DUT's memory in the range defined by contents of the passed in req
+ * structure.
+ *
+ * The Cr50 SPI hash dump vendor command implementation limits size of the
+ * dump to 32, so in case the caller requests more than 32 bytes retrieve them
+ * in 32 byte blocks.
+ *
+ * If base address of the range is not aligned at 16, retrieve smaller
+ * quantity such that the following transactions retrieve block starting at
+ * aligned addresses, this makes for a better looking hex dump.
+ */
+static int dump_range(struct transfer_descriptor *td,
+ struct vendor_cc_spi_hash_request *req)
+{
+ size_t remaining_size = req->size;
+ size_t response_size;
+ /* Max size of a single shot is 32 bytes. */
+ const size_t max_transfer = 32;
+ uint8_t response[max_transfer];
+
+ req->subcmd = SPI_HASH_SUBCMD_DUMP;
+ while (remaining_size) {
+ size_t shot_size = max_transfer;
+ uint8_t alignment;
+ uint32_t rv;
+
+ alignment = req->offset % 16;
+
+ if (alignment && ((alignment + remaining_size) > max_transfer))
+ /* first line should be truncated. */
+ shot_size = max_transfer - alignment;
+ else if (shot_size > remaining_size)
+ shot_size = remaining_size;
+
+ req->size = shot_size;
+ response_size = shot_size;
+ rv = send_vendor_command(td, VENDOR_CC_SPI_HASH,
+ req, sizeof(*req), response,
+ &response_size);
+ if (rv) {
+ fprintf(stderr,
+ "%s: failed getting dump contents at %x\n",
+ __func__, req->offset);
+ return -EINVAL;
+ }
+
+ if (response_size != shot_size) {
+ fprintf(stderr,
+ "%s: dump error: got %zd bytes, expected %zd\n",
+ __func__, response_size, shot_size);
+ return -EINVAL;
+ }
+
+ print_buffer_aligned(NULL, req->offset, shot_size, response);
+ remaining_size -= shot_size;
+ req->offset += shot_size;
+ }
+ printf("\n");
+
+ return 0;
+}
+
+/*
+ * Iterate through sections of a board descriptor database, retrieving hashes
+ * or straight memory blocks as defined by description sections.
+ */
+static int process_descriptor_sections(struct transfer_descriptor *td)
+{
+ struct vendor_cc_spi_hash_request req;
+ int rv;
+ struct addr_range *range;
+ enum range_type_t current_range = NOT_A_RANGE;
+
+ do {
+ /*
+ * Retrieve next range descriptor section from the descriptor
+ * database. The function below is guaranteed to set range to
+ * NULL on any error.
+ */
+ rv = parser_get_next_range(&range);
+ if (rv) {
+ /*
+ * ENODATA means all board's sections have been
+ * processed.
+ */
+ if (rv == -ENODATA)
+ rv = 0;
+ break;
+ }
+
+ if (current_range != range->range_type) {
+ rv = set_new_range(td, range->range_type);
+ if (rv)
+ break;
+ }
+
+ memset(&req, 0, sizeof(req));
+ req.offset = range->base_addr;
+ req.size = range->range_size;
+
+ if (range->variant_count)
+ rv = verify_hash_section(td, &req, range);
+ else
+ rv = dump_range(td, &req);
+
+ free(range);
+ range = NULL;
+ } while (!rv);
+
+ if (range)
+ free(range);
+
+ parser_done();
+
+ if (rv)
+ return rv;
+
+ memset(&req, 0, sizeof(req));
+ req.subcmd = SPI_HASH_SUBCMD_DISABLE;
+ rv = send_vendor_command(td, VENDOR_CC_SPI_HASH, &req,
+ sizeof(req), 0, NULL);
+ if (rv) {
+ fprintf(stderr,
+ "%s: spi hash disable TPM error %d\n", __func__, rv);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int verify_ro(struct transfer_descriptor *td,
+ const char *desc_file_name,
+ bool show_machine_output)
+{
+ /* First find out board ID of the target. */
+ struct board_id bid;
+ char rlz_code[sizeof(bid.type) + 1];
+
+ /*
+ * Find out what Board ID is the device we are talking to. This
+ * function calls exit() on any error.
+ */
+ process_bid(td, bid_get, &bid, show_machine_output);
+
+ if (bid.type != ~bid.type_inv) {
+ fprintf(stderr, "Inconsistent board ID: %08x != ~%08x\n",
+ bid.type, bid.type_inv);
+ return -EINVAL;
+ }
+
+ /*
+ * Convert bid from int to asciiz so that it could be used for
+ * strcmp() on the descriptor file section headers.
+ */
+ memcpy(rlz_code, &bid.type, sizeof(rlz_code) - 1);
+ rlz_code[sizeof(rlz_code) - 1] = '\0';
+
+ if (!parser_find_board(desc_file_name, rlz_code)) {
+ /* Opened the file and found descriptors for DUT. */
+ printf("Processing sections for board ID %s\n", rlz_code);
+ return process_descriptor_sections(td);
+ }
+
+ printf("No description for board ID %s found\n", rlz_code);
+ return -1;
+}
diff --git a/extra/usb_updater/verify_ro.h b/extra/usb_updater/verify_ro.h
new file mode 100644
index 0000000000..de2443b8b4
--- /dev/null
+++ b/extra/usb_updater/verify_ro.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __EXTRA_USB_UPDATER_VERIFY_RO_H
+#define __EXTRA_USB_UPDATER_VERIFY_RO_H
+
+#include <stdbool.h>
+
+#include "gsctool.h"
+
+/*
+ * Runs RO verification on the target specified in td using the description file
+ * desc_file_name. If show_machine_output is set, target's board ID will be
+ * outputted in a machine-friendly format. Returns 0 on success or a negative
+ * value if there is an error.
+ */
+int verify_ro(struct transfer_descriptor *td,
+ const char *desc_file_name,
+ bool show_machine_output);
+
+#endif // __EXTRA_USB_UPDATER_VERIFY_RO_H
diff --git a/include/2id.h b/include/2id.h
new file mode 100644
index 0000000000..8daa4d27db
--- /dev/null
+++ b/include/2id.h
@@ -0,0 +1,32 @@
+/* Copyright 2015 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Key ID, used to quickly match keys with signatures. There's not a standard
+ * fingerprint for private keys, so we're using the sha1sum of the public key
+ * in our keyb format. Pretty much anything would work as long as it's
+ * resistant to collisions and easy to compare.
+ *
+ * Note: This file is copied from
+ * src/platform/vboot_reference/firmware/2lib/include/2id.h
+ * and should be updated if necessary.
+ */
+
+#ifndef VBOOT_REFERENCE_VBOOT_2ID_H_
+#define VBOOT_REFERENCE_VBOOT_2ID_H_
+#include <stdint.h>
+
+#define VB2_ID_NUM_BYTES 20
+
+struct vb2_id {
+ uint8_t raw[VB2_ID_NUM_BYTES];
+} __attribute__((packed));
+
+#define EXPECTED_ID_SIZE VB2_ID_NUM_BYTES
+
+/* IDs to use for "keys" with sig_alg==VB2_SIG_NONE */
+#define VB2_ID_NONE_SHA1 {{0x00, 0x01,}}
+#define VB2_ID_NONE_SHA256 {{0x02, 0x56,}}
+#define VB2_ID_NONE_SHA512 {{0x05, 0x12,}}
+
+#endif /* VBOOT_REFERENCE_VBOOT_2ID_H_ */
diff --git a/include/accelgyro.h b/include/accelgyro.h
index 699fcda6df..f78af2bb90 100644
--- a/include/accelgyro.h
+++ b/include/accelgyro.h
@@ -91,23 +91,13 @@ struct accelgyro_drv {
int (*perform_calib)(const struct motion_sensor_t *s);
#ifdef CONFIG_ACCEL_INTERRUPTS
/**
- * Setup a one-time accel interrupt. If the threshold is low enough, the
- * interrupt may trigger due simply to noise and not any real motion.
- * If the threshold is 0, the interrupt will fire immediately.
- * @s Pointer to sensor data.
- * @threshold Threshold for interrupt in units of counts.
- */
- int (*set_interrupt)(const struct motion_sensor_t *s,
- unsigned int threshold);
-
- /**
* handler for interrupts triggered by the sensor: it runs in task and
* process the events that triggered an interrupt.
* @s Pointer to sensor data.
* @event Event to process. May add other events for the next processor.
*
* Return EC_SUCCESS when one event is handled, EC_ERROR_NOT_HANDLED
- * when no events have been proccessed.
+ * when no events have been processed.
*/
int (*irq_handler)(struct motion_sensor_t *s, uint32_t *event);
#endif
@@ -116,8 +106,12 @@ struct accelgyro_drv {
* Retrieve hardware FIFO from sensor,
* - put data in Sensor Hub fifo.
* - update sensor raw_xyz vector with the last information.
- * We put raw data in hub fifo and process data from theres.
+ * We put raw data in hub fifo and process data from there.
* @s Pointer to sensor data.
+ *
+ * NOTE: If a new driver supports this function, be sure to add a check
+ * for spoof_mode in order to load the sensor stack with the spoofed
+ * data. See accelgyro_bmi160.c::load_fifo for an example.
*/
int (*load_fifo)(struct motion_sensor_t *s);
#endif
@@ -128,7 +122,7 @@ struct accelgyro_drv {
* @s Pointer to sensor data.
* @activity activity to work on
* @enable 1 to enable, 0 to disable
- * @data addtional data if needed, activity dependant.
+ * @data additional data if needed, activity dependent.
*/
int (*manage_activity)(const struct motion_sensor_t *s,
enum motionsensor_activity activity,
diff --git a/include/base32.h b/include/base32.h
new file mode 100644
index 0000000000..ac04ce9c70
--- /dev/null
+++ b/include/base32.h
@@ -0,0 +1,73 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Base-32 encoding/decoding, designed for manual operator entry. */
+
+#ifndef __CROS_EC_BASE32_H
+#define __CROS_EC_BASE32_H
+
+/* Symbol map for base32 encoding */
+extern const char base32_map[33];
+
+/**
+ * CRC-5-USB Initially created for USB Token Packets. It uses
+ * the generator polynomial X^5 + X^2 + X^0 and is 5-bits.
+ *
+ * @param sym New symbol to update CRC with
+ * @param previous_crc Existing CRC value
+ * @return The updated CRC.
+ */
+uint8_t crc5_sym(uint8_t sym, uint8_t previous_crc);
+
+/**
+ * base32-encode data into a null-terminated string
+ *
+ * Uses A-Z0-9 encoding, skipping I,O,0,1 since they're easy to get mixed up.
+ *
+ * @param dest Destination buffer; set to empty string on
+ * error
+ * @param destlen_chars Length of destination buffer in characters
+ * @param src Source binary data
+ * @param srclen_bits Length of source *in bits*. If this is not a
+ * multiple of 8, the *most significant* bits of
+ * the last byte will be used. If this is not a
+ * multiple of 5, the least significant bits of
+ * the last symbol will be padded with 0 bits.
+ * @param add_crc_every If non-zero, add a CRC symbol after each group
+ * of this many symbols. There must be an exact
+ * number of groups; that is, ceil(srclen_bits/5)
+ * must be a multiple of add_crc_every.
+ * @return EC_SUCCESS, or non-zero error code.
+ */
+int base32_encode(char *dest, int destlen_chars,
+ const void *srcbits, int srclen_bits,
+ int add_crc_every);
+
+/**
+ * base32-decode data from a null-terminated string
+ *
+ * Ignores whitespace and '-' dashes in the source string.
+ *
+ * If the destination is smaller than the decoded bitstream, only that many
+ * bits will be decoded. This is useful for decoding the first part of a
+ * bitstream to look for a struct version.
+ *
+ * If the destination is larger than the decoded bitstream, check the return
+ * value to determine how many bits were decoded from the source. Note that if
+ * padding was added by base32_encode (that is, the input length was not a
+ * multiple of 5 bits), the padding will be included in the count.
+ *
+ * @param dest Destination; must be at least
+ * ceil(destlen_bits/8) bytes.
+ * @param destlen_bits Length of destination *in bits*.
+ * @param src Source string (null-terminated)
+ * @param crc_after_every If non-zero, expect CRC symbol after every
+ * group of this many symbols.
+ * @return Number of decoded *bits*, or -1 if error.
+ */
+int base32_decode(uint8_t *dest, int destlen_bits, const char *src,
+ int crc_after_every);
+
+#endif
diff --git a/include/battery.h b/include/battery.h
index e79ddd3adf..0ccd29319d 100644
--- a/include/battery.h
+++ b/include/battery.h
@@ -14,7 +14,11 @@
#define BATTERY_LEVEL_FULL 100
/* Tell host we're charged when battery level >= this percentage */
+#ifdef CONFIG_BATTERY_LEVEL_NEAR_FULL
+#define BATTERY_LEVEL_NEAR_FULL CONFIG_BATTERY_LEVEL_NEAR_FULL
+#else
#define BATTERY_LEVEL_NEAR_FULL 97
+#endif
/*
* Send battery-low host event when discharging and battery level <= this level
@@ -97,6 +101,7 @@ struct battery_info {
int voltage_max;
int voltage_normal;
int voltage_min;
+ /* (TODO(chromium:756700): add desired_charging_current */
/* Pre-charge current in mA */
int precharge_current;
/* Working temperature ranges in degrees C */
@@ -137,6 +142,21 @@ void battery_override_params(struct batt_params *batt);
enum battery_present battery_is_present(void);
/**
+ * Check for physical presence of battery.
+ *
+ * @return Whether there is a battery physically present, but possibly
+ * in a disconnected or cut off state, or if we can't tell;
+ */
+enum battery_present battery_hw_present(void);
+
+/**
+ * Check for battery initialization status.
+ *
+ * @return zero if not initialized.
+ */
+int board_battery_initialized(void);
+
+/**
* Get battery mode.
*
* See MODE_* constants in battery_smart.h
@@ -333,4 +353,8 @@ void print_battery_debug(void);
*/
enum battery_disconnect_state battery_get_disconnect_state(void);
+#ifdef CONFIG_CMD_I2C_STRESS_TEST_BATTERY
+extern struct i2c_stress_test_dev battery_i2c_stress_test_dev;
+#endif
+
#endif /* __CROS_EC_BATTERY_H */
diff --git a/include/battery_smart.h b/include/battery_smart.h
index c5fd1d6eec..1cbcf295f1 100644
--- a/include/battery_smart.h
+++ b/include/battery_smart.h
@@ -56,7 +56,8 @@
#define SB_DEVICE_NAME 0x21
#define SB_DEVICE_CHEMISTRY 0x22
#define SB_MANUFACTURER_DATA 0x23
-/* Extention of smart battery spec, may not be supported on all platforms */
+/* Extension of smart battery spec, may not be supported on all platforms */
+#define SB_PACK_STATUS 0x43
#define SB_ALT_MANUFACTURER_ACCESS 0x44
/* Battery mode */
@@ -137,21 +138,17 @@
#define BATTERY_DISCHARGING_DISABLED 0x20
#define BATTERY_CHARGING_DISABLED 0x40
-/* Read from charger */
-int sbc_read(int cmd, int *param);
-
-/* Write to charger */
-int sbc_write(int cmd, int param);
-
/* Read from battery */
int sb_read(int cmd, int *param);
/* Read sequence from battery */
-int sb_read_string(int port, int slave_addr, int offset, uint8_t *data,
- int len);
+int sb_read_string(int offset, uint8_t *data, int len);
/* Write to battery */
int sb_write(int cmd, int param);
+/* Read manufactures access data from the battery */
+int sb_read_mfgacc(int cmd, int block, uint8_t *data, int len);
+
#endif /* __CROS_EC_BATTERY_SMART_H */
diff --git a/include/board_config.h b/include/board_config.h
index 0a97b8a181..f3f42ea795 100644
--- a/include/board_config.h
+++ b/include/board_config.h
@@ -1,6 +1,8 @@
/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
+ *
+ * Extra hooks for board and chip initialization/configuration
*/
#ifndef __CROS_EC_BOARD_CONFIG_H
@@ -32,4 +34,37 @@ void board_config_pre_init(void);
void board_config_post_gpio_init(void);
#endif
+#ifdef CONFIG_BOARD_HAS_BEFORE_RSMRST
+/**
+ * Configure board before RSMRST# state change
+ *
+ * This board function allows workarounds to be applied to a board after all
+ * power rails are up but before the AP is out of reset.
+ *
+ * Most workarounds for power sequencing can go in board init hooks, but for
+ * devices where the power sequencing is driven by external PMIC the EC may
+ * not get interrupts in time to handle workarounds. For x86 platforms and
+ * boards which support RSMRST# passthrough this hook will allow the board
+ * to apply workarounds despite the PMIC sequencing.
+ */
+void board_before_rsmrst(int rsmrst);
+#endif
+
+/**
+ * Configure chip early in main(), just after board_config_pre_init().
+ *
+ * Most chip configuration is not particularly timing critical and can be done
+ * in other chip driver initialization such as system_pre_init() or HOOK_INIT
+ * handlers. Chip pre-init should be reserved for small amounts of critical
+ * functionality that can't wait that long. Think very hard before putting
+ * code here.
+ */
+void chip_pre_init(void);
+
+#ifdef CONFIG_EC_FEATURE_BOARD_OVERRIDE
+/* function for board specific overrides to default feature flags */
+uint32_t board_override_feature_flags0(uint32_t flags0);
+uint32_t board_override_feature_flags1(uint32_t flags1);
+#endif
+
#endif /* __CROS_EC_BOARD_CONFIG_H */
diff --git a/include/btle_hci_int.h b/include/btle_hci_int.h
index 839d72770c..32349eff61 100644
--- a/include/btle_hci_int.h
+++ b/include/btle_hci_int.h
@@ -271,10 +271,10 @@
#define HCI_EVENT_CHANGE_CONN_LINK_KEY_COMPLETE 0x0000000000000100ULL /* BT 1.1+ */
#define HCI_EVENT_MASTER_LINK_KEY_COMPLETE 0x0000000000000200ULL /* BT 1.1+ */
#define HCI_EVENT_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE 0x0000000000000400ULL /* BT 1.1+ */
-#define HCI_EVENT_READ_REMOTE_VERSON_INFO_COMPLETE 0x0000000000000800ULL /* BT 1.1+ */
+#define HCI_EVENT_READ_REMOTE_VERSION_INFO_COMPLETE 0x0000000000000800ULL /* BT 1.1+ */
#define HCI_EVENT_QOS_SETUP_COMPLETE 0x0000000000001000ULL /* BT 1.1+ */
#define HCI_EVENT_HARDWARE_ERROR 0x0000000000008000ULL /* BT 1.1+ */
-#define HCI_EVENT_FLUSH_OCCURED 0x0000000000010000ULL /* BT 1.1+ */
+#define HCI_EVENT_FLUSH_OCCURRED 0x0000000000010000ULL /* BT 1.1+ */
#define HCI_EVENT_ROLE_CHANGE 0x0000000000020000ULL /* BT 1.1+ */
#define HCI_EVENT_MODE_CHANGE 0x0000000000080000ULL /* BT 1.1+ */
#define HCI_EVENT_RETURN_LINK_KEYS 0x0000000000100000ULL /* BT 1.1+ */
@@ -1476,7 +1476,7 @@ struct hciCmplWriteAfhChannelAssessment {
/* ==== BT 2.1 ==== */
-#define HCI_CMD_Read_Extended_Inquiry_Reponse 0x0051 /* complete */
+#define HCI_CMD_Read_Extended_Inquiry_Response 0x0051 /* complete */
struct hciCmplReadEIR {
uint8_t status;
uint8_t useFec;
@@ -1976,7 +1976,7 @@ struct hciCmplReadDataBlockSize {
/* ==== BT 4.1 ==== */
-#define HCI_CMD_Read_Local_Suported_Codecs 0x000B /* complete */
+#define HCI_CMD_Read_Local_Supported_Codecs 0x000B /* complete */
struct hciCmplReadLocalSupportedCodecs {
uint8_t status;
uint8_t numSupportedCodecs;
@@ -2564,8 +2564,8 @@ struct hciEvtHwError {
uint8_t errCode;
} __packed;
-#define HCI_EVT_Flush_Occured 0x11
-struct hciEvtFlushOccured {
+#define HCI_EVT_Flush_Occurred 0x11
+struct hciEvtFlushOccurred {
uint16_t conn;
} __packed;
diff --git a/include/button.h b/include/button.h
index fbfd4f3879..c542d44c47 100644
--- a/include/button.h
+++ b/include/button.h
@@ -17,6 +17,7 @@ enum keyboard_button_type {
KEYBOARD_BUTTON_POWER = 0,
KEYBOARD_BUTTON_VOLUME_DOWN,
KEYBOARD_BUTTON_VOLUME_UP,
+ KEYBOARD_BUTTON_RECOVERY,
KEYBOARD_BUTTON_CAPSENSE_1,
KEYBOARD_BUTTON_CAPSENSE_2,
KEYBOARD_BUTTON_CAPSENSE_3,
@@ -43,6 +44,17 @@ struct button_config {
extern const struct button_config buttons[];
/*
+ * Buttons used to decide whether recovery is requested or not
+ */
+extern const struct button_config *recovery_buttons[];
+extern const int recovery_buttons_count;
+
+/*
+ * Button initialization, called from main.
+ */
+void button_init(void);
+
+/*
* Interrupt handler for button.
*
* @param signal Signal which triggered the interrupt.
diff --git a/include/case_closed_debug.h b/include/case_closed_debug.h
index ed2f13d099..6a8ce14ffc 100644
--- a/include/case_closed_debug.h
+++ b/include/case_closed_debug.h
@@ -7,34 +7,9 @@
#ifndef __CROS_EC_CASE_CLOSED_DEBUG_H
#define __CROS_EC_CASE_CLOSED_DEBUG_H
-enum ccd_mode {
- /*
- * The disabled mode tri-states the DP and DN lines.
- */
- CCD_MODE_DISABLED,
-
- /*
- * The partial mode allows some CCD functionality and is to be set
- * when the device is write protected and a CCD cable is detected.
- * This mode gives access to the APs console.
- */
- CCD_MODE_PARTIAL,
-
- /*
- * The fully enabled mode is used in factory and test lab
- * configurations where it is acceptable to be able to reflash the
- * device over CCD.
- */
- CCD_MODE_ENABLED,
-
- CCD_MODE_COUNT,
-};
-
-/*
- * Set current CCD mode, this function is idempotent.
+/**
+ * Return non-zero if the CCD external interface is enabled.
*/
-void ccd_set_mode(enum ccd_mode new_mode);
+int ccd_ext_is_enabled(void);
-/* Initialize the PHY based on CCD state */
-void ccd_phy_init(int enable_ccd);
#endif /* __CROS_EC_CASE_CLOSED_DEBUG_H */
diff --git a/include/ccd_config.h b/include/ccd_config.h
new file mode 100644
index 0000000000..240feb5240
--- /dev/null
+++ b/include/ccd_config.h
@@ -0,0 +1,292 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Case Closed Debugging configuration
+ */
+#ifndef __CROS_EC_CCD_CONFIG_H
+#define __CROS_EC_CCD_CONFIG_H
+
+#include <common.h>
+#include <stdint.h>
+
+/* Case-closed debugging state */
+enum ccd_state {
+ CCD_STATE_LOCKED = 0,
+ CCD_STATE_UNLOCKED,
+ CCD_STATE_OPENED,
+
+ /* Number of CCD states */
+ CCD_STATE_COUNT
+};
+
+/* Flags */
+enum ccd_flag {
+ /* Flags that can only be set internally; fill from bottom up */
+
+ /*
+ * Test lab mode is enabled. This MUST be in the first byte so that
+ * it's in a constant position across all versions of CCD config.
+ *
+ * Note: This is used internally by CCD config. Do NOT test this
+ * to control other things; use capabilities for those.
+ */
+ CCD_FLAG_TEST_LAB = (1 << 0),
+
+ /*
+ * What state were we in when the password was set?
+ * (0=opened, 1=unlocked)
+ */
+ CCD_FLAG_PASSWORD_SET_WHEN_UNLOCKED = (1 << 1),
+
+ /* (flags in the middle are unused) */
+
+ /* Flags that can be set via ccd_set_flags(); fill from top down */
+
+ /* Override write protect at boot */
+ CCD_FLAG_OVERRIDE_WP_AT_BOOT = (1 << 22),
+
+ /*
+ * If overriding WP at boot, set it to what value
+ * (0=disabled, 1=enabled)
+ */
+ CCD_FLAG_OVERRIDE_WP_STATE_ENABLED = (1 << 23),
+};
+
+/* Capabilities */
+enum ccd_capability {
+ /* UARTs to/from AP and EC */
+ CCD_CAP_GSC_RX_AP_TX = 0,
+ CCD_CAP_GSC_TX_AP_RX = 1,
+ CCD_CAP_GSC_RX_EC_TX = 2,
+ CCD_CAP_GSC_TX_EC_RX = 3,
+
+ /* Access to AP SPI flash */
+ CCD_CAP_AP_FLASH = 4,
+
+ /* Access to EC flash (SPI or internal) */
+ CCD_CAP_EC_FLASH = 5,
+
+ /* Override WP temporarily or at boot */
+ CCD_CAP_OVERRIDE_WP = 6,
+
+ /* Reboot EC or AP */
+ CCD_CAP_REBOOT_EC_AP = 7,
+
+ /* GSC restricted console commands */
+ CCD_CAP_GSC_RESTRICTED_CONSOLE = 8,
+
+ /* Allow ccd-unlock or ccd-open without AP reboot */
+ CCD_CAP_UNLOCK_WITHOUT_AP_REBOOT = 9,
+
+ /* Allow ccd-unlock or ccd-open without short physical presence */
+ CCD_CAP_UNLOCK_WITHOUT_SHORT_PP = 10,
+
+ /* Allow ccd-open without wiping TPM data */
+ CCD_CAP_OPEN_WITHOUT_TPM_WIPE = 11,
+
+ /* Allow ccd-open without long physical presence */
+ CCD_CAP_OPEN_WITHOUT_LONG_PP = 12,
+
+ /* Allow removing the battery to bypass physical presence requirement */
+ CCD_CAP_REMOVE_BATTERY_BYPASSES_PP = 13,
+
+ /* Allow GSC firmware update without wiping TPM data */
+ CCD_CAP_GSC_FW_UPDATE_WITHOUT_TPM_WIPE = 14,
+
+ /* Access to I2C via USB */
+ CCD_CAP_I2C = 15,
+
+ /* Read-only access to hash or dump EC or AP flash */
+ CCD_CAP_FLASH_READ = 16,
+
+ /* Number of currently defined capabilities */
+ CCD_CAP_COUNT
+};
+
+/* Capability states */
+enum ccd_capability_state {
+ /* Default value */
+ CCD_CAP_STATE_DEFAULT = 0,
+
+ /* Always available (state >= CCD_STATE_LOCKED) */
+ CCD_CAP_STATE_ALWAYS = 1,
+
+ /* Unless locked (state >= CCD_STATE_UNLOCKED) */
+ CCD_CAP_STATE_UNLESS_LOCKED = 2,
+
+ /* Only if opened (state >= CCD_STATE_OPENED) */
+ CCD_CAP_STATE_IF_OPENED = 3,
+
+ /* Number of capability states */
+ CCD_CAP_STATE_COUNT
+};
+
+struct ccd_capability_info {
+ /* Capability name */
+ const char *name;
+
+ /* Default state, if config set to CCD_CAP_STATE_DEFAULT */
+ enum ccd_capability_state default_state;
+};
+
+#define CAP_INFO_DATA { \
+ {"UartGscRxAPTx", CCD_CAP_STATE_ALWAYS}, \
+ {"UartGscTxAPRx", CCD_CAP_STATE_ALWAYS}, \
+ {"UartGscRxECTx", CCD_CAP_STATE_ALWAYS}, \
+ {"UartGscTxECRx", CCD_CAP_STATE_IF_OPENED}, \
+ \
+ {"FlashAP", CCD_CAP_STATE_IF_OPENED}, \
+ {"FlashEC", CCD_CAP_STATE_IF_OPENED}, \
+ {"OverrideWP", CCD_CAP_STATE_IF_OPENED}, \
+ {"RebootECAP", CCD_CAP_STATE_IF_OPENED}, \
+ \
+ {"GscFullConsole", CCD_CAP_STATE_IF_OPENED}, \
+ {"UnlockNoReboot", CCD_CAP_STATE_ALWAYS}, \
+ {"UnlockNoShortPP", CCD_CAP_STATE_ALWAYS}, \
+ {"OpenNoTPMWipe", CCD_CAP_STATE_IF_OPENED}, \
+ \
+ {"OpenNoLongPP", CCD_CAP_STATE_IF_OPENED}, \
+ {"BatteryBypassPP", CCD_CAP_STATE_ALWAYS}, \
+ {"UpdateNoTPMWipe", CCD_CAP_STATE_ALWAYS}, \
+ {"I2C", CCD_CAP_STATE_IF_OPENED}, \
+ {"FlashRead", CCD_CAP_STATE_ALWAYS}, \
+ }
+
+#define CCD_STATE_NAMES { "Locked", "Unlocked", "Opened" }
+#define CCD_CAP_STATE_NAMES { "Default", "Always", "UnlessLocked", "IfOpened" }
+
+/*
+ * Subcommand code, used to pass different CCD commands using the same TPM
+ * vendor command.
+ */
+enum ccd_vendor_subcommands {
+ CCDV_PASSWORD = 0,
+ CCDV_OPEN = 1,
+ CCDV_UNLOCK = 2,
+ CCDV_LOCK = 3,
+ CCDV_PP_POLL_UNLOCK = 4,
+ CCDV_PP_POLL_OPEN = 5,
+ CCDV_GET_INFO = 6
+};
+
+enum ccd_pp_state {
+ CCD_PP_CLOSED = 0,
+ CCD_PP_AWAITING_PRESS = 1,
+ CCD_PP_BETWEEN_PRESSES = 2,
+ CCD_PP_DONE = 3
+};
+
+/* Structure to communicate information about CCD state. */
+#define CCD_CAPS_WORDS ((CCD_CAP_COUNT * 2 + 31)/32)
+struct ccd_info_response {
+ uint32_t ccd_caps_current[CCD_CAPS_WORDS];
+ uint32_t ccd_caps_defaults[CCD_CAPS_WORDS];
+ uint32_t ccd_flags;
+ uint8_t ccd_state;
+ uint8_t ccd_force_disabled;
+ uint8_t ccd_has_password;
+} __packed;
+
+/**
+ * Initialize CCD configuration at boot.
+ *
+ * This must be called before any command which gets/sets the configuration.
+ *
+ * @param state Initial case-closed debugging state. This should be
+ * CCD_STATE_LOCKED unless this is a debug build, or if
+ * a previous value is being restored after a low-power
+ * resume.
+ */
+void ccd_config_init(enum ccd_state state);
+
+/**
+ * Get a single CCD flag.
+ *
+ * @param flag Flag to get
+ * @return 1 if flag is set, 0 if flag is clear
+ */
+int ccd_get_flag(enum ccd_flag flag);
+
+/**
+ * Set a single CCD flag.
+ *
+ * @param flag Flag to set
+ * @param value New value for flag (0=clear, non-zero=set)
+ * @return EC_SUCCESS or non-zero error code.
+ */
+int ccd_set_flag(enum ccd_flag flag, int value);
+
+/**
+ * Check if a CCD capability is enabled in the current CCD mode.
+ *
+ * @param cap Capability to check
+ * @return 1 if capability is enabled, 0 if disabled
+ */
+int ccd_is_cap_enabled(enum ccd_capability cap);
+
+/**
+ * Get the current CCD state.
+ *
+ * This is intended for use by the board if it needs to back up the CCD state
+ * across low-power states and then restore it when calling ccd_config_init().
+ * Do NOT use this to gate debug capabilities; use ccd_is_cap_enabled() or
+ * ccd_get_flag() instead.
+ *
+ * @return The current CCD state.
+ */
+enum ccd_state ccd_get_state(void);
+
+/**
+ * Force CCD disabled.
+ *
+ * This should be called if security checks fail and for some reason the board
+ * can't immediately reboot. It locks CCD and disables all CCD capabilities
+ * until reboot.
+ */
+void ccd_disable(void);
+
+/* Flags for ccd_reset_config() */
+enum ccd_reset_config_flags {
+ /* Also reset test lab flag */
+ CCD_RESET_TEST_LAB = (1 << 0),
+
+ /* Only reset Always/UnlessLocked settings */
+ CCD_RESET_UNLOCKED_ONLY = (1 << 1),
+
+ /*
+ * Do a factory reset to enable factory mode. Factory mode sets all ccd
+ * capabilities to always and disables write protect
+ */
+ CCD_RESET_FACTORY = (1 << 2)
+};
+
+/**
+ * Reset CCD config to the desired state.
+ *
+ * @param flags Reset flags (see enum ccd_reset_config_flags)
+ * @return EC_SUCCESS, or non-zero if error.
+ */
+int ccd_reset_config(unsigned int flags);
+
+/**
+ * Inform CCD about TPM reset so that the password management state machine
+ * can be restarted.
+ */
+void ccd_tpm_reset_callback(void);
+
+/**
+ * Return True if the ccd password is set. It is possible that a pending ccd
+ * change would set or clear the password, but we don't think this is a big
+ * issue or risk for now.
+ *
+ * @return 1 if password is set, 0 if it's not
+ */
+int ccd_has_password(void);
+
+/**
+ * Enter CCD factory mode. This will clear the TPM and do a hard reboot after
+ * updating the ccd config.
+ */
+void enable_ccd_factory_mode(void);
+#endif /* __CROS_EC_CCD_CONFIG_H */
diff --git a/include/charge_manager.h b/include/charge_manager.h
index 3623bc1635..6d5bacc2df 100644
--- a/include/charge_manager.h
+++ b/include/charge_manager.h
@@ -17,7 +17,13 @@
#define CHARGE_CURRENT_UNINITIALIZED -1
#define CHARGE_VOLTAGE_UNINITIALIZED -1
-/*
+/* Only track BC1.2 charge current if we support BC1.2 charging */
+#if defined(HAS_TASK_USB_CHG) || defined(HAS_TASK_USB_CHG_P0) || \
+defined(TEST_BUILD)
+#define CHARGE_MANAGER_BC12
+#endif
+
+/**
* Time to delay for detecting the charger type (must be long enough for BC1.2
* driver to get supplier information and notify charge manager).
*/
@@ -27,12 +33,18 @@
enum charge_supplier {
CHARGE_SUPPLIER_PD,
CHARGE_SUPPLIER_TYPEC,
+ CHARGE_SUPPLIER_TYPEC_DTS,
+#ifdef CHARGE_MANAGER_BC12
CHARGE_SUPPLIER_BC12_DCP,
CHARGE_SUPPLIER_BC12_CDP,
CHARGE_SUPPLIER_BC12_SDP,
CHARGE_SUPPLIER_PROPRIETARY,
CHARGE_SUPPLIER_OTHER,
CHARGE_SUPPLIER_VBUS,
+#endif /* CHARGE_MANAGER_BC12 */
+#if CONFIG_DEDICATED_CHARGE_PORT_COUNT > 0
+ CHARGE_SUPPLIER_DEDICATED,
+#endif
CHARGE_SUPPLIER_COUNT
};
@@ -42,7 +54,13 @@ struct charge_port_info {
int voltage;
};
-/* Called by charging tasks to update their available charge */
+/**
+ * Called by charging tasks to update their available charge.
+ *
+ * @param supplier Charge supplier to update.
+ * @param port Charge port to update.
+ * @param charge Charge port current / voltage.
+ */
void charge_manager_update_charge(int supplier,
int port,
struct charge_port_info *charge);
@@ -54,10 +72,21 @@ enum dualrole_capabilities {
CAP_DEDICATED,
};
-/* Called by charging tasks to indicate partner dualrole capability change */
+/**
+ * Notify charge_manager of a partner dualrole capability change.
+ *
+ * @param port Charge port which changed.
+ * @param cap New port capability.
+ */
void charge_manager_update_dualrole(int port, enum dualrole_capabilities cap);
-/*
+/**
+ * Tell charge_manager to leave safe mode and switch to standard port / ILIM
+ * selection logic.
+ */
+void charge_manager_leave_safe_mode(void);
+
+/**
* Charge ceiling can be set independently by different tasks / functions,
* for different purposes.
*/
@@ -70,20 +99,67 @@ enum ceil_requestor {
CEIL_REQUESTOR_COUNT,
};
-/* Update charge ceiling for a given port / requestor */
+#define CHARGE_PORT_COUNT \
+ (CONFIG_USB_PD_PORT_COUNT + CONFIG_DEDICATED_CHARGE_PORT_COUNT)
+
+/**
+ * Update charge ceiling for a given port. The ceiling can be set independently
+ * for several requestors, and the min. ceil will be enforced.
+ *
+ * @param port Charge port to update.
+ * @param requestor Charge ceiling requestor.
+ * @param ceil Charge ceiling (mA).
+ */
void charge_manager_set_ceil(int port, enum ceil_requestor requestor, int ceil);
-/* Select an 'override port', which is always the preferred charge port */
+/*
+ * Update PD charge ceiling for a given port. In the event that our ceiling
+ * is currently above ceil, change the current limit before returning, without
+ * waiting for a charge manager refresh. This function should only be used in
+ * time-critical situations where we absolutely cannot proceed without limiting
+ * our input current, and it should only be called from the PD tasks.
+ * If you ever call this function then you are a terrible person.
+ */
+void charge_manager_force_ceil(int port, int ceil);
+
+/**
+ * Select an 'override port', a port which is always the preferred charge port.
+ *
+ * @param port Charge port to select as override, or
+ * OVERRIDE_OFF to select no override port,
+ * or OVERRIDE_DONT_CHARGE to specific that no
+ * charge port should be selected.
+ * @return EC_SUCCESS on success,
+ * the other ec_error_list status on failure.
+ */
int charge_manager_set_override(int port);
+
+/**
+ * Get the override port.
+ *
+ * @return Port number or OVERRIDE_OFF or OVERRIDE_DONT_CHARGE.
+ */
int charge_manager_get_override(void);
-/* Returns the current active charge port, as determined by charge manager */
+/**
+ * Get the current active charge port, as determined by charge manager.
+ *
+ * @return Current active charge port.
+ */
int charge_manager_get_active_charge_port(void);
-/* Return the power limit (uW) set by charge manager. */
+/**
+ * Get the power limit set by charge manager.
+ *
+ * @return Power limit (uW).
+ */
int charge_manager_get_power_limit_uw(void);
-/* Return the charger current (mA) value. */
+/**
+ * Get the charger current (mA) value.
+ *
+ * @return Charger current (mA) or CHARGE_CURRENT_UNINITIALIZED.
+ */
int charge_manager_get_charger_current(void);
#ifdef CONFIG_USB_PD_LOGGING
@@ -91,33 +167,53 @@ int charge_manager_get_charger_current(void);
void charge_manager_save_log(int port);
#endif
-/* Update whether a given port is sourcing current. */
+/**
+ * Update whether a given port is sourcing current.
+ *
+ * @param port Port number to be updated.
+ * @param enable 0 if the source port is disabled;
+ * Otherwise the source port is enabled.
+ */
void charge_manager_source_port(int port, int enable);
-/*
+/**
* Get PD source power data objects.
*
- * @param src_pdo pointer to the data to return.
+ * @param src_pdo Pointer to the data to return.
+ * @param port Current port to evaluate against.
* @return number of PDOs returned.
*/
-int charge_manager_get_source_pdo(const uint32_t **src_pdo);
+int charge_manager_get_source_pdo(const uint32_t **src_pdo, const int port);
/* Board-level callback functions */
-/*
- * Set the active charge port. Returns EC_SUCCESS if the charge port is
- * accepted, returns ec_error_list status otherwise.
+/**
+ * Set the passed charge port as active.`
+ *
+ * @param charge_port Charge port to be enabled.
+ * @return EC_SUCCESS if the charge port is accepted,
+ * other ec_error_list status otherwise.
*/
int board_set_active_charge_port(int charge_port);
-/*
+/**
* Set the charge current limit.
*
* @param port PD port.
* @param supplier Identified CHARGE_SUPPLIER_*.
* @param charge_ma Desired charge current limit, <= max_ma.
* @param max_ma Maximum charge current limit, >= charge_ma.
+ * @param charge_mv Negotiated charge voltage (mV).
+ */
+void board_set_charge_limit(int port, int supplier, int charge_ma,
+ int max_ma, int charge_mv);
+
+/**
+ * Get whether the port is sourcing power on VBUS.
+ *
+ * @param port PD port.
+ * @return VBUS power state.
*/
-void board_set_charge_limit(int port, int supplier, int charge_ma, int max_ma);
+int board_vbus_source_enabled(int port);
#endif /* __CROS_EC_CHARGE_MANAGER_H */
diff --git a/include/charge_ramp.h b/include/charge_ramp.h
index 6b7b3ad247..c4082fdbf2 100644
--- a/include/charge_ramp.h
+++ b/include/charge_ramp.h
@@ -17,13 +17,23 @@ enum chg_ramp_vbus_state {
};
/**
+ * Check if VBUS is too low
+ *
+ * @param port Charge ramp port
+ * @param ramp_state Current ramp state
+ *
+ * @return VBUS is sagging low
+ */
+int board_is_vbus_too_low(int port, enum chg_ramp_vbus_state ramp_state);
+
+/**
* Check if ramping is allowed for given supplier
*
* @supplier Supplier to check
*
* @return Ramping is allowed for given supplier
*/
-int board_is_ramp_allowed(int supplier);
+int chg_ramp_allowed(int supplier);
/**
* Get the maximum current limit that we are allowed to ramp to
@@ -33,23 +43,7 @@ int board_is_ramp_allowed(int supplier);
*
* @return Maximum current in mA
*/
-int board_get_ramp_current_limit(int supplier, int sup_curr);
-
-/**
- * Check if board is consuming full input current
- *
- * @return Board is consuming full input current
- */
-int board_is_consuming_full_charge(void);
-
-/**
- * Check if VBUS is too low
- *
- * @param ramp_state Current ramp state
- *
- * @return VBUS is sagging low
- */
-int board_is_vbus_too_low(enum chg_ramp_vbus_state ramp_state);
+int chg_ramp_max(int supplier, int sup_curr);
/**
* Get the input current limit set by ramp module
@@ -82,9 +76,10 @@ int chg_ramp_is_detected(void);
* @supplier Active charging supplier
* @current Minimum input current limit
* @registration_time Timestamp of when the supplier is registered
+ * @voltage Negotiated charge voltage.
*/
void chg_ramp_charge_supplier_change(int port, int supplier, int current,
- timestamp_t registration_time);
+ timestamp_t registration_time, int voltage);
#else
static inline void chg_ramp_charge_supplier_change(
diff --git a/include/charge_state.h b/include/charge_state.h
index f0847cdd09..fb1a41527a 100644
--- a/include/charge_state.h
+++ b/include/charge_state.h
@@ -35,6 +35,8 @@ enum charge_state {
PWR_STATE_IDLE,
/* Discharging */
PWR_STATE_DISCHARGE,
+ /* Discharging and fully charged */
+ PWR_STATE_DISCHARGE_FULL,
/* Charging */
PWR_STATE_CHARGE,
/* Charging, almost fully charged */
@@ -61,6 +63,7 @@ enum charge_state {
"idle0", \
"idle", \
"discharge", \
+ "discharge_full", \
"charge", \
"charge_near_full", \
"error" \
@@ -89,6 +92,16 @@ uint32_t charge_get_flags(void);
int charge_get_percent(void);
/**
+ * Check if board is consuming full input current
+ *
+ * This returns true if the battery charge percentage is between 2% and 95%
+ * exclusive.
+ *
+ * @return Board is consuming full input current
+ */
+int charge_is_consuming_full_input_current(void);
+
+/**
* Return non-zero if discharging and battery so low we should shut down.
*/
int charge_want_shutdown(void);
@@ -110,7 +123,7 @@ int charge_prevent_power_on(int power_button_pressed);
*
* @return EC_SUCCESS if successful, non-zero if error.
*/
-int charge_temp_sensor_get_val(int idx, int *temp_ptr);
+int charge_get_battery_temp(int idx, int *temp_ptr);
/**
* Get the pointer to the battery parameters we saved in charge state.
@@ -120,17 +133,9 @@ int charge_temp_sensor_get_val(int idx, int *temp_ptr);
const struct batt_params *charger_current_battery_params(void);
-/* Pick the right implementation */
-#ifdef CONFIG_CHARGER_V1
-#ifdef CONFIG_CHARGER_V2
-#error "Choose either CONFIG_CHARGER_V1 or CONFIG_CHARGER_V2, not both"
-#else
-#include "charge_state_v1.h"
-#endif
-#else /* not V1 */
+/* Config Charger */
#ifdef CONFIG_CHARGER_V2
#include "charge_state_v2.h"
-#endif
-#endif /* CONFIG_CHARGER_V1 */
+#endif /* CONFIG_CHARGER_V2 */
#endif /* __CROS_EC_CHARGE_STATE_H */
diff --git a/include/charge_state_v2.h b/include/charge_state_v2.h
index c5aedb7a94..1c85323a60 100644
--- a/include/charge_state_v2.h
+++ b/include/charge_state_v2.h
@@ -40,48 +40,26 @@ struct charge_state_data {
int desired_input_current;
};
-/*
- * Optional customization.
- *
- * On input, the struct reflects the default behavior. The function can make
- * changes to the state, requested_voltage, or requested_current.
- *
- * Return value:
- * >0 Desired time in usec for this poll period.
- * 0 Use the default poll period (which varies with the state).
- * <0 An error occurred. The poll time will be shorter than usual. Too
- * many errors in a row may trigger some corrective action.
- */
-int charger_profile_override(struct charge_state_data *);
-
-/*
- * Access to custom profile params through host commands.
- * What this does is up to the implementation.
- */
-enum ec_status charger_profile_override_get_param(uint32_t param,
- uint32_t *value);
-enum ec_status charger_profile_override_set_param(uint32_t param,
- uint32_t value);
-
/**
* Set the charge input current limit. This value is stored and sent every
* time AC is applied.
*
* @param ma New input current limit in mA
+ * @param mv Negotiated charge voltage in mV.
* @return EC_SUCCESS or error
*/
-int charge_set_input_current_limit(int ma);
-
+int charge_set_input_current_limit(int ma, int mv);
-/**
- * Get value of battery parameter from charge state.
- *
- * @param batt_param battery parameter
- * @param dest Destination buffer for data
- * @param read_len Number of bytes to write to buffer
- * @return EC_SUCCESS if successful, non-zero if error.
+/*
+ * Expose charge/battery related state
*
+ * @param param command to get corresponding data
+ * @param value the corresponding data
+ * @return EC_SUCCESS or error
*/
-int virtual_battery_read(uint8_t batt_param, uint8_t *dest, int read_len);
+#ifdef CONFIG_CHARGE_STATE_DEBUG
+int charge_get_charge_state_debug(int param, uint32_t *value);
+#endif /* CONFIG_CHARGE_STATE_DEBUG */
+
#endif /* __CROS_EC_CHARGE_STATE_V2_H */
diff --git a/include/charger.h b/include/charger.h
index a98e3e0f99..16f7c3cbd0 100644
--- a/include/charger.h
+++ b/include/charger.h
@@ -10,7 +10,7 @@
#include "common.h"
-/* Charger infomation
+/* Charger information
* voltage unit: mV
* current unit: mA
*/
@@ -65,12 +65,28 @@ int charger_get_status(int *status);
int charger_set_mode(int mode);
/**
- * For chargers that are able to supply 5V output power for OTG dongle, this
- * function enables or disables 5V power output.
+ * For chargers that are able to supply output power for OTG dongle, this
+ * function enables or disables power output.
*/
int charger_enable_otg_power(int enabled);
/**
+ * Sets OTG current limit and voltage (independent of whether OTG power is
+ * currently enabled).
+ *
+ * Depending on the charger and use case, one needs to be careful about
+ * changing the current/voltage while OTG power is enabled, and it might be wise
+ * to reset the value before enabling OTG power to ensure one does not provide
+ * excessive voltage to a device.
+ *
+ * @param output_current Requested current limit in mA.
+ * @param output_voltage Requested voltage in mV.
+ *
+ * @return EC_SUCCESS on success, an error otherwise.
+ */
+int charger_set_otg_current_voltage(int output_current, int output_voltage);
+
+/**
* Return the closest match the charger can supply to the requested current.
*
* @param current Requested current in mA.
@@ -99,8 +115,8 @@ int charger_set_voltage(int voltage);
/* Discharge battery when on AC power. */
int charger_discharge_on_ac(int enable);
-/* Get the VBUS level from the charger */
-int charger_get_vbus_level(void);
+/* Get the VBUS voltage (mV) from the charger */
+int charger_get_vbus_voltage(int port);
/* Custom board function to discharge battery when on AC power */
int board_discharge_on_ac(int enable);
diff --git a/include/charger_profile_override.h b/include/charger_profile_override.h
new file mode 100644
index 0000000000..091eb11946
--- /dev/null
+++ b/include/charger_profile_override.h
@@ -0,0 +1,86 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Charger profile override for fast charging
+ */
+
+#ifndef __CROS_EC_CHARGER_PROFILE_OVERRIDE_H
+#define __CROS_EC_CHARGER_PROFILE_OVERRIDE_H
+
+#include "charge_state_v2.h"
+
+#define TEMPC_TENTHS_OF_DEG(c) ((c) * 10)
+
+#define CHARGER_PROF_TEMP_C_LAST_RANGE 0xFFFF
+
+#define CHARGER_PROF_VOLTAGE_MV_LAST_RANGE 0xFFFF
+
+/* Charge profile override info */
+struct fast_charge_profile {
+ /* temperature in 10ths of a degree C */
+ const int temp_c;
+ /* charge current for respective battery voltage ranges in mA. */
+ const int current_mA[CONFIG_CHARGER_PROFILE_VOLTAGE_RANGES];
+};
+
+/* Charge profile override parameters */
+struct fast_charge_params {
+ /* Total temperature ranges of the charge profile */
+ const int total_temp_ranges;
+ /* Default temperature range of the charge profile */
+ const int default_temp_range_profile;
+ /*
+ * Battery voltage ranges in mV.
+ * It is assumed that these values are added in ascending order in the
+ * board battery file.
+ */
+ const int voltage_mV[CONFIG_CHARGER_PROFILE_VOLTAGE_RANGES];
+ const struct fast_charge_profile *chg_profile_info;
+};
+
+/**
+ * Optional customization of charger profile override for fast charging.
+ *
+ * On input, the struct reflects the default behavior. The function can make
+ * changes to the state, requested_voltage, or requested_current.
+ *
+ * @param curr Charge state machine data.
+ *
+ * @return
+ * >0 Desired time in usec for this poll period.
+ * 0 Use the default poll period (which varies with the state).
+ * <0 An error occurred. The poll time will be shorter than usual.
+ * Too many errors in a row may trigger some corrective action.
+ */
+int charger_profile_override(struct charge_state_data *curr);
+
+/**
+ * Common code of charger profile override for fast charging.
+ *
+ * @param curr Charge state machine data.
+ * @param fast_chg_params Fast charge profile parameters.
+ * @param prev_chg_prof_info Previous charge profile info.
+ * @param batt_vtg_max Maximum battery voltage.
+ *
+ * @return
+ * >0 Desired time in usec for this poll period.
+ * 0 Use the default poll period (which varies with the state).
+ * <0 An error occurred. The poll time will be shorter than usual.
+ * Too many errors in a row may trigger some corrective action.
+ */
+int charger_profile_override_common(struct charge_state_data *curr,
+ const struct fast_charge_params *fast_chg_params,
+ const struct fast_charge_profile **prev_chg_prof_info,
+ int batt_vtg_max);
+
+/*
+ * Access to custom profile params through host commands.
+ * What this does is up to the implementation.
+ */
+enum ec_status charger_profile_override_get_param(uint32_t param,
+ uint32_t *value);
+enum ec_status charger_profile_override_set_param(uint32_t param,
+ uint32_t value);
+
+#endif /* __CROS_EC_CHARGER_PROFILE_OVERRIDE_H */
diff --git a/include/chipset.h b/include/chipset.h
index ac50df0cc1..3113643e17 100644
--- a/include/chipset.h
+++ b/include/chipset.h
@@ -82,6 +82,11 @@ void chipset_reset(int cold_reset);
*/
void power_interrupt(enum gpio_signal signal);
+/**
+ * Handle assert of eSPI_Reset# pin.
+ */
+void chipset_handle_espi_reset_assert(void);
+
#else /* !HAS_TASK_CHIPSET */
/*
* Allow other modules to compile if the chipset module is disabled. This is
@@ -98,6 +103,7 @@ static inline void chipset_throttle_cpu(int throttle) { }
static inline void chipset_force_shutdown(void) { }
static inline void chipset_reset(int cold_reset) { }
static inline void power_interrupt(enum gpio_signal signal) { }
+static inline void chipset_handle_espi_reset_assert(void) { }
#endif /* !HAS_TASK_CHIPSET */
diff --git a/include/common.h b/include/common.h
index 84b9366064..fff9415240 100644
--- a/include/common.h
+++ b/include/common.h
@@ -59,6 +59,13 @@
#endif
/*
+ * Define __unused in the same manner.
+ */
+#ifndef __unused
+#define __unused __attribute__((unused))
+#endif
+
+/*
* Force the toolchain to keep a symbol even with Link Time Optimization
* activated.
*
@@ -84,6 +91,12 @@
#define CELSIUS_TO_DECI_KELVIN(temp_c) ((temp_c) * 10 + 2731)
#define DECI_KELVIN_TO_CELSIUS(temp_dk) ((temp_dk - 2731) / 10)
+/* Calculate a value with error margin considered. For example,
+ * TARGET_WITH_MARGIN(X, 5) returns X' where X' * 100.5% is almost equal to
+ * but does not exceed X. */
+#define TARGET_WITH_MARGIN(target, tenths_percent) \
+ (((target) * 1000) / (1000 + (tenths_percent)))
+
/* Include top-level configuration file */
#include "config.h"
@@ -124,9 +137,32 @@ enum ec_error_list {
EC_ERROR_PARAM7 = 17,
EC_ERROR_PARAM8 = 18,
EC_ERROR_PARAM9 = 19,
- EC_ERROR_PARAM_COUNT = 20, /* Wrong number of params */
-
- EC_ERROR_NOT_HANDLED = 21, /* Interrupt event not handled */
+ /* Wrong number of params */
+ EC_ERROR_PARAM_COUNT = 20,
+ /* Interrupt event not handled */
+ EC_ERROR_NOT_HANDLED = 21,
+ /* Data has not changed */
+ EC_ERROR_UNCHANGED = 22,
+ /* Memory allocation */
+ EC_ERROR_MEMORY_ALLOCATION = 23,
+
+ /* Verified boot errors */
+ EC_ERROR_VBOOT_SIGNATURE = 0x1000, /* 4096 */
+ EC_ERROR_VBOOT_SIG_MAGIC = 0x1001,
+ EC_ERROR_VBOOT_SIG_SIZE = 0x1002,
+ EC_ERROR_VBOOT_SIG_ALGORITHM = 0x1003,
+ EC_ERROR_VBOOT_HASH_ALGORITHM = 0x1004,
+ EC_ERROR_VBOOT_SIG_OFFSET = 0x1005,
+ EC_ERROR_VBOOT_DATA_SIZE = 0x1006,
+
+ /* Verified boot key errors */
+ EC_ERROR_VBOOT_KEY = 0x1100,
+ EC_ERROR_VBOOT_KEY_MAGIC = 0x1101,
+ EC_ERROR_VBOOT_KEY_SIZE = 0x1102,
+
+ /* Verified boot data errors */
+ EC_ERROR_VBOOT_DATA = 0x1200,
+ EC_ERROR_VBOOT_DATA_VERIFY = 0x1201,
/* Module-internal error codes may use this range. */
EC_ERROR_INTERNAL_FIRST = 0x10000,
@@ -147,4 +183,7 @@ enum ec_error_list {
#define test_export_static static
#endif
+/* find the most significant bit. Not defined in n == 0. */
+#define __fls(n) (31 - __builtin_clz(n))
+
#endif /* __CROS_EC_COMMON_H */
diff --git a/include/compile_time_macros.h b/include/compile_time_macros.h
index f25ffd8407..b1e627d617 100644
--- a/include/compile_time_macros.h
+++ b/include/compile_time_macros.h
@@ -27,6 +27,8 @@
#define offsetof(type, member) __builtin_offsetof(type, member)
#endif
+#define member_size(type, member) sizeof(((type *)0)->member)
+
#define __visible __attribute__((externally_visible))
#endif /* __CROS_EC_COMPILE_TIME_MACROS_H */
diff --git a/include/config.h b/include/config.h
index 65b3bcf734..92221b8cec 100644
--- a/include/config.h
+++ b/include/config.h
@@ -36,11 +36,12 @@
* BOARD_*, CHIP_*, and CHIP_FAMILY_*.
*/
-/* Enable accelerometer interrupts. */
-#undef CONFIG_ACCEL_INTERRUPTS
-/* Add support for sensor FIFO:
- * define the size of the global fifo, must be a power of 2. */
+/*
+ * Add support for sensor FIFO:
+ * define the size of the global fifo, must be a power of 2.
+ */
#undef CONFIG_ACCEL_FIFO
+
/* The amount of free entries that trigger an interrupt to the AP. */
#undef CONFIG_ACCEL_FIFO_THRES
@@ -50,12 +51,38 @@
*/
#undef CONFIG_ACCEL_FORCE_MODE_MASK
+/* Enable accelerometer interrupts. */
+#undef CONFIG_ACCEL_INTERRUPTS
+
+/*
+ * Support "spoof" mode for sensors. This allows sensors to have their values
+ * spoofed to any arbitrary value. This is useful for testing.
+ */
+#define CONFIG_ACCEL_SPOOF_MODE
+
/* Specify type of accelerometers attached. */
#undef CONFIG_ACCEL_BMA255
#undef CONFIG_ACCEL_KXCJ9
#undef CONFIG_ACCEL_KX022
+#undef CONFIG_ACCEL_LIS2DH
#undef CONFIG_ACCELGYRO_LSM6DS0
#undef CONFIG_ACCELGYRO_BMI160
+#undef CONFIG_ACCELGYRO_LSM6DSM
+
+/* Support for BMI160 hardware orientation sensor */
+#undef CONFIG_BMI160_ORIENTATION_SENSOR
+
+/* Support for KIONIX KX022 hardware orientation sensor */
+#undef CONFIG_KX022_ORIENTATION_SENSOR
+
+/*
+ * Define if either CONFIG_BMI160_ORIENTATION_SUPPORT or
+ * CONFIG_KX022_ORIENTATION_SUPPORT is set.
+ */
+#undef CONFIG_ORIENTATION_SENSOR
+
+/* Support the orientation gesture */
+#undef CONFIG_GESTURE_ORIENTATION
/* Specify barometer attached */
#undef CONFIG_BARO_BMP280
@@ -82,9 +109,18 @@
*/
#undef CONFIG_ACCELGYRO_BMI160_INT_EVENT
+/* Set when INT2 is an ouptut */
+#undef CONFIG_ACCELGYRO_BMI160_INT2_OUTPUT
+
/* Specify type of Gyrometers attached. */
#undef CONFIG_GYRO_L3GD20H
+/*
+ * Define the event to raise when LIS2DH interrupt.
+ * Must be within TASK_EVENT_MOTION_INTERRUPT_MASK.
+ */
+#undef CONFIG_ACCEL_LIS2DH_INT_EVENT
+
/* Compile chip support for analog-to-digital convertor */
#undef CONFIG_ADC
@@ -110,7 +146,11 @@
* Some ALS modules may be connected to the EC. We need the command, and
* specific drivers for each module.
*/
+#ifdef HAS_TASK_ALS
+#define CONFIG_ALS
+#else
#undef CONFIG_ALS
+#endif
#undef CONFIG_ALS_AL3010
#undef CONFIG_ALS_ISL29035
#undef CONFIG_ALS_OPT3001
@@ -124,6 +164,14 @@
*/
#undef CONFIG_ALS_SI114X_INT_EVENT
+/*
+ * Enable Si114x to operate in polling mode. This config is used in conjunction
+ * with CONFIG_ALS_SI114X_INT_EVENT. When polling is enabled, the read is
+ * initiated in the same manner as when interrupts are used, but the event which
+ * triggers the irq_handler is generated by deferred call using a fixed delay.
+ */
+#undef CONFIG_ALS_SI114X_POLLING
+
/* Define which ALS sensor is used for dimming the lightbar when dark */
#undef CONFIG_ALS_LIGHTBAR_DIMMING
@@ -148,6 +196,9 @@
*/
#undef CONFIG_BACKLIGHT_REQ_GPIO
+/* Support base32 text encoding */
+#undef CONFIG_BASE32
+
/*****************************************************************************/
/* Battery config */
@@ -160,28 +211,30 @@
* Note that some boards have their own unique battery constants / functions.
* In this case, those are provided in board/(boardname)/battery.c, and none of
* these are defined.
+ * Defining one of these will automatically define CONFIG_BATTERY near the end
+ * of this file. If you add a new config here, you'll need to update that
+ * check.
*/
#undef CONFIG_BATTERY_BQ20Z453
#undef CONFIG_BATTERY_BQ27541
#undef CONFIG_BATTERY_BQ27621
-#undef CONFIG_BATTERY_RYU
-#undef CONFIG_BATTERY_SAMUS
+#undef CONFIG_BATTERY_MAX17055
/* Compile mock battery support; used by tests. */
#undef CONFIG_BATTERY_MOCK
-/*
- * Charger should call battery_override_params() to limit/correct the voltage
- * and current requested by the battery pack before acting on the request.
- *
- * This is valid with CONFIG_CHARGER_V1 only.
- */
-#undef CONFIG_BATTERY_OVERRIDE_PARAMS
-
/* Maximum time to wake a non-responsive battery, in second */
#define CONFIG_BATTERY_PRECHARGE_TIMEOUT 30
/*
+ * If defined, the charger will check a board specific function for battery hw
+ * presence as an additional condition to determine if power on is allowed for
+ * factory override, where allowing booting of a bare board with no battery and
+ * no power button press is required.
+ */
+#undef CONFIG_BATTERY_HW_PRESENT_CUSTOM
+
+/*
* If defined, the charger will check for battery presence before attempting
* to communicate with it. This avoids the 30 second delay when booting
* without a battery present. Do not use with CONFIG_BATTERY_PRESENT_GPIO.
@@ -207,6 +260,9 @@
*/
#undef CONFIG_BATTERY_SMART
+/* Chemistry of the battery device */
+#undef CONFIG_BATTERY_DEVICE_CHEMISTRY
+
/*
* Critical battery shutdown timeout (seconds)
*
@@ -257,6 +313,18 @@
*/
#undef CONFIG_BATTERY_REVIVE_DISCONNECT
+/*
+ * Specify the battery percentage at which the host is told it is full.
+ * If this value is not specified the default is 97% set in battery.h.
+ */
+#undef CONFIG_BATTERY_LEVEL_NEAR_FULL
+
+/*
+ * Expose some data when it is needed.
+ * For example, battery disconnect state
+ */
+#undef CONFIG_CHARGE_STATE_DEBUG
+
/* Include support for Bluetooth LE */
#undef CONFIG_BLUETOOTH_LE
@@ -284,6 +352,13 @@
#undef CONFIG_BOARD_HAS_RTC_RESET
/*
+ * Call board_before_rsmrst(state) before passing RSMRST# to the AP.
+ * This is for board workarounds that are required after rails are up
+ * but before the AP is out of reset.
+ */
+#undef CONFIG_BOARD_HAS_BEFORE_RSMRST
+
+/*
* Call board_config_post_gpio_init() after GPIOs are initialized. See
* include/board_config.h for more information.
*/
@@ -300,6 +375,9 @@
/* The decoding of the GPIOs defining board version is defined in board code */
#undef CONFIG_BOARD_SPECIFIC_VERSION
+/* EC responses to a board defined I2C slave address */
+#undef CONFIG_BOARD_I2C_SLAVE_ADDR
+
/* Permanent LM4 boot configuration */
#undef CONFIG_BOOTCFG_VALUE
@@ -322,11 +400,20 @@
#undef CONFIG_BUTTON_COUNT
/*
- * Enable case close debug (CCD) mode in the EC.
+ * Support for entering recovery mode using volume buttons. You need to
+ * list the buttons in recovery_buttons.
*/
-#undef CONFIG_CASE_CLOSED_DEBUG
-/* The case close debug (CCD) feature is provided by an external chip. */
-#undef CONFIG_CASE_CLOSED_DEBUG_EXTERNAL
+#undef CONFIG_BUTTON_RECOVERY
+
+/*
+ * Indicates there is a dedicated recovery button.
+ */
+#undef CONFIG_DEDICATED_RECOVERY_BUTTON
+
+/* Support V1 CCD configuration */
+#undef CONFIG_CASE_CLOSED_DEBUG_V1
+/* Allow unsafe debugging functionality in V1 configuration */
+#undef CONFIG_CASE_CLOSED_DEBUG_V1_UNSAFE
/*
* Capsense chip has buttons, too.
@@ -338,28 +425,36 @@
/* Compile charge manager */
#undef CONFIG_CHARGE_MANAGER
+/* Number of charge ports excluding type-c ports */
+#define CONFIG_DEDICATED_CHARGE_PORT_COUNT 0
+
/* Allow charge manager to default to charging from dual-role partners */
#undef CONFIG_CHARGE_MANAGER_DRP_CHARGING
/* Handle the external power limit host command in charge manager */
#undef CONFIG_CHARGE_MANAGER_EXTERNAL_POWER_LIMIT
-/* Compile input current ramping support */
-#undef CONFIG_CHARGE_RAMP
+/* Initially enter safe mode, with relaxed port / current selection rules */
+#define CONFIG_CHARGE_MANAGER_SAFE_MODE
+
+/* Leave safe mode when battery pct meets or exceeds this value */
+#define CONFIG_CHARGE_MANAGER_BAT_PCT_SAFE_MODE_EXIT 2
/* The hardware has some input current ramping/back-off mechanism */
#undef CONFIG_CHARGE_RAMP_HW
+/* Compile input current ramping support using software control */
+#undef CONFIG_CHARGE_RAMP_SW
+
/*****************************************************************************/
/* Charger config */
/* Compile common charge state code. You must pick an implementation. */
#undef CONFIG_CHARGER
-#undef CONFIG_CHARGER_V1
#undef CONFIG_CHARGER_V2
/* Compile charger-specific code for these chargers (pick at most one) */
-#undef CONFIG_CHARGER_BD99955
+#undef CONFIG_CHARGER_BD9995X
#undef CONFIG_CHARGER_BQ24707A
#undef CONFIG_CHARGER_BQ24715
#undef CONFIG_CHARGER_BQ24725
@@ -371,23 +466,24 @@
#undef CONFIG_CHARGER_BQ25892
#undef CONFIG_CHARGER_BQ25895
#undef CONFIG_CHARGER_ISL9237
-#undef CONFIG_CHARGER_TPS65090 /* Note: does not use CONFIG_CHARGER */
+#undef CONFIG_CHARGER_ISL9238
+#undef CONFIG_CHARGER_RT9466
+#undef CONFIG_CHARGER_RT9467
/*
- * BD99955 PD port to charger port mapping.
- * By default VBUS is selected as primary port.
- * Define only if the VCC is the primary port.
+ * Enable the CHG_EN at initialization to turn-on the BGATE which allows voltage
+ * to be applied to the battery PACK & wakes the battery if it is in shipmode.
*/
-#undef CONFIG_BD99955_PRIMARY_CHARGE_PORT_VCC
+#undef CONFIG_CHARGER_BD9995X_CHGEN
/*
- * BD99955 Power Save Mode
+ * BD9995X Power Save Mode
*
* Which power save mode should the charger enter when VBUS is removed. Check
- * driver/bd99955.h for the power save settings. By default, no power save mode
+ * driver/bd9995x.h for the power save settings. By default, no power save mode
* is enabled.
*/
-#undef CONFIG_BD99955_POWER_SAVE_MODE
+#undef CONFIG_BD9995X_POWER_SAVE_MODE
/*
* If the battery temperature sense pin is connected to charger,
@@ -458,6 +554,13 @@
*/
#undef CONFIG_CHARGER_MAX_INPUT_CURRENT
+/*
+ * Leave charger VBAT configured to battery-requested voltage under all
+ * conditions, even when AC is not present. This may be necessary to work
+ * around quirks of certain charger chips, such as the BD9995X.
+ */
+#undef CONFIG_CHARGER_MAINTAIN_VBAT
+
/* Minimum battery percentage for power on */
#undef CONFIG_CHARGER_MIN_BAT_PCT_FOR_POWER_ON
@@ -473,11 +576,25 @@
#undef CONFIG_CHARGER_LIMIT_POWER_THRESH_CHG_MW
/*
- * Equivalent of CONFIG_BATTERY_OVERRIDE_PARAMS for use with
- * CONFIG_CHARGER_V2
+ * Charger should call battery_override_params() to limit/correct the voltage
+ * and current requested by the battery pack before acting on the request.
+ *
+ * This is valid with CONFIG_CHARGER_V2 only.
*/
#undef CONFIG_CHARGER_PROFILE_OVERRIDE
+/*
+ * Common code for charger profile override. Should be used with
+ * CONFIG_CHARGER_PROFILE_OVERRIDE.
+ */
+#undef CONFIG_CHARGER_PROFILE_OVERRIDE_COMMON
+
+/*
+ * Battery voltage threshold ranges for charge profile override.
+ * Override it in board.h if battery has multiple threshold ranges.
+ */
+#define CONFIG_CHARGER_PROFILE_VOLTAGE_RANGES 2
+
/* Value of the charge sense resistor, in mOhms */
#undef CONFIG_CHARGER_SENSE_RESISTOR
@@ -485,15 +602,6 @@
#undef CONFIG_CHARGER_SENSE_RESISTOR_AC
/*
- * Maximum time to charge the battery, in hours.
- *
- * If this timeout is reached, the charger will enter force-idle state.
- * If not defined, charger will provide current until the battery asks it to
- * stop.
- */
-#undef CONFIG_CHARGER_TIMEOUT_HOURS
-
-/*
* Board has an GPIO pin to enable or disable charging.
*
* This GPIO should be named GPIO_CHARGER_EN, if active high. Or
@@ -508,21 +616,27 @@
#undef CONFIG_TRICKLE_CHARGING
/*****************************************************************************/
+
+/*
+ * Chip needs to do pre-init very early in main(), and provides chip_pre_init()
+ * to do so.
+ */
+#undef CONFIG_CHIP_PRE_INIT
+
+/*****************************************************************************/
/* Chipset config */
/* AP chipset support; pick at most one */
#undef CONFIG_CHIPSET_APOLLOLAKE/* Intel Apollolake (x86) */
-#undef CONFIG_CHIPSET_BAYTRAIL /* Intel Bay Trail (x86) */
#undef CONFIG_CHIPSET_BRASWELL /* Intel Braswell (x86) */
+#undef CONFIG_CHIPSET_CANNONLAKE /* Intel Cannonlake (x86) */
#undef CONFIG_CHIPSET_ECDRIVEN /* Dummy power module */
-#undef CONFIG_CHIPSET_GAIA /* Gaia and Ares (ARM) */
-#undef CONFIG_CHIPSET_HASWELL /* Intel Haswell (x86) */
#undef CONFIG_CHIPSET_MEDIATEK /* MediaTek MT81xx */
#undef CONFIG_CHIPSET_RK3399 /* Rockchip rk3399 */
/* TODO: Rename below config to CONFIG_CHIPSET_RK32XX */
#undef CONFIG_CHIPSET_ROCKCHIP /* Rockchip rk32xx */
#undef CONFIG_CHIPSET_SKYLAKE /* Intel Skylake (x86) */
-#undef CONFIG_CHIPSET_TEGRA /* nVidia Tegra 5 */
+#undef CONFIG_CHIPSET_STONEY /* AMD Stoney (x86)*/
/* Support chipset throttling */
#undef CONFIG_CHIPSET_CAN_THROTTLE
@@ -537,6 +651,12 @@
#define CONFIG_CHIPSET_HAS_PP1350
#define CONFIG_CHIPSET_HAS_PP5000
+/* Support PMIC reset(using LDO_EN) in chipset */
+#undef CONFIG_CHIPSET_HAS_PLATFORM_PMIC_RESET
+
+/* Redefine when we need a different power-on sequence on the same chipset. */
+#define CONFIG_CHIPSET_POWER_SEQ_VERSION 0
+
/*****************************************************************************/
/*
* Chip config for clock circuitry
@@ -547,6 +667,19 @@
/* Indicate if a clock source is connected to stm32f4's "HSE" specific input */
#undef CONFIG_STM32_CLOCK_HSE_HZ
+/* Indicate if a clock source is connected to "LSE" specific input */
+#undef CONFIG_STM32_CLOCK_LSE
+
+/*
+ * Chip config for clock source
+ * define = external crystal oscillator / undef = internal clock source
+ */
+#undef CONFIG_CLOCK_SRC_EXTERNAL
+
+/*****************************************************************************/
+/* Support curve25519 public key cryptography */
+#undef CONFIG_CURVE25519
+
/*****************************************************************************/
/* PMIC config */
@@ -564,70 +697,93 @@
* console.
*/
-#undef CONFIG_CMD_ACCELS
-#undef CONFIG_CMD_ACCEL_FIFO
-#undef CONFIG_CMD_ACCEL_INFO
-#undef CONFIG_CMD_ALS
+#undef CONFIG_CMD_ACCELS
+#undef CONFIG_CMD_ACCEL_FIFO
+#undef CONFIG_CMD_ACCEL_INFO
+#define CONFIG_CMD_ACCELSPOOF
+#undef CONFIG_CMD_ALS
#define CONFIG_CMD_APTHROTTLE
-#undef CONFIG_CMD_BATDEBUG
+#undef CONFIG_CMD_BATDEBUG
#define CONFIG_CMD_BATTFAKE
-#undef CONFIG_CMD_BUTTON
+#undef CONFIG_CMD_BATT_MFG_ACCESS
+#undef CONFIG_CMD_BUTTON
+#undef CONFIG_CMD_CCD_DISABLE /* 'ccd disable' subcommand */
#define CONFIG_CMD_CHARGER
-#undef CONFIG_CMD_CHARGER_ADC_AMON_BMON
-#undef CONFIG_CMD_CHARGER_PSYS
+#undef CONFIG_CMD_CHARGER_ADC_AMON_BMON
+#undef CONFIG_CMD_CHARGER_PROFILE_OVERRIDE
+#undef CONFIG_CMD_CHARGER_PROFILE_OVERRIDE_TEST
+#undef CONFIG_CMD_CHARGER_PSYS
#define CONFIG_CMD_CHARGE_SUPPLIER_INFO
-#undef CONFIG_CMD_CHGRAMP
-#undef CONFIG_CMD_CLOCKGATES
-#undef CONFIG_CMD_COMXTEST
+#undef CONFIG_CMD_CHGRAMP
+#undef CONFIG_CMD_CLOCKGATES
+#undef CONFIG_CMD_COMXTEST
#define CONFIG_CMD_CRASH
-#undef CONFIG_CMD_ECTEMP
+#define CONFIG_CMD_DEVICE_EVENT
+#undef CONFIG_CMD_ECTEMP
#define CONFIG_CMD_FASTCHARGE
-#undef CONFIG_CMD_FLASH
-#undef CONFIG_CMD_FORCETIME
-#undef CONFIG_CMD_GSV
-#undef CONFIG_CMD_GPIO_EXTENDED
+#undef CONFIG_CMD_FLASH
+#define CONFIG_CMD_FLASHINFO
+#undef CONFIG_CMD_FLASH_TRISTATE
+#undef CONFIG_CMD_FORCETIME
+#undef CONFIG_CMD_GPIO_EXTENDED
+#undef CONFIG_CMD_GSV
#define CONFIG_CMD_HASH
#define CONFIG_CMD_HCDEBUG
-#undef CONFIG_CMD_HOSTCMD
-#undef CONFIG_CMD_I2C_PROTECT
+#undef CONFIG_CMD_HOSTCMD
+#undef CONFIG_CMD_I2CWEDGE
+#undef CONFIG_CMD_I2C_PROTECT
#define CONFIG_CMD_I2C_SCAN
+#undef CONFIG_CMD_I2C_STRESS_TEST
+#undef CONFIG_CMD_I2C_STRESS_TEST_ACCEL
+#undef CONFIG_CMD_I2C_STRESS_TEST_ALS
+#undef CONFIG_CMD_I2C_STRESS_TEST_BATTERY
+#undef CONFIG_CMD_I2C_STRESS_TEST_CHARGER
+#undef CONFIG_CMD_I2C_STRESS_TEST_TCPC
#define CONFIG_CMD_I2C_XFER
-#undef CONFIG_CMD_I2CWEDGE
#define CONFIG_CMD_IDLE_STATS
-#define CONFIG_CMD_KEYBOARD
-#undef CONFIG_CMD_ILIM
+#undef CONFIG_CMD_ILIM
#define CONFIG_CMD_INA
-#define CONFIG_CMD_REGULATOR
-#undef CONFIG_CMD_RTC
-#undef CONFIG_CMD_JUMPTAGS
-#undef CONFIG_CMD_LID_ANGLE
-#undef CONFIG_CMD_MCDP
+#undef CONFIG_CMD_JUMPTAGS
+#define CONFIG_CMD_KEYBOARD
+#undef CONFIG_CMD_LID_ANGLE
+#undef CONFIG_CMD_MCDP
#define CONFIG_CMD_MD
#define CONFIG_CMD_MEM
+#define CONFIG_CMD_MMAPINFO
#define CONFIG_CMD_PD
-#undef CONFIG_CMD_PD_CONTROL
-#undef CONFIG_CMD_PD_DEV_DUMP_INFO
-#undef CONFIG_CMD_PD_FLASH
-#undef CONFIG_CMD_PLL
-#undef CONFIG_CMD_PMU
-#define CONFIG_CMD_POWER_AP
+#undef CONFIG_CMD_PD_CONTROL
+#undef CONFIG_CMD_PD_DEV_DUMP_INFO
+#undef CONFIG_CMD_PD_FLASH
+#undef CONFIG_CMD_PLL
+#undef CONFIG_CMD_PMU
#define CONFIG_CMD_POWERINDEBUG
-#undef CONFIG_CMD_POWERLED
-#undef CONFIG_CMD_RTC_ALARM
-#undef CONFIG_CMD_SCRATCHPAD
+#undef CONFIG_CMD_POWERLED
+#define CONFIG_CMD_POWER_AP
+#undef CONFIG_CMD_PPC_DUMP
+#define CONFIG_CMD_REGULATOR
+#undef CONFIG_CMD_RTC
+#undef CONFIG_CMD_RTC_ALARM
+#define CONFIG_CMD_RW
+#undef CONFIG_CMD_SCRATCHPAD
#define CONFIG_CMD_SHMEM
-#undef CONFIG_CMD_SLEEP
-#undef CONFIG_CMD_SPI_FLASH
-#undef CONFIG_CMD_SPI_NOR
-#undef CONFIG_CMD_SPI_XFER
-#undef CONFIG_CMD_STACKOVERFLOW
-#undef CONFIG_CMD_TASKREADY
+#undef CONFIG_CMD_SLEEP
+#define CONFIG_CMD_SLEEPMASK
+#undef CONFIG_CMD_SPI_FLASH
+#undef CONFIG_CMD_SPI_NOR
+#undef CONFIG_CMD_SPI_XFER
+#undef CONFIG_CMD_STACKOVERFLOW
+#define CONFIG_CMD_SYSINFO
+#define CONFIG_CMD_SYSJUMP
+#define CONFIG_CMD_SYSLOCK
+#undef CONFIG_CMD_TASKREADY
#define CONFIG_CMD_TEMP_SENSOR
#define CONFIG_CMD_TIMERINFO
+#undef CONFIG_CMD_TPM_LOG
#define CONFIG_CMD_TYPEC
-#undef CONFIG_CMD_USART_INFO
-#undef CONFIG_CMD_USB_PD_PE
+#undef CONFIG_CMD_USART_INFO
#define CONFIG_CMD_USBMUX
+#undef CONFIG_CMD_USB_PD_PE
+#define CONFIG_CMD_WAITMS
/*****************************************************************************/
@@ -641,6 +797,15 @@
#undef CONFIG_SOFTWARE_PANIC
/*
+ * Certain platforms(e.g. eve, poppy) cannot retain panic info in data ram since
+ * VCC is powered down on EC reset. On such platforms, panic data needs to be
+ * saved/restored to persistent storage by using chip specific
+ * implementations. This option can be enabled by those platforms that have and
+ * wish to use chip-implemented panic backup/restore functions.
+ */
+#undef CONFIG_CHIP_PANIC_BACKUP
+
+/*
* Provide the default GPIO abstraction layer.
* You want this unless you are doing a really tiny firmware.
*/
@@ -688,6 +853,12 @@
#undef CONFIG_CONSOLE_COMMAND_FLAGS_DEFAULT
/*
+ * Enable EC_CMD_CONSOLE_READ V1. One could disable this config to prevent
+ * kernel from creating the `console_log` debugfs entry.
+ */
+#define CONFIG_CONSOLE_ENABLE_READ_V1
+
+/*
* Number of entries in console history buffer.
*
* Boards may #undef this to reduce memory usage.
@@ -697,6 +868,9 @@
/* Max length of a single line of input */
#define CONFIG_CONSOLE_INPUT_LINE_SIZE 80
+/* Enable verbose output to UART console and extra timestamp print precision. */
+#define CONFIG_CONSOLE_VERBOSE
+
/*
* Enable the experimental console.
*
@@ -722,6 +896,32 @@
*/
#undef CONFIG_DCRYPTO
+/*
+ * When enabled, accelerate sha512 using the generic crypto engine;
+ * only supported on CR50
+ */
+#undef CONFIG_DCRYPTO_SHA512
+
+/*
+ * When enabled build support for SHA-384/512, requires CONFIG_DCRYPTO.
+ */
+#undef CONFIG_UPTO_SHA512
+
+/*
+ * When enabled ignore version et al during fw upgrade for chip/g.
+ */
+#undef CONFIG_IGNORE_G_UPDATE_CHECKS
+
+/*
+ * When enabled hardware alerts statistics provided via VendorCommand extension.
+ */
+#undef CONFIG_ENABLE_H1_ALERTS
+
+/*
+ * Enable console shell command 'alerts' that prints chip alerts statistics.
+ */
+#undef CONFIG_ENABLE_H1_ALERTS_CONSOLE
+
/*****************************************************************************/
/*
* Debugging config
@@ -793,6 +993,12 @@
*/
#define CONFIG_DEBUG_EXCEPTIONS
+/*
+ * Print orientation when device orientation changes
+ * (requires CONFIG_SENSOR_ORIENTATION)
+ */
+#undef CONFIG_DEBUG_ORIENTATION
+
/* Support Synchronous UART debug printf. */
#undef CONFIG_DEBUG_PRINTF
@@ -801,6 +1007,9 @@
/*****************************************************************************/
+/* Support events from devices attached to the EC */
+#undef CONFIG_DEVICE_EVENT
+
/* Monitor the states of other devices */
#undef CONFIG_DEVICE_STATE
@@ -816,9 +1025,30 @@
/* Support EC to Internal bus bridge. */
#undef CONFIG_EC2I
+/* EC capable of sensor speeds up to 200000 mHz */
+#define CONFIG_EC_MAX_SENSOR_FREQ_MILLIHZ 200000
+
+/*
+ * Allow board to override the feature bitmap provided through host command
+ * and ACPI.
+ */
+#undef CONFIG_EC_FEATURE_BOARD_OVERRIDE
+
/* Support EC chip internal data EEPROM */
#undef CONFIG_EEPROM
+/*
+ * Support for sending emulated sysrq events to AP (on designs with a keyboard,
+ * sysrq is passed as normal key presses).
+ */
+#undef CONFIG_EMULATED_SYSRQ
+
+/* Support for eSPI for host communication */
+#undef CONFIG_ESPI
+
+/* Use Virtual Wire signals instead of GPIO with eSPI interface */
+#undef CONFIG_ESPI_VW_SIGNALS
+
/* Include code for handling external power */
#define CONFIG_EXTPOWER
@@ -828,10 +1058,16 @@
/* Default debounce time for external power signal */
#define CONFIG_EXTPOWER_DEBOUNCE_MS 30
+/* Add support for CCD factory mode */
+#undef CONFIG_FACTORY_MODE
+
/*****************************************************************************/
/* Number of cooling fans. Undef if none. */
#undef CONFIG_FANS
+/* Percentage to which all fans are set at initiation */
+#define CONFIG_FAN_INIT_SPEED 100
+
/* Support fan control while in low-power idle */
#undef CONFIG_FAN_DSLEEP
@@ -854,13 +1090,18 @@
/*****************************************************************************/
/* Flash configuration */
-/* Support programming on-chip flash */
+/* This enables console commands and higher-level features */
#define CONFIG_FLASH
-
+/* This enables chip-specific access functions */
+#define CONFIG_FLASH_PHYSICAL
#undef CONFIG_FLASH_BANK_SIZE
#undef CONFIG_FLASH_ERASED_VALUE32
#undef CONFIG_FLASH_ERASE_SIZE
#undef CONFIG_FLASH_ROW_SIZE
+/* Allow deferred (async) flash erase */
+#undef CONFIG_FLASH_DEFERRED_ERASE
+/* Flash must be selected for write/erase operations to succeed. */
+#undef CONFIG_FLASH_SELECT_REQUIRED
/* Base address of program memory */
#undef CONFIG_PROGRAM_MEMORY_BASE
@@ -887,6 +1128,11 @@
#undef CONFIG_FLASH_PROTECT_NEXT_BOOT
/*
+ * Some platforms need to write protect RW independently of all flash.
+ */
+#undef CONFIG_FLASH_PROTECT_RW
+
+/*
* Store persistent write protect for the flash inside the flash data itself.
* This allows ECs with internal flash to emulate something closer to a SPI
* flash write protect register. If this is not defined, write protect state
@@ -906,6 +1152,19 @@
*/
#define CONFIG_FLASH_PSTATE_BANK
+/*
+ * Lock the PSTATE by default (currently only supported when
+ * CONFIG_FLASH_PSTATE_BANK is not defined).
+ */
+#undef CONFIG_FLASH_PSTATE_LOCKED
+
+/*
+ * For flash that is segemented in different regions.
+ */
+#undef CONFIG_FLASH_MULTIPLE_REGION
+/* Number of regions of different size/type */
+#undef CONFIG_FLASH_REGION_TYPE_COUNT
+
/* Total size of writable flash */
#undef CONFIG_FLASH_SIZE
@@ -922,6 +1181,12 @@
#undef CONFIG_EC_WRITABLE_STORAGE_OFF
#undef CONFIG_EC_WRITABLE_STORAGE_SIZE
+/* Enable robust non-volatile counter in flash */
+#undef CONFIG_FLASH_NVCOUNTER
+/* Address of start of the NVcounter flash page */
+#undef CONFIG_FLASH_NVCTR_BASE_A
+#undef CONFIG_FLASH_NVCTR_BASE_B
+
/*****************************************************************************/
/* NvMem Configuration */
/* Enable NV Memory module within flash */
@@ -935,6 +1200,16 @@
/* Size in bytes of NvMem area */
#undef CONFIG_FLASH_NVMEM_SIZE
+/* Enable <key,value> variable support (requires CONFIG_FLASH_NVMEM) */
+#undef CONFIG_FLASH_NVMEM_VARS
+/*
+ * We already have to define nvmem_user_sizes[] to specify the order and size
+ * of the user regions. CONFIG_FLASH_NVMEM_VARS looks for two symbols to
+ * specify the region number and size for the variable region.
+ */
+#undef CONFIG_FLASH_NVMEM_VARS_USER_NUM
+#undef CONFIG_FLASH_NVMEM_VARS_USER_SIZE
+
/*****************************************************************************/
/* Include a flashmap in the compiled firmware image */
@@ -994,6 +1269,39 @@
#undef CONFIG_WP_STORAGE_SIZE
/*
+ * Rollback protect region. If CONFIG_ROLLBACK is defined to enable the rollback
+ * protect region, CONFIG_ROLLBACK_OFF and CONFIG_ROLLBACK_SIZE must be defined
+ * too.
+ */
+#undef CONFIG_ROLLBACK
+#undef CONFIG_ROLLBACK_OFF
+#undef CONFIG_ROLLBACK_SIZE
+
+/* If defined, add support for storing some entropy in the rollback region. */
+#undef CONFIG_ROLLBACK_SECRET_SIZE
+
+/*
+ * If defined, inject some locally generated entropy when secret is updated,
+ * using board_get_entropy function.
+ * Large values may take a long time to generate.
+ */
+#undef CONFIG_ROLLBACK_SECRET_LOCAL_ENTROPY_SIZE
+
+/* If defined, we can update rollback information (RW can unset this). */
+#define CONFIG_ROLLBACK_UPDATE
+
+/*
+ * Current rollback version. Meaningless for RO (but provides the minimum value
+ * that will be written to the rollback protection at flash time).
+ *
+ * For RW, rollback version included in version structure, used by RO to
+ * determine if the RW image is recent enough and can be jumped to.
+ *
+ * Valid values are >= 0, <= INT32_MAX (positive, 32-bit signed integer).
+ */
+#define CONFIG_ROLLBACK_VERSION 0
+
+/*
* Board Image ec.bin contains a RO firmware. If not defined, the image will
* only contain the RW firmware. The RO firmware comes from another board.
*/
@@ -1066,7 +1374,6 @@
#undef CONFIG_GESTURE_SIGMO_EVENT
-
/* Do we want to detect the lid angle? */
#undef CONFIG_LID_ANGLE
@@ -1091,6 +1398,17 @@
*/
#undef CONFIG_LID_ANGLE_UPDATE
+/*
+ * During shutdown sequence sensor rails can be powered down asynchronously
+ * to the EC hence EC cannot interlock the sensor states with the power down
+ * states. To avoid this issue, defer switching the sensors rate with a
+ * configurable delay if in S3. By the time deferred function is serviced,
+ * if the chipset is in S5 we can back out from switching the sensor rate.
+ */
+#define CONFIG_MOTION_SENSE_SUSPEND_DELAY_US 0
+
+/* Define motion sensor count in board layer */
+#undef CONFIG_DYNAMIC_MOTION_SENSOR_COUNT
/******************************************************************************/
/* Host to RAM (H2RAM) Memory Mapping */
@@ -1103,6 +1421,8 @@
/* H2RAM Host LPC I/O base memory address */
#undef CONFIG_H2RAM_HOST_LPC_IO_BASE
+/* ISH boot start address */
+#undef CONFIG_ISH_BOOT_START
/*
* Define the minimal amount of time (in ms) betwen running motion sense task
* loop.
@@ -1124,6 +1444,15 @@
*/
#undef CONFIG_HOST_COMMAND_STATUS
+/* clear bit(s) to mask reporting of an EC_HOST_EVENT_XXX event(s) */
+#define CONFIG_HOST_EVENT_REPORT_MASK 0xffffffff
+
+/*
+ * The host commands are sorted in the .rodata.hcmds section so use the binary
+ * search algorithm to match a command to its handler
+ */
+#undef CONFIG_HOSTCMD_SECTION_SORTED
+
/*
* Host command parameters and response are 32-bit aligned. This generates
* much more efficient code on ARM.
@@ -1141,6 +1470,13 @@
#endif
/*
+ * Board supports host command to get EC SPI flash info. This is typically
+ * only needed if the factory needs to determine which of several possible SPI
+ * flash chips is attached to the EC on a given board.
+ */
+#undef CONFIG_HOSTCMD_FLASH_SPI_INFO
+
+/*
* For ECs where the host command interface is I2C, slave
* address which the EC will respond to.
*/
@@ -1178,9 +1514,18 @@
/* Panic when status of PD MCU reflects that it has crashed */
#undef CONFIG_HOSTCMD_PD_PANIC
-/* Board supports RTC host commands*/
+/* Board supports RTC host commands */
#undef CONFIG_HOSTCMD_RTC
+/* For access to VBNV on-EC battery-backed storage */
+#undef CONFIG_HOSTCMD_VBNV_CONTEXT
+
+/* EC controls the board's SKU ID and can report that to the AP */
+#undef CONFIG_HOSTCMD_SKUID
+
+/* Set SKU ID from AP */
+#undef CONFIG_HOSTCMD_AP_SET_SKUID
+
/*****************************************************************************/
/* Enable debugging and profiling statistics for hook functions */
@@ -1216,6 +1561,13 @@
/* For ECs with multiple wakeup pins, define enabled wakeup pins */
#undef CONFIG_HIBERNATE_WAKEUP_PINS
+/*
+ * Use PSL (Power Switch Logic) for hibernating. It turns off VCC power rail
+ * for ultra-low power consumption and uses PSL inputs rely on VSBY power rail
+ * to wake up ec and the whole system.
+ */
+#undef CONFIG_HIBERNATE_PSL
+
/* Use a hardware specific udelay(). */
#undef CONFIG_HW_SPECIFIC_UDELAY
@@ -1249,6 +1601,14 @@
#undef CONFIG_I2C_SCL_GATE_GPIO
/*
+ * Some chip supports two owned slave address. The second slave address is used
+ * for other purpose such as board specific i2c commands. This option can be
+ * set if user of the second slave address requires larger host packet buffer
+ * size.
+ */
+#define CONFIG_I2C_EXTRA_PACKET_SIZE 0
+
+/*
* I2C multi-port controller.
*
* If CONFIG_I2C_MULTI_PORT_CONTROLLER is defined, a single on-chip I2C
@@ -1285,11 +1645,12 @@
#undef CONFIG_IRQ_COUNT
/*
- * This is the block size of the ILM on the it839x chip.
- * The ILM for static code cache, CPU fetch instruction from
- * ILM(ILM -> CPU)instead of flash(flash -> IMMU -> CPU) if enabled.
+ * The IT8320 supports e-flash clock up to 48 MHz (IT8390 maximum is 32 MHz).
+ * Enable it if we want better performance of fetching instruction from e-flash.
+ *
+ * This is valid with PLL frequency equal to 48/96MHz only.
*/
-#undef CONFIG_IT83XX_ILM_BLOCK_SIZE
+#undef CONFIG_IT83XX_FLASH_CLOCK_48MHZ
/* To define it, if I2C channel C and PECI used at the same time. */
#undef CONFIG_IT83XX_SMCLK2_ON_GPC7
@@ -1340,12 +1701,35 @@
#undef CONFIG_KEYBOARD_BOARD_CONFIG
/*
+ * Support for boot key combinations (e.g. refresh key being held on boot to
+ * trigger recovery).
+ */
+#define CONFIG_KEYBOARD_BOOT_KEYS
+
+/* Add support for the new key. */
+#undef CONFIG_KEYBOARD_NEW_KEY
+
+/*
* Minimum CPU clocks between scans. This ensures that keyboard scanning
* doesn't starve the other EC tasks of CPU when running at a decreased system
* clock.
*/
#undef CONFIG_KEYBOARD_POST_SCAN_CLOCKS
+/* Print keyboard scan time intervals. */
+#undef CONFIG_KEYBOARD_PRINT_SCAN_TIMES
+
+/*
+ * Support for extra runtime key combinations (e.g. alt+volup+h/r for hibernate
+ * and warm reboot, respectively).
+ */
+#define CONFIG_KEYBOARD_RUNTIME_KEYS
+
+/*
+ * Allow the keyboard scan code set tables to be modified at runtime.
+ */
+#undef CONFIG_KEYBOARD_SCANCODE_MUTABLE
+
/*
* Call board-supplied keyboard_suppress_noise() function when the debounced
* keyboard state changes. Some boards use this to send a signal to the audio
@@ -1364,6 +1748,14 @@
*/
#undef CONFIG_KEYBOARD_TEST
+/*
+ * Enable quasi-bidirectional buffers for KSO pins. It has an open-drain output
+ * and a low-impedance pull-up. The low-impedance pull-up is active when ec
+ * changes the output data buffers from 0 to 1, thereby reducing the
+ * low-to-high transition time.
+ */
+#undef CONFIG_KEYBOARD_KSO_HIGH_DRIVE
+
/*****************************************************************************/
/* Support common LED interface */
@@ -1458,6 +1850,9 @@
/* Use Link-Time Optimizations to try to reduce the firmware code size */
#undef CONFIG_LTO
+/* Provide rudimentary malloc/free like services for shared memory. */
+#undef CONFIG_MALLOC
+
/* Need for a math library */
#undef CONFIG_MATH_UTIL
@@ -1500,9 +1895,15 @@
/* Support memory protection unit (MPU) */
#undef CONFIG_MPU
+/* Do not try hold I/O pins at frozen level during deep sleep */
+#undef CONFIG_NO_PINHOLD
+
/* Support one-wire interface */
#undef CONFIG_ONEWIRE
+/* Support One Time Protection structure */
+#undef CONFIG_OTP
+
/* Support PECI interface to x86 processor */
#undef CONFIG_PECI
@@ -1515,6 +1916,19 @@
*/
#undef CONFIG_PECI_TJMAX
+/* Support physical presence detection (via a physical button) */
+#undef CONFIG_PHYSICAL_PRESENCE
+
+/* Enable (unsafe!) developer debug features for physical presence */
+#undef CONFIG_PHYSICAL_PRESENCE_DEBUG_UNSAFE
+
+/*****************************************************************************/
+/* PinWeaver config
+ * A feature which exchanges a low entropy secret with rate limits for a high
+ * entropy secret. This enables a set of vendor specific commands for Cr50.
+ */
+#undef CONFIG_PINWEAVER
+
/*****************************************************************************/
/* PMU config */
@@ -1526,19 +1940,6 @@
*/
#undef CONFIG_PMU_HARD_RESET
-/* Support TPS65090 PMU */
-#undef CONFIG_PMU_TPS65090
-
-/* Suport TPS65090 PMU charging LED. */
-#undef CONFIG_PMU_TPS65090_CHARGING_LED
-
-/*
- * Support PMU powerinfo host and console commands. Note that the
- * implementation is currently specific to the Pit board, so don't blindly
- * enable this for another board without fixing that first.
- */
-#undef CONFIG_PMU_POWERINFO
-
/*
* Enable this config to make console UART self sufficient (no other
* initialization required before uart_init(), no interrupts, uart_tx_char()
@@ -1549,6 +1950,15 @@
*/
#undef CONFIG_POLLING_UART
+/* Define length of history buffer for port80 messages. */
+#define CONFIG_PORT80_HISTORY_LEN 128
+
+/*
+ * Enable/Disable printing of port80 messages in interrupt context. By default,
+ * this is disabled.
+ */
+#define CONFIG_PORT80_PRINT_IN_INT 0
+
/* Compile common code to support power button debouncing */
#undef CONFIG_POWER_BUTTON
@@ -1561,12 +1971,32 @@
/* Support sending the power button signal to x86 chipsets */
#undef CONFIG_POWER_BUTTON_X86
+/* Set power button state idle at init. Implemented only for npcx. */
+#undef CONFIG_POWER_BUTTON_INIT_IDLE
+
+/*
+ * Enable delay between DSW_PWROK and PWRBTN assertion.
+ * If enabled, DSW_PWROK_TO_PWRBTN_US and get_time_dsw_pwrok must be defined
+ * as well.
+ */
+#undef CONFIG_DELAY_DSW_PWROK_TO_PWRBTN
+
+/*
+ * The time in usec required for PMC to be ready to detect power button press.
+ * Refer to the timing diagram for G3 to S0 on PDG for details.
+ */
+#define CONFIG_DSW_PWROK_TO_PWRBTN_US (95 * MSEC)
+
+
/* Compile common code for AP power state machine */
#undef CONFIG_POWER_COMMON
/* Disable the power-on transition when the lid is opened */
#undef CONFIG_POWER_IGNORE_LID_OPEN
+/* Enable a task-safe way to control the PP5000 rail. */
+#undef CONFIG_POWER_PP5000_CONTROL
+
/* Support stopping in S5 on shutdown */
#undef CONFIG_POWER_SHUTDOWN_PAUSE_IN_S5
@@ -1606,7 +2036,14 @@
/* Base address of RAM for the chip */
#undef CONFIG_RAM_BASE
-/* Size of RAM available on the chip, in bytes */
+/*
+ * CONFIG_DATA_RAM_SIZE and CONFIG_RAM_SIZE indicate size of all data RAM
+ * available on the chip in bytes and size of data RAM available for EC in
+ * bytes, respectively.
+ * Usually, CONFIG_DATA_RAM_SIZE = CONFIG_RAM_SIZE but some chips need to
+ * allocate RAM for the mask ROM. Then CONFIG_DATA_RAM_SIZE > CONFIG_RAM_SIZE.
+ */
+#undef CONFIG_DATA_RAM_SIZE
#undef CONFIG_RAM_SIZE
/* Enable rbox peripheral */
@@ -1618,17 +2055,57 @@
/* Support IR357x Link voltage regulator debugging / reprogramming */
#undef CONFIG_REGULATOR_IR357X
+/* Support RMA auth challenge-response */
+#undef CONFIG_RMA_AUTH
+/* If that's defined, the server public key and ID must also be defined */
+#undef CONFIG_RMA_AUTH_SERVER_PUBLIC_KEY /* 32 bytes: {0xNN, 0xNN, ... 0xNN} */
+#undef CONFIG_RMA_AUTH_SERVER_KEY_ID /* 6-bit key ID, 0xMM */
+
+/* Enable hardware Random Number generator support */
+#undef CONFIG_RNG
+
/* Support verifying 2048-bit RSA signature */
#undef CONFIG_RSA
/* Define the RSA key size. */
#undef CONFIG_RSA_KEY_SIZE
+/* Use RSA exponent 3 instead of F4 (65537) */
+#undef CONFIG_RSA_EXPONENT_3
+
+/*
+ * Adjust the compiler optimization flags for the RSA code to get a speed-up
+ * at the expense of a small code size delta.
+ */
+#undef CONFIG_RSA_OPTIMIZED
+
/*
* Verify the RW firmware using the RSA signature.
* (for accessories without software sync)
*/
#undef CONFIG_RWSIG
+
+/*
+ * Disable rwsig jump when the reset source is hard pin-reset. This only work
+ * for the case where rwsig task is not used.
+ */
+#undef CONFIG_RWSIG_DONT_CHECK_ON_PIN_RESET
+
+/*
+ * When RWSIG verification is performed as a task, time to wait from signature
+ * verification to an automatic jump to RW (if AP does not request the wait to
+ * be interrupted).
+ */
+#define CONFIG_RWSIG_JUMP_TIMEOUT (1000 * MSEC)
+
+/*
+ * Defines what type of futility signature type should be used.
+ * RWSIG should be used for new designs.
+ * Old adapters use the USBPD1 futility signature type.
+ */
+#undef CONFIG_RWSIG_TYPE_RWSIG
+#undef CONFIG_RWSIG_TYPE_USBPD1
+
/*
* By default the pubkey and sig are put at the end of the first and second
* half of the total flash, and take up the minimum space possible. You can
@@ -1639,6 +2116,9 @@
#undef CONFIG_RW_SIG_ADDR
#undef CONFIG_RW_SIG_SIZE
+/* Size of the serial number if needed */
+#undef CONFIG_SERIALNO_LEN
+
/****************************************************************************/
/* Shared objects library. */
@@ -1672,6 +2152,9 @@
/* Support computing of other hash sizes (without the VBOOT code) */
#undef CONFIG_SHA256
+/* Unroll some loops in SHA256_transform for better performance. */
+#undef CONFIG_SHA256_UNROLLED
+
/* Emulate the CLZ (Count Leading Zeros) in software for CPU lacking support */
#undef CONFIG_SOFTWARE_CLZ
@@ -1698,27 +2181,26 @@
/* Support SPI flash */
#undef CONFIG_SPI_FLASH
+/* Support SPI flash protection register translation */
+#undef CONFIG_SPI_FLASH_REGS
+
/* Define the SPI port to use to access the flash */
#undef CONFIG_SPI_FLASH_PORT
-/* Support W25Q40 SPI flash */
+/* Select any of the following SPI flash configs that your board uses. */
+#undef CONFIG_SPI_FLASH_GD25LQ40
+#undef CONFIG_SPI_FLASH_GD25Q41B
#undef CONFIG_SPI_FLASH_W25Q40
-
-/* Support W25Q64 SPI flash */
#undef CONFIG_SPI_FLASH_W25Q64
-
-/* Support W25X40 SPI flash */
+#undef CONFIG_SPI_FLASH_W25Q80
#undef CONFIG_SPI_FLASH_W25X40
-/* Support GD25Q40 SPI flash */
-#undef CONFIG_SPI_FLASH_GD25LQ40
-
-/* Support GD25Q41B SPI flash */
-#undef CONFIG_SPI_FLASH_GD25Q41B
-
/* SPI flash part supports SR2 register */
#undef CONFIG_SPI_FLASH_HAS_SR2
+/* Define the SPI port to use to access the fingerprint sensor */
+#undef CONFIG_SPI_FP_PORT
+
/* Support JEDEC SFDP based Serial NOR flash */
#undef CONFIG_SPI_NOR
@@ -1736,9 +2218,27 @@
* two. */
#undef CONFIG_SPI_NOR_MAX_WRITE_SIZE
+/* If defined will enable block (64KiB) erase operations. */
+#undef CONFIG_SPI_NOR_BLOCK_ERASE
+
+/* If defined will read the sector/block to be erased first and only initiate
+ * the erase operation if not already in an erased state. The read operation
+ * (performed in CONFIG_SPI_NOR_MAX_READ_SIZE chunks) is aborted early if a
+ * non "0xff" byte is encountered.
+ * !! Make sure there is enough stack space to host a
+ * !! CONFIG_SPI_NOR_MAX_READ_SIZE sized buffer before enabling.
+ */
+#undef CONFIG_SPI_NOR_SMART_ERASE
+
/* SPI master feature */
#undef CONFIG_SPI_MASTER
+/* SPI master halfduplex/3-wire mode */
+#undef CONFIG_SPI_HALFDUPLEX
+
+/* Support STM32 SPI1 as master. */
+#undef CONFIG_STM32_SPI1_MASTER
+
/* SPI master configure gpios on init */
#undef CONFIG_SPI_MASTER_CONFIGURE_GPIOS
@@ -1779,9 +2279,14 @@
#undef CONFIG_SYSTEM_UNLOCKED
/*
+ * Device can be a tablet as well as a clamshell.
+ */
+#undef CONFIG_TABLET_MODE
+
+/*
* Add a virtual switch to indicate when we are in tablet mode.
*/
-#define CONFIG_TABLET_MODE_SWITCH
+#undef CONFIG_TABLET_MODE_SWITCH
/*****************************************************************************/
/* Task config */
@@ -1859,12 +2364,40 @@
#undef CONFIG_DPTF
/*****************************************************************************/
+/* Touchpad config */
+
+/* Enable touchpad, you must pick a driver (currently, only Elan exists) */
+#undef CONFIG_TOUCHPAD
+
+/* Enable Elan driver */
+#undef CONFIG_TOUCHPAD_ELAN
+
+/* Set I2C port and address (8-bit) */
+#undef CONFIG_TOUCHPAD_I2C_PORT
+#undef CONFIG_TOUCHPAD_I2C_ADDR
+
+/*
+ * Enable touchpad FW update over USB update protocol, and define touchpad
+ * virtual address and size.
+ */
+#undef CONFIG_TOUCHPAD_VIRTUAL_OFF
+#undef CONFIG_TOUCHPAD_VIRTUAL_SIZE
+
+/*
+ * Include hashes of the touchpad FW in the EC image, passed as TOUCHPAD_FW
+ * parameter to make command.
+ */
+#undef CONFIG_TOUCHPAD_HASH_FW
+
+/*****************************************************************************/
/* TPM-like configuration */
/* Speak the TPM SPI Hardware Protocol on the SPI slave interface */
#undef CONFIG_TPM_SPS
/* Speak to the TPM 2.0 hardware protocol on the I2C slave interface */
#undef CONFIG_TPM_I2CS
+/* Record TPM events in circular buffer */
+#undef CONFIG_TPM_LOGGING
/*****************************************************************************/
/* USART stream config */
@@ -1880,6 +2413,10 @@
#undef CONFIG_STREAM_USART4
/*****************************************************************************/
+/* U2F config: second factor authentication */
+#undef CONFIG_U2F
+
+/*****************************************************************************/
/* USB stream config */
#undef CONFIG_STREAM_USB
@@ -1889,6 +2426,9 @@
/* Baud rate for UARTs */
#define CONFIG_UART_BAUD_RATE 115200
+/* Allow bit banging of a UARTs pins and bypassing the UART block. */
+#undef CONFIG_UART_BITBANG
+
/* UART index (number) for EC console */
#undef CONFIG_UART_CONSOLE
@@ -1899,6 +2439,13 @@
#undef CONFIG_UART_INPUT_FILTER
/*
+ * Allow switching the EC console UART to an alternate pad. This must be
+ * used for short transactions only, and EC is only able to receive data on
+ * that alternate pad after it has been explicitly switched.
+ */
+#undef CONFIG_UART_PAD_SWITCH
+
+/*
* UART receive buffer size in bytes. Must be a power of 2 for macros in
* common/uart_buffering.c to work properly. Must be larger than
* CONFIG_CONSOLE_INPUT_LINE_SIZE to copy and paste scripts.
@@ -1952,8 +2499,8 @@
/* Check if max voltage request is allowed before each request */
#undef CONFIG_USB_PD_CHECK_MAX_REQUEST_ALLOWED
-/* Default state of PD communication enabled flag */
-#define CONFIG_USB_PD_COMM_ENABLED
+/* Default state of PD communication disabled flag */
+#undef CONFIG_USB_PD_COMM_DISABLED
/*
* Do not enable PD communication in RO as a security measure.
@@ -1970,6 +2517,12 @@
#define CONFIG_USB_PD_DEBUG_DR PD_ROLE_DFP
/*
+ * Define to have a fixed PD Task debug level.
+ * Undef to allow runtime change via console command.
+ */
+#undef CONFIG_USB_PD_DEBUG_LEVEL
+
+/*
* Define if this board can enable VBUS discharge (eg. through a GPIO-controlled
* discharge circuit, or through port controller registers) to discharge VBUS
* rapidly on disconnect.
@@ -1991,6 +2544,12 @@
/* Define if this board can act as a dual-role PD port (source and sink) */
#undef CONFIG_USB_PD_DUAL_ROLE
+/* Define if this board can used TCPC-controlled DRP toggle */
+#undef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
+
+/* Initial DRP / toggle policy */
+#define CONFIG_USB_PD_INITIAL_DRP_STATE PD_DRP_TOGGLE_OFF
+
/*
* Define if VBUS source GPIOs (GPIO_USB_C*_5V_EN) are active-low (and named
* (..._L) rather than default active-high.
@@ -2006,6 +2565,12 @@
/* Check whether PD is the sole power source before flash erase operation */
#undef CONFIG_USB_PD_FLASH_ERASE_CHECK
+/* Define if this board, operating as a sink, can give power back to a source */
+#undef CONFIG_USB_PD_GIVE_BACK
+
+/* Enable USB PD Rev3.0 features */
+#undef CONFIG_USB_PD_REV30
+
/* Major and Minor ChromeOS specific PD device Hardware IDs. */
#undef CONFIG_USB_PD_HW_DEV_ID_BOARD_MAJOR
#undef CONFIG_USB_PD_HW_DEV_ID_BOARD_MINOR
@@ -2023,8 +2588,8 @@
/* Record main PD events in a circular buffer */
#undef CONFIG_USB_PD_LOGGING
-/* The size in bytes of the FIFO used for PD events logging */
-#undef CONFIG_USB_PD_LOG_SIZE
+/* The size in bytes of the FIFO used for event logging */
+#define CONFIG_EVENT_LOG_SIZE 512
/* Save power by waking up on VBUS rather than polling CC */
#define CONFIG_USB_PD_LOW_POWER
@@ -2035,9 +2600,6 @@
/* Number of USB PD ports */
#undef CONFIG_USB_PD_PORT_COUNT
-/* Workaround TCPC that takes longer time to update CC status */
-#undef CONFIG_USB_PD_QUIRK_SLOW_CC_STATUS
-
/* Simple DFP, such as power adapter, will not send discovery VDM on connect */
#undef CONFIG_USB_PD_SIMPLE_DFP
@@ -2047,8 +2609,11 @@
/* Use TCPC module (type-C port controller) */
#undef CONFIG_USB_PD_TCPC
-/* Get TCPC firmware version */
-#undef CONFIG_USB_PD_TCPC_FW_VERSION
+/* Board provides specific TCPC init function */
+#undef CONFIG_USB_PD_TCPC_BOARD_INIT
+
+/* Enable TCPC to enter low power mode */
+#undef CONFIG_USB_PD_TCPC_LOW_POWER
/*
* Track VBUS level in TCPC module. This will only be needed if we're acting
@@ -2068,6 +2633,7 @@
#undef CONFIG_USB_PD_TCPM_ANX74XX
#undef CONFIG_USB_PD_TCPM_ANX7688
#undef CONFIG_USB_PD_TCPM_PS8751
+#undef CONFIG_USB_PD_TCPM_PS8805
/*
* Use this option if the TCPC port controller supports the optional register
@@ -2111,6 +2677,9 @@
/* USB Product ID. */
#undef CONFIG_USB_PID
+/* USB Type-C Power Path Controllers (PPC) */
+#undef CONFIG_USBC_PPC_SN5S330
+
/* Support for USB type-c superspeed mux */
#undef CONFIG_USBC_SS_MUX
@@ -2120,14 +2689,6 @@
*/
#undef CONFIG_USBC_SS_MUX_DFP_ONLY
-/* Sniffer header version
- * Version 1: [timestamp:2B, sequence number:2B]
- * Version 2: [timestamp:2B, sequence number:2B,
- * Vbus value: 2B, vbus timestamp offset: 2B]
- */
-#undef CONFIG_USBC_SNIFFER_HEADER_V1
-#undef CONFIG_USBC_SNIFFER_HEADER_V2
-
/* Support v1.1 type-C connection state machine */
#undef CONFIG_USBC_BACKWARDS_COMPATIBLE_DFP
@@ -2143,6 +2704,19 @@
/* USB Device version of product */
#undef CONFIG_USB_BCD_DEV
+/*
+ * Used during generation of VIF for USB Type-C Compliance Testing.
+ * Indicates whether the UUT can communicate with USB 2.0 or USB 3.1 as a host
+ * or as the Downstream Facing Port of a hub.
+ */
+#undef CONFIG_VIF_TYPE_C_CAN_ACT_AS_HOST
+
+/*
+ * Used during generation of VIF for USB Type-C Compliance Testing.
+ * Indicates whether the UUT has a captive cable.
+ */
+#undef CONFIG_VIF_CAPTIVE_CABLE
+
/*****************************************************************************/
/* Compile chip support for the USB device controller */
@@ -2154,12 +2728,35 @@
/* Common USB / BC1.2 charger detection routines */
#undef CONFIG_USB_CHARGER
+/* External BC1.2 charger detection devices. */
+#undef CONFIG_BC12_DETECT_BQ24392
+#undef CONFIG_BC12_DETECT_PI3USB9281
+/* Number of Pericom PI3USB9281 chips present in system */
+#undef CONFIG_BC12_DETECT_PI3USB9281_CHIP_COUNT
+
+
/* Enable USB serial console module. */
#undef CONFIG_USB_CONSOLE
/* Support USB HID interface. */
#undef CONFIG_USB_HID
+/* Support USB HID keyboard interface. */
+#undef CONFIG_USB_HID_KEYBOARD
+
+/* Support USB HID keyboard backlight. */
+#undef CONFIG_USB_HID_KEYBOARD_BACKLIGHT
+
+/* Support USB HID touchpad interface. */
+#undef CONFIG_USB_HID_TOUCHPAD
+
+/* HID touchpad logical dimensions */
+#undef CONFIG_USB_HID_TOUCHPAD_LOGICAL_MAX_X
+#undef CONFIG_USB_HID_TOUCHPAD_LOGICAL_MAX_Y
+/* HID touchpad physical dimensions (tenth of mm) */
+#undef CONFIG_USB_HID_TOUCHPAD_PHYSICAL_MAX_X
+#undef CONFIG_USB_HID_TOUCHPAD_PHYSICAL_MAX_Y
+
/* USB device buffers and descriptors */
#undef CONFIG_USB_RAM_ACCESS_SIZE
#undef CONFIG_USB_RAM_ACCESS_TYPE
@@ -2174,6 +2771,8 @@
/* Support control of multiple PHY */
#undef CONFIG_USB_SELECT_PHY
+/* Select which USB PHY will be used at startup */
+#undef CONFIG_USB_SELECT_PHY_DEFAULT
/* Support simple control of power to the device's USB ports */
#undef CONFIG_USB_PORT_POWER_DUMB
@@ -2192,6 +2791,13 @@
#undef CONFIG_USB_PORT_POWER_SMART
/*
+ * Support smart power control to the device's USB ports, however only CDP and
+ * SDP are supported. Usually this is the case if all the control lines to the
+ * charging port controller are hard-wired.
+ */
+#undef CONFIG_USB_PORT_POWER_SMART_CDP_SDP_ONLY
+
+/*
* Override the default charging mode for USB smart power control.
* Value is selected from usb_charge_mode in include/usb_charge.h
*/
@@ -2212,6 +2818,12 @@
*/
#undef CONFIG_USB_PORT_POWER_SMART_INVERTED
+/*
+ * Support waking up host by setting the K-state on the data lines (requires
+ * CONFIG_USB_SUSPEND to be set as well).
+ */
+#undef CONFIG_USB_REMOTE_WAKEUP
+
/* Support programmable USB device iSerial field. */
#undef CONFIG_USB_SERIALNO
@@ -2221,6 +2833,9 @@
/* Support reporting as self powered in USB configuration. */
#undef CONFIG_USB_SELF_POWERED
+/* Support correct handling of USB suspend (host-initiated). */
+#undef CONFIG_USB_SUSPEND
+
/* Default pull-up value on the USB-C ports when they are used as source. */
#define CONFIG_USB_PD_PULLUP TYPEC_RP_1A5
/*
@@ -2241,11 +2856,8 @@
/******************************************************************************/
/* USB port switch */
-/* Support the Pericom PI3USB9281 I2C USB switch */
-#undef CONFIG_USB_SWITCH_PI3USB9281
-
-/* Number of Pericom PI3USB9281 chips present in system */
-#undef CONFIG_USB_SWITCH_PI3USB9281_CHIP_COUNT
+/* Support the ITE IT5205 Type-C USB alternate mode mux. */
+#undef CONFIG_USB_MUX_IT5205
/* Support the Pericom PI3USB30532 USB3.0/DP1.2 Matrix Switch */
#undef CONFIG_USB_MUX_PI3USB30532
@@ -2253,6 +2865,9 @@
/* Support the Parade PS8740 Type-C Redriving Switch */
#undef CONFIG_USB_MUX_PS8740
+/* Support the Parade PS8743 Type-C Redriving Switch */
+#undef CONFIG_USB_MUX_PS8743
+
/* 'Virtual' USB mux under host (not EC) control */
#undef CONFIG_USB_MUX_VIRTUAL
@@ -2268,7 +2883,26 @@
/* USB I2C config */
#undef CONFIG_USB_I2C
+/* Allowed write count for USB over I2C */
+#define CONFIG_USB_I2C_MAX_WRITE_COUNT 60
+
+/*****************************************************************************/
+/* USB Power monitoring interface config */
+#undef CONFIG_USB_POWER
+
/*****************************************************************************/
+/*
+ * USB stream signing config. This allows data read over UART or SPI
+ * to have a signature generated that can be used to validate the data
+ * offline based on H1's registered key. Used by mn50.
+ */
+#undef CONFIG_STREAM_SIGNATURE
+
+
+/*****************************************************************************/
+
+/* Support early firmware selection */
+#undef CONFIG_VBOOT_EFS
/* Support computing hash of code for verified boot */
#undef CONFIG_VBOOT_HASH
@@ -2306,6 +2940,18 @@
#define CONFIG_AUX_TIMER_PERIOD_MS (CONFIG_WATCHDOG_PERIOD_MS - 500)
/*****************************************************************************/
+/* WebUSB config */
+
+/*
+ * Enable the WebUSB support and define its URL.
+ * Export a WebUSB Platform Descriptor in the Binary Object Store descriptor.
+ * The WebUSB landing page URL is equal to 'CONFIG_WEBUSB_URL' plus the
+ * https:// prefix.
+ * This requires CONFIG_USB_BOS.
+ */
+#undef CONFIG_WEBUSB_URL
+
+/*****************************************************************************/
/*
* Support controlling power to WiFi, WWAN (3G/LTE), and/or bluetooth modules.
@@ -2348,6 +2994,30 @@
/* A different config for the same update. TODO(vbendeb): dedup these */
#undef CONFIG_USB_UPDATE
+/* Add support for pairing over the USB update interface. */
+#undef CONFIG_USB_PAIRING
+
+/* PDU size for fw update over USB (or TPM). */
+#define CONFIG_UPDATE_PDU_SIZE 1024
+
+/*
+ * If defined, charge_get_state returns a special status if battery is
+ * discharging and battery is nearly full.
+ */
+#undef CONFIG_PWR_STATE_DISCHARGE_FULL
+
+/*
+ * Define this if a chip needs to add some information to the common 'version'
+ * command output.
+ */
+#undef CONFIG_EXTENDED_VERSION_INFO
+
+/*
+ * Define this if board ID support is required. For g chip based boards it
+ * allows to nail different images to different boards.
+ */
+#undef CONFIG_BOARD_ID_SUPPORT
+
/*****************************************************************************/
/*
* Include board and core configs, since those hold the CONFIG_ constants for a
@@ -2370,6 +3040,14 @@
/******************************************************************************/
/*
+ * Set default data ram size unless it's customized by the chip.
+ */
+#ifndef CONFIG_DATA_RAM_SIZE
+#define CONFIG_DATA_RAM_SIZE CONFIG_RAM_SIZE
+#endif
+
+/******************************************************************************/
+/*
* Disable the built-in console history if using the experimental console.
*
* The experimental console keeps its own session-persistent history which
@@ -2405,6 +3083,26 @@
#define CONFIG_MKBP_EVENT
#endif
+/******************************************************************************/
+/* Set generic orientation config if a specific orientation config is set. */
+#if defined(CONFIG_KX022_ORIENTATION_SENSOR) || \
+ defined(CONFIG_BMI160_ORIENTATION_SENSOR)
+#ifndef CONFIG_ACCEL_FIFO
+#error CONFIG_ACCEL_FIFO must be defined to use hw orientation sensor support
+#endif
+#define CONFIG_ORIENTATION_SENSOR
+#endif
+
+/*****************************************************************************/
+/* Define CONFIG_BATTERY if board has a battery. */
+#if defined(CONFIG_BATTERY_BQ20Z453) || \
+ defined(CONFIG_BATTERY_BQ27541) || \
+ defined(CONFIG_BATTERY_BQ27621) || \
+ defined(CONFIG_BATTERY_MAX17055) || \
+ defined(CONFIG_BATTERY_SMART)
+#define CONFIG_BATTERY
+#endif
+
/*****************************************************************************/
/*
* Handle task-dependent configs.
@@ -2415,15 +3113,13 @@
#ifndef HAS_TASK_CHIPSET
#undef CONFIG_CHIPSET_APOLLOLAKE
-#undef CONFIG_CHIPSET_BAYTRAIL
#undef CONFIG_CHIPSET_BRASWELL
-#undef CONFIG_CHIPSET_GAIA
-#undef CONFIG_CHIPSET_HASWELL
+#undef CONFIG_CHIPSET_CANNONLAKE
#undef CONFIG_CHIPSET_MEDIATEK
#undef CONFIG_CHIPSET_RK3399
#undef CONFIG_CHIPSET_ROCKCHIP
#undef CONFIG_CHIPSET_SKYLAKE
-#undef CONFIG_CHIPSET_TEGRA
+#undef CONFIG_CHIPSET_STONEY
#undef CONFIG_POWER_COMMON
#undef CONFIG_POWER_TRACK_HOST_SLEEP_STATE
#endif
@@ -2436,11 +3132,6 @@
*/
#endif
-#ifndef HAS_TASK_KEYSCAN
-#undef CONFIG_KEYBOARD_PROTOCOL_8042
-#undef CONFIG_KEYBOARD_PROTOCOL_MKBP
-#endif
-
#ifndef HAS_TASK_PDCMD
#undef CONFIG_HOSTCMD_PD
#endif
@@ -2460,4 +3151,8 @@
#error "CONFIG_AUX_TIMER_PERIOD_MS must be at least 2x HOOK_TICK_INTERVAL_MS"
#endif
+#ifdef CONFIG_USB_SERIALNO
+#define CONFIG_SERIALNO_LEN 28
+#endif
+
#endif /* __CROS_EC_CONFIG_H */
diff --git a/include/console.h b/include/console.h
index 3f8935a525..d8da234a24 100644
--- a/include/console.h
+++ b/include/console.h
@@ -41,6 +41,11 @@ struct console_command {
* CMD_FLAG_RESTRICTED commands are disabled.
*/
int console_is_restricted(void);
+#else
+static inline int console_is_restricted(void)
+{
+ return 0;
+}
#endif
/* Console channels */
diff --git a/include/console_channel.inc b/include/console_channel.inc
index ba48a31563..31ea119c1f 100644
--- a/include/console_channel.inc
+++ b/include/console_channel.inc
@@ -19,6 +19,9 @@ CONSOLE_CHANNEL(CC_BLUETOOTH_HCI,"bluetooth_hci")
#ifdef CONFIG_EXTENSION_COMMAND
CONSOLE_CHANNEL(CC_EXTENSION, "extension")
#endif
+#if defined(CONFIG_PHYSICAL_PRESENCE)
+CONSOLE_CHANNEL(CC_CCD, "ccd")
+#endif
CONSOLE_CHANNEL(CC_CHARGER, "charger")
CONSOLE_CHANNEL(CC_CHIPSET, "chipset")
CONSOLE_CHANNEL(CC_CLOCK, "clock")
@@ -26,6 +29,9 @@ CONSOLE_CHANNEL(CC_CLOCK, "clock")
CONSOLE_CHANNEL(CC_DMA, "dma")
#endif
CONSOLE_CHANNEL(CC_EVENTS, "events")
+#ifdef HAS_TASK_FPC1140
+CONSOLE_CHANNEL(CC_FP, "fingerprint")
+#endif
#ifdef CONFIG_GESTURE_SW_DETECTION
CONSOLE_CHANNEL(CC_GESTURE, "gesture")
#endif
@@ -66,6 +72,9 @@ CONSOLE_CHANNEL(CC_SWITCH, "switch")
#endif
CONSOLE_CHANNEL(CC_SYSTEM, "system")
CONSOLE_CHANNEL(CC_TASK, "task")
+#ifdef CONFIG_TOUCHPAD_ELAN
+CONSOLE_CHANNEL(CC_TOUCHPAD, "touchpad")
+#endif
#ifdef CONFIG_DPTF
CONSOLE_CHANNEL(CC_DPTF, "dptf")
#endif
diff --git a/include/crypto_api.h b/include/crypto_api.h
new file mode 100644
index 0000000000..2628e2bd7b
--- /dev/null
+++ b/include/crypto_api.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __INCLUDE_CRYPTO_API_H
+#define __INCLUDE_CRYPTO_API_H
+
+#include "util.h"
+
+/**
+ * Calculate hash of an arbitrary data
+ *
+ * Up to SHA_DIGEST_SIZE byte hash can be generated, if hash_len is
+ * longer - it is padded with zeros.
+ *
+ * @param p_buf: pointer to beginning of data
+ * @param num_bytes: length of data in bytes
+ * @param p_hash: pointer to where computed hash will be stored
+ * @param hash_len: length in bytes to use from sha computation. If this
+ * value exceeds SHA1 size (20 bytes), the rest of the
+ * hash is filled up with zeros.
+ */
+void app_compute_hash(uint8_t *p_buf, size_t num_bytes,
+ uint8_t *p_hash, size_t hash_len);
+
+#define CIPHER_SALT_SIZE 16
+
+/*
+ * Encrypt/decrypt a flat blob.
+ *
+ * Encrypt or decrypt the input buffer, and write the correspondingly
+ * ciphered output to out. The number of bytes produced is equal to
+ * the number of input bytes.
+ *
+ * This API is expected to be applied to a single contiguous region. WARNING:
+ * Presently calling this function more than once with "in" pointing to
+ * logically different buffers will result in using the same IV value
+ * internally and as such reduce encryption efficiency.
+ *
+ * @param salt pointer to a unique value to be associated with this blob,
+ * used for derivation of the proper IV, the size of this value
+ * is as defined by CIPHER_SALT_SIZE above.
+ * WARNING: a given salt/"in" pair must be unique (it is an ERROR
+ * to use a given salt with more than one unique buffer). For an
+ * example, a good salt would be a digest of the plaintext input.
+ * @param out Destination pointer where to write plaintext / ciphertext.
+ * @param in Source pointer where to read ciphertext / plaintext.
+ * @param len Number of bytes to read from in / write to out.
+ * @return non-zero on success, and zero otherwise.
+ */
+int app_cipher(const void *salt, void *out, const void *in, size_t size);
+
+#endif /* __INCLUDE_CRYPTO_API_H */
diff --git a/include/curve25519.h b/include/curve25519.h
new file mode 100644
index 0000000000..8287c94466
--- /dev/null
+++ b/include/curve25519.h
@@ -0,0 +1,70 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __CROS_EC_CURVE25519_H
+#define __CROS_EC_CURVE25519_H
+
+#include <stdint.h>
+
+/* Curve25519.
+ *
+ * Curve25519 is an elliptic curve. See https://tools.ietf.org/html/rfc7748.
+ */
+
+
+/* X25519.
+ *
+ * X25519 is the Diffie-Hellman primitive built from curve25519. It is
+ * sometimes referred to as “curve25519”, but “X25519” is a more precise
+ * name.
+ * See http://cr.yp.to/ecdh.html and https://tools.ietf.org/html/rfc7748.
+ */
+
+#define X25519_PRIVATE_KEY_LEN 32
+#define X25519_PUBLIC_VALUE_LEN 32
+
+/**
+ * Generate a public/private key pair.
+ * @param out_public_value generated public key.
+ * @param out_private_value generated private key.
+ */
+void X25519_keypair(uint8_t out_public_value[32], uint8_t out_private_key[32]);
+
+/**
+ * Diffie-Hellman function.
+ * @param out_shared_key
+ * @param private_key
+ * @param out_public_value
+ * @return one on success and zero on error.
+ *
+ * X25519() writes a shared key to @out_shared_key that is calculated from the
+ * given private key and the peer's public value.
+ *
+ * Don't use the shared key directly, rather use a KDF and also include the two
+ * public values as inputs.
+ */
+int X25519(uint8_t out_shared_key[32], const uint8_t private_key[32],
+ const uint8_t peers_public_value[32]);
+
+/**
+ * Compute the matching public key.
+ * @param out_public_value computed public key.
+ * @param private_key private key to use.
+ *
+ * X25519_public_from_private() calculates a Diffie-Hellman public value from
+ * the given private key and writes it to @out_public_value.
+ */
+void X25519_public_from_private(uint8_t out_public_value[32],
+ const uint8_t private_key[32]);
+
+/*
+ * Low-level x25519 function, defined by either the generic or cortex-m0
+ * implementation. Must not be called directly.
+ */
+void x25519_scalar_mult(uint8_t out[32],
+ const uint8_t scalar[32],
+ const uint8_t point[32]);
+
+#endif /* __CROS_EC_CURVE25519_H */
diff --git a/include/device_event.h b/include/device_event.h
new file mode 100644
index 0000000000..40d4114b16
--- /dev/null
+++ b/include/device_event.h
@@ -0,0 +1,44 @@
+/* Copyright (c) 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Device event module for Chrome EC */
+
+#ifndef __CROS_EC_DEVICE_EVENT_H
+#define __CROS_EC_DEVICE_EVENT_H
+
+#include "common.h"
+#include "ec_commands.h"
+
+/**
+ * Return the raw device event state.
+ */
+uint32_t device_get_events(void);
+
+/**
+ * Set one or more device event bits.
+ *
+ * @param mask Event bits to set (use EC_DEVICE_EVENT_MASK()).
+ */
+void device_set_events(uint32_t mask);
+
+/**
+ * Clear one or more device event bits.
+ *
+ * @param mask Event bits to clear (use EC_DEVICE_EVENT_MASK()).
+ * Write 1 to a bit to clear it.
+ */
+void device_clear_events(uint32_t mask);
+
+/**
+ * Set a single device event.
+ *
+ * @param event Event to set (EC_DEVICE_EVENT_*).
+ */
+static inline void device_set_single_event(int event)
+{
+ device_set_events(EC_DEVICE_EVENT_MASK(event));
+}
+
+#endif /* __CROS_EC_DEVICE_EVENT_H */
diff --git a/include/device_state.h b/include/device_state.h
index d7ff0661dc..e7894ba998 100644
--- a/include/device_state.h
+++ b/include/device_state.h
@@ -3,50 +3,85 @@
* found in the LICENSE file.
*/
-#include "gpio.h"
-
#ifndef __CROS_DEVICE_STATE_H
#define __CROS_DEVICE_STATE_H
-/* Device state indexes */
-enum device_state {
- DEVICE_STATE_UNKNOWN = 0,
- DEVICE_STATE_OFF,
- DEVICE_STATE_ON,
- DEVICE_STATE_COUNT,
-};
+enum gpio_signal;
+/* Device configuration */
struct device_config {
- const char *name; /* Device name */
- enum device_state state; /* Device status */
- /* Deferred handler to detect power off */
+ /* Device name */
+ const char *name;
+
+ /* Current state */
+ enum device_state state;
+
+ /*
+ * Last known state. That is, the last state value passed to
+ * device_set_state() which was DEVICE_STATE_OFF or DEVICE_STATE_ON.
+ * Or DEVICE_STATE_UNKNOWN, if device_set_state() has not been called
+ * for this device this boot.
+ */
+ enum device_state last_known_state;
+
+ /*
+ * Deferred handler to debounce state transitions. This is NOT used by
+ * the device_state module; it's just here as a convenience for the
+ * board.
+ */
const struct deferred_data *deferred;
- enum gpio_signal detect_on; /* GPIO detecting power on */
- enum gpio_signal detect_off; /* GPIO detecting power off */
+
+ /*
+ * GPIO used to detect the state. This is NOT used by the device_state
+ * module; it's just here as a convenience for the board.
+ */
+ enum gpio_signal detect;
};
+/*
+ * board.h must supply an enumerated list of devices, ending in DEVICE_COUNT.
+ */
enum device_type;
+/*
+ * board.c must provide this list of device configurations. It must match enum
+ * device_type, and must be DEVICE_COUNT entries long.
+ */
extern struct device_config device_states[];
-/* Return the device state */
-int device_get_state(enum device_type device);
-
/**
- * Sets the device state
+ * Get the current state for the device.
*
- * @param device the device to update
- * @param state the new device state
+ * @param device Device to check
+ * @return The device state (current; NOT last known).
*/
-void device_set_state(enum device_type device, enum device_state state);
+enum device_state device_get_state(enum device_type device);
-/* Update the device state based on the device gpios */
-void board_update_device_state(enum device_type device);
+/**
+ * Set the device state
+ *
+ * Updates the device's last known state if <state> is DEVICE_STATE_ON or
+ * DEVICE_STATE_OFF, and that's different than the device's last known state.
+ *
+ * Note that this only changes the recorded state. It does not notify anything
+ * of these changes. That must be done by the caller.
+ *
+ * @param device Device to update
+ * @param state New device state
+ * @return non-zero if this changed the device's last known state.
+ */
+int device_set_state(enum device_type device, enum device_state state);
/**
- * Enables or disables all device gpio interrupts
+ * Update the device state based on the device gpios.
+ *
+ * The board must implement this. It will be called for each device in the
+ * context of HOOK_SECOND. If the state has changed, the board is responsible
+ * for doing any associated reconfiguration and then calling
+ * device_set_state().
*
- * @param enable enable or disable detection
+ * @param device Device to check.
*/
-void device_detect_state_enable(int enable);
+void board_update_device_state(enum device_type device);
+
#endif /* __CROS_DEVICE_STATE_H */
diff --git a/include/ec_commands.h b/include/ec_commands.h
index 0cb61d9116..09ebc75319 100644
--- a/include/ec_commands.h
+++ b/include/ec_commands.h
@@ -8,10 +8,14 @@
#ifndef __CROS_EC_EC_COMMANDS_H
#define __CROS_EC_EC_COMMANDS_H
+#if !defined(__ACPI__) && !defined(__KERNEL__)
+#include <stdint.h>
+#endif
+
/*
- * Include common.h first for CONFIG_HOSTCMD_ALIGNED, if it's defined. This
- * generates more efficient code for accessing request/response structures
- * on ARM Cortex-M if the structures are guaranteed 32-bit aligned.
+ * Include common.h for CONFIG_HOSTCMD_ALIGNED, if it's defined. This
+ * generates more efficient code for accessing request/response structures on
+ * ARM Cortex-M if the structures are guaranteed 32-bit aligned.
*/
#ifdef CHROMIUM_EC
#include "common.h"
@@ -205,7 +209,7 @@
* - Wait for EC_LPC_CMDR_DATA bit to set
* - Read value from EC_LPC_ADDR_ACPI_DATA
*/
-#define EC_CMD_ACPI_READ 0x80
+#define EC_CMD_ACPI_READ 0x0080
/*
* ACPI Write Embedded Controller
@@ -220,7 +224,7 @@
* - Wait for EC_LPC_CMDR_PENDING bit to clear
* - Write value to EC_LPC_ADDR_ACPI_DATA
*/
-#define EC_CMD_ACPI_WRITE 0x81
+#define EC_CMD_ACPI_WRITE 0x0081
/*
* ACPI Burst Enable Embedded Controller
@@ -229,7 +233,7 @@
* commands back-to-back. While in this mode, writes to mapped multi-byte
* data are locked out to ensure data consistency.
*/
-#define EC_CMD_ACPI_BURST_ENABLE 0x82
+#define EC_CMD_ACPI_BURST_ENABLE 0x0082
/*
* ACPI Burst Disable Embedded Controller
@@ -237,7 +241,7 @@
* This disables burst mode on the EC and stops preventing EC writes to mapped
* multi-byte data.
*/
-#define EC_CMD_ACPI_BURST_DISABLE 0x83
+#define EC_CMD_ACPI_BURST_DISABLE 0x0083
/*
* ACPI Query Embedded Controller
@@ -246,7 +250,7 @@
* sets the result code to the 1-based index of the bit (event 0x00000001 = 1,
* event 0x80000000 = 32), or 0 if no event was pending.
*/
-#define EC_CMD_ACPI_QUERY_EVENT 0x84
+#define EC_CMD_ACPI_QUERY_EVENT 0x0084
/* Valid addresses in ACPI memory space, for read/write commands */
@@ -322,6 +326,27 @@
#define EC_ACPI_MEM_DEVICE_TABLET_MODE 0x01
/*
+ * Report device features. Uses the same format as the host command, except:
+ *
+ * bit 0 (EC_FEATURE_LIMITED) changes meaning from "EC code has a limited set
+ * of features", which is of limited interest when the system is already
+ * interpreting ACPI bytecode, to "EC_FEATURES[0-7] is not supported". Since
+ * these are supported, it defaults to 0.
+ * This allows detecting the presence of this field since older versions of
+ * the EC codebase would simply return 0xff to that unknown address. Check
+ * FEATURES0 != 0xff (or FEATURES0[0] == 0) to make sure that the other bits
+ * are valid.
+ */
+#define EC_ACPI_MEM_DEVICE_FEATURES0 0x0a
+#define EC_ACPI_MEM_DEVICE_FEATURES1 0x0b
+#define EC_ACPI_MEM_DEVICE_FEATURES2 0x0c
+#define EC_ACPI_MEM_DEVICE_FEATURES3 0x0d
+#define EC_ACPI_MEM_DEVICE_FEATURES4 0x0e
+#define EC_ACPI_MEM_DEVICE_FEATURES5 0x0f
+#define EC_ACPI_MEM_DEVICE_FEATURES6 0x10
+#define EC_ACPI_MEM_DEVICE_FEATURES7 0x11
+
+/*
* ACPI addresses 0x20 - 0xff map to EC_MEMMAP offset 0x00 - 0xdf. This data
* is read-only from the AP. Added in EC_ACPI_MEM_VERSION 2.
*/
@@ -458,7 +483,9 @@
#define EC_LPC_STATUS_BUSY_MASK \
(EC_LPC_STATUS_FROM_HOST | EC_LPC_STATUS_PROCESSING)
-/* Host command response codes */
+/* Host command response codes (16-bit). Note that response codes should be
+ * stored in a uint16_t rather than directly in a value of this type.
+ */
enum ec_status {
EC_RES_SUCCESS = 0,
EC_RES_INVALID_COMMAND = 1,
@@ -475,8 +502,8 @@ enum ec_status {
EC_RES_INVALID_HEADER = 12, /* Header contains invalid data */
EC_RES_REQUEST_TRUNCATED = 13, /* Didn't get the entire request */
EC_RES_RESPONSE_TOO_BIG = 14, /* Response was too big to handle */
- EC_RES_BUS_ERROR = 15, /* Communications bus error */
- EC_RES_BUSY = 16 /* Up but too busy. Should retry */
+ EC_RES_BUS_ERROR = 15, /* Communications bus error */
+ EC_RES_BUSY = 16 /* Up but too busy. Should retry */
};
/*
@@ -496,7 +523,8 @@ enum host_event_code {
EC_HOST_EVENT_BATTERY_CRITICAL = 7,
EC_HOST_EVENT_BATTERY = 8,
EC_HOST_EVENT_THERMAL_THRESHOLD = 9,
- EC_HOST_EVENT_THERMAL_OVERLOAD = 10,
+ /* Event generated by a device attached to the EC */
+ EC_HOST_EVENT_DEVICE = 10,
EC_HOST_EVENT_THERMAL = 11,
EC_HOST_EVENT_USB_CHARGER = 12,
EC_HOST_EVENT_KEY_PRESSED = 13,
@@ -548,6 +576,15 @@ enum host_event_code {
/* TABLET/LAPTOP mode event*/
EC_HOST_EVENT_MODE_CHANGE = 29,
+ /* Keyboard recovery combo with hardware reinitialization */
+ EC_HOST_EVENT_KEYBOARD_RECOVERY_HW_REINIT = 30,
+
+ /*
+ * Reserve this last bit to indicate that at least one bit in a
+ * secondary host event word is set. See crbug.com/633646.
+ */
+ EC_HOST_EVENT_EXTENDED = 31,
+
/*
* The high bit of the event mask is not used as a host event code. If
* it reads back as set, then the entire event mask should be
@@ -783,6 +820,9 @@ struct __ec_align4 ec_host_response {
* Parameter/response length is implicit in the structs. Some underlying
* communication protocols (I2C, SPI) may add length or checksum headers, but
* those are implementation-dependent and not defined here.
+ *
+ * All commands MUST be #defined to be 4-digit UPPER CASE hex values
+ * (e.g., 0x00AB, not 0xab) for CONFIG_HOSTCMD_SECTION_SORTED to work.
*/
/*****************************************************************************/
@@ -792,7 +832,7 @@ struct __ec_align4 ec_host_response {
* Get protocol version, used to deal with non-backward compatible protocol
* changes.
*/
-#define EC_CMD_PROTO_VERSION 0x00
+#define EC_CMD_PROTO_VERSION 0x0000
struct __ec_align4 ec_response_proto_version {
uint32_t version;
@@ -802,7 +842,7 @@ struct __ec_align4 ec_response_proto_version {
* Hello. This is a simple command to test the EC is responsive to
* commands.
*/
-#define EC_CMD_HELLO 0x01
+#define EC_CMD_HELLO 0x0001
struct __ec_align4 ec_params_hello {
uint32_t in_data; /* Pass anything here */
@@ -813,7 +853,7 @@ struct __ec_align4 ec_response_hello {
};
/* Get version number */
-#define EC_CMD_GET_VERSION 0x02
+#define EC_CMD_GET_VERSION 0x0002
enum ec_current_image {
EC_IMAGE_UNKNOWN = 0,
@@ -830,7 +870,7 @@ struct __ec_align4 ec_response_get_version {
};
/* Read test */
-#define EC_CMD_READ_TEST 0x03
+#define EC_CMD_READ_TEST 0x0003
struct __ec_align4 ec_params_read_test {
uint32_t offset; /* Starting value for read buffer */
@@ -846,10 +886,10 @@ struct __ec_align4 ec_response_read_test {
*
* Response is null-terminated string.
*/
-#define EC_CMD_GET_BUILD_INFO 0x04
+#define EC_CMD_GET_BUILD_INFO 0x0004
/* Get chip info */
-#define EC_CMD_GET_CHIP_INFO 0x05
+#define EC_CMD_GET_CHIP_INFO 0x0005
struct __ec_align4 ec_response_get_chip_info {
/* Null-terminated strings */
@@ -859,7 +899,7 @@ struct __ec_align4 ec_response_get_chip_info {
};
/* Get board HW version */
-#define EC_CMD_GET_BOARD_VERSION 0x06
+#define EC_CMD_GET_BOARD_VERSION 0x0006
struct __ec_align2 ec_response_board_version {
uint16_t board_version; /* A monotonously incrementing number. */
@@ -873,7 +913,7 @@ struct __ec_align2 ec_response_board_version {
*
* Response is params.size bytes of data.
*/
-#define EC_CMD_READ_MEMMAP 0x07
+#define EC_CMD_READ_MEMMAP 0x0007
struct __ec_align1 ec_params_read_memmap {
uint8_t offset; /* Offset in memmap (EC_MEMMAP_*) */
@@ -881,7 +921,7 @@ struct __ec_align1 ec_params_read_memmap {
};
/* Read versions supported for a command */
-#define EC_CMD_GET_CMD_VERSIONS 0x08
+#define EC_CMD_GET_CMD_VERSIONS 0x0008
struct __ec_align1 ec_params_get_cmd_versions {
uint8_t cmd; /* Command to check */
@@ -906,7 +946,7 @@ struct __ec_align4 ec_response_get_cmd_versions {
* lpc must read the status from the command register. Attempting this on
* lpc will overwrite the args/parameter space and corrupt its data.
*/
-#define EC_CMD_GET_COMMS_STATUS 0x09
+#define EC_CMD_GET_COMMS_STATUS 0x0009
/* Avoid using ec_status which is for return values */
enum ec_comms_status {
@@ -918,7 +958,7 @@ struct __ec_align4 ec_response_get_comms_status {
};
/* Fake a variety of responses, purely for testing purposes. */
-#define EC_CMD_TEST_PROTOCOL 0x0a
+#define EC_CMD_TEST_PROTOCOL 0x000A
/* Tell the EC what to send back to us. */
struct __ec_align4 ec_params_test_protocol {
@@ -933,7 +973,7 @@ struct __ec_align4 ec_response_test_protocol {
};
/* Get protocol information */
-#define EC_CMD_GET_PROTOCOL_INFO 0x0b
+#define EC_CMD_GET_PROTOCOL_INFO 0x000B
/* Flags for ec_response_get_protocol_info.flags */
/* EC_RES_IN_PROGRESS may be returned if a command is slow */
@@ -977,11 +1017,11 @@ struct __ec_align4 ec_response_get_set_value {
};
/* More than one command can use these structs to get/set parameters. */
-#define EC_CMD_GSV_PAUSE_IN_S5 0x0c
+#define EC_CMD_GSV_PAUSE_IN_S5 0x000C
/*****************************************************************************/
/* List the features supported by the firmware */
-#define EC_CMD_GET_FEATURES 0x0d
+#define EC_CMD_GET_FEATURES 0x000D
/* Supported features */
enum ec_feature_code {
@@ -1062,6 +1102,16 @@ enum ec_feature_code {
EC_FEATURE_USBC_SS_MUX_VIRTUAL = 26,
/* EC has RTC feature that can be controlled by host commands */
EC_FEATURE_RTC = 27,
+ /* The MCU exposes a Fingerprint sensor */
+ EC_FEATURE_FINGERPRINT = 28,
+ /* The MCU exposes a Touchpad */
+ EC_FEATURE_TOUCHPAD = 29,
+ /* The MCU has RWSIG task enabled */
+ EC_FEATURE_RWSIG = 30,
+ /* EC has device events support */
+ EC_FEATURE_DEVICE_EVENT = 31,
+ /* EC supports the unified wake masks for LPC/eSPI systems */
+ EC_FEATURE_UNIFIED_WAKE_MASKS = 32,
};
#define EC_FEATURE_MASK_0(event_code) (1UL << (event_code % 32))
@@ -1071,10 +1121,22 @@ struct __ec_align4 ec_response_get_features {
};
/*****************************************************************************/
+/* Get the board's SKU ID from EC */
+#define EC_CMD_GET_SKU_ID 0x000E
+
+/* Set SKU ID from AP */
+#define EC_CMD_SET_SKU_ID 0x000F
+
+struct __ec_align4 ec_sku_id_info {
+ uint32_t sku_id;
+};
+
+/*****************************************************************************/
/* Flash commands */
/* Get flash info */
-#define EC_CMD_FLASH_INFO 0x10
+#define EC_CMD_FLASH_INFO 0x0010
+#define EC_VER_FLASH_INFO 2
/* Version 0 returns these fields */
struct __ec_align4 ec_response_flash_info {
@@ -1101,6 +1163,13 @@ struct __ec_align4 ec_response_flash_info {
/* EC flash erases bits to 0 instead of 1 */
#define EC_FLASH_INFO_ERASE_TO_0 (1 << 0)
+/* Flash must be selected for read/write/erase operations to succeed. This may
+ * be necessary on a chip where write/erase can be corrupted by other board
+ * activity, or where the chip needs to enable some sort of programming voltage,
+ * or where the read/write/erase operations require cleanly suspending other
+ * chip functionality. */
+#define EC_FLASH_INFO_SELECT_REQUIRED (1 << 1)
+
/*
* Version 1 returns the same initial fields as version 0, with additional
* fields following.
@@ -1108,6 +1177,12 @@ struct __ec_align4 ec_response_flash_info {
* gcc anonymous structs don't seem to get along with the __packed directive;
* if they did we'd define the version 0 structure as a sub-structure of this
* one.
+ *
+ * Version 2 supports flash banks of different sizes:
+ * The caller specified the number of banks it has preallocated
+ * (num_banks_desc)
+ * The EC returns the number of banks describing the flash memory.
+ * It adds banks descriptions up to num_banks_desc.
*/
struct __ec_align4 ec_response_flash_info_1 {
/* Version 0 fields; see above for description */
@@ -1129,12 +1204,48 @@ struct __ec_align4 ec_response_flash_info_1 {
uint32_t flags;
};
+struct __ec_align4 ec_params_flash_info_2 {
+ /* Number of banks to describe */
+ uint16_t num_banks_desc;
+ /* Reserved; set 0; ignore on read */
+ uint8_t reserved[2];
+};
+
+struct ec_flash_bank {
+ /* Number of sector is in this bank. */
+ uint16_t count;
+ /* Size in power of 2 of each sector (8 --> 256 bytes) */
+ uint8_t size_exp;
+ /* Minimal write size for the sectors in this bank */
+ uint8_t write_size_exp;
+ /* Erase size for the sectors in this bank */
+ uint8_t erase_size_exp;
+ /* Size for write protection, usually identical to erase size. */
+ uint8_t protect_size_exp;
+ /* Reserved; set 0; ignore on read */
+ uint8_t reserved[2];
+};
+
+struct __ec_align4 ec_response_flash_info_2 {
+ /* Total flash in the EC. */
+ uint32_t flash_size;
+ /* Flags; see EC_FLASH_INFO_* */
+ uint32_t flags;
+ /* Maximum size to use to send data to write to the EC. */
+ uint32_t write_ideal_size;
+ /* Number of banks present in the EC. */
+ uint16_t num_banks_total;
+ /* Number of banks described in banks array. */
+ uint16_t num_banks_desc;
+ struct ec_flash_bank banks[0];
+};
+
/*
* Read flash
*
* Response is params.size bytes of data.
*/
-#define EC_CMD_FLASH_READ 0x11
+#define EC_CMD_FLASH_READ 0x0011
struct __ec_align4 ec_params_flash_read {
uint32_t offset; /* Byte offset to read */
@@ -1142,7 +1253,7 @@ struct __ec_align4 ec_params_flash_read {
};
/* Write flash */
-#define EC_CMD_FLASH_WRITE 0x12
+#define EC_CMD_FLASH_WRITE 0x0012
#define EC_VER_FLASH_WRITE 1
/* Version 0 of the flash command supported only 64 bytes of data */
@@ -1155,13 +1266,49 @@ struct __ec_align4 ec_params_flash_write {
};
/* Erase flash */
-#define EC_CMD_FLASH_ERASE 0x13
+#define EC_CMD_FLASH_ERASE 0x0013
+/* v0 */
struct __ec_align4 ec_params_flash_erase {
uint32_t offset; /* Byte offset to erase */
uint32_t size; /* Size to erase in bytes */
};
+
+#define EC_VER_FLASH_WRITE 1
+/* v1 add async erase:
+ * subcommands can returns:
+ * EC_RES_SUCCESS : erased (see ERASE_SECTOR_ASYNC case below).
+ * EC_RES_INVALID_PARAM : offset/size are not aligned on a erase boundary.
+ * EC_RES_ERROR : other errors.
+ * EC_RES_BUSY : an existing erase operation is in progress.
+ * EC_RES_ACCESS_DENIED: Trying to erase running image.
+ *
+ * When ERASE_SECTOR_ASYNC returns EC_RES_SUCCESS, the operation is just
+ * properly queued. The user must call ERASE_GET_RESULT subcommand to get
+ * the proper result.
+ * When ERASE_GET_RESULT returns EC_RES_BUSY, the caller must wait and send
+ * ERASE_GET_RESULT again to get the result of ERASE_SECTOR_ASYNC.
+ * ERASE_GET_RESULT command may timeout on EC where flash access is not
+ * permitted while erasing. (For instance, STM32F4).
+ */
+enum ec_flash_erase_cmd {
+ FLASH_ERASE_SECTOR, /* Erase and wait for result */
+ FLASH_ERASE_SECTOR_ASYNC, /* Erase and return immediately. */
+ FLASH_ERASE_GET_RESULT, /* Ask for last erase result */
+};
+
+struct __ec_align4 ec_params_flash_erase_v1 {
+ /* One of ec_flash_erase_cmd. */
+ uint8_t cmd;
+ /* Pad byte; currently always contains 0 */
+ uint8_t reserved;
+ /* No flags defined yet; set to 0 */
+ uint16_t flag;
+ /* Same as v0 parameters. */
+ struct ec_params_flash_erase params;
+};
+
/*
* Get/set flash protection.
*
@@ -1172,7 +1319,7 @@ struct __ec_align4 ec_params_flash_erase {
*
* If mask=0, simply returns the current flags state.
*/
-#define EC_CMD_FLASH_PROTECT 0x15
+#define EC_CMD_FLASH_PROTECT 0x0015
#define EC_VER_FLASH_PROTECT 1 /* Command version 1 */
/* Flags for flash protection */
@@ -1197,6 +1344,14 @@ struct __ec_align4 ec_params_flash_erase {
#define EC_FLASH_PROTECT_ERROR_INCONSISTENT (1 << 5)
/* Entire flash code protected when the EC boots */
#define EC_FLASH_PROTECT_ALL_AT_BOOT (1 << 6)
+/* RW flash code protected when the EC boots */
+#define EC_FLASH_PROTECT_RW_AT_BOOT (1 << 7)
+/* RW flash code protected now. */
+#define EC_FLASH_PROTECT_RW_NOW (1 << 8)
+/* Rollback information flash region protected when the EC boots */
+#define EC_FLASH_PROTECT_ROLLBACK_AT_BOOT (1 << 9)
+/* Rollback information flash region protected now */
+#define EC_FLASH_PROTECT_ROLLBACK_NOW (1 << 10)
struct __ec_align4 ec_params_flash_protect {
uint32_t mask; /* Bits in flags to apply */
@@ -1222,22 +1377,35 @@ struct __ec_align4 ec_response_flash_protect {
*/
/* Get the region offset/size */
-#define EC_CMD_FLASH_REGION_INFO 0x16
+#define EC_CMD_FLASH_REGION_INFO 0x0016
#define EC_VER_FLASH_REGION_INFO 1
enum ec_flash_region {
/* Region which holds read-only EC image */
EC_FLASH_REGION_RO = 0,
- /* Region which holds rewritable EC image */
- EC_FLASH_REGION_RW,
+ /*
+ * Region which holds active RW image. 'Active' is different from
+ * 'running'. Active means 'scheduled-to-run'. Since RO image always
+ * scheduled to run, active/non-active applies only to RW images (for
+ * the same reason 'update' applies only to RW images. It's a state of
+ * an image on a flash. Running image can be RO, RW_A, RW_B but active
+ * image can only be RW_A or RW_B. In recovery mode, an active RW image
+ * doesn't enter 'running' state but it's still active on a flash.
+ */
+ EC_FLASH_REGION_ACTIVE,
/*
* Region which should be write-protected in the factory (a superset of
* EC_FLASH_REGION_RO)
*/
EC_FLASH_REGION_WP_RO,
+ /* Region which holds updatable (non-active) RW image */
+ EC_FLASH_REGION_UPDATE,
/* Number of regions */
EC_FLASH_REGION_COUNT,
};
+/* 'RW' is vague if there are multiple RW images; we mean the active one,
+ * so the old constant is deprecated */
+#define EC_FLASH_REGION_RW EC_FLASH_REGION_ACTIVE
struct __ec_align4 ec_params_flash_region_info {
uint32_t region; /* enum ec_flash_region */
@@ -1249,7 +1417,7 @@ struct __ec_align4 ec_response_flash_region_info {
};
/* Read/write VbNvContext */
-#define EC_CMD_VBNV_CONTEXT 0x17
+#define EC_CMD_VBNV_CONTEXT 0x0017
#define EC_VER_VBNV_CONTEXT 1
#define EC_VBNV_BLOCK_SIZE 16
@@ -1267,18 +1435,46 @@ struct __ec_align4 ec_response_vbnvcontext {
uint8_t block[EC_VBNV_BLOCK_SIZE];
};
+
+/* Get SPI flash information */
+#define EC_CMD_FLASH_SPI_INFO 0x0018
+
+struct __ec_align1 ec_response_flash_spi_info {
+ /* JEDEC info from command 0x9F (manufacturer, memory type, size) */
+ uint8_t jedec[3];
+
+ /* Pad byte; currently always contains 0 */
+ uint8_t reserved0;
+
+ /* Manufacturer / device ID from command 0x90 */
+ uint8_t mfr_dev_id[2];
+
+ /* Status registers from command 0x05 and 0x35 */
+ uint8_t sr1, sr2;
+};
+
+
+/* Select flash during flash operations */
+#define EC_CMD_FLASH_SELECT 0x0019
+
+struct __ec_align4 ec_params_flash_select {
+ /* 1 to select flash, 0 to deselect flash */
+ uint8_t select;
+};
+
+
/*****************************************************************************/
/* PWM commands */
/* Get fan target RPM */
-#define EC_CMD_PWM_GET_FAN_TARGET_RPM 0x20
+#define EC_CMD_PWM_GET_FAN_TARGET_RPM 0x0020
struct __ec_align4 ec_response_pwm_get_fan_rpm {
uint32_t rpm;
};
/* Set target fan RPM */
-#define EC_CMD_PWM_SET_FAN_TARGET_RPM 0x21
+#define EC_CMD_PWM_SET_FAN_TARGET_RPM 0x0021
/* Version 0 of input params */
struct __ec_align4 ec_params_pwm_set_fan_target_rpm_v0 {
@@ -1293,7 +1489,7 @@ struct __ec_align_size1 ec_params_pwm_set_fan_target_rpm_v1 {
/* Get keyboard backlight */
/* OBSOLETE - Use EC_CMD_PWM_SET_DUTY */
-#define EC_CMD_PWM_GET_KEYBOARD_BACKLIGHT 0x22
+#define EC_CMD_PWM_GET_KEYBOARD_BACKLIGHT 0x0022
struct __ec_align1 ec_response_pwm_get_keyboard_backlight {
uint8_t percent;
@@ -1302,14 +1498,14 @@ struct __ec_align1 ec_response_pwm_get_keyboard_backlight {
/* Set keyboard backlight */
/* OBSOLETE - Use EC_CMD_PWM_SET_DUTY */
-#define EC_CMD_PWM_SET_KEYBOARD_BACKLIGHT 0x23
+#define EC_CMD_PWM_SET_KEYBOARD_BACKLIGHT 0x0023
struct __ec_align1 ec_params_pwm_set_keyboard_backlight {
uint8_t percent;
};
/* Set target fan PWM duty cycle */
-#define EC_CMD_PWM_SET_FAN_DUTY 0x24
+#define EC_CMD_PWM_SET_FAN_DUTY 0x0024
/* Version 0 of input params */
struct __ec_align4 ec_params_pwm_set_fan_duty_v0 {
@@ -1322,7 +1518,7 @@ struct __ec_align_size1 ec_params_pwm_set_fan_duty_v1 {
uint8_t fan_idx;
};
-#define EC_CMD_PWM_SET_DUTY 0x25
+#define EC_CMD_PWM_SET_DUTY 0x0025
/* 16 bit duty cycle, 0xffff = 100% */
#define EC_PWM_MAX_DUTY 0xffff
@@ -1342,7 +1538,7 @@ struct __ec_align4 ec_params_pwm_set_duty {
uint8_t index; /* Type-specific index, or 0 if unique */
};
-#define EC_CMD_PWM_GET_DUTY 0x26
+#define EC_CMD_PWM_GET_DUTY 0x0026
struct __ec_align1 ec_params_pwm_get_duty {
uint8_t pwm_type; /* ec_pwm_type */
@@ -1360,7 +1556,7 @@ struct __ec_align2 ec_response_pwm_get_duty {
* into a subcommand. We'll make separate structs for subcommands with
* different input args, so that we know how much to expect.
*/
-#define EC_CMD_LIGHTBAR_CMD 0x28
+#define EC_CMD_LIGHTBAR_CMD 0x0028
struct __ec_todo_unpacked rgb_s {
uint8_t r, g, b;
@@ -1660,7 +1856,7 @@ enum lightbar_command {
/*****************************************************************************/
/* LED control commands */
-#define EC_CMD_LED_CONTROL 0x29
+#define EC_CMD_LED_CONTROL 0x0029
enum ec_led_id {
/* LED to indicate battery state of charge */
@@ -1672,6 +1868,14 @@ enum ec_led_id {
EC_LED_ID_POWER_LED,
/* LED on power adapter or its plug */
EC_LED_ID_ADAPTER_LED,
+ /* LED to indicate left side */
+ EC_LED_ID_LEFT_LED,
+ /* LED to indicate right side */
+ EC_LED_ID_RIGHT_LED,
+ /* LED to indicate recovery mode with HW_REINIT */
+ EC_LED_ID_RECOVERY_HW_REINIT_LED,
+ /* LED to indicate sysrq debug mode. */
+ EC_LED_ID_SYSRQ_DEBUG_LED,
EC_LED_ID_COUNT
};
@@ -1718,7 +1922,7 @@ struct __ec_align1 ec_response_led_control {
*/
/* Verified boot hash command */
-#define EC_CMD_VBOOT_HASH 0x2a
+#define EC_CMD_VBOOT_HASH 0x002A
struct __ec_align4 ec_params_vboot_hash {
uint8_t cmd; /* enum ec_vboot_hash_cmd */
@@ -1762,15 +1966,20 @@ enum ec_vboot_hash_status {
* If one of these is specified, the EC will automatically update offset and
* size to the correct values for the specified image (RO or RW).
*/
-#define EC_VBOOT_HASH_OFFSET_RO 0xfffffffe
-#define EC_VBOOT_HASH_OFFSET_RW 0xfffffffd
+#define EC_VBOOT_HASH_OFFSET_RO 0xfffffffe
+#define EC_VBOOT_HASH_OFFSET_ACTIVE 0xfffffffd
+#define EC_VBOOT_HASH_OFFSET_UPDATE 0xfffffffc
+
+/* 'RW' is vague if there are multiple RW images; we mean the active one,
+ * so the old constant is deprecated */
+#define EC_VBOOT_HASH_OFFSET_RW EC_VBOOT_HASH_OFFSET_ACTIVE
/*****************************************************************************/
/*
* Motion sense commands. We'll make separate structs for sub-commands with
* different input args, so that we know how much to expect.
*/
-#define EC_CMD_MOTION_SENSE_CMD 0x2b
+#define EC_CMD_MOTION_SENSE_CMD 0x002B
/* Motion sense commands */
enum motionsense_command {
@@ -1879,6 +2088,12 @@ enum motionsense_command {
*/
MOTIONSENSE_CMD_FIFO_INT_ENABLE = 15,
+ /*
+ * Spoof the readings of the sensors. The spoofed readings can be set
+ * to arbitrary values, or will lock to the last read actual values.
+ */
+ MOTIONSENSE_CMD_SPOOF = 16,
+
/* Number of motionsense sub-commands. */
MOTIONSENSE_NUM_CMDS
};
@@ -1914,6 +2129,16 @@ enum motionsensor_chip {
MOTIONSENSE_CHIP_L3GD20H = 7,
MOTIONSENSE_CHIP_BMA255 = 8,
MOTIONSENSE_CHIP_BMP280 = 9,
+ MOTIONSENSE_CHIP_OPT3001 = 10,
+};
+
+/* List of orientation positions */
+enum motionsensor_orientation {
+ MOTIONSENSE_ORIENTATION_LANDSCAPE = 0,
+ MOTIONSENSE_ORIENTATION_PORTRAIT = 1,
+ MOTIONSENSE_ORIENTATION_UPSIDE_DOWN_PORTRAIT = 2,
+ MOTIONSENSE_ORIENTATION_UPSIDE_DOWN_LANDSCAPE = 3,
+ MOTIONSENSE_ORIENTATION_UNKNOWN = 4,
};
struct __ec_todo_packed ec_response_motion_sensor_data {
@@ -1925,7 +2150,7 @@ struct __ec_todo_packed ec_response_motion_sensor_data {
union {
int16_t data[3];
struct __ec_todo_packed {
- uint16_t rsvd;
+ uint16_t reserved;
uint32_t timestamp;
};
struct __ec_todo_unpacked {
@@ -1960,6 +2185,7 @@ enum motionsensor_activity {
MOTIONSENSE_ACTIVITY_RESERVED = 0,
MOTIONSENSE_ACTIVITY_SIG_MOTION = 1,
MOTIONSENSE_ACTIVITY_DOUBLE_TAP = 2,
+ MOTIONSENSE_ACTIVITY_ORIENTATION = 3,
};
struct __ec_todo_unpacked ec_motion_sense_activity {
@@ -2000,6 +2226,20 @@ struct __ec_todo_unpacked ec_motion_sense_activity {
#define LID_ANGLE_UNRELIABLE 500
+enum motionsense_spoof_mode {
+ /* Disable spoof mode. */
+ MOTIONSENSE_SPOOF_MODE_DISABLE = 0,
+
+ /* Enable spoof mode, but use provided component values. */
+ MOTIONSENSE_SPOOF_MODE_CUSTOM,
+
+ /* Enable spoof mode, but use the current sensor values. */
+ MOTIONSENSE_SPOOF_MODE_LOCK_CURRENT,
+
+ /* Query the current spoof mode status for the sensor. */
+ MOTIONSENSE_SPOOF_MODE_QUERY,
+};
+
struct __ec_todo_packed ec_params_motion_sense {
uint8_t cmd;
union {
@@ -2027,7 +2267,8 @@ struct __ec_todo_packed ec_params_motion_sense {
* and MOTIONSENSE_CMD_PERFORM_CALIB. */
struct __ec_todo_unpacked {
uint8_t sensor_num;
- } info, data, fifo_flush, perform_calib, list_activities;
+ } info, info_3, data, fifo_flush, perform_calib,
+ list_activities;
/*
* Used for MOTIONSENSE_CMD_EC_RATE, MOTIONSENSE_CMD_SENSOR_ODR
@@ -2101,6 +2342,20 @@ struct __ec_todo_packed ec_params_motion_sense {
*/
int8_t enable;
} fifo_int_enable;
+
+ /* Used for MOTIONSENSE_CMD_SPOOF */
+ struct __ec_todo_packed {
+ uint8_t sensor_id;
+
+ /* See enum motionsense_spoof_mode. */
+ uint8_t spoof_enable;
+
+ /* Ignored, used for alignment. */
+ uint8_t reserved;
+
+ /* Individual component values to spoof. */
+ int16_t components[3];
+ } spoof;
};
};
@@ -2133,20 +2388,42 @@ struct __ec_todo_packed ec_response_motion_sense {
uint8_t chip;
} info;
+ /* Used for MOTIONSENSE_CMD_INFO version 3 */
+ struct __ec_todo_unpacked {
+ /* Should be element of enum motionsensor_type. */
+ uint8_t type;
+
+ /* Should be element of enum motionsensor_location. */
+ uint8_t location;
+
+ /* Should be element of enum motionsensor_chip. */
+ uint8_t chip;
+
+ /* Minimum sensor sampling frequency */
+ uint32_t min_frequency;
+
+ /* Maximum sensor sampling frequency */
+ uint32_t max_frequency;
+
+ /* Max number of sensor events that could be in fifo */
+ uint32_t fifo_max_event_count;
+ } info_3;
+
/* Used for MOTIONSENSE_CMD_DATA */
struct ec_response_motion_sensor_data data;
/*
* Used for MOTIONSENSE_CMD_EC_RATE, MOTIONSENSE_CMD_SENSOR_ODR,
* MOTIONSENSE_CMD_SENSOR_RANGE,
- * MOTIONSENSE_CMD_KB_WAKE_ANGLE and
- * MOTIONSENSE_CMD_FIFO_INT_ENABLE.
+ * MOTIONSENSE_CMD_KB_WAKE_ANGLE,
+ * MOTIONSENSE_CMD_FIFO_INT_ENABLE and
+ * MOTIONSENSE_CMD_SPOOF.
*/
struct __ec_todo_unpacked {
/* Current value of the parameter queried. */
int32_t ret;
} ec_rate, sensor_odr, sensor_range, kb_wake_angle,
- fifo_int_enable;
+ fifo_int_enable, spoof;
/* Used for MOTIONSENSE_CMD_SENSOR_OFFSET */
struct __ec_todo_unpacked {
@@ -2182,17 +2459,31 @@ struct __ec_todo_packed ec_response_motion_sense {
/* Force lid open command */
/* Make lid event always open */
-#define EC_CMD_FORCE_LID_OPEN 0x2c
+#define EC_CMD_FORCE_LID_OPEN 0x002C
struct __ec_align1 ec_params_force_lid_open {
uint8_t enabled;
};
/*****************************************************************************/
+/* Configure the behavior of the power button */
+#define EC_CMD_CONFIG_POWER_BUTTON 0x002D
+
+enum ec_config_power_button_flags {
+ /* Enable/Disable power button pulses for x86 devices */
+ EC_POWER_BUTTON_ENABLE_PULSE = (1 << 0),
+};
+
+struct __ec_align1 ec_params_config_power_button {
+ /* See enum ec_config_power_button_flags */
+ uint8_t flags;
+};
+
+/*****************************************************************************/
/* USB charging control commands */
/* Set USB port charging mode */
-#define EC_CMD_USB_CHARGE_SET_MODE 0x30
+#define EC_CMD_USB_CHARGE_SET_MODE 0x0030
struct __ec_align1 ec_params_usb_charge_set_mode {
uint8_t usb_port_id;
@@ -2206,7 +2497,7 @@ struct __ec_align1 ec_params_usb_charge_set_mode {
#define EC_PSTORE_SIZE_MAX 64
/* Get persistent storage info */
-#define EC_CMD_PSTORE_INFO 0x40
+#define EC_CMD_PSTORE_INFO 0x0040
struct __ec_align4 ec_response_pstore_info {
/* Persistent storage size, in bytes */
@@ -2220,7 +2511,7 @@ struct __ec_align4 ec_response_pstore_info {
*
* Response is params.size bytes of data.
*/
-#define EC_CMD_PSTORE_READ 0x41
+#define EC_CMD_PSTORE_READ 0x0041
struct __ec_align4 ec_params_pstore_read {
uint32_t offset; /* Byte offset to read */
@@ -2228,7 +2519,7 @@ struct __ec_align4 ec_params_pstore_read {
};
/* Write persistent storage */
-#define EC_CMD_PSTORE_WRITE 0x42
+#define EC_CMD_PSTORE_WRITE 0x0042
struct __ec_align4 ec_params_pstore_write {
uint32_t offset; /* Byte offset to write */
@@ -2249,12 +2540,12 @@ struct __ec_align4 ec_response_rtc {
};
/* These use ec_response_rtc */
-#define EC_CMD_RTC_GET_VALUE 0x44
-#define EC_CMD_RTC_GET_ALARM 0x45
+#define EC_CMD_RTC_GET_VALUE 0x0044
+#define EC_CMD_RTC_GET_ALARM 0x0045
/* These all use ec_params_rtc */
-#define EC_CMD_RTC_SET_VALUE 0x46
-#define EC_CMD_RTC_SET_ALARM 0x47
+#define EC_CMD_RTC_SET_VALUE 0x0046
+#define EC_CMD_RTC_SET_ALARM 0x0047
/* Pass as time param to SET_ALARM to clear the current alarm */
#define EC_RTC_ALARM_CLEAR 0
@@ -2266,8 +2557,8 @@ struct __ec_align4 ec_response_rtc {
#define EC_PORT80_SIZE_MAX 32
/* Get last port80 code from previous boot */
-#define EC_CMD_PORT80_LAST_BOOT 0x48
-#define EC_CMD_PORT80_READ 0x48
+#define EC_CMD_PORT80_LAST_BOOT 0x0048
+#define EC_CMD_PORT80_READ 0x0048
enum ec_port80_subcmd {
EC_PORT80_GET_INFO = 0,
@@ -2311,7 +2602,7 @@ struct __ec_align2 ec_response_port80_last_boot {
#define EC_VSTORE_SLOT_MAX 32
/* Get persistent storage info */
-#define EC_CMD_VSTORE_INFO 0x49
+#define EC_CMD_VSTORE_INFO 0x0049
struct __ec_align_size1 ec_response_vstore_info {
/* Indicates which slots are locked */
uint32_t slot_locked;
@@ -2324,7 +2615,7 @@ struct __ec_align_size1 ec_response_vstore_info {
*
* Response is EC_VSTORE_SLOT_SIZE bytes of data.
*/
-#define EC_CMD_VSTORE_READ 0x4a
+#define EC_CMD_VSTORE_READ 0x004A
struct __ec_align1 ec_params_vstore_read {
uint8_t slot; /* Slot to read from */
@@ -2337,7 +2628,7 @@ struct __ec_align1 ec_response_vstore_read {
/*
* Write temporary secure storage and lock it.
*/
-#define EC_CMD_VSTORE_WRITE 0x4b
+#define EC_CMD_VSTORE_WRITE 0x004B
struct __ec_align1 ec_params_vstore_write {
uint8_t slot; /* Slot to write to */
@@ -2351,8 +2642,8 @@ struct __ec_align1 ec_params_vstore_write {
* Version 1 separates the CPU thermal limits from the fan control.
*/
-#define EC_CMD_THERMAL_SET_THRESHOLD 0x50
-#define EC_CMD_THERMAL_GET_THRESHOLD 0x51
+#define EC_CMD_THERMAL_SET_THRESHOLD 0x0050
+#define EC_CMD_THERMAL_GET_THRESHOLD 0x0051
/* The version 0 structs are opaque. You have to know what they are for
* the get/set commands to make any sense.
@@ -2389,11 +2680,27 @@ enum ec_temp_thresholds {
* Thermal configuration for one temperature sensor. Temps are in degrees K.
* Zero values will be silently ignored by the thermal task.
*
+ * Set 'temp_host' value allows thermal task to trigger some event with 1 degree
+ * hysteresis.
+ * For example,
+ * temp_host[EC_TEMP_THRESH_HIGH] = 300 K
+ * temp_host_release[EC_TEMP_THRESH_HIGH] = 0 K
+ * EC will throttle ap when temperature >= 301 K, and release throttling when
+ * temperature <= 299 K.
+ *
+ * Set 'temp_host_release' value allows thermal task has a custom hysteresis.
+ * For example,
+ * temp_host[EC_TEMP_THRESH_HIGH] = 300 K
+ * temp_host_release[EC_TEMP_THRESH_HIGH] = 295 K
+ * EC will throttle ap when temperature >= 301 K, and release throttling when
+ * temperature <= 294 K.
+ *
* Note that this structure is a sub-structure of
* ec_params_thermal_set_threshold_v1, but maintains its alignment there.
*/
struct __ec_align4 ec_thermal_config {
uint32_t temp_host[EC_TEMP_THRESH_COUNT]; /* levels of hotness */
+ uint32_t temp_host_release[EC_TEMP_THRESH_COUNT]; /* release levels */
uint32_t temp_fan_off; /* no active cooling needed */
uint32_t temp_fan_max; /* max active cooling needed */
};
@@ -2415,7 +2722,7 @@ struct __ec_align4 ec_params_thermal_set_threshold_v1 {
/****************************************************************************/
/* Toggle automatic fan control */
-#define EC_CMD_THERMAL_AUTO_FAN_CTRL 0x52
+#define EC_CMD_THERMAL_AUTO_FAN_CTRL 0x0052
/* Version 1 of input params */
struct __ec_align1 ec_params_auto_fan_ctrl_v1 {
@@ -2423,8 +2730,8 @@ struct __ec_align1 ec_params_auto_fan_ctrl_v1 {
};
/* Get/Set TMP006 calibration data */
-#define EC_CMD_TMP006_GET_CALIBRATION 0x53
-#define EC_CMD_TMP006_SET_CALIBRATION 0x54
+#define EC_CMD_TMP006_GET_CALIBRATION 0x0053
+#define EC_CMD_TMP006_SET_CALIBRATION 0x0054
/*
* The original TMP006 calibration only needed four params, but now we need
@@ -2475,7 +2782,7 @@ struct __ec_align4 ec_params_tmp006_set_calibration_v1 {
/* Read raw TMP006 data */
-#define EC_CMD_TMP006_GET_RAW 0x55
+#define EC_CMD_TMP006_GET_RAW 0x0055
struct __ec_align1 ec_params_tmp006_get_raw {
uint8_t index;
@@ -2499,12 +2806,12 @@ struct __ec_align4 ec_response_tmp006_get_raw {
* to obtain the instantaneous state, use EC_CMD_MKBP_INFO with the type
* EC_MKBP_INFO_CURRENT and event EC_MKBP_EVENT_KEY_MATRIX.
*/
-#define EC_CMD_MKBP_STATE 0x60
+#define EC_CMD_MKBP_STATE 0x0060
/*
* Provide information about various MKBP things. See enum ec_mkbp_info_type.
*/
-#define EC_CMD_MKBP_INFO 0x61
+#define EC_CMD_MKBP_INFO 0x0061
struct __ec_align_size1 ec_response_mkbp_info {
uint32_t rows;
@@ -2558,7 +2865,7 @@ enum ec_mkbp_info_type {
};
/* Simulate key press */
-#define EC_CMD_MKBP_SIMULATE_KEY 0x62
+#define EC_CMD_MKBP_SIMULATE_KEY 0x0062
struct __ec_align1 ec_params_mkbp_simulate_key {
uint8_t col;
@@ -2567,8 +2874,8 @@ struct __ec_align1 ec_params_mkbp_simulate_key {
};
/* Configure keyboard scanning */
-#define EC_CMD_MKBP_SET_CONFIG 0x64
-#define EC_CMD_MKBP_GET_CONFIG 0x65
+#define EC_CMD_MKBP_SET_CONFIG 0x0064
+#define EC_CMD_MKBP_GET_CONFIG 0x0065
/* flags */
enum mkbp_config_flags {
@@ -2621,7 +2928,7 @@ struct __ec_align_size1 ec_response_mkbp_get_config {
};
/* Run the key scan emulation */
-#define EC_CMD_KEYSCAN_SEQ_CTRL 0x66
+#define EC_CMD_KEYSCAN_SEQ_CTRL 0x0066
enum ec_keyscan_seq_cmd {
EC_KEYSCAN_SEQ_STATUS = 0, /* Get status information */
@@ -2682,7 +2989,7 @@ struct __ec_todo_packed ec_result_keyscan_seq_ctrl {
*
* Returns EC_RES_UNAVAILABLE if there is no event pending.
*/
-#define EC_CMD_GET_NEXT_EVENT 0x67
+#define EC_CMD_GET_NEXT_EVENT 0x0067
enum ec_mkbp_event {
/* Keyboard matrix changed. The event data is the new matrix state. */
@@ -2700,6 +3007,15 @@ enum ec_mkbp_event {
/* The state of the switches have changed. */
EC_MKBP_EVENT_SWITCH = 4,
+ /* New Fingerprint sensor event, the event data is fp_events bitmap. */
+ EC_MKBP_EVENT_FINGERPRINT = 5,
+
+ /*
+ * Sysrq event: send emulated sysrq. The event data is sysrq,
+ * corresponding to the key to be pressed.
+ */
+ EC_MKBP_EVENT_SYSRQ = 6,
+
/* Number of MKBP events */
EC_MKBP_EVENT_COUNT,
};
@@ -2712,13 +3028,17 @@ union __ec_align_offset1 ec_response_get_next_data {
struct __ec_todo_unpacked {
/* For aligning the fifo_info */
- uint8_t rsvd[3];
+ uint8_t reserved[3];
struct ec_response_motion_sense_fifo_info info;
} sensor_fifo;
uint32_t buttons;
uint32_t switches;
+
+ uint32_t fp_events;
+
+ uint32_t sysrq;
};
struct __ec_align1 ec_response_get_next_event {
@@ -2732,23 +3052,30 @@ struct __ec_align1 ec_response_get_next_event {
#define EC_MKBP_POWER_BUTTON 0
#define EC_MKBP_VOL_UP 1
#define EC_MKBP_VOL_DOWN 2
+#define EC_MKBP_RECOVERY 3
/* Switches */
#define EC_MKBP_LID_OPEN 0
#define EC_MKBP_TABLET_MODE 1
/* Run keyboard factory test scanning */
-#define EC_CMD_KEYBOARD_FACTORY_TEST 0x68
+#define EC_CMD_KEYBOARD_FACTORY_TEST 0x0068
struct __ec_align2 ec_response_keyboard_factory_test {
uint16_t shorted; /* Keyboard pins are shorted */
};
+/* Fingerprint events in 'fp_events' for EC_MKBP_EVENT_FINGERPRINT */
+#define EC_MKBP_FP_RAW_EVENT(fp_events) ((fp_events) & 0x00FFFFFF)
+#define EC_MKBP_FP_FINGER_DOWN (1 << 29)
+#define EC_MKBP_FP_FINGER_UP (1 << 30)
+#define EC_MKBP_FP_IMAGE_READY (1 << 31)
+
/*****************************************************************************/
/* Temperature sensor commands */
/* Read temperature sensor info */
-#define EC_CMD_TEMP_SENSOR_GET_INFO 0x70
+#define EC_CMD_TEMP_SENSOR_GET_INFO 0x0070
struct __ec_align1 ec_params_temp_sensor_get_info {
uint8_t id;
@@ -2783,30 +3110,30 @@ struct __ec_align4 ec_response_host_event_mask {
};
/* These all use ec_response_host_event_mask */
-#define EC_CMD_HOST_EVENT_GET_B 0x87
-#define EC_CMD_HOST_EVENT_GET_SMI_MASK 0x88
-#define EC_CMD_HOST_EVENT_GET_SCI_MASK 0x89
-#define EC_CMD_HOST_EVENT_GET_WAKE_MASK 0x8d
+#define EC_CMD_HOST_EVENT_GET_B 0x0087
+#define EC_CMD_HOST_EVENT_GET_SMI_MASK 0x0088
+#define EC_CMD_HOST_EVENT_GET_SCI_MASK 0x0089
+#define EC_CMD_HOST_EVENT_GET_WAKE_MASK 0x008D
/* These all use ec_params_host_event_mask */
-#define EC_CMD_HOST_EVENT_SET_SMI_MASK 0x8a
-#define EC_CMD_HOST_EVENT_SET_SCI_MASK 0x8b
-#define EC_CMD_HOST_EVENT_CLEAR 0x8c
-#define EC_CMD_HOST_EVENT_SET_WAKE_MASK 0x8e
-#define EC_CMD_HOST_EVENT_CLEAR_B 0x8f
+#define EC_CMD_HOST_EVENT_SET_SMI_MASK 0x008A
+#define EC_CMD_HOST_EVENT_SET_SCI_MASK 0x008B
+#define EC_CMD_HOST_EVENT_CLEAR 0x008C
+#define EC_CMD_HOST_EVENT_SET_WAKE_MASK 0x008E
+#define EC_CMD_HOST_EVENT_CLEAR_B 0x008F
/*****************************************************************************/
/* Switch commands */
/* Enable/disable LCD backlight */
-#define EC_CMD_SWITCH_ENABLE_BKLIGHT 0x90
+#define EC_CMD_SWITCH_ENABLE_BKLIGHT 0x0090
struct __ec_align1 ec_params_switch_enable_backlight {
uint8_t enabled;
};
/* Enable/disable WLAN/Bluetooth */
-#define EC_CMD_SWITCH_ENABLE_WIRELESS 0x91
+#define EC_CMD_SWITCH_ENABLE_WIRELESS 0x0091
#define EC_VER_SWITCH_ENABLE_WIRELESS 1
/* Version 0 params; no response */
@@ -2846,7 +3173,7 @@ struct __ec_align1 ec_response_switch_enable_wireless_v1 {
/* GPIO commands. Only available on EC if write protect has been disabled. */
/* Set GPIO output value */
-#define EC_CMD_GPIO_SET 0x92
+#define EC_CMD_GPIO_SET 0x0092
struct __ec_align1 ec_params_gpio_set {
char name[32];
@@ -2854,7 +3181,7 @@ struct __ec_align1 ec_params_gpio_set {
};
/* Get GPIO value */
-#define EC_CMD_GPIO_GET 0x93
+#define EC_CMD_GPIO_GET 0x0093
/* Version 0 of input params and response */
struct __ec_align1 ec_params_gpio_get {
@@ -2908,7 +3235,7 @@ enum gpio_get_subcmd {
*/
/* Read I2C bus */
-#define EC_CMD_I2C_READ 0x94
+#define EC_CMD_I2C_READ 0x0094
struct __ec_align_size1 ec_params_i2c_read {
uint16_t addr; /* 8-bit address (7-bit shifted << 1) */
@@ -2922,7 +3249,7 @@ struct __ec_align2 ec_response_i2c_read {
};
/* Write I2C bus */
-#define EC_CMD_I2C_WRITE 0x95
+#define EC_CMD_I2C_WRITE 0x0095
struct __ec_align_size1 ec_params_i2c_write {
uint16_t data;
@@ -2938,7 +3265,7 @@ struct __ec_align_size1 ec_params_i2c_write {
/* Force charge state machine to stop charging the battery or force it to
* discharge the battery.
*/
-#define EC_CMD_CHARGE_CONTROL 0x96
+#define EC_CMD_CHARGE_CONTROL 0x0096
#define EC_VER_CHARGE_CONTROL 1
enum ec_charge_control_mode {
@@ -2955,7 +3282,7 @@ struct __ec_align4 ec_params_charge_control {
/* Console commands. Only available when flash write protect is unlocked. */
/* Snapshot console output buffer for use by EC_CMD_CONSOLE_READ. */
-#define EC_CMD_CONSOLE_SNAPSHOT 0x97
+#define EC_CMD_CONSOLE_SNAPSHOT 0x0097
/*
* Read data from the saved snapshot. If the subcmd parameter is
@@ -2969,7 +3296,7 @@ struct __ec_align4 ec_params_charge_control {
* Response is null-terminated string. Empty string, if there is no more
* remaining output.
*/
-#define EC_CMD_CONSOLE_READ 0x98
+#define EC_CMD_CONSOLE_READ 0x0098
enum ec_console_read_subcmd {
CONSOLE_READ_NEXT = 0,
@@ -2989,7 +3316,7 @@ struct __ec_align1 ec_params_console_read_v1 {
* EC_RES_SUCCESS if the command was successful.
* EC_RES_ERROR if the cut off command failed.
*/
-#define EC_CMD_BATTERY_CUT_OFF 0x99
+#define EC_CMD_BATTERY_CUT_OFF 0x0099
#define EC_BATTERY_CUTOFF_FLAG_AT_SHUTDOWN (1 << 0)
@@ -3003,7 +3330,7 @@ struct __ec_align1 ec_params_battery_cutoff {
/*
* Switch USB mux or return to automatic switching.
*/
-#define EC_CMD_USB_MUX 0x9a
+#define EC_CMD_USB_MUX 0x009A
struct __ec_align1 ec_params_usb_mux {
uint8_t mux;
@@ -3020,7 +3347,7 @@ enum ec_ldo_state {
/*
* Switch on/off a LDO.
*/
-#define EC_CMD_LDO_SET 0x9b
+#define EC_CMD_LDO_SET 0x009B
struct __ec_align1 ec_params_ldo_set {
uint8_t index;
@@ -3030,7 +3357,7 @@ struct __ec_align1 ec_params_ldo_set {
/*
* Get LDO state.
*/
-#define EC_CMD_LDO_GET 0x9c
+#define EC_CMD_LDO_GET 0x009C
struct __ec_align1 ec_params_ldo_get {
uint8_t index;
@@ -3046,7 +3373,7 @@ struct __ec_align1 ec_response_ldo_get {
/*
* Get power info.
*/
-#define EC_CMD_POWER_INFO 0x9d
+#define EC_CMD_POWER_INFO 0x009D
struct __ec_align4 ec_response_power_info {
uint32_t usb_dev_type;
@@ -3059,7 +3386,7 @@ struct __ec_align4 ec_response_power_info {
/*****************************************************************************/
/* I2C passthru command */
-#define EC_CMD_I2C_PASSTHRU 0x9e
+#define EC_CMD_I2C_PASSTHRU 0x009E
/* Read data; if not present, message is a write */
#define EC_I2C_FLAG_READ (1 << 15)
@@ -3094,7 +3421,7 @@ struct __ec_align1 ec_response_i2c_passthru {
/*****************************************************************************/
/* Power button hang detect */
-#define EC_CMD_HANG_DETECT 0x9f
+#define EC_CMD_HANG_DETECT 0x009F
/* Reasons to start hang detection timer */
/* Power button pressed */
@@ -3153,7 +3480,7 @@ struct __ec_align4 ec_params_hang_detect {
* This is the single catch-all host command to exchange data regarding the
* charge state machine (v2 and up).
*/
-#define EC_CMD_CHARGE_STATE 0xa0
+#define EC_CMD_CHARGE_STATE 0x00A0
/* Subcommands for this host command */
enum charge_state_command {
@@ -3185,6 +3512,15 @@ enum charge_state_params {
CS_PARAM_CUSTOM_PROFILE_MIN = 0x10000,
CS_PARAM_CUSTOM_PROFILE_MAX = 0x1ffff,
+ /* Range for CONFIG_CHARGE_STATE_DEBUG params */
+ CS_PARAM_DEBUG_MIN = 0x20000,
+ CS_PARAM_DEBUG_CTL_MODE = 0x20000,
+ CS_PARAM_DEBUG_MANUAL_MODE,
+ CS_PARAM_DEBUG_SEEMS_DEAD,
+ CS_PARAM_DEBUG_SEEMS_DISCONNECTED,
+ CS_PARAM_DEBUG_BATT_REMOVED,
+ CS_PARAM_DEBUG_MAX = 0x2ffff,
+
/* Other custom param ranges go here... */
};
@@ -3229,7 +3565,7 @@ struct __ec_align4 ec_response_charge_state {
/*
* Set maximum battery charging current.
*/
-#define EC_CMD_CHARGE_CURRENT_LIMIT 0xa1
+#define EC_CMD_CHARGE_CURRENT_LIMIT 0x00A1
struct __ec_align4 ec_params_current_limit {
uint32_t limit; /* in mA */
@@ -3238,7 +3574,7 @@ struct __ec_align4 ec_params_current_limit {
/*
* Set maximum external voltage / current.
*/
-#define EC_CMD_EXTERNAL_POWER_LIMIT 0xa2
+#define EC_CMD_EXTERNAL_POWER_LIMIT 0x00A2
/* Command v0 is used only on Spring and is obsolete + unsupported */
struct __ec_align2 ec_params_external_power_limit_v1 {
@@ -3252,7 +3588,7 @@ struct __ec_align2 ec_params_external_power_limit_v1 {
/* Hibernate/Deep Sleep Commands */
/* Set the delay before going into hibernation. */
-#define EC_CMD_HIBERNATION_DELAY 0xa8
+#define EC_CMD_HIBERNATION_DELAY 0x00A8
struct __ec_align4 ec_params_hibernation_delay {
/*
@@ -3283,13 +3619,15 @@ struct __ec_align4 ec_response_hibernation_delay {
};
/* Inform the EC when entering a sleep state */
-#define EC_CMD_HOST_SLEEP_EVENT 0xa9
+#define EC_CMD_HOST_SLEEP_EVENT 0x00A9
enum host_sleep_event {
HOST_SLEEP_EVENT_S3_SUSPEND = 1,
HOST_SLEEP_EVENT_S3_RESUME = 2,
HOST_SLEEP_EVENT_S0IX_SUSPEND = 3,
- HOST_SLEEP_EVENT_S0IX_RESUME = 4
+ HOST_SLEEP_EVENT_S0IX_RESUME = 4,
+ /* S3 suspend with additional enabled wake sources */
+ HOST_SLEEP_EVENT_S3_WAKEABLE_SUSPEND = 5,
};
struct __ec_align1 ec_params_host_sleep_event {
@@ -3297,17 +3635,47 @@ struct __ec_align1 ec_params_host_sleep_event {
};
/*****************************************************************************/
+/* Device events */
+#define EC_CMD_DEVICE_EVENT 0x00AA
+
+enum ec_device_event {
+ EC_DEVICE_EVENT_TRACKPAD,
+ EC_DEVICE_EVENT_DSP,
+ EC_DEVICE_EVENT_WIFI,
+};
+
+enum ec_device_event_param {
+ /* Get and clear pending device events */
+ EC_DEVICE_EVENT_PARAM_GET_CURRENT_EVENTS,
+ /* Get device event mask */
+ EC_DEVICE_EVENT_PARAM_GET_ENABLED_EVENTS,
+ /* Set device event mask */
+ EC_DEVICE_EVENT_PARAM_SET_ENABLED_EVENTS,
+};
+
+#define EC_DEVICE_EVENT_MASK(event_code) (1UL << (event_code % 32))
+
+struct __ec_align_size1 ec_params_device_event {
+ uint32_t event_mask;
+ uint8_t param;
+};
+
+struct __ec_align4 ec_response_device_event {
+ uint32_t event_mask;
+};
+
+/*****************************************************************************/
/* Smart battery pass-through */
/* Get / Set 16-bit smart battery registers */
-#define EC_CMD_SB_READ_WORD 0xb0
-#define EC_CMD_SB_WRITE_WORD 0xb1
+#define EC_CMD_SB_READ_WORD 0x00B0
+#define EC_CMD_SB_WRITE_WORD 0x00B1
/* Get / Set string smart battery parameters
* formatted as SMBUS "block".
*/
-#define EC_CMD_SB_READ_BLOCK 0xb2
-#define EC_CMD_SB_WRITE_BLOCK 0xb3
+#define EC_CMD_SB_READ_BLOCK 0x00B2
+#define EC_CMD_SB_WRITE_BLOCK 0x00B3
struct __ec_align1 ec_params_sb_rd {
uint8_t reg;
@@ -3340,7 +3708,7 @@ struct __ec_align1 ec_params_sb_wr_block {
* requested value.
*/
-#define EC_CMD_BATTERY_VENDOR_PARAM 0xb4
+#define EC_CMD_BATTERY_VENDOR_PARAM 0x00B4
enum ec_battery_vendor_param_mode {
BATTERY_VENDOR_PARAM_MODE_GET = 0,
@@ -3361,7 +3729,7 @@ struct __ec_align4 ec_response_battery_vendor_param {
/*
* Smart Battery Firmware Update Commands
*/
-#define EC_CMD_SB_FW_UPDATE 0xb5
+#define EC_CMD_SB_FW_UPDATE 0x00B5
enum ec_sb_fw_update_subcmd {
EC_SB_FW_UPDATE_PREPARE = 0x0,
@@ -3422,7 +3790,7 @@ struct __ec_align1 ec_response_sb_fw_update {
* Default mode is VBOOT_MODE_NORMAL if EC did not receive this command.
* Valid Modes are: normal, developer, and recovery.
*/
-#define EC_CMD_ENTERING_MODE 0xb6
+#define EC_CMD_ENTERING_MODE 0x00B6
struct __ec_align4 ec_params_entering_mode {
int vboot_mode;
@@ -3437,7 +3805,7 @@ struct __ec_align4 ec_params_entering_mode {
* I2C passthru protection command: Protects I2C tunnels against access on
* certain addresses (board-specific).
*/
-#define EC_CMD_I2C_PASSTHRU_PROTECT 0xb7
+#define EC_CMD_I2C_PASSTHRU_PROTECT 0x00B7
enum ec_i2c_passthru_protect_subcmd {
EC_CMD_I2C_PASSTHRU_PROTECT_STATUS = 0x0,
@@ -3460,13 +3828,13 @@ struct __ec_align1 ec_response_i2c_passthru_protect {
* TODO(crosbug.com/p/23747): This is a confusing name, since it doesn't
* necessarily reboot the EC. Rename to "image" or something similar?
*/
-#define EC_CMD_REBOOT_EC 0xd2
+#define EC_CMD_REBOOT_EC 0x00D2
/* Command */
enum ec_reboot_cmd {
EC_REBOOT_CANCEL = 0, /* Cancel a pending reboot */
EC_REBOOT_JUMP_RO = 1, /* Jump to RO without rebooting */
- EC_REBOOT_JUMP_RW = 2, /* Jump to RW without rebooting */
+ EC_REBOOT_JUMP_RW = 2, /* Jump to active RW without rebooting */
/* (command 3 was jump to RW-B) */
EC_REBOOT_COLD = 4, /* Cold-reboot */
EC_REBOOT_DISABLE_JUMP = 5, /* Disable jump until next reboot */
@@ -3476,6 +3844,7 @@ enum ec_reboot_cmd {
/* Flags for ec_params_reboot_ec.reboot_flags */
#define EC_REBOOT_FLAG_RESERVED0 (1 << 0) /* Was recovery request */
#define EC_REBOOT_FLAG_ON_AP_SHUTDOWN (1 << 1) /* Reboot after AP shutdown */
+#define EC_REBOOT_FLAG_SWITCH_RW_SLOT (1 << 2) /* Switch RW slot */
struct __ec_align1 ec_params_reboot_ec {
uint8_t cmd; /* enum ec_reboot_cmd */
@@ -3488,7 +3857,7 @@ struct __ec_align1 ec_params_reboot_ec {
* Returns variable-length platform-dependent panic information. See panic.h
* for details.
*/
-#define EC_CMD_GET_PANIC_INFO 0xd3
+#define EC_CMD_GET_PANIC_INFO 0x00D3
/*****************************************************************************/
/*
@@ -3507,7 +3876,7 @@ struct __ec_align1 ec_params_reboot_ec {
*
* Use EC_CMD_REBOOT_EC to reboot the EC more politely.
*/
-#define EC_CMD_REBOOT 0xd1 /* Think "die" */
+#define EC_CMD_REBOOT 0x00D1 /* Think "die" */
/*
* Resend last response (not supported on LPC).
@@ -3516,7 +3885,7 @@ struct __ec_align1 ec_params_reboot_ec {
* there was no previous command, or the previous command's response was too
* big to save.
*/
-#define EC_CMD_RESEND_RESPONSE 0xdb
+#define EC_CMD_RESEND_RESPONSE 0x00DB
/*
* This header byte on a command indicate version 0. Any header byte less
@@ -3528,7 +3897,7 @@ struct __ec_align1 ec_params_reboot_ec {
*
* The old EC interface must not use commands 0xdc or higher.
*/
-#define EC_CMD_VERSION0 0xdc
+#define EC_CMD_VERSION0 0x00DC
/*****************************************************************************/
/*
@@ -3538,7 +3907,7 @@ struct __ec_align1 ec_params_reboot_ec {
*/
/* EC to PD MCU exchange status command */
-#define EC_CMD_PD_EXCHANGE_STATUS 0x100
+#define EC_CMD_PD_EXCHANGE_STATUS 0x0100
#define EC_VER_PD_EXCHANGE_STATUS 2
enum pd_charge_state {
@@ -3575,7 +3944,7 @@ struct __ec_align_size1 ec_response_pd_status {
};
/* AP to PD MCU host event status command, cleared on read */
-#define EC_CMD_PD_HOST_EVENT_STATUS 0x104
+#define EC_CMD_PD_HOST_EVENT_STATUS 0x0104
/* PD MCU host event status bits */
#define PD_EVENT_UPDATE_DEVICE (1 << 0)
@@ -3587,7 +3956,7 @@ struct __ec_align4 ec_response_host_event_status {
};
/* Set USB type-C port role and muxes */
-#define EC_CMD_USB_PD_CONTROL 0x101
+#define EC_CMD_USB_PD_CONTROL 0x0101
enum usb_pd_control_role {
USB_PD_CTRL_ROLE_NO_CHANGE = 0,
@@ -3595,6 +3964,7 @@ enum usb_pd_control_role {
USB_PD_CTRL_ROLE_TOGGLE_OFF = 2,
USB_PD_CTRL_ROLE_FORCE_SINK = 3,
USB_PD_CTRL_ROLE_FORCE_SOURCE = 4,
+ USB_PD_CTRL_ROLE_FREEZE = 5,
USB_PD_CTRL_ROLE_COUNT
};
@@ -3649,13 +4019,16 @@ struct __ec_align1 ec_response_usb_pd_control_v1 {
char state[32];
};
-#define EC_CMD_USB_PD_PORTS 0x102
+#define EC_CMD_USB_PD_PORTS 0x0102
+
+/* Maximum number of PD ports on a device, num_ports will be <= this */
+#define EC_USB_PD_MAX_PORTS 8
struct __ec_align1 ec_response_usb_pd_ports {
uint8_t num_ports;
};
-#define EC_CMD_USB_PD_POWER_INFO 0x103
+#define EC_CMD_USB_PD_POWER_INFO 0x0103
#define PD_POWER_CHARGING_PORT 0xff
struct __ec_align1 ec_params_usb_pd_power_info {
@@ -3673,6 +4046,7 @@ enum usb_chg_type {
USB_CHG_TYPE_OTHER,
USB_CHG_TYPE_VBUS,
USB_CHG_TYPE_UNKNOWN,
+ USB_CHG_TYPE_DEDICATED,
};
enum usb_power_roles {
USB_PD_PORT_POWER_DISCONNECTED,
@@ -3698,7 +4072,7 @@ struct __ec_align4 ec_response_usb_pd_power_info {
};
/* Write USB-PD device FW */
-#define EC_CMD_USB_PD_FW_UPDATE 0x110
+#define EC_CMD_USB_PD_FW_UPDATE 0x0110
enum usb_pd_fw_update_cmds {
USB_PD_FW_REBOOT,
@@ -3716,7 +4090,7 @@ struct __ec_align4 ec_params_usb_pd_fw_update {
};
/* Write USB-PD Accessory RW_HASH table entry */
-#define EC_CMD_USB_PD_RW_HASH_ENTRY 0x111
+#define EC_CMD_USB_PD_RW_HASH_ENTRY 0x0111
/* RW hash is first 20 bytes of SHA-256 of RW section */
#define PD_RW_HASH_SIZE 20
struct __ec_align1 ec_params_usb_pd_rw_hash_entry {
@@ -3729,14 +4103,14 @@ struct __ec_align1 ec_params_usb_pd_rw_hash_entry {
};
/* Read USB-PD Accessory info */
-#define EC_CMD_USB_PD_DEV_INFO 0x112
+#define EC_CMD_USB_PD_DEV_INFO 0x0112
struct __ec_align1 ec_params_usb_pd_info_request {
uint8_t port;
};
/* Read USB-PD Device discovery info */
-#define EC_CMD_USB_PD_DISCOVERY 0x113
+#define EC_CMD_USB_PD_DISCOVERY 0x0113
struct __ec_align_size1 ec_params_usb_pd_discovery_entry {
uint16_t vid; /* USB-IF VID */
uint16_t pid; /* USB-IF PID */
@@ -3744,7 +4118,7 @@ struct __ec_align_size1 ec_params_usb_pd_discovery_entry {
};
/* Override default charge behavior */
-#define EC_CMD_PD_CHARGE_PORT_OVERRIDE 0x114
+#define EC_CMD_PD_CHARGE_PORT_OVERRIDE 0x0114
/* Negative port parameters have special meaning */
enum usb_pd_override_ports {
@@ -3757,8 +4131,12 @@ struct __ec_align2 ec_params_charge_port_override {
int16_t override_port; /* Override port# */
};
-/* Read (and delete) one entry of PD event log */
-#define EC_CMD_PD_GET_LOG_ENTRY 0x115
+/*
+ * Read (and delete) one entry of PD event log.
+ * TODO(crbug.com/751742): Make this host command more generic to accommodate
+ * future non-PD logs that use the same internal EC event_log.
+ */
+#define EC_CMD_PD_GET_LOG_ENTRY 0x0115
struct __ec_align4 ec_response_pd_log {
uint32_t timestamp; /* relative timestamp in milliseconds */
@@ -3847,7 +4225,7 @@ struct __ec_align4 mcdp_info {
#define MCDP_FAMILY(family) ((family[0] << 8) | family[1])
/* Get/Set USB-PD Alternate mode info */
-#define EC_CMD_USB_PD_GET_AMODE 0x116
+#define EC_CMD_USB_PD_GET_AMODE 0x0116
struct __ec_align_size1 ec_params_usb_pd_get_mode_request {
uint16_t svid_idx; /* SVID index to get */
uint8_t port; /* port */
@@ -3859,7 +4237,7 @@ struct __ec_align4 ec_params_usb_pd_get_mode_response {
uint32_t vdo[6]; /* Mode VDOs */
};
-#define EC_CMD_USB_PD_SET_AMODE 0x117
+#define EC_CMD_USB_PD_SET_AMODE 0x0117
enum pd_mode_cmd {
PD_EXIT_MODE = 0,
@@ -3876,7 +4254,7 @@ struct __ec_align4 ec_params_usb_pd_set_mode_request {
};
/* Ask the PD MCU to record a log of a requested type */
-#define EC_CMD_PD_WRITE_LOG_ENTRY 0x118
+#define EC_CMD_PD_WRITE_LOG_ENTRY 0x0118
struct __ec_align1 ec_params_pd_write_log_entry {
uint8_t type; /* event type : see PD_EVENT_xx above */
@@ -3885,13 +4263,14 @@ struct __ec_align1 ec_params_pd_write_log_entry {
/* Control USB-PD chip */
-#define EC_CMD_PD_CONTROL 0x119
+#define EC_CMD_PD_CONTROL 0x0119
enum ec_pd_control_cmd {
PD_SUSPEND = 0, /* Suspend the PD chip (EC: stop talking to PD) */
PD_RESUME, /* Resume the PD chip (EC: start talking to PD) */
PD_RESET, /* Force reset the PD chip */
- PD_CONTROL_DISABLE /* Disable further calls to this command */
+ PD_CONTROL_DISABLE, /* Disable further calls to this command */
+ PD_CHIP_ON, /* Power on the PD chip */
};
struct __ec_align1 ec_params_pd_control {
@@ -3900,7 +4279,7 @@ struct __ec_align1 ec_params_pd_control {
};
/* Get info about USB-C SS muxes */
-#define EC_CMD_USB_PD_MUX_INFO 0x11a
+#define EC_CMD_USB_PD_MUX_INFO 0x011A
struct __ec_align1 ec_params_usb_pd_mux_info {
uint8_t port; /* USB-C port number */
@@ -3933,7 +4312,24 @@ struct __ec_align2 ec_response_pd_chip_info {
};
};
-#endif /* !__ACPI__ */
+/* Run RW signature verification and get status */
+#define EC_CMD_RWSIG_CHECK_STATUS 0x011C
+
+struct __ec_align4 ec_response_rwsig_check_status {
+ uint32_t status;
+};
+
+/* For controlling RWSIG task */
+#define EC_CMD_RWSIG_ACTION 0x011D
+
+enum rwsig_action {
+ RWSIG_ACTION_ABORT = 0, /* Abort RWSIG and prevent jumping */
+ RWSIG_ACTION_CONTINUE = 1, /* Jump to RW immediately */
+};
+
+struct __ec_align4 ec_params_rwsig_action {
+ uint32_t action;
+};
/*****************************************************************************/
/* The command range 0x200-0x2FF is reserved for Rotor. */
@@ -3942,8 +4338,113 @@ struct __ec_align2 ec_response_pd_chip_info {
/*
* Reserve a range of host commands for the CR51 firmware.
*/
-#define EC_CMD_CR51_BASE 0x300
-#define EC_CMD_CR51_LAST 0x3FF
+#define EC_CMD_CR51_BASE 0x0300
+#define EC_CMD_CR51_LAST 0x03FF
+
+/*****************************************************************************/
+/* Fingerprint MCU commands: range 0x0400-0x040x */
+
+/* Fingerprint SPI sensor passthru command: prototyping ONLY */
+#define EC_CMD_FP_PASSTHRU 0x0400
+
+#define EC_FP_FLAG_NOT_COMPLETE 0x1
+
+struct __ec_align2 ec_params_fp_passthru {
+ uint16_t len; /* Number of bytes to write then read */
+ uint16_t flags; /* EC_FP_FLAG_xxx */
+ uint8_t data[]; /* Data to send */
+};
+
+/* Fingerprint sensor configuration command: prototyping ONLY */
+#define EC_CMD_FP_SENSOR_CONFIG 0x0401
+
+#define EC_FP_SENSOR_CONFIG_MAX_REGS 16
+
+struct __ec_align2 ec_params_fp_sensor_config {
+ uint8_t count; /* Number of setup registers */
+ /*
+ * the value to send to each of the 'count' setup registers
+ * is stored in the 'data' array for 'len' bytes just after
+ * the previous one.
+ */
+ uint8_t len[EC_FP_SENSOR_CONFIG_MAX_REGS];
+ uint8_t data[];
+};
+
+/* Configure the Fingerprint MCU behavior */
+#define EC_CMD_FP_MODE 0x0402
+
+/* Put the sensor in its lowest power mode */
+#define FP_MODE_DEEPSLEEP (1<<0)
+/* Wait to see a finger on the sensor */
+#define FP_MODE_FINGER_DOWN (1<<1)
+/* Poll until the finger has left the sensor */
+#define FP_MODE_FINGER_UP (1<<2)
+/* Capture the current finger image */
+#define FP_MODE_CAPTURE (1<<3)
+/* special value: don't change anything just read back current mode */
+#define FP_MODE_DONT_CHANGE (1<<31)
+
+struct __ec_align4 ec_params_fp_mode {
+ uint32_t mode; /* as defined by FP_MODE_ constants */
+ /* TBD */
+};
+
+struct __ec_align4 ec_response_fp_mode {
+ uint32_t mode; /* as defined by FP_MODE_ constants */
+ /* TBD */
+};
+
+/* Retrieve Fingerprint sensor information */
+#define EC_CMD_FP_INFO 0x0403
+
+struct __ec_align2 ec_response_fp_info {
+ /* Sensor identification */
+ uint32_t vendor_id;
+ uint32_t product_id;
+ uint32_t model_id;
+ uint32_t version;
+ /* Image frame characteristics */
+ uint32_t frame_size;
+ uint32_t pixel_format; /* using V4L2_PIX_FMT_ */
+ uint16_t width;
+ uint16_t height;
+ uint16_t bpp;
+};
+
+/* Get the last captured finger frame: TODO: will be AES-encrypted */
+#define EC_CMD_FP_FRAME 0x0404
+
+struct __ec_align4 ec_params_fp_frame {
+ uint32_t offset;
+ uint32_t size;
+};
+
+/*****************************************************************************/
+/* Touchpad MCU commands: range 0x0500-0x05FF */
+
+/* Perform touchpad self test */
+#define EC_CMD_TP_SELF_TEST 0x0500
+
+/* Get number of frame types, and the size of each type */
+#define EC_CMD_TP_FRAME_INFO 0x0501
+
+struct __ec_align4 ec_response_tp_frame_info {
+ uint32_t n_frames;
+ uint32_t frame_sizes[0];
+};
+
+/* Create a snapshot of current frame readings */
+#define EC_CMD_TP_FRAME_SNAPSHOT 0x0502
+
+/* Read the frame */
+#define EC_CMD_TP_FRAME_GET 0x0503
+
+struct __ec_align4 ec_params_tp_frame_get {
+ uint32_t frame_index;
+ uint32_t offset;
+ uint32_t size;
+};
/*****************************************************************************/
/*
@@ -3953,15 +4454,34 @@ struct __ec_align2 ec_response_pd_chip_info {
* CAUTION: Don't go nuts with this. Shipping products should document ALL
* their EC commands for easier development, testing, debugging, and support.
*
+ * All commands MUST be #defined to be 4-digit UPPER CASE hex values
+ * (e.g., 0x00AB, not 0xab) for CONFIG_HOSTCMD_SECTION_SORTED to work.
+ *
* In your experimental code, you may want to do something like this:
*
- * #define EC_CMD_MAGIC_FOO (EC_CMD_BOARD_SPECIFIC_BASE + 0x000)
- * #define EC_CMD_MAGIC_BAR (EC_CMD_BOARD_SPECIFIC_BASE + 0x001)
- * #define EC_CMD_MAGIC_HEY (EC_CMD_BOARD_SPECIFIC_BASE + 0x002)
+ * #define EC_CMD_MAGIC_FOO 0x0000
+ * #define EC_CMD_MAGIC_BAR 0x0001
+ * #define EC_CMD_MAGIC_HEY 0x0002
+ *
+ * DECLARE_PRIVATE_HOST_COMMAND(EC_CMD_MAGIC_FOO, magic_foo_handler,
+ * EC_VER_MASK(0);
+ *
+ * DECLARE_PRIVATE_HOST_COMMAND(EC_CMD_MAGIC_BAR, magic_bar_handler,
+ * EC_VER_MASK(0);
+ *
+ * DECLARE_PRIVATE_HOST_COMMAND(EC_CMD_MAGIC_HEY, magic_hey_handler,
+ * EC_VER_MASK(0);
*/
#define EC_CMD_BOARD_SPECIFIC_BASE 0x3E00
#define EC_CMD_BOARD_SPECIFIC_LAST 0x3FFF
+/*
+ * Given the private host command offset, calculate the true private host
+ * command value.
+ */
+#define EC_PRIVATE_HOST_COMMAND_VALUE(command) \
+ (EC_CMD_BOARD_SPECIFIC_BASE + (command))
+
/*****************************************************************************/
/*
* Passthru commands
@@ -4000,4 +4520,6 @@ struct __ec_align2 ec_response_pd_chip_info {
#define EC_LPC_ADDR_OLD_PARAM EC_HOST_CMD_REGION1
#define EC_OLD_PARAM_SIZE EC_HOST_CMD_REGION_SIZE
+#endif /* !__ACPI__ */
+
#endif /* __CROS_EC_EC_COMMANDS_H */
diff --git a/include/event_log.h b/include/event_log.h
new file mode 100644
index 0000000000..45b10a3a2d
--- /dev/null
+++ b/include/event_log.h
@@ -0,0 +1,35 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __CROS_EC_EVENT_LOG_H
+#define __CROS_EC_EVENT_LOG_H
+
+struct event_log_entry {
+ uint32_t timestamp; /* relative timestamp in milliseconds */
+ uint8_t type; /* event type, caller-defined */
+ uint8_t size; /* [7:5] caller-def'd [4:0] payload size in bytes */
+ uint16_t data; /* type-defined data payload */
+ uint8_t payload[0]; /* optional additional data payload: 0..16 bytes */
+} __packed;
+
+#define EVENT_LOG_SIZE_MASK 0x1f
+#define EVENT_LOG_SIZE(size) ((size) & EVENT_LOG_SIZE_MASK)
+
+/* The timestamp is the microsecond counter shifted to get about a ms. */
+#define EVENT_LOG_TIMESTAMP_SHIFT 10 /* 1 LSB = 1024us */
+/* Returned in the "type" field, when there is no entry available */
+#define EVENT_LOG_NO_ENTRY 0xff
+
+/* Add an entry to the event log. */
+void log_add_event(uint8_t type, uint8_t size, uint16_t data,
+ void *payload, uint32_t timestamp);
+
+/*
+ * Remove and return an entry from the event log, if available.
+ * Returns size of log entry *r.
+ */
+int log_dequeue_event(struct event_log_entry *r);
+
+#endif /* __CROS_EC_EVENT_LOG_H */
diff --git a/include/extension.h b/include/extension.h
index 3b960f5439..e5f183eb49 100644
--- a/include/extension.h
+++ b/include/extension.h
@@ -26,8 +26,12 @@ typedef enum vendor_cmd_rc (*extension_handler)(enum vendor_cmd_cc code,
size_t command_size,
size_t *response_size);
-/*
+/**
* Find handler for an extension command.
+ *
+ * Use the interface specific function call in order to check the policies for
+ * handling the commands on that interface.
+ *
* @param command_code Code associated with a extension command handler.
* @param buffer Data to be processd by the handler, the same space
* is used for data returned by the handler.
@@ -36,10 +40,14 @@ typedef enum vendor_cmd_rc (*extension_handler)(enum vendor_cmd_cc code,
* data returned by the handler. A single byte return
* usually indicates an error and contains the error code.
*/
-uint32_t extension_route_command(uint16_t command_code,
+void usb_extension_route_command(uint16_t command_code,
void *buffer,
size_t command_size,
size_t *size);
+uint32_t tpm_extension_route_command(uint16_t command_code,
+ void *buffer,
+ size_t command_size,
+ size_t *size);
/* Pointer table */
struct extension_command {
diff --git a/include/flash.h b/include/flash.h
index e0873028f5..c769f5753d 100644
--- a/include/flash.h
+++ b/include/flash.h
@@ -11,26 +11,95 @@
#include "common.h"
#include "ec_commands.h" /* For EC_FLASH_PROTECT_* flags */
+#ifdef CONFIG_FLASH_MULTIPLE_REGION
+extern struct ec_flash_bank const flash_bank_array[
+ CONFIG_FLASH_REGION_TYPE_COUNT];
+
+/*
+ * Return the bank the offset is in.
+ * Return -1 if the offset is not at the beginning of that bank.
+ */
+int flash_bank_index(int offset);
+
+/*
+ * Number of banks between offset and offset+size.
+ *
+ * offset and offset + size should be addresses at the beginning of bank:
+ * 0 32
+ * +-------------------+--------...
+ * | bank 0 | bank 1 ...
+ * +-------------------+--------...
+ * In that case, begin = 0, end = 1, return is 1.
+ * otherwise, this is an error:
+ * 0 32 64
+ * +----------+--------+--------...
+ * | bank 0 | bank 1 ...
+ * +----------+--------+--------...
+ * begin = 0, end = -1....
+ * The idea is to prevent erasing more than you think.
+ */
+int flash_bank_count(int offset, int size);
+
+/*
+ * Return the size of the specified bank in bytes.
+ * Return -1 if the bank is too large.
+ */
+int flash_bank_size(int bank);
+
/* Number of physical flash banks */
+#define PHYSICAL_BANKS CONFIG_FLASH_MULTIPLE_REGION
+
+/* WP region offset and size in units of flash banks */
+#define WP_BANK_OFFSET flash_bank_index(CONFIG_WP_STORAGE_OFF)
+#define WP_BANK_COUNT \
+ (flash_bank_count(CONFIG_WP_STORAGE_OFF, CONFIG_WP_STORAGE_SIZE))
+
+#else /* CONFIG_FLASH_MULTIPLE_REGION */
+/* Number of physical flash banks */
+#ifndef PHYSICAL_BANKS
#define PHYSICAL_BANKS (CONFIG_FLASH_SIZE / CONFIG_FLASH_BANK_SIZE)
+#endif
-/*WP region offset and size in units of flash banks */
+/* WP region offset and size in units of flash banks */
#define WP_BANK_OFFSET (CONFIG_WP_STORAGE_OFF / CONFIG_FLASH_BANK_SIZE)
+#ifndef WP_BANK_COUNT
#define WP_BANK_COUNT (CONFIG_WP_STORAGE_SIZE / CONFIG_FLASH_BANK_SIZE)
+#endif
+#endif /* CONFIG_FLASH_MULTIPLE_REGION */
/* Persistent protection state flash offset / size / bank */
#if defined(CONFIG_FLASH_PSTATE) && defined(CONFIG_FLASH_PSTATE_BANK)
+
+#ifdef CONFIG_FLASH_MULTIPLE_REGION
+#error "Not supported."
+#endif
+
+#ifndef PSTATE_BANK
#define PSTATE_BANK (CONFIG_FW_PSTATE_OFF / CONFIG_FLASH_BANK_SIZE)
+#endif
+#ifndef PSTATE_BANK_COUNT
#define PSTATE_BANK_COUNT (CONFIG_FW_PSTATE_SIZE / CONFIG_FLASH_BANK_SIZE)
-#else
+#endif
+#else /* CONFIG_FLASH_PSTATE && CONFIG_FLASH_PSTATE_BANK */
#define PSTATE_BANK_COUNT 0
+#endif /* CONFIG_FLASH_PSTATE && CONFIG_FLASH_PSTATE_BANK */
+
+#ifdef CONFIG_ROLLBACK
+/*
+ * ROLLBACK region offset and size in units of flash banks.
+ */
+#define ROLLBACK_BANK_OFFSET (CONFIG_ROLLBACK_OFF / CONFIG_FLASH_BANK_SIZE)
+#define ROLLBACK_BANK_COUNT (CONFIG_ROLLBACK_SIZE / CONFIG_FLASH_BANK_SIZE)
#endif
-/* Range of write protection */
-enum flash_wp_range {
- FLASH_WP_NONE = 0,
- FLASH_WP_RO,
- FLASH_WP_ALL,
+/* This enum is useful to identify different regions during verification. */
+enum flash_region {
+ FLASH_REGION_RW = 0,
+ FLASH_REGION_RO,
+#ifdef CONFIG_ROLLBACK
+ FLASH_REGION_ROLLBACK,
+#endif
+ FLASH_REGION_COUNT
};
/*****************************************************************************/
@@ -86,10 +155,11 @@ uint32_t flash_physical_get_protect_flags(void);
/**
* Enable/disable protecting firmware/pstate at boot.
*
- * @param range The range to protect
+ * @param new_flags to protect (only EC_FLASH_PROTECT_*_AT_BOOT are
+ * taken care of)
* @return non-zero if error.
*/
-int flash_physical_protect_at_boot(enum flash_wp_range range);
+int flash_physical_protect_at_boot(uint32_t new_flags);
/**
* Protect flash now.
@@ -154,10 +224,11 @@ int flash_is_erased(uint32_t offset, int size);
* protect pin is deasserted, the protect setting is ignored, and the entire
* flash will be writable.
*
- * @param range The range to protect.
+ * @param new_flags to protect (only EC_FLASH_PROTECT_*_AT_BOOT are
+ * taken care of)
* @return EC_SUCCESS, or nonzero if error.
*/
-int flash_protect_at_boot(enum flash_wp_range range);
+int flash_protect_at_boot(uint32_t new_flags);
/*****************************************************************************/
/* High-level interface for use by other modules. */
@@ -249,17 +320,18 @@ int flash_set_protect(uint32_t mask, uint32_t flags);
* Get the serial number from flash.
*
* @return char * ascii serial number string.
+ * NULL if error.
*/
-const char *flash_read_serial(void);
+const char *flash_read_pstate_serial(void);
/**
* Set the serial number in flash.
*
- * @param serialno ascii serial number string < 30 char.
+ * @param serialno ascii serial number string.
*
* @return success status.
*/
-int flash_write_serial(const char *serialno);
+int flash_write_pstate_serial(const char *serialno);
/**
* Lock or unlock HW necessary for mapped storage read.
@@ -271,4 +343,14 @@ void flash_lock_mapped_storage(int lock);
#else
static inline void flash_lock_mapped_storage(int lock) { };
#endif /* CONFIG_EXTERNAL_STORAGE */
+
+/**
+ * Select flash for performing flash operations. Board should implement this
+ * if some steps needed be done before flash operation can succeed.
+ *
+ * @param select 1 to select flash, 0 to deselect (disable).
+ * @return EC_RES_* status code.
+ */
+int board_flash_select(int select);
+
#endif /* __CROS_EC_FLASH_H */
diff --git a/include/gpio.h b/include/gpio.h
index b588659a88..195f644d67 100644
--- a/include/gpio.h
+++ b/include/gpio.h
@@ -31,6 +31,8 @@
#define GPIO_INT_SHARED (1 << 15) /* Shared among multiple pins */
#define GPIO_SEL_1P8V (1 << 16) /* Support 1.8v */
#define GPIO_ALTERNATE (1 << 17) /* GPIO used for alternate function. */
+#define GPIO_LOCKED (1 << 18) /* Lock GPIO output and configuration */
+#define GPIO_HIB_WAKE_HIGH (1 << 19) /* Hibernate wake on high level */
/* Common flag combinations */
#define GPIO_OUT_LOW (GPIO_OUTPUT | GPIO_LOW)
@@ -121,6 +123,15 @@ int gpio_config_pin(enum module_id id, enum gpio_signal signal, int enable);
int gpio_get_level(enum gpio_signal signal);
/**
+ * Read a ternary GPIO input, activating internal pull-down, then pull-up,
+ * to check if the GPIO is high, low, or Hi-Z. Useful for board strappings.
+ *
+ * @param signal Signal to get
+ * @return 0 if low, 1 if high, 2 if Hi-Z.
+ */
+int gpio_get_ternary(enum gpio_signal signal);
+
+/**
* Return the name of a given GPIO signal.
*
* @param signal Signal to name
@@ -216,6 +227,16 @@ int gpio_enable_interrupt(enum gpio_signal signal);
int gpio_disable_interrupt(enum gpio_signal signal);
/**
+ * Clear pending interrupts for the signal.
+ *
+ * The signal must have been defined with an interrupt handler.
+ *
+ * @param signal Signal to clear interrupts for
+ * @return EC_SUCCESS, or non-zero on error.
+ */
+int gpio_clear_pending_interrupt(enum gpio_signal signal);
+
+/**
* Set flags for GPIO(s) by port and mask.
*
* Use gpio_set_flags() to set flags for an individual GPIO by id.
diff --git a/include/hooks.h b/include/hooks.h
index 1c6f5fe274..04206efde7 100644
--- a/include/hooks.h
+++ b/include/hooks.h
@@ -29,18 +29,20 @@ enum hook_priority {
HOOK_PRIO_INIT_LID = HOOK_PRIO_FIRST + 4,
/* Power button inits before chipset and switch */
HOOK_PRIO_INIT_POWER_BUTTON = HOOK_PRIO_FIRST + 5,
+ /* Init switch states after power button / lid */
+ HOOK_PRIO_INIT_SWITCH = HOOK_PRIO_FIRST + 6,
/* Init fan before PWM */
- HOOK_PRIO_INIT_FAN = HOOK_PRIO_FIRST + 6,
+ HOOK_PRIO_INIT_FAN = HOOK_PRIO_FIRST + 7,
/* PWM inits before modules which might use it (LEDs) */
- HOOK_PRIO_INIT_PWM = HOOK_PRIO_FIRST + 7,
+ HOOK_PRIO_INIT_PWM = HOOK_PRIO_FIRST + 8,
/* SPI inits before modules which might use it (sensors) */
- HOOK_PRIO_INIT_SPI = HOOK_PRIO_FIRST + 8,
+ HOOK_PRIO_INIT_SPI = HOOK_PRIO_FIRST + 9,
/* Extpower inits before modules which might use it (battery, LEDs) */
- HOOK_PRIO_INIT_EXTPOWER = HOOK_PRIO_FIRST + 9,
+ HOOK_PRIO_INIT_EXTPOWER = HOOK_PRIO_FIRST + 10,
/* Init VBOOT hash later, since it depends on deferred functions */
- HOOK_PRIO_INIT_VBOOT_HASH = HOOK_PRIO_FIRST + 10,
+ HOOK_PRIO_INIT_VBOOT_HASH = HOOK_PRIO_FIRST + 11,
/* Init charge manager before usage in board init */
- HOOK_PRIO_CHARGE_MANAGER_INIT = HOOK_PRIO_FIRST + 11,
+ HOOK_PRIO_CHARGE_MANAGER_INIT = HOOK_PRIO_FIRST + 12,
/* Specific values to lump temperature-related hooks together */
HOOK_PRIO_TEMP_SENSOR = 6000,
@@ -154,18 +156,20 @@ enum hook_type {
HOOK_POWER_BUTTON_CHANGE,
/*
- * Charge state machine status changed.
+ * Battery state of charge changed
*
* Hook routines are called from the charger task.
*/
- HOOK_CHARGE_STATE_CHANGE,
+ HOOK_BATTERY_SOC_CHANGE,
+#ifdef CONFIG_CASE_CLOSED_DEBUG_V1
/*
- * Battery state of charge changed
+ * Case-closed debugging configuration changed.
*
- * Hook routines are called from the charger task.
+ * Hook routines are called from the TICK, console, or TPM task.
*/
- HOOK_BATTERY_SOC_CHANGE,
+ HOOK_CCD_CHANGE,
+#endif
/*
* Periodic tick, every HOOK_TICK_INTERVAL.
diff --git a/include/host_command.h b/include/host_command.h
index c9dcd3ddd8..78157231ca 100644
--- a/include/host_command.h
+++ b/include/host_command.h
@@ -48,8 +48,13 @@ struct host_cmd_handler_args {
* command execution is complete. The driver may still override this
* when sending the response back to the host if it detects an error
* in the response or in its own operation.
+ *
+ * Note that while this holds an ec_status enum, we are intentionally
+ * representing this field as a uint16_t, to prevent issues related to
+ * compiler optimizations affecting the range of values representable
+ * by this field.
*/
- enum ec_status result;
+ uint16_t result;
};
/* Args for host packet handler */
@@ -95,8 +100,13 @@ struct host_packet {
* Error from driver; if this is non-zero, host command handler will
* return a properly formatted error response packet rather than
* calling a command handler.
+ *
+ * Note that while this holds an ec_status enum, we are intentionally
+ * representing this field as a uint16_t, to prevent issues related to
+ * compiler optimizations affecting the range of values representable
+ * by this field.
*/
- enum ec_status driver_result;
+ uint16_t driver_result;
};
/* Host command */
@@ -127,10 +137,14 @@ uint8_t *host_get_memmap(int offset);
* Process a host command and return its response
*
* @param args Command handler args
- * @return resulting status
+ * @return resulting status. Note that while this returns an ec_status enum, we
+ * are intentionally specifying the return type as a uint16_t, to prevent issues
+ * related to compiler optimizations affecting the range of values returnable
+ * from this function.
*/
-enum ec_status host_command_process(struct host_cmd_handler_args *args);
+uint16_t host_command_process(struct host_cmd_handler_args *args);
+#ifdef CONFIG_HOSTCMD_EVENTS
/**
* Set one or more host event bits.
*
@@ -162,7 +176,19 @@ void host_clear_events(uint32_t mask);
uint32_t host_get_events(void);
/**
- * Send a response to the relevent driver for transmission
+ * Check a single host event.
+ *
+ * @param event Event to check
+ * @return true if <event> is set or false otherwise
+ */
+static inline int host_is_event_set(enum host_event_code event)
+{
+ return host_get_events() & EC_HOST_EVENT_MASK(event);
+}
+#endif
+
+/**
+ * Send a response to the relevant driver for transmission
*
* Once command processing is complete, this is used to send a response
* back to the host.
@@ -193,18 +219,39 @@ int host_request_expected_size(const struct ec_host_request *r);
*/
void host_packet_receive(struct host_packet *pkt);
-/* Register a host command handler */
#ifdef HAS_TASK_HOSTCMD
-#define DECLARE_HOST_COMMAND(command, routine, version_mask) \
- const struct host_command __keep __host_cmd_##command \
- __attribute__((section(".rodata.hcmds"))) \
- = {routine, command, version_mask}
+#define EXPAND(off, cmd) __host_cmd_(off, cmd)
+#define __host_cmd_(off, cmd) __host_cmd_##off##cmd
+#define EXPANDSTR(off, cmd) "__host_cmd_"#off#cmd
+
+/*
+ * Register a host command handler with
+ * commands starting at offset 0x0000
+ */
+#define DECLARE_HOST_COMMAND(command, routine, version_mask) \
+ const struct host_command __keep EXPAND(0x0000, command) \
+ __attribute__((section(".rodata.hcmds."EXPANDSTR(0x0000, command)))) \
+ = {routine, command, version_mask}
+
+/*
+ * Register a private host command handler with
+ * commands starting at offset EC_CMD_BOARD_SPECIFIC_BASE,
+ */
+#define DECLARE_PRIVATE_HOST_COMMAND(command, routine, version_mask) \
+ const struct host_command __keep \
+ EXPAND(EC_CMD_BOARD_SPECIFIC_BASE, command) \
+ __attribute__((section(".rodata.hcmds."\
+ EXPANDSTR(EC_CMD_BOARD_SPECIFIC_BASE, command)))) \
+ = {routine, EC_PRIVATE_HOST_COMMAND_VALUE(command), \
+ version_mask}
#else
-#define DECLARE_HOST_COMMAND(command, routine, version_mask) \
- int (routine)(struct host_cmd_handler_args *args) \
- __attribute__((unused))
-#endif
+#define DECLARE_HOST_COMMAND(command, routine, version_mask) \
+ int (routine)(struct host_cmd_handler_args *args) \
+ __attribute__((unused))
+#define DECLARE_PRIVATE_HOST_COMMAND(command, routine, version_mask) \
+ DECLARE_HOST_COMMAND(command, routine, version_mask)
+#endif
/**
* Politely ask the CPU to enable/disable its own throttling.
@@ -258,4 +305,16 @@ int pd_host_command(int command, int version,
*/
int host_get_vboot_mode(void);
+/*
+ * Sends an emulated sysrq to the host, used by button-based debug mode.
+ * Only implemented on top of MKBP protocol.
+ *
+ * @param key Key to be sent (e.g. 'x')
+ */
+void host_send_sysrq(uint8_t key);
+
+/* Return the lower/higher part of the feature flags bitmap */
+uint32_t get_feature_flags0(void);
+uint32_t get_feature_flags1(void);
+
#endif /* __CROS_EC_HOST_COMMAND_H */
diff --git a/include/i2c.h b/include/i2c.h
index d3329df64a..73716cf705 100644
--- a/include/i2c.h
+++ b/include/i2c.h
@@ -9,11 +9,18 @@
#define __CROS_EC_I2C_H
#include "common.h"
+#include "host_command.h"
/* Flags for slave address field, in addition to the 8-bit address */
#define I2C_FLAG_BIG_ENDIAN 0x100 /* 16 byte values are MSB-first */
/*
+ * Max data size for a version 3 request/response packet. This is
+ * big enough for EC_CMD_GET_VERSION plus header info.
+ */
+#define I2C_MAX_HOST_PACKET_SIZE 128
+
+/*
* Supported I2C CLK frequencies.
* TODO(crbug.com/549286): Use this enum in i2c_port_t.
*/
@@ -40,6 +47,42 @@ struct i2c_port_t {
extern const struct i2c_port_t i2c_ports[];
extern const unsigned int i2c_ports_used;
+#ifdef CONFIG_CMD_I2C_STRESS_TEST
+struct i2c_test_reg_info {
+ int read_reg; /* Read register (WHO_AM_I, DEV_ID, MAN_ID) */
+ int read_val; /* Expected val (WHO_AM_I, DEV_ID, MAN_ID) */
+ int write_reg; /* Read/Write reg which doesn't impact the system */
+};
+
+struct i2c_test_results {
+ int read_success; /* Successful read count */
+ int read_fail; /* Read fail count */
+ int write_success; /* Successful write count */
+ int write_fail; /* Write fail count */
+};
+
+/* Data structure to define I2C test configuration. */
+struct i2c_stress_test_dev {
+ struct i2c_test_reg_info reg_info;
+ struct i2c_test_results test_results;
+ int (*i2c_read)(const int port, const int addr,
+ const int reg, int *data);
+ int (*i2c_write)(const int port, const int addr,
+ const int reg, int data);
+ int (*i2c_read_dev)(const int reg, int *data);
+ int (*i2c_write_dev)(const int reg, int data);
+};
+
+struct i2c_stress_test {
+ int port;
+ int addr;
+ struct i2c_stress_test_dev *i2c_test;
+};
+
+extern struct i2c_stress_test i2c_stress_tests[];
+extern const int i2c_test_dev_used;
+#endif
+
/* Flags for i2c_xfer() */
#define I2C_XFER_START (1 << 0) /* Start smbus session from idle state */
#define I2C_XFER_STOP (1 << 1) /* Terminate smbus session with stop bit */
@@ -231,7 +274,7 @@ int i2c_unwedge(int port);
* Read bytestream from <slaveaddr>:<offset> with format:
* [length_N] [byte_0] [byte_1] ... [byte_N-1]
*
- * <len> : the max length of receving buffer. to read N bytes
+ * <len> : the max length of receiving buffer. to read N bytes
* ascii, len should be at least N+1 to include the
* terminating 0.
* <len> == 0 : buffer size > 255
@@ -250,4 +293,44 @@ int i2c_read_string(int port, int slave_addr, int offset, uint8_t *data,
*/
int i2c_port_to_controller(int port);
+/**
+ * Command handler to get host command protocol information
+ *
+ * @param args: host command handler arguments
+ * @return EC_SUCCESS
+ */
+int i2c_get_protocol_info(struct host_cmd_handler_args *args);
+
+/**
+ * Callbacks processing received data and response
+ *
+ * i2c_data_recived will be called when a slave finishes receiving data and
+ * i2c_set_response will be called when a slave is expected to send response.
+ *
+ * Using these, Chrome OS host command protocol should be separated from
+ * i2c slave drivers (e.g. i2c-stm32f0.c, i2c-stm32f3.c).
+ *
+ * @param port: I2C port number
+ * @param buf: Buffer containing received data on call and response on return
+ * @param len: Size of received data
+ * @return Size of response data
+ */
+void i2c_data_received(int port, uint8_t *buf, int len);
+int i2c_set_response(int port, uint8_t *buf, int len);
+
+/**
+ * Initialize i2c master ports. This function can be called for cases where i2c
+ * ports are not initialized by default via a hook call.
+ */
+void i2cm_init(void);
+
+/**
+ * Board-level function to determine whether i2c passthru should be allowed
+ * on a given port.
+ *
+ * @parm port I2C port
+ *
+ * @return true, if passthru should be allowed on the port.
+ */
+int board_allow_i2c_passthru(int port);
#endif /* __CROS_EC_I2C_H */
diff --git a/include/keyboard_8042.h b/include/keyboard_8042.h
index 2d58c4c184..00fdc9901b 100644
--- a/include/keyboard_8042.h
+++ b/include/keyboard_8042.h
@@ -28,11 +28,4 @@ void button_state_changed(enum keyboard_button_type button, int is_pressed);
*/
void keyboard_host_write(int data, int is_cmd);
-/**
- * Called by keyboard scan code once any key state change (after de-bounce),
- *
- * This function will look up matrix table and convert scancode host.
- */
-void keyboard_state_changed(int row, int col, int is_pressed);
-
#endif /* __CROS_EC_KEYBOARD_8042_H */
diff --git a/include/keyboard_8042_sharedlib.h b/include/keyboard_8042_sharedlib.h
index 054795e86d..5c9b559279 100644
--- a/include/keyboard_8042_sharedlib.h
+++ b/include/keyboard_8042_sharedlib.h
@@ -19,8 +19,13 @@ struct button_8042_t {
};
/* The standard Chrome OS keyboard matrix table. */
+#ifdef CONFIG_KEYBOARD_SCANCODE_MUTABLE
+extern uint16_t scancode_set1[KEYBOARD_ROWS][KEYBOARD_COLS];
+extern uint16_t scancode_set2[KEYBOARD_ROWS][KEYBOARD_COLS];
+#else
extern const uint16_t scancode_set1[KEYBOARD_ROWS][KEYBOARD_COLS];
extern const uint16_t scancode_set2[KEYBOARD_ROWS][KEYBOARD_COLS];
+#endif
/* Button scancodes (Power, Volume Down, Volume Up, etc.) */
extern const struct button_8042_t buttons_8042[KEYBOARD_BUTTON_COUNT];
diff --git a/include/keyboard_config.h b/include/keyboard_config.h
index 6cb1b53a52..6e6fcef7f8 100644
--- a/include/keyboard_config.h
+++ b/include/keyboard_config.h
@@ -60,5 +60,8 @@
#define KEYBOARD_ROW_KEY_2 6
#define KEYBOARD_MASK_KEY_2 KEYBOARD_ROW_TO_MASK(KEYBOARD_ROW_KEY_2)
#define KEYBOARD_MASK_KSI2 KEYBOARD_ROW_TO_MASK(2)
+#define KEYBOARD_COL_LEFT_SHIFT 7
+#define KEYBOARD_ROW_LEFT_SHIFT 5
+#define KEYBOARD_MASK_LEFT_SHIFT KEYBOARD_ROW_TO_MASK(KEYBOARD_ROW_LEFT_SHIFT)
#endif /* __CROS_EC_KEYBOARD_CONFIG_H */
diff --git a/include/keyboard_protocol.h b/include/keyboard_protocol.h
index 6fc0a06f7c..a8eafe308d 100644
--- a/include/keyboard_protocol.h
+++ b/include/keyboard_protocol.h
@@ -35,6 +35,18 @@ void keyboard_update_button(enum keyboard_button_type button, int is_pressed);
#ifdef CONFIG_KEYBOARD_PROTOCOL_MKBP
#include "keyboard_mkbp.h"
+
+/* MKBP protocol takes the whole keyboard matrix, and does not care about
+ * individual key presses.
+ */
+static inline void keyboard_state_changed(int row, int col, int is_pressed) {}
+#else
+/**
+ * Called by keyboard scan code once any key state change (after de-bounce),
+ *
+ * This function will look up matrix table and convert scancode host.
+ */
+void keyboard_state_changed(int row, int col, int is_pressed);
#endif
#endif /* __CROS_EC_KEYBOARD_PROTOCOL_H */
diff --git a/include/keyboard_scan.h b/include/keyboard_scan.h
index 41cdbdf611..bce5046c9e 100644
--- a/include/keyboard_scan.h
+++ b/include/keyboard_scan.h
@@ -49,21 +49,28 @@ extern struct keyboard_scan_config keyscan_config;
/* Key held down at keyboard-controlled reset boot time. */
enum boot_key {
- BOOT_KEY_NONE, /* No keys other than keyboard-controlled reset keys */
- BOOT_KEY_ESC,
- BOOT_KEY_DOWN_ARROW,
- BOOT_KEY_OTHER = -1, /* None of the above */
+ /* No keys other than keyboard-controlled reset keys */
+ BOOT_KEY_NONE = 0,
+ BOOT_KEY_ESC = (1 << 0),
+ BOOT_KEY_DOWN_ARROW = (1 << 1),
+ BOOT_KEY_LEFT_SHIFT = (1 << 2),
};
-#ifdef HAS_TASK_KEYSCAN
+#if defined(HAS_TASK_KEYSCAN) && defined(CONFIG_KEYBOARD_BOOT_KEYS)
/**
- * Return the key held down at boot time in addition to the keyboard-controlled
- * reset keys. Returns BOOT_KEY_OTHER if none of the keys specifically checked
- * was pressed, or reset was not caused by a keyboard-controlled reset.
+ * Returns mask of all the keys held down at boot time in addition to the
+ * keyboard-controlled reset keys. If more than one boot key is held, mask bits
+ * will be set for each of those keys. Since more than one bit can be set,
+ * caller needs to ensure that boot keys match as intended.
+ *
+ * Returns BOOT_NONE if no additional key is held or if none of the keys
+ * specifically checked was pressed, or reset was not caused by a
+ * keyboard-controlled reset or if any key *other* than boot keys, power, or
+ * refresh is also pressed.
*/
-enum boot_key keyboard_scan_get_boot_key(void);
+uint32_t keyboard_scan_get_boot_keys(void);
#else
-static inline enum boot_key keyboard_scan_get_boot_key(void)
+static inline uint32_t keyboard_scan_get_boot_keys(void)
{
return BOOT_KEY_NONE;
}
@@ -91,6 +98,11 @@ enum kb_scan_disable_masks {
* @param mask Disable reasons from kb_scan_disable_masks
*/
void keyboard_scan_enable(int enable, enum kb_scan_disable_masks mask);
+
+/**
+ * Clears typematic key
+ */
+void clear_typematic_key(void);
#else
static inline void keyboard_scan_enable(int enable,
enum kb_scan_disable_masks mask) { }
diff --git a/include/led_common.h b/include/led_common.h
index 51230fbeb3..e86ed5b39c 100644
--- a/include/led_common.h
+++ b/include/led_common.h
@@ -17,7 +17,7 @@ extern const enum ec_led_id supported_led_ids[];
extern const int supported_led_ids_count;
/**
- * Enable or diable automatic control of an LED.
+ * Enable or disable automatic control of an LED.
*
* @param led_id ID of LED to enable or disable automatic control.
* @param enable 1 to enable . 0 to disable
@@ -67,4 +67,20 @@ int led_set_brightness(enum ec_led_id led_id, const uint8_t *brightness);
*
*/
void led_enable(int enable);
+
+enum ec_led_state {
+ LED_STATE_OFF = 0,
+ LED_STATE_ON = 1,
+ LED_STATE_RESET = 2,
+};
+
+/**
+ * Control state of LED.
+ *
+ * @param led_id ID of LED to control
+ * @param state 0=off, 1=on, 2=reset to default
+ *
+ */
+void led_control(enum ec_led_id id, enum ec_led_state state);
+
#endif /* __CROS_EC_LED_COMMON_H */
diff --git a/include/link_defs.h b/include/link_defs.h
index 69b964d1d8..9e621ebce7 100644
--- a/include/link_defs.h
+++ b/include/link_defs.h
@@ -52,10 +52,12 @@ extern const struct hook_data __hooks_tablet_mode_change[];
extern const struct hook_data __hooks_tablet_mode_change_end[];
extern const struct hook_data __hooks_pwrbtn_change[];
extern const struct hook_data __hooks_pwrbtn_change_end[];
-extern const struct hook_data __hooks_charge_state_change[];
-extern const struct hook_data __hooks_charge_state_change_end[];
extern const struct hook_data __hooks_battery_soc_change[];
extern const struct hook_data __hooks_battery_soc_change_end[];
+#ifdef CONFIG_CASE_CLOSED_DEBUG_V1
+extern const struct hook_data __hooks_ccd_change[];
+extern const struct hook_data __hooks_ccd_change_end[];
+#endif
extern const struct hook_data __hooks_tick[];
extern const struct hook_data __hooks_tick_end[];
extern const struct hook_data __hooks_second[];
@@ -87,6 +89,12 @@ extern const void *__irqhandler[];
/* Shared memory buffer. Use via shared_mem.h interface. */
extern uint8_t __shared_mem_buf[];
+/* Image sections used by the TPM2 library */
+extern uint8_t *__bss_libtpm2_start;
+extern uint8_t *__bss_libtpm2_end;
+extern uint8_t *__data_libtpm2_start;
+extern uint8_t *__data_libtpm2_end;
+
/* Image sections. */
extern const void *__ro_end;
extern const void *__data_start;
diff --git a/include/lpc.h b/include/lpc.h
index 3b77736f00..7b9a7d7012 100644
--- a/include/lpc.h
+++ b/include/lpc.h
@@ -71,17 +71,21 @@ enum lpc_host_event_type {
LPC_HOST_EVENT_SMI = 0,
LPC_HOST_EVENT_SCI,
LPC_HOST_EVENT_WAKE,
+ LPC_HOST_EVENT_ALWAYS_REPORT,
+ LPC_HOST_EVENT_COUNT,
};
/**
- * Set the event state.
+ * Get current state of host events.
*/
-void lpc_set_host_event_state(uint32_t mask);
+uint32_t lpc_get_host_events(void);
/**
- * Clear and return the lowest host event.
+ * Get host events that are set based on the type provided.
+ *
+ * @param type Event type
*/
-int lpc_query_host_event_state(void);
+uint32_t lpc_get_host_events_by_type(enum lpc_host_event_type type);
/**
* Set the event mask for the specified event type.
@@ -92,11 +96,18 @@ int lpc_query_host_event_state(void);
void lpc_set_host_event_mask(enum lpc_host_event_type type, uint32_t mask);
/**
- * Return the event mask for the specified event type.
+ * Get host event mask based on the type provided.
+ *
+ * @param type Event type
*/
uint32_t lpc_get_host_event_mask(enum lpc_host_event_type type);
/**
+ * Clear and return the lowest host event.
+ */
+int lpc_get_next_host_event(void);
+
+/**
* Set the EC_LPC_STATUS_* mask for the specified status.
*/
void lpc_set_acpi_status_mask(uint8_t mask);
@@ -113,6 +124,10 @@ void lpc_clear_acpi_status_mask(uint8_t mask);
*/
int lpc_get_pltrst_asserted(void);
+/**
+ * Reset the host with KBRST# or RCIN#
+ */
+void lpc_host_reset(void);
/* Disable LPC ACPI interrupts */
void lpc_disable_acpi_interrupts(void);
@@ -120,7 +135,27 @@ void lpc_disable_acpi_interrupts(void);
/* Enable LPC ACPI interrupts */
void lpc_enable_acpi_interrupts(void);
-void lpc_enable_wake_mask_for_lid_open(void);
+/**
+ * Update host event status. This function is called whenever host event bits
+ * need to be updated based on initialization complete or host event mask
+ * update or when a new host event is set or cleared.
+ */
+void lpc_update_host_event_status(void);
+
+/*
+ * This is a weak function defined in host_events_commands.c to override the
+ * LPC_HOST_EVENT_ALWAYS_REPORT mask. It can be implemented by boards if there
+ * is a need to use custom mask.
+ */
+uint32_t lpc_override_always_report_mask(void);
+
+/* Initialize LPC masks. */
+void lpc_init_mask(void);
+
+/*
+ * Clear LPC masks for SMI, SCI and wake upon resume from S3. This is done to
+ * mask these events until host unmasks them itself.
+ */
+void lpc_s3_resume_clear_masks(void);
-void lpc_disable_wake_mask_for_lid_open(void);
#endif /* __CROS_EC_LPC_H */
diff --git a/include/mkbp_event.h b/include/mkbp_event.h
index 6e97c2e973..cad43444b5 100644
--- a/include/mkbp_event.h
+++ b/include/mkbp_event.h
@@ -15,8 +15,9 @@
* when the AP queries the event, an error is returned and the event is lost.
*
* @param event_type One of EC_MKBP_EVENT_*.
+ * @return True if event succeeded to generate host interrupt.
*/
-void mkbp_send_event(uint8_t event_type);
+int mkbp_send_event(uint8_t event_type);
/*
* The struct to store the event source definition. The get_data routine is
diff --git a/include/motion_lid.h b/include/motion_lid.h
index 405d5e5e5d..155c7735c9 100644
--- a/include/motion_lid.h
+++ b/include/motion_lid.h
@@ -46,12 +46,6 @@ int host_cmd_motion_lid(struct host_cmd_handler_args *args);
void motion_lid_calc(void);
-#ifdef CONFIG_LID_ANGLE_TABLET_MODE
-int motion_lid_in_tablet_mode(void);
-#else
-static inline int motion_lid_in_tablet_mode(void) { return 0; }
-#endif
-
#endif /* __CROS_EC_MOTION_LID_H */
diff --git a/include/motion_sense.h b/include/motion_sense.h
index 98c27ae04d..478468022e 100644
--- a/include/motion_sense.h
+++ b/include/motion_sense.h
@@ -45,6 +45,12 @@ enum sensor_config {
#define ROUND_UP_FLAG (1 << 31)
#define BASE_ODR(_odr) ((_odr) & ~ROUND_UP_FLAG)
+#ifdef CONFIG_ACCEL_FIFO
+#define MAX_FIFO_EVENT_COUNT CONFIG_ACCEL_FIFO
+#else
+#define MAX_FIFO_EVENT_COUNT 0
+#endif
+
struct motion_data_t {
/*
* data rate the sensor will measure, in mHz: 0 suspended.
@@ -78,6 +84,12 @@ struct motion_sensor_t {
/* i2c address or SPI slave logic GPIO. */
uint8_t addr;
+ /*
+ * When non-zero, spoof mode will allow the EC to report arbitrary
+ * values for any of the components.
+ */
+ uint8_t in_spoof_mode;
+
const matrix_3x3_t *rot_standard_ref;
/*
@@ -108,6 +120,7 @@ struct motion_sensor_t {
enum sensor_state state;
vector_3_t raw_xyz;
vector_3_t xyz;
+ vector_3_t spoof_xyz;
/* How many flush events are pending */
uint32_t flush_pending;
@@ -134,11 +147,25 @@ struct motion_sensor_t {
* from sensor registers.
*/
uint32_t last_collection;
+
+ /* Minimum supported sampling frequency in miliHertz for this sensor */
+ uint32_t min_frequency;
+
+ /* Maximum supported sampling frequency in miliHertz for this sensor */
+ uint32_t max_frequency;
};
/* Defined at board level. */
extern struct motion_sensor_t motion_sensors[];
+#ifdef CONFIG_DYNAMIC_MOTION_SENSOR_COUNT
+extern unsigned motion_sensor_count;
+#else
extern const unsigned motion_sensor_count;
+#endif
+#if (!defined HAS_TASK_ALS) && (defined CONFIG_ALS)
+/* Needed if reading ALS via LPC is needed */
+extern const struct motion_sensor_t *motion_als_sensors[];
+#endif
/* optionally defined at board level */
extern unsigned int motion_min_interval;
@@ -165,7 +192,28 @@ void motion_sense_fifo_add_unit(struct ec_response_motion_sensor_data *data,
#endif
-#ifdef CONFIG_GESTURE_HOST_DETECTION
+/**
+ * Take actions at end of sensor initialization (currently only printing
+ * init done status to console).
+ *
+ * @param sensor sensor which was just initialized
+ * @param range range of sensor
+ */
+void sensor_init_done(const struct motion_sensor_t *sensor, int range);
+
+/**
+ * Board specific function that is called when a double_tap event is detected.
+ *
+ */
+void sensor_board_proc_double_tap(void);
+
+#ifdef CONFIG_ORIENTATION_SENSOR
+enum motionsensor_orientation motion_sense_remap_orientation(
+ const struct motion_sensor_t *s,
+ enum motionsensor_orientation orientation);
+#endif
+
+#if defined(CONFIG_GESTURE_HOST_DETECTION) || defined(CONFIG_ORIENTATION_SENSOR)
/* Add an extra sensor. We may need to add more */
#define MOTION_SENSE_ACTIVITY_SENSOR_ID (motion_sensor_count)
#define ALL_MOTION_SENSORS (MOTION_SENSE_ACTIVITY_SENSOR_ID + 1)
diff --git a/include/nvcounter.h b/include/nvcounter.h
new file mode 100644
index 0000000000..c523d6bd2d
--- /dev/null
+++ b/include/nvcounter.h
@@ -0,0 +1,20 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __EC_INCLUDE_NVCOUNTER_H
+#define __EC_INCLUDE_NVCOUNTER_H
+
+/*
+ * CONFIG_FLASH_NVCOUNTER provides a robust, non-volatile incrementing counter.
+ *
+ * It is currently uses 2 physical pages of flash for its underlying storage
+ * which are configured by CONFIG_FLASH_NVCTR_BASE_A and
+ * CONFIG_FLASH_NVCTR_BASE_B in board.h
+ */
+
+/* return the value of the counter after incrementing it */
+uint32_t nvcounter_incr(void);
+
+#endif /* __EC_INCLUDE_NVCOUNTER_H */
diff --git a/include/nvmem.h b/include/nvmem.h
index 781ea219f0..e9fae3d11d 100644
--- a/include/nvmem.h
+++ b/include/nvmem.h
@@ -6,38 +6,39 @@
#ifndef __CROS_EC_NVMEM_UTILS_H
#define __CROS_EC_NVMEM_UTILS_H
+#include "crypto_api.h"
+
/*
* In order to provide maximum robustness for NvMem operations, the NvMem space
* is divided into two equal sized partitions. A partition contains a tag
* and a buffer for each NvMem user.
*
* NvMem Partiion
- * ---------------------------------------------------------------------
- * |0x8 tag | User Buffer 0 | User Buffer 1 | .... | User Buffer N-1 |
- * ---------------------------------------------------------------------
+ * ------------------------------------------------------------------------
+ * |36 byte tag | User Buffer 0 | User Buffer 1 | .... | User Buffer N-1 |
+ * ------------------------------------------------------------------------
*
* Physical Block Tag details
- * ---------------------------------------------------------------------
- * | sha | version | reserved |
- * ---------------------------------------------------------------------
- * sha -> 4 bytes of sha1 digest
- * version -> 1 byte version number (0 - 0xfe)
- * reserved -> 3 bytes
+ * ------------------------------------------------------------------------
+ * | sha | padding | version | generation | reserved |
+ * -------------------------------------------------------------------------
+ * sha -> 16 bytes of sha1 digest
+ * padding -> 16 bytes for future extensions
+ * version -> nvmem layout version, currently at 0
+ * generation -> 1 byte generation number (0 - 0xfe)
+ * reserved -> 2 bytes
*
* At initialization time, each partition is scanned to see if it has a good sha
* entry. One of the two partitions being valid is a supported condition. If
- * however, neither partiion is valid, then a check is made to see if NvMem
- * space is fully erased. If this is detected, then the tag for partion 0 is
- * populated and written into flash. If neither partition is valid and they
- * aren't fully erased, then NvMem is marked corrupt and this failure condition
- * must be reported back to the caller.
+ * neither partiion is valid a new partition is created with generation set to
+ * zero.
*
* Note that the NvMem partitions can be placed anywhere in flash space, but
* must be equal in total size. A table is used by the NvMem module to get the
- * correct base address and offset for each partition.
+ * correct base address for each partition.
*
- * A version number is used to distinguish between two valid partitions with
- * the newsest version number (in a circular sense) marking the correct
+ * A generation number is used to distinguish between two valid partitions with
+ * the newsest generation number (in a circular sense) marking the correct
* partition to use. The parition number 0/1 is tracked via a static
* variable. When the NvMem contents need to be updated, the flash erase/write
* of the updated partition will use the inactive partition space in NvMem. This
@@ -51,9 +52,10 @@
* CONFIG_FLASH_NVMEM_BASE_(A|B) -> address of start of each partition
*
* The board.h file must define a macro or enum named NVMEM_NUM_USERS.
- * The board.c file must include 1 function and an array of user buffer lengths
+ * The board.c file must implement:
* nvmem_user_sizes[] -> array of user buffer lengths
- * nvmem_compute_sha() -> function used to compute 4 byte sha (or equivalent)
+ * The chip must provide
+ * app_compute_hash() -> function used to compute 16 byte sha (or equivalent)
*
* Note that total length of user buffers must satisfy the following:
* sum(user sizes) <= (NVMEM_PARTITION_SIZE) - sizeof(struct nvmem_tag)
@@ -63,15 +65,19 @@
extern uint32_t nvmem_user_sizes[NVMEM_NUM_USERS];
#define NVMEM_NUM_PARTITIONS 2
-#define NVMEM_SHA_SIZE 4
-#define NVMEM_VERSION_BITS 8
-#define NVMEM_VERSION_MASK ((1 << NVMEM_VERSION_BITS) - 1)
+#define NVMEM_SHA_SIZE CIPHER_SALT_SIZE
+#define NVMEM_GENERATION_BITS 8
+#define NVMEM_GENERATION_MASK ((1 << NVMEM_GENERATION_BITS) - 1)
+#define NVMEM_PADDING_SIZE 16
+#define NVMEM_LAYOUT_VERSION 0
/* Struct for NV block tag */
struct nvmem_tag {
uint8_t sha[NVMEM_SHA_SIZE];
- uint8_t version;
- uint8_t reserved[3];
+ uint8_t padding[NVMEM_PADDING_SIZE];
+ uint8_t layout_version;
+ uint8_t generation;
+ uint8_t reserved[2];
};
/* Structure MvMem Partition */
@@ -124,6 +130,9 @@ int nvmem_read(uint32_t startOffset, uint32_t size,
/**
* Write 'size' amount of bytes to NvMem
*
+ * Calling this function will wait for the mutex, then lock it until
+ * nvmem_commit() is invoked.
+ *
* @param startOffset: Offset (in bytes) into NVmem logical space
* @param size: Number of bytes to write
* @param data: Pointer to source buffer
@@ -138,6 +147,9 @@ int nvmem_write(uint32_t startOffset, uint32_t size,
/**
* Move 'size' amount of bytes within NvMem
*
+ * Calling this function will wait for the mutex, then lock it until
+ * nvmem_commit() is invoked.
+ *
* @param src_offset: source offset within NvMem logical space
* @param dest_offset: destination offset within NvMem logical space
* @param size: Number of bytes to move
@@ -152,28 +164,41 @@ int nvmem_move(uint32_t src_offset, uint32_t dest_offset, uint32_t size,
* Commit all previous NvMem writes to flash
*
* @return EC_SUCCESS if flash erase/operations are successful.
- * EC_ERROR_UNKNOWN otherwise.
+
+ * EC_ERROR_OVERFLOW in case the mutex is not locked when this
+ * function is called
+ * EC_ERROR_INVAL if task trying to commit is not the one
+ * holding the mutex
+ * EC_ERROR_UNKNOWN in other error cases
*/
int nvmem_commit(void);
-/**
- * One time initialization of NvMem partitions
- * @param version: Starting version number of partition 0
+/*
+ * Clear out a user's data across all partitions.
*
- * @return EC_SUCCESS if flash operations are successful.
- * EC_ERROR_UNKNOWN otherwise.
+ * @param user: The user who's data should be cleared.
+ * @return EC_SUCCESS if the user's data across all partitions was
+ * cleared. Error othrwise.
*/
-int nvmem_setup(uint8_t version);
+int nvmem_erase_user_data(enum nvmem_users user);
-/**
- * Compute sha1 (lower 4 bytes or equivalent checksum) for NvMem tag
+/*
+ * Temporarily stopping NVMEM commits could be beneficial. One use case is
+ * when TPM operations need to be sped up.
+ *
+ * Calling this function will wait for the mutex, then lock it until
+ * nvmem_commit() is invoked.
+ *
+ * Both below functions should be called from the same task.
+ */
+void nvmem_disable_commits(void);
+
+/*
+ * Only the task holding the mutex is allowed to enable commits.
*
- * @param p_buf: pointer to beginning of data
- * @param num_bytes: length of data in bytes
- * @param p_sha: pointer to where computed sha will be stored
- * @param sha_len: length in bytes to use from sha computation
+ * @return error if this task does not hold the lock or commit
+ * fails, EC_SUCCESS otherwise.
*/
-void nvmem_compute_sha(uint8_t *p_buf, int num_bytes, uint8_t *p_sha,
- int sha_len);
+int nvmem_enable_commits(void);
#endif /* __CROS_EC_NVMEM_UTILS_H */
diff --git a/include/nvmem_vars.h b/include/nvmem_vars.h
new file mode 100644
index 0000000000..867ea32018
--- /dev/null
+++ b/include/nvmem_vars.h
@@ -0,0 +1,87 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __EC_INCLUDE_NVMEM_VARS_H
+#define __EC_INCLUDE_NVMEM_VARS_H
+
+/*
+ * CONFIG_FLASH_NVMEM provides persistent, atomic-update storage in
+ * flash. The storage is logically divided into one or more "user regions", as
+ * configured in board.h and board.c
+ *
+ * CONFIG_FLASH_NVMEM_VARS stores a set of <KEY, VALUE> tuples in the nvmem
+ * user region designated by CONFIG_FLASH_NVMEM_VARS_USER_NUM (in board.h)
+ *
+ * Tuples are stored and managed using this struct:
+ */
+
+struct tuple {
+ uint8_t key_len; /* 1 - 255 */
+ uint8_t val_len; /* 1 - 255 */
+ uint8_t flags; /* RESERVED, will be zeroed */
+ uint8_t data_[0]; /* Opaque. Don't look here. */
+};
+
+/*
+ * Both KEY and VALUE can be any binary blob between 1 and 255 bytes (flash
+ * memory is limited, so if you need longer values just use two keys and
+ * concatenate the blobs). Zero-length KEYs or VALUEs are not allowed.
+ * Assigning a zero-length VALUE to a KEY just deletes that tuple (if it
+ * existed).
+ *
+ * The expected usage is:
+ *
+ * 1. At boot, call initvars() to ensure that the variable storage region is
+ * valid. If it isn't, this will initialize it to an empty set.
+ *
+ * 2. Call getenv() or setenv() as needed. The first call to either will copy
+ * the storage regsion from flash into a RAM buffer. Any changes made with
+ * setenv() will affect only that RAM buffer.
+ *
+ * 3. Call writevars() to commit the RAM buffer to flash and free it.
+ *
+ * CAUTION: The underlying CONFIG_FLASH_NVMEM implementation allows access by
+ * multiple tasks, provided each task access only one user region. There is no
+ * support for simultaneous access to the *same* user region by multiple tasks.
+ * CONFIG_FLASH_NVMEM_VARS stores all variables in one user region, so if
+ * variable access by multiple tasks is required, the tasks should establish
+ * their own locks or mutexes to fit their usage. In general that would mean
+ * aquiring a lock before calling getvar() or setvar(), and releasing it after
+ * calling writevars().
+ */
+
+/*
+ * Initialize the persistent storage. This checks the user region to ensure
+ * that all tuples are valid and that there is one additional '\0' at the end.
+ * If any discrepancies are found, it erases all values. This should return
+ * EC_SUCCESS unless there is a problem writing to flash.
+ */
+int initvars(void);
+
+/*
+ * Look up a key, return a pointer to the tuple. If the key is not found,
+ * return NULL. WARNING: The returned pointer is only valid until the next call
+ * to setvar() or writevars(). Use it or lose it.
+ */
+const struct tuple *getvar(const uint8_t *key, uint8_t key_len);
+
+/* Use these to access the data components of a valid struct tuple pointer */
+const uint8_t *tuple_key(const struct tuple *);
+const uint8_t *tuple_val(const struct tuple *);
+
+/*
+ * Save the tuple in the RAM buffer. If val is NULL or val_len is 0, the
+ * tuple is deleted (if it existed). Returns EC_SUCCESS or error code.
+ */
+int setvar(const uint8_t *key, uint8_t key_len,
+ const uint8_t *val, uint8_t val_len);
+
+/*
+ * Commit any changes made with setvar() to persistent memory, and invalidate
+ * the RAM buffer. Return EC_SUCCESS or error code on failure.
+ */
+int writevars(void);
+
+#endif /* __EC_INCLUDE_NVMEM_VARS_H */
diff --git a/include/otp.h b/include/otp.h
new file mode 100644
index 0000000000..7851411202
--- /dev/null
+++ b/include/otp.h
@@ -0,0 +1,32 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* OTP memory module for Chrome EC */
+
+#ifndef __CROS_EC_OTP_H
+#define __CROS_EC_OTP_H
+
+/*
+ * OTP: One Time Programable memory is used for storing persistent data.
+ */
+
+/**
+ * Set the serial number in OTP memory.
+ *
+ * @param serialno ascii serial number string.
+ *
+ * @return success status.
+ */
+int otp_write_serial(const char *serialno);
+
+/**
+ * Get the serial number from flash.
+ *
+ * @return char * ascii serial number string.
+ * NULL if error.
+ */
+const char *otp_read_serial(void);
+
+#endif /* __CROS_EC_OTP_H */
diff --git a/include/panic.h b/include/panic.h
index a4fbf24b92..e5e16dd190 100644
--- a/include/panic.h
+++ b/include/panic.h
@@ -129,12 +129,12 @@ void panic_assert_fail(const char *msg, const char *func, const char *fname,
*
* @param msg Panic message
*/
-void panic(const char *msg);
+void panic(const char *msg) __attribute__((noreturn));
/**
* Display a default message and reset
*/
-void panic_reboot(void);
+void panic_reboot(void) __attribute__((noreturn));
#ifdef CONFIG_SOFTWARE_PANIC
/**
@@ -170,4 +170,14 @@ void ignore_bus_fault(int ignored);
*/
struct panic_data *panic_get_data(void);
+/**
+ * Chip-specific implementation for backing up panic data to persistent
+ * storage. This function is used to ensure that the panic data can survive loss
+ * of VCC power rail.
+ *
+ * There is no generic restore function provided since every chip can decide
+ * when it is safe to restore panic data during the system initialization step.
+ */
+void chip_panic_data_backup(void);
+
#endif /* __CROS_EC_PANIC_H */
diff --git a/include/physical_presence.h b/include/physical_presence.h
new file mode 100644
index 0000000000..0acbc65691
--- /dev/null
+++ b/include/physical_presence.h
@@ -0,0 +1,76 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Physical presence detection
+ */
+#ifndef __CROS_EC_PHYSICAL_PRESENCE_H
+#define __CROS_EC_PHYSICAL_PRESENCE_H
+
+/**
+ * Start physical presence detect.
+ *
+ * If the physical presence sequence is successful, callback() will be called
+ * from the hook task context as a deferred function.
+ *
+ * On failure or abort, callback() will not be called.
+ *
+ * @param is_long Use long (!=0) or short (0) sequence)
+ * @param callback Function to call when successful
+ * @return EC_SUCCESS, EC_BUSY if detect already in progress, or other
+ * non-zero error code if error.
+ */
+int physical_detect_start(int is_long, void (*callback)(void));
+
+/**
+ * Check if a physical detect attempt is in progress
+ *
+ * @return non-zero if in progress
+ */
+int physical_detect_busy(void);
+
+/**
+ * Abort a currently-running physical presence detect.
+ *
+ * Note there is a race condition between stopping detect and a running
+ * detect finishing and calling its callback. The intent of this function
+ * is not to prevent that, but instead to avoid an aborted physical detect
+ * tying up the button for long periods when we no longer care.
+ */
+void physical_detect_abort(void);
+
+/**
+ * Handle a physical detect button press.
+ *
+ * This may be called from interrupt level.
+ *
+ * Returns EC_SUCCESS if the press was consumed, or EC_ERROR_NOT_HANDLED if
+ * physical detect was idle (so the press is for someone else).
+ */
+int physical_detect_press(void);
+
+/**
+ * Start/stop capturing the button for physical presence.
+ *
+ * When enabled, a debounced button press+release should call
+ * physical_detect_press().
+ *
+ * This should be implemented by the board.
+ *
+ * @param enable Enable (!=0) or disable (==0) capturing button.
+ */
+void board_physical_presence_enable(int enable);
+
+/**
+ * An API to report physical presence FSM state to an external entity. Of
+ * interest are states when key press is currently required or is expected
+ * soon.
+ */
+enum pp_fsm_state {
+ PP_OTHER = 0,
+ PP_AWAITING_PRESS = 1,
+ PP_BETWEEN_PRESSES = 2,
+};
+enum pp_fsm_state physical_presense_fsm_state(void);
+
+#endif /* __CROS_EC_PHYSICAL_PRESENCE_H */
diff --git a/include/pinweaver.h b/include/pinweaver.h
new file mode 100644
index 0000000000..58210fa037
--- /dev/null
+++ b/include/pinweaver.h
@@ -0,0 +1,142 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __CROS_EC_INCLUDE_PINWEAVER_H
+#define __CROS_EC_INCLUDE_PINWEAVER_H
+
+/* This is required before pinweaver_types.h to provide __packed and __aligned
+ * while preserving the ability of pinweaver_types.h to be used in code outside
+ * of src/platform/ec.
+ */
+#include <common.h>
+#include <pinweaver_types.h>
+
+#define PW_STORAGE_VERSION 0
+
+#define BITS_PER_LEVEL_MIN 1
+#define BITS_PER_LEVEL_MAX 5
+#define HEIGHT_MIN 1
+/* This will crash for logk == 0 so that condition must not be allowed when
+ * using this.
+ */
+#define HEIGHT_MAX(logk) ((sizeof(struct label_t) * 8) / logk)
+
+/* Persistent information used by this feature. */
+struct merkle_tree_t {
+ /* log2(Fan out). */
+ struct bits_per_level_t bits_per_level;
+ /* Height of the tree or param_l / bits_per_level. */
+ struct height_t height;
+
+ /* Root hash of the Merkle tree. */
+ uint8_t root[PW_HASH_SIZE];
+
+ /* Random bits used as part of the key derivation process. */
+ uint8_t key_derivation_nonce[16];
+
+ /* Key used to compute the HMACs of the metadata of the leaves. */
+ uint8_t PW_ALIGN_TO_WRD hmac_key[32];
+
+ /* Key used to encrypt and decrypt the metadata of the leaves. */
+ uint8_t PW_ALIGN_TO_WRD wrap_key[32];
+};
+
+/* Do not remove fields within the same PW_LEAF_MAJOR_VERSION. */
+/* Unencrypted part of the leaf data. */
+struct PW_PACKED leaf_public_data_t {
+ struct label_t label;
+ struct delay_schedule_entry_t delay_schedule[PW_SCHED_COUNT];
+
+ /* State used to rate limit. */
+ struct pw_timestamp_t timestamp;
+ struct attempt_count_t attempt_count;
+};
+
+/* Do not remove fields within the same PW_LEAF_MAJOR_VERSION. */
+/* Encrypted part of the leaf data. */
+struct PW_PACKED PW_ALIGN_TO_BLK leaf_sensitive_data_t {
+ uint8_t low_entropy_secret[PW_SECRET_SIZE];
+ uint8_t high_entropy_secret[PW_SECRET_SIZE];
+ uint8_t reset_secret[PW_SECRET_SIZE];
+};
+
+/* Represents leaf data in a form that can be exported for storage. */
+struct PW_PACKED wrapped_leaf_data_t {
+ /* This is first so that head.leaf_version will be the first field
+ * in the struct to keep the meaning of the struct from becoming
+ * ambiguous across versions.
+ */
+ struct leaf_header_t head;
+ /* Covers .head, .pub, and .cipher_text. */
+ uint8_t hmac[PW_HASH_SIZE];
+ uint8_t iv[PW_WRAP_BLOCK_SIZE];
+ struct leaf_public_data_t pub;
+ uint8_t cipher_text[sizeof(struct leaf_sensitive_data_t)];
+};
+
+/* Represents encrypted leaf data after the lengths and version in the header
+ * have been validated.
+ */
+struct imported_leaf_data_t {
+ /* This is first so that head.leaf_version will be the first field
+ * in the struct to keep the meaning of the struct from becoming
+ * ambiguous across versions.
+ */
+ const struct leaf_header_t *head;
+ /* Covers .head, .pub, and .cipher_text. */
+ const uint8_t *hmac;
+ const uint8_t *iv;
+ const struct leaf_public_data_t *pub;
+ const uint8_t *cipher_text;
+ const uint8_t (*hashes)[PW_HASH_SIZE];
+};
+
+/* The leaf data in a clear text working format. */
+struct leaf_data_t {
+ struct leaf_public_data_t pub;
+ struct leaf_sensitive_data_t sec;
+};
+
+/* Handler for incoming messages after they have been reconstructed.
+ *
+ * merkle_tree->root needs to be updated with new_root outside of this function.
+ */
+int pw_handle_request(struct merkle_tree_t *merkle_tree,
+ const struct pw_request_t *request,
+ struct pw_response_t *response);
+
+/******************************************************************************/
+/* Struct helper functions.
+ */
+
+/* Sets up pointers to the relevant fields inside an wrapped leaf based on the
+ * length fields in the header. These fields should be validated prior to
+ * calling this function.
+ */
+void import_leaf(const struct unimported_leaf_data_t *unimported,
+ struct imported_leaf_data_t *imported);
+
+/* Calculate how much is needed to add to the size of structs containing
+ * an struct unimported_leaf_data_t because the variable length fields at the
+ * end of the struct are not included by sizeof().
+ */
+#define PW_LEAF_PAYLOAD_SIZE (sizeof(struct wrapped_leaf_data_t) - \
+ sizeof(struct unimported_leaf_data_t))
+
+
+/******************************************************************************/
+/* Utility functions exported for better test coverage.
+ */
+
+/* Computes the total number of the sibling hashes along a path. */
+int get_path_auxiliary_hash_count(const struct merkle_tree_t *merkle_tree);
+
+/* Computes the parent hash for an array of child hashes. */
+void compute_hash(const uint8_t hashes[][PW_HASH_SIZE], uint16_t num_hashes,
+ struct index_t location,
+ const uint8_t child_hash[PW_HASH_SIZE],
+ uint8_t result[PW_HASH_SIZE]);
+
+#endif /* __CROS_EC_INCLUDE_PINWEAVER_H */
diff --git a/include/pinweaver_tpm_imports.h b/include/pinweaver_tpm_imports.h
new file mode 100644
index 0000000000..be882593d6
--- /dev/null
+++ b/include/pinweaver_tpm_imports.h
@@ -0,0 +1,27 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Compatibility layer between the TPM code and PinWeaver.
+ *
+ * This is needed because the headers for the TPM are not compatible with the
+ * headers used by pinweaver.c. It also makes it easier to mock the
+ * functionality derived from the TPM code.
+ */
+
+#ifndef __CROS_EC_INCLUDE_PINWEAVER_TPM_IMPORTS_H
+#define __CROS_EC_INCLUDE_PINWEAVER_TPM_IMPORTS_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+uint32_t get_restart_count(void);
+
+/* This is used to get the storage seed from the TPM implementation so
+ * TPM_Clear() will break the keys used by PinWeaver so that any metadata
+ * that persists on the machine storage is unusable by attackers.
+ */
+void get_storage_seed(void *buf, size_t *len);
+
+#endif /* __CROS_EC_INCLUDE_PINWEAVER_TPM_IMPORTS_H */
diff --git a/include/pinweaver_types.h b/include/pinweaver_types.h
new file mode 100644
index 0000000000..9fbb801e81
--- /dev/null
+++ b/include/pinweaver_types.h
@@ -0,0 +1,289 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Shared types between Cr50 and the AP side code. */
+
+#ifndef __CROS_EC_INCLUDE_PINWEAVER_TYPES_H
+#define __CROS_EC_INCLUDE_PINWEAVER_TYPES_H
+
+#include <stdint.h>
+
+#define PW_PACKED __packed
+
+#define PW_PROTOCOL_VERSION 0
+#define PW_LEAF_MAJOR_VERSION 0
+#define PW_LEAF_MINOR_VERSION 0
+
+#define PW_MAX_MESSAGE_SIZE (2048 - 12 /* sizeof(struct tpm_cmd_header) */)
+
+/* The block size of encryption used for wrapped_leaf_data_t. */
+#define PW_WRAP_BLOCK_SIZE 16
+
+#define PW_ALIGN_TO_WRD __aligned(4)
+
+#define PW_ALIGN_TO_BLK __aligned(PW_WRAP_BLOCK_SIZE)
+
+enum pw_error_codes_enum {
+ PW_ERR_VERSION_MISMATCH = 0x10000, /* EC_ERROR_INTERNAL_FIRST */
+ PW_ERR_TREE_INVALID,
+ PW_ERR_LENGTH_INVALID,
+ PW_ERR_TYPE_INVALID,
+ PW_ERR_BITS_PER_LEVEL_INVALID,
+ PW_ERR_HEIGHT_INVALID,
+ PW_ERR_LABEL_INVALID,
+ PW_ERR_DELAY_SCHEDULE_INVALID,
+ PW_ERR_PATH_AUTH_FAILED,
+ PW_ERR_LEAF_VERSION_MISMATCH,
+ PW_ERR_HMAC_AUTH_FAILED,
+ PW_ERR_LOWENT_AUTH_FAILED,
+ PW_ERR_RESET_AUTH_FAILED,
+ PW_ERR_CRYPTO_FAILURE,
+ PW_ERR_RATE_LIMIT_REACHED,
+};
+
+/* Represents the log2(fan out) of a tree. */
+struct PW_PACKED bits_per_level_t {
+ uint8_t v;
+};
+
+ /* Represent the height of a tree. */
+struct PW_PACKED height_t {
+ uint8_t v;
+};
+
+/* Represents a child index of a node in a tree. */
+struct PW_PACKED index_t {
+ uint8_t v;
+};
+
+/* Represents the child index for each level of a tree along a path to a leaf.
+ * It is a Little-endian unsigned integer with the following value (MSB->LSB)
+ * | Zero padding | 1st level index | ... | leaf index |,
+ * where each index is represented by bits_per_level bits.
+ */
+struct PW_PACKED label_t {
+ uint64_t v;
+};
+
+/* Represents a count of failed login attempts. This is capped at UINT32_MAX. */
+struct PW_PACKED attempt_count_t {
+ uint32_t v;
+};
+
+/* Represents a notion of time. */
+struct PW_PACKED pw_timestamp_t {
+ /* Number of boots. This is used to track if Cr50 has rebooted since
+ * timer_value was recorded.
+ */
+ uint32_t boot_count;
+ /* Seconds since boot. */
+ uint64_t timer_value;
+};
+
+/* Represents a time interval in seconds.
+ *
+ * This only needs to be sufficiently large to represent the longest time
+ * between allowed attempts.
+ */
+struct PW_PACKED time_diff_t {
+ uint32_t v;
+};
+#define PW_BLOCK_ATTEMPTS UINT32_MAX
+
+/* Number of bytes required for a hash or hmac value in the merkle tree. */
+#define PW_HASH_SIZE 32
+
+/* Represents a single entry in a delay schedule table. */
+struct PW_PACKED delay_schedule_entry_t {
+ struct attempt_count_t attempt_count;
+ struct time_diff_t time_diff;
+};
+
+/* Represents the number of entries in the delay schedule table which can be
+ * used to determine the next time an authentication attempt can be made.
+ */
+#define PW_SCHED_COUNT 16
+
+/* Number of bytes required to store a secret.
+ */
+#define PW_SECRET_SIZE 32
+
+struct PW_PACKED leaf_version_t {
+ /* minor comes first so this struct will be compatibile with uint32_t
+ * comparisons for little endian to make version comparisons easier.
+ *
+ * Changes to minor versions are allowed to add new fields, but not
+ * remove existing fields, and they are allowed to be interpreted by
+ * previous versions---any extra fields are truncated.
+ *
+ * Leafs will reject future major versions assuming they are
+ * incompatible, so fields in struct leaf_public_data_t and
+ * struct leaf_sensitive_data_t may be removed for new major versions.
+ * Upgrades across major versions will require explicit logic to
+ * map the old struct to the new struct or vice versa.
+ */
+ uint16_t minor;
+ uint16_t major;
+};
+
+/* Do not change this within the same PW_LEAF_MAJOR_VERSION. */
+struct PW_PACKED leaf_header_t {
+ /* Always have leaf_version at the beginning of
+ * struct wrapped_leaf_data_t to maintain preditable behavior across
+ * versions.
+ */
+ struct leaf_version_t leaf_version;
+ uint16_t pub_len;
+ uint16_t sec_len;
+};
+
+/* Represents a struct of unknown length to be imported to process a request. */
+struct PW_PACKED unimported_leaf_data_t {
+ /* This is first so that head.leaf_version will be the first field
+ * in the struct to make handling different struct versions easier.
+ */
+ struct leaf_header_t head;
+ /* Covers .head, .iv, and .payload (excluding path_hashes) */
+ uint8_t hmac[PW_HASH_SIZE];
+ uint8_t iv[PW_WRAP_BLOCK_SIZE];
+ /* This field is treated as having a zero size by the compiler so the
+ * actual size needs to be added to the size of this struct. This allows
+ * for forward compatibility using the pub_len and sec_len fields in the
+ * header.
+ *
+ * Has following layout:
+ * Required:
+ * uint8_t pub_data[head.pub_len];
+ * uint8_t ciphter_text[head.sec_len];
+ *
+ * For Requests only:
+ * uint8_t path_hashes[get_path_auxiliary_hash_count(.)][PW_HASH_SIZE];
+ */
+ uint8_t payload[];
+};
+
+/******************************************************************************/
+/* Message structs
+ *
+ * The message format is a pw_request_header_t followed by the data
+ */
+
+enum pw_message_type_enum {
+ PW_MT_INVALID = 0,
+
+ /* Request / "Question" types. */
+ PW_RESET_TREE = 1,
+ PW_INSERT_LEAF,
+ PW_REMOVE_LEAF,
+ PW_TRY_AUTH,
+ PW_RESET_AUTH,
+};
+
+struct PW_PACKED pw_message_type_t {
+ uint8_t v;
+};
+
+struct PW_PACKED pw_request_header_t {
+ uint8_t version;
+ struct pw_message_type_t type;
+ uint16_t data_length;
+};
+
+struct PW_PACKED pw_response_header_t {
+ uint8_t version;
+ uint16_t data_length; /* Does not include the header. */
+ uint32_t result_code;
+ uint8_t root[PW_HASH_SIZE];
+};
+
+struct PW_PACKED pw_request_reset_tree_t {
+ struct bits_per_level_t bits_per_level;
+ struct height_t height;
+};
+
+struct PW_PACKED pw_request_insert_leaf_t {
+ struct label_t label;
+ struct delay_schedule_entry_t delay_schedule[PW_SCHED_COUNT];
+ uint8_t low_entropy_secret[PW_SECRET_SIZE];
+ uint8_t high_entropy_secret[PW_SECRET_SIZE];
+ uint8_t reset_secret[PW_SECRET_SIZE];
+ /* This is a variable length field because it size is determined at
+ * runtime based on the chosen tree parameters. Its size is treated as
+ * zero by the compiler so the computed size needs to be added to the
+ * size of this struct in order to determine the actual size. This field
+ * has the form:
+ * uint8_t path_hashes[get_path_auxiliary_hash_count(.)][PW_HASH_SIZE];
+ */
+ uint8_t path_hashes[][PW_HASH_SIZE];
+};
+
+struct PW_PACKED pw_response_insert_leaf_t {
+ struct unimported_leaf_data_t unimported_leaf_data;
+};
+
+struct PW_PACKED pw_request_remove_leaf_t {
+ struct label_t leaf_location;
+ uint8_t leaf_hmac[PW_HASH_SIZE];
+ /* See (struct pw_request_insert_leaf_t).path_hashes. */
+ uint8_t path_hashes[][PW_HASH_SIZE];
+};
+
+struct PW_PACKED pw_request_try_auth_t {
+ uint8_t low_entropy_secret[PW_SECRET_SIZE];
+ struct unimported_leaf_data_t unimported_leaf_data;
+};
+
+struct PW_PACKED pw_response_try_auth_t {
+ /* Valid for the PW_ERR_RATE_LIMIT_REACHED return code only. */
+ struct time_diff_t seconds_to_wait;
+ /* Valid for the EC_SUCCESS return code only. */
+ uint8_t high_entropy_secret[PW_SECRET_SIZE];
+ /* Valid for the PW_ERR_LOWENT_AUTH_FAILED and EC_SUCCESS return codes.
+ */
+ struct unimported_leaf_data_t unimported_leaf_data;
+};
+
+struct PW_PACKED pw_request_reset_auth_t {
+ uint8_t reset_secret[PW_SECRET_SIZE];
+ struct unimported_leaf_data_t unimported_leaf_data;
+};
+
+struct PW_PACKED pw_response_reset_auth_t {
+ uint8_t high_entropy_secret[PW_SECRET_SIZE];
+ struct unimported_leaf_data_t unimported_leaf_data;
+};
+
+struct PW_PACKED pw_request_t {
+ struct pw_request_header_t header;
+ union {
+ struct pw_request_reset_tree_t reset_tree;
+ struct pw_request_insert_leaf_t insert_leaf;
+ struct pw_request_remove_leaf_t remove_leaf;
+ struct pw_request_try_auth_t try_auth;
+ struct pw_request_reset_auth_t reset_auth;
+ } data;
+};
+
+struct PW_PACKED pw_response_t {
+ struct pw_response_header_t header;
+ union {
+
+ struct pw_response_insert_leaf_t insert_leaf;
+ struct pw_response_try_auth_t try_auth;
+ struct pw_response_reset_auth_t reset_auth;
+ } data;
+};
+
+/* An explicit limit is set because struct unimported_leaf_data_t can have more
+ * than one variable length field so the max length for these fields needs to be
+ * defined so that meaningful parameter limits can be set to validate the tree
+ * parameters.
+ *
+ * 1536 was chosen because it is 3/4 of 2048 and allows for a maximum tree
+ * height of 16 for the default fan-out of 4.
+ */
+#define PW_MAX_PATH_SIZE 1536
+
+#endif /* __CROS_EC_INCLUDE_PINWEAVER_TYPES_H */
diff --git a/include/power.h b/include/power.h
index b81a952156..a39b757078 100644
--- a/include/power.h
+++ b/include/power.h
@@ -10,6 +10,7 @@
#include "common.h"
#include "gpio.h"
+#include "task_id.h"
enum power_state {
/* Steady states */
@@ -36,10 +37,31 @@ enum power_state {
#endif
};
+/*
+ * Power signal flags:
+ *
+ * +-----------------+------------------------------------+
+ * | Bit # | Description |
+ * +------------------------------------------------------+
+ * | 0 | Active level (low/high) |
+ * +------------------------------------------------------+
+ * | 1 | Signal interrupt state at boot |
+ * +------------------------------------------------------+
+ * | 2 : 32 | Reserved |
+ * +-----------------+------------------------------------+
+ */
+
+#define POWER_SIGNAL_ACTIVE_STATE (1 << 0)
+#define POWER_SIGNAL_ACTIVE_LOW (0 << 0)
+#define POWER_SIGNAL_ACTIVE_HIGH (1 << 0)
+
+#define POWER_SIGNAL_INTR_STATE (1 << 1)
+#define POWER_SIGNAL_DISABLE_AT_BOOT (1 << 1)
+
/* Information on an power signal */
struct power_signal_info {
enum gpio_signal gpio; /* GPIO for signal */
- int level; /* GPIO level which sets signal bit */
+ uint32_t flags; /* See POWER_SIGNAL_* macros */
const char *name; /* Name of signal */
};
@@ -58,6 +80,25 @@ extern const struct power_signal_info power_signal_list[];
uint32_t power_get_signals(void);
/**
+ * Check if provided power signal is currently asserted.
+ *
+ * @param s Power signal that needs to be checked.
+ *
+ * @return 1 if power signal is asserted, 0 otherwise.
+ */
+int power_signal_is_asserted(const struct power_signal_info *s);
+
+/**
+ * Enable interrupt for provided input signal.
+ */
+int power_signal_enable_interrupt(enum gpio_signal signal);
+
+/**
+ * Disable interrupt for provided input signal.
+ */
+int power_signal_disable_interrupt(enum gpio_signal signal);
+
+/**
* Check for required inputs
*
* @param want Mask of signals which must be present (one or more
@@ -117,20 +158,10 @@ enum power_state power_handle_state(enum power_state state);
*/
#ifdef HAS_TASK_CHIPSET
void power_signal_interrupt(enum gpio_signal signal);
-#ifdef CONFIG_POWER_S0IX
-void power_signal_interrupt_S0(enum gpio_signal signal);
-#endif
#else
static inline void power_signal_interrupt(enum gpio_signal signal) { }
-#ifdef CONFIG_POWER_S0IX
-static inline void power_signal_interrupt_S0(enum gpio_signal signal) { }
-#endif
#endif /* !HAS_TASK_CHIPSET */
-#ifdef CONFIG_POWER_S0IX
-int chipset_get_ps_debounced_level(enum gpio_signal signal);
-#endif
-
/**
* pause_in_s5 getter method.
*
@@ -152,5 +183,45 @@ void power_set_pause_in_s5(int pause);
* @return Believed sleep state of host.
*/
enum host_sleep_event power_get_host_sleep_state(void);
-#endif
+
+/**
+ * Provide callback to allow chipset to take any action on host sleep event
+ * command.
+ *
+ * @param state Current host sleep state updated by the host.
+ */
+void power_chipset_handle_host_sleep_event(enum host_sleep_event state);
+
+/*
+ * This is the default state of host sleep event. Calls to
+ * power_reset_host_sleep_state will set host sleep event to this
+ * value. EC components listening to host sleep event updates can check for this
+ * special value to know if the state was reset.
+ */
+#define HOST_SLEEP_EVENT_DEFAULT_RESET 0
+
+#ifdef CONFIG_POWER_S0IX
+/**
+ * Reset the sleep state reported by the host.
+ *
+ * @param sleep_event Reset sleep state.
+ */
+void power_reset_host_sleep_state(void);
+#endif /* CONFIG_POWER_S0IX */
+#endif /* CONFIG_POWER_TRACK_HOST_SLEEP_STATE */
+
+/**
+ * Enable/Disable the PP5000 rail.
+ *
+ * This function will turn on the 5V rail immediately if requested. However,
+ * the rail will not turn off until all tasks want it off.
+ *
+ * NOTE: Be careful when calling from deferred functions, as they will all be
+ * executed within the same task context! (The HOOKS task).
+ *
+ * @param tid: The caller's task ID.
+ * @param enable: 1 to turn on the rail, 0 to request the rail to be turned off.
+ */
+void power_5v_enable(task_id_t tid, int enable);
+
#endif /* __CROS_EC_POWER_H */
diff --git a/include/power_button.h b/include/power_button.h
index 3297c411eb..cfa234c8be 100644
--- a/include/power_button.h
+++ b/include/power_button.h
@@ -54,4 +54,12 @@ void power_button_pch_release(void);
*/
void power_button_pch_pulse(void);
+/**
+ * Returns the time when DSW_PWROK was asserted. It should be customized
+ * by each board. See CONFIG_DELAY_DSW_PWROK_TO_PWRBTN for details.
+ *
+ * @return time in usec when DSW_PWROK was asserted.
+ */
+int64_t get_time_dsw_pwrok(void);
+
#endif /* __CROS_EC_POWER_BUTTON_H */
diff --git a/include/pwm.h b/include/pwm.h
index 9c349d1fb6..24b89332fc 100644
--- a/include/pwm.h
+++ b/include/pwm.h
@@ -20,6 +20,12 @@ void pwm_enable(enum pwm_channel ch, int enabled);
int pwm_get_enabled(enum pwm_channel ch);
/**
+ * Set PWM channel frequency (Hz).
+ * PWM will be disabled until the duty is set.
+ */
+void pwm_set_freq(enum pwm_channel ch, uint32_t freq);
+
+/**
* Set PWM channel duty cycle (0-65535).
*/
void pwm_set_raw_duty(enum pwm_channel ch, uint16_t duty);
@@ -65,4 +71,8 @@ int pwm_get_duty(enum pwm_channel ch);
* PWM channel must stay active in low-power idle, if enabled.
*/
#define PWM_CONFIG_DSLEEP (1 << 4)
+/**
+ * PWM channel's IO type is open-drain, if enabled. (default IO is push-pull.)
+ */
+#define PWM_CONFIG_OPEN_DRAIN (1 << 5)
#endif /* __CROS_EC_PWM_H */
diff --git a/include/rma_auth.h b/include/rma_auth.h
new file mode 100644
index 0000000000..427e5c90de
--- /dev/null
+++ b/include/rma_auth.h
@@ -0,0 +1,69 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* RMA challenge-response */
+
+#ifndef __CROS_EC_RMA_AUTH_H
+#define __CROS_EC_RMA_AUTH_H
+
+#include <stdint.h>
+
+#include "common.h" /* For __packed. */
+
+/* Current challenge protocol version */
+#define RMA_CHALLENGE_VERSION 0
+
+/* Getters and setters for version_key_id byte */
+#define RMA_CHALLENGE_VKID_BYTE(version, keyid) \
+ (((version) << 6) | ((keyid) & 0x3f))
+#define RMA_CHALLENGE_GET_VERSION(vkidbyte) ((vkidbyte) >> 6)
+#define RMA_CHALLENGE_GET_KEY_ID(vkidbyte) ((vkidbyte) & 0x3f)
+
+struct __packed rma_challenge {
+ /* Top 2 bits are protocol version; bottom 6 are server KeyID */
+ uint8_t version_key_id;
+
+ /* Ephemeral public key from device */
+ uint8_t device_pub_key[32];
+
+ /* Board ID (.type) */
+ uint8_t board_id[4];
+
+ /* Device ID */
+ uint8_t device_id[8];
+};
+
+/* Size of encoded challenge and response, and buffer sizes to hold them */
+#define RMA_CHALLENGE_CHARS 80
+#define RMA_CHALLENGE_BUF_SIZE (RMA_CHALLENGE_CHARS + 1)
+
+#define RMA_AUTHCODE_CHARS 8
+#define RMA_AUTHCODE_BUF_SIZE (RMA_AUTHCODE_CHARS + 1)
+
+/**
+ * Create a new RMA challenge/response
+ *
+ * @return EC_SUCCESS, EC_ERROR_TIMEOUT if too soon since the last challenge,
+ * or other non-zero error code.
+ */
+int rma_create_challenge(void);
+
+/**
+ * Get the current challenge string
+ *
+ * @return a pointer to the challenge string. String will be empty if there
+ * is no active challenge.
+ */
+const char *rma_get_challenge(void);
+
+/**
+ * Try a RMA authorization code
+ *
+ * @param code Authorization code to try
+ * @return EC_SUCCESS if the response was correct, or non-zero error code.
+ */
+int rma_try_authcode(const char *code);
+
+#endif
diff --git a/include/rollback.h b/include/rollback.h
new file mode 100644
index 0000000000..e51d5c94a2
--- /dev/null
+++ b/include/rollback.h
@@ -0,0 +1,72 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __CROS_EC_ROLLBACK_H
+#define __CROS_EC_ROLLBACK_H
+
+#define CROS_EC_ROLLBACK_COOKIE 0x0b112233
+
+#ifndef __ASSEMBLER__
+
+/**
+ * Get minimum version set by rollback protection blocks.
+ *
+ * @return Minimum rollback version, 0 if neither block is initialized,
+ * negative value on error.
+ */
+int rollback_get_minimum_version(void);
+
+/**
+ * Get device secret from rollback protection block.
+ *
+ * @param secret CONFIG_ROLLBACK_SECRET_SIZE-long buffer to copy the secret to.
+ *
+ * @return EC_SUCCESS on success, EC_ERROR_* on error (e.g. secret is not
+ * initialized)
+ */
+int rollback_get_secret(uint8_t *secret);
+
+/**
+ * Update rollback protection block to the version passed as parameter.
+ *
+ * @param next_min_version Minimum version to write in rollback block.
+ *
+ * @return EC_SUCCESS on success, EC_ERROR_* on error.
+ */
+int rollback_update_version(int32_t next_min_version);
+
+/**
+ * Add entropy to the rollback block.
+ *
+ * @param data Data to be added to rollback block secret (after hashing)
+ * @param len data length
+ *
+ * @return EC_SUCCESS on success, EC_ERROR_* on error.
+ */
+int rollback_add_entropy(uint8_t *data, unsigned int len);
+
+/**
+ * Lock rollback protection block, reboot if necessary.
+ *
+ * @return EC_SUCCESS if rollback was already protected.
+ */
+int rollback_lock(void);
+
+/**
+ * Obtain some weak entropy (i.e. not guaranteed to be high quality), based on
+ * sensors or timing events.
+ *
+ * Must be defined if CONFIG_ROLLBACK_SECRET_SIZE is set. May sleep.
+ *
+ * @param buffer Buffer to fill with entropy.
+ * @param len Buffer length.
+ *
+ * @return true if the buffer was filled, false on error.
+ */
+int board_get_entropy(void *buffer, int len);
+
+#endif
+
+#endif /* __CROS_EC_ROLLBACK_H */
diff --git a/include/rsa.h b/include/rsa.h
index 2fb896c652..a2d5f73393 100644
--- a/include/rsa.h
+++ b/include/rsa.h
@@ -22,6 +22,8 @@
* plus 4 for n0inv, aligned on a multiple of 16
* Put numerical constants here to please the linker script.
*/
+#ifndef CONFIG_RWSIG_TYPE_RWSIG
+/* vboot2 public keys are packed in a slightly different way. */
#if CONFIG_RSA_KEY_SIZE == 2048
#define RSA_PUBLIC_KEY_SIZE 528
#elif CONFIG_RSA_KEY_SIZE == 4096
@@ -31,54 +33,36 @@
#else
#error Unsupported RSA key size
#endif
+#endif /* ! CONFIG_RWSIG_TYPE_RWSIG */
#endif /* CONFIG_RSA */
#ifndef __ASSEMBLER__
#include "common.h"
+#include "util.h"
+#ifdef CONFIG_RWSIG_TYPE_RWSIG
+/* RSA public key definition, VBoot2 packing */
+struct rsa_public_key {
+ uint32_t size;
+ uint32_t n0inv; /* -1 / n[0] mod 2^32 */
+ uint32_t n[RSANUMWORDS]; /* modulus as little endian array */
+ uint32_t rr[RSANUMWORDS]; /* R^2 as little endian array */
+};
+#else
/* RSA public key definition */
struct rsa_public_key {
uint32_t n[RSANUMWORDS]; /* modulus as little endian array */
uint32_t rr[RSANUMWORDS]; /* R^2 as little endian array */
uint32_t n0inv; /* -1 / n[0] mod 2^32 */
};
+#endif
int rsa_verify(const struct rsa_public_key *key,
const uint8_t *signature,
const uint8_t *sha,
uint32_t *workbuf32);
-void check_rw_signature(void);
-
#endif /* !__ASSEMBLER__ */
-/*
- * The signer puts the public key and signature into the RO and RW images
- * (respectively) at known locations after the complete image is assembled. But
- * since we compile the RO & RW images separately, the other image's addresses
- * can't be computed by the linker. So we just hardcode the addresses here.
- * These can be overridden in board.h files if desired.
- */
-
-/* The pubkey goes at the end of the first half of flash */
-#ifndef CONFIG_RO_PUBKEY_SIZE
-#define CONFIG_RO_PUBKEY_SIZE RSA_PUBLIC_KEY_SIZE
-#endif
-#ifndef CONFIG_RO_PUBKEY_ADDR
-#define CONFIG_RO_PUBKEY_ADDR (CONFIG_PROGRAM_MEMORY_BASE \
- + (CONFIG_FLASH_SIZE / 2) \
- - CONFIG_RO_PUBKEY_SIZE)
-#endif
-
-/* The signature goes at the end of the second half of flash */
-#ifndef CONFIG_RW_SIG_SIZE
-#define CONFIG_RW_SIG_SIZE RSANUMBYTES
-#endif
-#ifndef CONFIG_RW_SIG_ADDR
-#define CONFIG_RW_SIG_ADDR (CONFIG_PROGRAM_MEMORY_BASE \
- + CONFIG_FLASH_SIZE \
- - CONFIG_RW_SIG_SIZE)
-#endif
-
#endif /* __CROS_EC_RSA_H */
diff --git a/include/rtc.h b/include/rtc.h
new file mode 100644
index 0000000000..fa56634c5b
--- /dev/null
+++ b/include/rtc.h
@@ -0,0 +1,45 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* RTC cross-platform functions */
+
+#ifndef __CROS_EC_RTC_H
+#define __CROS_EC_RTC_H
+
+#include "common.h"
+
+#define SECS_PER_DAY (60 * 60 * 24)
+#define SECS_PER_YEAR (365 * SECS_PER_DAY)
+/* The seconds elapsed from 01-01-1970 to 01-01-2000 */
+#define SECS_TILL_YEAR_2K (946684800)
+#define IS_LEAP_YEAR(x) \
+ (((x) % 4 == 0) && (((x) % 100 != 0) || ((x) % 400 == 0)))
+
+struct calendar_date {
+ /* The number of years since A.D. 2000, i.e. year = 17 for y2017 */
+ uint8_t year;
+ /* 1-based indexing, i.e. sane values range from 1 to 12 */
+ uint8_t month;
+ /* 1-based indexing, i.e. sane values range from 1 to 31 */
+ uint8_t day;
+};
+
+/**
+ * Convert calendar date to seconds elapsed since epoch time.
+ *
+ * @param time The calendar date (years, months, and days).
+ * @return the seconds elapsed since epoch time (01-01-1970 00:00:00).
+ */
+uint32_t date_to_sec(struct calendar_date time);
+
+/**
+ * Convert seconds elapsed since epoch time to calendar date
+ *
+ * @param sec The seconds elapsed since epoch time (01-01-1970 00:00:00).
+ * @return the calendar date (years, months, and days).
+ */
+struct calendar_date sec_to_date(uint32_t sec);
+
+#endif /* __CROS_EC_RTC_H */
diff --git a/include/rwsig.h b/include/rwsig.h
new file mode 100644
index 0000000000..c43932b173
--- /dev/null
+++ b/include/rwsig.h
@@ -0,0 +1,126 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __CROS_EC_RWSIG_H
+#define __CROS_EC_RWSIG_H
+
+#include "config.h"
+#include "rsa.h"
+
+#ifndef __ASSEMBLER__
+#ifdef HAS_TASK_RWSIG
+/* The functions below only make sense if RWSIG task is defined. */
+
+/* Current status of RW signature verification */
+enum rwsig_status {
+ RWSIG_UNKNOWN = 0, /* Unknown/not started */
+ RWSIG_IN_PROGRESS,
+ RWSIG_VALID,
+ RWSIG_INVALID,
+ RWSIG_ABORTED,
+};
+
+/* Returns current rwsig verification status. */
+enum rwsig_status rwsig_get_status(void);
+
+/*
+ * Aborts current verification, also prevents RWSIG task from automatically
+ * jumping to RW.
+ * This is used by usb_updater2 when a RW update is required, giving it enough
+ * time to actually perform the update.
+ */
+void rwsig_abort(void);
+
+/*
+ * Tells RWSIG task to jump to RW immediately, if the signature is correct.
+ * This is used by usb_updater2 when no RW update is required, to speed up
+ * boot time.
+ */
+void rwsig_continue(void);
+
+#else /* !HAS_TASK_RWSIG */
+/* These functions can only be called directly if RWSIG task is not defined. */
+
+/* Checks RW signature. Returns a boolean indicating success. */
+int rwsig_check_signature(void);
+
+/* Jumps to RW, if signature is fine, returns on error (otherwise, jumps). */
+void rwsig_jump_now(void);
+
+#endif
+
+#endif /* !__ASSEMBLER__ */
+
+/*
+ * The signer puts the public key and signature into the RO and RW images
+ * (respectively) at known locations after the complete image is assembled. But
+ * since we compile the RO & RW images separately, the other image's addresses
+ * can't be computed by the linker. So we just hardcode the addresses here.
+ * These can be overridden in board.h files if desired.
+ */
+
+#ifndef CONFIG_RO_PUBKEY_SIZE
+#ifdef CONFIG_RWSIG_TYPE_RWSIG
+/*
+ * rwsig type: 1024 bytes is enough to fit RSA-3072 public key.
+ *
+ * TODO(crosbug.com/p/62321): This still wastes space. We could pack the key at
+ * any arbitrary location, but we need proper signer support to make sure it
+ * can overwrite the key correctly.
+ */
+#define CONFIG_RO_PUBKEY_SIZE 1024
+#else
+#define CONFIG_RO_PUBKEY_SIZE RSA_PUBLIC_KEY_SIZE
+#endif
+#endif /* ! CONFIG_RO_PUBKEY_SIZE */
+#ifndef CONFIG_RO_PUBKEY_ADDR
+#ifdef CONFIG_RWSIG_TYPE_RWSIG
+#define CONFIG_RO_PUBKEY_STORAGE_OFF (CONFIG_RO_STORAGE_OFF \
+ + CONFIG_RO_SIZE \
+ - CONFIG_RO_PUBKEY_SIZE)
+
+/* The pubkey resides at the end of the RO image */
+#define CONFIG_RO_PUBKEY_ADDR (CONFIG_PROGRAM_MEMORY_BASE \
+ + CONFIG_EC_PROTECTED_STORAGE_OFF \
+ + CONFIG_RO_PUBKEY_STORAGE_OFF)
+#else
+/*
+ * usbpd1 type assumes pubkey location at the end of first half of flash,
+ * which might actually be in the PSTATE region.
+ */
+#define CONFIG_RO_PUBKEY_ADDR (CONFIG_PROGRAM_MEMORY_BASE \
+ + (CONFIG_FLASH_SIZE / 2) \
+ - CONFIG_RO_PUBKEY_SIZE)
+#endif
+#endif /* CONFIG_RO_PUBKEY_ADDR */
+
+#ifndef CONFIG_RW_SIG_SIZE
+#ifdef CONFIG_RWSIG_TYPE_RWSIG
+/*
+ * rwsig type: futility expects signature to be 1024 bytes from the end of
+ * the file.
+ */
+#define CONFIG_RW_SIG_SIZE 1024
+#else
+#define CONFIG_RW_SIG_SIZE RSANUMBYTES
+#endif
+#endif /* ! CONFIG_RW_SIG_SIZE */
+/* The signature resides at the end of each RW copy */
+#define RW_SIG_OFFSET (CONFIG_RW_SIZE - CONFIG_RW_SIG_SIZE)
+#define RW_A_ADDR (CONFIG_PROGRAM_MEMORY_BASE + \
+ CONFIG_EC_WRITABLE_STORAGE_OFF + \
+ CONFIG_RW_STORAGE_OFF)
+/* Assume the layout is same as RW_A and it sits right after RW_A */
+#define RW_B_ADDR (CONFIG_PROGRAM_MEMORY_BASE + \
+ CONFIG_EC_WRITABLE_STORAGE_OFF + \
+ CONFIG_RW_B_STORAGE_OFF)
+#ifndef CONFIG_RW_SIG_ADDR
+#define CONFIG_RW_SIG_ADDR (RW_A_ADDR + RW_SIG_OFFSET)
+#endif
+#ifndef CONFIG_RW_B_SIG_ADDR
+#define CONFIG_RW_B_SIG_ADDR (RW_B_ADDR + RW_SIG_OFFSET)
+#endif
+
+#endif /* __CROS_EC_RWSIG_H */
diff --git a/include/sha256.h b/include/sha256.h
index 11260f9351..8fbb19dea2 100644
--- a/include/sha256.h
+++ b/include/sha256.h
@@ -26,4 +26,7 @@ void SHA256_init(struct sha256_ctx *ctx);
void SHA256_update(struct sha256_ctx *ctx, const uint8_t *data, uint32_t len);
uint8_t *SHA256_final(struct sha256_ctx *ctx);
+void hmac_SHA256(uint8_t *output, const uint8_t *key, const int key_len,
+ const uint8_t *message, const int message_len);
+
#endif /* __CROS_EC_SHA256_H */
diff --git a/include/shared_mem.h b/include/shared_mem.h
index f7b8bcbccb..b99cdb1341 100644
--- a/include/shared_mem.h
+++ b/include/shared_mem.h
@@ -18,11 +18,7 @@
#define __CROS_EC_SHARED_MEM_H
#include "common.h"
-
-/**
- * Initializes the module.
- */
-int shared_mem_init(void);
+#include <stddef.h>
/**
* Returns the maximum amount of shared memory which can be acquired, in
@@ -30,9 +26,14 @@ int shared_mem_init(void);
*/
int shared_mem_size(void);
-/**
+/*
* Acquires a shared memory area of the requested size in bytes.
*
+ * Doing a sysjump between images will corrupt and/or erase shared memory as
+ * jump tags are added and .bss is reinitialized. Due to the way jump tags are
+ * added to the end of RAM, shared memory must not be allocated, accessed, or
+ * freed after the start of a sysjump (for example, in HOOK_SYSJUMP).
+ *
* @param size Number of bytes requested
* @param dest_ptr If successful, set on return to the start of the
* granted memory buffer.
@@ -47,4 +48,32 @@ int shared_mem_acquire(int size, char **dest_ptr);
*/
void shared_mem_release(void *ptr);
+/*
+ * This structure is allocated at the base of the free memory chunk and every
+ * allocated buffer.
+ */
+struct shm_buffer {
+ struct shm_buffer *next_buffer;
+ struct shm_buffer *prev_buffer;
+ size_t buffer_size;
+};
+
+#ifdef TEST_BUILD
+
+/*
+ * When in test mode, all possible paths in the allocation/free functions set
+ * unique bits in an integer bitmap.
+ *
+ * The test function generates random allocation and free requests and
+ * monitors the bitmap until all bits have been set, which indicates that all
+ * possible paths have been executed.
+ */
+
+#define MAX_MASK_BIT 24
+#define ALL_PATHS_MASK ((1 << (MAX_MASK_BIT + 1)) - 1)
+void set_map_bit(uint32_t mask);
+extern struct shm_buffer *free_buf_chain;
+extern struct shm_buffer *allocced_buf_chain;
+#endif
+
#endif /* __CROS_EC_SHARED_MEM_H */
diff --git a/include/smbus.h b/include/smbus.h
index 367ceceb8d..15d42084b0 100644
--- a/include/smbus.h
+++ b/include/smbus.h
@@ -126,7 +126,7 @@ int smbus_read_word(uint8_t i2c_port, uint8_t slave_addr,
* @param plen uint8_t *, a pointer data length
* @return error_code
* EC_SUCCESS if success; none-zero if fail
- * EC_ERROR_BUSY if interface is bussy
+ * EC_ERROR_BUSY if interface is busy
* none zero error code if fail
*/
int smbus_read_block(uint8_t i2c_port, uint8_t slave_addr,
@@ -138,7 +138,7 @@ int smbus_read_block(uint8_t i2c_port, uint8_t slave_addr,
* Read bytestream from <slaveaddr>:<smbus_cmd> with format:
* [length_N] [byte_0] [byte_1] ... [byte_N-1][byte_N='\0']
*
- * <len> : the max length of receving buffer. to read N bytes
+ * <len> : the max length of receiving buffer. to read N bytes
* ascii, len should be at least N+1 to include the
* terminating 0 (NULL).
*
diff --git a/include/software_panic.h b/include/software_panic.h
index 15070f95ed..4afc20b184 100644
--- a/include/software_panic.h
+++ b/include/software_panic.h
@@ -20,5 +20,6 @@
#define PANIC_SW_PD_CRASH (PANIC_SW_BASE + 2)
#define PANIC_SW_ASSERT (PANIC_SW_BASE + 3)
#define PANIC_SW_WATCHDOG (PANIC_SW_BASE + 4)
+#define PANIC_SW_BAD_RNG (PANIC_SW_BASE + 5)
#endif /* __CROS_EC_SOFTWARE_PANIC_H */
diff --git a/include/spi.h b/include/spi.h
index 8fd7b2e311..49bddd3cc3 100644
--- a/include/spi.h
+++ b/include/spi.h
@@ -64,17 +64,22 @@ extern const unsigned int spi_devices_used;
*/
int spi_enable(int port, int enable);
+#define SPI_READBACK_ALL (-1)
+
/* Issue a SPI transaction. Assumes SPI port has already been enabled.
*
* Transmits <txlen> bytes from <txdata>, throwing away the corresponding
* received data, then transmits <rxlen> dummy bytes, saving the received data
* in <rxdata>.
+ * If SPI_READBACK_ALL is set in <rxlen>, the received data during transmission
+ * is recorded in rxdata buffer and it assumes that the real <rxlen> is equal
+ * to <txlen>.
*
* @param spi_device the SPI device to use
* @param txdata buffer to transmit
* @param txlen number of bytes in txdata.
* @param rxdata receive buffer.
- * @param rxlen number of bytes in rxdata.
+ * @param rxlen number of bytes in rxdata or SPI_READBACK_ALL.
*/
int spi_transaction(const struct spi_device_t *spi_device,
const uint8_t *txdata, int txlen,
@@ -91,6 +96,9 @@ int spi_transaction_async(const struct spi_device_t *spi_device,
/* Wait for async response received */
int spi_transaction_flush(const struct spi_device_t *spi_device);
+/* Wait for async response received but do not de-assert chip select */
+int spi_transaction_wait(const struct spi_device_t *spi_device);
+
#ifdef CONFIG_SPI
/**
* Called when the NSS level changes, signalling the start or end of a SPI
diff --git a/include/spi_flash.h b/include/spi_flash.h
index 9b8a0a64d5..4e4dd96381 100644
--- a/include/spi_flash.h
+++ b/include/spi_flash.h
@@ -123,18 +123,28 @@ int spi_flash_write(unsigned int offset, unsigned int bytes,
const uint8_t const *data);
/**
- * Returns the SPI flash JEDEC ID (manufacturer ID, memory type, and capacity)
+ * Gets the SPI flash JEDEC ID (manufacturer ID, memory type, and capacity)
*
- * @return flash JEDEC ID
+ * @param dest Destination buffer; must be 3 bytes long
+ * @return EC_SUCCESS or non-zero on error
*/
-uint32_t spi_flash_get_jedec_id(void);
+int spi_flash_get_jedec_id(uint8_t *dest);
/**
- * Returns the SPI flash unique ID (serial)
+ * Gets the SPI flash manufacturer and device ID
*
- * @return flash unique ID
+ * @param dest Destination buffer; must be 2 bytes long
+ * @return EC_SUCCESS or non-zero on error
*/
-uint64_t spi_flash_get_unique_id(void);
+int spi_flash_get_mfr_dev_id(uint8_t *dest);
+
+/**
+ * Gets the SPI flash unique ID (serial)
+ *
+ * @param dest Destination buffer; must be 8 bytes long
+ * @return EC_SUCCESS or non-zero on error
+ */
+int spi_flash_get_unique_id(uint8_t *dest);
/**
* Check for SPI flash status register write protection.
diff --git a/include/spi_nor.h b/include/spi_nor.h
index d6ad93f221..07be853520 100644
--- a/include/spi_nor.h
+++ b/include/spi_nor.h
@@ -79,6 +79,7 @@ extern const unsigned int spi_nor_devices_used;
/* If needed in the future this driver can be extended to discover SFDP
* advertised erase sizes and opcodes for SFDP v1.0+. */
#define SPI_NOR_DRIVER_SPECIFIED_OPCODE_4KIB_ERASE 0x20
+#define SPI_NOR_DRIVER_SPECIFIED_OPCODE_64KIB_ERASE 0xd8
/* If needed in the future this driver can be extended to discover 4B entry and
* exit methods for SFDP v1.5+. */
@@ -120,6 +121,17 @@ int spi_nor_set_4b_mode(struct spi_nor_device_t *spi_nor_device,
int enter_4b_addressing_mode);
/**
+ * Read JEDEC Identifier.
+ *
+ * @param spi_nor_device The Serial NOR Flash device to use.
+ * @param size Number of Bytes to read.
+ * @param data Destination buffer for data.
+ * @return ec_error_list (non-zero on error and timeout).
+ */
+int spi_nor_read_jedec_id(const struct spi_nor_device_t *spi_nor_device,
+ size_t size, uint8_t *data);
+
+/**
* Read from the Serial NOR Flash device.
*
* @param spi_nor_device The Serial NOR Flash device to use.
diff --git a/include/system.h b/include/system.h
index 92a47e2a37..5587446ff9 100644
--- a/include/system.h
+++ b/include/system.h
@@ -10,6 +10,7 @@
#include "atomic.h"
#include "common.h"
+#include "console.h"
#include "timer.h"
/* Reset causes */
@@ -41,12 +42,20 @@ enum system_image_copy_t {
SYSTEM_IMAGE_UNKNOWN = 0,
SYSTEM_IMAGE_RO,
SYSTEM_IMAGE_RW,
+ SYSTEM_IMAGE_RW_A = SYSTEM_IMAGE_RW,
/* Some systems may have these too */
SYSTEM_IMAGE_RO_B,
SYSTEM_IMAGE_RW_B,
};
/**
+ * Checks if running image is RW or not
+ *
+ * @return True if system is running in a RW image or false otherwise.
+ */
+int system_is_in_rw(void);
+
+/**
* Pre-initializes the module. This occurs before clocks or tasks are
* set up.
*/
@@ -193,6 +202,15 @@ int system_get_image_used(enum system_image_copy_t copy);
int system_run_image_copy(enum system_image_copy_t copy);
/**
+ * Get the rollback version for an image
+ *
+ * @param copy Image copy to get version from, or SYSTEM_IMAGE_UNKNOWN
+ * to get the version for the currently running image.
+ * @return The rollback version, negative value on error.
+ */
+int32_t system_get_rollback_version(enum system_image_copy_t copy);
+
+/**
* Get the version string for an image
*
* @param copy Image copy to get version from, or SYSTEM_IMAGE_UNKNOWN
@@ -203,6 +221,14 @@ int system_run_image_copy(enum system_image_copy_t copy);
const char *system_get_version(enum system_image_copy_t copy);
/**
+ * Get the SKU ID for a device
+ *
+ * @return A value that identifies the SKU variant of a model. Its meaning and
+ * the number of bits actually used is opaque outside board specific code.
+ */
+uint32_t system_get_sku_id(void);
+
+/**
* Return the board version number. The meaning of this number is
* board-dependent; boards where the code actually cares about this should
* declare enum board_version in board.h.
@@ -220,24 +246,28 @@ const char *system_get_build_info(void);
* Hard reset. Cuts power to the entire system. If not present, does a soft
* reset which just resets the core and on-chip peripherals.
*/
-#define SYSTEM_RESET_HARD (1 << 0)
+#define SYSTEM_RESET_HARD (1 << 0)
/*
* Preserve existing reset flags. Used by flash pre-init when it discovers it
* needs to do a hard reset to clear write protect registers.
*/
-#define SYSTEM_RESET_PRESERVE_FLAGS (1 << 1)
+#define SYSTEM_RESET_PRESERVE_FLAGS (1 << 1)
/*
* Leave AP off on next reboot, instead of powering it on to do EC software
* sync.
*/
-#define SYSTEM_RESET_LEAVE_AP_OFF (1 << 2)
+#define SYSTEM_RESET_LEAVE_AP_OFF (1 << 2)
+/*
+ * Indicate that this was a manually triggered reset.
+ */
+#define SYSTEM_RESET_MANUALLY_TRIGGERED (1 << 3)
/**
* Reset the system.
*
* @param flags Reset flags; see SYSTEM_RESET_* above.
*/
-void system_reset(int flags);
+void system_reset(int flags) __attribute__((noreturn));
/**
* Set a scratchpad register to the specified value.
@@ -263,14 +293,53 @@ const char *system_get_chip_name(void);
const char *system_get_chip_revision(void);
/**
- * Get/Set VbNvContext in non-volatile storage. The block should be 16 bytes
- * long, which is the current size of VbNvContext block.
+ * Get a unique per-chip id.
+ *
+ * @param id Set to the address of the unique id data (statically
+ * allocated, or register-backed).
+ * @return Number of bytes available at the provided address.
+ */
+int system_get_chip_unique_id(uint8_t **id);
+
+/**
+ * Optional board-level callback functions to read a unique serial number per
+ * chip. Default implementation reads from flash/otp (flash/otp_read_serial).
+ */
+const char *board_read_serial(void) __attribute__((weak));
+
+/**
+ * Optional board-level callback functions to write a unique serial number per
+ * chip. Default implementation reads from flash/otp (flash/otp_write_serial).
+ */
+int board_write_serial(const char *serial) __attribute__((weak));
+/*
+ * Common bbram entries. Chips don't necessarily need to implement
+ * all of these, error will be returned from system_get/set_bbram if
+ * not implemented.
+ */
+enum system_bbram_idx {
+ SYSTEM_BBRAM_IDX_VBNVBLOCK0 = 0,
+ /*
+ * ...
+ * 16 total bytes of VB NVRAM.
+ * ...
+ */
+ SYSTEM_BBRAM_IDX_VBNVBLOCK15 = 15,
+ /* PD state for CONFIG_USB_PD_DUAL_ROLE uses one byte per port */
+ SYSTEM_BBRAM_IDX_PD0,
+ SYSTEM_BBRAM_IDX_PD1,
+ SYSTEM_BBRAM_IDX_TRY_SLOT,
+};
+
+/**
+ * Get/Set byte in battery-backed storage.
*
- * @param block Pointer to a buffer holding VbNvContext.
- * @return 0 on success, !0 on error.
+ * @param idx bbram byte to get / set.
+ * @param value byte to read / write from / to bbram.
+ * @return 0 on success, !0 on error.
*/
-int system_get_vbnvcontext(uint8_t *block);
-int system_set_vbnvcontext(const uint8_t *block);
+int system_get_bbram(enum system_bbram_idx idx, uint8_t *value);
+int system_set_bbram(enum system_bbram_idx idx, uint8_t value);
/**
* Put the EC in hibernate (lowest EC power state).
@@ -309,6 +378,18 @@ void board_hibernate_late(void) __attribute__((weak));
timestamp_t system_get_rtc(void);
/**
+ * Print out the current real-time clock value to the console.
+ *
+ * @param channel Console channel to print on.
+ */
+/* TODO(aaboagye): Replace this with CONFIG_RTC eventually. */
+#ifdef CONFIG_CMD_RTC
+void print_system_rtc(enum console_channel channel);
+#else
+static inline void print_system_rtc(enum console_channel channel) { }
+#endif /* !defined(CONFIG_CMD_RTC) */
+
+/**
* Enable hibernate interrupt
*/
void system_enable_hib_interrupt(void);
@@ -328,8 +409,9 @@ enum {
SLEEP_MASK_I2C_SLAVE = (1 << 7), /* I2C slave communication ongoing */
SLEEP_MASK_FAN = (1 << 8), /* Fan control loop ongoing */
SLEEP_MASK_USB_DEVICE = (1 << 9), /* Generic USB device in use */
- SLEEP_MASK_RDD = (1 << 10),/* RDD ongoing */
-
+ SLEEP_MASK_PWM = (1 << 10), /* PWM output is enabled */
+ SLEEP_MASK_PHYSICAL_PRESENCE = (1 << 11), /* Physical presence
+ * detection ongoing */
SLEEP_MASK_FORCE_NO_DSLEEP = (1 << 15), /* Force disable. */
@@ -383,6 +465,7 @@ static inline void disable_sleep(uint32_t mask)
atomic_or(&sleep_mask, mask);
}
+/* The following three functions are not available on all chips. */
/**
* Postpone sleeping for at least this long, regardless of sleep_mask.
*
@@ -390,6 +473,16 @@ static inline void disable_sleep(uint32_t mask)
*/
void delay_sleep_by(uint32_t us);
+/*
+ **
+ * Funtctions to control deep sleep behavior. When disabled - the device never
+ * falls into deep sleep (the lowest power consumption state exit of which
+ * usually happens through the regular reset vector with just a few bits of
+ * state preserved).
+ */
+void disable_deep_sleep(void);
+void enable_deep_sleep(void);
+
/**
* Use hibernate module to set up an RTC interrupt at a given
* time from now
@@ -454,59 +547,52 @@ uintptr_t system_get_fw_reset_vector(uintptr_t base);
*/
int system_is_reboot_warm(void);
+#ifdef CONFIG_EXTENDED_VERSION_INFO
+void system_print_extended_version_info(void);
+#else
+static inline void system_print_extended_version_info(void)
+{
+}
+#endif
+
/**
- * On systems with protection from a failing RW update: read the retry counter
- * and act on it.
+ * Check if the system can supply enough power to boot AP
*
- * @return EC_SUCCESS if no flash write errors were encounterd.
+ * @return true if the system is powered enough or false otherwise
*/
-int system_process_retry_counter(void);
+int system_can_boot_ap(void);
/**
- * On systems with protection from a failing RW update: reset retry
- * counter, this is used after a new image upload is finished, to make
- * sure that the new image has a chance to run.
+ * Get active image copy
+ *
+ * Active slot contains an image which is being executed or will be executed
+ * after sysjump.
+ *
+ * @return Active copy index
*/
-void system_clear_retry_counter(void);
-
-
-/* Board properties options */
-#define BOARD_SLAVE_CONFIG_SPI (1 << 0) /* Slave SPI interface */
-#define BOARD_SLAVE_CONFIG_I2C (1 << 1) /* Slave I2C interface */
-#define BOARD_USB_AP (1 << 2) /* One of the PHYs is */
- /* connected to the AP */
-#define BOARD_DISABLE_UART0_RX (1 << 3) /* Disable UART0 RX */
-#define BOARD_MARK_UPDATE_ON_USB_REQ (1 << 4) /* update is good once the */
- /* controller gets a request */
-/* TODO(crosbug.com/p/56945): Remove when sys_rst_l has an external pullup */
-#define BOARD_NEEDS_SYS_RST_PULL_UP (1 << 5) /* Add a pullup to sys_rst_l */
+enum system_image_copy_t system_get_active_copy(void);
/**
- * Get board properites
+ * Get updatable (non-active) image copy
*
- *
- * @return uint32_t bit field where a set bit indicates option exists
+ * @return Updatable copy index
*/
-uint32_t system_get_board_properties(void);
-
-/* Board specific function used to initialize the system board properties. */
-void system_init_board_properties(void);
+enum system_image_copy_t system_get_update_copy(void);
/**
- * API for board specific version of system_get_board_properties
- *
- * This function must be in the board's board.c file
+ * Set active image copy
*
- * @return uint32_t bit field where a set bit indicates option exists
+ * @param copy Copy id to be activated.
+ * @return Non-zero if error.
*/
-uint32_t system_board_properties_callback(void);
+int system_set_active_copy(enum system_image_copy_t copy);
/**
- * A function provided by some platforms to hint that something is going
- * wrong.
+ * Get flash offset of a RW copy
*
- * @return a boolean, set to True if rolling reboot condition is suspected.
+ * @param copy Copy index to get the flash offset of.
+ * @return Flash offset of the slot storing <copy>
*/
-int system_rolling_reboot_suspected(void);
+uint32_t flash_get_rw_offset(enum system_image_copy_t copy);
#endif /* __CROS_EC_SYSTEM_H */
diff --git a/include/tablet_mode.h b/include/tablet_mode.h
new file mode 100644
index 0000000000..6f4ee95e1e
--- /dev/null
+++ b/include/tablet_mode.h
@@ -0,0 +1,12 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Header for tablet_mode.c */
+
+/* Return 1 if in tablet mode, 0 otherwise */
+int tablet_get_mode(void);
+void tablet_set_mode(int mode);
+
+
diff --git a/include/task.h b/include/task.h
index e177bae546..78c8ad9c06 100644
--- a/include/task.h
+++ b/include/task.h
@@ -19,6 +19,7 @@
#define TASK_EVENT_PECI_DONE (1 << 19)
/* I2C tx/rx interrupt handler completion event. */
+#ifdef CHIP_STM32
#define TASK_EVENT_I2C_COMPLETION(port) \
(1 << ((port) + 20))
#define TASK_EVENT_I2C_IDLE (TASK_EVENT_I2C_COMPLETION(0))
@@ -28,6 +29,9 @@
#error "Too many i2c ports for i2c events"
#endif
#endif
+#else
+#define TASK_EVENT_I2C_IDLE (1 << 20)
+#endif
/* DMA transmit complete event */
#define TASK_EVENT_DMA_TC (1 << 26)
diff --git a/include/task_filter.h b/include/task_filter.h
new file mode 100644
index 0000000000..af80194e7f
--- /dev/null
+++ b/include/task_filter.h
@@ -0,0 +1,50 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Filter tasklist in *.tasklist, depending on section (RO/RW), or
+ * TEST/CTS build.
+ */
+
+#ifndef __CROS_EC_TASK_FILTER_H
+#define __CROS_EC_TASK_FILTER_H
+
+#ifdef SECTION_IS_RO
+#define TASK_NOTEST_RO TASK_NOTEST
+#define TASK_TEST_RO TASK_TEST
+#define TASK_ALWAYS_RO TASK_ALWAYS
+#define TASK_NOTEST_RW(n, r, d, s)
+#define TASK_TEST_RW(n, r, d, s)
+#define TASK_ALWAYS_RW(n, r, d, s)
+#else /* SECTION_IS_RW */
+#define TASK_NOTEST_RW TASK_NOTEST
+#define TASK_TEST_RW TASK_TEST
+#define TASK_ALWAYS_RW TASK_ALWAYS
+#define TASK_NOTEST_RO(n, r, d, s)
+#define TASK_TEST_RO(n, r, d, s)
+#define TASK_ALWAYS_RO(n, r, d, s)
+#endif
+
+/* excludes non-base tasks for test build */
+#ifdef TEST_BUILD
+#define TASK_NOTEST(n, r, d, s)
+#define TASK_TEST TASK
+#else
+#define TASK_NOTEST TASK
+#define CONFIG_TEST_TASK_LIST
+#endif
+
+#ifndef CTS_MODULE
+#define CONFIG_CTS_TASK_LIST
+#endif
+
+#define TASK_ALWAYS TASK
+
+/* If included directly from Makefile, dump task list. */
+#ifdef _MAKEFILE
+#define TASK(n, r, d, s) n
+CONFIG_TASK_LIST CONFIG_TEST_TASK_LIST CONFIG_CTS_TASK_LIST
+#endif
+
+
+#endif /* __CROS_EC_TASK_FILTER_H */
diff --git a/include/task_id.h b/include/task_id.h
index bcd0b61c85..b4ecf9bf82 100644
--- a/include/task_id.h
+++ b/include/task_id.h
@@ -8,20 +8,7 @@
#ifndef __CROS_EC_TASK_ID_H
#define __CROS_EC_TASK_ID_H
-/* excludes non-base tasks for test build */
-#ifdef TEST_BUILD
-#define TASK_NOTEST(n, r, d, s)
-#define TASK_TEST TASK
-#else
-#define TASK_NOTEST TASK
-#define CONFIG_TEST_TASK_LIST
-#endif
-
-#ifndef CTS_MODULE
-#define CONFIG_CTS_TASK_LIST
-#endif
-
-#define TASK_ALWAYS TASK
+#include "task_filter.h"
/* define the name of the header containing the list of tasks */
#define STRINGIFY0(name) #name
diff --git a/include/tpm_log.h b/include/tpm_log.h
new file mode 100644
index 0000000000..cb1b6e099a
--- /dev/null
+++ b/include/tpm_log.h
@@ -0,0 +1,19 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __CROS_EC_TPM_LOG_H
+#define __CROS_EC_TPM_LOG_H
+
+#include "event_log.h"
+
+enum tpm_event {
+ TPM_EVENT_INIT,
+ TPM_I2C_RESET,
+};
+
+/* Log TPM event of given type with data payload. */
+void tpm_log_event(enum tpm_event type, uint16_t data);
+
+#endif /* __CROS_EC_TPM_LOG_H */
diff --git a/include/tpm_manufacture.h b/include/tpm_manufacture.h
index f43fd9ec13..df43bcc886 100644
--- a/include/tpm_manufacture.h
+++ b/include/tpm_manufacture.h
@@ -13,6 +13,23 @@
/* Returns non-zero if the TPM manufacture steps have been completed. */
int tpm_manufactured(void);
-int tpm_endorse(void);
+
+/* Codes for success and various manufacturing error conditions. */
+enum manufacturing_status {
+ mnf_success = 0,
+ mnf_no_certs = 1,
+ mnf_eps_decr = 2,
+ mnf_bad_rsa_size = 3,
+ mnf_bad_total_size = 4,
+ mnf_bad_rsa_type = 5,
+ mnf_bad_ecc_type = 6,
+ mnf_hmac_mismatch = 7,
+ mnf_rsa_proc = 8,
+ mnf_ecc_proc = 9,
+ mnf_store = 10,
+ mnf_manufactured = 11,
+};
+
+enum manufacturing_status tpm_endorse(void);
#endif /* __CROS_EC_TPM_MANUFACTURE_H */
diff --git a/include/tpm_registers.h b/include/tpm_registers.h
index 3416492f03..b82a355170 100644
--- a/include/tpm_registers.h
+++ b/include/tpm_registers.h
@@ -23,13 +23,44 @@ void tpm_register_put(uint32_t regaddr,
/* The SPI master is reading data from a TPM register. */
void tpm_register_get(uint32_t regaddr, uint8_t *dest, uint32_t data_size);
-/* Enable SPS TPM driver. */
-void sps_tpm_enable(void);
-
/* Get the current value of the burst size field of the status register. */
size_t tpm_get_burst_size(void);
/*
+ * Register functions to start and stop TPM communications layer. The
+ * communications layer should be kept down while TPM is being reset.
+ */
+typedef void (*interface_control_func)(void);
+void tpm_register_interface(interface_control_func interface_start,
+ interface_control_func interface_stop);
+
+/*
+ * This requests the TPM task to reset itself.
+ *
+ * If wait_until_done is false, it returns EC_SUCCESS immediately. Otherwise it
+ * returns EC_SUCCESS after the reset has completed, or an error code on
+ * failure.
+ *
+ * If wipe_nvmem_first is true, the caller is expected to keep the rest of the
+ * system in reset until TPM wipeout is completed.
+ */
+int tpm_reset_request(int wait_until_done, int wipe_nvmem_first);
+
+/*
+ * Tell the TPM task to re-enable nvmem commits.
+ *
+ * NOTE: This function is NOT to be used freely, but only meant to be used in
+ * exceptional cases such as unlocking the console following a TPM wipe.
+ */
+void tpm_reinstate_nvmem_commits(void);
+
+/*
+ * To be called by functions running on the TPM task context. Returns
+ * EC_SUCCESS on successful reset.
+ */
+int tpm_sync_reset(int wipe_first);
+
+/*
* This structure describes the header of all commands and responses sent and
* received over TPM FIFO.
*
@@ -44,9 +75,30 @@ struct tpm_cmd_header {
} __packed;
/*
+ * This function allows to process a TPM command coming from elsewhere, not
+ * from the communications interface.
+ *
+ * A common use case would be making cryptographic calculation on task
+ * contexts where stack the size is not large enough, for instance console
+ * commands. This function will block to let the TPM task a chance to run to
+ * execute the command and return the result in the same buffer.
+ *
+ * @param tpmh pointer to a buffer containing a marshalled TPM command, if it
+ * arrived over the communications channel. One of the header
+ * fields defines the command size.
+ *
+ * @param buffer_size the size of the buffer pointed to by tpmh - tells the
+ * TPM task how much room there is to store the response.
+ *
+ * Command execution result is reported in the response body.
+ */
+void tpm_alt_extension(struct tpm_cmd_header *tpmh, size_t buffer_size);
+
+/*
* The only TPM2 command we care about on the driver level, see
* crosbug.com/p/55667 for detals.
*/
#define TPM2_PCR_Read 0x0000017e
+#define TPM2_Startup 0x00000144
#endif /* __CROS_EC_TPM_REGISTERS_H */
diff --git a/include/tpm_vendor_cmds.h b/include/tpm_vendor_cmds.h
index 4861457d36..4091ac646b 100644
--- a/include/tpm_vendor_cmds.h
+++ b/include/tpm_vendor_cmds.h
@@ -6,10 +6,12 @@
#ifndef __INCLUDE_TPM_VENDOR_CMDS_H
#define __INCLUDE_TPM_VENDOR_CMDS_H
+#include "common.h" /* For __packed. */
+
/*
* This file includes definitions of extended/vendor TPM2 commands and their
* return codes. The definitions are shared between the embedded code and the
- * usb_updater utility running on the host.
+ * gsctool utility running on the host.
*/
/* Extension and vendor commands. */
@@ -40,6 +42,31 @@ enum vendor_cmd_cc {
VENDOR_CC_TURN_UPDATE_ON = 24,
VENDOR_CC_GET_BOARD_ID = 25,
VENDOR_CC_SET_BOARD_ID = 26,
+ VENDOR_CC_U2F_APDU = 27,
+ VENDOR_CC_POP_LOG_ENTRY = 28,
+ VENDOR_CC_GET_REC_BTN = 29,
+ VENDOR_CC_RMA_CHALLENGE_RESPONSE = 30,
+
+ /* A gap left for the no longer supported CCD password command. */
+
+ /*
+ * Disable factory mode. Reset all ccd capabilities to default and reset
+ * write protect to follow battery presence.
+ */
+ VENDOR_CC_DISABLE_FACTORY = 32,
+ VENDOR_CC_MANAGE_CCD_PWD = 33,
+ VENDOR_CC_CCD = 34,
+ VENDOR_CC_GET_ALERTS_DATA = 35,
+ VENDOR_CC_SPI_HASH = 36,
+ VENDOR_CC_PINWEAVER = 37,
+ /*
+ * Check the factory reset settings. If they're all set correctly, do a
+ * factory reset to enable ccd factory mode. All capabilities will be
+ * set to Always and write protect will be permanently disabled. This
+ * mode can't be reset unless VENDOR_CC_DISABLE_FACTORY is called or
+ * the 'ccd reset' console command is run.
+ */
+ VENDOR_CC_RESET_FACTORY = 38,
LAST_VENDOR_COMMAND = 65535,
};
@@ -58,6 +85,14 @@ enum vendor_cmd_rc {
VENDOR_RC_BOGUS_ARGS = 1,
VENDOR_RC_READ_FLASH_FAIL = 2,
VENDOR_RC_WRITE_FLASH_FAIL = 3,
+ VENDOR_RC_REQUEST_TOO_BIG = 4,
+ VENDOR_RC_RESPONSE_TOO_BIG = 5,
+ VENDOR_RC_INTERNAL_ERROR = 6,
+ VENDOR_RC_NOT_ALLOWED = 7,
+ VENDOR_RC_NO_SUCH_SUBCOMMAND = 8,
+ VENDOR_RC_IN_PROGRESS = 9,
+ VENDOR_RC_PASSWORD_REQUIRED = 10,
+
/* Only 7 bits available; max is 127 */
VENDOR_RC_NO_SUCH_COMMAND = 127,
};
@@ -89,5 +124,38 @@ enum vendor_cmd_rc {
*/
#define VENDOR_RC_ERR 0x00000500
+/*** Structures and constants for VENDOR_CC_SPI_HASH ***/
+
+enum vendor_cc_spi_hash_request_subcmd {
+ /* Relinquish the bus */
+ SPI_HASH_SUBCMD_DISABLE = 0,
+ /* Acquire the bus for AP SPI */
+ SPI_HASH_SUBCMD_AP = 1,
+ /* Acquire the bus for EC SPI */
+ SPI_HASH_SUBCMD_EC = 2,
+ /* Hash SPI data */
+ SPI_HASH_SUBCMD_SHA256 = 4,
+ /* Read SPI data */
+ SPI_HASH_SUBCMD_DUMP = 5,
+ /* Poll spi hash PP state. */
+ SPI_HASH_PP_POLL = 6,
+};
+
+enum vendor_cc_spi_hash_request_flags {
+ /* EC uses gang programmer mode */
+ SPI_HASH_FLAG_EC_GANG = (1 << 0),
+};
+
+/* Structure for VENDOR_CC_SPI_HASH request which follows tpm_header */
+struct vendor_cc_spi_hash_request {
+ uint8_t subcmd; /* See vendor_cc_spi_hash_request_subcmd */
+ uint8_t flags; /* See vendor_cc_spi_hash_request_flags */
+ /* Offset and size used by SHA256 and DUMP; ignored by other subcmds */
+ uint32_t offset; /* Offset in flash to hash/read */
+ uint32_t size; /* Size in bytes to hash/read */
+} __packed;
+
+/* Maximum size of a response = SHA-256 hash or 1-32 bytes of data */
+#define SPI_HASH_MAX_RESPONSE_BYTES 32
#endif /* __INCLUDE_TPM_VENDOR_CMDS_H */
diff --git a/include/trng.h b/include/trng.h
index e6dcba644c..ec56829e54 100644
--- a/include/trng.h
+++ b/include/trng.h
@@ -6,6 +6,7 @@
#define __EC_INCLUDE_TRNG_H
#include <stddef.h>
+#include <stdint.h>
/**
* Initialize the true random number generator.
@@ -15,6 +16,16 @@
void init_trng(void);
/**
+ * Shutdown the true random number generator.
+ *
+ * The opposite operation of init_trng(), disable the hardware resources
+ * used by the TRNG to save power.
+ *
+ * Not supported by all platforms.
+ **/
+void exit_trng(void);
+
+/**
* Retrieve a 32 bit random value.
*
* Not supported on all platforms.
diff --git a/include/u2f.h b/include/u2f.h
new file mode 100644
index 0000000000..cf7d92fa4f
--- /dev/null
+++ b/include/u2f.h
@@ -0,0 +1,106 @@
+// Common U2F raw message format header - Review Draft
+// 2014-10-08
+// Editor: Jakob Ehrensvard, Yubico, jakob@yubico.com
+
+#ifndef __U2F_H_INCLUDED__
+#define __U2F_H_INCLUDED__
+
+#ifdef _MSC_VER // Windows
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+typedef unsigned long int uint64_t;
+#else
+#include <stdint.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// General constants
+
+#define U2F_EC_KEY_SIZE 32 // EC key size in bytes
+#define U2F_EC_POINT_SIZE ((U2F_EC_KEY_SIZE * 2) + 1) // Size of EC point
+#define U2F_MAX_KH_SIZE 128 // Max size of key handle
+#define U2F_MAX_ATT_CERT_SIZE 2048 // Max size of attestation certificate
+#define U2F_MAX_EC_SIG_SIZE 72 // Max size of DER coded EC signature
+#define U2F_CTR_SIZE 4 // Size of counter field
+#define U2F_APPID_SIZE 32 // Size of application id
+#define U2F_CHAL_SIZE 32 // Size of challenge
+
+#define ENC_SIZE(x) ((x + 7) & 0xfff8)
+
+// EC (uncompressed) point
+
+#define U2F_POINT_UNCOMPRESSED 0x04 // Uncompressed point format
+
+typedef struct {
+ uint8_t pointFormat; // Point type
+ uint8_t x[U2F_EC_KEY_SIZE]; // X-value
+ uint8_t y[U2F_EC_KEY_SIZE]; // Y-value
+} U2F_EC_POINT;
+
+// U2F native commands
+
+#define U2F_REGISTER 0x01 // Registration command
+#define U2F_AUTHENTICATE 0x02 // Authenticate/sign command
+#define U2F_VERSION 0x03 // Read version string command
+
+#define U2F_VENDOR_FIRST 0x40 // First vendor defined command
+#define U2F_VENDOR_LAST 0xbf // Last vendor defined command
+
+// U2F_CMD_REGISTER command defines
+
+#define U2F_REGISTER_ID 0x05 // Version 2 registration identifier
+#define U2F_REGISTER_HASH_ID 0x00 // Version 2 hash identintifier
+
+typedef struct {
+ uint8_t chal[U2F_CHAL_SIZE]; // Challenge
+ uint8_t appId[U2F_APPID_SIZE]; // Application id
+} U2F_REGISTER_REQ;
+
+typedef struct {
+ uint8_t registerId; // Registration identifier (U2F_REGISTER_ID_V2)
+ U2F_EC_POINT pubKey; // Generated public key
+ uint8_t keyHandleLen; // Length of key handle
+ uint8_t keyHandleCertSig[
+ U2F_MAX_KH_SIZE + // Key handle
+ U2F_MAX_ATT_CERT_SIZE + // Attestation certificate
+ U2F_MAX_EC_SIG_SIZE]; // Registration signature
+} U2F_REGISTER_RESP;
+
+// U2F_CMD_AUTHENTICATE command defines
+
+// Authentication control byte
+
+#define U2F_AUTH_ENFORCE 0x03 // Enforce user presence and sign
+#define U2F_AUTH_CHECK_ONLY 0x07 // Check only
+#define U2F_AUTH_FLAG_TUP 0x01 // Test of user presence set
+
+typedef struct {
+ uint8_t chal[U2F_CHAL_SIZE]; // Challenge
+ uint8_t appId[U2F_APPID_SIZE]; // Application id
+ uint8_t keyHandleLen; // Length of key handle
+ uint8_t keyHandle[U2F_MAX_KH_SIZE]; // Key handle
+} U2F_AUTHENTICATE_REQ;
+
+typedef struct {
+ uint8_t flags; // U2F_AUTH_FLAG_ values
+ uint8_t ctr[U2F_CTR_SIZE]; // Counter field (big-endian)
+ uint8_t sig[U2F_MAX_EC_SIG_SIZE]; // Signature
+} U2F_AUTHENTICATE_RESP;
+
+// Command status responses
+
+#define U2F_SW_NO_ERROR 0x9000 // SW_NO_ERROR
+#define U2F_SW_WRONG_DATA 0x6A80 // SW_WRONG_DATA
+#define U2F_SW_CONDITIONS_NOT_SATISFIED 0x6985 // SW_CONDITIONS_NOT_SATISFIED
+#define U2F_SW_COMMAND_NOT_ALLOWED 0x6986 // SW_COMMAND_NOT_ALLOWED
+#define U2F_SW_INS_NOT_SUPPORTED 0x6D00 // SW_INS_NOT_SUPPORTED
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __U2F_H_INCLUDED__
diff --git a/include/u2f_impl.h b/include/u2f_impl.h
new file mode 100644
index 0000000000..6a14f5a151
--- /dev/null
+++ b/include/u2f_impl.h
@@ -0,0 +1,111 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* U2F implementation-specific callbacks and parameters. */
+
+#ifndef __CROS_EC_U2F_IMPL_H
+#define __CROS_EC_U2F_IMPL_H
+
+#include "common.h"
+#include "cryptoc/p256.h"
+
+/* APDU fields to pass around */
+struct apdu {
+ uint8_t p1;
+ uint8_t p2;
+ uint16_t len;
+ const uint8_t *data;
+};
+
+/*
+ * Parses an APDU-framed message according to the u2f protocol.
+ *
+ * @return 0 on failure, output buffer's byte count on success.
+ */
+unsigned u2f_apdu_rcv(uint8_t *buffer, unsigned in_len, unsigned max_len);
+
+/* ---- Physical presence ---- */
+
+enum touch_state {
+ POP_TOUCH_NO = 0, /* waiting for a user touch */
+ POP_TOUCH_YES = 1, /* touch recorded and latched */
+};
+
+/*
+ * Check whether the user presence event was latched.
+ *
+ * @param consume reset the latched touch event and the presence LED.
+ * @return POP_TOUCH_NO or POP_TOUCH_YES.
+ */
+enum touch_state pop_check_presence(int consume);
+
+/* ---- platform cryptography hooks ---- */
+
+/**
+ * Generate an origin-specific ECDSA keypair.
+ *
+ * Calculates a diversified chip-unique 256b value.
+ *
+ * @param seed ptr to store 32-byte seed to regenerate this key on this chip
+ * @param d pointer to ECDSA private key
+ * @param pk_x pointer to public key point
+ * @param pk_y pointer to public key point
+ *
+ * @return EC_SUCCESS if a valid keypair was created.
+ */
+int u2f_origin_keypair(uint8_t *seed, p256_int *d,
+ p256_int *pk_x, p256_int *pk_y);
+
+/**
+ * Reconstitute the origin ECDSA private key from its seed.
+ *
+ * @param seed value returned by origin_keypair.
+ * @param d ptr to store the retrieved private key.
+ * @return EC_SUCCESS if we retrieved the key.
+ */
+int u2f_origin_key(const uint8_t *seed, p256_int *d);
+
+/***
+ * Generate a hardware derived 256b private key.
+ *
+ * @param kek ptr to store the generated key.
+ * @param key_len size of the storage buffer. Should be 32 bytes.
+ * @return EC_SUCCESS if a valid key was created.
+ */
+int u2f_gen_kek(const uint8_t *origin, uint8_t *kek, size_t key_len);
+
+/**
+ * Generate a hardware derived ECDSA keypair for individual attestation.
+ *
+ * @param seed ptr to store 32-byte seed to regenerate this key on this chip
+ * @param d pointer to ECDSA private key
+ * @param pk_x pointer to public key point
+ * @param pk_y pointer to public key point
+ *
+ * @return EC_SUCCESS if a valid keypair was created.
+ */
+int g2f_individual_keypair(p256_int *d, p256_int *pk_x, p256_int *pk_y);
+
+/* ---- protocol extensions ---- */
+
+/* Use non-standard extensions to the U2F protocol */
+int use_g2f(void);
+
+/* Non-standardized command status responses */
+#define U2F_SW_CLA_NOT_SUPPORTED 0x6E00
+#define U2F_SW_WRONG_LENGTH 0x6700
+#define U2F_SW_WTF 0x6f00
+/* Additional flags for the P1 fields */
+#define G2F_ATTEST 0x80 /* fixed attestation cert */
+#define G2F_CONSUME 0x02 /* consume presence */
+
+/* Vendor command to enable/disable the extensions */
+#define U2F_VENDOR_MODE U2F_VENDOR_LAST
+
+/* call extensions for unsupported U2F INS */
+unsigned u2f_custom_dispatch(uint8_t ins, struct apdu apdu, uint8_t *buf,
+ unsigned *ret_len) __attribute__((weak));
+
+#endif /* __CROS_EC_U2F_IMPL_H */
diff --git a/include/uart.h b/include/uart.h
index c372e3c390..5de1af92d6 100644
--- a/include/uart.h
+++ b/include/uart.h
@@ -246,4 +246,47 @@ int uart_comx_putc_ok(void);
*/
void uart_comx_putc(int c);
+/*
+ * Functions for pad switching UART, only defined on some chips (npcx), and
+ * if CONFIG_UART_PAD_SWITCH is enabled.
+ */
+enum uart_pad {
+ UART_DEFAULT_PAD = 0,
+ UART_ALTERNATE_PAD = 1,
+};
+
+/**
+ * Reset UART pad to default pad, so that a panic information can be printed
+ * on the EC console.
+ */
+void uart_reset_default_pad_panic(void);
+
+/**
+ * Specialized function to write then read data on UART alternate pad.
+ * The transfer may be interrupted at any time if data is received on the main
+ * pad.
+ *
+ * @param tx Data to be sent
+ * @param tx_len Length of data to be sent
+ * @param rx Buffer to receive data
+ * @param rx_len Receive buffer length
+ * @param timeout_us Timeout in microseconds for the transaction to complete.
+ *
+ * @return The number of bytes read back (indicates a timeout if != rx_len).
+ * - -EC_ERROR_BUSY if the alternate pad cannot be used (e.g. default
+ * pad is currently being used), or if the transfer was interrupted.
+ * - -EC_ERROR_TIMEOUT in case tx_len bytes cannot be written in the
+ * time specified in timeout_us.
+ */
+int uart_alt_pad_write_read(uint8_t *tx, int tx_len, uint8_t *rx, int rx_len,
+ int timeout_us);
+
+/**
+ * Interrupt handler for default UART RX pin transition when UART is switched
+ * to alternate pad.
+ *
+ * @param signal Signal which triggered the interrupt.
+ */
+void uart_default_pad_rx_interrupt(enum gpio_signal signal);
+
#endif /* __CROS_EC_UART_H */
diff --git a/include/update_fw.h b/include/update_fw.h
index f1b76e1033..f31549e252 100644
--- a/include/update_fw.h
+++ b/include/update_fw.h
@@ -8,29 +8,246 @@
#include <stddef.h>
+
+/*
+ * This file contains structures used to facilitate EC firmware updates
+ * over USB (and over TPM for cr50).
+ *
+ * The firmware update protocol consists of two phases: connection
+ * establishment and actual image transfer.
+ *
+ * Image transfer is done in 1K blocks. The host supplying the image
+ * encapsulates blocks in PDUs by prepending a header including the flash
+ * offset where the block is destined and its digest.
+ *
+ * The EC device responds to each PDU with a confirmation which is 1 byte
+ * response. Zero value means success, non zero value is the error code
+ * reported by EC.
+ *
+ * To establish the connection, the host sends a different PDU, which
+ * contains no data and is destined to offset 0. Receiving such a PDU
+ * signals the EC that the host intends to transfer a new image.
+ *
+ * The connection establishment response is described by the
+ * first_response_pdu structure below.
+ */
+
+#define UPDATE_PROTOCOL_VERSION 6
+
+/*
+ * This is the format of the update PDU header.
+ *
+ * block digest: the first four bytes of the sha1 digest of the rest of the
+ * structure (can be 0 on boards where digest is ignored).
+ * block_base: offset of this PDU into the flash SPI.
+ */
+struct update_command {
+ uint32_t block_digest;
+ uint32_t block_base;
+ /* The actual payload goes here. */
+} __packed;
+
+/*
+ * This is the frame format the host uses when sending update PDUs over USB.
+ *
+ * The PDUs are up to 1K bytes in size, they are fragmented into USB chunks of
+ * 64 bytes each and reassembled on the receive side before being passed to
+ * the flash update function.
+ *
+ * The flash update function receives the unframed PDU body (starting at the
+ * cmd field below), and puts its reply into the same buffer the PDU was in.
+ */
+struct update_frame_header {
+ uint32_t block_size; /* Total frame size, including this field. */
+ struct update_command cmd;
+};
+
+/*
+ * A convenience structure which allows to group together various revision
+ * fields of the header created by the signer (cr50-specific).
+ *
+ * These fields are compared when deciding if versions of two images are the
+ * same or when deciding which one of the available images to run.
+ */
+struct signed_header_version {
+ uint32_t minor;
+ uint32_t major;
+ uint32_t epoch;
+};
+
+/*
+ * Response to the connection establishment request.
+ *
+ * When responding to the very first packet of the update sequence, the
+ * original USB update implementation was responding with a four byte value,
+ * just as to any other block of the transfer sequence.
+ *
+ * It became clear that there is a need to be able to enhance the update
+ * protocol, while staying backwards compatible.
+ *
+ * All newer protocol versions (starting with version 2) respond to the very
+ * first packet with an 8 byte or larger response, where the first 4 bytes are
+ * a version specific data, and the second 4 bytes - the protocol version
+ * number.
+ *
+ * This way the host receiving of a four byte value in response to the first
+ * packet is considered an indication of the target running the 'legacy'
+ * protocol, version 1. Receiving of an 8 byte or longer response would
+ * communicates the protocol version in the second 4 bytes.
+ */
+struct first_response_pdu {
+ uint32_t return_value;
+
+ /* The below fields are present in versions 2 and up. */
+
+ /* Type of header following (one of first_response_pdu_header_type) */
+ uint16_t header_type;
+
+ /* Must be UPDATE_PROTOCOL_VERSION */
+ uint16_t protocol_version;
+
+ /* In version 6 and up, a board-specific header follows. */
+ union {
+ /* cr50 (header_type = UPDATE_HEADER_TYPE_CR50) */
+ struct {
+ /* The below fields are present in versions 3 and up. */
+ uint32_t backup_ro_offset;
+ uint32_t backup_rw_offset;
+
+ /* The below fields are present in versions 4 and up. */
+ /*
+ * Versions of the currently active RO and RW sections.
+ */
+ struct signed_header_version shv[2];
+
+ /* The below fields are present in versions 5 and up */
+ /* keyids of the currently active RO and RW sections. */
+ uint32_t keyid[2];
+ } cr50;
+ /* Common code (header_type = UPDATE_HEADER_TYPE_COMMON) */
+ struct {
+ /* Maximum PDU size */
+ uint32_t maximum_pdu_size;
+
+ /* Flash protection status */
+ uint32_t flash_protection;
+
+ /* Offset of the other region */
+ uint32_t offset;
+
+ /* Version string of the other region */
+ char version[32];
+
+ /* Minimum rollback version that RO will accept */
+ int32_t min_rollback;
+
+ /* RO public key version */
+ uint32_t key_version;
+ } common;
+ };
+};
+
+enum first_response_pdu_header_type {
+ UPDATE_HEADER_TYPE_CR50 = 0, /* Must be 0 for backwards compatibility */
+ UPDATE_HEADER_TYPE_COMMON = 1,
+};
+
/* TODO: Handle this in update_fw.c, not usb_update.c */
#define UPDATE_DONE 0xB007AB1E
+#define UPDATE_EXTRA_CMD 0xB007AB1F
+
+enum update_extra_command {
+ UPDATE_EXTRA_CMD_IMMEDIATE_RESET = 0,
+ UPDATE_EXTRA_CMD_JUMP_TO_RW = 1,
+ UPDATE_EXTRA_CMD_STAY_IN_RO = 2,
+ UPDATE_EXTRA_CMD_UNLOCK_RW = 3,
+ UPDATE_EXTRA_CMD_UNLOCK_ROLLBACK = 4,
+ UPDATE_EXTRA_CMD_INJECT_ENTROPY = 5,
+ UPDATE_EXTRA_CMD_PAIR_CHALLENGE = 6,
+ UPDATE_EXTRA_CMD_TOUCHPAD_INFO = 7,
+};
/*
- * This array defines possible sections available for the firmare update.
- * The section which does not map the current execting code is picked as the
- * valid update area. The values are offsets into the flash space.
- *
- * This should be defined in board.c, with each entry containing:
- * {CONFIG_RW_MEM_OFF, CONFIG_RW_MEM_OFF + CONFIG_RW_SIZE}
- * for its relevant section.
+ * Pair challenge (from host), note that the packet, with header, must fit
+ * in a single USB packet (64 bytes), so its maximum length is 50 bytes.
*/
-struct section_descriptor {
- uint32_t sect_base_offset;
- uint32_t sect_top_offset;
+struct pair_challenge {
+ uint8_t host_public[32]; /* X22519 public key from host */
+ uint8_t nonce[16]; /* nonce to be used for HMAC */
};
-extern const struct section_descriptor * const rw_sections;
-extern const int num_rw_sections;
+/*
+ * Pair challenge response (from device).
+ */
+struct pair_challenge_response {
+ uint8_t status; /* = EC_RES_SUCCESS */
+ uint8_t device_public[32]; /* X22519 device public key of device */
+ /*
+ * Truncated output of
+ * HMAC_SHA256(x25519(device_private, host_public), nonce)
+ */
+ uint8_t authenticator[16];
+} __packed;
+
+struct touchpad_info {
+ uint8_t status; /* = EC_RES_SUCCESS */
+ uint8_t reserved; /* padding */
+ uint16_t vendor; /* Vendor USB id */
+
+ /*
+ * Virtual address to write to to update TP FW over USB update protocol,
+ * and FW size. Both are 0 if unsupported.
+ */
+ uint32_t fw_address;
+ uint32_t fw_size;
+
+ /*
+ * SHA256 hash of the trackpad FW accepted by this EC image.
+ * This is used by the updater to make sure we do not attempt to flash
+ * a touchpad FW that does not match the one shipped by the EC.
+ */
+ uint8_t allowed_fw_hash[32];
+ /* Vendor specific data. */
+ struct {
+ uint16_t id;
+ uint16_t fw_version;
+ uint16_t fw_checksum;
+ } elan;
+} __packed;
void fw_update_command_handler(void *body,
- size_t cmd_size,
- size_t *response_size);
+ size_t cmd_size,
+ size_t *response_size);
+
+/* Used to tell fw update the update ran successfully and is finished */
+void fw_update_complete(void);
+
+/* Verify integrity of the PDU received. */
+int update_pdu_valid(struct update_command *cmd_body, size_t cmd_size);
+
+/* Various update command return values. */
+enum {
+ UPDATE_SUCCESS = 0,
+ UPDATE_BAD_ADDR = 1,
+ UPDATE_ERASE_FAILURE = 2,
+ UPDATE_DATA_ERROR = 3,
+ UPDATE_WRITE_FAILURE = 4,
+ UPDATE_VERIFY_ERROR = 5,
+ UPDATE_GEN_ERROR = 6,
+ UPDATE_MALLOC_ERROR = 7,
+ UPDATE_ROLLBACK_ERROR = 8,
+ UPDATE_RATE_LIMIT_ERROR = 9,
+ UPDATE_RWSIG_BUSY = 10,
+};
+
+/* Obtain touchpad information */
+int touchpad_get_info(struct touchpad_info *tp);
+
+/* Touchpad FW update: Write a FW block. */
+int touchpad_update_write(int offset, int size, const uint8_t *data);
+
+/* SHA256 hash of the touchpad firmware expected by this image. */
+extern const uint8_t touchpad_fw_full_hash[32];
#endif /* ! __CROS_EC_UPDATE_FW_H */
diff --git a/include/usb_api.h b/include/usb_api.h
index c2e365cf28..8778dcc54e 100644
--- a/include/usb_api.h
+++ b/include/usb_api.h
@@ -42,6 +42,39 @@ void usb_disconnect(void);
*/
void usb_release(void);
+/*
+ * Returns true if USB device is currently suspended.
+ * Requires CONFIG_USB_SUSPEND to be defined.
+ */
+int usb_is_suspended(void);
+
+/*
+ * Preserve in non-volatile memory the state of the USB hardware registers
+ * which cannot be simply re-initialized when powered up again.
+ */
+void usb_save_suspended_state(void);
+
+/*
+ * Restore from non-volatile memory the state of the USB hardware registers
+ * which was lost by powering them down.
+ */
+void usb_restore_suspended_state(void);
+
+/*
+ * Tell the host to wake up. Does nothing if CONFIG_USB_REMOTE_WAKEUP is not
+ * defined.
+ *
+ * Returns immediately, suspend status can be checked using usb_is_suspended.
+ */
+#ifdef CONFIG_USB_REMOTE_WAKEUP
+void usb_wake(void);
+#else
+static inline void usb_wake(void) {}
+#endif
+
+/* Board-specific USB wake, for side-band wake, called by usb_wake above. */
+void board_usb_wake(void);
+
#ifdef CONFIG_USB_SELECT_PHY
/* Select which PHY to use. */
void usb_select_phy(uint32_t phy);
diff --git a/include/usb_charge.h b/include/usb_charge.h
index 34893eabf7..1de3372434 100644
--- a/include/usb_charge.h
+++ b/include/usb_charge.h
@@ -39,14 +39,6 @@ enum usb_charge_mode {
*/
int usb_charge_set_mode(int usb_port_id, enum usb_charge_mode mode);
-/**
- * Return a bitmask of which USB ports are enabled.
- *
- * If bit (1 << i) is set, port <i> is enabled. If it is clear, port <i> is
- * in USB_CHARGE_MODE_DISABLED.
- */
-int usb_charge_ports_enabled(void);
-
#ifdef HAS_TASK_USB_CHG_P0
#define USB_CHG_EVENT_BC12 TASK_EVENT_CUSTOM(1)
#define USB_CHG_EVENT_VBUS TASK_EVENT_CUSTOM(2)
@@ -96,4 +88,22 @@ void usb_charger_set_switches(int port, enum usb_switch setting);
*/
void usb_charger_vbus_change(int port, int vbus_level);
+/**
+ * Check if ramping is allowed for given supplier
+ *
+ * @supplier Supplier to check
+ *
+ * @return Ramping is allowed for given supplier
+ */
+int usb_charger_ramp_allowed(int supplier);
+
+/**
+ * Get the maximum current limit that we are allowed to ramp to
+ *
+ * @supplier Active supplier type
+ * @sup_curr Input current limit based on supplier
+ *
+ * @return Maximum current in mA
+ */
+int usb_charger_ramp_max(int supplier, int sup_curr);
#endif /* __CROS_EC_USB_CHARGE_H */
diff --git a/include/usb_descriptor.h b/include/usb_descriptor.h
index 1920db7938..a0e9d843cc 100644
--- a/include/usb_descriptor.h
+++ b/include/usb_descriptor.h
@@ -10,9 +10,6 @@
#include <stddef.h> /* for wchar_t */
-#include "usb_api.h"
-#include "usb_hw.h"
-
#define USB_MAX_PACKET_SIZE 64
/* USB 2.0 chapter 9 definitions */
@@ -89,6 +86,24 @@ struct usb_contid_caps_descriptor {
#define USB_DC_DTYPE_BILLBOARD 0x0d
/* RESERVED 0x00, 0xOe - 0xff */
+/* Platform descriptor */
+struct usb_platform_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType; /* USB_DT_DEVICE_CAPABILITY */
+ uint8_t bDevCapabilityType; /* USB_DC_DTYPE_PLATFORM */
+ uint8_t bReserved; /* SBZ */
+ uint8_t PlatformCapUUID[16]; /* USB_PLAT_CAP_xxx */
+ uint16_t bcdVersion; /* 0x0100 */
+ uint8_t bVendorCode;
+ uint8_t iLandingPage;
+} __packed;
+#define USB_DT_PLATFORM_SIZE 24
+
+/* Platform Capability UUIDs */
+#define USB_PLAT_CAP_WEBUSB /*{3408b638-09a9-47a0-8bfd-a0768815b665}*/ \
+ {0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47, \
+ 0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65}
+
/* Qualifier Descriptor */
struct usb_qualifier_descriptor {
uint8_t bLength;
@@ -190,6 +205,9 @@ struct usb_endpoint_descriptor {
/* We can use any protocol we want */
#define USB_PROTOCOL_GOOGLE_CR50_NON_HC_FW_UPDATE 0xff
+#define USB_SUBCLASS_GOOGLE_POWER 0x54
+#define USB_PROTOCOL_GOOGLE_POWER 0x01
+
/* Control requests */
/* bRequestType fields */
@@ -211,8 +229,13 @@ struct usb_endpoint_descriptor {
/* Standard requests for bRequest field in a SETUP packet. */
#define USB_REQ_GET_STATUS 0x00
+#define USB_REQ_GET_STATUS_SELF_POWERED (1 << 0)
+#define USB_REQ_GET_STATUS_REMOTE_WAKEUP (1 << 1)
#define USB_REQ_CLEAR_FEATURE 0x01
#define USB_REQ_SET_FEATURE 0x03
+#define USB_REQ_FEATURE_ENDPOINT_HALT 0x0000
+#define USB_REQ_FEATURE_DEVICE_REMOTE_WAKEUP 0x0001
+#define USB_REQ_FEATURE_TEST_MODE 0x0002
#define USB_REQ_SET_ADDRESS 0x05
#define USB_REQ_GET_DESCRIPTOR 0x06
#define USB_REQ_SET_DESCRIPTOR 0x07
@@ -222,6 +245,35 @@ struct usb_endpoint_descriptor {
#define USB_REQ_SET_INTERFACE 0x0B
#define USB_REQ_SYNCH_FRAME 0x0C
+/* WebUSB URL descriptors */
+#define WEBUSB_REQ_GET_URL 0x02
+#define USB_DT_WEBUSB_URL 0x03
+
+#define USB_URL_SCHEME_HTTP 0x00
+#define USB_URL_SCHEME_HTTPS 0x01
+#define USB_URL_SCHEME_NONE 0xff
+
+/*
+ * URL descriptor helper.
+ * (similar to string descriptor but UTF-8 instead of UTF-16)
+ */
+#define USB_URL_DESC(scheme, str) \
+ (const void *)&(const struct { \
+ uint8_t _len; \
+ uint8_t _type; \
+ uint8_t _scheme; \
+ char _data[sizeof(str)]; \
+ }) { \
+ /* Total size of the descriptor is : \
+ * size of the UTF-8 text plus the len/type fields \
+ * minus the string 0-termination \
+ */ \
+ sizeof(str) + 3 - 1, \
+ USB_DT_WEBUSB_URL, \
+ USB_URL_SCHEME_##scheme, \
+ str \
+ }
+
/* Setup Packet */
struct usb_setup_packet {
uint8_t bmRequestType;
@@ -253,11 +305,10 @@ struct usb_setup_packet {
#ifdef CONFIG_USB_SERIALNO
/* String Descriptor for USB, for editable strings. */
-#define USB_STRING_LEN 30
struct usb_string_desc {
uint8_t _len;
uint8_t _type;
- wchar_t _data[USB_STRING_LEN];
+ wchar_t _data[CONFIG_SERIALNO_LEN];
};
#define USB_WR_STRING_DESC(str) \
(&(struct usb_string_desc) { \
@@ -270,9 +321,12 @@ extern struct usb_string_desc *usb_serialno_desc;
#endif
/* Use these macros for declaring descriptors, to order them properly */
-#define USB_CONF_DESC(name) CONCAT2(usb_desc_, name) \
- __attribute__((section(".rodata.usb_desc_" STRINGIFY(name))))
+#define USB_CONF_DESC_VAR(name, varname) varname \
+ __keep __attribute__((section(".rodata.usb_desc_" STRINGIFY(name))))
+#define USB_CONF_DESC(name) USB_CONF_DESC_VAR(name, CONCAT2(usb_desc_, name))
#define USB_IFACE_DESC(num) USB_CONF_DESC(CONCAT3(iface, num, _0iface))
+#define USB_CUSTOM_DESC_VAR(i, name, varname) \
+ USB_CONF_DESC_VAR(CONCAT4(iface, i, _1, name), varname)
#define USB_CUSTOM_DESC(i, name) USB_CONF_DESC(CONCAT4(iface, i, _1, name))
#define USB_EP_DESC(i, num) USB_CONF_DESC(CONCAT4(iface, i, _2ep, num))
@@ -287,5 +341,6 @@ extern const uint8_t usb_string_desc[];
/* USB string descriptor with the firmware version */
extern const void * const usb_fw_version;
extern const struct bos_context bos_ctx;
+extern const void *webusb_url;
#endif /* __CROS_EC_USB_DESCRIPTOR_H */
diff --git a/include/usb_hid.h b/include/usb_hid.h
index d21449d659..d5b86196a7 100644
--- a/include/usb_hid.h
+++ b/include/usb_hid.h
@@ -39,7 +39,4 @@ struct usb_hid_descriptor {
struct usb_hid_class_descriptor desc[1];
} __packed;
-/* class implementation interfaces */
-void set_keyboard_report(uint64_t rpt);
-
#endif /* USB_H */
diff --git a/include/usb_hid_touchpad.h b/include/usb_hid_touchpad.h
new file mode 100644
index 0000000000..39b6277b96
--- /dev/null
+++ b/include/usb_hid_touchpad.h
@@ -0,0 +1,33 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * USB HID definitions.
+ */
+
+#ifndef __CROS_EC_USB_HID_TOUCHPAD_H
+#define __CROS_EC_USB_HID_TOUCHPAD_H
+
+#define USB_HID_TOUCHPAD_TIMESTAMP_UNIT 100 /* usec */
+
+struct usb_hid_touchpad_report {
+ uint8_t id; /* 0x01 */
+ struct {
+ unsigned tip:1;
+ unsigned inrange:1;
+ unsigned id:4;
+ unsigned pressure:10;
+ unsigned width:12;
+ unsigned height:12;
+ unsigned x:12;
+ unsigned y:12;
+ } __packed finger[5];
+ uint8_t count:7;
+ uint8_t button:1;
+ uint16_t timestamp;
+} __packed;
+
+/* class implementation interfaces */
+void set_touchpad_report(struct usb_hid_touchpad_report *report);
+
+#endif
diff --git a/include/usb_i2c.h b/include/usb_i2c.h
new file mode 100644
index 0000000000..c464e062a5
--- /dev/null
+++ b/include/usb_i2c.h
@@ -0,0 +1,177 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "consumer.h"
+#include "producer.h"
+#include "registers.h"
+#include "task.h"
+#include "usb_descriptor.h"
+#include "util.h"
+
+#ifndef __CROS_USB_I2C_H
+#define __CROS_USB_I2C_H
+
+/*
+ * Command:
+ * +----------+-----------+---------------+---------------+---------------+
+ * | port: 1B | addr: 1B | wr count : 1B | rd count : 1B | data : <= 60B |
+ * +----------+-----------+---------------+---------------+---------------+
+ *
+ * port address: 1 byte, i2c interface index
+ *
+ * slave address: 1 byte, i2c 7-bit bus address
+ *
+ * write count: 1 byte, zero based count of bytes to write. If write
+ * count exceed 60 bytes, following packets are expected
+ * to continue the payload without header.
+ *
+ * read count: 1 byte, zero based count of bytes to read
+ *
+ * data: payload of data to write.
+ *
+ * Response:
+ * +-------------+---+---+-----------------------+
+ * | status : 2B | 0 | 0 | read payload : <= 60B |
+ * +-------------+---+---+-----------------------+
+ *
+ * status: 2 byte status
+ * 0x0000: Success
+ * 0x0001: I2C timeout
+ * 0x0002: Busy, try again
+ * This can happen if someone else has acquired the shared memory
+ * buffer that the I2C driver uses as /dev/null
+ * 0x0003: Write count invalid (mismatch with merged payload)
+ * 0x0004: Read count invalid (> 60 bytes)
+ * 0x0005: The port specified is invalid.
+ * 0x0006: The I2C interface is disabled.
+ * 0x8000: Unknown error mask
+ * The bottom 15 bits will contain the bottom 15 bits from the EC
+ * error code.
+ *
+ * read payload: up to 60 bytes of data read from I2C, length will match
+ * requested read count
+ */
+
+enum usb_i2c_error {
+ USB_I2C_SUCCESS = 0x0000,
+ USB_I2C_TIMEOUT = 0x0001,
+ USB_I2C_BUSY = 0x0002,
+ USB_I2C_WRITE_COUNT_INVALID = 0x0003,
+ USB_I2C_READ_COUNT_INVALID = 0x0004,
+ USB_I2C_PORT_INVALID = 0x0005,
+ USB_I2C_DISABLED = 0x0006,
+ USB_I2C_UNKNOWN_ERROR = 0x8000,
+};
+
+
+#define USB_I2C_MAX_READ_COUNT 60
+#define USB_I2C_CONFIG_BUFFER_SIZE \
+ ((CONFIG_USB_I2C_MAX_WRITE_COUNT+4) > USB_MAX_PACKET_SIZE ? \
+ (CONFIG_USB_I2C_MAX_WRITE_COUNT+4) : USB_MAX_PACKET_SIZE)
+
+BUILD_ASSERT(USB_MAX_PACKET_SIZE == (2 + 1 + 1 + USB_I2C_MAX_READ_COUNT));
+
+/*
+ * Compile time Per-USB gpio configuration stored in flash. Instances of this
+ * structure are provided by the user of the USB i2c. This structure binds
+ * together all information required to operate a USB i2c.
+ */
+struct usb_i2c_config {
+ uint16_t *buffer;
+
+ /* Deferred function to call to handle SPI request. */
+ const struct deferred_data *deferred;
+
+ struct consumer const consumer;
+ struct queue const *tx_queue;
+};
+
+extern struct consumer_ops const usb_i2c_consumer_ops;
+
+/*
+ * Convenience macro for defining a USB I2C bridge driver.
+ *
+ * NAME is used to construct the names of the trampoline functions and the
+ * usb_i2c_config struct, the latter is just called NAME.
+ *
+ * INTERFACE is the index of the USB interface to associate with this
+ * I2C driver.
+ *
+ * INTERFACE_NAME is the index of the USB string descriptor (iInterface).
+ *
+ * ENDPOINT is the index of the USB bulk endpoint used for receiving and
+ * transmitting bytes.
+ */
+#define USB_I2C_CONFIG(NAME, \
+ INTERFACE, \
+ INTERFACE_NAME, \
+ ENDPOINT) \
+ static uint16_t \
+ CONCAT2(NAME, _buffer_) \
+ [USB_I2C_CONFIG_BUFFER_SIZE / 2]; \
+ static void CONCAT2(NAME, _deferred_)(void); \
+ DECLARE_DEFERRED(CONCAT2(NAME, _deferred_)); \
+ static struct queue const CONCAT2(NAME, _to_usb_); \
+ static struct queue const CONCAT3(usb_to_, NAME, _); \
+ USB_STREAM_CONFIG_FULL(CONCAT2(NAME, _usb_), \
+ INTERFACE, \
+ USB_CLASS_VENDOR_SPEC, \
+ USB_SUBCLASS_GOOGLE_I2C, \
+ USB_PROTOCOL_GOOGLE_I2C, \
+ INTERFACE_NAME, \
+ ENDPOINT, \
+ USB_MAX_PACKET_SIZE, \
+ USB_MAX_PACKET_SIZE, \
+ CONCAT3(usb_to_, NAME, _), \
+ CONCAT2(NAME, _to_usb_)) \
+ struct usb_i2c_config const NAME = { \
+ .buffer = CONCAT2(NAME, _buffer_), \
+ .deferred = &CONCAT2(NAME, _deferred__data), \
+ .consumer = { \
+ .queue = &CONCAT3(usb_to_, NAME, _), \
+ .ops = &usb_i2c_consumer_ops, \
+ }, \
+ .tx_queue = &CONCAT2(NAME, _to_usb_), \
+ }; \
+ static struct queue const CONCAT2(NAME, _to_usb_) = \
+ QUEUE_DIRECT(USB_MAX_PACKET_SIZE, uint8_t, \
+ null_producer, CONCAT2(NAME, _usb_).consumer); \
+ static struct queue const CONCAT3(usb_to_, NAME, _) = \
+ QUEUE_DIRECT(CONFIG_USB_I2C_MAX_WRITE_COUNT+4, uint8_t, \
+ CONCAT2(NAME, _usb_).producer, NAME.consumer); \
+ static void CONCAT2(NAME, _deferred_)(void) \
+ { usb_i2c_deferred(&NAME); }
+
+/*
+ * Handle I2C request in a deferred callback.
+ */
+void usb_i2c_deferred(struct usb_i2c_config const *config);
+
+/*
+ * These functions should be implemented by the board to provide any board
+ * specific operations required to enable or disable access to the I2C device,
+ * and to return the current board enable state.
+ */
+
+/**
+ * Enable the I2C device
+ *
+ * @return EC_SUCCESS or non-zero error code.
+ */
+int usb_i2c_board_enable(void);
+
+/**
+ * Disable the I2C device
+ */
+void usb_i2c_board_disable(void);
+
+/**
+ * Check if the I2C device is enabled
+ *
+ * @return 1 if enabled, 0 if disabled.
+ */
+int usb_i2c_board_is_enabled(void);
+
+#endif /* __CROS_USB_I2C_H */
diff --git a/include/usb_mux.h b/include/usb_mux.h
index c9bd76ba21..151c397995 100644
--- a/include/usb_mux.h
+++ b/include/usb_mux.h
@@ -71,7 +71,8 @@ struct usb_mux {
/**
* Board specific initialization for USB mux that is
- * called after mux->driver->init() function.
+ * called after mux->driver->init() function and every time the port
+ * leaves auto-toggle state.
*
* @param mux USB mux to tune
* @return EC_SUCCESS on success, non-zero error code on failure.
@@ -87,8 +88,9 @@ struct usb_mux {
};
/* Supported USB mux drivers */
+extern const struct usb_mux_driver it5205_usb_mux_driver;
extern const struct usb_mux_driver pi3usb30532_usb_mux_driver;
-extern const struct usb_mux_driver ps8740_usb_mux_driver;
+extern const struct usb_mux_driver ps874x_usb_mux_driver;
extern const struct usb_mux_driver tcpm_usb_mux_driver;
extern const struct usb_mux_driver virtual_usb_mux_driver;
diff --git a/include/usb_pd.h b/include/usb_pd.h
index ef2cb73512..ac3d16cbc5 100644
--- a/include/usb_pd.h
+++ b/include/usb_pd.h
@@ -38,10 +38,11 @@ enum pd_rx_errors {
};
/* Events for USB PD task */
-#define PD_EVENT_RX (1<<2) /* Incoming packet event */
-#define PD_EVENT_TX (1<<3) /* Outgoing packet event */
-#define PD_EVENT_CC (1<<4) /* CC line change event */
-#define PD_EVENT_TCPC_RESET (1<<5) /* TCPC has reset */
+#define PD_EVENT_RX (1<<2) /* Incoming packet event */
+#define PD_EVENT_TX (1<<3) /* Outgoing packet event */
+#define PD_EVENT_CC (1<<4) /* CC line change event */
+#define PD_EVENT_TCPC_RESET (1<<5) /* TCPC has reset */
+#define PD_EVENT_UPDATE_DUAL_ROLE (1<<6) /* DRP state has changed */
/* --- PD data message helpers --- */
#define PDO_MAX_OBJECTS 7
@@ -56,11 +57,14 @@ enum pd_rx_errors {
* if present shall be sent in Minimum Voltage order; lowest to highest.
* 4. The Variable Supply (non battery) Objects,
* if present, shall be sent in Minimum Voltage order; lowest to highest.
+ * 5. (PD3.0) The Augmented PDO is defined to allow extension beyond the 4 PDOs
+ * above by examining bits <29:28> to determine the additional PDO function.
*/
-#define PDO_TYPE_FIXED (0 << 30)
-#define PDO_TYPE_BATTERY (1 << 30)
-#define PDO_TYPE_VARIABLE (2 << 30)
-#define PDO_TYPE_MASK (3 << 30)
+#define PDO_TYPE_FIXED (0 << 30)
+#define PDO_TYPE_BATTERY (1 << 30)
+#define PDO_TYPE_VARIABLE (2 << 30)
+#define PDO_TYPE_AUGMENTED (3 << 30)
+#define PDO_TYPE_MASK (3 << 30)
#define PDO_FIXED_DUAL_ROLE (1 << 29) /* Dual role device */
#define PDO_FIXED_SUSPEND (1 << 28) /* USB Suspend supported */
@@ -133,15 +137,18 @@ enum pd_rx_errors {
#define SVID_DISCOVERY_MAX 16
/* Timers */
+#define PD_T_SINK_TX (18*MSEC) /* between 16ms and 20 */
+#define PD_T_CHUNK_SENDER_RSP (24*MSEC) /* between 24ms and 30ms */
+#define PD_T_CHUNK_SENDER_REQ (24*MSEC) /* between 24ms and 30ms */
#define PD_T_SEND_SOURCE_CAP (100*MSEC) /* between 100ms and 200ms */
-#define PD_T_SINK_WAIT_CAP (240*MSEC) /* between 210ms and 250ms */
+#define PD_T_SINK_WAIT_CAP (600*MSEC) /* between 310ms and 620ms */
#define PD_T_SINK_TRANSITION (35*MSEC) /* between 20ms and 35ms */
#define PD_T_SOURCE_ACTIVITY (45*MSEC) /* between 40ms and 50ms */
#define PD_T_SENDER_RESPONSE (30*MSEC) /* between 24ms and 30ms */
#define PD_T_PS_TRANSITION (500*MSEC) /* between 450ms and 550ms */
#define PD_T_PS_SOURCE_ON (480*MSEC) /* between 390ms and 480ms */
#define PD_T_PS_SOURCE_OFF (920*MSEC) /* between 750ms and 920ms */
-#define PD_T_PS_HARD_RESET (15*MSEC) /* between 10ms and 20ms */
+#define PD_T_PS_HARD_RESET (25*MSEC) /* between 25ms and 35ms */
#define PD_T_ERROR_RECOVERY (25*MSEC) /* 25ms */
#define PD_T_CC_DEBOUNCE (100*MSEC) /* between 100ms and 200ms */
/* DRP_SNK + DRP_SRC must be between 50ms and 100ms with 30%-70% duty cycle */
@@ -159,6 +166,7 @@ enum pd_rx_errors {
#define PD_T_VCONN_SOURCE_ON (100*MSEC) /* 100ms */
#define PD_T_TRY_SRC (125*MSEC) /* Max time for Try.SRC state */
#define PD_T_TRY_WAIT (600*MSEC) /* Max time for TryWait.SNK state */
+#define PD_T_SINK_REQUEST (100*MSEC) /* Wait 100ms before next request */
/* number of edges and time window to detect CC line is not idle */
#define PD_RX_TRANSITION_COUNT 3
@@ -262,23 +270,27 @@ struct pd_policy {
* VDM object is minimum of VDM header + 6 additional data objects.
*/
+#define VDO_MAX_SIZE 7
+
+#define VDM_VER10 0
+#define VDM_VER20 1
+
/*
* VDM header
* ----------
* <31:16> :: SVID
* <15> :: VDM type ( 1b == structured, 0b == unstructured )
- * <14:13> :: Structured VDM version (can only be 00 == 1.0 currently)
+ * <14:13> :: Structured VDM version (00b == Rev 2.0, 01b == Rev 3.0 )
* <12:11> :: reserved
* <10:8> :: object position (1-7 valid ... used for enter/exit mode only)
* <7:6> :: command type (SVDM only?)
* <5> :: reserved (SVDM), command type (UVDM)
* <4:0> :: command
*/
-#define VDO_MAX_SIZE 7
-#define VDO(vid, type, custom) \
- (((vid) << 16) | \
- ((type) << 15) | \
- ((custom) & 0x7FFF))
+#define VDO(vid, type, custom) \
+ (((vid) << 16) | \
+ ((type) << 15) | \
+ ((custom) & 0x7FFF))
#define VDO_SVDM_TYPE (1 << 15)
#define VDO_SVDM_VERS(x) (x << 13)
@@ -558,7 +570,7 @@ struct pd_policy {
/* Per DisplayPort Spec v1.3 Section 3.3 */
#define HPD_USTREAM_DEBOUNCE_LVL (2*MSEC)
#define HPD_USTREAM_DEBOUNCE_IRQ (250)
-#define HPD_DSTREAM_DEBOUNCE_IRQ (750) /* between 500-1000us */
+#define HPD_DSTREAM_DEBOUNCE_IRQ (500) /* between 500-1000us */
/*
* DisplayPort Configure VDO
@@ -634,7 +646,6 @@ enum pd_states {
#ifdef CONFIG_USB_PD_DUAL_ROLE
PD_STATE_SNK_DISCONNECTED,
PD_STATE_SNK_DISCONNECTED_DEBOUNCE,
- PD_STATE_SNK_ACCESSORY,
PD_STATE_SNK_HARD_RESET_RECOVER,
PD_STATE_SNK_DISCOVERY,
PD_STATE_SNK_REQUESTED,
@@ -650,7 +661,6 @@ enum pd_states {
PD_STATE_SRC_DISCONNECTED,
PD_STATE_SRC_DISCONNECTED_DEBOUNCE,
- PD_STATE_SRC_ACCESSORY,
PD_STATE_SRC_HARD_RESET_RECOVER,
PD_STATE_SRC_STARTUP,
PD_STATE_SRC_DISCOVERY,
@@ -683,6 +693,9 @@ enum pd_states {
PD_STATE_BIST_TX,
#endif
+#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
+ PD_STATE_DRP_AUTO_TOGGLE,
+#endif
/* Number of states. Not an actual state. */
PD_STATE_COUNT,
};
@@ -690,8 +703,9 @@ enum pd_states {
#define PD_FLAGS_PING_ENABLED (1 << 0) /* SRC_READY pings enabled */
#define PD_FLAGS_PARTNER_DR_POWER (1 << 1) /* port partner is dualrole power */
#define PD_FLAGS_PARTNER_DR_DATA (1 << 2) /* port partner is dualrole data */
-#define PD_FLAGS_DATA_SWAPPED (1 << 3) /* data swap complete */
+#define PD_FLAGS_CHECK_IDENTITY (1 << 3) /* discover identity in READY */
#define PD_FLAGS_SNK_CAP_RECVD (1 << 4) /* sink capabilities received */
+#define PD_FLAGS_TCPC_DRP_TOGGLE (1 << 5) /* TCPC-controlled DRP toggling */
#define PD_FLAGS_EXPLICIT_CONTRACT (1 << 6) /* explicit pwr contract in place */
#define PD_FLAGS_VBUS_NEVER_LOW (1 << 7) /* VBUS input has never been low */
#define PD_FLAGS_PREVIOUS_PD_CONN (1 << 8) /* previously PD connected */
@@ -702,11 +716,13 @@ enum pd_states {
#define PD_FLAGS_TRY_SRC (1 << 13)/* Try.SRC states are active */
#define PD_FLAGS_PARTNER_USB_COMM (1 << 14)/* port partner is USB comms */
#define PD_FLAGS_UPDATE_SRC_CAPS (1 << 15)/* send new source capabilities */
+#define PD_FLAGS_TS_DTS_PARTNER (1 << 16)/* partner has rp/rp or rd/rd */
/* Flags to clear on a disconnect */
#define PD_FLAGS_RESET_ON_DISCONNECT_MASK (PD_FLAGS_PARTNER_DR_POWER | \
PD_FLAGS_PARTNER_DR_DATA | \
- PD_FLAGS_DATA_SWAPPED | \
+ PD_FLAGS_CHECK_IDENTITY | \
PD_FLAGS_SNK_CAP_RECVD | \
+ PD_FLAGS_TCPC_DRP_TOGGLE | \
PD_FLAGS_EXPLICIT_CONTRACT | \
PD_FLAGS_PREVIOUS_PD_CONN | \
PD_FLAGS_CHECK_PR_ROLE | \
@@ -715,8 +731,8 @@ enum pd_states {
PD_FLAGS_VCONN_ON | \
PD_FLAGS_TRY_SRC | \
PD_FLAGS_PARTNER_USB_COMM | \
- PD_FLAGS_UPDATE_SRC_CAPS)
-
+ PD_FLAGS_UPDATE_SRC_CAPS | \
+ PD_FLAGS_TS_DTS_PARTNER)
enum pd_cc_states {
PD_CC_NONE,
@@ -733,9 +749,15 @@ enum pd_cc_states {
#ifdef CONFIG_USB_PD_DUAL_ROLE
enum pd_dual_role_states {
+ /* While disconnected, toggle between src and sink */
PD_DRP_TOGGLE_ON,
+ /* Stay in src until disconnect, then stay in sink forever */
PD_DRP_TOGGLE_OFF,
+ /* Stay in current power role, don't switch. No auto-toggle support */
+ PD_DRP_FREEZE,
+ /* Switch to sink */
PD_DRP_FORCE_SINK,
+ /* Switch to source */
PD_DRP_FORCE_SOURCE,
};
/**
@@ -758,6 +780,7 @@ void pd_set_dual_role(enum pd_dual_role_states state);
* @param port Port number from which to get role
*/
int pd_get_role(int port);
+
#endif
/* Control Message type */
@@ -777,6 +800,46 @@ enum pd_ctrl_msg_type {
PD_CTRL_WAIT = 12,
PD_CTRL_SOFT_RESET = 13,
/* 14-15 Reserved */
+
+ /* Used for REV 3.0 */
+ PD_CTRL_NOT_SUPPORTED = 16,
+ PD_CTRL_GET_SOURCE_CAP_EXT = 17,
+ PD_CTRL_GET_STATUS = 18,
+ PD_CTRL_FR_SWAP = 19,
+ PD_CTRL_GET_PPS_STATUS = 20,
+ PD_CTRL_GET_COUNTRY_CODES = 21,
+ /* 22-31 Reserved */
+};
+
+/* Battery Status Data Object fields for REV 3.0 */
+#define BSDO_CAP_UNKNOWN 0xffff
+#define BSDO_CAP(n) (((n) & 0xffff) << 16)
+#define BSDO_INVALID (1 << 8)
+#define BSDO_PRESENT (1 << 9)
+#define BSDO_DISCHARGING (1 << 10)
+#define BSDO_IDLE (1 << 11)
+
+/* Get Battery Cap Message fields for REV 3.0 */
+#define BATT_CAP_REF(n) (((n) >> 16) & 0xff)
+
+/* Extended message type for REV 3.0 */
+enum pd_ext_msg_type {
+ /* 0 Reserved */
+ PD_EXT_SOURCE_CAP = 1,
+ PD_EXT_STATUS = 2,
+ PD_EXT_GET_BATTERY_CAP = 3,
+ PD_EXT_GET_BATTERY_STATUS = 4,
+ PD_EXT_BATTERY_CAP = 5,
+ PD_EXT_GET_MANUFACTURER_INFO = 6,
+ PD_EXT_MANUFACTURER_INFO = 7,
+ PD_EXT_SECURITY_REQUEST = 8,
+ PD_EXT_SECURITY_RESPONSE = 9,
+ PD_EXT_FIRMWARE_UPDATE_REQUEST = 10,
+ PD_EXT_FIRMWARE_UPDATE_RESPONSE = 11,
+ PD_EXT_PPS_STATUS = 12,
+ PD_EXT_COUNTRY_INFO = 13,
+ PD_EXT_COUNTRY_CODES = 14,
+ /* 15-31 Reserved */
};
/* Data message type */
@@ -786,13 +849,18 @@ enum pd_data_msg_type {
PD_DATA_REQUEST = 2,
PD_DATA_BIST = 3,
PD_DATA_SINK_CAP = 4,
- /* 5-14 Reserved */
+ /* 5-14 Reserved for REV 2.0 */
+ PD_DATA_BATTERY_STATUS = 5,
+ PD_DATA_ALERT = 6,
+ PD_DATA_GET_COUNTRY_INFO = 7,
+ /* 8-14 Reserved for REV 3.0 */
PD_DATA_VENDOR_DEF = 15,
};
/* Protocol revision */
#define PD_REV10 0
#define PD_REV20 1
+#define PD_REV30 2
/* Power role */
#define PD_ROLE_SINK 0
@@ -804,22 +872,56 @@ enum pd_data_msg_type {
#define PD_ROLE_VCONN_OFF 0
#define PD_ROLE_VCONN_ON 1
+/* chunk is a request or response in REV 3.0 */
+#define CHUNK_RESPONSE 0
+#define CHUNK_REQUEST 1
+
+/* collision avoidance Rp values in REV 3.0 */
+#define SINK_TX_OK TYPEC_RP_3A0
+#define SINK_TX_NG TYPEC_RP_1A5
+
/* Port role at startup */
+#ifndef PD_ROLE_DEFAULT
+#ifdef CONFIG_USB_PD_DUAL_ROLE
+#define PD_ROLE_DEFAULT(port) PD_ROLE_SINK
+#else
+#define PD_ROLE_DEFAULT(port) PD_ROLE_SOURCE
+#endif
+#endif
+
+/* Port default state at startup */
#ifdef CONFIG_USB_PD_DUAL_ROLE
-#define PD_ROLE_DEFAULT PD_ROLE_SINK
+#define PD_DEFAULT_STATE(port) ((PD_ROLE_DEFAULT(port) == PD_ROLE_SOURCE) ? \
+ PD_STATE_SRC_DISCONNECTED : \
+ PD_STATE_SNK_DISCONNECTED)
#else
-#define PD_ROLE_DEFAULT PD_ROLE_SOURCE
+#define PD_DEFAULT_STATE(port) PD_STATE_SRC_DISCONNECTED
#endif
+/* build extended message header */
+/* All extended messages are chunked, so set bit 15 */
+#define PD_EXT_HEADER(cnum, rchk, dsize) \
+ ((1 << 15) | ((cnum) << 11) | \
+ ((rchk) << 10) | (dsize))
+
/* build message header */
-#define PD_HEADER(type, prole, drole, id, cnt) \
- ((type) | (PD_REV20 << 6) | \
- ((drole) << 5) | ((prole) << 8) | \
- ((id) << 9) | ((cnt) << 12))
+#define PD_HEADER(type, prole, drole, id, cnt, rev, ext) \
+ ((type) | ((rev) << 6) | \
+ ((drole) << 5) | ((prole) << 8) | \
+ ((id) << 9) | ((cnt) << 12) | ((ext) << 15))
+/* Used for processing pd header */
+#define PD_HEADER_EXT(header) (((header) >> 15) & 1)
#define PD_HEADER_CNT(header) (((header) >> 12) & 7)
#define PD_HEADER_TYPE(header) ((header) & 0xF)
#define PD_HEADER_ID(header) (((header) >> 9) & 7)
+#define PD_HEADER_REV(header) (((header) >> 6) & 3)
+
+/* Used for processing pd extended header */
+#define PD_EXT_HEADER_CHUNKED(header) (((header) >> 15) & 1)
+#define PD_EXT_HEADER_CHUNK_NUM(header) (((header) >> 11) & 0xf)
+#define PD_EXT_HEADER_REQ_CHUNK(header) (((header) >> 10) & 1)
+#define PD_EXT_HEADER_DATA_SIZE(header) ((header) & 0x1ff)
/* K-codes for special symbols */
#define PD_SYNC1 0x18
@@ -856,19 +958,38 @@ enum pd_request_type {
PD_REQUEST_MAX,
};
+#ifdef CONFIG_USB_PD_REV30
+/**
+ * Get current PD Revision
+ *
+ * @param port USB-C port number
+ * @return 0 for PD_REV1.0, 1 for PD_REV2.0, 2 for PD_REV3.0
+ */
+int pd_get_rev(int port);
+
+/**
+ * Get current PD VDO Version
+ *
+ * @param port USB-C port number
+ * @return 0 for PD_REV1.0, 1 for PD_REV2.0
+ */
+int pd_get_vdo_ver(int port);
+#else
+#define pd_get_rev(n) PD_REV20
+#define pd_get_vdo_ver(n) VDM_VER10
+#endif
/**
* Decide which PDO to choose from the source capabilities.
*
- * @param cnt the number of Power Data Objects.
- * @param src_caps Power Data Objects representing the source capabilities.
+ * @param port USB-C port number
* @param rdo requested Request Data Object.
* @param ma selected current limit (stored on success)
* @param mv selected supply voltage (stored on success)
* @param req_type request type
* @return <0 if invalid, else EC_SUCCESS
*/
-int pd_build_request(int cnt, uint32_t *src_caps, uint32_t *rdo,
- uint32_t *ma, uint32_t *mv, enum pd_request_type req_type);
+int pd_build_request(int port, uint32_t *rdo, uint32_t *ma, uint32_t *mv,
+ enum pd_request_type req_type);
/**
* Check if max voltage request is allowed (only used if
@@ -888,6 +1009,35 @@ int pd_is_max_request_allowed(void);
void pd_process_source_cap(int port, int cnt, uint32_t *src_caps);
/**
+ * Find PDO index that offers the most amount of power and stays within
+ * max_mv voltage.
+ *
+ * @param port USB-C port number
+ * @param max_mv maximum voltage (or -1 if no limit)
+ * @param pdo raw pdo corresponding to index, or index 0 on error (output)
+ * @return index of PDO within source cap packet
+ */
+int pd_find_pdo_index(int port, int max_mv, uint32_t *pdo);
+
+/**
+ * Extract power information out of a Power Data Object (PDO)
+ *
+ * @param pdo raw pdo to extract
+ * @param ma current of the PDO (output)
+ * @param mv voltage of the PDO (output)
+ */
+void pd_extract_pdo_power(uint32_t pdo, uint32_t *ma, uint32_t *mv);
+
+/**
+ * Reduce the sink power consumption to a minimum value.
+ *
+ * @param port USB-C port number
+ * @param ma reduce current to minimum value.
+ * @param mv reduce voltage to minimum value.
+ */
+void pd_snk_give_back(int port, uint32_t * const ma, uint32_t * const mv);
+
+/**
* Put a cap on the max voltage requested as a sink.
* @param mv maximum voltage in millivolts.
*/
@@ -911,16 +1061,19 @@ int pd_is_valid_input_voltage(int mv);
* Request a new operating voltage.
*
* @param rdo Request Data Object with the selected operating point.
+ * @param port The port which the request came in on.
* @return EC_SUCCESS if we can get the requested voltage/OP, <0 else.
*/
-int pd_check_requested_voltage(uint32_t rdo);
+int pd_check_requested_voltage(uint32_t rdo, const int port);
/**
* Run board specific checks on request message
*
+ * @param rdo the request data object word sent by the sink.
+ * @param pdo_cnt the total number of source PDOs.
* @return EC_SUCCESS if request is ok , <0 else.
*/
-int pd_board_check_request(uint32_t rdo);
+int pd_board_check_request(uint32_t rdo, int pdo_cnt);
/**
* Select a new output voltage.
@@ -988,6 +1141,11 @@ void pd_set_input_current_limit(int port, uint32_t max_ma,
*/
void pd_update_contract(int port);
+/* Encode DTS status of port partner in current limit parameter */
+typedef uint32_t typec_current_t;
+#define TYPEC_CURRENT_DTS_MASK (1 << 31)
+#define TYPEC_CURRENT_ILIM_MASK (~TYPEC_CURRENT_DTS_MASK)
+
/**
* Set the type-C input current limit.
*
@@ -995,10 +1153,18 @@ void pd_update_contract(int port);
* @param max_ma Maximum current limit
* @param supply_voltage Voltage at which current limit is applied
*/
-void typec_set_input_current_limit(int port, uint32_t max_ma,
+void typec_set_input_current_limit(int port, typec_current_t max_ma,
uint32_t supply_voltage);
/**
+ * Set the type-C current limit when sourcing current..
+ *
+ * @param port USB-C port number
+ * @param rp One of enum tcpc_rp_value (eg TYPEC_RP_3A0) defining the limit.
+ */
+void typec_set_source_current_limit(int port, int rp);
+
+/**
* Verify board specific health status : current, voltages...
*
* @return EC_SUCCESS if the board is good, <0 else.
@@ -1229,7 +1395,11 @@ extern const int pd_snk_pdo_cnt;
*
* @param mask host event mask.
*/
+#if defined(HAS_TASK_HOSTCMD) && !defined(TEST_BUILD)
void pd_send_host_event(int mask);
+#else
+static inline void pd_send_host_event(int mask) { }
+#endif
/**
* Determine if in alternate mode or not.
@@ -1402,6 +1572,15 @@ int pd_rx_started(int port);
*/
void pd_set_suspend(int port, int enable);
+/**
+ * Check if the port has been initialized and PD task has not been
+ * suspended.
+ *
+ * @param port USB-C port number
+ * @return true if the PD task is not suspended.
+ */
+int pd_is_port_enabled(int port);
+
/* Callback when the hardware has detected an incoming packet */
void pd_rx_event(int port);
/* Start sampling the CC line for reception */
@@ -1454,6 +1633,13 @@ void pd_hw_init_rx(int port);
int pd_analyze_rx(int port, uint32_t *payload);
/**
+ * Check if PD communication is enabled
+ *
+ * @return true if it's enabled or false otherwise
+ */
+int pd_comm_is_enabled(int port);
+
+/**
* Get connected state
*
* @param port USB-C port number
@@ -1498,6 +1684,14 @@ int pd_get_partner_data_swap_capable(int port);
void pd_request_power_swap(int port);
/**
+ * Try to become the VCONN source, if we are not already the source and the
+ * other side is willing to accept a VCONN swap.
+ *
+ * @param port USB-C port number
+ */
+void pd_try_vconn_src(int port);
+
+/**
* Request data swap command to be issued
*
* @param port USB-C port number
@@ -1509,9 +1703,10 @@ void pd_request_data_swap(int port);
* the port can still detect connection and source power but will not
* send or respond to any PD communication.
*
+ * @param port USB-C port number
* @param enable Enable flag to set
*/
-void pd_comm_enable(int enable);
+void pd_comm_enable(int port, int enable);
/**
* Set the PD pings enabled flag. When source has negotiated power over
@@ -1536,6 +1731,21 @@ void pd_prepare_reset(void);
*/
void pd_set_new_power_request(int port);
+/**
+ * Return true if partner port is a DTS or TS capable of entering debug
+ * mode (eg. is presenting Rp/Rp or Rd/Rd).
+ *
+ * @param port USB-C port number
+ */
+int pd_ts_dts_plugged(int port);
+
+/**
+ * Return true if partner port is known to be PD capable.
+ *
+ * @param port USB-C port number
+ */
+int pd_capable(int port);
+
/* ----- Logging ----- */
#ifdef CONFIG_USB_PD_LOGGING
/**
diff --git a/include/usb_pd_tcpm.h b/include/usb_pd_tcpm.h
index 42069be458..d8ad743caa 100644
--- a/include/usb_pd_tcpm.h
+++ b/include/usb_pd_tcpm.h
@@ -67,6 +67,16 @@ struct tcpm_drv {
int (*init)(int port);
/**
+ * Release the TCPM hardware and disconnect the driver.
+ * Only .init() can be called after .release().
+ *
+ * @param port Type-C port number
+ *
+ * @return EC_SUCCESS or error
+ */
+ int (*release)(int port);
+
+ /**
* Read the CC line status.
*
* @param port Type-C port number
@@ -187,6 +197,18 @@ struct tcpm_drv {
*/
void (*tcpc_discharge_vbus)(int port, int enable);
+#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
+ /**
+ * Enable TCPC auto DRP toggling.
+ *
+ * @param port Type-C port number
+ * @param enable 1: Enable 0: Disable
+ *
+ * @return EC_SUCCESS or error
+ */
+ int (*drp_toggle)(int port, int enable);
+#endif
+
/**
* Get firmware version.
*
@@ -221,6 +243,14 @@ struct tcpc_config_t {
uint16_t tcpc_get_alert_status(void);
/**
+ * Optional, set the TCPC power mode.
+ *
+ * @param port Type-C port number
+ * @param mode 0: off/sleep, 1: on/awake
+ */
+void board_set_tcpc_power_mode(int port, int mode) __attribute__((weak));
+
+/**
* Initialize TCPC.
*
* @param port Type-C port number
@@ -243,4 +273,13 @@ void tcpc_alert_clear(int port);
*/
int tcpc_run(int port, int evt);
+/**
+ * Initialize board specific TCPC functions post TCPC initialization.
+ *
+ * @param port Type-C port number
+ *
+ * @return EC_SUCCESS or error
+ */
+int board_tcpc_post_init(int port) __attribute__((weak));
+
#endif /* __CROS_EC_USB_PD_TCPM_H */
diff --git a/include/util.h b/include/util.h
index e031148e45..bf3405728a 100644
--- a/include/util.h
+++ b/include/util.h
@@ -49,9 +49,6 @@
/* True of x is a power of two */
#define POWER_OF_TWO(x) (x && !(x & (x - 1)))
-/* find the most significant bit. Not defined in n == 0. */
-#define __fls(n) (31 - __builtin_clz(n))
-
/*
* macros for integer division with various rounding variants
* default integer division rounds down.
@@ -66,12 +63,17 @@ int isspace(int c);
int isalpha(int c);
int isprint(int c);
int memcmp(const void *s1, const void *s2, size_t len);
+int safe_memcmp(const void *s1, const void *s2, size_t len);
void *memcpy(void *dest, const void *src, size_t len);
__visible void *memset(void *dest, int c, size_t len);
void *memmove(void *dest, const void *src, size_t len);
+void *memchr(const void *buffer, int c, size_t n);
int strcasecmp(const char *s1, const char *s2);
int strncasecmp(const char *s1, const char *s2, size_t size);
-int strlen(const char *s);
+size_t strlen(const char *s);
+size_t strnlen(const char *s, size_t maxlen);
+char *strncpy(char *dest, const char *src, size_t n);
+int strncmp(const char *s1, const char *s2, size_t n);
/* Like strtol(), but for integers. */
int strtoi(const char *nptr, char **endptr, int base);
diff --git a/include/vb21_struct.h b/include/vb21_struct.h
new file mode 100644
index 0000000000..30adf84d04
--- /dev/null
+++ b/include/vb21_struct.h
@@ -0,0 +1,346 @@
+/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Vboot 2.1 data structures
+ *
+ * Offsets should be padded to 32-bit boundaries, since some architectures
+ * have trouble with accessing unaligned integers.
+ *
+ * Note: This file is copied from
+ * src/platform/vboot_reference/firmware/lib21/include/vb21_struct.h
+ * and should be updated if necessary.
+ */
+
+#ifndef VBOOT_REFERENCE_VB21_STRUCT_H_
+#define VBOOT_REFERENCE_VB21_STRUCT_H_
+#include <stdint.h>
+
+#include "2id.h"
+
+/*
+ * Magic numbers used by vb21_struct_common.magic.
+ *
+ * All valid numbers should be listed here to avoid accidental overlap.
+ * Numbers start at a large value, so that previous parsers (which stored
+ * things like lengths and offsets at that field) will detect and reject new
+ * structs as invalid.
+ */
+enum vb21_struct_common_magic {
+ /* "Vb2B" = vb21_keyblock.c.magic */
+ VB21_MAGIC_KEYBLOCK = 0x42326256,
+
+ /* "Vb2F" = vb21_fw_preamble.c.magic */
+ VB21_MAGIC_FW_PREAMBLE = 0x46326256,
+
+ /* "Vb2I" = vb21_packed_private_key.c.magic */
+ VB21_MAGIC_PACKED_PRIVATE_KEY = 0x49326256,
+
+ /* "Vb2K" = vb2_kernel_preamble.c.magic */
+ VB21_MAGIC_KERNEL_PREAMBLE = 0x4b326256,
+
+ /* "Vb2P" = vb21_packed_key.c.magic */
+ VB21_MAGIC_PACKED_KEY = 0x50326256,
+
+ /* "Vb2S" = vb21_signature.c.magic */
+ VB21_MAGIC_SIGNATURE = 0x53326256,
+};
+
+
+/*
+ * Generic struct header for all vboot2.1 structs. This makes it easy to
+ * automatically parse and identify vboot structs (e.g., in futility). This
+ * must be the first member of the parent vboot2.1 struct.
+ */
+struct vb21_struct_common {
+ /* Magic number; see vb21_struct_common_magic for expected values */
+ uint32_t magic;
+
+ /*
+ * Parent struct version; see each struct for the expected value.
+ *
+ * How to handle struct version mismatches, if the parser is version
+ * A.b and the data is version C.d:
+ * 1) If A.b == C.d, we're good.
+ * 2) If A != C, the data cannot be parsed at all.
+ * 3) If b < d, C.d is a newer version of data which is backwards-
+ * compatible to old parsers. We're good.
+ * 4) If b > d, C.d is an older version of data. The parser should
+ * use default values for fields added after version d. We're
+ * good.
+ *
+ * Struct versions start at 3.0, since the highest version of the old
+ * structures was 2.1. This way, there is no possibility of collision
+ * for old code which depends on the version number.
+ */
+ uint16_t struct_version_major;
+ uint16_t struct_version_minor;
+
+ /*
+ * Size of the parent structure and all its data, including the
+ * description and any necessary padding. That is, all data must lie
+ * in a contiguous region of <total_size> bytes starting at the first
+ * byte of this header.
+ */
+ uint32_t total_size;
+
+ /*
+ * Size of the fixed portion of the parent structure. If a description
+ * is present, it must start at this offset.
+ */
+ uint32_t fixed_size;
+
+ /*
+ * The object may contain an ASCII description following the fixed
+ * portion of the structure. If it is present, it must be
+ * null-terminated, and padded with 0 (null) bytes to a multiple of 32
+ * bits.
+ *
+ * Size of ASCII description in bytes, counting null terminator and
+ * padding (if any). Set 0 if no description is present. If non-zero,
+ * there must be a null terminator (0) at offset (fixed_size +
+ * desc_size - 1).
+ */
+ uint32_t desc_size;
+} __attribute__((packed));
+
+#define EXPECTED_VB21_STRUCT_COMMON_SIZE 20
+
+/* Current version of vb21_packed_key struct */
+#define VB21_PACKED_KEY_VERSION_MAJOR 3
+#define VB21_PACKED_KEY_VERSION_MINOR 0
+
+/*
+ * Packed public key data
+ *
+ * The key data must be arranged like this:
+ * 1) vb21_packed_key header struct h
+ * 2) Key description (pointed to by h.c.fixed_size)
+ * 3) Key data key (pointed to by h.key_offset)
+ */
+struct vb21_packed_key {
+ /* Common header fields */
+ struct vb21_struct_common c;
+
+ /* Offset of key data from start of this struct */
+ uint32_t key_offset;
+
+ /* Size of key data in bytes (NOT strength of key in bits) */
+ uint32_t key_size;
+
+ /* Signature algorithm used by the key (enum vb2_signature_algorithm) */
+ uint16_t sig_alg;
+
+ /*
+ * Hash digest algorithm used with the key (enum vb2_hash_algorithm).
+ * This is explicitly specified as part of the key to prevent use of a
+ * strong key with a weak hash.
+ */
+ uint16_t hash_alg;
+
+ /* Key version */
+ uint32_t key_version;
+
+ /* Key ID */
+ struct vb2_id id;
+} __attribute__((packed));
+
+#define EXPECTED_VB21_PACKED_KEY_SIZE \
+ (EXPECTED_VB21_STRUCT_COMMON_SIZE + 16 + EXPECTED_ID_SIZE)
+
+/* Current version of vb21_packed_private_key struct */
+#define VB21_PACKED_PRIVATE_KEY_VERSION_MAJOR 3
+#define VB21_PACKED_PRIVATE_KEY_VERSION_MINOR 0
+
+/*
+ * Packed private key data
+ *
+ * The key data must be arranged like this:
+ * 1) vb21_packed_private_key header struct h
+ * 2) Key description (pointed to by h.c.fixed_size)
+ * 3) Key data key (pointed to by h.key_offset)
+ */
+struct vb21_packed_private_key {
+ /* Common header fields */
+ struct vb21_struct_common c;
+
+ /* Offset of key data from start of this struct */
+ uint32_t key_offset;
+
+ /* Size of key data in bytes (NOT strength of key in bits) */
+ uint32_t key_size;
+
+ /* Signature algorithm used by the key (enum vb2_signature_algorithm) */
+ uint16_t sig_alg;
+
+ /*
+ * Hash digest algorithm used with the key (enum vb2_hash_algorithm).
+ * This is explicitly specified as part of the key to prevent use of a
+ * strong key with a weak hash.
+ */
+ uint16_t hash_alg;
+
+ /* Key ID */
+ struct vb2_id id;
+} __attribute__((packed));
+
+#define EXPECTED_VB21_PACKED_PRIVATE_KEY_SIZE \
+ (EXPECTED_VB21_STRUCT_COMMON_SIZE + 12 + EXPECTED_ID_SIZE)
+
+/* Current version of vb21_signature struct */
+#define VB21_SIGNATURE_VERSION_MAJOR 3
+#define VB21_SIGNATURE_VERSION_MINOR 0
+
+/*
+ * Signature data
+ *
+ * The signature data must be arranged like this:
+ * 1) vb21_signature header struct h
+ * 2) Signature description (pointed to by h.c.fixed_size)
+ * 3) Signature data (pointed to by h.sig_offset)
+ */
+struct vb21_signature {
+ /* Common header fields */
+ struct vb21_struct_common c;
+
+ /* Offset of signature data from start of this struct */
+ uint32_t sig_offset;
+
+ /* Size of signature data in bytes */
+ uint32_t sig_size;
+
+ /* Size of the data block which was signed in bytes */
+ uint32_t data_size;
+
+ /* Signature algorithm used (enum vb2_signature_algorithm) */
+ uint16_t sig_alg;
+
+ /* Hash digest algorithm used (enum vb2_hash_algorithm) */
+ uint16_t hash_alg;
+
+ /*
+ * ID for the signature.
+ *
+ * If this is a keyblock signature entry, this is the ID of the key
+ * used to generate this signature. This allows the firmware to
+ * quickly determine which signature block (if any) goes with the key
+ * being used by the firmware.
+ *
+ * If this is a preamble hash entry, this is the ID of the data type
+ * being hashed. There is no key ID, because sig_alg=VB2_ALG_NONE.
+ */
+ struct vb2_id id;
+} __attribute__((packed));
+
+#define EXPECTED_VB21_SIGNATURE_SIZE \
+ (EXPECTED_VB21_STRUCT_COMMON_SIZE + 16 + EXPECTED_ID_SIZE)
+
+
+/* Current version of vb21_keyblock struct */
+#define VB21_KEYBLOCK_VERSION_MAJOR 3
+#define VB21_KEYBLOCK_VERSION_MINOR 0
+
+/*
+ * Key block. This contains a signed, versioned key for use in the next stage
+ * of verified boot.
+ *
+ * The key block data must be arranged like this:
+ * 1) vb21_keyblock header struct h
+ * 2) Keyblock description (pointed to by h.c.fixed_size)
+ * 3) Data key (pointed to by h.data_key_offset)
+ * 4) Signatures (first signature pointed to by h.sig_offset)
+ *
+ * The signatures from 4) must cover all the data from 1), 2), 3). That is,
+ * signatures must sign all data up to sig_offset.
+ */
+struct vb21_keyblock {
+ /* Common header fields */
+ struct vb21_struct_common c;
+
+ /* Flags (VB2_KEY_BLOCK_FLAG_*) */
+ uint32_t flags;
+
+ /*
+ * Offset of key (struct vb21_packed_key) to use in next stage of
+ * verification, from start of the keyblock.
+ */
+ uint32_t key_offset;
+
+ /* Number of keyblock signatures which follow */
+ uint32_t sig_count;
+
+ /*
+ * Offset of the first signature (struct vb21_signature) from the start
+ * of the keyblock.
+ *
+ * Signatures sign the contents of this struct and the data pointed to
+ * by data_key_offset, but not themselves or other signatures.
+ *
+ * For the firmware, there may be only one signature.
+ *
+ * Kernels often have at least two signatures - one using the kernel
+ * subkey from the RW firmware (for signed kernels) and one which is
+ * simply a SHA-512 hash (for unsigned developer kernels).
+ *
+ * The ID for each signature indicates which key was used to generate
+ * the signature.
+ */
+ uint32_t sig_offset;
+} __attribute__((packed));
+
+#define EXPECTED_VB21_KEYBLOCK_SIZE (EXPECTED_VB21_STRUCT_COMMON_SIZE + 16)
+
+
+/* Current version of vb21_fw_preamble struct */
+#define VB21_FW_PREAMBLE_VERSION_MAJOR 3
+#define VB21_FW_PREAMBLE_VERSION_MINOR 0
+
+/* Flags for vb21_fw_preamble.flags */
+/* Reserved; do not use */
+#define VB21_FIRMWARE_PREAMBLE_RESERVED0 0x00000001
+/* Do not allow use of any hardware crypto accelerators. */
+#define VB21_FIRMWARE_PREAMBLE_DISALLOW_HWCRYPTO 0x00000002
+
+/*
+ * Firmware preamble
+ *
+ * The preamble data must be arranged like this:
+ * 1) vb21_fw_preamble header struct h
+ * 2) Preamble description (pointed to by h.c.fixed_size)
+ * 3) Hashes (pointed to by h.hash_offset)
+ * 4) Signature (pointed to by h.sig_offset)
+ *
+ * The signature 4) must cover all the data from 1), 2), 3).
+ */
+struct vb21_fw_preamble {
+ /* Common header fields */
+ struct vb21_struct_common c;
+
+ /* Flags; see VB21_FIRMWARE_PREAMBLE_* */
+ uint32_t flags;
+
+ /* Firmware version */
+ uint32_t fw_version;
+
+ /* Offset of signature (struct vb21_signature) for this preamble */
+ uint32_t sig_offset;
+
+ /*
+ * The preamble contains a list of hashes (struct vb21_signature) for
+ * the various firmware components. These have sig_alg=VB2_SIG_NONE,
+ * and the ID for each hash identifies the component being hashed.
+ * The calling firmware is responsible for knowing where to find those
+ * components, which may be on a different storage device than this
+ * preamble.
+ */
+
+ /* Number of hash entries */
+ uint32_t hash_count;
+
+ /* Offset of first hash entry from start of preamble */
+ uint32_t hash_offset;
+} __attribute__((packed));
+
+#define EXPECTED_VB21_FW_PREAMBLE_SIZE (EXPECTED_VB21_STRUCT_COMMON_SIZE + 20)
+
+#endif /* VBOOT_REFERENCE_VB21_STRUCT_H_ */
diff --git a/include/vboot.h b/include/vboot.h
new file mode 100644
index 0000000000..b9f03c6423
--- /dev/null
+++ b/include/vboot.h
@@ -0,0 +1,48 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "common.h"
+#include "vb21_struct.h"
+#include "rsa.h"
+
+/**
+ * Validate key contents.
+ *
+ * @param key
+ * @return EC_SUCCESS or EC_ERROR_*
+ */
+int vb21_is_packed_key_valid(const struct vb21_packed_key *key);
+
+/**
+ * Validate signature contents.
+ *
+ * @param sig Signature to be validated.
+ * @param key Key to be used for validating <sig>.
+ * @return EC_SUCCESS or EC_ERROR_*
+ */
+int vb21_is_signature_valid(const struct vb21_signature *sig,
+ const struct vb21_packed_key *key);
+
+/**
+ * Check data region is filled with ones
+ *
+ * @param data Data to be validated.
+ * @param start Offset where validation starts.
+ * @param end Offset where validation ends. data[end] won't be checked.
+ * @return EC_SUCCESS or EC_ERROR_*
+ */
+int vboot_is_padding_valid(const uint8_t *data, uint32_t start, uint32_t end);
+
+/**
+ * Verify data by RSA signature
+ *
+ * @param data Data to be verified.
+ * @param len Number of bytes in <data>.
+ * @param key Key to be used for verification.
+ * @param sig Signature of <data>
+ * @return EC_SUCCESS or EC_ERROR_*
+ */
+int vboot_verify(const uint8_t *data, int len,
+ const struct rsa_public_key *key, const uint8_t *sig);
diff --git a/include/version.h b/include/version.h
index 8853d76561..14aa61a0e2 100644
--- a/include/version.h
+++ b/include/version.h
@@ -10,18 +10,21 @@
#include "common.h"
-#define CROS_EC_VERSION_COOKIE1 0xce112233
-#define CROS_EC_VERSION_COOKIE2 0xce445566
+#define CROS_EC_IMAGE_DATA_COOKIE1 0xce778899
+#define CROS_EC_IMAGE_DATA_COOKIE2 0xceaabbdd
-struct version_struct {
+struct image_data {
uint32_t cookie1;
char version[32];
+ uint32_t size;
+ int32_t rollback_version;
uint32_t cookie2;
} __packed;
-extern const struct version_struct version_data;
+extern const struct image_data current_image_data;
extern const char build_info[];
-extern const char __version_struct_offset[];
+extern const char __image_data_offset[];
+extern const void *__image_size;
/**
* Get the number of commits field from version string.
diff --git a/include/virtual_battery.h b/include/virtual_battery.h
new file mode 100644
index 0000000000..c686a76172
--- /dev/null
+++ b/include/virtual_battery.h
@@ -0,0 +1,49 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __CROS_EC_VIRTUAL_BATTERY_H
+#define __CROS_EC_VIRTUAL_BATTERY_H
+
+#if defined(CONFIG_I2C_VIRTUAL_BATTERY) && defined(CONFIG_BATTERY_SMART)
+#define VIRTUAL_BATTERY_ADDR BATTERY_ADDR
+#endif
+
+/**
+ * Read/write value of battery parameter from charge state.
+ *
+ * @param batt_cmd_head The beginning of the smart battery command
+ * @param dest Destination buffer for data
+ * @param read_len Number of bytes to read to the buffer
+ * @param write_len Number of bytes to write
+ * @return EC_SUCCESS if successful, non-zero if error.
+ *
+ */
+int virtual_battery_operation(const uint8_t *batt_cmd_head,
+ uint8_t *dest,
+ int read_len,
+ int write_len);
+
+/**
+ * Parse a command for virtual battery function.
+ *
+ * @param resp Pointer to the data structure to store the i2c messages
+ * @param in_len Accumulative number of bytes read
+ * @param err_code Pointer to the return value of i2c_xfer() or
+ * virtual_battery_operation()
+ * @param xferflags Flags
+ * @param read_len Number of bytes to read
+ * @param write_len Number of bytes to write
+ * @param out Data to send
+ * @return EC_SUCCESS if successful, non-zero if error.
+ */
+int virtual_battery_handler(struct ec_response_i2c_passthru *resp,
+ int in_len, int *err_code, int xferflags,
+ int read_len, int write_len,
+ const uint8_t *out);
+
+/* Reset the state machine and static variables. */
+void reset_parse_state(void);
+
+#endif /* __CROS_EC_VIRTUAL_BATTERY_H */
diff --git a/include/watchdog.h b/include/watchdog.h
index 1853a3a87b..b06fadeee5 100644
--- a/include/watchdog.h
+++ b/include/watchdog.h
@@ -8,6 +8,10 @@
#ifndef __CROS_EC_WATCHDOG_H
#define __CROS_EC_WATCHDOG_H
+#include <stdint.h>
+
+#include "config.h"
+
/**
* Initialize the watchdog.
*
diff --git a/test/base32.c b/test/base32.c
new file mode 100644
index 0000000000..6fd574bb73
--- /dev/null
+++ b/test/base32.c
@@ -0,0 +1,210 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Test Base-32 encoding/decoding
+ */
+
+#include <stdio.h>
+#include "common.h"
+#include "base32.h"
+#include "test_util.h"
+#include "util.h"
+
+static int test_crc5(void)
+{
+ uint32_t seen;
+ int i, j, c;
+ int errors = 0;
+
+ /*
+ * For every current CRC value and symbol, new CRC value is unique.
+ * This guarantees a single-character typo will be detected.
+ */
+ for (i = 0; i < 32; i++) {
+ seen = 0;
+ for (j = 0; j < 32; j++)
+ seen |= 1 << crc5_sym(j, i);
+ TEST_ASSERT(seen == 0xffffffff);
+ }
+
+ /*
+ * Do the same in the opposite order, to make sure a subsequent
+ * character doesn't obscure a previous typo.
+ */
+ for (i = 0; i < 32; i++) {
+ seen = 0;
+ for (j = 0; j < 32; j++)
+ seen |= 1 << crc5_sym(i, j);
+ TEST_ASSERT(seen == 0xffffffff);
+ }
+
+ /* Transposing different symbols generates distinct CRCs */
+ for (c = 0; c < 32; c++) {
+ for (i = 0; i < 32; i++) {
+ for (j = i + 1; j < 32; j++) {
+ if (crc5_sym(j, crc5_sym(i, c)) ==
+ crc5_sym(i, crc5_sym(j, c)))
+ errors++;
+ }
+ }
+ }
+ TEST_ASSERT(errors == 0);
+
+ return EC_SUCCESS;
+}
+
+static int enctest(const void *src, int srcbits, int crc_every,
+ const char *enc)
+{
+ char dest[32];
+
+ if (base32_encode(dest, sizeof(dest), src, srcbits, crc_every))
+ return -1;
+ if (strlen(dest) != strlen(enc) || strncmp(dest, enc, strlen(dest))) {
+ fprintf(stderr, "expected encode: \"%s\"\n", enc);
+ fprintf(stderr, "got encode: \"%s\"\n", dest);
+ return -2;
+ }
+ return 0;
+}
+
+#define ENCTEST(a, b, c, d) TEST_ASSERT(enctest(a, b, c, d) == 0)
+
+static int test_encode(void)
+{
+ const uint8_t src1[5] = {0xff, 0x00, 0xff, 0x00, 0xff};
+ char enc[32];
+
+ /* Test for enough space; error produces null string */
+ *enc = 1;
+ TEST_ASSERT(base32_encode(enc, 3, src1, 15, 0) == EC_ERROR_INVAL);
+ TEST_ASSERT(*enc == 0);
+
+ /* Empty source */
+ ENCTEST("\x00", 0, 0, "");
+
+ /* Single symbol uses top 5 bits */
+ ENCTEST("\x07", 5, 0, "A");
+ ENCTEST("\xb8", 5, 0, "Z");
+ ENCTEST("\xc0", 5, 0, "2");
+ ENCTEST("\xf8", 5, 0, "9");
+
+ /* Multiples of 5 bits use top bits */
+ ENCTEST("\x08\x86", 10, 0, "BC");
+ ENCTEST("\x08\x86", 15, 0, "BCD");
+
+ /* Multiples of 8 bits pad with 0 bits */
+ ENCTEST("\xff", 8, 0, "96");
+ ENCTEST("\x08\x87", 16, 0, "BCDS");
+
+ /* Multiples of 40 bits use all the bits */
+ ENCTEST("\xff\x00\xff\x00\xff", 40, 0, "96AR8AH9");
+
+ /* CRC requires exact multiple of symbol count */
+ ENCTEST("\xff\x00\xff\x00\xff", 40, 4, "96ARU8AH9D");
+ ENCTEST("\xff\x00\xff\x00\xff", 40, 8, "96AR8AH9L");
+ TEST_ASSERT(
+ base32_encode(enc, 16, (uint8_t *)"\xff\x00\xff\x00\xff", 40, 6)
+ == EC_ERROR_INVAL);
+ /* But what matters is symbol count, not bit count */
+ ENCTEST("\xff\x00\xff\x00\xfe", 39, 4, "96ARU8AH8P");
+
+ return EC_SUCCESS;
+}
+
+static int cmpbytes(const uint8_t *expect, const uint8_t *got, int len,
+ const char *desc)
+{
+ int i;
+
+ if (!memcmp(expect, got, len))
+ return 0;
+
+ fprintf(stderr, "expected %s:", desc);
+ for (i = 0; i < len; i++)
+ fprintf(stderr, " %02x", expect[i]);
+ fprintf(stderr, "\ngot %s: ", desc);
+ for (i = 0; i < len; i++)
+ fprintf(stderr, " %02x", got[i]);
+ fprintf(stderr, "\n");
+
+ return -2;
+}
+
+static int dectest(const void *dec, int decbits, int crc_every, const char *enc)
+{
+ uint8_t dest[32];
+ int destbits = decbits > 0 ? decbits : sizeof(dest) * 8;
+ int wantbits = decbits > 0 ? decbits : 5 * strlen(enc);
+ int gotbits = base32_decode(dest, destbits, enc, crc_every);
+
+ TEST_ASSERT(gotbits == wantbits);
+ if (gotbits != wantbits)
+ return -1;
+ return cmpbytes(dec, dest, (decbits + 7) / 8, "decode");
+}
+
+#define DECTEST(a, b, c, d) TEST_ASSERT(dectest(a, b, c, d) == 0)
+
+static int test_decode(void)
+{
+ uint8_t dec[32];
+
+ /* Decode tests, dest-limited */
+ DECTEST("\xf8", 5, 0, "97");
+ DECTEST("\x08", 5, 0, "BCDS");
+ DECTEST("\x08\x80", 10, 0, "BCDS");
+ DECTEST("\x08\x86", 15, 0, "BCDS");
+ DECTEST("\xff", 8, 0, "96");
+ DECTEST("\x08\x87", 16, 0, "BCDS");
+ DECTEST("\xff\x00\xff\x00\xff", 40, 0, "96AR8AH9");
+ DECTEST("\xff\x00\xff\x00\xfe", 39, 4, "96ARU8AH8P");
+
+ /* Decode ignores whitespace and dashes */
+ DECTEST("\xff\x00\xff\x00\xff", 40, 0, " 96\tA-R\r8A H9\n");
+
+ /* Invalid symbol fails */
+ TEST_ASSERT(base32_decode(dec, 16, "AI", 0) == -1);
+
+ /* If dest buffer is big, use all the source bits */
+ DECTEST("", 0, 0, "");
+ DECTEST("\xf8", 0, 0, "9");
+ DECTEST("\x07\xc0", 0, 0, "A9");
+ DECTEST("\x00\x3e", 0, 0, "AA9");
+ DECTEST("\x00\x01\xf0", 0, 0, "AAA9");
+ DECTEST("\xff\x00\xff\x00\xff", 0, 0, "96AR8AH9");
+
+ /* Decode always overwrites destination */
+ memset(dec, 0xff, sizeof(dec));
+ DECTEST("\x00\x00\x00\x00\x00", 0, 0, "AAAAAAAA");
+ memset(dec, 0x00, sizeof(dec));
+ DECTEST("\xff\xff\xff\xff\xff", 0, 0, "99999999");
+
+ /* Good CRCs */
+ DECTEST("\xff\x00\xff\x00\xff", 40, 4, "96ARU8AH9D");
+ DECTEST("\xff\x00\xff\x00\xff", 40, 8, "96AR8AH9L");
+
+ /* CRC requires exact multiple of symbol count */
+ TEST_ASSERT(base32_decode(dec, 40, "96ARL8AH9", 4) == -1);
+ /* But what matters is symbol count, not bit count */
+ DECTEST("\xff\x00\xff\x00\xfe", 39, 4, "96ARU8AH8P");
+
+ /* Detect errors in data, CRC, and transposition */
+ TEST_ASSERT(base32_decode(dec, 40, "96AQL", 4) == -1);
+ TEST_ASSERT(base32_decode(dec, 40, "96ARM", 4) == -1);
+ TEST_ASSERT(base32_decode(dec, 40, "96RAL", 4) == -1);
+
+ return EC_SUCCESS;
+}
+
+void run_test(void)
+{
+ test_reset();
+
+ RUN_TEST(test_crc5);
+ RUN_TEST(test_encode);
+ RUN_TEST(test_decode);
+
+ test_print_result();
+}
diff --git a/test/base32.tasklist b/test/base32.tasklist
new file mode 100644
index 0000000000..e241aab4bb
--- /dev/null
+++ b/test/base32.tasklist
@@ -0,0 +1,17 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ *
+ * For each task, use the macro TASK_TEST(n, r, d, s) where :
+ * 'n' in the name of the task
+ * 'r' in the main routine of the task
+ * 'd' in an opaque parameter passed to the routine at startup
+ * 's' is the stack size in bytes; must be a multiple of 8
+ */
+#define CONFIG_TEST_TASK_LIST /* No test task */
diff --git a/test/build.mk b/test/build.mk
index a757ac2642..0ba589b193 100644
--- a/test/build.mk
+++ b/test/build.mk
@@ -6,22 +6,17 @@
# on-board test binaries build
#
-test-list-y=pingpong timer_calib timer_dos timer_jump mutex utils
+test-list-y=pingpong timer_calib timer_dos timer_jump mutex utils utils_str
#disable: powerdemo
test-list-$(BOARD_BDS)+=
-test-list-$(BOARD_PIT)+=kb_scan stress
+
+test-list-$(BOARD_HAMMER)+=entropy
# Samus has board-specific chipset code, and the tests don't
# compile with it. Disable them for now.
test-list-$(BOARD_SAMUS)=
-# Ryu has issues when building tests
-test-list-$(BOARD_RYU)=
-
-# llama has issues when building tests
-test-list-$(BOARD_LLAMA)=
-
# So does Cr50
test-list-$(BOARD_CR50)=
@@ -35,15 +30,58 @@ test-list-$(BOARD_SAMUS_PD)=
ifneq ($(TEST_LIST_HOST),)
test-list-host=$(TEST_LIST_HOST)
else
-test-list-host=mutex pingpong utils kb_scan kb_mkbp lid_sw power_button hooks
-test-list-host+=thermal flash queue kb_8042 extpwr_gpio console_edit system
-test-list-host+=sbs_charging host_command
-test-list-host+=bklight_lid bklight_passthru interrupt timer_dos button
-test-list-host+=math_util motion_lid sbs_charging_v2 battery_get_params_smart
-test-list-host+=lightbar inductive_charging usb_pd fan charge_manager
-test-list-host+=charge_manager_drp_charging charge_ramp
+test-list-host = base32
+test-list-host += battery_get_params_smart
+test-list-host += bklight_lid
+test-list-host += bklight_passthru
+test-list-host += button
+test-list-host += charge_manager
+test-list-host += charge_manager_drp_charging
+test-list-host += charge_ramp
+test-list-host += console_edit
+test-list-host += entropy
+test-list-host += extpwr_gpio
+test-list-host += fan
+test-list-host += flash
+test-list-host += hooks
+test-list-host += host_command
+test-list-host += inductive_charging
+test-list-host += interrupt
+test-list-host += kb_8042
+test-list-host += kb_mkbp
+test-list-host += kb_scan
+test-list-host += lid_sw
+test-list-host += lightbar
+test-list-host += math_util
+test-list-host += motion_lid
+test-list-host += mutex
+test-list-host += nvmem
+test-list-host += nvmem_vars
+test-list-host += pingpong
+test-list-host += pinweaver
+test-list-host += power_button
+test-list-host += queue
+test-list-host += rma_auth
+test-list-host += rsa
+test-list-host += rsa3
+test-list-host += rtc
+test-list-host += sbs_charging_v2
+test-list-host += sha256
+test-list-host += sha256_unrolled
+test-list-host += shmalloc
+test-list-host += system
+test-list-host += thermal
+test-list-host += timer_dos
+test-list-host += usb_pd
+test-list-host += usb_pd_giveback
+test-list-host += usb_pd_rev30
+test-list-host += utils
+test-list-host += utils_str
+test-list-host += vboot
+test-list-host += x25519
endif
+base32-y=base32.o
battery_get_params_smart-y=battery_get_params_smart.o
bklight_lid-y=bklight_lid.o
bklight_passthru-y=bklight_passthru.o
@@ -52,34 +90,48 @@ charge_manager-y=charge_manager.o
charge_manager_drp_charging-y=charge_manager.o
charge_ramp-y+=charge_ramp.o
console_edit-y=console_edit.o
+entropy-y=entropy.o
extpwr_gpio-y=extpwr_gpio.o
+fan-y=fan.o
flash-y=flash.o
hooks-y=hooks.o
host_command-y=host_command.o
inductive_charging-y=inductive_charging.o
-interrupt-y=interrupt.o
interrupt-scale=10
+interrupt-y=interrupt.o
kb_8042-y=kb_8042.o
kb_mkbp-y=kb_mkbp.o
kb_scan-y=kb_scan.o
lid_sw-y=lid_sw.o
+lightbar-y=lightbar.o
math_util-y=math_util.o
motion_lid-y=motion_lid.o
mutex-y=mutex.o
nvmem-y=nvmem.o
+nvmem_vars-y=nvmem_vars.o
pingpong-y=pingpong.o
+pinweaver-y=pinweaver.o
power_button-y=power_button.o
powerdemo-y=powerdemo.o
queue-y=queue.o
+rma_auth-y=rma_auth.o
+rsa-y=rsa.o
+rsa3-y=rsa.o
+rtc-y=rtc.o
sbs_charging-y=sbs_charging.o
sbs_charging_v2-y=sbs_charging_v2.o
+sha256-y=sha256.o
+sha256_unrolled-y=sha256.o
+shmalloc-y=shmalloc.o
stress-y=stress.o
system-y=system.o
thermal-y=thermal.o
timer_calib-y=timer_calib.o
timer_dos-y=timer_dos.o
usb_pd-y=usb_pd.o
+usb_pd_giveback-y=usb_pd.o
+usb_pd_rev30-y=usb_pd.o
utils-y=utils.o
-battery_get_params_smart-y=battery_get_params_smart.o
-lightbar-y=lightbar.o
-fan-y=fan.o
+utils_str-y=utils_str.o
+vboot-y=vboot.o
+x25519-y=x25519.o
diff --git a/test/button.c b/test/button.c
index 5f18effb0c..2e8511df98 100644
--- a/test/button.c
+++ b/test/button.c
@@ -140,7 +140,7 @@ static int test_button_press_both(void)
return EC_SUCCESS;
}
-static void button_init(void)
+static void button_test_init(void)
{
int i;
@@ -159,21 +159,23 @@ void run_test(void)
test_reset();
button_init();
+
+ button_test_init();
RUN_TEST(test_button_press);
- button_init();
+ button_test_init();
RUN_TEST(test_button_release);
- button_init();
+ button_test_init();
RUN_TEST(test_button_debounce_short_press);
- button_init();
+ button_test_init();
RUN_TEST(test_button_debounce_short_bounce);
- button_init();
+ button_test_init();
RUN_TEST(test_button_debounce_stability);
- button_init();
+ button_test_init();
RUN_TEST(test_button_press_both);
test_print_result();
diff --git a/test/charge_manager.c b/test/charge_manager.c
index bb65c2a3f0..90bdf37a66 100644
--- a/test/charge_manager.c
+++ b/test/charge_manager.c
@@ -26,6 +26,7 @@ const int supplier_priority[] = {
[CHARGE_SUPPLIER_TEST6] = 3,
[CHARGE_SUPPLIER_TEST7] = 5,
[CHARGE_SUPPLIER_TEST8] = 6,
+ [CHARGE_SUPPLIER_TEST9] = 6,
};
BUILD_ASSERT((int)CHARGE_SUPPLIER_COUNT == (int)CHARGE_SUPPLIER_TEST_COUNT);
BUILD_ASSERT(ARRAY_SIZE(supplier_priority) == CHARGE_SUPPLIER_COUNT);
@@ -37,7 +38,8 @@ static int new_power_request[CONFIG_USB_PD_PORT_COUNT];
static int power_role[CONFIG_USB_PD_PORT_COUNT];
/* Callback functions called by CM on state change */
-void board_set_charge_limit(int port, int supplier, int charge_ma, int max_ma)
+void board_set_charge_limit(int port, int supplier, int charge_ma,
+ int max_ma, int charge_mv)
{
active_charge_limit = charge_ma;
}
@@ -162,6 +164,48 @@ static int test_initialization(void)
return EC_SUCCESS;
}
+static int test_safe_mode(void)
+{
+ int port = 0;
+ struct charge_port_info charge;
+
+ /* Initialize table to no charge */
+ initialize_charge_table(0, 5000, 5000);
+
+ /*
+ * Set a 2A non-dedicated charger on port 0 and verify that
+ * it is selected, due to safe mode.
+ */
+ charge_manager_update_dualrole(port, CAP_DUALROLE);
+ charge.current = 2000;
+ charge.voltage = 5000;
+ charge_manager_update_charge(CHARGE_SUPPLIER_TEST2, port, &charge);
+ wait_for_charge_manager_refresh();
+ TEST_ASSERT(active_charge_port == port);
+ TEST_ASSERT(active_charge_limit == 2000);
+
+ /* Verify ceil is ignored, due to safe mode. */
+ charge_manager_set_ceil(port, 0, 500);
+ wait_for_charge_manager_refresh();
+ TEST_ASSERT(active_charge_limit == 2000);
+
+ /*
+ * Leave safe mode and verify normal port selection rules go
+ * into effect.
+ */
+ charge_manager_leave_safe_mode();
+ wait_for_charge_manager_refresh();
+#ifdef CONFIG_CHARGE_MANAGER_DRP_CHARGING
+ TEST_ASSERT(active_charge_port == port);
+ TEST_ASSERT(active_charge_limit == 500);
+#else
+ TEST_ASSERT(active_charge_port == CHARGE_PORT_NONE);
+#endif
+
+ /* For subsequent tests, safe mode is exited. */
+ return EC_SUCCESS;
+}
+
static int test_priority(void)
{
struct charge_port_info charge;
@@ -288,6 +332,14 @@ static int test_charge_ceil(void)
TEST_ASSERT(active_charge_port == 1);
TEST_ASSERT(active_charge_limit == 2500);
+ /* Verify forced ceil takes effect immediately */
+ charge_manager_force_ceil(1, 500);
+ TEST_ASSERT(active_charge_port == 1);
+ TEST_ASSERT(active_charge_limit == 500);
+ wait_for_charge_manager_refresh();
+ TEST_ASSERT(active_charge_port == 1);
+ TEST_ASSERT(active_charge_limit == 500);
+
return EC_SUCCESS;
}
@@ -739,6 +791,7 @@ void run_test(void)
test_reset();
RUN_TEST(test_initialization);
+ RUN_TEST(test_safe_mode);
RUN_TEST(test_priority);
RUN_TEST(test_charge_ceil);
RUN_TEST(test_new_power_request);
diff --git a/test/charge_ramp.c b/test/charge_ramp.c
index 4fc1c5ac63..f6f8bd542e 100644
--- a/test/charge_ramp.c
+++ b/test/charge_ramp.c
@@ -36,40 +36,53 @@ static int charge_limit_ma;
/* Mock functions */
-int board_is_ramp_allowed(int supplier)
+/* Override test_mockable implementations in charge_ramp module */
+int chg_ramp_allowed(int supplier)
{
/* Ramp for TEST4-TEST8 */
return supplier > CHARGE_SUPPLIER_TEST3;
}
-int board_is_consuming_full_charge(void)
+int chg_ramp_max(int supplier, int sup_curr)
+{
+ if (supplier == CHARGE_SUPPLIER_TEST7)
+ return 1600;
+ else if (supplier == CHARGE_SUPPLIER_TEST8)
+ return 2400;
+ else
+ return 3000;
+}
+
+/* These usb_charger functions are unused, but necessary to link */
+int usb_charger_ramp_allowed(int supplier)
+{
+ return 0;
+}
+
+int usb_charger_ramp_max(int supplier, int sup_curr)
+{
+ return 0;
+}
+
+int charge_is_consuming_full_input_current(void)
{
return charge_limit_ma <= system_load_current_ma;
}
-int board_is_vbus_too_low(enum chg_ramp_vbus_state ramp_state)
+int board_is_vbus_too_low(int port, enum chg_ramp_vbus_state ramp_state)
{
return MIN(system_load_current_ma, charge_limit_ma) >
vbus_low_current_ma;
}
-void board_set_charge_limit(int port, int supplier, int limit_ma, int max_ma)
+void board_set_charge_limit(int port, int supplier, int limit_ma,
+ int max_ma, int max_mv)
{
charge_limit_ma = limit_ma;
if (charge_limit_ma > overcurrent_current_ma)
task_set_event(TASK_ID_TEST_RUNNER, TASK_EVENT_OVERCURRENT, 0);
}
-int board_get_ramp_current_limit(int supplier, int sup_curr)
-{
- if (supplier == CHARGE_SUPPLIER_TEST7)
- return 1600;
- else if (supplier == CHARGE_SUPPLIER_TEST8)
- return 2400;
- else
- return 3000;
-}
-
/* Test utilities */
static void plug_charger_with_ts(int supplier_type, int port, int min_current,
@@ -79,7 +92,7 @@ static void plug_charger_with_ts(int supplier_type, int port, int min_current,
vbus_low_current_ma = vbus_low_current;
overcurrent_current_ma = overcurrent_current;
chg_ramp_charge_supplier_change(port, supplier_type, min_current,
- reg_time);
+ reg_time, 0);
}
static void plug_charger(int supplier_type, int port, int min_current,
@@ -93,7 +106,7 @@ static void plug_charger(int supplier_type, int port, int min_current,
static void unplug_charger(void)
{
chg_ramp_charge_supplier_change(CHARGE_PORT_NONE, CHARGE_SUPPLIER_NONE,
- 0, get_time());
+ 0, get_time(), 0);
}
static int unplug_charger_and_check(void)
@@ -120,7 +133,13 @@ static int test_no_ramp(void)
system_load_current_ma = 3000;
/* A powerful charger, but hey, you're not allowed to ramp! */
plug_charger(CHARGE_SUPPLIER_TEST1, 0, 500, 3000, 3000);
- usleep(CHARGE_DETECT_DELAY_TEST);
+ /*
+ * NOTE: Since this is currently the first test being run, give the
+ * charge ramp task enough time to actually transition states and set
+ * the charge limit. This just needs at least transition to the
+ * CHG_RAMP_OVERCURRENT_DETECT state.
+ */
+ usleep(CHARGE_DETECT_DELAY_TEST + 200*MSEC);
/* That's right. Start at 500 mA */
TEST_ASSERT(charge_limit_ma == 500);
TEST_ASSERT(wait_stable_no_overcurrent());
@@ -195,7 +214,7 @@ static int test_switch_outlet(void)
/*
* Now the user decides to move it to a nearby outlet...actually
- * he decides to move it 5 times!
+ * they decide to move it 5 times!
*/
for (i = 0; i < 5; ++i) {
usleep(SECOND * 20);
@@ -223,8 +242,8 @@ static int test_fast_switch(void)
plug_charger(CHARGE_SUPPLIER_TEST4, 0, 500, 3000, 3000);
/*
- * Here comes that naughty user again, and this time he's switching
- * outlet really quickly. Fortunately this time he only does it twice.
+ * Here comes that naughty user again, and this time they are switching
+ * outlet really quickly. Fortunately this time they only do it twice.
*/
for (i = 0; i < 2; ++i) {
usleep(SECOND * 20);
@@ -484,6 +503,11 @@ void run_test(void)
{
test_reset();
+ /*
+ * If the following test order changes, make sure to add enough time for
+ * the charge ramp task to make its first transition after plugging in a
+ * charger. See the comment in test_no_ramp().
+ */
RUN_TEST(test_no_ramp);
RUN_TEST(test_full_ramp);
RUN_TEST(test_vbus_dip);
diff --git a/test/entropy.c b/test/entropy.c
new file mode 100644
index 0000000000..7498d4edf9
--- /dev/null
+++ b/test/entropy.c
@@ -0,0 +1,92 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Tests entropy source.
+ */
+
+#include "console.h"
+#include "common.h"
+#include "rollback.h"
+#include "test_util.h"
+#include "timer.h"
+#include "util.h"
+#include "watchdog.h"
+
+static int buckets[256];
+
+static const int log2_mult = 2;
+
+/*
+ * log2 (multiplied by 2). For non-power of 2, this rounds to the closest
+ * half-integer, otherwise the value is exact.
+ */
+uint32_t log2(int32_t val)
+{
+ int val1 = 31 - __builtin_clz(val);
+ int val2 = 32 - __builtin_clz(val - 1);
+
+ return log2_mult * (val1 + val2)/2;
+}
+
+void run_test(void)
+{
+ const int loopcount = 512;
+
+ uint8_t buffer[32];
+ timestamp_t t0, t1;
+ int i, j;
+ uint32_t entropy;
+ const int totalcount = loopcount * sizeof(buffer);
+ const int log2totalcount = log2(totalcount);
+
+ memset(buckets, 0, sizeof(buckets));
+
+ for (i = 0; i < loopcount; i++) {
+ t0 = get_time();
+ if (!board_get_entropy(buffer, sizeof(buffer))) {
+ ccprintf("Cannot get entropy\n");
+ test_fail();
+ return;
+ }
+ t1 = get_time();
+ if (i == 0)
+ ccprintf("Got %d bytes in %ld us\n",
+ sizeof(buffer), t1.val - t0.val);
+
+ for (j = 0; j < sizeof(buffer); j++)
+ buckets[buffer[j]]++;
+
+ watchdog_reload();
+ }
+
+ ccprintf("Total count: %d\n", totalcount);
+ ccprintf("Buckets: ");
+ entropy = 0;
+ for (j = 0; j < 256; j++) {
+ /*
+ * Shannon entropy (base 2) is sum of -p[j] * log_2(p[j]).
+ * p[j] = buckets[j]/totalcount
+ * -p[j] * log_2(p[j])
+ * = -(buckets[j]/totalcount) * log_2(buckets[j]/totalcount)
+ * = buckets[j] * (log_2(totalcount) - log_2(buckets[j]))
+ * / totalcount
+ * Our log2() function is scaled by log2_mult, and we defer the
+ * division by totalcount until we get the total sum, so we need
+ * to divide by (log2_mult * totalcount) at the end.
+ */
+ entropy += buckets[j] * (log2totalcount - log2(buckets[j]));
+ ccprintf("%d;", buckets[j]);
+ cflush();
+ }
+ ccprintf("\n");
+
+ ccprintf("Entropy: %u/1000 bits\n",
+ entropy * 1000 / (log2_mult * totalcount));
+
+ /* We want at least 2 bits of entropy (out of a maximum of 8) */
+ if ((entropy / (log2_mult * totalcount)) >= 2)
+ test_pass();
+ else
+ test_fail();
+}
diff --git a/test/entropy.tasklist b/test/entropy.tasklist
new file mode 100644
index 0000000000..e76178ba0a
--- /dev/null
+++ b/test/entropy.tasklist
@@ -0,0 +1,17 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ *
+ * For each task, use the macro TASK_TEST(n, r, d, s) where :
+ * 'n' in the name of the task
+ * 'r' in the main routine of the task
+ * 'd' in an opaque parameter passed to the routine at startup
+ * 's' is the stack size in bytes; must be a multiple of 8
+ */
+#define CONFIG_TEST_TASK_LIST
diff --git a/test/flash.c b/test/flash.c
index ab0cf29aa7..3ee3384619 100644
--- a/test/flash.c
+++ b/test/flash.c
@@ -313,7 +313,7 @@ static int test_overwrite_other(void)
uint32_t offset, size;
/* Test that we can overwrite the other image */
- if (system_get_image_copy() == SYSTEM_IMAGE_RW) {
+ if (system_is_in_rw()) {
offset = CONFIG_RO_STORAGE_OFF;
size = CONFIG_RO_SIZE;
} else {
@@ -364,11 +364,14 @@ static int test_region_info(void)
VERIFY_REGION_INFO(EC_FLASH_REGION_RO,
CONFIG_EC_PROTECTED_STORAGE_OFF +
CONFIG_RO_STORAGE_OFF, CONFIG_RO_SIZE);
- VERIFY_REGION_INFO(EC_FLASH_REGION_RW,
+ VERIFY_REGION_INFO(EC_FLASH_REGION_ACTIVE,
CONFIG_EC_WRITABLE_STORAGE_OFF +
CONFIG_RW_STORAGE_OFF, CONFIG_RW_SIZE);
VERIFY_REGION_INFO(EC_FLASH_REGION_WP_RO,
CONFIG_WP_STORAGE_OFF, CONFIG_WP_STORAGE_SIZE);
+ VERIFY_REGION_INFO(EC_FLASH_REGION_UPDATE,
+ CONFIG_EC_WRITABLE_STORAGE_OFF +
+ CONFIG_RW_STORAGE_OFF, CONFIG_RW_SIZE);
return EC_SUCCESS;
}
diff --git a/test/kb_scan.c b/test/kb_scan.c
index 926b0a27df..b9dd661224 100644
--- a/test/kb_scan.c
+++ b/test/kb_scan.c
@@ -350,12 +350,12 @@ static int lid_test(void)
static int test_check_boot_esc(void)
{
- TEST_CHECK(keyboard_scan_get_boot_key() == BOOT_KEY_ESC);
+ TEST_CHECK(keyboard_scan_get_boot_keys() == BOOT_KEY_ESC);
}
static int test_check_boot_down(void)
{
- TEST_CHECK(keyboard_scan_get_boot_key() == BOOT_KEY_DOWN_ARROW);
+ TEST_CHECK(keyboard_scan_get_boot_keys() == BOOT_KEY_DOWN_ARROW);
}
void test_init(void)
diff --git a/test/motion_lid.c b/test/motion_lid.c
index f610250e9c..bed03379ff 100644
--- a/test/motion_lid.c
+++ b/test/motion_lid.c
@@ -10,6 +10,7 @@
#include "accelgyro.h"
#include "common.h"
+#include "gpio.h"
#include "hooks.h"
#include "host_command.h"
#include "motion_lid.h"
@@ -206,6 +207,7 @@ static int test_lid_angle(void)
lid->xyz[X] = 0;
lid->xyz[Y] = 0;
lid->xyz[Z] = -1000;
+ gpio_set_level(GPIO_LID_OPEN, 0);
/* Initial wake up, like init does */
task_wake(TASK_ID_MOTIONSENSE);
@@ -220,7 +222,10 @@ static int test_lid_angle(void)
lid->xyz[X] = 0;
lid->xyz[Y] = 1000;
lid->xyz[Z] = 0;
+ gpio_set_level(GPIO_LID_OPEN, 1);
+ msleep(100);
wait_for_valid_sample();
+
TEST_ASSERT(motion_lid_get_angle() == 90);
/* Set lid open to 225. */
@@ -237,12 +242,15 @@ static int test_lid_angle(void)
wait_for_valid_sample();
TEST_ASSERT(motion_lid_get_angle() == 350);
- /* Set lid open to 10, check rotation did not change. */
+ /*
+ * Set lid open to 10. Since the lid switch still indicates that it's
+ * open, we should be getting an unreliable reading.
+ */
lid->xyz[X] = 0;
lid->xyz[Y] = 173;
lid->xyz[Z] = -984;
wait_for_valid_sample();
- TEST_ASSERT(motion_lid_get_angle() == 350);
+ TEST_ASSERT(motion_lid_get_angle() == LID_ANGLE_UNRELIABLE);
/* Rotate back to 180 and then 10 */
lid->xyz[X] = 0;
@@ -251,11 +259,15 @@ static int test_lid_angle(void)
wait_for_valid_sample();
TEST_ASSERT(motion_lid_get_angle() == 180);
+ /*
+ * Again, since the lid isn't closed, the angle should be unreliable.
+ * See SMALL_LID_ANGLE_RANGE.
+ */
lid->xyz[X] = 0;
lid->xyz[Y] = 173;
lid->xyz[Z] = -984;
wait_for_valid_sample();
- TEST_ASSERT(motion_lid_get_angle() == 10);
+ TEST_ASSERT(motion_lid_get_angle() == LID_ANGLE_UNRELIABLE);
/*
* Align base with hinge and make sure it returns unreliable for angle.
@@ -280,6 +292,44 @@ static int test_lid_angle(void)
wait_for_valid_sample();
TEST_ASSERT(motion_lid_get_angle() == 180);
+ /*
+ * Close the lid and set the angle to 0.
+ */
+ base->xyz[X] = 0;
+ base->xyz[Y] = 0;
+ base->xyz[Z] = 1000;
+ lid->xyz[X] = 0;
+ lid->xyz[Y] = 0;
+ lid->xyz[Z] = -1000;
+ gpio_set_level(GPIO_LID_OPEN, 0);
+ msleep(100);
+ wait_for_valid_sample();
+ TEST_ASSERT(motion_lid_get_angle() == 0);
+
+ /*
+ * Make the angle large, but since the lid is closed, the angle should
+ * be regarded as unreliable.
+ */
+ lid->xyz[X] = 0;
+ lid->xyz[Y] = -173;
+ lid->xyz[Z] = -984;
+ wait_for_valid_sample();
+ TEST_ASSERT(motion_lid_get_angle() == LID_ANGLE_UNRELIABLE);
+
+ /*
+ * Open the lid to 350, and then close the lid and set the angle
+ * to 10. The reading of small angle shouldn't be corrected.
+ */
+ gpio_set_level(GPIO_LID_OPEN, 1);
+ msleep(100);
+ gpio_set_level(GPIO_LID_OPEN, 0);
+ msleep(100);
+ lid->xyz[X] = 0;
+ lid->xyz[Y] = 173;
+ lid->xyz[Z] = -984;
+ wait_for_valid_sample();
+ TEST_ASSERT(motion_lid_get_angle() == 10);
+
return EC_SUCCESS;
}
diff --git a/test/nvmem.c b/test/nvmem.c
index 9f007f0191..d09bf8752a 100644
--- a/test/nvmem.c
+++ b/test/nvmem.c
@@ -30,8 +30,22 @@ static uint8_t read_buffer[NVMEM_PARTITION_SIZE];
static int flash_write_fail;
static int lock_test_started;
-void nvmem_compute_sha(uint8_t *p_buf, int num_bytes, uint8_t *p_sha,
- int sha_bytes)
+int app_cipher(const void *salt_p, void *out_p, const void *in_p, size_t size)
+{
+
+ const uint8_t *in = in_p;
+ uint8_t *out = out_p;
+ const uint8_t *salt = salt_p;
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ out[i] = in[i] ^ salt[i % CIPHER_SALT_SIZE];
+
+ return 1;
+}
+
+void app_compute_hash(uint8_t *p_buf, size_t num_bytes,
+ uint8_t *p_hash, size_t hash_bytes)
{
uint32_t crc;
uint32_t *p_data;
@@ -46,8 +60,11 @@ void nvmem_compute_sha(uint8_t *p_buf, int num_bytes, uint8_t *p_sha,
crc32_hash32(*p_data++);
crc = crc32_result();
- p_data = (uint32_t *)p_sha;
- *p_data = crc;
+ for (n = 0; n < hash_bytes; n += sizeof(crc)) {
+ size_t copy_bytes = MIN(sizeof(crc), hash_bytes - n);
+
+ memcpy(p_hash + n, &crc, copy_bytes);
+ }
}
/* Used to allow/prevent Flash erase/write operations */
@@ -154,14 +171,72 @@ static int test_configured_nvmem(void)
* partitions are configured and valid.
*/
- /* Configure all NvMem partitions with starting version number 0 */
- nvmem_setup(0);
/* Call NvMem initialization */
return nvmem_init();
}
+/* Verify that nvmem_erase_user_data only erases the given user's data. */
+static int test_nvmem_erase_user_data(void)
+{
+ uint32_t write_value;
+ uint32_t read_value;
+ int i;
+
+ nvmem_init();
+
+ /* Make sure all partitions have data in them. */
+ for (i = 0; i < NVMEM_NUM_PARTITIONS; i++) {
+ write_value = i;
+ nvmem_write(0, sizeof(write_value), &write_value, NVMEM_USER_0);
+ write_value = 2;
+ nvmem_write(0, sizeof(write_value), &write_value, NVMEM_USER_1);
+ write_value = 3;
+ nvmem_write(0, sizeof(write_value), &write_value, NVMEM_USER_2);
+ nvmem_commit();
+ }
+
+ /* Check that the writes took place. */
+ read_value = ~write_value;
+ nvmem_read(0, sizeof(read_value), &read_value, NVMEM_USER_0);
+ TEST_ASSERT(read_value == i-1);
+ nvmem_read(0, sizeof(read_value), &read_value, NVMEM_USER_1);
+ TEST_ASSERT(read_value == 2);
+ nvmem_read(0, sizeof(read_value), &read_value, NVMEM_USER_2);
+ TEST_ASSERT(read_value == 3);
+
+ /*
+ * nvmem_erase_user_data() is supposed to erase the user's data across
+ * all partitions.
+ */
+ nvmem_erase_user_data(NVMEM_USER_0);
+
+ for (i = 0; i < NVMEM_NUM_PARTITIONS; i++) {
+ /* Make sure USER 0's data is (still) gone. */
+ nvmem_read(0, sizeof(read_value), &read_value, NVMEM_USER_0);
+ TEST_ASSERT(read_value == 0xffffffff);
+
+ /* Make sure the other users' data has been untouched. */
+ nvmem_read(0, sizeof(read_value), &read_value, NVMEM_USER_1);
+ TEST_ASSERT(read_value == 2);
+
+ /*
+ * The active partition changes when the contents of the cache
+ * changes. Therefore, in order to examine all the paritions,
+ * we'll keep modifying one of the user's data.
+ */
+ nvmem_read(0, sizeof(read_value), &read_value, NVMEM_USER_2);
+ TEST_ASSERT(read_value == (3+i));
+ write_value = 4 + i;
+ nvmem_write(0, sizeof(write_value), &write_value, NVMEM_USER_2);
+ nvmem_commit();
+ }
+
+ return EC_SUCCESS;
+}
+
static int test_corrupt_nvmem(void)
{
+ uint8_t invalid_value = 0x55;
int ret;
struct nvmem_tag *p_part;
uint8_t *p_data;
@@ -169,12 +244,11 @@ static int test_corrupt_nvmem(void)
/*
* The purpose of this test is to check nvmem_init() in the case when no
* vailid partition exists (not fully erased and no valid sha). In this
- * case, the initialization function will call setup() to create two new
- * valid partitions.
+ * case, the initialization create one new valid partition.
*/
/* Overwrite each partition will all 0s */
- memset(write_buffer, 0, NVMEM_PARTITION_SIZE);
+ memset(write_buffer, invalid_value, NVMEM_PARTITION_SIZE);
flash_physical_write(CONFIG_FLASH_NVMEM_OFFSET_A,
NVMEM_PARTITION_SIZE,
(const char *)write_buffer);
@@ -183,36 +257,44 @@ static int test_corrupt_nvmem(void)
(const char *)write_buffer);
/*
* The initialization function will look for a valid partition and if
- * none is found, then will call nvmem_setup() which will erase the
- * paritions and setup new tags.
+ * none is found, it will create one, and save it at partition index
+ * 1.
*/
ret = nvmem_init();
if (ret)
return ret;
- /* Fill buffer with 0xffs */
- memset(write_buffer, 0xff, NVMEM_PARTITION_SIZE);
+
/*
- * nvmem_setup() will write put version 1 into partition 1 since the
- * commit() function toggles the active partition. Check here that
- * partition 0 has a version number of 1 and that all of the user buffer
- * data has been erased.
+ * nvmem_init() called on uninitialized flash will create the first
+ * valid partition with generation set to 0 at flash partition 1.
+ *
+ * Check here that partition 1 has a generation number of 0.
*/
- p_part = (struct nvmem_tag *)CONFIG_FLASH_NVMEM_BASE_A;
- TEST_ASSERT(p_part->version == 1);
- p_data = (uint8_t *)p_part + sizeof(struct nvmem_tag);
- /* Verify that partition 0 is fully erased */
- TEST_ASSERT_ARRAY_EQ(write_buffer, p_data, NVMEM_PARTITION_SIZE -
- sizeof(struct nvmem_tag));
-
- /* Run the same test for partition 1 which should have version 0 */
p_part = (struct nvmem_tag *)CONFIG_FLASH_NVMEM_BASE_B;
- TEST_ASSERT(p_part->version == 0);
+ TEST_ASSERT(p_part->generation == 0);
p_data = (uint8_t *)p_part + sizeof(struct nvmem_tag);
- ccprintf("Partition Version = %d\n", p_part->version);
- /* Verify that partition 1 is fully erased */
- TEST_ASSERT_ARRAY_EQ(write_buffer, p_data, NVMEM_PARTITION_SIZE -
- sizeof(struct nvmem_tag));
- return ret;
+
+ /* Verify that partition 0 is still empty. */
+ memset(write_buffer, invalid_value, NVMEM_PARTITION_SIZE);
+ p_data = (void *)CONFIG_FLASH_NVMEM_BASE_A;
+ TEST_ASSERT_ARRAY_EQ(write_buffer, p_data, NVMEM_PARTITION_SIZE);
+
+ /* Now let's write a different value into user NVMEM_CR50 */
+ invalid_value ^= ~0;
+ TEST_ASSERT(nvmem_write(0, sizeof(invalid_value),
+ &invalid_value, NVMEM_USER_0) == EC_SUCCESS);
+ TEST_ASSERT(nvmem_commit() == EC_SUCCESS);
+
+ /* Verify that partition 1 generation did not change. */
+ TEST_ASSERT(p_part->generation == 0);
+
+ /*
+ * Now verify that partition 0 generation is set to 1;
+ */
+ p_part = (struct nvmem_tag *)CONFIG_FLASH_NVMEM_BASE_A;
+ TEST_ASSERT(p_part->generation == 1);
+
+ return EC_SUCCESS;
}
static int test_write_read_sequence(void)
@@ -284,38 +366,6 @@ static int test_write_fail(void)
return !ret;
}
-static int test_cache_not_available(void)
-{
- char **p_shared;
- int ret;
- uint32_t offset = 0;
- uint32_t num_bytes = 0x200;
-
- /*
- * The purpose of this test is to validate that NvMem writes behave as
- * expected when the shared memory buffer (used for cache ram) is and
- * isn't available.
- */
-
- /* Do write/read sequence that's expected to be successful */
- if (test_write_read(offset, num_bytes, NVMEM_USER_1))
- return EC_ERROR_UNKNOWN;
-
- /* Acquire shared memory */
- if (shared_mem_acquire(num_bytes, p_shared))
- return EC_ERROR_UNKNOWN;
-
- /* Attempt write/read sequence that should fail */
- ret = test_write_read(offset, num_bytes, NVMEM_USER_1);
- /* Release shared memory */
- shared_mem_release(*p_shared);
- if (!ret)
- return EC_ERROR_UNKNOWN;
-
- /* Write/read sequence should work now */
- return test_write_read(offset, num_bytes, NVMEM_USER_1);
-}
-
static int test_buffer_overflow(void)
{
int ret;
@@ -563,6 +613,82 @@ static int test_lock(void)
return EC_SUCCESS;
}
+static int test_nvmem_save(void)
+{
+ /*
+ * The purpose of this test is to verify that if the written value
+ * did not change the cache contents there is no actual write
+ * happening at the commit time.
+ */
+ int dummy_value;
+ int offset = 0x10;
+ uint8_t generation_a;
+ uint8_t generation_b;
+ uint8_t prev_generation;
+ uint8_t new_generation;
+ const struct nvmem_tag *part_a;
+ const struct nvmem_tag *part_b;
+ const struct nvmem_tag *new_gen_part;
+ const struct nvmem_tag *prev_gen_part;
+
+ part_a = (const struct nvmem_tag *)CONFIG_FLASH_NVMEM_BASE_A;
+ part_b = (const struct nvmem_tag *)CONFIG_FLASH_NVMEM_BASE_B;
+ /*
+ * Make sure nvmem is initialized and both partitions have been
+ * written.
+ */
+ nvmem_init();
+
+ /*
+ * Make sure something is changed at offset 0x10 into the second user
+ * space.
+ */
+ nvmem_read(offset, sizeof(dummy_value), &dummy_value, NVMEM_USER_1);
+ dummy_value ^= ~0;
+ nvmem_write(0x10, sizeof(dummy_value), &dummy_value, NVMEM_USER_1);
+ nvmem_commit();
+
+ /* Verify that the two generation values are different. */
+ generation_a = part_a->generation;
+ generation_b = part_b->generation;
+ TEST_ASSERT(generation_a != generation_b);
+
+ /*
+ * Figure out which one should change next, we are close to the
+ * beginnig of the test, no wrap is expected.
+ */
+ if (generation_a > generation_b) {
+ prev_generation = generation_a;
+ new_generation = generation_a + 1;
+ new_gen_part = part_b;
+ prev_gen_part = part_a;
+ } else {
+ prev_generation = generation_b;
+ new_generation = generation_b + 1;
+ new_gen_part = part_a;
+ prev_gen_part = part_b;
+ }
+
+ /* Write a new value, this should trigger generation switch. */
+ dummy_value += 1;
+ TEST_ASSERT(nvmem_write(0x10, sizeof(dummy_value),
+ &dummy_value, NVMEM_USER_1) == EC_SUCCESS);
+ TEST_ASSERT(nvmem_commit() == EC_SUCCESS);
+
+ TEST_ASSERT(prev_gen_part->generation == prev_generation);
+ TEST_ASSERT(new_gen_part->generation == new_generation);
+
+ /* Write the same value, this should NOT trigger generation switch. */
+ TEST_ASSERT(nvmem_write(0x10, sizeof(dummy_value),
+ &dummy_value, NVMEM_USER_1) == EC_SUCCESS);
+ TEST_ASSERT(nvmem_commit() == EC_SUCCESS);
+
+ TEST_ASSERT(prev_gen_part->generation == prev_generation);
+ TEST_ASSERT(new_gen_part->generation == new_generation);
+
+ return EC_SUCCESS;
+}
+
static void run_test_setup(void)
{
/* Allow Flash erase/writes */
@@ -573,24 +699,17 @@ static void run_test_setup(void)
void run_test(void)
{
run_test_setup();
- /* Test NvMem Initialization function */
RUN_TEST(test_corrupt_nvmem);
RUN_TEST(test_fully_erased_nvmem);
RUN_TEST(test_configured_nvmem);
- /* Test Read/Write/Commit functions */
RUN_TEST(test_write_read_sequence);
RUN_TEST(test_write_full_multi);
- /* Test flash erase/write fail case */
RUN_TEST(test_write_fail);
- /* Test shared_mem not available case */
- RUN_TEST(test_cache_not_available);
- /* Test buffer overflow logic */
RUN_TEST(test_buffer_overflow);
- /* Test NvMem Move function */
RUN_TEST(test_move);
- /* Test NvMem IsDifferent function */
RUN_TEST(test_is_different);
- /* Test Nvmem write lock */
RUN_TEST(test_lock);
+ RUN_TEST(test_nvmem_erase_user_data);
+ RUN_TEST(test_nvmem_save);
test_print_result();
}
diff --git a/test/nvmem_vars.c b/test/nvmem_vars.c
new file mode 100644
index 0000000000..99e059215e
--- /dev/null
+++ b/test/nvmem_vars.c
@@ -0,0 +1,538 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Test of the key=val variable implementation (set, get, delete, etc).
+ */
+
+#include "common.h"
+#include "compile_time_macros.h"
+#include "nvmem.h"
+#include "nvmem_vars.h"
+#include "printf.h"
+#include "shared_mem.h"
+#include "test_util.h"
+
+/* Declare the user regions (see test_config.h) */
+uint32_t nvmem_user_sizes[] = {
+ CONFIG_FLASH_NVMEM_VARS_USER_SIZE,
+};
+BUILD_ASSERT(ARRAY_SIZE(nvmem_user_sizes) == NVMEM_NUM_USERS);
+
+/****************************************************************************/
+/* Mock the flash storage */
+
+static uint8_t ram_buffer[CONFIG_FLASH_NVMEM_VARS_USER_SIZE];
+static uint8_t flash_buffer[CONFIG_FLASH_NVMEM_VARS_USER_SIZE];
+
+extern char *rbuf;
+
+/* Internal functions exported for test */
+void release_local_copy(void)
+{
+ rbuf = NULL;
+}
+
+int get_local_copy(void)
+{
+ if (!rbuf) {
+ memcpy(ram_buffer, flash_buffer, sizeof(ram_buffer));
+ rbuf = (char *)ram_buffer;
+ }
+ return EC_SUCCESS;
+}
+
+int nvmem_read(uint32_t startOffset, uint32_t size,
+ void *data_, enum nvmem_users user)
+{
+ /* Our mocks make some assumptions */
+ if (startOffset != 0 ||
+ size > CONFIG_FLASH_NVMEM_VARS_USER_SIZE ||
+ user != CONFIG_FLASH_NVMEM_VARS_USER_NUM)
+ return EC_ERROR_UNIMPLEMENTED;
+
+ if (!data_)
+ return EC_ERROR_INVAL;
+
+ memcpy(data_, flash_buffer, size);
+
+ return EC_SUCCESS;
+}
+
+int nvmem_write(uint32_t startOffset, uint32_t size,
+ void *data_, enum nvmem_users user)
+{
+ /* Our mocks make some assumptions */
+ if (startOffset != 0 ||
+ size > CONFIG_FLASH_NVMEM_VARS_USER_SIZE ||
+ user != CONFIG_FLASH_NVMEM_VARS_USER_NUM)
+ return EC_ERROR_UNIMPLEMENTED;
+
+ if (!data_)
+ return EC_ERROR_INVAL;
+
+ memcpy(ram_buffer, data_, size);
+
+ return EC_SUCCESS;
+}
+
+int nvmem_commit(void)
+{
+ memcpy(flash_buffer, ram_buffer, CONFIG_FLASH_NVMEM_VARS_USER_SIZE);
+ return EC_SUCCESS;
+}
+
+int nvmem_erase_user_data(enum nvmem_users user)
+{
+ memset(ram_buffer, 0xff, sizeof(ram_buffer));
+ memset(flash_buffer, 0xff, sizeof(flash_buffer));
+ return EC_SUCCESS;
+}
+
+/****************************************************************************/
+/* Helper routines */
+
+static void erase_flash(void)
+{
+ /* Invalidate the RAM cache */
+ release_local_copy();
+
+ /* Zero flash */
+ memset(flash_buffer, 0xff, sizeof(flash_buffer));
+}
+
+/* Erase flash, then copy data_ over it */
+static void load_flash(const uint8_t *data_, size_t data_len)
+{
+ erase_flash();
+ memcpy(flash_buffer, data_, data_len);
+}
+
+/* Return true if flash matches data_, and is followed by 0xff to the end */
+static int verify_flash(const uint8_t *data_, size_t data_len)
+{
+ size_t i;
+
+ /* mismatch means false */
+ if (memcmp(flash_buffer, data_, data_len))
+ return 0;
+
+ for (i = data_len;
+ i < CONFIG_FLASH_NVMEM_VARS_USER_SIZE - data_len;
+ i++)
+ if (flash_buffer[i] != 0xff)
+ return 0;
+ return 1;
+}
+
+/*
+ * Treating both as strings, save the <key, value> pair.
+ */
+int str_setvar(const char *key, const char *val)
+{
+ /* Only for tests, so assume the length will fit */
+ uint8_t key_len, val_len;
+
+ key_len = strlen(key);
+ val_len = val ? strlen(val) : 0;
+
+ return setvar(key, key_len, val, val_len);
+}
+
+/*
+ * Treating both as strings, lookup the key and compare the result with the
+ * expected value. Return true if they match.
+ */
+static int str_matches(const char *key, const char *expected_val)
+{
+ const struct tuple *t = getvar(key, strlen(key));
+ uint8_t expected_len;
+
+ if (!expected_val && !t)
+ return 1;
+
+ if (expected_val && !t)
+ return 0;
+
+ if (!expected_val && t)
+ return 0;
+
+ expected_len = strlen(expected_val);
+ return !memcmp(tuple_val(t), expected_val, expected_len);
+}
+
+/****************************************************************************/
+/* Tests */
+
+static int check_init(void)
+{
+ /* Valid entries */
+ const uint8_t good[] = { 0x01, 0x01, 0x00, 'A', 'a',
+ 0x01, 0x01, 0x00, 'B', 'b',
+ 0x00 };
+
+ /* Empty variables are 0x00, followed by all 0xff */
+ const uint8_t empty[] = { 0x00 };
+
+ /*
+ * This is parsed as though there's only one variable, but it's wrong
+ * because the rest of the storage isn't 0xff.
+ */
+ const uint8_t bad_key[] = { 0x01, 0x01, 0x00, 'A', 'a',
+ 0x00, 0x01, 0x00, 'B', 'b',
+ 0x00 };
+
+ /* Zero-length variables are not allowed */
+ const uint8_t bad_val[] = { 0x01, 0x01, 0x00, 'A', 'a',
+ 0x01, 0x00, 0x00, 'B', 'b',
+ 0x00 };
+
+ /* The next constants use magic numbers based on on the region size */
+ BUILD_ASSERT(CONFIG_FLASH_NVMEM_VARS_USER_SIZE == 600);
+
+ /* This is one byte too large */
+ const uint8_t too_big[] = { [0] = 0xff, [1] = 0xff, /* 0 - 512 */
+ [513] = 0x01, [514] = 0x53, /* 513 - 599 */
+ [599] = 0x00 };
+
+ /* This should just barely fit */
+ const uint8_t just_right[] = { [0] = 0xff, [1] = 0xff, /* 0-512 */
+ [513] = 0x01, [514] = 0x52, /* 513-598 */
+ [599] = 0x00 };
+
+ /* No end marker */
+ const uint8_t not_right[] = { [0] = 0xff, [1] = 0xff, /* 0-512 */
+ [513] = 0x01, [514] = 0x52, /* 513-598 */
+ [599] = 0xff };
+
+ load_flash(good, sizeof(good));
+ TEST_ASSERT(initvars() == EC_SUCCESS);
+ TEST_ASSERT(verify_flash(good, sizeof(good)));
+
+ load_flash(empty, sizeof(empty));
+ TEST_ASSERT(initvars() == EC_SUCCESS);
+ TEST_ASSERT(verify_flash(empty, sizeof(empty)));
+
+ /* All 0xff quickly runs off the end of the storage */
+ erase_flash();
+ TEST_ASSERT(initvars() == EC_SUCCESS);
+ TEST_ASSERT(verify_flash(empty, sizeof(empty)));
+
+ load_flash(bad_key, sizeof(bad_key));
+ TEST_ASSERT(initvars() == EC_SUCCESS);
+ TEST_ASSERT(verify_flash(empty, sizeof(empty)));
+
+ load_flash(bad_val, sizeof(bad_val));
+ TEST_ASSERT(initvars() == EC_SUCCESS);
+ TEST_ASSERT(verify_flash(empty, sizeof(empty)));
+
+ load_flash(too_big, sizeof(too_big));
+ TEST_ASSERT(initvars() == EC_SUCCESS);
+ TEST_ASSERT(verify_flash(empty, sizeof(empty)));
+
+ load_flash(just_right, sizeof(just_right));
+ TEST_ASSERT(initvars() == EC_SUCCESS);
+ TEST_ASSERT(verify_flash(just_right, sizeof(just_right)));
+
+ load_flash(not_right, sizeof(not_right));
+ TEST_ASSERT(initvars() == EC_SUCCESS);
+ TEST_ASSERT(verify_flash(empty, sizeof(empty)));
+
+ return EC_SUCCESS;
+}
+
+static int simple_search(void)
+{
+ const uint8_t preload[] = {
+ 0x02, 0x02, 0x00, 'h', 'o', 'y', 'o',
+ 0x02, 0x4, 0x00, 'y', 'o', 'h', 'o', 'y', 'o',
+ 0x02, 0x06, 0x00, 'm', 'o', 'y', 'o', 'h', 'o', 'y', 'o',
+ 0x00 };
+
+ load_flash(preload, sizeof(preload));
+ TEST_ASSERT(initvars() == EC_SUCCESS);
+ TEST_ASSERT(verify_flash(preload, sizeof(preload)));
+
+ TEST_ASSERT(str_matches("no", 0));
+ TEST_ASSERT(str_matches("ho", "yo"));
+ TEST_ASSERT(str_matches("yo", "hoyo"));
+ TEST_ASSERT(str_matches("mo", "yohoyo"));
+
+ return EC_SUCCESS;
+}
+
+static int simple_write(void)
+{
+ const uint8_t after_one[] = {
+ 0x02, 0x02, 0x00, 'h', 'o', 'y', 'o',
+ 0x00 };
+
+ const uint8_t after_two[] = {
+ 0x02, 0x02, 0x00, 'h', 'o', 'y', 'o',
+ 0x02, 0x4, 0x00, 'y', 'o', 'h', 'o', 'y', 'o',
+ 0x00 };
+
+ const uint8_t after_three[] = {
+ 0x02, 0x02, 0x00, 'h', 'o', 'y', 'o',
+ 0x02, 0x4, 0x00, 'y', 'o', 'h', 'o', 'y', 'o',
+ 0x02, 0x06, 0x00, 'm', 'o', 'y', 'o', 'h', 'o', 'y', 'o',
+ 0x00 };
+
+ erase_flash();
+ TEST_ASSERT(initvars() == EC_SUCCESS);
+
+ TEST_ASSERT(setvar("ho", 2, "yo", 2) == EC_SUCCESS);
+ TEST_ASSERT(writevars() == EC_SUCCESS);
+ TEST_ASSERT(verify_flash(after_one, sizeof(after_one)));
+
+ TEST_ASSERT(setvar("yo", 2, "hoyo", 4) == EC_SUCCESS);
+ TEST_ASSERT(writevars() == EC_SUCCESS);
+ TEST_ASSERT(verify_flash(after_two, sizeof(after_two)));
+
+ TEST_ASSERT(setvar("mo", 2, "yohoyo", 6) == EC_SUCCESS);
+ TEST_ASSERT(writevars() == EC_SUCCESS);
+ TEST_ASSERT(verify_flash(after_three, sizeof(after_three)));
+
+ return EC_SUCCESS;
+}
+
+static int simple_delete(void)
+{
+ const char start_with[] = {
+ 0x01, 0x05, 0x00, 'A', 'a', 'a', 'a', 'a', 'a',
+ 0x02, 0x03, 0x00, 'B', 'B', 'b', 'b', 'b',
+ 0x03, 0x06, 0x00, 'C', 'C', 'C', 'x', 'y', 'z', 'p', 'd', 'q',
+ 0x01, 0x03, 0x00, 'M', 'm', '0', 'm',
+ 0x04, 0x01, 0x00, 'N', 'N', 'N', 'N', 'n',
+ 0x00 };
+
+ const char after_one[] = {
+ 0x02, 0x03, 0x00, 'B', 'B', 'b', 'b', 'b',
+ 0x03, 0x06, 0x00, 'C', 'C', 'C', 'x', 'y', 'z', 'p', 'd', 'q',
+ 0x01, 0x03, 0x00, 'M', 'm', '0', 'm',
+ 0x04, 0x01, 0x00, 'N', 'N', 'N', 'N', 'n',
+ 0x00 };
+
+ const char after_two[] = {
+ 0x02, 0x03, 0x00, 'B', 'B', 'b', 'b', 'b',
+ 0x03, 0x06, 0x00, 'C', 'C', 'C', 'x', 'y', 'z', 'p', 'd', 'q',
+ 0x01, 0x03, 0x00, 'M', 'm', '0', 'm',
+ 0x00 };
+
+ const char after_three[] = {
+ 0x02, 0x03, 0x00, 'B', 'B', 'b', 'b', 'b',
+ 0x01, 0x03, 0x00, 'M', 'm', '0', 'm',
+ 0x00 };
+
+ const char empty[] = { 0x00 };
+
+ erase_flash();
+ TEST_ASSERT(initvars() == EC_SUCCESS);
+
+ TEST_ASSERT(setvar("A", 1, "aaaaa", 5) == EC_SUCCESS);
+ TEST_ASSERT(setvar("BB", 2, "bbb", 3) == EC_SUCCESS);
+ TEST_ASSERT(setvar("CCC", 3, "xyzpdq", 6) == EC_SUCCESS);
+ TEST_ASSERT(setvar("M", 1, "m0m", 3) == EC_SUCCESS);
+ TEST_ASSERT(setvar("NNNN", 4, "n", 1) == EC_SUCCESS);
+ TEST_ASSERT(writevars() == EC_SUCCESS);
+ TEST_ASSERT(verify_flash(start_with, sizeof(start_with)));
+
+ /* Zap first variable by setting var_len to 0 */
+ TEST_ASSERT(setvar("A", 1, "yohoyo", 0) == EC_SUCCESS);
+ TEST_ASSERT(writevars() == EC_SUCCESS);
+ TEST_ASSERT(verify_flash(after_one, sizeof(after_one)));
+
+ /* Zap last variable by passing null pointer */
+ TEST_ASSERT(setvar("NNNN", 4, 0, 3) == EC_SUCCESS);
+ TEST_ASSERT(writevars() == EC_SUCCESS);
+ TEST_ASSERT(verify_flash(after_two, sizeof(after_two)));
+
+ /* Ensure that zapping nonexistant variable does nothing */
+ TEST_ASSERT(setvar("XXX", 3, 0, 0) == EC_SUCCESS);
+ TEST_ASSERT(writevars() == EC_SUCCESS);
+ TEST_ASSERT(verify_flash(after_two, sizeof(after_two)));
+
+ /* Zap variable in the middle */
+ TEST_ASSERT(setvar("CCC", 3, 0, 0) == EC_SUCCESS);
+ TEST_ASSERT(writevars() == EC_SUCCESS);
+ TEST_ASSERT(verify_flash(after_three, sizeof(after_three)));
+
+ /* Zap the rest */
+ TEST_ASSERT(setvar("BB", 2, 0, 0) == EC_SUCCESS);
+ TEST_ASSERT(setvar("M", 1, 0, 0) == EC_SUCCESS);
+ TEST_ASSERT(writevars() == EC_SUCCESS);
+ TEST_ASSERT(verify_flash(empty, sizeof(empty)));
+
+ /* Zapping a nonexistant variable still does nothing */
+ TEST_ASSERT(setvar("XXX", 3, 0, 0) == EC_SUCCESS);
+ TEST_ASSERT(writevars() == EC_SUCCESS);
+ TEST_ASSERT(verify_flash(empty, sizeof(empty)));
+
+ return EC_SUCCESS;
+}
+
+static int complex_write(void)
+{
+ erase_flash();
+ TEST_ASSERT(initvars() == EC_SUCCESS);
+
+ /* Do a bunch of writes and erases */
+ str_setvar("ho", "aa");
+ str_setvar("zo", "nn");
+ str_setvar("yo", "CCCCCCCC");
+ str_setvar("zooo", "yyyyyyy");
+ str_setvar("yo", "AA");
+ str_setvar("ho", 0);
+ str_setvar("yi", "BBB");
+ str_setvar("yi", "AA");
+ str_setvar("hixx", 0);
+ str_setvar("yo", "BBB");
+ str_setvar("zo", "");
+ str_setvar("hi", "bbb");
+ str_setvar("ho", "cccccc");
+ str_setvar("yo", "");
+ str_setvar("zo", "ggggg");
+
+ /* What do we expect to find? */
+ TEST_ASSERT(str_matches("hi", "bbb"));
+ TEST_ASSERT(str_matches("hixx", 0));
+ TEST_ASSERT(str_matches("ho", "cccccc"));
+ TEST_ASSERT(str_matches("yi", "AA"));
+ TEST_ASSERT(str_matches("yo", 0));
+ TEST_ASSERT(str_matches("zo", "ggggg"));
+ TEST_ASSERT(str_matches("zooo", "yyyyyyy"));
+
+ return EC_SUCCESS;
+}
+
+static int weird_keys(void)
+{
+ uint8_t keyA[255];
+ uint8_t keyB[255];
+ const char *valA = "this is A";
+ const char *valB = "THIS IS b";
+ int i;
+ const struct tuple *t;
+
+ erase_flash();
+ TEST_ASSERT(initvars() == EC_SUCCESS);
+
+ for (i = 0; i < 255; i++) {
+ keyA[i] = i;
+ keyB[i] = 255 - i;
+ }
+
+ TEST_ASSERT(setvar(keyA, sizeof(keyA),
+ valA, strlen(valA)) == EC_SUCCESS);
+
+ TEST_ASSERT(setvar(keyB, sizeof(keyB),
+ valB, strlen(valB)) == EC_SUCCESS);
+
+ TEST_ASSERT(writevars() == EC_SUCCESS);
+
+ t = getvar(keyA, sizeof(keyA));
+ TEST_ASSERT(t);
+ TEST_ASSERT(t->val_len == strlen(valA));
+ TEST_ASSERT(memcmp(tuple_val(t), valA, strlen(valA)) == 0);
+
+ t = getvar(keyB, sizeof(keyB));
+ TEST_ASSERT(t);
+ TEST_ASSERT(t->val_len == strlen(valB));
+ TEST_ASSERT(memcmp(tuple_val(t), valB, strlen(valB)) == 0);
+
+ return EC_SUCCESS;
+}
+
+static int weird_values(void)
+{
+ const char *keyA = "this is A";
+ const char *keyB = "THIS IS b";
+ char valA[255];
+ char valB[255];
+ int i;
+ const struct tuple *t;
+
+ erase_flash();
+ TEST_ASSERT(initvars() == EC_SUCCESS);
+
+ for (i = 0; i < 255; i++) {
+ valA[i] = i;
+ valB[i] = 255 - i;
+ }
+
+ TEST_ASSERT(setvar(keyA, strlen(keyA),
+ valA, sizeof(valA)) == EC_SUCCESS);
+ TEST_ASSERT(str_setvar("c", "CcC") == EC_SUCCESS);
+ TEST_ASSERT(setvar(keyB, strlen(keyB),
+ valB, sizeof(valB)) == EC_SUCCESS);
+ TEST_ASSERT(str_setvar("d", "dDd") == EC_SUCCESS);
+
+ TEST_ASSERT(writevars() == EC_SUCCESS);
+
+ t = getvar(keyA, strlen(keyA));
+ TEST_ASSERT(t);
+ TEST_ASSERT(memcmp(tuple_val(t), valA, sizeof(valA)) == 0);
+
+ t = getvar(keyB, strlen(keyB));
+ TEST_ASSERT(t);
+ TEST_ASSERT(memcmp(tuple_val(t), valB, sizeof(valB)) == 0);
+
+ TEST_ASSERT(str_matches("c", "CcC"));
+ TEST_ASSERT(str_matches("d", "dDd"));
+
+ return EC_SUCCESS;
+}
+
+static int fill_it_up(void)
+{
+ int i, n;
+ char key[20];
+
+ erase_flash();
+ TEST_ASSERT(initvars() == EC_SUCCESS);
+
+ /*
+ * Some magic numbers here, because we want to use up 10 bytes at a
+ * time and end up with exactly 9 free bytes left.
+ */
+ TEST_ASSERT(CONFIG_FLASH_NVMEM_VARS_USER_SIZE % 10 == 0);
+ n = CONFIG_FLASH_NVMEM_VARS_USER_SIZE / 10;
+ TEST_ASSERT(n < 1000);
+
+ /* Fill up the storage */
+ for (i = 0; i < n - 1; i++) {
+ /* 3-byte header, 5-char key, 2-char val, == 10 chars */
+ snprintf(key, sizeof(key), "kk%03d", i);
+ TEST_ASSERT(setvar(key, 5, "aa", 2) == EC_SUCCESS);
+ }
+
+ /*
+ * Should be nine bytes left in rbuf (because we need one more '\0' at
+ * the end). This won't fit.
+ */
+ TEST_ASSERT(setvar("kk999", 5, "aa", 2) == EC_ERROR_OVERFLOW);
+ /* But this will. */
+ TEST_ASSERT(setvar("kk999", 5, "a", 1) == EC_SUCCESS);
+ /* And this, because it replaces a previous entry */
+ TEST_ASSERT(setvar("kk000", 5, "bc", 2) == EC_SUCCESS);
+ /* But this still won't fit */
+ TEST_ASSERT(setvar("kk999", 5, "de", 2) == EC_ERROR_OVERFLOW);
+
+ return EC_SUCCESS;
+}
+
+void run_test(void)
+{
+ test_reset();
+
+ RUN_TEST(check_init);
+ RUN_TEST(simple_write);
+ RUN_TEST(simple_search);
+ RUN_TEST(simple_delete);
+ RUN_TEST(complex_write);
+ RUN_TEST(weird_keys);
+ RUN_TEST(weird_values);
+ RUN_TEST(fill_it_up);
+
+ test_print_result();
+}
diff --git a/test/nvmem_vars.tasklist b/test/nvmem_vars.tasklist
new file mode 100644
index 0000000000..cc500f5e8f
--- /dev/null
+++ b/test/nvmem_vars.tasklist
@@ -0,0 +1,17 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ *
+ * For each task, use the macro TASK_TEST(n, r, d, s) where :
+ * 'n' in the name of the task
+ * 'r' in the main routine of the task
+ * 'd' in an opaque parameter passed to the routine at startup
+ * 's' is the stack size in bytes; must be a multiple of 8
+ */
+#define CONFIG_TEST_TASK_LIST /* No test task */
diff --git a/test/pinweaver.c b/test/pinweaver.c
new file mode 100644
index 0000000000..16c40ec9aa
--- /dev/null
+++ b/test/pinweaver.c
@@ -0,0 +1,1739 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <pinweaver.h>
+
+#include <dcrypto.h>
+#include <sha256.h>
+#include <stdint.h>
+#include <string.h>
+#include <timer.h>
+#include <util.h>
+#include <pinweaver_types.h>
+
+#include "test_util.h"
+
+struct pw_test_data_t {
+ union {
+ struct pw_request_t request;
+ struct pw_response_t response;
+ /* Reserve space for the variable length fields. */
+ uint8_t tpm_buffer_size[PW_MAX_MESSAGE_SIZE];
+ };
+};
+
+/******************************************************************************/
+/* Test data
+ */
+const int EMPTY_TREE_PATH_LENGTH = 18;
+const struct merkle_tree_t EMPTY_TREE = {
+ {2} /* bits_per_level */,
+ {6} /* height */,
+ /* root */
+ {0x81, 0xaa, 0xe9, 0xde, 0x93, 0xf4, 0xdf, 0x88,
+ 0x18, 0xfa, 0xff, 0xbd, 0xb7, 0x09, 0xc0, 0x86,
+ 0x48, 0xdd, 0xcd, 0x35, 0x00, 0xf2, 0x88, 0xd6,
+ 0x3f, 0xa6, 0x5e, 0x80, 0x10, 0x19, 0x41, 0x17},
+ /* key derivation nonce. */
+ {0x75, 0xf8, 0x43, 0xf7, 0x23, 0xbd, 0x2a, 0x0f,
+ 0x8d, 0x34, 0xbf, 0xa6, 0x6d, 0xf9, 0x44, 0x38},
+ /* hmac_key */
+ {0x96, 0xc6, 0xb1, 0x64, 0xb6, 0xa7, 0xa8, 0x01,
+ 0xd5, 0x1d, 0x8e, 0x97, 0x24, 0x86, 0xf8, 0x6f,
+ 0xd4, 0x84, 0x0f, 0x95, 0x52, 0x93, 0x8d, 0x7d,
+ 0x00, 0xbb, 0xba, 0xc8, 0xed, 0x7f, 0xa4, 0x7a},
+ /* wrap_key */
+ {0x95, 0xc9, 0x0a, 0xd4, 0xb3, 0x61, 0x1b, 0xcf,
+ 0x1b, 0x49, 0x2b, 0xd6, 0x5d, 0xbc, 0x80, 0xa9,
+ 0xf4, 0x83, 0xf2, 0x84, 0xd4, 0x04, 0x57, 0x7f,
+ 0x02, 0xae, 0x37, 0x64, 0xae, 0xda, 0x71, 0x2a},
+};
+
+const struct leaf_data_t DEFAULT_LEAF = {
+ /*pub*/
+ {
+ /* label = {0, 1, 2, 3, 0, 1} */
+ {0x1b1llu},
+ /* delay_schedule */
+ {{{5}, {20} }, {{6}, {60} }, {{7}, {300} }, {{8}, {600} },
+ {{9}, {1800} }, {{10}, {3600} }, {{50}, {PW_BLOCK_ATTEMPTS} },
+ {{0}, {0} },
+ {{0}, {0} }, {{0}, {0} }, {{0}, {0} }, {{0}, {0} },
+ {{0}, {0} }, {{0}, {0} }, {{0}, {0} }, {{0}, {0} }, },
+ /*timestamp*/
+ {0, 0},
+ /* attempt_count */
+ {0},
+ },
+ /*sec*/
+ {
+ /* low_entropy_secret */
+ {0xba, 0xbc, 0x98, 0x9d, 0x97, 0x20, 0xcf, 0xea,
+ 0xaa, 0xbd, 0xb2, 0xe3, 0xe0, 0x2c, 0x5c, 0x55,
+ 0x06, 0x60, 0x93, 0xbd, 0x07, 0xe2, 0xba, 0x92,
+ 0x10, 0x19, 0x24, 0xb1, 0x29, 0x33, 0x5a, 0xe2},
+ /* high_entropy_secret */
+ {0xe3, 0x46, 0xe3, 0x62, 0x01, 0x5d, 0xfe, 0x0a,
+ 0xd3, 0x67, 0xd7, 0xef, 0xab, 0x01, 0xad, 0x0e,
+ 0x3a, 0xed, 0xe8, 0x2f, 0x99, 0xd1, 0x2d, 0x13,
+ 0x4d, 0x4e, 0xe4, 0x02, 0xbe, 0x71, 0x8e, 0x40},
+ /* reset_secret */
+ {0x8c, 0x33, 0x8c, 0xa7, 0x0f, 0x81, 0xa4, 0xee,
+ 0x24, 0xcd, 0x04, 0x84, 0x9c, 0xa8, 0xfd, 0xdd,
+ 0x14, 0xb0, 0xad, 0xe6, 0xb7, 0x6a, 0x10, 0xfc,
+ 0x03, 0x22, 0xcb, 0x71, 0x31, 0xd3, 0x74, 0xd6},
+ },
+};
+
+const struct leaf_header_t DEFAULT_HEAD = {
+ {
+ .minor = PW_LEAF_MINOR_VERSION,
+ .major = PW_LEAF_MAJOR_VERSION,
+ },
+ sizeof(DEFAULT_LEAF.pub),
+ sizeof(DEFAULT_LEAF.sec),
+};
+
+const uint8_t DEFAULT_IV[] = {
+ 0xaa, 0x65, 0x97, 0xc7, 0x02, 0x23, 0xb8, 0xdc,
+ 0xb3, 0x55, 0xca, 0x3a, 0xab, 0xd0, 0x03, 0x90,
+};
+
+const uint8_t EMPTY_HMAC[32] = {};
+
+const uint32_t DEFAULT_STORAGE_SEED[8] = {
+ 0xe9e9880b, 0xb2a9fa0e, 0x9dcf22af, 0xc40156d0,
+ 0xca8535dc, 0x748606ee, 0x68f0f627, 0x7df7558a,
+};
+
+/* This is not the actual hmac. */
+const uint8_t DEFAULT_HMAC[] = {
+ 0x87, 0x7e, 0xe2, 0xb2, 0x60, 0xeb, 0xf3, 0x4b,
+ 0x80, 0x3e, 0xca, 0xcb, 0xe6, 0x24, 0x21, 0x86,
+ 0xd9, 0xe3, 0x91, 0xf7, 0x2d, 0x16, 0x59, 0xd8,
+ 0x0f, 0x37, 0x0a, 0xf4, 0x64, 0x19, 0x44, 0xe7,
+};
+
+const uint8_t ROOT_WITH_DEFAULT_HMAC[] = {
+ 0x24, 0xad, 0xe4, 0xad, 0xf2, 0xdc, 0x40, 0x26,
+ 0x15, 0x03, 0x16, 0x6f, 0x3c, 0x32, 0x05, 0x99,
+ 0xf8, 0x25, 0x22, 0x92, 0xb9, 0xc7, 0xcd, 0x18,
+ 0x37, 0xc2, 0xf2, 0x72, 0x31, 0xdd, 0xc4, 0xaf,
+};
+
+/******************************************************************************/
+/* Config Variables and defines for Mocks.
+ */
+
+uint32_t MOCK_restart_count;
+
+const uint8_t *MOCK_rand_bytes_src;
+size_t MOCK_rand_bytes_offset;
+size_t MOCK_rand_bytes_len;
+
+void (*MOCK_hash_update_cb)(const void *data, size_t len);
+static void auth_hash_update_cb(const void *data, size_t len);
+
+const uint8_t *MOCK_hmac;
+size_t MOCK_DECRYPTO_init_counter;
+size_t MOCK_DECRYPTO_release_counter;
+
+#define MOCK_AES_XOR_BYTE(b) ((uint8_t)(0x77 + (b & 15)))
+int MOCK_aes_fail;
+int MOCK_appkey_derive_fail;
+enum dcrypto_appid MOCK_hwctx_appkey;
+
+/******************************************************************************/
+/* Helper functions
+ */
+static int do_request(struct merkle_tree_t *merkle_tree,
+ struct pw_test_data_t *buf)
+{
+ int ret = pw_handle_request(merkle_tree, &buf->request, &buf->response);
+ size_t offset = buf->response.header.data_length +
+ sizeof(buf->response.header);
+
+ /* Zero out bytes that won't be sent for testing.*/
+ memset(buf->tpm_buffer_size + offset, 0,
+ sizeof(buf->tpm_buffer_size) - offset);
+ return ret;
+}
+
+static const char *pw_error_str(int code)
+{
+ switch (code) {
+ case EC_SUCCESS:
+ return "EC_SUCCESS";
+ case EC_ERROR_UNKNOWN:
+ return "EC_ERROR_UNKNOWN";
+ case EC_ERROR_UNIMPLEMENTED:
+ return "EC_ERROR_UNIMPLEMENTED";
+ case PW_ERR_VERSION_MISMATCH:
+ return "PW_ERR_VERSION_MISMATCH";
+ case PW_ERR_LENGTH_INVALID:
+ return "PW_ERR_LENGTH_INVALID";
+ case PW_ERR_TYPE_INVALID:
+ return "PW_ERR_TYPE_INVALID";
+ case PW_ERR_BITS_PER_LEVEL_INVALID:
+ return "PW_ERR_BITS_PER_LEVEL_INVALID";
+ case PW_ERR_HEIGHT_INVALID:
+ return "PW_ERR_HEIGHT_INVALID";
+ case PW_ERR_LABEL_INVALID:
+ return "PW_ERR_LABEL_INVALID";
+ case PW_ERR_DELAY_SCHEDULE_INVALID:
+ return "PW_ERR_DELAY_SCHEDULE_INVALID";
+ case PW_ERR_PATH_AUTH_FAILED:
+ return "PW_ERR_PATH_AUTH_FAILED";
+ case PW_ERR_LEAF_VERSION_MISMATCH:
+ return "PW_ERR_LEAF_VERSION_MISMATCH";
+ case PW_ERR_HMAC_AUTH_FAILED:
+ return "PW_ERR_HMAC_AUTH_FAILED";
+ case PW_ERR_LOWENT_AUTH_FAILED:
+ return "PW_ERR_LOWENT_AUTH_FAILED";
+ case PW_ERR_RESET_AUTH_FAILED:
+ return "PW_ERR_RESET_AUTH_FAILED";
+ case PW_ERR_CRYPTO_FAILURE:
+ return "PW_ERR_CRYPTO_FAILURE";
+ case PW_ERR_RATE_LIMIT_REACHED:
+ return "PW_ERR_RATE_LIMIT_REACHED";
+ default:
+ return "?";
+ }
+}
+
+/* Pinweaver specific return code check. This prints the string representation
+ * of the return code instead of just the number.
+ */
+#define TEST_RET_EQ(n, m) \
+ do { \
+ int val1 = n; \
+ int val2 = m; \
+ if (val1 != val2) { \
+ ccprintf("%d: ASSERTION failed: %s (%d) != %s (%d)\n", \
+ __LINE__, pw_error_str(val1), val1, \
+ pw_error_str(val2), val2); \
+ task_dump_trace(); \
+ return EC_ERROR_UNKNOWN; \
+ } \
+ } while (0)
+
+/* Allows mock functions when that don't return success / failure to have
+ * assertions.
+ */
+#define TEST_ASRT_NORET(n) \
+ do { \
+ if (!(n)) { \
+ int x = 0;\
+ ccprintf("%d: ASSERTION failed: %s\n", __LINE__, #n); \
+ task_dump_trace(); \
+ x = 1 / x; \
+ } \
+ } while (0)
+
+/* For debugging and generating test data. */
+void print_array(const uint8_t *data, size_t n) __attribute__ ((unused));
+void print_array(const uint8_t *data, size_t n)
+{
+ size_t x;
+
+ if (n > 0) {
+ ccprintf("uint8_t data[] = {");
+ for (x = 0; x < n - 1; ++x) {
+ if ((x & 7) != 7)
+ ccprintf("0x%02x, ", data[x]);
+ else
+ ccprintf("0x%02x,\n", data[x]);
+ }
+ ccprintf("0x%02x};\n", data[x]);
+ }
+}
+
+/* For exporting structs. This is useful for validating the results of crypto
+ * operations.
+ */
+void print_hex(const uint8_t *data, size_t n) __attribute__ ((unused));
+void print_hex(const uint8_t *data, size_t n)
+{
+ size_t x;
+
+ for (x = 0; x < n; ++x)
+ ccprintf("%02x ", data[x]);
+}
+
+static void setup_default_empty_path(uint8_t hashes[][PW_HASH_SIZE])
+{
+ uint8_t num_siblings = (1 << EMPTY_TREE.bits_per_level.v) - 1;
+ const uint8_t level_hashes[5][PW_HASH_SIZE] = {
+ /* Values for level 5 are all 0 for empty. */
+ /* SHA256 for level 5, values for level 4. */
+ {0x38, 0x72, 0x3a, 0x2e, 0x5e, 0x8a, 0x17, 0xaa,
+ 0x79, 0x50, 0xdc, 0x00, 0x82, 0x09, 0x94, 0x4e,
+ 0x89, 0x8f, 0x69, 0xa7, 0xbd, 0x10, 0xa2, 0x3c,
+ 0x83, 0x9d, 0x34, 0x1e, 0x93, 0x5f, 0xd5, 0xca},
+ /* SHA256 for level 4, values for level 3. */
+ {0xfe, 0xc1, 0x2b, 0x09, 0x33, 0x31, 0x28, 0x34,
+ 0x79, 0x1f, 0x07, 0x64, 0x1a, 0xed, 0x30, 0x53,
+ 0x11, 0x1f, 0x15, 0x3e, 0x1e, 0x3e, 0xd1, 0xf0,
+ 0xcd, 0x16, 0xcb, 0x39, 0x25, 0xfd, 0x5f, 0x84},
+ /* SHA256 for level 3, values for level 2. */
+ {0xb6, 0xd4, 0x9c, 0x89, 0x76, 0x45, 0x9c, 0xe9,
+ 0x9c, 0x0b, 0xad, 0x5d, 0x71, 0xdf, 0x92, 0x77,
+ 0xf6, 0x82, 0x62, 0x63, 0x81, 0x9f, 0xc9, 0x2f,
+ 0x61, 0x9c, 0x29, 0x67, 0x52, 0x37, 0x01, 0x51},
+ /* SHA256 for level 2, values for level 1. */
+ {0x87, 0xeb, 0x61, 0x6b, 0x2c, 0x42, 0x07, 0x5e,
+ 0x70, 0x2d, 0x48, 0x49, 0xf2, 0xe0, 0x13, 0x11,
+ 0xc4, 0xe6, 0x98, 0xfa, 0x22, 0x7e, 0x65, 0xc6,
+ 0x66, 0x33, 0x6b, 0xb6, 0xd7, 0xb9, 0x45, 0xfa},
+ /* SHA256 for level 1, values for level 0. */
+ {0x80, 0x91, 0x04, 0x3f, 0x6c, 0x29, 0x06, 0x35,
+ 0x86, 0x99, 0x21, 0x88, 0x1f, 0xd9, 0xae, 0xb8,
+ 0x35, 0x94, 0x26, 0x19, 0x64, 0x68, 0x4f, 0x4f,
+ 0x4c, 0x66, 0x13, 0xa9, 0x66, 0x69, 0x25, 0x0e},};
+ uint8_t hx;
+ uint8_t kx;
+
+ /* Empty first level. */
+ memset(hashes, 0, num_siblings * PW_HASH_SIZE);
+ hashes += num_siblings;
+
+ for (hx = 1; hx < EMPTY_TREE.height.v; ++hx) {
+ for (kx = 0; kx < num_siblings; ++kx) {
+ memcpy(hashes, level_hashes[hx - 1], PW_HASH_SIZE);
+ ++hashes;
+ }
+ }
+}
+
+static void setup_default_unimported_leaf_data_and_hashes(
+ const struct leaf_data_t *leaf_data,
+ const uint8_t hmac[PW_HASH_SIZE],
+ struct unimported_leaf_data_t *data)
+{
+ memcpy(&data->head, &DEFAULT_HEAD, sizeof(DEFAULT_HEAD));
+ memcpy(data->hmac, hmac, sizeof(data->hmac));
+ memcpy(data->iv, DEFAULT_IV, sizeof(DEFAULT_IV));
+ memcpy(data->payload, &leaf_data->pub, sizeof(leaf_data->pub));
+ DCRYPTO_aes_ctr(data->payload + sizeof(leaf_data->pub),
+ EMPTY_TREE.wrap_key, sizeof(EMPTY_TREE.wrap_key) * 8,
+ DEFAULT_IV, (const uint8_t *)&leaf_data->sec,
+ sizeof(leaf_data->sec));
+ setup_default_empty_path((void *)(data->payload + DEFAULT_HEAD.pub_len +
+ DEFAULT_HEAD.sec_len));
+}
+
+static void setup_reset_tree_defaults(struct merkle_tree_t *merkle_tree,
+ struct pw_request_t *request)
+{
+ MOCK_DECRYPTO_init_counter = 0;
+ MOCK_DECRYPTO_release_counter = 0;
+
+ memset(merkle_tree, 0, sizeof(*merkle_tree));
+
+ request->header.version = PW_PROTOCOL_VERSION;
+ request->header.type.v = PW_RESET_TREE;
+ request->header.data_length = sizeof(struct pw_request_reset_tree_t);
+
+ request->data.reset_tree.bits_per_level.v = 2; /* k = 4 */
+ request->data.reset_tree.height.v = 6; /* L = 12 */
+
+ MOCK_rand_bytes_src = (uint8_t *)EMPTY_TREE.key_derivation_nonce;
+ MOCK_rand_bytes_offset = 0;
+ MOCK_rand_bytes_len = sizeof(EMPTY_TREE.key_derivation_nonce);
+ MOCK_appkey_derive_fail = EC_SUCCESS;
+}
+
+static void setup_insert_leaf_defaults(struct merkle_tree_t *merkle_tree,
+ struct pw_request_t *request)
+{
+ MOCK_DECRYPTO_init_counter = 0;
+ MOCK_DECRYPTO_release_counter = 0;
+
+ memcpy(merkle_tree, &EMPTY_TREE, sizeof(EMPTY_TREE));
+
+ request->header.version = PW_PROTOCOL_VERSION;
+ request->header.type.v = PW_INSERT_LEAF;
+ request->header.data_length = sizeof(struct pw_request_insert_leaf_t) +
+ get_path_auxiliary_hash_count(&EMPTY_TREE) *
+ PW_HASH_SIZE;
+
+ request->data.insert_leaf.label.v = DEFAULT_LEAF.pub.label.v;
+ memcpy(&request->data.insert_leaf.delay_schedule,
+ &DEFAULT_LEAF.pub.delay_schedule,
+ sizeof(DEFAULT_LEAF.pub.delay_schedule));
+ memcpy(&request->data.insert_leaf.low_entropy_secret,
+ &DEFAULT_LEAF.sec.low_entropy_secret,
+ sizeof(DEFAULT_LEAF.sec.low_entropy_secret));
+ memcpy(&request->data.insert_leaf.high_entropy_secret,
+ &DEFAULT_LEAF.sec.high_entropy_secret,
+ sizeof(DEFAULT_LEAF.sec.high_entropy_secret));
+ memcpy(&request->data.insert_leaf.reset_secret,
+ &DEFAULT_LEAF.sec.reset_secret,
+ sizeof(DEFAULT_LEAF.sec.reset_secret));
+ setup_default_empty_path(request->data.insert_leaf.path_hashes);
+
+ MOCK_rand_bytes_src = DEFAULT_IV;
+ MOCK_rand_bytes_offset = 0;
+ MOCK_rand_bytes_len = sizeof(DEFAULT_IV);
+ MOCK_hash_update_cb = 0;
+ MOCK_hmac = DEFAULT_HMAC;
+ MOCK_aes_fail = 0;
+}
+
+static void setup_remove_leaf_defaults(struct merkle_tree_t *merkle_tree,
+ struct pw_request_t *request)
+{
+ MOCK_DECRYPTO_init_counter = 0;
+ MOCK_DECRYPTO_release_counter = 0;
+
+ memcpy(merkle_tree, &EMPTY_TREE, sizeof(EMPTY_TREE));
+ memcpy(merkle_tree->root, ROOT_WITH_DEFAULT_HMAC,
+ sizeof(ROOT_WITH_DEFAULT_HMAC));
+
+ request->header.version = PW_PROTOCOL_VERSION;
+ request->header.type.v = PW_REMOVE_LEAF;
+ request->header.data_length =
+ sizeof(struct pw_request_remove_leaf_t) +
+ get_path_auxiliary_hash_count(&EMPTY_TREE) *
+ PW_HASH_SIZE;
+
+ request->data.remove_leaf.leaf_location = DEFAULT_LEAF.pub.label;
+ memcpy(request->data.remove_leaf.leaf_hmac, DEFAULT_HMAC,
+ sizeof(request->data.remove_leaf.leaf_hmac));
+ setup_default_empty_path(request->data.remove_leaf.path_hashes);
+}
+
+static void setup_try_auth_defaults_with_leaf(
+ const struct leaf_data_t *leaf_data,
+ struct merkle_tree_t *merkle_tree,
+ struct pw_request_t *request)
+{
+ MOCK_DECRYPTO_init_counter = 0;
+ MOCK_DECRYPTO_release_counter = 0;
+
+ memcpy(merkle_tree, &EMPTY_TREE, sizeof(EMPTY_TREE));
+ if (leaf_data->pub.attempt_count.v != 6 &&
+ leaf_data->pub.attempt_count.v != 10) {
+ memcpy(merkle_tree->root, ROOT_WITH_DEFAULT_HMAC,
+ sizeof(ROOT_WITH_DEFAULT_HMAC));
+
+ /* Gets overwritten by auth_hash_update_cb. */
+ MOCK_hmac = DEFAULT_HMAC;
+ } else
+ /* Gets overwritten by auth_hash_update_cb. */
+ MOCK_hmac = EMPTY_HMAC;
+
+ request->header.version = PW_PROTOCOL_VERSION;
+ request->header.type.v = PW_TRY_AUTH;
+ request->header.data_length =
+ sizeof(struct pw_request_try_auth_t) +
+ PW_LEAF_PAYLOAD_SIZE +
+ get_path_auxiliary_hash_count(&EMPTY_TREE) *
+ PW_HASH_SIZE;
+
+ memcpy(request->data.try_auth.low_entropy_secret,
+ DEFAULT_LEAF.sec.low_entropy_secret,
+ sizeof(request->data.try_auth.low_entropy_secret));
+ setup_default_unimported_leaf_data_and_hashes(
+ leaf_data, MOCK_hmac,
+ &request->data.try_auth.unimported_leaf_data);
+
+ MOCK_restart_count = 0;
+ force_time((timestamp_t){.val = 0});
+ MOCK_rand_bytes_src = DEFAULT_IV;
+ MOCK_rand_bytes_offset = 0;
+ MOCK_rand_bytes_len = sizeof(DEFAULT_IV);
+ MOCK_hash_update_cb = auth_hash_update_cb;
+ MOCK_aes_fail = 0;
+}
+
+static void setup_try_auth_defaults(struct merkle_tree_t *merkle_tree,
+ struct pw_request_t *request)
+{
+ setup_try_auth_defaults_with_leaf(&DEFAULT_LEAF, merkle_tree, request);
+}
+
+static void setup_reset_auth_defaults(struct merkle_tree_t *merkle_tree,
+ struct pw_request_t *request)
+{
+ struct leaf_public_data_t *pub =
+ (void *)request->data.reset_auth.unimported_leaf_data
+ .payload;
+
+ MOCK_DECRYPTO_init_counter = 0;
+ MOCK_DECRYPTO_release_counter = 0;
+ memcpy(merkle_tree, &EMPTY_TREE, sizeof(EMPTY_TREE));
+
+ request->header.version = PW_PROTOCOL_VERSION;
+ request->header.type.v = PW_RESET_AUTH;
+ request->header.data_length =
+ sizeof(struct pw_request_reset_auth_t) +
+ PW_LEAF_PAYLOAD_SIZE +
+ get_path_auxiliary_hash_count(&EMPTY_TREE) *
+ PW_HASH_SIZE;
+
+ memcpy(request->data.reset_auth.reset_secret,
+ DEFAULT_LEAF.sec.reset_secret,
+ sizeof(request->data.reset_auth.reset_secret));
+
+ setup_default_unimported_leaf_data_and_hashes(
+ &DEFAULT_LEAF, EMPTY_HMAC,
+ &request->data.try_auth.unimported_leaf_data);
+ pub->attempt_count.v = 6;
+
+ MOCK_rand_bytes_src = DEFAULT_IV;
+ MOCK_rand_bytes_offset = 0;
+ MOCK_rand_bytes_len = sizeof(DEFAULT_IV);
+ MOCK_hash_update_cb = auth_hash_update_cb;
+ MOCK_hmac = EMPTY_HMAC; /* Gets overwritten by auth_hash_update_cb. */
+ MOCK_aes_fail = 0;
+}
+
+/* Increases the length of the pub and cipher_text by 4 each. */
+static void setup_mock_future_version(
+ struct unimported_leaf_data_t *unimported_leaf_data,
+ uint16_t *req_length)
+{
+ uint8_t *start = unimported_leaf_data->payload;
+ const uint8_t size_increase = 4;
+ const uint16_t cipher_text_offset = unimported_leaf_data->head.pub_len;
+ const uint16_t hashes_offset = cipher_text_offset +
+ unimported_leaf_data->head.sec_len;
+
+ /* Shift hashes by 8*/
+ memmove(start + hashes_offset + size_increase * 2,
+ start + hashes_offset,
+ get_path_auxiliary_hash_count(&EMPTY_TREE) *
+ PW_HASH_SIZE);
+
+ /* Shift cipher_text by 4*/
+ memmove(start + cipher_text_offset + size_increase,
+ start + cipher_text_offset,
+ unimported_leaf_data->head.sec_len);
+
+ ++unimported_leaf_data->head.leaf_version.minor;
+ unimported_leaf_data->head.pub_len += size_increase;
+ unimported_leaf_data->head.sec_len += size_increase;
+ *req_length += size_increase * 2;
+}
+
+static int test_handle_short_msg(struct merkle_tree_t *merkle_tree,
+ struct pw_test_data_t *buf,
+ const uint8_t root[PW_HASH_SIZE])
+{
+ int ret = do_request(merkle_tree, buf);
+
+ TEST_RET_EQ(buf->response.header.result_code, ret);
+ TEST_ASSERT(buf->response.header.version == PW_PROTOCOL_VERSION);
+ TEST_ASSERT(buf->response.header.data_length == 0);
+ TEST_ASSERT_ARRAY_EQ(buf->response.header.root, root, PW_HASH_SIZE);
+ TEST_ASSERT_ARRAY_EQ(buf->response.header.root, merkle_tree->root,
+ PW_HASH_SIZE);
+ return ret;
+}
+
+/* Changes MOCK_hmac in a deterministic way based on the contents of the data
+ * with the goal of making it easier to catch bugs in the handling of try_auth
+ * and reset_auth requests.
+ */
+static void auth_hash_update_cb(const void *data, size_t len)
+{
+ const struct leaf_data_t *leaf_data = data;
+
+ if (len != sizeof(leaf_data->pub) && len != sizeof(leaf_data->pub) + 4)
+ return;
+
+ switch (leaf_data->pub.attempt_count.v) {
+ case 10:
+ case 6:
+ MOCK_hmac = EMPTY_HMAC;
+ break;
+ default:
+ MOCK_hmac = DEFAULT_HMAC;
+ break;
+ }
+}
+
+/******************************************************************************/
+/* Mock implementations of TPM, TRNG, and Dcrypto functionality.
+ */
+
+uint32_t get_restart_count(void)
+{
+ return MOCK_restart_count;
+}
+
+void get_storage_seed(void *buf, size_t *len)
+{
+ *len = *len < sizeof(DEFAULT_STORAGE_SEED) ? *len :
+ sizeof(DEFAULT_STORAGE_SEED);
+ memcpy(buf, DEFAULT_STORAGE_SEED, *len);
+}
+
+void rand_bytes(void *buffer, size_t len)
+{
+ if (!MOCK_rand_bytes_src)
+ return;
+
+ TEST_ASRT_NORET(len <= MOCK_rand_bytes_len - MOCK_rand_bytes_offset);
+
+ memcpy(buffer, MOCK_rand_bytes_src + MOCK_rand_bytes_offset, len);
+ MOCK_rand_bytes_offset += len;
+ if (MOCK_rand_bytes_len == MOCK_rand_bytes_offset)
+ MOCK_rand_bytes_offset = 0;
+}
+
+void HASH_update(struct HASH_CTX *ctx, const void *data, size_t len)
+{
+ if (MOCK_hash_update_cb)
+ MOCK_hash_update_cb(data, len);
+ if (ctx)
+ SHA256_update(ctx, data, len);
+}
+
+uint8_t *HASH_final(struct HASH_CTX *ctx)
+{
+ ++MOCK_DECRYPTO_release_counter;
+ return SHA256_final(ctx);
+}
+
+void DCRYPTO_SHA256_init(LITE_SHA256_CTX *ctx, uint32_t sw_required)
+{
+ SHA256_init(ctx);
+ ++MOCK_DECRYPTO_init_counter;
+}
+
+void DCRYPTO_HMAC_SHA256_init(LITE_HMAC_CTX *ctx, const void *key,
+ unsigned int len)
+{
+ TEST_ASRT_NORET(len == sizeof(EMPTY_TREE.hmac_key));
+ TEST_ASRT_NORET(memcmp(key, EMPTY_TREE.hmac_key,
+ sizeof(EMPTY_TREE.hmac_key)) == 0);
+ SHA256_init(&ctx->hash);
+ ++MOCK_DECRYPTO_init_counter;
+}
+
+const uint8_t *DCRYPTO_HMAC_final(LITE_HMAC_CTX *ctx)
+{
+ ++MOCK_DECRYPTO_release_counter;
+ return MOCK_hmac;
+}
+
+/* Perform a symmetric transformation of the data to simulate AES without
+ * requiring a full AES-CTR implementation.
+ *
+ * 1 for success 0 for fail
+ */
+int DCRYPTO_aes_ctr(uint8_t *out, const uint8_t *key, uint32_t key_bits,
+ const uint8_t *iv, const uint8_t *in, size_t in_len)
+{
+ size_t x;
+
+ if (MOCK_aes_fail) {
+ --MOCK_aes_fail;
+ return 0;
+ }
+
+ TEST_ASSERT(key_bits == 256);
+ TEST_ASSERT_ARRAY_EQ(key, EMPTY_TREE.wrap_key,
+ sizeof(EMPTY_TREE.wrap_key));
+ TEST_ASSERT_ARRAY_EQ(iv, DEFAULT_IV, sizeof(DEFAULT_IV));
+ TEST_ASSERT(in_len == sizeof(struct leaf_sensitive_data_t));
+
+ for (x = 0; x < in_len; ++x)
+ out[x] = MOCK_AES_XOR_BYTE(x) ^ in[x];
+ return 1;
+}
+
+/* 1 for success 0 for fail*/
+int DCRYPTO_appkey_init(enum dcrypto_appid appid, struct APPKEY_CTX *ctx)
+{
+ MOCK_hwctx_appkey = appid;
+ return 1;
+}
+
+void DCRYPTO_appkey_finish(struct APPKEY_CTX *ctx)
+{
+ MOCK_hwctx_appkey = 0;
+}
+
+/* 1 for success 0 for fail*/
+int DCRYPTO_appkey_derive(enum dcrypto_appid appid, const uint32_t input[8],
+ uint32_t output[8])
+{
+ TEST_ASSERT(appid == PINWEAVER);
+ TEST_ASSERT(MOCK_hwctx_appkey == appid);
+
+ if (MOCK_appkey_derive_fail != EC_SUCCESS)
+ return 0;
+
+ if (input[6] ^ DEFAULT_STORAGE_SEED[6])
+ memcpy(output, EMPTY_TREE.hmac_key,
+ sizeof(EMPTY_TREE.hmac_key));
+ else
+ memcpy(output, EMPTY_TREE.wrap_key,
+ sizeof(EMPTY_TREE.wrap_key));
+ return 1;
+}
+
+/******************************************************************************/
+/* Reusable test cases.
+ */
+
+static int check_dcrypto_mutex_usage(void)
+{
+ if (MOCK_DECRYPTO_init_counter == MOCK_DECRYPTO_release_counter)
+ return EC_SUCCESS;
+ ccprintf("ASSERTION failed: DCRYPTO init(%d) != DCRYPTO release(%d)\n",
+ MOCK_DECRYPTO_init_counter, MOCK_DECRYPTO_release_counter);
+ return EC_ERROR_UNKNOWN;
+}
+
+static int invalid_length_with_leaf_head(
+ size_t head_offset,
+ void (*defaults)(struct merkle_tree_t *, struct pw_request_t *))
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+ struct leaf_header_t *req_head = (void *)&buf + head_offset;
+ uint8_t old_root[PW_HASH_SIZE];
+
+ defaults(&merkle_tree, &buf.request);
+ memcpy(old_root, merkle_tree.root, sizeof(old_root));
+
+ buf.request.header.data_length = 0;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, old_root),
+ PW_ERR_LENGTH_INVALID);
+
+ defaults(&merkle_tree, &buf.request);
+
+ ++buf.request.header.data_length;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, old_root),
+ PW_ERR_LENGTH_INVALID);
+
+ defaults(&merkle_tree, &buf.request);
+
+ ++req_head->pub_len;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, old_root),
+ PW_ERR_LENGTH_INVALID);
+
+ defaults(&merkle_tree, &buf.request);
+
+ ++req_head->leaf_version.minor;
+ --req_head->pub_len;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, old_root),
+ PW_ERR_LENGTH_INVALID);
+ return check_dcrypto_mutex_usage();
+
+}
+
+/******************************************************************************/
+/* Basic operation test cases.
+ */
+
+static int get_path_auxiliary_hash_count_test(void)
+{
+ struct merkle_tree_t merkle_tree;
+
+ memcpy(&merkle_tree, &EMPTY_TREE, sizeof(merkle_tree));
+
+ TEST_ASSERT(get_path_auxiliary_hash_count(&merkle_tree) ==
+ EMPTY_TREE_PATH_LENGTH);
+ return EC_SUCCESS;
+}
+
+static int compute_hash_test(void)
+{
+ const uint8_t hashes[4][PW_HASH_SIZE] = {
+ {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ };
+ const struct {
+ struct index_t index;
+ uint8_t result[PW_HASH_SIZE];
+ } test_cases[] = {
+ {{0},
+ {0xd5, 0xd9, 0x25, 0xb6, 0xa9, 0x90, 0x24, 0x12,
+ 0x39, 0x0e, 0xfa, 0xd4, 0x8d, 0x55, 0x45, 0xf3,
+ 0x23, 0x6c, 0x6d, 0xff, 0xcc, 0xc8, 0xe1, 0x39,
+ 0xc7, 0xc3, 0x25, 0xf0, 0xd2, 0xa8, 0xf2, 0x0c}
+ },
+ {{1},
+ {0x64, 0x3e, 0x56, 0xbc, 0xb9, 0xda, 0x18, 0xaf,
+ 0xa0, 0x8c, 0x1f, 0xf8, 0x5e, 0xba, 0x58, 0xd0,
+ 0xe1, 0x99, 0x61, 0xe0, 0xe2, 0x12, 0xe9, 0x14,
+ 0xb5, 0x33, 0x46, 0x35, 0x52, 0x1e, 0xaf, 0x91}
+ },
+ {{3},
+ {0xd0, 0x90, 0xc7, 0x3d, 0x12, 0xfb, 0xbc, 0xbc,
+ 0x78, 0xcc, 0xbe, 0x58, 0x21, 0x14, 0xcf, 0x38,
+ 0x68, 0x49, 0x20, 0xe9, 0x61, 0xcb, 0x35, 0xc4,
+ 0x95, 0xb0, 0x14, 0x5a, 0x35, 0x43, 0x3e, 0x73}
+ },
+ };
+ uint8_t result[PW_HASH_SIZE];
+ size_t x;
+
+ for (x = 0; x < ARRAY_SIZE(test_cases); ++x) {
+ compute_hash(hashes, 3, test_cases[x].index, hashes[3], result);
+ TEST_ASSERT_ARRAY_EQ(result, test_cases[x].result,
+ sizeof(result));
+ }
+
+ return EC_SUCCESS;
+}
+
+/******************************************************************************/
+/* Header validation test cases.
+ */
+
+static int handle_request_version_mismatch(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+
+ setup_reset_tree_defaults(&merkle_tree, &buf.request);
+
+ buf.request.header.version = PW_PROTOCOL_VERSION + 1;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_HMAC),
+ PW_ERR_VERSION_MISMATCH);
+ return EC_SUCCESS;
+}
+
+static int handle_request_invalid_type(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+
+ memcpy(&merkle_tree, &EMPTY_TREE, sizeof(merkle_tree));
+ memset(&buf.response, 0x77, sizeof(buf.response));
+
+ buf.request.header.version = PW_PROTOCOL_VERSION;
+ buf.request.header.type.v = PW_MT_INVALID;
+ buf.request.header.data_length = 0;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root),
+ PW_ERR_TYPE_INVALID);
+ return EC_SUCCESS;
+}
+
+/******************************************************************************/
+/* Reset Tree test cases.
+ */
+
+static int handle_reset_tree_invalid_length(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+
+ setup_reset_tree_defaults(&merkle_tree, &buf.request);
+
+ ++buf.request.header.data_length;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_HMAC),
+ PW_ERR_LENGTH_INVALID);
+ return check_dcrypto_mutex_usage();
+}
+
+static int handle_reset_tree_bits_per_level_invalid(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+
+ setup_reset_tree_defaults(&merkle_tree, &buf.request);
+
+ /* Test lower bound. */
+ buf.request.data.reset_tree.bits_per_level.v = BITS_PER_LEVEL_MIN - 1;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, merkle_tree.root),
+ PW_ERR_BITS_PER_LEVEL_INVALID);
+
+ setup_reset_tree_defaults(&merkle_tree, &buf.request);
+
+ /* Test upper bound. */
+ buf.request.data.reset_tree.bits_per_level.v = BITS_PER_LEVEL_MAX + 1;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_HMAC),
+ PW_ERR_BITS_PER_LEVEL_INVALID);
+ return check_dcrypto_mutex_usage();
+}
+
+static int handle_reset_tree_height_invalid(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+
+ setup_reset_tree_defaults(&merkle_tree, &buf.request);
+
+ /* Test lower bound. */
+ buf.request.data.reset_tree.height.v = HEIGHT_MIN - 1;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, merkle_tree.root),
+ PW_ERR_HEIGHT_INVALID);
+
+ setup_reset_tree_defaults(&merkle_tree, &buf.request);
+
+ /* Test upper bound. */
+ buf.request.data.reset_tree.height.v =
+ HEIGHT_MAX(buf.request.data.reset_tree
+ .bits_per_level.v) + 1;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_HMAC),
+ PW_ERR_HEIGHT_INVALID);
+ return check_dcrypto_mutex_usage();
+}
+
+static int handle_reset_tree_crypto_failure(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+
+ setup_reset_tree_defaults(&merkle_tree, &buf.request);
+
+ /* Test lower bound. */
+ MOCK_appkey_derive_fail = PW_ERR_CRYPTO_FAILURE;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_HMAC),
+ PW_ERR_CRYPTO_FAILURE);
+ return check_dcrypto_mutex_usage();
+}
+
+static int handle_reset_tree_success(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+
+ setup_reset_tree_defaults(&merkle_tree, &buf.request);
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root),
+ EC_SUCCESS);
+
+ TEST_ASSERT_ARRAY_EQ((uint8_t *)&merkle_tree, (uint8_t *)&EMPTY_TREE,
+ sizeof(EMPTY_TREE));
+
+ return check_dcrypto_mutex_usage();
+}
+
+/******************************************************************************/
+/* Insert leaf test cases.
+ */
+
+static int handle_insert_leaf_invalid_length(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+
+ setup_insert_leaf_defaults(&merkle_tree, &buf.request);
+
+ ++buf.request.header.data_length;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root),
+ PW_ERR_LENGTH_INVALID);
+ return check_dcrypto_mutex_usage();
+}
+
+static int handle_insert_leaf_label_invalid(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+
+ setup_insert_leaf_defaults(&merkle_tree, &buf.request);
+
+ buf.request.data.insert_leaf.label.v |= 0x030000;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root),
+ PW_ERR_LABEL_INVALID);
+ return check_dcrypto_mutex_usage();
+}
+
+static int handle_insert_leaf_delay_schedule_invalid(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+ struct delay_schedule_entry_t (*ds)[PW_SCHED_COUNT] =
+ &buf.request.data.insert_leaf.delay_schedule;
+
+ setup_insert_leaf_defaults(&merkle_tree, &buf.request);
+
+ /* Non-increasing attempt_count. */
+ (*ds)[1].attempt_count.v = 0;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root),
+ PW_ERR_DELAY_SCHEDULE_INVALID);
+
+ setup_insert_leaf_defaults(&merkle_tree, &buf.request);
+
+ /* Non-increasing time_diff. */
+ (*ds)[1].time_diff.v = 0;
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root),
+ PW_ERR_DELAY_SCHEDULE_INVALID);
+
+ setup_insert_leaf_defaults(&merkle_tree, &buf.request);
+
+ /* attempt_count noise. */
+ (*ds)[14].attempt_count.v = 99;
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root),
+ PW_ERR_DELAY_SCHEDULE_INVALID);
+
+ setup_insert_leaf_defaults(&merkle_tree, &buf.request);
+
+ /* time_diff noise. */
+ (*ds)[14].time_diff.v = 99;
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root),
+ PW_ERR_DELAY_SCHEDULE_INVALID);
+
+ setup_insert_leaf_defaults(&merkle_tree, &buf.request);
+
+ /* Empty delay_schedule. */
+ memset(&(*ds)[0], 0, sizeof(*ds));
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root),
+ PW_ERR_DELAY_SCHEDULE_INVALID);
+ return check_dcrypto_mutex_usage();
+}
+
+static int handle_insert_leaf_path_auth_failed(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+
+ setup_insert_leaf_defaults(&merkle_tree, &buf.request);
+
+ buf.request.data.insert_leaf.path_hashes[0][0] ^= 0xff;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root),
+ PW_ERR_PATH_AUTH_FAILED);
+ return check_dcrypto_mutex_usage();
+}
+
+static int handle_insert_leaf_crypto_failure(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+
+ setup_insert_leaf_defaults(&merkle_tree, &buf.request);
+
+ MOCK_aes_fail = 1;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root),
+ PW_ERR_CRYPTO_FAILURE);
+ return check_dcrypto_mutex_usage();
+}
+
+static int handle_insert_leaf_success(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+ size_t x;
+ const uint8_t *plain_text = (const uint8_t *)&DEFAULT_LEAF.sec;
+ struct wrapped_leaf_data_t *wrapped_leaf_data =
+ (void *)&buf.response.data.insert_leaf
+ .unimported_leaf_data;
+
+ setup_insert_leaf_defaults(&merkle_tree, &buf.request);
+
+ TEST_RET_EQ(do_request(&merkle_tree, &buf), EC_SUCCESS);
+
+ TEST_ASSERT(buf.response.header.version == PW_PROTOCOL_VERSION);
+ TEST_ASSERT(buf.response.header.data_length ==
+ sizeof(buf.response.data.insert_leaf) +
+ PW_LEAF_PAYLOAD_SIZE);
+ TEST_RET_EQ(buf.response.header.result_code, EC_SUCCESS);
+
+ TEST_ASSERT_ARRAY_EQ(buf.response.header.root, ROOT_WITH_DEFAULT_HMAC,
+ sizeof(ROOT_WITH_DEFAULT_HMAC));
+ TEST_ASSERT_ARRAY_EQ(buf.response.header.root, merkle_tree.root,
+ PW_HASH_SIZE);
+ TEST_ASSERT_ARRAY_EQ(
+ buf.response.data.insert_leaf.unimported_leaf_data.hmac,
+ DEFAULT_HMAC, sizeof(DEFAULT_HMAC));
+ TEST_ASSERT_ARRAY_EQ(
+ (uint8_t *)&wrapped_leaf_data->pub,
+ (uint8_t *)&DEFAULT_LEAF.pub, sizeof(DEFAULT_LEAF.pub));
+ for (x = 0; x < sizeof(DEFAULT_LEAF.sec); ++x)
+ TEST_ASSERT(plain_text[x] ==
+ (wrapped_leaf_data->cipher_text[x] ^
+ MOCK_AES_XOR_BYTE(x)));
+
+ return check_dcrypto_mutex_usage();
+}
+
+/******************************************************************************/
+/* Remove leaf test cases.
+ */
+
+static int handle_remove_leaf_invalid_length(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+
+ setup_remove_leaf_defaults(&merkle_tree, &buf.request);
+
+ ++buf.request.header.data_length;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf,
+ ROOT_WITH_DEFAULT_HMAC),
+ PW_ERR_LENGTH_INVALID);
+ return check_dcrypto_mutex_usage();
+}
+
+static int handle_remove_leaf_label_invalid(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+
+ setup_remove_leaf_defaults(&merkle_tree, &buf.request);
+
+ buf.request.data.remove_leaf.leaf_location.v |= 0x030000;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf,
+ ROOT_WITH_DEFAULT_HMAC),
+ PW_ERR_LABEL_INVALID);
+ return check_dcrypto_mutex_usage();
+}
+
+static int handle_remove_leaf_path_auth_failed(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+
+ setup_remove_leaf_defaults(&merkle_tree, &buf.request);
+
+ buf.request.data.remove_leaf.path_hashes[0][0] ^= 0xff;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf,
+ ROOT_WITH_DEFAULT_HMAC),
+ PW_ERR_PATH_AUTH_FAILED);
+ return check_dcrypto_mutex_usage();
+}
+
+static int handle_remove_leaf_success(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+
+ setup_remove_leaf_defaults(&merkle_tree, &buf.request);
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root),
+ EC_SUCCESS);
+
+ return check_dcrypto_mutex_usage();
+}
+
+/******************************************************************************/
+/* Try auth test cases.
+ */
+
+static int handle_try_auth_invalid_length(void)
+{
+ return invalid_length_with_leaf_head(
+ (size_t)&((struct pw_request_t *)0)->data.try_auth
+ .unimported_leaf_data.head,
+ setup_try_auth_defaults);
+}
+
+static int handle_try_auth_leaf_version_mismatch(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+ struct leaf_header_t *req_head =
+ &buf.request.data.try_auth.unimported_leaf_data.head;
+
+ setup_try_auth_defaults(&merkle_tree, &buf.request);
+
+ ++req_head->leaf_version.major;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf,
+ ROOT_WITH_DEFAULT_HMAC),
+ PW_ERR_LEAF_VERSION_MISMATCH);
+ return check_dcrypto_mutex_usage();
+}
+
+static int handle_try_auth_label_invalid(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+ struct leaf_data_t leaf_data;
+
+ memcpy(&leaf_data, &DEFAULT_LEAF, sizeof(DEFAULT_LEAF));
+ leaf_data.pub.label.v |= 0x030000;
+ setup_try_auth_defaults_with_leaf(&leaf_data, &merkle_tree,
+ &buf.request);
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf,
+ ROOT_WITH_DEFAULT_HMAC),
+ PW_ERR_LABEL_INVALID);
+ return check_dcrypto_mutex_usage();
+}
+
+static int handle_try_auth_path_auth_failed(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+ uint8_t (*path_hashes)[32] =
+ (void *)buf.request.data.try_auth.unimported_leaf_data
+ .payload +
+ sizeof(struct leaf_public_data_t) +
+ sizeof(struct leaf_sensitive_data_t);
+
+ setup_try_auth_defaults(&merkle_tree, &buf.request);
+
+ (*path_hashes)[0] ^= 0xff;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf,
+ ROOT_WITH_DEFAULT_HMAC),
+ PW_ERR_PATH_AUTH_FAILED);
+ return check_dcrypto_mutex_usage();
+}
+
+static int handle_try_auth_hmac_auth_failed(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+
+ setup_try_auth_defaults(&merkle_tree, &buf.request);
+
+ MOCK_hash_update_cb = 0;
+ MOCK_hmac = EMPTY_TREE.root;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf,
+ ROOT_WITH_DEFAULT_HMAC),
+ PW_ERR_HMAC_AUTH_FAILED);
+ return check_dcrypto_mutex_usage();
+}
+
+static int handle_try_auth_crypto_failure(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+
+ setup_try_auth_defaults(&merkle_tree, &buf.request);
+
+ MOCK_aes_fail = 1;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf,
+ ROOT_WITH_DEFAULT_HMAC),
+ PW_ERR_CRYPTO_FAILURE);
+ return check_dcrypto_mutex_usage();
+}
+
+static int check_try_auth_rate_limit_reached_response(
+ struct merkle_tree_t *merkle_tree,
+ struct pw_test_data_t *buf,
+ const struct time_diff_t seconds_to_wait)
+{
+ uint8_t old_root[PW_HASH_SIZE];
+
+ memcpy(old_root, merkle_tree->root, sizeof(old_root));
+
+ TEST_RET_EQ(do_request(merkle_tree, buf), PW_ERR_RATE_LIMIT_REACHED);
+
+ TEST_ASSERT(buf->response.header.version == PW_PROTOCOL_VERSION);
+ TEST_ASSERT(buf->response.header.data_length ==
+ sizeof(struct pw_response_try_auth_t) +
+ PW_LEAF_PAYLOAD_SIZE);
+ TEST_RET_EQ(buf->response.header.result_code,
+ PW_ERR_RATE_LIMIT_REACHED);
+ TEST_ASSERT_ARRAY_EQ(buf->response.header.root, old_root,
+ sizeof(old_root));
+ TEST_ASSERT_ARRAY_EQ(buf->response.header.root, merkle_tree->root,
+ sizeof(merkle_tree->root));
+ TEST_ASSERT(buf->response.data.try_auth.seconds_to_wait.v ==
+ seconds_to_wait.v);
+ TEST_ASSERT_MEMSET(buf->response.data.try_auth.high_entropy_secret,
+ 0, PW_SECRET_SIZE);
+ TEST_ASSERT_MEMSET((uint8_t *)&buf->response.data.try_auth
+ .unimported_leaf_data, 0,
+ sizeof(buf->response.data.try_auth
+ .unimported_leaf_data) + PW_LEAF_PAYLOAD_SIZE);
+
+ return check_dcrypto_mutex_usage();
+}
+
+static int handle_try_auth_rate_limit_reached(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+ struct leaf_data_t leaf_data = {};
+
+ /* Test PW_BLOCK_ATTEMPTS. */
+ memcpy(&leaf_data, &DEFAULT_LEAF, sizeof(DEFAULT_LEAF));
+ leaf_data.pub.attempt_count.v = 51;
+ MOCK_restart_count = 1;
+ force_time((timestamp_t){.val = 7200llu * SECOND});
+ setup_try_auth_defaults_with_leaf(&leaf_data, &merkle_tree,
+ &buf.request);
+
+ TEST_RET_EQ(check_try_auth_rate_limit_reached_response(
+ &merkle_tree, &buf,
+ (const struct time_diff_t){PW_BLOCK_ATTEMPTS}),
+ EC_SUCCESS);
+
+ memcpy(&leaf_data, &DEFAULT_LEAF, sizeof(DEFAULT_LEAF));
+ memset(leaf_data.pub.delay_schedule, 0,
+ sizeof(leaf_data.pub.delay_schedule));
+ leaf_data.pub.delay_schedule[0].attempt_count.v = 5;
+ leaf_data.pub.delay_schedule[0].time_diff.v = PW_BLOCK_ATTEMPTS;
+ leaf_data.pub.attempt_count.v = 6;
+ MOCK_restart_count = 1;
+ force_time((timestamp_t){.val = 7200llu * SECOND});
+ setup_try_auth_defaults_with_leaf(&leaf_data, &merkle_tree,
+ &buf.request);
+
+ TEST_RET_EQ(check_try_auth_rate_limit_reached_response(
+ &merkle_tree, &buf,
+ (const struct time_diff_t){PW_BLOCK_ATTEMPTS}),
+ EC_SUCCESS);
+
+ /* Test same boot_count case. */
+ memcpy(&leaf_data, &DEFAULT_LEAF, sizeof(DEFAULT_LEAF));
+ leaf_data.pub.attempt_count.v = 10;
+ leaf_data.pub.timestamp.boot_count = 0;
+ leaf_data.pub.timestamp.timer_value = 7200llu;
+ setup_try_auth_defaults_with_leaf(&leaf_data, &merkle_tree,
+ &buf.request);
+ MOCK_restart_count = 0;
+ force_time((timestamp_t){.val = (leaf_data.pub.timestamp.timer_value +
+ 3599llu) * SECOND});
+
+ TEST_RET_EQ(check_try_auth_rate_limit_reached_response(
+ &merkle_tree, &buf, (const struct time_diff_t){1}),
+ EC_SUCCESS);
+
+ /* Test boot_count + 1 case. */
+ memcpy(&leaf_data, &DEFAULT_LEAF, sizeof(DEFAULT_LEAF));
+ leaf_data.pub.attempt_count.v = 10;
+ leaf_data.pub.timestamp.boot_count = 0;
+ leaf_data.pub.timestamp.timer_value = 7200llu;
+ setup_try_auth_defaults_with_leaf(&leaf_data, &merkle_tree,
+ &buf.request);
+ MOCK_restart_count = 1;
+ force_time((timestamp_t){.val = 3599llu * SECOND});
+
+ TEST_RET_EQ(check_try_auth_rate_limit_reached_response(
+ &merkle_tree, &buf, (const struct time_diff_t){1}),
+ EC_SUCCESS);
+
+ return check_dcrypto_mutex_usage();
+}
+
+static int handle_try_auth_lowent_auth_failed(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+ struct leaf_data_t leaf_data = {};
+ struct leaf_public_data_t *pub =
+ (void *)buf.response.data.try_auth.unimported_leaf_data
+ .payload;
+ struct leaf_sensitive_data_t sec = {};
+ uint8_t *resp_cipher_text = (void *)pub + sizeof(*pub);
+
+ memcpy(&leaf_data, &DEFAULT_LEAF, sizeof(DEFAULT_LEAF));
+ leaf_data.pub.attempt_count.v = 5;
+ leaf_data.sec.low_entropy_secret[
+ sizeof(leaf_data.sec.low_entropy_secret) - 1] =
+ ~leaf_data.sec.low_entropy_secret[
+ sizeof(leaf_data.sec.low_entropy_secret) - 1];
+ setup_try_auth_defaults_with_leaf(&leaf_data, &merkle_tree,
+ &buf.request);
+ MOCK_restart_count = 1;
+ force_time((timestamp_t){.val = (65ull * SECOND)});
+
+ TEST_RET_EQ(do_request(&merkle_tree, &buf), PW_ERR_LOWENT_AUTH_FAILED);
+
+ TEST_ASSERT(buf.response.header.version == PW_PROTOCOL_VERSION);
+ TEST_ASSERT(buf.response.header.data_length ==
+ sizeof(struct pw_response_try_auth_t) +
+ PW_LEAF_PAYLOAD_SIZE);
+ TEST_RET_EQ(buf.response.header.result_code, PW_ERR_LOWENT_AUTH_FAILED);
+
+ TEST_ASSERT_ARRAY_EQ(buf.response.header.root, EMPTY_TREE.root,
+ sizeof(EMPTY_TREE.root));
+ TEST_ASSERT_ARRAY_EQ(buf.response.header.root, merkle_tree.root,
+ sizeof(merkle_tree.root));
+
+ TEST_ASSERT_ARRAY_EQ(
+ buf.response.data.try_auth.unimported_leaf_data.hmac,
+ EMPTY_HMAC, sizeof(EMPTY_HMAC));
+ TEST_ASSERT_ARRAY_EQ(buf.response.data.try_auth.unimported_leaf_data.iv,
+ DEFAULT_IV, sizeof(DEFAULT_IV));
+ DCRYPTO_aes_ctr((uint8_t *)&sec, EMPTY_TREE.wrap_key,
+ sizeof(EMPTY_TREE.wrap_key) * 8, DEFAULT_IV,
+ resp_cipher_text, sizeof(sec));
+ TEST_ASSERT(pub->label.v == leaf_data.pub.label.v);
+ TEST_ASSERT_ARRAY_EQ(
+ (uint8_t *)&pub->delay_schedule,
+ (uint8_t *)&leaf_data.pub.delay_schedule,
+ sizeof(leaf_data.pub.delay_schedule));
+ TEST_ASSERT_ARRAY_EQ((uint8_t *)&sec, (uint8_t *)&leaf_data.sec,
+ sizeof(leaf_data.sec));
+ TEST_ASSERT(pub->attempt_count.v == leaf_data.pub.attempt_count.v + 1);
+ TEST_ASSERT(pub->timestamp.boot_count == 1);
+
+ TEST_ASSERT_MEMSET(buf.response.data.try_auth.high_entropy_secret,
+ 0, PW_SECRET_SIZE);
+
+ /* A threshold of 100 is used since some time will pass after
+ * force_time() is called.
+ */
+ TEST_ASSERT(pub->timestamp.timer_value - 65ull < 100);
+ return check_dcrypto_mutex_usage();
+}
+
+static int handle_try_auth_success(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+ struct leaf_data_t leaf_data = {};
+ struct leaf_public_data_t *pub =
+ (void *)buf.response.data.try_auth.unimported_leaf_data
+ .payload;
+ struct leaf_sensitive_data_t sec = {};
+ uint8_t *resp_cipher_text = (void *)pub + sizeof(*pub);
+
+ /* Test same boot_count case. */
+ memcpy(&leaf_data, &DEFAULT_LEAF, sizeof(leaf_data));
+ leaf_data.pub.attempt_count.v = 6;
+ setup_try_auth_defaults_with_leaf(&leaf_data, &merkle_tree,
+ &buf.request);
+ MOCK_restart_count = 0;
+ force_time((timestamp_t){.val = 65 * SECOND});
+
+ TEST_RET_EQ(do_request(&merkle_tree, &buf), EC_SUCCESS);
+
+ TEST_ASSERT(buf.response.header.version == PW_PROTOCOL_VERSION);
+ TEST_ASSERT(buf.response.header.data_length ==
+ sizeof(struct pw_response_try_auth_t) +
+ PW_LEAF_PAYLOAD_SIZE);
+ TEST_RET_EQ(buf.response.header.result_code, EC_SUCCESS);
+
+ TEST_ASSERT_ARRAY_EQ(buf.response.header.root, ROOT_WITH_DEFAULT_HMAC,
+ sizeof(ROOT_WITH_DEFAULT_HMAC));
+ TEST_ASSERT_ARRAY_EQ(buf.response.header.root, merkle_tree.root,
+ sizeof(merkle_tree.root));
+
+ TEST_ASSERT_ARRAY_EQ(
+ buf.response.data.try_auth.unimported_leaf_data.hmac,
+ DEFAULT_HMAC, sizeof(DEFAULT_HMAC));
+ TEST_ASSERT_ARRAY_EQ(buf.response.data.try_auth.unimported_leaf_data.iv,
+ DEFAULT_IV, sizeof(DEFAULT_IV));
+ DCRYPTO_aes_ctr((uint8_t *)&sec, EMPTY_TREE.wrap_key,
+ sizeof(EMPTY_TREE.wrap_key) * 8, DEFAULT_IV,
+ resp_cipher_text, sizeof(sec));
+ TEST_ASSERT(pub->label.v == leaf_data.pub.label.v);
+ TEST_ASSERT_ARRAY_EQ(
+ (uint8_t *)&pub->delay_schedule,
+ (uint8_t *)&leaf_data.pub.delay_schedule,
+ sizeof(leaf_data.pub.delay_schedule));
+ TEST_ASSERT_ARRAY_EQ((uint8_t *)&sec, (uint8_t *)&DEFAULT_LEAF.sec,
+ sizeof(DEFAULT_LEAF.sec));
+ TEST_ASSERT(pub->attempt_count.v == 0);
+
+ TEST_ASSERT_ARRAY_EQ(buf.response.data.try_auth.high_entropy_secret,
+ DEFAULT_LEAF.sec.high_entropy_secret,
+ sizeof(DEFAULT_LEAF.sec.high_entropy_secret));
+
+ /* Test boot_count + 1 case. */
+ leaf_data.pub.attempt_count.v = 6;
+ leaf_data.pub.timestamp.boot_count = 0;
+ leaf_data.pub.timestamp.timer_value = 7200llu;
+ setup_try_auth_defaults_with_leaf(&leaf_data, &merkle_tree,
+ &buf.request);
+ MOCK_restart_count = 1;
+ force_time((timestamp_t){.val = 65llu * SECOND});
+
+ TEST_RET_EQ(do_request(&merkle_tree, &buf), EC_SUCCESS);
+
+ TEST_ASSERT(buf.response.header.version == PW_PROTOCOL_VERSION);
+ TEST_ASSERT(buf.response.header.data_length ==
+ sizeof(struct pw_response_try_auth_t) +
+ PW_LEAF_PAYLOAD_SIZE);
+ TEST_RET_EQ(buf.response.header.result_code, EC_SUCCESS);
+
+ TEST_ASSERT_ARRAY_EQ(buf.response.header.root, ROOT_WITH_DEFAULT_HMAC,
+ sizeof(ROOT_WITH_DEFAULT_HMAC));
+ TEST_ASSERT_ARRAY_EQ(buf.response.header.root, merkle_tree.root,
+ sizeof(merkle_tree.root));
+
+ TEST_ASSERT_ARRAY_EQ(
+ buf.response.data.try_auth.unimported_leaf_data.hmac,
+ DEFAULT_HMAC, sizeof(DEFAULT_HMAC));
+ TEST_ASSERT_ARRAY_EQ(buf.response.data.try_auth.unimported_leaf_data.iv,
+ DEFAULT_IV, sizeof(DEFAULT_IV));
+ DCRYPTO_aes_ctr((uint8_t *)&sec, EMPTY_TREE.wrap_key,
+ sizeof(EMPTY_TREE.wrap_key) * 8, DEFAULT_IV,
+ resp_cipher_text, sizeof(sec));
+ TEST_ASSERT(pub->label.v == leaf_data.pub.label.v);
+ TEST_ASSERT_ARRAY_EQ(
+ (uint8_t *)&pub->delay_schedule,
+ (uint8_t *)&leaf_data.pub.delay_schedule,
+ sizeof(leaf_data.pub.delay_schedule));
+ TEST_ASSERT_ARRAY_EQ((uint8_t *)&sec, (uint8_t *)&DEFAULT_LEAF.sec,
+ sizeof(DEFAULT_LEAF.sec));
+ TEST_ASSERT(pub->attempt_count.v == 0);
+
+ TEST_ASSERT_ARRAY_EQ(buf.response.data.try_auth.high_entropy_secret,
+ DEFAULT_LEAF.sec.high_entropy_secret,
+ sizeof(DEFAULT_LEAF.sec.high_entropy_secret));
+ return check_dcrypto_mutex_usage();
+}
+
+/******************************************************************************/
+/* Reset auth test cases.
+ */
+
+static int handle_reset_auth_invalid_length(void)
+{
+ return invalid_length_with_leaf_head(
+ (size_t)&((struct pw_request_t *)0)->data.reset_auth
+ .unimported_leaf_data.head,
+ setup_reset_auth_defaults);
+}
+
+static int handle_reset_auth_label_invalid(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+ struct leaf_public_data_t *pub =
+ (void *)buf.request.data.reset_auth.unimported_leaf_data
+ .payload;
+
+ setup_reset_auth_defaults(&merkle_tree, &buf.request);
+ pub->label.v |= 0x030000;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, merkle_tree.root),
+ PW_ERR_LABEL_INVALID);
+ return check_dcrypto_mutex_usage();
+}
+
+static int handle_reset_auth_path_auth_failed(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+ uint8_t (*path_hashes)[32] =
+ (void *)buf.request.data.reset_auth.unimported_leaf_data
+ .payload +
+ sizeof(struct leaf_public_data_t) +
+ sizeof(struct leaf_sensitive_data_t);
+
+ setup_reset_auth_defaults(&merkle_tree, &buf.request);
+
+ (*path_hashes)[0] ^= 0xff;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, merkle_tree.root),
+ PW_ERR_PATH_AUTH_FAILED);
+ return check_dcrypto_mutex_usage();
+}
+
+static int handle_reset_auth_hmac_auth_failed(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+
+ setup_reset_auth_defaults(&merkle_tree, &buf.request);
+
+ MOCK_hash_update_cb = 0;
+ MOCK_hmac = EMPTY_TREE.root;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, merkle_tree.root),
+ PW_ERR_HMAC_AUTH_FAILED);
+ return check_dcrypto_mutex_usage();
+}
+
+static int handle_reset_auth_crypto_failure(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+
+ setup_reset_auth_defaults(&merkle_tree, &buf.request);
+
+ MOCK_aes_fail = 1;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, merkle_tree.root),
+ PW_ERR_CRYPTO_FAILURE);
+ return check_dcrypto_mutex_usage();
+}
+
+static int handle_reset_auth_reset_auth_failed(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+
+ setup_reset_auth_defaults(&merkle_tree, &buf.request);
+
+ buf.request.data.reset_auth.reset_secret[0] ^= 0xff;
+
+ TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, merkle_tree.root),
+ PW_ERR_RESET_AUTH_FAILED);
+ return check_dcrypto_mutex_usage();
+}
+
+static int handle_reset_auth_success(void)
+{
+ struct merkle_tree_t merkle_tree;
+ struct pw_test_data_t buf;
+ struct leaf_public_data_t *pub =
+ (void *)buf.response.data.reset_auth
+ .unimported_leaf_data.payload;
+ struct leaf_sensitive_data_t sec = {};
+ uint8_t *resp_cipher_text = (void *)pub + sizeof(*pub);
+
+ setup_reset_auth_defaults(&merkle_tree, &buf.request);
+
+ TEST_RET_EQ(do_request(&merkle_tree, &buf), EC_SUCCESS);
+
+ TEST_ASSERT(buf.response.header.version == PW_PROTOCOL_VERSION);
+ TEST_ASSERT(buf.response.header.data_length ==
+ sizeof(struct pw_response_reset_auth_t) +
+ PW_LEAF_PAYLOAD_SIZE);
+ TEST_RET_EQ(buf.response.header.result_code, EC_SUCCESS);
+
+ TEST_ASSERT_ARRAY_EQ(buf.response.header.root, ROOT_WITH_DEFAULT_HMAC,
+ sizeof(ROOT_WITH_DEFAULT_HMAC));
+ TEST_ASSERT_ARRAY_EQ(buf.response.header.root, merkle_tree.root,
+ sizeof(merkle_tree.root));
+
+ TEST_ASSERT_ARRAY_EQ(
+ buf.response.data.reset_auth.high_entropy_secret,
+ DEFAULT_LEAF.sec.high_entropy_secret,
+ sizeof(DEFAULT_LEAF.sec.high_entropy_secret));
+ TEST_ASSERT_ARRAY_EQ(
+ buf.response.data.reset_auth.unimported_leaf_data.hmac,
+ DEFAULT_HMAC, sizeof(DEFAULT_HMAC));
+ TEST_ASSERT_ARRAY_EQ(
+ buf.response.data.reset_auth.unimported_leaf_data.iv,
+ DEFAULT_IV, sizeof(DEFAULT_IV));
+ DCRYPTO_aes_ctr((uint8_t *)&sec, EMPTY_TREE.wrap_key,
+ sizeof(EMPTY_TREE.wrap_key) * 8, DEFAULT_IV,
+ resp_cipher_text, sizeof(sec));
+ TEST_ASSERT(pub->label.v == DEFAULT_LEAF.pub.label.v);
+ TEST_ASSERT_ARRAY_EQ(
+ (const uint8_t *)&pub->delay_schedule,
+ (const uint8_t *)&DEFAULT_LEAF.pub.delay_schedule,
+ sizeof(DEFAULT_LEAF.pub.delay_schedule));
+ TEST_ASSERT_ARRAY_EQ((uint8_t *)&sec, (uint8_t *)&DEFAULT_LEAF.sec,
+ sizeof(DEFAULT_LEAF.sec));
+ TEST_ASSERT(pub->attempt_count.v == 0);
+
+ /* Test with different minor version and struct lengths. */
+ setup_reset_auth_defaults(&merkle_tree, &buf.request);
+ setup_mock_future_version(
+ &buf.request.data.reset_auth.unimported_leaf_data,
+ &buf.request.header.data_length);
+
+ TEST_RET_EQ(do_request(&merkle_tree, &buf), EC_SUCCESS);
+
+ TEST_ASSERT(buf.response.header.version == PW_PROTOCOL_VERSION);
+ TEST_ASSERT(buf.response.header.data_length ==
+ sizeof(struct pw_response_reset_auth_t) +
+ PW_LEAF_PAYLOAD_SIZE);
+ TEST_RET_EQ(buf.response.header.result_code, EC_SUCCESS);
+
+ TEST_ASSERT_ARRAY_EQ(buf.response.header.root, ROOT_WITH_DEFAULT_HMAC,
+ sizeof(ROOT_WITH_DEFAULT_HMAC));
+ TEST_ASSERT_ARRAY_EQ(buf.response.header.root, merkle_tree.root,
+ sizeof(merkle_tree.root));
+
+ TEST_ASSERT_ARRAY_EQ(
+ buf.response.data.reset_auth.high_entropy_secret,
+ DEFAULT_LEAF.sec.high_entropy_secret,
+ sizeof(DEFAULT_LEAF.sec.high_entropy_secret));
+ TEST_ASSERT_ARRAY_EQ(
+ buf.response.data.reset_auth.unimported_leaf_data.hmac,
+ DEFAULT_HMAC, sizeof(DEFAULT_HMAC));
+ TEST_ASSERT_ARRAY_EQ(
+ buf.response.data.reset_auth.unimported_leaf_data.iv,
+ DEFAULT_IV, sizeof(DEFAULT_IV));
+ DCRYPTO_aes_ctr((uint8_t *)&sec, EMPTY_TREE.wrap_key,
+ sizeof(EMPTY_TREE.wrap_key) * 8, DEFAULT_IV,
+ resp_cipher_text, sizeof(sec));
+ TEST_ASSERT(pub->label.v == DEFAULT_LEAF.pub.label.v);
+ TEST_ASSERT_ARRAY_EQ(
+ (const uint8_t *)&pub->delay_schedule,
+ (const uint8_t *)&DEFAULT_LEAF.pub.delay_schedule,
+ sizeof(DEFAULT_LEAF.pub.delay_schedule));
+ TEST_ASSERT_ARRAY_EQ((uint8_t *)&sec, (uint8_t *)&DEFAULT_LEAF.sec,
+ sizeof(DEFAULT_LEAF.sec));
+ TEST_ASSERT(pub->attempt_count.v == 0);
+ return check_dcrypto_mutex_usage();
+}
+
+/******************************************************************************/
+/* Main test function. Encapsulates the test cases..
+ */
+
+void run_test(void)
+{
+ test_reset();
+
+ /* Test basic operations. */
+ RUN_TEST(get_path_auxiliary_hash_count_test);
+ RUN_TEST(compute_hash_test);
+
+ /* Test header validation. */
+ RUN_TEST(handle_request_version_mismatch);
+ RUN_TEST(handle_request_invalid_type);
+
+ /* Test reset tree. */
+ RUN_TEST(handle_reset_tree_invalid_length);
+ RUN_TEST(handle_reset_tree_bits_per_level_invalid);
+ RUN_TEST(handle_reset_tree_height_invalid);
+ RUN_TEST(handle_reset_tree_crypto_failure);
+ RUN_TEST(handle_reset_tree_success);
+
+ /* Test insert leaf. */
+ RUN_TEST(handle_insert_leaf_invalid_length);
+ RUN_TEST(handle_insert_leaf_label_invalid);
+ RUN_TEST(handle_insert_leaf_delay_schedule_invalid);
+ RUN_TEST(handle_insert_leaf_path_auth_failed);
+ RUN_TEST(handle_insert_leaf_crypto_failure);
+ RUN_TEST(handle_insert_leaf_success);
+
+ /* Test remove leaf. */
+ RUN_TEST(handle_remove_leaf_invalid_length);
+ RUN_TEST(handle_remove_leaf_label_invalid);
+ RUN_TEST(handle_remove_leaf_path_auth_failed);
+ RUN_TEST(handle_remove_leaf_success);
+
+ /* Test try auth. */
+ RUN_TEST(handle_try_auth_invalid_length);
+ RUN_TEST(handle_try_auth_leaf_version_mismatch);
+ RUN_TEST(handle_try_auth_label_invalid);
+ RUN_TEST(handle_try_auth_path_auth_failed);
+ RUN_TEST(handle_try_auth_hmac_auth_failed);
+ RUN_TEST(handle_try_auth_crypto_failure);
+ RUN_TEST(handle_try_auth_rate_limit_reached);
+ RUN_TEST(handle_try_auth_lowent_auth_failed);
+ RUN_TEST(handle_try_auth_success);
+
+ /* Test reset auth. */
+ RUN_TEST(handle_reset_auth_invalid_length);
+ RUN_TEST(handle_reset_auth_label_invalid);
+ RUN_TEST(handle_reset_auth_path_auth_failed);
+ RUN_TEST(handle_reset_auth_hmac_auth_failed);
+ RUN_TEST(handle_reset_auth_crypto_failure);
+ RUN_TEST(handle_reset_auth_reset_auth_failed);
+ RUN_TEST(handle_reset_auth_success);
+
+ test_print_result();
+}
diff --git a/test/pinweaver.tasklist b/test/pinweaver.tasklist
new file mode 100644
index 0000000000..de4df33e13
--- /dev/null
+++ b/test/pinweaver.tasklist
@@ -0,0 +1,17 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ *
+ * For each task, use the macro TASK_TEST(n, r, d, s) where :
+ * 'n' in the name of the task
+ * 'r' in the main routine of the task
+ * 'd' in an opaque parameter passed to the routine at startup
+ * 's' is the stack size in bytes; must be a multiple of 8
+ */
+#define CONFIG_TEST_TASK_LIST
diff --git a/test/rma_auth.c b/test/rma_auth.c
new file mode 100644
index 0000000000..ced910d778
--- /dev/null
+++ b/test/rma_auth.c
@@ -0,0 +1,200 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Test RMA auth challenge/response
+ */
+
+#include <endian.h>
+#include <stdio.h>
+#include "common.h"
+#include "chip/g/board_id.h"
+#include "curve25519.h"
+#include "base32.h"
+#include "sha256.h"
+#include "rma_auth.h"
+#include "test_util.h"
+#include "timer.h"
+#include "util.h"
+
+/* Dummy implementations for testing */
+static uint8_t dummy_board_id[4] = {'Z', 'Z', 'C', 'R'};
+static uint8_t dummy_device_id[8] = {'T', 'H', 'X', 1, 1, 3, 8, 0xfe};
+static int server_protocol_version = RMA_CHALLENGE_VERSION;
+static uint8_t server_private_key[32] = RMA_TEST_SERVER_PRIVATE_KEY;
+static int server_key_id = RMA_TEST_SERVER_KEY_ID;
+
+void rand_bytes(void *buffer, size_t len)
+{
+ FILE *f = fopen("/dev/urandom", "rb");
+
+ assert(f);
+ fread(buffer, 1, len, f);
+ fclose(f);
+}
+
+int read_board_id(struct board_id *id)
+{
+ memcpy(&id->type, dummy_board_id, sizeof(id->type));
+ id->type_inv = ~id->type;
+ id->flags = 0xFF00;
+ return EC_SUCCESS;
+}
+
+int system_get_chip_unique_id(uint8_t **id)
+{
+ *id = dummy_device_id;
+ return sizeof(dummy_device_id);
+}
+
+/**
+ * Simulate the server side of a RMA challenge-response.
+ *
+ * @param out_auth_code Buffer for generated authorization code
+ * (must be >= CR50_AUTH_CODE_CHARS + 1 chars)
+ * @param challenge Challenge from device
+ * @return 0 if success, non-zero if error.
+ */
+int rma_server_side(char *out_auth_code, const char *challenge)
+{
+ int version, key_id;
+ uint32_t device_id[2];
+ uint8_t secret[32];
+ uint8_t hmac[32];
+ struct rma_challenge c;
+ uint8_t *cptr = (uint8_t *)&c;
+
+ /* Convert the challenge back into binary */
+ if (base32_decode(cptr, 8 * sizeof(c), challenge, 9) != 8 * sizeof(c)) {
+ printf("Error decoding challenge\n");
+ return -1;
+ }
+
+ version = RMA_CHALLENGE_GET_VERSION(c.version_key_id);
+ if (version != server_protocol_version) {
+ printf("Unsupported challenge version %d\n", version);
+ return -1;
+ }
+
+ key_id = RMA_CHALLENGE_GET_KEY_ID(c.version_key_id);
+
+ printf("\nChallenge: %s\n", challenge);
+ printf(" Version: %d\n", version);
+ printf(" Server KeyID: %d\n", key_id);
+ printf(" BoardID: %c%c%c%c\n",
+ isprint(c.board_id[0]) ? c.board_id[0] : '?',
+ isprint(c.board_id[1]) ? c.board_id[1] : '?',
+ isprint(c.board_id[2]) ? c.board_id[2] : '?',
+ isprint(c.board_id[3]) ? c.board_id[3] : '?');
+
+ memcpy(device_id, c.device_id, sizeof(device_id));
+ printf(" DeviceID: 0x%08x 0x%08x\n", device_id[0], device_id[1]);
+
+ if (key_id != server_key_id) {
+ printf("Unsupported KeyID %d\n", key_id);
+ return -1;
+ }
+
+ /*
+ * Make sure the current user is authorized to reset this board.
+ *
+ * Since this is just a test, here we'll just make sure the BoardID
+ * and DeviceID match what we expected.
+ */
+ if (memcmp(c.board_id, dummy_board_id, sizeof(c.board_id))) {
+ printf("BoardID mismatch\n");
+ return -1;
+ }
+ if (memcmp(c.device_id, dummy_device_id, sizeof(c.device_id))) {
+ printf("DeviceID mismatch\n");
+ return -1;
+ }
+
+ /* Calculate the shared secret */
+ X25519(secret, server_private_key, c.device_pub_key);
+
+ /*
+ * Auth code is a truncated HMAC of the ephemeral public key, BoardID,
+ * and DeviceID.
+ */
+ hmac_SHA256(hmac, secret, sizeof(secret), cptr + 1, sizeof(c) - 1);
+ if (base32_encode(out_auth_code, RMA_AUTHCODE_BUF_SIZE,
+ hmac, RMA_AUTHCODE_CHARS * 5, 0)) {
+ printf("Error encoding auth code\n");
+ return -1;
+ }
+ printf("Authcode: %s\n", out_auth_code);
+
+ return 0;
+};
+
+#define FORCE_TIME(t) { ts.val = (t); force_time(ts); }
+
+static int test_rma_auth(void)
+{
+ const char *challenge;
+ char authcode[RMA_AUTHCODE_BUF_SIZE];
+ timestamp_t ts;
+
+ /* Test rate limiting */
+ FORCE_TIME(9 * SECOND);
+ TEST_ASSERT(rma_create_challenge() == EC_ERROR_TIMEOUT);
+ TEST_ASSERT(rma_try_authcode("Bad") == EC_ERROR_ACCESS_DENIED);
+ TEST_ASSERT(strlen(rma_get_challenge()) == 0);
+
+ FORCE_TIME(10 * SECOND);
+ TEST_ASSERT(rma_create_challenge() == 0);
+ TEST_ASSERT(strlen(rma_get_challenge()) == RMA_CHALLENGE_CHARS);
+
+ /* Test using up tries */
+ TEST_ASSERT(rma_try_authcode("Bad") == EC_ERROR_INVAL);
+ TEST_ASSERT(strlen(rma_get_challenge()) == RMA_CHALLENGE_CHARS);
+ TEST_ASSERT(rma_try_authcode("BadCodeZ") == EC_ERROR_INVAL);
+ TEST_ASSERT(strlen(rma_get_challenge()) == RMA_CHALLENGE_CHARS);
+ TEST_ASSERT(rma_try_authcode("BadLongCode") == EC_ERROR_INVAL);
+ /* Out of tries now */
+ TEST_ASSERT(strlen(rma_get_challenge()) == 0);
+ TEST_ASSERT(rma_try_authcode("Bad") == EC_ERROR_ACCESS_DENIED);
+
+ FORCE_TIME(19 * SECOND);
+ TEST_ASSERT(rma_create_challenge() == EC_ERROR_TIMEOUT);
+ TEST_ASSERT(strlen(rma_get_challenge()) == 0);
+
+ FORCE_TIME(21 * SECOND);
+ TEST_ASSERT(rma_create_challenge() == 0);
+ challenge = rma_get_challenge();
+ TEST_ASSERT(strlen(challenge) == RMA_CHALLENGE_CHARS);
+ TEST_ASSERT(rma_server_side(authcode, challenge) == 0);
+ TEST_ASSERT(rma_try_authcode(authcode) == EC_SUCCESS);
+
+ /*
+ * Make sure the server-side checks for fields work. That is, test
+ * our ability to test those fields...
+ */
+ server_protocol_version++;
+ TEST_ASSERT(rma_server_side(authcode, challenge) == -1);
+ server_protocol_version--;
+
+ server_key_id++;
+ TEST_ASSERT(rma_server_side(authcode, challenge) == -1);
+ server_key_id--;
+
+ dummy_board_id[0]++;
+ TEST_ASSERT(rma_server_side(authcode, challenge) == -1);
+ dummy_board_id[0]--;
+
+ dummy_device_id[0]++;
+ TEST_ASSERT(rma_server_side(authcode, challenge) == -1);
+ dummy_device_id[0]--;
+
+ return EC_SUCCESS;
+}
+
+void run_test(void)
+{
+ test_reset();
+
+ RUN_TEST(test_rma_auth);
+
+ test_print_result();
+}
diff --git a/test/rma_auth.tasklist b/test/rma_auth.tasklist
new file mode 100644
index 0000000000..e241aab4bb
--- /dev/null
+++ b/test/rma_auth.tasklist
@@ -0,0 +1,17 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ *
+ * For each task, use the macro TASK_TEST(n, r, d, s) where :
+ * 'n' in the name of the task
+ * 'r' in the main routine of the task
+ * 'd' in an opaque parameter passed to the routine at startup
+ * 's' is the stack size in bytes; must be a multiple of 8
+ */
+#define CONFIG_TEST_TASK_LIST /* No test task */
diff --git a/test/rsa.c b/test/rsa.c
new file mode 100644
index 0000000000..401b502855
--- /dev/null
+++ b/test/rsa.c
@@ -0,0 +1,53 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Tests RSA implementation.
+ */
+
+#include "console.h"
+#include "common.h"
+#include "rsa.h"
+#include "test_util.h"
+#include "util.h"
+
+#ifdef TEST_RSA3
+#include "rsa2048-3.h"
+#else
+#include "rsa2048-F4.h"
+#endif
+
+void run_test(void)
+{
+ int good;
+ uint32_t rsa_workbuf[3 * RSANUMBYTES/4];
+
+ good = rsa_verify(rsa_key, sig, hash, rsa_workbuf);
+ if (!good) {
+ ccprintf("RSA verify FAILED\n");
+ test_fail();
+ return;
+ }
+ ccprintf("RSA verify OK\n");
+
+ /* Test with a wrong hash */
+ good = rsa_verify(rsa_key, sig, hash_wrong, rsa_workbuf);
+ if (good) {
+ ccprintf("RSA verify OK (expected fail)\n");
+ test_fail();
+ return;
+ }
+ ccprintf("RSA verify FAILED (as expected)\n");
+
+ /* Test with a wrong signature */
+ good = rsa_verify(rsa_key, sig+1, hash, rsa_workbuf);
+ if (good) {
+ ccprintf("RSA verify OK (expected fail)\n");
+ test_fail();
+ return;
+ }
+ ccprintf("RSA verify FAILED (as expected)\n");
+
+ test_pass();
+}
+
diff --git a/test/rsa.tasklist b/test/rsa.tasklist
new file mode 100644
index 0000000000..30e5f7fc22
--- /dev/null
+++ b/test/rsa.tasklist
@@ -0,0 +1,17 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ *
+ * For each task, use the macro TASK_TEST(n, r, d, s) where :
+ * 'n' in the name of the task
+ * 'r' in the main routine of the task
+ * 'd' in an opaque parameter passed to the routine at startup
+ * 's' is the stack size in bytes; must be a multiple of 8
+ */
+#define CONFIG_TEST_TASK_LIST
diff --git a/test/rsa2048-3.h b/test/rsa2048-3.h
new file mode 100644
index 0000000000..d1b15c15a4
--- /dev/null
+++ b/test/rsa2048-3.h
@@ -0,0 +1,111 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * RSA 2048 with 3 exponent public key and verification.
+ * Private key in rsa2048-3.pem.
+ */
+
+/* First generate a key:
+ * # openssl genrsa -3 -out key.pem 2048
+ * # openssl rsa -in key.pem -pubout > key.pub
+ * Then dump the key:
+ * # dumpRSAPublicKey -pub key.pub | xxd -i
+ */
+const uint8_t rsa_data[] = {
+ 0x40, 0x00, 0x00, 0x00, 0x0f, 0x46, 0xe8, 0x2c, 0x11, 0x17, 0x38, 0xfd,
+ 0xef, 0xa2, 0xb5, 0x2d, 0x6d, 0x76, 0xe1, 0x70, 0x7d, 0x67, 0xb1, 0x9a,
+ 0x18, 0x78, 0x90, 0xe2, 0xce, 0xa6, 0x81, 0xa0, 0x13, 0x37, 0xf2, 0x71,
+ 0xf0, 0x44, 0x96, 0xaf, 0x52, 0x53, 0xd4, 0x23, 0x51, 0x19, 0xe5, 0xb0,
+ 0x6d, 0x95, 0x99, 0x11, 0x88, 0x5c, 0xed, 0x52, 0x62, 0x07, 0xa6, 0x02,
+ 0xc6, 0xba, 0x48, 0xae, 0x78, 0xd9, 0xfb, 0x73, 0x7a, 0x33, 0xfe, 0x8b,
+ 0xe5, 0x38, 0xf6, 0x8b, 0xa1, 0x3f, 0x1d, 0xe1, 0xfc, 0xae, 0x19, 0xf1,
+ 0x80, 0x94, 0x06, 0xfc, 0x44, 0x69, 0x3c, 0xec, 0xb2, 0xf0, 0x29, 0x9a,
+ 0x97, 0x09, 0x81, 0x88, 0x1a, 0x56, 0x2e, 0xcb, 0xf9, 0x0b, 0x08, 0x5c,
+ 0xd3, 0x44, 0x6a, 0xce, 0xe2, 0xbc, 0x71, 0x03, 0x93, 0x0b, 0x80, 0x0e,
+ 0x12, 0x4c, 0x25, 0x61, 0x97, 0x15, 0x8a, 0x91, 0x37, 0x1e, 0x63, 0x35,
+ 0x83, 0xa0, 0xb8, 0xbb, 0x07, 0x80, 0x7f, 0xbf, 0x2c, 0x1e, 0xab, 0xeb,
+ 0xfb, 0x3d, 0x2c, 0xe7, 0xee, 0x32, 0xca, 0x7f, 0x9b, 0xe5, 0xf7, 0x04,
+ 0xcc, 0xd5, 0xc9, 0x99, 0x55, 0xd2, 0xdb, 0xe5, 0x27, 0x70, 0xac, 0x1a,
+ 0x81, 0x07, 0xff, 0x99, 0x5f, 0x34, 0x6a, 0x91, 0x5a, 0xb3, 0x3a, 0x37,
+ 0xef, 0x61, 0xd4, 0xab, 0xf2, 0x90, 0x98, 0x9a, 0xf7, 0x35, 0x73, 0x93,
+ 0x64, 0xf1, 0x27, 0x3f, 0x9a, 0x52, 0xa6, 0x91, 0x89, 0xb0, 0x5e, 0x70,
+ 0x4c, 0x7e, 0x9e, 0x80, 0x50, 0x2a, 0x25, 0x78, 0xea, 0x6d, 0xad, 0x96,
+ 0x04, 0x45, 0x92, 0xaa, 0x03, 0x6f, 0xec, 0x31, 0xbf, 0x82, 0x4b, 0x4e,
+ 0xb5, 0xf2, 0xc2, 0x0b, 0x88, 0x0a, 0x26, 0xac, 0x2e, 0x02, 0x08, 0x38,
+ 0xce, 0xbd, 0x12, 0xd0, 0x1b, 0x6a, 0x82, 0xe2, 0xe9, 0xb2, 0x9a, 0x3c,
+ 0x1f, 0x61, 0xa0, 0xac, 0xa6, 0x8d, 0x49, 0x60, 0xd6, 0xf5, 0x65, 0xda,
+ 0xde, 0x9e, 0xda, 0x20, 0x67, 0x58, 0x0f, 0xd8, 0x71, 0xd4, 0x21, 0xf5,
+ 0xef, 0x64, 0x91, 0x14, 0x1b, 0xc2, 0x2d, 0x8f, 0x5f, 0xa2, 0x09, 0xc4,
+ 0x82, 0x7f, 0x3a, 0xca, 0xef, 0x5a, 0x12, 0x20, 0x0e, 0x68, 0x36, 0xf1,
+ 0xe0, 0xa2, 0x03, 0x90, 0x68, 0x67, 0xdc, 0x6c, 0x44, 0x41, 0xc7, 0x49,
+ 0xad, 0xa3, 0x93, 0xe7, 0xa3, 0xa1, 0x88, 0xd9, 0xf6, 0x14, 0x2d, 0x8a,
+ 0xc2, 0x8f, 0xb9, 0x14, 0x06, 0xdc, 0x14, 0xd1, 0xe2, 0xf6, 0x04, 0x0b,
+ 0x24, 0x42, 0x24, 0x8a, 0x2e, 0x09, 0x02, 0xeb, 0x55, 0x62, 0x57, 0x67,
+ 0x34, 0xf0, 0xa4, 0x30, 0xb3, 0x06, 0xd9, 0xa3, 0x6c, 0xf3, 0x1f, 0x5f,
+ 0x8f, 0x36, 0x82, 0x3c, 0x12, 0x97, 0x6a, 0xff, 0x84, 0xcd, 0x98, 0x88,
+ 0xad, 0xc2, 0xa0, 0xcc, 0xea, 0x33, 0x7a, 0xc3, 0x7d, 0x29, 0x90, 0x1e,
+ 0xd0, 0x3e, 0x2e, 0x0f, 0xa1, 0xc1, 0x16, 0x46, 0x1c, 0xcd, 0xb0, 0x4e,
+ 0x44, 0x61, 0xa2, 0x77, 0x25, 0x40, 0xba, 0xe6, 0x89, 0xf8, 0xba, 0x5d,
+ 0xe0, 0x4b, 0x6d, 0x8c, 0xe4, 0xe7, 0xf0, 0x5f, 0x13, 0x25, 0x51, 0x72,
+ 0x3b, 0xeb, 0x32, 0xaf, 0x80, 0xde, 0xa6, 0x20, 0x63, 0x38, 0x43, 0x10,
+ 0xf7, 0x8e, 0xfb, 0xd3, 0x06, 0xf9, 0x51, 0x98, 0xf8, 0xc1, 0x62, 0x0d,
+ 0x23, 0x2b, 0x66, 0xd9, 0xe7, 0xd5, 0x03, 0xbb, 0xee, 0x36, 0xde, 0x5c,
+ 0xd8, 0x22, 0x7c, 0x4b, 0xf9, 0x26, 0x63, 0x96, 0x6a, 0x4c, 0x9b, 0x59,
+ 0xd0, 0xf2, 0xc4, 0xf8, 0x79, 0xf5, 0x43, 0x9b, 0xdf, 0x26, 0xeb, 0x2e,
+ 0x80, 0xe2, 0x27, 0x9e, 0xd3, 0xa5, 0x45, 0x37, 0x4c, 0xbd, 0xf9, 0xb0,
+ 0x23, 0xa1, 0x21, 0x4e, 0x1f, 0x6e, 0xdd, 0xac, 0xa6, 0x2c, 0x83, 0x61,
+ 0xdf, 0x8f, 0x9a, 0xfb, 0x55, 0x0a, 0x88, 0x0b, 0x0b, 0x34, 0xbd, 0x35,
+ 0x43, 0x2d, 0xe4, 0x49,
+};
+
+const struct rsa_public_key *rsa_key = (struct rsa_public_key *)rsa_data;
+BUILD_ASSERT(sizeof(*rsa_key) == sizeof(rsa_data));
+
+/* SHA-256 sum to verify:
+ * # sha256sum README | sed -e 's/\(..\)/0x\1, /mg'
+ */
+const uint8_t hash[] = {
+ 0x6c, 0x5f, 0xef, 0x7f, 0x63, 0x1d, 0xb4, 0x35, 0x6c, 0xae, 0x8b, 0x2a,
+ 0x4e, 0xde, 0xc5, 0xeb, 0x11, 0xba, 0x1f, 0x44, 0x40, 0xb6, 0x3a, 0x52,
+ 0xf2, 0x70, 0xef, 0xee, 0x44, 0x4b, 0x57, 0x62
+};
+
+/* Incorrect hash to test the negative case */
+const uint8_t hash_wrong[] = {
+ 0x61, 0x1b, 0xd2, 0x44, 0xc7, 0x18, 0xa7, 0x2d, 0x0f, 0x2d, 0x3d, 0x0f,
+ 0xe3, 0xb3, 0xc5, 0xe4, 0x12, 0xc2, 0x7b, 0x1e, 0x05, 0x2c, 0x6f, 0xad,
+ 0xc4, 0xac, 0x71, 0x55, 0xe8, 0x80, 0x5c, 0x38
+};
+
+/* Generate signature using futility:
+ * # futility create key.pem
+ * # futility sign --type rwsig --prikey key.vbprik2 README README.out
+ * # dd skip=56 bs=1 if=README.out | xxd -i
+ */
+const uint8_t sig[] = {
+ 0xad, 0x93, 0x9d, 0x5e, 0x4b, 0x66, 0xbd, 0xaa, 0xd0, 0x31, 0x15, 0x0f,
+ 0x0c, 0x2c, 0x48, 0x61, 0xfc, 0x81, 0xef, 0xbf, 0x99, 0x7d, 0xc8, 0x51,
+ 0x78, 0x84, 0xb0, 0x7c, 0x26, 0x09, 0x5c, 0x53, 0x36, 0x8c, 0xef, 0xef,
+ 0x24, 0x71, 0xe8, 0xff, 0xa7, 0xac, 0x1a, 0xe3, 0xf7, 0xf3, 0x88, 0xde,
+ 0xc8, 0x2c, 0x13, 0x29, 0x30, 0x90, 0xf9, 0xb0, 0xbc, 0x10, 0xcc, 0x72,
+ 0xc7, 0xb0, 0x4c, 0x0e, 0x0c, 0x14, 0xe1, 0xca, 0x41, 0x4d, 0x3c, 0x40,
+ 0x8e, 0x2e, 0x45, 0x90, 0x7a, 0xb4, 0xa5, 0xd6, 0x8f, 0xf6, 0xfb, 0xef,
+ 0x51, 0x47, 0x60, 0x21, 0x6d, 0x6f, 0xae, 0x9b, 0xb1, 0x9b, 0x34, 0x48,
+ 0x21, 0x9a, 0x5e, 0x70, 0xa3, 0x52, 0xa2, 0x00, 0x11, 0x20, 0x2d, 0x2a,
+ 0xda, 0xc1, 0x23, 0x46, 0xbc, 0xb5, 0xa4, 0x0a, 0xec, 0x6a, 0x3a, 0xe4,
+ 0x96, 0x17, 0x8a, 0x69, 0xce, 0x6f, 0xb0, 0x1d, 0x24, 0xd5, 0x20, 0x03,
+ 0x0d, 0xe4, 0x6a, 0xf2, 0x8d, 0x19, 0x13, 0x7f, 0xd0, 0xe9, 0x51, 0xc7,
+ 0x9b, 0x43, 0x11, 0xb4, 0x77, 0x1c, 0xed, 0xb9, 0xf0, 0xfc, 0xc3, 0xf7,
+ 0x1a, 0xa0, 0x5e, 0x35, 0x2d, 0xc4, 0x35, 0x15, 0x42, 0xfa, 0x2f, 0xeb,
+ 0xe6, 0xc6, 0xed, 0x98, 0x42, 0x93, 0xb1, 0x26, 0x61, 0x29, 0x37, 0xee,
+ 0x87, 0x88, 0x7c, 0xc6, 0xa3, 0xd0, 0x5b, 0xaf, 0xb3, 0x19, 0xa1, 0x1d,
+ 0x5f, 0x13, 0x37, 0x58, 0x33, 0x3b, 0xa8, 0xef, 0xea, 0x17, 0x87, 0x99,
+ 0x43, 0xe3, 0x0a, 0x97, 0xdb, 0x8d, 0x54, 0x69, 0x33, 0x58, 0x9d, 0x2f,
+ 0xfc, 0x32, 0x78, 0xca, 0x3a, 0x65, 0xca, 0x8e, 0x9b, 0x94, 0x18, 0xaa,
+ 0xf0, 0x2b, 0x97, 0xcc, 0xa9, 0xd2, 0xd0, 0x8c, 0x73, 0xd6, 0x82, 0x99,
+ 0xac, 0x07, 0x74, 0x47, 0x6e, 0x4c, 0x7b, 0xf9, 0x18, 0xf6, 0x13, 0x50,
+ 0x9e, 0xea, 0xc7, 0x1c,
+ /* Padding */
+ 0x00
+};
diff --git a/test/rsa2048-3.pem b/test/rsa2048-3.pem
new file mode 100644
index 0000000000..0f89ecf9d3
--- /dev/null
+++ b/test/rsa2048-3.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA2mX11mBJjaasoGEfPJqy6eKCahvQEr3OOAgCLqwmCogLwvK1
+TkuCvzHsbwOqkkUElq1t6nglKlCAnn5McF6wiZGmUpo/J/Fkk3M195qYkPKr1GHv
+NzqzWpFqNF+Z/weBGqxwJ+Xb0lWZydXMBPflm3/KMu7nLD3766seLL9/gAe7uKCD
+NWMeN5GKFZdhJUwSDoALkwNxvOLOakTTXAgL+csuVhqIgQmXminwsuw8aUT8BpSA
+8Rmu/OEdP6GL9jjli/4zenP72XiuSLrGAqYHYlLtXIgRmZVtsOUZUSPUU1KvlkTw
+cfI3E6CBps7ikHgYmrFnfXDhdm0ttaLv/TgXEQIBAwKCAQEAkZlOjurbs8RzFZYU
+0xHMm+xW8WfgDH6JerABdHLEBwVdLKHONDJXKiFISgJxttitucjz8aVuHDWrFFQy
+9ZR1sQvENxF/b/ZDDPd5T7xltfcdOEFKJNHM5wucIupmqgUAvHL1b+6SjDkRMTky
+rfqZElUxd0nvctP9R8dpcypVAAPsiicMqUCbqLyc+fovk8ooQ752pNZBYGI6VwgE
+PgXq2ltM/MAyZmH98R2/uSHTqa3kN5oYv36XnMwJ7LkMrZnipNcEKg+In3EGFhoF
+hiRGh4EFQd0yGx68Lq1DX5BhjZjDsDeAiKzSk2jJwcmr679cYokFfTj7zv2QtyEp
+rfeqSwKBgQDtpNcRRTuTkZEcmBiI6s8wtNtREo5l+ciGzB+IWXzp6UhRECYAEK6C
+hnlT6kLivQhHKyWMfn/ziXvqoduB8iU2Q8YWo8vjzLzhHAsnlq7fQx/IB5ShbRAc
+OiIfloAQbNpVfIpbGY1pbvlccuWmPn3KBfTTnx1vZLcTbkMvwFAevQKBgQDrRI7e
+8kahKOWCBoeQ3M2k9AcIiTOpsobwG5lEpYJByPnpytQ81sgYKgP9MvacLbfeiAfP
+U1vYCDMjurB2/6zbUPWWl5DLHZJEC4iWIsC+U/GdcielA9c3ML8Uq0sxkhM0kWdU
+i2GRx4n2kTq6cFtEAO8Lon34WznBNK4Bt/R45QKBgQCebeS2Lie3tmC9ursF8d91
+zeeLYbRD+9sEiBUFkP3xRjA2CsQACx8BrvuNRtdB01raHMOy/v/3sP1HFpJWoW4k
+LS65woftMyiWErIaZHSU12qFWmMWSLVoJsFqZFVgSJGOUwbnZl5GSfuS90PEKakx
+WU3iahOfmHoM9CzKgDVp0wKBgQCc2F8/TC8WG0OsBFpgkzPDTVoFsM0bzFn1Z7uD
+GQGBMKab3I195IVlcVf+Ifm9c8/psAU04j06sCIX0cr5/8iSNfkPD7XcvmGCslsO
+wdXUN/Zo9sUYrTokyyoNx4d2YWIjC5o4XOu2hQakYNHRoDzYAJ9dFv6lkiaAzclW
+eqL7QwKBgDgfnTj3blHv+u9PmxcStiP2LAh53fXutbqzkRTJ4y+Dzoi2NHrn0+uF
+LL61l2UDg+s/0oACZtaMV1Iqf/LdCEEb1IONE1OL6vm3WuKLB/QY82apnufP+TA5
+OujowZYvaIllZ2ujwPJ14t/XbFzBiaioLlPwIzyh5DFKA1hZJxSU
+-----END RSA PRIVATE KEY-----
diff --git a/test/rsa2048-F4.h b/test/rsa2048-F4.h
new file mode 100644
index 0000000000..afe66a198f
--- /dev/null
+++ b/test/rsa2048-F4.h
@@ -0,0 +1,111 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * RSA 2048 with F4 exponent public key and verification.
+ * Private key in rsa2048-F4.pem.
+ */
+
+/* First generate a key:
+ * # openssl genrsa -out key.pem 2048
+ * # openssl rsa -in key.pem -pubout > key.pub
+ * Then dump the key:
+ * # dumpRSAPublicKey -pub key.pub | xxd -i
+ */
+const uint8_t rsa_data[] = {
+ 0x40, 0x00, 0x00, 0x00, 0xeb, 0xb6, 0x8c, 0xb4, 0x3d, 0xbe, 0xa2, 0xde,
+ 0x0c, 0xa8, 0x6b, 0xcc, 0x1b, 0x58, 0x2e, 0x1b, 0x44, 0x3f, 0xda, 0xdb,
+ 0x1d, 0xe1, 0xe4, 0xfd, 0x4b, 0xc5, 0x34, 0xc9, 0x7e, 0x58, 0xfc, 0x82,
+ 0x6d, 0x95, 0x9f, 0x46, 0x01, 0xaf, 0x7c, 0xa1, 0x50, 0xd5, 0x9c, 0x22,
+ 0xe1, 0x04, 0xcb, 0x41, 0x9a, 0xc4, 0xfe, 0xfa, 0xb6, 0x67, 0x89, 0x0f,
+ 0xe5, 0x59, 0xa0, 0xd4, 0x55, 0xb3, 0xb1, 0x6f, 0x06, 0x28, 0x68, 0x1b,
+ 0x0e, 0x25, 0x97, 0x47, 0xc6, 0xbe, 0x46, 0x14, 0x60, 0x77, 0x11, 0x46,
+ 0xe2, 0x0c, 0x20, 0x59, 0x8c, 0x95, 0x87, 0x62, 0xe8, 0x05, 0xa9, 0xaf,
+ 0x53, 0xab, 0x19, 0xca, 0xc0, 0xf4, 0x41, 0x05, 0x95, 0x3a, 0x6f, 0xd0,
+ 0xdd, 0x87, 0xa7, 0xad, 0xcb, 0x5a, 0x43, 0x86, 0xb4, 0xf4, 0xe9, 0x45,
+ 0x2a, 0x50, 0xe9, 0xe0, 0xf3, 0x16, 0x29, 0x87, 0xd9, 0xec, 0xc5, 0x48,
+ 0xee, 0xf3, 0x53, 0xec, 0x18, 0x7f, 0x46, 0xdd, 0x4b, 0xb9, 0xf9, 0x4b,
+ 0xcd, 0xe4, 0x2a, 0xc8, 0x7d, 0x1a, 0x5e, 0x58, 0x8b, 0x55, 0xf8, 0xed,
+ 0x4d, 0x65, 0xc0, 0x4a, 0x8e, 0x27, 0x2f, 0x97, 0x82, 0x2f, 0x86, 0x69,
+ 0xcf, 0xde, 0x00, 0x95, 0x0e, 0x90, 0x39, 0x12, 0x3e, 0x69, 0x2b, 0x7f,
+ 0xd4, 0xc2, 0xb0, 0x4b, 0x89, 0x41, 0xb2, 0x8f, 0xb7, 0xfb, 0xed, 0xf3,
+ 0x13, 0x1d, 0xb5, 0x01, 0x10, 0x00, 0xdf, 0x3a, 0xbe, 0x0d, 0x1f, 0x12,
+ 0xd8, 0x9c, 0xeb, 0x30, 0xfa, 0x7e, 0x57, 0xde, 0x95, 0x7e, 0xdf, 0x06,
+ 0x9f, 0x7e, 0x08, 0xfc, 0x4f, 0xd4, 0xfa, 0x8f, 0x9a, 0x8a, 0xc8, 0x03,
+ 0xe7, 0xf2, 0xd0, 0x8e, 0x35, 0xb7, 0x33, 0x67, 0x02, 0x77, 0x1e, 0x8f,
+ 0xe9, 0xc8, 0x80, 0x35, 0x6d, 0x24, 0xa2, 0xf9, 0x1c, 0x05, 0xd8, 0x1e,
+ 0x79, 0x07, 0x7e, 0xd4, 0x48, 0xb8, 0x95, 0xfd, 0xf8, 0xb4, 0x0b, 0xd6,
+ 0x29, 0x86, 0xb1, 0x7e, 0xbe, 0xf4, 0xe6, 0xfb, 0x24, 0x62, 0xf1, 0x00,
+ 0x1c, 0xf1, 0x4a, 0x33, 0xea, 0x90, 0xf9, 0xbc, 0x5d, 0x4a, 0xf1, 0x6b,
+ 0x07, 0xfe, 0x77, 0x62, 0x60, 0x83, 0xc2, 0x22, 0x54, 0xfa, 0x94, 0xf9,
+ 0x59, 0x48, 0x62, 0x02, 0xd4, 0x97, 0x53, 0x3e, 0xfb, 0xfc, 0x06, 0x63,
+ 0xf7, 0x28, 0x75, 0x34, 0x2c, 0xac, 0x98, 0xae, 0x8b, 0x78, 0xbd, 0x3c,
+ 0x94, 0x58, 0x40, 0xdf, 0x8e, 0xec, 0x13, 0xcd, 0xe7, 0x20, 0xb8, 0x84,
+ 0xda, 0xbd, 0x8e, 0x76, 0xbd, 0x1a, 0x7d, 0x3d, 0x18, 0x99, 0x91, 0x54,
+ 0x19, 0xbb, 0xab, 0xbe, 0xc3, 0x8c, 0x0d, 0x23, 0x0b, 0xef, 0x5f, 0x1c,
+ 0x49, 0xf0, 0xd1, 0x02, 0x81, 0x37, 0xc8, 0x75, 0x2e, 0xb9, 0x41, 0xf3,
+ 0x90, 0xc4, 0xa2, 0xdc, 0x2f, 0xa2, 0x21, 0xd0, 0x8b, 0x3b, 0x40, 0x3a,
+ 0xc4, 0x26, 0x7c, 0x7d, 0x7b, 0x79, 0xe2, 0x9b, 0xe3, 0xb7, 0x68, 0xd1,
+ 0xcf, 0xc7, 0xce, 0x8c, 0x26, 0x8f, 0x2d, 0xd0, 0x89, 0xc3, 0x18, 0xd1,
+ 0x07, 0x93, 0xa6, 0x1f, 0x9d, 0x13, 0x2a, 0xf2, 0xaf, 0xef, 0xbe, 0xb2,
+ 0x02, 0x39, 0xd8, 0xd3, 0xeb, 0xdf, 0x97, 0xe7, 0x91, 0xb2, 0xc5, 0xd0,
+ 0x21, 0x8f, 0xdd, 0x0c, 0x95, 0x30, 0xc0, 0x5b, 0xd6, 0x00, 0xd2, 0x62,
+ 0x71, 0x89, 0x69, 0x2b, 0x22, 0x67, 0x05, 0x67, 0x1f, 0x02, 0x57, 0x6d,
+ 0xc6, 0x3f, 0xed, 0xfe, 0x1f, 0x4c, 0x28, 0x7b, 0x36, 0x32, 0x3a, 0xa2,
+ 0x61, 0xd2, 0x7a, 0xe1, 0xfd, 0x74, 0x02, 0xe3, 0x70, 0x76, 0x02, 0x19,
+ 0x3a, 0x46, 0xe9, 0x86, 0x50, 0x6e, 0xce, 0x3f, 0x58, 0x0b, 0x0e, 0xef,
+ 0xcf, 0x5a, 0xa2, 0x66, 0x7a, 0xa2, 0x0e, 0x02, 0x56, 0x87, 0x98, 0x67,
+ 0x90, 0xf4, 0x9f, 0x3b, 0xf7, 0xaa, 0x1c, 0xd9, 0xda, 0x0d, 0x49, 0x12,
+ 0x50, 0xa3, 0x70, 0x62,
+};
+
+const struct rsa_public_key *rsa_key = (struct rsa_public_key *)rsa_data;
+BUILD_ASSERT(sizeof(*rsa_key) == sizeof(rsa_data));
+
+/* SHA-256 sum to verify:
+ * # sha256sum README | sed -e 's/\(..\)/0x\1, /mg'
+ */
+const uint8_t hash[] = {
+ 0x6c, 0x5f, 0xef, 0x7f, 0x63, 0x1d, 0xb4, 0x35, 0x6c, 0xae, 0x8b, 0x2a,
+ 0x4e, 0xde, 0xc5, 0xeb, 0x11, 0xba, 0x1f, 0x44, 0x40, 0xb6, 0x3a, 0x52,
+ 0xf2, 0x70, 0xef, 0xee, 0x44, 0x4b, 0x57, 0x62
+};
+
+/* Incorrect hash to test the negative case */
+const uint8_t hash_wrong[] = {
+ 0x61, 0x1b, 0xd2, 0x44, 0xc7, 0x18, 0xa7, 0x2d, 0x0f, 0x2d, 0x3d, 0x0f,
+ 0xe3, 0xb3, 0xc5, 0xe4, 0x12, 0xc2, 0x7b, 0x1e, 0x05, 0x2c, 0x6f, 0xad,
+ 0xc4, 0xac, 0x71, 0x55, 0xe8, 0x80, 0x5c, 0x38
+};
+
+/* Generate signature using futility:
+ * # futility create key.pem
+ * # futility sign --type rwsig --prikey key.vbprik2 README README.out
+ * # dd skip=56 bs=1 if=README.out | xxd -i
+ */
+const uint8_t sig[] = {
+ 0x1a, 0xd8, 0xa0, 0x94, 0x1f, 0x96, 0x8c, 0x40, 0x22, 0xbf, 0xb7, 0xe0,
+ 0x65, 0x30, 0xf8, 0xe1, 0xef, 0x9e, 0x70, 0x38, 0x02, 0xd9, 0x30, 0x10,
+ 0x9f, 0xf9, 0x23, 0x34, 0xd8, 0xe3, 0x04, 0xed, 0x10, 0xcb, 0xde, 0x8b,
+ 0xc3, 0x1e, 0x89, 0x58, 0x24, 0x90, 0xc1, 0x09, 0x3a, 0x08, 0xd9, 0x2f,
+ 0x5b, 0xe9, 0xbe, 0x0c, 0x23, 0xd6, 0xd0, 0x5d, 0x1e, 0x85, 0x59, 0x62,
+ 0xcb, 0x14, 0x76, 0x3f, 0x8f, 0xe6, 0xb9, 0xd2, 0xbd, 0x1c, 0xef, 0xd6,
+ 0xb0, 0x21, 0xaf, 0x3d, 0x93, 0x6d, 0x2d, 0x78, 0x31, 0x87, 0x37, 0xab,
+ 0xfe, 0xca, 0xe8, 0x32, 0xda, 0x86, 0x67, 0x48, 0x0e, 0xab, 0xd0, 0xdd,
+ 0x14, 0x39, 0xe5, 0x8e, 0x65, 0xbb, 0x28, 0xe6, 0xcd, 0xb7, 0xad, 0xbe,
+ 0x86, 0x95, 0xec, 0xf5, 0xc0, 0x00, 0x45, 0x92, 0x3d, 0x67, 0xd7, 0xb6,
+ 0x30, 0x60, 0x6c, 0x6d, 0x86, 0xb5, 0xb5, 0x97, 0xe7, 0x52, 0xca, 0xa4,
+ 0x21, 0xae, 0x48, 0xf9, 0x4a, 0xc8, 0xad, 0xeb, 0xd6, 0x0b, 0xa4, 0x58,
+ 0x61, 0xf2, 0xaf, 0xbc, 0x2a, 0xe6, 0xd7, 0x78, 0x23, 0x66, 0x9c, 0x12,
+ 0x87, 0x54, 0x30, 0x68, 0x4b, 0xdb, 0xc2, 0x66, 0xf6, 0x4b, 0x49, 0x44,
+ 0xbd, 0xb9, 0x1a, 0xdc, 0x71, 0x0f, 0xa4, 0x63, 0xfc, 0x56, 0x41, 0x91,
+ 0xe5, 0x30, 0xb6, 0xa9, 0xe6, 0x8c, 0x6f, 0x91, 0x8e, 0x36, 0xd6, 0x4d,
+ 0xe1, 0xdd, 0x0e, 0x1d, 0x1a, 0x71, 0x51, 0x68, 0xf6, 0xf3, 0xea, 0x74,
+ 0x3a, 0x5f, 0x35, 0x6a, 0x0f, 0xd4, 0x86, 0x96, 0x3e, 0x89, 0xab, 0x88,
+ 0x03, 0x40, 0xe2, 0x2b, 0x72, 0xa5, 0xa5, 0xf7, 0x79, 0xd0, 0x5a, 0xcb,
+ 0xfa, 0x40, 0x12, 0xe2, 0x0c, 0xf0, 0xfb, 0xd8, 0x9c, 0x41, 0x30, 0xab,
+ 0x99, 0x5d, 0x26, 0xe0, 0x6a, 0xcf, 0x0b, 0x4b, 0x4f, 0xe4, 0x85, 0xe6,
+ 0x8d, 0xe3, 0x11, 0xe0,
+ /* Padding */
+ 0x00
+};
diff --git a/test/rsa2048-F4.pem b/test/rsa2048-F4.pem
new file mode 100644
index 0000000000..1662f8ebb5
--- /dev/null
+++ b/test/rsa2048-F4.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA1gu0+P2VuEjUfgd5HtgFHPmiJG01gMjpjx53AmcztzWO0PLn
+A8iKmo/61E/8CH6fBt9+ld5Xfvow65zYEh8NvjrfABABtR0T8+37t4+yQYlLsMLU
+fytpPhI5kA6VAN7PaYYvgpcvJ45KwGVN7fhVi1heGn3IKuTNS/m5S91GfxjsU/Pu
+SMXs2YcpFvPg6VAqRen0tIZDWsutp4fd0G86lQVB9MDKGatTr6kF6GKHlYxZIAzi
+RhF3YBRGvsZHlyUOG2goBm+xs1XUoFnlD4lntvr+xJpBywThIpzVUKF8rwFGn5Vt
+gvxYfsk0xUv95OEd29o/RBsuWBvMa6gM3qK+PQIDAQABAoIBAAc0ca0H7Cg921k6
+qysMnm9xP7H2MxzYpnP41IyyKJ18IgiKhJguAexd+FV5M8SdboDuuPYWe998UHU9
+3FAP14iVtrfr0gLkra1CT3zIS3nFQ1T52elF7s72EhX1R7K1zUmCCMteh2nPcliz
+kEH4X/jGyrQdk8VN2lM6XrBdDGhuwI6iGA+/lxc8xJAmNvpw6qPVAacv1/2af7/u
+s37JD/jpyCibhMvoguRDDozeWckWNcFEhICz6fmwCUHIG48XwwWV2sHPimG1WNwb
+uF6Ma5VxnUNHvNG8uAEy4437AKxBGIVVresTyfDpY2LSIpWAPag/MXEoUBBjcOTm
+8L5IjMkCgYEA9cZH0rrkUEHH+OajhWO1Ca/tgICxRn5PdlYHGhFXv1va5XPZbEdL
+R8s4Gth5c7jLqjehbfOv8p070oz1KMYsrjO+WpZbxmTzse1TzHEaZhwfdt09+y+v
+43dKZgMoj1whlILOWw39cS9jrbh7YWLeAYydwy2/V8pw/7MVFY0a2gsCgYEA3vN8
+cpudgrv4TnyVKJ388hSEwMVp2wompfou5toumlOnGosnQvcgXBt0EsJJUiD9KYOo
+wQDeExTGAkHntcU2VJuqsO3Wn7sI8gXQbU1XUTqhqEsLftWkKj2reLe09rGYWlSw
+ad5miBH4LTSO/2wZfUSxfxBaTcS7ISna9NsDPdcCgYAMQmCwxTvAORfFdZOwgqG0
+Iv9gyoqNLp2+FFp0VWsgE2/exCGTQhciNKPOyv974zrdebrmpiIfovIp9XgBGal+
+4vvavudDBSQWuvTUHMwpTbvQDQcbcWx/lyKx5fRu+jR+mOu8JP2AWNHLB4m3+NuE
+DkSMSMrjkSiDyKYDli9BswKBgQCAwysKnelYSetcmQMkVCp0PXl2RA2g3bn4fgd8
+eGIV615FLDzepg9gYtKkyuTBtB/CTDG4ViHr70F0qE+EYYPBVa49RP+BfOnrrYP0
+vIhDd5NJuR3IgOaxJrDTpXW0TFlrQiIo4rNgvtAQe5xi1DHccUH52p3s8EQLITs9
+0weKPQKBgANF9UkCnISX6HeOJmvSg9dBSZlQMON3Jwk01yTH9fDxFykfip6y/RC4
+izprxpUjWJIGlAAR/nW+/gGaK0EhuCnWcmE46u0ho6ylqpkScrKQ8jPqXoZ9gqn0
+uyKzLlyv0r+jEs87SWBjKc1Lt84piq93dNrlzXkf4/zCDPif5vML
+-----END RSA PRIVATE KEY-----
diff --git a/test/rsa3.tasklist b/test/rsa3.tasklist
new file mode 120000
index 0000000000..6cfe0ef051
--- /dev/null
+++ b/test/rsa3.tasklist
@@ -0,0 +1 @@
+rsa.tasklist \ No newline at end of file
diff --git a/test/rtc.c b/test/rtc.c
new file mode 100644
index 0000000000..5d16d3008c
--- /dev/null
+++ b/test/rtc.c
@@ -0,0 +1,104 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Tests for rtc time conversions
+ */
+
+#include "console.h"
+#include "common.h"
+#include "rtc.h"
+#include "test_util.h"
+#include "util.h"
+
+/* Known conversion pairs of date and epoch time. */
+static struct {
+ struct calendar_date time;
+ uint32_t sec;
+} test_case[] = {
+ {{8, 3, 1}, 1204329600},
+ {{17, 10, 1}, 1506816000},
+};
+
+static int calendar_time_comp(struct calendar_date time_1,
+ struct calendar_date time_2)
+{
+ return (time_1.year == time_2.year &&
+ time_1.month == time_2.month &&
+ time_1.day == time_2.day);
+}
+
+static int test_time_conversion(void)
+{
+ struct calendar_date time_1;
+ struct calendar_date time_2;
+ uint32_t sec;
+ int i;
+
+ /* The seconds elapsed from 01-01-1970 to 01-01-2000 */
+ sec = SECS_TILL_YEAR_2K;
+ time_1.year = 0;
+ time_1.month = 1;
+ time_1.day = 1;
+
+ /* Test from year 2000 to 2050 */
+ for (i = 0; i <= 50; i++) {
+ /* Test Jan. 1 */
+ time_1.year = i;
+ time_1.month = 1;
+ time_1.day = 1;
+
+ TEST_ASSERT(date_to_sec(time_1) == sec);
+ time_2 = sec_to_date(sec);
+ TEST_ASSERT(calendar_time_comp(time_1, time_2));
+
+ /* Test the day boundary between Jan. 1 and Jan. 2 */
+ time_2 = sec_to_date(sec + SECS_PER_DAY - 1);
+ TEST_ASSERT(calendar_time_comp(time_1, time_2));
+
+ time_1.day = 2;
+
+ TEST_ASSERT(date_to_sec(time_1) == sec + SECS_PER_DAY);
+ time_2 = sec_to_date(sec + SECS_PER_DAY);
+ TEST_ASSERT(calendar_time_comp(time_1, time_2));
+
+ /*
+ * Test the month boundary and leap year:
+ * Is the 60th day of a year Mar. 1 or Feb. 29?
+ */
+ time_2 = sec_to_date(sec + 59 * SECS_PER_DAY);
+ if (IS_LEAP_YEAR(i))
+ TEST_ASSERT(time_2.month == 2 && time_2.day == 29);
+ else
+ TEST_ASSERT(time_2.month == 3 && time_2.day == 1);
+
+ /* Test the year boundary on Dec. 31 */
+ sec += SECS_PER_YEAR - (IS_LEAP_YEAR(i) ? 0 : SECS_PER_DAY);
+ time_1.month = 12;
+ time_1.day = 31;
+
+ TEST_ASSERT(date_to_sec(time_1) == sec);
+ time_2 = sec_to_date(sec);
+ TEST_ASSERT(calendar_time_comp(time_1, time_2));
+
+ sec += SECS_PER_DAY;
+ time_2 = sec_to_date(sec - 1);
+ TEST_ASSERT(calendar_time_comp(time_1, time_2));
+ }
+
+ /* Verify known test cases */
+ for (i = 0; i < ARRAY_SIZE(test_case); i++) {
+ TEST_ASSERT(date_to_sec(test_case[i].time) == test_case[i].sec);
+ time_1 = sec_to_date(test_case[i].sec);
+ TEST_ASSERT(calendar_time_comp(time_1, test_case[i].time));
+ }
+
+ return EC_SUCCESS;
+}
+
+void run_test(void)
+{
+ RUN_TEST(test_time_conversion);
+
+ test_print_result();
+}
diff --git a/test/rtc.tasklist b/test/rtc.tasklist
new file mode 100644
index 0000000000..e76178ba0a
--- /dev/null
+++ b/test/rtc.tasklist
@@ -0,0 +1,17 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ *
+ * For each task, use the macro TASK_TEST(n, r, d, s) where :
+ * 'n' in the name of the task
+ * 'r' in the main routine of the task
+ * 'd' in an opaque parameter passed to the routine at startup
+ * 's' is the stack size in bytes; must be a multiple of 8
+ */
+#define CONFIG_TEST_TASK_LIST
diff --git a/test/sbs_charging_v2.c b/test/sbs_charging_v2.c
index 5e11c722fe..e69cbdfb34 100644
--- a/test/sbs_charging_v2.c
+++ b/test/sbs_charging_v2.c
@@ -408,7 +408,7 @@ static int test_external_funcs(void)
TEST_ASSERT(!charge_want_shutdown());
TEST_ASSERT(charge_get_percent() == 50);
temp = 0;
- rv = charge_temp_sensor_get_val(0, &temp);
+ rv = charge_get_battery_temp(0, &temp);
TEST_ASSERT(rv == EC_SUCCESS);
TEST_ASSERT(K_TO_C(temp) == 25);
diff --git a/test/sha256.c b/test/sha256.c
new file mode 100644
index 0000000000..9bb99bb6ab
--- /dev/null
+++ b/test/sha256.c
@@ -0,0 +1,220 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Tests SHA256 implementation.
+ */
+
+#include "console.h"
+#include "common.h"
+#include "sha256.h"
+#include "test_util.h"
+#include "util.h"
+
+/* Short Msg from NIST FIPS 180-4 (Len = 8) */
+static const uint8_t sha256_8_input[] = {
+ 0xd3
+};
+static const uint8_t sha256_8_output[SHA256_DIGEST_SIZE] = {
+ 0x28, 0x96, 0x9c, 0xdf, 0xa7, 0x4a, 0x12, 0xc8, 0x2f, 0x3b, 0xad, 0x96,
+ 0x0b, 0x0b, 0x00, 0x0a, 0xca, 0x2a, 0xc3, 0x29, 0xde, 0xea, 0x5c, 0x23,
+ 0x28, 0xeb, 0xc6, 0xf2, 0xba, 0x98, 0x02, 0xc1
+};
+
+/* Short Msg from NIST FIPS 180-4 (Len = 72) */
+static const uint8_t sha256_72_input[] = {
+ 0x33, 0x34, 0xc5, 0x80, 0x75, 0xd3, 0xf4, 0x13, 0x9e
+};
+static const uint8_t sha256_72_output[SHA256_DIGEST_SIZE] = {
+ 0x07, 0x8d, 0xa3, 0xd7, 0x7e, 0xd4, 0x3b, 0xd3, 0x03, 0x7a, 0x43, 0x3f,
+ 0xd0, 0x34, 0x18, 0x55, 0x02, 0x37, 0x93, 0xf9, 0xaf, 0xd0, 0x8b, 0x4b,
+ 0x08, 0xea, 0x1e, 0x55, 0x97, 0xce, 0xef, 0x20
+};
+
+/* Long Msg from NIST FIPS 180-4 (Len = 2888) */
+static const uint8_t sha256_2888_input[] = {
+ 0x82, 0x82, 0x96, 0x90, 0xaa, 0x37, 0x33, 0xc6, 0x2b, 0x90, 0xd3, 0x29,
+ 0x78, 0x86, 0x95, 0x2f, 0xc1, 0xdc, 0x47, 0x3d, 0x67, 0xbb, 0x7d, 0x6b,
+ 0xb2, 0x99, 0xe0, 0x88, 0xc6, 0x5f, 0xc9, 0x5e, 0xd3, 0xca, 0x0f, 0x36,
+ 0x8d, 0x11, 0x1d, 0x9f, 0xdc, 0xc9, 0x47, 0x6c, 0xd4, 0x06, 0x5e, 0xfc,
+ 0xe7, 0xc4, 0x81, 0xbe, 0x59, 0x85, 0x37, 0xf3, 0xf5, 0x3b, 0xbb, 0xb6,
+ 0xff, 0x67, 0x97, 0x3a, 0x69, 0x83, 0x74, 0x54, 0x49, 0x9e, 0x31, 0x39,
+ 0x8b, 0x46, 0x32, 0x88, 0xe3, 0xaa, 0xfb, 0x8b, 0x06, 0x00, 0xfd, 0xba,
+ 0x1a, 0x25, 0xaf, 0x80, 0x6b, 0x83, 0xe1, 0x42, 0x5f, 0x38, 0x4e, 0x9e,
+ 0xac, 0x75, 0x70, 0xf0, 0xc8, 0x23, 0x98, 0x1b, 0xa2, 0xcd, 0x3d, 0x86,
+ 0x8f, 0xba, 0x94, 0x64, 0x87, 0x59, 0x62, 0x39, 0x91, 0xe3, 0x0f, 0x99,
+ 0x7c, 0x3b, 0xfb, 0x33, 0xd0, 0x19, 0x15, 0x0f, 0x04, 0x67, 0xa9, 0x14,
+ 0xf1, 0xeb, 0x79, 0xcd, 0x87, 0x27, 0x10, 0x6d, 0xbf, 0x7d, 0x53, 0x10,
+ 0xd0, 0x97, 0x59, 0x43, 0xa6, 0x06, 0x7c, 0xc7, 0x90, 0x29, 0xb0, 0x92,
+ 0x39, 0x51, 0x14, 0x17, 0xd9, 0x22, 0xc7, 0xc7, 0xac, 0x3d, 0xfd, 0xd8,
+ 0xa4, 0x1c, 0x52, 0x45, 0x5b, 0x3c, 0x5e, 0x16, 0x4b, 0x82, 0x89, 0xe1,
+ 0x41, 0xd8, 0x20, 0x91, 0x0f, 0x17, 0xa9, 0x66, 0x81, 0x29, 0x74, 0x3d,
+ 0x93, 0x6f, 0x73, 0x12, 0xe1, 0x60, 0x4b, 0xc3, 0x5f, 0x73, 0xab, 0x16,
+ 0x4a, 0x3f, 0xdd, 0xfe, 0x5f, 0xe1, 0x9b, 0x1a, 0x4a, 0x9f, 0x23, 0x7f,
+ 0x61, 0xcb, 0x8e, 0xb7, 0x92, 0xe9, 0x5d, 0x09, 0x9a, 0x14, 0x55, 0xfb,
+ 0x78, 0x9d, 0x8d, 0x16, 0x22, 0xf6, 0xc5, 0xe9, 0x76, 0xce, 0xf9, 0x51,
+ 0x73, 0x7e, 0x36, 0xf7, 0xa9, 0xa4, 0xad, 0x19, 0xee, 0x0d, 0x06, 0x8e,
+ 0x53, 0xd9, 0xf6, 0x04, 0x57, 0xd9, 0x14, 0x8d, 0x5a, 0x3c, 0xe8, 0x5a,
+ 0x54, 0x6b, 0x45, 0xc5, 0xc6, 0x31, 0xd9, 0x95, 0xf1, 0x1f, 0x03, 0x7e,
+ 0x47, 0x2f, 0xe4, 0xe8, 0x1f, 0xa7, 0xb9, 0xf2, 0xac, 0x40, 0x68, 0xb5,
+ 0x30, 0x88, 0x58, 0xcd, 0x6d, 0x85, 0x86, 0x16, 0x5c, 0x9b, 0xd6, 0xb3,
+ 0x22, 0xaf, 0xa7, 0x55, 0x40, 0x8d, 0xa9, 0xb9, 0x0a, 0x87, 0xf3, 0x73,
+ 0x5a, 0x5f, 0x50, 0xeb, 0x85, 0x68, 0xda, 0xa5, 0x8e, 0xe7, 0xcb, 0xc5,
+ 0x9a, 0xbf, 0x8f, 0xd2, 0xa4, 0x4e, 0x1e, 0xba, 0x72, 0x92, 0x88, 0x16,
+ 0xc8, 0x90, 0xd1, 0xb0, 0xdb, 0xf6, 0x00, 0x42, 0x08, 0xff, 0x73, 0x81,
+ 0xc6, 0x97, 0x75, 0x5a, 0xda, 0xc0, 0x13, 0x7c, 0xca, 0x34, 0x2b, 0x16,
+ 0x93
+};
+static const uint8_t sha256_2888_output[SHA256_DIGEST_SIZE] = {
+ 0x5f, 0x4e, 0x16, 0xa7, 0x2d, 0x6c, 0x98, 0x57, 0xda, 0x0b, 0xa0, 0x09,
+ 0xcc, 0xac, 0xd4, 0xf2, 0x6d, 0x7f, 0x6b, 0xf6, 0xc1, 0xb7, 0x8a, 0x2e,
+ 0xd3, 0x5e, 0x68, 0xfc, 0xb1, 0x5b, 0x8e, 0x40
+};
+
+/* HMAC short key (40 bytes) from NIST FIPS 198-1 (Count = 34) */
+static const uint8_t hmac_short_msg[] = {
+ 0x49, 0x53, 0x40, 0x8b, 0xe3, 0xdd, 0xde, 0x42, 0x52, 0x1e, 0xb6, 0x25,
+ 0xa3, 0x7a, 0xf0, 0xd2, 0xcf, 0x9e, 0xd1, 0x84, 0xf5, 0xb6, 0x27, 0xe5,
+ 0xe7, 0xe0, 0xe8, 0x24, 0xe8, 0xe1, 0x16, 0x48, 0xb4, 0x18, 0xe5, 0xc4,
+ 0xc1, 0xb0, 0x20, 0x4b, 0xc5, 0x19, 0xc9, 0xe5, 0x78, 0xb8, 0x00, 0x43,
+ 0x9b, 0xdd, 0x25, 0x4f, 0x39, 0xf6, 0x41, 0x08, 0x2d, 0x03, 0xa2, 0x8d,
+ 0xe4, 0x4a, 0xc6, 0x77, 0x64, 0x4c, 0x7b, 0x6c, 0x8d, 0xf7, 0x43, 0xf2,
+ 0x9f, 0x1d, 0xfd, 0x80, 0xfd, 0x25, 0xc2, 0xdb, 0x31, 0x01, 0x0e, 0xa0,
+ 0x2f, 0x60, 0x20, 0x1c, 0xde, 0x24, 0xa3, 0x64, 0xd4, 0x16, 0x8d, 0xa2,
+ 0x61, 0xd8, 0x48, 0xae, 0xd0, 0x1c, 0x10, 0xde, 0xe9, 0x14, 0x9c, 0x1e,
+ 0xbb, 0x29, 0x00, 0x43, 0x98, 0xf0, 0xd2, 0x9c, 0x60, 0x5a, 0x8b, 0xca,
+ 0x03, 0x2b, 0x31, 0xd2, 0x41, 0xad, 0x33, 0x71
+};
+static const uint8_t hmac_short_key[] = {
+ 0x9d, 0xa0, 0xc1, 0x14, 0x68, 0x2f, 0x82, 0xc1, 0xd1, 0xe9, 0xb5, 0x44,
+ 0x30, 0x58, 0x0b, 0x9c, 0x56, 0x94, 0x89, 0xca, 0x16, 0xb9, 0x2e, 0xe1,
+ 0x04, 0x98, 0xd5, 0x5d, 0x7c, 0xad, 0x5d, 0xb5, 0xe6, 0x52, 0x06, 0x34,
+ 0x39, 0x31, 0x1e, 0x04
+};
+static const uint8_t hmac_short_output[] = {
+ 0xcd, 0xea, 0xcf, 0xce, 0xbf, 0x46, 0xcc, 0x9d, 0x7e, 0x4d, 0x41, 0x75,
+ 0xe5, 0xd8, 0xd2, 0x67, 0xc2, 0x3a, 0x64, 0xcd, 0xe8, 0x3e, 0x86, 0x7e,
+ 0x50, 0x01, 0xec, 0xf2, 0x6f, 0xbd, 0x30, 0xd2
+};
+
+/* HMAC medium key (64 bytes) from NIST FIPS 198-1 (Count = 120) */
+static const uint8_t hmac_medium_msg[] = {
+ 0xed, 0x4f, 0x26, 0x9a, 0x88, 0x51, 0xeb, 0x31, 0x54, 0x77, 0x15, 0x16,
+ 0xb2, 0x72, 0x28, 0x15, 0x52, 0x00, 0x77, 0x80, 0x49, 0xb2, 0xdc, 0x19,
+ 0x63, 0xf3, 0xac, 0x32, 0xba, 0x46, 0xea, 0x13, 0x87, 0xcf, 0xbb, 0x9c,
+ 0x39, 0x15, 0x1a, 0x2c, 0xc4, 0x06, 0xcd, 0xc1, 0x3c, 0x3c, 0x98, 0x60,
+ 0xa2, 0x7e, 0xb0, 0xb7, 0xfe, 0x8a, 0x72, 0x01, 0xad, 0x11, 0x55, 0x2a,
+ 0xfd, 0x04, 0x1e, 0x33, 0xf7, 0x0e, 0x53, 0xd9, 0x7c, 0x62, 0xf1, 0x71,
+ 0x94, 0xb6, 0x61, 0x17, 0x02, 0x8f, 0xa9, 0x07, 0x1c, 0xc0, 0xe0, 0x4b,
+ 0xd9, 0x2d, 0xe4, 0x97, 0x2c, 0xd5, 0x4f, 0x71, 0x90, 0x10, 0xa6, 0x94,
+ 0xe4, 0x14, 0xd4, 0x97, 0x7a, 0xbe, 0xd7, 0xca, 0x6b, 0x90, 0xba, 0x61,
+ 0x2d, 0xf6, 0xc3, 0xd4, 0x67, 0xcd, 0xed, 0x85, 0x03, 0x25, 0x98, 0xa4,
+ 0x85, 0x46, 0x80, 0x4f, 0x9c, 0xf2, 0xec, 0xfe
+};
+static const uint8_t hmac_medium_key[] = {
+ 0x99, 0x28, 0x68, 0x50, 0x4d, 0x25, 0x64, 0xc4, 0xfb, 0x47, 0xbc, 0xbd,
+ 0x4a, 0xe4, 0x82, 0xd8, 0xfb, 0x0e, 0x8e, 0x56, 0xd7, 0xb8, 0x18, 0x64,
+ 0xe6, 0x19, 0x86, 0xa0, 0xe2, 0x56, 0x82, 0xda, 0xeb, 0x5b, 0x50, 0x17,
+ 0x7c, 0x09, 0x5e, 0xdc, 0x9e, 0x97, 0x1d, 0xa9, 0x5c, 0x32, 0x10, 0xc3,
+ 0x76, 0xe7, 0x23, 0x36, 0x5a, 0xc3, 0x3d, 0x1b, 0x4f, 0x39, 0x18, 0x17,
+ 0xf4, 0xc3, 0x51, 0x24
+};
+static const uint8_t hmac_medium_output[] = {
+ 0x2f, 0x83, 0x21, 0xf4, 0x16, 0xb9, 0xbb, 0x24, 0x9f, 0x11, 0x3b, 0x13,
+ 0xfc, 0x12, 0xd7, 0x0e, 0x16, 0x68, 0xdc, 0x33, 0x28, 0x39, 0xc1, 0x0d,
+ 0xaa, 0x57, 0x17, 0x89, 0x6c, 0xb7, 0x0d, 0xdf
+};
+
+static int test_sha256(const uint8_t *input, int input_len,
+ const uint8_t *output)
+{
+ struct sha256_ctx ctx;
+ uint8_t *tmp;
+ int i;
+
+ /* Basic test */
+ SHA256_init(&ctx);
+ SHA256_update(&ctx, input, input_len);
+ tmp = SHA256_final(&ctx);
+
+ if (memcmp(tmp, output, SHA256_DIGEST_SIZE) != 0) {
+ ccprintf("SHA256 test failed\n");
+ return 0;
+ }
+
+ /* Splitting the input string in chunks of 1 byte also works. */
+ SHA256_init(&ctx);
+ for (i = 0; i < input_len; i++)
+ SHA256_update(&ctx, &input[i], 1);
+ tmp = SHA256_final(&ctx);
+
+ if (memcmp(tmp, output, SHA256_DIGEST_SIZE) != 0) {
+ ccprintf("SHA256 test failed (1-byte chunks)\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int test_hmac(const uint8_t *key, int key_len,
+ const uint8_t *input, int input_len,
+ const uint8_t *output)
+{
+ uint8_t tmp[SHA256_DIGEST_SIZE];
+
+ hmac_SHA256(tmp, key, key_len, input, input_len);
+
+ if (memcmp(tmp, output, sizeof(output)) != 0) {
+ ccprintf("hmac_SHA256 test failed\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+void run_test(void)
+{
+ ccprintf("Testing short message (8 bytes)\n");
+ if (!test_sha256(sha256_8_input, sizeof(sha256_8_input),
+ sha256_8_output)) {
+ test_fail();
+ return;
+ }
+
+ ccprintf("Testing short message (72 bytes)\n");
+ if (!test_sha256(sha256_72_input, sizeof(sha256_72_input),
+ sha256_72_output)) {
+ test_fail();
+ return;
+ }
+
+ ccprintf("Testing long message (2888 bytes)\n");
+ if (!test_sha256(sha256_2888_input, sizeof(sha256_2888_input),
+ sha256_2888_output)) {
+ test_fail();
+ return;
+ }
+
+ ccprintf("HMAC: Testing short key\n");
+ if (!test_hmac(hmac_short_key, sizeof(hmac_short_key),
+ hmac_short_msg, sizeof(hmac_short_msg),
+ hmac_short_output)) {
+ test_fail();
+ return;
+ }
+
+ ccprintf("HMAC: Testing medium key\n");
+ if (!test_hmac(hmac_medium_key, sizeof(hmac_medium_key),
+ hmac_medium_msg, sizeof(hmac_medium_msg),
+ hmac_medium_output)) {
+ test_fail();
+ return;
+ }
+
+ /*
+ * Note: Our HMAC implementation does not support longer than
+ * 64 bytes keys.
+ */
+
+ test_pass();
+}
diff --git a/test/sha256.tasklist b/test/sha256.tasklist
new file mode 100644
index 0000000000..e76178ba0a
--- /dev/null
+++ b/test/sha256.tasklist
@@ -0,0 +1,17 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ *
+ * For each task, use the macro TASK_TEST(n, r, d, s) where :
+ * 'n' in the name of the task
+ * 'r' in the main routine of the task
+ * 'd' in an opaque parameter passed to the routine at startup
+ * 's' is the stack size in bytes; must be a multiple of 8
+ */
+#define CONFIG_TEST_TASK_LIST
diff --git a/test/sha256_unrolled.tasklist b/test/sha256_unrolled.tasklist
new file mode 120000
index 0000000000..06d8620957
--- /dev/null
+++ b/test/sha256_unrolled.tasklist
@@ -0,0 +1 @@
+sha256.tasklist \ No newline at end of file
diff --git a/test/shmalloc.c b/test/shmalloc.c
new file mode 100644
index 0000000000..d96579c275
--- /dev/null
+++ b/test/shmalloc.c
@@ -0,0 +1,305 @@
+/*
+ * Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "common.h"
+#include "compile_time_macros.h"
+#include "console.h"
+#include "link_defs.h"
+#include "shared_mem.h"
+#include "test_util.h"
+
+/*
+ * Total size of memory in the malloc pool (shared between free and allocated
+ * buffers.
+ */
+static int total_size;
+
+/*
+ * Number of randomized allocation/free attempts, large enough to execute all
+ * branches in the malloc/free module.
+ */
+static int counter = 500000;
+
+/*
+ * A good random number generator approximation. Guaranteed to generate the
+ * same sequence on all test runs.
+ */
+static uint32_t next = 127;
+static uint32_t myrand(void)
+{
+ next = next * 1103515245 + 12345;
+ return ((uint32_t)(next/65536) % 32768);
+}
+
+/* Keep track of buffers allocated by the test function. */
+static struct {
+ void *buf;
+ size_t buffer_size;
+} allocations[12]; /* Up to 12 buffers could be allocated concurrently. */
+
+/*
+ * Verify that allocated and free buffers do not overlap, and that our and
+ * malloc's ideas of the number of allocated buffers match.
+ */
+
+static int check_for_overlaps(void)
+{
+ int i;
+ int allocation_match;
+ int allocations_count, allocated_count;
+
+ allocations_count = allocated_count = 0;
+ for (i = 0; i < ARRAY_SIZE(allocations); i++) {
+ struct shm_buffer *allocced_buf;
+
+ if (!allocations[i].buf)
+ continue;
+
+ /*
+ * Indication of finding the allocated buffer in internal
+ * malloc structures.
+ */
+ allocation_match = 0;
+
+ /* number of buffers allocated by the test program. */
+ allocations_count++;
+
+ /*
+ * Number of allocated buffers malloc knows about, calculated
+ * multiple times to keep things simple.
+ */
+ allocated_count = 0;
+ for (allocced_buf = allocced_buf_chain;
+ allocced_buf;
+ allocced_buf = allocced_buf->next_buffer) {
+ int allocated_size, allocation_size;
+
+ allocated_count++;
+ if (allocations[i].buf != (allocced_buf + 1))
+ continue;
+
+ allocated_size = allocced_buf->buffer_size;
+ allocation_size = allocations[i].buffer_size;
+
+ /*
+ * Verify that size requested by the allocator matches
+ * the value used by malloc, i.e. does not exceed the
+ * allocated size and is no less than two buffer
+ * structures lower (which can happen when the
+ * requested size was rounded up to cover gaps smaller
+ * than the buffer header structure size).
+ */
+ if ((allocation_size > allocated_size) ||
+ ((allocated_size - allocation_size) >=
+ (2 * sizeof(struct shm_buffer) + sizeof(int)))) {
+ ccprintf("inconsistency: allocated (size %d)"
+ " allocation %d(size %d)\n",
+ allocated_size, i, allocation_size);
+ return 0;
+ }
+
+ if (allocation_match++) {
+ ccprintf("inconsistency: duplicated match\n");
+ return 0;
+ }
+ }
+ if (!allocation_match) {
+ ccprintf("missing match %p!\n", allocations[i].buf);
+ return 0;
+ }
+ }
+ if (allocations_count != allocated_count) {
+ ccprintf("count mismatch (%d != %d)!\n",
+ allocations_count, allocated_count);
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Verify that shared memory is in a consistent state, i.e. that there is no
+ * overlaps between allocated and free buffers, and that all memory is
+ * accounted for (is either allocated or available).
+ */
+
+static int shmem_is_ok(int line)
+{
+ int count = 0;
+ int running_size = 0;
+ struct shm_buffer *pbuf = free_buf_chain;
+
+ if (pbuf && pbuf->prev_buffer) {
+ ccprintf("Bad free buffer list start %p\n", pbuf);
+ goto bailout;
+ }
+
+ while (pbuf) {
+ struct shm_buffer *top;
+
+ running_size += pbuf->buffer_size;
+ if (count++ > 100)
+ goto bailout; /* Is there a loop? */
+
+ top = (struct shm_buffer *)((uintptr_t)pbuf +
+ pbuf->buffer_size);
+ if (pbuf->next_buffer) {
+ if (top >= pbuf->next_buffer) {
+ ccprintf("%s:%d"
+ " - inconsistent buffer size at %p\n",
+ __func__, __LINE__, pbuf);
+ goto bailout;
+ }
+ if (pbuf->next_buffer->prev_buffer != pbuf) {
+ ccprintf("%s:%d"
+ " - inconsistent next buffer at %p\n",
+ __func__, __LINE__, pbuf);
+ goto bailout;
+ }
+ }
+ pbuf = pbuf->next_buffer;
+ }
+
+ if (pbuf) { /* Must be a loop. */
+ ccprintf("Too many buffers in the chain\n");
+ goto bailout;
+ }
+
+ /* Make sure there were at least 5 buffers allocated at one point. */
+ if (count > 5)
+ set_map_bit(1 << 24);
+
+ /* Add allocated sizes. */
+ for (pbuf = allocced_buf_chain; pbuf; pbuf = pbuf->next_buffer)
+ running_size += pbuf->buffer_size;
+
+ if (total_size) {
+ if (total_size != running_size)
+ goto bailout;
+ } else {
+ /* Remember total size for future reference. */
+ total_size = running_size;
+ }
+
+ if (!check_for_overlaps())
+ goto bailout;
+
+ return 1;
+
+ bailout:
+ ccprintf("Line %d, counter %d. The list has been corrupted, "
+ "total size %d, running size %d\n",
+ line, counter, total_size, running_size);
+ return 0;
+}
+
+/*
+ * Bitmap used to keep track of branches taken by malloc/free routines. Once
+ * all bits in the 0..(MAX_MASK_BIT - 1) range are set, consider the test
+ * completed.
+ */
+static uint32_t test_map;
+
+void run_test(void)
+{
+ int index;
+ const int shmem_size = shared_mem_size();
+
+ while (counter--) {
+ char *shptr;
+ uint32_t r_data;
+
+ r_data = myrand();
+
+ if (!(counter % 50000))
+ ccprintf("%d\n", counter);
+
+ /*
+ * If all bits we care about are set in the map - the test is
+ * over.
+ */
+ if ((test_map & ALL_PATHS_MASK) == ALL_PATHS_MASK) {
+ if (test_map & ~ALL_PATHS_MASK) {
+ ccprintf("Unexpected mask bits set: %x"
+ ", counter %d\n",
+ test_map & ~ALL_PATHS_MASK,
+ counter);
+ test_fail();
+ return;
+ }
+ ccprintf("Done testing, counter at %d\n", counter);
+ test_pass();
+ return;
+ }
+
+ /* Pick a random allocation entry. */
+ index = r_data % ARRAY_SIZE(allocations);
+ if (allocations[index].buf) {
+ /*
+ * If there is a buffer associated with the entry -
+ * release it.
+ */
+ shared_mem_release(allocations[index].buf);
+ allocations[index].buf = 0;
+ if (!shmem_is_ok(__LINE__)) {
+ test_fail();
+ return;
+ }
+ } else {
+ size_t alloc_size = r_data % (shmem_size);
+
+ /*
+ * If the allocation entry is empty - allocate a
+ * buffer of a random size up to max shared memory.
+ */
+ if (shared_mem_acquire(alloc_size, &shptr) ==
+ EC_SUCCESS) {
+ allocations[index].buf = (void *) shptr;
+ allocations[index].buffer_size = alloc_size;
+
+ /*
+ * Make sure every allocated byte is
+ * modified.
+ */
+ while (alloc_size--)
+ shptr[alloc_size] =
+ shptr[alloc_size] ^ 0xff;
+
+ if (!shmem_is_ok(__LINE__)) {
+ test_fail();
+ return;
+ }
+ }
+ }
+ }
+
+ /*
+ * The test is over, free all still allcated buffers, if any. Keep
+ * verifying memory consistency after each free() invocation.
+ */
+ for (index = 0; index < ARRAY_SIZE(allocations); index++)
+ if (allocations[index].buf) {
+ shared_mem_release(allocations[index].buf);
+ allocations[index].buf = NULL;
+ if (!shmem_is_ok(__LINE__)) {
+ test_fail();
+ return;
+ }
+ }
+
+ ccprintf("Did not pass all paths, map %x != %x\n",
+ test_map, ALL_PATHS_MASK);
+ test_fail();
+}
+
+void set_map_bit(uint32_t mask)
+{
+ test_map |= mask;
+}
diff --git a/test/shmalloc.tasklist b/test/shmalloc.tasklist
new file mode 100644
index 0000000000..ab483e513f
--- /dev/null
+++ b/test/shmalloc.tasklist
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ *
+ * For each task, use the macro TASK_TEST(n, r, d, s) where :
+ * 'n' in the name of the task
+ * 'r' in the main routine of the task
+ * 'd' in an opaque parameter passed to the routine at startup
+ * 's' is the stack size in bytes; must be a multiple of 8
+ */
+#define CONFIG_TEST_TASK_LIST
+
diff --git a/test/stress.c b/test/stress.c
index 18e15eafb9..4cadcadb53 100644
--- a/test/stress.c
+++ b/test/stress.c
@@ -30,11 +30,8 @@ struct i2c_test_param_t {
int addr;
int offset;
int data; /* Non-negative represents data to write. -1 to read. */
-} i2c_test_params[] = {
-#if defined(BOARD_PIT)
- {8, 0, 0x90, 0x19, -1},
-#endif
-};
+} i2c_test_params[];
+
/* Disable I2C test for boards without test configuration */
#if defined(BOARD_BDS) || defined(BOARD_AURON)
#undef CONFIG_I2C
diff --git a/test/test_config.h b/test/test_config.h
index 371303b4cb..f438c1d112 100644
--- a/test/test_config.h
+++ b/test/test_config.h
@@ -11,10 +11,17 @@
/* Test config flags only apply for test builds */
#ifdef TEST_BUILD
+/* Host commands are sorted. */
+#define CONFIG_HOSTCMD_SECTION_SORTED
+
/* Don't compile features unless specifically testing for them */
#undef CONFIG_VBOOT_HASH
#undef CONFIG_USB_PD_LOGGING
+#ifdef TEST_BASE32
+#define CONFIG_BASE32
+#endif
+
#ifdef TEST_BKLIGHT_LID
#define CONFIG_BACKLIGHT_LID
#endif
@@ -48,25 +55,63 @@
#define CONFIG_LID_ANGLE_TABLET_MODE
#define CONFIG_LID_ANGLE_SENSOR_BASE 0
#define CONFIG_LID_ANGLE_SENSOR_LID 1
+#define CONFIG_TABLET_MODE
#endif
-#ifdef TEST_SBS_CHARGING
-#define CONFIG_BATTERY_MOCK
-#define CONFIG_BATTERY_SMART
-#define CONFIG_CHARGER
-#define CONFIG_CHARGER_V1
-#define CONFIG_CHARGER_INPUT_CURRENT 4032
-#define CONFIG_CHARGER_DISCHARGE_ON_AC
-#define CONFIG_CHARGER_DISCHARGE_ON_AC_CUSTOM
-#define CONFIG_I2C
-#define CONFIG_I2C_MASTER
-int board_discharge_on_ac(int enabled);
-#define I2C_PORT_MASTER 0
-#define I2C_PORT_BATTERY 0
-#define I2C_PORT_CHARGER 0
+#ifdef TEST_RMA_AUTH
+
+/* Test server public and private keys */
+#define RMA_TEST_SERVER_PUBLIC_KEY { \
+ 0x03, 0xae, 0x2d, 0x2c, 0x06, 0x23, 0xe0, 0x73, \
+ 0x0d, 0xd3, 0xb7, 0x92, 0xac, 0x54, 0xc5, 0xfd, \
+ 0x7e, 0x9c, 0xf0, 0xa8, 0xeb, 0x7e, 0x2a, 0xb5, \
+ 0xdb, 0xf4, 0x79, 0x5f, 0x8a, 0x0f, 0x28, 0x3f}
+#define RMA_TEST_SERVER_PRIVATE_KEY { \
+ 0x47, 0x3b, 0xa5, 0xdb, 0xc4, 0xbb, 0xd6, 0x77, \
+ 0x20, 0xbd, 0xd8, 0xbd, 0xc8, 0x7a, 0xbb, 0x07, \
+ 0x03, 0x79, 0xba, 0x7b, 0x52, 0x8c, 0xec, 0xb3, \
+ 0x4d, 0xaa, 0x69, 0xf5, 0x65, 0xb4, 0x31, 0xad}
+#define RMA_TEST_SERVER_KEY_ID 0x10
+
+#define CONFIG_BASE32
+#define CONFIG_CURVE25519
+#define CONFIG_RMA_AUTH
+#define CONFIG_RMA_AUTH_SERVER_PUBLIC_KEY RMA_TEST_SERVER_PUBLIC_KEY
+#define CONFIG_RMA_AUTH_SERVER_KEY_ID RMA_TEST_SERVER_KEY_ID
+#define CONFIG_RNG
+#define CONFIG_SHA256
+#define CC_EXTENSION CC_COMMAND
+
+#endif
+
+#ifdef TEST_RSA
+#define CONFIG_RSA
+#define CONFIG_RSA_KEY_SIZE 2048
+#define CONFIG_RWSIG_TYPE_RWSIG
+#endif
+
+#ifdef TEST_RSA3
+#define CONFIG_RSA
+#define CONFIG_RSA_KEY_SIZE 2048
+#define CONFIG_RSA_EXPONENT_3
+#define CONFIG_RWSIG_TYPE_RWSIG
+#endif
+
+#ifdef TEST_SHA256
+#define CONFIG_SHA256
+#endif
+
+#ifdef TEST_SHA256_UNROLLED
+#define CONFIG_SHA256
+#define CONFIG_SHA256_UNROLLED
+#endif
+
+#ifdef TEST_SHMALLOC
+#define CONFIG_MALLOC
#endif
#ifdef TEST_SBS_CHARGING_V2
+#define CONFIG_BATTERY
#define CONFIG_BATTERY_MOCK
#define CONFIG_BATTERY_SMART
#define CONFIG_CHARGER
@@ -122,7 +167,8 @@ int ncp15wb_calculate_temp(uint16_t adc);
#define CONFIG_ALS_LIGHTBAR_DIMMING 0
#endif
-#ifdef TEST_USB_PD
+#if defined(TEST_USB_PD) || defined(TEST_USB_PD_GIVEBACK) || \
+ defined(TEST_USB_PD_REV30)
#define CONFIG_USB_POWER_DELIVERY
#define CONFIG_USB_PD_CUSTOM_VDM
#define CONFIG_USB_PD_DUAL_ROLE
@@ -131,24 +177,34 @@ int ncp15wb_calculate_temp(uint16_t adc);
#define CONFIG_USB_PD_TCPM_STUB
#define CONFIG_SHA256
#define CONFIG_SW_CRC
+#ifdef TEST_USB_PD_REV30
+#define CONFIG_USB_PD_REV30
+#define CONFIG_USB_PID 0x5000
+#endif
+#ifdef TEST_USB_PD_GIVEBACK
+#define CONFIG_USB_PD_GIVE_BACK
#endif
+#endif /* TEST_USB_PD || TEST_USB_PD_GIVEBACK || TEST_USB_PD_REV30 */
-#ifdef TEST_CHARGE_MANAGER
+#if defined(TEST_CHARGE_MANAGER) || defined(TEST_CHARGE_MANAGER_DRP_CHARGING)
#define CONFIG_CHARGE_MANAGER
-#undef CONFIG_CHARGE_MANAGER_DRP_CHARGING
#define CONFIG_USB_PD_DUAL_ROLE
#define CONFIG_USB_PD_PORT_COUNT 2
-#endif
+#define CONFIG_BATTERY
+#define CONFIG_BATTERY_SMART
+#define CONFIG_I2C
+#define CONFIG_I2C_MASTER
+#define I2C_PORT_BATTERY 0
+#endif /* TEST_CHARGE_MANAGER_* */
#ifdef TEST_CHARGE_MANAGER_DRP_CHARGING
-#define CONFIG_CHARGE_MANAGER
#define CONFIG_CHARGE_MANAGER_DRP_CHARGING
-#define CONFIG_USB_PD_DUAL_ROLE
-#define CONFIG_USB_PD_PORT_COUNT 2
-#endif
+#else
+#undef CONFIG_CHARGE_MANAGER_DRP_CHARGING
+#endif /* TEST_CHARGE_MANAGER_DRP_CHARGING */
#ifdef TEST_CHARGE_RAMP
-#define CONFIG_CHARGE_RAMP
+#define CONFIG_CHARGE_RAMP_SW
#define CONFIG_USB_PD_PORT_COUNT 2
#endif
@@ -182,10 +238,55 @@ enum nvmem_users {
#endif
#endif
+#ifdef TEST_NVMEM_VARS
+#define NVMEM_PARTITION_SIZE 0x3000
+#define CONFIG_FLASH_NVMEM_VARS
#ifndef __ASSEMBLER__
-/* Callback function from charge_manager to send host event */
-static inline void pd_send_host_event(int mask) { }
+/* Define the user region numbers */
+enum nvmem_users {
+ CONFIG_FLASH_NVMEM_VARS_USER_NUM,
+ NVMEM_NUM_USERS
+};
+/* Define a test var. */
+enum nvmem_vars {
+ NVMEM_VAR_TEST_VAR,
+};
+#endif
+#define CONFIG_FLASH_NVMEM_VARS_USER_SIZE 600
+#endif /* TEST_NVMEM_VARS */
+
+#ifdef TEST_PINWEAVER
+#define CONFIG_PINWEAVER
+#define CONFIG_SHA256
+#endif
+
+#ifdef TEST_RTC
+#define CONFIG_HOSTCMD_RTC
#endif
+#ifdef TEST_VBOOT
+#define CONFIG_RWSIG
+#define CONFIG_SHA256
+#define CONFIG_RSA
+#define CONFIG_RWSIG_TYPE_RWSIG
+#define CONFIG_RW_B
+#define CONFIG_RW_B_MEM_OFF CONFIG_RO_MEM_OFF
+#undef CONFIG_RO_SIZE
+#define CONFIG_RO_SIZE (CONFIG_FLASH_SIZE / 4)
+#undef CONFIG_RW_SIZE
+#define CONFIG_RW_SIZE CONFIG_RO_SIZE
+#define CONFIG_RW_A_STORAGE_OFF CONFIG_RW_STORAGE_OFF
+#define CONFIG_RW_B_STORAGE_OFF (CONFIG_RW_A_STORAGE_OFF + \
+ CONFIG_RW_SIZE)
+#define CONFIG_RW_A_SIGN_STORAGE_OFF (CONFIG_RW_A_STORAGE_OFF + \
+ CONFIG_RW_SIZE - CONFIG_RW_SIG_SIZE)
+#define CONFIG_RW_B_SIGN_STORAGE_OFF (CONFIG_RW_B_STORAGE_OFF + \
+ CONFIG_RW_SIZE - CONFIG_RW_SIG_SIZE)
+#endif
+
+#ifdef TEST_X25519
+#define CONFIG_CURVE25519
+#endif /* TEST_X25519 */
+
#endif /* TEST_BUILD */
#endif /* __TEST_TEST_CONFIG_H */
diff --git a/test/tpm_test/Makefile b/test/tpm_test/Makefile
index 164b77e083..eeed4c2650 100644
--- a/test/tpm_test/Makefile
+++ b/test/tpm_test/Makefile
@@ -2,11 +2,19 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+# V unset for normal output, V=1 for verbose output, V=0 for silent build
+# (warnings/errors only). Use echo thus: $(call echo,"stuff to echo")
+ifeq ($(V),0)
+Q := @
+echo = echo -n;
+else
+echo = echo $(1);
ifeq ($(V),)
Q := @
else
Q :=
endif
+endif
obj = ../../build/tpm_test
src = .
@@ -17,7 +25,12 @@ vpath %c $(src) ../../chip/g/dcrypto $(src)/testlib
CFLAGS = -fPIC
CFLAGS += -I /usr/include/python2.7
CFLAGS += -I../../../../third_party/cryptoc/include
+CFLAGS += -I../../board/cr50
+CFLAGS += -I../../chip/g
CFLAGS += -I../../chip/g/dcrypto
+CFLAGS += -I../../include
+CFLAGS += -I..
+CFLAGS += -I../..
CFLAGS += -I.
CFLAGS += -Itestlib
CFLAGS += -DLIBFTDI1=1
@@ -39,31 +52,31 @@ DEPS := $(OBJS:.o=.o.d) $(BN_OBJS:.o=.o.d)
$(OBJS) $(BN_OBJS): | $(obj)
$(obj)/%.o: $(obj)/%.c
- @echo " CC $(notdir $@)"
+ $(call echo," CC $(notdir $@)")
$(Q)gcc $(CFLAGS) -o $@ $<
$(obj)/%.o: %.c
- @echo " CC $(notdir $@)"
+ $(call echo," CC $(notdir $@)")
$(Q)gcc $(CFLAGS) -Wall -Werror -MMD -MF $@.d -o $@ $<
$(obj)/_$(TARGET).so: $(OBJS) $(obj)/$(TARGET).py
- @echo " LD $(notdir $@)"
+ $(call echo," LD $(notdir $@)")
$(Q)rm -f $@
$(Q)gcc -shared $(OBJS) -lftdi1 -o $@
$(obj)/%_wrap.c: $(src)/%.i
- @echo " SWIG $(notdir $@)"
+ $(call echo," SWIG $(notdir $@)")
$(Q)swig -python -outdir $(obj) -o $@ $<
clean:
@rm -rf $(obj)/
$(obj):
- @echo " MKDIR $(obj)"
+ $(call echo," MKDIR $(obj)")
$(Q)mkdir -p $(obj)
$(obj)/bn_test: $(BN_OBJS)
- @echo " LD $(notdir $@)"
+ $(call echo," LD $(notdir $@)")
$(Q)$(CC) -o $@ $^ -lcrypto
-include $(DEPS)
diff --git a/test/tpm_test/bn_test.c b/test/tpm_test/bn_test.c
index c0eda9f450..9daf1bae64 100644
--- a/test/tpm_test/bn_test.c
+++ b/test/tpm_test/bn_test.c
@@ -114,6 +114,16 @@ fail:
return result;
}
+void *always_memset(void *s, int c, size_t n)
+{
+ memset(s, c, n);
+ return s;
+}
+
+void watchdog_reload(void)
+{
+}
+
int main(void)
{
assert(test_bn_modinv() == 0);
diff --git a/test/tpm_test/crypto_test.py b/test/tpm_test/crypto_test.py
index 1c292dcdbc..5790c1ee1c 100644
--- a/test/tpm_test/crypto_test.py
+++ b/test/tpm_test/crypto_test.py
@@ -36,7 +36,7 @@ def get_attribute(tdesc, attr_name, required=True):
"""
# Fields stored in hex format by default.
- default_hex = ('cipher_text', 'iv', 'key')
+ default_hex = ('aad', 'cipher_text', 'iv', 'key', 'tag')
data = tdesc.find(attr_name)
if data is None:
@@ -108,7 +108,7 @@ SUPPORTED_MODES = {
}),
}
-def crypto_run(node_name, op_type, key, iv, in_text, out_text, tpm):
+def crypto_run(node_name, op_type, key, iv, aad, in_text, out_text, tpm):
"""Perform a basic operation(encrypt or decrypt).
This function creates an extended command with the requested parameters,
@@ -125,6 +125,7 @@ def crypto_run(node_name, op_type, key, iv, in_text, out_text, tpm):
directly to the device as a field in the extended command
key: a binary string
iv: a binary string, might be empty
+ aad: additional authenticated data
in_text: a binary string, the input of the encrypt/decrypt operation
out_text: a binary string, might be empty, the expected output of the
operation. Note that it could be shorter than actual output (padded to
@@ -156,6 +157,9 @@ def crypto_run(node_name, op_type, key, iv, in_text, out_text, tpm):
cmd += '%c' % len(iv)
if iv:
cmd += iv
+ cmd += '%c' % len(aad)
+ if aad:
+ cmd += aad
cmd += struct.pack('>H', len(in_text))
cmd += in_text
if tpm.debug_enabled():
@@ -203,18 +207,33 @@ def crypto_test(tdesc, tpm):
node_name,
''.join('%2.2x' % ord(x) for x in key)))
iv = get_attribute(tdesc, 'iv', required=False)
- if iv and len(iv) != 16:
+ if iv and not node_name.startswith('AES:GCM') and len(iv) != 16:
raise subcmd.TpmTestError('wrong iv size "%s:%s"' % (
node_name,
''.join('%2.2x' % ord(x) for x in iv)))
- clear_text = get_attribute(tdesc, 'clear_text')
+ clear_text = get_attribute(tdesc, 'clear_text', required=False)
+ if clear_text:
+ clear_text_len = get_attribute(tdesc, 'clear_text_len', required=False)
+ if clear_text_len:
+ clear_text = clear_text[:int(clear_text_len)]
+ else:
+ clear_text_len = None
if tpm.debug_enabled():
print('clear text size', len(clear_text))
cipher_text = get_attribute(tdesc, 'cipher_text', required=False)
+ if clear_text_len:
+ cipher_text = cipher_text[:int(clear_text_len)]
+ tag = get_attribute(tdesc, 'tag', required=False)
+ aad = get_attribute(tdesc, 'aad', required=False)
+ if aad:
+ aad_len = get_attribute(tdesc, 'aad_len', required=False)
+ if aad_len:
+ aad = aad[:int(aad_len)]
real_cipher_text = crypto_run(node_name, ENCRYPT, key, iv,
- clear_text, cipher_text, tpm)
- crypto_run(node_name, DECRYPT, key, iv, real_cipher_text,
- clear_text, tpm)
+ aad or '', clear_text, cipher_text + tag, tpm)
+ crypto_run(node_name, DECRYPT, key, iv, aad or '',
+ real_cipher_text[:len(real_cipher_text) - len(tag)],
+ clear_text + tag, tpm)
print(utils.cursor_back() + 'SUCCESS: %s' % node_name)
def crypto_tests(tpm, xml_file):
diff --git a/test/tpm_test/crypto_test.xml b/test/tpm_test/crypto_test.xml
index 7cf0758749..152f55c97f 100644
--- a/test/tpm_test/crypto_test.xml
+++ b/test/tpm_test/crypto_test.xml
@@ -39,6 +39,7 @@ Many of the crypto_test elements were borrowed from NIST test vectors.
34b58f68 a9e27607 7bdd8e72 8b2b528b
</cipher_text>
</crypto_test>
+
<crypto_test name="AES:ECB128 1">
<clear_text format="hex">
33221100 77665544 bbaa9988 ffeeddcc
@@ -96,6 +97,75 @@ Many of the crypto_test elements were borrowed from NIST test vectors.
</cipher_text>
<iv>f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc</iv>
</crypto_test>
+
+ <crypto_test name="AES:CTR128I 2">
+ <clear_text format="hex">
+ 8081582f 93b9e22f b62411a3 1cc78eac
+ 43e9160a e450aeee c03fd20f 2c857832
+ </clear_text>
+ <key>
+ 8c6b27c1 b3f18092 f782418c 0f52779d
+ </key>
+ <cipher_text>
+ 0d61fe65 927bb9db 9f9c8cc4 6287a402
+ f530a9cf c892dec2 86cb6ae3 2b54fc89
+ </cipher_text>
+ <iv>
+ 00000000 00000000 00000000 FFFFFFFF
+ </iv>
+ </crypto_test>
+
+ <crypto_test name="AES:CTR128I 3">
+ <clear_text format="hex">
+ d66dc833 7ca1d802 572c5244 a24ae3ab
+ ef87947a f917ccf8 568e4d8d 5a4c46d0
+ </clear_text>
+ <key>
+ 82bee9e5 57d2cc8c fa8796d4 338eff1d
+ </key>
+ <cipher_text>
+ 4ab3cfa4 2866ae63 ea4bbc19 a041774d
+ 3c16e4a3 5b5589f2 ff6e2e94 6ead92ba
+ </cipher_text>
+ <iv>
+ 00000000 00000000 FFFFFFFF FFFFFFFF
+ </iv>
+ </crypto_test>
+
+ <crypto_test name="AES:CTR128I 4">
+ <clear_text format="hex">
+ 50ee7879 ff5eeb9b 8b9bbf8d 75d13193
+ a61b24a3 5b3cd159 1fa0290c 67693d8c
+ </clear_text>
+ <key>
+ ad9af8e4 dfca7c06 d61adf4c a5d845a3
+ </key>
+ <cipher_text>
+ 123b06d0 fdfcc772 a8a96688 29f40ff2
+ 0fcfa412 01fc81ec 15bde846 1ef15d21
+ </cipher_text>
+ <iv>
+ 00000000 FFFFFFFF FFFFFFFF FFFFFFFF
+ </iv>
+ </crypto_test>
+
+ <crypto_test name="AES:CTR128I 5">
+ <clear_text format="hex">
+ 3b0c5276 f93ae7c6 7791b673 c2af23a1
+ c907cb9b 44681b6a dce78a4c f688dcb1
+ </clear_text>
+ <key>
+ f8e5cff3 c5032a29 f1ec8fb9 d01cb31a
+ </key>
+ <cipher_text>
+ 9a74141f 8f8db10e 81e6f51e 84f571a6
+ 72e1d939 4b1ad600 7fd5d973 4497a688
+ </cipher_text>
+ <iv>
+ FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
+ </iv>
+ </crypto_test>
+
<crypto_test name="AES:CTR256I 1">
<clear_text format="hex">
13c31e60 a5895777 04f5a7b7 28d2f3bb
@@ -560,4 +630,270 @@ Many of the crypto_test elements were borrowed from NIST test vectors.
</iv>
</crypto_test>
+ <crypto_test name="AES:GCM128 1">
+ <key>
+ 53c372160082fa7a468a6b4c26d0f0f6
+ </key>
+ <iv>
+ 05
+ </iv>
+ <tag>
+ 21d72a8e745f45f9313db5d88e7ef241
+ </tag>
+ </crypto_test>
+
+ <crypto_test name="AES:GCM128 2">
+ <key>
+ 74b5dd7fd041c2533aedfb3e1c374ec4
+ </key>
+ <iv>
+ 3f3a28eee37555c78748fd3e
+ </iv>
+ <tag>
+ f91d6db396e5d5b97f8b3ef8b31c9752
+ </tag>
+ <clear_text format="hex">
+ b442ded5256c646129bd875ca2b9d362
+ </clear_text>
+ <cipher_text>
+ a5a4cd2c1eb95c410f2a5c13fdb2c978
+ </cipher_text>
+ </crypto_test>
+
+ <crypto_test name="AES:GCM128 3">
+ <key>
+ 5571dcfeb2002d197598dd3cbaebb90d
+ </key>
+ <iv>
+ f5746ba756171a5cb13883a0
+ </iv>
+ <tag>
+ 55c43dd28798d4ceeae817c78429ecbe
+ </tag>
+ <aad>
+ c406d22a557e6e173688a02afa16688859e777fa
+ </aad>
+ <clear_text format="hex">
+ 5b433168f1da578848b113c5b5130d8290c42ca7989aa7bdd820f5a6d1393c76
+ </clear_text>
+ <cipher_text>
+ 0538821592a189da6f1dfc3b564d78878b12d1badedbff4da52fbbef685ec362
+ </cipher_text>
+ </crypto_test>
+
+ <crypto_test name="AES:GCM128 4">
+ <key>
+ 7063be77e2c4718979cbd140eb7fd7e8
+ </key>
+ <iv>
+ 190fe0e001bad7fef397a736
+ </iv>
+ <tag>
+ 8dcc9f2093ed753666719c8e46d99d70
+ </tag>
+ <aad>
+ 1dec437a785a0a9c3365b1a0ab3c21a6
+ </aad>
+ </crypto_test>
+
+ <crypto_test name="AES:GCM128 5">
+ <key>
+ 79e8e399576e683ec585821d2b5ef764
+ </key>
+ <iv>
+ b80addc2a86ada68230d9cad
+ </iv>
+ <tag>
+ 10ba4f3f341faf0eaaadbab0855d99e9
+ </tag>
+ <aad>
+ 2de468b6a84c444ed9fd3cb2d5ed9f5a21a58a17b0904814f53c73936c5222cf47ee17599a8041658c7a86c6fc099339
+ </aad>
+ </crypto_test>
+
+ <crypto_test name="AES:GCM128 6">
+ <key>
+ 8824a2d45c1dddf8d6a7196c4c9617ca
+ </key>
+ <iv>
+ 7f83d5f3041aac22d5d1e025
+ </iv>
+ <tag>
+ f9ac3ef273f8cdd186c526779c6e8248
+ </tag>
+ <aad>
+ 24d4c5f1c6963fb88cb28cad470ed2a05a3b025e
+ </aad>
+ <clear_text format="hex">
+ 6a01437b97648916e67b45fb2241a5d2
+ </clear_text>
+ <cipher_text>
+ ee67bdc2ac5ce9f56eb0e327a8d03130
+ </cipher_text>
+ </crypto_test>
+
+ <crypto_test name="AES:GCM128 7">
+ <key>
+ 635c7cbd562b54b771be0ea088156a33
+ </key>
+ <iv>
+ 231f72878e3c9cbabc1a57a5
+ </iv>
+ <tag>
+ 2267b59c2e250b0d46ff7bb9f80fb0e4
+ </tag>
+ <aad>
+ 7580eca60e37d3a0188959b7483eb9f36251474499b89749a9a67fa84e849f93b7a88a003b4c9f0e28d3191ae743f56bcbfe7b12a517ada1ccfc53ecacecfa266953c7c47daa8e4963ef6a702709004dbae4119b5e3e996c0000d99e
+ </aad>
+ <aad_len>
+ 90
+ </aad_len>
+ <clear_text format="hex">
+ bbdd15de6121201ef69aa7e8f3c65aa5
+ </clear_text>
+ <cipher_text>
+ c028eb4162d7e4fe612397de80bc63c8
+ </cipher_text>,
+ </crypto_test>
+
+ <crypto_test name="AES:GCM128 8">
+ <key>
+ 23deefdff02b12c689b50a37734b800e
+ </key>
+ <iv>
+ 02a8d6920f679099e279de16
+ </iv>
+ <tag>
+ 9982a39d07c5cd680dc3d1941545cd41
+ </tag>
+ <aad>
+ 786db1a2c1e61d250e35ce9142f25e5c
+ </aad>
+ <clear_text format="hex">
+ 8c0a266478e97d2821756ce9000000d0
+ </clear_text>
+ <clear_text_len>
+ 13
+ </clear_text_len>
+ <cipher_text>
+ 94de78bf177c848ab4d44936000000d0
+ </cipher_text>
+ </crypto_test>
+
+ <crypto_test name="AES:GCM128 9">
+ <key>
+ dbb8d3cbce11fbfce60657341a8873cd
+ </key>
+ <iv>
+ 68bb62dc5d9aecd041679d75
+ </iv>
+ <tag>
+ b2215a420c5890ea03349188b54b912b
+ </tag>
+ <aad>
+ 61b64409f39462fe08bb2ac959b2c17e4edc32b068285f0c636ebe1c478f17c25af32643c280cad3e785348ec852b2e5
+ </aad>
+ <aad_len>
+ 48
+ </aad_len>
+ <clear_text format="hex">
+ f53bf8855bd5df982adebfc800000064
+ </clear_text>
+ <clear_text_len>
+ 13
+ </clear_text_len>
+ <cipher_text>
+ 3b6b6f20ecdf32b040839fd3000000b1
+ </cipher_text>
+ </crypto_test>
+
+ <crypto_test name="AES:GCM128 10">
+ <key>
+ 1007719909c0ab5969bdf2e438b39d86
+ </key>
+ <iv>
+ 5ea9a9079c1e82a35132c613
+ </iv>
+ <tag>
+ 11d970781a81547ff1706934410c09de
+ </tag>
+ <clear_text format="hex">
+ 50c34bf56f4fed1fa85efb6d0bdf060182e636d85cb72562e8f622028359b359
+ </clear_text>
+ <cipher_text>
+ 59c156056cf34ef8452b60b10920b1261b6175c7c04db6ff9792cad9016a2ccd
+ </cipher_text>
+ </crypto_test>
+
+ <crypto_test name="AES:GCM128 11">
+ <key>
+ a17cbf8fdd25d52f58621ee9c251fe73
+ </key>
+ <iv>
+ 51ea0b20a190977b5eafadcf
+ </iv>
+ <tag>
+ 7407867cb93283f8bd6bcea727a77202
+ </tag>
+ <aad>
+ 7fc014a4ec0be6e29e40cc9c6f9c899ebb8005e661c807262385f0f71bda9ce61d713a9c09359c1dc9e471176d99b9502e4fd00a10b3d1002a545358ff9fe0960fc82efc2887f88caef094f5a6984fb18b4e23882703f7fb000064b3
+ </aad>
+ <aad_len>
+ 90
+ </aad_len>
+ <clear_text format="hex">
+ 27e6d33963494b7c42160d840aaefae697dae25b554e1cf602ce57bbc4d40319
+ </clear_text>
+ <cipher_text>
+ f78e67fe95ac696f6d3b55db7aa0d5ad51e1c89da39f6afe1662cda16878b836
+ </cipher_text>
+ </crypto_test>
+
+ <crypto_test name="AES:GCM128 12">
+ <key>
+ bb5315b6b9954885d01c75298403f8c5
+ </key>
+ <iv>
+ 99f963885de564ae5774bd0b
+ </iv>
+ <tag>
+ 4a5f9b5dc6c16898d86fbd9c4013f051
+ </tag>
+ <aad>
+ f2b514d9e58cb0d1b39ca53e45725810
+ </aad>
+ <clear_text format="hex">
+ 32111b9beac4d01712cf4379adc6693c36973c2e75c9518c55d1454186eee1ddffcac840975c7af1136ad237f34bee7e006d0969
+ </clear_text>
+ <clear_text_len>
+ 51
+ </clear_text_len>
+ <cipher_text>
+ 63b4faac80258a2b3df11251bc82e085bd49dc89a84f169242d2daa2f2b2a1c3df2f6f695f0279f5a96e143f7de4a37d00654bc3
+ </cipher_text>
+ </crypto_test>
+
+ <crypto_test name="AES:GCM128 13">
+ <key>
+ 2862503caa50f4680c99280936ebe115
+ </key>
+ <iv>
+ 90521d81e7578d76c7b67bd8
+ </iv>
+ <tag>
+ fc5e9bbdcd0880a47d4f3a974438722c
+ </tag>
+ <aad>
+ e0c7e2da2bfdd3a319ca4ec0a07851b184cfb503c2280c890ff215a67f42db8a128c69707887efb2fb93110c7416cdb8
+ </aad>
+ <clear_text format="hex">
+ f8a8d0ed19e93328bfe20f744acfde9edc726cc8ef0c4989aa83697b85fc99af7dc85c6c7c8af96327f31b86a86dea1f00ab156a
+ </clear_text>
+ <clear_text_len>
+ 51
+ </clear_text_len>
+ <cipher_text>
+ b02514a54b3b8d6005ecd446f0da1dca8920dd2bec5405ae841c2afbdc823dc6abb9dd71490b1f1b7cd22adee773512b00a60a00
+ </cipher_text>
+ </crypto_test>
</crypto_tests>
diff --git a/test/tpm_test/hash_test.py b/test/tpm_test/hash_test.py
index 66c5fbf00b..29b2214fc6 100644
--- a/test/tpm_test/hash_test.py
+++ b/test/tpm_test/hash_test.py
@@ -25,7 +25,7 @@ MODE_SHA256 = 1
# A standard empty response to HASH extended commands.
EMPTY_RESPONSE = ''.join('%c' % x for x in (0x80, 0x01, 0x00, 0x00, 0x00, 0x0c,
- 0xba, 0xcc, 0xd0, 0x0a, 0x00, 0x01))
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01))
test_inputs = (
# SHA mode cmd mode handle text
(MODE_SHA1, 'single', 0, 'anything really will work here'),
diff --git a/test/tpm_test/rsa1024.pem b/test/tpm_test/rsa1024.pem
new file mode 100644
index 0000000000..3a349130f0
--- /dev/null
+++ b/test/tpm_test/rsa1024.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDfTq9zRZSYNDB+Jq1Ag/kXIbBOGw1qRM5OPi5yTJffiYo5ECWu
+IEzyOyCypRDdsmtiTqafkkrZhpfMcCA7ajJjyn9Z+1e2qZnp0C4PHNR9i6C9D9LV
+Ox8RtGqUz08KK0Tn+mskkbSCH/Z1tpHFoPYv1f8Qc5s09nqII6lCPKgkkQIDAQAB
+AoGBAJpthfQHqG1hmi+De8jj+3y9tXkuSCa3kpyVb/VndpgGO+qeehBjEhNqRICG
+mpVWb+C6V4x+1Ph9lbixyfiMxm7le6CvoE5OhNeXuVrdMuUr5YCzsr9W/wHc5qZs
+SoEdj+pL7SQI9GevDfL9Nz8xJfruNbDbZhH/SeHl/xvMww4JAkEA+V55ZUNwQINQ
+Crths4d7JI8qA1u1S5SUZ6qY1hRAkDykDW1YMcVC8S0VDufN5j7K2JQ3qkzW8yEu
+pP4deUTXswJBAOU+zUuXxZY5cJc6EKnDNQrWK/USjbLACxxfoAuGg6eQ6fgWkp/O
+E0wU6J5MJO//WCIG+c/9Gbcj+eOz43qbsKsCQFYw7Uyu7pGd0YCkG7Tt0wZj5WWb
+wSIKjPD36jO0dExmaV2quZ0aTXUG3Ax22pgGhB4vvL3EKVeH1JN6sb1EqjkCQC8X
+yqanxABLRnTaicfGASR7wMX0jMVWrDGk90TG2k7W9yluwaowdEhh1zOFouTmiJ1c
+3365mMnFizUapDVwvEcCQGqkxcod+m/s4Zq2a9DAyfN3FD2AtJ7QasveLXvR16Od
+MRa0hOWA8d3hh+eHU8DsJ+csEuKeDui3tV4kY0UTF0Y=
+-----END RSA PRIVATE KEY-----
diff --git a/test/tpm_test/rsa2048.pem b/test/tpm_test/rsa2048.pem
new file mode 100644
index 0000000000..8031296fbd
--- /dev/null
+++ b/test/tpm_test/rsa2048.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAnNdhLkOOFb7Nc5+39YZL45WQXIUZTB0uLO9uH+11Mg8KwXKf
+DHhQopmCU5C+ZCNJdXsM6y1ol9avsaoq3l6b4wYN8qzZ1x9Qbsld67TwwJgjBDBG
+ENzUa1fHMMMG3a9RbkBB+BDeSRhSsxjKSVCoOs22lHvb8S0FzlcLvjhIu8mxdja4
+qMziB1zIe8/P8Pqjxdc6XrL0v+rC7VEWopKcNqaGDiSlZhXnlyJQBP/JTbC8JwVe
+LPfv3F1YoTtgg7eMt9A2bVUuBSNjdEqXN6d4QO8+Zv26brNySiGCHzOtYgzyGtJq
+tafyUWkfOKVXmsWIZ+MRplNPsekHQd7o35OpmQIDAQABAoIBAE6dAh/fSouJvI8U
+4m8VZlpncBl/uUNWaPuq8ybbrd9ufLSj0Ca+86Pcj9908IleyoYxLDOA6ikZOa0y
+nxQglcBAG6OkkffqwTUWh5YKdpYCa6LA043GMk6vi67cQkfBhW5elPJS+ifnIiSU
+62e+HuSCkd5xCrgjGgLnzIIG0iYVVJdSzfU/bca5cDC+xYimsGUWnEyE4npu6ce9
+z0Un/BnGIx0riKJnH8LW06B5+7/qOKjfT7ybju4Et3wA15UaA4J66EG4sa9/8TCJ
+Vm0HEVV53WgPgghczCRHVGiG8fA/UhCt5BYzFgIhYuMvXesiW2S0KSJ0JCmpTGaE
+McqZlfUCgYEAyIBv9i/7SYt3OeI9PR9N+btUBg1xv1SxHqIgft3PIRbpwLqUAtKk
+Lng8+2Sg5+knZCkZdMV3u+FttIMdQ1qAcuw8MsMgLM73uvbGDPRW/d8hVfPiViWm
+s5aknLj9nOyH+toupPYPFOaBIoTnwB3RP+2wutjk6dQYM64pUXl50Q8CgYEAyEEq
+QvFqgawGq9C3wLvGE939Xjx3/sEudvCUwF0kizAN+CrHJngbgVpClq33DqQbLI84
+BgWNmG43ZbQsgOI41XnS6mLyMqx7iJDDTp5T5X7vE7Hj1UHRqRUEPGF0XhoAXIqL
+F9V4rV7gzzVjCpUecL6X8tN4BoqImyfIsrE9itcCgYA0NHzydPvQ4mdgwu9/Aq+i
+3ou6J7X+Q2b6uuwLHGXaD8U9UVdIhOAK2XPHYSQkPijrg2gFZ4UNfly6K4lrCB15
+zti9vuCZyinmnGpk5RnhcD+VybKdC6CkEg06YVBnk460Wira+NZkcsAc5M4Sz7C0
+HIdvnxm7aGYEzswjUqXNMQKBgEA49e7GMdwoaXNM2sGK9vmEJi/EwM8I8XffrDUN
+Kh0hajl+rqPdvSR86AIfBK7DXpupytPTkBeksUuCvwsOgh+klEnrNbWer3eaxag/
+CrT9QntUf7bzBuRtAxDCSGmteRQ0smsQYUVoujx56KuKK1sJJP4RZ9rhLvJjjfAQ
++6W9AoGBAL0hOd3kCVY8kxDQry9M30lPZcftJga2IxTErBex60rA9ifmHLm4iFXt
+/e52/1OZVE4Cf7QFEFeSLiBA+U8I0FdV4VMyrfTpjKNVBIDB7JmskdhQbIPXOZzY
+fC2yxCmVsD6PjMDDlT//ck5AQVrktdDpwBj3OX7JSc31KOMBb8FU
+-----END RSA PRIVATE KEY-----
diff --git a/test/tpm_test/rsa4096.pem b/test/tpm_test/rsa4096.pem
new file mode 100644
index 0000000000..c1f82a2d38
--- /dev/null
+++ b/test/tpm_test/rsa4096.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJJwIBAAKCAgEAtNKrnPpCmjdwKuQth2d/FbYxBgbVWumOqdRta5K5Yzd9nPDK
+CkQZ8HGnTOCQnL5pSpxVGLNCQ9lsZf3RzyAMkjQnRkhYCxPN8WdwAn7nTKnY/UgX
+gPYBDmGwvu+HK3EOZYHBUM64ZdLX+nbEtxPZ7drWn54QslD0/fT57NgzVu/0ItPC
+uknVqQuwGKPZJs0ndmpyGzGsPULo2SbTkLs5ZLvqiQt6tIg56unKGYxC+2kiyo69
+bHO3iKChGaj6lFUgb4C612kSH3SC7K/uKK/N2q0bW1TkK+8MTdGr1wPEpJmgvH6d
+PC80O9Xdau2mGZODvuLUb5L/Vaf3Z+vjRtPnbcAplSu/3bSTu0UB/FMhzp+LkIAJ
+uhpuCIcVtHSj3LvbRfI4IIXDnkpFHnUYBUKGrUfmqu2Imp83o7jHmTXiqI6dLrzy
+ZEjDY08c0EXDQaG+5cKkXUXwA4PJgExo8NHa8VGB1f7E6IrWMpU42Dva94xcawH0
+bhK1tjbj0apJRGrz5nsZffg1yy24YlTPLWazf+A3ZRmC1ogkwjF+H27aSyivXivo
+NLLcx9GVDJTnCaDtqpEvkW/wAN8NRu/SWXtlzqLtiuIe0OGTcn8YeRg188rbAV8J
+SCFF/olE7Qd5aDLY1ougxtz5VolbzjUF6cFKHiQLyHMYGWvt+T6SkvILdSUCAwEA
+AQKCAgBWbFY/edE5WgPPTC2CiPHRk7mMktmIURaxjukZQBBBHnV3/BHkpDXtmLSI
+ZtBXSh6S3XNCkfK68QEBIjYUE9JOUoTu74a9DKMinPiJCNRN7OPb8ofhSDKrB//s
+0hi9p5Rk6YZWs+aoLAS0He3ZPrCrISvxMB/0ygK+GkcVbyPiil8aAjIQzVdEK2Tn
+8e/Ivsb8rtWIr84NnZwipY76nrFItxPamlT0UiO0ZjcEzOf6t348Z8qbOhdfQr6c
+wAm7uY/+Gv2yFPLne81TiKaAZb4ypQftN/6yDNfJncvOwWtL7G1Jig5mhH0nmAjy
+oVEA6mNOaaV1CkHlU5lI3xJKeN8j5DWPVu8+12gCjQgasBwo9aEMgNZgu+GYJEt5
+YnNZNz0aYcwajRGrOnrGQBMdlj97ZGgJQableFqWxjxx2TnUuhr6jj62fyMB5FBh
+zVeFesLIvoobRXxYC7vpe1oyfy+hJjTrmDlhQiBLpX4zoAJG3qgqtuQPWrM3dkjT
+yESNSMoLIL2WA2XclWWlH/nQDYGB3HzJtJeg+M6+EDRxTL+pgTgsWxDIkizSiI1c
+Ums7slNfSrNlPPt8hCqyI+Hm+CjgSSXM8//mZjgWI0Req3Pj6aiYeCMJbSyNTJK1
+J4S4MNcLmG0c4rxd1SH9b3S9cIesKLibkl9X23pF8jW7xXxDZQKCAQEA596OJByw
+BAqoj/Vor3SOdnpTmXLpqNAT4YsmA4futyySPGuVaP4dypGrZhu6OTrnuQaUxcad
+3bbnpZeM6UQKoEbN7egCruWSx6h9wi3qzdXjm1OiLnTsPzKGm5U3nvp1CIZCUoay
+ceNWWeDcNTmZupnjKkG8RUpdqAy5pytji81L2y8eT38EbHLPVN5QpaJ9NS1TzuFF
+qZeOD3CbkxfNp1fy39dVq5Zx3n8Eg3vi6LvHWTYY6i4eG5HP5adb/CnT2MO7T//P
+qRb0ERIBmR+R+BTkBXQFUc65mTuV4XCxmvA4Nz8ydk5+4Gh6s5PZwiazeDH87Eup
+awv2b/pZsxr8twKCAQEAx6Qj7vE5MRSFA193+OnLgp4pikCosEzv+xcXmaYeuVmi
+H3RSexwM9QwtPy2cd9vIAsnG+o1aUTkU7I+MO4ZFLHp7DNVuXDS3hHPjYYx5GC87
+7NsTP3+fXNCJDsb5lO812BkTEijNcokazim9vyJxNL/2GDmYNPL1KcN0KTMhskdi
+3vBscyQ+lYMcqL6fi25mNL1F+JKIA7dT7yoEnvx9xqJPmvlktogILmfoowfdpi5k
+h6FVq40XyLpBQ/M2GiDmL4PNSLSAFnVhab8mh2ULGrMSGy/a6BEHz7qa18mjGseO
+NXL4FlVyfdsv3CWlitQqoiKUnwHnHeuNr28W6vh5AwKCAQAr4be+59r7+NRr4kL8
+qa9ohsAZk2DbPP32OnJoSqqH6hyG6MlvBGC4/JaWjXrR5+8A2lj/kRZBZqMyeJsH
+boQgTyYb90PCu9nqhV2/iRcd+3PG6q4P4rrvPu2wti2/naDWiyo0Gh/dY+vsuJyU
+SiFo6kTOs4AhEPDmo/nixFhjlefcRG+VFfHNYHESm7xhjH3ruXdZ+NJJRVByZZpb
+3S5jlEZ3zHX/Mkq8lAdTpveLmjYhERboAvBZwV+6E9FZyMS6ClkBy+UOGDT6ohDB
+XPMwIywASDPVhq0jbd5wuvYx33KUKhavwy1J5RwLrliQ4OgoQDWgtrUKeEocaSHe
+vqXDAoIBAEYG4UPTAUih9fY06pQ5DdWHPPLtsz4D/rmIZBLVHjnNovx9hOEB+dmK
+p+RdT2ELiqDPvifspR2QdDJ2N645btInNDpQMyHMrAKd08hHycId71spjRrc3T1l
+OG4ihTEkpzJhuTrJbScbyHdAVPpSTns+Skg9C5KnFi/MC1bYRJ2QRLIGi0PoFrvC
+/a6DDtuNofQl4AFNBMCo8ZwWlQBfeI7QKDQn/pe4J4Z/lC57d9futfyNLsu59fnG
+u1XmXbfUimloRf2Wssct7Tl2f0FGxBpdbaBzrMlyD9dhkSbX54phLRS6eyL6Xeqf
+k64Y1nRX74xnrNIJjNQF5/D9eoB5H5kCggEADkSUsvqv/hpMMNRIyXsIKIko4EW3
+L0ceh7Unl1vvekku7aLMrHqzu6tu6QoUXc9jMxhS9et41a7NqeLH4MFEqENoxQ6G
+tJTCuklNgKWiooQIN/Wm0t82a3M3kvcqIbCCG5EGDL/7Uh48DQGpSOGehEn6TYnG
+W42bU/Beq38LQzN4eQPhsiqmOwNJ4dYc4NqpOIkXi6UTCe9hDcpX2p7hG55zkpOA
+2rxpIOeOVSRHHOE8RsfIr8FzO/823P1mOQe5xMSCf1GSj6I+bopT0chL9JsiHLZa
+1VPn744OJjXt14LAt4byOVvgdSoQAgxWEC0LhmHYNXt8/YRMJiZ01bc1dA==
+-----END RSA PRIVATE KEY-----
diff --git a/test/tpm_test/rsa768.pem b/test/tpm_test/rsa768.pem
new file mode 100644
index 0000000000..5be0fdf5f7
--- /dev/null
+++ b/test/tpm_test/rsa768.pem
@@ -0,0 +1,12 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIBywIBAAJhALDb7UbZMvB81CAj0jVahhfbJHI2MzvCZIukSW50/vrSggzEEjpI
+Z+EVzJTfRBtOwBi6RhtRLOIPwDJ37V+L5aMA5jwtpxCJU6grM3Q49zYA/d1bvXvB
+fOF1kCt4LTmFaQIDAQABAmEArq25UCWMG1yfQtM+dnXfRUarW6bOuXJJTmbIJDGn
++WHbEvLBMhF7kCOwuUU/Bl2i1zUP3fwD342Ra4P5We5nHhognov49uKy9SlxTCJU
+z36XvHAk3W1S/hfZ1kF7dkABAjEA1glkyPNcAsfGR05/Q50xRnozhaCkFuoie81k
+m1Dspy9+z+tpKTSOt7Wzun+bAX1pAjEA04iSLdXGKfTwLmHwYK2pRhGpDGkUMQk2
+i3AbEZsmOTQ0/fGaiVFjCsZgC7oYjsgBAjA8oMSPt3+kufoMUMvz1x8SG6NkgrB4
+XTIPZ4rMBAxE/0sokkJjjaOvniSe+25o6aECMQCG3oedM7SSIbpVSFqTuYW4yB/J
+auHV1fLx+ns3wX0gcdnro3SNYtfMEelA8NkhiAECMGVIqAN9qpGW5qAyikDrmzod
+7+wMrWHefpWKsih4+MIvAo7WOOoM+1UasRwVGhMxFQ==
+-----END RSA PRIVATE KEY-----
diff --git a/test/tpm_test/rsa_test.py b/test/tpm_test/rsa_test.py
index a368adefd0..b98d3306c5 100644
--- a/test/tpm_test/rsa_test.py
+++ b/test/tpm_test/rsa_test.py
@@ -6,13 +6,23 @@
"""Module for testing rsa functions using extended commands."""
import binascii
+import Crypto
+import Crypto.Hash.SHA
+import Crypto.Hash.SHA256
+import Crypto.Hash.SHA384
+import Crypto.Hash.SHA512
+from Crypto.PublicKey import RSA
+import Crypto.Signature.PKCS1_PSS
+import Crypto.Signature.PKCS1_v1_5
import hashlib
+import os
import rsa
import struct
import subcmd
import utils
+_MODULE_DIR = os.path.dirname(os.path.abspath(__file__))
_RSA_OPCODES = {
'ENCRYPT': 0x00,
@@ -41,9 +51,29 @@ _RSA_PADDING = {
_HASH = {
'NONE': 0x00,
'SHA1': 0x04,
- 'SHA256': 0x0B
+ 'SHA256': 0x0B,
+ 'SHA384': 0x0C,
+ 'SHA512': 0x0D,
}
+_SIGNER = {
+ 'PKCS1-SSA': Crypto.Signature.PKCS1_v1_5,
+ 'PKCS1-PSS': Crypto.Signature.PKCS1_PSS,
+}
+
+_HASHER = {
+ 'SHA1': Crypto.Hash.SHA,
+ 'SHA256': Crypto.Hash.SHA256,
+ 'SHA384': Crypto.Hash.SHA384,
+ 'SHA512': Crypto.Hash.SHA512,
+}
+
+_KEYS = {
+ 768: RSA.importKey(open(os.path.join(_MODULE_DIR, 'rsa768.pem')).read()),
+ 1024: RSA.importKey(open(os.path.join(_MODULE_DIR, 'rsa1024.pem')).read()),
+ 2048: RSA.importKey(open(os.path.join(_MODULE_DIR, 'rsa2048.pem')).read()),
+ 4096: RSA.importKey(open(os.path.join(_MODULE_DIR, 'rsa4096.pem')).read()),
+}
# Command format.
#
@@ -80,13 +110,8 @@ def _encrypt_cmd(padding, hashing, key_len, msg):
dl='', dig='')
-def _sign_cmd(padding, hashing, key_len, msg):
+def _sign_cmd(padding, hashing, key_len, digest):
op = _RSA_OPCODES['SIGN']
- digest = ''
- if hashing == _HASH['SHA1']:
- digest = hashlib.sha1(msg).digest()
- elif hashing == _HASH['SHA256']:
- digest = hashlib.sha256(msg).digest()
digest_len = len(digest)
return _RSA_CMD_FORMAT.format(o=op, p=padding, h=hashing,
kl=struct.pack('>H', key_len),
@@ -94,14 +119,9 @@ def _sign_cmd(padding, hashing, key_len, msg):
dl='', dig='')
-def _verify_cmd(padding, hashing, key_len, sig, msg):
+def _verify_cmd(padding, hashing, key_len, sig, digest):
op = _RSA_OPCODES['VERIFY']
sig_len = len(sig)
- digest = ''
- if hashing == _HASH['SHA1']:
- digest = hashlib.sha1(msg).digest()
- elif hashing == _HASH['SHA256']:
- digest = hashlib.sha256(msg).digest()
digest_len = len(digest)
return _RSA_CMD_FORMAT.format(o=op, p=padding, h=hashing,
kl=struct.pack('>H', key_len),
@@ -585,9 +605,23 @@ _SIGN_INPUTS = (
('PKCS1-SSA', 'SHA1', 768),
('PKCS1-SSA', 'SHA256', 768),
('PKCS1-SSA', 'SHA256', 1024),
+ ('PKCS1-SSA', 'SHA384', 2048),
+ ('PKCS1-SSA', 'SHA512', 2048),
+ ('PKCS1-PSS', 'SHA1', 768),
+ ('PKCS1-PSS', 'SHA256', 768),
+ ('PKCS1-PSS', 'SHA256', 2048),
+)
+
+_VERIFY_INPUTS = (
+ ('PKCS1-SSA', 'SHA1', 768),
+ ('PKCS1-SSA', 'SHA256', 768),
+ ('PKCS1-SSA', 'SHA256', 1024),
+ ('PKCS1-SSA', 'SHA384', 2048),
+ ('PKCS1-SSA', 'SHA512', 4096),
('PKCS1-PSS', 'SHA1', 768),
('PKCS1-PSS', 'SHA256', 768),
('PKCS1-PSS', 'SHA256', 2048),
+ ('PKCS1-PSS', 'SHA256', 4096),
)
_KEYTEST_INPUTS = (
@@ -644,17 +678,43 @@ def _encrypt_tests(tpm):
def _sign_tests(tpm):
- msg = 'Hello CR50!'
-
for data in _SIGN_INPUTS:
+ msg = rsa.randnum.read_random_bits(256)
padding, hashing, key_len = data
test_name = 'RSA-SIGN:%s:%s:%d' % data
- cmd = _sign_cmd(_RSA_PADDING[padding], _HASH[hashing], key_len, msg)
+
+ key = _KEYS[key_len]
+ verifier = _SIGNER[padding].new(key)
+ h = _HASHER[hashing].new()
+ h.update(msg)
+
+ cmd = _sign_cmd(_RSA_PADDING[padding], _HASH[hashing], key_len, h.digest())
wrapped_response = tpm.command(tpm.wrap_ext_command(subcmd.RSA, cmd))
signature = tpm.unwrap_ext_response(subcmd.RSA, wrapped_response)
+ signer = _SIGNER[padding].new(key)
+ expected_signature = signer.sign(h)
+
+ if not verifier.verify(h, signature):
+ raise subcmd.TpmTestError('%s error' % (
+ test_name,))
+ print('%sSUCCESS: %s' % (utils.cursor_back(), test_name))
+
+
+def _verify_tests(tpm):
+ for data in _VERIFY_INPUTS:
+ msg = rsa.randnum.read_random_bits(256)
+ padding, hashing, key_len = data
+ test_name = 'RSA-VERIFY:%s:%s:%d' % data
+
+ key = _KEYS[key_len]
+ signer = _SIGNER[padding].new(key)
+ h = _HASHER[hashing].new()
+ h.update(msg)
+ signature = signer.sign(h)
+
cmd = _verify_cmd(_RSA_PADDING[padding], _HASH[hashing],
- key_len, signature, msg)
+ key_len, signature, h.digest())
wrapped_response = tpm.command(tpm.wrap_ext_command(subcmd.RSA, cmd))
verified = tpm.unwrap_ext_response(subcmd.RSA, wrapped_response)
expected = '\x01'
@@ -751,6 +811,7 @@ def _x509_verify_tests(tpm):
def rsa_test(tpm):
_encrypt_tests(tpm)
_sign_tests(tpm)
+ _verify_tests(tpm)
_keytest_tests(tpm)
_keygen_tests(tpm)
_primegen_tests(tpm)
diff --git a/test/tpm_test/tpmtest.py b/test/tpm_test/tpmtest.py
index 09cf8fdc2d..a774e4913e 100755
--- a/test/tpm_test/tpmtest.py
+++ b/test/tpm_test/tpmtest.py
@@ -73,7 +73,12 @@ class TPM(object):
if size > 4096:
raise subcmd.TpmTestError(prefix + 'invalid size %d' % size)
if response_mode:
- return
+ # Startup response code or extension command response code
+ if cmd_code == 0x100 or cmd_code == 0:
+ return
+ else:
+ raise subcmd.TpmTestError(
+ prefix + 'invalid command code 0x%x' % cmd_code)
if cmd_code >= 0x11f and cmd_code <= 0x18f:
return # This is a valid command
if cmd_code == EXT_CMD:
@@ -111,16 +116,16 @@ class TPM(object):
error message describes the problem.
"""
header_size = struct.calcsize(self.HEADER_FMT)
- tag, size, cmd, subcmd = struct.unpack(self.HEADER_FMT,
+ tag, size, cmd, sub = struct.unpack(self.HEADER_FMT,
response[:header_size])
if tag != 0x8001:
raise subcmd.TpmTestError('Wrong response tag: %4.4x' % tag)
- if cmd != EXT_CMD:
+ if cmd:
raise subcmd.TpmTestError('Unexpected response command field: %8.8x' %
cmd)
- if subcmd != expected_subcmd:
+ if sub != expected_subcmd:
raise subcmd.TpmTestError('Unexpected response subcommand field: %2.2x' %
- subcmd)
+ sub)
if size != len(response):
raise subcmd.TpmTestError('Size mismatch: header %d, actual %d' % (
size, len(response)))
diff --git a/test/tpm_test/upgrade_test.py b/test/tpm_test/upgrade_test.py
index db70b5ce76..2d2c0b7169 100644
--- a/test/tpm_test/upgrade_test.py
+++ b/test/tpm_test/upgrade_test.py
@@ -36,7 +36,7 @@ def upgrade(tpm):
if len(base_str) < 4:
raise subcmd.TpmTestError('Initialization error %d' %
ord(base_str[0]))
- base = struct.unpack('>I', base_str)[0]
+ base = struct.unpack_from('>4I', base_str)[3]
if base == 0x84000:
fname = 'build/cr50/RW/ec.RW_B.flat'
elif base == 0x44000:
@@ -44,7 +44,7 @@ def upgrade(tpm):
else:
raise subcmd.TpmTestError('Unknown base address 0x%x' % base)
fname = os.path.join(os.path.dirname(__file__), '../..', fname)
- data = open(fname, 'r').read()
+ data = open(fname, 'r').read()[:2000]
transferred = 0
block_size = 1024
diff --git a/test/usb_pd.c b/test/usb_pd.c
index ea68c66599..69b7448ea6 100644
--- a/test/usb_pd.c
+++ b/test/usb_pd.c
@@ -4,7 +4,7 @@
*
* Test USB PD module.
*/
-
+#include "battery.h"
#include "common.h"
#include "crc.h"
#include "task.h"
@@ -14,6 +14,14 @@
#include "usb_pd_test_util.h"
#include "util.h"
+#define PORT0 0
+#define PORT1 1
+
+#define BATTERY_DESIGN_VOLTAGE 7600
+#define BATTERY_DESIGN_CAPACITY 5131
+#define BATTERY_FULL_CHARGE_CAPACITY 5131
+#define BATTERY_REMAINING_CAPACITY 2566
+
struct pd_port_t {
int host_mode;
int has_vbus;
@@ -22,9 +30,60 @@ struct pd_port_t {
int polarity;
int partner_role; /* -1 for none */
int partner_polarity;
+ int rev;
} pd_port[CONFIG_USB_PD_PORT_COUNT];
+static int give_back_called;
+
/* Mock functions */
+#ifdef CONFIG_USB_PD_REV30
+
+uint16_t pd_get_identity_vid(int port)
+{
+ return 0;
+}
+
+uint16_t pd_get_identity_pid(int port)
+{
+ return 0;
+}
+
+enum battery_present battery_is_present(void)
+{
+ return BP_YES;
+}
+
+int battery_status(int *status)
+{
+ *status = 1;
+ return 0;
+}
+
+int battery_remaining_capacity(int *capacity)
+{
+ *capacity = BATTERY_REMAINING_CAPACITY;
+ return 0;
+}
+
+int battery_full_charge_capacity(int *capacity)
+{
+ *capacity = BATTERY_FULL_CHARGE_CAPACITY;
+ return 0;
+}
+
+int battery_design_capacity(int *capacity)
+{
+ *capacity = BATTERY_DESIGN_CAPACITY;
+ return 0;
+}
+
+int battery_design_voltage(int *voltage)
+{
+ *voltage = BATTERY_DESIGN_VOLTAGE;
+ return 0;
+}
+
+#endif
int pd_adc_read(int port, int cc)
{
@@ -67,6 +126,11 @@ int pd_vdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload)
return 0;
}
+int board_select_rp_value(int port, int rp)
+{
+ return 0;
+}
+
/* Tests */
void inc_tx_id(int port)
@@ -87,6 +151,11 @@ static void init_ports(void)
pd_port[i].host_mode = 0;
pd_port[i].partner_role = -1;
pd_port[i].has_vbus = 0;
+#ifdef CONFIG_USB_PD_REV30
+ pd_port[i].rev = PD_REV30;
+#else
+ pd_port[i].rev = PD_REV20;
+#endif
}
}
@@ -113,27 +182,93 @@ static void simulate_rx_msg(int port, uint16_t header, int cnt,
pd_simulate_rx(port);
}
-static void simulate_source_cap(int port)
+static void simulate_wait(int port)
+{
+ uint16_t header = PD_HEADER(PD_CTRL_WAIT, PD_ROLE_SOURCE,
+ PD_ROLE_DFP, pd_port[port].msg_rx_id,
+ 0, pd_port[port].rev, 0);
+
+ simulate_rx_msg(port, header, 0, NULL);
+}
+
+static void simulate_accept(int port)
+{
+ uint16_t header = PD_HEADER(PD_CTRL_ACCEPT, PD_ROLE_SOURCE,
+ PD_ROLE_DFP, pd_port[port].msg_rx_id,
+ 0, pd_port[port].rev, 0);
+
+ simulate_rx_msg(port, header, 0, NULL);
+}
+
+static void simulate_reject(int port)
+{
+ uint16_t header = PD_HEADER(PD_CTRL_REJECT, PD_ROLE_SOURCE,
+ PD_ROLE_DFP, pd_port[port].msg_rx_id,
+ 0, pd_port[port].rev, 0);
+
+ simulate_rx_msg(port, header, 0, NULL);
+}
+
+
+#ifdef CONFIG_USB_PD_REV30
+static void simulate_get_bat_cap(int port)
{
+ uint16_t msg[2];
+ uint16_t header = PD_HEADER(PD_EXT_GET_BATTERY_CAP, PD_ROLE_SOURCE,
+ PD_ROLE_DFP, pd_port[port].msg_rx_id,
+ 1, pd_port[port].rev, 1);
+
+ /* set extended header */
+ msg[0] = PD_EXT_HEADER(0, 0, 1);
+
+ /* set battery status ref */
+ msg[1] = 0;
+
+ simulate_rx_msg(port, header, 1, (const uint32_t *)msg);
+}
+
+static void simulate_get_bat_status(int port)
+{
+ uint16_t msg[2];
+ uint16_t header = PD_HEADER(PD_EXT_GET_BATTERY_STATUS, PD_ROLE_SOURCE,
+ PD_ROLE_DFP, pd_port[port].msg_rx_id,
+ 1, pd_port[port].rev, 1);
+
+ /* set extended header */
+ msg[0] = PD_EXT_HEADER(0, 0, 1);
+
+ /* set battery status ref */
+ msg[1] = 0;
+
+ simulate_rx_msg(port, header, 1, (const uint32_t *)msg);
+}
+#endif
+
+static void simulate_source_cap(int port, uint32_t cnt)
+{
+ uint32_t src_pdo_cnt = (cnt == 0) ? 1 : pd_src_pdo_cnt;
+
uint16_t header = PD_HEADER(PD_DATA_SOURCE_CAP, PD_ROLE_SOURCE,
PD_ROLE_DFP, pd_port[port].msg_rx_id,
- pd_src_pdo_cnt);
- simulate_rx_msg(port, header, pd_src_pdo_cnt, pd_src_pdo);
+ src_pdo_cnt, pd_port[port].rev, 0);
+
+ simulate_rx_msg(port, header, src_pdo_cnt, pd_src_pdo);
}
static void simulate_goodcrc(int port, int role, int id)
{
- simulate_rx_msg(port, PD_HEADER(PD_CTRL_GOOD_CRC, role, role, id, 0),
- 0, NULL);
+ simulate_rx_msg(port, PD_HEADER(PD_CTRL_GOOD_CRC, role, role, id, 0,
+ pd_port[port].rev, 0), 0, NULL);
}
static int verify_goodcrc(int port, int role, int id)
{
- return pd_test_tx_msg_verify_sop(0) &&
- pd_test_tx_msg_verify_short(0, PD_HEADER(PD_CTRL_GOOD_CRC,
- role, role, id, 0)) &&
- pd_test_tx_msg_verify_crc(0) &&
- pd_test_tx_msg_verify_eop(0);
+
+ return pd_test_tx_msg_verify_sop(port) &&
+ pd_test_tx_msg_verify_short(port, PD_HEADER(PD_CTRL_GOOD_CRC,
+ role, role, id, 0, 0, 0)) &&
+ pd_test_tx_msg_verify_crc(port) &&
+ pd_test_tx_msg_verify_eop(port);
}
static void plug_in_source(int port, int polarity)
@@ -152,74 +287,531 @@ static void plug_in_sink(int port, int polarity)
static void unplug(int port)
{
+ pd_port[port].msg_tx_id = 0;
+ pd_port[port].msg_rx_id = 0;
pd_port[port].has_vbus = 0;
pd_port[port].partner_role = -1;
task_wake(PD_PORT_TO_TASK_ID(port));
usleep(30 * MSEC);
}
-static int test_request(void)
+void pd_snk_give_back(int port, uint32_t * const ma, uint32_t * const mv)
+{
+ if (*ma == 3000)
+ give_back_called = 1;
+}
+
+static void simulate_ps_rdy(int port)
+{
+ uint16_t header = PD_HEADER(PD_CTRL_PS_RDY, PD_ROLE_SOURCE,
+ PD_ROLE_DFP, pd_port[port].msg_rx_id,
+ 0, pd_port[port].rev, 0);
+
+ simulate_rx_msg(port, header, 0, NULL);
+}
+
+static void simulate_goto_min(int port)
+{
+ uint16_t header = PD_HEADER(PD_CTRL_GOTO_MIN, PD_ROLE_SOURCE,
+ PD_ROLE_DFP, pd_port[port].msg_rx_id, 0, pd_port[port].rev, 0);
+
+ simulate_rx_msg(port, header, 0, NULL);
+}
+
+static int test_request_with_wait_and_contract(void)
{
+#ifdef CONFIG_USB_PD_REV30
+ uint32_t expected_status_bsdo =
+ BSDO_CAP(DIV_ROUND_NEAREST(BATTERY_REMAINING_CAPACITY *
+ BATTERY_DESIGN_VOLTAGE, 100000)) |
+ BSDO_PRESENT;
+ uint16_t expected_cap_hdr = PD_EXT_HEADER(0, 0, 9);
+ uint16_t expected_cap_vid = USB_VID_GOOGLE;
+#ifdef CONFIG_USB_PID
+ uint16_t expected_cap_pid = CONFIG_USB_PID;
+#else
+ uint16_t expected_cap_pid = 0;
+#endif
+ uint16_t expected_cap_des =
+ DIV_ROUND_NEAREST(BATTERY_DESIGN_CAPACITY *
+ BATTERY_DESIGN_VOLTAGE, 100000);
+ uint16_t expected_cap_ful =
+ DIV_ROUND_NEAREST(BATTERY_FULL_CHARGE_CAPACITY *
+ BATTERY_DESIGN_VOLTAGE, 100000);
+ uint16_t expected_cap_type = 0;
+#endif
+
+#ifdef CONFIG_USB_PD_GIVE_BACK
+ uint32_t expected_rdo =
+ RDO_FIXED(2, 3000, PD_MIN_CURRENT_MA, RDO_GIVE_BACK);
+#else
+ uint32_t expected_rdo = RDO_FIXED(2, 3000, 3000, 0);
+#endif
+ uint8_t port = PORT0;
+
+ plug_in_source(port, 0);
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(2 * PD_T_CC_DEBOUNCE + 100 * MSEC);
+ TEST_ASSERT(pd_port[port].polarity == 0);
+
+ /* We're in SNK_DISCOVERY now. Let's send the source cap. */
+ simulate_source_cap(port, 1);
+ task_wait_event(30 * MSEC);
+ TEST_ASSERT(verify_goodcrc(port, PD_ROLE_SINK,
+ pd_port[port].msg_rx_id));
+
+ /* Wait for the power request */
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(35 * MSEC); /* tSenderResponse: 24~30 ms */
+ inc_rx_id(port);
+
+ /* Process the request */
+ TEST_ASSERT(pd_test_tx_msg_verify_sop(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_short(port,
+ PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP,
+ pd_port[port].msg_tx_id, 1, pd_port[port].rev, 0)));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_rdo));
+ TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ /* Request was good. Send GoodCRC */
+ simulate_goodcrc(port, PD_ROLE_SOURCE, pd_port[port].msg_tx_id);
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+ inc_tx_id(port);
+
+ /* We're in SNK_REQUESTED. Send accept */
+ simulate_accept(port);
+ task_wait_event(30 * MSEC);
+ TEST_ASSERT(verify_goodcrc(0, PD_ROLE_SINK, pd_port[port].msg_rx_id));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+ inc_rx_id(port);
+
+ /*
+ * We're in SNK_TRANSITION.
+ * And we have an explicit power contract.
+ */
+ simulate_source_cap(port, 1);
+ task_wait_event(30 * MSEC);
+ TEST_ASSERT(verify_goodcrc(port, PD_ROLE_SINK,
+ pd_port[port].msg_rx_id));
+
+ /* Wait for the power request */
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(35 * MSEC); /* tSenderResponse: 24~30 ms */
+ inc_rx_id(port);
+
+ /* Process the request */
+ TEST_ASSERT(pd_test_tx_msg_verify_sop(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_short(port,
+ PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP,
+ pd_port[port].msg_tx_id, 1, pd_port[port].rev, 0)));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_rdo));
+ TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ /* Request was good. Send GoodCRC */
+ simulate_goodcrc(port, PD_ROLE_SOURCE, pd_port[port].msg_tx_id);
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+ inc_tx_id(port);
+
+ /* We're in SNK_REQUESTED. Send wait */
+ simulate_wait(port);
+ task_wait_event(30 * MSEC);
+ TEST_ASSERT(verify_goodcrc(0, PD_ROLE_SINK, pd_port[port].msg_rx_id));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ /* PD_T_SINK_REQUEST. Request is sent again after 100 ms */
+ task_wait_event(100 * MSEC);
+ inc_rx_id(port);
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ /* We had an explicit contract. So request should have been resent. */
+ /* Process the request */
+ TEST_ASSERT(pd_test_tx_msg_verify_sop(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_short(port,
+ PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP,
+ pd_port[port].msg_tx_id, 1,
+ pd_port[port].rev, 0
+ )));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_rdo));
+ TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ /* Request was good. Send GoodCRC */
+ simulate_goodcrc(port, PD_ROLE_SOURCE, pd_port[port].msg_tx_id);
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+ inc_tx_id(port);
+
+ /* We're in SNK_REQUESTED. Send accept */
+ simulate_accept(port);
+ task_wait_event(30 * MSEC);
+ TEST_ASSERT(verify_goodcrc(0, PD_ROLE_SINK, pd_port[port].msg_rx_id));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+ inc_rx_id(port);
+
+ /* We're in SNK_TRANSITION. Send ps_rdy */
+ simulate_ps_rdy(port);
+ task_wait_event(30 * MSEC);
+ TEST_ASSERT(verify_goodcrc(0, PD_ROLE_SINK, pd_port[port].msg_rx_id));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+ inc_rx_id(port);
+
+ /*
+ * Test Extended Get_Battery_Cap and Get_Battery_Status messages.
+ */
+#ifdef CONFIG_USB_PD_REV30
+ /* We're in SNK_READY. Send get battery cap. */
+ simulate_get_bat_cap(port);
+ task_wait_event(30 * MSEC);
+ TEST_ASSERT(verify_goodcrc(0, PD_ROLE_SINK, pd_port[port].msg_rx_id));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+ inc_rx_id(port);
+
+ /* Process the request */
+ TEST_ASSERT(pd_test_tx_msg_verify_sop(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_short(port,
+ PD_HEADER(PD_EXT_BATTERY_CAP, PD_ROLE_SINK, PD_ROLE_UFP,
+ pd_port[port].msg_tx_id, 3, pd_port[port].rev, 1)));
+ TEST_ASSERT(pd_test_tx_msg_verify_short(port, expected_cap_hdr));
+ TEST_ASSERT(pd_test_tx_msg_verify_short(port, expected_cap_vid));
+ TEST_ASSERT(pd_test_tx_msg_verify_short(port, expected_cap_pid));
+ TEST_ASSERT(pd_test_tx_msg_verify_short(port, expected_cap_des));
+ TEST_ASSERT(pd_test_tx_msg_verify_short(port, expected_cap_ful));
+ TEST_ASSERT(pd_test_tx_msg_verify_short(port, expected_cap_type));
+ TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ /* Request was good. Send GoodCRC */
+ simulate_goodcrc(port, PD_ROLE_SOURCE, pd_port[port].msg_tx_id);
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+ inc_tx_id(port);
+
+ /* Send get battery status. */
+ simulate_get_bat_status(port);
+ task_wait_event(30 * MSEC);
+ TEST_ASSERT(verify_goodcrc(0, PD_ROLE_SINK, pd_port[port].msg_rx_id));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+ inc_rx_id(port);
+
+ /* Process the request */
+ TEST_ASSERT(pd_test_tx_msg_verify_sop(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_short(port,
+ PD_HEADER(PD_DATA_BATTERY_STATUS, PD_ROLE_SINK, PD_ROLE_UFP,
+ pd_port[port].msg_tx_id, 1, pd_port[port].rev, 0)));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_status_bsdo));
+ TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ /* Request was good. Send GoodCRC */
+ simulate_goodcrc(port, PD_ROLE_SOURCE, pd_port[port].msg_tx_id);
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+ inc_tx_id(port);
+#endif
+ /* We're in SNK_READY. Send goto_min */
+ simulate_goto_min(port);
+ task_wait_event(30 * MSEC);
+ TEST_ASSERT(verify_goodcrc(0, PD_ROLE_SINK, pd_port[port].msg_rx_id));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+ inc_rx_id(port);
+
+#ifdef CONFIG_USB_PD_GIVE_BACK
+ TEST_ASSERT(give_back_called);
+#else
+ TEST_ASSERT(!give_back_called);
+#endif
+ /* We're done */
+ unplug(port);
+
+ return EC_SUCCESS;
+}
+
+static int test_request_with_wait(void)
+{
+#ifdef CONFIG_USB_PD_GIVE_BACK
+ uint32_t expected_rdo = RDO_FIXED(1, 900, PD_MIN_CURRENT_MA,
+ RDO_CAP_MISMATCH | RDO_GIVE_BACK);
+#else
uint32_t expected_rdo = RDO_FIXED(1, 900, 900, RDO_CAP_MISMATCH);
+#endif
+ uint8_t port = PORT0;
+
+ plug_in_source(port, 0);
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(2 * PD_T_CC_DEBOUNCE + 100 * MSEC);
+ TEST_ASSERT(pd_port[port].polarity == 0);
+
+ /* We're in SNK_DISCOVERY now. Let's send the source cap. */
+ simulate_source_cap(port, 0);
+ task_wait_event(30 * MSEC);
+ TEST_ASSERT(verify_goodcrc(port,
+ PD_ROLE_SINK, pd_port[port].msg_rx_id));
- plug_in_source(0, 0);
+ /* Wait for the power request */
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(35 * MSEC); /* tSenderResponse: 24~30 ms */
+ inc_rx_id(port);
+
+ /* Process the request */
+ TEST_ASSERT(pd_test_tx_msg_verify_sop(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_short(port,
+ PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP,
+ pd_port[port].msg_tx_id, 1, pd_port[port].rev, 0)));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_rdo));
+ TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ /* Request is good. Send GoodCRC */
+ simulate_goodcrc(port, PD_ROLE_SOURCE, pd_port[port].msg_tx_id);
task_wake(PD_PORT_TO_TASK_ID(0));
+ task_wait_event(30 * MSEC);
+ inc_tx_id(port);
+
+ /* We're in SNK_REQUESTED. Send wait */
+ simulate_wait(port);
+ task_wait_event(30 * MSEC);
+ TEST_ASSERT(verify_goodcrc(0, PD_ROLE_SINK, pd_port[port].msg_rx_id));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+ inc_rx_id(port);
+
+ /* We didn't have an explicit contract. So we're in SNK_DISCOVERY. */
+ /* Resend Source Cap. */
+ simulate_source_cap(port, 0);
+ task_wait_event(30 * MSEC);
+ TEST_ASSERT(verify_goodcrc(port,
+ PD_ROLE_SINK, pd_port[port].msg_rx_id));
+
+ /* Wait for the power request */
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(35 * MSEC); /* tSenderResponse: 24~30 ms */
+ inc_rx_id(port);
+
+ /* Process the request */
+ TEST_ASSERT(pd_test_tx_msg_verify_sop(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_short(port,
+ PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP,
+ pd_port[port].msg_tx_id, 1, pd_port[port].rev, 0)));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_rdo));
+ TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ /* Request was good. Send GoodCRC */
+ simulate_goodcrc(port, PD_ROLE_SOURCE, pd_port[port].msg_tx_id);
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+ inc_tx_id(port);
+
+ /* We're done */
+ unplug(port);
+ return EC_SUCCESS;
+}
+
+static int test_request_with_reject(void)
+{
+#ifdef CONFIG_USB_PD_GIVE_BACK
+ uint32_t expected_rdo = RDO_FIXED(1, 900, PD_MIN_CURRENT_MA,
+ RDO_CAP_MISMATCH | RDO_GIVE_BACK);
+#else
+ uint32_t expected_rdo = RDO_FIXED(1, 900, 900, RDO_CAP_MISMATCH);
+#endif
+ uint8_t port = PORT0;
+
+ plug_in_source(port, 0);
+ task_wake(PD_PORT_TO_TASK_ID(port));
task_wait_event(2 * PD_T_CC_DEBOUNCE + 100 * MSEC);
- TEST_ASSERT(pd_port[0].polarity == 0);
+ TEST_ASSERT(pd_port[port].polarity == 0);
/* We're in SNK_DISCOVERY now. Let's send the source cap. */
- simulate_source_cap(0);
+ simulate_source_cap(port, 0);
task_wait_event(30 * MSEC);
- TEST_ASSERT(verify_goodcrc(0, PD_ROLE_SINK, pd_port[0].msg_rx_id));
+ TEST_ASSERT(verify_goodcrc(port,
+ PD_ROLE_SINK, pd_port[port].msg_rx_id));
/* Wait for the power request */
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(35 * MSEC); /* tSenderResponse: 24~30 ms */
+ inc_rx_id(port);
+
+ /* Process the request */
+ TEST_ASSERT(pd_test_tx_msg_verify_sop(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_short(port,
+ PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP,
+ pd_port[port].msg_tx_id, 1, pd_port[port].rev, 0)));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_rdo));
+ TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ /* Request is good. Send GoodCRC */
+ simulate_goodcrc(port, PD_ROLE_SOURCE, pd_port[port].msg_tx_id);
task_wake(PD_PORT_TO_TASK_ID(0));
+ task_wait_event(30 * MSEC);
+ inc_tx_id(port);
+
+ /* We're in SNK_REQUESTED. Send reject */
+ simulate_reject(port);
+ task_wait_event(30 * MSEC);
+ TEST_ASSERT(verify_goodcrc(0, PD_ROLE_SINK, pd_port[port].msg_rx_id));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+ inc_rx_id(port);
+
+ /* We're in SNK_READY. Send source cap. again. */
+ simulate_source_cap(port, 0);
+ task_wait_event(30 * MSEC);
+ TEST_ASSERT(verify_goodcrc(port,
+ PD_ROLE_SINK, pd_port[port].msg_rx_id));
+
+ /* Wait for the power request */
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(35 * MSEC); /* tSenderResponse: 24~30 ms */
+ inc_rx_id(port);
+
+ /* Process the request */
+ TEST_ASSERT(pd_test_tx_msg_verify_sop(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_short(port,
+ PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP,
+ pd_port[port].msg_tx_id, 1, pd_port[port].rev, 0)));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_rdo));
+ TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
+
+ /* We're done */
+ unplug(port);
+ return EC_SUCCESS;
+}
+
+static int test_request(void)
+{
+#ifdef CONFIG_USB_PD_GIVE_BACK
+ uint32_t expected_rdo = RDO_FIXED(1, 900, PD_MIN_CURRENT_MA,
+ RDO_CAP_MISMATCH | RDO_GIVE_BACK);
+#else
+ uint32_t expected_rdo = RDO_FIXED(1, 900, 900, RDO_CAP_MISMATCH);
+#endif
+ uint8_t port = PORT0;
+
+ plug_in_source(port, 0);
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(2 * PD_T_CC_DEBOUNCE + 100 * MSEC);
+ TEST_ASSERT(pd_port[port].polarity == 0);
+
+ /* We're in SNK_DISCOVERY now. Let's send the source cap. */
+ simulate_source_cap(port, 0);
+ task_wait_event(30 * MSEC);
+ TEST_ASSERT(verify_goodcrc(port,
+ PD_ROLE_SINK, pd_port[port].msg_rx_id));
+
+ /* Wait for the power request */
+ task_wake(PD_PORT_TO_TASK_ID(port));
task_wait_event(35 * MSEC); /* tSenderResponse: 24~30 ms */
- inc_rx_id(0);
+ inc_rx_id(port);
/* Process the request */
- TEST_ASSERT(pd_test_tx_msg_verify_sop(0));
- TEST_ASSERT(pd_test_tx_msg_verify_short(0,
+ TEST_ASSERT(pd_test_tx_msg_verify_sop(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_short(port,
PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP,
- pd_port[0].msg_tx_id, 1)));
- TEST_ASSERT(pd_test_tx_msg_verify_word(0, expected_rdo));
- TEST_ASSERT(pd_test_tx_msg_verify_crc(0));
- TEST_ASSERT(pd_test_tx_msg_verify_eop(0));
- inc_tx_id(0);
+ pd_port[port].msg_tx_id, 1, pd_port[port].rev, 0)));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_rdo));
+ TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ /* Request was good. Send GoodCRC */
+ simulate_goodcrc(port, PD_ROLE_SOURCE, pd_port[port].msg_tx_id);
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+ inc_tx_id(port);
/* We're done */
- unplug(0);
+ unplug(port);
+
return EC_SUCCESS;
}
static int test_sink(void)
{
int i;
+ uint8_t port = PORT1;
- plug_in_sink(1, 1);
- task_wake(PD_PORT_TO_TASK_ID(1));
+ plug_in_sink(port, 1);
+ task_wake(PD_PORT_TO_TASK_ID(port));
task_wait_event(250 * MSEC); /* tTypeCSinkWaitCap: 210~250 ms */
- TEST_ASSERT(pd_port[1].polarity == 1);
+ TEST_ASSERT(pd_port[port].polarity == 1);
/* The source cap should be sent */
- TEST_ASSERT(pd_test_tx_msg_verify_sop(1));
- TEST_ASSERT(pd_test_tx_msg_verify_short(1,
+ TEST_ASSERT(pd_test_tx_msg_verify_sop(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_short(port,
PD_HEADER(PD_DATA_SOURCE_CAP, PD_ROLE_SOURCE,
- PD_ROLE_DFP, pd_port[1].msg_tx_id,
- pd_src_pdo_cnt)));
+ PD_ROLE_DFP, pd_port[port].msg_tx_id,
+ pd_src_pdo_cnt, pd_port[port].rev, 0)));
+
for (i = 0; i < pd_src_pdo_cnt; ++i)
- TEST_ASSERT(pd_test_tx_msg_verify_word(1, pd_src_pdo[i]));
- TEST_ASSERT(pd_test_tx_msg_verify_crc(1));
- TEST_ASSERT(pd_test_tx_msg_verify_eop(1));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, pd_src_pdo[i]));
+
+ TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
+
+ /* Wake from pd_start_tx */
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ usleep(30 * MSEC);
/* Looks good. Ack the source cap. */
- simulate_goodcrc(1, PD_ROLE_SINK, pd_port[1].msg_tx_id);
- task_wake(PD_PORT_TO_TASK_ID(1));
+ simulate_goodcrc(port, PD_ROLE_SINK, pd_port[port].msg_tx_id);
+
+ /* Wake from pd_rx_start */
+ task_wake(PD_PORT_TO_TASK_ID(port));
usleep(30 * MSEC);
- inc_tx_id(1);
+ inc_tx_id(port);
/* We're done */
- unplug(1);
+ unplug(port);
return EC_SUCCESS;
}
@@ -231,6 +823,9 @@ void run_test(void)
RUN_TEST(test_request);
RUN_TEST(test_sink);
+ RUN_TEST(test_request_with_wait);
+ RUN_TEST(test_request_with_wait_and_contract);
+ RUN_TEST(test_request_with_reject);
test_print_result();
}
diff --git a/test/usb_pd_giveback.tasklist b/test/usb_pd_giveback.tasklist
new file mode 120000
index 0000000000..45cc6c8aa2
--- /dev/null
+++ b/test/usb_pd_giveback.tasklist
@@ -0,0 +1 @@
+usb_pd.tasklist \ No newline at end of file
diff --git a/test/usb_pd_rev30.tasklist b/test/usb_pd_rev30.tasklist
new file mode 120000
index 0000000000..45cc6c8aa2
--- /dev/null
+++ b/test/usb_pd_rev30.tasklist
@@ -0,0 +1 @@
+usb_pd.tasklist \ No newline at end of file
diff --git a/test/utils.c b/test/utils.c
index e7de7f8b3d..c382a46828 100644
--- a/test/utils.c
+++ b/test/utils.c
@@ -13,59 +13,6 @@
#include "timer.h"
#include "util.h"
-static int test_isalpha(void)
-{
- TEST_CHECK(isalpha('a') && isalpha('z') && isalpha('A') &&
- isalpha('Z') && !isalpha('0') && !isalpha('~') &&
- !isalpha(' ') && !isalpha('\0') && !isalpha('\n'));
-}
-
-static int test_isprint(void)
-{
- TEST_CHECK(isprint('a') && isprint('z') && isprint('A') &&
- isprint('Z') && isprint('0') && isprint('~') &&
- isprint(' ') && !isprint('\0') && !isprint('\n'));
-}
-
-static int test_strtoi(void)
-{
- char *e;
-
- TEST_ASSERT(strtoi("10", &e, 0) == 10);
- TEST_ASSERT(e && (*e == '\0'));
- TEST_ASSERT(strtoi("0x1f z", &e, 0) == 31);
- TEST_ASSERT(e && (*e == ' '));
- TEST_ASSERT(strtoi("10a", &e, 16) == 266);
- TEST_ASSERT(e && (*e == '\0'));
- TEST_ASSERT(strtoi("0x02C", &e, 16) == 44);
- TEST_ASSERT(e && (*e == '\0'));
- TEST_ASSERT(strtoi(" -12", &e, 0) == -12);
- TEST_ASSERT(e && (*e == '\0'));
- TEST_ASSERT(strtoi("!", &e, 0) == 0);
- TEST_ASSERT(e && (*e == '!'));
-
- return EC_SUCCESS;
-}
-
-static int test_parse_bool(void)
-{
- int v;
-
- TEST_ASSERT(parse_bool("on", &v) == 1);
- TEST_ASSERT(v == 1);
- TEST_ASSERT(parse_bool("off", &v) == 1);
- TEST_ASSERT(v == 0);
- TEST_ASSERT(parse_bool("enable", &v) == 1);
- TEST_ASSERT(v == 1);
- TEST_ASSERT(parse_bool("disable", &v) == 1);
- TEST_ASSERT(v == 0);
- TEST_ASSERT(parse_bool("di", &v) == 0);
- TEST_ASSERT(parse_bool("en", &v) == 0);
- TEST_ASSERT(parse_bool("of", &v) == 0);
-
- return EC_SUCCESS;
-}
-
static int test_memmove(void)
{
int i;
@@ -231,50 +178,17 @@ static int test_memset(void)
return EC_SUCCESS;
}
-static int test_strzcpy(void)
+static int test_memchr(void)
{
- char dest[10];
-
- strzcpy(dest, "test", 10);
- TEST_ASSERT_ARRAY_EQ("test", dest, 5);
- strzcpy(dest, "testtesttest", 10);
- TEST_ASSERT_ARRAY_EQ("testtestt", dest, 10);
- strzcpy(dest, "aaaa", -1);
- TEST_ASSERT_ARRAY_EQ("testtestt", dest, 10);
+ char *buf = "1234";
+ TEST_ASSERT(memchr("123", '4', 8) == NULL);
+ TEST_ASSERT(memchr("123", '3', 2) == NULL);
+ TEST_ASSERT(memchr(buf, '3', 8) == buf + 2);
+ TEST_ASSERT(memchr(buf, '4', 4) == buf + 3);
return EC_SUCCESS;
}
-static int test_strlen(void)
-{
- TEST_CHECK(strlen("this is a string") == 16);
-}
-
-static int test_strcasecmp(void)
-{
- TEST_CHECK((strcasecmp("test string", "TEST strIng") == 0) &&
- (strcasecmp("test123!@#", "TesT123!@#") == 0) &&
- (strcasecmp("lower", "UPPER") != 0));
-}
-
-static int test_strncasecmp(void)
-{
- TEST_CHECK((strncasecmp("test string", "TEST str", 4) == 0) &&
- (strncasecmp("test string", "TEST str", 8) == 0) &&
- (strncasecmp("test123!@#", "TesT321!@#", 5) != 0) &&
- (strncasecmp("test123!@#", "TesT321!@#", 4) == 0) &&
- (strncasecmp("1test123!@#", "1TesT321!@#", 5) == 0) &&
- (strncasecmp("1test123", "teststr", 0) == 0));
-}
-
-static int test_atoi(void)
-{
- TEST_CHECK((atoi(" 901") == 901) &&
- (atoi("-12c") == -12) &&
- (atoi(" 0 ") == 0) &&
- (atoi("\t111") == 111));
-}
-
static int test_uint64divmod_0(void)
{
uint64_t n = 8567106442584750ULL;
@@ -446,18 +360,10 @@ void run_test(void)
{
test_reset();
- RUN_TEST(test_isalpha);
- RUN_TEST(test_isprint);
- RUN_TEST(test_strtoi);
- RUN_TEST(test_parse_bool);
RUN_TEST(test_memmove);
RUN_TEST(test_memcpy);
RUN_TEST(test_memset);
- RUN_TEST(test_strzcpy);
- RUN_TEST(test_strlen);
- RUN_TEST(test_strcasecmp);
- RUN_TEST(test_strncasecmp);
- RUN_TEST(test_atoi);
+ RUN_TEST(test_memchr);
RUN_TEST(test_uint64divmod_0);
RUN_TEST(test_uint64divmod_1);
RUN_TEST(test_uint64divmod_2);
diff --git a/test/utils_str.c b/test/utils_str.c
new file mode 100644
index 0000000000..23890639e2
--- /dev/null
+++ b/test/utils_str.c
@@ -0,0 +1,162 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Test common utilities (string functions).
+ */
+
+#include "common.h"
+#include "console.h"
+#include "system.h"
+#include "test_util.h"
+#include "timer.h"
+#include "util.h"
+
+static int test_isalpha(void)
+{
+ TEST_CHECK(isalpha('a') && isalpha('z') && isalpha('A') &&
+ isalpha('Z') && !isalpha('0') && !isalpha('~') &&
+ !isalpha(' ') && !isalpha('\0') && !isalpha('\n'));
+}
+
+static int test_isprint(void)
+{
+ TEST_CHECK(isprint('a') && isprint('z') && isprint('A') &&
+ isprint('Z') && isprint('0') && isprint('~') &&
+ isprint(' ') && !isprint('\0') && !isprint('\n'));
+}
+
+static int test_strtoi(void)
+{
+ char *e;
+
+ TEST_ASSERT(strtoi("10", &e, 0) == 10);
+ TEST_ASSERT(e && (*e == '\0'));
+ TEST_ASSERT(strtoi("0x1f z", &e, 0) == 31);
+ TEST_ASSERT(e && (*e == ' '));
+ TEST_ASSERT(strtoi("10a", &e, 16) == 266);
+ TEST_ASSERT(e && (*e == '\0'));
+ TEST_ASSERT(strtoi("0x02C", &e, 16) == 44);
+ TEST_ASSERT(e && (*e == '\0'));
+ TEST_ASSERT(strtoi(" -12", &e, 0) == -12);
+ TEST_ASSERT(e && (*e == '\0'));
+ TEST_ASSERT(strtoi("!", &e, 0) == 0);
+ TEST_ASSERT(e && (*e == '!'));
+
+ return EC_SUCCESS;
+}
+
+static int test_parse_bool(void)
+{
+ int v;
+
+ TEST_ASSERT(parse_bool("on", &v) == 1);
+ TEST_ASSERT(v == 1);
+ TEST_ASSERT(parse_bool("off", &v) == 1);
+ TEST_ASSERT(v == 0);
+ TEST_ASSERT(parse_bool("enable", &v) == 1);
+ TEST_ASSERT(v == 1);
+ TEST_ASSERT(parse_bool("disable", &v) == 1);
+ TEST_ASSERT(v == 0);
+ TEST_ASSERT(parse_bool("di", &v) == 0);
+ TEST_ASSERT(parse_bool("en", &v) == 0);
+ TEST_ASSERT(parse_bool("of", &v) == 0);
+
+ return EC_SUCCESS;
+}
+
+static int test_strzcpy(void)
+{
+ char dest[10];
+
+ strzcpy(dest, "test", 10);
+ TEST_ASSERT_ARRAY_EQ("test", dest, 5);
+ strzcpy(dest, "testtesttest", 10);
+ TEST_ASSERT_ARRAY_EQ("testtestt", dest, 10);
+ strzcpy(dest, "aaaa", -1);
+ TEST_ASSERT_ARRAY_EQ("testtestt", dest, 10);
+
+ return EC_SUCCESS;
+}
+
+static int test_strncpy(void)
+{
+ char dest[10];
+
+ strncpy(dest, "test", 10);
+ TEST_ASSERT_ARRAY_EQ("test", dest, 5);
+ strncpy(dest, "12345", 6);
+ TEST_ASSERT_ARRAY_EQ("12345", dest, 6);
+ strncpy(dest, "testtesttest", 10);
+ TEST_ASSERT_ARRAY_EQ("testtestte", dest, 10);
+
+ return EC_SUCCESS;
+}
+
+static int test_strncmp(void)
+{
+ TEST_ASSERT(strncmp("123", "123", 8) == 0);
+ TEST_ASSERT(strncmp("789", "456", 8) > 0);
+ TEST_ASSERT(strncmp("abc", "abd", 4) < 0);
+ TEST_ASSERT(strncmp("abc", "abd", 2) == 0);
+ return EC_SUCCESS;
+}
+
+static int test_strlen(void)
+{
+ TEST_CHECK(strlen("this is a string") == 16);
+}
+
+static int test_strnlen(void)
+{
+ TEST_ASSERT(strnlen("this is a string", 17) == 16);
+ TEST_ASSERT(strnlen("this is a string", 16) == 16);
+ TEST_ASSERT(strnlen("this is a string", 5) == 5);
+
+ return EC_SUCCESS;
+}
+
+static int test_strcasecmp(void)
+{
+ TEST_CHECK((strcasecmp("test string", "TEST strIng") == 0) &&
+ (strcasecmp("test123!@#", "TesT123!@#") == 0) &&
+ (strcasecmp("lower", "UPPER") != 0));
+}
+
+static int test_strncasecmp(void)
+{
+ TEST_CHECK((strncasecmp("test string", "TEST str", 4) == 0) &&
+ (strncasecmp("test string", "TEST str", 8) == 0) &&
+ (strncasecmp("test123!@#", "TesT321!@#", 5) != 0) &&
+ (strncasecmp("test123!@#", "TesT321!@#", 4) == 0) &&
+ (strncasecmp("1test123!@#", "1TesT321!@#", 5) == 0) &&
+ (strncasecmp("1test123", "teststr", 0) == 0));
+}
+
+static int test_atoi(void)
+{
+ TEST_CHECK((atoi(" 901") == 901) &&
+ (atoi("-12c") == -12) &&
+ (atoi(" 0 ") == 0) &&
+ (atoi("\t111") == 111));
+}
+
+void run_test(void)
+{
+ test_reset();
+
+ RUN_TEST(test_isalpha);
+ RUN_TEST(test_isprint);
+ RUN_TEST(test_strtoi);
+ RUN_TEST(test_parse_bool);
+ RUN_TEST(test_strzcpy);
+ RUN_TEST(test_strncpy);
+ RUN_TEST(test_strncmp);
+ RUN_TEST(test_strlen);
+ RUN_TEST(test_strnlen);
+ RUN_TEST(test_strcasecmp);
+ RUN_TEST(test_strncasecmp);
+ RUN_TEST(test_atoi);
+
+ test_print_result();
+}
diff --git a/test/utils_str.tasklist b/test/utils_str.tasklist
new file mode 100644
index 0000000000..e241aab4bb
--- /dev/null
+++ b/test/utils_str.tasklist
@@ -0,0 +1,17 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ *
+ * For each task, use the macro TASK_TEST(n, r, d, s) where :
+ * 'n' in the name of the task
+ * 'r' in the main routine of the task
+ * 'd' in an opaque parameter passed to the routine at startup
+ * 's' is the stack size in bytes; must be a multiple of 8
+ */
+#define CONFIG_TEST_TASK_LIST /* No test task */
diff --git a/test/vboot.c b/test/vboot.c
new file mode 100644
index 0000000000..69521eff6e
--- /dev/null
+++ b/test/vboot.c
@@ -0,0 +1,142 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Test vboot
+ */
+
+#include <stdlib.h>
+#include "common.h"
+#include "rsa.h"
+#include "test_util.h"
+#include "vboot.h"
+#include "rsa2048-3.h"
+#include "rwsig.h"
+
+struct vboot_key {
+ struct vb21_packed_key vb21_key;
+ struct rsa_public_key key_data;
+};
+
+struct vboot_sig {
+ struct vb21_signature vb21_sig;
+ uint8_t sig_data[RSANUMBYTES];
+};
+
+static void reset_data(struct vboot_key *k, struct vboot_sig *s)
+{
+ k->vb21_key.c.magic = VB21_MAGIC_PACKED_KEY;
+ k->vb21_key.key_offset = sizeof(struct vb21_packed_key);
+ k->vb21_key.key_size = sizeof(rsa_data);
+ memcpy(&k->key_data, rsa_data, sizeof(rsa_data));
+
+ s->vb21_sig.c.magic = VB21_MAGIC_SIGNATURE;
+ s->vb21_sig.sig_size = RSANUMBYTES;
+ s->vb21_sig.sig_offset = sizeof(struct vb21_signature);
+ s->vb21_sig.sig_alg = k->vb21_key.sig_alg;
+ s->vb21_sig.hash_alg = k->vb21_key.hash_alg;
+ s->vb21_sig.data_size = CONFIG_RW_SIZE - CONFIG_RW_SIG_SIZE - 32;
+ memcpy(s->sig_data, sig, sizeof(s->sig_data));
+}
+
+static int test_vboot(void)
+{
+ struct vboot_key k;
+ struct vboot_sig s;
+ uint8_t data[CONFIG_RW_SIZE];
+ int len;
+ int err;
+
+ /* Success */
+ reset_data(&k, &s);
+ memset(data, 0xff, CONFIG_RW_SIZE);
+ err = vb21_is_packed_key_valid(&k.vb21_key);
+ TEST_ASSERT(err == EC_SUCCESS);
+ err = vb21_is_signature_valid(&s.vb21_sig, &k.vb21_key);
+ TEST_ASSERT(err == EC_SUCCESS);
+ len = s.vb21_sig.data_size;
+ err = vboot_is_padding_valid(data, len,
+ CONFIG_RW_SIZE - CONFIG_RW_SIG_SIZE);
+ TEST_ASSERT(err == EC_SUCCESS);
+
+ /* Invalid magic */
+ reset_data(&k, &s);
+ k.vb21_key.c.magic = VB21_MAGIC_SIGNATURE;
+ err = vb21_is_packed_key_valid(&k.vb21_key);
+ TEST_ASSERT(err == EC_ERROR_VBOOT_KEY_MAGIC);
+
+ /* Invalid key size */
+ reset_data(&k, &s);
+ k.vb21_key.key_size--;
+ err = vb21_is_packed_key_valid(&k.vb21_key);
+ TEST_ASSERT(err == EC_ERROR_VBOOT_KEY_SIZE);
+
+ /* Invalid magic */
+ reset_data(&k, &s);
+ s.vb21_sig.c.magic = VB21_MAGIC_PACKED_KEY;
+ err = vb21_is_signature_valid(&s.vb21_sig, &k.vb21_key);
+ TEST_ASSERT(err == EC_ERROR_VBOOT_SIG_MAGIC);
+
+ /* Invalid sig size */
+ reset_data(&k, &s);
+ s.vb21_sig.sig_size--;
+ err = vb21_is_signature_valid(&s.vb21_sig, &k.vb21_key);
+ TEST_ASSERT(err == EC_ERROR_VBOOT_SIG_SIZE);
+
+ /* Sig algorithm mismatch */
+ reset_data(&k, &s);
+ s.vb21_sig.sig_alg++;
+ err = vb21_is_signature_valid(&s.vb21_sig, &k.vb21_key);
+ TEST_ASSERT(err == EC_ERROR_VBOOT_SIG_ALGORITHM);
+
+ /* Hash algorithm mismatch */
+ reset_data(&k, &s);
+ s.vb21_sig.hash_alg++;
+ err = vb21_is_signature_valid(&s.vb21_sig, &k.vb21_key);
+ TEST_ASSERT(err == EC_ERROR_VBOOT_HASH_ALGORITHM);
+
+ /* Invalid sig_offset */
+ reset_data(&k, &s);
+ s.vb21_sig.sig_offset--;
+ err = vb21_is_signature_valid(&s.vb21_sig, &k.vb21_key);
+ TEST_ASSERT(err == EC_ERROR_VBOOT_SIG_OFFSET);
+
+ /* Invalid data size */
+ reset_data(&k, &s);
+ s.vb21_sig.data_size = CONFIG_RW_SIZE;
+ err = vb21_is_signature_valid(&s.vb21_sig, &k.vb21_key);
+ TEST_ASSERT(err == EC_ERROR_VBOOT_DATA_SIZE);
+
+ /* Invalid padding */
+ reset_data(&k, &s);
+ len = s.vb21_sig.data_size;
+ data[len] = 0;
+ err = vboot_is_padding_valid(data, len,
+ CONFIG_RW_SIZE - CONFIG_RW_SIG_SIZE);
+ TEST_ASSERT(err == EC_ERROR_INVAL);
+
+ /* Invalid padding size */
+ reset_data(&k, &s);
+ len = s.vb21_sig.data_size + 1;
+ err = vboot_is_padding_valid(data, len,
+ CONFIG_RW_SIZE - CONFIG_RW_SIG_SIZE);
+ TEST_ASSERT(err == EC_ERROR_INVAL);
+
+ /* Padding size is too large */
+ reset_data(&k, &s);
+ len = s.vb21_sig.data_size + 64;
+ err = vboot_is_padding_valid(data, len,
+ CONFIG_RW_SIZE - CONFIG_RW_SIG_SIZE);
+ TEST_ASSERT(err == EC_ERROR_INVAL);
+
+ return EC_SUCCESS;
+}
+
+void run_test(void)
+{
+ test_reset();
+
+ RUN_TEST(test_vboot);
+
+ test_print_result();
+}
diff --git a/test/vboot.tasklist b/test/vboot.tasklist
new file mode 100644
index 0000000000..e241aab4bb
--- /dev/null
+++ b/test/vboot.tasklist
@@ -0,0 +1,17 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ *
+ * For each task, use the macro TASK_TEST(n, r, d, s) where :
+ * 'n' in the name of the task
+ * 'r' in the main routine of the task
+ * 'd' in an opaque parameter passed to the routine at startup
+ * 's' is the stack size in bytes; must be a multiple of 8
+ */
+#define CONFIG_TEST_TASK_LIST /* No test task */
diff --git a/test/x25519.c b/test/x25519.c
new file mode 100644
index 0000000000..96f0de0287
--- /dev/null
+++ b/test/x25519.c
@@ -0,0 +1,197 @@
+/* Copyright (c) 2015, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include "console.h"
+#include "common.h"
+#include "curve25519.h"
+#include "test_util.h"
+#include "timer.h"
+#include "util.h"
+#include "watchdog.h"
+
+/*
+ * Define this to test 1 million iterations of x25519 (takes up to
+ * a few minutes on host, up to a few days on microcontroller).
+ */
+#undef TEST_X25519_1M_ITERATIONS
+
+static int test_x25519(void)
+{
+ /* Taken from https://tools.ietf.org/html/rfc7748#section-5.2 */
+ static const uint8_t scalar1[32] = {
+ 0xa5, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d,
+ 0x3b, 0x16, 0x15, 0x4b, 0x82, 0x46, 0x5e, 0xdd,
+ 0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc, 0x5a, 0x18,
+ 0x50, 0x6a, 0x22, 0x44, 0xba, 0x44, 0x9a, 0xc4,
+ };
+ static const uint8_t point1[32] = {
+ 0xe6, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb,
+ 0x35, 0x94, 0xc1, 0xa4, 0x24, 0xb1, 0x5f, 0x7c,
+ 0x72, 0x66, 0x24, 0xec, 0x26, 0xb3, 0x35, 0x3b,
+ 0x10, 0xa9, 0x03, 0xa6, 0xd0, 0xab, 0x1c, 0x4c,
+ };
+ static const uint8_t expected1[32] = {
+ 0xc3, 0xda, 0x55, 0x37, 0x9d, 0xe9, 0xc6, 0x90,
+ 0x8e, 0x94, 0xea, 0x4d, 0xf2, 0x8d, 0x08, 0x4f,
+ 0x32, 0xec, 0xcf, 0x03, 0x49, 0x1c, 0x71, 0xf7,
+ 0x54, 0xb4, 0x07, 0x55, 0x77, 0xa2, 0x85, 0x52,
+ };
+ static const uint8_t scalar2[32] = {
+ 0x4b, 0x66, 0xe9, 0xd4, 0xd1, 0xb4, 0x67, 0x3c,
+ 0x5a, 0xd2, 0x26, 0x91, 0x95, 0x7d, 0x6a, 0xf5,
+ 0xc1, 0x1b, 0x64, 0x21, 0xe0, 0xea, 0x01, 0xd4,
+ 0x2c, 0xa4, 0x16, 0x9e, 0x79, 0x18, 0xba, 0x0d,
+ };
+ static const uint8_t point2[32] = {
+ 0xe5, 0x21, 0x0f, 0x12, 0x78, 0x68, 0x11, 0xd3,
+ 0xf4, 0xb7, 0x95, 0x9d, 0x05, 0x38, 0xae, 0x2c,
+ 0x31, 0xdb, 0xe7, 0x10, 0x6f, 0xc0, 0x3c, 0x3e,
+ 0xfc, 0x4c, 0xd5, 0x49, 0xc7, 0x15, 0xa4, 0x93,
+ };
+ static const uint8_t expected2[32] = {
+ 0x95, 0xcb, 0xde, 0x94, 0x76, 0xe8, 0x90, 0x7d,
+ 0x7a, 0xad, 0xe4, 0x5c, 0xb4, 0xb8, 0x73, 0xf8,
+ 0x8b, 0x59, 0x5a, 0x68, 0x79, 0x9f, 0xa1, 0x52,
+ 0xe6, 0xf8, 0xf7, 0x64, 0x7a, 0xac, 0x79, 0x57,
+ };
+ uint8_t out[32];
+
+ X25519(out, scalar1, point1);
+
+ if (memcmp(expected1, out, sizeof(out)) != 0) {
+ ccprintf("X25519 test one failed.\n");
+ return 0;
+ }
+
+
+ X25519(out, scalar2, point2);
+
+ if (memcmp(expected2, out, sizeof(out)) != 0) {
+ ccprintf("X25519 test two failed.\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int test_x25519_small_order(void)
+{
+ static const uint8_t kSmallOrderPoint[32] = {
+ 0xe0, 0xeb, 0x7a, 0x7c, 0x3b, 0x41, 0xb8, 0xae,
+ 0x16, 0x56, 0xe3, 0xfa, 0xf1, 0x9f, 0xc4, 0x6a,
+ 0xda, 0x09, 0x8d, 0xeb, 0x9c, 0x32, 0xb1, 0xfd,
+ 0x86, 0x62, 0x05, 0x16, 0x5f, 0x49, 0xb8,
+ };
+ uint8_t out[32], private_key[32];
+
+ memset(private_key, 0x11, sizeof(private_key));
+
+ if (X25519(out, private_key, kSmallOrderPoint)) {
+ ccprintf("X25519 returned success with a small-order input.\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int test_x25519_iterated(void)
+{
+ /* Taken from https://tools.ietf.org/html/rfc7748#section-5.2 */
+ static const uint8_t expected_1K[32] = {
+ 0x68, 0x4c, 0xf5, 0x9b, 0xa8, 0x33, 0x09, 0x55,
+ 0x28, 0x00, 0xef, 0x56, 0x6f, 0x2f, 0x4d, 0x3c,
+ 0x1c, 0x38, 0x87, 0xc4, 0x93, 0x60, 0xe3, 0x87,
+ 0x5f, 0x2e, 0xb9, 0x4d, 0x99, 0x53, 0x2c, 0x51,
+ };
+#ifdef TEST_X25519_1M_ITERATIONS
+ static const uint8_t expected_1M[32] = {
+ 0x7c, 0x39, 0x11, 0xe0, 0xab, 0x25, 0x86, 0xfd,
+ 0x86, 0x44, 0x97, 0x29, 0x7e, 0x57, 0x5e, 0x6f,
+ 0x3b, 0xc6, 0x01, 0xc0, 0x88, 0x3c, 0x30, 0xdf,
+ 0x5f, 0x4d, 0xd2, 0xd2, 0x4f, 0x66, 0x54, 0x24
+ };
+#endif
+ uint8_t scalar[32] = {9}, point[32] = {9}, out[32];
+ unsigned i;
+
+ for (i = 0; i < 1000; i++) {
+ watchdog_reload();
+ X25519(out, scalar, point);
+ memcpy(point, scalar, sizeof(point));
+ memcpy(scalar, out, sizeof(scalar));
+ }
+
+ if (memcmp(expected_1K, scalar, sizeof(expected_1K)) != 0) {
+ ccprintf("1,000 iterations X25519 test failed\n");
+ return 0;
+ }
+
+#ifdef TEST_X25519_1M_ITERATIONS
+ for (; i < 1000000; i++) {
+ watchdog_reload();
+ X25519(out, scalar, point);
+ memcpy(point, scalar, sizeof(point));
+ memcpy(scalar, out, sizeof(scalar));
+ if ((i % 10000) == 0)
+ ccprints("%d", i);
+ }
+
+ if (memcmp(expected_1M, scalar, sizeof(expected_1M)) != 0) {
+ ccprintf("1,000,000 iterations X25519 test failed\n");
+ return 0;
+ }
+#endif
+
+ return 1;
+}
+
+static void test_x25519_speed(void)
+{
+ static const uint8_t scalar1[32] = {
+ 0xa5, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d,
+ 0x3b, 0x16, 0x15, 0x4b, 0x82, 0x46, 0x5e, 0xdd,
+ 0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc, 0x5a, 0x18,
+ 0x50, 0x6a, 0x22, 0x44, 0xba, 0x44, 0x9a, 0xc4,
+ };
+ static const uint8_t point1[32] = {
+ 0xe6, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb,
+ 0x35, 0x94, 0xc1, 0xa4, 0x24, 0xb1, 0x5f, 0x7c,
+ 0x72, 0x66, 0x24, 0xec, 0x26, 0xb3, 0x35, 0x3b,
+ 0x10, 0xa9, 0x03, 0xa6, 0xd0, 0xab, 0x1c, 0x4c,
+ };
+ uint8_t out[32];
+ timestamp_t t0, t1;
+
+ X25519(out, scalar1, point1);
+ t0 = get_time();
+ X25519(out, scalar1, point1);
+ t1 = get_time();
+ ccprintf("X25519 duration %ld us\n", t1.val - t0.val);
+}
+
+void run_test(void)
+{
+ watchdog_reload();
+ /* do not check speed, just as a benchmark */
+ test_x25519_speed();
+
+ watchdog_reload();
+ if (!test_x25519() || !test_x25519_iterated() ||
+ !test_x25519_small_order()) {
+ test_fail();
+ return;
+ }
+
+ test_pass();
+}
diff --git a/test/x25519.tasklist b/test/x25519.tasklist
new file mode 100644
index 0000000000..e76178ba0a
--- /dev/null
+++ b/test/x25519.tasklist
@@ -0,0 +1,17 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ *
+ * For each task, use the macro TASK_TEST(n, r, d, s) where :
+ * 'n' in the name of the task
+ * 'r' in the main routine of the task
+ * 'd' in an opaque parameter passed to the routine at startup
+ * 's' is the stack size in bytes; must be a multiple of 8
+ */
+#define CONFIG_TEST_TASK_LIST
diff --git a/util/build.mk b/util/build.mk
index 89772f9746..c7a3b5b522 100644
--- a/util/build.mk
+++ b/util/build.mk
@@ -9,6 +9,7 @@
host-util-bin=ectool lbplay stm32mon ec_sb_firmware_update lbcc \
ec_parse_panicinfo
build-util-bin=ec_uartd iteflash
+build-util-art+=util/export_taskinfo.so
ifeq ($(CHIP),npcx)
build-util-bin+=ecst
endif
@@ -22,3 +23,43 @@ ec_sb_firmware_update-objs+=powerd_lock.o
lbplay-objs=lbplay.o $(comm-objs)
ec_parse_panicinfo-objs=ec_parse_panicinfo.o ec_panicinfo.o
+
+# USB type-C Vendor Information File generation
+ifeq ($(CONFIG_USB_POWER_DELIVERY),y)
+build-util-bin+=genvif
+build-util-art+=$(BOARD)_vif.txt
+$(out)/util/genvif: $(out)/util/usb_pd_policy.o board/$(BOARD)/board.h \
+ include/usb_pd.h include/usb_pd_tcpm.h
+$(out)/util/genvif: BUILD_LDFLAGS+=$(out)/util/usb_pd_policy.o -flto
+
+STANDALONE_FLAGS=-ffreestanding -fno-builtin -nostdinc \
+ -Ibuiltin/ -D"__keep= " -DVIF_BUILD
+$(out)/util/usb_pd_policy.o: board/$(BOARD)/usb_pd_policy.c
+ $(call quiet,c_to_vif,BUILDCC)
+deps-$(CONFIG_USB_POWER_DELIVERY) += $(out)/util/usb_pd_policy.o.d
+endif # CONFIG_USB_POWER_DELIVERY
+
+ifneq ($(CONFIG_TOUCHPAD_HASH_FW),)
+build-util-bin += gen_touchpad_hash
+
+# Assume RW section (touchpad FW must be identical for both RO+RW)
+$(out)/util/gen_touchpad_hash: BUILD_LDFLAGS += -DSECTION_IS_RW
+
+OPENSSL_CFLAGS := $(shell $(PKG_CONFIG) --libs openssl)
+OPENSSL_LDFLAGS := $(shell $(PKG_CONFIG) --libs openssl)
+
+$(out)/util/gen_touchpad_hash: BUILD_CFLAGS += $(OPENSSL_CFLAGS)
+$(out)/util/gen_touchpad_hash: BUILD_LDFLAGS += $(OPENSSL_LDFLAGS)
+endif # CONFIG_TOUCHPAD_VIRTUAL_OFF
+
+$(out)/util/export_taskinfo.so: $(out)/util/export_taskinfo_ro.o \
+ $(out)/util/export_taskinfo_rw.o
+ $(call quiet,link_taskinfo,BUILDLD)
+
+$(out)/util/export_taskinfo_ro.o: util/export_taskinfo.c
+ $(call quiet,c_to_taskinfo,BUILDCC,RO)
+
+$(out)/util/export_taskinfo_rw.o: util/export_taskinfo.c
+ $(call quiet,c_to_taskinfo,BUILDCC,RW)
+
+deps-y += $(out)/util/export_taskinfo_ro.o.d $(out)/util/export_taskinfo_rw.o.d
diff --git a/util/comm-host.c b/util/comm-host.c
index 31ad56343e..9fc65ab634 100644
--- a/util/comm-host.c
+++ b/util/comm-host.c
@@ -10,6 +10,7 @@
#include <string.h>
#include "comm-host.h"
+#include "cros_ec_dev.h"
#include "ec_commands.h"
#include "misc_util.h"
@@ -96,6 +97,11 @@ int comm_init(int interfaces, const char *device_name)
!comm_init_dev(device_name))
goto init_ok;
+ /* Do not fallback to other communication methods if target is not a
+ * cros_ec device */
+ if (strcmp(CROS_EC_DEV_NAME, device_name))
+ goto init_failed;
+
/* Fallback to direct LPC on x86 */
if ((interfaces & COMM_LPC) && comm_init_lpc && !comm_init_lpc())
goto init_ok;
@@ -104,6 +110,7 @@ int comm_init(int interfaces, const char *device_name)
if ((interfaces & COMM_I2C) && comm_init_i2c && !comm_init_i2c())
goto init_ok;
+ init_failed:
/* Give up */
fprintf(stderr, "Unable to establish host communication\n");
return 1;
diff --git a/util/ec3po/console.py b/util/ec3po/console.py
index e5f888af0e..2cc2517582 100755
--- a/util/ec3po/console.py
+++ b/util/ec3po/console.py
@@ -517,7 +517,7 @@ class Console(object):
# Reset the input buffer.
self.input_buffer = ''
self.input_buffer_pos = 0
- self.logger.debug('Reset input buffer.')
+ self.logger.log(1, 'Reset input buffer.')
return
# Keep handling the ESC sequence if we're in the middle of it.
diff --git a/util/ec3po/console_unittest.py b/util/ec3po/console_unittest.py
index 82bf4ba212..768417df8f 100755
--- a/util/ec3po/console_unittest.py
+++ b/util/ec3po/console_unittest.py
@@ -587,7 +587,7 @@ class TestConsoleEditingMethods(unittest.TestCase):
# Perform the same verification.
CheckInputBufferPosition(self, len(test_str))
- # We expect to see the test string, a jump to the begining of the line, and
+ # We expect to see the test string, a jump to the beginning of the line, and
# one jump to the end of the line.
exp_console_out = test_str
# Jump to beginning.
diff --git a/util/ec3po/interpreter.py b/util/ec3po/interpreter.py
index 73123b746a..9a0880f49c 100644
--- a/util/ec3po/interpreter.py
+++ b/util/ec3po/interpreter.py
@@ -133,7 +133,7 @@ class Interpreter(object):
command: A string which contains the command to be sent.
"""
self.ec_cmd_queue.put(command)
- self.logger.debug('Commands now in queue: %d', self.ec_cmd_queue.qsize())
+ self.logger.log(1, 'Commands now in queue: %d', self.ec_cmd_queue.qsize())
# Add the EC UART as an output to be serviced.
if self.connected and self.ec_uart_pty not in self.outputs:
@@ -229,7 +229,7 @@ class Interpreter(object):
return
# Ignore any other commands while in the disconnected state.
- self.logger.debug('command: \'%s\'', command)
+ self.logger.log(1, 'command: \'%s\'', command)
if not self.connected:
self.logger.debug('Ignoring command because currently disconnected.')
return
@@ -300,7 +300,7 @@ class Interpreter(object):
# Send the command.
self.ec_uart_pty.write(cmd)
self.ec_uart_pty.flush()
- self.logger.debug('Sent command to EC.')
+ self.logger.log(1, 'Sent command to EC.')
if self.enhanced_ec and cmd != EC_SYN:
# Now, that we've sent the command, store the current command as the last
@@ -320,10 +320,10 @@ class Interpreter(object):
def HandleECData(self):
"""Handle any debug prints from the EC."""
- self.logger.debug('EC has data')
+ self.logger.log(1, 'EC has data')
# Read what the EC sent us.
data = os.read(self.ec_uart_pty.fileno(), EC_MAX_READ)
- self.logger.debug('got: \'%s\'', binascii.hexlify(data))
+ self.logger.log(1, 'got: \'%s\'', binascii.hexlify(data))
if '&E' in data and self.enhanced_ec:
# We received an error, so we should retry it if possible.
self.logger.warning('Error string found in data.')
@@ -341,12 +341,12 @@ class Interpreter(object):
# Done interrogating.
self.interrogating = False
# For now, just forward everything the EC sends us.
- self.logger.debug('Forwarding to user...')
+ self.logger.log(1, 'Forwarding to user...')
self.dbg_pipe.send(data)
def HandleUserData(self):
"""Handle any incoming commands from the user."""
- self.logger.debug('Command data available. Begin processing.')
+ self.logger.log(1, 'Command data available. Begin processing.')
data = self.cmd_pipe.recv()
# Process the command.
self.ProcessCommand(data)
diff --git a/util/ec_sb_firmware_update.c b/util/ec_sb_firmware_update.c
index 538d575f07..54b7ff1567 100644
--- a/util/ec_sb_firmware_update.c
+++ b/util/ec_sb_firmware_update.c
@@ -72,7 +72,8 @@ enum {
F_UPDATE = 0x4, /* do firmware update */
F_NEED_UPDATE = 0x8, /* need firmware update */
F_POWERD_DISABLED = 0x10, /* powerd is disabled */
- F_LFCC_ZERO = 0x20 /* last full charge is zero */
+ F_LFCC_ZERO = 0x20, /* last full charge is zero */
+ F_BATT_DISCHARGE = 0x40 /* battery discharging */
};
struct fw_update_ctrl {
@@ -471,6 +472,20 @@ static enum fw_update_state s1_read_battery_info(
"Require AC Adapter Counnected.");
return S10_TERMINAL;
}
+
+ if ((fw_update->flags & F_BATT_DISCHARGE) &&
+ (fw_update->flags & F_AC_PRESENT)) {
+ /*
+ * If battery discharge due to battery learning mode,
+ * we can't update battery FW, because device will shutdown
+ * during FW update.
+ */
+ fw_update->rv = 0;
+ log_msg(fw_update, S1_READ_INFO,
+ "battery can't update in learning mode");
+ return S10_TERMINAL;
+ }
+
return S2_WRITE_PREPARE;
}
@@ -790,6 +805,11 @@ int main(int argc, char *argv[])
printf("AC_PRESENT\n");
}
}
+
+ if (val & EC_BATT_FLAG_DISCHARGING) {
+ fw_update.flags |= F_BATT_DISCHARGE;
+ printf("Battery is in discharge state\n");
+ }
rv = ec_readmem(EC_MEMMAP_BATT_LFCC, sizeof(val), &val);
if (rv <= 0) {
printf("EC Memmap read error:%d\n", rv);
diff --git a/util/ecst.c b/util/ecst.c
index 9a6632c71a..142160ead0 100755
--- a/util/ecst.c
+++ b/util/ecst.c
@@ -30,10 +30,15 @@ int is_ptr_merge;
unsigned int g_ram_start_address;
unsigned int g_ram_size;
unsigned int api_file_size_bytes;
+int is_mrider15 = FALSE;
+
/* Chips information, RAM start address and RAM size. */
struct chip_info chip_info[] = {{NPCX5M5G_RAM_ADDR, NPCX5M5G_RAM_SIZE},
- {NPCX5M6G_RAM_ADDR, NPCX5M6G_RAM_SIZE} };
+ {NPCX5M6G_RAM_ADDR, NPCX5M6G_RAM_SIZE},
+ {NPCX7M5X_RAM_ADDR, NPCX7M5X_RAM_SIZE},
+ {NPCX7M6X_RAM_ADDR, NPCX7M6X_RAM_SIZE},
+ {NPCX7M7X_RAM_ADDR, NPCX7M7X_RAM_SIZE},};
static unsigned int calc_api_csum_bin(void);
static unsigned int initialize_crc_32(void);
@@ -66,7 +71,8 @@ int main(int argc, char *argv[])
/* Following variables: common to all modes */
int main_status = TRUE;
unsigned int main_temp = 0L;
- char main_str_temp[TMP_STR_SIZE];
+ char main_str_temp[TMP_STR_SIZE];
+ char *end_ptr;
int arg_num;
int arg_ind;
@@ -77,6 +83,7 @@ int main(int argc, char *argv[])
/* Following variables are used when bin file is provided */
struct tbinparams bin_params;
+
bin_params.bin_params = 0;
input_file_name[0] = '\0';
@@ -91,20 +98,21 @@ int main(int argc, char *argv[])
/* Initialize Global variables */
g_verbose = NO_VERBOSE;
- g_ram_start_address = chip_info[NPCX5M5G].ram_addr;
- g_ram_size = chip_info[NPCX5M5G].ram_size;
+ g_ram_start_address = chip_info[DEFAULT_CHIP].ram_addr;
+ g_ram_size = chip_info[DEFAULT_CHIP].ram_size;
/* Set default values */
g_calc_type = CALC_TYPE_NONE;
- bin_params.spi_max_clk = SPI_MAX_CLOCK_DEAFULT;
- bin_params.spi_read_mode = SPI_READ_MODE_DEAFULT;
+ bin_params.spi_max_clk = SPI_MAX_CLOCK_DEFAULT;
+ bin_params.spi_clk_ratio = 0x00;
+ bin_params.spi_read_mode = SPI_READ_MODE_DEFAULT;
bin_params.fw_load_addr =
- chip_info[NPCX5M5G].ram_addr;
+ chip_info[DEFAULT_CHIP].ram_addr;
bin_params.fw_ep =
- chip_info[NPCX5M5G].ram_addr;
+ chip_info[DEFAULT_CHIP].ram_addr;
bin_params.fw_err_detec_s_addr = FW_CRC_START_ADDR;
bin_params.fw_err_detec_e_addr = FW_CRC_START_ADDR;
- bin_params.flash_size = FLASH_SIZE_DEAFULT;
+ bin_params.flash_size = FLASH_SIZE_DEFAULT;
bin_params.fw_hdr_offset = 0;
ptr_fw_addr = 0x00000000;
@@ -182,10 +190,68 @@ int main(int argc, char *argv[])
"%s",
main_str_temp) != 1)) {
my_printf(TERR, "\nCannot read chip name, ");
- my_printf(TERR, "npcx5m5g or npcx5m6g.\n");
+ my_printf(TERR, "npcx7m7k, npcx7m6f");
+ my_printf(TERR, ", npcx7m6g, npcx7m5g");
+ my_printf(TERR, ", npcx5m5g or npcx5m6g.\n");
main_status = FALSE;
} else {
if (str_cmp_no_case(main_str_temp,
+ "npcx7m7k") == 0) {
+ if ((bin_params.bin_params
+ & BIN_FW_LOAD_START_ADDR) ==
+ 0x00000000)
+ bin_params.fw_load_addr =
+ chip_info[NPCX7M7].ram_addr;
+
+ if ((bin_params.bin_params
+ & BIN_FW_ENTRY_POINT) ==
+ 0x00000000)
+ bin_params.fw_ep =
+ chip_info[NPCX7M7].ram_addr;
+
+ g_ram_start_address =
+ chip_info[NPCX7M7].ram_addr;
+ g_ram_size =
+ chip_info[NPCX7M7].ram_size;
+ } else if ((str_cmp_no_case(main_str_temp,
+ "npcx7m6f") == 0) ||
+ (str_cmp_no_case(main_str_temp,
+ "npcx7m6g") == 0)) {
+ if ((bin_params.bin_params
+ & BIN_FW_LOAD_START_ADDR) ==
+ 0x00000000)
+ bin_params.fw_load_addr =
+ chip_info[NPCX7M6].ram_addr;
+
+ if ((bin_params.bin_params &
+ BIN_FW_ENTRY_POINT) ==
+ 0x00000000)
+ bin_params.fw_ep =
+ chip_info[NPCX7M6].ram_addr;
+
+ g_ram_start_address =
+ chip_info[NPCX7M6].ram_addr;
+ g_ram_size =
+ chip_info[NPCX7M6].ram_size;
+ } else if (str_cmp_no_case(main_str_temp,
+ "npcx7m5g") == 0) {
+ if ((bin_params.bin_params
+ & BIN_FW_LOAD_START_ADDR) ==
+ 0x00000000)
+ bin_params.fw_load_addr =
+ chip_info[NPCX7M5].ram_addr;
+
+ if ((bin_params.bin_params &
+ BIN_FW_ENTRY_POINT) ==
+ 0x00000000)
+ bin_params.fw_ep =
+ chip_info[NPCX7M5].ram_addr;
+
+ g_ram_start_address =
+ chip_info[NPCX7M5].ram_addr;
+ g_ram_size =
+ chip_info[NPCX7M5].ram_size;
+ } else if (str_cmp_no_case(main_str_temp,
"npcx5m5g") == 0) {
if ((bin_params.bin_params
& BIN_FW_LOAD_START_ADDR) ==
@@ -202,6 +268,9 @@ int main(int argc, char *argv[])
chip_info[NPCX5M5G].ram_addr;
g_ram_size =
chip_info[NPCX5M5G].ram_size;
+
+ is_mrider15 = TRUE;
+
} else if (str_cmp_no_case(main_str_temp,
"npcx5m6g") == 0) {
if ((bin_params.bin_params &
@@ -221,13 +290,17 @@ int main(int argc, char *argv[])
g_ram_size =
chip_info[NPCX5M6G].ram_size;
+ is_mrider15 = TRUE;
+
} else {
my_printf(TERR,
"\nInvalid chip name (%s) ",
main_str_temp);
- my_printf(TERR, "should be npcx5m5g ");
- my_printf(TERR, "or npcx5m6g.\n");
- main_status = FALSE;
+ my_printf(TERR, "should be npcx7m7k, ");
+ my_printf(TERR, "npcx7m6f, npcx7m6g, ");
+ my_printf(TERR, "npcx7m5g, npcx5m5g ");
+ my_printf(TERR, " or npcx5m6g.");
+ main_status = FALSE;
}
}
@@ -339,7 +412,7 @@ int main(int argc, char *argv[])
arg_ind++;
bin_params.fw_hdr_offset = main_temp;
}
- /* -spimacclk Get SPI flash mac clock. */
+ /* -spimaxclk Get SPI flash max clock. */
} else if (str_cmp_no_case(hdr_args[arg_ind],
"-spimaxclk") == 0) {
arg_ind++;
@@ -351,8 +424,22 @@ int main(int argc, char *argv[])
main_status = FALSE;
} else
bin_params.spi_max_clk =
+ (unsigned char) main_temp;
+ /* -spiclkratio Get SPI flash max clock ratio. */
+ } else if (str_cmp_no_case(hdr_args[arg_ind],
+ "-spiclkratio") == 0) {
+ arg_ind++;
+ if ((hdr_args[arg_ind] == NULL) ||
+ (sscanf(hdr_args[arg_ind],
+ "%d", &main_temp) != 1)) {
+ my_printf(TERR,
+ "\nCannot read SPI Clock Ratio\n");
+ main_status = FALSE;
+ } else
+ bin_params.spi_clk_ratio =
(unsigned char)main_temp;
- /* spireadmode get SPI read mode. */
+
+ /* spireadmode get SPI read mode. */
} else if (str_cmp_no_case(hdr_args[arg_ind],
"-spireadmode") == 0) {
arg_ind++;
@@ -385,18 +472,25 @@ int main(int argc, char *argv[])
SPI_QUAD_MODE;
else {
my_printf(TERR,
- "\nInvalid SPI Flash Read "
- "Mode (%s), it should be "
- "normal, singleMode, "
- "dualMode or quadMode !\n",
+ "\nInvalid SPI Flash Read ");
+ my_printf(TERR,
+ "Mode (%s), it should be ",
main_str_temp);
+ my_printf(TERR,
+ "normal, singleMode, ");
+ my_printf(TERR,
+ "dualMode or quadMode !\n");
main_status = FALSE;
}
}
- /* -nofcrc disable FW CRC. */
- } else if (str_cmp_no_case(hdr_args[arg_ind], "-nofcrc") == 0)
- bin_params.bin_params |=
- BIN_FW_CRC_DISABLE;
+
+ }
+ /* -unlimburst enable unlimited burst */
+ else if (str_cmp_no_case(hdr_args[arg_ind], "-unlimburst") == 0)
+ bin_params.bin_params |= BIN_UNLIM_BURST_ENABLE;
+ /* -nofcrc disable FW CRC. */
+ else if (str_cmp_no_case(hdr_args[arg_ind], "-nofcrc") == 0)
+ bin_params.bin_params |= BIN_FW_CRC_DISABLE;
/* -fwloadaddr, Get the FW load address. */
else if (str_cmp_no_case(hdr_args[arg_ind],
@@ -414,10 +508,12 @@ int main(int argc, char *argv[])
if ((main_temp &
ADDR_16_BYTES_ALIGNED_MASK) != 0) {
my_printf(TERR,
- "\nFW load address start "
- "address (0x%08X) is not "
- "16-bytes aligned !\n",
+ "\nFW load address start ");
+ my_printf(TERR,
+ "address (0x%08X) is not ",
main_temp);
+ my_printf(TERR,
+ "16-bytes aligned !\n");
main_status = FALSE;
} else {
bin_params.fw_load_addr =
@@ -431,7 +527,8 @@ int main(int argc, char *argv[])
if ((bin_params.bin_params & BIN_FW_USER_ARM_RESET)
!= 0x00000000) {
my_printf(TERR,
- "\n-fwep not allowed, FW entry point"
+ "\n-fwep not allowed, FW entry point");
+ my_printf(TERR,
" already set using -usearmrst!\n");
main_status = FALSE;
} else {
@@ -462,7 +559,8 @@ int main(int argc, char *argv[])
"%x",
&main_temp) != 1)) {
my_printf(TERR,
- "\nCannot read FW CRC"
+ "\nCannot read FW CRC");
+ my_printf(TERR,
" start address !\n");
main_status = FALSE;
} else {
@@ -478,11 +576,16 @@ int main(int argc, char *argv[])
} else if (str_cmp_no_case(hdr_args[arg_ind],
"-crcsize") == 0) {
arg_ind++;
- if ((hdr_args[arg_ind] == NULL) ||
- (sscanf(hdr_args[arg_ind], "%x", &main_temp)
- != 1)) {
- my_printf(TERR, "\nCannot read FW CRC ");
- my_printf(TERR, "\area size !\n");
+ main_temp = 0x00;
+ if (hdr_args[arg_ind] == NULL)
+ end_ptr = NULL;
+ else
+ main_temp = strtol(hdr_args[arg_ind],
+ &end_ptr, 16);
+
+ if (hdr_args[arg_ind] == end_ptr) {
+ my_printf(TERR,
+ "\nCannot read FW CRC area size !\n");
main_status = FALSE;
} else {
bin_params.fw_err_detec_e_addr =
@@ -566,7 +669,8 @@ int main(int argc, char *argv[])
if ((main_temp & ADDR_16_BYTES_ALIGNED_MASK)
!= 0) {
my_printf(TERR,
- "\nFW Image address (0x%08X)"
+ "\nFW Image address (0x%08X)");
+ my_printf(TERR,
" isn't 16-bytes aligned !\n",
main_temp);
main_status = FALSE;
@@ -591,9 +695,14 @@ int main(int argc, char *argv[])
/* -bhoffset, BootLoader Header Offset (BH location in BT). */
else if (str_cmp_no_case(hdr_args[arg_ind], "-bhoffset") == 0) {
arg_ind++;
- if ((hdr_args[arg_ind] == NULL) ||
- (sscanf(hdr_args[arg_ind], "%x", &main_temp)
- != 1)) {
+ main_temp = 0x00;
+ if (hdr_args[arg_ind] == NULL)
+ end_ptr = NULL;
+ else
+ main_temp = strtol(hdr_args[arg_ind],
+ &end_ptr, 16);
+
+ if (hdr_args[arg_ind] == end_ptr) {
my_printf(TERR, "\nCannot read BootLoader");
my_printf(TERR, " Header Offset !\n");
main_status = FALSE;
@@ -602,18 +711,20 @@ int main(int argc, char *argv[])
if ((main_temp & ADDR_16_BYTES_ALIGNED_MASK)
!= 0) {
my_printf(TERR,
- "\nFW Image address (0x%08X) "
- "is not 16-bytes aligned !\n",
+ "\nFW Image address (0x%08X) ",
main_temp);
- main_status = FALSE;
+ my_printf(TERR,
+ "is not 16-bytes aligned!\n");
}
if (main_temp > MAX_FLASH_SIZE) {
my_printf(TERR,
- "\nFW Image address (0x%08X)"
- " is higher from flash size "
- "(0x%08X) !\n",
- main_temp,
+ "\nFW Image address (0x%08X)",
+ main_temp);
+ my_printf(TERR,
+ " is higher from flash size");
+ my_printf(TERR,
+ " (0x%08X) !\n",
MAX_FLASH_SIZE);
main_status = FALSE;
} else {
@@ -727,7 +838,8 @@ void exit_with_usage(void)
my_printf(TUSG, "\n -argfile <filename> - Arguments file name; ");
my_printf(TUSG, "includes multiple flags");
my_printf(TUSG, "\n -chip <name> - EC Chip Name: ");
- my_printf(TUSG, "npcx5m5g|npcx5m6g (default is npcx5m5g)");
+ my_printf(TUSG, "npcx7m7k|npcx7m6f|npcx7m5g|npcx5m5g|npcx5m6g");
+ my_printf(TUSG, " (default is npcx5m5g)");
my_printf(TUSG, "\n -v - Verbose; prints ");
my_printf(TUSG, "information messages");
my_printf(TUSG, "\n -vv - Super Verbose; prints ");
@@ -741,8 +853,16 @@ void exit_with_usage(void)
my_printf(TUSG, "(default is ON)");
my_printf(TUSG, "\n -spimaxclk <val> - SPI Flash Maximum Clock, in");
my_printf(TUSG, " MHz: 20|25|33|40|50 (default is 20)");
+ my_printf(TUSG, "\n -spiclkratio <val> - Core Clock / SPI Flash ");
+ my_printf(TUSG, "Clocks Ratio: 1 | 2 (default is 1)");
+ my_printf(TUSG, "\n ");
+ my_printf(TUSG, "Note: Not relevant for npcx5mng chips family");
my_printf(TUSG, "\n -spireadmode <type> - SPI Flash Read Mode: ");
my_printf(TUSG, "normal|fast|dual|quad (default is normal)");
+ my_printf(TUSG, "\n -unlimburst - Enable FIU Unlimited ");
+ my_printf(TUSG, "\n ");
+ my_printf(TUSG, "Note: Not relevant for npcx5mng chips family");
+ my_printf(TUSG, "Burst for SPI Flash Accesses (default is disable).");
my_printf(TUSG, "\n -fwloadaddr <addr> - Firmware load start ");
my_printf(TUSG, "address (default is Start-of-RAM)");
my_printf(TUSG, "\n Located in code RAM, ");
@@ -868,22 +988,17 @@ int copy_file_to_file(char *dst_file_name,
*/
void my_printf(int error_level, char *fmt, ...)
{
- char buffer[256];
va_list argptr;
- va_start(argptr, fmt);
- vsprintf(buffer, fmt, argptr);
- va_end(argptr);
if ((g_verbose == NO_VERBOSE) && (error_level == TINF))
return;
- if ((error_level == TDBG) && (g_verbose != SUPER_VERBOSE))
+ if ((g_verbose != SUPER_VERBOSE) && (error_level == TDBG))
return;
- if (error_level == TERR)
- fprintf(stderr, "%s", buffer);
- else
- printf("%s", buffer);
+ va_start(argptr, fmt);
+ vprintf(fmt, argptr);
+ va_end(argptr);
}
/*
@@ -1004,7 +1119,6 @@ int read_from_file(unsigned int offset,
my_printf(TERR, "\nIn read_from_file - %s", print_string);
my_printf(TERR, "\n\nInvalid call to read_from_file\n\n");
return FALSE;
- break;
}
my_printf(TINF, "\nIn read_from_file - %s", print_string);
@@ -1201,12 +1315,12 @@ int main_bin(struct tbinparams binary_params)
if (((int)binary_params.fw_hdr_offset < 0) ||
(binary_params.fw_hdr_offset > bin_file_size_bytes)) {
my_printf(TERR,
- "\nFW header offset 0x%08x (%d) should be in the"
+ "\nFW header offset 0x%08x (%d) should be in the",
+ binary_params.fw_hdr_offset);
+ my_printf(TERR,
" range of 0 and file size (%d).\n",
binary_params.fw_hdr_offset,
- binary_params.fw_hdr_offset,
- bin_file_size_bytes);
- return FALSE;
+ bin_file_size_bytes); return FALSE;
}
/* Get the input directory and input file name. */
@@ -1286,7 +1400,36 @@ int main_bin(struct tbinparams binary_params)
binary_params.spi_max_clk);
my_printf(TERR, "- it should be 20, 25, 33, 40 or 50 MHz");
return FALSE;
+ }
+
+ /* If SPI clock ratio set for MRIDER15, then it is error. */
+ if ((binary_params.spi_clk_ratio != 0x00) && (is_mrider15 == TRUE)) {
+
+ my_printf(TERR, "\nspiclkratio is not relevant for");
+ my_printf(TERR, " npcx5mng chips family !\n");
+
+ return FALSE;
+ }
+
+ /*
+ * In case SPIU clock ratio didn't set by the user,
+ * set it to its default value.
+ */
+ if (binary_params.spi_clk_ratio == 0x00)
+ binary_params.spi_clk_ratio = SPI_CLOCK_RATIO_1_VAL;
+
+ switch (binary_params.spi_clk_ratio) {
+ case SPI_CLOCK_RATIO_1_VAL:
+ tmp_param &= SPI_CLOCK_RATIO_1;
+ break;
+ case SPI_CLOCK_RATIO_2_VAL:
+ tmp_param |= SPI_CLOCK_RATIO_2;
break;
+ default:
+ my_printf(TERR, "\n\nInvalid SPI Core Clock Ratio (%d) ",
+ binary_params.spi_clk_ratio);
+ my_printf(TERR, "- it should be 1 or 2");
+ return FALSE;
}
if (!write_to_file(tmp_param, HDR_SPI_MAX_CLK_OFFSET, 1,
@@ -1294,7 +1437,20 @@ int main_bin(struct tbinparams binary_params)
return FALSE;
/* Write the SPI flash Read Mode. */
- if (!write_to_file(binary_params.spi_read_mode,
+ tmp_param = binary_params.spi_read_mode;
+ /* If needed, set the unlimited burst bit. */
+ if (binary_params.bin_params & BIN_UNLIM_BURST_ENABLE) {
+ if (is_mrider15 == TRUE) {
+
+ my_printf(TERR, "\nunlimburst is not relevant for");
+ my_printf(TERR, " npcx5mng chips family !\n");
+
+ return FALSE;
+ }
+
+ tmp_param |= SPI_UNLIMITED_BURST_ENABLE;
+ }
+ if (!write_to_file(tmp_param,
HDR_SPI_READ_MODE_OFFSET, 1,
"HDR - SPI flash Read Mode "))
return FALSE;
@@ -1319,11 +1475,13 @@ int main_bin(struct tbinparams binary_params)
(g_ram_start_address + g_ram_size)) ||
(binary_params.fw_load_addr < g_ram_start_address)) {
my_printf(TERR,
- "\nFW load address (0x%08x) should be between "
+ "\nFW load address (0x%08x) should be between ",
+ binary_params.fw_load_addr);
+ my_printf(TERR,
"start (0x%08x) and end (0x%08x) of RAM ).",
- binary_params.fw_load_addr,
g_ram_start_address,
(g_ram_start_address + g_ram_size));
+
return FALSE;
}
@@ -1443,8 +1601,8 @@ int main_bin(struct tbinparams binary_params)
binary_params.fw_err_detec_e_addr);
my_printf(TERR,
"than the FW length %d (0x%08x)",
- (binary_params.fw_len - 1),
- (binary_params.fw_len - 1));
+ (binary_params.fw_len),
+ (binary_params.fw_len));
return FALSE;
}
}
@@ -1549,7 +1707,6 @@ int main_bin(struct tbinparams binary_params)
binary_params.flash_size);
my_printf(TERR, " it should be 1, 2, 4, 8 or 16 MBytes\n");
return FALSE;
- break;
}
if (!write_to_file(tmp_param,
HDR_FLASH_SIZE_OFFSET,
diff --git a/util/ecst.h b/util/ecst.h
index d61f95be3b..4bbac5f2ce 100755
--- a/util/ecst.h
+++ b/util/ecst.h
@@ -22,145 +22,165 @@
--------------------------------------------------------------------------*/
/* For the beauty */
-#define TRUE 1
-#define FALSE 0
+#define TRUE 1
+#define FALSE 0
/* CHANGEME when the version is updated */
-#define T_VER 1
-#define T_REV_MAJOR 0
-#define T_REV_MINOR 1
+#define T_VER 1
+#define T_REV_MAJOR 0
+#define T_REV_MINOR 3
/* Header starts by default at 0x20000 */
-#define FIRMWARE_OFFSET_FROM_HEADER 0x40
+#define FIRMWARE_OFFSET_FROM_HEADER 0x40
-#define ARM_FW_ENTRY_POINT_OFFSET 0x04
+#define ARM_FW_ENTRY_POINT_OFFSET 0x04
/* Some useful offsets inside the header */
-#define HDR_ANCHOR_OFFSET 0
-#define HDR_EXTENDED_ANCHOR_OFFSET 4
-#define HDR_SPI_MAX_CLK_OFFSET 6
-#define HDR_SPI_READ_MODE_OFFSET 7
-#define HDR_ERR_DETECTION_CONF_OFFSET 8
-#define HDR_FW_LOAD_START_ADDR_OFFSET 9
-#define HDR_FW_ENTRY_POINT_OFFSET 13
-#define HDR_FW_ERR_DETECT_START_ADDR_OFFSET 17
-#define HDR_FW_ERR_DETECT_END_ADDR_OFFSET 21
-#define HDR_FW_LENGTH_OFFSET 25
-#define HDR_FLASH_SIZE_OFFSET 29
-#define HDR_RESERVED 30
-#define HDR_FW_HEADER_SIG_OFFSET 56
-#define HDR_FW_IMAGE_SIG_OFFSET 60
-
-
-#define FIRMW_CKSM_OFFSET 0x3C
+#define HDR_ANCHOR_OFFSET 0
+#define HDR_EXTENDED_ANCHOR_OFFSET 4
+#define HDR_SPI_MAX_CLK_OFFSET 6
+#define HDR_SPI_READ_MODE_OFFSET 7
+#define HDR_ERR_DETECTION_CONF_OFFSET 8
+#define HDR_FW_LOAD_START_ADDR_OFFSET 9
+#define HDR_FW_ENTRY_POINT_OFFSET 13
+#define HDR_FW_ERR_DETECT_START_ADDR_OFFSET 17
+#define HDR_FW_ERR_DETECT_END_ADDR_OFFSET 21
+#define HDR_FW_LENGTH_OFFSET 25
+#define HDR_FLASH_SIZE_OFFSET 29
+#define HDR_RESERVED 30
+#define HDR_FW_HEADER_SIG_OFFSET 56
+#define HDR_FW_IMAGE_SIG_OFFSET 60
+
+
+#define FIRMW_CKSM_OFFSET 0x3C
/* Header field known values */
-#define FW_HDR_ANCHOR 0x2A3B4D5E
-#define FW_HDR_EXT_ANCHOR_ENABLE 0xAB1E
-#define FW_HDR_EXT_ANCHOR_DISABLE 0x54E1
-#define FW_CRC_DISABLE 0x00
-#define FW_CRC_ENABLE 0x02
-#define HEADER_CRC_FIELDS_SIZE 8
+#define FW_HDR_ANCHOR 0x2A3B4D5E
+#define FW_HDR_EXT_ANCHOR_ENABLE 0xAB1E
+#define FW_HDR_EXT_ANCHOR_DISABLE 0x54E1
+#define FW_CRC_DISABLE 0x00
+#define FW_CRC_ENABLE 0x02
+#define HEADER_CRC_FIELDS_SIZE 8
-#define HDR_PTR_SIGNATURE 0x55AA650E
+#define HDR_PTR_SIGNATURE 0x55AA650E
-#define CKSMCRC_INV_BIT_OFFSET 0x1
+#define CKSMCRC_INV_BIT_OFFSET 0x1
/* Some common Sizes */
-#define STR_SIZE 200
-#define ARG_SIZE 100
-#define NAME_SIZE 160
-#define BUFF_SIZE 0x400
-#define HEADER_SIZE 64
-#define TMP_STR_SIZE 20
-#define PAD_VALUE 0x00
+#define STR_SIZE 200
+#define ARG_SIZE 100
+#define NAME_SIZE 160
+#define BUFF_SIZE 0x400
+#define HEADER_SIZE 64
+#define TMP_STR_SIZE 20
+#define PAD_VALUE 0x00
-#define MAX_ARGS 100
+#define MAX_ARGS 100
/* Text Colors */
-#define TDBG 0x02 /* Dark Green */
-#define TPAS 0x0A /* light green */
-#define TINF 0x0B /* light turquise */
-#define TERR 0x0C /* light red */
-#define TUSG 0x0E /* light yellow */
+#define TDBG 0x02 /* Dark Green */
+#define TPAS 0x0A /* light green */
+#define TINF 0x0B /* light turquise */
+#define TERR 0x0C /* light red */
+#define TUSG 0x0E /* light yellow */
/* Indicates bin Command line parameters */
-#define BIN_FW_HDR_CRC_DISABLE 0x0001
-#define BIN_FW_CRC_DISABLE 0x0002
-#define BIN_FW_START 0x0004
-#define BIN_FW_SIZE 0x0008
-#define BIN_CK_FIRMWARE 0x0010
-#define BIN_FW_CKS_START 0x0020
-#define BIN_FW_CKS_SIZE 0x0040
-#define BIN_FW_CHANGE_SIG 0x0080
-#define BIN_FW_SPI_MAX_CLK 0x0100
-#define BIN_FW_LOAD_START_ADDR 0x0200
-#define BIN_FW_ENTRY_POINT 0x0400
-#define BIN_FW_LENGTH 0x0800
-#define BIN_FW_HDR_OFFSET 0x1000
-#define BIN_FW_USER_ARM_RESET 0x2000
-
-#define ECRP_OFFSET 0x01
-#define ECRP_INPUT_FILE 0x02
-#define ECRP_OUTPUT_FILE 0x04
-
-#define DIR_DELIMITER_STR "/"
-
-#define SPI_MAX_CLOCK_20_MHZ_VAL 20
-#define SPI_MAX_CLOCK_25_MHZ_VAL 25
-#define SPI_MAX_CLOCK_33_MHZ_VAL 33
-#define SPI_MAX_CLOCK_40_MHZ_VAL 40
-#define SPI_MAX_CLOCK_50_MHZ_VAL 50
-
-#define SPI_MAX_CLOCK_20_MHZ 0x00
-#define SPI_MAX_CLOCK_25_MHZ 0x01
-#define SPI_MAX_CLOCK_33_MHZ 0x02
-#define SPI_MAX_CLOCK_40_MHZ 0x03
-#define SPI_MAX_CLOCK_50_MHZ 0x04
-
-
-#define SPI_NORMAL_MODE_VAL "normal"
-#define SPI_SINGLE_MODE_VAL "fast"
-#define SPI_DUAL_MODE_VAL "dual"
-#define SPI_QUAD_MODE_VAL "quad"
-
-#define SPI_NORMAL_MODE 0x00
-#define SPI_SINGLE_MODE 0x01
-#define SPI_DUAL_MODE 0x03
-#define SPI_QUAD_MODE 0x04
-
-#define FLASH_SIZE_1_MBYTES_VAL 1
-#define FLASH_SIZE_2_MBYTES_VAL 2
-#define FLASH_SIZE_4_MBYTES_VAL 4
-#define FLASH_SIZE_8_MBYTES_VAL 8
-#define FLASH_SIZE_16_MBYTES_VAL 16
-
-#define FLASH_SIZE_1_MBYTES 0x01
-#define FLASH_SIZE_2_MBYTES 0x03
-#define FLASH_SIZE_4_MBYTES 0x07
-#define FLASH_SIZE_8_MBYTES 0x0F
-#define FLASH_SIZE_16_MBYTES 0x1F
-
-/* Header fields deafult values. */
-#define SPI_MAX_CLOCK_DEAFULT SPI_MAX_CLOCK_20_MHZ_VAL
-#define SPI_READ_MODE_DEAFULT SPI_NORMAL_MODE
-#define FLASH_SIZE_DEAFULT FLASH_SIZE_16_MBYTES_VAL
-#define FW_CRC_START_ADDR 0x00000000
-
-#define ADDR_16_BYTES_ALIGNED_MASK 0x0000000F
-#define ADDR_4_BYTES_ALIGNED_MASK 0x00000003
-
-#define MAX_FLASH_SIZE 0x03ffffff
+#define BIN_FW_HDR_CRC_DISABLE 0x0001
+#define BIN_FW_CRC_DISABLE 0x0002
+#define BIN_FW_START 0x0004
+#define BIN_FW_SIZE 0x0008
+#define BIN_CK_FIRMWARE 0x0010
+#define BIN_FW_CKS_START 0x0020
+#define BIN_FW_CKS_SIZE 0x0040
+#define BIN_FW_CHANGE_SIG 0x0080
+#define BIN_FW_SPI_MAX_CLK 0x0100
+#define BIN_FW_LOAD_START_ADDR 0x0200
+#define BIN_FW_ENTRY_POINT 0x0400
+#define BIN_FW_LENGTH 0x0800
+#define BIN_FW_HDR_OFFSET 0x1000
+#define BIN_FW_USER_ARM_RESET 0x2000
+#define BIN_UNLIM_BURST_ENABLE 0x4000
+
+#define ECRP_OFFSET 0x01
+#define ECRP_INPUT_FILE 0x02
+#define ECRP_OUTPUT_FILE 0x04
+
+#define DIR_DELIMITER_STR "/"
+
+#define SPI_MAX_CLOCK_20_MHZ_VAL 20
+#define SPI_MAX_CLOCK_25_MHZ_VAL 25
+#define SPI_MAX_CLOCK_33_MHZ_VAL 33
+#define SPI_MAX_CLOCK_40_MHZ_VAL 40
+#define SPI_MAX_CLOCK_50_MHZ_VAL 50
+
+#define SPI_MAX_CLOCK_20_MHZ 0x00
+#define SPI_MAX_CLOCK_25_MHZ 0x01
+#define SPI_MAX_CLOCK_33_MHZ 0x02
+#define SPI_MAX_CLOCK_40_MHZ 0x03
+#define SPI_MAX_CLOCK_50_MHZ 0x04
+#define SPI_MAX_CLOCK_MASK 0xF8
+
+#define SPI_CLOCK_RATIO_1_VAL 1
+#define SPI_CLOCK_RATIO_2_VAL 2
+
+#define SPI_CLOCK_RATIO_1 0x07
+#define SPI_CLOCK_RATIO_2 0x08
+
+#define SPI_NORMAL_MODE_VAL "normal"
+#define SPI_SINGLE_MODE_VAL "fast"
+#define SPI_DUAL_MODE_VAL "dual"
+#define SPI_QUAD_MODE_VAL "quad"
+
+#define SPI_NORMAL_MODE 0x00
+#define SPI_SINGLE_MODE 0x01
+#define SPI_DUAL_MODE 0x03
+#define SPI_QUAD_MODE 0x04
+
+#define SPI_UNLIMITED_BURST_ENABLE 0x08
+
+#define FLASH_SIZE_1_MBYTES_VAL 1
+#define FLASH_SIZE_2_MBYTES_VAL 2
+#define FLASH_SIZE_4_MBYTES_VAL 4
+#define FLASH_SIZE_8_MBYTES_VAL 8
+#define FLASH_SIZE_16_MBYTES_VAL 16
+
+#define FLASH_SIZE_1_MBYTES 0x01
+#define FLASH_SIZE_2_MBYTES 0x03
+#define FLASH_SIZE_4_MBYTES 0x07
+#define FLASH_SIZE_8_MBYTES 0x0F
+#define FLASH_SIZE_16_MBYTES 0x1F
+
+/* Header fields default values. */
+#define SPI_MAX_CLOCK_DEFAULT SPI_MAX_CLOCK_20_MHZ_VAL
+#define SPI_READ_MODE_DEFAULT SPI_NORMAL_MODE
+#define FLASH_SIZE_DEFAULT FLASH_SIZE_16_MBYTES_VAL
+#define FW_CRC_START_ADDR 0x00000000
+
+#define ADDR_16_BYTES_ALIGNED_MASK 0x0000000F
+#define ADDR_4_BYTES_ALIGNED_MASK 0x00000003
+
+#define MAX_FLASH_SIZE 0x03ffffff
/* Chips: convert from name to index. */
-#define NPCX5M5G 0
-#define NPCX5M6G 1
-
-#define NPCX5M5G_RAM_ADDR 0x100A8000
-#define NPCX5M5G_RAM_SIZE 0x20000
-#define NPCX5M6G_RAM_ADDR 0x10088000
-#define NPCX5M6G_RAM_SIZE 0x40000
+#define NPCX5M5G 0
+#define NPCX5M6G 1
+#define NPCX7M5 2
+#define NPCX7M6 3
+#define NPCX7M7 4
+
+#define DEFAULT_CHIP NPCX5M5G
+
+#define NPCX5M5G_RAM_ADDR 0x100A8000
+#define NPCX5M5G_RAM_SIZE 0x20000
+#define NPCX5M6G_RAM_ADDR 0x10088000
+#define NPCX5M6G_RAM_SIZE 0x40000
+#define NPCX7M5X_RAM_ADDR 0x100A8000
+#define NPCX7M5X_RAM_SIZE 0x20000
+#define NPCX7M6X_RAM_ADDR 0x10090000
+#define NPCX7M6X_RAM_SIZE 0x40000
+#define NPCX7M7X_RAM_ADDR 0x10070000
+#define NPCX7M7X_RAM_SIZE 0x60000
/*---------------------------------------------------------------------------
Typedefs
@@ -171,6 +191,7 @@ struct tbinparams {
unsigned int anchor;
unsigned short ext_anchor;
unsigned char spi_max_clk;
+ unsigned char spi_clk_ratio;
unsigned char spi_read_mode;
unsigned char err_detec_cnf;
unsigned int fw_load_addr;
@@ -179,10 +200,10 @@ struct tbinparams {
unsigned int fw_err_detec_e_addr;
unsigned int fw_len;
unsigned int flash_size;
- unsigned int hdr_crc;
- unsigned int fw_crc;
- unsigned int fw_hdr_offset;
- unsigned int bin_params;
+ unsigned int hdr_crc;
+ unsigned int fw_crc;
+ unsigned int fw_hdr_offset;
+ unsigned int bin_params;
} bin_params_struct;
enum verbose_level {
diff --git a/util/ectool.c b/util/ectool.c
index 795e550121..067f934121 100644
--- a/util/ectool.c
+++ b/util/ectool.c
@@ -18,6 +18,7 @@
#include "battery.h"
#include "comm-host.h"
#include "compile_time_macros.h"
+#include "cros_ec_dev.h"
#include "ec_panicinfo.h"
#include "ec_flash.h"
#include "ectool.h"
@@ -102,6 +103,8 @@ const char help_str[] =
" Erases EC flash\n"
" flashinfo\n"
" Prints information on the EC flash\n"
+ " flashspiinfo\n"
+ " Prints information on EC SPI flash, if present\n"
" flashpd <dev_id> <port> <filename>\n"
" Flash commands over PD\n"
" flashprotect [now] [enable | disable]\n"
@@ -110,6 +113,12 @@ const char help_str[] =
" Reads from EC flash to a file\n"
" flashwrite <offset> <infile>\n"
" Writes to EC flash from a file\n"
+ " fpframe\n"
+ " Retrieve the finger image as a PGM image\n"
+ " fpinfo\n"
+ " Prints information about the Fingerprint sensor\n"
+ " fpmode [capture|deepsleep|fingerdown|fingerup]\n"
+ " Configure/Read the fingerprint sensor current mode\n"
" forcelidopen <enable>\n"
" Forces the lid switch to open position\n"
" gpioget <GPIO name>\n"
@@ -152,7 +161,7 @@ const char help_str[] =
" Prints saved panic info\n"
" pause_in_s5 [on|off]\n"
" Whether or not the AP should pause in S5 on shutdown\n"
- " pdcontrol [suspend|resume|reset|disable]\n"
+ " pdcontrol [suspend|resume|reset|disable|on]\n"
" Controls the PD chip\n"
" pdchipinfo <port>\n"
" Get PD chip information\n"
@@ -206,6 +215,10 @@ const char help_str[] =
" Set real-time clock alarm to go off in <sec> seconds\n"
" rwhashpd <dev_id> <HASH[0] ... <HASH[4]>\n"
" Set entry in PD MCU's device rw_hash table.\n"
+ " rwsigaction\n"
+ " Control the behavior of RWSIG task.\n"
+ " rwsigstatus\n"
+ " Run RW signature verification and get status.\n"
" sertest\n"
" Serial output test for COM2\n"
" switches\n"
@@ -218,6 +231,10 @@ const char help_str[] =
" Get the threshold temperature values from the thermal engine.\n"
" thermalset <platform-specific args>\n"
" Set the threshold temperature values for the thermal engine.\n"
+ " tpselftest\n"
+ " Run touchpad self test.\n"
+ " tpframeget\n"
+ " Get touchpad frame data.\n"
" tmp006cal <tmp006_index> [params...]\n"
" Get/set TMP006 calibration\n"
" tmp006raw <tmp006_index>\n"
@@ -250,7 +267,8 @@ BUILD_ASSERT(ARRAY_SIZE(led_color_names) == EC_LED_COLOR_COUNT);
/* Note: depends on enum ec_led_id */
static const char * const led_names[] = {
- "battery", "power", "adapter"};
+ "battery", "power", "adapter", "left", "right", "recovery_hwreinit",
+ "sysrq debug" };
BUILD_ASSERT(ARRAY_SIZE(led_names) == EC_LED_ID_COUNT);
/* Check SBS numerical value range */
@@ -392,13 +410,16 @@ int cmd_hostsleepstate(int argc, char *argv[])
struct ec_params_host_sleep_event p;
if (argc < 2) {
- fprintf(stderr, "Usage: %s [suspend|resume|freeze|thaw]\n",
+ fprintf(stderr, "Usage: %s "
+ "[suspend|wsuspend|resume|freeze|thaw]\n",
argv[0]);
return -1;
}
if (!strcmp(argv[1], "suspend"))
p.sleep_event = HOST_SLEEP_EVENT_S3_SUSPEND;
+ else if (!strcmp(argv[1], "wsuspend"))
+ p.sleep_event = HOST_SLEEP_EVENT_S3_WAKEABLE_SUSPEND;
else if (!strcmp(argv[1], "resume"))
p.sleep_event = HOST_SLEEP_EVENT_S3_RESUME;
else if (!strcmp(argv[1], "freeze"))
@@ -458,16 +479,17 @@ int cmd_s5(int argc, char *argv[])
{
struct ec_params_get_set_value p;
struct ec_params_get_set_value r;
- int rv;
+ int rv, param;
p.flags = 0;
if (argc > 1) {
p.flags |= EC_GSV_SET;
- if (!parse_bool(argv[1], &p.value)) {
+ if (!parse_bool(argv[1], &param)) {
fprintf(stderr, "invalid arg \"%s\"\n", argv[1]);
return -1;
}
+ p.value = param;
}
rv = ec_command(EC_CMD_GSV_PAUSE_IN_S5, 0,
@@ -507,6 +529,8 @@ static const char * const ec_feature_names[] = {
[EC_FEATURE_VSTORE] = "Temporary secure vstore",
[EC_FEATURE_USBC_SS_MUX_VIRTUAL] = "Host-controlled USB-C SS mux",
[EC_FEATURE_RTC] = "Real-time clock",
+ [EC_FEATURE_TOUCHPAD] = "Touchpad",
+ [EC_FEATURE_RWSIG] = "RWSIG task",
};
int cmd_inventory(int argc, char *argv[])
@@ -754,6 +778,33 @@ int cmd_flash_info(int argc, char *argv[])
return 0;
}
+int cmd_flash_spi_info(int argc, char *argv[])
+{
+ struct ec_response_flash_spi_info r;
+ int rv;
+
+ memset(&r, 0, sizeof(r));
+
+ /* Print SPI flash info if available */
+ if (!ec_cmd_version_supported(EC_CMD_FLASH_SPI_INFO, 0)) {
+ printf("EC has no info (does not use SPI flash?)\n");
+ return -1;
+ }
+
+ rv = ec_command(EC_CMD_FLASH_SPI_INFO, 0, NULL, 0, &r, sizeof(r));
+ if (rv < 0)
+ return rv;
+
+ printf("JEDECManufacturerID 0x%02x\n", r.jedec[0]);
+ printf("JEDECDeviceID 0x%02x 0x%02x\n", r.jedec[1], r.jedec[2]);
+ printf("JEDECCapacity %d\n", 1 << r.jedec[2]);
+ printf("ManufacturerID 0x%02x\n", r.mfr_dev_id[0]);
+ printf("DeviceID 0x%02x\n", r.mfr_dev_id[1]);
+ printf("StatusRegister1 0x%02x\n", r.sr1);
+ printf("StatusRegister2 0x%02x\n", r.sr2);
+ return 0;
+}
+
int cmd_flash_read(int argc, char *argv[])
{
int offset, size;
@@ -877,10 +928,18 @@ static void print_flash_protect_flags(const char *desc, uint32_t flags)
printf(" wp_gpio_asserted");
if (flags & EC_FLASH_PROTECT_RO_AT_BOOT)
printf(" ro_at_boot");
+ if (flags & EC_FLASH_PROTECT_RW_AT_BOOT)
+ printf(" rw_at_boot");
+ if (flags & EC_FLASH_PROTECT_ROLLBACK_AT_BOOT)
+ printf(" rollback_at_boot");
if (flags & EC_FLASH_PROTECT_ALL_AT_BOOT)
printf(" all_at_boot");
if (flags & EC_FLASH_PROTECT_RO_NOW)
printf(" ro_now");
+ if (flags & EC_FLASH_PROTECT_RW_NOW)
+ printf(" rw_now");
+ if (flags & EC_FLASH_PROTECT_ROLLBACK_NOW)
+ printf(" rollback_now");
if (flags & EC_FLASH_PROTECT_ALL_NOW)
printf(" all_now");
if (flags & EC_FLASH_PROTECT_ERROR_STUCK)
@@ -981,6 +1040,144 @@ int cmd_rw_hash_pd(int argc, char *argv[])
return rv;
}
+int cmd_rwsig_status(int argc, char *argv[])
+{
+ int rv;
+ struct ec_response_rwsig_check_status resp;
+
+ rv = ec_command(EC_CMD_RWSIG_CHECK_STATUS, 0, NULL, 0,
+ &resp, sizeof(resp));
+ if (rv < 0)
+ return rv;
+
+ printf("RW signature check: %s\n", resp.status ? "OK" : "FAILED");
+
+ return 0;
+}
+
+int cmd_rwsig_action(int argc, char *argv[])
+{
+ struct ec_params_rwsig_action req;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s abort | continue\n", argv[0]);
+ return -1;
+ }
+
+ if (!strcasecmp(argv[1], "abort"))
+ req.action = RWSIG_ACTION_ABORT;
+ else if (!strcasecmp(argv[1], "continue"))
+ req.action = RWSIG_ACTION_CONTINUE;
+ else
+ return -1;
+
+ return ec_command(EC_CMD_RWSIG_ACTION, 0, &req, sizeof(req), NULL, 0);
+}
+
+int cmd_fp_mode(int argc, char *argv[])
+{
+ struct ec_params_fp_mode p;
+ struct ec_response_fp_mode r;
+ uint32_t mode = 0;
+ int i, rv;
+
+ if (argc == 1)
+ mode = FP_MODE_DONT_CHANGE;
+ for (i = 1; i < argc; i++) {
+ if (!strncmp(argv[i], "deepsleep", 9))
+ mode |= FP_MODE_DEEPSLEEP;
+ else if (!strncmp(argv[i], "fingerdown", 10))
+ mode |= FP_MODE_FINGER_DOWN;
+ else if (!strncmp(argv[i], "fingerup", 8))
+ mode |= FP_MODE_FINGER_UP;
+ else if (!strncmp(argv[i], "capture", 7))
+ mode |= FP_MODE_CAPTURE;
+ }
+
+ p.mode = mode;
+ rv = ec_command(EC_CMD_FP_MODE, 0, &p, sizeof(p), &r, sizeof(r));
+ if (rv < 0)
+ return rv;
+
+ printf("FP mode: (0x%x) ", r.mode);
+ if (r.mode & FP_MODE_DEEPSLEEP)
+ printf("deepsleep ");
+ if (r.mode & FP_MODE_FINGER_DOWN)
+ printf("finger-down ");
+ if (r.mode & FP_MODE_FINGER_UP)
+ printf("finger-up ");
+ if (r.mode & FP_MODE_CAPTURE)
+ printf("capture ");
+ printf("\n");
+ return rv;
+}
+
+int cmd_fp_info(int argc, char *argv[])
+{
+ struct ec_response_fp_info r;
+ int rv;
+
+ rv = ec_command(EC_CMD_FP_INFO, 0, NULL, 0, &r, sizeof(r));
+ if (rv < 0)
+ return rv;
+
+ printf("Fingerprint sensor: vendor %x product %x model %x version %x\n",
+ r.vendor_id, r.product_id, r.model_id, r.version);
+ printf("Image: size %dx%d %d bpp\n", r.width, r.height, r.bpp);
+
+ return 0;
+}
+
+int cmd_fp_frame(int argc, char *argv[])
+{
+ struct ec_response_fp_info r;
+ struct ec_params_fp_frame p;
+ int rv = 0;
+ size_t stride, size;
+ uint8_t *buffer8 = ec_inbuf;
+
+ rv = ec_command(EC_CMD_FP_INFO, 0, NULL, 0, &r, sizeof(r));
+ if (rv < 0)
+ return rv;
+
+ stride = (size_t)r.width * r.bpp/8;
+ if (stride > ec_max_insize) {
+ fprintf(stderr, "Not implemented for line size %zu B "
+ "(%u pixels) > EC transfer size %d\n",
+ stride, r.width, ec_max_insize);
+ return -1;
+ }
+ if (r.bpp != 8) {
+ fprintf(stderr, "Not implemented for BPP = %d != 8\n", r.bpp);
+ return -1;
+ }
+
+ size = stride * r.height;
+
+ /* Print 8-bpp PGM ASCII header */
+ printf("P2\n%d %d\n%d\n", r.width, r.height, (1 << r.bpp) - 1);
+
+ p.offset = 0;
+ p.size = stride;
+ while (size) {
+ int x;
+
+ rv = ec_command(EC_CMD_FP_FRAME, 0, &p, sizeof(p),
+ ec_inbuf, stride);
+ if (rv < 0)
+ return rv;
+ p.offset += stride;
+ size -= stride;
+
+ for (x = 0; x < stride; x++)
+ printf("%d ", buffer8[x]);
+ printf("\n");
+ }
+ printf("# END OF FILE\n");
+
+ return 0;
+}
+
/**
* determine if in GFU mode or not.
*
@@ -1130,7 +1327,7 @@ int cmd_flash_pd(int argc, char *argv[])
int rv, fsize, step = 96;
char *e;
char *buf;
- uint32_t *data = &(p->size) + 1;
+ char *data = (char *)p + sizeof(*p);
if (argc < 4) {
fprintf(stderr, "Usage: %s <dev_id> <port> <filename>\n",
@@ -1427,7 +1624,7 @@ int cmd_temperature(int argc, char *argv[])
id);
break;
default:
- printf("%d: %d\n", id,
+ printf("%d: %d K\n", id,
rv + EC_TEMP_SENSOR_OFFSET);
}
}
@@ -1463,7 +1660,7 @@ int cmd_temperature(int argc, char *argv[])
fprintf(stderr, "Sensor not calibrated\n");
return -1;
default:
- printf("%d\n", rv + EC_TEMP_SENSOR_OFFSET);
+ printf("%d K\n", rv + EC_TEMP_SENSOR_OFFSET);
return 0;
}
}
@@ -1723,11 +1920,12 @@ static int get_num_fans(void)
int idx, rv;
struct ec_response_get_features r;
+ /*
+ * iff the EC supports the GET_FEATURES,
+ * check whether it has fan support enabled.
+ */
rv = ec_command(EC_CMD_GET_FEATURES, 0, NULL, 0, &r, sizeof(r));
- if (rv < 0)
- return rv;
-
- if (!(r.flags[0] & (1 << EC_FEATURE_PWM_FAN)))
+ if (rv == EC_SUCCESS && !(r.flags[0] & (1 << EC_FEATURE_PWM_FAN)))
return 0;
for (idx = 0; idx < EC_FAN_SPEED_ENTRIES; idx++) {
@@ -3394,14 +3592,14 @@ static const struct {
uint8_t insize;
} ms_command_sizes[] = {
MS_DUMP_SIZE(),
- MS_SIZES(info),
+ MS_SIZES(info_3),
MS_SIZES(ec_rate),
MS_SIZES(sensor_odr),
MS_SIZES(sensor_range),
MS_SIZES(kb_wake_angle),
MS_SIZES(data),
- MS_SIZES(fifo_flush),
MS_FIFO_INFO_SIZE(),
+ MS_SIZES(fifo_flush),
MS_SIZES(fifo_read),
MS_SIZES(perform_calib),
MS_SIZES(sensor_offset),
@@ -3409,6 +3607,7 @@ static const struct {
MS_SIZES(set_activity),
MS_SIZES(lid_angle),
MS_SIZES(fifo_int_enable),
+ MS_SIZES(spoof),
};
BUILD_ASSERT(ARRAY_SIZE(ms_command_sizes) == MOTIONSENSE_NUM_CMDS);
#undef MS_SIZES
@@ -3427,7 +3626,7 @@ static int ms_help(const char *cmd)
printf(" %s data NUM - read sensor latest data\n",
cmd);
printf(" %s fifo_info - print fifo info\n", cmd);
- printf(" %s fifo_int enable [0/1] - enable/disable/get fifo "
+ printf(" %s fifo_int_enable [0/1] - enable/disable/get fifo "
"interrupt status\n", cmd);
printf(" %s fifo_read MAX_DATA - read fifo data\n", cmd);
printf(" %s fifo_flush NUM - trigger fifo interrupt\n",
@@ -3437,6 +3636,8 @@ static int ms_help(const char *cmd)
printf(" %s set_activity NUM ACT EN - enable/disable activity\n",
cmd);
printf(" %s lid_angle - print lid angle\n", cmd);
+ printf(" %s spoof -- NUM [0/1] [X Y Z] - enable/disable spoofing\n",
+ cmd);
return 0;
}
@@ -3470,8 +3671,8 @@ static int cmd_motionsense(int argc, char **argv)
{ "Motion sensing active", "1"},
};
- /* No motionsense command has more than 5 args. */
- if (argc > 5)
+ /* No motionsense command has more than 7 args. */
+ if (argc > 7)
return ms_help(argv[0]);
if ((argc == 1) ||
@@ -3519,18 +3720,34 @@ static int cmd_motionsense(int argc, char **argv)
}
if (argc == 3 && !strcasecmp(argv[1], "info")) {
- param.cmd = MOTIONSENSE_CMD_INFO;
+ struct ec_params_get_cmd_versions p;
+ struct ec_response_get_cmd_versions r;
+ int version = 0;
+ param.cmd = MOTIONSENSE_CMD_INFO;
param.sensor_odr.sensor_num = strtol(argv[2], &e, 0);
if (e && *e) {
fprintf(stderr, "Bad %s arg.\n", argv[2]);
return -1;
}
- rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 1,
+ /* tool defaults to using latest version of info command */
+ p.cmd = EC_CMD_MOTION_SENSE_CMD;
+ rv = ec_command(EC_CMD_GET_CMD_VERSIONS, 0, &p, sizeof(p),
+ &r, sizeof(r));
+ if (rv < 0) {
+ if (rv == -EC_RES_INVALID_PARAM)
+ printf("Command 0x%02x not supported by EC.\n",
+ EC_CMD_GET_CMD_VERSIONS);
+ return rv;
+ }
+
+ if (r.version_mask)
+ version = __fls(r.version_mask);
+
+ rv = ec_command(EC_CMD_MOTION_SENSE_CMD, version,
&param, ms_command_sizes[param.cmd].outsize,
resp, ms_command_sizes[param.cmd].insize);
-
if (rv < 0)
return rv;
@@ -3554,6 +3771,9 @@ static int cmd_motionsense(int argc, char **argv)
case MOTIONSENSE_TYPE_ACTIVITY:
printf("activity\n");
break;
+ case MOTIONSENSE_TYPE_BARO:
+ printf("barometer\n");
+ break;
default:
printf("unknown\n");
}
@@ -3587,10 +3807,30 @@ static int cmd_motionsense(int argc, char **argv)
case MOTIONSENSE_CHIP_KX022:
printf("kx022\n");
break;
+ case MOTIONSENSE_CHIP_L3GD20H:
+ printf("l3gd20h\n");
+ break;
+ case MOTIONSENSE_CHIP_BMA255:
+ printf("bma255\n");
+ break;
+ case MOTIONSENSE_CHIP_BMP280:
+ printf("bmp280\n");
+ break;
+ case MOTIONSENSE_CHIP_OPT3001:
+ printf("opt3001\n");
+ break;
default:
printf("unknown\n");
}
+ if (version >= 3) {
+ printf("Min Frequency: %d mHz\n",
+ resp->info_3.min_frequency);
+ printf("Max Frequency: %d mHz\n",
+ resp->info_3.max_frequency);
+ printf("FIFO Max Event Count: %d\n",
+ resp->info_3.fifo_max_event_count);
+ }
return 0;
}
@@ -3717,6 +3957,17 @@ static int cmd_motionsense(int argc, char **argv)
}
if (argc == 2 && !strcasecmp(argv[1], "fifo_info")) {
+ int sensor_count;
+
+ param.cmd = MOTIONSENSE_CMD_DUMP;
+ param.dump.max_sensor_count = 0;
+ rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 1,
+ &param, ms_command_sizes[param.cmd].outsize,
+ resp, ms_command_sizes[param.cmd].insize);
+ if (rv < 0)
+ return rv;
+ sensor_count = resp->dump.sensor_count;
+
param.cmd = MOTIONSENSE_CMD_FIFO_INFO;
rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 2,
&param, ms_command_sizes[param.cmd].outsize,
@@ -3728,9 +3979,8 @@ static int cmd_motionsense(int argc, char **argv)
printf("Count: %d\n", resp->fifo_info.count);
printf("Timestamp:%" PRIx32 "\n", resp->fifo_info.timestamp);
printf("Total lost: %d\n", resp->fifo_info.total_lost);
- for (i = 0; i < ECTOOL_MAX_SENSOR; i++) {
- int lost;
- lost = resp->fifo_info.lost[i];
+ for (i = 0; i < sensor_count; i++) {
+ int lost = resp->fifo_info.lost[i];
if (lost != 0)
printf("Lost %d: %d\n", i, lost);
}
@@ -3901,6 +4151,75 @@ static int cmd_motionsense(int argc, char **argv)
return 0;
}
+ if (argc >= 3 && !strcasecmp(argv[1], "spoof")) {
+ param.cmd = MOTIONSENSE_CMD_SPOOF;
+ /* By default, just query the current spoof status. */
+ param.spoof.spoof_enable = MOTIONSENSE_SPOOF_MODE_QUERY;
+ param.spoof.sensor_id = strtol(argv[2], &e, 0);
+ if (e && *e) {
+ fprintf(stderr, "Bad %s arg.\n", argv[2]);
+ return -1;
+ }
+
+ if (argc >= 4) {
+ int enable, i;
+ int16_t val;
+
+ enable = strtol(argv[3], &e, 0);
+ if ((e && *e) || (enable != 0 && enable != 1)) {
+ fprintf(stderr, "Bad %s arg.\n", argv[3]);
+ return -1;
+ }
+
+ if ((enable == 1) && (argc == 4)) {
+ /*
+ * Enable spoofing, but lock to current sensor
+ * values.
+ */
+ param.spoof.spoof_enable =
+ MOTIONSENSE_SPOOF_MODE_LOCK_CURRENT;
+ } else if ((enable == 1) && (argc == 7)) {
+ /*
+ * Enable spoofing, but use provided component
+ * values.
+ */
+ param.spoof.spoof_enable =
+ MOTIONSENSE_SPOOF_MODE_CUSTOM;
+ for (i = 0; i < 3; i++) {
+ val = strtol(argv[4+i], &e, 0);
+ if (e && *e) {
+ fprintf(stderr, "Bad %s arg.\n",
+ argv[4+i]);
+ return -1;
+ }
+ param.spoof.components[i] = val;
+ }
+ } else if (enable == 0) {
+ param.spoof.spoof_enable =
+ MOTIONSENSE_SPOOF_MODE_DISABLE;
+ } else {
+ return ms_help(argv[0]);
+ }
+ }
+
+ rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 2,
+ &param, ms_command_sizes[param.cmd].outsize,
+ resp, ms_command_sizes[param.cmd].insize);
+ if (rv < 0)
+ return rv;
+
+ if (param.spoof.spoof_enable == MOTIONSENSE_SPOOF_MODE_QUERY)
+ /*
+ * Response is the current spoof status of the
+ * sensor.
+ */
+ printf("Sensor %d spoof mode is %s.\n",
+ param.spoof.sensor_id,
+ resp->spoof.ret ? "enabled" : "disabled");
+
+ return 0;
+ }
+
return ms_help(argv[0]);
}
@@ -4095,7 +4414,8 @@ int cmd_usb_mux(int argc, char *argv[])
int cmd_usb_pd(int argc, char *argv[])
{
- const char *role_str[] = {"", "toggle", "toggle-off", "sink", "source"};
+ const char *role_str[] = {"", "toggle", "toggle-off", "sink", "source",
+ "freeze"};
const char *mux_str[] = {"", "none", "usb", "dp", "dock", "auto"};
const char *swap_str[] = {"", "dr_swap", "pr_swap", "vconn_swap"};
struct ec_params_usb_pd_control p;
@@ -5874,6 +6194,7 @@ int cmd_ec_hash(int argc, char *argv[])
char *e;
int rv;
+ memset(&p, 0, sizeof(p));
if (argc < 2) {
/* Get hash status */
p.cmd = EC_VBOOT_HASH_GET;
@@ -5913,7 +6234,7 @@ int cmd_ec_hash(int argc, char *argv[])
p.size = 0;
printf("Hashing EC-RO...\n");
} else if (!strcasecmp(argv[2], "rw")) {
- p.offset = EC_VBOOT_HASH_OFFSET_RW;
+ p.offset = EC_VBOOT_HASH_OFFSET_ACTIVE;
p.size = 0;
printf("Hashing EC-RW...\n");
} else if (argc < 4) {
@@ -6730,12 +7051,23 @@ int cmd_pd_control(int argc, char *argv[])
p.subcmd = PD_RESUME;
else if (!strcmp(argv[1], "disable"))
p.subcmd = PD_CONTROL_DISABLE;
+ else if (!strcmp(argv[1], "on") || !strcmp(argv[1], "chip_on"))
+ p.subcmd = PD_CHIP_ON;
else {
fprintf(stderr, "Unknown command: %s\n", argv[1]);
return -1;
}
- p.chip = 0;
+ if (argc == 2) {
+ p.chip = 0;
+ } else {
+ char *e;
+ p.chip = strtol(argv[2], &e, 0);
+ if (e && *e) {
+ fprintf(stderr, "Bad port number '%s'.\n", argv[2]);
+ return -1;
+ }
+ }
rv = ec_command(EC_CMD_PD_CONTROL, 0, &p, sizeof(p), NULL, 0);
return (rv < 0 ? rv : 0);
@@ -6819,6 +7151,77 @@ int cmd_pd_write_log(int argc, char *argv[])
return ec_command(EC_CMD_PD_WRITE_LOG_ENTRY, 0, &p, sizeof(p), NULL, 0);
}
+int cmd_tp_self_test(int argc, char* argv[])
+{
+ int rv;
+
+ rv = ec_command(EC_CMD_TP_SELF_TEST, 0, NULL, 0, NULL, 0);
+ if (rv < 0)
+ return rv;
+
+ printf("Touchpad self test: %s\n",
+ rv == EC_RES_SUCCESS ? "passed" : "failed");
+
+ return rv;
+}
+
+int cmd_tp_frame_get(int argc, char* argv[])
+{
+ int i, j;
+ uint32_t remaining = 0, offset = 0;
+ int rv = EC_SUCCESS;
+ uint8_t *data;
+ struct ec_response_tp_frame_info* r;
+ struct ec_params_tp_frame_get p;
+
+ data = malloc(ec_max_insize);
+ r = malloc(ec_max_insize);
+
+ rv = ec_command(EC_CMD_TP_FRAME_INFO, 0, NULL, 0, r, ec_max_insize);
+ if (rv < 0) {
+ fprintf(stderr, "Failed to get toucpad frame info.\n");
+ goto err;
+ }
+
+ rv = ec_command(EC_CMD_TP_FRAME_SNAPSHOT, 0, NULL, 0, NULL, 0);
+ if (rv < 0) {
+ fprintf(stderr, "Failed to snapshot frame.\n");
+ goto err;
+ }
+
+ for (i = 0; i < r->n_frames; i++) {
+ p.frame_index = i;
+ offset = 0;
+ remaining = r->frame_sizes[i];
+
+ while (remaining > 0) {
+ p.offset = offset;
+ p.size = MIN(remaining, ec_max_insize);
+
+ rv = ec_command(EC_CMD_TP_FRAME_GET, 0,
+ &p, sizeof(p), data, p.size);
+ if (rv < 0) {
+ fprintf(stderr, "Failed to get frame data "
+ "at offset 0x%x\n", offset);
+ goto err;
+ }
+
+ for (j = 0; j < p.size; j++)
+ printf("%02x ", data[j]);
+
+ offset += p.size;
+ remaining -= p.size;
+ }
+ printf("\n");
+ }
+
+err:
+ free(data);
+ free(r);
+
+ return rv < 0;
+}
+
/* NULL-terminated list of commands */
const struct command commands[] = {
{"autofanctrl", cmd_thermal_auto_fan_ctrl},
@@ -6852,8 +7255,12 @@ const struct command commands[] = {
{"flashread", cmd_flash_read},
{"flashwrite", cmd_flash_write},
{"flashinfo", cmd_flash_info},
+ {"flashspiinfo", cmd_flash_spi_info},
{"flashpd", cmd_flash_pd},
{"forcelidopen", cmd_force_lid_open},
+ {"fpframe", cmd_fp_frame},
+ {"fpinfo", cmd_fp_info},
+ {"fpmode", cmd_fp_mode},
{"gpioget", cmd_gpio_get},
{"gpioset", cmd_gpio_set},
{"hangdetect", cmd_hang_detect},
@@ -6902,6 +7309,8 @@ const struct command commands[] = {
{"rtcset", cmd_rtc_set},
{"rtcsetalarm", cmd_rtc_set_alarm},
{"rwhashpd", cmd_rw_hash_pd},
+ {"rwsigaction", cmd_rwsig_action},
+ {"rwsigstatus", cmd_rwsig_status},
{"sertest", cmd_serial_test},
{"port80flood", cmd_port_80_flood},
{"switches", cmd_switches},
@@ -6910,6 +7319,8 @@ const struct command commands[] = {
{"test", cmd_test},
{"thermalget", cmd_thermal_get_threshold},
{"thermalset", cmd_thermal_set_threshold},
+ {"tpselftest", cmd_tp_self_test},
+ {"tpframeget", cmd_tp_frame_get},
{"tmp006cal", cmd_tmp006cal},
{"tmp006raw", cmd_tmp006raw},
{"usbchargemode", cmd_usb_charge_set_mode},
@@ -6927,7 +7338,7 @@ int main(int argc, char *argv[])
const struct command *cmd;
int dev = 0;
int interfaces = COMM_ALL;
- char device_name[40] = "cros_ec";
+ char device_name[41] = CROS_EC_DEV_NAME;
int rv = 1;
int parse_error = 0;
char *e;
@@ -6964,6 +7375,7 @@ int main(int argc, char *argv[])
break;
case OPT_NAME:
strncpy(device_name, optarg, 40);
+ device_name[40] = '\0';
break;
}
}
@@ -6981,6 +7393,9 @@ int main(int argc, char *argv[])
/* Handle sub-devices command offset */
if (dev > 0 && dev < 4) {
set_command_offset(EC_CMD_PASSTHRU_OFFSET(dev));
+ } else if (dev == 8) {
+ /* Special offset for Fingerprint MCU */
+ strcpy(device_name, "cros_fp");
} else if (dev != 0) {
fprintf(stderr, "Bad device number %d\n", dev);
parse_error = 1;
diff --git a/util/export_taskinfo.c b/util/export_taskinfo.c
new file mode 100644
index 0000000000..b6b9bea7b8
--- /dev/null
+++ b/util/export_taskinfo.c
@@ -0,0 +1,43 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * The cmd_c_to_taskinfo will compile this file with different
+ * section definitions to export different tasklists.
+ */
+
+#include <stdint.h>
+
+#include "config.h"
+#include "task_id.h"
+
+#ifdef SECTION_IS_RO
+#define GET_TASKINFOS_FUNC get_ro_taskinfos
+#elif defined(SECTION_IS_RW)
+#define GET_TASKINFOS_FUNC get_rw_taskinfos
+#else
+#error "Current section (RO/RW) is not defined."
+#endif
+
+struct taskinfo {
+ char *name;
+ char *routine;
+ uint32_t stack_size;
+};
+
+#define TASK(n, r, d, s) { \
+ .name = #n, \
+ .routine = #r, \
+ .stack_size = s, \
+},
+static const struct taskinfo const taskinfos[] = {
+ CONFIG_TASK_LIST
+};
+#undef TASK
+
+uint32_t GET_TASKINFOS_FUNC(const struct taskinfo **infos)
+{
+ *infos = taskinfos;
+ /* Calculate the number of tasks */
+ return sizeof(taskinfos) / sizeof(*taskinfos);
+}
diff --git a/util/flash_ec b/util/flash_ec
index 284ab4c1fd..d19aa0508d 100755
--- a/util/flash_ec
+++ b/util/flash_ec
@@ -47,11 +47,10 @@ die() {
BOARDS_IT83XX=(
it83xx_evb
+ reef_it8320
)
BOARDS_LM4=(
- auron
- rambi
samus
)
@@ -60,21 +59,22 @@ BOARDS_STM32=(
blaze
chell_pd
elm
+ eve_fp
glados_pd
+ hammer
honeybuns
jerry
kitty
- llama
- lucid
minimuffin
oak
oak_pd
pit
plankton
- ryu
samus_pd
- snoball
+ scarlet
+ staff
strago_pd
+ whiskers
zinger
)
BOARDS_STM32_PROG_EN=(
@@ -89,7 +89,9 @@ BOARDS_STM32_DFU=(
servo_v4
servo_micro
sweetberry
+ polyberry
stm32f446e-eval
+ tigertail
)
BOARDS_NPCX_5M5G_JTAG=(
@@ -100,12 +102,29 @@ BOARDS_NPCX_5M5G_JTAG=(
BOARDS_NPCX_5M6G_JTAG=(
)
+BOARDS_NPCX_7M6X_JTAG=(
+ npcx7_evb
+)
+
BOARDS_NPCX_SPI=(
- amenia
+ coral
+ eve
+ fizz
+ glkrvp
gru
kevin
+ nautilus
+ nefario
+ poppy
reef
+ soraka
wheatley
+ kahlee
+ grunt
+)
+
+BOARDS_NPCX_INT_SPI=(
+ zoombini
)
BOARDS_NRF51=(
@@ -119,15 +138,25 @@ BOARDS_MEC1322=(
)
BOARDS_SPI_1800MV=(
+ coral
gru
kevin
+ nefario
reef
)
BOARDS_RAIDEN=(
+ coral
+ eve
+ fizz
gru
kevin
+ nautilus
+ nefario
+ poppy
reef
+ scarlet
+ soraka
)
# Flags
@@ -162,7 +191,18 @@ if [ -z "${FLAGS_board}" -a -z "${FLAGS_chip}" ]; then
die "should specify a board or a chip."
fi
-SERVO_TYPE=servo
+DUT_CONTROL_CMD="dut-control --port=${FLAGS_port}"
+
+function dut_control() {
+ $DUT_CONTROL_CMD "$@" >/dev/null
+}
+
+function get_servo_type() {
+ if dut_control "servo_type" ; then
+ $DUT_CONTROL_CMD servo_type | sed -e s/servo_type://
+ fi
+}
+
BOARD=${FLAGS_board}
BOARD_ROOT=/build/${BOARD}
@@ -184,20 +224,22 @@ elif $(in_array "${BOARDS_STM32[@]}" "${BOARD}"); then
CHIP="stm32"
elif $(in_array "${BOARDS_STM32_DFU[@]}" "${BOARD}"); then
CHIP="stm32_dfu"
- NEED_SERVO="no"
elif $(in_array "${BOARDS_NPCX_5M5G_JTAG[@]}" "${BOARD}"); then
CHIP="npcx_5m5g_jtag"
elif $(in_array "${BOARDS_NPCX_5M6G_JTAG[@]}" "${BOARD}"); then
CHIP="npcx_5m6g_jtag"
+elif $(in_array "${BOARDS_NPCX_7M6X_JTAG[@]}" "${BOARD}"); then
+ CHIP="npcx_7m6x_jtag"
elif $(in_array "${BOARDS_NPCX_SPI[@]}" "${BOARD}"); then
CHIP="npcx_spi"
+elif $(in_array "${BOARDS_NPCX_INT_SPI[@]}" "${BOARD}"); then
+ CHIP="npcx_spi"
elif $(in_array "${BOARDS_NRF51[@]}" "${BOARD}"); then
CHIP="nrf51"
elif $(in_array "${BOARDS_MEC1322[@]}" "${BOARD}"); then
CHIP="mec1322"
elif $(in_array "${BOARDS_IT83XX[@]}" "${BOARD}"); then
CHIP="it83xx"
- NEED_SERVO="no"
elif [ -n "${FLAGS_chip}" ]; then
CHIP="${FLAGS_chip}"
else
@@ -208,6 +250,10 @@ if [ -n "${FLAGS_chip}" -a "${CHIP}" != "${FLAGS_chip}" ]; then
die "board ${BOARD} doesn't use chip ${FLAGS_chip}"
fi
+if [ "${CHIP}" = "stm32_dfu" -o "${CHIP}" = "it83xx" ]; then
+ NEED_SERVO="no"
+fi
+
servo_has_warm_reset() {
dut_control warm_reset >/dev/null 2>&1
}
@@ -241,7 +287,14 @@ servo_sh_hard_reset() {
}
ec_reset() {
- eval ${SERVO_TYPE}_${MCU}_hard_reset
+ stype=${SERVO_TYPE}
+ if [[ "${SERVO_TYPE}" =~ "servo" ]] ; then
+ stype=servo
+ fi
+
+ if [[ ! -z "${stype}" ]]; then
+ eval ${stype}_${MCU}_hard_reset
+ fi
}
# force the EC to boot in serial monitor mode
@@ -250,7 +303,12 @@ toad_ec_boot0() {
}
servo_ec_boot0() {
- dut_control ec_boot_mode:on
+ if [[ "${SERVO_TYPE}" =~ "_with_ccd" ]] ; then
+ info "Using CCD."
+ dut_control ccd_ec_boot_mode:on
+ else
+ dut_control ec_boot_mode:on
+ fi
}
servo_usbpd_boot0() {
@@ -266,7 +324,12 @@ ec_enable_boot0() {
if $(in_array "${BOARDS_STM32_PROG_EN[@]}" "${BOARD}"); then
dut_control prog_en:yes
fi
- eval ${SERVO_TYPE}_${MCU}_boot0
+ if [[ "${SERVO_TYPE}" =~ "servo" ]] ; then
+ stype=servo
+ else
+ stype=${SERVO_TYPE}
+ fi
+ eval ${stype}_${MCU}_boot0
}
# Returns 0 on success (if on beaglebone)
@@ -275,11 +338,24 @@ on_servov3() {
}
# Returns 0 on success (if raiden should be used instead of servo)
+error_reported= # Avoid double printing the error message.
on_raiden() {
- if $(in_array "${BOARDS_RAIDEN[@]}" "${BOARD}") && \
- [ "${FLAGS_raiden}" = ${FLAGS_TRUE} ] ; then
+ if [[ "${SERVO_TYPE}" =~ "servo_v4" ]] || \
+ [[ "${SERVO_TYPE}" =~ "servo_micro" ]]; then
return 0
fi
+ if [ -z "${BOARD}" ]; then
+ [ "${FLAGS_raiden}" = ${FLAGS_TRUE} ] && return 0 || return 1
+ fi
+ if [ "${FLAGS_raiden}" = ${FLAGS_TRUE} ]; then
+ if in_array "${BOARDS_RAIDEN[@]}" "${BOARD}"; then
+ return 0
+ fi
+ if [ -z "${error_reported}" ]; then
+ error_reported="y"
+ die "raiden mode not supported on ${BOARD}" >&2
+ fi
+ fi
return 1
}
@@ -296,7 +372,7 @@ cleanup() {
kill -CONT ${pid}
done
- if ! on_raiden; then
+ if ! on_raiden || [[ "${SERVO_TYPE}" =~ "servo_micro" ]] ; then
ec_reset
fi
}
@@ -339,42 +415,60 @@ function ec_image() {
die "no EC image found : build one or specify one."
}
-DUT_CONTROL_CMD="dut-control --port=${FLAGS_port}"
-
-function dut_control() {
- $DUT_CONTROL_CMD "$@" >/dev/null
-}
-
-# Find the EC UART on the servo v2
-function ec_uart() {
- SERVOD_FAIL="Cannot communicate with servo. is servod running ?"
- ($DUT_CONTROL_CMD raw_${MCU}_uart_pty || \
- $DUT_CONTROL_CMD ${MCU}_uart_pty || \
- die "${SERVOD_FAIL}") | cut -d: -f2
+# Find the EC UART provided by servo.
+function servo_ec_uart() {
+ SERVOD_FAIL="Cannot communicate with servod. Is servod running?"
+ PTY=$(($DUT_CONTROL_CMD raw_${MCU}_uart_pty ||
+ $DUT_CONTROL_CMD ${MCU}_uart_pty) | cut -d: -f2)
+ if [[ -z "${PTY}" ]]; then
+ die "${SERVOD_FAIL}"
+ fi
+ echo $PTY
}
# Servo variables management
case "${BOARD}" in
oak_pd|samus_pd|strago_pd ) MCU="usbpd" ;;
chell_pd|glados_pd ) MCU="usbpd" ;;
+ eve_fp ) MCU="usbpd" ;;
dingdong|hoho|twinkie ) DUT_CONTROL_CMD="true" ; MCU="ec" ;;
*) MCU="ec" ;;
esac
-servo_VARS="${MCU}_uart_en ${MCU}_uart_parity \
-${MCU}_uart_baudrate jtag_buf_on_flex_en jtag_buf_en dev_mode"
+# Not every control is supported on every servo type. Therefore, define which
+# controls are supported by each servo type.
+servo_v2_VARS="jtag_buf_on_flex_en jtag_buf_en cold_reset spi1_vref"
+servo_micro_VARS="cold_reset spi1_vref"
+servo_v4_with_ccd_cr50_VARS="cold_reset"
+# Flashing an STM32 over the UART requires modifying the UART properties along
+# with the boot mode pin.
if [ "${CHIP}" = "stm32" ] ; then
- servo_VARS+=" ${MCU}_boot_mode"
+ common_stm32_VARS=" ${MCU}_uart_en ${MCU}_uart_parity"
+ common_stm32_VARS+=" ${MCU}_uart_baudrate"
+ servo_v2_VARS+=$common_stm32_VARS
+ servo_v2_VARS+=" ${MCU}_boot_mode"
+ servo_micro_VARS+=$common_stm32_VARS
+ servo_micro_VARS+=" ${MCU}_boot_mode"
+ servo_v4_with_ccd_cr50_VARS+=$common_stm32_VARS
+ servo_v4_with_ccd_cr50_VARS+=" ccd_${MCU}_boot_mode ec_uart_bitbang_en"
fi
if $(in_array "${BOARDS_STM32_PROG_EN[@]}" "${BOARD}"); then
- servo_VARS+=" prog_en"
+ servo_v2_VARS+=" prog_en"
+fi
+toad_VARS="${MCU}_uart_parity ${MCU}_uart_baudrate boot_mode"
+
+if $(in_array "${BOARDS_NPCX_INT_SPI[@]}" "${BOARD}"); then
+ servo_v2_VARS+=" fw_up"
fi
-toad_VARS="${MCU}_uart_parity \
-${MCU}_uart_baudrate boot_mode"
+# Some servo boards use the same controls.
+servo_v3_VARS="${servo_v2_VARS}"
+servo_v4_with_servo_micro_VARS="${servo_micro_VARS}"
function servo_save() {
SERVO_VARS_NAME=${SERVO_TYPE}_VARS
- $DUT_CONTROL_CMD ${!SERVO_VARS_NAME}
+ if [[ -n "${!SERVO_VARS_NAME}" ]]; then
+ $DUT_CONTROL_CMD ${!SERVO_VARS_NAME}
+ fi
}
function servo_restore() {
@@ -390,6 +484,11 @@ function claim_pty() {
"'cros_sdk --no-ns-pid' (see crbug.com/444931 for details)"
fi
+ if [[ -z "$1" ]]; then
+ warn "No parameter passed to claim_pty()"
+ return
+ fi
+
# Disconnect the EC-3PO interpreter from the UART since it will
# interfere with flashing.
dut_control ${MCU}_ec3po_interp_connect:off || \
@@ -412,6 +511,24 @@ function claim_pty() {
done
}
+function get_serial() {
+ if [[ "${SERVO_TYPE}" =~ "servo_v4_with_servo_micro" ]]; then
+ if [[ -z "${BOARD}" ]]; then
+ sn_ctl="servo_micro_"
+ else
+ sn_ctl="servo_micro_for_${BOARD}_"
+ fi
+ elif [[ "${SERVO_TYPE}" =~ "_with_ccd" ]] ; then
+ sn_ctl="ccd_"
+ else
+ # If it's none of the above, the main serialname will do.
+ sn_ctl=""
+ fi
+
+ SERIALNAME=$(${DUT_CONTROL_CMD} "${sn_ctl}serialname" | cut -d: -f2)
+ echo $SERIALNAME
+}
+
# Board specific flashing scripts
# helper function for using servo v2/3 with openocd
@@ -437,7 +554,7 @@ function flash_openocd() {
die "Failed to program ${IMG}"
}
-# helper function for using servo v2/3 with flashrom
+# helper function for using servo with flashrom
function flash_flashrom() {
TOOL_PATH="${EC_DIR}/build/${BOARD}/util:/usr/sbin/:$PATH"
FLASHROM=$(PATH="${TOOL_PATH}" which flashrom)
@@ -445,25 +562,28 @@ function flash_flashrom() {
if on_servov3; then
FLASHROM_PARAM="-p linux_spi"
elif on_raiden; then
- info "Using raiden debug cable."
- FLASHROM_PARAM="-p raiden_debug_spi:target=EC"
+ if [[ "${SERVO_TYPE}" =~ "servo_micro" ]]; then
+ # Servo micro doesn't use the "target" parameter.
+ FLASHROM_PARAM="-p raiden_debug_spi:"
+ else
+ FLASHROM_PARAM="-p raiden_debug_spi:target=EC,"
+ fi
else
- FLASHROM_PARAM="-p ft2232_spi:type=servo-v2,port=B"
+ FLASHROM_PARAM="-p ft2232_spi:type=servo-v2,port=B,"
fi
if [ ! -x "$FLASHROM" ]; then
die "no flashrom util found."
fi
- if ! on_raiden; then
- if ! on_servov3; then
- SERIALNAME=$(${DUT_CONTROL_CMD} serialname | \
- cut -d: -f2)
- if [[ "$SERIALNAME" != "" ]] ; then
- FLASHROM_PARAM+=",serial=${SERIALNAME}"
- fi
+ if ! on_servov3; then
+ SERIALNAME=$(get_serial)
+ if [[ "$SERIALNAME" != "" ]] ; then
+ FLASHROM_PARAM+="serial=${SERIALNAME}"
fi
+ fi
+ if ! on_raiden || [[ "${SERVO_TYPE}" =~ "servo_micro" ]] ; then
if $(in_array "${BOARDS_SPI_1800MV[@]}" "${BOARD}"); then
SPI_VOLTAGE="pp1800"
else
@@ -472,9 +592,27 @@ function flash_flashrom() {
dut_control cold_reset:on
+ # If spi flash is in npcx's ec, enable gang programer mode
+ if $(in_array "${BOARDS_NPCX_INT_SPI[@]}" "${BOARD}"); then
+ # Set GP_SEL# as low then start ec
+ dut_control fw_up:on
+ sleep 0.1
+ dut_control cold_reset:off
+ fi
+
# Turn on SPI1 interface on servo for SPI Flash Chip
- dut_control spi1_vref:${SPI_VOLTAGE} spi1_buf_en:on \
- spi1_buf_on_flex_en:on
+ dut_control spi1_vref:${SPI_VOLTAGE} spi1_buf_en:on
+ if [[ ! "${SERVO_TYPE}" =~ "servo_micro" ]]; then
+ # Servo micro doesn't support this control.
+ dut_control spi1_buf_on_flex_en:on
+ fi
+
+ # b/65694390: Zoombini takes enough power that when flashing
+ # without power, the SPI Vref voltage dips for a little bit.
+ # Therefore, wait 1 second to let the voltage stabilize.
+ if [[ "${BOARD}" == "zoombini" ]]; then
+ sleep 1
+ fi
else
# Temp layout
L=/tmp/flash_spi_layout_$$
@@ -485,15 +623,19 @@ function flash_flashrom() {
--fast-verify"
fi
- SPI_SIZE=$(sudo ${FLASHROM} ${FLASHROM_PARAM} --get-size 2>/dev/null | \
- tail -n 1)
+
+ # flashrom should report the image size at the end of the output.
+ SPI_SIZE=$(sudo ${FLASHROM} ${FLASHROM_PARAM} --get-size 2>/dev/null |\
+ grep -oe '[0-9]\+$') || \
+ die "Failed to determine chip size!"
+
IMG_SIZE=$(stat -c%s "$IMG")
PATCH_SIZE=$((${SPI_SIZE} - ${IMG_SIZE}))
# Temp image
T=/tmp/flash_spi_$$
-if $(in_array "${BOARDS_NPCX_SPI[@]}" "${BOARD}"); then
+if [ "${CHIP}" = "npcx_spi" ] ; then
{ # Patch temp image up to SPI_SIZE
cat $IMG
if [[ ${IMG_SIZE} -lt ${SPI_SIZE} ]] ; then
@@ -515,10 +657,17 @@ fi
rm $T
- if ! on_raiden; then
+ if ! on_raiden || [[ "${SERVO_TYPE}" =~ "servo_micro" ]] ; then
# Turn off SPI1 interface on servo
- dut_control spi1_vref:off spi1_buf_en:off \
- spi1_buf_on_flex_en:off
+ dut_control spi1_vref:off spi1_buf_en:off
+ if [[ ! "${SERVO_TYPE}" =~ "servo_micro" ]] ; then
+ dut_control spi1_buf_on_flex_en:off
+ fi
+
+ # Set GP_SEL# as default to disable GP mode when ec reboots
+ if $(in_array "${BOARDS_NPCX_INT_SPI[@]}" "${BOARD}"); then
+ dut_control fw_up:off
+ fi
else
rm $L
fi
@@ -530,18 +679,27 @@ fi
function flash_stm32() {
TOOL_PATH="${EC_DIR}/build/${BOARD}/util:$PATH"
STM32MON=$(PATH="${TOOL_PATH}" which stm32mon)
+ EC_UART="$(servo_ec_uart)"
if [ ! -x "$STM32MON" ]; then
die "no stm32mon util found."
fi
info "Using serial flasher : ${STM32MON}"
+ info "${MCU} UART pty : ${EC_UART}"
claim_pty ${EC_UART}
- if [ "${SERVO_TYPE}" = "servo" ] ; then
+ if [[ "${SERVO_TYPE}" =~ "servo" ]] ; then
dut_control ${MCU}_uart_en:on
fi
dut_control ${MCU}_uart_parity:even
- dut_control ${MCU}_uart_baudrate:115200
+
+ if [ "${SERVO_TYPE}" == "servo_v4_with_ccd_cr50" ] ; then
+ dut_control ${MCU}_uart_baudrate:9600
+ dut_control ${MCU}_uart_bitbang_en:on
+ else
+ dut_control ${MCU}_uart_baudrate:115200
+ fi
+
if $(servo_has_warm_reset); then
dut_control warm_reset:on
fi
@@ -646,6 +804,10 @@ function flash_npcx_5m6g_jtag() {
flash_npcx_jtag
}
+function flash_npcx_7m6x_jtag() {
+ flash_npcx_jtag
+}
+
function flash_npcx_spi() {
flash_flashrom
}
@@ -654,6 +816,7 @@ function flash_mec1322() {
flash_flashrom
}
+SERVO_TYPE="$(get_servo_type)"
if dut_control boot_mode 2>/dev/null ; then
if [[ "${MCU}" != "ec" ]] ; then
die "Toad cable can't support non-ec UARTs"
@@ -661,15 +824,13 @@ if dut_control boot_mode 2>/dev/null ; then
SERVO_TYPE=toad
info "Using a dedicated debug cable"
fi
+info "Using ${SERVO_TYPE}."
IMG="$(ec_image)"
info "Using ${MCU} image : ${IMG}"
if ! on_raiden && [ "${NEED_SERVO}" != "no" ] ; then
- EC_UART="$(ec_uart)"
- info "${MCU} UART pty : ${EC_UART}"
-
- save="$(servo_save)"
+ save="$(servo_save)"
fi
info "Flashing chip ${CHIP}."
diff --git a/util/gen_touchpad_hash.c b/util/gen_touchpad_hash.c
new file mode 100644
index 0000000000..e03c4638f3
--- /dev/null
+++ b/util/gen_touchpad_hash.c
@@ -0,0 +1,174 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <err.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openssl/sha.h>
+
+#include "config.h"
+
+static void print_hex(FILE *out, uint8_t *digest, int len, int last)
+{
+ int i;
+
+ fputs("{ ", out);
+ for (i = 0; i < len; i++)
+ fprintf(out, "0x%02x, ", digest[i]);
+
+ fprintf(out, "}%c\n", last ? ';' : ',');
+}
+
+/* Output blank hashes */
+static int hash_fw_blank(FILE *hashes)
+{
+ uint8_t digest[SHA256_DIGEST_LENGTH] = { 0 };
+ int len;
+
+ fprintf(hashes, "const uint8_t touchpad_fw_hashes[%d][%d] = {\n",
+ CONFIG_TOUCHPAD_VIRTUAL_SIZE / CONFIG_UPDATE_PDU_SIZE,
+ SHA256_DIGEST_LENGTH);
+ for (len = 0; len < CONFIG_TOUCHPAD_VIRTUAL_SIZE;
+ len += CONFIG_UPDATE_PDU_SIZE) {
+ print_hex(hashes, digest, sizeof(digest), 0);
+ }
+ fputs("};\n", hashes);
+
+ fprintf(hashes, "const uint8_t touchpad_fw_full_hash[%d] =\n\t",
+ SHA256_DIGEST_LENGTH);
+ print_hex(hashes, digest, SHA256_DIGEST_LENGTH, 1);
+
+ return 0;
+}
+
+static int hash_fw(FILE *tp_fw, FILE *hashes)
+{
+ uint8_t buffer[CONFIG_UPDATE_PDU_SIZE];
+ int len = 0;
+ int rb;
+ SHA256_CTX ctx;
+ SHA256_CTX ctx_all;
+ uint8_t digest[SHA256_DIGEST_LENGTH];
+
+ SHA256_Init(&ctx_all);
+ fprintf(hashes, "const uint8_t touchpad_fw_hashes[%d][%d] = {\n",
+ CONFIG_TOUCHPAD_VIRTUAL_SIZE / CONFIG_UPDATE_PDU_SIZE,
+ SHA256_DIGEST_LENGTH);
+ while (1) {
+ rb = fread(buffer, 1, sizeof(buffer), tp_fw);
+ len += rb;
+
+ if (rb == 0)
+ break;
+
+ /* Calculate hash for the block. */
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, buffer, rb);
+ SHA256_Final(digest, &ctx);
+
+ SHA256_Update(&ctx_all, buffer, rb);
+
+ print_hex(hashes, digest, sizeof(digest), 0);
+
+ if (rb < sizeof(buffer))
+ break;
+ }
+ fputs("};\n", hashes);
+
+ SHA256_Final(digest, &ctx_all);
+ fprintf(hashes, "const uint8_t touchpad_fw_full_hash[%d] =\n\t",
+ SHA256_DIGEST_LENGTH);
+ print_hex(hashes, digest, SHA256_DIGEST_LENGTH, 1);
+
+ if (!feof(tp_fw) || ferror(tp_fw)) {
+ warn("Error reading input file");
+ return 1;
+ }
+
+ if (len != CONFIG_TOUCHPAD_VIRTUAL_SIZE) {
+ warnx("Incorrect TP FW size (%d vs %d)", len,
+ CONFIG_TOUCHPAD_VIRTUAL_SIZE);
+ return 1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int nopt;
+ int ret;
+ const char *out = NULL;
+ char *tp_fw_name = NULL;
+ FILE *tp_fw = NULL;
+ FILE *hashes;
+ const char short_opt[] = "f:ho:";
+ const struct option long_opts[] = {
+ { "firmware", 1, NULL, 'f' },
+ { "help", 0, NULL, 'h' },
+ { "out", 1, NULL, 'o' },
+ { NULL }
+ };
+ const char usage[] = "USAGE: %s -f <touchpad FW> -o <output file>\n";
+
+ while ((nopt = getopt_long(argc, argv, short_opt,
+ long_opts, NULL)) != -1) {
+ switch (nopt) {
+ case 'f': /* -f or --firmware */
+ tp_fw_name = optarg;
+ break;
+
+ case 'h': /* -h or --help */
+ fprintf(stdout, usage, argv[0]);
+ return 0;
+
+ case 'o': /* -o or --out */
+ out = optarg;
+ break;
+
+ default: /* Invalid parameter. */
+ fprintf(stderr, usage, argv[0]);
+ return 1;
+ }
+ };
+
+ if (out == NULL)
+ return 1;
+
+ hashes = fopen(out, "we");
+ if (!hashes)
+ err(1, "Cannot open output file");
+
+ fputs("#include <stdint.h>\n\n", hashes);
+ if (tp_fw_name) {
+ tp_fw = fopen(tp_fw_name, "re");
+
+ if (!tp_fw) {
+ warn("Cannot open firmware");
+ ret = 1;
+ goto out;
+ }
+
+ ret = hash_fw(tp_fw, hashes);
+
+ fclose(tp_fw);
+ } else {
+ printf("No touchpad FW provided, outputting blank hashes.\n");
+ ret = hash_fw_blank(hashes);
+ }
+
+out:
+ fclose(hashes);
+
+ /* In case of failure, remove output file. */
+ if (ret != 0)
+ unlink(out);
+
+ return ret;
+}
diff --git a/util/genvif.c b/util/genvif.c
new file mode 100644
index 0000000000..306784a9bc
--- /dev/null
+++ b/util/genvif.c
@@ -0,0 +1,551 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#define _GNU_SOURCE /* for asprintf */
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <dirent.h>
+#include <limits.h>
+
+#include "config.h"
+#include "usb_pd.h"
+#include "usb_pd_tcpm.h"
+#include "charge_manager.h"
+#include "system.h"
+
+#define PD_REV_2_0 1
+#define PD_REV_3_0 2
+
+#define VIF_SPEC "Revision 1.11, Version 1.0"
+#define VENDOR_NAME "Google"
+#define PD_SPEC_REV PD_REV_2_0
+
+enum dtype {SNK = 0, SRC = 3, DRP = 4};
+
+const uint32_t vdo_idh __attribute__((weak)) = 0;
+
+const uint32_t *src_pdo;
+uint32_t src_pdo_cnt;
+
+char *yes_no(int val)
+{
+ return val ? "YES" : "NO";
+}
+
+enum system_image_copy_t system_get_image_copy(void)
+{
+ return SYSTEM_IMAGE_RW;
+}
+
+static void init_src_pdos(void)
+{
+#ifdef CONFIG_USB_PD_DYNAMIC_SRC_CAP
+ src_pdo_cnt = charge_manager_get_source_pdo(&src_pdo, 0);
+#else
+ src_pdo_cnt = pd_src_pdo_cnt;
+ src_pdo = pd_src_pdo;
+#endif
+}
+
+static int is_src(void)
+{
+ return src_pdo_cnt;
+}
+
+static int is_snk(void)
+{
+#ifdef CONFIG_USB_PD_DUAL_ROLE
+ return pd_snk_pdo_cnt;
+#else
+ return 0;
+#endif
+}
+
+static int is_extpwr(void)
+{
+ if (is_src())
+ return !!(src_pdo[0] & PDO_FIXED_EXTERNAL);
+ else
+ return 0;
+}
+
+static int is_drp(void)
+{
+ if (is_src())
+ return !!(src_pdo[0] & PDO_FIXED_DUAL_ROLE);
+ else
+ return 0;
+}
+
+#ifdef CONFIG_USB_PD_DUAL_ROLE
+static char *giveback(void)
+{
+#ifdef CONFIG_USB_PD_GIVE_BACK
+ return "YES";
+#else
+ return "NO";
+#endif
+}
+#endif
+
+static char *is_comms_cap(void)
+{
+ if (is_src())
+ return yes_no(src_pdo[0] & PDO_FIXED_COMM_CAP);
+ else
+ return "NO";
+}
+
+static char *dr_swap_to_ufp_supported(void)
+{
+ if (src_pdo[0] & PDO_FIXED_DATA_SWAP)
+ return yes_no(pd_check_data_swap(0, PD_ROLE_DFP));
+
+ return "NO";
+}
+
+static char *dr_swap_to_dfp_supported(void)
+{
+ if (src_pdo[0] & PDO_FIXED_DATA_SWAP)
+ return yes_no(pd_check_data_swap(0, PD_ROLE_UFP));
+
+ return "NO";
+}
+
+static char *vconn_swap(void)
+{
+#ifdef CONFIG_USBC_VCONN_SWAP
+ return "YES";
+#else
+ return "NO";
+#endif
+}
+
+static char *try_src(void)
+{
+#ifdef CONFIG_USB_PD_TRY_SRC
+ return "YES";
+#else
+ return "NO";
+#endif
+}
+
+static char *can_act_as_host(void)
+{
+#ifdef CONFIG_VIF_TYPE_C_CAN_ACT_AS_HOST
+ return "YES";
+#else
+ return "NO";
+#endif
+}
+
+static char *can_act_as_device(void)
+{
+#ifdef CONFIG_USB
+ return "YES";
+#else
+ return "NO";
+#endif
+}
+
+static char *captive_cable(void)
+{
+#ifdef CONFIG_VIF_CAPTIVE_CABLE
+ return "YES";
+#else
+ return "NO";
+#endif
+}
+
+static char *sources_vconn(void)
+{
+#ifdef CONFIG_USBC_VCONN
+ return "YES";
+#else
+ return "NO";
+#endif
+}
+
+static char *battery_powered(void)
+{
+#ifdef CONFIG_BATTERY
+ return "YES";
+#else
+ return "NO";
+#endif
+}
+
+static uint32_t product_type(void)
+{
+ return PD_IDH_PTYPE(vdo_idh);
+}
+
+static uint32_t pid_sop(void)
+{
+#ifdef CONFIG_USB_PID
+ return CONFIG_USB_PID;
+#else
+ return 0;
+#endif
+}
+
+static uint32_t rp_value(void)
+{
+#ifdef CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT
+ return CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT;
+#else
+ return 0;
+#endif
+}
+
+static char *attempts_discov_sop(enum dtype type)
+{
+#ifdef CONFIG_USB_PD_SIMPLE_DFP
+ if (type == SRC)
+ return "NO";
+ else
+ return "YES";
+#else
+ return "YES";
+#endif
+}
+
+static uint32_t bcddevice_sop(void)
+{
+#ifdef CONFIG_USB_BCD_DEV
+ return CONFIG_USB_BCD_DEV;
+#else
+ return 0;
+#endif
+}
+
+static uint32_t write_pdo_to_vif(FILE *vif, uint32_t pdo,
+ enum dtype type, uint32_t pnum)
+{
+ uint32_t power = 0;
+
+ if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_FIXED) {
+ uint32_t current = pdo & 0x3ff;
+ uint32_t voltage = (pdo >> 10) & 0x3ff;
+
+ power = ((current * 10) * (voltage * 50)) / 1000;
+
+ fprintf(vif, "%s_PDO_Supply_Type%d: 0\r\n",
+ (type == SRC) ? "Src" : "Snk", pnum);
+ if (type == SRC)
+ fprintf(vif, "Src_PDO_Peak_Current%d: 0\r\n", pnum);
+ fprintf(vif, "%s_PDO_Voltage%d: %d\r\n",
+ (type == SRC) ? "Src" : "Snk", pnum, voltage);
+ if (type == SRC)
+ fprintf(vif, "Src_PDO_Max_Current%d: %d\r\n",
+ pnum, current);
+ else
+ fprintf(vif, "Snk_PDO_Op_Current%d: %d\r\n",
+ pnum, current);
+ } else if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_BATTERY) {
+ uint32_t max_voltage = (pdo >> 20) & 0x3ff;
+ uint32_t min_voltage = (pdo >> 10) & 0x3ff;
+
+ power = pdo & 0x3ff;
+
+ fprintf(vif, "%s_PDO_Supply_Type%d: 1\r\n",
+ (type == SRC) ? "Src" : "Snk", pnum);
+ fprintf(vif, "%s_PDO_Min_Voltage%d: %d\r\n",
+ (type == SRC) ? "Src" : "Snk", pnum, min_voltage);
+ fprintf(vif, "%s_PDO_Max_Voltage%d: %d\r\n",
+ (type == SRC) ? "Src" : "Snk", pnum, max_voltage);
+ if (type == SRC)
+ fprintf(vif, "Src_PDO_Max_Power%d: %d\r\n",
+ pnum, power);
+ else
+ fprintf(vif, "Snk_PDO_Op_Power%d: %d\r\n",
+ pnum, power);
+ } else if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_VARIABLE) {
+ uint32_t max_voltage = (pdo >> 20) & 0x3ff;
+ uint32_t min_voltage = (pdo >> 10) & 0x3ff;
+ uint32_t current = pdo & 0x3ff;
+
+ power = ((current * 10) * (max_voltage * 50)) / 1000;
+
+ fprintf(vif, "%s_PDO_Supply_Type%d: 2\r\n",
+ (type == SRC) ? "Src" : "Snk", pnum);
+ if (type == SRC)
+ fprintf(vif, "Src_PDO_Peak_Current%d: 0\r\n", pnum);
+ fprintf(vif, "%s_PDO_Min_Voltage%d: %d\r\n",
+ (type == SRC) ? "Src" : "Snk", pnum, min_voltage);
+ fprintf(vif, "%s_PDO_Max_Voltage%d: %d\r\n",
+ (type == SRC) ? "Src" : "Snk", pnum, max_voltage);
+ if (type == SRC)
+ fprintf(vif, "Src_PDO_Max_Current%d: %d\r\n",
+ pnum, current);
+ else
+ fprintf(vif, "Snk_PDO_Op_Current%d: %d\r\n",
+ pnum, current);
+ } else {
+ fprintf(stderr, "ERROR: Invalid PDO_TYPE %d.\n", pdo);
+ }
+
+ return power;
+}
+
+/**
+ * Carriage and line feed, '\r\n', is needed because the file is processed
+ * on a Windows machine.
+ */
+static int gen_vif(const char *name, const char *board,
+ const char *vif_producer)
+{
+ FILE *vif;
+ enum dtype type;
+
+ if (is_drp())
+ type = DRP;
+ else if (is_src() && is_snk())
+ /* No DRP with SRC and SNK PDOs detected. So ignore. */
+ /* ie. Twinki or Plankton */
+ return 0;
+ else if (is_src())
+ type = SRC;
+ else if (is_snk())
+ type = SNK;
+ else
+ return 1;
+
+ /* Create VIF */
+ vif = fopen(name, "w+");
+ if (vif == NULL)
+ return 1;
+
+ /* Write VIF Header */
+ fprintf(vif, "$VIF_Specification: \"%s\"\r\n", VIF_SPEC);
+ fprintf(vif, "$VIF_Producer: \"%s\"\r\n", vif_producer);
+ fprintf(vif, "$Vendor_Name: \"%s\"\r\n", VENDOR_NAME);
+ fprintf(vif, "$Product_Name: \"%s\"\r\n", board);
+
+ fprintf(vif, "PD_Specification_Revision: %d\r\n", PD_SPEC_REV);
+ fprintf(vif, "UUT_Device_Type: %d\r\n", type);
+ fprintf(vif, "USB_Comms_Capable: %s\r\n", is_comms_cap());
+ fprintf(vif, "DR_Swap_To_DFP_Supported: %s\r\n",
+ dr_swap_to_dfp_supported());
+ fprintf(vif, "DR_Swap_To_UFP_Supported: %s\r\n",
+ dr_swap_to_ufp_supported());
+ fprintf(vif, "Externally_Powered: %s\r\n", yes_no(is_extpwr()));
+ fprintf(vif, "VCONN_Swap_To_On_Supported: %s\r\n", vconn_swap());
+ fprintf(vif, "VCONN_Swap_To_Off_Supported: %s\r\n", vconn_swap());
+ fprintf(vif, "Responds_To_Discov_SOP: YES\r\n");
+ fprintf(vif, "Attempts_Discov_SOP: %s\r\n", attempts_discov_sop(type));
+ fprintf(vif, "SOP_Capable: YES\r\n");
+ fprintf(vif, "SOP_P_Capable: NO\r\n");
+ fprintf(vif, "SOP_PP_Capable: NO\r\n");
+ fprintf(vif, "SOP_P_Debug_Capable: NO\r\n");
+ fprintf(vif, "SOP_PP_Debug_Capable: NO\r\n");
+
+ /* Write Source Fields */
+ if (type == DRP || type == SRC) {
+ uint32_t max_power = 0;
+
+ fprintf(vif, "USB_Suspend_May_Be_Cleared: YES\r\n");
+ fprintf(vif, "Sends_Pings: NO\r\n");
+ fprintf(vif, "Num_Src_PDOs: %d\r\n", src_pdo_cnt);
+
+ /* Write Source PDOs */
+ {
+ int i;
+ uint32_t pwr;
+
+ for (i = 0; i < src_pdo_cnt; i++) {
+ pwr = write_pdo_to_vif(vif, src_pdo[i],
+ SRC, i+1);
+ if (pwr > max_power)
+ max_power = pwr;
+ }
+ }
+
+ fprintf(vif, "PD_Power_as_Source: %d\r\n", max_power);
+ }
+
+ /* Write Sink Fields */
+#ifdef CONFIG_USB_PD_DUAL_ROLE
+ if (type == DRP || type == SNK) {
+ uint32_t max_power = 0;
+ uint32_t pwr;
+ int i;
+
+ fprintf(vif, "USB_Suspend_May_Be_Cleared: NO\r\n");
+ fprintf(vif, "GiveBack_May_Be_Set: %s\r\n", giveback());
+ fprintf(vif, "Higher_Capability_Set: NO\r\n");
+ fprintf(vif, "Num_Snk_PDOs: %d\r\n", pd_snk_pdo_cnt);
+
+ /* Write Sink PDOs */
+ for (i = 0; i < pd_snk_pdo_cnt; i++) {
+ pwr = write_pdo_to_vif(vif, pd_snk_pdo[i], SNK, i+1);
+ if (pwr > max_power)
+ max_power = pwr;
+ }
+
+ fprintf(vif, "PD_Power_as_Sink: %d\r\n", max_power);
+ }
+
+ /* Write DRP Fields */
+ if (type == DRP) {
+ fprintf(vif, "Accepts_PR_Swap_As_Src: YES\r\n");
+ fprintf(vif, "Accepts_PR_Swap_As_Snk: YES\r\n");
+ fprintf(vif, "Requests_PR_Swap_As_Src: YES\r\n");
+ fprintf(vif, "Requests_PR_Swap_As_Snk: YES\r\n");
+ }
+#endif
+
+ /* SOP Discovery Fields */
+ fprintf(vif, "Structured_VDM_Version_SOP: 0\r\n");
+ fprintf(vif, "XID_SOP: 0\r\n");
+ fprintf(vif, "Data_Capable_as_USB_Host_SOP: %s\r\n",
+ can_act_as_host());
+ fprintf(vif, "Data_Capable_as_USB_Device_SOP: %s\r\n",
+ can_act_as_device());
+ fprintf(vif, "Product_Type_SOP: %d\r\n", product_type());
+ fprintf(vif, "Modal_Operation_Supported_SOP: YES\r\n");
+ fprintf(vif, "USB_VID_SOP: 0x%04x\r\n", USB_VID_GOOGLE);
+ fprintf(vif, "PID_SOP: 0x%04x\r\n", pid_sop());
+ fprintf(vif, "bcdDevice_SOP: 0x%04x\r\n", bcddevice_sop());
+
+ fprintf(vif, "SVID1_SOP: 0x%04x\r\n", USB_VID_GOOGLE);
+ fprintf(vif, "SVID1_num_modes_min_SOP: 1\r\n");
+ fprintf(vif, "SVID1_num_modes_max_SOP: 1\r\n");
+ fprintf(vif, "SVID1_num_modes_fixed_SOP: YES\r\n");
+ fprintf(vif, "SVID1_mode1_enter_SOP: YES\r\n");
+
+#ifdef USB_SID_DISPLAYPORT
+ fprintf(vif, "SVID2_SOP: 0x%04x\r\n", USB_SID_DISPLAYPORT);
+ fprintf(vif, "SVID2_num_modes_min_SOP: 2\r\n");
+ fprintf(vif, "SVID2_num_modes_max_SOP: 2\r\n");
+ fprintf(vif, "SVID2_num_modes_fixed_SOP: YES\r\n");
+ fprintf(vif, "SVID2_mode1_enter_SOP: YES\r\n");
+ fprintf(vif, "SVID2_mode2_enter_SOP: YES\r\n");
+
+ fprintf(vif, "Num_SVIDs_min_SOP: 2\r\n");
+ fprintf(vif, "Num_SVIDs_max_SOP: 2\r\n");
+ fprintf(vif, "SVID_fixed_SOP: YES\r\n");
+#else
+ fprintf(vif, "Num_SVIDs_min_SOP: 1\r\n");
+ fprintf(vif, "Num_SVIDs_max_SOP: 1\r\n");
+ fprintf(vif, "SVID_fixed_SOP: YES\r\n");
+#endif
+
+ /* set Type_C_State_Machine */
+ {
+ int typec;
+
+ switch (type) {
+ case DRP:
+ typec = 2;
+ break;
+
+ case SNK:
+ typec = 1;
+ break;
+
+ default:
+ typec = 0;
+ }
+
+ fprintf(vif, "Type_C_State_Machine: %d\r\n", typec);
+ }
+
+ fprintf(vif, "Type_C_Implements_Try_SRC: %s\r\n", try_src());
+ fprintf(vif, "Type_C_Implements_Try_SNK: NO\r\n");
+ fprintf(vif, "Rp_Value: %d\r\n", rp_value());
+ /* None of the current devices send SOP' / SOP", so NO.*/
+ fprintf(vif, "Type_C_Supports_VCONN_Powered_Accessory: NO\r\n");
+ fprintf(vif, "Type_C_Is_VCONN_Powered_Accessory: NO\r\n");
+ fprintf(vif, "Type_C_Can_Act_As_Host: %s\r\n", can_act_as_host());
+ fprintf(vif, "Type_C_Host_Speed: 4\r\n");
+ fprintf(vif, "Type_C_Can_Act_As_Device: %s\r\n", can_act_as_device());
+ fprintf(vif, "Type_C_Device_Speed: 4\r\n");
+ fprintf(vif, "Type_C_Power_Source: 2\r\n");
+ fprintf(vif, "Type_C_BC_1_2_Support: 1\r\n");
+ fprintf(vif, "Type_C_Battery_Powered: %s\r\n", battery_powered());
+ fprintf(vif, "Type_C_Port_On_Hub: NO\r\n");
+ fprintf(vif, "Type_C_Supports_Audio_Accessory: NO\r\n");
+ fprintf(vif, "Captive_Cable: %s\r\n", captive_cable());
+ fprintf(vif, "Type_C_Source_Vconn: %s\r\n", sources_vconn());
+
+ fclose(vif);
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int nopt;
+ int ret;
+ const char *out = NULL;
+ const char *board = NULL;
+ const char *vif_producer;
+ DIR *vifdir;
+ char *name;
+ int name_size;
+ const char * const short_opt = "hb:o:";
+ const struct option long_opts[] = {
+ { "help", 0, NULL, 'h' },
+ { "board", 1, NULL, 'b' },
+ { "out", 1, NULL, 'o' },
+ { NULL }
+ };
+
+ vif_producer = argv[0];
+
+ do {
+ nopt = getopt_long(argc, argv, short_opt, long_opts, NULL);
+ switch (nopt) {
+ case 'h': /* -h or --help */
+ printf("USAGE: %s -b <board name> -o <out directory>\n",
+ vif_producer);
+ return 1;
+
+ case 'b': /* -b or --board */
+ board = optarg;
+ break;
+
+ case 'o': /* -o or --out */
+ out = optarg;
+ break;
+
+ case -1:
+ break;
+
+ default:
+ abort();
+ }
+ } while (nopt != -1);
+
+ if (out == NULL || board == NULL)
+ return 1;
+
+ /* Make sure VIF directory exists */
+ vifdir = opendir(out);
+ if (vifdir == NULL) {
+ fprintf(stderr, "ERROR: %s directory does not exist.\n", out);
+ return 1;
+ }
+ closedir(vifdir);
+
+ init_src_pdos();
+
+ name_size = asprintf(&name, "%s/%s_vif.txt", out, board);
+ if (name_size < 0) {
+ fprintf(stderr, "ERROR: Out of memory.\n");
+ return 1;
+ }
+
+ ret = gen_vif(name, board, vif_producer);
+
+ free(name);
+
+ return ret;
+}
diff --git a/util/getversion.sh b/util/getversion.sh
index 5d385fda1e..660ca24baf 100755
--- a/util/getversion.sh
+++ b/util/getversion.sh
@@ -11,7 +11,7 @@
dc=$'\001'
# Default marker to indicate 'dirty' repositories
-dirty_marker='-dirty'
+dirty_marker='+'
# This function examines the state of the current directory and attempts to
# extract its version information: the latest tag, if any, how many patches
@@ -28,7 +28,7 @@ dirty_marker='-dirty'
# "no_version"
get_tree_version() {
- local dirty
+ local marker
local ghash
local numcommits
local tag
@@ -54,9 +54,11 @@ get_tree_version() {
git status > /dev/null 2>&1
if [ -n "$(git diff-index --name-only HEAD 2>/dev/null)" ]; then
- dirty="${dirty_marker}"
+ marker="${dirty_marker}"
+ else
+ marker="-"
fi
- vbase="${ver_major}.${ver_branch}.${numcommits}-${ghash}${dirty}"
+ vbase="${ver_major}.${ver_branch}.${numcommits}${marker}${ghash}"
else
# Fall back to the VCSID provided by the packaging system if available.
if ghash=${VCSID##*-}; then
@@ -66,21 +68,22 @@ get_tree_version() {
vbase="no_version"
fi
fi
- echo "${vbase}${dc}${dirty}"
+ if [[ "${marker}" == "${dirty_marker}" ]]; then
+ echo "${vbase}${dc}${marker}"
+ else
+ echo "${vbase}${dc}"
+ fi
}
IFS="${dc}"
-ver="${CR50_DEV:+DEV/}${BOARD}_"
+ver="${CR50_DEV:+DBG/}${BOARD}_"
global_dirty= # set if any of the component repos is 'dirty'.
dir_list=( . ) # list of component directories, always includes the EC tree
case "${BOARD}" in
(cr50)
- # cr50 includes sources from 4 different git trees. Shortened 'dirty' tree
- # marker allows to keep the summary version string shorter.
- dirty_marker='+'
- dir_list+=( private-cr51 ../../third_party/tpm2 ../../third_party/cryptoc )
+ dir_list+=( ../../third_party/tpm2 ../../third_party/cryptoc )
;;
esac
@@ -121,6 +124,8 @@ if [ -n "$global_dirty" ]; then
echo "#define DATE \"$(date '+%F %T')\""
else
echo "/* Repo is clean, use the commit date of the last commit */"
- gitdate=$(git log -1 --format='%ci' HEAD | cut -d ' ' -f '1 2')
+ # If called from an ebuild we won't have a git repo, so redirect stderr
+ # to avoid annoying 'Not a git repository' errors.
+ gitdate=$(git log -1 --format='%ci' HEAD 2>/dev/null | cut -d ' ' -f '1 2')
echo "#define DATE \"${gitdate}\""
fi
diff --git a/util/host_command_check.sh b/util/host_command_check.sh
new file mode 100755
index 0000000000..57f0b8ffe3
--- /dev/null
+++ b/util/host_command_check.sh
@@ -0,0 +1,128 @@
+#!/bin/bash
+#
+# Copyright 2017 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+#######################################
+# Test if the following conditions hold for the ec host command
+# The alpha numeric value of the define starts with 0x
+# The alpha numeric value of the define is 4-hex digits
+# The hex digits "A B C D E F" are capitalized
+# Arguments:
+# string - ec host command to check
+# Returns:
+# 0 if command is ok, else 1
+########################################
+check_cmd() {
+ IFS=" "
+ # Remove any tabs that may exist
+ tts=$(echo "$1" | sed 's/\t/ /g')
+ arr=( $tts )
+
+ # Check for 0x
+ if [[ "${arr[2]}" != 0x* ]]; then
+ return 1
+ fi
+
+ # Check that length is 6. 0x + 4 hex digits
+ if [[ ${#arr[2]} != 6 ]]; then
+ return 1
+ fi
+
+ # Check that hex digits are valid and uppercase
+ hd=${arr[2]:2}
+ if ! [[ $hd =~ ^[0-9A-F]{4}$ ]]; then
+ return 1
+ fi
+
+ # command is ok
+ return 0
+}
+
+#######################################
+# Test if the string arg is in one of the following formats:
+# file.X:#define EC_CMD_X XxXXXX
+# file.X:#define EC_PRV_CMD_X XxXXXX
+# Arguments:
+# string - potential ec host command
+# Returns:
+# 0 if command is formated properly, else 1
+########################################
+should_check() {
+ IFS=" "
+ arr=( $1 )
+
+ # Check for file.X:#define
+ IFS=":"
+ temp=( ${arr[0]} )
+ # Check for file.X
+ if [ ! -f "${temp[0]}" ]; then
+ return 1
+ fi
+
+ # Check for #define
+ if [[ "${temp[1]}" != "#define" ]]; then
+ return 1
+ fi
+
+ # Check for EC_CMD_XXX or EC_PRV_CMD_XXX
+ if [[ "${arr[1]}" != EC_CMD_* ]] && [[ "${arr[1]}" != EC_PRV_CMD_* ]]; then
+ return 1
+ fi
+
+ # Check for EC_XXX_XXX(n)
+ if [[ "${arr[1]}" =~ ')'$ ]]; then
+ return 1
+ fi
+
+ return 0
+}
+
+main() {
+ ec_errors=()
+ ei=0
+ # Search all file occurrences of "EC_CMD" and store in array
+ IFS=$'\n'
+ ec_cmds=($(grep -r "EC_CMD"))
+
+ # Loop through and find valid occurrences of "EC_CMD" to check
+ length=${#ec_cmds[@]}
+ for ((i = 0; i != length; i++)); do
+ if should_check "${ec_cmds[i]}"; then
+ if ! check_cmd "${ec_cmds[i]}"; then
+ ec_errors[$ei]="${ec_cmds[i]}"
+ ((ei++))
+ fi
+ fi
+ done
+
+ # Search all file occurrances of "EC_PRV_CMD" and store in array
+ IFS=$'\n'
+ ec_prv_cmds=($(grep -r "EC_PRV_CMD"))
+
+ # Loop through and find valid occurrences of "EC_PRV_CMD" to check
+ length=${#ec_prv_cmds[@]}
+ for ((i = 0; i != length; i++)); do
+ if should_check "${ec_prv_cmds[i]}"; then
+ if ! check_cmd "${ec_prv_cmds[i]}"; then
+ ec_errors[$ei]="${ec_prv_cmds[i]}"
+ ((ei++))
+ fi
+ fi
+ done
+
+ # Check if any malformed ec host commands were found
+ if [ ! $ei -eq 0 ]; then
+ echo "The following host commands are malformed:"
+ # print all malformed host commands
+ for ((i = 0; i != ei; i++)); do
+ echo "FILE: ${ec_errors[i]}"
+ done
+ exit 1
+ fi
+
+ exit 0
+}
+
+main "$@"
diff --git a/util/iteflash.c b/util/iteflash.c
index 82d2c9d3fb..701c9dd2a5 100644
--- a/util/iteflash.c
+++ b/util/iteflash.c
@@ -288,6 +288,11 @@ static int dbgr_reset(struct ftdi_context *ftdi)
{
int ret = 0;
+ /* Reset CPU only, and we keep power state until flashing is done. */
+ ret |= i2c_write_byte(ftdi, 0x2f, 0x20);
+ ret |= i2c_write_byte(ftdi, 0x2e, 0x06);
+ ret |= i2c_write_byte(ftdi, 0x30, 0x40);
+
ret |= i2c_write_byte(ftdi, 0x27, 0x80);
if (ret < 0)
printf("DBGR RESET FAILED\n");
@@ -295,18 +300,23 @@ static int dbgr_reset(struct ftdi_context *ftdi)
return 0;
}
-/* Do WatchDog Reset*/
-static int do_watchdog_reset(struct ftdi_context *ftdi)
+static int exit_dbgr_mode(struct ftdi_context *ftdi)
{
+ uint8_t val;
int ret = 0;
- ret |= i2c_write_byte(ftdi, 0x2f, 0x20);
- ret |= i2c_write_byte(ftdi, 0x2e, 0x06);
- ret |= i2c_write_byte(ftdi, 0x30, 0x4C);
- ret |= i2c_write_byte(ftdi, 0x27, 0x80);
-
- if (ret < 0)
- printf("WATCHDOG RESET FAILED\n");
+ /* We have to exit dbgr mode so that EC won't hold I2C bus. */
+ ret |= i2c_write_byte(ftdi, 0x2f, 0x1c);
+ ret |= i2c_write_byte(ftdi, 0x2e, 0x08);
+ ret |= i2c_read_byte(ftdi, 0x30, &val);
+ ret |= i2c_write_byte(ftdi, 0x30, (val | (1 << 4)));
+ /*
+ * NOTE:
+ * We won't be able to send any commands to EC
+ * if we have exit dbgr mode.
+ * We do a cold reset for EC after flashing.
+ */
+ printf("=== EXIT DBGR MODE %s ===\n", (ret < 0) ? "FAILED" : "DONE");
return 0;
}
@@ -893,6 +903,8 @@ int verify_flash(struct ftdi_context *ftdi, const char *filename,
if (!buffer || !buffer2) {
fprintf(stderr, "Cannot allocate %d bytes\n", flash_size);
+ free(buffer);
+ free(buffer2);
return -ENOMEM;
}
@@ -904,12 +916,12 @@ int verify_flash(struct ftdi_context *ftdi, const char *filename,
}
file_size = fread(buffer, 1, flash_size, hnd);
+ fclose(hnd);
if (file_size <= 0) {
fprintf(stderr, "Cannot read %s\n", filename);
res = -EIO;
goto exit;
}
- fclose(hnd);
printf("Verify %d bytes at 0x%08x\n", file_size, offset);
res = command_read_pages(ftdi, offset, flash_size, buffer2);
@@ -1090,8 +1102,8 @@ int main(int argc, char **argv)
ret = 0;
terminate:
- /* DO EC WATCHDOG RESET */
- do_watchdog_reset(hnd);
+ /* Exit DBGR mode */
+ exit_dbgr_mode(hnd);
/* Close the FTDI USB handle */
ftdi_usb_close(hnd);
diff --git a/util/misc_util.c b/util/misc_util.c
index 3e4ff71f0f..40977dd607 100644
--- a/util/misc_util.c
+++ b/util/misc_util.c
@@ -48,8 +48,11 @@ char *read_file(const char *filename, int *size)
fseek(f, 0, SEEK_END);
*size = ftell(f);
rewind(f);
- if (*size > 0x100000) {
- fprintf(stderr, "File seems unreasonably large\n");
+ if ((*size > 0x100000) || (*size < 0)) {
+ if (*size < 0)
+ perror("ftell failed");
+ else
+ fprintf(stderr, "File seems unreasonably large\n");
fclose(f);
return NULL;
}
diff --git a/util/openocd/npcx.cfg b/util/openocd/npcx.cfg
index 42b04dcd3d..05116085a5 100644
--- a/util/openocd/npcx.cfg
+++ b/util/openocd/npcx.cfg
@@ -10,7 +10,7 @@ source [find target/swj-dp.tcl]
if { [info exists CHIPNAME] } {
set _CHIPNAME $CHIPNAME
} else {
- set _CHIPNAME npcx5m5g
+ set _CHIPNAME npcx_ec
}
if { [info exists ENDIAN] } {
@@ -48,8 +48,8 @@ adapter_khz 100
adapter_nsrst_delay 100
jtag_ntrst_delay 100
-# use srst to perform a system reset
-cortex_m reset_config srst
+# use sysresetreq to perform a system reset
+cortex_m reset_config sysresetreq
#reset configuration
reset_config trst_and_srst
diff --git a/util/openocd/npcx_cmds.tcl b/util/openocd/npcx_cmds.tcl
index a21b804997..ca44343084 100644
--- a/util/openocd/npcx_cmds.tcl
+++ b/util/openocd/npcx_cmds.tcl
@@ -71,6 +71,17 @@ proc flash_npcx5m6g {image_path image_offset spifw_image} {
echo "*** Finish program npcx5m6g ***\r\n"
}
+proc flash_npcx7m6x {image_path image_offset spifw_image} {
+ # 192 KB for RO & RW regions
+ set fw_size 0x30000
+ # Code RAM start address
+ set cram_addr 0x10090000
+
+ echo "*** Start to program npcx7m6f/g/k with $image_path ***"
+ flash_npcx $image_path $cram_addr $image_offset $fw_size $spifw_image
+ echo "*** Finish program npcx7m6f/g/k ***\r\n"
+}
+
proc flash_npcx_ro {chip_name image_dir image_offset} {
set MPU_RNR 0xE000ED98;
set MPU_RASR 0xE000EDA0;
@@ -82,7 +93,7 @@ proc flash_npcx_ro {chip_name image_dir image_offset} {
# Halt CPU first
halt
- # diable MPU for Data RAM
+ # disable MPU for Data RAM
mww $MPU_RNR 0x1
mww $MPU_RASR 0x0
@@ -92,6 +103,9 @@ proc flash_npcx_ro {chip_name image_dir image_offset} {
} elseif {$chip_name == "npcx_5m6g_jtag"} {
# program RO region
flash_npcx5m6g $ro_image_path $image_offset $spifw_image
+ } elseif {$chip_name == "npcx_7m6x_jtag"} {
+ # program RO region
+ flash_npcx7m6x $ro_image_path $image_offset $spifw_image
} else {
echo $chip_name "no supported."
}
@@ -109,7 +123,7 @@ proc flash_npcx_all {chip_name image_dir image_offset} {
# Halt CPU first
halt
- # diable MPU for Data RAM
+ # disable MPU for Data RAM
mww $MPU_RNR 0x1
mww $MPU_RASR 0x0
@@ -121,12 +135,19 @@ proc flash_npcx_all {chip_name image_dir image_offset} {
# program RW region
flash_npcx5m5g $rw_image_path $rw_image_offset $spifw_image
} elseif {$chip_name == "npcx_5m6g_jtag"} {
- # RW images offset - 512 KB
+ # RW images offset - 256 KB
set rw_image_offset [expr ($image_offset + 0x40000)]
# program RO region
flash_npcx5m6g $ro_image_path $image_offset $spifw_image
# program RW region
flash_npcx5m6g $rw_image_path $rw_image_offset $spifw_image
+ } elseif {$chip_name == "npcx_7m6x_jtag"} {
+ # RW images offset - 256 KB
+ set rw_image_offset [expr ($image_offset + 0x40000)]
+ # program RO region
+ flash_npcx7m6x $ro_image_path $image_offset $spifw_image
+ # program RW region
+ flash_npcx7m6x $rw_image_path $rw_image_offset $spifw_image
} else {
echo $chip_name "no supported."
}
diff --git a/util/presubmit_check.sh b/util/presubmit_check.sh
index 0ba4776473..b11c8dec47 100755
--- a/util/presubmit_check.sh
+++ b/util/presubmit_check.sh
@@ -9,10 +9,18 @@ if [[ ! -e .tests-passed ]]; then
exit 1
fi
+# Directories that need to be tested by separate unit tests.
+unittest_dirs="util/ec3po/ extra/stack_analyzer/"
+
changed=$(find ${PRESUBMIT_FILES} -newer .tests-passed)
-ec3po_files=$(echo "${PRESUBMIT_FILES}" | grep util/ec3po/)
-# Filter out ec3po files from changed files.
-changed=$(echo "${changed}" | grep -v util/ec3po/)
+# Filter out unittest_dirs files from changed files. They're handled separately.
+for dir in $unittest_dirs; do
+ changed=$(echo "${changed}" | grep -v "${dir}")
+done
+# Filter out flash_ec since it's not part of any unit tests.
+changed=$(echo "${changed}" | grep -v util/flash_ec)
+# Filter out this file itself.
+changed=$(echo "${changed}" | grep -v util/presubmit_check.sh)
if [[ -n "${changed}" ]]; then
echo "Files have changed since last time unit tests passed:"
echo "${changed}" | sed -e 's/^/ /'
@@ -20,15 +28,22 @@ if [[ -n "${changed}" ]]; then
exit 1
fi
-if [[ ! -e util/ec3po/.tests-passed ]] && [[ -n "${ec3po_files}" ]]; then
- echo 'Unit tests have not passed. Please run "util/ec3po/run_tests.sh".'
- exit 1
-fi
+for dir in $unittest_dirs; do
+ dir_files=$(echo "${PRESUBMIT_FILES}" | grep "${dir}")
+ if [[ -z "${dir_files}" ]]; then
+ continue
+ fi
-changed_ec3po_files=$(find ${ec3po_files} -newer util/ec3po/.tests-passed)
-if [[ -n "${changed_ec3po_files}" ]] && [[ -n "${ec3po_files}" ]]; then
- echo "Files have changed since last time EC-3PO unit tests passed:"
- echo "${changed_ec3po_files}" | sed -e 's/^/ /'
- echo 'Please run "util/ec3po/run_tests.sh".'
- exit 1
-fi
+ if [[ ! -e "${dir}/.tests-passed" ]]; then
+ echo "Unit tests have not passed. Please run \"${dir}run_tests.sh\"."
+ exit 1
+ fi
+
+ changed_files=$(find ${dir_files} -newer "${dir}/.tests-passed")
+ if [[ -n "${changed_files}" ]] && [[ -n "${dir_files}" ]]; then
+ echo "Files have changed since last time unit tests passed:"
+ echo "${changed_files}" | sed -e 's/^/ /'
+ echo "Please run \"${dir}run_tests.sh\"."
+ exit 1
+ fi
+done
diff --git a/util/run_ects.py b/util/run_ects.py
new file mode 100644
index 0000000000..a633430113
--- /dev/null
+++ b/util/run_ects.py
@@ -0,0 +1,92 @@
+# Copyright 2017 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Run all eCTS tests and publish results."""
+
+
+import argparse
+import logging
+import os
+import subprocess
+import sys
+
+# List of tests to run.
+TESTS = ['meta', 'gpio', 'hook', 'i2c', 'interrupt', 'mutex', 'task', 'timer']
+
+
+class CtsRunner(object):
+ """Class running eCTS tests."""
+
+ def __init__(self, ec_dir, dryrun):
+ self.ec_dir = ec_dir
+ self.cts_py = []
+ if dryrun:
+ self.cts_py += ['echo']
+ self.cts_py += [os.path.join(ec_dir, 'cts/cts.py')]
+
+ def run_cmd(self, cmd):
+ try:
+ rc = subprocess.call(cmd)
+ if rc != 0:
+ return False
+ except OSError:
+ return False
+ return True
+
+ def run_test(self, test):
+ cmd = self.cts_py + ['-m', test]
+ self.run_cmd(cmd)
+
+ def run(self, tests):
+ for test in tests:
+ logging.info('Running', test, 'test.')
+ self.run_test(test)
+
+ def sync(self):
+ logging.info('Syncing tree...')
+ os.chdir(self.ec_dir)
+ cmd = ['repo', 'sync', '.']
+ return self.run_cmd(cmd)
+
+ def upload(self):
+ logging.info('Uploading results...')
+
+
+def main():
+ if not os.path.exists('/etc/cros_chroot_version'):
+ logging.error('This script has to run inside chroot.')
+ sys.exit(-1)
+
+ ec_dir = os.path.realpath(os.path.dirname(__file__) + '/..')
+
+ parser = argparse.ArgumentParser(description='Run eCTS and report results.')
+ parser.add_argument('-d',
+ '--dryrun',
+ action='store_true',
+ help='Echo commands to be executed without running them.')
+ parser.add_argument('-s',
+ '--sync',
+ action='store_true',
+ help='Sync tree before running tests.')
+ parser.add_argument('-u',
+ '--upload',
+ action='store_true',
+ help='Upload test results.')
+ args = parser.parse_args()
+
+ runner = CtsRunner(ec_dir, args.dryrun)
+
+ if args.sync:
+ if not runner.sync():
+ logging.error('Failed to sync.')
+ sys.exit(-1)
+
+ runner.run(TESTS)
+
+ if args.upload:
+ runner.upload()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/util/signer/bs b/util/signer/bs
new file mode 100755
index 0000000000..8815e97d29
--- /dev/null
+++ b/util/signer/bs
@@ -0,0 +1,224 @@
+#!/bin/bash
+
+#
+# Copyright 2016 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# This script is a utility which allows to create differently signed CR50
+# images from different sources.
+#
+set -e
+set -u
+
+progname=$(basename $0)
+
+tmpf="/tmp/bs_manifest.$$"
+trap "{ rm -rf [01].flat ${tmpf} ; }" EXIT
+
+usage() {
+ local rv="${1}"
+ cat <<EOF
+
+This script allows to sign CR50 RW images. By default it uses ec.RW.elf and
+ec.RW_B.elf in build/cr50/RW as inputs and util/signer/ec_RW-manifest-dev.json
+as the manifest, and places the newly signed images into build/cr50/ec.bin.
+
+The only outside dependency of this script is the signing utility itself,
+which is expected to be available as \$HOME/bin/codesigner.
+
+The following command line options are accepted:
+
+ b1 - generate signature for the b1 version of the H1 chip
+ elves <elf1> <elf2> - sign the supplied elf files instead of the default
+ ones. Handy if the builder generated files need to be signed
+ help - print this message
+ hex - generate hex output instead of binary (place in 0.signed.hex and
+ 1.signed.hex in the local directory)
+ prod - sign with prod key (no debug image will be signed)
+
+This script also allows to sign dev images for running on prod RO. To do that
+invoke this script as follows:
+
+ H1_DEVIDS='<dev id0> <dev id1>' ${progname} [other options, if any]
+
+where <dev id0> <dev id1> are values reported by sysinfo command in the
+DEV_ID: line when run on the CR50 for which the image is built.
+
+The same values can be obtained in the lsusb command output:
+
+ lsusb -vd 18d1:5014 | grep -i serial
+
+note that the lsusb reported values are in hex and need to be prefixed with
+0x.
+
+Finally, this script also allows to specify the board ID fields of the RW
+headers. The fields come from the evironment variable CR50_BOARD_ID, which is
+required to include three colon separated fields. The first field is a four
+letter board RLZ code, the second field is board id mask in hex, no 0x prefix,
+and the third field - board ID flags, again, hex, no 0x prefix.
+
+CR50_BOARD_ID='XXYY:ffffff00:ff00' ${progname} [other options, if any]
+
+both H1_DEVIDS and CR50_BOARD_ID can be defined independently.
+
+EOF
+ exit "${rv}"
+}
+
+# This function modifies the manifest to include device ID and board ID nodes,
+# if H1_DEVIDS and CR50_BOARD_ID are defined in the environment, respectively,
+tweak_manifest () {
+ local sub
+
+ # If defined, plug in dev ID nodes before the 'fuses' node.
+ if [[ -z "${do_prod}" && -n "${H1_DEVIDS}" ]]; then
+ echo "creating a customized DEV image for DEV IDS ${H1_DEVIDS}"
+ sub=$(printf "\\\n \"DEV_ID0\": %s,\\\n \"DEV_ID1\": %s," ${H1_DEVIDS})
+ sed -i "s/\"fuses\": {/\"fuses\": {${sub}/" "${tmpf}"
+ fi
+
+ if [[ -z "${CR50_BOARD_ID}" ]]; then
+ return
+ fi
+
+ # CR50_BOARD_ID is set, let's parse it and plug in the board ID related
+ # nodes into manifest before the 'fuses' node.
+ local bid_params
+ local rlz
+
+ bid_params=( $(echo $CR50_BOARD_ID | sed 's/:/ /g') )
+ # A very basic sanity check: it needs to consist of three colon separated
+ # fields.
+ if [[ ${#bid_params[@]} != 3 ]]; then
+ echo "Wrong board ID string \"$CR50_BOARD_ID\"}" >&2
+ exit 1
+ fi
+
+ # Convert board RLZ code from ASCII to hex
+ rlz="0x$(echo -n ${bid_params[0]} | hexdump -ve '/1 "%02x"')"
+
+ # Prepare text of all three board ID related nodes
+ sub="$(printf "\\\n\"board_id\": %s,\\\n" "${rlz}")"
+ sub+="$(printf "\"board_id_mask\": %s,\\\n" "0x${bid_params[1]}")"
+ sub+="$(printf "\"board_id_flags\": %s,\\\n" "0x${bid_params[2]}")"
+ sed -i "s/\"fuses\": {/${sub}\"fuses\": {/" "${tmpf}"
+}
+
+# This is the suggested location of the codesigner utility.
+BIN_ROOT="${HOME}/bin"
+
+# This is where the new signed image will be pasted into.
+: ${RESULT_FILE=build/cr50/ec.bin}
+TMP_RESULT_FILE="${RESULT_FILE}.tmp"
+
+if [[ -z "${CROS_WORKON_SRCROOT}" ]]; then
+ echo "${progname}: This script must run inside Chrome OS chroot" >&2
+ exit 1
+fi
+
+: ${CR50_BOARD_ID=}
+: ${H1_DEVIDS=}
+EC_ROOT="${CROS_WORKON_SRCROOT}/src/platform/ec"
+EC_BIN_ROOT="${EC_ROOT}/util/signer"
+
+do_hex=
+do_b1=
+do_prod=
+
+# Prepare the default manifest.
+cp "${EC_BIN_ROOT}/ec_RW-manifest-dev.json" "${tmpf}"
+
+elves=( build/cr50/RW/ec.RW.elf build/cr50/RW/ec.RW_B.elf )
+cd "${EC_ROOT}"
+while (( $# )); do
+ param="${1}"
+ case "${param}" in
+ (hex) do_hex='true';;
+ (b1)
+ do_b1='true'
+ sed -i 's/\(.*FW_DEFINED_DATA_BLK0.*\): 2/\1: 0/' "${tmpf}"
+ ;;
+ (elves)
+ if [[ (( $# < 3 )) ]]; then
+ echo "two elf file names are required" >&2
+ exit 1
+ fi
+ elves=( $2 $3 )
+ shift
+ shift
+ ;;
+ (prod)
+ do_prod='true'
+ ;;
+ (help)
+ usage 0
+ ;;
+ (*)
+ usage 1
+ ;;
+ esac
+ shift
+done
+
+if [[ -z "${do_hex}" && ! -f "${RESULT_FILE}" ]]; then
+ echo "${RESULT_FILE} not found. Run 'make BOARD=cr50' first" >&2
+ exit 1
+fi
+
+if [[ -n "${do_prod}" && -n "${do_b1}" ]]; then
+ echo "can not build prod images for B1, sorry..."
+ exit 1
+fi
+
+signer_command_params=()
+signer_command_params+=(--b -x ${EC_BIN_ROOT}/fuses.xml)
+if [[ -z "${do_prod}" ]]; then
+ signer_command_params+=(-k ${EC_BIN_ROOT}/cr50_rom0-dev-blsign.pem.pub)
+else
+ cp "${EC_BIN_ROOT}/ec_RW-manifest-prod.json" "${tmpf}"
+ signer_command_params+=(-k ${EC_BIN_ROOT}/cr50_RW-prod.pem.pub)
+fi
+signer_command_params+=(-j ${tmpf})
+
+if [[ -n "${do_hex}" ]]; then
+ dst_suffix='signed.hex'
+else
+ signer_command_params+=(--format=bin)
+ dst_suffix='flat'
+fi
+
+tweak_manifest
+
+count=0
+for elf in ${elves[@]}; do
+ if [[ -n "${do_prod}" ]]; then
+ if grep -q "DEV/cr50" "${elf}"; then
+ echo "Will not sign debug image with prod keys" >&2
+ exit 1
+ fi
+ fi
+ signed_file="${count}.${dst_suffix}"
+
+ # Make sure this file is not owned by root
+ touch "${signed_file}"
+ sudo ${BIN_ROOT}/codesigner ${signer_command_params[@]} \
+ -i ${elf} -o "${signed_file}"
+ if [[ ! -s "${signed_file}" ]]; then
+ echo "${progname}: error: empty signed file ${signed_file}" >&2
+ exit 1
+ fi
+ : $(( count++ ))
+done
+
+if [[ -z "${do_hex}" ]]; then
+ # Full binary image is required, paste the newly signed blobs into the
+ # output image, preserving it in case dd fails for whatever reason.
+ cp "${RESULT_FILE}" "${TMP_RESULT_FILE}"
+ dd if="0.flat" of="${TMP_RESULT_FILE}" seek=16384 bs=1 conv=notrunc
+ dd if="1.flat" of="${TMP_RESULT_FILE}" seek=278528 bs=1 conv=notrunc
+ rm [01].flat
+ mv "${TMP_RESULT_FILE}" "${RESULT_FILE}"
+fi
+
+echo "SUCCESS!!!"
diff --git a/util/signer/build.mk b/util/signer/build.mk
index 07db0ea7ea..88fea91bce 100644
--- a/util/signer/build.mk
+++ b/util/signer/build.mk
@@ -10,7 +10,14 @@ signer_INC := $(addprefix common/, aes.h ecdh.h gnubby.h \
signer_SRC := codesigner.cc publickey.cc image.cc gnubby.cc aes.cc ecdh.cc
SIGNER_DEPS := $(addprefix $(signer_ROOT)/, $(signer_SRC) $(signer_INC))
-HOST_CXXFLAGS += -I/usr/include/libxml2
-$(out)/util/signer: $(SIGNER_DEPS)
+HOST_CXXFLAGS += -I/usr/include/libxml2 -I $(out)
+$(out)/util/signer: $(SIGNER_DEPS) $(out)/pmjp.h
$(call quiet,cxx_to_host,HOSTCXX)
+# When building self signed Cr50 images we still want the epoch/major/minor
+# fields come from the dev manifest. Since a full blown JSON parser for C is
+# not readily available, this rule generates a small .h file with only the
+# fields of interest retrieved from the dev JSON file.
+$(out)/pmjp.h: util/signer/pmjp.py util/signer/ec_RW-manifest-dev.json
+ @echo " PMJP $@"
+ $(Q)./util/signer/pmjp.py ./util/signer/ec_RW-manifest-dev.json > $@
diff --git a/util/signer/codesigner.cc b/util/signer/codesigner.cc
index a2a840509b..89b23eccd2 100644
--- a/util/signer/codesigner.cc
+++ b/util/signer/codesigner.cc
@@ -17,6 +17,8 @@
#include <common/signed_header.h>
#ifdef HAVE_JSON
#include <rapidjson/document.h>
+#else
+#include <pmjp.h>
#endif
#include <map>
@@ -297,6 +299,7 @@ void usage(int argc, char* argv[]) {
"--input=$elf-filename\n"
"--output=output-filename\n"
"--key=$pem-filename\n"
+ "[--b] ignored option, could be included for forward compatibility\n"
"[--cros] to sign for the ChromeOS realm w/o manifest\n"
"[--xml=$xml-filename] typically 'havenTop.xml'\n"
"[--json=$json-filename] the signing manifest\n"
@@ -313,6 +316,7 @@ void usage(int argc, char* argv[]) {
int getOptions(int argc, char* argv[]) {
static struct option long_options[] = {
// name, has_arg
+ {"b", no_argument, NULL, 'b'},
{"cros", no_argument, NULL, 'c'},
{"format", required_argument, NULL, 'f'},
{"help", no_argument, NULL, 'h'},
@@ -330,7 +334,7 @@ int getOptions(int argc, char* argv[]) {
{0, 0, 0, 0}};
int c, option_index = 0;
outputFormat.assign("hex");
- while ((c = getopt_long(argc, argv, "i:o:p:k:x:j:f:s:H:chvr", long_options,
+ while ((c = getopt_long(argc, argv, "i:o:p:k:x:j:f:s:H:bchvr", long_options,
&option_index)) != -1) {
switch (c) {
case 0:
@@ -338,6 +342,8 @@ int getOptions(int argc, char* argv[]) {
if (optarg) fprintf(stderr, " with arg %s", optarg);
fprintf(stderr, "\n");
break;
+ case 'b':
+ break;
case 'c':
FLAGS_cros = true;
break;
@@ -429,7 +435,9 @@ int main(int argc, char* argv[]) {
if (jsonFilename.empty()) {
// Defaults, in case no JSON
values.insert(make_pair("keyid", key.n0inv()));
- values.insert(make_pair("epoch", 0x1337));
+ values.insert(make_pair("epoch", MANIFEST_EPOCH));
+ values.insert(make_pair("major", MANIFEST_MAJOR));
+ values.insert(make_pair("minor", MANIFEST_MINOR));
}
// Hardcoded expectation. Can be overwritten in JSON w/ new explicit value.
diff --git a/util/signer/cr50_RW-prod.pem.pub b/util/signer/cr50_RW-prod.pem.pub
new file mode 100644
index 0000000000..f043eea161
--- /dev/null
+++ b/util/signer/cr50_RW-prod.pem.pub
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKCAQEAthqml36PUHk5MgurodTG
+puEsqK9/28/gEiCZGgfKL2rZKzU7CSiD82nmMgMoaxNTcPZgln+WELXIZUv81Up3
+GT6dA2dSDSQgmdgI1/x3OkEf9BkmHajuvhZTDteI18X/9TsXwly9zoxEFRy/JW8X
+Cz9/eOE7xcgoIzji0WmnosMKyxiOv67hhH+JvJ01uQhcxOag2606uIBknovHZT7l
+kf3RsEquoZqGK2WFwin9gl4KXv8yQ2F0h9LnfezIURWuz4J6pNc8EI7jYeP5eBrJ
+AfE8HsnDD6I2OpoNNM0BnbPq7gbn5CJJn5bZ6dNM4YBH8saJgNVBYOV9XqHdtiLV
+uwIBAw==
+-----END PUBLIC KEY-----
diff --git a/util/signer/create_released_image.sh b/util/signer/create_released_image.sh
new file mode 100755
index 0000000000..9dccd02aba
--- /dev/null
+++ b/util/signer/create_released_image.sh
@@ -0,0 +1,222 @@
+#!/bin/bash
+
+#
+# Copyright 2017 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# This script is a utility which allows to sign prod CR50 images for release
+# and place them in a tarball suitable for uploading to the BCS.
+#
+# The util/signer/ec_RW-manifest-prod.json manifest present in the EC
+# directory is used for signing.
+#
+
+set -u
+
+# A very crude RO verification function. The key signature found at a fixed
+# offset into the RO blob must match the RO type. Prod keys have bit D2 set to
+# one, dev keys have this bit set to zero.
+verify_ro() {
+ local ro_bin="${1}"
+ local type_expected="${2}"
+ local key_byte
+
+ if [ ! -f "${ro_bin}" ]; then
+ echo "${ro_bin} not a file!" >&2
+ exit 1
+ fi
+
+ # Key signature's lowest byte is byte #5 in the line at offset 0001a0.
+ key_byte="$(od -Ax -t x1 -v "${ro_bin}" | awk '/0001a0/ {print $6};')"
+ case "${key_byte}" in
+ (?[4567cdef])
+ if [ "${type_expected}" == "prod" ]; then
+ return 0
+ fi
+ ;;
+ (?[012389ab])
+ if [ "${type_expected}" == "dev" ]; then
+ return 0
+ fi
+ ;;
+ esac
+
+ echo "RO key in ${ro_bin} does not match type ${type_expected}" >&2
+ exit 1
+}
+
+# This function prepares a full CR50 image, consisting of two ROs and two RWs
+# placed at their respective offsets into the resulting blob. It invokes the
+# bs (binary signer) script to actually convert elf versions of RWs into
+# binaries and sign them.
+#
+# The signed image is placed in the directory named as concatenation of RO and
+# RW version numbers and board ID fields, if set to non-default. The ebuild
+# downloading the tarball from the BCS expects the image to be in that
+# directory.
+prepare_image() {
+ local awk_prog
+ local count=0
+ local extra_param=
+ local image_type="${1}"
+ local raw_version
+ local ro_a_hex="$(readlink -f "${2}")"
+ local ro_b_hex="$(readlink -f "${3}")"
+ local rw_a="$(readlink -f "${4}")"
+ local rw_b="$(readlink -f "${5}")"
+ local version
+
+ for f in "${ro_a_hex}" "${ro_b_hex}"; do
+ if ! objcopy -I ihex "${f}" -O binary "${TMPD}/${count}.bin"; then
+ echo "failed to convert ${f} from hex to bin" >&2
+ exit 1
+ fi
+ verify_ro "${TMPD}/${count}.bin" "${image_type}"
+ : $(( count += 1 ))
+ done
+
+ if [ "${image_type}" == "prod" ]; then
+ extra_param+=' prod'
+ fi
+
+ if ! "${EC_ROOT}/util/signer/bs" ${extra_param} elves \
+ "${rw_a}" "${rw_b}" > /dev/null;
+ then
+ echo "Failed invoking ${EC_ROOT}/util/signer/bs ${extra_param} " \
+ "elves ${rw_a} ${rw_b}" >&2
+ exit 1
+ fi
+
+ dd if="${TMPD}/0.bin" of="${RESULT_FILE}" conv=notrunc
+ dd if="${TMPD}/1.bin" of="${RESULT_FILE}" seek=262144 bs=1 conv=notrunc
+
+ # A typical Cr50 version reported by gsctool looks as follows:
+ # RO_A:0.0.10 RW_A:0.0.22[ABCD:00000013:00000012] ...(the same for R[OW]_B).
+ #
+ # In case Board ID field is not set in the image, it is reported as
+ # [00000000:00000000:00000000]
+ #
+ # We want the generated tarball file name to include all relevant version
+ # fields. Let's retrieve the version string and process it using awk to
+ # generate the proper file name. Only the RO_A and RW_A version numbers are
+ # used, this script trusts the user to submit for processing a proper image
+ # where both ROs and both RWs are of the same version respectively.
+ #
+ # As a result, blob versions are converted as follows:
+ # RO_A:0.0.10 RW_A:0.0.22[ABCD:00000013:00000012] into
+ # r0.0.10.w0.0.22_ABCD_00000013_00000012
+ #
+ # RO_A:0.0.10 RW_A:0.0.22[00000000:00000000:00000000] into
+ # r0.0.10.w0.0.22
+ #
+ # The below awk program accomplishes this preprocessing.
+ awk_prog='/^RO_A:/ {
+ # drop the RO_A/RW_A strings
+ gsub(/R[OW]_A:/, "")
+ # Drop default mask value completely.
+ gsub(/\[00000000:00000000:00000000\]/, "")
+ # If there is a non-default mask:
+ # - replace opening brackets and colons with underscores.
+ gsub(/[\[\:]/, "_")
+ # - drop the trailing bracket.
+ gsub(/\]/, "")
+ # Print filtered out RO_A and RW_A values
+ print "r" $1 ".w" $2
+}'
+
+ raw_version="$("${GSCTOOL}" -b "${RESULT_FILE}")" ||
+ ( echo "${ME}: Failed to retrieve blob version" >&2 && exit 1 )
+
+ version="$(awk "${awk_prog}" <<< "${raw_version}" )"
+ if [ -z "${dest_dir}" ]; then
+ # Note that this is a global variable
+ dest_dir="cr50.${version}"
+ if [ ! -d "${dest_dir}" ]; then
+ mkdir "${dest_dir}"
+ else
+ echo "${dest_dir} already exists, will overwrite" >&2
+ fi
+ elif [ "${dest_dir}" != "cr50.${version}" ]; then
+ echo "dev and prod versions mismatch!" >&2
+ exit 1
+ fi
+
+ cp "${RESULT_FILE}" "${dest_dir}/cr50.bin.${image_type}"
+ echo "saved ${image_type} binary in ${dest_dir}/cr50.bin.${image_type}"
+}
+
+# Execution starts here ===========================
+ME="$(basename $0)"
+
+if [ -z "${CROS_WORKON_SRCROOT}" ]; then
+ echo "${ME}: This script must run inside Chrome OS chroot" >&2
+ exit 1
+fi
+
+SCRIPT_ROOT="${CROS_WORKON_SRCROOT}/src/scripts"
+. "${SCRIPT_ROOT}/build_library/build_common.sh" || exit 1
+
+TMPD="$(mktemp -d /tmp/${ME}.XXXXX)"
+trap "/bin/rm -rf ${TMPD}" SIGINT SIGTERM EXIT
+
+EC_ROOT="${CROS_WORKON_SRCROOT}/src/platform/ec"
+RESULT_FILE="${TMPD}/release.bin"
+dest_dir=
+IMAGE_SIZE='524288'
+export RESULT_FILE
+
+GSCTOOL="/usr/sbin/gsctool"
+if [[ ! -x "${GSCTOOL}" ]]; then
+ emerge_command="USE=cr50_onboard sudo -E emerge ec-utils"
+ echo "${ME}: gsctool not found, run \"${emerge_command}\"" >&2
+ exit 1
+fi
+
+DEFINE_string cr50_board_id "" \
+ "Optional string representing Board ID field of the Cr50 RW header.
+Consists of three fields separated by colon: <RLZ>:<hex mask>:<hex flags>"
+
+# Do not put this before the DEFINE_ invocations - they routinely experience
+# error return values.
+set -e
+
+FLAGS_HELP="usage: ${ME} [flags] <blobs>
+
+blobs are:
+ <prod RO A>.hex <prod RO B>.hex <RW.elf> <RW_B.elf>"
+
+# Parse command line.
+FLAGS "$@" || exit 1
+
+eval set -- "${FLAGS_ARGV}"
+if [ "${#*}" != "4" ]; then
+ flags_help
+ exit 1
+fi
+
+dd if=/dev/zero bs="${IMAGE_SIZE}" count=1 2>/dev/null |
+ tr \\000 \\377 > "${RESULT_FILE}"
+if [ "$(stat -c '%s' "${RESULT_FILE}")" != "${IMAGE_SIZE}" ]; then
+ echo "Failed creating ${RESULT_FILE}" >&2
+ exit 1
+fi
+
+prod_ro_a="${1}"
+prod_ro_b="${2}"
+rw_a="${3}"
+rw_b="${4}"
+
+# Used by the bs script.
+export CR50_BOARD_ID="${FLAGS_cr50_board_id}"
+
+prepare_image 'prod' "${prod_ro_a}" "${prod_ro_b}" "${rw_a}" "${rw_b}"
+tarball="${dest_dir}.tbz2"
+tar jcf "${tarball}" "${dest_dir}"
+rm -rf "${dest_dir}"
+
+bcs_path="gs://chromeos-localmirror/distfiles"
+echo "SUCCESS!!!!!!"
+echo "use the below commands to copy the new image to the BCS"
+echo "gsutil cp ${tarball} ${bcs_path}"
+echo "gsutil acl ch -u AllUsers:R ${bcs_path}/${tarball}"
diff --git a/util/signer/ec_RW-manifest-dev.json b/util/signer/ec_RW-manifest-dev.json
new file mode 100644
index 0000000000..a5c28894a1
--- /dev/null
+++ b/util/signer/ec_RW-manifest-dev.json
@@ -0,0 +1,48 @@
+{
+// List of fuses and their expected values.
+"fuses": {
+ "FLASH_PERSO_PAGE_LOCK": 5, // individualized
+ "FW_DEFINED_DATA_BLK0": 2, // kevin EVT 1
+ "FW_DEFINED_DATA_EXTRA_BLK6": 0 // escape hatch
+},
+// Rollback state.
+"info": {
+ "1": -1, "2": -1, "3": -1, "4": -1, "5": -1, "6": -1,
+ "7": -1, "8": -1, "9": -1, "10": -1, "11": -1, "12": -1 , "13": -1,
+ "14": -1, "15": -1, "16": -1, "17": -1, "18": -1, "19": -1, "20": -1,
+ "21": -1, "22": -1, "23": -1, "24": -1, "25": -1, "26": -1, "27": -1,
+ "28": -1, "29": -1, "30": -1, "31": -1, "32": -1, "33": -1, "34": -1,
+ "35": -1, "36": -1, "37": -1, "38": -1, "39": -1, "40": -1, "41": -1,
+ "42": -1, "43": -1, "44": -1, "45": -1, "46": -1, "47": -1, "48": -1,
+ "49": -1, "50": -1, "51": -1, "52": -1, "53": -1, "54": -1, "55": -1,
+ "56": -1, "57": -1, "58": -1, "59": -1, "60": -1, "61": -1, "62": -1,
+ "63": -1, "64": -1, "65": -1, "66": -1, "67": -1, "68": -1, "69": -1,
+ "70": -1, "71": -1, "72": -1, "73": -1, "74": -1, "75": -1, "76": -1,
+ "77": -1, "78": -1, "79": -1, "80": -1, "81": -1, "82": -1, "83": -1,
+ "84": -1, "85": -1, "86": -1, "87": -1, "88": -1, "89": -1, "90": -1,
+ "91": -1, "92": -1, "93": -1, "94": -1, "95": -1, "96": -1, "97": -1,
+ "98": -1, "99": -1, "100": -1, "101": -1, "102": -1, "103": -1, "104": -1,
+"105": -1, "106": -1, "107": -1, "108": -1, "109": -1, "110": -1, "111": -1,
+"112": -1, "113": -1, "114": -1, "115": -1, "116": -1, "117": -1, "118": -1,
+"119": -1, "120": -1, "121": -1, "122": -1, "123": -1, "124": -1, "125": -1,
+"126": -1, "127": -1
+},
+
+ // Note: tag needs to match what cros_personalize anticipated!
+ // https://cs.corp.google.com/search/?q=kCrosFwr
+ "tag": "00000000000000000000000000000000000000000000000000000000",
+
+ // cros_loader uses b1-dev key as key to verify RW with
+ "keyid": -1187158727, // b1-dev key
+
+ "p4cl": 177, // P4 sync cl for XML we link against. 177 == 0xb1.
+
+ "timestamp": 0,
+ "epoch": 0, // FWR diversification contributor, 32 bits.
+ "major": 0, // FW2_HIK_CHAIN counter.
+ "minor": 25, // Mostly harmless version field.
+ "applysec": -1, // Mask to and with fuse BROM_APPLYSEC.
+ "config1": 13, // Which BROM_CONFIG1 actions to take before launching.
+ "err_response": 0, // Mask to or with fuse BROM_ERR_RESPONSE.
+ "expect_response": 3 // purgatory level when expectation fails.
+}
diff --git a/util/signer/ec_RW-manifest-prod.json b/util/signer/ec_RW-manifest-prod.json
new file mode 100644
index 0000000000..585a2f5d21
--- /dev/null
+++ b/util/signer/ec_RW-manifest-prod.json
@@ -0,0 +1,52 @@
+{
+// List of fuses and their expected values.
+"fuses": {
+ "FLASH_PERSO_PAGE_LOCK": 5, // individualized
+ "FW_DEFINED_DATA_BLK0": 2, // cros SKU
+ "FW_DEFINED_DATA_EXTRA_BLK6": 0 // escape hatch
+},
+// Rollback state.
+"info": {
+ "1": -1, "2": -1, "3": -1, "4": -1, "5": -1, "6": -1,
+ "7": -1, "8": -1, "9": -1, "10": -1, "11": -1, "12": -1 , "13": -1,
+ "14": -1, "15": -1, "16": -1, "17": -1, "18": -1, "19": -1, "20": -1,
+ "21": -1, "22": -1, "23": -1, "24": -1, "25": -1, "26": -1, "27": -1,
+ "28": -1, "29": -1, "30": -1, "31": -1, "32": -1, "33": -1, "34": -1,
+ "35": -1, "36": -1, "37": -1, "38": -1, "39": -1, "40": -1, "41": -1,
+ "42": -1, "43": -1, "44": -1, "45": -1, "46": -1, "47": -1, "48": -1,
+ "49": -1, "50": -1, "51": -1, "52": -1, "53": -1, "54": -1, "55": -1,
+ "56": -1, "57": -1, "58": -1, "59": -1, "60": -1, "61": -1, "62": -1,
+ "63": -1, "64": -1, "65": -1, "66": -1, "67": -1, "68": -1, "69": -1,
+ "70": -1, "71": -1, "72": -1, "73": -1, "74": -1, "75": -1, "76": -1,
+ "77": -1, "78": -1, "79": -1, "80": -1, "81": -1, "82": -1, "83": -1,
+ "84": -1, "85": -1, "86": -1, "87": -1, "88": -1, "89": -1, "90": -1,
+ "91": -1, "92": -1, "93": -1, "94": -1, "95": -1, "96": -1, "97": -1,
+ "98": -1, "99": -1, "100": -1, "101": -1, "102": -1, "103": -1, "104": -1,
+"105": -1, "106": -1, "107": -1, "108": -1, "109": -1, "110": -1, "111": -1,
+"112": -1, "113": -1, "114": -1, "115": -1, "116": -1, "117": -1, "118": -1,
+"119": -1, "120": -1, "121": -1, "122": -1, "123": -1, "124": -1, "125": -1,
+"126": -1, "127": -1
+},
+
+ // Note: tag needs to match what cros_personalize anticipated!
+ // https://cs.corp.google.com/search/?q=kCrosFwr
+ "tag": "00000000000000000000000000000000000000000000000000000000",
+
+ // cros_loader uses b1-dev key as key to verify RW with
+ //"keyid": -1187158727, // b1-dev key
+ "keyid": -561489779, // prod RW key
+
+ "p4cl": 177, // P4 sync cl for XML we link against. 177 == 0xb1.
+
+ // Make sure a value is there so that current time is not used, and make
+ // sure the value is small so that any dev build with the same
+ // epoch/major/minor would be preferred
+ "timestamp": 1,
+ "epoch": 0, // FWR diversification contributor, 32 bits.
+ "major": 0, // FW2_HIK_CHAIN counter.
+ "minor": 25, // Mostly harmless version field.
+ "applysec": -1, // Mask to and with fuse BROM_APPLYSEC.
+ "config1": 13, // Which BROM_CONFIG1 actions to take before launching.
+ "err_response": 0, // Mask to or with fuse BROM_ERR_RESPONSE.
+ "expect_response": 3 // purgatory level when expectation fails.
+}
diff --git a/util/signer/gnubby.cc b/util/signer/gnubby.cc
index 478d77470f..77cd5eb2a3 100644
--- a/util/signer/gnubby.cc
+++ b/util/signer/gnubby.cc
@@ -294,7 +294,8 @@ void getPIN(uint8_t* out) {
static
std::string tokenFilename(const uint8_t* fp) {
const char* home = getenv("HOME");
- if (home == NULL) getpwuid(getuid())->pw_dir;
+ if (home == NULL)
+ home = getpwuid(getuid())->pw_dir;
std::string s(home);
s.append("/.tmp/");
for (int i = 0; i < 32; ++i) {
diff --git a/util/signer/loader-testkey-A.pem b/util/signer/loader-testkey-A.pem
index b6162bce30..ea16e603e9 100644
--- a/util/signer/loader-testkey-A.pem
+++ b/util/signer/loader-testkey-A.pem
@@ -1,39 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
-MIIG4AIBAAKCAYB6julYjQuar3epRzKCKxlEa35//3QFHtXvqlaJ5yuY8A0+cYbP
-8l1WAILE8VIdOMLpnA+dHSjhTf4ePfOAtPsxSEQs+QA8ESiSvp5VnvzXfkDqG9Xh
-bAQyKTP4pgT9pzzrk3f+S7ig3FJuGM2CTUhTr3lwYR53AZnE0iEuNNVjtzG81fzG
-iyer8GtPhYJnuK4N0cZv5RwVpgecFXs4JSCNCG9lkRblQFaxyiK2oMMe4jmiSP1o
-A8ocKwuE21HEHegiEqX8NvBK4DDv7HnLZ/pi416niHbATzIuOSzCuGmSTXnrUHiY
-Cxb4nRUxGXZ6IZq7pzdq1RjbrMj/P2Hg86Pm+mgS40OS3PhGptJ1nVyE7VegIrts
-CEQrANuaLPgVvpUtUjsLGXUJXHh+OUfULMrPAzLcuIXZDZ3LmEOT0uBOW0Q/sRfI
-B7AdWyBy50zvE+zrBCN5MxwW1Pv3qLAY4YH3HSXYD1uwwG8w5/34wCOoeDBwDuDX
-xQvajP//tOmGyHECAQMCggGAUbSbkF4HvHT6cNohrBy7gvJUVVT4A2nj9Rw5sUTH
-u0qzfvZZ3/bo5ABXLfY2viXXRmgKaL4bQN6paX6iVc38y4WCyKYAKAtwYdRpjmn9
-5P7V8Wfj651YIXDNUG6t/m998mJP/t0lwJLhnrszrDOFjR+mSutppKu72IwWHs3j
-l892feP92bIacqBHilkBmnses+Eu9UNoDm6vvWOnesNrCLBKQ7YPQ4A5y9wXJGss
-v0F7wYX+RVfcEsddAzzhLWlEczbF/afBrQQozQ6aVrRIl9Y5oxUxCFq3sQV9CQtc
-xAmIj7S/L74pVKJL/4w15PS8gEa8ZRT4hw2njIE10S9vt4MBpPYecyWtoibhNFQ4
-OgSN6o4HsSESm48uaXWjcWCECH212r0jZ1KA/tUifnClCdjqgki9JNP9CcWmcMIw
-hdRacjscPxtYkxVZ+rnXI1OG/OLXz+qsEH2Ste48tcZsCA9FgbxOG2aGilTwPI5C
-RvQbWbig6uyo1ea5cDs3tIhLAoHBAPVmSJnuBVIAAv2B+gDqPaY6Gc9tvAq5R8ef
-z73tJGTvS/ei611sLqYDgSiuNJOEDP4TclX7fKPIEXnqChQn3b4bFdVd5vxDvv6u
-Tcz2NYR5QZOvIpOoYF53J0nsQcO/udYdjzZ29NCrn3wV15o0Fe4b5oF16oJw6ALu
-gHGyrYyPro1sl2s/kiSdKEggUUC1vW2+ikYUy8bOzXiTCwJEdYSUvm4s1P1U2R5w
-o4rcNPEBdWqvqR98SI8hIzCrEMw1VQKBwH/aNGXNSHVZ8L7UmEhvT9jn7mN68SkN
-1t0RIXc6OgdP4RwebnMBqlOmIgkY8Q8ucv0pqn06J21QPDKThxMEnpWaz8YULung
-tNS+Mtoo0qltQSyejU6uR+My7rIQQSfuFUp4GwyGyx21F4PjvHxPvVIh7JfVf59e
-ujoBSXj86fkNDgbr1f4fGRwd8TKwOPlg5QHxVXs+TjdJ0m4wSgXzamZxfydIEYpR
-zEL38hMOi00Ikf7KRlflxgqRxqaxBSvGrQKBwQCjmYW79AOMAAH+VqarRtPEJrvf
-nn1ce4Uvv9/T822Yn4f6bJzo8snEAlYbHs23rV3+t6GOp6htMAumnAa4GpPUEg6O
-Ppn9gn9UdDPd+XkC+4ENH2xicEA++hoxSCvX1SaOvl95pKM1x7+oDo+8IrlJZ+8A
-+UcBoJqsnwBLzHOzCnReSGTyKmFtvhrawDYrI9OefwbZYzKEid5QYgdW2E5YYyme
-yI3+OJC+9cJcks32APjxynC/qDBfa2zLHLXdeOMCgcBVPCLuiNr45qB/OGWFn4qQ
-mp7s/KDGCTnothZPfCavipYSvvRMq8bibsFbZfYKHvdTcRxTfBpI4Cght69iAxRj
-vIqEDXSb6yM4fsyRcIxw84DIabOJyYVCIfR2tYDFSWOG+ryzBIdpI2UCl9L9in42
-wUhlOP+/lHwmq4ZQqJv7XglZ8o6pahC9aUt3ICX7le4BS45SKYl6MTb0IDFZTPGZ
-oP9vhWEG4TLXT/a3XweIsGFUhtmP7oQHC9nEdgNyhHMCgcEAhn1Fdj7yQq50JtlD
-TamB86TMRs+yB/MsU80ZhDX0D8z9Ap2fNFQdYmfkNmx93hfN7TghTySTh0oBG24F
-h0WlNFk6ofw/H48K915a4Th1DEzJrR8EmoQtVtfBb5RYrv4vPUBL+nDcyUoxz3ae
-43DPLzd01pTs548g65FOau5FHubkh62KLD5vltdr0LQywljouEMp1jmNPgIYPG8S
-xD1m44kpRvX4qbZMk0uR421MEGQq/xJLWYk28M2m7yAtQQ6J
+MIIEogIBAAKCAQEAp/kh8/NGr1GUMA6c0tq9cRhVMaMwhYCF6mkpeW/D+1k3lL5q
+pkjqDcYBZG4xbhdCgEH9ppPYKzwKBVieWuqf7uymLBlCLmaPA6P4J+IwhS001WoD
+0kACEhnbL4xeP21fwuz9/u6ucoM8kJsFV/gacADmuOKTrU89Kyj2J5iLWVQPMMAM
+BOk+3BNamWwnCRk+CvcT+EQHtzcFkK2avm4HUQNSzhL407NbvsHwUjv7N6wtjeu5
+VLaTLTHxk9Z5savcn2jgxWASn4M59dpD7KSTYi4LsY8NPUWswz0E2a0vk8rfthtA
+amTkU4MT9ohVYq2JTCj5DC3DV/0Z7xiZ+ZsYPQIBAwKCAQBv+2v394R04Q11XxM3
+PH5LZY4hF3WuVa6cRhumSoKnkM+4fvHEMJwJLquYSXZJZNcAK/5vDTrHfVwDkGmR
+8b/0ncQdZiwe7woCbVAalssDc3iORq021Va2u+d1CD7U85Usnf6p9HRMV321vK46
+pWb1Ve8l7GJziijHcKQaZbI7jEq4JKyk9lL7seEWjf2zHyiLnh8wxQK7Ebizrqw9
+EIH3tmC6JKvbGJPizQ6tz1O0bVwiaHmZObouRxBTE8fL2zuSmJunqsYK4xqWfRsb
++RcSDndzBTW89qZr7i3h22g8jUsMiPBqV9/l9w1dOxnWwAtQSHfebcCA2u3OxUGM
+9dpTAoGBANhm0GYySwuCJc8lJpsBUl2tbuw7pzRdDe8BuqGv2aaEHx7arwFat1AA
+ZHVlQquWaKxwCuyFY/QlGq4uTNHhkBgygnFeEvtZ0KaKSVBBXY0Fbhq+N6rsX7FQ
+eRb4sz7We/aFR2K1V52dHaetOjMBfLhX1e7dZRwX8xnSSKuQeB6DAoGBAMa1uKLb
+LLbgYrnScI97GCOMGvjzdU9BjoGBbPay+53ZUqLcLPWwVy3qKeToQlISn3bqRBZp
+fAfCrKro6/weUusRAYXrzO41XeuJ1UsBUWPBqj3Gz5G1dAHQ3qkOMNRievieBnUV
+iXbdctg9dXufEL/75lZhJAZ+wZtmqAwVsjI/AoGBAJBEiu7MMgesGTTDbxIA4ZPI
+9J19GiLos/Sr0cEf5m8Cv2nnH1Y8ejVVmE5Dgce5mx2gB0hY7U1uEcl0MzaWYBAh
+rEuUDKeRNcRcMOArk7NY9BHUJRydlSDgULn7IinkUqRY2kHOOmkTaRpzfCIA/dA6
+jp8+Q2gP92aMMHJgUBRXAoGBAIR5JcHncySVlyaMSwpSEBeyvKX3o4ortFZWSKR3
+Umk7jGySyKPK5MlGxpia1uFhv6ScLWRGUq/XHcdF8qgUN0dgq66dM0l46UexONyr
+i5fWcX6EimEjoqvglHC0II2W/KW+rvi5Bk8+TJAo+P0UtdVSmY7rbVmp1meZxV1j
+zCF/AoGAcm2nAn275kfGZjXkTCYTZ6IXJgxcc4vXhv573UfNIJnC0Sg9rsgFiXHc
+nuQwFh5pTm4hU7uEknc/IobFLdCqM9mqujuYmboj0pmbRfOsjV9hqcmuo1OrSbJa
+gozzsNqU2I6srVW5SlCwWu1c4rBlBZvcdUtBRRb2b6bnhe29ykg=
-----END RSA PRIVATE KEY-----
diff --git a/util/signer/pmjp.py b/util/signer/pmjp.py
new file mode 100755
index 0000000000..92e3db035c
--- /dev/null
+++ b/util/signer/pmjp.py
@@ -0,0 +1,53 @@
+#!/usr/bin/python
+# Copyright 2017 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Poor man's JSON parser.
+
+This module reads the input JSON file, retrieves from it some name/value pairs
+and generates a .h file to allow a C code use the definitions.
+
+The JSON file name is required to be passed in in the command line, the nodes
+this script pays attention to are included in required_keys tuple below.
+"""
+
+import json
+import sys
+
+required_keys = ('epoch', 'major', 'minor')
+
+
+def main(json_file_name):
+ # get rid of the comments
+ json_text = []
+ h_file_text = ['''
+/*
+ * Copyright %d The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* This file was autogenerated, do not edit. */
+''',]
+
+ json_file = open(json_file_name, 'r')
+ for line in json_file.read().splitlines():
+ json_text.append(line.split('//')[0])
+
+ j = json.loads('\n'.join(json_text))
+
+ for key in required_keys:
+ if key in j.keys():
+ value = j[key]
+ else:
+ value = '0'
+
+ h_file_text.append('#define MANIFEST_%s %s' % (key.upper(), value))
+
+ h_file_text.append('')
+ return '\n'.join(h_file_text)
+
+
+if __name__ == '__main__':
+ print main(sys.argv[1])
diff --git a/util/stm32mon.c b/util/stm32mon.c
index 36b968f2c5..f8d1cb8542 100644
--- a/util/stm32mon.c
+++ b/util/stm32mon.c
@@ -27,10 +27,19 @@
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <linux/i2c-dev.h>
+#include <linux/spi/spidev.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
+/*
+ * Some Ubuntu versions do not export SPI_IOC_WR_MODE32 even though
+ * the kernel shipped on those supports it.
+ */
+#ifndef SPI_IOC_WR_MODE32
+#define SPI_IOC_WR_MODE32 _IOW(SPI_IOC_MAGIC, 5, __u32)
+#endif
+
/* Monitor command set */
#define CMD_INIT 0x7f /* Starts the monitor */
@@ -42,6 +51,7 @@
#define CMD_WRITEMEM 0x31 /* Writes memory (SRAM or Flash) */
#define CMD_ERASE 0x43 /* Erases n pages of Flash memory */
#define CMD_EXTERASE 0x44 /* Erases n pages of Flash memory */
+#define CMD_NO_STRETCH_ERASE 0x45 /* Erases while sending busy frame */
#define CMD_WP 0x63 /* Enables write protect */
#define CMD_WU 0x73 /* Disables write protect */
#define CMD_RP 0x82 /* Enables the read protection */
@@ -49,31 +59,41 @@
#define RESP_NACK 0x1f
#define RESP_ACK 0x79
+#define RESP_BUSY 0x76
+
+/* SPI Start of Frame */
+#define SOF 0x5A
/* Extended erase special parameters */
#define ERASE_ALL 0xffff
#define ERASE_BANK1 0xfffe
#define ERASE_BANK2 0xfffd
+/* Upper bound of fully erasing the flash and rebooting the monitor */
+#define MAX_DELAY_MASS_ERASE_REBOOT 100000 /* us */
+
/* known STM32 SoC parameters */
struct stm32_def {
uint16_t id;
const char *name;
- uint32_t flash_start;
uint32_t flash_size;
uint32_t page_size;
- uint32_t cmds_len;
+ uint32_t cmds_len[2];
} chip_defs[] = {
- {0x416, "STM32L15xxB", 0x08000000, 0x20000, 256, 13},
- {0x429, "STM32L15xxB-A", 0x08000000, 0x20000, 256, 13},
- {0x427, "STM32L15xxC", 0x08000000, 0x40000, 256, 13},
- {0x420, "STM32F100xx", 0x08000000, 0x20000, 1024, 13},
- {0x410, "STM32F102R8", 0x08000000, 0x10000, 1024, 13},
- {0x440, "STM32F05x", 0x08000000, 0x10000, 1024, 13},
- {0x444, "STM32F03x", 0x08000000, 0x08000, 1024, 13},
- {0x448, "STM32F07xB", 0x08000000, 0x20000, 2048, 13},
- {0x432, "STM32F37xx", 0x08000000, 0x40000, 2048, 13},
- {0x442, "STM32F09x", 0x08000000, 0x40000, 2048, 13},
+ {0x416, "STM32L15xxB", 0x20000, 256, {13, 13} },
+ {0x429, "STM32L15xxB-A", 0x20000, 256, {13, 13} },
+ {0x427, "STM32L15xxC", 0x40000, 256, {13, 13} },
+ {0x435, "STM32L44xx", 0x40000, 2048, {13, 13} },
+ {0x420, "STM32F100xx", 0x20000, 1024, {13, 13} },
+ {0x410, "STM32F102R8", 0x10000, 1024, {13, 13} },
+ {0x440, "STM32F05x", 0x10000, 1024, {13, 13} },
+ {0x444, "STM32F03x", 0x08000, 1024, {13, 13} },
+ {0x448, "STM32F07xB", 0x20000, 2048, {13, 13} },
+ {0x432, "STM32F37xx", 0x40000, 2048, {13, 13} },
+ {0x442, "STM32F09x", 0x40000, 2048, {13, 13} },
+ {0x431, "STM32F411", 0x80000, 16384, {13, 19} },
+ {0x441, "STM32F412", 0x80000, 16384, {13, 19} },
+ {0x451, "STM32F76x", 0x200000, 32768, {13, 19} },
{ 0 }
};
@@ -82,12 +102,28 @@ struct stm32_def {
#define PAGE_SIZE 256
#define INVALID_I2C_ADAPTER -1
+enum interface_mode {
+ MODE_SERIAL,
+ MODE_I2C,
+ MODE_SPI,
+} mode = MODE_SERIAL;
+
+/* I2c address the EC is listening depends on the device:
+ * stm32f07xxx: 0x76
+ * stm32f411xx: 0x72
+ */
+#define DEFAULT_I2C_SLAVE_ADDRESS 0x76
+
/* store custom parameters */
speed_t baudrate = DEFAULT_BAUDRATE;
int i2c_adapter = INVALID_I2C_ADAPTER;
+const char *spi_adapter;
+int i2c_slave_address = DEFAULT_I2C_SLAVE_ADDRESS;
+uint8_t boot_loader_version;
const char *serial_port = "/dev/ttyUSB1";
const char *input_filename;
const char *output_filename;
+uint32_t offset = 0x08000000, length = 0;
/* optional command flags */
enum {
@@ -144,7 +180,7 @@ int open_serial(const char *port)
/*
* tcsetattr() returns success if any of the modifications succeed, so
* its return value of zero is not an indication of success, one needs
- * to check the result explicitely.
+ * to check the result explicitly.
*/
tcsetattr(fd, TCSANOW, &cfg);
if (tcgetattr(fd, &cfg)) {
@@ -188,11 +224,7 @@ int open_i2c(const int port)
perror("Unable to open i2c adapter");
return -1;
}
- /*
- * When in I2C mode, the bootloader is listening at address 0x76 (10 bit
- * mode), 0x3B (7 bit mode)
- */
- if (ioctl(fd, I2C_SLAVE, 0x3B) < 0) {
+ if (ioctl(fd, I2C_SLAVE, i2c_slave_address >> 1) < 0) {
perror("Unable to select proper address");
close(fd);
return -1;
@@ -201,14 +233,43 @@ int open_i2c(const int port)
return fd;
}
+int open_spi(const char *port)
+{
+ int fd;
+ int res;
+ uint32_t mode = SPI_MODE_0;
+ uint8_t bits = 8;
+
+ fd = open(port, O_RDWR);
+ if (fd == -1) {
+ perror("Unable to open SPI controller");
+ return -1;
+ }
+
+ res = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
+ if (res == -1) {
+ perror("Cannot set SPI mode");
+ close(fd);
+ return -1;
+ }
+
+ res = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
+ if (res == -1) {
+ perror("Cannot set SPI bits per word");
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
static void discard_input(int fd)
{
uint8_t buffer[64];
int res, i;
- /* Skip in i2c mode */
- if (i2c_adapter != INVALID_I2C_ADAPTER)
+ /* Skip in i2c and spi modes */
+ if (mode != MODE_SERIAL)
return;
/* eat trailing garbage */
@@ -228,6 +289,7 @@ int wait_for_ack(int fd)
uint8_t resp;
int res;
time_t deadline = time(NULL) + DEFAULT_TIMEOUT;
+ uint8_t ack = RESP_ACK;
while (time(NULL) < deadline) {
res = read(fd, &resp, 1);
@@ -236,14 +298,25 @@ int wait_for_ack(int fd)
return -EIO;
}
if (res == 1) {
- if (resp == RESP_ACK)
+ if (resp == RESP_ACK) {
+ if (mode == MODE_SPI) /* Ack the ACK */
+ if (write(fd, &ack, 1) != 1)
+ return -EIO;
return 0;
- else if (resp == RESP_NACK) {
+ } else if (resp == RESP_NACK) {
fprintf(stderr, "NACK\n");
+ if (mode == MODE_SPI) /* Ack the NACK */
+ if (write(fd, &ack, 1) != 1)
+ return -EIO;
discard_input(fd);
return -EINVAL;
+ } else if (resp == RESP_BUSY) {
+ /* I2C Boot protocol 1.1 */
+ deadline = time(NULL) + DEFAULT_TIMEOUT;
} else {
- fprintf(stderr, "Receive junk: %02x\n", resp);
+ if (mode == MODE_SERIAL)
+ fprintf(stderr, "Receive junk: %02x\n",
+ resp);
}
}
}
@@ -257,10 +330,12 @@ int send_command(int fd, uint8_t cmd, payload_t *loads, int cnt,
int res, i, c;
payload_t *p;
int readcnt = 0;
- uint8_t cmd_frame[] = { cmd, 0xff ^ cmd }; /* XOR checksum */
+ uint8_t cmd_frame[] = { SOF, cmd, 0xff ^ cmd }; /* XOR checksum */
+ /* only the SPI mode needs the Start Of Frame byte */
+ int cmd_off = mode == MODE_SPI ? 0 : 1;
/* Send the command index */
- res = write(fd, cmd_frame, 2);
+ res = write(fd, cmd_frame + cmd_off, sizeof(cmd_frame) - cmd_off);
if (res <= 0) {
perror("Failed to write command frame");
return -1;
@@ -314,6 +389,9 @@ int send_command(int fd, uint8_t cmd, payload_t *loads, int cnt,
/* Read the answer payload */
if (resp) {
+ if (mode == MODE_SPI) /* ignore dummy byte */
+ if (read(fd, resp, 1) < 0)
+ return -1;
while ((resp_size > 0) && (res = read(fd, resp, resp_size))) {
if (res < 0) {
perror("Failed to read payload");
@@ -367,10 +445,10 @@ struct stm32_def *command_get_id(int fd)
int init_monitor(int fd)
{
int res;
- uint8_t init = CMD_INIT;
+ uint8_t init = mode == MODE_SPI ? SOF : CMD_INIT;
/* Skip in i2c mode */
- if (i2c_adapter != INVALID_I2C_ADAPTER)
+ if (mode == MODE_I2C)
return 0;
printf("Waiting for the monitor startup ...");
@@ -415,9 +493,9 @@ int command_get_commands(int fd, struct stm32_def *chip)
/*
* For i2c, we have to request the exact amount of bytes we expect.
- * TODO(gwendal): Broken on device with Bootloader version 1.1
*/
- res = send_command(fd, CMD_GETCMD, NULL, 0, cmds, chip->cmds_len, 1);
+ res = send_command(fd, CMD_GETCMD, NULL, 0, cmds,
+ chip->cmds_len[(mode == MODE_I2C ? 1 : 0)], 1);
if (res > 0) {
if (cmds[0] > sizeof(cmds) - 2) {
fprintf(stderr, "invalid GET answer (%02x...)\n",
@@ -426,6 +504,7 @@ int command_get_commands(int fd, struct stm32_def *chip)
}
printf("Bootloader v%d.%d, commands : ",
cmds[1] >> 4, cmds[1] & 0xf);
+ boot_loader_version = cmds[1];
erase = command_erase;
for (i = 2; i < 2 + cmds[0]; i++) {
@@ -434,7 +513,7 @@ int command_get_commands(int fd, struct stm32_def *chip)
printf("%02x ", cmds[i]);
}
- if (i2c_adapter != INVALID_I2C_ADAPTER)
+ if (mode == MODE_I2C)
erase = command_erase_i2c;
printf("\n");
@@ -547,6 +626,7 @@ int command_ext_erase(int fd, uint16_t count, uint16_t start)
int command_erase_i2c(int fd, uint16_t count, uint16_t start)
{
int res;
+ uint8_t erase_cmd;
uint16_t count_be = htons(count);
payload_t load[2] = {
{ 2, (uint8_t *)&count_be},
@@ -576,7 +656,9 @@ int command_erase_i2c(int fd, uint16_t count, uint16_t start)
load_cnt = 1;
}
- res = send_command(fd, CMD_EXTERASE, load, load_cnt,
+ erase_cmd = (boot_loader_version == 0x10 ? CMD_EXTERASE :
+ CMD_NO_STRETCH_ERASE);
+ res = send_command(fd, erase_cmd, load, load_cnt,
NULL, 0, 1);
if (res >= 0)
printf("Flash erased.\n");
@@ -631,7 +713,13 @@ int command_read_unprotect(int fd)
}
printf("Flash read unprotected.\n");
- /* This commands triggers a reset */
+ /*
+ * This command triggers a reset.
+ *
+ * Wait at least the 'mass-erase' delay, else we could reconnect
+ * before the actual reset depending on the bootloader.
+ */
+ usleep(MAX_DELAY_MASS_ERASE_REBOOT);
if (init_monitor(fd) < 0) {
fprintf(stderr, "Cannot recover after RP reset\n");
return -EIO;
@@ -655,7 +743,13 @@ int command_write_unprotect(int fd)
}
printf("Flash write unprotected.\n");
- /* This commands triggers a reset */
+ /*
+ * This command triggers a reset.
+ *
+ * Wait at least the 'mass-erase' delay, else we could reconnect
+ * before the actual reset depending on the bootloader.
+ */
+ usleep(MAX_DELAY_MASS_ERASE_REBOOT);
if (init_monitor(fd) < 0) {
fprintf(stderr, "Cannot recover after WP reset\n");
return -EIO;
@@ -693,8 +787,11 @@ int read_flash(int fd, struct stm32_def *chip, const char *filename,
{
int res;
FILE *hnd;
- uint8_t *buffer = malloc(size);
+ uint8_t *buffer;
+ if (!size)
+ size = chip->flash_size;
+ buffer = malloc(size);
if (!buffer) {
fprintf(stderr, "Cannot allocate %d bytes\n", size);
return -ENOMEM;
@@ -707,9 +804,6 @@ int read_flash(int fd, struct stm32_def *chip, const char *filename,
return -EIO;
}
- if (!size)
- size = chip->flash_size;
- offset += chip->flash_start;
printf("Reading %d bytes at 0x%08x\n", size, offset);
res = command_read_mem(fd, offset, size, buffer);
if (res > 0) {
@@ -754,7 +848,6 @@ int write_flash(int fd, struct stm32_def *chip, const char *filename,
}
fclose(hnd);
- offset += chip->flash_start;
printf("Writing %d bytes at 0x%08x\n", res, offset);
written = command_write_mem(fd, offset, res, buffer);
if (written != res) {
@@ -775,31 +868,42 @@ static const struct option longopts[] = {
{"erase", 0, 0, 'e'},
{"go", 0, 0, 'g'},
{"help", 0, 0, 'h'},
+ {"location", 1, 0, 'l'},
{"unprotect", 0, 0, 'u'},
{"baudrate", 1, 0, 'b'},
{"adapter", 1, 0, 'a'},
+ {"spi", 1, 0, 's'},
+ {"length", 1, 0, 'n'},
+ {"offset", 1, 0, 'o'},
{NULL, 0, 0, 0}
};
void display_usage(char *program)
{
fprintf(stderr,
- "Usage: %s [-a <i2c_adapter> | [-d <tty>] [-b <baudrate>]]"
- " [-u] [-e] [-U] [-r <file>] [-w <file>] [-g]\n", program);
+ "Usage: %s [-a <i2c_adapter> [-l address ]] | [-s]"
+ " [-d <tty>] [-b <baudrate>]] [-u] [-e] [-U]"
+ " [-r <file>] [-w <file>] [-o offset] [-l length] [-g]\n",
+ program);
fprintf(stderr, "Can access the controller via serial port or i2c\n");
fprintf(stderr, "Serial port mode:\n");
fprintf(stderr, "--d[evice] <tty> : use <tty> as the serial port\n");
fprintf(stderr, "--b[audrate] <baudrate> : set serial port speed "
"to <baudrate> bauds\n");
fprintf(stderr, "i2c mode:\n");
- fprintf(stderr, "--a[dapter] <id> : use i2c adapter <id>.\n\n");
+ fprintf(stderr, "--a[dapter] <id> : use i2c adapter <id>.\n");
+ fprintf(stderr, "--l[ocation] <address> : use address <address>.\n");
+ fprintf(stderr, "--s[pi]: use spi mode.\n");
fprintf(stderr, "--u[nprotect] : remove flash write protect\n");
fprintf(stderr, "--U[nprotect] : remove flash read protect\n");
fprintf(stderr, "--e[rase] : erase all the flash content\n");
fprintf(stderr, "--r[ead] <file> : read the flash content and "
"write it into <file>\n");
+ fprintf(stderr, "--s[pi] </dev/spi> : use SPI adapter on </dev>.\n");
fprintf(stderr, "--w[rite] <file|-> : read <file> or\n\t"
"standard input and write it to flash\n");
+ fprintf(stderr, "--o[ffset] : offset to read/write/start from/to\n");
+ fprintf(stderr, "--n[length] : amount to read/write\n");
fprintf(stderr, "--g[o] : jump to execute flash entrypoint\n");
exit(2);
@@ -832,17 +936,22 @@ int parse_parameters(int argc, char **argv)
int opt, idx;
int flags = 0;
- while ((opt = getopt_long(argc, argv, "a:b:d:eghr:w:uU?",
+ while ((opt = getopt_long(argc, argv, "a:l:b:d:eghn:o:r:s:w:uU?",
longopts, &idx)) != -1) {
switch (opt) {
case 'a':
i2c_adapter = atoi(optarg);
+ mode = MODE_I2C;
+ break;
+ case 'l':
+ i2c_slave_address = strtol(optarg, NULL, 0);
break;
case 'b':
baudrate = parse_baudrate(optarg);
break;
case 'd':
serial_port = optarg;
+ mode = MODE_SERIAL;
break;
case 'e':
flags |= FLAG_ERASE;
@@ -854,9 +963,19 @@ int parse_parameters(int argc, char **argv)
case '?':
display_usage(argv[0]);
break;
+ case 'n':
+ length = strtol(optarg, NULL, 0);
+ break;
+ case 'o':
+ offset = strtol(optarg, NULL, 0);
+ break;
case 'r':
input_filename = optarg;
break;
+ case 's':
+ spi_adapter = optarg;
+ mode = MODE_SPI;
+ break;
case 'w':
output_filename = optarg;
break;
@@ -881,11 +1000,17 @@ int main(int argc, char **argv)
/* Parse command line options */
flags = parse_parameters(argc, argv);
- if (i2c_adapter == INVALID_I2C_ADAPTER) {
+ switch (mode) {
+ case MODE_SPI:
+ ser = open_spi(spi_adapter);
+ break;
+ case MODE_I2C:
+ ser = open_i2c(i2c_adapter);
+ break;
+ case MODE_SERIAL:
+ default:
/* Open the serial port tty */
ser = open_serial(serial_port);
- } else {
- ser = open_i2c(i2c_adapter);
}
if (ser < 0)
return 1;
@@ -905,9 +1030,9 @@ int main(int argc, char **argv)
command_write_unprotect(ser);
if (flags & FLAG_ERASE || output_filename) {
- if (!strncmp("STM32L15", chip->name, 8)) {
- /* Mass erase is not supported on STM32L15xx */
- /* command_ext_erase(ser, ERASE_ALL, 0); */
+ if ((!strncmp("STM32L15", chip->name, 8)) ||
+ (!strncmp("STM32F41", chip->name, 8))) {
+ /* Mass erase is not supported on these chips*/
int i, page_count = chip->flash_size / chip->page_size;
for (i = 0; i < page_count; i += 128) {
int count = MIN(128, page_count - i);
@@ -923,21 +1048,20 @@ int main(int argc, char **argv)
}
if (input_filename) {
- ret = read_flash(ser, chip, input_filename,
- 0, chip->flash_size);
+ ret = read_flash(ser, chip, input_filename, offset, length);
if (ret)
goto terminate;
}
if (output_filename) {
- ret = write_flash(ser, chip, output_filename, 0);
+ ret = write_flash(ser, chip, output_filename, offset);
if (ret)
goto terminate;
}
/* Run the program from flash */
if (flags & FLAG_GO)
- command_go(ser, chip->flash_start);
+ command_go(ser, offset);
/* Normal exit */
ret = 0;
diff --git a/util/tagbranch.sh b/util/tagbranch.sh
new file mode 100755
index 0000000000..3e196b6f25
--- /dev/null
+++ b/util/tagbranch.sh
@@ -0,0 +1,84 @@
+#!/bin/bash
+#
+# Copyright 2017 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# Generate git strings for tagging EC branches.
+#
+# This script builds up on ideas put by vpalatin@ into util/getversion.sh
+#
+# Git allows to count number of patches between the current state of the tree
+# and any directly preceding tag in the tree. That is if we tag the first
+# patch in the current branch, we can tell how many patches are in the branch
+# above that tag. And if we tag the branch such that the tag string includes
+# the branch name, we can also say what branch we are in looking at the
+# closest tag in the tree.
+#
+# This admittedly brittle script automates the process of tagging for the EC
+# git tree in Chrome OS repo, but it could be used for any other Chrome OS
+# repo git tree just as well.
+#
+# The script is brittle because it relies on the following assumptions which
+# are true for Chrome OS repo at the time of writing:
+#
+# - the upstream branch alias name shows up in the 'git branch -a' output
+# separated by ->
+# - the upstream branch alias name has the format of
+# cros/<branch name>
+# - the remote git server name shows up in 'git config -l' output in the
+# line starting with "remote.cros.url="
+# - firmware branch names have format of firmware-<board>-XXXXXX
+# - the current branch was cut off of <remote name>/master
+#
+# The tag name generated by this script would be the XXXXX string with dots,
+# if any, replaced by underscores.
+
+# Retrieve the upstream branch alias name
+UPSTREAM="$(git branch -a | awk '/->/ {print $3}')"
+if [[ -z "${UPSTREAM}" ]]; then
+ echo "Failed to determine upstream branch alias" >&2
+ exit 1
+fi
+
+export ORIGIN_NAME="cros"
+ORIGIN="$(git config "remote.${ORIGIN_NAME}.url")"
+
+# The last common patch between this branch and the master.
+BRANCH_POINT="$(git merge-base "${UPSTREAM}" "${ORIGIN_NAME}/master")"
+if [[ -z "${BRANCH_POINT}" ]]; then
+ echo "Failed to determine cros/master branch point" >&2
+ exit 1
+fi
+
+# Derive tag base string from the upstream branch name as described above.
+TAG_BASE="$(sed 's/.*-// # drop everything up to including the last -
+ s/\./_/g # replace dots and dashes with underscores
+ ' <<< "${UPSTREAM}" )"
+
+if [[ "${TAG_BASE}" == "master" ]]; then
+ echo "Nothing to tag in master branch" >&2
+ exit 1
+fi
+
+TAG="v1.${TAG_BASE}.0"
+
+#SHA1 of the first patch of this branch
+BASE_SHA="$(git rev-list --ancestry-path "${BRANCH_POINT}".."${UPSTREAM}" |
+ tail -1)"
+
+echo "Will run git tag -a -m \"firmware branch ${TAG}\" ${TAG} ${BASE_SHA}"
+if git tag -a -m "firmware branch ${TAG}" "${TAG}" "${BASE_SHA}"; then
+ cat <<EOF
+
+A new tag '$TAG' has been set. Use the following command
+to push it to the server
+
+git push --tags ${ORIGIN} ${TAG}
+
+Or if you want to delete it:
+
+git tag -d $TAG
+
+EOF
+fi