Blame view

drivers/nvdimm/security.c 13.9 KB
4c6926a23   Dave Jiang   acpi/nfit, libnvd...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  // SPDX-License-Identifier: GPL-2.0
  /* Copyright(c) 2018 Intel Corporation. All rights reserved. */
  
  #include <linux/module.h>
  #include <linux/device.h>
  #include <linux/ndctl.h>
  #include <linux/slab.h>
  #include <linux/io.h>
  #include <linux/mm.h>
  #include <linux/cred.h>
  #include <linux/key.h>
  #include <linux/key-type.h>
  #include <keys/user-type.h>
  #include <keys/encrypted-type.h>
  #include "nd-core.h"
  #include "nd.h"
d2a4ac73f   Dave Jiang   acpi/nfit, libnvd...
17
18
  #define NVDIMM_BASE_KEY		0
  #define NVDIMM_NEW_KEY		1
4c6926a23   Dave Jiang   acpi/nfit, libnvd...
19
20
21
  static bool key_revalidate = true;
  module_param(key_revalidate, bool, 0444);
  MODULE_PARM_DESC(key_revalidate, "Require key validation at init.");
037c8489a   Dave Jiang   libnvdimm/securit...
22
  static const char zero_key[NVDIMM_PASSPHRASE_LEN];
4c6926a23   Dave Jiang   acpi/nfit, libnvd...
23
24
25
26
27
28
29
30
31
32
33
  static void *key_data(struct key *key)
  {
  	struct encrypted_key_payload *epayload = dereference_key_locked(key);
  
  	lockdep_assert_held_read(&key->sem);
  
  	return epayload->decrypted_data;
  }
  
  static void nvdimm_put_key(struct key *key)
  {
64e77c8c0   Dave Jiang   acpi/nfit, libnvd...
34
35
  	if (!key)
  		return;
4c6926a23   Dave Jiang   acpi/nfit, libnvd...
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
  	up_read(&key->sem);
  	key_put(key);
  }
  
  /*
   * Retrieve kernel key for DIMM and request from user space if
   * necessary. Returns a key held for read and must be put by
   * nvdimm_put_key() before the usage goes out of scope.
   */
  static struct key *nvdimm_request_key(struct nvdimm *nvdimm)
  {
  	struct key *key = NULL;
  	static const char NVDIMM_PREFIX[] = "nvdimm:";
  	char desc[NVDIMM_KEY_DESC_LEN + sizeof(NVDIMM_PREFIX)];
  	struct device *dev = &nvdimm->dev;
  
  	sprintf(desc, "%s%s", NVDIMM_PREFIX, nvdimm->dimm_id);
028db3e29   Linus Torvalds   Revert "Merge tag...
53
  	key = request_key(&key_type_encrypted, desc, "");
4c6926a23   Dave Jiang   acpi/nfit, libnvd...
54
55
  	if (IS_ERR(key)) {
  		if (PTR_ERR(key) == -ENOKEY)
37379cfc6   Dan Williams   libnvdimm/securit...
56
57
  			dev_dbg(dev, "request_key() found no key
  ");
4c6926a23   Dave Jiang   acpi/nfit, libnvd...
58
  		else
37379cfc6   Dan Williams   libnvdimm/securit...
59
60
  			dev_dbg(dev, "request_key() upcall failed
  ");
4c6926a23   Dave Jiang   acpi/nfit, libnvd...
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
  		key = NULL;
  	} else {
  		struct encrypted_key_payload *epayload;
  
  		down_read(&key->sem);
  		epayload = dereference_key_locked(key);
  		if (epayload->decrypted_datalen != NVDIMM_PASSPHRASE_LEN) {
  			up_read(&key->sem);
  			key_put(key);
  			key = NULL;
  		}
  	}
  
  	return key;
  }
d2e5b6436   Dave Jiang   libnvdimm/securit...
76
77
78
79
80
81
82
83
84
  static const void *nvdimm_get_key_payload(struct nvdimm *nvdimm,
  		struct key **key)
  {
  	*key = nvdimm_request_key(nvdimm);
  	if (!*key)
  		return zero_key;
  
  	return key_data(*key);
  }
03b65b22a   Dave Jiang   acpi/nfit, libnvd...
85
  static struct key *nvdimm_lookup_user_key(struct nvdimm *nvdimm,
d2a4ac73f   Dave Jiang   acpi/nfit, libnvd...
86
  		key_serial_t id, int subclass)
03b65b22a   Dave Jiang   acpi/nfit, libnvd...
87
88
89
90
91
  {
  	key_ref_t keyref;
  	struct key *key;
  	struct encrypted_key_payload *epayload;
  	struct device *dev = &nvdimm->dev;
813357fea   Dan Williams   libnvdimm/securit...
92
  	keyref = lookup_user_key(id, 0, KEY_NEED_SEARCH);
03b65b22a   Dave Jiang   acpi/nfit, libnvd...
93
94
95
96
97
98
99
100
  	if (IS_ERR(keyref))
  		return NULL;
  
  	key = key_ref_to_ptr(keyref);
  	if (key->type != &key_type_encrypted) {
  		key_put(key);
  		return NULL;
  	}
03b65b22a   Dave Jiang   acpi/nfit, libnvd...
101

d2a4ac73f   Dave Jiang   acpi/nfit, libnvd...
102
103
  	dev_dbg(dev, "%s: key found: %#x
  ", __func__, key_serial(key));
03b65b22a   Dave Jiang   acpi/nfit, libnvd...
104

d2a4ac73f   Dave Jiang   acpi/nfit, libnvd...
105
  	down_read_nested(&key->sem, subclass);
03b65b22a   Dave Jiang   acpi/nfit, libnvd...
106
107
108
109
110
111
112
113
  	epayload = dereference_key_locked(key);
  	if (epayload->decrypted_datalen != NVDIMM_PASSPHRASE_LEN) {
  		up_read(&key->sem);
  		key_put(key);
  		key = NULL;
  	}
  	return key;
  }
d2e5b6436   Dave Jiang   libnvdimm/securit...
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
  static const void *nvdimm_get_user_key_payload(struct nvdimm *nvdimm,
  		key_serial_t id, int subclass, struct key **key)
  {
  	*key = NULL;
  	if (id == 0) {
  		if (subclass == NVDIMM_BASE_KEY)
  			return zero_key;
  		else
  			return NULL;
  	}
  
  	*key = nvdimm_lookup_user_key(nvdimm, id, subclass);
  	if (!*key)
  		return NULL;
  
  	return key_data(*key);
  }
  
  
  static int nvdimm_key_revalidate(struct nvdimm *nvdimm)
4c6926a23   Dave Jiang   acpi/nfit, libnvd...
134
135
136
  {
  	struct key *key;
  	int rc;
d2e5b6436   Dave Jiang   libnvdimm/securit...
137
  	const void *data;
4c6926a23   Dave Jiang   acpi/nfit, libnvd...
138
139
  
  	if (!nvdimm->sec.ops->change_key)
d2e5b6436   Dave Jiang   libnvdimm/securit...
140
  		return -EOPNOTSUPP;
4c6926a23   Dave Jiang   acpi/nfit, libnvd...
141

d2e5b6436   Dave Jiang   libnvdimm/securit...
142
  	data = nvdimm_get_key_payload(nvdimm, &key);
4c6926a23   Dave Jiang   acpi/nfit, libnvd...
143
144
145
146
147
  
  	/*
  	 * Send the same key to the hardware as new and old key to
  	 * verify that the key is good.
  	 */
d2e5b6436   Dave Jiang   libnvdimm/securit...
148
  	rc = nvdimm->sec.ops->change_key(nvdimm, data, data, NVDIMM_USER);
4c6926a23   Dave Jiang   acpi/nfit, libnvd...
149
150
  	if (rc < 0) {
  		nvdimm_put_key(key);
d2e5b6436   Dave Jiang   libnvdimm/securit...
151
  		return rc;
4c6926a23   Dave Jiang   acpi/nfit, libnvd...
152
  	}
d2e5b6436   Dave Jiang   libnvdimm/securit...
153
154
  
  	nvdimm_put_key(key);
d78c620a2   Dan Williams   libnvdimm/securit...
155
  	nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
d2e5b6436   Dave Jiang   libnvdimm/securit...
156
  	return 0;
4c6926a23   Dave Jiang   acpi/nfit, libnvd...
157
158
159
160
161
162
  }
  
  static int __nvdimm_security_unlock(struct nvdimm *nvdimm)
  {
  	struct device *dev = &nvdimm->dev;
  	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
d2e5b6436   Dave Jiang   libnvdimm/securit...
163
164
  	struct key *key;
  	const void *data;
4c6926a23   Dave Jiang   acpi/nfit, libnvd...
165
166
167
168
169
170
  	int rc;
  
  	/* The bus lock should be held at the top level of the call stack */
  	lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
  
  	if (!nvdimm->sec.ops || !nvdimm->sec.ops->unlock
d78c620a2   Dan Williams   libnvdimm/securit...
171
  			|| !nvdimm->sec.flags)
4c6926a23   Dave Jiang   acpi/nfit, libnvd...
172
  		return -EIO;
674f31a35   Dave Jiang   libnvdimm: preven...
173
174
175
  	/* No need to go further if security is disabled */
  	if (test_bit(NVDIMM_SECURITY_DISABLED, &nvdimm->sec.flags))
  		return 0;
7d988097c   Dave Jiang   acpi/nfit, libnvd...
176
  	if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
37379cfc6   Dan Williams   libnvdimm/securit...
177
178
  		dev_dbg(dev, "Security operation in progress.
  ");
7d988097c   Dave Jiang   acpi/nfit, libnvd...
179
180
  		return -EBUSY;
  	}
4c6926a23   Dave Jiang   acpi/nfit, libnvd...
181
182
183
184
185
186
187
  	/*
  	 * If the pre-OS has unlocked the DIMM, attempt to send the key
  	 * from request_key() to the hardware for verification.  Failure
  	 * to revalidate the key against the hardware results in a
  	 * freeze of the security configuration. I.e. if the OS does not
  	 * have the key, security is being managed pre-OS.
  	 */
d78c620a2   Dan Williams   libnvdimm/securit...
188
  	if (test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.flags)) {
4c6926a23   Dave Jiang   acpi/nfit, libnvd...
189
190
  		if (!key_revalidate)
  			return 0;
d2e5b6436   Dave Jiang   libnvdimm/securit...
191
  		return nvdimm_key_revalidate(nvdimm);
4c6926a23   Dave Jiang   acpi/nfit, libnvd...
192
  	} else
d2e5b6436   Dave Jiang   libnvdimm/securit...
193
  		data = nvdimm_get_key_payload(nvdimm, &key);
4c6926a23   Dave Jiang   acpi/nfit, libnvd...
194

d2e5b6436   Dave Jiang   libnvdimm/securit...
195
  	rc = nvdimm->sec.ops->unlock(nvdimm, data);
4c6926a23   Dave Jiang   acpi/nfit, libnvd...
196
197
198
199
200
  	dev_dbg(dev, "key: %d unlock: %s
  ", key_serial(key),
  			rc == 0 ? "success" : "fail");
  
  	nvdimm_put_key(key);
d78c620a2   Dan Williams   libnvdimm/securit...
201
  	nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
4c6926a23   Dave Jiang   acpi/nfit, libnvd...
202
203
204
205
206
207
208
209
210
211
212
213
214
  	return rc;
  }
  
  int nvdimm_security_unlock(struct device *dev)
  {
  	struct nvdimm *nvdimm = to_nvdimm(dev);
  	int rc;
  
  	nvdimm_bus_lock(dev);
  	rc = __nvdimm_security_unlock(nvdimm);
  	nvdimm_bus_unlock(dev);
  	return rc;
  }
03b65b22a   Dave Jiang   acpi/nfit, libnvd...
215

d78c620a2   Dan Williams   libnvdimm/securit...
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
  static int check_security_state(struct nvdimm *nvdimm)
  {
  	struct device *dev = &nvdimm->dev;
  
  	if (test_bit(NVDIMM_SECURITY_FROZEN, &nvdimm->sec.flags)) {
  		dev_dbg(dev, "Incorrect security state: %#lx
  ",
  				nvdimm->sec.flags);
  		return -EIO;
  	}
  
  	if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
  		dev_dbg(dev, "Security operation in progress.
  ");
  		return -EBUSY;
  	}
  
  	return 0;
  }
7b60422cb   Dan Williams   libnvdimm/securit...
235
  static int security_disable(struct nvdimm *nvdimm, unsigned int keyid)
03b65b22a   Dave Jiang   acpi/nfit, libnvd...
236
237
238
239
240
  {
  	struct device *dev = &nvdimm->dev;
  	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
  	struct key *key;
  	int rc;
d2e5b6436   Dave Jiang   libnvdimm/securit...
241
  	const void *data;
03b65b22a   Dave Jiang   acpi/nfit, libnvd...
242
243
244
245
246
  
  	/* The bus lock should be held at the top level of the call stack */
  	lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
  
  	if (!nvdimm->sec.ops || !nvdimm->sec.ops->disable
d78c620a2   Dan Williams   libnvdimm/securit...
247
  			|| !nvdimm->sec.flags)
03b65b22a   Dave Jiang   acpi/nfit, libnvd...
248
  		return -EOPNOTSUPP;
d78c620a2   Dan Williams   libnvdimm/securit...
249
250
251
  	rc = check_security_state(nvdimm);
  	if (rc)
  		return rc;
7d988097c   Dave Jiang   acpi/nfit, libnvd...
252

d2e5b6436   Dave Jiang   libnvdimm/securit...
253
254
255
  	data = nvdimm_get_user_key_payload(nvdimm, keyid,
  			NVDIMM_BASE_KEY, &key);
  	if (!data)
03b65b22a   Dave Jiang   acpi/nfit, libnvd...
256
  		return -ENOKEY;
d2e5b6436   Dave Jiang   libnvdimm/securit...
257
  	rc = nvdimm->sec.ops->disable(nvdimm, data);
03b65b22a   Dave Jiang   acpi/nfit, libnvd...
258
259
260
261
262
  	dev_dbg(dev, "key: %d disable: %s
  ", key_serial(key),
  			rc == 0 ? "success" : "fail");
  
  	nvdimm_put_key(key);
d78c620a2   Dan Williams   libnvdimm/securit...
263
  	nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
03b65b22a   Dave Jiang   acpi/nfit, libnvd...
264
265
  	return rc;
  }
d2a4ac73f   Dave Jiang   acpi/nfit, libnvd...
266

7b60422cb   Dan Williams   libnvdimm/securit...
267
  static int security_update(struct nvdimm *nvdimm, unsigned int keyid,
89fa9d8ea   Dave Jiang   acpi/nfit, libnvd...
268
269
  		unsigned int new_keyid,
  		enum nvdimm_passphrase_type pass_type)
d2a4ac73f   Dave Jiang   acpi/nfit, libnvd...
270
271
272
273
274
  {
  	struct device *dev = &nvdimm->dev;
  	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
  	struct key *key, *newkey;
  	int rc;
d2e5b6436   Dave Jiang   libnvdimm/securit...
275
  	const void *data, *newdata;
d2a4ac73f   Dave Jiang   acpi/nfit, libnvd...
276
277
278
279
280
  
  	/* The bus lock should be held at the top level of the call stack */
  	lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
  
  	if (!nvdimm->sec.ops || !nvdimm->sec.ops->change_key
d78c620a2   Dan Williams   libnvdimm/securit...
281
  			|| !nvdimm->sec.flags)
d2a4ac73f   Dave Jiang   acpi/nfit, libnvd...
282
  		return -EOPNOTSUPP;
d78c620a2   Dan Williams   libnvdimm/securit...
283
284
285
  	rc = check_security_state(nvdimm);
  	if (rc)
  		return rc;
d2a4ac73f   Dave Jiang   acpi/nfit, libnvd...
286

d2e5b6436   Dave Jiang   libnvdimm/securit...
287
288
289
290
  	data = nvdimm_get_user_key_payload(nvdimm, keyid,
  			NVDIMM_BASE_KEY, &key);
  	if (!data)
  		return -ENOKEY;
d2a4ac73f   Dave Jiang   acpi/nfit, libnvd...
291

d2e5b6436   Dave Jiang   libnvdimm/securit...
292
293
294
  	newdata = nvdimm_get_user_key_payload(nvdimm, new_keyid,
  			NVDIMM_NEW_KEY, &newkey);
  	if (!newdata) {
d2a4ac73f   Dave Jiang   acpi/nfit, libnvd...
295
296
297
  		nvdimm_put_key(key);
  		return -ENOKEY;
  	}
d2e5b6436   Dave Jiang   libnvdimm/securit...
298
  	rc = nvdimm->sec.ops->change_key(nvdimm, data, newdata, pass_type);
89fa9d8ea   Dave Jiang   acpi/nfit, libnvd...
299
300
  	dev_dbg(dev, "key: %d %d update%s: %s
  ",
d2a4ac73f   Dave Jiang   acpi/nfit, libnvd...
301
  			key_serial(key), key_serial(newkey),
89fa9d8ea   Dave Jiang   acpi/nfit, libnvd...
302
  			pass_type == NVDIMM_MASTER ? "(master)" : "(user)",
d2a4ac73f   Dave Jiang   acpi/nfit, libnvd...
303
304
305
306
  			rc == 0 ? "success" : "fail");
  
  	nvdimm_put_key(newkey);
  	nvdimm_put_key(key);
89fa9d8ea   Dave Jiang   acpi/nfit, libnvd...
307
  	if (pass_type == NVDIMM_MASTER)
d78c620a2   Dan Williams   libnvdimm/securit...
308
  		nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm,
89fa9d8ea   Dave Jiang   acpi/nfit, libnvd...
309
310
  				NVDIMM_MASTER);
  	else
d78c620a2   Dan Williams   libnvdimm/securit...
311
  		nvdimm->sec.flags = nvdimm_security_flags(nvdimm,
89fa9d8ea   Dave Jiang   acpi/nfit, libnvd...
312
  				NVDIMM_USER);
d2a4ac73f   Dave Jiang   acpi/nfit, libnvd...
313
314
  	return rc;
  }
64e77c8c0   Dave Jiang   acpi/nfit, libnvd...
315

7b60422cb   Dan Williams   libnvdimm/securit...
316
  static int security_erase(struct nvdimm *nvdimm, unsigned int keyid,
89fa9d8ea   Dave Jiang   acpi/nfit, libnvd...
317
  		enum nvdimm_passphrase_type pass_type)
64e77c8c0   Dave Jiang   acpi/nfit, libnvd...
318
319
320
  {
  	struct device *dev = &nvdimm->dev;
  	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
037c8489a   Dave Jiang   libnvdimm/securit...
321
  	struct key *key = NULL;
64e77c8c0   Dave Jiang   acpi/nfit, libnvd...
322
  	int rc;
037c8489a   Dave Jiang   libnvdimm/securit...
323
  	const void *data;
64e77c8c0   Dave Jiang   acpi/nfit, libnvd...
324
325
326
327
328
  
  	/* The bus lock should be held at the top level of the call stack */
  	lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
  
  	if (!nvdimm->sec.ops || !nvdimm->sec.ops->erase
d78c620a2   Dan Williams   libnvdimm/securit...
329
  			|| !nvdimm->sec.flags)
64e77c8c0   Dave Jiang   acpi/nfit, libnvd...
330
  		return -EOPNOTSUPP;
d78c620a2   Dan Williams   libnvdimm/securit...
331
332
333
  	rc = check_security_state(nvdimm);
  	if (rc)
  		return rc;
7d988097c   Dave Jiang   acpi/nfit, libnvd...
334

d78c620a2   Dan Williams   libnvdimm/securit...
335
  	if (!test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.ext_flags)
89fa9d8ea   Dave Jiang   acpi/nfit, libnvd...
336
  			&& pass_type == NVDIMM_MASTER) {
37379cfc6   Dan Williams   libnvdimm/securit...
337
  		dev_dbg(dev,
89fa9d8ea   Dave Jiang   acpi/nfit, libnvd...
338
339
340
341
  			"Attempt to secure erase in wrong master state.
  ");
  		return -EOPNOTSUPP;
  	}
d2e5b6436   Dave Jiang   libnvdimm/securit...
342
343
344
345
  	data = nvdimm_get_user_key_payload(nvdimm, keyid,
  			NVDIMM_BASE_KEY, &key);
  	if (!data)
  		return -ENOKEY;
64e77c8c0   Dave Jiang   acpi/nfit, libnvd...
346

037c8489a   Dave Jiang   libnvdimm/securit...
347
  	rc = nvdimm->sec.ops->erase(nvdimm, data, pass_type);
89fa9d8ea   Dave Jiang   acpi/nfit, libnvd...
348
349
350
  	dev_dbg(dev, "key: %d erase%s: %s
  ", key_serial(key),
  			pass_type == NVDIMM_MASTER ? "(master)" : "(user)",
64e77c8c0   Dave Jiang   acpi/nfit, libnvd...
351
352
353
  			rc == 0 ? "success" : "fail");
  
  	nvdimm_put_key(key);
d78c620a2   Dan Williams   libnvdimm/securit...
354
  	nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
64e77c8c0   Dave Jiang   acpi/nfit, libnvd...
355
356
  	return rc;
  }
7d988097c   Dave Jiang   acpi/nfit, libnvd...
357

7b60422cb   Dan Williams   libnvdimm/securit...
358
  static int security_overwrite(struct nvdimm *nvdimm, unsigned int keyid)
7d988097c   Dave Jiang   acpi/nfit, libnvd...
359
360
361
  {
  	struct device *dev = &nvdimm->dev;
  	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
d2e5b6436   Dave Jiang   libnvdimm/securit...
362
  	struct key *key = NULL;
7d988097c   Dave Jiang   acpi/nfit, libnvd...
363
  	int rc;
d2e5b6436   Dave Jiang   libnvdimm/securit...
364
  	const void *data;
7d988097c   Dave Jiang   acpi/nfit, libnvd...
365
366
367
368
369
  
  	/* The bus lock should be held at the top level of the call stack */
  	lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
  
  	if (!nvdimm->sec.ops || !nvdimm->sec.ops->overwrite
d78c620a2   Dan Williams   libnvdimm/securit...
370
  			|| !nvdimm->sec.flags)
7d988097c   Dave Jiang   acpi/nfit, libnvd...
371
  		return -EOPNOTSUPP;
7d988097c   Dave Jiang   acpi/nfit, libnvd...
372
  	if (dev->driver == NULL) {
37379cfc6   Dan Williams   libnvdimm/securit...
373
374
  		dev_dbg(dev, "Unable to overwrite while DIMM active.
  ");
7d988097c   Dave Jiang   acpi/nfit, libnvd...
375
376
  		return -EINVAL;
  	}
d78c620a2   Dan Williams   libnvdimm/securit...
377
378
379
  	rc = check_security_state(nvdimm);
  	if (rc)
  		return rc;
7d988097c   Dave Jiang   acpi/nfit, libnvd...
380

d2e5b6436   Dave Jiang   libnvdimm/securit...
381
382
383
384
  	data = nvdimm_get_user_key_payload(nvdimm, keyid,
  			NVDIMM_BASE_KEY, &key);
  	if (!data)
  		return -ENOKEY;
7d988097c   Dave Jiang   acpi/nfit, libnvd...
385

d2e5b6436   Dave Jiang   libnvdimm/securit...
386
  	rc = nvdimm->sec.ops->overwrite(nvdimm, data);
7d988097c   Dave Jiang   acpi/nfit, libnvd...
387
388
389
390
391
392
393
394
  	dev_dbg(dev, "key: %d overwrite submission: %s
  ", key_serial(key),
  			rc == 0 ? "success" : "fail");
  
  	nvdimm_put_key(key);
  	if (rc == 0) {
  		set_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags);
  		set_bit(NDD_WORK_PENDING, &nvdimm->flags);
d78c620a2   Dan Williams   libnvdimm/securit...
395
  		set_bit(NVDIMM_SECURITY_OVERWRITE, &nvdimm->sec.flags);
7d988097c   Dave Jiang   acpi/nfit, libnvd...
396
397
398
399
400
401
402
  		/*
  		 * Make sure we don't lose device while doing overwrite
  		 * query.
  		 */
  		get_device(dev);
  		queue_delayed_work(system_wq, &nvdimm->dwork, 0);
  	}
89fa9d8ea   Dave Jiang   acpi/nfit, libnvd...
403

7d988097c   Dave Jiang   acpi/nfit, libnvd...
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
  	return rc;
  }
  
  void __nvdimm_security_overwrite_query(struct nvdimm *nvdimm)
  {
  	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(&nvdimm->dev);
  	int rc;
  	unsigned int tmo;
  
  	/* The bus lock should be held at the top level of the call stack */
  	lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
  
  	/*
  	 * Abort and release device if we no longer have the overwrite
  	 * flag set. It means the work has been canceled.
  	 */
  	if (!test_bit(NDD_WORK_PENDING, &nvdimm->flags))
  		return;
  
  	tmo = nvdimm->sec.overwrite_tmo;
  
  	if (!nvdimm->sec.ops || !nvdimm->sec.ops->query_overwrite
d78c620a2   Dan Williams   libnvdimm/securit...
426
  			|| !nvdimm->sec.flags)
7d988097c   Dave Jiang   acpi/nfit, libnvd...
427
428
429
430
431
432
433
434
435
436
437
438
439
  		return;
  
  	rc = nvdimm->sec.ops->query_overwrite(nvdimm);
  	if (rc == -EBUSY) {
  
  		/* setup delayed work again */
  		tmo += 10;
  		queue_delayed_work(system_wq, &nvdimm->dwork, tmo * HZ);
  		nvdimm->sec.overwrite_tmo = min(15U * 60U, tmo);
  		return;
  	}
  
  	if (rc < 0)
37379cfc6   Dan Williams   libnvdimm/securit...
440
441
  		dev_dbg(&nvdimm->dev, "overwrite failed
  ");
7d988097c   Dave Jiang   acpi/nfit, libnvd...
442
443
444
  	else
  		dev_dbg(&nvdimm->dev, "overwrite completed
  ");
7f674025d   Jane Chu   libnvdimm/securit...
445
446
447
448
449
  	/*
  	 * Mark the overwrite work done and update dimm security flags,
  	 * then send a sysfs event notification to wake up userspace
  	 * poll threads to picked up the changed state.
  	 */
7d988097c   Dave Jiang   acpi/nfit, libnvd...
450
451
452
  	nvdimm->sec.overwrite_tmo = 0;
  	clear_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags);
  	clear_bit(NDD_WORK_PENDING, &nvdimm->flags);
d78c620a2   Dan Williams   libnvdimm/securit...
453
  	nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
dad42d175   Jane Chu   libnvdimm/securit...
454
  	nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm, NVDIMM_MASTER);
7f674025d   Jane Chu   libnvdimm/securit...
455
456
457
  	if (nvdimm->sec.overwrite_state)
  		sysfs_notify_dirent(nvdimm->sec.overwrite_state);
  	put_device(&nvdimm->dev);
7d988097c   Dave Jiang   acpi/nfit, libnvd...
458
459
460
461
462
463
464
465
466
467
468
  }
  
  void nvdimm_security_overwrite_query(struct work_struct *work)
  {
  	struct nvdimm *nvdimm =
  		container_of(work, typeof(*nvdimm), dwork.work);
  
  	nvdimm_bus_lock(&nvdimm->dev);
  	__nvdimm_security_overwrite_query(nvdimm);
  	nvdimm_bus_unlock(&nvdimm->dev);
  }
7b60422cb   Dan Williams   libnvdimm/securit...
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
  
  #define OPS							\
  	C( OP_FREEZE,		"freeze",		1),	\
  	C( OP_DISABLE,		"disable",		2),	\
  	C( OP_UPDATE,		"update",		3),	\
  	C( OP_ERASE,		"erase",		2),	\
  	C( OP_OVERWRITE,	"overwrite",		2),	\
  	C( OP_MASTER_UPDATE,	"master_update",	3),	\
  	C( OP_MASTER_ERASE,	"master_erase",		2)
  #undef C
  #define C(a, b, c) a
  enum nvdimmsec_op_ids { OPS };
  #undef C
  #define C(a, b, c) { b, c }
  static struct {
  	const char *name;
  	int args;
  } ops[] = { OPS };
  #undef C
  
  #define SEC_CMD_SIZE 32
  #define KEY_ID_SIZE 10
  
  ssize_t nvdimm_security_store(struct device *dev, const char *buf, size_t len)
  {
  	struct nvdimm *nvdimm = to_nvdimm(dev);
  	ssize_t rc;
  	char cmd[SEC_CMD_SIZE+1], keystr[KEY_ID_SIZE+1],
  		nkeystr[KEY_ID_SIZE+1];
  	unsigned int key, newkey;
  	int i;
  
  	rc = sscanf(buf, "%"__stringify(SEC_CMD_SIZE)"s"
  			" %"__stringify(KEY_ID_SIZE)"s"
  			" %"__stringify(KEY_ID_SIZE)"s",
  			cmd, keystr, nkeystr);
  	if (rc < 1)
  		return -EINVAL;
  	for (i = 0; i < ARRAY_SIZE(ops); i++)
  		if (sysfs_streq(cmd, ops[i].name))
  			break;
  	if (i >= ARRAY_SIZE(ops))
  		return -EINVAL;
  	if (ops[i].args > 1)
  		rc = kstrtouint(keystr, 0, &key);
  	if (rc >= 0 && ops[i].args > 2)
  		rc = kstrtouint(nkeystr, 0, &newkey);
  	if (rc < 0)
  		return rc;
  
  	if (i == OP_FREEZE) {
  		dev_dbg(dev, "freeze
  ");
  		rc = nvdimm_security_freeze(nvdimm);
  	} else if (i == OP_DISABLE) {
  		dev_dbg(dev, "disable %u
  ", key);
  		rc = security_disable(nvdimm, key);
  	} else if (i == OP_UPDATE || i == OP_MASTER_UPDATE) {
  		dev_dbg(dev, "%s %u %u
  ", ops[i].name, key, newkey);
  		rc = security_update(nvdimm, key, newkey, i == OP_UPDATE
  				? NVDIMM_USER : NVDIMM_MASTER);
  	} else if (i == OP_ERASE || i == OP_MASTER_ERASE) {
  		dev_dbg(dev, "%s %u
  ", ops[i].name, key);
  		if (atomic_read(&nvdimm->busy)) {
  			dev_dbg(dev, "Unable to secure erase while DIMM active.
  ");
  			return -EBUSY;
  		}
  		rc = security_erase(nvdimm, key, i == OP_ERASE
  				? NVDIMM_USER : NVDIMM_MASTER);
  	} else if (i == OP_OVERWRITE) {
  		dev_dbg(dev, "overwrite %u
  ", key);
  		if (atomic_read(&nvdimm->busy)) {
  			dev_dbg(dev, "Unable to overwrite while DIMM active.
  ");
  			return -EBUSY;
  		}
  		rc = security_overwrite(nvdimm, key);
  	} else
  		return -EINVAL;
  
  	if (rc == 0)
  		rc = len;
  	return rc;
  }