diff mbox series

[v6,02/14] cxl: Add Get Supported Features command for kernel usage

Message ID 20250218225721.2682235-3-dave.jiang@intel.com
State Superseded
Headers show
Series cxl: Add CXL feature commands support via fwctl | expand

Commit Message

Dave Jiang Feb. 18, 2025, 10:54 p.m. UTC
CXL spec r3.2 8.2.9.6.1 Get Supported Features (Opcode 0500h)
The command retrieve the list of supported device-specific features
(identified by UUID) and general information about each Feature.

The driver will retrieve the Feature entries in order to make checks and
provide information for the Get Feature and Set Feature command. One of
the main piece of information retrieved are the effects a Set Feature
command would have for a particular feature. The retrieved Feature
entries are stored in the cxl_mailbox context.

The setup of Features is initiated via devm_cxl_setup_features() during the
pci probe function before the cxl_memdev is enumerated.

Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Li Ming <ming.li@zohomail.com>
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
---
v6:
- Remove @cap comment from 'cxl_features_state' (Shiju)
---
 drivers/cxl/Kconfig          |  11 +++
 drivers/cxl/core/Makefile    |   1 +
 drivers/cxl/core/features.c  | 175 +++++++++++++++++++++++++++++++++++
 drivers/cxl/cxlmem.h         |   4 +
 drivers/cxl/pci.c            |   4 +
 include/cxl/features.h       |  87 +++++++++++++++++
 tools/testing/cxl/Kbuild     |   1 +
 tools/testing/cxl/test/mem.c |   4 +
 8 files changed, 287 insertions(+)
 create mode 100644 drivers/cxl/core/features.c

Comments

Shiju Jose Feb. 20, 2025, 3:37 p.m. UTC | #1
>-----Original Message-----
>From: Dave Jiang <dave.jiang@intel.com>
>Sent: 18 February 2025 22:55
>To: linux-cxl@vger.kernel.org
>Cc: dan.j.williams@intel.com; ira.weiny@intel.com; vishal.l.verma@intel.com;
>alison.schofield@intel.com; Jonathan Cameron
><jonathan.cameron@huawei.com>; dave@stgolabs.net; jgg@nvidia.com; Shiju
>Jose <shiju.jose@huawei.com>; saeed@kernel.org; Li Ming
><ming.li@zohomail.com>
>Subject: [PATCH v6 02/14] cxl: Add Get Supported Features command for kernel
>usage
>
>CXL spec r3.2 8.2.9.6.1 Get Supported Features (Opcode 0500h) The command
>retrieve the list of supported device-specific features (identified by UUID) and
>general information about each Feature.
>
>The driver will retrieve the Feature entries in order to make checks and provide
>information for the Get Feature and Set Feature command. One of the main
>piece of information retrieved are the effects a Set Feature command would
>have for a particular feature. The retrieved Feature entries are stored in the
>cxl_mailbox context.
>
>The setup of Features is initiated via devm_cxl_setup_features() during the pci
>probe function before the cxl_memdev is enumerated.
>
>Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
>Reviewed-by: Dan Williams <dan.j.williams@intel.com>
>Reviewed-by: Li Ming <ming.li@zohomail.com>
>Signed-off-by: Dave Jiang <dave.jiang@intel.com>

Tested-by: Shiju Jose <shiju.jose@huawei.com>

>---
>v6:
>- Remove @cap comment from 'cxl_features_state' (Shiju)
>---
> drivers/cxl/Kconfig          |  11 +++
> drivers/cxl/core/Makefile    |   1 +
> drivers/cxl/core/features.c  | 175 +++++++++++++++++++++++++++++++++++
> drivers/cxl/cxlmem.h         |   4 +
> drivers/cxl/pci.c            |   4 +
> include/cxl/features.h       |  87 +++++++++++++++++
> tools/testing/cxl/Kbuild     |   1 +
> tools/testing/cxl/test/mem.c |   4 +
> 8 files changed, 287 insertions(+)
> create mode 100644 drivers/cxl/core/features.c
>
>diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig index
>876469e23f7a..ad2e796e4ac6 100644
>--- a/drivers/cxl/Kconfig
>+++ b/drivers/cxl/Kconfig
>@@ -102,6 +102,17 @@ config CXL_MEM
>
> 	  If unsure say 'm'.
>
>+config CXL_FEATURES
>+	bool "CXL: Features"
>+	depends on CXL_PCI
>+	help
>+	  Enable support for CXL Features. A CXL device that includes a mailbox
>+	  supports commands that allows listing, getting, and setting of
>+	  optionally defined features such as memory sparing or post package
>+	  sparing. Vendors may define custom features for the device.
>+
>+	  If unsure say 'n'
>+
> config CXL_PORT
> 	default CXL_BUS
> 	tristate
>diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile index
>9259bcc6773c..b0bfbd9eac9b 100644
>--- a/drivers/cxl/core/Makefile
>+++ b/drivers/cxl/core/Makefile
>@@ -16,3 +16,4 @@ cxl_core-y += pmu.o
> cxl_core-y += cdat.o
> cxl_core-$(CONFIG_TRACING) += trace.o
> cxl_core-$(CONFIG_CXL_REGION) += region.o
>+cxl_core-$(CONFIG_CXL_FEATURES) += features.o
>diff --git a/drivers/cxl/core/features.c b/drivers/cxl/core/features.c new file
>mode 100644 index 000000000000..f2898a56bd63
>--- /dev/null
>+++ b/drivers/cxl/core/features.c
>@@ -0,0 +1,175 @@
>+// SPDX-License-Identifier: GPL-2.0-only
>+/* Copyright(c) 2024-2025 Intel Corporation. All rights reserved. */
>+#include <linux/device.h> #include <cxl/mailbox.h> #include
>+<cxl/features.h> #include "cxl.h"
>+#include "cxlmem.h"
>+
>+inline struct cxl_features_state *to_cxlfs(struct cxl_dev_state *cxlds)
>+{
>+	return cxlds->cxlfs;
>+}
>+EXPORT_SYMBOL_NS_GPL(to_cxlfs, "CXL");
>+
>+static int cxl_get_supported_features_count(struct cxl_mailbox
>+*cxl_mbox) {
>+	struct cxl_mbox_get_sup_feats_out mbox_out;
>+	struct cxl_mbox_get_sup_feats_in mbox_in;
>+	struct cxl_mbox_cmd mbox_cmd;
>+	int rc;
>+
>+	memset(&mbox_in, 0, sizeof(mbox_in));
>+	mbox_in.count = cpu_to_le32(sizeof(mbox_out));
>+	memset(&mbox_out, 0, sizeof(mbox_out));
>+	mbox_cmd = (struct cxl_mbox_cmd) {
>+		.opcode = CXL_MBOX_OP_GET_SUPPORTED_FEATURES,
>+		.size_in = sizeof(mbox_in),
>+		.payload_in = &mbox_in,
>+		.size_out = sizeof(mbox_out),
>+		.payload_out = &mbox_out,
>+		.min_out = sizeof(mbox_out),
>+	};
>+	rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd);
>+	if (rc < 0)
>+		return rc;
>+
>+	return le16_to_cpu(mbox_out.supported_feats);
>+}
>+
>+static struct cxl_feat_entries *
>+get_supported_features(struct cxl_features_state *cxlfs) {
>+	int remain_feats, max_size, max_feats, start, rc, hdr_size;
>+	struct cxl_mailbox *cxl_mbox = &cxlfs->cxlds->cxl_mbox;
>+	int feat_size = sizeof(struct cxl_feat_entry);
>+	struct cxl_mbox_get_sup_feats_in mbox_in;
>+	struct cxl_feat_entry *entry;
>+	struct cxl_mbox_cmd mbox_cmd;
>+	int count;
>+
>+	count = cxl_get_supported_features_count(cxl_mbox);
>+	if (count <= 0)
>+		return NULL;
>+
>+	struct cxl_feat_entries *entries __free(kvfree) =
>+		kvmalloc(struct_size(entries, ent, count), GFP_KERNEL);
>+	if (!entries)
>+		return NULL;
>+
>+	struct cxl_mbox_get_sup_feats_out *mbox_out __free(kvfree) =
>+		kvmalloc(cxl_mbox->payload_size, GFP_KERNEL);
>+	if (!mbox_out)
>+		return NULL;
>+
>+	hdr_size = struct_size(mbox_out, ents, 0);
>+	max_size = cxl_mbox->payload_size - hdr_size;
>+	/* max feat entries that can fit in mailbox max payload size */
>+	max_feats = max_size / feat_size;
>+	entry = entries->ent;
>+
>+	start = 0;
>+	remain_feats = count;
>+	do {
>+		int retrieved, alloc_size, copy_feats;
>+		int num_entries;
>+
>+		if (remain_feats > max_feats) {
>+			alloc_size = struct_size(mbox_out, ents, max_feats);
>+			remain_feats = remain_feats - max_feats;
>+			copy_feats = max_feats;
>+		} else {
>+			alloc_size = struct_size(mbox_out, ents, remain_feats);
>+			copy_feats = remain_feats;
>+			remain_feats = 0;
>+		}
>+
>+		memset(&mbox_in, 0, sizeof(mbox_in));
>+		mbox_in.count = cpu_to_le32(alloc_size);
>+		mbox_in.start_idx = cpu_to_le16(start);
>+		memset(mbox_out, 0, alloc_size);
>+		mbox_cmd = (struct cxl_mbox_cmd) {
>+			.opcode = CXL_MBOX_OP_GET_SUPPORTED_FEATURES,
>+			.size_in = sizeof(mbox_in),
>+			.payload_in = &mbox_in,
>+			.size_out = alloc_size,
>+			.payload_out = mbox_out,
>+			.min_out = hdr_size,
>+		};
>+		rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd);
>+		if (rc < 0)
>+			return NULL;
>+
>+		if (mbox_cmd.size_out <= hdr_size)
>+			return NULL;
>+
>+		/*
>+		 * Make sure retrieved out buffer is multiple of feature
>+		 * entries.
>+		 */
>+		retrieved = mbox_cmd.size_out - hdr_size;
>+		if (retrieved % feat_size)
>+			return NULL;
>+
>+		num_entries = le16_to_cpu(mbox_out->num_entries);
>+		/*
>+		 * If the reported output entries * defined entry size !=
>+		 * retrieved output bytes, then the output package is incorrect.
>+		 */
>+		if (num_entries * feat_size != retrieved)
>+			return NULL;
>+
>+		memcpy(entry, mbox_out->ents, retrieved);
>+		entry += num_entries;
>+		/*
>+		 * If the number of output entries is less than expected, add the
>+		 * remaining entries to the next batch.
>+		 */
>+		remain_feats += copy_feats - num_entries;
>+		start += num_entries;
>+	} while (remain_feats);
>+
>+	entries->num_features = count;
>+
>+	return no_free_ptr(entries);
>+}
>+
>+static void free_cxlfs(void *_cxlfs)
>+{
>+	struct cxl_features_state *cxlfs = _cxlfs;
>+	struct cxl_dev_state *cxlds = cxlfs->cxlds;
>+
>+	cxlds->cxlfs = NULL;
>+	kvfree(cxlfs->entries);
>+	kfree(cxlfs);
>+}
>+
>+/**
>+ * devm_cxl_setup_features() - Allocate and initialize features context
>+ * @cxlds: CXL device context
>+ *
>+ * Return 0 on success or -errno on failure.
>+ */
>+int devm_cxl_setup_features(struct cxl_dev_state *cxlds) {
>+	struct cxl_mailbox *cxl_mbox = &cxlds->cxl_mbox;
>+
>+	if (cxl_mbox->feat_cap < CXL_FEATURES_RO)
>+		return -ENODEV;
>+
>+	struct cxl_features_state *cxlfs __free(kfree) =
>+		kzalloc(sizeof(*cxlfs), GFP_KERNEL);
>+	if (!cxlfs)
>+		return -ENOMEM;
>+
>+	cxlfs->cxlds = cxlds;
>+
>+	cxlfs->entries = get_supported_features(cxlfs);
>+	if (!cxlfs->entries)
>+		return -ENOMEM;
>+
>+	cxlds->cxlfs = cxlfs;
>+
>+	return devm_add_action_or_reset(cxlds->dev, free_cxlfs,
>+no_free_ptr(cxlfs)); } EXPORT_SYMBOL_NS_GPL(devm_cxl_setup_features,
>+"CXL");
>diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h index
>55c55685cb39..0dc2682eb379 100644
>--- a/drivers/cxl/cxlmem.h
>+++ b/drivers/cxl/cxlmem.h
>@@ -392,6 +392,7 @@ struct cxl_dpa_perf {
>  * @serial: PCIe Device Serial Number
>  * @type: Generic Memory Class device or Vendor Specific Memory device
>  * @cxl_mbox: CXL mailbox context
>+ * @cxl_features: CXL features context
>  */
> struct cxl_dev_state {
> 	struct device *dev;
>@@ -407,6 +408,9 @@ struct cxl_dev_state {
> 	u64 serial;
> 	enum cxl_devtype type;
> 	struct cxl_mailbox cxl_mbox;
>+#ifdef CONFIG_CXL_FEATURES
>+	struct cxl_features_state *cxlfs;
>+#endif
> };
>
> static inline struct cxl_dev_state *mbox_to_cxlds(struct cxl_mailbox *cxl_mbox)
>diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c index
>a96e54c6259e..3e666ec51580 100644
>--- a/drivers/cxl/pci.c
>+++ b/drivers/cxl/pci.c
>@@ -997,6 +997,10 @@ static int cxl_pci_probe(struct pci_dev *pdev, const
>struct pci_device_id *id)
> 	if (rc)
> 		return rc;
>
>+	rc = devm_cxl_setup_features(cxlds);
>+	if (rc)
>+		dev_dbg(&pdev->dev, "No CXL Features discovered\n");
>+
> 	cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlds);
> 	if (IS_ERR(cxlmd))
> 		return PTR_ERR(cxlmd);
>diff --git a/include/cxl/features.h b/include/cxl/features.h index
>357d3acf8429..5b3485eb0f77 100644
>--- a/include/cxl/features.h
>+++ b/include/cxl/features.h
>@@ -3,6 +3,8 @@
> #ifndef __CXL_FEATURES_H__
> #define __CXL_FEATURES_H__
>
>+#include <linux/uuid.h>
>+
> /* Feature commands capability supported by a device */  enum
>cxl_features_capability {
> 	CXL_FEATURES_NONE = 0,
>@@ -10,4 +12,89 @@ enum cxl_features_capability {
> 	CXL_FEATURES_RW,
> };
>
>+/* Get Supported Features (0x500h) CXL r3.2 8.2.9.6.1 */ struct
>+cxl_mbox_get_sup_feats_in {
>+	__le32 count;
>+	__le16 start_idx;
>+	u8 reserved[2];
>+} __packed;
>+
>+/* CXL spec r3.2 Table 8-87 command effects */
>+#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_EFFECTS_VALID			BIT(9)
>+#define CXL_CMD_CONFIG_CHANGE_CONV_RESET	BIT(10)
>+#define CXL_CMD_CONFIG_CHANGE_CXL_RESET		BIT(11)
>+
>+/*
>+ * CXL spec r3.2 Table 8-109
>+ * Get Supported Features Supported Feature Entry  */ 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;
>+
>+/* @flags field for 'struct cxl_feat_entry' */
>+#define CXL_FEATURE_F_CHANGEABLE		BIT(0)
>+#define CXL_FEATURE_F_PERSIST_FW_UPDATE		BIT(4)
>+#define CXL_FEATURE_F_DEFAULT_SEL		BIT(5)
>+#define CXL_FEATURE_F_SAVED_SEL			BIT(6)
>+
>+/*
>+ * CXL spec r3.2 Table 8-108
>+ * Get supported Features Output Payload  */ struct
>+cxl_mbox_get_sup_feats_out {
>+	__struct_group(cxl_mbox_get_sup_feats_out_hdr, hdr, /* no attrs */,
>+		__le16 num_entries;
>+		__le16 supported_feats;
>+		__u8 reserved[4];
>+	);
>+	struct cxl_feat_entry ents[] __counted_by_le(num_entries); } __packed;
>+
>+/**
>+ * struct cxl_features_state - The Features state for the device
>+ * @cxlds: Pointer to CXL device state
>+ * @entries: CXl feature entry context
>+ *	@num_features: total Features supported by the device
>+ *	@ent: Flex array of Feature detail entries from the device
>+ */
>+struct cxl_features_state {
>+	struct cxl_dev_state *cxlds;
>+	struct cxl_feat_entries {
>+		int num_features;
>+		struct cxl_feat_entry ent[] __counted_by(num_features);
>+	} *entries;
>+};
>+
>+#ifdef CONFIG_CXL_FEATURES
>+inline struct cxl_features_state *to_cxlfs(struct cxl_dev_state
>+*cxlds); int devm_cxl_setup_features(struct cxl_dev_state *cxlds);
>+#else static inline struct cxl_features_state *to_cxlfs(struct
>+cxl_dev_state *cxlds) {
>+	return NULL;
>+}
>+
>+static inline int devm_cxl_setup_features(struct cxl_dev_state *cxlds)
>+{
>+	return -EOPNOTSUPP;
>+}
>+#endif
>+
> #endif
>diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild index
>b1256fee3567..0a6572ab6f37 100644
>--- a/tools/testing/cxl/Kbuild
>+++ b/tools/testing/cxl/Kbuild
>@@ -63,6 +63,7 @@ cxl_core-y += $(CXL_CORE_SRC)/pmu.o  cxl_core-y +=
>$(CXL_CORE_SRC)/cdat.o
> cxl_core-$(CONFIG_TRACING) += $(CXL_CORE_SRC)/trace.o
> cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o
>+cxl_core-$(CONFIG_CXL_FEATURES) += $(CXL_CORE_SRC)/features.o
> cxl_core-y += config_check.o
> cxl_core-y += cxl_core_test.o
> cxl_core-y += cxl_core_exports.o
>diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c index
>8d731bd63988..e9494cd446ef 100644
>--- a/tools/testing/cxl/test/mem.c
>+++ b/tools/testing/cxl/test/mem.c
>@@ -1558,6 +1558,10 @@ static int cxl_mock_mem_probe(struct
>platform_device *pdev)
> 	if (rc)
> 		return rc;
>
>+	rc = devm_cxl_setup_features(cxlds);
>+	if (rc)
>+		dev_dbg(dev, "No CXL Features discovered\n");
>+
> 	cxl_mock_add_event_logs(&mdata->mes);
>
> 	cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlds);
>--
>2.48.1
diff mbox series

Patch

diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig
index 876469e23f7a..ad2e796e4ac6 100644
--- a/drivers/cxl/Kconfig
+++ b/drivers/cxl/Kconfig
@@ -102,6 +102,17 @@  config CXL_MEM
 
 	  If unsure say 'm'.
 
+config CXL_FEATURES
+	bool "CXL: Features"
+	depends on CXL_PCI
+	help
+	  Enable support for CXL Features. A CXL device that includes a mailbox
+	  supports commands that allows listing, getting, and setting of
+	  optionally defined features such as memory sparing or post package
+	  sparing. Vendors may define custom features for the device.
+
+	  If unsure say 'n'
+
 config CXL_PORT
 	default CXL_BUS
 	tristate
diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile
index 9259bcc6773c..b0bfbd9eac9b 100644
--- a/drivers/cxl/core/Makefile
+++ b/drivers/cxl/core/Makefile
@@ -16,3 +16,4 @@  cxl_core-y += pmu.o
 cxl_core-y += cdat.o
 cxl_core-$(CONFIG_TRACING) += trace.o
 cxl_core-$(CONFIG_CXL_REGION) += region.o
+cxl_core-$(CONFIG_CXL_FEATURES) += features.o
diff --git a/drivers/cxl/core/features.c b/drivers/cxl/core/features.c
new file mode 100644
index 000000000000..f2898a56bd63
--- /dev/null
+++ b/drivers/cxl/core/features.c
@@ -0,0 +1,175 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright(c) 2024-2025 Intel Corporation. All rights reserved. */
+#include <linux/device.h>
+#include <cxl/mailbox.h>
+#include <cxl/features.h>
+#include "cxl.h"
+#include "cxlmem.h"
+
+inline struct cxl_features_state *to_cxlfs(struct cxl_dev_state *cxlds)
+{
+	return cxlds->cxlfs;
+}
+EXPORT_SYMBOL_NS_GPL(to_cxlfs, "CXL");
+
+static int cxl_get_supported_features_count(struct cxl_mailbox *cxl_mbox)
+{
+	struct cxl_mbox_get_sup_feats_out mbox_out;
+	struct cxl_mbox_get_sup_feats_in mbox_in;
+	struct cxl_mbox_cmd mbox_cmd;
+	int rc;
+
+	memset(&mbox_in, 0, sizeof(mbox_in));
+	mbox_in.count = cpu_to_le32(sizeof(mbox_out));
+	memset(&mbox_out, 0, sizeof(mbox_out));
+	mbox_cmd = (struct cxl_mbox_cmd) {
+		.opcode = CXL_MBOX_OP_GET_SUPPORTED_FEATURES,
+		.size_in = sizeof(mbox_in),
+		.payload_in = &mbox_in,
+		.size_out = sizeof(mbox_out),
+		.payload_out = &mbox_out,
+		.min_out = sizeof(mbox_out),
+	};
+	rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd);
+	if (rc < 0)
+		return rc;
+
+	return le16_to_cpu(mbox_out.supported_feats);
+}
+
+static struct cxl_feat_entries *
+get_supported_features(struct cxl_features_state *cxlfs)
+{
+	int remain_feats, max_size, max_feats, start, rc, hdr_size;
+	struct cxl_mailbox *cxl_mbox = &cxlfs->cxlds->cxl_mbox;
+	int feat_size = sizeof(struct cxl_feat_entry);
+	struct cxl_mbox_get_sup_feats_in mbox_in;
+	struct cxl_feat_entry *entry;
+	struct cxl_mbox_cmd mbox_cmd;
+	int count;
+
+	count = cxl_get_supported_features_count(cxl_mbox);
+	if (count <= 0)
+		return NULL;
+
+	struct cxl_feat_entries *entries __free(kvfree) =
+		kvmalloc(struct_size(entries, ent, count), GFP_KERNEL);
+	if (!entries)
+		return NULL;
+
+	struct cxl_mbox_get_sup_feats_out *mbox_out __free(kvfree) =
+		kvmalloc(cxl_mbox->payload_size, GFP_KERNEL);
+	if (!mbox_out)
+		return NULL;
+
+	hdr_size = struct_size(mbox_out, ents, 0);
+	max_size = cxl_mbox->payload_size - hdr_size;
+	/* max feat entries that can fit in mailbox max payload size */
+	max_feats = max_size / feat_size;
+	entry = entries->ent;
+
+	start = 0;
+	remain_feats = count;
+	do {
+		int retrieved, alloc_size, copy_feats;
+		int num_entries;
+
+		if (remain_feats > max_feats) {
+			alloc_size = struct_size(mbox_out, ents, max_feats);
+			remain_feats = remain_feats - max_feats;
+			copy_feats = max_feats;
+		} else {
+			alloc_size = struct_size(mbox_out, ents, remain_feats);
+			copy_feats = remain_feats;
+			remain_feats = 0;
+		}
+
+		memset(&mbox_in, 0, sizeof(mbox_in));
+		mbox_in.count = cpu_to_le32(alloc_size);
+		mbox_in.start_idx = cpu_to_le16(start);
+		memset(mbox_out, 0, alloc_size);
+		mbox_cmd = (struct cxl_mbox_cmd) {
+			.opcode = CXL_MBOX_OP_GET_SUPPORTED_FEATURES,
+			.size_in = sizeof(mbox_in),
+			.payload_in = &mbox_in,
+			.size_out = alloc_size,
+			.payload_out = mbox_out,
+			.min_out = hdr_size,
+		};
+		rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd);
+		if (rc < 0)
+			return NULL;
+
+		if (mbox_cmd.size_out <= hdr_size)
+			return NULL;
+
+		/*
+		 * Make sure retrieved out buffer is multiple of feature
+		 * entries.
+		 */
+		retrieved = mbox_cmd.size_out - hdr_size;
+		if (retrieved % feat_size)
+			return NULL;
+
+		num_entries = le16_to_cpu(mbox_out->num_entries);
+		/*
+		 * If the reported output entries * defined entry size !=
+		 * retrieved output bytes, then the output package is incorrect.
+		 */
+		if (num_entries * feat_size != retrieved)
+			return NULL;
+
+		memcpy(entry, mbox_out->ents, retrieved);
+		entry += num_entries;
+		/*
+		 * If the number of output entries is less than expected, add the
+		 * remaining entries to the next batch.
+		 */
+		remain_feats += copy_feats - num_entries;
+		start += num_entries;
+	} while (remain_feats);
+
+	entries->num_features = count;
+
+	return no_free_ptr(entries);
+}
+
+static void free_cxlfs(void *_cxlfs)
+{
+	struct cxl_features_state *cxlfs = _cxlfs;
+	struct cxl_dev_state *cxlds = cxlfs->cxlds;
+
+	cxlds->cxlfs = NULL;
+	kvfree(cxlfs->entries);
+	kfree(cxlfs);
+}
+
+/**
+ * devm_cxl_setup_features() - Allocate and initialize features context
+ * @cxlds: CXL device context
+ *
+ * Return 0 on success or -errno on failure.
+ */
+int devm_cxl_setup_features(struct cxl_dev_state *cxlds)
+{
+	struct cxl_mailbox *cxl_mbox = &cxlds->cxl_mbox;
+
+	if (cxl_mbox->feat_cap < CXL_FEATURES_RO)
+		return -ENODEV;
+
+	struct cxl_features_state *cxlfs __free(kfree) =
+		kzalloc(sizeof(*cxlfs), GFP_KERNEL);
+	if (!cxlfs)
+		return -ENOMEM;
+
+	cxlfs->cxlds = cxlds;
+
+	cxlfs->entries = get_supported_features(cxlfs);
+	if (!cxlfs->entries)
+		return -ENOMEM;
+
+	cxlds->cxlfs = cxlfs;
+
+	return devm_add_action_or_reset(cxlds->dev, free_cxlfs, no_free_ptr(cxlfs));
+}
+EXPORT_SYMBOL_NS_GPL(devm_cxl_setup_features, "CXL");
diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index 55c55685cb39..0dc2682eb379 100644
--- a/drivers/cxl/cxlmem.h
+++ b/drivers/cxl/cxlmem.h
@@ -392,6 +392,7 @@  struct cxl_dpa_perf {
  * @serial: PCIe Device Serial Number
  * @type: Generic Memory Class device or Vendor Specific Memory device
  * @cxl_mbox: CXL mailbox context
+ * @cxl_features: CXL features context
  */
 struct cxl_dev_state {
 	struct device *dev;
@@ -407,6 +408,9 @@  struct cxl_dev_state {
 	u64 serial;
 	enum cxl_devtype type;
 	struct cxl_mailbox cxl_mbox;
+#ifdef CONFIG_CXL_FEATURES
+	struct cxl_features_state *cxlfs;
+#endif
 };
 
 static inline struct cxl_dev_state *mbox_to_cxlds(struct cxl_mailbox *cxl_mbox)
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index a96e54c6259e..3e666ec51580 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -997,6 +997,10 @@  static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	if (rc)
 		return rc;
 
+	rc = devm_cxl_setup_features(cxlds);
+	if (rc)
+		dev_dbg(&pdev->dev, "No CXL Features discovered\n");
+
 	cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlds);
 	if (IS_ERR(cxlmd))
 		return PTR_ERR(cxlmd);
diff --git a/include/cxl/features.h b/include/cxl/features.h
index 357d3acf8429..5b3485eb0f77 100644
--- a/include/cxl/features.h
+++ b/include/cxl/features.h
@@ -3,6 +3,8 @@ 
 #ifndef __CXL_FEATURES_H__
 #define __CXL_FEATURES_H__
 
+#include <linux/uuid.h>
+
 /* Feature commands capability supported by a device */
 enum cxl_features_capability {
 	CXL_FEATURES_NONE = 0,
@@ -10,4 +12,89 @@  enum cxl_features_capability {
 	CXL_FEATURES_RW,
 };
 
+/* Get Supported Features (0x500h) CXL r3.2 8.2.9.6.1 */
+struct cxl_mbox_get_sup_feats_in {
+	__le32 count;
+	__le16 start_idx;
+	u8 reserved[2];
+} __packed;
+
+/* CXL spec r3.2 Table 8-87 command effects */
+#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_EFFECTS_VALID			BIT(9)
+#define CXL_CMD_CONFIG_CHANGE_CONV_RESET	BIT(10)
+#define CXL_CMD_CONFIG_CHANGE_CXL_RESET		BIT(11)
+
+/*
+ * CXL spec r3.2 Table 8-109
+ * Get Supported Features Supported Feature Entry
+ */
+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;
+
+/* @flags field for 'struct cxl_feat_entry' */
+#define CXL_FEATURE_F_CHANGEABLE		BIT(0)
+#define CXL_FEATURE_F_PERSIST_FW_UPDATE		BIT(4)
+#define CXL_FEATURE_F_DEFAULT_SEL		BIT(5)
+#define CXL_FEATURE_F_SAVED_SEL			BIT(6)
+
+/*
+ * CXL spec r3.2 Table 8-108
+ * Get supported Features Output Payload
+ */
+struct cxl_mbox_get_sup_feats_out {
+	__struct_group(cxl_mbox_get_sup_feats_out_hdr, hdr, /* no attrs */,
+		__le16 num_entries;
+		__le16 supported_feats;
+		__u8 reserved[4];
+	);
+	struct cxl_feat_entry ents[] __counted_by_le(num_entries);
+} __packed;
+
+/**
+ * struct cxl_features_state - The Features state for the device
+ * @cxlds: Pointer to CXL device state
+ * @entries: CXl feature entry context
+ *	@num_features: total Features supported by the device
+ *	@ent: Flex array of Feature detail entries from the device
+ */
+struct cxl_features_state {
+	struct cxl_dev_state *cxlds;
+	struct cxl_feat_entries {
+		int num_features;
+		struct cxl_feat_entry ent[] __counted_by(num_features);
+	} *entries;
+};
+
+#ifdef CONFIG_CXL_FEATURES
+inline struct cxl_features_state *to_cxlfs(struct cxl_dev_state *cxlds);
+int devm_cxl_setup_features(struct cxl_dev_state *cxlds);
+#else
+static inline struct cxl_features_state *to_cxlfs(struct cxl_dev_state *cxlds)
+{
+	return NULL;
+}
+
+static inline int devm_cxl_setup_features(struct cxl_dev_state *cxlds)
+{
+	return -EOPNOTSUPP;
+}
+#endif
+
 #endif
diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild
index b1256fee3567..0a6572ab6f37 100644
--- a/tools/testing/cxl/Kbuild
+++ b/tools/testing/cxl/Kbuild
@@ -63,6 +63,7 @@  cxl_core-y += $(CXL_CORE_SRC)/pmu.o
 cxl_core-y += $(CXL_CORE_SRC)/cdat.o
 cxl_core-$(CONFIG_TRACING) += $(CXL_CORE_SRC)/trace.o
 cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o
+cxl_core-$(CONFIG_CXL_FEATURES) += $(CXL_CORE_SRC)/features.o
 cxl_core-y += config_check.o
 cxl_core-y += cxl_core_test.o
 cxl_core-y += cxl_core_exports.o
diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c
index 8d731bd63988..e9494cd446ef 100644
--- a/tools/testing/cxl/test/mem.c
+++ b/tools/testing/cxl/test/mem.c
@@ -1558,6 +1558,10 @@  static int cxl_mock_mem_probe(struct platform_device *pdev)
 	if (rc)
 		return rc;
 
+	rc = devm_cxl_setup_features(cxlds);
+	if (rc)
+		dev_dbg(dev, "No CXL Features discovered\n");
+
 	cxl_mock_add_event_logs(&mdata->mes);
 
 	cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlds);