Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/cxl/test/cxl_translate.c
51349 views
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 */
24
static char *table[MAX_TABLE_ENTRIES];
25
static 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
*/
37
struct cxl_cxims_data *cximsd;
38
static u64 xormaps[] = {
39
0x2020900,
40
0x4041200,
41
0x1010400,
42
0x800,
43
};
44
45
static int nr_maps = ARRAY_SIZE(xormaps);
46
47
#define HBIW_TO_NR_MAPS_SIZE (CXL_DECODER_MAX_INTERLEAVE + 1)
48
static 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
*/
64
static 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
*/
93
static 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
*/
117
static 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
*/
141
static 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
*/
188
static 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
*/
230
static 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
245
static 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
290
struct 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
298
static 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
340
static 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
*/
373
static 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
438
static void __exit cxl_translate_exit(void)
439
{
440
pr_info("CXL translate test module unloaded\n");
441
}
442
443
module_param_array(table, charp, &table_num, 0444);
444
MODULE_PARM_DESC(table, "Test vectors as space-separated decimal strings");
445
446
MODULE_LICENSE("GPL");
447
MODULE_DESCRIPTION("cxl_test: cxl address translation test module");
448
MODULE_IMPORT_NS("CXL");
449
450
module_init(cxl_translate_init);
451
module_exit(cxl_translate_exit);
452
453