Path: blob/master/tools/testing/cxl/test/cxl_translate.c
38226 views
// SPDX-License-Identifier: GPL-2.0-only1// Copyright(c) 2025 Intel Corporation. All rights reserved.23/* Preface all log entries with "cxl_translate" */4#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt56#include <linux/moduleparam.h>7#include <linux/module.h>8#include <linux/kernel.h>9#include <linux/init.h>10#include <linux/slab.h>11#include <linux/acpi.h>12#include <cxlmem.h>13#include <cxl.h>1415/* Maximum number of test vectors and entry length */16#define MAX_TABLE_ENTRIES 12817#define MAX_ENTRY_LEN 1281819/* Expected number of parameters in each test vector */20#define EXPECTED_PARAMS 72122/* Module parameters for test vectors */23static char *table[MAX_TABLE_ENTRIES];24static int table_num;2526/* Interleave Arithmetic */27#define MODULO_MATH 028#define XOR_MATH 12930/*31* XOR mapping configuration32* The test data sets all use the same set of xormaps. When additional33* data sets arrive for validation, this static setup will need to34* be changed to accept xormaps as additional parameters.35*/36struct cxl_cxims_data *cximsd;37static u64 xormaps[] = {380x2020900,390x4041200,400x1010400,410x800,42};4344static int nr_maps = ARRAY_SIZE(xormaps);4546#define HBIW_TO_NR_MAPS_SIZE (CXL_DECODER_MAX_INTERLEAVE + 1)47static const int hbiw_to_nr_maps[HBIW_TO_NR_MAPS_SIZE] = {48[1] = 0, [2] = 1, [3] = 0, [4] = 2, [6] = 1, [8] = 3, [12] = 2, [16] = 449};5051/**52* to_hpa - calculate an HPA offset from a DPA offset and position53*54* dpa_offset: device physical address offset55* pos: devices position in interleave56* r_eiw: region encoded interleave ways57* r_eig: region encoded interleave granularity58* hb_ways: host bridge interleave ways59* math: interleave arithmetic (MODULO_MATH or XOR_MATH)60*61* Returns: host physical address offset62*/63static u64 to_hpa(u64 dpa_offset, int pos, u8 r_eiw, u16 r_eig, u8 hb_ways,64u8 math)65{66u64 hpa_offset;6768/* Calculate base HPA offset from DPA and position */69hpa_offset = cxl_calculate_hpa_offset(dpa_offset, pos, r_eiw, r_eig);7071if (math == XOR_MATH) {72cximsd->nr_maps = hbiw_to_nr_maps[hb_ways];73if (cximsd->nr_maps)74return cxl_do_xormap_calc(cximsd, hpa_offset, hb_ways);75}76return hpa_offset;77}7879/**80* to_dpa - translate an HPA offset to DPA offset81*82* hpa_offset: host physical address offset83* r_eiw: region encoded interleave ways84* r_eig: region encoded interleave granularity85* hb_ways: host bridge interleave ways86* math: interleave arithmetic (MODULO_MATH or XOR_MATH)87*88* Returns: device physical address offset89*/90static u64 to_dpa(u64 hpa_offset, u8 r_eiw, u16 r_eig, u8 hb_ways, u8 math)91{92u64 offset = hpa_offset;9394if (math == XOR_MATH) {95cximsd->nr_maps = hbiw_to_nr_maps[hb_ways];96if (cximsd->nr_maps)97offset =98cxl_do_xormap_calc(cximsd, hpa_offset, hb_ways);99}100return cxl_calculate_dpa_offset(offset, r_eiw, r_eig);101}102103/**104* to_pos - extract an interleave position from an HPA offset105*106* hpa_offset: host physical address offset107* r_eiw: region encoded interleave ways108* r_eig: region encoded interleave granularity109* hb_ways: host bridge interleave ways110* math: interleave arithmetic (MODULO_MATH or XOR_MATH)111*112* Returns: devices position in region interleave113*/114static u64 to_pos(u64 hpa_offset, u8 r_eiw, u16 r_eig, u8 hb_ways, u8 math)115{116u64 offset = hpa_offset;117118/* Reverse XOR mapping if specified */119if (math == XOR_MATH)120offset = cxl_do_xormap_calc(cximsd, hpa_offset, hb_ways);121122return cxl_calculate_position(offset, r_eiw, r_eig);123}124125/**126* run_translation_test - execute forward and reverse translations127*128* @dpa: device physical address129* @pos: expected position in region interleave130* @r_eiw: region encoded interleave ways131* @r_eig: region encoded interleave granularity132* @hb_ways: host bridge interleave ways133* @math: interleave arithmetic (MODULO_MATH or XOR_MATH)134* @expect_spa: expected system physical address135*136* Returns: 0 on success, -1 on failure137*/138static int run_translation_test(u64 dpa, int pos, u8 r_eiw, u16 r_eig,139u8 hb_ways, int math, u64 expect_hpa)140{141u64 translated_spa, reverse_dpa;142int reverse_pos;143144/* Test Device to Host translation: DPA + POS -> SPA */145translated_spa = to_hpa(dpa, pos, r_eiw, r_eig, hb_ways, math);146if (translated_spa != expect_hpa) {147pr_err("Device to host failed: expected HPA %llu, got %llu\n",148expect_hpa, translated_spa);149return -1;150}151152/* Test Host to Device DPA translation: SPA -> DPA */153reverse_dpa = to_dpa(translated_spa, r_eiw, r_eig, hb_ways, math);154if (reverse_dpa != dpa) {155pr_err("Host to Device DPA failed: expected %llu, got %llu\n",156dpa, reverse_dpa);157return -1;158}159160/* Test Host to Device Position translation: SPA -> POS */161reverse_pos = to_pos(translated_spa, r_eiw, r_eig, hb_ways, math);162if (reverse_pos != pos) {163pr_err("Position lookup failed: expected %d, got %d\n", pos,164reverse_pos);165return -1;166}167168return 0;169}170171/**172* parse_test_vector - parse a single test vector string173*174* entry: test vector string to parse175* dpa: device physical address176* pos: expected position in region interleave177* r_eiw: region encoded interleave ways178* r_eig: region encoded interleave granularity179* hb_ways: host bridge interleave ways180* math: interleave arithmetic (MODULO_MATH or XOR_MATH)181* expect_spa: expected system physical address182*183* Returns: 0 on success, negative error code on failure184*/185static int parse_test_vector(const char *entry, u64 *dpa, int *pos, u8 *r_eiw,186u16 *r_eig, u8 *hb_ways, int *math,187u64 *expect_hpa)188{189unsigned int tmp_r_eiw, tmp_r_eig, tmp_hb_ways;190int parsed;191192parsed = sscanf(entry, "%llu %d %u %u %u %d %llu", dpa, pos, &tmp_r_eiw,193&tmp_r_eig, &tmp_hb_ways, math, expect_hpa);194195if (parsed != EXPECTED_PARAMS) {196pr_err("Parse error: expected %d parameters, got %d in '%s'\n",197EXPECTED_PARAMS, parsed, entry);198return -EINVAL;199}200if (tmp_r_eiw > U8_MAX || tmp_r_eig > U16_MAX || tmp_hb_ways > U8_MAX) {201pr_err("Parameter overflow in entry: '%s'\n", entry);202return -ERANGE;203}204if (*math != MODULO_MATH && *math != XOR_MATH) {205pr_err("Invalid math type %d in entry: '%s'\n", *math, entry);206return -EINVAL;207}208*r_eiw = tmp_r_eiw;209*r_eig = tmp_r_eig;210*hb_ways = tmp_hb_ways;211212return 0;213}214215/*216* setup_xor_mapping - Initialize XOR mapping data structure217*218* The test data sets all use the same HBIG so we can use one set219* of xormaps, and set the number to apply based on HBIW before220* calling cxl_do_xormap_calc().221*222* When additional data sets arrive for validation with different223* HBIG's this static setup will need to be updated.224*225* Returns: 0 on success, negative error code on failure226*/227static int setup_xor_mapping(void)228{229if (nr_maps <= 0)230return -EINVAL;231232cximsd = kzalloc(struct_size(cximsd, xormaps, nr_maps), GFP_KERNEL);233if (!cximsd)234return -ENOMEM;235236memcpy(cximsd->xormaps, xormaps, nr_maps * sizeof(*cximsd->xormaps));237cximsd->nr_maps = nr_maps;238239return 0;240}241242static int test_random_params(void)243{244u8 valid_eiws[] = { 0, 1, 2, 3, 4, 8, 9, 10 };245u16 valid_eigs[] = { 0, 1, 2, 3, 4, 5, 6 };246int i, ways, pos, reverse_pos;247u64 dpa, hpa, reverse_dpa;248int iterations = 10000;249int failures = 0;250251for (i = 0; i < iterations; i++) {252/* Generate valid random parameters for eiw, eig, pos, dpa */253u8 eiw = valid_eiws[get_random_u32() % ARRAY_SIZE(valid_eiws)];254u16 eig = valid_eigs[get_random_u32() % ARRAY_SIZE(valid_eigs)];255256eiw_to_ways(eiw, &ways);257pos = get_random_u32() % ways;258dpa = get_random_u64() >> 12;259260hpa = cxl_calculate_hpa_offset(dpa, pos, eiw, eig);261reverse_dpa = cxl_calculate_dpa_offset(hpa, eiw, eig);262reverse_pos = cxl_calculate_position(hpa, eiw, eig);263264if (reverse_dpa != dpa || reverse_pos != pos) {265pr_err("test random iter %d FAIL hpa=%llu, dpa=%llu reverse_dpa=%llu, pos=%d reverse_pos=%d eiw=%u eig=%u\n",266i, hpa, dpa, reverse_dpa, pos, reverse_pos, eiw,267eig);268269if (failures++ > 10) {270pr_err("test random too many failures, stop\n");271break;272}273}274}275pr_info("..... test random: PASS %d FAIL %d\n", i - failures, failures);276277if (failures)278return -EINVAL;279280return 0;281}282283struct param_test {284u8 eiw;285u16 eig;286int pos;287bool expect; /* true: expect pass, false: expect fail */288const char *desc;289};290291static struct param_test param_tests[] = {292{ 0x0, 0, 0, true, "1-way, min eig=0, pos=0" },293{ 0x0, 3, 0, true, "1-way, mid eig=3, pos=0" },294{ 0x0, 6, 0, true, "1-way, max eig=6, pos=0" },295{ 0x1, 0, 0, true, "2-way, eig=0, pos=0" },296{ 0x1, 3, 1, true, "2-way, eig=3, max pos=1" },297{ 0x1, 6, 1, true, "2-way, eig=6, max pos=1" },298{ 0x2, 0, 0, true, "4-way, eig=0, pos=0" },299{ 0x2, 3, 3, true, "4-way, eig=3, max pos=3" },300{ 0x2, 6, 3, true, "4-way, eig=6, max pos=3" },301{ 0x3, 0, 0, true, "8-way, eig=0, pos=0" },302{ 0x3, 3, 7, true, "8-way, eig=3, max pos=7" },303{ 0x3, 6, 7, true, "8-way, eig=6, max pos=7" },304{ 0x4, 0, 0, true, "16-way, eig=0, pos=0" },305{ 0x4, 3, 15, true, "16-way, eig=3, max pos=15" },306{ 0x4, 6, 15, true, "16-way, eig=6, max pos=15" },307{ 0x8, 0, 0, true, "3-way, eig=0, pos=0" },308{ 0x8, 3, 2, true, "3-way, eig=3, max pos=2" },309{ 0x8, 6, 2, true, "3-way, eig=6, max pos=2" },310{ 0x9, 0, 0, true, "6-way, eig=0, pos=0" },311{ 0x9, 3, 5, true, "6-way, eig=3, max pos=5" },312{ 0x9, 6, 5, true, "6-way, eig=6, max pos=5" },313{ 0xA, 0, 0, true, "12-way, eig=0, pos=0" },314{ 0xA, 3, 11, true, "12-way, eig=3, max pos=11" },315{ 0xA, 6, 11, true, "12-way, eig=6, max pos=11" },316{ 0x5, 0, 0, false, "invalid eiw=5" },317{ 0x7, 0, 0, false, "invalid eiw=7" },318{ 0xB, 0, 0, false, "invalid eiw=0xB" },319{ 0xFF, 0, 0, false, "invalid eiw=0xFF" },320{ 0x1, 7, 0, false, "invalid eig=7 (out of range)" },321{ 0x2, 0x10, 0, false, "invalid eig=0x10" },322{ 0x3, 0xFFFF, 0, false, "invalid eig=0xFFFF" },323{ 0x1, 0, -1, false, "pos < 0" },324{ 0x1, 0, 2, false, "2-way, pos=2 (>= ways)" },325{ 0x2, 0, 4, false, "4-way, pos=4 (>= ways)" },326{ 0x3, 0, 8, false, "8-way, pos=8 (>= ways)" },327{ 0x4, 0, 16, false, "16-way, pos=16 (>= ways)" },328{ 0x8, 0, 3, false, "3-way, pos=3 (>= ways)" },329{ 0x9, 0, 6, false, "6-way, pos=6 (>= ways)" },330{ 0xA, 0, 12, false, "12-way, pos=12 (>= ways)" },331};332333static int test_cxl_validate_translation_params(void)334{335int i, rc, failures = 0;336bool valid;337338for (i = 0; i < ARRAY_SIZE(param_tests); i++) {339struct param_test *t = ¶m_tests[i];340341rc = cxl_validate_translation_params(t->eiw, t->eig, t->pos);342valid = (rc == 0);343344if (valid != t->expect) {345pr_err("test params failed: %s\n", t->desc);346failures++;347}348}349pr_info("..... test params: PASS %d FAIL %d\n", i - failures, failures);350351if (failures)352return -EINVAL;353354return 0;355}356357/*358* cxl_translate_init359*360* Run the internal validation tests when no params are passed.361* Otherwise, parse the parameters (test vectors), and kick off362* the translation test.363*364* Returns: 0 on success, negative error code on failure365*/366static int __init cxl_translate_init(void)367{368int rc, i;369370/* If no tables are passed, validate module params only */371if (table_num == 0) {372pr_info("Internal validation test start...\n");373rc = test_cxl_validate_translation_params();374if (rc)375return rc;376377rc = test_random_params();378if (rc)379return rc;380381pr_info("Internal validation test completed successfully\n");382383return 0;384}385386pr_info("CXL translate test module loaded with %d test vectors\n",387table_num);388389rc = setup_xor_mapping();390if (rc)391return rc;392393/* Process each test vector */394for (i = 0; i < table_num; i++) {395u64 dpa, expect_spa;396int pos, math;397u8 r_eiw, hb_ways;398u16 r_eig;399400pr_debug("Processing test vector %d: '%s'\n", i, table[i]);401402/* Parse the test vector */403rc = parse_test_vector(table[i], &dpa, &pos, &r_eiw, &r_eig,404&hb_ways, &math, &expect_spa);405if (rc) {406pr_err("CXL Translate Test %d: FAIL\n"407" Failed to parse test vector '%s'\n",408i, table[i]);409continue;410}411/* Run the translation test */412rc = run_translation_test(dpa, pos, r_eiw, r_eig, hb_ways, math,413expect_spa);414if (rc) {415pr_err("CXL Translate Test %d: FAIL\n"416" dpa=%llu pos=%d r_eiw=%u r_eig=%u hb_ways=%u math=%s expect_spa=%llu\n",417i, dpa, pos, r_eiw, r_eig, hb_ways,418(math == XOR_MATH) ? "XOR" : "MODULO",419expect_spa);420} else {421pr_info("CXL Translate Test %d: PASS\n", i);422}423}424425kfree(cximsd);426pr_info("CXL translate test completed\n");427428return 0;429}430431static void __exit cxl_translate_exit(void)432{433pr_info("CXL translate test module unloaded\n");434}435436module_param_array(table, charp, &table_num, 0444);437MODULE_PARM_DESC(table, "Test vectors as space-separated decimal strings");438439MODULE_LICENSE("GPL");440MODULE_DESCRIPTION("cxl_test: cxl address translation test module");441MODULE_IMPORT_NS("CXL");442443module_init(cxl_translate_init);444module_exit(cxl_translate_exit);445446447