summaryrefslogtreecommitdiff
path: root/tools/testing/cxl
diff options
context:
space:
mode:
Diffstat (limited to 'tools/testing/cxl')
-rw-r--r--tools/testing/cxl/Kbuild9
-rw-r--r--tools/testing/cxl/test/Kbuild3
-rw-r--r--tools/testing/cxl/test/accel.c66
-rw-r--r--tools/testing/cxl/test/cxl.c1280
-rw-r--r--tools/testing/cxl/test/cxl_translate.c2
-rw-r--r--tools/testing/cxl/test/hmem_test.c48
-rw-r--r--tools/testing/cxl/test/mem.c67
-rw-r--r--tools/testing/cxl/test/mock.c50
-rw-r--r--tools/testing/cxl/test/mock.h10
9 files changed, 1309 insertions, 226 deletions
diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild
index 53d84a6874b7..2be1df80fcc9 100644
--- a/tools/testing/cxl/Kbuild
+++ b/tools/testing/cxl/Kbuild
@@ -11,8 +11,12 @@ ldflags-y += --wrap=devm_cxl_endpoint_decoders_setup
ldflags-y += --wrap=hmat_get_extended_linear_cache_size
ldflags-y += --wrap=devm_cxl_add_dport_by_dev
ldflags-y += --wrap=devm_cxl_switch_port_decoders_setup
+ldflags-y += --wrap=walk_hmem_resources
+ldflags-y += --wrap=region_intersects
+ldflags-y += --wrap=region_intersects_soft_reserve
DRIVERS := ../../../drivers
+DAX_HMEM_SRC := $(DRIVERS)/dax/hmem
CXL_SRC := $(DRIVERS)/cxl
CXL_CORE_SRC := $(DRIVERS)/cxl/core
ccflags-y := -I$(srctree)/drivers/cxl/
@@ -59,7 +63,7 @@ cxl_core-y += $(CXL_CORE_SRC)/hdm.o
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_REGION) += $(CXL_CORE_SRC)/region.o $(CXL_CORE_SRC)/region_pmem.o $(CXL_CORE_SRC)/region_dax.o
cxl_core-$(CONFIG_CXL_MCE) += $(CXL_CORE_SRC)/mce.o
cxl_core-$(CONFIG_CXL_FEATURES) += $(CXL_CORE_SRC)/features.o
cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += $(CXL_CORE_SRC)/edac.o
@@ -70,6 +74,9 @@ cxl_core-y += config_check.o
cxl_core-y += cxl_core_test.o
cxl_core-y += cxl_core_exports.o
+obj-m += dax_hmem.o
+dax_hmem-y := $(DAX_HMEM_SRC)/hmem.o
+
KBUILD_CFLAGS := $(filter-out -Wmissing-prototypes -Wmissing-declarations, $(KBUILD_CFLAGS))
obj-m += test/
diff --git a/tools/testing/cxl/test/Kbuild b/tools/testing/cxl/test/Kbuild
index af50972c8b6d..9a24ddc28488 100644
--- a/tools/testing/cxl/test/Kbuild
+++ b/tools/testing/cxl/test/Kbuild
@@ -5,9 +5,12 @@ obj-m += cxl_test.o
obj-m += cxl_mock.o
obj-m += cxl_mock_mem.o
obj-m += cxl_translate.o
+obj-m += cxl_mock_accel.o
cxl_test-y := cxl.o
+cxl_test-y += hmem_test.o
cxl_mock-y := mock.o
cxl_mock_mem-y := mem.o
+cxl_mock_accel-y := accel.o
KBUILD_CFLAGS := $(filter-out -Wmissing-prototypes -Wmissing-declarations, $(KBUILD_CFLAGS))
diff --git a/tools/testing/cxl/test/accel.c b/tools/testing/cxl/test/accel.c
new file mode 100644
index 000000000000..8e6f4687ca02
--- /dev/null
+++ b/tools/testing/cxl/test/accel.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright(c) 2026 Intel Corporation. All rights reserved.
+
+#include <linux/platform_device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/sizes.h>
+#include <cxl/mailbox.h>
+#include <cxlmem.h>
+
+struct mock_cxl_accel {
+ struct cxl_dev_state cxlds;
+ struct cxl_memdev *cxlmd;
+};
+
+static int cxl_mock_accel_probe(struct platform_device *pdev)
+{
+ struct mock_cxl_accel *cxl_accel;
+ struct device *dev = &pdev->dev;
+ struct cxl_dev_state *cxlds;
+ struct cxl_memdev *cxlmd;
+ struct range mock_range;
+ int rc;
+
+ cxl_accel = devm_cxl_dev_state_create(&pdev->dev, CXL_DEVTYPE_DEVMEM,
+ pdev->id + 1, 0,
+ struct mock_cxl_accel, cxlds,
+ false);
+ if (!cxl_accel)
+ return -ENOMEM;
+
+ cxlds = &cxl_accel->cxlds;
+ cxlds->media_ready = true;
+ rc = cxl_set_capacity(cxlds, SZ_512M);
+ if (rc)
+ return rc;
+
+ cxlmd = devm_cxl_probe_mem(cxlds, &mock_range);
+ if (IS_ERR(cxlmd))
+ return PTR_ERR(cxlmd);
+ cxl_accel->cxlmd = cxlmd;
+
+ dev_dbg(dev, "Probed mock accelerator with range %pra\n", &mock_range);
+
+ return 0;
+}
+
+static const struct platform_device_id cxl_mock_accel_ids[] = {
+ { .name = "cxl_type2_accel" },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, cxl_mock_accel_ids);
+
+static struct platform_driver cxl_mock_accel_driver = {
+ .probe = cxl_mock_accel_probe,
+ .id_table = cxl_mock_accel_ids,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+};
+
+module_platform_driver(cxl_mock_accel_driver);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("cxl_test: accelerator device mock module");
+MODULE_IMPORT_NS("CXL");
diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c
index 81e2aef3627a..62bd92b3be45 100644
--- a/tools/testing/cxl/test/cxl.c
+++ b/tools/testing/cxl/test/cxl.c
@@ -16,6 +16,8 @@
static int interleave_arithmetic;
static bool extended_linear_cache;
+static bool fail_autoassemble;
+static bool type2_test;
#define FAKE_QTG_ID 42
@@ -26,6 +28,7 @@ static bool extended_linear_cache;
#define NR_CXL_SWITCH_PORTS 2
#define NR_CXL_PORT_DECODERS 8
#define NR_BRIDGES (NR_CXL_HOST_BRIDGES + NR_CXL_SINGLE_HOST + NR_CXL_RCH)
+#define NR_CXL_TYPE2_ACCEL 1
#define MOCK_AUTO_REGION_SIZE_DEFAULT SZ_512M
static int mock_auto_region_size = MOCK_AUTO_REGION_SIZE_DEFAULT;
@@ -51,6 +54,31 @@ struct platform_device *cxl_mem_single[NR_MEM_SINGLE];
static struct platform_device *cxl_rch[NR_CXL_RCH];
static struct platform_device *cxl_rcd[NR_CXL_RCH];
+/*
+ * Decoder registry
+ *
+ * Record decoder programming so that the topology can be reconstructed
+ * after cxl_acpi unbind/bind. This allows a user-created region config
+ * to be replayed as if firmware had provided the region at enumeration
+ * time.
+ *
+ * Entries are keyed by a stable port identity (port->uport_dev) combined
+ * with the decoder id. Decoder state is saved at initialization and
+ * updated on commit and reset.
+ *
+ * On re-enumeration mock_init_hdm_decoder() consults this registry to
+ * restore enabled decoders. Disabled decoders are reinitialized to a
+ * clean default state rather than replaying stale programming.
+ */
+static DEFINE_XARRAY(decoder_registry);
+
+/*
+ * When set, decoder reset will not update the registry. This allows
+ * region destroy operations to reset live decoders without erasing
+ * the saved programming needed for replay after re-enumeration.
+ */
+static bool decoder_reset_preserve_registry;
+
static inline bool is_multi_bridge(struct device *dev)
{
int i;
@@ -292,7 +320,7 @@ static struct {
.restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM |
ACPI_CEDT_CFMWS_RESTRICT_VOLATILE,
.qtg_id = FAKE_QTG_ID,
- .window_size = SZ_256M,
+ .window_size = SZ_256M > PMD_SIZE ? SZ_256M : PMD_SIZE,
},
.target = { 3 },
},
@@ -358,6 +386,19 @@ static struct {
},
};
+static struct acpi_cedt_cfmws type2_cfmws0 = {
+ .header = {
+ .type = ACPI_CEDT_TYPE_CFMWS,
+ .length = sizeof(mock_cedt.cfmws0),
+ },
+ .interleave_ways = 0,
+ .granularity = 4,
+ .restrictions = ACPI_CEDT_CFMWS_RESTRICT_DEVMEM |
+ ACPI_CEDT_CFMWS_RESTRICT_VOLATILE,
+ .qtg_id = FAKE_QTG_ID,
+ .window_size = SZ_256M * 4,
+};
+
struct acpi_cedt_cfmws *mock_cfmws[] = {
[0] = &mock_cedt.cfmws0.cfmws,
[1] = &mock_cedt.cfmws1.cfmws,
@@ -407,12 +448,16 @@ static void depopulate_all_mock_resources(void)
static struct cxl_mock_res *alloc_mock_res(resource_size_t size, int align)
{
- struct cxl_mock_res *res = kzalloc(sizeof(*res), GFP_KERNEL);
struct genpool_data_align data = {
.align = align,
};
unsigned long phys;
+ struct cxl_mock_res *res __free(kfree) = kzalloc(sizeof(*res),
+ GFP_KERNEL);
+ if (!res)
+ return NULL;
+
INIT_LIST_HEAD(&res->list);
phys = gen_pool_alloc_algo(cxl_mock_pool, size,
gen_pool_first_fit_align, &data);
@@ -427,7 +472,7 @@ static struct cxl_mock_res *alloc_mock_res(resource_size_t size, int align)
list_add(&res->list, &mock_res);
mutex_unlock(&mock_res_lock);
- return res;
+ return no_free_ptr(res);
}
/* Only update CFMWS0 as this is used by the auto region. */
@@ -446,6 +491,11 @@ static void cfmws_elc_update(struct acpi_cedt_cfmws *window, int index)
window->window_size = mock_auto_region_size * 2;
}
+static void update_type2_cfmws(void)
+{
+ memcpy(&mock_cedt.cfmws0.cfmws, &type2_cfmws0, sizeof(type2_cfmws0));
+}
+
static int populate_cedt(void)
{
struct cxl_mock_res *res;
@@ -467,11 +517,18 @@ static int populate_cedt(void)
chbs->length = size;
}
+ if (type2_test)
+ update_type2_cfmws();
+
for (i = cfmws_start; i <= cfmws_end; i++) {
struct acpi_cedt_cfmws *window = mock_cfmws[i];
+ int align = SZ_256M;
- cfmws_elc_update(window, i);
- res = alloc_mock_res(window->window_size, SZ_256M);
+ if (i == 0 && !type2_test)
+ cfmws_elc_update(window, i);
+ if (window->restrictions & ACPI_CEDT_CFMWS_RESTRICT_VOLATILE)
+ align = max_t(int, SZ_256M, PMD_SIZE);
+ res = alloc_mock_res(window->window_size, align);
if (!res)
return -ENOMEM;
window->base_hpa = res->range.start;
@@ -704,6 +761,194 @@ static int map_targets(struct device *dev, void *data)
return 0;
}
+/*
+ * Build a stable registry key from the decoder's upstream port identity
+ * and decoder id.
+ *
+ * Decoder objects and cxl_port objects are reallocated on each enumeration,
+ * so their addresses cannot be used directly as replay keys. However,
+ * port->uport_dev is stable for a given topology across cxl_acpi unbind/bind
+ * in cxl_test, so use that as the port identity and pack the local decoder
+ * id into the low bits.
+ *
+ * The key is formed as:
+ * ((unsigned long)port->uport_dev << 4) | cxld->id
+ *
+ * The low bits hold the decoder id (which must fit in 4 bits) while
+ * the remaining bits identify the upstream port. This key is only used
+ * within cxl_test to locate saved decoder state during replay.
+ */
+static unsigned long cxld_registry_index(struct cxl_decoder *cxld)
+{
+ struct cxl_port *port = to_cxl_port(cxld->dev.parent);
+
+ dev_WARN_ONCE(&port->dev, cxld->id >= 16,
+ "decoder id:%d out of range\n", cxld->id);
+ return (((unsigned long)port->uport_dev) << 4) | cxld->id;
+}
+
+struct cxl_test_decoder {
+ union {
+ struct cxl_switch_decoder cxlsd;
+ struct cxl_endpoint_decoder cxled;
+ };
+ struct range dpa_range;
+};
+
+static struct cxl_test_decoder *cxld_registry_find(struct cxl_decoder *cxld)
+{
+ return xa_load(&decoder_registry, cxld_registry_index(cxld));
+}
+
+#define dbg_cxld(port, msg, cxld) \
+ do { \
+ struct cxl_decoder *___d = (cxld); \
+ dev_dbg((port)->uport_dev, \
+ "decoder%d: %s range: %#llx-%#llx iw: %d ig: %d flags: %#lx\n", \
+ ___d->id, msg, ___d->hpa_range.start, \
+ ___d->hpa_range.end + 1, ___d->interleave_ways, \
+ ___d->interleave_granularity, ___d->flags); \
+ } while (0)
+
+static int mock_decoder_commit(struct cxl_decoder *cxld);
+static void mock_decoder_reset(struct cxl_decoder *cxld);
+static void init_disabled_mock_decoder(struct cxl_decoder *cxld);
+
+static void cxld_copy(struct cxl_decoder *a, struct cxl_decoder *b)
+{
+ a->id = b->id;
+ a->hpa_range = b->hpa_range;
+ a->interleave_ways = b->interleave_ways;
+ a->interleave_granularity = b->interleave_granularity;
+ a->target_type = b->target_type;
+ a->flags = b->flags;
+ a->commit = mock_decoder_commit;
+ a->reset = mock_decoder_reset;
+}
+
+/*
+ * Restore decoder programming saved in the registry.
+ *
+ * Only decoders that were saved enabled are restored. Disabled decoders
+ * are left in their default inactive state so that stale programming is
+ * not resurrected after topology replay.
+ *
+ * For endpoint decoders this also restores the DPA reservation needed
+ * to reconstruct committed mappings.
+ */
+static int cxld_registry_restore(struct cxl_decoder *cxld,
+ struct cxl_test_decoder *td)
+{
+ struct cxl_port *port = to_cxl_port(cxld->dev.parent);
+ int rc;
+
+ if (is_switch_decoder(&cxld->dev)) {
+ struct cxl_switch_decoder *cxlsd =
+ to_cxl_switch_decoder(&cxld->dev);
+
+ if (!(td->cxlsd.cxld.flags & CXL_DECODER_F_ENABLE))
+ return 0;
+
+ dbg_cxld(port, "restore", &td->cxlsd.cxld);
+ cxld_copy(cxld, &td->cxlsd.cxld);
+ WARN_ON(cxlsd->nr_targets != td->cxlsd.nr_targets);
+
+ /* Restore saved target intent; live dport binding happens later */
+ for (int i = 0; i < cxlsd->nr_targets; i++) {
+ cxlsd->target[i] = NULL;
+ cxld->target_map[i] = td->cxlsd.cxld.target_map[i];
+ }
+
+ port->commit_end = cxld->id;
+
+ } else {
+ struct cxl_endpoint_decoder *cxled =
+ to_cxl_endpoint_decoder(&cxld->dev);
+
+ if (!(td->cxled.cxld.flags & CXL_DECODER_F_ENABLE))
+ return 0;
+
+ dbg_cxld(port, "restore", &td->cxled.cxld);
+ cxld_copy(cxld, &td->cxled.cxld);
+ cxled->state = td->cxled.state;
+ cxled->skip = td->cxled.skip;
+ if (range_len(&td->dpa_range)) {
+ rc = devm_cxl_dpa_reserve(cxled, td->dpa_range.start,
+ range_len(&td->dpa_range),
+ td->cxled.skip);
+ if (rc) {
+ init_disabled_mock_decoder(cxld);
+ return rc;
+ }
+ }
+ port->commit_end = cxld->id;
+ }
+
+ return 0;
+}
+
+static void __cxld_registry_save(struct cxl_test_decoder *td,
+ struct cxl_decoder *cxld)
+{
+ if (is_switch_decoder(&cxld->dev)) {
+ struct cxl_switch_decoder *cxlsd =
+ to_cxl_switch_decoder(&cxld->dev);
+
+ cxld_copy(&td->cxlsd.cxld, cxld);
+ td->cxlsd.nr_targets = cxlsd->nr_targets;
+
+ /* Save target port_id as a stable identify for the dport */
+ for (int i = 0; i < cxlsd->nr_targets; i++) {
+ struct cxl_dport *dport;
+
+ if (!cxlsd->target[i])
+ continue;
+
+ dport = cxlsd->target[i];
+ td->cxlsd.cxld.target_map[i] = dport->port_id;
+ }
+ } else {
+ struct cxl_endpoint_decoder *cxled =
+ to_cxl_endpoint_decoder(&cxld->dev);
+
+ cxld_copy(&td->cxled.cxld, cxld);
+ td->cxled.state = cxled->state;
+ td->cxled.skip = cxled->skip;
+
+ if (!(cxld->flags & CXL_DECODER_F_ENABLE)) {
+ td->dpa_range.start = 0;
+ td->dpa_range.end = -1;
+ } else if (cxled->dpa_res) {
+ td->dpa_range.start = cxled->dpa_res->start;
+ td->dpa_range.end = cxled->dpa_res->end;
+ } else {
+ td->dpa_range.start = 0;
+ td->dpa_range.end = -1;
+ }
+ }
+}
+
+static void cxld_registry_save(struct cxl_test_decoder *td,
+ struct cxl_decoder *cxld)
+{
+ struct cxl_port *port = to_cxl_port(cxld->dev.parent);
+
+ dbg_cxld(port, "save", cxld);
+ __cxld_registry_save(td, cxld);
+}
+
+static void cxld_registry_update(struct cxl_decoder *cxld)
+{
+ struct cxl_test_decoder *td = cxld_registry_find(cxld);
+ struct cxl_port *port = to_cxl_port(cxld->dev.parent);
+
+ if (WARN_ON_ONCE(!td))
+ return;
+
+ dbg_cxld(port, "update", cxld);
+ __cxld_registry_save(td, cxld);
+}
+
static int mock_decoder_commit(struct cxl_decoder *cxld)
{
struct cxl_port *port = to_cxl_port(cxld->dev.parent);
@@ -723,6 +968,13 @@ static int mock_decoder_commit(struct cxl_decoder *cxld)
port->commit_end++;
cxld->flags |= CXL_DECODER_F_ENABLE;
+ if (is_endpoint_decoder(&cxld->dev)) {
+ struct cxl_endpoint_decoder *cxled =
+ to_cxl_endpoint_decoder(&cxld->dev);
+
+ cxled->state = CXL_DECODER_STATE_AUTO;
+ }
+ cxld_registry_update(cxld);
return 0;
}
@@ -743,6 +995,65 @@ static void mock_decoder_reset(struct cxl_decoder *cxld)
"%s: out of order reset, expected decoder%d.%d\n",
dev_name(&cxld->dev), port->id, port->commit_end);
cxld->flags &= ~CXL_DECODER_F_ENABLE;
+
+ if (is_endpoint_decoder(&cxld->dev)) {
+ struct cxl_endpoint_decoder *cxled =
+ to_cxl_endpoint_decoder(&cxld->dev);
+
+ cxled->state = CXL_DECODER_STATE_MANUAL;
+ cxled->skip = 0;
+ }
+ if (decoder_reset_preserve_registry)
+ dev_dbg(port->uport_dev, "decoder%d: skip registry update\n",
+ cxld->id);
+ else
+ cxld_registry_update(cxld);
+}
+
+static struct cxl_test_decoder *cxld_registry_new(struct cxl_decoder *cxld)
+{
+ struct cxl_test_decoder *td __free(kfree) =
+ kzalloc(sizeof(*td), GFP_KERNEL);
+ unsigned long key = cxld_registry_index(cxld);
+
+ if (!td)
+ return NULL;
+
+ if (xa_insert(&decoder_registry, key, td, GFP_KERNEL)) {
+ WARN_ON(1);
+ return NULL;
+ }
+
+ cxld_registry_save(td, cxld);
+ return no_free_ptr(td);
+}
+
+static void init_disabled_mock_decoder(struct cxl_decoder *cxld)
+{
+ cxld->hpa_range.start = 0;
+ cxld->hpa_range.end = -1;
+ cxld->interleave_ways = 1;
+ cxld->interleave_granularity = 0;
+ cxld->target_type = CXL_DECODER_HOSTONLYMEM;
+ cxld->flags = 0;
+ cxld->commit = mock_decoder_commit;
+ cxld->reset = mock_decoder_reset;
+
+ if (is_switch_decoder(&cxld->dev)) {
+ struct cxl_switch_decoder *cxlsd =
+ to_cxl_switch_decoder(&cxld->dev);
+
+ for (int i = 0; i < cxlsd->nr_targets; i++) {
+ cxlsd->target[i] = NULL;
+ cxld->target_map[i] = 0;
+ }
+ } else {
+ struct cxl_endpoint_decoder *cxled =
+ to_cxl_endpoint_decoder(&cxld->dev);
+
+ cxled->state = CXL_DECODER_STATE_MANUAL;
+ cxled->skip = 0;
+ }
}
static void default_mock_decoder(struct cxl_decoder *cxld)
@@ -757,6 +1068,8 @@ static void default_mock_decoder(struct cxl_decoder *cxld)
cxld->target_type = CXL_DECODER_HOSTONLYMEM;
cxld->commit = mock_decoder_commit;
cxld->reset = mock_decoder_reset;
+
+ WARN_ON_ONCE(!cxld_registry_new(cxld));
}
static int first_decoder(struct device *dev, const void *data)
@@ -771,41 +1084,27 @@ static int first_decoder(struct device *dev, const void *data)
return 0;
}
-static void mock_init_hdm_decoder(struct cxl_decoder *cxld)
-{
- struct acpi_cedt_cfmws *window = mock_cfmws[0];
- struct platform_device *pdev = NULL;
- struct cxl_endpoint_decoder *cxled;
- struct cxl_switch_decoder *cxlsd;
- struct cxl_port *port, *iter;
- struct cxl_memdev *cxlmd;
- struct cxl_dport *dport;
- struct device *dev;
- bool hb0 = false;
- u64 base;
- int i;
+enum cxld_init_type {
+ MOCK_DECODER_INIT_DEFAULT,
+ MOCK_DECODER_INIT_SAVED,
+ MOCK_DECODER_INIT_TYPE3_AUTO,
+ MOCK_DECODER_INIT_TYPE2_AUTO,
+};
- if (is_endpoint_decoder(&cxld->dev)) {
- cxled = to_cxl_endpoint_decoder(&cxld->dev);
- cxlmd = cxled_to_memdev(cxled);
- WARN_ON(!dev_is_platform(cxlmd->dev.parent));
- pdev = to_platform_device(cxlmd->dev.parent);
+static enum cxld_init_type get_decoder_init_type(struct cxl_decoder *cxld,
+ struct platform_device *pdev,
+ bool hb0,
+ struct cxl_test_decoder **td)
+{
+ struct cxl_test_decoder *found_td = cxld_registry_find(cxld);
- /* check is endpoint is attach to host-bridge0 */
- port = cxled_to_port(cxled);
- do {
- if (port->uport_dev == &cxl_host_bridge[0]->dev) {
- hb0 = true;
- break;
- }
- if (is_cxl_port(port->dev.parent))
- port = to_cxl_port(port->dev.parent);
- else
- port = NULL;
- } while (port);
- port = cxled_to_port(cxled);
+ if (found_td) {
+ *td = found_td;
+ return MOCK_DECODER_INIT_SAVED;
}
+ *td = NULL;
+
/*
* The first decoder on the first 2 devices on the first switch
* attached to host-bridge0 mock a fake / static RAM region. All
@@ -814,7 +1113,118 @@ static void mock_init_hdm_decoder(struct cxl_decoder *cxld)
*
* See 'cxl list -BMPu -m cxl_mem.0,cxl_mem.4'
*/
- if (!hb0 || pdev->id % 4 || pdev->id > 4 || cxld->id > 0) {
+ if (!is_endpoint_decoder(&cxld->dev) || !hb0 || pdev->id % 4 ||
+ pdev->id > 4 || cxld->id > 0)
+ return MOCK_DECODER_INIT_DEFAULT;
+
+ return type2_test ? MOCK_DECODER_INIT_TYPE2_AUTO :
+ MOCK_DECODER_INIT_TYPE3_AUTO;
+}
+
+static bool mock_decoder_handle_saved(struct cxl_decoder *cxld, struct cxl_test_decoder *td)
+{
+ bool enabled;
+
+ if (is_switch_decoder(&cxld->dev))
+ enabled = td->cxlsd.cxld.flags & CXL_DECODER_F_ENABLE;
+ else
+ enabled = td->cxled.cxld.flags & CXL_DECODER_F_ENABLE;
+
+ if (enabled)
+ return !cxld_registry_restore(cxld, td);
+
+ init_disabled_mock_decoder(cxld);
+ return false;
+}
+
+static void mock_init_hdm_type2_cxled(struct cxl_endpoint_decoder *cxled,
+ struct cxl_port *port)
+{
+ struct acpi_cedt_cfmws *window = mock_cfmws[0];
+ struct cxl_decoder *cxld = &cxled->cxld;
+ struct cxl_switch_decoder *cxlsd;
+ struct cxl_dport *dport;
+ struct cxl_port *root_port;
+ struct device *dev;
+ u64 base;
+
+ base = window->base_hpa;
+ cxld->hpa_range = (struct range) {
+ .start = base,
+ .end = base + mock_auto_region_size - 1,
+ };
+
+ cxld->interleave_ways = 1;
+ eig_to_granularity(window->granularity, &cxld->interleave_granularity);
+ cxld->target_type = CXL_DECODER_DEVMEM;
+ cxld->flags = CXL_DECODER_F_ENABLE;
+ cxled->state = CXL_DECODER_STATE_AUTO;
+ port->commit_end = cxld->id;
+ devm_cxl_dpa_reserve(cxled, 0,
+ mock_auto_region_size / cxld->interleave_ways, 0);
+ cxld->commit = mock_decoder_commit;
+ cxld->reset = mock_decoder_reset;
+
+ WARN_ON_ONCE(!cxld_registry_new(cxld));
+ /*
+ * Now that endpoint decoder is set up, walk up the hierarchy
+ * and setup the root port decoder targeting @cxlmd.
+ */
+ dport = port->parent_dport;
+ root_port = dport->port;
+ dev = device_find_child(&root_port->dev, NULL, first_decoder);
+ /*
+ * Ancestor ports are guaranteed to be enumerated before
+ * @port, and all ports have at least one decoder.
+ */
+ if (WARN_ON(!dev))
+ return;
+
+ cxlsd = to_cxl_switch_decoder(dev);
+ cxld = &cxlsd->cxld;
+ cxld->target_type = CXL_DECODER_DEVMEM;
+ cxld->flags = CXL_DECODER_F_ENABLE;
+ root_port->commit_end = 0;
+ cxld->interleave_ways = 1;
+ cxld->interleave_granularity = 4096;
+ cxld->target_map[0] = dport->port_id;
+ cxld->hpa_range = (struct range) {
+ .start = base,
+ .end = base + mock_auto_region_size - 1,
+ };
+ cxld->commit = mock_decoder_commit;
+ cxld->reset = mock_decoder_reset;
+
+ /*
+ * Only target_map[] is programmed above, mimicking
+ * firmware. On real hardware target[] is populated as
+ * dports enumerate, via update_decoder_targets(). The
+ * mock's dports are already bound by now, so fire that
+ * resolution explicitly here rather than stamping
+ * target[] directly.
+ */
+ cxl_port_update_decoder_targets(root_port, dport);
+
+ cxld_registry_update(cxld);
+ put_device(dev);
+}
+
+static void mock_init_hdm_type3_cxled(struct cxl_endpoint_decoder *cxled,
+ struct cxl_port *port,
+ struct platform_device *pdev,
+ bool hb0)
+{
+ struct acpi_cedt_cfmws *window = mock_cfmws[0];
+ struct cxl_decoder *cxld = &cxled->cxld;
+ struct cxl_switch_decoder *cxlsd;
+ struct cxl_dport *dport;
+ struct cxl_port *iter;
+ struct device *dev;
+ u64 base;
+ int i;
+
+ /* Simulate missing cxl_mem.4 configuration */
+ if (hb0 && pdev->id == 4 && cxld->id == 0 && fail_autoassemble) {
default_mock_decoder(cxld);
return;
}
@@ -838,6 +1248,7 @@ static void mock_init_hdm_decoder(struct cxl_decoder *cxld)
cxld->commit = mock_decoder_commit;
cxld->reset = mock_decoder_reset;
+ WARN_ON_ONCE(!cxld_registry_new(cxld));
/*
* Now that endpoint decoder is set up, walk up the hierarchy
* and setup the switch and root port decoders targeting @cxlmd.
@@ -857,16 +1268,12 @@ static void mock_init_hdm_decoder(struct cxl_decoder *cxld)
cxlsd = to_cxl_switch_decoder(dev);
if (i == 0) {
/* put cxl_mem.4 second in the decode order */
- if (pdev->id == 4) {
- cxlsd->target[1] = dport;
- cxld->target_map[1] = dport->port_id;
- } else {
- cxlsd->target[0] = dport;
- cxld->target_map[0] = dport->port_id;
- }
+ if (pdev->id == 4)
+ cxlsd->cxld.target_map[1] = dport->port_id;
+ else
+ cxlsd->cxld.target_map[0] = dport->port_id;
} else {
- cxlsd->target[0] = dport;
- cxld->target_map[0] = dport->port_id;
+ cxlsd->cxld.target_map[0] = dport->port_id;
}
cxld = &cxlsd->cxld;
cxld->target_type = CXL_DECODER_HOSTONLYMEM;
@@ -885,16 +1292,102 @@ static void mock_init_hdm_decoder(struct cxl_decoder *cxld)
.start = base,
.end = base + mock_auto_region_size - 1,
};
+ cxld->commit = mock_decoder_commit;
+ cxld->reset = mock_decoder_reset;
+
+ /*
+ * Only target_map[] is programmed above, mimicking
+ * firmware. On real hardware target[] is populated as
+ * dports enumerate, via update_decoder_targets(). The
+ * mock's dports are already bound by now, so fire that
+ * resolution explicitly here rather than stamping
+ * target[] directly.
+ */
+ cxl_port_update_decoder_targets(iter, dport);
+
+ cxld_registry_update(cxld);
put_device(dev);
}
}
+/*
+ * Initialize a decoder during HDM enumeration.
+ *
+ * If a saved registry entry exists:
+ * - enabled decoders are restored from the saved programming
+ * - disabled decoders are initialized in a clean disabled state
+ *
+ * If no registry entry exists the decoder follows the normal mock
+ * initialization path, including the special auto-region setup for
+ * the first endpoints under host-bridge0.
+ *
+ * Returns true if decoder state was restored from the registry. In
+ * that case the saved decode configuration (including target mapping)
+ * has already been applied and the map_targets() is skipped.
+ */
+static bool mock_init_hdm_decoder(struct cxl_decoder *cxld)
+{
+ struct cxl_endpoint_decoder *cxled = NULL;
+ struct platform_device *pdev = NULL;
+ struct cxl_test_decoder *td;
+ struct cxl_memdev *cxlmd;
+ struct cxl_port *port;
+ bool hb0 = false;
+
+ if (is_endpoint_decoder(&cxld->dev)) {
+ cxled = to_cxl_endpoint_decoder(&cxld->dev);
+ cxlmd = cxled_to_memdev(cxled);
+ WARN_ON(!dev_is_platform(cxlmd->dev.parent));
+ pdev = to_platform_device(cxlmd->dev.parent);
+
+ /* check is endpoint is attach to host-bridge0 */
+ port = cxled_to_port(cxled);
+ do {
+ if (port->uport_dev == &cxl_host_bridge[0]->dev) {
+ hb0 = true;
+ break;
+ }
+ if (is_cxl_port(port->dev.parent))
+ port = to_cxl_port(port->dev.parent);
+ else
+ port = NULL;
+ } while (port);
+ port = cxled_to_port(cxled);
+ } else {
+ port = to_cxl_port(cxld->dev.parent);
+ }
+
+ switch (get_decoder_init_type(cxld, pdev, hb0, &td)) {
+ case MOCK_DECODER_INIT_SAVED:
+ if (WARN_ON(!td))
+ return false;
+ return mock_decoder_handle_saved(cxld, td);
+ case MOCK_DECODER_INIT_DEFAULT:
+ /*
+ * The default path picks up all the decoders that are not
+ * endpoint.
+ */
+ default_mock_decoder(cxld);
+ return false;
+ case MOCK_DECODER_INIT_TYPE3_AUTO:
+ mock_init_hdm_type3_cxled(cxled, port, pdev, hb0);
+ return false;
+ case MOCK_DECODER_INIT_TYPE2_AUTO:
+ mock_init_hdm_type2_cxled(cxled, port);
+ return false;
+ default:
+ return false;
+ }
+ return false;
+}
+
static int mock_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
struct cxl_endpoint_dvsec_info *info)
{
struct cxl_port *port = cxlhdm->port;
struct cxl_port *parent_port = to_cxl_port(port->dev.parent);
int target_count, i;
+ bool restored;
if (is_cxl_endpoint(port))
target_count = 0;
@@ -934,10 +1427,8 @@ static int mock_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
}
ctx.target_map = cxld->target_map;
-
- mock_init_hdm_decoder(cxld);
-
- if (target_count) {
+ restored = mock_init_hdm_decoder(cxld);
+ if (target_count && !restored) {
rc = device_for_each_child(port->uport_dev, &ctx,
map_targets);
if (rc) {
@@ -1114,6 +1605,53 @@ static void mock_cxl_endpoint_parse_cdat(struct cxl_port *port)
cxl_endpoint_get_perf_coordinates(port, ep_c);
}
+/*
+ * Simulate that the first half of mock CXL Window 0 is "Soft Reserve" capacity
+ */
+static int mock_walk_hmem_resources(struct device *host, walk_hmem_fn fn)
+{
+ struct acpi_cedt_cfmws *cfmws = mock_cfmws[0];
+ struct resource window =
+ DEFINE_RES_MEM(cfmws->base_hpa, cfmws->window_size / 2);
+
+ dev_dbg(host, "walk cxl_test resource: %pr\n", &window);
+ return fn(host, 0, &window);
+}
+
+/*
+ * This should only be called by the dax_hmem case, treat mismatches (negative
+ * result) as "fallback to base region_intersects()". Simulate that the first
+ * half of mock CXL Window 0 is IORES_DESC_CXL capacity.
+ */
+static int mock_region_intersects(resource_size_t start, size_t size,
+ unsigned long flags, unsigned long desc)
+{
+ struct resource res = DEFINE_RES_MEM(start, size);
+ struct acpi_cedt_cfmws *cfmws = mock_cfmws[0];
+ struct resource window =
+ DEFINE_RES_MEM(cfmws->base_hpa, cfmws->window_size / 2);
+
+ if (resource_overlaps(&res, &window))
+ return REGION_INTERSECTS;
+ pr_debug("warning: no cxl_test CXL intersection for %pr\n", &res);
+ return -1;
+}
+
+
+static int
+mock_region_intersects_soft_reserve(resource_size_t start, size_t size)
+{
+ struct resource res = DEFINE_RES_MEM(start, size);
+ struct acpi_cedt_cfmws *cfmws = mock_cfmws[0];
+ struct resource window =
+ DEFINE_RES_MEM(cfmws->base_hpa, cfmws->window_size / 2);
+
+ if (resource_overlaps(&res, &window))
+ return REGION_INTERSECTS;
+ pr_debug("warning: no cxl_test soft reserve intersection for %pr\n", &res);
+ return -1;
+}
+
static struct cxl_mock_ops cxl_mock_ops = {
.is_mock_adev = is_mock_adev,
.is_mock_bridge = is_mock_bridge,
@@ -1129,6 +1667,9 @@ static struct cxl_mock_ops cxl_mock_ops = {
.devm_cxl_add_dport_by_dev = mock_cxl_add_dport_by_dev,
.hmat_get_extended_linear_cache_size =
mock_hmat_get_extended_linear_cache_size,
+ .walk_hmem_resources = mock_walk_hmem_resources,
+ .region_intersects = mock_region_intersects,
+ .region_intersects_soft_reserve = mock_region_intersects_soft_reserve,
.list = LIST_HEAD_INIT(cxl_mock_ops.list),
};
@@ -1144,6 +1685,23 @@ static void mock_companion(struct acpi_device *adev, struct device *dev)
#define SZ_64G (SZ_32G * 2)
#endif
+static int cxl_mock_platform_device_add(struct platform_device *pdev,
+ struct platform_device **ppdev)
+{
+ int rc;
+
+ if (ppdev)
+ *ppdev = pdev;
+ rc = platform_device_add(pdev);
+ if (rc) {
+ platform_device_put(pdev);
+ if (ppdev)
+ *ppdev = NULL;
+ }
+
+ return rc;
+}
+
static __init int cxl_rch_topo_init(void)
{
int rc, i;
@@ -1154,17 +1712,16 @@ static __init int cxl_rch_topo_init(void)
struct platform_device *pdev;
pdev = platform_device_alloc("cxl_host_bridge", idx);
- if (!pdev)
+ if (!pdev) {
+ rc = -ENOMEM;
goto err_bridge;
+ }
mock_companion(adev, &pdev->dev);
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_rch[i]);
+ if (rc)
goto err_bridge;
- }
- cxl_rch[i] = pdev;
mock_pci_bus[idx].bridge = &pdev->dev;
rc = sysfs_create_link(&pdev->dev.kobj, &pdev->dev.kobj,
"firmware_node");
@@ -1212,17 +1769,16 @@ static __init int cxl_single_topo_init(void)
pdev = platform_device_alloc("cxl_host_bridge",
NR_CXL_HOST_BRIDGES + i);
- if (!pdev)
+ if (!pdev) {
+ rc = -ENOMEM;
goto err_bridge;
+ }
mock_companion(adev, &pdev->dev);
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_hb_single[i]);
+ if (rc)
goto err_bridge;
- }
- cxl_hb_single[i] = pdev;
mock_pci_bus[i + NR_CXL_HOST_BRIDGES].bridge = &pdev->dev;
rc = sysfs_create_link(&pdev->dev.kobj, &pdev->dev.kobj,
"physical_node");
@@ -1237,16 +1793,15 @@ static __init int cxl_single_topo_init(void)
pdev = platform_device_alloc("cxl_root_port",
NR_MULTI_ROOT + i);
- if (!pdev)
+ if (!pdev) {
+ rc = -ENOMEM;
goto err_port;
+ }
pdev->dev.parent = &bridge->dev;
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_root_single[i]);
+ if (rc)
goto err_port;
- }
- cxl_root_single[i] = pdev;
}
for (i = 0; i < ARRAY_SIZE(cxl_swu_single); i++) {
@@ -1255,16 +1810,15 @@ static __init int cxl_single_topo_init(void)
pdev = platform_device_alloc("cxl_switch_uport",
NR_MULTI_ROOT + i);
- if (!pdev)
+ if (!pdev) {
+ rc = -ENOMEM;
goto err_uport;
+ }
pdev->dev.parent = &root_port->dev;
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_swu_single[i]);
+ if (rc)
goto err_uport;
- }
- cxl_swu_single[i] = pdev;
}
for (i = 0; i < ARRAY_SIZE(cxl_swd_single); i++) {
@@ -1274,16 +1828,15 @@ static __init int cxl_single_topo_init(void)
pdev = platform_device_alloc("cxl_switch_dport",
i + NR_MEM_MULTI);
- if (!pdev)
+ if (!pdev) {
+ rc = -ENOMEM;
goto err_dport;
+ }
pdev->dev.parent = &uport->dev;
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_swd_single[i]);
+ if (rc)
goto err_dport;
- }
- cxl_swd_single[i] = pdev;
}
return 0;
@@ -1330,19 +1883,84 @@ static void cxl_single_topo_exit(void)
}
}
-static void cxl_mem_exit(void)
+static void cxl_type3_mem_exit(void)
{
+ struct platform_device *pdev;
int i;
- for (i = ARRAY_SIZE(cxl_rcd) - 1; i >= 0; i--)
+ for (i = ARRAY_SIZE(cxl_rcd) - 1; i >= 0; i--) {
+ pdev = cxl_rcd[i];
+ if (!pdev)
+ continue;
platform_device_unregister(cxl_rcd[i]);
- for (i = ARRAY_SIZE(cxl_mem_single) - 1; i >= 0; i--)
+ }
+
+ for (i = ARRAY_SIZE(cxl_mem_single) - 1; i >= 0; i--) {
+ pdev = cxl_mem_single[i];
+ if (!pdev)
+ continue;
platform_device_unregister(cxl_mem_single[i]);
- for (i = ARRAY_SIZE(cxl_mem) - 1; i >= 0; i--)
+ }
+
+ for (i = ARRAY_SIZE(cxl_mem) - 1; i >= 0; i--) {
+ pdev = cxl_mem[i];
+ if (!pdev)
+ continue;
+ platform_device_unregister(pdev);
+ }
+}
+
+static void cxl_type2_mem_exit(void)
+{
+ for (int i = NR_CXL_TYPE2_ACCEL - 1; i >= 0; i--) {
+ struct platform_device *pdev = cxl_mem[i];
+
+ if (!pdev)
+ continue;
+ platform_device_unregister(pdev);
+ }
+}
+
+static void cxl_mem_exit(void)
+{
+ if (type2_test) {
+ cxl_type2_mem_exit();
+ return;
+ }
+
+ cxl_type3_mem_exit();
+}
+
+static int cxl_type2_mem_init(void)
+{
+ int i, rc;
+
+ for (i = 0; i < NR_CXL_TYPE2_ACCEL; i++) {
+ struct platform_device *dport = cxl_root_port[i];
+ struct platform_device *pdev;
+
+ pdev = platform_device_alloc("cxl_type2_accel", i);
+ if (!pdev) {
+ rc = -ENOMEM;
+ goto err_mem;
+ }
+ pdev->dev.parent = &dport->dev;
+ set_dev_node(&pdev->dev, i % 2);
+
+ rc = cxl_mock_platform_device_add(pdev, &cxl_mem[i]);
+ if (rc)
+ goto err_mem;
+ }
+
+ return 0;
+
+err_mem:
+ for (i = NR_CXL_TYPE2_ACCEL - 1; i >= 0; i--)
platform_device_unregister(cxl_mem[i]);
+ return rc;
}
-static int cxl_mem_init(void)
+static int cxl_type3_mem_init(void)
{
int i, rc;
@@ -1351,17 +1969,16 @@ static int cxl_mem_init(void)
struct platform_device *pdev;
pdev = platform_device_alloc("cxl_mem", i);
- if (!pdev)
+ if (!pdev) {
+ rc = -ENOMEM;
goto err_mem;
+ }
pdev->dev.parent = &dport->dev;
set_dev_node(&pdev->dev, i % 2);
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_mem[i]);
+ if (rc)
goto err_mem;
- }
- cxl_mem[i] = pdev;
}
for (i = 0; i < ARRAY_SIZE(cxl_mem_single); i++) {
@@ -1369,17 +1986,16 @@ static int cxl_mem_init(void)
struct platform_device *pdev;
pdev = platform_device_alloc("cxl_mem", NR_MEM_MULTI + i);
- if (!pdev)
+ if (!pdev) {
+ rc = -ENOMEM;
goto err_single;
+ }
pdev->dev.parent = &dport->dev;
set_dev_node(&pdev->dev, i % 2);
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_mem_single[i]);
+ if (rc)
goto err_single;
- }
- cxl_mem_single[i] = pdev;
}
for (i = 0; i < ARRAY_SIZE(cxl_rcd); i++) {
@@ -1388,17 +2004,16 @@ static int cxl_mem_init(void)
struct platform_device *pdev;
pdev = platform_device_alloc("cxl_rcd", idx);
- if (!pdev)
+ if (!pdev) {
+ rc = -ENOMEM;
goto err_rcd;
+ }
pdev->dev.parent = &rch->dev;
set_dev_node(&pdev->dev, i % 2);
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_rcd[i]);
+ if (rc)
goto err_rcd;
- }
- cxl_rcd[i] = pdev;
}
return 0;
@@ -1415,61 +2030,90 @@ err_mem:
return rc;
}
-static __init int cxl_test_init(void)
+static int cxl_mem_init(void)
{
- int rc, i;
- struct range mappable;
-
- cxl_acpi_test();
- cxl_core_test();
- cxl_mem_test();
- cxl_pmem_test();
- cxl_port_test();
+ if (type2_test)
+ return cxl_type2_mem_init();
+ return cxl_type3_mem_init();
+}
- register_cxl_mock_ops(&cxl_mock_ops);
+static ssize_t
+decoder_reset_preserve_registry_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "%d\n", decoder_reset_preserve_registry);
+}
- cxl_mock_pool = gen_pool_create(ilog2(SZ_2M), NUMA_NO_NODE);
- if (!cxl_mock_pool) {
- rc = -ENOMEM;
- goto err_gen_pool_create;
- }
- mappable = mhp_get_pluggable_range(true);
+static ssize_t
+decoder_reset_preserve_registry_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int rc;
- rc = gen_pool_add(cxl_mock_pool,
- min(iomem_resource.end + 1 - SZ_64G,
- mappable.end + 1 - SZ_64G),
- SZ_64G, NUMA_NO_NODE);
+ rc = kstrtobool(buf, &decoder_reset_preserve_registry);
if (rc)
- goto err_gen_pool_add;
+ return rc;
+ return count;
+}
- if (interleave_arithmetic == 1) {
- cfmws_start = CFMWS_XOR_ARRAY_START;
- cfmws_end = CFMWS_XOR_ARRAY_END;
- } else {
- cfmws_start = CFMWS_MOD_ARRAY_START;
- cfmws_end = CFMWS_MOD_ARRAY_END;
+static DEVICE_ATTR_RW(decoder_reset_preserve_registry);
+
+static struct attribute *cxl_acpi_attrs[] = {
+ &dev_attr_decoder_reset_preserve_registry.attr, NULL
+};
+ATTRIBUTE_GROUPS(cxl_acpi);
+
+static bool __init have_multiple_modparms(void)
+{
+ int count = 0;
+
+ if (interleave_arithmetic)
+ count++;
+ if (extended_linear_cache)
+ count++;
+ if (hmem_test)
+ count++;
+ if (type2_test)
+ count++;
+
+ return count > 1;
+}
+
+static void host_bridges_remove(void)
+{
+ int i;
+
+ for (i = ARRAY_SIZE(cxl_host_bridge) - 1; i >= 0; i--) {
+ struct platform_device *pdev = cxl_host_bridge[i];
+
+ if (!pdev)
+ continue;
+
+ sysfs_remove_link(&pdev->dev.kobj, "physical_node");
+ platform_device_unregister(cxl_host_bridge[i]);
}
+}
- rc = populate_cedt();
- if (rc)
- goto err_populate;
+static int host_bridges_populate(void)
+{
+ int rc = 0;
- for (i = 0; i < ARRAY_SIZE(cxl_host_bridge); i++) {
+ for (int i = 0; i < ARRAY_SIZE(cxl_host_bridge); i++) {
struct acpi_device *adev = &host_bridge[i];
struct platform_device *pdev;
pdev = platform_device_alloc("cxl_host_bridge", i);
- if (!pdev)
+ if (!pdev) {
+ rc = -ENOMEM;
goto err_bridge;
+ }
mock_companion(adev, &pdev->dev);
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_host_bridge[i]);
+ if (rc)
goto err_bridge;
- }
- cxl_host_bridge[i] = pdev;
mock_pci_bus[i].bridge = &pdev->dev;
rc = sysfs_create_link(&pdev->dev.kobj, &pdev->dev.kobj,
"physical_node");
@@ -1477,109 +2121,328 @@ static __init int cxl_test_init(void)
goto err_bridge;
}
- for (i = 0; i < ARRAY_SIZE(cxl_root_port); i++) {
+ return 0;
+
+err_bridge:
+ host_bridges_remove();
+ return rc;
+}
+
+static void cxl_rootports_remove(void)
+{
+ for (int i = ARRAY_SIZE(cxl_root_port) - 1; i >= 0; i--) {
+ struct platform_device *pdev = cxl_root_port[i];
+
+ if (!pdev)
+ continue;
+
+ platform_device_unregister(pdev);
+ }
+}
+
+static int cxl_rootports_populate(void)
+{
+ int rc = 0;
+
+ for (int i = 0; i < ARRAY_SIZE(cxl_root_port); i++) {
struct platform_device *bridge =
cxl_host_bridge[i % ARRAY_SIZE(cxl_host_bridge)];
struct platform_device *pdev;
pdev = platform_device_alloc("cxl_root_port", i);
- if (!pdev)
+ if (!pdev) {
+ rc = -ENOMEM;
goto err_port;
+ }
+
pdev->dev.parent = &bridge->dev;
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_root_port[i]);
+ if (rc)
goto err_port;
- }
- cxl_root_port[i] = pdev;
}
- BUILD_BUG_ON(ARRAY_SIZE(cxl_switch_uport) != ARRAY_SIZE(cxl_root_port));
- for (i = 0; i < ARRAY_SIZE(cxl_switch_uport); i++) {
+ return 0;
+
+err_port:
+ cxl_rootports_remove();
+ return rc;
+}
+
+static void cxl_usps_remove(void)
+{
+ for (int i = ARRAY_SIZE(cxl_switch_uport) - 1; i >= 0; i--) {
+ struct platform_device *pdev = cxl_switch_uport[i];
+
+ if (!pdev)
+ continue;
+
+ platform_device_unregister(cxl_switch_uport[i]);
+ }
+}
+
+static int cxl_usps_populate(void)
+{
+ int rc = 0;
+
+ for (int i = 0; i < ARRAY_SIZE(cxl_switch_uport); i++) {
struct platform_device *root_port = cxl_root_port[i];
struct platform_device *pdev;
pdev = platform_device_alloc("cxl_switch_uport", i);
- if (!pdev)
+ if (!pdev) {
+ rc = -ENOMEM;
goto err_uport;
+ }
+
pdev->dev.parent = &root_port->dev;
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_switch_uport[i]);
+ if (rc)
goto err_uport;
- }
- cxl_switch_uport[i] = pdev;
}
- for (i = 0; i < ARRAY_SIZE(cxl_switch_dport); i++) {
+ return 0;
+
+err_uport:
+ cxl_usps_remove();
+ return rc;
+}
+
+static void cxl_dsps_remove(void)
+{
+ for (int i = ARRAY_SIZE(cxl_switch_dport) - 1; i >= 0; i--) {
+ struct platform_device *pdev = cxl_switch_dport[i];
+
+ if (!pdev)
+ continue;
+
+ platform_device_unregister(cxl_switch_dport[i]);
+ }
+}
+
+
+static int cxl_dsps_populate(void)
+{
+ int rc = 0;
+
+ for (int i = 0; i < ARRAY_SIZE(cxl_switch_dport); i++) {
struct platform_device *uport =
cxl_switch_uport[i % ARRAY_SIZE(cxl_switch_uport)];
struct platform_device *pdev;
pdev = platform_device_alloc("cxl_switch_dport", i);
- if (!pdev)
+ if (!pdev) {
+ rc = -ENOMEM;
goto err_dport;
+ }
pdev->dev.parent = &uport->dev;
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_switch_dport[i]);
+ if (rc)
goto err_dport;
- }
- cxl_switch_dport[i] = pdev;
}
+ return 0;
+
+err_dport:
+ cxl_dsps_remove();
+ return rc;
+}
+
+static void cxl_switches_remove(void)
+{
+ cxl_dsps_remove();
+ cxl_usps_remove();
+}
+
+static int cxl_switches_populate(void)
+{
+ int rc;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cxl_switch_uport) != ARRAY_SIZE(cxl_root_port));
+ rc = cxl_usps_populate();
+ if (rc)
+ return rc;
+
+ rc = cxl_dsps_populate();
+ if (rc) {
+ cxl_usps_remove();
+ return rc;
+ }
+
+ return 0;
+}
+
+static void cxl_type2_topo_exit(void)
+{
+ cxl_rootports_remove();
+ host_bridges_remove();
+}
+
+static int cxl_type2_topo_init(void)
+{
+ int rc;
+
+ rc = host_bridges_populate();
+ if (rc)
+ return rc;
+
+ rc = cxl_rootports_populate();
+ if (rc) {
+ host_bridges_remove();
+ return rc;
+ }
+
+ return 0;
+}
+
+static void cxl_type3_topo_exit(void)
+{
+ cxl_rch_topo_exit();
+ cxl_single_topo_exit();
+ cxl_switches_remove();
+ cxl_rootports_remove();
+ host_bridges_remove();
+}
+
+static int cxl_type3_topo_init(void)
+{
+ int rc;
+
+ rc = host_bridges_populate();
+ if (rc)
+ return rc;
+
+ rc = cxl_rootports_populate();
+ if (rc)
+ goto err_host_bridges;
+
+ rc = cxl_switches_populate();
+ if (rc)
+ goto err_root_ports;
+
rc = cxl_single_topo_init();
if (rc)
- goto err_dport;
+ goto err_switches;
rc = cxl_rch_topo_init();
if (rc)
goto err_single;
+ return 0;
+
+err_single:
+ cxl_single_topo_exit();
+err_switches:
+ cxl_switches_remove();
+err_root_ports:
+ cxl_rootports_remove();
+err_host_bridges:
+ host_bridges_remove();
+ return rc;
+}
+
+static void cxl_topo_exit(void)
+{
+ if (type2_test) {
+ cxl_type2_topo_exit();
+ return;
+ }
+
+ cxl_type3_topo_exit();
+}
+
+static int cxl_topo_init(void)
+{
+ if (type2_test)
+ return cxl_type2_topo_init();
+ return cxl_type3_topo_init();
+}
+
+static __init int cxl_test_init(void)
+{
+ struct range mappable;
+ int rc;
+
+ /* Enforce a single module param active at a time */
+ if (have_multiple_modparms())
+ return -EINVAL;
+
+ if (!IS_ALIGNED(mock_auto_region_size, PMD_SIZE)) {
+ pr_err_once("mock_auto_region_size %d must be PMD-aligned\n",
+ mock_auto_region_size);
+ return -EINVAL;
+ }
+
+ cxl_acpi_test();
+ cxl_core_test();
+ cxl_mem_test();
+ cxl_pmem_test();
+ cxl_port_test();
+
+ register_cxl_mock_ops(&cxl_mock_ops);
+
+ cxl_mock_pool = gen_pool_create(ilog2(SZ_2M), NUMA_NO_NODE);
+ if (!cxl_mock_pool) {
+ rc = -ENOMEM;
+ goto err_gen_pool_create;
+ }
+ mappable = mhp_get_pluggable_range(true);
+
+ rc = gen_pool_add(cxl_mock_pool,
+ min(iomem_resource.end + 1 - SZ_64G,
+ mappable.end + 1 - SZ_64G),
+ SZ_64G, NUMA_NO_NODE);
+ if (rc)
+ goto err_gen_pool_add;
+
+ if (interleave_arithmetic == 1) {
+ cfmws_start = CFMWS_XOR_ARRAY_START;
+ cfmws_end = CFMWS_XOR_ARRAY_END;
+ } else {
+ cfmws_start = CFMWS_MOD_ARRAY_START;
+ cfmws_end = CFMWS_MOD_ARRAY_END;
+ }
+
+ rc = populate_cedt();
+ if (rc)
+ goto err_populate;
+
+ rc = cxl_topo_init();
+ if (rc)
+ goto err_populate;
+
cxl_acpi = platform_device_alloc("cxl_acpi", 0);
- if (!cxl_acpi)
- goto err_rch;
+ if (!cxl_acpi) {
+ rc = -ENOMEM;
+ goto err_topo;
+ }
mock_companion(&acpi0017_mock, &cxl_acpi->dev);
acpi0017_mock.dev.bus = &platform_bus_type;
+ cxl_acpi->dev.groups = cxl_acpi_groups;
- rc = platform_device_add(cxl_acpi);
+ rc = cxl_mock_platform_device_add(cxl_acpi, NULL);
if (rc)
- goto err_root;
+ goto err_topo;
rc = cxl_mem_init();
if (rc)
goto err_root;
+ rc = hmem_test_init();
+ if (rc)
+ goto err_mem;
+
return 0;
+err_mem:
+ cxl_mem_exit();
err_root:
- platform_device_put(cxl_acpi);
-err_rch:
- cxl_rch_topo_exit();
-err_single:
- cxl_single_topo_exit();
-err_dport:
- for (i = ARRAY_SIZE(cxl_switch_dport) - 1; i >= 0; i--)
- platform_device_unregister(cxl_switch_dport[i]);
-err_uport:
- for (i = ARRAY_SIZE(cxl_switch_uport) - 1; i >= 0; i--)
- platform_device_unregister(cxl_switch_uport[i]);
-err_port:
- for (i = ARRAY_SIZE(cxl_root_port) - 1; i >= 0; i--)
- platform_device_unregister(cxl_root_port[i]);
-err_bridge:
- for (i = ARRAY_SIZE(cxl_host_bridge) - 1; i >= 0; i--) {
- struct platform_device *pdev = cxl_host_bridge[i];
-
- if (!pdev)
- continue;
- sysfs_remove_link(&pdev->dev.kobj, "physical_node");
- platform_device_unregister(cxl_host_bridge[i]);
- }
+ platform_device_unregister(cxl_acpi);
+err_topo:
+ cxl_topo_exit();
err_populate:
depopulate_all_mock_resources();
err_gen_pool_add:
@@ -1589,37 +2452,38 @@ err_gen_pool_create:
return rc;
}
-static __exit void cxl_test_exit(void)
+static void free_decoder_registry(void)
{
- int i;
+ unsigned long index;
+ void *entry;
+
+ xa_for_each(&decoder_registry, index, entry) {
+ xa_erase(&decoder_registry, index);
+ kfree(entry);
+ }
+}
+static __exit void cxl_test_exit(void)
+{
+ hmem_test_exit();
cxl_mem_exit();
platform_device_unregister(cxl_acpi);
- cxl_rch_topo_exit();
- cxl_single_topo_exit();
- for (i = ARRAY_SIZE(cxl_switch_dport) - 1; i >= 0; i--)
- platform_device_unregister(cxl_switch_dport[i]);
- for (i = ARRAY_SIZE(cxl_switch_uport) - 1; i >= 0; i--)
- platform_device_unregister(cxl_switch_uport[i]);
- for (i = ARRAY_SIZE(cxl_root_port) - 1; i >= 0; i--)
- platform_device_unregister(cxl_root_port[i]);
- for (i = ARRAY_SIZE(cxl_host_bridge) - 1; i >= 0; i--) {
- struct platform_device *pdev = cxl_host_bridge[i];
-
- if (!pdev)
- continue;
- sysfs_remove_link(&pdev->dev.kobj, "physical_node");
- platform_device_unregister(cxl_host_bridge[i]);
- }
+ cxl_topo_exit();
depopulate_all_mock_resources();
gen_pool_destroy(cxl_mock_pool);
unregister_cxl_mock_ops(&cxl_mock_ops);
+ free_decoder_registry();
+ xa_destroy(&decoder_registry);
}
module_param(interleave_arithmetic, int, 0444);
MODULE_PARM_DESC(interleave_arithmetic, "Modulo:0, XOR:1");
module_param(extended_linear_cache, bool, 0444);
MODULE_PARM_DESC(extended_linear_cache, "Enable extended linear cache support");
+module_param(fail_autoassemble, bool, 0444);
+MODULE_PARM_DESC(fail_autoassemble, "Simulate missing member of an auto-region");
+module_param(type2_test, bool, 0444);
+MODULE_PARM_DESC(type2_test, "Enable type 2 support testing");
module_init(cxl_test_init);
module_exit(cxl_test_exit);
MODULE_LICENSE("GPL v2");
diff --git a/tools/testing/cxl/test/cxl_translate.c b/tools/testing/cxl/test/cxl_translate.c
index 16328b2112b2..25a27e01ac21 100644
--- a/tools/testing/cxl/test/cxl_translate.c
+++ b/tools/testing/cxl/test/cxl_translate.c
@@ -236,8 +236,8 @@ static int setup_xor_mapping(void)
if (!cximsd)
return -ENOMEM;
- memcpy(cximsd->xormaps, xormaps, nr_maps * sizeof(*cximsd->xormaps));
cximsd->nr_maps = nr_maps;
+ memcpy(cximsd->xormaps, xormaps, nr_maps * sizeof(*cximsd->xormaps));
return 0;
}
diff --git a/tools/testing/cxl/test/hmem_test.c b/tools/testing/cxl/test/hmem_test.c
new file mode 100644
index 000000000000..0fa00f7e16db
--- /dev/null
+++ b/tools/testing/cxl/test/hmem_test.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Intel Corporation */
+#include <linux/moduleparam.h>
+#include <linux/workqueue.h>
+#include "../../../drivers/dax/bus.h"
+#include "mock.h"
+
+bool hmem_test;
+
+static void hmem_test_work(struct work_struct *work)
+{
+}
+
+static void hmem_test_release(struct device *dev)
+{
+ struct hmem_platform_device *hpdev =
+ container_of(dev, typeof(*hpdev), pdev.dev);
+
+ memset(hpdev, 0, sizeof(*hpdev));
+}
+
+static struct hmem_platform_device hmem_test_device = {
+ .pdev = {
+ .name = "hmem_platform",
+ .id = 1,
+ .dev = {
+ .release = hmem_test_release,
+ },
+ },
+ .work = __WORK_INITIALIZER(hmem_test_device.work, hmem_test_work),
+};
+
+int hmem_test_init(void)
+{
+ if (!hmem_test)
+ return 0;
+
+ return platform_device_register(&hmem_test_device.pdev);
+}
+
+void hmem_test_exit(void)
+{
+ if (hmem_test)
+ platform_device_unregister(&hmem_test_device.pdev);
+}
+
+module_param(hmem_test, bool, 0444);
+MODULE_PARM_DESC(hmem_test, "Enable/disable the dax_hmem test platform device");
diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c
index cb87e8c0e63c..7b756000a1a6 100644
--- a/tools/testing/cxl/test/mem.c
+++ b/tools/testing/cxl/test/mem.c
@@ -2,7 +2,6 @@
// Copyright(c) 2021 Intel Corporation. All rights reserved.
#include <linux/platform_device.h>
-#include <linux/mod_devicetable.h>
#include <linux/vmalloc.h>
#include <linux/module.h>
#include <linux/delay.h>
@@ -312,12 +311,17 @@ static int mock_get_event(struct device *dev, struct cxl_mbox_cmd *cmd)
static int mock_clear_event(struct device *dev, struct cxl_mbox_cmd *cmd)
{
- struct cxl_mbox_clear_event_payload *pl = cmd->payload_in;
+ struct cxl_mbox_clear_event_payload *pl;
struct mock_event_log *log;
- u8 log_type = pl->event_log;
+ u8 log_type;
u16 handle;
int nr;
+ if (cmd->size_in < sizeof(*pl))
+ return -EINVAL;
+
+ pl = cmd->payload_in;
+ log_type = pl->event_log;
if (log_type >= CXL_EVENT_TYPE_MAX)
return -EINVAL;
@@ -574,14 +578,19 @@ static int mock_gsl(struct cxl_mbox_cmd *cmd)
static int mock_get_log(struct cxl_memdev_state *mds, struct cxl_mbox_cmd *cmd)
{
struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;
- struct cxl_mbox_get_log *gl = cmd->payload_in;
- u32 offset = le32_to_cpu(gl->offset);
- u32 length = le32_to_cpu(gl->length);
uuid_t uuid = DEFINE_CXL_CEL_UUID;
+ struct cxl_mbox_get_log *gl;
void *data = &mock_cel;
+ u32 offset;
+ u32 length;
if (cmd->size_in < sizeof(*gl))
return -EINVAL;
+
+ gl = cmd->payload_in;
+ offset = le32_to_cpu(gl->offset);
+ length = le32_to_cpu(gl->length);
+
if (length > cxl_mbox->payload_size)
return -EINVAL;
if (offset + length > sizeof(mock_cel))
@@ -1053,7 +1062,7 @@ static int mock_get_lsa(struct cxl_mockmem_data *mdata,
return -EINVAL;
offset = le32_to_cpu(get_lsa->offset);
length = le32_to_cpu(get_lsa->length);
- if (offset + length > LSA_SIZE)
+ if (offset > LSA_SIZE || length > LSA_SIZE - offset)
return -EINVAL;
if (length > cmd->size_out)
return -EINVAL;
@@ -1073,7 +1082,7 @@ static int mock_set_lsa(struct cxl_mockmem_data *mdata,
return -EINVAL;
offset = le32_to_cpu(set_lsa->offset);
length = cmd->size_in - sizeof(*set_lsa);
- if (offset + length > LSA_SIZE)
+ if (offset > LSA_SIZE || length > LSA_SIZE - offset)
return -EINVAL;
memcpy(lsa + offset, &set_lsa->data[0], length);
@@ -1336,10 +1345,14 @@ static int mock_fw_info(struct cxl_mockmem_data *mdata,
static int mock_transfer_fw(struct cxl_mockmem_data *mdata,
struct cxl_mbox_cmd *cmd)
{
- struct cxl_mbox_transfer_fw *transfer = cmd->payload_in;
+ struct cxl_mbox_transfer_fw *transfer;
void *fw = mdata->fw;
size_t offset, length;
+ if (cmd->size_in < sizeof(*transfer))
+ return -EINVAL;
+
+ transfer = cmd->payload_in;
offset = le32_to_cpu(transfer->offset) * CXL_FW_TRANSFER_ALIGNMENT;
length = cmd->size_in - sizeof(*transfer);
if (offset + length > FW_SIZE)
@@ -1415,11 +1428,18 @@ static int mock_get_test_feature(struct cxl_mockmem_data *mdata,
struct cxl_mbox_cmd *cmd)
{
struct vendor_test_feat *output = cmd->payload_out;
- struct cxl_mbox_get_feat_in *input = cmd->payload_in;
- u16 offset = le16_to_cpu(input->offset);
- u16 count = le16_to_cpu(input->count);
+ struct cxl_mbox_get_feat_in *input;
+ u16 offset;
+ u16 count;
u8 *ptr;
+ if (cmd->size_in < sizeof(*input))
+ return -EINVAL;
+
+ input = cmd->payload_in;
+ offset = le16_to_cpu(input->offset);
+ count = le16_to_cpu(input->count);
+
if (offset > sizeof(*output)) {
cmd->return_code = CXL_MBOX_CMD_RC_INPUT;
return -EINVAL;
@@ -1693,14 +1713,18 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
struct cxl_mockmem_data *mdata;
struct cxl_mailbox *cxl_mbox;
struct cxl_dpa_info range_info = { 0 };
+ u64 serial;
int rc;
+ /* Increase async probe race window */
+ usleep_range(500*1000, 1000*1000);
+
mdata = devm_kzalloc(dev, sizeof(*mdata), GFP_KERNEL);
if (!mdata)
return -ENOMEM;
dev_set_drvdata(dev, mdata);
- mdata->lsa = vmalloc(LSA_SIZE);
+ mdata->lsa = vzalloc(LSA_SIZE);
if (!mdata->lsa)
return -ENOMEM;
mdata->fw = vmalloc(FW_SIZE);
@@ -1716,7 +1740,19 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
if (rc)
return rc;
- mds = cxl_memdev_state_create(dev);
+ /*
+ * Mock serials have historically been derived from pdev->id and stayed
+ * single-digit, so they never exercised either decimal-vs-hex key
+ * lookup or unsigned formatting. Give one mock device a full-width
+ * serial with bit 63 set, matching real hardware such as Montage CXL
+ * devices. pdev->id 7 is unused by the auto-region topology.
+ */
+ if (pdev->id == 7)
+ serial = 0x8a34567890abcdef;
+ else
+ serial = pdev->id + 1;
+
+ mds = cxl_memdev_state_create(dev, serial, 0);
if (IS_ERR(mds))
return PTR_ERR(mds);
@@ -1732,7 +1768,6 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
mds->event.buf = (struct cxl_get_event_payload *) mdata->event_buf;
INIT_DELAYED_WORK(&mds->security.poll_dwork, cxl_mockmem_sanitize_work);
- cxlds->serial = pdev->id + 1;
if (is_rcd(pdev))
cxlds->rcd = true;
@@ -1767,7 +1802,7 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
cxl_mock_add_event_logs(&mdata->mes);
- cxlmd = devm_cxl_add_memdev(cxlds, NULL);
+ cxlmd = devm_cxl_add_classdev(cxlds);
if (IS_ERR(cxlmd))
return PTR_ERR(cxlmd);
diff --git a/tools/testing/cxl/test/mock.c b/tools/testing/cxl/test/mock.c
index b8fcb50c1027..6454b868b122 100644
--- a/tools/testing/cxl/test/mock.c
+++ b/tools/testing/cxl/test/mock.c
@@ -251,6 +251,56 @@ struct cxl_dport *__wrap_devm_cxl_add_dport_by_dev(struct cxl_port *port,
}
EXPORT_SYMBOL_NS_GPL(__wrap_devm_cxl_add_dport_by_dev, "CXL");
+int __wrap_region_intersects(resource_size_t start, size_t size,
+ unsigned long flags, unsigned long desc)
+{
+ int rc = -1;
+ int index;
+ struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
+
+ if (ops)
+ rc = ops->region_intersects(start, size, flags, desc);
+ if (rc < 0)
+ rc = region_intersects(start, size, flags, desc);
+ put_cxl_mock_ops(index);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(__wrap_region_intersects);
+
+int __wrap_region_intersects_soft_reserve(resource_size_t start, size_t size)
+{
+ int rc = -1;
+ int index;
+ struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
+
+ if (ops)
+ rc = ops->region_intersects_soft_reserve(start, size);
+ if (rc < 0)
+ rc = region_intersects_soft_reserve(start, size);
+ put_cxl_mock_ops(index);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(__wrap_region_intersects_soft_reserve);
+
+int __wrap_walk_hmem_resources(struct device *host, walk_hmem_fn fn)
+{
+ int index, rc = 0;
+ bool is_mock = strcmp(dev_name(host), "hmem_platform.1") == 0;
+ struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
+
+ if (is_mock) {
+ if (ops)
+ rc = ops->walk_hmem_resources(host, fn);
+ } else {
+ rc = walk_hmem_resources(host, fn);
+ }
+ put_cxl_mock_ops(index);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(__wrap_walk_hmem_resources);
+
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("cxl_test: emulation module");
MODULE_IMPORT_NS("ACPI");
diff --git a/tools/testing/cxl/test/mock.h b/tools/testing/cxl/test/mock.h
index 2684b89c8aa2..846d7c5d6eaa 100644
--- a/tools/testing/cxl/test/mock.h
+++ b/tools/testing/cxl/test/mock.h
@@ -2,8 +2,11 @@
#include <linux/list.h>
#include <linux/acpi.h>
+#include <linux/dax.h>
#include <cxl.h>
+extern bool hmem_test;
+
struct cxl_mock_ops {
struct list_head list;
bool (*is_mock_adev)(struct acpi_device *dev);
@@ -27,8 +30,15 @@ struct cxl_mock_ops {
int (*hmat_get_extended_linear_cache_size)(struct resource *backing_res,
int nid,
resource_size_t *cache_size);
+ int (*walk_hmem_resources)(struct device *host, walk_hmem_fn fn);
+ int (*region_intersects)(resource_size_t start, size_t size,
+ unsigned long flags, unsigned long desc);
+ int (*region_intersects_soft_reserve)(resource_size_t start,
+ size_t size);
};
+int hmem_test_init(void);
+void hmem_test_exit(void);
void register_cxl_mock_ops(struct cxl_mock_ops *ops);
void unregister_cxl_mock_ops(struct cxl_mock_ops *ops);
struct cxl_mock_ops *get_cxl_mock_ops(int *index);