Patchwork Locking infrastructure and locking for W39V080FA and PMC 49LF040

login
register
about
Submitter Sean Nelson
Date 2010-02-04 21:11:52
Message ID <4B6B3818.5070401@gmail.com>
Download mbox | patch
Permalink /patch/880/
State Bitrotted
Headers show

Comments

Sean Nelson - 2010-02-04 21:11:52
On 2/4/10 2:18 AM, Carl-Daniel Hailfinger wrote:
> On 04.02.2010 07:29, Sean Nelson wrote:
>    
>> Locking infrastructure
>> Add lock-status command option.
>> Winbond W39V080FA has 64k lock blocks.
>> PMC Pm49FL004 has 64k lock blocks. Tested, but write/erase fails.
>>      
> Hm. That's bad. Does the code work in an older revision (e.g. 0.9.1)?
>
>
>    
The oldest revision I could compile was r714, but I still couldn't erase 
or write. Previous revisions had a "rdmsr" compile problem. The board 
may need a board enable.
>
>> +#define NUM_LOCKFUNCTIONS 3
>>      
> Excessive? I'd say we restrict that to 2 (or maybe even 1) until there
> is a need for more.
>
>
>    

> Semantics... if we have multiple lock printing functions, which result
> do we trust?
>
>    
Well...for example, the Winbond W39V080FA has two write protect status 
functions, one for #TBL/#WP and one for Block Lock status.
>    
>>   {
>>   	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);
>>      
> Bad idea. We don't want to unlock the flash so early, especially if
> we're only planning to read.
>
>
>    
Some of the chips have higher lock states like locking-out reading. 
Also, we would be calling unlock twice otherwise since we need to lock 
and unlock for erase/write.
Some of the chipdrivers unlock, erase/write, and lock for each erase or 
write function.
>
>> +		print_lock_status_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();
>>   			programmer_shutdown();
>>   			return 1;
>>   		}
>>   	} else if (read_it) {
>>   		if (read_flash(flash, filename)) {
>>   			programmer_shutdown();
>>   			return 1;
>>   		}
>> -	} else {
>> +	} else if (write_it | verify_it) {
>>      
> This chance can die as well.
>    
The only times the following code is used is for verifying and writing, 
I don't like using a simple else; but that's just my opinion.
>    
>>   		struct stat image_stat;
>>
>>   		if (flash->tested&  TEST_BAD_ERASE) {
>>   			fprintf(stderr, "Erase is not working on this chip "
>>   				"and erase is needed for write. ");
>>   			if (!force) {
>>   				fprintf(stderr, "Aborting.\n");
>>   				programmer_shutdown();
>>   				return 1;
>>   			} else {
>>   				fprintf(stderr, "Continuing anyway.\n");
>>   			}
>>   		}
>> @@ -1196,26 +1382,28 @@ int doit(struct flashchip *flash, int force, char *filename, int read_it, int wr
>>   			return 1;
>>   		}
>>   		ret = flash->write(flash, buf);
>>   		if (ret) {
>>   			fprintf(stderr, "FAILED!\n");
>>   			emergency_help_message();
>>   			programmer_shutdown();
>>   			return 1;
>>   		} else {
>>   			printf("COMPLETE.\n");
>>   		}
>>   	}
>>
>> +	lock_flash(flash);
>>      
> We probably don't want this either. For starters, it means that a
> previously unlocked chip will now be unusable for all older flashrom
> versions.
>
>
>    
I've removed it but older flashrom versions also did the same locking and unlock sequence for chipdrivers.

>> +
>>   	if (verify_it) {
>>   		/* Work around chips which need some time to calm down. */
>>   		if (write_it)
>>   			programmer_delay(1000*1000);
>>   		ret = verify_flash(flash, buf);
>>   		/* If we tried to write, and verification now fails, we
>>   		 * might have an emergency situation.
>>   		 */
>>   		if (ret&&  write_it)
>>   			emergency_help_message();
>>   	}
>>
>>   	programmer_shutdown();
>> diff --git a/jedec.c b/jedec.c
>> index 055e910..2002fd6 100644
>> --- a/jedec.c
>> +++ b/jedec.c
>> @@ -485,13 +485,33 @@ 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 block)
>> +{
>> +	chip_writeb(0, flash->virtual_registers + block + 2);
>> +	return 0;
>> +}
>> +
>> +int lock_block_jedec(struct flashchip *flash, unsigned int block)
>> +{
>> +	chip_writeb(1, flash->virtual_registers + block + 2);
>> +	return 0;
>> +}
>> +
>> +int lock_status_block_jedec(struct flashchip *flash, unsigned int block)
>> +{
>> +	int lock;
>> +	lock = chip_readb(flash->virtual_registers + block + 2);
>> +	return lock&  0x7;
>> +}
>> +
>>      
>    
I've expanded the lock_status_* functions so themselves print the status.
> Assuming there are multiple lock functions, which one is correct? If
> they all have the same results anyway, there's no point in having more
> than one set of lock functions. If they may have differing results,
> either something is totally broken or there are different types of locks
> (and then we'd have to lock and unlock _all_ of them, not just the first
> one that is successful).
>
> There is another conceptual problem. How do you differentiate between
> - temporary reversible software locks
> - temporary irreversible software locks (irreversible until next reset etc.)
> - permanent software locks (write-once for the whole chip lifetime)
> - unchangeable hardware locks (WP#, BPL#)
> - locks which lock locks (once that "master" lock is set, other locks
> may not be changed)
> - read locks (i.e. chip region will return 0xff)
> - write locks
>
> All of the above exists in some flash chips supported by flashrom.
>    
I just considered the general case with multiple lock states. Non-SPI is 
only being considered in this patch. The Lock functions can/should deal 
with the different lock concepts.
You are more than welcome to take my patch and extend it, I only wrote 
this patch as a starting point for us and was actually expecting you and 
Uwe to expand on it.
 From what I could gleam from the chipdrivers and datasheets is we can 
condense the lock code into my defined lock/unlock/status functions.
> Regards,
> Carl-Daniel
>
>    
~ Sean Nelson
\ No newline at end of file

Patch

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;
+}