mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
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.
This commit is contained in:
@@ -11,25 +11,36 @@
|
||||
#include "cxlpci.h"
|
||||
#include "cxl.h"
|
||||
|
||||
struct cxl_cxims_data {
|
||||
int nr_maps;
|
||||
u64 xormaps[] __counted_by(nr_maps);
|
||||
};
|
||||
|
||||
static const guid_t acpi_cxl_qtg_id_guid =
|
||||
GUID_INIT(0xF365F9A6, 0xA7DE, 0x4071,
|
||||
0xA6, 0x6A, 0xB4, 0x0C, 0x0B, 0x4F, 0x8E, 0x52);
|
||||
|
||||
static u64 cxl_apply_xor_maps(struct cxl_root_decoder *cxlrd, u64 addr)
|
||||
#define HBIW_TO_NR_MAPS_SIZE (CXL_DECODER_MAX_INTERLEAVE + 1)
|
||||
static const int hbiw_to_nr_maps[HBIW_TO_NR_MAPS_SIZE] = {
|
||||
[1] = 0, [2] = 1, [3] = 0, [4] = 2, [6] = 1, [8] = 3, [12] = 2, [16] = 4
|
||||
};
|
||||
|
||||
static const int valid_hbiw[] = { 1, 2, 3, 4, 6, 8, 12, 16 };
|
||||
|
||||
u64 cxl_do_xormap_calc(struct cxl_cxims_data *cximsd, u64 addr, int hbiw)
|
||||
{
|
||||
struct cxl_cxims_data *cximsd = cxlrd->platform_data;
|
||||
int hbiw = cxlrd->cxlsd.nr_targets;
|
||||
int nr_maps_to_apply = -1;
|
||||
u64 val;
|
||||
int pos;
|
||||
|
||||
/* No xormaps for host bridge interleave ways of 1 or 3 */
|
||||
if (hbiw == 1 || hbiw == 3)
|
||||
return addr;
|
||||
/*
|
||||
* Strictly validate hbiw since this function is used for testing and
|
||||
* that nullifies any expectation of trusted parameters from the CXL
|
||||
* Region Driver.
|
||||
*/
|
||||
for (int i = 0; i < ARRAY_SIZE(valid_hbiw); i++) {
|
||||
if (valid_hbiw[i] == hbiw) {
|
||||
nr_maps_to_apply = hbiw_to_nr_maps[hbiw];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (nr_maps_to_apply == -1 || nr_maps_to_apply > cximsd->nr_maps)
|
||||
return ULLONG_MAX;
|
||||
|
||||
/*
|
||||
* 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)
|
||||
|
||||
return addr;
|
||||
}
|
||||
EXPORT_SYMBOL_FOR_MODULES(cxl_do_xormap_calc, "cxl_translate");
|
||||
|
||||
static u64 cxl_apply_xor_maps(struct cxl_root_decoder *cxlrd, u64 addr)
|
||||
{
|
||||
struct cxl_cxims_data *cximsd = cxlrd->platform_data;
|
||||
|
||||
return cxl_do_xormap_calc(cximsd, addr, cxlrd->cxlsd.nr_targets);
|
||||
}
|
||||
|
||||
struct cxl_cxims_context {
|
||||
struct device *dev;
|
||||
|
||||
@@ -2953,28 +2953,119 @@ static bool has_spa_to_hpa(struct cxl_root_decoder *cxlrd)
|
||||
return cxlrd->ops && cxlrd->ops->spa_to_hpa;
|
||||
}
|
||||
|
||||
u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
|
||||
u64 dpa)
|
||||
#define CXL_POS_ZERO 0
|
||||
/**
|
||||
* 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);
|
||||
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;
|
||||
int ways, gran;
|
||||
|
||||
for (int i = 0; i < p->nr_targets; i++) {
|
||||
cxled = p->targets[i];
|
||||
if (cxlmd == cxled_to_memdev(cxled))
|
||||
break;
|
||||
if (eiw_to_ways(eiw, &ways)) {
|
||||
pr_debug("%s: invalid eiw=%u\n", __func__, eiw);
|
||||
return -EINVAL;
|
||||
}
|
||||
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;
|
||||
|
||||
pos = cxled->pos;
|
||||
ways_to_eiw(p->interleave_ways, &eiw);
|
||||
granularity_to_eig(p->interleave_granularity, &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 &= ~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
|
||||
@@ -2986,9 +3077,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
|
||||
*/
|
||||
|
||||
/* Remove the dpa base */
|
||||
dpa_offset = dpa - cxl_dpa_resource_start(cxled);
|
||||
|
||||
mask_upper = GENMASK_ULL(51, eig + 8);
|
||||
|
||||
if (eiw < 8) {
|
||||
@@ -3003,6 +3091,37 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
|
||||
/* The lower bits remain unchanged */
|
||||
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 */
|
||||
hpa = hpa_offset + p->res->start + p->cache_size;
|
||||
|
||||
@@ -3035,8 +3154,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_endpoint_decoder *cxled;
|
||||
u64 hpa, hpa_offset, dpa_offset;
|
||||
u64 bits_upper, bits_lower;
|
||||
u64 shifted, rem, temp;
|
||||
u16 eig = 0;
|
||||
u8 eiw = 0;
|
||||
int pos;
|
||||
@@ -3058,50 +3175,15 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
|
||||
} else {
|
||||
hpa_offset = offset;
|
||||
}
|
||||
/*
|
||||
* 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);
|
||||
div64_u64_rem(shifted, p->interleave_ways, &rem);
|
||||
pos = rem;
|
||||
}
|
||||
|
||||
pos = cxl_calculate_position(hpa_offset, eiw, eig);
|
||||
if (pos < 0 || pos >= p->nr_targets) {
|
||||
dev_dbg(&cxlr->dev, "Invalid position %d for %d targets\n",
|
||||
pos, p->nr_targets);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
dpa_offset = cxl_calculate_dpa_offset(hpa_offset, eiw, eig);
|
||||
|
||||
/* Look-up and return the result: a memdev and a DPA */
|
||||
for (int i = 0; i < p->nr_targets; i++) {
|
||||
|
||||
@@ -746,6 +746,25 @@ static inline bool is_cxl_root(struct cxl_port *port)
|
||||
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);
|
||||
struct cxl_cxims_data {
|
||||
int nr_maps;
|
||||
u64 xormaps[] __counted_by(nr_maps);
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_CXL_ACPI)
|
||||
u64 cxl_do_xormap_calc(struct cxl_cxims_data *cximsd, u64 addr, int hbiw);
|
||||
#else
|
||||
static inline u64 cxl_do_xormap_calc(struct cxl_cxims_data *cximsd, u64 addr, int hbiw)
|
||||
{
|
||||
return ULLONG_MAX;
|
||||
}
|
||||
#endif
|
||||
|
||||
int cxl_num_decoders_committed(struct cxl_port *port);
|
||||
bool is_cxl_port(const struct device *dev);
|
||||
struct cxl_port *to_cxl_port(const struct device *dev);
|
||||
|
||||
@@ -4,6 +4,7 @@ ccflags-y := -I$(srctree)/drivers/cxl/ -I$(srctree)/drivers/cxl/core
|
||||
obj-m += cxl_test.o
|
||||
obj-m += cxl_mock.o
|
||||
obj-m += cxl_mock_mem.o
|
||||
obj-m += cxl_translate.o
|
||||
|
||||
cxl_test-y := cxl.o
|
||||
cxl_mock-y := mock.o
|
||||
|
||||
445
tools/testing/cxl/test/cxl_translate.c
Normal file
445
tools/testing/cxl/test/cxl_translate.c
Normal file
@@ -0,0 +1,445 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
// Copyright(c) 2025 Intel Corporation. All rights reserved.
|
||||
|
||||
/* Preface all log entries with "cxl_translate" */
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <cxlmem.h>
|
||||
#include <cxl.h>
|
||||
|
||||
/* Maximum number of test vectors and entry length */
|
||||
#define MAX_TABLE_ENTRIES 128
|
||||
#define MAX_ENTRY_LEN 128
|
||||
|
||||
/* Expected number of parameters in each test vector */
|
||||
#define EXPECTED_PARAMS 7
|
||||
|
||||
/* Module parameters for test vectors */
|
||||
static char *table[MAX_TABLE_ENTRIES];
|
||||
static int table_num;
|
||||
|
||||
/* Interleave Arithmetic */
|
||||
#define MODULO_MATH 0
|
||||
#define XOR_MATH 1
|
||||
|
||||
/*
|
||||
* XOR mapping configuration
|
||||
* The test data sets all use the same set of xormaps. When additional
|
||||
* data sets arrive for validation, this static setup will need to
|
||||
* be changed to accept xormaps as additional parameters.
|
||||
*/
|
||||
struct cxl_cxims_data *cximsd;
|
||||
static u64 xormaps[] = {
|
||||
0x2020900,
|
||||
0x4041200,
|
||||
0x1010400,
|
||||
0x800,
|
||||
};
|
||||
|
||||
static int nr_maps = ARRAY_SIZE(xormaps);
|
||||
|
||||
#define HBIW_TO_NR_MAPS_SIZE (CXL_DECODER_MAX_INTERLEAVE + 1)
|
||||
static const int hbiw_to_nr_maps[HBIW_TO_NR_MAPS_SIZE] = {
|
||||
[1] = 0, [2] = 1, [3] = 0, [4] = 2, [6] = 1, [8] = 3, [12] = 2, [16] = 4
|
||||
};
|
||||
|
||||
/**
|
||||
* to_hpa - calculate an HPA offset from a DPA offset and position
|
||||
*
|
||||
* dpa_offset: device physical address offset
|
||||
* pos: devices position in interleave
|
||||
* r_eiw: region encoded interleave ways
|
||||
* r_eig: region encoded interleave granularity
|
||||
* hb_ways: host bridge interleave ways
|
||||
* math: interleave arithmetic (MODULO_MATH or XOR_MATH)
|
||||
*
|
||||
* Returns: host physical address offset
|
||||
*/
|
||||
static u64 to_hpa(u64 dpa_offset, int pos, u8 r_eiw, u16 r_eig, u8 hb_ways,
|
||||
u8 math)
|
||||
{
|
||||
u64 hpa_offset;
|
||||
|
||||
/* Calculate base HPA offset from DPA and position */
|
||||
hpa_offset = cxl_calculate_hpa_offset(dpa_offset, pos, r_eiw, r_eig);
|
||||
|
||||
if (math == XOR_MATH) {
|
||||
cximsd->nr_maps = hbiw_to_nr_maps[hb_ways];
|
||||
if (cximsd->nr_maps)
|
||||
return cxl_do_xormap_calc(cximsd, hpa_offset, hb_ways);
|
||||
}
|
||||
return hpa_offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* to_dpa - translate an HPA offset to DPA offset
|
||||
*
|
||||
* hpa_offset: host physical address offset
|
||||
* r_eiw: region encoded interleave ways
|
||||
* r_eig: region encoded interleave granularity
|
||||
* hb_ways: host bridge interleave ways
|
||||
* math: interleave arithmetic (MODULO_MATH or XOR_MATH)
|
||||
*
|
||||
* Returns: device physical address offset
|
||||
*/
|
||||
static u64 to_dpa(u64 hpa_offset, u8 r_eiw, u16 r_eig, u8 hb_ways, u8 math)
|
||||
{
|
||||
u64 offset = hpa_offset;
|
||||
|
||||
if (math == XOR_MATH) {
|
||||
cximsd->nr_maps = hbiw_to_nr_maps[hb_ways];
|
||||
if (cximsd->nr_maps)
|
||||
offset =
|
||||
cxl_do_xormap_calc(cximsd, hpa_offset, hb_ways);
|
||||
}
|
||||
return cxl_calculate_dpa_offset(offset, r_eiw, r_eig);
|
||||
}
|
||||
|
||||
/**
|
||||
* to_pos - extract an interleave position from an HPA offset
|
||||
*
|
||||
* hpa_offset: host physical address offset
|
||||
* r_eiw: region encoded interleave ways
|
||||
* r_eig: region encoded interleave granularity
|
||||
* hb_ways: host bridge interleave ways
|
||||
* math: interleave arithmetic (MODULO_MATH or XOR_MATH)
|
||||
*
|
||||
* Returns: devices position in region interleave
|
||||
*/
|
||||
static u64 to_pos(u64 hpa_offset, u8 r_eiw, u16 r_eig, u8 hb_ways, u8 math)
|
||||
{
|
||||
u64 offset = hpa_offset;
|
||||
|
||||
/* Reverse XOR mapping if specified */
|
||||
if (math == XOR_MATH)
|
||||
offset = cxl_do_xormap_calc(cximsd, hpa_offset, hb_ways);
|
||||
|
||||
return cxl_calculate_position(offset, r_eiw, r_eig);
|
||||
}
|
||||
|
||||
/**
|
||||
* run_translation_test - execute forward and reverse translations
|
||||
*
|
||||
* @dpa: device physical address
|
||||
* @pos: expected position in region interleave
|
||||
* @r_eiw: region encoded interleave ways
|
||||
* @r_eig: region encoded interleave granularity
|
||||
* @hb_ways: host bridge interleave ways
|
||||
* @math: interleave arithmetic (MODULO_MATH or XOR_MATH)
|
||||
* @expect_spa: expected system physical address
|
||||
*
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
static int run_translation_test(u64 dpa, int pos, u8 r_eiw, u16 r_eig,
|
||||
u8 hb_ways, int math, u64 expect_hpa)
|
||||
{
|
||||
u64 translated_spa, reverse_dpa;
|
||||
int reverse_pos;
|
||||
|
||||
/* Test Device to Host translation: DPA + POS -> SPA */
|
||||
translated_spa = to_hpa(dpa, pos, r_eiw, r_eig, hb_ways, math);
|
||||
if (translated_spa != expect_hpa) {
|
||||
pr_err("Device to host failed: expected HPA %llu, got %llu\n",
|
||||
expect_hpa, translated_spa);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Test Host to Device DPA translation: SPA -> DPA */
|
||||
reverse_dpa = to_dpa(translated_spa, r_eiw, r_eig, hb_ways, math);
|
||||
if (reverse_dpa != dpa) {
|
||||
pr_err("Host to Device DPA failed: expected %llu, got %llu\n",
|
||||
dpa, reverse_dpa);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Test Host to Device Position translation: SPA -> POS */
|
||||
reverse_pos = to_pos(translated_spa, r_eiw, r_eig, hb_ways, math);
|
||||
if (reverse_pos != pos) {
|
||||
pr_err("Position lookup failed: expected %d, got %d\n", pos,
|
||||
reverse_pos);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* parse_test_vector - parse a single test vector string
|
||||
*
|
||||
* entry: test vector string to parse
|
||||
* dpa: device physical address
|
||||
* pos: expected position in region interleave
|
||||
* r_eiw: region encoded interleave ways
|
||||
* r_eig: region encoded interleave granularity
|
||||
* hb_ways: host bridge interleave ways
|
||||
* math: interleave arithmetic (MODULO_MATH or XOR_MATH)
|
||||
* expect_spa: expected system physical address
|
||||
*
|
||||
* Returns: 0 on success, negative error code on failure
|
||||
*/
|
||||
static int parse_test_vector(const char *entry, u64 *dpa, int *pos, u8 *r_eiw,
|
||||
u16 *r_eig, u8 *hb_ways, int *math,
|
||||
u64 *expect_hpa)
|
||||
{
|
||||
unsigned int tmp_r_eiw, tmp_r_eig, tmp_hb_ways;
|
||||
int parsed;
|
||||
|
||||
parsed = sscanf(entry, "%llu %d %u %u %u %d %llu", dpa, pos, &tmp_r_eiw,
|
||||
&tmp_r_eig, &tmp_hb_ways, math, expect_hpa);
|
||||
|
||||
if (parsed != EXPECTED_PARAMS) {
|
||||
pr_err("Parse error: expected %d parameters, got %d in '%s'\n",
|
||||
EXPECTED_PARAMS, parsed, entry);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (tmp_r_eiw > U8_MAX || tmp_r_eig > U16_MAX || tmp_hb_ways > U8_MAX) {
|
||||
pr_err("Parameter overflow in entry: '%s'\n", entry);
|
||||
return -ERANGE;
|
||||
}
|
||||
if (*math != MODULO_MATH && *math != XOR_MATH) {
|
||||
pr_err("Invalid math type %d in entry: '%s'\n", *math, entry);
|
||||
return -EINVAL;
|
||||
}
|
||||
*r_eiw = tmp_r_eiw;
|
||||
*r_eig = tmp_r_eig;
|
||||
*hb_ways = tmp_hb_ways;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* setup_xor_mapping - Initialize XOR mapping data structure
|
||||
*
|
||||
* The test data sets all use the same HBIG so we can use one set
|
||||
* of xormaps, and set the number to apply based on HBIW before
|
||||
* calling cxl_do_xormap_calc().
|
||||
*
|
||||
* When additional data sets arrive for validation with different
|
||||
* HBIG's this static setup will need to be updated.
|
||||
*
|
||||
* Returns: 0 on success, negative error code on failure
|
||||
*/
|
||||
static int setup_xor_mapping(void)
|
||||
{
|
||||
if (nr_maps <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
cximsd = kzalloc(struct_size(cximsd, xormaps, nr_maps), GFP_KERNEL);
|
||||
if (!cximsd)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(cximsd->xormaps, xormaps, nr_maps * sizeof(*cximsd->xormaps));
|
||||
cximsd->nr_maps = nr_maps;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_random_params(void)
|
||||
{
|
||||
u8 valid_eiws[] = { 0, 1, 2, 3, 4, 8, 9, 10 };
|
||||
u16 valid_eigs[] = { 0, 1, 2, 3, 4, 5, 6 };
|
||||
int i, ways, pos, reverse_pos;
|
||||
u64 dpa, hpa, reverse_dpa;
|
||||
int iterations = 10000;
|
||||
int failures = 0;
|
||||
|
||||
for (i = 0; i < iterations; i++) {
|
||||
/* Generate valid random parameters for eiw, eig, pos, dpa */
|
||||
u8 eiw = valid_eiws[get_random_u32() % ARRAY_SIZE(valid_eiws)];
|
||||
u16 eig = valid_eigs[get_random_u32() % ARRAY_SIZE(valid_eigs)];
|
||||
|
||||
eiw_to_ways(eiw, &ways);
|
||||
pos = get_random_u32() % ways;
|
||||
dpa = get_random_u64() >> 12;
|
||||
|
||||
hpa = cxl_calculate_hpa_offset(dpa, pos, eiw, eig);
|
||||
reverse_dpa = cxl_calculate_dpa_offset(hpa, eiw, eig);
|
||||
reverse_pos = cxl_calculate_position(hpa, eiw, eig);
|
||||
|
||||
if (reverse_dpa != dpa || reverse_pos != pos) {
|
||||
pr_err("test random iter %d FAIL hpa=%llu, dpa=%llu reverse_dpa=%llu, pos=%d reverse_pos=%d eiw=%u eig=%u\n",
|
||||
i, hpa, dpa, reverse_dpa, pos, reverse_pos, eiw,
|
||||
eig);
|
||||
|
||||
if (failures++ > 10) {
|
||||
pr_err("test random too many failures, stop\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
pr_info("..... test random: PASS %d FAIL %d\n", i - failures, failures);
|
||||
|
||||
if (failures)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct param_test {
|
||||
u8 eiw;
|
||||
u16 eig;
|
||||
int pos;
|
||||
bool expect; /* true: expect pass, false: expect fail */
|
||||
const char *desc;
|
||||
};
|
||||
|
||||
static struct param_test param_tests[] = {
|
||||
{ 0x0, 0, 0, true, "1-way, min eig=0, pos=0" },
|
||||
{ 0x0, 3, 0, true, "1-way, mid eig=3, pos=0" },
|
||||
{ 0x0, 6, 0, true, "1-way, max eig=6, pos=0" },
|
||||
{ 0x1, 0, 0, true, "2-way, eig=0, pos=0" },
|
||||
{ 0x1, 3, 1, true, "2-way, eig=3, max pos=1" },
|
||||
{ 0x1, 6, 1, true, "2-way, eig=6, max pos=1" },
|
||||
{ 0x2, 0, 0, true, "4-way, eig=0, pos=0" },
|
||||
{ 0x2, 3, 3, true, "4-way, eig=3, max pos=3" },
|
||||
{ 0x2, 6, 3, true, "4-way, eig=6, max pos=3" },
|
||||
{ 0x3, 0, 0, true, "8-way, eig=0, pos=0" },
|
||||
{ 0x3, 3, 7, true, "8-way, eig=3, max pos=7" },
|
||||
{ 0x3, 6, 7, true, "8-way, eig=6, max pos=7" },
|
||||
{ 0x4, 0, 0, true, "16-way, eig=0, pos=0" },
|
||||
{ 0x4, 3, 15, true, "16-way, eig=3, max pos=15" },
|
||||
{ 0x4, 6, 15, true, "16-way, eig=6, max pos=15" },
|
||||
{ 0x8, 0, 0, true, "3-way, eig=0, pos=0" },
|
||||
{ 0x8, 3, 2, true, "3-way, eig=3, max pos=2" },
|
||||
{ 0x8, 6, 2, true, "3-way, eig=6, max pos=2" },
|
||||
{ 0x9, 0, 0, true, "6-way, eig=0, pos=0" },
|
||||
{ 0x9, 3, 5, true, "6-way, eig=3, max pos=5" },
|
||||
{ 0x9, 6, 5, true, "6-way, eig=6, max pos=5" },
|
||||
{ 0xA, 0, 0, true, "12-way, eig=0, pos=0" },
|
||||
{ 0xA, 3, 11, true, "12-way, eig=3, max pos=11" },
|
||||
{ 0xA, 6, 11, true, "12-way, eig=6, max pos=11" },
|
||||
{ 0x5, 0, 0, false, "invalid eiw=5" },
|
||||
{ 0x7, 0, 0, false, "invalid eiw=7" },
|
||||
{ 0xB, 0, 0, false, "invalid eiw=0xB" },
|
||||
{ 0xFF, 0, 0, false, "invalid eiw=0xFF" },
|
||||
{ 0x1, 7, 0, false, "invalid eig=7 (out of range)" },
|
||||
{ 0x2, 0x10, 0, false, "invalid eig=0x10" },
|
||||
{ 0x3, 0xFFFF, 0, false, "invalid eig=0xFFFF" },
|
||||
{ 0x1, 0, -1, false, "pos < 0" },
|
||||
{ 0x1, 0, 2, false, "2-way, pos=2 (>= ways)" },
|
||||
{ 0x2, 0, 4, false, "4-way, pos=4 (>= ways)" },
|
||||
{ 0x3, 0, 8, false, "8-way, pos=8 (>= ways)" },
|
||||
{ 0x4, 0, 16, false, "16-way, pos=16 (>= ways)" },
|
||||
{ 0x8, 0, 3, false, "3-way, pos=3 (>= ways)" },
|
||||
{ 0x9, 0, 6, false, "6-way, pos=6 (>= ways)" },
|
||||
{ 0xA, 0, 12, false, "12-way, pos=12 (>= ways)" },
|
||||
};
|
||||
|
||||
static int test_cxl_validate_translation_params(void)
|
||||
{
|
||||
int i, rc, failures = 0;
|
||||
bool valid;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(param_tests); i++) {
|
||||
struct param_test *t = ¶m_tests[i];
|
||||
|
||||
rc = cxl_validate_translation_params(t->eiw, t->eig, t->pos);
|
||||
valid = (rc == 0);
|
||||
|
||||
if (valid != t->expect) {
|
||||
pr_err("test params failed: %s\n", t->desc);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
pr_info("..... test params: PASS %d FAIL %d\n", i - failures, failures);
|
||||
|
||||
if (failures)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* cxl_translate_init
|
||||
*
|
||||
* Run the internal validation tests when no params are passed.
|
||||
* Otherwise, parse the parameters (test vectors), and kick off
|
||||
* the translation test.
|
||||
*
|
||||
* Returns: 0 on success, negative error code on failure
|
||||
*/
|
||||
static int __init cxl_translate_init(void)
|
||||
{
|
||||
int rc, i;
|
||||
|
||||
/* If no tables are passed, validate module params only */
|
||||
if (table_num == 0) {
|
||||
pr_info("Internal validation test start...\n");
|
||||
rc = test_cxl_validate_translation_params();
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = test_random_params();
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
pr_info("Internal validation test completed successfully\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_info("CXL translate test module loaded with %d test vectors\n",
|
||||
table_num);
|
||||
|
||||
rc = setup_xor_mapping();
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Process each test vector */
|
||||
for (i = 0; i < table_num; i++) {
|
||||
u64 dpa, expect_spa;
|
||||
int pos, math;
|
||||
u8 r_eiw, hb_ways;
|
||||
u16 r_eig;
|
||||
|
||||
pr_debug("Processing test vector %d: '%s'\n", i, table[i]);
|
||||
|
||||
/* Parse the test vector */
|
||||
rc = parse_test_vector(table[i], &dpa, &pos, &r_eiw, &r_eig,
|
||||
&hb_ways, &math, &expect_spa);
|
||||
if (rc) {
|
||||
pr_err("CXL Translate Test %d: FAIL\n"
|
||||
" Failed to parse test vector '%s'\n",
|
||||
i, table[i]);
|
||||
continue;
|
||||
}
|
||||
/* Run the translation test */
|
||||
rc = run_translation_test(dpa, pos, r_eiw, r_eig, hb_ways, math,
|
||||
expect_spa);
|
||||
if (rc) {
|
||||
pr_err("CXL Translate Test %d: FAIL\n"
|
||||
" dpa=%llu pos=%d r_eiw=%u r_eig=%u hb_ways=%u math=%s expect_spa=%llu\n",
|
||||
i, dpa, pos, r_eiw, r_eig, hb_ways,
|
||||
(math == XOR_MATH) ? "XOR" : "MODULO",
|
||||
expect_spa);
|
||||
} else {
|
||||
pr_info("CXL Translate Test %d: PASS\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
kfree(cximsd);
|
||||
pr_info("CXL translate test completed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit cxl_translate_exit(void)
|
||||
{
|
||||
pr_info("CXL translate test module unloaded\n");
|
||||
}
|
||||
|
||||
module_param_array(table, charp, &table_num, 0444);
|
||||
MODULE_PARM_DESC(table, "Test vectors as space-separated decimal strings");
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("cxl_test: cxl address translation test module");
|
||||
MODULE_IMPORT_NS("CXL");
|
||||
|
||||
module_init(cxl_translate_init);
|
||||
module_exit(cxl_translate_exit);
|
||||
Reference in New Issue
Block a user