From c8f9a06f6ca4171e137f35de5734be32eacf1f51 Mon Sep 17 00:00:00 2001 From: stefanct Date: Sun, 3 Aug 2014 13:05:34 +0000 Subject: 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 Signed-off-by: Stefan Tauner Acked-by: Stefan Tauner git-svn-id: https://code.coreboot.org/svn/flashrom/trunk@1833 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1 --- jedec.c | 185 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 184 insertions(+), 1 deletion(-) (limited to 'jedec.c') 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 * Copyright (C) 2006 coresystems GmbH - * Copyright (C) 2007 Carl-Daniel Hailfinger + * Copyright (C) 2007, 2011 Carl-Daniel Hailfinger * Copyright (C) 2009 Sean Nelson + * 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); +} + -- cgit v1.2.1