spi: Implement top-aligned to avoid BBAR hassle
The BBAR quirk in `ichspi' is the only case left where we need a flash
context in the SPI `.send_command' functions. Our Git history suggests
that the elaborate calculation there was not added for an encountered
setup but rather all possible settings of BBAR [1]. There are only few
settings that make sense, however.
BBAR sets a simple address boundary. Reads for any flash address below
the BBAR setting will be rejected. This was originally the only read-
protection mechanism, introduced with ICH7. The ICH7 datasheet states
that upper bits, above the flash chip's size, should be set to all 1s.
This makes sense, as otherwise the read-protection could be circumven-
ted by setting a higher address above BBAR, where the flash chip would
simply ignore the most significant bits. Conversely, this requires us
to "lift" the flash addresses when the BBAR is configured properly. We
can achieve this by top-aligning all addresses.
Newer chipsets have protected-range registers (PRx) now, that allow to
configure read protection. Also the descriptor mode was introduced. So
flash addresses have to match the descriptor regions, and lifting them
isn't feasible. The BBAR register was still around until Wilcat Point
(PCH9), though, probably useless, and without the note about upper ad-
dress bits. Odd though, since [2], we only consider the BBAR on newer
chipsets when in descriptor mode.
As the BBAR protection seems unlikely on newer chipsets, and the quirk
handling error-prone, we'll only change addresses on ICH7 and similar
old chipsets. We don't want the dependency on the flash context, hence
let the generic `spi25' code top align the addresses.
[1] commit ed098d62d66d (spi: Move ICH BBAR quirk out of the way)
[2] commit 4095ed797f87 (Add support for Intel Silvermont: Bay Trail,
Rangeley and Avoton)
Change-Id: Ic6f6f5a24d89d4a1ebe2b99f08aabfcd65df129f
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.sourcearcade.org/c/flashprog/+/74896
diff --git a/ichspi.c b/ichspi.c
index 9a5f1b7..0a32778 100644
--- a/ichspi.c
+++ b/ichspi.c
@@ -1127,23 +1127,17 @@
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;
+ const uint32_t valid_end = 1 << 24;
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);
+ /* BBAR may cut part of the chip off at the lower end. */
+ if (addr < ichspi_bbar) {
+ msg_perr("%s: Address 0x%06x not in allowed range 0x%06x-0x%06x\n",
+ __func__, addr, ichspi_bbar, valid_end - 1);
return SPI_INVALID_ADDRESS;
}
- addr += addr_offset;
}
result = run_opcode(flash, *opcode, addr, count, data);
@@ -1641,6 +1635,7 @@
int ich7_init_spi(void *spibar, enum ich_chipset ich_gen)
{
+ struct spi_master mst = spi_master_ich7;
unsigned int i;
ich_generation = ich_gen;
@@ -1673,9 +1668,12 @@
}
ich_init_opcodes();
- ich_set_bbar(0);
- return register_spi_master(&spi_master_ich7, 0, NULL);
+ ich_set_bbar(0);
+ if (ichspi_bbar > 0)
+ mst.features |= SPI_MASTER_TOP_ALIGNED;
+
+ return register_spi_master(&mst, 0, NULL);
}
static const struct spi_master spi_master_ich9 = {
@@ -1969,6 +1967,7 @@
int via_init_spi(uint32_t mmio_base)
{
+ struct spi_master mst = spi_master_via;
int i;
ich_spibar = rphysmap("VIA SPI MMIO registers", mmio_base, 0x70);
@@ -1979,7 +1978,6 @@
/* Not sure if it speaks all these bus protocols. */
internal_buses_supported &= BUS_LPC | BUS_FWH;
ich_generation = CHIPSET_ICH7;
- register_spi_master(&spi_master_via, 0, NULL);
msg_pdbg("0x00: 0x%04x (SPIS)\n", mmio_readw(ich_spibar + 0));
msg_pdbg("0x02: 0x%04x (SPIC)\n", mmio_readw(ich_spibar + 2));
@@ -2011,8 +2009,11 @@
ichspi_lock = true;
}
- ich_set_bbar(0);
ich_init_opcodes();
- return 0;
+ ich_set_bbar(0);
+ if (ichspi_bbar > 0)
+ mst.features |= SPI_MASTER_TOP_ALIGNED;
+
+ return register_spi_master(&mst, 0, NULL);
}
diff --git a/include/programmer.h b/include/programmer.h
index b19049e..3270e0e 100644
--- a/include/programmer.h
+++ b/include/programmer.h
@@ -292,6 +292,8 @@
#define SPI_MASTER_QPI (1U << 6) /**< Can send commands with quad i/o */
#define SPI_MASTER_DTR_IN (1U << 7) /**< Double Transfer Rate: Can read two bits
per clock cycle per line */
+#define SPI_MASTER_TOP_ALIGNED (1U << 8) /**< Use addresses at the top of address space,
+ i.e. 2^24-flash_size .. 2^24-1 */
/* Shorthands: */
#define SPI_MASTER_DUAL (SPI_MASTER_DUAL_IN | SPI_MASTER_DUAL_IO)
@@ -533,6 +535,10 @@
{
return flash->mst.spi->features & SPI_MASTER_QPI;
}
+static inline bool spi_master_top_aligned(const struct flashctx *const flash)
+{
+ return flash->mst.spi->features & SPI_MASTER_TOP_ALIGNED;
+}
/* usbdev.c */
struct libusb_device_handle;
diff --git a/spi25.c b/spi25.c
index b1d6288..334e87a 100644
--- a/spi25.c
+++ b/spi25.c
@@ -394,9 +394,13 @@
}
static int spi_prepare_address(struct flashctx *const flash, uint8_t cmd_buf[],
- const bool native_4ba, const unsigned int addr)
+ const bool native_4ba, const unsigned int rel_addr)
{
const size_t len = spi_address_length(flash, native_4ba);
+ unsigned int addr = rel_addr;
+
+ if (spi_master_top_aligned(flash))
+ addr = rel_addr - flashprog_flash_getsize(flash); /* intentional integer underflow */
switch (len) {
case 4:
@@ -411,9 +415,9 @@
return len;
case 3:
if (flash->chip->feature_bits & FEATURE_4BA_EAR_ANY) {
- if (spi_set_extended_address(flash, addr >> 24))
+ if (spi_set_extended_address(flash, rel_addr >> 24))
return -1;
- } else if (addr >> 24) {
+ } else if (rel_addr >> 24) {
msg_cerr("Can't handle 4-byte address for opcode '0x%02x'\n"
"with this chip/programmer combination.\n", cmd_buf[0]);
return -1;