Make flash-size limiting of atapromise a general --force feature

To allow accessing at least part of flash chips behind this size-limited
controller,  the `atapromise' driver used a local function that adjusted
a chip description.  As flashprog would have bailed out earlier already,
this was only ever usable with the `--force' flag.

The same adjustment can be used with other programmers. Making it a glo-
bal feature also gets rid of a driver peculiarity and removes the depen-
dency on `flashctx` in the `atapromise' driver.

The logic enforces a complete chip erase if any erase is necessary. So,
while this should make part of the chip fully read/writable, content of
the inaccessible area will be lost on write.

Change-Id: I6bce8ff7781f683b001f76154621f22bd03687bc
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.sourcearcade.org/c/flashprog/+/431
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
diff --git a/atapromise.c b/atapromise.c
index 62c7e3d..aa83577 100644
--- a/atapromise.c
+++ b/atapromise.c
@@ -75,39 +75,6 @@
 	return NULL;
 }
 
-static void atapromise_limit_chip(struct flashchip *chip)
-{
-	unsigned int i, size;
-	unsigned int usable_erasers = 0;
-
-	size = chip->total_size * 1024;
-
-	/* Chip is small enough or already limited. */
-	if (size <= rom_size)
-		return;
-
-	/* Undefine all block_erasers that don't operate on the whole chip,
-	 * and adjust the eraseblock size of those which do.
-	 */
-	for (i = 0; i < NUM_ERASEFUNCTIONS; ++i) {
-		if (chip->block_erasers[i].eraseblocks[0].size != size) {
-			chip->block_erasers[i].eraseblocks[0].count = 0;
-			chip->block_erasers[i].block_erase = NULL;
-		} else {
-			chip->block_erasers[i].eraseblocks[0].size = rom_size;
-			usable_erasers++;
-		}
-	}
-
-	if (usable_erasers) {
-		chip->total_size = rom_size / 1024;
-		if (chip->page_size > rom_size)
-			chip->page_size = rom_size;
-	} else {
-		msg_pdbg("Failed to adjust size of chip \"%s\" (%d kB).\n", chip->name, chip->total_size);
-	}
-}
-
 static int atapromise_init(struct flashprog_programmer *const prog)
 {
 	struct pci_dev *dev = NULL;
@@ -153,14 +120,12 @@
 {
 	uint32_t data;
 
-	atapromise_limit_chip(flash->chip);
 	data = (rom_base_addr + (addr & ADDR_MASK)) << 8 | val;
 	OUTL(data, io_base_addr + 0x14);
 }
 
 static uint8_t atapromise_chip_readb(const struct flashctx *flash, const chipaddr addr)
 {
-	atapromise_limit_chip(flash->chip);
 	return pci_mmio_readb(atapromise_bar + (addr & ADDR_MASK));
 }
 
diff --git a/cli_classic.c b/cli_classic.c
index 2593f62..4ed820f 100644
--- a/cli_classic.c
+++ b/cli_classic.c
@@ -170,14 +170,23 @@
 }
 
 /* Returns true if the flash chip cannot be completely accessed due to size/address limits of the programmer. */
-static bool max_decode_exceeded(const struct flashctx *const flash)
+static bool max_decode_exceeded(const struct flashctx *const flash, const bool force)
 {
 	if (flashprog_flash_getsize(flash) <= flash->mst.common->max_rom_decode)
 		return false;
 
 	msg_pdbg("Chip size %u kB is bigger than supported size %zu kB of\n"
-		 "chipset/board/programmer for memory-mapped interface, probe/read/erase/write\n"
-		 "may fail.\n", flash->chip->total_size, flash->mst.common->max_rom_decode / KiB);
+		 "chipset/board/programmer for memory-mapped interface.\n",
+		 flash->chip->total_size, flash->mst.common->max_rom_decode / KiB);
+
+	if (!force) {
+		msg_cerr("This flash chip is too big for this programmer (--verbose/-V gives details).\n"
+			 "Use --force/-f to override at your own risk. Any write may erase the whole\n"
+			 "chip and leave the unwritten area in erased state! You have been warned.\n");
+	} else {
+		msg_cwarn("This flash chip is too big for this programmer. Any write may erase the whole\n"
+			  "chip and leave the unwritten area in erased state!\n");
+	}
 	return true;
 }
 
@@ -529,11 +538,11 @@
 
 	print_chip_support_status(fill_flash->chip);
 
-	if (max_decode_exceeded(fill_flash) && !force) {
-		msg_cerr("This flash chip is too big for this programmer (--verbose/-V gives details).\n"
-			 "Use --force/-f to override at your own risk.\n");
-		ret = 1;
-		goto out_shutdown;
+	if (max_decode_exceeded(fill_flash, force)) {
+		if (!force || flashprog_limit_chip(fill_flash)) {
+			ret = 1;
+			goto out_shutdown;
+		}
 	}
 
 	if (!(read_it | write_it | verify_it | erase_it | flash_name | flash_size)) {
diff --git a/helpers.c b/helpers.c
index b892c20..53fee54 100644
--- a/helpers.c
+++ b/helpers.c
@@ -18,7 +18,9 @@
 #include <ctype.h>
 #include <stdlib.h>
 #include <string.h>
+
 #include "flash.h"
+#include "programmer.h"
 
 /* Check if raw data is all 0 or all 1. */
 bool flashprog_no_data(const void *const raw_data, const size_t len)
@@ -59,6 +61,43 @@
 	return 0;
 }
 
+int flashprog_limit_chip(struct flashctx *flash)
+{
+	const chipsize_t limit = flash->mst.common->max_rom_decode;
+	struct flashchip *const chip = flash->chip;
+	const chipsize_t chip_size = chip->total_size * 1024;
+	unsigned int usable_erasers = 0;
+	unsigned int i;
+
+
+	/* Chip is small enough or already limited. */
+	if (chip_size <= limit)
+		return 0;
+
+	/* Undefine all block_erasers that don't operate on the whole chip,
+	   and adjust the eraseblock size of those which do. */
+	for (i = 0; i < NUM_ERASEFUNCTIONS; ++i) {
+		if (chip->block_erasers[i].eraseblocks[0].size != chip_size) {
+			chip->block_erasers[i].eraseblocks[0].count = 0;
+			chip->block_erasers[i].block_erase = NULL;
+		} else {
+			chip->block_erasers[i].eraseblocks[0].size = limit;
+			usable_erasers++;
+		}
+	}
+
+	if (usable_erasers) {
+		chip->total_size = limit / 1024;
+		if (chip->page_size > limit)
+			chip->page_size = limit;
+		return 0;
+	} else {
+		msg_pdbg("Failed to adjust size of chip \"%s\" (%d kB).\n",
+			 chip->name, chip->total_size);
+		return -1;
+	}
+}
+
 /* Returns the minimum number of bits needed to represent the given address.
  * FIXME: use mind-blowing implementation. */
 uint32_t address_to_bits(uint32_t addr)
diff --git a/include/flash.h b/include/flash.h
index eec09b4..8aff7a1 100644
--- a/include/flash.h
+++ b/include/flash.h
@@ -524,6 +524,7 @@
 /* helpers.c */
 bool flashprog_no_data(const void *raw_data, size_t);
 int flashprog_read_chunked(struct flashctx *, uint8_t *dst, unsigned int start, unsigned int len, unsigned int chunksize, readfunc_t *);
+int flashprog_limit_chip(struct flashctx *);
 uint32_t address_to_bits(uint32_t addr);
 unsigned int bitcount(unsigned long a);
 #undef MIN