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
This commit is contained in:
Sam Leffler 2009-02-05 18:12:07 +00:00
parent b8bd4a9751
commit 63425a7f25
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=188156
6 changed files with 221 additions and 15 deletions

View File

@ -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

View File

@ -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 */

View File

@ -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;

View File

@ -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

View File

@ -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_ */

View File

@ -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_ */