xref: /qemu/hw/misc/mips_itu.c (revision 2abf0da2)
1 /*
2  * Inter-Thread Communication Unit emulation.
3  *
4  * Copyright (c) 2016 Imagination Technologies
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "qemu/osdep.h"
21 #include "qemu/units.h"
22 #include "qemu/log.h"
23 #include "qemu/module.h"
24 #include "qapi/error.h"
25 #include "hw/core/cpu.h"
26 #include "hw/misc/mips_itu.h"
27 #include "hw/qdev-properties.h"
28 #include "target/mips/cpu.h"
29 
30 #define ITC_TAG_ADDRSPACE_SZ (ITC_ADDRESSMAP_NUM * 8)
31 /* Initialize as 4kB area to fit all 32 cells with default 128B grain.
32    Storage may be resized by the software. */
33 #define ITC_STORAGE_ADDRSPACE_SZ 0x1000
34 
35 #define ITC_FIFO_NUM_MAX 16
36 #define ITC_SEMAPH_NUM_MAX 16
37 #define ITC_AM1_NUMENTRIES_OFS 20
38 
39 #define ITC_CELL_PV_MAX_VAL 0xFFFF
40 
41 #define ITC_CELL_TAG_FIFO_DEPTH 28
42 #define ITC_CELL_TAG_FIFO_PTR 18
43 #define ITC_CELL_TAG_FIFO 17
44 #define ITC_CELL_TAG_T 16
45 #define ITC_CELL_TAG_F 1
46 #define ITC_CELL_TAG_E 0
47 
48 #define ITC_AM0_BASE_ADDRESS_MASK 0xFFFFFC00ULL
49 #define ITC_AM0_EN_MASK 0x1
50 
51 #define ITC_AM1_ADDR_MASK_MASK 0x1FC00
52 #define ITC_AM1_ENTRY_GRAIN_MASK 0x7
53 
54 typedef enum ITCView {
55     ITCVIEW_BYPASS  = 0,
56     ITCVIEW_CONTROL = 1,
57     ITCVIEW_EF_SYNC = 2,
58     ITCVIEW_EF_TRY  = 3,
59     ITCVIEW_PV_SYNC = 4,
60     ITCVIEW_PV_TRY  = 5,
61     ITCVIEW_PV_ICR0 = 15,
62 } ITCView;
63 
64 #define ITC_ICR0_CELL_NUM        16
65 #define ITC_ICR0_BLK_GRAIN       8
66 #define ITC_ICR0_BLK_GRAIN_MASK  0x7
67 #define ITC_ICR0_ERR_AXI         2
68 #define ITC_ICR0_ERR_PARITY      1
69 #define ITC_ICR0_ERR_EXEC        0
70 
71 MemoryRegion *mips_itu_get_tag_region(MIPSITUState *itu)
72 {
73     return &itu->tag_io;
74 }
75 
76 static uint64_t itc_tag_read(void *opaque, hwaddr addr, unsigned size)
77 {
78     MIPSITUState *tag = (MIPSITUState *)opaque;
79     uint64_t index = addr >> 3;
80 
81     if (index >= ITC_ADDRESSMAP_NUM) {
82         qemu_log_mask(LOG_GUEST_ERROR, "Read 0x%" PRIx64 "\n", addr);
83         return 0;
84     }
85 
86     return tag->ITCAddressMap[index];
87 }
88 
89 void itc_reconfigure(MIPSITUState *tag)
90 {
91     uint64_t *am = &tag->ITCAddressMap[0];
92     MemoryRegion *mr = &tag->storage_io;
93     hwaddr address = am[0] & ITC_AM0_BASE_ADDRESS_MASK;
94     uint64_t size = (1 * KiB) + (am[1] & ITC_AM1_ADDR_MASK_MASK);
95     bool is_enabled = (am[0] & ITC_AM0_EN_MASK) != 0;
96 
97     if (tag->saar) {
98         address = (tag->saar[0] & 0xFFFFFFFFE000ULL) << 4;
99         size = 1ULL << ((tag->saar[0] >> 1) & 0x1f);
100         is_enabled = tag->saar[0] & 1;
101     }
102 
103     memory_region_transaction_begin();
104     if (!(size & (size - 1))) {
105         memory_region_set_size(mr, size);
106     }
107     memory_region_set_address(mr, address);
108     memory_region_set_enabled(mr, is_enabled);
109     memory_region_transaction_commit();
110 }
111 
112 static void itc_tag_write(void *opaque, hwaddr addr,
113                           uint64_t data, unsigned size)
114 {
115     MIPSITUState *tag = (MIPSITUState *)opaque;
116     uint64_t *am = &tag->ITCAddressMap[0];
117     uint64_t am_old, mask;
118     uint64_t index = addr >> 3;
119 
120     switch (index) {
121     case 0:
122         mask = ITC_AM0_BASE_ADDRESS_MASK | ITC_AM0_EN_MASK;
123         break;
124     case 1:
125         mask = ITC_AM1_ADDR_MASK_MASK | ITC_AM1_ENTRY_GRAIN_MASK;
126         break;
127     default:
128         qemu_log_mask(LOG_GUEST_ERROR, "Bad write 0x%" PRIx64 "\n", addr);
129         return;
130     }
131 
132     am_old = am[index];
133     am[index] = (data & mask) | (am_old & ~mask);
134     if (am_old != am[index]) {
135         itc_reconfigure(tag);
136     }
137 }
138 
139 static const MemoryRegionOps itc_tag_ops = {
140     .read = itc_tag_read,
141     .write = itc_tag_write,
142     .impl = {
143         .max_access_size = 8,
144     },
145     .endianness = DEVICE_NATIVE_ENDIAN,
146 };
147 
148 static inline uint32_t get_num_cells(MIPSITUState *s)
149 {
150     return s->num_fifo + s->num_semaphores;
151 }
152 
153 static inline ITCView get_itc_view(hwaddr addr)
154 {
155     return (addr >> 3) & 0xf;
156 }
157 
158 static inline int get_cell_stride_shift(const MIPSITUState *s)
159 {
160     /* Minimum interval (for EntryGain = 0) is 128 B */
161     if (s->saar) {
162         return 7 + ((s->icr0 >> ITC_ICR0_BLK_GRAIN) &
163                     ITC_ICR0_BLK_GRAIN_MASK);
164     } else {
165         return 7 + (s->ITCAddressMap[1] & ITC_AM1_ENTRY_GRAIN_MASK);
166     }
167 }
168 
169 static inline ITCStorageCell *get_cell(MIPSITUState *s,
170                                        hwaddr addr)
171 {
172     uint32_t cell_idx = addr >> get_cell_stride_shift(s);
173     uint32_t num_cells = get_num_cells(s);
174 
175     if (cell_idx >= num_cells) {
176         cell_idx = num_cells - 1;
177     }
178 
179     return &s->cell[cell_idx];
180 }
181 
182 static void wake_blocked_threads(ITCStorageCell *c)
183 {
184     CPUState *cs;
185     CPU_FOREACH(cs) {
186         if (cs->halted && (c->blocked_threads & (1ULL << cs->cpu_index))) {
187             cpu_interrupt(cs, CPU_INTERRUPT_WAKE);
188         }
189     }
190     c->blocked_threads = 0;
191 }
192 
193 static G_NORETURN
194 void block_thread_and_exit(ITCStorageCell *c)
195 {
196     c->blocked_threads |= 1ULL << current_cpu->cpu_index;
197     current_cpu->halted = 1;
198     current_cpu->exception_index = EXCP_HLT;
199     cpu_loop_exit_restore(current_cpu, current_cpu->mem_io_pc);
200 }
201 
202 /* ITC Bypass View */
203 
204 static inline uint64_t view_bypass_read(ITCStorageCell *c)
205 {
206     if (c->tag.FIFO) {
207         return c->data[c->fifo_out];
208     } else {
209         return c->data[0];
210     }
211 }
212 
213 static inline void view_bypass_write(ITCStorageCell *c, uint64_t val)
214 {
215     if (c->tag.FIFO && (c->tag.FIFOPtr > 0)) {
216         int idx = (c->fifo_out + c->tag.FIFOPtr - 1) % ITC_CELL_DEPTH;
217         c->data[idx] = val;
218     }
219 
220     /* ignore a write to the semaphore cell */
221 }
222 
223 /* ITC Control View */
224 
225 static inline uint64_t view_control_read(ITCStorageCell *c)
226 {
227     return ((uint64_t)c->tag.FIFODepth << ITC_CELL_TAG_FIFO_DEPTH) |
228            (c->tag.FIFOPtr << ITC_CELL_TAG_FIFO_PTR) |
229            (c->tag.FIFO << ITC_CELL_TAG_FIFO) |
230            (c->tag.T << ITC_CELL_TAG_T) |
231            (c->tag.E << ITC_CELL_TAG_E) |
232            (c->tag.F << ITC_CELL_TAG_F);
233 }
234 
235 static inline void view_control_write(ITCStorageCell *c, uint64_t val)
236 {
237     c->tag.T = (val >> ITC_CELL_TAG_T) & 1;
238     c->tag.E = (val >> ITC_CELL_TAG_E) & 1;
239     c->tag.F = (val >> ITC_CELL_TAG_F) & 1;
240 
241     if (c->tag.E) {
242         c->tag.FIFOPtr = 0;
243     }
244 }
245 
246 /* ITC Empty/Full View */
247 
248 static uint64_t view_ef_common_read(ITCStorageCell *c, bool blocking)
249 {
250     uint64_t ret = 0;
251 
252     if (!c->tag.FIFO) {
253         return 0;
254     }
255 
256     c->tag.F = 0;
257 
258     if (blocking && c->tag.E) {
259         block_thread_and_exit(c);
260     }
261 
262     if (c->blocked_threads) {
263         wake_blocked_threads(c);
264     }
265 
266     if (c->tag.FIFOPtr > 0) {
267         ret = c->data[c->fifo_out];
268         c->fifo_out = (c->fifo_out + 1) % ITC_CELL_DEPTH;
269         c->tag.FIFOPtr--;
270     }
271 
272     if (c->tag.FIFOPtr == 0) {
273         c->tag.E = 1;
274     }
275 
276     return ret;
277 }
278 
279 static uint64_t view_ef_sync_read(ITCStorageCell *c)
280 {
281     return view_ef_common_read(c, true);
282 }
283 
284 static uint64_t view_ef_try_read(ITCStorageCell *c)
285 {
286     return view_ef_common_read(c, false);
287 }
288 
289 static inline void view_ef_common_write(ITCStorageCell *c, uint64_t val,
290                                         bool blocking)
291 {
292     if (!c->tag.FIFO) {
293         return;
294     }
295 
296     c->tag.E = 0;
297 
298     if (blocking && c->tag.F) {
299         block_thread_and_exit(c);
300     }
301 
302     if (c->blocked_threads) {
303         wake_blocked_threads(c);
304     }
305 
306     if (c->tag.FIFOPtr < ITC_CELL_DEPTH) {
307         int idx = (c->fifo_out + c->tag.FIFOPtr) % ITC_CELL_DEPTH;
308         c->data[idx] = val;
309         c->tag.FIFOPtr++;
310     }
311 
312     if (c->tag.FIFOPtr == ITC_CELL_DEPTH) {
313         c->tag.F = 1;
314     }
315 }
316 
317 static void view_ef_sync_write(ITCStorageCell *c, uint64_t val)
318 {
319     view_ef_common_write(c, val, true);
320 }
321 
322 static void view_ef_try_write(ITCStorageCell *c, uint64_t val)
323 {
324     view_ef_common_write(c, val, false);
325 }
326 
327 /* ITC P/V View */
328 
329 static uint64_t view_pv_common_read(ITCStorageCell *c, bool blocking)
330 {
331     uint64_t ret = c->data[0];
332 
333     if (c->tag.FIFO) {
334         return 0;
335     }
336 
337     if (c->data[0] > 0) {
338         c->data[0]--;
339     } else if (blocking) {
340         block_thread_and_exit(c);
341     }
342 
343     return ret;
344 }
345 
346 static uint64_t view_pv_sync_read(ITCStorageCell *c)
347 {
348     return view_pv_common_read(c, true);
349 }
350 
351 static uint64_t view_pv_try_read(ITCStorageCell *c)
352 {
353     return view_pv_common_read(c, false);
354 }
355 
356 static inline void view_pv_common_write(ITCStorageCell *c)
357 {
358     if (c->tag.FIFO) {
359         return;
360     }
361 
362     if (c->data[0] < ITC_CELL_PV_MAX_VAL) {
363         c->data[0]++;
364     }
365 
366     if (c->blocked_threads) {
367         wake_blocked_threads(c);
368     }
369 }
370 
371 static void view_pv_sync_write(ITCStorageCell *c)
372 {
373     view_pv_common_write(c);
374 }
375 
376 static void view_pv_try_write(ITCStorageCell *c)
377 {
378     view_pv_common_write(c);
379 }
380 
381 static void raise_exception(int excp)
382 {
383     current_cpu->exception_index = excp;
384     cpu_loop_exit(current_cpu);
385 }
386 
387 static uint64_t itc_storage_read(void *opaque, hwaddr addr, unsigned size)
388 {
389     MIPSITUState *s = (MIPSITUState *)opaque;
390     ITCStorageCell *cell = get_cell(s, addr);
391     ITCView view = get_itc_view(addr);
392     uint64_t ret = -1;
393 
394     switch (size) {
395     case 1:
396     case 2:
397         s->icr0 |= 1 << ITC_ICR0_ERR_AXI;
398         raise_exception(EXCP_DBE);
399         return 0;
400     }
401 
402     switch (view) {
403     case ITCVIEW_BYPASS:
404         ret = view_bypass_read(cell);
405         break;
406     case ITCVIEW_CONTROL:
407         ret = view_control_read(cell);
408         break;
409     case ITCVIEW_EF_SYNC:
410         ret = view_ef_sync_read(cell);
411         break;
412     case ITCVIEW_EF_TRY:
413         ret = view_ef_try_read(cell);
414         break;
415     case ITCVIEW_PV_SYNC:
416         ret = view_pv_sync_read(cell);
417         break;
418     case ITCVIEW_PV_TRY:
419         ret = view_pv_try_read(cell);
420         break;
421     case ITCVIEW_PV_ICR0:
422         ret = s->icr0;
423         break;
424     default:
425         qemu_log_mask(LOG_GUEST_ERROR,
426                       "itc_storage_read: Bad ITC View %d\n", (int)view);
427         break;
428     }
429 
430     return ret;
431 }
432 
433 static void itc_storage_write(void *opaque, hwaddr addr, uint64_t data,
434                               unsigned size)
435 {
436     MIPSITUState *s = (MIPSITUState *)opaque;
437     ITCStorageCell *cell = get_cell(s, addr);
438     ITCView view = get_itc_view(addr);
439 
440     switch (size) {
441     case 1:
442     case 2:
443         s->icr0 |= 1 << ITC_ICR0_ERR_AXI;
444         raise_exception(EXCP_DBE);
445         return;
446     }
447 
448     switch (view) {
449     case ITCVIEW_BYPASS:
450         view_bypass_write(cell, data);
451         break;
452     case ITCVIEW_CONTROL:
453         view_control_write(cell, data);
454         break;
455     case ITCVIEW_EF_SYNC:
456         view_ef_sync_write(cell, data);
457         break;
458     case ITCVIEW_EF_TRY:
459         view_ef_try_write(cell, data);
460         break;
461     case ITCVIEW_PV_SYNC:
462         view_pv_sync_write(cell);
463         break;
464     case ITCVIEW_PV_TRY:
465         view_pv_try_write(cell);
466         break;
467     case ITCVIEW_PV_ICR0:
468         if (data & 0x7) {
469             /* clear ERROR bits */
470             s->icr0 &= ~(data & 0x7);
471         }
472         /* set BLK_GRAIN */
473         s->icr0 &= ~0x700;
474         s->icr0 |= data & 0x700;
475         break;
476     default:
477         qemu_log_mask(LOG_GUEST_ERROR,
478                       "itc_storage_write: Bad ITC View %d\n", (int)view);
479         break;
480     }
481 
482 }
483 
484 static const MemoryRegionOps itc_storage_ops = {
485     .read = itc_storage_read,
486     .write = itc_storage_write,
487     .endianness = DEVICE_NATIVE_ENDIAN,
488 };
489 
490 static void itc_reset_cells(MIPSITUState *s)
491 {
492     int i;
493 
494     memset(s->cell, 0, get_num_cells(s) * sizeof(s->cell[0]));
495 
496     for (i = 0; i < s->num_fifo; i++) {
497         s->cell[i].tag.E = 1;
498         s->cell[i].tag.FIFO = 1;
499         s->cell[i].tag.FIFODepth = ITC_CELL_DEPTH_SHIFT;
500     }
501 }
502 
503 static void mips_itu_init(Object *obj)
504 {
505     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
506     MIPSITUState *s = MIPS_ITU(obj);
507 
508     memory_region_init_io(&s->storage_io, OBJECT(s), &itc_storage_ops, s,
509                           "mips-itc-storage", ITC_STORAGE_ADDRSPACE_SZ);
510     sysbus_init_mmio(sbd, &s->storage_io);
511 
512     memory_region_init_io(&s->tag_io, OBJECT(s), &itc_tag_ops, s,
513                           "mips-itc-tag", ITC_TAG_ADDRSPACE_SZ);
514 }
515 
516 static void mips_itu_realize(DeviceState *dev, Error **errp)
517 {
518     MIPSITUState *s = MIPS_ITU(dev);
519     CPUMIPSState *env;
520 
521     if (s->num_fifo > ITC_FIFO_NUM_MAX) {
522         error_setg(errp, "Exceed maximum number of FIFO cells: %d",
523                    s->num_fifo);
524         return;
525     }
526     if (s->num_semaphores > ITC_SEMAPH_NUM_MAX) {
527         error_setg(errp, "Exceed maximum number of Semaphore cells: %d",
528                    s->num_semaphores);
529         return;
530     }
531     if (!s->cpu0) {
532         error_setg(errp, "Missing 'cpu[0]' property");
533         return;
534     }
535 
536     env = &MIPS_CPU(s->cpu0)->env;
537     if (env->saarp) {
538         s->saar = env->CP0_SAAR;
539     }
540 
541     s->cell = g_new(ITCStorageCell, get_num_cells(s));
542 }
543 
544 static void mips_itu_reset(DeviceState *dev)
545 {
546     MIPSITUState *s = MIPS_ITU(dev);
547 
548     if (s->saar) {
549         s->saar[0] = 0x11 << 1;
550         s->icr0 = get_num_cells(s) << ITC_ICR0_CELL_NUM;
551     } else {
552         s->ITCAddressMap[0] = 0;
553         s->ITCAddressMap[1] =
554             ((ITC_STORAGE_ADDRSPACE_SZ - 1) & ITC_AM1_ADDR_MASK_MASK) |
555             (get_num_cells(s) << ITC_AM1_NUMENTRIES_OFS);
556     }
557     itc_reconfigure(s);
558 
559     itc_reset_cells(s);
560 }
561 
562 static Property mips_itu_properties[] = {
563     DEFINE_PROP_UINT32("num-fifo", MIPSITUState, num_fifo,
564                       ITC_FIFO_NUM_MAX),
565     DEFINE_PROP_UINT32("num-semaphores", MIPSITUState, num_semaphores,
566                       ITC_SEMAPH_NUM_MAX),
567     DEFINE_PROP_LINK("cpu[0]", MIPSITUState, cpu0, TYPE_MIPS_CPU, ArchCPU *),
568     DEFINE_PROP_END_OF_LIST(),
569 };
570 
571 static void mips_itu_class_init(ObjectClass *klass, void *data)
572 {
573     DeviceClass *dc = DEVICE_CLASS(klass);
574 
575     device_class_set_props(dc, mips_itu_properties);
576     dc->realize = mips_itu_realize;
577     dc->reset = mips_itu_reset;
578 }
579 
580 static const TypeInfo mips_itu_info = {
581     .name          = TYPE_MIPS_ITU,
582     .parent        = TYPE_SYS_BUS_DEVICE,
583     .instance_size = sizeof(MIPSITUState),
584     .instance_init = mips_itu_init,
585     .class_init    = mips_itu_class_init,
586 };
587 
588 static void mips_itu_register_types(void)
589 {
590     type_register_static(&mips_itu_info);
591 }
592 
593 type_init(mips_itu_register_types)
594