diff options
Diffstat (limited to 'chip/g/flash.c')
-rw-r--r-- | chip/g/flash.c | 629 |
1 files changed, 0 insertions, 629 deletions
diff --git a/chip/g/flash.c b/chip/g/flash.c deleted file mode 100644 index 4829c986c8..0000000000 --- a/chip/g/flash.c +++ /dev/null @@ -1,629 +0,0 @@ -/* Copyright 2015 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. - */ - -/* - * The SoC's internal flash consists of two separate "banks" of 256K bytes each - * (sometimes called "macros" because of how they're implemented in Verilog). - * - * Each flash bank contains 128 "blocks" or "pages" of 2K bytes each. These - * blocks can be erased individually, or the entire bank can be erased at once. - * - * When the flash content is erased, all its bits are set to 1. - * - * The flash content can be read directly as bytes, halfwords, or words, just - * like any memory region. However, writes can only happen through special - * operations, in units of properly aligned 32-bit words. - * - * The flash controller has a 32-word write buffer. This allows up to 32 - * adjacent words (128 bytes) within a bank to be written in one operation. - * - * Multiple writes to the same flash word can be done without first erasing the - * block, however: - * - * A) writes can only change stored bits from 1 to 0, and - * - * B) the manufacturer recommends that no more than two writes be done between - * erase cycles for best results (in terms of reliability, longevity, etc.) - * - * All of this is fairly typical of most flash parts. This next thing is NOT - * typical: - * - * +--------------------------------------------------------------------------+ - * + While any write or erase operation is in progress, ALL other access to + - * + that entire bank is stalled. Data reads, instruction fetches, interrupt + - * + vector lookup -- every access blocks until the flash operation finishes. + - * +--------------------------------------------------------------------------+ - * - */ - -#include "common.h" -#include "board_id.h" -#include "console.h" -#include "cryptoc/util.h" -#include "extension.h" -#include "flash.h" -#include "flash_log.h" -#include "registers.h" -#include "shared_mem.h" -#include "task.h" -#include "timer.h" -#include "watchdog.h" - -#define CPRINTS(format, args...) cprints(CC_EXTENSION, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_EXTENSION, format, ## args) - -/* Mutex to prevent concurrent accesses to flash engine. */ -static struct mutex flash_mtx; - -#ifdef CONFIG_FLASH_LOG -static void flash_log_space_control(int enable) -{ - GWRITE_FIELD(GLOBALSEC, FLASH_REGION5_CTRL, WR_EN, !!enable); -} -#endif - -int flash_pre_init(void) -{ - struct g_flash_region regions[4]; - int i, num_regions; - - num_regions = flash_regions_to_enable(regions, ARRAY_SIZE(regions)); - - for (i = 0; i < num_regions; i++) { - int reg_base; - - /* Region range */ - reg_base = GBASE(GLOBALSEC) + - GOFFSET(GLOBALSEC, FLASH_REGION2_BASE_ADDR) + - i * 8; - - REG32(reg_base) = regions[i].reg_base; - - /* - * The hardware requires a value which is 1 less than the - * actual region size. - */ - REG32(reg_base + 4) = regions[i].reg_size - 1; - - /* Region permissions. */ - reg_base = GBASE(GLOBALSEC) + - GOFFSET(GLOBALSEC, FLASH_REGION2_CTRL) + - i * 4; - REG32(reg_base) = regions[i].reg_perms; - } - -#ifdef CONFIG_FLASH_LOG - /* - * Allow access to flash elog space and register the access control - * function. - */ - GREG32(GLOBALSEC, FLASH_REGION5_BASE_ADDR) = CONFIG_FLASH_LOG_BASE; - GREG32(GLOBALSEC, FLASH_REGION5_SIZE) = CONFIG_FLASH_LOG_SPACE - 1; - GWRITE_FIELD(GLOBALSEC, FLASH_REGION5_CTRL, EN, 1); - GWRITE_FIELD(GLOBALSEC, FLASH_REGION5_CTRL, RD_EN, 1); - flash_log_register_flash_control_callback(flash_log_space_control); -#endif - - /* Create a flash region window for INFO1 access. */ - GREG32(GLOBALSEC, FLASH_REGION7_BASE_ADDR) = FLASH_INFO_MEMORY_BASE; - GREG32(GLOBALSEC, FLASH_REGION7_SIZE) = FLASH_INFO_SIZE - 1; - GWRITE_FIELD(GLOBALSEC, FLASH_REGION7_CTRL, EN, 1); - GWRITE_FIELD(GLOBALSEC, FLASH_REGION7_CTRL, RD_EN, 1); - - return EC_SUCCESS; -} - -int flash_physical_get_protect(int bank) -{ - return 0; /* Not protected */ -} - -uint32_t flash_physical_get_protect_flags(void) -{ - return 0; /* no flags set */ -} - -uint32_t flash_physical_get_valid_flags(void) -{ - /* These are the flags we're going to pay attention to */ - return EC_FLASH_PROTECT_RO_AT_BOOT | - EC_FLASH_PROTECT_RO_NOW | - EC_FLASH_PROTECT_ALL_NOW; -} - -uint32_t flash_physical_get_writable_flags(uint32_t cur_flags) -{ - return 0; /* no flags writable */ -} - -int flash_physical_protect_at_boot(uint32_t new_flags) -{ - return EC_SUCCESS; /* yeah, I did it. */ -} - -int flash_physical_protect_now(int all) -{ - return EC_SUCCESS; /* yeah, I did it. */ -} - - -enum flash_op { - OP_ERASE_BLOCK, - OP_WRITE_BLOCK, - OP_READ_BLOCK, -}; - -static int do_flash_op(enum flash_op op, int is_info_bank, - int byte_offset, int words) -{ - volatile uint32_t *fsh_pe_control; - uint32_t opcode, tmp, errors; - int retry_count, max_attempts, extra_prog_pulse, i; - int timedelay_us = 100; - uint32_t prev_error = 0; - - /* Make sure the smart program/erase algorithms are enabled. */ - if (!GREAD(FLASH, FSH_TIMING_PROG_SMART_ALGO_ON) || - !GREAD(FLASH, FSH_TIMING_ERASE_SMART_ALGO_ON)) { - CPRINTF("%s:%d\n", __func__, __LINE__); - return EC_ERROR_UNIMPLEMENTED; - } - - /* Error status is self-clearing. Read it until it does (we hope). */ - for (i = 0; i < 50; i++) { - tmp = GREAD(FLASH, FSH_ERROR); - if (!tmp) - break; - usleep(timedelay_us); - } - /* If we can't clear the error status register then something is wrong. - */ - if (tmp) { - CPRINTF("%s:%d\n", __func__, __LINE__); - return EC_ERROR_UNKNOWN; - } - - /* We have two flash banks. Adjust offset and registers accordingly. */ - if (is_info_bank) { - /* Only INFO bank operations are supported. */ - fsh_pe_control = GREG32_ADDR(FLASH, FSH_PE_CONTROL1); - } else if (byte_offset >= CFG_FLASH_HALF) { - byte_offset -= CFG_FLASH_HALF; - fsh_pe_control = GREG32_ADDR(FLASH, FSH_PE_CONTROL1); - } else { - fsh_pe_control = GREG32_ADDR(FLASH, FSH_PE_CONTROL0); - } - - /* What are we doing? */ - switch (op) { - case OP_ERASE_BLOCK: -#ifndef CR50_RELAXED - if (is_info_bank) - /* Erasing the INFO bank from the RW section is - * unsupported. */ - return EC_ERROR_INVAL; -#endif - opcode = 0x31415927; - words = 0; /* don't care, really */ - /* This number is based on the TSMC spec Nme=Terase/Tsme */ - max_attempts = 45; - break; - case OP_WRITE_BLOCK: - opcode = 0x27182818; - words--; /* count register is zero-based */ - /* This number is based on the TSMC spec Nmp=Tprog/Tsmp */ - max_attempts = 9; - break; - case OP_READ_BLOCK: - if (!is_info_bank) - /* This code path only supports reading from - * the INFO bank. - */ - return EC_ERROR_INVAL; - opcode = 0x16021765; - words = 1; - max_attempts = 9; - break; - default: - return EC_ERROR_INVAL; - } - - /* - * Set the parameters. For writes, we assume the write buffer is - * already filled before we call this function. - */ - GWRITE_FIELD(FLASH, FSH_TRANS, OFFSET, - byte_offset / 4); /* word offset */ - GWRITE_FIELD(FLASH, FSH_TRANS, MAINB, is_info_bank ? 1 : 0); - GWRITE_FIELD(FLASH, FSH_TRANS, SIZE, words); - - /* TODO: Make sure this function isn't getting called "too often" in - * between erases. - */ - extra_prog_pulse = 0; - for (retry_count = 0; retry_count < max_attempts; retry_count++) { - /* Kick it off */ - GWRITE(FLASH, FSH_PE_EN, 0xb11924e1); - *fsh_pe_control = opcode; - - /* Wait for completion. 150ms should be enough - * (crosbug.com/p/45366). - */ - for (i = 0; i < 1500; i++) { - tmp = *fsh_pe_control; - if (!tmp) - break; - usleep(timedelay_us); - } - - /* Timed out waiting for control register to clear */ - if (tmp) { - /* Stop the failed operation. */ - *fsh_pe_control = 0; - CPRINTF("%s:%d\n", __func__, __LINE__); - return EC_ERROR_UNKNOWN; - } - /* Check error status */ - errors = GREAD(FLASH, FSH_ERROR); - - if (errors && (errors != prev_error)) { - prev_error = errors; - CPRINTF("%s:%d errors %x fsh_pe_control %pP\n", - __func__, __LINE__, errors, fsh_pe_control); - } - /* Error status is self-clearing. Read it until it does - * (we hope). - */ - for (i = 0; i < 50; i++) { - tmp = GREAD(FLASH, FSH_ERROR); - if (!tmp) - break; - usleep(timedelay_us); - } - /* If we can't clear the error status register then something - * is wrong. - */ - if (tmp) { - CPRINTF("%s:%d\n", __func__, __LINE__); - return EC_ERROR_UNKNOWN; - } - /* The operation was successful. */ - if (!errors) { - /* From the spec: - * "In addition, one more program pulse is needed after - * program verification is passed." - */ - if (op == OP_WRITE_BLOCK && !extra_prog_pulse) { - extra_prog_pulse = 1; - max_attempts++; - continue; - } - return EC_SUCCESS; - } - /* If there were errors after completion retry. */ - watchdog_reload(); - } - CPRINTF("%s:%d, retry count %d\n", __func__, __LINE__, retry_count); - return EC_ERROR_UNKNOWN; -} - -/* Write up to CONFIG_FLASH_WRITE_IDEAL_SIZE bytes at once */ -static int write_batch(int byte_offset, int is_info_bank, - int words, const uint8_t *data) -{ - volatile uint32_t *fsh_wr_data = GREG32_ADDR(FLASH, FSH_WR_DATA0); - uint32_t val; - int i; - int rv; - - mutex_lock(&flash_mtx); - - /* Load the write buffer. */ - for (i = 0; i < words; i++) { - /* - * We have to write 32-bit values, but we can't guarantee - * alignment for the data. We'll just assemble the word - * manually to avoid alignment faults. Note that we're assuming - * little-endian order here. - */ - val = ((data[3] << 24) | (data[2] << 16) | - (data[1] << 8) | data[0]); - - *fsh_wr_data = val; - data += 4; - fsh_wr_data++; - } - - rv = do_flash_op(OP_WRITE_BLOCK, is_info_bank, byte_offset, words); - - mutex_unlock(&flash_mtx); - - return rv; -} - -static int flash_physical_write_internal(int byte_offset, int is_info_bank, - int num_bytes, const char *data) -{ - int num, ret; - - /* The offset and size must be a multiple of CONFIG_FLASH_WRITE_SIZE */ - if (byte_offset % CONFIG_FLASH_WRITE_SIZE || - num_bytes % CONFIG_FLASH_WRITE_SIZE) - return EC_ERROR_INVAL; - - while (num_bytes) { - num = MIN(num_bytes, CONFIG_FLASH_WRITE_IDEAL_SIZE); - /* - * Make sure that the write operation will not go - * past a CONFIG_FLASH_ROW_SIZE boundary. - */ - num = MIN(num, CONFIG_FLASH_ROW_SIZE - - byte_offset % CONFIG_FLASH_ROW_SIZE); - ret = write_batch(byte_offset, - is_info_bank, - num / 4, /* word count */ - (const uint8_t *)data); - if (ret) - return ret; - - num_bytes -= num; - byte_offset += num; - data += num; - } - - return EC_SUCCESS; -} - -int flash_physical_write(int byte_offset, int num_bytes, const char *data) -{ - return flash_physical_write_internal(byte_offset, 0, num_bytes, data); -} - -int flash_physical_info_read_word(int byte_offset, uint32_t *dst) -{ - int ret; - - if (byte_offset % CONFIG_FLASH_WRITE_SIZE) - return EC_ERROR_INVAL; - - mutex_lock(&flash_mtx); - - ret = do_flash_op(OP_READ_BLOCK, 1, byte_offset, 1); - if (ret == EC_SUCCESS) - *dst = GREG32(FLASH, FSH_DOUT_VAL1); - - mutex_unlock(&flash_mtx); - - return ret; -} - -void flash_info_write_enable(void) -{ - GWRITE_FIELD(GLOBALSEC, FLASH_REGION7_CTRL, WR_EN, 1); -} - -void flash_info_write_disable(void) -{ - GWRITE_FIELD(GLOBALSEC, FLASH_REGION7_CTRL, WR_EN, 0); -} - -int flash_info_physical_write(int byte_offset, int num_bytes, const char *data) -{ - if (byte_offset < 0 || num_bytes < 0 || - byte_offset + num_bytes > FLASH_INFO_SIZE || - (byte_offset | num_bytes) & (CONFIG_FLASH_WRITE_SIZE - 1)) - return EC_ERROR_INVAL; - - return flash_physical_write_internal(byte_offset, 1, num_bytes, data); -} - -int flash_physical_erase(int byte_offset, int num_bytes) -{ - int ret; - - /* Offset and size must be a multiple of CONFIG_FLASH_ERASE_SIZE */ - if (byte_offset % CONFIG_FLASH_ERASE_SIZE || - num_bytes % CONFIG_FLASH_ERASE_SIZE) - return EC_ERROR_INVAL; - - while (num_bytes) { - - mutex_lock(&flash_mtx); - - /* We may be asked to erase multiple banks */ - ret = do_flash_op(OP_ERASE_BLOCK, - 0, /* not the INFO bank */ - byte_offset, - num_bytes / 4); /* word count */ - - mutex_unlock(&flash_mtx); - - if (ret) { - CPRINTF("Failed to erase block at %x\n", byte_offset); - return ret; - } - - num_bytes -= CONFIG_FLASH_ERASE_SIZE; - byte_offset += CONFIG_FLASH_ERASE_SIZE; - } - - return EC_SUCCESS; -} - - -/* Enable write access to the backup RO section. */ -void flash_open_ro_window(uint32_t offset, size_t size_b) -{ - GREG32(GLOBALSEC, FLASH_REGION6_BASE_ADDR) = - offset + CONFIG_PROGRAM_MEMORY_BASE; - GREG32(GLOBALSEC, FLASH_REGION6_SIZE) = size_b - 1; - GWRITE_FIELD(GLOBALSEC, FLASH_REGION6_CTRL, EN, 1); - GWRITE_FIELD(GLOBALSEC, FLASH_REGION6_CTRL, RD_EN, 1); - GWRITE_FIELD(GLOBALSEC, FLASH_REGION6_CTRL, WR_EN, 1); -} - -#ifdef CR50_DEV -/* - * The seed is the first 32 bytes of the manufacture state space. That is all - * we care about. We can ignore the rest of the manufacture state. - */ -#define ENDORSEMENT_SEED_SIZE 32 - -static enum vendor_cmd_rc vc_endorsement_seed(enum vendor_cmd_cc code, - void *buf, - size_t input_size, - size_t *response_size) -{ - uint8_t endorsement_seed[ENDORSEMENT_SEED_SIZE]; - int rv = VENDOR_RC_SUCCESS; - int is_erased = 1; - int set_seed = input_size == ENDORSEMENT_SEED_SIZE; - int i; - uint32_t *p; - int offset; - - *response_size = 0; - if (input_size && !set_seed) { - CPRINTS("%s: invalid seed", __func__); - return VENDOR_RC_BOGUS_ARGS; - } - - /* Read the endorsement key seed. */ - p = (uint32_t *)endorsement_seed; - for (i = 0; i < (ENDORSEMENT_SEED_SIZE / sizeof(*p)); i++) { - offset = FLASH_INFO_MANUFACTURE_STATE_OFFSET + i * sizeof(*p); - if (flash_physical_info_read_word(offset, p + i) != - EC_SUCCESS) { - CPRINTS("%s: failed read", __func__); - return VENDOR_RC_INTERNAL_ERROR; - } - if (p[i] != 0xffffffff) - is_erased = 0; - } - - if (set_seed && !is_erased) { - CPRINTS("%s: seed already set!", __func__); - return VENDOR_RC_NOT_ALLOWED; - } - - if (!input_size) { - *response_size = ENDORSEMENT_SEED_SIZE; - memcpy(buf, endorsement_seed, *response_size); - return VENDOR_RC_SUCCESS; - } - - flash_info_write_enable(); - if (flash_info_physical_write(FLASH_INFO_MANUFACTURE_STATE_OFFSET, - input_size, - (char *)buf) != EC_SUCCESS) { - CPRINTS("%s: failed write", __func__); - rv = VENDOR_RC_INTERNAL_ERROR; - } - flash_info_write_disable(); - return rv; -} -DECLARE_VENDOR_COMMAND(VENDOR_CC_ENDORSEMENT_SEED, vc_endorsement_seed); -#endif -#ifdef CR50_RELAXED -static int command_erase_flash_info(int argc, char **argv) -{ - int i; - int rv; - struct info1_layout *info1; - uint32_t *p; - - rv = shared_mem_acquire(sizeof(*info1), (char **)&info1); - if (rv != EC_SUCCESS) { - ccprintf("Failed to allocate memory for info1!\n"); - return rv; - } - - /* Read the entire info1. */ - p = (uint32_t *)info1; - for (i = 0; i < (sizeof(*info1) / sizeof(*p)); i++) { - if (flash_physical_info_read_word(i * sizeof(*p), p + i) != - EC_SUCCESS) { - ccprintf("Failed to read word %d!\n", i); - goto exit; - } - } - -#ifdef CR50_SQA - /* - * SQA images erase INFO1 RW mask, but do not allow erasing board ID. - * - * If compiled with CR50_SQA=1, board ID flags will set to zero, if - * compiled with CR50_SQA=2 or greater, board ID flags can be set to - * an arbitrary value passed in on the command line, but guaranteeing - * not to lock out the currently running image. - */ - { - uint32_t flags = 0; -#if CR50_SQA > 1 - if (argc > 1) { - char *e; - - flags = strtoi(argv[1], &e, 0); - if (*e) { - rv = EC_ERROR_PARAM1; - goto exit; - } - } -#endif - if (board_id_is_blank(&info1->board_space.bid)) { - ccprintf("BID is erased. Not modifying flags\n"); - } else { - ccprintf("setting BID flags to %x\n", flags); - info1->board_space.bid.flags = flags; - } - if (check_board_id_vs_header(&info1->board_space.bid, - get_current_image_header())) { - ccprintf("Flags %x would lock out current image\n", - flags); - rv = EC_ERROR_PARAM1; - goto exit; - } - } -#else /* CR50_SQA ^^^^^^ defined vvvvvvv Not defined. */ - /* - * This must be CR50_DEV=1 image, just erase the board information - * space. - */ - memset(&info1->board_space, 0xff, sizeof(info1->board_space)); -#endif /* CR50_SQA Not defined. */ - - memset(info1->rw_info_map, 0xff, sizeof(info1->rw_info_map)); - - mutex_lock(&flash_mtx); - - flash_info_write_enable(); - - rv = do_flash_op(OP_ERASE_BLOCK, 1, 0, 512); - - mutex_unlock(&flash_mtx); - - if (rv != EC_SUCCESS) { - ccprintf("Failed to erase info space!\n"); - goto exit; - } - - rv = flash_info_physical_write(0, sizeof(*info1), (char *)info1); - if (rv != EC_SUCCESS) - ccprintf("Failed write back info1 contents!\n"); - - exit: - flash_info_write_disable(); - always_memset(info1, 0, sizeof(*info1)); - shared_mem_release(info1); - return rv; -} -DECLARE_SAFE_CONSOLE_COMMAND(eraseflashinfo, command_erase_flash_info, -#if defined(CR50_SQA) && (CR50_SQA > 1) - "[bid flags]", - "Erase INFO1 flash space and set Board ID flags"); -#else - "", "Erase INFO1 flash space"); -#endif -#endif |