diff options
author | stefanct <stefanct@2b7e53f0-3cfb-0310-b3e9-8179ed1497e1> | 2014-08-03 13:05:34 +0000 |
---|---|---|
committer | stefanct <stefanct@2b7e53f0-3cfb-0310-b3e9-8179ed1497e1> | 2014-08-03 13:05:34 +0000 |
commit | c8f9a06f6ca4171e137f35de5734be32eacf1f51 (patch) | |
tree | e5fec0cda5a546133a7440f65da442329ec3db48 /jedec.c | |
parent | d7010dd365a5455d111638fd5f359f5537608325 (diff) | |
download | flashrom-c8f9a06f6ca4171e137f35de5734be32eacf1f51.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.
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>
git-svn-id: https://code.coreboot.org/svn/flashrom/trunk@1833 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
Diffstat (limited to 'jedec.c')
-rw-r--r-- | jedec.c | 185 |
1 files changed, 184 insertions, 1 deletions
@@ -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); +} + |