[RFC] Add ability to lock bootblock on Winbond Flash ROMs

Submitted by Hatim Kanchwala on Aug. 26, 2016, 7:29 a.m.

Details

Message ID 6c4709ed-83e5-f6b3-d075-54f0169ab75b@hatimak.me
State New
Headers show

Commit Message

Hatim Kanchwala Aug. 26, 2016, 7:29 a.m.
Hello Timothy and David,

I have attached a patch that adds support for W25Q128BV and W25Q128FV chips (I noticed Timothy needed these chips;) so I added only these for now). This patch should be applied on top of all my most recent patches. Instead of downloading and locally applying all those patches and then applying the attached one, I recommend you clone my fork of flashrom (dev branch) from GitHub (https://github.com/hatimak/flashrom/tree/dev). I have already pushed the attached patch there ;)

Now, on how to use it. Once you compile and install it, have a look at the man page and the --help output. If you run --wp-list, you can see the access protection table. For you particular case you will need --wp-set-range start=0xfff000,len=4. This will lock the top 4kB of either or W25Q128BV or W25Q128FV. The man page will explain how you can set status register write protection if you need that. If it happens that you need OTP support for these chips, that is available as well :)

The CLI is arguably not the best, but this is a more general solution than what you sent. I hope this is sufficient and helps you out. Feel free to reach out for any comments/feedback/help over mail or IRC (hatim on #flashrom).

Thanks,
Hatim

On Wednesday 24 August 2016 05:42 AM, David Hendricks wrote:
> Hi Timothy,
> Check out Hatim's GSOC work for manipulating status register and write protect bits:
> https://patchwork.coreboot.org/patch/4471/
> https://patchwork.coreboot.org/patch/4472/
> https://patchwork.coreboot.org/patch/4469/
> https://patchwork.coreboot.org/patch/4467/
> https://patchwork.coreboot.org/patch/4468/
> https://patchwork.coreboot.org/patch/4470/
> 
> There's also similar work done in Chromium.org fork of flashrom which might do what you need (--wp-enable command): https://chromium.googlesource.com/chromiumos/third_party/flashrom/
> 
Thanks David for your reply :) Seeing my work being used and recommended feels extraordinarily happy ;)

> 
> On Tue, Aug 23, 2016 at 2:32 PM, Timothy Pearson <tpearson@raptorengineering.com <mailto:tpearson@raptorengineering.com>> wrote:
> 
>     RFC PATCH
> 
>     TPM-enabled systems require an immutable (or at least very difficult to modify) CRTM, which can be provided via a locked bootblock sector.
> 
>     Add initial support for locking the upper 4K sector on Winbond Flash ROMs.  Unlocking the sector after it has been locked requires hardware access to the write protect pin, satisfying the above requirements for many use cases.
> 
>     To use, pass the --lock flag to Flashrom.  This flag can be used in isolation, or as part of a normal read / write / verify cycle.
> 
Timothy, thanks for your patch :) Hope what I explained above is helpful to you. And I would gently point out that you forgot the "Signed-off-by" tag in your patch :)

>     Index: spi25_statusreg.c
>     ===================================================================
>     --- spi25_statusreg.c   (revision 1955)
>     +++ spi25_statusreg.c   (working copy)
>     @@ -123,6 +123,78 @@
>             return readarr[0];
>      }
> 
>     +static uint8_t spi_read_status_register_2(struct flashctx *flash)
>     +{
>     +       static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { JEDEC_RDSR2 };
>     +       /* FIXME: No workarounds for driver/hardware bugs in generic code. */
>     +       unsigned char readarr[2]; /* JEDEC_RDSR2_INSIZE=1 but wbsio needs 2 */
>     +       int ret;
>     +
>     +       /* Read Status Register */
>     +       ret = spi_send_command(flash, sizeof(cmd), sizeof(readarr), cmd, readarr);
>     +       if (ret)
>     +               msg_cerr("RDSR2 failed!\n");
>     +
>     +       return readarr[0];
>     +}
>     +
>     +/* Winbond sector protection enable that tries to set the status register bits given. */
>     +int spi_enable_sectorprotect_winbond(struct flashctx *flash, const uint8_t status_flags)
>     +{
>     +       uint8_t status, status2;
>     +       int result;
>     +       uint8_t lock_mask = 0x80;
>     +       uint8_t lock2_mask = 0x1;
>     +
>     +       status = spi_read_status_register(flash);
>     +       status2 = spi_read_status_register_2(flash);
>     +       if ((status & lock_mask) != 0) {
>     +               msg_cdbg("\n\tNeed to disable the register lock first... ");
>     +               if ((status2 & lock2_mask) != 0) {
>     +                       msg_cerr("Hardware protection is active, enabling write protection is impossible.\n");
>     +                       return 1;
>     +               }
>     +               /* All bits except the register lock bit (often called SPRL, SRWD, WPEN) are readonly. */
>     +               result = spi_write_status_register(flash, status & ~lock_mask);
>     +               if (result) {
>     +                       msg_cerr("spi_write_status_register failed.\n");
>     +                       return result;
>     +               }
>     +               status = spi_read_status_register(flash);
>     +               if ((status & lock_mask) != 0) {
>     +                       msg_cerr("Unsetting lock bit(s) failed.\n");
>     +                       return 1;
>     +               }
>     +               msg_cdbg("done.\n");
>     +       }
>     +
>     +       /* Set requested protect bits */
>     +       result = spi_write_status_register(flash, status | (status_flags << 2) );
>     +       if (result) {
>     +               msg_cerr("spi_write_status_register failed.\n");
>     +               return result;
>     +       }
>     +       status = spi_read_status_register(flash);
>     +       if ((status & (status_flags << 2)) != (status_flags << 2)) {
>     +               msg_cerr("Setting lock bit(s) failed.\n");
>     +               return 1;
>     +       }
>     +
>     +       /* Set lock bit */
>     +       result = spi_write_status_register(flash, status | lock_mask);
>     +       if (result) {
>     +               msg_cerr("spi_write_status_register failed.\n");
>     +               return result;
>     +       }
>     +       status = spi_read_status_register(flash);
>     +       if ((status & lock_mask) == 0) {
>     +               msg_cerr("Setting lock bit(s) failed.\n");
>     +               return 1;
>     +       }
>     +
>     +       return 0;
>     +}
>     +
>      /* A generic block protection disable.
>       * Tests if a protection is enabled with the block protection mask (bp_mask) and returns success otherwise.
>       * Tests if the register bits are locked with the lock_mask (lock_mask).
>     Index: flashrom.c
>     ===================================================================
>     --- flashrom.c  (revision 1955)
>     +++ flashrom.c  (working copy)
>     @@ -1977,7 +1977,7 @@
>       * Besides that, the function itself is a textbook example of abysmal code flow.
>       */
>      int doit(struct flashctx *flash, int force, const char *filename, int read_it,
>     -        int write_it, int erase_it, int verify_it)
>     +        int write_it, int erase_it, int verify_it, int lock_it)
>      {
>             uint8_t *oldcontents;
>             uint8_t *newcontents;
>     @@ -2129,6 +2129,13 @@
>                             msg_cinfo("VERIFIED.\n");
>             }
> 
>     +       if (lock_it && flash->chip->lock) {
>     +               msg_cinfo("Locking upper 4K (bootblock)... ");
>     +               ret = flash->chip->lock(flash, 0x11);
>     +               if (!ret)
>     +                       msg_cinfo("LOCKED.\n");
>     +       }
>     +
>      out:
>             free(oldcontents);
>             free(newcontents);
>     Index: cli_classic.c
>     ===================================================================
>     --- cli_classic.c       (revision 1955)
>     +++ cli_classic.c       (working copy)
>     @@ -49,6 +49,7 @@
>                    " -r | --read <file>                 read flash and save to <file>\n"
>                    " -w | --write <file>                write <file> to flash\n"
>                    " -v | --verify <file>               verify flash against <file>\n"
>     +              " -u | --lock                        lock upper Flash region (typically the 4K bootblock)\n"
>                    " -E | --erase                       erase flash memory\n"
>                    " -V | --verbose                     more verbose output\n"
>                    " -c | --chip <chipname>             probe only for specified flash chip\n"
>     @@ -101,18 +102,19 @@
>      #if CONFIG_PRINT_WIKI == 1
>             int list_supported_wiki = 0;
>      #endif
>     -       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, operation_specified = 0;
>             enum programmer prog = PROGRAMMER_INVALID;
>             int ret = 0;
> 
>     -       static const char optstring[] = "r:Rw:v:nVEfc:l:i:p:Lzho:";
>     +       static const char optstring[] = "r:Rw:v:nuVEfc:l:i:p:Lzho:";
>             static const struct option long_options[] = {
>                     {"read",                1, NULL, 'r'},
>                     {"write",               1, NULL, 'w'},
>                     {"erase",               0, NULL, 'E'},
>                     {"verify",              1, NULL, 'v'},
>                     {"noverify",            0, NULL, 'n'},
>     +               {"lock",                0, NULL, 'u'},
>                     {"chip",                1, NULL, 'c'},
>                     {"verbose",             0, NULL, 'V'},
>                     {"force",               0, NULL, 'f'},
>     @@ -187,6 +189,9 @@
>                             }
>                             dont_verify_it = 1;
>                             break;
>     +               case 'u':
>     +                       lock_it = 1;
>     +                       break;
>                     case 'c':
>                             chip_to_probe = strdup(optarg);
>                             break;
>     @@ -522,7 +527,7 @@
>                     goto out_shutdown;
>             }
> 
>     -       if (!(read_it | write_it | verify_it | erase_it)) {
>     +       if (!(read_it | write_it | verify_it | erase_it | lock_it)) {
>                     msg_ginfo("No operations were specified.\n");
>                     goto out_shutdown;
>             }
>     @@ -542,7 +547,7 @@
>              * Give the chip time to settle.
>              */
>             programmer_delay(100000);
>     -       ret |= doit(fill_flash, force, filename, read_it, write_it, erase_it, verify_it);
>     +       ret |= doit(fill_flash, force, filename, read_it, write_it, erase_it, verify_it, lock_it);
> 
>             unmap_flash(fill_flash);
>      out_shutdown:
>     Index: flashchips.c
>     ===================================================================
>     --- flashchips.c        (revision 1955)
>     +++ flashchips.c        (working copy)
>     @@ -14581,6 +14581,7 @@
>                     },
>                     .printlock      = spi_prettyprint_status_register_plain, /* TODO: improve */
>                     .unlock         = spi_disable_blockprotect,
>     +               .lock           = spi_enable_sectorprotect_winbond,
>                     .write          = spi_chip_write_256,
>                     .read           = spi_chip_read,
>                     .voltage        = {2700, 3600},
>     Index: chipdrivers.h
>     ===================================================================
>     --- chipdrivers.h       (revision 1955)
>     +++ chipdrivers.h       (working copy)
>     @@ -73,6 +73,7 @@
>      int spi_prettyprint_status_register_bp4_srwd(struct flashctx *flash);
>      int spi_prettyprint_status_register_bp2_bpl(struct flashctx *flash);
>      int spi_prettyprint_status_register_bp2_tb_bpl(struct flashctx *flash);
>     +int spi_enable_sectorprotect_winbond(struct flashctx *flash, const uint8_t status_flags);
>      int spi_disable_blockprotect(struct flashctx *flash);
>      int spi_disable_blockprotect_bp1_srwd(struct flashctx *flash);
>      int spi_disable_blockprotect_bp2_srwd(struct flashctx *flash);
>     Index: flash.h
>     ===================================================================
>     --- flash.h     (revision 1955)
>     +++ flash.h     (working copy)
>     @@ -200,6 +200,7 @@
>             } block_erasers[NUM_ERASEFUNCTIONS];
> 
>             int (*printlock) (struct flashctx *flash);
>     +       int (*lock) (struct flashctx *flash, const uint8_t status_flags);
>             int (*unlock) (struct flashctx *flash);
>             int (*write) (struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len);
>             int (*read) (struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len);
>     @@ -281,7 +282,7 @@
>      void print_banner(void);
>      void list_programmers_linebreak(int startcol, int cols, int paren);
>      int selfcheck(void);
>     -int doit(struct flashctx *flash, int force, const char *filename, int read_it, int write_it, int erase_it, int verify_it);
>     +int doit(struct flashctx *flash, int force, const 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, const char *filename);
>      int write_buf_to_file(const unsigned char *buf, unsigned long size, const char *filename);
> 
>     Index: spi.h
>     ===================================================================
>     --- spi.h       (revision 1955)
>     +++ spi.h       (working copy)
>     @@ -121,6 +121,11 @@
>      #define JEDEC_RDSR_OUTSIZE     0x01
>      #define JEDEC_RDSR_INSIZE      0x01
> 
>     +/* Read Status Register 2 */
>     +#define JEDEC_RDSR2            0x35
>     +#define JEDEC_RDSR2_OUTSIZE    0x01
>     +#define JEDEC_RDSR2_INSIZE     0x01
>     +
>      /* Status Register Bits */
>      #define SPI_SR_WIP     (0x01 << 0)
>      #define SPI_SR_WEL     (0x01 << 1)
> 
>     _______________________________________________
>     flashrom mailing list
>     flashrom@flashrom.org <mailto:flashrom@flashrom.org>
>     https://www.flashrom.org/mailman/listinfo/flashrom <https://www.flashrom.org/mailman/listinfo/flashrom>
> 
> 
> 
> 
> -- 
> David Hendricks (dhendrix)
> Systems Software Engineer, Google Inc.

Patch hide | download patch | download mbox

From 12bd794218573836302854675b25203d01f60bb1 Mon Sep 17 00:00:00 2001
From: Hatim Kanchwala <hatim@hatimak.me>
Date: Fri, 26 Aug 2016 12:38:34 +0530
Subject: [PATCH] Add support for W25Q128BV and W25Q128FV

Uses new status register, access protection and OTP infrastructure (WIP). Chip definition for "W25Q128.V" needed to be separated into 2 owing to different status register configurations of W25Q128BV and W25Q128FV.

Signed-off-by: Hatim Kanchwala <hatim@hatimak.me>
---
 flashchips.c           | 48 +++++++++++++++++++++++++++++++++++++++++++++---
 otp_layouts.c          |  2 +-
 statusreg_layouts.c    |  4 ++--
 writeprotect_layouts.c |  2 +-
 4 files changed, 49 insertions(+), 7 deletions(-)

diff --git a/flashchips.c b/flashchips.c
index b946cb8..957d63c 100644
--- a/flashchips.c
+++ b/flashchips.c
@@ -14729,70 +14729,112 @@  const struct flashchip flashchips[] = {
 				.eraseblocks = { {8 * 1024 * 1024, 1} },
 				.block_erase = spi_block_erase_60,
 			}, {
 				.eraseblocks = { {8 * 1024 * 1024, 1} },
 				.block_erase = spi_block_erase_c7,
 			}
 		},
 		.status_register = &w25q40bl_64fv_sr,
 		.write		= spi_chip_write_256,
 		.read		= spi_chip_read,
 		.voltage	= {2700, 3600},
 		.wp		= &gd_w_wp,
 		.otp		= &gd_w256_3_otp,
 	},
 
 	{
 		.vendor		= "Winbond",
-		.name		= "W25Q128.V",
+		.name		= "W25Q128BF",
 		.bustype	= BUS_SPI,
 		.manufacture_id	= WINBOND_NEX_ID,
 		.model_id	= WINBOND_NEX_W25Q128_V,
 		.total_size	= 16384,
 		.page_size	= 256,
 		/* supports SFDP */
 		/* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44, read ID 0x4B */
 		.feature_bits	= FEATURE_WRSR_WREN | FEATURE_OTP,
 		.tested		= TEST_OK_PREW,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
 		.block_erasers	=
 		{
 			{
 				.eraseblocks = { {4 * 1024, 4096} },
 				.block_erase = spi_block_erase_20,
 			}, {
 				.eraseblocks = { {32 * 1024, 512} },
 				.block_erase = spi_block_erase_52,
 			}, {
 				.eraseblocks = { {64 * 1024, 256} },
 				.block_erase = spi_block_erase_d8,
 			}, {
 				.eraseblocks = { {16 * 1024 * 1024, 1} },
 				.block_erase = spi_block_erase_60,
 			}, {
 				.eraseblocks = { {16 * 1024 * 1024, 1} },
 				.block_erase = spi_block_erase_c7,
 			}
 		},
-		.printlock	= spi_prettyprint_status_register_plain, /* TODO: improve */
-		.unlock		= spi_disable_blockprotect,
+		.status_register = &w25q40bl_64fv_sr,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+		.wp		= &gd_w_wp,
+		.otp		= &gd_w256_3_otp,
+	},
+
+	{
+		.vendor		= "Winbond",
+		.name		= "W25Q128FV",
+		.bustype	= BUS_SPI,
+		.manufacture_id	= WINBOND_NEX_ID,
+		.model_id	= WINBOND_NEX_W25Q128_V,
+		.total_size	= 16384,
+		.page_size	= 256,
+		/* supports SFDP */
+		/* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44, read ID 0x4B */
+		.feature_bits	= FEATURE_WRSR_WREN | FEATURE_OTP,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 4096} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 512} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 256} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {16 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {16 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.status_register = &w25q128fw_sr,
 		.write		= spi_chip_write_256,
 		.read		= spi_chip_read,
 		.voltage	= {2700, 3600},
+		.wp		= &gd_w_wp,
+		.otp		= &gd_w256_3_otp,
 	},
 
 	{
 		.vendor		= "Winbond",
 		.name		= "W25Q20.W",
 		.bustype	= BUS_SPI,
 		.manufacture_id	= WINBOND_NEX_ID,
 		.model_id	= WINBOND_NEX_W25Q20_W,
 		.total_size	= 256,
 		.page_size	= 256,
 		/* OTP: 256B total; read 0x48; write 0x42, erase 0x44, read ID 0x4B */
 		.feature_bits	= FEATURE_WRSR_WREN | FEATURE_OTP,
 		.tested		= TEST_UNTESTED,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
 		.block_erasers	=
 		{
diff --git a/otp_layouts.c b/otp_layouts.c
index e19516a..5860a4a 100644
--- a/otp_layouts.c
+++ b/otp_layouts.c
@@ -130,35 +130,35 @@  struct otp en512_16384otp = {
 	.region = {
 		{
 			.addr = 0xFFF000,
 			.size = 512,
 			.status_bit = SRP0,
 		},
 	},
 	.status = &eon_status_generic,
 	.print_status = &eon_print_status_generic,
 	.read = &eon_read_generic,
 	.write = &eon_write_generic,
 	.erase = &eon_erase_generic,
 	.lock = &eon_lock_generic,
 };
 
 /* === GigaDevice and Winbond === */
 // FIXME(hatim): Deal with chips with shared OTP modifier bit
-/* GD25LQ16, GD25LQ80, GD25LQ40, W25Q40BL, W25Q64FV
+/* GD25LQ16, GD25LQ80, GD25LQ40, W25Q40BL, W25Q64FV, W25Q128BV
  * (There is an additional 256 bytes security register
  * at 0x000000 which is reserved and can only be read.) */
 // FIXME(hatim): Add support to interact with the reserved security register
 struct otp gd_w256_3_otp = {
 	.region = {
 		{
 			.addr = 0x001000,
 			.size = 256,
 			.status_bit = LB1,
 		}, {
 			.addr = 0x002000,
 			.size = 256,
 			.status_bit = LB2,
 		}, {
 			.addr = 0x003000,
 			.size = 256,
 			.status_bit = LB3,
diff --git a/statusreg_layouts.c b/statusreg_layouts.c
index e83b1b4..cf963b9 100644
--- a/statusreg_layouts.c
+++ b/statusreg_layouts.c
@@ -267,35 +267,35 @@  struct status_register gd25q80b_128_sr = {
 };
 
 /* === Winbond === */
 /* W25Q80, W25Q16, W25Q32 */
 struct status_register w25q80_16_32_sr = {
 	.layout = {
 		{ WIP, WEL, BP0, BP1, BP2, TB, SEC, SRP0 },
 		{ SRP1, QE, RESV, RESV, RESV, RESV, RESV, RESV },
 	},
 	.read		= &spi_read_status_register_generic,
 	.write		= &spi_write_status_register_generic,
 	.print		= &spi_prettyprint_status_register_generic,
 	.print_wp_mode	= &spi_prettyprint_status_register_wp_generic,
 	.get_wp_mode	= &get_wp_mode_generic,
 	.set_wp_mode	= &set_wp_mode_generic,
 };
 
-/* W25Q40BL, W25Q64FV */
+/* W25Q40BL, W25Q64FV, W25Q128BV */
 struct status_register w25q40bl_64fv_sr = {
 	.layout = {
 		{ WIP, WEL, BP0, BP1, BP2, TB, SEC, SRP0 },
 		{ SRP1, QE, RESV, LB1, LB2, LB3, CMP, SUS },
 	},
 	.read		= &spi_read_status_register_generic,
 	.write		= &spi_write_status_register_generic,
 	.print		= &spi_prettyprint_status_register_generic,
 	.print_wp_mode	= &spi_prettyprint_status_register_wp_generic,
 	.get_wp_mode	= &get_wp_mode_generic,
 	.set_wp_mode	= &set_wp_mode_generic,
 };
 
 /* === Triple status registers === */
 /* === GigaDevice === */
 /* GD25LQ05B, GD25LQ10B, GD25LQ20B */
 struct status_register gd25lq05_10_20b_sr = {
@@ -344,31 +344,31 @@  struct status_register gd25q127c_sr = {
 
 /* GD25Q128C */
 struct status_register gd25q128c_sr = {
 	.layout = {
 		{ WIP, WEL, BP0, BP1, BP2, BP3, BP4, SRP0 },
 		{ SRP1, QE, SUS2, LB1, LB2, LB3, CMP, SUS1 },
 		{ RESV, RESV, WPS, RESV, RESV, DRV0, DRV1, RST },
 	},
 	.read		= &spi_read_status_register_generic,
 	.write		= &spi_write_status_register_generic,
 	.print		= &spi_prettyprint_status_register_generic,
 	.print_wp_mode	= &spi_prettyprint_status_register_wp_generic,
 	.get_wp_mode	= &get_wp_mode_generic,
 	.set_wp_mode	= &set_wp_mode_generic,
 };
 
 /* === Winbond === */
-/* W25Q128FW */
+/* W25Q128FW, W25Q128FV */
 struct status_register w25q128fw_sr = {
 	.layout = {
 		{ WIP, WEL, BP0, BP1, BP2, TB, SEC, SRP0 },
 		{ SRP1, QE, RESV, LB1, LB2, LB3, CMP, SUS },
 		{ RESV, RESV, WPS, RESV, RESV, DRV0, DRV1, RST },
 	},
 	.read		= &spi_read_status_register_generic,
 	.write		= &spi_write_status_register_generic,
 	.print		= &spi_prettyprint_status_register_generic,
 	.print_wp_mode	= &spi_prettyprint_status_register_wp_generic,
 	.get_wp_mode	= &get_wp_mode_generic,
 	.set_wp_mode	= &set_wp_mode_generic,
 };
diff --git a/writeprotect_layouts.c b/writeprotect_layouts.c
index 67e59c2..d216b91 100644
--- a/writeprotect_layouts.c
+++ b/writeprotect_layouts.c
@@ -30,35 +30,35 @@ 
  *	int (*set_range) (struct flashctx *flash, uint32_t start, uint32_t len);
  *	int (*disable) (struct flashctx *flash);
  *	int (*print_table) (struct flashctx *flash);
  * };
  */
 
 /* A25LQ032, A25LQ32A */
 struct wp a25l032_32a_wp = {
 	.range_table	= &a25l032_range_table,
 	.bp_bitmask	= &bp_bitmask_generic,
 	.print_table	= &print_table_generic,
 	.set_range	= &set_range_generic,
 	.disable	= &disable_generic,
 };
 
 /* A25L080, A25LQ16, GD25LQ40, GD25LQ80, GD25LQ16, GD25Q16, GD25Q16B GD25Q32(B),
  * GD25Q64(B), GD25Q128B, GD25Q128C, GD25VQ16C, GD25VQ21B, GD25VQ40C, GD25VQ41B,
- * GD25VQ80C, W25Q40BL, W25Q64FV */
+ * GD25VQ80C, W25Q40BL, W25Q64FV, W25Q128BV, W25Q128FV */
 struct wp gd_w_wp = {
 	.range_table	= &sec_block_range_pattern,
 	.bp_bitmask	= &bp_bitmask_generic,
 	.print_table	= &print_table_generic,
 	.set_range	= &set_range_generic,
 	.disable	= &disable_generic,
 };
 
 /* EN25QH128 */
 struct wp en25qh128_wp = {
 	.ranges = (struct range[]){
 		/* BP3 effectively acts as TB bit,
 		 * BP[0..2] function normally. */
 		{ 0x000000, 0 },
 		{ 0xff0000, 64 },
 		{ 0xfe0000, 128 },
 		{ 0xfc0000, 256 },
-- 
Hatim Kanchwala
hatim@hatimak.me