Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/cxl/core/atl.c
121832 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Copyright (C) 2025 Advanced Micro Devices, Inc.
4
*/
5
6
#include <linux/prmt.h>
7
#include <linux/pci.h>
8
#include <linux/acpi.h>
9
10
#include <cxlmem.h>
11
#include "core.h"
12
13
/*
14
* PRM Address Translation - CXL DPA to System Physical Address
15
*
16
* Reference:
17
*
18
* AMD Family 1Ah Models 00h–0Fh and Models 10h–1Fh
19
* ACPI v6.5 Porting Guide, Publication # 58088
20
*/
21
22
static const guid_t prm_cxl_dpa_spa_guid =
23
GUID_INIT(0xee41b397, 0x25d4, 0x452c, 0xad, 0x54, 0x48, 0xc6, 0xe3,
24
0x48, 0x0b, 0x94);
25
26
struct prm_cxl_dpa_spa_data {
27
u64 dpa;
28
u8 reserved;
29
u8 devfn;
30
u8 bus;
31
u8 segment;
32
u64 *spa;
33
} __packed;
34
35
static u64 prm_cxl_dpa_spa(struct pci_dev *pci_dev, u64 dpa)
36
{
37
struct prm_cxl_dpa_spa_data data;
38
u64 spa;
39
int rc;
40
41
data = (struct prm_cxl_dpa_spa_data) {
42
.dpa = dpa,
43
.devfn = pci_dev->devfn,
44
.bus = pci_dev->bus->number,
45
.segment = pci_domain_nr(pci_dev->bus),
46
.spa = &spa,
47
};
48
49
rc = acpi_call_prm_handler(prm_cxl_dpa_spa_guid, &data);
50
if (rc) {
51
pci_dbg(pci_dev, "failed to get SPA for %#llx: %d\n", dpa, rc);
52
return ULLONG_MAX;
53
}
54
55
pci_dbg(pci_dev, "PRM address translation: DPA -> SPA: %#llx -> %#llx\n", dpa, spa);
56
57
return spa;
58
}
59
60
static int cxl_prm_setup_root(struct cxl_root *cxl_root, void *data)
61
{
62
struct cxl_region_context *ctx = data;
63
struct cxl_endpoint_decoder *cxled = ctx->cxled;
64
struct cxl_decoder *cxld = &cxled->cxld;
65
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
66
struct range hpa_range = ctx->hpa_range;
67
struct pci_dev *pci_dev;
68
u64 spa_len, len;
69
u64 addr, base_spa, base;
70
int ways, gran;
71
72
/*
73
* When Normalized Addressing is enabled, the endpoint maintains a 1:1
74
* mapping between HPA and DPA. If disabled, skip address translation
75
* and perform only a range check.
76
*/
77
if (hpa_range.start != cxled->dpa_res->start)
78
return 0;
79
80
/*
81
* Endpoints are programmed passthrough in Normalized Addressing mode.
82
*/
83
if (ctx->interleave_ways != 1) {
84
dev_dbg(&cxld->dev, "unexpected interleaving config: ways: %d granularity: %d\n",
85
ctx->interleave_ways, ctx->interleave_granularity);
86
return -ENXIO;
87
}
88
89
if (!cxlmd || !dev_is_pci(cxlmd->dev.parent)) {
90
dev_dbg(&cxld->dev, "No endpoint found: %s, range %#llx-%#llx\n",
91
dev_name(cxld->dev.parent), hpa_range.start,
92
hpa_range.end);
93
return -ENXIO;
94
}
95
96
pci_dev = to_pci_dev(cxlmd->dev.parent);
97
98
/* Translate HPA range to SPA. */
99
base = hpa_range.start;
100
hpa_range.start = prm_cxl_dpa_spa(pci_dev, hpa_range.start);
101
hpa_range.end = prm_cxl_dpa_spa(pci_dev, hpa_range.end);
102
base_spa = hpa_range.start;
103
104
if (hpa_range.start == ULLONG_MAX || hpa_range.end == ULLONG_MAX) {
105
dev_dbg(cxld->dev.parent,
106
"CXL address translation: Failed to translate HPA range: %#llx-%#llx:%#llx-%#llx(%s)\n",
107
hpa_range.start, hpa_range.end, ctx->hpa_range.start,
108
ctx->hpa_range.end, dev_name(&cxld->dev));
109
return -ENXIO;
110
}
111
112
/*
113
* Since translated addresses include the interleaving offsets, align
114
* the range to 256 MB.
115
*/
116
hpa_range.start = ALIGN_DOWN(hpa_range.start, SZ_256M);
117
hpa_range.end = ALIGN(hpa_range.end, SZ_256M) - 1;
118
119
len = range_len(&ctx->hpa_range);
120
spa_len = range_len(&hpa_range);
121
if (!len || !spa_len || spa_len % len) {
122
dev_dbg(cxld->dev.parent,
123
"CXL address translation: HPA range not contiguous: %#llx-%#llx:%#llx-%#llx(%s)\n",
124
hpa_range.start, hpa_range.end, ctx->hpa_range.start,
125
ctx->hpa_range.end, dev_name(&cxld->dev));
126
return -ENXIO;
127
}
128
129
ways = spa_len / len;
130
gran = SZ_256;
131
132
/*
133
* Determine interleave granularity
134
*
135
* Note: The position of the chunk from one interleaving block to the
136
* next may vary and thus cannot be considered constant. Address offsets
137
* larger than the interleaving block size cannot be used to calculate
138
* the granularity.
139
*/
140
if (ways > 1) {
141
while (gran <= SZ_16M) {
142
addr = prm_cxl_dpa_spa(pci_dev, base + gran);
143
if (addr != base_spa + gran)
144
break;
145
gran <<= 1;
146
}
147
}
148
149
if (gran > SZ_16M) {
150
dev_dbg(cxld->dev.parent,
151
"CXL address translation: Cannot determine granularity: %#llx-%#llx:%#llx-%#llx(%s)\n",
152
hpa_range.start, hpa_range.end, ctx->hpa_range.start,
153
ctx->hpa_range.end, dev_name(&cxld->dev));
154
return -ENXIO;
155
}
156
157
/*
158
* The current kernel implementation does not support endpoint
159
* setup with Normalized Addressing. It only translates an
160
* endpoint's DPA to the SPA range of the host bridge.
161
* Therefore, the endpoint address range cannot be determined,
162
* making a non-auto setup impossible. If a decoder requires
163
* address translation, reprogramming should be disabled and
164
* the decoder locked.
165
*
166
* The BIOS, however, provides all the necessary address
167
* translation data, which the kernel can use to reconfigure
168
* endpoint decoders with normalized addresses. Locking the
169
* decoders in the BIOS would prevent a capable kernel (or
170
* other operating systems) from shutting down auto-generated
171
* regions and managing resources dynamically.
172
*
173
* Indicate that Normalized Addressing is enabled.
174
*/
175
cxld->flags |= CXL_DECODER_F_LOCK;
176
cxld->flags |= CXL_DECODER_F_NORMALIZED_ADDRESSING;
177
178
ctx->hpa_range = hpa_range;
179
ctx->interleave_ways = ways;
180
ctx->interleave_granularity = gran;
181
182
dev_dbg(&cxld->dev,
183
"address mapping found for %s (hpa -> spa): %#llx+%#llx -> %#llx+%#llx ways:%d granularity:%d\n",
184
dev_name(cxlmd->dev.parent), base, len, hpa_range.start,
185
spa_len, ways, gran);
186
187
return 0;
188
}
189
190
void cxl_setup_prm_address_translation(struct cxl_root *cxl_root)
191
{
192
struct device *host = cxl_root->port.uport_dev;
193
u64 spa;
194
struct prm_cxl_dpa_spa_data data = { .spa = &spa };
195
int rc;
196
197
/*
198
* Applies only to PCIe Host Bridges which are children of the CXL Root
199
* Device (HID=“ACPI0017”). Check this and drop cxl_test instances.
200
*/
201
if (!acpi_match_device(host->driver->acpi_match_table, host))
202
return;
203
204
/* Check kernel (-EOPNOTSUPP) and firmware support (-ENODEV) */
205
rc = acpi_call_prm_handler(prm_cxl_dpa_spa_guid, &data);
206
if (rc == -EOPNOTSUPP || rc == -ENODEV)
207
return;
208
209
cxl_root->ops.translation_setup_root = cxl_prm_setup_root;
210
}
211
EXPORT_SYMBOL_NS_GPL(cxl_setup_prm_address_translation, "CXL");
212
213