diff options
author | Henry Sun <henrysun@google.com> | 2019-12-17 13:59:47 +0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-05-27 07:17:02 +0000 |
commit | 05763ab160f1fa395d80b7159b3d71e30e6045c5 (patch) | |
tree | ed02b006498b0eaaa0743f92ca8328b2641c2ed4 | |
parent | b3af8a7125cef37701af5466fd30064b1bf4ee02 (diff) | |
download | chrome-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>
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)¤t_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 Binary files differnew file mode 100644 index 0000000000..ea9f6a9a2d --- /dev/null +++ b/extra/usb_updater/desc_parser.o 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(§ions[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(§ions[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, ©_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(§ions[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], ¶m)) { 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, ¶m, 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, + ¶m, 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, ¶m, 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, + ¶m, 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 |