Merge branch 'for-6.5/cxl-region-fixes' into for-6.5/cxl
authorDan Williams <dan.j.williams@intel.com>
Mon, 26 Jun 2023 00:23:50 +0000 (17:23 -0700)
committerDan Williams <dan.j.williams@intel.com>
Mon, 26 Jun 2023 00:23:50 +0000 (17:23 -0700)
Pick up the recent fixes to how CPU caches are managed relative to
region setup / teardown, and make sure that all decoders transition
successfully before updating the region state from COMMIT => ACTIVE.

23 files changed:
Documentation/ABI/testing/sysfs-bus-cxl
drivers/cxl/Kconfig
drivers/cxl/acpi.c
drivers/cxl/core/hdm.c
drivers/cxl/core/mbox.c
drivers/cxl/core/memdev.c
drivers/cxl/core/pci.c
drivers/cxl/core/pmem.c
drivers/cxl/core/port.c
drivers/cxl/core/region.c
drivers/cxl/core/regs.c
drivers/cxl/cxl.h
drivers/cxl/cxlmem.h
drivers/cxl/mem.c
drivers/cxl/pci.c
drivers/cxl/pmem.c
drivers/cxl/port.c
drivers/cxl/security.c
include/linux/rcuwait.h
tools/testing/cxl/Kbuild
tools/testing/cxl/test/cxl.c
tools/testing/cxl/test/mem.c
tools/testing/cxl/test/mock.c

index 48ac0d9..6350dd8 100644 (file)
@@ -58,6 +58,54 @@ 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', '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/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/memX/firmware/
+Date:          April, 2023
+KernelVersion: v6.5
+Contact:       linux-cxl@vger.kernel.org
+Description:
+               (RW) Firmware uploader mechanism. The different files under
+               this directory can be used to upload and activate new
+               firmware for CXL devices. The interfaces under this are
+               documented in sysfs-class-firmware.
+
+
 What:          /sys/bus/cxl/devices/*/devtype
 Date:          June, 2021
 KernelVersion: v5.14
index ff4e781..80d8e35 100644 (file)
@@ -82,6 +82,7 @@ config CXL_PMEM
 config CXL_MEM
        tristate "CXL: Memory Expansion"
        depends on CXL_PCI
+       select FW_UPLOAD
        default CXL_BUS
        help
          The CXL.mem protocol allows a device to act as a provider of "System
index 7e1765b..603e5df 100644 (file)
@@ -258,7 +258,7 @@ static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg,
 
        cxld = &cxlrd->cxlsd.cxld;
        cxld->flags = cfmws_to_decoder_flags(cfmws->restrictions);
-       cxld->target_type = CXL_DECODER_EXPANDER;
+       cxld->target_type = CXL_DECODER_HOSTONLYMEM;
        cxld->hpa_range = (struct range) {
                .start = res->start,
                .end = res->end,
index 7889ff2..715c1f1 100644 (file)
@@ -570,8 +570,9 @@ static void cxld_set_interleave(struct cxl_decoder *cxld, u32 *ctrl)
 
 static void cxld_set_type(struct cxl_decoder *cxld, u32 *ctrl)
 {
-       u32p_replace_bits(ctrl, !!(cxld->target_type == 3),
-                         CXL_HDM_DECODER0_CTRL_TYPE);
+       u32p_replace_bits(ctrl,
+                         !!(cxld->target_type == CXL_DECODER_HOSTONLYMEM),
+                         CXL_HDM_DECODER0_CTRL_HOSTONLY);
 }
 
 static int cxlsd_set_targets(struct cxl_switch_decoder *cxlsd, u64 *tgt)
@@ -764,7 +765,7 @@ static int cxl_setup_hdm_decoder_from_dvsec(
        if (!len)
                return -ENOENT;
 
-       cxld->target_type = CXL_DECODER_EXPANDER;
+       cxld->target_type = CXL_DECODER_HOSTONLYMEM;
        cxld->commit = NULL;
        cxld->reset = NULL;
        cxld->hpa_range = info->dvsec_range[which];
@@ -793,8 +794,8 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
                            int *target_map, void __iomem *hdm, int which,
                            u64 *dpa_base, struct cxl_endpoint_dvsec_info *info)
 {
+       struct cxl_endpoint_decoder *cxled = NULL;
        u64 size, base, skip, dpa_size, lo, hi;
-       struct cxl_endpoint_decoder *cxled;
        bool committed;
        u32 remainder;
        int i, rc;
@@ -827,6 +828,8 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
                return -ENXIO;
        }
 
+       if (info)
+               cxled = to_cxl_endpoint_decoder(&cxld->dev);
        cxld->hpa_range = (struct range) {
                .start = base,
                .end = base + size - 1,
@@ -837,10 +840,10 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
                cxld->flags |= CXL_DECODER_F_ENABLE;
                if (ctrl & CXL_HDM_DECODER0_CTRL_LOCK)
                        cxld->flags |= CXL_DECODER_F_LOCK;
-               if (FIELD_GET(CXL_HDM_DECODER0_CTRL_TYPE, ctrl))
-                       cxld->target_type = CXL_DECODER_EXPANDER;
+               if (FIELD_GET(CXL_HDM_DECODER0_CTRL_HOSTONLY, ctrl))
+                       cxld->target_type = CXL_DECODER_HOSTONLYMEM;
                else
-                       cxld->target_type = CXL_DECODER_ACCELERATOR;
+                       cxld->target_type = CXL_DECODER_DEVMEM;
                if (cxld->id != port->commit_end + 1) {
                        dev_warn(&port->dev,
                                 "decoder%d.%d: Committed out of order\n",
@@ -856,12 +859,28 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
                }
                port->commit_end = cxld->id;
        } else {
-               /* unless / until type-2 drivers arrive, assume type-3 */
-               if (FIELD_GET(CXL_HDM_DECODER0_CTRL_TYPE, ctrl) == 0) {
-                       ctrl |= CXL_HDM_DECODER0_CTRL_TYPE;
+               if (cxled) {
+                       struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
+                       struct cxl_dev_state *cxlds = cxlmd->cxlds;
+
+                       /*
+                        * Default by devtype until a device arrives that needs
+                        * more precision.
+                        */
+                       if (cxlds->type == CXL_DEVTYPE_CLASSMEM)
+                               cxld->target_type = CXL_DECODER_HOSTONLYMEM;
+                       else
+                               cxld->target_type = CXL_DECODER_DEVMEM;
+               } else {
+                       /* To be overridden by region type at commit time */
+                       cxld->target_type = CXL_DECODER_HOSTONLYMEM;
+               }
+
+               if (!FIELD_GET(CXL_HDM_DECODER0_CTRL_HOSTONLY, ctrl) &&
+                   cxld->target_type == CXL_DECODER_HOSTONLYMEM) {
+                       ctrl |= CXL_HDM_DECODER0_CTRL_HOSTONLY;
                        writel(ctrl, hdm + CXL_HDM_DECODER0_CTRL_OFFSET(which));
                }
-               cxld->target_type = CXL_DECODER_EXPANDER;
        }
        rc = eiw_to_ways(FIELD_GET(CXL_HDM_DECODER0_CTRL_IW_MASK, ctrl),
                          &cxld->interleave_ways);
@@ -880,7 +899,7 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
                port->id, cxld->id, cxld->hpa_range.start, cxld->hpa_range.end,
                cxld->interleave_ways, cxld->interleave_granularity);
 
-       if (!info) {
+       if (!cxled) {
                lo = readl(hdm + CXL_HDM_DECODER0_TL_LOW(which));
                hi = readl(hdm + CXL_HDM_DECODER0_TL_HIGH(which));
                target_list.value = (hi << 32) + lo;
@@ -903,7 +922,6 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
        lo = readl(hdm + CXL_HDM_DECODER0_SKIP_LOW(which));
        hi = readl(hdm + CXL_HDM_DECODER0_SKIP_HIGH(which));
        skip = (hi << 32) + lo;
-       cxled = to_cxl_endpoint_decoder(&cxld->dev);
        rc = devm_cxl_dpa_reserve(cxled, *dpa_base + skip, dpa_size, skip);
        if (rc) {
                dev_err(&port->dev,
index bea9cf3..d6d067f 100644 (file)
@@ -182,7 +182,7 @@ static const char *cxl_mem_opcode_to_name(u16 opcode)
 
 /**
  * cxl_internal_send_cmd() - Kernel internal interface to send a mailbox command
- * @cxlds: The device data for the operation
+ * @mds: The driver data for the operation
  * @mbox_cmd: initialized command to execute
  *
  * Context: Any context.
@@ -198,19 +198,19 @@ static const char *cxl_mem_opcode_to_name(u16 opcode)
  * error. While this distinction can be useful for commands from userspace, the
  * kernel will only be able to use results when both are successful.
  */
-int cxl_internal_send_cmd(struct cxl_dev_state *cxlds,
+int cxl_internal_send_cmd(struct cxl_memdev_state *mds,
                          struct cxl_mbox_cmd *mbox_cmd)
 {
        size_t out_size, min_out;
        int rc;
 
-       if (mbox_cmd->size_in > cxlds->payload_size ||
-           mbox_cmd->size_out > cxlds->payload_size)
+       if (mbox_cmd->size_in > mds->payload_size ||
+           mbox_cmd->size_out > mds->payload_size)
                return -E2BIG;
 
        out_size = mbox_cmd->size_out;
        min_out = mbox_cmd->min_out;
-       rc = cxlds->mbox_send(cxlds, mbox_cmd);
+       rc = mds->mbox_send(mds, mbox_cmd);
        /*
         * EIO is reserved for a payload size mismatch and mbox_send()
         * may not return this error.
@@ -220,7 +220,8 @@ int cxl_internal_send_cmd(struct cxl_dev_state *cxlds,
        if (rc)
                return rc;
 
-       if (mbox_cmd->return_code != CXL_MBOX_CMD_RC_SUCCESS)
+       if (mbox_cmd->return_code != CXL_MBOX_CMD_RC_SUCCESS &&
+           mbox_cmd->return_code != CXL_MBOX_CMD_RC_BACKGROUND)
                return cxl_mbox_cmd_rc2errno(mbox_cmd);
 
        if (!out_size)
@@ -297,7 +298,7 @@ static bool cxl_payload_from_user_allowed(u16 opcode, void *payload_in)
 }
 
 static int cxl_mbox_cmd_ctor(struct cxl_mbox_cmd *mbox,
-                            struct cxl_dev_state *cxlds, u16 opcode,
+                            struct cxl_memdev_state *mds, u16 opcode,
                             size_t in_size, size_t out_size, u64 in_payload)
 {
        *mbox = (struct cxl_mbox_cmd) {
@@ -312,7 +313,7 @@ static int cxl_mbox_cmd_ctor(struct cxl_mbox_cmd *mbox,
                        return PTR_ERR(mbox->payload_in);
 
                if (!cxl_payload_from_user_allowed(opcode, mbox->payload_in)) {
-                       dev_dbg(cxlds->dev, "%s: input payload not allowed\n",
+                       dev_dbg(mds->cxlds.dev, "%s: input payload not allowed\n",
                                cxl_mem_opcode_to_name(opcode));
                        kvfree(mbox->payload_in);
                        return -EBUSY;
@@ -321,7 +322,7 @@ static int cxl_mbox_cmd_ctor(struct cxl_mbox_cmd *mbox,
 
        /* Prepare to handle a full payload for variable sized output */
        if (out_size == CXL_VARIABLE_PAYLOAD)
-               mbox->size_out = cxlds->payload_size;
+               mbox->size_out = mds->payload_size;
        else
                mbox->size_out = out_size;
 
@@ -343,7 +344,7 @@ static void cxl_mbox_cmd_dtor(struct cxl_mbox_cmd *mbox)
 
 static int cxl_to_mem_cmd_raw(struct cxl_mem_command *mem_cmd,
                              const struct cxl_send_command *send_cmd,
-                             struct cxl_dev_state *cxlds)
+                             struct cxl_memdev_state *mds)
 {
        if (send_cmd->raw.rsvd)
                return -EINVAL;
@@ -353,13 +354,13 @@ static int cxl_to_mem_cmd_raw(struct cxl_mem_command *mem_cmd,
         * gets passed along without further checking, so it must be
         * validated here.
         */
-       if (send_cmd->out.size > cxlds->payload_size)
+       if (send_cmd->out.size > mds->payload_size)
                return -EINVAL;
 
        if (!cxl_mem_raw_command_allowed(send_cmd->raw.opcode))
                return -EPERM;
 
-       dev_WARN_ONCE(cxlds->dev, true, "raw command path used\n");
+       dev_WARN_ONCE(mds->cxlds.dev, true, "raw command path used\n");
 
        *mem_cmd = (struct cxl_mem_command) {
                .info = {
@@ -375,7 +376,7 @@ static int cxl_to_mem_cmd_raw(struct cxl_mem_command *mem_cmd,
 
 static int cxl_to_mem_cmd(struct cxl_mem_command *mem_cmd,
                          const struct cxl_send_command *send_cmd,
-                         struct cxl_dev_state *cxlds)
+                         struct cxl_memdev_state *mds)
 {
        struct cxl_mem_command *c = &cxl_mem_commands[send_cmd->id];
        const struct cxl_command_info *info = &c->info;
@@ -390,11 +391,11 @@ static int cxl_to_mem_cmd(struct cxl_mem_command *mem_cmd,
                return -EINVAL;
 
        /* Check that the command is enabled for hardware */
-       if (!test_bit(info->id, cxlds->enabled_cmds))
+       if (!test_bit(info->id, mds->enabled_cmds))
                return -ENOTTY;
 
        /* Check that the command is not claimed for exclusive kernel use */
-       if (test_bit(info->id, cxlds->exclusive_cmds))
+       if (test_bit(info->id, mds->exclusive_cmds))
                return -EBUSY;
 
        /* Check the input buffer is the expected size */
@@ -423,7 +424,7 @@ static int cxl_to_mem_cmd(struct cxl_mem_command *mem_cmd,
 /**
  * cxl_validate_cmd_from_user() - Check fields for CXL_MEM_SEND_COMMAND.
  * @mbox_cmd: Sanitized and populated &struct cxl_mbox_cmd.
- * @cxlds: The device data for the operation
+ * @mds: The driver data for the operation
  * @send_cmd: &struct cxl_send_command copied in from userspace.
  *
  * Return:
@@ -438,7 +439,7 @@ static int cxl_to_mem_cmd(struct cxl_mem_command *mem_cmd,
  * safe to send to the hardware.
  */
 static int cxl_validate_cmd_from_user(struct cxl_mbox_cmd *mbox_cmd,
-                                     struct cxl_dev_state *cxlds,
+                                     struct cxl_memdev_state *mds,
                                      const struct cxl_send_command *send_cmd)
 {
        struct cxl_mem_command mem_cmd;
@@ -452,20 +453,20 @@ static int cxl_validate_cmd_from_user(struct cxl_mbox_cmd *mbox_cmd,
         * supports, but output can be arbitrarily large (simply write out as
         * much data as the hardware provides).
         */
-       if (send_cmd->in.size > cxlds->payload_size)
+       if (send_cmd->in.size > mds->payload_size)
                return -EINVAL;
 
        /* Sanitize and construct a cxl_mem_command */
        if (send_cmd->id == CXL_MEM_COMMAND_ID_RAW)
-               rc = cxl_to_mem_cmd_raw(&mem_cmd, send_cmd, cxlds);
+               rc = cxl_to_mem_cmd_raw(&mem_cmd, send_cmd, mds);
        else
-               rc = cxl_to_mem_cmd(&mem_cmd, send_cmd, cxlds);
+               rc = cxl_to_mem_cmd(&mem_cmd, send_cmd, mds);
 
        if (rc)
                return rc;
 
        /* Sanitize and construct a cxl_mbox_cmd */
-       return cxl_mbox_cmd_ctor(mbox_cmd, cxlds, mem_cmd.opcode,
+       return cxl_mbox_cmd_ctor(mbox_cmd, mds, mem_cmd.opcode,
                                 mem_cmd.info.size_in, mem_cmd.info.size_out,
                                 send_cmd->in.payload);
 }
@@ -473,6 +474,7 @@ static int cxl_validate_cmd_from_user(struct cxl_mbox_cmd *mbox_cmd,
 int cxl_query_cmd(struct cxl_memdev *cxlmd,
                  struct cxl_mem_query_commands __user *q)
 {
+       struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
        struct device *dev = &cxlmd->dev;
        struct cxl_mem_command *cmd;
        u32 n_commands;
@@ -494,9 +496,9 @@ int cxl_query_cmd(struct cxl_memdev *cxlmd,
        cxl_for_each_cmd(cmd) {
                struct cxl_command_info info = cmd->info;
 
-               if (test_bit(info.id, cxlmd->cxlds->enabled_cmds))
+               if (test_bit(info.id, mds->enabled_cmds))
                        info.flags |= CXL_MEM_COMMAND_FLAG_ENABLED;
-               if (test_bit(info.id, cxlmd->cxlds->exclusive_cmds))
+               if (test_bit(info.id, mds->exclusive_cmds))
                        info.flags |= CXL_MEM_COMMAND_FLAG_EXCLUSIVE;
 
                if (copy_to_user(&q->commands[j++], &info, sizeof(info)))
@@ -511,7 +513,7 @@ int cxl_query_cmd(struct cxl_memdev *cxlmd,
 
 /**
  * handle_mailbox_cmd_from_user() - Dispatch a mailbox command for userspace.
- * @cxlds: The device data for the operation
+ * @mds: The driver data for the operation
  * @mbox_cmd: The validated mailbox command.
  * @out_payload: Pointer to userspace's output payload.
  * @size_out: (Input) Max payload size to copy out.
@@ -532,12 +534,12 @@ int cxl_query_cmd(struct cxl_memdev *cxlmd,
  *
  * See cxl_send_cmd().
  */
-static int handle_mailbox_cmd_from_user(struct cxl_dev_state *cxlds,
+static int handle_mailbox_cmd_from_user(struct cxl_memdev_state *mds,
                                        struct cxl_mbox_cmd *mbox_cmd,
                                        u64 out_payload, s32 *size_out,
                                        u32 *retval)
 {
-       struct device *dev = cxlds->dev;
+       struct device *dev = mds->cxlds.dev;
        int rc;
 
        dev_dbg(dev,
@@ -547,7 +549,7 @@ static int handle_mailbox_cmd_from_user(struct cxl_dev_state *cxlds,
                cxl_mem_opcode_to_name(mbox_cmd->opcode),
                mbox_cmd->opcode, mbox_cmd->size_in);
 
-       rc = cxlds->mbox_send(cxlds, mbox_cmd);
+       rc = mds->mbox_send(mds, mbox_cmd);
        if (rc)
                goto out;
 
@@ -576,7 +578,7 @@ out:
 
 int cxl_send_cmd(struct cxl_memdev *cxlmd, struct cxl_send_command __user *s)
 {
-       struct cxl_dev_state *cxlds = cxlmd->cxlds;
+       struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
        struct device *dev = &cxlmd->dev;
        struct cxl_send_command send;
        struct cxl_mbox_cmd mbox_cmd;
@@ -587,11 +589,11 @@ int cxl_send_cmd(struct cxl_memdev *cxlmd, struct cxl_send_command __user *s)
        if (copy_from_user(&send, s, sizeof(send)))
                return -EFAULT;
 
-       rc = cxl_validate_cmd_from_user(&mbox_cmd, cxlmd->cxlds, &send);
+       rc = cxl_validate_cmd_from_user(&mbox_cmd, mds, &send);
        if (rc)
                return rc;
 
-       rc = handle_mailbox_cmd_from_user(cxlds, &mbox_cmd, send.out.payload,
+       rc = handle_mailbox_cmd_from_user(mds, &mbox_cmd, send.out.payload,
                                          &send.out.size, &send.retval);
        if (rc)
                return rc;
@@ -602,13 +604,14 @@ int cxl_send_cmd(struct cxl_memdev *cxlmd, struct cxl_send_command __user *s)
        return 0;
 }
 
-static int cxl_xfer_log(struct cxl_dev_state *cxlds, uuid_t *uuid, u32 *size, u8 *out)
+static int cxl_xfer_log(struct cxl_memdev_state *mds, uuid_t *uuid,
+                       u32 *size, u8 *out)
 {
        u32 remaining = *size;
        u32 offset = 0;
 
        while (remaining) {
-               u32 xfer_size = min_t(u32, remaining, cxlds->payload_size);
+               u32 xfer_size = min_t(u32, remaining, mds->payload_size);
                struct cxl_mbox_cmd mbox_cmd;
                struct cxl_mbox_get_log log;
                int rc;
@@ -627,7 +630,7 @@ static int cxl_xfer_log(struct cxl_dev_state *cxlds, uuid_t *uuid, u32 *size, u8
                        .payload_out = out,
                };
 
-               rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
+               rc = cxl_internal_send_cmd(mds, &mbox_cmd);
 
                /*
                 * The output payload length that indicates the number
@@ -654,17 +657,18 @@ static int cxl_xfer_log(struct cxl_dev_state *cxlds, uuid_t *uuid, u32 *size, u8
 
 /**
  * cxl_walk_cel() - Walk through the Command Effects Log.
- * @cxlds: The device data for the operation
+ * @mds: The driver data for the operation
  * @size: Length of the Command Effects Log.
  * @cel: CEL
  *
  * Iterate over each entry in the CEL and determine if the driver supports the
  * command. If so, the command is enabled for the device and can be used later.
  */
-static void cxl_walk_cel(struct cxl_dev_state *cxlds, size_t size, u8 *cel)
+static void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel)
 {
        struct cxl_cel_entry *cel_entry;
        const int cel_entries = size / sizeof(*cel_entry);
+       struct device *dev = mds->cxlds.dev;
        int i;
 
        cel_entry = (struct cxl_cel_entry *) cel;
@@ -674,39 +678,39 @@ static void cxl_walk_cel(struct cxl_dev_state *cxlds, size_t size, u8 *cel)
                struct cxl_mem_command *cmd = cxl_mem_find_command(opcode);
 
                if (!cmd && !cxl_is_poison_command(opcode)) {
-                       dev_dbg(cxlds->dev,
+                       dev_dbg(dev,
                                "Opcode 0x%04x unsupported by driver\n", opcode);
                        continue;
                }
 
                if (cmd)
-                       set_bit(cmd->info.id, cxlds->enabled_cmds);
+                       set_bit(cmd->info.id, mds->enabled_cmds);
 
                if (cxl_is_poison_command(opcode))
-                       cxl_set_poison_cmd_enabled(&cxlds->poison, opcode);
+                       cxl_set_poison_cmd_enabled(&mds->poison, opcode);
 
-               dev_dbg(cxlds->dev, "Opcode 0x%04x enabled\n", opcode);
+               dev_dbg(dev, "Opcode 0x%04x enabled\n", opcode);
        }
 }
 
-static struct cxl_mbox_get_supported_logs *cxl_get_gsl(struct cxl_dev_state *cxlds)
+static struct cxl_mbox_get_supported_logs *cxl_get_gsl(struct cxl_memdev_state *mds)
 {
        struct cxl_mbox_get_supported_logs *ret;
        struct cxl_mbox_cmd mbox_cmd;
        int rc;
 
-       ret = kvmalloc(cxlds->payload_size, GFP_KERNEL);
+       ret = kvmalloc(mds->payload_size, GFP_KERNEL);
        if (!ret)
                return ERR_PTR(-ENOMEM);
 
        mbox_cmd = (struct cxl_mbox_cmd) {
                .opcode = CXL_MBOX_OP_GET_SUPPORTED_LOGS,
-               .size_out = cxlds->payload_size,
+               .size_out = mds->payload_size,
                .payload_out = ret,
                /* At least the record number field must be valid */
                .min_out = 2,
        };
-       rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
+       rc = cxl_internal_send_cmd(mds, &mbox_cmd);
        if (rc < 0) {
                kvfree(ret);
                return ERR_PTR(rc);
@@ -729,22 +733,22 @@ static const uuid_t log_uuid[] = {
 
 /**
  * cxl_enumerate_cmds() - Enumerate commands for a device.
- * @cxlds: The device data for the operation
+ * @mds: The driver data for the operation
  *
  * Returns 0 if enumerate completed successfully.
  *
  * CXL devices have optional support for certain commands. This function will
  * determine the set of supported commands for the hardware and update the
- * enabled_cmds bitmap in the @cxlds.
+ * enabled_cmds bitmap in the @mds.
  */
-int cxl_enumerate_cmds(struct cxl_dev_state *cxlds)
+int cxl_enumerate_cmds(struct cxl_memdev_state *mds)
 {
        struct cxl_mbox_get_supported_logs *gsl;
-       struct device *dev = cxlds->dev;
+       struct device *dev = mds->cxlds.dev;
        struct cxl_mem_command *cmd;
        int i, rc;
 
-       gsl = cxl_get_gsl(cxlds);
+       gsl = cxl_get_gsl(mds);
        if (IS_ERR(gsl))
                return PTR_ERR(gsl);
 
@@ -765,19 +769,19 @@ int cxl_enumerate_cmds(struct cxl_dev_state *cxlds)
                        goto out;
                }
 
-               rc = cxl_xfer_log(cxlds, &uuid, &size, log);
+               rc = cxl_xfer_log(mds, &uuid, &size, log);
                if (rc) {
                        kvfree(log);
                        goto out;
                }
 
-               cxl_walk_cel(cxlds, size, log);
+               cxl_walk_cel(mds, size, log);
                kvfree(log);
 
                /* In case CEL was bogus, enable some default commands. */
                cxl_for_each_cmd(cmd)
                        if (cmd->flags & CXL_CMD_FLAG_FORCE_ENABLE)
-                               set_bit(cmd->info.id, cxlds->enabled_cmds);
+                               set_bit(cmd->info.id, mds->enabled_cmds);
 
                /* Found the required CEL */
                rc = 0;
@@ -838,7 +842,7 @@ static void cxl_event_trace_record(const struct cxl_memdev *cxlmd,
        }
 }
 
-static int cxl_clear_event_record(struct cxl_dev_state *cxlds,
+static int cxl_clear_event_record(struct cxl_memdev_state *mds,
                                  enum cxl_event_log_type log,
                                  struct cxl_get_event_payload *get_pl)
 {
@@ -852,9 +856,9 @@ static int cxl_clear_event_record(struct cxl_dev_state *cxlds,
        int i;
 
        /* Payload size may limit the max handles */
-       if (pl_size > cxlds->payload_size) {
-               max_handles = (cxlds->payload_size - sizeof(*payload)) /
-                               sizeof(__le16);
+       if (pl_size > mds->payload_size) {
+               max_handles = (mds->payload_size - sizeof(*payload)) /
+                             sizeof(__le16);
                pl_size = struct_size(payload, handles, max_handles);
        }
 
@@ -879,12 +883,12 @@ static int cxl_clear_event_record(struct cxl_dev_state *cxlds,
        i = 0;
        for (cnt = 0; cnt < total; cnt++) {
                payload->handles[i++] = get_pl->records[cnt].hdr.handle;
-               dev_dbg(cxlds->dev, "Event log '%d': Clearing %u\n",
-                       log, le16_to_cpu(payload->handles[i]));
+               dev_dbg(mds->cxlds.dev, "Event log '%d': Clearing %u\n", log,
+                       le16_to_cpu(payload->handles[i]));
 
                if (i == max_handles) {
                        payload->nr_recs = i;
-                       rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
+                       rc = cxl_internal_send_cmd(mds, &mbox_cmd);
                        if (rc)
                                goto free_pl;
                        i = 0;
@@ -895,7 +899,7 @@ static int cxl_clear_event_record(struct cxl_dev_state *cxlds,
        if (i) {
                payload->nr_recs = i;
                mbox_cmd.size_in = struct_size(payload, handles, i);
-               rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
+               rc = cxl_internal_send_cmd(mds, &mbox_cmd);
                if (rc)
                        goto free_pl;
        }
@@ -905,32 +909,34 @@ free_pl:
        return rc;
 }
 
-static void cxl_mem_get_records_log(struct cxl_dev_state *cxlds,
+static void cxl_mem_get_records_log(struct cxl_memdev_state *mds,
                                    enum cxl_event_log_type type)
 {
+       struct cxl_memdev *cxlmd = mds->cxlds.cxlmd;
+       struct device *dev = mds->cxlds.dev;
        struct cxl_get_event_payload *payload;
        struct cxl_mbox_cmd mbox_cmd;
        u8 log_type = type;
        u16 nr_rec;
 
-       mutex_lock(&cxlds->event.log_lock);
-       payload = cxlds->event.buf;
+       mutex_lock(&mds->event.log_lock);
+       payload = mds->event.buf;
 
        mbox_cmd = (struct cxl_mbox_cmd) {
                .opcode = CXL_MBOX_OP_GET_EVENT_RECORD,
                .payload_in = &log_type,
                .size_in = sizeof(log_type),
                .payload_out = payload,
-               .size_out = cxlds->payload_size,
+               .size_out = mds->payload_size,
                .min_out = struct_size(payload, records, 0),
        };
 
        do {
                int rc, i;
 
-               rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
+               rc = cxl_internal_send_cmd(mds, &mbox_cmd);
                if (rc) {
-                       dev_err_ratelimited(cxlds->dev,
+                       dev_err_ratelimited(dev,
                                "Event log '%d': Failed to query event records : %d",
                                type, rc);
                        break;
@@ -941,27 +947,27 @@ static void cxl_mem_get_records_log(struct cxl_dev_state *cxlds,
                        break;
 
                for (i = 0; i < nr_rec; i++)
-                       cxl_event_trace_record(cxlds->cxlmd, type,
+                       cxl_event_trace_record(cxlmd, type,
                                               &payload->records[i]);
 
                if (payload->flags & CXL_GET_EVENT_FLAG_OVERFLOW)
-                       trace_cxl_overflow(cxlds->cxlmd, type, payload);
+                       trace_cxl_overflow(cxlmd, type, payload);
 
-               rc = cxl_clear_event_record(cxlds, type, payload);
+               rc = cxl_clear_event_record(mds, type, payload);
                if (rc) {
-                       dev_err_ratelimited(cxlds->dev,
+                       dev_err_ratelimited(dev,
                                "Event log '%d': Failed to clear events : %d",
                                type, rc);
                        break;
                }
        } while (nr_rec);
 
-       mutex_unlock(&cxlds->event.log_lock);
+       mutex_unlock(&mds->event.log_lock);
 }
 
 /**
  * cxl_mem_get_event_records - Get Event Records from the device
- * @cxlds: The device data for the operation
+ * @mds: The driver data for the operation
  * @status: Event Status register value identifying which events are available.
  *
  * Retrieve all event records available on the device, report them as trace
@@ -970,24 +976,24 @@ static void cxl_mem_get_records_log(struct cxl_dev_state *cxlds,
  * See CXL rev 3.0 @8.2.9.2.2 Get Event Records
  * See CXL rev 3.0 @8.2.9.2.3 Clear Event Records
  */
-void cxl_mem_get_event_records(struct cxl_dev_state *cxlds, u32 status)
+void cxl_mem_get_event_records(struct cxl_memdev_state *mds, u32 status)
 {
-       dev_dbg(cxlds->dev, "Reading event logs: %x\n", status);
+       dev_dbg(mds->cxlds.dev, "Reading event logs: %x\n", status);
 
        if (status & CXLDEV_EVENT_STATUS_FATAL)
-               cxl_mem_get_records_log(cxlds, CXL_EVENT_TYPE_FATAL);
+               cxl_mem_get_records_log(mds, CXL_EVENT_TYPE_FATAL);
        if (status & CXLDEV_EVENT_STATUS_FAIL)
-               cxl_mem_get_records_log(cxlds, CXL_EVENT_TYPE_FAIL);
+               cxl_mem_get_records_log(mds, CXL_EVENT_TYPE_FAIL);
        if (status & CXLDEV_EVENT_STATUS_WARN)
-               cxl_mem_get_records_log(cxlds, CXL_EVENT_TYPE_WARN);
+               cxl_mem_get_records_log(mds, CXL_EVENT_TYPE_WARN);
        if (status & CXLDEV_EVENT_STATUS_INFO)
-               cxl_mem_get_records_log(cxlds, CXL_EVENT_TYPE_INFO);
+               cxl_mem_get_records_log(mds, CXL_EVENT_TYPE_INFO);
 }
 EXPORT_SYMBOL_NS_GPL(cxl_mem_get_event_records, CXL);
 
 /**
  * cxl_mem_get_partition_info - Get partition info
- * @cxlds: The device data for the operation
+ * @mds: The driver data for the operation
  *
  * Retrieve the current partition info for the device specified.  The active
  * values are the current capacity in bytes.  If not 0, the 'next' values are
@@ -997,7 +1003,7 @@ EXPORT_SYMBOL_NS_GPL(cxl_mem_get_event_records, CXL);
  *
  * See CXL @8.2.9.5.2.1 Get Partition Info
  */
-static int cxl_mem_get_partition_info(struct cxl_dev_state *cxlds)
+static int cxl_mem_get_partition_info(struct cxl_memdev_state *mds)
 {
        struct cxl_mbox_get_partition_info pi;
        struct cxl_mbox_cmd mbox_cmd;
@@ -1008,17 +1014,17 @@ static int cxl_mem_get_partition_info(struct cxl_dev_state *cxlds)
                .size_out = sizeof(pi),
                .payload_out = &pi,
        };
-       rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
+       rc = cxl_internal_send_cmd(mds, &mbox_cmd);
        if (rc)
                return rc;
 
-       cxlds->active_volatile_bytes =
+       mds->active_volatile_bytes =
                le64_to_cpu(pi.active_volatile_cap) * CXL_CAPACITY_MULTIPLIER;
-       cxlds->active_persistent_bytes =
+       mds->active_persistent_bytes =
                le64_to_cpu(pi.active_persistent_cap) * CXL_CAPACITY_MULTIPLIER;
-       cxlds->next_volatile_bytes =
+       mds->next_volatile_bytes =
                le64_to_cpu(pi.next_volatile_cap) * CXL_CAPACITY_MULTIPLIER;
-       cxlds->next_persistent_bytes =
+       mds->next_persistent_bytes =
                le64_to_cpu(pi.next_volatile_cap) * CXL_CAPACITY_MULTIPLIER;
 
        return 0;
@@ -1026,14 +1032,14 @@ static int cxl_mem_get_partition_info(struct cxl_dev_state *cxlds)
 
 /**
  * cxl_dev_state_identify() - Send the IDENTIFY command to the device.
- * @cxlds: The device data for the operation
+ * @mds: The driver data for the operation
  *
  * Return: 0 if identify was executed successfully or media not ready.
  *
  * This will dispatch the identify command to the device and on success populate
  * structures to be exported to sysfs.
  */
-int cxl_dev_state_identify(struct cxl_dev_state *cxlds)
+int cxl_dev_state_identify(struct cxl_memdev_state *mds)
 {
        /* See CXL 2.0 Table 175 Identify Memory Device Output Payload */
        struct cxl_mbox_identify id;
@@ -1041,7 +1047,7 @@ int cxl_dev_state_identify(struct cxl_dev_state *cxlds)
        u32 val;
        int rc;
 
-       if (!cxlds->media_ready)
+       if (!mds->cxlds.media_ready)
                return 0;
 
        mbox_cmd = (struct cxl_mbox_cmd) {
@@ -1049,31 +1055,92 @@ int cxl_dev_state_identify(struct cxl_dev_state *cxlds)
                .size_out = sizeof(id),
                .payload_out = &id,
        };
-       rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
+       rc = cxl_internal_send_cmd(mds, &mbox_cmd);
        if (rc < 0)
                return rc;
 
-       cxlds->total_bytes =
+       mds->total_bytes =
                le64_to_cpu(id.total_capacity) * CXL_CAPACITY_MULTIPLIER;
-       cxlds->volatile_only_bytes =
+       mds->volatile_only_bytes =
                le64_to_cpu(id.volatile_capacity) * CXL_CAPACITY_MULTIPLIER;
-       cxlds->persistent_only_bytes =
+       mds->persistent_only_bytes =
                le64_to_cpu(id.persistent_capacity) * CXL_CAPACITY_MULTIPLIER;
-       cxlds->partition_align_bytes =
+       mds->partition_align_bytes =
                le64_to_cpu(id.partition_align) * CXL_CAPACITY_MULTIPLIER;
 
-       cxlds->lsa_size = le32_to_cpu(id.lsa_size);
-       memcpy(cxlds->firmware_version, id.fw_revision, sizeof(id.fw_revision));
+       mds->lsa_size = le32_to_cpu(id.lsa_size);
+       memcpy(mds->firmware_version, id.fw_revision,
+              sizeof(id.fw_revision));
 
-       if (test_bit(CXL_POISON_ENABLED_LIST, cxlds->poison.enabled_cmds)) {
+       if (test_bit(CXL_POISON_ENABLED_LIST, mds->poison.enabled_cmds)) {
                val = get_unaligned_le24(id.poison_list_max_mer);
-               cxlds->poison.max_errors = min_t(u32, val, CXL_POISON_LIST_MAX);
+               mds->poison.max_errors = min_t(u32, val, CXL_POISON_LIST_MAX);
        }
 
        return 0;
 }
 EXPORT_SYMBOL_NS_GPL(cxl_dev_state_identify, CXL);
 
+/**
+ * cxl_mem_sanitize() - Send a sanitization command to the device.
+ * @mds: 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_memdev_state *mds, 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 };
+       struct cxl_dev_state *cxlds = &mds->cxlds;
+
+       if (cmd != CXL_MBOX_OP_SANITIZE && cmd != CXL_MBOX_OP_SECURE_ERASE)
+               return -EINVAL;
+
+       rc = cxl_internal_send_cmd(mds, &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;
+
+       if (cmd == CXL_MBOX_OP_SECURE_ERASE &&
+           sec_out & CXL_PMEM_SEC_STATE_LOCKED)
+               return -EINVAL;
+
+       rc = cxl_internal_send_cmd(mds, &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)
@@ -1100,8 +1167,9 @@ static int add_dpa_res(struct device *dev, struct resource *parent,
        return 0;
 }
 
-int cxl_mem_create_range_info(struct cxl_dev_state *cxlds)
+int cxl_mem_create_range_info(struct cxl_memdev_state *mds)
 {
+       struct cxl_dev_state *cxlds = &mds->cxlds;
        struct device *dev = cxlds->dev;
        int rc;
 
@@ -1113,35 +1181,35 @@ int cxl_mem_create_range_info(struct cxl_dev_state *cxlds)
        }
 
        cxlds->dpa_res =
-               (struct resource)DEFINE_RES_MEM(0, cxlds->total_bytes);
+               (struct resource)DEFINE_RES_MEM(0, mds->total_bytes);
 
-       if (cxlds->partition_align_bytes == 0) {
+       if (mds->partition_align_bytes == 0) {
                rc = add_dpa_res(dev, &cxlds->dpa_res, &cxlds->ram_res, 0,
-                                cxlds->volatile_only_bytes, "ram");
+                                mds->volatile_only_bytes, "ram");
                if (rc)
                        return rc;
                return add_dpa_res(dev, &cxlds->dpa_res, &cxlds->pmem_res,
-                                  cxlds->volatile_only_bytes,
-                                  cxlds->persistent_only_bytes, "pmem");
+                                  mds->volatile_only_bytes,
+                                  mds->persistent_only_bytes, "pmem");
        }
 
-       rc = cxl_mem_get_partition_info(cxlds);
+       rc = cxl_mem_get_partition_info(mds);
        if (rc) {
                dev_err(dev, "Failed to query partition information\n");
                return rc;
        }
 
        rc = add_dpa_res(dev, &cxlds->dpa_res, &cxlds->ram_res, 0,
-                        cxlds->active_volatile_bytes, "ram");
+                        mds->active_volatile_bytes, "ram");
        if (rc)
                return rc;
        return add_dpa_res(dev, &cxlds->dpa_res, &cxlds->pmem_res,
-                          cxlds->active_volatile_bytes,
-                          cxlds->active_persistent_bytes, "pmem");
+                          mds->active_volatile_bytes,
+                          mds->active_persistent_bytes, "pmem");
 }
 EXPORT_SYMBOL_NS_GPL(cxl_mem_create_range_info, CXL);
 
-int cxl_set_timestamp(struct cxl_dev_state *cxlds)
+int cxl_set_timestamp(struct cxl_memdev_state *mds)
 {
        struct cxl_mbox_cmd mbox_cmd;
        struct cxl_mbox_set_timestamp_in pi;
@@ -1154,7 +1222,7 @@ int cxl_set_timestamp(struct cxl_dev_state *cxlds)
                .payload_in = &pi,
        };
 
-       rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
+       rc = cxl_internal_send_cmd(mds, &mbox_cmd);
        /*
         * Command is optional. Devices may have another way of providing
         * a timestamp, or may return all 0s in timestamp fields.
@@ -1170,18 +1238,18 @@ EXPORT_SYMBOL_NS_GPL(cxl_set_timestamp, CXL);
 int cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len,
                       struct cxl_region *cxlr)
 {
-       struct cxl_dev_state *cxlds = cxlmd->cxlds;
+       struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
        struct cxl_mbox_poison_out *po;
        struct cxl_mbox_poison_in pi;
        struct cxl_mbox_cmd mbox_cmd;
        int nr_records = 0;
        int rc;
 
-       rc = mutex_lock_interruptible(&cxlds->poison.lock);
+       rc = mutex_lock_interruptible(&mds->poison.lock);
        if (rc)
                return rc;
 
-       po = cxlds->poison.list_out;
+       po = mds->poison.list_out;
        pi.offset = cpu_to_le64(offset);
        pi.length = cpu_to_le64(len / CXL_POISON_LEN_MULT);
 
@@ -1189,13 +1257,13 @@ int cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len,
                .opcode = CXL_MBOX_OP_GET_POISON,
                .size_in = sizeof(pi),
                .payload_in = &pi,
-               .size_out = cxlds->payload_size,
+               .size_out = mds->payload_size,
                .payload_out = po,
                .min_out = struct_size(po, record, 0),
        };
 
        do {
-               rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
+               rc = cxl_internal_send_cmd(mds, &mbox_cmd);
                if (rc)
                        break;
 
@@ -1206,14 +1274,14 @@ int cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len,
 
                /* Protect against an uncleared _FLAG_MORE */
                nr_records = nr_records + le16_to_cpu(po->count);
-               if (nr_records >= cxlds->poison.max_errors) {
+               if (nr_records >= mds->poison.max_errors) {
                        dev_dbg(&cxlmd->dev, "Max Error Records reached: %d\n",
                                nr_records);
                        break;
                }
        } while (po->flags & CXL_POISON_FLAG_MORE);
 
-       mutex_unlock(&cxlds->poison.lock);
+       mutex_unlock(&mds->poison.lock);
        return rc;
 }
 EXPORT_SYMBOL_NS_GPL(cxl_mem_get_poison, CXL);
@@ -1223,52 +1291,53 @@ static void free_poison_buf(void *buf)
        kvfree(buf);
 }
 
-/* Get Poison List output buffer is protected by cxlds->poison.lock */
-static int cxl_poison_alloc_buf(struct cxl_dev_state *cxlds)
+/* Get Poison List output buffer is protected by mds->poison.lock */
+static int cxl_poison_alloc_buf(struct cxl_memdev_state *mds)
 {
-       cxlds->poison.list_out = kvmalloc(cxlds->payload_size, GFP_KERNEL);
-       if (!cxlds->poison.list_out)
+       mds->poison.list_out = kvmalloc(mds->payload_size, GFP_KERNEL);
+       if (!mds->poison.list_out)
                return -ENOMEM;
 
-       return devm_add_action_or_reset(cxlds->dev, free_poison_buf,
-                                       cxlds->poison.list_out);
+       return devm_add_action_or_reset(mds->cxlds.dev, free_poison_buf,
+                                       mds->poison.list_out);
 }
 
-int cxl_poison_state_init(struct cxl_dev_state *cxlds)
+int cxl_poison_state_init(struct cxl_memdev_state *mds)
 {
        int rc;
 
-       if (!test_bit(CXL_POISON_ENABLED_LIST, cxlds->poison.enabled_cmds))
+       if (!test_bit(CXL_POISON_ENABLED_LIST, mds->poison.enabled_cmds))
                return 0;
 
-       rc = cxl_poison_alloc_buf(cxlds);
+       rc = cxl_poison_alloc_buf(mds);
        if (rc) {
-               clear_bit(CXL_POISON_ENABLED_LIST, cxlds->poison.enabled_cmds);
+               clear_bit(CXL_POISON_ENABLED_LIST, mds->poison.enabled_cmds);
                return rc;
        }
 
-       mutex_init(&cxlds->poison.lock);
+       mutex_init(&mds->poison.lock);
        return 0;
 }
 EXPORT_SYMBOL_NS_GPL(cxl_poison_state_init, CXL);
 
-struct cxl_dev_state *cxl_dev_state_create(struct device *dev)
+struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev)
 {
-       struct cxl_dev_state *cxlds;
+       struct cxl_memdev_state *mds;
 
-       cxlds = devm_kzalloc(dev, sizeof(*cxlds), GFP_KERNEL);
-       if (!cxlds) {
+       mds = devm_kzalloc(dev, sizeof(*mds), GFP_KERNEL);
+       if (!mds) {
                dev_err(dev, "No memory available\n");
                return ERR_PTR(-ENOMEM);
        }
 
-       mutex_init(&cxlds->mbox_mutex);
-       mutex_init(&cxlds->event.log_lock);
-       cxlds->dev = dev;
+       mutex_init(&mds->mbox_mutex);
+       mutex_init(&mds->event.log_lock);
+       mds->cxlds.dev = dev;
+       mds->cxlds.type = CXL_DEVTYPE_CLASSMEM;
 
-       return cxlds;
+       return mds;
 }
-EXPORT_SYMBOL_NS_GPL(cxl_dev_state_create, CXL);
+EXPORT_SYMBOL_NS_GPL(cxl_memdev_state_create, CXL);
 
 void __init cxl_mbox_init(void)
 {
index 057a432..90237b9 100644 (file)
@@ -1,6 +1,8 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /* Copyright(c) 2020 Intel Corporation. */
 
+#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/firmware.h>
 #include <linux/device.h>
 #include <linux/slab.h>
 #include <linux/idr.h>
@@ -39,8 +41,11 @@ static ssize_t firmware_version_show(struct device *dev,
 {
        struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
        struct cxl_dev_state *cxlds = cxlmd->cxlds;
+       struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds);
 
-       return sysfs_emit(buf, "%.16s\n", cxlds->firmware_version);
+       if (!mds)
+               return sysfs_emit(buf, "\n");
+       return sysfs_emit(buf, "%.16s\n", mds->firmware_version);
 }
 static DEVICE_ATTR_RO(firmware_version);
 
@@ -49,8 +54,11 @@ static ssize_t payload_max_show(struct device *dev,
 {
        struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
        struct cxl_dev_state *cxlds = cxlmd->cxlds;
+       struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds);
 
-       return sysfs_emit(buf, "%zu\n", cxlds->payload_size);
+       if (!mds)
+               return sysfs_emit(buf, "\n");
+       return sysfs_emit(buf, "%zu\n", mds->payload_size);
 }
 static DEVICE_ATTR_RO(payload_max);
 
@@ -59,8 +67,11 @@ static ssize_t label_storage_size_show(struct device *dev,
 {
        struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
        struct cxl_dev_state *cxlds = cxlmd->cxlds;
+       struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds);
 
-       return sysfs_emit(buf, "%zu\n", cxlds->lsa_size);
+       if (!mds)
+               return sysfs_emit(buf, "\n");
+       return sysfs_emit(buf, "%zu\n", mds->lsa_size);
 }
 static DEVICE_ATTR_RO(label_storage_size);
 
@@ -107,6 +118,89 @@ static ssize_t numa_node_show(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR_RO(numa_node);
 
+static ssize_t security_state_show(struct device *dev,
+                                  struct device_attribute *attr,
+                                  char *buf)
+{
+       struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
+       struct cxl_dev_state *cxlds = cxlmd->cxlds;
+       struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds);
+       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);
+       unsigned long state = mds->security.state;
+
+       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");
+       if (state & CXL_PMEM_SEC_STATE_FROZEN ||
+           state & CXL_PMEM_SEC_STATE_MASTER_PLIMIT ||
+           state & CXL_PMEM_SEC_STATE_USER_PLIMIT)
+               return sysfs_emit(buf, "frozen\n");
+       if (state & CXL_PMEM_SEC_STATE_LOCKED)
+               return sysfs_emit(buf, "locked\n");
+       else
+               return sysfs_emit(buf, "unlocked\n");
+}
+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_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
+       struct cxl_port *port = cxlmd->endpoint;
+       bool sanitize;
+       ssize_t rc;
+
+       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(mds, CXL_MBOX_OP_SANITIZE);
+
+       return rc ? rc : len;
+}
+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_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
+       struct cxl_port *port = cxlmd->endpoint;
+       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(mds, 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;
@@ -140,7 +234,7 @@ int cxl_trigger_poison_list(struct cxl_memdev *cxlmd)
        struct cxl_port *port;
        int rc;
 
-       port = dev_get_drvdata(&cxlmd->dev);
+       port = cxlmd->endpoint;
        if (!port || !is_cxl_endpoint(port))
                return -EINVAL;
 
@@ -198,7 +292,7 @@ static struct cxl_region *cxl_dpa_to_region(struct cxl_memdev *cxlmd, u64 dpa)
        ctx = (struct cxl_dpa_to_region_context) {
                .dpa = dpa,
        };
-       port = dev_get_drvdata(&cxlmd->dev);
+       port = cxlmd->endpoint;
        if (port && is_cxl_endpoint(port) && port->commit_end != -1)
                device_for_each_child(&port->dev, &ctx, __cxl_dpa_to_region);
 
@@ -231,7 +325,7 @@ static int cxl_validate_poison_dpa(struct cxl_memdev *cxlmd, u64 dpa)
 
 int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa)
 {
-       struct cxl_dev_state *cxlds = cxlmd->cxlds;
+       struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
        struct cxl_mbox_inject_poison inject;
        struct cxl_poison_record record;
        struct cxl_mbox_cmd mbox_cmd;
@@ -255,13 +349,13 @@ int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa)
                .size_in = sizeof(inject),
                .payload_in = &inject,
        };
-       rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
+       rc = cxl_internal_send_cmd(mds, &mbox_cmd);
        if (rc)
                goto out;
 
        cxlr = cxl_dpa_to_region(cxlmd, dpa);
        if (cxlr)
-               dev_warn_once(cxlds->dev,
+               dev_warn_once(mds->cxlds.dev,
                              "poison inject dpa:%#llx region: %s\n", dpa,
                              dev_name(&cxlr->dev));
 
@@ -279,7 +373,7 @@ EXPORT_SYMBOL_NS_GPL(cxl_inject_poison, CXL);
 
 int cxl_clear_poison(struct cxl_memdev *cxlmd, u64 dpa)
 {
-       struct cxl_dev_state *cxlds = cxlmd->cxlds;
+       struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
        struct cxl_mbox_clear_poison clear;
        struct cxl_poison_record record;
        struct cxl_mbox_cmd mbox_cmd;
@@ -312,14 +406,15 @@ int cxl_clear_poison(struct cxl_memdev *cxlmd, u64 dpa)
                .payload_in = &clear,
        };
 
-       rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
+       rc = cxl_internal_send_cmd(mds, &mbox_cmd);
        if (rc)
                goto out;
 
        cxlr = cxl_dpa_to_region(cxlmd, dpa);
        if (cxlr)
-               dev_warn_once(cxlds->dev, "poison clear dpa:%#llx region: %s\n",
-                             dpa, dev_name(&cxlr->dev));
+               dev_warn_once(mds->cxlds.dev,
+                             "poison clear dpa:%#llx region: %s\n", dpa,
+                             dev_name(&cxlr->dev));
 
        record = (struct cxl_poison_record) {
                .address = cpu_to_le64(dpa),
@@ -352,6 +447,13 @@ static struct attribute *cxl_memdev_ram_attributes[] = {
        NULL,
 };
 
+static struct attribute *cxl_memdev_security_attributes[] = {
+       &dev_attr_security_state.attr,
+       &dev_attr_security_sanitize.attr,
+       &dev_attr_security_erase.attr,
+       NULL,
+};
+
 static umode_t cxl_memdev_visible(struct kobject *kobj, struct attribute *a,
                                  int n)
 {
@@ -375,10 +477,16 @@ static struct attribute_group cxl_memdev_pmem_attribute_group = {
        .attrs = cxl_memdev_pmem_attributes,
 };
 
+static struct attribute_group cxl_memdev_security_attribute_group = {
+       .name = "security",
+       .attrs = cxl_memdev_security_attributes,
+};
+
 static const struct attribute_group *cxl_memdev_attribute_groups[] = {
        &cxl_memdev_attribute_group,
        &cxl_memdev_ram_attribute_group,
        &cxl_memdev_pmem_attribute_group,
+       &cxl_memdev_security_attribute_group,
        NULL,
 };
 
@@ -397,17 +505,18 @@ EXPORT_SYMBOL_NS_GPL(is_cxl_memdev, CXL);
 
 /**
  * set_exclusive_cxl_commands() - atomically disable user cxl commands
- * @cxlds: The device state to operate on
+ * @mds: The device state to operate on
  * @cmds: bitmap of commands to mark exclusive
  *
  * Grab the cxl_memdev_rwsem in write mode to flush in-flight
  * invocations of the ioctl path and then disable future execution of
  * commands with the command ids set in @cmds.
  */
-void set_exclusive_cxl_commands(struct cxl_dev_state *cxlds, unsigned long *cmds)
+void set_exclusive_cxl_commands(struct cxl_memdev_state *mds,
+                               unsigned long *cmds)
 {
        down_write(&cxl_memdev_rwsem);
-       bitmap_or(cxlds->exclusive_cmds, cxlds->exclusive_cmds, cmds,
+       bitmap_or(mds->exclusive_cmds, mds->exclusive_cmds, cmds,
                  CXL_MEM_COMMAND_ID_MAX);
        up_write(&cxl_memdev_rwsem);
 }
@@ -415,23 +524,34 @@ EXPORT_SYMBOL_NS_GPL(set_exclusive_cxl_commands, CXL);
 
 /**
  * clear_exclusive_cxl_commands() - atomically enable user cxl commands
- * @cxlds: The device state to modify
+ * @mds: The device state to modify
  * @cmds: bitmap of commands to mark available for userspace
  */
-void clear_exclusive_cxl_commands(struct cxl_dev_state *cxlds, unsigned long *cmds)
+void clear_exclusive_cxl_commands(struct cxl_memdev_state *mds,
+                                 unsigned long *cmds)
 {
        down_write(&cxl_memdev_rwsem);
-       bitmap_andnot(cxlds->exclusive_cmds, cxlds->exclusive_cmds, cmds,
+       bitmap_andnot(mds->exclusive_cmds, mds->exclusive_cmds, cmds,
                      CXL_MEM_COMMAND_ID_MAX);
        up_write(&cxl_memdev_rwsem);
 }
 EXPORT_SYMBOL_NS_GPL(clear_exclusive_cxl_commands, CXL);
 
+static void cxl_memdev_security_shutdown(struct device *dev)
+{
+       struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
+       struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
+
+       if (mds->security.poll)
+               cancel_delayed_work_sync(&mds->security.poll_dwork);
+}
+
 static void cxl_memdev_shutdown(struct device *dev)
 {
        struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
 
        down_write(&cxl_memdev_rwsem);
+       cxl_memdev_security_shutdown(dev);
        cxlmd->cxlds = NULL;
        up_write(&cxl_memdev_rwsem);
 }
@@ -511,10 +631,12 @@ static long cxl_memdev_ioctl(struct file *file, unsigned int cmd,
                             unsigned long arg)
 {
        struct cxl_memdev *cxlmd = file->private_data;
+       struct cxl_dev_state *cxlds;
        int rc = -ENXIO;
 
        down_read(&cxl_memdev_rwsem);
-       if (cxlmd->cxlds)
+       cxlds = cxlmd->cxlds;
+       if (cxlds && cxlds->type == CXL_DEVTYPE_CLASSMEM)
                rc = __cxl_memdev_ioctl(cxlmd, cmd, arg);
        up_read(&cxl_memdev_rwsem);
 
@@ -542,6 +664,316 @@ static int cxl_memdev_release_file(struct inode *inode, struct file *file)
        return 0;
 }
 
+/**
+ * cxl_mem_get_fw_info - Get Firmware info
+ * @cxlds: The device data for the operation
+ *
+ * Retrieve firmware info for the device specified.
+ *
+ * Return: 0 if no error: or the result of the mailbox command.
+ *
+ * See CXL-3.0 8.2.9.3.1 Get FW Info
+ */
+static int cxl_mem_get_fw_info(struct cxl_memdev_state *mds)
+{
+       struct cxl_mbox_get_fw_info info;
+       struct cxl_mbox_cmd mbox_cmd;
+       int rc;
+
+       mbox_cmd = (struct cxl_mbox_cmd) {
+               .opcode = CXL_MBOX_OP_GET_FW_INFO,
+               .size_out = sizeof(info),
+               .payload_out = &info,
+       };
+
+       rc = cxl_internal_send_cmd(mds, &mbox_cmd);
+       if (rc < 0)
+               return rc;
+
+       mds->fw.num_slots = info.num_slots;
+       mds->fw.cur_slot = FIELD_GET(CXL_FW_INFO_SLOT_INFO_CUR_MASK,
+                                      info.slot_info);
+
+       return 0;
+}
+
+/**
+ * cxl_mem_activate_fw - Activate Firmware
+ * @mds: The device data for the operation
+ * @slot: slot number to activate
+ *
+ * Activate firmware in a given slot for the device specified.
+ *
+ * Return: 0 if no error: or the result of the mailbox command.
+ *
+ * See CXL-3.0 8.2.9.3.3 Activate FW
+ */
+static int cxl_mem_activate_fw(struct cxl_memdev_state *mds, int slot)
+{
+       struct cxl_mbox_activate_fw activate;
+       struct cxl_mbox_cmd mbox_cmd;
+
+       if (slot == 0 || slot > mds->fw.num_slots)
+               return -EINVAL;
+
+       mbox_cmd = (struct cxl_mbox_cmd) {
+               .opcode = CXL_MBOX_OP_ACTIVATE_FW,
+               .size_in = sizeof(activate),
+               .payload_in = &activate,
+       };
+
+       /* Only offline activation supported for now */
+       activate.action = CXL_FW_ACTIVATE_OFFLINE;
+       activate.slot = slot;
+
+       return cxl_internal_send_cmd(mds, &mbox_cmd);
+}
+
+/**
+ * cxl_mem_abort_fw_xfer - Abort an in-progress FW transfer
+ * @mds: The device data for the operation
+ *
+ * Abort an in-progress firmware transfer for the device specified.
+ *
+ * Return: 0 if no error: or the result of the mailbox command.
+ *
+ * See CXL-3.0 8.2.9.3.2 Transfer FW
+ */
+static int cxl_mem_abort_fw_xfer(struct cxl_memdev_state *mds)
+{
+       struct cxl_mbox_transfer_fw *transfer;
+       struct cxl_mbox_cmd mbox_cmd;
+       int rc;
+
+       transfer = kzalloc(struct_size(transfer, data, 0), GFP_KERNEL);
+       if (!transfer)
+               return -ENOMEM;
+
+       /* Set a 1s poll interval and a total wait time of 30s */
+       mbox_cmd = (struct cxl_mbox_cmd) {
+               .opcode = CXL_MBOX_OP_TRANSFER_FW,
+               .size_in = sizeof(*transfer),
+               .payload_in = transfer,
+               .poll_interval_ms = 1000,
+               .poll_count = 30,
+       };
+
+       transfer->action = CXL_FW_TRANSFER_ACTION_ABORT;
+
+       rc = cxl_internal_send_cmd(mds, &mbox_cmd);
+       kfree(transfer);
+       return rc;
+}
+
+static void cxl_fw_cleanup(struct fw_upload *fwl)
+{
+       struct cxl_memdev_state *mds = fwl->dd_handle;
+
+       mds->fw.next_slot = 0;
+}
+
+static int cxl_fw_do_cancel(struct fw_upload *fwl)
+{
+       struct cxl_memdev_state *mds = fwl->dd_handle;
+       struct cxl_dev_state *cxlds = &mds->cxlds;
+       struct cxl_memdev *cxlmd = cxlds->cxlmd;
+       int rc;
+
+       rc = cxl_mem_abort_fw_xfer(mds);
+       if (rc < 0)
+               dev_err(&cxlmd->dev, "Error aborting FW transfer: %d\n", rc);
+
+       return FW_UPLOAD_ERR_CANCELED;
+}
+
+static enum fw_upload_err cxl_fw_prepare(struct fw_upload *fwl, const u8 *data,
+                                        u32 size)
+{
+       struct cxl_memdev_state *mds = fwl->dd_handle;
+       struct cxl_mbox_transfer_fw *transfer;
+
+       if (!size)
+               return FW_UPLOAD_ERR_INVALID_SIZE;
+
+       mds->fw.oneshot = struct_size(transfer, data, size) <
+                           mds->payload_size;
+
+       if (cxl_mem_get_fw_info(mds))
+               return FW_UPLOAD_ERR_HW_ERROR;
+
+       /*
+        * So far no state has been changed, hence no other cleanup is
+        * necessary. Simply return the cancelled status.
+        */
+       if (test_and_clear_bit(CXL_FW_CANCEL, mds->fw.state))
+               return FW_UPLOAD_ERR_CANCELED;
+
+       return FW_UPLOAD_ERR_NONE;
+}
+
+static enum fw_upload_err cxl_fw_write(struct fw_upload *fwl, const u8 *data,
+                                      u32 offset, u32 size, u32 *written)
+{
+       struct cxl_memdev_state *mds = fwl->dd_handle;
+       struct cxl_dev_state *cxlds = &mds->cxlds;
+       struct cxl_memdev *cxlmd = cxlds->cxlmd;
+       struct cxl_mbox_transfer_fw *transfer;
+       struct cxl_mbox_cmd mbox_cmd;
+       u32 cur_size, remaining;
+       size_t size_in;
+       int rc;
+
+       *written = 0;
+
+       /* Offset has to be aligned to 128B (CXL-3.0 8.2.9.3.2 Table 8-57) */
+       if (!IS_ALIGNED(offset, CXL_FW_TRANSFER_ALIGNMENT)) {
+               dev_err(&cxlmd->dev,
+                       "misaligned offset for FW transfer slice (%u)\n",
+                       offset);
+               return FW_UPLOAD_ERR_RW_ERROR;
+       }
+
+       /*
+        * Pick transfer size based on mds->payload_size @size must bw 128-byte
+        * aligned, ->payload_size is a power of 2 starting at 256 bytes, and
+        * sizeof(*transfer) is 128.  These constraints imply that @cur_size
+        * will always be 128b aligned.
+        */
+       cur_size = min_t(size_t, size, mds->payload_size - sizeof(*transfer));
+
+       remaining = size - cur_size;
+       size_in = struct_size(transfer, data, cur_size);
+
+       if (test_and_clear_bit(CXL_FW_CANCEL, mds->fw.state))
+               return cxl_fw_do_cancel(fwl);
+
+       /*
+        * Slot numbers are 1-indexed
+        * cur_slot is the 0-indexed next_slot (i.e. 'cur_slot - 1 + 1')
+        * Check for rollover using modulo, and 1-index it by adding 1
+        */
+       mds->fw.next_slot = (mds->fw.cur_slot % mds->fw.num_slots) + 1;
+
+       /* Do the transfer via mailbox cmd */
+       transfer = kzalloc(size_in, GFP_KERNEL);
+       if (!transfer)
+               return FW_UPLOAD_ERR_RW_ERROR;
+
+       transfer->offset = cpu_to_le32(offset / CXL_FW_TRANSFER_ALIGNMENT);
+       memcpy(transfer->data, data + offset, cur_size);
+       if (mds->fw.oneshot) {
+               transfer->action = CXL_FW_TRANSFER_ACTION_FULL;
+               transfer->slot = mds->fw.next_slot;
+       } else {
+               if (offset == 0) {
+                       transfer->action = CXL_FW_TRANSFER_ACTION_INITIATE;
+               } else if (remaining == 0) {
+                       transfer->action = CXL_FW_TRANSFER_ACTION_END;
+                       transfer->slot = mds->fw.next_slot;
+               } else {
+                       transfer->action = CXL_FW_TRANSFER_ACTION_CONTINUE;
+               }
+       }
+
+       mbox_cmd = (struct cxl_mbox_cmd) {
+               .opcode = CXL_MBOX_OP_TRANSFER_FW,
+               .size_in = size_in,
+               .payload_in = transfer,
+               .poll_interval_ms = 1000,
+               .poll_count = 30,
+       };
+
+       rc = cxl_internal_send_cmd(mds, &mbox_cmd);
+       if (rc < 0) {
+               rc = FW_UPLOAD_ERR_RW_ERROR;
+               goto out_free;
+       }
+
+       *written = cur_size;
+
+       /* Activate FW if oneshot or if the last slice was written */
+       if (mds->fw.oneshot || remaining == 0) {
+               dev_dbg(&cxlmd->dev, "Activating firmware slot: %d\n",
+                       mds->fw.next_slot);
+               rc = cxl_mem_activate_fw(mds, mds->fw.next_slot);
+               if (rc < 0) {
+                       dev_err(&cxlmd->dev, "Error activating firmware: %d\n",
+                               rc);
+                       rc = FW_UPLOAD_ERR_HW_ERROR;
+                       goto out_free;
+               }
+       }
+
+       rc = FW_UPLOAD_ERR_NONE;
+
+out_free:
+       kfree(transfer);
+       return rc;
+}
+
+static enum fw_upload_err cxl_fw_poll_complete(struct fw_upload *fwl)
+{
+       struct cxl_memdev_state *mds = fwl->dd_handle;
+
+       /*
+        * cxl_internal_send_cmd() handles background operations synchronously.
+        * No need to wait for completions here - any errors would've been
+        * reported and handled during the ->write() call(s).
+        * Just check if a cancel request was received, and return success.
+        */
+       if (test_and_clear_bit(CXL_FW_CANCEL, mds->fw.state))
+               return cxl_fw_do_cancel(fwl);
+
+       return FW_UPLOAD_ERR_NONE;
+}
+
+static void cxl_fw_cancel(struct fw_upload *fwl)
+{
+       struct cxl_memdev_state *mds = fwl->dd_handle;
+
+       set_bit(CXL_FW_CANCEL, mds->fw.state);
+}
+
+static const struct fw_upload_ops cxl_memdev_fw_ops = {
+        .prepare = cxl_fw_prepare,
+        .write = cxl_fw_write,
+        .poll_complete = cxl_fw_poll_complete,
+        .cancel = cxl_fw_cancel,
+        .cleanup = cxl_fw_cleanup,
+};
+
+static void devm_cxl_remove_fw_upload(void *fwl)
+{
+       firmware_upload_unregister(fwl);
+}
+
+int cxl_memdev_setup_fw_upload(struct cxl_memdev_state *mds)
+{
+       struct cxl_dev_state *cxlds = &mds->cxlds;
+       struct device *dev = &cxlds->cxlmd->dev;
+       struct fw_upload *fwl;
+       int rc;
+
+       if (!test_bit(CXL_MEM_COMMAND_ID_GET_FW_INFO, mds->enabled_cmds))
+               return 0;
+
+       fwl = firmware_upload_register(THIS_MODULE, dev, dev_name(dev),
+                                      &cxl_memdev_fw_ops, mds);
+       if (IS_ERR(fwl))
+               return dev_err_probe(dev, PTR_ERR(fwl),
+                                    "Failed to register firmware loader\n");
+
+       rc = devm_add_action_or_reset(cxlds->dev, devm_cxl_remove_fw_upload,
+                                     fwl);
+       if (rc)
+               dev_err(dev,
+                       "Failed to add firmware loader remove action: %d\n",
+                       rc);
+
+       return rc;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_memdev_setup_fw_upload, CXL);
+
 static const struct file_operations cxl_memdev_fops = {
        .owner = THIS_MODULE,
        .unlocked_ioctl = cxl_memdev_ioctl,
@@ -551,6 +983,35 @@ static const struct file_operations cxl_memdev_fops = {
        .llseek = noop_llseek,
 };
 
+static void put_sanitize(void *data)
+{
+       struct cxl_memdev_state *mds = data;
+
+       sysfs_put(mds->security.sanitize_node);
+}
+
+static int cxl_memdev_security_init(struct cxl_memdev *cxlmd)
+{
+       struct cxl_dev_state *cxlds = cxlmd->cxlds;
+       struct cxl_memdev_state *mds = to_cxl_memdev_state(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;
+       }
+       mds->security.sanitize_node = sysfs_get_dirent(sec, "state");
+       sysfs_put(sec);
+       if (!mds->security.sanitize_node) {
+               dev_err(dev, "sysfs_get_dirent 'state' failed\n");
+               return -ENODEV;
+       }
+
+       return devm_add_action_or_reset(cxlds->dev, put_sanitize, mds);
+ }
+
 struct cxl_memdev *devm_cxl_add_memdev(struct cxl_dev_state *cxlds)
 {
        struct cxl_memdev *cxlmd;
@@ -579,6 +1040,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);
index 67f4ab6..74962b1 100644 (file)
@@ -308,36 +308,17 @@ static void disable_hdm(void *_cxlhdm)
               hdm + CXL_HDM_DECODER_CTRL_OFFSET);
 }
 
-int devm_cxl_enable_hdm(struct cxl_port *port, struct cxl_hdm *cxlhdm)
+static int devm_cxl_enable_hdm(struct device *host, struct cxl_hdm *cxlhdm)
 {
-       void __iomem *hdm;
+       void __iomem *hdm = cxlhdm->regs.hdm_decoder;
        u32 global_ctrl;
 
-       /*
-        * If the hdm capability was not mapped there is nothing to enable and
-        * the caller is responsible for what happens next.  For example,
-        * emulate a passthrough decoder.
-        */
-       if (IS_ERR(cxlhdm))
-               return 0;
-
-       hdm = cxlhdm->regs.hdm_decoder;
        global_ctrl = readl(hdm + CXL_HDM_DECODER_CTRL_OFFSET);
-
-       /*
-        * If the HDM decoder capability was enabled on entry, skip
-        * registering disable_hdm() since this decode capability may be
-        * owned by platform firmware.
-        */
-       if (global_ctrl & CXL_HDM_DECODER_ENABLE)
-               return 0;
-
        writel(global_ctrl | CXL_HDM_DECODER_ENABLE,
               hdm + CXL_HDM_DECODER_CTRL_OFFSET);
 
-       return devm_add_action_or_reset(&port->dev, disable_hdm, cxlhdm);
+       return devm_add_action_or_reset(host, disable_hdm, cxlhdm);
 }
-EXPORT_SYMBOL_NS_GPL(devm_cxl_enable_hdm, CXL);
 
 int cxl_dvsec_rr_decode(struct device *dev, int d,
                        struct cxl_endpoint_dvsec_info *info)
@@ -511,7 +492,7 @@ int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm,
        if (info->mem_enabled)
                return 0;
 
-       rc = devm_cxl_enable_hdm(port, cxlhdm);
+       rc = devm_cxl_enable_hdm(&port->dev, cxlhdm);
        if (rc)
                return rc;
 
index f8c38d9..fc94f52 100644 (file)
@@ -64,7 +64,7 @@ static int match_nvdimm_bridge(struct device *dev, void *data)
 
 struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_memdev *cxlmd)
 {
-       struct cxl_port *port = find_cxl_root(dev_get_drvdata(&cxlmd->dev));
+       struct cxl_port *port = find_cxl_root(cxlmd->endpoint);
        struct device *dev;
 
        if (!port)
index e7c284c..56be641 100644 (file)
@@ -117,9 +117,9 @@ static ssize_t target_type_show(struct device *dev,
        struct cxl_decoder *cxld = to_cxl_decoder(dev);
 
        switch (cxld->target_type) {
-       case CXL_DECODER_ACCELERATOR:
+       case CXL_DECODER_DEVMEM:
                return sysfs_emit(buf, "accelerator\n");
-       case CXL_DECODER_EXPANDER:
+       case CXL_DECODER_HOSTONLYMEM:
                return sysfs_emit(buf, "expander\n");
        }
        return -ENXIO;
@@ -1161,7 +1161,7 @@ static struct device *grandparent(struct device *dev)
 static void delete_endpoint(void *data)
 {
        struct cxl_memdev *cxlmd = data;
-       struct cxl_port *endpoint = dev_get_drvdata(&cxlmd->dev);
+       struct cxl_port *endpoint = cxlmd->endpoint;
        struct cxl_port *parent_port;
        struct device *parent;
 
@@ -1176,6 +1176,7 @@ static void delete_endpoint(void *data)
                devm_release_action(parent, cxl_unlink_uport, endpoint);
                devm_release_action(parent, unregister_port, endpoint);
        }
+       cxlmd->endpoint = NULL;
        device_unlock(parent);
        put_device(parent);
 out:
@@ -1187,7 +1188,7 @@ int cxl_endpoint_autoremove(struct cxl_memdev *cxlmd, struct cxl_port *endpoint)
        struct device *dev = &cxlmd->dev;
 
        get_device(&endpoint->dev);
-       dev_set_drvdata(dev, endpoint);
+       cxlmd->endpoint = endpoint;
        cxlmd->depth = endpoint->depth;
        return devm_add_action_or_reset(dev, delete_endpoint, cxlmd);
 }
@@ -1550,7 +1551,7 @@ static int cxl_decoder_init(struct cxl_port *port, struct cxl_decoder *cxld)
        /* Pre initialize an "empty" decoder */
        cxld->interleave_ways = 1;
        cxld->interleave_granularity = PAGE_SIZE;
-       cxld->target_type = CXL_DECODER_EXPANDER;
+       cxld->target_type = CXL_DECODER_HOSTONLYMEM;
        cxld->hpa_range = (struct range) {
                .start = 0,
                .end = -1,
index bfdd424..38db377 100644 (file)
@@ -853,6 +853,18 @@ static int cxl_rr_alloc_decoder(struct cxl_port *port, struct cxl_region *cxlr,
                return -EBUSY;
        }
 
+       /*
+        * Endpoints should already match the region type, but backstop that
+        * assumption with an assertion. Switch-decoders change mapping-type
+        * based on what is mapped when they are assigned to a region.
+        */
+       dev_WARN_ONCE(&cxlr->dev,
+                     port == cxled_to_port(cxled) &&
+                             cxld->target_type != cxlr->type,
+                     "%s:%s mismatch decoder type %d -> %d\n",
+                     dev_name(&cxled_to_memdev(cxled)->dev),
+                     dev_name(&cxld->dev), cxld->target_type, cxlr->type);
+       cxld->target_type = cxlr->type;
        cxl_rr->decoder = cxld;
        return 0;
 }
@@ -2146,7 +2158,7 @@ static struct cxl_region *__create_region(struct cxl_root_decoder *cxlrd,
                return ERR_PTR(-EBUSY);
        }
 
-       return devm_cxl_add_region(cxlrd, id, mode, CXL_DECODER_EXPANDER);
+       return devm_cxl_add_region(cxlrd, id, mode, CXL_DECODER_HOSTONLYMEM);
 }
 
 static ssize_t create_pmem_region_store(struct device *dev,
index 1476a02..52d1dbe 100644 (file)
@@ -200,10 +200,10 @@ void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
 }
 
 int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
-                          struct cxl_register_map *map, unsigned long map_mask)
+                          const struct cxl_register_map *map, unsigned long map_mask)
 {
        struct mapinfo {
-               struct cxl_reg_map *rmap;
+               const struct cxl_reg_map *rmap;
                void __iomem **addr;
        } mapinfo[] = {
                { &map->component_map.hdm_decoder, &regs->hdm_decoder },
@@ -233,11 +233,11 @@ EXPORT_SYMBOL_NS_GPL(cxl_map_component_regs, CXL);
 
 int cxl_map_device_regs(struct device *dev,
                        struct cxl_device_regs *regs,
-                       struct cxl_register_map *map)
+                       const struct cxl_register_map *map)
 {
        resource_size_t phys_addr = map->resource;
        struct mapinfo {
-               struct cxl_reg_map *rmap;
+               const struct cxl_reg_map *rmap;
                void __iomem **addr;
        } mapinfo[] = {
                { &map->device_map.status, &regs->status, },
index 492673d..d2bae6d 100644 (file)
@@ -56,7 +56,7 @@
 #define   CXL_HDM_DECODER0_CTRL_COMMIT BIT(9)
 #define   CXL_HDM_DECODER0_CTRL_COMMITTED BIT(10)
 #define   CXL_HDM_DECODER0_CTRL_COMMIT_ERROR BIT(11)
-#define   CXL_HDM_DECODER0_CTRL_TYPE BIT(12)
+#define   CXL_HDM_DECODER0_CTRL_HOSTONLY BIT(12)
 #define CXL_HDM_DECODER0_TL_LOW(i) (0x20 * (i) + 0x24)
 #define CXL_HDM_DECODER0_TL_HIGH(i) (0x20 * (i) + 0x28)
 #define CXL_HDM_DECODER0_SKIP_LOW(i) CXL_HDM_DECODER0_TL_LOW(i)
@@ -176,14 +176,22 @@ static inline int ways_to_eiw(unsigned int ways, u8 *eiw)
 /* CXL 2.0 8.2.8.4 Mailbox Registers */
 #define CXLDEV_MBOX_CAPS_OFFSET 0x00
 #define   CXLDEV_MBOX_CAP_PAYLOAD_SIZE_MASK GENMASK(4, 0)
+#define   CXLDEV_MBOX_CAP_BG_CMD_IRQ BIT(6)
+#define   CXLDEV_MBOX_CAP_IRQ_MSGNUM_MASK GENMASK(10, 7)
 #define CXLDEV_MBOX_CTRL_OFFSET 0x04
 #define   CXLDEV_MBOX_CTRL_DOORBELL BIT(0)
+#define   CXLDEV_MBOX_CTRL_BG_CMD_IRQ BIT(2)
 #define CXLDEV_MBOX_CMD_OFFSET 0x08
 #define   CXLDEV_MBOX_CMD_COMMAND_OPCODE_MASK GENMASK_ULL(15, 0)
 #define   CXLDEV_MBOX_CMD_PAYLOAD_LENGTH_MASK GENMASK_ULL(36, 16)
 #define CXLDEV_MBOX_STATUS_OFFSET 0x10
+#define   CXLDEV_MBOX_STATUS_BG_CMD BIT(0)
 #define   CXLDEV_MBOX_STATUS_RET_CODE_MASK GENMASK_ULL(47, 32)
 #define CXLDEV_MBOX_BG_CMD_STATUS_OFFSET 0x18
+#define   CXLDEV_MBOX_BG_CMD_COMMAND_OPCODE_MASK GENMASK_ULL(15, 0)
+#define   CXLDEV_MBOX_BG_CMD_COMMAND_PCT_MASK GENMASK_ULL(22, 16)
+#define   CXLDEV_MBOX_BG_CMD_COMMAND_RC_MASK GENMASK_ULL(47, 32)
+#define   CXLDEV_MBOX_BG_CMD_COMMAND_VENDOR_MASK GENMASK_ULL(63, 48)
 #define CXLDEV_MBOX_PAYLOAD_OFFSET 0x20
 
 /*
@@ -254,10 +262,10 @@ void cxl_probe_component_regs(struct device *dev, void __iomem *base,
 void cxl_probe_device_regs(struct device *dev, void __iomem *base,
                           struct cxl_device_reg_map *map);
 int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
-                          struct cxl_register_map *map,
+                          const struct cxl_register_map *map,
                           unsigned long map_mask);
 int cxl_map_device_regs(struct device *dev, struct cxl_device_regs *regs,
-                       struct cxl_register_map *map);
+                       const struct cxl_register_map *map);
 
 enum cxl_regloc_type;
 int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
@@ -290,8 +298,8 @@ resource_size_t cxl_rcrb_to_component(struct device *dev,
 #define CXL_DECODER_F_MASK  GENMASK(5, 0)
 
 enum cxl_decoder_type {
-       CXL_DECODER_ACCELERATOR = 2,
-       CXL_DECODER_EXPANDER = 3,
+       CXL_DECODER_DEVMEM = 2,
+       CXL_DECODER_HOSTONLYMEM = 3,
 };
 
 /*
@@ -712,7 +720,6 @@ struct cxl_endpoint_dvsec_info {
 struct cxl_hdm;
 struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port,
                                   struct cxl_endpoint_dvsec_info *info);
-int devm_cxl_enable_hdm(struct cxl_port *port, struct cxl_hdm *cxlhdm);
 int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
                                struct cxl_endpoint_dvsec_info *info);
 int devm_cxl_add_passthrough_decoder(struct cxl_port *port);
index a2845a7..25234a4 100644 (file)
@@ -5,6 +5,7 @@
 #include <uapi/linux/cxl_mem.h>
 #include <linux/cdev.h>
 #include <linux/uuid.h>
+#include <linux/rcuwait.h>
 #include "cxl.h"
 
 /* CXL 2.0 8.2.8.5.1.1 Memory Device Status Register */
@@ -38,6 +39,7 @@
  * @detach_work: active memdev lost a port in its ancestry
  * @cxl_nvb: coordinate removal of @cxl_nvd if present
  * @cxl_nvd: optional bridge to an nvdimm if the device supports pmem
+ * @endpoint: connection to the CXL port topology for this memory device
  * @id: id number of this memdev instance.
  * @depth: endpoint port depth
  */
@@ -48,6 +50,7 @@ struct cxl_memdev {
        struct work_struct detach_work;
        struct cxl_nvdimm_bridge *cxl_nvb;
        struct cxl_nvdimm *cxl_nvd;
+       struct cxl_port *endpoint;
        int id;
        int depth;
 };
@@ -82,6 +85,8 @@ static inline bool is_cxl_endpoint(struct cxl_port *port)
 }
 
 struct cxl_memdev *devm_cxl_add_memdev(struct cxl_dev_state *cxlds);
+struct cxl_memdev_state;
+int cxl_memdev_setup_fw_upload(struct cxl_memdev_state *mds);
 int devm_cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled,
                         resource_size_t base, resource_size_t len,
                         resource_size_t skipped);
@@ -108,6 +113,9 @@ static inline struct cxl_ep *cxl_ep_load(struct cxl_port *port,
  *            variable sized output commands, it tells the exact number of bytes
  *            written.
  * @min_out: (input) internal command output payload size validation
+ * @poll_count: (input) Number of timeouts to attempt.
+ * @poll_interval_ms: (input) Time between mailbox background command polling
+ *                    interval timeouts.
  * @return_code: (output) Error code returned from hardware.
  *
  * This is the primary mechanism used to send commands to the hardware.
@@ -123,6 +131,8 @@ struct cxl_mbox_cmd {
        size_t size_in;
        size_t size_out;
        size_t min_out;
+       int poll_count;
+       int poll_interval_ms;
        u16 return_code;
 };
 
@@ -195,7 +205,7 @@ static inline int cxl_mbox_cmd_rc2errno(struct cxl_mbox_cmd *mbox_cmd)
  */
 #define CXL_CAPACITY_MULTIPLIER SZ_256M
 
-/**
+/*
  * Event Interrupt Policy
  *
  * CXL rev 3.0 section 8.2.9.2.4; Table 8-52
@@ -215,8 +225,8 @@ struct cxl_event_interrupt_policy {
 /**
  * struct cxl_event_state - Event log driver state
  *
- * @event_buf: Buffer to receive event data
- * @event_log_lock: Serialize event_buf and log use
+ * @buf: Buffer to receive event data
+ * @log_lock: Serialize event_buf and log use
  */
 struct cxl_event_state {
        struct cxl_get_event_payload *buf;
@@ -254,6 +264,115 @@ struct cxl_poison_state {
        struct mutex lock;  /* Protect reads of poison list */
 };
 
+/*
+ * Get FW Info
+ * CXL rev 3.0 section 8.2.9.3.1; Table 8-56
+ */
+struct cxl_mbox_get_fw_info {
+       u8 num_slots;
+       u8 slot_info;
+       u8 activation_cap;
+       u8 reserved[13];
+       char slot_1_revision[16];
+       char slot_2_revision[16];
+       char slot_3_revision[16];
+       char slot_4_revision[16];
+} __packed;
+
+#define CXL_FW_INFO_SLOT_INFO_CUR_MASK                 GENMASK(2, 0)
+#define CXL_FW_INFO_SLOT_INFO_NEXT_MASK                        GENMASK(5, 3)
+#define CXL_FW_INFO_SLOT_INFO_NEXT_SHIFT               3
+#define CXL_FW_INFO_ACTIVATION_CAP_HAS_LIVE_ACTIVATE   BIT(0)
+
+/*
+ * Transfer FW Input Payload
+ * CXL rev 3.0 section 8.2.9.3.2; Table 8-57
+ */
+struct cxl_mbox_transfer_fw {
+       u8 action;
+       u8 slot;
+       u8 reserved[2];
+       __le32 offset;
+       u8 reserved2[0x78];
+       u8 data[];
+} __packed;
+
+#define CXL_FW_TRANSFER_ACTION_FULL    0x0
+#define CXL_FW_TRANSFER_ACTION_INITIATE        0x1
+#define CXL_FW_TRANSFER_ACTION_CONTINUE        0x2
+#define CXL_FW_TRANSFER_ACTION_END     0x3
+#define CXL_FW_TRANSFER_ACTION_ABORT   0x4
+
+/*
+ * CXL rev 3.0 section 8.2.9.3.2 mandates 128-byte alignment for FW packages
+ * and for each part transferred in a Transfer FW command.
+ */
+#define CXL_FW_TRANSFER_ALIGNMENT      128
+
+/*
+ * Activate FW Input Payload
+ * CXL rev 3.0 section 8.2.9.3.3; Table 8-58
+ */
+struct cxl_mbox_activate_fw {
+       u8 action;
+       u8 slot;
+} __packed;
+
+#define CXL_FW_ACTIVATE_ONLINE         0x0
+#define CXL_FW_ACTIVATE_OFFLINE                0x1
+
+/* FW state bits */
+#define CXL_FW_STATE_BITS              32
+#define CXL_FW_CANCEL          BIT(0)
+
+/**
+ * struct cxl_fw_state - Firmware upload / activation state
+ *
+ * @state: fw_uploader state bitmask
+ * @oneshot: whether the fw upload fits in a single transfer
+ * @num_slots: Number of FW slots available
+ * @cur_slot: Slot number currently active
+ * @next_slot: Slot number for the new firmware
+ */
+struct cxl_fw_state {
+       DECLARE_BITMAP(state, CXL_FW_STATE_BITS);
+       bool oneshot;
+       int num_slots;
+       int cur_slot;
+       int next_slot;
+};
+
+/**
+ * struct cxl_security_state - Device security state
+ *
+ * @state: state of last security operation
+ * @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;
+};
+
+/*
+ * enum cxl_devtype - delineate type-2 from a generic type-3 device
+ * @CXL_DEVTYPE_DEVMEM - Vendor specific CXL Type-2 device implementing HDM-D or
+ *                      HDM-DB, no requirement that this device implements a
+ *                      mailbox, or other memory-device-standard manageability
+ *                      flows.
+ * @CXL_DEVTYPE_CLASSMEM - Common class definition of a CXL Type-3 device with
+ *                        HDM-H and class-mandatory memory device registers
+ */
+enum cxl_devtype {
+       CXL_DEVTYPE_DEVMEM,
+       CXL_DEVTYPE_CLASSMEM,
+};
+
 /**
  * struct cxl_dev_state - The driver device state
  *
@@ -267,6 +386,36 @@ struct cxl_poison_state {
  * @cxl_dvsec: Offset to the PCIe device DVSEC
  * @rcd: operating in RCD mode (CXL 3.0 9.11.8 CXL Devices Attached to an RCH)
  * @media_ready: Indicate whether the device media is usable
+ * @dpa_res: Overall DPA resource tree for the device
+ * @pmem_res: Active Persistent memory capacity configuration
+ * @ram_res: Active Volatile memory capacity configuration
+ * @component_reg_phys: register base of component registers
+ * @serial: PCIe Device Serial Number
+ * @type: Generic Memory Class device or Vendor Specific Memory device
+ */
+struct cxl_dev_state {
+       struct device *dev;
+       struct cxl_memdev *cxlmd;
+       struct cxl_regs regs;
+       int cxl_dvsec;
+       bool rcd;
+       bool media_ready;
+       struct resource dpa_res;
+       struct resource pmem_res;
+       struct resource ram_res;
+       resource_size_t component_reg_phys;
+       u64 serial;
+       enum cxl_devtype type;
+};
+
+/**
+ * struct cxl_memdev_state - Generic Type-3 Memory Device Class driver data
+ *
+ * CXL 8.1.12.1 PCI Header - Class Code Register Memory Device defines
+ * common memory device functionality like the presence of a mailbox and
+ * the functionality related to that like Identify Memory Device and Get
+ * Partition Info
+ * @cxlds: Core driver state common across Type-2 and Type-3 devices
  * @payload_size: Size of space for payload
  *                (CXL 2.0 8.2.8.4.3 Mailbox Capabilities Register)
  * @lsa_size: Size of Label Storage Area
@@ -275,9 +424,6 @@ struct cxl_poison_state {
  * @firmware_version: Firmware version for the memory device.
  * @enabled_cmds: Hardware commands found enabled in CEL.
  * @exclusive_cmds: Commands that are kernel-internal only
- * @dpa_res: Overall DPA resource tree for the device
- * @pmem_res: Active Persistent memory capacity configuration
- * @ram_res: Active Volatile memory capacity configuration
  * @total_bytes: sum of all possible capacities
  * @volatile_only_bytes: hard volatile capacity
  * @persistent_only_bytes: hard persistent capacity
@@ -286,54 +432,48 @@ struct cxl_poison_state {
  * @active_persistent_bytes: sum of hard + soft persistent
  * @next_volatile_bytes: volatile capacity change pending device reset
  * @next_persistent_bytes: persistent capacity change pending device reset
- * @component_reg_phys: register base of component registers
- * @info: Cached DVSEC information about the device.
- * @serial: PCIe Device Serial Number
  * @event: event log driver state
  * @poison: poison driver state info
+ * @fw: firmware upload / activation state
  * @mbox_send: @dev specific transport for transmitting mailbox commands
  *
- * See section 8.2.9.5.2 Capacity Configuration and Label Storage for
+ * See CXL 3.0 8.2.9.8.2 Capacity Configuration and Label Storage for
  * details on capacity parameters.
  */
-struct cxl_dev_state {
-       struct device *dev;
-       struct cxl_memdev *cxlmd;
-
-       struct cxl_regs regs;
-       int cxl_dvsec;
-
-       bool rcd;
-       bool media_ready;
+struct cxl_memdev_state {
+       struct cxl_dev_state cxlds;
        size_t payload_size;
        size_t lsa_size;
        struct mutex mbox_mutex; /* Protects device mailbox and firmware */
        char firmware_version[0x10];
        DECLARE_BITMAP(enabled_cmds, CXL_MEM_COMMAND_ID_MAX);
        DECLARE_BITMAP(exclusive_cmds, CXL_MEM_COMMAND_ID_MAX);
-
-       struct resource dpa_res;
-       struct resource pmem_res;
-       struct resource ram_res;
        u64 total_bytes;
        u64 volatile_only_bytes;
        u64 persistent_only_bytes;
        u64 partition_align_bytes;
-
        u64 active_volatile_bytes;
        u64 active_persistent_bytes;
        u64 next_volatile_bytes;
        u64 next_persistent_bytes;
-
-       resource_size_t component_reg_phys;
-       u64 serial;
-
        struct cxl_event_state event;
        struct cxl_poison_state poison;
+       struct cxl_security_state security;
+       struct cxl_fw_state fw;
 
-       int (*mbox_send)(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd);
+       struct rcuwait mbox_wait;
+       int (*mbox_send)(struct cxl_memdev_state *mds,
+                        struct cxl_mbox_cmd *cmd);
 };
 
+static inline struct cxl_memdev_state *
+to_cxl_memdev_state(struct cxl_dev_state *cxlds)
+{
+       if (cxlds->type != CXL_DEVTYPE_CLASSMEM)
+               return NULL;
+       return container_of(cxlds, struct cxl_memdev_state, cxlds);
+}
+
 enum cxl_opcode {
        CXL_MBOX_OP_INVALID             = 0x0000,
        CXL_MBOX_OP_RAW                 = CXL_MBOX_OP_INVALID,
@@ -342,6 +482,7 @@ enum cxl_opcode {
        CXL_MBOX_OP_GET_EVT_INT_POLICY  = 0x0102,
        CXL_MBOX_OP_SET_EVT_INT_POLICY  = 0x0103,
        CXL_MBOX_OP_GET_FW_INFO         = 0x0200,
+       CXL_MBOX_OP_TRANSFER_FW         = 0x0201,
        CXL_MBOX_OP_ACTIVATE_FW         = 0x0202,
        CXL_MBOX_OP_SET_TIMESTAMP       = 0x0301,
        CXL_MBOX_OP_GET_SUPPORTED_LOGS  = 0x0400,
@@ -362,6 +503,8 @@ enum cxl_opcode {
        CXL_MBOX_OP_GET_SCAN_MEDIA_CAPS = 0x4303,
        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,
@@ -692,18 +835,20 @@ enum {
        CXL_PMEM_SEC_PASS_USER,
 };
 
-int cxl_internal_send_cmd(struct cxl_dev_state *cxlds,
+int cxl_internal_send_cmd(struct cxl_memdev_state *mds,
                          struct cxl_mbox_cmd *cmd);
-int cxl_dev_state_identify(struct cxl_dev_state *cxlds);
+int cxl_dev_state_identify(struct cxl_memdev_state *mds);
 int cxl_await_media_ready(struct cxl_dev_state *cxlds);
-int cxl_enumerate_cmds(struct cxl_dev_state *cxlds);
-int cxl_mem_create_range_info(struct cxl_dev_state *cxlds);
-struct cxl_dev_state *cxl_dev_state_create(struct device *dev);
-void set_exclusive_cxl_commands(struct cxl_dev_state *cxlds, unsigned long *cmds);
-void clear_exclusive_cxl_commands(struct cxl_dev_state *cxlds, unsigned long *cmds);
-void cxl_mem_get_event_records(struct cxl_dev_state *cxlds, u32 status);
-int cxl_set_timestamp(struct cxl_dev_state *cxlds);
-int cxl_poison_state_init(struct cxl_dev_state *cxlds);
+int cxl_enumerate_cmds(struct cxl_memdev_state *mds);
+int cxl_mem_create_range_info(struct cxl_memdev_state *mds);
+struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev);
+void set_exclusive_cxl_commands(struct cxl_memdev_state *mds,
+                               unsigned long *cmds);
+void clear_exclusive_cxl_commands(struct cxl_memdev_state *mds,
+                                 unsigned long *cmds);
+void cxl_mem_get_event_records(struct cxl_memdev_state *mds, u32 status);
+int cxl_set_timestamp(struct cxl_memdev_state *mds);
+int cxl_poison_state_init(struct cxl_memdev_state *mds);
 int cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len,
                       struct cxl_region *cxlr);
 int cxl_trigger_poison_list(struct cxl_memdev *cxlmd);
@@ -722,6 +867,8 @@ static inline void cxl_mem_active_dec(void)
 }
 #endif
 
+int cxl_mem_sanitize(struct cxl_memdev_state *mds, u16 cmd);
+
 struct cxl_hdm {
        struct cxl_component_regs regs;
        unsigned int decoder_count;
index 519edd0..584f9ee 100644 (file)
@@ -117,6 +117,7 @@ DEFINE_DEBUGFS_ATTRIBUTE(cxl_poison_clear_fops, NULL,
 static int cxl_mem_probe(struct device *dev)
 {
        struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
+       struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
        struct cxl_dev_state *cxlds = cxlmd->cxlds;
        struct device *endpoint_parent;
        struct cxl_port *parent_port;
@@ -141,10 +142,10 @@ static int cxl_mem_probe(struct device *dev)
        dentry = cxl_debugfs_create_dir(dev_name(dev));
        debugfs_create_devm_seqfile(dev, "dpamem", dentry, cxl_mem_dpa_show);
 
-       if (test_bit(CXL_POISON_ENABLED_INJECT, cxlds->poison.enabled_cmds))
+       if (test_bit(CXL_POISON_ENABLED_INJECT, mds->poison.enabled_cmds))
                debugfs_create_file("inject_poison", 0200, dentry, cxlmd,
                                    &cxl_poison_inject_fops);
-       if (test_bit(CXL_POISON_ENABLED_CLEAR, cxlds->poison.enabled_cmds))
+       if (test_bit(CXL_POISON_ENABLED_CLEAR, mds->poison.enabled_cmds))
                debugfs_create_file("clear_poison", 0200, dentry, cxlmd,
                                    &cxl_poison_clear_fops);
 
@@ -227,9 +228,12 @@ static umode_t cxl_mem_visible(struct kobject *kobj, struct attribute *a, int n)
 {
        if (a == &dev_attr_trigger_poison_list.attr) {
                struct device *dev = kobj_to_dev(kobj);
+               struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
+               struct cxl_memdev_state *mds =
+                       to_cxl_memdev_state(cxlmd->cxlds);
 
                if (!test_bit(CXL_POISON_ENABLED_LIST,
-                             to_cxl_memdev(dev)->cxlds->poison.enabled_cmds))
+                             mds->poison.enabled_cmds))
                        return 0;
        }
        return a->mode;
index 0872f22..18cfb7a 100644 (file)
@@ -84,9 +84,92 @@ static int cxl_pci_mbox_wait_for_doorbell(struct cxl_dev_state *cxlds)
                            status & CXLMDEV_DEV_FATAL ? " fatal" : "",        \
                            status & CXLMDEV_FW_HALT ? " firmware-halt" : "")
 
+struct cxl_dev_id {
+       struct cxl_dev_state *cxlds;
+};
+
+static int cxl_request_irq(struct cxl_dev_state *cxlds, int irq,
+                          irq_handler_t handler, irq_handler_t thread_fn)
+{
+       struct device *dev = cxlds->dev;
+       struct cxl_dev_id *dev_id;
+
+       /* dev_id must be globally unique and must contain the cxlds */
+       dev_id = devm_kzalloc(dev, sizeof(*dev_id), GFP_KERNEL);
+       if (!dev_id)
+               return -ENOMEM;
+       dev_id->cxlds = cxlds;
+
+       return devm_request_threaded_irq(dev, irq, handler, thread_fn,
+                                        IRQF_SHARED | IRQF_ONESHOT,
+                                        NULL, dev_id);
+}
+
+static bool cxl_mbox_background_complete(struct cxl_dev_state *cxlds)
+{
+       u64 reg;
+
+       reg = readq(cxlds->regs.mbox + CXLDEV_MBOX_BG_CMD_STATUS_OFFSET);
+       return FIELD_GET(CXLDEV_MBOX_BG_CMD_COMMAND_PCT_MASK, reg) == 100;
+}
+
+static irqreturn_t cxl_pci_mbox_irq(int irq, void *id)
+{
+       u64 reg;
+       u16 opcode;
+       struct cxl_dev_id *dev_id = id;
+       struct cxl_dev_state *cxlds = dev_id->cxlds;
+       struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds);
+
+       if (!cxl_mbox_background_complete(cxlds))
+               return IRQ_NONE;
+
+       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 (mds->security.sanitize_node)
+                       sysfs_notify_dirent(mds->security.sanitize_node);
+
+               dev_dbg(cxlds->dev, "Sanitization operation ended\n");
+       } else {
+               /* short-circuit the wait in __cxl_pci_mbox_send_cmd() */
+               rcuwait_wake_up(&mds->mbox_wait);
+       }
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * Sanitization operation polling mode.
+ */
+static void cxl_mbox_sanitize_work(struct work_struct *work)
+{
+       struct cxl_memdev_state *mds =
+               container_of(work, typeof(*mds), security.poll_dwork.work);
+       struct cxl_dev_state *cxlds = &mds->cxlds;
+
+       mutex_lock(&mds->mbox_mutex);
+       if (cxl_mbox_background_complete(cxlds)) {
+               mds->security.poll_tmo_secs = 0;
+               put_device(cxlds->dev);
+
+               if (mds->security.sanitize_node)
+                       sysfs_notify_dirent(mds->security.sanitize_node);
+
+               dev_dbg(cxlds->dev, "Sanitization operation ended\n");
+       } else {
+               int timeout = mds->security.poll_tmo_secs + 10;
+
+               mds->security.poll_tmo_secs = min(15 * 60, timeout);
+               queue_delayed_work(system_wq, &mds->security.poll_dwork,
+                                  timeout * HZ);
+       }
+       mutex_unlock(&mds->mbox_mutex);
+}
+
 /**
  * __cxl_pci_mbox_send_cmd() - Execute a mailbox command
- * @cxlds: The device state to communicate with.
+ * @mds: The memory device driver data
  * @mbox_cmd: Command to send to the memory device.
  *
  * Context: Any context. Expects mbox_mutex to be held.
@@ -106,16 +189,17 @@ static int cxl_pci_mbox_wait_for_doorbell(struct cxl_dev_state *cxlds)
  * not need to coordinate with each other. The driver only uses the primary
  * mailbox.
  */
-static int __cxl_pci_mbox_send_cmd(struct cxl_dev_state *cxlds,
+static int __cxl_pci_mbox_send_cmd(struct cxl_memdev_state *mds,
                                   struct cxl_mbox_cmd *mbox_cmd)
 {
+       struct cxl_dev_state *cxlds = &mds->cxlds;
        void __iomem *payload = cxlds->regs.mbox + CXLDEV_MBOX_PAYLOAD_OFFSET;
        struct device *dev = cxlds->dev;
        u64 cmd_reg, status_reg;
        size_t out_len;
        int rc;
 
-       lockdep_assert_held(&cxlds->mbox_mutex);
+       lockdep_assert_held(&mds->mbox_mutex);
 
        /*
         * Here are the steps from 8.2.8.4 of the CXL 2.0 spec.
@@ -144,6 +228,16 @@ static int __cxl_pci_mbox_send_cmd(struct cxl_dev_state *cxlds,
                return -EBUSY;
        }
 
+       /*
+        * With sanitize polling, hardware might be done and the poller still
+        * not be in sync. Ensure no new command comes in until so. Keep the
+        * hardware semantics and only allow device health status.
+        */
+       if (mds->security.poll_tmo_secs > 0) {
+               if (mbox_cmd->opcode != CXL_MBOX_OP_GET_HEALTH_INFO)
+                       return -EBUSY;
+       }
+
        cmd_reg = FIELD_PREP(CXLDEV_MBOX_CMD_COMMAND_OPCODE_MASK,
                             mbox_cmd->opcode);
        if (mbox_cmd->size_in) {
@@ -177,12 +271,80 @@ static int __cxl_pci_mbox_send_cmd(struct cxl_dev_state *cxlds,
        mbox_cmd->return_code =
                FIELD_GET(CXLDEV_MBOX_STATUS_RET_CODE_MASK, status_reg);
 
+       /*
+        * Handle the background command in a synchronous manner.
+        *
+        * All other mailbox commands will serialize/queue on the mbox_mutex,
+        * which we currently hold. Furthermore this also guarantees that
+        * cxl_mbox_background_complete() checks are safe amongst each other,
+        * in that no new bg operation can occur in between.
+        *
+        * Background operations are timesliced in accordance with the nature
+        * of the command. In the event of timeout, the mailbox state is
+        * indeterminate until the next successful command submission and the
+        * driver can get back in sync with the hardware state.
+        */
+       if (mbox_cmd->return_code == CXL_MBOX_CMD_RC_BACKGROUND) {
+               u64 bg_status_reg;
+               int i, timeout;
+
+               /*
+                * Sanitization is a special case which monopolizes the device
+                * and cannot be timesliced. Handle asynchronously instead,
+                * and allow userspace to poll(2) for completion.
+                */
+               if (mbox_cmd->opcode == CXL_MBOX_OP_SANITIZE) {
+                       if (mds->security.poll_tmo_secs != -1) {
+                               /* hold the device throughout */
+                               get_device(cxlds->dev);
+
+                               /* give first timeout a second */
+                               timeout = 1;
+                               mds->security.poll_tmo_secs = timeout;
+                               queue_delayed_work(system_wq,
+                                                  &mds->security.poll_dwork,
+                                                  timeout * HZ);
+                       }
+
+                       dev_dbg(dev, "Sanitization operation started\n");
+                       goto success;
+               }
+
+               dev_dbg(dev, "Mailbox background operation (0x%04x) started\n",
+                       mbox_cmd->opcode);
+
+               timeout = mbox_cmd->poll_interval_ms;
+               for (i = 0; i < mbox_cmd->poll_count; i++) {
+                       if (rcuwait_wait_event_timeout(&mds->mbox_wait,
+                                      cxl_mbox_background_complete(cxlds),
+                                      TASK_UNINTERRUPTIBLE,
+                                      msecs_to_jiffies(timeout)) > 0)
+                               break;
+               }
+
+               if (!cxl_mbox_background_complete(cxlds)) {
+                       dev_err(dev, "timeout waiting for background (%d ms)\n",
+                               timeout * mbox_cmd->poll_count);
+                       return -ETIMEDOUT;
+               }
+
+               bg_status_reg = readq(cxlds->regs.mbox +
+                                     CXLDEV_MBOX_BG_CMD_STATUS_OFFSET);
+               mbox_cmd->return_code =
+                       FIELD_GET(CXLDEV_MBOX_BG_CMD_COMMAND_RC_MASK,
+                                 bg_status_reg);
+               dev_dbg(dev,
+                       "Mailbox background operation (0x%04x) completed\n",
+                       mbox_cmd->opcode);
+       }
+
        if (mbox_cmd->return_code != CXL_MBOX_CMD_RC_SUCCESS) {
                dev_dbg(dev, "Mailbox operation had an error: %s\n",
                        cxl_mbox_cmd_rc2str(mbox_cmd));
                return 0; /* completed but caller must check return_code */
        }
 
+success:
        /* #7 */
        cmd_reg = readq(cxlds->regs.mbox + CXLDEV_MBOX_CMD_OFFSET);
        out_len = FIELD_GET(CXLDEV_MBOX_CMD_PAYLOAD_LENGTH_MASK, cmd_reg);
@@ -196,8 +358,9 @@ static int __cxl_pci_mbox_send_cmd(struct cxl_dev_state *cxlds,
                 * have requested less data than the hardware supplied even
                 * within spec.
                 */
-               size_t n = min3(mbox_cmd->size_out, cxlds->payload_size, out_len);
+               size_t n;
 
+               n = min3(mbox_cmd->size_out, mds->payload_size, out_len);
                memcpy_fromio(mbox_cmd->payload_out, payload, n);
                mbox_cmd->size_out = n;
        } else {
@@ -207,20 +370,23 @@ static int __cxl_pci_mbox_send_cmd(struct cxl_dev_state *cxlds,
        return 0;
 }
 
-static int cxl_pci_mbox_send(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
+static int cxl_pci_mbox_send(struct cxl_memdev_state *mds,
+                            struct cxl_mbox_cmd *cmd)
 {
        int rc;
 
-       mutex_lock_io(&cxlds->mbox_mutex);
-       rc = __cxl_pci_mbox_send_cmd(cxlds, cmd);
-       mutex_unlock(&cxlds->mbox_mutex);
+       mutex_lock_io(&mds->mbox_mutex);
+       rc = __cxl_pci_mbox_send_cmd(mds, cmd);
+       mutex_unlock(&mds->mbox_mutex);
 
        return rc;
 }
 
-static int cxl_pci_setup_mailbox(struct cxl_dev_state *cxlds)
+static int cxl_pci_setup_mailbox(struct cxl_memdev_state *mds)
 {
+       struct cxl_dev_state *cxlds = &mds->cxlds;
        const int cap = readl(cxlds->regs.mbox + CXLDEV_MBOX_CAPS_OFFSET);
+       struct device *dev = cxlds->dev;
        unsigned long timeout;
        u64 md_status;
 
@@ -234,8 +400,7 @@ static int cxl_pci_setup_mailbox(struct cxl_dev_state *cxlds)
        } while (!time_after(jiffies, timeout));
 
        if (!(md_status & CXLMDEV_MBOX_IF_READY)) {
-               cxl_err(cxlds->dev, md_status,
-                       "timeout awaiting mailbox ready");
+               cxl_err(dev, md_status, "timeout awaiting mailbox ready");
                return -ETIMEDOUT;
        }
 
@@ -246,12 +411,12 @@ static int cxl_pci_setup_mailbox(struct cxl_dev_state *cxlds)
         * source for future doorbell busy events.
         */
        if (cxl_pci_mbox_wait_for_doorbell(cxlds) != 0) {
-               cxl_err(cxlds->dev, md_status, "timeout awaiting mailbox idle");
+               cxl_err(dev, md_status, "timeout awaiting mailbox idle");
                return -ETIMEDOUT;
        }
 
-       cxlds->mbox_send = cxl_pci_mbox_send;
-       cxlds->payload_size =
+       mds->mbox_send = cxl_pci_mbox_send;
+       mds->payload_size =
                1 << FIELD_GET(CXLDEV_MBOX_CAP_PAYLOAD_SIZE_MASK, cap);
 
        /*
@@ -261,16 +426,43 @@ static int cxl_pci_setup_mailbox(struct cxl_dev_state *cxlds)
         * there's no point in going forward. If the size is too large, there's
         * no harm is soft limiting it.
         */
-       cxlds->payload_size = min_t(size_t, cxlds->payload_size, SZ_1M);
-       if (cxlds->payload_size < 256) {
-               dev_err(cxlds->dev, "Mailbox is too small (%zub)",
-                       cxlds->payload_size);
+       mds->payload_size = min_t(size_t, mds->payload_size, SZ_1M);
+       if (mds->payload_size < 256) {
+               dev_err(dev, "Mailbox is too small (%zub)",
+                       mds->payload_size);
                return -ENXIO;
        }
 
-       dev_dbg(cxlds->dev, "Mailbox payload sized %zu",
-               cxlds->payload_size);
+       dev_dbg(dev, "Mailbox payload sized %zu", mds->payload_size);
+
+       rcuwait_init(&mds->mbox_wait);
+
+       if (cap & CXLDEV_MBOX_CAP_BG_CMD_IRQ) {
+               u32 ctrl;
+               int irq, msgnum;
+               struct pci_dev *pdev = to_pci_dev(cxlds->dev);
+
+               msgnum = FIELD_GET(CXLDEV_MBOX_CAP_IRQ_MSGNUM_MASK, cap);
+               irq = pci_irq_vector(pdev, msgnum);
+               if (irq < 0)
+                       goto mbox_poll;
+
+               if (cxl_request_irq(cxlds, irq, cxl_pci_mbox_irq, NULL))
+                       goto mbox_poll;
+
+               /* enable background command mbox irq support */
+               ctrl = readl(cxlds->regs.mbox + CXLDEV_MBOX_CTRL_OFFSET);
+               ctrl |= CXLDEV_MBOX_CTRL_BG_CMD_IRQ;
+               writel(ctrl, cxlds->regs.mbox + CXLDEV_MBOX_CTRL_OFFSET);
+
+               return 0;
+       }
+
+mbox_poll:
+       mds->security.poll = true;
+       INIT_DELAYED_WORK(&mds->security.poll_dwork, cxl_mbox_sanitize_work);
 
+       dev_dbg(cxlds->dev, "Mailbox interrupts are unsupported");
        return 0;
 }
 
@@ -365,19 +557,6 @@ static bool is_cxl_restricted(struct pci_dev *pdev)
        return pci_pcie_type(pdev) == PCI_EXP_TYPE_RC_END;
 }
 
-/*
- * CXL v3.0 6.2.3 Table 6-4
- * The table indicates that if PCIe Flit Mode is set, then CXL is in 256B flits
- * mode, otherwise it's 68B flits mode.
- */
-static bool cxl_pci_flit_256(struct pci_dev *pdev)
-{
-       u16 lnksta2;
-
-       pcie_capability_read_word(pdev, PCI_EXP_LNKSTA2, &lnksta2);
-       return lnksta2 & PCI_EXP_LNKSTA2_FLIT;
-}
-
 static int cxl_pci_ras_unmask(struct pci_dev *pdev)
 {
        struct pci_host_bridge *host_bridge = pci_find_host_bridge(pdev->bus);
@@ -404,9 +583,8 @@ static int cxl_pci_ras_unmask(struct pci_dev *pdev)
                addr = cxlds->regs.ras + CXL_RAS_UNCORRECTABLE_MASK_OFFSET;
                orig_val = readl(addr);
 
-               mask = CXL_RAS_UNCORRECTABLE_MASK_MASK;
-               if (!cxl_pci_flit_256(pdev))
-                       mask &= ~CXL_RAS_UNCORRECTABLE_MASK_F256B_MASK;
+               mask = CXL_RAS_UNCORRECTABLE_MASK_MASK |
+                      CXL_RAS_UNCORRECTABLE_MASK_F256B_MASK;
                val = orig_val & ~mask;
                writel(val, addr);
                dev_dbg(&pdev->dev,
@@ -433,18 +611,18 @@ static void free_event_buf(void *buf)
 
 /*
  * There is a single buffer for reading event logs from the mailbox.  All logs
- * share this buffer protected by the cxlds->event_log_lock.
+ * share this buffer protected by the mds->event_log_lock.
  */
-static int cxl_mem_alloc_event_buf(struct cxl_dev_state *cxlds)
+static int cxl_mem_alloc_event_buf(struct cxl_memdev_state *mds)
 {
        struct cxl_get_event_payload *buf;
 
-       buf = kvmalloc(cxlds->payload_size, GFP_KERNEL);
+       buf = kvmalloc(mds->payload_size, GFP_KERNEL);
        if (!buf)
                return -ENOMEM;
-       cxlds->event.buf = buf;
+       mds->event.buf = buf;
 
-       return devm_add_action_or_reset(cxlds->dev, free_event_buf, buf);
+       return devm_add_action_or_reset(mds->cxlds.dev, free_event_buf, buf);
 }
 
 static int cxl_alloc_irq_vectors(struct pci_dev *pdev)
@@ -469,14 +647,11 @@ static int cxl_alloc_irq_vectors(struct pci_dev *pdev)
        return 0;
 }
 
-struct cxl_dev_id {
-       struct cxl_dev_state *cxlds;
-};
-
 static irqreturn_t cxl_event_thread(int irq, void *id)
 {
        struct cxl_dev_id *dev_id = id;
        struct cxl_dev_state *cxlds = dev_id->cxlds;
+       struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds);
        u32 status;
 
        do {
@@ -489,7 +664,7 @@ static irqreturn_t cxl_event_thread(int irq, void *id)
                status &= CXLDEV_EVENT_STATUS_ALL;
                if (!status)
                        break;
-               cxl_mem_get_event_records(cxlds, status);
+               cxl_mem_get_event_records(mds, status);
                cond_resched();
        } while (status);
 
@@ -498,31 +673,21 @@ static irqreturn_t cxl_event_thread(int irq, void *id)
 
 static int cxl_event_req_irq(struct cxl_dev_state *cxlds, u8 setting)
 {
-       struct device *dev = cxlds->dev;
-       struct pci_dev *pdev = to_pci_dev(dev);
-       struct cxl_dev_id *dev_id;
+       struct pci_dev *pdev = to_pci_dev(cxlds->dev);
        int irq;
 
        if (FIELD_GET(CXLDEV_EVENT_INT_MODE_MASK, setting) != CXL_INT_MSI_MSIX)
                return -ENXIO;
 
-       /* dev_id must be globally unique and must contain the cxlds */
-       dev_id = devm_kzalloc(dev, sizeof(*dev_id), GFP_KERNEL);
-       if (!dev_id)
-               return -ENOMEM;
-       dev_id->cxlds = cxlds;
-
        irq =  pci_irq_vector(pdev,
                              FIELD_GET(CXLDEV_EVENT_INT_MSGNUM_MASK, setting));
        if (irq < 0)
                return irq;
 
-       return devm_request_threaded_irq(dev, irq, NULL, cxl_event_thread,
-                                        IRQF_SHARED | IRQF_ONESHOT, NULL,
-                                        dev_id);
+       return cxl_request_irq(cxlds, irq, NULL, cxl_event_thread);
 }
 
-static int cxl_event_get_int_policy(struct cxl_dev_state *cxlds,
+static int cxl_event_get_int_policy(struct cxl_memdev_state *mds,
                                    struct cxl_event_interrupt_policy *policy)
 {
        struct cxl_mbox_cmd mbox_cmd = {
@@ -532,15 +697,15 @@ static int cxl_event_get_int_policy(struct cxl_dev_state *cxlds,
        };
        int rc;
 
-       rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
+       rc = cxl_internal_send_cmd(mds, &mbox_cmd);
        if (rc < 0)
-               dev_err(cxlds->dev, "Failed to get event interrupt policy : %d",
-                       rc);
+               dev_err(mds->cxlds.dev,
+                       "Failed to get event interrupt policy : %d", rc);
 
        return rc;
 }
 
-static int cxl_event_config_msgnums(struct cxl_dev_state *cxlds,
+static int cxl_event_config_msgnums(struct cxl_memdev_state *mds,
                                    struct cxl_event_interrupt_policy *policy)
 {
        struct cxl_mbox_cmd mbox_cmd;
@@ -559,23 +724,24 @@ static int cxl_event_config_msgnums(struct cxl_dev_state *cxlds,
                .size_in = sizeof(*policy),
        };
 
-       rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
+       rc = cxl_internal_send_cmd(mds, &mbox_cmd);
        if (rc < 0) {
-               dev_err(cxlds->dev, "Failed to set event interrupt policy : %d",
+               dev_err(mds->cxlds.dev, "Failed to set event interrupt policy : %d",
                        rc);
                return rc;
        }
 
        /* Retrieve final interrupt settings */
-       return cxl_event_get_int_policy(cxlds, policy);
+       return cxl_event_get_int_policy(mds, policy);
 }
 
-static int cxl_event_irqsetup(struct cxl_dev_state *cxlds)
+static int cxl_event_irqsetup(struct cxl_memdev_state *mds)
 {
+       struct cxl_dev_state *cxlds = &mds->cxlds;
        struct cxl_event_interrupt_policy policy;
        int rc;
 
-       rc = cxl_event_config_msgnums(cxlds, &policy);
+       rc = cxl_event_config_msgnums(mds, &policy);
        if (rc)
                return rc;
 
@@ -614,7 +780,7 @@ static bool cxl_event_int_is_fw(u8 setting)
 }
 
 static int cxl_event_config(struct pci_host_bridge *host_bridge,
-                           struct cxl_dev_state *cxlds)
+                           struct cxl_memdev_state *mds)
 {
        struct cxl_event_interrupt_policy policy;
        int rc;
@@ -626,11 +792,11 @@ static int cxl_event_config(struct pci_host_bridge *host_bridge,
        if (!host_bridge->native_cxl_error)
                return 0;
 
-       rc = cxl_mem_alloc_event_buf(cxlds);
+       rc = cxl_mem_alloc_event_buf(mds);
        if (rc)
                return rc;
 
-       rc = cxl_event_get_int_policy(cxlds, &policy);
+       rc = cxl_event_get_int_policy(mds, &policy);
        if (rc)
                return rc;
 
@@ -638,15 +804,16 @@ static int cxl_event_config(struct pci_host_bridge *host_bridge,
            cxl_event_int_is_fw(policy.warn_settings) ||
            cxl_event_int_is_fw(policy.failure_settings) ||
            cxl_event_int_is_fw(policy.fatal_settings)) {
-               dev_err(cxlds->dev, "FW still in control of Event Logs despite _OSC settings\n");
+               dev_err(mds->cxlds.dev,
+                       "FW still in control of Event Logs despite _OSC settings\n");
                return -EBUSY;
        }
 
-       rc = cxl_event_irqsetup(cxlds);
+       rc = cxl_event_irqsetup(mds);
        if (rc)
                return rc;
 
-       cxl_mem_get_event_records(cxlds, CXLDEV_EVENT_STATUS_ALL);
+       cxl_mem_get_event_records(mds, CXLDEV_EVENT_STATUS_ALL);
 
        return 0;
 }
@@ -654,9 +821,10 @@ static int cxl_event_config(struct pci_host_bridge *host_bridge,
 static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
        struct pci_host_bridge *host_bridge = pci_find_host_bridge(pdev->bus);
+       struct cxl_memdev_state *mds;
+       struct cxl_dev_state *cxlds;
        struct cxl_register_map map;
        struct cxl_memdev *cxlmd;
-       struct cxl_dev_state *cxlds;
        int rc;
 
        /*
@@ -671,9 +839,10 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                return rc;
        pci_set_master(pdev);
 
-       cxlds = cxl_dev_state_create(&pdev->dev);
-       if (IS_ERR(cxlds))
-               return PTR_ERR(cxlds);
+       mds = cxl_memdev_state_create(&pdev->dev);
+       if (IS_ERR(mds))
+               return PTR_ERR(mds);
+       cxlds = &mds->cxlds;
        pci_set_drvdata(pdev, cxlds);
 
        cxlds->rcd = is_cxl_restricted(pdev);
@@ -714,31 +883,31 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        else
                dev_warn(&pdev->dev, "Media not active (%d)\n", rc);
 
-       rc = cxl_pci_setup_mailbox(cxlds);
+       rc = cxl_alloc_irq_vectors(pdev);
        if (rc)
                return rc;
 
-       rc = cxl_enumerate_cmds(cxlds);
+       rc = cxl_pci_setup_mailbox(mds);
        if (rc)
                return rc;
 
-       rc = cxl_set_timestamp(cxlds);
+       rc = cxl_enumerate_cmds(mds);
        if (rc)
                return rc;
 
-       rc = cxl_poison_state_init(cxlds);
+       rc = cxl_set_timestamp(mds);
        if (rc)
                return rc;
 
-       rc = cxl_dev_state_identify(cxlds);
+       rc = cxl_poison_state_init(mds);
        if (rc)
                return rc;
 
-       rc = cxl_mem_create_range_info(cxlds);
+       rc = cxl_dev_state_identify(mds);
        if (rc)
                return rc;
 
-       rc = cxl_alloc_irq_vectors(pdev);
+       rc = cxl_mem_create_range_info(mds);
        if (rc)
                return rc;
 
@@ -746,7 +915,11 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        if (IS_ERR(cxlmd))
                return PTR_ERR(cxlmd);
 
-       rc = cxl_event_config(host_bridge, cxlds);
+       rc = cxl_memdev_setup_fw_upload(mds);
+       if (rc)
+               return rc;
+
+       rc = cxl_event_config(host_bridge, mds);
        if (rc)
                return rc;
 
index 71cfa1f..7cb8994 100644 (file)
@@ -15,9 +15,9 @@ extern const struct nvdimm_security_ops *cxl_security_ops;
 
 static __read_mostly DECLARE_BITMAP(exclusive_cmds, CXL_MEM_COMMAND_ID_MAX);
 
-static void clear_exclusive(void *cxlds)
+static void clear_exclusive(void *mds)
 {
-       clear_exclusive_cxl_commands(cxlds, exclusive_cmds);
+       clear_exclusive_cxl_commands(mds, exclusive_cmds);
 }
 
 static void unregister_nvdimm(void *nvdimm)
@@ -65,13 +65,13 @@ static int cxl_nvdimm_probe(struct device *dev)
        struct cxl_nvdimm *cxl_nvd = to_cxl_nvdimm(dev);
        struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
        struct cxl_nvdimm_bridge *cxl_nvb = cxlmd->cxl_nvb;
+       struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
        unsigned long flags = 0, cmd_mask = 0;
-       struct cxl_dev_state *cxlds = cxlmd->cxlds;
        struct nvdimm *nvdimm;
        int rc;
 
-       set_exclusive_cxl_commands(cxlds, exclusive_cmds);
-       rc = devm_add_action_or_reset(dev, clear_exclusive, cxlds);
+       set_exclusive_cxl_commands(mds, exclusive_cmds);
+       rc = devm_add_action_or_reset(dev, clear_exclusive, mds);
        if (rc)
                return rc;
 
@@ -100,22 +100,23 @@ static struct cxl_driver cxl_nvdimm_driver = {
        },
 };
 
-static int cxl_pmem_get_config_size(struct cxl_dev_state *cxlds,
+static int cxl_pmem_get_config_size(struct cxl_memdev_state *mds,
                                    struct nd_cmd_get_config_size *cmd,
                                    unsigned int buf_len)
 {
        if (sizeof(*cmd) > buf_len)
                return -EINVAL;
 
-       *cmd = (struct nd_cmd_get_config_size) {
-                .config_size = cxlds->lsa_size,
-                .max_xfer = cxlds->payload_size - sizeof(struct cxl_mbox_set_lsa),
+       *cmd = (struct nd_cmd_get_config_size){
+               .config_size = mds->lsa_size,
+               .max_xfer =
+                       mds->payload_size - sizeof(struct cxl_mbox_set_lsa),
        };
 
        return 0;
 }
 
-static int cxl_pmem_get_config_data(struct cxl_dev_state *cxlds,
+static int cxl_pmem_get_config_data(struct cxl_memdev_state *mds,
                                    struct nd_cmd_get_config_data_hdr *cmd,
                                    unsigned int buf_len)
 {
@@ -140,13 +141,13 @@ static int cxl_pmem_get_config_data(struct cxl_dev_state *cxlds,
                .payload_out = cmd->out_buf,
        };
 
-       rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
+       rc = cxl_internal_send_cmd(mds, &mbox_cmd);
        cmd->status = 0;
 
        return rc;
 }
 
-static int cxl_pmem_set_config_data(struct cxl_dev_state *cxlds,
+static int cxl_pmem_set_config_data(struct cxl_memdev_state *mds,
                                    struct nd_cmd_set_config_hdr *cmd,
                                    unsigned int buf_len)
 {
@@ -176,7 +177,7 @@ static int cxl_pmem_set_config_data(struct cxl_dev_state *cxlds,
                .size_in = struct_size(set_lsa, data, cmd->in_length),
        };
 
-       rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
+       rc = cxl_internal_send_cmd(mds, &mbox_cmd);
 
        /*
         * Set "firmware" status (4-packed bytes at the end of the input
@@ -194,18 +195,18 @@ static int cxl_pmem_nvdimm_ctl(struct nvdimm *nvdimm, unsigned int cmd,
        struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
        unsigned long cmd_mask = nvdimm_cmd_mask(nvdimm);
        struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
-       struct cxl_dev_state *cxlds = cxlmd->cxlds;
+       struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
 
        if (!test_bit(cmd, &cmd_mask))
                return -ENOTTY;
 
        switch (cmd) {
        case ND_CMD_GET_CONFIG_SIZE:
-               return cxl_pmem_get_config_size(cxlds, buf, buf_len);
+               return cxl_pmem_get_config_size(mds, buf, buf_len);
        case ND_CMD_GET_CONFIG_DATA:
-               return cxl_pmem_get_config_data(cxlds, buf, buf_len);
+               return cxl_pmem_get_config_data(mds, buf, buf_len);
        case ND_CMD_SET_CONFIG_DATA:
-               return cxl_pmem_set_config_data(cxlds, buf, buf_len);
+               return cxl_pmem_set_config_data(mds, buf, buf_len);
        default:
                return -ENOTTY;
        }
index c23b616..07c5ac5 100644 (file)
@@ -60,17 +60,13 @@ static int discover_region(struct device *dev, void *root)
 static int cxl_switch_port_probe(struct cxl_port *port)
 {
        struct cxl_hdm *cxlhdm;
-       int rc, nr_dports;
-
-       nr_dports = devm_cxl_port_enumerate_dports(port);
-       if (nr_dports < 0)
-               return nr_dports;
+       int rc;
 
-       cxlhdm = devm_cxl_setup_hdm(port, NULL);
-       rc = devm_cxl_enable_hdm(port, cxlhdm);
-       if (rc)
+       rc = devm_cxl_port_enumerate_dports(port);
+       if (rc < 0)
                return rc;
 
+       cxlhdm = devm_cxl_setup_hdm(port, NULL);
        if (!IS_ERR(cxlhdm))
                return devm_cxl_enumerate_decoders(cxlhdm, NULL);
 
@@ -79,7 +75,7 @@ static int cxl_switch_port_probe(struct cxl_port *port)
                return PTR_ERR(cxlhdm);
        }
 
-       if (nr_dports == 1) {
+       if (rc == 1) {
                dev_dbg(&port->dev, "Fallback to passthrough decoder\n");
                return devm_cxl_add_passthrough_decoder(port);
        }
index 4ad4bda..21856a3 100644 (file)
@@ -14,7 +14,7 @@ static unsigned long cxl_pmem_get_security_flags(struct nvdimm *nvdimm,
 {
        struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
        struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
-       struct cxl_dev_state *cxlds = cxlmd->cxlds;
+       struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
        unsigned long security_flags = 0;
        struct cxl_get_security_output {
                __le32 flags;
@@ -29,11 +29,14 @@ static unsigned long cxl_pmem_get_security_flags(struct nvdimm *nvdimm,
                .payload_out = &out,
        };
 
-       rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
+       rc = cxl_internal_send_cmd(mds, &mbox_cmd);
        if (rc < 0)
                return 0;
 
        sec_out = le32_to_cpu(out.flags);
+       /* cache security state */
+       mds->security.state = sec_out;
+
        if (ptype == NVDIMM_MASTER) {
                if (sec_out & CXL_PMEM_SEC_STATE_MASTER_PASS_SET)
                        set_bit(NVDIMM_SECURITY_UNLOCKED, &security_flags);
@@ -67,7 +70,7 @@ static int cxl_pmem_security_change_key(struct nvdimm *nvdimm,
 {
        struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
        struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
-       struct cxl_dev_state *cxlds = cxlmd->cxlds;
+       struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
        struct cxl_mbox_cmd mbox_cmd;
        struct cxl_set_pass set_pass;
 
@@ -84,7 +87,7 @@ static int cxl_pmem_security_change_key(struct nvdimm *nvdimm,
                .payload_in = &set_pass,
        };
 
-       return cxl_internal_send_cmd(cxlds, &mbox_cmd);
+       return cxl_internal_send_cmd(mds, &mbox_cmd);
 }
 
 static int __cxl_pmem_security_disable(struct nvdimm *nvdimm,
@@ -93,7 +96,7 @@ static int __cxl_pmem_security_disable(struct nvdimm *nvdimm,
 {
        struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
        struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
-       struct cxl_dev_state *cxlds = cxlmd->cxlds;
+       struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
        struct cxl_disable_pass dis_pass;
        struct cxl_mbox_cmd mbox_cmd;
 
@@ -109,7 +112,7 @@ static int __cxl_pmem_security_disable(struct nvdimm *nvdimm,
                .payload_in = &dis_pass,
        };
 
-       return cxl_internal_send_cmd(cxlds, &mbox_cmd);
+       return cxl_internal_send_cmd(mds, &mbox_cmd);
 }
 
 static int cxl_pmem_security_disable(struct nvdimm *nvdimm,
@@ -128,12 +131,12 @@ static int cxl_pmem_security_freeze(struct nvdimm *nvdimm)
 {
        struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
        struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
-       struct cxl_dev_state *cxlds = cxlmd->cxlds;
+       struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
        struct cxl_mbox_cmd mbox_cmd = {
                .opcode = CXL_MBOX_OP_FREEZE_SECURITY,
        };
 
-       return cxl_internal_send_cmd(cxlds, &mbox_cmd);
+       return cxl_internal_send_cmd(mds, &mbox_cmd);
 }
 
 static int cxl_pmem_security_unlock(struct nvdimm *nvdimm,
@@ -141,7 +144,7 @@ static int cxl_pmem_security_unlock(struct nvdimm *nvdimm,
 {
        struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
        struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
-       struct cxl_dev_state *cxlds = cxlmd->cxlds;
+       struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
        u8 pass[NVDIMM_PASSPHRASE_LEN];
        struct cxl_mbox_cmd mbox_cmd;
        int rc;
@@ -153,7 +156,7 @@ static int cxl_pmem_security_unlock(struct nvdimm *nvdimm,
                .payload_in = pass,
        };
 
-       rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
+       rc = cxl_internal_send_cmd(mds, &mbox_cmd);
        if (rc < 0)
                return rc;
 
@@ -166,7 +169,7 @@ static int cxl_pmem_security_passphrase_erase(struct nvdimm *nvdimm,
 {
        struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
        struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
-       struct cxl_dev_state *cxlds = cxlmd->cxlds;
+       struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
        struct cxl_mbox_cmd mbox_cmd;
        struct cxl_pass_erase erase;
        int rc;
@@ -182,7 +185,7 @@ static int cxl_pmem_security_passphrase_erase(struct nvdimm *nvdimm,
                .payload_in = &erase,
        };
 
-       rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
+       rc = cxl_internal_send_cmd(mds, &mbox_cmd);
        if (rc < 0)
                return rc;
 
index 8052d34..2734342 100644 (file)
@@ -49,9 +49,9 @@ static inline void prepare_to_rcuwait(struct rcuwait *w)
 
 extern void finish_rcuwait(struct rcuwait *w);
 
-#define rcuwait_wait_event(w, condition, state)                                \
+#define ___rcuwait_wait_event(w, condition, state, ret, cmd)           \
 ({                                                                     \
-       int __ret = 0;                                                  \
+       long __ret = ret;                                               \
        prepare_to_rcuwait(w);                                          \
        for (;;) {                                                      \
                /*                                                      \
@@ -67,10 +67,27 @@ extern void finish_rcuwait(struct rcuwait *w);
                        break;                                          \
                }                                                       \
                                                                        \
-               schedule();                                             \
+               cmd;                                                    \
        }                                                               \
        finish_rcuwait(w);                                              \
        __ret;                                                          \
 })
 
+#define rcuwait_wait_event(w, condition, state)                                \
+       ___rcuwait_wait_event(w, condition, state, 0, schedule())
+
+#define __rcuwait_wait_event_timeout(w, condition, state, timeout)     \
+       ___rcuwait_wait_event(w, ___wait_cond_timeout(condition),       \
+                             state, timeout,                           \
+                             __ret = schedule_timeout(__ret))
+
+#define rcuwait_wait_event_timeout(w, condition, state, timeout)       \
+({                                                                     \
+       long __ret = timeout;                                           \
+       if (!___wait_cond_timeout(condition))                           \
+               __ret = __rcuwait_wait_event_timeout(w, condition,      \
+                                                    state, timeout);   \
+       __ret;                                                          \
+})
+
 #endif /* _LINUX_RCUWAIT_H_ */
index 6f9347a..fba7bec 100644 (file)
@@ -6,7 +6,6 @@ ldflags-y += --wrap=acpi_pci_find_root
 ldflags-y += --wrap=nvdimm_bus_register
 ldflags-y += --wrap=devm_cxl_port_enumerate_dports
 ldflags-y += --wrap=devm_cxl_setup_hdm
-ldflags-y += --wrap=devm_cxl_enable_hdm
 ldflags-y += --wrap=devm_cxl_add_passthrough_decoder
 ldflags-y += --wrap=devm_cxl_enumerate_decoders
 ldflags-y += --wrap=cxl_await_media_ready
index bf00dc5..5565164 100644 (file)
@@ -713,7 +713,7 @@ static void default_mock_decoder(struct cxl_decoder *cxld)
 
        cxld->interleave_ways = 1;
        cxld->interleave_granularity = 256;
-       cxld->target_type = CXL_DECODER_EXPANDER;
+       cxld->target_type = CXL_DECODER_HOSTONLYMEM;
        cxld->commit = mock_decoder_commit;
        cxld->reset = mock_decoder_reset;
 }
@@ -787,7 +787,7 @@ static void mock_init_hdm_decoder(struct cxl_decoder *cxld)
 
        cxld->interleave_ways = 2;
        eig_to_granularity(window->granularity, &cxld->interleave_granularity);
-       cxld->target_type = CXL_DECODER_EXPANDER;
+       cxld->target_type = CXL_DECODER_HOSTONLYMEM;
        cxld->flags = CXL_DECODER_F_ENABLE;
        cxled->state = CXL_DECODER_STATE_AUTO;
        port->commit_end = cxld->id;
@@ -820,7 +820,7 @@ static void mock_init_hdm_decoder(struct cxl_decoder *cxld)
                } else
                        cxlsd->target[0] = dport;
                cxld = &cxlsd->cxld;
-               cxld->target_type = CXL_DECODER_EXPANDER;
+               cxld->target_type = CXL_DECODER_HOSTONLYMEM;
                cxld->flags = CXL_DECODER_F_ENABLE;
                iter->commit_end = 0;
                /*
index 34b4802..464fc39 100644 (file)
@@ -8,11 +8,14 @@
 #include <linux/sizes.h>
 #include <linux/bits.h>
 #include <asm/unaligned.h>
+#include <crypto/sha2.h>
 #include <cxlmem.h>
 
 #include "trace.h"
 
 #define LSA_SIZE SZ_128K
+#define FW_SIZE SZ_64M
+#define FW_SLOTS 3
 #define DEV_SIZE SZ_2G
 #define EFFECT(x) (1U << x)
 
 
 static unsigned int poison_inject_dev_max = MOCK_INJECT_DEV_MAX;
 
+enum cxl_command_effects {
+       CONF_CHANGE_COLD_RESET = 0,
+       CONF_CHANGE_IMMEDIATE,
+       DATA_CHANGE_IMMEDIATE,
+       POLICY_CHANGE_IMMEDIATE,
+       LOG_CHANGE_IMMEDIATE,
+       SECURITY_CHANGE_IMMEDIATE,
+       BACKGROUND_OP,
+       SECONDARY_MBOX_SUPPORTED,
+};
+
+#define CXL_CMD_EFFECT_NONE cpu_to_le16(0)
+
 static struct cxl_cel_entry mock_cel[] = {
        {
                .opcode = cpu_to_le16(CXL_MBOX_OP_GET_SUPPORTED_LOGS),
-               .effect = cpu_to_le16(0),
+               .effect = CXL_CMD_EFFECT_NONE,
        },
        {
                .opcode = cpu_to_le16(CXL_MBOX_OP_IDENTIFY),
-               .effect = cpu_to_le16(0),
+               .effect = CXL_CMD_EFFECT_NONE,
        },
        {
                .opcode = cpu_to_le16(CXL_MBOX_OP_GET_LSA),
-               .effect = cpu_to_le16(0),
+               .effect = CXL_CMD_EFFECT_NONE,
        },
        {
                .opcode = cpu_to_le16(CXL_MBOX_OP_GET_PARTITION_INFO),
-               .effect = cpu_to_le16(0),
+               .effect = CXL_CMD_EFFECT_NONE,
        },
        {
                .opcode = cpu_to_le16(CXL_MBOX_OP_SET_LSA),
-               .effect = cpu_to_le16(EFFECT(1) | EFFECT(2)),
+               .effect = cpu_to_le16(EFFECT(CONF_CHANGE_IMMEDIATE) |
+                                     EFFECT(DATA_CHANGE_IMMEDIATE)),
        },
        {
                .opcode = cpu_to_le16(CXL_MBOX_OP_GET_HEALTH_INFO),
-               .effect = cpu_to_le16(0),
+               .effect = CXL_CMD_EFFECT_NONE,
        },
        {
                .opcode = cpu_to_le16(CXL_MBOX_OP_GET_POISON),
-               .effect = cpu_to_le16(0),
+               .effect = CXL_CMD_EFFECT_NONE,
        },
        {
                .opcode = cpu_to_le16(CXL_MBOX_OP_INJECT_POISON),
-               .effect = cpu_to_le16(0),
+               .effect = cpu_to_le16(EFFECT(DATA_CHANGE_IMMEDIATE)),
        },
        {
                .opcode = cpu_to_le16(CXL_MBOX_OP_CLEAR_POISON),
-               .effect = cpu_to_le16(0),
+               .effect = cpu_to_le16(EFFECT(DATA_CHANGE_IMMEDIATE)),
+       },
+       {
+               .opcode = cpu_to_le16(CXL_MBOX_OP_GET_FW_INFO),
+               .effect = CXL_CMD_EFFECT_NONE,
+       },
+       {
+               .opcode = cpu_to_le16(CXL_MBOX_OP_TRANSFER_FW),
+               .effect = cpu_to_le16(EFFECT(CONF_CHANGE_COLD_RESET) |
+                                     EFFECT(BACKGROUND_OP)),
+       },
+       {
+               .opcode = cpu_to_le16(CXL_MBOX_OP_ACTIVATE_FW),
+               .effect = cpu_to_le16(EFFECT(CONF_CHANGE_COLD_RESET) |
+                                     EFFECT(CONF_CHANGE_IMMEDIATE)),
        },
 };
 
@@ -102,13 +133,17 @@ struct mock_event_log {
 };
 
 struct mock_event_store {
-       struct cxl_dev_state *cxlds;
+       struct cxl_memdev_state *mds;
        struct mock_event_log mock_logs[CXL_EVENT_TYPE_MAX];
        u32 ev_status;
 };
 
 struct cxl_mockmem_data {
        void *lsa;
+       void *fw;
+       int fw_slot;
+       int fw_staged;
+       size_t fw_size;
        u32 security_state;
        u8 user_pass[NVDIMM_PASSPHRASE_LEN];
        u8 master_pass[NVDIMM_PASSPHRASE_LEN];
@@ -180,8 +215,7 @@ static void mes_add_event(struct mock_event_store *mes,
        log->nr_events++;
 }
 
-static int mock_get_event(struct cxl_dev_state *cxlds,
-                         struct cxl_mbox_cmd *cmd)
+static int mock_get_event(struct device *dev, struct cxl_mbox_cmd *cmd)
 {
        struct cxl_get_event_payload *pl;
        struct mock_event_log *log;
@@ -201,7 +235,7 @@ static int mock_get_event(struct cxl_dev_state *cxlds,
 
        memset(cmd->payload_out, 0, cmd->size_out);
 
-       log = event_find_log(cxlds->dev, log_type);
+       log = event_find_log(dev, log_type);
        if (!log || event_log_empty(log))
                return 0;
 
@@ -234,8 +268,7 @@ static int mock_get_event(struct cxl_dev_state *cxlds,
        return 0;
 }
 
-static int mock_clear_event(struct cxl_dev_state *cxlds,
-                           struct cxl_mbox_cmd *cmd)
+static int mock_clear_event(struct device *dev, struct cxl_mbox_cmd *cmd)
 {
        struct cxl_mbox_clear_event_payload *pl = cmd->payload_in;
        struct mock_event_log *log;
@@ -246,7 +279,7 @@ static int mock_clear_event(struct cxl_dev_state *cxlds,
        if (log_type >= CXL_EVENT_TYPE_MAX)
                return -EINVAL;
 
-       log = event_find_log(cxlds->dev, log_type);
+       log = event_find_log(dev, log_type);
        if (!log)
                return 0; /* No mock data in this log */
 
@@ -256,7 +289,7 @@ static int mock_clear_event(struct cxl_dev_state *cxlds,
         * However, this is not good behavior for the host so test it.
         */
        if (log->clear_idx + pl->nr_recs > log->cur_idx) {
-               dev_err(cxlds->dev,
+               dev_err(dev,
                        "Attempting to clear more events than returned!\n");
                return -EINVAL;
        }
@@ -266,7 +299,7 @@ static int mock_clear_event(struct cxl_dev_state *cxlds,
             nr < pl->nr_recs;
             nr++, handle++) {
                if (handle != le16_to_cpu(pl->handles[nr])) {
-                       dev_err(cxlds->dev, "Clearing events out of order\n");
+                       dev_err(dev, "Clearing events out of order\n");
                        return -EINVAL;
                }
        }
@@ -293,7 +326,7 @@ static void cxl_mock_event_trigger(struct device *dev)
                        event_reset_log(log);
        }
 
-       cxl_mem_get_event_records(mes->cxlds, mes->ev_status);
+       cxl_mem_get_event_records(mes->mds, mes->ev_status);
 }
 
 struct cxl_event_record_raw maint_needed = {
@@ -453,7 +486,7 @@ static int mock_gsl(struct cxl_mbox_cmd *cmd)
        return 0;
 }
 
-static int mock_get_log(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
+static int mock_get_log(struct cxl_memdev_state *mds, struct cxl_mbox_cmd *cmd)
 {
        struct cxl_mbox_get_log *gl = cmd->payload_in;
        u32 offset = le32_to_cpu(gl->offset);
@@ -463,7 +496,7 @@ static int mock_get_log(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
 
        if (cmd->size_in < sizeof(*gl))
                return -EINVAL;
-       if (length > cxlds->payload_size)
+       if (length > mds->payload_size)
                return -EINVAL;
        if (offset + length > sizeof(mock_cel))
                return -EINVAL;
@@ -477,7 +510,7 @@ static int mock_get_log(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
        return 0;
 }
 
-static int mock_rcd_id(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
+static int mock_rcd_id(struct cxl_mbox_cmd *cmd)
 {
        struct cxl_mbox_identify id = {
                .fw_revision = { "mock fw v1 " },
@@ -495,7 +528,7 @@ static int mock_rcd_id(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
        return 0;
 }
 
-static int mock_id(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
+static int mock_id(struct cxl_mbox_cmd *cmd)
 {
        struct cxl_mbox_identify id = {
                .fw_revision = { "mock fw v1 " },
@@ -517,8 +550,7 @@ static int mock_id(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
        return 0;
 }
 
-static int mock_partition_info(struct cxl_dev_state *cxlds,
-                              struct cxl_mbox_cmd *cmd)
+static int mock_partition_info(struct cxl_mbox_cmd *cmd)
 {
        struct cxl_mbox_get_partition_info pi = {
                .active_volatile_cap =
@@ -535,11 +567,52 @@ static int mock_partition_info(struct cxl_dev_state *cxlds,
        return 0;
 }
 
-static int mock_get_security_state(struct cxl_dev_state *cxlds,
-                                  struct cxl_mbox_cmd *cmd)
+static int mock_sanitize(struct cxl_mockmem_data *mdata,
+                        struct cxl_mbox_cmd *cmd)
 {
-       struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
+       if (cmd->size_in != 0)
+               return -EINVAL;
+
+       if (cmd->size_out != 0)
+               return -EINVAL;
+
+       if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET) {
+               cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
+               return -ENXIO;
+       }
+       if (mdata->security_state & CXL_PMEM_SEC_STATE_LOCKED) {
+               cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
+               return -ENXIO;
+       }
+
+       return 0; /* assume less than 2 secs, no bg */
+}
+
+static int mock_secure_erase(struct cxl_mockmem_data *mdata,
+                            struct cxl_mbox_cmd *cmd)
+{
+       if (cmd->size_in != 0)
+               return -EINVAL;
 
+       if (cmd->size_out != 0)
+               return -EINVAL;
+
+       if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET) {
+               cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
+               return -ENXIO;
+       }
+
+       if (mdata->security_state & CXL_PMEM_SEC_STATE_LOCKED) {
+               cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
+               return -ENXIO;
+       }
+
+       return 0;
+}
+
+static int mock_get_security_state(struct cxl_mockmem_data *mdata,
+                                  struct cxl_mbox_cmd *cmd)
+{
        if (cmd->size_in)
                return -EINVAL;
 
@@ -569,9 +642,9 @@ static void user_plimit_check(struct cxl_mockmem_data *mdata)
                mdata->security_state |= CXL_PMEM_SEC_STATE_USER_PLIMIT;
 }
 
-static int mock_set_passphrase(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
+static int mock_set_passphrase(struct cxl_mockmem_data *mdata,
+                              struct cxl_mbox_cmd *cmd)
 {
-       struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
        struct cxl_set_pass *set_pass;
 
        if (cmd->size_in != sizeof(*set_pass))
@@ -629,9 +702,9 @@ static int mock_set_passphrase(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd
        return -EINVAL;
 }
 
-static int mock_disable_passphrase(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
+static int mock_disable_passphrase(struct cxl_mockmem_data *mdata,
+                                  struct cxl_mbox_cmd *cmd)
 {
-       struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
        struct cxl_disable_pass *dis_pass;
 
        if (cmd->size_in != sizeof(*dis_pass))
@@ -700,10 +773,9 @@ static int mock_disable_passphrase(struct cxl_dev_state *cxlds, struct cxl_mbox_
        return 0;
 }
 
-static int mock_freeze_security(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
+static int mock_freeze_security(struct cxl_mockmem_data *mdata,
+                               struct cxl_mbox_cmd *cmd)
 {
-       struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
-
        if (cmd->size_in != 0)
                return -EINVAL;
 
@@ -717,10 +789,9 @@ static int mock_freeze_security(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd
        return 0;
 }
 
-static int mock_unlock_security(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
+static int mock_unlock_security(struct cxl_mockmem_data *mdata,
+                               struct cxl_mbox_cmd *cmd)
 {
-       struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
-
        if (cmd->size_in != NVDIMM_PASSPHRASE_LEN)
                return -EINVAL;
 
@@ -759,10 +830,9 @@ static int mock_unlock_security(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd
        return 0;
 }
 
-static int mock_passphrase_secure_erase(struct cxl_dev_state *cxlds,
+static int mock_passphrase_secure_erase(struct cxl_mockmem_data *mdata,
                                        struct cxl_mbox_cmd *cmd)
 {
-       struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
        struct cxl_pass_erase *erase;
 
        if (cmd->size_in != sizeof(*erase))
@@ -858,10 +928,10 @@ static int mock_passphrase_secure_erase(struct cxl_dev_state *cxlds,
        return 0;
 }
 
-static int mock_get_lsa(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
+static int mock_get_lsa(struct cxl_mockmem_data *mdata,
+                       struct cxl_mbox_cmd *cmd)
 {
        struct cxl_mbox_get_lsa *get_lsa = cmd->payload_in;
-       struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
        void *lsa = mdata->lsa;
        u32 offset, length;
 
@@ -878,10 +948,10 @@ static int mock_get_lsa(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
        return 0;
 }
 
-static int mock_set_lsa(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
+static int mock_set_lsa(struct cxl_mockmem_data *mdata,
+                       struct cxl_mbox_cmd *cmd)
 {
        struct cxl_mbox_set_lsa *set_lsa = cmd->payload_in;
-       struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
        void *lsa = mdata->lsa;
        u32 offset, length;
 
@@ -896,8 +966,7 @@ static int mock_set_lsa(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
        return 0;
 }
 
-static int mock_health_info(struct cxl_dev_state *cxlds,
-                           struct cxl_mbox_cmd *cmd)
+static int mock_health_info(struct cxl_mbox_cmd *cmd)
 {
        struct cxl_mbox_health_info health_info = {
                /* set flags for maint needed, perf degraded, hw replacement */
@@ -1114,9 +1183,90 @@ static struct attribute *cxl_mock_mem_core_attrs[] = {
 };
 ATTRIBUTE_GROUPS(cxl_mock_mem_core);
 
-static int cxl_mock_mbox_send(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
+static int mock_fw_info(struct cxl_mockmem_data *mdata,
+                       struct cxl_mbox_cmd *cmd)
+{
+       struct cxl_mbox_get_fw_info fw_info = {
+               .num_slots = FW_SLOTS,
+               .slot_info = (mdata->fw_slot & 0x7) |
+                            ((mdata->fw_staged & 0x7) << 3),
+               .activation_cap = 0,
+       };
+
+       strcpy(fw_info.slot_1_revision, "cxl_test_fw_001");
+       strcpy(fw_info.slot_2_revision, "cxl_test_fw_002");
+       strcpy(fw_info.slot_3_revision, "cxl_test_fw_003");
+       strcpy(fw_info.slot_4_revision, "");
+
+       if (cmd->size_out < sizeof(fw_info))
+               return -EINVAL;
+
+       memcpy(cmd->payload_out, &fw_info, sizeof(fw_info));
+       return 0;
+}
+
+static int mock_transfer_fw(struct cxl_mockmem_data *mdata,
+                           struct cxl_mbox_cmd *cmd)
 {
+       struct cxl_mbox_transfer_fw *transfer = cmd->payload_in;
+       void *fw = mdata->fw;
+       size_t offset, length;
+
+       offset = le32_to_cpu(transfer->offset) * CXL_FW_TRANSFER_ALIGNMENT;
+       length = cmd->size_in - sizeof(*transfer);
+       if (offset + length > FW_SIZE)
+               return -EINVAL;
+
+       switch (transfer->action) {
+       case CXL_FW_TRANSFER_ACTION_FULL:
+               if (offset != 0)
+                       return -EINVAL;
+               fallthrough;
+       case CXL_FW_TRANSFER_ACTION_END:
+               if (transfer->slot == 0 || transfer->slot > FW_SLOTS)
+                       return -EINVAL;
+               mdata->fw_size = offset + length;
+               break;
+       case CXL_FW_TRANSFER_ACTION_INITIATE:
+       case CXL_FW_TRANSFER_ACTION_CONTINUE:
+               break;
+       case CXL_FW_TRANSFER_ACTION_ABORT:
+               return 0;
+       default:
+               return -EINVAL;
+       }
+
+       memcpy(fw + offset, transfer->data, length);
+       return 0;
+}
+
+static int mock_activate_fw(struct cxl_mockmem_data *mdata,
+                           struct cxl_mbox_cmd *cmd)
+{
+       struct cxl_mbox_activate_fw *activate = cmd->payload_in;
+
+       if (activate->slot == 0 || activate->slot > FW_SLOTS)
+               return -EINVAL;
+
+       switch (activate->action) {
+       case CXL_FW_ACTIVATE_ONLINE:
+               mdata->fw_slot = activate->slot;
+               mdata->fw_staged = 0;
+               return 0;
+       case CXL_FW_ACTIVATE_OFFLINE:
+               mdata->fw_staged = activate->slot;
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static int cxl_mock_mbox_send(struct cxl_memdev_state *mds,
+                             struct cxl_mbox_cmd *cmd)
+{
+       struct cxl_dev_state *cxlds = &mds->cxlds;
        struct device *dev = cxlds->dev;
+       struct cxl_mockmem_data *mdata = dev_get_drvdata(dev);
        int rc = -EIO;
 
        switch (cmd->opcode) {
@@ -1127,49 +1277,55 @@ static int cxl_mock_mbox_send(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *
                rc = mock_gsl(cmd);
                break;
        case CXL_MBOX_OP_GET_LOG:
-               rc = mock_get_log(cxlds, cmd);
+               rc = mock_get_log(mds, cmd);
                break;
        case CXL_MBOX_OP_IDENTIFY:
                if (cxlds->rcd)
-                       rc = mock_rcd_id(cxlds, cmd);
+                       rc = mock_rcd_id(cmd);
                else
-                       rc = mock_id(cxlds, cmd);
+                       rc = mock_id(cmd);
                break;
        case CXL_MBOX_OP_GET_LSA:
-               rc = mock_get_lsa(cxlds, cmd);
+               rc = mock_get_lsa(mdata, cmd);
                break;
        case CXL_MBOX_OP_GET_PARTITION_INFO:
-               rc = mock_partition_info(cxlds, cmd);
+               rc = mock_partition_info(cmd);
                break;
        case CXL_MBOX_OP_GET_EVENT_RECORD:
-               rc = mock_get_event(cxlds, cmd);
+               rc = mock_get_event(dev, cmd);
                break;
        case CXL_MBOX_OP_CLEAR_EVENT_RECORD:
-               rc = mock_clear_event(cxlds, cmd);
+               rc = mock_clear_event(dev, cmd);
                break;
        case CXL_MBOX_OP_SET_LSA:
-               rc = mock_set_lsa(cxlds, cmd);
+               rc = mock_set_lsa(mdata, cmd);
                break;
        case CXL_MBOX_OP_GET_HEALTH_INFO:
-               rc = mock_health_info(cxlds, cmd);
+               rc = mock_health_info(cmd);
+               break;
+       case CXL_MBOX_OP_SANITIZE:
+               rc = mock_sanitize(mdata, cmd);
+               break;
+       case CXL_MBOX_OP_SECURE_ERASE:
+               rc = mock_secure_erase(mdata, cmd);
                break;
        case CXL_MBOX_OP_GET_SECURITY_STATE:
-               rc = mock_get_security_state(cxlds, cmd);
+               rc = mock_get_security_state(mdata, cmd);
                break;
        case CXL_MBOX_OP_SET_PASSPHRASE:
-               rc = mock_set_passphrase(cxlds, cmd);
+               rc = mock_set_passphrase(mdata, cmd);
                break;
        case CXL_MBOX_OP_DISABLE_PASSPHRASE:
-               rc = mock_disable_passphrase(cxlds, cmd);
+               rc = mock_disable_passphrase(mdata, cmd);
                break;
        case CXL_MBOX_OP_FREEZE_SECURITY:
-               rc = mock_freeze_security(cxlds, cmd);
+               rc = mock_freeze_security(mdata, cmd);
                break;
        case CXL_MBOX_OP_UNLOCK:
-               rc = mock_unlock_security(cxlds, cmd);
+               rc = mock_unlock_security(mdata, cmd);
                break;
        case CXL_MBOX_OP_PASSPHRASE_SECURE_ERASE:
-               rc = mock_passphrase_secure_erase(cxlds, cmd);
+               rc = mock_passphrase_secure_erase(mdata, cmd);
                break;
        case CXL_MBOX_OP_GET_POISON:
                rc = mock_get_poison(cxlds, cmd);
@@ -1180,6 +1336,15 @@ static int cxl_mock_mbox_send(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *
        case CXL_MBOX_OP_CLEAR_POISON:
                rc = mock_clear_poison(cxlds, cmd);
                break;
+       case CXL_MBOX_OP_GET_FW_INFO:
+               rc = mock_fw_info(mdata, cmd);
+               break;
+       case CXL_MBOX_OP_TRANSFER_FW:
+               rc = mock_transfer_fw(mdata, cmd);
+               break;
+       case CXL_MBOX_OP_ACTIVATE_FW:
+               rc = mock_activate_fw(mdata, cmd);
+               break;
        default:
                break;
        }
@@ -1195,6 +1360,11 @@ static void label_area_release(void *lsa)
        vfree(lsa);
 }
 
+static void fw_buf_release(void *buf)
+{
+       vfree(buf);
+}
+
 static bool is_rcd(struct platform_device *pdev)
 {
        const struct platform_device_id *id = platform_get_device_id(pdev);
@@ -1215,6 +1385,7 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct cxl_memdev *cxlmd;
+       struct cxl_memdev_state *mds;
        struct cxl_dev_state *cxlds;
        struct cxl_mockmem_data *mdata;
        int rc;
@@ -1227,52 +1398,67 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
        mdata->lsa = vmalloc(LSA_SIZE);
        if (!mdata->lsa)
                return -ENOMEM;
+       mdata->fw = vmalloc(FW_SIZE);
+       if (!mdata->fw)
+               return -ENOMEM;
+       mdata->fw_slot = 2;
+
        rc = devm_add_action_or_reset(dev, label_area_release, mdata->lsa);
        if (rc)
                return rc;
 
-       cxlds = cxl_dev_state_create(dev);
-       if (IS_ERR(cxlds))
-               return PTR_ERR(cxlds);
+       rc = devm_add_action_or_reset(dev, fw_buf_release, mdata->fw);
+       if (rc)
+               return rc;
+
+       mds = cxl_memdev_state_create(dev);
+       if (IS_ERR(mds))
+               return PTR_ERR(mds);
 
+       mds->mbox_send = cxl_mock_mbox_send;
+       mds->payload_size = SZ_4K;
+       mds->event.buf = (struct cxl_get_event_payload *) mdata->event_buf;
+
+       cxlds = &mds->cxlds;
        cxlds->serial = pdev->id;
-       cxlds->mbox_send = cxl_mock_mbox_send;
-       cxlds->payload_size = SZ_4K;
-       cxlds->event.buf = (struct cxl_get_event_payload *) mdata->event_buf;
        if (is_rcd(pdev)) {
                cxlds->rcd = true;
                cxlds->component_reg_phys = CXL_RESOURCE_NONE;
        }
 
-       rc = cxl_enumerate_cmds(cxlds);
+       rc = cxl_enumerate_cmds(mds);
        if (rc)
                return rc;
 
-       rc = cxl_poison_state_init(cxlds);
+       rc = cxl_poison_state_init(mds);
        if (rc)
                return rc;
 
-       rc = cxl_set_timestamp(cxlds);
+       rc = cxl_set_timestamp(mds);
        if (rc)
                return rc;
 
        cxlds->media_ready = true;
-       rc = cxl_dev_state_identify(cxlds);
+       rc = cxl_dev_state_identify(mds);
        if (rc)
                return rc;
 
-       rc = cxl_mem_create_range_info(cxlds);
+       rc = cxl_mem_create_range_info(mds);
        if (rc)
                return rc;
 
-       mdata->mes.cxlds = cxlds;
+       mdata->mes.mds = mds;
        cxl_mock_add_event_logs(&mdata->mes);
 
        cxlmd = devm_cxl_add_memdev(cxlds);
        if (IS_ERR(cxlmd))
                return PTR_ERR(cxlmd);
 
-       cxl_mem_get_event_records(cxlds, CXLDEV_EVENT_STATUS_ALL);
+       rc = cxl_memdev_setup_fw_upload(mds);
+       if (rc)
+               return rc;
+
+       cxl_mem_get_event_records(mds, CXLDEV_EVENT_STATUS_ALL);
 
        return 0;
 }
@@ -1310,9 +1496,40 @@ static ssize_t security_lock_store(struct device *dev, struct device_attribute *
 
 static DEVICE_ATTR_RW(security_lock);
 
+static ssize_t fw_buf_checksum_show(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct cxl_mockmem_data *mdata = dev_get_drvdata(dev);
+       u8 hash[SHA256_DIGEST_SIZE];
+       unsigned char *hstr, *hptr;
+       struct sha256_state sctx;
+       ssize_t written = 0;
+       int i;
+
+       sha256_init(&sctx);
+       sha256_update(&sctx, mdata->fw, mdata->fw_size);
+       sha256_final(&sctx, hash);
+
+       hstr = kzalloc((SHA256_DIGEST_SIZE * 2) + 1, GFP_KERNEL);
+       if (!hstr)
+               return -ENOMEM;
+
+       hptr = hstr;
+       for (i = 0; i < SHA256_DIGEST_SIZE; i++)
+               hptr += sprintf(hptr, "%02x", hash[i]);
+
+       written = sysfs_emit(buf, "%s\n", hstr);
+
+       kfree(hstr);
+       return written;
+}
+
+static DEVICE_ATTR_RO(fw_buf_checksum);
+
 static struct attribute *cxl_mock_mem_attrs[] = {
        &dev_attr_security_lock.attr,
        &dev_attr_event_trigger.attr,
+       &dev_attr_fw_buf_checksum.attr,
        NULL
 };
 ATTRIBUTE_GROUPS(cxl_mock_mem);
index 2844165..de3933a 100644 (file)
@@ -149,21 +149,6 @@ struct cxl_hdm *__wrap_devm_cxl_setup_hdm(struct cxl_port *port,
 }
 EXPORT_SYMBOL_NS_GPL(__wrap_devm_cxl_setup_hdm, CXL);
 
-int __wrap_devm_cxl_enable_hdm(struct cxl_port *port, struct cxl_hdm *cxlhdm)
-{
-       int index, rc;
-       struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
-
-       if (ops && ops->is_mock_port(port->uport))
-               rc = 0;
-       else
-               rc = devm_cxl_enable_hdm(port, cxlhdm);
-       put_cxl_mock_ops(index);
-
-       return rc;
-}
-EXPORT_SYMBOL_NS_GPL(__wrap_devm_cxl_enable_hdm, CXL);
-
 int __wrap_devm_cxl_add_passthrough_decoder(struct cxl_port *port)
 {
        int rc, index;