@@ -2953,28 +2953,119 @@ static bool has_spa_to_hpa(struct cxl_root_decoder *cxlrd)
29532953 return cxlrd -> ops && cxlrd -> ops -> spa_to_hpa ;
29542954}
29552955
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 )
29582968{
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 ;
29662970
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 ;
2974+ }
2975+ if (eig_to_granularity (eig , & gran )) {
2976+ pr_debug ("%s: invalid eig=%u\n" , __func__ , eig );
2977+ return - EINVAL ;
29712978 }
2972- if (!cxled || cxlmd != cxled_to_memdev (cxled ))
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 )
29732996 return ULLONG_MAX ;
29742997
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 ;
29783069
29793070 /*
29803071 * The device position in the region interleave set was removed
@@ -2986,9 +3077,6 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
29863077 * 8.2.4.19.13 Implementation Note: Device Decode Logic
29873078 */
29883079
2989- /* Remove the dpa base */
2990- dpa_offset = dpa - cxl_dpa_resource_start (cxled );
2991-
29923080 mask_upper = GENMASK_ULL (51 , eig + 8 );
29933081
29943082 if (eiw < 8 ) {
@@ -3003,6 +3091,37 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
30033091 /* The lower bits remain unchanged */
30043092 hpa_offset |= dpa_offset & GENMASK_ULL (eig + 7 , 0 );
30053093
3094+ return hpa_offset ;
3095+ }
3096+ EXPORT_SYMBOL_FOR_MODULES (cxl_calculate_hpa_offset , "cxl_translate" );
3097+
3098+ u64 cxl_dpa_to_hpa (struct cxl_region * cxlr , const struct cxl_memdev * cxlmd ,
3099+ u64 dpa )
3100+ {
3101+ struct cxl_root_decoder * cxlrd = to_cxl_root_decoder (cxlr -> dev .parent );
3102+ struct cxl_region_params * p = & cxlr -> params ;
3103+ struct cxl_endpoint_decoder * cxled = NULL ;
3104+ u64 dpa_offset , hpa_offset , hpa ;
3105+ u16 eig = 0 ;
3106+ u8 eiw = 0 ;
3107+ int pos ;
3108+
3109+ for (int i = 0 ; i < p -> nr_targets ; i ++ ) {
3110+ if (cxlmd == cxled_to_memdev (p -> targets [i ])) {
3111+ cxled = p -> targets [i ];
3112+ break ;
3113+ }
3114+ }
3115+ if (!cxled )
3116+ return ULLONG_MAX ;
3117+
3118+ pos = cxled -> pos ;
3119+ ways_to_eiw (p -> interleave_ways , & eiw );
3120+ granularity_to_eig (p -> interleave_granularity , & eig );
3121+
3122+ dpa_offset = dpa - cxl_dpa_resource_start (cxled );
3123+ hpa_offset = cxl_calculate_hpa_offset (dpa_offset , pos , eiw , eig );
3124+
30063125 /* Apply the hpa_offset to the region base address */
30073126 hpa = hpa_offset + p -> res -> start + p -> cache_size ;
30083127
@@ -3035,8 +3154,6 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
30353154 struct cxl_root_decoder * cxlrd = to_cxl_root_decoder (cxlr -> dev .parent );
30363155 struct cxl_endpoint_decoder * cxled ;
30373156 u64 hpa , hpa_offset , dpa_offset ;
3038- u64 bits_upper , bits_lower ;
3039- u64 shifted , rem , temp ;
30403157 u16 eig = 0 ;
30413158 u8 eiw = 0 ;
30423159 int pos ;
@@ -3058,50 +3175,15 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
30583175 } else {
30593176 hpa_offset = offset ;
30603177 }
3061- /*
3062- * Interleave position: CXL Spec 3.2 Section 8.2.4.20.13
3063- * eiw < 8
3064- * Position is in the IW bits at HPA_OFFSET[IG+8+IW-1:IG+8].
3065- * Per spec "remove IW bits starting with bit position IG+8"
3066- * eiw >= 8
3067- * Position is not explicitly stored in HPA_OFFSET bits. It is
3068- * derived from the modulo operation of the upper bits using
3069- * the total number of interleave ways.
3070- */
3071- if (eiw < 8 ) {
3072- pos = (hpa_offset >> (eig + 8 )) & GENMASK (eiw - 1 , 0 );
3073- } else {
3074- shifted = hpa_offset >> (eig + 8 );
3075- div64_u64_rem (shifted , p -> interleave_ways , & rem );
3076- pos = rem ;
3077- }
3178+
3179+ pos = cxl_calculate_position (hpa_offset , eiw , eig );
30783180 if (pos < 0 || pos >= p -> nr_targets ) {
30793181 dev_dbg (& cxlr -> dev , "Invalid position %d for %d targets\n" ,
30803182 pos , p -> nr_targets );
30813183 return - ENXIO ;
30823184 }
30833185
3084- /*
3085- * DPA offset: CXL Spec 3.2 Section 8.2.4.20.13
3086- * Lower bits [IG+7:0] pass through unchanged
3087- * (eiw < 8)
3088- * Per spec: DPAOffset[51:IG+8] = (HPAOffset[51:IG+IW+8] >> IW)
3089- * Clear the position bits to isolate upper section, then
3090- * reverse the left shift by eiw that occurred during DPA->HPA
3091- * (eiw >= 8)
3092- * Per spec: DPAOffset[51:IG+8] = HPAOffset[51:IG+IW] / 3
3093- * Extract upper bits from the correct bit range and divide by 3
3094- * to recover the original DPA upper bits
3095- */
3096- bits_lower = hpa_offset & GENMASK_ULL (eig + 7 , 0 );
3097- if (eiw < 8 ) {
3098- temp = hpa_offset &= ~((u64 )GENMASK (eig + eiw + 8 - 1 , 0 ));
3099- dpa_offset = temp >> eiw ;
3100- } else {
3101- bits_upper = div64_u64 (hpa_offset >> (eig + eiw ), 3 );
3102- dpa_offset = bits_upper << (eig + 8 );
3103- }
3104- dpa_offset |= bits_lower ;
3186+ dpa_offset = cxl_calculate_dpa_offset (hpa_offset , eiw , eig );
31053187
31063188 /* Look-up and return the result: a memdev and a DPA */
31073189 for (int i = 0 ; i < p -> nr_targets ; i ++ ) {
0 commit comments