summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2015-02-04 10:44:02 -0800
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2015-02-05 21:03:01 +0000
commitf8af89c40d974e79b3e69698f6d8aa98e0e40fc9 (patch)
tree9d469c96dd9240af3bff374aa93dc92a041c45bc
parentc0be4409522bc53447940ac88aff455a6844427c (diff)
downloadchrome-ec-f8af89c40d974e79b3e69698f6d8aa98e0e40fc9.tar.gz
Support vboot hash and system version if flash isn't memory-mapped
Some EC chips (mec1322) use external SPI flash which is not mapped into the EC CPU's address space. These must explicitly read data from flash when calculating the vboot hash or reading the version string of the image which isn't currently loaded into code RAM. To test this bug, I used a board with known working mapped flash, and temporarily patched it to act like it didn't have mapped flash. Also add a flashread console command, useful for manually testing. BUG=chrome-os-partner:35308 BRANCH=glower,strago TEST=manual 1. Apply this patch to samus 2. Check result for 'vboot hash RW' 3. Check result for 'version' 4a. In board/samus/board.h, #undef CONFIG_FLASH_MAPPED and #define CONFIG_CMD_FLASH 4b. In chip/lm4/flash.c, add the following: int flash_physical_read(int offset, int size, char *data) { const char *src; if (offset > CONFIG_FLASH_SIZE || offset + size > CONFIG_FLASH_SIZE) return EC_ERROR_INVAL; src = (const char *)((uintptr_t)CONFIG_FLASH_BASE + offset); memcpy(data, src, size); return EC_SUCCESS; } Steps 4a,4b will make the LM4 chip act like it doesn't have memory-mapped flash. 5. From the dev system, util/flash_ec --board=samus --ro 6. Check result for 'vboot hash RW'. Should be same as 2. 7. Check result for 'version' for RW version. Should be same as in 3. 8. From the dev system, util/flash_ec --board=samus 9. sysjump rw 10. Check result for 'version' for RO version. Should be same as in 3. 11. Compare 'flashread 0x100 0x100' with 'md 0x100 0x40'. The results should be the same (but endian-swapped, since flashread is byte ordered and md is 32-bit ordered). 12. Revert changes from steps 4a-4b. Change-Id: I951d6f5603a84e326740936e4e84dfe6296a0f59 Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/246200 Reviewed-by: Shawn N <shawnn@chromium.org>
-rw-r--r--common/flash.c135
-rw-r--r--common/system.c30
-rw-r--r--common/vboot_hash.c39
-rw-r--r--include/config.h7
-rw-r--r--include/flash.h22
5 files changed, 209 insertions, 24 deletions
diff --git a/common/flash.c b/common/flash.c
index 3b269deb46..5f4f5fc2d6 100644
--- a/common/flash.c
+++ b/common/flash.c
@@ -36,6 +36,17 @@ struct persist_state {
/* Protect persist state and RO firmware at boot */
#define PERSIST_FLAG_PROTECT_RO 0x02
+int flash_range_ok(int offset, int size_req, int align)
+{
+ if (offset < 0 || size_req < 0 ||
+ offset + size_req > CONFIG_FLASH_SIZE ||
+ (offset | size_req) & (align - 1))
+ return 0; /* Invalid range */
+
+ return 1;
+}
+
+#ifdef CONFIG_FLASH_MAPPED
/**
* Get the physical memory address of a flash offset
*
@@ -52,6 +63,17 @@ static const char *flash_physical_dataptr(int offset)
return (char *)((uintptr_t)CONFIG_FLASH_BASE + offset);
}
+int flash_dataptr(int offset, int size_req, int align, const char **ptrp)
+{
+ if (!flash_range_ok(offset, size_req, align))
+ return -1; /* Invalid range */
+ if (ptrp)
+ *ptrp = flash_physical_dataptr(offset);
+
+ return CONFIG_FLASH_SIZE - offset;
+}
+#endif
+
/**
* Read persistent state into pstate.
*
@@ -59,7 +81,7 @@ static const char *flash_physical_dataptr(int offset)
*/
static void flash_read_pstate(struct persist_state *pstate)
{
- memcpy(pstate, flash_physical_dataptr(PSTATE_OFFSET), sizeof(*pstate));
+ flash_read(PSTATE_OFFSET, sizeof(*pstate), (char *)pstate);
/* Sanity-check data and initialize if necessary */
if (pstate->version != PERSIST_STATE_VERSION) {
@@ -103,22 +125,12 @@ static int flash_write_pstate(const struct persist_state *pstate)
(const char *)pstate);
}
-int flash_dataptr(int offset, int size_req, int align, const char **ptrp)
-{
- if (offset < 0 || size_req < 0 ||
- offset + size_req > CONFIG_FLASH_SIZE ||
- (offset | size_req) & (align - 1))
- return -1; /* Invalid range */
- if (ptrp)
- *ptrp = flash_physical_dataptr(offset);
-
- return CONFIG_FLASH_SIZE - offset;
-}
-
int flash_is_erased(uint32_t offset, int size)
{
const uint32_t *ptr;
+#ifdef CONFIG_FLASH_MAPPED
+ /* Use pointer directly to flash */
if (flash_dataptr(offset, size, sizeof(uint32_t),
(const char **)&ptr) < 0)
return 0;
@@ -126,13 +138,49 @@ int flash_is_erased(uint32_t offset, int size)
for (size /= sizeof(uint32_t); size > 0; size--, ptr++)
if (*ptr != CONFIG_FLASH_ERASED_VALUE32)
return 0;
+#else
+ /* Read flash a chunk at a time */
+ uint32_t buf[8];
+ int bsize;
+
+ while (size) {
+ bsize = MIN(size, sizeof(buf));
+
+ if (flash_read(offset, bsize, (char *)buf))
+ return 0;
+
+ size -= bsize;
+ offset += bsize;
+
+ ptr = buf;
+ for (bsize /= sizeof(uint32_t); bsize > 0; bsize--, ptr++)
+ if (*ptr != CONFIG_FLASH_ERASED_VALUE32)
+ return 0;
+
+ }
+#endif
return 1;
}
+int flash_read(int offset, int size, char *data)
+{
+#ifdef CONFIG_FLASH_MAPPED
+ const char *src;
+
+ if (flash_dataptr(offset, size, 1, &src) < 0)
+ return EC_ERROR_INVAL;
+
+ memcpy(data, src, size);
+ return EC_SUCCESS;
+#else
+ return flash_physical_read(offset, size, data);
+#endif
+}
+
int flash_write(int offset, int size, const char *data)
{
- if (flash_dataptr(offset, size, CONFIG_FLASH_WRITE_SIZE, NULL) < 0)
+ if (!flash_range_ok(offset, size, CONFIG_FLASH_WRITE_SIZE))
return EC_ERROR_INVAL; /* Invalid range */
#ifdef CONFIG_VBOOT_HASH
@@ -144,7 +192,7 @@ int flash_write(int offset, int size, const char *data)
int flash_erase(int offset, int size)
{
- if (flash_dataptr(offset, size, CONFIG_FLASH_ERASE_SIZE, NULL) < 0)
+ if (!flash_range_ok(offset, size, CONFIG_FLASH_ERASE_SIZE))
return EC_ERROR_INVAL; /* Invalid range */
#ifdef CONFIG_VBOOT_HASH
@@ -456,6 +504,55 @@ DECLARE_CONSOLE_COMMAND(flashwrite, command_flash_write,
"offset [size]",
"Write pattern to flash",
NULL);
+
+static int command_flash_read(int argc, char **argv)
+{
+ int offset = -1;
+ int size = 256;
+ int rv;
+ char *data;
+ int i;
+
+ rv = parse_offset_size(argc, argv, 1, &offset, &size);
+ if (rv)
+ return rv;
+
+ if (size > shared_mem_size())
+ size = shared_mem_size();
+
+ /* Acquire the shared memory buffer */
+ rv = shared_mem_acquire(size, &data);
+ if (rv) {
+ ccputs("Can't get shared mem\n");
+ return rv;
+ }
+
+ /* Read the data */
+ if (flash_read(offset, size, data)) {
+ shared_mem_release(data);
+ return EC_ERROR_INVAL;
+ }
+
+ /* Dump it */
+ for (i = 0; i < size; i++) {
+ if ((offset + i) % 16) {
+ ccprintf(" %02x", data[i]);
+ } else {
+ ccprintf("\n%08x: %02x", offset + i, data[i]);
+ cflush();
+ }
+ }
+ ccprintf("\n");
+
+ /* Free the buffer */
+ shared_mem_release(data);
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(flashread, command_flash_read,
+ "offset [size]",
+ "Read flash",
+ NULL);
#endif
static int command_flash_wp(int argc, char **argv)
@@ -534,15 +631,13 @@ DECLARE_HOST_COMMAND(EC_CMD_FLASH_INFO,
static int flash_command_read(struct host_cmd_handler_args *args)
{
const struct ec_params_flash_read *p = args->params;
- const char *src;
-
- if (flash_dataptr(p->offset, p->size, 1, &src) < 0)
- return EC_RES_ERROR;
if (p->size > args->response_max)
return EC_RES_OVERFLOW;
- memcpy(args->response, src, p->size);
+ if (flash_read(p->offset, p->size, args->response))
+ return EC_RES_ERROR;
+
args->response_size = p->size;
return EC_RES_SUCCESS;
diff --git a/common/system.c b/common/system.c
index cdb940c77b..39ccc29165 100644
--- a/common/system.c
+++ b/common/system.c
@@ -499,6 +499,10 @@ int system_run_image_copy(enum system_image_copy_t copy)
const char *system_get_version(enum system_image_copy_t copy)
{
+#ifndef CONFIG_FLASH_MAPPED
+ static struct version_struct vdata;
+#endif
+
uintptr_t addr;
const struct version_struct *v;
@@ -510,17 +514,35 @@ const char *system_get_version(enum system_image_copy_t copy)
if (addr == 0xffffffff)
return "";
- /* The version string is always located after the reset vectors, so
- * it's the same as in the current image. */
-#ifdef CONFIG_CODERAM_ARCH /* TODO: (ML) we run FW in Code RAM */
+ /*
+ * The version string is always located after the reset vectors, so
+ * it's the same offset as in the current image. Find that offset.
+ */
+#ifdef CONFIG_CODERAM_ARCH
+ /*
+ * Code has been copied from flash to code RAM, so offset of the
+ * current image's version struct is from the start of code RAM, not
+ * the start of the flash image.
+ */
addr += ((uintptr_t)&version_data - CONFIG_CDRAM_BASE);
#else
+ /* Offset is from the start of the flash image */
addr += ((uintptr_t)&version_data - get_base(system_get_image_copy()));
#endif
+#ifdef CONFIG_FLASH_MAPPED
+ /* Directly access the version data */
+ v = (const struct version_struct *)addr;
+#else
+ /* Read the version struct into a buffer */
+ if (flash_read(addr - CONFIG_FLASH_BASE, sizeof(vdata), (char *)&vdata))
+ return "";
+
+ v = &vdata;
+#endif
+
/* Make sure the version struct cookies match before returning the
* version string. */
- v = (const struct version_struct *)addr;
if (v->cookie1 == RO(version_data).cookie1 &&
v->cookie2 == RO(version_data).cookie2)
return v->version;
diff --git a/common/vboot_hash.c b/common/vboot_hash.c
index e7717e96db..f2fa839c67 100644
--- a/common/vboot_hash.c
+++ b/common/vboot_hash.c
@@ -7,9 +7,11 @@
#include "common.h"
#include "console.h"
+#include "flash.h"
#include "hooks.h"
#include "host_command.h"
#include "sha256.h"
+#include "shared_mem.h"
#include "system.h"
#include "task.h"
#include "timer.h"
@@ -56,6 +58,37 @@ static void vboot_hash_abort(void)
}
}
+#ifndef CONFIG_FLASH_MAPPED
+
+static void vboot_hash_next_chunk(void);
+
+static int read_and_hash_chunk(int offset, int size)
+{
+ char *buf;
+ int rv;
+
+ rv = shared_mem_acquire(size, &buf);
+ if (rv == EC_ERROR_BUSY) {
+ /* Couldn't update hash right now; try again later */
+ hook_call_deferred(vboot_hash_next_chunk, WORK_INTERVAL_US);
+ return rv;
+ } else if (rv != EC_SUCCESS) {
+ vboot_hash_abort();
+ return rv;
+ }
+
+ rv = flash_read(offset, size, buf);
+ if (rv == EC_SUCCESS)
+ SHA256_update(&ctx, (const uint8_t *)buf, size);
+ else
+ vboot_hash_abort();
+
+ shared_mem_release(buf);
+ return rv;
+}
+
+#endif
+
/**
* Do next chunk of hashing work, if any.
*/
@@ -72,8 +105,14 @@ static void vboot_hash_next_chunk(void)
/* Compute the next chunk of hash */
size = MIN(CHUNK_SIZE, data_size - curr_pos);
+
+#ifdef CONFIG_FLASH_MAPPED
SHA256_update(&ctx, (const uint8_t *)(CONFIG_FLASH_BASE +
data_offset + curr_pos), size);
+#else
+ if (read_and_hash_chunk(data_offset + curr_pos, size) != EC_SUCCESS)
+ return;
+#endif
curr_pos += size;
if (curr_pos >= data_size) {
diff --git a/include/config.h b/include/config.h
index e3983eb064..a6d7d6cfb6 100644
--- a/include/config.h
+++ b/include/config.h
@@ -550,6 +550,13 @@
#undef CONFIG_FLASH_BASE
#undef CONFIG_FLASH_ERASED_VALUE32
#undef CONFIG_FLASH_ERASE_SIZE
+
+/*
+ * Flash is directly mapped into the EC's address space. If this is not
+ * defined, the flash driver must implement flash_physical_read().
+ */
+#define CONFIG_FLASH_MAPPED
+
#undef CONFIG_FLASH_PHYSICAL_SIZE
#undef CONFIG_FLASH_PROTECT_NEXT_BOOT
#undef CONFIG_FLASH_SIZE
diff --git a/include/flash.h b/include/flash.h
index a77d5a1cb2..3a4a120938 100644
--- a/include/flash.h
+++ b/include/flash.h
@@ -39,6 +39,15 @@ enum flash_wp_range {
/* Low-level methods, for use by flash_common. */
/**
+ * Read from physical flash.
+ *
+ * @param offset Flash offset to write.
+ * @param size Number of bytes to write.
+ * @param data Destination buffer for data. Must be 32-bit aligned.
+ */
+int flash_physical_read(int offset, int size, char *data);
+
+/**
* Write to physical flash.
*
* Offset and size must be a multiple of CONFIG_FLASH_WRITE_SIZE.
@@ -190,6 +199,19 @@ int flash_get_size(void);
int flash_dataptr(int offset, int size_req, int align, const char **ptrp);
/**
+ * Read from flash.
+ *
+ * If flash is mapped (CONFIG_FLASH_MAPPED), it is usually more efficient to
+ * use flash_dataptr() to get a pointer directly to the flash memory rather
+ * than use flash_read(), since the former saves a memcpy() operation.
+ *
+ * @param offset Flash offset to write.
+ * @param size Number of bytes to write.
+ * @param data Destination buffer for data. Must be 32-bit aligned.
+ */
+int flash_read(int offset, int size, char *data);
+
+/**
* Write to flash.
*
* Offset and size must be a multiple of CONFIG_FLASH_WRITE_SIZE.