/* Copyright 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 */ #include "flash.h" #include "registers.h" #include "switch.h" #include "system.h" #include "timer.h" #include "util.h" #include "watchdog.h" #define FLASH_FWB_WORDS 32 #define FLASH_FWB_BYTES (FLASH_FWB_WORDS * 4) #define BANK_SHIFT 5 /* bank registers have 32bits each, 2^32 */ #define BANK_MASK (BIT(BANK_SHIFT) - 1) /* 5 bits */ #define F_BANK(b) ((b) >> BANK_SHIFT) #define F_BIT(b) (1 << ((b) & BANK_MASK)) /* Flash timeouts. These are 2x the spec sheet max. */ #define ERASE_TIMEOUT_MS 200 #define WRITE_TIMEOUT_US 300 int stuck_locked; /* Is physical flash stuck protected? */ int all_protected; /* Has all-flash protection been requested? */ /** * Protect flash banks until reboot. * * @param start_bank Start bank to protect * @param bank_count Number of banks to protect */ static void protect_banks(int start_bank, int bank_count) { int bank; for (bank = start_bank; bank < start_bank + bank_count; bank++) LM4_FLASH_FMPPE[F_BANK(bank)] &= ~F_BIT(bank); } /** * Perform a write-buffer operation. Buffer (FWB) and address (FMA) must be * pre-loaded. * * @return EC_SUCCESS, or nonzero if error. */ static int write_buffer(void) { int t; if (all_protected) return EC_ERROR_ACCESS_DENIED; if (!LM4_FLASH_FWBVAL) return EC_SUCCESS; /* Nothing to do */ /* Clear previous error status */ LM4_FLASH_FCMISC = LM4_FLASH_FCRIS; /* Start write operation at page boundary */ LM4_FLASH_FMC2 = 0xa4420001; /* * Reload the watchdog timer, so that writing a large amount of flash * doesn't cause a watchdog reset. */ watchdog_reload(); /* Wait for write to complete */ for (t = 0; LM4_FLASH_FMC2 & 0x01; t += 10) { if (t > WRITE_TIMEOUT_US) return EC_ERROR_TIMEOUT; udelay(10); } /* Check for error conditions - program failed, erase needed, * voltage error. */ if (LM4_FLASH_FCRIS & 0x2e01) return EC_ERROR_UNKNOWN; return EC_SUCCESS; } /*****************************************************************************/ /* Physical layer APIs */ int crec_flash_physical_write(int offset, int size, const char *data) { const uint32_t *data32 = (const uint32_t *)data; int rv; int i; if (all_protected) return EC_ERROR_ACCESS_DENIED; /* Fail if offset, size, and data aren't at least word-aligned */ if ((offset | size | (uint32_t)(uintptr_t)data) & 3) return EC_ERROR_INVAL; /* Get initial write buffer index and page */ LM4_FLASH_FMA = offset & ~(FLASH_FWB_BYTES - 1); i = (offset >> 2) & (FLASH_FWB_WORDS - 1); /* Copy words into buffer */ for (; size > 0; size -= 4) { LM4_FLASH_FWB[i++] = *data32++; if (i == FLASH_FWB_WORDS) { rv = write_buffer(); if (rv != EC_SUCCESS) return rv; /* Advance to next page */ i = 0; LM4_FLASH_FMA += FLASH_FWB_BYTES; } } /* Handle final partial page, if any */ if (i > 0) return write_buffer(); return EC_SUCCESS; } int crec_flash_physical_erase(int offset, int size) { if (all_protected) return EC_ERROR_ACCESS_DENIED; LM4_FLASH_FCMISC = LM4_FLASH_FCRIS; /* Clear previous error status */ for (; size > 0; size -= CONFIG_FLASH_ERASE_SIZE, offset += CONFIG_FLASH_ERASE_SIZE) { int t; /* Do nothing if already erased */ if (crec_flash_is_erased(offset, CONFIG_FLASH_ERASE_SIZE)) continue; LM4_FLASH_FMA = offset; /* * Reload the watchdog timer, so that erasing many flash pages * doesn't cause a watchdog reset. May not need this now that * we're using msleep() below. */ watchdog_reload(); /* Start erase */ LM4_FLASH_FMC = 0xa4420002; /* Wait for erase to complete */ for (t = 0; LM4_FLASH_FMC & 0x02; t++) { if (t > ERASE_TIMEOUT_MS) return EC_ERROR_TIMEOUT; msleep(1); } /* Check for error conditions - erase failed, voltage error, * protection error */ if (LM4_FLASH_FCRIS & 0x0a01) return EC_ERROR_UNKNOWN; } return EC_SUCCESS; } int crec_flash_physical_get_protect(int bank) { return (LM4_FLASH_FMPPE[F_BANK(bank)] & F_BIT(bank)) ? 0 : 1; } uint32_t crec_flash_physical_get_protect_flags(void) { uint32_t flags = 0; /* Read all-protected state from our shadow copy */ if (all_protected) flags |= EC_FLASH_PROTECT_ALL_NOW; /* Check if blocks were stuck locked at pre-init */ if (stuck_locked) flags |= EC_FLASH_PROTECT_ERROR_STUCK; return flags; } int crec_flash_physical_protect_now(int all) { if (all) { /* Protect the entire flash */ all_protected = 1; protect_banks(0, CONFIG_FLASH_SIZE_BYTES / CONFIG_FLASH_BANK_SIZE); } else /* Protect the WP region (read-only section and pstate) */ protect_banks(WP_BANK_OFFSET, WP_BANK_COUNT); return EC_SUCCESS; } uint32_t crec_flash_physical_get_valid_flags(void) { return EC_FLASH_PROTECT_RO_AT_BOOT | EC_FLASH_PROTECT_RO_NOW | EC_FLASH_PROTECT_ALL_NOW; } uint32_t crec_flash_physical_get_writable_flags(uint32_t cur_flags) { uint32_t ret = 0; /* If RO protection isn't enabled, its at-boot state can be changed. */ if (!(cur_flags & EC_FLASH_PROTECT_RO_NOW)) ret |= EC_FLASH_PROTECT_RO_AT_BOOT; /* * If entire flash isn't protected at this boot, it can be enabled if * the WP GPIO is asserted. */ if (!(cur_flags & EC_FLASH_PROTECT_ALL_NOW) && (cur_flags & EC_FLASH_PROTECT_GPIO_ASSERTED)) ret |= EC_FLASH_PROTECT_ALL_NOW; return ret; } /*****************************************************************************/ /* High-level APIs */ int crec_flash_pre_init(void) { uint32_t reset_flags = system_get_reset_flags(); uint32_t prot_flags = crec_flash_get_protect(); uint32_t unwanted_prot_flags = EC_FLASH_PROTECT_ALL_NOW | EC_FLASH_PROTECT_ERROR_INCONSISTENT; /* * If we have already jumped between images, an earlier image could * have applied write protection. Nothing additional needs to be done. */ if (reset_flags & EC_RESET_FLAG_SYSJUMP) return EC_SUCCESS; if (prot_flags & EC_FLASH_PROTECT_GPIO_ASSERTED) { /* * Write protect is asserted. If we want RO flash protected, * protect it now. */ if ((prot_flags & EC_FLASH_PROTECT_RO_AT_BOOT) && !(prot_flags & EC_FLASH_PROTECT_RO_NOW)) { int rv = crec_flash_set_protect(EC_FLASH_PROTECT_RO_NOW, EC_FLASH_PROTECT_RO_NOW); if (rv) return rv; /* Re-read flags */ prot_flags = crec_flash_get_protect(); } /* Update all-now flag if all flash is protected */ if (prot_flags & EC_FLASH_PROTECT_ALL_NOW) all_protected = 1; } else { /* Don't want RO flash protected */ unwanted_prot_flags |= EC_FLASH_PROTECT_RO_NOW; } /* If there are no unwanted flags, done */ if (!(prot_flags & unwanted_prot_flags)) return EC_SUCCESS; /* * If the last reboot was a power-on reset, it should have cleared * write-protect. If it didn't, then the flash write protect registers * have been permanently committed and we can't fix that. */ if (reset_flags & EC_RESET_FLAG_POWER_ON) { stuck_locked = 1; return EC_ERROR_ACCESS_DENIED; } /* Otherwise, do a hard boot to clear the flash protection registers */ system_reset(SYSTEM_RESET_HARD | SYSTEM_RESET_PRESERVE_FLAGS); /* That doesn't return, so if we're still here that's an error */ return EC_ERROR_UNKNOWN; }