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
,
30 g_autofree CXLFixedWindow
*fw
= g_malloc0(sizeof(*fw
));
34 for (target
= object
->targets
; target
; target
= target
->next
) {
38 fw
->enc_int_ways
= cxl_interleave_ways_enc(fw
->num_targets
, errp
);
43 if (object
->size
% (256 * MiB
)) {
45 "Size of a CXL fixed memory window must be a multiple of 256MiB");
48 fw
->size
= object
->size
;
50 if (object
->has_interleave_granularity
) {
52 cxl_interleave_granularity_enc(object
->interleave_granularity
,
58 /* Default to 256 byte interleave */
62 fw
->targets
= g_malloc0_n(fw
->num_targets
, sizeof(*fw
->targets
));
63 for (i
= 0, target
= object
->targets
; target
; i
++, target
= target
->next
) {
64 /* This link cannot be resolved yet, so stash the name for now */
65 fw
->targets
[i
] = g_strdup(target
->value
);
68 cxl_state
->fixed_windows
= g_list_append(cxl_state
->fixed_windows
,
69 g_steal_pointer(&fw
));
74 void cxl_fmws_link_targets(CXLState
*cxl_state
, Error
**errp
)
76 if (cxl_state
&& cxl_state
->fixed_windows
) {
79 for (it
= cxl_state
->fixed_windows
; it
; it
= it
->next
) {
80 CXLFixedWindow
*fw
= it
->data
;
83 for (i
= 0; i
< fw
->num_targets
; i
++) {
87 o
= object_resolve_path_type(fw
->targets
[i
],
91 error_setg(errp
, "Could not resolve CXLFM target %s",
95 fw
->target_hbs
[i
] = PXB_CXL_DEV(o
);
101 static bool cxl_hdm_find_target(uint32_t *cache_mem
, hwaddr addr
,
104 int hdm_inc
= R_CXL_HDM_DECODER1_BASE_LO
- R_CXL_HDM_DECODER0_BASE_LO
;
105 unsigned int hdm_count
;
110 cap
= ldl_le_p(cache_mem
+ R_CXL_HDM_DECODER_CAPABILITY
);
111 hdm_count
= cxl_decoder_count_dec(FIELD_EX32(cap
,
112 CXL_HDM_DECODER_CAPABILITY
,
114 for (i
= 0; i
< hdm_count
; i
++) {
115 uint32_t ctrl
, ig_enc
, iw_enc
, target_idx
;
119 low
= ldl_le_p(cache_mem
+ R_CXL_HDM_DECODER0_BASE_LO
+ i
* hdm_inc
);
120 high
= ldl_le_p(cache_mem
+ R_CXL_HDM_DECODER0_BASE_HI
+ i
* hdm_inc
);
121 base
= (low
& 0xf0000000) | ((uint64_t)high
<< 32);
122 low
= ldl_le_p(cache_mem
+ R_CXL_HDM_DECODER0_SIZE_LO
+ i
* hdm_inc
);
123 high
= ldl_le_p(cache_mem
+ R_CXL_HDM_DECODER0_SIZE_HI
+ i
* hdm_inc
);
124 size
= (low
& 0xf0000000) | ((uint64_t)high
<< 32);
125 if (addr
< base
|| addr
>= base
+ size
) {
129 ctrl
= ldl_le_p(cache_mem
+ R_CXL_HDM_DECODER0_CTRL
+ i
* hdm_inc
);
130 if (!FIELD_EX32(ctrl
, CXL_HDM_DECODER0_CTRL
, COMMITTED
)) {
134 ig_enc
= FIELD_EX32(ctrl
, CXL_HDM_DECODER0_CTRL
, IG
);
135 iw_enc
= FIELD_EX32(ctrl
, CXL_HDM_DECODER0_CTRL
, IW
);
136 target_idx
= (addr
/ cxl_decode_ig(ig_enc
)) % (1 << iw_enc
);
138 if (target_idx
< 4) {
139 uint32_t val
= ldl_le_p(cache_mem
+
140 R_CXL_HDM_DECODER0_TARGET_LIST_LO
+
142 *target
= extract32(val
, target_idx
* 8, 8);
144 uint32_t val
= ldl_le_p(cache_mem
+
145 R_CXL_HDM_DECODER0_TARGET_LIST_HI
+
147 *target
= extract32(val
, (target_idx
- 4) * 8, 8);
155 static PCIDevice
*cxl_cfmws_find_device(CXLFixedWindow
*fw
, hwaddr addr
)
157 CXLComponentState
*hb_cstate
, *usp_cstate
;
159 CXLUpstreamPort
*usp
;
166 /* Address is relative to memory region. Convert to HPA */
169 rb_index
= (addr
/ cxl_decode_ig(fw
->enc_int_gran
)) % fw
->num_targets
;
170 hb
= PCI_HOST_BRIDGE(fw
->target_hbs
[rb_index
]->cxl_host_bridge
);
171 if (!hb
|| !hb
->bus
|| !pci_bus_is_cxl(hb
->bus
)) {
175 if (cxl_get_hb_passthrough(hb
)) {
176 rp
= pcie_find_port_first(hb
->bus
);
181 hb_cstate
= cxl_get_hb_cstate(hb
);
186 cache_mem
= hb_cstate
->crb
.cache_mem_registers
;
188 target_found
= cxl_hdm_find_target(cache_mem
, addr
, &target
);
193 rp
= pcie_find_port_by_pn(hb
->bus
, target
);
199 d
= pci_bridge_get_sec_bus(PCI_BRIDGE(rp
))->devices
[0];
204 if (object_dynamic_cast(OBJECT(d
), TYPE_CXL_TYPE3
)) {
209 * Could also be a switch. Note only one level of switching currently
212 if (!object_dynamic_cast(OBJECT(d
), TYPE_CXL_USP
)) {
217 usp_cstate
= cxl_usp_to_cstate(usp
);
222 cache_mem
= usp_cstate
->crb
.cache_mem_registers
;
224 target_found
= cxl_hdm_find_target(cache_mem
, addr
, &target
);
229 d
= pcie_find_port_by_pn(&PCI_BRIDGE(d
)->sec_bus
, target
);
234 d
= pci_bridge_get_sec_bus(PCI_BRIDGE(d
))->devices
[0];
239 if (!object_dynamic_cast(OBJECT(d
), TYPE_CXL_TYPE3
)) {
246 static MemTxResult
cxl_read_cfmws(void *opaque
, hwaddr addr
, uint64_t *data
,
247 unsigned size
, MemTxAttrs attrs
)
249 CXLFixedWindow
*fw
= opaque
;
252 d
= cxl_cfmws_find_device(fw
, addr
);
255 /* Reads to invalid address return poison */
259 return cxl_type3_read(d
, addr
+ fw
->base
, data
, size
, attrs
);
262 static MemTxResult
cxl_write_cfmws(void *opaque
, hwaddr addr
,
263 uint64_t data
, unsigned size
,
266 CXLFixedWindow
*fw
= opaque
;
269 d
= cxl_cfmws_find_device(fw
, addr
);
271 /* Writes to invalid address are silent */
275 return cxl_type3_write(d
, addr
+ fw
->base
, data
, size
, attrs
);
278 const MemoryRegionOps cfmws_ops
= {
279 .read_with_attrs
= cxl_read_cfmws
,
280 .write_with_attrs
= cxl_write_cfmws
,
281 .endianness
= DEVICE_LITTLE_ENDIAN
,
283 .min_access_size
= 1,
284 .max_access_size
= 8,
288 .min_access_size
= 1,
289 .max_access_size
= 8,
294 static void machine_get_cxl(Object
*obj
, Visitor
*v
, const char *name
,
295 void *opaque
, Error
**errp
)
297 CXLState
*cxl_state
= opaque
;
298 bool value
= cxl_state
->is_enabled
;
300 visit_type_bool(v
, name
, &value
, errp
);
303 static void machine_set_cxl(Object
*obj
, Visitor
*v
, const char *name
,
304 void *opaque
, Error
**errp
)
306 CXLState
*cxl_state
= opaque
;
309 if (!visit_type_bool(v
, name
, &value
, errp
)) {
312 cxl_state
->is_enabled
= value
;
315 static void machine_get_cfmw(Object
*obj
, Visitor
*v
, const char *name
,
316 void *opaque
, Error
**errp
)
318 CXLState
*state
= opaque
;
319 CXLFixedMemoryWindowOptionsList
**list
= &state
->cfmw_list
;
321 visit_type_CXLFixedMemoryWindowOptionsList(v
, name
, list
, errp
);
324 static void machine_set_cfmw(Object
*obj
, Visitor
*v
, const char *name
,
325 void *opaque
, Error
**errp
)
327 CXLState
*state
= opaque
;
328 CXLFixedMemoryWindowOptionsList
*cfmw_list
= NULL
;
329 CXLFixedMemoryWindowOptionsList
*it
;
331 visit_type_CXLFixedMemoryWindowOptionsList(v
, name
, &cfmw_list
, errp
);
336 for (it
= cfmw_list
; it
; it
= it
->next
) {
337 cxl_fixed_memory_window_config(state
, it
->value
, errp
);
339 state
->cfmw_list
= cfmw_list
;
342 void cxl_machine_init(Object
*obj
, CXLState
*state
)
344 object_property_add(obj
, "cxl", "bool", machine_get_cxl
,
345 machine_set_cxl
, NULL
, state
);
346 object_property_set_description(obj
, "cxl",
347 "Set on/off to enable/disable "
348 "CXL instantiation");
350 object_property_add(obj
, "cxl-fmw", "CXLFixedMemoryWindow",
351 machine_get_cfmw
, machine_set_cfmw
,
353 object_property_set_description(obj
, "cxl-fmw",
354 "CXL Fixed Memory Windows (array)");
357 void cxl_hook_up_pxb_registers(PCIBus
*bus
, CXLState
*state
, Error
**errp
)
359 /* Walk the pci busses looking for pxb busses to hook up */
361 QLIST_FOREACH(bus
, &bus
->child
, sibling
) {
362 if (!pci_bus_is_root(bus
)) {
365 if (pci_bus_is_cxl(bus
)) {
366 if (!state
->is_enabled
) {
367 error_setg(errp
, "CXL host bridges present, but cxl=off");
370 pxb_cxl_hook_up_registers(state
, bus
, errp
);