summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRong Chang <rongchang@google.com>2011-12-09 14:34:54 +0800
committerRong Chang <rongchang@google.com>2011-12-12 18:21:08 +0800
commit1eb5417847831c9dda4c8609af67ad21f9bfba42 (patch)
tree1f94c726cb5d72397a66c765981d46ad8dd0aa6d
parent82f89a25373e0e614d3d1e94c91b1455242467ab (diff)
downloadchrome-ec-1eb5417847831c9dda4c8609af67ad21f9bfba42.tar.gz
Add flash write protect range command
This CL add host command to enable, get/set flash write protect range. BUG=None TEST=Use flashrom utility to set write protect range, enable write protect and get status. Change-Id: I345f1eb65944d8cf8028e6fdb7e43c40cc742a6d Signed-off-by: Rong Chang <rongchang@chromium.org>
-rw-r--r--chip/lm4/flash.c126
-rw-r--r--chip/lm4/registers.h1
-rw-r--r--common/flash_commands.c96
-rw-r--r--common/host_command.c12
-rw-r--r--include/flash_commands.h4
-rw-r--r--include/lpc_commands.h42
6 files changed, 277 insertions, 4 deletions
diff --git a/chip/lm4/flash.c b/chip/lm4/flash.c
index 115747d7d9..10a07343d2 100644
--- a/chip/lm4/flash.c
+++ b/chip/lm4/flash.c
@@ -11,6 +11,10 @@
#include "registers.h"
#include "util.h"
+#define BANK_SHIFT 5 /* bank registers have 32bits each, 2^32 */
+#define BANK_MASK ((1 << BANK_SHIFT) - 1) /* 5 bits */
+#define F_BANK(b) ((b) >> BANK_SHIFT)
+#define F_BIT(b) (1 << ((b) & BANK_MASK))
static int usable_flash_size;
@@ -151,22 +155,136 @@ int flash_erase(int offset, int size)
return EC_SUCCESS;
}
+/* Get write protect status of single flash block
+ * return value:
+ * 0 - WP
+ * non-zero - writable
+ */
+static uint32_t get_block_wp(int block)
+{
+ return LM4_FLASH_FMPPE[F_BANK(block)] & F_BIT(block);
+}
-int flash_get_write_protect_range(int *offset, int *size)
+static void set_block_wp(int block)
{
- return EC_ERROR_UNIMPLEMENTED;
+ LM4_FLASH_FMPPE[F_BANK(block)] &= ~F_BIT(block);
}
+static int find_first_wp_block(void)
+{
+ int block;
+ for (block = 0; block < LM4_FLASH_FSIZE; block++)
+ if (get_block_wp(block) == 0)
+ return block;
+ return -1;
+}
+
+static int find_last_wp_block(void)
+{
+ int block;
+ for (block = LM4_FLASH_FSIZE - 1; block >= 0; block--)
+ if (get_block_wp(block) == 0)
+ return block;
+ return -1;
+}
+
+static int get_wp_range(int *start, int *nblock)
+{
+ int start_blk, end_blk;
+
+ start_blk = find_first_wp_block();
+
+ if (start_blk < 0) {
+ /* Flash is not write protected */
+ *start = 0;
+ *nblock = 0;
+ return EC_SUCCESS;
+ }
+
+ /* TODO: Sanity check the shadow value? */
+
+ end_blk = find_last_wp_block();
+ *nblock = end_blk - start_blk + 1;
+ *start = start_blk;
+ return EC_SUCCESS;
+}
+
+
+static int set_wp_range(int start, int nblock)
+{
+ int end_blk, block;
+
+ if (nblock == 0)
+ return EC_SUCCESS;
+
+ end_blk = (start + nblock - 1);
+
+ for (block = start; block <= end_blk; block++)
+ set_block_wp(block);
+
+ return EC_SUCCESS;
+}
+
+int flash_get_write_protect_range(int *offset, int *size)
+{
+ int start, nblock;
+ int rv;
+
+ rv = get_wp_range(&start, &nblock);
+ if (rv)
+ return rv;
+
+ *size = nblock * FLASH_PROTECT_BYTES;
+ *offset = start * FLASH_PROTECT_BYTES;
+ return EC_SUCCESS;
+}
int flash_set_write_protect_range(int offset, int size)
{
- return EC_ERROR_UNIMPLEMENTED;
+ int start, nblock;
+ int rv;
+
+ if ((offset < 0) || (size < 0) || ((offset + size) >
+ (LM4_FLASH_FSIZE * FLASH_PROTECT_BYTES)))
+ return EC_ERROR_UNKNOWN; /* Invalid range */
+
+ rv = flash_get_write_protect_status();
+
+ if (rv & EC_FLASH_WP_RANGE_LOCKED) {
+ if (size == 0) {
+ /* TODO: Clear shadow if system WP is asserted */
+ /* TODO: Reboot EC */
+ return EC_SUCCESS;
+ }
+
+ return EC_ERROR_UNKNOWN; /* Range locked */
+ }
+
+ start = offset / FLASH_PROTECT_BYTES;
+ nblock = ((offset + size - 1) / FLASH_PROTECT_BYTES) - start + 1;
+ rv = set_wp_range(start, nblock);
+ if (rv)
+ return rv;
+
+ return EC_SUCCESS;
}
int flash_get_write_protect_status(void)
{
- return EC_ERROR_UNIMPLEMENTED;
+ int start, nblock;
+ int rv;
+
+ rv = get_wp_range(&start, &nblock);
+ if (rv)
+ return rv;
+
+ rv = 0;
+ if (nblock)
+ rv |= EC_FLASH_WP_RANGE_LOCKED;
+ /* TODO: get WP gpio*/
+
+ return rv;
}
diff --git a/chip/lm4/registers.h b/chip/lm4/registers.h
index 69eebe7237..3ac924c64f 100644
--- a/chip/lm4/registers.h
+++ b/chip/lm4/registers.h
@@ -135,6 +135,7 @@ static inline int lm4_lpc_addr(int ch, int offset)
#define LM4_FLASH_FMPRE1 LM4REG(0x400fe204)
#define LM4_FLASH_FMPRE2 LM4REG(0x400fe208)
#define LM4_FLASH_FMPRE3 LM4REG(0x400fe20c)
+#define LM4_FLASH_FMPPE ((volatile uint32_t*)0x400fe400)
#define LM4_FLASH_FMPPE0 LM4REG(0x400fe400)
#define LM4_FLASH_FMPPE1 LM4REG(0x400fe404)
#define LM4_FLASH_FMPPE2 LM4REG(0x400fe408)
diff --git a/common/flash_commands.c b/common/flash_commands.c
index c53f8e6d97..95f1b9639e 100644
--- a/common/flash_commands.c
+++ b/common/flash_commands.c
@@ -145,12 +145,50 @@ static int command_flash_wp(int argc, char **argv)
return EC_SUCCESS;
}
+static int command_flash_wp_range(int argc, char **argv)
+{
+ int offset, size;
+ char *endptr;
+ int rv;
+
+ if (argc < 3) {
+ uart_puts("Usage: flashwprange [offset size]\n");
+ rv = flash_get_write_protect_range(&offset, &size);
+ if (rv)
+ uart_puts("flash_get_write_protect_range failed\n");
+ else
+ uart_printf("Current range : offset(%d) size(%d)\n",
+ offset, size);
+ uart_printf("FMPPEs : %08x %08x %08x %08x\n",
+ LM4_FLASH_FMPPE0, LM4_FLASH_FMPPE1,
+ LM4_FLASH_FMPPE2, LM4_FLASH_FMPPE3);
+ } else {
+ offset = strtoi(argv[1], &endptr, 0);
+ if (*endptr) {
+ uart_printf("Invalid offset \"%s\"\n", argv[1]);
+ return EC_ERROR_UNKNOWN;
+ }
+ size = strtoi(argv[2], &endptr, 0);
+ if (*endptr) {
+ uart_printf("Invalid size \"%s\"\n", argv[2]);
+ return EC_ERROR_UNKNOWN;
+ }
+
+ rv = flash_set_write_protect_range(offset, size);
+ if (rv) {
+ uart_puts("flash_set_write_protect_range failed\n");
+ return rv;
+ }
+ }
+ return EC_SUCCESS;
+}
static const struct console_command console_commands[] = {
{"flasherase", command_flash_erase},
{"flashinfo", command_flash_info},
{"flashwrite", command_flash_write},
{"flashwp", command_flash_wp},
+ {"flashwprange", command_flash_wp_range},
};
static const struct console_group command_group = {
"Flash", console_commands, ARRAY_SIZE(console_commands)
@@ -217,6 +255,64 @@ enum lpc_status flash_command_erase(uint8_t *data)
return EC_LPC_STATUS_SUCCESS;
}
+/* TODO: use shadow range in EEPROM */
+static int shadow_wp_offset;
+static int shadow_wp_size;
+
+enum lpc_status flash_command_wp_enable(uint8_t *data)
+{
+ struct lpc_params_flash_wp_enable *p =
+ (struct lpc_params_flash_wp_enable *)data;
+ int offset, size;
+
+ if (p->enable_wp) {
+ offset = shadow_wp_offset;
+ size = shadow_wp_size;
+ } else {
+ offset = 0;
+ size = 0;
+ }
+ if (flash_set_write_protect_range(offset, size))
+ return EC_LPC_STATUS_ERROR;
+
+ return EC_LPC_STATUS_SUCCESS;
+}
+
+enum lpc_status flash_command_wp_get_state(uint8_t *data)
+{
+ struct lpc_response_flash_wp_enable *p =
+ (struct lpc_response_flash_wp_enable *)data;
+
+ if (flash_get_write_protect_status() & EC_FLASH_WP_RANGE_LOCKED)
+ p->enable_wp = 1;
+ else
+ p->enable_wp = 0;
+
+ return EC_LPC_STATUS_SUCCESS;
+}
+
+enum lpc_status flash_command_wp_set_range(uint8_t *data)
+{
+ struct lpc_params_flash_wp_range *p =
+ (struct lpc_params_flash_wp_range *)data;
+
+ if (flash_set_write_protect_range(p->offset, p->size))
+ return EC_LPC_STATUS_ERROR;
+
+ return EC_LPC_STATUS_SUCCESS;
+}
+
+enum lpc_status flash_command_wp_get_range(uint8_t *data)
+{
+ struct lpc_response_flash_wp_range *p =
+ (struct lpc_response_flash_wp_range *)data;
+
+ if (flash_get_write_protect_range(&p->offset, &p->size))
+ return EC_LPC_STATUS_ERROR;
+
+ return EC_LPC_STATUS_SUCCESS;
+}
+
/*****************************************************************************/
/* Initialization */
diff --git a/common/host_command.c b/common/host_command.c
index 528749f18c..12790e4050 100644
--- a/common/host_command.c
+++ b/common/host_command.c
@@ -155,6 +155,18 @@ static void command_process(int slot)
case EC_LPC_COMMAND_FLASH_ERASE:
lpc_send_host_response(slot, flash_command_erase(data));
return;
+ case EC_LPC_COMMAND_FLASH_WP_ENABLE:
+ lpc_send_host_response(flash_command_wp_enable(host_data));
+ return;
+ case EC_LPC_COMMAND_FLASH_WP_GET_STATE:
+ lpc_send_host_response(flash_command_wp_get_state(host_data));
+ return;
+ case EC_LPC_COMMAND_FLASH_WP_SET_RANGE:
+ lpc_send_host_response(flash_command_wp_set_range(host_data));
+ return;
+ case EC_LPC_COMMAND_FLASH_WP_GET_RANGE:
+ lpc_send_host_response(flash_command_wp_get_range(host_data));
+ return;
default:
lpc_send_host_response(slot, EC_LPC_STATUS_INVALID_COMMAND);
}
diff --git a/include/flash_commands.h b/include/flash_commands.h
index e74fef1318..230dc4014d 100644
--- a/include/flash_commands.h
+++ b/include/flash_commands.h
@@ -19,5 +19,9 @@ enum lpc_status flash_command_get_info(uint8_t *data);
enum lpc_status flash_command_read(uint8_t *data);
enum lpc_status flash_command_write(uint8_t *data);
enum lpc_status flash_command_erase(uint8_t *data);
+enum lpc_status flash_command_wp_enable(uint8_t *data);
+enum lpc_status flash_command_wp_get_state(uint8_t *data);
+enum lpc_status flash_command_wp_set_range(uint8_t *data);
+enum lpc_status flash_command_wp_get_range(uint8_t *data);
#endif /* __CROS_EC_FLASH_COMMANDS_H */
diff --git a/include/lpc_commands.h b/include/lpc_commands.h
index 5f422fd843..37b54dd380 100644
--- a/include/lpc_commands.h
+++ b/include/lpc_commands.h
@@ -138,5 +138,47 @@ struct lpc_params_flash_erase {
uint32_t size; /* Size to erase in bytes */
} __attribute__ ((packed));
+/* Flashmap offset */
+#define EC_LPC_COMMAND_FLASH_GET_FLASHMAP 0x14
+struct lpc_response_flash_flashmap {
+ uint32_t offset; /* Flashmap offset */
+} __attribute__ ((packed));
+
+/* Enable/disable flash write protect */
+#define EC_LPC_COMMAND_FLASH_WP_ENABLE 0x15
+struct lpc_params_flash_wp_enable {
+ uint32_t enable_wp;
+} __attribute__ ((packed));
+
+/* Get flash write protection commit state */
+#define EC_LPC_COMMAND_FLASH_WP_GET_STATE 0x16
+struct lpc_response_flash_wp_enable {
+ uint32_t enable_wp;
+} __attribute__ ((packed));
+
+/* Set/get flash write protection range */
+#define EC_LPC_COMMAND_FLASH_WP_SET_RANGE 0x17
+struct lpc_params_flash_wp_range {
+ /* Byte offset aligned to info.protect_block_size */
+ uint32_t offset;
+ /* Size should be multiply of info.protect_block_size */
+ uint32_t size;
+} __attribute__ ((packed));
+
+#define EC_LPC_COMMAND_FLASH_WP_GET_RANGE 0x18
+struct lpc_response_flash_wp_range {
+ uint32_t offset;
+ uint32_t size;
+} __attribute__ ((packed));
+
+/* Read flash write protection GPIO pin */
+#define EC_LPC_COMMAND_FLASH_WP_GET_GPIO 0x19
+struct lpc_params_flash_wp_gpio {
+ uint32_t pin_no;
+} __attribute__ ((packed));
+struct lpc_response_flash_wp_gpio {
+ uint32_t value;
+} __attribute__ ((packed));
+
#endif /* __CROS_EC_LPC_COMMANDS_H */