Changes since v1 [1]:
- Cleanup patch1, simplify flags return in the overwrite case and
consolidate frozen-state cases (Jeff)
- Clarify the motivation for patch2 (Jeff)
- Collect Dave's Reviewed-by
[1]: https://lists.01.org/pipermail/linux-nvdimm/2019-August/023133.html
---
Jeff reported a scenario where ndctl was failing to unlock DIMMs [2].
Through the course of debug it was discovered that the security
interface on the DIMMs was in the 'frozen' state disallowing unlock, or
any security operation. Unfortunately the kernel only showed that the
DIMMs were 'locked', not 'locked' and 'frozen'.
Introduce a new sysfs 'frozen' attribute so that ndctl can reflect the
"security-operations-allowed" state independently of the lock status.
Then, followup with cleanups related to replacing a security-state-enum
with a set of flags.
[2]: https://lists.01.org/pipermail/linux-nvdimm/2019-August/022856.html
---
Dan Williams (3):
libnvdimm/security: Introduce a 'frozen' attribute
libnvdimm/security: Tighten scope of nvdimm->busy vs security operations
libnvdimm/security: Consolidate 'security' operations
drivers/acpi/nfit/intel.c | 59 ++++++-----
drivers/nvdimm/bus.c | 2
drivers/nvdimm/dimm_devs.c | 134 ++++++--------------------
drivers/nvdimm/nd-core.h | 51 ++++------
drivers/nvdimm/security.c | 199 +++++++++++++++++++++++++-------------
include/linux/libnvdimm.h | 9 +-
tools/testing/nvdimm/dimm_devs.c | 19 +---
7 files changed, 226 insertions(+), 247 deletions(-)
In the process of debugging a system with an NVDIMM that was failing to
unlock it was found that the kernel is reporting 'locked' while the DIMM
security interface is 'frozen'. Unfortunately the security state is
tracked internally as an enum which prevents it from communicating the
difference between 'locked' and 'locked + frozen'. It follows that the
enum also prevents the kernel from communicating 'unlocked + frozen'
which would be useful for debugging why security operations like 'change
passphrase' are disabled.
Ditch the security state enum for a set of flags and introduce a new
sysfs attribute explicitly for the 'frozen' state. The regression risk
is low because the 'frozen' state was already blocked behind the
'locked' state, but will need to revisit if there were cases where
applications need 'frozen' to show up in the primary 'security'
attribute. The expectation is that communicating 'frozen' is mostly a
helper for debug and status monitoring.
Reviewed-by: Dave Jiang <[email protected]>
Reported-by: Jeff Moyer <[email protected]>
Signed-off-by: Dan Williams <[email protected]>
---
drivers/acpi/nfit/intel.c | 59 ++++++++++++-----------
drivers/nvdimm/bus.c | 2 -
drivers/nvdimm/dimm_devs.c | 59 +++++++++++++----------
drivers/nvdimm/nd-core.h | 21 ++++++--
drivers/nvdimm/security.c | 99 ++++++++++++++++++--------------------
include/linux/libnvdimm.h | 9 ++-
tools/testing/nvdimm/dimm_devs.c | 19 ++-----
7 files changed, 141 insertions(+), 127 deletions(-)
diff --git a/drivers/acpi/nfit/intel.c b/drivers/acpi/nfit/intel.c
index cddd0fcf622c..1113b679cd7b 100644
--- a/drivers/acpi/nfit/intel.c
+++ b/drivers/acpi/nfit/intel.c
@@ -7,10 +7,11 @@
#include "intel.h"
#include "nfit.h"
-static enum nvdimm_security_state intel_security_state(struct nvdimm *nvdimm,
+static unsigned long intel_security_flags(struct nvdimm *nvdimm,
enum nvdimm_passphrase_type ptype)
{
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+ unsigned long security_flags = 0;
struct {
struct nd_cmd_pkg pkg;
struct nd_intel_get_security_state cmd;
@@ -27,7 +28,7 @@ static enum nvdimm_security_state intel_security_state(struct nvdimm *nvdimm,
int rc;
if (!test_bit(NVDIMM_INTEL_GET_SECURITY_STATE, &nfit_mem->dsm_mask))
- return -ENXIO;
+ return 0;
/*
* Short circuit the state retrieval while we are doing overwrite.
@@ -35,38 +36,42 @@ static enum nvdimm_security_state intel_security_state(struct nvdimm *nvdimm,
* until the overwrite DSM completes.
*/
if (nvdimm_in_overwrite(nvdimm) && ptype == NVDIMM_USER)
- return NVDIMM_SECURITY_OVERWRITE;
+ return BIT(NVDIMM_SECURITY_OVERWRITE);
rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
- if (rc < 0)
- return rc;
- if (nd_cmd.cmd.status)
- return -EIO;
+ if (rc < 0 || nd_cmd.cmd.status) {
+ pr_err("%s: security state retrieval failed (%d:%#x)\n",
+ nvdimm_name(nvdimm), rc, nd_cmd.cmd.status);
+ return 0;
+ }
/* check and see if security is enabled and locked */
if (ptype == NVDIMM_MASTER) {
if (nd_cmd.cmd.extended_state & ND_INTEL_SEC_ESTATE_ENABLED)
- return NVDIMM_SECURITY_UNLOCKED;
- else if (nd_cmd.cmd.extended_state &
- ND_INTEL_SEC_ESTATE_PLIMIT)
- return NVDIMM_SECURITY_FROZEN;
- } else {
- if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_UNSUPPORTED)
- return -ENXIO;
- else if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_ENABLED) {
- if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_LOCKED)
- return NVDIMM_SECURITY_LOCKED;
- else if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_FROZEN
- || nd_cmd.cmd.state &
- ND_INTEL_SEC_STATE_PLIMIT)
- return NVDIMM_SECURITY_FROZEN;
- else
- return NVDIMM_SECURITY_UNLOCKED;
- }
+ set_bit(NVDIMM_SECURITY_UNLOCKED, &security_flags);
+ else
+ set_bit(NVDIMM_SECURITY_DISABLED, &security_flags);
+ if (nd_cmd.cmd.extended_state & ND_INTEL_SEC_ESTATE_PLIMIT)
+ set_bit(NVDIMM_SECURITY_FROZEN, &security_flags);
+ return security_flags;
}
- /* this should cover master security disabled as well */
- return NVDIMM_SECURITY_DISABLED;
+ if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_UNSUPPORTED)
+ return 0;
+
+ if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_ENABLED) {
+ if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_FROZEN ||
+ nd_cmd.cmd.state & ND_INTEL_SEC_STATE_PLIMIT)
+ set_bit(NVDIMM_SECURITY_FROZEN, &security_flags);
+
+ if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_LOCKED)
+ set_bit(NVDIMM_SECURITY_LOCKED, &security_flags);
+ else
+ set_bit(NVDIMM_SECURITY_UNLOCKED, &security_flags);
+ } else
+ set_bit(NVDIMM_SECURITY_DISABLED, &security_flags);
+
+ return security_flags;
}
static int intel_security_freeze(struct nvdimm *nvdimm)
@@ -371,7 +376,7 @@ static void nvdimm_invalidate_cache(void)
#endif
static const struct nvdimm_security_ops __intel_security_ops = {
- .state = intel_security_state,
+ .get_flags = intel_security_flags,
.freeze = intel_security_freeze,
.change_key = intel_security_change_key,
.disable = intel_security_disable,
diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c
index 798c5c4aea9c..29479d3b01b0 100644
--- a/drivers/nvdimm/bus.c
+++ b/drivers/nvdimm/bus.c
@@ -400,7 +400,7 @@ static int child_unregister(struct device *dev, void *data)
/* We are shutting down. Make state frozen artificially. */
nvdimm_bus_lock(dev);
- nvdimm->sec.state = NVDIMM_SECURITY_FROZEN;
+ set_bit(NVDIMM_SECURITY_FROZEN, &nvdimm->sec.flags);
if (test_and_clear_bit(NDD_WORK_PENDING, &nvdimm->flags))
dev_put = true;
nvdimm_bus_unlock(dev);
diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c
index 29a065e769ea..53330625fe07 100644
--- a/drivers/nvdimm/dimm_devs.c
+++ b/drivers/nvdimm/dimm_devs.c
@@ -372,24 +372,27 @@ __weak ssize_t security_show(struct device *dev,
{
struct nvdimm *nvdimm = to_nvdimm(dev);
- switch (nvdimm->sec.state) {
- case NVDIMM_SECURITY_DISABLED:
+ if (test_bit(NVDIMM_SECURITY_DISABLED, &nvdimm->sec.flags))
return sprintf(buf, "disabled\n");
- case NVDIMM_SECURITY_UNLOCKED:
+ if (test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.flags))
return sprintf(buf, "unlocked\n");
- case NVDIMM_SECURITY_LOCKED:
+ if (test_bit(NVDIMM_SECURITY_LOCKED, &nvdimm->sec.flags))
return sprintf(buf, "locked\n");
- case NVDIMM_SECURITY_FROZEN:
- return sprintf(buf, "frozen\n");
- case NVDIMM_SECURITY_OVERWRITE:
+ if (test_bit(NVDIMM_SECURITY_OVERWRITE, &nvdimm->sec.flags))
return sprintf(buf, "overwrite\n");
- default:
- return -ENOTTY;
- }
-
return -ENOTTY;
}
+static ssize_t frozen_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct nvdimm *nvdimm = to_nvdimm(dev);
+
+ return sprintf(buf, "%d\n", test_bit(NVDIMM_SECURITY_FROZEN,
+ &nvdimm->sec.flags));
+}
+static DEVICE_ATTR_RO(frozen);
+
#define OPS \
C( OP_FREEZE, "freeze", 1), \
C( OP_DISABLE, "disable", 2), \
@@ -501,6 +504,7 @@ static struct attribute *nvdimm_attributes[] = {
&dev_attr_commands.attr,
&dev_attr_available_slots.attr,
&dev_attr_security.attr,
+ &dev_attr_frozen.attr,
NULL,
};
@@ -509,17 +513,24 @@ static umode_t nvdimm_visible(struct kobject *kobj, struct attribute *a, int n)
struct device *dev = container_of(kobj, typeof(*dev), kobj);
struct nvdimm *nvdimm = to_nvdimm(dev);
- if (a != &dev_attr_security.attr)
+ if (a != &dev_attr_security.attr && a != &dev_attr_frozen.attr)
return a->mode;
- if (nvdimm->sec.state < 0)
+ if (!nvdimm->sec.flags)
return 0;
- /* Are there any state mutation ops? */
- if (nvdimm->sec.ops->freeze || nvdimm->sec.ops->disable
- || nvdimm->sec.ops->change_key
- || nvdimm->sec.ops->erase
- || nvdimm->sec.ops->overwrite)
+
+ if (a == &dev_attr_security.attr) {
+ /* Are there any state mutation ops (make writable)? */
+ if (nvdimm->sec.ops->freeze || nvdimm->sec.ops->disable
+ || nvdimm->sec.ops->change_key
+ || nvdimm->sec.ops->erase
+ || nvdimm->sec.ops->overwrite)
+ return a->mode;
+ return 0444;
+ }
+
+ if (nvdimm->sec.ops->freeze)
return a->mode;
- return 0444;
+ return 0;
}
struct attribute_group nvdimm_attribute_group = {
@@ -569,8 +580,8 @@ struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus,
* attribute visibility.
*/
/* get security state and extended (master) state */
- nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
- nvdimm->sec.ext_state = nvdimm_security_state(nvdimm, NVDIMM_MASTER);
+ nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
+ nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm, NVDIMM_MASTER);
nd_device_register(dev);
return nvdimm;
@@ -588,7 +599,7 @@ int nvdimm_security_setup_events(struct device *dev)
{
struct nvdimm *nvdimm = to_nvdimm(dev);
- if (nvdimm->sec.state < 0 || !nvdimm->sec.ops
+ if (!nvdimm->sec.flags || !nvdimm->sec.ops
|| !nvdimm->sec.ops->overwrite)
return 0;
nvdimm->sec.overwrite_state = sysfs_get_dirent(dev->kobj.sd, "security");
@@ -614,7 +625,7 @@ int nvdimm_security_freeze(struct nvdimm *nvdimm)
if (!nvdimm->sec.ops || !nvdimm->sec.ops->freeze)
return -EOPNOTSUPP;
- if (nvdimm->sec.state < 0)
+ if (!nvdimm->sec.flags)
return -EIO;
if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
@@ -623,7 +634,7 @@ int nvdimm_security_freeze(struct nvdimm *nvdimm)
}
rc = nvdimm->sec.ops->freeze(nvdimm);
- nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
+ nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
return rc;
}
diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h
index 0ac52b6eb00e..da2bbfd56d9f 100644
--- a/drivers/nvdimm/nd-core.h
+++ b/drivers/nvdimm/nd-core.h
@@ -39,21 +39,32 @@ struct nvdimm {
const char *dimm_id;
struct {
const struct nvdimm_security_ops *ops;
- enum nvdimm_security_state state;
- enum nvdimm_security_state ext_state;
+ unsigned long flags;
+ unsigned long ext_flags;
unsigned int overwrite_tmo;
struct kernfs_node *overwrite_state;
} sec;
struct delayed_work dwork;
};
-static inline enum nvdimm_security_state nvdimm_security_state(
+static inline unsigned long nvdimm_security_flags(
struct nvdimm *nvdimm, enum nvdimm_passphrase_type ptype)
{
+ u64 flags;
+ const u64 state_flags = 1UL << NVDIMM_SECURITY_DISABLED
+ | 1UL << NVDIMM_SECURITY_LOCKED
+ | 1UL << NVDIMM_SECURITY_UNLOCKED
+ | 1UL << NVDIMM_SECURITY_OVERWRITE;
+
if (!nvdimm->sec.ops)
- return -ENXIO;
+ return 0;
- return nvdimm->sec.ops->state(nvdimm, ptype);
+ flags = nvdimm->sec.ops->get_flags(nvdimm, ptype);
+ /* disabled, locked, unlocked, and overwrite are mutually exclusive */
+ dev_WARN_ONCE(&nvdimm->dev, hweight64(flags & state_flags) > 1,
+ "reported invalid security state: %#llx\n",
+ (unsigned long long) flags);
+ return flags;
}
int nvdimm_security_freeze(struct nvdimm *nvdimm);
#if IS_ENABLED(CONFIG_NVDIMM_KEYS)
diff --git a/drivers/nvdimm/security.c b/drivers/nvdimm/security.c
index a570f2263a42..5862d0eee9db 100644
--- a/drivers/nvdimm/security.c
+++ b/drivers/nvdimm/security.c
@@ -158,7 +158,7 @@ static int nvdimm_key_revalidate(struct nvdimm *nvdimm)
}
nvdimm_put_key(key);
- nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
+ nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
return 0;
}
@@ -174,7 +174,7 @@ static int __nvdimm_security_unlock(struct nvdimm *nvdimm)
lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
if (!nvdimm->sec.ops || !nvdimm->sec.ops->unlock
- || nvdimm->sec.state < 0)
+ || !nvdimm->sec.flags)
return -EIO;
if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
@@ -189,7 +189,7 @@ static int __nvdimm_security_unlock(struct nvdimm *nvdimm)
* freeze of the security configuration. I.e. if the OS does not
* have the key, security is being managed pre-OS.
*/
- if (nvdimm->sec.state == NVDIMM_SECURITY_UNLOCKED) {
+ if (test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.flags)) {
if (!key_revalidate)
return 0;
@@ -202,7 +202,7 @@ static int __nvdimm_security_unlock(struct nvdimm *nvdimm)
rc == 0 ? "success" : "fail");
nvdimm_put_key(key);
- nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
+ nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
return rc;
}
@@ -217,6 +217,24 @@ int nvdimm_security_unlock(struct device *dev)
return rc;
}
+static int check_security_state(struct nvdimm *nvdimm)
+{
+ struct device *dev = &nvdimm->dev;
+
+ if (test_bit(NVDIMM_SECURITY_FROZEN, &nvdimm->sec.flags)) {
+ dev_dbg(dev, "Incorrect security state: %#lx\n",
+ nvdimm->sec.flags);
+ return -EIO;
+ }
+
+ if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
+ dev_dbg(dev, "Security operation in progress.\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid)
{
struct device *dev = &nvdimm->dev;
@@ -229,19 +247,12 @@ int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid)
lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
if (!nvdimm->sec.ops || !nvdimm->sec.ops->disable
- || nvdimm->sec.state < 0)
+ || !nvdimm->sec.flags)
return -EOPNOTSUPP;
- if (nvdimm->sec.state >= NVDIMM_SECURITY_FROZEN) {
- dev_dbg(dev, "Incorrect security state: %d\n",
- nvdimm->sec.state);
- return -EIO;
- }
-
- if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
- dev_dbg(dev, "Security operation in progress.\n");
- return -EBUSY;
- }
+ rc = check_security_state(nvdimm);
+ if (rc)
+ return rc;
data = nvdimm_get_user_key_payload(nvdimm, keyid,
NVDIMM_BASE_KEY, &key);
@@ -253,7 +264,7 @@ int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid)
rc == 0 ? "success" : "fail");
nvdimm_put_key(key);
- nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
+ nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
return rc;
}
@@ -271,14 +282,12 @@ int nvdimm_security_update(struct nvdimm *nvdimm, unsigned int keyid,
lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
if (!nvdimm->sec.ops || !nvdimm->sec.ops->change_key
- || nvdimm->sec.state < 0)
+ || !nvdimm->sec.flags)
return -EOPNOTSUPP;
- if (nvdimm->sec.state >= NVDIMM_SECURITY_FROZEN) {
- dev_dbg(dev, "Incorrect security state: %d\n",
- nvdimm->sec.state);
- return -EIO;
- }
+ rc = check_security_state(nvdimm);
+ if (rc)
+ return rc;
data = nvdimm_get_user_key_payload(nvdimm, keyid,
NVDIMM_BASE_KEY, &key);
@@ -301,10 +310,10 @@ int nvdimm_security_update(struct nvdimm *nvdimm, unsigned int keyid,
nvdimm_put_key(newkey);
nvdimm_put_key(key);
if (pass_type == NVDIMM_MASTER)
- nvdimm->sec.ext_state = nvdimm_security_state(nvdimm,
+ nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm,
NVDIMM_MASTER);
else
- nvdimm->sec.state = nvdimm_security_state(nvdimm,
+ nvdimm->sec.flags = nvdimm_security_flags(nvdimm,
NVDIMM_USER);
return rc;
}
@@ -322,7 +331,7 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid,
lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
if (!nvdimm->sec.ops || !nvdimm->sec.ops->erase
- || nvdimm->sec.state < 0)
+ || !nvdimm->sec.flags)
return -EOPNOTSUPP;
if (atomic_read(&nvdimm->busy)) {
@@ -330,18 +339,11 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid,
return -EBUSY;
}
- if (nvdimm->sec.state >= NVDIMM_SECURITY_FROZEN) {
- dev_dbg(dev, "Incorrect security state: %d\n",
- nvdimm->sec.state);
- return -EIO;
- }
-
- if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
- dev_dbg(dev, "Security operation in progress.\n");
- return -EBUSY;
- }
+ rc = check_security_state(nvdimm);
+ if (rc)
+ return rc;
- if (nvdimm->sec.ext_state != NVDIMM_SECURITY_UNLOCKED
+ if (!test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.ext_flags)
&& pass_type == NVDIMM_MASTER) {
dev_dbg(dev,
"Attempt to secure erase in wrong master state.\n");
@@ -359,7 +361,7 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid,
rc == 0 ? "success" : "fail");
nvdimm_put_key(key);
- nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
+ nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
return rc;
}
@@ -375,7 +377,7 @@ int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid)
lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
if (!nvdimm->sec.ops || !nvdimm->sec.ops->overwrite
- || nvdimm->sec.state < 0)
+ || !nvdimm->sec.flags)
return -EOPNOTSUPP;
if (atomic_read(&nvdimm->busy)) {
@@ -388,16 +390,9 @@ int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid)
return -EINVAL;
}
- if (nvdimm->sec.state >= NVDIMM_SECURITY_FROZEN) {
- dev_dbg(dev, "Incorrect security state: %d\n",
- nvdimm->sec.state);
- return -EIO;
- }
-
- if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
- dev_dbg(dev, "Security operation in progress.\n");
- return -EBUSY;
- }
+ rc = check_security_state(nvdimm);
+ if (rc)
+ return rc;
data = nvdimm_get_user_key_payload(nvdimm, keyid,
NVDIMM_BASE_KEY, &key);
@@ -412,7 +407,7 @@ int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid)
if (rc == 0) {
set_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags);
set_bit(NDD_WORK_PENDING, &nvdimm->flags);
- nvdimm->sec.state = NVDIMM_SECURITY_OVERWRITE;
+ set_bit(NVDIMM_SECURITY_OVERWRITE, &nvdimm->sec.flags);
/*
* Make sure we don't lose device while doing overwrite
* query.
@@ -443,7 +438,7 @@ void __nvdimm_security_overwrite_query(struct nvdimm *nvdimm)
tmo = nvdimm->sec.overwrite_tmo;
if (!nvdimm->sec.ops || !nvdimm->sec.ops->query_overwrite
- || nvdimm->sec.state < 0)
+ || !nvdimm->sec.flags)
return;
rc = nvdimm->sec.ops->query_overwrite(nvdimm);
@@ -467,8 +462,8 @@ void __nvdimm_security_overwrite_query(struct nvdimm *nvdimm)
clear_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags);
clear_bit(NDD_WORK_PENDING, &nvdimm->flags);
put_device(&nvdimm->dev);
- nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
- nvdimm->sec.ext_state = nvdimm_security_state(nvdimm, NVDIMM_MASTER);
+ nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
+ nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_MASTER);
}
void nvdimm_security_overwrite_query(struct work_struct *work)
diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
index 7a64b3ddb408..b6eddf912568 100644
--- a/include/linux/libnvdimm.h
+++ b/include/linux/libnvdimm.h
@@ -160,8 +160,11 @@ static inline struct nd_blk_region_desc *to_blk_region_desc(
}
-enum nvdimm_security_state {
- NVDIMM_SECURITY_ERROR = -1,
+/*
+ * Note that separate bits for locked + unlocked are defined so that
+ * 'flags == 0' corresponds to an error / not-supported state.
+ */
+enum nvdimm_security_bits {
NVDIMM_SECURITY_DISABLED,
NVDIMM_SECURITY_UNLOCKED,
NVDIMM_SECURITY_LOCKED,
@@ -182,7 +185,7 @@ enum nvdimm_passphrase_type {
};
struct nvdimm_security_ops {
- enum nvdimm_security_state (*state)(struct nvdimm *nvdimm,
+ unsigned long (*get_flags)(struct nvdimm *nvdimm,
enum nvdimm_passphrase_type pass_type);
int (*freeze)(struct nvdimm *nvdimm);
int (*change_key)(struct nvdimm *nvdimm,
diff --git a/tools/testing/nvdimm/dimm_devs.c b/tools/testing/nvdimm/dimm_devs.c
index 2d4baf57822f..57bd27dedf1f 100644
--- a/tools/testing/nvdimm/dimm_devs.c
+++ b/tools/testing/nvdimm/dimm_devs.c
@@ -18,24 +18,13 @@ ssize_t security_show(struct device *dev,
* For the test version we need to poll the "hardware" in order
* to get the updated status for unlock testing.
*/
- nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
- nvdimm->sec.ext_state = nvdimm_security_state(nvdimm, NVDIMM_MASTER);
+ nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
- switch (nvdimm->sec.state) {
- case NVDIMM_SECURITY_DISABLED:
+ if (test_bit(NVDIMM_SECURITY_DISABLED, &nvdimm->sec.flags))
return sprintf(buf, "disabled\n");
- case NVDIMM_SECURITY_UNLOCKED:
+ if (test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.flags))
return sprintf(buf, "unlocked\n");
- case NVDIMM_SECURITY_LOCKED:
+ if (test_bit(NVDIMM_SECURITY_LOCKED, &nvdimm->sec.flags))
return sprintf(buf, "locked\n");
- case NVDIMM_SECURITY_FROZEN:
- return sprintf(buf, "frozen\n");
- case NVDIMM_SECURITY_OVERWRITE:
- return sprintf(buf, "overwrite\n");
- default:
- return -ENOTTY;
- }
-
return -ENOTTY;
}
-
An attempt to freeze DIMMs currently runs afoul of default blocking of
all security operations in the entry to the 'store' routine for the
'security' sysfs attribute.
The blanket blocking of all security operations while the DIMM is in
active use in a region is too restrictive. The only security operations
that need to be aware of the ->busy state are those that mutate the
state of data, i.e. erase and overwrite.
Refactor the ->busy checks to be applied at the entry common entry point
in __security_store() rather than each of the helper routines to enable
freeze to be run regardless of busy state.
Reviewed-by: Dave Jiang <[email protected]>
Signed-off-by: Dan Williams <[email protected]>
---
drivers/nvdimm/dimm_devs.c | 33 ++++++++++++++++-----------------
drivers/nvdimm/security.c | 10 ----------
2 files changed, 16 insertions(+), 27 deletions(-)
diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c
index 53330625fe07..d837cb9be83d 100644
--- a/drivers/nvdimm/dimm_devs.c
+++ b/drivers/nvdimm/dimm_devs.c
@@ -424,9 +424,6 @@ static ssize_t __security_store(struct device *dev, const char *buf, size_t len)
unsigned int key, newkey;
int i;
- if (atomic_read(&nvdimm->busy))
- return -EBUSY;
-
rc = sscanf(buf, "%"__stringify(SEC_CMD_SIZE)"s"
" %"__stringify(KEY_ID_SIZE)"s"
" %"__stringify(KEY_ID_SIZE)"s",
@@ -451,23 +448,25 @@ static ssize_t __security_store(struct device *dev, const char *buf, size_t len)
} else if (i == OP_DISABLE) {
dev_dbg(dev, "disable %u\n", key);
rc = nvdimm_security_disable(nvdimm, key);
- } else if (i == OP_UPDATE) {
- dev_dbg(dev, "update %u %u\n", key, newkey);
- rc = nvdimm_security_update(nvdimm, key, newkey, NVDIMM_USER);
- } else if (i == OP_ERASE) {
- dev_dbg(dev, "erase %u\n", key);
- rc = nvdimm_security_erase(nvdimm, key, NVDIMM_USER);
+ } else if (i == OP_UPDATE || i == OP_MASTER_UPDATE) {
+ dev_dbg(dev, "%s %u %u\n", ops[i].name, key, newkey);
+ rc = nvdimm_security_update(nvdimm, key, newkey, i == OP_UPDATE
+ ? NVDIMM_USER : NVDIMM_MASTER);
+ } else if (i == OP_ERASE || i == OP_MASTER_ERASE) {
+ dev_dbg(dev, "%s %u\n", ops[i].name, key);
+ if (atomic_read(&nvdimm->busy)) {
+ dev_dbg(dev, "Unable to secure erase while DIMM active.\n");
+ return -EBUSY;
+ }
+ rc = nvdimm_security_erase(nvdimm, key, i == OP_ERASE
+ ? NVDIMM_USER : NVDIMM_MASTER);
} else if (i == OP_OVERWRITE) {
dev_dbg(dev, "overwrite %u\n", key);
+ if (atomic_read(&nvdimm->busy)) {
+ dev_dbg(dev, "Unable to overwrite while DIMM active.\n");
+ return -EBUSY;
+ }
rc = nvdimm_security_overwrite(nvdimm, key);
- } else if (i == OP_MASTER_UPDATE) {
- dev_dbg(dev, "master_update %u %u\n", key, newkey);
- rc = nvdimm_security_update(nvdimm, key, newkey,
- NVDIMM_MASTER);
- } else if (i == OP_MASTER_ERASE) {
- dev_dbg(dev, "master_erase %u\n", key);
- rc = nvdimm_security_erase(nvdimm, key,
- NVDIMM_MASTER);
} else
return -EINVAL;
diff --git a/drivers/nvdimm/security.c b/drivers/nvdimm/security.c
index 5862d0eee9db..2166e627383a 100644
--- a/drivers/nvdimm/security.c
+++ b/drivers/nvdimm/security.c
@@ -334,11 +334,6 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid,
|| !nvdimm->sec.flags)
return -EOPNOTSUPP;
- if (atomic_read(&nvdimm->busy)) {
- dev_dbg(dev, "Unable to secure erase while DIMM active.\n");
- return -EBUSY;
- }
-
rc = check_security_state(nvdimm);
if (rc)
return rc;
@@ -380,11 +375,6 @@ int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid)
|| !nvdimm->sec.flags)
return -EOPNOTSUPP;
- if (atomic_read(&nvdimm->busy)) {
- dev_dbg(dev, "Unable to overwrite while DIMM active.\n");
- return -EBUSY;
- }
-
if (dev->driver == NULL) {
dev_dbg(dev, "Unable to overwrite while DIMM active.\n");
return -EINVAL;
The security operations are exported from libnvdimm/security.c to
libnvdimm/dimm_devs.c, and libnvdimm/security.c is optionally compiled
based on the CONFIG_NVDIMM_KEYS config symbol.
Rather than export the operations across compile objects, just move the
__security_store() entry point to live with the helpers.
Acked-by: Jeff Moyer <[email protected]>
Reviewed-by: Dave Jiang <[email protected]>
Signed-off-by: Dan Williams <[email protected]>
---
drivers/nvdimm/dimm_devs.c | 84 -----------------------------------------
drivers/nvdimm/nd-core.h | 30 +--------------
drivers/nvdimm/security.c | 90 ++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 90 insertions(+), 114 deletions(-)
diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c
index d837cb9be83d..196aa44c4936 100644
--- a/drivers/nvdimm/dimm_devs.c
+++ b/drivers/nvdimm/dimm_devs.c
@@ -393,88 +393,6 @@ static ssize_t frozen_show(struct device *dev,
}
static DEVICE_ATTR_RO(frozen);
-#define OPS \
- C( OP_FREEZE, "freeze", 1), \
- C( OP_DISABLE, "disable", 2), \
- C( OP_UPDATE, "update", 3), \
- C( OP_ERASE, "erase", 2), \
- C( OP_OVERWRITE, "overwrite", 2), \
- C( OP_MASTER_UPDATE, "master_update", 3), \
- C( OP_MASTER_ERASE, "master_erase", 2)
-#undef C
-#define C(a, b, c) a
-enum nvdimmsec_op_ids { OPS };
-#undef C
-#define C(a, b, c) { b, c }
-static struct {
- const char *name;
- int args;
-} ops[] = { OPS };
-#undef C
-
-#define SEC_CMD_SIZE 32
-#define KEY_ID_SIZE 10
-
-static ssize_t __security_store(struct device *dev, const char *buf, size_t len)
-{
- struct nvdimm *nvdimm = to_nvdimm(dev);
- ssize_t rc;
- char cmd[SEC_CMD_SIZE+1], keystr[KEY_ID_SIZE+1],
- nkeystr[KEY_ID_SIZE+1];
- unsigned int key, newkey;
- int i;
-
- rc = sscanf(buf, "%"__stringify(SEC_CMD_SIZE)"s"
- " %"__stringify(KEY_ID_SIZE)"s"
- " %"__stringify(KEY_ID_SIZE)"s",
- cmd, keystr, nkeystr);
- if (rc < 1)
- return -EINVAL;
- for (i = 0; i < ARRAY_SIZE(ops); i++)
- if (sysfs_streq(cmd, ops[i].name))
- break;
- if (i >= ARRAY_SIZE(ops))
- return -EINVAL;
- if (ops[i].args > 1)
- rc = kstrtouint(keystr, 0, &key);
- if (rc >= 0 && ops[i].args > 2)
- rc = kstrtouint(nkeystr, 0, &newkey);
- if (rc < 0)
- return rc;
-
- if (i == OP_FREEZE) {
- dev_dbg(dev, "freeze\n");
- rc = nvdimm_security_freeze(nvdimm);
- } else if (i == OP_DISABLE) {
- dev_dbg(dev, "disable %u\n", key);
- rc = nvdimm_security_disable(nvdimm, key);
- } else if (i == OP_UPDATE || i == OP_MASTER_UPDATE) {
- dev_dbg(dev, "%s %u %u\n", ops[i].name, key, newkey);
- rc = nvdimm_security_update(nvdimm, key, newkey, i == OP_UPDATE
- ? NVDIMM_USER : NVDIMM_MASTER);
- } else if (i == OP_ERASE || i == OP_MASTER_ERASE) {
- dev_dbg(dev, "%s %u\n", ops[i].name, key);
- if (atomic_read(&nvdimm->busy)) {
- dev_dbg(dev, "Unable to secure erase while DIMM active.\n");
- return -EBUSY;
- }
- rc = nvdimm_security_erase(nvdimm, key, i == OP_ERASE
- ? NVDIMM_USER : NVDIMM_MASTER);
- } else if (i == OP_OVERWRITE) {
- dev_dbg(dev, "overwrite %u\n", key);
- if (atomic_read(&nvdimm->busy)) {
- dev_dbg(dev, "Unable to overwrite while DIMM active.\n");
- return -EBUSY;
- }
- rc = nvdimm_security_overwrite(nvdimm, key);
- } else
- return -EINVAL;
-
- if (rc == 0)
- rc = len;
- return rc;
-}
-
static ssize_t security_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t len)
@@ -489,7 +407,7 @@ static ssize_t security_store(struct device *dev,
nd_device_lock(dev);
nvdimm_bus_lock(dev);
wait_nvdimm_bus_probe_idle(dev);
- rc = __security_store(dev, buf, len);
+ rc = nvdimm_security_store(dev, buf, len);
nvdimm_bus_unlock(dev);
nd_device_unlock(dev);
diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h
index da2bbfd56d9f..454454ba1738 100644
--- a/drivers/nvdimm/nd-core.h
+++ b/drivers/nvdimm/nd-core.h
@@ -68,35 +68,11 @@ static inline unsigned long nvdimm_security_flags(
}
int nvdimm_security_freeze(struct nvdimm *nvdimm);
#if IS_ENABLED(CONFIG_NVDIMM_KEYS)
-int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid);
-int nvdimm_security_update(struct nvdimm *nvdimm, unsigned int keyid,
- unsigned int new_keyid,
- enum nvdimm_passphrase_type pass_type);
-int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid,
- enum nvdimm_passphrase_type pass_type);
-int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid);
+ssize_t nvdimm_security_store(struct device *dev, const char *buf, size_t len);
void nvdimm_security_overwrite_query(struct work_struct *work);
#else
-static inline int nvdimm_security_disable(struct nvdimm *nvdimm,
- unsigned int keyid)
-{
- return -EOPNOTSUPP;
-}
-static inline int nvdimm_security_update(struct nvdimm *nvdimm,
- unsigned int keyid,
- unsigned int new_keyid,
- enum nvdimm_passphrase_type pass_type)
-{
- return -EOPNOTSUPP;
-}
-static inline int nvdimm_security_erase(struct nvdimm *nvdimm,
- unsigned int keyid,
- enum nvdimm_passphrase_type pass_type)
-{
- return -EOPNOTSUPP;
-}
-static inline int nvdimm_security_overwrite(struct nvdimm *nvdimm,
- unsigned int keyid)
+static inline ssize_t nvdimm_security_store(struct device *dev,
+ const char *buf, size_t len)
{
return -EOPNOTSUPP;
}
diff --git a/drivers/nvdimm/security.c b/drivers/nvdimm/security.c
index 2166e627383a..9e45b207ff01 100644
--- a/drivers/nvdimm/security.c
+++ b/drivers/nvdimm/security.c
@@ -235,7 +235,7 @@ static int check_security_state(struct nvdimm *nvdimm)
return 0;
}
-int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid)
+static int security_disable(struct nvdimm *nvdimm, unsigned int keyid)
{
struct device *dev = &nvdimm->dev;
struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
@@ -268,7 +268,7 @@ int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid)
return rc;
}
-int nvdimm_security_update(struct nvdimm *nvdimm, unsigned int keyid,
+static int security_update(struct nvdimm *nvdimm, unsigned int keyid,
unsigned int new_keyid,
enum nvdimm_passphrase_type pass_type)
{
@@ -318,7 +318,7 @@ int nvdimm_security_update(struct nvdimm *nvdimm, unsigned int keyid,
return rc;
}
-int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid,
+static int security_erase(struct nvdimm *nvdimm, unsigned int keyid,
enum nvdimm_passphrase_type pass_type)
{
struct device *dev = &nvdimm->dev;
@@ -360,7 +360,7 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid,
return rc;
}
-int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid)
+static int security_overwrite(struct nvdimm *nvdimm, unsigned int keyid)
{
struct device *dev = &nvdimm->dev;
struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
@@ -465,3 +465,85 @@ void nvdimm_security_overwrite_query(struct work_struct *work)
__nvdimm_security_overwrite_query(nvdimm);
nvdimm_bus_unlock(&nvdimm->dev);
}
+
+#define OPS \
+ C( OP_FREEZE, "freeze", 1), \
+ C( OP_DISABLE, "disable", 2), \
+ C( OP_UPDATE, "update", 3), \
+ C( OP_ERASE, "erase", 2), \
+ C( OP_OVERWRITE, "overwrite", 2), \
+ C( OP_MASTER_UPDATE, "master_update", 3), \
+ C( OP_MASTER_ERASE, "master_erase", 2)
+#undef C
+#define C(a, b, c) a
+enum nvdimmsec_op_ids { OPS };
+#undef C
+#define C(a, b, c) { b, c }
+static struct {
+ const char *name;
+ int args;
+} ops[] = { OPS };
+#undef C
+
+#define SEC_CMD_SIZE 32
+#define KEY_ID_SIZE 10
+
+ssize_t nvdimm_security_store(struct device *dev, const char *buf, size_t len)
+{
+ struct nvdimm *nvdimm = to_nvdimm(dev);
+ ssize_t rc;
+ char cmd[SEC_CMD_SIZE+1], keystr[KEY_ID_SIZE+1],
+ nkeystr[KEY_ID_SIZE+1];
+ unsigned int key, newkey;
+ int i;
+
+ rc = sscanf(buf, "%"__stringify(SEC_CMD_SIZE)"s"
+ " %"__stringify(KEY_ID_SIZE)"s"
+ " %"__stringify(KEY_ID_SIZE)"s",
+ cmd, keystr, nkeystr);
+ if (rc < 1)
+ return -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(ops); i++)
+ if (sysfs_streq(cmd, ops[i].name))
+ break;
+ if (i >= ARRAY_SIZE(ops))
+ return -EINVAL;
+ if (ops[i].args > 1)
+ rc = kstrtouint(keystr, 0, &key);
+ if (rc >= 0 && ops[i].args > 2)
+ rc = kstrtouint(nkeystr, 0, &newkey);
+ if (rc < 0)
+ return rc;
+
+ if (i == OP_FREEZE) {
+ dev_dbg(dev, "freeze\n");
+ rc = nvdimm_security_freeze(nvdimm);
+ } else if (i == OP_DISABLE) {
+ dev_dbg(dev, "disable %u\n", key);
+ rc = security_disable(nvdimm, key);
+ } else if (i == OP_UPDATE || i == OP_MASTER_UPDATE) {
+ dev_dbg(dev, "%s %u %u\n", ops[i].name, key, newkey);
+ rc = security_update(nvdimm, key, newkey, i == OP_UPDATE
+ ? NVDIMM_USER : NVDIMM_MASTER);
+ } else if (i == OP_ERASE || i == OP_MASTER_ERASE) {
+ dev_dbg(dev, "%s %u\n", ops[i].name, key);
+ if (atomic_read(&nvdimm->busy)) {
+ dev_dbg(dev, "Unable to secure erase while DIMM active.\n");
+ return -EBUSY;
+ }
+ rc = security_erase(nvdimm, key, i == OP_ERASE
+ ? NVDIMM_USER : NVDIMM_MASTER);
+ } else if (i == OP_OVERWRITE) {
+ dev_dbg(dev, "overwrite %u\n", key);
+ if (atomic_read(&nvdimm->busy)) {
+ dev_dbg(dev, "Unable to overwrite while DIMM active.\n");
+ return -EBUSY;
+ }
+ rc = security_overwrite(nvdimm, key);
+ } else
+ return -EINVAL;
+
+ if (rc == 0)
+ rc = len;
+ return rc;
+}
Dan Williams <[email protected]> writes:
> In the process of debugging a system with an NVDIMM that was failing to
> unlock it was found that the kernel is reporting 'locked' while the DIMM
> security interface is 'frozen'. Unfortunately the security state is
> tracked internally as an enum which prevents it from communicating the
> difference between 'locked' and 'locked + frozen'. It follows that the
> enum also prevents the kernel from communicating 'unlocked + frozen'
> which would be useful for debugging why security operations like 'change
> passphrase' are disabled.
>
> Ditch the security state enum for a set of flags and introduce a new
> sysfs attribute explicitly for the 'frozen' state. The regression risk
> is low because the 'frozen' state was already blocked behind the
> 'locked' state, but will need to revisit if there were cases where
> applications need 'frozen' to show up in the primary 'security'
> attribute. The expectation is that communicating 'frozen' is mostly a
> helper for debug and status monitoring.
>
> Reviewed-by: Dave Jiang <[email protected]>
> Reported-by: Jeff Moyer <[email protected]>
> Signed-off-by: Dan Williams <[email protected]>
Reviewed-by: Jeff Moyer <[email protected]>
> ---
> drivers/acpi/nfit/intel.c | 59 ++++++++++++-----------
> drivers/nvdimm/bus.c | 2 -
> drivers/nvdimm/dimm_devs.c | 59 +++++++++++++----------
> drivers/nvdimm/nd-core.h | 21 ++++++--
> drivers/nvdimm/security.c | 99 ++++++++++++++++++--------------------
> include/linux/libnvdimm.h | 9 ++-
> tools/testing/nvdimm/dimm_devs.c | 19 ++-----
> 7 files changed, 141 insertions(+), 127 deletions(-)
>
> diff --git a/drivers/acpi/nfit/intel.c b/drivers/acpi/nfit/intel.c
> index cddd0fcf622c..1113b679cd7b 100644
> --- a/drivers/acpi/nfit/intel.c
> +++ b/drivers/acpi/nfit/intel.c
> @@ -7,10 +7,11 @@
> #include "intel.h"
> #include "nfit.h"
>
> -static enum nvdimm_security_state intel_security_state(struct nvdimm *nvdimm,
> +static unsigned long intel_security_flags(struct nvdimm *nvdimm,
> enum nvdimm_passphrase_type ptype)
> {
> struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
> + unsigned long security_flags = 0;
> struct {
> struct nd_cmd_pkg pkg;
> struct nd_intel_get_security_state cmd;
> @@ -27,7 +28,7 @@ static enum nvdimm_security_state intel_security_state(struct nvdimm *nvdimm,
> int rc;
>
> if (!test_bit(NVDIMM_INTEL_GET_SECURITY_STATE, &nfit_mem->dsm_mask))
> - return -ENXIO;
> + return 0;
>
> /*
> * Short circuit the state retrieval while we are doing overwrite.
> @@ -35,38 +36,42 @@ static enum nvdimm_security_state intel_security_state(struct nvdimm *nvdimm,
> * until the overwrite DSM completes.
> */
> if (nvdimm_in_overwrite(nvdimm) && ptype == NVDIMM_USER)
> - return NVDIMM_SECURITY_OVERWRITE;
> + return BIT(NVDIMM_SECURITY_OVERWRITE);
>
> rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
> - if (rc < 0)
> - return rc;
> - if (nd_cmd.cmd.status)
> - return -EIO;
> + if (rc < 0 || nd_cmd.cmd.status) {
> + pr_err("%s: security state retrieval failed (%d:%#x)\n",
> + nvdimm_name(nvdimm), rc, nd_cmd.cmd.status);
> + return 0;
> + }
>
> /* check and see if security is enabled and locked */
> if (ptype == NVDIMM_MASTER) {
> if (nd_cmd.cmd.extended_state & ND_INTEL_SEC_ESTATE_ENABLED)
> - return NVDIMM_SECURITY_UNLOCKED;
> - else if (nd_cmd.cmd.extended_state &
> - ND_INTEL_SEC_ESTATE_PLIMIT)
> - return NVDIMM_SECURITY_FROZEN;
> - } else {
> - if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_UNSUPPORTED)
> - return -ENXIO;
> - else if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_ENABLED) {
> - if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_LOCKED)
> - return NVDIMM_SECURITY_LOCKED;
> - else if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_FROZEN
> - || nd_cmd.cmd.state &
> - ND_INTEL_SEC_STATE_PLIMIT)
> - return NVDIMM_SECURITY_FROZEN;
> - else
> - return NVDIMM_SECURITY_UNLOCKED;
> - }
> + set_bit(NVDIMM_SECURITY_UNLOCKED, &security_flags);
> + else
> + set_bit(NVDIMM_SECURITY_DISABLED, &security_flags);
> + if (nd_cmd.cmd.extended_state & ND_INTEL_SEC_ESTATE_PLIMIT)
> + set_bit(NVDIMM_SECURITY_FROZEN, &security_flags);
> + return security_flags;
> }
>
> - /* this should cover master security disabled as well */
> - return NVDIMM_SECURITY_DISABLED;
> + if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_UNSUPPORTED)
> + return 0;
> +
> + if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_ENABLED) {
> + if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_FROZEN ||
> + nd_cmd.cmd.state & ND_INTEL_SEC_STATE_PLIMIT)
> + set_bit(NVDIMM_SECURITY_FROZEN, &security_flags);
> +
> + if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_LOCKED)
> + set_bit(NVDIMM_SECURITY_LOCKED, &security_flags);
> + else
> + set_bit(NVDIMM_SECURITY_UNLOCKED, &security_flags);
> + } else
> + set_bit(NVDIMM_SECURITY_DISABLED, &security_flags);
> +
> + return security_flags;
> }
>
> static int intel_security_freeze(struct nvdimm *nvdimm)
> @@ -371,7 +376,7 @@ static void nvdimm_invalidate_cache(void)
> #endif
>
> static const struct nvdimm_security_ops __intel_security_ops = {
> - .state = intel_security_state,
> + .get_flags = intel_security_flags,
> .freeze = intel_security_freeze,
> .change_key = intel_security_change_key,
> .disable = intel_security_disable,
> diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c
> index 798c5c4aea9c..29479d3b01b0 100644
> --- a/drivers/nvdimm/bus.c
> +++ b/drivers/nvdimm/bus.c
> @@ -400,7 +400,7 @@ static int child_unregister(struct device *dev, void *data)
>
> /* We are shutting down. Make state frozen artificially. */
> nvdimm_bus_lock(dev);
> - nvdimm->sec.state = NVDIMM_SECURITY_FROZEN;
> + set_bit(NVDIMM_SECURITY_FROZEN, &nvdimm->sec.flags);
> if (test_and_clear_bit(NDD_WORK_PENDING, &nvdimm->flags))
> dev_put = true;
> nvdimm_bus_unlock(dev);
> diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c
> index 29a065e769ea..53330625fe07 100644
> --- a/drivers/nvdimm/dimm_devs.c
> +++ b/drivers/nvdimm/dimm_devs.c
> @@ -372,24 +372,27 @@ __weak ssize_t security_show(struct device *dev,
> {
> struct nvdimm *nvdimm = to_nvdimm(dev);
>
> - switch (nvdimm->sec.state) {
> - case NVDIMM_SECURITY_DISABLED:
> + if (test_bit(NVDIMM_SECURITY_DISABLED, &nvdimm->sec.flags))
> return sprintf(buf, "disabled\n");
> - case NVDIMM_SECURITY_UNLOCKED:
> + if (test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.flags))
> return sprintf(buf, "unlocked\n");
> - case NVDIMM_SECURITY_LOCKED:
> + if (test_bit(NVDIMM_SECURITY_LOCKED, &nvdimm->sec.flags))
> return sprintf(buf, "locked\n");
> - case NVDIMM_SECURITY_FROZEN:
> - return sprintf(buf, "frozen\n");
> - case NVDIMM_SECURITY_OVERWRITE:
> + if (test_bit(NVDIMM_SECURITY_OVERWRITE, &nvdimm->sec.flags))
> return sprintf(buf, "overwrite\n");
> - default:
> - return -ENOTTY;
> - }
> -
> return -ENOTTY;
> }
>
> +static ssize_t frozen_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct nvdimm *nvdimm = to_nvdimm(dev);
> +
> + return sprintf(buf, "%d\n", test_bit(NVDIMM_SECURITY_FROZEN,
> + &nvdimm->sec.flags));
> +}
> +static DEVICE_ATTR_RO(frozen);
> +
> #define OPS \
> C( OP_FREEZE, "freeze", 1), \
> C( OP_DISABLE, "disable", 2), \
> @@ -501,6 +504,7 @@ static struct attribute *nvdimm_attributes[] = {
> &dev_attr_commands.attr,
> &dev_attr_available_slots.attr,
> &dev_attr_security.attr,
> + &dev_attr_frozen.attr,
> NULL,
> };
>
> @@ -509,17 +513,24 @@ static umode_t nvdimm_visible(struct kobject *kobj, struct attribute *a, int n)
> struct device *dev = container_of(kobj, typeof(*dev), kobj);
> struct nvdimm *nvdimm = to_nvdimm(dev);
>
> - if (a != &dev_attr_security.attr)
> + if (a != &dev_attr_security.attr && a != &dev_attr_frozen.attr)
> return a->mode;
> - if (nvdimm->sec.state < 0)
> + if (!nvdimm->sec.flags)
> return 0;
> - /* Are there any state mutation ops? */
> - if (nvdimm->sec.ops->freeze || nvdimm->sec.ops->disable
> - || nvdimm->sec.ops->change_key
> - || nvdimm->sec.ops->erase
> - || nvdimm->sec.ops->overwrite)
> +
> + if (a == &dev_attr_security.attr) {
> + /* Are there any state mutation ops (make writable)? */
> + if (nvdimm->sec.ops->freeze || nvdimm->sec.ops->disable
> + || nvdimm->sec.ops->change_key
> + || nvdimm->sec.ops->erase
> + || nvdimm->sec.ops->overwrite)
> + return a->mode;
> + return 0444;
> + }
> +
> + if (nvdimm->sec.ops->freeze)
> return a->mode;
> - return 0444;
> + return 0;
> }
>
> struct attribute_group nvdimm_attribute_group = {
> @@ -569,8 +580,8 @@ struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus,
> * attribute visibility.
> */
> /* get security state and extended (master) state */
> - nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
> - nvdimm->sec.ext_state = nvdimm_security_state(nvdimm, NVDIMM_MASTER);
> + nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
> + nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm, NVDIMM_MASTER);
> nd_device_register(dev);
>
> return nvdimm;
> @@ -588,7 +599,7 @@ int nvdimm_security_setup_events(struct device *dev)
> {
> struct nvdimm *nvdimm = to_nvdimm(dev);
>
> - if (nvdimm->sec.state < 0 || !nvdimm->sec.ops
> + if (!nvdimm->sec.flags || !nvdimm->sec.ops
> || !nvdimm->sec.ops->overwrite)
> return 0;
> nvdimm->sec.overwrite_state = sysfs_get_dirent(dev->kobj.sd, "security");
> @@ -614,7 +625,7 @@ int nvdimm_security_freeze(struct nvdimm *nvdimm)
> if (!nvdimm->sec.ops || !nvdimm->sec.ops->freeze)
> return -EOPNOTSUPP;
>
> - if (nvdimm->sec.state < 0)
> + if (!nvdimm->sec.flags)
> return -EIO;
>
> if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
> @@ -623,7 +634,7 @@ int nvdimm_security_freeze(struct nvdimm *nvdimm)
> }
>
> rc = nvdimm->sec.ops->freeze(nvdimm);
> - nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
> + nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
>
> return rc;
> }
> diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h
> index 0ac52b6eb00e..da2bbfd56d9f 100644
> --- a/drivers/nvdimm/nd-core.h
> +++ b/drivers/nvdimm/nd-core.h
> @@ -39,21 +39,32 @@ struct nvdimm {
> const char *dimm_id;
> struct {
> const struct nvdimm_security_ops *ops;
> - enum nvdimm_security_state state;
> - enum nvdimm_security_state ext_state;
> + unsigned long flags;
> + unsigned long ext_flags;
> unsigned int overwrite_tmo;
> struct kernfs_node *overwrite_state;
> } sec;
> struct delayed_work dwork;
> };
>
> -static inline enum nvdimm_security_state nvdimm_security_state(
> +static inline unsigned long nvdimm_security_flags(
> struct nvdimm *nvdimm, enum nvdimm_passphrase_type ptype)
> {
> + u64 flags;
> + const u64 state_flags = 1UL << NVDIMM_SECURITY_DISABLED
> + | 1UL << NVDIMM_SECURITY_LOCKED
> + | 1UL << NVDIMM_SECURITY_UNLOCKED
> + | 1UL << NVDIMM_SECURITY_OVERWRITE;
> +
> if (!nvdimm->sec.ops)
> - return -ENXIO;
> + return 0;
>
> - return nvdimm->sec.ops->state(nvdimm, ptype);
> + flags = nvdimm->sec.ops->get_flags(nvdimm, ptype);
> + /* disabled, locked, unlocked, and overwrite are mutually exclusive */
> + dev_WARN_ONCE(&nvdimm->dev, hweight64(flags & state_flags) > 1,
> + "reported invalid security state: %#llx\n",
> + (unsigned long long) flags);
> + return flags;
> }
> int nvdimm_security_freeze(struct nvdimm *nvdimm);
> #if IS_ENABLED(CONFIG_NVDIMM_KEYS)
> diff --git a/drivers/nvdimm/security.c b/drivers/nvdimm/security.c
> index a570f2263a42..5862d0eee9db 100644
> --- a/drivers/nvdimm/security.c
> +++ b/drivers/nvdimm/security.c
> @@ -158,7 +158,7 @@ static int nvdimm_key_revalidate(struct nvdimm *nvdimm)
> }
>
> nvdimm_put_key(key);
> - nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
> + nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
> return 0;
> }
>
> @@ -174,7 +174,7 @@ static int __nvdimm_security_unlock(struct nvdimm *nvdimm)
> lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
>
> if (!nvdimm->sec.ops || !nvdimm->sec.ops->unlock
> - || nvdimm->sec.state < 0)
> + || !nvdimm->sec.flags)
> return -EIO;
>
> if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
> @@ -189,7 +189,7 @@ static int __nvdimm_security_unlock(struct nvdimm *nvdimm)
> * freeze of the security configuration. I.e. if the OS does not
> * have the key, security is being managed pre-OS.
> */
> - if (nvdimm->sec.state == NVDIMM_SECURITY_UNLOCKED) {
> + if (test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.flags)) {
> if (!key_revalidate)
> return 0;
>
> @@ -202,7 +202,7 @@ static int __nvdimm_security_unlock(struct nvdimm *nvdimm)
> rc == 0 ? "success" : "fail");
>
> nvdimm_put_key(key);
> - nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
> + nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
> return rc;
> }
>
> @@ -217,6 +217,24 @@ int nvdimm_security_unlock(struct device *dev)
> return rc;
> }
>
> +static int check_security_state(struct nvdimm *nvdimm)
> +{
> + struct device *dev = &nvdimm->dev;
> +
> + if (test_bit(NVDIMM_SECURITY_FROZEN, &nvdimm->sec.flags)) {
> + dev_dbg(dev, "Incorrect security state: %#lx\n",
> + nvdimm->sec.flags);
> + return -EIO;
> + }
> +
> + if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
> + dev_dbg(dev, "Security operation in progress.\n");
> + return -EBUSY;
> + }
> +
> + return 0;
> +}
> +
> int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid)
> {
> struct device *dev = &nvdimm->dev;
> @@ -229,19 +247,12 @@ int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid)
> lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
>
> if (!nvdimm->sec.ops || !nvdimm->sec.ops->disable
> - || nvdimm->sec.state < 0)
> + || !nvdimm->sec.flags)
> return -EOPNOTSUPP;
>
> - if (nvdimm->sec.state >= NVDIMM_SECURITY_FROZEN) {
> - dev_dbg(dev, "Incorrect security state: %d\n",
> - nvdimm->sec.state);
> - return -EIO;
> - }
> -
> - if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
> - dev_dbg(dev, "Security operation in progress.\n");
> - return -EBUSY;
> - }
> + rc = check_security_state(nvdimm);
> + if (rc)
> + return rc;
>
> data = nvdimm_get_user_key_payload(nvdimm, keyid,
> NVDIMM_BASE_KEY, &key);
> @@ -253,7 +264,7 @@ int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid)
> rc == 0 ? "success" : "fail");
>
> nvdimm_put_key(key);
> - nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
> + nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
> return rc;
> }
>
> @@ -271,14 +282,12 @@ int nvdimm_security_update(struct nvdimm *nvdimm, unsigned int keyid,
> lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
>
> if (!nvdimm->sec.ops || !nvdimm->sec.ops->change_key
> - || nvdimm->sec.state < 0)
> + || !nvdimm->sec.flags)
> return -EOPNOTSUPP;
>
> - if (nvdimm->sec.state >= NVDIMM_SECURITY_FROZEN) {
> - dev_dbg(dev, "Incorrect security state: %d\n",
> - nvdimm->sec.state);
> - return -EIO;
> - }
> + rc = check_security_state(nvdimm);
> + if (rc)
> + return rc;
>
> data = nvdimm_get_user_key_payload(nvdimm, keyid,
> NVDIMM_BASE_KEY, &key);
> @@ -301,10 +310,10 @@ int nvdimm_security_update(struct nvdimm *nvdimm, unsigned int keyid,
> nvdimm_put_key(newkey);
> nvdimm_put_key(key);
> if (pass_type == NVDIMM_MASTER)
> - nvdimm->sec.ext_state = nvdimm_security_state(nvdimm,
> + nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm,
> NVDIMM_MASTER);
> else
> - nvdimm->sec.state = nvdimm_security_state(nvdimm,
> + nvdimm->sec.flags = nvdimm_security_flags(nvdimm,
> NVDIMM_USER);
> return rc;
> }
> @@ -322,7 +331,7 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid,
> lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
>
> if (!nvdimm->sec.ops || !nvdimm->sec.ops->erase
> - || nvdimm->sec.state < 0)
> + || !nvdimm->sec.flags)
> return -EOPNOTSUPP;
>
> if (atomic_read(&nvdimm->busy)) {
> @@ -330,18 +339,11 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid,
> return -EBUSY;
> }
>
> - if (nvdimm->sec.state >= NVDIMM_SECURITY_FROZEN) {
> - dev_dbg(dev, "Incorrect security state: %d\n",
> - nvdimm->sec.state);
> - return -EIO;
> - }
> -
> - if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
> - dev_dbg(dev, "Security operation in progress.\n");
> - return -EBUSY;
> - }
> + rc = check_security_state(nvdimm);
> + if (rc)
> + return rc;
>
> - if (nvdimm->sec.ext_state != NVDIMM_SECURITY_UNLOCKED
> + if (!test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.ext_flags)
> && pass_type == NVDIMM_MASTER) {
> dev_dbg(dev,
> "Attempt to secure erase in wrong master state.\n");
> @@ -359,7 +361,7 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid,
> rc == 0 ? "success" : "fail");
>
> nvdimm_put_key(key);
> - nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
> + nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
> return rc;
> }
>
> @@ -375,7 +377,7 @@ int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid)
> lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
>
> if (!nvdimm->sec.ops || !nvdimm->sec.ops->overwrite
> - || nvdimm->sec.state < 0)
> + || !nvdimm->sec.flags)
> return -EOPNOTSUPP;
>
> if (atomic_read(&nvdimm->busy)) {
> @@ -388,16 +390,9 @@ int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid)
> return -EINVAL;
> }
>
> - if (nvdimm->sec.state >= NVDIMM_SECURITY_FROZEN) {
> - dev_dbg(dev, "Incorrect security state: %d\n",
> - nvdimm->sec.state);
> - return -EIO;
> - }
> -
> - if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
> - dev_dbg(dev, "Security operation in progress.\n");
> - return -EBUSY;
> - }
> + rc = check_security_state(nvdimm);
> + if (rc)
> + return rc;
>
> data = nvdimm_get_user_key_payload(nvdimm, keyid,
> NVDIMM_BASE_KEY, &key);
> @@ -412,7 +407,7 @@ int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid)
> if (rc == 0) {
> set_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags);
> set_bit(NDD_WORK_PENDING, &nvdimm->flags);
> - nvdimm->sec.state = NVDIMM_SECURITY_OVERWRITE;
> + set_bit(NVDIMM_SECURITY_OVERWRITE, &nvdimm->sec.flags);
> /*
> * Make sure we don't lose device while doing overwrite
> * query.
> @@ -443,7 +438,7 @@ void __nvdimm_security_overwrite_query(struct nvdimm *nvdimm)
> tmo = nvdimm->sec.overwrite_tmo;
>
> if (!nvdimm->sec.ops || !nvdimm->sec.ops->query_overwrite
> - || nvdimm->sec.state < 0)
> + || !nvdimm->sec.flags)
> return;
>
> rc = nvdimm->sec.ops->query_overwrite(nvdimm);
> @@ -467,8 +462,8 @@ void __nvdimm_security_overwrite_query(struct nvdimm *nvdimm)
> clear_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags);
> clear_bit(NDD_WORK_PENDING, &nvdimm->flags);
> put_device(&nvdimm->dev);
> - nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
> - nvdimm->sec.ext_state = nvdimm_security_state(nvdimm, NVDIMM_MASTER);
> + nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
> + nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_MASTER);
> }
>
> void nvdimm_security_overwrite_query(struct work_struct *work)
> diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
> index 7a64b3ddb408..b6eddf912568 100644
> --- a/include/linux/libnvdimm.h
> +++ b/include/linux/libnvdimm.h
> @@ -160,8 +160,11 @@ static inline struct nd_blk_region_desc *to_blk_region_desc(
>
> }
>
> -enum nvdimm_security_state {
> - NVDIMM_SECURITY_ERROR = -1,
> +/*
> + * Note that separate bits for locked + unlocked are defined so that
> + * 'flags == 0' corresponds to an error / not-supported state.
> + */
> +enum nvdimm_security_bits {
> NVDIMM_SECURITY_DISABLED,
> NVDIMM_SECURITY_UNLOCKED,
> NVDIMM_SECURITY_LOCKED,
> @@ -182,7 +185,7 @@ enum nvdimm_passphrase_type {
> };
>
> struct nvdimm_security_ops {
> - enum nvdimm_security_state (*state)(struct nvdimm *nvdimm,
> + unsigned long (*get_flags)(struct nvdimm *nvdimm,
> enum nvdimm_passphrase_type pass_type);
> int (*freeze)(struct nvdimm *nvdimm);
> int (*change_key)(struct nvdimm *nvdimm,
> diff --git a/tools/testing/nvdimm/dimm_devs.c b/tools/testing/nvdimm/dimm_devs.c
> index 2d4baf57822f..57bd27dedf1f 100644
> --- a/tools/testing/nvdimm/dimm_devs.c
> +++ b/tools/testing/nvdimm/dimm_devs.c
> @@ -18,24 +18,13 @@ ssize_t security_show(struct device *dev,
> * For the test version we need to poll the "hardware" in order
> * to get the updated status for unlock testing.
> */
> - nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
> - nvdimm->sec.ext_state = nvdimm_security_state(nvdimm, NVDIMM_MASTER);
> + nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
>
> - switch (nvdimm->sec.state) {
> - case NVDIMM_SECURITY_DISABLED:
> + if (test_bit(NVDIMM_SECURITY_DISABLED, &nvdimm->sec.flags))
> return sprintf(buf, "disabled\n");
> - case NVDIMM_SECURITY_UNLOCKED:
> + if (test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.flags))
> return sprintf(buf, "unlocked\n");
> - case NVDIMM_SECURITY_LOCKED:
> + if (test_bit(NVDIMM_SECURITY_LOCKED, &nvdimm->sec.flags))
> return sprintf(buf, "locked\n");
> - case NVDIMM_SECURITY_FROZEN:
> - return sprintf(buf, "frozen\n");
> - case NVDIMM_SECURITY_OVERWRITE:
> - return sprintf(buf, "overwrite\n");
> - default:
> - return -ENOTTY;
> - }
> -
> return -ENOTTY;
> }
> -
Dan Williams <[email protected]> writes:
> An attempt to freeze DIMMs currently runs afoul of default blocking of
> all security operations in the entry to the 'store' routine for the
> 'security' sysfs attribute.
>
> The blanket blocking of all security operations while the DIMM is in
> active use in a region is too restrictive. The only security operations
> that need to be aware of the ->busy state are those that mutate the
> state of data, i.e. erase and overwrite.
>
> Refactor the ->busy checks to be applied at the entry common entry point
> in __security_store() rather than each of the helper routines to enable
> freeze to be run regardless of busy state.
>
> Reviewed-by: Dave Jiang <[email protected]>
> Signed-off-by: Dan Williams <[email protected]>
Reviewed-by: Jeff Moyer <[email protected]>
> ---
> drivers/nvdimm/dimm_devs.c | 33 ++++++++++++++++-----------------
> drivers/nvdimm/security.c | 10 ----------
> 2 files changed, 16 insertions(+), 27 deletions(-)
>
> diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c
> index 53330625fe07..d837cb9be83d 100644
> --- a/drivers/nvdimm/dimm_devs.c
> +++ b/drivers/nvdimm/dimm_devs.c
> @@ -424,9 +424,6 @@ static ssize_t __security_store(struct device *dev, const char *buf, size_t len)
> unsigned int key, newkey;
> int i;
>
> - if (atomic_read(&nvdimm->busy))
> - return -EBUSY;
> -
> rc = sscanf(buf, "%"__stringify(SEC_CMD_SIZE)"s"
> " %"__stringify(KEY_ID_SIZE)"s"
> " %"__stringify(KEY_ID_SIZE)"s",
> @@ -451,23 +448,25 @@ static ssize_t __security_store(struct device *dev, const char *buf, size_t len)
> } else if (i == OP_DISABLE) {
> dev_dbg(dev, "disable %u\n", key);
> rc = nvdimm_security_disable(nvdimm, key);
> - } else if (i == OP_UPDATE) {
> - dev_dbg(dev, "update %u %u\n", key, newkey);
> - rc = nvdimm_security_update(nvdimm, key, newkey, NVDIMM_USER);
> - } else if (i == OP_ERASE) {
> - dev_dbg(dev, "erase %u\n", key);
> - rc = nvdimm_security_erase(nvdimm, key, NVDIMM_USER);
> + } else if (i == OP_UPDATE || i == OP_MASTER_UPDATE) {
> + dev_dbg(dev, "%s %u %u\n", ops[i].name, key, newkey);
> + rc = nvdimm_security_update(nvdimm, key, newkey, i == OP_UPDATE
> + ? NVDIMM_USER : NVDIMM_MASTER);
> + } else if (i == OP_ERASE || i == OP_MASTER_ERASE) {
> + dev_dbg(dev, "%s %u\n", ops[i].name, key);
> + if (atomic_read(&nvdimm->busy)) {
> + dev_dbg(dev, "Unable to secure erase while DIMM active.\n");
> + return -EBUSY;
> + }
> + rc = nvdimm_security_erase(nvdimm, key, i == OP_ERASE
> + ? NVDIMM_USER : NVDIMM_MASTER);
> } else if (i == OP_OVERWRITE) {
> dev_dbg(dev, "overwrite %u\n", key);
> + if (atomic_read(&nvdimm->busy)) {
> + dev_dbg(dev, "Unable to overwrite while DIMM active.\n");
> + return -EBUSY;
> + }
> rc = nvdimm_security_overwrite(nvdimm, key);
> - } else if (i == OP_MASTER_UPDATE) {
> - dev_dbg(dev, "master_update %u %u\n", key, newkey);
> - rc = nvdimm_security_update(nvdimm, key, newkey,
> - NVDIMM_MASTER);
> - } else if (i == OP_MASTER_ERASE) {
> - dev_dbg(dev, "master_erase %u\n", key);
> - rc = nvdimm_security_erase(nvdimm, key,
> - NVDIMM_MASTER);
> } else
> return -EINVAL;
>
> diff --git a/drivers/nvdimm/security.c b/drivers/nvdimm/security.c
> index 5862d0eee9db..2166e627383a 100644
> --- a/drivers/nvdimm/security.c
> +++ b/drivers/nvdimm/security.c
> @@ -334,11 +334,6 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid,
> || !nvdimm->sec.flags)
> return -EOPNOTSUPP;
>
> - if (atomic_read(&nvdimm->busy)) {
> - dev_dbg(dev, "Unable to secure erase while DIMM active.\n");
> - return -EBUSY;
> - }
> -
> rc = check_security_state(nvdimm);
> if (rc)
> return rc;
> @@ -380,11 +375,6 @@ int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid)
> || !nvdimm->sec.flags)
> return -EOPNOTSUPP;
>
> - if (atomic_read(&nvdimm->busy)) {
> - dev_dbg(dev, "Unable to overwrite while DIMM active.\n");
> - return -EBUSY;
> - }
> -
> if (dev->driver == NULL) {
> dev_dbg(dev, "Unable to overwrite while DIMM active.\n");
> return -EINVAL;
>
> _______________________________________________
> Linux-nvdimm mailing list
> [email protected]
> https://lists.01.org/mailman/listinfo/linux-nvdimm
Dan Williams <[email protected]> writes:
> Changes since v1 [1]:
> - Cleanup patch1, simplify flags return in the overwrite case and
> consolidate frozen-state cases (Jeff)
> - Clarify the motivation for patch2 (Jeff)
> - Collect Dave's Reviewed-by
The series tests out fine for me.
Thanks, Dan!
-Jeff
>
> [1]: https://lists.01.org/pipermail/linux-nvdimm/2019-August/023133.html
>
> ---
>
> Jeff reported a scenario where ndctl was failing to unlock DIMMs [2].
> Through the course of debug it was discovered that the security
> interface on the DIMMs was in the 'frozen' state disallowing unlock, or
> any security operation. Unfortunately the kernel only showed that the
> DIMMs were 'locked', not 'locked' and 'frozen'.
>
> Introduce a new sysfs 'frozen' attribute so that ndctl can reflect the
> "security-operations-allowed" state independently of the lock status.
> Then, followup with cleanups related to replacing a security-state-enum
> with a set of flags.
>
> [2]: https://lists.01.org/pipermail/linux-nvdimm/2019-August/022856.html
>
> ---
>
> Dan Williams (3):
> libnvdimm/security: Introduce a 'frozen' attribute
> libnvdimm/security: Tighten scope of nvdimm->busy vs security operations
> libnvdimm/security: Consolidate 'security' operations
>
>
> drivers/acpi/nfit/intel.c | 59 ++++++-----
> drivers/nvdimm/bus.c | 2
> drivers/nvdimm/dimm_devs.c | 134 ++++++--------------------
> drivers/nvdimm/nd-core.h | 51 ++++------
> drivers/nvdimm/security.c | 199 +++++++++++++++++++++++++-------------
> include/linux/libnvdimm.h | 9 +-
> tools/testing/nvdimm/dimm_devs.c | 19 +---
> 7 files changed, 226 insertions(+), 247 deletions(-)