hw/intc: GICv3 ITS register definitions added
[qemu.git] / hw / intc / arm_gicv3_its.c
blob8234939ccc17c92d0e69c0ec782b0761984d776f
1 /*
2 * ITS emulation for a GICv3-based system
4 * Copyright Linaro.org 2021
6 * Authors:
7 * Shashi Mallela <shashi.mallela@linaro.org>
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.
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"
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)
27 struct GICv3ITSClass {
28 GICv3ITSCommonClass parent_class;
29 void (*parent_reset)(DeviceState *dev);
32 static uint64_t baser_base_addr(uint64_t value, uint32_t page_sz)
34 uint64_t result = 0;
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;
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;
47 default:
48 break;
50 return result;
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
58 static void extract_table_params(GICv3ITSState *s)
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;
66 for (int i = 0; i < 8; i++) {
67 value = s->baser[i];
69 if (!value) {
70 continue;
73 page_sz_type = FIELD_EX64(value, GITS_BASER, PAGESIZE);
75 switch (page_sz_type) {
76 case 0:
77 page_sz = GITS_PAGE_SIZE_4K;
78 break;
80 case 1:
81 page_sz = GITS_PAGE_SIZE_16K;
82 break;
84 case 2:
85 case 3:
86 page_sz = GITS_PAGE_SIZE_64K;
87 break;
89 default:
90 g_assert_not_reached();
93 num_pages = FIELD_EX64(value, GITS_BASER, SIZE) + 1;
95 type = FIELD_EX64(value, GITS_BASER, TYPE);
97 switch (type) {
99 case GITS_BASER_TYPE_DEVICE:
100 memset(&s->dt, 0 , sizeof(s->dt));
101 s->dt.valid = FIELD_EX64(value, GITS_BASER, VALID);
103 if (!s->dt.valid) {
104 return;
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);
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));
119 s->dt.maxids.max_devids = (1UL << (FIELD_EX64(s->typer, GITS_TYPER,
120 DEVBITS) + 1));
122 s->dt.base_addr = baser_base_addr(value, page_sz);
124 break;
126 case GITS_BASER_TYPE_COLLECTION:
127 memset(&s->ct, 0 , sizeof(s->ct));
128 s->ct.valid = FIELD_EX64(value, GITS_BASER, VALID);
131 * GITS_TYPER.HCC is 0 for this implementation
132 * hence writes are discarded if ct.valid is 0
134 if (!s->ct.valid) {
135 return;
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);
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));
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);
158 s->ct.base_addr = baser_base_addr(value, page_sz);
160 break;
162 default:
163 break;
168 static void extract_cmdq_params(GICv3ITSState *s)
170 uint16_t num_pages = 0;
171 uint64_t value = s->cbaser;
173 num_pages = FIELD_EX64(value, GITS_CBASER, SIZE) + 1;
175 memset(&s->cq, 0 , sizeof(s->cq));
176 s->cq.valid = FIELD_EX64(value, GITS_CBASER, VALID);
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;
186 static MemTxResult gicv3_its_translation_write(void *opaque, hwaddr offset,
187 uint64_t data, unsigned size,
188 MemTxAttrs attrs)
190 return MEMTX_OK;
193 static bool its_writel(GICv3ITSState *s, hwaddr offset,
194 uint64_t value, MemTxAttrs attrs)
196 bool result = true;
197 int index;
199 switch (offset) {
200 case GITS_CTLR:
201 s->ctlr |= (value & ~(s->ctlr));
203 if (s->ctlr & ITS_CTLR_ENABLED) {
204 extract_table_params(s);
205 extract_cmdq_params(s);
206 s->creadr = 0;
208 break;
209 case GITS_CBASER:
211 * IMPDEF choice:- GITS_CBASER register becomes RO if ITS is
212 * already enabled
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;
219 break;
220 case GITS_CBASER + 4:
222 * IMPDEF choice:- GITS_CBASER register becomes RO if ITS is
223 * already enabled
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;
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);
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);
258 break;
259 case GITS_BASER ... GITS_BASER + 0x3f:
261 * IMPDEF choice:- GITS_BASERn register becomes RO if ITS is
262 * already enabled
264 if (!(s->ctlr & ITS_CTLR_ENABLED)) {
265 index = (offset - GITS_BASER) / 8;
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;
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;
290 return result;
293 static bool its_readl(GICv3ITSState *s, hwaddr offset,
294 uint64_t *data, MemTxAttrs attrs)
296 bool result = true;
297 int index;
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);
341 break;
342 default:
343 result = false;
344 break;
346 return result;
349 static bool its_writell(GICv3ITSState *s, hwaddr offset,
350 uint64_t value, MemTxAttrs attrs)
352 bool result = true;
353 int index;
355 switch (offset) {
356 case GITS_BASER ... GITS_BASER + 0x3f:
358 * IMPDEF choice:- GITS_BASERn register becomes RO if ITS is
359 * already enabled
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);
366 break;
367 case GITS_CBASER:
369 * IMPDEF choice:- GITS_CBASER register becomes RO if ITS is
370 * already enabled
372 if (!(s->ctlr & ITS_CTLR_ENABLED)) {
373 s->cbaser = value;
374 s->creadr = 0;
375 s->cwriter = s->creadr;
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);
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;
401 return result;
404 static bool its_readll(GICv3ITSState *s, hwaddr offset,
405 uint64_t *data, MemTxAttrs attrs)
407 bool result = true;
408 int index;
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;
431 return result;
434 static MemTxResult gicv3_its_read(void *opaque, hwaddr offset, uint64_t *data,
435 unsigned size, MemTxAttrs attrs)
437 GICv3ITSState *s = (GICv3ITSState *)opaque;
438 bool result;
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;
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);
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.
462 *data = 0;
464 return MEMTX_OK;
467 static MemTxResult gicv3_its_write(void *opaque, hwaddr offset, uint64_t data,
468 unsigned size, MemTxAttrs attrs)
470 GICv3ITSState *s = (GICv3ITSState *)opaque;
471 bool result;
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;
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);
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.
496 return MEMTX_OK;
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,
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,
518 static void gicv3_arm_its_realize(DeviceState *dev, Error **errp)
520 GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev);
521 int i;
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;
530 gicv3_its_init_mmio(s, &gicv3_its_control_ops, &gicv3_its_translation_ops);
532 address_space_init(&s->gicv3->dma_as, s->gicv3->dma,
533 "gicv3-its-sysmem");
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);
546 static void gicv3_its_reset(DeviceState *dev)
548 GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev);
549 GICv3ITSClass *c = ARM_GICV3_ITS_GET_CLASS(s);
551 c->parent_reset(dev);
553 /* Quiescent bit reset to 1 */
554 s->ctlr = FIELD_DP32(s->ctlr, GITS_CTLR, QUIESCENT, 1);
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
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);
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);
578 static void gicv3_its_post_load(GICv3ITSState *s)
580 if (s->ctlr & ITS_CTLR_ENABLED) {
581 extract_table_params(s);
582 extract_cmdq_params(s);
586 static Property gicv3_its_props[] = {
587 DEFINE_PROP_LINK("parent-gicv3", GICv3ITSState, gicv3, "arm-gicv3",
588 GICv3State *),
589 DEFINE_PROP_END_OF_LIST(),
592 static void gicv3_its_class_init(ObjectClass *klass, void *data)
594 DeviceClass *dc = DEVICE_CLASS(klass);
595 GICv3ITSClass *ic = ARM_GICV3_ITS_CLASS(klass);
596 GICv3ITSCommonClass *icc = ARM_GICV3_ITS_COMMON_CLASS(klass);
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;
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),
612 static void gicv3_its_register_types(void)
614 type_register_static(&gicv3_its_info);
617 type_init(gicv3_its_register_types)