File Index
Files and directories
Sign in
FILE

cxl_translate.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/cxl/test/cxl_translate.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2// Copyright(c) 2025 Intel Corporation. All rights reserved.
  3
  4/* Preface all log entries with "cxl_translate" */
  5#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  6
  7#include <linux/moduleparam.h>
  8#include <linux/module.h>
  9#include <linux/kernel.h>
 10#include <linux/init.h>
 11#include <linux/slab.h>
 12#include <linux/acpi.h>
 13#include <cxlmem.h>
 14#include <cxl.h>
 15
 16/* Maximum number of test vectors and entry length */
 17#define MAX_TABLE_ENTRIES 128
 18#define MAX_ENTRY_LEN 128
 19
 20/* Expected number of parameters in each test vector */
 21#define EXPECTED_PARAMS 7
 22
 23/* Module parameters for test vectors */
 24static char *table[MAX_TABLE_ENTRIES];
 25static int table_num;
 26
 27/* Interleave Arithmetic */
 28#define MODULO_MATH 0
 29#define XOR_MATH 1
 30
 31/*
 32 * XOR mapping configuration
 33 * The test data sets all use the same set of xormaps. When additional
 34 * data sets arrive for validation, this static setup will need to
 35 * be changed to accept xormaps as additional parameters.
 36 */
 37struct cxl_cxims_data *cximsd;
 38static u64 xormaps[] = {
 39	0x2020900,
 40	0x4041200,
 41	0x1010400,
 42	0x800,
 43};
 44
 45static int nr_maps = ARRAY_SIZE(xormaps);
 46
 47#define HBIW_TO_NR_MAPS_SIZE (CXL_DECODER_MAX_INTERLEAVE + 1)
 48static const int hbiw_to_nr_maps[HBIW_TO_NR_MAPS_SIZE] = {
 49	[1] = 0, [2] = 1, [3] = 0, [4] = 2, [6] = 1, [8] = 3, [12] = 2, [16] = 4
 50};
 51
 52/**
 53 * to_hpa - calculate an HPA offset from a DPA offset and position
 54 *
 55 * dpa_offset: device physical address offset
 56 * pos: devices position in interleave
 57 * r_eiw: region encoded interleave ways
 58 * r_eig: region encoded interleave granularity
 59 * hb_ways: host bridge interleave ways
 60 * math: interleave arithmetic (MODULO_MATH or XOR_MATH)
 61 *
 62 * Returns: host physical address offset
 63 */
 64static u64 to_hpa(u64 dpa_offset, int pos, u8 r_eiw, u16 r_eig, u8 hb_ways,
 65		  u8 math)
 66{
 67	u64 hpa_offset;
 68
 69	/* Calculate base HPA offset from DPA and position */
 70	hpa_offset = cxl_calculate_hpa_offset(dpa_offset, pos, r_eiw, r_eig);
 71	if (hpa_offset == ULLONG_MAX)
 72		return ULLONG_MAX;
 73
 74	if (math == XOR_MATH) {
 75		cximsd->nr_maps = hbiw_to_nr_maps[hb_ways];
 76		if (cximsd->nr_maps)
 77			return cxl_do_xormap_calc(cximsd, hpa_offset, hb_ways);
 78	}
 79	return hpa_offset;
 80}
 81
 82/**
 83 * to_dpa - translate an HPA offset to DPA offset
 84 *
 85 * hpa_offset: host physical address offset
 86 * r_eiw: region encoded interleave ways
 87 * r_eig: region encoded interleave granularity
 88 * hb_ways: host bridge interleave ways
 89 * math: interleave arithmetic (MODULO_MATH or XOR_MATH)
 90 *
 91 * Returns: device physical address offset
 92 */
 93static u64 to_dpa(u64 hpa_offset, u8 r_eiw, u16 r_eig, u8 hb_ways, u8 math)
 94{
 95	u64 offset = hpa_offset;
 96
 97	if (math == XOR_MATH) {
 98		cximsd->nr_maps = hbiw_to_nr_maps[hb_ways];
 99		if (cximsd->nr_maps)
100			offset =
101				cxl_do_xormap_calc(cximsd, hpa_offset, hb_ways);
102	}
103	return cxl_calculate_dpa_offset(offset, r_eiw, r_eig);
104}
105
106/**
107 * to_pos - extract an interleave position from an HPA offset
108 *
109 * hpa_offset: host physical address offset
110 * r_eiw: region encoded interleave ways
111 * r_eig: region encoded interleave granularity
112 * hb_ways: host bridge interleave ways
113 * math: interleave arithmetic (MODULO_MATH or XOR_MATH)
114 *
115 * Returns: devices position in region interleave
116 */
117static u64 to_pos(u64 hpa_offset, u8 r_eiw, u16 r_eig, u8 hb_ways, u8 math)
118{
119	u64 offset = hpa_offset;
120
121	/* Reverse XOR mapping if specified */
122	if (math == XOR_MATH)
123		offset = cxl_do_xormap_calc(cximsd, hpa_offset, hb_ways);
124
125	return cxl_calculate_position(offset, r_eiw, r_eig);
126}
127
128/**
129 * run_translation_test - execute forward and reverse translations
130 *
131 * @dpa: device physical address
132 * @pos: expected position in region interleave
133 * @r_eiw: region encoded interleave ways
134 * @r_eig: region encoded interleave granularity
135 * @hb_ways: host bridge interleave ways
136 * @math: interleave arithmetic (MODULO_MATH or XOR_MATH)
137 * @expect_spa: expected system physical address
138 *
139 * Returns: 0 on success, -1 on failure
140 */
141static int run_translation_test(u64 dpa, int pos, u8 r_eiw, u16 r_eig,
142				u8 hb_ways, int math, u64 expect_hpa)
143{
144	u64 translated_spa, reverse_dpa;
145	int reverse_pos;
146
147	/* Test Device to Host translation: DPA + POS -> SPA */
148	translated_spa = to_hpa(dpa, pos, r_eiw, r_eig, hb_ways, math);
149	if (translated_spa != expect_hpa) {
150		pr_err("Device to host failed: expected HPA %llu, got %llu\n",
151		       expect_hpa, translated_spa);
152		return -1;
153	}
154
155	/* Test Host to Device DPA translation: SPA -> DPA */
156	reverse_dpa = to_dpa(translated_spa, r_eiw, r_eig, hb_ways, math);
157	if (reverse_dpa != dpa) {
158		pr_err("Host to Device DPA failed: expected %llu, got %llu\n",
159		       dpa, reverse_dpa);
160		return -1;
161	}
162
163	/* Test Host to Device Position translation: SPA -> POS */
164	reverse_pos = to_pos(translated_spa, r_eiw, r_eig, hb_ways, math);
165	if (reverse_pos != pos) {
166		pr_err("Position lookup failed: expected %d, got %d\n", pos,
167		       reverse_pos);
168		return -1;
169	}
170
171	return 0;
172}
173
174/**
175 * parse_test_vector - parse a single test vector string
176 *
177 * entry: test vector string to parse
178 * dpa: device physical address
179 * pos: expected position in region interleave
180 * r_eiw: region encoded interleave ways
181 * r_eig: region encoded interleave granularity
182 * hb_ways: host bridge interleave ways
183 * math: interleave arithmetic (MODULO_MATH or XOR_MATH)
184 * expect_spa: expected system physical address
185 *
186 * Returns: 0 on success, negative error code on failure
187 */
188static int parse_test_vector(const char *entry, u64 *dpa, int *pos, u8 *r_eiw,
189			     u16 *r_eig, u8 *hb_ways, int *math,
190			     u64 *expect_hpa)
191{
192	unsigned int tmp_r_eiw, tmp_r_eig, tmp_hb_ways;
193	int parsed;
194
195	parsed = sscanf(entry, "%llu %d %u %u %u %d %llu", dpa, pos, &tmp_r_eiw,
196			&tmp_r_eig, &tmp_hb_ways, math, expect_hpa);
197
198	if (parsed != EXPECTED_PARAMS) {
199		pr_err("Parse error: expected %d parameters, got %d in '%s'\n",
200		       EXPECTED_PARAMS, parsed, entry);
201		return -EINVAL;
202	}
203	if (tmp_r_eiw > U8_MAX || tmp_r_eig > U16_MAX || tmp_hb_ways > U8_MAX) {
204		pr_err("Parameter overflow in entry: '%s'\n", entry);
205		return -ERANGE;
206	}
207	if (*math != MODULO_MATH && *math != XOR_MATH) {
208		pr_err("Invalid math type %d in entry: '%s'\n", *math, entry);
209		return -EINVAL;
210	}
211	*r_eiw = tmp_r_eiw;
212	*r_eig = tmp_r_eig;
213	*hb_ways = tmp_hb_ways;
214
215	return 0;
216}
217
218/*
219 * setup_xor_mapping - Initialize XOR mapping data structure
220 *
221 * The test data sets all use the same HBIG so we can use one set
222 * of xormaps, and set the number to apply based on HBIW before
223 * calling cxl_do_xormap_calc().
224 *
225 * When additional data sets arrive for validation with different
226 * HBIG's this static setup will need to be updated.
227 *
228 * Returns: 0 on success, negative error code on failure
229 */
230static int setup_xor_mapping(void)
231{
232	if (nr_maps <= 0)
233		return -EINVAL;
234
235	cximsd = kzalloc(struct_size(cximsd, xormaps, nr_maps), GFP_KERNEL);
236	if (!cximsd)
237		return -ENOMEM;
238
239	memcpy(cximsd->xormaps, xormaps, nr_maps * sizeof(*cximsd->xormaps));
240	cximsd->nr_maps = nr_maps;
241
242	return 0;
243}
244
245static int test_random_params(void)
246{
247	u8 valid_eiws[] = { 0, 1, 2, 3, 4, 8, 9, 10 };
248	u16 valid_eigs[] = { 0, 1, 2, 3, 4, 5, 6 };
249	int i, ways, pos, reverse_pos;
250	u64 dpa, hpa, reverse_dpa;
251	int iterations = 10000;
252	int failures = 0;
253
254	for (i = 0; i < iterations; i++) {
255		/* Generate valid random parameters for eiw, eig, pos, dpa */
256		u8 eiw = valid_eiws[get_random_u32() % ARRAY_SIZE(valid_eiws)];
257		u16 eig = valid_eigs[get_random_u32() % ARRAY_SIZE(valid_eigs)];
258
259		eiw_to_ways(eiw, &ways);
260		pos = get_random_u32() % ways;
261		dpa = get_random_u64() >> 12;
262
263		reverse_dpa = ULLONG_MAX;
264		reverse_pos = -1;
265
266		hpa = cxl_calculate_hpa_offset(dpa, pos, eiw, eig);
267		if (hpa != ULLONG_MAX) {
268			reverse_dpa = cxl_calculate_dpa_offset(hpa, eiw, eig);
269			reverse_pos = cxl_calculate_position(hpa, eiw, eig);
270			if (reverse_dpa == dpa && reverse_pos == pos)
271				continue;
272		}
273
274		pr_err("test random iter %d FAIL hpa=%llu, dpa=%llu reverse_dpa=%llu, pos=%d reverse_pos=%d eiw=%u eig=%u\n",
275		       i, hpa, dpa, reverse_dpa, pos, reverse_pos, eiw, eig);
276
277		if (failures++ > 10) {
278			pr_err("test random too many failures, stop\n");
279			break;
280		}
281	}
282	pr_info("..... test random: PASS %d FAIL %d\n", i - failures, failures);
283
284	if (failures)
285		return -EINVAL;
286
287	return 0;
288}
289
290struct param_test {
291	u8 eiw;
292	u16 eig;
293	int pos;
294	bool expect; /* true: expect pass, false: expect fail */
295	const char *desc;
296};
297
298static struct param_test param_tests[] = {
299	{ 0x0, 0, 0, true, "1-way, min eig=0, pos=0" },
300	{ 0x0, 3, 0, true, "1-way, mid eig=3, pos=0" },
301	{ 0x0, 6, 0, true, "1-way, max eig=6, pos=0" },
302	{ 0x1, 0, 0, true, "2-way, eig=0, pos=0" },
303	{ 0x1, 3, 1, true, "2-way, eig=3, max pos=1" },
304	{ 0x1, 6, 1, true, "2-way, eig=6, max pos=1" },
305	{ 0x2, 0, 0, true, "4-way, eig=0, pos=0" },
306	{ 0x2, 3, 3, true, "4-way, eig=3, max pos=3" },
307	{ 0x2, 6, 3, true, "4-way, eig=6, max pos=3" },
308	{ 0x3, 0, 0, true, "8-way, eig=0, pos=0" },
309	{ 0x3, 3, 7, true, "8-way, eig=3, max pos=7" },
310	{ 0x3, 6, 7, true, "8-way, eig=6, max pos=7" },
311	{ 0x4, 0, 0, true, "16-way, eig=0, pos=0" },
312	{ 0x4, 3, 15, true, "16-way, eig=3, max pos=15" },
313	{ 0x4, 6, 15, true, "16-way, eig=6, max pos=15" },
314	{ 0x8, 0, 0, true, "3-way, eig=0, pos=0" },
315	{ 0x8, 3, 2, true, "3-way, eig=3, max pos=2" },
316	{ 0x8, 6, 2, true, "3-way, eig=6, max pos=2" },
317	{ 0x9, 0, 0, true, "6-way, eig=0, pos=0" },
318	{ 0x9, 3, 5, true, "6-way, eig=3, max pos=5" },
319	{ 0x9, 6, 5, true, "6-way, eig=6, max pos=5" },
320	{ 0xA, 0, 0, true, "12-way, eig=0, pos=0" },
321	{ 0xA, 3, 11, true, "12-way, eig=3, max pos=11" },
322	{ 0xA, 6, 11, true, "12-way, eig=6, max pos=11" },
323	{ 0x5, 0, 0, false, "invalid eiw=5" },
324	{ 0x7, 0, 0, false, "invalid eiw=7" },
325	{ 0xB, 0, 0, false, "invalid eiw=0xB" },
326	{ 0xFF, 0, 0, false, "invalid eiw=0xFF" },
327	{ 0x1, 7, 0, false, "invalid eig=7 (out of range)" },
328	{ 0x2, 0x10, 0, false, "invalid eig=0x10" },
329	{ 0x3, 0xFFFF, 0, false, "invalid eig=0xFFFF" },
330	{ 0x1, 0, -1, false, "pos < 0" },
331	{ 0x1, 0, 2, false, "2-way, pos=2 (>= ways)" },
332	{ 0x2, 0, 4, false, "4-way, pos=4 (>= ways)" },
333	{ 0x3, 0, 8, false, "8-way, pos=8 (>= ways)" },
334	{ 0x4, 0, 16, false, "16-way, pos=16 (>= ways)" },
335	{ 0x8, 0, 3, false, "3-way, pos=3 (>= ways)" },
336	{ 0x9, 0, 6, false, "6-way, pos=6 (>= ways)" },
337	{ 0xA, 0, 12, false, "12-way, pos=12 (>= ways)" },
338};
339
340static int test_cxl_validate_translation_params(void)
341{
342	int i, rc, failures = 0;
343	bool valid;
344
345	for (i = 0; i < ARRAY_SIZE(param_tests); i++) {
346		struct param_test *t = &param_tests[i];
347
348		rc = cxl_validate_translation_params(t->eiw, t->eig, t->pos);
349		valid = (rc == 0);
350
351		if (valid != t->expect) {
352			pr_err("test params failed: %s\n", t->desc);
353			failures++;
354		}
355	}
356	pr_info("..... test params: PASS %d FAIL %d\n", i - failures, failures);
357
358	if (failures)
359		return -EINVAL;
360
361	return 0;
362}
363
364/*
365 * cxl_translate_init
366 *
367 * Run the internal validation tests when no params are passed.
368 * Otherwise, parse the parameters (test vectors), and kick off
369 * the translation test.
370 *
371 * Returns: 0 on success, negative error code on failure
372 */
373static int __init cxl_translate_init(void)
374{
375	int rc, i;
376
377	/* If no tables are passed, validate module params only */
378	if (table_num == 0) {
379		pr_info("Internal validation test start...\n");
380		rc = test_cxl_validate_translation_params();
381		if (rc)
382			return rc;
383
384		rc = test_random_params();
385		if (rc)
386			return rc;
387
388		pr_info("Internal validation test completed successfully\n");
389
390		return 0;
391	}
392
393	pr_info("CXL translate test module loaded with %d test vectors\n",
394		table_num);
395
396	rc = setup_xor_mapping();
397	if (rc)
398		return rc;
399
400	/* Process each test vector */
401	for (i = 0; i < table_num; i++) {
402		u64 dpa, expect_spa;
403		int pos, math;
404		u8 r_eiw, hb_ways;
405		u16 r_eig;
406
407		pr_debug("Processing test vector %d: '%s'\n", i, table[i]);
408
409		/* Parse the test vector */
410		rc = parse_test_vector(table[i], &dpa, &pos, &r_eiw, &r_eig,
411				       &hb_ways, &math, &expect_spa);
412		if (rc) {
413			pr_err("CXL Translate Test %d: FAIL\n"
414			       "    Failed to parse test vector '%s'\n",
415			       i, table[i]);
416			continue;
417		}
418		/* Run the translation test */
419		rc = run_translation_test(dpa, pos, r_eiw, r_eig, hb_ways, math,
420					  expect_spa);
421		if (rc) {
422			pr_err("CXL Translate Test %d: FAIL\n"
423			       "    dpa=%llu pos=%d r_eiw=%u r_eig=%u hb_ways=%u math=%s expect_spa=%llu\n",
424			       i, dpa, pos, r_eiw, r_eig, hb_ways,
425			       (math == XOR_MATH) ? "XOR" : "MODULO",
426			       expect_spa);
427		} else {
428			pr_info("CXL Translate Test %d: PASS\n", i);
429		}
430	}
431
432	kfree(cximsd);
433	pr_info("CXL translate test completed\n");
434
435	return 0;
436}
437
438static void __exit cxl_translate_exit(void)
439{
440	pr_info("CXL translate test module unloaded\n");
441}
442
443module_param_array(table, charp, &table_num, 0444);
444MODULE_PARM_DESC(table, "Test vectors as space-separated decimal strings");
445
446MODULE_LICENSE("GPL");
447MODULE_DESCRIPTION("cxl_test: cxl address translation test module");
448MODULE_IMPORT_NS("CXL");
449
450module_init(cxl_translate_init);
451module_exit(cxl_translate_exit);
~/ Read-only file preview.