@@ -26,6 +26,10 @@ static int nvdimm_probe(struct device *dev)
struct nvdimm_drvdata *ndd;
int rc;
+ rc = nvdimm_check_security_busy(dev);
+ if (rc)
+ return rc;
+
rc = nvdimm_check_config_data(dev);
if (rc) {
/* not required for non-aliased nvdimm, ex. NVDIMM-N */
@@ -187,12 +187,16 @@ static int nvdimm_security_erase(struct device *dev, unsigned int keyid)
struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
struct key *key;
struct user_key_payload *payload;
- int rc = 0;
+ int rc;
bool is_userkey = false;
if (!nvdimm->security_ops)
return -EOPNOTSUPP;
+ rc = nvdimm_check_security_busy(dev);
+ if (rc)
+ return rc;
+
nvdimm_bus_lock(&nvdimm_bus->dev);
if (atomic_read(&nvdimm->busy)) {
dev_warn(dev, "Unable to secure erase while DIMM active.\n");
@@ -212,6 +216,8 @@ static int nvdimm_security_erase(struct device *dev, unsigned int keyid)
goto out;
}
+ nvdimm_set_security_busy(dev);
+
/* look for a key from cached key if exists */
key = nvdimm_get_and_verify_key(dev, keyid);
if (IS_ERR(key)) {
@@ -246,6 +252,7 @@ static int nvdimm_security_erase(struct device *dev, unsigned int keyid)
key_put(key);
out:
+ nvdimm_clear_security_busy(dev);
nvdimm_bus_unlock(&nvdimm_bus->dev);
nvdimm_security_get_state(dev);
return rc;
@@ -262,6 +269,10 @@ static int nvdimm_security_freeze_lock(struct device *dev)
if (nvdimm->state == NVDIMM_SECURITY_UNSUPPORTED)
return -EOPNOTSUPP;
+ rc = nvdimm_check_security_busy(dev);
+ if (rc)
+ return rc;
+
rc = nvdimm->security_ops->freeze_lock(nvdimm);
if (rc < 0)
return rc;
@@ -284,6 +295,10 @@ static int nvdimm_security_disable(struct device *dev, unsigned int keyid)
if (nvdimm->state == NVDIMM_SECURITY_UNSUPPORTED)
return -EOPNOTSUPP;
+ rc = nvdimm_check_security_busy(dev);
+ if (rc)
+ return rc;
+
/* look for a key from cached key */
key = nvdimm_get_and_verify_key(dev, keyid);
if (IS_ERR(key))
@@ -335,6 +350,10 @@ int nvdimm_security_unlock_dimm(struct device *dev)
nvdimm->state == NVDIMM_SECURITY_DISABLED)
return 0;
+ rc = nvdimm_check_security_busy(dev);
+ if (rc)
+ return rc;
+
key = nvdimm_get_key(dev);
if (!key)
key = nvdimm_request_key(dev);
@@ -390,6 +409,10 @@ static int nvdimm_security_change_key(struct device *dev,
if (nvdimm->state == NVDIMM_SECURITY_FROZEN)
return -EBUSY;
+ rc = nvdimm_check_security_busy(dev);
+ if (rc)
+ return rc;
+
/* look for a key from cached key if exists */
old_key = nvdimm_get_and_verify_key(dev, old_keyid);
if (IS_ERR(old_key))
@@ -467,6 +490,33 @@ static int nvdimm_security_change_key(struct device *dev,
return rc;
}
+/*
+ * Check if we are doing security wipes
+ */
+int nvdimm_check_security_busy(struct device *dev)
+{
+ struct nvdimm *nvdimm = to_nvdimm(dev);
+
+ if (test_bit(NDD_SECURITY_BUSY, &nvdimm->flags))
+ return -EBUSY;
+
+ return 0;
+}
+
+void nvdimm_set_security_busy(struct device *dev)
+{
+ struct nvdimm *nvdimm = to_nvdimm(dev);
+
+ set_bit(NDD_SECURITY_BUSY, &nvdimm->flags);
+}
+
+void nvdimm_clear_security_busy(struct device *dev)
+{
+ struct nvdimm *nvdimm = to_nvdimm(dev);
+
+ clear_bit(NDD_SECURITY_BUSY, &nvdimm->flags);
+}
+
/*
* Retrieve bus and dimm handle and return if this bus supports
* get_config_data commands
@@ -239,6 +239,9 @@ void nd_region_exit(void);
struct nvdimm;
struct nvdimm_drvdata *to_ndd(struct nd_mapping *nd_mapping);
int nvdimm_check_config_data(struct device *dev);
+int nvdimm_check_security_busy(struct device *dev);
+void nvdimm_set_security_busy(struct device *dev);
+void nvdimm_clear_security_busy(struct device *dev);
int nvdimm_init_nsarea(struct nvdimm_drvdata *ndd);
int nvdimm_init_config_data(struct nvdimm_drvdata *ndd);
int nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset,
@@ -78,6 +78,13 @@ int nd_region_activate(struct nd_region *nd_region)
for (i = 0; i < nd_region->ndr_mappings; i++) {
struct nd_mapping *nd_mapping = &nd_region->mapping[i];
struct nvdimm *nvdimm = nd_mapping->nvdimm;
+ int rc;
+
+ rc = nvdimm_check_security_busy(&nvdimm->dev);
+ if (rc) {
+ nvdimm_bus_unlock(&nd_region->dev);
+ return rc;
+ }
/* at least one null hint slot per-dimm for the "no-hint" case */
flush_data_size += sizeof(void *);
@@ -38,6 +38,8 @@ enum {
NDD_UNARMED = 1,
/* locked memory devices should not be accessed */
NDD_LOCKED = 2,
+ /* memory under security wipes should not be accessed */
+ NDD_SECURITY_BUSY = 3,
/* need to set a limit somewhere, but yes, this is likely overkill */
ND_IOCTL_MAX_BUFLEN = SZ_4M,
Adding a flag for nvdimm->flags to support erase functions. While it's ok to hold the nvdimm_bus lock for secure erase due to minimal time to execute the command, overwrite requires a significantly longer time and makes this impossible. The flag will block any drivers from being loaded and DIMMs being probed. Signed-off-by: Dave Jiang <dave.jiang@intel.com> --- drivers/nvdimm/dimm.c | 4 +++ drivers/nvdimm/dimm_devs.c | 52 +++++++++++++++++++++++++++++++++++++++++- drivers/nvdimm/nd.h | 3 ++ drivers/nvdimm/region_devs.c | 7 ++++++ include/linux/libnvdimm.h | 2 ++ 5 files changed, 67 insertions(+), 1 deletion(-)