mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 18:51:56 -04:00
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:
commit
75f95da078
27 changed files with 1971 additions and 54 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
obj-$(CONFIG_ACPI_NFIT) := nfit.o
|
||||
nfit-y := core.o
|
||||
nfit-y += intel.o
|
||||
nfit-$(CONFIG_X86_MCE) += mce.o
|
||||
|
|
|
|||
|
|
@ -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
388
drivers/acpi/nfit/intel.c
Normal 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;
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue