nvdimm.lists.linux.dev archive mirror
 help / color / mirror / Atom feed
From: Ben Widawsky <ben.widawsky@intel.com>
To: Dan Williams <dan.j.williams@intel.com>
Cc: linux-cxl@vger.kernel.org, kernel test robot <lkp@intel.com>,
	vishal.l.verma@intel.com, nvdimm@lists.linux.dev,
	alison.schofield@intel.com, ira.weiny@intel.com,
	Jonathan.Cameron@huawei.com
Subject: Re: [PATCH v4 11/21] cxl/mbox: Move mailbox and other non-PCI specific infrastructure to the core
Date: Thu, 9 Sep 2021 09:41:25 -0700	[thread overview]
Message-ID: <20210909164125.ttvptq6eiiirvnnp@intel.com> (raw)
In-Reply-To: <163116435233.2460985.16197340449713287180.stgit@dwillia2-desk3.amr.corp.intel.com>

On 21-09-08 22:12:32, Dan Williams wrote:
> Now that the internals of mailbox operations are abstracted from the PCI
> specifics a bulk of infrastructure can move to the core.
> 
> The CXL_PMEM driver intends to proxy LIBNVDIMM UAPI and driver requests
> to the equivalent functionality provided by the CXL hardware mailbox
> interface. In support of that intent move the mailbox implementation to
> a shared location for the CXL_PCI driver native IOCTL path and CXL_PMEM
> nvdimm command proxy path to share.
> 
> A unit test framework seeks to implement a unit test backend transport
> for mailbox commands to communicate mocked up payloads. It can reuse all
> of the mailbox infrastructure minus the PCI specifics, so that also gets
> moved to the core.
> 
> Finally with the mailbox infrastructure and ioctl handling being
> transport generic there is no longer any need to pass file
> file_operations to devm_cxl_add_memdev(). That allows all the ioctl
> boilerplate to move into the core for unit test reuse.
> 
> No functional change intended, just code movement.

At some point, I think some of the comments and kernel docs need updating since
the target is no longer exclusively memory devices. Perhaps you do this in later
patches....

> 
> Acked-by: Ben Widawsky <ben.widawsky@intel.com>
> Reported-by: kernel test robot <lkp@intel.com>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> ---
>  Documentation/driver-api/cxl/memory-devices.rst |    3 
>  drivers/cxl/core/Makefile                       |    1 
>  drivers/cxl/core/bus.c                          |    4 
>  drivers/cxl/core/core.h                         |    8 
>  drivers/cxl/core/mbox.c                         |  825 +++++++++++++++++++++
>  drivers/cxl/core/memdev.c                       |   81 ++
>  drivers/cxl/cxlmem.h                            |   78 +-
>  drivers/cxl/pci.c                               |  924 -----------------------
>  8 files changed, 975 insertions(+), 949 deletions(-)
>  create mode 100644 drivers/cxl/core/mbox.c
> 
> diff --git a/Documentation/driver-api/cxl/memory-devices.rst b/Documentation/driver-api/cxl/memory-devices.rst
> index 50ebcda17ad0..4624951b5c38 100644
> --- a/Documentation/driver-api/cxl/memory-devices.rst
> +++ b/Documentation/driver-api/cxl/memory-devices.rst
> @@ -45,6 +45,9 @@ CXL Core
>  .. kernel-doc:: drivers/cxl/core/regs.c
>     :doc: cxl registers
>  
> +.. kernel-doc:: drivers/cxl/core/mbox.c
> +   :doc: cxl mbox
> +
>  External Interfaces
>  ===================
>  
> diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile
> index 0fdbf3c6ac1a..07eb8e1fb8a6 100644
> --- a/drivers/cxl/core/Makefile
> +++ b/drivers/cxl/core/Makefile
> @@ -6,3 +6,4 @@ cxl_core-y := bus.o
>  cxl_core-y += pmem.o
>  cxl_core-y += regs.o
>  cxl_core-y += memdev.o
> +cxl_core-y += mbox.o
> diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> index 37b87adaa33f..8073354ba232 100644
> --- a/drivers/cxl/core/bus.c
> +++ b/drivers/cxl/core/bus.c
> @@ -636,6 +636,8 @@ static __init int cxl_core_init(void)
>  {
>  	int rc;
>  
> +	cxl_mbox_init();
> +
>  	rc = cxl_memdev_init();
>  	if (rc)
>  		return rc;
> @@ -647,6 +649,7 @@ static __init int cxl_core_init(void)
>  
>  err:
>  	cxl_memdev_exit();
> +	cxl_mbox_exit();
>  	return rc;
>  }
>  
> @@ -654,6 +657,7 @@ static void cxl_core_exit(void)
>  {
>  	bus_unregister(&cxl_bus_type);
>  	cxl_memdev_exit();
> +	cxl_mbox_exit();
>  }
>  
>  module_init(cxl_core_init);
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index 036a3c8106b4..c85b7fbad02d 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
> @@ -14,7 +14,15 @@ static inline void unregister_cxl_dev(void *dev)
>  	device_unregister(dev);
>  }
>  
> +struct cxl_send_command;
> +struct cxl_mem_query_commands;
> +int cxl_query_cmd(struct cxl_memdev *cxlmd,
> +		  struct cxl_mem_query_commands __user *q);
> +int cxl_send_cmd(struct cxl_memdev *cxlmd, struct cxl_send_command __user *s);
> +
>  int cxl_memdev_init(void);
>  void cxl_memdev_exit(void);
> +void cxl_mbox_init(void);
> +void cxl_mbox_exit(void);

cxl_mbox_fini()?

>  
>  #endif /* __CXL_CORE_H__ */
> diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
> new file mode 100644
> index 000000000000..31e183991726
> --- /dev/null
> +++ b/drivers/cxl/core/mbox.c
> @@ -0,0 +1,825 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright(c) 2020 Intel Corporation. All rights reserved. */
> +#include <linux/io-64-nonatomic-lo-hi.h>
> +#include <linux/security.h>
> +#include <linux/debugfs.h>
> +#include <linux/mutex.h>
> +#include <cxlmem.h>
> +#include <cxl.h>
> +
> +#include "core.h"
> +
> +static bool cxl_raw_allow_all;
> +
> +/**
> + * DOC: cxl mbox
> + *
> + * Core implementation of the CXL 2.0 Type-3 Memory Device Mailbox. The
> + * implementation is used by the cxl_pci driver to initialize the device
> + * and implement the cxl_mem.h IOCTL UAPI. It also implements the
> + * backend of the cxl_pmem_ctl() transport for LIBNVDIMM.
> + */
> +
> +#define cxl_for_each_cmd(cmd)                                                  \
> +	for ((cmd) = &cxl_mem_commands[0];                                     \
> +	     ((cmd) - cxl_mem_commands) < ARRAY_SIZE(cxl_mem_commands); (cmd)++)
> +
> +#define CXL_CMD(_id, sin, sout, _flags)                                        \
> +	[CXL_MEM_COMMAND_ID_##_id] = {                                         \
> +	.info =	{                                                              \
> +			.id = CXL_MEM_COMMAND_ID_##_id,                        \
> +			.size_in = sin,                                        \
> +			.size_out = sout,                                      \
> +		},                                                             \
> +	.opcode = CXL_MBOX_OP_##_id,                                           \
> +	.flags = _flags,                                                       \
> +	}
> +
> +/*
> + * This table defines the supported mailbox commands for the driver. This table
> + * is made up of a UAPI structure. Non-negative values as parameters in the
> + * table will be validated against the user's input. For example, if size_in is
> + * 0, and the user passed in 1, it is an error.
> + */
> +static struct cxl_mem_command cxl_mem_commands[CXL_MEM_COMMAND_ID_MAX] = {
> +	CXL_CMD(IDENTIFY, 0, 0x43, CXL_CMD_FLAG_FORCE_ENABLE),
> +#ifdef CONFIG_CXL_MEM_RAW_COMMANDS
> +	CXL_CMD(RAW, ~0, ~0, 0),
> +#endif
> +	CXL_CMD(GET_SUPPORTED_LOGS, 0, ~0, CXL_CMD_FLAG_FORCE_ENABLE),
> +	CXL_CMD(GET_FW_INFO, 0, 0x50, 0),
> +	CXL_CMD(GET_PARTITION_INFO, 0, 0x20, 0),
> +	CXL_CMD(GET_LSA, 0x8, ~0, 0),
> +	CXL_CMD(GET_HEALTH_INFO, 0, 0x12, 0),
> +	CXL_CMD(GET_LOG, 0x18, ~0, CXL_CMD_FLAG_FORCE_ENABLE),
> +	CXL_CMD(SET_PARTITION_INFO, 0x0a, 0, 0),
> +	CXL_CMD(SET_LSA, ~0, 0, 0),
> +	CXL_CMD(GET_ALERT_CONFIG, 0, 0x10, 0),
> +	CXL_CMD(SET_ALERT_CONFIG, 0xc, 0, 0),
> +	CXL_CMD(GET_SHUTDOWN_STATE, 0, 0x1, 0),
> +	CXL_CMD(SET_SHUTDOWN_STATE, 0x1, 0, 0),
> +	CXL_CMD(GET_POISON, 0x10, ~0, 0),
> +	CXL_CMD(INJECT_POISON, 0x8, 0, 0),
> +	CXL_CMD(CLEAR_POISON, 0x48, 0, 0),
> +	CXL_CMD(GET_SCAN_MEDIA_CAPS, 0x10, 0x4, 0),
> +	CXL_CMD(SCAN_MEDIA, 0x11, 0, 0),
> +	CXL_CMD(GET_SCAN_MEDIA, 0, ~0, 0),
> +};
> +
> +/*
> + * Commands that RAW doesn't permit. The rationale for each:
> + *
> + * CXL_MBOX_OP_ACTIVATE_FW: Firmware activation requires adjustment /
> + * coordination of transaction timeout values at the root bridge level.
> + *
> + * CXL_MBOX_OP_SET_PARTITION_INFO: The device memory map may change live
> + * and needs to be coordinated with HDM updates.
> + *
> + * CXL_MBOX_OP_SET_LSA: The label storage area may be cached by the
> + * driver and any writes from userspace invalidates those contents.
> + *
> + * CXL_MBOX_OP_SET_SHUTDOWN_STATE: Set shutdown state assumes no writes
> + * to the device after it is marked clean, userspace can not make that
> + * assertion.
> + *
> + * CXL_MBOX_OP_[GET_]SCAN_MEDIA: The kernel provides a native error list that
> + * is kept up to date with patrol notifications and error management.
> + */
> +static u16 cxl_disabled_raw_commands[] = {
> +	CXL_MBOX_OP_ACTIVATE_FW,
> +	CXL_MBOX_OP_SET_PARTITION_INFO,
> +	CXL_MBOX_OP_SET_LSA,
> +	CXL_MBOX_OP_SET_SHUTDOWN_STATE,
> +	CXL_MBOX_OP_SCAN_MEDIA,
> +	CXL_MBOX_OP_GET_SCAN_MEDIA,
> +};
> +
> +/*
> + * Command sets that RAW doesn't permit. All opcodes in this set are
> + * disabled because they pass plain text security payloads over the
> + * user/kernel boundary. This functionality is intended to be wrapped
> + * behind the keys ABI which allows for encrypted payloads in the UAPI
> + */
> +static u8 security_command_sets[] = {
> +	0x44, /* Sanitize */
> +	0x45, /* Persistent Memory Data-at-rest Security */
> +	0x46, /* Security Passthrough */
> +};
> +
> +static bool cxl_is_security_command(u16 opcode)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(security_command_sets); i++)
> +		if (security_command_sets[i] == (opcode >> 8))
> +			return true;
> +	return false;
> +}
> +
> +static struct cxl_mem_command *cxl_mem_find_command(u16 opcode)
> +{
> +	struct cxl_mem_command *c;
> +
> +	cxl_for_each_cmd(c)
> +		if (c->opcode == opcode)
> +			return c;
> +
> +	return NULL;
> +}
> +
> +/**
> + * cxl_mem_mbox_send_cmd() - Send a mailbox command to a memory device.
> + * @cxlm: The CXL memory device to communicate with.
> + * @opcode: Opcode for the mailbox command.
> + * @in: The input payload for the mailbox command.
> + * @in_size: The length of the input payload
> + * @out: Caller allocated buffer for the output.
> + * @out_size: Expected size of output.
> + *
> + * Context: Any context. Will acquire and release mbox_mutex.
> + * Return:
> + *  * %>=0	- Number of bytes returned in @out.
> + *  * %-E2BIG	- Payload is too large for hardware.
> + *  * %-EBUSY	- Couldn't acquire exclusive mailbox access.
> + *  * %-EFAULT	- Hardware error occurred.
> + *  * %-ENXIO	- Command completed, but device reported an error.
> + *  * %-EIO	- Unexpected output size.
> + *
> + * Mailbox commands may execute successfully yet the device itself reported an
> + * error. While this distinction can be useful for commands from userspace, the
> + * kernel will only be able to use results when both are successful.
> + *
> + * See __cxl_mem_mbox_send_cmd()
> + */
> +int cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm, u16 opcode, void *in,
> +			  size_t in_size, void *out, size_t out_size)
> +{
> +	const struct cxl_mem_command *cmd = cxl_mem_find_command(opcode);
> +	struct cxl_mbox_cmd mbox_cmd = {
> +		.opcode = opcode,
> +		.payload_in = in,
> +		.size_in = in_size,
> +		.size_out = out_size,
> +		.payload_out = out,
> +	};
> +	int rc;
> +
> +	if (out_size > cxlm->payload_size)
> +		return -E2BIG;
> +
> +	rc = cxlm->mbox_send(cxlm, &mbox_cmd);
> +	if (rc)
> +		return rc;
> +
> +	/* TODO: Map return code to proper kernel style errno */
> +	if (mbox_cmd.return_code != CXL_MBOX_SUCCESS)
> +		return -ENXIO;
> +
> +	/*
> +	 * Variable sized commands can't be validated and so it's up to the
> +	 * caller to do that if they wish.
> +	 */
> +	if (cmd->info.size_out >= 0 && mbox_cmd.size_out != out_size)
> +		return -EIO;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(cxl_mem_mbox_send_cmd);
> +
> +static bool cxl_mem_raw_command_allowed(u16 opcode)
> +{
> +	int i;
> +
> +	if (!IS_ENABLED(CONFIG_CXL_MEM_RAW_COMMANDS))
> +		return false;
> +
> +	if (security_locked_down(LOCKDOWN_PCI_ACCESS))
> +		return false;
> +
> +	if (cxl_raw_allow_all)
> +		return true;
> +
> +	if (cxl_is_security_command(opcode))
> +		return false;
> +
> +	for (i = 0; i < ARRAY_SIZE(cxl_disabled_raw_commands); i++)
> +		if (cxl_disabled_raw_commands[i] == opcode)
> +			return false;
> +
> +	return true;
> +}
> +
> +/**
> + * cxl_validate_cmd_from_user() - Check fields for CXL_MEM_SEND_COMMAND.
> + * @cxlm: &struct cxl_mem device whose mailbox will be used.
> + * @send_cmd: &struct cxl_send_command copied in from userspace.
> + * @out_cmd: Sanitized and populated &struct cxl_mem_command.
> + *
> + * Return:
> + *  * %0	- @out_cmd is ready to send.
> + *  * %-ENOTTY	- Invalid command specified.
> + *  * %-EINVAL	- Reserved fields or invalid values were used.
> + *  * %-ENOMEM	- Input or output buffer wasn't sized properly.
> + *  * %-EPERM	- Attempted to use a protected command.
> + *
> + * The result of this command is a fully validated command in @out_cmd that is
> + * safe to send to the hardware.
> + *
> + * See handle_mailbox_cmd_from_user()
> + */
> +static int cxl_validate_cmd_from_user(struct cxl_mem *cxlm,
> +				      const struct cxl_send_command *send_cmd,
> +				      struct cxl_mem_command *out_cmd)
> +{
> +	const struct cxl_command_info *info;
> +	struct cxl_mem_command *c;
> +
> +	if (send_cmd->id == 0 || send_cmd->id >= CXL_MEM_COMMAND_ID_MAX)
> +		return -ENOTTY;
> +
> +	/*
> +	 * The user can never specify an input payload larger than what hardware
> +	 * supports, but output can be arbitrarily large (simply write out as
> +	 * much data as the hardware provides).
> +	 */
> +	if (send_cmd->in.size > cxlm->payload_size)
> +		return -EINVAL;
> +
> +	/*
> +	 * Checks are bypassed for raw commands but a WARN/taint will occur
> +	 * later in the callchain
> +	 */
> +	if (send_cmd->id == CXL_MEM_COMMAND_ID_RAW) {
> +		const struct cxl_mem_command temp = {
> +			.info = {
> +				.id = CXL_MEM_COMMAND_ID_RAW,
> +				.flags = 0,
> +				.size_in = send_cmd->in.size,
> +				.size_out = send_cmd->out.size,
> +			},
> +			.opcode = send_cmd->raw.opcode
> +		};
> +
> +		if (send_cmd->raw.rsvd)
> +			return -EINVAL;
> +
> +		/*
> +		 * Unlike supported commands, the output size of RAW commands
> +		 * gets passed along without further checking, so it must be
> +		 * validated here.
> +		 */
> +		if (send_cmd->out.size > cxlm->payload_size)
> +			return -EINVAL;
> +
> +		if (!cxl_mem_raw_command_allowed(send_cmd->raw.opcode))
> +			return -EPERM;
> +
> +		memcpy(out_cmd, &temp, sizeof(temp));
> +
> +		return 0;
> +	}
> +
> +	if (send_cmd->flags & ~CXL_MEM_COMMAND_FLAG_MASK)
> +		return -EINVAL;
> +
> +	if (send_cmd->rsvd)
> +		return -EINVAL;
> +
> +	if (send_cmd->in.rsvd || send_cmd->out.rsvd)
> +		return -EINVAL;
> +
> +	/* Convert user's command into the internal representation */
> +	c = &cxl_mem_commands[send_cmd->id];
> +	info = &c->info;
> +
> +	/* Check that the command is enabled for hardware */
> +	if (!test_bit(info->id, cxlm->enabled_cmds))
> +		return -ENOTTY;
> +
> +	/* Check the input buffer is the expected size */
> +	if (info->size_in >= 0 && info->size_in != send_cmd->in.size)
> +		return -ENOMEM;
> +
> +	/* Check the output buffer is at least large enough */
> +	if (info->size_out >= 0 && send_cmd->out.size < info->size_out)
> +		return -ENOMEM;
> +
> +	memcpy(out_cmd, c, sizeof(*c));
> +	out_cmd->info.size_in = send_cmd->in.size;
> +	/*
> +	 * XXX: out_cmd->info.size_out will be controlled by the driver, and the
> +	 * specified number of bytes @send_cmd->out.size will be copied back out
> +	 * to userspace.
> +	 */
> +
> +	return 0;
> +}
> +
> +#define cxl_cmd_count ARRAY_SIZE(cxl_mem_commands)
> +
> +int cxl_query_cmd(struct cxl_memdev *cxlmd,
> +		  struct cxl_mem_query_commands __user *q)
> +{
> +	struct device *dev = &cxlmd->dev;
> +	struct cxl_mem_command *cmd;
> +	u32 n_commands;
> +	int j = 0;
> +
> +	dev_dbg(dev, "Query IOCTL\n");
> +
> +	if (get_user(n_commands, &q->n_commands))
> +		return -EFAULT;
> +
> +	/* returns the total number if 0 elements are requested. */
> +	if (n_commands == 0)
> +		return put_user(cxl_cmd_count, &q->n_commands);
> +
> +	/*
> +	 * otherwise, return max(n_commands, total commands) cxl_command_info
> +	 * structures.
> +	 */
> +	cxl_for_each_cmd(cmd) {
> +		const struct cxl_command_info *info = &cmd->info;
> +
> +		if (copy_to_user(&q->commands[j++], info, sizeof(*info)))
> +			return -EFAULT;
> +
> +		if (j == n_commands)
> +			break;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * handle_mailbox_cmd_from_user() - Dispatch a mailbox command for userspace.
> + * @cxlm: The CXL memory device to communicate with.
> + * @cmd: The validated command.
> + * @in_payload: Pointer to userspace's input payload.
> + * @out_payload: Pointer to userspace's output payload.
> + * @size_out: (Input) Max payload size to copy out.
> + *            (Output) Payload size hardware generated.
> + * @retval: Hardware generated return code from the operation.
> + *
> + * Return:
> + *  * %0	- Mailbox transaction succeeded. This implies the mailbox
> + *		  protocol completed successfully not that the operation itself
> + *		  was successful.
> + *  * %-ENOMEM  - Couldn't allocate a bounce buffer.
> + *  * %-EFAULT	- Something happened with copy_to/from_user.
> + *  * %-EINTR	- Mailbox acquisition interrupted.
> + *  * %-EXXX	- Transaction level failures.
> + *
> + * Creates the appropriate mailbox command and dispatches it on behalf of a
> + * userspace request. The input and output payloads are copied between
> + * userspace.
> + *
> + * See cxl_send_cmd().
> + */
> +static int handle_mailbox_cmd_from_user(struct cxl_mem *cxlm,
> +					const struct cxl_mem_command *cmd,
> +					u64 in_payload, u64 out_payload,
> +					s32 *size_out, u32 *retval)
> +{
> +	struct device *dev = cxlm->dev;
> +	struct cxl_mbox_cmd mbox_cmd = {
> +		.opcode = cmd->opcode,
> +		.size_in = cmd->info.size_in,
> +		.size_out = cmd->info.size_out,
> +	};
> +	int rc;
> +
> +	if (cmd->info.size_out) {
> +		mbox_cmd.payload_out = kvzalloc(cmd->info.size_out, GFP_KERNEL);
> +		if (!mbox_cmd.payload_out)
> +			return -ENOMEM;
> +	}
> +
> +	if (cmd->info.size_in) {
> +		mbox_cmd.payload_in = vmemdup_user(u64_to_user_ptr(in_payload),
> +						   cmd->info.size_in);
> +		if (IS_ERR(mbox_cmd.payload_in)) {
> +			kvfree(mbox_cmd.payload_out);
> +			return PTR_ERR(mbox_cmd.payload_in);
> +		}
> +	}
> +
> +	dev_dbg(dev,
> +		"Submitting %s command for user\n"
> +		"\topcode: %x\n"
> +		"\tsize: %ub\n",
> +		cxl_command_names[cmd->info.id].name, mbox_cmd.opcode,
> +		cmd->info.size_in);
> +
> +	dev_WARN_ONCE(dev, cmd->info.id == CXL_MEM_COMMAND_ID_RAW,
> +		      "raw command path used\n");
> +
> +	rc = cxlm->mbox_send(cxlm, &mbox_cmd);
> +	if (rc)
> +		goto out;
> +
> +	/*
> +	 * @size_out contains the max size that's allowed to be written back out
> +	 * to userspace. While the payload may have written more output than
> +	 * this it will have to be ignored.
> +	 */
> +	if (mbox_cmd.size_out) {
> +		dev_WARN_ONCE(dev, mbox_cmd.size_out > *size_out,
> +			      "Invalid return size\n");
> +		if (copy_to_user(u64_to_user_ptr(out_payload),
> +				 mbox_cmd.payload_out, mbox_cmd.size_out)) {
> +			rc = -EFAULT;
> +			goto out;
> +		}
> +	}
> +
> +	*size_out = mbox_cmd.size_out;
> +	*retval = mbox_cmd.return_code;
> +
> +out:
> +	kvfree(mbox_cmd.payload_in);
> +	kvfree(mbox_cmd.payload_out);
> +	return rc;
> +}
> +
> +int cxl_send_cmd(struct cxl_memdev *cxlmd, struct cxl_send_command __user *s)
> +{
> +	struct cxl_mem *cxlm = cxlmd->cxlm;
> +	struct device *dev = &cxlmd->dev;
> +	struct cxl_send_command send;
> +	struct cxl_mem_command c;
> +	int rc;
> +
> +	dev_dbg(dev, "Send IOCTL\n");
> +
> +	if (copy_from_user(&send, s, sizeof(send)))
> +		return -EFAULT;
> +
> +	rc = cxl_validate_cmd_from_user(cxlmd->cxlm, &send, &c);
> +	if (rc)
> +		return rc;
> +
> +	/* Prepare to handle a full payload for variable sized output */
> +	if (c.info.size_out < 0)
> +		c.info.size_out = cxlm->payload_size;
> +
> +	rc = handle_mailbox_cmd_from_user(cxlm, &c, send.in.payload,
> +					  send.out.payload, &send.out.size,
> +					  &send.retval);
> +	if (rc)
> +		return rc;
> +
> +	if (copy_to_user(s, &send, sizeof(send)))
> +		return -EFAULT;
> +
> +	return 0;
> +}
> +
> +static int cxl_xfer_log(struct cxl_mem *cxlm, uuid_t *uuid, u32 size, u8 *out)
> +{
> +	u32 remaining = size;
> +	u32 offset = 0;
> +
> +	while (remaining) {
> +		u32 xfer_size = min_t(u32, remaining, cxlm->payload_size);
> +		struct cxl_mbox_get_log {
> +			uuid_t uuid;
> +			__le32 offset;
> +			__le32 length;
> +		} __packed log = {
> +			.uuid = *uuid,
> +			.offset = cpu_to_le32(offset),
> +			.length = cpu_to_le32(xfer_size)
> +		};
> +		int rc;
> +
> +		rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_GET_LOG, &log,
> +					   sizeof(log), out, xfer_size);
> +		if (rc < 0)
> +			return rc;
> +
> +		out += xfer_size;
> +		remaining -= xfer_size;
> +		offset += xfer_size;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * cxl_walk_cel() - Walk through the Command Effects Log.
> + * @cxlm: Device.
> + * @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_mem *cxlm, size_t size, u8 *cel)
> +{
> +	struct cel_entry {
> +		__le16 opcode;
> +		__le16 effect;
> +	} __packed * cel_entry;
> +	const int cel_entries = size / sizeof(*cel_entry);
> +	int i;
> +
> +	cel_entry = (struct cel_entry *)cel;
> +
> +	for (i = 0; i < cel_entries; i++) {
> +		u16 opcode = le16_to_cpu(cel_entry[i].opcode);
> +		struct cxl_mem_command *cmd = cxl_mem_find_command(opcode);
> +
> +		if (!cmd) {
> +			dev_dbg(cxlm->dev,
> +				"Opcode 0x%04x unsupported by driver", opcode);
> +			continue;
> +		}
> +
> +		set_bit(cmd->info.id, cxlm->enabled_cmds);
> +	}
> +}
> +
> +struct cxl_mbox_get_supported_logs {
> +	__le16 entries;
> +	u8 rsvd[6];
> +	struct gsl_entry {
> +		uuid_t uuid;
> +		__le32 size;
> +	} __packed entry[];
> +} __packed;
> +
> +static struct cxl_mbox_get_supported_logs *cxl_get_gsl(struct cxl_mem *cxlm)
> +{
> +	struct cxl_mbox_get_supported_logs *ret;
> +	int rc;
> +
> +	ret = kvmalloc(cxlm->payload_size, GFP_KERNEL);
> +	if (!ret)
> +		return ERR_PTR(-ENOMEM);
> +
> +	rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_GET_SUPPORTED_LOGS, NULL,
> +				   0, ret, cxlm->payload_size);
> +	if (rc < 0) {
> +		kvfree(ret);
> +		return ERR_PTR(rc);
> +	}
> +
> +	return ret;
> +}
> +
> +enum {
> +	CEL_UUID,
> +	VENDOR_DEBUG_UUID,
> +};
> +
> +/* See CXL 2.0 Table 170. Get Log Input Payload */
> +static const uuid_t log_uuid[] = {
> +	[CEL_UUID] = UUID_INIT(0xda9c0b5, 0xbf41, 0x4b78, 0x8f, 0x79, 0x96,
> +			       0xb1, 0x62, 0x3b, 0x3f, 0x17),
> +	[VENDOR_DEBUG_UUID] = UUID_INIT(0xe1819d9, 0x11a9, 0x400c, 0x81, 0x1f,
> +					0xd6, 0x07, 0x19, 0x40, 0x3d, 0x86),
> +};
> +
> +/**
> + * cxl_mem_enumerate_cmds() - Enumerate commands for a device.
> + * @cxlm: The device.
> + *
> + * 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 @cxlm.
> + */
> +int cxl_mem_enumerate_cmds(struct cxl_mem *cxlm)
> +{
> +	struct cxl_mbox_get_supported_logs *gsl;
> +	struct device *dev = cxlm->dev;
> +	struct cxl_mem_command *cmd;
> +	int i, rc;
> +
> +	gsl = cxl_get_gsl(cxlm);
> +	if (IS_ERR(gsl))
> +		return PTR_ERR(gsl);
> +
> +	rc = -ENOENT;
> +	for (i = 0; i < le16_to_cpu(gsl->entries); i++) {
> +		u32 size = le32_to_cpu(gsl->entry[i].size);
> +		uuid_t uuid = gsl->entry[i].uuid;
> +		u8 *log;
> +
> +		dev_dbg(dev, "Found LOG type %pU of size %d", &uuid, size);
> +
> +		if (!uuid_equal(&uuid, &log_uuid[CEL_UUID]))
> +			continue;
> +
> +		log = kvmalloc(size, GFP_KERNEL);
> +		if (!log) {
> +			rc = -ENOMEM;
> +			goto out;
> +		}
> +
> +		rc = cxl_xfer_log(cxlm, &uuid, size, log);
> +		if (rc) {
> +			kvfree(log);
> +			goto out;
> +		}
> +
> +		cxl_walk_cel(cxlm, 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, cxlm->enabled_cmds);
> +
> +		/* Found the required CEL */
> +		rc = 0;
> +	}
> +
> +out:
> +	kvfree(gsl);
> +	return rc;
> +}
> +EXPORT_SYMBOL_GPL(cxl_mem_enumerate_cmds);
> +
> +/**
> + * cxl_mem_get_partition_info - Get partition info
> + * @cxlm: cxl_mem instance to update partition info
> + *
> + * 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
> + * the pending values, in bytes, which take affect on next cold reset.
> + *
> + * Return: 0 if no error: or the result of the mailbox command.
> + *
> + * See CXL @8.2.9.5.2.1 Get Partition Info
> + */
> +static int cxl_mem_get_partition_info(struct cxl_mem *cxlm)
> +{
> +	struct cxl_mbox_get_partition_info {
> +		__le64 active_volatile_cap;
> +		__le64 active_persistent_cap;
> +		__le64 next_volatile_cap;
> +		__le64 next_persistent_cap;
> +	} __packed pi;
> +	int rc;
> +
> +	rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_GET_PARTITION_INFO,
> +				   NULL, 0, &pi, sizeof(pi));
> +
> +	if (rc)
> +		return rc;
> +
> +	cxlm->active_volatile_bytes =
> +		le64_to_cpu(pi.active_volatile_cap) * CXL_CAPACITY_MULTIPLIER;
> +	cxlm->active_persistent_bytes =
> +		le64_to_cpu(pi.active_persistent_cap) * CXL_CAPACITY_MULTIPLIER;
> +	cxlm->next_volatile_bytes =
> +		le64_to_cpu(pi.next_volatile_cap) * CXL_CAPACITY_MULTIPLIER;
> +	cxlm->next_persistent_bytes =
> +		le64_to_cpu(pi.next_volatile_cap) * CXL_CAPACITY_MULTIPLIER;
> +
> +	return 0;
> +}
> +
> +/**
> + * cxl_mem_identify() - Send the IDENTIFY command to the device.
> + * @cxlm: The device to identify.
> + *
> + * Return: 0 if identify was executed successfully.
> + *
> + * This will dispatch the identify command to the device and on success populate
> + * structures to be exported to sysfs.
> + */
> +int cxl_mem_identify(struct cxl_mem *cxlm)
> +{
> +	/* See CXL 2.0 Table 175 Identify Memory Device Output Payload */
> +	struct cxl_mbox_identify {
> +		char fw_revision[0x10];
> +		__le64 total_capacity;
> +		__le64 volatile_capacity;
> +		__le64 persistent_capacity;
> +		__le64 partition_align;
> +		__le16 info_event_log_size;
> +		__le16 warning_event_log_size;
> +		__le16 failure_event_log_size;
> +		__le16 fatal_event_log_size;
> +		__le32 lsa_size;
> +		u8 poison_list_max_mer[3];
> +		__le16 inject_poison_limit;
> +		u8 poison_caps;
> +		u8 qos_telemetry_caps;
> +	} __packed id;
> +	int rc;
> +
> +	rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_IDENTIFY, NULL, 0, &id,
> +				   sizeof(id));
> +	if (rc < 0)
> +		return rc;
> +
> +	cxlm->total_bytes =
> +		le64_to_cpu(id.total_capacity) * CXL_CAPACITY_MULTIPLIER;
> +	cxlm->volatile_only_bytes =
> +		le64_to_cpu(id.volatile_capacity) * CXL_CAPACITY_MULTIPLIER;
> +	cxlm->persistent_only_bytes =
> +		le64_to_cpu(id.persistent_capacity) * CXL_CAPACITY_MULTIPLIER;
> +	cxlm->partition_align_bytes =
> +		le64_to_cpu(id.partition_align) * CXL_CAPACITY_MULTIPLIER;
> +
> +	dev_dbg(cxlm->dev,
> +		"Identify Memory Device\n"
> +		"     total_bytes = %#llx\n"
> +		"     volatile_only_bytes = %#llx\n"
> +		"     persistent_only_bytes = %#llx\n"
> +		"     partition_align_bytes = %#llx\n",
> +		cxlm->total_bytes, cxlm->volatile_only_bytes,
> +		cxlm->persistent_only_bytes, cxlm->partition_align_bytes);
> +
> +	cxlm->lsa_size = le32_to_cpu(id.lsa_size);
> +	memcpy(cxlm->firmware_version, id.fw_revision, sizeof(id.fw_revision));
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(cxl_mem_identify);
> +
> +int cxl_mem_create_range_info(struct cxl_mem *cxlm)
> +{
> +	int rc;
> +
> +	if (cxlm->partition_align_bytes == 0) {
> +		cxlm->ram_range.start = 0;
> +		cxlm->ram_range.end = cxlm->volatile_only_bytes - 1;
> +		cxlm->pmem_range.start = cxlm->volatile_only_bytes;
> +		cxlm->pmem_range.end = cxlm->volatile_only_bytes +
> +				       cxlm->persistent_only_bytes - 1;
> +		return 0;
> +	}
> +
> +	rc = cxl_mem_get_partition_info(cxlm);
> +	if (rc) {
> +		dev_err(cxlm->dev, "Failed to query partition information\n");
> +		return rc;
> +	}
> +
> +	dev_dbg(cxlm->dev,
> +		"Get Partition Info\n"
> +		"     active_volatile_bytes = %#llx\n"
> +		"     active_persistent_bytes = %#llx\n"
> +		"     next_volatile_bytes = %#llx\n"
> +		"     next_persistent_bytes = %#llx\n",
> +		cxlm->active_volatile_bytes, cxlm->active_persistent_bytes,
> +		cxlm->next_volatile_bytes, cxlm->next_persistent_bytes);
> +
> +	cxlm->ram_range.start = 0;
> +	cxlm->ram_range.end = cxlm->active_volatile_bytes - 1;
> +
> +	cxlm->pmem_range.start = cxlm->active_volatile_bytes;
> +	cxlm->pmem_range.end =
> +		cxlm->active_volatile_bytes + cxlm->active_persistent_bytes - 1;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(cxl_mem_create_range_info);
> +
> +struct cxl_mem *cxl_mem_create(struct device *dev)
> +{
> +	struct cxl_mem *cxlm;
> +
> +	cxlm = devm_kzalloc(dev, sizeof(*cxlm), GFP_KERNEL);
> +	if (!cxlm) {
> +		dev_err(dev, "No memory available\n");
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	mutex_init(&cxlm->mbox_mutex);
> +	cxlm->dev = dev;
> +	cxlm->enabled_cmds =
> +		devm_kmalloc_array(dev, BITS_TO_LONGS(cxl_cmd_count),
> +				   sizeof(unsigned long),
> +				   GFP_KERNEL | __GFP_ZERO);
> +	if (!cxlm->enabled_cmds) {
> +		dev_err(dev, "No memory available for bitmap\n");
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	return cxlm;
> +}
> +EXPORT_SYMBOL_GPL(cxl_mem_create);
> +
> +static struct dentry *cxl_debugfs;
> +
> +void __init cxl_mbox_init(void)
> +{
> +	struct dentry *mbox_debugfs;
> +
> +	cxl_debugfs = debugfs_create_dir("cxl", NULL);
> +	mbox_debugfs = debugfs_create_dir("mbox", cxl_debugfs);
> +	debugfs_create_bool("raw_allow_all", 0600, mbox_debugfs,
> +			    &cxl_raw_allow_all);
> +}
> +
> +void cxl_mbox_exit(void)
> +{
> +	debugfs_remove_recursive(cxl_debugfs);
> +}
> diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> index 331ef7d6c598..df2ba87238c2 100644
> --- a/drivers/cxl/core/memdev.c
> +++ b/drivers/cxl/core/memdev.c
> @@ -8,6 +8,8 @@
>  #include <cxlmem.h>
>  #include "core.h"
>  
> +static DECLARE_RWSEM(cxl_memdev_rwsem);
> +
>  /*
>   * An entire PCI topology full of devices should be enough for any
>   * config
> @@ -132,16 +134,21 @@ static const struct device_type cxl_memdev_type = {
>  	.groups = cxl_memdev_attribute_groups,
>  };
>  
> +static void cxl_memdev_shutdown(struct device *dev)
> +{
> +	struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
> +
> +	down_write(&cxl_memdev_rwsem);
> +	cxlmd->cxlm = NULL;
> +	up_write(&cxl_memdev_rwsem);
> +}
> +
>  static void cxl_memdev_unregister(void *_cxlmd)
>  {
>  	struct cxl_memdev *cxlmd = _cxlmd;
>  	struct device *dev = &cxlmd->dev;
> -	struct cdev *cdev = &cxlmd->cdev;
> -	const struct cdevm_file_operations *cdevm_fops;
> -
> -	cdevm_fops = container_of(cdev->ops, typeof(*cdevm_fops), fops);
> -	cdevm_fops->shutdown(dev);
>  
> +	cxl_memdev_shutdown(dev);
>  	cdev_device_del(&cxlmd->cdev, dev);
>  	put_device(dev);
>  }
> @@ -180,16 +187,72 @@ static struct cxl_memdev *cxl_memdev_alloc(struct cxl_mem *cxlm,
>  	return ERR_PTR(rc);
>  }
>  
> +static long __cxl_memdev_ioctl(struct cxl_memdev *cxlmd, unsigned int cmd,
> +			       unsigned long arg)
> +{
> +	switch (cmd) {
> +	case CXL_MEM_QUERY_COMMANDS:
> +		return cxl_query_cmd(cxlmd, (void __user *)arg);
> +	case CXL_MEM_SEND_COMMAND:
> +		return cxl_send_cmd(cxlmd, (void __user *)arg);
> +	default:
> +		return -ENOTTY;
> +	}
> +}
> +
> +static long cxl_memdev_ioctl(struct file *file, unsigned int cmd,
> +			     unsigned long arg)
> +{
> +	struct cxl_memdev *cxlmd = file->private_data;
> +	int rc = -ENXIO;
> +
> +	down_read(&cxl_memdev_rwsem);
> +	if (cxlmd->cxlm)
> +		rc = __cxl_memdev_ioctl(cxlmd, cmd, arg);
> +	up_read(&cxl_memdev_rwsem);
> +
> +	return rc;
> +}
> +
> +static int cxl_memdev_open(struct inode *inode, struct file *file)
> +{
> +	struct cxl_memdev *cxlmd =
> +		container_of(inode->i_cdev, typeof(*cxlmd), cdev);
> +
> +	get_device(&cxlmd->dev);
> +	file->private_data = cxlmd;
> +
> +	return 0;
> +}
> +
> +static int cxl_memdev_release_file(struct inode *inode, struct file *file)
> +{
> +	struct cxl_memdev *cxlmd =
> +		container_of(inode->i_cdev, typeof(*cxlmd), cdev);
> +
> +	put_device(&cxlmd->dev);
> +
> +	return 0;
> +}
> +
> +static const struct file_operations cxl_memdev_fops = {
> +	.owner = THIS_MODULE,
> +	.unlocked_ioctl = cxl_memdev_ioctl,
> +	.open = cxl_memdev_open,
> +	.release = cxl_memdev_release_file,
> +	.compat_ioctl = compat_ptr_ioctl,
> +	.llseek = noop_llseek,
> +};
> +
>  struct cxl_memdev *
> -devm_cxl_add_memdev(struct cxl_mem *cxlm,
> -		    const struct cdevm_file_operations *cdevm_fops)
> +devm_cxl_add_memdev(struct cxl_mem *cxlm)
>  {
>  	struct cxl_memdev *cxlmd;
>  	struct device *dev;
>  	struct cdev *cdev;
>  	int rc;
>  
> -	cxlmd = cxl_memdev_alloc(cxlm, &cdevm_fops->fops);
> +	cxlmd = cxl_memdev_alloc(cxlm, &cxl_memdev_fops);
>  	if (IS_ERR(cxlmd))
>  		return cxlmd;
>  
> @@ -219,7 +282,7 @@ devm_cxl_add_memdev(struct cxl_mem *cxlm,
>  	 * The cdev was briefly live, shutdown any ioctl operations that
>  	 * saw that state.
>  	 */
> -	cdevm_fops->shutdown(dev);
> +	cxl_memdev_shutdown(dev);
>  	put_device(dev);
>  	return ERR_PTR(rc);
>  }
> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> index 9be5e26c5b48..35fd16d12532 100644
> --- a/drivers/cxl/cxlmem.h
> +++ b/drivers/cxl/cxlmem.h
> @@ -2,6 +2,7 @@
>  /* Copyright(c) 2020-2021 Intel Corporation. */
>  #ifndef __CXL_MEM_H__
>  #define __CXL_MEM_H__
> +#include <uapi/linux/cxl_mem.h>
>  #include <linux/cdev.h>
>  #include "cxl.h"
>  
> @@ -28,21 +29,6 @@
>  	(FIELD_GET(CXLMDEV_RESET_NEEDED_MASK, status) !=                       \
>  	 CXLMDEV_RESET_NEEDED_NOT)
>  
> -/**
> - * struct cdevm_file_operations - devm coordinated cdev file operations
> - * @fops: file operations that are synchronized against @shutdown
> - * @shutdown: disconnect driver data
> - *
> - * @shutdown is invoked in the devres release path to disconnect any
> - * driver instance data from @dev. It assumes synchronization with any
> - * fops operation that requires driver data. After @shutdown an
> - * operation may only reference @device data.
> - */
> -struct cdevm_file_operations {
> -	struct file_operations fops;
> -	void (*shutdown)(struct device *dev);
> -};
> -
>  /**
>   * struct cxl_memdev - CXL bus object representing a Type-3 Memory Device
>   * @dev: driver core device object
> @@ -62,9 +48,7 @@ static inline struct cxl_memdev *to_cxl_memdev(struct device *dev)
>  	return container_of(dev, struct cxl_memdev, dev);
>  }
>  
> -struct cxl_memdev *
> -devm_cxl_add_memdev(struct cxl_mem *cxlm,
> -		    const struct cdevm_file_operations *cdevm_fops);
> +struct cxl_memdev *devm_cxl_add_memdev(struct cxl_mem *cxlm);
>  
>  /**
>   * struct cxl_mbox_cmd - A command to be submitted to hardware.
> @@ -158,4 +142,62 @@ struct cxl_mem {
>  
>  	int (*mbox_send)(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd);
>  };
> +
> +enum cxl_opcode {
> +	CXL_MBOX_OP_INVALID		= 0x0000,
> +	CXL_MBOX_OP_RAW			= CXL_MBOX_OP_INVALID,
> +	CXL_MBOX_OP_GET_FW_INFO		= 0x0200,
> +	CXL_MBOX_OP_ACTIVATE_FW		= 0x0202,
> +	CXL_MBOX_OP_GET_SUPPORTED_LOGS	= 0x0400,
> +	CXL_MBOX_OP_GET_LOG		= 0x0401,
> +	CXL_MBOX_OP_IDENTIFY		= 0x4000,
> +	CXL_MBOX_OP_GET_PARTITION_INFO	= 0x4100,
> +	CXL_MBOX_OP_SET_PARTITION_INFO	= 0x4101,
> +	CXL_MBOX_OP_GET_LSA		= 0x4102,
> +	CXL_MBOX_OP_SET_LSA		= 0x4103,
> +	CXL_MBOX_OP_GET_HEALTH_INFO	= 0x4200,
> +	CXL_MBOX_OP_GET_ALERT_CONFIG	= 0x4201,
> +	CXL_MBOX_OP_SET_ALERT_CONFIG	= 0x4202,
> +	CXL_MBOX_OP_GET_SHUTDOWN_STATE	= 0x4203,
> +	CXL_MBOX_OP_SET_SHUTDOWN_STATE	= 0x4204,
> +	CXL_MBOX_OP_GET_POISON		= 0x4300,
> +	CXL_MBOX_OP_INJECT_POISON	= 0x4301,
> +	CXL_MBOX_OP_CLEAR_POISON	= 0x4302,
> +	CXL_MBOX_OP_GET_SCAN_MEDIA_CAPS	= 0x4303,
> +	CXL_MBOX_OP_SCAN_MEDIA		= 0x4304,
> +	CXL_MBOX_OP_GET_SCAN_MEDIA	= 0x4305,
> +	CXL_MBOX_OP_MAX			= 0x10000
> +};
> +
> +/**
> + * struct cxl_mem_command - Driver representation of a memory device command
> + * @info: Command information as it exists for the UAPI
> + * @opcode: The actual bits used for the mailbox protocol
> + * @flags: Set of flags effecting driver behavior.
> + *
> + *  * %CXL_CMD_FLAG_FORCE_ENABLE: In cases of error, commands with this flag
> + *    will be enabled by the driver regardless of what hardware may have
> + *    advertised.
> + *
> + * The cxl_mem_command is the driver's internal representation of commands that
> + * are supported by the driver. Some of these commands may not be supported by
> + * the hardware. The driver will use @info to validate the fields passed in by
> + * the user then submit the @opcode to the hardware.
> + *
> + * See struct cxl_command_info.
> + */
> +struct cxl_mem_command {
> +	struct cxl_command_info info;
> +	enum cxl_opcode opcode;
> +	u32 flags;
> +#define CXL_CMD_FLAG_NONE 0
> +#define CXL_CMD_FLAG_FORCE_ENABLE BIT(0)
> +};
> +
> +int cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm, u16 opcode, void *in,
> +			  size_t in_size, void *out, size_t out_size);
> +int cxl_mem_identify(struct cxl_mem *cxlm);
> +int cxl_mem_enumerate_cmds(struct cxl_mem *cxlm);
> +int cxl_mem_create_range_info(struct cxl_mem *cxlm);
> +struct cxl_mem *cxl_mem_create(struct device *dev);
>  #endif /* __CXL_MEM_H__ */
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index 9d8050fdd69c..c9f2ac134f4d 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -1,16 +1,12 @@
>  // SPDX-License-Identifier: GPL-2.0-only
>  /* Copyright(c) 2020 Intel Corporation. All rights reserved. */
> -#include <uapi/linux/cxl_mem.h>
> -#include <linux/security.h>
> -#include <linux/debugfs.h>
> +#include <linux/io-64-nonatomic-lo-hi.h>
>  #include <linux/module.h>
>  #include <linux/sizes.h>
>  #include <linux/mutex.h>
>  #include <linux/list.h>
> -#include <linux/cdev.h>
>  #include <linux/pci.h>
>  #include <linux/io.h>
> -#include <linux/io-64-nonatomic-lo-hi.h>
>  #include "cxlmem.h"
>  #include "pci.h"
>  #include "cxl.h"
> @@ -37,162 +33,6 @@
>  /* CXL 2.0 - 8.2.8.4 */
>  #define CXL_MAILBOX_TIMEOUT_MS (2 * HZ)
>  
> -enum opcode {
> -	CXL_MBOX_OP_INVALID		= 0x0000,
> -	CXL_MBOX_OP_RAW			= CXL_MBOX_OP_INVALID,
> -	CXL_MBOX_OP_GET_FW_INFO		= 0x0200,
> -	CXL_MBOX_OP_ACTIVATE_FW		= 0x0202,
> -	CXL_MBOX_OP_GET_SUPPORTED_LOGS	= 0x0400,
> -	CXL_MBOX_OP_GET_LOG		= 0x0401,
> -	CXL_MBOX_OP_IDENTIFY		= 0x4000,
> -	CXL_MBOX_OP_GET_PARTITION_INFO	= 0x4100,
> -	CXL_MBOX_OP_SET_PARTITION_INFO	= 0x4101,
> -	CXL_MBOX_OP_GET_LSA		= 0x4102,
> -	CXL_MBOX_OP_SET_LSA		= 0x4103,
> -	CXL_MBOX_OP_GET_HEALTH_INFO	= 0x4200,
> -	CXL_MBOX_OP_GET_ALERT_CONFIG	= 0x4201,
> -	CXL_MBOX_OP_SET_ALERT_CONFIG	= 0x4202,
> -	CXL_MBOX_OP_GET_SHUTDOWN_STATE	= 0x4203,
> -	CXL_MBOX_OP_SET_SHUTDOWN_STATE	= 0x4204,
> -	CXL_MBOX_OP_GET_POISON		= 0x4300,
> -	CXL_MBOX_OP_INJECT_POISON	= 0x4301,
> -	CXL_MBOX_OP_CLEAR_POISON	= 0x4302,
> -	CXL_MBOX_OP_GET_SCAN_MEDIA_CAPS	= 0x4303,
> -	CXL_MBOX_OP_SCAN_MEDIA		= 0x4304,
> -	CXL_MBOX_OP_GET_SCAN_MEDIA	= 0x4305,
> -	CXL_MBOX_OP_MAX			= 0x10000
> -};
> -
> -static DECLARE_RWSEM(cxl_memdev_rwsem);
> -static struct dentry *cxl_debugfs;
> -static bool cxl_raw_allow_all;
> -
> -enum {
> -	CEL_UUID,
> -	VENDOR_DEBUG_UUID,
> -};
> -
> -/* See CXL 2.0 Table 170. Get Log Input Payload */
> -static const uuid_t log_uuid[] = {
> -	[CEL_UUID] = UUID_INIT(0xda9c0b5, 0xbf41, 0x4b78, 0x8f, 0x79, 0x96,
> -			       0xb1, 0x62, 0x3b, 0x3f, 0x17),
> -	[VENDOR_DEBUG_UUID] = UUID_INIT(0xe1819d9, 0x11a9, 0x400c, 0x81, 0x1f,
> -					0xd6, 0x07, 0x19, 0x40, 0x3d, 0x86),
> -};
> -
> -/**
> - * struct cxl_mem_command - Driver representation of a memory device command
> - * @info: Command information as it exists for the UAPI
> - * @opcode: The actual bits used for the mailbox protocol
> - * @flags: Set of flags effecting driver behavior.
> - *
> - *  * %CXL_CMD_FLAG_FORCE_ENABLE: In cases of error, commands with this flag
> - *    will be enabled by the driver regardless of what hardware may have
> - *    advertised.
> - *
> - * The cxl_mem_command is the driver's internal representation of commands that
> - * are supported by the driver. Some of these commands may not be supported by
> - * the hardware. The driver will use @info to validate the fields passed in by
> - * the user then submit the @opcode to the hardware.
> - *
> - * See struct cxl_command_info.
> - */
> -struct cxl_mem_command {
> -	struct cxl_command_info info;
> -	enum opcode opcode;
> -	u32 flags;
> -#define CXL_CMD_FLAG_NONE 0
> -#define CXL_CMD_FLAG_FORCE_ENABLE BIT(0)
> -};
> -
> -#define CXL_CMD(_id, sin, sout, _flags)                                        \
> -	[CXL_MEM_COMMAND_ID_##_id] = {                                         \
> -	.info =	{                                                              \
> -			.id = CXL_MEM_COMMAND_ID_##_id,                        \
> -			.size_in = sin,                                        \
> -			.size_out = sout,                                      \
> -		},                                                             \
> -	.opcode = CXL_MBOX_OP_##_id,                                           \
> -	.flags = _flags,                                                       \
> -	}
> -
> -/*
> - * This table defines the supported mailbox commands for the driver. This table
> - * is made up of a UAPI structure. Non-negative values as parameters in the
> - * table will be validated against the user's input. For example, if size_in is
> - * 0, and the user passed in 1, it is an error.
> - */
> -static struct cxl_mem_command mem_commands[CXL_MEM_COMMAND_ID_MAX] = {
> -	CXL_CMD(IDENTIFY, 0, 0x43, CXL_CMD_FLAG_FORCE_ENABLE),
> -#ifdef CONFIG_CXL_MEM_RAW_COMMANDS
> -	CXL_CMD(RAW, ~0, ~0, 0),
> -#endif
> -	CXL_CMD(GET_SUPPORTED_LOGS, 0, ~0, CXL_CMD_FLAG_FORCE_ENABLE),
> -	CXL_CMD(GET_FW_INFO, 0, 0x50, 0),
> -	CXL_CMD(GET_PARTITION_INFO, 0, 0x20, 0),
> -	CXL_CMD(GET_LSA, 0x8, ~0, 0),
> -	CXL_CMD(GET_HEALTH_INFO, 0, 0x12, 0),
> -	CXL_CMD(GET_LOG, 0x18, ~0, CXL_CMD_FLAG_FORCE_ENABLE),
> -	CXL_CMD(SET_PARTITION_INFO, 0x0a, 0, 0),
> -	CXL_CMD(SET_LSA, ~0, 0, 0),
> -	CXL_CMD(GET_ALERT_CONFIG, 0, 0x10, 0),
> -	CXL_CMD(SET_ALERT_CONFIG, 0xc, 0, 0),
> -	CXL_CMD(GET_SHUTDOWN_STATE, 0, 0x1, 0),
> -	CXL_CMD(SET_SHUTDOWN_STATE, 0x1, 0, 0),
> -	CXL_CMD(GET_POISON, 0x10, ~0, 0),
> -	CXL_CMD(INJECT_POISON, 0x8, 0, 0),
> -	CXL_CMD(CLEAR_POISON, 0x48, 0, 0),
> -	CXL_CMD(GET_SCAN_MEDIA_CAPS, 0x10, 0x4, 0),
> -	CXL_CMD(SCAN_MEDIA, 0x11, 0, 0),
> -	CXL_CMD(GET_SCAN_MEDIA, 0, ~0, 0),
> -};
> -
> -/*
> - * Commands that RAW doesn't permit. The rationale for each:
> - *
> - * CXL_MBOX_OP_ACTIVATE_FW: Firmware activation requires adjustment /
> - * coordination of transaction timeout values at the root bridge level.
> - *
> - * CXL_MBOX_OP_SET_PARTITION_INFO: The device memory map may change live
> - * and needs to be coordinated with HDM updates.
> - *
> - * CXL_MBOX_OP_SET_LSA: The label storage area may be cached by the
> - * driver and any writes from userspace invalidates those contents.
> - *
> - * CXL_MBOX_OP_SET_SHUTDOWN_STATE: Set shutdown state assumes no writes
> - * to the device after it is marked clean, userspace can not make that
> - * assertion.
> - *
> - * CXL_MBOX_OP_[GET_]SCAN_MEDIA: The kernel provides a native error list that
> - * is kept up to date with patrol notifications and error management.
> - */
> -static u16 cxl_disabled_raw_commands[] = {
> -	CXL_MBOX_OP_ACTIVATE_FW,
> -	CXL_MBOX_OP_SET_PARTITION_INFO,
> -	CXL_MBOX_OP_SET_LSA,
> -	CXL_MBOX_OP_SET_SHUTDOWN_STATE,
> -	CXL_MBOX_OP_SCAN_MEDIA,
> -	CXL_MBOX_OP_GET_SCAN_MEDIA,
> -};
> -
> -/*
> - * Command sets that RAW doesn't permit. All opcodes in this set are
> - * disabled because they pass plain text security payloads over the
> - * user/kernel boundary. This functionality is intended to be wrapped
> - * behind the keys ABI which allows for encrypted payloads in the UAPI
> - */
> -static u8 security_command_sets[] = {
> -	0x44, /* Sanitize */
> -	0x45, /* Persistent Memory Data-at-rest Security */
> -	0x46, /* Security Passthrough */
> -};
> -
> -#define cxl_for_each_cmd(cmd)                                                  \
> -	for ((cmd) = &mem_commands[0];                                         \
> -	     ((cmd) - mem_commands) < ARRAY_SIZE(mem_commands); (cmd)++)
> -
> -#define cxl_cmd_count ARRAY_SIZE(mem_commands)
> -
>  static int cxl_mem_wait_for_doorbell(struct cxl_mem *cxlm)
>  {
>  	const unsigned long start = jiffies;
> @@ -215,16 +55,6 @@ static int cxl_mem_wait_for_doorbell(struct cxl_mem *cxlm)
>  	return 0;
>  }
>  
> -static bool cxl_is_security_command(u16 opcode)
> -{
> -	int i;
> -
> -	for (i = 0; i < ARRAY_SIZE(security_command_sets); i++)
> -		if (security_command_sets[i] == (opcode >> 8))
> -			return true;
> -	return false;
> -}
> -
>  static void cxl_mem_mbox_timeout(struct cxl_mem *cxlm,
>  				 struct cxl_mbox_cmd *mbox_cmd)
>  {
> @@ -446,433 +276,6 @@ static int cxl_pci_mbox_send(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd)
>  	return rc;
>  }
>  
> -/**
> - * handle_mailbox_cmd_from_user() - Dispatch a mailbox command for userspace.
> - * @cxlm: The CXL memory device to communicate with.
> - * @cmd: The validated command.
> - * @in_payload: Pointer to userspace's input payload.
> - * @out_payload: Pointer to userspace's output payload.
> - * @size_out: (Input) Max payload size to copy out.
> - *            (Output) Payload size hardware generated.
> - * @retval: Hardware generated return code from the operation.
> - *
> - * Return:
> - *  * %0	- Mailbox transaction succeeded. This implies the mailbox
> - *		  protocol completed successfully not that the operation itself
> - *		  was successful.
> - *  * %-ENOMEM  - Couldn't allocate a bounce buffer.
> - *  * %-EFAULT	- Something happened with copy_to/from_user.
> - *  * %-EINTR	- Mailbox acquisition interrupted.
> - *  * %-EXXX	- Transaction level failures.
> - *
> - * Creates the appropriate mailbox command and dispatches it on behalf of a
> - * userspace request. The input and output payloads are copied between
> - * userspace.
> - *
> - * See cxl_send_cmd().
> - */
> -static int handle_mailbox_cmd_from_user(struct cxl_mem *cxlm,
> -					const struct cxl_mem_command *cmd,
> -					u64 in_payload, u64 out_payload,
> -					s32 *size_out, u32 *retval)
> -{
> -	struct device *dev = cxlm->dev;
> -	struct cxl_mbox_cmd mbox_cmd = {
> -		.opcode = cmd->opcode,
> -		.size_in = cmd->info.size_in,
> -		.size_out = cmd->info.size_out,
> -	};
> -	int rc;
> -
> -	if (cmd->info.size_out) {
> -		mbox_cmd.payload_out = kvzalloc(cmd->info.size_out, GFP_KERNEL);
> -		if (!mbox_cmd.payload_out)
> -			return -ENOMEM;
> -	}
> -
> -	if (cmd->info.size_in) {
> -		mbox_cmd.payload_in = vmemdup_user(u64_to_user_ptr(in_payload),
> -						   cmd->info.size_in);
> -		if (IS_ERR(mbox_cmd.payload_in)) {
> -			kvfree(mbox_cmd.payload_out);
> -			return PTR_ERR(mbox_cmd.payload_in);
> -		}
> -	}
> -
> -	dev_dbg(dev,
> -		"Submitting %s command for user\n"
> -		"\topcode: %x\n"
> -		"\tsize: %ub\n",
> -		cxl_command_names[cmd->info.id].name, mbox_cmd.opcode,
> -		cmd->info.size_in);
> -
> -	dev_WARN_ONCE(dev, cmd->info.id == CXL_MEM_COMMAND_ID_RAW,
> -		      "raw command path used\n");
> -
> -	rc = cxlm->mbox_send(cxlm, &mbox_cmd);
> -	if (rc)
> -		goto out;
> -
> -	/*
> -	 * @size_out contains the max size that's allowed to be written back out
> -	 * to userspace. While the payload may have written more output than
> -	 * this it will have to be ignored.
> -	 */
> -	if (mbox_cmd.size_out) {
> -		dev_WARN_ONCE(dev, mbox_cmd.size_out > *size_out,
> -			      "Invalid return size\n");
> -		if (copy_to_user(u64_to_user_ptr(out_payload),
> -				 mbox_cmd.payload_out, mbox_cmd.size_out)) {
> -			rc = -EFAULT;
> -			goto out;
> -		}
> -	}
> -
> -	*size_out = mbox_cmd.size_out;
> -	*retval = mbox_cmd.return_code;
> -
> -out:
> -	kvfree(mbox_cmd.payload_in);
> -	kvfree(mbox_cmd.payload_out);
> -	return rc;
> -}
> -
> -static bool cxl_mem_raw_command_allowed(u16 opcode)
> -{
> -	int i;
> -
> -	if (!IS_ENABLED(CONFIG_CXL_MEM_RAW_COMMANDS))
> -		return false;
> -
> -	if (security_locked_down(LOCKDOWN_PCI_ACCESS))
> -		return false;
> -
> -	if (cxl_raw_allow_all)
> -		return true;
> -
> -	if (cxl_is_security_command(opcode))
> -		return false;
> -
> -	for (i = 0; i < ARRAY_SIZE(cxl_disabled_raw_commands); i++)
> -		if (cxl_disabled_raw_commands[i] == opcode)
> -			return false;
> -
> -	return true;
> -}
> -
> -/**
> - * cxl_validate_cmd_from_user() - Check fields for CXL_MEM_SEND_COMMAND.
> - * @cxlm: &struct cxl_mem device whose mailbox will be used.
> - * @send_cmd: &struct cxl_send_command copied in from userspace.
> - * @out_cmd: Sanitized and populated &struct cxl_mem_command.
> - *
> - * Return:
> - *  * %0	- @out_cmd is ready to send.
> - *  * %-ENOTTY	- Invalid command specified.
> - *  * %-EINVAL	- Reserved fields or invalid values were used.
> - *  * %-ENOMEM	- Input or output buffer wasn't sized properly.
> - *  * %-EPERM	- Attempted to use a protected command.
> - *
> - * The result of this command is a fully validated command in @out_cmd that is
> - * safe to send to the hardware.
> - *
> - * See handle_mailbox_cmd_from_user()
> - */
> -static int cxl_validate_cmd_from_user(struct cxl_mem *cxlm,
> -				      const struct cxl_send_command *send_cmd,
> -				      struct cxl_mem_command *out_cmd)
> -{
> -	const struct cxl_command_info *info;
> -	struct cxl_mem_command *c;
> -
> -	if (send_cmd->id == 0 || send_cmd->id >= CXL_MEM_COMMAND_ID_MAX)
> -		return -ENOTTY;
> -
> -	/*
> -	 * The user can never specify an input payload larger than what hardware
> -	 * supports, but output can be arbitrarily large (simply write out as
> -	 * much data as the hardware provides).
> -	 */
> -	if (send_cmd->in.size > cxlm->payload_size)
> -		return -EINVAL;
> -
> -	/*
> -	 * Checks are bypassed for raw commands but a WARN/taint will occur
> -	 * later in the callchain
> -	 */
> -	if (send_cmd->id == CXL_MEM_COMMAND_ID_RAW) {
> -		const struct cxl_mem_command temp = {
> -			.info = {
> -				.id = CXL_MEM_COMMAND_ID_RAW,
> -				.flags = 0,
> -				.size_in = send_cmd->in.size,
> -				.size_out = send_cmd->out.size,
> -			},
> -			.opcode = send_cmd->raw.opcode
> -		};
> -
> -		if (send_cmd->raw.rsvd)
> -			return -EINVAL;
> -
> -		/*
> -		 * Unlike supported commands, the output size of RAW commands
> -		 * gets passed along without further checking, so it must be
> -		 * validated here.
> -		 */
> -		if (send_cmd->out.size > cxlm->payload_size)
> -			return -EINVAL;
> -
> -		if (!cxl_mem_raw_command_allowed(send_cmd->raw.opcode))
> -			return -EPERM;
> -
> -		memcpy(out_cmd, &temp, sizeof(temp));
> -
> -		return 0;
> -	}
> -
> -	if (send_cmd->flags & ~CXL_MEM_COMMAND_FLAG_MASK)
> -		return -EINVAL;
> -
> -	if (send_cmd->rsvd)
> -		return -EINVAL;
> -
> -	if (send_cmd->in.rsvd || send_cmd->out.rsvd)
> -		return -EINVAL;
> -
> -	/* Convert user's command into the internal representation */
> -	c = &mem_commands[send_cmd->id];
> -	info = &c->info;
> -
> -	/* Check that the command is enabled for hardware */
> -	if (!test_bit(info->id, cxlm->enabled_cmds))
> -		return -ENOTTY;
> -
> -	/* Check the input buffer is the expected size */
> -	if (info->size_in >= 0 && info->size_in != send_cmd->in.size)
> -		return -ENOMEM;
> -
> -	/* Check the output buffer is at least large enough */
> -	if (info->size_out >= 0 && send_cmd->out.size < info->size_out)
> -		return -ENOMEM;
> -
> -	memcpy(out_cmd, c, sizeof(*c));
> -	out_cmd->info.size_in = send_cmd->in.size;
> -	/*
> -	 * XXX: out_cmd->info.size_out will be controlled by the driver, and the
> -	 * specified number of bytes @send_cmd->out.size will be copied back out
> -	 * to userspace.
> -	 */
> -
> -	return 0;
> -}
> -
> -static int cxl_query_cmd(struct cxl_memdev *cxlmd,
> -			 struct cxl_mem_query_commands __user *q)
> -{
> -	struct device *dev = &cxlmd->dev;
> -	struct cxl_mem_command *cmd;
> -	u32 n_commands;
> -	int j = 0;
> -
> -	dev_dbg(dev, "Query IOCTL\n");
> -
> -	if (get_user(n_commands, &q->n_commands))
> -		return -EFAULT;
> -
> -	/* returns the total number if 0 elements are requested. */
> -	if (n_commands == 0)
> -		return put_user(cxl_cmd_count, &q->n_commands);
> -
> -	/*
> -	 * otherwise, return max(n_commands, total commands) cxl_command_info
> -	 * structures.
> -	 */
> -	cxl_for_each_cmd(cmd) {
> -		const struct cxl_command_info *info = &cmd->info;
> -
> -		if (copy_to_user(&q->commands[j++], info, sizeof(*info)))
> -			return -EFAULT;
> -
> -		if (j == n_commands)
> -			break;
> -	}
> -
> -	return 0;
> -}
> -
> -static int cxl_send_cmd(struct cxl_memdev *cxlmd,
> -			struct cxl_send_command __user *s)
> -{
> -	struct cxl_mem *cxlm = cxlmd->cxlm;
> -	struct device *dev = &cxlmd->dev;
> -	struct cxl_send_command send;
> -	struct cxl_mem_command c;
> -	int rc;
> -
> -	dev_dbg(dev, "Send IOCTL\n");
> -
> -	if (copy_from_user(&send, s, sizeof(send)))
> -		return -EFAULT;
> -
> -	rc = cxl_validate_cmd_from_user(cxlmd->cxlm, &send, &c);
> -	if (rc)
> -		return rc;
> -
> -	/* Prepare to handle a full payload for variable sized output */
> -	if (c.info.size_out < 0)
> -		c.info.size_out = cxlm->payload_size;
> -
> -	rc = handle_mailbox_cmd_from_user(cxlm, &c, send.in.payload,
> -					  send.out.payload, &send.out.size,
> -					  &send.retval);
> -	if (rc)
> -		return rc;
> -
> -	if (copy_to_user(s, &send, sizeof(send)))
> -		return -EFAULT;
> -
> -	return 0;
> -}
> -
> -static long __cxl_memdev_ioctl(struct cxl_memdev *cxlmd, unsigned int cmd,
> -			       unsigned long arg)
> -{
> -	switch (cmd) {
> -	case CXL_MEM_QUERY_COMMANDS:
> -		return cxl_query_cmd(cxlmd, (void __user *)arg);
> -	case CXL_MEM_SEND_COMMAND:
> -		return cxl_send_cmd(cxlmd, (void __user *)arg);
> -	default:
> -		return -ENOTTY;
> -	}
> -}
> -
> -static long cxl_memdev_ioctl(struct file *file, unsigned int cmd,
> -			     unsigned long arg)
> -{
> -	struct cxl_memdev *cxlmd = file->private_data;
> -	int rc = -ENXIO;
> -
> -	down_read(&cxl_memdev_rwsem);
> -	if (cxlmd->cxlm)
> -		rc = __cxl_memdev_ioctl(cxlmd, cmd, arg);
> -	up_read(&cxl_memdev_rwsem);
> -
> -	return rc;
> -}
> -
> -static int cxl_memdev_open(struct inode *inode, struct file *file)
> -{
> -	struct cxl_memdev *cxlmd =
> -		container_of(inode->i_cdev, typeof(*cxlmd), cdev);
> -
> -	get_device(&cxlmd->dev);
> -	file->private_data = cxlmd;
> -
> -	return 0;
> -}
> -
> -static int cxl_memdev_release_file(struct inode *inode, struct file *file)
> -{
> -	struct cxl_memdev *cxlmd =
> -		container_of(inode->i_cdev, typeof(*cxlmd), cdev);
> -
> -	put_device(&cxlmd->dev);
> -
> -	return 0;
> -}
> -
> -static void cxl_memdev_shutdown(struct device *dev)
> -{
> -	struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
> -
> -	down_write(&cxl_memdev_rwsem);
> -	cxlmd->cxlm = NULL;
> -	up_write(&cxl_memdev_rwsem);
> -}
> -
> -static const struct cdevm_file_operations cxl_memdev_fops = {
> -	.fops = {
> -		.owner = THIS_MODULE,
> -		.unlocked_ioctl = cxl_memdev_ioctl,
> -		.open = cxl_memdev_open,
> -		.release = cxl_memdev_release_file,
> -		.compat_ioctl = compat_ptr_ioctl,
> -		.llseek = noop_llseek,
> -	},
> -	.shutdown = cxl_memdev_shutdown,
> -};
> -
> -static inline struct cxl_mem_command *cxl_mem_find_command(u16 opcode)
> -{
> -	struct cxl_mem_command *c;
> -
> -	cxl_for_each_cmd(c)
> -		if (c->opcode == opcode)
> -			return c;
> -
> -	return NULL;
> -}
> -
> -/**
> - * cxl_mem_mbox_send_cmd() - Send a mailbox command to a memory device.
> - * @cxlm: The CXL memory device to communicate with.
> - * @opcode: Opcode for the mailbox command.
> - * @in: The input payload for the mailbox command.
> - * @in_size: The length of the input payload
> - * @out: Caller allocated buffer for the output.
> - * @out_size: Expected size of output.
> - *
> - * Context: Any context. Will acquire and release mbox_mutex.
> - * Return:
> - *  * %>=0	- Number of bytes returned in @out.
> - *  * %-E2BIG	- Payload is too large for hardware.
> - *  * %-EBUSY	- Couldn't acquire exclusive mailbox access.
> - *  * %-EFAULT	- Hardware error occurred.
> - *  * %-ENXIO	- Command completed, but device reported an error.
> - *  * %-EIO	- Unexpected output size.
> - *
> - * Mailbox commands may execute successfully yet the device itself reported an
> - * error. While this distinction can be useful for commands from userspace, the
> - * kernel will only be able to use results when both are successful.
> - *
> - * See __cxl_mem_mbox_send_cmd()
> - */
> -static int cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm, u16 opcode,
> -				 void *in, size_t in_size,
> -				 void *out, size_t out_size)
> -{
> -	const struct cxl_mem_command *cmd = cxl_mem_find_command(opcode);
> -	struct cxl_mbox_cmd mbox_cmd = {
> -		.opcode = opcode,
> -		.payload_in = in,
> -		.size_in = in_size,
> -		.size_out = out_size,
> -		.payload_out = out,
> -	};
> -	int rc;
> -
> -	if (out_size > cxlm->payload_size)
> -		return -E2BIG;
> -
> -	rc = cxlm->mbox_send(cxlm, &mbox_cmd);
> -	if (rc)
> -		return rc;
> -
> -	/* TODO: Map return code to proper kernel style errno */
> -	if (mbox_cmd.return_code != CXL_MBOX_SUCCESS)
> -		return -ENXIO;
> -
> -	/*
> -	 * Variable sized commands can't be validated and so it's up to the
> -	 * caller to do that if they wish.
> -	 */
> -	if (cmd->info.size_out >= 0 && mbox_cmd.size_out != out_size)
> -		return -EIO;
> -
> -	return 0;
> -}
> -
>  static int cxl_mem_setup_mailbox(struct cxl_mem *cxlm)
>  {
>  	const int cap = readl(cxlm->regs.mbox + CXLDEV_MBOX_CAPS_OFFSET);
> @@ -901,30 +304,6 @@ static int cxl_mem_setup_mailbox(struct cxl_mem *cxlm)
>  	return 0;
>  }
>  
> -static struct cxl_mem *cxl_mem_create(struct device *dev)
> -{
> -	struct cxl_mem *cxlm;
> -
> -	cxlm = devm_kzalloc(dev, sizeof(*cxlm), GFP_KERNEL);
> -	if (!cxlm) {
> -		dev_err(dev, "No memory available\n");
> -		return ERR_PTR(-ENOMEM);
> -	}
> -
> -	mutex_init(&cxlm->mbox_mutex);
> -	cxlm->dev = dev;
> -	cxlm->enabled_cmds =
> -		devm_kmalloc_array(dev, BITS_TO_LONGS(cxl_cmd_count),
> -				   sizeof(unsigned long),
> -				   GFP_KERNEL | __GFP_ZERO);
> -	if (!cxlm->enabled_cmds) {
> -		dev_err(dev, "No memory available for bitmap\n");
> -		return ERR_PTR(-ENOMEM);
> -	}
> -
> -	return cxlm;
> -}
> -
>  static void __iomem *cxl_mem_map_regblock(struct cxl_mem *cxlm,
>  					  u8 bar, u64 offset)
>  {
> @@ -1132,298 +511,6 @@ static int cxl_mem_setup_regs(struct cxl_mem *cxlm)
>  	return ret;
>  }
>  
> -static int cxl_xfer_log(struct cxl_mem *cxlm, uuid_t *uuid, u32 size, u8 *out)
> -{
> -	u32 remaining = size;
> -	u32 offset = 0;
> -
> -	while (remaining) {
> -		u32 xfer_size = min_t(u32, remaining, cxlm->payload_size);
> -		struct cxl_mbox_get_log {
> -			uuid_t uuid;
> -			__le32 offset;
> -			__le32 length;
> -		} __packed log = {
> -			.uuid = *uuid,
> -			.offset = cpu_to_le32(offset),
> -			.length = cpu_to_le32(xfer_size)
> -		};
> -		int rc;
> -
> -		rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_GET_LOG, &log,
> -					   sizeof(log), out, xfer_size);
> -		if (rc < 0)
> -			return rc;
> -
> -		out += xfer_size;
> -		remaining -= xfer_size;
> -		offset += xfer_size;
> -	}
> -
> -	return 0;
> -}
> -
> -/**
> - * cxl_walk_cel() - Walk through the Command Effects Log.
> - * @cxlm: Device.
> - * @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_mem *cxlm, size_t size, u8 *cel)
> -{
> -	struct cel_entry {
> -		__le16 opcode;
> -		__le16 effect;
> -	} __packed * cel_entry;
> -	const int cel_entries = size / sizeof(*cel_entry);
> -	int i;
> -
> -	cel_entry = (struct cel_entry *)cel;
> -
> -	for (i = 0; i < cel_entries; i++) {
> -		u16 opcode = le16_to_cpu(cel_entry[i].opcode);
> -		struct cxl_mem_command *cmd = cxl_mem_find_command(opcode);
> -
> -		if (!cmd) {
> -			dev_dbg(cxlm->dev,
> -				"Opcode 0x%04x unsupported by driver", opcode);
> -			continue;
> -		}
> -
> -		set_bit(cmd->info.id, cxlm->enabled_cmds);
> -	}
> -}
> -
> -struct cxl_mbox_get_supported_logs {
> -	__le16 entries;
> -	u8 rsvd[6];
> -	struct gsl_entry {
> -		uuid_t uuid;
> -		__le32 size;
> -	} __packed entry[];
> -} __packed;
> -
> -static struct cxl_mbox_get_supported_logs *cxl_get_gsl(struct cxl_mem *cxlm)
> -{
> -	struct cxl_mbox_get_supported_logs *ret;
> -	int rc;
> -
> -	ret = kvmalloc(cxlm->payload_size, GFP_KERNEL);
> -	if (!ret)
> -		return ERR_PTR(-ENOMEM);
> -
> -	rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_GET_SUPPORTED_LOGS, NULL,
> -				   0, ret, cxlm->payload_size);
> -	if (rc < 0) {
> -		kvfree(ret);
> -		return ERR_PTR(rc);
> -	}
> -
> -	return ret;
> -}
> -
> -/**
> - * cxl_mem_get_partition_info - Get partition info
> - * @cxlm: cxl_mem instance to update partition info
> - *
> - * Retrieve the current partition info for the device specified.  If not 0, the
> - * 'next' values are pending and take affect on next cold reset.
> - *
> - * Return: 0 if no error: or the result of the mailbox command.
> - *
> - * See CXL @8.2.9.5.2.1 Get Partition Info
> - */
> -static int cxl_mem_get_partition_info(struct cxl_mem *cxlm)
> -{
> -	struct cxl_mbox_get_partition_info {
> -		__le64 active_volatile_cap;
> -		__le64 active_persistent_cap;
> -		__le64 next_volatile_cap;
> -		__le64 next_persistent_cap;
> -	} __packed pi;
> -	int rc;
> -
> -	rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_GET_PARTITION_INFO,
> -				   NULL, 0, &pi, sizeof(pi));
> -	if (rc)
> -		return rc;
> -
> -	cxlm->active_volatile_bytes =
> -		le64_to_cpu(pi.active_volatile_cap) * CXL_CAPACITY_MULTIPLIER;
> -	cxlm->active_persistent_bytes =
> -		le64_to_cpu(pi.active_persistent_cap) * CXL_CAPACITY_MULTIPLIER;
> -	cxlm->next_volatile_bytes =
> -		le64_to_cpu(pi.next_volatile_cap) * CXL_CAPACITY_MULTIPLIER;
> -	cxlm->next_persistent_bytes =
> -		le64_to_cpu(pi.next_volatile_cap) * CXL_CAPACITY_MULTIPLIER;
> -
> -	return 0;
> -}
> -
> -/**
> - * cxl_mem_enumerate_cmds() - Enumerate commands for a device.
> - * @cxlm: The device.
> - *
> - * 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 @cxlm.
> - */
> -static int cxl_mem_enumerate_cmds(struct cxl_mem *cxlm)
> -{
> -	struct cxl_mbox_get_supported_logs *gsl;
> -	struct device *dev = cxlm->dev;
> -	struct cxl_mem_command *cmd;
> -	int i, rc;
> -
> -	gsl = cxl_get_gsl(cxlm);
> -	if (IS_ERR(gsl))
> -		return PTR_ERR(gsl);
> -
> -	rc = -ENOENT;
> -	for (i = 0; i < le16_to_cpu(gsl->entries); i++) {
> -		u32 size = le32_to_cpu(gsl->entry[i].size);
> -		uuid_t uuid = gsl->entry[i].uuid;
> -		u8 *log;
> -
> -		dev_dbg(dev, "Found LOG type %pU of size %d", &uuid, size);
> -
> -		if (!uuid_equal(&uuid, &log_uuid[CEL_UUID]))
> -			continue;
> -
> -		log = kvmalloc(size, GFP_KERNEL);
> -		if (!log) {
> -			rc = -ENOMEM;
> -			goto out;
> -		}
> -
> -		rc = cxl_xfer_log(cxlm, &uuid, size, log);
> -		if (rc) {
> -			kvfree(log);
> -			goto out;
> -		}
> -
> -		cxl_walk_cel(cxlm, 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, cxlm->enabled_cmds);
> -
> -		/* Found the required CEL */
> -		rc = 0;
> -	}
> -
> -out:
> -	kvfree(gsl);
> -	return rc;
> -}
> -
> -/**
> - * cxl_mem_identify() - Send the IDENTIFY command to the device.
> - * @cxlm: The device to identify.
> - *
> - * Return: 0 if identify was executed successfully.
> - *
> - * This will dispatch the identify command to the device and on success populate
> - * structures to be exported to sysfs.
> - */
> -static int cxl_mem_identify(struct cxl_mem *cxlm)
> -{
> -	/* See CXL 2.0 Table 175 Identify Memory Device Output Payload */
> -	struct cxl_mbox_identify {
> -		char fw_revision[0x10];
> -		__le64 total_capacity;
> -		__le64 volatile_capacity;
> -		__le64 persistent_capacity;
> -		__le64 partition_align;
> -		__le16 info_event_log_size;
> -		__le16 warning_event_log_size;
> -		__le16 failure_event_log_size;
> -		__le16 fatal_event_log_size;
> -		__le32 lsa_size;
> -		u8 poison_list_max_mer[3];
> -		__le16 inject_poison_limit;
> -		u8 poison_caps;
> -		u8 qos_telemetry_caps;
> -	} __packed id;
> -	int rc;
> -
> -	rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_IDENTIFY, NULL, 0, &id,
> -				   sizeof(id));
> -	if (rc < 0)
> -		return rc;
> -
> -	cxlm->total_bytes = le64_to_cpu(id.total_capacity);
> -	cxlm->total_bytes *= CXL_CAPACITY_MULTIPLIER;
> -
> -	cxlm->volatile_only_bytes = le64_to_cpu(id.volatile_capacity);
> -	cxlm->volatile_only_bytes *= CXL_CAPACITY_MULTIPLIER;
> -
> -	cxlm->persistent_only_bytes = le64_to_cpu(id.persistent_capacity);
> -	cxlm->persistent_only_bytes *= CXL_CAPACITY_MULTIPLIER;
> -
> -	cxlm->partition_align_bytes = le64_to_cpu(id.partition_align);
> -	cxlm->partition_align_bytes *= CXL_CAPACITY_MULTIPLIER;
> -
> -	dev_dbg(cxlm->dev,
> -		"Identify Memory Device\n"
> -		"     total_bytes = %#llx\n"
> -		"     volatile_only_bytes = %#llx\n"
> -		"     persistent_only_bytes = %#llx\n"
> -		"     partition_align_bytes = %#llx\n",
> -		cxlm->total_bytes, cxlm->volatile_only_bytes,
> -		cxlm->persistent_only_bytes, cxlm->partition_align_bytes);
> -
> -	cxlm->lsa_size = le32_to_cpu(id.lsa_size);
> -	memcpy(cxlm->firmware_version, id.fw_revision, sizeof(id.fw_revision));
> -
> -	return 0;
> -}
> -
> -static int cxl_mem_create_range_info(struct cxl_mem *cxlm)
> -{
> -	int rc;
> -
> -	if (cxlm->partition_align_bytes == 0) {
> -		cxlm->ram_range.start = 0;
> -		cxlm->ram_range.end = cxlm->volatile_only_bytes - 1;
> -		cxlm->pmem_range.start = cxlm->volatile_only_bytes;
> -		cxlm->pmem_range.end = cxlm->volatile_only_bytes +
> -					cxlm->persistent_only_bytes - 1;
> -		return 0;
> -	}
> -
> -	rc = cxl_mem_get_partition_info(cxlm);
> -	if (rc < 0) {
> -		dev_err(cxlm->dev, "Failed to query partition information\n");
> -		return rc;
> -	}
> -
> -	dev_dbg(cxlm->dev,
> -		"Get Partition Info\n"
> -		"     active_volatile_bytes = %#llx\n"
> -		"     active_persistent_bytes = %#llx\n"
> -		"     next_volatile_bytes = %#llx\n"
> -		"     next_persistent_bytes = %#llx\n",
> -		cxlm->active_volatile_bytes, cxlm->active_persistent_bytes,
> -		cxlm->next_volatile_bytes, cxlm->next_persistent_bytes);
> -
> -	cxlm->ram_range.start = 0;
> -	cxlm->ram_range.end = cxlm->active_volatile_bytes - 1;
> -
> -	cxlm->pmem_range.start = cxlm->active_volatile_bytes;
> -	cxlm->pmem_range.end = cxlm->active_volatile_bytes +
> -				cxlm->active_persistent_bytes - 1;
> -
> -	return 0;
> -}
> -
>  static int cxl_mem_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>  {
>  	struct cxl_memdev *cxlmd;
> @@ -1458,7 +545,7 @@ static int cxl_mem_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>  	if (rc)
>  		return rc;
>  
> -	cxlmd = devm_cxl_add_memdev(cxlm, &cxl_memdev_fops);
> +	cxlmd = devm_cxl_add_memdev(cxlm);
>  	if (IS_ERR(cxlmd))
>  		return PTR_ERR(cxlmd);
>  
> @@ -1486,7 +573,6 @@ static struct pci_driver cxl_mem_driver = {
>  
>  static __init int cxl_mem_init(void)
>  {
> -	struct dentry *mbox_debugfs;
>  	int rc;
>  
>  	/* Double check the anonymous union trickery in struct cxl_regs */
> @@ -1497,17 +583,11 @@ static __init int cxl_mem_init(void)
>  	if (rc)
>  		return rc;
>  
> -	cxl_debugfs = debugfs_create_dir("cxl", NULL);
> -	mbox_debugfs = debugfs_create_dir("mbox", cxl_debugfs);
> -	debugfs_create_bool("raw_allow_all", 0600, mbox_debugfs,
> -			    &cxl_raw_allow_all);
> -
>  	return 0;
>  }
>  
>  static __exit void cxl_mem_exit(void)
>  {
> -	debugfs_remove_recursive(cxl_debugfs);
>  	pci_unregister_driver(&cxl_mem_driver);
>  }
>  
> 

  reply	other threads:[~2021-09-09 16:41 UTC|newest]

Thread overview: 78+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-09-09  5:11 [PATCH v4 00/21] cxl_test: Enable CXL Topology and UAPI regression tests Dan Williams
2021-09-09  5:11 ` [PATCH v4 01/21] libnvdimm/labels: Add uuid helpers Dan Williams
2021-09-09  5:11 ` [PATCH v4 02/21] libnvdimm/label: Add a helper for nlabel validation Dan Williams
2021-09-09  5:11 ` [PATCH v4 03/21] libnvdimm/labels: Introduce the concept of multi-range namespace labels Dan Williams
2021-09-09 13:09   ` Jonathan Cameron
2021-09-09 15:16     ` Dan Williams
2021-09-09  5:11 ` [PATCH v4 04/21] libnvdimm/labels: Fix kernel-doc for label.h Dan Williams
2021-09-10  8:38   ` Jonathan Cameron
2021-09-09  5:11 ` [PATCH v4 05/21] libnvdimm/label: Define CXL region labels Dan Williams
2021-09-09 15:58   ` Ben Widawsky
2021-09-09 18:38     ` Dan Williams
2021-09-09  5:12 ` [PATCH v4 06/21] libnvdimm/labels: Introduce CXL labels Dan Williams
2021-09-09  5:12 ` [PATCH v4 07/21] cxl/pci: Make 'struct cxl_mem' device type generic Dan Williams
2021-09-09 16:12   ` Ben Widawsky
2021-09-10  8:43   ` Jonathan Cameron
2021-09-09  5:12 ` [PATCH v4 08/21] cxl/pci: Clean up cxl_mem_get_partition_info() Dan Williams
2021-09-09 16:20   ` Ben Widawsky
2021-09-09 18:06     ` Dan Williams
2021-09-09 21:05       ` Ben Widawsky
2021-09-09 21:10         ` Dan Williams
2021-09-10  8:56         ` Jonathan Cameron
2021-09-13 22:19   ` [PATCH v5 " Dan Williams
2021-09-13 22:21     ` Dan Williams
2021-09-13 22:24   ` [PATCH v6 " Dan Williams
2021-09-09  5:12 ` [PATCH v4 09/21] cxl/mbox: Introduce the mbox_send operation Dan Williams
2021-09-09 16:34   ` Ben Widawsky
2021-09-10  8:58   ` Jonathan Cameron
2021-09-09  5:12 ` [PATCH v4 10/21] cxl/pci: Drop idr.h Dan Williams
2021-09-09 16:34   ` Ben Widawsky
2021-09-10  8:46     ` Jonathan Cameron
2021-09-09  5:12 ` [PATCH v4 11/21] cxl/mbox: Move mailbox and other non-PCI specific infrastructure to the core Dan Williams
2021-09-09 16:41   ` Ben Widawsky [this message]
2021-09-09 18:50     ` Dan Williams
2021-09-09 20:35       ` Ben Widawsky
2021-09-09 21:05         ` Dan Williams
2021-09-10  9:13   ` Jonathan Cameron
2021-09-09  5:12 ` [PATCH v4 12/21] cxl/pci: Use module_pci_driver Dan Williams
2021-09-09  5:12 ` [PATCH v4 13/21] cxl/mbox: Convert 'enabled_cmds' to DECLARE_BITMAP Dan Williams
2021-09-09  5:12 ` [PATCH v4 14/21] cxl/mbox: Add exclusive kernel command support Dan Williams
2021-09-09 17:02   ` Ben Widawsky
2021-09-10  9:33   ` Jonathan Cameron
2021-09-13 23:46     ` Dan Williams
2021-09-14  9:01       ` Jonathan Cameron
2021-09-14 12:22       ` Konstantin Ryabitsev
2021-09-14 14:39         ` Dan Williams
2021-09-14 15:51           ` Konstantin Ryabitsev
2021-09-14 19:03   ` [PATCH v5 " Dan Williams
2021-09-09  5:12 ` [PATCH v4 15/21] cxl/pmem: Translate NVDIMM label commands to CXL label commands Dan Williams
2021-09-09 17:22   ` Ben Widawsky
2021-09-09 19:03     ` Dan Williams
2021-09-09 20:32       ` Ben Widawsky
2021-09-10  9:39         ` Jonathan Cameron
2021-09-09 22:08   ` [PATCH v5 " Dan Williams
2021-09-10  9:40     ` Jonathan Cameron
2021-09-14 19:06   ` Dan Williams
2021-09-09  5:12 ` [PATCH v4 16/21] cxl/pmem: Add support for multiple nvdimm-bridge objects Dan Williams
2021-09-09 22:03   ` Dan Williams
2021-09-14 19:08   ` [PATCH v5 " Dan Williams
2021-09-09  5:13 ` [PATCH v4 17/21] tools/testing/cxl: Introduce a mocked-up CXL port hierarchy Dan Williams
2021-09-10  9:53   ` Jonathan Cameron
2021-09-10 18:46     ` Dan Williams
2021-09-14 19:14   ` [PATCH v5 " Dan Williams
2021-09-09  5:13 ` [PATCH v4 18/21] cxl/bus: Populate the target list at decoder create Dan Williams
2021-09-10  9:57   ` Jonathan Cameron
2021-09-09  5:13 ` [PATCH v4 19/21] cxl/mbox: Move command definitions to common location Dan Williams
2021-09-09  5:13 ` [PATCH v4 20/21] tools/testing/cxl: Introduce a mock memory device + driver Dan Williams
2021-09-10 10:09   ` Jonathan Cameron
2021-09-09  5:13 ` [PATCH v4 21/21] cxl/core: Split decoder setup into alloc + add Dan Williams
2021-09-10 10:33   ` Jonathan Cameron
2021-09-10 18:36     ` Dan Williams
2021-09-11 17:15       ` Ben Widawsky
2021-09-11 20:20         ` Dan Williams
2021-09-14 19:31   ` [PATCH v5 " Dan Williams
2021-09-21 14:24     ` Ben Widawsky
2021-09-21 16:18       ` Dan Williams
2021-09-21 19:22     ` [PATCH v6 " Dan Williams
2021-12-10 19:38       ` Nathan Chancellor
2021-12-10 19:41         ` Dan Williams

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210909164125.ttvptq6eiiirvnnp@intel.com \
    --to=ben.widawsky@intel.com \
    --cc=Jonathan.Cameron@huawei.com \
    --cc=alison.schofield@intel.com \
    --cc=dan.j.williams@intel.com \
    --cc=ira.weiny@intel.com \
    --cc=linux-cxl@vger.kernel.org \
    --cc=lkp@intel.com \
    --cc=nvdimm@lists.linux.dev \
    --cc=vishal.l.verma@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).