@@ -9257,6 +9257,13 @@ L: linux-kernel@vger.kernel.org
S: Maintained
F: drivers/fwctl/mlx5/
+FWCTL CXL DRIVER
+M: Dave Jiang <dave.jiang@intel.com>
+R: Dan Williams <dan.j.williams@intel.com>
+L: linux-cxl@vger.kernel.org
+S: Maintained
+F: drivers/fwctl/cxl/
+
GALAXYCORE GC0308 CAMERA SENSOR DRIVER
M: Sebastian Reichel <sre@kernel.org>
L: linux-media@vger.kernel.org
@@ -19,5 +19,14 @@ config FWCTL_MLX5
This will allow configuration and debug tools to work out of the box on
mainstream kernel.
+ If you don't know what to do here, say N.
+
+config FWCTL_CXL
+ tristate "CXL fwctl driver"
+ depends on CXL_BUS
+ help
+ CXLCTL provides interface for the user process to access user allowed
+ mailbox commands for CXL device.
+
If you don't know what to do here, say N.
endif
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_FWCTL) += fwctl.o
obj-$(CONFIG_FWCTL_MLX5) += mlx5/
+obj-$(CONFIG_FWCTL_CXL) += cxl/
fwctl-y += main.o
new file mode 100644
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_FWCTL_CXL) += cxl_fwctl.o
+
+cxl_fwctl-y += cxl.o
new file mode 100644
@@ -0,0 +1,274 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2024, Intel Corporation
+ */
+#include <linux/fwctl.h>
+#include <linux/auxiliary_bus.h>
+#include <linux/cxl/mailbox.h>
+#include <linux/auxiliary_bus.h>
+#include <uapi/fwctl/cxl.h>
+
+struct cxlctl_uctx {
+ struct fwctl_uctx uctx;
+ u32 uctx_caps;
+ u32 uctx_uid;
+};
+
+struct cxlctl_dev {
+ struct fwctl_device fwctl;
+ struct cxl_mailbox *mbox;
+};
+
+DEFINE_FREE(cxlctl, struct cxlctl_dev *, if (_T) fwctl_put(&_T->fwctl))
+
+static int cxlctl_open_uctx(struct fwctl_uctx *uctx)
+{
+ struct cxlctl_uctx *cxlctl_uctx =
+ container_of(uctx, struct cxlctl_uctx, uctx);
+
+ cxlctl_uctx->uctx_caps = BIT(FWCTL_CXL_QUERY_COMMANDS) |
+ BIT(FWCTL_CXL_SEND_COMMAND);
+
+ return 0;
+}
+
+static void cxlctl_close_uctx(struct fwctl_uctx *uctx)
+{
+}
+
+static void *cxlctl_info(struct fwctl_uctx *uctx, size_t *length)
+{
+ struct cxlctl_uctx *cxlctl_uctx =
+ container_of(uctx, struct cxlctl_uctx, uctx);
+ struct fwctl_info_cxl *info;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return ERR_PTR(-ENOMEM);
+
+ info->uctx_caps = cxlctl_uctx->uctx_caps;
+
+ return info;
+}
+
+static bool cxlctl_validate_set_features(struct cxl_mailbox *cxl_mbox,
+ const struct fwctl_cxl_command *send_cmd,
+ enum fwctl_rpc_scope scope)
+{
+ struct cxl_feat_entry *feat;
+ bool found = false;
+ uuid_t uuid;
+ u16 mask;
+
+ if (send_cmd->in.size < sizeof(struct set_feature_input))
+ return false;
+
+ if (copy_from_user(&uuid, u64_to_user_ptr(send_cmd->in.payload),
+ sizeof(uuid)))
+ return false;
+
+ for (int i = 0; i < cxl_mbox->num_features; i++) {
+ feat = &cxl_mbox->entries[i];
+ if (uuid_equal(&uuid, &feat->uuid)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ return false;
+
+ /* Currently no user background command support */
+ if (feat->effects & CXL_CMD_BACKGROUND)
+ return false;
+
+ mask = CXL_CMD_CONFIG_CHANGE_IMMEDIATE |
+ CXL_CMD_DATA_CHANGE_IMMEDIATE |
+ CXL_CMD_POLICY_CHANGE_IMMEDIATE |
+ CXL_CMD_LOG_CHANGE_IMMEDIATE;
+ if (feat->effects & mask && scope >= FWCTL_RPC_DEBUG_WRITE)
+ return true;
+
+ /* These effects supported for all scope */
+ if ((feat->effects & CXL_CMD_CONFIG_CHANGE_COLD_RESET ||
+ feat->effects & CXL_CMD_CONFIG_CHANGE_CONV_RESET) &&
+ scope >= FWCTL_RPC_DEBUG_READ_ONLY)
+ return true;
+
+ return false;
+}
+
+static bool cxlctl_validate_hw_cmds(struct cxl_mailbox *cxl_mbox,
+ const struct fwctl_cxl_command *send_cmd,
+ enum fwctl_rpc_scope scope)
+{
+ struct cxl_mem_command *cmd;
+
+ /*
+ * Only supporting feature commands.
+ */
+ if (!cxl_mbox->num_features)
+ return false;
+
+ cmd = cxl_get_mem_command(send_cmd->id);
+ if (!cmd)
+ return false;
+
+ if (test_bit(cmd->info.id, cxl_mbox->enabled_cmds))
+ return false;
+
+ if (test_bit(cmd->info.id, cxl_mbox->exclusive_cmds))
+ return false;
+
+ switch (cmd->opcode) {
+ case CXL_MBOX_OP_GET_SUPPORTED_FEATURES:
+ case CXL_MBOX_OP_GET_FEATURE:
+ if (scope >= FWCTL_RPC_DEBUG_READ_ONLY)
+ return true;
+ break;
+ case CXL_MBOX_OP_SET_FEATURE:
+ return cxlctl_validate_set_features(cxl_mbox, send_cmd, scope);
+ default:
+ return false;
+ };
+
+ return false;
+}
+
+static bool cxlctl_validate_query_commands(struct fwctl_rpc_cxl *rpc_in)
+{
+ int cmds;
+
+ if (rpc_in->payload_size < sizeof(rpc_in->query))
+ return false;
+
+ cmds = rpc_in->query.n_commands;
+ if (cmds) {
+ int cmds_size = rpc_in->payload_size - sizeof(rpc_in->query);
+
+ if (cmds != cmds_size / sizeof(struct cxl_command_info))
+ return false;
+ }
+
+ return true;
+}
+
+static bool cxlctl_validate_rpc(struct fwctl_uctx *uctx,
+ struct fwctl_rpc_cxl *rpc_in,
+ enum fwctl_rpc_scope scope)
+{
+ struct cxlctl_dev *cxlctl =
+ container_of(uctx->fwctl, struct cxlctl_dev, fwctl);
+
+ switch (rpc_in->rpc_cmd) {
+ case FWCTL_CXL_QUERY_COMMANDS:
+ return cxlctl_validate_query_commands(rpc_in);
+
+ case FWCTL_CXL_SEND_COMMAND:
+ return cxlctl_validate_hw_cmds(cxlctl->mbox,
+ &rpc_in->send_cmd, scope);
+
+ default:
+ return false;
+ }
+}
+
+static void *send_cxl_command(struct cxl_mailbox *cxl_mbox,
+ struct fwctl_cxl_command *send_cmd,
+ size_t *out_len)
+{
+ struct cxl_mbox_cmd mbox_cmd;
+ int rc;
+
+ rc = cxl_fwctl_send_cmd(cxl_mbox, send_cmd, &mbox_cmd, out_len);
+ if (rc)
+ return ERR_PTR(rc);
+
+ *out_len = mbox_cmd.size_out;
+
+ return mbox_cmd.payload_out;
+}
+
+static void *cxlctl_fw_rpc(struct fwctl_uctx *uctx, enum fwctl_rpc_scope scope,
+ void *in, size_t in_len, size_t *out_len)
+{
+ struct cxlctl_dev *cxlctl =
+ container_of(uctx->fwctl, struct cxlctl_dev, fwctl);
+ struct cxl_mailbox *cxl_mbox = cxlctl->mbox;
+ struct fwctl_rpc_cxl *rpc_in = in;
+
+ if (!cxlctl_validate_rpc(uctx, rpc_in, scope))
+ return ERR_PTR(-EPERM);
+
+ switch (rpc_in->rpc_cmd) {
+ case FWCTL_CXL_QUERY_COMMANDS:
+ return cxl_query_cmd_from_fwctl(cxl_mbox, &rpc_in->query,
+ out_len);
+
+ case FWCTL_CXL_SEND_COMMAND:
+ return send_cxl_command(cxl_mbox, &rpc_in->send_cmd, out_len);
+
+ default:
+ return ERR_PTR(-EOPNOTSUPP);
+ }
+}
+
+static const struct fwctl_ops cxlctl_ops = {
+ .device_type = FWCTL_DEVICE_TYPE_CXL,
+ .uctx_size = sizeof(struct cxlctl_uctx),
+ .open_uctx = cxlctl_open_uctx,
+ .close_uctx = cxlctl_close_uctx,
+ .info = cxlctl_info,
+ .fw_rpc = cxlctl_fw_rpc,
+};
+
+static int cxlctl_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct cxl_mailbox *mbox = container_of(adev, struct cxl_mailbox, adev);
+ struct cxlctl_dev *cxlctl __free(cxlctl) =
+ fwctl_alloc_device(mbox->host, &cxlctl_ops,
+ struct cxlctl_dev, fwctl);
+ int rc;
+
+ if (!cxlctl)
+ return -ENOMEM;
+
+ cxlctl->mbox = mbox;
+
+ rc = fwctl_register(&cxlctl->fwctl);
+ if (rc)
+ return rc;
+
+ auxiliary_set_drvdata(adev, no_free_ptr(cxlctl));
+
+ return 0;
+}
+
+static void cxlctl_remove(struct auxiliary_device *adev)
+{
+ struct cxlctl_dev *ctldev __free(cxlctl) = auxiliary_get_drvdata(adev);
+
+ fwctl_unregister(&ctldev->fwctl);
+}
+
+static const struct auxiliary_device_id cxlctl_id_table[] = {
+ { .name = "CXL.fwctl", },
+ {},
+};
+MODULE_DEVICE_TABLE(auxiliary, cxlctl_id_table);
+
+static struct auxiliary_driver cxlctl_driver = {
+ .name = "cxl_fwctl",
+ .probe = cxlctl_probe,
+ .remove = cxlctl_remove,
+ .id_table = cxlctl_id_table,
+};
+
+module_auxiliary_driver(cxlctl_driver);
+
+MODULE_IMPORT_NS(CXL);
+MODULE_IMPORT_NS(FWCTL);
+MODULE_DESCRIPTION("CXL fwctl driver");
+MODULE_AUTHOR("Intel Corporation");
+MODULE_LICENSE("GPL");
@@ -4,6 +4,7 @@
#define __CXL_MBOX_H__
#include <uapi/linux/cxl_mem.h>
+#include <uapi/fwctl/cxl.h>
#include <linux/auxiliary_bus.h>
/**
@@ -68,4 +69,107 @@ struct cxl_mailbox {
struct cxl_feat_entry *entries;
};
+enum cxl_opcode {
+ CXL_MBOX_OP_INVALID = 0x0000,
+ CXL_MBOX_OP_RAW = CXL_MBOX_OP_INVALID,
+ CXL_MBOX_OP_GET_EVENT_RECORD = 0x0100,
+ CXL_MBOX_OP_CLEAR_EVENT_RECORD = 0x0101,
+ 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_GET_TIMESTAMP = 0x0300,
+ CXL_MBOX_OP_SET_TIMESTAMP = 0x0301,
+ CXL_MBOX_OP_GET_SUPPORTED_LOGS = 0x0400,
+ CXL_MBOX_OP_GET_LOG = 0x0401,
+ CXL_MBOX_OP_GET_LOG_CAPS = 0x0402,
+ CXL_MBOX_OP_CLEAR_LOG = 0x0403,
+ CXL_MBOX_OP_GET_SUP_LOG_SUBLIST = 0x0405,
+ CXL_MBOX_OP_GET_SUPPORTED_FEATURES = 0x0500,
+ CXL_MBOX_OP_GET_FEATURE = 0x0501,
+ CXL_MBOX_OP_SET_FEATURE = 0x0502,
+ 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_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,
+ CXL_MBOX_OP_UNLOCK = 0x4503,
+ CXL_MBOX_OP_FREEZE_SECURITY = 0x4504,
+ CXL_MBOX_OP_PASSPHRASE_SECURE_ERASE = 0x4505,
+ CXL_MBOX_OP_MAX = 0x10000
+};
+
+#define CXL_CMD_CONFIG_CHANGE_COLD_RESET BIT(0)
+#define CXL_CMD_CONFIG_CHANGE_IMMEDIATE BIT(1)
+#define CXL_CMD_DATA_CHANGE_IMMEDIATE BIT(2)
+#define CXL_CMD_POLICY_CHANGE_IMMEDIATE BIT(3)
+#define CXL_CMD_LOG_CHANGE_IMMEDIATE BIT(4)
+#define CXL_CMD_SECURITY_STATE_CHANGE BIT(5)
+#define CXL_CMD_BACKGROUND BIT(6)
+#define CXL_CMD_BGCMD_ABORT_SUPPORTED BIT(7)
+#define CXL_CMD_CONFIG_CHANGE_CONV_RESET (BIT(9) | BIT(10))
+#define CXL_CMD_CONFIG_CHANGE_CXL_RESET (BIT(9) | BIT(11))
+
+struct cxl_feat_entry {
+ uuid_t uuid;
+ __le16 id;
+ __le16 get_feat_size;
+ __le16 set_feat_size;
+ __le32 flags;
+ u8 get_feat_ver;
+ u8 set_feat_ver;
+ __le16 effects;
+ u8 reserved[18];
+} __packed;
+
+/**
+ * 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_FORCE_ENABLE BIT(0)
+};
+
+struct cxl_mem_command *cxl_get_mem_command(u32 id);
+int cxl_fwctl_send_cmd(struct cxl_mailbox *cxl_mbox,
+ struct fwctl_cxl_command *fwctl_cmd,
+ struct cxl_mbox_cmd *mbox_cmd,
+ size_t *out_len);
+void *cxl_query_cmd_from_fwctl(struct cxl_mailbox *cxl_mbox,
+ struct cxl_mem_query_commands *q,
+ size_t *out_len);
+
#endif
new file mode 100644
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (c) 2024, Intel Corporation
+ *
+ * These are definitions for the mailbox command interface of CXL subsystem.
+ */
+#ifndef _UAPI_FWCTL_CXL_H_
+#define _UAPI_FWCTL_CXL_H_
+
+#include <linux/types.h>
+
+enum fwctl_cxl_commands {
+ FWCTL_CXL_QUERY_COMMANDS = 0,
+ FWCTL_CXL_SEND_COMMAND,
+};
+
+/**
+ * struct fwctl_info_cxl - ioctl(FWCTL_INFO) out_device_data
+ * @uctx_caps: The command capabilities driver accepts.
+ *
+ * Return basic information about the FW interface available.
+ */
+struct fwctl_info_cxl {
+ __u32 uctx_caps;
+};
+
+/*
+ * CXL spec r3.1 Table 8-101 Set Feature Input Payload
+ */
+struct set_feature_input {
+ __u8 uuid[16];
+ __u32 flags;
+ __u16 offset;
+ __u8 version;
+ __u8 reserved[9];
+ __u8 data[];
+} __packed;
+
+/**
+ * struct cxl_send_command - Send a command to a memory device.
+ * @id: The command to send to the memory device. This must be one of the
+ * commands returned by the query command.
+ * @flags: Flags for the command (input).
+ * @raw: Special fields for raw commands
+ * @raw.opcode: Opcode passed to hardware when using the RAW command.
+ * @raw.rsvd: Must be zero.
+ * @rsvd: Must be zero.
+ * @retval: Return value from the memory device (output).
+ * @in: Parameters associated with input payload.
+ * @in.size: Size of the payload to provide to the device (input).
+ * @in.rsvd: Must be zero.
+ * @in.payload: Pointer to memory for payload input, payload is little endian.
+ *
+ * Output payload is defined with 'struct fwctl_rpc' and is the hardware output
+ */
+struct fwctl_cxl_command {
+ __u32 id;
+ __u32 flags;
+ union {
+ struct {
+ __u16 opcode;
+ __u16 rsvd;
+ } raw;
+ __u32 rsvd;
+ };
+
+ struct {
+ __u32 size;
+ __u32 rsvd;
+ __u64 payload;
+ } in;
+};
+
+/**
+ * struct fwctl_rpc_cxl - ioctl(FWCTL_RPC) input
+ */
+struct fwctl_rpc_cxl {
+ __u32 rpc_cmd;
+ __u32 payload_size;
+ __u32 version;
+ __u32 rsvd;
+ union {
+ struct cxl_mem_query_commands query;
+ struct fwctl_cxl_command send_cmd;
+ };
+};
+
+struct fwctl_rpc_cxl_out {
+ __u32 retval;
+ __u32 rsvd;
+ __u8 payload[];
+};
+
+#endif
@@ -43,6 +43,7 @@ enum {
enum fwctl_device_type {
FWCTL_DEVICE_TYPE_ERROR = 0,
FWCTL_DEVICE_TYPE_MLX5 = 1,
+ FWCTL_DEVICE_TYPE_CXL = 2,
};
/**