@@ -250,6 +250,7 @@ long nvdimm_clear_poison(struct device *dev, phys_addr_t phys,
void nvdimm_set_aliasing(struct device *dev);
void nvdimm_set_locked(struct device *dev);
void nvdimm_clear_locked(struct device *dev);
+int nvdimm_check_security_busy(struct nvdimm *nvdimm);
struct nd_btt *to_nd_btt(struct device *dev);
struct nd_gen_sb {
@@ -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);
+ 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 *);
@@ -19,6 +19,27 @@ static bool no_key_self_verify;
module_param(no_key_self_verify, bool, 0644);
MODULE_PARM_DESC(no_key_self_verify, "Bypass security key self verify");
+/*
+ * Check if we are doing security wipes
+ */
+int nvdimm_check_security_busy(struct nvdimm *nvdimm)
+{
+ if (test_bit(NDD_SECURITY_BUSY, &nvdimm->flags))
+ return -EBUSY;
+
+ return 0;
+}
+
+static inline void nvdimm_set_security_busy(struct nvdimm *nvdimm)
+{
+ set_bit(NDD_SECURITY_BUSY, &nvdimm->flags);
+}
+
+static inline void nvdimm_clear_security_busy(struct nvdimm *nvdimm)
+{
+ clear_bit(NDD_SECURITY_BUSY, &nvdimm->flags);
+}
+
/*
* Retrieve user injected key
*/
@@ -85,6 +106,13 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid)
nvdimm_bus_lock(dev);
mutex_lock(&nvdimm->sec_mutex);
+ rc = nvdimm_check_security_busy(nvdimm);
+ if (rc < 0) {
+ dev_warn(dev, "Security operation in progress.\n");
+ goto out;
+ }
+
+ nvdimm_set_security_busy(nvdimm);
if (atomic_read(&nvdimm->busy)) {
dev_warn(dev, "Unable to secure erase while DIMM active.\n");
rc = -EBUSY;
@@ -113,6 +141,7 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid)
key_put(key);
out:
+ nvdimm_clear_security_busy(nvdimm);
mutex_unlock(&nvdimm->sec_mutex);
nvdimm_bus_unlock(dev);
nvdimm_security_get_state(nvdimm);
@@ -130,15 +159,19 @@ int nvdimm_security_freeze_lock(struct nvdimm *nvdimm)
return -EOPNOTSUPP;
mutex_lock(&nvdimm->sec_mutex);
+ rc = nvdimm_check_security_busy(nvdimm);
+ if (rc < 0)
+ goto out;
+
rc = nvdimm->security_ops->freeze_lock(nvdimm);
- if (rc < 0) {
- mutex_unlock(&nvdimm->sec_mutex);
- return rc;
- }
+ if (rc < 0)
+ goto out;
nvdimm_security_get_state(nvdimm);
+
+out:
mutex_unlock(&nvdimm->sec_mutex);
- return 0;
+ return rc;
}
int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid)
@@ -156,6 +189,12 @@ int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid)
return -EOPNOTSUPP;
mutex_lock(&nvdimm->sec_mutex);
+ rc = nvdimm_check_security_busy(nvdimm);
+ if (rc < 0) {
+ mutex_unlock(&nvdimm->sec_mutex);
+ return rc;
+ }
+
/* look for a key from cached key */
key = nvdimm_lookup_user_key(dev, keyid);
if (!key) {
@@ -232,6 +271,12 @@ int nvdimm_security_unlock_dimm(struct nvdimm *nvdimm)
return 0;
mutex_lock(&nvdimm->sec_mutex);
+ rc = nvdimm_check_security_busy(nvdimm);
+ if (rc < 0) {
+ mutex_unlock(&nvdimm->sec_mutex);
+ return rc;
+ }
+
/*
* If the pre-OS has unlocked the DIMM, we will attempt to send
* the key from request_key() to the hardware for verification.
@@ -294,7 +339,7 @@ int nvdimm_security_change_key(struct nvdimm *nvdimm,
unsigned int old_keyid, unsigned int new_keyid)
{
int rc;
- struct key *key, *old_key;
+ struct key *key = NULL, *old_key = NULL;
void *old_data = NULL, *new_data;
struct device *dev = &nvdimm->dev;
struct encrypted_key_payload *epayload, *old_epayload;
@@ -306,6 +351,10 @@ int nvdimm_security_change_key(struct nvdimm *nvdimm,
return -EBUSY;
mutex_lock(&nvdimm->sec_mutex);
+ rc = nvdimm_check_security_busy(nvdimm);
+ if (rc < 0)
+ goto out;
+
/* look for a key from cached key if exists */
old_key = nvdimm_lookup_user_key(dev, old_keyid);
if (old_key)
@@ -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/nd.h | 1 + drivers/nvdimm/region_devs.c | 7 +++++ drivers/nvdimm/security.c | 61 ++++++++++++++++++++++++++++++++++++++---- include/linux/libnvdimm.h | 2 + 4 files changed, 65 insertions(+), 6 deletions(-)