@@ -7,4 +7,4 @@ obj-m += cxl_mock_mem.o
cxl_test-y := cxl.o
cxl_mock-y := mock.o
-cxl_mock_mem-y := mem.o
+cxl_mock_mem-y := mem.o events.o
new file mode 100644
@@ -0,0 +1,223 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright(c) 2022 Intel Corporation. All rights reserved.
+
+#include <cxlmem.h>
+#include <trace/events/cxl.h>
+
+#include "events.h"
+
+#define CXL_TEST_EVENT_CNT_MAX 15
+
+struct mock_event_log {
+ int cur_event;
+ int nr_events;
+ struct cxl_event_record_raw *events[CXL_TEST_EVENT_CNT_MAX];
+};
+
+struct mock_event_store {
+ struct cxl_dev_state *cxlds;
+ struct mock_event_log mock_logs[CXL_EVENT_TYPE_MAX];
+};
+
+DEFINE_XARRAY(mock_dev_event_store);
+
+struct mock_event_log *find_event_log(struct device *dev, int log_type)
+{
+ struct mock_event_store *mes = xa_load(&mock_dev_event_store,
+ (unsigned long)dev);
+
+ if (!mes || log_type >= CXL_EVENT_TYPE_MAX)
+ return NULL;
+ return &mes->mock_logs[log_type];
+}
+
+struct cxl_event_record_raw *get_cur_event(struct mock_event_log *log)
+{
+ return log->events[log->cur_event];
+}
+
+__le16 get_cur_event_handle(struct mock_event_log *log)
+{
+ return cpu_to_le16(log->cur_event);
+}
+
+static bool log_empty(struct mock_event_log *log)
+{
+ return log->cur_event == log->nr_events;
+}
+
+static int log_rec_left(struct mock_event_log *log)
+{
+ return log->nr_events - log->cur_event;
+}
+
+static void event_store_add_event(struct mock_event_store *mes,
+ enum cxl_event_log_type log_type,
+ struct cxl_event_record_raw *event)
+{
+ struct mock_event_log *log;
+
+ if (WARN_ON(log_type >= CXL_EVENT_TYPE_MAX))
+ return;
+
+ log = &mes->mock_logs[log_type];
+ if (WARN_ON(log->nr_events >= CXL_TEST_EVENT_CNT_MAX))
+ return;
+
+ log->events[log->nr_events] = event;
+ log->nr_events++;
+}
+
+int mock_get_event(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
+{
+ struct cxl_get_event_payload *pl;
+ struct mock_event_log *log;
+ u8 log_type;
+
+ /* Valid request? */
+ if (cmd->size_in != sizeof(log_type))
+ return -EINVAL;
+
+ log_type = *((u8 *)cmd->payload_in);
+ if (log_type >= CXL_EVENT_TYPE_MAX)
+ return -EINVAL;
+
+ log = find_event_log(cxlds->dev, log_type);
+ if (!log || log_empty(log))
+ goto no_data;
+
+ /* Don't handle more than 1 record at a time */
+ if (cmd->size_out < sizeof(*pl))
+ return -EINVAL;
+
+ pl = cmd->payload_out;
+ memset(pl, 0, sizeof(*pl));
+
+ pl->record_count = cpu_to_le16(1);
+
+ if (log_rec_left(log) > 1)
+ pl->flags |= CXL_GET_EVENT_FLAG_MORE_RECORDS;
+
+ memcpy(&pl->record, get_cur_event(log), sizeof(pl->record));
+ pl->record.hdr.handle = get_cur_event_handle(log);
+ return 0;
+
+no_data:
+ /* Room for header? */
+ if (cmd->size_out < (sizeof(*pl) - sizeof(pl->record)))
+ return -EINVAL;
+
+ memset(cmd->payload_out, 0, cmd->size_out);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mock_get_event);
+
+/*
+ * Get and clear event only handle 1 record at a time as this is what is
+ * currently implemented in the main code.
+ */
+int mock_clear_event(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
+{
+ struct cxl_mbox_clear_event_payload *pl = cmd->payload_in;
+ struct mock_event_log *log;
+ u8 log_type = pl->event_log;
+
+ /* Don't handle more than 1 record at a time */
+ if (pl->nr_recs != 1)
+ return -EINVAL;
+
+ if (log_type >= CXL_EVENT_TYPE_MAX)
+ return -EINVAL;
+
+ log = find_event_log(cxlds->dev, log_type);
+ if (!log)
+ return 0; /* No mock data in this log */
+
+ /*
+ * The current code clears events as they are read. Test that behavior
+ * only; don't support clearning from the middle of the log
+ */
+ if (log->cur_event != le16_to_cpu(pl->handle)) {
+ dev_err(cxlds->dev, "Clearing events out of order\n");
+ return -EINVAL;
+ }
+
+ log->cur_event++;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mock_clear_event);
+
+void cxl_mock_event_trigger(struct device *dev)
+{
+ struct mock_event_store *mes = xa_load(&mock_dev_event_store,
+ (unsigned long)dev);
+ int i;
+
+ for (i = CXL_EVENT_TYPE_INFO; i < CXL_EVENT_TYPE_MAX; i++) {
+ struct mock_event_log *log;
+
+ log = find_event_log(dev, i);
+ if (log)
+ log->cur_event = 0;
+ }
+
+ cxl_mem_get_event_records(mes->cxlds);
+}
+EXPORT_SYMBOL_GPL(cxl_mock_event_trigger);
+
+struct cxl_event_record_raw maint_needed = {
+ .hdr = {
+ .id = UUID_INIT(0xDEADBEEF, 0xCAFE, 0xBABE,
+ 0xa5, 0x5a, 0xa5, 0x5a, 0xa5, 0xa5, 0x5a, 0xa5),
+ .length = sizeof(struct cxl_event_record_raw),
+ .flags[0] = CXL_EVENT_RECORD_FLAG_MAINT_NEEDED,
+ /* .handle = Set dynamically */
+ .related_handle = cpu_to_le16(0xa5b6),
+ },
+ .data = { 0xDE, 0xAD, 0xBE, 0xEF },
+};
+
+struct cxl_event_record_raw hardware_replace = {
+ .hdr = {
+ .id = UUID_INIT(0xBABECAFE, 0xBEEF, 0xDEAD,
+ 0xa5, 0x5a, 0xa5, 0x5a, 0xa5, 0xa5, 0x5a, 0xa5),
+ .length = sizeof(struct cxl_event_record_raw),
+ .flags[0] = CXL_EVENT_RECORD_FLAG_HW_REPLACE,
+ /* .handle = Set dynamically */
+ .related_handle = cpu_to_le16(0xb6a5),
+ },
+ .data = { 0xDE, 0xAD, 0xBE, 0xEF },
+};
+
+void cxl_mock_add_event_logs(struct cxl_dev_state *cxlds)
+{
+ struct device *dev = cxlds->dev;
+ struct mock_event_store *mes;
+
+ mes = kzalloc(sizeof(*mes), GFP_KERNEL);
+ if (WARN_ON(!mes))
+ return;
+ mes->cxlds = cxlds;
+
+ if (xa_insert(&mock_dev_event_store, (unsigned long)dev, mes,
+ GFP_KERNEL)) {
+ dev_err(dev, "Event store not available for %s\n",
+ dev_name(dev));
+ return;
+ }
+
+ event_store_add_event(mes, CXL_EVENT_TYPE_INFO, &maint_needed);
+
+ event_store_add_event(mes, CXL_EVENT_TYPE_FATAL, &hardware_replace);
+
+}
+EXPORT_SYMBOL_GPL(cxl_mock_add_event_logs);
+
+void cxl_mock_remove_event_logs(struct device *dev)
+{
+ struct mock_event_store *mes;
+
+ mes = xa_erase(&mock_dev_event_store, (unsigned long)dev);
+ kfree(mes);
+}
+EXPORT_SYMBOL_GPL(cxl_mock_remove_event_logs);
new file mode 100644
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include <cxlmem.h>
+
+int mock_get_event(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd);
+int mock_clear_event(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd);
+void cxl_mock_add_event_logs(struct cxl_dev_state *cxlds);
+void cxl_mock_remove_event_logs(struct device *dev);
+void cxl_mock_event_trigger(struct device *dev);
@@ -8,6 +8,7 @@
#include <linux/sizes.h>
#include <linux/bits.h>
#include <cxlmem.h>
+#include "events.h"
#define LSA_SIZE SZ_128K
#define DEV_SIZE SZ_2G
@@ -224,6 +225,12 @@ static int cxl_mock_mbox_send(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *
case CXL_MBOX_OP_GET_PARTITION_INFO:
rc = mock_partition_info(cxlds, cmd);
break;
+ case CXL_MBOX_OP_GET_EVENT_RECORD:
+ rc = mock_get_event(cxlds, cmd);
+ break;
+ case CXL_MBOX_OP_CLEAR_EVENT_RECORD:
+ rc = mock_clear_event(cxlds, cmd);
+ break;
case CXL_MBOX_OP_SET_LSA:
rc = mock_set_lsa(cxlds, cmd);
break;
@@ -245,6 +252,21 @@ static void label_area_release(void *lsa)
vfree(lsa);
}
+static ssize_t event_trigger_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ cxl_mock_event_trigger(dev);
+ return count;
+}
+static DEVICE_ATTR_WO(event_trigger);
+
+static struct attribute *cxl_mock_event_attrs[] = {
+ &dev_attr_event_trigger.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(cxl_mock_event);
+
static int cxl_mock_mem_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -281,6 +303,8 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
if (rc)
return rc;
+ cxl_mock_add_event_logs(cxlds);
+
cxlmd = devm_cxl_add_memdev(cxlds);
if (IS_ERR(cxlmd))
return PTR_ERR(cxlmd);
@@ -293,6 +317,12 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
return 0;
}
+static int cxl_mock_mem_remove(struct platform_device *pdev)
+{
+ cxl_mock_remove_event_logs(&pdev->dev);
+ return 0;
+}
+
static const struct platform_device_id cxl_mock_mem_ids[] = {
{ .name = "cxl_mem", },
{ },
@@ -301,9 +331,11 @@ MODULE_DEVICE_TABLE(platform, cxl_mock_mem_ids);
static struct platform_driver cxl_mock_mem_driver = {
.probe = cxl_mock_mem_probe,
+ .remove = cxl_mock_mem_remove,
.id_table = cxl_mock_mem_ids,
.driver = {
.name = KBUILD_MODNAME,
+ .dev_groups = cxl_mock_event_groups,
},
};