summaryrefslogtreecommitdiff
path: root/test/ec_comm.c
diff options
context:
space:
mode:
Diffstat (limited to 'test/ec_comm.c')
-rw-r--r--test/ec_comm.c374
1 files changed, 374 insertions, 0 deletions
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();
+}