* [PATCH v2 0/3] CXL: Add a loadable module for address translation
@ 2025-08-29 7:21 Alison Schofield
2025-08-29 7:21 ` [PATCH v2 1/3] cxl/region: Refactor address translation funcs for testing Alison Schofield
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Alison Schofield @ 2025-08-29 7:21 UTC (permalink / raw)
To: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
Vishal Verma, Ira Weiny, Dan Williams
Cc: linux-cxl
Changes in v2:
- Rebase on cxl/next
This set has dependencies on patches only in cxl/next.
- Make the new helpers static by default, exportable for tests (Dan)
- Restore useful dev_dbg() message for invalid position (Jonathan)
- Create and free cxims data struct in test module (Dan)
- Make some test module comments kdoc's (Jonathan)
- Move a block comment to new helper (Jonathan)
- Remove useless check for ULLONG_MAX (Jonathan)
This series refactors CXL address translation code to support testing
and adds a dedicated test module for validation of the translation
calculations.
The work is presented in 3 patches:
1. Extracts the core translation logic into standalone, testable functions.
2. Provides access to XOR interleave calculations also with a standalone
testable function.
3. Adds the test module that validates both Host to Device, and Device
to Host address translations.
Accessing the core functions in the test module was implemented by
adding the needed core/region functions to cxl_core_exports.c and
adding a similar 'cxl_acpi_exports.c' to access the cxl/acpi.c funcs.
The companion CXL Unit Test script is posted separately.
Alison Schofield (3):
cxl/region: Refactor address translation funcs for testing
cxl/acpi: Make the XOR calculations available for testing
cxl/test: Add cxl_translate module for address translation testing
drivers/cxl/acpi.c | 35 ++-
drivers/cxl/core/region.c | 147 +++++++-----
drivers/cxl/cxl.h | 5 +
include/linux/acpi.h | 7 +
tools/testing/cxl/Kbuild | 2 +
tools/testing/cxl/cxl_acpi_exports.c | 7 +
tools/testing/cxl/cxl_core_exports.c | 12 +
tools/testing/cxl/cxl_test.h | 18 ++
tools/testing/cxl/test/Kbuild | 2 +
tools/testing/cxl/test/cxl_translate.c | 303 +++++++++++++++++++++++++
10 files changed, 467 insertions(+), 71 deletions(-)
create mode 100644 tools/testing/cxl/cxl_acpi_exports.c
create mode 100644 tools/testing/cxl/cxl_test.h
create mode 100644 tools/testing/cxl/test/cxl_translate.c
base-commit: 561c4e30bff93b3c33e694a459f8580f8a6b3c8c
--
2.37.3
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 1/3] cxl/region: Refactor address translation funcs for testing
2025-08-29 7:21 [PATCH v2 0/3] CXL: Add a loadable module for address translation Alison Schofield
@ 2025-08-29 7:21 ` Alison Schofield
2025-09-04 22:05 ` Dave Jiang
2025-08-29 7:21 ` [PATCH v2 2/3] cxl/acpi: Make the XOR calculations available " Alison Schofield
2025-08-29 7:21 ` [PATCH v2 3/3] cxl/test: Add cxl_translate module for address translation testing Alison Schofield
2 siblings, 1 reply; 7+ messages in thread
From: Alison Schofield @ 2025-08-29 7:21 UTC (permalink / raw)
To: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
Vishal Verma, Ira Weiny, Dan Williams
Cc: linux-cxl
In preparation for adding a test module that exercises the address
translation calculations, extract the core calculations into stand-
alone functions that operate on base parameters without dependencies
on struct cxl_region.
Mark the new functions as static outside of test builds by adding
and using a new __mock_export label.
This refactoring enables unit testing of the address translation logic
with controlled inputs, while maintaining identical functionality in
the existing code paths.
The moved code has only one change. In the new cxl_calculate_position()
eiw_to_ways(eiw, &ways) replaces the prior usage of p->interleave_ways,
since the new function cannot depend upon struct cxl_region_params.
Signed-off-by: Alison Schofield <alison.schofield@intel.com>
---
drivers/cxl/core/region.c | 147 ++++++++++++++++++++++----------------
drivers/cxl/cxl.h | 5 ++
2 files changed, 92 insertions(+), 60 deletions(-)
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 29d3809ab2bb..71c01447b234 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -2928,28 +2928,66 @@ static bool has_spa_to_hpa(struct cxl_root_decoder *cxlrd)
return cxlrd->ops && cxlrd->ops->spa_to_hpa;
}
-u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
- u64 dpa)
+__mock_export u64 cxl_calculate_dpa_offset(u64 hpa_offset, u8 eiw, u16 eig)
{
- struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
- u64 dpa_offset, hpa_offset, bits_upper, mask_upper, hpa;
- struct cxl_region_params *p = &cxlr->params;
- struct cxl_endpoint_decoder *cxled = NULL;
- u16 eig = 0;
- u8 eiw = 0;
+ u64 dpa_offset, bits_lower, bits_upper, temp;
+
+ /*
+ * DPA offset: CXL Spec 3.2 Section 8.2.4.20.13
+ * Lower bits [IG+7:0] pass through unchanged
+ * (eiw < 8)
+ * Per spec: DPAOffset[51:IG+8] = (HPAOffset[51:IG+IW+8] >> IW)
+ * Clear the position bits to isolate upper section, then
+ * reverse the left shift by eiw that occurred during DPA->HPA
+ * (eiw >= 8)
+ * Per spec: DPAOffset[51:IG+8] = HPAOffset[51:IG+IW] / 3
+ * Extract upper bits from the correct bit range and divide by 3
+ * to recover the original DPA upper bits
+ */
+ bits_lower = hpa_offset & GENMASK_ULL(eig + 7, 0);
+ if (eiw < 8) {
+ temp = hpa_offset &= ~((u64)GENMASK(eig + eiw + 8 - 1, 0));
+ dpa_offset = temp >> eiw;
+ } else {
+ bits_upper = div64_u64(hpa_offset >> (eig + eiw), 3);
+ dpa_offset = bits_upper << (eig + 8);
+ }
+ dpa_offset |= bits_lower;
+
+ return dpa_offset;
+}
+
+__mock_export int cxl_calculate_position(u64 hpa_offset, u8 eiw, u16 eig)
+{
+ unsigned int ways = 0;
+ u64 shifted, rem;
int pos;
- for (int i = 0; i < p->nr_targets; i++) {
- cxled = p->targets[i];
- if (cxlmd == cxled_to_memdev(cxled))
- break;
+ /*
+ * Interleave position: CXL Spec 3.2 Section 8.2.4.20.13
+ * eiw < 8
+ * Position is in the IW bits at HPA_OFFSET[IG+8+IW-1:IG+8].
+ * Per spec "remove IW bits starting with bit position IG+8"
+ * eiw >= 8
+ * Position is not explicitly stored in HPA_OFFSET bits. It is
+ * derived from the modulo operation of the upper bits using
+ * the total number of interleave ways.
+ */
+ if (eiw < 8) {
+ pos = (hpa_offset >> (eig + 8)) & GENMASK(eiw - 1, 0);
+ } else {
+ shifted = hpa_offset >> (eig + 8);
+ eiw_to_ways(eiw, &ways);
+ div64_u64_rem(shifted, ways, &rem);
+ pos = rem;
}
- if (!cxled || cxlmd != cxled_to_memdev(cxled))
- return ULLONG_MAX;
- pos = cxled->pos;
- ways_to_eiw(p->interleave_ways, &eiw);
- granularity_to_eig(p->interleave_granularity, &eig);
+ return pos;
+}
+
+__mock_export u64 cxl_calculate_hpa_offset(u64 dpa_offset, int pos, u8 eiw, u16 eig)
+{
+ u64 mask_upper, hpa_offset, bits_upper;
/*
* The device position in the region interleave set was removed
@@ -2961,9 +2999,6 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
* 8.2.4.19.13 Implementation Note: Device Decode Logic
*/
- /* Remove the dpa base */
- dpa_offset = dpa - cxl_dpa_resource_start(cxled);
-
mask_upper = GENMASK_ULL(51, eig + 8);
if (eiw < 8) {
@@ -2978,6 +3013,35 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
/* The lower bits remain unchanged */
hpa_offset |= dpa_offset & GENMASK_ULL(eig + 7, 0);
+ return hpa_offset;
+}
+
+u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
+ u64 dpa)
+{
+ struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
+ struct cxl_region_params *p = &cxlr->params;
+ struct cxl_endpoint_decoder *cxled = NULL;
+ u64 dpa_offset, hpa_offset, hpa;
+ u16 eig = 0;
+ u8 eiw = 0;
+ int pos;
+
+ for (int i = 0; i < p->nr_targets; i++) {
+ cxled = p->targets[i];
+ if (cxlmd == cxled_to_memdev(cxled))
+ break;
+ }
+ if (!cxled || cxlmd != cxled_to_memdev(cxled))
+ return ULLONG_MAX;
+
+ pos = cxled->pos;
+ ways_to_eiw(p->interleave_ways, &eiw);
+ granularity_to_eig(p->interleave_granularity, &eig);
+
+ dpa_offset = dpa - cxl_dpa_resource_start(cxled);
+ hpa_offset = cxl_calculate_hpa_offset(dpa_offset, pos, eiw, eig);
+
/* Apply the hpa_offset to the region base address */
hpa = hpa_offset + p->res->start + p->cache_size;
@@ -3010,8 +3074,6 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
struct cxl_endpoint_decoder *cxled;
u64 hpa, hpa_offset, dpa_offset;
- u64 bits_upper, bits_lower;
- u64 shifted, rem, temp;
u16 eig = 0;
u8 eiw = 0;
int pos;
@@ -3033,50 +3095,15 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
} else {
hpa_offset = offset;
}
- /*
- * Interleave position: CXL Spec 3.2 Section 8.2.4.20.13
- * eiw < 8
- * Position is in the IW bits at HPA_OFFSET[IG+8+IW-1:IG+8].
- * Per spec "remove IW bits starting with bit position IG+8"
- * eiw >= 8
- * Position is not explicitly stored in HPA_OFFSET bits. It is
- * derived from the modulo operation of the upper bits using
- * the total number of interleave ways.
- */
- if (eiw < 8) {
- pos = (hpa_offset >> (eig + 8)) & GENMASK(eiw - 1, 0);
- } else {
- shifted = hpa_offset >> (eig + 8);
- div64_u64_rem(shifted, p->interleave_ways, &rem);
- pos = rem;
- }
+
+ pos = cxl_calculate_position(hpa_offset, eiw, eig);
if (pos < 0 || pos >= p->nr_targets) {
dev_dbg(&cxlr->dev, "Invalid position %d for %d targets\n",
pos, p->nr_targets);
return -ENXIO;
}
- /*
- * DPA offset: CXL Spec 3.2 Section 8.2.4.20.13
- * Lower bits [IG+7:0] pass through unchanged
- * (eiw < 8)
- * Per spec: DPAOffset[51:IG+8] = (HPAOffset[51:IG+IW+8] >> IW)
- * Clear the position bits to isolate upper section, then
- * reverse the left shift by eiw that occurred during DPA->HPA
- * (eiw >= 8)
- * Per spec: DPAOffset[51:IG+8] = HPAOffset[51:IG+IW] / 3
- * Extract upper bits from the correct bit range and divide by 3
- * to recover the original DPA upper bits
- */
- bits_lower = hpa_offset & GENMASK_ULL(eig + 7, 0);
- if (eiw < 8) {
- temp = hpa_offset &= ~((u64)GENMASK(eig + eiw + 8 - 1, 0));
- dpa_offset = temp >> eiw;
- } else {
- bits_upper = div64_u64(hpa_offset >> (eig + eiw), 3);
- dpa_offset = bits_upper << (eig + 8);
- }
- dpa_offset |= bits_lower;
+ dpa_offset = cxl_calculate_dpa_offset(hpa_offset, eiw, eig);
/* Look-up and return the result: a memdev and a DPA */
for (int i = 0; i < p->nr_targets; i++) {
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 4fe3df06f57a..57590d131f75 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -922,5 +922,10 @@ bool cxl_endpoint_decoder_reset_detected(struct cxl_port *port);
#define __mock static
#endif
+/* Unit test build overrides this to export, otherwise static */
+#ifndef __mock_export
+#define __mock_export static
+#endif
+
u16 cxl_gpf_get_dvsec(struct device *dev);
#endif /* __CXL_H__ */
--
2.37.3
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 2/3] cxl/acpi: Make the XOR calculations available for testing
2025-08-29 7:21 [PATCH v2 0/3] CXL: Add a loadable module for address translation Alison Schofield
2025-08-29 7:21 ` [PATCH v2 1/3] cxl/region: Refactor address translation funcs for testing Alison Schofield
@ 2025-08-29 7:21 ` Alison Schofield
2025-09-04 23:21 ` Dave Jiang
2025-08-29 7:21 ` [PATCH v2 3/3] cxl/test: Add cxl_translate module for address translation testing Alison Schofield
2 siblings, 1 reply; 7+ messages in thread
From: Alison Schofield @ 2025-08-29 7:21 UTC (permalink / raw)
To: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
Vishal Verma, Ira Weiny, Dan Williams
Cc: linux-cxl
In preparation for adding a test module that can exercise the address
translation functions performed by the CXL Driver, refactor the XOR
implementation like this:
- Extract the core calculation into a standalone helper function,
- Mark the new helper function as __mock_export to make it available
to test modules while keeping it otherwise static,
- Enhance the parameter validation since this new function will be
called from a test module with no guarantee of valid rameters.
- Move the define of struct cxl_cxims_data to include/linux/acpi.h
so the test module can build and pass xormap data.
Signed-off-by: Alison Schofield <alison.schofield@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
---
drivers/cxl/acpi.c | 35 ++++++++++++++++++++++++-----------
include/linux/acpi.h | 7 +++++++
2 files changed, 31 insertions(+), 11 deletions(-)
diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
index 26c494704437..f924058dbd4c 100644
--- a/drivers/cxl/acpi.c
+++ b/drivers/cxl/acpi.c
@@ -11,11 +11,6 @@
#include "cxlpci.h"
#include "cxl.h"
-struct cxl_cxims_data {
- int nr_maps;
- u64 xormaps[] __counted_by(nr_maps);
-};
-
/*
* There is one CXIMS, therefore one set of XOR maps, that all CXL Windows with
* the same host bridge granularity share. The number of maps to apply at address
@@ -28,20 +23,31 @@ static const int hbiw_to_nr_maps[HBIW_TO_NR_MAPS_SIZE] = {
[1] = 0, [2] = 1, [3] = 0, [4] = 2, [6] = 1, [8] = 3, [12] = 2, [16] = 4
};
+static const int valid_hbiw[] = { 1, 2, 3, 4, 6, 8, 12, 16 };
+
static const guid_t acpi_cxl_qtg_id_guid =
GUID_INIT(0xF365F9A6, 0xA7DE, 0x4071,
0xA6, 0x6A, 0xB4, 0x0C, 0x0B, 0x4F, 0x8E, 0x52);
-static u64 cxl_apply_xor_maps(struct cxl_root_decoder *cxlrd, u64 addr)
+__mock_export u64 cxl_do_xormap_calc(struct cxl_cxims_data *cximsd, u64 addr, int hbiw)
{
- int nr_maps_to_apply = hbiw_to_nr_maps[cxlrd->cxlsd.nr_targets];
- struct cxl_cxims_data *cximsd = cxlrd->platform_data;
+ int nr_maps_to_apply = -1;
u64 val;
int pos;
- /* No xormaps for host bridge interleave ways of 1 or 3 */
- if (!nr_maps_to_apply)
- return addr;
+ /*
+ * Strictly validate hbiw since this function is used for testing and
+ * that nullifies any expectation of trusted parameters from the CXL
+ * Region Driver.
+ */
+ for (int i = 0; i < ARRAY_SIZE(valid_hbiw); i++) {
+ if (valid_hbiw[i] == hbiw) {
+ nr_maps_to_apply = hbiw_to_nr_maps[hbiw];
+ break;
+ }
+ }
+ if (nr_maps_to_apply == -1 || nr_maps_to_apply > cximsd->nr_maps)
+ return ULLONG_MAX;
/*
* In regions using XOR interleave arithmetic the CXL HPA may not
@@ -73,6 +79,13 @@ static u64 cxl_apply_xor_maps(struct cxl_root_decoder *cxlrd, u64 addr)
return addr;
}
+static u64 cxl_apply_xor_maps(struct cxl_root_decoder *cxlrd, u64 addr)
+{
+ struct cxl_cxims_data *cximsd = cxlrd->platform_data;
+
+ return cxl_do_xormap_calc(cximsd, addr, cxlrd->cxlsd.nr_targets);
+}
+
struct cxl_cxims_context {
struct device *dev;
struct cxl_root_decoder *cxlrd;
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 1c5bb1e887cd..78e639a529c2 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -1616,4 +1616,11 @@ static inline bool acpi_node_backed_by_real_pxm(int nid)
}
#endif
+#if IS_ENABLED(CONFIG_CXL_ACPI)
+struct cxl_cxims_data {
+ int nr_maps;
+ u64 xormaps[] __counted_by(nr_maps);
+};
+#endif
+
#endif /*_LINUX_ACPI_H*/
--
2.37.3
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 3/3] cxl/test: Add cxl_translate module for address translation testing
2025-08-29 7:21 [PATCH v2 0/3] CXL: Add a loadable module for address translation Alison Schofield
2025-08-29 7:21 ` [PATCH v2 1/3] cxl/region: Refactor address translation funcs for testing Alison Schofield
2025-08-29 7:21 ` [PATCH v2 2/3] cxl/acpi: Make the XOR calculations available " Alison Schofield
@ 2025-08-29 7:21 ` Alison Schofield
2025-09-04 23:24 ` Dave Jiang
2 siblings, 1 reply; 7+ messages in thread
From: Alison Schofield @ 2025-08-29 7:21 UTC (permalink / raw)
To: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
Vishal Verma, Ira Weiny, Dan Williams
Cc: linux-cxl
Add a loadable test module that validates CXL address translation
calculations using parameterized test vectors. The module tests both
host to device and device to host address translations for Modulo and
XOR interleave arithmetic.
Test vectors are provided as module parameters in the format:
"dpa pos r_eiw r_eig hb_ways math expected_spa"
The module performs round-trip validation:
1. Translate a DPA and position to a SPA
2. Verify the result matches expected SPA
3. Translate that SPA back to a DPA and position
4. Verify round-trip consistency
The module accesses the refactored translation functions through the
exports made available only to CXL test modules.
Signed-off-by: Alison Schofield <alison.schofield@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
---
tools/testing/cxl/Kbuild | 2 +
tools/testing/cxl/cxl_acpi_exports.c | 7 +
tools/testing/cxl/cxl_core_exports.c | 12 +
tools/testing/cxl/cxl_test.h | 18 ++
tools/testing/cxl/test/Kbuild | 2 +
tools/testing/cxl/test/cxl_translate.c | 303 +++++++++++++++++++++++++
6 files changed, 344 insertions(+)
create mode 100644 tools/testing/cxl/cxl_acpi_exports.c
create mode 100644 tools/testing/cxl/cxl_test.h
create mode 100644 tools/testing/cxl/test/cxl_translate.c
diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild
index d07f14cb7aa4..95397434fc12 100644
--- a/tools/testing/cxl/Kbuild
+++ b/tools/testing/cxl/Kbuild
@@ -21,6 +21,7 @@ CXL_SRC := $(DRIVERS)/cxl
CXL_CORE_SRC := $(DRIVERS)/cxl/core
ccflags-y := -I$(srctree)/drivers/cxl/
ccflags-y += -D__mock=__weak
+ccflags-y += -D__mock_export=
ccflags-y += -DTRACE_INCLUDE_PATH=$(CXL_CORE_SRC) -I$(srctree)/drivers/cxl/core/
obj-m += cxl_acpi.o
@@ -29,6 +30,7 @@ cxl_acpi-y := $(CXL_SRC)/acpi.o
cxl_acpi-y += mock_acpi.o
cxl_acpi-y += config_check.o
cxl_acpi-y += cxl_acpi_test.o
+cxl_acpi-y += cxl_acpi_exports.o
obj-m += cxl_pmem.o
diff --git a/tools/testing/cxl/cxl_acpi_exports.c b/tools/testing/cxl/cxl_acpi_exports.c
new file mode 100644
index 000000000000..b66fa0696db1
--- /dev/null
+++ b/tools/testing/cxl/cxl_acpi_exports.c
@@ -0,0 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
+
+#include "cxl_test.h"
+
+/* Export of cxl_acpi (acpi.o) symbol used only by cxl_translate */
+EXPORT_SYMBOL_NS_GPL(cxl_do_xormap_calc, "CXL");
diff --git a/tools/testing/cxl/cxl_core_exports.c b/tools/testing/cxl/cxl_core_exports.c
index f088792a8925..4927797dfa31 100644
--- a/tools/testing/cxl/cxl_core_exports.c
+++ b/tools/testing/cxl/cxl_core_exports.c
@@ -2,6 +2,18 @@
/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
#include "cxl.h"
+#include "cxl_test.h"
/* Exporting of cxl_core symbols that are only used by cxl_test */
EXPORT_SYMBOL_NS_GPL(cxl_num_decoders_committed, "CXL");
+
+/*
+ * Exporting of cxl_core symbols used only by the cxl_translate module.
+ *
+ * Note: checkpatch warns about EXPORT_SYMBOL placement, but this is
+ * the established pattern for CXL test exports where functions are
+ * defined in drivers/cxl/core/.
+ */
+EXPORT_SYMBOL_NS_GPL(cxl_calculate_hpa_offset, "CXL");
+EXPORT_SYMBOL_NS_GPL(cxl_calculate_dpa_offset, "CXL");
+EXPORT_SYMBOL_NS_GPL(cxl_calculate_position, "CXL");
diff --git a/tools/testing/cxl/cxl_test.h b/tools/testing/cxl/cxl_test.h
new file mode 100644
index 000000000000..94777d999cb3
--- /dev/null
+++ b/tools/testing/cxl/cxl_test.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __CXL_TEST_H__
+#define __CXL_TEST_H__
+
+#include <linux/acpi.h>
+
+/* Function declarations only visible to test code */
+
+/* XOR calculation function from drivers/cxl/acpi.c */
+u64 cxl_do_xormap_calc(struct cxl_cxims_data *cximsd, u64 addr, int hbiw);
+
+/* Address translation functions from drivers/cxl/core/region.c */
+u64 cxl_calculate_hpa_offset(u64 dpa_offset, int pos, u8 eiw, u16 eig);
+u64 cxl_calculate_dpa_offset(u64 hpa_offset, u8 eiw, u16 eig);
+int cxl_calculate_position(u64 hpa_offset, u8 eiw, u16 eig);
+
+#endif
diff --git a/tools/testing/cxl/test/Kbuild b/tools/testing/cxl/test/Kbuild
index 6b1927897856..d55973e61fdd 100644
--- a/tools/testing/cxl/test/Kbuild
+++ b/tools/testing/cxl/test/Kbuild
@@ -5,6 +5,8 @@ obj-m += cxl_test.o
obj-m += cxl_mock.o
obj-m += cxl_mock_mem.o
+obj-m += cxl_translate.o
+
cxl_test-y := cxl.o
cxl_mock-y := mock.o
cxl_mock_mem-y := mem.o
diff --git a/tools/testing/cxl/test/cxl_translate.c b/tools/testing/cxl/test/cxl_translate.c
new file mode 100644
index 000000000000..f7076f4069d4
--- /dev/null
+++ b/tools/testing/cxl/test/cxl_translate.c
@@ -0,0 +1,303 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright(c) 2025 Intel Corporation. All rights reserved.
+
+#include <linux/moduleparam.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <cxlmem.h>
+
+#include "../cxl_test.h"
+
+/* Maximum number of test vectors and entry length */
+#define MAX_TABLE_ENTRIES 128
+#define MAX_ENTRY_LEN 128
+
+/* Expected number of parameters in each test vector */
+#define EXPECTED_PARAMS 7
+
+/* Module parameters for test vectors */
+static char *table[MAX_TABLE_ENTRIES];
+static int table_num;
+
+/* Interleave Arithmetic */
+#define MODULO_MATH 0
+#define XOR_MATH 1
+
+/*
+ * XOR mapping configuration
+ * The test data sets all use the same set of xormaps. When additional
+ * data sets arrive for validation, this static setup will need to
+ * be changed to accept xormaps as additional parameters.
+ */
+struct cxl_cxims_data *cximsd;
+static u64 xormaps[] = {
+ 0x2020900,
+ 0x4041200,
+ 0x1010400,
+ 0x800,
+};
+
+static int nr_maps = ARRAY_SIZE(xormaps);
+
+/**
+ * to_hpa - calculate an HPA offset from a DPA offset and position
+ *
+ * dpa_offset: device physical address offset
+ * pos: devices position in interleave
+ * r_eiw: region encoded interleave ways
+ * r_eig: region encoded interleave granularity
+ * hb_ways: host bridge interleave ways
+ * math: interleave arithmetic (MODULO_MATH or XOR_MATH)
+ *
+ * Returns: host physical address offset
+ */
+static u64 to_hpa(u64 dpa_offset, int pos, u8 r_eiw, u16 r_eig, u8 hb_ways,
+ u8 math)
+{
+ u64 hpa_offset;
+
+ /* Calculate base HPA offset from DPA and position */
+ hpa_offset = cxl_calculate_hpa_offset(dpa_offset, pos, r_eiw, r_eig);
+
+ /* Apply XOR mapping if specified */
+ if (math == XOR_MATH)
+ hpa_offset = cxl_do_xormap_calc(cximsd, hpa_offset, hb_ways);
+
+ return hpa_offset;
+}
+
+/**
+ * to_dpa - translate an HPA offset to DPA offset
+ *
+ * hpa_offset: host physical address offset
+ * r_eiw: region encoded interleave ways
+ * r_eig: region encoded interleave granularity
+ * hb_ways: host bridge interleave ways
+ * math: interleave arithmetic (MODULO_MATH or XOR_MATH)
+ *
+ * Returns: device physical address offset
+ */
+static u64 to_dpa(u64 hpa_offset, u8 r_eiw, u16 r_eig, u8 hb_ways, u8 math)
+{
+ u64 offset = hpa_offset;
+
+ /* Reverse XOR mapping if specified */
+ if (math == XOR_MATH)
+ offset = cxl_do_xormap_calc(cximsd, hpa_offset, hb_ways);
+
+ return cxl_calculate_dpa_offset(offset, r_eiw, r_eig);
+}
+
+/**
+ * to_pos - extract an interleave position from an HPA offset
+ *
+ * hpa_offset: host physical address offset
+ * r_eiw: region encoded interleave ways
+ * r_eig: region encoded interleave granularity
+ * hb_ways: host bridge interleave ways
+ * math: interleave arithmetic (MODULO_MATH or XOR_MATH)
+ *
+ * Returns: devices position in region interleave
+ */
+static u64 to_pos(u64 hpa_offset, u8 r_eiw, u16 r_eig, u8 hb_ways, u8 math)
+{
+ u64 offset = hpa_offset;
+
+ /* Reverse XOR mapping if specified */
+ if (math == XOR_MATH)
+ offset = cxl_do_xormap_calc(cximsd, hpa_offset, hb_ways);
+
+ return cxl_calculate_position(offset, r_eiw, r_eig);
+}
+
+/**
+ * run_translation_test - execute forward and reverse translations
+ *
+ * @dpa: device physical address
+ * @pos: expected position in region interleave
+ * @r_eiw: region encoded interleave ways
+ * @r_eig: region encoded interleave granularity
+ * @hb_ways: host bridge interleave ways
+ * @math: interleave arithmetic (MODULO_MATH or XOR_MATH)
+ * @expect_spa: expected system physical address
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+static int run_translation_test(u64 dpa, int pos, u8 r_eiw, u16 r_eig,
+ u8 hb_ways, int math, u64 expect_hpa)
+{
+ u64 translated_spa, reverse_dpa;
+ int reverse_pos;
+
+ /* Test Device to Host translation: DPA + POS -> SPA */
+ translated_spa = to_hpa(dpa, pos, r_eiw, r_eig, hb_ways, math);
+ if (translated_spa != expect_hpa) {
+ pr_err("Device to host failed: expected HPA %llu, got %llu\n",
+ expect_hpa, translated_spa);
+ return -1;
+ }
+
+ /* Test Host to Device DPA translation: SPA -> DPA */
+ reverse_dpa = to_dpa(translated_spa, r_eiw, r_eig, hb_ways, math);
+ if (reverse_dpa != dpa) {
+ pr_err("Host to Device DPA failed: expected %llu, got %llu\n",
+ dpa, reverse_dpa);
+ return -1;
+ }
+
+ /* Test Host to Device Position translation: SPA -> POS */
+ reverse_pos = to_pos(translated_spa, r_eiw, r_eig, hb_ways, math);
+ if (reverse_pos != pos) {
+ pr_err("Position lookup failed: expected %d, got %d\n", pos,
+ reverse_pos);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * parse_test_vector - parse a single test vector string
+ *
+ * entry: test vector string to parse
+ * dpa: device physical address
+ * pos: expected position in region interleave
+ * r_eiw: region encoded interleave ways
+ * r_eig: region encoded interleave granularity
+ * hb_ways: host bridge interleave ways
+ * math: interleave arithmetic (MODULO_MATH or XOR_MATH)
+ * expect_spa: expected system physical address
+ *
+ * Returns: 0 on success, negative error code on failure
+ */
+static int parse_test_vector(const char *entry, u64 *dpa, int *pos, u8 *r_eiw,
+ u16 *r_eig, u8 *hb_ways, int *math,
+ u64 *expect_hpa)
+{
+ unsigned int tmp_r_eiw, tmp_r_eig, tmp_hb_ways;
+ int parsed;
+
+ parsed = sscanf(entry, "%llu %d %u %u %u %d %llu", dpa, pos, &tmp_r_eiw,
+ &tmp_r_eig, &tmp_hb_ways, math, expect_hpa);
+
+ if (parsed != EXPECTED_PARAMS) {
+ pr_err("Parse error: expected %d parameters, got %d in '%s'\n",
+ EXPECTED_PARAMS, parsed, entry);
+ return -EINVAL;
+ }
+ if (tmp_r_eiw > U8_MAX || tmp_r_eig > U16_MAX || tmp_hb_ways > U8_MAX) {
+ pr_err("Parameter overflow in entry: '%s'\n", entry);
+ return -ERANGE;
+ }
+ if (*math != MODULO_MATH && *math != XOR_MATH) {
+ pr_err("Invalid math type %d in entry: '%s'\n", *math, entry);
+ return -EINVAL;
+ }
+ *r_eiw = tmp_r_eiw;
+ *r_eig = tmp_r_eig;
+ *hb_ways = tmp_hb_ways;
+
+ return 0;
+}
+
+/*
+ * setup_xor_mapping - Initialize XOR mapping data structure
+ *
+ * The test data sets all use the same set of xormaps. When additional
+ * data sets arrive for validation, this static setup will need to
+ * be changed to accept xormaps as additional parameters.
+ *
+ * Returns: 0 on success, negative error code on failure
+ */
+static int setup_xor_mapping(void)
+{
+ if (nr_maps <= 0)
+ return -EINVAL;
+
+ cximsd = kzalloc(struct_size(cximsd, xormaps, nr_maps), GFP_KERNEL);
+ if (!cximsd)
+ return -ENOMEM;
+
+ memcpy(cximsd->xormaps, xormaps, nr_maps * sizeof(*cximsd->xormaps));
+ cximsd->nr_maps = nr_maps;
+
+ return 0;
+}
+
+/*
+ * cxl_translate_init - parse test vectors and kicks off translation tests
+ *
+ * Returns: 0 on success, negative error code on failure
+ */
+static int __init cxl_translate_init(void)
+{
+ int ret, i;
+
+ /* Validate module parameters */
+ if (table_num == 0) {
+ pr_err("No test vectors provided\n");
+ return -EINVAL;
+ }
+
+ pr_info("CXL translate test module loaded with %d test vectors\n",
+ table_num);
+
+ ret = setup_xor_mapping();
+ if (ret)
+ return ret;
+
+ /* Process each test vector */
+ for (i = 0; i < table_num; i++) {
+ u64 dpa, expect_spa;
+ int pos, math;
+ u8 r_eiw, hb_ways;
+ u16 r_eig;
+
+ pr_debug("Processing test vector %d: '%s'\n", i, table[i]);
+
+ /* Parse the test vector */
+ ret = parse_test_vector(table[i], &dpa, &pos, &r_eiw, &r_eig,
+ &hb_ways, &math, &expect_spa);
+ if (ret) {
+ pr_err("CXL Translate Test %d: FAIL\n"
+ " Failed to parse test vector '%s'\n",
+ i, table[i]);
+ continue;
+ }
+ /* Run the translation test */
+ ret = run_translation_test(dpa, pos, r_eiw, r_eig, hb_ways,
+ math, expect_spa);
+ if (ret) {
+ pr_err("CXL Translate Test %d: FAIL\n"
+ " dpa=%llu pos=%d r_eiw=%u r_eig=%u hb_ways=%u math=%s expect_spa=%llu\n",
+ i, dpa, pos, r_eiw, r_eig, hb_ways,
+ (math == XOR_MATH) ? "XOR" : "MODULO",
+ expect_spa);
+ } else {
+ pr_info("CXL Translate Test %d: PASS\n", i);
+ }
+ }
+
+ pr_info("CXL translate test completed\n");
+ return 0;
+}
+
+static void __exit cxl_translate_exit(void)
+{
+ kfree(cximsd);
+
+ pr_info("CXL translate test module unloaded\n");
+}
+
+module_param_array(table, charp, &table_num, 0444);
+MODULE_PARM_DESC(table, "Test vectors as space-separated decimal strings");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("cxl_test: cxl address translation test module");
+MODULE_IMPORT_NS("CXL");
+
+module_init(cxl_translate_init);
+module_exit(cxl_translate_exit);
--
2.37.3
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v2 1/3] cxl/region: Refactor address translation funcs for testing
2025-08-29 7:21 ` [PATCH v2 1/3] cxl/region: Refactor address translation funcs for testing Alison Schofield
@ 2025-09-04 22:05 ` Dave Jiang
0 siblings, 0 replies; 7+ messages in thread
From: Dave Jiang @ 2025-09-04 22:05 UTC (permalink / raw)
To: Alison Schofield, Davidlohr Bueso, Jonathan Cameron, Vishal Verma,
Ira Weiny, Dan Williams
Cc: linux-cxl
On 8/29/25 12:21 AM, Alison Schofield wrote:
> In preparation for adding a test module that exercises the address
> translation calculations, extract the core calculations into stand-
> alone functions that operate on base parameters without dependencies
> on struct cxl_region.
>
> Mark the new functions as static outside of test builds by adding
> and using a new __mock_export label.
>
> This refactoring enables unit testing of the address translation logic
> with controlled inputs, while maintaining identical functionality in
> the existing code paths.
>
> The moved code has only one change. In the new cxl_calculate_position()
> eiw_to_ways(eiw, &ways) replaces the prior usage of p->interleave_ways,
> since the new function cannot depend upon struct cxl_region_params.
>
> Signed-off-by: Alison Schofield <alison.schofield@intel.com>
> ---
> drivers/cxl/core/region.c | 147 ++++++++++++++++++++++----------------
> drivers/cxl/cxl.h | 5 ++
> 2 files changed, 92 insertions(+), 60 deletions(-)
>
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 29d3809ab2bb..71c01447b234 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -2928,28 +2928,66 @@ static bool has_spa_to_hpa(struct cxl_root_decoder *cxlrd)
> return cxlrd->ops && cxlrd->ops->spa_to_hpa;
> }
>
> -u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
> - u64 dpa)
> +__mock_export u64 cxl_calculate_dpa_offset(u64 hpa_offset, u8 eiw, u16 eig)
> {
> - struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
> - u64 dpa_offset, hpa_offset, bits_upper, mask_upper, hpa;
> - struct cxl_region_params *p = &cxlr->params;
> - struct cxl_endpoint_decoder *cxled = NULL;
> - u16 eig = 0;
> - u8 eiw = 0;
> + u64 dpa_offset, bits_lower, bits_upper, temp;
> +
> + /*
> + * DPA offset: CXL Spec 3.2 Section 8.2.4.20.13
> + * Lower bits [IG+7:0] pass through unchanged
> + * (eiw < 8)
> + * Per spec: DPAOffset[51:IG+8] = (HPAOffset[51:IG+IW+8] >> IW)
> + * Clear the position bits to isolate upper section, then
> + * reverse the left shift by eiw that occurred during DPA->HPA
> + * (eiw >= 8)
> + * Per spec: DPAOffset[51:IG+8] = HPAOffset[51:IG+IW] / 3
> + * Extract upper bits from the correct bit range and divide by 3
> + * to recover the original DPA upper bits
> + */
> + bits_lower = hpa_offset & GENMASK_ULL(eig + 7, 0);
> + if (eiw < 8) {
> + temp = hpa_offset &= ~((u64)GENMASK(eig + eiw + 8 - 1, 0));
Should this use GENMASK_ULL() instead of casting to u64?
> + dpa_offset = temp >> eiw;
> + } else {
> + bits_upper = div64_u64(hpa_offset >> (eig + eiw), 3);
> + dpa_offset = bits_upper << (eig + 8);
> + }
> + dpa_offset |= bits_lower;
> +
> + return dpa_offset;
> +}
> +
> +__mock_export int cxl_calculate_position(u64 hpa_offset, u8 eiw, u16 eig)
> +{
> + unsigned int ways = 0;
> + u64 shifted, rem;
> int pos;
>
> - for (int i = 0; i < p->nr_targets; i++) {
> - cxled = p->targets[i];
> - if (cxlmd == cxled_to_memdev(cxled))
> - break;
> + /*
> + * Interleave position: CXL Spec 3.2 Section 8.2.4.20.13
> + * eiw < 8
> + * Position is in the IW bits at HPA_OFFSET[IG+8+IW-1:IG+8].
> + * Per spec "remove IW bits starting with bit position IG+8"
> + * eiw >= 8
> + * Position is not explicitly stored in HPA_OFFSET bits. It is
> + * derived from the modulo operation of the upper bits using
> + * the total number of interleave ways.
> + */
> + if (eiw < 8) {
> + pos = (hpa_offset >> (eig + 8)) & GENMASK(eiw - 1, 0);
> + } else {
> + shifted = hpa_offset >> (eig + 8);
> + eiw_to_ways(eiw, &ways);
> + div64_u64_rem(shifted, ways, &rem);
> + pos = rem;
> }
> - if (!cxled || cxlmd != cxled_to_memdev(cxled))
> - return ULLONG_MAX;
>
> - pos = cxled->pos;
> - ways_to_eiw(p->interleave_ways, &eiw);
> - granularity_to_eig(p->interleave_granularity, &eig);
> + return pos;
> +}
> +
> +__mock_export u64 cxl_calculate_hpa_offset(u64 dpa_offset, int pos, u8 eiw, u16 eig)
> +{
> + u64 mask_upper, hpa_offset, bits_upper;
>
> /*
> * The device position in the region interleave set was removed
> @@ -2961,9 +2999,6 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
> * 8.2.4.19.13 Implementation Note: Device Decode Logic
> */
>
> - /* Remove the dpa base */
> - dpa_offset = dpa - cxl_dpa_resource_start(cxled);
> -
> mask_upper = GENMASK_ULL(51, eig + 8);
>
> if (eiw < 8) {
> @@ -2978,6 +3013,35 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
> /* The lower bits remain unchanged */
> hpa_offset |= dpa_offset & GENMASK_ULL(eig + 7, 0);
>
> + return hpa_offset;
> +}
> +
> +u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
> + u64 dpa)
> +{
> + struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
> + struct cxl_region_params *p = &cxlr->params;
> + struct cxl_endpoint_decoder *cxled = NULL;
> + u64 dpa_offset, hpa_offset, hpa;
> + u16 eig = 0;
> + u8 eiw = 0;
> + int pos;
> +
> + for (int i = 0; i < p->nr_targets; i++) {
> + cxled = p->targets[i];
> + if (cxlmd == cxled_to_memdev(cxled))
> + break;
> + }
> + if (!cxled || cxlmd != cxled_to_memdev(cxled))
> + return ULLONG_MAX;
Maybe:
for (int i = 0; i < p->nr_targets; i++) {
if (cxlmd == cxled_to_memdev(p->targets[i])) {
cxled = p->targets[i];
break;
}
}
if (!cxled)
return ULLONG_MAX;
DJ
> +
> + pos = cxled->pos;
> + ways_to_eiw(p->interleave_ways, &eiw);
> + granularity_to_eig(p->interleave_granularity, &eig);
> +
> + dpa_offset = dpa - cxl_dpa_resource_start(cxled);
> + hpa_offset = cxl_calculate_hpa_offset(dpa_offset, pos, eiw, eig);
> +
> /* Apply the hpa_offset to the region base address */
> hpa = hpa_offset + p->res->start + p->cache_size;
>
> @@ -3010,8 +3074,6 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
> struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
> struct cxl_endpoint_decoder *cxled;
> u64 hpa, hpa_offset, dpa_offset;
> - u64 bits_upper, bits_lower;
> - u64 shifted, rem, temp;
> u16 eig = 0;
> u8 eiw = 0;
> int pos;
> @@ -3033,50 +3095,15 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
> } else {
> hpa_offset = offset;
> }
> - /*
> - * Interleave position: CXL Spec 3.2 Section 8.2.4.20.13
> - * eiw < 8
> - * Position is in the IW bits at HPA_OFFSET[IG+8+IW-1:IG+8].
> - * Per spec "remove IW bits starting with bit position IG+8"
> - * eiw >= 8
> - * Position is not explicitly stored in HPA_OFFSET bits. It is
> - * derived from the modulo operation of the upper bits using
> - * the total number of interleave ways.
> - */
> - if (eiw < 8) {
> - pos = (hpa_offset >> (eig + 8)) & GENMASK(eiw - 1, 0);
> - } else {
> - shifted = hpa_offset >> (eig + 8);
> - div64_u64_rem(shifted, p->interleave_ways, &rem);
> - pos = rem;
> - }
> +
> + pos = cxl_calculate_position(hpa_offset, eiw, eig);
> if (pos < 0 || pos >= p->nr_targets) {
> dev_dbg(&cxlr->dev, "Invalid position %d for %d targets\n",
> pos, p->nr_targets);
> return -ENXIO;
> }
>
> - /*
> - * DPA offset: CXL Spec 3.2 Section 8.2.4.20.13
> - * Lower bits [IG+7:0] pass through unchanged
> - * (eiw < 8)
> - * Per spec: DPAOffset[51:IG+8] = (HPAOffset[51:IG+IW+8] >> IW)
> - * Clear the position bits to isolate upper section, then
> - * reverse the left shift by eiw that occurred during DPA->HPA
> - * (eiw >= 8)
> - * Per spec: DPAOffset[51:IG+8] = HPAOffset[51:IG+IW] / 3
> - * Extract upper bits from the correct bit range and divide by 3
> - * to recover the original DPA upper bits
> - */
> - bits_lower = hpa_offset & GENMASK_ULL(eig + 7, 0);
> - if (eiw < 8) {
> - temp = hpa_offset &= ~((u64)GENMASK(eig + eiw + 8 - 1, 0));
> - dpa_offset = temp >> eiw;
> - } else {
> - bits_upper = div64_u64(hpa_offset >> (eig + eiw), 3);
> - dpa_offset = bits_upper << (eig + 8);
> - }
> - dpa_offset |= bits_lower;
> + dpa_offset = cxl_calculate_dpa_offset(hpa_offset, eiw, eig);
>
> /* Look-up and return the result: a memdev and a DPA */
> for (int i = 0; i < p->nr_targets; i++) {
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 4fe3df06f57a..57590d131f75 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -922,5 +922,10 @@ bool cxl_endpoint_decoder_reset_detected(struct cxl_port *port);
> #define __mock static
> #endif
>
> +/* Unit test build overrides this to export, otherwise static */
> +#ifndef __mock_export
> +#define __mock_export static
> +#endif
> +
> u16 cxl_gpf_get_dvsec(struct device *dev);
> #endif /* __CXL_H__ */
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 2/3] cxl/acpi: Make the XOR calculations available for testing
2025-08-29 7:21 ` [PATCH v2 2/3] cxl/acpi: Make the XOR calculations available " Alison Schofield
@ 2025-09-04 23:21 ` Dave Jiang
0 siblings, 0 replies; 7+ messages in thread
From: Dave Jiang @ 2025-09-04 23:21 UTC (permalink / raw)
To: Alison Schofield, Davidlohr Bueso, Jonathan Cameron, Vishal Verma,
Ira Weiny, Dan Williams
Cc: linux-cxl
On 8/29/25 12:21 AM, Alison Schofield wrote:
> In preparation for adding a test module that can exercise the address
> translation functions performed by the CXL Driver, refactor the XOR
> implementation like this:
>
> - Extract the core calculation into a standalone helper function,
> - Mark the new helper function as __mock_export to make it available
> to test modules while keeping it otherwise static,
> - Enhance the parameter validation since this new function will be
> called from a test module with no guarantee of valid rameters.
> - Move the define of struct cxl_cxims_data to include/linux/acpi.h
> so the test module can build and pass xormap data.
>
> Signed-off-by: Alison Schofield <alison.schofield@intel.com>
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> ---
> drivers/cxl/acpi.c | 35 ++++++++++++++++++++++++-----------
> include/linux/acpi.h | 7 +++++++
> 2 files changed, 31 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
> index 26c494704437..f924058dbd4c 100644
> --- a/drivers/cxl/acpi.c
> +++ b/drivers/cxl/acpi.c
> @@ -11,11 +11,6 @@
> #include "cxlpci.h"
> #include "cxl.h"
>
> -struct cxl_cxims_data {
> - int nr_maps;
> - u64 xormaps[] __counted_by(nr_maps);
> -};
> -
> /*
> * There is one CXIMS, therefore one set of XOR maps, that all CXL Windows with
> * the same host bridge granularity share. The number of maps to apply at address
> @@ -28,20 +23,31 @@ static const int hbiw_to_nr_maps[HBIW_TO_NR_MAPS_SIZE] = {
> [1] = 0, [2] = 1, [3] = 0, [4] = 2, [6] = 1, [8] = 3, [12] = 2, [16] = 4
> };
>
> +static const int valid_hbiw[] = { 1, 2, 3, 4, 6, 8, 12, 16 };
> +
> static const guid_t acpi_cxl_qtg_id_guid =
> GUID_INIT(0xF365F9A6, 0xA7DE, 0x4071,
> 0xA6, 0x6A, 0xB4, 0x0C, 0x0B, 0x4F, 0x8E, 0x52);
>
> -static u64 cxl_apply_xor_maps(struct cxl_root_decoder *cxlrd, u64 addr)
> +__mock_export u64 cxl_do_xormap_calc(struct cxl_cxims_data *cximsd, u64 addr, int hbiw)
> {
> - int nr_maps_to_apply = hbiw_to_nr_maps[cxlrd->cxlsd.nr_targets];
> - struct cxl_cxims_data *cximsd = cxlrd->platform_data;
> + int nr_maps_to_apply = -1;
> u64 val;
> int pos;
>
> - /* No xormaps for host bridge interleave ways of 1 or 3 */
> - if (!nr_maps_to_apply)
> - return addr;
> + /*
> + * Strictly validate hbiw since this function is used for testing and
> + * that nullifies any expectation of trusted parameters from the CXL
> + * Region Driver.
> + */
> + for (int i = 0; i < ARRAY_SIZE(valid_hbiw); i++) {
> + if (valid_hbiw[i] == hbiw) {
> + nr_maps_to_apply = hbiw_to_nr_maps[hbiw];
> + break;
> + }
> + }
> + if (nr_maps_to_apply == -1 || nr_maps_to_apply > cximsd->nr_maps)
> + return ULLONG_MAX;
>
> /*
> * In regions using XOR interleave arithmetic the CXL HPA may not
> @@ -73,6 +79,13 @@ static u64 cxl_apply_xor_maps(struct cxl_root_decoder *cxlrd, u64 addr)
> return addr;
> }
>
> +static u64 cxl_apply_xor_maps(struct cxl_root_decoder *cxlrd, u64 addr)
> +{
> + struct cxl_cxims_data *cximsd = cxlrd->platform_data;
> +
> + return cxl_do_xormap_calc(cximsd, addr, cxlrd->cxlsd.nr_targets);
> +}
> +
> struct cxl_cxims_context {
> struct device *dev;
> struct cxl_root_decoder *cxlrd;
> diff --git a/include/linux/acpi.h b/include/linux/acpi.h
> index 1c5bb1e887cd..78e639a529c2 100644
> --- a/include/linux/acpi.h
> +++ b/include/linux/acpi.h
> @@ -1616,4 +1616,11 @@ static inline bool acpi_node_backed_by_real_pxm(int nid)
> }
> #endif
>
> +#if IS_ENABLED(CONFIG_CXL_ACPI)
> +struct cxl_cxims_data {
> + int nr_maps;
> + u64 xormaps[] __counted_by(nr_maps);
> +};
> +#endif
Maybe move this to drivers/cxl/cxl.h instead?
> +
> #endif /*_LINUX_ACPI_H*/
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 3/3] cxl/test: Add cxl_translate module for address translation testing
2025-08-29 7:21 ` [PATCH v2 3/3] cxl/test: Add cxl_translate module for address translation testing Alison Schofield
@ 2025-09-04 23:24 ` Dave Jiang
0 siblings, 0 replies; 7+ messages in thread
From: Dave Jiang @ 2025-09-04 23:24 UTC (permalink / raw)
To: Alison Schofield, Davidlohr Bueso, Jonathan Cameron, Vishal Verma,
Ira Weiny, Dan Williams
Cc: linux-cxl
On 8/29/25 12:21 AM, Alison Schofield wrote:
> Add a loadable test module that validates CXL address translation
> calculations using parameterized test vectors. The module tests both
> host to device and device to host address translations for Modulo and
> XOR interleave arithmetic.
>
> Test vectors are provided as module parameters in the format:
> "dpa pos r_eiw r_eig hb_ways math expected_spa"
>
> The module performs round-trip validation:
> 1. Translate a DPA and position to a SPA
> 2. Verify the result matches expected SPA
> 3. Translate that SPA back to a DPA and position
> 4. Verify round-trip consistency
>
> The module accesses the refactored translation functions through the
> exports made available only to CXL test modules.
>
> Signed-off-by: Alison Schofield <alison.schofield@intel.com>
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> ---
> tools/testing/cxl/Kbuild | 2 +
> tools/testing/cxl/cxl_acpi_exports.c | 7 +
> tools/testing/cxl/cxl_core_exports.c | 12 +
> tools/testing/cxl/cxl_test.h | 18 ++
> tools/testing/cxl/test/Kbuild | 2 +
> tools/testing/cxl/test/cxl_translate.c | 303 +++++++++++++++++++++++++
> 6 files changed, 344 insertions(+)
> create mode 100644 tools/testing/cxl/cxl_acpi_exports.c
> create mode 100644 tools/testing/cxl/cxl_test.h
> create mode 100644 tools/testing/cxl/test/cxl_translate.c
>
> diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild
> index d07f14cb7aa4..95397434fc12 100644
> --- a/tools/testing/cxl/Kbuild
> +++ b/tools/testing/cxl/Kbuild
> @@ -21,6 +21,7 @@ CXL_SRC := $(DRIVERS)/cxl
> CXL_CORE_SRC := $(DRIVERS)/cxl/core
> ccflags-y := -I$(srctree)/drivers/cxl/
> ccflags-y += -D__mock=__weak
> +ccflags-y += -D__mock_export=
> ccflags-y += -DTRACE_INCLUDE_PATH=$(CXL_CORE_SRC) -I$(srctree)/drivers/cxl/core/
>
> obj-m += cxl_acpi.o
> @@ -29,6 +30,7 @@ cxl_acpi-y := $(CXL_SRC)/acpi.o
> cxl_acpi-y += mock_acpi.o
> cxl_acpi-y += config_check.o
> cxl_acpi-y += cxl_acpi_test.o
> +cxl_acpi-y += cxl_acpi_exports.o
>
> obj-m += cxl_pmem.o
>
> diff --git a/tools/testing/cxl/cxl_acpi_exports.c b/tools/testing/cxl/cxl_acpi_exports.c
> new file mode 100644
> index 000000000000..b66fa0696db1
> --- /dev/null
> +++ b/tools/testing/cxl/cxl_acpi_exports.c
> @@ -0,0 +1,7 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
> +
> +#include "cxl_test.h"
> +
> +/* Export of cxl_acpi (acpi.o) symbol used only by cxl_translate */
> +EXPORT_SYMBOL_NS_GPL(cxl_do_xormap_calc, "CXL");
> diff --git a/tools/testing/cxl/cxl_core_exports.c b/tools/testing/cxl/cxl_core_exports.c
> index f088792a8925..4927797dfa31 100644
> --- a/tools/testing/cxl/cxl_core_exports.c
> +++ b/tools/testing/cxl/cxl_core_exports.c
> @@ -2,6 +2,18 @@
> /* Copyright(c) 2022 Intel Corporation. All rights reserved. */
>
> #include "cxl.h"
> +#include "cxl_test.h"
>
> /* Exporting of cxl_core symbols that are only used by cxl_test */
> EXPORT_SYMBOL_NS_GPL(cxl_num_decoders_committed, "CXL");
> +
> +/*
> + * Exporting of cxl_core symbols used only by the cxl_translate module.
> + *
> + * Note: checkpatch warns about EXPORT_SYMBOL placement, but this is
> + * the established pattern for CXL test exports where functions are
> + * defined in drivers/cxl/core/.
> + */
> +EXPORT_SYMBOL_NS_GPL(cxl_calculate_hpa_offset, "CXL");
> +EXPORT_SYMBOL_NS_GPL(cxl_calculate_dpa_offset, "CXL");
> +EXPORT_SYMBOL_NS_GPL(cxl_calculate_position, "CXL");
> diff --git a/tools/testing/cxl/cxl_test.h b/tools/testing/cxl/cxl_test.h
> new file mode 100644
> index 000000000000..94777d999cb3
> --- /dev/null
> +++ b/tools/testing/cxl/cxl_test.h
> @@ -0,0 +1,18 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#ifndef __CXL_TEST_H__
> +#define __CXL_TEST_H__
> +
> +#include <linux/acpi.h>
> +
> +/* Function declarations only visible to test code */
> +
> +/* XOR calculation function from drivers/cxl/acpi.c */
> +u64 cxl_do_xormap_calc(struct cxl_cxims_data *cximsd, u64 addr, int hbiw);
> +
> +/* Address translation functions from drivers/cxl/core/region.c */
> +u64 cxl_calculate_hpa_offset(u64 dpa_offset, int pos, u8 eiw, u16 eig);
> +u64 cxl_calculate_dpa_offset(u64 hpa_offset, u8 eiw, u16 eig);
> +int cxl_calculate_position(u64 hpa_offset, u8 eiw, u16 eig);
> +
> +#endif
> diff --git a/tools/testing/cxl/test/Kbuild b/tools/testing/cxl/test/Kbuild
> index 6b1927897856..d55973e61fdd 100644
> --- a/tools/testing/cxl/test/Kbuild
> +++ b/tools/testing/cxl/test/Kbuild
> @@ -5,6 +5,8 @@ obj-m += cxl_test.o
> obj-m += cxl_mock.o
> obj-m += cxl_mock_mem.o
>
> +obj-m += cxl_translate.o
> +
> cxl_test-y := cxl.o
> cxl_mock-y := mock.o
> cxl_mock_mem-y := mem.o
> diff --git a/tools/testing/cxl/test/cxl_translate.c b/tools/testing/cxl/test/cxl_translate.c
> new file mode 100644
> index 000000000000..f7076f4069d4
> --- /dev/null
> +++ b/tools/testing/cxl/test/cxl_translate.c
> @@ -0,0 +1,303 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +// Copyright(c) 2025 Intel Corporation. All rights reserved.
> +
> +#include <linux/moduleparam.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/acpi.h>
> +#include <cxlmem.h>
> +
> +#include "../cxl_test.h"
> +
> +/* Maximum number of test vectors and entry length */
> +#define MAX_TABLE_ENTRIES 128
> +#define MAX_ENTRY_LEN 128
> +
> +/* Expected number of parameters in each test vector */
> +#define EXPECTED_PARAMS 7
> +
> +/* Module parameters for test vectors */
> +static char *table[MAX_TABLE_ENTRIES];
> +static int table_num;
> +
> +/* Interleave Arithmetic */
> +#define MODULO_MATH 0
> +#define XOR_MATH 1
> +
> +/*
> + * XOR mapping configuration
> + * The test data sets all use the same set of xormaps. When additional
> + * data sets arrive for validation, this static setup will need to
> + * be changed to accept xormaps as additional parameters.
> + */
> +struct cxl_cxims_data *cximsd;
> +static u64 xormaps[] = {
> + 0x2020900,
> + 0x4041200,
> + 0x1010400,
> + 0x800,
> +};
> +
> +static int nr_maps = ARRAY_SIZE(xormaps);
> +
> +/**
> + * to_hpa - calculate an HPA offset from a DPA offset and position
> + *
> + * dpa_offset: device physical address offset
> + * pos: devices position in interleave
> + * r_eiw: region encoded interleave ways
> + * r_eig: region encoded interleave granularity
> + * hb_ways: host bridge interleave ways
> + * math: interleave arithmetic (MODULO_MATH or XOR_MATH)
> + *
> + * Returns: host physical address offset
> + */
> +static u64 to_hpa(u64 dpa_offset, int pos, u8 r_eiw, u16 r_eig, u8 hb_ways,
> + u8 math)
> +{
> + u64 hpa_offset;
> +
> + /* Calculate base HPA offset from DPA and position */
> + hpa_offset = cxl_calculate_hpa_offset(dpa_offset, pos, r_eiw, r_eig);
> +
> + /* Apply XOR mapping if specified */
> + if (math == XOR_MATH)
> + hpa_offset = cxl_do_xormap_calc(cximsd, hpa_offset, hb_ways);
> +
> + return hpa_offset;
> +}
> +
> +/**
> + * to_dpa - translate an HPA offset to DPA offset
> + *
> + * hpa_offset: host physical address offset
> + * r_eiw: region encoded interleave ways
> + * r_eig: region encoded interleave granularity
> + * hb_ways: host bridge interleave ways
> + * math: interleave arithmetic (MODULO_MATH or XOR_MATH)
> + *
> + * Returns: device physical address offset
> + */
> +static u64 to_dpa(u64 hpa_offset, u8 r_eiw, u16 r_eig, u8 hb_ways, u8 math)
> +{
> + u64 offset = hpa_offset;
> +
> + /* Reverse XOR mapping if specified */
> + if (math == XOR_MATH)
> + offset = cxl_do_xormap_calc(cximsd, hpa_offset, hb_ways);
> +
> + return cxl_calculate_dpa_offset(offset, r_eiw, r_eig);
> +}
> +
> +/**
> + * to_pos - extract an interleave position from an HPA offset
> + *
> + * hpa_offset: host physical address offset
> + * r_eiw: region encoded interleave ways
> + * r_eig: region encoded interleave granularity
> + * hb_ways: host bridge interleave ways
> + * math: interleave arithmetic (MODULO_MATH or XOR_MATH)
> + *
> + * Returns: devices position in region interleave
> + */
> +static u64 to_pos(u64 hpa_offset, u8 r_eiw, u16 r_eig, u8 hb_ways, u8 math)
> +{
> + u64 offset = hpa_offset;
> +
> + /* Reverse XOR mapping if specified */
> + if (math == XOR_MATH)
> + offset = cxl_do_xormap_calc(cximsd, hpa_offset, hb_ways);
> +
> + return cxl_calculate_position(offset, r_eiw, r_eig);
> +}
> +
> +/**
> + * run_translation_test - execute forward and reverse translations
> + *
> + * @dpa: device physical address
> + * @pos: expected position in region interleave
> + * @r_eiw: region encoded interleave ways
> + * @r_eig: region encoded interleave granularity
> + * @hb_ways: host bridge interleave ways
> + * @math: interleave arithmetic (MODULO_MATH or XOR_MATH)
> + * @expect_spa: expected system physical address
> + *
> + * Returns: 0 on success, -1 on failure
> + */
> +static int run_translation_test(u64 dpa, int pos, u8 r_eiw, u16 r_eig,
> + u8 hb_ways, int math, u64 expect_hpa)
> +{
> + u64 translated_spa, reverse_dpa;
> + int reverse_pos;
> +
> + /* Test Device to Host translation: DPA + POS -> SPA */
> + translated_spa = to_hpa(dpa, pos, r_eiw, r_eig, hb_ways, math);
> + if (translated_spa != expect_hpa) {
> + pr_err("Device to host failed: expected HPA %llu, got %llu\n",
> + expect_hpa, translated_spa);
> + return -1;
> + }
> +
> + /* Test Host to Device DPA translation: SPA -> DPA */
> + reverse_dpa = to_dpa(translated_spa, r_eiw, r_eig, hb_ways, math);
> + if (reverse_dpa != dpa) {
> + pr_err("Host to Device DPA failed: expected %llu, got %llu\n",
> + dpa, reverse_dpa);
> + return -1;
> + }
> +
> + /* Test Host to Device Position translation: SPA -> POS */
> + reverse_pos = to_pos(translated_spa, r_eiw, r_eig, hb_ways, math);
> + if (reverse_pos != pos) {
> + pr_err("Position lookup failed: expected %d, got %d\n", pos,
> + reverse_pos);
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * parse_test_vector - parse a single test vector string
> + *
> + * entry: test vector string to parse
> + * dpa: device physical address
> + * pos: expected position in region interleave
> + * r_eiw: region encoded interleave ways
> + * r_eig: region encoded interleave granularity
> + * hb_ways: host bridge interleave ways
> + * math: interleave arithmetic (MODULO_MATH or XOR_MATH)
> + * expect_spa: expected system physical address
> + *
> + * Returns: 0 on success, negative error code on failure
> + */
> +static int parse_test_vector(const char *entry, u64 *dpa, int *pos, u8 *r_eiw,
> + u16 *r_eig, u8 *hb_ways, int *math,
> + u64 *expect_hpa)
> +{
> + unsigned int tmp_r_eiw, tmp_r_eig, tmp_hb_ways;
> + int parsed;
> +
> + parsed = sscanf(entry, "%llu %d %u %u %u %d %llu", dpa, pos, &tmp_r_eiw,
> + &tmp_r_eig, &tmp_hb_ways, math, expect_hpa);
> +
> + if (parsed != EXPECTED_PARAMS) {
> + pr_err("Parse error: expected %d parameters, got %d in '%s'\n",
> + EXPECTED_PARAMS, parsed, entry);
> + return -EINVAL;
> + }
> + if (tmp_r_eiw > U8_MAX || tmp_r_eig > U16_MAX || tmp_hb_ways > U8_MAX) {
> + pr_err("Parameter overflow in entry: '%s'\n", entry);
> + return -ERANGE;
> + }
> + if (*math != MODULO_MATH && *math != XOR_MATH) {
> + pr_err("Invalid math type %d in entry: '%s'\n", *math, entry);
> + return -EINVAL;
> + }
> + *r_eiw = tmp_r_eiw;
> + *r_eig = tmp_r_eig;
> + *hb_ways = tmp_hb_ways;
> +
> + return 0;
> +}
> +
> +/*
> + * setup_xor_mapping - Initialize XOR mapping data structure
> + *
> + * The test data sets all use the same set of xormaps. When additional
> + * data sets arrive for validation, this static setup will need to
> + * be changed to accept xormaps as additional parameters.
> + *
> + * Returns: 0 on success, negative error code on failure
> + */
> +static int setup_xor_mapping(void)
> +{
> + if (nr_maps <= 0)
> + return -EINVAL;
> +
> + cximsd = kzalloc(struct_size(cximsd, xormaps, nr_maps), GFP_KERNEL);
> + if (!cximsd)
> + return -ENOMEM;
> +
> + memcpy(cximsd->xormaps, xormaps, nr_maps * sizeof(*cximsd->xormaps));
> + cximsd->nr_maps = nr_maps;
> +
> + return 0;
> +}
> +
> +/*
> + * cxl_translate_init - parse test vectors and kicks off translation tests
> + *
> + * Returns: 0 on success, negative error code on failure
> + */
> +static int __init cxl_translate_init(void)
> +{
> + int ret, i;
> +
> + /* Validate module parameters */
> + if (table_num == 0) {
> + pr_err("No test vectors provided\n");
> + return -EINVAL;
> + }
> +
> + pr_info("CXL translate test module loaded with %d test vectors\n",
> + table_num);
> +
> + ret = setup_xor_mapping();
> + if (ret)
> + return ret;
> +
> + /* Process each test vector */
> + for (i = 0; i < table_num; i++) {
> + u64 dpa, expect_spa;
> + int pos, math;
> + u8 r_eiw, hb_ways;
> + u16 r_eig;
> +
> + pr_debug("Processing test vector %d: '%s'\n", i, table[i]);
> +
> + /* Parse the test vector */
> + ret = parse_test_vector(table[i], &dpa, &pos, &r_eiw, &r_eig,
> + &hb_ways, &math, &expect_spa);
> + if (ret) {
> + pr_err("CXL Translate Test %d: FAIL\n"
> + " Failed to parse test vector '%s'\n",
> + i, table[i]);
> + continue;
> + }
> + /* Run the translation test */
> + ret = run_translation_test(dpa, pos, r_eiw, r_eig, hb_ways,
> + math, expect_spa);
> + if (ret) {
> + pr_err("CXL Translate Test %d: FAIL\n"
> + " dpa=%llu pos=%d r_eiw=%u r_eig=%u hb_ways=%u math=%s expect_spa=%llu\n",
> + i, dpa, pos, r_eiw, r_eig, hb_ways,
> + (math == XOR_MATH) ? "XOR" : "MODULO",
> + expect_spa);
> + } else {
> + pr_info("CXL Translate Test %d: PASS\n", i);
> + }
> + }
> +
> + pr_info("CXL translate test completed\n");
> + return 0;
> +}
> +
> +static void __exit cxl_translate_exit(void)
> +{
> + kfree(cximsd);
> +
> + pr_info("CXL translate test module unloaded\n");
> +}
> +
> +module_param_array(table, charp, &table_num, 0444);
> +MODULE_PARM_DESC(table, "Test vectors as space-separated decimal strings");
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("cxl_test: cxl address translation test module");
> +MODULE_IMPORT_NS("CXL");
> +
> +module_init(cxl_translate_init);
> +module_exit(cxl_translate_exit);
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2025-09-04 23:24 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-29 7:21 [PATCH v2 0/3] CXL: Add a loadable module for address translation Alison Schofield
2025-08-29 7:21 ` [PATCH v2 1/3] cxl/region: Refactor address translation funcs for testing Alison Schofield
2025-09-04 22:05 ` Dave Jiang
2025-08-29 7:21 ` [PATCH v2 2/3] cxl/acpi: Make the XOR calculations available " Alison Schofield
2025-09-04 23:21 ` Dave Jiang
2025-08-29 7:21 ` [PATCH v2 3/3] cxl/test: Add cxl_translate module for address translation testing Alison Schofield
2025-09-04 23:24 ` Dave Jiang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).