summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--board/cr50/board.c13
-rw-r--r--board/cr50/tpm2/NVMem.c51
-rw-r--r--common/nvmem.c79
-rw-r--r--include/nvmem.h37
-rw-r--r--test/nvmem.c123
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();
}