xref: /openbmc/qemu/hw/cxl/cxl-host.c (revision 61c44bcf510f4db51c28d0288e528cfdf0ebabc3)
1 /*
2  * CXL host parameter parsing routines
3  *
4  * Copyright (c) 2022 Huawei
5  * Modeled loosely on the NUMA options handling in hw/core/numa.c
6  */
7 
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"
15 
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"
24 
25 static void cxl_fixed_memory_window_config(CXLState *cxl_state,
26                                            CXLFixedMemoryWindowOptions *object,
27                                            Error **errp)
28 {
29     g_autofree CXLFixedWindow *fw = g_malloc0(sizeof(*fw));
30     strList *target;
31     int i;
32 
33     for (target = object->targets; target; target = target->next) {
34         fw->num_targets++;
35     }
36 
37     fw->enc_int_ways = cxl_interleave_ways_enc(fw->num_targets, errp);
38     if (*errp) {
39         return;
40     }
41 
42     if (object->size % (256 * MiB)) {
43         error_setg(errp,
44                    "Size of a CXL fixed memory window must be a multiple of 256MiB");
45         return;
46     }
47     fw->size = object->size;
48 
49     if (object->has_interleave_granularity) {
50         fw->enc_int_gran =
51             cxl_interleave_granularity_enc(object->interleave_granularity,
52                                            errp);
53         if (*errp) {
54             return;
55         }
56     } else {
57         /* Default to 256 byte interleave */
58         fw->enc_int_gran = 0;
59     }
60 
61     fw->targets = g_malloc0_n(fw->num_targets, sizeof(*fw->targets));
62     for (i = 0, target = object->targets; target; i++, target = target->next) {
63         /* This link cannot be resolved yet, so stash the name for now */
64         fw->targets[i] = g_strdup(target->value);
65     }
66 
67     cxl_state->fixed_windows = g_list_append(cxl_state->fixed_windows,
68                                              g_steal_pointer(&fw));
69 
70     return;
71 }
72 
73 void cxl_fmws_link_targets(CXLState *cxl_state, Error **errp)
74 {
75     if (cxl_state && cxl_state->fixed_windows) {
76         GList *it;
77 
78         for (it = cxl_state->fixed_windows; it; it = it->next) {
79             CXLFixedWindow *fw = it->data;
80             int i;
81 
82             for (i = 0; i < fw->num_targets; i++) {
83                 Object *o;
84                 bool ambig;
85 
86                 o = object_resolve_path_type(fw->targets[i],
87                                              TYPE_PXB_CXL_DEV,
88                                              &ambig);
89                 if (!o) {
90                     error_setg(errp, "Could not resolve CXLFM target %s",
91                                fw->targets[i]);
92                     return;
93                 }
94                 fw->target_hbs[i] = PXB_CXL_DEV(o);
95             }
96         }
97     }
98 }
99 
100 /* TODO: support, multiple hdm decoders */
101 static bool cxl_hdm_find_target(uint32_t *cache_mem, hwaddr addr,
102                                 uint8_t *target)
103 {
104     int hdm_inc = R_CXL_HDM_DECODER1_BASE_LO - R_CXL_HDM_DECODER0_BASE_LO;
105     uint32_t ctrl;
106     uint32_t ig_enc;
107     uint32_t iw_enc;
108     uint32_t target_idx;
109     int i = 0;
110 
111     ctrl = cache_mem[R_CXL_HDM_DECODER0_CTRL + i * hdm_inc];
112     if (!FIELD_EX32(ctrl, CXL_HDM_DECODER0_CTRL, COMMITTED)) {
113         return false;
114     }
115 
116     ig_enc = FIELD_EX32(ctrl, CXL_HDM_DECODER0_CTRL, IG);
117     iw_enc = FIELD_EX32(ctrl, CXL_HDM_DECODER0_CTRL, IW);
118     target_idx = (addr / cxl_decode_ig(ig_enc)) % (1 << iw_enc);
119 
120     if (target_idx < 4) {
121         *target = extract32(cache_mem[R_CXL_HDM_DECODER0_TARGET_LIST_LO],
122                             target_idx * 8, 8);
123     } else {
124         *target = extract32(cache_mem[R_CXL_HDM_DECODER0_TARGET_LIST_HI],
125                             (target_idx - 4) * 8, 8);
126     }
127 
128     return true;
129 }
130 
131 static PCIDevice *cxl_cfmws_find_device(CXLFixedWindow *fw, hwaddr addr)
132 {
133     CXLComponentState *hb_cstate, *usp_cstate;
134     PCIHostState *hb;
135     CXLUpstreamPort *usp;
136     int rb_index;
137     uint32_t *cache_mem;
138     uint8_t target;
139     bool target_found;
140     PCIDevice *rp, *d;
141 
142     /* Address is relative to memory region. Convert to HPA */
143     addr += fw->base;
144 
145     rb_index = (addr / cxl_decode_ig(fw->enc_int_gran)) % fw->num_targets;
146     hb = PCI_HOST_BRIDGE(fw->target_hbs[rb_index]->cxl_host_bridge);
147     if (!hb || !hb->bus || !pci_bus_is_cxl(hb->bus)) {
148         return NULL;
149     }
150 
151     if (cxl_get_hb_passthrough(hb)) {
152         rp = pcie_find_port_first(hb->bus);
153         if (!rp) {
154             return NULL;
155         }
156     } else {
157         hb_cstate = cxl_get_hb_cstate(hb);
158         if (!hb_cstate) {
159             return NULL;
160         }
161 
162         cache_mem = hb_cstate->crb.cache_mem_registers;
163 
164         target_found = cxl_hdm_find_target(cache_mem, addr, &target);
165         if (!target_found) {
166             return NULL;
167         }
168 
169         rp = pcie_find_port_by_pn(hb->bus, target);
170         if (!rp) {
171             return NULL;
172         }
173     }
174 
175     d = pci_bridge_get_sec_bus(PCI_BRIDGE(rp))->devices[0];
176     if (!d) {
177         return NULL;
178     }
179 
180     if (object_dynamic_cast(OBJECT(d), TYPE_CXL_TYPE3)) {
181         return d;
182     }
183 
184     /*
185      * Could also be a switch.  Note only one level of switching currently
186      * supported.
187      */
188     if (!object_dynamic_cast(OBJECT(d), TYPE_CXL_USP)) {
189         return NULL;
190     }
191     usp = CXL_USP(d);
192 
193     usp_cstate = cxl_usp_to_cstate(usp);
194     if (!usp_cstate) {
195         return NULL;
196     }
197 
198     cache_mem = usp_cstate->crb.cache_mem_registers;
199 
200     target_found = cxl_hdm_find_target(cache_mem, addr, &target);
201     if (!target_found) {
202         return NULL;
203     }
204 
205     d = pcie_find_port_by_pn(&PCI_BRIDGE(d)->sec_bus, target);
206     if (!d) {
207         return NULL;
208     }
209 
210     d = pci_bridge_get_sec_bus(PCI_BRIDGE(d))->devices[0];
211     if (!d) {
212         return NULL;
213     }
214 
215     if (!object_dynamic_cast(OBJECT(d), TYPE_CXL_TYPE3)) {
216         return NULL;
217     }
218 
219     return d;
220 }
221 
222 static MemTxResult cxl_read_cfmws(void *opaque, hwaddr addr, uint64_t *data,
223                                   unsigned size, MemTxAttrs attrs)
224 {
225     CXLFixedWindow *fw = opaque;
226     PCIDevice *d;
227 
228     d = cxl_cfmws_find_device(fw, addr);
229     if (d == NULL) {
230         *data = 0;
231         /* Reads to invalid address return poison */
232         return MEMTX_ERROR;
233     }
234 
235     return cxl_type3_read(d, addr + fw->base, data, size, attrs);
236 }
237 
238 static MemTxResult cxl_write_cfmws(void *opaque, hwaddr addr,
239                                    uint64_t data, unsigned size,
240                                    MemTxAttrs attrs)
241 {
242     CXLFixedWindow *fw = opaque;
243     PCIDevice *d;
244 
245     d = cxl_cfmws_find_device(fw, addr);
246     if (d == NULL) {
247         /* Writes to invalid address are silent */
248         return MEMTX_OK;
249     }
250 
251     return cxl_type3_write(d, addr + fw->base, data, size, attrs);
252 }
253 
254 const MemoryRegionOps cfmws_ops = {
255     .read_with_attrs = cxl_read_cfmws,
256     .write_with_attrs = cxl_write_cfmws,
257     .endianness = DEVICE_LITTLE_ENDIAN,
258     .valid = {
259         .min_access_size = 1,
260         .max_access_size = 8,
261         .unaligned = true,
262     },
263     .impl = {
264         .min_access_size = 1,
265         .max_access_size = 8,
266         .unaligned = true,
267     },
268 };
269 
270 static void machine_get_cxl(Object *obj, Visitor *v, const char *name,
271                             void *opaque, Error **errp)
272 {
273     CXLState *cxl_state = opaque;
274     bool value = cxl_state->is_enabled;
275 
276     visit_type_bool(v, name, &value, errp);
277 }
278 
279 static void machine_set_cxl(Object *obj, Visitor *v, const char *name,
280                             void *opaque, Error **errp)
281 {
282     CXLState *cxl_state = opaque;
283     bool value;
284 
285     if (!visit_type_bool(v, name, &value, errp)) {
286         return;
287     }
288     cxl_state->is_enabled = value;
289 }
290 
291 static void machine_get_cfmw(Object *obj, Visitor *v, const char *name,
292                              void *opaque, Error **errp)
293 {
294     CXLFixedMemoryWindowOptionsList **list = opaque;
295 
296     visit_type_CXLFixedMemoryWindowOptionsList(v, name, list, errp);
297 }
298 
299 static void machine_set_cfmw(Object *obj, Visitor *v, const char *name,
300                              void *opaque, Error **errp)
301 {
302     CXLState *state = opaque;
303     CXLFixedMemoryWindowOptionsList *cfmw_list = NULL;
304     CXLFixedMemoryWindowOptionsList *it;
305 
306     visit_type_CXLFixedMemoryWindowOptionsList(v, name, &cfmw_list, errp);
307     if (!cfmw_list) {
308         return;
309     }
310 
311     for (it = cfmw_list; it; it = it->next) {
312         cxl_fixed_memory_window_config(state, it->value, errp);
313     }
314     state->cfmw_list = cfmw_list;
315 }
316 
317 void cxl_machine_init(Object *obj, CXLState *state)
318 {
319     object_property_add(obj, "cxl", "bool", machine_get_cxl,
320                         machine_set_cxl, NULL, state);
321     object_property_set_description(obj, "cxl",
322                                     "Set on/off to enable/disable "
323                                     "CXL instantiation");
324 
325     object_property_add(obj, "cxl-fmw", "CXLFixedMemoryWindow",
326                         machine_get_cfmw, machine_set_cfmw,
327                         NULL, state);
328     object_property_set_description(obj, "cxl-fmw",
329                                     "CXL Fixed Memory Windows (array)");
330 }
331 
332 void cxl_hook_up_pxb_registers(PCIBus *bus, CXLState *state, Error **errp)
333 {
334     /* Walk the pci busses looking for pxb busses to hook up */
335     if (bus) {
336         QLIST_FOREACH(bus, &bus->child, sibling) {
337             if (!pci_bus_is_root(bus)) {
338                 continue;
339             }
340             if (pci_bus_is_cxl(bus)) {
341                 if (!state->is_enabled) {
342                     error_setg(errp, "CXL host bridges present, but cxl=off");
343                     return;
344                 }
345                 pxb_cxl_hook_up_registers(state, bus, errp);
346             }
347         }
348     }
349 }
350