Skip to content

Commit 87c6967

Browse files
committed
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.
2 parents 2be5754 + 06377c5 commit 87c6967

5 files changed

Lines changed: 637 additions & 71 deletions

File tree

drivers/cxl/acpi.c

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,36 @@
1111
#include "cxlpci.h"
1212
#include "cxl.h"
1313

14-
struct cxl_cxims_data {
15-
int nr_maps;
16-
u64 xormaps[] __counted_by(nr_maps);
17-
};
18-
1914
static const guid_t acpi_cxl_qtg_id_guid =
2015
GUID_INIT(0xF365F9A6, 0xA7DE, 0x4071,
2116
0xA6, 0x6A, 0xB4, 0x0C, 0x0B, 0x4F, 0x8E, 0x52);
2217

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)
2426
{
25-
struct cxl_cxims_data *cximsd = cxlrd->platform_data;
26-
int hbiw = cxlrd->cxlsd.nr_targets;
27+
int nr_maps_to_apply = -1;
2728
u64 val;
2829
int pos;
2930

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;
3344

3445
/*
3546
* In regions using XOR interleave arithmetic the CXL HPA may not
@@ -60,6 +71,14 @@ static u64 cxl_apply_xor_maps(struct cxl_root_decoder *cxlrd, u64 addr)
6071

6172
return addr;
6273
}
74+
EXPORT_SYMBOL_FOR_MODULES(cxl_do_xormap_calc, "cxl_translate");
75+
76+
static u64 cxl_apply_xor_maps(struct cxl_root_decoder *cxlrd, u64 addr)
77+
{
78+
struct cxl_cxims_data *cximsd = cxlrd->platform_data;
79+
80+
return cxl_do_xormap_calc(cximsd, addr, cxlrd->cxlsd.nr_targets);
81+
}
6382

6483
struct cxl_cxims_context {
6584
struct device *dev;

drivers/cxl/core/region.c

Lines changed: 142 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -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++) {

drivers/cxl/cxl.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,25 @@ static inline bool is_cxl_root(struct cxl_port *port)
746746
return port->uport_dev == port->dev.parent;
747747
}
748748

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+
749768
int cxl_num_decoders_committed(struct cxl_port *port);
750769
bool is_cxl_port(const struct device *dev);
751770
struct cxl_port *to_cxl_port(const struct device *dev);

tools/testing/cxl/test/Kbuild

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ ccflags-y := -I$(srctree)/drivers/cxl/ -I$(srctree)/drivers/cxl/core
44
obj-m += cxl_test.o
55
obj-m += cxl_mock.o
66
obj-m += cxl_mock_mem.o
7+
obj-m += cxl_translate.o
78

89
cxl_test-y := cxl.o
910
cxl_mock-y := mock.o

0 commit comments

Comments
 (0)