Commit b3ed2ce0 authored by Dave Jiang's avatar Dave Jiang Committed by Dan Williams

acpi/nfit: Add support for Intel DSM 1.8 commands

Add command definition for security commands defined in Intel DSM
specification v1.8 [1]. This includes "get security state", "set
passphrase", "unlock unit", "freeze lock", "secure erase", "overwrite",
"overwrite query", "master passphrase enable/disable", and "master
erase", . Since this adds several Intel definitions, move the relevant
bits to their own header.

These commands mutate physical data, but that manipulation is not cache
coherent. The requirement to flush and invalidate caches makes these
commands unsuitable to be called from userspace, so extra logic is added
to detect and block these commands from being submitted via the ioctl
command submission path.

Lastly, the commands may contain sensitive key material that should not
be dumped in a standard debug session. Update the nvdimm-command
payload-dump facility to move security command payloads behind a
default-off compile time switch.

[1]: http://pmem.io/documents/NVDIMM_DSM_Interface-V1.8.pdfSigned-off-by: default avatarDave Jiang <dave.jiang@intel.com>
Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
parent 25956467
...@@ -13,3 +13,14 @@ config ACPI_NFIT ...@@ -13,3 +13,14 @@ config ACPI_NFIT
To compile this driver as a module, choose M here: To compile this driver as a module, choose M here:
the module will be called nfit. 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.
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/nd.h> #include <linux/nd.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <acpi/nfit.h> #include <acpi/nfit.h>
#include "intel.h"
#include "nfit.h" #include "nfit.h"
#include "intel.h" #include "intel.h"
...@@ -380,6 +381,14 @@ static u8 nfit_dsm_revid(unsigned family, unsigned func) ...@@ -380,6 +381,14 @@ static u8 nfit_dsm_revid(unsigned family, unsigned func)
[NVDIMM_INTEL_QUERY_FWUPDATE] = 2, [NVDIMM_INTEL_QUERY_FWUPDATE] = 2,
[NVDIMM_INTEL_SET_THRESHOLD] = 2, [NVDIMM_INTEL_SET_THRESHOLD] = 2,
[NVDIMM_INTEL_INJECT_ERROR] = 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,
}, },
}; };
u8 id; u8 id;
...@@ -394,6 +403,17 @@ static u8 nfit_dsm_revid(unsigned family, unsigned func) ...@@ -394,6 +403,17 @@ static u8 nfit_dsm_revid(unsigned family, unsigned func)
return id; 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, 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) unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc)
{ {
...@@ -478,9 +498,10 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, ...@@ -478,9 +498,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", dev_dbg(dev, "%s cmd: %d: func: %d input length: %d\n",
dimm_name, cmd, func, in_buf.buffer.length); dimm_name, cmd, func, in_buf.buffer.length);
print_hex_dump_debug("nvdimm in ", DUMP_PREFIX_OFFSET, 4, 4, if (payload_dumpable(nvdimm, func))
in_buf.buffer.pointer, print_hex_dump_debug("nvdimm in ", DUMP_PREFIX_OFFSET, 4, 4,
min_t(u32, 256, in_buf.buffer.length), true); in_buf.buffer.pointer,
min_t(u32, 256, in_buf.buffer.length), true);
/* call the BIOS, prefer the named methods over _DSM if available */ /* call the BIOS, prefer the named methods over _DSM if available */
if (nvdimm && cmd == ND_CMD_GET_CONFIG_SIZE if (nvdimm && cmd == ND_CMD_GET_CONFIG_SIZE
...@@ -3337,7 +3358,7 @@ static int acpi_nfit_flush_probe(struct nvdimm_bus_descriptor *nd_desc) ...@@ -3337,7 +3358,7 @@ static int acpi_nfit_flush_probe(struct nvdimm_bus_descriptor *nd_desc)
return 0; 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 nvdimm *nvdimm, unsigned int cmd)
{ {
struct acpi_nfit_desc *acpi_desc = to_acpi_nfit_desc(nd_desc); struct acpi_nfit_desc *acpi_desc = to_acpi_nfit_desc(nd_desc);
...@@ -3359,6 +3380,23 @@ static int acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc, ...@@ -3359,6 +3380,23 @@ static int acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc,
return 0; 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, int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc,
enum nfit_ars_state req_type) enum nfit_ars_state req_type)
{ {
......
...@@ -35,4 +35,78 @@ struct nd_intel_smart { ...@@ -35,4 +35,78 @@ struct nd_intel_smart {
}; };
} __packed; } __packed;
#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 #endif
...@@ -60,14 +60,33 @@ enum nvdimm_family_cmds { ...@@ -60,14 +60,33 @@ enum nvdimm_family_cmds {
NVDIMM_INTEL_QUERY_FWUPDATE = 16, NVDIMM_INTEL_QUERY_FWUPDATE = 16,
NVDIMM_INTEL_SET_THRESHOLD = 17, NVDIMM_INTEL_SET_THRESHOLD = 17,
NVDIMM_INTEL_INJECT_ERROR = 18, 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 \ #define NVDIMM_INTEL_CMDMASK \
(NVDIMM_STANDARD_CMDMASK | 1 << NVDIMM_INTEL_GET_MODES \ (NVDIMM_STANDARD_CMDMASK | 1 << NVDIMM_INTEL_GET_MODES \
| 1 << NVDIMM_INTEL_GET_FWINFO | 1 << NVDIMM_INTEL_START_FWUPDATE \ | 1 << NVDIMM_INTEL_GET_FWINFO | 1 << NVDIMM_INTEL_START_FWUPDATE \
| 1 << NVDIMM_INTEL_SEND_FWUPDATE | 1 << NVDIMM_INTEL_FINISH_FWUPDATE \ | 1 << NVDIMM_INTEL_SEND_FWUPDATE | 1 << NVDIMM_INTEL_FINISH_FWUPDATE \
| 1 << NVDIMM_INTEL_QUERY_FWUPDATE | 1 << NVDIMM_INTEL_SET_THRESHOLD \ | 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 { enum nfit_uuids {
/* for simplicity alias the uuid index with the family id */ /* for simplicity alias the uuid index with the family id */
......
...@@ -902,7 +902,7 @@ static int nd_cmd_clear_to_send(struct nvdimm_bus *nvdimm_bus, ...@@ -902,7 +902,7 @@ static int nd_cmd_clear_to_send(struct nvdimm_bus *nvdimm_bus,
/* ask the bus provider if it would like to block this request */ /* ask the bus provider if it would like to block this request */
if (nd_desc->clear_to_send) { if (nd_desc->clear_to_send) {
int rc = nd_desc->clear_to_send(nd_desc, nvdimm, cmd); int rc = nd_desc->clear_to_send(nd_desc, nvdimm, cmd, data);
if (rc) if (rc)
return rc; return rc;
......
...@@ -87,7 +87,7 @@ struct nvdimm_bus_descriptor { ...@@ -87,7 +87,7 @@ struct nvdimm_bus_descriptor {
ndctl_fn ndctl; ndctl_fn ndctl;
int (*flush_probe)(struct nvdimm_bus_descriptor *nd_desc); int (*flush_probe)(struct nvdimm_bus_descriptor *nd_desc);
int (*clear_to_send)(struct nvdimm_bus_descriptor *nd_desc, int (*clear_to_send)(struct nvdimm_bus_descriptor *nd_desc,
struct nvdimm *nvdimm, unsigned int cmd); struct nvdimm *nvdimm, unsigned int cmd, void *data);
}; };
struct nd_cmd_desc { struct nd_cmd_desc {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment