The cxl-translate.sh unit test feeds trusted data sets to a kernel test module: cxl_translate. The module accesses the CXL region driver's address translation functions.
The trusted samples are either from the CXL Driver Writers Guide[1] or from another source that has been verified. ie a spreadsheet reviewed by CXL developers. [1] https://www.intel.com/content/www/us/en/content-details/643805/cxl-memory-device-sw-guide.html Signed-off-by: Alison Schofield <alison.schofi...@intel.com> --- Changes in v2: Use shell builtins true|false rather than string compares (Marc) Further explain the disable of SC2034 for nameref's (Marc) Use long format journalctl options, ie. --reverse (Marc) Correct module name as cxl_translate in commit log test/cxl-translate.sh | 302 ++++++++++++++++++++++++++++++++++++++++++ test/meson.build | 2 + 2 files changed, 304 insertions(+) create mode 100755 test/cxl-translate.sh diff --git a/test/cxl-translate.sh b/test/cxl-translate.sh new file mode 100755 index 000000000000..af872352d73c --- /dev/null +++ b/test/cxl-translate.sh @@ -0,0 +1,302 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2025 Intel Corporation. All rights reserved. + +# shellcheck disable=SC2034 +# +# Arrays in this script are passed by name into helper functions using Bash's +# nameref feature `declare -n`. This pattern supports writing generic test +# harnesses that can iterate over many different test vector arrays simply by +# passing the array name. ShellCheck doesn't track nameref indirection, so it +# incorrectly reports these arrays as unused (SC2034). At runtime they are +# fully used through the nameref, so these warnings are safe to ignore. + +# source common to get the err() only +. "$(dirname "$0")"/common + +trap 'err $LINENO' ERR +set -ex +rc=1 + +MODULO=0 +XOR=1 + +# Test against 'Sample Sets' and 'XOR Tables' +# +# Sample Set's have a pattern and the expected HPAs have been verified +# although maybe not published. They verify Modulo and XOR translations. +# +# XOR Table's are extracted from the CXL Driver Writers Guide [1]. +# Although the XOR Tables do not include an explicit check of the Modulo +# translation result, a Modulo calculation is always the first step in +# any XOR calculation. ie. if Modulo fails so does XOR. +# +# [1] https://www.intel.com/content/www/us/en/content-details/643805/cxl-memory-device-sw-guide.html + +# Start time for kernel log checking at millisecond granularity +set_log_start_time() { + log_start_time=$(date '+%Y-%m-%d %H:%M:%S.%3N') +} + +check_dmesg_results() { + local nr_entries=$1 + local expect_failures=${2:-false} # Optional param, builtin true|false + local log nr_pass nr_fail + + log=$(journalctl --reverse --dmesg --since "$log_start_time") + + nr_pass=$(echo "$log" | grep -c "CXL Translate Test.*PASS") || nr_pass=0 + nr_fail=$(echo "$log" | grep -c "CXL Translate Test.*FAIL") || nr_fail=0 + + if ! $expect_failures; then + # Expect all PASS and no FAIL + [ "$nr_pass" -eq "$nr_entries" ] || err "$LINENO" + [ "$nr_fail" -eq 0 ] || err "$LINENO" + else + # Expect no PASS and all FAIL + [ "$nr_pass" -eq 0 ] || err "$LINENO" + [ "$nr_fail" -eq "$nr_entries" ] || err "$LINENO" + fi +} + +# Sample Sets +# +# params_#: dpa, region eiw, region eig, host bridge ways +# expect_[modulo|xor]_#: expected spa for each position in the region +# interleave set for the modulo|xor math. +# +# Feeds the parameters with an expected SPA for each position in the region +# interleave to the test module. Returns success if the calculation matches +# the expected SPA and the reverse calculation returns the original DPA and +# position. + +# 4 way region interleave using 4 host bridges +# Notation: 1+1+1+1 +declare -A Sample_4R_4H=( + ["params_0"]="0 2 0 4" + ["expect_modulo_0"]="0 256 512 768" + ["expect_xor_0"]="0 256 512 768" + ["params_1"]="256 2 0 4" + ["expect_modulo_1"]="1024 1280 1536 1792" + ["expect_xor_1"]="1024 1280 1536 1792" + ["params_2"]="2048 2 0 4" + ["expect_modulo_2"]="8192 8448 8704 8960" + ["expect_xor_2"]="8192 8448 8704 8960" +) + +# 12 way region interleave using 12 host bridges +# Notation: 1+1+1+1+1+1+1+1+1+1+1+1 +declare -A Sample_12R_12H=( + ["params_0"]="0 10 0 12" + ["expect_modulo_0"]="0 256 512 768 1024 1280 1536 1792 2048 2304 2560 2816" + ["expect_xor_0"]="0 256 512 768 1024 1280 1536 1792 2304 2048 2816 2560" + ["params_1"]="512 10 0 12" + ["expect_modulo_1"]="6144 6400 6656 6912 7168 7424 7680 7936 8192 8448 8704 8960" + ["expect_xor_1"]="6912 6656 6400 6144 7936 7680 7424 7168 8192 8448 8704 8960" +) + +decode_r_eiw() +{ + case $1 in + 0) echo 1 ;; + 1) echo 2 ;; + 2) echo 4 ;; + 3) echo 8 ;; + 4) echo 16 ;; + 8) echo 3 ;; + 9) echo 6 ;; + 10) echo 12 ;; + *) echo "Invalid r_eiw value: $1" ; err "$LINENO" ;; + esac +} + +generate_sample_tests() { + local -n input_sample_set=$1 + local -n output_array=$2 + + # Find all params_* keys and extract the index + local indices=() + for key in "${!input_sample_set[@]}"; do + if [[ $key =~ ^params_([0-9]+)$ ]]; then + indices+=("${BASH_REMATCH[1]}") + fi + done + + # Sort indices to process in order + mapfile -t indices < <(printf '%s\n' "${indices[@]}" | sort -n) + + for i in "${indices[@]}"; do + # Split the parameters and expected spa values + IFS=' ' read -r dpa r_eiw r_eig hb_ways <<< "${input_sample_set["params_$i"]}" + IFS=' ' read -r -a expect_modulo_values <<< "${input_sample_set["expect_modulo_$i"]}" + IFS=' ' read -r -a expect_xor_values <<< "${input_sample_set["expect_xor_$i"]}" + + ways=$(decode_r_eiw "$r_eiw") + for ((pos = 0; pos < ways; pos++)); do + expect_spa_modulo=${expect_modulo_values[$pos]} + expect_spa_xor=${expect_xor_values[$pos]} + + # Add the MODULO test case, then the XOR test case + output_array+=("$dpa $pos $r_eiw $r_eig $hb_ways $MODULO $expect_spa_modulo") + output_array+=("$dpa $pos $r_eiw $r_eig $hb_ways $XOR $expect_spa_xor") + done + done +} + +test_sample_sets() { + local sample_name=$1 + local -n sample_set=$1 + local generated_tests=() + + generate_sample_tests sample_set generated_tests + + IFS=',' + table_string="${generated_tests[*]}" + IFS=' ' + + modprobe -r cxl-translate + set_log_start_time + echo "Testing $sample_name with ${#generated_tests[@]} test entries" + modprobe cxl-translate "table=$table_string" + check_dmesg_results "${#generated_tests[@]}" +} + +# XOR Tables +# +# The tables that follow are the XOR translation examples in the +# CXL Driver Writers Guide Sections 2.13.24.1 and 25.1 +# Note that the Guide uses the device number in its table notation. +# Here the 'position' of that device number is used, not the +# device number itself, which is meaningless in this context. +# +# Format: "dpa pos r_eiw r_eig hb_ways xor_math(0|1) xor_spa: + +# 4 way region interleave using 4 host bridges +# Notation: 1+1+1+1 +XOR_Table_4R_4H=( + "248 0 2 0 4 1 248" + "16 1 2 0 4 1 272" + "16 2 2 0 4 1 528" + "32 3 2 0 4 1 800" + "288 0 2 0 4 1 1056" + "288 1 2 0 4 1 1312" + "288 2 2 0 4 1 1568" + "288 3 2 0 4 1 1824" + "544 1 2 0 4 1 2080" + "544 0 2 0 4 1 2336" + "544 3 2 0 4 1 2592" + "1040 2 2 0 4 1 4112" + "1568 3 2 0 4 1 6176" + "32784 1 2 0 4 1 131088" + "65552 2 2 0 4 1 262160" + "98336 3 2 0 4 1 393248" + "98328 2 2 0 4 1 393496" + "98352 2 2 0 4 1 393520" + "443953523 0 2 0 4 1 1775813747" +) + +XOR_Table_8R_4H=( + "248 0 3 0 4 1 248" + "16 1 3 0 4 1 272" + "16 2 3 0 4 1 528" + "32 3 3 0 4 1 800" + "272 1 3 0 4 1 2064" + "528 2 3 0 4 1 4112" + "800 3 3 0 4 1 6176" + "16400 1 3 0 4 1 131088" + "32784 2 3 0 4 1 262160" + "49184 3 3 0 4 1 393248" + "49176 2 3 0 4 1 393496" + "49200 2 3 0 4 1 393520" + "116520373 3 3 0 4 1 932162229" + "244690459 5 3 0 4 1 1957525275" + "292862215 5 3 0 4 1 2342899463" + "30721158 4 3 0 4 1 245769350" + "246386959 4 3 0 4 1 1971096847" + "72701249 5 3 0 4 1 581610561" + "529382429 5 3 0 4 1 4235060509" + "191132300 2 3 0 4 1 1529057420" + "18589081 1 3 0 4 1 148712089" + "344295715 7 3 0 4 1 2754367011" +) + +XOR_Table_12R_12H=( + "224 0 10 0 12 1 224" + "16 1 10 0 12 1 272" + "16 2 10 0 12 1 528" + "32 3 10 0 12 1 800" + "32 4 10 0 12 1 1056" + "32 5 10 0 12 1 1312" + "32 6 10 0 12 1 1568" + "32 7 10 0 12 1 1824" + "32 9 10 0 12 1 2080" + "32 8 10 0 12 1 2336" + "32 11 10 0 12 1 2592" + "32 10 10 0 12 1 2848" + "288 0 10 0 12 1 3360" + "299017087 7 10 0 12 1 3588205439" + "329210435 0 10 0 12 1 3950524995" + "151050637 11 10 0 12 1 1812608653" + "145169214 2 10 0 12 1 1742030654" + "328998732 10 10 0 12 1 3947985996" + "159252439 3 10 0 12 1 1911027415" + "342098916 5 10 0 12 1 4105186020" + "97970344 8 10 0 12 1 1175645096" + "214995572 8 10 0 12 1 2579948404" + "101289661 7 10 0 12 1 1215475645" + "40424079 7 10 0 12 1 485088911" + "231458716 7 10 0 12 1 2777503900" +) + +# A fail table entry is expected to fail the DPA->SPA calculation. +# Intent is to show that the test module can tell good from bad. +# If one of these cases passes, don't trust other pass results. +# This is not a test of module parsing, so don't send garbage. +Expect_Fail_Table=( + "544 3 2 0 4 1 2080" # Change position + "544 2 2 0 4 1 2336" + "544 1 2 0 4 1 2592" + "272 1 2 0 4 1 2064" # Change r_eiw + "528 2 1 0 4 1 4112" + "800 3 10 0 4 1 6176" + "32 4 10 0 12 1 1156" # Change expected spa + "32 5 10 0 12 1 1112" + "32 6 10 0 12 1 1168" + "32 7 10 0 12 1 1124" +) + +test_tables() { + local table_name=$1 + local -n table_ref=$1 + local expect_failures=${2:-false} # Optional param, builtin true|false + local IFS + + IFS=',' + table_string="${table_ref[*]}" + IFS=' ' + + if $expect_failures; then + echo "Testing $table_name with ${#table_ref[@]} entries (expecting FAIL results)" + else + echo "Testing $table_name with ${#table_ref[@]} test entries" + fi + + modprobe -r cxl-translate + set_log_start_time + modprobe cxl-translate "table=$table_string" + + check_dmesg_results "${#table_ref[@]}" "$expect_failures" +} + +test_tables Expect_Fail_Table true +test_sample_sets Sample_4R_4H +test_sample_sets Sample_12R_12H +test_tables XOR_Table_4R_4H +test_tables XOR_Table_8R_4H +test_tables XOR_Table_12R_12H + +echo "Translate test complete with no failures" + +modprobe -r cxl-translate + + diff --git a/test/meson.build b/test/meson.build index 91eb6c2b1363..bf4d73eea918 100644 --- a/test/meson.build +++ b/test/meson.build @@ -168,6 +168,7 @@ cxl_sanitize = find_program('cxl-sanitize.sh') cxl_destroy_region = find_program('cxl-destroy-region.sh') cxl_qos_class = find_program('cxl-qos-class.sh') cxl_poison = find_program('cxl-poison.sh') +cxl_translate = find_program('cxl-translate.sh') tests = [ [ 'libndctl', libndctl, 'ndctl' ], @@ -201,6 +202,7 @@ tests = [ [ 'cxl-destroy-region.sh', cxl_destroy_region, 'cxl' ], [ 'cxl-qos-class.sh', cxl_qos_class, 'cxl' ], [ 'cxl-poison.sh', cxl_poison, 'cxl' ], + [ 'cxl-translate.sh', cxl_translate, 'cxl' ], ] if get_option('destructive').enabled() -- 2.37.3