cxl/region: Refactor address translation funcs for testing

In preparation for adding a test module that exercises the address
translation calculations, extract the core calculations into stand-
alone functions that operate on base parameters without dependencies
on struct cxl_region.

Perform additional parameter validation to protect against a test
module sending bad parameters. Export the validation function, as
well as the three core translation functions for use by test module
cxl_translate only.

This refactoring enables unit testing of the address translation logic
with controlled inputs, while preserving identical functionality in
the existing code paths.

Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Alison Schofield <alison.schofield@intel.com>
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
This commit is contained in:
Alison Schofield 2025-10-14 01:24:30 -07:00 committed by Dave Jiang
parent 6146a0f1df
commit b78b9e7b79
2 changed files with 148 additions and 60 deletions

View File

@ -2934,28 +2934,119 @@ static bool has_spa_to_hpa(struct cxl_root_decoder *cxlrd)
return cxlrd->ops && cxlrd->ops->spa_to_hpa; return cxlrd->ops && cxlrd->ops->spa_to_hpa;
} }
u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd, #define CXL_POS_ZERO 0
u64 dpa) /**
* cxl_validate_translation_params
* @eiw: encoded interleave ways
* @eig: encoded interleave granularity
* @pos: position in interleave
*
* Callers pass CXL_POS_ZERO when no position parameter needs validating.
*
* Returns: 0 on success, -EINVAL on first invalid parameter
*/
int cxl_validate_translation_params(u8 eiw, u16 eig, int pos)
{ {
struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent); int ways, gran;
u64 dpa_offset, hpa_offset, bits_upper, mask_upper, hpa;
struct cxl_region_params *p = &cxlr->params;
struct cxl_endpoint_decoder *cxled = NULL;
u16 eig = 0;
u8 eiw = 0;
int pos;
for (int i = 0; i < p->nr_targets; i++) { if (eiw_to_ways(eiw, &ways)) {
cxled = p->targets[i]; pr_debug("%s: invalid eiw=%u\n", __func__, eiw);
if (cxlmd == cxled_to_memdev(cxled)) return -EINVAL;
break;
} }
if (!cxled || cxlmd != cxled_to_memdev(cxled)) if (eig_to_granularity(eig, &gran)) {
pr_debug("%s: invalid eig=%u\n", __func__, eig);
return -EINVAL;
}
if (pos < 0 || pos >= ways) {
pr_debug("%s: invalid pos=%d for ways=%u\n", __func__, pos,
ways);
return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL_FOR_MODULES(cxl_validate_translation_params, "cxl_translate");
u64 cxl_calculate_dpa_offset(u64 hpa_offset, u8 eiw, u16 eig)
{
u64 dpa_offset, bits_lower, bits_upper, temp;
int ret;
ret = cxl_validate_translation_params(eiw, eig, CXL_POS_ZERO);
if (ret)
return ULLONG_MAX; return ULLONG_MAX;
pos = cxled->pos; /*
ways_to_eiw(p->interleave_ways, &eiw); * DPA offset: CXL Spec 3.2 Section 8.2.4.20.13
granularity_to_eig(p->interleave_granularity, &eig); * Lower bits [IG+7:0] pass through unchanged
* (eiw < 8)
* Per spec: DPAOffset[51:IG+8] = (HPAOffset[51:IG+IW+8] >> IW)
* Clear the position bits to isolate upper section, then
* reverse the left shift by eiw that occurred during DPA->HPA
* (eiw >= 8)
* Per spec: DPAOffset[51:IG+8] = HPAOffset[51:IG+IW] / 3
* Extract upper bits from the correct bit range and divide by 3
* to recover the original DPA upper bits
*/
bits_lower = hpa_offset & GENMASK_ULL(eig + 7, 0);
if (eiw < 8) {
temp = hpa_offset &= ~GENMASK_ULL(eig + eiw + 8 - 1, 0);
dpa_offset = temp >> eiw;
} else {
bits_upper = div64_u64(hpa_offset >> (eig + eiw), 3);
dpa_offset = bits_upper << (eig + 8);
}
dpa_offset |= bits_lower;
return dpa_offset;
}
EXPORT_SYMBOL_FOR_MODULES(cxl_calculate_dpa_offset, "cxl_translate");
int cxl_calculate_position(u64 hpa_offset, u8 eiw, u16 eig)
{
unsigned int ways = 0;
u64 shifted, rem;
int pos, ret;
ret = cxl_validate_translation_params(eiw, eig, CXL_POS_ZERO);
if (ret)
return ret;
if (!eiw)
/* position is 0 if no interleaving */
return 0;
/*
* Interleave position: CXL Spec 3.2 Section 8.2.4.20.13
* eiw < 8
* Position is in the IW bits at HPA_OFFSET[IG+8+IW-1:IG+8].
* Per spec "remove IW bits starting with bit position IG+8"
* eiw >= 8
* Position is not explicitly stored in HPA_OFFSET bits. It is
* derived from the modulo operation of the upper bits using
* the total number of interleave ways.
*/
if (eiw < 8) {
pos = (hpa_offset >> (eig + 8)) & GENMASK(eiw - 1, 0);
} else {
shifted = hpa_offset >> (eig + 8);
eiw_to_ways(eiw, &ways);
div64_u64_rem(shifted, ways, &rem);
pos = rem;
}
return pos;
}
EXPORT_SYMBOL_FOR_MODULES(cxl_calculate_position, "cxl_translate");
u64 cxl_calculate_hpa_offset(u64 dpa_offset, int pos, u8 eiw, u16 eig)
{
u64 mask_upper, hpa_offset, bits_upper;
int ret;
ret = cxl_validate_translation_params(eiw, eig, pos);
if (ret)
return ULLONG_MAX;
/* /*
* The device position in the region interleave set was removed * The device position in the region interleave set was removed
@ -2967,9 +3058,6 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
* 8.2.4.19.13 Implementation Note: Device Decode Logic * 8.2.4.19.13 Implementation Note: Device Decode Logic
*/ */
/* Remove the dpa base */
dpa_offset = dpa - cxl_dpa_resource_start(cxled);
mask_upper = GENMASK_ULL(51, eig + 8); mask_upper = GENMASK_ULL(51, eig + 8);
if (eiw < 8) { if (eiw < 8) {
@ -2984,6 +3072,37 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
/* The lower bits remain unchanged */ /* The lower bits remain unchanged */
hpa_offset |= dpa_offset & GENMASK_ULL(eig + 7, 0); hpa_offset |= dpa_offset & GENMASK_ULL(eig + 7, 0);
return hpa_offset;
}
EXPORT_SYMBOL_FOR_MODULES(cxl_calculate_hpa_offset, "cxl_translate");
u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
u64 dpa)
{
struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
struct cxl_region_params *p = &cxlr->params;
struct cxl_endpoint_decoder *cxled = NULL;
u64 dpa_offset, hpa_offset, hpa;
u16 eig = 0;
u8 eiw = 0;
int pos;
for (int i = 0; i < p->nr_targets; i++) {
if (cxlmd == cxled_to_memdev(p->targets[i])) {
cxled = p->targets[i];
break;
}
}
if (!cxled)
return ULLONG_MAX;
pos = cxled->pos;
ways_to_eiw(p->interleave_ways, &eiw);
granularity_to_eig(p->interleave_granularity, &eig);
dpa_offset = dpa - cxl_dpa_resource_start(cxled);
hpa_offset = cxl_calculate_hpa_offset(dpa_offset, pos, eiw, eig);
/* Apply the hpa_offset to the region base address */ /* Apply the hpa_offset to the region base address */
hpa = hpa_offset + p->res->start + p->cache_size; hpa = hpa_offset + p->res->start + p->cache_size;
@ -3016,8 +3135,6 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent); struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
struct cxl_endpoint_decoder *cxled; struct cxl_endpoint_decoder *cxled;
u64 hpa, hpa_offset, dpa_offset; u64 hpa, hpa_offset, dpa_offset;
u64 bits_upper, bits_lower;
u64 shifted, rem, temp;
u16 eig = 0; u16 eig = 0;
u8 eiw = 0; u8 eiw = 0;
int pos; int pos;
@ -3039,50 +3156,15 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
} else { } else {
hpa_offset = offset; hpa_offset = offset;
} }
/*
* Interleave position: CXL Spec 3.2 Section 8.2.4.20.13 pos = cxl_calculate_position(hpa_offset, eiw, eig);
* eiw < 8
* Position is in the IW bits at HPA_OFFSET[IG+8+IW-1:IG+8].
* Per spec "remove IW bits starting with bit position IG+8"
* eiw >= 8
* Position is not explicitly stored in HPA_OFFSET bits. It is
* derived from the modulo operation of the upper bits using
* the total number of interleave ways.
*/
if (eiw < 8) {
pos = (hpa_offset >> (eig + 8)) & GENMASK(eiw - 1, 0);
} else {
shifted = hpa_offset >> (eig + 8);
div64_u64_rem(shifted, p->interleave_ways, &rem);
pos = rem;
}
if (pos < 0 || pos >= p->nr_targets) { if (pos < 0 || pos >= p->nr_targets) {
dev_dbg(&cxlr->dev, "Invalid position %d for %d targets\n", dev_dbg(&cxlr->dev, "Invalid position %d for %d targets\n",
pos, p->nr_targets); pos, p->nr_targets);
return -ENXIO; return -ENXIO;
} }
/* dpa_offset = cxl_calculate_dpa_offset(hpa_offset, eiw, eig);
* DPA offset: CXL Spec 3.2 Section 8.2.4.20.13
* Lower bits [IG+7:0] pass through unchanged
* (eiw < 8)
* Per spec: DPAOffset[51:IG+8] = (HPAOffset[51:IG+IW+8] >> IW)
* Clear the position bits to isolate upper section, then
* reverse the left shift by eiw that occurred during DPA->HPA
* (eiw >= 8)
* Per spec: DPAOffset[51:IG+8] = HPAOffset[51:IG+IW] / 3
* Extract upper bits from the correct bit range and divide by 3
* to recover the original DPA upper bits
*/
bits_lower = hpa_offset & GENMASK_ULL(eig + 7, 0);
if (eiw < 8) {
temp = hpa_offset &= ~((u64)GENMASK(eig + eiw + 8 - 1, 0));
dpa_offset = temp >> eiw;
} else {
bits_upper = div64_u64(hpa_offset >> (eig + eiw), 3);
dpa_offset = bits_upper << (eig + 8);
}
dpa_offset |= bits_lower;
/* Look-up and return the result: a memdev and a DPA */ /* Look-up and return the result: a memdev and a DPA */
for (int i = 0; i < p->nr_targets; i++) { for (int i = 0; i < p->nr_targets; i++) {

View File

@ -738,6 +738,12 @@ static inline bool is_cxl_root(struct cxl_port *port)
return port->uport_dev == port->dev.parent; return port->uport_dev == port->dev.parent;
} }
/* Address translation functions exported to cxl_translate test module only */
int cxl_validate_translation_params(u8 eiw, u16 eig, int pos);
u64 cxl_calculate_hpa_offset(u64 dpa_offset, int pos, u8 eiw, u16 eig);
u64 cxl_calculate_dpa_offset(u64 hpa_offset, u8 eiw, u16 eig);
int cxl_calculate_position(u64 hpa_offset, u8 eiw, u16 eig);
int cxl_num_decoders_committed(struct cxl_port *port); int cxl_num_decoders_committed(struct cxl_port *port);
bool is_cxl_port(const struct device *dev); bool is_cxl_port(const struct device *dev);
struct cxl_port *to_cxl_port(const struct device *dev); struct cxl_port *to_cxl_port(const struct device *dev);