summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNamyoon Woo <namyoon@google.com>2020-03-07 21:34:18 -0800
committerCommit Bot <commit-bot@chromium.org>2020-03-11 20:55:41 +0000
commit744a123033a043f7b10b7937ed7814fa1505d3fd (patch)
treece70fdf2b7197b8c80e9c4c120e5db1a249a1bb9
parentb3a99aa3f7d4c9c34363a4bc905fd07e22bb27b8 (diff)
downloadchrome-ec-744a123033a043f7b10b7937ed7814fa1505d3fd.tar.gz
introducing an unittest of EC-EFS
This patch adds a test case for EC-EFS functions. BUG=b:150650877 BRANCH=cr50 TEST=make run-ec_comm make runhosttests make buildall -j Signed-off-by: Namyoon Woo <namyoon@chromium.org> Change-Id: I90cdc3aa73cf8946da4cf094de5ca0adfaaa0a7c Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2096338 Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
-rw-r--r--board/host/board.c25
-rw-r--r--board/host/board.h16
-rw-r--r--board/host/gpio.inc5
-rw-r--r--board/host/tpm_nvmem_ops.h31
-rw-r--r--chip/host/build.mk4
-rw-r--r--chip/host/registers.c36
-rw-r--r--chip/host/registers.h5
-rw-r--r--chip/host/uart.c84
-rw-r--r--chip/host/uartn.h26
-rw-r--r--common/ec_efs.c7
-rw-r--r--include/ec_comm.h5
-rw-r--r--include/test_util.h5
-rw-r--r--test/build.mk2
-rw-r--r--test/ec_comm.c374
-rw-r--r--test/ec_comm.tasklist9
-rw-r--r--test/test_config.h5
16 files changed, 620 insertions, 19 deletions
diff --git a/board/host/board.c b/board/host/board.c
index 80ac631d19..cccb6f15ec 100644
--- a/board/host/board.c
+++ b/board/host/board.c
@@ -5,6 +5,7 @@
/* Emulator board-specific configuration */
#include "button.h"
+#include "ec_comm.h"
#include "extpower.h"
#include "gpio.h"
#include "host_command.h"
@@ -89,3 +90,27 @@ int board_get_entropy(void *buffer, int len)
return 1;
}
#endif
+
+test_mockable void ccd_update_state(void)
+{
+
+}
+
+test_mockable void ec_comm_packet_mode_en(enum gpio_signal unsed)
+{
+
+}
+
+test_mockable void ec_comm_packet_mode_dis(enum gpio_signal unsed)
+{
+
+}
+
+int board_has_ec_cr50_comm_support(void)
+{
+#ifdef CONFIG_EC_EFS_SUPPORT
+ return 1;
+#else
+ return 0;
+#endif
+}
diff --git a/board/host/board.h b/board/host/board.h
index ad65749c3d..8ccdd40b80 100644
--- a/board/host/board.h
+++ b/board/host/board.h
@@ -77,10 +77,26 @@ enum {
#define CONFIG_SPI_MASTER
#define CONFIG_SPI_FP_PORT 1 /* SPI1: third master config */
+/* UART indexes (use define rather than enum to expand them) */
+enum {
+ UART_DEFAULT = 0,
+ UART_CR50 = 0,
+ UART_AP = 1,
+ UART_EC = 2,
+ UART_COUNT,
+
+ UART_NULL = 0xff,
+};
+
#define CONFIG_RNG
void fps_event(enum gpio_signal signal);
/* Let the tests always check the other NVMEM slot. */
static inline int board_nvmem_legacy_check_needed(void){ return 1; }
+/* Mock functions for EC-CR50 communication test */
+int board_has_ec_cr50_comm_support(void);
+void board_reboot_ec_deferred(int usec_delay);
+void ccd_update_state(void);
+
#endif /* __CROS_EC_BOARD_H */
diff --git a/board/host/gpio.inc b/board/host/gpio.inc
index 5a08172a07..bf2f974bd3 100644
--- a/board/host/gpio.inc
+++ b/board/host/gpio.inc
@@ -16,6 +16,11 @@ GPIO_INT(VOLUME_UP_L, PIN(0, 4), GPIO_INT_BOTH, button_interrupt)
GPIO_INT(CHARGE_DONE, PIN(0, 5), GPIO_INT_BOTH, inductive_charging_interrupt)
/* Fingerprint */
GPIO_INT(FPS_INT, PIN(0, 14), GPIO_INT_RISING, fps_event)
+/* GPIOs for EC-CR50 communication */
+GPIO_INT(EC_PACKET_MODE_EN, PIN(0, 16), GPIO_INT_RISING,
+ ec_comm_packet_mode_en)
+GPIO_INT(EC_PACKET_MODE_DIS, PIN(0, 17), GPIO_INT_FALLING,
+ ec_comm_packet_mode_dis)
GPIO(EC_INT_L, PIN(0, 6), 0)
GPIO(WP, PIN(0, 7), 0)
diff --git a/board/host/tpm_nvmem_ops.h b/board/host/tpm_nvmem_ops.h
new file mode 100644
index 0000000000..f2090f7733
--- /dev/null
+++ b/board/host/tpm_nvmem_ops.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2020 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_OPS_H
+#define __EC_BOARD_CR50_TPM_NVMEM_OPS_H
+
+enum tpm_read_rv {
+ TPM_READ_SUCCESS,
+ TPM_READ_NOT_FOUND,
+ TPM_READ_TOO_SMALL,
+};
+
+enum tpm_write_rv {
+ TPM_WRITE_CREATED,
+ TPM_WRITE_UPDATED,
+ TPM_WRITE_FAIL,
+};
+
+enum tpm_nv_hidden_object {
+ TPM_HIDDEN_U2F_KEK,
+ TPM_HIDDEN_U2F_KH_SALT,
+};
+
+enum tpm_read_rv read_tpm_nvmem(uint16_t object_index,
+ uint16_t object_size,
+ void *obj_value);
+
+#endif /* ! __EC_BOARD_CR50_TPM_NVMEM_OPS_H */
diff --git a/chip/host/build.mk b/chip/host/build.mk
index f57fe85502..f018c2a281 100644
--- a/chip/host/build.mk
+++ b/chip/host/build.mk
@@ -8,8 +8,8 @@
CORE:=host
-chip-y=system.o gpio.o uart.o persistence.o flash.o lpc.o reboot.o i2c.o \
- clock.o spi_master.o trng.o
+chip-y=clock.o flash.o gpio.o i2c.o lpc.o persistence.o reboot.o registers.o \
+ spi_master.o system.o trng.o uart.o
ifndef CONFIG_KEYBOARD_NOT_RAW
chip-$(HAS_TASK_KEYSCAN)+=keyboard_raw.o
endif
diff --git a/chip/host/registers.c b/chip/host/registers.c
new file mode 100644
index 0000000000..fad062ea15
--- /dev/null
+++ b/chip/host/registers.c
@@ -0,0 +1,36 @@
+/* Copyright 2020 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.
+ *
+ * Registers for test
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "registers.h"
+#include "util.h"
+
+static struct faux_register_array {
+ const char * const name;
+ unsigned int var;
+} mock_registers_[] = {
+ {
+ .name = GNAME(PMU, PWRDN_SCRATCH20),
+ },
+
+ /* Define registers as needed. */
+};
+
+void *get_reg_addr(const char * const reg_name)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mock_registers_); i++)
+ if (!strcmp(mock_registers_[i].name, reg_name))
+ return &mock_registers_[i].var;
+
+ fprintf(stderr, "Unknown register is accessed: %s\n", reg_name);
+ exit(1);
+}
diff --git a/chip/host/registers.h b/chip/host/registers.h
index 7347ce04d3..87cd17d5fd 100644
--- a/chip/host/registers.h
+++ b/chip/host/registers.h
@@ -9,3 +9,8 @@
* There is no register for emulator, but this file exists to prevent
* compilation failure if any file includes registers.h
*/
+
+#define GNAME(mname, rname) "GC_ ## mname ## _ ## rname ## _NAME"
+#define GREG32(mname, rname) REG32(get_reg_addr(GNAME(mname, rname)))
+
+void *get_reg_addr(const char * const reg_name);
diff --git a/chip/host/uart.c b/chip/host/uart.c
index 578924612f..4d505acf80 100644
--- a/chip/host/uart.c
+++ b/chip/host/uart.c
@@ -8,6 +8,7 @@
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
+#include <stdlib.h>
#include <termio.h>
#include <unistd.h>
@@ -16,6 +17,7 @@
#include "task.h"
#include "test_util.h"
#include "uart.h"
+#include "uartn.h"
#include "util.h"
static int stopped = 1;
@@ -31,34 +33,69 @@ static int char_available;
static struct queue const cached_char = QUEUE_NULL(INPUT_BUFFER_SIZE, char);
#define CONSOLE_CAPTURE_SIZE 2048
-static char capture_buf[CONSOLE_CAPTURE_SIZE];
-static int capture_size;
-static int capture_enabled;
+static struct {
+ char buf[CONSOLE_CAPTURE_SIZE];
+ int size;
+ int enabled;
+} uart_capture[UART_COUNT];
-void test_capture_console(int enabled)
+
+static void putch_into_captured_buf(int uart, char c, int is_last)
{
- if (enabled == capture_enabled)
+ uart_capture[uart].buf[uart_capture[uart].size] = c;
+ if (!is_last)
+ uart_capture[uart].size++;
+}
+
+void test_capture_uartn(int uart, int enabled)
+{
+ if (uart >= UART_COUNT) {
+ fprintf(stderr, "Unknown UART port accessed: %d\n", uart);
+ exit(1);
+ }
+
+ if (enabled == uart_capture[uart].enabled)
return;
if (enabled)
- capture_size = 0;
+ uart_capture[uart].size = 0;
else
- capture_buf[capture_size] = '\0';
+ putch_into_captured_buf(uart, '\0', 1);
- capture_enabled = enabled;
+ uart_capture[uart].enabled = enabled;
}
-static void test_capture_char(char c)
+void test_capture_console(int enabled)
+{
+ test_capture_uartn(UART_DEFAULT, enabled);
+}
+
+static void test_capture_char(int uart, char c)
{
- if (capture_size == CONSOLE_CAPTURE_SIZE)
+ if (uart >= UART_COUNT) {
+ fprintf(stderr, "Unknown UART port accessed: %d\n", uart);
+ exit(1);
+ }
+
+ if (uart_capture[uart].size == CONSOLE_CAPTURE_SIZE)
return;
- capture_buf[capture_size++] = c;
+
+ putch_into_captured_buf(uart, c, 0);
}
+const char *test_get_captured_uartn(int uart)
+{
+ if (uart >= UART_COUNT) {
+ fprintf(stderr, "Unknown UART port accessed: %d\n", uart);
+ exit(1);
+ }
+
+ return uart_capture[uart].buf;
+}
const char *test_get_captured_console(void)
{
- return (const char *)capture_buf;
+ return test_get_captured_uartn(UART_DEFAULT);
}
static void uart_interrupt(void)
@@ -90,7 +127,7 @@ int uart_tx_stopped(void)
void uart_tx_flush(void)
{
- /* Nothing */
+ uartn_tx_flush(UART_DEFAULT);
}
int uart_tx_ready(void)
@@ -105,10 +142,7 @@ int uart_rx_available(void)
void uart_write_char(char c)
{
- if (capture_enabled)
- test_capture_char(c);
- printf("%c", c);
- fflush(stdout);
+ uartn_write_char(UART_DEFAULT, c);
}
int uart_read_char(void)
@@ -193,3 +227,19 @@ void uart_init(void)
stopped = 1; /* Not transmitting yet */
init_done = 1;
}
+
+test_mockable void uartn_tx_flush(int uart_unused)
+{
+ /* Nothing */
+}
+
+test_mockable void uartn_write_char(int uart, char c)
+{
+ if (uart_capture[uart].enabled)
+ test_capture_char(uart, c);
+
+ if (uart == UART_DEFAULT) {
+ printf("%c", c);
+ fflush(stdout);
+ }
+}
diff --git a/chip/host/uartn.h b/chip/host/uartn.h
new file mode 100644
index 0000000000..5056e61bb7
--- /dev/null
+++ b/chip/host/uartn.h
@@ -0,0 +1,26 @@
+/* Copyright 2020 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_UARTN_H
+#define __CROS_EC_UARTN_H
+
+#include "uart.h"
+
+/**
+ * Flush the transmit FIFO.
+ */
+void uartn_tx_flush(int uart);
+
+/**
+ * Send a character to the UART data register.
+ *
+ * If the transmit FIFO is full, blocks until there is space.
+ *
+ * @param c Character to send.
+ */
+void uartn_write_char(int uart, char c);
+
+
+#endif /* __CROS_EC_UARTN_H */
diff --git a/common/ec_efs.c b/common/ec_efs.c
index b61ed87888..9cc4061882 100644
--- a/common/ec_efs.c
+++ b/common/ec_efs.c
@@ -280,3 +280,10 @@ void ec_efs_print_status(void)
HEX_BUF(ec_efs_ctx.hash, SHA256_DIGEST_SIZE));
#endif
}
+
+#ifdef BOARD_HOST
+uint8_t ec_efs_get_boot_mode(void)
+{
+ return ec_efs_ctx.boot_mode;
+}
+#endif
diff --git a/include/ec_comm.h b/include/ec_comm.h
index 263d311e63..dee31df4be 100644
--- a/include/ec_comm.h
+++ b/include/ec_comm.h
@@ -42,4 +42,9 @@ void ec_efs_refresh(void);
/* print EC-EFS status */
void ec_efs_print_status(void);
+#ifdef BOARD_HOST
+/* return the current boot mode. For test purpose only. */
+uint8_t ec_efs_get_boot_mode(void);
+#endif
+
#endif /* __CROS_EC_COMM_H */
diff --git a/include/test_util.h b/include/test_util.h
index 23034f918e..bfcd940cea 100644
--- a/include/test_util.h
+++ b/include/test_util.h
@@ -197,6 +197,11 @@ void test_chipset_on(void);
/* Simulates chipset power off */
void test_chipset_off(void);
+/* Start/stop capturing a specific UART output */
+void test_capture_uartn(int uart, int enabled);
+/* Get captured a specific UART output */
+const char *test_get_captured_uartn(int uart);
+
/* Start/stop capturing console output */
void test_capture_console(int enabled);
diff --git a/test/build.mk b/test/build.mk
index 4284374df0..143041ee7f 100644
--- a/test/build.mk
+++ b/test/build.mk
@@ -23,6 +23,7 @@ test-list-host += charge_manager_drp_charging
test-list-host += charge_ramp
test-list-host += console_edit
test-list-host += crc32
+test-list-host += ec_comm
test-list-host += entropy
test-list-host += extpwr_gpio
test-list-host += fan
@@ -101,6 +102,7 @@ charge_manager_drp_charging-y=charge_manager.o
charge_ramp-y+=charge_ramp.o
console_edit-y=console_edit.o
crc32-y=crc32.o
+ec_comm-y=ec_comm.o
entropy-y=entropy.o
extpwr_gpio-y=extpwr_gpio.o
fan-y=fan.o
diff --git a/test/ec_comm.c b/test/ec_comm.c
new file mode 100644
index 0000000000..ba92e3132d
--- /dev/null
+++ b/test/ec_comm.c
@@ -0,0 +1,374 @@
+/* Copyright 2020 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 ec_comm
+ */
+
+#include "common.h"
+#include "crc8.h"
+#include "ec_comm.h"
+#include "test_util.h"
+#include "timer.h"
+#include "tpm_nvmem.h"
+#include "tpm_nvmem_ops.h"
+#include "uart.h"
+#include "util.h"
+#include "vboot.h"
+
+const uint8_t sample_ec_hash[SHA256_DIGEST_SIZE] = {
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+};
+
+struct vb2_secdata_kernel test_secdata = {
+ .struct_version = VB2_SECDATA_KERNEL_STRUCT_VERSION_MIN,
+ .struct_size = sizeof(struct vb2_secdata_kernel),
+ .reserved0 = 0,
+ .kernel_versions = VB2_SECDATA_KERNEL_UID,
+};
+
+union cr50_test_packet {
+ struct cr50_comm_packet ph;
+ uint8_t packet[CR50_COMM_MAX_PACKET_SIZE * 2];
+};
+
+union cr50_test_packet sample_packet_cmd_set_mode = {
+ .ph.magic = CR50_COMM_MAGIC_WORD,
+ .ph.version = CR50_COMM_VERSION,
+ .ph.crc = 0,
+ .ph.cmd = CR50_COMM_CMD_SET_BOOT_MODE,
+ .ph.size = 1,
+};
+
+union cr50_test_packet sample_packet_cmd_verify_hash = {
+ .ph.magic = CR50_COMM_MAGIC_WORD,
+ .ph.version = CR50_COMM_VERSION,
+ .ph.crc = 0,
+ .ph.cmd = CR50_COMM_CMD_VERIFY_HASH,
+ .ph.size = SHA256_DIGEST_SIZE,
+};
+
+/* EC Reset Count. It is used to see if ec has been reset. */
+static int ec_reset_count_;
+
+/*
+ * Return 1 if EC has been reset since the last call of this function, or
+ * 0 otherwise.
+ */
+static int ec_has_reset(void)
+{
+ static int prev_ec_reset_count;
+
+ if (prev_ec_reset_count == ec_reset_count_)
+ return 0;
+
+ prev_ec_reset_count = ec_reset_count_;
+ return 1;
+}
+
+void board_reboot_ec_deferred(int usec_delay_used)
+{
+ /* ec_reset */
+ ec_reset_count_++;
+ ec_efs_reset();
+}
+
+enum tpm_read_rv read_tpm_nvmem(uint16_t object_index, uint16_t object_size,
+ void *obj_value)
+{
+ /* Check the input parameter */
+ if (object_index != KERNEL_NV_INDEX)
+ return TPM_READ_NOT_FOUND;
+ if (object_size != test_secdata.struct_size)
+ return TPM_READ_TOO_SMALL;
+
+ /*
+ * Copy the test_secdata to obj_value as if it was loaded
+ * from NVMEM.
+ */
+ memcpy(obj_value, (void *)&test_secdata, object_size);
+
+ return TPM_READ_SUCCESS;
+}
+
+/*
+ * Return 1 if the byte is found in buf string.
+ * 0 otherwise
+ */
+static int find_byte(const char *buf, uint8_t byte)
+{
+ int i = strlen(buf);
+
+ while (i--) {
+ if (*buf == byte)
+ return 1;
+ buf++;
+ }
+ return 0;
+}
+
+/*
+ * Calculate CRC8 of the the given CR50 packet and fill it in
+ * the packet.
+ */
+static void calculate_crc8(union cr50_test_packet *pk)
+{
+ const int offset_cmd = offsetof(struct cr50_comm_packet, cmd);
+
+ /* Calculate the sample EC-CR50 packet. */
+ pk->ph.crc = crc8((uint8_t *)&pk->ph.cmd,
+ (sizeof(struct cr50_comm_packet)
+ + pk->ph.size - offset_cmd));
+}
+
+/*
+ * Test EC CR50 communication with the given EC-CR50 packet.
+ *
+ * @param pk an ec_cr50 comm packet to test
+ * @param preambles the number of preambles
+ * @param resp_exp the expected response in two bytes as in
+ * CR50_COMM_RESPONSE.
+ * if it is zero, then no response is expected.
+ */
+static int test_ec_comm(const union cr50_test_packet *pk, int preambles,
+ uint16_t resp_exp)
+{
+ uint8_t *buf;
+ int leng;
+ int i;
+ const char *resp;
+
+ leng = sizeof(struct cr50_comm_packet) + pk->ph.size;
+
+ /* Prepare the input packet. */
+ buf = (uint8_t *)pk;
+
+ /* Start the test */
+ ec_comm_packet_mode_en(1);
+ TEST_ASSERT(ec_comm_is_uart_in_packet_mode(UART_EC));
+
+ for (i = 0; i < preambles; i++)
+ ec_comm_process_packet(CR50_COMM_PREAMBLE);
+
+ test_capture_uartn(UART_EC, 1);
+
+ for (i = 0; i < leng; i++)
+ ec_comm_process_packet(buf[i]);
+
+ resp = test_get_captured_uartn(UART_EC);
+
+ if (resp_exp)
+ TEST_ASSERT(*(uint16_t *)resp == resp_exp);
+ else
+ /* Check if there was any EC-CR50-comm response. */
+ TEST_ASSERT(find_byte(resp, '\xec') == 0);
+
+ test_capture_uartn(UART_EC, 0);
+
+ ec_comm_packet_mode_dis(1);
+ TEST_ASSERT(!ec_comm_is_uart_in_packet_mode(UART_EC));
+
+ return EC_SUCCESS;
+}
+
+/*
+ * Test the failure case for packet errors.
+ */
+static int test_ec_comm_packet_failure(void)
+{
+ /* Copy the sample packet to buffer. */
+ union cr50_test_packet pk = sample_packet_cmd_verify_hash;
+ int preambles = MIN_LENGTH_PREAMBLE;
+
+ ec_has_reset();
+
+ TEST_ASSERT(ec_efs_get_boot_mode() == EC_EFS_BOOT_MODE_NORMAL);
+
+ /* Test 1: Test with less preambles than required. */
+ calculate_crc8(&pk);
+ TEST_ASSERT(!test_ec_comm(&pk, 1, 0));
+
+ /* Test 2: Test a wrong magic */
+ pk.ph.magic = 0x1234;
+ calculate_crc8(&pk);
+ TEST_ASSERT(!test_ec_comm(&pk, preambles, CR50_COMM_ERROR_MAGIC));
+
+ /* Test 3: Test with a wrong CRC */
+ pk = sample_packet_cmd_verify_hash;
+ calculate_crc8(&pk);
+ pk.ph.crc += 0x01; /* corrupt the CRC */
+ TEST_ASSERT(!test_ec_comm(&pk, preambles, CR50_COMM_ERROR_CRC));
+
+ /* Test 4: Test with too large payload */
+ pk = sample_packet_cmd_verify_hash;
+ pk.ph.size = SHA256_DIGEST_SIZE + 1;
+ pk.ph.data[SHA256_DIGEST_SIZE] = 0xff;
+ calculate_crc8(&pk);
+
+ TEST_ASSERT(!test_ec_comm(&pk, preambles, CR50_COMM_ERROR_SIZE));
+
+ /* Test 5: Test with a undefined command */
+ pk = sample_packet_cmd_verify_hash;
+ pk.ph.cmd = 0x1000;
+ calculate_crc8(&pk);
+ TEST_ASSERT(!test_ec_comm(&pk, preambles,
+ CR50_COMM_ERROR_UNDEFINED_CMD));
+
+ /* Test 6: Test with a wrong struct version */
+ pk = sample_packet_cmd_verify_hash;
+ pk.ph.version = CR50_COMM_VERSION + 0x01;
+ calculate_crc8(&pk);
+ TEST_ASSERT(!test_ec_comm(&pk, preambles,
+ CR50_COMM_ERROR_STRUCT_VERSION));
+
+ /* Check if ec has ever been reset during these tests */
+ TEST_ASSERT(!ec_has_reset());
+
+ TEST_ASSERT(ec_efs_get_boot_mode() == EC_EFS_BOOT_MODE_NORMAL);
+
+ return EC_SUCCESS;
+}
+
+/*
+ * Test cases for set_boot_mode command.
+ */
+static int test_ec_comm_set_boot_mode(void)
+{
+ /* Copy the sample packet to buffer. */
+ union cr50_test_packet pk = sample_packet_cmd_set_mode;
+ int preambles;
+
+ ec_has_reset();
+
+ /* Test 1: Attempt to set boot mode to NORMAL. */
+ pk.ph.data[0] = EC_EFS_BOOT_MODE_NORMAL;
+ calculate_crc8(&pk);
+ preambles = MIN_LENGTH_PREAMBLE * 2;
+ TEST_ASSERT(!test_ec_comm(&pk, preambles, CR50_COMM_SUCCESS));
+ TEST_ASSERT(!ec_has_reset()); /* EC must not be reset. */
+ TEST_ASSERT(ec_efs_get_boot_mode() == EC_EFS_BOOT_MODE_NORMAL);
+
+ /* Test 2: Attempt to set boot mode to NORMAL again. */
+ preambles = MIN_LENGTH_PREAMBLE;
+ TEST_ASSERT(!test_ec_comm(&pk, preambles, CR50_COMM_SUCCESS));
+ TEST_ASSERT(!ec_has_reset()); /* EC must not be reset. */
+ TEST_ASSERT(ec_efs_get_boot_mode() == EC_EFS_BOOT_MODE_NORMAL);
+
+ /*
+ * Test 3: Attempt to set boot mode to NO BOOT.
+ * EC should not be reset with this boot mode change from NORMAL
+ * to NO_BOOT.
+ */
+ pk.ph.data[0] = EC_EFS_BOOT_MODE_NO_BOOT;
+ calculate_crc8(&pk);
+ TEST_ASSERT(!test_ec_comm(&pk, preambles, CR50_COMM_SUCCESS));
+ TEST_ASSERT(!ec_has_reset()); /* EC must not be reset. */
+ TEST_ASSERT(ec_efs_get_boot_mode() == EC_EFS_BOOT_MODE_NO_BOOT);
+
+ /*
+ * Test 4: Attempt to set boot mode to NO BOOT again.
+ * EC should not be reset since it is a repeating command.
+ */
+ TEST_ASSERT(!test_ec_comm(&pk, preambles, CR50_COMM_SUCCESS));
+ TEST_ASSERT(!ec_has_reset()); /* EC must not be reset. */
+ TEST_ASSERT(ec_efs_get_boot_mode() == EC_EFS_BOOT_MODE_NO_BOOT);
+
+ /*
+ * Test 5: Attempt to set boot mode to NORMAL.
+ * EC should be reset with this boot mode change from NO_BOOT
+ * to NORMAL.
+ */
+ pk.ph.data[0] = EC_EFS_BOOT_MODE_NORMAL;
+ calculate_crc8(&pk);
+ TEST_ASSERT(!test_ec_comm(&pk, preambles, 0));
+ TEST_ASSERT(ec_has_reset()); /* EC must be reset. */
+ TEST_ASSERT(ec_efs_get_boot_mode() == EC_EFS_BOOT_MODE_NORMAL);
+
+ return EC_SUCCESS;
+}
+
+/*
+ * Test cases for verify_hash command.
+ */
+static int test_ec_comm_verify_hash(void)
+{
+ /* Copy the sample packet to buffer. */
+ union cr50_test_packet pk = sample_packet_cmd_verify_hash;
+ int preambles = MIN_LENGTH_PREAMBLE;
+
+ ec_has_reset();
+
+ TEST_ASSERT(ec_efs_get_boot_mode() == EC_EFS_BOOT_MODE_NORMAL);
+
+ /* Test 1: Attempt to verify EC Hash. */
+ calculate_crc8(&pk);
+ preambles = MIN_LENGTH_PREAMBLE * 2;
+ TEST_ASSERT(!test_ec_comm(&pk, preambles, CR50_COMM_SUCCESS));
+ TEST_ASSERT(!ec_has_reset());
+ TEST_ASSERT(ec_efs_get_boot_mode() == EC_EFS_BOOT_MODE_NORMAL);
+
+ /* Test 2: Attempt to verify EC Hash again. */
+ preambles = MIN_LENGTH_PREAMBLE;
+ TEST_ASSERT(!test_ec_comm(&pk, preambles, CR50_COMM_SUCCESS));
+ TEST_ASSERT(!ec_has_reset());
+ TEST_ASSERT(ec_efs_get_boot_mode() == EC_EFS_BOOT_MODE_NORMAL);
+
+ /* Test 3: Attempt to verify a wrong EC Hash. */
+ pk.ph.data[0] ^= 0xff; /* corrupt the payload */
+ calculate_crc8(&pk);
+ TEST_ASSERT(!test_ec_comm(&pk, preambles, CR50_COMM_ERROR_BAD_PAYLOAD));
+ TEST_ASSERT(!ec_has_reset()); /* EC should not be reset though. */
+ TEST_ASSERT(ec_efs_get_boot_mode() == EC_EFS_BOOT_MODE_NO_BOOT);
+
+ /* Test 4: Attempt to verify a wrong EC Hash again. */
+ TEST_ASSERT(!test_ec_comm(&pk, preambles, CR50_COMM_ERROR_BAD_PAYLOAD));
+ TEST_ASSERT(!ec_has_reset()); /* EC should not be reset though. */
+ TEST_ASSERT(ec_efs_get_boot_mode() == EC_EFS_BOOT_MODE_NO_BOOT);
+
+ /*
+ * Test 5: Attempt to verify the correct EC Hash.
+ * EC should be reset because EC Boot mode is NO BOOT.
+ */
+ pk = sample_packet_cmd_verify_hash;
+ calculate_crc8(&pk);
+ preambles = MIN_LENGTH_PREAMBLE * 2;
+ TEST_ASSERT(!test_ec_comm(&pk, preambles, 0));
+ TEST_ASSERT(ec_has_reset()); /* EC must be reset. */
+ TEST_ASSERT(ec_efs_get_boot_mode() == EC_EFS_BOOT_MODE_NORMAL);
+
+ /* Check if ec has ever been reset during these tests */
+ return EC_SUCCESS;
+}
+
+void run_test(void)
+{
+ uint8_t size_to_crc;
+
+ /* Prepare the sample kernel secdata and a sample packet. */
+ memcpy(test_secdata.ec_hash, sample_ec_hash, sizeof(sample_ec_hash));
+ memcpy(sample_packet_cmd_verify_hash.ph.data,
+ sample_ec_hash, sizeof(sample_ec_hash));
+
+ /* Calculate the CRC8 for the sample kernel secdata. */
+ size_to_crc = test_secdata.struct_size -
+ offsetof(struct vb2_secdata_kernel, crc8) -
+ sizeof(test_secdata.crc8);
+ test_secdata.crc8 = crc8((uint8_t *)&test_secdata.reserved0,
+ size_to_crc);
+
+ /* Module init */
+ board_reboot_ec_deferred(0);
+ ec_efs_refresh();
+
+ /* Start test */
+ test_reset();
+
+ RUN_TEST(test_ec_comm_packet_failure);
+ RUN_TEST(test_ec_comm_set_boot_mode);
+ RUN_TEST(test_ec_comm_verify_hash);
+
+ test_print_result();
+}
diff --git a/test/ec_comm.tasklist b/test/ec_comm.tasklist
new file mode 100644
index 0000000000..52c0d390ef
--- /dev/null
+++ b/test/ec_comm.tasklist
@@ -0,0 +1,9 @@
+/* Copyright 2020 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.
+ */
+
+/**
+ * See CONFIG_TASK_LIST in config.h for details.
+ */
+#define CONFIG_TEST_TASK_LIST /* No test task */
diff --git a/test/test_config.h b/test/test_config.h
index 5c9b1ed3f7..e5d19f0bf8 100644
--- a/test/test_config.h
+++ b/test/test_config.h
@@ -410,6 +410,11 @@ int ncp15wb_calculate_temp(uint16_t adc);
#define CONFIG_USB_PD_PORT_MAX_COUNT 2
#endif
+#ifdef TEST_EC_COMM
+#define CONFIG_CRC8
+#define CONFIG_EC_EFS_SUPPORT
+#endif
+
#if defined(TEST_NVMEM) || defined(TEST_NVMEM_VARS)
#define CONFIG_CRC8
#define CONFIG_FLASH_ERASED_VALUE32 (-1U)