xref: /openbmc/qemu/hw/cxl/cxl-host.c (revision 0edc2afe0c8197bbcb98f948c609fb74c9b1ffd5)
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 "system/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 
cxl_fixed_memory_window_config(CXLFixedMemoryWindowOptions * object,int index,Error ** errp)25 static void cxl_fixed_memory_window_config(CXLFixedMemoryWindowOptions *object,
26                                            int index, Error **errp)
27 {
28     ERRP_GUARD();
29     DeviceState *dev = qdev_new(TYPE_CXL_FMW);
30     CXLFixedWindow *fw = CXL_FMW(dev);
31     strList *target;
32     int i;
33 
34     fw->index = index;
35 
36     for (target = object->targets; target; target = target->next) {
37         fw->num_targets++;
38     }
39 
40     fw->enc_int_ways = cxl_interleave_ways_enc(fw->num_targets, errp);
41     if (*errp) {
42         return;
43     }
44 
45     if (object->size % (256 * MiB)) {
46         error_setg(errp,
47                    "Size of a CXL fixed memory window must be a multiple of 256MiB");
48         return;
49     }
50     fw->size = object->size;
51 
52     if (object->has_interleave_granularity) {
53         fw->enc_int_gran =
54             cxl_interleave_granularity_enc(object->interleave_granularity,
55                                            errp);
56         if (*errp) {
57             return;
58         }
59     } else {
60         /* Default to 256 byte interleave */
61         fw->enc_int_gran = 0;
62     }
63 
64     fw->targets = g_malloc0_n(fw->num_targets, sizeof(*fw->targets));
65     for (i = 0, target = object->targets; target; i++, target = target->next) {
66         /* This link cannot be resolved yet, so stash the name for now */
67         fw->targets[i] = g_strdup(target->value);
68     }
69 
70     sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp);
71 }
72 
cxl_fmws_link(Object * obj,void * opaque)73 static int cxl_fmws_link(Object *obj, void *opaque)
74 {
75     struct CXLFixedWindow *fw;
76     int i;
77 
78     if (!object_dynamic_cast(obj, TYPE_CXL_FMW)) {
79         return 0;
80     }
81     fw = CXL_FMW(obj);
82 
83     for (i = 0; i < fw->num_targets; i++) {
84         Object *o;
85         bool ambig;
86 
87         o = object_resolve_path_type(fw->targets[i], TYPE_PXB_CXL_DEV,
88                                      &ambig);
89         if (!o) {
90             error_setg(&error_fatal, "Could not resolve CXLFM target %s",
91                        fw->targets[i]);
92             return 1;
93         }
94         fw->target_hbs[i] = PXB_CXL_DEV(o);
95     }
96     return 0;
97 }
98 
cxl_fmws_link_targets(Error ** errp)99 void cxl_fmws_link_targets(Error **errp)
100 {
101     /* Order doesn't matter for this, so no need to build list */
102     object_child_foreach_recursive(object_get_root(), cxl_fmws_link, NULL);
103 }
104 
cxl_hdm_find_target(uint32_t * cache_mem,hwaddr addr,uint8_t * target)105 static bool cxl_hdm_find_target(uint32_t *cache_mem, hwaddr addr,
106                                 uint8_t *target)
107 {
108     int hdm_inc = R_CXL_HDM_DECODER1_BASE_LO - R_CXL_HDM_DECODER0_BASE_LO;
109     unsigned int hdm_count;
110     bool found = false;
111     int i;
112     uint32_t cap;
113 
114     cap = ldl_le_p(cache_mem + R_CXL_HDM_DECODER_CAPABILITY);
115     hdm_count = cxl_decoder_count_dec(FIELD_EX32(cap,
116                                                  CXL_HDM_DECODER_CAPABILITY,
117                                                  DECODER_COUNT));
118     for (i = 0; i < hdm_count; i++) {
119         uint32_t ctrl, ig_enc, iw_enc, target_idx;
120         uint32_t low, high;
121         uint64_t base, size;
122 
123         low = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_BASE_LO + i * hdm_inc);
124         high = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_BASE_HI + i * hdm_inc);
125         base = (low & 0xf0000000) | ((uint64_t)high << 32);
126         low = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_SIZE_LO + i * hdm_inc);
127         high = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_SIZE_HI + i * hdm_inc);
128         size = (low & 0xf0000000) | ((uint64_t)high << 32);
129         if (addr < base || addr >= base + size) {
130             continue;
131         }
132 
133         ctrl = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_CTRL + i * hdm_inc);
134         if (!FIELD_EX32(ctrl, CXL_HDM_DECODER0_CTRL, COMMITTED)) {
135             return false;
136         }
137         found = true;
138         ig_enc = FIELD_EX32(ctrl, CXL_HDM_DECODER0_CTRL, IG);
139         iw_enc = FIELD_EX32(ctrl, CXL_HDM_DECODER0_CTRL, IW);
140         target_idx = (addr / cxl_decode_ig(ig_enc)) % (1 << iw_enc);
141 
142         if (target_idx < 4) {
143             uint32_t val = ldl_le_p(cache_mem +
144                                     R_CXL_HDM_DECODER0_TARGET_LIST_LO +
145                                     i * hdm_inc);
146             *target = extract32(val, target_idx * 8, 8);
147         } else {
148             uint32_t val = ldl_le_p(cache_mem +
149                                     R_CXL_HDM_DECODER0_TARGET_LIST_HI +
150                                     i * hdm_inc);
151             *target = extract32(val, (target_idx - 4) * 8, 8);
152         }
153         break;
154     }
155 
156     return found;
157 }
158 
cxl_cfmws_find_device(CXLFixedWindow * fw,hwaddr addr)159 static PCIDevice *cxl_cfmws_find_device(CXLFixedWindow *fw, hwaddr addr)
160 {
161     CXLComponentState *hb_cstate, *usp_cstate;
162     PCIHostState *hb;
163     CXLUpstreamPort *usp;
164     int rb_index;
165     uint32_t *cache_mem;
166     uint8_t target;
167     bool target_found;
168     PCIDevice *rp, *d;
169 
170     /* Address is relative to memory region. Convert to HPA */
171     addr += fw->base;
172 
173     rb_index = (addr / cxl_decode_ig(fw->enc_int_gran)) % fw->num_targets;
174     hb = PCI_HOST_BRIDGE(fw->target_hbs[rb_index]->cxl_host_bridge);
175     if (!hb || !hb->bus || !pci_bus_is_cxl(hb->bus)) {
176         return NULL;
177     }
178 
179     if (cxl_get_hb_passthrough(hb)) {
180         rp = pcie_find_port_first(hb->bus);
181         if (!rp) {
182             return NULL;
183         }
184     } else {
185         hb_cstate = cxl_get_hb_cstate(hb);
186         if (!hb_cstate) {
187             return NULL;
188         }
189 
190         cache_mem = hb_cstate->crb.cache_mem_registers;
191 
192         target_found = cxl_hdm_find_target(cache_mem, addr, &target);
193         if (!target_found) {
194             return NULL;
195         }
196 
197         rp = pcie_find_port_by_pn(hb->bus, target);
198         if (!rp) {
199             return NULL;
200         }
201     }
202 
203     d = pci_bridge_get_sec_bus(PCI_BRIDGE(rp))->devices[0];
204     if (!d) {
205         return NULL;
206     }
207 
208     if (object_dynamic_cast(OBJECT(d), TYPE_CXL_TYPE3)) {
209         return d;
210     }
211 
212     /*
213      * Could also be a switch.  Note only one level of switching currently
214      * supported.
215      */
216     if (!object_dynamic_cast(OBJECT(d), TYPE_CXL_USP)) {
217         return NULL;
218     }
219     usp = CXL_USP(d);
220 
221     usp_cstate = cxl_usp_to_cstate(usp);
222     if (!usp_cstate) {
223         return NULL;
224     }
225 
226     cache_mem = usp_cstate->crb.cache_mem_registers;
227 
228     target_found = cxl_hdm_find_target(cache_mem, addr, &target);
229     if (!target_found) {
230         return NULL;
231     }
232 
233     d = pcie_find_port_by_pn(&PCI_BRIDGE(d)->sec_bus, target);
234     if (!d) {
235         return NULL;
236     }
237 
238     d = pci_bridge_get_sec_bus(PCI_BRIDGE(d))->devices[0];
239     if (!d) {
240         return NULL;
241     }
242 
243     if (!object_dynamic_cast(OBJECT(d), TYPE_CXL_TYPE3)) {
244         return NULL;
245     }
246 
247     return d;
248 }
249 
cxl_read_cfmws(void * opaque,hwaddr addr,uint64_t * data,unsigned size,MemTxAttrs attrs)250 static MemTxResult cxl_read_cfmws(void *opaque, hwaddr addr, uint64_t *data,
251                                   unsigned size, MemTxAttrs attrs)
252 {
253     CXLFixedWindow *fw = opaque;
254     PCIDevice *d;
255 
256     d = cxl_cfmws_find_device(fw, addr);
257     if (d == NULL) {
258         *data = 0;
259         /* Reads to invalid address return poison */
260         return MEMTX_ERROR;
261     }
262 
263     return cxl_type3_read(d, addr + fw->base, data, size, attrs);
264 }
265 
cxl_write_cfmws(void * opaque,hwaddr addr,uint64_t data,unsigned size,MemTxAttrs attrs)266 static MemTxResult cxl_write_cfmws(void *opaque, hwaddr addr,
267                                    uint64_t data, unsigned size,
268                                    MemTxAttrs attrs)
269 {
270     CXLFixedWindow *fw = opaque;
271     PCIDevice *d;
272 
273     d = cxl_cfmws_find_device(fw, addr);
274     if (d == NULL) {
275         /* Writes to invalid address are silent */
276         return MEMTX_OK;
277     }
278 
279     return cxl_type3_write(d, addr + fw->base, data, size, attrs);
280 }
281 
282 const MemoryRegionOps cfmws_ops = {
283     .read_with_attrs = cxl_read_cfmws,
284     .write_with_attrs = cxl_write_cfmws,
285     .endianness = DEVICE_LITTLE_ENDIAN,
286     .valid = {
287         .min_access_size = 1,
288         .max_access_size = 8,
289         .unaligned = true,
290     },
291     .impl = {
292         .min_access_size = 1,
293         .max_access_size = 8,
294         .unaligned = true,
295     },
296 };
297 
machine_get_cxl(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)298 static void machine_get_cxl(Object *obj, Visitor *v, const char *name,
299                             void *opaque, Error **errp)
300 {
301     CXLState *cxl_state = opaque;
302     bool value = cxl_state->is_enabled;
303 
304     visit_type_bool(v, name, &value, errp);
305 }
306 
machine_set_cxl(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)307 static void machine_set_cxl(Object *obj, Visitor *v, const char *name,
308                             void *opaque, Error **errp)
309 {
310     CXLState *cxl_state = opaque;
311     bool value;
312 
313     if (!visit_type_bool(v, name, &value, errp)) {
314         return;
315     }
316     cxl_state->is_enabled = value;
317 }
318 
machine_get_cfmw(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)319 static void machine_get_cfmw(Object *obj, Visitor *v, const char *name,
320                              void *opaque, Error **errp)
321 {
322     CXLState *state = opaque;
323     CXLFixedMemoryWindowOptionsList **list = &state->cfmw_list;
324 
325     visit_type_CXLFixedMemoryWindowOptionsList(v, name, list, errp);
326 }
327 
machine_set_cfmw(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)328 static void machine_set_cfmw(Object *obj, Visitor *v, const char *name,
329                              void *opaque, Error **errp)
330 {
331     CXLState *state = opaque;
332     CXLFixedMemoryWindowOptionsList *cfmw_list = NULL;
333     CXLFixedMemoryWindowOptionsList *it;
334     int index;
335 
336     visit_type_CXLFixedMemoryWindowOptionsList(v, name, &cfmw_list, errp);
337     if (!cfmw_list) {
338         return;
339     }
340 
341     for (it = cfmw_list, index = 0; it; it = it->next, index++) {
342         cxl_fixed_memory_window_config(it->value, index, errp);
343     }
344     state->cfmw_list = cfmw_list;
345 }
346 
cxl_machine_init(Object * obj,CXLState * state)347 void cxl_machine_init(Object *obj, CXLState *state)
348 {
349     object_property_add(obj, "cxl", "bool", machine_get_cxl,
350                         machine_set_cxl, NULL, state);
351     object_property_set_description(obj, "cxl",
352                                     "Set on/off to enable/disable "
353                                     "CXL instantiation");
354 
355     object_property_add(obj, "cxl-fmw", "CXLFixedMemoryWindow",
356                         machine_get_cfmw, machine_set_cfmw,
357                         NULL, state);
358     object_property_set_description(obj, "cxl-fmw",
359                                     "CXL Fixed Memory Windows (array)");
360 }
361 
cxl_hook_up_pxb_registers(PCIBus * bus,CXLState * state,Error ** errp)362 void cxl_hook_up_pxb_registers(PCIBus *bus, CXLState *state, Error **errp)
363 {
364     /* Walk the pci busses looking for pxb busses to hook up */
365     if (bus) {
366         QLIST_FOREACH(bus, &bus->child, sibling) {
367             if (!pci_bus_is_root(bus)) {
368                 continue;
369             }
370             if (pci_bus_is_cxl(bus)) {
371                 if (!state->is_enabled) {
372                     error_setg(errp, "CXL host bridges present, but cxl=off");
373                     return;
374                 }
375                 pxb_cxl_hook_up_registers(state, bus, errp);
376             }
377         }
378     }
379 }
380 
cxl_fmws_find(Object * obj,void * opaque)381 static int cxl_fmws_find(Object *obj, void *opaque)
382 {
383     GSList **list = opaque;
384 
385     if (!object_dynamic_cast(obj, TYPE_CXL_FMW)) {
386         return 0;
387     }
388     *list = g_slist_prepend(*list, obj);
389 
390     return 0;
391 }
392 
cxl_fmws_get_all(void)393 static GSList *cxl_fmws_get_all(void)
394 {
395     GSList *list = NULL;
396 
397     object_child_foreach_recursive(object_get_root(), cxl_fmws_find, &list);
398 
399     return list;
400 }
401 
cfmws_cmp(gconstpointer a,gconstpointer b,gpointer d)402 static gint cfmws_cmp(gconstpointer a, gconstpointer b, gpointer d)
403 {
404     const struct CXLFixedWindow *ap = a;
405     const struct CXLFixedWindow *bp = b;
406 
407     return ap->index > bp->index;
408 }
409 
cxl_fmws_get_all_sorted(void)410 GSList *cxl_fmws_get_all_sorted(void)
411 {
412     return g_slist_sort_with_data(cxl_fmws_get_all(), cfmws_cmp, NULL);
413 }
414 
cxl_fmws_mmio_map(Object * obj,void * opaque)415 static int cxl_fmws_mmio_map(Object *obj, void *opaque)
416 {
417     struct CXLFixedWindow *fw;
418 
419     if (!object_dynamic_cast(obj, TYPE_CXL_FMW)) {
420         return 0;
421     }
422     fw = CXL_FMW(obj);
423     sysbus_mmio_map(SYS_BUS_DEVICE(fw), 0, fw->base);
424 
425     return 0;
426 }
427 
cxl_fmws_update_mmio(void)428 void cxl_fmws_update_mmio(void)
429 {
430     /* Ordering is not required for this */
431     object_child_foreach_recursive(object_get_root(), cxl_fmws_mmio_map, NULL);
432 }
433 
cxl_fmws_set_memmap(hwaddr base,hwaddr max_addr)434 hwaddr cxl_fmws_set_memmap(hwaddr base, hwaddr max_addr)
435 {
436     GSList *cfmws_list, *iter;
437     CXLFixedWindow *fw;
438 
439     cfmws_list = cxl_fmws_get_all_sorted();
440     for (iter = cfmws_list; iter; iter = iter->next) {
441         fw = CXL_FMW(iter->data);
442         if (base + fw->size <= max_addr) {
443             fw->base = base;
444             base += fw->size;
445         }
446     }
447     g_slist_free(cfmws_list);
448 
449     return base;
450 }
451 
cxl_fmw_realize(DeviceState * dev,Error ** errp)452 static void cxl_fmw_realize(DeviceState *dev, Error **errp)
453 {
454     CXLFixedWindow *fw = CXL_FMW(dev);
455 
456     memory_region_init_io(&fw->mr, OBJECT(dev), &cfmws_ops, fw,
457                           "cxl-fixed-memory-region", fw->size);
458     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &fw->mr);
459 }
460 
461 /*
462  * Note: Fixed memory windows represent fixed address decoders on the host and
463  * as such have no dynamic state to reset or migrate
464  */
cxl_fmw_class_init(ObjectClass * klass,const void * data)465 static void cxl_fmw_class_init(ObjectClass *klass, const void *data)
466 {
467     DeviceClass *dc = DEVICE_CLASS(klass);
468 
469     dc->desc = "CXL Fixed Memory Window";
470     dc->realize = cxl_fmw_realize;
471     /* Reason - created by machines as tightly coupled to machine memory map */
472     dc->user_creatable = false;
473 }
474 
475 static const TypeInfo cxl_fmw_info = {
476     .name = TYPE_CXL_FMW,
477     .parent = TYPE_SYS_BUS_DEVICE,
478     .instance_size = sizeof(CXLFixedWindow),
479     .class_init = cxl_fmw_class_init,
480 };
481 
cxl_host_register_types(void)482 static void cxl_host_register_types(void)
483 {
484     type_register_static(&cxl_fmw_info);
485 }
486 type_init(cxl_host_register_types)
487