From 9968c9dd568e83f57fdc1f6127f8b369a0594991 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Mon, 12 Jun 2023 11:10:33 -0700 Subject: cxl/mem: Introduce security state sysfs file Add a read-only sysfs file to display the security state of a device (currently only pmem): /sys/bus/cxl/devices/memX/security/state This introduces a cxl_security_state structure that is to be the placeholder for common CXL security features. Reviewed-by: Jonathan Cameron Reviewed-by: Fan Ni Signed-off-by: Davidlohr Bueso Reviewed-by: Dave Jiang Link: https://lore.kernel.org/r/20230612181038.14421-3-dave@stgolabs.net Signed-off-by: Dan Williams --- Documentation/ABI/testing/sysfs-bus-cxl | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-cxl b/Documentation/ABI/testing/sysfs-bus-cxl index 48ac0d911801..721a44d8a482 100644 --- a/Documentation/ABI/testing/sysfs-bus-cxl +++ b/Documentation/ABI/testing/sysfs-bus-cxl @@ -58,6 +58,16 @@ Description: affinity for this device. +What: /sys/bus/cxl/devices/memX/security/state +Date: June, 2023 +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'. + + What: /sys/bus/cxl/devices/*/devtype Date: June, 2021 KernelVersion: v5.14 -- cgit v1.2.3 From 48dcdbb16e5dc0947f949ce17bc2d09a625a0d5c Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Mon, 12 Jun 2023 11:10:35 -0700 Subject: cxl/mem: Wire up Sanitization support 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 Reviewed-by: Jonathan Cameron Signed-off-by: Davidlohr Bueso Link: https://lore.kernel.org/r/20230612181038.14421-5-dave@stgolabs.net Signed-off-by: Dan Williams --- Documentation/ABI/testing/sysfs-bus-cxl | 21 ++++++++++- drivers/cxl/core/mbox.c | 55 +++++++++++++++++++++++++++ drivers/cxl/core/memdev.c | 67 +++++++++++++++++++++++++++++++++ drivers/cxl/cxlmem.h | 4 ++ drivers/cxl/pci.c | 6 +++ 5 files changed, 151 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-cxl b/Documentation/ABI/testing/sysfs-bus-cxl index 721a44d8a482..f86fe3671375 100644 --- a/Documentation/ABI/testing/sysfs-bus-cxl +++ b/Documentation/ABI/testing/sysfs-bus-cxl @@ -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 diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c index 5993261e3e08..e6e60c8b606e 100644 --- a/drivers/cxl/core/mbox.c +++ b/drivers/cxl/core/mbox.c @@ -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) diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c index 834f418b6bcb..bdd1edfd62e8 100644 --- a/drivers/cxl/core/memdev.c +++ b/drivers/cxl/core/memdev.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright(c) 2020 Intel Corporation. */ +#include #include #include #include @@ -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"); @@ -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; @@ -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, }; @@ -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; @@ -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); diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h index 698cd10aea4a..5b84f27fc76a 100644 --- a/drivers/cxl/cxlmem.h +++ b/drivers/cxl/cxlmem.h @@ -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; }; /** @@ -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; diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c index 8f1309577653..8bc19cae0850 100644 --- a/drivers/cxl/pci.c +++ b/drivers/cxl/pci.c @@ -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() */ @@ -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; -- cgit v1.2.3 From 180ffd338c35057c3e8521d55555ae3b36b67fa6 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Mon, 12 Jun 2023 11:10:37 -0700 Subject: cxl/mem: Support Secure Erase Implement support for the non-pmem exclusive secure erase, per CXL specs. Create a write-only 'security/erase' sysfs file to perform the requested operation. As with the sanitation this requires the device being offline and thus no active HPA-DPA decoding. The expectation is that userspace can use it such as: cxl disable-memdev memX echo 1 > /sys/bus/cxl/devices/memX/security/erase cxl enable-memdev memX Reviewed-by: Jonathan Cameron Reviewed-by: Dave Jiang Reviewed-by: Fan Ni Signed-off-by: Davidlohr Bueso Link: https://lore.kernel.org/r/20230612181038.14421-7-dave@stgolabs.net Signed-off-by: Dan Williams --- Documentation/ABI/testing/sysfs-bus-cxl | 10 ++++++++++ drivers/cxl/core/mbox.c | 6 +++++- drivers/cxl/core/memdev.c | 28 ++++++++++++++++++++++++++++ drivers/cxl/cxlmem.h | 1 + 4 files changed, 44 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-cxl b/Documentation/ABI/testing/sysfs-bus-cxl index f86fe3671375..c619493e413e 100644 --- a/Documentation/ABI/testing/sysfs-bus-cxl +++ b/Documentation/ABI/testing/sysfs-bus-cxl @@ -85,6 +85,16 @@ Description: the device to be not be actively decoding any HPA ranges. +What /sys/bus/cxl/devices/memX/security/erase +Date: June, 2023 +KernelVersion: v6.5 +Contact: linux-cxl@vger.kernel.org +Description: + (WO) Write a boolean 'true' string value to this attribute to + secure erase user data by changing the media encryption keys for + all user data areas of the device. + + What: /sys/bus/cxl/devices/*/devtype Date: June, 2021 KernelVersion: v5.14 diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c index e6e60c8b606e..c86d0b3159b0 100644 --- a/drivers/cxl/core/mbox.c +++ b/drivers/cxl/core/mbox.c @@ -1102,7 +1102,7 @@ int cxl_mem_sanitize(struct cxl_dev_state *cxlds, u16 cmd) }; struct cxl_mbox_cmd mbox_cmd = { .opcode = cmd }; - if (cmd != CXL_MBOX_OP_SANITIZE) + if (cmd != CXL_MBOX_OP_SANITIZE && cmd != CXL_MBOX_OP_SECURE_ERASE) return -EINVAL; rc = cxl_internal_send_cmd(cxlds, &sec_cmd); @@ -1120,6 +1120,10 @@ int cxl_mem_sanitize(struct cxl_dev_state *cxlds, u16 cmd) if (sec_out & CXL_PMEM_SEC_STATE_USER_PASS_SET) return -EINVAL; + if (cmd == CXL_MBOX_OP_SECURE_ERASE && + sec_out & CXL_PMEM_SEC_STATE_LOCKED) + return -EINVAL; + rc = cxl_internal_send_cmd(cxlds, &mbox_cmd); if (rc < 0) { dev_err(cxlds->dev, "Failed to sanitize device : %d", rc); diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c index bdd1edfd62e8..ed8de7efddef 100644 --- a/drivers/cxl/core/memdev.c +++ b/drivers/cxl/core/memdev.c @@ -163,6 +163,33 @@ static ssize_t security_sanitize_store(struct device *dev, static struct device_attribute dev_attr_security_sanitize = __ATTR(sanitize, 0200, NULL, security_sanitize_store); +static ssize_t security_erase_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 erase; + + if (kstrtobool(buf, &erase) || !erase) + 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_SECURE_ERASE); + + return rc ? rc : len; +} +static struct device_attribute dev_attr_security_erase = + __ATTR(erase, 0200, NULL, security_erase_store); + static int cxl_get_poison_by_memdev(struct cxl_memdev *cxlmd) { struct cxl_dev_state *cxlds = cxlmd->cxlds; @@ -411,6 +438,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, + &dev_attr_security_erase.attr, NULL, }; diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h index 5b84f27fc76a..78ff518012bf 100644 --- a/drivers/cxl/cxlmem.h +++ b/drivers/cxl/cxlmem.h @@ -388,6 +388,7 @@ enum cxl_opcode { CXL_MBOX_OP_SCAN_MEDIA = 0x4304, CXL_MBOX_OP_GET_SCAN_MEDIA = 0x4305, CXL_MBOX_OP_SANITIZE = 0x4400, + CXL_MBOX_OP_SECURE_ERASE = 0x4401, CXL_MBOX_OP_GET_SECURITY_STATE = 0x4500, CXL_MBOX_OP_SET_PASSPHRASE = 0x4501, CXL_MBOX_OP_DISABLE_PASSPHRASE = 0x4502, -- cgit v1.2.3