aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Jiang <dave.jiang@intel.com>2018-12-07 16:02:12 -0500
committerDan Williams <dan.j.williams@intel.com>2018-12-21 15:44:41 -0500
commit64e77c8c047fb91ea8c7800c1238108a72f0bf9c (patch)
tree59aef33ac371f18b005319560addc9c9c85dea9b
parentd2a4ac73f56a5d0709d28b41fec8d15e4500f8f1 (diff)
acpi/nfit, libnvdimm: Add support for issue secure erase DSM to Intel nvdimm
Add support to issue a secure erase DSM to the Intel nvdimm. The required passphrase is acquired from an encrypted key in the kernel user keyring. To trigger the action, "erase <keyid>" is written to the "security" sysfs attribute. Signed-off-by: Dave Jiang <dave.jiang@intel.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
-rw-r--r--drivers/acpi/nfit/intel.c47
-rw-r--r--drivers/nvdimm/dimm_devs.c9
-rw-r--r--drivers/nvdimm/nd-core.h5
-rw-r--r--drivers/nvdimm/security.c41
-rw-r--r--include/linux/libnvdimm.h2
5 files changed, 102 insertions, 2 deletions
diff --git a/drivers/acpi/nfit/intel.c b/drivers/acpi/nfit/intel.c
index bb033b74bff0..e0e04b730b4f 100644
--- a/drivers/acpi/nfit/intel.c
+++ b/drivers/acpi/nfit/intel.c
@@ -203,6 +203,52 @@ static int intel_security_disable(struct nvdimm *nvdimm,
203 return 0; 203 return 0;
204} 204}
205 205
206static int intel_security_erase(struct nvdimm *nvdimm,
207 const struct nvdimm_key_data *key)
208{
209 int rc;
210 struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
211 struct {
212 struct nd_cmd_pkg pkg;
213 struct nd_intel_secure_erase cmd;
214 } nd_cmd = {
215 .pkg = {
216 .nd_family = NVDIMM_FAMILY_INTEL,
217 .nd_size_in = ND_INTEL_PASSPHRASE_SIZE,
218 .nd_size_out = ND_INTEL_STATUS_SIZE,
219 .nd_fw_size = ND_INTEL_STATUS_SIZE,
220 .nd_command = NVDIMM_INTEL_SECURE_ERASE,
221 },
222 };
223
224 if (!test_bit(NVDIMM_INTEL_SECURE_ERASE, &nfit_mem->dsm_mask))
225 return -ENOTTY;
226
227 /* flush all cache before we erase DIMM */
228 nvdimm_invalidate_cache();
229 memcpy(nd_cmd.cmd.passphrase, key->data,
230 sizeof(nd_cmd.cmd.passphrase));
231 rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
232 if (rc < 0)
233 return rc;
234
235 switch (nd_cmd.cmd.status) {
236 case 0:
237 break;
238 case ND_INTEL_STATUS_NOT_SUPPORTED:
239 return -EOPNOTSUPP;
240 case ND_INTEL_STATUS_INVALID_PASS:
241 return -EINVAL;
242 case ND_INTEL_STATUS_INVALID_STATE:
243 default:
244 return -ENXIO;
245 }
246
247 /* DIMM erased, invalidate all CPU caches before we read it */
248 nvdimm_invalidate_cache();
249 return 0;
250}
251
206/* 252/*
207 * TODO: define a cross arch wbinvd equivalent when/if 253 * TODO: define a cross arch wbinvd equivalent when/if
208 * NVDIMM_FAMILY_INTEL command support arrives on another arch. 254 * NVDIMM_FAMILY_INTEL command support arrives on another arch.
@@ -226,6 +272,7 @@ static const struct nvdimm_security_ops __intel_security_ops = {
226 .disable = intel_security_disable, 272 .disable = intel_security_disable,
227#ifdef CONFIG_X86 273#ifdef CONFIG_X86
228 .unlock = intel_security_unlock, 274 .unlock = intel_security_unlock,
275 .erase = intel_security_erase,
229#endif 276#endif
230}; 277};
231 278
diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c
index 1cc3a6af3d0e..bc432b7c17b8 100644
--- a/drivers/nvdimm/dimm_devs.c
+++ b/drivers/nvdimm/dimm_devs.c
@@ -394,7 +394,8 @@ static ssize_t security_show(struct device *dev,
394#define OPS \ 394#define OPS \
395 C( OP_FREEZE, "freeze", 1), \ 395 C( OP_FREEZE, "freeze", 1), \
396 C( OP_DISABLE, "disable", 2), \ 396 C( OP_DISABLE, "disable", 2), \
397 C( OP_UPDATE, "update", 3) 397 C( OP_UPDATE, "update", 3), \
398 C( OP_ERASE, "erase", 2)
398#undef C 399#undef C
399#define C(a, b, c) a 400#define C(a, b, c) a
400enum nvdimmsec_op_ids { OPS }; 401enum nvdimmsec_op_ids { OPS };
@@ -448,6 +449,9 @@ static ssize_t __security_store(struct device *dev, const char *buf, size_t len)
448 } else if (i == OP_UPDATE) { 449 } else if (i == OP_UPDATE) {
449 dev_dbg(dev, "update %u %u\n", key, newkey); 450 dev_dbg(dev, "update %u %u\n", key, newkey);
450 rc = nvdimm_security_update(nvdimm, key, newkey); 451 rc = nvdimm_security_update(nvdimm, key, newkey);
452 } else if (i == OP_ERASE) {
453 dev_dbg(dev, "erase %u\n", key);
454 rc = nvdimm_security_erase(nvdimm, key);
451 } else 455 } else
452 return -EINVAL; 456 return -EINVAL;
453 457
@@ -498,7 +502,8 @@ static umode_t nvdimm_visible(struct kobject *kobj, struct attribute *a, int n)
498 return 0; 502 return 0;
499 /* Are there any state mutation ops? */ 503 /* Are there any state mutation ops? */
500 if (nvdimm->sec.ops->freeze || nvdimm->sec.ops->disable 504 if (nvdimm->sec.ops->freeze || nvdimm->sec.ops->disable
501 || nvdimm->sec.ops->change_key) 505 || nvdimm->sec.ops->change_key
506 || nvdimm->sec.ops->erase)
502 return a->mode; 507 return a->mode;
503 return 0444; 508 return 0444;
504} 509}
diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h
index c2567f9ae07b..b4b633ccfbe9 100644
--- a/drivers/nvdimm/nd-core.h
+++ b/drivers/nvdimm/nd-core.h
@@ -61,6 +61,7 @@ int nvdimm_security_freeze(struct nvdimm *nvdimm);
61int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid); 61int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid);
62int nvdimm_security_update(struct nvdimm *nvdimm, unsigned int keyid, 62int nvdimm_security_update(struct nvdimm *nvdimm, unsigned int keyid,
63 unsigned int new_keyid); 63 unsigned int new_keyid);
64int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid);
64#else 65#else
65static inline int nvdimm_security_disable(struct nvdimm *nvdimm, 66static inline int nvdimm_security_disable(struct nvdimm *nvdimm,
66 unsigned int keyid) 67 unsigned int keyid)
@@ -72,6 +73,10 @@ static inline int nvdimm_security_update(struct nvdimm *nvdimm, unsigned int key
72{ 73{
73 return -EOPNOTSUPP; 74 return -EOPNOTSUPP;
74} 75}
76static inline int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid)
77{
78 return -EOPNOTSUPP;
79}
75#endif 80#endif
76 81
77/** 82/**
diff --git a/drivers/nvdimm/security.c b/drivers/nvdimm/security.c
index df7f070e96fb..05677be3c0dd 100644
--- a/drivers/nvdimm/security.c
+++ b/drivers/nvdimm/security.c
@@ -33,6 +33,9 @@ static void *key_data(struct key *key)
33 33
34static void nvdimm_put_key(struct key *key) 34static void nvdimm_put_key(struct key *key)
35{ 35{
36 if (!key)
37 return;
38
36 up_read(&key->sem); 39 up_read(&key->sem);
37 key_put(key); 40 key_put(key);
38} 41}
@@ -259,3 +262,41 @@ int nvdimm_security_update(struct nvdimm *nvdimm, unsigned int keyid,
259 nvdimm->sec.state = nvdimm_security_state(nvdimm); 262 nvdimm->sec.state = nvdimm_security_state(nvdimm);
260 return rc; 263 return rc;
261} 264}
265
266int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid)
267{
268 struct device *dev = &nvdimm->dev;
269 struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
270 struct key *key;
271 int rc;
272
273 /* The bus lock should be held at the top level of the call stack */
274 lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
275
276 if (!nvdimm->sec.ops || !nvdimm->sec.ops->erase
277 || nvdimm->sec.state < 0)
278 return -EOPNOTSUPP;
279
280 if (atomic_read(&nvdimm->busy)) {
281 dev_warn(dev, "Unable to secure erase while DIMM active.\n");
282 return -EBUSY;
283 }
284
285 if (nvdimm->sec.state >= NVDIMM_SECURITY_FROZEN) {
286 dev_warn(dev, "Incorrect security state: %d\n",
287 nvdimm->sec.state);
288 return -EIO;
289 }
290
291 key = nvdimm_lookup_user_key(nvdimm, keyid, NVDIMM_BASE_KEY);
292 if (!key)
293 return -ENOKEY;
294
295 rc = nvdimm->sec.ops->erase(nvdimm, key_data(key));
296 dev_dbg(dev, "key: %d erase: %s\n", key_serial(key),
297 rc == 0 ? "success" : "fail");
298
299 nvdimm_put_key(key);
300 nvdimm->sec.state = nvdimm_security_state(nvdimm);
301 return rc;
302}
diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
index d0afa115356e..9a6cb7067dc7 100644
--- a/include/linux/libnvdimm.h
+++ b/include/linux/libnvdimm.h
@@ -180,6 +180,8 @@ struct nvdimm_security_ops {
180 const struct nvdimm_key_data *key_data); 180 const struct nvdimm_key_data *key_data);
181 int (*disable)(struct nvdimm *nvdimm, 181 int (*disable)(struct nvdimm *nvdimm,
182 const struct nvdimm_key_data *key_data); 182 const struct nvdimm_key_data *key_data);
183 int (*erase)(struct nvdimm *nvdimm,
184 const struct nvdimm_key_data *key_data);
183}; 185};
184 186
185void badrange_init(struct badrange *badrange); 187void badrange_init(struct badrange *badrange);