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