/* 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 28

/* 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. */
};
/* written with flash_physical_write, need to respect alignment constraints */
#ifndef CHIP_FAMILY_STM32L /* STM32L1xx is somewhat lying to us */
BUILD_ASSERT(sizeof(struct persist_state) % CONFIG_FLASH_WRITE_SIZE == 0);
#endif

#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 */

#ifdef CONFIG_FLASH_MULTIPLE_REGION
int flash_bank_size(int bank)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(flash_bank_array); i++) {
		if (bank < flash_bank_array[i].count)
			return 1 << flash_bank_array[i].size_exp;
		bank -= flash_bank_array[i].count;
	}
	return -1;
}

int flash_bank_index(int offset)
{
	int bank_offset = 0, i;

	for (i = 0; i < ARRAY_SIZE(flash_bank_array); i++) {
		int all_sector_size = flash_bank_array[i].count <<
			flash_bank_array[i].size_exp;
		if (offset >= all_sector_size) {
			offset -= all_sector_size;
			bank_offset += flash_bank_array[i].count;
			continue;
		}
		if (offset & ((1 << flash_bank_array[i].size_exp) - 1))
			return -1;
		return bank_offset + (offset >> flash_bank_array[i].size_exp);
	}
	if (offset != 0)
		return -1;
	return bank_offset;
}

int flash_bank_count(int offset, int size)
{
	int begin = flash_bank_index(offset);
	int end = flash_bank_index(offset + size);

	if (begin == -1 || end == -1)
		return -1;
	return end - begin;
}
#endif  /* CONFIG_FLASH_MULTIPLE_REGION */

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)
{
#ifndef CONFIG_FLASH_MULTIPLE_REGION
	if (!flash_range_ok(offset, size, CONFIG_FLASH_ERASE_SIZE))
		return EC_ERROR_INVAL;  /* Invalid range */
#endif

#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};
#ifdef CONFIG_ROLLBACK
	/* 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 |
				   EC_FLASH_PROTECT_ROLLBACK_NOW;
#else
	const uint32_t all_flags = EC_FLASH_PROTECT_RO_NOW |
				   EC_FLASH_PROTECT_RW_NOW;
#endif

	/* 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;

#ifdef CONFIG_ROLLBACK
		if (i >= ROLLBACK_BANK_OFFSET &&
		    i < ROLLBACK_BANK_OFFSET + ROLLBACK_BANK_COUNT) {
			region = FLASH_REGION_ROLLBACK;
			bank_flag = EC_FLASH_PROTECT_ROLLBACK_NOW;
		}
#endif

		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 or ROLLBACK 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_ROLLBACK_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/ROLLBACK */
	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_ROLLBACK
		new_flags_at_boot &= ~EC_FLASH_PROTECT_ROLLBACK_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_RW_AT_BOOT;
		new_flags_at_boot &= ~EC_FLASH_PROTECT_ALL_AT_BOOT;
	}
#endif

#ifdef CONFIG_ROLLBACK
	if ((mask & EC_FLASH_PROTECT_ROLLBACK_AT_BOOT) &&
	    !(flags & EC_FLASH_PROTECT_ROLLBACK_AT_BOOT)) {
		new_flags_at_boot &= ~EC_FLASH_PROTECT_ROLLBACK_AT_BOOT;
		new_flags_at_boot &= ~EC_FLASH_PROTECT_ALL_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/ROLLBACK_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

#ifdef CONFIG_ROLLBACK
	new_flags_at_boot |= flags & EC_FLASH_PROTECT_ROLLBACK_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, flags;

	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);
#ifdef CONFIG_FLASH_MULTIPLE_REGION
	ccprintf("Regions:\n");
	for (i = 0; i < ARRAY_SIZE(flash_bank_array); i++) {
		ccprintf(" %d region%s:\n",
			 flash_bank_array[i].count,
			 (flash_bank_array[i].count == 1 ? "" : "s"));
		ccprintf("  Erase:   %4d B (to %d-bits)\n",
			 1 << flash_bank_array[i].erase_size_exp,
			 CONFIG_FLASH_ERASED_VALUE32 ? 1 : 0);
		ccprintf("  Size/Protect: %4d B\n",
			 1 << flash_bank_array[i].size_exp);
	}
#else
	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);
#endif
	flags = flash_get_protect();
	ccprintf("Flags:  ");
	if (flags & EC_FLASH_PROTECT_GPIO_ASSERTED)
		ccputs(" wp_gpio_asserted");
	if (flags & EC_FLASH_PROTECT_RO_AT_BOOT)
		ccputs(" ro_at_boot");
	if (flags & EC_FLASH_PROTECT_ALL_AT_BOOT)
		ccputs(" all_at_boot");
	if (flags & EC_FLASH_PROTECT_RO_NOW)
		ccputs(" ro_now");
	if (flags & EC_FLASH_PROTECT_ALL_NOW)
		ccputs(" all_now");
#ifdef CONFIG_FLASH_PROTECT_RW
	if (flags & EC_FLASH_PROTECT_RW_AT_BOOT)
		ccputs(" rw_at_boot");
	if (flags & EC_FLASH_PROTECT_RW_NOW)
		ccputs(" rw_now");
#endif
	if (flags & EC_FLASH_PROTECT_ERROR_STUCK)
		ccputs(" STUCK");
	if (flags & EC_FLASH_PROTECT_ERROR_INCONSISTENT)
		ccputs(" INCONSISTENT");
#ifdef CONFIG_ROLLBACK
	if (flags & EC_FLASH_PROTECT_ROLLBACK_AT_BOOT)
		ccputs(" rollback_at_boot");
	if (flags & EC_FLASH_PROTECT_ROLLBACK_NOW)
		ccputs(" rollback_now");
#endif
	ccputs("\n");

	ccputs("Protected now:");
	for (i = 0; i < PHYSICAL_BANKS; 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 = -1;
	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 = -1;
	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

#ifdef CONFIG_ROLLBACK
	if (!strcasecmp(argv[1], "rb"))
		return flash_set_protect(EC_FLASH_PROTECT_ROLLBACK_AT_BOOT, -1);

	if (!strcasecmp(argv[1], "norb"))
		return flash_set_protect(EC_FLASH_PROTECT_ROLLBACK_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,
			"<BOOLEAN> | now | all | noall"
#ifdef CONFIG_FLASH_PROTECT_RW
			" | rw | norw"
#endif
#ifdef CONFIG_ROLLBACK
			" | rb | norb"
#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)
{
	const struct ec_params_flash_info_2 *p_2 = args->params;
	struct ec_response_flash_info_2 *r_2 = args->response;
#ifdef CONFIG_FLASH_MULTIPLE_REGION
	int banks_size = ARRAY_SIZE(flash_bank_array);
	const struct ec_flash_bank *banks = flash_bank_array;
#else
	struct ec_response_flash_info_1 *r_1 = args->response;
#if CONFIG_FLASH_BANK_SIZE < CONFIG_FLASH_ERASE_SIZE
#error "Flash: Bank size expected bigger or equal to erase size."
#endif
	struct ec_flash_bank single_bank = {
		.count = CONFIG_FLASH_SIZE / CONFIG_FLASH_BANK_SIZE,
		.size_exp = __fls(CONFIG_FLASH_BANK_SIZE),
		.write_size_exp = __fls(CONFIG_FLASH_WRITE_SIZE),
		.erase_size_exp = __fls(CONFIG_FLASH_ERASE_SIZE),
		.protect_size_exp = __fls(CONFIG_FLASH_BANK_SIZE),
	};
	int banks_size = 1;
	const struct ec_flash_bank *banks = &single_bank;
#endif
	int banks_len;
	int ideal_size;

	/*
	 * Compute the ideal amount of data for the host to send us,
	 * based on the maximum response size and the ideal write size.
	 */
	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 (!ideal_size)
		ideal_size = (args->response_max -
				sizeof(struct ec_params_flash_write)) &
				~(CONFIG_FLASH_WRITE_SIZE - 1);


	if (args->version >= 2) {
		args->response_size = sizeof(struct ec_response_flash_info_2);
		r_2->flash_size = CONFIG_FLASH_SIZE - EC_FLASH_REGION_START;
#if (CONFIG_FLASH_ERASED_VALUE32 == 0)
		r_2->flags = EC_FLASH_INFO_ERASE_TO_0;
#else
		r_2->flags = 0;
#endif
#ifdef CONFIG_FLASH_SELECT_REQUIRED
		r_2->flags |= EC_FLASH_INFO_SELECT_REQUIRED;
#endif
		r_2->write_ideal_size = ideal_size;
		r_2->num_banks_total = banks_size;
		r_2->num_banks_desc = MIN(banks_size, p_2->num_banks_desc);
		banks_len = r_2->num_banks_desc * sizeof(struct ec_flash_bank);
		memcpy(r_2->banks, banks, banks_len);
		args->response_size += banks_len;
		return EC_RES_SUCCESS;
	}
#ifdef CONFIG_FLASH_MULTIPLE_REGION
	return EC_RES_INVALID_PARAM;
#else
	r_1->flash_size = CONFIG_FLASH_SIZE - EC_FLASH_REGION_START;
	r_1->flags = 0;
	r_1->write_block_size = CONFIG_FLASH_WRITE_SIZE;
	r_1->erase_block_size = CONFIG_FLASH_ERASE_SIZE;
	r_1->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 {
		args->response_size = sizeof(struct ec_response_flash_info_1);
		/* Fill in full version 1 struct */
		r_1->write_ideal_size = ideal_size;
#if (CONFIG_FLASH_ERASED_VALUE32 == 0)
		r_1->flags |= EC_FLASH_INFO_ERASE_TO_0;
#endif
#ifdef CONFIG_FLASH_SELECT_REQUIRED
		r_1->flags |= EC_FLASH_INFO_SELECT_REQUIRED;
#endif
	}
	return EC_RES_SUCCESS;
#endif  /* CONFIG_FLASH_MULTIPLE_REGION */
}
#ifdef CONFIG_FLASH_MULTIPLE_REGION
#define FLASH_INFO_VER EC_VER_MASK(2)
#else
#define FLASH_INFO_VER (EC_VER_MASK(0) | EC_VER_MASK(1) | EC_VER_MASK(2))
#endif
DECLARE_HOST_COMMAND(EC_CMD_FLASH_INFO,
		     flash_command_get_info, FLASH_INFO_VER);


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));


#ifdef CONFIG_FLASH_SELECT_REQUIRED

static int flash_command_select(struct host_cmd_handler_args *args)
{
	const struct ec_params_flash_select *p = args->params;

	return board_flash_select(p->select);
}
DECLARE_HOST_COMMAND(EC_CMD_FLASH_SELECT,
		     flash_command_select,
		     EC_VER_MASK(0));

#endif /* CONFIG_FLASH_SELECT_REQUIRED */
