/* 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 "common.h" #include "console.h" #include "flash.h" #include "gpio.h" #include "host_command.h" #include "shared_mem.h" #include "system.h" #include "util.h" #include "vboot_hash.h" /* * Contents of erased flash, as a 32-bit value. Most platforms erase flash * bits to 1. */ #ifndef CONFIG_FLASH_ERASED_VALUE32 #define CONFIG_FLASH_ERASED_VALUE32 (-1U) #endif #ifdef CONFIG_FLASH_PSTATE /* * If flash isn't mapped to the EC's address space, it's probably SPI, and * should be using SPI write protect, not PSTATE. */ #if !defined(CONFIG_INTERNAL_STORAGE) || !defined(CONFIG_MAPPED_STORAGE) #error "PSTATE should only be used with internal mem-mapped flash." #endif #ifdef CONFIG_FLASH_PSTATE_BANK /* Persistent protection state - emulates a SPI status register for flashrom */ /* NOTE: It's not expected that RO and RW will support * differing PSTATE versions. */ #define PERSIST_STATE_VERSION 3 /* Expected persist_state.version */ #define SERIALNO_MAX 30 /* Flags for persist_state.flags */ /* Protect persist state and RO firmware at boot */ #define PERSIST_FLAG_PROTECT_RO 0x02 #define PSTATE_VALID_FLAGS (1 << 0) #define PSTATE_VALID_SERIALNO (1 << 1) struct persist_state { uint8_t version; /* Version of this struct */ uint8_t flags; /* Lock flags (PERSIST_FLAG_*) */ uint8_t valid_fields; /* Flags for valid data. */ uint8_t reserved; /* Reserved; set 0 */ uint8_t serialno[SERIALNO_MAX]; /* Serial number. */ }; #else /* !CONFIG_FLASH_PSTATE_BANK */ /* * Flags for write protect state depend on the erased value of flash. The * locked value must be the same as the unlocked value with one or more bits * transitioned away from the erased state. That way, it is possible to * rewrite the data in-place to set the lock. * * STM32F0x can only write 0x0000 to a non-erased half-word, which means * PSTATE_MAGIC_LOCKED isn't quite as pretty. That's ok; the only thing * we actually need to detect is PSTATE_MAGIC_UNLOCKED, since that's the * only value we'll ever alter, and the only value which causes us not to * lock the flash at boot. */ #if (CONFIG_FLASH_ERASED_VALUE32 == -1U) #define PSTATE_MAGIC_UNLOCKED 0x4f4e5057 /* "WPNO" */ #define PSTATE_MAGIC_LOCKED 0x00000000 /* "" */ #elif (CONFIG_FLASH_ERASED_VALUE32 == 0) #define PSTATE_MAGIC_UNLOCKED 0x4f4e5057 /* "WPNO" */ #define PSTATE_MAGIC_LOCKED 0x5f5f5057 /* "WP__" */ #else /* What kind of wacky flash doesn't erase all bits to 1 or 0? */ #error "PSTATE needs magic values for this flash architecture." #endif /* * Rewriting the write protect flag in place currently requires a minimum write * size <= the size of the flag value. * * We could work around this on chips with larger minimum write size by reading * the write block containing the flag into RAM, changing it to the locked * value, and then rewriting that block. But we should only pay for that * complexity when we run across another chip which needs it. */ #if (CONFIG_FLASH_WRITE_SIZE > 4) #error "Non-bank-based PSTATE requires flash write size <= 32 bits." #endif const uint32_t pstate_data __attribute__((section(".rodata.pstate"))) = PSTATE_MAGIC_UNLOCKED; #endif /* !CONFIG_FLASH_PSTATE_BANK */ #endif /* CONFIG_FLASH_PSTATE */ int flash_range_ok(int offset, int size_req, int align) { if (offset < 0 || size_req < 0 || offset + size_req > CONFIG_FLASH_SIZE || (offset | size_req) & (align - 1)) return 0; /* Invalid range */ return 1; } #ifdef CONFIG_MAPPED_STORAGE /** * Get the physical memory address of a flash offset * * This is used for direct flash access. We assume that the flash is * contiguous from this start address through to the end of the usable * flash. * * @param offset Flash offset to get address of * @param dataptrp Returns pointer to memory address of flash offset * @return pointer to flash memory offset, if ok, else NULL */ static const char *flash_physical_dataptr(int offset) { return (char *)((uintptr_t)CONFIG_MAPPED_STORAGE_BASE + offset); } int flash_dataptr(int offset, int size_req, int align, const char **ptrp) { if (!flash_range_ok(offset, size_req, align)) return -1; /* Invalid range */ if (ptrp) *ptrp = flash_physical_dataptr(offset); return CONFIG_FLASH_SIZE - offset; } #endif #ifdef CONFIG_FLASH_PSTATE #ifdef CONFIG_FLASH_PSTATE_BANK /** * Read and return persistent state flags (EC_FLASH_PROTECT_*) */ static uint32_t flash_read_pstate(void) { const struct persist_state *pstate = (const struct persist_state *) flash_physical_dataptr(CONFIG_FW_PSTATE_OFF); if ((pstate->version == PERSIST_STATE_VERSION) && (pstate->valid_fields & PSTATE_VALID_FLAGS) && (pstate->flags & PERSIST_FLAG_PROTECT_RO)) { /* Lock flag is known to be set */ return EC_FLASH_PROTECT_RO_AT_BOOT; } else { #ifdef CONFIG_WP_ALWAYS return PERSIST_FLAG_PROTECT_RO; #else return 0; #endif } } /** * Read and return persistent serial number. */ static const char *flash_read_pstate_serial(void) { const struct persist_state *pstate = (const struct persist_state *) flash_physical_dataptr(CONFIG_FW_PSTATE_OFF); if ((pstate->version == PERSIST_STATE_VERSION) && (pstate->valid_fields & PSTATE_VALID_SERIALNO)) { return (const char *)(pstate->serialno); } return 0; } /** * Write persistent state after erasing. * * @param pstate New data to set in pstate. NOT memory mapped * old pstate as it will be erased. * @return EC_SUCCESS, or nonzero if error. */ static int flash_write_pstate_data(struct persist_state *newpstate) { int rv; /* Erase pstate */ rv = flash_physical_erase(CONFIG_FW_PSTATE_OFF, CONFIG_FW_PSTATE_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. */ /* Write the updated pstate */ return flash_physical_write(CONFIG_FW_PSTATE_OFF, sizeof(*newpstate), (const char *)newpstate); } /** * Validate and Init persistent state datastructure. * * @param pstate A pstate data structure. Will be valid at complete. * @return EC_SUCCESS, or nonzero if error. */ static int validate_pstate_struct(struct persist_state *pstate) { if (pstate->version != PERSIST_STATE_VERSION) { memset(pstate, 0, sizeof(*pstate)); pstate->version = PERSIST_STATE_VERSION; } return EC_SUCCESS; } /** * Write persistent state from pstate, erasing if necessary. * * @param flags New flash write protect flags to set in pstate. * @return EC_SUCCESS, or nonzero if error. */ static int flash_write_pstate(uint32_t flags) { struct persist_state newpstate; const struct persist_state *pstate = (const struct persist_state *) flash_physical_dataptr(CONFIG_FW_PSTATE_OFF); /* Only check the flags we write to pstate */ flags &= EC_FLASH_PROTECT_RO_AT_BOOT; /* Check if pstate has actually changed */ if (flags == flash_read_pstate()) return EC_SUCCESS; /* Cache the old copy for read/modify/write. */ memcpy(&newpstate, pstate, sizeof(newpstate)); validate_pstate_struct(&newpstate); if (flags & EC_FLASH_PROTECT_RO_AT_BOOT) newpstate.flags |= PERSIST_FLAG_PROTECT_RO; else newpstate.flags &= ~PERSIST_FLAG_PROTECT_RO; newpstate.valid_fields |= PSTATE_VALID_FLAGS; return flash_write_pstate_data(&newpstate); } /** * Write persistent serial number to pstate, erasing if necessary. * * @param serialno New iascii serial number to set in pstate. * @return EC_SUCCESS, or nonzero if error. */ static int flash_write_pstate_serial(const char *serialno) { int i; struct persist_state newpstate; const struct persist_state *pstate = (const struct persist_state *) flash_physical_dataptr(CONFIG_FW_PSTATE_OFF); /* Check that this is OK */ if (!serialno) return EC_ERROR_INVAL; /* Cache the old copy for read/modify/write. */ memcpy(&newpstate, pstate, sizeof(newpstate)); validate_pstate_struct(&newpstate); /* Copy in serialno. */ for (i = 0; i < SERIALNO_MAX - 1; i++) { newpstate.serialno[i] = serialno[i]; if (serialno[i] == 0) break; } for (; i < SERIALNO_MAX; i++) newpstate.serialno[i] = 0; newpstate.valid_fields |= PSTATE_VALID_SERIALNO; return flash_write_pstate_data(&newpstate); } #else /* !CONFIG_FLASH_PSTATE_BANK */ /** * Return the address of the pstate data in EC-RO. */ static const uintptr_t get_pstate_addr(void) { uintptr_t addr = (uintptr_t)&pstate_data; /* Always use the pstate data in RO, even if we're RW */ if (system_get_image_copy() == SYSTEM_IMAGE_RW) addr += CONFIG_RO_MEM_OFF - CONFIG_RW_MEM_OFF; return addr; } /** * Read and return persistent state flags (EC_FLASH_PROTECT_*) */ static uint32_t flash_read_pstate(void) { /* Check for the unlocked magic value */ if (*(const uint32_t *)get_pstate_addr() == PSTATE_MAGIC_UNLOCKED) return 0; /* Anything else is locked */ return EC_FLASH_PROTECT_RO_AT_BOOT; } /** * Write persistent state from pstate, erasing if necessary. * * @param flags New flash write protect flags to set in pstate. * @return EC_SUCCESS, or nonzero if error. */ static int flash_write_pstate(uint32_t flags) { const uint32_t new_pstate = PSTATE_MAGIC_LOCKED; /* Only check the flags we write to pstate */ flags &= EC_FLASH_PROTECT_RO_AT_BOOT; /* Check if pstate has actually changed */ if (flags == flash_read_pstate()) return EC_SUCCESS; /* We can only set the protect flag, not clear it */ if (!(flags & EC_FLASH_PROTECT_RO_AT_BOOT)) return EC_ERROR_ACCESS_DENIED; /* * Write a new pstate. We can overwrite the existing value, because * we're only moving bits from the erased state to the unerased state. */ return flash_physical_write(get_pstate_addr() - CONFIG_PROGRAM_MEMORY_BASE, sizeof(new_pstate), (const char *)&new_pstate); } #endif /* !CONFIG_FLASH_PSTATE_BANK */ #endif /* CONFIG_FLASH_PSTATE */ int flash_is_erased(uint32_t offset, int size) { const uint32_t *ptr; #ifdef CONFIG_MAPPED_STORAGE /* Use pointer directly to flash */ if (flash_dataptr(offset, size, sizeof(uint32_t), (const char **)&ptr) < 0) return 0; flash_lock_mapped_storage(1); for (size /= sizeof(uint32_t); size > 0; size--, ptr++) if (*ptr != CONFIG_FLASH_ERASED_VALUE32) { flash_lock_mapped_storage(0); return 0; } flash_lock_mapped_storage(0); #else /* Read flash a chunk at a time */ uint32_t buf[8]; int bsize; while (size) { bsize = MIN(size, sizeof(buf)); if (flash_read(offset, bsize, (char *)buf)) return 0; size -= bsize; offset += bsize; ptr = buf; for (bsize /= sizeof(uint32_t); bsize > 0; bsize--, ptr++) if (*ptr != CONFIG_FLASH_ERASED_VALUE32) return 0; } #endif return 1; } int flash_read(int offset, int size, char *data) { #ifdef CONFIG_MAPPED_STORAGE const char *src; if (flash_dataptr(offset, size, 1, &src) < 0) return EC_ERROR_INVAL; flash_lock_mapped_storage(1); memcpy(data, src, size); flash_lock_mapped_storage(0); return EC_SUCCESS; #else return flash_physical_read(offset, size, data); #endif } int flash_write(int offset, int size, const char *data) { if (!flash_range_ok(offset, size, CONFIG_FLASH_WRITE_SIZE)) return EC_ERROR_INVAL; /* Invalid range */ #ifdef CONFIG_VBOOT_HASH /* * Abort hash calculations when flashrom flash updates * are in progress.Otherwise invalidate the pre-computed hash, * since it's likely to change after flash write */ if (vboot_hash_in_progress()) vboot_hash_abort(); else vboot_hash_invalidate(offset, size); #endif return flash_physical_write(offset, size, data); } int flash_erase(int offset, int size) { if (!flash_range_ok(offset, size, CONFIG_FLASH_ERASE_SIZE)) return EC_ERROR_INVAL; /* Invalid range */ #ifdef CONFIG_VBOOT_HASH /* * Abort hash calculations when flashrom flash updates * are in progress.Otherwise invalidate the pre-computed hash, * since it's likely to be wrong after erase. */ if (vboot_hash_in_progress()) vboot_hash_abort(); else vboot_hash_invalidate(offset, size); #endif return flash_physical_erase(offset, size); } const char *flash_read_serial(void) { #if defined(CONFIG_FLASH_PSTATE) && defined(CONFIG_FLASH_PSTATE_BANK) return flash_read_pstate_serial(); #else return 0; #endif } int flash_write_serial(const char *serialno) { #if defined(CONFIG_FLASH_PSTATE) && defined(CONFIG_FLASH_PSTATE_BANK) return flash_write_pstate_serial(serialno); #else return EC_ERROR_UNIMPLEMENTED; #endif } int flash_protect_at_boot(uint32_t new_flags) { #ifdef CONFIG_FLASH_PSTATE uint32_t new_pstate_flags = new_flags & EC_FLASH_PROTECT_RO_AT_BOOT; /* Read the current persist state from flash */ if (flash_read_pstate() != new_pstate_flags) { /* Need to update pstate */ int rv; #ifdef CONFIG_FLASH_PSTATE_BANK /* Fail if write protect block is already locked */ if (flash_physical_get_protect(PSTATE_BANK)) return EC_ERROR_ACCESS_DENIED; #endif /* Write the desired flags */ rv = flash_write_pstate(new_pstate_flags); if (rv) return rv; } #ifdef CONFIG_FLASH_PROTECT_NEXT_BOOT /* * Try updating at-boot protection state, if on a platform where write * protection only changes after a reboot. Otherwise we wouldn't * update it until after the next reboot, and we'd need to reboot * again. Ignore errors, because the protection registers might * already be locked this boot, and we'll still apply the correct state * again on the next boot. * * This assumes PSTATE immediately follows RO, which it does on * all STM32 platforms (which are the only ones with this config). */ flash_physical_protect_at_boot(new_flags); #endif return EC_SUCCESS; #else return flash_physical_protect_at_boot(new_flags); #endif } uint32_t flash_get_protect(void) { uint32_t flags = 0; int i; /* Region protection status */ int not_protected[FLASH_REGION_COUNT] = {0}; /* Flags that must be set to set ALL_NOW flag. */ const uint32_t all_flags = EC_FLASH_PROTECT_RO_NOW | EC_FLASH_PROTECT_RW_NOW; /* Read write protect GPIO */ #ifdef CONFIG_WP_ALWAYS flags |= EC_FLASH_PROTECT_GPIO_ASSERTED; #elif defined(CONFIG_WP_ACTIVE_HIGH) if (gpio_get_level(GPIO_WP)) flags |= EC_FLASH_PROTECT_GPIO_ASSERTED; #else if (!gpio_get_level(GPIO_WP_L)) flags |= EC_FLASH_PROTECT_GPIO_ASSERTED; #endif #ifdef CONFIG_FLASH_PSTATE /* Read persistent state of RO-at-boot flag */ flags |= flash_read_pstate(); #endif /* Scan flash protection */ for (i = 0; i < PHYSICAL_BANKS; i++) { int is_ro = (i >= WP_BANK_OFFSET && i < WP_BANK_OFFSET + WP_BANK_COUNT); enum flash_region region = is_ro ? FLASH_REGION_RO : FLASH_REGION_RW; int bank_flag = is_ro ? EC_FLASH_PROTECT_RO_NOW : EC_FLASH_PROTECT_RW_NOW; if (flash_physical_get_protect(i)) { /* At least one bank in the region is protected */ flags |= bank_flag; if (not_protected[region]) flags |= EC_FLASH_PROTECT_ERROR_INCONSISTENT; } else { /* At least one bank in the region is NOT protected */ not_protected[region] = 1; if (flags & bank_flag) flags |= EC_FLASH_PROTECT_ERROR_INCONSISTENT; } } if ((flags & all_flags) == all_flags) flags |= EC_FLASH_PROTECT_ALL_NOW; /* * If the RW banks are protected but the RO banks aren't, that's * inconsistent. * * Note that we check this before adding in the physical flags below, * since some chips can also protect ALL_NOW for the current boot by * locking up the flash program-erase registers. */ if ((flags & all_flags) && !(flags & EC_FLASH_PROTECT_RO_NOW)) flags |= EC_FLASH_PROTECT_ERROR_INCONSISTENT; #ifndef CONFIG_FLASH_PROTECT_RW /* RW flag was used for intermediate computations, clear it now. */ flags &= ~EC_FLASH_PROTECT_RW_NOW; #endif /* Add in flags from physical layer */ return flags | flash_physical_get_protect_flags(); } int flash_set_protect(uint32_t mask, uint32_t flags) { int retval = EC_SUCCESS; int rv; int old_flags_at_boot = flash_get_protect() & (EC_FLASH_PROTECT_RO_AT_BOOT | EC_FLASH_PROTECT_RW_AT_BOOT | EC_FLASH_PROTECT_ALL_AT_BOOT); int new_flags_at_boot = old_flags_at_boot; /* Sanitize input flags */ flags = flags & mask; /* * Process flags we can set. Track the most recent error, but process * all flags before returning. */ /* * AT_BOOT flags are trickier than NOW flags, as they can be set * when HW write protection is disabled and can be unset without * a reboot. * * If we are only setting/clearing RO_AT_BOOT, things are simple. * Setting ALL_AT_BOOT is processed only if HW write protection is * enabled and RO_AT_BOOT is set, so it's also simple. * * The most tricky one is when we want to clear ALL_AT_BOOT. We need * to determine whether to clear protection for the entire flash or * leave RO protected. There are two cases that we want to keep RO * protected: * 1. RO_AT_BOOT was already set before flash_set_protect() is * called. * 2. RO_AT_BOOT was not set, but it's requested to be set by * the caller of flash_set_protect(). */ new_flags_at_boot &= ~(mask & EC_FLASH_PROTECT_RO_AT_BOOT); new_flags_at_boot |= flags & EC_FLASH_PROTECT_RO_AT_BOOT; /* Removing ALL must also remove RW */ if ((mask & EC_FLASH_PROTECT_ALL_AT_BOOT) && !(flags & EC_FLASH_PROTECT_ALL_AT_BOOT)) { new_flags_at_boot &= ~EC_FLASH_PROTECT_ALL_AT_BOOT; #ifdef CONFIG_FLASH_PROTECT_RW new_flags_at_boot &= ~EC_FLASH_PROTECT_RW_AT_BOOT; #endif } #ifdef CONFIG_FLASH_PROTECT_RW /* Removing RW must also remove ALL (otherwise nothing will happen). */ if ((mask & EC_FLASH_PROTECT_RW_AT_BOOT) && !(flags & EC_FLASH_PROTECT_RW_AT_BOOT)) { new_flags_at_boot &= ~EC_FLASH_PROTECT_ALL_AT_BOOT; new_flags_at_boot &= ~EC_FLASH_PROTECT_RW_AT_BOOT; } #endif if (new_flags_at_boot != old_flags_at_boot) { rv = flash_protect_at_boot(new_flags_at_boot); if (rv) retval = rv; old_flags_at_boot = new_flags_at_boot; } /* * All subsequent flags only work if write protect is enabled (that is, * hardware WP flag) *and* RO is protected at boot (software WP flag). */ if ((~flash_get_protect()) & (EC_FLASH_PROTECT_GPIO_ASSERTED | EC_FLASH_PROTECT_RO_AT_BOOT)) return retval; /* * The case where ALL/RW_AT_BOOT is unset is already covered above, * so we do not need to mask it out. */ new_flags_at_boot |= flags & EC_FLASH_PROTECT_ALL_AT_BOOT; #ifdef CONFIG_FLASH_PROTECT_RW new_flags_at_boot |= flags & EC_FLASH_PROTECT_RW_AT_BOOT; #endif if (new_flags_at_boot != old_flags_at_boot) { rv = flash_protect_at_boot(new_flags_at_boot); if (rv) retval = rv; } if (flags & EC_FLASH_PROTECT_RO_NOW) { rv = flash_physical_protect_now(0); if (rv) retval = rv; } if (flags & EC_FLASH_PROTECT_ALL_NOW) { rv = flash_physical_protect_now(1); if (rv) retval = rv; } return retval; } /*****************************************************************************/ /* Console commands */ static int command_flash_info(int argc, char **argv) { int i; ccprintf("Usable: %4d KB\n", CONFIG_FLASH_SIZE / 1024); ccprintf("Write: %4d B (ideal %d B)\n", CONFIG_FLASH_WRITE_SIZE, CONFIG_FLASH_WRITE_IDEAL_SIZE); ccprintf("Erase: %4d B (to %d-bits)\n", CONFIG_FLASH_ERASE_SIZE, CONFIG_FLASH_ERASED_VALUE32 ? 1 : 0); ccprintf("Protect: %4d B\n", CONFIG_FLASH_BANK_SIZE); i = flash_get_protect(); ccprintf("Flags: "); if (i & EC_FLASH_PROTECT_GPIO_ASSERTED) ccputs(" wp_gpio_asserted"); if (i & EC_FLASH_PROTECT_RO_AT_BOOT) ccputs(" ro_at_boot"); if (i & EC_FLASH_PROTECT_ALL_AT_BOOT) ccputs(" all_at_boot"); if (i & EC_FLASH_PROTECT_RO_NOW) ccputs(" ro_now"); if (i & EC_FLASH_PROTECT_ALL_NOW) ccputs(" all_now"); #ifdef CONFIG_FLASH_PROTECT_RW if (i & EC_FLASH_PROTECT_RW_AT_BOOT) ccputs(" rw_at_boot"); if (i & EC_FLASH_PROTECT_RW_NOW) ccputs(" rw_now"); #endif if (i & EC_FLASH_PROTECT_ERROR_STUCK) ccputs(" STUCK"); if (i & EC_FLASH_PROTECT_ERROR_INCONSISTENT) ccputs(" INCONSISTENT"); ccputs("\n"); ccputs("Protected now:"); for (i = 0; i < CONFIG_FLASH_SIZE / CONFIG_FLASH_BANK_SIZE; i++) { if (!(i & 31)) ccputs("\n "); else if (!(i & 7)) ccputs(" "); ccputs(flash_physical_get_protect(i) ? "Y" : "."); } ccputs("\n"); return EC_SUCCESS; } DECLARE_SAFE_CONSOLE_COMMAND(flashinfo, command_flash_info, NULL, "Print flash info"); #ifdef CONFIG_CMD_FLASH static int command_flash_erase(int argc, char **argv) { int offset = -1; int size = CONFIG_FLASH_ERASE_SIZE; int rv; if (flash_get_protect() & EC_FLASH_PROTECT_ALL_NOW) return EC_ERROR_ACCESS_DENIED; rv = parse_offset_size(argc, argv, 1, &offset, &size); if (rv) return rv; ccprintf("Erasing %d bytes at 0x%x...\n", size, offset); return flash_erase(offset, size); } DECLARE_CONSOLE_COMMAND(flasherase, command_flash_erase, "offset [size]", "Erase flash"); static int command_flash_write(int argc, char **argv) { int offset = -1; int size = CONFIG_FLASH_ERASE_SIZE; int rv; char *data; int i; if (flash_get_protect() & EC_FLASH_PROTECT_ALL_NOW) return EC_ERROR_ACCESS_DENIED; rv = parse_offset_size(argc, argv, 1, &offset, &size); if (rv) return rv; if (size > shared_mem_size()) size = shared_mem_size(); /* Acquire the shared memory buffer */ rv = shared_mem_acquire(size, &data); if (rv) { ccputs("Can't get shared mem\n"); return rv; } /* Fill the data buffer with a pattern */ for (i = 0; i < size; i++) data[i] = i; ccprintf("Writing %d bytes to 0x%x...\n", size, offset); rv = flash_write(offset, size, data); /* Free the buffer */ shared_mem_release(data); return rv; } DECLARE_CONSOLE_COMMAND(flashwrite, command_flash_write, "offset [size]", "Write pattern to flash"); static int command_flash_read(int argc, char **argv) { int offset = -1; int size = 256; int rv; char *data; int i; rv = parse_offset_size(argc, argv, 1, &offset, &size); if (rv) return rv; if (size > shared_mem_size()) size = shared_mem_size(); /* Acquire the shared memory buffer */ rv = shared_mem_acquire(size, &data); if (rv) { ccputs("Can't get shared mem\n"); return rv; } /* Read the data */ if (flash_read(offset, size, data)) { shared_mem_release(data); return EC_ERROR_INVAL; } /* Dump it */ for (i = 0; i < size; i++) { if ((offset + i) % 16) { ccprintf(" %02x", data[i]); } else { ccprintf("\n%08x: %02x", offset + i, data[i]); cflush(); } } ccprintf("\n"); /* Free the buffer */ shared_mem_release(data); return EC_SUCCESS; } DECLARE_CONSOLE_COMMAND(flashread, command_flash_read, "offset [size]", "Read flash"); #endif static int command_flash_wp(int argc, char **argv) { int val; if (argc < 2) return EC_ERROR_PARAM_COUNT; if (!strcasecmp(argv[1], "now")) return flash_set_protect(EC_FLASH_PROTECT_ALL_NOW, -1); if (!strcasecmp(argv[1], "all")) return flash_set_protect(EC_FLASH_PROTECT_ALL_AT_BOOT, -1); if (!strcasecmp(argv[1], "noall")) return flash_set_protect(EC_FLASH_PROTECT_ALL_AT_BOOT, 0); #ifdef CONFIG_FLASH_PROTECT_RW if (!strcasecmp(argv[1], "rw")) return flash_set_protect(EC_FLASH_PROTECT_RW_AT_BOOT, -1); if (!strcasecmp(argv[1], "norw")) return flash_set_protect(EC_FLASH_PROTECT_RW_AT_BOOT, 0); #endif /* Do this last, since anything starting with 'n' means "no" */ if (parse_bool(argv[1], &val)) return flash_set_protect(EC_FLASH_PROTECT_RO_AT_BOOT, val ? -1 : 0); return EC_ERROR_PARAM1; } DECLARE_CONSOLE_COMMAND(flashwp, command_flash_wp, " | now | all | noall" #ifdef CONFIG_FLASH_PROTECT_RW " | rw | norw" #endif , "Modify flash write protect"); /*****************************************************************************/ /* Host commands */ /* * All internal EC code assumes that offsets are provided relative to * physical address zero of storage. In some cases, the region of storage * belonging to the EC is not physical address zero - a non-zero fmap_base * indicates so. Since fmap_base is not yet handled correctly by external * code, we must perform the adjustment in our host command handlers - * adjust all offsets so they are relative to the beginning of the storage * region belonging to the EC. TODO(crbug.com/529365): Handle fmap_base * correctly in flashrom, dump_fmap, etc. and remove EC_FLASH_REGION_START. */ #define EC_FLASH_REGION_START MIN(CONFIG_EC_PROTECTED_STORAGE_OFF, \ CONFIG_EC_WRITABLE_STORAGE_OFF) static int flash_command_get_info(struct host_cmd_handler_args *args) { struct ec_response_flash_info_1 *r = args->response; r->flash_size = CONFIG_FLASH_SIZE - EC_FLASH_REGION_START; r->write_block_size = CONFIG_FLASH_WRITE_SIZE; r->erase_block_size = CONFIG_FLASH_ERASE_SIZE; r->protect_block_size = CONFIG_FLASH_BANK_SIZE; if (args->version == 0) { /* Only version 0 fields returned */ args->response_size = sizeof(struct ec_response_flash_info); } else { /* Fill in full version 1 struct */ /* * Compute the ideal amount of data for the host to send us, * based on the maximum response size and the ideal write size. */ r->write_ideal_size = (args->response_max - sizeof(struct ec_params_flash_write)) & ~(CONFIG_FLASH_WRITE_IDEAL_SIZE - 1); /* * If we can't get at least one ideal block, then just want * as high a multiple of the minimum write size as possible. */ if (!r->write_ideal_size) r->write_ideal_size = (args->response_max - sizeof(struct ec_params_flash_write)) & ~(CONFIG_FLASH_WRITE_SIZE - 1); r->flags = 0; #if (CONFIG_FLASH_ERASED_VALUE32 == 0) r->flags |= EC_FLASH_INFO_ERASE_TO_0; #endif args->response_size = sizeof(*r); } return EC_RES_SUCCESS; } DECLARE_HOST_COMMAND(EC_CMD_FLASH_INFO, flash_command_get_info, EC_VER_MASK(0) | EC_VER_MASK(1)); static int flash_command_read(struct host_cmd_handler_args *args) { const struct ec_params_flash_read *p = args->params; uint32_t offset = p->offset + EC_FLASH_REGION_START; if (p->size > args->response_max) return EC_RES_OVERFLOW; if (flash_read(offset, p->size, args->response)) return EC_RES_ERROR; args->response_size = p->size; return EC_RES_SUCCESS; } DECLARE_HOST_COMMAND(EC_CMD_FLASH_READ, flash_command_read, EC_VER_MASK(0)); /** * Flash write command * * Version 0 and 1 are equivalent from the EC-side; the only difference is * that the host can only send 64 bytes of data at a time in version 0. */ static int flash_command_write(struct host_cmd_handler_args *args) { const struct ec_params_flash_write *p = args->params; uint32_t offset = p->offset + EC_FLASH_REGION_START; if (flash_get_protect() & EC_FLASH_PROTECT_ALL_NOW) return EC_RES_ACCESS_DENIED; if (p->size + sizeof(*p) > args->params_size) return EC_RES_INVALID_PARAM; if (system_unsafe_to_overwrite(offset, p->size)) return EC_RES_ACCESS_DENIED; if (flash_write(offset, p->size, (const uint8_t *)(p + 1))) return EC_RES_ERROR; return EC_RES_SUCCESS; } DECLARE_HOST_COMMAND(EC_CMD_FLASH_WRITE, flash_command_write, EC_VER_MASK(0) | EC_VER_MASK(EC_VER_FLASH_WRITE)); /* * Make sure our image sizes are a multiple of flash block erase size so that * the host can erase the entire image. */ BUILD_ASSERT(CONFIG_RO_SIZE % CONFIG_FLASH_ERASE_SIZE == 0); BUILD_ASSERT(CONFIG_RW_SIZE % CONFIG_FLASH_ERASE_SIZE == 0); static int flash_command_erase(struct host_cmd_handler_args *args) { const struct ec_params_flash_erase *p = args->params; uint32_t offset = p->offset + EC_FLASH_REGION_START; if (flash_get_protect() & EC_FLASH_PROTECT_ALL_NOW) return EC_RES_ACCESS_DENIED; if (system_unsafe_to_overwrite(offset, p->size)) return EC_RES_ACCESS_DENIED; /* Indicate that we might be a while */ #if defined(HAS_TASK_HOSTCMD) && defined(CONFIG_HOST_COMMAND_STATUS) args->result = EC_RES_IN_PROGRESS; host_send_response(args); #endif if (flash_erase(offset, p->size)) return EC_RES_ERROR; return EC_RES_SUCCESS; } DECLARE_HOST_COMMAND(EC_CMD_FLASH_ERASE, flash_command_erase, EC_VER_MASK(0)); static int flash_command_protect(struct host_cmd_handler_args *args) { const struct ec_params_flash_protect *p = args->params; struct ec_response_flash_protect *r = args->response; /* * Handle requesting new flags. Note that we ignore the return code * from flash_set_protect(), since errors will be visible to the caller * via the flags in the response. (If we returned error, the caller * wouldn't get the response.) */ if (p->mask) flash_set_protect(p->mask, p->flags); /* * Retrieve the current flags. The caller can use this to determine * which of the requested flags could be set. This is cleaner than * simply returning error, because it provides information to the * caller about the actual result. */ r->flags = flash_get_protect(); /* Indicate which flags are valid on this platform */ r->valid_flags = EC_FLASH_PROTECT_GPIO_ASSERTED | EC_FLASH_PROTECT_ERROR_STUCK | EC_FLASH_PROTECT_ERROR_INCONSISTENT | flash_physical_get_valid_flags(); r->writable_flags = flash_physical_get_writable_flags(r->flags); args->response_size = sizeof(*r); return EC_RES_SUCCESS; } /* * TODO(crbug.com/239197) : Adding both versions to the version mask is a * temporary workaround for a problem in the cros_ec driver. Drop * EC_VER_MASK(0) once cros_ec driver can send the correct version. */ DECLARE_HOST_COMMAND(EC_CMD_FLASH_PROTECT, flash_command_protect, EC_VER_MASK(0) | EC_VER_MASK(1)); static int flash_command_region_info(struct host_cmd_handler_args *args) { const struct ec_params_flash_region_info *p = args->params; struct ec_response_flash_region_info *r = args->response; switch (p->region) { case EC_FLASH_REGION_RO: r->offset = CONFIG_EC_PROTECTED_STORAGE_OFF + CONFIG_RO_STORAGE_OFF - EC_FLASH_REGION_START; r->size = CONFIG_RO_SIZE; break; case EC_FLASH_REGION_RW: r->offset = CONFIG_EC_WRITABLE_STORAGE_OFF + CONFIG_RW_STORAGE_OFF - EC_FLASH_REGION_START; r->size = CONFIG_RW_SIZE; break; case EC_FLASH_REGION_WP_RO: r->offset = CONFIG_WP_STORAGE_OFF - EC_FLASH_REGION_START; r->size = CONFIG_WP_STORAGE_SIZE; break; default: return EC_RES_INVALID_PARAM; } args->response_size = sizeof(*r); return EC_RES_SUCCESS; } DECLARE_HOST_COMMAND(EC_CMD_FLASH_REGION_INFO, flash_command_region_info, EC_VER_MASK(EC_VER_FLASH_REGION_INFO));