/* 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, }; #if CONFIG_EC_EFS2_VERSION == 0 #define EXPECTED_BOOT_MODE_AFTER_EC_RST EC_EFS_BOOT_MODE_NORMAL #define EXPECTED_BOOT_MODE_AFTER_VERIFY EC_EFS_BOOT_MODE_NORMAL #elif CONFIG_EC_EFS2_VERSION == 1 #define EXPECTED_BOOT_MODE_AFTER_EC_RST EC_EFS_BOOT_MODE_TRUSTED_RO #define EXPECTED_BOOT_MODE_AFTER_VERIFY EC_EFS_BOOT_MODE_VERIFIED #endif /* 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() == EXPECTED_BOOT_MODE_AFTER_EC_RST); /* 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() == EXPECTED_BOOT_MODE_AFTER_EC_RST); 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; uint8_t boot_mode_expected; ec_has_reset(); boot_mode_expected = EXPECTED_BOOT_MODE_AFTER_EC_RST; TEST_ASSERT(ec_efs_get_boot_mode() == boot_mode_expected); /* * Test 1-1: Attempt to set boot mode to EXPECTED_BOOT_MODE_AFTER_EC_RST * NORMAL -> NORMAL (in 2.0) or RO -> RO (in 2.1) */ boot_mode_expected = EXPECTED_BOOT_MODE_AFTER_EC_RST; pk.ph.data[0] = boot_mode_expected; 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() == boot_mode_expected); /* * Test 1-1: Attempt to set boot mode to EXPECTED_BOOT_MODE_AFTER_EC_RST * NORMAL -> NORMAL (in 2.0) or RO -> RO (in 2.1) */ boot_mode_expected = EXPECTED_BOOT_MODE_AFTER_EC_RST; 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() == boot_mode_expected); #if CONFIG_EC_EFS2_VERSION == 1 /* * Test 1-3: Attempt to set boot mode to BOOT_MODE_VERIFIED. * It should fail. * RO -> VERIFIED (x) RO (o) */ boot_mode_expected = EC_EFS_BOOT_MODE_TRUSTED_RO; pk.ph.data[0] = EC_EFS_BOOT_MODE_VERIFIED; calculate_crc8(&pk); TEST_ASSERT(!test_ec_comm(&pk, preambles, CR50_COMM_ERROR_BAD_PARAM)); TEST_ASSERT(!ec_has_reset()); /* EC must not be reset. */ TEST_ASSERT(ec_efs_get_boot_mode() == boot_mode_expected); #endif /* * Test 2-1: Attempt to set boot mode to NO BOOT. * EC should not be reset with this boot mode change from * BOOT_MODE_TRUSTED_RO to BOOT_MODE_NO_BOOT. * INITIAL(RO or NORMAL) -> NO_BOOT */ boot_mode_expected = EC_EFS_BOOT_MODE_NO_BOOT; pk.ph.data[0] = boot_mode_expected; 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() == boot_mode_expected); /* * Test 2-2: Attempt to set boot mode to NO BOOT again. * EC should not be reset since it is a repeating command. * NO_BOOT -> NO_BOOT */ boot_mode_expected = EC_EFS_BOOT_MODE_NO_BOOT; 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() == boot_mode_expected); #if CONFIG_EC_EFS2_VERSION == 1 /* * Test 2-3: Attempt to set boot mode to VERIFIED. It should fail. * NO_BOOT -> VERIFIED (x) NO_BOOT(o) */ boot_mode_expected = EC_EFS_BOOT_MODE_NO_BOOT; pk.ph.data[0] = EC_EFS_BOOT_MODE_VERIFIED; calculate_crc8(&pk); TEST_ASSERT(!test_ec_comm(&pk, preambles, CR50_COMM_ERROR_BAD_PARAM)); TEST_ASSERT(!ec_has_reset()); /* EC must not be reset. */ TEST_ASSERT(ec_efs_get_boot_mode() == boot_mode_expected); #endif /* * Test 2-4: Attempt to set boot mode to BOOT_MODE_TRUSTED_RO. * EC should be reset with this boot mode change from * BOOT_MODE_NO_BOOT to BOOT_MODE_TRUSTED_RO. * NO_BOOT -> NORMAL (in 2.0) or RO (in 2.1) */ boot_mode_expected = EXPECTED_BOOT_MODE_AFTER_EC_RST; pk.ph.data[0] = EXPECTED_BOOT_MODE_AFTER_EC_RST; 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() == boot_mode_expected); 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; uint8_t boot_mode_expected; ec_has_reset(); boot_mode_expected = EXPECTED_BOOT_MODE_AFTER_EC_RST; TEST_ASSERT(ec_efs_get_boot_mode() == boot_mode_expected); /* * Test 1: Attempt to verify EC Hash. * NORMAL -> NORMAL (in 2.0) * RO -> VERIFIED (in 2.1) */ boot_mode_expected = EXPECTED_BOOT_MODE_AFTER_VERIFY; 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() == boot_mode_expected); /* * Test 2: Attempt to verify EC Hash again. * NORMAL -> NORMAL (in 2.0) * VERIFIED -> VERIFIED (in 2.1) */ boot_mode_expected = EXPECTED_BOOT_MODE_AFTER_VERIFY; 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() == boot_mode_expected); /* * Test 3: Attempt to verify a wrong EC Hash. * NORMAL -> NO_BOOT (in 2.0) * VERIFIED -> NO_BOOT (in 2.1) */ boot_mode_expected = EC_EFS_BOOT_MODE_NO_BOOT; 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() == boot_mode_expected); /* * Test 4: Attempt to verify a wrong EC Hash again. * NO_BOOT -> NO_BOOT */ boot_mode_expected = EC_EFS_BOOT_MODE_NO_BOOT; 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() == boot_mode_expected); /* * Test 5: Attempt to verify the correct EC Hash. * EC should be reset because EC Boot mode is NO BOOT. * NO_BOOT -> INITIAL */ boot_mode_expected = EXPECTED_BOOT_MODE_AFTER_EC_RST; 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() == boot_mode_expected); /* Check if ec has ever been reset during these tests */ return EC_SUCCESS; } /* * Test cases for verify_hash command failure case. */ static int test_ec_comm_verify_hash_fail(void) { /* Copy the sample packet to buffer. */ union cr50_test_packet pk = sample_packet_cmd_verify_hash; int preambles = MIN_LENGTH_PREAMBLE; uint8_t boot_mode_expected; ec_has_reset(); boot_mode_expected = EXPECTED_BOOT_MODE_AFTER_EC_RST; TEST_ASSERT(ec_efs_get_boot_mode() == boot_mode_expected); /* * Test 1: Attempt to verify a wrong EC Hash. * RO -> NO_BOOT */ boot_mode_expected = EC_EFS_BOOT_MODE_NO_BOOT; 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() == boot_mode_expected); /* Check if ec has ever been reset during these tests */ return EC_SUCCESS; } #if CONFIG_EC_EFS2_VERSION == 1 /* * Test cases for EC-EFS 2.1 protocols */ static int test_ec_comm_verify_efs2_1(void) { /* Copy the sample packet to buffer. */ union cr50_test_packet pk_verify = sample_packet_cmd_verify_hash; union cr50_test_packet pk_set_bootmode = sample_packet_cmd_set_mode; int preambles = MIN_LENGTH_PREAMBLE; uint8_t boot_mode_expected; ec_has_reset(); boot_mode_expected = EC_EFS_BOOT_MODE_TRUSTED_RO; TEST_ASSERT(ec_efs_get_boot_mode() == boot_mode_expected); /* * Test 1-1: Attempt to verify EC Hash while RO. * RO -> VERIFIED */ boot_mode_expected = EC_EFS_BOOT_MODE_VERIFIED; calculate_crc8(&pk_verify); TEST_ASSERT(!test_ec_comm(&pk_verify, preambles, CR50_COMM_SUCCESS)); TEST_ASSERT(!ec_has_reset()); TEST_ASSERT(ec_efs_get_boot_mode() == EC_EFS_BOOT_MODE_VERIFIED); /* * Test 1-2: Attempt to change BOOT_MODE to RO. Should fail. * VERIFIED -> RO, EC Reset */ boot_mode_expected = EC_EFS_BOOT_MODE_TRUSTED_RO; pk_set_bootmode.ph.data[0] = boot_mode_expected; calculate_crc8(&pk_set_bootmode); TEST_ASSERT(!test_ec_comm(&pk_set_bootmode, preambles, 0)); TEST_ASSERT(ec_has_reset()); /* EC must not be reset. */ TEST_ASSERT(ec_efs_get_boot_mode() == boot_mode_expected); /* * Test 1-3: Attempt to change BOOT_MODE to VERIFIED. * Should fail, since VERIFIED is not allowed * as a parameter of SET_BOOT_MODE command. * RO -> VERIFIED (x) RO(o) */ boot_mode_expected = EC_EFS_BOOT_MODE_TRUSTED_RO; pk_set_bootmode.ph.data[0] = EC_EFS_BOOT_MODE_VERIFIED; calculate_crc8(&pk_set_bootmode); TEST_ASSERT(!test_ec_comm(&pk_set_bootmode, preambles, CR50_COMM_ERROR_BAD_PARAM)); TEST_ASSERT(!ec_has_reset()); /* EC must not be reset. */ TEST_ASSERT(ec_efs_get_boot_mode() == boot_mode_expected); /* * Test 1-4: Attempt to change BOOT_MODE to NO_BOOT. * Should succeed * VERIFIED -> NO_BOOT */ boot_mode_expected = EC_EFS_BOOT_MODE_NO_BOOT; pk_set_bootmode.ph.data[0] = boot_mode_expected; calculate_crc8(&pk_set_bootmode); TEST_ASSERT(!test_ec_comm(&pk_set_bootmode, preambles, CR50_COMM_SUCCESS)); TEST_ASSERT(!ec_has_reset()); /* EC must not be reset. */ TEST_ASSERT(ec_efs_get_boot_mode() == boot_mode_expected); /* * Test 1-5: Attempt to verify EC Hash while NO_BOOT * NO_BOOT -> RO */ boot_mode_expected = EC_EFS_BOOT_MODE_TRUSTED_RO; TEST_ASSERT(!test_ec_comm(&pk_verify, preambles, 0)); TEST_ASSERT(ec_has_reset()); TEST_ASSERT(ec_efs_get_boot_mode() == boot_mode_expected); /* Check if ec has ever been reset during these tests */ return EC_SUCCESS; } #endif /* CONFIG_EC_EFS2_VERSION == 1 */ 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); /* Start test */ test_reset(); board_reboot_ec_deferred(0); ec_efs_refresh(); RUN_TEST(test_ec_comm_packet_failure); board_reboot_ec_deferred(0); ec_efs_refresh(); RUN_TEST(test_ec_comm_set_boot_mode); board_reboot_ec_deferred(0); ec_efs_refresh(); RUN_TEST(test_ec_comm_verify_hash); board_reboot_ec_deferred(0); ec_efs_refresh(); RUN_TEST(test_ec_comm_verify_hash_fail); #if CONFIG_EC_EFS2_VERSION == 1 board_reboot_ec_deferred(0); ec_efs_refresh(); RUN_TEST(test_ec_comm_verify_efs2_1); #endif test_print_result(); }