Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'for-6.19/cxl-addr-xlat' into cxl-for-next

Enable unit testing for XOR address translation of SPA to DPA and vice versa.

+637 -71
+30 -11
drivers/cxl/acpi.c
··· 11 11 #include "cxlpci.h" 12 12 #include "cxl.h" 13 13 14 - struct cxl_cxims_data { 15 - int nr_maps; 16 - u64 xormaps[] __counted_by(nr_maps); 17 - }; 18 - 19 14 static const guid_t acpi_cxl_qtg_id_guid = 20 15 GUID_INIT(0xF365F9A6, 0xA7DE, 0x4071, 21 16 0xA6, 0x6A, 0xB4, 0x0C, 0x0B, 0x4F, 0x8E, 0x52); 22 17 23 - static u64 cxl_apply_xor_maps(struct cxl_root_decoder *cxlrd, u64 addr) 18 + #define HBIW_TO_NR_MAPS_SIZE (CXL_DECODER_MAX_INTERLEAVE + 1) 19 + static const int hbiw_to_nr_maps[HBIW_TO_NR_MAPS_SIZE] = { 20 + [1] = 0, [2] = 1, [3] = 0, [4] = 2, [6] = 1, [8] = 3, [12] = 2, [16] = 4 21 + }; 22 + 23 + static const int valid_hbiw[] = { 1, 2, 3, 4, 6, 8, 12, 16 }; 24 + 25 + u64 cxl_do_xormap_calc(struct cxl_cxims_data *cximsd, u64 addr, int hbiw) 24 26 { 25 - struct cxl_cxims_data *cximsd = cxlrd->platform_data; 26 - int hbiw = cxlrd->cxlsd.nr_targets; 27 + int nr_maps_to_apply = -1; 27 28 u64 val; 28 29 int pos; 29 30 30 - /* No xormaps for host bridge interleave ways of 1 or 3 */ 31 - if (hbiw == 1 || hbiw == 3) 32 - return addr; 31 + /* 32 + * Strictly validate hbiw since this function is used for testing and 33 + * that nullifies any expectation of trusted parameters from the CXL 34 + * Region Driver. 35 + */ 36 + for (int i = 0; i < ARRAY_SIZE(valid_hbiw); i++) { 37 + if (valid_hbiw[i] == hbiw) { 38 + nr_maps_to_apply = hbiw_to_nr_maps[hbiw]; 39 + break; 40 + } 41 + } 42 + if (nr_maps_to_apply == -1 || nr_maps_to_apply > cximsd->nr_maps) 43 + return ULLONG_MAX; 33 44 34 45 /* 35 46 * In regions using XOR interleave arithmetic the CXL HPA may not ··· 70 59 } 71 60 72 61 return addr; 62 + } 63 + EXPORT_SYMBOL_FOR_MODULES(cxl_do_xormap_calc, "cxl_translate"); 64 + 65 + static u64 cxl_apply_xor_maps(struct cxl_root_decoder *cxlrd, u64 addr) 66 + { 67 + struct cxl_cxims_data *cximsd = cxlrd->platform_data; 68 + 69 + return cxl_do_xormap_calc(cximsd, addr, cxlrd->cxlsd.nr_targets); 73 70 } 74 71 75 72 struct cxl_cxims_context {
+142 -60
drivers/cxl/core/region.c
··· 2953 2953 return cxlrd->ops && cxlrd->ops->spa_to_hpa; 2954 2954 } 2955 2955 2956 - u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd, 2957 - u64 dpa) 2956 + #define CXL_POS_ZERO 0 2957 + /** 2958 + * cxl_validate_translation_params 2959 + * @eiw: encoded interleave ways 2960 + * @eig: encoded interleave granularity 2961 + * @pos: position in interleave 2962 + * 2963 + * Callers pass CXL_POS_ZERO when no position parameter needs validating. 2964 + * 2965 + * Returns: 0 on success, -EINVAL on first invalid parameter 2966 + */ 2967 + int cxl_validate_translation_params(u8 eiw, u16 eig, int pos) 2958 2968 { 2959 - struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent); 2960 - u64 dpa_offset, hpa_offset, bits_upper, mask_upper, hpa; 2961 - struct cxl_region_params *p = &cxlr->params; 2962 - struct cxl_endpoint_decoder *cxled = NULL; 2963 - u16 eig = 0; 2964 - u8 eiw = 0; 2965 - int pos; 2969 + int ways, gran; 2966 2970 2967 - for (int i = 0; i < p->nr_targets; i++) { 2968 - cxled = p->targets[i]; 2969 - if (cxlmd == cxled_to_memdev(cxled)) 2970 - break; 2971 + if (eiw_to_ways(eiw, &ways)) { 2972 + pr_debug("%s: invalid eiw=%u\n", __func__, eiw); 2973 + return -EINVAL; 2971 2974 } 2972 - if (!cxled || cxlmd != cxled_to_memdev(cxled)) 2975 + if (eig_to_granularity(eig, &gran)) { 2976 + pr_debug("%s: invalid eig=%u\n", __func__, eig); 2977 + return -EINVAL; 2978 + } 2979 + if (pos < 0 || pos >= ways) { 2980 + pr_debug("%s: invalid pos=%d for ways=%u\n", __func__, pos, 2981 + ways); 2982 + return -EINVAL; 2983 + } 2984 + 2985 + return 0; 2986 + } 2987 + EXPORT_SYMBOL_FOR_MODULES(cxl_validate_translation_params, "cxl_translate"); 2988 + 2989 + u64 cxl_calculate_dpa_offset(u64 hpa_offset, u8 eiw, u16 eig) 2990 + { 2991 + u64 dpa_offset, bits_lower, bits_upper, temp; 2992 + int ret; 2993 + 2994 + ret = cxl_validate_translation_params(eiw, eig, CXL_POS_ZERO); 2995 + if (ret) 2973 2996 return ULLONG_MAX; 2974 2997 2975 - pos = cxled->pos; 2976 - ways_to_eiw(p->interleave_ways, &eiw); 2977 - granularity_to_eig(p->interleave_granularity, &eig); 2998 + /* 2999 + * DPA offset: CXL Spec 3.2 Section 8.2.4.20.13 3000 + * Lower bits [IG+7:0] pass through unchanged 3001 + * (eiw < 8) 3002 + * Per spec: DPAOffset[51:IG+8] = (HPAOffset[51:IG+IW+8] >> IW) 3003 + * Clear the position bits to isolate upper section, then 3004 + * reverse the left shift by eiw that occurred during DPA->HPA 3005 + * (eiw >= 8) 3006 + * Per spec: DPAOffset[51:IG+8] = HPAOffset[51:IG+IW] / 3 3007 + * Extract upper bits from the correct bit range and divide by 3 3008 + * to recover the original DPA upper bits 3009 + */ 3010 + bits_lower = hpa_offset & GENMASK_ULL(eig + 7, 0); 3011 + if (eiw < 8) { 3012 + temp = hpa_offset &= ~GENMASK_ULL(eig + eiw + 8 - 1, 0); 3013 + dpa_offset = temp >> eiw; 3014 + } else { 3015 + bits_upper = div64_u64(hpa_offset >> (eig + eiw), 3); 3016 + dpa_offset = bits_upper << (eig + 8); 3017 + } 3018 + dpa_offset |= bits_lower; 3019 + 3020 + return dpa_offset; 3021 + } 3022 + EXPORT_SYMBOL_FOR_MODULES(cxl_calculate_dpa_offset, "cxl_translate"); 3023 + 3024 + int cxl_calculate_position(u64 hpa_offset, u8 eiw, u16 eig) 3025 + { 3026 + unsigned int ways = 0; 3027 + u64 shifted, rem; 3028 + int pos, ret; 3029 + 3030 + ret = cxl_validate_translation_params(eiw, eig, CXL_POS_ZERO); 3031 + if (ret) 3032 + return ret; 3033 + 3034 + if (!eiw) 3035 + /* position is 0 if no interleaving */ 3036 + return 0; 3037 + 3038 + /* 3039 + * Interleave position: CXL Spec 3.2 Section 8.2.4.20.13 3040 + * eiw < 8 3041 + * Position is in the IW bits at HPA_OFFSET[IG+8+IW-1:IG+8]. 3042 + * Per spec "remove IW bits starting with bit position IG+8" 3043 + * eiw >= 8 3044 + * Position is not explicitly stored in HPA_OFFSET bits. It is 3045 + * derived from the modulo operation of the upper bits using 3046 + * the total number of interleave ways. 3047 + */ 3048 + if (eiw < 8) { 3049 + pos = (hpa_offset >> (eig + 8)) & GENMASK(eiw - 1, 0); 3050 + } else { 3051 + shifted = hpa_offset >> (eig + 8); 3052 + eiw_to_ways(eiw, &ways); 3053 + div64_u64_rem(shifted, ways, &rem); 3054 + pos = rem; 3055 + } 3056 + 3057 + return pos; 3058 + } 3059 + EXPORT_SYMBOL_FOR_MODULES(cxl_calculate_position, "cxl_translate"); 3060 + 3061 + u64 cxl_calculate_hpa_offset(u64 dpa_offset, int pos, u8 eiw, u16 eig) 3062 + { 3063 + u64 mask_upper, hpa_offset, bits_upper; 3064 + int ret; 3065 + 3066 + ret = cxl_validate_translation_params(eiw, eig, pos); 3067 + if (ret) 3068 + return ULLONG_MAX; 2978 3069 2979 3070 /* 2980 3071 * The device position in the region interleave set was removed ··· 3076 2985 * ways and granularity and is defined in the CXL Spec 3.0 Section 3077 2986 * 8.2.4.19.13 Implementation Note: Device Decode Logic 3078 2987 */ 3079 - 3080 - /* Remove the dpa base */ 3081 - dpa_offset = dpa - cxl_dpa_resource_start(cxled); 3082 2988 3083 2989 mask_upper = GENMASK_ULL(51, eig + 8); 3084 2990 ··· 3090 3002 3091 3003 /* The lower bits remain unchanged */ 3092 3004 hpa_offset |= dpa_offset & GENMASK_ULL(eig + 7, 0); 3005 + 3006 + return hpa_offset; 3007 + } 3008 + EXPORT_SYMBOL_FOR_MODULES(cxl_calculate_hpa_offset, "cxl_translate"); 3009 + 3010 + u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd, 3011 + u64 dpa) 3012 + { 3013 + struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent); 3014 + struct cxl_region_params *p = &cxlr->params; 3015 + struct cxl_endpoint_decoder *cxled = NULL; 3016 + u64 dpa_offset, hpa_offset, hpa; 3017 + u16 eig = 0; 3018 + u8 eiw = 0; 3019 + int pos; 3020 + 3021 + for (int i = 0; i < p->nr_targets; i++) { 3022 + if (cxlmd == cxled_to_memdev(p->targets[i])) { 3023 + cxled = p->targets[i]; 3024 + break; 3025 + } 3026 + } 3027 + if (!cxled) 3028 + return ULLONG_MAX; 3029 + 3030 + pos = cxled->pos; 3031 + ways_to_eiw(p->interleave_ways, &eiw); 3032 + granularity_to_eig(p->interleave_granularity, &eig); 3033 + 3034 + dpa_offset = dpa - cxl_dpa_resource_start(cxled); 3035 + hpa_offset = cxl_calculate_hpa_offset(dpa_offset, pos, eiw, eig); 3093 3036 3094 3037 /* Apply the hpa_offset to the region base address */ 3095 3038 hpa = hpa_offset + p->res->start + p->cache_size; ··· 3154 3035 struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent); 3155 3036 struct cxl_endpoint_decoder *cxled; 3156 3037 u64 hpa, hpa_offset, dpa_offset; 3157 - u64 bits_upper, bits_lower; 3158 - u64 shifted, rem, temp; 3159 3038 u16 eig = 0; 3160 3039 u8 eiw = 0; 3161 3040 int pos; ··· 3175 3058 } else { 3176 3059 hpa_offset = offset; 3177 3060 } 3178 - /* 3179 - * Interleave position: CXL Spec 3.2 Section 8.2.4.20.13 3180 - * eiw < 8 3181 - * Position is in the IW bits at HPA_OFFSET[IG+8+IW-1:IG+8]. 3182 - * Per spec "remove IW bits starting with bit position IG+8" 3183 - * eiw >= 8 3184 - * Position is not explicitly stored in HPA_OFFSET bits. It is 3185 - * derived from the modulo operation of the upper bits using 3186 - * the total number of interleave ways. 3187 - */ 3188 - if (eiw < 8) { 3189 - pos = (hpa_offset >> (eig + 8)) & GENMASK(eiw - 1, 0); 3190 - } else { 3191 - shifted = hpa_offset >> (eig + 8); 3192 - div64_u64_rem(shifted, p->interleave_ways, &rem); 3193 - pos = rem; 3194 - } 3061 + 3062 + pos = cxl_calculate_position(hpa_offset, eiw, eig); 3195 3063 if (pos < 0 || pos >= p->nr_targets) { 3196 3064 dev_dbg(&cxlr->dev, "Invalid position %d for %d targets\n", 3197 3065 pos, p->nr_targets); 3198 3066 return -ENXIO; 3199 3067 } 3200 3068 3201 - /* 3202 - * DPA offset: CXL Spec 3.2 Section 8.2.4.20.13 3203 - * Lower bits [IG+7:0] pass through unchanged 3204 - * (eiw < 8) 3205 - * Per spec: DPAOffset[51:IG+8] = (HPAOffset[51:IG+IW+8] >> IW) 3206 - * Clear the position bits to isolate upper section, then 3207 - * reverse the left shift by eiw that occurred during DPA->HPA 3208 - * (eiw >= 8) 3209 - * Per spec: DPAOffset[51:IG+8] = HPAOffset[51:IG+IW] / 3 3210 - * Extract upper bits from the correct bit range and divide by 3 3211 - * to recover the original DPA upper bits 3212 - */ 3213 - bits_lower = hpa_offset & GENMASK_ULL(eig + 7, 0); 3214 - if (eiw < 8) { 3215 - temp = hpa_offset &= ~((u64)GENMASK(eig + eiw + 8 - 1, 0)); 3216 - dpa_offset = temp >> eiw; 3217 - } else { 3218 - bits_upper = div64_u64(hpa_offset >> (eig + eiw), 3); 3219 - dpa_offset = bits_upper << (eig + 8); 3220 - } 3221 - dpa_offset |= bits_lower; 3069 + dpa_offset = cxl_calculate_dpa_offset(hpa_offset, eiw, eig); 3222 3070 3223 3071 /* Look-up and return the result: a memdev and a DPA */ 3224 3072 for (int i = 0; i < p->nr_targets; i++) {
+19
drivers/cxl/cxl.h
··· 746 746 return port->uport_dev == port->dev.parent; 747 747 } 748 748 749 + /* Address translation functions exported to cxl_translate test module only */ 750 + int cxl_validate_translation_params(u8 eiw, u16 eig, int pos); 751 + u64 cxl_calculate_hpa_offset(u64 dpa_offset, int pos, u8 eiw, u16 eig); 752 + u64 cxl_calculate_dpa_offset(u64 hpa_offset, u8 eiw, u16 eig); 753 + int cxl_calculate_position(u64 hpa_offset, u8 eiw, u16 eig); 754 + struct cxl_cxims_data { 755 + int nr_maps; 756 + u64 xormaps[] __counted_by(nr_maps); 757 + }; 758 + 759 + #if IS_ENABLED(CONFIG_CXL_ACPI) 760 + u64 cxl_do_xormap_calc(struct cxl_cxims_data *cximsd, u64 addr, int hbiw); 761 + #else 762 + static inline u64 cxl_do_xormap_calc(struct cxl_cxims_data *cximsd, u64 addr, int hbiw) 763 + { 764 + return ULLONG_MAX; 765 + } 766 + #endif 767 + 749 768 int cxl_num_decoders_committed(struct cxl_port *port); 750 769 bool is_cxl_port(const struct device *dev); 751 770 struct cxl_port *to_cxl_port(const struct device *dev);
+1
tools/testing/cxl/test/Kbuild
··· 4 4 obj-m += cxl_test.o 5 5 obj-m += cxl_mock.o 6 6 obj-m += cxl_mock_mem.o 7 + obj-m += cxl_translate.o 7 8 8 9 cxl_test-y := cxl.o 9 10 cxl_mock-y := mock.o
+445
tools/testing/cxl/test/cxl_translate.c
··· 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 + 72 + if (math == XOR_MATH) { 73 + cximsd->nr_maps = hbiw_to_nr_maps[hb_ways]; 74 + if (cximsd->nr_maps) 75 + return cxl_do_xormap_calc(cximsd, hpa_offset, hb_ways); 76 + } 77 + return hpa_offset; 78 + } 79 + 80 + /** 81 + * to_dpa - translate an HPA offset to DPA offset 82 + * 83 + * hpa_offset: host physical address offset 84 + * r_eiw: region encoded interleave ways 85 + * r_eig: region encoded interleave granularity 86 + * hb_ways: host bridge interleave ways 87 + * math: interleave arithmetic (MODULO_MATH or XOR_MATH) 88 + * 89 + * Returns: device physical address offset 90 + */ 91 + static u64 to_dpa(u64 hpa_offset, u8 r_eiw, u16 r_eig, u8 hb_ways, u8 math) 92 + { 93 + u64 offset = hpa_offset; 94 + 95 + if (math == XOR_MATH) { 96 + cximsd->nr_maps = hbiw_to_nr_maps[hb_ways]; 97 + if (cximsd->nr_maps) 98 + offset = 99 + cxl_do_xormap_calc(cximsd, hpa_offset, hb_ways); 100 + } 101 + return cxl_calculate_dpa_offset(offset, r_eiw, r_eig); 102 + } 103 + 104 + /** 105 + * to_pos - extract an interleave position from an HPA offset 106 + * 107 + * hpa_offset: host physical address offset 108 + * r_eiw: region encoded interleave ways 109 + * r_eig: region encoded interleave granularity 110 + * hb_ways: host bridge interleave ways 111 + * math: interleave arithmetic (MODULO_MATH or XOR_MATH) 112 + * 113 + * Returns: devices position in region interleave 114 + */ 115 + static u64 to_pos(u64 hpa_offset, u8 r_eiw, u16 r_eig, u8 hb_ways, u8 math) 116 + { 117 + u64 offset = hpa_offset; 118 + 119 + /* Reverse XOR mapping if specified */ 120 + if (math == XOR_MATH) 121 + offset = cxl_do_xormap_calc(cximsd, hpa_offset, hb_ways); 122 + 123 + return cxl_calculate_position(offset, r_eiw, r_eig); 124 + } 125 + 126 + /** 127 + * run_translation_test - execute forward and reverse translations 128 + * 129 + * @dpa: device physical address 130 + * @pos: expected position in region interleave 131 + * @r_eiw: region encoded interleave ways 132 + * @r_eig: region encoded interleave granularity 133 + * @hb_ways: host bridge interleave ways 134 + * @math: interleave arithmetic (MODULO_MATH or XOR_MATH) 135 + * @expect_spa: expected system physical address 136 + * 137 + * Returns: 0 on success, -1 on failure 138 + */ 139 + static int run_translation_test(u64 dpa, int pos, u8 r_eiw, u16 r_eig, 140 + u8 hb_ways, int math, u64 expect_hpa) 141 + { 142 + u64 translated_spa, reverse_dpa; 143 + int reverse_pos; 144 + 145 + /* Test Device to Host translation: DPA + POS -> SPA */ 146 + translated_spa = to_hpa(dpa, pos, r_eiw, r_eig, hb_ways, math); 147 + if (translated_spa != expect_hpa) { 148 + pr_err("Device to host failed: expected HPA %llu, got %llu\n", 149 + expect_hpa, translated_spa); 150 + return -1; 151 + } 152 + 153 + /* Test Host to Device DPA translation: SPA -> DPA */ 154 + reverse_dpa = to_dpa(translated_spa, r_eiw, r_eig, hb_ways, math); 155 + if (reverse_dpa != dpa) { 156 + pr_err("Host to Device DPA failed: expected %llu, got %llu\n", 157 + dpa, reverse_dpa); 158 + return -1; 159 + } 160 + 161 + /* Test Host to Device Position translation: SPA -> POS */ 162 + reverse_pos = to_pos(translated_spa, r_eiw, r_eig, hb_ways, math); 163 + if (reverse_pos != pos) { 164 + pr_err("Position lookup failed: expected %d, got %d\n", pos, 165 + reverse_pos); 166 + return -1; 167 + } 168 + 169 + return 0; 170 + } 171 + 172 + /** 173 + * parse_test_vector - parse a single test vector string 174 + * 175 + * entry: test vector string to parse 176 + * dpa: device physical address 177 + * pos: expected position in region interleave 178 + * r_eiw: region encoded interleave ways 179 + * r_eig: region encoded interleave granularity 180 + * hb_ways: host bridge interleave ways 181 + * math: interleave arithmetic (MODULO_MATH or XOR_MATH) 182 + * expect_spa: expected system physical address 183 + * 184 + * Returns: 0 on success, negative error code on failure 185 + */ 186 + static int parse_test_vector(const char *entry, u64 *dpa, int *pos, u8 *r_eiw, 187 + u16 *r_eig, u8 *hb_ways, int *math, 188 + u64 *expect_hpa) 189 + { 190 + unsigned int tmp_r_eiw, tmp_r_eig, tmp_hb_ways; 191 + int parsed; 192 + 193 + parsed = sscanf(entry, "%llu %d %u %u %u %d %llu", dpa, pos, &tmp_r_eiw, 194 + &tmp_r_eig, &tmp_hb_ways, math, expect_hpa); 195 + 196 + if (parsed != EXPECTED_PARAMS) { 197 + pr_err("Parse error: expected %d parameters, got %d in '%s'\n", 198 + EXPECTED_PARAMS, parsed, entry); 199 + return -EINVAL; 200 + } 201 + if (tmp_r_eiw > U8_MAX || tmp_r_eig > U16_MAX || tmp_hb_ways > U8_MAX) { 202 + pr_err("Parameter overflow in entry: '%s'\n", entry); 203 + return -ERANGE; 204 + } 205 + if (*math != MODULO_MATH && *math != XOR_MATH) { 206 + pr_err("Invalid math type %d in entry: '%s'\n", *math, entry); 207 + return -EINVAL; 208 + } 209 + *r_eiw = tmp_r_eiw; 210 + *r_eig = tmp_r_eig; 211 + *hb_ways = tmp_hb_ways; 212 + 213 + return 0; 214 + } 215 + 216 + /* 217 + * setup_xor_mapping - Initialize XOR mapping data structure 218 + * 219 + * The test data sets all use the same HBIG so we can use one set 220 + * of xormaps, and set the number to apply based on HBIW before 221 + * calling cxl_do_xormap_calc(). 222 + * 223 + * When additional data sets arrive for validation with different 224 + * HBIG's this static setup will need to be updated. 225 + * 226 + * Returns: 0 on success, negative error code on failure 227 + */ 228 + static int setup_xor_mapping(void) 229 + { 230 + if (nr_maps <= 0) 231 + return -EINVAL; 232 + 233 + cximsd = kzalloc(struct_size(cximsd, xormaps, nr_maps), GFP_KERNEL); 234 + if (!cximsd) 235 + return -ENOMEM; 236 + 237 + memcpy(cximsd->xormaps, xormaps, nr_maps * sizeof(*cximsd->xormaps)); 238 + cximsd->nr_maps = nr_maps; 239 + 240 + return 0; 241 + } 242 + 243 + static int test_random_params(void) 244 + { 245 + u8 valid_eiws[] = { 0, 1, 2, 3, 4, 8, 9, 10 }; 246 + u16 valid_eigs[] = { 0, 1, 2, 3, 4, 5, 6 }; 247 + int i, ways, pos, reverse_pos; 248 + u64 dpa, hpa, reverse_dpa; 249 + int iterations = 10000; 250 + int failures = 0; 251 + 252 + for (i = 0; i < iterations; i++) { 253 + /* Generate valid random parameters for eiw, eig, pos, dpa */ 254 + u8 eiw = valid_eiws[get_random_u32() % ARRAY_SIZE(valid_eiws)]; 255 + u16 eig = valid_eigs[get_random_u32() % ARRAY_SIZE(valid_eigs)]; 256 + 257 + eiw_to_ways(eiw, &ways); 258 + pos = get_random_u32() % ways; 259 + dpa = get_random_u64() >> 12; 260 + 261 + hpa = cxl_calculate_hpa_offset(dpa, pos, eiw, eig); 262 + reverse_dpa = cxl_calculate_dpa_offset(hpa, eiw, eig); 263 + reverse_pos = cxl_calculate_position(hpa, eiw, eig); 264 + 265 + if (reverse_dpa != dpa || reverse_pos != pos) { 266 + pr_err("test random iter %d FAIL hpa=%llu, dpa=%llu reverse_dpa=%llu, pos=%d reverse_pos=%d eiw=%u eig=%u\n", 267 + i, hpa, dpa, reverse_dpa, pos, reverse_pos, eiw, 268 + eig); 269 + 270 + if (failures++ > 10) { 271 + pr_err("test random too many failures, stop\n"); 272 + break; 273 + } 274 + } 275 + } 276 + pr_info("..... test random: PASS %d FAIL %d\n", i - failures, failures); 277 + 278 + if (failures) 279 + return -EINVAL; 280 + 281 + return 0; 282 + } 283 + 284 + struct param_test { 285 + u8 eiw; 286 + u16 eig; 287 + int pos; 288 + bool expect; /* true: expect pass, false: expect fail */ 289 + const char *desc; 290 + }; 291 + 292 + static struct param_test param_tests[] = { 293 + { 0x0, 0, 0, true, "1-way, min eig=0, pos=0" }, 294 + { 0x0, 3, 0, true, "1-way, mid eig=3, pos=0" }, 295 + { 0x0, 6, 0, true, "1-way, max eig=6, pos=0" }, 296 + { 0x1, 0, 0, true, "2-way, eig=0, pos=0" }, 297 + { 0x1, 3, 1, true, "2-way, eig=3, max pos=1" }, 298 + { 0x1, 6, 1, true, "2-way, eig=6, max pos=1" }, 299 + { 0x2, 0, 0, true, "4-way, eig=0, pos=0" }, 300 + { 0x2, 3, 3, true, "4-way, eig=3, max pos=3" }, 301 + { 0x2, 6, 3, true, "4-way, eig=6, max pos=3" }, 302 + { 0x3, 0, 0, true, "8-way, eig=0, pos=0" }, 303 + { 0x3, 3, 7, true, "8-way, eig=3, max pos=7" }, 304 + { 0x3, 6, 7, true, "8-way, eig=6, max pos=7" }, 305 + { 0x4, 0, 0, true, "16-way, eig=0, pos=0" }, 306 + { 0x4, 3, 15, true, "16-way, eig=3, max pos=15" }, 307 + { 0x4, 6, 15, true, "16-way, eig=6, max pos=15" }, 308 + { 0x8, 0, 0, true, "3-way, eig=0, pos=0" }, 309 + { 0x8, 3, 2, true, "3-way, eig=3, max pos=2" }, 310 + { 0x8, 6, 2, true, "3-way, eig=6, max pos=2" }, 311 + { 0x9, 0, 0, true, "6-way, eig=0, pos=0" }, 312 + { 0x9, 3, 5, true, "6-way, eig=3, max pos=5" }, 313 + { 0x9, 6, 5, true, "6-way, eig=6, max pos=5" }, 314 + { 0xA, 0, 0, true, "12-way, eig=0, pos=0" }, 315 + { 0xA, 3, 11, true, "12-way, eig=3, max pos=11" }, 316 + { 0xA, 6, 11, true, "12-way, eig=6, max pos=11" }, 317 + { 0x5, 0, 0, false, "invalid eiw=5" }, 318 + { 0x7, 0, 0, false, "invalid eiw=7" }, 319 + { 0xB, 0, 0, false, "invalid eiw=0xB" }, 320 + { 0xFF, 0, 0, false, "invalid eiw=0xFF" }, 321 + { 0x1, 7, 0, false, "invalid eig=7 (out of range)" }, 322 + { 0x2, 0x10, 0, false, "invalid eig=0x10" }, 323 + { 0x3, 0xFFFF, 0, false, "invalid eig=0xFFFF" }, 324 + { 0x1, 0, -1, false, "pos < 0" }, 325 + { 0x1, 0, 2, false, "2-way, pos=2 (>= ways)" }, 326 + { 0x2, 0, 4, false, "4-way, pos=4 (>= ways)" }, 327 + { 0x3, 0, 8, false, "8-way, pos=8 (>= ways)" }, 328 + { 0x4, 0, 16, false, "16-way, pos=16 (>= ways)" }, 329 + { 0x8, 0, 3, false, "3-way, pos=3 (>= ways)" }, 330 + { 0x9, 0, 6, false, "6-way, pos=6 (>= ways)" }, 331 + { 0xA, 0, 12, false, "12-way, pos=12 (>= ways)" }, 332 + }; 333 + 334 + static int test_cxl_validate_translation_params(void) 335 + { 336 + int i, rc, failures = 0; 337 + bool valid; 338 + 339 + for (i = 0; i < ARRAY_SIZE(param_tests); i++) { 340 + struct param_test *t = &param_tests[i]; 341 + 342 + rc = cxl_validate_translation_params(t->eiw, t->eig, t->pos); 343 + valid = (rc == 0); 344 + 345 + if (valid != t->expect) { 346 + pr_err("test params failed: %s\n", t->desc); 347 + failures++; 348 + } 349 + } 350 + pr_info("..... test params: PASS %d FAIL %d\n", i - failures, failures); 351 + 352 + if (failures) 353 + return -EINVAL; 354 + 355 + return 0; 356 + } 357 + 358 + /* 359 + * cxl_translate_init 360 + * 361 + * Run the internal validation tests when no params are passed. 362 + * Otherwise, parse the parameters (test vectors), and kick off 363 + * the translation test. 364 + * 365 + * Returns: 0 on success, negative error code on failure 366 + */ 367 + static int __init cxl_translate_init(void) 368 + { 369 + int rc, i; 370 + 371 + /* If no tables are passed, validate module params only */ 372 + if (table_num == 0) { 373 + pr_info("Internal validation test start...\n"); 374 + rc = test_cxl_validate_translation_params(); 375 + if (rc) 376 + return rc; 377 + 378 + rc = test_random_params(); 379 + if (rc) 380 + return rc; 381 + 382 + pr_info("Internal validation test completed successfully\n"); 383 + 384 + return 0; 385 + } 386 + 387 + pr_info("CXL translate test module loaded with %d test vectors\n", 388 + table_num); 389 + 390 + rc = setup_xor_mapping(); 391 + if (rc) 392 + return rc; 393 + 394 + /* Process each test vector */ 395 + for (i = 0; i < table_num; i++) { 396 + u64 dpa, expect_spa; 397 + int pos, math; 398 + u8 r_eiw, hb_ways; 399 + u16 r_eig; 400 + 401 + pr_debug("Processing test vector %d: '%s'\n", i, table[i]); 402 + 403 + /* Parse the test vector */ 404 + rc = parse_test_vector(table[i], &dpa, &pos, &r_eiw, &r_eig, 405 + &hb_ways, &math, &expect_spa); 406 + if (rc) { 407 + pr_err("CXL Translate Test %d: FAIL\n" 408 + " Failed to parse test vector '%s'\n", 409 + i, table[i]); 410 + continue; 411 + } 412 + /* Run the translation test */ 413 + rc = run_translation_test(dpa, pos, r_eiw, r_eig, hb_ways, math, 414 + expect_spa); 415 + if (rc) { 416 + pr_err("CXL Translate Test %d: FAIL\n" 417 + " dpa=%llu pos=%d r_eiw=%u r_eig=%u hb_ways=%u math=%s expect_spa=%llu\n", 418 + i, dpa, pos, r_eiw, r_eig, hb_ways, 419 + (math == XOR_MATH) ? "XOR" : "MODULO", 420 + expect_spa); 421 + } else { 422 + pr_info("CXL Translate Test %d: PASS\n", i); 423 + } 424 + } 425 + 426 + kfree(cximsd); 427 + pr_info("CXL translate test completed\n"); 428 + 429 + return 0; 430 + } 431 + 432 + static void __exit cxl_translate_exit(void) 433 + { 434 + pr_info("CXL translate test module unloaded\n"); 435 + } 436 + 437 + module_param_array(table, charp, &table_num, 0444); 438 + MODULE_PARM_DESC(table, "Test vectors as space-separated decimal strings"); 439 + 440 + MODULE_LICENSE("GPL"); 441 + MODULE_DESCRIPTION("cxl_test: cxl address translation test module"); 442 + MODULE_IMPORT_NS("CXL"); 443 + 444 + module_init(cxl_translate_init); 445 + module_exit(cxl_translate_exit);