xref: /linux/drivers/acpi/nfit/intel.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
1f2989396SDave Jiang // SPDX-License-Identifier: GPL-2.0
2f2989396SDave Jiang /* Copyright(c) 2018 Intel Corporation. All rights reserved. */
3f2989396SDave Jiang #include <linux/libnvdimm.h>
4f2989396SDave Jiang #include <linux/ndctl.h>
5f2989396SDave Jiang #include <linux/acpi.h>
6*1156b441SDavidlohr Bueso #include <linux/memregion.h>
74c6926a2SDave Jiang #include <asm/smp.h>
8f2989396SDave Jiang #include "intel.h"
9f2989396SDave Jiang #include "nfit.h"
10f2989396SDave Jiang 
firmware_activate_noidle_show(struct device * dev,struct device_attribute * attr,char * buf)11a1facc1fSDan Williams static ssize_t firmware_activate_noidle_show(struct device *dev,
12a1facc1fSDan Williams 		struct device_attribute *attr, char *buf)
13a1facc1fSDan Williams {
14a1facc1fSDan Williams 	struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
15a1facc1fSDan Williams 	struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);
16a1facc1fSDan Williams 	struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
17a1facc1fSDan Williams 
18a1facc1fSDan Williams 	return sprintf(buf, "%s\n", acpi_desc->fwa_noidle ? "Y" : "N");
19a1facc1fSDan Williams }
20a1facc1fSDan Williams 
firmware_activate_noidle_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)21a1facc1fSDan Williams static ssize_t firmware_activate_noidle_store(struct device *dev,
22a1facc1fSDan Williams 		struct device_attribute *attr, const char *buf, size_t size)
23a1facc1fSDan Williams {
24a1facc1fSDan Williams 	struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
25a1facc1fSDan Williams 	struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);
26a1facc1fSDan Williams 	struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
27a1facc1fSDan Williams 	ssize_t rc;
28a1facc1fSDan Williams 	bool val;
29a1facc1fSDan Williams 
30a1facc1fSDan Williams 	rc = kstrtobool(buf, &val);
31a1facc1fSDan Williams 	if (rc)
32a1facc1fSDan Williams 		return rc;
33a1facc1fSDan Williams 	if (val != acpi_desc->fwa_noidle)
34a1facc1fSDan Williams 		acpi_desc->fwa_cap = NVDIMM_FWA_CAP_INVALID;
35a1facc1fSDan Williams 	acpi_desc->fwa_noidle = val;
36a1facc1fSDan Williams 	return size;
37a1facc1fSDan Williams }
38a1facc1fSDan Williams DEVICE_ATTR_RW(firmware_activate_noidle);
39a1facc1fSDan Williams 
intel_fwa_supported(struct nvdimm_bus * nvdimm_bus)40a1facc1fSDan Williams bool intel_fwa_supported(struct nvdimm_bus *nvdimm_bus)
41a1facc1fSDan Williams {
42a1facc1fSDan Williams 	struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);
43a1facc1fSDan Williams 	struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
44a1facc1fSDan Williams 	unsigned long *mask;
45a1facc1fSDan Williams 
46a1facc1fSDan Williams 	if (!test_bit(NVDIMM_BUS_FAMILY_INTEL, &nd_desc->bus_family_mask))
47a1facc1fSDan Williams 		return false;
48a1facc1fSDan Williams 
49a1facc1fSDan Williams 	mask = &acpi_desc->family_dsm_mask[NVDIMM_BUS_FAMILY_INTEL];
50a1facc1fSDan Williams 	return *mask == NVDIMM_BUS_INTEL_FW_ACTIVATE_CMDMASK;
51a1facc1fSDan Williams }
52a1facc1fSDan Williams 
intel_security_flags(struct nvdimm * nvdimm,enum nvdimm_passphrase_type ptype)53d78c620aSDan Williams static unsigned long intel_security_flags(struct nvdimm *nvdimm,
5489fa9d8eSDave Jiang 		enum nvdimm_passphrase_type ptype)
55f2989396SDave Jiang {
56f2989396SDave Jiang 	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
57d78c620aSDan Williams 	unsigned long security_flags = 0;
58f2989396SDave Jiang 	struct {
59f2989396SDave Jiang 		struct nd_cmd_pkg pkg;
60f2989396SDave Jiang 		struct nd_intel_get_security_state cmd;
61f2989396SDave Jiang 	} nd_cmd = {
62f2989396SDave Jiang 		.pkg = {
63f2989396SDave Jiang 			.nd_command = NVDIMM_INTEL_GET_SECURITY_STATE,
64f2989396SDave Jiang 			.nd_family = NVDIMM_FAMILY_INTEL,
65f2989396SDave Jiang 			.nd_size_out =
66f2989396SDave Jiang 				sizeof(struct nd_intel_get_security_state),
67f2989396SDave Jiang 			.nd_fw_size =
68f2989396SDave Jiang 				sizeof(struct nd_intel_get_security_state),
69f2989396SDave Jiang 		},
70f2989396SDave Jiang 	};
71f2989396SDave Jiang 	int rc;
72f2989396SDave Jiang 
73f2989396SDave Jiang 	if (!test_bit(NVDIMM_INTEL_GET_SECURITY_STATE, &nfit_mem->dsm_mask))
74d78c620aSDan Williams 		return 0;
75f2989396SDave Jiang 
767d988097SDave Jiang 	/*
777d988097SDave Jiang 	 * Short circuit the state retrieval while we are doing overwrite.
787d988097SDave Jiang 	 * The DSM spec states that the security state is indeterminate
797d988097SDave Jiang 	 * until the overwrite DSM completes.
807d988097SDave Jiang 	 */
8189fa9d8eSDave Jiang 	if (nvdimm_in_overwrite(nvdimm) && ptype == NVDIMM_USER)
82d78c620aSDan Williams 		return BIT(NVDIMM_SECURITY_OVERWRITE);
837d988097SDave Jiang 
84f2989396SDave Jiang 	rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
85d78c620aSDan Williams 	if (rc < 0 || nd_cmd.cmd.status) {
86d78c620aSDan Williams 		pr_err("%s: security state retrieval failed (%d:%#x)\n",
87d78c620aSDan Williams 				nvdimm_name(nvdimm), rc, nd_cmd.cmd.status);
88d78c620aSDan Williams 		return 0;
89d78c620aSDan Williams 	}
90f2989396SDave Jiang 
91f2989396SDave Jiang 	/* check and see if security is enabled and locked */
9289fa9d8eSDave Jiang 	if (ptype == NVDIMM_MASTER) {
9389fa9d8eSDave Jiang 		if (nd_cmd.cmd.extended_state & ND_INTEL_SEC_ESTATE_ENABLED)
94d78c620aSDan Williams 			set_bit(NVDIMM_SECURITY_UNLOCKED, &security_flags);
95f2989396SDave Jiang 		else
96d78c620aSDan Williams 			set_bit(NVDIMM_SECURITY_DISABLED, &security_flags);
97d78c620aSDan Williams 		if (nd_cmd.cmd.extended_state & ND_INTEL_SEC_ESTATE_PLIMIT)
98d78c620aSDan Williams 			set_bit(NVDIMM_SECURITY_FROZEN, &security_flags);
99d78c620aSDan Williams 		return security_flags;
10089fa9d8eSDave Jiang 	}
10189fa9d8eSDave Jiang 
102d78c620aSDan Williams 	if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_UNSUPPORTED)
103d78c620aSDan Williams 		return 0;
104d78c620aSDan Williams 
105d78c620aSDan Williams 	if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_ENABLED) {
106d78c620aSDan Williams 		if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_FROZEN ||
107d78c620aSDan Williams 		    nd_cmd.cmd.state & ND_INTEL_SEC_STATE_PLIMIT)
108d78c620aSDan Williams 			set_bit(NVDIMM_SECURITY_FROZEN, &security_flags);
109d78c620aSDan Williams 
110d78c620aSDan Williams 		if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_LOCKED)
111d78c620aSDan Williams 			set_bit(NVDIMM_SECURITY_LOCKED, &security_flags);
112d78c620aSDan Williams 		else
113d78c620aSDan Williams 			set_bit(NVDIMM_SECURITY_UNLOCKED, &security_flags);
114d78c620aSDan Williams 	} else
115d78c620aSDan Williams 		set_bit(NVDIMM_SECURITY_DISABLED, &security_flags);
116d78c620aSDan Williams 
117d78c620aSDan Williams 	return security_flags;
118f2989396SDave Jiang }
119f2989396SDave Jiang 
intel_security_freeze(struct nvdimm * nvdimm)12037833fb7SDave Jiang static int intel_security_freeze(struct nvdimm *nvdimm)
12137833fb7SDave Jiang {
12237833fb7SDave Jiang 	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
12337833fb7SDave Jiang 	struct {
12437833fb7SDave Jiang 		struct nd_cmd_pkg pkg;
12537833fb7SDave Jiang 		struct nd_intel_freeze_lock cmd;
12637833fb7SDave Jiang 	} nd_cmd = {
12737833fb7SDave Jiang 		.pkg = {
12837833fb7SDave Jiang 			.nd_command = NVDIMM_INTEL_FREEZE_LOCK,
12937833fb7SDave Jiang 			.nd_family = NVDIMM_FAMILY_INTEL,
13037833fb7SDave Jiang 			.nd_size_out = ND_INTEL_STATUS_SIZE,
13137833fb7SDave Jiang 			.nd_fw_size = ND_INTEL_STATUS_SIZE,
13237833fb7SDave Jiang 		},
13337833fb7SDave Jiang 	};
13437833fb7SDave Jiang 	int rc;
13537833fb7SDave Jiang 
13637833fb7SDave Jiang 	if (!test_bit(NVDIMM_INTEL_FREEZE_LOCK, &nfit_mem->dsm_mask))
13737833fb7SDave Jiang 		return -ENOTTY;
13837833fb7SDave Jiang 
13937833fb7SDave Jiang 	rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
14037833fb7SDave Jiang 	if (rc < 0)
14137833fb7SDave Jiang 		return rc;
14237833fb7SDave Jiang 	if (nd_cmd.cmd.status)
14337833fb7SDave Jiang 		return -EIO;
14437833fb7SDave Jiang 	return 0;
14537833fb7SDave Jiang }
14637833fb7SDave Jiang 
intel_security_change_key(struct nvdimm * nvdimm,const struct nvdimm_key_data * old_data,const struct nvdimm_key_data * new_data,enum nvdimm_passphrase_type ptype)1474c6926a2SDave Jiang static int intel_security_change_key(struct nvdimm *nvdimm,
1484c6926a2SDave Jiang 		const struct nvdimm_key_data *old_data,
14989fa9d8eSDave Jiang 		const struct nvdimm_key_data *new_data,
15089fa9d8eSDave Jiang 		enum nvdimm_passphrase_type ptype)
1514c6926a2SDave Jiang {
1524c6926a2SDave Jiang 	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
15389fa9d8eSDave Jiang 	unsigned int cmd = ptype == NVDIMM_MASTER ?
15489fa9d8eSDave Jiang 		NVDIMM_INTEL_SET_MASTER_PASSPHRASE :
15589fa9d8eSDave Jiang 		NVDIMM_INTEL_SET_PASSPHRASE;
1564c6926a2SDave Jiang 	struct {
1574c6926a2SDave Jiang 		struct nd_cmd_pkg pkg;
1584c6926a2SDave Jiang 		struct nd_intel_set_passphrase cmd;
1594c6926a2SDave Jiang 	} nd_cmd = {
1604c6926a2SDave Jiang 		.pkg = {
1614c6926a2SDave Jiang 			.nd_family = NVDIMM_FAMILY_INTEL,
1624c6926a2SDave Jiang 			.nd_size_in = ND_INTEL_PASSPHRASE_SIZE * 2,
1634c6926a2SDave Jiang 			.nd_size_out = ND_INTEL_STATUS_SIZE,
1644c6926a2SDave Jiang 			.nd_fw_size = ND_INTEL_STATUS_SIZE,
16589fa9d8eSDave Jiang 			.nd_command = cmd,
1664c6926a2SDave Jiang 		},
1674c6926a2SDave Jiang 	};
1684c6926a2SDave Jiang 	int rc;
1694c6926a2SDave Jiang 
17089fa9d8eSDave Jiang 	if (!test_bit(cmd, &nfit_mem->dsm_mask))
1714c6926a2SDave Jiang 		return -ENOTTY;
1724c6926a2SDave Jiang 
1734c6926a2SDave Jiang 	memcpy(nd_cmd.cmd.old_pass, old_data->data,
1744c6926a2SDave Jiang 			sizeof(nd_cmd.cmd.old_pass));
1754c6926a2SDave Jiang 	memcpy(nd_cmd.cmd.new_pass, new_data->data,
1764c6926a2SDave Jiang 			sizeof(nd_cmd.cmd.new_pass));
1774c6926a2SDave Jiang 	rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
1784c6926a2SDave Jiang 	if (rc < 0)
1794c6926a2SDave Jiang 		return rc;
1804c6926a2SDave Jiang 
1814c6926a2SDave Jiang 	switch (nd_cmd.cmd.status) {
1824c6926a2SDave Jiang 	case 0:
1834c6926a2SDave Jiang 		return 0;
1844c6926a2SDave Jiang 	case ND_INTEL_STATUS_INVALID_PASS:
1854c6926a2SDave Jiang 		return -EINVAL;
1864c6926a2SDave Jiang 	case ND_INTEL_STATUS_NOT_SUPPORTED:
1874c6926a2SDave Jiang 		return -EOPNOTSUPP;
1884c6926a2SDave Jiang 	case ND_INTEL_STATUS_INVALID_STATE:
1894c6926a2SDave Jiang 	default:
1904c6926a2SDave Jiang 		return -EIO;
1914c6926a2SDave Jiang 	}
1924c6926a2SDave Jiang }
1934c6926a2SDave Jiang 
intel_security_unlock(struct nvdimm * nvdimm,const struct nvdimm_key_data * key_data)194ccb7f15aSNathan Chancellor static int __maybe_unused intel_security_unlock(struct nvdimm *nvdimm,
1954c6926a2SDave Jiang 		const struct nvdimm_key_data *key_data)
1964c6926a2SDave Jiang {
1974c6926a2SDave Jiang 	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
1984c6926a2SDave Jiang 	struct {
1994c6926a2SDave Jiang 		struct nd_cmd_pkg pkg;
2004c6926a2SDave Jiang 		struct nd_intel_unlock_unit cmd;
2014c6926a2SDave Jiang 	} nd_cmd = {
2024c6926a2SDave Jiang 		.pkg = {
2034c6926a2SDave Jiang 			.nd_command = NVDIMM_INTEL_UNLOCK_UNIT,
2044c6926a2SDave Jiang 			.nd_family = NVDIMM_FAMILY_INTEL,
2054c6926a2SDave Jiang 			.nd_size_in = ND_INTEL_PASSPHRASE_SIZE,
2064c6926a2SDave Jiang 			.nd_size_out = ND_INTEL_STATUS_SIZE,
2074c6926a2SDave Jiang 			.nd_fw_size = ND_INTEL_STATUS_SIZE,
2084c6926a2SDave Jiang 		},
2094c6926a2SDave Jiang 	};
2104c6926a2SDave Jiang 	int rc;
2114c6926a2SDave Jiang 
2124c6926a2SDave Jiang 	if (!test_bit(NVDIMM_INTEL_UNLOCK_UNIT, &nfit_mem->dsm_mask))
2134c6926a2SDave Jiang 		return -ENOTTY;
2144c6926a2SDave Jiang 
2154c6926a2SDave Jiang 	memcpy(nd_cmd.cmd.passphrase, key_data->data,
2164c6926a2SDave Jiang 			sizeof(nd_cmd.cmd.passphrase));
2174c6926a2SDave Jiang 	rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
2184c6926a2SDave Jiang 	if (rc < 0)
2194c6926a2SDave Jiang 		return rc;
2204c6926a2SDave Jiang 	switch (nd_cmd.cmd.status) {
2214c6926a2SDave Jiang 	case 0:
2224c6926a2SDave Jiang 		break;
2234c6926a2SDave Jiang 	case ND_INTEL_STATUS_INVALID_PASS:
2244c6926a2SDave Jiang 		return -EINVAL;
2254c6926a2SDave Jiang 	default:
2264c6926a2SDave Jiang 		return -EIO;
2274c6926a2SDave Jiang 	}
2284c6926a2SDave Jiang 
2294c6926a2SDave Jiang 	return 0;
2304c6926a2SDave Jiang }
2314c6926a2SDave Jiang 
intel_security_disable(struct nvdimm * nvdimm,const struct nvdimm_key_data * key_data)23203b65b22SDave Jiang static int intel_security_disable(struct nvdimm *nvdimm,
23303b65b22SDave Jiang 		const struct nvdimm_key_data *key_data)
23403b65b22SDave Jiang {
23503b65b22SDave Jiang 	int rc;
23603b65b22SDave Jiang 	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
23703b65b22SDave Jiang 	struct {
23803b65b22SDave Jiang 		struct nd_cmd_pkg pkg;
23903b65b22SDave Jiang 		struct nd_intel_disable_passphrase cmd;
24003b65b22SDave Jiang 	} nd_cmd = {
24103b65b22SDave Jiang 		.pkg = {
24203b65b22SDave Jiang 			.nd_command = NVDIMM_INTEL_DISABLE_PASSPHRASE,
24303b65b22SDave Jiang 			.nd_family = NVDIMM_FAMILY_INTEL,
24403b65b22SDave Jiang 			.nd_size_in = ND_INTEL_PASSPHRASE_SIZE,
24503b65b22SDave Jiang 			.nd_size_out = ND_INTEL_STATUS_SIZE,
24603b65b22SDave Jiang 			.nd_fw_size = ND_INTEL_STATUS_SIZE,
24703b65b22SDave Jiang 		},
24803b65b22SDave Jiang 	};
24903b65b22SDave Jiang 
25003b65b22SDave Jiang 	if (!test_bit(NVDIMM_INTEL_DISABLE_PASSPHRASE, &nfit_mem->dsm_mask))
25103b65b22SDave Jiang 		return -ENOTTY;
25203b65b22SDave Jiang 
25303b65b22SDave Jiang 	memcpy(nd_cmd.cmd.passphrase, key_data->data,
25403b65b22SDave Jiang 			sizeof(nd_cmd.cmd.passphrase));
25503b65b22SDave Jiang 	rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
25603b65b22SDave Jiang 	if (rc < 0)
25703b65b22SDave Jiang 		return rc;
25803b65b22SDave Jiang 
25903b65b22SDave Jiang 	switch (nd_cmd.cmd.status) {
26003b65b22SDave Jiang 	case 0:
26103b65b22SDave Jiang 		break;
26203b65b22SDave Jiang 	case ND_INTEL_STATUS_INVALID_PASS:
26303b65b22SDave Jiang 		return -EINVAL;
26403b65b22SDave Jiang 	case ND_INTEL_STATUS_INVALID_STATE:
26503b65b22SDave Jiang 	default:
26603b65b22SDave Jiang 		return -ENXIO;
26703b65b22SDave Jiang 	}
26803b65b22SDave Jiang 
26903b65b22SDave Jiang 	return 0;
27003b65b22SDave Jiang }
27103b65b22SDave Jiang 
intel_security_erase(struct nvdimm * nvdimm,const struct nvdimm_key_data * key,enum nvdimm_passphrase_type ptype)272ccb7f15aSNathan Chancellor static int __maybe_unused intel_security_erase(struct nvdimm *nvdimm,
27389fa9d8eSDave Jiang 		const struct nvdimm_key_data *key,
27489fa9d8eSDave Jiang 		enum nvdimm_passphrase_type ptype)
27564e77c8cSDave Jiang {
27664e77c8cSDave Jiang 	int rc;
27764e77c8cSDave Jiang 	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
27889fa9d8eSDave Jiang 	unsigned int cmd = ptype == NVDIMM_MASTER ?
27989fa9d8eSDave Jiang 		NVDIMM_INTEL_MASTER_SECURE_ERASE : NVDIMM_INTEL_SECURE_ERASE;
28064e77c8cSDave Jiang 	struct {
28164e77c8cSDave Jiang 		struct nd_cmd_pkg pkg;
28264e77c8cSDave Jiang 		struct nd_intel_secure_erase cmd;
28364e77c8cSDave Jiang 	} nd_cmd = {
28464e77c8cSDave Jiang 		.pkg = {
28564e77c8cSDave Jiang 			.nd_family = NVDIMM_FAMILY_INTEL,
28664e77c8cSDave Jiang 			.nd_size_in = ND_INTEL_PASSPHRASE_SIZE,
28764e77c8cSDave Jiang 			.nd_size_out = ND_INTEL_STATUS_SIZE,
28864e77c8cSDave Jiang 			.nd_fw_size = ND_INTEL_STATUS_SIZE,
28989fa9d8eSDave Jiang 			.nd_command = cmd,
29064e77c8cSDave Jiang 		},
29164e77c8cSDave Jiang 	};
29264e77c8cSDave Jiang 
29389fa9d8eSDave Jiang 	if (!test_bit(cmd, &nfit_mem->dsm_mask))
29464e77c8cSDave Jiang 		return -ENOTTY;
29564e77c8cSDave Jiang 
29664e77c8cSDave Jiang 	memcpy(nd_cmd.cmd.passphrase, key->data,
29764e77c8cSDave Jiang 			sizeof(nd_cmd.cmd.passphrase));
29864e77c8cSDave Jiang 	rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
29964e77c8cSDave Jiang 	if (rc < 0)
30064e77c8cSDave Jiang 		return rc;
30164e77c8cSDave Jiang 
30264e77c8cSDave Jiang 	switch (nd_cmd.cmd.status) {
30364e77c8cSDave Jiang 	case 0:
30464e77c8cSDave Jiang 		break;
30564e77c8cSDave Jiang 	case ND_INTEL_STATUS_NOT_SUPPORTED:
30664e77c8cSDave Jiang 		return -EOPNOTSUPP;
30764e77c8cSDave Jiang 	case ND_INTEL_STATUS_INVALID_PASS:
30864e77c8cSDave Jiang 		return -EINVAL;
30964e77c8cSDave Jiang 	case ND_INTEL_STATUS_INVALID_STATE:
31064e77c8cSDave Jiang 	default:
31164e77c8cSDave Jiang 		return -ENXIO;
31264e77c8cSDave Jiang 	}
31364e77c8cSDave Jiang 
31464e77c8cSDave Jiang 	return 0;
31564e77c8cSDave Jiang }
31664e77c8cSDave Jiang 
intel_security_query_overwrite(struct nvdimm * nvdimm)317ccb7f15aSNathan Chancellor static int __maybe_unused intel_security_query_overwrite(struct nvdimm *nvdimm)
3187d988097SDave Jiang {
3197d988097SDave Jiang 	int rc;
3207d988097SDave Jiang 	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
3217d988097SDave Jiang 	struct {
3227d988097SDave Jiang 		struct nd_cmd_pkg pkg;
3237d988097SDave Jiang 		struct nd_intel_query_overwrite cmd;
3247d988097SDave Jiang 	} nd_cmd = {
3257d988097SDave Jiang 		.pkg = {
3267d988097SDave Jiang 			.nd_command = NVDIMM_INTEL_QUERY_OVERWRITE,
3277d988097SDave Jiang 			.nd_family = NVDIMM_FAMILY_INTEL,
3287d988097SDave Jiang 			.nd_size_out = ND_INTEL_STATUS_SIZE,
3297d988097SDave Jiang 			.nd_fw_size = ND_INTEL_STATUS_SIZE,
3307d988097SDave Jiang 		},
3317d988097SDave Jiang 	};
3327d988097SDave Jiang 
3337d988097SDave Jiang 	if (!test_bit(NVDIMM_INTEL_QUERY_OVERWRITE, &nfit_mem->dsm_mask))
3347d988097SDave Jiang 		return -ENOTTY;
3357d988097SDave Jiang 
3367d988097SDave Jiang 	rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
3377d988097SDave Jiang 	if (rc < 0)
3387d988097SDave Jiang 		return rc;
3397d988097SDave Jiang 
3407d988097SDave Jiang 	switch (nd_cmd.cmd.status) {
3417d988097SDave Jiang 	case 0:
3427d988097SDave Jiang 		break;
3437d988097SDave Jiang 	case ND_INTEL_STATUS_OQUERY_INPROGRESS:
3447d988097SDave Jiang 		return -EBUSY;
3457d988097SDave Jiang 	default:
3467d988097SDave Jiang 		return -ENXIO;
3477d988097SDave Jiang 	}
3487d988097SDave Jiang 
3497d988097SDave Jiang 	return 0;
3507d988097SDave Jiang }
3517d988097SDave Jiang 
intel_security_overwrite(struct nvdimm * nvdimm,const struct nvdimm_key_data * nkey)352ccb7f15aSNathan Chancellor static int __maybe_unused intel_security_overwrite(struct nvdimm *nvdimm,
3537d988097SDave Jiang 		const struct nvdimm_key_data *nkey)
3547d988097SDave Jiang {
3557d988097SDave Jiang 	int rc;
3567d988097SDave Jiang 	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
3577d988097SDave Jiang 	struct {
3587d988097SDave Jiang 		struct nd_cmd_pkg pkg;
3597d988097SDave Jiang 		struct nd_intel_overwrite cmd;
3607d988097SDave Jiang 	} nd_cmd = {
3617d988097SDave Jiang 		.pkg = {
3627d988097SDave Jiang 			.nd_command = NVDIMM_INTEL_OVERWRITE,
3637d988097SDave Jiang 			.nd_family = NVDIMM_FAMILY_INTEL,
3647d988097SDave Jiang 			.nd_size_in = ND_INTEL_PASSPHRASE_SIZE,
3657d988097SDave Jiang 			.nd_size_out = ND_INTEL_STATUS_SIZE,
3667d988097SDave Jiang 			.nd_fw_size = ND_INTEL_STATUS_SIZE,
3677d988097SDave Jiang 		},
3687d988097SDave Jiang 	};
3697d988097SDave Jiang 
3707d988097SDave Jiang 	if (!test_bit(NVDIMM_INTEL_OVERWRITE, &nfit_mem->dsm_mask))
3717d988097SDave Jiang 		return -ENOTTY;
3727d988097SDave Jiang 
3737d988097SDave Jiang 	memcpy(nd_cmd.cmd.passphrase, nkey->data,
3747d988097SDave Jiang 			sizeof(nd_cmd.cmd.passphrase));
3757d988097SDave Jiang 	rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
3767d988097SDave Jiang 	if (rc < 0)
3777d988097SDave Jiang 		return rc;
3787d988097SDave Jiang 
3797d988097SDave Jiang 	switch (nd_cmd.cmd.status) {
3807d988097SDave Jiang 	case 0:
3817d988097SDave Jiang 		return 0;
3827d988097SDave Jiang 	case ND_INTEL_STATUS_OVERWRITE_UNSUPPORTED:
3837d988097SDave Jiang 		return -ENOTSUPP;
3847d988097SDave Jiang 	case ND_INTEL_STATUS_INVALID_PASS:
3857d988097SDave Jiang 		return -EINVAL;
3867d988097SDave Jiang 	case ND_INTEL_STATUS_INVALID_STATE:
3877d988097SDave Jiang 	default:
3887d988097SDave Jiang 		return -ENXIO;
3897d988097SDave Jiang 	}
3907d988097SDave Jiang }
3917d988097SDave Jiang 
392f2989396SDave Jiang static const struct nvdimm_security_ops __intel_security_ops = {
393d78c620aSDan Williams 	.get_flags = intel_security_flags,
39437833fb7SDave Jiang 	.freeze = intel_security_freeze,
3954c6926a2SDave Jiang 	.change_key = intel_security_change_key,
39603b65b22SDave Jiang 	.disable = intel_security_disable,
3974c6926a2SDave Jiang #ifdef CONFIG_X86
3984c6926a2SDave Jiang 	.unlock = intel_security_unlock,
39964e77c8cSDave Jiang 	.erase = intel_security_erase,
4007d988097SDave Jiang 	.overwrite = intel_security_overwrite,
4017d988097SDave Jiang 	.query_overwrite = intel_security_query_overwrite,
4024c6926a2SDave Jiang #endif
403f2989396SDave Jiang };
4044c6926a2SDave Jiang 
405f2989396SDave Jiang const struct nvdimm_security_ops *intel_security_ops = &__intel_security_ops;
406a1facc1fSDan Williams 
intel_bus_fwa_businfo(struct nvdimm_bus_descriptor * nd_desc,struct nd_intel_bus_fw_activate_businfo * info)407a1facc1fSDan Williams static int intel_bus_fwa_businfo(struct nvdimm_bus_descriptor *nd_desc,
408a1facc1fSDan Williams 		struct nd_intel_bus_fw_activate_businfo *info)
409a1facc1fSDan Williams {
410a1facc1fSDan Williams 	struct {
411a1facc1fSDan Williams 		struct nd_cmd_pkg pkg;
412a1facc1fSDan Williams 		struct nd_intel_bus_fw_activate_businfo cmd;
413a1facc1fSDan Williams 	} nd_cmd = {
414a1facc1fSDan Williams 		.pkg = {
415a1facc1fSDan Williams 			.nd_command = NVDIMM_BUS_INTEL_FW_ACTIVATE_BUSINFO,
416a1facc1fSDan Williams 			.nd_family = NVDIMM_BUS_FAMILY_INTEL,
417a1facc1fSDan Williams 			.nd_size_out =
418a1facc1fSDan Williams 				sizeof(struct nd_intel_bus_fw_activate_businfo),
419a1facc1fSDan Williams 			.nd_fw_size =
420a1facc1fSDan Williams 				sizeof(struct nd_intel_bus_fw_activate_businfo),
421a1facc1fSDan Williams 		},
422a1facc1fSDan Williams 	};
423a1facc1fSDan Williams 	int rc;
424a1facc1fSDan Williams 
425a1facc1fSDan Williams 	rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd),
426a1facc1fSDan Williams 			NULL);
427a1facc1fSDan Williams 	*info = nd_cmd.cmd;
428a1facc1fSDan Williams 	return rc;
429a1facc1fSDan Williams }
430a1facc1fSDan Williams 
431a1facc1fSDan Williams /* The fw_ops expect to be called with the nvdimm_bus_lock() held */
intel_bus_fwa_state(struct nvdimm_bus_descriptor * nd_desc)432a1facc1fSDan Williams static enum nvdimm_fwa_state intel_bus_fwa_state(
433a1facc1fSDan Williams 		struct nvdimm_bus_descriptor *nd_desc)
434a1facc1fSDan Williams {
435a1facc1fSDan Williams 	struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
436a1facc1fSDan Williams 	struct nd_intel_bus_fw_activate_businfo info;
437a1facc1fSDan Williams 	struct device *dev = acpi_desc->dev;
438a1facc1fSDan Williams 	enum nvdimm_fwa_state state;
439a1facc1fSDan Williams 	int rc;
440a1facc1fSDan Williams 
441a1facc1fSDan Williams 	/*
442a1facc1fSDan Williams 	 * It should not be possible for platform firmware to return
443a1facc1fSDan Williams 	 * busy because activate is a synchronous operation. Treat it
444a1facc1fSDan Williams 	 * similar to invalid, i.e. always refresh / poll the status.
445a1facc1fSDan Williams 	 */
446a1facc1fSDan Williams 	switch (acpi_desc->fwa_state) {
447a1facc1fSDan Williams 	case NVDIMM_FWA_INVALID:
448a1facc1fSDan Williams 	case NVDIMM_FWA_BUSY:
449a1facc1fSDan Williams 		break;
450a1facc1fSDan Williams 	default:
451a1facc1fSDan Williams 		/* check if capability needs to be refreshed */
452a1facc1fSDan Williams 		if (acpi_desc->fwa_cap == NVDIMM_FWA_CAP_INVALID)
453a1facc1fSDan Williams 			break;
454a1facc1fSDan Williams 		return acpi_desc->fwa_state;
455a1facc1fSDan Williams 	}
456a1facc1fSDan Williams 
457a1facc1fSDan Williams 	/* Refresh with platform firmware */
458a1facc1fSDan Williams 	rc = intel_bus_fwa_businfo(nd_desc, &info);
459a1facc1fSDan Williams 	if (rc)
460a1facc1fSDan Williams 		return NVDIMM_FWA_INVALID;
461a1facc1fSDan Williams 
462a1facc1fSDan Williams 	switch (info.state) {
463a1facc1fSDan Williams 	case ND_INTEL_FWA_IDLE:
464a1facc1fSDan Williams 		state = NVDIMM_FWA_IDLE;
465a1facc1fSDan Williams 		break;
466a1facc1fSDan Williams 	case ND_INTEL_FWA_BUSY:
467a1facc1fSDan Williams 		state = NVDIMM_FWA_BUSY;
468a1facc1fSDan Williams 		break;
469a1facc1fSDan Williams 	case ND_INTEL_FWA_ARMED:
470a1facc1fSDan Williams 		if (info.activate_tmo > info.max_quiesce_tmo)
471a1facc1fSDan Williams 			state = NVDIMM_FWA_ARM_OVERFLOW;
472a1facc1fSDan Williams 		else
473a1facc1fSDan Williams 			state = NVDIMM_FWA_ARMED;
474a1facc1fSDan Williams 		break;
475a1facc1fSDan Williams 	default:
476a1facc1fSDan Williams 		dev_err_once(dev, "invalid firmware activate state %d\n",
477a1facc1fSDan Williams 				info.state);
478a1facc1fSDan Williams 		return NVDIMM_FWA_INVALID;
479a1facc1fSDan Williams 	}
480a1facc1fSDan Williams 
481a1facc1fSDan Williams 	/*
482a1facc1fSDan Williams 	 * Capability data is available in the same payload as state. It
483a1facc1fSDan Williams 	 * is expected to be static.
484a1facc1fSDan Williams 	 */
485a1facc1fSDan Williams 	if (acpi_desc->fwa_cap == NVDIMM_FWA_CAP_INVALID) {
486a1facc1fSDan Williams 		if (info.capability & ND_INTEL_BUS_FWA_CAP_FWQUIESCE)
487a1facc1fSDan Williams 			acpi_desc->fwa_cap = NVDIMM_FWA_CAP_QUIESCE;
488a1facc1fSDan Williams 		else if (info.capability & ND_INTEL_BUS_FWA_CAP_OSQUIESCE) {
489a1facc1fSDan Williams 			/*
490a1facc1fSDan Williams 			 * Skip hibernate cycle by default if platform
491a1facc1fSDan Williams 			 * indicates that it does not need devices to be
492a1facc1fSDan Williams 			 * quiesced.
493a1facc1fSDan Williams 			 */
494a1facc1fSDan Williams 			acpi_desc->fwa_cap = NVDIMM_FWA_CAP_LIVE;
495a1facc1fSDan Williams 		} else
496a1facc1fSDan Williams 			acpi_desc->fwa_cap = NVDIMM_FWA_CAP_NONE;
497a1facc1fSDan Williams 	}
498a1facc1fSDan Williams 
499a1facc1fSDan Williams 	acpi_desc->fwa_state = state;
500a1facc1fSDan Williams 
501a1facc1fSDan Williams 	return state;
502a1facc1fSDan Williams }
503a1facc1fSDan Williams 
intel_bus_fwa_capability(struct nvdimm_bus_descriptor * nd_desc)504a1facc1fSDan Williams static enum nvdimm_fwa_capability intel_bus_fwa_capability(
505a1facc1fSDan Williams 		struct nvdimm_bus_descriptor *nd_desc)
506a1facc1fSDan Williams {
507a1facc1fSDan Williams 	struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
508a1facc1fSDan Williams 
509a1facc1fSDan Williams 	if (acpi_desc->fwa_cap > NVDIMM_FWA_CAP_INVALID)
510a1facc1fSDan Williams 		return acpi_desc->fwa_cap;
511a1facc1fSDan Williams 
512a1facc1fSDan Williams 	if (intel_bus_fwa_state(nd_desc) > NVDIMM_FWA_INVALID)
513a1facc1fSDan Williams 		return acpi_desc->fwa_cap;
514a1facc1fSDan Williams 
515a1facc1fSDan Williams 	return NVDIMM_FWA_CAP_INVALID;
516a1facc1fSDan Williams }
517a1facc1fSDan Williams 
intel_bus_fwa_activate(struct nvdimm_bus_descriptor * nd_desc)518a1facc1fSDan Williams static int intel_bus_fwa_activate(struct nvdimm_bus_descriptor *nd_desc)
519a1facc1fSDan Williams {
520a1facc1fSDan Williams 	struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
521a1facc1fSDan Williams 	struct {
522a1facc1fSDan Williams 		struct nd_cmd_pkg pkg;
523a1facc1fSDan Williams 		struct nd_intel_bus_fw_activate cmd;
524a1facc1fSDan Williams 	} nd_cmd = {
525a1facc1fSDan Williams 		.pkg = {
526a1facc1fSDan Williams 			.nd_command = NVDIMM_BUS_INTEL_FW_ACTIVATE,
527a1facc1fSDan Williams 			.nd_family = NVDIMM_BUS_FAMILY_INTEL,
528a1facc1fSDan Williams 			.nd_size_in = sizeof(nd_cmd.cmd.iodev_state),
529a1facc1fSDan Williams 			.nd_size_out =
530a1facc1fSDan Williams 				sizeof(struct nd_intel_bus_fw_activate),
531a1facc1fSDan Williams 			.nd_fw_size =
532a1facc1fSDan Williams 				sizeof(struct nd_intel_bus_fw_activate),
533a1facc1fSDan Williams 		},
534a1facc1fSDan Williams 		/*
535a1facc1fSDan Williams 		 * Even though activate is run from a suspended context,
536a1facc1fSDan Williams 		 * for safety, still ask platform firmware to force
537a1facc1fSDan Williams 		 * quiesce devices by default. Let a module
538a1facc1fSDan Williams 		 * parameter override that policy.
539a1facc1fSDan Williams 		 */
540a1facc1fSDan Williams 		.cmd = {
541a1facc1fSDan Williams 			.iodev_state = acpi_desc->fwa_noidle
542a1facc1fSDan Williams 				? ND_INTEL_BUS_FWA_IODEV_OS_IDLE
543a1facc1fSDan Williams 				: ND_INTEL_BUS_FWA_IODEV_FORCE_IDLE,
544a1facc1fSDan Williams 		},
545a1facc1fSDan Williams 	};
546a1facc1fSDan Williams 	int rc;
547a1facc1fSDan Williams 
548a1facc1fSDan Williams 	switch (intel_bus_fwa_state(nd_desc)) {
549a1facc1fSDan Williams 	case NVDIMM_FWA_ARMED:
550a1facc1fSDan Williams 	case NVDIMM_FWA_ARM_OVERFLOW:
551a1facc1fSDan Williams 		break;
552a1facc1fSDan Williams 	default:
553a1facc1fSDan Williams 		return -ENXIO;
554a1facc1fSDan Williams 	}
555a1facc1fSDan Williams 
556a1facc1fSDan Williams 	rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd),
557a1facc1fSDan Williams 			NULL);
558a1facc1fSDan Williams 
559a1facc1fSDan Williams 	/*
560a1facc1fSDan Williams 	 * Whether the command succeeded, or failed, the agent checking
561a1facc1fSDan Williams 	 * for the result needs to query the DIMMs individually.
562a1facc1fSDan Williams 	 * Increment the activation count to invalidate all the DIMM
563a1facc1fSDan Williams 	 * states at once (it's otherwise not possible to take
564a1facc1fSDan Williams 	 * acpi_desc->init_mutex in this context)
565a1facc1fSDan Williams 	 */
566a1facc1fSDan Williams 	acpi_desc->fwa_state = NVDIMM_FWA_INVALID;
567a1facc1fSDan Williams 	acpi_desc->fwa_count++;
568a1facc1fSDan Williams 
569a1facc1fSDan Williams 	dev_dbg(acpi_desc->dev, "result: %d\n", rc);
570a1facc1fSDan Williams 
571a1facc1fSDan Williams 	return rc;
572a1facc1fSDan Williams }
573a1facc1fSDan Williams 
574a1facc1fSDan Williams static const struct nvdimm_bus_fw_ops __intel_bus_fw_ops = {
575a1facc1fSDan Williams 	.activate_state = intel_bus_fwa_state,
576a1facc1fSDan Williams 	.capability = intel_bus_fwa_capability,
577a1facc1fSDan Williams 	.activate = intel_bus_fwa_activate,
578a1facc1fSDan Williams };
579a1facc1fSDan Williams 
580a1facc1fSDan Williams const struct nvdimm_bus_fw_ops *intel_bus_fw_ops = &__intel_bus_fw_ops;
581a1facc1fSDan Williams 
intel_fwa_dimminfo(struct nvdimm * nvdimm,struct nd_intel_fw_activate_dimminfo * info)582a1facc1fSDan Williams static int intel_fwa_dimminfo(struct nvdimm *nvdimm,
583a1facc1fSDan Williams 		struct nd_intel_fw_activate_dimminfo *info)
584a1facc1fSDan Williams {
585a1facc1fSDan Williams 	struct {
586a1facc1fSDan Williams 		struct nd_cmd_pkg pkg;
587a1facc1fSDan Williams 		struct nd_intel_fw_activate_dimminfo cmd;
588a1facc1fSDan Williams 	} nd_cmd = {
589a1facc1fSDan Williams 		.pkg = {
590a1facc1fSDan Williams 			.nd_command = NVDIMM_INTEL_FW_ACTIVATE_DIMMINFO,
591a1facc1fSDan Williams 			.nd_family = NVDIMM_FAMILY_INTEL,
592a1facc1fSDan Williams 			.nd_size_out =
593a1facc1fSDan Williams 				sizeof(struct nd_intel_fw_activate_dimminfo),
594a1facc1fSDan Williams 			.nd_fw_size =
595a1facc1fSDan Williams 				sizeof(struct nd_intel_fw_activate_dimminfo),
596a1facc1fSDan Williams 		},
597a1facc1fSDan Williams 	};
598a1facc1fSDan Williams 	int rc;
599a1facc1fSDan Williams 
600a1facc1fSDan Williams 	rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
601a1facc1fSDan Williams 	*info = nd_cmd.cmd;
602a1facc1fSDan Williams 	return rc;
603a1facc1fSDan Williams }
604a1facc1fSDan Williams 
intel_fwa_state(struct nvdimm * nvdimm)605a1facc1fSDan Williams static enum nvdimm_fwa_state intel_fwa_state(struct nvdimm *nvdimm)
606a1facc1fSDan Williams {
607a1facc1fSDan Williams 	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
608a1facc1fSDan Williams 	struct acpi_nfit_desc *acpi_desc = nfit_mem->acpi_desc;
609a1facc1fSDan Williams 	struct nd_intel_fw_activate_dimminfo info;
610a1facc1fSDan Williams 	int rc;
611a1facc1fSDan Williams 
612a1facc1fSDan Williams 	/*
613a1facc1fSDan Williams 	 * Similar to the bus state, since activate is synchronous the
614a1facc1fSDan Williams 	 * busy state should resolve within the context of 'activate'.
615a1facc1fSDan Williams 	 */
616a1facc1fSDan Williams 	switch (nfit_mem->fwa_state) {
617a1facc1fSDan Williams 	case NVDIMM_FWA_INVALID:
618a1facc1fSDan Williams 	case NVDIMM_FWA_BUSY:
619a1facc1fSDan Williams 		break;
620a1facc1fSDan Williams 	default:
621a1facc1fSDan Williams 		/* If no activations occurred the old state is still valid */
622a1facc1fSDan Williams 		if (nfit_mem->fwa_count == acpi_desc->fwa_count)
623a1facc1fSDan Williams 			return nfit_mem->fwa_state;
624a1facc1fSDan Williams 	}
625a1facc1fSDan Williams 
626a1facc1fSDan Williams 	rc = intel_fwa_dimminfo(nvdimm, &info);
627a1facc1fSDan Williams 	if (rc)
628a1facc1fSDan Williams 		return NVDIMM_FWA_INVALID;
629a1facc1fSDan Williams 
630a1facc1fSDan Williams 	switch (info.state) {
631a1facc1fSDan Williams 	case ND_INTEL_FWA_IDLE:
632a1facc1fSDan Williams 		nfit_mem->fwa_state = NVDIMM_FWA_IDLE;
633a1facc1fSDan Williams 		break;
634a1facc1fSDan Williams 	case ND_INTEL_FWA_BUSY:
635a1facc1fSDan Williams 		nfit_mem->fwa_state = NVDIMM_FWA_BUSY;
636a1facc1fSDan Williams 		break;
637a1facc1fSDan Williams 	case ND_INTEL_FWA_ARMED:
638a1facc1fSDan Williams 		nfit_mem->fwa_state = NVDIMM_FWA_ARMED;
639a1facc1fSDan Williams 		break;
640a1facc1fSDan Williams 	default:
641a1facc1fSDan Williams 		nfit_mem->fwa_state = NVDIMM_FWA_INVALID;
642a1facc1fSDan Williams 		break;
643a1facc1fSDan Williams 	}
644a1facc1fSDan Williams 
645a1facc1fSDan Williams 	switch (info.result) {
646a1facc1fSDan Williams 	case ND_INTEL_DIMM_FWA_NONE:
647a1facc1fSDan Williams 		nfit_mem->fwa_result = NVDIMM_FWA_RESULT_NONE;
648a1facc1fSDan Williams 		break;
649a1facc1fSDan Williams 	case ND_INTEL_DIMM_FWA_SUCCESS:
650a1facc1fSDan Williams 		nfit_mem->fwa_result = NVDIMM_FWA_RESULT_SUCCESS;
651a1facc1fSDan Williams 		break;
652a1facc1fSDan Williams 	case ND_INTEL_DIMM_FWA_NOTSTAGED:
653a1facc1fSDan Williams 		nfit_mem->fwa_result = NVDIMM_FWA_RESULT_NOTSTAGED;
654a1facc1fSDan Williams 		break;
655a1facc1fSDan Williams 	case ND_INTEL_DIMM_FWA_NEEDRESET:
656a1facc1fSDan Williams 		nfit_mem->fwa_result = NVDIMM_FWA_RESULT_NEEDRESET;
657a1facc1fSDan Williams 		break;
658a1facc1fSDan Williams 	case ND_INTEL_DIMM_FWA_MEDIAFAILED:
659a1facc1fSDan Williams 	case ND_INTEL_DIMM_FWA_ABORT:
660a1facc1fSDan Williams 	case ND_INTEL_DIMM_FWA_NOTSUPP:
661a1facc1fSDan Williams 	case ND_INTEL_DIMM_FWA_ERROR:
662a1facc1fSDan Williams 	default:
663a1facc1fSDan Williams 		nfit_mem->fwa_result = NVDIMM_FWA_RESULT_FAIL;
664a1facc1fSDan Williams 		break;
665a1facc1fSDan Williams 	}
666a1facc1fSDan Williams 
667a1facc1fSDan Williams 	nfit_mem->fwa_count = acpi_desc->fwa_count;
668a1facc1fSDan Williams 
669a1facc1fSDan Williams 	return nfit_mem->fwa_state;
670a1facc1fSDan Williams }
671a1facc1fSDan Williams 
intel_fwa_result(struct nvdimm * nvdimm)672a1facc1fSDan Williams static enum nvdimm_fwa_result intel_fwa_result(struct nvdimm *nvdimm)
673a1facc1fSDan Williams {
674a1facc1fSDan Williams 	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
675a1facc1fSDan Williams 	struct acpi_nfit_desc *acpi_desc = nfit_mem->acpi_desc;
676a1facc1fSDan Williams 
677a1facc1fSDan Williams 	if (nfit_mem->fwa_count == acpi_desc->fwa_count
678a1facc1fSDan Williams 			&& nfit_mem->fwa_result > NVDIMM_FWA_RESULT_INVALID)
679a1facc1fSDan Williams 		return nfit_mem->fwa_result;
680a1facc1fSDan Williams 
681a1facc1fSDan Williams 	if (intel_fwa_state(nvdimm) > NVDIMM_FWA_INVALID)
682a1facc1fSDan Williams 		return nfit_mem->fwa_result;
683a1facc1fSDan Williams 
684a1facc1fSDan Williams 	return NVDIMM_FWA_RESULT_INVALID;
685a1facc1fSDan Williams }
686a1facc1fSDan Williams 
intel_fwa_arm(struct nvdimm * nvdimm,enum nvdimm_fwa_trigger arm)687a1facc1fSDan Williams static int intel_fwa_arm(struct nvdimm *nvdimm, enum nvdimm_fwa_trigger arm)
688a1facc1fSDan Williams {
689a1facc1fSDan Williams 	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
690a1facc1fSDan Williams 	struct acpi_nfit_desc *acpi_desc = nfit_mem->acpi_desc;
691a1facc1fSDan Williams 	struct {
692a1facc1fSDan Williams 		struct nd_cmd_pkg pkg;
693a1facc1fSDan Williams 		struct nd_intel_fw_activate_arm cmd;
694a1facc1fSDan Williams 	} nd_cmd = {
695a1facc1fSDan Williams 		.pkg = {
696a1facc1fSDan Williams 			.nd_command = NVDIMM_INTEL_FW_ACTIVATE_ARM,
697a1facc1fSDan Williams 			.nd_family = NVDIMM_FAMILY_INTEL,
698a1facc1fSDan Williams 			.nd_size_in = sizeof(nd_cmd.cmd.activate_arm),
699a1facc1fSDan Williams 			.nd_size_out =
700a1facc1fSDan Williams 				sizeof(struct nd_intel_fw_activate_arm),
701a1facc1fSDan Williams 			.nd_fw_size =
702a1facc1fSDan Williams 				sizeof(struct nd_intel_fw_activate_arm),
703a1facc1fSDan Williams 		},
704a1facc1fSDan Williams 		.cmd = {
705a1facc1fSDan Williams 			.activate_arm = arm == NVDIMM_FWA_ARM
706a1facc1fSDan Williams 				? ND_INTEL_DIMM_FWA_ARM
707a1facc1fSDan Williams 				: ND_INTEL_DIMM_FWA_DISARM,
708a1facc1fSDan Williams 		},
709a1facc1fSDan Williams 	};
710a1facc1fSDan Williams 	int rc;
711a1facc1fSDan Williams 
712a1facc1fSDan Williams 	switch (intel_fwa_state(nvdimm)) {
713a1facc1fSDan Williams 	case NVDIMM_FWA_INVALID:
714a1facc1fSDan Williams 		return -ENXIO;
715a1facc1fSDan Williams 	case NVDIMM_FWA_BUSY:
716a1facc1fSDan Williams 		return -EBUSY;
717a1facc1fSDan Williams 	case NVDIMM_FWA_IDLE:
718a1facc1fSDan Williams 		if (arm == NVDIMM_FWA_DISARM)
719a1facc1fSDan Williams 			return 0;
720a1facc1fSDan Williams 		break;
721a1facc1fSDan Williams 	case NVDIMM_FWA_ARMED:
722a1facc1fSDan Williams 		if (arm == NVDIMM_FWA_ARM)
723a1facc1fSDan Williams 			return 0;
724a1facc1fSDan Williams 		break;
725a1facc1fSDan Williams 	default:
726a1facc1fSDan Williams 		return -ENXIO;
727a1facc1fSDan Williams 	}
728a1facc1fSDan Williams 
729a1facc1fSDan Williams 	/*
730a1facc1fSDan Williams 	 * Invalidate the bus-level state, now that we're committed to
731a1facc1fSDan Williams 	 * changing the 'arm' state.
732a1facc1fSDan Williams 	 */
733a1facc1fSDan Williams 	acpi_desc->fwa_state = NVDIMM_FWA_INVALID;
734a1facc1fSDan Williams 	nfit_mem->fwa_state = NVDIMM_FWA_INVALID;
735a1facc1fSDan Williams 
736a1facc1fSDan Williams 	rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
737a1facc1fSDan Williams 
738a1facc1fSDan Williams 	dev_dbg(acpi_desc->dev, "%s result: %d\n", arm == NVDIMM_FWA_ARM
739a1facc1fSDan Williams 			? "arm" : "disarm", rc);
740a1facc1fSDan Williams 	return rc;
741a1facc1fSDan Williams }
742a1facc1fSDan Williams 
743a1facc1fSDan Williams static const struct nvdimm_fw_ops __intel_fw_ops = {
744a1facc1fSDan Williams 	.activate_state = intel_fwa_state,
745a1facc1fSDan Williams 	.activate_result = intel_fwa_result,
746a1facc1fSDan Williams 	.arm = intel_fwa_arm,
747a1facc1fSDan Williams };
748a1facc1fSDan Williams 
749a1facc1fSDan Williams const struct nvdimm_fw_ops *intel_fw_ops = &__intel_fw_ops;
750