summaryrefslogtreecommitdiffstats
path: root/jedec.c
diff options
context:
space:
mode:
authorCarl-Daniel Hailfinger <c-d.hailfinger.devel.2006@gmx.net>2014-08-03 13:05:34 +0000
committerStefan Tauner <stefan.tauner@alumni.tuwien.ac.at>2014-08-03 13:05:34 +0000
commitef3ac8ac17eac9d2041ea9c9e711a9b059412b59 (patch)
treee5fec0cda5a546133a7440f65da442329ec3db48 /jedec.c
parent2a41f0a2c0daeb0ee2a38a252ab96135d70a6b81 (diff)
downloadast2050-flashrom-ef3ac8ac17eac9d2041ea9c9e711a9b059412b59.zip
ast2050-flashrom-ef3ac8ac17eac9d2041ea9c9e711a9b059412b59.tar.gz
Refactor unlocking of many chips with locking at register space address +2
This includes PMC Pm49*, SST 49LF00*, ST M50* and Winbond W39* families. The erase and write test status bits of all affected chips have been reset. Corresponding to flashrom svn r1833. Signed-off-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@gmx.net> Signed-off-by: Stefan Tauner <stefan.tauner@alumni.tuwien.ac.at> Acked-by: Stefan Tauner <stefan.tauner@alumni.tuwien.ac.at>
Diffstat (limited to 'jedec.c')
-rw-r--r--jedec.c185
1 files changed, 184 insertions, 1 deletions
diff --git a/jedec.c b/jedec.c
index 08cc88b..29fdae8 100644
--- a/jedec.c
+++ b/jedec.c
@@ -4,8 +4,9 @@
* Copyright (C) 2000 Silicon Integrated System Corporation
* Copyright (C) 2006 Giampiero Giancipoli <gianci@email.it>
* Copyright (C) 2006 coresystems GmbH <info@coresystems.de>
- * Copyright (C) 2007 Carl-Daniel Hailfinger
+ * Copyright (C) 2007, 2011 Carl-Daniel Hailfinger
* Copyright (C) 2009 Sean Nelson <audiohacked@gmail.com>
+ * Copyright (C) 2014 Stefan Tauner
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -510,3 +511,185 @@ int erase_chip_jedec(struct flashctx *flash)
mask = getaddrmask(flash->chip);
return erase_chip_jedec_common(flash, mask);
}
+
+struct unlockblock {
+ unsigned int size;
+ unsigned int count;
+};
+
+typedef int (*unlockblock_func)(const struct flashctx *flash, chipaddr offset);
+static int regspace2_walk_unlockblocks(const struct flashctx *flash, const struct unlockblock *block, unlockblock_func func)
+{
+ chipaddr off = flash->virtual_registers + 2;
+ while (block->count != 0) {
+ unsigned int j;
+ for (j = 0; j < block->count; j++) {
+ if (func(flash, off))
+ return -1;
+ off += block->size;
+ }
+ block++;
+ }
+ return 0;
+}
+
+#define REG2_RWLOCK ((1 << 2) | (1 << 0))
+#define REG2_LOCKDOWN (1 << 1)
+#define REG2_MASK (REG2_RWLOCK | REG2_LOCKDOWN)
+
+static int printlock_regspace2_block(const struct flashctx *flash, chipaddr offset)
+{
+ chipaddr wrprotect = flash->virtual_registers + offset + 2;
+ uint8_t state = chip_readb(flash, wrprotect);
+ msg_cdbg("Lock status of block at 0x%0*" PRIxPTR " is ", PRIxPTR_WIDTH, offset);
+ switch (state & REG2_MASK) {
+ case 0:
+ msg_cdbg("Full Access.\n");
+ break;
+ case 1:
+ msg_cdbg("Write Lock (Default State).\n");
+ break;
+ case 2:
+ msg_cdbg("Locked Open (Full Access, Locked Down).\n");
+ break;
+ case 3:
+ msg_cdbg("Write Lock, Locked Down.\n");
+ break;
+ case 4:
+ msg_cdbg("Read Lock.\n");
+ break;
+ case 5:
+ msg_cdbg("Read/Write Lock.\n");
+ break;
+ case 6:
+ msg_cdbg("Read Lock, Locked Down.\n");
+ break;
+ case 7:
+ msg_cdbg("Read/Write Lock, Locked Down.\n");
+ break;
+ }
+ return 0;
+}
+
+int printlock_regspace2_blocks(const struct flashctx *flash, const struct unlockblock *blocks)
+{
+ return regspace2_walk_unlockblocks(flash, blocks, &printlock_regspace2_block);
+}
+
+static int printlock_regspace2_uniform(struct flashctx *flash, unsigned long block_size)
+{
+ const unsigned int elems = flash->chip->total_size * 1024 / block_size;
+ struct unlockblock blocks[2] = {{.size = block_size, .count = elems}};
+ return regspace2_walk_unlockblocks(flash, blocks, &printlock_regspace2_block);
+}
+
+int printlock_regspace2_uniform_64k(struct flashctx *flash)
+{
+ return printlock_regspace2_uniform(flash, 64 * 1024);
+}
+
+int printlock_regspace2_block_eraser_0(struct flashctx *flash)
+{
+ // FIXME: this depends on the eraseblocks not to be filled up completely (i.e. to be null-terminated).
+ const struct unlockblock *unlockblocks =
+ (const struct unlockblock *)flash->chip->block_erasers[0].eraseblocks;
+ return regspace2_walk_unlockblocks(flash, unlockblocks, &printlock_regspace2_block);
+}
+
+int printlock_regspace2_block_eraser_1(struct flashctx *flash)
+{
+ // FIXME: this depends on the eraseblocks not to be filled up completely (i.e. to be null-terminated).
+ const struct unlockblock *unlockblocks =
+ (const struct unlockblock *)flash->chip->block_erasers[1].eraseblocks;
+ return regspace2_walk_unlockblocks(flash, unlockblocks, &printlock_regspace2_block);
+}
+
+static int changelock_regspace2_block(const struct flashctx *flash, chipaddr offset, uint8_t new_bits)
+{
+ chipaddr wrprotect = flash->virtual_registers + offset + 2;
+ uint8_t old;
+
+ if (new_bits & ~REG2_MASK) {
+ msg_cerr("Invalid locking change 0x%02x requested at 0x%0*" PRIxPTR "! "
+ "Please report a bug at flashrom@flashrom.org\n",
+ new_bits, PRIxPTR_WIDTH, offset);
+ return -1;
+ }
+ old = chip_readb(flash, wrprotect);
+ /* Early exist if no change (of read/write/lockdown) was requested. */
+ if (((old ^ new_bits) & REG2_MASK) == 0) {
+ msg_cdbg2("Locking status at 0x%0*" PRIxPTR " not changed\n", PRIxPTR_WIDTH, offset);
+ return 0;
+ }
+ /* Normally lockdowns can not be cleared. Try nevertheless if requested. */
+ if ((old & REG2_LOCKDOWN) && !(new_bits & REG2_LOCKDOWN)) {
+ chip_writeb(flash, old & ~REG2_LOCKDOWN, wrprotect);
+ if (chip_readb(flash, wrprotect) != (old & ~REG2_LOCKDOWN)) {
+ msg_cerr("Lockdown can't be removed at 0x%0*" PRIxPTR "!\n", PRIxPTR_WIDTH, offset);
+ return -1;
+ }
+ }
+ /* Change read or write lock? */
+ if ((old ^ new_bits) & REG2_RWLOCK) {
+ /* Do not lockdown yet. */
+ msg_cdbg("Changing locking status at 0x%0*" PRIxPTR " to 0x%02x\n", PRIxPTR_WIDTH, offset, new_bits & REG2_RWLOCK);
+ chip_writeb(flash, new_bits & REG2_RWLOCK, wrprotect);
+ if (chip_readb(flash, wrprotect) != (new_bits & REG2_RWLOCK)) {
+ msg_cerr("Locking status change FAILED at 0x%0*" PRIxPTR "!\n", PRIxPTR_WIDTH, offset);
+ return -1;
+ }
+ }
+ /* Enable lockdown if requested. */
+ if (!(old & REG2_LOCKDOWN) && (new_bits & REG2_LOCKDOWN)) {
+ msg_cdbg("Enabling lockdown at 0x%0*" PRIxPTR "\n", PRIxPTR_WIDTH, offset);
+ chip_writeb(flash, new_bits, wrprotect);
+ if (chip_readb(flash, wrprotect) != new_bits) {
+ msg_cerr("Enabling lockdown FAILED at 0x%0*" PRIxPTR "!\n", PRIxPTR_WIDTH, offset);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int unlock_regspace2_block(const struct flashctx *flash, chipaddr off)
+{
+ chipaddr wrprotect = flash->virtual_registers + off + 2;
+ uint8_t old = chip_readb(flash, wrprotect);
+ /* We don't care for the lockdown bit as long as the RW locks are 0 after we're done */
+ return changelock_regspace2_block(flash, off, old & ~REG2_RWLOCK);
+}
+
+static int unlock_regspace2_uniform(struct flashctx *flash, unsigned long block_size)
+{
+ const unsigned int elems = flash->chip->total_size * 1024 / block_size;
+ struct unlockblock blocks[2] = {{.size = block_size, .count = elems}};
+ return regspace2_walk_unlockblocks(flash, blocks, &unlock_regspace2_block);
+}
+
+int unlock_regspace2_uniform_64k(struct flashctx *flash)
+{
+ return unlock_regspace2_uniform(flash, 64 * 1024);
+}
+
+int unlock_regspace2_uniform_32k(struct flashctx *flash)
+{
+ return unlock_regspace2_uniform(flash, 32 * 1024);
+}
+
+int unlock_regspace2_block_eraser_0(struct flashctx *flash)
+{
+ // FIXME: this depends on the eraseblocks not to be filled up completely (i.e. to be null-terminated).
+ const struct unlockblock *unlockblocks =
+ (const struct unlockblock *)flash->chip->block_erasers[0].eraseblocks;
+ return regspace2_walk_unlockblocks(flash, unlockblocks, &unlock_regspace2_block);
+}
+
+int unlock_regspace2_block_eraser_1(struct flashctx *flash)
+{
+ // FIXME: this depends on the eraseblocks not to be filled up completely (i.e. to be null-terminated).
+ const struct unlockblock *unlockblocks =
+ (const struct unlockblock *)flash->chip->block_erasers[1].eraseblocks;
+ return regspace2_walk_unlockblocks(flash, unlockblocks, &unlock_regspace2_block);
+}
+
OpenPOWER on IntegriCloud