@@ -51,6 +51,7 @@
#define CDAT_DSMAS_DPA_OFFSET(entry) ((u64)((entry)[3]) << 32 | (entry)[2])
#define CDAT_DSMAS_DPA_LEN(entry) ((u64)((entry)[5]) << 32 | (entry)[4])
#define CDAT_DSMAS_NON_VOLATILE(flags) ((flags & 0x04) >> 2)
+#define CDAT_DSMAS_ENTRY_SIZE (6 * sizeof(u32))
/* Device Scoped Latency and Bandwidth Information Structure */
#define CDAT_DSLBIS_DW1_HANDLE 0x000000ff
@@ -60,22 +61,26 @@
#define CDAT_DSLBIS_DW4_ENTRY_0 0x0000ffff
#define CDAT_DSLBIS_DW4_ENTRY_1 0xffff0000
#define CDAT_DSLBIS_DW5_ENTRY_2 0x0000ffff
+#define CDAT_DSLBIS_ENTRY_SIZE (6 * sizeof(u32))
/* Device Scoped Memory Side Cache Information Structure */
#define CDAT_DSMSCIS_DW1_HANDLE 0x000000ff
#define CDAT_DSMSCIS_MEMORY_SIDE_CACHE_SIZE(entry) \
((u64)((entry)[3]) << 32 | (entry)[2])
#define CDAT_DSMSCIS_DW4_MEMORY_SIDE_CACHE_ATTRS 0xffffffff
+#define CDAT_DSMSCIS_ENTRY_SIZE (5 * sizeof(u32))
/* Device Scoped Initiator Structure */
#define CDAT_DSIS_DW1_FLAGS 0x000000ff
#define CDAT_DSIS_DW1_HANDLE 0x0000ff00
+#define CDAT_DSIS_ENTRY_SIZE (2 * sizeof(u32))
/* Device Scoped EFI Memory Type Structure */
#define CDAT_DSEMTS_DW1_HANDLE 0x000000ff
#define CDAT_DSEMTS_DW1_EFI_MEMORY_TYPE_ATTR 0x0000ff00
#define CDAT_DSEMTS_DPA_OFFSET(entry) ((u64)((entry)[3]) << 32 | (entry)[2])
#define CDAT_DSEMTS_DPA_LENGTH(entry) ((u64)((entry)[5]) << 32 | (entry)[4])
+#define CDAT_DSEMTS_ENTRY_SIZE (6 * sizeof(u32))
/* Switch Scoped Latency and Bandwidth Information Structure */
#define CDAT_SSLBIS_DW1_DATA_TYPE 0x000000ff
@@ -83,9 +88,27 @@
#define CDAT_SSLBIS_ENTRY_PORT_X(entry, i) ((entry)[4 + (i) * 2] & 0x0000ffff)
#define CDAT_SSLBIS_ENTRY_PORT_Y(entry, i) (((entry)[4 + (i) * 2] & 0xffff0000) >> 16)
#define CDAT_SSLBIS_ENTRY_LAT_OR_BW(entry, i) ((entry)[4 + (i) * 2 + 1] & 0x0000ffff)
+#define CDAT_SSLBIS_HEADER_SIZE (6 * sizeof(u32))
#define CXL_DOE_PROTOCOL_TABLE_ACCESS 2
+/**
+ * struct cxl_dsmas - host unmarshaled version of DSMAS data
+ *
+ * As defined in the Coherent Device Attribute Table (CDAT) specification this
+ * represents a single DSMAS entry in that table.
+ *
+ * @dpa_base: The lowest Device Physical Address associated with this DSMAD
+ * @length: Length in bytes of this DSMAD
+ * @non_volatile: If set, the memory region represents Non-Volatile memory
+ */
+struct cxl_dsmas {
+ u64 dpa_base;
+ u64 length;
+ /* Flags */
+ u8 non_volatile:1;
+};
+
/**
* struct cxl_cdat - CXL CDAT data
*
@@ -688,3 +688,75 @@ void read_cdat_data(struct cxl_port *port)
retries);
}
EXPORT_SYMBOL_NS_GPL(read_cdat_data, CXL);
+
+void parse_dsmas(struct cxl_memdev *cxlmd, struct cxl_port *port)
+{
+ struct device *dev = &port->dev;
+ struct cxl_dsmas *dsmas_ary = NULL;
+ u32 *data = port->cdat.table;
+ int bytes_left = port->cdat.length;
+ int nr_dsmas = 0;
+
+ if (!data) {
+ dev_info(dev, "No CDAT data available for DSMAS\n");
+ return;
+ }
+
+ /* Skip header */
+ data += CDAT_HEADER_LENGTH_DW;
+ bytes_left -= CDAT_HEADER_LENGTH_BYTES;
+
+ while (bytes_left > 0) {
+ u32 *cur_rec = data;
+ u8 type = FIELD_GET(CDAT_STRUCTURE_DW0_TYPE, cur_rec[0]);
+ u16 length = FIELD_GET(CDAT_STRUCTURE_DW0_LENGTH, cur_rec[0]);
+
+ if (type == CDAT_STRUCTURE_DW0_TYPE_DSMAS) {
+ struct cxl_dsmas *new_ary;
+ u8 flags;
+
+ /* Protect against malicious devices */
+ if (bytes_left < CDAT_DSMAS_ENTRY_SIZE ||
+ length != CDAT_DSMAS_ENTRY_SIZE) {
+ dev_err(dev, "Invalid DSMAS data detected\n");
+ return;
+ }
+
+ new_ary = devm_krealloc(dev, dsmas_ary,
+ sizeof(*dsmas_ary) * (nr_dsmas + 1),
+ GFP_KERNEL);
+ if (!new_ary) {
+ dev_err(dev,
+ "Failed to allocate memory for DSMAS data (nr_dsmas %d)\n",
+ nr_dsmas);
+ return;
+ }
+ dsmas_ary = new_ary;
+
+ flags = FIELD_GET(CDAT_DSMAS_DW1_FLAGS, cur_rec[1]);
+
+ dsmas_ary[nr_dsmas].dpa_base = CDAT_DSMAS_DPA_OFFSET(cur_rec);
+ dsmas_ary[nr_dsmas].length = CDAT_DSMAS_DPA_LEN(cur_rec);
+ dsmas_ary[nr_dsmas].non_volatile = CDAT_DSMAS_NON_VOLATILE(flags);
+
+ dev_dbg(dev, "DSMAS %d: %llx:%llx %s\n",
+ nr_dsmas,
+ dsmas_ary[nr_dsmas].dpa_base,
+ dsmas_ary[nr_dsmas].dpa_base +
+ dsmas_ary[nr_dsmas].length,
+ (dsmas_ary[nr_dsmas].non_volatile ?
+ "Persistent" : "Volatile")
+ );
+
+ nr_dsmas++;
+ }
+
+ data += (length / sizeof(u32));
+ bytes_left -= length;
+ }
+
+ dev_dbg(dev, "Found %d DSMAS entries\n", nr_dsmas);
+ cxlmd->dsmas_ary = dsmas_ary;
+ cxlmd->nr_dsmas = nr_dsmas;
+}
+EXPORT_SYMBOL_NS_GPL(parse_dsmas, CXL);
@@ -10,6 +10,8 @@
#include <linux/io.h>
#include "cdat.h"
+#include "cdat.h"
+
/**
* DOC: cxl objects
*
@@ -36,6 +36,8 @@
* @cxlds: The device state backing this device
* @detach_work: active memdev lost a port in its ancestry
* @id: id number of this memdev instance.
+ * @dsmas_ary: Array of DSMAS entries as parsed from the CDAT table
+ * @nr_dsmas: Number of entries in dsmas_ary
*/
struct cxl_memdev {
struct device dev;
@@ -43,6 +45,8 @@ struct cxl_memdev {
struct cxl_dev_state *cxlds;
struct work_struct detach_work;
int id;
+ struct cxl_dsmas *dsmas_ary;
+ int nr_dsmas;
};
static inline struct cxl_memdev *to_cxl_memdev(struct device *dev)
@@ -75,4 +75,5 @@ int devm_cxl_port_enumerate_dports(struct cxl_port *port);
struct cxl_dev_state;
int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm);
void read_cdat_data(struct cxl_port *port);
+void parse_dsmas(struct cxl_memdev *cxlmd, struct cxl_port *port);
#endif /* __CXL_PCI_H__ */
@@ -35,6 +35,7 @@ static int create_endpoint(struct cxl_memdev *cxlmd,
if (IS_ERR(endpoint))
return PTR_ERR(endpoint);
+ parse_dsmas(cxlmd, endpoint);
dev_dbg(&cxlmd->dev, "add: %s\n", dev_name(&endpoint->dev));
if (!endpoint->dev.driver) {