diff options
author | Dave Jiang <dave.jiang@intel.com> | 2018-12-07 16:02:12 -0500 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2018-12-21 15:44:41 -0500 |
commit | 64e77c8c047fb91ea8c7800c1238108a72f0bf9c (patch) | |
tree | 59aef33ac371f18b005319560addc9c9c85dea9b | |
parent | d2a4ac73f56a5d0709d28b41fec8d15e4500f8f1 (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.c | 47 | ||||
-rw-r--r-- | drivers/nvdimm/dimm_devs.c | 9 | ||||
-rw-r--r-- | drivers/nvdimm/nd-core.h | 5 | ||||
-rw-r--r-- | drivers/nvdimm/security.c | 41 | ||||
-rw-r--r-- | include/linux/libnvdimm.h | 2 |
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 | ||
206 | static 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 |
400 | enum nvdimmsec_op_ids { OPS }; | 401 | enum 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); | |||
61 | int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid); | 61 | int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid); |
62 | int nvdimm_security_update(struct nvdimm *nvdimm, unsigned int keyid, | 62 | int nvdimm_security_update(struct nvdimm *nvdimm, unsigned int keyid, |
63 | unsigned int new_keyid); | 63 | unsigned int new_keyid); |
64 | int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid); | ||
64 | #else | 65 | #else |
65 | static inline int nvdimm_security_disable(struct nvdimm *nvdimm, | 66 | static 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 | } |
76 | static 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 | ||
34 | static void nvdimm_put_key(struct key *key) | 34 | static 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 | |||
266 | int 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 | ||
185 | void badrange_init(struct badrange *badrange); | 187 | void badrange_init(struct badrange *badrange); |