264
return d;
265
}
266
267
+typedef struct CXLDirectPTState {
268
+ CXLType3Dev *ct3d;
269
+ hwaddr decoder_base;
270
+ hwaddr decoder_size;
271
+ hwaddr dpa_base;
272
+ unsigned int hdm_decoder_idx;
273
+} CXLDirectPTState;
274
+
275
+static void cxl_fmws_direct_passthrough_setup(CXLDirectPTState *state,
276
+ CXLFixedWindow *fw)
277
+{
278
+ CXLType3Dev *ct3d = state->ct3d;
279
+ MemoryRegion *mr = NULL;
280
+ uint64_t vmr_size = 0, pmr_size = 0, offset = 0;
281
+ MemoryRegion *direct_mr;
282
+ g_autofree char *direct_mr_name;
283
+ unsigned int idx = state->hdm_decoder_idx;
284
+
285
+ if (ct3d->hostvmem) {
286
+ MemoryRegion *vmr = host_memory_backend_get_memory(ct3d->hostvmem);
287
+
288
+ vmr_size = memory_region_size(vmr);
289
+ if (state->dpa_base < vmr_size) {
290
+ mr = vmr;
291
+ offset = state->dpa_base;
292
+ }
293
+ }
294
+ if (!mr && ct3d->hostpmem) {
295
+ MemoryRegion *pmr = host_memory_backend_get_memory(ct3d->hostpmem);
296
+
297
+ pmr_size = memory_region_size(pmr);
298
+ if (state->dpa_base - vmr_size < pmr_size) {
299
+ mr = pmr;
300
+ offset = state->dpa_base - vmr_size;
301
+ }
302
+ }
303
+ if (!mr) {
304
+ return;
305
+ }
306
+
307
+ if (ct3d->direct_mr_fw[idx]) {
308
+ return;
309
+ }
310
+
311
+ direct_mr = &ct3d->direct_mr[idx];
312
+ direct_mr_name = g_strdup_printf("cxl-direct-mapping-alias-%u", idx);
313
+ if (!direct_mr_name) {
314
+ return;
315
+ }
316
+
317
+ memory_region_init_alias(direct_mr, OBJECT(ct3d), direct_mr_name, mr,
318
+ offset, state->decoder_size);
319
+ memory_region_transaction_begin();
320
+ memory_region_add_subregion(&fw->mr,
321
+ state->decoder_base - fw->base, direct_mr);
322
+ memory_region_transaction_commit();
323
+ ct3d->direct_mr_fw[idx] = fw;
324
+}
325
+
326
+static void cxl_fmws_direct_passthrough_remove(CXLType3Dev *ct3d,
327
+ uint64_t decoder_base,
328
+ unsigned int idx)
329
+{
330
+ CXLFixedWindow *owner_fw = ct3d->direct_mr_fw[idx];
331
+ MemoryRegion *direct_mr = &ct3d->direct_mr[idx];
332
+
333
+ if (!owner_fw) {
334
+ return;
335
+ }
336
+
337
+ if (!memory_region_is_mapped(direct_mr)) {
338
+ return;
339
+ }
340
+
341
+ if (cxl_cfmws_find_device(owner_fw, decoder_base, false)) {
342
+ return;
343
+ }
344
+
345
+ memory_region_transaction_begin();
346
+ memory_region_del_subregion(&owner_fw->mr, direct_mr);
347
+ object_unparent(OBJECT(direct_mr));
348
+ memory_region_transaction_commit();
349
+ ct3d->direct_mr_fw[idx] = NULL;
350
+}
351
+
352
+static int cxl_fmws_direct_passthrough(Object *obj, void *opaque)
353
+{
354
+ CXLDirectPTState *state = opaque;
355
+ CXLFixedWindow *fw;
356
+
357
+ if (!object_dynamic_cast(obj, TYPE_CXL_FMW)) {
358
+ return 0;
359
+ }
360
+
361
+ fw = CXL_FMW(obj);
362
+
363
+ /* Verify not interleaved */
364
+ if (!cxl_cfmws_find_device(fw, state->decoder_base, false)) {
365
+ return 0;
366
+ }
367
+
368
+ cxl_fmws_direct_passthrough_setup(state, fw);
369
+
370
+ return 0;
371
+}
372
+
373
+static int update_non_interleaved(Object *obj, void *opaque)
374
+{
375
+ const int hdm_inc = R_CXL_HDM_DECODER1_BASE_LO - R_CXL_HDM_DECODER0_BASE_LO;
376
+ bool commit = *(bool *)opaque;
377
+ CXLType3Dev *ct3d;
378
+ uint32_t *cache_mem;
379
+ unsigned int hdm_count, i;
380
+ int interleave_ways_dec;
381
+ uint32_t cap;
382
+ uint64_t dpa_base = 0;
383
+
384
+ if (!object_dynamic_cast(obj, TYPE_CXL_TYPE3)) {
385
+ return 0;
386
+ }
387
+
388
+ ct3d = CXL_TYPE3(obj);
389
+ cache_mem = ct3d->cxl_cstate.crb.cache_mem_registers;
390
+ cap = ldl_le_p(cache_mem + R_CXL_HDM_DECODER_CAPABILITY);
391
+ hdm_count = cxl_decoder_count_dec(FIELD_EX32(cap,
392
+ CXL_HDM_DECODER_CAPABILITY,
393
+ DECODER_COUNT));
394
+ for (i = 0; i < hdm_count; i++) {
395
+ uint64_t decoder_base, decoder_size, skip;
396
+ uint32_t hdm_ctrl, low, high;
397
+ int iw, committed;
398
+
399
+ hdm_ctrl = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_CTRL + i * hdm_inc);
400
+ committed = FIELD_EX32(hdm_ctrl, CXL_HDM_DECODER0_CTRL, COMMITTED);
401
+
402
+ /*
403
+ * Optimization: Looking for a fully committed path; if the type 3 HDM
404
+ * decoder is not commmitted, it cannot lie on such a path.
405
+ */
406
+ if (commit && !committed) {
407
+ return 0;
408
+ }
409
+
410
+ low = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_DPA_SKIP_LO +
411
+ i * hdm_inc);
412
+ high = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_DPA_SKIP_HI +
413
+ i * hdm_inc);
414
+ skip = ((uint64_t)high << 32) | (low & 0xf0000000);
415
+ dpa_base += skip;
416
+
417
+ low = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_SIZE_LO + i * hdm_inc);
418
+ high = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_SIZE_HI + i * hdm_inc);
419
+ decoder_size = ((uint64_t)high << 32) | (low & 0xf0000000);
420
+
421
+ low = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_BASE_LO + i * hdm_inc);
422
+ high = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_BASE_HI + i * hdm_inc);
423
+ decoder_base = ((uint64_t)high << 32) | (low & 0xf0000000);
424
+
425
+ iw = FIELD_EX32(hdm_ctrl, CXL_HDM_DECODER0_CTRL, IW);
426
+
427
+ if (iw == 0) {
428
+ if (!commit) {
429
+ cxl_fmws_direct_passthrough_remove(ct3d, decoder_base, i);
430
+ } else {
431
+ CXLDirectPTState state = {
432
+ .ct3d = ct3d,
433
+ .decoder_base = decoder_base,
434
+ .decoder_size = decoder_size,
435
+ .dpa_base = dpa_base,
436
+ .hdm_decoder_idx = i,
437
+ };
438
+
439
+ object_child_foreach_recursive(object_get_root(),
440
+ cxl_fmws_direct_passthrough,
441
+ &state);
442
+ }
443
+ }
444
+
445
+ interleave_ways_dec = cxl_interleave_ways_dec(iw, &error_fatal);
446
+ if (interleave_ways_dec == 0) {
447
+ return 0;
448
+ }
449
+
450
+ dpa_base += decoder_size / interleave_ways_dec;
451
+ }
452
+
453
+ return 0;
454
+}
455
+
456
+void cfmws_update_non_interleaved(bool commit)
457
+{
458
+ /*
459
+ * Walk endpoints to find both committed and uncommitted decoders,
460
+ * then check if they are not interleaved (but the path is fully set up).
461
+ */
462
+ object_child_foreach_recursive(object_get_root(),
463
+ update_non_interleaved, &commit);
464
+
465
+ return;
466
+}
467
+
468
static MemTxResult cxl_read_cfmws(void *opaque, hwaddr addr, uint64_t *data,
469
unsigned size, MemTxAttrs attrs)
470
{