18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright(c) 2018 Intel Corporation. All rights reserved. */
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/module.h>
58c2ecf20Sopenharmony_ci#include <linux/device.h>
68c2ecf20Sopenharmony_ci#include <linux/ndctl.h>
78c2ecf20Sopenharmony_ci#include <linux/slab.h>
88c2ecf20Sopenharmony_ci#include <linux/io.h>
98c2ecf20Sopenharmony_ci#include <linux/mm.h>
108c2ecf20Sopenharmony_ci#include <linux/cred.h>
118c2ecf20Sopenharmony_ci#include <linux/key.h>
128c2ecf20Sopenharmony_ci#include <linux/key-type.h>
138c2ecf20Sopenharmony_ci#include <keys/user-type.h>
148c2ecf20Sopenharmony_ci#include <keys/encrypted-type.h>
158c2ecf20Sopenharmony_ci#include "nd-core.h"
168c2ecf20Sopenharmony_ci#include "nd.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define NVDIMM_BASE_KEY		0
198c2ecf20Sopenharmony_ci#define NVDIMM_NEW_KEY		1
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic bool key_revalidate = true;
228c2ecf20Sopenharmony_cimodule_param(key_revalidate, bool, 0444);
238c2ecf20Sopenharmony_ciMODULE_PARM_DESC(key_revalidate, "Require key validation at init.");
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic const char zero_key[NVDIMM_PASSPHRASE_LEN];
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic void *key_data(struct key *key)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	struct encrypted_key_payload *epayload = dereference_key_locked(key);
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	lockdep_assert_held_read(&key->sem);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	return epayload->decrypted_data;
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic void nvdimm_put_key(struct key *key)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	if (!key)
398c2ecf20Sopenharmony_ci		return;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	up_read(&key->sem);
428c2ecf20Sopenharmony_ci	key_put(key);
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/*
468c2ecf20Sopenharmony_ci * Retrieve kernel key for DIMM and request from user space if
478c2ecf20Sopenharmony_ci * necessary. Returns a key held for read and must be put by
488c2ecf20Sopenharmony_ci * nvdimm_put_key() before the usage goes out of scope.
498c2ecf20Sopenharmony_ci */
508c2ecf20Sopenharmony_cistatic struct key *nvdimm_request_key(struct nvdimm *nvdimm)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	struct key *key = NULL;
538c2ecf20Sopenharmony_ci	static const char NVDIMM_PREFIX[] = "nvdimm:";
548c2ecf20Sopenharmony_ci	char desc[NVDIMM_KEY_DESC_LEN + sizeof(NVDIMM_PREFIX)];
558c2ecf20Sopenharmony_ci	struct device *dev = &nvdimm->dev;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	sprintf(desc, "%s%s", NVDIMM_PREFIX, nvdimm->dimm_id);
588c2ecf20Sopenharmony_ci	key = request_key(&key_type_encrypted, desc, "");
598c2ecf20Sopenharmony_ci	if (IS_ERR(key)) {
608c2ecf20Sopenharmony_ci		if (PTR_ERR(key) == -ENOKEY)
618c2ecf20Sopenharmony_ci			dev_dbg(dev, "request_key() found no key\n");
628c2ecf20Sopenharmony_ci		else
638c2ecf20Sopenharmony_ci			dev_dbg(dev, "request_key() upcall failed\n");
648c2ecf20Sopenharmony_ci		key = NULL;
658c2ecf20Sopenharmony_ci	} else {
668c2ecf20Sopenharmony_ci		struct encrypted_key_payload *epayload;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci		down_read(&key->sem);
698c2ecf20Sopenharmony_ci		epayload = dereference_key_locked(key);
708c2ecf20Sopenharmony_ci		if (epayload->decrypted_datalen != NVDIMM_PASSPHRASE_LEN) {
718c2ecf20Sopenharmony_ci			up_read(&key->sem);
728c2ecf20Sopenharmony_ci			key_put(key);
738c2ecf20Sopenharmony_ci			key = NULL;
748c2ecf20Sopenharmony_ci		}
758c2ecf20Sopenharmony_ci	}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	return key;
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic const void *nvdimm_get_key_payload(struct nvdimm *nvdimm,
818c2ecf20Sopenharmony_ci		struct key **key)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	*key = nvdimm_request_key(nvdimm);
848c2ecf20Sopenharmony_ci	if (!*key)
858c2ecf20Sopenharmony_ci		return zero_key;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	return key_data(*key);
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic struct key *nvdimm_lookup_user_key(struct nvdimm *nvdimm,
918c2ecf20Sopenharmony_ci		key_serial_t id, int subclass)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	key_ref_t keyref;
948c2ecf20Sopenharmony_ci	struct key *key;
958c2ecf20Sopenharmony_ci	struct encrypted_key_payload *epayload;
968c2ecf20Sopenharmony_ci	struct device *dev = &nvdimm->dev;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	keyref = lookup_user_key(id, 0, KEY_NEED_SEARCH);
998c2ecf20Sopenharmony_ci	if (IS_ERR(keyref))
1008c2ecf20Sopenharmony_ci		return NULL;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	key = key_ref_to_ptr(keyref);
1038c2ecf20Sopenharmony_ci	if (key->type != &key_type_encrypted) {
1048c2ecf20Sopenharmony_ci		key_put(key);
1058c2ecf20Sopenharmony_ci		return NULL;
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s: key found: %#x\n", __func__, key_serial(key));
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	down_read_nested(&key->sem, subclass);
1118c2ecf20Sopenharmony_ci	epayload = dereference_key_locked(key);
1128c2ecf20Sopenharmony_ci	if (epayload->decrypted_datalen != NVDIMM_PASSPHRASE_LEN) {
1138c2ecf20Sopenharmony_ci		up_read(&key->sem);
1148c2ecf20Sopenharmony_ci		key_put(key);
1158c2ecf20Sopenharmony_ci		key = NULL;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci	return key;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic const void *nvdimm_get_user_key_payload(struct nvdimm *nvdimm,
1218c2ecf20Sopenharmony_ci		key_serial_t id, int subclass, struct key **key)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	*key = NULL;
1248c2ecf20Sopenharmony_ci	if (id == 0) {
1258c2ecf20Sopenharmony_ci		if (subclass == NVDIMM_BASE_KEY)
1268c2ecf20Sopenharmony_ci			return zero_key;
1278c2ecf20Sopenharmony_ci		else
1288c2ecf20Sopenharmony_ci			return NULL;
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	*key = nvdimm_lookup_user_key(nvdimm, id, subclass);
1328c2ecf20Sopenharmony_ci	if (!*key)
1338c2ecf20Sopenharmony_ci		return NULL;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	return key_data(*key);
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic int nvdimm_key_revalidate(struct nvdimm *nvdimm)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	struct key *key;
1428c2ecf20Sopenharmony_ci	int rc;
1438c2ecf20Sopenharmony_ci	const void *data;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	if (!nvdimm->sec.ops->change_key)
1468c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	data = nvdimm_get_key_payload(nvdimm, &key);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	/*
1518c2ecf20Sopenharmony_ci	 * Send the same key to the hardware as new and old key to
1528c2ecf20Sopenharmony_ci	 * verify that the key is good.
1538c2ecf20Sopenharmony_ci	 */
1548c2ecf20Sopenharmony_ci	rc = nvdimm->sec.ops->change_key(nvdimm, data, data, NVDIMM_USER);
1558c2ecf20Sopenharmony_ci	if (rc < 0) {
1568c2ecf20Sopenharmony_ci		nvdimm_put_key(key);
1578c2ecf20Sopenharmony_ci		return rc;
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	nvdimm_put_key(key);
1618c2ecf20Sopenharmony_ci	nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
1628c2ecf20Sopenharmony_ci	return 0;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic int __nvdimm_security_unlock(struct nvdimm *nvdimm)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	struct device *dev = &nvdimm->dev;
1688c2ecf20Sopenharmony_ci	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
1698c2ecf20Sopenharmony_ci	struct key *key;
1708c2ecf20Sopenharmony_ci	const void *data;
1718c2ecf20Sopenharmony_ci	int rc;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	/* The bus lock should be held at the top level of the call stack */
1748c2ecf20Sopenharmony_ci	lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	if (!nvdimm->sec.ops || !nvdimm->sec.ops->unlock
1778c2ecf20Sopenharmony_ci			|| !nvdimm->sec.flags)
1788c2ecf20Sopenharmony_ci		return -EIO;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	/* No need to go further if security is disabled */
1818c2ecf20Sopenharmony_ci	if (test_bit(NVDIMM_SECURITY_DISABLED, &nvdimm->sec.flags))
1828c2ecf20Sopenharmony_ci		return 0;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
1858c2ecf20Sopenharmony_ci		dev_dbg(dev, "Security operation in progress.\n");
1868c2ecf20Sopenharmony_ci		return -EBUSY;
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	/*
1908c2ecf20Sopenharmony_ci	 * If the pre-OS has unlocked the DIMM, attempt to send the key
1918c2ecf20Sopenharmony_ci	 * from request_key() to the hardware for verification.  Failure
1928c2ecf20Sopenharmony_ci	 * to revalidate the key against the hardware results in a
1938c2ecf20Sopenharmony_ci	 * freeze of the security configuration. I.e. if the OS does not
1948c2ecf20Sopenharmony_ci	 * have the key, security is being managed pre-OS.
1958c2ecf20Sopenharmony_ci	 */
1968c2ecf20Sopenharmony_ci	if (test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.flags)) {
1978c2ecf20Sopenharmony_ci		if (!key_revalidate)
1988c2ecf20Sopenharmony_ci			return 0;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci		return nvdimm_key_revalidate(nvdimm);
2018c2ecf20Sopenharmony_ci	} else
2028c2ecf20Sopenharmony_ci		data = nvdimm_get_key_payload(nvdimm, &key);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	rc = nvdimm->sec.ops->unlock(nvdimm, data);
2058c2ecf20Sopenharmony_ci	dev_dbg(dev, "key: %d unlock: %s\n", key_serial(key),
2068c2ecf20Sopenharmony_ci			rc == 0 ? "success" : "fail");
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	nvdimm_put_key(key);
2098c2ecf20Sopenharmony_ci	nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
2108c2ecf20Sopenharmony_ci	return rc;
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ciint nvdimm_security_unlock(struct device *dev)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	struct nvdimm *nvdimm = to_nvdimm(dev);
2168c2ecf20Sopenharmony_ci	int rc;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	nvdimm_bus_lock(dev);
2198c2ecf20Sopenharmony_ci	rc = __nvdimm_security_unlock(nvdimm);
2208c2ecf20Sopenharmony_ci	nvdimm_bus_unlock(dev);
2218c2ecf20Sopenharmony_ci	return rc;
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic int check_security_state(struct nvdimm *nvdimm)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	struct device *dev = &nvdimm->dev;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	if (test_bit(NVDIMM_SECURITY_FROZEN, &nvdimm->sec.flags)) {
2298c2ecf20Sopenharmony_ci		dev_dbg(dev, "Incorrect security state: %#lx\n",
2308c2ecf20Sopenharmony_ci				nvdimm->sec.flags);
2318c2ecf20Sopenharmony_ci		return -EIO;
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
2358c2ecf20Sopenharmony_ci		dev_dbg(dev, "Security operation in progress.\n");
2368c2ecf20Sopenharmony_ci		return -EBUSY;
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	return 0;
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic int security_disable(struct nvdimm *nvdimm, unsigned int keyid)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	struct device *dev = &nvdimm->dev;
2458c2ecf20Sopenharmony_ci	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
2468c2ecf20Sopenharmony_ci	struct key *key;
2478c2ecf20Sopenharmony_ci	int rc;
2488c2ecf20Sopenharmony_ci	const void *data;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	/* The bus lock should be held at the top level of the call stack */
2518c2ecf20Sopenharmony_ci	lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	if (!nvdimm->sec.ops || !nvdimm->sec.ops->disable
2548c2ecf20Sopenharmony_ci			|| !nvdimm->sec.flags)
2558c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	rc = check_security_state(nvdimm);
2588c2ecf20Sopenharmony_ci	if (rc)
2598c2ecf20Sopenharmony_ci		return rc;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	data = nvdimm_get_user_key_payload(nvdimm, keyid,
2628c2ecf20Sopenharmony_ci			NVDIMM_BASE_KEY, &key);
2638c2ecf20Sopenharmony_ci	if (!data)
2648c2ecf20Sopenharmony_ci		return -ENOKEY;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	rc = nvdimm->sec.ops->disable(nvdimm, data);
2678c2ecf20Sopenharmony_ci	dev_dbg(dev, "key: %d disable: %s\n", key_serial(key),
2688c2ecf20Sopenharmony_ci			rc == 0 ? "success" : "fail");
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	nvdimm_put_key(key);
2718c2ecf20Sopenharmony_ci	nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
2728c2ecf20Sopenharmony_ci	return rc;
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cistatic int security_update(struct nvdimm *nvdimm, unsigned int keyid,
2768c2ecf20Sopenharmony_ci		unsigned int new_keyid,
2778c2ecf20Sopenharmony_ci		enum nvdimm_passphrase_type pass_type)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	struct device *dev = &nvdimm->dev;
2808c2ecf20Sopenharmony_ci	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
2818c2ecf20Sopenharmony_ci	struct key *key, *newkey;
2828c2ecf20Sopenharmony_ci	int rc;
2838c2ecf20Sopenharmony_ci	const void *data, *newdata;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	/* The bus lock should be held at the top level of the call stack */
2868c2ecf20Sopenharmony_ci	lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	if (!nvdimm->sec.ops || !nvdimm->sec.ops->change_key
2898c2ecf20Sopenharmony_ci			|| !nvdimm->sec.flags)
2908c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	rc = check_security_state(nvdimm);
2938c2ecf20Sopenharmony_ci	if (rc)
2948c2ecf20Sopenharmony_ci		return rc;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	data = nvdimm_get_user_key_payload(nvdimm, keyid,
2978c2ecf20Sopenharmony_ci			NVDIMM_BASE_KEY, &key);
2988c2ecf20Sopenharmony_ci	if (!data)
2998c2ecf20Sopenharmony_ci		return -ENOKEY;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	newdata = nvdimm_get_user_key_payload(nvdimm, new_keyid,
3028c2ecf20Sopenharmony_ci			NVDIMM_NEW_KEY, &newkey);
3038c2ecf20Sopenharmony_ci	if (!newdata) {
3048c2ecf20Sopenharmony_ci		nvdimm_put_key(key);
3058c2ecf20Sopenharmony_ci		return -ENOKEY;
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	rc = nvdimm->sec.ops->change_key(nvdimm, data, newdata, pass_type);
3098c2ecf20Sopenharmony_ci	dev_dbg(dev, "key: %d %d update%s: %s\n",
3108c2ecf20Sopenharmony_ci			key_serial(key), key_serial(newkey),
3118c2ecf20Sopenharmony_ci			pass_type == NVDIMM_MASTER ? "(master)" : "(user)",
3128c2ecf20Sopenharmony_ci			rc == 0 ? "success" : "fail");
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	nvdimm_put_key(newkey);
3158c2ecf20Sopenharmony_ci	nvdimm_put_key(key);
3168c2ecf20Sopenharmony_ci	if (pass_type == NVDIMM_MASTER)
3178c2ecf20Sopenharmony_ci		nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm,
3188c2ecf20Sopenharmony_ci				NVDIMM_MASTER);
3198c2ecf20Sopenharmony_ci	else
3208c2ecf20Sopenharmony_ci		nvdimm->sec.flags = nvdimm_security_flags(nvdimm,
3218c2ecf20Sopenharmony_ci				NVDIMM_USER);
3228c2ecf20Sopenharmony_ci	return rc;
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic int security_erase(struct nvdimm *nvdimm, unsigned int keyid,
3268c2ecf20Sopenharmony_ci		enum nvdimm_passphrase_type pass_type)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	struct device *dev = &nvdimm->dev;
3298c2ecf20Sopenharmony_ci	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
3308c2ecf20Sopenharmony_ci	struct key *key = NULL;
3318c2ecf20Sopenharmony_ci	int rc;
3328c2ecf20Sopenharmony_ci	const void *data;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	/* The bus lock should be held at the top level of the call stack */
3358c2ecf20Sopenharmony_ci	lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	if (!nvdimm->sec.ops || !nvdimm->sec.ops->erase
3388c2ecf20Sopenharmony_ci			|| !nvdimm->sec.flags)
3398c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	rc = check_security_state(nvdimm);
3428c2ecf20Sopenharmony_ci	if (rc)
3438c2ecf20Sopenharmony_ci		return rc;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	if (!test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.ext_flags)
3468c2ecf20Sopenharmony_ci			&& pass_type == NVDIMM_MASTER) {
3478c2ecf20Sopenharmony_ci		dev_dbg(dev,
3488c2ecf20Sopenharmony_ci			"Attempt to secure erase in wrong master state.\n");
3498c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	data = nvdimm_get_user_key_payload(nvdimm, keyid,
3538c2ecf20Sopenharmony_ci			NVDIMM_BASE_KEY, &key);
3548c2ecf20Sopenharmony_ci	if (!data)
3558c2ecf20Sopenharmony_ci		return -ENOKEY;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	rc = nvdimm->sec.ops->erase(nvdimm, data, pass_type);
3588c2ecf20Sopenharmony_ci	dev_dbg(dev, "key: %d erase%s: %s\n", key_serial(key),
3598c2ecf20Sopenharmony_ci			pass_type == NVDIMM_MASTER ? "(master)" : "(user)",
3608c2ecf20Sopenharmony_ci			rc == 0 ? "success" : "fail");
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	nvdimm_put_key(key);
3638c2ecf20Sopenharmony_ci	nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
3648c2ecf20Sopenharmony_ci	return rc;
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic int security_overwrite(struct nvdimm *nvdimm, unsigned int keyid)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	struct device *dev = &nvdimm->dev;
3708c2ecf20Sopenharmony_ci	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
3718c2ecf20Sopenharmony_ci	struct key *key = NULL;
3728c2ecf20Sopenharmony_ci	int rc;
3738c2ecf20Sopenharmony_ci	const void *data;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	/* The bus lock should be held at the top level of the call stack */
3768c2ecf20Sopenharmony_ci	lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	if (!nvdimm->sec.ops || !nvdimm->sec.ops->overwrite
3798c2ecf20Sopenharmony_ci			|| !nvdimm->sec.flags)
3808c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	rc = check_security_state(nvdimm);
3838c2ecf20Sopenharmony_ci	if (rc)
3848c2ecf20Sopenharmony_ci		return rc;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	data = nvdimm_get_user_key_payload(nvdimm, keyid,
3878c2ecf20Sopenharmony_ci			NVDIMM_BASE_KEY, &key);
3888c2ecf20Sopenharmony_ci	if (!data)
3898c2ecf20Sopenharmony_ci		return -ENOKEY;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	rc = nvdimm->sec.ops->overwrite(nvdimm, data);
3928c2ecf20Sopenharmony_ci	dev_dbg(dev, "key: %d overwrite submission: %s\n", key_serial(key),
3938c2ecf20Sopenharmony_ci			rc == 0 ? "success" : "fail");
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	nvdimm_put_key(key);
3968c2ecf20Sopenharmony_ci	if (rc == 0) {
3978c2ecf20Sopenharmony_ci		set_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags);
3988c2ecf20Sopenharmony_ci		set_bit(NDD_WORK_PENDING, &nvdimm->flags);
3998c2ecf20Sopenharmony_ci		set_bit(NVDIMM_SECURITY_OVERWRITE, &nvdimm->sec.flags);
4008c2ecf20Sopenharmony_ci		/*
4018c2ecf20Sopenharmony_ci		 * Make sure we don't lose device while doing overwrite
4028c2ecf20Sopenharmony_ci		 * query.
4038c2ecf20Sopenharmony_ci		 */
4048c2ecf20Sopenharmony_ci		get_device(dev);
4058c2ecf20Sopenharmony_ci		queue_delayed_work(system_wq, &nvdimm->dwork, 0);
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	return rc;
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_civoid __nvdimm_security_overwrite_query(struct nvdimm *nvdimm)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(&nvdimm->dev);
4148c2ecf20Sopenharmony_ci	int rc;
4158c2ecf20Sopenharmony_ci	unsigned int tmo;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	/* The bus lock should be held at the top level of the call stack */
4188c2ecf20Sopenharmony_ci	lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	/*
4218c2ecf20Sopenharmony_ci	 * Abort and release device if we no longer have the overwrite
4228c2ecf20Sopenharmony_ci	 * flag set. It means the work has been canceled.
4238c2ecf20Sopenharmony_ci	 */
4248c2ecf20Sopenharmony_ci	if (!test_bit(NDD_WORK_PENDING, &nvdimm->flags))
4258c2ecf20Sopenharmony_ci		return;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	tmo = nvdimm->sec.overwrite_tmo;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	if (!nvdimm->sec.ops || !nvdimm->sec.ops->query_overwrite
4308c2ecf20Sopenharmony_ci			|| !nvdimm->sec.flags)
4318c2ecf20Sopenharmony_ci		return;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	rc = nvdimm->sec.ops->query_overwrite(nvdimm);
4348c2ecf20Sopenharmony_ci	if (rc == -EBUSY) {
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci		/* setup delayed work again */
4378c2ecf20Sopenharmony_ci		tmo += 10;
4388c2ecf20Sopenharmony_ci		queue_delayed_work(system_wq, &nvdimm->dwork, tmo * HZ);
4398c2ecf20Sopenharmony_ci		nvdimm->sec.overwrite_tmo = min(15U * 60U, tmo);
4408c2ecf20Sopenharmony_ci		return;
4418c2ecf20Sopenharmony_ci	}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	if (rc < 0)
4448c2ecf20Sopenharmony_ci		dev_dbg(&nvdimm->dev, "overwrite failed\n");
4458c2ecf20Sopenharmony_ci	else
4468c2ecf20Sopenharmony_ci		dev_dbg(&nvdimm->dev, "overwrite completed\n");
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	/*
4498c2ecf20Sopenharmony_ci	 * Mark the overwrite work done and update dimm security flags,
4508c2ecf20Sopenharmony_ci	 * then send a sysfs event notification to wake up userspace
4518c2ecf20Sopenharmony_ci	 * poll threads to picked up the changed state.
4528c2ecf20Sopenharmony_ci	 */
4538c2ecf20Sopenharmony_ci	nvdimm->sec.overwrite_tmo = 0;
4548c2ecf20Sopenharmony_ci	clear_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags);
4558c2ecf20Sopenharmony_ci	clear_bit(NDD_WORK_PENDING, &nvdimm->flags);
4568c2ecf20Sopenharmony_ci	nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
4578c2ecf20Sopenharmony_ci	nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm, NVDIMM_MASTER);
4588c2ecf20Sopenharmony_ci	if (nvdimm->sec.overwrite_state)
4598c2ecf20Sopenharmony_ci		sysfs_notify_dirent(nvdimm->sec.overwrite_state);
4608c2ecf20Sopenharmony_ci	put_device(&nvdimm->dev);
4618c2ecf20Sopenharmony_ci}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_civoid nvdimm_security_overwrite_query(struct work_struct *work)
4648c2ecf20Sopenharmony_ci{
4658c2ecf20Sopenharmony_ci	struct nvdimm *nvdimm =
4668c2ecf20Sopenharmony_ci		container_of(work, typeof(*nvdimm), dwork.work);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	nvdimm_bus_lock(&nvdimm->dev);
4698c2ecf20Sopenharmony_ci	__nvdimm_security_overwrite_query(nvdimm);
4708c2ecf20Sopenharmony_ci	nvdimm_bus_unlock(&nvdimm->dev);
4718c2ecf20Sopenharmony_ci}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci#define OPS							\
4748c2ecf20Sopenharmony_ci	C( OP_FREEZE,		"freeze",		1),	\
4758c2ecf20Sopenharmony_ci	C( OP_DISABLE,		"disable",		2),	\
4768c2ecf20Sopenharmony_ci	C( OP_UPDATE,		"update",		3),	\
4778c2ecf20Sopenharmony_ci	C( OP_ERASE,		"erase",		2),	\
4788c2ecf20Sopenharmony_ci	C( OP_OVERWRITE,	"overwrite",		2),	\
4798c2ecf20Sopenharmony_ci	C( OP_MASTER_UPDATE,	"master_update",	3),	\
4808c2ecf20Sopenharmony_ci	C( OP_MASTER_ERASE,	"master_erase",		2)
4818c2ecf20Sopenharmony_ci#undef C
4828c2ecf20Sopenharmony_ci#define C(a, b, c) a
4838c2ecf20Sopenharmony_cienum nvdimmsec_op_ids { OPS };
4848c2ecf20Sopenharmony_ci#undef C
4858c2ecf20Sopenharmony_ci#define C(a, b, c) { b, c }
4868c2ecf20Sopenharmony_cistatic struct {
4878c2ecf20Sopenharmony_ci	const char *name;
4888c2ecf20Sopenharmony_ci	int args;
4898c2ecf20Sopenharmony_ci} ops[] = { OPS };
4908c2ecf20Sopenharmony_ci#undef C
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci#define SEC_CMD_SIZE 32
4938c2ecf20Sopenharmony_ci#define KEY_ID_SIZE 10
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_cissize_t nvdimm_security_store(struct device *dev, const char *buf, size_t len)
4968c2ecf20Sopenharmony_ci{
4978c2ecf20Sopenharmony_ci	struct nvdimm *nvdimm = to_nvdimm(dev);
4988c2ecf20Sopenharmony_ci	ssize_t rc;
4998c2ecf20Sopenharmony_ci	char cmd[SEC_CMD_SIZE+1], keystr[KEY_ID_SIZE+1],
5008c2ecf20Sopenharmony_ci		nkeystr[KEY_ID_SIZE+1];
5018c2ecf20Sopenharmony_ci	unsigned int key, newkey;
5028c2ecf20Sopenharmony_ci	int i;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	rc = sscanf(buf, "%"__stringify(SEC_CMD_SIZE)"s"
5058c2ecf20Sopenharmony_ci			" %"__stringify(KEY_ID_SIZE)"s"
5068c2ecf20Sopenharmony_ci			" %"__stringify(KEY_ID_SIZE)"s",
5078c2ecf20Sopenharmony_ci			cmd, keystr, nkeystr);
5088c2ecf20Sopenharmony_ci	if (rc < 1)
5098c2ecf20Sopenharmony_ci		return -EINVAL;
5108c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ops); i++)
5118c2ecf20Sopenharmony_ci		if (sysfs_streq(cmd, ops[i].name))
5128c2ecf20Sopenharmony_ci			break;
5138c2ecf20Sopenharmony_ci	if (i >= ARRAY_SIZE(ops))
5148c2ecf20Sopenharmony_ci		return -EINVAL;
5158c2ecf20Sopenharmony_ci	if (ops[i].args > 1)
5168c2ecf20Sopenharmony_ci		rc = kstrtouint(keystr, 0, &key);
5178c2ecf20Sopenharmony_ci	if (rc >= 0 && ops[i].args > 2)
5188c2ecf20Sopenharmony_ci		rc = kstrtouint(nkeystr, 0, &newkey);
5198c2ecf20Sopenharmony_ci	if (rc < 0)
5208c2ecf20Sopenharmony_ci		return rc;
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	if (i == OP_FREEZE) {
5238c2ecf20Sopenharmony_ci		dev_dbg(dev, "freeze\n");
5248c2ecf20Sopenharmony_ci		rc = nvdimm_security_freeze(nvdimm);
5258c2ecf20Sopenharmony_ci	} else if (i == OP_DISABLE) {
5268c2ecf20Sopenharmony_ci		dev_dbg(dev, "disable %u\n", key);
5278c2ecf20Sopenharmony_ci		rc = security_disable(nvdimm, key);
5288c2ecf20Sopenharmony_ci	} else if (i == OP_UPDATE || i == OP_MASTER_UPDATE) {
5298c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s %u %u\n", ops[i].name, key, newkey);
5308c2ecf20Sopenharmony_ci		rc = security_update(nvdimm, key, newkey, i == OP_UPDATE
5318c2ecf20Sopenharmony_ci				? NVDIMM_USER : NVDIMM_MASTER);
5328c2ecf20Sopenharmony_ci	} else if (i == OP_ERASE || i == OP_MASTER_ERASE) {
5338c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s %u\n", ops[i].name, key);
5348c2ecf20Sopenharmony_ci		if (atomic_read(&nvdimm->busy)) {
5358c2ecf20Sopenharmony_ci			dev_dbg(dev, "Unable to secure erase while DIMM active.\n");
5368c2ecf20Sopenharmony_ci			return -EBUSY;
5378c2ecf20Sopenharmony_ci		}
5388c2ecf20Sopenharmony_ci		rc = security_erase(nvdimm, key, i == OP_ERASE
5398c2ecf20Sopenharmony_ci				? NVDIMM_USER : NVDIMM_MASTER);
5408c2ecf20Sopenharmony_ci	} else if (i == OP_OVERWRITE) {
5418c2ecf20Sopenharmony_ci		dev_dbg(dev, "overwrite %u\n", key);
5428c2ecf20Sopenharmony_ci		if (atomic_read(&nvdimm->busy)) {
5438c2ecf20Sopenharmony_ci			dev_dbg(dev, "Unable to overwrite while DIMM active.\n");
5448c2ecf20Sopenharmony_ci			return -EBUSY;
5458c2ecf20Sopenharmony_ci		}
5468c2ecf20Sopenharmony_ci		rc = security_overwrite(nvdimm, key);
5478c2ecf20Sopenharmony_ci	} else
5488c2ecf20Sopenharmony_ci		return -EINVAL;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	if (rc == 0)
5518c2ecf20Sopenharmony_ci		rc = len;
5528c2ecf20Sopenharmony_ci	return rc;
5538c2ecf20Sopenharmony_ci}
554