Merge "Implement persistent flash write protect settings"

This commit is contained in:
Gerrit
2012-04-02 12:49:54 -07:00
committed by Gerrit Code Review
7 changed files with 623 additions and 341 deletions

View File

@@ -21,6 +21,10 @@
#define CONFIG_TMP006
#define CONFIG_USB_CHARGE
/* Temporarily use RECOVERYn signal for the write protect signal instead of
* WRITE_PROTECTn. This way we can test via servo. crosbug.com/p/8580. */
#define CONFIG_WP_USES_RECOVERY_GPIO
/* 66.667 Mhz clock frequency */
#define CPU_CLOCK 66666667

View File

@@ -12,7 +12,7 @@
/* Memory mapping */
#define CONFIG_FLASH_BASE 0x00000000
#define CONFIG_FLASH_SIZE 0x00040000
#define CONFIG_FLASH_BANK_SIZE 0x2000
#define CONFIG_FLASH_BANK_SIZE 0x00000800 /* Protect bank size */
#define CONFIG_RAM_BASE 0x20000000
#define CONFIG_RAM_SIZE 0x00008000

View File

@@ -1,4 +1,4 @@
/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
@@ -6,24 +6,21 @@
/* Flash memory module for Chrome EC */
#include "flash.h"
#include "gpio.h"
#include "uart.h"
#include "registers.h"
#include "util.h"
#define FLASH_WRITE_BYTES 4
#define FLASH_FWB_WORDS 32
#define FLASH_FWB_BYTES (FLASH_FWB_WORDS * 4)
#define FLASH_ERASE_BYTES 1024
#define FLASH_PROTECT_BYTES 2048
#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;
int flash_get_size(void)
{
return usable_flash_size;
}
int flash_get_write_block_size(void)
{
@@ -39,16 +36,19 @@ int flash_get_erase_block_size(void)
int flash_get_protect_block_size(void)
{
BUILD_ASSERT(FLASH_PROTECT_BYTES == CONFIG_FLASH_BANK_SIZE);
return FLASH_PROTECT_BYTES;
}
int flash_read(int offset, int size, char *data)
int flash_physical_size(void)
{
if (size < 0 || offset > usable_flash_size ||
offset + size > usable_flash_size)
return EC_ERROR_UNKNOWN; /* Invalid range */
return (LM4_FLASH_FSIZE + 1) * FLASH_PROTECT_BYTES;
}
int flash_physical_read(int offset, int size, char *data)
{
/* Just read the flash from its memory window. */
/* TODO: (crosbug.com/p/7473) is this affected by data cache?
* That is, if we read a block, then alter it, then read it
@@ -58,8 +58,8 @@ int flash_read(int offset, int size, char *data)
}
/* Performs a write-buffer operation. Buffer (FWB) and address (FMA)
* must be pre-loaded. */
/* Perform a write-buffer operation. Buffer (FWB) and address (FMA) must be
* pre-loaded. */
static int write_buffer(void)
{
if (!LM4_FLASH_FWBVAL)
@@ -72,32 +72,25 @@ static int write_buffer(void)
LM4_FLASH_FMC2 = 0xa4420001;
/* Wait for write to complete */
/* TODO: timeout */
while (LM4_FLASH_FMC2 & 0x01) {}
/* Check for error conditions - program failed, erase needed,
* voltage error. */
if (LM4_FLASH_FCRIS & 0x2600)
if (LM4_FLASH_FCRIS & 0x2e01)
return EC_ERROR_UNKNOWN;
return EC_SUCCESS;
}
int flash_write(int offset, int size, const char *data)
int flash_physical_write(int offset, int size, const char *data)
{
const uint32_t *data32 = (const uint32_t *)data;
int rv;
int i;
if (size < 0 || offset > usable_flash_size ||
offset + size > usable_flash_size ||
(offset | size) & (FLASH_WRITE_BYTES - 1))
return EC_ERROR_UNKNOWN; /* Invalid range */
/* TODO (crosbug.com/p/7478) - safety check - don't allow writing to
* the image we're running from */
/* Get initial page and write buffer index */
/* Get initial write buffer index and page */
LM4_FLASH_FMA = offset & ~(FLASH_FWB_BYTES - 1);
i = (offset >> 2) & (FLASH_FWB_WORDS - 1);
@@ -125,16 +118,8 @@ int flash_write(int offset, int size, const char *data)
}
int flash_erase(int offset, int size)
int flash_physical_erase(int offset, int size)
{
if (size < 0 || offset > usable_flash_size ||
offset + size > usable_flash_size ||
(offset | size) & (FLASH_ERASE_BYTES - 1))
return EC_ERROR_UNKNOWN; /* Invalid range */
/* TODO (crosbug.com/p/7478) - safety check - don't allow erasing the
* image we're running from */
LM4_FLASH_FCMISC = LM4_FLASH_FCRIS; /* Clear previous error status */
LM4_FLASH_FMA = offset;
@@ -143,10 +128,12 @@ int flash_erase(int offset, int size)
LM4_FLASH_FMC = 0xa4420002;
/* Wait for erase to complete */
/* TODO: timeout */
while (LM4_FLASH_FMC & 0x02) {}
/* Check for error conditions - erase failed, voltage error */
if (LM4_FLASH_FCRIS & 0x0a00)
/* Check for error conditions - erase failed, voltage error,
* protection error */
if (LM4_FLASH_FCRIS & 0x0a01)
return EC_ERROR_UNKNOWN;
LM4_FLASH_FMA += FLASH_ERASE_BYTES;
@@ -155,149 +142,14 @@ 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)
int flash_physical_get_protect(int block)
{
return LM4_FLASH_FMPPE[F_BANK(block)] & F_BIT(block);
return (LM4_FLASH_FMPPE[F_BANK(block)] & F_BIT(block)) ? 0 : 1;
}
static void set_block_wp(int block)
void flash_physical_set_protect(int block)
{
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(uint32_t *start, uint32_t *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 = (uint32_t)(end_blk - start_blk + 1);
*start = (uint32_t)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(uint32_t *offset, uint32_t *size)
{
uint32_t 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(uint32_t offset, uint32_t size)
{
int start, nblock;
int rv;
if ((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)
{
uint32_t 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;
}
int flash_pre_init(void)
{
/* Calculate usable flash size. Reserve one protection block
* at the top to hold the write protect range. FSIZE already
* returns one less than the number of protection pages. */
usable_flash_size = LM4_FLASH_FSIZE * FLASH_PROTECT_BYTES;
/* TODO (crosbug.com/p/7453) - check WP# GPIO. If it's set and the
* flash protect range is set, write the flash protection registers.
* Probably cleaner to do this in vboot, since we're going to need to
* use the same last block of flash to hold the firmware rollback
* counters. */
return EC_SUCCESS;
}

View File

@@ -10,7 +10,7 @@ common-y+=memory_commands.o shared_mem.o system_common.o
common-y+=gpio_commands.o version.o
common-$(CONFIG_BATTERY_ATL706486)+=battery_atl706486.o
common-$(CONFIG_CHARGER_BQ24725)+=charger_bq24725.o
common-$(CONFIG_FLASH)+=flash_commands.o
common-$(CONFIG_FLASH)+=flash_common.o flash_commands.o
common-$(CONFIG_LIGHTBAR)+=lightbar.o
common-$(CONFIG_LPC)+=port80.o host_event_commands.o
common-$(CONFIG_POWER_LED)+=power_led.o

View File

@@ -14,13 +14,81 @@
#include "uart.h"
#include "util.h"
/* Parse offset and size from command line argv[0] and argv[1].
*
* Default values: If argc<1, leaves offset unchanged, returning error if
* *offset<0. If argc<2, leaves size unchanged, returning error if *size<0. */
static int parse_offset_size(int argc, char **argv, int *offset, int *size)
{
char *e;
int i;
if (argc >= 1) {
i = (uint32_t)strtoi(argv[0], &e, 0);
if (e && *e) {
uart_printf("Invalid offset \"%s\"\n", argv[0]);
return EC_ERROR_INVAL;
}
*offset = i;
} else if (*offset < 0) {
uart_puts("Must specify offset.\n");
return EC_ERROR_INVAL;
}
if (argc >= 2) {
i = (uint32_t)strtoi(argv[1], &e, 1);
if (e && *e) {
uart_printf("Invalid size \"%s\"\n", argv[1]);
return EC_ERROR_INVAL;
}
*size = i;
} else if (*size < 0) {
uart_puts("Must specify offset and size.\n");
return EC_ERROR_INVAL;
}
return EC_SUCCESS;
}
/*****************************************************************************/
/* Console commands */
static int command_flash_info(int argc, char **argv)
{
uart_printf("Usable flash size: %d B\n", flash_get_size());
const uint8_t *wp;
int banks = flash_get_size() / flash_get_protect_block_size();
int i;
uart_printf("Physical size: %4d KB\n", flash_physical_size() / 1024);
uart_printf("Usable size: %4d KB\n", flash_get_size() / 1024);
uart_printf("Write block: %4d B\n", flash_get_write_block_size());
uart_printf("Erase block: %4d B\n", flash_get_erase_block_size());
uart_printf("Protect block: %4d B\n", flash_get_protect_block_size());
i = flash_get_protect_lock();
uart_printf("Protect lock: %s%s\n",
(i & FLASH_PROTECT_LOCK_SET) ? "LOCKED" : "unlocked",
(i & FLASH_PROTECT_LOCK_APPLIED) ? " AND APPLIED" : "");
uart_printf("WP pin: %s\n", (i & FLASH_PROTECT_PIN_ASSERTED) ?
"ASSERTED" : "deasserted");
wp = flash_get_protect_array();
uart_puts("Protected now:");
for (i = 0; i < banks; i++) {
if (!(i & 7))
uart_puts(" ");
uart_puts(wp[i] & FLASH_PROTECT_UNTIL_REBOOT ? "Y" : ".");
}
uart_puts("\n Persistent: ");
for (i = 0; i < banks; i++) {
if (!(i & 7))
uart_puts(" ");
uart_puts(wp[i] & FLASH_PROTECT_PERSISTENT ? "Y" : ".");
}
uart_puts("\n");
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(flashinfo, command_flash_info);
@@ -28,73 +96,37 @@ DECLARE_CONSOLE_COMMAND(flashinfo, command_flash_info);
static int command_flash_erase(int argc, char **argv)
{
int offset = 0;
int size = FLASH_ERASE_BYTES;
char *endptr;
int offset = -1;
int size = flash_get_erase_block_size();
int rv;
if (argc < 2) {
uart_puts("Usage: flasherase <offset> [size]\n");
return EC_ERROR_UNKNOWN;
}
offset = strtoi(argv[1], &endptr, 0);
if (*endptr) {
uart_puts("Invalid offset\n");
return EC_ERROR_UNKNOWN;
}
if (argc > 2) {
size = strtoi(argv[2], &endptr, 0);
if (*endptr) {
uart_puts("Invalid size\n");
return EC_ERROR_UNKNOWN;
}
}
rv = parse_offset_size(argc - 1, argv + 1, &offset, &size);
if (rv)
return rv;
uart_printf("Erasing %d bytes at offset 0x%x (%d)...\n",
size, offset, offset);
rv = flash_erase(offset, size);
if (rv == EC_SUCCESS)
uart_puts("done.\n");
else
uart_printf("failed. (error %d)\n", rv);
return rv;
return flash_erase(offset, size);
}
DECLARE_CONSOLE_COMMAND(flasherase, command_flash_erase);
static int command_flash_write(int argc, char **argv)
{
char *data;
int offset = 0;
int size = 1024; /* Default size */
char *endptr;
int offset = -1;
int size = flash_get_erase_block_size();
int rv;
char *data;
int i;
if (argc < 2) {
uart_puts("Usage: flashwrite <offset> <size>\n");
return EC_ERROR_UNKNOWN;
}
offset = strtoi(argv[1], &endptr, 0);
if (*endptr) {
uart_puts("Invalid offset\n");
return EC_ERROR_UNKNOWN;
}
rv = parse_offset_size(argc - 1, argv + 1, &offset, &size);
if (rv)
return rv;
if (argc > 2) {
size = strtoi(argv[2], &endptr, 0);
if (*endptr) {
uart_puts("Invalid size\n");
return EC_ERROR_UNKNOWN;
}
if (size > shared_mem_size()) {
uart_puts("Truncating size\n");
size = sizeof(data);
}
if (size > shared_mem_size()) {
uart_puts("Truncating size\n");
size = shared_mem_size();
}
/* Acquire the shared memory buffer */
@@ -124,70 +156,46 @@ static int command_flash_write(int argc, char **argv)
DECLARE_CONSOLE_COMMAND(flashwrite, command_flash_write);
static const char flash_wp_help[] =
"Usage: flashwp <now | set | clear> <offset> [size]\n"
" or: flashwp <lock | unlock>\n";
static int command_flash_wp(int argc, char **argv)
{
int b = 0;
char *endptr;
if (argc < 2) {
uart_puts("Usage: flashwp [bitmask]\n");
uart_printf("(current value of FMPPE1: 0x%08x)\n",
LM4_FLASH_FMPPE1);
return EC_SUCCESS;
}
b = strtoi(argv[1], &endptr, 0);
if (*endptr) {
uart_puts("Invalid bitmask\n");
return EC_ERROR_UNKNOWN;
}
uart_printf("FMPPE1 before: 0x%08x\n", LM4_FLASH_FMPPE1);
LM4_FLASH_FMPPE1 = b;
uart_printf("FMPPE1 after: 0x%08x\n", LM4_FLASH_FMPPE1);
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(flashwp, command_flash_wp);
static int command_flash_wp_range(int argc, char **argv)
{
uint32_t offset, size;
char *endptr;
int offset = -1;
int size = flash_get_protect_block_size();
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 = (uint32_t)strtoi(argv[1], &endptr, 0);
if (*endptr) {
uart_printf("Invalid offset \"%s\"\n", argv[1]);
return EC_ERROR_UNKNOWN;
}
size = (uint32_t)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;
}
if (argc < 2) {
uart_puts(flash_wp_help);
return EC_ERROR_INVAL;
}
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(flashwprange, command_flash_wp_range);
/* Commands that don't need offset and size */
if (!strcasecmp(argv[1], "lock"))
return flash_lock_protect(1);
else if (!strcasecmp(argv[1], "unlock"))
return flash_lock_protect(0);
/* All remaining commands need offset and size */
rv = parse_offset_size(argc - 2, argv + 2, &offset, &size);
if (rv)
return rv;
if (!strcasecmp(argv[1], "now"))
return flash_protect_until_reboot(offset, size);
else if (!strcasecmp(argv[1], "set"))
return flash_set_protect(offset, size, 1);
else if (!strcasecmp(argv[1], "clear"))
return flash_set_protect(offset, size, 0);
else {
uart_puts(flash_wp_help);
return EC_ERROR_INVAL;
}
}
DECLARE_CONSOLE_COMMAND(flashwp, command_flash_wp);
/*****************************************************************************/
/* Host commands */
@@ -198,9 +206,9 @@ enum lpc_status flash_command_get_info(uint8_t *data)
(struct lpc_response_flash_info *)data;
r->flash_size = flash_get_size();
r->write_block_size = FLASH_WRITE_BYTES;
r->erase_block_size = FLASH_ERASE_BYTES;
r->protect_block_size = FLASH_PROTECT_BYTES;
r->write_block_size = flash_get_write_block_size();
r->erase_block_size = flash_get_erase_block_size();
r->protect_block_size = flash_get_protect_block_size();
return EC_LPC_RESULT_SUCCESS;
}
DECLARE_HOST_COMMAND(EC_LPC_COMMAND_FLASH_INFO, flash_command_get_info);
@@ -286,7 +294,13 @@ enum lpc_status flash_command_erase(uint8_t *data)
DECLARE_HOST_COMMAND(EC_LPC_COMMAND_FLASH_ERASE, flash_command_erase);
/* TODO: use shadow range in EEPROM */
#ifdef REWORK_THESE_TO_USE_NEW_WP_MECHANISM
/* TODO: (crosbug.com/p/8448) rework these to use the new WP mechanism. Note
* that the concept of a single range oversimplifies how WP works; there are
* multiple protected regions, including the persistent WP state (last bank)
* and rollback information (bank before that; will be implemented by
* vboot). */
static int shadow_wp_offset;
static int shadow_wp_size;
@@ -354,3 +368,5 @@ enum lpc_status flash_command_wp_get_range(uint8_t *data)
}
DECLARE_HOST_COMMAND(EC_LPC_COMMAND_FLASH_WP_GET_RANGE,
flash_command_wp_get_range);
#endif /* REWORK_THESE_TO_USE_NEW_WP_MECHANISM */

349
common/flash_common.c Normal file
View File

@@ -0,0 +1,349 @@
/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* Flash memory module for Chrome EC - common functions */
#include "config.h"
#include "flash.h"
#include "gpio.h"
#include "registers.h"
#include "uart.h"
#include "util.h"
#define PERSIST_STATE_VERSION 1
#define MAX_BANKS (CONFIG_FLASH_SIZE / CONFIG_FLASH_BANK_SIZE)
/* Persistent protection state */
struct persist_state {
uint8_t version; /* Version of this struct */
uint8_t lock; /* Lock flags */
uint8_t reserved[2]; /* Reserved; set 0 */
uint8_t blocks[MAX_BANKS]; /* Per-block flags */
};
static int usable_flash_size; /* Usable flash size, not counting pstate */
static struct persist_state pstate; /* RAM copy of pstate data */
/* Return non-zero if the write protect pin is asserted */
static int wp_pin_asserted(void)
{
#ifdef CONFIG_WP_USES_RECOVERY_GPIO
/* Bypass all WP if the recovery GPIO is asserted. This is a temporary
* workaround to allow servo-based testing of WP. See
* crosbug.com/p/8580. */
return gpio_get_level(GPIO_RECOVERYn) == 0 ? 0 : 1;
#else
return gpio_get_level(GPIO_WRITE_PROTECTn) == 0 ? 1 : 0;
#endif
}
/* Read persistent state into pstate. */
static int read_pstate(void)
{
int i;
int rv = flash_physical_read(usable_flash_size, sizeof(pstate),
(char *)&pstate);
if (rv)
return rv;
/* Sanity-check data and initialize if necessary */
if (pstate.version != PERSIST_STATE_VERSION) {
memset(&pstate, 0, sizeof(pstate));
pstate.version = PERSIST_STATE_VERSION;
}
/* Mask off currently-valid flags */
pstate.lock &= FLASH_PROTECT_LOCK_SET;
for (i = 0; i < MAX_BANKS; i++)
pstate.blocks[i] &= FLASH_PROTECT_PERSISTENT;
return EC_SUCCESS;
}
/* Write persistent state from pstate, erasing if necessary. */
static int write_pstate(void)
{
int rv;
/* Erase top protection block. Assumes pstate size is less than
* erase/protect block size, and protect block size is less than erase
* block size. */
/* TODO: optimize based on current physical flash contents; we can
* avoid the erase if we're only changing 1's into 0's. */
rv = flash_physical_erase(usable_flash_size,
flash_get_protect_block_size());
if (rv)
return rv;
/* Note that if we lose power in here, we'll lose the pstate contents.
* That's ok, because it's only possible to write the pstate before
* it's protected. */
/* Rewrite the data */
return flash_physical_write(usable_flash_size, sizeof(pstate),
(const char *)&pstate);
}
/* Apply write protect based on persistent state. */
static int apply_pstate(void)
{
int pbsize = flash_get_protect_block_size();
int banks = usable_flash_size / pbsize;
int rv, i;
/* If write protect is disabled, nothing to do */
if (!wp_pin_asserted())
return EC_SUCCESS;
/* Read the current persist state from flash */
rv = read_pstate();
if (rv)
return rv;
/* If flash isn't locked, nothing to do */
if (!(pstate.lock & FLASH_PROTECT_LOCK_SET))
return EC_SUCCESS;
/* Lock the protection data first */
flash_physical_set_protect(banks);
/* Then lock any banks necessary */
for (i = 0; i < banks; i++) {
if (pstate.blocks[i] & FLASH_PROTECT_PERSISTENT)
flash_physical_set_protect(i);
}
return EC_SUCCESS;
}
/* Return non-zero if pstate block is already write-protected. */
static int is_pstate_lock_applied(void)
{
int pstate_block = usable_flash_size / flash_get_protect_block_size();
/* Fail if write protect block is already locked */
return flash_physical_get_protect(pstate_block);
}
int flash_get_size(void)
{
return usable_flash_size;
}
int flash_read(int offset, int size, char *data)
{
if (size < 0 || offset > usable_flash_size ||
offset + size > usable_flash_size)
return EC_ERROR_UNKNOWN; /* Invalid range */
return flash_physical_read(offset, size, data);
}
int flash_write(int offset, int size, const char *data)
{
if (size < 0 || offset > usable_flash_size ||
offset + size > usable_flash_size ||
(offset | size) & (flash_get_write_block_size() - 1))
return EC_ERROR_UNKNOWN; /* Invalid range */
/* TODO (crosbug.com/p/7478) - safety check - don't allow writing to
* the image we're running from */
return flash_physical_write(offset, size, data);
}
int flash_erase(int offset, int size)
{
if (size < 0 || offset > usable_flash_size ||
offset + size > usable_flash_size ||
(offset | size) & (flash_get_erase_block_size() - 1))
return EC_ERROR_UNKNOWN; /* Invalid range */
/* TODO (crosbug.com/p/7478) - safety check - don't allow erasing the
* image we're running from */
return flash_physical_erase(offset, size);
}
int flash_protect_until_reboot(int offset, int size)
{
int pbsize = flash_get_protect_block_size();
int i;
if (size < 0 || offset > usable_flash_size ||
offset + size > usable_flash_size ||
(offset | size) & (pbsize - 1))
return EC_ERROR_INVAL; /* Invalid range */
/* Convert offset and size to blocks */
offset /= pbsize;
size /= pbsize;
for (i = 0; i < size; i++)
flash_physical_set_protect(offset + i);
return EC_SUCCESS;
}
int flash_set_protect(int offset, int size, int enable)
{
uint8_t newflag = enable ? FLASH_PROTECT_PERSISTENT : 0;
int pbsize = flash_get_protect_block_size();
int rv, i;
if (size < 0 || offset > usable_flash_size ||
offset + size > usable_flash_size ||
(offset | size) & (pbsize - 1))
return EC_ERROR_INVAL; /* Invalid range */
/* Fail if write protect block is already locked */
if (is_pstate_lock_applied())
return EC_ERROR_UNKNOWN;
/* Read the current persist state from flash */
rv = read_pstate();
if (rv)
return rv;
/* Convert offset and size to blocks */
offset /= pbsize;
size /= pbsize;
/* Set the new state */
for (i = 0; i < size; i++) {
pstate.blocks[offset + i] &= ~FLASH_PROTECT_PERSISTENT;
pstate.blocks[offset + i] |= newflag;
}
/* Write the state back to flash */
return write_pstate();
}
int flash_lock_protect(int lock)
{
int rv;
/* Fail if write protect block is already locked */
if (is_pstate_lock_applied())
return EC_ERROR_UNKNOWN;
/* Read the current persist state from flash */
rv = read_pstate();
if (rv)
return rv;
/* Set the new flag */
pstate.lock = lock ? FLASH_PROTECT_LOCK_SET : 0;
/* Write the state back to flash */
rv = write_pstate();
if (rv)
return rv;
/* If unlocking, done now */
if (!lock)
return EC_SUCCESS;
/* Otherwise, we need to apply all locks NOW */
return apply_pstate();
}
const uint8_t *flash_get_protect_array(void)
{
/* Return a copy of the current write protect state. This is an array
* of per-protect-block flags. (This is NOT the actual array, so
* attempting to change it will have no effect.) */
int pbsize = flash_get_protect_block_size();
int banks = usable_flash_size / pbsize;
int i;
/* Read the current persist state from flash */
read_pstate();
/* Combine with current block protection state */
for (i = 0; i < banks; i++) {
if (flash_physical_get_protect(i))
pstate.blocks[i] |= FLASH_PROTECT_UNTIL_REBOOT;
}
/* Return the block array */
return pstate.blocks;
}
int flash_get_protect(int offset, int size)
{
int pbsize = flash_get_protect_block_size();
uint8_t minflags = 0xff;
int i;
if (size < 0 || offset > usable_flash_size ||
offset + size > usable_flash_size ||
(offset | size) & (pbsize - 1))
return 0; /* Invalid range; assume nothing protected */
/* Convert offset and size to blocks */
offset /= pbsize;
size /= pbsize;
/* Read the current persist state from flash */
read_pstate();
/* Combine with current block protection state */
for (i = 0; i < size; i++) {
int f = pstate.blocks[offset + i];
if (flash_physical_get_protect(offset + i))
f |= FLASH_PROTECT_UNTIL_REBOOT;
minflags &= f;
}
return minflags;
}
int flash_get_protect_lock(void)
{
int flags;
/* Read the current persist state from flash */
read_pstate();
flags = pstate.lock;
/* Check if lock has been applied */
if (is_pstate_lock_applied())
flags |= FLASH_PROTECT_LOCK_APPLIED;
/* Check if write protect pin is asserted now */
if (wp_pin_asserted())
flags |= FLASH_PROTECT_PIN_ASSERTED;
return flags;
}
/*****************************************************************************/
/* Initialization */
int flash_pre_init(void)
{
/* Calculate usable flash size. Reserve one protection block
* at the top to hold the write protect data. */
usable_flash_size = flash_physical_size() -
flash_get_protect_block_size();
/* Apply write protect to blocks if needed */
return apply_pstate();
}

View File

@@ -11,12 +11,37 @@
#include "common.h"
#define FLASH_WRITE_BYTES 4
#define FLASH_FWB_WORDS 32
#define FLASH_FWB_BYTES (FLASH_FWB_WORDS * 4)
#define FLASH_ERASE_BYTES 1024
#define FLASH_PROTECT_BYTES 2048
/*****************************************************************************/
/* Low-level methods, for use by flash_common. */
/* Return the write / erase / protect block size, in bytes. Operations must be
* aligned to and multiples of the granularity. For example, erase operations
* must have offset and size which are multiples of the erase block size. */
int flash_get_write_block_size(void);
int flash_get_erase_block_size(void);
int flash_get_protect_block_size(void);
/* Return the physical size of flash in bytes */
int flash_physical_size(void);
/* Read <size> bytes of data from offset <offset> into <data>. */
int flash_physical_read(int offset, int size, char *data);
/* Write <size> bytes of data to flash at byte offset <offset>.
* <data> must be 32-bit aligned. */
int flash_physical_write(int offset, int size, const char *data);
/* Erase <size> bytes of flash at byte offset <offset>. */
int flash_physical_erase(int offset, int size);
/* Return non-zero if block is protected until reboot. */
int flash_physical_get_protect(int block);
/* Protects the block until reboot. */
void flash_physical_set_protect(int block);
/*****************************************************************************/
/* High-level interface for use by other modules. */
/* Initializes the module. */
int flash_pre_init(void);
@@ -25,14 +50,6 @@ int flash_pre_init(void);
* smaller than the actual flash size, */
int flash_get_size(void);
/* Returns the write / erase / protect block size, in bytes.
* Operations must be aligned to and multiples of the granularity.
* For example, erase operations must have offset and size which are
* multiples of the erase block size. */
int flash_get_write_block_size(void);
int flash_get_erase_block_size(void);
int flash_get_protect_block_size(void);
/* Reads <size> bytes of data from offset <offset> into <data>. */
int flash_read(int offset, int size, char *data);
@@ -43,46 +60,90 @@ int flash_write(int offset, int size, const char *data);
/* Erases <size> bytes of flash at byte offset <offset>. */
int flash_erase(int offset, int size);
/* TODO: not super happy about the following APIs yet.
/* Flash protection APIs
*
* The theory of operation is that we'll use the last page of flash to
* hold the write protect range, and the flag for whether the last
* page itself should be protected. Then when flash_init() is called,
* it checks if the write protect pin is asserted, and if so, it
* writes (but does not commit) the flash protection registers.
* Flash can be protected on a per-block basis at any point by calling
* flash_protect_until_reboot(). Once a block is protected, it will stay
* protected until reboot. This function may be called at any time, regardless
* of the persistent flash protection state, and protection will be applied
* immediately.
*
* This simulates what a SPI flash does, where the status register
* holds the write protect range, and a bit which protects the status
* register itself. The bit is only obeyed if the write protect pin
* is enabled.
* Flash may also be protected in a persistent fashion by calling
* flash_set_protect(). This sets a persistent flag for each block which is
* checked at boot time and applied if the hardware write protect pin is
* enabled.
*
* It's an imperfect simulation, because in a SPI flash, as soon as
* you deassert the pin you can alter the status register, where here
* it'll take a cold boot to clear protection. Also, here protection
* gets written to the registers as soon as you set the write protect
* lock, which is different than SPI, where it's effective as soon as
* you set the write protect range. */
* The flash persistent protection settings are themselves protected by a lock,
* which can be set via flash_lock_protect(). Once the protection settings are
* locked:
*
* (1) They will be immediately applied (as if flash_protect_until_reboot()
* had been called).
*
* (2) The persistent settings cannot be changed. That is, subsequent calls
* to flash_set_protect() and flash_lock_protect() will fail.
*
* The lock can be bypassed by cold-booting the system with the hardware write
* protect pin deasserted. In this case, the persistent settings and lock
* state may be changed until flash_lock_protect(non-zero) is called, or until
* the system is rebooted with the write protect pin asserted - at which point,
* protection is re-applied. */
/* Gets or sets the write protect range in bytes. This setting is
* stored in flash, and persists across reboots. If size is non-zero,
* the write protect range is also locked, and may not be subsequently
* altered until after a cold boot with the write protect pin
* deasserted. */
int flash_get_write_protect_range(uint32_t *offset, uint32_t *size);
int flash_set_write_protect_range(uint32_t offset, uint32_t size);
/* Write-protect <size> bytes of flash at byte offset <offset> until next
* reboot. */
int flash_protect_until_reboot(int offset, int size);
/* The write protect range has been stored into the chip registers
* this boot. The flash is write protected and the range cannot be
* changed without rebooting. */
#define EC_FLASH_WP_RANGE_LOCKED 0x01
/* The write protect pin was asserted at init time. */
#define EC_FLASH_WP_PIN_ASSERTED_AT_INIT 0x02
/* The write protect pin is asserted now. */
#define EC_FLASH_WP_PIN_ASSERTED_NOW 0x04
/* Higher-level APIs to emulate SPI write protect */
/* Returns the current write protect status; see EC_FLASH_WP_*
* for valid flags. */
int flash_get_write_protect_status(void);
/* Set (enable=1) or clear (enable=0) the persistent write protect setting for
* the specified range. This will only succeed if write protect is unlocked.
* This will take effect on the next boot, or when flash_lock_protect(1) is
* called. */
int flash_set_protect(int offset, int size, int enable);
/* Lock or unlock the persistent write protect settings. Once the write
* protect settings are locked, they will STAY locked until the system is
* cold-booted with the hardware write protect pin disabled.
*
* If called with lock!=0, this will also immediately protect all
* persistently-protected blocks. */
int flash_lock_protect(int lock);
/* Flags for flash_get_protect() and flash_get_protect_array(). */
/* Protected persistently. Note that if the write protect pin was deasserted
* at boot time, a block may have the FLASH_PROTECT_PERSISTENT flag indicating
* the block would be protected on a normal boot, but may not have the
* FLASH_PROTECT_UNTIL_REBOOT flag indicating it's actually protected right
* now. */
#define FLASH_PROTECT_PERSISTENT 0x01
/* Protected until reboot. This will be set for persistently-protected blocks
* as soon as the flash module protects them, and for non-persistent protection
* after flash_protect_until_reboot() is called on a block. */
#define FLASH_PROTECT_UNTIL_REBOOT 0x02
/* Return a copy of the current write protect state. This is an array of
* per-protect-block flags. The data is valid until the next call to a flash
* function. */
const uint8_t *flash_get_protect_array(void);
/* Return the lowest amount of protection for any flash block in the specified
* range. That is, if any byte in the range is not protected until reboot,
* FLASH_PROTECT_UNTIL_REBOOT will not be set. */
int flash_get_protect(int offset, int size);
/* Flags for flash_get_protect_lock() */
/* Flash protection lock has been set. Note that if the write protect pin was
* deasserted at boot time, this simply indicates the state of the lock
* setting, and not whether blocks are actually protected. */
#define FLASH_PROTECT_LOCK_SET 0x01
/* Flash protection lock has actually been applied. All blocks with
FLASH_PROTECT_PERSISTENT have been protected, and flash protection cannot be
unlocked. */
#define FLASH_PROTECT_LOCK_APPLIED 0x02
/* Write protect pin is currently asserted */
#define FLASH_PROTECT_PIN_ASSERTED 0x04
/* Return the flash protect lock status. */
int flash_get_protect_lock(void);
#endif /* __CROS_EC_FLASH_H */