2 * CXL host parameter parsing routines
4 * Copyright (c) 2022 Huawei
5 * Modeled loosely on the NUMA options handling in hw/core/numa.c
8 #include "qemu/osdep.h"
9 #include "qemu/units.h"
10 #include "qemu/bitmap.h"
11 #include "qemu/error-report.h"
12 #include "qapi/error.h"
13 #include "sysemu/qtest.h"
14 #include "hw/boards.h"
16 #include "qapi/qapi-visit-machine.h"
17 #include "hw/cxl/cxl.h"
18 #include "hw/cxl/cxl_host.h"
19 #include "hw/pci/pci_bus.h"
20 #include "hw/pci/pci_bridge.h"
21 #include "hw/pci/pci_host.h"
22 #include "hw/pci/pcie_port.h"
23 #include "hw/pci-bridge/pci_expander_bridge.h"
25 static void cxl_fixed_memory_window_config(CXLState
*cxl_state
,
26 CXLFixedMemoryWindowOptions
*object
,
29 g_autofree CXLFixedWindow
*fw
= g_malloc0(sizeof(*fw
));
33 for (target
= object
->targets
; target
; target
= target
->next
) {
37 fw
->enc_int_ways
= cxl_interleave_ways_enc(fw
->num_targets
, errp
);
42 fw
->targets
= g_malloc0_n(fw
->num_targets
, sizeof(*fw
->targets
));
43 for (i
= 0, target
= object
->targets
; target
; i
++, target
= target
->next
) {
44 /* This link cannot be resolved yet, so stash the name for now */
45 fw
->targets
[i
] = g_strdup(target
->value
);
48 if (object
->size
% (256 * MiB
)) {
50 "Size of a CXL fixed memory window must be a multiple of 256MiB");
53 fw
->size
= object
->size
;
55 if (object
->has_interleave_granularity
) {
57 cxl_interleave_granularity_enc(object
->interleave_granularity
,
63 /* Default to 256 byte interleave */
67 cxl_state
->fixed_windows
= g_list_append(cxl_state
->fixed_windows
,
68 g_steal_pointer(&fw
));
73 void cxl_fmws_link_targets(CXLState
*cxl_state
, Error
**errp
)
75 if (cxl_state
&& cxl_state
->fixed_windows
) {
78 for (it
= cxl_state
->fixed_windows
; it
; it
= it
->next
) {
79 CXLFixedWindow
*fw
= it
->data
;
82 for (i
= 0; i
< fw
->num_targets
; i
++) {
86 o
= object_resolve_path_type(fw
->targets
[i
],
90 error_setg(errp
, "Could not resolve CXLFM target %s",
94 fw
->target_hbs
[i
] = PXB_CXL_DEV(o
);
100 /* TODO: support, multiple hdm decoders */
101 static bool cxl_hdm_find_target(uint32_t *cache_mem
, hwaddr addr
,
109 ctrl
= cache_mem
[R_CXL_HDM_DECODER0_CTRL
];
110 if (!FIELD_EX32(ctrl
, CXL_HDM_DECODER0_CTRL
, COMMITTED
)) {
114 ig_enc
= FIELD_EX32(ctrl
, CXL_HDM_DECODER0_CTRL
, IG
);
115 iw_enc
= FIELD_EX32(ctrl
, CXL_HDM_DECODER0_CTRL
, IW
);
116 target_idx
= (addr
/ cxl_decode_ig(ig_enc
)) % (1 << iw_enc
);
118 if (target_idx
< 4) {
119 *target
= extract32(cache_mem
[R_CXL_HDM_DECODER0_TARGET_LIST_LO
],
122 *target
= extract32(cache_mem
[R_CXL_HDM_DECODER0_TARGET_LIST_HI
],
123 (target_idx
- 4) * 8, 8);
129 static PCIDevice
*cxl_cfmws_find_device(CXLFixedWindow
*fw
, hwaddr addr
)
131 CXLComponentState
*hb_cstate
, *usp_cstate
;
133 CXLUpstreamPort
*usp
;
140 /* Address is relative to memory region. Convert to HPA */
143 rb_index
= (addr
/ cxl_decode_ig(fw
->enc_int_gran
)) % fw
->num_targets
;
144 hb
= PCI_HOST_BRIDGE(fw
->target_hbs
[rb_index
]->cxl
.cxl_host_bridge
);
145 if (!hb
|| !hb
->bus
|| !pci_bus_is_cxl(hb
->bus
)) {
149 hb_cstate
= cxl_get_hb_cstate(hb
);
154 cache_mem
= hb_cstate
->crb
.cache_mem_registers
;
156 target_found
= cxl_hdm_find_target(cache_mem
, addr
, &target
);
161 rp
= pcie_find_port_by_pn(hb
->bus
, target
);
166 d
= pci_bridge_get_sec_bus(PCI_BRIDGE(rp
))->devices
[0];
171 if (object_dynamic_cast(OBJECT(d
), TYPE_CXL_TYPE3
)) {
176 * Could also be a switch. Note only one level of switching currently
179 if (!object_dynamic_cast(OBJECT(d
), TYPE_CXL_USP
)) {
184 usp_cstate
= cxl_usp_to_cstate(usp
);
189 cache_mem
= usp_cstate
->crb
.cache_mem_registers
;
191 target_found
= cxl_hdm_find_target(cache_mem
, addr
, &target
);
196 d
= pcie_find_port_by_pn(&PCI_BRIDGE(d
)->sec_bus
, target
);
201 d
= pci_bridge_get_sec_bus(PCI_BRIDGE(d
))->devices
[0];
206 if (!object_dynamic_cast(OBJECT(d
), TYPE_CXL_TYPE3
)) {
213 static MemTxResult
cxl_read_cfmws(void *opaque
, hwaddr addr
, uint64_t *data
,
214 unsigned size
, MemTxAttrs attrs
)
216 CXLFixedWindow
*fw
= opaque
;
219 d
= cxl_cfmws_find_device(fw
, addr
);
222 /* Reads to invalid address return poison */
226 return cxl_type3_read(d
, addr
+ fw
->base
, data
, size
, attrs
);
229 static MemTxResult
cxl_write_cfmws(void *opaque
, hwaddr addr
,
230 uint64_t data
, unsigned size
,
233 CXLFixedWindow
*fw
= opaque
;
236 d
= cxl_cfmws_find_device(fw
, addr
);
238 /* Writes to invalid address are silent */
242 return cxl_type3_write(d
, addr
+ fw
->base
, data
, size
, attrs
);
245 const MemoryRegionOps cfmws_ops
= {
246 .read_with_attrs
= cxl_read_cfmws
,
247 .write_with_attrs
= cxl_write_cfmws
,
248 .endianness
= DEVICE_LITTLE_ENDIAN
,
250 .min_access_size
= 1,
251 .max_access_size
= 8,
255 .min_access_size
= 1,
256 .max_access_size
= 8,
261 static void machine_get_cxl(Object
*obj
, Visitor
*v
, const char *name
,
262 void *opaque
, Error
**errp
)
264 CXLState
*cxl_state
= opaque
;
265 bool value
= cxl_state
->is_enabled
;
267 visit_type_bool(v
, name
, &value
, errp
);
270 static void machine_set_cxl(Object
*obj
, Visitor
*v
, const char *name
,
271 void *opaque
, Error
**errp
)
273 CXLState
*cxl_state
= opaque
;
276 if (!visit_type_bool(v
, name
, &value
, errp
)) {
279 cxl_state
->is_enabled
= value
;
282 static void machine_get_cfmw(Object
*obj
, Visitor
*v
, const char *name
,
283 void *opaque
, Error
**errp
)
285 CXLFixedMemoryWindowOptionsList
**list
= opaque
;
287 visit_type_CXLFixedMemoryWindowOptionsList(v
, name
, list
, errp
);
290 static void machine_set_cfmw(Object
*obj
, Visitor
*v
, const char *name
,
291 void *opaque
, Error
**errp
)
293 CXLState
*state
= opaque
;
294 CXLFixedMemoryWindowOptionsList
*cfmw_list
= NULL
;
295 CXLFixedMemoryWindowOptionsList
*it
;
297 visit_type_CXLFixedMemoryWindowOptionsList(v
, name
, &cfmw_list
, errp
);
302 for (it
= cfmw_list
; it
; it
= it
->next
) {
303 cxl_fixed_memory_window_config(state
, it
->value
, errp
);
305 state
->cfmw_list
= cfmw_list
;
308 void cxl_machine_init(Object
*obj
, CXLState
*state
)
310 object_property_add(obj
, "cxl", "bool", machine_get_cxl
,
311 machine_set_cxl
, NULL
, state
);
312 object_property_set_description(obj
, "cxl",
313 "Set on/off to enable/disable "
314 "CXL instantiation");
316 object_property_add(obj
, "cxl-fmw", "CXLFixedMemoryWindow",
317 machine_get_cfmw
, machine_set_cfmw
,
319 object_property_set_description(obj
, "cxl-fmw",
320 "CXL Fixed Memory Windows (array)");
323 void cxl_hook_up_pxb_registers(PCIBus
*bus
, CXLState
*state
, Error
**errp
)
325 /* Walk the pci busses looking for pxb busses to hook up */
327 QLIST_FOREACH(bus
, &bus
->child
, sibling
) {
328 if (!pci_bus_is_root(bus
)) {
331 if (pci_bus_is_cxl(bus
)) {
332 if (!state
->is_enabled
) {
333 error_setg(errp
, "CXL host bridges present, but cxl=off");
336 pxb_cxl_hook_up_registers(state
, bus
, errp
);