summaryrefslogtreecommitdiffstats
path: root/flashrom.c
diff options
context:
space:
mode:
Diffstat (limited to 'flashrom.c')
-rw-r--r--flashrom.c2134
1 files changed, 920 insertions, 1214 deletions
diff --git a/flashrom.c b/flashrom.c
index c89abad60..630c69db8 100644
--- a/flashrom.c
+++ b/flashrom.c
@@ -19,32 +19,28 @@
* GNU General Public License for more details.
*/
+#include <stdbool.h>
#include <stdio.h>
+#include <stdint.h>
#include <sys/types.h>
-#ifndef __LIBPAYLOAD__
-#include <fcntl.h>
-#include <sys/stat.h>
-#endif
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
-#include <getopt.h>
-#if HAVE_UTSNAME == 1
-#include <sys/utsname.h>
-#endif
+
#include "flash.h"
#include "flashchips.h"
#include "programmer.h"
-#include "hwaccess.h"
+#include "hwaccess_physmap.h"
#include "chipdrivers.h"
+#include "erasure_layout.h"
+
+static bool use_legacy_erase_path = false;
const char flashrom_version[] = FLASHROM_VERSION;
-const char *chip_to_probe = NULL;
-static enum programmer programmer = PROGRAMMER_INVALID;
-static const char *programmer_param = NULL;
+static const struct programmer_entry *programmer = NULL;
/*
* Programmers supporting multiple buses can have differing size limits on
@@ -53,488 +49,10 @@ static const char *programmer_param = NULL;
struct decode_sizes max_rom_decode;
/* If nonzero, used as the start address of bottom-aligned flash. */
-unsigned long flashbase;
+uintptr_t flashbase;
/* Is writing allowed with this programmer? */
-int programmer_may_write;
-
-const struct programmer_entry programmer_table[] = {
-#if CONFIG_INTERNAL == 1
- {
- .name = "internal",
- .type = OTHER,
- .devs.note = NULL,
- .init = internal_init,
- .map_flash_region = physmap,
- .unmap_flash_region = physunmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_DUMMY == 1
- {
- .name = "dummy",
- .type = OTHER,
- /* FIXME */
- .devs.note = "Dummy device, does nothing and logs all accesses\n",
- .init = dummy_init,
- .map_flash_region = dummy_map,
- .unmap_flash_region = dummy_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_MEC1308 == 1
- {
- .name = "mec1308",
- .type = OTHER,
- .devs.note = "Microchip MEC1308 Embedded Controller.\n",
- .init = mec1308_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_NIC3COM == 1
- {
- .name = "nic3com",
- .type = PCI,
- .devs.dev = nics_3com,
- .init = nic3com_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_NICREALTEK == 1
- {
- /* This programmer works for Realtek RTL8139 and SMC 1211. */
- .name = "nicrealtek",
- .type = PCI,
- .devs.dev = nics_realtek,
- .init = nicrealtek_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_NICNATSEMI == 1
- {
- .name = "nicnatsemi",
- .type = PCI,
- .devs.dev = nics_natsemi,
- .init = nicnatsemi_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_GFXNVIDIA == 1
- {
- .name = "gfxnvidia",
- .type = PCI,
- .devs.dev = gfx_nvidia,
- .init = gfxnvidia_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_RAIDEN_DEBUG_SPI == 1
- {
- .name = "raiden_debug_spi",
- .type = USB,
- .devs.dev = devs_raiden,
- .init = raiden_debug_spi_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_DRKAISER == 1
- {
- .name = "drkaiser",
- .type = PCI,
- .devs.dev = drkaiser_pcidev,
- .init = drkaiser_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_SATASII == 1
- {
- .name = "satasii",
- .type = PCI,
- .devs.dev = satas_sii,
- .init = satasii_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_ATAHPT == 1
- {
- .name = "atahpt",
- .type = PCI,
- .devs.dev = ata_hpt,
- .init = atahpt_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_ATAVIA == 1
- {
- .name = "atavia",
- .type = PCI,
- .devs.dev = ata_via,
- .init = atavia_init,
- .map_flash_region = atavia_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_ATAPROMISE == 1
- {
- .name = "atapromise",
- .type = PCI,
- .devs.dev = ata_promise,
- .init = atapromise_init,
- .map_flash_region = atapromise_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_IT8212 == 1
- {
- .name = "it8212",
- .type = PCI,
- .devs.dev = devs_it8212,
- .init = it8212_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_FT2232_SPI == 1
- {
- .name = "ft2232_spi",
- .type = USB,
- .devs.dev = devs_ft2232spi,
- .init = ft2232_spi_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_SERPROG == 1
- {
- .name = "serprog",
- .type = OTHER,
- /* FIXME */
- .devs.note = "All programmer devices speaking the serprog protocol\n",
- .init = serprog_init,
- .map_flash_region = serprog_map,
- .unmap_flash_region = fallback_unmap,
- .delay = serprog_delay,
- },
-#endif
-
-#if CONFIG_BUSPIRATE_SPI == 1
- {
- .name = "buspirate_spi",
- .type = OTHER,
- /* FIXME */
- .devs.note = "Dangerous Prototypes Bus Pirate\n",
- .init = buspirate_spi_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_DEDIPROG == 1
- {
- .name = "dediprog",
- .type = USB,
- .devs.dev = devs_dediprog,
- .init = dediprog_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_DEVELOPERBOX_SPI == 1
- {
- .name = "developerbox",
- .type = USB,
- .devs.dev = devs_developerbox_spi,
- .init = developerbox_spi_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_ENE_LPC == 1
- {
- .name = "ene_lpc",
- .type = OTHER,
- .devs.note = "ENE LPC interface keyboard controller\n",
- .init = ene_lpc_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_RAYER_SPI == 1
- {
- .name = "rayer_spi",
- .type = OTHER,
- /* FIXME */
- .devs.note = "RayeR parallel port programmer\n",
- .init = rayer_spi_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_PONY_SPI == 1
- {
- .name = "pony_spi",
- .type = OTHER,
- /* FIXME */
- .devs.note = "Programmers compatible with SI-Prog, serbang or AJAWe\n",
- .init = pony_spi_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_NICINTEL == 1
- {
- .name = "nicintel",
- .type = PCI,
- .devs.dev = nics_intel,
- .init = nicintel_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_NICINTEL_SPI == 1
- {
- .name = "nicintel_spi",
- .type = PCI,
- .devs.dev = nics_intel_spi,
- .init = nicintel_spi_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_NICINTEL_EEPROM == 1
- {
- .name = "nicintel_eeprom",
- .type = PCI,
- .devs.dev = nics_intel_ee,
- .init = nicintel_ee_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_OGP_SPI == 1
- {
- .name = "ogp_spi",
- .type = PCI,
- .devs.dev = ogp_spi,
- .init = ogp_spi_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_SATAMV == 1
- {
- .name = "satamv",
- .type = PCI,
- .devs.dev = satas_mv,
- .init = satamv_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_LINUX_MTD == 1
- {
- .name = "linux_mtd",
- .type = OTHER,
- .devs.note = "Device files /dev/mtd*\n",
- .init = linux_mtd_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_LINUX_SPI == 1
- {
- .name = "linux_spi",
- .type = OTHER,
- .devs.note = "Device files /dev/spidev*.*\n",
- .init = linux_spi_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_LSPCON_I2C_SPI == 1
- {
- .name = "lspcon_i2c_spi",
- .type = OTHER,
- .devs.note = "Device files /dev/i2c-*.\n",
- .init = lspcon_i2c_spi_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_REALTEK_MST_I2C_SPI == 1
- {
- .name = "realtek_mst_i2c_spi",
- .type = OTHER,
- .devs.note = "Device files /dev/i2c-*.\n",
- .init = realtek_mst_i2c_spi_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_USBBLASTER_SPI == 1
- {
- .name = "usbblaster_spi",
- .type = USB,
- .devs.dev = devs_usbblasterspi,
- .init = usbblaster_spi_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_MSTARDDC_SPI == 1
- {
- .name = "mstarddc_spi",
- .type = OTHER,
- .devs.note = "MSTAR DDC devices addressable via /dev/i2c-* on Linux.\n",
- .init = mstarddc_spi_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_PICKIT2_SPI == 1
- {
- .name = "pickit2_spi",
- .type = USB,
- .devs.dev = devs_pickit2_spi,
- .init = pickit2_spi_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_CH341A_SPI == 1
- {
- .name = "ch341a_spi",
- .type = USB,
- .devs.dev = devs_ch341a_spi,
- .init = ch341a_spi_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = ch341a_spi_delay,
- },
-#endif
-
-#if CONFIG_DIGILENT_SPI == 1
- {
- .name = "digilent_spi",
- .type = USB,
- .devs.dev = devs_digilent_spi,
- .init = digilent_spi_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_JLINK_SPI == 1
- {
- .name = "jlink_spi",
- .type = OTHER,
- .init = jlink_spi_init,
- .devs.note = "SEGGER J-Link and compatible devices\n",
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_NI845X_SPI == 1
- {
- .name = "ni845x_spi",
- .type = OTHER, // choose other because NI-845x uses own USB implementation
- .devs.note = "National Instruments USB-845x\n",
- .init = ni845x_spi_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
-#if CONFIG_STLINKV3_SPI == 1
- {
- .name = "stlinkv3_spi",
- .type = USB,
- .devs.dev = devs_stlinkv3_spi,
- .init = stlinkv3_spi_init,
- .map_flash_region = fallback_map,
- .unmap_flash_region = fallback_unmap,
- .delay = internal_delay,
- },
-#endif
-
- {0}, /* This entry corresponds to PROGRAMMER_INVALID. */
-};
+bool programmer_may_write;
#define SHUTDOWN_MAXFN 32
static int shutdown_fn_count = 0;
@@ -546,12 +64,18 @@ static struct shutdown_func_data {
/* Initialize to 0 to make sure nobody registers a shutdown function before
* programmer init.
*/
-static int may_register_shutdown = 0;
-
-/* Did we change something or was every erase/write skipped (if any)? */
-static bool all_skipped = true;
-
-static int check_block_eraser(const struct flashctx *flash, int k, int log);
+static bool may_register_shutdown = false;
+
+static struct bus_type_info {
+ enum chipbustype type;
+ const char *name;
+} bustypes[] = {
+ { BUS_PARALLEL, "Parallel, " },
+ { BUS_LPC, "LPC, " },
+ { BUS_FWH, "FWH, " },
+ { BUS_SPI, "SPI, " },
+ { BUS_PROG, "Programmer-specific, " },
+};
/* Register a function to be executed on programmer shutdown.
* The advantage over atexit() is that you can supply a void pointer which will
@@ -581,7 +105,7 @@ int register_shutdown(int (*function) (void *data), void *data)
}
int register_chip_restore(chip_restore_fn_cb_t func,
- struct flashctx *flash, uint8_t status)
+ struct flashctx *flash, void *data)
{
if (flash->chip_restore_fn_count >= MAX_CHIP_RESTORE_FUNCTIONS) {
msg_perr("Tried to register more than %i chip restore"
@@ -589,7 +113,7 @@ int register_chip_restore(chip_restore_fn_cb_t func,
return 1;
}
flash->chip_restore_fn[flash->chip_restore_fn_count].func = func;
- flash->chip_restore_fn[flash->chip_restore_fn_count].status = status;
+ flash->chip_restore_fn[flash->chip_restore_fn_count].data = data;
flash->chip_restore_fn_count++;
return 0;
@@ -602,17 +126,17 @@ static int deregister_chip_restore(struct flashctx *flash)
while (flash->chip_restore_fn_count > 0) {
int i = --flash->chip_restore_fn_count;
rc |= flash->chip_restore_fn[i].func(
- flash, flash->chip_restore_fn[i].status);
+ flash, flash->chip_restore_fn[i].data);
}
return rc;
}
-int programmer_init(enum programmer prog, const char *param)
+int programmer_init(const struct programmer_entry *prog, const char *param)
{
int ret;
- if (prog >= PROGRAMMER_INVALID) {
+ if (prog == NULL) {
msg_perr("Invalid programmer specified!\n");
return -1;
}
@@ -628,29 +152,41 @@ int programmer_init(enum programmer prog, const char *param)
/* Default to top aligned flash at 4 GB. */
flashbase = 0;
/* Registering shutdown functions is now allowed. */
- may_register_shutdown = 1;
+ may_register_shutdown = true;
/* Default to allowing writes. Broken programmers set this to 0. */
- programmer_may_write = 1;
+ programmer_may_write = true;
+
+ struct programmer_cfg cfg;
- programmer_param = param;
- msg_pdbg("Initializing %s programmer\n", programmer_table[programmer].name);
- ret = programmer_table[programmer].init();
- if (programmer_param && strlen(programmer_param)) {
+ if (param) {
+ cfg.params = strdup(param);
+ if (!cfg.params) {
+ msg_perr("Out of memory!\n");
+ return ERROR_FLASHROM_FATAL;
+ }
+ } else {
+ cfg.params = NULL;
+ }
+
+ msg_pdbg("Initializing %s programmer\n", prog->name);
+ ret = prog->init(&cfg);
+ if (cfg.params && strlen(cfg.params)) {
if (ret != 0) {
/* It is quite possible that any unhandled programmer parameter would have been valid,
* but an error in actual programmer init happened before the parameter was evaluated.
*/
msg_pwarn("Unhandled programmer parameters (possibly due to another failure): %s\n",
- programmer_param);
+ cfg.params);
} else {
/* Actual programmer init was successful, but the user specified an invalid or unusable
* (for the current programmer configuration) parameter.
*/
- msg_perr("Unhandled programmer parameters: %s\n", programmer_param);
+ msg_perr("Unhandled programmer parameters: %s\n", cfg.params);
msg_perr("Aborting.\n");
- ret = ERROR_FATAL;
+ ret = ERROR_FLASHROM_FATAL;
}
}
+ free(cfg.params);
return ret;
}
@@ -664,77 +200,100 @@ int programmer_shutdown(void)
int ret = 0;
/* Registering shutdown functions is no longer allowed. */
- may_register_shutdown = 0;
+ may_register_shutdown = false;
while (shutdown_fn_count > 0) {
int i = --shutdown_fn_count;
ret |= shutdown_fn[i].func(shutdown_fn[i].data);
}
-
- programmer_param = NULL;
registered_master_count = 0;
return ret;
}
-void *programmer_map_flash_region(const char *descr, uintptr_t phys_addr, size_t len)
+void *master_map_flash_region(const struct registered_master *mst,
+ const char *descr, uintptr_t phys_addr,
+ size_t len)
{
- void *ret = programmer_table[programmer].map_flash_region(descr, phys_addr, len);
+ /* Check the bus master for a specialized map_flash_region; default to
+ * fallback if it does not specialize it
+ */
+ void *(*map_flash_region) (const char *descr, uintptr_t phys_addr, size_t len) = NULL;
+ if (mst->buses_supported & BUS_SPI)
+ map_flash_region = mst->spi.map_flash_region;
+ else if (mst->buses_supported & BUS_NONSPI)
+ map_flash_region = mst->par.map_flash_region;
+
+ /* A result of NULL causes mapped addresses to be chip physical
+ * addresses, assuming only a single region is mapped (the entire flash
+ * space). Chips with a second region (like a register map) require a
+ * real memory mapping to distinguish the different ranges. Those chips
+ * are FWH/LPC, so the bus master provides a real mapping.
+ */
+ void *ret = NULL;
+ if (map_flash_region)
+ ret = map_flash_region(descr, phys_addr, len);
msg_gspew("%s: mapping %s from 0x%0*" PRIxPTR " to 0x%0*" PRIxPTR "\n",
- __func__, descr, PRIxPTR_WIDTH, phys_addr, PRIxPTR_WIDTH, (uintptr_t) ret);
+ __func__, descr, PRIxPTR_WIDTH, phys_addr, PRIxPTR_WIDTH, (uintptr_t) ret);
return ret;
}
-void programmer_unmap_flash_region(void *virt_addr, size_t len)
+void master_unmap_flash_region(const struct registered_master *mst,
+ void *virt_addr, size_t len)
{
- programmer_table[programmer].unmap_flash_region(virt_addr, len);
- msg_gspew("%s: unmapped 0x%0*" PRIxPTR "\n", __func__, PRIxPTR_WIDTH, (uintptr_t)virt_addr);
-}
+ void (*unmap_flash_region) (void *virt_addr, size_t len) = NULL;
+ if (mst->buses_supported & BUS_SPI)
+ unmap_flash_region = mst->spi.unmap_flash_region;
+ else if (mst->buses_supported & BUS_NONSPI)
+ unmap_flash_region = mst->par.unmap_flash_region;
-void chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr)
-{
- flash->mst->par.chip_writeb(flash, val, addr);
-}
-
-void chip_writew(const struct flashctx *flash, uint16_t val, chipaddr addr)
-{
- flash->mst->par.chip_writew(flash, val, addr);
-}
-
-void chip_writel(const struct flashctx *flash, uint32_t val, chipaddr addr)
-{
- flash->mst->par.chip_writel(flash, val, addr);
-}
-
-void chip_writen(const struct flashctx *flash, const uint8_t *buf, chipaddr addr, size_t len)
-{
- flash->mst->par.chip_writen(flash, buf, addr, len);
+ if (unmap_flash_region)
+ unmap_flash_region(virt_addr, len);
+ msg_gspew("%s: unmapped 0x%0*" PRIxPTR "\n", __func__, PRIxPTR_WIDTH, (uintptr_t)virt_addr);
}
-uint8_t chip_readb(const struct flashctx *flash, const chipaddr addr)
+static bool master_uses_physmap(const struct registered_master *mst)
{
- return flash->mst->par.chip_readb(flash, addr);
+#if CONFIG_INTERNAL == 1
+ if (mst->buses_supported & BUS_SPI)
+ return mst->spi.map_flash_region == physmap;
+ else if (mst->buses_supported & BUS_NONSPI)
+ return mst->par.map_flash_region == physmap;
+#endif
+ return false;
}
-uint16_t chip_readw(const struct flashctx *flash, const chipaddr addr)
+void programmer_delay(const struct flashctx *flash, unsigned int usecs)
{
- return flash->mst->par.chip_readw(flash, addr);
-}
+ if (usecs == 0)
+ return;
-uint32_t chip_readl(const struct flashctx *flash, const chipaddr addr)
-{
- return flash->mst->par.chip_readl(flash, addr);
-}
+ /**
+ * Drivers should either use default_delay() directly or their
+ * own custom delay. Only core flashrom logic calls programmer_delay()
+ * which should always have a valid flash context. A NULL context
+ * more than likely indicates a layering violation or BUG however
+ * for now dispatch a default_delay() as a safe default for the NULL
+ * base case.
+ */
+ if (!flash) {
+ msg_perr("%s called with NULL flash context. "
+ "Please report a bug at flashrom@flashrom.org\n",
+ __func__);
+ return default_delay(usecs);
+ }
-void chip_readn(const struct flashctx *flash, uint8_t *buf, chipaddr addr,
- size_t len)
-{
- flash->mst->par.chip_readn(flash, buf, addr, len);
-}
+ if (flash->mst->buses_supported & BUS_SPI) {
+ if (flash->mst->spi.delay)
+ return flash->mst->spi.delay(flash, usecs);
+ } else if (flash->mst->buses_supported & BUS_PARALLEL) {
+ if (flash->mst->par.delay)
+ return flash->mst->par.delay(flash, usecs);
+ } else if (flash->mst->buses_supported & BUS_PROG) {
+ if (flash->mst->opaque.delay)
+ return flash->mst->opaque.delay(flash, usecs);
+ }
-void programmer_delay(unsigned int usecs)
-{
- if (usecs > 0)
- programmer_table[programmer].delay(usecs);
+ return default_delay(usecs);
}
int read_memmapped(struct flashctx *flash, uint8_t *buf, unsigned int start,
@@ -750,7 +309,7 @@ int read_memmapped(struct flashctx *flash, uint8_t *buf, unsigned int start,
* needle and remove everything from the first occurrence of needle to the next
* delimiter from haystack.
*/
-char *extract_param(const char *const *haystack, const char *needle, const char *delim)
+static char *extract_param(char *const *haystack, const char *needle, const char *delim)
{
char *param_pos, *opt_pos, *rest;
char *opt = NULL;
@@ -792,7 +351,7 @@ char *extract_param(const char *const *haystack, const char *needle, const char
opt = malloc(optlen + 1);
if (!opt) {
msg_gerr("Out of memory!\n");
- exit(1);
+ return NULL;
}
strncpy(opt, opt_pos, optlen);
opt[optlen] = '\0';
@@ -806,13 +365,133 @@ char *extract_param(const char *const *haystack, const char *needle, const char
return opt;
}
-char *extract_programmer_param(const char *param_name)
+char *extract_programmer_param_str(const struct programmer_cfg *cfg, const char *param_name)
{
- return extract_param(&programmer_param, param_name, ",");
+ return extract_param(&cfg->params, param_name, ",");
+}
+
+void get_flash_region(const struct flashctx *flash, int addr, struct flash_region *region)
+{
+ if ((flash->mst->buses_supported & BUS_PROG) && flash->mst->opaque.get_region) {
+ flash->mst->opaque.get_region(flash, addr, region);
+ } else if (flash->mst->buses_supported & BUS_SPI && flash->mst->spi.get_region) {
+ flash->mst->spi.get_region(flash, addr, region);
+ } else {
+ region->name = strdup("");
+ region->start = 0;
+ region->end = flashrom_flash_getsize(flash);
+ region->read_prot = false;
+ region->write_prot = false;
+ }
+}
+
+int check_for_unwritable_regions(const struct flashctx *flash, unsigned int start, unsigned int len)
+{
+ struct flash_region region;
+ for (unsigned int addr = start; addr < start + len; addr = region.end) {
+ get_flash_region(flash, addr, &region);
+
+ if (region.write_prot) {
+ msg_gerr("%s: cannot write/erase inside %s region (%#08"PRIx32"..%#08"PRIx32").\n",
+ __func__, region.name, region.start, region.end - 1);
+ free(region.name);
+ return -1;
+ }
+ free(region.name);
+ }
+ return 0;
+}
+
+/* special unit-test hook */
+erasefunc_t *g_test_erase_injector;
+
+erasefunc_t *lookup_erase_func_ptr(const struct block_eraser *const eraser)
+{
+ switch (eraser->block_erase) {
+ case SPI_BLOCK_ERASE_EMULATION: return &spi_block_erase_emulation;
+ case SPI_BLOCK_ERASE_20: return &spi_block_erase_20;
+ case SPI_BLOCK_ERASE_21: return &spi_block_erase_21;
+ case SPI_BLOCK_ERASE_40: return NULL; // FIXME unhandled &spi_block_erase_40;
+ case SPI_BLOCK_ERASE_50: return &spi_block_erase_50;
+ case SPI_BLOCK_ERASE_52: return &spi_block_erase_52;
+ case SPI_BLOCK_ERASE_53: return &spi_block_erase_53;
+ case SPI_BLOCK_ERASE_5C: return &spi_block_erase_5c;
+ case SPI_BLOCK_ERASE_60: return &spi_block_erase_60;
+ case SPI_BLOCK_ERASE_62: return &spi_block_erase_62;
+ case SPI_BLOCK_ERASE_81: return &spi_block_erase_81;
+ case SPI_BLOCK_ERASE_C4: return &spi_block_erase_c4;
+ case SPI_BLOCK_ERASE_C7: return &spi_block_erase_c7;
+ case SPI_BLOCK_ERASE_D7: return &spi_block_erase_d7;
+ case SPI_BLOCK_ERASE_D8: return &spi_block_erase_d8;
+ case SPI_BLOCK_ERASE_DB: return &spi_block_erase_db;
+ case SPI_BLOCK_ERASE_DC: return &spi_block_erase_dc;
+ case S25FL_BLOCK_ERASE: return &s25fl_block_erase;
+ case S25FS_BLOCK_ERASE_D8: return &s25fs_block_erase_d8;
+ case JEDEC_SECTOR_ERASE: return &erase_sector_jedec; // TODO rename to &jedec_sector_erase;
+ case JEDEC_BLOCK_ERASE: return &erase_block_jedec; // TODO rename to &jedec_block_erase;
+ case JEDEC_CHIP_BLOCK_ERASE: return &erase_chip_block_jedec; // TODO rename to &jedec_chip_block_erase;
+ case OPAQUE_ERASE: return &erase_opaque; // TODO rename to &opqaue_erase;
+ case SPI_ERASE_AT45CS_SECTOR: return &spi_erase_at45cs_sector;
+ case SPI_ERASE_AT45DB_BLOCK: return &spi_erase_at45db_block;
+ case SPI_ERASE_AT45DB_CHIP: return &spi_erase_at45db_chip;
+ case SPI_ERASE_AT45DB_PAGE: return &spi_erase_at45db_page;
+ case SPI_ERASE_AT45DB_SECTOR: return &spi_erase_at45db_sector;
+ case ERASE_CHIP_28SF040: return &erase_chip_28sf040;
+ case ERASE_SECTOR_28SF040: return &erase_sector_28sf040;
+ case ERASE_BLOCK_82802AB: return &erase_block_82802ab;
+ case ERASE_SECTOR_49LFXXXC: return &erase_sector_49lfxxxc;
+ case STM50_SECTOR_ERASE: return &erase_sector_stm50; // TODO rename to &stm50_sector_erase;
+ case EDI_CHIP_BLOCK_ERASE: return &edi_chip_block_erase;
+ case TEST_ERASE_INJECTOR: return g_test_erase_injector;
+ /* default: total function, 0 indicates no erase function set.
+ * We explicitly do not want a default catch-all case in the switch
+ * to ensure unhandled enum's are compiler warnings.
+ */
+ case NO_BLOCK_ERASE_FUNC: return NULL;
+ };
+
+ return NULL;
+}
+
+int check_block_eraser(const struct flashctx *flash, int k, int log)
+{
+ struct block_eraser eraser = flash->chip->block_erasers[k];
+
+ if (eraser.block_erase == NO_BLOCK_ERASE_FUNC && !eraser.eraseblocks[0].count) {
+ if (log)
+ msg_cdbg("not defined. ");
+ return 1;
+ }
+ if (eraser.block_erase == NO_BLOCK_ERASE_FUNC && eraser.eraseblocks[0].count) {
+ if (log)
+ msg_cdbg("eraseblock layout is known, but matching "
+ "block erase function is not implemented. ");
+ return 1;
+ }
+ if (eraser.block_erase != NO_BLOCK_ERASE_FUNC && !eraser.eraseblocks[0].count) {
+ if (log)
+ msg_cdbg("block erase function found, but "
+ "eraseblock layout is not defined. ");
+ return 1;
+ }
+
+ if (flash->mst->buses_supported & BUS_SPI) {
+ const uint8_t *opcode = spi_get_opcode_from_erasefn(eraser.block_erase);
+ for (int i = 0; opcode[i]; i++) {
+ if (!spi_probe_opcode(flash, opcode[i])) {
+ if (log)
+ msg_cdbg("block erase function and layout found "
+ "but SPI master doesn't support the function. ");
+ return 1;
+ }
+ }
+ }
+ // TODO: Once erase functions are annotated with allowed buses, check that as well.
+ return 0;
}
/* Returns the number of well-defined erasers for a chip. */
-static unsigned int count_usable_erasers(const struct flashctx *flash)
+unsigned int count_usable_erasers(const struct flashctx *flash)
{
unsigned int usable_erasefunctions = 0;
int k;
@@ -844,22 +523,102 @@ static int compare_range(const uint8_t *wantbuf, const uint8_t *havebuf, unsigne
}
/* start is an offset to the base address of the flash chip */
-static int check_erased_range(struct flashctx *flash, unsigned int start, unsigned int len)
+int check_erased_range(struct flashctx *flash, unsigned int start, unsigned int len)
{
int ret;
- uint8_t *cmpbuf = malloc(len);
const uint8_t erased_value = ERASED_VALUE(flash);
+ uint8_t *cmpbuf = malloc(len);
if (!cmpbuf) {
- msg_gerr("Could not allocate memory!\n");
- exit(1);
+ msg_gerr("Out of memory!\n");
+ return -1;
}
memset(cmpbuf, erased_value, len);
ret = verify_range(flash, cmpbuf, start, len);
+
free(cmpbuf);
return ret;
}
+/* special unit-test hook */
+read_func_t *g_test_read_injector;
+
+static read_func_t *lookup_read_func_ptr(const struct flashchip *chip)
+{
+ switch (chip->read) {
+ case SPI_CHIP_READ: return &spi_chip_read;
+ case READ_OPAQUE: return &read_opaque;
+ case READ_MEMMAPPED: return &read_memmapped;
+ case EDI_CHIP_READ: return &edi_chip_read;
+ case SPI_READ_AT45DB: return spi_read_at45db;
+ case SPI_READ_AT45DB_E8: return spi_read_at45db_e8;
+ case TEST_READ_INJECTOR: return g_test_read_injector;
+ /* default: total function, 0 indicates no read function set.
+ * We explicitly do not want a default catch-all case in the switch
+ * to ensure unhandled enum's are compiler warnings.
+ */
+ case NO_READ_FUNC: return NULL;
+ };
+
+ return NULL;
+}
+
+/*
+ * @brief Wrapper for flash->read() with additional high-level policy.
+ *
+ * @param flash flash chip
+ * @param buf buffer to store data in
+ * @param start start address
+ * @param len number of bytes to read
+ * @return 0 on success,
+ * -1 if any read fails.
+ *
+ * This wrapper simplifies most cases when the flash chip needs to be read
+ * since policy decisions such as non-fatal error handling is centralized.
+ */
+int read_flash(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len)
+{
+ unsigned int read_len;
+ for (unsigned int addr = start; addr < start + len; addr += read_len) {
+ struct flash_region region;
+ get_flash_region(flash, addr, &region);
+
+ read_len = min(start + len, region.end) - addr;
+ uint8_t *rbuf = buf + addr - start;
+
+ if (region.read_prot) {
+ if (flash->flags.skip_unreadable_regions) {
+ msg_gdbg("%s: cannot read inside %s region (%#08"PRIx32"..%#08"PRIx32"), "
+ "filling (%#08x..%#08x) with erased value instead.\n",
+ __func__, region.name, region.start, region.end - 1,
+ addr, addr + read_len - 1);
+ free(region.name);
+
+ memset(rbuf, ERASED_VALUE(flash), read_len);
+ continue;
+ }
+
+ msg_gerr("%s: cannot read inside %s region (%#08"PRIx32"..%#08"PRIx32").\n",
+ __func__, region.name, region.start, region.end - 1);
+ free(region.name);
+ return -1;
+ }
+ msg_gdbg("%s: %s region (%#08"PRIx32"..%#08"PRIx32") is readable, reading range (%#08x..%#08x).\n",
+ __func__, region.name, region.start, region.end - 1, addr, addr + read_len - 1);
+ free(region.name);
+
+ read_func_t *read_func = lookup_read_func_ptr(flash->chip);
+ int ret = read_func(flash, rbuf, addr, read_len);
+ if (ret) {
+ msg_gerr("%s: failed to read (%#08x..%#08x).\n", __func__, addr, addr + read_len - 1);
+ return -1;
+ }
+
+ }
+
+ return 0;
+}
+
/*
* @cmpbuf buffer to compare against, cmpbuf[0] is expected to match the
* flash content at location start
@@ -872,35 +631,62 @@ int verify_range(struct flashctx *flash, const uint8_t *cmpbuf, unsigned int sta
if (!len)
return -1;
- if (!flash->chip->read) {
- msg_cerr("ERROR: flashrom has no read function for this flash chip.\n");
+ if (start + len > flash->chip->total_size * 1024) {
+ msg_gerr("Error: %s called with start 0x%x + len 0x%x >"
+ " total_size 0x%x\n", __func__, start, len,
+ flash->chip->total_size * 1024);
return -1;
}
uint8_t *readbuf = malloc(len);
if (!readbuf) {
- msg_gerr("Could not allocate memory!\n");
+ msg_gerr("Out of memory!\n");
return -1;
}
+
int ret = 0;
- if (start + len > flash->chip->total_size * 1024) {
- msg_gerr("Error: %s called with start 0x%x + len 0x%x >"
- " total_size 0x%x\n", __func__, start, len,
- flash->chip->total_size * 1024);
- ret = -1;
- goto out_free;
- }
+ msg_gdbg("%#06x..%#06x ", start, start + len - 1);
+
+ unsigned int read_len;
+ for (size_t addr = start; addr < start + len; addr += read_len) {
+ struct flash_region region;
+ get_flash_region(flash, addr, &region);
+ read_len = min(start + len, region.end) - addr;
+
+ if ((region.write_prot && flash->flags.skip_unwritable_regions) ||
+ (region.read_prot && flash->flags.skip_unreadable_regions)) {
+ msg_gdbg("%s: Skipping verification of %s region (%#08"PRIx32"..%#08"PRIx32")\n",
+ __func__, region.name, region.start, region.end - 1);
+ free(region.name);
+ continue;
+ }
+
+ if (region.read_prot) {
+ msg_gerr("%s: Verification imposible because %s region (%#08"PRIx32"..%#08"PRIx32") is unreadable.\n",
+ __func__, region.name, region.start, region.end - 1);
+ free(region.name);
+ goto out_free;
+ }
+
+ msg_gdbg("%s: Verifying %s region (%#08"PRIx32"..%#08"PRIx32")\n",
+ __func__, region.name, region.start, region.end - 1);
+ free(region.name);
+
+ ret = read_flash(flash, readbuf, addr, read_len);
+ if (ret) {
+ msg_gerr("Verification impossible because read failed "
+ "at 0x%x (len 0x%x)\n", start, len);
+ ret = -1;
+ goto out_free;
+ }
+
+ ret = compare_range(cmpbuf + (addr - start), readbuf, addr, read_len);
+ if (ret)
+ goto out_free;
- ret = flash->chip->read(flash, readbuf, start, len);
- if (ret) {
- msg_gerr("Verification impossible because read failed "
- "at 0x%x (len 0x%x)\n", start, len);
- ret = -1;
- goto out_free;
}
- ret = compare_range(cmpbuf, readbuf, start, len);
out_free:
free(readbuf);
return ret;
@@ -945,42 +731,42 @@ int need_erase(const uint8_t *have, const uint8_t *want, unsigned int len,
unsigned int i;
switch (gran) {
- case write_gran_1bit:
+ case WRITE_GRAN_1BIT:
for (i = 0; i < len; i++)
if ((have[i] & want[i]) != want[i]) {
result = 1;
break;
}
break;
- case write_gran_1byte:
+ case WRITE_GRAN_1BYTE:
for (i = 0; i < len; i++)
if ((have[i] != want[i]) && (have[i] != erased_value)) {
result = 1;
break;
}
break;
- case write_gran_128bytes:
+ case WRITE_GRAN_128BYTES:
result = need_erase_gran_bytes(have, want, len, 128, erased_value);
break;
- case write_gran_256bytes:
+ case WRITE_GRAN_256BYTES:
result = need_erase_gran_bytes(have, want, len, 256, erased_value);
break;
- case write_gran_264bytes:
+ case WRITE_GRAN_264BYTES:
result = need_erase_gran_bytes(have, want, len, 264, erased_value);
break;
- case write_gran_512bytes:
+ case WRITE_GRAN_512BYTES:
result = need_erase_gran_bytes(have, want, len, 512, erased_value);
break;
- case write_gran_528bytes:
+ case WRITE_GRAN_528BYTES:
result = need_erase_gran_bytes(have, want, len, 528, erased_value);
break;
- case write_gran_1024bytes:
+ case WRITE_GRAN_1024BYTES:
result = need_erase_gran_bytes(have, want, len, 1024, erased_value);
break;
- case write_gran_1056bytes:
+ case WRITE_GRAN_1056BYTES:
result = need_erase_gran_bytes(have, want, len, 1056, erased_value);
break;
- case write_gran_1byte_implicit_erase:
+ case WRITE_GRAN_1BYTE_IMPLICIT_ERASE:
/* Do not erase, handle content changes from anything->0xff by writing 0xff. */
result = 0;
break;
@@ -1014,39 +800,39 @@ int need_erase(const uint8_t *have, const uint8_t *want, unsigned int len,
* in relation to the max write length of the programmer and the max write
* length of the chip.
*/
-static unsigned int get_next_write(const uint8_t *have, const uint8_t *want, unsigned int len,
+unsigned int get_next_write(const uint8_t *have, const uint8_t *want, unsigned int len,
unsigned int *first_start,
enum write_granularity gran)
{
- int need_write = 0;
+ bool need_write = false;
unsigned int rel_start = 0, first_len = 0;
unsigned int i, limit, stride;
switch (gran) {
- case write_gran_1bit:
- case write_gran_1byte:
- case write_gran_1byte_implicit_erase:
+ case WRITE_GRAN_1BIT:
+ case WRITE_GRAN_1BYTE:
+ case WRITE_GRAN_1BYTE_IMPLICIT_ERASE:
stride = 1;
break;
- case write_gran_128bytes:
+ case WRITE_GRAN_128BYTES:
stride = 128;
break;
- case write_gran_256bytes:
+ case WRITE_GRAN_256BYTES:
stride = 256;
break;
- case write_gran_264bytes:
+ case WRITE_GRAN_264BYTES:
stride = 264;
break;
- case write_gran_512bytes:
+ case WRITE_GRAN_512BYTES:
stride = 512;
break;
- case write_gran_528bytes:
+ case WRITE_GRAN_528BYTES:
stride = 528;
break;
- case write_gran_1024bytes:
+ case WRITE_GRAN_1024BYTES:
stride = 1024;
break;
- case write_gran_1056bytes:
+ case WRITE_GRAN_1056BYTES:
stride = 1056;
break;
default:
@@ -1063,7 +849,7 @@ static unsigned int get_next_write(const uint8_t *have, const uint8_t *want, uns
if (memcmp(have + i * stride, want + i * stride, limit)) {
if (!need_write) {
/* First location where have and want differ. */
- need_write = 1;
+ need_write = true;
rel_start = i * stride;
}
} else {
@@ -1081,59 +867,16 @@ static unsigned int get_next_write(const uint8_t *have, const uint8_t *want, uns
return first_len;
}
-/* Returns the number of busses commonly supported by the current programmer and flash chip where the latter
- * can not be completely accessed due to size/address limits of the programmer. */
-unsigned int count_max_decode_exceedings(const struct flashctx *flash)
-{
- unsigned int limitexceeded = 0;
- uint32_t size = flash->chip->total_size * 1024;
- enum chipbustype buses = flash->mst->buses_supported & flash->chip->bustype;
-
- if ((buses & BUS_PARALLEL) && (max_rom_decode.parallel < size)) {
- limitexceeded++;
- msg_pdbg("Chip size %u kB is bigger than supported "
- "size %u kB of chipset/board/programmer "
- "for %s interface, "
- "probe/read/erase/write may fail. ", size / 1024,
- max_rom_decode.parallel / 1024, "Parallel");
- }
- if ((buses & BUS_LPC) && (max_rom_decode.lpc < size)) {
- limitexceeded++;
- msg_pdbg("Chip size %u kB is bigger than supported "
- "size %u kB of chipset/board/programmer "
- "for %s interface, "
- "probe/read/erase/write may fail. ", size / 1024,
- max_rom_decode.lpc / 1024, "LPC");
- }
- if ((buses & BUS_FWH) && (max_rom_decode.fwh < size)) {
- limitexceeded++;
- msg_pdbg("Chip size %u kB is bigger than supported "
- "size %u kB of chipset/board/programmer "
- "for %s interface, "
- "probe/read/erase/write may fail. ", size / 1024,
- max_rom_decode.fwh / 1024, "FWH");
- }
- if ((buses & BUS_SPI) && (max_rom_decode.spi < size)) {
- limitexceeded++;
- msg_pdbg("Chip size %u kB is bigger than supported "
- "size %u kB of chipset/board/programmer "
- "for %s interface, "
- "probe/read/erase/write may fail. ", size / 1024,
- max_rom_decode.spi / 1024, "SPI");
- }
- return limitexceeded;
-}
-
void unmap_flash(struct flashctx *flash)
{
if (flash->virtual_registers != (chipaddr)ERROR_PTR) {
- programmer_unmap_flash_region((void *)flash->virtual_registers, flash->chip->total_size * 1024);
+ master_unmap_flash_region(flash->mst, (void *)flash->virtual_registers, flash->chip->total_size * 1024);
flash->physical_registers = 0;
flash->virtual_registers = (chipaddr)ERROR_PTR;
}
if (flash->virtual_memory != (chipaddr)ERROR_PTR) {
- programmer_unmap_flash_region((void *)flash->virtual_memory, flash->chip->total_size * 1024);
+ master_unmap_flash_region(flash->mst, (void *)flash->virtual_memory, flash->chip->total_size * 1024);
flash->physical_memory = 0;
flash->virtual_memory = (chipaddr)ERROR_PTR;
}
@@ -1153,7 +896,7 @@ int map_flash(struct flashctx *flash)
const chipsize_t size = flash->chip->total_size * 1024;
uintptr_t base = flashbase ? flashbase : (0xffffffff - size + 1);
- void *addr = programmer_map_flash_region(flash->chip->name, base, size);
+ void *addr = master_map_flash_region(flash->mst, flash->chip->name, base, size);
if (addr == ERROR_PTR) {
msg_perr("Could not map flash chip %s at 0x%0*" PRIxPTR ".\n",
flash->chip->name, PRIxPTR_WIDTH, base);
@@ -1167,7 +910,7 @@ int map_flash(struct flashctx *flash)
* Ignore these problems for now and always report success. */
if (flash->chip->feature_bits & FEATURE_REGISTERMAP) {
base = 0xffffffff - size - 0x400000 + 1;
- addr = programmer_map_flash_region("flash chip registers", base, size);
+ addr = master_map_flash_region(flash->mst, "flash chip registers", base, size);
if (addr == ERROR_PTR) {
msg_pdbg2("Could not map flash chip registers %s at 0x%0*" PRIxPTR ".\n",
flash->chip->name, PRIxPTR_WIDTH, base);
@@ -1181,38 +924,171 @@ int map_flash(struct flashctx *flash)
/*
* Return a string corresponding to the bustype parameter.
- * Memory is obtained with malloc() and must be freed with free() by the caller.
+ * Memory to store the string is allocated. The caller is responsible to free memory.
+ * If there is not enough memory remaining, then NULL is returned.
*/
char *flashbuses_to_text(enum chipbustype bustype)
{
- char *ret = calloc(1, 1);
+ char *ret, *ptr;
+
/*
* FIXME: Once all chipsets and flash chips have been updated, NONSPI
* will cease to exist and should be eliminated here as well.
*/
- if (bustype == BUS_NONSPI) {
- ret = strcat_realloc(ret, "Non-SPI, ");
- } else {
- if (bustype & BUS_PARALLEL)
- ret = strcat_realloc(ret, "Parallel, ");
- if (bustype & BUS_LPC)
- ret = strcat_realloc(ret, "LPC, ");
- if (bustype & BUS_FWH)
- ret = strcat_realloc(ret, "FWH, ");
- if (bustype & BUS_SPI)
- ret = strcat_realloc(ret, "SPI, ");
- if (bustype & BUS_PROG)
- ret = strcat_realloc(ret, "Programmer-specific, ");
- if (bustype == BUS_NONE)
- ret = strcat_realloc(ret, "None, ");
+ if (bustype == BUS_NONSPI)
+ return strdup("Non-SPI");
+ if (bustype == BUS_NONE)
+ return strdup("None");
+
+ ret = calloc(1, 1);
+ if (!ret)
+ return NULL;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(bustypes); i++)
+ {
+ if (bustype & bustypes[i].type) {
+ ptr = strcat_realloc(ret, bustypes[i].name);
+ if (!ptr) {
+ free(ret);
+ return NULL;
+ }
+ ret = ptr;
+ }
}
+
/* Kill last comma. */
ret[strlen(ret) - 2] = '\0';
- ret = realloc(ret, strlen(ret) + 1);
- return ret;
+ ptr = realloc(ret, strlen(ret) + 1);
+ if (!ptr)
+ free(ret);
+ return ptr;
+}
+
+static int init_default_layout(struct flashctx *flash)
+{
+ /* Fill default layout covering the whole chip. */
+ if (flashrom_layout_new(&flash->default_layout) ||
+ flashrom_layout_add_region(flash->default_layout,
+ 0, flash->chip->total_size * 1024 - 1, "complete flash") ||
+ flashrom_layout_include_region(flash->default_layout, "complete flash"))
+ return -1;
+ return 0;
+}
+
+/* special unit-test hook */
+write_func_t *g_test_write_injector;
+
+static write_func_t *lookup_write_func_ptr(const struct flashchip *chip)
+{
+ switch (chip->write) {
+ case WRITE_JEDEC: return &write_jedec;
+ case WRITE_JEDEC1: return &write_jedec_1;
+ case WRITE_OPAQUE: return &write_opaque;
+ case SPI_CHIP_WRITE1: return &spi_chip_write_1;
+ case SPI_CHIP_WRITE256: return &spi_chip_write_256;
+ case SPI_WRITE_AAI: return &spi_aai_write;
+ case SPI_WRITE_AT45DB: return &spi_write_at45db;
+ case WRITE_28SF040: return &write_28sf040;
+ case WRITE_82802AB: return &write_82802ab;
+ case WRITE_EN29LV640B: return &write_en29lv640b;
+ case EDI_CHIP_WRITE: return &edi_chip_write;
+ case TEST_WRITE_INJECTOR: return g_test_write_injector;
+ /* default: total function, 0 indicates no write function set.
+ * We explicitly do not want a default catch-all case in the switch
+ * to ensure unhandled enum's are compiler warnings.
+ */
+ case NO_WRITE_FUNC: return NULL;
+ };
+
+ return NULL;
+}
+
+/*
+ * write_flash - wrapper for flash->write() with additional high-level policy
+ *
+ * @param flash flash chip
+ * @param buf buffer to write to flash
+ * @param start start address in flash
+ * @param len number of bytes to write
+ * @return 0 on success,
+ * -1 if any write fails.
+ *
+ * This wrapper simplifies most cases when the flash chip needs to be written
+ * since policy decisions such as non-fatal error handling is centralized.
+ */
+int write_flash(struct flashctx *flash, const uint8_t *buf,
+ unsigned int start, unsigned int len)
+{
+ if (!flash->flags.skip_unwritable_regions) {
+ if (check_for_unwritable_regions(flash, start, len))
+ return -1;
+ }
+
+ unsigned int write_len;
+ for (unsigned int addr = start; addr < start + len; addr += write_len) {
+ struct flash_region region;
+ get_flash_region(flash, addr, &region);
+
+ write_len = min(start + len, region.end) - addr;
+ const uint8_t *rbuf = buf + addr - start;
+
+ if (region.write_prot) {
+ msg_gdbg("%s: cannot write inside %s region (%#08"PRIx32"..%#08"PRIx32"), skipping (%#08x..%#08x).\n",
+ __func__, region.name, region.start, region.end - 1, addr, addr + write_len - 1);
+ free(region.name);
+ continue;
+ }
+
+ msg_gdbg("%s: %s region (%#08"PRIx32"..%#08"PRIx32") is writable, writing range (%#08x..%#08x).\n",
+ __func__, region.name, region.start, region.end - 1, addr, addr + write_len - 1);
+
+ write_func_t *write_func = lookup_write_func_ptr(flash->chip);
+ int ret = write_func(flash, rbuf, addr, write_len);
+ if (ret) {
+ msg_gerr("%s: failed to write (%#08x..%#08x).\n", __func__, addr, addr + write_len - 1);
+ free(region.name);
+ return -1;
+ }
+
+ free(region.name);
+ }
+
+ return 0;
}
-int probe_flash(struct registered_master *mst, int startchip, struct flashctx *flash, int force)
+typedef int (probe_func_t)(struct flashctx *flash);
+
+static probe_func_t *lookup_probe_func_ptr(const struct flashchip *chip)
+{
+ switch (chip->probe) {
+ case PROBE_JEDEC: return &probe_jedec;
+ case PROBE_JEDEC_29GL: return &probe_jedec_29gl;
+ case PROBE_OPAQUE: return &probe_opaque;
+ case PROBE_EDI_KB9012: return &edi_probe_kb9012;
+ case PROBE_AT82802AB: return &probe_82802ab;
+ case PROBE_W29EE011: return &probe_w29ee011;
+ case PROBE_EN29LV640B: return &probe_en29lv640b;
+ case PROBE_SPI_AT25F: return &probe_spi_at25f;
+ case PROBE_SPI_AT45DB: return &probe_spi_at45db;
+ case PROBE_SPI_BIG_SPANSION: return &probe_spi_big_spansion;
+ case PROBE_SPI_RDID: return &probe_spi_rdid;
+ case PROBE_SPI_RDID4: return &probe_spi_rdid4;
+ case PROBE_SPI_REMS: return &probe_spi_rems;
+ case PROBE_SPI_RES1: return &probe_spi_res1;
+ case PROBE_SPI_RES2: return &probe_spi_res2;
+ case PROBE_SPI_SFDP: return &probe_spi_sfdp;
+ case PROBE_SPI_ST95: return &probe_spi_st95;
+ /* default: total function, 0 indicates no probe function set.
+ * We explicitly do not want a default catch-all case in the switch
+ * to ensure unhandled enum's are compiler warnings.
+ */
+ case NO_PROBE_FUNC: return NULL;
+ };
+
+ return NULL;
+}
+
+int probe_flash(struct registered_master *mst, int startchip, struct flashctx *flash, int force, const char *const chip_to_probe)
{
const struct flashchip *chip;
enum chipbustype buses_common;
@@ -1228,18 +1104,19 @@ int probe_flash(struct registered_master *mst, int startchip, struct flashctx *f
if (chip->bustype == BUS_SPI && !chip_to_probe && chip->spi_cmd_set != SPI25)
continue;
msg_gdbg("Probing for %s %s, %d kB: ", chip->vendor, chip->name, chip->total_size);
- if (!chip->probe && !force) {
+ probe_func_t *probe_func = lookup_probe_func_ptr(chip);
+ if (!probe_func && !force) {
msg_gdbg("failed! flashrom has no probe function for this flash chip.\n");
continue;
}
/* Start filling in the dynamic data. */
- flash->chip = calloc(1, sizeof(struct flashchip));
+ flash->chip = calloc(1, sizeof(*flash->chip));
if (!flash->chip) {
msg_gerr("Out of memory!\n");
- exit(1);
+ return -1;
}
- memcpy(flash->chip, chip, sizeof(struct flashchip));
+ *flash->chip = *chip;
flash->mst = mst;
if (map_flash(flash) != 0)
@@ -1251,7 +1128,11 @@ int probe_flash(struct registered_master *mst, int startchip, struct flashctx *f
if (force)
break;
- if (flash->chip->probe(flash) != 1)
+ if (probe_func == &probe_w29ee011)
+ if (!w29ee011_can_override(flash->chip->name, chip_to_probe))
+ goto notfound;
+
+ if (probe_func(flash) != 1)
goto notfound;
/* If this is the first chip found, accept it.
@@ -1300,36 +1181,26 @@ notfound:
if (!flash->chip)
return -1;
- /* Fill fallback layout covering the whole chip. */
- struct single_layout *const fallback = &flash->fallback_layout;
- fallback->base.entries = &fallback->entry;
- fallback->base.num_entries = 1;
- fallback->entry.start = 0;
- fallback->entry.end = flash->chip->total_size * 1024 - 1;
- fallback->entry.included = true;
- fallback->entry.name = strdup("complete flash");
- if (!fallback->entry.name) {
- msg_cerr("Failed to probe chip: %s\n", strerror(errno));
+ if (init_default_layout(flash) < 0)
return -1;
- }
tmp = flashbuses_to_text(flash->chip->bustype);
msg_cinfo("%s %s flash chip \"%s\" (%d kB, %s) ", force ? "Assuming" : "Found",
- flash->chip->vendor, flash->chip->name, flash->chip->total_size, tmp);
+ flash->chip->vendor, flash->chip->name, flash->chip->total_size, tmp ? tmp : "?");
free(tmp);
-#if CONFIG_INTERNAL == 1
- if (programmer_table[programmer].map_flash_region == physmap)
+ if (master_uses_physmap(mst))
msg_cinfo("mapped at physical address 0x%0*" PRIxPTR ".\n",
PRIxPTR_WIDTH, flash->physical_memory);
else
-#endif
- msg_cinfo("on %s.\n", programmer_table[programmer].name);
+ msg_cinfo("on %s.\n", programmer->name);
/* Flash registers may more likely not be mapped if the chip was forced.
* Lock info may be stored in registers, so avoid lock info printing. */
- if (!force)
- if (flash->chip->printlock)
- flash->chip->printlock(flash);
+ if (!force) {
+ printlockfunc_t *printlock = lookup_printlock_func_ptr(flash);
+ if (printlock)
+ printlock(flash);
+ }
/* Get out of the way for later runs. */
unmap_flash(flash);
@@ -1338,127 +1209,31 @@ notfound:
return chip - flashchips;
}
-int read_buf_from_file(unsigned char *buf, unsigned long size,
- const char *filename)
-{
-#ifdef __LIBPAYLOAD__
- msg_gerr("Error: No file I/O support in libpayload\n");
- return 1;
-#else
- int ret = 0;
-
- FILE *image;
- if ((image = fopen(filename, "rb")) == NULL) {
- msg_gerr("Error: opening file \"%s\" failed: %s\n", filename, strerror(errno));
- return 1;
- }
-
- struct stat image_stat;
- if (fstat(fileno(image), &image_stat) != 0) {
- msg_gerr("Error: getting metadata of file \"%s\" failed: %s\n", filename, strerror(errno));
- ret = 1;
- goto out;
- }
- if (image_stat.st_size != (intmax_t)size) {
- msg_gerr("Error: Image size (%jd B) doesn't match the flash chip's size (%lu B)!\n",
- (intmax_t)image_stat.st_size, size);
- ret = 1;
- goto out;
- }
-
- unsigned long numbytes = fread(buf, 1, size, image);
- if (numbytes != size) {
- msg_gerr("Error: Failed to read complete file. Got %ld bytes, "
- "wanted %ld!\n", numbytes, size);
- ret = 1;
- }
-out:
- (void)fclose(image);
- return ret;
-#endif
-}
-
-int write_buf_to_file(const unsigned char *buf, unsigned long size, const char *filename)
+/**
+ * @brief Reads the included layout regions into a buffer.
+ *
+ * If there is no layout set in the given flash context, the whole chip will
+ * be read.
+ *
+ * @param flashctx Flash context to be used.
+ * @param buffer Buffer of full chip size to read into.
+ * @return 0 on success,
+ * 1 if any read fails.
+ */
+static int read_by_layout(struct flashctx *const flashctx, uint8_t *const buffer)
{
-#ifdef __LIBPAYLOAD__
- msg_gerr("Error: No file I/O support in libpayload\n");
- return 1;
-#else
- FILE *image;
- int ret = 0;
-
- if (!filename) {
- msg_gerr("No filename specified.\n");
- return 1;
- }
- if ((image = fopen(filename, "wb")) == NULL) {
- msg_gerr("Error: opening file \"%s\" failed: %s\n", filename, strerror(errno));
- return 1;
- }
-
- unsigned long numbytes = fwrite(buf, 1, size, image);
- if (numbytes != size) {
- msg_gerr("Error: file %s could not be written completely.\n", filename);
- ret = 1;
- goto out;
- }
- if (fflush(image)) {
- msg_gerr("Error: flushing file \"%s\" failed: %s\n", filename, strerror(errno));
- ret = 1;
- }
- // Try to fsync() only regular files and if that function is available at all (e.g. not on MinGW).
-#if defined(_POSIX_FSYNC) && (_POSIX_FSYNC != -1)
- struct stat image_stat;
- if (fstat(fileno(image), &image_stat) != 0) {
- msg_gerr("Error: getting metadata of file \"%s\" failed: %s\n", filename, strerror(errno));
- ret = 1;
- goto out;
- }
- if (S_ISREG(image_stat.st_mode)) {
- if (fsync(fileno(image))) {
- msg_gerr("Error: fsyncing file \"%s\" failed: %s\n", filename, strerror(errno));
- ret = 1;
- }
- }
-#endif
-out:
- if (fclose(image)) {
- msg_gerr("Error: closing file \"%s\" failed: %s\n", filename, strerror(errno));
- ret = 1;
- }
- return ret;
-#endif
-}
+ const struct flashrom_layout *const layout = get_layout(flashctx);
+ const struct romentry *entry = NULL;
-static int read_by_layout(struct flashctx *, uint8_t *);
-int read_flash_to_file(struct flashctx *flash, const char *filename)
-{
- unsigned long size = flash->chip->total_size * 1024;
- unsigned char *buf = calloc(size, sizeof(unsigned char));
- int ret = 0;
+ while ((entry = layout_next_included(layout, entry))) {
+ const struct flash_region *region = &entry->region;
+ const chipoff_t region_start = region->start;
+ const chipsize_t region_len = region->end - region->start + 1;
- msg_cinfo("Reading flash... ");
- if (!buf) {
- msg_gerr("Memory allocation failed!\n");
- msg_cinfo("FAILED.\n");
- return 1;
- }
- if (!flash->chip->read) {
- msg_cerr("No read function available for this flash chip.\n");
- ret = 1;
- goto out_free;
- }
- if (read_by_layout(flash, buf)) {
- msg_cerr("Read operation failed!\n");
- ret = 1;
- goto out_free;
+ if (read_flash(flashctx, buffer + region_start, region_start, region_len))
+ return 1;
}
-
- ret = write_buf_to_file(buf, size, filename);
-out_free:
- free(buf);
- msg_cinfo("%s.\n", ret ? "FAILED" : "done");
- return ret;
+ return 0;
}
/* Even if an error is found, the function will keep going and check the rest. */
@@ -1466,10 +1241,12 @@ static int selfcheck_eraseblocks(const struct flashchip *chip)
{
int i, j, k;
int ret = 0;
+ unsigned int prev_eraseblock_count = chip->total_size * 1024;
for (k = 0; k < NUM_ERASEFUNCTIONS; k++) {
unsigned int done = 0;
struct block_eraser eraser = chip->block_erasers[k];
+ unsigned int curr_eraseblock_count = 0;
for (i = 0; i < NUM_ERASEREGIONS; i++) {
/* Blocks with zero size are bugs in flashchips.c. */
@@ -1492,6 +1269,7 @@ static int selfcheck_eraseblocks(const struct flashchip *chip)
}
done += eraser.eraseblocks[i].count *
eraser.eraseblocks[i].size;
+ curr_eraseblock_count += eraser.eraseblocks[i].count;
}
/* Empty eraseblock definition with erase function. */
if (!done && eraser.block_erase)
@@ -1523,61 +1301,17 @@ static int selfcheck_eraseblocks(const struct flashchip *chip)
ret = 1;
}
}
+ if (curr_eraseblock_count > prev_eraseblock_count) {
+ msg_gerr("ERROR: Flash chip %s erase function %i is not "
+ "in order. Please report a bug at flashrom@flashrom.org\n",
+ chip->name, k);
+ ret = 1;
+ }
+ prev_eraseblock_count = curr_eraseblock_count;
}
return ret;
}
-static int check_block_eraser(const struct flashctx *flash, int k, int log)
-{
- struct block_eraser eraser = flash->chip->block_erasers[k];
-
- if (!eraser.block_erase && !eraser.eraseblocks[0].count) {
- if (log)
- msg_cdbg("not defined. ");
- return 1;
- }
- if (!eraser.block_erase && eraser.eraseblocks[0].count) {
- if (log)
- msg_cdbg("eraseblock layout is known, but matching "
- "block erase function is not implemented. ");
- return 1;
- }
- if (eraser.block_erase && !eraser.eraseblocks[0].count) {
- if (log)
- msg_cdbg("block erase function found, but "
- "eraseblock layout is not defined. ");
- return 1;
- }
- // TODO: Once erase functions are annotated with allowed buses, check that as well.
- return 0;
-}
-
-/**
- * @brief Reads the included layout regions into a buffer.
- *
- * If there is no layout set in the given flash context, the whole chip will
- * be read.
- *
- * @param flashctx Flash context to be used.
- * @param buffer Buffer of full chip size to read into.
- * @return 0 on success,
- * 1 if any read fails.
- */
-static int read_by_layout(struct flashctx *const flashctx, uint8_t *const buffer)
-{
- const struct flashrom_layout *const layout = get_layout(flashctx);
- const struct romentry *entry = NULL;
-
- while ((entry = layout_next_included(layout, entry))) {
- const chipoff_t region_start = entry->start;
- const chipsize_t region_len = entry->end - entry->start + 1;
-
- if (flashctx->chip->read(flashctx, buffer + region_start, region_start, region_len))
- return 1;
- }
- return 0;
-}
-
typedef int (*erasefn_t)(struct flashctx *, unsigned int addr, unsigned int len);
/**
* @private
@@ -1601,11 +1335,12 @@ struct walk_info {
chipoff_t erase_end;
};
/* returns 0 on success, 1 to retry with another erase function, 2 for immediate abort */
-typedef int (*per_blockfn_t)(struct flashctx *, const struct walk_info *, erasefn_t);
+typedef int (*per_blockfn_t)(struct flashctx *, const struct walk_info *, erasefn_t, bool *);
static int walk_eraseblocks(struct flashctx *const flashctx,
struct walk_info *const info,
- const size_t erasefunction, const per_blockfn_t per_blockfn)
+ const size_t erasefunction, const per_blockfn_t per_blockfn,
+ bool *all_skipped)
{
int ret;
size_t i, j;
@@ -1630,9 +1365,10 @@ static int walk_eraseblocks(struct flashctx *const flashctx,
first = false;
else
msg_cdbg(", ");
- msg_cdbg("0x%06x-0x%06x:", info->erase_start, info->erase_end);
+ msg_cdbg("0x%06"PRIx32"-0x%06"PRIx32":", info->erase_start, info->erase_end);
- ret = per_blockfn(flashctx, info, eraser->block_erase);
+ erasefunc_t *erase_func = lookup_erase_func_ptr(eraser);
+ ret = per_blockfn(flashctx, info, erase_func, all_skipped);
if (ret)
return ret;
}
@@ -1644,17 +1380,18 @@ static int walk_eraseblocks(struct flashctx *const flashctx,
}
static int walk_by_layout(struct flashctx *const flashctx, struct walk_info *const info,
- const per_blockfn_t per_blockfn)
+ const per_blockfn_t per_blockfn, bool *all_skipped)
{
const struct flashrom_layout *const layout = get_layout(flashctx);
const struct romentry *entry = NULL;
- all_skipped = true;
+ *all_skipped = true;
msg_cinfo("Erasing and writing flash chip... ");
while ((entry = layout_next_included(layout, entry))) {
- info->region_start = entry->start;
- info->region_end = entry->end;
+ const struct flash_region *region = &entry->region;
+ info->region_start = region->start;
+ info->region_end = region->end;
size_t j;
int error = 1; /* retry as long as it's 1 */
@@ -1665,7 +1402,7 @@ static int walk_by_layout(struct flashctx *const flashctx, struct walk_info *con
if (check_block_eraser(flashctx, j, 1))
continue;
- error = walk_eraseblocks(flashctx, info, j, per_blockfn);
+ error = walk_eraseblocks(flashctx, info, j, per_blockfn, all_skipped);
if (error != 1)
break;
@@ -1689,14 +1426,15 @@ static int walk_by_layout(struct flashctx *const flashctx, struct walk_info *con
return 1;
}
}
- if (all_skipped)
+ if (*all_skipped)
msg_cinfo("\nWarning: Chip content is identical to the requested image.\n");
msg_cinfo("Erase/write done.\n");
return 0;
}
static int erase_block(struct flashctx *const flashctx,
- const struct walk_info *const info, const erasefn_t erasefn)
+ const struct walk_info *const info, const erasefn_t erasefn,
+ bool *all_skipped)
{
const unsigned int erase_len = info->erase_end + 1 - info->erase_start;
const bool region_unaligned = info->region_start > info->erase_start ||
@@ -1724,7 +1462,7 @@ static int erase_block(struct flashctx *const flashctx,
if (info->region_start > info->erase_start) {
const chipoff_t start = info->erase_start;
const chipsize_t len = info->region_start - info->erase_start;
- if (flashctx->chip->read(flashctx, backup_contents, start, len)) {
+ if (read_flash(flashctx, backup_contents, start, len)) {
msg_cerr("Can't read! Aborting.\n");
goto _free_ret;
}
@@ -1734,7 +1472,7 @@ static int erase_block(struct flashctx *const flashctx,
const chipoff_t start = info->region_end + 1;
const chipoff_t rel_start = start - info->erase_start; /* within this erase block */
const chipsize_t len = info->erase_end - info->region_end;
- if (flashctx->chip->read(flashctx, backup_contents + rel_start, start, len)) {
+ if (read_flash(flashctx, backup_contents + rel_start, start, len)) {
msg_cerr("Can't read! Aborting.\n");
goto _free_ret;
}
@@ -1742,16 +1480,42 @@ static int erase_block(struct flashctx *const flashctx,
}
ret = 1;
- all_skipped = false;
+ *all_skipped = false;
msg_cdbg("E");
- if (erasefn(flashctx, info->erase_start, erase_len))
- goto _free_ret;
- if (check_erased_range(flashctx, info->erase_start, erase_len)) {
- msg_cerr("ERASE FAILED!\n");
- goto _free_ret;
+
+ if (!flashctx->flags.skip_unwritable_regions) {
+ if (check_for_unwritable_regions(flashctx, info->erase_start, erase_len))
+ goto _free_ret;
}
+ unsigned int len;
+ for (unsigned int addr = info->erase_start; addr < info->erase_start + erase_len; addr += len) {
+ struct flash_region region;
+ get_flash_region(flashctx, addr, &region);
+
+ len = min(info->erase_start + erase_len, region.end) - addr;
+
+ if (region.write_prot) {
+ msg_gdbg("%s: cannot erase inside %s region (%#08"PRIx32"..%#08"PRIx32"), skipping range (%#08x..%#08x).\n",
+ __func__, region.name, region.start, region.end - 1, addr, addr + len - 1);
+ free(region.name);
+ continue;
+ }
+
+ msg_gdbg("%s: %s region (%#08"PRIx32"..%#08"PRIx32") is writable, erasing range (%#08x..%#08x).\n",
+ __func__, region.name, region.start, region.end - 1, addr, addr + len - 1);
+ free(region.name);
+
+ if (erasefn(flashctx, addr, len))
+ goto _free_ret;
+ if (check_erased_range(flashctx, addr, len)) {
+ msg_cerr("ERASE FAILED!\n");
+ goto _free_ret;
+ }
+ }
+
+
if (region_unaligned) {
unsigned int starthere = 0, lenhere = 0, writecount = 0;
/* get_next_write() sets starthere to a new value after the call. */
@@ -1760,7 +1524,7 @@ static int erase_block(struct flashctx *const flashctx,
if (!writecount++)
msg_cdbg("W");
/* Needs the partial write function signature. */
- if (flashctx->chip->write(flashctx, backup_contents + starthere,
+ if (write_flash(flashctx, backup_contents + starthere,
info->erase_start + starthere, lenhere))
goto _free_ret;
starthere += lenhere;
@@ -1782,18 +1546,69 @@ _free_ret:
* be erased.
*
* @param flashctx Flash context to be used.
- * @param buffer Buffer of full chip size to read into.
* @return 0 on success,
* 1 if all available erase functions failed.
*/
-static int erase_by_layout(struct flashctx *const flashctx)
+static int erase_by_layout_legacy(struct flashctx *const flashctx)
{
struct walk_info info = { 0 };
- return walk_by_layout(flashctx, &info, &erase_block);
+ bool all_skipped = true;
+ return walk_by_layout(flashctx, &info, &erase_block, &all_skipped);
+}
+
+static int erase_by_layout_new(struct flashctx *const flashctx)
+{
+ bool all_skipped = true;
+ const uint32_t flash_size = flashctx->chip->total_size * 1024;
+ uint8_t* curcontents = malloc(flash_size);
+ uint8_t* newcontents = malloc(flash_size);
+ struct erase_layout *erase_layout;
+ create_erase_layout(flashctx, &erase_layout);
+ int ret = 0;
+
+ //erase layout creation failed
+ if (!erase_layout) {
+ ret = 1;
+ goto _ret;
+ }
+
+ //not enough memory
+ if (!curcontents || !newcontents) {
+ ret = 1;
+ goto _ret;
+ }
+
+ memset(curcontents, ~ERASED_VALUE(flashctx), flash_size);
+ memset(newcontents, ERASED_VALUE(flashctx), flash_size);
+
+ const struct flashrom_layout *const flash_layout = get_layout(flashctx);
+ const struct romentry *entry = NULL;
+ while ((entry = layout_next_included(flash_layout, entry))) {
+ ret = erase_write(flashctx, entry->region.start, entry->region.end, curcontents, newcontents, erase_layout, &all_skipped);
+ if (ret) {
+ ret = 1;
+ msg_cerr("Erase Failed");
+ goto _ret;
+ }
+ }
+
+_ret:
+ free(curcontents);
+ free(newcontents);
+ free_erase_layout(erase_layout, count_usable_erasers(flashctx));
+ return ret;
+}
+
+static int erase_by_layout(struct flashctx *const flashctx)
+{
+ if (use_legacy_erase_path)
+ return erase_by_layout_legacy(flashctx);
+ return erase_by_layout_new(flashctx);
}
static int read_erase_write_block(struct flashctx *const flashctx,
- const struct walk_info *const info, const erasefn_t erasefn)
+ const struct walk_info *const info, const erasefn_t erasefn,
+ bool *all_skipped)
{
const chipsize_t erase_len = info->erase_end + 1 - info->erase_start;
const bool region_unaligned = info->region_start > info->erase_start ||
@@ -1822,7 +1637,7 @@ static int read_erase_write_block(struct flashctx *const flashctx,
if (info->region_start > info->erase_start) {
const chipoff_t start = info->erase_start;
const chipsize_t len = info->region_start - info->erase_start;
- if (flashctx->chip->read(flashctx, newc, start, len)) {
+ if (read_flash(flashctx, newc, start, len)) {
msg_cerr("Can't read! Aborting.\n");
goto _free_ret;
}
@@ -1833,7 +1648,7 @@ static int read_erase_write_block(struct flashctx *const flashctx,
const chipoff_t start = info->region_end + 1;
const chipoff_t rel_start = start - info->erase_start; /* within this erase block */
const chipsize_t len = info->erase_end - info->region_end;
- if (flashctx->chip->read(flashctx, newc + rel_start, start, len)) {
+ if (read_flash(flashctx, newc + rel_start, start, len)) {
msg_cerr("Can't read! Aborting.\n");
goto _free_ret;
}
@@ -1851,7 +1666,7 @@ static int read_erase_write_block(struct flashctx *const flashctx,
const uint8_t erased_value = ERASED_VALUE(flashctx);
if (!(flashctx->chip->feature_bits & FEATURE_NO_ERASE) &&
need_erase(curcontents, newcontents, erase_len, flashctx->chip->gran, erased_value)) {
- if (erase_block(flashctx, info, erasefn))
+ if (erase_block(flashctx, info, erasefn, all_skipped))
goto _free_ret;
/* Erase was successful. Adjust curcontents. */
memset(curcontents, erased_value, erase_len);
@@ -1865,7 +1680,7 @@ static int read_erase_write_block(struct flashctx *const flashctx,
if (!writecount++)
msg_cdbg("W");
/* Needs the partial write function signature. */
- if (flashctx->chip->write(flashctx, newcontents + starthere,
+ if (write_flash(flashctx, newcontents + starthere,
info->erase_start + starthere, lenhere))
goto _free_ret;
starthere += lenhere;
@@ -1874,7 +1689,7 @@ static int read_erase_write_block(struct flashctx *const flashctx,
if (skipped)
msg_cdbg("S");
else
- all_skipped = false;
+ *all_skipped = false;
/* Update curcontents, other regions with overlapping erase blocks
might rely on this. */
@@ -1899,13 +1714,58 @@ _free_ret:
* @return 0 on success,
* 1 if anything has gone wrong.
*/
-static int write_by_layout(struct flashctx *const flashctx,
- void *const curcontents, const void *const newcontents)
+static int write_by_layout_legacy(struct flashctx *const flashctx,
+ void *const curcontents, const void *const newcontents,
+ bool *all_skipped)
{
struct walk_info info;
info.curcontents = curcontents;
info.newcontents = newcontents;
- return walk_by_layout(flashctx, &info, read_erase_write_block);
+ return walk_by_layout(flashctx, &info, read_erase_write_block, all_skipped);
+}
+
+static int write_by_layout_new(struct flashctx *const flashctx,
+ void *const curcontents, const void *const newcontents,
+ bool *all_skipped)
+{
+ const int erasefn_count = count_usable_erasers(flashctx);
+ int ret = 1;
+
+ const struct flashrom_layout *const flash_layout = get_layout(flashctx);
+ struct erase_layout *erase_layout;
+ create_erase_layout(flashctx, &erase_layout);
+
+ if (!flash_layout) {
+ goto _ret;
+ }
+ if (!erase_layout) {
+ goto _ret;
+ }
+
+ const struct romentry *entry = NULL;
+ while ((entry = layout_next_included(flash_layout, entry))) {
+ ret = erase_write(flashctx, entry->region.start,
+ entry->region.end,
+ curcontents,
+ (uint8_t *)newcontents,
+ erase_layout, all_skipped);
+ if (ret) {
+ msg_cerr("Write Failed!");
+ goto _ret;
+ }
+ }
+_ret:
+ free_erase_layout(erase_layout, erasefn_count);
+ return ret;
+}
+
+static int write_by_layout(struct flashctx *const flashctx,
+ uint8_t *const curcontents, const uint8_t *const newcontents,
+ bool *all_skipped)
+{
+ if (use_legacy_erase_path)
+ return write_by_layout_legacy(flashctx, curcontents, newcontents, all_skipped);
+ return write_by_layout_new(flashctx, curcontents, newcontents, all_skipped);
}
/**
@@ -1915,23 +1775,26 @@ static int write_by_layout(struct flashctx *const flashctx,
* contents will be compared.
*
* @param flashctx Flash context to be used.
+ * @param layout Flash layout information.
* @param curcontents A buffer of full chip size to read current chip contents into.
* @param newcontents The new image to compare to.
* @return 0 on success,
* 1 if reading failed,
* 3 if the contents don't match.
*/
-static int verify_by_layout(struct flashctx *const flashctx,
- void *const curcontents, const uint8_t *const newcontents)
+static int verify_by_layout(
+ struct flashctx *const flashctx,
+ const struct flashrom_layout *const layout,
+ void *const curcontents, const uint8_t *const newcontents)
{
- const struct flashrom_layout *const layout = get_layout(flashctx);
const struct romentry *entry = NULL;
while ((entry = layout_next_included(layout, entry))) {
- const chipoff_t region_start = entry->start;
- const chipsize_t region_len = entry->end - entry->start + 1;
+ const struct flash_region *region = &entry->region;
+ const chipoff_t region_start = region->start;
+ const chipsize_t region_len = region->end - region->start + 1;
- if (flashctx->chip->read(flashctx, curcontents + region_start, region_start, region_len))
+ if (read_flash(flashctx, curcontents + region_start, region_start, region_len))
return 1;
if (compare_range(newcontents + region_start, curcontents + region_start,
region_start, region_len))
@@ -1940,37 +1803,42 @@ static int verify_by_layout(struct flashctx *const flashctx,
return 0;
}
+static bool is_internal_programmer()
+{
+#if CONFIG_INTERNAL == 1
+ return programmer == &programmer_internal;
+#else
+ return false;
+#endif
+}
+
static void nonfatal_help_message(void)
{
msg_gerr("Good, writing to the flash chip apparently didn't do anything.\n");
-#if CONFIG_INTERNAL == 1
- if (programmer == PROGRAMMER_INTERNAL)
+ if (is_internal_programmer())
msg_gerr("This means we have to add special support for your board, programmer or flash\n"
- "chip. Please report this on IRC at chat.freenode.net (channel #flashrom) or\n"
- "mail flashrom@flashrom.org, thanks!\n"
+ "chip. Please report this to the mailing list at flashrom@flashrom.org or on\n"
+ "IRC (see https://www.flashrom.org/Contact for details), thanks!\n"
"-------------------------------------------------------------------------------\n"
"You may now reboot or simply leave the machine running.\n");
else
-#endif
msg_gerr("Please check the connections (especially those to write protection pins) between\n"
"the programmer and the flash chip. If you think the error is caused by flashrom\n"
- "please report this on IRC at chat.freenode.net (channel #flashrom) or\n"
- "mail flashrom@flashrom.org, thanks!\n");
+ "please report this to the mailing list at flashrom@flashrom.org or on IRC (see\n"
+ "https://www.flashrom.org/Contact for details), thanks!\n");
}
-static void emergency_help_message(void)
+void emergency_help_message(void)
{
msg_gerr("Your flash chip is in an unknown state.\n");
-#if CONFIG_INTERNAL == 1
- if (programmer == PROGRAMMER_INTERNAL)
- msg_gerr("Get help on IRC at chat.freenode.net (channel #flashrom) or\n"
- "mail flashrom@flashrom.org with the subject \"FAILED: <your board name>\"!\n"
+ if (is_internal_programmer())
+ msg_gerr("Get help on IRC (see https://www.flashrom.org/Contact) or mail\n"
+ "flashrom@flashrom.org with the subject \"FAILED: <your board name>\"!"
"-------------------------------------------------------------------------------\n"
"DO NOT REBOOT OR POWEROFF!\n");
else
-#endif
- msg_gerr("Please report this on IRC at chat.freenode.net (channel #flashrom) or\n"
- "mail flashrom@flashrom.org, thanks!\n");
+ msg_gerr("Please report this to the mailing list at flashrom@flashrom.org or\n"
+ "on IRC (see https://www.flashrom.org/Contact for details), thanks!\n");
}
void list_programmers_linebreak(int startcol, int cols, int paren)
@@ -1978,11 +1846,11 @@ void list_programmers_linebreak(int startcol, int cols, int paren)
const char *pname;
int pnamelen;
int remaining = 0, firstline = 1;
- enum programmer p;
+ size_t p;
int i;
- for (p = 0; p < PROGRAMMER_INVALID; p++) {
- pname = programmer_table[p].name;
+ for (p = 0; p < programmer_table_size; p++) {
+ pname = programmer_table[p]->name;
pnamelen = strlen(pname);
if (remaining - pnamelen - 2 < 0) {
if (firstline)
@@ -2002,7 +1870,7 @@ void list_programmers_linebreak(int startcol, int cols, int paren)
}
msg_ginfo("%s", pname);
remaining -= pnamelen;
- if (p < PROGRAMMER_INVALID - 1) {
+ if (p < programmer_table_size - 1) {
msg_ginfo(",");
remaining--;
} else {
@@ -2012,147 +1880,44 @@ void list_programmers_linebreak(int startcol, int cols, int paren)
}
}
-static void print_sysinfo(void)
-{
-#if IS_WINDOWS
- SYSTEM_INFO si;
- OSVERSIONINFOEX osvi;
-
- memset(&si, 0, sizeof(SYSTEM_INFO));
- memset(&osvi, 0, sizeof(OSVERSIONINFOEX));
- msg_ginfo(" on Windows");
- /* Tell Windows which version of the structure we want. */
- osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
- if (GetVersionEx((OSVERSIONINFO*) &osvi))
- msg_ginfo(" %lu.%lu", osvi.dwMajorVersion, osvi.dwMinorVersion);
- else
- msg_ginfo(" unknown version");
- GetSystemInfo(&si);
- switch (si.wProcessorArchitecture) {
- case PROCESSOR_ARCHITECTURE_AMD64:
- msg_ginfo(" (x86_64)");
- break;
- case PROCESSOR_ARCHITECTURE_INTEL:
- msg_ginfo(" (x86)");
- break;
- default:
- msg_ginfo(" (unknown arch)");
- break;
- }
-#elif HAVE_UTSNAME == 1
- struct utsname osinfo;
-
- uname(&osinfo);
- msg_ginfo(" on %s %s (%s)", osinfo.sysname, osinfo.release,
- osinfo.machine);
-#else
- msg_ginfo(" on unknown machine");
-#endif
-}
-
-void print_buildinfo(void)
-{
- msg_gdbg("flashrom was built with");
-#if NEED_PCI == 1
-#ifdef PCILIB_VERSION
- msg_gdbg(" libpci %s,", PCILIB_VERSION);
-#else
- msg_gdbg(" unknown PCI library,");
-#endif
-#endif
-#ifdef __clang__
- msg_gdbg(" LLVM Clang");
-#ifdef __clang_version__
- msg_gdbg(" %s,", __clang_version__);
-#else
- msg_gdbg(" unknown version (before r102686),");
-#endif
-#elif defined(__GNUC__)
- msg_gdbg(" GCC");
-#ifdef __VERSION__
- msg_gdbg(" %s,", __VERSION__);
-#else
- msg_gdbg(" unknown version,");
-#endif
-#else
- msg_gdbg(" unknown compiler,");
-#endif
-#if defined (__FLASHROM_LITTLE_ENDIAN__)
- msg_gdbg(" little endian");
-#elif defined (__FLASHROM_BIG_ENDIAN__)
- msg_gdbg(" big endian");
-#else
-#error Endianness could not be determined
-#endif
- msg_gdbg("\n");
-}
-
-void print_version(void)
-{
- msg_ginfo("flashrom %s", flashrom_version);
- print_sysinfo();
- msg_ginfo("\n");
-}
-
-void print_banner(void)
-{
- msg_ginfo("flashrom is free software, get the source code at "
- "https://flashrom.org\n");
- msg_ginfo("\n");
-}
-
int selfcheck(void)
{
unsigned int i;
int ret = 0;
- /* Safety check. Instead of aborting after the first error, check
- * if more errors exist.
- */
- if (ARRAY_SIZE(programmer_table) - 1 != PROGRAMMER_INVALID) {
- msg_gerr("Programmer table miscompilation!\n");
- ret = 1;
- }
- for (i = 0; i < PROGRAMMER_INVALID; i++) {
- const struct programmer_entry p = programmer_table[i];
- if (p.name == NULL) {
+ for (i = 0; i < programmer_table_size; i++) {
+ const struct programmer_entry *const p = programmer_table[i];
+ if (p == NULL) {
+ msg_gerr("Programmer with index %d is NULL instead of a valid pointer!\n", i);
+ ret = 1;
+ continue;
+ }
+ if (p->name == NULL) {
msg_gerr("All programmers need a valid name, but the one with index %d does not!\n", i);
ret = 1;
/* This might hide other problems with this programmer, but allows for better error
* messages below without jumping through hoops. */
continue;
}
- switch (p.type) {
+ switch (p->type) {
case USB:
case PCI:
case OTHER:
- if (p.devs.note == NULL) {
- if (strcmp("internal", p.name) == 0)
+ if (p->devs.note == NULL) {
+ if (strcmp("internal", p->name) == 0)
break; /* This one has its device list stored separately. */
msg_gerr("Programmer %s has neither a device list nor a textual description!\n",
- p.name);
+ p->name);
ret = 1;
}
break;
default:
- msg_gerr("Programmer %s does not have a valid type set!\n", p.name);
+ msg_gerr("Programmer %s does not have a valid type set!\n", p->name);
ret = 1;
break;
}
- if (p.init == NULL) {
- msg_gerr("Programmer %s does not have a valid init function!\n", p.name);
- ret = 1;
- }
- if (p.delay == NULL) {
- msg_gerr("Programmer %s does not have a valid delay function!\n", p.name);
- ret = 1;
- }
- if (p.map_flash_region == NULL) {
- msg_gerr("Programmer %s does not have a valid map_flash_region function!\n", p.name);
- ret = 1;
- }
- if (p.unmap_flash_region == NULL) {
- msg_gerr("Programmer %s does not have a valid unmap_flash_region function!\n", p.name);
+ if (p->init == NULL) {
+ msg_gerr("Programmer %s does not have a valid init function!\n", p->name);
ret = 1;
}
}
@@ -2189,8 +1954,8 @@ int selfcheck(void)
return ret;
}
-/* FIXME: This function signature needs to be improved once doit() has a better
- * function signature.
+/* FIXME: This function signature needs to be improved once prepare_flash_access()
+ * has a better function signature.
*/
static int chip_safety_check(const struct flashctx *flash, int force,
int read_it, int write_it, int erase_it, int verify_it)
@@ -2216,7 +1981,7 @@ static int chip_safety_check(const struct flashctx *flash, int force,
return 1;
msg_cerr("Continuing anyway.\n");
}
- if (!chip->read) {
+ if (!lookup_read_func_ptr(chip)) {
msg_cerr("flashrom has no read function for this "
"flash chip.\n");
return 1;
@@ -2251,7 +2016,7 @@ static int chip_safety_check(const struct flashctx *flash, int force,
return 1;
msg_cerr("Continuing anyway.\n");
}
- if (!chip->write) {
+ if (!lookup_write_func_ptr(chip)) {
msg_cerr("flashrom has no write function for this "
"flash chip.\n");
return 1;
@@ -2260,6 +2025,78 @@ static int chip_safety_check(const struct flashctx *flash, int force,
return 0;
}
+static int restore_flash_wp(struct flashctx *const flash, void *data)
+{
+ struct flashrom_wp_cfg *wp_cfg = data;
+ enum flashrom_wp_result ret = flashrom_wp_write_cfg(flash, wp_cfg);
+ flashrom_wp_cfg_release(wp_cfg);
+
+ return (ret == FLASHROM_WP_OK) ? 0 : -1;
+}
+
+static int save_initial_flash_wp(struct flashctx *const flash)
+{
+ struct flashrom_wp_cfg *initial_wp_cfg;
+ if (flashrom_wp_cfg_new(&initial_wp_cfg) != FLASHROM_WP_OK)
+ return -1;
+
+ if (flashrom_wp_read_cfg(initial_wp_cfg, flash) != FLASHROM_WP_OK) {
+ flashrom_wp_cfg_release(initial_wp_cfg);
+ return -1;
+ }
+
+ if (register_chip_restore(restore_flash_wp, flash, initial_wp_cfg)) {
+ flashrom_wp_cfg_release(initial_wp_cfg);
+ return -1;
+ }
+ return 0;
+}
+
+static int unlock_flash_wp(struct flashctx *const flash,
+ const bool write_it, const bool erase_it)
+
+{
+ int ret = 0;
+
+ /* WP only disables write protection, so only use WP to unlock
+ * for write/erase operations.
+ *
+ * For read/verify operations, we still call the chip's unlock
+ * function, which may disable read locks if the chip has them.
+ */
+ if (!write_it && !erase_it) {
+ msg_cdbg("Skipping writeprotect-based unlocking for read/verify operations.\n");
+ return -1;
+ }
+
+ /* Save original WP state to be restored later */
+ if (save_initial_flash_wp(flash)) {
+ ret = -1;
+ goto warn_out;
+ }
+
+ /* Disable WP */
+ struct flashrom_wp_cfg *unlocked_wp_cfg;
+ if (flashrom_wp_cfg_new(&unlocked_wp_cfg) != FLASHROM_WP_OK) {
+ ret = -1;
+ goto warn_out;
+ }
+
+ flashrom_wp_set_range(unlocked_wp_cfg, 0, 0);
+ flashrom_wp_set_mode(unlocked_wp_cfg, FLASHROM_WP_MODE_DISABLED);
+ if (flashrom_wp_write_cfg(flash, unlocked_wp_cfg) != FLASHROM_WP_OK) {
+ ret = -1;
+ }
+
+ flashrom_wp_cfg_release(unlocked_wp_cfg);
+
+warn_out:
+ if (ret)
+ msg_cerr("Failed to unlock flash status reg with wp support.\n");
+
+ return ret;
+}
+
int prepare_flash_access(struct flashctx *const flash,
const bool read_it, const bool write_it,
const bool erase_it, const bool verify_it)
@@ -2269,7 +2106,7 @@ int prepare_flash_access(struct flashctx *const flash,
return 1;
}
- if (flash->layout == get_global_layout() && normalize_romentries(flash)) {
+ if (layout_sanity_checks(flash)) {
msg_cerr("Requested regions can not be handled. Aborting.\n");
return 1;
}
@@ -2277,14 +2114,28 @@ int prepare_flash_access(struct flashctx *const flash,
if (map_flash(flash) != 0)
return 1;
- /* Given the existence of read locks, we want to unlock for read,
- erase and write. */
- if (flash->chip->unlock)
- flash->chip->unlock(flash);
+ /* Initialize chip_restore_fn_count before chip unlock calls. */
+ flash->chip_restore_fn_count = 0;
+
+ int ret = 1;
+ if (flash->chip->decode_range != NO_DECODE_RANGE_FUNC ||
+ (flash->mst->buses_supported & BUS_PROG && flash->mst->opaque.wp_write_cfg)) {
+ ret = unlock_flash_wp(flash, write_it, erase_it);
+ }
+ /*
+ * Fall back to chip unlock function if we haven't already successfully
+ * unlocked using WP (e.g. WP unlocking failed, chip had no WP support,
+ * WP was skipped for read/verify ops).
+ *
+ * Given the existence of read locks, we want to unlock for read,
+ * erase, write, and verify.
+ */
+ blockprotect_func_t *bp_func = lookup_blockprotect_func_ptr(flash->chip);
+ if (ret && bp_func)
+ bp_func(flash);
flash->address_high_byte = -1;
flash->in_4ba_mode = false;
- flash->chip_restore_fn_count = 0;
/* Be careful about 4BA chips and broken masters */
if (flash->chip->total_size > 16 * 1024 && spi_master_no_4ba_modes(flash)) {
@@ -2297,8 +2148,7 @@ int prepare_flash_access(struct flashctx *const flash,
}
/* Enable/disable 4-byte addressing mode if flash chip supports it */
- if (flash->chip->feature_bits & (FEATURE_4BA_ENTER | FEATURE_4BA_ENTER_WREN | FEATURE_4BA_ENTER_EAR7)) {
- int ret;
+ if (spi_chip_4ba(flash)) {
if (spi_master_4ba(flash))
ret = spi_enter_4ba(flash);
else
@@ -2318,20 +2168,6 @@ void finalize_flash_access(struct flashctx *const flash)
unmap_flash(flash);
}
-/**
- * @addtogroup flashrom-flash
- * @{
- */
-
-/**
- * @brief Erase the specified ROM chip.
- *
- * If a layout is set in the given flash context, only included regions
- * will be erased.
- *
- * @param flashctx The context of the flash chip to erase.
- * @return 0 on success.
- */
int flashrom_flash_erase(struct flashctx *const flashctx)
{
if (prepare_flash_access(flashctx, false, false, true, false))
@@ -2344,26 +2180,6 @@ int flashrom_flash_erase(struct flashctx *const flashctx)
return ret;
}
-/** @} */ /* end flashrom-flash */
-
-/**
- * @defgroup flashrom-ops Operations
- * @{
- */
-
-/**
- * @brief Read the current image from the specified ROM chip.
- *
- * If a layout is set in the specified flash context, only included regions
- * will be read.
- *
- * @param flashctx The context of the flash chip.
- * @param buffer Target buffer to write image to.
- * @param buffer_len Size of target buffer in bytes.
- * @return 0 on success,
- * 2 if buffer_len is too short for the flash chip's contents,
- * or 1 on any other failure.
- */
int flashrom_image_read(struct flashctx *const flashctx, void *const buffer, const size_t buffer_len)
{
const size_t flash_size = flashctx->chip->total_size * 1024;
@@ -2398,12 +2214,13 @@ static void combine_image_by_layout(const struct flashctx *const flashctx,
chipoff_t start = 0;
while ((included = layout_next_included_region(layout, start))) {
- if (included->start > start) {
+ const struct flash_region *region = &included->region;
+ if (region->start > start) {
/* copy everything up to the start of this included region */
- memcpy(newcontents + start, oldcontents + start, included->start - start);
+ memcpy(newcontents + start, oldcontents + start, region->start - start);
}
/* skip this included region */
- start = included->end + 1;
+ start = region->end + 1;
if (start == 0)
return;
}
@@ -2413,28 +2230,14 @@ static void combine_image_by_layout(const struct flashctx *const flashctx,
memcpy(newcontents + start, oldcontents + start, copy_len);
}
-/**
- * @brief Write the specified image to the ROM chip.
- *
- * If a layout is set in the specified flash context, only erase blocks
- * containing included regions will be touched.
- *
- * @param flashctx The context of the flash chip.
- * @param buffer Source buffer to read image from (may be altered for full verification).
- * @param buffer_len Size of source buffer in bytes.
- * @param refbuffer If given, assume flash chip contains same data as `refbuffer`.
- * @return 0 on success,
- * 4 if buffer_len doesn't match the size of the flash chip,
- * 3 if write was tried but nothing has changed,
- * 2 if write failed and flash contents changed,
- * or 1 on any other failure.
- */
int flashrom_image_write(struct flashctx *const flashctx, void *const buffer, const size_t buffer_len,
const void *const refbuffer)
{
const size_t flash_size = flashctx->chip->total_size * 1024;
const bool verify_all = flashctx->flags.verify_whole_chip;
const bool verify = flashctx->flags.verify_after_write;
+ const struct flashrom_layout *const verify_layout =
+ verify_all ? get_default_layout(flashctx) : get_layout(flashctx);
if (buffer_len != flash_size)
return 4;
@@ -2453,7 +2256,7 @@ int flashrom_image_write(struct flashctx *const flashctx, void *const buffer, co
}
#if CONFIG_INTERNAL == 1
- if (programmer == PROGRAMMER_INTERNAL && cb_check_image(newcontents, flash_size) < 0) {
+ if (is_internal_programmer() && cb_check_image(newcontents, flash_size) < 0) {
if (flashctx->flags.force_boardmismatch) {
msg_pinfo("Proceeding anyway because user forced us to.\n");
} else {
@@ -2483,7 +2286,7 @@ int flashrom_image_write(struct flashctx *const flashctx, void *const buffer, co
*/
msg_cinfo("Reading old flash chip contents... ");
if (verify_all) {
- if (flashctx->chip->read(flashctx, oldcontents, 0, flash_size)) {
+ if (read_flash(flashctx, oldcontents, 0, flash_size)) {
msg_cinfo("FAILED.\n");
goto _finalize_ret;
}
@@ -2497,13 +2300,15 @@ int flashrom_image_write(struct flashctx *const flashctx, void *const buffer, co
msg_cinfo("done.\n");
}
- if (write_by_layout(flashctx, curcontents, newcontents)) {
+ bool all_skipped = true;
+
+ if (write_by_layout(flashctx, curcontents, newcontents, &all_skipped)) {
msg_cerr("Uh oh. Erase/write failed. ");
ret = 2;
if (verify_all) {
msg_cerr("Checking if anything has changed.\n");
msg_cinfo("Reading current flash chip contents... ");
- if (!flashctx->chip->read(flashctx, curcontents, 0, flash_size)) {
+ if (!read_flash(flashctx, curcontents, 0, flash_size)) {
msg_cinfo("done.\n");
if (!memcmp(oldcontents, curcontents, flash_size)) {
nonfatal_help_message();
@@ -2523,19 +2328,14 @@ int flashrom_image_write(struct flashctx *const flashctx, void *const buffer, co
/* Verify only if we actually changed something. */
if (verify && !all_skipped) {
- const struct flashrom_layout *const layout_bak = flashctx->layout;
-
msg_cinfo("Verifying flash... ");
/* Work around chips which need some time to calm down. */
- programmer_delay(1000*1000);
+ programmer_delay(flashctx, 1000*1000);
- if (verify_all) {
+ if (verify_all)
combine_image_by_layout(flashctx, newcontents, oldcontents);
- flashctx->layout = NULL;
- }
- ret = verify_by_layout(flashctx, curcontents, newcontents);
- flashctx->layout = layout_bak;
+ ret = verify_by_layout(flashctx, verify_layout, curcontents, newcontents);
/* If we tried to write, and verification now fails, we
might have an emergency situation. */
if (ret)
@@ -2555,22 +2355,9 @@ _free_ret:
return ret;
}
-/**
- * @brief Verify the ROM chip's contents with the specified image.
- *
- * If a layout is set in the specified flash context, only included regions
- * will be verified.
- *
- * @param flashctx The context of the flash chip.
- * @param buffer Source buffer to verify with.
- * @param buffer_len Size of source buffer in bytes.
- * @return 0 on success,
- * 3 if the chip's contents don't match,
- * 2 if buffer_len doesn't match the size of the flash chip,
- * or 1 on any other failure.
- */
int flashrom_image_verify(struct flashctx *const flashctx, const void *const buffer, const size_t buffer_len)
{
+ const struct flashrom_layout *const layout = get_layout(flashctx);
const size_t flash_size = flashctx->chip->total_size * 1024;
if (buffer_len != flash_size)
@@ -2589,7 +2376,7 @@ int flashrom_image_verify(struct flashctx *const flashctx, const void *const buf
goto _free_ret;
msg_cinfo("Verifying flash... ");
- ret = verify_by_layout(flashctx, curcontents, newcontents);
+ ret = verify_by_layout(flashctx, layout, curcontents, newcontents);
if (!ret)
msg_cinfo("VERIFIED.\n");
@@ -2598,84 +2385,3 @@ _free_ret:
free(curcontents);
return ret;
}
-
-/** @} */ /* end flashrom-ops */
-
-int do_read(struct flashctx *const flash, const char *const filename)
-{
- if (prepare_flash_access(flash, true, false, false, false))
- return 1;
-
- const int ret = read_flash_to_file(flash, filename);
-
- finalize_flash_access(flash);
-
- return ret;
-}
-
-int do_erase(struct flashctx *const flash)
-{
- const int ret = flashrom_flash_erase(flash);
-
- /*
- * FIXME: Do we really want the scary warning if erase failed?
- * After all, after erase the chip is either blank or partially
- * blank or it has the old contents. A blank chip won't boot,
- * so if the user wanted erase and reboots afterwards, the user
- * knows very well that booting won't work.
- */
- if (ret)
- emergency_help_message();
-
- return ret;
-}
-
-int do_write(struct flashctx *const flash, const char *const filename, const char *const referencefile)
-{
- const size_t flash_size = flash->chip->total_size * 1024;
- int ret = 1;
-
- uint8_t *const newcontents = malloc(flash_size);
- uint8_t *const refcontents = referencefile ? malloc(flash_size) : NULL;
-
- if (!newcontents || (referencefile && !refcontents)) {
- msg_gerr("Out of memory!\n");
- goto _free_ret;
- }
-
- if (read_buf_from_file(newcontents, flash_size, filename))
- goto _free_ret;
-
- if (referencefile) {
- if (read_buf_from_file(refcontents, flash_size, referencefile))
- goto _free_ret;
- }
-
- ret = flashrom_image_write(flash, newcontents, flash_size, refcontents);
-
-_free_ret:
- free(refcontents);
- free(newcontents);
- return ret;
-}
-
-int do_verify(struct flashctx *const flash, const char *const filename)
-{
- const size_t flash_size = flash->chip->total_size * 1024;
- int ret = 1;
-
- uint8_t *const newcontents = malloc(flash_size);
- if (!newcontents) {
- msg_gerr("Out of memory!\n");
- goto _free_ret;
- }
-
- if (read_buf_from_file(newcontents, flash_size, filename))
- goto _free_ret;
-
- ret = flashrom_image_verify(flash, newcontents, flash_size);
-
-_free_ret:
- free(newcontents);
- return ret;
-}