libnvdimm for 4.21

* 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.
 -----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJcJalaAAoJEB7SkWpmfYgCmpkP/35Isou2xxbYdICt9HR4WjiE
 E4r66Ck+ivZW4oEAub3RQw/SwcZePjuHArO5MgVGFoM1dBMABE+JvKiZm1xybCil
 iuWT/V7ozlNf5LuvacvjUXe/suUIoqji9yzLEEx9mDaISehrYvvy579DFxkE8Aww
 8Vcx8E1DQkXiWY4d9nmDmAjt8GQPNmsdHfTwMcJg7XSywTkURF64vW7/94aN8QtV
 rH5ZKKOra8Lqn0S05RoH4XegZcE6TZxgE23ZXsobBJrWdbGVFmgbz6AaewJc/+QU
 ZW3dx/1NAe7Op25xewJOjdG/Kl0gzWtqCrHCu8LeaSjwtHsgMQ/FBhL9g/7GRtHt
 hcQy12Iu9YTn3FOl8yfNRKb7lE5/1nJ2MtW8Z55WKHONHzN9cgrZLOwmpTYgrR/r
 SmIzwKNFavLwwz9bj+paHhmTngLDnNbvR6FwAKwSbWinotbLEyfMhU1jJ9RmHxSV
 M+jqS1BJ9IvH+WVN5bVa7oCNpVeq6yMw0Ow2vWXMPbiwh17ShACI59paqZKRiR06
 WH8SgdTPOZuMndtQV+m8zFBiJtmgAtGSiN8c1A/1QZJHDTpXz2MdWhtx5McvFsZ9
 Jot4Vd8mFWI7oVLdb+PF/AGZnKNAnD8PdsAlFws0k7+rM6lf6gf5n/CkjjMuSapG
 Q/kf0cOpPq8wdsrnOzyc
 =7KYd
 -----END PGP SIGNATURE-----

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
  ...
This commit is contained in:
Linus Torvalds 2018-12-28 15:05:13 -08:00
commit 75f95da078
27 changed files with 1971 additions and 54 deletions

View file

@ -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.

View file

@ -1,3 +1,4 @@
obj-$(CONFIG_ACPI_NFIT) := nfit.o
nfit-y := core.o
nfit-y += intel.o
nfit-$(CONFIG_X86_MCE) += mce.o

View file

@ -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;
}

388
drivers/acpi/nfit/intel.c Normal file
View file

@ -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;

View file

@ -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

View file

@ -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;