diff options
author | Rong Chang <rongchang@google.com> | 2011-12-09 14:34:54 +0800 |
---|---|---|
committer | Rong Chang <rongchang@google.com> | 2011-12-12 18:21:08 +0800 |
commit | 1eb5417847831c9dda4c8609af67ad21f9bfba42 (patch) | |
tree | 1f94c726cb5d72397a66c765981d46ad8dd0aa6d | |
parent | 82f89a25373e0e614d3d1e94c91b1455242467ab (diff) | |
download | chrome-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.c | 126 | ||||
-rw-r--r-- | chip/lm4/registers.h | 1 | ||||
-rw-r--r-- | common/flash_commands.c | 96 | ||||
-rw-r--r-- | common/host_command.c | 12 | ||||
-rw-r--r-- | include/flash_commands.h | 4 | ||||
-rw-r--r-- | include/lpc_commands.h | 42 |
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 */ |