Path: blob/master/tools/testing/cxl/test/cxl_translate.c
51349 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);70if (hpa_offset == ULLONG_MAX)71return ULLONG_MAX;7273if (math == XOR_MATH) {74cximsd->nr_maps = hbiw_to_nr_maps[hb_ways];75if (cximsd->nr_maps)76return cxl_do_xormap_calc(cximsd, hpa_offset, hb_ways);77}78return hpa_offset;79}8081/**82* to_dpa - translate an HPA offset to DPA offset83*84* hpa_offset: host physical address offset85* r_eiw: region encoded interleave ways86* r_eig: region encoded interleave granularity87* hb_ways: host bridge interleave ways88* math: interleave arithmetic (MODULO_MATH or XOR_MATH)89*90* Returns: device physical address offset91*/92static u64 to_dpa(u64 hpa_offset, u8 r_eiw, u16 r_eig, u8 hb_ways, u8 math)93{94u64 offset = hpa_offset;9596if (math == XOR_MATH) {97cximsd->nr_maps = hbiw_to_nr_maps[hb_ways];98if (cximsd->nr_maps)99offset =100cxl_do_xormap_calc(cximsd, hpa_offset, hb_ways);101}102return cxl_calculate_dpa_offset(offset, r_eiw, r_eig);103}104105/**106* to_pos - extract an interleave position from an HPA offset107*108* hpa_offset: host physical address offset109* r_eiw: region encoded interleave ways110* r_eig: region encoded interleave granularity111* hb_ways: host bridge interleave ways112* math: interleave arithmetic (MODULO_MATH or XOR_MATH)113*114* Returns: devices position in region interleave115*/116static u64 to_pos(u64 hpa_offset, u8 r_eiw, u16 r_eig, u8 hb_ways, u8 math)117{118u64 offset = hpa_offset;119120/* Reverse XOR mapping if specified */121if (math == XOR_MATH)122offset = cxl_do_xormap_calc(cximsd, hpa_offset, hb_ways);123124return cxl_calculate_position(offset, r_eiw, r_eig);125}126127/**128* run_translation_test - execute forward and reverse translations129*130* @dpa: device physical address131* @pos: expected position in region interleave132* @r_eiw: region encoded interleave ways133* @r_eig: region encoded interleave granularity134* @hb_ways: host bridge interleave ways135* @math: interleave arithmetic (MODULO_MATH or XOR_MATH)136* @expect_spa: expected system physical address137*138* Returns: 0 on success, -1 on failure139*/140static int run_translation_test(u64 dpa, int pos, u8 r_eiw, u16 r_eig,141u8 hb_ways, int math, u64 expect_hpa)142{143u64 translated_spa, reverse_dpa;144int reverse_pos;145146/* Test Device to Host translation: DPA + POS -> SPA */147translated_spa = to_hpa(dpa, pos, r_eiw, r_eig, hb_ways, math);148if (translated_spa != expect_hpa) {149pr_err("Device to host failed: expected HPA %llu, got %llu\n",150expect_hpa, translated_spa);151return -1;152}153154/* Test Host to Device DPA translation: SPA -> DPA */155reverse_dpa = to_dpa(translated_spa, r_eiw, r_eig, hb_ways, math);156if (reverse_dpa != dpa) {157pr_err("Host to Device DPA failed: expected %llu, got %llu\n",158dpa, reverse_dpa);159return -1;160}161162/* Test Host to Device Position translation: SPA -> POS */163reverse_pos = to_pos(translated_spa, r_eiw, r_eig, hb_ways, math);164if (reverse_pos != pos) {165pr_err("Position lookup failed: expected %d, got %d\n", pos,166reverse_pos);167return -1;168}169170return 0;171}172173/**174* parse_test_vector - parse a single test vector string175*176* entry: test vector string to parse177* dpa: device physical address178* pos: expected position in region interleave179* r_eiw: region encoded interleave ways180* r_eig: region encoded interleave granularity181* hb_ways: host bridge interleave ways182* math: interleave arithmetic (MODULO_MATH or XOR_MATH)183* expect_spa: expected system physical address184*185* Returns: 0 on success, negative error code on failure186*/187static int parse_test_vector(const char *entry, u64 *dpa, int *pos, u8 *r_eiw,188u16 *r_eig, u8 *hb_ways, int *math,189u64 *expect_hpa)190{191unsigned int tmp_r_eiw, tmp_r_eig, tmp_hb_ways;192int parsed;193194parsed = sscanf(entry, "%llu %d %u %u %u %d %llu", dpa, pos, &tmp_r_eiw,195&tmp_r_eig, &tmp_hb_ways, math, expect_hpa);196197if (parsed != EXPECTED_PARAMS) {198pr_err("Parse error: expected %d parameters, got %d in '%s'\n",199EXPECTED_PARAMS, parsed, entry);200return -EINVAL;201}202if (tmp_r_eiw > U8_MAX || tmp_r_eig > U16_MAX || tmp_hb_ways > U8_MAX) {203pr_err("Parameter overflow in entry: '%s'\n", entry);204return -ERANGE;205}206if (*math != MODULO_MATH && *math != XOR_MATH) {207pr_err("Invalid math type %d in entry: '%s'\n", *math, entry);208return -EINVAL;209}210*r_eiw = tmp_r_eiw;211*r_eig = tmp_r_eig;212*hb_ways = tmp_hb_ways;213214return 0;215}216217/*218* setup_xor_mapping - Initialize XOR mapping data structure219*220* The test data sets all use the same HBIG so we can use one set221* of xormaps, and set the number to apply based on HBIW before222* calling cxl_do_xormap_calc().223*224* When additional data sets arrive for validation with different225* HBIG's this static setup will need to be updated.226*227* Returns: 0 on success, negative error code on failure228*/229static int setup_xor_mapping(void)230{231if (nr_maps <= 0)232return -EINVAL;233234cximsd = kzalloc(struct_size(cximsd, xormaps, nr_maps), GFP_KERNEL);235if (!cximsd)236return -ENOMEM;237238memcpy(cximsd->xormaps, xormaps, nr_maps * sizeof(*cximsd->xormaps));239cximsd->nr_maps = nr_maps;240241return 0;242}243244static int test_random_params(void)245{246u8 valid_eiws[] = { 0, 1, 2, 3, 4, 8, 9, 10 };247u16 valid_eigs[] = { 0, 1, 2, 3, 4, 5, 6 };248int i, ways, pos, reverse_pos;249u64 dpa, hpa, reverse_dpa;250int iterations = 10000;251int failures = 0;252253for (i = 0; i < iterations; i++) {254/* Generate valid random parameters for eiw, eig, pos, dpa */255u8 eiw = valid_eiws[get_random_u32() % ARRAY_SIZE(valid_eiws)];256u16 eig = valid_eigs[get_random_u32() % ARRAY_SIZE(valid_eigs)];257258eiw_to_ways(eiw, &ways);259pos = get_random_u32() % ways;260dpa = get_random_u64() >> 12;261262reverse_dpa = ULLONG_MAX;263reverse_pos = -1;264265hpa = cxl_calculate_hpa_offset(dpa, pos, eiw, eig);266if (hpa != ULLONG_MAX) {267reverse_dpa = cxl_calculate_dpa_offset(hpa, eiw, eig);268reverse_pos = cxl_calculate_position(hpa, eiw, eig);269if (reverse_dpa == dpa && reverse_pos == pos)270continue;271}272273pr_err("test random iter %d FAIL hpa=%llu, dpa=%llu reverse_dpa=%llu, pos=%d reverse_pos=%d eiw=%u eig=%u\n",274i, hpa, dpa, reverse_dpa, pos, reverse_pos, eiw, eig);275276if (failures++ > 10) {277pr_err("test random too many failures, stop\n");278break;279}280}281pr_info("..... test random: PASS %d FAIL %d\n", i - failures, failures);282283if (failures)284return -EINVAL;285286return 0;287}288289struct param_test {290u8 eiw;291u16 eig;292int pos;293bool expect; /* true: expect pass, false: expect fail */294const char *desc;295};296297static struct param_test param_tests[] = {298{ 0x0, 0, 0, true, "1-way, min eig=0, pos=0" },299{ 0x0, 3, 0, true, "1-way, mid eig=3, pos=0" },300{ 0x0, 6, 0, true, "1-way, max eig=6, pos=0" },301{ 0x1, 0, 0, true, "2-way, eig=0, pos=0" },302{ 0x1, 3, 1, true, "2-way, eig=3, max pos=1" },303{ 0x1, 6, 1, true, "2-way, eig=6, max pos=1" },304{ 0x2, 0, 0, true, "4-way, eig=0, pos=0" },305{ 0x2, 3, 3, true, "4-way, eig=3, max pos=3" },306{ 0x2, 6, 3, true, "4-way, eig=6, max pos=3" },307{ 0x3, 0, 0, true, "8-way, eig=0, pos=0" },308{ 0x3, 3, 7, true, "8-way, eig=3, max pos=7" },309{ 0x3, 6, 7, true, "8-way, eig=6, max pos=7" },310{ 0x4, 0, 0, true, "16-way, eig=0, pos=0" },311{ 0x4, 3, 15, true, "16-way, eig=3, max pos=15" },312{ 0x4, 6, 15, true, "16-way, eig=6, max pos=15" },313{ 0x8, 0, 0, true, "3-way, eig=0, pos=0" },314{ 0x8, 3, 2, true, "3-way, eig=3, max pos=2" },315{ 0x8, 6, 2, true, "3-way, eig=6, max pos=2" },316{ 0x9, 0, 0, true, "6-way, eig=0, pos=0" },317{ 0x9, 3, 5, true, "6-way, eig=3, max pos=5" },318{ 0x9, 6, 5, true, "6-way, eig=6, max pos=5" },319{ 0xA, 0, 0, true, "12-way, eig=0, pos=0" },320{ 0xA, 3, 11, true, "12-way, eig=3, max pos=11" },321{ 0xA, 6, 11, true, "12-way, eig=6, max pos=11" },322{ 0x5, 0, 0, false, "invalid eiw=5" },323{ 0x7, 0, 0, false, "invalid eiw=7" },324{ 0xB, 0, 0, false, "invalid eiw=0xB" },325{ 0xFF, 0, 0, false, "invalid eiw=0xFF" },326{ 0x1, 7, 0, false, "invalid eig=7 (out of range)" },327{ 0x2, 0x10, 0, false, "invalid eig=0x10" },328{ 0x3, 0xFFFF, 0, false, "invalid eig=0xFFFF" },329{ 0x1, 0, -1, false, "pos < 0" },330{ 0x1, 0, 2, false, "2-way, pos=2 (>= ways)" },331{ 0x2, 0, 4, false, "4-way, pos=4 (>= ways)" },332{ 0x3, 0, 8, false, "8-way, pos=8 (>= ways)" },333{ 0x4, 0, 16, false, "16-way, pos=16 (>= ways)" },334{ 0x8, 0, 3, false, "3-way, pos=3 (>= ways)" },335{ 0x9, 0, 6, false, "6-way, pos=6 (>= ways)" },336{ 0xA, 0, 12, false, "12-way, pos=12 (>= ways)" },337};338339static int test_cxl_validate_translation_params(void)340{341int i, rc, failures = 0;342bool valid;343344for (i = 0; i < ARRAY_SIZE(param_tests); i++) {345struct param_test *t = ¶m_tests[i];346347rc = cxl_validate_translation_params(t->eiw, t->eig, t->pos);348valid = (rc == 0);349350if (valid != t->expect) {351pr_err("test params failed: %s\n", t->desc);352failures++;353}354}355pr_info("..... test params: PASS %d FAIL %d\n", i - failures, failures);356357if (failures)358return -EINVAL;359360return 0;361}362363/*364* cxl_translate_init365*366* Run the internal validation tests when no params are passed.367* Otherwise, parse the parameters (test vectors), and kick off368* the translation test.369*370* Returns: 0 on success, negative error code on failure371*/372static int __init cxl_translate_init(void)373{374int rc, i;375376/* If no tables are passed, validate module params only */377if (table_num == 0) {378pr_info("Internal validation test start...\n");379rc = test_cxl_validate_translation_params();380if (rc)381return rc;382383rc = test_random_params();384if (rc)385return rc;386387pr_info("Internal validation test completed successfully\n");388389return 0;390}391392pr_info("CXL translate test module loaded with %d test vectors\n",393table_num);394395rc = setup_xor_mapping();396if (rc)397return rc;398399/* Process each test vector */400for (i = 0; i < table_num; i++) {401u64 dpa, expect_spa;402int pos, math;403u8 r_eiw, hb_ways;404u16 r_eig;405406pr_debug("Processing test vector %d: '%s'\n", i, table[i]);407408/* Parse the test vector */409rc = parse_test_vector(table[i], &dpa, &pos, &r_eiw, &r_eig,410&hb_ways, &math, &expect_spa);411if (rc) {412pr_err("CXL Translate Test %d: FAIL\n"413" Failed to parse test vector '%s'\n",414i, table[i]);415continue;416}417/* Run the translation test */418rc = run_translation_test(dpa, pos, r_eiw, r_eig, hb_ways, math,419expect_spa);420if (rc) {421pr_err("CXL Translate Test %d: FAIL\n"422" dpa=%llu pos=%d r_eiw=%u r_eig=%u hb_ways=%u math=%s expect_spa=%llu\n",423i, dpa, pos, r_eiw, r_eig, hb_ways,424(math == XOR_MATH) ? "XOR" : "MODULO",425expect_spa);426} else {427pr_info("CXL Translate Test %d: PASS\n", i);428}429}430431kfree(cximsd);432pr_info("CXL translate test completed\n");433434return 0;435}436437static void __exit cxl_translate_exit(void)438{439pr_info("CXL translate test module unloaded\n");440}441442module_param_array(table, charp, &table_num, 0444);443MODULE_PARM_DESC(table, "Test vectors as space-separated decimal strings");444445MODULE_LICENSE("GPL");446MODULE_DESCRIPTION("cxl_test: cxl address translation test module");447MODULE_IMPORT_NS("CXL");448449module_init(cxl_translate_init);450module_exit(cxl_translate_exit);451452453