diff --git a/chipdrivers.h b/chipdrivers.h index c5062ca..fb9b1e8 100644 --- a/chipdrivers.h +++ b/chipdrivers.h @@ -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); diff --git a/flash.h b/flash.h index d8f02bb..cd27e60 100644 --- a/flash.h +++ b/flash.h @@ -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 */ diff --git a/flashchips.c b/flashchips.c index f66b95a..174dce6 100644 --- a/flashchips.c +++ b/flashchips.c @@ -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, diff --git a/flashrom.c b/flashrom.c index 326f725..1d5439a 100644 --- a/flashrom.c +++ b/flashrom.c @@ -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(); diff --git a/jedec.c b/jedec.c index 055e910..6b857bf 100644 --- a/jedec.c +++ b/jedec.c @@ -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; +} \ No newline at end of file