750 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Copyright(c) 2018 Intel Corporation. All rights reserved. */3#include <linux/libnvdimm.h>4#include <linux/ndctl.h>5#include <linux/acpi.h>6#include <linux/memregion.h>7#include <asm/smp.h>8#include "intel.h"9#include "nfit.h"10 11static ssize_t firmware_activate_noidle_show(struct device *dev,12 struct device_attribute *attr, char *buf)13{14 struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);15 struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);16 struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);17 18 return sprintf(buf, "%s\n", acpi_desc->fwa_noidle ? "Y" : "N");19}20 21static ssize_t firmware_activate_noidle_store(struct device *dev,22 struct device_attribute *attr, const char *buf, size_t size)23{24 struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);25 struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);26 struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);27 ssize_t rc;28 bool val;29 30 rc = kstrtobool(buf, &val);31 if (rc)32 return rc;33 if (val != acpi_desc->fwa_noidle)34 acpi_desc->fwa_cap = NVDIMM_FWA_CAP_INVALID;35 acpi_desc->fwa_noidle = val;36 return size;37}38DEVICE_ATTR_RW(firmware_activate_noidle);39 40bool intel_fwa_supported(struct nvdimm_bus *nvdimm_bus)41{42 struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);43 struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);44 unsigned long *mask;45 46 if (!test_bit(NVDIMM_BUS_FAMILY_INTEL, &nd_desc->bus_family_mask))47 return false;48 49 mask = &acpi_desc->family_dsm_mask[NVDIMM_BUS_FAMILY_INTEL];50 return *mask == NVDIMM_BUS_INTEL_FW_ACTIVATE_CMDMASK;51}52 53static unsigned long intel_security_flags(struct nvdimm *nvdimm,54 enum nvdimm_passphrase_type ptype)55{56 struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);57 unsigned long security_flags = 0;58 struct {59 struct nd_cmd_pkg pkg;60 struct nd_intel_get_security_state cmd;61 } nd_cmd = {62 .pkg = {63 .nd_command = NVDIMM_INTEL_GET_SECURITY_STATE,64 .nd_family = NVDIMM_FAMILY_INTEL,65 .nd_size_out =66 sizeof(struct nd_intel_get_security_state),67 .nd_fw_size =68 sizeof(struct nd_intel_get_security_state),69 },70 };71 int rc;72 73 if (!test_bit(NVDIMM_INTEL_GET_SECURITY_STATE, &nfit_mem->dsm_mask))74 return 0;75 76 /*77 * Short circuit the state retrieval while we are doing overwrite.78 * The DSM spec states that the security state is indeterminate79 * until the overwrite DSM completes.80 */81 if (nvdimm_in_overwrite(nvdimm) && ptype == NVDIMM_USER)82 return BIT(NVDIMM_SECURITY_OVERWRITE);83 84 rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);85 if (rc < 0 || nd_cmd.cmd.status) {86 pr_err("%s: security state retrieval failed (%d:%#x)\n",87 nvdimm_name(nvdimm), rc, nd_cmd.cmd.status);88 return 0;89 }90 91 /* check and see if security is enabled and locked */92 if (ptype == NVDIMM_MASTER) {93 if (nd_cmd.cmd.extended_state & ND_INTEL_SEC_ESTATE_ENABLED)94 set_bit(NVDIMM_SECURITY_UNLOCKED, &security_flags);95 else96 set_bit(NVDIMM_SECURITY_DISABLED, &security_flags);97 if (nd_cmd.cmd.extended_state & ND_INTEL_SEC_ESTATE_PLIMIT)98 set_bit(NVDIMM_SECURITY_FROZEN, &security_flags);99 return security_flags;100 }101 102 if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_UNSUPPORTED)103 return 0;104 105 if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_ENABLED) {106 if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_FROZEN ||107 nd_cmd.cmd.state & ND_INTEL_SEC_STATE_PLIMIT)108 set_bit(NVDIMM_SECURITY_FROZEN, &security_flags);109 110 if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_LOCKED)111 set_bit(NVDIMM_SECURITY_LOCKED, &security_flags);112 else113 set_bit(NVDIMM_SECURITY_UNLOCKED, &security_flags);114 } else115 set_bit(NVDIMM_SECURITY_DISABLED, &security_flags);116 117 return security_flags;118}119 120static int intel_security_freeze(struct nvdimm *nvdimm)121{122 struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);123 struct {124 struct nd_cmd_pkg pkg;125 struct nd_intel_freeze_lock cmd;126 } nd_cmd = {127 .pkg = {128 .nd_command = NVDIMM_INTEL_FREEZE_LOCK,129 .nd_family = NVDIMM_FAMILY_INTEL,130 .nd_size_out = ND_INTEL_STATUS_SIZE,131 .nd_fw_size = ND_INTEL_STATUS_SIZE,132 },133 };134 int rc;135 136 if (!test_bit(NVDIMM_INTEL_FREEZE_LOCK, &nfit_mem->dsm_mask))137 return -ENOTTY;138 139 rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);140 if (rc < 0)141 return rc;142 if (nd_cmd.cmd.status)143 return -EIO;144 return 0;145}146 147static int intel_security_change_key(struct nvdimm *nvdimm,148 const struct nvdimm_key_data *old_data,149 const struct nvdimm_key_data *new_data,150 enum nvdimm_passphrase_type ptype)151{152 struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);153 unsigned int cmd = ptype == NVDIMM_MASTER ?154 NVDIMM_INTEL_SET_MASTER_PASSPHRASE :155 NVDIMM_INTEL_SET_PASSPHRASE;156 struct {157 struct nd_cmd_pkg pkg;158 struct nd_intel_set_passphrase cmd;159 } nd_cmd = {160 .pkg = {161 .nd_family = NVDIMM_FAMILY_INTEL,162 .nd_size_in = ND_INTEL_PASSPHRASE_SIZE * 2,163 .nd_size_out = ND_INTEL_STATUS_SIZE,164 .nd_fw_size = ND_INTEL_STATUS_SIZE,165 .nd_command = cmd,166 },167 };168 int rc;169 170 if (!test_bit(cmd, &nfit_mem->dsm_mask))171 return -ENOTTY;172 173 memcpy(nd_cmd.cmd.old_pass, old_data->data,174 sizeof(nd_cmd.cmd.old_pass));175 memcpy(nd_cmd.cmd.new_pass, new_data->data,176 sizeof(nd_cmd.cmd.new_pass));177 rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);178 if (rc < 0)179 return rc;180 181 switch (nd_cmd.cmd.status) {182 case 0:183 return 0;184 case ND_INTEL_STATUS_INVALID_PASS:185 return -EINVAL;186 case ND_INTEL_STATUS_NOT_SUPPORTED:187 return -EOPNOTSUPP;188 case ND_INTEL_STATUS_INVALID_STATE:189 default:190 return -EIO;191 }192}193 194static int __maybe_unused intel_security_unlock(struct nvdimm *nvdimm,195 const struct nvdimm_key_data *key_data)196{197 struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);198 struct {199 struct nd_cmd_pkg pkg;200 struct nd_intel_unlock_unit cmd;201 } nd_cmd = {202 .pkg = {203 .nd_command = NVDIMM_INTEL_UNLOCK_UNIT,204 .nd_family = NVDIMM_FAMILY_INTEL,205 .nd_size_in = ND_INTEL_PASSPHRASE_SIZE,206 .nd_size_out = ND_INTEL_STATUS_SIZE,207 .nd_fw_size = ND_INTEL_STATUS_SIZE,208 },209 };210 int rc;211 212 if (!test_bit(NVDIMM_INTEL_UNLOCK_UNIT, &nfit_mem->dsm_mask))213 return -ENOTTY;214 215 memcpy(nd_cmd.cmd.passphrase, key_data->data,216 sizeof(nd_cmd.cmd.passphrase));217 rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);218 if (rc < 0)219 return rc;220 switch (nd_cmd.cmd.status) {221 case 0:222 break;223 case ND_INTEL_STATUS_INVALID_PASS:224 return -EINVAL;225 default:226 return -EIO;227 }228 229 return 0;230}231 232static int intel_security_disable(struct nvdimm *nvdimm,233 const struct nvdimm_key_data *key_data)234{235 int rc;236 struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);237 struct {238 struct nd_cmd_pkg pkg;239 struct nd_intel_disable_passphrase cmd;240 } nd_cmd = {241 .pkg = {242 .nd_command = NVDIMM_INTEL_DISABLE_PASSPHRASE,243 .nd_family = NVDIMM_FAMILY_INTEL,244 .nd_size_in = ND_INTEL_PASSPHRASE_SIZE,245 .nd_size_out = ND_INTEL_STATUS_SIZE,246 .nd_fw_size = ND_INTEL_STATUS_SIZE,247 },248 };249 250 if (!test_bit(NVDIMM_INTEL_DISABLE_PASSPHRASE, &nfit_mem->dsm_mask))251 return -ENOTTY;252 253 memcpy(nd_cmd.cmd.passphrase, key_data->data,254 sizeof(nd_cmd.cmd.passphrase));255 rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);256 if (rc < 0)257 return rc;258 259 switch (nd_cmd.cmd.status) {260 case 0:261 break;262 case ND_INTEL_STATUS_INVALID_PASS:263 return -EINVAL;264 case ND_INTEL_STATUS_INVALID_STATE:265 default:266 return -ENXIO;267 }268 269 return 0;270}271 272static int __maybe_unused intel_security_erase(struct nvdimm *nvdimm,273 const struct nvdimm_key_data *key,274 enum nvdimm_passphrase_type ptype)275{276 int rc;277 struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);278 unsigned int cmd = ptype == NVDIMM_MASTER ?279 NVDIMM_INTEL_MASTER_SECURE_ERASE : NVDIMM_INTEL_SECURE_ERASE;280 struct {281 struct nd_cmd_pkg pkg;282 struct nd_intel_secure_erase cmd;283 } nd_cmd = {284 .pkg = {285 .nd_family = NVDIMM_FAMILY_INTEL,286 .nd_size_in = ND_INTEL_PASSPHRASE_SIZE,287 .nd_size_out = ND_INTEL_STATUS_SIZE,288 .nd_fw_size = ND_INTEL_STATUS_SIZE,289 .nd_command = cmd,290 },291 };292 293 if (!test_bit(cmd, &nfit_mem->dsm_mask))294 return -ENOTTY;295 296 memcpy(nd_cmd.cmd.passphrase, key->data,297 sizeof(nd_cmd.cmd.passphrase));298 rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);299 if (rc < 0)300 return rc;301 302 switch (nd_cmd.cmd.status) {303 case 0:304 break;305 case ND_INTEL_STATUS_NOT_SUPPORTED:306 return -EOPNOTSUPP;307 case ND_INTEL_STATUS_INVALID_PASS:308 return -EINVAL;309 case ND_INTEL_STATUS_INVALID_STATE:310 default:311 return -ENXIO;312 }313 314 return 0;315}316 317static int __maybe_unused intel_security_query_overwrite(struct nvdimm *nvdimm)318{319 int rc;320 struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);321 struct {322 struct nd_cmd_pkg pkg;323 struct nd_intel_query_overwrite cmd;324 } nd_cmd = {325 .pkg = {326 .nd_command = NVDIMM_INTEL_QUERY_OVERWRITE,327 .nd_family = NVDIMM_FAMILY_INTEL,328 .nd_size_out = ND_INTEL_STATUS_SIZE,329 .nd_fw_size = ND_INTEL_STATUS_SIZE,330 },331 };332 333 if (!test_bit(NVDIMM_INTEL_QUERY_OVERWRITE, &nfit_mem->dsm_mask))334 return -ENOTTY;335 336 rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);337 if (rc < 0)338 return rc;339 340 switch (nd_cmd.cmd.status) {341 case 0:342 break;343 case ND_INTEL_STATUS_OQUERY_INPROGRESS:344 return -EBUSY;345 default:346 return -ENXIO;347 }348 349 return 0;350}351 352static int __maybe_unused intel_security_overwrite(struct nvdimm *nvdimm,353 const struct nvdimm_key_data *nkey)354{355 int rc;356 struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);357 struct {358 struct nd_cmd_pkg pkg;359 struct nd_intel_overwrite cmd;360 } nd_cmd = {361 .pkg = {362 .nd_command = NVDIMM_INTEL_OVERWRITE,363 .nd_family = NVDIMM_FAMILY_INTEL,364 .nd_size_in = ND_INTEL_PASSPHRASE_SIZE,365 .nd_size_out = ND_INTEL_STATUS_SIZE,366 .nd_fw_size = ND_INTEL_STATUS_SIZE,367 },368 };369 370 if (!test_bit(NVDIMM_INTEL_OVERWRITE, &nfit_mem->dsm_mask))371 return -ENOTTY;372 373 memcpy(nd_cmd.cmd.passphrase, nkey->data,374 sizeof(nd_cmd.cmd.passphrase));375 rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);376 if (rc < 0)377 return rc;378 379 switch (nd_cmd.cmd.status) {380 case 0:381 return 0;382 case ND_INTEL_STATUS_OVERWRITE_UNSUPPORTED:383 return -ENOTSUPP;384 case ND_INTEL_STATUS_INVALID_PASS:385 return -EINVAL;386 case ND_INTEL_STATUS_INVALID_STATE:387 default:388 return -ENXIO;389 }390}391 392static const struct nvdimm_security_ops __intel_security_ops = {393 .get_flags = intel_security_flags,394 .freeze = intel_security_freeze,395 .change_key = intel_security_change_key,396 .disable = intel_security_disable,397#ifdef CONFIG_X86398 .unlock = intel_security_unlock,399 .erase = intel_security_erase,400 .overwrite = intel_security_overwrite,401 .query_overwrite = intel_security_query_overwrite,402#endif403};404 405const struct nvdimm_security_ops *intel_security_ops = &__intel_security_ops;406 407static int intel_bus_fwa_businfo(struct nvdimm_bus_descriptor *nd_desc,408 struct nd_intel_bus_fw_activate_businfo *info)409{410 struct {411 struct nd_cmd_pkg pkg;412 struct nd_intel_bus_fw_activate_businfo cmd;413 } nd_cmd = {414 .pkg = {415 .nd_command = NVDIMM_BUS_INTEL_FW_ACTIVATE_BUSINFO,416 .nd_family = NVDIMM_BUS_FAMILY_INTEL,417 .nd_size_out =418 sizeof(struct nd_intel_bus_fw_activate_businfo),419 .nd_fw_size =420 sizeof(struct nd_intel_bus_fw_activate_businfo),421 },422 };423 int rc;424 425 rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd),426 NULL);427 *info = nd_cmd.cmd;428 return rc;429}430 431/* The fw_ops expect to be called with the nvdimm_bus_lock() held */432static enum nvdimm_fwa_state intel_bus_fwa_state(433 struct nvdimm_bus_descriptor *nd_desc)434{435 struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);436 struct nd_intel_bus_fw_activate_businfo info;437 struct device *dev = acpi_desc->dev;438 enum nvdimm_fwa_state state;439 int rc;440 441 /*442 * It should not be possible for platform firmware to return443 * busy because activate is a synchronous operation. Treat it444 * similar to invalid, i.e. always refresh / poll the status.445 */446 switch (acpi_desc->fwa_state) {447 case NVDIMM_FWA_INVALID:448 case NVDIMM_FWA_BUSY:449 break;450 default:451 /* check if capability needs to be refreshed */452 if (acpi_desc->fwa_cap == NVDIMM_FWA_CAP_INVALID)453 break;454 return acpi_desc->fwa_state;455 }456 457 /* Refresh with platform firmware */458 rc = intel_bus_fwa_businfo(nd_desc, &info);459 if (rc)460 return NVDIMM_FWA_INVALID;461 462 switch (info.state) {463 case ND_INTEL_FWA_IDLE:464 state = NVDIMM_FWA_IDLE;465 break;466 case ND_INTEL_FWA_BUSY:467 state = NVDIMM_FWA_BUSY;468 break;469 case ND_INTEL_FWA_ARMED:470 if (info.activate_tmo > info.max_quiesce_tmo)471 state = NVDIMM_FWA_ARM_OVERFLOW;472 else473 state = NVDIMM_FWA_ARMED;474 break;475 default:476 dev_err_once(dev, "invalid firmware activate state %d\n",477 info.state);478 return NVDIMM_FWA_INVALID;479 }480 481 /*482 * Capability data is available in the same payload as state. It483 * is expected to be static.484 */485 if (acpi_desc->fwa_cap == NVDIMM_FWA_CAP_INVALID) {486 if (info.capability & ND_INTEL_BUS_FWA_CAP_FWQUIESCE)487 acpi_desc->fwa_cap = NVDIMM_FWA_CAP_QUIESCE;488 else if (info.capability & ND_INTEL_BUS_FWA_CAP_OSQUIESCE) {489 /*490 * Skip hibernate cycle by default if platform491 * indicates that it does not need devices to be492 * quiesced.493 */494 acpi_desc->fwa_cap = NVDIMM_FWA_CAP_LIVE;495 } else496 acpi_desc->fwa_cap = NVDIMM_FWA_CAP_NONE;497 }498 499 acpi_desc->fwa_state = state;500 501 return state;502}503 504static enum nvdimm_fwa_capability intel_bus_fwa_capability(505 struct nvdimm_bus_descriptor *nd_desc)506{507 struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);508 509 if (acpi_desc->fwa_cap > NVDIMM_FWA_CAP_INVALID)510 return acpi_desc->fwa_cap;511 512 if (intel_bus_fwa_state(nd_desc) > NVDIMM_FWA_INVALID)513 return acpi_desc->fwa_cap;514 515 return NVDIMM_FWA_CAP_INVALID;516}517 518static int intel_bus_fwa_activate(struct nvdimm_bus_descriptor *nd_desc)519{520 struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);521 struct {522 struct nd_cmd_pkg pkg;523 struct nd_intel_bus_fw_activate cmd;524 } nd_cmd = {525 .pkg = {526 .nd_command = NVDIMM_BUS_INTEL_FW_ACTIVATE,527 .nd_family = NVDIMM_BUS_FAMILY_INTEL,528 .nd_size_in = sizeof(nd_cmd.cmd.iodev_state),529 .nd_size_out =530 sizeof(struct nd_intel_bus_fw_activate),531 .nd_fw_size =532 sizeof(struct nd_intel_bus_fw_activate),533 },534 /*535 * Even though activate is run from a suspended context,536 * for safety, still ask platform firmware to force537 * quiesce devices by default. Let a module538 * parameter override that policy.539 */540 .cmd = {541 .iodev_state = acpi_desc->fwa_noidle542 ? ND_INTEL_BUS_FWA_IODEV_OS_IDLE543 : ND_INTEL_BUS_FWA_IODEV_FORCE_IDLE,544 },545 };546 int rc;547 548 switch (intel_bus_fwa_state(nd_desc)) {549 case NVDIMM_FWA_ARMED:550 case NVDIMM_FWA_ARM_OVERFLOW:551 break;552 default:553 return -ENXIO;554 }555 556 rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd),557 NULL);558 559 /*560 * Whether the command succeeded, or failed, the agent checking561 * for the result needs to query the DIMMs individually.562 * Increment the activation count to invalidate all the DIMM563 * states at once (it's otherwise not possible to take564 * acpi_desc->init_mutex in this context)565 */566 acpi_desc->fwa_state = NVDIMM_FWA_INVALID;567 acpi_desc->fwa_count++;568 569 dev_dbg(acpi_desc->dev, "result: %d\n", rc);570 571 return rc;572}573 574static const struct nvdimm_bus_fw_ops __intel_bus_fw_ops = {575 .activate_state = intel_bus_fwa_state,576 .capability = intel_bus_fwa_capability,577 .activate = intel_bus_fwa_activate,578};579 580const struct nvdimm_bus_fw_ops *intel_bus_fw_ops = &__intel_bus_fw_ops;581 582static int intel_fwa_dimminfo(struct nvdimm *nvdimm,583 struct nd_intel_fw_activate_dimminfo *info)584{585 struct {586 struct nd_cmd_pkg pkg;587 struct nd_intel_fw_activate_dimminfo cmd;588 } nd_cmd = {589 .pkg = {590 .nd_command = NVDIMM_INTEL_FW_ACTIVATE_DIMMINFO,591 .nd_family = NVDIMM_FAMILY_INTEL,592 .nd_size_out =593 sizeof(struct nd_intel_fw_activate_dimminfo),594 .nd_fw_size =595 sizeof(struct nd_intel_fw_activate_dimminfo),596 },597 };598 int rc;599 600 rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);601 *info = nd_cmd.cmd;602 return rc;603}604 605static enum nvdimm_fwa_state intel_fwa_state(struct nvdimm *nvdimm)606{607 struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);608 struct acpi_nfit_desc *acpi_desc = nfit_mem->acpi_desc;609 struct nd_intel_fw_activate_dimminfo info;610 int rc;611 612 /*613 * Similar to the bus state, since activate is synchronous the614 * busy state should resolve within the context of 'activate'.615 */616 switch (nfit_mem->fwa_state) {617 case NVDIMM_FWA_INVALID:618 case NVDIMM_FWA_BUSY:619 break;620 default:621 /* If no activations occurred the old state is still valid */622 if (nfit_mem->fwa_count == acpi_desc->fwa_count)623 return nfit_mem->fwa_state;624 }625 626 rc = intel_fwa_dimminfo(nvdimm, &info);627 if (rc)628 return NVDIMM_FWA_INVALID;629 630 switch (info.state) {631 case ND_INTEL_FWA_IDLE:632 nfit_mem->fwa_state = NVDIMM_FWA_IDLE;633 break;634 case ND_INTEL_FWA_BUSY:635 nfit_mem->fwa_state = NVDIMM_FWA_BUSY;636 break;637 case ND_INTEL_FWA_ARMED:638 nfit_mem->fwa_state = NVDIMM_FWA_ARMED;639 break;640 default:641 nfit_mem->fwa_state = NVDIMM_FWA_INVALID;642 break;643 }644 645 switch (info.result) {646 case ND_INTEL_DIMM_FWA_NONE:647 nfit_mem->fwa_result = NVDIMM_FWA_RESULT_NONE;648 break;649 case ND_INTEL_DIMM_FWA_SUCCESS:650 nfit_mem->fwa_result = NVDIMM_FWA_RESULT_SUCCESS;651 break;652 case ND_INTEL_DIMM_FWA_NOTSTAGED:653 nfit_mem->fwa_result = NVDIMM_FWA_RESULT_NOTSTAGED;654 break;655 case ND_INTEL_DIMM_FWA_NEEDRESET:656 nfit_mem->fwa_result = NVDIMM_FWA_RESULT_NEEDRESET;657 break;658 case ND_INTEL_DIMM_FWA_MEDIAFAILED:659 case ND_INTEL_DIMM_FWA_ABORT:660 case ND_INTEL_DIMM_FWA_NOTSUPP:661 case ND_INTEL_DIMM_FWA_ERROR:662 default:663 nfit_mem->fwa_result = NVDIMM_FWA_RESULT_FAIL;664 break;665 }666 667 nfit_mem->fwa_count = acpi_desc->fwa_count;668 669 return nfit_mem->fwa_state;670}671 672static enum nvdimm_fwa_result intel_fwa_result(struct nvdimm *nvdimm)673{674 struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);675 struct acpi_nfit_desc *acpi_desc = nfit_mem->acpi_desc;676 677 if (nfit_mem->fwa_count == acpi_desc->fwa_count678 && nfit_mem->fwa_result > NVDIMM_FWA_RESULT_INVALID)679 return nfit_mem->fwa_result;680 681 if (intel_fwa_state(nvdimm) > NVDIMM_FWA_INVALID)682 return nfit_mem->fwa_result;683 684 return NVDIMM_FWA_RESULT_INVALID;685}686 687static int intel_fwa_arm(struct nvdimm *nvdimm, enum nvdimm_fwa_trigger arm)688{689 struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);690 struct acpi_nfit_desc *acpi_desc = nfit_mem->acpi_desc;691 struct {692 struct nd_cmd_pkg pkg;693 struct nd_intel_fw_activate_arm cmd;694 } nd_cmd = {695 .pkg = {696 .nd_command = NVDIMM_INTEL_FW_ACTIVATE_ARM,697 .nd_family = NVDIMM_FAMILY_INTEL,698 .nd_size_in = sizeof(nd_cmd.cmd.activate_arm),699 .nd_size_out =700 sizeof(struct nd_intel_fw_activate_arm),701 .nd_fw_size =702 sizeof(struct nd_intel_fw_activate_arm),703 },704 .cmd = {705 .activate_arm = arm == NVDIMM_FWA_ARM706 ? ND_INTEL_DIMM_FWA_ARM707 : ND_INTEL_DIMM_FWA_DISARM,708 },709 };710 int rc;711 712 switch (intel_fwa_state(nvdimm)) {713 case NVDIMM_FWA_INVALID:714 return -ENXIO;715 case NVDIMM_FWA_BUSY:716 return -EBUSY;717 case NVDIMM_FWA_IDLE:718 if (arm == NVDIMM_FWA_DISARM)719 return 0;720 break;721 case NVDIMM_FWA_ARMED:722 if (arm == NVDIMM_FWA_ARM)723 return 0;724 break;725 default:726 return -ENXIO;727 }728 729 /*730 * Invalidate the bus-level state, now that we're committed to731 * changing the 'arm' state.732 */733 acpi_desc->fwa_state = NVDIMM_FWA_INVALID;734 nfit_mem->fwa_state = NVDIMM_FWA_INVALID;735 736 rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);737 738 dev_dbg(acpi_desc->dev, "%s result: %d\n", arm == NVDIMM_FWA_ARM739 ? "arm" : "disarm", rc);740 return rc;741}742 743static const struct nvdimm_fw_ops __intel_fw_ops = {744 .activate_state = intel_fwa_state,745 .activate_result = intel_fwa_result,746 .arm = intel_fwa_arm,747};748 749const struct nvdimm_fw_ops *intel_fw_ops = &__intel_fw_ops;750