nvdimm.lists.linux.dev archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/5] Adding nvdimm overwrite support
@ 2018-09-28 22:44 Dave Jiang
  2018-09-28 22:44 ` [PATCH v2 1/5] libnvdimm: introduce NDD_SECURITY_BUSY flag Dave Jiang
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Dave Jiang @ 2018-09-28 22:44 UTC (permalink / raw)
  To: dan.j.williams; +Cc: linux-nvdimm

The following series implements the overwrite support for Intel nvdimm.
Overwrite DSM commands are part of Intel DSM v1.7 spec. It allows the
nvdimm to wipe all the information on the target nvdimm (including the
label area). The operation can take tens of mintues or more depending
on the size of the nvdimm.

v2:
- rebase with Dan's changes for the security code.

---

Dave Jiang (5):
      libnvdimm: introduce NDD_SECURITY_BUSY flag
      libnvdimm: Add security DSM overwrite support
      nfit_test: Add overwrite support for nfit_test
      libnvdimm: add overwrite status notification
      libnvdimm: add documentation for ovewrite


 Documentation/nvdimm/security.txt |   11 ++
 drivers/acpi/nfit/core.c          |    5 +
 drivers/acpi/nfit/intel.c         |  113 ++++++++++++++++++++
 drivers/acpi/nfit/intel.h         |    4 +
 drivers/acpi/nfit/nfit.h          |    1 
 drivers/nvdimm/core.c             |    3 +
 drivers/nvdimm/dimm.c             |    4 +
 drivers/nvdimm/dimm_devs.c        |  204 +++++++++++++++++++++++++++++++++++++
 drivers/nvdimm/nd-core.h          |    4 +
 drivers/nvdimm/nd.h               |    3 +
 drivers/nvdimm/region_devs.c      |    7 +
 include/linux/libnvdimm.h         |    7 +
 tools/testing/nvdimm/test/nfit.c  |   55 ++++++++++
 13 files changed, 419 insertions(+), 2 deletions(-)

--
_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v2 1/5] libnvdimm: introduce NDD_SECURITY_BUSY flag
  2018-09-28 22:44 [PATCH v2 0/5] Adding nvdimm overwrite support Dave Jiang
@ 2018-09-28 22:44 ` Dave Jiang
  2018-09-28 22:44 ` [PATCH v2 2/5] libnvdimm: Add security DSM overwrite support Dave Jiang
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Dave Jiang @ 2018-09-28 22:44 UTC (permalink / raw)
  To: dan.j.williams; +Cc: linux-nvdimm

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(-)

diff --git a/drivers/nvdimm/dimm.c b/drivers/nvdimm/dimm.c
index b6381ddbd6c1..5ff9367b8671 100644
--- a/drivers/nvdimm/dimm.c
+++ b/drivers/nvdimm/dimm.c
@@ -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 */
diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c
index 656a8fafca8e..7a71ea666676 100644
--- a/drivers/nvdimm/dimm_devs.c
+++ b/drivers/nvdimm/dimm_devs.c
@@ -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
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
index fa3b89127078..252fbe6d4ecd 100644
--- a/drivers/nvdimm/nd.h
+++ b/drivers/nvdimm/nd.h
@@ -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,
diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c
index fa37afcd43ff..3e089c533397 100644
--- a/drivers/nvdimm/region_devs.c
+++ b/drivers/nvdimm/region_devs.c
@@ -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 *);
diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
index c352b8195675..32c1da8c49e2 100644
--- a/include/linux/libnvdimm.h
+++ b/include/linux/libnvdimm.h
@@ -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,

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v2 2/5] libnvdimm: Add security DSM overwrite support
  2018-09-28 22:44 [PATCH v2 0/5] Adding nvdimm overwrite support Dave Jiang
  2018-09-28 22:44 ` [PATCH v2 1/5] libnvdimm: introduce NDD_SECURITY_BUSY flag Dave Jiang
@ 2018-09-28 22:44 ` Dave Jiang
  2018-09-28 22:45 ` [PATCH v2 3/5] nfit_test: Add overwrite support for nfit_test Dave Jiang
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Dave Jiang @ 2018-09-28 22:44 UTC (permalink / raw)
  To: dan.j.williams; +Cc: linux-nvdimm

We are adding support for the security calls of ovewrite and query
overwrite from Intel DSM spec v1.7. This will allow triggering of
overwrite on Intel NVDIMMs. The overwrite operation can take tens
of minutes. When the overwrite DSM is issued successfully, the NVDIMMs
will be unaccessible. The kernel will do backoff polling to detect when
the overwrite process is completed. According to the DSM spec v1.7,
the 128G NVDIMMs can take up to 15mins to perform overwrite and larger
DIMMs will take longer.

Signed-off-by: Dave Jiang <dave.jiang@intel.com>
---
 drivers/acpi/nfit/intel.c  |  113 ++++++++++++++++++++++++++++++++++++
 drivers/acpi/nfit/intel.h  |    3 +
 drivers/acpi/nfit/nfit.h   |    1 
 drivers/nvdimm/core.c      |    3 +
 drivers/nvdimm/dimm_devs.c |  140 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/nvdimm/nd-core.h   |    3 +
 include/linux/libnvdimm.h  |    4 +
 7 files changed, 266 insertions(+), 1 deletion(-)

diff --git a/drivers/acpi/nfit/intel.c b/drivers/acpi/nfit/intel.c
index 8d2413810a5f..2783e060e2b1 100644
--- a/drivers/acpi/nfit/intel.c
+++ b/drivers/acpi/nfit/intel.c
@@ -18,6 +18,112 @@
 #include "intel.h"
 #include "nfit.h"
 
+static int intel_dimm_security_query_overwrite(struct nvdimm *nvdimm)
+{
+	int cmd_rc, rc = 0;
+	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+	struct {
+		struct nd_cmd_pkg pkg;
+		struct nd_intel_query_overwrite cmd;
+	} nd_cmd = {
+		.pkg = {
+			.nd_command = NVDIMM_INTEL_QUERY_OVERWRITE,
+			.nd_family = NVDIMM_FAMILY_INTEL,
+			.nd_size_in = 0,
+			.nd_size_out = ND_INTEL_STATUS_SIZE,
+			.nd_fw_size = ND_INTEL_STATUS_SIZE,
+		},
+		.cmd = {
+			.status = 0,
+		},
+	};
+
+	if (!test_bit(NVDIMM_INTEL_QUERY_OVERWRITE, &nfit_mem->dsm_mask))
+		return -ENOTTY;
+
+	rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), &cmd_rc);
+	if (rc < 0)
+		goto out;
+	if (cmd_rc < 0) {
+		rc = cmd_rc;
+		goto out;
+	}
+
+	switch (nd_cmd.cmd.status) {
+	case 0:
+		break;
+	case ND_INTEL_STATUS_OQUERY_INPROGRESS:
+		rc = -EBUSY;
+		goto out;
+	default:
+		rc = -ENXIO;
+		goto out;
+	}
+
+	/* flush all cache before we make the nvdimms available */
+	wbinvd_on_all_cpus();
+	nfit_mem->overwrite = false;
+
+out:
+	return rc;
+}
+
+static int intel_dimm_security_overwrite(struct nvdimm *nvdimm,
+		const struct nvdimm_key_data *nkey)
+{
+	int cmd_rc, rc = 0;
+	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+	struct {
+		struct nd_cmd_pkg pkg;
+		struct nd_intel_overwrite cmd;
+	} nd_cmd = {
+		.pkg = {
+			.nd_command = NVDIMM_INTEL_OVERWRITE,
+			.nd_family = NVDIMM_FAMILY_INTEL,
+			.nd_size_in = ND_INTEL_PASSPHRASE_SIZE,
+			.nd_size_out = ND_INTEL_STATUS_SIZE,
+			.nd_fw_size = ND_INTEL_STATUS_SIZE,
+		},
+		.cmd = {
+			.status = 0,
+		},
+	};
+
+	if (!test_bit(NVDIMM_INTEL_OVERWRITE, &nfit_mem->dsm_mask))
+		return -ENOTTY;
+
+	/* flush all cache before we erase DIMM */
+	wbinvd_on_all_cpus();
+	memcpy(nd_cmd.cmd.passphrase, nkey->data,
+			sizeof(nd_cmd.cmd.passphrase));
+	rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), &cmd_rc);
+	if (rc < 0)
+		goto out;
+	if (cmd_rc < 0) {
+		rc = cmd_rc;
+		goto out;
+	}
+
+	switch (nd_cmd.cmd.status) {
+	case 0:
+		nfit_mem->overwrite = true;
+		break;
+	case ND_INTEL_STATUS_OVERWRITE_UNSUPPORTED:
+		rc = -ENOTSUPP;
+		break;
+	case ND_INTEL_STATUS_INVALID_PASS:
+		rc = -EINVAL;
+		goto out;
+	case ND_INTEL_STATUS_INVALID_STATE:
+	default:
+		rc = -ENXIO;
+		goto out;
+	}
+
+ out:
+	return rc;
+}
+
 static int intel_dimm_security_erase(struct nvdimm *nvdimm,
 		const struct nvdimm_key_data *nkey)
 {
@@ -317,6 +423,11 @@ static int intel_dimm_security_state(struct nvdimm *nvdimm,
 		return 0;
 	}
 
+	if (nfit_mem->overwrite == true) {
+		*state = NVDIMM_SECURITY_OVERWRITE;
+		return 0;
+	}
+
 	*state = NVDIMM_SECURITY_DISABLED;
 	rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), &cmd_rc);
 	if (rc < 0)
@@ -365,5 +476,7 @@ static const struct nvdimm_security_ops __intel_security_ops = {
 	.disable = intel_dimm_security_disable,
 	.freeze_lock = intel_dimm_security_freeze_lock,
 	.erase = intel_dimm_security_erase,
+	.overwrite = intel_dimm_security_overwrite,
+	.query_overwrite = intel_dimm_security_query_overwrite,
 };
 const struct nvdimm_security_ops *intel_security_ops = &__intel_security_ops;
diff --git a/drivers/acpi/nfit/intel.h b/drivers/acpi/nfit/intel.h
index f831084f95fa..65d57a6abbc2 100644
--- a/drivers/acpi/nfit/intel.h
+++ b/drivers/acpi/nfit/intel.h
@@ -17,12 +17,15 @@ extern const struct nvdimm_security_ops *intel_security_ops;
 #define ND_INTEL_STATUS_NOT_READY	9
 #define ND_INTEL_STATUS_INVALID_STATE	10
 #define ND_INTEL_STATUS_INVALID_PASS	11
+#define ND_INTEL_STATUS_OVERWRITE_UNSUPPORTED	0x10007
+#define ND_INTEL_STATUS_OQUERY_INPROGRESS	0x10007
 
 #define ND_INTEL_SEC_STATE_ENABLED	0x02
 #define ND_INTEL_SEC_STATE_LOCKED	0x04
 #define ND_INTEL_SEC_STATE_FROZEN	0x08
 #define ND_INTEL_SEC_STATE_PLIMIT	0x10
 #define ND_INTEL_SEC_STATE_UNSUPPORTED	0x20
+#define ND_INTEL_SEC_STATE_OVERWRITE	0x40
 
 struct nd_intel_get_security_state {
 	u32 status;
diff --git a/drivers/acpi/nfit/nfit.h b/drivers/acpi/nfit/nfit.h
index 36c8695a3d27..e36a111d393a 100644
--- a/drivers/acpi/nfit/nfit.h
+++ b/drivers/acpi/nfit/nfit.h
@@ -198,6 +198,7 @@ struct nfit_mem {
 	int family;
 	bool has_lsr;
 	bool has_lsw;
+	bool overwrite;
 	char id[NFIT_DIMM_ID_LEN+1];
 };
 
diff --git a/drivers/nvdimm/core.c b/drivers/nvdimm/core.c
index acce050856a8..b2496c06178b 100644
--- a/drivers/nvdimm/core.c
+++ b/drivers/nvdimm/core.c
@@ -437,6 +437,9 @@ static __init int libnvdimm_init(void)
 {
 	int rc;
 
+	rc = nvdimm_devs_init();
+	if (rc)
+		return rc;
 	rc = nvdimm_bus_init();
 	if (rc)
 		return rc;
diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c
index 7a71ea666676..73f7e879d6e0 100644
--- a/drivers/nvdimm/dimm_devs.c
+++ b/drivers/nvdimm/dimm_devs.c
@@ -28,6 +28,7 @@
 #include "nd.h"
 
 static DEFINE_IDA(dimm_ida);
+static struct workqueue_struct *nvdimm_wq;
 
 /*
  * Find key that's cached with nvdimm
@@ -181,6 +182,126 @@ int nvdimm_security_get_state(struct device *dev)
 	return nvdimm->security_ops->state(nvdimm, &nvdimm->state);
 }
 
+static void nvdimm_overwrite_query(struct work_struct *work)
+{
+	struct nvdimm *nvdimm;
+	struct nvdimm_bus *nvdimm_bus;
+	int rc;
+	unsigned int tmo;
+
+	nvdimm = container_of(work, typeof(*nvdimm), dwork.work);
+	nvdimm_bus = walk_to_nvdimm_bus(&nvdimm->dev);
+	tmo = nvdimm->overwrite_tmo;
+
+	if (!nvdimm->security_ops)
+		return;
+
+	rc = nvdimm->security_ops->query_overwrite(nvdimm);
+	if (rc == -EBUSY) {
+
+		/* setup delayed work again */
+		tmo += 10;
+		queue_delayed_work(nvdimm_wq, &nvdimm->dwork, tmo * HZ);
+		nvdimm->overwrite_tmo = min(15U * 60U, tmo);
+		return;
+	}
+
+	if (rc < 0)
+		dev_warn(&nvdimm->dev, "Overwrite failed\n");
+	else
+		dev_info(&nvdimm->dev, "Overwrite completed\n");
+
+	nvdimm->overwrite_tmo = 0;
+	nvdimm_clear_security_busy(&nvdimm->dev);
+	nvdimm_security_get_state(&nvdimm->dev);
+}
+
+static int nvdimm_security_overwrite(struct device *dev, unsigned int keyid)
+{
+	struct nvdimm *nvdimm = to_nvdimm(dev);
+	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
+	struct key *key;
+	struct user_key_payload *payload;
+	int rc;
+	char pass[NVDIMM_PASSPHRASE_LEN];
+	bool is_userkey = false;
+
+	if (!nvdimm->security_ops)
+		return 0;
+
+	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 overwrite while DIMM active.\n");
+		rc = -EBUSY;
+		goto out;
+	}
+
+	if (dev_get_drvdata(dev)) {
+		dev_warn(dev, "Unable to overwrite while DIMM active.\n");
+		rc = -EINVAL;
+		goto out;
+	}
+
+	if (nvdimm->state == NVDIMM_SECURITY_UNSUPPORTED) {
+		dev_warn(dev, "Attempt to overwrite in wrong state.\n");
+		rc = -EOPNOTSUPP;
+		goto out;
+	}
+
+	/* look for a key from keyring if exists and remove */
+	key = nvdimm_get_and_verify_key(dev, keyid);
+	if (IS_ERR(key)) {
+		dev_dbg(dev, "Unable to get and verify key\n");
+		rc = PTR_ERR(key);
+		goto out;
+	}
+	if (!key) {
+		dev_dbg(dev, "No cached key found\n");
+		/* get old user key */
+		key = nvdimm_lookup_user_key(dev, keyid);
+		if (key)
+			is_userkey = true;
+	}
+
+	if (key) {
+		down_read(&key->sem);
+		payload = key->payload.data[0];
+		memcpy(pass, payload->data, NVDIMM_PASSPHRASE_LEN);
+		up_read(&key->sem);
+	} else
+		memset(pass, 0, NVDIMM_PASSPHRASE_LEN);
+
+	nvdimm_set_security_busy(dev);
+	/*
+	 * Overwrite can execute w/o passphrase. we will let the hardware
+	 * determine whether we need a passphrase or not.
+	 */
+	rc = nvdimm->security_ops->overwrite(nvdimm, (void *)pass);
+	if (rc == 0) {
+		if (key) {
+			if (!is_userkey) {
+				key_invalidate(key);
+				nvdimm->key = NULL;
+			}
+			key_put(key);
+			memset(pass, 0, NVDIMM_PASSPHRASE_LEN);
+		}
+		nvdimm->state = NVDIMM_SECURITY_OVERWRITE;
+		queue_delayed_work(nvdimm_wq, &nvdimm->dwork, 0);
+	} else
+		nvdimm_clear_security_busy(dev);
+
+ out:
+	nvdimm_bus_unlock(&nvdimm_bus->dev);
+	nvdimm_security_get_state(dev);
+	return rc;
+
+}
+
 static int nvdimm_security_erase(struct device *dev, unsigned int keyid)
 {
 	struct nvdimm *nvdimm = to_nvdimm(dev);
@@ -898,6 +1019,8 @@ __weak ssize_t security_show(struct device *dev,
 		return sprintf(buf, "locked\n");
 	case NVDIMM_SECURITY_FROZEN:
 		return sprintf(buf, "frozen\n");
+	case NVDIMM_SECURITY_OVERWRITE:
+		return sprintf(buf, "overwrite\n");
 	case NVDIMM_SECURITY_UNSUPPORTED:
 	default:
 		return sprintf(buf, "unsupported\n");
@@ -944,6 +1067,9 @@ static ssize_t security_store(struct device *dev,
 			return -EINVAL;
 		dev_dbg(dev, "erase %#x\n", old_key);
 		rc = nvdimm_security_erase(dev, old_key);
+	} else if (sysfs_streq(cmd, "overwrite")) {
+		dev_dbg(dev, "overwrite %u\n", old_key);
+		rc = nvdimm_security_overwrite(dev, old_key);
 	} else
 		return -EINVAL;
 
@@ -1000,6 +1126,8 @@ struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
 	dev->type = &nvdimm_device_type;
 	dev->devt = MKDEV(nvdimm_major, nvdimm->id);
 	dev->groups = groups;
+	nvdimm->overwrite_tmo = 0;
+	INIT_DELAYED_WORK(&nvdimm->dwork, nvdimm_overwrite_query);
 	nd_device_register(dev);
 
 	return nvdimm;
@@ -1276,7 +1404,17 @@ int nvdimm_bus_check_dimm_count(struct nvdimm_bus *nvdimm_bus, int dimm_count)
 }
 EXPORT_SYMBOL_GPL(nvdimm_bus_check_dimm_count);
 
-void __exit nvdimm_devs_exit(void)
+int __init nvdimm_devs_init(void)
+{
+	nvdimm_wq = create_singlethread_workqueue("nvdimm");
+	if (!nvdimm_wq)
+		return -ENOMEM;
+
+	return 0;
+}
+
+void  nvdimm_devs_exit(void)
 {
+	destroy_workqueue(nvdimm_wq);
 	ida_destroy(&dimm_ida);
 }
diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h
index b4a6ca15d077..efc7eb9e624c 100644
--- a/drivers/nvdimm/nd-core.h
+++ b/drivers/nvdimm/nd-core.h
@@ -46,6 +46,8 @@ struct nvdimm {
 	const struct nvdimm_security_ops *security_ops;
 	enum nvdimm_security_state state;
 	struct key *key;
+	struct delayed_work dwork;
+	unsigned int overwrite_tmo;
 };
 
 /**
@@ -81,6 +83,7 @@ struct nvdimm_bus *walk_to_nvdimm_bus(struct device *nd_dev);
 int __init nvdimm_bus_init(void);
 void nvdimm_bus_exit(void);
 void nvdimm_devs_exit(void);
+int nvdimm_devs_init(void);
 void nd_region_devs_exit(void);
 void nd_region_probe_success(struct nvdimm_bus *nvdimm_bus, struct device *dev);
 struct nd_region;
diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
index 32c1da8c49e2..b59ff6b798b2 100644
--- a/include/linux/libnvdimm.h
+++ b/include/linux/libnvdimm.h
@@ -163,6 +163,7 @@ enum nvdimm_security_state {
 	NVDIMM_SECURITY_UNLOCKED,
 	NVDIMM_SECURITY_LOCKED,
 	NVDIMM_SECURITY_FROZEN,
+	NVDIMM_SECURITY_OVERWRITE,
 	NVDIMM_SECURITY_UNSUPPORTED,
 };
 
@@ -186,6 +187,9 @@ struct nvdimm_security_ops {
 	int (*freeze_lock)(struct nvdimm *nvdimm);
 	int (*erase)(struct nvdimm *nvdimm,
 			const struct nvdimm_key_data *nkey);
+	int (*overwrite)(struct nvdimm *nvdimm,
+			const struct nvdimm_key_data *nkey);
+	int (*query_overwrite)(struct nvdimm *nvdimm);
 };
 
 void badrange_init(struct badrange *badrange);

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v2 3/5] nfit_test: Add overwrite support for nfit_test
  2018-09-28 22:44 [PATCH v2 0/5] Adding nvdimm overwrite support Dave Jiang
  2018-09-28 22:44 ` [PATCH v2 1/5] libnvdimm: introduce NDD_SECURITY_BUSY flag Dave Jiang
  2018-09-28 22:44 ` [PATCH v2 2/5] libnvdimm: Add security DSM overwrite support Dave Jiang
@ 2018-09-28 22:45 ` Dave Jiang
  2018-09-28 22:45 ` [PATCH v2 4/5] libnvdimm: add overwrite status notification Dave Jiang
  2018-09-28 22:45 ` [PATCH v2 5/5] libnvdimm: add documentation for ovewrite Dave Jiang
  4 siblings, 0 replies; 6+ messages in thread
From: Dave Jiang @ 2018-09-28 22:45 UTC (permalink / raw)
  To: dan.j.williams; +Cc: linux-nvdimm

With the implementation of Intel NVDIMM DSM overwrite, we are adding unit
test to nfit_test for testing of overwrite operation.

Signed-off-by: Dave Jiang <dave.jiang@intel.com>
---
 drivers/acpi/nfit/intel.h        |    1 +
 tools/testing/nvdimm/test/nfit.c |   55 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 56 insertions(+)

diff --git a/drivers/acpi/nfit/intel.h b/drivers/acpi/nfit/intel.h
index 65d57a6abbc2..c7426ebf3d08 100644
--- a/drivers/acpi/nfit/intel.h
+++ b/drivers/acpi/nfit/intel.h
@@ -19,6 +19,7 @@ extern const struct nvdimm_security_ops *intel_security_ops;
 #define ND_INTEL_STATUS_INVALID_PASS	11
 #define ND_INTEL_STATUS_OVERWRITE_UNSUPPORTED	0x10007
 #define ND_INTEL_STATUS_OQUERY_INPROGRESS	0x10007
+#define ND_INTEL_STATUS_OQUERY_SEQUENCE_ERR	0x20007
 
 #define ND_INTEL_SEC_STATE_ENABLED	0x02
 #define ND_INTEL_SEC_STATE_LOCKED	0x04
diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c
index 1d7ae7aecb46..ee09ea84823a 100644
--- a/tools/testing/nvdimm/test/nfit.c
+++ b/tools/testing/nvdimm/test/nfit.c
@@ -149,6 +149,7 @@ static int dimm_fail_cmd_code[NUM_DCR];
 struct nfit_test_sec {
 	u8 state;
 	u8 passphrase[32];
+	u64 overwrite_end_time;
 } dimm_sec_info[NUM_DCR];
 
 static const struct nd_intel_smart smart_def = {
@@ -1072,6 +1073,50 @@ static int nd_intel_test_cmd_secure_erase(struct nfit_test *t,
 	return 0;
 }
 
+static int nd_intel_test_cmd_overwrite(struct nfit_test *t,
+		struct nd_intel_overwrite *nd_cmd,
+		unsigned int buf_len, int dimm)
+{
+	struct device *dev = &t->pdev.dev;
+	struct nfit_test_sec *sec = &dimm_sec_info[dimm];
+
+	if ((sec->state & ND_INTEL_SEC_STATE_ENABLED) &&
+			memcmp(nd_cmd->passphrase, sec->passphrase,
+				ND_INTEL_PASSPHRASE_SIZE) != 0) {
+		nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS;
+		dev_dbg(dev, "overwrite: wrong passphrase\n");
+		return 0;
+	}
+
+	memset(sec->passphrase, 0, ND_INTEL_PASSPHRASE_SIZE);
+	sec->state = ND_INTEL_SEC_STATE_OVERWRITE;
+	dev_dbg(dev, "overwrite progressing.\n");
+	sec->overwrite_end_time = get_jiffies_64() + 5 * HZ;
+
+	return 0;
+}
+
+static int nd_intel_test_cmd_query_overwrite(struct nfit_test *t,
+		struct nd_intel_query_overwrite *nd_cmd,
+		unsigned int buf_len, int dimm)
+{
+	struct device *dev = &t->pdev.dev;
+	struct nfit_test_sec *sec = &dimm_sec_info[dimm];
+
+	if (!(sec->state & ND_INTEL_SEC_STATE_OVERWRITE)) {
+		nd_cmd->status = ND_INTEL_STATUS_OQUERY_SEQUENCE_ERR;
+		return 0;
+	}
+
+	if (time_is_before_jiffies64(sec->overwrite_end_time)) {
+		sec->overwrite_end_time = 0;
+		sec->state = 0;
+		dev_dbg(dev, "overwrite is complete\n");
+	} else
+		nd_cmd->status = ND_INTEL_STATUS_OQUERY_INPROGRESS;
+	return 0;
+}
+
 static int get_dimm(struct nfit_mem *nfit_mem, unsigned int func)
 {
 	int i;
@@ -1143,6 +1188,14 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
 				rc = nd_intel_test_cmd_secure_erase(t,
 						buf, buf_len, i);
 				break;
+			case NVDIMM_INTEL_OVERWRITE:
+				rc = nd_intel_test_cmd_overwrite(t,
+						buf, buf_len, i - t->dcr_idx);
+				break;
+			case NVDIMM_INTEL_QUERY_OVERWRITE:
+				rc = nd_intel_test_cmd_query_overwrite(t,
+						buf, buf_len, i - t->dcr_idx);
+				break;
 			case ND_INTEL_ENABLE_LSS_STATUS:
 				rc = nd_intel_test_cmd_set_lss_status(t,
 						buf, buf_len);
@@ -2378,6 +2431,8 @@ static void nfit_test0_setup(struct nfit_test *t)
 	set_bit(NVDIMM_INTEL_UNLOCK_UNIT, &acpi_desc->dimm_cmd_force_en);
 	set_bit(NVDIMM_INTEL_FREEZE_LOCK, &acpi_desc->dimm_cmd_force_en);
 	set_bit(NVDIMM_INTEL_SECURE_ERASE, &acpi_desc->dimm_cmd_force_en);
+	set_bit(NVDIMM_INTEL_OVERWRITE, &acpi_desc->dimm_cmd_force_en);
+	set_bit(NVDIMM_INTEL_QUERY_OVERWRITE, &acpi_desc->dimm_cmd_force_en);
 }
 
 static void nfit_test1_setup(struct nfit_test *t)

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v2 4/5] libnvdimm: add overwrite status notification
  2018-09-28 22:44 [PATCH v2 0/5] Adding nvdimm overwrite support Dave Jiang
                   ` (2 preceding siblings ...)
  2018-09-28 22:45 ` [PATCH v2 3/5] nfit_test: Add overwrite support for nfit_test Dave Jiang
@ 2018-09-28 22:45 ` Dave Jiang
  2018-09-28 22:45 ` [PATCH v2 5/5] libnvdimm: add documentation for ovewrite Dave Jiang
  4 siblings, 0 replies; 6+ messages in thread
From: Dave Jiang @ 2018-09-28 22:45 UTC (permalink / raw)
  To: dan.j.williams; +Cc: linux-nvdimm

Adding sysfs notification for when overwrite has completed to allow
user monitoring app to be aware of overwrite completion status.

Signed-off-by: Dave Jiang <dave.jiang@intel.com>
---
 drivers/acpi/nfit/core.c   |    5 +++++
 drivers/nvdimm/dimm_devs.c |   12 ++++++++++++
 drivers/nvdimm/nd-core.h   |    1 +
 include/linux/libnvdimm.h  |    1 +
 4 files changed, 19 insertions(+)

diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c
index b7773c70ee81..93c044527994 100644
--- a/drivers/acpi/nfit/core.c
+++ b/drivers/acpi/nfit/core.c
@@ -1950,6 +1950,11 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
 		if (!nvdimm)
 			continue;
 
+		rc = nvdimm_setup_security_events(nvdimm);
+		if (rc < 0)
+			dev_warn(acpi_desc->dev,
+					"no security event setup failed\n");
+
 		nfit_kernfs = sysfs_get_dirent(nvdimm_kobj(nvdimm)->sd, "nfit");
 		if (nfit_kernfs)
 			nfit_mem->flags_attr = sysfs_get_dirent(nfit_kernfs,
diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c
index 73f7e879d6e0..12bafa3ba8f9 100644
--- a/drivers/nvdimm/dimm_devs.c
+++ b/drivers/nvdimm/dimm_devs.c
@@ -211,6 +211,8 @@ static void nvdimm_overwrite_query(struct work_struct *work)
 	else
 		dev_info(&nvdimm->dev, "Overwrite completed\n");
 
+	if (nvdimm->overwrite_state)
+		sysfs_notify_dirent(nvdimm->overwrite_state);
 	nvdimm->overwrite_tmo = 0;
 	nvdimm_clear_security_busy(&nvdimm->dev);
 	nvdimm_security_get_state(&nvdimm->dev);
@@ -1134,6 +1136,16 @@ struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
 }
 EXPORT_SYMBOL_GPL(nvdimm_create);
 
+int nvdimm_setup_security_events(struct nvdimm *nvdimm)
+{
+	nvdimm->overwrite_state = sysfs_get_dirent(nvdimm->dev.kobj.sd,
+			"security");
+	if (!nvdimm->overwrite_state)
+		return -ENODEV;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(nvdimm_setup_security_events);
+
 int alias_dpa_busy(struct device *dev, void *data)
 {
 	resource_size_t map_end, blk_start, new;
diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h
index efc7eb9e624c..043016f41156 100644
--- a/drivers/nvdimm/nd-core.h
+++ b/drivers/nvdimm/nd-core.h
@@ -48,6 +48,7 @@ struct nvdimm {
 	struct key *key;
 	struct delayed_work dwork;
 	unsigned int overwrite_tmo;
+	struct kernfs_node *overwrite_state;
 };
 
 /**
diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
index b59ff6b798b2..baf33b74cddf 100644
--- a/include/linux/libnvdimm.h
+++ b/include/linux/libnvdimm.h
@@ -218,6 +218,7 @@ struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
 		unsigned long cmd_mask, int num_flush,
 		struct resource *flush_wpq, const char *dimm_id,
 		const struct nvdimm_security_ops *sec_ops);
+int nvdimm_setup_security_events(struct nvdimm *nvdimm);
 const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd);
 const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd);
 u32 nd_cmd_in_size(struct nvdimm *nvdimm, int cmd,

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v2 5/5] libnvdimm: add documentation for ovewrite
  2018-09-28 22:44 [PATCH v2 0/5] Adding nvdimm overwrite support Dave Jiang
                   ` (3 preceding siblings ...)
  2018-09-28 22:45 ` [PATCH v2 4/5] libnvdimm: add overwrite status notification Dave Jiang
@ 2018-09-28 22:45 ` Dave Jiang
  4 siblings, 0 replies; 6+ messages in thread
From: Dave Jiang @ 2018-09-28 22:45 UTC (permalink / raw)
  To: dan.j.williams; +Cc: linux-nvdimm

Add overwrite command usages to security documentation.

Signed-off-by: Dave Jiang <dave.jiang@intel.com>
---
 Documentation/nvdimm/security.txt |   11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/Documentation/nvdimm/security.txt b/Documentation/nvdimm/security.txt
index 50cbb6cb96a1..ded5f0e3f7c3 100644
--- a/Documentation/nvdimm/security.txt
+++ b/Documentation/nvdimm/security.txt
@@ -91,6 +91,17 @@ its keyid should be passed in via sysfs.
 The command format for doing a secure erase is:
 erase <old keyid>
 
+9. Overwrite
+------------
+The command format for doing an overwrite is:
+overwrite <old keyid>
+
+Overwrite can be done without a key if security is not enabled. A key serial
+of 0 can be passed in to indicate no key.
+
+The sysfs attribute "security" can be polled to wait on overwrite completion.
+Overwrite can last tens of minutes or more depending on nvdimm size.
+
 An "old" key with the passphrase payload that is tied to the nvdimm should be
 injected with a key description that does not have the "nvdimm:" prefix and
 its keyid should be passed in via sysfs.

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2018-09-28 22:45 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-09-28 22:44 [PATCH v2 0/5] Adding nvdimm overwrite support Dave Jiang
2018-09-28 22:44 ` [PATCH v2 1/5] libnvdimm: introduce NDD_SECURITY_BUSY flag Dave Jiang
2018-09-28 22:44 ` [PATCH v2 2/5] libnvdimm: Add security DSM overwrite support Dave Jiang
2018-09-28 22:45 ` [PATCH v2 3/5] nfit_test: Add overwrite support for nfit_test Dave Jiang
2018-09-28 22:45 ` [PATCH v2 4/5] libnvdimm: add overwrite status notification Dave Jiang
2018-09-28 22:45 ` [PATCH v2 5/5] libnvdimm: add documentation for ovewrite Dave Jiang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).