spi: Move ICH BBAR quirk out of the way

Get rid of the layering violations around ICH's BBAR. Move all the weird
address handling into (surprise, surprise) `ichspi.c`. Might fix writes
for the `BBAR != 0` case by accident.

Background: Some ICHs have a BBAR (BIOS Base Address Configuration
Register) that, if set, limits the valid address range to [BBAR, 2^24).
Current code lifted addresses for REMS, RES and READ operations by BBAR,
now we do it for all addresses in ichspi. Special care has to be taken
if the BBAR is not aligned by the flash chip's size. In this case, the
lower part of the chip (from BBAR aligned down, up to BBAR) is inacces-
sible (this seems to be the original intend behind BBAR) and has to be
left out in the address offset calculation.

Change-Id: Icbac513c5339e8aff624870252133284ef85ab73
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.coreboot.org/22396
Reviewed-by: David Hendricks <david.hendricks@gmail.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/ichspi.c b/ichspi.c
index c7bda92..5fe25f6 100644
--- a/ichspi.c
+++ b/ichspi.c
@@ -229,7 +229,7 @@
 static int ichspi_lock = 0;
 
 static enum ich_chipset ich_generation = CHIPSET_ICH_UNKNOWN;
-uint32_t ichspi_bbar = 0;
+static uint32_t ichspi_bbar;
 
 static void *ich_spibar = NULL;
 
@@ -1150,19 +1150,6 @@
 		return SPI_INVALID_LENGTH;
 	}
 
-	/* if opcode-type requires an address */
-	if (opcode->spi_type == SPI_OPCODE_TYPE_READ_WITH_ADDRESS ||
-	    opcode->spi_type == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS) {
-		addr = (writearr[1] << 16) |
-		    (writearr[2] << 8) | (writearr[3] << 0);
-		if (addr < ichspi_bbar) {
-			msg_perr("%s: Address 0x%06x below allowed "
-				 "range 0x%06x-0xffffff\n", __func__,
-				 addr, ichspi_bbar);
-			return SPI_INVALID_ADDRESS;
-		}
-	}
-
 	/* Translate read/write array/count.
 	 * The maximum data length is identical for the maximum read length and
 	 * for the maximum write length excluding opcode and address. Opcode and
@@ -1181,6 +1168,30 @@
 		count = readcnt;
 	}
 
+	/* if opcode-type requires an address */
+	if (cmd == JEDEC_REMS || cmd == JEDEC_RES) {
+		addr = ichspi_bbar;
+	} else if (opcode->spi_type == SPI_OPCODE_TYPE_READ_WITH_ADDRESS ||
+	    opcode->spi_type == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS) {
+		/* BBAR may cut part of the chip off at the lower end. */
+		const uint32_t valid_base = ichspi_bbar & ((flash->chip->total_size * 1024) - 1);
+		const uint32_t addr_offset = ichspi_bbar - valid_base;
+		/* Highest address we can program is (2^24 - 1). */
+		const uint32_t valid_end = (1 << 24) - addr_offset;
+
+		addr = writearr[1] << 16 | writearr[2] << 8 | writearr[3];
+		const uint32_t addr_end = addr + count;
+
+		if (addr < valid_base ||
+		    addr_end < addr || /* integer overflow check */
+		    addr_end > valid_end) {
+			msg_perr("%s: Addressed region 0x%06x-0x%06x not in allowed range 0x%06x-0x%06x\n",
+				 __func__, addr, addr_end - 1, valid_base, valid_end - 1);
+			return SPI_INVALID_ADDRESS;
+		}
+		addr += addr_offset;
+	}
+
 	result = run_opcode(flash, *opcode, addr, count, data);
 	if (result) {
 		msg_pdbg("Running OPCODE 0x%02x failed ", opcode->opcode);