xref: /openbmc/qemu/hw/intc/arm_gicv3_its.c (revision 1b08e436d0deaece35f7fa21aba6e6afe26cb3ac)
1 /*
2  * ITS emulation for a GICv3-based system
3  *
4  * Copyright Linaro.org 2021
5  *
6  * Authors:
7  *  Shashi Mallela <shashi.mallela@linaro.org>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2 or (at your
10  * option) any later version.  See the COPYING file in the top-level directory.
11  *
12  */
13 
14 #include "qemu/osdep.h"
15 #include "qemu/log.h"
16 #include "hw/qdev-properties.h"
17 #include "hw/intc/arm_gicv3_its_common.h"
18 #include "gicv3_internal.h"
19 #include "qom/object.h"
20 #include "qapi/error.h"
21 
22 typedef struct GICv3ITSClass GICv3ITSClass;
23 /* This is reusing the GICv3ITSState typedef from ARM_GICV3_ITS_COMMON */
24 DECLARE_OBJ_CHECKERS(GICv3ITSState, GICv3ITSClass,
25                      ARM_GICV3_ITS, TYPE_ARM_GICV3_ITS)
26 
27 struct GICv3ITSClass {
28     GICv3ITSCommonClass parent_class;
29     void (*parent_reset)(DeviceState *dev);
30 };
31 
32 static uint64_t baser_base_addr(uint64_t value, uint32_t page_sz)
33 {
34     uint64_t result = 0;
35 
36     switch (page_sz) {
37     case GITS_PAGE_SIZE_4K:
38     case GITS_PAGE_SIZE_16K:
39         result = FIELD_EX64(value, GITS_BASER, PHYADDR) << 12;
40         break;
41 
42     case GITS_PAGE_SIZE_64K:
43         result = FIELD_EX64(value, GITS_BASER, PHYADDRL_64K) << 16;
44         result |= FIELD_EX64(value, GITS_BASER, PHYADDRH_64K) << 48;
45         break;
46 
47     default:
48         break;
49     }
50     return result;
51 }
52 
53 /*
54  * This function extracts the ITS Device and Collection table specific
55  * parameters (like base_addr, size etc) from GITS_BASER register.
56  * It is called during ITS enable and also during post_load migration
57  */
58 static void extract_table_params(GICv3ITSState *s)
59 {
60     uint16_t num_pages = 0;
61     uint8_t  page_sz_type;
62     uint8_t type;
63     uint32_t page_sz = 0;
64     uint64_t value;
65 
66     for (int i = 0; i < 8; i++) {
67         value = s->baser[i];
68 
69         if (!value) {
70             continue;
71         }
72 
73         page_sz_type = FIELD_EX64(value, GITS_BASER, PAGESIZE);
74 
75         switch (page_sz_type) {
76         case 0:
77             page_sz = GITS_PAGE_SIZE_4K;
78             break;
79 
80         case 1:
81             page_sz = GITS_PAGE_SIZE_16K;
82             break;
83 
84         case 2:
85         case 3:
86             page_sz = GITS_PAGE_SIZE_64K;
87             break;
88 
89         default:
90             g_assert_not_reached();
91         }
92 
93         num_pages = FIELD_EX64(value, GITS_BASER, SIZE) + 1;
94 
95         type = FIELD_EX64(value, GITS_BASER, TYPE);
96 
97         switch (type) {
98 
99         case GITS_BASER_TYPE_DEVICE:
100             memset(&s->dt, 0 , sizeof(s->dt));
101             s->dt.valid = FIELD_EX64(value, GITS_BASER, VALID);
102 
103             if (!s->dt.valid) {
104                 return;
105             }
106 
107             s->dt.page_sz = page_sz;
108             s->dt.indirect = FIELD_EX64(value, GITS_BASER, INDIRECT);
109             s->dt.entry_sz = FIELD_EX64(value, GITS_BASER, ENTRYSIZE);
110 
111             if (!s->dt.indirect) {
112                 s->dt.max_entries = (num_pages * page_sz) / s->dt.entry_sz;
113             } else {
114                 s->dt.max_entries = (((num_pages * page_sz) /
115                                      L1TABLE_ENTRY_SIZE) *
116                                      (page_sz / s->dt.entry_sz));
117             }
118 
119             s->dt.maxids.max_devids = (1UL << (FIELD_EX64(s->typer, GITS_TYPER,
120                                        DEVBITS) + 1));
121 
122             s->dt.base_addr = baser_base_addr(value, page_sz);
123 
124             break;
125 
126         case GITS_BASER_TYPE_COLLECTION:
127             memset(&s->ct, 0 , sizeof(s->ct));
128             s->ct.valid = FIELD_EX64(value, GITS_BASER, VALID);
129 
130             /*
131              * GITS_TYPER.HCC is 0 for this implementation
132              * hence writes are discarded if ct.valid is 0
133              */
134             if (!s->ct.valid) {
135                 return;
136             }
137 
138             s->ct.page_sz = page_sz;
139             s->ct.indirect = FIELD_EX64(value, GITS_BASER, INDIRECT);
140             s->ct.entry_sz = FIELD_EX64(value, GITS_BASER, ENTRYSIZE);
141 
142             if (!s->ct.indirect) {
143                 s->ct.max_entries = (num_pages * page_sz) / s->ct.entry_sz;
144             } else {
145                 s->ct.max_entries = (((num_pages * page_sz) /
146                                      L1TABLE_ENTRY_SIZE) *
147                                      (page_sz / s->ct.entry_sz));
148             }
149 
150             if (FIELD_EX64(s->typer, GITS_TYPER, CIL)) {
151                 s->ct.maxids.max_collids = (1UL << (FIELD_EX64(s->typer,
152                                             GITS_TYPER, CIDBITS) + 1));
153             } else {
154                 /* 16-bit CollectionId supported when CIL == 0 */
155                 s->ct.maxids.max_collids = (1UL << 16);
156             }
157 
158             s->ct.base_addr = baser_base_addr(value, page_sz);
159 
160             break;
161 
162         default:
163             break;
164         }
165     }
166 }
167 
168 static void extract_cmdq_params(GICv3ITSState *s)
169 {
170     uint16_t num_pages = 0;
171     uint64_t value = s->cbaser;
172 
173     num_pages = FIELD_EX64(value, GITS_CBASER, SIZE) + 1;
174 
175     memset(&s->cq, 0 , sizeof(s->cq));
176     s->cq.valid = FIELD_EX64(value, GITS_CBASER, VALID);
177 
178     if (s->cq.valid) {
179         s->cq.max_entries = (num_pages * GITS_PAGE_SIZE_4K) /
180                              GITS_CMDQ_ENTRY_SIZE;
181         s->cq.base_addr = FIELD_EX64(value, GITS_CBASER, PHYADDR);
182         s->cq.base_addr <<= R_GITS_CBASER_PHYADDR_SHIFT;
183     }
184 }
185 
186 static MemTxResult gicv3_its_translation_write(void *opaque, hwaddr offset,
187                                                uint64_t data, unsigned size,
188                                                MemTxAttrs attrs)
189 {
190     return MEMTX_OK;
191 }
192 
193 static bool its_writel(GICv3ITSState *s, hwaddr offset,
194                               uint64_t value, MemTxAttrs attrs)
195 {
196     bool result = true;
197     int index;
198 
199     switch (offset) {
200     case GITS_CTLR:
201         s->ctlr |= (value & ~(s->ctlr));
202 
203         if (s->ctlr & ITS_CTLR_ENABLED) {
204             extract_table_params(s);
205             extract_cmdq_params(s);
206             s->creadr = 0;
207         }
208         break;
209     case GITS_CBASER:
210         /*
211          * IMPDEF choice:- GITS_CBASER register becomes RO if ITS is
212          *                 already enabled
213          */
214         if (!(s->ctlr & ITS_CTLR_ENABLED)) {
215             s->cbaser = deposit64(s->cbaser, 0, 32, value);
216             s->creadr = 0;
217             s->cwriter = s->creadr;
218         }
219         break;
220     case GITS_CBASER + 4:
221         /*
222          * IMPDEF choice:- GITS_CBASER register becomes RO if ITS is
223          *                 already enabled
224          */
225         if (!(s->ctlr & ITS_CTLR_ENABLED)) {
226             s->cbaser = deposit64(s->cbaser, 32, 32, value);
227             s->creadr = 0;
228             s->cwriter = s->creadr;
229         }
230         break;
231     case GITS_CWRITER:
232         s->cwriter = deposit64(s->cwriter, 0, 32,
233                                (value & ~R_GITS_CWRITER_RETRY_MASK));
234         break;
235     case GITS_CWRITER + 4:
236         s->cwriter = deposit64(s->cwriter, 32, 32, value);
237         break;
238     case GITS_CREADR:
239         if (s->gicv3->gicd_ctlr & GICD_CTLR_DS) {
240             s->creadr = deposit64(s->creadr, 0, 32,
241                                   (value & ~R_GITS_CREADR_STALLED_MASK));
242         } else {
243             /* RO register, ignore the write */
244             qemu_log_mask(LOG_GUEST_ERROR,
245                           "%s: invalid guest write to RO register at offset "
246                           TARGET_FMT_plx "\n", __func__, offset);
247         }
248         break;
249     case GITS_CREADR + 4:
250         if (s->gicv3->gicd_ctlr & GICD_CTLR_DS) {
251             s->creadr = deposit64(s->creadr, 32, 32, value);
252         } else {
253             /* RO register, ignore the write */
254             qemu_log_mask(LOG_GUEST_ERROR,
255                           "%s: invalid guest write to RO register at offset "
256                           TARGET_FMT_plx "\n", __func__, offset);
257         }
258         break;
259     case GITS_BASER ... GITS_BASER + 0x3f:
260         /*
261          * IMPDEF choice:- GITS_BASERn register becomes RO if ITS is
262          *                 already enabled
263          */
264         if (!(s->ctlr & ITS_CTLR_ENABLED)) {
265             index = (offset - GITS_BASER) / 8;
266 
267             if (offset & 7) {
268                 value <<= 32;
269                 value &= ~GITS_BASER_RO_MASK;
270                 s->baser[index] &= GITS_BASER_RO_MASK | MAKE_64BIT_MASK(0, 32);
271                 s->baser[index] |= value;
272             } else {
273                 value &= ~GITS_BASER_RO_MASK;
274                 s->baser[index] &= GITS_BASER_RO_MASK | MAKE_64BIT_MASK(32, 32);
275                 s->baser[index] |= value;
276             }
277         }
278         break;
279     case GITS_IIDR:
280     case GITS_IDREGS ... GITS_IDREGS + 0x2f:
281         /* RO registers, ignore the write */
282         qemu_log_mask(LOG_GUEST_ERROR,
283                       "%s: invalid guest write to RO register at offset "
284                       TARGET_FMT_plx "\n", __func__, offset);
285         break;
286     default:
287         result = false;
288         break;
289     }
290     return result;
291 }
292 
293 static bool its_readl(GICv3ITSState *s, hwaddr offset,
294                              uint64_t *data, MemTxAttrs attrs)
295 {
296     bool result = true;
297     int index;
298 
299     switch (offset) {
300     case GITS_CTLR:
301         *data = s->ctlr;
302         break;
303     case GITS_IIDR:
304         *data = gicv3_iidr();
305         break;
306     case GITS_IDREGS ... GITS_IDREGS + 0x2f:
307         /* ID registers */
308         *data = gicv3_idreg(offset - GITS_IDREGS);
309         break;
310     case GITS_TYPER:
311         *data = extract64(s->typer, 0, 32);
312         break;
313     case GITS_TYPER + 4:
314         *data = extract64(s->typer, 32, 32);
315         break;
316     case GITS_CBASER:
317         *data = extract64(s->cbaser, 0, 32);
318         break;
319     case GITS_CBASER + 4:
320         *data = extract64(s->cbaser, 32, 32);
321         break;
322     case GITS_CREADR:
323         *data = extract64(s->creadr, 0, 32);
324         break;
325     case GITS_CREADR + 4:
326         *data = extract64(s->creadr, 32, 32);
327         break;
328     case GITS_CWRITER:
329         *data = extract64(s->cwriter, 0, 32);
330         break;
331     case GITS_CWRITER + 4:
332         *data = extract64(s->cwriter, 32, 32);
333         break;
334     case GITS_BASER ... GITS_BASER + 0x3f:
335         index = (offset - GITS_BASER) / 8;
336         if (offset & 7) {
337             *data = extract64(s->baser[index], 32, 32);
338         } else {
339             *data = extract64(s->baser[index], 0, 32);
340         }
341         break;
342     default:
343         result = false;
344         break;
345     }
346     return result;
347 }
348 
349 static bool its_writell(GICv3ITSState *s, hwaddr offset,
350                                uint64_t value, MemTxAttrs attrs)
351 {
352     bool result = true;
353     int index;
354 
355     switch (offset) {
356     case GITS_BASER ... GITS_BASER + 0x3f:
357         /*
358          * IMPDEF choice:- GITS_BASERn register becomes RO if ITS is
359          *                 already enabled
360          */
361         if (!(s->ctlr & ITS_CTLR_ENABLED)) {
362             index = (offset - GITS_BASER) / 8;
363             s->baser[index] &= GITS_BASER_RO_MASK;
364             s->baser[index] |= (value & ~GITS_BASER_RO_MASK);
365         }
366         break;
367     case GITS_CBASER:
368         /*
369          * IMPDEF choice:- GITS_CBASER register becomes RO if ITS is
370          *                 already enabled
371          */
372         if (!(s->ctlr & ITS_CTLR_ENABLED)) {
373             s->cbaser = value;
374             s->creadr = 0;
375             s->cwriter = s->creadr;
376         }
377         break;
378     case GITS_CWRITER:
379         s->cwriter = value & ~R_GITS_CWRITER_RETRY_MASK;
380         break;
381     case GITS_CREADR:
382         if (s->gicv3->gicd_ctlr & GICD_CTLR_DS) {
383             s->creadr = value & ~R_GITS_CREADR_STALLED_MASK;
384         } else {
385             /* RO register, ignore the write */
386             qemu_log_mask(LOG_GUEST_ERROR,
387                           "%s: invalid guest write to RO register at offset "
388                           TARGET_FMT_plx "\n", __func__, offset);
389         }
390         break;
391     case GITS_TYPER:
392         /* RO registers, ignore the write */
393         qemu_log_mask(LOG_GUEST_ERROR,
394                       "%s: invalid guest write to RO register at offset "
395                       TARGET_FMT_plx "\n", __func__, offset);
396         break;
397     default:
398         result = false;
399         break;
400     }
401     return result;
402 }
403 
404 static bool its_readll(GICv3ITSState *s, hwaddr offset,
405                               uint64_t *data, MemTxAttrs attrs)
406 {
407     bool result = true;
408     int index;
409 
410     switch (offset) {
411     case GITS_TYPER:
412         *data = s->typer;
413         break;
414     case GITS_BASER ... GITS_BASER + 0x3f:
415         index = (offset - GITS_BASER) / 8;
416         *data = s->baser[index];
417         break;
418     case GITS_CBASER:
419         *data = s->cbaser;
420         break;
421     case GITS_CREADR:
422         *data = s->creadr;
423         break;
424     case GITS_CWRITER:
425         *data = s->cwriter;
426         break;
427     default:
428         result = false;
429         break;
430     }
431     return result;
432 }
433 
434 static MemTxResult gicv3_its_read(void *opaque, hwaddr offset, uint64_t *data,
435                                   unsigned size, MemTxAttrs attrs)
436 {
437     GICv3ITSState *s = (GICv3ITSState *)opaque;
438     bool result;
439 
440     switch (size) {
441     case 4:
442         result = its_readl(s, offset, data, attrs);
443         break;
444     case 8:
445         result = its_readll(s, offset, data, attrs);
446         break;
447     default:
448         result = false;
449         break;
450     }
451 
452     if (!result) {
453         qemu_log_mask(LOG_GUEST_ERROR,
454                       "%s: invalid guest read at offset " TARGET_FMT_plx
455                       "size %u\n", __func__, offset, size);
456         /*
457          * The spec requires that reserved registers are RAZ/WI;
458          * so use false returns from leaf functions as a way to
459          * trigger the guest-error logging but don't return it to
460          * the caller, or we'll cause a spurious guest data abort.
461          */
462         *data = 0;
463     }
464     return MEMTX_OK;
465 }
466 
467 static MemTxResult gicv3_its_write(void *opaque, hwaddr offset, uint64_t data,
468                                    unsigned size, MemTxAttrs attrs)
469 {
470     GICv3ITSState *s = (GICv3ITSState *)opaque;
471     bool result;
472 
473     switch (size) {
474     case 4:
475         result = its_writel(s, offset, data, attrs);
476         break;
477     case 8:
478         result = its_writell(s, offset, data, attrs);
479         break;
480     default:
481         result = false;
482         break;
483     }
484 
485     if (!result) {
486         qemu_log_mask(LOG_GUEST_ERROR,
487                       "%s: invalid guest write at offset " TARGET_FMT_plx
488                       "size %u\n", __func__, offset, size);
489         /*
490          * The spec requires that reserved registers are RAZ/WI;
491          * so use false returns from leaf functions as a way to
492          * trigger the guest-error logging but don't return it to
493          * the caller, or we'll cause a spurious guest data abort.
494          */
495     }
496     return MEMTX_OK;
497 }
498 
499 static const MemoryRegionOps gicv3_its_control_ops = {
500     .read_with_attrs = gicv3_its_read,
501     .write_with_attrs = gicv3_its_write,
502     .valid.min_access_size = 4,
503     .valid.max_access_size = 8,
504     .impl.min_access_size = 4,
505     .impl.max_access_size = 8,
506     .endianness = DEVICE_NATIVE_ENDIAN,
507 };
508 
509 static const MemoryRegionOps gicv3_its_translation_ops = {
510     .write_with_attrs = gicv3_its_translation_write,
511     .valid.min_access_size = 2,
512     .valid.max_access_size = 4,
513     .impl.min_access_size = 2,
514     .impl.max_access_size = 4,
515     .endianness = DEVICE_NATIVE_ENDIAN,
516 };
517 
518 static void gicv3_arm_its_realize(DeviceState *dev, Error **errp)
519 {
520     GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev);
521     int i;
522 
523     for (i = 0; i < s->gicv3->num_cpu; i++) {
524         if (!(s->gicv3->cpu[i].gicr_typer & GICR_TYPER_PLPIS)) {
525             error_setg(errp, "Physical LPI not supported by CPU %d", i);
526             return;
527         }
528     }
529 
530     gicv3_its_init_mmio(s, &gicv3_its_control_ops, &gicv3_its_translation_ops);
531 
532     address_space_init(&s->gicv3->dma_as, s->gicv3->dma,
533                        "gicv3-its-sysmem");
534 
535     /* set the ITS default features supported */
536     s->typer = FIELD_DP64(s->typer, GITS_TYPER, PHYSICAL,
537                           GITS_TYPE_PHYSICAL);
538     s->typer = FIELD_DP64(s->typer, GITS_TYPER, ITT_ENTRY_SIZE,
539                           ITS_ITT_ENTRY_SIZE - 1);
540     s->typer = FIELD_DP64(s->typer, GITS_TYPER, IDBITS, ITS_IDBITS);
541     s->typer = FIELD_DP64(s->typer, GITS_TYPER, DEVBITS, ITS_DEVBITS);
542     s->typer = FIELD_DP64(s->typer, GITS_TYPER, CIL, 1);
543     s->typer = FIELD_DP64(s->typer, GITS_TYPER, CIDBITS, ITS_CIDBITS);
544 }
545 
546 static void gicv3_its_reset(DeviceState *dev)
547 {
548     GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev);
549     GICv3ITSClass *c = ARM_GICV3_ITS_GET_CLASS(s);
550 
551     c->parent_reset(dev);
552 
553     /* Quiescent bit reset to 1 */
554     s->ctlr = FIELD_DP32(s->ctlr, GITS_CTLR, QUIESCENT, 1);
555 
556     /*
557      * setting GITS_BASER0.Type = 0b001 (Device)
558      *         GITS_BASER1.Type = 0b100 (Collection Table)
559      *         GITS_BASER<n>.Type,where n = 3 to 7 are 0b00 (Unimplemented)
560      *         GITS_BASER<0,1>.Page_Size = 64KB
561      * and default translation table entry size to 16 bytes
562      */
563     s->baser[0] = FIELD_DP64(s->baser[0], GITS_BASER, TYPE,
564                              GITS_BASER_TYPE_DEVICE);
565     s->baser[0] = FIELD_DP64(s->baser[0], GITS_BASER, PAGESIZE,
566                              GITS_BASER_PAGESIZE_64K);
567     s->baser[0] = FIELD_DP64(s->baser[0], GITS_BASER, ENTRYSIZE,
568                              GITS_DTE_SIZE - 1);
569 
570     s->baser[1] = FIELD_DP64(s->baser[1], GITS_BASER, TYPE,
571                              GITS_BASER_TYPE_COLLECTION);
572     s->baser[1] = FIELD_DP64(s->baser[1], GITS_BASER, PAGESIZE,
573                              GITS_BASER_PAGESIZE_64K);
574     s->baser[1] = FIELD_DP64(s->baser[1], GITS_BASER, ENTRYSIZE,
575                              GITS_CTE_SIZE - 1);
576 }
577 
578 static void gicv3_its_post_load(GICv3ITSState *s)
579 {
580     if (s->ctlr & ITS_CTLR_ENABLED) {
581         extract_table_params(s);
582         extract_cmdq_params(s);
583     }
584 }
585 
586 static Property gicv3_its_props[] = {
587     DEFINE_PROP_LINK("parent-gicv3", GICv3ITSState, gicv3, "arm-gicv3",
588                      GICv3State *),
589     DEFINE_PROP_END_OF_LIST(),
590 };
591 
592 static void gicv3_its_class_init(ObjectClass *klass, void *data)
593 {
594     DeviceClass *dc = DEVICE_CLASS(klass);
595     GICv3ITSClass *ic = ARM_GICV3_ITS_CLASS(klass);
596     GICv3ITSCommonClass *icc = ARM_GICV3_ITS_COMMON_CLASS(klass);
597 
598     dc->realize = gicv3_arm_its_realize;
599     device_class_set_props(dc, gicv3_its_props);
600     device_class_set_parent_reset(dc, gicv3_its_reset, &ic->parent_reset);
601     icc->post_load = gicv3_its_post_load;
602 }
603 
604 static const TypeInfo gicv3_its_info = {
605     .name = TYPE_ARM_GICV3_ITS,
606     .parent = TYPE_ARM_GICV3_ITS_COMMON,
607     .instance_size = sizeof(GICv3ITSState),
608     .class_init = gicv3_its_class_init,
609     .class_size = sizeof(GICv3ITSClass),
610 };
611 
612 static void gicv3_its_register_types(void)
613 {
614     type_register_static(&gicv3_its_info);
615 }
616 
617 type_init(gicv3_its_register_types)
618