162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright(c) 2018 Intel Corporation. All rights reserved. */ 362306a36Sopenharmony_ci#include <linux/libnvdimm.h> 462306a36Sopenharmony_ci#include <linux/ndctl.h> 562306a36Sopenharmony_ci#include <linux/acpi.h> 662306a36Sopenharmony_ci#include <linux/memregion.h> 762306a36Sopenharmony_ci#include <asm/smp.h> 862306a36Sopenharmony_ci#include "intel.h" 962306a36Sopenharmony_ci#include "nfit.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_cistatic ssize_t firmware_activate_noidle_show(struct device *dev, 1262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 1362306a36Sopenharmony_ci{ 1462306a36Sopenharmony_ci struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev); 1562306a36Sopenharmony_ci struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus); 1662306a36Sopenharmony_ci struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci return sprintf(buf, "%s\n", acpi_desc->fwa_noidle ? "Y" : "N"); 1962306a36Sopenharmony_ci} 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic ssize_t firmware_activate_noidle_store(struct device *dev, 2262306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t size) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev); 2562306a36Sopenharmony_ci struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus); 2662306a36Sopenharmony_ci struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); 2762306a36Sopenharmony_ci ssize_t rc; 2862306a36Sopenharmony_ci bool val; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci rc = kstrtobool(buf, &val); 3162306a36Sopenharmony_ci if (rc) 3262306a36Sopenharmony_ci return rc; 3362306a36Sopenharmony_ci if (val != acpi_desc->fwa_noidle) 3462306a36Sopenharmony_ci acpi_desc->fwa_cap = NVDIMM_FWA_CAP_INVALID; 3562306a36Sopenharmony_ci acpi_desc->fwa_noidle = val; 3662306a36Sopenharmony_ci return size; 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ciDEVICE_ATTR_RW(firmware_activate_noidle); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cibool intel_fwa_supported(struct nvdimm_bus *nvdimm_bus) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus); 4362306a36Sopenharmony_ci struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); 4462306a36Sopenharmony_ci unsigned long *mask; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (!test_bit(NVDIMM_BUS_FAMILY_INTEL, &nd_desc->bus_family_mask)) 4762306a36Sopenharmony_ci return false; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci mask = &acpi_desc->family_dsm_mask[NVDIMM_BUS_FAMILY_INTEL]; 5062306a36Sopenharmony_ci return *mask == NVDIMM_BUS_INTEL_FW_ACTIVATE_CMDMASK; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic unsigned long intel_security_flags(struct nvdimm *nvdimm, 5462306a36Sopenharmony_ci enum nvdimm_passphrase_type ptype) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); 5762306a36Sopenharmony_ci unsigned long security_flags = 0; 5862306a36Sopenharmony_ci struct { 5962306a36Sopenharmony_ci struct nd_cmd_pkg pkg; 6062306a36Sopenharmony_ci struct nd_intel_get_security_state cmd; 6162306a36Sopenharmony_ci } nd_cmd = { 6262306a36Sopenharmony_ci .pkg = { 6362306a36Sopenharmony_ci .nd_command = NVDIMM_INTEL_GET_SECURITY_STATE, 6462306a36Sopenharmony_ci .nd_family = NVDIMM_FAMILY_INTEL, 6562306a36Sopenharmony_ci .nd_size_out = 6662306a36Sopenharmony_ci sizeof(struct nd_intel_get_security_state), 6762306a36Sopenharmony_ci .nd_fw_size = 6862306a36Sopenharmony_ci sizeof(struct nd_intel_get_security_state), 6962306a36Sopenharmony_ci }, 7062306a36Sopenharmony_ci }; 7162306a36Sopenharmony_ci int rc; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (!test_bit(NVDIMM_INTEL_GET_SECURITY_STATE, &nfit_mem->dsm_mask)) 7462306a36Sopenharmony_ci return 0; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* 7762306a36Sopenharmony_ci * Short circuit the state retrieval while we are doing overwrite. 7862306a36Sopenharmony_ci * The DSM spec states that the security state is indeterminate 7962306a36Sopenharmony_ci * until the overwrite DSM completes. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_ci if (nvdimm_in_overwrite(nvdimm) && ptype == NVDIMM_USER) 8262306a36Sopenharmony_ci return BIT(NVDIMM_SECURITY_OVERWRITE); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL); 8562306a36Sopenharmony_ci if (rc < 0 || nd_cmd.cmd.status) { 8662306a36Sopenharmony_ci pr_err("%s: security state retrieval failed (%d:%#x)\n", 8762306a36Sopenharmony_ci nvdimm_name(nvdimm), rc, nd_cmd.cmd.status); 8862306a36Sopenharmony_ci return 0; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* check and see if security is enabled and locked */ 9262306a36Sopenharmony_ci if (ptype == NVDIMM_MASTER) { 9362306a36Sopenharmony_ci if (nd_cmd.cmd.extended_state & ND_INTEL_SEC_ESTATE_ENABLED) 9462306a36Sopenharmony_ci set_bit(NVDIMM_SECURITY_UNLOCKED, &security_flags); 9562306a36Sopenharmony_ci else 9662306a36Sopenharmony_ci set_bit(NVDIMM_SECURITY_DISABLED, &security_flags); 9762306a36Sopenharmony_ci if (nd_cmd.cmd.extended_state & ND_INTEL_SEC_ESTATE_PLIMIT) 9862306a36Sopenharmony_ci set_bit(NVDIMM_SECURITY_FROZEN, &security_flags); 9962306a36Sopenharmony_ci return security_flags; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_UNSUPPORTED) 10362306a36Sopenharmony_ci return 0; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_ENABLED) { 10662306a36Sopenharmony_ci if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_FROZEN || 10762306a36Sopenharmony_ci nd_cmd.cmd.state & ND_INTEL_SEC_STATE_PLIMIT) 10862306a36Sopenharmony_ci set_bit(NVDIMM_SECURITY_FROZEN, &security_flags); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_LOCKED) 11162306a36Sopenharmony_ci set_bit(NVDIMM_SECURITY_LOCKED, &security_flags); 11262306a36Sopenharmony_ci else 11362306a36Sopenharmony_ci set_bit(NVDIMM_SECURITY_UNLOCKED, &security_flags); 11462306a36Sopenharmony_ci } else 11562306a36Sopenharmony_ci set_bit(NVDIMM_SECURITY_DISABLED, &security_flags); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return security_flags; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic int intel_security_freeze(struct nvdimm *nvdimm) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); 12362306a36Sopenharmony_ci struct { 12462306a36Sopenharmony_ci struct nd_cmd_pkg pkg; 12562306a36Sopenharmony_ci struct nd_intel_freeze_lock cmd; 12662306a36Sopenharmony_ci } nd_cmd = { 12762306a36Sopenharmony_ci .pkg = { 12862306a36Sopenharmony_ci .nd_command = NVDIMM_INTEL_FREEZE_LOCK, 12962306a36Sopenharmony_ci .nd_family = NVDIMM_FAMILY_INTEL, 13062306a36Sopenharmony_ci .nd_size_out = ND_INTEL_STATUS_SIZE, 13162306a36Sopenharmony_ci .nd_fw_size = ND_INTEL_STATUS_SIZE, 13262306a36Sopenharmony_ci }, 13362306a36Sopenharmony_ci }; 13462306a36Sopenharmony_ci int rc; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (!test_bit(NVDIMM_INTEL_FREEZE_LOCK, &nfit_mem->dsm_mask)) 13762306a36Sopenharmony_ci return -ENOTTY; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL); 14062306a36Sopenharmony_ci if (rc < 0) 14162306a36Sopenharmony_ci return rc; 14262306a36Sopenharmony_ci if (nd_cmd.cmd.status) 14362306a36Sopenharmony_ci return -EIO; 14462306a36Sopenharmony_ci return 0; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic int intel_security_change_key(struct nvdimm *nvdimm, 14862306a36Sopenharmony_ci const struct nvdimm_key_data *old_data, 14962306a36Sopenharmony_ci const struct nvdimm_key_data *new_data, 15062306a36Sopenharmony_ci enum nvdimm_passphrase_type ptype) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); 15362306a36Sopenharmony_ci unsigned int cmd = ptype == NVDIMM_MASTER ? 15462306a36Sopenharmony_ci NVDIMM_INTEL_SET_MASTER_PASSPHRASE : 15562306a36Sopenharmony_ci NVDIMM_INTEL_SET_PASSPHRASE; 15662306a36Sopenharmony_ci struct { 15762306a36Sopenharmony_ci struct nd_cmd_pkg pkg; 15862306a36Sopenharmony_ci struct nd_intel_set_passphrase cmd; 15962306a36Sopenharmony_ci } nd_cmd = { 16062306a36Sopenharmony_ci .pkg = { 16162306a36Sopenharmony_ci .nd_family = NVDIMM_FAMILY_INTEL, 16262306a36Sopenharmony_ci .nd_size_in = ND_INTEL_PASSPHRASE_SIZE * 2, 16362306a36Sopenharmony_ci .nd_size_out = ND_INTEL_STATUS_SIZE, 16462306a36Sopenharmony_ci .nd_fw_size = ND_INTEL_STATUS_SIZE, 16562306a36Sopenharmony_ci .nd_command = cmd, 16662306a36Sopenharmony_ci }, 16762306a36Sopenharmony_ci }; 16862306a36Sopenharmony_ci int rc; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (!test_bit(cmd, &nfit_mem->dsm_mask)) 17162306a36Sopenharmony_ci return -ENOTTY; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci memcpy(nd_cmd.cmd.old_pass, old_data->data, 17462306a36Sopenharmony_ci sizeof(nd_cmd.cmd.old_pass)); 17562306a36Sopenharmony_ci memcpy(nd_cmd.cmd.new_pass, new_data->data, 17662306a36Sopenharmony_ci sizeof(nd_cmd.cmd.new_pass)); 17762306a36Sopenharmony_ci rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL); 17862306a36Sopenharmony_ci if (rc < 0) 17962306a36Sopenharmony_ci return rc; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci switch (nd_cmd.cmd.status) { 18262306a36Sopenharmony_ci case 0: 18362306a36Sopenharmony_ci return 0; 18462306a36Sopenharmony_ci case ND_INTEL_STATUS_INVALID_PASS: 18562306a36Sopenharmony_ci return -EINVAL; 18662306a36Sopenharmony_ci case ND_INTEL_STATUS_NOT_SUPPORTED: 18762306a36Sopenharmony_ci return -EOPNOTSUPP; 18862306a36Sopenharmony_ci case ND_INTEL_STATUS_INVALID_STATE: 18962306a36Sopenharmony_ci default: 19062306a36Sopenharmony_ci return -EIO; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic int __maybe_unused intel_security_unlock(struct nvdimm *nvdimm, 19562306a36Sopenharmony_ci const struct nvdimm_key_data *key_data) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); 19862306a36Sopenharmony_ci struct { 19962306a36Sopenharmony_ci struct nd_cmd_pkg pkg; 20062306a36Sopenharmony_ci struct nd_intel_unlock_unit cmd; 20162306a36Sopenharmony_ci } nd_cmd = { 20262306a36Sopenharmony_ci .pkg = { 20362306a36Sopenharmony_ci .nd_command = NVDIMM_INTEL_UNLOCK_UNIT, 20462306a36Sopenharmony_ci .nd_family = NVDIMM_FAMILY_INTEL, 20562306a36Sopenharmony_ci .nd_size_in = ND_INTEL_PASSPHRASE_SIZE, 20662306a36Sopenharmony_ci .nd_size_out = ND_INTEL_STATUS_SIZE, 20762306a36Sopenharmony_ci .nd_fw_size = ND_INTEL_STATUS_SIZE, 20862306a36Sopenharmony_ci }, 20962306a36Sopenharmony_ci }; 21062306a36Sopenharmony_ci int rc; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (!test_bit(NVDIMM_INTEL_UNLOCK_UNIT, &nfit_mem->dsm_mask)) 21362306a36Sopenharmony_ci return -ENOTTY; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci memcpy(nd_cmd.cmd.passphrase, key_data->data, 21662306a36Sopenharmony_ci sizeof(nd_cmd.cmd.passphrase)); 21762306a36Sopenharmony_ci rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL); 21862306a36Sopenharmony_ci if (rc < 0) 21962306a36Sopenharmony_ci return rc; 22062306a36Sopenharmony_ci switch (nd_cmd.cmd.status) { 22162306a36Sopenharmony_ci case 0: 22262306a36Sopenharmony_ci break; 22362306a36Sopenharmony_ci case ND_INTEL_STATUS_INVALID_PASS: 22462306a36Sopenharmony_ci return -EINVAL; 22562306a36Sopenharmony_ci default: 22662306a36Sopenharmony_ci return -EIO; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci return 0; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic int intel_security_disable(struct nvdimm *nvdimm, 23362306a36Sopenharmony_ci const struct nvdimm_key_data *key_data) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci int rc; 23662306a36Sopenharmony_ci struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); 23762306a36Sopenharmony_ci struct { 23862306a36Sopenharmony_ci struct nd_cmd_pkg pkg; 23962306a36Sopenharmony_ci struct nd_intel_disable_passphrase cmd; 24062306a36Sopenharmony_ci } nd_cmd = { 24162306a36Sopenharmony_ci .pkg = { 24262306a36Sopenharmony_ci .nd_command = NVDIMM_INTEL_DISABLE_PASSPHRASE, 24362306a36Sopenharmony_ci .nd_family = NVDIMM_FAMILY_INTEL, 24462306a36Sopenharmony_ci .nd_size_in = ND_INTEL_PASSPHRASE_SIZE, 24562306a36Sopenharmony_ci .nd_size_out = ND_INTEL_STATUS_SIZE, 24662306a36Sopenharmony_ci .nd_fw_size = ND_INTEL_STATUS_SIZE, 24762306a36Sopenharmony_ci }, 24862306a36Sopenharmony_ci }; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (!test_bit(NVDIMM_INTEL_DISABLE_PASSPHRASE, &nfit_mem->dsm_mask)) 25162306a36Sopenharmony_ci return -ENOTTY; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci memcpy(nd_cmd.cmd.passphrase, key_data->data, 25462306a36Sopenharmony_ci sizeof(nd_cmd.cmd.passphrase)); 25562306a36Sopenharmony_ci rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL); 25662306a36Sopenharmony_ci if (rc < 0) 25762306a36Sopenharmony_ci return rc; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci switch (nd_cmd.cmd.status) { 26062306a36Sopenharmony_ci case 0: 26162306a36Sopenharmony_ci break; 26262306a36Sopenharmony_ci case ND_INTEL_STATUS_INVALID_PASS: 26362306a36Sopenharmony_ci return -EINVAL; 26462306a36Sopenharmony_ci case ND_INTEL_STATUS_INVALID_STATE: 26562306a36Sopenharmony_ci default: 26662306a36Sopenharmony_ci return -ENXIO; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci return 0; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic int __maybe_unused intel_security_erase(struct nvdimm *nvdimm, 27362306a36Sopenharmony_ci const struct nvdimm_key_data *key, 27462306a36Sopenharmony_ci enum nvdimm_passphrase_type ptype) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci int rc; 27762306a36Sopenharmony_ci struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); 27862306a36Sopenharmony_ci unsigned int cmd = ptype == NVDIMM_MASTER ? 27962306a36Sopenharmony_ci NVDIMM_INTEL_MASTER_SECURE_ERASE : NVDIMM_INTEL_SECURE_ERASE; 28062306a36Sopenharmony_ci struct { 28162306a36Sopenharmony_ci struct nd_cmd_pkg pkg; 28262306a36Sopenharmony_ci struct nd_intel_secure_erase cmd; 28362306a36Sopenharmony_ci } nd_cmd = { 28462306a36Sopenharmony_ci .pkg = { 28562306a36Sopenharmony_ci .nd_family = NVDIMM_FAMILY_INTEL, 28662306a36Sopenharmony_ci .nd_size_in = ND_INTEL_PASSPHRASE_SIZE, 28762306a36Sopenharmony_ci .nd_size_out = ND_INTEL_STATUS_SIZE, 28862306a36Sopenharmony_ci .nd_fw_size = ND_INTEL_STATUS_SIZE, 28962306a36Sopenharmony_ci .nd_command = cmd, 29062306a36Sopenharmony_ci }, 29162306a36Sopenharmony_ci }; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (!test_bit(cmd, &nfit_mem->dsm_mask)) 29462306a36Sopenharmony_ci return -ENOTTY; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci memcpy(nd_cmd.cmd.passphrase, key->data, 29762306a36Sopenharmony_ci sizeof(nd_cmd.cmd.passphrase)); 29862306a36Sopenharmony_ci rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL); 29962306a36Sopenharmony_ci if (rc < 0) 30062306a36Sopenharmony_ci return rc; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci switch (nd_cmd.cmd.status) { 30362306a36Sopenharmony_ci case 0: 30462306a36Sopenharmony_ci break; 30562306a36Sopenharmony_ci case ND_INTEL_STATUS_NOT_SUPPORTED: 30662306a36Sopenharmony_ci return -EOPNOTSUPP; 30762306a36Sopenharmony_ci case ND_INTEL_STATUS_INVALID_PASS: 30862306a36Sopenharmony_ci return -EINVAL; 30962306a36Sopenharmony_ci case ND_INTEL_STATUS_INVALID_STATE: 31062306a36Sopenharmony_ci default: 31162306a36Sopenharmony_ci return -ENXIO; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return 0; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic int __maybe_unused intel_security_query_overwrite(struct nvdimm *nvdimm) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci int rc; 32062306a36Sopenharmony_ci struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); 32162306a36Sopenharmony_ci struct { 32262306a36Sopenharmony_ci struct nd_cmd_pkg pkg; 32362306a36Sopenharmony_ci struct nd_intel_query_overwrite cmd; 32462306a36Sopenharmony_ci } nd_cmd = { 32562306a36Sopenharmony_ci .pkg = { 32662306a36Sopenharmony_ci .nd_command = NVDIMM_INTEL_QUERY_OVERWRITE, 32762306a36Sopenharmony_ci .nd_family = NVDIMM_FAMILY_INTEL, 32862306a36Sopenharmony_ci .nd_size_out = ND_INTEL_STATUS_SIZE, 32962306a36Sopenharmony_ci .nd_fw_size = ND_INTEL_STATUS_SIZE, 33062306a36Sopenharmony_ci }, 33162306a36Sopenharmony_ci }; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (!test_bit(NVDIMM_INTEL_QUERY_OVERWRITE, &nfit_mem->dsm_mask)) 33462306a36Sopenharmony_ci return -ENOTTY; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL); 33762306a36Sopenharmony_ci if (rc < 0) 33862306a36Sopenharmony_ci return rc; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci switch (nd_cmd.cmd.status) { 34162306a36Sopenharmony_ci case 0: 34262306a36Sopenharmony_ci break; 34362306a36Sopenharmony_ci case ND_INTEL_STATUS_OQUERY_INPROGRESS: 34462306a36Sopenharmony_ci return -EBUSY; 34562306a36Sopenharmony_ci default: 34662306a36Sopenharmony_ci return -ENXIO; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic int __maybe_unused intel_security_overwrite(struct nvdimm *nvdimm, 35362306a36Sopenharmony_ci const struct nvdimm_key_data *nkey) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci int rc; 35662306a36Sopenharmony_ci struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); 35762306a36Sopenharmony_ci struct { 35862306a36Sopenharmony_ci struct nd_cmd_pkg pkg; 35962306a36Sopenharmony_ci struct nd_intel_overwrite cmd; 36062306a36Sopenharmony_ci } nd_cmd = { 36162306a36Sopenharmony_ci .pkg = { 36262306a36Sopenharmony_ci .nd_command = NVDIMM_INTEL_OVERWRITE, 36362306a36Sopenharmony_ci .nd_family = NVDIMM_FAMILY_INTEL, 36462306a36Sopenharmony_ci .nd_size_in = ND_INTEL_PASSPHRASE_SIZE, 36562306a36Sopenharmony_ci .nd_size_out = ND_INTEL_STATUS_SIZE, 36662306a36Sopenharmony_ci .nd_fw_size = ND_INTEL_STATUS_SIZE, 36762306a36Sopenharmony_ci }, 36862306a36Sopenharmony_ci }; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (!test_bit(NVDIMM_INTEL_OVERWRITE, &nfit_mem->dsm_mask)) 37162306a36Sopenharmony_ci return -ENOTTY; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci memcpy(nd_cmd.cmd.passphrase, nkey->data, 37462306a36Sopenharmony_ci sizeof(nd_cmd.cmd.passphrase)); 37562306a36Sopenharmony_ci rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL); 37662306a36Sopenharmony_ci if (rc < 0) 37762306a36Sopenharmony_ci return rc; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci switch (nd_cmd.cmd.status) { 38062306a36Sopenharmony_ci case 0: 38162306a36Sopenharmony_ci return 0; 38262306a36Sopenharmony_ci case ND_INTEL_STATUS_OVERWRITE_UNSUPPORTED: 38362306a36Sopenharmony_ci return -ENOTSUPP; 38462306a36Sopenharmony_ci case ND_INTEL_STATUS_INVALID_PASS: 38562306a36Sopenharmony_ci return -EINVAL; 38662306a36Sopenharmony_ci case ND_INTEL_STATUS_INVALID_STATE: 38762306a36Sopenharmony_ci default: 38862306a36Sopenharmony_ci return -ENXIO; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic const struct nvdimm_security_ops __intel_security_ops = { 39362306a36Sopenharmony_ci .get_flags = intel_security_flags, 39462306a36Sopenharmony_ci .freeze = intel_security_freeze, 39562306a36Sopenharmony_ci .change_key = intel_security_change_key, 39662306a36Sopenharmony_ci .disable = intel_security_disable, 39762306a36Sopenharmony_ci#ifdef CONFIG_X86 39862306a36Sopenharmony_ci .unlock = intel_security_unlock, 39962306a36Sopenharmony_ci .erase = intel_security_erase, 40062306a36Sopenharmony_ci .overwrite = intel_security_overwrite, 40162306a36Sopenharmony_ci .query_overwrite = intel_security_query_overwrite, 40262306a36Sopenharmony_ci#endif 40362306a36Sopenharmony_ci}; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ciconst struct nvdimm_security_ops *intel_security_ops = &__intel_security_ops; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic int intel_bus_fwa_businfo(struct nvdimm_bus_descriptor *nd_desc, 40862306a36Sopenharmony_ci struct nd_intel_bus_fw_activate_businfo *info) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci struct { 41162306a36Sopenharmony_ci struct nd_cmd_pkg pkg; 41262306a36Sopenharmony_ci struct nd_intel_bus_fw_activate_businfo cmd; 41362306a36Sopenharmony_ci } nd_cmd = { 41462306a36Sopenharmony_ci .pkg = { 41562306a36Sopenharmony_ci .nd_command = NVDIMM_BUS_INTEL_FW_ACTIVATE_BUSINFO, 41662306a36Sopenharmony_ci .nd_family = NVDIMM_BUS_FAMILY_INTEL, 41762306a36Sopenharmony_ci .nd_size_out = 41862306a36Sopenharmony_ci sizeof(struct nd_intel_bus_fw_activate_businfo), 41962306a36Sopenharmony_ci .nd_fw_size = 42062306a36Sopenharmony_ci sizeof(struct nd_intel_bus_fw_activate_businfo), 42162306a36Sopenharmony_ci }, 42262306a36Sopenharmony_ci }; 42362306a36Sopenharmony_ci int rc; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), 42662306a36Sopenharmony_ci NULL); 42762306a36Sopenharmony_ci *info = nd_cmd.cmd; 42862306a36Sopenharmony_ci return rc; 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci/* The fw_ops expect to be called with the nvdimm_bus_lock() held */ 43262306a36Sopenharmony_cistatic enum nvdimm_fwa_state intel_bus_fwa_state( 43362306a36Sopenharmony_ci struct nvdimm_bus_descriptor *nd_desc) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); 43662306a36Sopenharmony_ci struct nd_intel_bus_fw_activate_businfo info; 43762306a36Sopenharmony_ci struct device *dev = acpi_desc->dev; 43862306a36Sopenharmony_ci enum nvdimm_fwa_state state; 43962306a36Sopenharmony_ci int rc; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci /* 44262306a36Sopenharmony_ci * It should not be possible for platform firmware to return 44362306a36Sopenharmony_ci * busy because activate is a synchronous operation. Treat it 44462306a36Sopenharmony_ci * similar to invalid, i.e. always refresh / poll the status. 44562306a36Sopenharmony_ci */ 44662306a36Sopenharmony_ci switch (acpi_desc->fwa_state) { 44762306a36Sopenharmony_ci case NVDIMM_FWA_INVALID: 44862306a36Sopenharmony_ci case NVDIMM_FWA_BUSY: 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci default: 45162306a36Sopenharmony_ci /* check if capability needs to be refreshed */ 45262306a36Sopenharmony_ci if (acpi_desc->fwa_cap == NVDIMM_FWA_CAP_INVALID) 45362306a36Sopenharmony_ci break; 45462306a36Sopenharmony_ci return acpi_desc->fwa_state; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* Refresh with platform firmware */ 45862306a36Sopenharmony_ci rc = intel_bus_fwa_businfo(nd_desc, &info); 45962306a36Sopenharmony_ci if (rc) 46062306a36Sopenharmony_ci return NVDIMM_FWA_INVALID; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci switch (info.state) { 46362306a36Sopenharmony_ci case ND_INTEL_FWA_IDLE: 46462306a36Sopenharmony_ci state = NVDIMM_FWA_IDLE; 46562306a36Sopenharmony_ci break; 46662306a36Sopenharmony_ci case ND_INTEL_FWA_BUSY: 46762306a36Sopenharmony_ci state = NVDIMM_FWA_BUSY; 46862306a36Sopenharmony_ci break; 46962306a36Sopenharmony_ci case ND_INTEL_FWA_ARMED: 47062306a36Sopenharmony_ci if (info.activate_tmo > info.max_quiesce_tmo) 47162306a36Sopenharmony_ci state = NVDIMM_FWA_ARM_OVERFLOW; 47262306a36Sopenharmony_ci else 47362306a36Sopenharmony_ci state = NVDIMM_FWA_ARMED; 47462306a36Sopenharmony_ci break; 47562306a36Sopenharmony_ci default: 47662306a36Sopenharmony_ci dev_err_once(dev, "invalid firmware activate state %d\n", 47762306a36Sopenharmony_ci info.state); 47862306a36Sopenharmony_ci return NVDIMM_FWA_INVALID; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci /* 48262306a36Sopenharmony_ci * Capability data is available in the same payload as state. It 48362306a36Sopenharmony_ci * is expected to be static. 48462306a36Sopenharmony_ci */ 48562306a36Sopenharmony_ci if (acpi_desc->fwa_cap == NVDIMM_FWA_CAP_INVALID) { 48662306a36Sopenharmony_ci if (info.capability & ND_INTEL_BUS_FWA_CAP_FWQUIESCE) 48762306a36Sopenharmony_ci acpi_desc->fwa_cap = NVDIMM_FWA_CAP_QUIESCE; 48862306a36Sopenharmony_ci else if (info.capability & ND_INTEL_BUS_FWA_CAP_OSQUIESCE) { 48962306a36Sopenharmony_ci /* 49062306a36Sopenharmony_ci * Skip hibernate cycle by default if platform 49162306a36Sopenharmony_ci * indicates that it does not need devices to be 49262306a36Sopenharmony_ci * quiesced. 49362306a36Sopenharmony_ci */ 49462306a36Sopenharmony_ci acpi_desc->fwa_cap = NVDIMM_FWA_CAP_LIVE; 49562306a36Sopenharmony_ci } else 49662306a36Sopenharmony_ci acpi_desc->fwa_cap = NVDIMM_FWA_CAP_NONE; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci acpi_desc->fwa_state = state; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci return state; 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cistatic enum nvdimm_fwa_capability intel_bus_fwa_capability( 50562306a36Sopenharmony_ci struct nvdimm_bus_descriptor *nd_desc) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci if (acpi_desc->fwa_cap > NVDIMM_FWA_CAP_INVALID) 51062306a36Sopenharmony_ci return acpi_desc->fwa_cap; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (intel_bus_fwa_state(nd_desc) > NVDIMM_FWA_INVALID) 51362306a36Sopenharmony_ci return acpi_desc->fwa_cap; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci return NVDIMM_FWA_CAP_INVALID; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cistatic int intel_bus_fwa_activate(struct nvdimm_bus_descriptor *nd_desc) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); 52162306a36Sopenharmony_ci struct { 52262306a36Sopenharmony_ci struct nd_cmd_pkg pkg; 52362306a36Sopenharmony_ci struct nd_intel_bus_fw_activate cmd; 52462306a36Sopenharmony_ci } nd_cmd = { 52562306a36Sopenharmony_ci .pkg = { 52662306a36Sopenharmony_ci .nd_command = NVDIMM_BUS_INTEL_FW_ACTIVATE, 52762306a36Sopenharmony_ci .nd_family = NVDIMM_BUS_FAMILY_INTEL, 52862306a36Sopenharmony_ci .nd_size_in = sizeof(nd_cmd.cmd.iodev_state), 52962306a36Sopenharmony_ci .nd_size_out = 53062306a36Sopenharmony_ci sizeof(struct nd_intel_bus_fw_activate), 53162306a36Sopenharmony_ci .nd_fw_size = 53262306a36Sopenharmony_ci sizeof(struct nd_intel_bus_fw_activate), 53362306a36Sopenharmony_ci }, 53462306a36Sopenharmony_ci /* 53562306a36Sopenharmony_ci * Even though activate is run from a suspended context, 53662306a36Sopenharmony_ci * for safety, still ask platform firmware to force 53762306a36Sopenharmony_ci * quiesce devices by default. Let a module 53862306a36Sopenharmony_ci * parameter override that policy. 53962306a36Sopenharmony_ci */ 54062306a36Sopenharmony_ci .cmd = { 54162306a36Sopenharmony_ci .iodev_state = acpi_desc->fwa_noidle 54262306a36Sopenharmony_ci ? ND_INTEL_BUS_FWA_IODEV_OS_IDLE 54362306a36Sopenharmony_ci : ND_INTEL_BUS_FWA_IODEV_FORCE_IDLE, 54462306a36Sopenharmony_ci }, 54562306a36Sopenharmony_ci }; 54662306a36Sopenharmony_ci int rc; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci switch (intel_bus_fwa_state(nd_desc)) { 54962306a36Sopenharmony_ci case NVDIMM_FWA_ARMED: 55062306a36Sopenharmony_ci case NVDIMM_FWA_ARM_OVERFLOW: 55162306a36Sopenharmony_ci break; 55262306a36Sopenharmony_ci default: 55362306a36Sopenharmony_ci return -ENXIO; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), 55762306a36Sopenharmony_ci NULL); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* 56062306a36Sopenharmony_ci * Whether the command succeeded, or failed, the agent checking 56162306a36Sopenharmony_ci * for the result needs to query the DIMMs individually. 56262306a36Sopenharmony_ci * Increment the activation count to invalidate all the DIMM 56362306a36Sopenharmony_ci * states at once (it's otherwise not possible to take 56462306a36Sopenharmony_ci * acpi_desc->init_mutex in this context) 56562306a36Sopenharmony_ci */ 56662306a36Sopenharmony_ci acpi_desc->fwa_state = NVDIMM_FWA_INVALID; 56762306a36Sopenharmony_ci acpi_desc->fwa_count++; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci dev_dbg(acpi_desc->dev, "result: %d\n", rc); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci return rc; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic const struct nvdimm_bus_fw_ops __intel_bus_fw_ops = { 57562306a36Sopenharmony_ci .activate_state = intel_bus_fwa_state, 57662306a36Sopenharmony_ci .capability = intel_bus_fwa_capability, 57762306a36Sopenharmony_ci .activate = intel_bus_fwa_activate, 57862306a36Sopenharmony_ci}; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ciconst struct nvdimm_bus_fw_ops *intel_bus_fw_ops = &__intel_bus_fw_ops; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic int intel_fwa_dimminfo(struct nvdimm *nvdimm, 58362306a36Sopenharmony_ci struct nd_intel_fw_activate_dimminfo *info) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci struct { 58662306a36Sopenharmony_ci struct nd_cmd_pkg pkg; 58762306a36Sopenharmony_ci struct nd_intel_fw_activate_dimminfo cmd; 58862306a36Sopenharmony_ci } nd_cmd = { 58962306a36Sopenharmony_ci .pkg = { 59062306a36Sopenharmony_ci .nd_command = NVDIMM_INTEL_FW_ACTIVATE_DIMMINFO, 59162306a36Sopenharmony_ci .nd_family = NVDIMM_FAMILY_INTEL, 59262306a36Sopenharmony_ci .nd_size_out = 59362306a36Sopenharmony_ci sizeof(struct nd_intel_fw_activate_dimminfo), 59462306a36Sopenharmony_ci .nd_fw_size = 59562306a36Sopenharmony_ci sizeof(struct nd_intel_fw_activate_dimminfo), 59662306a36Sopenharmony_ci }, 59762306a36Sopenharmony_ci }; 59862306a36Sopenharmony_ci int rc; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL); 60162306a36Sopenharmony_ci *info = nd_cmd.cmd; 60262306a36Sopenharmony_ci return rc; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistatic enum nvdimm_fwa_state intel_fwa_state(struct nvdimm *nvdimm) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); 60862306a36Sopenharmony_ci struct acpi_nfit_desc *acpi_desc = nfit_mem->acpi_desc; 60962306a36Sopenharmony_ci struct nd_intel_fw_activate_dimminfo info; 61062306a36Sopenharmony_ci int rc; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* 61362306a36Sopenharmony_ci * Similar to the bus state, since activate is synchronous the 61462306a36Sopenharmony_ci * busy state should resolve within the context of 'activate'. 61562306a36Sopenharmony_ci */ 61662306a36Sopenharmony_ci switch (nfit_mem->fwa_state) { 61762306a36Sopenharmony_ci case NVDIMM_FWA_INVALID: 61862306a36Sopenharmony_ci case NVDIMM_FWA_BUSY: 61962306a36Sopenharmony_ci break; 62062306a36Sopenharmony_ci default: 62162306a36Sopenharmony_ci /* If no activations occurred the old state is still valid */ 62262306a36Sopenharmony_ci if (nfit_mem->fwa_count == acpi_desc->fwa_count) 62362306a36Sopenharmony_ci return nfit_mem->fwa_state; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci rc = intel_fwa_dimminfo(nvdimm, &info); 62762306a36Sopenharmony_ci if (rc) 62862306a36Sopenharmony_ci return NVDIMM_FWA_INVALID; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci switch (info.state) { 63162306a36Sopenharmony_ci case ND_INTEL_FWA_IDLE: 63262306a36Sopenharmony_ci nfit_mem->fwa_state = NVDIMM_FWA_IDLE; 63362306a36Sopenharmony_ci break; 63462306a36Sopenharmony_ci case ND_INTEL_FWA_BUSY: 63562306a36Sopenharmony_ci nfit_mem->fwa_state = NVDIMM_FWA_BUSY; 63662306a36Sopenharmony_ci break; 63762306a36Sopenharmony_ci case ND_INTEL_FWA_ARMED: 63862306a36Sopenharmony_ci nfit_mem->fwa_state = NVDIMM_FWA_ARMED; 63962306a36Sopenharmony_ci break; 64062306a36Sopenharmony_ci default: 64162306a36Sopenharmony_ci nfit_mem->fwa_state = NVDIMM_FWA_INVALID; 64262306a36Sopenharmony_ci break; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci switch (info.result) { 64662306a36Sopenharmony_ci case ND_INTEL_DIMM_FWA_NONE: 64762306a36Sopenharmony_ci nfit_mem->fwa_result = NVDIMM_FWA_RESULT_NONE; 64862306a36Sopenharmony_ci break; 64962306a36Sopenharmony_ci case ND_INTEL_DIMM_FWA_SUCCESS: 65062306a36Sopenharmony_ci nfit_mem->fwa_result = NVDIMM_FWA_RESULT_SUCCESS; 65162306a36Sopenharmony_ci break; 65262306a36Sopenharmony_ci case ND_INTEL_DIMM_FWA_NOTSTAGED: 65362306a36Sopenharmony_ci nfit_mem->fwa_result = NVDIMM_FWA_RESULT_NOTSTAGED; 65462306a36Sopenharmony_ci break; 65562306a36Sopenharmony_ci case ND_INTEL_DIMM_FWA_NEEDRESET: 65662306a36Sopenharmony_ci nfit_mem->fwa_result = NVDIMM_FWA_RESULT_NEEDRESET; 65762306a36Sopenharmony_ci break; 65862306a36Sopenharmony_ci case ND_INTEL_DIMM_FWA_MEDIAFAILED: 65962306a36Sopenharmony_ci case ND_INTEL_DIMM_FWA_ABORT: 66062306a36Sopenharmony_ci case ND_INTEL_DIMM_FWA_NOTSUPP: 66162306a36Sopenharmony_ci case ND_INTEL_DIMM_FWA_ERROR: 66262306a36Sopenharmony_ci default: 66362306a36Sopenharmony_ci nfit_mem->fwa_result = NVDIMM_FWA_RESULT_FAIL; 66462306a36Sopenharmony_ci break; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci nfit_mem->fwa_count = acpi_desc->fwa_count; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci return nfit_mem->fwa_state; 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_cistatic enum nvdimm_fwa_result intel_fwa_result(struct nvdimm *nvdimm) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); 67562306a36Sopenharmony_ci struct acpi_nfit_desc *acpi_desc = nfit_mem->acpi_desc; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci if (nfit_mem->fwa_count == acpi_desc->fwa_count 67862306a36Sopenharmony_ci && nfit_mem->fwa_result > NVDIMM_FWA_RESULT_INVALID) 67962306a36Sopenharmony_ci return nfit_mem->fwa_result; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (intel_fwa_state(nvdimm) > NVDIMM_FWA_INVALID) 68262306a36Sopenharmony_ci return nfit_mem->fwa_result; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci return NVDIMM_FWA_RESULT_INVALID; 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic int intel_fwa_arm(struct nvdimm *nvdimm, enum nvdimm_fwa_trigger arm) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); 69062306a36Sopenharmony_ci struct acpi_nfit_desc *acpi_desc = nfit_mem->acpi_desc; 69162306a36Sopenharmony_ci struct { 69262306a36Sopenharmony_ci struct nd_cmd_pkg pkg; 69362306a36Sopenharmony_ci struct nd_intel_fw_activate_arm cmd; 69462306a36Sopenharmony_ci } nd_cmd = { 69562306a36Sopenharmony_ci .pkg = { 69662306a36Sopenharmony_ci .nd_command = NVDIMM_INTEL_FW_ACTIVATE_ARM, 69762306a36Sopenharmony_ci .nd_family = NVDIMM_FAMILY_INTEL, 69862306a36Sopenharmony_ci .nd_size_in = sizeof(nd_cmd.cmd.activate_arm), 69962306a36Sopenharmony_ci .nd_size_out = 70062306a36Sopenharmony_ci sizeof(struct nd_intel_fw_activate_arm), 70162306a36Sopenharmony_ci .nd_fw_size = 70262306a36Sopenharmony_ci sizeof(struct nd_intel_fw_activate_arm), 70362306a36Sopenharmony_ci }, 70462306a36Sopenharmony_ci .cmd = { 70562306a36Sopenharmony_ci .activate_arm = arm == NVDIMM_FWA_ARM 70662306a36Sopenharmony_ci ? ND_INTEL_DIMM_FWA_ARM 70762306a36Sopenharmony_ci : ND_INTEL_DIMM_FWA_DISARM, 70862306a36Sopenharmony_ci }, 70962306a36Sopenharmony_ci }; 71062306a36Sopenharmony_ci int rc; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci switch (intel_fwa_state(nvdimm)) { 71362306a36Sopenharmony_ci case NVDIMM_FWA_INVALID: 71462306a36Sopenharmony_ci return -ENXIO; 71562306a36Sopenharmony_ci case NVDIMM_FWA_BUSY: 71662306a36Sopenharmony_ci return -EBUSY; 71762306a36Sopenharmony_ci case NVDIMM_FWA_IDLE: 71862306a36Sopenharmony_ci if (arm == NVDIMM_FWA_DISARM) 71962306a36Sopenharmony_ci return 0; 72062306a36Sopenharmony_ci break; 72162306a36Sopenharmony_ci case NVDIMM_FWA_ARMED: 72262306a36Sopenharmony_ci if (arm == NVDIMM_FWA_ARM) 72362306a36Sopenharmony_ci return 0; 72462306a36Sopenharmony_ci break; 72562306a36Sopenharmony_ci default: 72662306a36Sopenharmony_ci return -ENXIO; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci /* 73062306a36Sopenharmony_ci * Invalidate the bus-level state, now that we're committed to 73162306a36Sopenharmony_ci * changing the 'arm' state. 73262306a36Sopenharmony_ci */ 73362306a36Sopenharmony_ci acpi_desc->fwa_state = NVDIMM_FWA_INVALID; 73462306a36Sopenharmony_ci nfit_mem->fwa_state = NVDIMM_FWA_INVALID; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci dev_dbg(acpi_desc->dev, "%s result: %d\n", arm == NVDIMM_FWA_ARM 73962306a36Sopenharmony_ci ? "arm" : "disarm", rc); 74062306a36Sopenharmony_ci return rc; 74162306a36Sopenharmony_ci} 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_cistatic const struct nvdimm_fw_ops __intel_fw_ops = { 74462306a36Sopenharmony_ci .activate_state = intel_fwa_state, 74562306a36Sopenharmony_ci .activate_result = intel_fwa_result, 74662306a36Sopenharmony_ci .arm = intel_fwa_arm, 74762306a36Sopenharmony_ci}; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ciconst struct nvdimm_fw_ops *intel_fw_ops = &__intel_fw_ops; 750