brintos

brintos / linux-shallow public Read only

0
0
Text · 19.1 KiB · 3902759 Raw
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