From patchwork Fri Jan 28 09:03:27 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: [RFC,+] lock command for flashrom Date: Fri, 28 Jan 2011 09:03:27 -0000 From: Mathias Krause X-Patchwork-Id: 2572 Message-Id: <4D42865F.8030105@secunet.com> To: flashrom Cc: Carl-Daniel Hailfinger Hi, flashrom currently tries to unlock the chip before each read/write/erase command. To prevent further modifications of the chip content, e.g. for security reasons, it would be helpful if flashrom would also be capable of the opposite operation. I talked with Carl-Daniel a little about it and he mentioned problems like having to implement features like partial locks, chips that cannot be unlocked after being locked, etc. To support those features and handle the problematic chips something more than this little patch can do must be implemented. But for now it would be nice to have *something*. See it as a starting point to solve the much bigger problem. This patch adds a function pointer to struct flashchip and a command line option to trigger this function to enable full flash chip write protection. It's currently only implemented for the Atmel AT26DF081A since this is the chip I'm currently working with and which I can test easily. ToDo: Add man page documentation. Signed-off-by: Mathias Krause Index: flash.h =================================================================== --- flash.h (Revision 1256) +++ flash.h (Arbeitskopie) @@ -136,6 +136,7 @@ int (*printlock) (struct flashchip *flash); int (*unlock) (struct flashchip *flash); + int (*lock) (struct flashchip *flash); int (*write) (struct flashchip *flash, uint8_t *buf, int start, int len); int (*read) (struct flashchip *flash, uint8_t *buf, int start, int len); @@ -207,7 +208,7 @@ void print_banner(void); void list_programmers_linebreak(int startcol, int cols, int paren); 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 doit(struct flashchip *flash, int force, char *filename, int read_it, int write_it, int erase_it, int verify_it, int lock_it); int read_buf_from_file(unsigned char *buf, unsigned long size, char *filename); int write_buf_to_file(unsigned char *buf, unsigned long size, char *filename); Index: cli_classic.c =================================================================== --- cli_classic.c (Revision 1256) +++ cli_classic.c (Arbeitskopie) @@ -37,7 +37,7 @@ #if CONFIG_PRINT_WIKI == 1 "-z|" #endif - "-E|-r |-w |-v ]\n" + "-E|-K|-r |-w |-v ]\n" " [-c ] [-m [:]] [-l ]\n" " [-i ] [-p [:]]\n\n"); @@ -57,6 +57,7 @@ " -v | --verify verify flash against " "\n" " -E | --erase erase flash device\n" + " -K | --lock lock flash device\n" " -V | --verbose more verbose output\n" " -c | --chip probe only for specified " "flash chip\n" @@ -85,7 +86,7 @@ #if CONFIG_PRINT_WIKI == 1 "-z, " #endif - "-E, -r, -w, -v or no operation.\n" + "-E, -K, -r, -w, -v or no operation.\n" "If no operation is specified, flashrom will only probe for " "flash chips.\n\n"); } @@ -106,7 +107,7 @@ int opt; int option_index = 0; int force = 0; - int read_it = 0, write_it = 0, erase_it = 0, verify_it = 0; + int read_it = 0, write_it = 0, erase_it = 0, verify_it = 0, lock_it = 0; int dont_verify_it = 0, list_supported = 0; #if CONFIG_PRINT_WIKI == 1 int list_supported_wiki = 0; @@ -114,11 +115,12 @@ int operation_specified = 0; int i; - static const char optstring[] = "r:Rw:v:nVEfc:m:l:i:p:Lzh"; + static const char optstring[] = "r:Rw:v:nVEKfc:m:l:i:p:Lzh"; static const struct option long_options[] = { {"read", 1, NULL, 'r'}, {"write", 1, NULL, 'w'}, {"erase", 0, NULL, 'E'}, + {"lock", 0, NULL, 'K'}, {"verify", 1, NULL, 'v'}, {"noverify", 0, NULL, 'n'}, {"chip", 1, NULL, 'c'}, @@ -208,6 +210,14 @@ } erase_it = 1; break; + case 'K': + if (++operation_specified > 1) { + fprintf(stderr, "More than one operation " + "specified. Aborting.\n"); + cli_classic_abort_usage(); + } + lock_it = 1; + break; case 'm': #if CONFIG_INTERNAL == 1 tempstr = strdup(optarg); @@ -409,14 +419,14 @@ return 1; } - if (!(read_it | write_it | verify_it | erase_it)) { + if (!(read_it | write_it | verify_it | erase_it | lock_it)) { printf("No operations were specified.\n"); // FIXME: flash writes stay enabled! programmer_shutdown(); exit(0); } - if (!filename && !erase_it) { + if (!filename && !(erase_it || lock_it)) { printf("Error: No filename specified.\n"); // FIXME: flash writes stay enabled! programmer_shutdown(); @@ -432,5 +442,5 @@ * Give the chip time to settle. */ programmer_delay(100000); - return doit(flash, force, filename, read_it, write_it, erase_it, verify_it); + return doit(flash, force, filename, read_it, write_it, erase_it, verify_it, lock_it); } Index: spi25.c =================================================================== --- spi25.c (Revision 1256) +++ spi25.c (Arbeitskopie) @@ -1197,6 +1197,44 @@ return 0; } +int spi_enable_blockprotect_at25df(struct flashchip *flash) +{ + uint8_t status; + int result; + + /* Reset SPRL when needed to be able to modify status register */ + status = spi_read_status_register(); + if (status & (1 << 7)) { + msg_cdbg("Need to disable Sector Protection Register Lock\n"); + if ((status & (1 << 4)) == 0) { + msg_cerr("WP# pin is active, disabling " + "write protection is impossible.\n"); + return 1; + } + /* All bits except bit 7 (SPRL) are readonly. */ + result = spi_write_status_register(flash, status & ~(1 << 7)); + if (result) { + msg_cerr("spi_write_status_register failed\n"); + return result; + } + + } + /* Global protect. Make sure to set SPRL as well. */ + result = spi_write_status_register(flash, status | 0xbc); + if (result) { + msg_cerr("spi_write_status_register failed\n"); + return result; + } + status = spi_read_status_register(); + if ((status & (3 << 2)) != (3 << 2)) { + msg_cerr("Block protection could not be enabled!\n"); + return 1; + } + msg_cinfo("Block protection enabled, %s locked!\n", + (status & (1 << 4)) ? "software" : "hardware"); + return 0; +} + int spi_nbyte_read(int address, uint8_t *bytes, int len) { const unsigned char cmd[JEDEC_READ_OUTSIZE] = { Index: flashchips.c =================================================================== --- flashchips.c (Revision 1256) +++ flashchips.c (Arbeitskopie) @@ -1501,6 +1501,7 @@ } }, .unlock = spi_disable_blockprotect_at25df, + .lock = spi_enable_blockprotect_at25df, .write = spi_chip_write_256, .read = spi_chip_read, }, Index: flashrom.c =================================================================== --- flashrom.c (Revision 1256) +++ flashrom.c (Arbeitskopie) @@ -1759,7 +1759,7 @@ /* FIXME: This function signature needs to be improved once doit() has a better * function signature. */ -int chip_safety_check(struct flashchip *flash, int force, char *filename, int read_it, int write_it, int erase_it, int verify_it) +int chip_safety_check(struct flashchip *flash, int force, char *filename, int read_it, int write_it, int erase_it, int verify_it, int lock_it) { if (!programmer_may_write && (write_it || erase_it)) { msg_perr("Write/erase is not working yet on your programmer in " @@ -1809,6 +1809,13 @@ return 1; } } + if (lock_it) { + if (!flash->lock) { + msg_cerr("flashrom has no lock function for this " + "flash chip.\n"); + return 1; + } + } return 0; } @@ -1816,14 +1823,14 @@ * but right now it allows us to split off the CLI code. * Besides that, the function itself is a textbook example of abysmal code flow. */ -int doit(struct flashchip *flash, int force, char *filename, int read_it, int write_it, int erase_it, int verify_it) +int doit(struct flashchip *flash, int force, char *filename, int read_it, int write_it, int erase_it, int verify_it, int lock_it) { uint8_t *oldcontents; uint8_t *newcontents; int ret = 0; unsigned long size = flash->total_size * 1024; - if (chip_safety_check(flash, force, filename, read_it, write_it, erase_it, verify_it)) { + if (chip_safety_check(flash, force, filename, read_it, write_it, erase_it, verify_it, lock_it)) { msg_cerr("Aborting.\n"); ret = 1; goto out_nofree; @@ -1835,6 +1842,11 @@ if (flash->unlock) flash->unlock(flash); + if (lock_it) { + ret = flash->lock(flash); + goto out_nofree; + } + if (read_it) { ret = read_flash_to_file(flash, filename); goto out_nofree; Index: chipdrivers.h =================================================================== --- chipdrivers.h (Revision 1256) +++ chipdrivers.h (Arbeitskopie) @@ -54,6 +54,7 @@ int spi_disable_blockprotect_at25f(struct flashchip *flash); int spi_disable_blockprotect_at25fs010(struct flashchip *flash); int spi_disable_blockprotect_at25fs040(struct flashchip *flash); +int spi_enable_blockprotect_at25df(struct flashchip *flash); int spi_byte_program(int addr, uint8_t databyte); int spi_nbyte_program(int addr, uint8_t *bytes, int len); int spi_nbyte_read(int addr, uint8_t *bytes, int len);