diff options
-rw-r--r-- | board/cr50/board.c | 13 | ||||
-rw-r--r-- | board/cr50/tpm2/NVMem.c | 51 | ||||
-rw-r--r-- | common/nvmem.c | 79 | ||||
-rw-r--r-- | include/nvmem.h | 37 | ||||
-rw-r--r-- | test/nvmem.c | 123 |
5 files changed, 296 insertions, 7 deletions
diff --git a/board/cr50/board.c b/board/cr50/board.c index 63ce7b8f96..ebe2f65937 100644 --- a/board/cr50/board.c +++ b/board/cr50/board.c @@ -23,8 +23,17 @@ /* Define interrupt and gpio structs */ #include "gpio_list.h" -#define NVMEM_CR50_SIZE 0x400 -#define NVMEM_TPM_SIZE (NVMEM_PARTITION_SIZE - NVMEM_CR50_SIZE - \ +/* + * TODO: NV_MEMORY_SIZE is defined in 2 places. Here and in + * /src/third_party/tmp2/Implementation.h. This needs to be + * fixed so that it's only defined in one location to ensure that the TPM2.0 lib + * code and the NvMem code specific to Cr50 is consistent. Will + * either reference existing issue or create one to track + * this as ultimately only want this defined in 1 place. + */ +#define NV_MEMORY_SIZE 7168 +#define NVMEM_TPM_SIZE NV_MEMORY_SIZE +#define NVMEM_CR50_SIZE (NVMEM_PARTITION_SIZE - NVMEM_TPM_SIZE - \ sizeof(struct nvmem_tag)) /* NvMem user buffer lengths table */ uint32_t nvmem_user_sizes[NVMEM_NUM_USERS] = { diff --git a/board/cr50/tpm2/NVMem.c b/board/cr50/tpm2/NVMem.c index 6ec0031454..7dad3d5d3d 100644 --- a/board/cr50/tpm2/NVMem.c +++ b/board/cr50/tpm2/NVMem.c @@ -15,9 +15,12 @@ #include "PlatformData.h" #include "TpmError.h" #include "assert.h" +#include "nvmem.h" /* Local state */ +#ifndef CONFIG_FLASH_NVMEM static unsigned char s_NV[NV_MEMORY_SIZE]; +#endif static BOOL s_NvIsAvailable; static BOOL s_NV_unrecoverable; static BOOL s_NV_recoverable; @@ -52,9 +55,24 @@ int _plat__NVEnable(void *platParameter) s_NV_unrecoverable = FALSE; s_NV_recoverable = FALSE; +#ifdef CONFIG_FLASH_NVMEM + /* TODO: Need to define what is recoverable and unrecoverable + * conditions with regards to NvMem module. For now, the only + * requirement is that at Cr50 board initialization time, the + * nvmem_init() function either detects a valid partition, or + * 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; if (s_NV_unrecoverable) return -1; return s_NV_recoverable; +#else + if (s_NV_unrecoverable) + return -1; + return s_NV_recoverable; +#endif } void _plat__NVDisable(void) @@ -73,10 +91,23 @@ void _plat__NVDisable(void) */ int _plat__IsNvAvailable(void) { + +#ifdef CONFIG_FLASH_NVMEM + int rv; + /* + * sNv_IsAvailable is a state variable that can be accesed by the + * simmulator to control access to NvMemory. This variable and + * 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(); + return rv; +#else if (!s_NvIsAvailable) return 1; return 0; +#endif } /* @@ -88,7 +119,11 @@ void _plat__NvMemoryRead(unsigned int startOffset, { assert(startOffset + size <= NV_MEMORY_SIZE); /* Copy the data from the NV image */ +#ifdef CONFIG_FLASH_NVMEM + nvmem_read(startOffset, size, data, NVMEM_TPM); +#else memcpy(data, &s_NV[startOffset], size); +#endif return; } @@ -101,8 +136,12 @@ _plat__NvIsDifferent(unsigned int startOffset, unsigned int size, void *data) { +#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); +#endif } /* @@ -116,7 +155,11 @@ void _plat__NvMemoryWrite(unsigned int startOffset, { assert(startOffset + size <= NV_MEMORY_SIZE); /* Copy the data to the NV image */ +#ifdef CONFIG_FLASH_NVMEM + nvmem_write(startOffset, size, data, NVMEM_TPM); +#else memcpy(&s_NV[startOffset], data, size); +#endif } /* @@ -129,8 +172,12 @@ void _plat__NvMemoryMove(unsigned int sourceOffset, { assert(sourceOffset + size <= NV_MEMORY_SIZE); assert(destOffset + size <= NV_MEMORY_SIZE); +#ifdef CONFIG_FLASH_NVMEM + nvmem_move(sourceOffset, destOffset, size, NVMEM_TPM); +#else /* Move data in RAM */ memmove(&s_NV[destOffset], &s_NV[sourceOffset], size); +#endif return; } @@ -144,7 +191,11 @@ void _plat__NvMemoryMove(unsigned int sourceOffset, */ int _plat__NvCommit(void) { +#ifdef CONFIG_FLASH_NVMEM + return nvmem_commit(); +#else return 0; +#endif } /* diff --git a/common/nvmem.c b/common/nvmem.c index 7fbf5ebada..63ea1581f5 100644 --- a/common/nvmem.c +++ b/common/nvmem.c @@ -33,6 +33,9 @@ static int nvmem_act_partition; /* NvMem Cache Memory pointer */ static uint8_t *cache_base_ptr; +/* NvMem error state */ +static int nvmem_error_state; + static int nvmem_verify_partition_sha(int index) { uint8_t sha_comp[NVMEM_SHA_SIZE]; @@ -319,10 +322,14 @@ int nvmem_init(void) CPRINTF("%s:%d\n", __func__, __LINE__); return ret; } + /* Initialize error state, assume everything is good */ + nvmem_error_state = EC_SUCCESS; /* Default state for cache_base_ptr */ cache_base_ptr = NULL; ret = nvmem_find_partition(); if (ret != EC_SUCCESS) { + /* Change error state to non-zero */ + nvmem_error_state = EC_ERROR_UNKNOWN; CPRINTF("%s:%d\n", __func__, __LINE__); return ret; } @@ -330,7 +337,40 @@ int nvmem_init(void) return EC_SUCCESS; } -int nvmem_read(unsigned int offset, unsigned int size, +int nvmem_get_error_state(void) +{ + return nvmem_error_state; +} + +int nvmem_is_different(uint32_t offset, uint32_t size, void *data, + enum nvmem_users user) +{ + int ret; + uint8_t *p_src; + uintptr_t src_addr; + uint32_t src_offset; + + /* Point to either NvMem flash or ram if that's active */ + if (cache_base_ptr == NULL) + src_addr = CONFIG_FLASH_NVMEM_BASE + nvmem_act_partition * + NVMEM_PARTITION_SIZE; + + else + src_addr = (uintptr_t)cache_base_ptr; + + /* Get partition offset for this read operation */ + ret = nvmem_get_partition_off(user, offset, size, &src_offset); + if (ret != EC_SUCCESS) + return ret; + + /* Advance to the correct byte within the data buffer */ + src_addr += src_offset; + p_src = (uint8_t *)src_addr; + /* Compare NvMem with data */ + return memcmp(p_src, data, size); +} + +int nvmem_read(uint32_t offset, uint32_t size, void *data, enum nvmem_users user) { int ret; @@ -359,7 +399,7 @@ int nvmem_read(unsigned int offset, unsigned int size, return EC_SUCCESS; } -int nvmem_write(unsigned int offset, unsigned int size, +int nvmem_write(uint32_t offset, uint32_t size, void *data, enum nvmem_users user) { int ret; @@ -388,6 +428,41 @@ int nvmem_write(unsigned int offset, unsigned int size, return EC_SUCCESS; } +int nvmem_move(uint32_t src_offset, uint32_t dest_offset, uint32_t size, + enum nvmem_users user) +{ + int ret; + uint8_t *p_src, *p_dest; + uintptr_t base_addr; + uint32_t s_buff_offset, d_buff_offset; + + /* Make sure that the cache buffer is active */ + ret = nvmem_update_cache_ptr(); + if (ret) + /* TODO: What to do when can't access cache buffer? */ + return ret; + + /* Compute partition offset for source */ + ret = nvmem_get_partition_off(user, src_offset, size, &s_buff_offset); + if (ret != EC_SUCCESS) + return ret; + + /* Compute partition offset for destination */ + ret = nvmem_get_partition_off(user, dest_offset, size, &d_buff_offset); + if (ret != EC_SUCCESS) + return ret; + + base_addr = (uintptr_t)cache_base_ptr; + /* Create pointer to src location within partition */ + p_src = (uint8_t *)(base_addr + s_buff_offset); + /* Create pointer to dest location within partition */ + p_dest = (uint8_t *)(base_addr + d_buff_offset); + /* Move the data block in NvMem */ + memmove(p_dest, p_src, size); + + return EC_SUCCESS; +} + int nvmem_commit(void) { int nvmem_offset; diff --git a/include/nvmem.h b/include/nvmem.h index 408e0002f4..2785cfdafc 100644 --- a/include/nvmem.h +++ b/include/nvmem.h @@ -79,6 +79,25 @@ struct nvmem_tag { int nvmem_init(void); /** + * Get Nvmem internal error state + * + * @return nvmem_error_state variable. + */ +int nvmem_get_error_state(void); + +/** + * Compare 'size' amount of bytes in NvMem + * + * @param offset: Offset (in bytes) into NVmem logical space + * @param size: Number of bytes to compare + * @param data: Pointer to data to be compared with + * @param user: Data section within NvMem space + * @return 0 if the data is same, non-zero if data is different + */ +int nvmem_is_different(uint32_t offset, uint32_t size, + void *data, enum nvmem_users user); + +/** * Read 'size' amount of bytes from NvMem * * @param startOffset: Offset (in bytes) into NVmem logical space @@ -88,7 +107,7 @@ int nvmem_init(void); * @return EC_ERROR_OVERFLOW (non-zero) if the read operation would exceed the * buffer length of the given user, otherwise EC_SUCCESS. */ -int nvmem_read(unsigned int startOffset, unsigned int size, +int nvmem_read(uint32_t startOffset, uint32_t size, void *data, enum nvmem_users user); /** @@ -102,10 +121,22 @@ int nvmem_read(unsigned int startOffset, unsigned int size, * EC_ERROR_TIMEOUT if nvmem cache buffer is not available * EC_SUCCESS if no errors. */ -int nvmem_write(unsigned int startOffset, unsigned int size, +int nvmem_write(uint32_t startOffset, uint32_t size, void *data, enum nvmem_users user); - +/** + * Move 'size' amount of bytes within NvMem + * + * @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 + * @param user: Data section within NvMem space + * @return EC_ERROR_OVERFLOW if write exceeds buffer length + * EC_ERROR_TIMEOUT if nvmem cache buffer is not available + * EC_SUCCESS if no errors. + */ +int nvmem_move(uint32_t src_offset, uint32_t dest_offset, uint32_t size, + enum nvmem_users user); /** * Commit all previous NvMem writes to flash * diff --git a/test/nvmem.c b/test/nvmem.c index 096e0ed5b9..2567853341 100644 --- a/test/nvmem.c +++ b/test/nvmem.c @@ -325,6 +325,125 @@ static int test_buffer_overflow(void) return EC_SUCCESS; } +static int test_move(void) +{ + uint32_t len = 0x100; + uint32_t nv1_offset; + uint32_t nv2_offset; + int user = 0; + int n; + int ret; + + /* + * The purpose of this test is to check that nvmem_move() behaves + * properly. This test only uses one user buffer as accessing multiple + * user buffers is tested separately. This test uses writes a set of + * test data then test move operations with full overlap, half overlap + * and no overlap. Folliwng these tests, the boundary conditions for + * move operations are checked for the giver user buffer. + */ + + nv1_offset = 0; + for (n = 0; n < 3; n++) { + /* Generate Test data */ + generate_random_data(nv1_offset, len); + nv2_offset = nv1_offset + (len / 2) * n; + /* Write data to Nvmem cache memory */ + nvmem_write(nv1_offset, len, &write_buffer[nv1_offset], user); + nvmem_commit(); + /* Test move while data is in cache area */ + nvmem_move(nv1_offset, nv2_offset, len, user); + nvmem_read(nv2_offset, len, read_buffer, user); + if (memcmp(write_buffer, read_buffer, len)) + return EC_ERROR_UNKNOWN; + ccprintf("Memmove nv1 = 0x%x, nv2 = 0x%x\n", + nv1_offset, nv2_offset); + } + /* Test invalid buffer offsets */ + /* Destination offset is equal to length of buffer */ + nv1_offset = 0; + nv2_offset = nvmem_user_sizes[user]; + /* Attempt to move just 1 byte */ + ret = nvmem_move(nv1_offset, nv2_offset, 1, user); + if (!ret) + return EC_ERROR_UNKNOWN; + + /* Source offset is equal to length of buffer */ + nv1_offset = nvmem_user_sizes[user]; + nv2_offset = 0; + /* Attempt to move just 1 byte */ + ret = nvmem_move(nv1_offset, nv2_offset, 1, user); + if (!ret) + return EC_ERROR_UNKNOWN; + + nv1_offset = 0; + nv2_offset = nvmem_user_sizes[user] - len; + /* Move data chunk from start to end of buffer */ + ret = nvmem_move(nv1_offset, nv2_offset, + len, user); + if (ret) + return ret; + + /* Attempt to move data chunk 1 byte beyond end of user buffer */ + nv1_offset = 0; + nv2_offset = nvmem_user_sizes[user] - len + 1; + ret = nvmem_move(nv1_offset, nv2_offset, + len, user); + if (!ret) + return EC_ERROR_UNKNOWN; + + return EC_SUCCESS; +} + +static int test_is_different(void) +{ + uint32_t len = 0x41; + uint32_t nv1_offset = 0; + int user = 1; + int ret; + + /* + * The purpose of this test is to verify nv_is_different(). Test data is + * written to a location in user buffer 1, then a case that's expected + * to pass along with a case that is expected to fail are checked. Next + * the same tests are repeated when the NvMem write is followed by a + * commit operation. + */ + + /* Generate test data */ + generate_random_data(nv1_offset, len); + /* Write to NvMem cache buffer */ + nvmem_write(nv1_offset, len, &write_buffer[nv1_offset], user); + /* Expected to be the same */ + ret = nvmem_is_different(nv1_offset, len, + &write_buffer[nv1_offset], user); + if (ret) + return EC_ERROR_UNKNOWN; + + /* Expected to be different */ + ret = nvmem_is_different(nv1_offset + 1, len, + &write_buffer[nv1_offset], user); + if (!ret) + return EC_ERROR_UNKNOWN; + + /* Commit cache buffer and retest */ + nvmem_commit(); + /* Expected to be the same */ + ret = nvmem_is_different(nv1_offset, len, + &write_buffer[nv1_offset], user); + if (ret) + return EC_ERROR_UNKNOWN; + + /* Expected to be different */ + write_buffer[nv1_offset] ^= 0xff; + ret = nvmem_is_different(nv1_offset, len, + &write_buffer[nv1_offset], user); + if (!ret) + return EC_ERROR_UNKNOWN; + + return EC_SUCCESS; +} + static void run_test_setup(void) { /* Allow Flash erase/writes */ @@ -348,5 +467,9 @@ void run_test(void) 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_print_result(); } |