aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorSam Leffler <sam@FreeBSD.org>2009-02-05 18:12:07 +0000
committerSam Leffler <sam@FreeBSD.org>2009-02-05 18:12:07 +0000
commit63425a7f25f2d5b32a8a80a1f4bc2d776fe97adc (patch)
treeca2ee3ec2914c2b518d9a0ad88a9c4ee7439e7d9 /sys
parentb8bd4a9751d101a32257bd893d81c008376bb1bb (diff)
downloadsrc-63425a7f25f2d5b32a8a80a1f4bc2d776fe97adc.tar.gz
src-63425a7f25f2d5b32a8a80a1f4bc2d776fe97adc.zip
Add support for frobbing Intel StrataFlash Protection Registers:
o add CFI_SUPPORT_STRATAFLASH compile option to enable support o add new ioctls to get/set the factory and user/oem segments of the PR and to get/set Protection Lock Register that fuses the user segment o add #defines for bits in the status register o update cfi_wait_ready to take an offset so it can be used to wait for PR write completion and replace constants w/ symbolic names Note: writing the user segment isn't correct; committing now to get review. Sponsored by: Carlson Wireless Reviewed by: imp, Chris Anderson
Notes
Notes: svn path=/head/; revision=188156
Diffstat (limited to 'sys')
-rw-r--r--sys/conf/options4
-rw-r--r--sys/dev/cfi/cfi_core.c175
-rw-r--r--sys/dev/cfi/cfi_dev.c22
-rw-r--r--sys/dev/cfi/cfi_reg.h22
-rw-r--r--sys/dev/cfi/cfi_var.h7
-rw-r--r--sys/sys/cfictl.h6
6 files changed, 221 insertions, 15 deletions
diff --git a/sys/conf/options b/sys/conf/options
index 64504fd09fcc..4caff3002f1c 100644
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -804,3 +804,7 @@ TDMA_TXRATE_11A_DEFAULT opt_tdma.h
# Virtualize the network stack
VIMAGE opt_global.h
VIMAGE_GLOBALS opt_global.h
+
+# Common Flash Interface (CFI) options
+CFI_SUPPORT_STRATAFLASH opt_cfi.h
+CFI_ARMEDANDDANGEROUS opt_cfi.h
diff --git a/sys/dev/cfi/cfi_core.c b/sys/dev/cfi/cfi_core.c
index c25e446f2939..4f2eebce8f8f 100644
--- a/sys/dev/cfi/cfi_core.c
+++ b/sys/dev/cfi/cfi_core.c
@@ -30,6 +30,8 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include "opt_cfi.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
@@ -70,7 +72,6 @@ cfi_read(struct cfi_softc *sc, u_int ofs)
val = ~0;
break;
}
-
return (val);
}
@@ -300,10 +301,10 @@ cfi_detach(device_t dev)
}
static int
-cfi_wait_ready(struct cfi_softc *sc, u_int timeout)
+cfi_wait_ready(struct cfi_softc *sc, u_int ofs, u_int timeout)
{
int done, error;
- uint32_t st0, st;
+ uint32_t st0 = 0, st = 0;
done = 0;
error = 0;
@@ -315,21 +316,27 @@ cfi_wait_ready(struct cfi_softc *sc, u_int timeout)
switch (sc->sc_cmdset) {
case CFI_VEND_INTEL_ECS:
case CFI_VEND_INTEL_SCS:
- st = cfi_read(sc, sc->sc_wrofs);
- done = (st & 0x80);
+ st = cfi_read(sc, ofs);
+ done = (st & CFI_INTEL_STATUS_WSMS);
if (done) {
- if (st & 0x02)
+ /* NB: bit 0 is reserved */
+ st &= ~(CFI_INTEL_XSTATUS_RSVD |
+ CFI_INTEL_STATUS_WSMS |
+ CFI_INTEL_STATUS_RSVD);
+ if (st & CFI_INTEL_STATUS_DPS)
error = EPERM;
- else if (st & 0x10)
+ else if (st & CFI_INTEL_STATUS_PSLBS)
error = EIO;
- else if (st & 0x20)
+ else if (st & CFI_INTEL_STATUS_ECLBS)
error = ENXIO;
+ else if (st)
+ error = EACCES;
}
break;
case CFI_VEND_AMD_SCS:
case CFI_VEND_AMD_ECS:
- st0 = cfi_read(sc, sc->sc_wrofs);
- st = cfi_read(sc, sc->sc_wrofs);
+ st0 = cfi_read(sc, ofs);
+ st = cfi_read(sc, ofs);
done = ((st & 0x40) == (st0 & 0x40)) ? 1 : 0;
break;
}
@@ -337,7 +344,7 @@ cfi_wait_ready(struct cfi_softc *sc, u_int timeout)
if (!done && !error)
error = ETIMEDOUT;
if (error)
- printf("\nerror=%d\n", error);
+ printf("\nerror=%d (st 0x%x st0 0x%x)\n", error, st, st0);
return (error);
}
@@ -369,7 +376,7 @@ cfi_write_block(struct cfi_softc *sc)
/* Better safe than sorry... */
return (ENODEV);
}
- error = cfi_wait_ready(sc, sc->sc_erase_timeout);
+ error = cfi_wait_ready(sc, sc->sc_wrofs, sc->sc_erase_timeout);
if (error)
goto out;
@@ -411,7 +418,7 @@ cfi_write_block(struct cfi_softc *sc)
intr_restore(intr);
- error = cfi_wait_ready(sc, sc->sc_write_timeout);
+ error = cfi_wait_ready(sc, sc->sc_wrofs, sc->sc_write_timeout);
if (error)
goto out;
}
@@ -422,3 +429,145 @@ cfi_write_block(struct cfi_softc *sc)
cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
return (error);
}
+
+#ifdef CFI_SUPPORT_STRATAFLASH
+/*
+ * Intel StrataFlash Protection Register Support.
+ *
+ * The memory includes a 128-bit Protection Register that can be
+ * used for security. There are two 64-bit segments; one is programmed
+ * at the factory with a unique 64-bit number which is immutable.
+ * The other segment is left blank for User (OEM) programming.
+ * Once the User/OEM segment is programmed it can be locked
+ * to prevent future programming by writing bit 0 of the Protection
+ * Lock Register (PLR). The PLR can written only once.
+ */
+
+static uint16_t
+cfi_get16(struct cfi_softc *sc, int off)
+{
+ uint16_t v = bus_space_read_2(sc->sc_tag, sc->sc_handle, off<<1);
+ return v;
+}
+
+static void
+cfi_put16(struct cfi_softc *sc, int off, uint16_t v)
+{
+ bus_space_write_2(sc->sc_tag, sc->sc_handle, off<<1, v);
+}
+
+/*
+ * Read the factory-defined 64-bit segment of the PR.
+ */
+int
+cfi_intel_get_factory_pr(struct cfi_softc *sc, uint64_t *id)
+{
+ if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
+ return EOPNOTSUPP;
+ KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
+
+ cfi_write(sc, 0, CFI_INTEL_READ_ID);
+ *id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(0)))<<48 |
+ ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(1)))<<32 |
+ ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(2)))<<16 |
+ ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(3)));
+ cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
+ return 0;
+}
+
+/*
+ * Read the User/OEM 64-bit segment of the PR.
+ */
+int
+cfi_intel_get_oem_pr(struct cfi_softc *sc, uint64_t *id)
+{
+ if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
+ return EOPNOTSUPP;
+ KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
+
+ cfi_write(sc, 0, CFI_INTEL_READ_ID);
+ *id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(4)))<<48 |
+ ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(5)))<<32 |
+ ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(6)))<<16 |
+ ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(7)));
+ cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
+ return 0;
+}
+
+/*
+ * Write the User/OEM 64-bit segment of the PR.
+ */
+int
+cfi_intel_set_oem_pr(struct cfi_softc *sc, uint64_t id)
+{
+ register_t intr;
+ int i, error;
+
+ if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
+ return EOPNOTSUPP;
+ KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
+
+ for (i = 7; i >= 4; i--, id >>= 16) {
+ intr = intr_disable();
+ cfi_write(sc, 0, CFI_INTEL_PP_SETUP);
+ cfi_put16(sc, CFI_INTEL_PR(i), id&0xffff);
+ intr_restore(intr);
+ error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS,
+ sc->sc_write_timeout);
+ if (error)
+ break;
+ }
+ cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
+ return error;
+}
+
+/*
+ * Read the contents of the Protection Lock Register.
+ */
+int
+cfi_intel_get_plr(struct cfi_softc *sc, uint32_t *plr)
+{
+ if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
+ return EOPNOTSUPP;
+ KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
+
+ cfi_write(sc, 0, CFI_INTEL_READ_ID);
+ *plr = cfi_get16(sc, CFI_INTEL_PLR);
+ cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
+ return 0;
+}
+
+/*
+ * Write the Protection Lock Register to lock down the
+ * user-settable segment of the Protection Register.
+ * NOTE: this operation is not reversible.
+ */
+int
+cfi_intel_set_plr(struct cfi_softc *sc)
+{
+#ifdef CFI_ARMEDANDDANGEROUS
+ register_t intr;
+#endif
+ int error;
+
+ if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
+ return EOPNOTSUPP;
+ KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
+
+#ifdef CFI_ARMEDANDDANGEROUS
+ /* worthy of console msg */
+ device_printf(sc->sc_dev, "set PLR\n");
+ intr = intr_disable();
+ cfi_write(sc, 0, CFI_INTEL_PP_SETUP);
+ cfi_put16(sc, CFI_INTEL_PLR, 0xFFFD);
+ intr_restore(intr);
+ error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, sc->sc_write_timeout);
+ cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
+#else
+ device_printf(sc->sc_dev, "%s: PLR not set, "
+ "CFI_ARMEDANDDANGEROUS not configured\n", __func__);
+ error = ENXIO;
+#endif
+ return error;
+}
+#endif /* CFI_SUPPORT_STRATAFLASH */
diff --git a/sys/dev/cfi/cfi_dev.c b/sys/dev/cfi/cfi_dev.c
index b8db1d2d3300..89069cbbe95d 100644
--- a/sys/dev/cfi/cfi_dev.c
+++ b/sys/dev/cfi/cfi_dev.c
@@ -30,6 +30,8 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include "opt_cfi.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
@@ -255,14 +257,13 @@ cfi_devioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
sc = dev->si_drv1;
error = 0;
- switch(cmd) {
+ switch (cmd) {
case CFIOCQRY:
if (sc->sc_writing) {
error = cfi_block_finish(sc);
if (error)
break;
}
-
rq = (struct cfiocqry *)data;
if (rq->offset >= sc->sc_size / sc->sc_width)
return (ESPIPE);
@@ -274,6 +275,23 @@ cfi_devioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
error = copyout(&val, rq->buffer++, 1);
}
break;
+#ifdef CFI_SUPPORT_STRATAFLASH
+ case CFIOCGFACTORYPR:
+ error = cfi_intel_get_factory_pr(sc, (uint64_t *)data);
+ break;
+ case CFIOCGOEMPR:
+ error = cfi_intel_get_oem_pr(sc, (uint64_t *)data);
+ break;
+ case CFIOCSOEMPR:
+ error = cfi_intel_set_oem_pr(sc, *(uint64_t *)data);
+ break;
+ case CFIOCGPLR:
+ error = cfi_intel_get_plr(sc, (uint32_t *)data);
+ break;
+ case CFIOCSPLR:
+ error = cfi_intel_set_plr(sc);
+ break;
+#endif /* CFI_SUPPORT_STRATAFLASH */
default:
error = ENOIOCTL;
break;
diff --git a/sys/dev/cfi/cfi_reg.h b/sys/dev/cfi/cfi_reg.h
index fe4d73059748..d4926781213a 100644
--- a/sys/dev/cfi/cfi_reg.h
+++ b/sys/dev/cfi/cfi_reg.h
@@ -104,6 +104,28 @@ struct cfi_qry {
#define CFI_BCS_CONFIRM 0xd0
#define CFI_BCS_READ_ARRAY 0xff
+/* Intel commands. */
+#define CFI_INTEL_READ_ID 0x90 /* Read Identifier */
+#define CFI_INTEL_PP_SETUP 0xc0 /* Protection Program Setup */
+
+/* NB: these are addresses for 16-bit accesses */
+#define CFI_INTEL_PLR 0x80 /* Protection Lock Register */
+#define CFI_INTEL_PR(n) (0x81+(n)) /* Protection Register */
+
+/* Status register definitions */
+#define CFI_INTEL_STATUS_WSMS 0x0080 /* Write Machine Status */
+#define CFI_INTEL_STATUS_ESS 0x0040 /* Erase Suspend Status */
+#define CFI_INTEL_STATUS_ECLBS 0x0020 /* Erase and Clear Lock-Bit Status */
+#define CFI_INTEL_STATUS_PSLBS 0x0010 /* Program and Set Lock-Bit Status */
+#define CFI_INTEL_STATUS_VPENS 0x0008 /* Programming Voltage Status */
+#define CFI_INTEL_STATUS_PSS 0x0004 /* Program Suspend Status */
+#define CFI_INTEL_STATUS_DPS 0x0002 /* Device Protect Status */
+#define CFI_INTEL_STATUS_RSVD 0x0001 /* reserved */
+
+/* eXtended Status register definitions */
+#define CFI_INTEL_XSTATUS_WBS 0x8000 /* Write Buffer Status */
+#define CFI_INTEL_XSTATUS_RSVD 0x7f00 /* reserved */
+
/* AMD commands. */
#define CFI_AMD_BLOCK_ERASE 0x30
#define CFI_AMD_UNLOCK_ACK 0x55
diff --git a/sys/dev/cfi/cfi_var.h b/sys/dev/cfi/cfi_var.h
index 5c99702df622..d2a778b1e86c 100644
--- a/sys/dev/cfi/cfi_var.h
+++ b/sys/dev/cfi/cfi_var.h
@@ -74,4 +74,11 @@ uint32_t cfi_read(struct cfi_softc *, u_int);
uint8_t cfi_read_qry(struct cfi_softc *, u_int);
int cfi_write_block(struct cfi_softc *);
+#ifdef CFI_SUPPORT_STRATAFLASH
+int cfi_intel_get_factory_pr(struct cfi_softc *sc, uint64_t *);
+int cfi_intel_get_oem_pr(struct cfi_softc *sc, uint64_t *);
+int cfi_intel_set_oem_pr(struct cfi_softc *sc, uint64_t);
+int cfi_intel_get_plr(struct cfi_softc *sc, uint32_t *);
+int cfi_intel_set_plr(struct cfi_softc *sc);
+#endif /* CFI_SUPPORT_STRATAFLASH */
#endif /* _DEV_CFI_VAR_H_ */
diff --git a/sys/sys/cfictl.h b/sys/sys/cfictl.h
index ef6d5d12efeb..a5439e04fbe9 100644
--- a/sys/sys/cfictl.h
+++ b/sys/sys/cfictl.h
@@ -44,4 +44,10 @@ struct cfiocqry {
#define CFIOCQRY _IOWR('q', 0, struct cfiocqry)
+/* Intel StrataFlash Protection Register support */
+#define CFIOCGFACTORYPR _IOR('q', 1, uint64_t) /* get factory protection reg */
+#define CFIOCGOEMPR _IOR('q', 2, uint64_t) /* get oem protection reg */
+#define CFIOCSOEMPR _IOW('q', 3, uint64_t) /* set oem protection reg */
+#define CFIOCGPLR _IOR('q', 4, uint32_t) /* get protection lock reg */
+#define CFIOCSPLR _IO('q', 5) /* set protection log reg */
#endif /* _SYS_CFICTL_H_ */