@@ -79,26 +79,29 @@ int write_en29f002a(struct flashchip *flash, uint8_t *buf);
uint8_t oddparity(uint8_t val);
void toggle_ready_jedec(chipaddr dst);
void data_polling_jedec(chipaddr dst, uint8_t data);
int write_byte_program_jedec(chipaddr bios, uint8_t *src,
chipaddr dst);
int probe_jedec(struct flashchip *flash);
int erase_chip_jedec(struct flashchip *flash);
int write_jedec(struct flashchip *flash, uint8_t *buf);
int write_jedec_1(struct flashchip *flash, uint8_t *buf);
int erase_sector_jedec(struct flashchip *flash, unsigned int page, unsigned int pagesize);
int erase_block_jedec(struct flashchip *flash, unsigned int page, unsigned int blocksize);
int erase_chip_block_jedec(struct flashchip *flash, unsigned int page, unsigned int blocksize);
int write_sector_jedec_common(struct flashchip *flash, uint8_t *src, chipaddr dst, unsigned int page_size, unsigned int mask);
+int lock_status_block_jedec(struct flashchip *flash, unsigned int blockaddr);
+int unlock_block_jedec(struct flashchip *flash, unsigned int blockaddr);
+int lock_block_jedec(struct flashchip *flash, unsigned int blockaddr);
/* m29f002.c */
int erase_m29f002(struct flashchip *flash);
int write_m29f002t(struct flashchip *flash, uint8_t *buf);
int write_m29f002b(struct flashchip *flash, uint8_t *buf);
/* m29f400bt.c */
int probe_m29f400bt(struct flashchip *flash);
int erase_m29f400bt(struct flashchip *flash);
int block_erase_m29f400bt(struct flashchip *flash, unsigned int start, unsigned int len);
int block_erase_chip_m29f400bt(struct flashchip *flash, unsigned int start, unsigned int len);
int write_m29f400bt(struct flashchip *flash, uint8_t *buf);
int write_coreboot_m29f400bt(struct flashchip *flash, uint8_t *buf);
@@ -142,26 +142,30 @@ enum chipbustype {
};
/*
* How many different contiguous runs of erase blocks with one size each do
* we have for a given erase function?
*/
#define NUM_ERASEREGIONS 5
/*
* How many different erase functions do we have per chip?
*/
#define NUM_ERASEFUNCTIONS 5
+/* How many different lock segments can a chip have? */
+#define NUM_LOCKREGIONS 4
+#define NUM_LOCKFUNCTIONS 2
+
#define FEATURE_REGISTERMAP (1 << 0)
#define FEATURE_BYTEWRITES (1 << 1)
#define FEATURE_LONG_RESET (0 << 4)
#define FEATURE_SHORT_RESET (1 << 4)
#define FEATURE_EITHER_RESET FEATURE_LONG_RESET
#define FEATURE_ADDR_FULL (0 << 2)
#define FEATURE_ADDR_MASK (3 << 2)
#define FEATURE_ADDR_2AA (1 << 2)
#define FEATURE_ADDR_AAA (2 << 2)
#define FEATURE_ADDR_SHIFTED 0
struct flashchip {
const char *vendor;
@@ -195,26 +199,41 @@ struct flashchip {
/*
* Erase blocks and associated erase function. Any chip erase function
* is stored as chip-sized virtual block together with said function.
*/
struct block_eraser {
struct eraseblock{
unsigned int size; /* Eraseblock size */
unsigned int count; /* Number of contiguous blocks with that size */
} eraseblocks[NUM_ERASEREGIONS];
int (*block_erase) (struct flashchip *flash, unsigned int blockaddr, unsigned int blocklen);
} block_erasers[NUM_ERASEFUNCTIONS];
+ /*
+ * Lock blocks and associated locking related functions. Lock blocks
+ * are usually uniform blocks with three functions for locking,
+ * unlocking, and lock status.
+ */
+ struct block_locker {
+ struct lockblock {
+ unsigned int size; /* Lock block size; 64k or 32k */
+ unsigned int count; /* Number of contiguous blocks */
+ } lockblocks[NUM_LOCKREGIONS];
+ int (*lock) (struct flashchip *flash, unsigned int block);
+ int (*unlock) (struct flashchip *flash, unsigned int block);
+ int (*lock_status) (struct flashchip *flash, unsigned int block);
+ } block_lockers[NUM_LOCKFUNCTIONS];
+
int (*write) (struct flashchip *flash, uint8_t *buf);
int (*read) (struct flashchip *flash, uint8_t *buf, int start, int len);
/* Some flash devices have an additional register space. */
chipaddr virtual_memory;
chipaddr virtual_registers;
};
#define TEST_UNTESTED 0
#define TEST_OK_PROBE (1 << 0)
#define TEST_OK_READ (1 << 1)
#define TEST_OK_ERASE (1 << 2)
@@ -516,26 +535,29 @@ int erase_flash(struct flashchip *flash);
struct flashchip *probe_flash(struct flashchip *first_flash, int force);
int read_flash(struct flashchip *flash, char *filename);
void check_chip_supported(struct flashchip *flash);
int check_max_decode(enum chipbustype buses, uint32_t size);
int min(int a, int b);
int max(int a, int b);
char *extract_param(char **haystack, char *needle, char *delim);
int check_erased_range(struct flashchip *flash, int start, int len);
int verify_range(struct flashchip *flash, uint8_t *cmpbuf, int start, int len, char *message);
char *strcat_realloc(char *dest, const char *src);
void print_version(void);
int selfcheck(void);
int doit(struct flashchip *flash, int force, char *filename, int read_it, int write_it, int erase_it, int verify_it);
+int unlock_flash(struct flashchip *flash);
+int lock_flash(struct flashchip *flash);
+int print_lock_status_flash(struct flashchip *flash);
#define OK 0
#define NT 1 /* Not tested */
/* cli_output.c */
int print(int type, const char *fmt, ...);
#define MSG_ERROR 0
#define MSG_INFO 1
#define MSG_DEBUG 2
#define MSG_BARF 3
#define msg_gerr(...) print(MSG_ERROR, __VA_ARGS__) /* general errors */
#define msg_perr(...) print(MSG_ERROR, __VA_ARGS__) /* programmer errors */
#define msg_cerr(...) print(MSG_ERROR, __VA_ARGS__) /* chip errors */
@@ -3425,27 +3425,36 @@ struct flashchip flashchips[] = {
.block_erasers =
{
{
.eraseblocks = { {4 * 1024, 64} },
.block_erase = erase_sector_jedec,
}, {
.eraseblocks = { {16 * 1024, 16} },
.block_erase = erase_block_jedec,
}, {
.eraseblocks = { {256 * 1024, 1} },
.block_erase = erase_chip_block_jedec,
}
},
- .write = write_49fl00x,
+ .block_lockers =
+ {
+ {
+ .lockblocks = { {64 * 1024, 8} },
+ .lock = lock_block_jedec,
+ .unlock = unlock_block_jedec,
+ .lock_status = lock_status_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
.read = read_memmapped,
},
{
.vendor = "PMC",
.name = "Pm49FL004",
.bustype = CHIP_BUSTYPE_LPC | CHIP_BUSTYPE_FWH, /* A/A Mux*/
.manufacture_id = PMC_ID_NOPREFIX,
.model_id = PMC_49FL004,
.total_size = 512,
.page_size = 64 * 1024,
.feature_bits = FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
.tested = TEST_UNTESTED,
@@ -3455,27 +3464,36 @@ struct flashchip flashchips[] = {
.block_erasers =
{
{
.eraseblocks = { {4 * 1024, 128} },
.block_erase = erase_sector_jedec,
}, {
.eraseblocks = { {64 * 1024, 8} },
.block_erase = erase_block_jedec,
}, {
.eraseblocks = { {512 * 1024, 1} },
.block_erase = erase_chip_block_jedec,
}
},
- .write = write_49fl00x,
+ .block_lockers =
+ {
+ {
+ .lockblocks = { {64 * 1024, 8} },
+ .lock = lock_block_jedec,
+ .unlock = unlock_block_jedec,
+ .lock_status = lock_status_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
.read = read_memmapped,
},
{
.vendor = "Sanyo",
.name = "LF25FW203A",
.bustype = CHIP_BUSTYPE_SPI,
.manufacture_id = SANYO_ID,
.model_id = SANYO_LE25FW203A,
.total_size = 2048,
.page_size = 256,
.tested = TEST_UNTESTED,
.probe = probe_spi_rdid,
@@ -6120,26 +6138,40 @@ struct flashchip flashchips[] = {
.probe = probe_jedec,
.probe_timing = TIMING_FIXME,
.erase = NULL, /* Was erase_winbond_fwhub */
.block_erasers =
{
{
.eraseblocks = { {64 * 1024, 16}, },
.block_erase = erase_sector_jedec,
}, {
.eraseblocks = { {1024 * 1024, 1} },
.block_erase = erase_chip_block_jedec,
}
},
+ .block_lockers =
+ {
+ { /* block lock functions */
+ .lockblocks = { {64 * 1024, 16} },
+ .lock = lock_block_jedec,
+ .unlock = unlock_block_jedec,
+ .lock_status = lock_status_block_jedec,
+ }, { /* #TBL / #WP status */
+ .lockblocks = { {512 * 1024, 1} }, /* dummy block */
+ .lock = NULL,
+ .unlock = NULL,
+ .lock_status = lock_status_chip_jedec,
+ }
+ },
.write = write_jedec_1,
.read = read_memmapped,
},
{
.vendor = "Winbond",
.name = "W39V080FA (dual mode)",
.bustype = CHIP_BUSTYPE_FWH,
.manufacture_id = WINBOND_ID,
.model_id = W_39V080FA_DM,
.total_size = 512,
.page_size = 64 * 1024,
.feature_bits = FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
@@ -6147,26 +6179,40 @@ struct flashchip flashchips[] = {
.probe = probe_jedec,
.probe_timing = TIMING_FIXME,
.erase = NULL, /* Was erase_winbond_fwhub */
.block_erasers =
{
{
.eraseblocks = { {64 * 1024, 8}, },
.block_erase = erase_sector_jedec,
}, {
.eraseblocks = { {512 * 1024, 1} },
.block_erase = erase_chip_block_jedec,
}
},
+ .block_lockers =
+ {
+ { /* block lock functions */
+ .lockblocks = { {64 * 1024, 8} },
+ .lock = lock_block_jedec,
+ .unlock = unlock_block_jedec,
+ .lock_status = lock_status_block_jedec,
+ }, { /* #TBL / #WP status */
+ .lockblocks = { {512 * 1024, 1} }, /* dummy block */
+ .lock = NULL,
+ .unlock = NULL,
+ .lock_status = lock_status_chip_jedec,
+ }
+ },
.write = write_jedec_1,
.read = read_memmapped,
},
{
.vendor = "Atmel",
.name = "unknown Atmel SPI chip",
.bustype = CHIP_BUSTYPE_SPI,
.manufacture_id = ATMEL_ID,
.model_id = GENERIC_DEVICE_ID,
.total_size = 0,
.page_size = 256,
.tested = TEST_BAD_PREW,
@@ -795,26 +795,28 @@ struct flashchip *probe_flash(struct flashchip *first_flash, int force)
break;
notfound:
programmer_unmap_flash_region((void *)flash->virtual_memory, size);
}
if (!flash || !flash->name)
return NULL;
printf("Found chip \"%s %s\" (%d KB, %s) at physical address 0x%lx.\n",
flash->vendor, flash->name, flash->total_size,
flashbuses_to_text(flash->bustype), base);
+ print_lock_status_flash(flash);
+
return flash;
}
int verify_flash(struct flashchip *flash, uint8_t *buf)
{
int ret;
int total_size = flash->total_size * 1024;
printf("Verifying flash... ");
ret = verify_range(flash, buf, 0, total_size, NULL);
if (!ret)
@@ -984,26 +986,213 @@ int erase_flash(struct flashchip *flash)
if (!found) {
fprintf(stderr, "ERROR: flashrom has no erase function for this flash chip.\n");
return 1;
}
if (ret) {
fprintf(stderr, "FAILED!\n");
} else {
printf("SUCCESS.\n");
}
return ret;
}
+int unlock_flash(struct flashchip *flash)
+{
+ int i, j, k, ret = 0, found = 0;
+ unsigned int start;
+
+ printf("Unlocking flash chip... ");
+ for (k = 0; k < NUM_LOCKFUNCTIONS; k++) {
+ unsigned int done = 0;
+ struct block_locker locker = flash->block_lockers[k];
+
+ printf_debug("Looking at blockwise unlock function %i... ", k);
+ if (!locker.unlock && !locker.lockblocks[0].count) {
+ printf_debug("not defined. "
+ "Looking for another unlock function.\n");
+ continue;
+ }
+ if (!locker.unlock && locker.lockblocks[0].count) {
+ printf_debug("lockblock layout is known, but no "
+ "matching block unlock function found. "
+ "Looking for another unlock function.\n");
+ continue;
+ }
+ if (locker.unlock && !locker.lockblocks[0].count) {
+ printf_debug("block unlock function found, but "
+ "lockblock layout is unknown. "
+ "Looking for another unlock function.\n");
+ continue;
+ }
+ found = 1;
+ printf_debug("trying... ");
+ for (i = 0; i < NUM_LOCKREGIONS; i++) {
+ /* count==0 for all automatically initialized array
+ * members so the loop below won't be executed for them.
+ */
+ for (j = 0; j < locker.lockblocks[i].count; j++) {
+ start = done + locker.lockblocks[i].size * j;
+ ret = locker.unlock(flash, start);
+ printf_debug("0x%06x, ", start);
+ if (ret)
+ break;
+ }
+ if (ret)
+ break;
+ done += locker.lockblocks[i].count *
+ locker.lockblocks[i].size;
+ }
+ printf_debug("\n");
+ /* If everything is OK, don't try another unlock function. */
+ if (!ret)
+ break;
+ }
+ if (!found) {
+ fprintf(stderr, "ERROR: flashrom has no unlock function for this flash chip.\n");
+ return 1;
+ }
+
+ if (ret) {
+ fprintf(stderr, "FAILED!\n");
+ } else {
+ printf("SUCCESS.\n");
+ }
+ return ret;
+}
+
+int lock_flash(struct flashchip *flash)
+{
+ int i, j, k, ret = 0, found = 0;
+ unsigned int start;
+
+ printf("Locking flash chip... ");
+ for (k = 0; k < NUM_LOCKFUNCTIONS; k++) {
+ unsigned int done = 0;
+ struct block_locker locker = flash->block_lockers[k];
+
+ printf_debug("Looking at blockwise lock function %i... ", k);
+ if (!locker.lock && !locker.lockblocks[0].count) {
+ printf_debug("not defined. "
+ "Looking for another lock function.\n");
+ continue;
+ }
+ if (!locker.lock && locker.lockblocks[0].count) {
+ printf_debug("lockblock layout is known, but no "
+ "matching block lock function found. "
+ "Looking for another lock function.\n");
+ continue;
+ }
+ if (locker.lock && !locker.lockblocks[0].count) {
+ printf_debug("block lock function found, but "
+ "lockblock layout is unknown. "
+ "Looking for another lock function.\n");
+ continue;
+ }
+ found = 1;
+ printf_debug("trying... ");
+ for (i = 0; i < NUM_LOCKREGIONS; i++) {
+ /* count==0 for all automatically initialized array
+ * members so the loop below won't be executed for them.
+ */
+ for (j = 0; j < locker.lockblocks[i].count; j++) {
+ start = done + locker.lockblocks[i].size * j;
+ ret = locker.lock(flash, start);
+ printf_debug("0x%06x, ", start);
+ if (ret)
+ break;
+ }
+ if (ret)
+ break;
+ done += locker.lockblocks[i].count *
+ locker.lockblocks[i].size;
+ }
+ printf_debug("\n");
+ /* If everything is OK, don't try another lock function. */
+ if (!ret)
+ break;
+ }
+ if (!found) {
+ fprintf(stderr, "ERROR: flashrom has no lock function for this flash chip.\n");
+ return 1;
+ }
+
+ if (ret) {
+ fprintf(stderr, "FAILED!\n");
+ } else {
+ printf("SUCCESS.\n");
+ }
+ return ret;
+}
+
+int print_lock_status_flash(struct flashchip *flash)
+{
+ int i, j, k, ret = 0, found = 0;
+ unsigned int start, lock_stat = 0;
+
+ printf("Printing Lock Status for flash chip... ");
+ for (k = 0; k < NUM_LOCKFUNCTIONS; k++) {
+ unsigned int done = 0;
+ struct block_locker locker = flash->block_lockers[k];
+
+ printf_debug("Looking at blockwise lock_status function %i... ", k);
+ if (!locker.lock_status && !locker.lockblocks[0].count) {
+ printf_debug("not defined. "
+ "Looking for another lock_status function.\n");
+ continue;
+ }
+ if (!locker.lock_status && locker.lockblocks[0].count) {
+ printf_debug("lockblock layout is known, but no "
+ "matching block lock_status function found. "
+ "Looking for another lock_status function.\n");
+ continue;
+ }
+ if (locker.lock_status && !locker.lockblocks[0].count) {
+ printf_debug("block lock_status function found, but "
+ "lockblock layout is unknown. "
+ "Looking for another lock_status function.\n");
+ continue;
+ }
+ found = 1;
+ printf("trying... ");
+ for (i = 0; i < NUM_LOCKREGIONS; i++) {
+ /* count==0 for all automatically initialized array
+ * members so the loop below won't be executed for them.
+ */
+ for (j = 0; j < locker.lockblocks[i].count; j++) {
+ start = done + locker.lockblocks[i].size * j;
+ ret = locker.lock_status(flash, start);
+ }
+ done += locker.lockblocks[i].count *
+ locker.lockblocks[i].size;
+ if (ret) {
+ break;
+ }
+ }
+ printf("\n");
+ }
+ if (!found) {
+ fprintf(stderr, "WARNING: flashrom has no lock_status function for this flash chip.\n");
+ return 1;
+ }
+
+ if (ret) {
+ fprintf(stderr, "FAILED!\n");
+ } else {
+ printf("SUCCESS.\n");
+ }
+ return ret;
+}
+
void emergency_help_message(void)
{
fprintf(stderr, "Your flash chip is in an unknown state.\n"
"Get help on IRC at irc.freenode.net (channel #flashrom) or\n"
"mail flashrom@flashrom.org!\n--------------------"
"-----------------------------------------------------------\n"
"DO NOT REBOOT OR POWEROFF!\n");
}
/* The way to go if you want a delimited list of programmers*/
void list_programmers(char *delim)
{
enum programmer p;
@@ -1099,26 +1288,28 @@ int main(int argc, char *argv[])
* but right now it allows us to split off the CLI code.
*/
int doit(struct flashchip *flash, int force, char *filename, int read_it, int write_it, int erase_it, int verify_it)
{
uint8_t *buf;
unsigned long numbytes;
FILE *image;
int ret = 0;
unsigned long size;
size = flash->total_size * 1024;
buf = (uint8_t *) calloc(size, sizeof(char));
+ unlock_flash(flash);
+
if (erase_it) {
if (flash->tested & TEST_BAD_ERASE) {
fprintf(stderr, "Erase is not working on this chip. ");
if (!force) {
fprintf(stderr, "Aborting.\n");
programmer_shutdown();
return 1;
} else {
fprintf(stderr, "Continuing anyway.\n");
}
}
if (erase_flash(flash)) {
emergency_help_message();
@@ -485,13 +485,113 @@ int erase_block_jedec(struct flashchip *flash, unsigned int page, unsigned int s
int mask;
mask = getaddrmask(flash);
return erase_block_jedec_common(flash, page, size, mask);
}
int erase_chip_jedec(struct flashchip *flash)
{
int mask;
mask = getaddrmask(flash);
return erase_chip_jedec_common(flash, mask);
}
+
+int unlock_block_jedec(struct flashchip *flash, unsigned int blockaddr)
+{
+ chip_writeb(0, flash->virtual_registers + blockaddr + 2);
+ return 0;
+}
+
+int lock_block_jedec(struct flashchip *flash, unsigned int blockaddr)
+{
+ chip_writeb(1, flash->virtual_registers + blockaddr + 2);
+ return 0;
+}
+
+int lock_status_block_jedec(struct flashchip *flash, unsigned int blockaddr)
+{
+ chipaddr wrprotect = flash->virtual_registers + offset + 2;
+ uint8_t locking;
+
+ printf_debug("Trying to unlock block @0x%08x = 0x%02x\n", offset,
+ chip_readb(wrprotect));
+
+ locking = chip_readb(wrprotect);
+ switch (locking & 0x7) {
+ case 0:
+ printf_debug("Full Access.\n");
+ return 0;
+ case 1:
+ printf_debug("Write Lock (Default State).\n");
+ chip_writeb(0, wrprotect);
+ return 0;
+ case 2:
+ printf_debug("Locked Open (Full Access, Lock Down).\n");
+ return 0;
+ case 3:
+ fprintf(stderr, "Error: Write Lock, Locked Down.\n");
+ return -1;
+ case 4:
+ printf_debug("Read Lock.\n");
+ chip_writeb(0, wrprotect);
+ return 0;
+ case 5:
+ printf_debug("Read/Write Lock.\n");
+ chip_writeb(0, wrprotect);
+ return 0;
+ case 6:
+ fprintf(stderr, "Error: Read Lock, Locked Down.\n");
+ return -1;
+ case 7:
+ fprintf(stderr, "Error: Read/Write Lock, Locked Down.\n");
+ return -1;
+ }
+
+ /* We will never reach this point, but GCC doesn't know */
+ return -1;
+}
+
+int lock_status_chip_jedec(struct flashchip *flash, unsigned int blockaddr)
+{
+ int i, total_size = flash->total_size * 1024;
+ chipaddr bios = flash->virtual_memory;
+ uint8_t locking;
+ int mask;
+
+ mask = getaddrmask(flash);
+ /* Are there any hardware restrictions that we can't overcome?
+ * If flashrom fail here, someone's got to check all those GPIOs.
+ */
+
+ /* Product Identification Entry */
+ chip_writeb(0xAA, bios + (0x5555 & mask));
+ chip_writeb(0x55, bios + (0x2AAA & mask));
+ chip_writeb(0x90, bios + (0x5555 & mask));
+ programmer_delay(10);
+
+ /* Read Hardware Lock Bits */
+ locking = chip_readb(bios + 0xffff2);
+
+ /* Product Identification Exit */
+ chip_writeb(0xAA, bios + (0x5555 & mask));
+ chip_writeb(0x55, bios + (0x2AAA & mask));
+ chip_writeb(0xF0, bios + (0x5555 & mask));
+ programmer_delay(10);
+
+ printf_debug("Lockout bits:\n");
+
+ if (locking & (1 << 2))
+ fprintf(stderr, "Error: hardware bootblock locking (#TBL).\n");
+ else
+ printf_debug("No hardware bootblock locking (good!)\n");
+
+ if (locking & (1 << 3))
+ fprintf(stderr, "Error: hardware block locking (#WP).\n");
+ else
+ printf_debug("No hardware block locking (good!)\n");
+
+ if (locking & ((1 << 2) | (1 << 3)))
+ return -1;
+
+ return 0;
+}