* [PATCH v8 00/13] cxl: ACPI PRM Address Translation Support and AMD Zen5 enablement
@ 2025-12-09 18:06 Robert Richter
2025-12-09 18:06 ` [PATCH v8 01/13] cxl/region: Rename misleading variable name @hpa to @hpa_range Robert Richter
` (12 more replies)
0 siblings, 13 replies; 21+ messages in thread
From: Robert Richter @ 2025-12-09 18:06 UTC (permalink / raw)
To: Alison Schofield, Vishal Verma, Ira Weiny, Dan Williams,
Jonathan Cameron, Dave Jiang, Davidlohr Bueso
Cc: linux-cxl, linux-kernel, Gregory Price, Fabio M. De Francesco,
Terry Bowman, Joshua Hahn, Robert Richter
This patch set adds support for address translation using ACPI PRM and
enables this for AMD Zen5 platforms. v4 is the current appoach in
response to earlier attempts to implement CXL address translation:
* v1: [1] and the comments on it, esp. Dan's [2],
* v2: [3] and comments on [4], esp. Dave's [5],
* v3: [6] and comments on it, esp. Dave's [7],
* v4: [8].
This version addresses review comments. Compared to v7 it adds two
additional patches to support HPA/SPA root decoder translation
callbacks. No major changes otherwise. See the changelog for
details. Thank you all for your reviews and testing.
Documentation of CXL Address Translation Support will be added to the
Kernel's "Compute Express Link: Linux Conventions". This patch
submission will be the base for a documention patch that describes CXL
Address Translation support accordingly.
The CXL driver currently does not implement address translation which
assumes the host physical addresses (HPA) and system physical
addresses (SPA) are equal.
Systems with different HPA and SPA addresses need address translation.
If this is the case, the hardware addresses esp. used in the HDM
decoder configurations are different to the system's or parent port
address ranges. E.g. AMD Zen5 systems may be configured to use
'Normalized addresses'. Then, CXL endpoints have their own physical
address base which is not the same as the SPA used by the CXL host
bridge. Thus, addresses need to be translated from the endpoint's to
its CXL host bridge's address range.
To enable address translation, the endpoint's HPA range must be
translated to the CXL host bridge's address range. A callback is
introduced to translate a decoder's HPA to the CXL host bridge's
address range. The callback is then used to determine the region
parameters which includes the SPA translated address range of the
endpoint decoder and the interleaving configuration. This is stored in
struct cxl_region which allows an endpoint decoder to determine that
parameters based on its assigned region.
Note that only auto-discovery of decoders is supported. Thus, decoders
are locked and cannot be configured manually.
Finally, Zen5 address translation is enabled using ACPI PRMT.
This series bases on cxl/next.
V8:
* rebased onto cxl-for-6.19,
* updated sob-chains,
* renamed cxl_root callback to translation_setup_root,
* renamed functions to cxl_root_setup_translation and cxl_prm_setup_root,
* added comment around cxl_root_setup_translation(),
* added check for ULLONG_MAX of return value of translation functions,
* added callback to setup translation for regions
(cxl_region_setup_translation, cxl_prm_setup_region),
* add HPA/SPA callback handlers that return ULLONG_MAX (Alison),
V7:
* rebased onto cxl/for-6.19/cxl-prm,
* reworded comment and description of 11/11 (decoder lock),
V6:
* rebased onto v6.18-rc5 and CXL updates for v6.19,
* note: applies on top of: [PATCH v3 0/3] CXL updates for v6.19,
V5:
* fixed build error with !CXL_REGION (kbot),
* updated sob-chains,
* added note to get_cxl_root_decoder() to drop reference after use
(Dave),
* moved initialziation of base* variables in
cxl_prm_translate_hpa_range() (Dave, Jonathan),
* fixed initialization of cxlr->hpa_range for the non-auto case
(Alison),
* added description of the @hpa_range arg to
cxl_calc_interleave_pos() (kbot),
* removed optional patches 12-14 to send them separately (Alison,
Dave),
* reordered patches 1-6 to reduce dependencies between them and give
way for early pick up candidates,
* rebased onto cxl/next (c692f5a947ad),
* added commas in comment in cxl_add_to_region() (Jonathan),
* removed cxlmd from struct cxl_region_context (Dave, Jonathan),
* removed use of PTR_ERR_OR_ZERO() (Jonathan),
* increased wrap width to 80 chars for comments in cxl_atl.c (Jonathan),
* moved (ways > 1) check out of while loop in cxl_prm_translate_hpa_range()
(Jonathan),
* removed trailing comma in struct prm_cxl_dpa_spa_data initializer (Jonathan),
* updated patch description on locking the decoders (Dave, Jonathan),
* spell fix in patch description (Jonathan),
V4:
* rebased onto v6.18-rc2 (cxl/next),
* updated sob-chain,
* reworked and simplified code to use an address translation callback
bound to the root port,
* moved all address translation code to core/atl.c,
* cxlr->cxlrd change, updated patch description (Alison),
* use DEFINE_RANGE() (Jonathan),
* change name to @hpa_range (Dave, Jonathan),
* updated patch description if there is a no-op (Gregory),
* use Designated initializers for struct cxl_region_context (Dave),
* move callback handler to struct cxl_root_ops (Dave),
* move hanler inialization to acpi_probe() (Dave),
* updated comment where Normalized Addressing is checked (Dave),
* limit PRM enablement only to AMD supported kernel configs (AMD_NB)
(Jonathan),
* added 3 related optional cleanup patches at the end of the series,
V3:
* rebased onto cxl/next,
* complete rework to reduce number of required changes/patches and to
remove platform specific code (Dan and Dave),
* changed implementation allowing to add address translation to the
CXL specification (documention patch in preparation),
* simplified and generalized determination of interleaving
parameters using the address translation callback,
* depend only on the existence of the ACPI PRM GUID for CXL Address
Translation enablement, removed platform checks,
* small changes to region code only which does not require a full
rework and refactoring of the code, just separating region
parameter setup and region construction,
* moved code to new core/atl.c file,
* fixed subsys_initcall order dependency of EFI runtime services
(Gregory and Joshua),
V2:
* rebased onto cxl/next,
* split of v1 in two parts:
* removed cleanups and updates from this series to post them as a
separate series (Dave),
* this part 2 applies on top of part 1, v3,
* added tags to SOB chain,
* reworked architecture, vendor and platform setup (Jonathan):
* added patch "cxl/x86: Prepare for architectural platform setup",
* added function arch_cxl_port_platform_setup() plus a __weak
versions for archs other than x86,
* moved code to core/x86,
* added comment to cxl_to_hpa_fn (Ben),
* updated year in copyright statement (Ben),
* cxl_port_calc_hpa(): Removed HPA check for zero (Jonathan), return
1 if modified,
* cxl_port_calc_pos(): Updated description and wording (Ben),
* added sereral patches around interleaving and SPA calculation in
cxl_endpoint_decoder_initialize(),
* reworked iterator in cxl_endpoint_decoder_initialize() (Gregory),
* fixed region interleaving parameters() (Alison),
* fixed check in cxl_region_attach() (Alison),
* Clarified in coverletter that not all ports in a system must
implement the to_hpa() callback (Terry).
[1] https://lore.kernel.org/linux-cxl/20240701174754.967954-1-rrichter@amd.com/
[2] https://lore.kernel.org/linux-cxl/669086821f136_5fffa29473@dwillia2-xfh.jf.intel.com.notmuch/
[3] https://patchwork.kernel.org/project/cxl/cover/20250218132356.1809075-1-rrichter@amd.com/
[4] https://patchwork.kernel.org/project/cxl/cover/20250715191143.1023512-1-rrichter@amd.com/
[5] https://lore.kernel.org/all/78284b12-3e0b-4758-af18-397f32136c3f@intel.com/
[6] https://patchwork.kernel.org/project/cxl/cover/20250912144514.526441-1-rrichter@amd.com/
[7] https://lore.kernel.org/all/20250912144514.526441-8-rrichter@amd.com/T/#m23c2adb9d1e20770ccd5d11475288bda382b0af5
[8] https://patchwork.kernel.org/project/cxl/cover/20251103184804.509762-1-rrichter@amd.com/
Robert Richter (13):
cxl/region: Rename misleading variable name @hpa to @hpa_range
cxl/region: Store root decoder in struct cxl_region
cxl/region: Store HPA range in struct cxl_region
cxl: Simplify cxl_root_ops allocation and handling
cxl/region: Separate region parameter setup and region construction
cxl/region: Add @hpa_range argument to function
cxl_calc_interleave_pos()
cxl/region: Use region data to get the root decoder
cxl: Introduce callback for HPA address ranges translation
cxl/acpi: Prepare use of EFI runtime services
cxl: Enable AMD Zen5 address translation using ACPI PRMT
cxl/atl: Lock decoders that need address translation
cxl: Check if ULLONG_MAX was returned from translation functions
cxl: Disable HPA/SPA translation handlers for Normalized addressing
drivers/cxl/Kconfig | 5 +
drivers/cxl/acpi.c | 17 +-
drivers/cxl/core/Makefile | 1 +
drivers/cxl/core/atl.c | 250 +++++++++++++++++++++++++
drivers/cxl/core/cdat.c | 8 +-
drivers/cxl/core/core.h | 8 +
drivers/cxl/core/hdm.c | 2 +-
drivers/cxl/core/port.c | 8 +-
drivers/cxl/core/region.c | 218 ++++++++++++++-------
drivers/cxl/cxl.h | 32 +++-
tools/testing/cxl/test/cxl_translate.c | 4 +-
11 files changed, 454 insertions(+), 99 deletions(-)
create mode 100644 drivers/cxl/core/atl.c
base-commit: ea5514e300568cbe8f19431c3e424d4791db8291
--
2.47.3
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v8 01/13] cxl/region: Rename misleading variable name @hpa to @hpa_range
2025-12-09 18:06 [PATCH v8 00/13] cxl: ACPI PRM Address Translation Support and AMD Zen5 enablement Robert Richter
@ 2025-12-09 18:06 ` Robert Richter
2025-12-09 18:06 ` [PATCH v8 02/13] cxl/region: Store root decoder in struct cxl_region Robert Richter
` (11 subsequent siblings)
12 siblings, 0 replies; 21+ messages in thread
From: Robert Richter @ 2025-12-09 18:06 UTC (permalink / raw)
To: Alison Schofield, Vishal Verma, Ira Weiny, Dan Williams,
Jonathan Cameron, Dave Jiang, Davidlohr Bueso
Cc: linux-cxl, linux-kernel, Gregory Price, Fabio M. De Francesco,
Terry Bowman, Joshua Hahn, Robert Richter
@hpa is actually a @hpa_range, rename variables accordingly.
Reviewed-by: Gregory Price <gourry@gourry.net>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Signed-off-by: Robert Richter <rrichter@amd.com>
---
drivers/cxl/core/region.c | 28 +++++++++++++++-------------
1 file changed, 15 insertions(+), 13 deletions(-)
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 2cf5b29cefd2..1fd2a13288e9 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -3471,9 +3471,9 @@ static int match_decoder_by_range(struct device *dev, const void *data)
}
static struct cxl_decoder *
-cxl_port_find_switch_decoder(struct cxl_port *port, struct range *hpa)
+cxl_port_find_switch_decoder(struct cxl_port *port, struct range *hpa_range)
{
- struct device *cxld_dev = device_find_child(&port->dev, hpa,
+ struct device *cxld_dev = device_find_child(&port->dev, hpa_range,
match_decoder_by_range);
return cxld_dev ? to_cxl_decoder(cxld_dev) : NULL;
@@ -3486,14 +3486,14 @@ cxl_find_root_decoder(struct cxl_endpoint_decoder *cxled)
struct cxl_port *port = cxled_to_port(cxled);
struct cxl_root *cxl_root __free(put_cxl_root) = find_cxl_root(port);
struct cxl_decoder *root, *cxld = &cxled->cxld;
- struct range *hpa = &cxld->hpa_range;
+ struct range *hpa_range = &cxld->hpa_range;
- root = cxl_port_find_switch_decoder(&cxl_root->port, hpa);
+ root = cxl_port_find_switch_decoder(&cxl_root->port, hpa_range);
if (!root) {
dev_err(cxlmd->dev.parent,
"%s:%s no CXL window for range %#llx:%#llx\n",
dev_name(&cxlmd->dev), dev_name(&cxld->dev),
- cxld->hpa_range.start, cxld->hpa_range.end);
+ hpa_range->start, hpa_range->end);
return NULL;
}
@@ -3559,7 +3559,7 @@ static int __construct_region(struct cxl_region *cxlr,
struct cxl_endpoint_decoder *cxled)
{
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
- struct range *hpa = &cxled->cxld.hpa_range;
+ struct range *hpa_range = &cxled->cxld.hpa_range;
struct cxl_region_params *p;
struct resource *res;
int rc;
@@ -3580,7 +3580,7 @@ static int __construct_region(struct cxl_region *cxlr,
if (!res)
return -ENOMEM;
- *res = DEFINE_RES_MEM_NAMED(hpa->start, range_len(hpa),
+ *res = DEFINE_RES_MEM_NAMED(hpa_range->start, range_len(hpa_range),
dev_name(&cxlr->dev));
rc = cxl_extended_linear_cache_resize(cxlr, res);
@@ -3663,11 +3663,12 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
}
static struct cxl_region *
-cxl_find_region_by_range(struct cxl_root_decoder *cxlrd, struct range *hpa)
+cxl_find_region_by_range(struct cxl_root_decoder *cxlrd,
+ struct range *hpa_range)
{
struct device *region_dev;
- region_dev = device_find_child(&cxlrd->cxlsd.cxld.dev, hpa,
+ region_dev = device_find_child(&cxlrd->cxlsd.cxld.dev, hpa_range,
match_region_by_range);
if (!region_dev)
return NULL;
@@ -3677,7 +3678,7 @@ cxl_find_region_by_range(struct cxl_root_decoder *cxlrd, struct range *hpa)
int cxl_add_to_region(struct cxl_endpoint_decoder *cxled)
{
- struct range *hpa = &cxled->cxld.hpa_range;
+ struct range *hpa_range = &cxled->cxld.hpa_range;
struct cxl_region_params *p;
bool attach = false;
int rc;
@@ -3688,12 +3689,13 @@ int cxl_add_to_region(struct cxl_endpoint_decoder *cxled)
return -ENXIO;
/*
- * Ensure that if multiple threads race to construct_region() for @hpa
- * one does the construction and the others add to that.
+ * Ensure that, if multiple threads race to construct_region()
+ * for the HPA range, one does the construction and the others
+ * add to that.
*/
mutex_lock(&cxlrd->range_lock);
struct cxl_region *cxlr __free(put_cxl_region) =
- cxl_find_region_by_range(cxlrd, hpa);
+ cxl_find_region_by_range(cxlrd, hpa_range);
if (!cxlr)
cxlr = construct_region(cxlrd, cxled);
mutex_unlock(&cxlrd->range_lock);
--
2.47.3
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v8 02/13] cxl/region: Store root decoder in struct cxl_region
2025-12-09 18:06 [PATCH v8 00/13] cxl: ACPI PRM Address Translation Support and AMD Zen5 enablement Robert Richter
2025-12-09 18:06 ` [PATCH v8 01/13] cxl/region: Rename misleading variable name @hpa to @hpa_range Robert Richter
@ 2025-12-09 18:06 ` Robert Richter
2025-12-09 18:06 ` [PATCH v8 03/13] cxl/region: Store HPA range " Robert Richter
` (10 subsequent siblings)
12 siblings, 0 replies; 21+ messages in thread
From: Robert Richter @ 2025-12-09 18:06 UTC (permalink / raw)
To: Alison Schofield, Vishal Verma, Ira Weiny, Dan Williams,
Jonathan Cameron, Dave Jiang, Davidlohr Bueso
Cc: linux-cxl, linux-kernel, Gregory Price, Fabio M. De Francesco,
Terry Bowman, Joshua Hahn, Robert Richter
A region is always bound to a root decoder. The region's associated
root decoder is often needed. Add it to struct cxl_region.
This simplifies the code by removing dynamic lookups and the root
decoder argument from the function argument list where possible.
Patch is a prerequisite to implement address translation which uses
struct cxl_region to store all relevant region and interleaving
parameters. It changes the argument list of __construct_region() in
preparation of adding a context argument. Additionally the arg list of
cxl_region_attach_position() is simplified and the use of
to_cxl_root_decoder() removed, which always reconstructs and checks
the pointer. The pointer never changes and is frequently used. Code
becomes more readable as this amphazises the binding between both
objects.
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Reviewed-by: Gregory Price <gourry@gourry.net>
Signed-off-by: Robert Richter <rrichter@amd.com>
---
drivers/cxl/core/region.c | 37 +++++++++++++++++++------------------
drivers/cxl/cxl.h | 2 ++
2 files changed, 21 insertions(+), 18 deletions(-)
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 1fd2a13288e9..077cfc75e081 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -486,9 +486,9 @@ static ssize_t interleave_ways_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
- struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev->parent);
- struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld;
struct cxl_region *cxlr = to_cxl_region(dev);
+ struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
+ struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld;
struct cxl_region_params *p = &cxlr->params;
unsigned int val, save;
int rc;
@@ -549,9 +549,9 @@ static ssize_t interleave_granularity_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
- struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev->parent);
- struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld;
struct cxl_region *cxlr = to_cxl_region(dev);
+ struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
+ struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld;
struct cxl_region_params *p = &cxlr->params;
int rc, val;
u16 ig;
@@ -625,7 +625,7 @@ static DEVICE_ATTR_RO(mode);
static int alloc_hpa(struct cxl_region *cxlr, resource_size_t size)
{
- struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
+ struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
struct cxl_region_params *p = &cxlr->params;
struct resource *res;
u64 remainder = 0;
@@ -1370,7 +1370,7 @@ static int cxl_port_setup_targets(struct cxl_port *port,
struct cxl_region *cxlr,
struct cxl_endpoint_decoder *cxled)
{
- struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
+ struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
int parent_iw, parent_ig, ig, iw, rc, pos = cxled->pos;
struct cxl_port *parent_port = to_cxl_port(port->dev.parent);
struct cxl_region_ref *cxl_rr = cxl_rr_load(port, cxlr);
@@ -1728,10 +1728,10 @@ static int cxl_region_validate_position(struct cxl_region *cxlr,
}
static int cxl_region_attach_position(struct cxl_region *cxlr,
- struct cxl_root_decoder *cxlrd,
struct cxl_endpoint_decoder *cxled,
const struct cxl_dport *dport, int pos)
{
+ struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
struct cxl_switch_decoder *cxlsd = &cxlrd->cxlsd;
struct cxl_decoder *cxld = &cxlsd->cxld;
@@ -1968,7 +1968,7 @@ static int cxl_region_sort_targets(struct cxl_region *cxlr)
static int cxl_region_attach(struct cxl_region *cxlr,
struct cxl_endpoint_decoder *cxled, int pos)
{
- struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
+ struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
struct cxl_dev_state *cxlds = cxlmd->cxlds;
struct cxl_region_params *p = &cxlr->params;
@@ -2073,8 +2073,7 @@ static int cxl_region_attach(struct cxl_region *cxlr,
ep_port = cxled_to_port(cxled);
dport = cxl_find_dport_by_dev(root_port,
ep_port->host_bridge);
- rc = cxl_region_attach_position(cxlr, cxlrd, cxled,
- dport, i);
+ rc = cxl_region_attach_position(cxlr, cxled, dport, i);
if (rc)
return rc;
}
@@ -2097,7 +2096,7 @@ static int cxl_region_attach(struct cxl_region *cxlr,
if (rc)
return rc;
- rc = cxl_region_attach_position(cxlr, cxlrd, cxled, dport, pos);
+ rc = cxl_region_attach_position(cxlr, cxled, dport, pos);
if (rc)
return rc;
@@ -2393,8 +2392,8 @@ static const struct attribute_group *region_groups[] = {
static void cxl_region_release(struct device *dev)
{
- struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev->parent);
struct cxl_region *cxlr = to_cxl_region(dev);
+ struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
int id = atomic_read(&cxlrd->region_id);
/*
@@ -2477,10 +2476,12 @@ static struct cxl_region *cxl_region_alloc(struct cxl_root_decoder *cxlrd, int i
* region id allocations
*/
get_device(dev->parent);
+ cxlr->cxlrd = cxlrd;
+ cxlr->id = id;
+
device_set_pm_not_required(dev);
dev->bus = &cxl_bus_type;
dev->type = &cxl_region_type;
- cxlr->id = id;
cxl_region_set_lock(cxlr, &cxlrd->cxlsd.cxld);
return cxlr;
@@ -3112,7 +3113,7 @@ 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_root_decoder *cxlrd = cxlr->cxlrd;
struct cxl_region_params *p = &cxlr->params;
struct cxl_endpoint_decoder *cxled = NULL;
u64 dpa_offset, hpa_offset, hpa;
@@ -3165,7 +3166,7 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
struct dpa_result *result)
{
struct cxl_region_params *p = &cxlr->params;
- struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
+ struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
struct cxl_endpoint_decoder *cxled;
u64 hpa, hpa_offset, dpa_offset;
u16 eig = 0;
@@ -3519,7 +3520,7 @@ static int match_region_by_range(struct device *dev, const void *data)
static int cxl_extended_linear_cache_resize(struct cxl_region *cxlr,
struct resource *res)
{
- struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
+ struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
struct cxl_region_params *p = &cxlr->params;
resource_size_t size = resource_size(res);
resource_size_t cache_size, start;
@@ -3555,9 +3556,9 @@ static int cxl_extended_linear_cache_resize(struct cxl_region *cxlr,
}
static int __construct_region(struct cxl_region *cxlr,
- struct cxl_root_decoder *cxlrd,
struct cxl_endpoint_decoder *cxled)
{
+ struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
struct range *hpa_range = &cxled->cxld.hpa_range;
struct cxl_region_params *p;
@@ -3653,7 +3654,7 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
return cxlr;
}
- rc = __construct_region(cxlr, cxlrd, cxled);
+ rc = __construct_region(cxlr, cxled);
if (rc) {
devm_release_action(port->uport_dev, unregister_region, cxlr);
return ERR_PTR(rc);
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index ba17fa86d249..10ce9c3a8a55 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -529,6 +529,7 @@ enum cxl_partition_mode {
* struct cxl_region - CXL region
* @dev: This region's device
* @id: This region's id. Id is globally unique across all regions
+ * @cxlrd: Region's root decoder
* @mode: Operational mode of the mapped capacity
* @type: Endpoint decoder target type
* @cxl_nvb: nvdimm bridge for coordinating @cxlr_pmem setup / shutdown
@@ -542,6 +543,7 @@ enum cxl_partition_mode {
struct cxl_region {
struct device dev;
int id;
+ struct cxl_root_decoder *cxlrd;
enum cxl_partition_mode mode;
enum cxl_decoder_type type;
struct cxl_nvdimm_bridge *cxl_nvb;
--
2.47.3
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v8 03/13] cxl/region: Store HPA range in struct cxl_region
2025-12-09 18:06 [PATCH v8 00/13] cxl: ACPI PRM Address Translation Support and AMD Zen5 enablement Robert Richter
2025-12-09 18:06 ` [PATCH v8 01/13] cxl/region: Rename misleading variable name @hpa to @hpa_range Robert Richter
2025-12-09 18:06 ` [PATCH v8 02/13] cxl/region: Store root decoder in struct cxl_region Robert Richter
@ 2025-12-09 18:06 ` Robert Richter
2025-12-09 18:06 ` [PATCH v8 04/13] cxl: Simplify cxl_root_ops allocation and handling Robert Richter
` (9 subsequent siblings)
12 siblings, 0 replies; 21+ messages in thread
From: Robert Richter @ 2025-12-09 18:06 UTC (permalink / raw)
To: Alison Schofield, Vishal Verma, Ira Weiny, Dan Williams,
Jonathan Cameron, Dave Jiang, Davidlohr Bueso
Cc: linux-cxl, linux-kernel, Gregory Price, Fabio M. De Francesco,
Terry Bowman, Joshua Hahn, Robert Richter
Each region has a known host physical address (HPA) range it is
assigned to. Endpoint decoders assigned to a region share the same HPA
range. The region's address range is the system's physical address
(SPA) range.
Endpoint decoders in systems that need address translation use HPAs
which are not SPAs. To make the SPA range accessible to the endpoint
decoders, store and track the region's SPA range in struct cxl_region.
Introduce the @hpa_range member to the struct. Now, the SPA range of
an endpoint decoder can be determined based on its assigned region.
Patch is a prerequisite to implement address translation which uses
struct cxl_region to store all relevant region and interleaving
parameters.
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Reviewed-by: Gregory Price <gourry@gourry.net>
Signed-off-by: Robert Richter <rrichter@amd.com>
---
drivers/cxl/core/region.c | 7 +++++++
drivers/cxl/cxl.h | 2 ++
2 files changed, 9 insertions(+)
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 077cfc75e081..57cf3ab28f43 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -661,6 +661,8 @@ static int alloc_hpa(struct cxl_region *cxlr, resource_size_t size)
return PTR_ERR(res);
}
+ cxlr->hpa_range = DEFINE_RANGE(res->start, res->end);
+
p->res = res;
p->state = CXL_CONFIG_INTERLEAVE_ACTIVE;
@@ -697,6 +699,8 @@ static int free_hpa(struct cxl_region *cxlr)
if (p->state >= CXL_CONFIG_ACTIVE)
return -EBUSY;
+ cxlr->hpa_range = DEFINE_RANGE(0, -1);
+
cxl_region_iomem_release(cxlr);
p->state = CXL_CONFIG_IDLE;
return 0;
@@ -2450,6 +2454,8 @@ static void unregister_region(void *_cxlr)
for (i = 0; i < p->interleave_ways; i++)
detach_target(cxlr, i);
+ cxlr->hpa_range = DEFINE_RANGE(0, -1);
+
cxl_region_iomem_release(cxlr);
put_device(&cxlr->dev);
}
@@ -3576,6 +3582,7 @@ static int __construct_region(struct cxl_region *cxlr,
}
set_bit(CXL_REGION_F_AUTO, &cxlr->flags);
+ cxlr->hpa_range = *hpa_range;
res = kmalloc(sizeof(*res), GFP_KERNEL);
if (!res)
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 10ce9c3a8a55..3a5ca1936ed1 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -530,6 +530,7 @@ enum cxl_partition_mode {
* @dev: This region's device
* @id: This region's id. Id is globally unique across all regions
* @cxlrd: Region's root decoder
+ * @hpa_range: Address range occupied by the region
* @mode: Operational mode of the mapped capacity
* @type: Endpoint decoder target type
* @cxl_nvb: nvdimm bridge for coordinating @cxlr_pmem setup / shutdown
@@ -544,6 +545,7 @@ struct cxl_region {
struct device dev;
int id;
struct cxl_root_decoder *cxlrd;
+ struct range hpa_range;
enum cxl_partition_mode mode;
enum cxl_decoder_type type;
struct cxl_nvdimm_bridge *cxl_nvb;
--
2.47.3
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v8 04/13] cxl: Simplify cxl_root_ops allocation and handling
2025-12-09 18:06 [PATCH v8 00/13] cxl: ACPI PRM Address Translation Support and AMD Zen5 enablement Robert Richter
` (2 preceding siblings ...)
2025-12-09 18:06 ` [PATCH v8 03/13] cxl/region: Store HPA range " Robert Richter
@ 2025-12-09 18:06 ` Robert Richter
2025-12-09 18:06 ` [PATCH v8 05/13] cxl/region: Separate region parameter setup and region construction Robert Richter
` (8 subsequent siblings)
12 siblings, 0 replies; 21+ messages in thread
From: Robert Richter @ 2025-12-09 18:06 UTC (permalink / raw)
To: Alison Schofield, Vishal Verma, Ira Weiny, Dan Williams,
Jonathan Cameron, Dave Jiang, Davidlohr Bueso
Cc: linux-cxl, linux-kernel, Gregory Price, Fabio M. De Francesco,
Terry Bowman, Joshua Hahn, Robert Richter
A root port's callback handlers are collected in struct cxl_root_ops.
The structure is dynamically allocated, though it contains only a
single pointer in it. This also requires to check two pointers to
check for the existance of a callback.
Simplify the allocation, release and handler check by embedding the
ops statically in struct cxl_root.
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Signed-off-by: Robert Richter <rrichter@amd.com>
---
drivers/cxl/acpi.c | 7 ++-----
drivers/cxl/core/cdat.c | 8 ++++----
drivers/cxl/core/port.c | 8 ++------
drivers/cxl/cxl.h | 19 ++++++++++---------
4 files changed, 18 insertions(+), 24 deletions(-)
diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
index 77ac940e3013..b4bed40ef7c0 100644
--- a/drivers/cxl/acpi.c
+++ b/drivers/cxl/acpi.c
@@ -318,10 +318,6 @@ static int cxl_acpi_qos_class(struct cxl_root *cxl_root,
return cxl_acpi_evaluate_qtg_dsm(handle, coord, entries, qos_class);
}
-static const struct cxl_root_ops acpi_root_ops = {
- .qos_class = cxl_acpi_qos_class,
-};
-
static void del_cxl_resource(struct resource *res)
{
if (!res)
@@ -923,9 +919,10 @@ static int cxl_acpi_probe(struct platform_device *pdev)
cxl_res->end = -1;
cxl_res->flags = IORESOURCE_MEM;
- cxl_root = devm_cxl_add_root(host, &acpi_root_ops);
+ cxl_root = devm_cxl_add_root(host);
if (IS_ERR(cxl_root))
return PTR_ERR(cxl_root);
+ cxl_root->ops.qos_class = cxl_acpi_qos_class;
root_port = &cxl_root->port;
rc = bus_for_each_dev(adev->dev.bus, NULL, root_port,
diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
index 7120b5f2e31f..18f0f2a25113 100644
--- a/drivers/cxl/core/cdat.c
+++ b/drivers/cxl/core/cdat.c
@@ -213,7 +213,7 @@ static int cxl_port_perf_data_calculate(struct cxl_port *port,
if (!cxl_root)
return -ENODEV;
- if (!cxl_root->ops || !cxl_root->ops->qos_class)
+ if (!cxl_root->ops.qos_class)
return -EOPNOTSUPP;
xa_for_each(dsmas_xa, index, dent) {
@@ -221,9 +221,9 @@ static int cxl_port_perf_data_calculate(struct cxl_port *port,
cxl_coordinates_combine(dent->coord, dent->cdat_coord, ep_c);
dent->entries = 1;
- rc = cxl_root->ops->qos_class(cxl_root,
- &dent->coord[ACCESS_COORDINATE_CPU],
- 1, &qos_class);
+ rc = cxl_root->ops.qos_class(cxl_root,
+ &dent->coord[ACCESS_COORDINATE_CPU],
+ 1, &qos_class);
if (rc != 1)
continue;
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index fef3aa0c6680..2338d146577c 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -954,19 +954,15 @@ struct cxl_port *devm_cxl_add_port(struct device *host,
}
EXPORT_SYMBOL_NS_GPL(devm_cxl_add_port, "CXL");
-struct cxl_root *devm_cxl_add_root(struct device *host,
- const struct cxl_root_ops *ops)
+struct cxl_root *devm_cxl_add_root(struct device *host)
{
- struct cxl_root *cxl_root;
struct cxl_port *port;
port = devm_cxl_add_port(host, host, CXL_RESOURCE_NONE, NULL);
if (IS_ERR(port))
return ERR_CAST(port);
- cxl_root = to_cxl_root(port);
- cxl_root->ops = ops;
- return cxl_root;
+ return to_cxl_root(port);
}
EXPORT_SYMBOL_NS_GPL(devm_cxl_add_root, "CXL");
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 3a5ca1936ed1..0e15dc6e169f 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -646,6 +646,14 @@ struct cxl_port {
resource_size_t component_reg_phys;
};
+struct cxl_root;
+
+struct cxl_root_ops {
+ int (*qos_class)(struct cxl_root *cxl_root,
+ struct access_coordinate *coord, int entries,
+ int *qos_class);
+};
+
/**
* struct cxl_root - logical collection of root cxl_port items
*
@@ -654,7 +662,7 @@ struct cxl_port {
*/
struct cxl_root {
struct cxl_port port;
- const struct cxl_root_ops *ops;
+ struct cxl_root_ops ops;
};
static inline struct cxl_root *
@@ -663,12 +671,6 @@ to_cxl_root(const struct cxl_port *port)
return container_of(port, struct cxl_root, port);
}
-struct cxl_root_ops {
- int (*qos_class)(struct cxl_root *cxl_root,
- struct access_coordinate *coord, int entries,
- int *qos_class);
-};
-
static inline struct cxl_dport *
cxl_find_dport_by_dev(struct cxl_port *port, const struct device *dport_dev)
{
@@ -782,8 +784,7 @@ struct cxl_port *devm_cxl_add_port(struct device *host,
struct device *uport_dev,
resource_size_t component_reg_phys,
struct cxl_dport *parent_dport);
-struct cxl_root *devm_cxl_add_root(struct device *host,
- const struct cxl_root_ops *ops);
+struct cxl_root *devm_cxl_add_root(struct device *host);
struct cxl_root *find_cxl_root(struct cxl_port *port);
DEFINE_FREE(put_cxl_root, struct cxl_root *, if (_T) put_device(&_T->port.dev))
--
2.47.3
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v8 05/13] cxl/region: Separate region parameter setup and region construction
2025-12-09 18:06 [PATCH v8 00/13] cxl: ACPI PRM Address Translation Support and AMD Zen5 enablement Robert Richter
` (3 preceding siblings ...)
2025-12-09 18:06 ` [PATCH v8 04/13] cxl: Simplify cxl_root_ops allocation and handling Robert Richter
@ 2025-12-09 18:06 ` Robert Richter
2025-12-09 18:06 ` [PATCH v8 06/13] cxl/region: Add @hpa_range argument to function cxl_calc_interleave_pos() Robert Richter
` (7 subsequent siblings)
12 siblings, 0 replies; 21+ messages in thread
From: Robert Richter @ 2025-12-09 18:06 UTC (permalink / raw)
To: Alison Schofield, Vishal Verma, Ira Weiny, Dan Williams,
Jonathan Cameron, Dave Jiang, Davidlohr Bueso
Cc: linux-cxl, linux-kernel, Gregory Price, Fabio M. De Francesco,
Terry Bowman, Joshua Hahn, Robert Richter
To construct a region, the region parameters such as address range and
interleaving config need to be determined. This is done while
constructing the region by inspecting the endpoint decoder
configuration. The endpoint decoder is passed as a function argument.
With address translation the endpoint decoder data is no longer
sufficient to extract the region parameters as some of the information
is obtained using other methods such as using firmware calls.
In a first step, separate code to determine the region parameters from
the region construction. Temporarily store all the data to create the
region in the new struct cxl_region_context. Once the region data is
determined and struct cxl_region_context is filled, construct the
region.
Patch is a prerequisite to implement address translation. The code
separation helps to later extend it to determine region parameters
using other methods as needed, esp. to support address translation.
Reviewed-by: Gregory Price <gourry@gourry.net>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Tested-by: Gregory Price <gourry@gourry.net>
Signed-off-by: Robert Richter <rrichter@amd.com>
---
drivers/cxl/core/core.h | 8 ++++++++
drivers/cxl/core/region.c | 27 ++++++++++++++++++---------
2 files changed, 26 insertions(+), 9 deletions(-)
diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
index 1fb66132b777..ae9e1bb51562 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -19,6 +19,14 @@ enum cxl_detach_mode {
};
#ifdef CONFIG_CXL_REGION
+
+struct cxl_region_context {
+ struct cxl_endpoint_decoder *cxled;
+ struct range hpa_range;
+ int interleave_ways;
+ int interleave_granularity;
+};
+
extern struct device_attribute dev_attr_create_pmem_region;
extern struct device_attribute dev_attr_create_ram_region;
extern struct device_attribute dev_attr_delete_region;
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 57cf3ab28f43..dfb21e8b8fb5 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -3562,11 +3562,12 @@ static int cxl_extended_linear_cache_resize(struct cxl_region *cxlr,
}
static int __construct_region(struct cxl_region *cxlr,
- struct cxl_endpoint_decoder *cxled)
+ struct cxl_region_context *ctx)
{
+ struct cxl_endpoint_decoder *cxled = ctx->cxled;
struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
- struct range *hpa_range = &cxled->cxld.hpa_range;
+ struct range *hpa_range = &ctx->hpa_range;
struct cxl_region_params *p;
struct resource *res;
int rc;
@@ -3619,8 +3620,8 @@ static int __construct_region(struct cxl_region *cxlr,
}
p->res = res;
- p->interleave_ways = cxled->cxld.interleave_ways;
- p->interleave_granularity = cxled->cxld.interleave_granularity;
+ p->interleave_ways = ctx->interleave_ways;
+ p->interleave_granularity = ctx->interleave_granularity;
p->state = CXL_CONFIG_INTERLEAVE_ACTIVE;
rc = sysfs_update_group(&cxlr->dev.kobj, get_cxl_region_target_group());
@@ -3640,8 +3641,9 @@ static int __construct_region(struct cxl_region *cxlr,
/* Establish an empty region covering the given HPA range */
static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
- struct cxl_endpoint_decoder *cxled)
+ struct cxl_region_context *ctx)
{
+ struct cxl_endpoint_decoder *cxled = ctx->cxled;
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
struct cxl_port *port = cxlrd_to_port(cxlrd);
struct cxl_dev_state *cxlds = cxlmd->cxlds;
@@ -3661,7 +3663,7 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
return cxlr;
}
- rc = __construct_region(cxlr, cxled);
+ rc = __construct_region(cxlr, ctx);
if (rc) {
devm_release_action(port->uport_dev, unregister_region, cxlr);
return ERR_PTR(rc);
@@ -3686,11 +3688,18 @@ cxl_find_region_by_range(struct cxl_root_decoder *cxlrd,
int cxl_add_to_region(struct cxl_endpoint_decoder *cxled)
{
- struct range *hpa_range = &cxled->cxld.hpa_range;
+ struct cxl_region_context ctx;
struct cxl_region_params *p;
bool attach = false;
int rc;
+ ctx = (struct cxl_region_context) {
+ .cxled = cxled,
+ .hpa_range = cxled->cxld.hpa_range,
+ .interleave_ways = cxled->cxld.interleave_ways,
+ .interleave_granularity = cxled->cxld.interleave_granularity,
+ };
+
struct cxl_root_decoder *cxlrd __free(put_cxl_root_decoder) =
cxl_find_root_decoder(cxled);
if (!cxlrd)
@@ -3703,9 +3712,9 @@ int cxl_add_to_region(struct cxl_endpoint_decoder *cxled)
*/
mutex_lock(&cxlrd->range_lock);
struct cxl_region *cxlr __free(put_cxl_region) =
- cxl_find_region_by_range(cxlrd, hpa_range);
+ cxl_find_region_by_range(cxlrd, &ctx.hpa_range);
if (!cxlr)
- cxlr = construct_region(cxlrd, cxled);
+ cxlr = construct_region(cxlrd, &ctx);
mutex_unlock(&cxlrd->range_lock);
rc = PTR_ERR_OR_ZERO(cxlr);
--
2.47.3
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v8 06/13] cxl/region: Add @hpa_range argument to function cxl_calc_interleave_pos()
2025-12-09 18:06 [PATCH v8 00/13] cxl: ACPI PRM Address Translation Support and AMD Zen5 enablement Robert Richter
` (4 preceding siblings ...)
2025-12-09 18:06 ` [PATCH v8 05/13] cxl/region: Separate region parameter setup and region construction Robert Richter
@ 2025-12-09 18:06 ` Robert Richter
2025-12-09 18:06 ` [PATCH v8 07/13] cxl/region: Use region data to get the root decoder Robert Richter
` (6 subsequent siblings)
12 siblings, 0 replies; 21+ messages in thread
From: Robert Richter @ 2025-12-09 18:06 UTC (permalink / raw)
To: Alison Schofield, Vishal Verma, Ira Weiny, Dan Williams,
Jonathan Cameron, Dave Jiang, Davidlohr Bueso
Cc: linux-cxl, linux-kernel, Gregory Price, Fabio M. De Francesco,
Terry Bowman, Joshua Hahn, Robert Richter
cxl_calc_interleave_pos() uses the endpoint decoder's HPA range to
determine its interleaving position. This requires the endpoint
decoders to be an SPA, which is not the case for systems that need
address translation.
Add a separate @hpa_range argument to function
cxl_calc_interleave_pos() to specify the address range. Now it is
possible to pass the SPA translated address range of an endpoint
decoder to function cxl_calc_interleave_pos().
Refactor only, no functional changes.
Patch is a prerequisite to implement address translation.
Reviewed-by: Gregory Price <gourry@gourry.net>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Tested-by: Gregory Price <gourry@gourry.net>
Signed-off-by: Robert Richter <rrichter@amd.com>
---
drivers/cxl/core/region.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index dfb21e8b8fb5..71e80223f475 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -1875,6 +1875,7 @@ static int find_pos_and_ways(struct cxl_port *port, struct range *range,
/**
* cxl_calc_interleave_pos() - calculate an endpoint position in a region
* @cxled: endpoint decoder member of given region
+ * @hpa_range: translated HPA range of the endpoint
*
* The endpoint position is calculated by traversing the topology from
* the endpoint to the root decoder and iteratively applying this
@@ -1887,11 +1888,11 @@ static int find_pos_and_ways(struct cxl_port *port, struct range *range,
* Return: position >= 0 on success
* -ENXIO on failure
*/
-static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled)
+static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled,
+ struct range *hpa_range)
{
struct cxl_port *iter, *port = cxled_to_port(cxled);
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
- struct range *range = &cxled->cxld.hpa_range;
int parent_ways = 0, parent_pos = 0, pos = 0;
int rc;
@@ -1929,7 +1930,8 @@ static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled)
if (is_cxl_root(iter))
break;
- rc = find_pos_and_ways(iter, range, &parent_pos, &parent_ways);
+ rc = find_pos_and_ways(iter, hpa_range, &parent_pos,
+ &parent_ways);
if (rc)
return rc;
@@ -1939,7 +1941,7 @@ static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled)
dev_dbg(&cxlmd->dev,
"decoder:%s parent:%s port:%s range:%#llx-%#llx pos:%d\n",
dev_name(&cxled->cxld.dev), dev_name(cxlmd->dev.parent),
- dev_name(&port->dev), range->start, range->end, pos);
+ dev_name(&port->dev), hpa_range->start, hpa_range->end, pos);
return pos;
}
@@ -1952,7 +1954,7 @@ static int cxl_region_sort_targets(struct cxl_region *cxlr)
for (i = 0; i < p->nr_targets; i++) {
struct cxl_endpoint_decoder *cxled = p->targets[i];
- cxled->pos = cxl_calc_interleave_pos(cxled);
+ cxled->pos = cxl_calc_interleave_pos(cxled, &cxlr->hpa_range);
/*
* Record that sorting failed, but still continue to calc
* cxled->pos so that follow-on code paths can reliably
@@ -2136,7 +2138,7 @@ static int cxl_region_attach(struct cxl_region *cxlr,
struct cxl_endpoint_decoder *cxled = p->targets[i];
int test_pos;
- test_pos = cxl_calc_interleave_pos(cxled);
+ test_pos = cxl_calc_interleave_pos(cxled, &cxlr->hpa_range);
dev_dbg(&cxled->cxld.dev,
"Test cxl_calc_interleave_pos(): %s test_pos:%d cxled->pos:%d\n",
(test_pos == cxled->pos) ? "success" : "fail",
--
2.47.3
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v8 07/13] cxl/region: Use region data to get the root decoder
2025-12-09 18:06 [PATCH v8 00/13] cxl: ACPI PRM Address Translation Support and AMD Zen5 enablement Robert Richter
` (5 preceding siblings ...)
2025-12-09 18:06 ` [PATCH v8 06/13] cxl/region: Add @hpa_range argument to function cxl_calc_interleave_pos() Robert Richter
@ 2025-12-09 18:06 ` Robert Richter
2025-12-09 18:06 ` [PATCH v8 08/13] cxl: Introduce callback for HPA address ranges translation Robert Richter
` (5 subsequent siblings)
12 siblings, 0 replies; 21+ messages in thread
From: Robert Richter @ 2025-12-09 18:06 UTC (permalink / raw)
To: Alison Schofield, Vishal Verma, Ira Weiny, Dan Williams,
Jonathan Cameron, Dave Jiang, Davidlohr Bueso
Cc: linux-cxl, linux-kernel, Gregory Price, Fabio M. De Francesco,
Terry Bowman, Joshua Hahn, Robert Richter
To find a region's root decoder, the endpoint's HPA range is used to
search the matching decoder by its range. With address translation the
endpoint decoder's range is in a different address space and thus
cannot be used to determine the root decoder.
The region parameters are encapsulated within struc cxl_region_context
and may include the translated Host Physical Address (HPA) range. Use
this context to identify the root decoder rather than relying on the
endpoint.
Modify cxl_find_root_decoder() and add the region context as
parameter. Rename this function to get_cxl_root_decoder() as a
counterpart to put_cxl_root_decoder(). Simplify the implementation by
removing function cxl_port_find_switch_decode(). The function is
unnecessary because it is not referenced or utilized elsewhere in the
code.
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Tested-by: Gregory Price <gourry@gourry.net>
Signed-off-by: Robert Richter <rrichter@amd.com>
---
drivers/cxl/core/region.c | 50 +++++++++++++++++++--------------------
1 file changed, 24 insertions(+), 26 deletions(-)
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 71e80223f475..e4efb281def4 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -3466,47 +3466,44 @@ static int devm_cxl_add_dax_region(struct cxl_region *cxlr)
return rc;
}
-static int match_decoder_by_range(struct device *dev, const void *data)
+static int match_root_decoder(struct device *dev, const void *data)
{
const struct range *r1, *r2 = data;
- struct cxl_decoder *cxld;
+ struct cxl_root_decoder *cxlrd;
- if (!is_switch_decoder(dev))
+ if (!is_root_decoder(dev))
return 0;
- cxld = to_cxl_decoder(dev);
- r1 = &cxld->hpa_range;
- return range_contains(r1, r2);
-}
-
-static struct cxl_decoder *
-cxl_port_find_switch_decoder(struct cxl_port *port, struct range *hpa_range)
-{
- struct device *cxld_dev = device_find_child(&port->dev, hpa_range,
- match_decoder_by_range);
+ cxlrd = to_cxl_root_decoder(dev);
+ r1 = &cxlrd->cxlsd.cxld.hpa_range;
- return cxld_dev ? to_cxl_decoder(cxld_dev) : NULL;
+ return range_contains(r1, r2);
}
+/*
+ * Note, when finished with the device, drop the reference with
+ * put_device() or use the put_cxl_root_decoder helper.
+ */
static struct cxl_root_decoder *
-cxl_find_root_decoder(struct cxl_endpoint_decoder *cxled)
+get_cxl_root_decoder(struct cxl_endpoint_decoder *cxled,
+ struct cxl_region_context *ctx)
{
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
struct cxl_port *port = cxled_to_port(cxled);
struct cxl_root *cxl_root __free(put_cxl_root) = find_cxl_root(port);
- struct cxl_decoder *root, *cxld = &cxled->cxld;
- struct range *hpa_range = &cxld->hpa_range;
+ struct device *cxlrd_dev;
- root = cxl_port_find_switch_decoder(&cxl_root->port, hpa_range);
- if (!root) {
+ cxlrd_dev = device_find_child(&cxl_root->port.dev, &ctx->hpa_range,
+ match_root_decoder);
+ if (!cxlrd_dev) {
dev_err(cxlmd->dev.parent,
"%s:%s no CXL window for range %#llx:%#llx\n",
- dev_name(&cxlmd->dev), dev_name(&cxld->dev),
- hpa_range->start, hpa_range->end);
- return NULL;
+ dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
+ ctx->hpa_range.start, ctx->hpa_range.end);
+ return ERR_PTR(-ENXIO);
}
- return to_cxl_root_decoder(&root->dev);
+ return to_cxl_root_decoder(cxlrd_dev);
}
static int match_region_by_range(struct device *dev, const void *data)
@@ -3703,9 +3700,10 @@ int cxl_add_to_region(struct cxl_endpoint_decoder *cxled)
};
struct cxl_root_decoder *cxlrd __free(put_cxl_root_decoder) =
- cxl_find_root_decoder(cxled);
- if (!cxlrd)
- return -ENXIO;
+ get_cxl_root_decoder(cxled, &ctx);
+
+ if (IS_ERR(cxlrd))
+ return PTR_ERR(cxlrd);
/*
* Ensure that, if multiple threads race to construct_region()
--
2.47.3
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v8 08/13] cxl: Introduce callback for HPA address ranges translation
2025-12-09 18:06 [PATCH v8 00/13] cxl: ACPI PRM Address Translation Support and AMD Zen5 enablement Robert Richter
` (6 preceding siblings ...)
2025-12-09 18:06 ` [PATCH v8 07/13] cxl/region: Use region data to get the root decoder Robert Richter
@ 2025-12-09 18:06 ` Robert Richter
2025-12-09 18:06 ` [PATCH v8 09/13] cxl/acpi: Prepare use of EFI runtime services Robert Richter
` (4 subsequent siblings)
12 siblings, 0 replies; 21+ messages in thread
From: Robert Richter @ 2025-12-09 18:06 UTC (permalink / raw)
To: Alison Schofield, Vishal Verma, Ira Weiny, Dan Williams,
Jonathan Cameron, Dave Jiang, Davidlohr Bueso
Cc: linux-cxl, linux-kernel, Gregory Price, Fabio M. De Francesco,
Terry Bowman, Joshua Hahn, Robert Richter
Introduce a callback to translate an endpoint's HPA range to the
address range of the root port which is the System Physical Address
(SPA) range used by a region. The callback can be set if a platform
needs to handle address translation.
The callback is attached to the root port. An endpoint's root port can
easily be determined in the PCI hierarchy without any CXL specific
knowledge. This allows the early use of address translation for CXL
enumeration. Address translation is esp. needed for the detection of
the root decoders. Thus, the callback is embedded in struct
cxl_root_ops instead of struct cxl_rd_ops.
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Tested-by: Gregory Price <gourry@gourry.net>
Signed-off-by: Robert Richter <rrichter@amd.com>
---
drivers/cxl/core/region.c | 24 ++++++++++++++++++++++++
drivers/cxl/cxl.h | 1 +
2 files changed, 25 insertions(+)
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index e4efb281def4..c7ac78f1b644 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -3480,6 +3480,15 @@ static int match_root_decoder(struct device *dev, const void *data)
return range_contains(r1, r2);
}
+static int cxl_root_setup_translation(struct cxl_root *cxl_root,
+ struct cxl_region_context *ctx)
+{
+ if (!cxl_root->ops.translation_setup_root)
+ return 0;
+
+ return cxl_root->ops.translation_setup_root(cxl_root, ctx);
+}
+
/*
* Note, when finished with the device, drop the reference with
* put_device() or use the put_cxl_root_decoder helper.
@@ -3492,6 +3501,21 @@ get_cxl_root_decoder(struct cxl_endpoint_decoder *cxled,
struct cxl_port *port = cxled_to_port(cxled);
struct cxl_root *cxl_root __free(put_cxl_root) = find_cxl_root(port);
struct device *cxlrd_dev;
+ int rc;
+
+ /*
+ * Adjust the endpoint's HPA range and interleaving
+ * configuration to the root decoder’s memory space before
+ * setting up the root decoder.
+ */
+ rc = cxl_root_setup_translation(cxl_root, ctx);
+ if (rc) {
+ dev_err(cxlmd->dev.parent,
+ "%s:%s Failed to setup translation for address range %#llx:%#llx\n",
+ dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
+ ctx->hpa_range.start, ctx->hpa_range.end);
+ return ERR_PTR(rc);
+ }
cxlrd_dev = device_find_child(&cxl_root->port.dev, &ctx->hpa_range,
match_root_decoder);
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 0e15dc6e169f..8ea334d81edf 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -652,6 +652,7 @@ struct cxl_root_ops {
int (*qos_class)(struct cxl_root *cxl_root,
struct access_coordinate *coord, int entries,
int *qos_class);
+ int (*translation_setup_root)(struct cxl_root *cxl_root, void *data);
};
/**
--
2.47.3
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v8 09/13] cxl/acpi: Prepare use of EFI runtime services
2025-12-09 18:06 [PATCH v8 00/13] cxl: ACPI PRM Address Translation Support and AMD Zen5 enablement Robert Richter
` (7 preceding siblings ...)
2025-12-09 18:06 ` [PATCH v8 08/13] cxl: Introduce callback for HPA address ranges translation Robert Richter
@ 2025-12-09 18:06 ` Robert Richter
2025-12-09 18:06 ` [PATCH v8 10/13] cxl: Enable AMD Zen5 address translation using ACPI PRMT Robert Richter
` (3 subsequent siblings)
12 siblings, 0 replies; 21+ messages in thread
From: Robert Richter @ 2025-12-09 18:06 UTC (permalink / raw)
To: Alison Schofield, Vishal Verma, Ira Weiny, Dan Williams,
Jonathan Cameron, Dave Jiang, Davidlohr Bueso
Cc: linux-cxl, linux-kernel, Gregory Price, Fabio M. De Francesco,
Terry Bowman, Joshua Hahn, Robert Richter
In order to use EFI runtime services, esp. ACPI PRM which uses the
efi_rts_wq workqueue, initialize EFI before CXL ACPI.
There is a subsys_initcall order dependency if driver is builtin:
subsys_initcall(cxl_acpi_init);
subsys_initcall(efisubsys_init);
Prevent the efi_rts_wq workqueue being used by cxl_acpi_init() before
its allocation. Use subsys_initcall_sync(cxl_acpi_init) to always run
efisubsys_init() first.
Reported-by: Gregory Price <gourry@gourry.net>
Tested-by: Joshua Hahn <joshua.hahnjy@gmail.com>
Reviewed-by: Joshua Hahn <joshua.hahnjy@gmail.com>
Reviewed-by: Gregory Price <gourry@gourry.net>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Tested-by: Gregory Price <gourry@gourry.net>
Signed-off-by: Robert Richter <rrichter@amd.com>
---
drivers/cxl/acpi.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
index b4bed40ef7c0..a31d0f97f916 100644
--- a/drivers/cxl/acpi.c
+++ b/drivers/cxl/acpi.c
@@ -1005,8 +1005,12 @@ static void __exit cxl_acpi_exit(void)
cxl_bus_drain();
}
-/* load before dax_hmem sees 'Soft Reserved' CXL ranges */
-subsys_initcall(cxl_acpi_init);
+/*
+ * Load before dax_hmem sees 'Soft Reserved' CXL ranges. Use
+ * subsys_initcall_sync() since there is an order dependency with
+ * subsys_initcall(efisubsys_init), which must run first.
+ */
+subsys_initcall_sync(cxl_acpi_init);
/*
* Arrange for host-bridge ports to be active synchronous with
--
2.47.3
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v8 10/13] cxl: Enable AMD Zen5 address translation using ACPI PRMT
2025-12-09 18:06 [PATCH v8 00/13] cxl: ACPI PRM Address Translation Support and AMD Zen5 enablement Robert Richter
` (8 preceding siblings ...)
2025-12-09 18:06 ` [PATCH v8 09/13] cxl/acpi: Prepare use of EFI runtime services Robert Richter
@ 2025-12-09 18:06 ` Robert Richter
2025-12-09 18:06 ` [PATCH v8 11/13] cxl/atl: Lock decoders that need address translation Robert Richter
` (2 subsequent siblings)
12 siblings, 0 replies; 21+ messages in thread
From: Robert Richter @ 2025-12-09 18:06 UTC (permalink / raw)
To: Alison Schofield, Vishal Verma, Ira Weiny, Dan Williams,
Jonathan Cameron, Dave Jiang, Davidlohr Bueso
Cc: linux-cxl, linux-kernel, Gregory Price, Fabio M. De Francesco,
Terry Bowman, Joshua Hahn, Robert Richter
Add AMD Zen5 support for address translation.
Zen5 systems may be configured to use 'Normalized addresses'. Then,
host physical addresses (HPA) are different from their system physical
addresses (SPA). The endpoint has its own physical address space and
an incoming HPA is already converted to the device's physical address
(DPA). Thus it has interleaving disabled and CXL endpoints are
programmed passthrough (DPA == HPA).
Host Physical Addresses (HPAs) need to be translated from the endpoint
to its CXL host bridge, esp. to identify the endpoint's root decoder
and region's address range. ACPI Platform Runtime Mechanism (PRM)
provides a handler to translate the DPA to its SPA. This is documented
in:
AMD Family 1Ah Models 00h–0Fh and Models 10h–1Fh
ACPI v6.5 Porting Guide, Publication # 58088
https://www.amd.com/en/search/documentation/hub.html
With Normalized Addressing this PRM handler must be used to translate
an HPA of an endpoint to its SPA.
Do the following to implement AMD Zen5 address translation:
Introduce a new file core/atl.c to handle ACPI PRM specific address
translation code. Naming is loosely related to the kernel's AMD
Address Translation Library (CONFIG_AMD_ATL) but implementation does
not depend on it, nor it is vendor specific. Use Kbuild and Kconfig
options respectively to enable the code depending on architecture and
platform options.
AMD Zen5 systems support the ACPI PRM CXL Address Translation firmware
call (see ACPI v6.5 Porting Guide, Address Translation - CXL DPA to
System Physical Address). Firmware enables the PRM handler if the
platform has address translation implemented. Check firmware and
kernel support of ACPI PRM using the specific GUID. On success enable
address translation by setting up the earlier introduced root port
callback, see function cxl_prm_setup_translation(). Setup is done in
cxl_setup_prm_address_translation(), it is the only function that
needs to be exported. For low level PRM firmware calls, use the ACPI
framework.
Identify the region's interleaving ways by inspecting the address
ranges. Also determine the interleaving granularity using the address
translation callback. Note that the position of the chunk from one
interleaving block to the next may vary and thus cannot be considered
constant. Address offsets larger than the interleaving block size
cannot be used to calculate the granularity. Thus, probe the
granularity using address translation for various HPAs in the same
interleaving block.
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Tested-by: Gregory Price <gourry@gourry.net>
Signed-off-by: Robert Richter <rrichter@amd.com>
---
drivers/cxl/Kconfig | 5 +
drivers/cxl/acpi.c | 2 +
drivers/cxl/core/Makefile | 1 +
drivers/cxl/core/atl.c | 197 ++++++++++++++++++++++++++++++++++++++
drivers/cxl/cxl.h | 7 ++
5 files changed, 212 insertions(+)
create mode 100644 drivers/cxl/core/atl.c
diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig
index 48b7314afdb8..103950a9b73e 100644
--- a/drivers/cxl/Kconfig
+++ b/drivers/cxl/Kconfig
@@ -233,4 +233,9 @@ config CXL_MCE
def_bool y
depends on X86_MCE && MEMORY_FAILURE
+config CXL_ATL
+ def_bool y
+ depends on CXL_REGION
+ depends on ACPI_PRMT && AMD_NB
+
endif
diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
index a31d0f97f916..50c2987e0459 100644
--- a/drivers/cxl/acpi.c
+++ b/drivers/cxl/acpi.c
@@ -925,6 +925,8 @@ static int cxl_acpi_probe(struct platform_device *pdev)
cxl_root->ops.qos_class = cxl_acpi_qos_class;
root_port = &cxl_root->port;
+ cxl_setup_prm_address_translation(cxl_root);
+
rc = bus_for_each_dev(adev->dev.bus, NULL, root_port,
add_host_bridge_dport);
if (rc < 0)
diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile
index 5ad8fef210b5..11fe272a6e29 100644
--- a/drivers/cxl/core/Makefile
+++ b/drivers/cxl/core/Makefile
@@ -20,3 +20,4 @@ cxl_core-$(CONFIG_CXL_REGION) += region.o
cxl_core-$(CONFIG_CXL_MCE) += mce.o
cxl_core-$(CONFIG_CXL_FEATURES) += features.o
cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += edac.o
+cxl_core-$(CONFIG_CXL_ATL) += atl.o
diff --git a/drivers/cxl/core/atl.c b/drivers/cxl/core/atl.c
new file mode 100644
index 000000000000..8293a55127cb
--- /dev/null
+++ b/drivers/cxl/core/atl.c
@@ -0,0 +1,197 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/prmt.h>
+#include <linux/pci.h>
+#include <linux/acpi.h>
+
+#include <cxlmem.h>
+#include "core.h"
+
+/*
+ * PRM Address Translation - CXL DPA to System Physical Address
+ *
+ * Reference:
+ *
+ * AMD Family 1Ah Models 00h–0Fh and Models 10h–1Fh
+ * ACPI v6.5 Porting Guide, Publication # 58088
+ */
+
+static const guid_t prm_cxl_dpa_spa_guid =
+ GUID_INIT(0xee41b397, 0x25d4, 0x452c, 0xad, 0x54, 0x48, 0xc6, 0xe3,
+ 0x48, 0x0b, 0x94);
+
+struct prm_cxl_dpa_spa_data {
+ u64 dpa;
+ u8 reserved;
+ u8 devfn;
+ u8 bus;
+ u8 segment;
+ u64 *spa;
+} __packed;
+
+static u64 prm_cxl_dpa_spa(struct pci_dev *pci_dev, u64 dpa)
+{
+ struct prm_cxl_dpa_spa_data data;
+ u64 spa;
+ int rc;
+
+ data = (struct prm_cxl_dpa_spa_data) {
+ .dpa = dpa,
+ .devfn = pci_dev->devfn,
+ .bus = pci_dev->bus->number,
+ .segment = pci_domain_nr(pci_dev->bus),
+ .spa = &spa,
+ };
+
+ rc = acpi_call_prm_handler(prm_cxl_dpa_spa_guid, &data);
+ if (rc) {
+ pci_dbg(pci_dev, "failed to get SPA for %#llx: %d\n", dpa, rc);
+ return ULLONG_MAX;
+ }
+
+ pci_dbg(pci_dev, "PRM address translation: DPA -> SPA: %#llx -> %#llx\n", dpa, spa);
+
+ return spa;
+}
+
+static int cxl_prm_setup_root(struct cxl_root *cxl_root, void *data)
+{
+ struct cxl_region_context *ctx = data;
+ struct cxl_endpoint_decoder *cxled = ctx->cxled;
+ struct cxl_decoder *cxld = &cxled->cxld;
+ struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
+ struct range hpa_range = ctx->hpa_range;
+ struct pci_dev *pci_dev;
+ u64 spa_len, len = range_len(&hpa_range);
+ u64 addr, base_spa, base;
+ int ways, gran;
+
+ /*
+ * When Normalized Addressing is enabled, the endpoint maintains a 1:1
+ * mapping between HPA and DPA. If disabled, skip address translation
+ * and perform only a range check.
+ */
+ if (hpa_range.start != cxled->dpa_res->start)
+ return 0;
+
+ if (!IS_ALIGNED(hpa_range.start, SZ_256M) ||
+ !IS_ALIGNED(hpa_range.end + 1, SZ_256M)) {
+ dev_dbg(cxld->dev.parent,
+ "CXL address translation: Unaligned decoder HPA range: %#llx-%#llx(%s)\n",
+ hpa_range.start, hpa_range.end, dev_name(&cxld->dev));
+ return -ENXIO;
+ }
+
+ /*
+ * Endpoints are programmed passthrough in Normalized Addressing mode.
+ */
+ if (ctx->interleave_ways != 1) {
+ dev_dbg(&cxld->dev, "unexpected interleaving config: ways: %d granularity: %d\n",
+ ctx->interleave_ways, ctx->interleave_granularity);
+ return -ENXIO;
+ }
+
+ if (!cxlmd || !dev_is_pci(cxlmd->dev.parent)) {
+ dev_dbg(&cxld->dev, "No endpoint found: %s, range %#llx-%#llx\n",
+ dev_name(cxld->dev.parent), hpa_range.start,
+ hpa_range.end);
+ return -ENXIO;
+ }
+
+ pci_dev = to_pci_dev(cxlmd->dev.parent);
+
+ /* Translate HPA range to SPA. */
+ base = hpa_range.start;
+ hpa_range.start = prm_cxl_dpa_spa(pci_dev, hpa_range.start);
+ hpa_range.end = prm_cxl_dpa_spa(pci_dev, hpa_range.end);
+ base_spa = hpa_range.start;
+
+ if (hpa_range.start == ULLONG_MAX || hpa_range.end == ULLONG_MAX) {
+ dev_dbg(cxld->dev.parent,
+ "CXL address translation: Failed to translate HPA range: %#llx-%#llx:%#llx-%#llx(%s)\n",
+ hpa_range.start, hpa_range.end, ctx->hpa_range.start,
+ ctx->hpa_range.end, dev_name(&cxld->dev));
+ return -ENXIO;
+ }
+
+ /*
+ * Since translated addresses include the interleaving offsets, align
+ * the range to 256 MB.
+ */
+ hpa_range.start = ALIGN_DOWN(hpa_range.start, SZ_256M);
+ hpa_range.end = ALIGN(hpa_range.end, SZ_256M) - 1;
+
+ spa_len = range_len(&hpa_range);
+ if (!len || !spa_len || spa_len % len) {
+ dev_dbg(cxld->dev.parent,
+ "CXL address translation: HPA range not contiguous: %#llx-%#llx:%#llx-%#llx(%s)\n",
+ hpa_range.start, hpa_range.end, ctx->hpa_range.start,
+ ctx->hpa_range.end, dev_name(&cxld->dev));
+ return -ENXIO;
+ }
+
+ ways = spa_len / len;
+ gran = SZ_256;
+
+ /*
+ * Determine interleave granularity
+ *
+ * Note: The position of the chunk from one interleaving block to the
+ * next may vary and thus cannot be considered constant. Address offsets
+ * larger than the interleaving block size cannot be used to calculate
+ * the granularity.
+ */
+ if (ways > 1) {
+ while (gran <= SZ_16M) {
+ addr = prm_cxl_dpa_spa(pci_dev, base + gran);
+ if (addr != base_spa + gran)
+ break;
+ gran <<= 1;
+ }
+ }
+
+ if (gran > SZ_16M) {
+ dev_dbg(cxld->dev.parent,
+ "CXL address translation: Cannot determine granularity: %#llx-%#llx:%#llx-%#llx(%s)\n",
+ hpa_range.start, hpa_range.end, ctx->hpa_range.start,
+ ctx->hpa_range.end, dev_name(&cxld->dev));
+ return -ENXIO;
+ }
+
+ ctx->hpa_range = hpa_range;
+ ctx->interleave_ways = ways;
+ ctx->interleave_granularity = gran;
+
+ dev_dbg(&cxld->dev,
+ "address mapping found for %s (hpa -> spa): %#llx+%#llx -> %#llx+%#llx ways:%d granularity:%d\n",
+ dev_name(cxlmd->dev.parent), base, len, hpa_range.start,
+ spa_len, ways, gran);
+
+ return 0;
+}
+
+void cxl_setup_prm_address_translation(struct cxl_root *cxl_root)
+{
+ struct device *host = cxl_root->port.uport_dev;
+ u64 spa;
+ struct prm_cxl_dpa_spa_data data = { .spa = &spa };
+ int rc;
+
+ /*
+ * Applies only to PCIe Host Bridges which are children of the CXL Root
+ * Device (HID=“ACPI0017”). Check this and drop cxl_test instances.
+ */
+ if (!acpi_match_device(host->driver->acpi_match_table, host))
+ return;
+
+ /* Check kernel (-EOPNOTSUPP) and firmware support (-ENODEV) */
+ rc = acpi_call_prm_handler(prm_cxl_dpa_spa_guid, &data);
+ if (rc == -EOPNOTSUPP || rc == -ENODEV)
+ return;
+
+ cxl_root->ops.translation_setup_root = cxl_prm_setup_root;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_setup_prm_address_translation, "CXL");
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 8ea334d81edf..20b0fd43fa7b 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -817,6 +817,13 @@ static inline void cxl_dport_init_ras_reporting(struct cxl_dport *dport,
struct device *host) { }
#endif
+#ifdef CONFIG_CXL_ATL
+void cxl_setup_prm_address_translation(struct cxl_root *cxl_root);
+#else
+static inline
+void cxl_setup_prm_address_translation(struct cxl_root *cxl_root) {}
+#endif
+
struct cxl_decoder *to_cxl_decoder(struct device *dev);
struct cxl_root_decoder *to_cxl_root_decoder(struct device *dev);
struct cxl_switch_decoder *to_cxl_switch_decoder(struct device *dev);
--
2.47.3
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v8 11/13] cxl/atl: Lock decoders that need address translation
2025-12-09 18:06 [PATCH v8 00/13] cxl: ACPI PRM Address Translation Support and AMD Zen5 enablement Robert Richter
` (9 preceding siblings ...)
2025-12-09 18:06 ` [PATCH v8 10/13] cxl: Enable AMD Zen5 address translation using ACPI PRMT Robert Richter
@ 2025-12-09 18:06 ` Robert Richter
2025-12-09 18:06 ` [PATCH v8 12/13] cxl: Check if ULLONG_MAX was returned from translation functions Robert Richter
2025-12-09 18:06 ` [PATCH v8 13/13] cxl: Disable HPA/SPA translation handlers for Normalized addressing Robert Richter
12 siblings, 0 replies; 21+ messages in thread
From: Robert Richter @ 2025-12-09 18:06 UTC (permalink / raw)
To: Alison Schofield, Vishal Verma, Ira Weiny, Dan Williams,
Jonathan Cameron, Dave Jiang, Davidlohr Bueso
Cc: linux-cxl, linux-kernel, Gregory Price, Fabio M. De Francesco,
Terry Bowman, Joshua Hahn, Robert Richter
The current kernel implementation does not support endpoint setup with
Normalized Addressing. It only translates an endpoint's DPA to the SPA
range of the host bridge. Therefore, the endpoint address range cannot
be determined, making a non-auto setup impossible. If a decoder
requires address translation, reprogramming should be disabled and the
decoder locked.
The BIOS, however, provides all the necessary address translation
data, which the kernel can use to reconfigure endpoint decoders with
normalized addresses. Locking the decoders in the BIOS would prevent a
capable kernel (or other operating systems) from shutting down
auto-generated regions and managing resources dynamically.
Reviewed-by: Gregory Price <gourry@gourry.net>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Tested-by: Gregory Price <gourry@gourry.net>
Signed-off-by: Robert Richter <rrichter@amd.com>
---
drivers/cxl/core/atl.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/drivers/cxl/core/atl.c b/drivers/cxl/core/atl.c
index 8293a55127cb..4bd2289b8bbb 100644
--- a/drivers/cxl/core/atl.c
+++ b/drivers/cxl/core/atl.c
@@ -161,6 +161,24 @@ static int cxl_prm_setup_root(struct cxl_root *cxl_root, void *data)
return -ENXIO;
}
+ /*
+ * The current kernel implementation does not support endpoint
+ * setup with Normalized Addressing. It only translates an
+ * endpoint's DPA to the SPA range of the host bridge.
+ * Therefore, the endpoint address range cannot be determined,
+ * making a non-auto setup impossible. If a decoder requires
+ * address translation, reprogramming should be disabled and
+ * the decoder locked.
+ *
+ * The BIOS, however, provides all the necessary address
+ * translation data, which the kernel can use to reconfigure
+ * endpoint decoders with normalized addresses. Locking the
+ * decoders in the BIOS would prevent a capable kernel (or
+ * other operating systems) from shutting down auto-generated
+ * regions and managing resources dynamically.
+ */
+ cxld->flags |= CXL_DECODER_F_LOCK;
+
ctx->hpa_range = hpa_range;
ctx->interleave_ways = ways;
ctx->interleave_granularity = gran;
--
2.47.3
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v8 12/13] cxl: Check if ULLONG_MAX was returned from translation functions
2025-12-09 18:06 [PATCH v8 00/13] cxl: ACPI PRM Address Translation Support and AMD Zen5 enablement Robert Richter
` (10 preceding siblings ...)
2025-12-09 18:06 ` [PATCH v8 11/13] cxl/atl: Lock decoders that need address translation Robert Richter
@ 2025-12-09 18:06 ` Robert Richter
2025-12-10 16:23 ` Dave Jiang
` (3 more replies)
2025-12-09 18:06 ` [PATCH v8 13/13] cxl: Disable HPA/SPA translation handlers for Normalized addressing Robert Richter
12 siblings, 4 replies; 21+ messages in thread
From: Robert Richter @ 2025-12-09 18:06 UTC (permalink / raw)
To: Alison Schofield, Vishal Verma, Ira Weiny, Dan Williams,
Jonathan Cameron, Dave Jiang, Davidlohr Bueso
Cc: linux-cxl, linux-kernel, Gregory Price, Fabio M. De Francesco,
Terry Bowman, Joshua Hahn, Robert Richter
The return address of translation functions is not consistently
checked for a valid address. Check if ULLONG_MAX was returned.
Signed-off-by: Robert Richter <rrichter@amd.com>
---
drivers/cxl/core/hdm.c | 2 +-
drivers/cxl/core/region.c | 36 +++++++++++++++++++-------
tools/testing/cxl/test/cxl_translate.c | 4 +--
3 files changed, 29 insertions(+), 13 deletions(-)
diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index 1c5d2022c87a..8b50cdce4b29 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -530,7 +530,7 @@ resource_size_t cxl_dpa_size(struct cxl_endpoint_decoder *cxled)
resource_size_t cxl_dpa_resource_start(struct cxl_endpoint_decoder *cxled)
{
- resource_size_t base = -1;
+ resource_size_t base = ULLONG_MAX;
lockdep_assert_held(&cxl_rwsem.dpa);
if (cxled->dpa_res)
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index c7ac78f1b644..2c070c7c7bfe 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -3124,7 +3124,7 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
struct cxl_region_params *p = &cxlr->params;
struct cxl_endpoint_decoder *cxled = NULL;
- u64 dpa_offset, hpa_offset, hpa;
+ u64 base, dpa_offset, hpa_offset, hpa;
u16 eig = 0;
u8 eiw = 0;
int pos;
@@ -3142,8 +3142,14 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
ways_to_eiw(p->interleave_ways, &eiw);
granularity_to_eig(p->interleave_granularity, &eig);
- dpa_offset = dpa - cxl_dpa_resource_start(cxled);
+ base = cxl_dpa_resource_start(cxled);
+ if (base == ULLONG_MAX)
+ return ULLONG_MAX;
+
+ dpa_offset = dpa - base;
hpa_offset = cxl_calculate_hpa_offset(dpa_offset, pos, eiw, eig);
+ if (hpa_offset == ULLONG_MAX)
+ return ULLONG_MAX;
/* Apply the hpa_offset to the region base address */
hpa = hpa_offset + p->res->start + p->cache_size;
@@ -3152,6 +3158,9 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
if (cxlrd->ops.hpa_to_spa)
hpa = cxlrd->ops.hpa_to_spa(cxlrd, hpa);
+ if (hpa == ULLONG_MAX)
+ return ULLONG_MAX;
+
if (!cxl_resource_contains_addr(p->res, hpa)) {
dev_dbg(&cxlr->dev,
"Addr trans fail: hpa 0x%llx not in region\n", hpa);
@@ -3176,10 +3185,11 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
struct cxl_region_params *p = &cxlr->params;
struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
struct cxl_endpoint_decoder *cxled;
- u64 hpa, hpa_offset, dpa_offset;
+ u64 hpa_offset = offset;
+ u64 dpa_base, dpa_offset;
u16 eig = 0;
u8 eiw = 0;
- int pos;
+ int pos = -1;
lockdep_assert_held(&cxl_rwsem.region);
lockdep_assert_held(&cxl_rwsem.dpa);
@@ -3193,13 +3203,14 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
* CXL HPA is assumed to equal SPA.
*/
if (cxlrd->ops.spa_to_hpa) {
- hpa = cxlrd->ops.spa_to_hpa(cxlrd, p->res->start + offset);
- hpa_offset = hpa - p->res->start;
- } else {
- hpa_offset = offset;
+ hpa_offset = cxlrd->ops.spa_to_hpa(cxlrd, p->res->start + offset);
+ if (hpa_offset != ULLONG_MAX)
+ hpa_offset -= p->res->start;
}
- pos = cxl_calculate_position(hpa_offset, eiw, eig);
+ if (hpa_offset != ULLONG_MAX)
+ 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);
@@ -3213,8 +3224,13 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
cxled = p->targets[i];
if (cxled->pos != pos)
continue;
+
+ dpa_base = cxl_dpa_resource_start(cxled);
+ if (dpa_base == ULLONG_MAX)
+ continue;
+
result->cxlmd = cxled_to_memdev(cxled);
- result->dpa = cxl_dpa_resource_start(cxled) + dpa_offset;
+ result->dpa = dpa_base + dpa_offset;
return 0;
}
diff --git a/tools/testing/cxl/test/cxl_translate.c b/tools/testing/cxl/test/cxl_translate.c
index 2200ae21795c..66f8270aacd8 100644
--- a/tools/testing/cxl/test/cxl_translate.c
+++ b/tools/testing/cxl/test/cxl_translate.c
@@ -69,7 +69,7 @@ static u64 to_hpa(u64 dpa_offset, int pos, u8 r_eiw, u16 r_eig, u8 hb_ways,
/* 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) {
+ if (hpa_offset != ULLONG_MAX && 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);
@@ -262,7 +262,7 @@ static int test_random_params(void)
reverse_dpa = cxl_calculate_dpa_offset(hpa, eiw, eig);
reverse_pos = cxl_calculate_position(hpa, eiw, eig);
- if (reverse_dpa != dpa || reverse_pos != pos) {
+ if (hpa == ULLONG_MAX || 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);
--
2.47.3
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v8 13/13] cxl: Disable HPA/SPA translation handlers for Normalized addressing
2025-12-09 18:06 [PATCH v8 00/13] cxl: ACPI PRM Address Translation Support and AMD Zen5 enablement Robert Richter
` (11 preceding siblings ...)
2025-12-09 18:06 ` [PATCH v8 12/13] cxl: Check if ULLONG_MAX was returned from translation functions Robert Richter
@ 2025-12-09 18:06 ` Robert Richter
2025-12-10 17:44 ` Dave Jiang
` (2 more replies)
12 siblings, 3 replies; 21+ messages in thread
From: Robert Richter @ 2025-12-09 18:06 UTC (permalink / raw)
To: Alison Schofield, Vishal Verma, Ira Weiny, Dan Williams,
Jonathan Cameron, Dave Jiang, Davidlohr Bueso
Cc: linux-cxl, linux-kernel, Gregory Price, Fabio M. De Francesco,
Terry Bowman, Joshua Hahn, Robert Richter
The root decoder implements callbacks hpa_to_spa and spa_to_hpa to
perform Host Physical Address (HPA) and System Physical Address
translations respectively. The callbacks are needed in cases where HPA
!= SPA to convert addresses for tracing and error handling and to
setup Poison injection. Currently this is used for XOR interleaving.
In AMD Zen5 systems with Normalized addressing, the addresses of
endpoints are not SPA and those callbacks need to be implemented.
Now, as ACPI PRM translation could be expensive in tracing or error
handling code paths, do not yet enable this direction of translation
(hpa_to_spa) to avoid its intensive use. Instead, mark the HPA invalid
and return an error for this case.
The spa_to_hpa callback will be used in region_offset_to_dpa_result()
to determine the endpoint by the position in the interleaving
chunk. With Normalized addressing, the order of endpoints in the
interleaving chunk is implementation defined. Do not use this approach
and and return an error instead.
Disable both HPA/SPA translation handlers for Normalized addressing by
returning an error (ULLONG_MAX).
Signed-off-by: Robert Richter <rrichter@amd.com>
---
drivers/cxl/core/atl.c | 35 +++++++++++++++++++++++++++++++++++
drivers/cxl/core/region.c | 21 ++++++++++++++++++++-
drivers/cxl/cxl.h | 1 +
3 files changed, 56 insertions(+), 1 deletion(-)
diff --git a/drivers/cxl/core/atl.c b/drivers/cxl/core/atl.c
index 4bd2289b8bbb..9d05a83bde73 100644
--- a/drivers/cxl/core/atl.c
+++ b/drivers/cxl/core/atl.c
@@ -57,6 +57,39 @@ static u64 prm_cxl_dpa_spa(struct pci_dev *pci_dev, u64 dpa)
return spa;
}
+static u64 atl_hpa_to_spa(struct cxl_root_decoder *cxlrd, u64 hpa)
+{
+ /*
+ * PRM translation could be expensive in tracing or error
+ * handling code paths. Avoid this for now and return an
+ * error instead.
+ */
+
+ return ULLONG_MAX;
+}
+
+static u64 atl_spa_to_hpa(struct cxl_root_decoder *cxlrd, u64 spa)
+{
+ /*
+ * The callback will be used in region_offset_to_dpa_result()
+ * to determine the endpoint by the position in the
+ * interleaving chunk. With Normalized addressing, the order
+ * of endpoints in the interleaving chunk is implementation
+ * defined. Do not use this approach and and return an error
+ * instead.
+ */
+
+ return ULLONG_MAX;
+}
+
+static int cxl_prm_setup_region(struct cxl_region *cxlr)
+{
+ cxlr->cxlrd->ops.hpa_to_spa = atl_hpa_to_spa;
+ cxlr->cxlrd->ops.spa_to_hpa = atl_spa_to_hpa;
+
+ return 0;
+}
+
static int cxl_prm_setup_root(struct cxl_root *cxl_root, void *data)
{
struct cxl_region_context *ctx = data;
@@ -183,6 +216,8 @@ static int cxl_prm_setup_root(struct cxl_root *cxl_root, void *data)
ctx->interleave_ways = ways;
ctx->interleave_granularity = gran;
+ cxl_root->ops.translation_setup_region = cxl_prm_setup_region;
+
dev_dbg(&cxld->dev,
"address mapping found for %s (hpa -> spa): %#llx+%#llx -> %#llx+%#llx ways:%d granularity:%d\n",
dev_name(cxlmd->dev.parent), base, len, hpa_range.start,
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 2c070c7c7bfe..130af1cf95b7 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -623,6 +623,21 @@ static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_RO(mode);
+static int cxl_region_setup_translation(struct cxl_region *cxlr)
+{
+ struct cxl_port *root = to_cxl_port(cxlr->dev.parent->parent);
+ struct cxl_root *cxl_root;
+
+ if (!root || !is_cxl_root(root))
+ return 0;
+
+ cxl_root = to_cxl_root(root);
+ if (!cxl_root || !cxl_root->ops.translation_setup_region)
+ return 0;
+
+ return cxl_root->ops.translation_setup_region(cxlr);
+}
+
static int alloc_hpa(struct cxl_region *cxlr, resource_size_t size)
{
struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
@@ -666,7 +681,7 @@ static int alloc_hpa(struct cxl_region *cxlr, resource_size_t size)
p->res = res;
p->state = CXL_CONFIG_INTERLEAVE_ACTIVE;
- return 0;
+ return cxl_region_setup_translation(cxlr);
}
static void cxl_region_iomem_release(struct cxl_region *cxlr)
@@ -3663,6 +3678,10 @@ static int __construct_region(struct cxl_region *cxlr,
p->interleave_granularity = ctx->interleave_granularity;
p->state = CXL_CONFIG_INTERLEAVE_ACTIVE;
+ rc = cxl_region_setup_translation(cxlr);
+ if (rc)
+ return rc;
+
rc = sysfs_update_group(&cxlr->dev.kobj, get_cxl_region_target_group());
if (rc)
return rc;
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 20b0fd43fa7b..f4623728a815 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -653,6 +653,7 @@ struct cxl_root_ops {
struct access_coordinate *coord, int entries,
int *qos_class);
int (*translation_setup_root)(struct cxl_root *cxl_root, void *data);
+ int (*translation_setup_region)(struct cxl_region *cxlr);
};
/**
--
2.47.3
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH v8 12/13] cxl: Check if ULLONG_MAX was returned from translation functions
2025-12-09 18:06 ` [PATCH v8 12/13] cxl: Check if ULLONG_MAX was returned from translation functions Robert Richter
@ 2025-12-10 16:23 ` Dave Jiang
2025-12-11 19:50 ` kernel test robot
` (2 subsequent siblings)
3 siblings, 0 replies; 21+ messages in thread
From: Dave Jiang @ 2025-12-10 16:23 UTC (permalink / raw)
To: Robert Richter, Alison Schofield, Vishal Verma, Ira Weiny,
Dan Williams, Jonathan Cameron, Davidlohr Bueso
Cc: linux-cxl, linux-kernel, Gregory Price, Fabio M. De Francesco,
Terry Bowman, Joshua Hahn
On 12/9/25 11:06 AM, Robert Richter wrote:
> The return address of translation functions is not consistently
> checked for a valid address. Check if ULLONG_MAX was returned.
>
> Signed-off-by: Robert Richter <rrichter@amd.com>
> ---
> drivers/cxl/core/hdm.c | 2 +-
> drivers/cxl/core/region.c | 36 +++++++++++++++++++-------
> tools/testing/cxl/test/cxl_translate.c | 4 +--
> 3 files changed, 29 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
> index 1c5d2022c87a..8b50cdce4b29 100644
> --- a/drivers/cxl/core/hdm.c
> +++ b/drivers/cxl/core/hdm.c
> @@ -530,7 +530,7 @@ resource_size_t cxl_dpa_size(struct cxl_endpoint_decoder *cxled)
>
> resource_size_t cxl_dpa_resource_start(struct cxl_endpoint_decoder *cxled)
> {
> - resource_size_t base = -1;
> + resource_size_t base = ULLONG_MAX;
>
> lockdep_assert_held(&cxl_rwsem.dpa);
> if (cxled->dpa_res)
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index c7ac78f1b644..2c070c7c7bfe 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -3124,7 +3124,7 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
> struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
> struct cxl_region_params *p = &cxlr->params;
> struct cxl_endpoint_decoder *cxled = NULL;
> - u64 dpa_offset, hpa_offset, hpa;
> + u64 base, dpa_offset, hpa_offset, hpa;
> u16 eig = 0;
> u8 eiw = 0;
> int pos;
> @@ -3142,8 +3142,14 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
> ways_to_eiw(p->interleave_ways, &eiw);
> granularity_to_eig(p->interleave_granularity, &eig);
>
> - dpa_offset = dpa - cxl_dpa_resource_start(cxled);
> + base = cxl_dpa_resource_start(cxled);
> + if (base == ULLONG_MAX)
> + return ULLONG_MAX;
> +
> + dpa_offset = dpa - base;
> hpa_offset = cxl_calculate_hpa_offset(dpa_offset, pos, eiw, eig);
> + if (hpa_offset == ULLONG_MAX)
> + return ULLONG_MAX;
>
> /* Apply the hpa_offset to the region base address */
> hpa = hpa_offset + p->res->start + p->cache_size;
> @@ -3152,6 +3158,9 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
> if (cxlrd->ops.hpa_to_spa)
> hpa = cxlrd->ops.hpa_to_spa(cxlrd, hpa);
>
> + if (hpa == ULLONG_MAX)
> + return ULLONG_MAX;
> +
> if (!cxl_resource_contains_addr(p->res, hpa)) {
> dev_dbg(&cxlr->dev,
> "Addr trans fail: hpa 0x%llx not in region\n", hpa);
> @@ -3176,10 +3185,11 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
> struct cxl_region_params *p = &cxlr->params;
> struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
> struct cxl_endpoint_decoder *cxled;
> - u64 hpa, hpa_offset, dpa_offset;
> + u64 hpa_offset = offset;
> + u64 dpa_base, dpa_offset;
> u16 eig = 0;
> u8 eiw = 0;
> - int pos;
> + int pos = -1;
>
> lockdep_assert_held(&cxl_rwsem.region);
> lockdep_assert_held(&cxl_rwsem.dpa);
> @@ -3193,13 +3203,14 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
> * CXL HPA is assumed to equal SPA.
> */
> if (cxlrd->ops.spa_to_hpa) {
> - hpa = cxlrd->ops.spa_to_hpa(cxlrd, p->res->start + offset);
> - hpa_offset = hpa - p->res->start;
> - } else {
> - hpa_offset = offset;
> + hpa_offset = cxlrd->ops.spa_to_hpa(cxlrd, p->res->start + offset);
> + if (hpa_offset != ULLONG_MAX)
Should it just return error here when hpa_offset == ULLONG_MAX?
> + hpa_offset -= p->res->start;
> }>
> - pos = cxl_calculate_position(hpa_offset, eiw, eig);
> + if (hpa_offset != ULLONG_MAX)
And this check won't be need if it returned earlier above
> + 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);
> @@ -3213,8 +3224,13 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
> cxled = p->targets[i];
> if (cxled->pos != pos)
> continue;
> +
> + dpa_base = cxl_dpa_resource_start(cxled);
> + if (dpa_base == ULLONG_MAX)
> + continue;
If dpa_base is ULLONG_MAX, should it be an error and exit instead of continuing?
DJ
> +
> result->cxlmd = cxled_to_memdev(cxled);
> - result->dpa = cxl_dpa_resource_start(cxled) + dpa_offset;
> + result->dpa = dpa_base + dpa_offset;
>
> return 0;
> }
> diff --git a/tools/testing/cxl/test/cxl_translate.c b/tools/testing/cxl/test/cxl_translate.c
> index 2200ae21795c..66f8270aacd8 100644
> --- a/tools/testing/cxl/test/cxl_translate.c
> +++ b/tools/testing/cxl/test/cxl_translate.c
> @@ -69,7 +69,7 @@ static u64 to_hpa(u64 dpa_offset, int pos, u8 r_eiw, u16 r_eig, u8 hb_ways,
> /* 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) {
> + if (hpa_offset != ULLONG_MAX && 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);
> @@ -262,7 +262,7 @@ static int test_random_params(void)
> reverse_dpa = cxl_calculate_dpa_offset(hpa, eiw, eig);
> reverse_pos = cxl_calculate_position(hpa, eiw, eig);
>
> - if (reverse_dpa != dpa || reverse_pos != pos) {
> + if (hpa == ULLONG_MAX || 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);
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v8 13/13] cxl: Disable HPA/SPA translation handlers for Normalized addressing
2025-12-09 18:06 ` [PATCH v8 13/13] cxl: Disable HPA/SPA translation handlers for Normalized addressing Robert Richter
@ 2025-12-10 17:44 ` Dave Jiang
2025-12-15 13:25 ` Jonathan Cameron
2025-12-16 5:21 ` Alison Schofield
2 siblings, 0 replies; 21+ messages in thread
From: Dave Jiang @ 2025-12-10 17:44 UTC (permalink / raw)
To: Robert Richter, Alison Schofield, Vishal Verma, Ira Weiny,
Dan Williams, Jonathan Cameron, Davidlohr Bueso
Cc: linux-cxl, linux-kernel, Gregory Price, Fabio M. De Francesco,
Terry Bowman, Joshua Hahn
On 12/9/25 11:06 AM, Robert Richter wrote:
> The root decoder implements callbacks hpa_to_spa and spa_to_hpa to
> perform Host Physical Address (HPA) and System Physical Address
> translations respectively. The callbacks are needed in cases where HPA
> != SPA to convert addresses for tracing and error handling and to
> setup Poison injection. Currently this is used for XOR interleaving.
>
> In AMD Zen5 systems with Normalized addressing, the addresses of
> endpoints are not SPA and those callbacks need to be implemented.
>
> Now, as ACPI PRM translation could be expensive in tracing or error
> handling code paths, do not yet enable this direction of translation
> (hpa_to_spa) to avoid its intensive use. Instead, mark the HPA invalid
> and return an error for this case.
>
> The spa_to_hpa callback will be used in region_offset_to_dpa_result()
> to determine the endpoint by the position in the interleaving
> chunk. With Normalized addressing, the order of endpoints in the
> interleaving chunk is implementation defined. Do not use this approach
> and and return an error instead.
>
> Disable both HPA/SPA translation handlers for Normalized addressing by
> returning an error (ULLONG_MAX).
>
> Signed-off-by: Robert Richter <rrichter@amd.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> ---
> drivers/cxl/core/atl.c | 35 +++++++++++++++++++++++++++++++++++
> drivers/cxl/core/region.c | 21 ++++++++++++++++++++-
> drivers/cxl/cxl.h | 1 +
> 3 files changed, 56 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/cxl/core/atl.c b/drivers/cxl/core/atl.c
> index 4bd2289b8bbb..9d05a83bde73 100644
> --- a/drivers/cxl/core/atl.c
> +++ b/drivers/cxl/core/atl.c
> @@ -57,6 +57,39 @@ static u64 prm_cxl_dpa_spa(struct pci_dev *pci_dev, u64 dpa)
> return spa;
> }
>
> +static u64 atl_hpa_to_spa(struct cxl_root_decoder *cxlrd, u64 hpa)
> +{
> + /*
> + * PRM translation could be expensive in tracing or error
> + * handling code paths. Avoid this for now and return an
> + * error instead.
> + */
> +
> + return ULLONG_MAX;
> +}
> +
> +static u64 atl_spa_to_hpa(struct cxl_root_decoder *cxlrd, u64 spa)
> +{
> + /*
> + * The callback will be used in region_offset_to_dpa_result()
> + * to determine the endpoint by the position in the
> + * interleaving chunk. With Normalized addressing, the order
> + * of endpoints in the interleaving chunk is implementation
> + * defined. Do not use this approach and and return an error
> + * instead.
> + */
> +
> + return ULLONG_MAX;
> +}
> +
> +static int cxl_prm_setup_region(struct cxl_region *cxlr)
> +{
> + cxlr->cxlrd->ops.hpa_to_spa = atl_hpa_to_spa;
> + cxlr->cxlrd->ops.spa_to_hpa = atl_spa_to_hpa;
> +
> + return 0;
> +}
> +
> static int cxl_prm_setup_root(struct cxl_root *cxl_root, void *data)
> {
> struct cxl_region_context *ctx = data;
> @@ -183,6 +216,8 @@ static int cxl_prm_setup_root(struct cxl_root *cxl_root, void *data)
> ctx->interleave_ways = ways;
> ctx->interleave_granularity = gran;
>
> + cxl_root->ops.translation_setup_region = cxl_prm_setup_region;
> +
> dev_dbg(&cxld->dev,
> "address mapping found for %s (hpa -> spa): %#llx+%#llx -> %#llx+%#llx ways:%d granularity:%d\n",
> dev_name(cxlmd->dev.parent), base, len, hpa_range.start,
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 2c070c7c7bfe..130af1cf95b7 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -623,6 +623,21 @@ static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
> }
> static DEVICE_ATTR_RO(mode);
>
> +static int cxl_region_setup_translation(struct cxl_region *cxlr)
> +{
> + struct cxl_port *root = to_cxl_port(cxlr->dev.parent->parent);
> + struct cxl_root *cxl_root;
> +
> + if (!root || !is_cxl_root(root))
> + return 0;
> +
> + cxl_root = to_cxl_root(root);
> + if (!cxl_root || !cxl_root->ops.translation_setup_region)
> + return 0;
> +
> + return cxl_root->ops.translation_setup_region(cxlr);
> +}
> +
> static int alloc_hpa(struct cxl_region *cxlr, resource_size_t size)
> {
> struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
> @@ -666,7 +681,7 @@ static int alloc_hpa(struct cxl_region *cxlr, resource_size_t size)
> p->res = res;
> p->state = CXL_CONFIG_INTERLEAVE_ACTIVE;
>
> - return 0;
> + return cxl_region_setup_translation(cxlr);
> }
>
> static void cxl_region_iomem_release(struct cxl_region *cxlr)
> @@ -3663,6 +3678,10 @@ static int __construct_region(struct cxl_region *cxlr,
> p->interleave_granularity = ctx->interleave_granularity;
> p->state = CXL_CONFIG_INTERLEAVE_ACTIVE;
>
> + rc = cxl_region_setup_translation(cxlr);
> + if (rc)
> + return rc;
> +
> rc = sysfs_update_group(&cxlr->dev.kobj, get_cxl_region_target_group());
> if (rc)
> return rc;
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 20b0fd43fa7b..f4623728a815 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -653,6 +653,7 @@ struct cxl_root_ops {
> struct access_coordinate *coord, int entries,
> int *qos_class);
> int (*translation_setup_root)(struct cxl_root *cxl_root, void *data);
> + int (*translation_setup_region)(struct cxl_region *cxlr);
> };
>
> /**
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v8 12/13] cxl: Check if ULLONG_MAX was returned from translation functions
2025-12-09 18:06 ` [PATCH v8 12/13] cxl: Check if ULLONG_MAX was returned from translation functions Robert Richter
2025-12-10 16:23 ` Dave Jiang
@ 2025-12-11 19:50 ` kernel test robot
2025-12-14 23:40 ` kernel test robot
2025-12-16 5:26 ` Alison Schofield
3 siblings, 0 replies; 21+ messages in thread
From: kernel test robot @ 2025-12-11 19:50 UTC (permalink / raw)
To: Robert Richter, Alison Schofield, Vishal Verma, Ira Weiny,
Dan Williams, Jonathan Cameron, Dave Jiang, Davidlohr Bueso
Cc: oe-kbuild-all, linux-cxl, linux-kernel, Gregory Price,
Fabio M. De Francesco, Terry Bowman, Joshua Hahn, Robert Richter
Hi Robert,
kernel test robot noticed the following build warnings:
[auto build test WARNING on ea5514e300568cbe8f19431c3e424d4791db8291]
url: https://github.com/intel-lab-lkp/linux/commits/Robert-Richter/cxl-region-Rename-misleading-variable-name-hpa-to-hpa_range/20251210-021858
base: ea5514e300568cbe8f19431c3e424d4791db8291
patch link: https://lore.kernel.org/r/20251209180659.208842-13-rrichter%40amd.com
patch subject: [PATCH v8 12/13] cxl: Check if ULLONG_MAX was returned from translation functions
config: i386-randconfig-141-20251212 (https://download.01.org/0day-ci/archive/20251212/202512120346.aT9GKk20-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.4.0-5) 12.4.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251212/202512120346.aT9GKk20-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202512120346.aT9GKk20-lkp@intel.com/
All warnings (new ones prefixed by >>):
In file included from include/linux/limits.h:7,
from include/linux/overflow.h:6,
from include/linux/string.h:13,
from include/linux/seq_file.h:6,
from drivers/cxl/core/hdm.c:3:
drivers/cxl/core/hdm.c: In function 'cxl_dpa_resource_start':
>> include/vdso/limits.h:16:25: warning: conversion from 'long long unsigned int' to 'resource_size_t' {aka 'unsigned int'} changes value from '18446744073709551615' to '4294967295' [-Woverflow]
16 | #define ULLONG_MAX (~0ULL)
| ^
drivers/cxl/core/hdm.c:533:32: note: in expansion of macro 'ULLONG_MAX'
533 | resource_size_t base = ULLONG_MAX;
| ^~~~~~~~~~
vim +16 include/vdso/limits.h
3e0e9f8c6e3ca9 Vincenzo Frascino 2020-03-20 4
3e0e9f8c6e3ca9 Vincenzo Frascino 2020-03-20 5 #define USHRT_MAX ((unsigned short)~0U)
3e0e9f8c6e3ca9 Vincenzo Frascino 2020-03-20 6 #define SHRT_MAX ((short)(USHRT_MAX >> 1))
3e0e9f8c6e3ca9 Vincenzo Frascino 2020-03-20 7 #define SHRT_MIN ((short)(-SHRT_MAX - 1))
3e0e9f8c6e3ca9 Vincenzo Frascino 2020-03-20 8 #define INT_MAX ((int)(~0U >> 1))
3e0e9f8c6e3ca9 Vincenzo Frascino 2020-03-20 9 #define INT_MIN (-INT_MAX - 1)
3e0e9f8c6e3ca9 Vincenzo Frascino 2020-03-20 10 #define UINT_MAX (~0U)
3e0e9f8c6e3ca9 Vincenzo Frascino 2020-03-20 11 #define LONG_MAX ((long)(~0UL >> 1))
3e0e9f8c6e3ca9 Vincenzo Frascino 2020-03-20 12 #define LONG_MIN (-LONG_MAX - 1)
3e0e9f8c6e3ca9 Vincenzo Frascino 2020-03-20 13 #define ULONG_MAX (~0UL)
3e0e9f8c6e3ca9 Vincenzo Frascino 2020-03-20 14 #define LLONG_MAX ((long long)(~0ULL >> 1))
3e0e9f8c6e3ca9 Vincenzo Frascino 2020-03-20 15 #define LLONG_MIN (-LLONG_MAX - 1)
3e0e9f8c6e3ca9 Vincenzo Frascino 2020-03-20 @16 #define ULLONG_MAX (~0ULL)
3e0e9f8c6e3ca9 Vincenzo Frascino 2020-03-20 17 #define UINTPTR_MAX ULONG_MAX
3e0e9f8c6e3ca9 Vincenzo Frascino 2020-03-20 18
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v8 12/13] cxl: Check if ULLONG_MAX was returned from translation functions
2025-12-09 18:06 ` [PATCH v8 12/13] cxl: Check if ULLONG_MAX was returned from translation functions Robert Richter
2025-12-10 16:23 ` Dave Jiang
2025-12-11 19:50 ` kernel test robot
@ 2025-12-14 23:40 ` kernel test robot
2025-12-16 5:26 ` Alison Schofield
3 siblings, 0 replies; 21+ messages in thread
From: kernel test robot @ 2025-12-14 23:40 UTC (permalink / raw)
To: Robert Richter, Alison Schofield, Vishal Verma, Ira Weiny,
Dan Williams, Jonathan Cameron, Dave Jiang, Davidlohr Bueso
Cc: llvm, oe-kbuild-all, linux-cxl, linux-kernel, Gregory Price,
Fabio M. De Francesco, Terry Bowman, Joshua Hahn, Robert Richter
Hi Robert,
kernel test robot noticed the following build warnings:
[auto build test WARNING on ea5514e300568cbe8f19431c3e424d4791db8291]
url: https://github.com/intel-lab-lkp/linux/commits/Robert-Richter/cxl-region-Rename-misleading-variable-name-hpa-to-hpa_range/20251210-021858
base: ea5514e300568cbe8f19431c3e424d4791db8291
patch link: https://lore.kernel.org/r/20251209180659.208842-13-rrichter%40amd.com
patch subject: [PATCH v8 12/13] cxl: Check if ULLONG_MAX was returned from translation functions
config: i386-randconfig-003-20251215 (https://download.01.org/0day-ci/archive/20251215/202512150712.0PH2z2O5-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251215/202512150712.0PH2z2O5-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202512150712.0PH2z2O5-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/cxl/core/hdm.c:533:25: warning: implicit conversion from 'unsigned long long' to 'resource_size_t' (aka 'unsigned int') changes value from 18446744073709551615 to 4294967295 [-Wconstant-conversion]
533 | resource_size_t base = ULLONG_MAX;
| ~~~~ ^~~~~~~~~~
include/vdso/limits.h:16:21: note: expanded from macro 'ULLONG_MAX'
16 | #define ULLONG_MAX (~0ULL)
| ^~~~~
1 warning generated.
vim +533 drivers/cxl/core/hdm.c
530
531 resource_size_t cxl_dpa_resource_start(struct cxl_endpoint_decoder *cxled)
532 {
> 533 resource_size_t base = ULLONG_MAX;
534
535 lockdep_assert_held(&cxl_rwsem.dpa);
536 if (cxled->dpa_res)
537 base = cxled->dpa_res->start;
538
539 return base;
540 }
541
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v8 13/13] cxl: Disable HPA/SPA translation handlers for Normalized addressing
2025-12-09 18:06 ` [PATCH v8 13/13] cxl: Disable HPA/SPA translation handlers for Normalized addressing Robert Richter
2025-12-10 17:44 ` Dave Jiang
@ 2025-12-15 13:25 ` Jonathan Cameron
2025-12-16 5:21 ` Alison Schofield
2 siblings, 0 replies; 21+ messages in thread
From: Jonathan Cameron @ 2025-12-15 13:25 UTC (permalink / raw)
To: Robert Richter
Cc: Alison Schofield, Vishal Verma, Ira Weiny, Dan Williams,
Dave Jiang, Davidlohr Bueso, linux-cxl, linux-kernel,
Gregory Price, Fabio M. De Francesco, Terry Bowman, Joshua Hahn
On Tue, 9 Dec 2025 19:06:49 +0100
Robert Richter <rrichter@amd.com> wrote:
> The root decoder implements callbacks hpa_to_spa and spa_to_hpa to
> perform Host Physical Address (HPA) and System Physical Address
> translations respectively. The callbacks are needed in cases where HPA
> != SPA to convert addresses for tracing and error handling and to
> setup Poison injection. Currently this is used for XOR interleaving.
>
> In AMD Zen5 systems with Normalized addressing, the addresses of
> endpoints are not SPA and those callbacks need to be implemented.
>
> Now, as ACPI PRM translation could be expensive in tracing or error
> handling code paths, do not yet enable this direction of translation
> (hpa_to_spa) to avoid its intensive use. Instead, mark the HPA invalid
> and return an error for this case.
>
> The spa_to_hpa callback will be used in region_offset_to_dpa_result()
> to determine the endpoint by the position in the interleaving
> chunk. With Normalized addressing, the order of endpoints in the
> interleaving chunk is implementation defined. Do not use this approach
> and and return an error instead.
>
> Disable both HPA/SPA translation handlers for Normalized addressing by
> returning an error (ULLONG_MAX).
>
> Signed-off-by: Robert Richter <rrichter@amd.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
I'll be interested to see whether we end up deciding in the long run
that paying the cost of the call is worth while to get more useful
error records. This makes sense for now.
Jonathan
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v8 13/13] cxl: Disable HPA/SPA translation handlers for Normalized addressing
2025-12-09 18:06 ` [PATCH v8 13/13] cxl: Disable HPA/SPA translation handlers for Normalized addressing Robert Richter
2025-12-10 17:44 ` Dave Jiang
2025-12-15 13:25 ` Jonathan Cameron
@ 2025-12-16 5:21 ` Alison Schofield
2 siblings, 0 replies; 21+ messages in thread
From: Alison Schofield @ 2025-12-16 5:21 UTC (permalink / raw)
To: Robert Richter
Cc: Vishal Verma, Ira Weiny, Dan Williams, Jonathan Cameron,
Dave Jiang, Davidlohr Bueso, linux-cxl, linux-kernel,
Gregory Price, Fabio M. De Francesco, Terry Bowman, Joshua Hahn
On Tue, Dec 09, 2025 at 07:06:49PM +0100, Robert Richter wrote:
> The root decoder implements callbacks hpa_to_spa and spa_to_hpa to
> perform Host Physical Address (HPA) and System Physical Address
> translations respectively. The callbacks are needed in cases where HPA
> != SPA to convert addresses for tracing and error handling and to
> setup Poison injection. Currently this is used for XOR interleaving.
>
> In AMD Zen5 systems with Normalized addressing, the addresses of
> endpoints are not SPA and those callbacks need to be implemented.
>
> Now, as ACPI PRM translation could be expensive in tracing or error
> handling code paths, do not yet enable this direction of translation
> (hpa_to_spa) to avoid its intensive use. Instead, mark the HPA invalid
> and return an error for this case.
>
> The spa_to_hpa callback will be used in region_offset_to_dpa_result()
> to determine the endpoint by the position in the interleaving
> chunk. With Normalized addressing, the order of endpoints in the
> interleaving chunk is implementation defined. Do not use this approach
> and and return an error instead.
>
> Disable both HPA/SPA translation handlers for Normalized addressing by
> returning an error (ULLONG_MAX).
Hi Robert,
I did suggest failing gracefully, like you implement here, but now it
feels like we are 'weaseling our way'[1] out of something.
We can shut these features down at a higher level rather than tiptoeing
through their implementation and adding a clever exit point.
This struck me in the changes to cxl_dpa_to_hpa() work because I suspect
the work that even gets us from a DPA to a CXL_HPA is not accurate for
Zen5, and so we're being wasteful going through the motions of DPA to HPA
only to know we are going to throw it all away at the end in an HPA->SPA
callback. And then for the region_offset_to_dpa_result() it seems silly to
even offer the inject poison by region offset option if we intend to fail
it every time.
Please consider this -
SPA->HPA:
region_offset_to_dpa_result() exists to support inject and clear poison by
region offset. That's an expert user feature for testing only and I'm
thinking we should just turn it off for Zen5. By turning it off, then the
debugfs attributes will not be present, and it becomes a no-op. Note this
also avoids users seeing but always failing attempts to use the feature.
How to turn it off? Look in region driver where we turn it on based
on memdev capabilities and add an arch specific check.
HPA->SPA:
cxl_dpa_to_hpa() provides the translated addresses that get reported in
trace events for errors and also for actions related to poison list reads,
and poison inject and clear by memdev. Please note that when we say 'trace'
in this context it's all about how we report the details of these errors:
poison, general media, or dram errors in trace events. It is not about any
performance path tracing activity.
How to turn it off? Look in cxl/core.h. Here we turn it on for CXL_REGION
and off otherwise. Can you expand that to turn it off for CXL_ATL?
-- Alison
[1] To "weasel your way" (in or out of something) means to get into or
avoid a situation using cleverness, trickery, or dishonesty, much like
the sly, slippery nature of the animal.
>
> Signed-off-by: Robert Richter <rrichter@amd.com>
> ---
> drivers/cxl/core/atl.c | 35 +++++++++++++++++++++++++++++++++++
> drivers/cxl/core/region.c | 21 ++++++++++++++++++++-
> drivers/cxl/cxl.h | 1 +
> 3 files changed, 56 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/cxl/core/atl.c b/drivers/cxl/core/atl.c
> index 4bd2289b8bbb..9d05a83bde73 100644
> --- a/drivers/cxl/core/atl.c
> +++ b/drivers/cxl/core/atl.c
> @@ -57,6 +57,39 @@ static u64 prm_cxl_dpa_spa(struct pci_dev *pci_dev, u64 dpa)
> return spa;
> }
>
> +static u64 atl_hpa_to_spa(struct cxl_root_decoder *cxlrd, u64 hpa)
> +{
> + /*
> + * PRM translation could be expensive in tracing or error
> + * handling code paths. Avoid this for now and return an
> + * error instead.
> + */
> +
> + return ULLONG_MAX;
> +}
> +
> +static u64 atl_spa_to_hpa(struct cxl_root_decoder *cxlrd, u64 spa)
> +{
> + /*
> + * The callback will be used in region_offset_to_dpa_result()
> + * to determine the endpoint by the position in the
> + * interleaving chunk. With Normalized addressing, the order
> + * of endpoints in the interleaving chunk is implementation
> + * defined. Do not use this approach and and return an error
> + * instead.
> + */
> +
> + return ULLONG_MAX;
> +}
> +
> +static int cxl_prm_setup_region(struct cxl_region *cxlr)
> +{
> + cxlr->cxlrd->ops.hpa_to_spa = atl_hpa_to_spa;
> + cxlr->cxlrd->ops.spa_to_hpa = atl_spa_to_hpa;
> +
> + return 0;
> +}
> +
> static int cxl_prm_setup_root(struct cxl_root *cxl_root, void *data)
> {
> struct cxl_region_context *ctx = data;
> @@ -183,6 +216,8 @@ static int cxl_prm_setup_root(struct cxl_root *cxl_root, void *data)
> ctx->interleave_ways = ways;
> ctx->interleave_granularity = gran;
>
> + cxl_root->ops.translation_setup_region = cxl_prm_setup_region;
> +
> dev_dbg(&cxld->dev,
> "address mapping found for %s (hpa -> spa): %#llx+%#llx -> %#llx+%#llx ways:%d granularity:%d\n",
> dev_name(cxlmd->dev.parent), base, len, hpa_range.start,
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 2c070c7c7bfe..130af1cf95b7 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -623,6 +623,21 @@ static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
> }
> static DEVICE_ATTR_RO(mode);
>
> +static int cxl_region_setup_translation(struct cxl_region *cxlr)
> +{
> + struct cxl_port *root = to_cxl_port(cxlr->dev.parent->parent);
> + struct cxl_root *cxl_root;
> +
> + if (!root || !is_cxl_root(root))
> + return 0;
> +
> + cxl_root = to_cxl_root(root);
> + if (!cxl_root || !cxl_root->ops.translation_setup_region)
> + return 0;
> +
> + return cxl_root->ops.translation_setup_region(cxlr);
> +}
> +
> static int alloc_hpa(struct cxl_region *cxlr, resource_size_t size)
> {
> struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
> @@ -666,7 +681,7 @@ static int alloc_hpa(struct cxl_region *cxlr, resource_size_t size)
> p->res = res;
> p->state = CXL_CONFIG_INTERLEAVE_ACTIVE;
>
> - return 0;
> + return cxl_region_setup_translation(cxlr);
> }
>
> static void cxl_region_iomem_release(struct cxl_region *cxlr)
> @@ -3663,6 +3678,10 @@ static int __construct_region(struct cxl_region *cxlr,
> p->interleave_granularity = ctx->interleave_granularity;
> p->state = CXL_CONFIG_INTERLEAVE_ACTIVE;
>
> + rc = cxl_region_setup_translation(cxlr);
> + if (rc)
> + return rc;
> +
> rc = sysfs_update_group(&cxlr->dev.kobj, get_cxl_region_target_group());
> if (rc)
> return rc;
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 20b0fd43fa7b..f4623728a815 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -653,6 +653,7 @@ struct cxl_root_ops {
> struct access_coordinate *coord, int entries,
> int *qos_class);
> int (*translation_setup_root)(struct cxl_root *cxl_root, void *data);
> + int (*translation_setup_region)(struct cxl_region *cxlr);
> };
>
> /**
> --
> 2.47.3
>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v8 12/13] cxl: Check if ULLONG_MAX was returned from translation functions
2025-12-09 18:06 ` [PATCH v8 12/13] cxl: Check if ULLONG_MAX was returned from translation functions Robert Richter
` (2 preceding siblings ...)
2025-12-14 23:40 ` kernel test robot
@ 2025-12-16 5:26 ` Alison Schofield
3 siblings, 0 replies; 21+ messages in thread
From: Alison Schofield @ 2025-12-16 5:26 UTC (permalink / raw)
To: Robert Richter
Cc: Vishal Verma, Ira Weiny, Dan Williams, Jonathan Cameron,
Dave Jiang, Davidlohr Bueso, linux-cxl, linux-kernel,
Gregory Price, Fabio M. De Francesco, Terry Bowman, Joshua Hahn
On Tue, Dec 09, 2025 at 07:06:48PM +0100, Robert Richter wrote:
> The return address of translation functions is not consistently
> checked for a valid address. Check if ULLONG_MAX was returned.
The why of the dpa base address change is not explained as that
That -1 return would be ULLONG_MAX in 64 bit system, so not
clear why the return value of cxl_dpa_resource_start() needs to
change.
Is this is for general cleanup, then, like DaveJ noted, returning
sooner is better. I guess post this as a general cleanup pointing
out why the earlier returns are needed.
>
> Signed-off-by: Robert Richter <rrichter@amd.com>
> ---
> drivers/cxl/core/hdm.c | 2 +-
> drivers/cxl/core/region.c | 36 +++++++++++++++++++-------
> tools/testing/cxl/test/cxl_translate.c | 4 +--
> 3 files changed, 29 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
> index 1c5d2022c87a..8b50cdce4b29 100644
> --- a/drivers/cxl/core/hdm.c
> +++ b/drivers/cxl/core/hdm.c
> @@ -530,7 +530,7 @@ resource_size_t cxl_dpa_size(struct cxl_endpoint_decoder *cxled)
>
> resource_size_t cxl_dpa_resource_start(struct cxl_endpoint_decoder *cxled)
> {
> - resource_size_t base = -1;
> + resource_size_t base = ULLONG_MAX;
>
> lockdep_assert_held(&cxl_rwsem.dpa);
> if (cxled->dpa_res)
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index c7ac78f1b644..2c070c7c7bfe 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -3124,7 +3124,7 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
> struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
> struct cxl_region_params *p = &cxlr->params;
> struct cxl_endpoint_decoder *cxled = NULL;
> - u64 dpa_offset, hpa_offset, hpa;
> + u64 base, dpa_offset, hpa_offset, hpa;
> u16 eig = 0;
> u8 eiw = 0;
> int pos;
> @@ -3142,8 +3142,14 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
> ways_to_eiw(p->interleave_ways, &eiw);
> granularity_to_eig(p->interleave_granularity, &eig);
>
> - dpa_offset = dpa - cxl_dpa_resource_start(cxled);
> + base = cxl_dpa_resource_start(cxled);
> + if (base == ULLONG_MAX)
> + return ULLONG_MAX;
> +
> + dpa_offset = dpa - base;
> hpa_offset = cxl_calculate_hpa_offset(dpa_offset, pos, eiw, eig);
> + if (hpa_offset == ULLONG_MAX)
> + return ULLONG_MAX;
>
> /* Apply the hpa_offset to the region base address */
> hpa = hpa_offset + p->res->start + p->cache_size;
> @@ -3152,6 +3158,9 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
> if (cxlrd->ops.hpa_to_spa)
> hpa = cxlrd->ops.hpa_to_spa(cxlrd, hpa);
>
> + if (hpa == ULLONG_MAX)
> + return ULLONG_MAX;
> +
> if (!cxl_resource_contains_addr(p->res, hpa)) {
> dev_dbg(&cxlr->dev,
> "Addr trans fail: hpa 0x%llx not in region\n", hpa);
> @@ -3176,10 +3185,11 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
> struct cxl_region_params *p = &cxlr->params;
> struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
> struct cxl_endpoint_decoder *cxled;
> - u64 hpa, hpa_offset, dpa_offset;
> + u64 hpa_offset = offset;
> + u64 dpa_base, dpa_offset;
> u16 eig = 0;
> u8 eiw = 0;
> - int pos;
> + int pos = -1;
>
> lockdep_assert_held(&cxl_rwsem.region);
> lockdep_assert_held(&cxl_rwsem.dpa);
> @@ -3193,13 +3203,14 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
> * CXL HPA is assumed to equal SPA.
> */
> if (cxlrd->ops.spa_to_hpa) {
> - hpa = cxlrd->ops.spa_to_hpa(cxlrd, p->res->start + offset);
> - hpa_offset = hpa - p->res->start;
> - } else {
> - hpa_offset = offset;
> + hpa_offset = cxlrd->ops.spa_to_hpa(cxlrd, p->res->start + offset);
> + if (hpa_offset != ULLONG_MAX)
> + hpa_offset -= p->res->start;
> }
>
> - pos = cxl_calculate_position(hpa_offset, eiw, eig);
> + if (hpa_offset != ULLONG_MAX)
> + 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);
> @@ -3213,8 +3224,13 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
> cxled = p->targets[i];
> if (cxled->pos != pos)
> continue;
> +
> + dpa_base = cxl_dpa_resource_start(cxled);
> + if (dpa_base == ULLONG_MAX)
> + continue;
> +
> result->cxlmd = cxled_to_memdev(cxled);
> - result->dpa = cxl_dpa_resource_start(cxled) + dpa_offset;
> + result->dpa = dpa_base + dpa_offset;
>
> return 0;
> }
> diff --git a/tools/testing/cxl/test/cxl_translate.c b/tools/testing/cxl/test/cxl_translate.c
> index 2200ae21795c..66f8270aacd8 100644
> --- a/tools/testing/cxl/test/cxl_translate.c
> +++ b/tools/testing/cxl/test/cxl_translate.c
> @@ -69,7 +69,7 @@ static u64 to_hpa(u64 dpa_offset, int pos, u8 r_eiw, u16 r_eig, u8 hb_ways,
> /* 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) {
> + if (hpa_offset != ULLONG_MAX && 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);
> @@ -262,7 +262,7 @@ static int test_random_params(void)
> reverse_dpa = cxl_calculate_dpa_offset(hpa, eiw, eig);
> reverse_pos = cxl_calculate_position(hpa, eiw, eig);
>
> - if (reverse_dpa != dpa || reverse_pos != pos) {
> + if (hpa == ULLONG_MAX || 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);
> --
> 2.47.3
>
^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2025-12-16 5:27 UTC | newest]
Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-12-09 18:06 [PATCH v8 00/13] cxl: ACPI PRM Address Translation Support and AMD Zen5 enablement Robert Richter
2025-12-09 18:06 ` [PATCH v8 01/13] cxl/region: Rename misleading variable name @hpa to @hpa_range Robert Richter
2025-12-09 18:06 ` [PATCH v8 02/13] cxl/region: Store root decoder in struct cxl_region Robert Richter
2025-12-09 18:06 ` [PATCH v8 03/13] cxl/region: Store HPA range " Robert Richter
2025-12-09 18:06 ` [PATCH v8 04/13] cxl: Simplify cxl_root_ops allocation and handling Robert Richter
2025-12-09 18:06 ` [PATCH v8 05/13] cxl/region: Separate region parameter setup and region construction Robert Richter
2025-12-09 18:06 ` [PATCH v8 06/13] cxl/region: Add @hpa_range argument to function cxl_calc_interleave_pos() Robert Richter
2025-12-09 18:06 ` [PATCH v8 07/13] cxl/region: Use region data to get the root decoder Robert Richter
2025-12-09 18:06 ` [PATCH v8 08/13] cxl: Introduce callback for HPA address ranges translation Robert Richter
2025-12-09 18:06 ` [PATCH v8 09/13] cxl/acpi: Prepare use of EFI runtime services Robert Richter
2025-12-09 18:06 ` [PATCH v8 10/13] cxl: Enable AMD Zen5 address translation using ACPI PRMT Robert Richter
2025-12-09 18:06 ` [PATCH v8 11/13] cxl/atl: Lock decoders that need address translation Robert Richter
2025-12-09 18:06 ` [PATCH v8 12/13] cxl: Check if ULLONG_MAX was returned from translation functions Robert Richter
2025-12-10 16:23 ` Dave Jiang
2025-12-11 19:50 ` kernel test robot
2025-12-14 23:40 ` kernel test robot
2025-12-16 5:26 ` Alison Schofield
2025-12-09 18:06 ` [PATCH v8 13/13] cxl: Disable HPA/SPA translation handlers for Normalized addressing Robert Richter
2025-12-10 17:44 ` Dave Jiang
2025-12-15 13:25 ` Jonathan Cameron
2025-12-16 5:21 ` Alison Schofield
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox