1#include "qemu/osdep.h"
2#include "qemu/units.h"
3#include "qemu/error-report.h"
4#include "hw/mem/memory-device.h"
5#include "hw/mem/pc-dimm.h"
6#include "hw/pci/pci.h"
7#include "hw/qdev-properties.h"
8#include "qapi/error.h"
9#include "qemu/log.h"
10#include "qemu/module.h"
11#include "qemu/pmem.h"
12#include "qemu/range.h"
13#include "qemu/rcu.h"
14#include "sysemu/hostmem.h"
15#include "hw/cxl/cxl.h"
16
17static void build_dvsecs(CXLType3Dev *ct3d)
18{
19 CXLComponentState *cxl_cstate = &ct3d->cxl_cstate;
20 uint8_t *dvsec;
21
22 dvsec = (uint8_t *)&(CXLDVSECDevice){
23 .cap = 0x1e,
24 .ctrl = 0x2,
25 .status2 = 0x2,
26 .range1_size_hi = ct3d->hostmem->size >> 32,
27 .range1_size_lo = (2 << 5) | (2 << 2) | 0x3 |
28 (ct3d->hostmem->size & 0xF0000000),
29 .range1_base_hi = 0,
30 .range1_base_lo = 0,
31 };
32 cxl_component_create_dvsec(cxl_cstate, CXL2_TYPE3_DEVICE,
33 PCIE_CXL_DEVICE_DVSEC_LENGTH,
34 PCIE_CXL_DEVICE_DVSEC,
35 PCIE_CXL2_DEVICE_DVSEC_REVID, dvsec);
36
37 dvsec = (uint8_t *)&(CXLDVSECRegisterLocator){
38 .rsvd = 0,
39 .reg0_base_lo = RBI_COMPONENT_REG | CXL_COMPONENT_REG_BAR_IDX,
40 .reg0_base_hi = 0,
41 .reg1_base_lo = RBI_CXL_DEVICE_REG | CXL_DEVICE_REG_BAR_IDX,
42 .reg1_base_hi = 0,
43 };
44 cxl_component_create_dvsec(cxl_cstate, CXL2_TYPE3_DEVICE,
45 REG_LOC_DVSEC_LENGTH, REG_LOC_DVSEC,
46 REG_LOC_DVSEC_REVID, dvsec);
47 dvsec = (uint8_t *)&(CXLDVSECDeviceGPF){
48 .phase2_duration = 0x603,
49 .phase2_power = 0x33,
50 };
51 cxl_component_create_dvsec(cxl_cstate, CXL2_TYPE3_DEVICE,
52 GPF_DEVICE_DVSEC_LENGTH, GPF_PORT_DVSEC,
53 GPF_DEVICE_DVSEC_REVID, dvsec);
54}
55
56static void hdm_decoder_commit(CXLType3Dev *ct3d, int which)
57{
58 ComponentRegisters *cregs = &ct3d->cxl_cstate.crb;
59 uint32_t *cache_mem = cregs->cache_mem_registers;
60
61 assert(which == 0);
62
63
64 ARRAY_FIELD_DP32(cache_mem, CXL_HDM_DECODER0_CTRL, COMMIT, 0);
65 ARRAY_FIELD_DP32(cache_mem, CXL_HDM_DECODER0_CTRL, ERR, 0);
66
67 ARRAY_FIELD_DP32(cache_mem, CXL_HDM_DECODER0_CTRL, COMMITTED, 1);
68}
69
70static void ct3d_reg_write(void *opaque, hwaddr offset, uint64_t value,
71 unsigned size)
72{
73 CXLComponentState *cxl_cstate = opaque;
74 ComponentRegisters *cregs = &cxl_cstate->crb;
75 CXLType3Dev *ct3d = container_of(cxl_cstate, CXLType3Dev, cxl_cstate);
76 uint32_t *cache_mem = cregs->cache_mem_registers;
77 bool should_commit = false;
78 int which_hdm = -1;
79
80 assert(size == 4);
81 g_assert(offset < CXL2_COMPONENT_CM_REGION_SIZE);
82
83 switch (offset) {
84 case A_CXL_HDM_DECODER0_CTRL:
85 should_commit = FIELD_EX32(value, CXL_HDM_DECODER0_CTRL, COMMIT);
86 which_hdm = 0;
87 break;
88 default:
89 break;
90 }
91
92 stl_le_p((uint8_t *)cache_mem + offset, value);
93 if (should_commit) {
94 hdm_decoder_commit(ct3d, which_hdm);
95 }
96}
97
98static bool cxl_setup_memory(CXLType3Dev *ct3d, Error **errp)
99{
100 DeviceState *ds = DEVICE(ct3d);
101 MemoryRegion *mr;
102 char *name;
103
104 if (!ct3d->hostmem) {
105 error_setg(errp, "memdev property must be set");
106 return false;
107 }
108
109 mr = host_memory_backend_get_memory(ct3d->hostmem);
110 if (!mr) {
111 error_setg(errp, "memdev property must be set");
112 return false;
113 }
114 memory_region_set_nonvolatile(mr, true);
115 memory_region_set_enabled(mr, true);
116 host_memory_backend_set_mapped(ct3d->hostmem, true);
117
118 if (ds->id) {
119 name = g_strdup_printf("cxl-type3-dpa-space:%s", ds->id);
120 } else {
121 name = g_strdup("cxl-type3-dpa-space");
122 }
123 address_space_init(&ct3d->hostmem_as, mr, name);
124 g_free(name);
125
126 ct3d->cxl_dstate.pmem_size = ct3d->hostmem->size;
127
128 if (!ct3d->lsa) {
129 error_setg(errp, "lsa property must be set");
130 return false;
131 }
132
133 return true;
134}
135
136static void ct3_realize(PCIDevice *pci_dev, Error **errp)
137{
138 CXLType3Dev *ct3d = CXL_TYPE3(pci_dev);
139 CXLComponentState *cxl_cstate = &ct3d->cxl_cstate;
140 ComponentRegisters *regs = &cxl_cstate->crb;
141 MemoryRegion *mr = ®s->component_registers;
142 uint8_t *pci_conf = pci_dev->config;
143
144 if (!cxl_setup_memory(ct3d, errp)) {
145 return;
146 }
147
148 pci_config_set_prog_interface(pci_conf, 0x10);
149 pci_config_set_class(pci_conf, PCI_CLASS_MEMORY_CXL);
150
151 pcie_endpoint_cap_init(pci_dev, 0x80);
152 cxl_cstate->dvsec_offset = 0x100;
153
154 ct3d->cxl_cstate.pdev = pci_dev;
155 build_dvsecs(ct3d);
156
157 regs->special_ops = g_new0(MemoryRegionOps, 1);
158 regs->special_ops->write = ct3d_reg_write;
159
160 cxl_component_register_block_init(OBJECT(pci_dev), cxl_cstate,
161 TYPE_CXL_TYPE3);
162
163 pci_register_bar(
164 pci_dev, CXL_COMPONENT_REG_BAR_IDX,
165 PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64, mr);
166
167 cxl_device_register_block_init(OBJECT(pci_dev), &ct3d->cxl_dstate);
168 pci_register_bar(pci_dev, CXL_DEVICE_REG_BAR_IDX,
169 PCI_BASE_ADDRESS_SPACE_MEMORY |
170 PCI_BASE_ADDRESS_MEM_TYPE_64,
171 &ct3d->cxl_dstate.device_registers);
172}
173
174static void ct3_exit(PCIDevice *pci_dev)
175{
176 CXLType3Dev *ct3d = CXL_TYPE3(pci_dev);
177 CXLComponentState *cxl_cstate = &ct3d->cxl_cstate;
178 ComponentRegisters *regs = &cxl_cstate->crb;
179
180 g_free(regs->special_ops);
181 address_space_destroy(&ct3d->hostmem_as);
182}
183
184
185static bool cxl_type3_dpa(CXLType3Dev *ct3d, hwaddr host_addr, uint64_t *dpa)
186{
187 uint32_t *cache_mem = ct3d->cxl_cstate.crb.cache_mem_registers;
188 uint64_t decoder_base, decoder_size, hpa_offset;
189 uint32_t hdm0_ctrl;
190 int ig, iw;
191
192 decoder_base = (((uint64_t)cache_mem[R_CXL_HDM_DECODER0_BASE_HI] << 32) |
193 cache_mem[R_CXL_HDM_DECODER0_BASE_LO]);
194 if ((uint64_t)host_addr < decoder_base) {
195 return false;
196 }
197
198 hpa_offset = (uint64_t)host_addr - decoder_base;
199
200 decoder_size = ((uint64_t)cache_mem[R_CXL_HDM_DECODER0_SIZE_HI] << 32) |
201 cache_mem[R_CXL_HDM_DECODER0_SIZE_LO];
202 if (hpa_offset >= decoder_size) {
203 return false;
204 }
205
206 hdm0_ctrl = cache_mem[R_CXL_HDM_DECODER0_CTRL];
207 iw = FIELD_EX32(hdm0_ctrl, CXL_HDM_DECODER0_CTRL, IW);
208 ig = FIELD_EX32(hdm0_ctrl, CXL_HDM_DECODER0_CTRL, IG);
209
210 *dpa = (MAKE_64BIT_MASK(0, 8 + ig) & hpa_offset) |
211 ((MAKE_64BIT_MASK(8 + ig + iw, 64 - 8 - ig - iw) & hpa_offset) >> iw);
212
213 return true;
214}
215
216MemTxResult cxl_type3_read(PCIDevice *d, hwaddr host_addr, uint64_t *data,
217 unsigned size, MemTxAttrs attrs)
218{
219 CXLType3Dev *ct3d = CXL_TYPE3(d);
220 uint64_t dpa_offset;
221 MemoryRegion *mr;
222
223
224 mr = host_memory_backend_get_memory(ct3d->hostmem);
225 if (!mr) {
226 return MEMTX_ERROR;
227 }
228
229 if (!cxl_type3_dpa(ct3d, host_addr, &dpa_offset)) {
230 return MEMTX_ERROR;
231 }
232
233 if (dpa_offset > int128_get64(mr->size)) {
234 return MEMTX_ERROR;
235 }
236
237 return address_space_read(&ct3d->hostmem_as, dpa_offset, attrs, data, size);
238}
239
240MemTxResult cxl_type3_write(PCIDevice *d, hwaddr host_addr, uint64_t data,
241 unsigned size, MemTxAttrs attrs)
242{
243 CXLType3Dev *ct3d = CXL_TYPE3(d);
244 uint64_t dpa_offset;
245 MemoryRegion *mr;
246
247 mr = host_memory_backend_get_memory(ct3d->hostmem);
248 if (!mr) {
249 return MEMTX_OK;
250 }
251
252 if (!cxl_type3_dpa(ct3d, host_addr, &dpa_offset)) {
253 return MEMTX_OK;
254 }
255
256 if (dpa_offset > int128_get64(mr->size)) {
257 return MEMTX_OK;
258 }
259 return address_space_write(&ct3d->hostmem_as, dpa_offset, attrs,
260 &data, size);
261}
262
263static void ct3d_reset(DeviceState *dev)
264{
265 CXLType3Dev *ct3d = CXL_TYPE3(dev);
266 uint32_t *reg_state = ct3d->cxl_cstate.crb.cache_mem_registers;
267 uint32_t *write_msk = ct3d->cxl_cstate.crb.cache_mem_regs_write_mask;
268
269 cxl_component_register_init_common(reg_state, write_msk, CXL2_TYPE3_DEVICE);
270 cxl_device_register_init_common(&ct3d->cxl_dstate);
271}
272
273static Property ct3_props[] = {
274 DEFINE_PROP_LINK("memdev", CXLType3Dev, hostmem, TYPE_MEMORY_BACKEND,
275 HostMemoryBackend *),
276 DEFINE_PROP_LINK("lsa", CXLType3Dev, lsa, TYPE_MEMORY_BACKEND,
277 HostMemoryBackend *),
278 DEFINE_PROP_END_OF_LIST(),
279};
280
281static uint64_t get_lsa_size(CXLType3Dev *ct3d)
282{
283 MemoryRegion *mr;
284
285 mr = host_memory_backend_get_memory(ct3d->lsa);
286 return memory_region_size(mr);
287}
288
289static void validate_lsa_access(MemoryRegion *mr, uint64_t size,
290 uint64_t offset)
291{
292 assert(offset + size <= memory_region_size(mr));
293 assert(offset + size > offset);
294}
295
296static uint64_t get_lsa(CXLType3Dev *ct3d, void *buf, uint64_t size,
297 uint64_t offset)
298{
299 MemoryRegion *mr;
300 void *lsa;
301
302 mr = host_memory_backend_get_memory(ct3d->lsa);
303 validate_lsa_access(mr, size, offset);
304
305 lsa = memory_region_get_ram_ptr(mr) + offset;
306 memcpy(buf, lsa, size);
307
308 return size;
309}
310
311static void set_lsa(CXLType3Dev *ct3d, const void *buf, uint64_t size,
312 uint64_t offset)
313{
314 MemoryRegion *mr;
315 void *lsa;
316
317 mr = host_memory_backend_get_memory(ct3d->lsa);
318 validate_lsa_access(mr, size, offset);
319
320 lsa = memory_region_get_ram_ptr(mr) + offset;
321 memcpy(lsa, buf, size);
322 memory_region_set_dirty(mr, offset, size);
323
324
325
326
327
328}
329
330static void ct3_class_init(ObjectClass *oc, void *data)
331{
332 DeviceClass *dc = DEVICE_CLASS(oc);
333 PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
334 CXLType3Class *cvc = CXL_TYPE3_CLASS(oc);
335
336 pc->realize = ct3_realize;
337 pc->exit = ct3_exit;
338 pc->class_id = PCI_CLASS_STORAGE_EXPRESS;
339 pc->vendor_id = PCI_VENDOR_ID_INTEL;
340 pc->device_id = 0xd93;
341 pc->revision = 1;
342
343 set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
344 dc->desc = "CXL PMEM Device (Type 3)";
345 dc->reset = ct3d_reset;
346 device_class_set_props(dc, ct3_props);
347
348 cvc->get_lsa_size = get_lsa_size;
349 cvc->get_lsa = get_lsa;
350 cvc->set_lsa = set_lsa;
351}
352
353static const TypeInfo ct3d_info = {
354 .name = TYPE_CXL_TYPE3,
355 .parent = TYPE_PCI_DEVICE,
356 .class_size = sizeof(struct CXLType3Class),
357 .class_init = ct3_class_init,
358 .instance_size = sizeof(CXLType3Dev),
359 .interfaces = (InterfaceInfo[]) {
360 { INTERFACE_CXL_DEVICE },
361 { INTERFACE_PCIE_DEVICE },
362 {}
363 },
364};
365
366static void ct3d_registers(void)
367{
368 type_register_static(&ct3d_info);
369}
370
371type_init(ct3d_registers);
372