diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-12-28 15:05:13 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-12-28 15:05:13 -0800 |
commit | 75f95da078b2891cd186f074ffc15a8e7c3f082d (patch) | |
tree | 2ba8da10d0d1e20565e893a777d4349fe9d483dd /drivers/acpi | |
parent | 4ed7bdc1eb4c82cf4bfdf6a94dd36fd695f6f387 (diff) | |
parent | 4b5f747e82b12b6d8ab815fc259827a615c7f2c3 (diff) | |
download | linux-75f95da078b2891cd186f074ffc15a8e7c3f082d.tar.gz linux-75f95da078b2891cd186f074ffc15a8e7c3f082d.tar.bz2 linux-75f95da078b2891cd186f074ffc15a8e7c3f082d.zip |
Merge tag 'libnvdimm-for-4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm
Pull libnvdimm updates from Dan Williams:
"The vast bulk of this update is the new support for the security
capabilities of some nvdimms.
The userspace tooling for this capability is still a work in progress,
but the changes survive the existing libnvdimm unit tests. The changes
also pass manual checkout on hardware and the new nfit_test emulation
of the security capability.
The touches of the security/keys/ files have received the necessary
acks from Mimi and David. Those changes were necessary to allow for a
new generic encrypted-key type, and allow the nvdimm sub-system to
lookup key material referenced by the libnvdimm-sysfs interface.
Summary:
- Add support for the security features of nvdimm devices that
implement a security model similar to ATA hard drive security. The
security model supports locking access to the media at
device-power-loss, to be unlocked with a passphrase, and
secure-erase (crypto-scramble).
Unlike the ATA security case where the kernel expects device
security to be managed in a pre-OS environment, the libnvdimm
security implementation allows key provisioning and key-operations
at OS runtime. Keys are managed with the kernel's encrypted-keys
facility to provide data-at-rest security for the libnvdimm key
material. The usage model mirrors fscrypt key management, but is
driven via libnvdimm sysfs.
- Miscellaneous updates for api usage and comment fixes"
* tag 'libnvdimm-for-4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm: (21 commits)
libnvdimm/security: Quiet security operations
libnvdimm/security: Add documentation for nvdimm security support
tools/testing/nvdimm: add Intel DSM 1.8 support for nfit_test
tools/testing/nvdimm: Add overwrite support for nfit_test
tools/testing/nvdimm: Add test support for Intel nvdimm security DSMs
acpi/nfit, libnvdimm/security: add Intel DSM 1.8 master passphrase support
acpi/nfit, libnvdimm/security: Add security DSM overwrite support
acpi/nfit, libnvdimm: Add support for issue secure erase DSM to Intel nvdimm
acpi/nfit, libnvdimm: Add enable/update passphrase support for Intel nvdimms
acpi/nfit, libnvdimm: Add disable passphrase support to Intel nvdimm.
acpi/nfit, libnvdimm: Add unlock of nvdimm support for Intel DIMMs
acpi/nfit, libnvdimm: Add freeze security support to Intel nvdimm
acpi/nfit, libnvdimm: Introduce nvdimm_security_ops
keys-encrypted: add nvdimm key format type to encrypted keys
keys: Export lookup_user_key to external users
acpi/nfit, libnvdimm: Store dimm id as a member to struct nvdimm
libnvdimm, namespace: Replace kmemdup() with kstrndup()
libnvdimm, label: Switch to bitmap_zalloc()
ACPI/nfit: Adjust annotation for why return 0 if fail to find NFIT at start
libnvdimm, bus: Check id immediately following ida_simple_get
...
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/nfit/Kconfig | 11 | ||||
-rw-r--r-- | drivers/acpi/nfit/Makefile | 1 | ||||
-rw-r--r-- | drivers/acpi/nfit/core.c | 103 | ||||
-rw-r--r-- | drivers/acpi/nfit/intel.c | 388 | ||||
-rw-r--r-- | drivers/acpi/nfit/intel.h | 76 | ||||
-rw-r--r-- | drivers/acpi/nfit/nfit.h | 24 |
6 files changed, 584 insertions, 19 deletions
diff --git a/drivers/acpi/nfit/Kconfig b/drivers/acpi/nfit/Kconfig index f7c57e33499e..52eefd732cf2 100644 --- a/drivers/acpi/nfit/Kconfig +++ b/drivers/acpi/nfit/Kconfig @@ -13,3 +13,14 @@ config ACPI_NFIT To compile this driver as a module, choose M here: the module will be called nfit. + +config NFIT_SECURITY_DEBUG + bool "Enable debug for NVDIMM security commands" + depends on ACPI_NFIT + help + Some NVDIMM devices and controllers support encryption and + other security features. The payloads for the commands that + enable those features may contain sensitive clear-text + security material. Disable debug of those command payloads + by default. If you are a kernel developer actively working + on NVDIMM security enabling say Y, otherwise say N. diff --git a/drivers/acpi/nfit/Makefile b/drivers/acpi/nfit/Makefile index a407e769f103..751081c47886 100644 --- a/drivers/acpi/nfit/Makefile +++ b/drivers/acpi/nfit/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_ACPI_NFIT) := nfit.o nfit-y := core.o +nfit-y += intel.o nfit-$(CONFIG_X86_MCE) += mce.o diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 5912d30020c7..011d3db19c80 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -24,6 +24,7 @@ #include <linux/nd.h> #include <asm/cacheflush.h> #include <acpi/nfit.h> +#include "intel.h" #include "nfit.h" #include "intel.h" @@ -380,6 +381,16 @@ static u8 nfit_dsm_revid(unsigned family, unsigned func) [NVDIMM_INTEL_QUERY_FWUPDATE] = 2, [NVDIMM_INTEL_SET_THRESHOLD] = 2, [NVDIMM_INTEL_INJECT_ERROR] = 2, + [NVDIMM_INTEL_GET_SECURITY_STATE] = 2, + [NVDIMM_INTEL_SET_PASSPHRASE] = 2, + [NVDIMM_INTEL_DISABLE_PASSPHRASE] = 2, + [NVDIMM_INTEL_UNLOCK_UNIT] = 2, + [NVDIMM_INTEL_FREEZE_LOCK] = 2, + [NVDIMM_INTEL_SECURE_ERASE] = 2, + [NVDIMM_INTEL_OVERWRITE] = 2, + [NVDIMM_INTEL_QUERY_OVERWRITE] = 2, + [NVDIMM_INTEL_SET_MASTER_PASSPHRASE] = 2, + [NVDIMM_INTEL_MASTER_SECURE_ERASE] = 2, }, }; u8 id; @@ -394,6 +405,17 @@ static u8 nfit_dsm_revid(unsigned family, unsigned func) return id; } +static bool payload_dumpable(struct nvdimm *nvdimm, unsigned int func) +{ + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); + + if (nfit_mem && nfit_mem->family == NVDIMM_FAMILY_INTEL + && func >= NVDIMM_INTEL_GET_SECURITY_STATE + && func <= NVDIMM_INTEL_MASTER_SECURE_ERASE) + return IS_ENABLED(CONFIG_NFIT_SECURITY_DEBUG); + return true; +} + int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc) { @@ -478,9 +500,10 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, dev_dbg(dev, "%s cmd: %d: func: %d input length: %d\n", dimm_name, cmd, func, in_buf.buffer.length); - print_hex_dump_debug("nvdimm in ", DUMP_PREFIX_OFFSET, 4, 4, - in_buf.buffer.pointer, - min_t(u32, 256, in_buf.buffer.length), true); + if (payload_dumpable(nvdimm, func)) + print_hex_dump_debug("nvdimm in ", DUMP_PREFIX_OFFSET, 4, 4, + in_buf.buffer.pointer, + min_t(u32, 256, in_buf.buffer.length), true); /* call the BIOS, prefer the named methods over _DSM if available */ if (nvdimm && cmd == ND_CMD_GET_CONFIG_SIZE @@ -1573,18 +1596,10 @@ static DEVICE_ATTR_RO(flags); static ssize_t id_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev); + struct nvdimm *nvdimm = to_nvdimm(dev); + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); - if (dcr->valid_fields & ACPI_NFIT_CONTROL_MFG_INFO_VALID) - return sprintf(buf, "%04x-%02x-%04x-%08x\n", - be16_to_cpu(dcr->vendor_id), - dcr->manufacturing_location, - be16_to_cpu(dcr->manufacturing_date), - be32_to_cpu(dcr->serial_number)); - else - return sprintf(buf, "%04x-%08x\n", - be16_to_cpu(dcr->vendor_id), - be32_to_cpu(dcr->serial_number)); + return sprintf(buf, "%s\n", nfit_mem->id); } static DEVICE_ATTR_RO(id); @@ -1780,10 +1795,23 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, const guid_t *guid; int i; int family = -1; + struct acpi_nfit_control_region *dcr = nfit_mem->dcr; /* nfit test assumes 1:1 relationship between commands and dsms */ nfit_mem->dsm_mask = acpi_desc->dimm_cmd_force_en; nfit_mem->family = NVDIMM_FAMILY_INTEL; + + if (dcr->valid_fields & ACPI_NFIT_CONTROL_MFG_INFO_VALID) + sprintf(nfit_mem->id, "%04x-%02x-%04x-%08x", + be16_to_cpu(dcr->vendor_id), + dcr->manufacturing_location, + be16_to_cpu(dcr->manufacturing_date), + be32_to_cpu(dcr->serial_number)); + else + sprintf(nfit_mem->id, "%04x-%08x", + be16_to_cpu(dcr->vendor_id), + be32_to_cpu(dcr->serial_number)); + adev = to_acpi_dev(acpi_desc); if (!adev) { /* unit test case */ @@ -1904,6 +1932,16 @@ static void shutdown_dimm_notify(void *data) mutex_unlock(&acpi_desc->init_mutex); } +static const struct nvdimm_security_ops *acpi_nfit_get_security_ops(int family) +{ + switch (family) { + case NVDIMM_FAMILY_INTEL: + return intel_security_ops; + default: + return NULL; + } +} + static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) { struct nfit_mem *nfit_mem; @@ -1970,10 +2008,11 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) flush = nfit_mem->nfit_flush ? nfit_mem->nfit_flush->flush : NULL; - nvdimm = nvdimm_create(acpi_desc->nvdimm_bus, nfit_mem, + nvdimm = __nvdimm_create(acpi_desc->nvdimm_bus, nfit_mem, acpi_nfit_dimm_attribute_groups, flags, cmd_mask, flush ? flush->hint_count : 0, - nfit_mem->flush_wpq); + nfit_mem->flush_wpq, &nfit_mem->id[0], + acpi_nfit_get_security_ops(nfit_mem->family)); if (!nvdimm) return -ENOMEM; @@ -2008,6 +2047,11 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) if (!nvdimm) continue; + rc = nvdimm_security_setup_events(nvdimm); + if (rc < 0) + dev_warn(acpi_desc->dev, + "security event setup failed: %d\n", rc); + nfit_kernfs = sysfs_get_dirent(nvdimm_kobj(nvdimm)->sd, "nfit"); if (nfit_kernfs) nfit_mem->flags_attr = sysfs_get_dirent(nfit_kernfs, @@ -3337,7 +3381,7 @@ static int acpi_nfit_flush_probe(struct nvdimm_bus_descriptor *nd_desc) return 0; } -static int acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc, +static int __acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, unsigned int cmd) { struct acpi_nfit_desc *acpi_desc = to_acpi_nfit_desc(nd_desc); @@ -3359,6 +3403,23 @@ static int acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc, return 0; } +/* prevent security commands from being issued via ioctl */ +static int acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc, + struct nvdimm *nvdimm, unsigned int cmd, void *buf) +{ + struct nd_cmd_pkg *call_pkg = buf; + unsigned int func; + + if (nvdimm && cmd == ND_CMD_CALL && + call_pkg->nd_family == NVDIMM_FAMILY_INTEL) { + func = call_pkg->nd_command; + if ((1 << func) & NVDIMM_INTEL_SECURITY_CMDMASK) + return -EOPNOTSUPP; + } + + return __acpi_nfit_clear_to_send(nd_desc, nvdimm, cmd); +} + int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc, enum nfit_ars_state req_type) { @@ -3474,7 +3535,13 @@ static int acpi_nfit_add(struct acpi_device *adev) status = acpi_get_table(ACPI_SIG_NFIT, 0, &tbl); if (ACPI_FAILURE(status)) { - /* This is ok, we could have an nvdimm hotplugged later */ + /* The NVDIMM root device allows OS to trigger enumeration of + * NVDIMMs through NFIT at boot time and re-enumeration at + * root level via the _FIT method during runtime. + * This is ok to return 0 here, we could have an nvdimm + * hotplugged later and evaluate _FIT method which returns + * data in the format of a series of NFIT Structures. + */ dev_dbg(dev, "failed to find NFIT at startup\n"); return 0; } diff --git a/drivers/acpi/nfit/intel.c b/drivers/acpi/nfit/intel.c new file mode 100644 index 000000000000..850b2927b4e7 --- /dev/null +++ b/drivers/acpi/nfit/intel.c @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2018 Intel Corporation. All rights reserved. */ +#include <linux/libnvdimm.h> +#include <linux/ndctl.h> +#include <linux/acpi.h> +#include <asm/smp.h> +#include "intel.h" +#include "nfit.h" + +static enum nvdimm_security_state intel_security_state(struct nvdimm *nvdimm, + enum nvdimm_passphrase_type ptype) +{ + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); + struct { + struct nd_cmd_pkg pkg; + struct nd_intel_get_security_state cmd; + } nd_cmd = { + .pkg = { + .nd_command = NVDIMM_INTEL_GET_SECURITY_STATE, + .nd_family = NVDIMM_FAMILY_INTEL, + .nd_size_out = + sizeof(struct nd_intel_get_security_state), + .nd_fw_size = + sizeof(struct nd_intel_get_security_state), + }, + }; + int rc; + + if (!test_bit(NVDIMM_INTEL_GET_SECURITY_STATE, &nfit_mem->dsm_mask)) + return -ENXIO; + + /* + * Short circuit the state retrieval while we are doing overwrite. + * The DSM spec states that the security state is indeterminate + * until the overwrite DSM completes. + */ + if (nvdimm_in_overwrite(nvdimm) && ptype == NVDIMM_USER) + return NVDIMM_SECURITY_OVERWRITE; + + rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL); + if (rc < 0) + return rc; + if (nd_cmd.cmd.status) + return -EIO; + + /* check and see if security is enabled and locked */ + if (ptype == NVDIMM_MASTER) { + if (nd_cmd.cmd.extended_state & ND_INTEL_SEC_ESTATE_ENABLED) + return NVDIMM_SECURITY_UNLOCKED; + else if (nd_cmd.cmd.extended_state & + ND_INTEL_SEC_ESTATE_PLIMIT) + return NVDIMM_SECURITY_FROZEN; + } else { + if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_UNSUPPORTED) + return -ENXIO; + else if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_ENABLED) { + if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_LOCKED) + return NVDIMM_SECURITY_LOCKED; + else if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_FROZEN + || nd_cmd.cmd.state & + ND_INTEL_SEC_STATE_PLIMIT) + return NVDIMM_SECURITY_FROZEN; + else + return NVDIMM_SECURITY_UNLOCKED; + } + } + + /* this should cover master security disabled as well */ + return NVDIMM_SECURITY_DISABLED; +} + +static int intel_security_freeze(struct nvdimm *nvdimm) +{ + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); + struct { + struct nd_cmd_pkg pkg; + struct nd_intel_freeze_lock cmd; + } nd_cmd = { + .pkg = { + .nd_command = NVDIMM_INTEL_FREEZE_LOCK, + .nd_family = NVDIMM_FAMILY_INTEL, + .nd_size_out = ND_INTEL_STATUS_SIZE, + .nd_fw_size = ND_INTEL_STATUS_SIZE, + }, + }; + int rc; + + if (!test_bit(NVDIMM_INTEL_FREEZE_LOCK, &nfit_mem->dsm_mask)) + return -ENOTTY; + + rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL); + if (rc < 0) + return rc; + if (nd_cmd.cmd.status) + return -EIO; + return 0; +} + +static int 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) +{ + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); + unsigned int cmd = ptype == NVDIMM_MASTER ? + NVDIMM_INTEL_SET_MASTER_PASSPHRASE : + NVDIMM_INTEL_SET_PASSPHRASE; + struct { + struct nd_cmd_pkg pkg; + struct nd_intel_set_passphrase cmd; + } nd_cmd = { + .pkg = { + .nd_family = NVDIMM_FAMILY_INTEL, + .nd_size_in = ND_INTEL_PASSPHRASE_SIZE * 2, + .nd_size_out = ND_INTEL_STATUS_SIZE, + .nd_fw_size = ND_INTEL_STATUS_SIZE, + .nd_command = cmd, + }, + }; + int rc; + + if (!test_bit(cmd, &nfit_mem->dsm_mask)) + return -ENOTTY; + + if (old_data) + memcpy(nd_cmd.cmd.old_pass, old_data->data, + sizeof(nd_cmd.cmd.old_pass)); + memcpy(nd_cmd.cmd.new_pass, new_data->data, + sizeof(nd_cmd.cmd.new_pass)); + rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL); + if (rc < 0) + return rc; + + switch (nd_cmd.cmd.status) { + case 0: + return 0; + case ND_INTEL_STATUS_INVALID_PASS: + return -EINVAL; + case ND_INTEL_STATUS_NOT_SUPPORTED: + return -EOPNOTSUPP; + case ND_INTEL_STATUS_INVALID_STATE: + default: + return -EIO; + } +} + +static void nvdimm_invalidate_cache(void); + +static int intel_security_unlock(struct nvdimm *nvdimm, + const struct nvdimm_key_data *key_data) +{ + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); + struct { + struct nd_cmd_pkg pkg; + struct nd_intel_unlock_unit cmd; + } nd_cmd = { + .pkg = { + .nd_command = NVDIMM_INTEL_UNLOCK_UNIT, + .nd_family = NVDIMM_FAMILY_INTEL, + .nd_size_in = ND_INTEL_PASSPHRASE_SIZE, + .nd_size_out = ND_INTEL_STATUS_SIZE, + .nd_fw_size = ND_INTEL_STATUS_SIZE, + }, + }; + int rc; + + if (!test_bit(NVDIMM_INTEL_UNLOCK_UNIT, &nfit_mem->dsm_mask)) + return -ENOTTY; + + memcpy(nd_cmd.cmd.passphrase, key_data->data, + sizeof(nd_cmd.cmd.passphrase)); + rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL); + if (rc < 0) + return rc; + switch (nd_cmd.cmd.status) { + case 0: + break; + case ND_INTEL_STATUS_INVALID_PASS: + return -EINVAL; + default: + return -EIO; + } + + /* DIMM unlocked, invalidate all CPU caches before we read it */ + nvdimm_invalidate_cache(); + + return 0; +} + +static int intel_security_disable(struct nvdimm *nvdimm, + const struct nvdimm_key_data *key_data) +{ + int rc; + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); + struct { + struct nd_cmd_pkg pkg; + struct nd_intel_disable_passphrase cmd; + } nd_cmd = { + .pkg = { + .nd_command = NVDIMM_INTEL_DISABLE_PASSPHRASE, + .nd_family = NVDIMM_FAMILY_INTEL, + .nd_size_in = ND_INTEL_PASSPHRASE_SIZE, + .nd_size_out = ND_INTEL_STATUS_SIZE, + .nd_fw_size = ND_INTEL_STATUS_SIZE, + }, + }; + + if (!test_bit(NVDIMM_INTEL_DISABLE_PASSPHRASE, &nfit_mem->dsm_mask)) + return -ENOTTY; + + memcpy(nd_cmd.cmd.passphrase, key_data->data, + sizeof(nd_cmd.cmd.passphrase)); + rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL); + if (rc < 0) + return rc; + + switch (nd_cmd.cmd.status) { + case 0: + break; + case ND_INTEL_STATUS_INVALID_PASS: + return -EINVAL; + case ND_INTEL_STATUS_INVALID_STATE: + default: + return -ENXIO; + } + + return 0; +} + +static int intel_security_erase(struct nvdimm *nvdimm, + const struct nvdimm_key_data *key, + enum nvdimm_passphrase_type ptype) +{ + int rc; + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); + unsigned int cmd = ptype == NVDIMM_MASTER ? + NVDIMM_INTEL_MASTER_SECURE_ERASE : NVDIMM_INTEL_SECURE_ERASE; + struct { + struct nd_cmd_pkg pkg; + struct nd_intel_secure_erase cmd; + } nd_cmd = { + .pkg = { + .nd_family = NVDIMM_FAMILY_INTEL, + .nd_size_in = ND_INTEL_PASSPHRASE_SIZE, + .nd_size_out = ND_INTEL_STATUS_SIZE, + .nd_fw_size = ND_INTEL_STATUS_SIZE, + .nd_command = cmd, + }, + }; + + if (!test_bit(cmd, &nfit_mem->dsm_mask)) + return -ENOTTY; + + /* flush all cache before we erase DIMM */ + nvdimm_invalidate_cache(); + memcpy(nd_cmd.cmd.passphrase, key->data, + sizeof(nd_cmd.cmd.passphrase)); + rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL); + if (rc < 0) + return rc; + + switch (nd_cmd.cmd.status) { + case 0: + break; + case ND_INTEL_STATUS_NOT_SUPPORTED: + return -EOPNOTSUPP; + case ND_INTEL_STATUS_INVALID_PASS: + return -EINVAL; + case ND_INTEL_STATUS_INVALID_STATE: + default: + return -ENXIO; + } + + /* DIMM erased, invalidate all CPU caches before we read it */ + nvdimm_invalidate_cache(); + return 0; +} + +static int intel_security_query_overwrite(struct nvdimm *nvdimm) +{ + int rc; + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); + struct { + struct nd_cmd_pkg pkg; + struct nd_intel_query_overwrite cmd; + } nd_cmd = { + .pkg = { + .nd_command = NVDIMM_INTEL_QUERY_OVERWRITE, + .nd_family = NVDIMM_FAMILY_INTEL, + .nd_size_out = ND_INTEL_STATUS_SIZE, + .nd_fw_size = ND_INTEL_STATUS_SIZE, + }, + }; + + if (!test_bit(NVDIMM_INTEL_QUERY_OVERWRITE, &nfit_mem->dsm_mask)) + return -ENOTTY; + + rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL); + if (rc < 0) + return rc; + + switch (nd_cmd.cmd.status) { + case 0: + break; + case ND_INTEL_STATUS_OQUERY_INPROGRESS: + return -EBUSY; + default: + return -ENXIO; + } + + /* flush all cache before we make the nvdimms available */ + nvdimm_invalidate_cache(); + return 0; +} + +static int intel_security_overwrite(struct nvdimm *nvdimm, + const struct nvdimm_key_data *nkey) +{ + int rc; + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); + struct { + struct nd_cmd_pkg pkg; + struct nd_intel_overwrite cmd; + } nd_cmd = { + .pkg = { + .nd_command = NVDIMM_INTEL_OVERWRITE, + .nd_family = NVDIMM_FAMILY_INTEL, + .nd_size_in = ND_INTEL_PASSPHRASE_SIZE, + .nd_size_out = ND_INTEL_STATUS_SIZE, + .nd_fw_size = ND_INTEL_STATUS_SIZE, + }, + }; + + if (!test_bit(NVDIMM_INTEL_OVERWRITE, &nfit_mem->dsm_mask)) + return -ENOTTY; + + /* flush all cache before we erase DIMM */ + nvdimm_invalidate_cache(); + if (nkey) + memcpy(nd_cmd.cmd.passphrase, nkey->data, + sizeof(nd_cmd.cmd.passphrase)); + rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL); + if (rc < 0) + return rc; + + switch (nd_cmd.cmd.status) { + case 0: + return 0; + case ND_INTEL_STATUS_OVERWRITE_UNSUPPORTED: + return -ENOTSUPP; + case ND_INTEL_STATUS_INVALID_PASS: + return -EINVAL; + case ND_INTEL_STATUS_INVALID_STATE: + default: + return -ENXIO; + } +} + +/* + * TODO: define a cross arch wbinvd equivalent when/if + * NVDIMM_FAMILY_INTEL command support arrives on another arch. + */ +#ifdef CONFIG_X86 +static void nvdimm_invalidate_cache(void) +{ + wbinvd_on_all_cpus(); +} +#else +static void nvdimm_invalidate_cache(void) +{ + WARN_ON_ONCE("cache invalidation required after unlock\n"); +} +#endif + +static const struct nvdimm_security_ops __intel_security_ops = { + .state = intel_security_state, + .freeze = intel_security_freeze, + .change_key = intel_security_change_key, + .disable = intel_security_disable, +#ifdef CONFIG_X86 + .unlock = intel_security_unlock, + .erase = intel_security_erase, + .overwrite = intel_security_overwrite, + .query_overwrite = intel_security_query_overwrite, +#endif +}; + +const struct nvdimm_security_ops *intel_security_ops = &__intel_security_ops; diff --git a/drivers/acpi/nfit/intel.h b/drivers/acpi/nfit/intel.h index 86746312381f..0aca682ab9d7 100644 --- a/drivers/acpi/nfit/intel.h +++ b/drivers/acpi/nfit/intel.h @@ -35,4 +35,80 @@ struct nd_intel_smart { }; } __packed; +extern const struct nvdimm_security_ops *intel_security_ops; + +#define ND_INTEL_STATUS_SIZE 4 +#define ND_INTEL_PASSPHRASE_SIZE 32 + +#define ND_INTEL_STATUS_NOT_SUPPORTED 1 +#define ND_INTEL_STATUS_RETRY 5 +#define ND_INTEL_STATUS_NOT_READY 9 +#define ND_INTEL_STATUS_INVALID_STATE 10 +#define ND_INTEL_STATUS_INVALID_PASS 11 +#define ND_INTEL_STATUS_OVERWRITE_UNSUPPORTED 0x10007 +#define ND_INTEL_STATUS_OQUERY_INPROGRESS 0x10007 +#define ND_INTEL_STATUS_OQUERY_SEQUENCE_ERR 0x20007 + +#define ND_INTEL_SEC_STATE_ENABLED 0x02 +#define ND_INTEL_SEC_STATE_LOCKED 0x04 +#define ND_INTEL_SEC_STATE_FROZEN 0x08 +#define ND_INTEL_SEC_STATE_PLIMIT 0x10 +#define ND_INTEL_SEC_STATE_UNSUPPORTED 0x20 +#define ND_INTEL_SEC_STATE_OVERWRITE 0x40 + +#define ND_INTEL_SEC_ESTATE_ENABLED 0x01 +#define ND_INTEL_SEC_ESTATE_PLIMIT 0x02 + +struct nd_intel_get_security_state { + u32 status; + u8 extended_state; + u8 reserved[3]; + u8 state; + u8 reserved1[3]; +} __packed; + +struct nd_intel_set_passphrase { + u8 old_pass[ND_INTEL_PASSPHRASE_SIZE]; + u8 new_pass[ND_INTEL_PASSPHRASE_SIZE]; + u32 status; +} __packed; + +struct nd_intel_unlock_unit { + u8 passphrase[ND_INTEL_PASSPHRASE_SIZE]; + u32 status; +} __packed; + +struct nd_intel_disable_passphrase { + u8 passphrase[ND_INTEL_PASSPHRASE_SIZE]; + u32 status; +} __packed; + +struct nd_intel_freeze_lock { + u32 status; +} __packed; + +struct nd_intel_secure_erase { + u8 passphrase[ND_INTEL_PASSPHRASE_SIZE]; + u32 status; +} __packed; + +struct nd_intel_overwrite { + u8 passphrase[ND_INTEL_PASSPHRASE_SIZE]; + u32 status; +} __packed; + +struct nd_intel_query_overwrite { + u32 status; +} __packed; + +struct nd_intel_set_master_passphrase { + u8 old_pass[ND_INTEL_PASSPHRASE_SIZE]; + u8 new_pass[ND_INTEL_PASSPHRASE_SIZE]; + u32 status; +} __packed; + +struct nd_intel_master_secure_erase { + u8 passphrase[ND_INTEL_PASSPHRASE_SIZE]; + u32 status; +} __packed; #endif diff --git a/drivers/acpi/nfit/nfit.h b/drivers/acpi/nfit/nfit.h index df0f6b8407e7..33691aecfcee 100644 --- a/drivers/acpi/nfit/nfit.h +++ b/drivers/acpi/nfit/nfit.h @@ -60,14 +60,33 @@ enum nvdimm_family_cmds { NVDIMM_INTEL_QUERY_FWUPDATE = 16, NVDIMM_INTEL_SET_THRESHOLD = 17, NVDIMM_INTEL_INJECT_ERROR = 18, + NVDIMM_INTEL_GET_SECURITY_STATE = 19, + NVDIMM_INTEL_SET_PASSPHRASE = 20, + NVDIMM_INTEL_DISABLE_PASSPHRASE = 21, + NVDIMM_INTEL_UNLOCK_UNIT = 22, + NVDIMM_INTEL_FREEZE_LOCK = 23, + NVDIMM_INTEL_SECURE_ERASE = 24, + NVDIMM_INTEL_OVERWRITE = 25, + NVDIMM_INTEL_QUERY_OVERWRITE = 26, + NVDIMM_INTEL_SET_MASTER_PASSPHRASE = 27, + NVDIMM_INTEL_MASTER_SECURE_ERASE = 28, }; +#define NVDIMM_INTEL_SECURITY_CMDMASK \ +(1 << NVDIMM_INTEL_GET_SECURITY_STATE | 1 << NVDIMM_INTEL_SET_PASSPHRASE \ +| 1 << NVDIMM_INTEL_DISABLE_PASSPHRASE | 1 << NVDIMM_INTEL_UNLOCK_UNIT \ +| 1 << NVDIMM_INTEL_FREEZE_LOCK | 1 << NVDIMM_INTEL_SECURE_ERASE \ +| 1 << NVDIMM_INTEL_OVERWRITE | 1 << NVDIMM_INTEL_QUERY_OVERWRITE \ +| 1 << NVDIMM_INTEL_SET_MASTER_PASSPHRASE \ +| 1 << NVDIMM_INTEL_MASTER_SECURE_ERASE) + #define NVDIMM_INTEL_CMDMASK \ (NVDIMM_STANDARD_CMDMASK | 1 << NVDIMM_INTEL_GET_MODES \ | 1 << NVDIMM_INTEL_GET_FWINFO | 1 << NVDIMM_INTEL_START_FWUPDATE \ | 1 << NVDIMM_INTEL_SEND_FWUPDATE | 1 << NVDIMM_INTEL_FINISH_FWUPDATE \ | 1 << NVDIMM_INTEL_QUERY_FWUPDATE | 1 << NVDIMM_INTEL_SET_THRESHOLD \ - | 1 << NVDIMM_INTEL_INJECT_ERROR | 1 << NVDIMM_INTEL_LATCH_SHUTDOWN) + | 1 << NVDIMM_INTEL_INJECT_ERROR | 1 << NVDIMM_INTEL_LATCH_SHUTDOWN \ + | NVDIMM_INTEL_SECURITY_CMDMASK) enum nfit_uuids { /* for simplicity alias the uuid index with the family id */ @@ -164,6 +183,8 @@ enum nfit_mem_flags { NFIT_MEM_DIRTY_COUNT, }; +#define NFIT_DIMM_ID_LEN 22 + /* assembled tables for a given dimm/memory-device */ struct nfit_mem { struct nvdimm *nvdimm; @@ -181,6 +202,7 @@ struct nfit_mem { struct list_head list; struct acpi_device *adev; struct acpi_nfit_desc *acpi_desc; + char id[NFIT_DIMM_ID_LEN+1]; struct resource *flush_wpq; unsigned long dsm_mask; unsigned long flags; |