Skip to content

Commit

Permalink
cxl/mem: Wire up Sanitization support
Browse files Browse the repository at this point in the history
Implement support for CXL 3.0 8.2.9.8.5.1 Sanitize. This is done by
adding a security/sanitize' memdev sysfs file to trigger the operation
and extend the status file to make it poll(2)-capable for completion.
Unlike all other background commands, this is the only operation that
is special and monopolizes the device for long periods of time.

In addition to the traditional pmem security requirements, all regions
must also be offline in order to perform the operation. This permits
avoiding explicit global CPU cache management, relying instead on the
implict cache management when a region transitions between
CXL_CONFIG_ACTIVE and CXL_CONFIG_COMMIT.

The expectation is that userspace can use it such as:

    cxl disable-memdev memX
    echo 1 > /sys/bus/cxl/devices/memX/security/sanitize
    cxl wait-sanitize memX
    cxl enable-memdev memX

Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Davidlohr Bueso <dave@stgolabs.net>
Link: https://lore.kernel.org/r/20230612181038.14421-5-dave@stgolabs.net
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
  • Loading branch information
Davidlohr Bueso authored and djbw committed Jun 25, 2023
1 parent 0c36b6a commit 48dcdbb
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 2 deletions.
21 changes: 19 additions & 2 deletions Documentation/ABI/testing/sysfs-bus-cxl
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,25 @@ KernelVersion: v6.5
Contact: linux-cxl@vger.kernel.org
Description:
(RO) Reading this file will display the CXL security state for
that device. Such states can be: 'disabled', or those available
only for persistent memory: 'locked', 'unlocked' or 'frozen'.
that device. Such states can be: 'disabled', 'sanitize', when
a sanitization is currently underway; or those available only
for persistent memory: 'locked', 'unlocked' or 'frozen'. This
sysfs entry is select/poll capable from userspace to notify
upon completion of a sanitize operation.


What: /sys/bus/cxl/devices/memX/security/sanitize
Date: June, 2023
KernelVersion: v6.5
Contact: linux-cxl@vger.kernel.org
Description:
(WO) Write a boolean 'true' string value to this attribute to
sanitize the device to securely re-purpose or decommission it.
This is done by ensuring that all user data and meta-data,
whether it resides in persistent capacity, volatile capacity,
or the LSA, is made permanently unavailable by whatever means
is appropriate for the media type. This functionality requires
the device to be not be actively decoding any HPA ranges.


What: /sys/bus/cxl/devices/*/devtype
Expand Down
55 changes: 55 additions & 0 deletions drivers/cxl/core/mbox.c
Original file line number Diff line number Diff line change
Expand Up @@ -1075,6 +1075,61 @@ int cxl_dev_state_identify(struct cxl_dev_state *cxlds)
}
EXPORT_SYMBOL_NS_GPL(cxl_dev_state_identify, CXL);

/**
* cxl_mem_sanitize() - Send a sanitization command to the device.
* @cxlds: The device data for the operation
* @cmd: The specific sanitization command opcode
*
* Return: 0 if the command was executed successfully, regardless of
* whether or not the actual security operation is done in the background,
* such as for the Sanitize case.
* Error return values can be the result of the mailbox command, -EINVAL
* when security requirements are not met or invalid contexts.
*
* See CXL 3.0 @8.2.9.8.5.1 Sanitize and @8.2.9.8.5.2 Secure Erase.
*/
int cxl_mem_sanitize(struct cxl_dev_state *cxlds, u16 cmd)
{
int rc;
u32 sec_out = 0;
struct cxl_get_security_output {
__le32 flags;
} out;
struct cxl_mbox_cmd sec_cmd = {
.opcode = CXL_MBOX_OP_GET_SECURITY_STATE,
.payload_out = &out,
.size_out = sizeof(out),
};
struct cxl_mbox_cmd mbox_cmd = { .opcode = cmd };

if (cmd != CXL_MBOX_OP_SANITIZE)
return -EINVAL;

rc = cxl_internal_send_cmd(cxlds, &sec_cmd);
if (rc < 0) {
dev_err(cxlds->dev, "Failed to get security state : %d", rc);
return rc;
}

/*
* Prior to using these commands, any security applied to
* the user data areas of the device shall be DISABLED (or
* UNLOCKED for secure erase case).
*/
sec_out = le32_to_cpu(out.flags);
if (sec_out & CXL_PMEM_SEC_STATE_USER_PASS_SET)
return -EINVAL;

rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
if (rc < 0) {
dev_err(cxlds->dev, "Failed to sanitize device : %d", rc);
return rc;
}

return 0;
}
EXPORT_SYMBOL_NS_GPL(cxl_mem_sanitize, CXL);

static int add_dpa_res(struct device *dev, struct resource *parent,
struct resource *res, resource_size_t start,
resource_size_t size, const char *type)
Expand Down
67 changes: 67 additions & 0 deletions drivers/cxl/core/memdev.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2020 Intel Corporation. */

#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/idr.h>
Expand Down Expand Up @@ -114,6 +115,12 @@ static ssize_t security_state_show(struct device *dev,
struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
struct cxl_dev_state *cxlds = cxlmd->cxlds;
unsigned long state = cxlds->security.state;
u64 reg = readq(cxlds->regs.mbox + CXLDEV_MBOX_BG_CMD_STATUS_OFFSET);
u32 pct = FIELD_GET(CXLDEV_MBOX_BG_CMD_COMMAND_PCT_MASK, reg);
u16 cmd = FIELD_GET(CXLDEV_MBOX_BG_CMD_COMMAND_OPCODE_MASK, reg);

if (cmd == CXL_MBOX_OP_SANITIZE && pct != 100)
return sysfs_emit(buf, "sanitize\n");

if (!(state & CXL_PMEM_SEC_STATE_USER_PASS_SET))
return sysfs_emit(buf, "disabled\n");
Expand All @@ -129,6 +136,33 @@ static ssize_t security_state_show(struct device *dev,
static struct device_attribute dev_attr_security_state =
__ATTR(state, 0444, security_state_show, NULL);

static ssize_t security_sanitize_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
struct cxl_dev_state *cxlds = cxlmd->cxlds;
struct cxl_port *port = dev_get_drvdata(&cxlmd->dev);
ssize_t rc;
bool sanitize;

if (kstrtobool(buf, &sanitize) || !sanitize)
return -EINVAL;

if (!port || !is_cxl_endpoint(port))
return -EINVAL;

/* ensure no regions are mapped to this memdev */
if (port->commit_end != -1)
return -EBUSY;

rc = cxl_mem_sanitize(cxlds, CXL_MBOX_OP_SANITIZE);

return rc ? rc : len;
}
static struct device_attribute dev_attr_security_sanitize =
__ATTR(sanitize, 0200, NULL, security_sanitize_store);

static int cxl_get_poison_by_memdev(struct cxl_memdev *cxlmd)
{
struct cxl_dev_state *cxlds = cxlmd->cxlds;
Expand Down Expand Up @@ -376,6 +410,7 @@ static struct attribute *cxl_memdev_ram_attributes[] = {

static struct attribute *cxl_memdev_security_attributes[] = {
&dev_attr_security_state.attr,
&dev_attr_security_sanitize.attr,
NULL,
};

Expand Down Expand Up @@ -594,6 +629,34 @@ static const struct file_operations cxl_memdev_fops = {
.llseek = noop_llseek,
};

static void put_sanitize(void *data)
{
struct cxl_dev_state *cxlds = data;

sysfs_put(cxlds->security.sanitize_node);
}

static int cxl_memdev_security_init(struct cxl_memdev *cxlmd)
{
struct cxl_dev_state *cxlds = cxlmd->cxlds;
struct device *dev = &cxlmd->dev;
struct kernfs_node *sec;

sec = sysfs_get_dirent(dev->kobj.sd, "security");
if (!sec) {
dev_err(dev, "sysfs_get_dirent 'security' failed\n");
return -ENODEV;
}
cxlds->security.sanitize_node = sysfs_get_dirent(sec, "state");
sysfs_put(sec);
if (!cxlds->security.sanitize_node) {
dev_err(dev, "sysfs_get_dirent 'state' failed\n");
return -ENODEV;
}

return devm_add_action_or_reset(cxlds->dev, put_sanitize, cxlds);
}

struct cxl_memdev *devm_cxl_add_memdev(struct cxl_dev_state *cxlds)
{
struct cxl_memdev *cxlmd;
Expand Down Expand Up @@ -622,6 +685,10 @@ struct cxl_memdev *devm_cxl_add_memdev(struct cxl_dev_state *cxlds)
if (rc)
goto err;

rc = cxl_memdev_security_init(cxlmd);
if (rc)
goto err;

rc = devm_add_action_or_reset(cxlds->dev, cxl_memdev_unregister, cxlmd);
if (rc)
return ERR_PTR(rc);
Expand Down
4 changes: 4 additions & 0 deletions drivers/cxl/cxlmem.h
Original file line number Diff line number Diff line change
Expand Up @@ -267,12 +267,14 @@ struct cxl_poison_state {
* @poll: polling for sanitization is enabled, device has no mbox irq support
* @poll_tmo_secs: polling timeout
* @poll_dwork: polling work item
* @sanitize_node: sanitation sysfs file to notify
*/
struct cxl_security_state {
unsigned long state;
bool poll;
int poll_tmo_secs;
struct delayed_work poll_dwork;
struct kernfs_node *sanitize_node;
};

/**
Expand Down Expand Up @@ -746,6 +748,8 @@ static inline void cxl_mem_active_dec(void)
}
#endif

int cxl_mem_sanitize(struct cxl_dev_state *cxlds, u16 cmd);

struct cxl_hdm {
struct cxl_component_regs regs;
unsigned int decoder_count;
Expand Down
6 changes: 6 additions & 0 deletions drivers/cxl/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ static irqreturn_t cxl_pci_mbox_irq(int irq, void *id)
reg = readq(cxlds->regs.mbox + CXLDEV_MBOX_BG_CMD_STATUS_OFFSET);
opcode = FIELD_GET(CXLDEV_MBOX_BG_CMD_COMMAND_OPCODE_MASK, reg);
if (opcode == CXL_MBOX_OP_SANITIZE) {
if (cxlds->security.sanitize_node)
sysfs_notify_dirent(cxlds->security.sanitize_node);

dev_dbg(cxlds->dev, "Sanitization operation ended\n");
} else {
/* short-circuit the wait in __cxl_pci_mbox_send_cmd() */
Expand All @@ -150,6 +153,9 @@ static void cxl_mbox_sanitize_work(struct work_struct *work)
cxlds->security.poll_tmo_secs = 0;
put_device(cxlds->dev);

if (cxlds->security.sanitize_node)
sysfs_notify_dirent(cxlds->security.sanitize_node);

dev_dbg(cxlds->dev, "Sanitization operation ended\n");
} else {
int timeout = cxlds->security.poll_tmo_secs + 10;
Expand Down

0 comments on commit 48dcdbb

Please sign in to comment.