[flashrom] [PATCH] Warn of one-time programmable (OTP) memory

Stefan Tauner stefan.tauner at student.tuwien.ac.at
Sun Aug 14 05:36:04 CEST 2011


Some flash chips contain OTP memory that we cannot read or write (yet). This
prohibits us from cloning them, hence warn the user if we detect it. Not all
variations of the tagged chips contain OTP memory. They are often only
enabled on request or have there own ordering numbers. There is usually no
way to distinguish them afaict.

The manpage is extended to describe the backgrounds a bit. While i touched
that section, i also reformatted it a bit and removed the reference to the trac
bug reporting facility.

This patch is based on the idea and code of Daniel Lenski.
---
the reason i removed the trac reference is that it is not used, i have no (working)
login and it does not really fits our work flow anyway. the only user that used it
in my active flashrom time ranted about it. would anyone miss it?

the new default verbosity output is:
This chip may contain one-time programmable memory (see man page for details).

the new dbg1/-V output is:
This chip may contain one-time programmable memory.
flashrom cannot read, and may never be able to write it. flashrom may not be
able to completely clone the contents of this chip (see man page for details).

there are certainly more chips with OTP memories out there. additions are
welcome!
i have only tagged chips with user modifiably OTP/security memories. there
are some chips out there which have factory written IDs, but no OTP memory.
a similar warning could/should be printed for those, but i did not want to
abuse the OTP tag for this. also, there is nothing we can do, but read those IDs.
duplicating chips using them is not possible, so a warning in the man page might
suffice?

since OTP memory (or any other data outside the linear address range of the
"main" memory) can not be handled very well in flashrom right now, it is not
clear how OTP memory access could be developed further. of course one could
start to implement the basic methods needed to read them, but how they
should be integrated then is not clear.
one could start by adding an otp_print field to the struct flashchip and just
dump the bytes on probing similar to the lock printing (not on default verbosity
and possibly only if there are bytes different from 0x00 and 0xff).

carl-daniel seems to be quite sceptical regarding handling OTP at all in flashrom.
he stated "it does not really fit our device model at all", which is right, but could
be changed.
he also asked "should we really handle all features present in flash chips?"
and the answer is probably "no", although i don't see a reason why we should
not, if the effort is not too much and the architectural changes needed aren't
obvious NOGOs.
but OTP regions are somewhat special, they are not just *some* feature.

one argument is the one stated in the message printed by this patch. flashrom
is not able to clone chips that have some kind of OTP memory written right now.
imo "cloning" a flash chip seems to be a valid use case for a tool like flashrom as
long as it is feasible (which is not for chips with unique, preconfigured IDs).

the other more theoretical argument i have is: OTP memory is just some
memory in the flash chip. it may need other access patterns, but it is not much
different from other write protected memories apart from that.
some chips implement it in a way that it is even possible to erase the OTP
regions. those regions are just normal flash and are made unwriteable by fuses
in a register or another addressable byte.

Signed-off-by: Daniel Lenski <dlenski at gmail.com>
Signed-off-by: Stefan Tauner <stefan.tauner at student.tuwien.ac.at>
---
 flash.h      |    1 +
 flashchips.c |   69 ++++++++++++++++++++++++++++++++++-----------------------
 flashrom.8   |   22 ++++++++++++++----
 flashrom.c   |    7 ++++++
 4 files changed, 66 insertions(+), 33 deletions(-)

diff --git a/flash.h b/flash.h
index 4b1cca2..f09b2e7 100644
--- a/flash.h
+++ b/flash.h
@@ -91,6 +91,7 @@ enum chipbustype {
 #define FEATURE_ADDR_SHIFTED	(1 << 5)
 #define FEATURE_WRSR_EWSR	(1 << 6)
 #define FEATURE_WRSR_WREN	(1 << 7)
+#define FEATURE_OTP		(1 << 8)
 #define FEATURE_WRSR_EITHER	(FEATURE_WRSR_EWSR | FEATURE_WRSR_WREN)
 
 struct flashchip {
diff --git a/flashchips.c b/flashchips.c
index 1a5622f..d8fd2e9 100644
--- a/flashchips.c
+++ b/flashchips.c
@@ -1120,7 +1120,8 @@ const struct flashchip flashchips[] = {
 		.model_id	= AMIC_A25L032,
 		.total_size	= 4096,
 		.page_size	= 256,
-		.feature_bits	= FEATURE_WRSR_WREN,
+		/* OTP: 64B total; read 0x4B, 0x48; write 0x42 */
+		.feature_bits	= FEATURE_WRSR_WREN | FEATURE_OTP,
 		.tested		= TEST_UNTESTED,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
@@ -1158,7 +1159,8 @@ const struct flashchip flashchips[] = {
 		.model_id	= AMIC_A25LQ032,
 		.total_size	= 4096,
 		.page_size	= 256,
-		.feature_bits	= FEATURE_WRSR_WREN,
+		/* OTP: 64B total; read 0x4B, 0x48; write 0x42 */
+		.feature_bits	= FEATURE_WRSR_WREN | FEATURE_OTP,
 		.tested		= TEST_UNTESTED,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
@@ -1315,7 +1317,8 @@ const struct flashchip flashchips[] = {
 		.model_id	= ATMEL_AT25DF021,
 		.total_size	= 256,
 		.page_size	= 256,
-		.feature_bits	= FEATURE_WRSR_WREN,
+		/* OTP: 128B total, 64B pre-programmed; read 0x77; write 0x9B */
+		.feature_bits	= FEATURE_WRSR_WREN | FEATURE_OTP,
 		.tested		= TEST_UNTESTED,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
@@ -1543,7 +1546,8 @@ const struct flashchip flashchips[] = {
 		.model_id	= ATMEL_AT25DF321A,
 		.total_size	= 4096,
 		.page_size	= 256,
-		.feature_bits	= FEATURE_WRSR_WREN,
+		/* OTP: 128B total, 64B pre-programmed; read 0x77; write 0x9B */
+		.feature_bits	= FEATURE_WRSR_WREN | FEATURE_OTP,
 		.tested		= TEST_UNTESTED,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
@@ -1619,7 +1623,8 @@ const struct flashchip flashchips[] = {
 		.model_id	= ATMEL_AT25DQ161,
 		.total_size	= 2048,
 		.page_size	= 256,
-		.feature_bits	= FEATURE_WRSR_WREN,
+		/* OTP: 128B total, 64B pre-programmed; read 0x77; write 0x9B */
+		.feature_bits	= FEATURE_WRSR_WREN | FEATURE_OTP,
 		.tested		= TEST_UNTESTED,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
@@ -1657,7 +1662,8 @@ const struct flashchip flashchips[] = {
 		.model_id	= ATMEL_AT25F512B,
 		.total_size	= 64,
 		.page_size	= 256,
-		.feature_bits	= FEATURE_WRSR_WREN,
+		/* OTP: 128B total, 64B pre-programmed; read 0x77; write 0x9B */
+		.feature_bits	= FEATURE_WRSR_WREN | FEATURE_OTP,
 		.tested		= TEST_UNTESTED,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
@@ -2168,6 +2174,8 @@ const struct flashchip flashchips[] = {
 		.model_id	= ATMEL_AT45DB321D,
 		.total_size	= 4096 /* Size can only be determined from status register */,
 		.page_size	= 512 /* Size can only be determined from status register */,
+		/* OTP: 128B total, 64B pre-programmed; read 0x77; write 0x9B */
+		.feature_bits	= FEATURE_OTP,
 		.tested		= TEST_BAD_READ,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
@@ -3176,8 +3184,8 @@ const struct flashchip flashchips[] = {
 		.model_id	= EON_EN25Q40,
 		.total_size	= 512,
 		.page_size	= 256,
-		/* TODO: chip features 256-byte one-time programmable region */
-		.feature_bits	= FEATURE_WRSR_WREN,
+		/* OTP: 256B total; enter 0x3A */
+		.feature_bits	= FEATURE_WRSR_WREN | FEATURE_OTP,
 		.tested		= TEST_UNTESTED,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
@@ -3211,8 +3219,8 @@ const struct flashchip flashchips[] = {
 		.model_id	= EON_EN25Q80,
 		.total_size	= 1024,
 		.page_size	= 256,
-		/* TODO: chip features 256-byte one-time programmable region */
-		.feature_bits	= FEATURE_WRSR_WREN,
+		/* OTP: 256B total; enter 0x3A */
+		.feature_bits	= FEATURE_WRSR_WREN | FEATURE_OTP,
 		.tested		= TEST_UNTESTED,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
@@ -3248,9 +3256,8 @@ const struct flashchip flashchips[] = {
 		.model_id	= EON_EN25Q16,
 		.total_size	= 2048,
 		.page_size	= 256,
-		/* TODO: EN25D16 features 512-byte one-time programmable region,
-		 * EN25Q16 features a 128-byte one-time programmable region */
-		.feature_bits	= FEATURE_WRSR_WREN,
+		/* OTP: D16 512B/Q16 128B total; enter 0x3A */
+		.feature_bits	= FEATURE_WRSR_WREN | FEATURE_OTP,
 		.tested		= TEST_UNTESTED,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
@@ -3288,8 +3295,8 @@ const struct flashchip flashchips[] = {
 		.model_id	= EON_EN25Q32,
 		.total_size	= 4096,
 		.page_size	= 256,
-		/* TODO: chip features 512-byte one-time programmable region */
-		.feature_bits	= FEATURE_WRSR_WREN,
+		/* OTP: 512B total; enter 0x3A */
+		.feature_bits	= FEATURE_WRSR_WREN | FEATURE_OTP,
 		.tested		= TEST_UNTESTED,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
@@ -3323,8 +3330,8 @@ const struct flashchip flashchips[] = {
 		.model_id	= EON_EN25Q64,
 		.total_size	= 8192,
 		.page_size	= 256,
-		/* TODO: chip features 512-byte one-time programmable region */
-		.feature_bits	= FEATURE_WRSR_WREN,
+		/* OTP: 512B total; enter 0x3A */
+		.feature_bits	= FEATURE_WRSR_WREN | FEATURE_OTP,
 		.tested		= TEST_UNTESTED,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
@@ -3358,8 +3365,8 @@ const struct flashchip flashchips[] = {
 		.model_id	= EON_EN25Q128,
 		.total_size	= 16384,
 		.page_size	= 256,
-		/* TODO: chip features 512-byte one-time programmable region */
-		.feature_bits	= FEATURE_WRSR_WREN,
+		/* OTP: 512B total; enter 0x3A */
+		.feature_bits	= FEATURE_WRSR_WREN | FEATURE_OTP,
 		.tested		= TEST_UNTESTED,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
@@ -3392,10 +3399,9 @@ const struct flashchip flashchips[] = {
 		.model_id	= EON_EN25QH16,
 		.total_size	= 2048,
 		.page_size	= 256,
-		/* TODO: chip features 512-byte one-time programmable region
-		 * and supports SFDP.
-		 */
-		.feature_bits	= FEATURE_WRSR_WREN,
+		/* supports SFDP */
+		/* OTP: 512B total; enter 0x3A */
+		.feature_bits	= FEATURE_WRSR_WREN | FEATURE_OTP,
 		.tested		= TEST_UNTESTED,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
@@ -6972,6 +6978,8 @@ const struct flashchip flashchips[] = {
 		.model_id	= ST_M25PX16,
 		.total_size	= 2048,
 		.page_size	= 256,
+		/* OTP: 64B total; read 0x4B; write 0x42 */
+		.feature_bits	= FEATURE_OTP,
 		.tested		= TEST_OK_PREW,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
@@ -7840,7 +7848,8 @@ const struct flashchip flashchips[] = {
 		.model_id	= WINBOND_NEX_W25Q80,
 		.total_size	= 1024,
 		.page_size	= 256,
-		.feature_bits	= FEATURE_WRSR_WREN,
+		/* OTP: 1024B total, 256B reserved; read 0x48; write 0x42 */
+		.feature_bits	= FEATURE_WRSR_WREN | FEATURE_OTP,
 		.tested		= TEST_OK_PREW,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
@@ -7877,7 +7886,8 @@ const struct flashchip flashchips[] = {
 		.model_id	= WINBOND_NEX_W25Q16,
 		.total_size	= 2048,
 		.page_size	= 256,
-		.feature_bits	= FEATURE_WRSR_WREN,
+		/* OTP: 1024B total, 256B reserved; read 0x48; write 0x42 */
+		.feature_bits	= FEATURE_WRSR_WREN | FEATURE_OTP,
 		.tested		= TEST_OK_PREW,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
@@ -7914,7 +7924,8 @@ const struct flashchip flashchips[] = {
 		.model_id	= WINBOND_NEX_W25Q32,
 		.total_size	= 4096,
 		.page_size	= 256,
-		.feature_bits	= FEATURE_WRSR_WREN,
+		/* OTP: 1024B total, 256B reserved; read 0x48; write 0x42 */
+		.feature_bits	= FEATURE_WRSR_WREN | FEATURE_OTP,
 		.tested		= TEST_OK_PREW,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
@@ -7951,7 +7962,8 @@ const struct flashchip flashchips[] = {
 		.model_id	= WINBOND_NEX_W25Q64,
 		.total_size	= 8192,
 		.page_size	= 256,
-		.feature_bits	= FEATURE_WRSR_WREN,
+		/* OTP: 1024B total, 256B reserved; read 0x48; write 0x42 */
+		.feature_bits	= FEATURE_WRSR_WREN | FEATURE_OTP,
 		.tested		= TEST_OK_PREW,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
@@ -7987,7 +7999,8 @@ const struct flashchip flashchips[] = {
 		.model_id	= WINBOND_NEX_W25Q128,
 		.total_size	= 16384,
 		.page_size	= 256,
-		.feature_bits	= FEATURE_WRSR_WREN,
+		/* OTP: 1024B total, 256B reserved; read 0x48; write 0x42 */
+		.feature_bits	= FEATURE_WRSR_WREN | FEATURE_OTP,
 		.tested		= TEST_OK_PROBE,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
diff --git a/flashrom.8 b/flashrom.8
index 1b9bf60..b017f9b 100644
--- a/flashrom.8
+++ b/flashrom.8
@@ -600,17 +600,15 @@ in
 .B "/etc/rc.securelevel"
 and rebooting, or rebooting into single user mode.
 .SH BUGS
-Please report any bugs at
-.sp
-.B "  http://www.flashrom.org/trac/flashrom/newticket"
-.sp
-or on the flashrom mailing list at
+Please report any bugs on the flashrom mailing list at
 .B "<flashrom at flashrom.org>"
 .sp
 We recommend to subscribe first at
 .sp
 .B "  http://www.flashrom.org/mailman/listinfo/flashrom"
 .sp
+Laptops
+.RS
 Using flashrom on laptops is dangerous and may easily make your hardware
 unusable unless you can desolder the flash chip and have a full flash chip
 backup. This is caused by the embedded controller (EC) present in many laptops,
@@ -620,6 +618,20 @@ and flashrom will attempt to detect it and abort immediately for safety reasons.
 More information about flashrom on laptops is available from
 .sp
 .B "  http://www.flashrom.org/Laptops"
+.RE
+.sp
+One-time programmable (OTP) memory and unique IDs
+.RS
+Some flash chips contain OTP memory often denoted as "security registers".
+They usually have a capacity in the range of some bytes to a few hundred
+bytes and can be used to give devices unique IDs etc. flashrom is not able
+to read or write these memories and may therefore not be able to duplicate a
+chip completely. For chip types known to include OTP memories a warning is
+printed when they are detected.
+.sp
+Similar to OTP memories are unique, factory programmed, unforgeable IDs.
+They are not modifiable by the user at all.
+.RE
 .SH LICENSE
 .B flashrom
 is covered by the GNU General Public License (GPL), version 2. Some files are
diff --git a/flashrom.c b/flashrom.c
index 07cfdd9..e315bdb 100644
--- a/flashrom.c
+++ b/flashrom.c
@@ -1743,6 +1743,13 @@ int selfcheck(void)
 
 void check_chip_supported(const struct flashchip *flash)
 {
+	if (flash->feature_bits & FEATURE_OTP) {
+		msg_cinfo("This chip may contain one-time programmable memory");
+		msg_cdbg(".\nflashrom cannot read, and may never be able to "
+			 "write it. flashrom may not be\nable to completely "
+			 "clone the contents of this chip");
+		msg_cinfo(" (see man page for details).\n");
+	}
 	if (TEST_OK_MASK != (flash->tested & TEST_OK_MASK)) {
 		msg_cinfo("===\n");
 		if (flash->tested & TEST_BAD_MASK) {
-- 
1.7.1





More information about the flashrom mailing list