cxl_translate.c 13 KB

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