summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott <scollyer@chromium.org>2016-05-20 11:28:19 -0700
committerchrome-bot <chrome-bot@chromium.org>2016-05-26 18:08:57 -0700
commit7184144012cdcfdede45efc3ad3b121ec16ddacf (patch)
treeae46cd084ce5270b68588c872b9fee379d267572
parentdf35737d63811cd7ad9e4ce62406448fd709b431 (diff)
downloadchrome-ec-7184144012cdcfdede45efc3ad3b121ec16ddacf.tar.gz
Cr50: NvMem: Connected function stubs in /board/tpm2/NVMem.c
Used #define CONFIG_FLASH_NVMEM to have functions in /board/tpm2/NVMem.c utlitize on chip Nvmem functions. On chip NV Memory availability is tied to an internal nvmem error state which itself only depends on finding at least one valid partition. Added nvmem_is_different and nvmem_move functions which were needed to complete the tpm2 platform interface. In addition, added unit tests to support these two new functions. BUG=chrome-os-partner:44745 BRANCH=none TEST=manual make runtests TEST_LIST_HOST=nvmem and verify that all tests pass. Tested with tcg_test utility to test reads/writes using the command "build/test-tpm2/install/bin/compliance --ntpm localhost:9883 --select CPCTPM_TC2_3_33_07_01". Change-Id: I475fdd1331e28ede00f9b674c7bee1536fa9ea48 Signed-off-by: Scott <scollyer@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/346236 Commit-Ready: Scott Collyer <scollyer@chromium.org> Tested-by: Scott Collyer <scollyer@chromium.org> Reviewed-by: Bill Richardson <wfrichar@chromium.org>
-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();
}