diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst index 69f57c2886..adf497e679 100644 --- a/docs/system/arm/nuvoton.rst +++ b/docs/system/arm/nuvoton.rst @@ -20,6 +20,7 @@ Hyperscale applications. The following machines are based on this chip : - ``quanta-gbs-bmc`` Quanta GBS server BMC - ``quanta-gsj`` Quanta GSJ server BMC +- ``kudo-bmc`` Fii USA Kudo server BMC There are also two more SoCs, NPCM710 and NPCM705, which are single-core variants of NPCM750 and NPCM730, respectively. These are currently not diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index e23830f4b7..f40e854dec 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -373,6 +373,11 @@ static qemu_irq get_sse_irq_in(MPS2TZMachineState *mms, int irqno) } } +/* Union describing the device-specific extra data we pass to the devfn. */ +typedef union PPCExtraData { + bool i2c_internal; +} PPCExtraData; + /* Most of the devices in the AN505 FPGA image sit behind * Peripheral Protection Controllers. These data structures * define the layout of which devices sit behind which PPCs. @@ -382,7 +387,8 @@ static qemu_irq get_sse_irq_in(MPS2TZMachineState *mms, int irqno) */ typedef MemoryRegion *MakeDevFn(MPS2TZMachineState *mms, void *opaque, const char *name, hwaddr size, - const int *irqs); + const int *irqs, + const PPCExtraData *extradata); typedef struct PPCPortInfo { const char *name; @@ -391,6 +397,7 @@ typedef struct PPCPortInfo { hwaddr addr; hwaddr size; int irqs[3]; /* currently no device needs more IRQ lines than this */ + PPCExtraData extradata; /* to pass device-specific info to the devfn */ } PPCPortInfo; typedef struct PPCInfo { @@ -401,7 +408,8 @@ typedef struct PPCInfo { static MemoryRegion *make_unimp_dev(MPS2TZMachineState *mms, void *opaque, const char *name, hwaddr size, - const int *irqs) + const int *irqs, + const PPCExtraData *extradata) { /* Initialize, configure and realize a TYPE_UNIMPLEMENTED_DEVICE, * and return a pointer to its MemoryRegion. @@ -417,7 +425,7 @@ static MemoryRegion *make_unimp_dev(MPS2TZMachineState *mms, static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque, const char *name, hwaddr size, - const int *irqs) + const int *irqs, const PPCExtraData *extradata) { /* The irq[] array is tx, rx, combined, in that order */ MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms); @@ -441,7 +449,7 @@ static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque, static MemoryRegion *make_scc(MPS2TZMachineState *mms, void *opaque, const char *name, hwaddr size, - const int *irqs) + const int *irqs, const PPCExtraData *extradata) { MPS2SCC *scc = opaque; DeviceState *sccdev; @@ -465,7 +473,7 @@ static MemoryRegion *make_scc(MPS2TZMachineState *mms, void *opaque, static MemoryRegion *make_fpgaio(MPS2TZMachineState *mms, void *opaque, const char *name, hwaddr size, - const int *irqs) + const int *irqs, const PPCExtraData *extradata) { MPS2FPGAIO *fpgaio = opaque; MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms); @@ -480,7 +488,8 @@ static MemoryRegion *make_fpgaio(MPS2TZMachineState *mms, void *opaque, static MemoryRegion *make_eth_dev(MPS2TZMachineState *mms, void *opaque, const char *name, hwaddr size, - const int *irqs) + const int *irqs, + const PPCExtraData *extradata) { SysBusDevice *s; NICInfo *nd = &nd_table[0]; @@ -500,7 +509,8 @@ static MemoryRegion *make_eth_dev(MPS2TZMachineState *mms, void *opaque, static MemoryRegion *make_eth_usb(MPS2TZMachineState *mms, void *opaque, const char *name, hwaddr size, - const int *irqs) + const int *irqs, + const PPCExtraData *extradata) { /* * The AN524 makes the ethernet and USB share a PPC port. @@ -543,7 +553,7 @@ static MemoryRegion *make_eth_usb(MPS2TZMachineState *mms, void *opaque, static MemoryRegion *make_mpc(MPS2TZMachineState *mms, void *opaque, const char *name, hwaddr size, - const int *irqs) + const int *irqs, const PPCExtraData *extradata) { TZMPC *mpc = opaque; int i = mpc - &mms->mpc[0]; @@ -615,7 +625,7 @@ static void remap_irq_fn(void *opaque, int n, int level) static MemoryRegion *make_dma(MPS2TZMachineState *mms, void *opaque, const char *name, hwaddr size, - const int *irqs) + const int *irqs, const PPCExtraData *extradata) { /* The irq[] array is DMACINTR, DMACINTERR, DMACINTTC, in that order */ PL080State *dma = opaque; @@ -672,7 +682,7 @@ static MemoryRegion *make_dma(MPS2TZMachineState *mms, void *opaque, static MemoryRegion *make_spi(MPS2TZMachineState *mms, void *opaque, const char *name, hwaddr size, - const int *irqs) + const int *irqs, const PPCExtraData *extradata) { /* * The AN505 has five PL022 SPI controllers. @@ -694,7 +704,7 @@ static MemoryRegion *make_spi(MPS2TZMachineState *mms, void *opaque, static MemoryRegion *make_i2c(MPS2TZMachineState *mms, void *opaque, const char *name, hwaddr size, - const int *irqs) + const int *irqs, const PPCExtraData *extradata) { ArmSbconI2CState *i2c = opaque; SysBusDevice *s; @@ -702,12 +712,26 @@ static MemoryRegion *make_i2c(MPS2TZMachineState *mms, void *opaque, object_initialize_child(OBJECT(mms), name, i2c, TYPE_ARM_SBCON_I2C); s = SYS_BUS_DEVICE(i2c); sysbus_realize(s, &error_fatal); + + /* + * If this is an internal-use-only i2c bus, mark it full + * so that user-created i2c devices are not plugged into it. + * If we implement models of any on-board i2c devices that + * plug in to one of the internal-use-only buses, then we will + * need to create and plugging those in here before we mark the + * bus as full. + */ + if (extradata->i2c_internal) { + BusState *qbus = qdev_get_child_bus(DEVICE(i2c), "i2c"); + qbus_mark_full(qbus); + } + return sysbus_mmio_get_region(s, 0); } static MemoryRegion *make_rtc(MPS2TZMachineState *mms, void *opaque, const char *name, hwaddr size, - const int *irqs) + const int *irqs, const PPCExtraData *extradata) { PL031State *pl031 = opaque; SysBusDevice *s; @@ -912,10 +936,14 @@ static void mps2tz_common_init(MachineState *machine) { "uart2", make_uart, &mms->uart[2], 0x40202000, 0x1000, { 36, 37, 44 } }, { "uart3", make_uart, &mms->uart[3], 0x40203000, 0x1000, { 38, 39, 45 } }, { "uart4", make_uart, &mms->uart[4], 0x40204000, 0x1000, { 40, 41, 46 } }, - { "i2c0", make_i2c, &mms->i2c[0], 0x40207000, 0x1000 }, - { "i2c1", make_i2c, &mms->i2c[1], 0x40208000, 0x1000 }, - { "i2c2", make_i2c, &mms->i2c[2], 0x4020c000, 0x1000 }, - { "i2c3", make_i2c, &mms->i2c[3], 0x4020d000, 0x1000 }, + { "i2c0", make_i2c, &mms->i2c[0], 0x40207000, 0x1000, {}, + { .i2c_internal = true /* touchscreen */ } }, + { "i2c1", make_i2c, &mms->i2c[1], 0x40208000, 0x1000, {}, + { .i2c_internal = true /* audio conf */ } }, + { "i2c2", make_i2c, &mms->i2c[2], 0x4020c000, 0x1000, {}, + { .i2c_internal = false /* shield 0 */ } }, + { "i2c3", make_i2c, &mms->i2c[3], 0x4020d000, 0x1000, {}, + { .i2c_internal = false /* shield 1 */ } }, }, }, { .name = "apb_ppcexp2", @@ -956,15 +984,20 @@ static void mps2tz_common_init(MachineState *machine) }, { .name = "apb_ppcexp1", .ports = { - { "i2c0", make_i2c, &mms->i2c[0], 0x41200000, 0x1000 }, - { "i2c1", make_i2c, &mms->i2c[1], 0x41201000, 0x1000 }, + { "i2c0", make_i2c, &mms->i2c[0], 0x41200000, 0x1000, {}, + { .i2c_internal = true /* touchscreen */ } }, + { "i2c1", make_i2c, &mms->i2c[1], 0x41201000, 0x1000, {}, + { .i2c_internal = true /* audio conf */ } }, { "spi0", make_spi, &mms->spi[0], 0x41202000, 0x1000, { 52 } }, { "spi1", make_spi, &mms->spi[1], 0x41203000, 0x1000, { 53 } }, { "spi2", make_spi, &mms->spi[2], 0x41204000, 0x1000, { 54 } }, - { "i2c2", make_i2c, &mms->i2c[2], 0x41205000, 0x1000 }, - { "i2c3", make_i2c, &mms->i2c[3], 0x41206000, 0x1000 }, + { "i2c2", make_i2c, &mms->i2c[2], 0x41205000, 0x1000, {}, + { .i2c_internal = false /* shield 0 */ } }, + { "i2c3", make_i2c, &mms->i2c[3], 0x41206000, 0x1000, {}, + { .i2c_internal = false /* shield 1 */ } }, { /* port 7 reserved */ }, - { "i2c4", make_i2c, &mms->i2c[4], 0x41208000, 0x1000 }, + { "i2c4", make_i2c, &mms->i2c[4], 0x41208000, 0x1000, {}, + { .i2c_internal = true /* DDR4 EEPROM */ } }, }, }, { .name = "apb_ppcexp2", @@ -1006,15 +1039,20 @@ static void mps2tz_common_init(MachineState *machine) }, { .name = "apb_ppcexp1", .ports = { - { "i2c0", make_i2c, &mms->i2c[0], 0x49200000, 0x1000 }, - { "i2c1", make_i2c, &mms->i2c[1], 0x49201000, 0x1000 }, + { "i2c0", make_i2c, &mms->i2c[0], 0x49200000, 0x1000, {}, + { .i2c_internal = true /* touchscreen */ } }, + { "i2c1", make_i2c, &mms->i2c[1], 0x49201000, 0x1000, {}, + { .i2c_internal = true /* audio conf */ } }, { "spi0", make_spi, &mms->spi[0], 0x49202000, 0x1000, { 53 } }, { "spi1", make_spi, &mms->spi[1], 0x49203000, 0x1000, { 54 } }, { "spi2", make_spi, &mms->spi[2], 0x49204000, 0x1000, { 55 } }, - { "i2c2", make_i2c, &mms->i2c[2], 0x49205000, 0x1000 }, - { "i2c3", make_i2c, &mms->i2c[3], 0x49206000, 0x1000 }, + { "i2c2", make_i2c, &mms->i2c[2], 0x49205000, 0x1000, {}, + { .i2c_internal = false /* shield 0 */ } }, + { "i2c3", make_i2c, &mms->i2c[3], 0x49206000, 0x1000, {}, + { .i2c_internal = false /* shield 1 */ } }, { /* port 7 reserved */ }, - { "i2c4", make_i2c, &mms->i2c[4], 0x49208000, 0x1000 }, + { "i2c4", make_i2c, &mms->i2c[4], 0x49208000, 0x1000, {}, + { .i2c_internal = true /* DDR4 EEPROM */ } }, }, }, { .name = "apb_ppcexp2", @@ -1084,7 +1122,7 @@ static void mps2tz_common_init(MachineState *machine) } mr = pinfo->devfn(mms, pinfo->opaque, pinfo->name, pinfo->size, - pinfo->irqs); + pinfo->irqs, &pinfo->extradata); portname = g_strdup_printf("port[%d]", port); object_property_set_link(OBJECT(ppc), portname, OBJECT(mr), &error_fatal); diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c index 4634aa1a1c..bb76fa6889 100644 --- a/hw/arm/mps2.c +++ b/hw/arm/mps2.c @@ -428,7 +428,17 @@ static void mps2_common_init(MachineState *machine) 0x40023000, /* Audio */ 0x40029000, /* Shield0 */ 0x4002a000}; /* Shield1 */ - sysbus_create_simple(TYPE_ARM_SBCON_I2C, i2cbase[i], NULL); + DeviceState *dev; + + dev = sysbus_create_simple(TYPE_ARM_SBCON_I2C, i2cbase[i], NULL); + if (i < 2) { + /* + * internal-only bus: mark it full to avoid user-created + * i2c devices being plugged into it. + */ + BusState *qbus = qdev_get_child_bus(dev, "i2c"); + qbus_mark_full(qbus); + } } create_unimplemented_device("i2s", 0x40024000, 0x400); diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c index e5a3243995..a656169f61 100644 --- a/hw/arm/npcm7xx_boards.c +++ b/hw/arm/npcm7xx_boards.c @@ -31,6 +31,7 @@ #define NPCM750_EVB_POWER_ON_STRAPS 0x00001ff7 #define QUANTA_GSJ_POWER_ON_STRAPS 0x00001fff #define QUANTA_GBS_POWER_ON_STRAPS 0x000017ff +#define KUDO_BMC_POWER_ON_STRAPS 0x00001fff static const char npcm7xx_default_bootrom[] = "npcm7xx_bootrom.bin"; @@ -357,6 +358,23 @@ static void quanta_gbs_init(MachineState *machine) npcm7xx_load_kernel(machine, soc); } +static void kudo_bmc_init(MachineState *machine) +{ + NPCM7xxState *soc; + + soc = npcm7xx_create_soc(machine, KUDO_BMC_POWER_ON_STRAPS); + npcm7xx_connect_dram(soc, machine->ram); + qdev_realize(DEVICE(soc), NULL, &error_fatal); + + npcm7xx_load_bootrom(machine, soc); + npcm7xx_connect_flash(&soc->fiu[0], 0, "mx66u51235f", + drive_get(IF_MTD, 0, 0)); + npcm7xx_connect_flash(&soc->fiu[1], 0, "mx66u51235f", + drive_get(IF_MTD, 3, 0)); + + npcm7xx_load_kernel(machine, soc); +} + static void npcm7xx_set_soc_type(NPCM7xxMachineClass *nmc, const char *type) { NPCM7xxClass *sc = NPCM7XX_CLASS(object_class_by_name(type)); @@ -417,6 +435,18 @@ static void gbs_bmc_machine_class_init(ObjectClass *oc, void *data) mc->default_ram_size = 1 * GiB; } +static void kudo_bmc_machine_class_init(ObjectClass *oc, void *data) +{ + NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_CLASS(oc); + MachineClass *mc = MACHINE_CLASS(oc); + + npcm7xx_set_soc_type(nmc, TYPE_NPCM730); + + mc->desc = "Kudo BMC (Cortex-A9)"; + mc->init = kudo_bmc_init; + mc->default_ram_size = 1 * GiB; +}; + static const TypeInfo npcm7xx_machine_types[] = { { .name = TYPE_NPCM7XX_MACHINE, @@ -437,6 +467,10 @@ static const TypeInfo npcm7xx_machine_types[] = { .name = MACHINE_TYPE_NAME("quanta-gbs-bmc"), .parent = TYPE_NPCM7XX_MACHINE, .class_init = gbs_bmc_machine_class_init, + }, { + .name = MACHINE_TYPE_NAME("kudo-bmc"), + .parent = TYPE_NPCM7XX_MACHINE, + .class_init = kudo_bmc_machine_class_init, }, }; diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 73e9c6bb7c..1d59f0e59f 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -584,6 +584,12 @@ static void create_its(VirtMachineState *vms) const char *itsclass = its_class_name(); DeviceState *dev; + if (!strcmp(itsclass, "arm-gicv3-its")) { + if (!vms->tcg_its) { + itsclass = NULL; + } + } + if (!itsclass) { /* Do nothing if not supported */ return; @@ -621,7 +627,7 @@ static void create_v2m(VirtMachineState *vms) vms->msi_controller = VIRT_MSI_CTRL_GICV2M; } -static void create_gic(VirtMachineState *vms) +static void create_gic(VirtMachineState *vms, MemoryRegion *mem) { MachineState *ms = MACHINE(vms); /* We create a standalone GIC */ @@ -655,6 +661,14 @@ static void create_gic(VirtMachineState *vms) nb_redist_regions); qdev_prop_set_uint32(vms->gic, "redist-region-count[0]", redist0_count); + if (!kvm_irqchip_in_kernel()) { + if (vms->tcg_its) { + object_property_set_link(OBJECT(vms->gic), "sysmem", + OBJECT(mem), &error_fatal); + qdev_prop_set_bit(vms->gic, "has-lpi", true); + } + } + if (nb_redist_regions == 2) { uint32_t redist1_capacity = vms->memmap[VIRT_HIGH_GIC_REDIST2].size / GICV3_REDIST_SIZE; @@ -2039,7 +2053,7 @@ static void machvirt_init(MachineState *machine) virt_flash_fdt(vms, sysmem, secure_sysmem ?: sysmem); - create_gic(vms); + create_gic(vms, sysmem); virt_cpu_post_init(vms, sysmem); @@ -2742,6 +2756,12 @@ static void virt_instance_init(Object *obj) } else { /* Default allows ITS instantiation */ vms->its = true; + + if (vmc->no_tcg_its) { + vms->tcg_its = false; + } else { + vms->tcg_its = true; + } } /* Default disallows iommu instantiation */ @@ -2791,8 +2811,13 @@ DEFINE_VIRT_MACHINE_AS_LATEST(6, 2) static void virt_machine_6_1_options(MachineClass *mc) { + VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc)); + virt_machine_6_2_options(mc); compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len); + + /* qemu ITS was introduced with 6.2 */ + vmc->no_tcg_its = true; } DEFINE_VIRT_MACHINE(6, 1) diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c index b4b5e8a3ee..c069a30842 100644 --- a/hw/char/cadence_uart.c +++ b/hw/char/cadence_uart.c @@ -235,8 +235,18 @@ static void uart_parameters_setup(CadenceUARTState *s) static int uart_can_receive(void *opaque) { CadenceUARTState *s = opaque; - int ret = MAX(CADENCE_UART_RX_FIFO_SIZE, CADENCE_UART_TX_FIFO_SIZE); - uint32_t ch_mode = s->r[R_MR] & UART_MR_CHMODE; + int ret; + uint32_t ch_mode; + + /* ignore characters when unclocked or in reset */ + if (!clock_is_enabled(s->refclk) || device_is_in_reset(DEVICE(s))) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: uart is unclocked or in reset\n", + __func__); + return 0; + } + + ret = MAX(CADENCE_UART_RX_FIFO_SIZE, CADENCE_UART_TX_FIFO_SIZE); + ch_mode = s->r[R_MR] & UART_MR_CHMODE; if (ch_mode == NORMAL_MODE || ch_mode == ECHO_MODE) { ret = MIN(ret, CADENCE_UART_RX_FIFO_SIZE - s->rx_count); @@ -353,11 +363,6 @@ static void uart_receive(void *opaque, const uint8_t *buf, int size) CadenceUARTState *s = opaque; uint32_t ch_mode = s->r[R_MR] & UART_MR_CHMODE; - /* ignore characters when unclocked or in reset */ - if (!clock_is_enabled(s->refclk) || device_is_in_reset(DEVICE(s))) { - return; - } - if (ch_mode == NORMAL_MODE || ch_mode == ECHO_MODE) { uart_write_rx_fifo(opaque, buf, size); } @@ -373,6 +378,8 @@ static void uart_event(void *opaque, QEMUChrEvent event) /* ignore characters when unclocked or in reset */ if (!clock_is_enabled(s->refclk) || device_is_in_reset(DEVICE(s))) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: uart is unclocked or in reset\n", + __func__); return; } @@ -403,15 +410,22 @@ static void uart_read_rx_fifo(CadenceUARTState *s, uint32_t *c) uart_update_status(s); } -static void uart_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) +static MemTxResult uart_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size, MemTxAttrs attrs) { CadenceUARTState *s = opaque; + /* ignore access when unclocked or in reset */ + if (!clock_is_enabled(s->refclk) || device_is_in_reset(DEVICE(s))) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: uart is unclocked or in reset\n", + __func__); + return MEMTX_ERROR; + } + DB_PRINT(" offset:%x data:%08x\n", (unsigned)offset, (unsigned)value); offset >>= 2; if (offset >= CADENCE_UART_R_MAX) { - return; + return MEMTX_DECODE_ERROR; } switch (offset) { case R_IER: /* ier (wts imr) */ @@ -458,30 +472,41 @@ static void uart_write(void *opaque, hwaddr offset, break; } uart_update_status(s); + + return MEMTX_OK; } -static uint64_t uart_read(void *opaque, hwaddr offset, - unsigned size) +static MemTxResult uart_read(void *opaque, hwaddr offset, + uint64_t *value, unsigned size, MemTxAttrs attrs) { CadenceUARTState *s = opaque; uint32_t c = 0; + /* ignore access when unclocked or in reset */ + if (!clock_is_enabled(s->refclk) || device_is_in_reset(DEVICE(s))) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: uart is unclocked or in reset\n", + __func__); + return MEMTX_ERROR; + } + offset >>= 2; if (offset >= CADENCE_UART_R_MAX) { - c = 0; - } else if (offset == R_TX_RX) { + return MEMTX_DECODE_ERROR; + } + if (offset == R_TX_RX) { uart_read_rx_fifo(s, &c); } else { - c = s->r[offset]; + c = s->r[offset]; } DB_PRINT(" offset:%x data:%08x\n", (unsigned)(offset << 2), (unsigned)c); - return c; + *value = c; + return MEMTX_OK; } static const MemoryRegionOps uart_ops = { - .read = uart_read, - .write = uart_write, + .read_with_attrs = uart_read, + .write_with_attrs = uart_write, .endianness = DEVICE_NATIVE_ENDIAN, }; diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c index d63f8af604..3f24707838 100644 --- a/hw/intc/arm_gicv3.c +++ b/hw/intc/arm_gicv3.c @@ -165,6 +165,16 @@ static void gicv3_redist_update_noirqset(GICv3CPUState *cs) cs->hppi.grp = gicv3_irq_group(cs->gic, cs, cs->hppi.irq); } + if ((cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) && cs->gic->lpi_enable && + (cs->hpplpi.prio != 0xff)) { + if (irqbetter(cs, cs->hpplpi.irq, cs->hpplpi.prio)) { + cs->hppi.irq = cs->hpplpi.irq; + cs->hppi.prio = cs->hpplpi.prio; + cs->hppi.grp = cs->hpplpi.grp; + seenbetter = true; + } + } + /* If the best interrupt we just found would preempt whatever * was the previous best interrupt before this update, then * we know it's definitely the best one now. @@ -339,9 +349,13 @@ static void gicv3_set_irq(void *opaque, int irq, int level) static void arm_gicv3_post_load(GICv3State *s) { + int i; /* Recalculate our cached idea of the current highest priority * pending interrupt, but don't set IRQ or FIQ lines. */ + for (i = 0; i < s->num_cpu; i++) { + gicv3_redist_update_lpi(&s->cpu[i]); + } gicv3_full_update_noirqset(s); /* Repopulate the cache of GICv3CPUState pointers for target CPUs */ gicv3_cache_all_target_cpustates(s); diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index 58ef65f589..223db16fec 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -345,6 +345,11 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) return; } + if (s->lpi_enable && !s->dma) { + error_setg(errp, "Redist-ITS: Guest 'sysmem' reference link not set"); + return; + } + s->cpu = g_new0(GICv3CPUState, s->num_cpu); for (i = 0; i < s->num_cpu; i++) { @@ -381,6 +386,10 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) (1 << 24) | (i << 8) | (last << 4); + + if (s->lpi_enable) { + s->cpu[i].gicr_typer |= GICR_TYPER_PLPIS; + } } } @@ -426,6 +435,7 @@ static void arm_gicv3_common_reset(DeviceState *dev) memset(cs->gicr_ipriorityr, 0, sizeof(cs->gicr_ipriorityr)); cs->hppi.prio = 0xff; + cs->hpplpi.prio = 0xff; /* State in the CPU interface must *not* be reset here, because it * is part of the CPU's reset domain, not the GIC device's. @@ -494,9 +504,12 @@ static Property arm_gicv3_common_properties[] = { DEFINE_PROP_UINT32("num-cpu", GICv3State, num_cpu, 1), DEFINE_PROP_UINT32("num-irq", GICv3State, num_irq, 32), DEFINE_PROP_UINT32("revision", GICv3State, revision, 3), + DEFINE_PROP_BOOL("has-lpi", GICv3State, lpi_enable, 0), DEFINE_PROP_BOOL("has-security-extensions", GICv3State, security_extn, 0), DEFINE_PROP_ARRAY("redist-region-count", GICv3State, nb_redist_regions, redist_region_count, qdev_prop_uint32, uint32_t), + DEFINE_PROP_LINK("sysmem", GICv3State, dma, TYPE_MEMORY_REGION, + MemoryRegion *), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index a032d505f5..462a35f66e 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -899,10 +899,12 @@ static void icc_activate_irq(GICv3CPUState *cs, int irq) cs->gicr_iactiver0 = deposit32(cs->gicr_iactiver0, irq, 1, 1); cs->gicr_ipendr0 = deposit32(cs->gicr_ipendr0, irq, 1, 0); gicv3_redist_update(cs); - } else { + } else if (irq < GICV3_LPI_INTID_START) { gicv3_gicd_active_set(cs->gic, irq); gicv3_gicd_pending_clear(cs->gic, irq); gicv3_update(cs->gic, irq, 1); + } else { + gicv3_redist_lpi_pending(cs, irq, 0); } } @@ -1318,7 +1320,8 @@ static void icc_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri, trace_gicv3_icc_eoir_write(is_eoir0 ? 0 : 1, gicv3_redist_affid(cs), value); - if (irq >= cs->gic->num_irq) { + if ((irq >= cs->gic->num_irq) && + !(cs->gic->lpi_enable && (irq >= GICV3_LPI_INTID_START))) { /* This handles two cases: * 1. If software writes the ID of a spurious interrupt [ie 1020-1023] * to the GICC_EOIR, the GIC ignores that write. diff --git a/hw/intc/arm_gicv3_dist.c b/hw/intc/arm_gicv3_dist.c index 5beb7c4235..4164500ea9 100644 --- a/hw/intc/arm_gicv3_dist.c +++ b/hw/intc/arm_gicv3_dist.c @@ -384,7 +384,9 @@ static bool gicd_readl(GICv3State *s, hwaddr offset, * A3V == 1 (non-zero values of Affinity level 3 supported) * IDbits == 0xf (we support 16-bit interrupt identifiers) * DVIS == 0 (Direct virtual LPI injection not supported) - * LPIS == 0 (LPIs not supported) + * LPIS == 1 (LPIs are supported if affinity routing is enabled) + * num_LPIs == 0b00000 (bits [15:11],Number of LPIs as indicated + * by GICD_TYPER.IDbits) * MBIS == 0 (message-based SPIs not supported) * SecurityExtn == 1 if security extns supported * CPUNumber == 0 since for us ARE is always 1 @@ -399,6 +401,7 @@ static bool gicd_readl(GICv3State *s, hwaddr offset, bool sec_extn = !(s->gicd_ctlr & GICD_CTLR_DS); *data = (1 << 25) | (1 << 24) | (sec_extn << 10) | + (s->lpi_enable << GICD_TYPER_LPIS_SHIFT) | (0xf << 19) | itlinesnumber; return true; } diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c new file mode 100644 index 0000000000..84bcbb5f56 --- /dev/null +++ b/hw/intc/arm_gicv3_its.c @@ -0,0 +1,1322 @@ +/* + * ITS emulation for a GICv3-based system + * + * Copyright Linaro.org 2021 + * + * Authors: + * Shashi Mallela + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at your + * option) any later version. See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/qdev-properties.h" +#include "hw/intc/arm_gicv3_its_common.h" +#include "gicv3_internal.h" +#include "qom/object.h" +#include "qapi/error.h" + +typedef struct GICv3ITSClass GICv3ITSClass; +/* This is reusing the GICv3ITSState typedef from ARM_GICV3_ITS_COMMON */ +DECLARE_OBJ_CHECKERS(GICv3ITSState, GICv3ITSClass, + ARM_GICV3_ITS, TYPE_ARM_GICV3_ITS) + +struct GICv3ITSClass { + GICv3ITSCommonClass parent_class; + void (*parent_reset)(DeviceState *dev); +}; + +/* + * This is an internal enum used to distinguish between LPI triggered + * via command queue and LPI triggered via gits_translater write. + */ +typedef enum ItsCmdType { + NONE = 0, /* internal indication for GITS_TRANSLATER write */ + CLEAR = 1, + DISCARD = 2, + INTERRUPT = 3, +} ItsCmdType; + +typedef struct { + uint32_t iteh; + uint64_t itel; +} IteEntry; + +static uint64_t baser_base_addr(uint64_t value, uint32_t page_sz) +{ + uint64_t result = 0; + + switch (page_sz) { + case GITS_PAGE_SIZE_4K: + case GITS_PAGE_SIZE_16K: + result = FIELD_EX64(value, GITS_BASER, PHYADDR) << 12; + break; + + case GITS_PAGE_SIZE_64K: + result = FIELD_EX64(value, GITS_BASER, PHYADDRL_64K) << 16; + result |= FIELD_EX64(value, GITS_BASER, PHYADDRH_64K) << 48; + break; + + default: + break; + } + return result; +} + +static bool get_cte(GICv3ITSState *s, uint16_t icid, uint64_t *cte, + MemTxResult *res) +{ + AddressSpace *as = &s->gicv3->dma_as; + uint64_t l2t_addr; + uint64_t value; + bool valid_l2t; + uint32_t l2t_id; + uint32_t max_l2_entries; + + if (s->ct.indirect) { + l2t_id = icid / (s->ct.page_sz / L1TABLE_ENTRY_SIZE); + + value = address_space_ldq_le(as, + s->ct.base_addr + + (l2t_id * L1TABLE_ENTRY_SIZE), + MEMTXATTRS_UNSPECIFIED, res); + + if (*res == MEMTX_OK) { + valid_l2t = (value & L2_TABLE_VALID_MASK) != 0; + + if (valid_l2t) { + max_l2_entries = s->ct.page_sz / s->ct.entry_sz; + + l2t_addr = value & ((1ULL << 51) - 1); + + *cte = address_space_ldq_le(as, l2t_addr + + ((icid % max_l2_entries) * GITS_CTE_SIZE), + MEMTXATTRS_UNSPECIFIED, res); + } + } + } else { + /* Flat level table */ + *cte = address_space_ldq_le(as, s->ct.base_addr + + (icid * GITS_CTE_SIZE), + MEMTXATTRS_UNSPECIFIED, res); + } + + return (*cte & TABLE_ENTRY_VALID_MASK) != 0; +} + +static bool update_ite(GICv3ITSState *s, uint32_t eventid, uint64_t dte, + IteEntry ite) +{ + AddressSpace *as = &s->gicv3->dma_as; + uint64_t itt_addr; + MemTxResult res = MEMTX_OK; + + itt_addr = (dte & GITS_DTE_ITTADDR_MASK) >> GITS_DTE_ITTADDR_SHIFT; + itt_addr <<= ITTADDR_SHIFT; /* 256 byte aligned */ + + address_space_stq_le(as, itt_addr + (eventid * (sizeof(uint64_t) + + sizeof(uint32_t))), ite.itel, MEMTXATTRS_UNSPECIFIED, + &res); + + if (res == MEMTX_OK) { + address_space_stl_le(as, itt_addr + (eventid * (sizeof(uint64_t) + + sizeof(uint32_t))) + sizeof(uint32_t), ite.iteh, + MEMTXATTRS_UNSPECIFIED, &res); + } + if (res != MEMTX_OK) { + return false; + } else { + return true; + } +} + +static bool get_ite(GICv3ITSState *s, uint32_t eventid, uint64_t dte, + uint16_t *icid, uint32_t *pIntid, MemTxResult *res) +{ + AddressSpace *as = &s->gicv3->dma_as; + uint64_t itt_addr; + bool status = false; + IteEntry ite = {}; + + itt_addr = (dte & GITS_DTE_ITTADDR_MASK) >> GITS_DTE_ITTADDR_SHIFT; + itt_addr <<= ITTADDR_SHIFT; /* 256 byte aligned */ + + ite.itel = address_space_ldq_le(as, itt_addr + + (eventid * (sizeof(uint64_t) + + sizeof(uint32_t))), MEMTXATTRS_UNSPECIFIED, + res); + + if (*res == MEMTX_OK) { + ite.iteh = address_space_ldl_le(as, itt_addr + + (eventid * (sizeof(uint64_t) + + sizeof(uint32_t))) + sizeof(uint32_t), + MEMTXATTRS_UNSPECIFIED, res); + + if (*res == MEMTX_OK) { + if (ite.itel & TABLE_ENTRY_VALID_MASK) { + if ((ite.itel >> ITE_ENTRY_INTTYPE_SHIFT) & + GITS_TYPE_PHYSICAL) { + *pIntid = (ite.itel & ITE_ENTRY_INTID_MASK) >> + ITE_ENTRY_INTID_SHIFT; + *icid = ite.iteh & ITE_ENTRY_ICID_MASK; + status = true; + } + } + } + } + return status; +} + +static uint64_t get_dte(GICv3ITSState *s, uint32_t devid, MemTxResult *res) +{ + AddressSpace *as = &s->gicv3->dma_as; + uint64_t l2t_addr; + uint64_t value; + bool valid_l2t; + uint32_t l2t_id; + uint32_t max_l2_entries; + + if (s->dt.indirect) { + l2t_id = devid / (s->dt.page_sz / L1TABLE_ENTRY_SIZE); + + value = address_space_ldq_le(as, + s->dt.base_addr + + (l2t_id * L1TABLE_ENTRY_SIZE), + MEMTXATTRS_UNSPECIFIED, res); + + if (*res == MEMTX_OK) { + valid_l2t = (value & L2_TABLE_VALID_MASK) != 0; + + if (valid_l2t) { + max_l2_entries = s->dt.page_sz / s->dt.entry_sz; + + l2t_addr = value & ((1ULL << 51) - 1); + + value = address_space_ldq_le(as, l2t_addr + + ((devid % max_l2_entries) * GITS_DTE_SIZE), + MEMTXATTRS_UNSPECIFIED, res); + } + } + } else { + /* Flat level table */ + value = address_space_ldq_le(as, s->dt.base_addr + + (devid * GITS_DTE_SIZE), + MEMTXATTRS_UNSPECIFIED, res); + } + + return value; +} + +/* + * This function handles the processing of following commands based on + * the ItsCmdType parameter passed:- + * 1. triggering of lpi interrupt translation via ITS INT command + * 2. triggering of lpi interrupt translation via gits_translater register + * 3. handling of ITS CLEAR command + * 4. handling of ITS DISCARD command + */ +static bool process_its_cmd(GICv3ITSState *s, uint64_t value, uint32_t offset, + ItsCmdType cmd) +{ + AddressSpace *as = &s->gicv3->dma_as; + uint32_t devid, eventid; + MemTxResult res = MEMTX_OK; + bool dte_valid; + uint64_t dte = 0; + uint32_t max_eventid; + uint16_t icid = 0; + uint32_t pIntid = 0; + bool ite_valid = false; + uint64_t cte = 0; + bool cte_valid = false; + bool result = false; + uint64_t rdbase; + + if (cmd == NONE) { + devid = offset; + } else { + devid = ((value & DEVID_MASK) >> DEVID_SHIFT); + + offset += NUM_BYTES_IN_DW; + value = address_space_ldq_le(as, s->cq.base_addr + offset, + MEMTXATTRS_UNSPECIFIED, &res); + } + + if (res != MEMTX_OK) { + return result; + } + + eventid = (value & EVENTID_MASK); + + dte = get_dte(s, devid, &res); + + if (res != MEMTX_OK) { + return result; + } + dte_valid = dte & TABLE_ENTRY_VALID_MASK; + + if (dte_valid) { + max_eventid = (1UL << (((dte >> 1U) & SIZE_MASK) + 1)); + + ite_valid = get_ite(s, eventid, dte, &icid, &pIntid, &res); + + if (res != MEMTX_OK) { + return result; + } + + if (ite_valid) { + cte_valid = get_cte(s, icid, &cte, &res); + } + + if (res != MEMTX_OK) { + return result; + } + } + + if ((devid > s->dt.maxids.max_devids) || !dte_valid || !ite_valid || + !cte_valid || (eventid > max_eventid)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid command attributes " + "devid %d or eventid %d or invalid dte %d or" + "invalid cte %d or invalid ite %d\n", + __func__, devid, eventid, dte_valid, cte_valid, + ite_valid); + /* + * in this implementation, in case of error + * we ignore this command and move onto the next + * command in the queue + */ + } else { + /* + * Current implementation only supports rdbase == procnum + * Hence rdbase physical address is ignored + */ + rdbase = (cte & GITS_CTE_RDBASE_PROCNUM_MASK) >> 1U; + + if (rdbase > s->gicv3->num_cpu) { + return result; + } + + if ((cmd == CLEAR) || (cmd == DISCARD)) { + gicv3_redist_process_lpi(&s->gicv3->cpu[rdbase], pIntid, 0); + } else { + gicv3_redist_process_lpi(&s->gicv3->cpu[rdbase], pIntid, 1); + } + + if (cmd == DISCARD) { + IteEntry ite = {}; + /* remove mapping from interrupt translation table */ + result = update_ite(s, eventid, dte, ite); + } + } + + return result; +} + +static bool process_mapti(GICv3ITSState *s, uint64_t value, uint32_t offset, + bool ignore_pInt) +{ + AddressSpace *as = &s->gicv3->dma_as; + uint32_t devid, eventid; + uint32_t pIntid = 0; + uint32_t max_eventid, max_Intid; + bool dte_valid; + MemTxResult res = MEMTX_OK; + uint16_t icid = 0; + uint64_t dte = 0; + IteEntry ite; + uint32_t int_spurious = INTID_SPURIOUS; + bool result = false; + + devid = ((value & DEVID_MASK) >> DEVID_SHIFT); + offset += NUM_BYTES_IN_DW; + value = address_space_ldq_le(as, s->cq.base_addr + offset, + MEMTXATTRS_UNSPECIFIED, &res); + + if (res != MEMTX_OK) { + return result; + } + + eventid = (value & EVENTID_MASK); + + if (!ignore_pInt) { + pIntid = ((value & pINTID_MASK) >> pINTID_SHIFT); + } + + offset += NUM_BYTES_IN_DW; + value = address_space_ldq_le(as, s->cq.base_addr + offset, + MEMTXATTRS_UNSPECIFIED, &res); + + if (res != MEMTX_OK) { + return result; + } + + icid = value & ICID_MASK; + + dte = get_dte(s, devid, &res); + + if (res != MEMTX_OK) { + return result; + } + dte_valid = dte & TABLE_ENTRY_VALID_MASK; + + max_eventid = (1UL << (((dte >> 1U) & SIZE_MASK) + 1)); + + if (!ignore_pInt) { + max_Intid = (1ULL << (GICD_TYPER_IDBITS + 1)) - 1; + } + + if ((devid > s->dt.maxids.max_devids) || (icid > s->ct.maxids.max_collids) + || !dte_valid || (eventid > max_eventid) || + (!ignore_pInt && (((pIntid < GICV3_LPI_INTID_START) || + (pIntid > max_Intid)) && (pIntid != INTID_SPURIOUS)))) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid command attributes " + "devid %d or icid %d or eventid %d or pIntid %d or" + "unmapped dte %d\n", __func__, devid, icid, eventid, + pIntid, dte_valid); + /* + * in this implementation, in case of error + * we ignore this command and move onto the next + * command in the queue + */ + } else { + /* add ite entry to interrupt translation table */ + ite.itel = (dte_valid & TABLE_ENTRY_VALID_MASK) | + (GITS_TYPE_PHYSICAL << ITE_ENTRY_INTTYPE_SHIFT); + + if (ignore_pInt) { + ite.itel |= (eventid << ITE_ENTRY_INTID_SHIFT); + } else { + ite.itel |= (pIntid << ITE_ENTRY_INTID_SHIFT); + } + ite.itel |= (int_spurious << ITE_ENTRY_INTSP_SHIFT); + ite.iteh = icid; + + result = update_ite(s, eventid, dte, ite); + } + + return result; +} + +static bool update_cte(GICv3ITSState *s, uint16_t icid, bool valid, + uint64_t rdbase) +{ + AddressSpace *as = &s->gicv3->dma_as; + uint64_t value; + uint64_t l2t_addr; + bool valid_l2t; + uint32_t l2t_id; + uint32_t max_l2_entries; + uint64_t cte = 0; + MemTxResult res = MEMTX_OK; + + if (!s->ct.valid) { + return true; + } + + if (valid) { + /* add mapping entry to collection table */ + cte = (valid & TABLE_ENTRY_VALID_MASK) | (rdbase << 1ULL); + } + + /* + * The specification defines the format of level 1 entries of a + * 2-level table, but the format of level 2 entries and the format + * of flat-mapped tables is IMPDEF. + */ + if (s->ct.indirect) { + l2t_id = icid / (s->ct.page_sz / L1TABLE_ENTRY_SIZE); + + value = address_space_ldq_le(as, + s->ct.base_addr + + (l2t_id * L1TABLE_ENTRY_SIZE), + MEMTXATTRS_UNSPECIFIED, &res); + + if (res != MEMTX_OK) { + return false; + } + + valid_l2t = (value & L2_TABLE_VALID_MASK) != 0; + + if (valid_l2t) { + max_l2_entries = s->ct.page_sz / s->ct.entry_sz; + + l2t_addr = value & ((1ULL << 51) - 1); + + address_space_stq_le(as, l2t_addr + + ((icid % max_l2_entries) * GITS_CTE_SIZE), + cte, MEMTXATTRS_UNSPECIFIED, &res); + } + } else { + /* Flat level table */ + address_space_stq_le(as, s->ct.base_addr + (icid * GITS_CTE_SIZE), + cte, MEMTXATTRS_UNSPECIFIED, &res); + } + if (res != MEMTX_OK) { + return false; + } else { + return true; + } +} + +static bool process_mapc(GICv3ITSState *s, uint32_t offset) +{ + AddressSpace *as = &s->gicv3->dma_as; + uint16_t icid; + uint64_t rdbase; + bool valid; + MemTxResult res = MEMTX_OK; + bool result = false; + uint64_t value; + + offset += NUM_BYTES_IN_DW; + offset += NUM_BYTES_IN_DW; + + value = address_space_ldq_le(as, s->cq.base_addr + offset, + MEMTXATTRS_UNSPECIFIED, &res); + + if (res != MEMTX_OK) { + return result; + } + + icid = value & ICID_MASK; + + rdbase = (value & R_MAPC_RDBASE_MASK) >> R_MAPC_RDBASE_SHIFT; + rdbase &= RDBASE_PROCNUM_MASK; + + valid = (value & CMD_FIELD_VALID_MASK); + + if ((icid > s->ct.maxids.max_collids) || (rdbase > s->gicv3->num_cpu)) { + qemu_log_mask(LOG_GUEST_ERROR, + "ITS MAPC: invalid collection table attributes " + "icid %d rdbase %" PRIu64 "\n", icid, rdbase); + /* + * in this implementation, in case of error + * we ignore this command and move onto the next + * command in the queue + */ + } else { + result = update_cte(s, icid, valid, rdbase); + } + + return result; +} + +static bool update_dte(GICv3ITSState *s, uint32_t devid, bool valid, + uint8_t size, uint64_t itt_addr) +{ + AddressSpace *as = &s->gicv3->dma_as; + uint64_t value; + uint64_t l2t_addr; + bool valid_l2t; + uint32_t l2t_id; + uint32_t max_l2_entries; + uint64_t dte = 0; + MemTxResult res = MEMTX_OK; + + if (s->dt.valid) { + if (valid) { + /* add mapping entry to device table */ + dte = (valid & TABLE_ENTRY_VALID_MASK) | + ((size & SIZE_MASK) << 1U) | + (itt_addr << GITS_DTE_ITTADDR_SHIFT); + } + } else { + return true; + } + + /* + * The specification defines the format of level 1 entries of a + * 2-level table, but the format of level 2 entries and the format + * of flat-mapped tables is IMPDEF. + */ + if (s->dt.indirect) { + l2t_id = devid / (s->dt.page_sz / L1TABLE_ENTRY_SIZE); + + value = address_space_ldq_le(as, + s->dt.base_addr + + (l2t_id * L1TABLE_ENTRY_SIZE), + MEMTXATTRS_UNSPECIFIED, &res); + + if (res != MEMTX_OK) { + return false; + } + + valid_l2t = (value & L2_TABLE_VALID_MASK) != 0; + + if (valid_l2t) { + max_l2_entries = s->dt.page_sz / s->dt.entry_sz; + + l2t_addr = value & ((1ULL << 51) - 1); + + address_space_stq_le(as, l2t_addr + + ((devid % max_l2_entries) * GITS_DTE_SIZE), + dte, MEMTXATTRS_UNSPECIFIED, &res); + } + } else { + /* Flat level table */ + address_space_stq_le(as, s->dt.base_addr + (devid * GITS_DTE_SIZE), + dte, MEMTXATTRS_UNSPECIFIED, &res); + } + if (res != MEMTX_OK) { + return false; + } else { + return true; + } +} + +static bool process_mapd(GICv3ITSState *s, uint64_t value, uint32_t offset) +{ + AddressSpace *as = &s->gicv3->dma_as; + uint32_t devid; + uint8_t size; + uint64_t itt_addr; + bool valid; + MemTxResult res = MEMTX_OK; + bool result = false; + + devid = ((value & DEVID_MASK) >> DEVID_SHIFT); + + offset += NUM_BYTES_IN_DW; + value = address_space_ldq_le(as, s->cq.base_addr + offset, + MEMTXATTRS_UNSPECIFIED, &res); + + if (res != MEMTX_OK) { + return result; + } + + size = (value & SIZE_MASK); + + offset += NUM_BYTES_IN_DW; + value = address_space_ldq_le(as, s->cq.base_addr + offset, + MEMTXATTRS_UNSPECIFIED, &res); + + if (res != MEMTX_OK) { + return result; + } + + itt_addr = (value & ITTADDR_MASK) >> ITTADDR_SHIFT; + + valid = (value & CMD_FIELD_VALID_MASK); + + if ((devid > s->dt.maxids.max_devids) || + (size > FIELD_EX64(s->typer, GITS_TYPER, IDBITS))) { + qemu_log_mask(LOG_GUEST_ERROR, + "ITS MAPD: invalid device table attributes " + "devid %d or size %d\n", devid, size); + /* + * in this implementation, in case of error + * we ignore this command and move onto the next + * command in the queue + */ + } else { + result = update_dte(s, devid, valid, size, itt_addr); + } + + return result; +} + +/* + * Current implementation blocks until all + * commands are processed + */ +static void process_cmdq(GICv3ITSState *s) +{ + uint32_t wr_offset = 0; + uint32_t rd_offset = 0; + uint32_t cq_offset = 0; + uint64_t data; + AddressSpace *as = &s->gicv3->dma_as; + MemTxResult res = MEMTX_OK; + bool result = true; + uint8_t cmd; + int i; + + if (!(s->ctlr & ITS_CTLR_ENABLED)) { + return; + } + + wr_offset = FIELD_EX64(s->cwriter, GITS_CWRITER, OFFSET); + + if (wr_offset > s->cq.max_entries) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid write offset " + "%d\n", __func__, wr_offset); + return; + } + + rd_offset = FIELD_EX64(s->creadr, GITS_CREADR, OFFSET); + + if (rd_offset > s->cq.max_entries) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid read offset " + "%d\n", __func__, rd_offset); + return; + } + + while (wr_offset != rd_offset) { + cq_offset = (rd_offset * GITS_CMDQ_ENTRY_SIZE); + data = address_space_ldq_le(as, s->cq.base_addr + cq_offset, + MEMTXATTRS_UNSPECIFIED, &res); + if (res != MEMTX_OK) { + result = false; + } + cmd = (data & CMD_MASK); + + switch (cmd) { + case GITS_CMD_INT: + res = process_its_cmd(s, data, cq_offset, INTERRUPT); + break; + case GITS_CMD_CLEAR: + res = process_its_cmd(s, data, cq_offset, CLEAR); + break; + case GITS_CMD_SYNC: + /* + * Current implementation makes a blocking synchronous call + * for every command issued earlier, hence the internal state + * is already consistent by the time SYNC command is executed. + * Hence no further processing is required for SYNC command. + */ + break; + case GITS_CMD_MAPD: + result = process_mapd(s, data, cq_offset); + break; + case GITS_CMD_MAPC: + result = process_mapc(s, cq_offset); + break; + case GITS_CMD_MAPTI: + result = process_mapti(s, data, cq_offset, false); + break; + case GITS_CMD_MAPI: + result = process_mapti(s, data, cq_offset, true); + break; + case GITS_CMD_DISCARD: + result = process_its_cmd(s, data, cq_offset, DISCARD); + break; + case GITS_CMD_INV: + case GITS_CMD_INVALL: + /* + * Current implementation doesn't cache any ITS tables, + * but the calculated lpi priority information. We only + * need to trigger lpi priority re-calculation to be in + * sync with LPI config table or pending table changes. + */ + for (i = 0; i < s->gicv3->num_cpu; i++) { + gicv3_redist_update_lpi(&s->gicv3->cpu[i]); + } + break; + default: + break; + } + if (result) { + rd_offset++; + rd_offset %= s->cq.max_entries; + s->creadr = FIELD_DP64(s->creadr, GITS_CREADR, OFFSET, rd_offset); + } else { + /* + * in this implementation, in case of dma read/write error + * we stall the command processing + */ + s->creadr = FIELD_DP64(s->creadr, GITS_CREADR, STALLED, 1); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %x cmd processing failed\n", __func__, cmd); + break; + } + } +} + +/* + * This function extracts the ITS Device and Collection table specific + * parameters (like base_addr, size etc) from GITS_BASER register. + * It is called during ITS enable and also during post_load migration + */ +static void extract_table_params(GICv3ITSState *s) +{ + uint16_t num_pages = 0; + uint8_t page_sz_type; + uint8_t type; + uint32_t page_sz = 0; + uint64_t value; + + for (int i = 0; i < 8; i++) { + value = s->baser[i]; + + if (!value) { + continue; + } + + page_sz_type = FIELD_EX64(value, GITS_BASER, PAGESIZE); + + switch (page_sz_type) { + case 0: + page_sz = GITS_PAGE_SIZE_4K; + break; + + case 1: + page_sz = GITS_PAGE_SIZE_16K; + break; + + case 2: + case 3: + page_sz = GITS_PAGE_SIZE_64K; + break; + + default: + g_assert_not_reached(); + } + + num_pages = FIELD_EX64(value, GITS_BASER, SIZE) + 1; + + type = FIELD_EX64(value, GITS_BASER, TYPE); + + switch (type) { + + case GITS_BASER_TYPE_DEVICE: + memset(&s->dt, 0 , sizeof(s->dt)); + s->dt.valid = FIELD_EX64(value, GITS_BASER, VALID); + + if (!s->dt.valid) { + return; + } + + s->dt.page_sz = page_sz; + s->dt.indirect = FIELD_EX64(value, GITS_BASER, INDIRECT); + s->dt.entry_sz = FIELD_EX64(value, GITS_BASER, ENTRYSIZE); + + if (!s->dt.indirect) { + s->dt.max_entries = (num_pages * page_sz) / s->dt.entry_sz; + } else { + s->dt.max_entries = (((num_pages * page_sz) / + L1TABLE_ENTRY_SIZE) * + (page_sz / s->dt.entry_sz)); + } + + s->dt.maxids.max_devids = (1UL << (FIELD_EX64(s->typer, GITS_TYPER, + DEVBITS) + 1)); + + s->dt.base_addr = baser_base_addr(value, page_sz); + + break; + + case GITS_BASER_TYPE_COLLECTION: + memset(&s->ct, 0 , sizeof(s->ct)); + s->ct.valid = FIELD_EX64(value, GITS_BASER, VALID); + + /* + * GITS_TYPER.HCC is 0 for this implementation + * hence writes are discarded if ct.valid is 0 + */ + if (!s->ct.valid) { + return; + } + + s->ct.page_sz = page_sz; + s->ct.indirect = FIELD_EX64(value, GITS_BASER, INDIRECT); + s->ct.entry_sz = FIELD_EX64(value, GITS_BASER, ENTRYSIZE); + + if (!s->ct.indirect) { + s->ct.max_entries = (num_pages * page_sz) / s->ct.entry_sz; + } else { + s->ct.max_entries = (((num_pages * page_sz) / + L1TABLE_ENTRY_SIZE) * + (page_sz / s->ct.entry_sz)); + } + + if (FIELD_EX64(s->typer, GITS_TYPER, CIL)) { + s->ct.maxids.max_collids = (1UL << (FIELD_EX64(s->typer, + GITS_TYPER, CIDBITS) + 1)); + } else { + /* 16-bit CollectionId supported when CIL == 0 */ + s->ct.maxids.max_collids = (1UL << 16); + } + + s->ct.base_addr = baser_base_addr(value, page_sz); + + break; + + default: + break; + } + } +} + +static void extract_cmdq_params(GICv3ITSState *s) +{ + uint16_t num_pages = 0; + uint64_t value = s->cbaser; + + num_pages = FIELD_EX64(value, GITS_CBASER, SIZE) + 1; + + memset(&s->cq, 0 , sizeof(s->cq)); + s->cq.valid = FIELD_EX64(value, GITS_CBASER, VALID); + + if (s->cq.valid) { + s->cq.max_entries = (num_pages * GITS_PAGE_SIZE_4K) / + GITS_CMDQ_ENTRY_SIZE; + s->cq.base_addr = FIELD_EX64(value, GITS_CBASER, PHYADDR); + s->cq.base_addr <<= R_GITS_CBASER_PHYADDR_SHIFT; + } +} + +static MemTxResult gicv3_its_translation_write(void *opaque, hwaddr offset, + uint64_t data, unsigned size, + MemTxAttrs attrs) +{ + GICv3ITSState *s = (GICv3ITSState *)opaque; + bool result = true; + uint32_t devid = 0; + + switch (offset) { + case GITS_TRANSLATER: + if (s->ctlr & ITS_CTLR_ENABLED) { + devid = attrs.requester_id; + result = process_its_cmd(s, data, devid, NONE); + } + break; + default: + break; + } + + if (result) { + return MEMTX_OK; + } else { + return MEMTX_ERROR; + } +} + +static bool its_writel(GICv3ITSState *s, hwaddr offset, + uint64_t value, MemTxAttrs attrs) +{ + bool result = true; + int index; + + switch (offset) { + case GITS_CTLR: + s->ctlr |= (value & ~(s->ctlr)); + + if (s->ctlr & ITS_CTLR_ENABLED) { + extract_table_params(s); + extract_cmdq_params(s); + s->creadr = 0; + process_cmdq(s); + } + break; + case GITS_CBASER: + /* + * IMPDEF choice:- GITS_CBASER register becomes RO if ITS is + * already enabled + */ + if (!(s->ctlr & ITS_CTLR_ENABLED)) { + s->cbaser = deposit64(s->cbaser, 0, 32, value); + s->creadr = 0; + s->cwriter = s->creadr; + } + break; + case GITS_CBASER + 4: + /* + * IMPDEF choice:- GITS_CBASER register becomes RO if ITS is + * already enabled + */ + if (!(s->ctlr & ITS_CTLR_ENABLED)) { + s->cbaser = deposit64(s->cbaser, 32, 32, value); + s->creadr = 0; + s->cwriter = s->creadr; + } + break; + case GITS_CWRITER: + s->cwriter = deposit64(s->cwriter, 0, 32, + (value & ~R_GITS_CWRITER_RETRY_MASK)); + if (s->cwriter != s->creadr) { + process_cmdq(s); + } + break; + case GITS_CWRITER + 4: + s->cwriter = deposit64(s->cwriter, 32, 32, value); + break; + case GITS_CREADR: + if (s->gicv3->gicd_ctlr & GICD_CTLR_DS) { + s->creadr = deposit64(s->creadr, 0, 32, + (value & ~R_GITS_CREADR_STALLED_MASK)); + } else { + /* RO register, ignore the write */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid guest write to RO register at offset " + TARGET_FMT_plx "\n", __func__, offset); + } + break; + case GITS_CREADR + 4: + if (s->gicv3->gicd_ctlr & GICD_CTLR_DS) { + s->creadr = deposit64(s->creadr, 32, 32, value); + } else { + /* RO register, ignore the write */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid guest write to RO register at offset " + TARGET_FMT_plx "\n", __func__, offset); + } + break; + case GITS_BASER ... GITS_BASER + 0x3f: + /* + * IMPDEF choice:- GITS_BASERn register becomes RO if ITS is + * already enabled + */ + if (!(s->ctlr & ITS_CTLR_ENABLED)) { + index = (offset - GITS_BASER) / 8; + + if (offset & 7) { + value <<= 32; + value &= ~GITS_BASER_RO_MASK; + s->baser[index] &= GITS_BASER_RO_MASK | MAKE_64BIT_MASK(0, 32); + s->baser[index] |= value; + } else { + value &= ~GITS_BASER_RO_MASK; + s->baser[index] &= GITS_BASER_RO_MASK | MAKE_64BIT_MASK(32, 32); + s->baser[index] |= value; + } + } + break; + case GITS_IIDR: + case GITS_IDREGS ... GITS_IDREGS + 0x2f: + /* RO registers, ignore the write */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid guest write to RO register at offset " + TARGET_FMT_plx "\n", __func__, offset); + break; + default: + result = false; + break; + } + return result; +} + +static bool its_readl(GICv3ITSState *s, hwaddr offset, + uint64_t *data, MemTxAttrs attrs) +{ + bool result = true; + int index; + + switch (offset) { + case GITS_CTLR: + *data = s->ctlr; + break; + case GITS_IIDR: + *data = gicv3_iidr(); + break; + case GITS_IDREGS ... GITS_IDREGS + 0x2f: + /* ID registers */ + *data = gicv3_idreg(offset - GITS_IDREGS); + break; + case GITS_TYPER: + *data = extract64(s->typer, 0, 32); + break; + case GITS_TYPER + 4: + *data = extract64(s->typer, 32, 32); + break; + case GITS_CBASER: + *data = extract64(s->cbaser, 0, 32); + break; + case GITS_CBASER + 4: + *data = extract64(s->cbaser, 32, 32); + break; + case GITS_CREADR: + *data = extract64(s->creadr, 0, 32); + break; + case GITS_CREADR + 4: + *data = extract64(s->creadr, 32, 32); + break; + case GITS_CWRITER: + *data = extract64(s->cwriter, 0, 32); + break; + case GITS_CWRITER + 4: + *data = extract64(s->cwriter, 32, 32); + break; + case GITS_BASER ... GITS_BASER + 0x3f: + index = (offset - GITS_BASER) / 8; + if (offset & 7) { + *data = extract64(s->baser[index], 32, 32); + } else { + *data = extract64(s->baser[index], 0, 32); + } + break; + default: + result = false; + break; + } + return result; +} + +static bool its_writell(GICv3ITSState *s, hwaddr offset, + uint64_t value, MemTxAttrs attrs) +{ + bool result = true; + int index; + + switch (offset) { + case GITS_BASER ... GITS_BASER + 0x3f: + /* + * IMPDEF choice:- GITS_BASERn register becomes RO if ITS is + * already enabled + */ + if (!(s->ctlr & ITS_CTLR_ENABLED)) { + index = (offset - GITS_BASER) / 8; + s->baser[index] &= GITS_BASER_RO_MASK; + s->baser[index] |= (value & ~GITS_BASER_RO_MASK); + } + break; + case GITS_CBASER: + /* + * IMPDEF choice:- GITS_CBASER register becomes RO if ITS is + * already enabled + */ + if (!(s->ctlr & ITS_CTLR_ENABLED)) { + s->cbaser = value; + s->creadr = 0; + s->cwriter = s->creadr; + } + break; + case GITS_CWRITER: + s->cwriter = value & ~R_GITS_CWRITER_RETRY_MASK; + if (s->cwriter != s->creadr) { + process_cmdq(s); + } + break; + case GITS_CREADR: + if (s->gicv3->gicd_ctlr & GICD_CTLR_DS) { + s->creadr = value & ~R_GITS_CREADR_STALLED_MASK; + } else { + /* RO register, ignore the write */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid guest write to RO register at offset " + TARGET_FMT_plx "\n", __func__, offset); + } + break; + case GITS_TYPER: + /* RO registers, ignore the write */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid guest write to RO register at offset " + TARGET_FMT_plx "\n", __func__, offset); + break; + default: + result = false; + break; + } + return result; +} + +static bool its_readll(GICv3ITSState *s, hwaddr offset, + uint64_t *data, MemTxAttrs attrs) +{ + bool result = true; + int index; + + switch (offset) { + case GITS_TYPER: + *data = s->typer; + break; + case GITS_BASER ... GITS_BASER + 0x3f: + index = (offset - GITS_BASER) / 8; + *data = s->baser[index]; + break; + case GITS_CBASER: + *data = s->cbaser; + break; + case GITS_CREADR: + *data = s->creadr; + break; + case GITS_CWRITER: + *data = s->cwriter; + break; + default: + result = false; + break; + } + return result; +} + +static MemTxResult gicv3_its_read(void *opaque, hwaddr offset, uint64_t *data, + unsigned size, MemTxAttrs attrs) +{ + GICv3ITSState *s = (GICv3ITSState *)opaque; + bool result; + + switch (size) { + case 4: + result = its_readl(s, offset, data, attrs); + break; + case 8: + result = its_readll(s, offset, data, attrs); + break; + default: + result = false; + break; + } + + if (!result) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid guest read at offset " TARGET_FMT_plx + "size %u\n", __func__, offset, size); + /* + * The spec requires that reserved registers are RAZ/WI; + * so use false returns from leaf functions as a way to + * trigger the guest-error logging but don't return it to + * the caller, or we'll cause a spurious guest data abort. + */ + *data = 0; + } + return MEMTX_OK; +} + +static MemTxResult gicv3_its_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size, MemTxAttrs attrs) +{ + GICv3ITSState *s = (GICv3ITSState *)opaque; + bool result; + + switch (size) { + case 4: + result = its_writel(s, offset, data, attrs); + break; + case 8: + result = its_writell(s, offset, data, attrs); + break; + default: + result = false; + break; + } + + if (!result) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid guest write at offset " TARGET_FMT_plx + "size %u\n", __func__, offset, size); + /* + * The spec requires that reserved registers are RAZ/WI; + * so use false returns from leaf functions as a way to + * trigger the guest-error logging but don't return it to + * the caller, or we'll cause a spurious guest data abort. + */ + } + return MEMTX_OK; +} + +static const MemoryRegionOps gicv3_its_control_ops = { + .read_with_attrs = gicv3_its_read, + .write_with_attrs = gicv3_its_write, + .valid.min_access_size = 4, + .valid.max_access_size = 8, + .impl.min_access_size = 4, + .impl.max_access_size = 8, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const MemoryRegionOps gicv3_its_translation_ops = { + .write_with_attrs = gicv3_its_translation_write, + .valid.min_access_size = 2, + .valid.max_access_size = 4, + .impl.min_access_size = 2, + .impl.max_access_size = 4, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void gicv3_arm_its_realize(DeviceState *dev, Error **errp) +{ + GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev); + int i; + + for (i = 0; i < s->gicv3->num_cpu; i++) { + if (!(s->gicv3->cpu[i].gicr_typer & GICR_TYPER_PLPIS)) { + error_setg(errp, "Physical LPI not supported by CPU %d", i); + return; + } + } + + gicv3_its_init_mmio(s, &gicv3_its_control_ops, &gicv3_its_translation_ops); + + address_space_init(&s->gicv3->dma_as, s->gicv3->dma, + "gicv3-its-sysmem"); + + /* set the ITS default features supported */ + s->typer = FIELD_DP64(s->typer, GITS_TYPER, PHYSICAL, + GITS_TYPE_PHYSICAL); + s->typer = FIELD_DP64(s->typer, GITS_TYPER, ITT_ENTRY_SIZE, + ITS_ITT_ENTRY_SIZE - 1); + s->typer = FIELD_DP64(s->typer, GITS_TYPER, IDBITS, ITS_IDBITS); + s->typer = FIELD_DP64(s->typer, GITS_TYPER, DEVBITS, ITS_DEVBITS); + s->typer = FIELD_DP64(s->typer, GITS_TYPER, CIL, 1); + s->typer = FIELD_DP64(s->typer, GITS_TYPER, CIDBITS, ITS_CIDBITS); +} + +static void gicv3_its_reset(DeviceState *dev) +{ + GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev); + GICv3ITSClass *c = ARM_GICV3_ITS_GET_CLASS(s); + + c->parent_reset(dev); + + /* Quiescent bit reset to 1 */ + s->ctlr = FIELD_DP32(s->ctlr, GITS_CTLR, QUIESCENT, 1); + + /* + * setting GITS_BASER0.Type = 0b001 (Device) + * GITS_BASER1.Type = 0b100 (Collection Table) + * GITS_BASER.Type,where n = 3 to 7 are 0b00 (Unimplemented) + * GITS_BASER<0,1>.Page_Size = 64KB + * and default translation table entry size to 16 bytes + */ + s->baser[0] = FIELD_DP64(s->baser[0], GITS_BASER, TYPE, + GITS_BASER_TYPE_DEVICE); + s->baser[0] = FIELD_DP64(s->baser[0], GITS_BASER, PAGESIZE, + GITS_BASER_PAGESIZE_64K); + s->baser[0] = FIELD_DP64(s->baser[0], GITS_BASER, ENTRYSIZE, + GITS_DTE_SIZE - 1); + + s->baser[1] = FIELD_DP64(s->baser[1], GITS_BASER, TYPE, + GITS_BASER_TYPE_COLLECTION); + s->baser[1] = FIELD_DP64(s->baser[1], GITS_BASER, PAGESIZE, + GITS_BASER_PAGESIZE_64K); + s->baser[1] = FIELD_DP64(s->baser[1], GITS_BASER, ENTRYSIZE, + GITS_CTE_SIZE - 1); +} + +static void gicv3_its_post_load(GICv3ITSState *s) +{ + if (s->ctlr & ITS_CTLR_ENABLED) { + extract_table_params(s); + extract_cmdq_params(s); + } +} + +static Property gicv3_its_props[] = { + DEFINE_PROP_LINK("parent-gicv3", GICv3ITSState, gicv3, "arm-gicv3", + GICv3State *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void gicv3_its_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + GICv3ITSClass *ic = ARM_GICV3_ITS_CLASS(klass); + GICv3ITSCommonClass *icc = ARM_GICV3_ITS_COMMON_CLASS(klass); + + dc->realize = gicv3_arm_its_realize; + device_class_set_props(dc, gicv3_its_props); + device_class_set_parent_reset(dc, gicv3_its_reset, &ic->parent_reset); + icc->post_load = gicv3_its_post_load; +} + +static const TypeInfo gicv3_its_info = { + .name = TYPE_ARM_GICV3_ITS, + .parent = TYPE_ARM_GICV3_ITS_COMMON, + .instance_size = sizeof(GICv3ITSState), + .class_init = gicv3_its_class_init, + .class_size = sizeof(GICv3ITSClass), +}; + +static void gicv3_its_register_types(void) +{ + type_register_static(&gicv3_its_info); +} + +type_init(gicv3_its_register_types) diff --git a/hw/intc/arm_gicv3_its_common.c b/hw/intc/arm_gicv3_its_common.c index 66c4c6a188..7d7f3882e7 100644 --- a/hw/intc/arm_gicv3_its_common.c +++ b/hw/intc/arm_gicv3_its_common.c @@ -50,6 +50,8 @@ static int gicv3_its_post_load(void *opaque, int version_id) static const VMStateDescription vmstate_its = { .name = "arm_gicv3_its", + .version_id = 1, + .minimum_version_id = 1, .pre_save = gicv3_its_pre_save, .post_load = gicv3_its_post_load, .priority = MIG_PRI_GICV3_ITS, @@ -99,14 +101,15 @@ static const MemoryRegionOps gicv3_its_trans_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -void gicv3_its_init_mmio(GICv3ITSState *s, const MemoryRegionOps *ops) +void gicv3_its_init_mmio(GICv3ITSState *s, const MemoryRegionOps *ops, + const MemoryRegionOps *tops) { SysBusDevice *sbd = SYS_BUS_DEVICE(s); memory_region_init_io(&s->iomem_its_cntrl, OBJECT(s), ops, s, "control", ITS_CONTROL_SIZE); memory_region_init_io(&s->iomem_its_translation, OBJECT(s), - &gicv3_its_trans_ops, s, + tops ? tops : &gicv3_its_trans_ops, s, "translation", ITS_TRANS_SIZE); /* Our two regions are always adjacent, therefore we now combine them diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c index b554d2ede0..0b4cbed28b 100644 --- a/hw/intc/arm_gicv3_its_kvm.c +++ b/hw/intc/arm_gicv3_its_kvm.c @@ -106,7 +106,7 @@ static void kvm_arm_its_realize(DeviceState *dev, Error **errp) kvm_arm_register_device(&s->iomem_its_cntrl, -1, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_ITS_ADDR_TYPE, s->dev_fd, 0); - gicv3_its_init_mmio(s, NULL); + gicv3_its_init_mmio(s, NULL, NULL); if (!kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS, GITS_CTLR)) { diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c index 53da703ed8..7072bfcbb1 100644 --- a/hw/intc/arm_gicv3_redist.c +++ b/hw/intc/arm_gicv3_redist.c @@ -248,10 +248,19 @@ static MemTxResult gicr_writel(GICv3CPUState *cs, hwaddr offset, case GICR_CTLR: /* For our implementation, GICR_TYPER.DPGS is 0 and so all * the DPG bits are RAZ/WI. We don't do anything asynchronously, - * so UWP and RWP are RAZ/WI. And GICR_TYPER.LPIS is 0 (we don't - * implement LPIs) so Enable_LPIs is RES0. So there are no writable - * bits for us. + * so UWP and RWP are RAZ/WI. GICR_TYPER.LPIS is 1 (we + * implement LPIs) so Enable_LPIs is programmable. */ + if (cs->gicr_typer & GICR_TYPER_PLPIS) { + if (value & GICR_CTLR_ENABLE_LPIS) { + cs->gicr_ctlr |= GICR_CTLR_ENABLE_LPIS; + /* Check for any pending interr in pending table */ + gicv3_redist_update_lpi(cs); + gicv3_redist_update(cs); + } else { + cs->gicr_ctlr &= ~GICR_CTLR_ENABLE_LPIS; + } + } return MEMTX_OK; case GICR_STATUSR: /* RAZ/WI for our implementation */ @@ -526,6 +535,144 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data, return r; } +static void gicv3_redist_check_lpi_priority(GICv3CPUState *cs, int irq) +{ + AddressSpace *as = &cs->gic->dma_as; + uint64_t lpict_baddr; + uint8_t lpite; + uint8_t prio; + + lpict_baddr = cs->gicr_propbaser & R_GICR_PROPBASER_PHYADDR_MASK; + + address_space_read(as, lpict_baddr + ((irq - GICV3_LPI_INTID_START) * + sizeof(lpite)), MEMTXATTRS_UNSPECIFIED, &lpite, + sizeof(lpite)); + + if (!(lpite & LPI_CTE_ENABLED)) { + return; + } + + if (cs->gic->gicd_ctlr & GICD_CTLR_DS) { + prio = lpite & LPI_PRIORITY_MASK; + } else { + prio = ((lpite & LPI_PRIORITY_MASK) >> 1) | 0x80; + } + + if ((prio < cs->hpplpi.prio) || + ((prio == cs->hpplpi.prio) && (irq <= cs->hpplpi.irq))) { + cs->hpplpi.irq = irq; + cs->hpplpi.prio = prio; + /* LPIs are always non-secure Grp1 interrupts */ + cs->hpplpi.grp = GICV3_G1NS; + } +} + +void gicv3_redist_update_lpi(GICv3CPUState *cs) +{ + /* + * This function scans the LPI pending table and for each pending + * LPI, reads the corresponding entry from LPI configuration table + * to extract the priority info and determine if the current LPI + * priority is lower than the last computed high priority lpi interrupt. + * If yes, replace current LPI as the new high priority lpi interrupt. + */ + AddressSpace *as = &cs->gic->dma_as; + uint64_t lpipt_baddr; + uint32_t pendt_size = 0; + uint8_t pend; + int i, bit; + uint64_t idbits; + + idbits = MIN(FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER, IDBITS), + GICD_TYPER_IDBITS); + + if (!(cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || !cs->gicr_propbaser || + !cs->gicr_pendbaser) { + return; + } + + cs->hpplpi.prio = 0xff; + + lpipt_baddr = cs->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK; + + /* Determine the highest priority pending interrupt among LPIs */ + pendt_size = (1ULL << (idbits + 1)); + + for (i = GICV3_LPI_INTID_START / 8; i < pendt_size / 8; i++) { + address_space_read(as, lpipt_baddr + i, MEMTXATTRS_UNSPECIFIED, &pend, + sizeof(pend)); + + while (pend) { + bit = ctz32(pend); + gicv3_redist_check_lpi_priority(cs, i * 8 + bit); + pend &= ~(1 << bit); + } + } +} + +void gicv3_redist_lpi_pending(GICv3CPUState *cs, int irq, int level) +{ + /* + * This function updates the pending bit in lpi pending table for + * the irq being activated or deactivated. + */ + AddressSpace *as = &cs->gic->dma_as; + uint64_t lpipt_baddr; + bool ispend = false; + uint8_t pend; + + /* + * get the bit value corresponding to this irq in the + * lpi pending table + */ + lpipt_baddr = cs->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK; + + address_space_read(as, lpipt_baddr + ((irq / 8) * sizeof(pend)), + MEMTXATTRS_UNSPECIFIED, &pend, sizeof(pend)); + + ispend = extract32(pend, irq % 8, 1); + + /* no change in the value of pending bit, return */ + if (ispend == level) { + return; + } + pend = deposit32(pend, irq % 8, 1, level ? 1 : 0); + + address_space_write(as, lpipt_baddr + ((irq / 8) * sizeof(pend)), + MEMTXATTRS_UNSPECIFIED, &pend, sizeof(pend)); + + /* + * check if this LPI is better than the current hpplpi, if yes + * just set hpplpi.prio and .irq without doing a full rescan + */ + if (level) { + gicv3_redist_check_lpi_priority(cs, irq); + } else { + if (irq == cs->hpplpi.irq) { + gicv3_redist_update_lpi(cs); + } + } +} + +void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level) +{ + uint64_t idbits; + + idbits = MIN(FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER, IDBITS), + GICD_TYPER_IDBITS); + + if (!(cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || !cs->gicr_propbaser || + !cs->gicr_pendbaser || (irq > (1ULL << (idbits + 1)) - 1) || + irq < GICV3_LPI_INTID_START) { + return; + } + + /* set/clear the pending bit for this irq */ + gicv3_redist_lpi_pending(cs, irq, level); + + gicv3_redist_update(cs); +} + void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level) { /* Update redistributor state for a change in an external PPI input line */ diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h index 05303a55c8..a0369dace7 100644 --- a/hw/intc/gicv3_internal.h +++ b/hw/intc/gicv3_internal.h @@ -24,6 +24,7 @@ #ifndef QEMU_ARM_GICV3_INTERNAL_H #define QEMU_ARM_GICV3_INTERNAL_H +#include "hw/registerfields.h" #include "hw/intc/arm_gicv3_common.h" /* Distributor registers, as offsets from the distributor base address */ @@ -67,6 +68,11 @@ #define GICD_CTLR_E1NWF (1U << 7) #define GICD_CTLR_RWP (1U << 31) +#define GICD_TYPER_LPIS_SHIFT 17 + +/* 16 bits EventId */ +#define GICD_TYPER_IDBITS 0xf + /* * Redistributor frame offsets from RD_base */ @@ -122,17 +128,19 @@ #define GICR_WAKER_ProcessorSleep (1U << 1) #define GICR_WAKER_ChildrenAsleep (1U << 2) -#define GICR_PROPBASER_OUTER_CACHEABILITY_MASK (7ULL << 56) -#define GICR_PROPBASER_ADDR_MASK (0xfffffffffULL << 12) -#define GICR_PROPBASER_SHAREABILITY_MASK (3U << 10) -#define GICR_PROPBASER_CACHEABILITY_MASK (7U << 7) -#define GICR_PROPBASER_IDBITS_MASK (0x1f) +FIELD(GICR_PROPBASER, IDBITS, 0, 5) +FIELD(GICR_PROPBASER, INNERCACHE, 7, 3) +FIELD(GICR_PROPBASER, SHAREABILITY, 10, 2) +FIELD(GICR_PROPBASER, PHYADDR, 12, 40) +FIELD(GICR_PROPBASER, OUTERCACHE, 56, 3) -#define GICR_PENDBASER_PTZ (1ULL << 62) -#define GICR_PENDBASER_OUTER_CACHEABILITY_MASK (7ULL << 56) -#define GICR_PENDBASER_ADDR_MASK (0xffffffffULL << 16) -#define GICR_PENDBASER_SHAREABILITY_MASK (3U << 10) -#define GICR_PENDBASER_CACHEABILITY_MASK (7U << 7) +FIELD(GICR_PENDBASER, INNERCACHE, 7, 3) +FIELD(GICR_PENDBASER, SHAREABILITY, 10, 2) +FIELD(GICR_PENDBASER, PHYADDR, 16, 36) +FIELD(GICR_PENDBASER, OUTERCACHE, 56, 3) +FIELD(GICR_PENDBASER, PTZ, 62, 1) + +#define GICR_PROPBASER_IDBITS_THRESHOLD 0xd #define ICC_CTLR_EL1_CBPR (1U << 0) #define ICC_CTLR_EL1_EOIMODE (1U << 1) @@ -239,6 +247,163 @@ #define ICH_VTR_EL2_PREBITS_SHIFT 26 #define ICH_VTR_EL2_PRIBITS_SHIFT 29 +/* ITS Registers */ + +FIELD(GITS_BASER, SIZE, 0, 8) +FIELD(GITS_BASER, PAGESIZE, 8, 2) +FIELD(GITS_BASER, SHAREABILITY, 10, 2) +FIELD(GITS_BASER, PHYADDR, 12, 36) +FIELD(GITS_BASER, PHYADDRL_64K, 16, 32) +FIELD(GITS_BASER, PHYADDRH_64K, 12, 4) +FIELD(GITS_BASER, ENTRYSIZE, 48, 5) +FIELD(GITS_BASER, OUTERCACHE, 53, 3) +FIELD(GITS_BASER, TYPE, 56, 3) +FIELD(GITS_BASER, INNERCACHE, 59, 3) +FIELD(GITS_BASER, INDIRECT, 62, 1) +FIELD(GITS_BASER, VALID, 63, 1) + +FIELD(GITS_CBASER, SIZE, 0, 8) +FIELD(GITS_CBASER, SHAREABILITY, 10, 2) +FIELD(GITS_CBASER, PHYADDR, 12, 40) +FIELD(GITS_CBASER, OUTERCACHE, 53, 3) +FIELD(GITS_CBASER, INNERCACHE, 59, 3) +FIELD(GITS_CBASER, VALID, 63, 1) + +FIELD(GITS_CREADR, STALLED, 0, 1) +FIELD(GITS_CREADR, OFFSET, 5, 15) + +FIELD(GITS_CWRITER, RETRY, 0, 1) +FIELD(GITS_CWRITER, OFFSET, 5, 15) + +FIELD(GITS_CTLR, ENABLED, 0, 1) +FIELD(GITS_CTLR, QUIESCENT, 31, 1) + +FIELD(GITS_TYPER, PHYSICAL, 0, 1) +FIELD(GITS_TYPER, ITT_ENTRY_SIZE, 4, 4) +FIELD(GITS_TYPER, IDBITS, 8, 5) +FIELD(GITS_TYPER, DEVBITS, 13, 5) +FIELD(GITS_TYPER, SEIS, 18, 1) +FIELD(GITS_TYPER, PTA, 19, 1) +FIELD(GITS_TYPER, CIDBITS, 32, 4) +FIELD(GITS_TYPER, CIL, 36, 1) + +#define GITS_IDREGS 0xFFD0 + +#define ITS_CTLR_ENABLED (1U) /* ITS Enabled */ + +#define GITS_BASER_RO_MASK (R_GITS_BASER_ENTRYSIZE_MASK | \ + R_GITS_BASER_TYPE_MASK) + +#define GITS_BASER_PAGESIZE_4K 0 +#define GITS_BASER_PAGESIZE_16K 1 +#define GITS_BASER_PAGESIZE_64K 2 + +#define GITS_BASER_TYPE_DEVICE 1ULL +#define GITS_BASER_TYPE_COLLECTION 4ULL + +#define GITS_PAGE_SIZE_4K 0x1000 +#define GITS_PAGE_SIZE_16K 0x4000 +#define GITS_PAGE_SIZE_64K 0x10000 + +#define L1TABLE_ENTRY_SIZE 8 + +#define LPI_CTE_ENABLED TABLE_ENTRY_VALID_MASK +#define LPI_PRIORITY_MASK 0xfc + +#define GITS_CMDQ_ENTRY_SIZE 32 +#define NUM_BYTES_IN_DW 8 + +#define CMD_MASK 0xff + +/* ITS Commands */ +#define GITS_CMD_CLEAR 0x04 +#define GITS_CMD_DISCARD 0x0F +#define GITS_CMD_INT 0x03 +#define GITS_CMD_MAPC 0x09 +#define GITS_CMD_MAPD 0x08 +#define GITS_CMD_MAPI 0x0B +#define GITS_CMD_MAPTI 0x0A +#define GITS_CMD_INV 0x0C +#define GITS_CMD_INVALL 0x0D +#define GITS_CMD_SYNC 0x05 + +/* MAPC command fields */ +#define ICID_LENGTH 16 +#define ICID_MASK ((1U << ICID_LENGTH) - 1) +FIELD(MAPC, RDBASE, 16, 32) + +#define RDBASE_PROCNUM_LENGTH 16 +#define RDBASE_PROCNUM_MASK ((1ULL << RDBASE_PROCNUM_LENGTH) - 1) + +/* MAPD command fields */ +#define ITTADDR_LENGTH 44 +#define ITTADDR_SHIFT 8 +#define ITTADDR_MASK MAKE_64BIT_MASK(ITTADDR_SHIFT, ITTADDR_LENGTH) +#define SIZE_MASK 0x1f + +/* MAPI command fields */ +#define EVENTID_MASK ((1ULL << 32) - 1) + +/* MAPTI command fields */ +#define pINTID_SHIFT 32 +#define pINTID_MASK MAKE_64BIT_MASK(32, 32) + +#define DEVID_SHIFT 32 +#define DEVID_MASK MAKE_64BIT_MASK(32, 32) + +#define VALID_SHIFT 63 +#define CMD_FIELD_VALID_MASK (1ULL << VALID_SHIFT) +#define L2_TABLE_VALID_MASK CMD_FIELD_VALID_MASK +#define TABLE_ENTRY_VALID_MASK (1ULL << 0) + +/** + * Default features advertised by this version of ITS + */ +/* Physical LPIs supported */ +#define GITS_TYPE_PHYSICAL (1U << 0) + +/* + * 12 bytes Interrupt translation Table Entry size + * as per Table 5.3 in GICv3 spec + * ITE Lower 8 Bytes + * Bits: | 49 ... 26 | 25 ... 2 | 1 | 0 | + * Values: | 1023 | IntNum | IntType | Valid | + * ITE Higher 4 Bytes + * Bits: | 31 ... 16 | 15 ...0 | + * Values: | vPEID | ICID | + */ +#define ITS_ITT_ENTRY_SIZE 0xC +#define ITE_ENTRY_INTTYPE_SHIFT 1 +#define ITE_ENTRY_INTID_SHIFT 2 +#define ITE_ENTRY_INTID_MASK MAKE_64BIT_MASK(2, 24) +#define ITE_ENTRY_INTSP_SHIFT 26 +#define ITE_ENTRY_ICID_MASK MAKE_64BIT_MASK(0, 16) + +/* 16 bits EventId */ +#define ITS_IDBITS GICD_TYPER_IDBITS + +/* 16 bits DeviceId */ +#define ITS_DEVBITS 0xF + +/* 16 bits CollectionId */ +#define ITS_CIDBITS 0xF + +/* + * 8 bytes Device Table Entry size + * Valid = 1 bit,ITTAddr = 44 bits,Size = 5 bits + */ +#define GITS_DTE_SIZE (0x8ULL) +#define GITS_DTE_ITTADDR_SHIFT 6 +#define GITS_DTE_ITTADDR_MASK MAKE_64BIT_MASK(GITS_DTE_ITTADDR_SHIFT, \ + ITTADDR_LENGTH) + +/* + * 8 bytes Collection Table Entry size + * Valid = 1 bit,RDBase = 36 bits(considering max RDBASE) + */ +#define GITS_CTE_SIZE (0x8ULL) +#define GITS_CTE_RDBASE_PROCNUM_MASK MAKE_64BIT_MASK(1, RDBASE_PROCNUM_LENGTH) + /* Special interrupt IDs */ #define INTID_SECURE 1020 #define INTID_NONSECURE 1021 @@ -296,6 +461,9 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data, unsigned size, MemTxAttrs attrs); void gicv3_dist_set_irq(GICv3State *s, int irq, int level); void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level); +void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level); +void gicv3_redist_lpi_pending(GICv3CPUState *cs, int irq, int level); +void gicv3_redist_update_lpi(GICv3CPUState *cs); void gicv3_redist_send_sgi(GICv3CPUState *cs, int grp, int irq, bool ns); void gicv3_init_cpuif(GICv3State *s); diff --git a/hw/intc/meson.build b/hw/intc/meson.build index 6e52a166e3..4dcfea6aa8 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -8,6 +8,7 @@ softmmu_ss.add(when: 'CONFIG_ARM_GIC', if_true: files( 'arm_gicv3_dist.c', 'arm_gicv3_its_common.c', 'arm_gicv3_redist.c', + 'arm_gicv3_its.c', )) softmmu_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_pic.c')) softmmu_ss.add(when: 'CONFIG_HEATHROW_PIC', if_true: files('heathrow_pic.c')) diff --git a/hw/misc/zynq_slcr.c b/hw/misc/zynq_slcr.c index 5086e6b7ed..8b70285961 100644 --- a/hw/misc/zynq_slcr.c +++ b/hw/misc/zynq_slcr.c @@ -269,6 +269,21 @@ static uint64_t zynq_slcr_compute_clock(const uint64_t periods[], zynq_slcr_compute_clock((plls), (state)->regs[reg], \ reg ## _ ## enable_field ## _SHIFT) +static void zynq_slcr_compute_clocks_internal(ZynqSLCRState *s, uint64_t ps_clk) +{ + uint64_t io_pll = zynq_slcr_compute_pll(ps_clk, s->regs[R_IO_PLL_CTRL]); + uint64_t arm_pll = zynq_slcr_compute_pll(ps_clk, s->regs[R_ARM_PLL_CTRL]); + uint64_t ddr_pll = zynq_slcr_compute_pll(ps_clk, s->regs[R_DDR_PLL_CTRL]); + + uint64_t uart_mux[4] = {io_pll, io_pll, arm_pll, ddr_pll}; + + /* compute uartX reference clocks */ + clock_set(s->uart0_ref_clk, + ZYNQ_COMPUTE_CLK(s, uart_mux, R_UART_CLK_CTRL, CLKACT0)); + clock_set(s->uart1_ref_clk, + ZYNQ_COMPUTE_CLK(s, uart_mux, R_UART_CLK_CTRL, CLKACT1)); +} + /** * Compute and set the ouputs clocks periods. * But do not propagate them further. Connected clocks @@ -283,17 +298,7 @@ static void zynq_slcr_compute_clocks(ZynqSLCRState *s) ps_clk = 0; } - uint64_t io_pll = zynq_slcr_compute_pll(ps_clk, s->regs[R_IO_PLL_CTRL]); - uint64_t arm_pll = zynq_slcr_compute_pll(ps_clk, s->regs[R_ARM_PLL_CTRL]); - uint64_t ddr_pll = zynq_slcr_compute_pll(ps_clk, s->regs[R_DDR_PLL_CTRL]); - - uint64_t uart_mux[4] = {io_pll, io_pll, arm_pll, ddr_pll}; - - /* compute uartX reference clocks */ - clock_set(s->uart0_ref_clk, - ZYNQ_COMPUTE_CLK(s, uart_mux, R_UART_CLK_CTRL, CLKACT0)); - clock_set(s->uart1_ref_clk, - ZYNQ_COMPUTE_CLK(s, uart_mux, R_UART_CLK_CTRL, CLKACT1)); + zynq_slcr_compute_clocks_internal(s, ps_clk); } /** @@ -416,7 +421,7 @@ static void zynq_slcr_reset_hold(Object *obj) ZynqSLCRState *s = ZYNQ_SLCR(obj); /* will disable all output clocks */ - zynq_slcr_compute_clocks(s); + zynq_slcr_compute_clocks_internal(s, 0); zynq_slcr_propagate_clocks(s); } @@ -425,7 +430,7 @@ static void zynq_slcr_reset_exit(Object *obj) ZynqSLCRState *s = ZYNQ_SLCR(obj); /* will compute output clocks according to ps_clk and registers */ - zynq_slcr_compute_clocks(s); + zynq_slcr_compute_clocks_internal(s, clock_get(s->ps_clk)); zynq_slcr_propagate_clocks(s); } diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 9661c46699..b461b8d261 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -120,6 +120,7 @@ struct VirtMachineClass { MachineClass parent; bool disallow_affinity_adjustment; bool no_its; + bool no_tcg_its; bool no_pmu; bool claim_edge_triggered_timers; bool smbios_old_sys_ver; @@ -141,6 +142,7 @@ struct VirtMachineState { bool highmem; bool highmem_ecam; bool its; + bool tcg_its; bool virt; bool ras; bool mte; diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index 91491a2f66..aa4f0d6770 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -36,6 +36,8 @@ #define GICV3_MAXIRQ 1020 #define GICV3_MAXSPI (GICV3_MAXIRQ - GIC_INTERNAL) +#define GICV3_LPI_INTID_START 8192 + #define GICV3_REDIST_SIZE 0x20000 /* Number of SGI target-list bits */ @@ -202,6 +204,13 @@ struct GICv3CPUState { * real state above; it doesn't need to be migrated. */ PendingIrq hppi; + + /* + * Cached information recalculated from LPI tables + * in guest memory + */ + PendingIrq hpplpi; + /* This is temporary working state, to avoid a malloc in gicv3_update() */ bool seenbetter; }; @@ -219,6 +228,7 @@ struct GICv3State { uint32_t num_cpu; uint32_t num_irq; uint32_t revision; + bool lpi_enable; bool security_extn; bool irq_reset_nonsecure; bool gicd_no_migration_shift_bug; @@ -226,6 +236,9 @@ struct GICv3State { int dev_fd; /* kvm device fd if backed by kvm vgic support */ Error *migration_blocker; + MemoryRegion *dma; + AddressSpace dma_as; + /* Distributor */ /* for a GIC with the security extensions the NS banked version of this diff --git a/include/hw/intc/arm_gicv3_its_common.h b/include/hw/intc/arm_gicv3_its_common.h index 5a0952b404..4e79145dde 100644 --- a/include/hw/intc/arm_gicv3_its_common.h +++ b/include/hw/intc/arm_gicv3_its_common.h @@ -25,17 +25,41 @@ #include "hw/intc/arm_gicv3_common.h" #include "qom/object.h" +#define TYPE_ARM_GICV3_ITS "arm-gicv3-its" + #define ITS_CONTROL_SIZE 0x10000 #define ITS_TRANS_SIZE 0x10000 #define ITS_SIZE (ITS_CONTROL_SIZE + ITS_TRANS_SIZE) #define GITS_CTLR 0x0 #define GITS_IIDR 0x4 +#define GITS_TYPER 0x8 #define GITS_CBASER 0x80 #define GITS_CWRITER 0x88 #define GITS_CREADR 0x90 #define GITS_BASER 0x100 +#define GITS_TRANSLATER 0x0040 + +typedef struct { + bool valid; + bool indirect; + uint16_t entry_sz; + uint32_t page_sz; + uint32_t max_entries; + union { + uint32_t max_devids; + uint32_t max_collids; + } maxids; + uint64_t base_addr; +} TableDesc; + +typedef struct { + bool valid; + uint32_t max_entries; + uint64_t base_addr; +} CmdQDesc; + struct GICv3ITSState { SysBusDevice parent_obj; @@ -52,17 +76,23 @@ struct GICv3ITSState { /* Registers */ uint32_t ctlr; uint32_t iidr; + uint64_t typer; uint64_t cbaser; uint64_t cwriter; uint64_t creadr; uint64_t baser[8]; + TableDesc dt; + TableDesc ct; + CmdQDesc cq; + Error *migration_blocker; }; typedef struct GICv3ITSState GICv3ITSState; -void gicv3_its_init_mmio(GICv3ITSState *s, const MemoryRegionOps *ops); +void gicv3_its_init_mmio(GICv3ITSState *s, const MemoryRegionOps *ops, + const MemoryRegionOps *tops); #define TYPE_ARM_GICV3_ITS_COMMON "arm-gicv3-its-common" typedef struct GICv3ITSCommonClass GICv3ITSCommonClass; diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index bafc311bfa..762f9584dd 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -264,6 +264,7 @@ struct BusState { HotplugHandler *hotplug_handler; int max_index; bool realized; + bool full; int num_children; /* @@ -798,6 +799,29 @@ static inline bool qbus_is_hotpluggable(BusState *bus) return bus->hotplug_handler; } +/** + * qbus_mark_full: Mark this bus as full, so no more devices can be attached + * @bus: Bus to mark as full + * + * By default, QEMU will allow devices to be plugged into a bus up + * to the bus class's device count limit. Calling this function + * marks a particular bus as full, so that no more devices can be + * plugged into it. In particular this means that the bus will not + * be considered as a candidate for plugging in devices created by + * the user on the commandline or via the monitor. + * If a machine has multiple buses of a given type, such as I2C, + * where some of those buses in the real hardware are used only for + * internal devices and some are exposed via expansion ports, you + * can use this function to mark the internal-only buses as full + * after you have created all their internal devices. Then user + * created devices will appear on the expansion-port bus where + * guest software expects them. + */ +static inline void qbus_mark_full(BusState *bus) +{ + bus->full = true; +} + void device_listener_register(DeviceListener *listener); void device_listener_unregister(DeviceListener *listener); diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c index a304754ab9..0705f00846 100644 --- a/softmmu/qdev-monitor.c +++ b/softmmu/qdev-monitor.c @@ -435,7 +435,12 @@ static DeviceState *qbus_find_dev(BusState *bus, char *elem) static inline bool qbus_is_full(BusState *bus) { - BusClass *bus_class = BUS_GET_CLASS(bus); + BusClass *bus_class; + + if (bus->full) { + return true; + } + bus_class = BUS_GET_CLASS(bus); return bus_class->max_dev && bus->num_children >= bus_class->max_dev; } diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 6a987f65e4..fb0ef1ee2c 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -3455,6 +3455,7 @@ FIELD(TBFLAG_ANY, FPEXC_EL, 8, 2) FIELD(TBFLAG_ANY, DEBUG_TARGET_EL, 10, 2) /* Memory operations require alignment: SCTLR_ELx.A or CCR.UNALIGN_TRP */ FIELD(TBFLAG_ANY, ALIGN_MEM, 12, 1) +FIELD(TBFLAG_ANY, PSTATE__IL, 13, 1) /* * Bit usage when in AArch32 state, both A- and M-profile. diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c index 26f79f9141..19445b3c94 100644 --- a/target/arm/helper-a64.c +++ b/target/arm/helper-a64.c @@ -1071,6 +1071,7 @@ illegal_return: if (!arm_singlestep_active(env)) { env->pstate &= ~PSTATE_SS; } + helper_rebuild_hflags_a64(env, cur_el); qemu_log_mask(LOG_GUEST_ERROR, "Illegal exception return at EL%d: " "resuming execution at 0x%" PRIx64 "\n", cur_el, env->pc); } diff --git a/target/arm/helper.c b/target/arm/helper.c index a7ae78146d..b210da2bc2 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -13462,6 +13462,10 @@ static CPUARMTBFlags rebuild_hflags_a32(CPUARMState *env, int fp_el, DP_TBFLAG_A32(flags, HSTR_ACTIVE, 1); } + if (env->uncached_cpsr & CPSR_IL) { + DP_TBFLAG_ANY(flags, PSTATE__IL, 1); + } + return rebuild_hflags_common_32(env, fp_el, mmu_idx, flags); } @@ -13556,6 +13560,10 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, } } + if (env->pstate & PSTATE_IL) { + DP_TBFLAG_ANY(flags, PSTATE__IL, 1); + } + if (cpu_isar_feature(aa64_mte, env_archcpu(env))) { /* * Set MTE_ACTIVE if any access may be Checked, and leave clear diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 5d55de1a49..94b970bbf9 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -70,12 +70,17 @@ bool kvm_arm_create_scratch_host_vcpu(const uint32_t *cpus_to_try, struct kvm_vcpu_init *init) { int ret = 0, kvmfd = -1, vmfd = -1, cpufd = -1; + int max_vm_pa_size; kvmfd = qemu_open_old("/dev/kvm", O_RDWR); if (kvmfd < 0) { goto err; } - vmfd = ioctl(kvmfd, KVM_CREATE_VM, 0); + max_vm_pa_size = ioctl(kvmfd, KVM_CHECK_EXTENSION, KVM_CAP_ARM_VM_IPA_SIZE); + if (max_vm_pa_size < 0) { + max_vm_pa_size = 0; + } + vmfd = ioctl(kvmfd, KVM_CREATE_VM, max_vm_pa_size); if (vmfd < 0) { goto err; } diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index 34f8daa377..0613454975 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -525,8 +525,8 @@ static inline const char *its_class_name(void) /* KVM implementation requires this capability */ return kvm_direct_msi_enabled() ? "arm-its-kvm" : NULL; } else { - /* Software emulation is not implemented yet */ - return NULL; + /* Software emulation based model */ + return "arm-gicv3-its"; } } diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index 8dd88a0cb1..f30f4130a2 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -277,4 +277,9 @@ static inline uint32_t syn_wfx(int cv, int cond, int ti, bool is_16bit) (cv << 24) | (cond << 20) | ti; } +static inline uint32_t syn_illegalstate(void) +{ + return (EC_ILLEGALSTATE << ARM_EL_EC_SHIFT) | ARM_EL_IL; +} + #endif /* TARGET_ARM_SYNDROME_H */ diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 422e2ac0c9..333bc836b2 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -14649,11 +14649,128 @@ static bool btype_destination_ok(uint32_t insn, bool bt, int btype) return false; } -/* C3.1 A64 instruction index by encoding */ -static void disas_a64_insn(CPUARMState *env, DisasContext *s) +static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, + CPUState *cpu) { + DisasContext *dc = container_of(dcbase, DisasContext, base); + CPUARMState *env = cpu->env_ptr; + ARMCPU *arm_cpu = env_archcpu(env); + CPUARMTBFlags tb_flags = arm_tbflags_from_tb(dc->base.tb); + int bound, core_mmu_idx; + + dc->isar = &arm_cpu->isar; + dc->condjmp = 0; + + dc->aarch64 = 1; + /* If we are coming from secure EL0 in a system with a 32-bit EL3, then + * there is no secure EL1, so we route exceptions to EL3. + */ + dc->secure_routed_to_el3 = arm_feature(env, ARM_FEATURE_EL3) && + !arm_el_is_aa64(env, 3); + dc->thumb = 0; + dc->sctlr_b = 0; + dc->be_data = EX_TBFLAG_ANY(tb_flags, BE_DATA) ? MO_BE : MO_LE; + dc->condexec_mask = 0; + dc->condexec_cond = 0; + core_mmu_idx = EX_TBFLAG_ANY(tb_flags, MMUIDX); + dc->mmu_idx = core_to_aa64_mmu_idx(core_mmu_idx); + dc->tbii = EX_TBFLAG_A64(tb_flags, TBII); + dc->tbid = EX_TBFLAG_A64(tb_flags, TBID); + dc->tcma = EX_TBFLAG_A64(tb_flags, TCMA); + dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx); +#if !defined(CONFIG_USER_ONLY) + dc->user = (dc->current_el == 0); +#endif + dc->fp_excp_el = EX_TBFLAG_ANY(tb_flags, FPEXC_EL); + dc->align_mem = EX_TBFLAG_ANY(tb_flags, ALIGN_MEM); + dc->pstate_il = EX_TBFLAG_ANY(tb_flags, PSTATE__IL); + dc->sve_excp_el = EX_TBFLAG_A64(tb_flags, SVEEXC_EL); + dc->sve_len = (EX_TBFLAG_A64(tb_flags, ZCR_LEN) + 1) * 16; + dc->pauth_active = EX_TBFLAG_A64(tb_flags, PAUTH_ACTIVE); + dc->bt = EX_TBFLAG_A64(tb_flags, BT); + dc->btype = EX_TBFLAG_A64(tb_flags, BTYPE); + dc->unpriv = EX_TBFLAG_A64(tb_flags, UNPRIV); + dc->ata = EX_TBFLAG_A64(tb_flags, ATA); + dc->mte_active[0] = EX_TBFLAG_A64(tb_flags, MTE_ACTIVE); + dc->mte_active[1] = EX_TBFLAG_A64(tb_flags, MTE0_ACTIVE); + dc->vec_len = 0; + dc->vec_stride = 0; + dc->cp_regs = arm_cpu->cp_regs; + dc->features = env->features; + dc->dcz_blocksize = arm_cpu->dcz_blocksize; + +#ifdef CONFIG_USER_ONLY + /* In sve_probe_page, we assume TBI is enabled. */ + tcg_debug_assert(dc->tbid & 1); +#endif + + /* Single step state. The code-generation logic here is: + * SS_ACTIVE == 0: + * generate code with no special handling for single-stepping (except + * that anything that can make us go to SS_ACTIVE == 1 must end the TB; + * this happens anyway because those changes are all system register or + * PSTATE writes). + * SS_ACTIVE == 1, PSTATE.SS == 1: (active-not-pending) + * emit code for one insn + * emit code to clear PSTATE.SS + * emit code to generate software step exception for completed step + * end TB (as usual for having generated an exception) + * SS_ACTIVE == 1, PSTATE.SS == 0: (active-pending) + * emit code to generate a software step exception + * end the TB + */ + dc->ss_active = EX_TBFLAG_ANY(tb_flags, SS_ACTIVE); + dc->pstate_ss = EX_TBFLAG_ANY(tb_flags, PSTATE__SS); + dc->is_ldex = false; + dc->debug_target_el = EX_TBFLAG_ANY(tb_flags, DEBUG_TARGET_EL); + + /* Bound the number of insns to execute to those left on the page. */ + bound = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; + + /* If architectural single step active, limit to 1. */ + if (dc->ss_active) { + bound = 1; + } + dc->base.max_insns = MIN(dc->base.max_insns, bound); + + init_tmp_a64_array(dc); +} + +static void aarch64_tr_tb_start(DisasContextBase *db, CPUState *cpu) +{ +} + +static void aarch64_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + tcg_gen_insn_start(dc->base.pc_next, 0, 0); + dc->insn_start = tcg_last_op(); +} + +static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *s = container_of(dcbase, DisasContext, base); + CPUARMState *env = cpu->env_ptr; uint32_t insn; + if (s->ss_active && !s->pstate_ss) { + /* Singlestep state is Active-pending. + * If we're in this state at the start of a TB then either + * a) we just took an exception to an EL which is being debugged + * and this is the first insn in the exception handler + * b) debug exceptions were masked and we just unmasked them + * without changing EL (eg by clearing PSTATE.D) + * In either case we're going to take a swstep exception in the + * "did not step an insn" case, and so the syndrome ISV and EX + * bits should be zero. + */ + assert(s->base.num_insns == 1); + gen_swstep_exception(s, 0, 0); + s->base.is_jmp = DISAS_NORETURN; + return; + } + s->pc_curr = s->base.pc_next; insn = arm_ldl_code(env, s->base.pc_next, s->sctlr_b); s->insn = insn; @@ -14662,6 +14779,16 @@ static void disas_a64_insn(CPUARMState *env, DisasContext *s) s->fp_access_checked = false; s->sve_access_checked = false; + if (s->pstate_il) { + /* + * Illegal execution state. This has priority over BTI + * exceptions, but comes after instruction abort exceptions. + */ + gen_exception_insn(s, s->pc_curr, EXCP_UDEF, + syn_illegalstate(), default_exception_el(s)); + return; + } + if (dc_isar_feature(aa64_bti, s)) { if (s->base.num_insns == 1) { /* @@ -14744,130 +14871,8 @@ static void disas_a64_insn(CPUARMState *env, DisasContext *s) if (s->btype > 0 && s->base.is_jmp != DISAS_NORETURN) { reset_btype(s); } -} -static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, - CPUState *cpu) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUARMState *env = cpu->env_ptr; - ARMCPU *arm_cpu = env_archcpu(env); - CPUARMTBFlags tb_flags = arm_tbflags_from_tb(dc->base.tb); - int bound, core_mmu_idx; - - dc->isar = &arm_cpu->isar; - dc->condjmp = 0; - - dc->aarch64 = 1; - /* If we are coming from secure EL0 in a system with a 32-bit EL3, then - * there is no secure EL1, so we route exceptions to EL3. - */ - dc->secure_routed_to_el3 = arm_feature(env, ARM_FEATURE_EL3) && - !arm_el_is_aa64(env, 3); - dc->thumb = 0; - dc->sctlr_b = 0; - dc->be_data = EX_TBFLAG_ANY(tb_flags, BE_DATA) ? MO_BE : MO_LE; - dc->condexec_mask = 0; - dc->condexec_cond = 0; - core_mmu_idx = EX_TBFLAG_ANY(tb_flags, MMUIDX); - dc->mmu_idx = core_to_aa64_mmu_idx(core_mmu_idx); - dc->tbii = EX_TBFLAG_A64(tb_flags, TBII); - dc->tbid = EX_TBFLAG_A64(tb_flags, TBID); - dc->tcma = EX_TBFLAG_A64(tb_flags, TCMA); - dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx); -#if !defined(CONFIG_USER_ONLY) - dc->user = (dc->current_el == 0); -#endif - dc->fp_excp_el = EX_TBFLAG_ANY(tb_flags, FPEXC_EL); - dc->align_mem = EX_TBFLAG_ANY(tb_flags, ALIGN_MEM); - dc->sve_excp_el = EX_TBFLAG_A64(tb_flags, SVEEXC_EL); - dc->sve_len = (EX_TBFLAG_A64(tb_flags, ZCR_LEN) + 1) * 16; - dc->pauth_active = EX_TBFLAG_A64(tb_flags, PAUTH_ACTIVE); - dc->bt = EX_TBFLAG_A64(tb_flags, BT); - dc->btype = EX_TBFLAG_A64(tb_flags, BTYPE); - dc->unpriv = EX_TBFLAG_A64(tb_flags, UNPRIV); - dc->ata = EX_TBFLAG_A64(tb_flags, ATA); - dc->mte_active[0] = EX_TBFLAG_A64(tb_flags, MTE_ACTIVE); - dc->mte_active[1] = EX_TBFLAG_A64(tb_flags, MTE0_ACTIVE); - dc->vec_len = 0; - dc->vec_stride = 0; - dc->cp_regs = arm_cpu->cp_regs; - dc->features = env->features; - dc->dcz_blocksize = arm_cpu->dcz_blocksize; - -#ifdef CONFIG_USER_ONLY - /* In sve_probe_page, we assume TBI is enabled. */ - tcg_debug_assert(dc->tbid & 1); -#endif - - /* Single step state. The code-generation logic here is: - * SS_ACTIVE == 0: - * generate code with no special handling for single-stepping (except - * that anything that can make us go to SS_ACTIVE == 1 must end the TB; - * this happens anyway because those changes are all system register or - * PSTATE writes). - * SS_ACTIVE == 1, PSTATE.SS == 1: (active-not-pending) - * emit code for one insn - * emit code to clear PSTATE.SS - * emit code to generate software step exception for completed step - * end TB (as usual for having generated an exception) - * SS_ACTIVE == 1, PSTATE.SS == 0: (active-pending) - * emit code to generate a software step exception - * end the TB - */ - dc->ss_active = EX_TBFLAG_ANY(tb_flags, SS_ACTIVE); - dc->pstate_ss = EX_TBFLAG_ANY(tb_flags, PSTATE__SS); - dc->is_ldex = false; - dc->debug_target_el = EX_TBFLAG_ANY(tb_flags, DEBUG_TARGET_EL); - - /* Bound the number of insns to execute to those left on the page. */ - bound = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; - - /* If architectural single step active, limit to 1. */ - if (dc->ss_active) { - bound = 1; - } - dc->base.max_insns = MIN(dc->base.max_insns, bound); - - init_tmp_a64_array(dc); -} - -static void aarch64_tr_tb_start(DisasContextBase *db, CPUState *cpu) -{ -} - -static void aarch64_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - - tcg_gen_insn_start(dc->base.pc_next, 0, 0); - dc->insn_start = tcg_last_op(); -} - -static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUARMState *env = cpu->env_ptr; - - if (dc->ss_active && !dc->pstate_ss) { - /* Singlestep state is Active-pending. - * If we're in this state at the start of a TB then either - * a) we just took an exception to an EL which is being debugged - * and this is the first insn in the exception handler - * b) debug exceptions were masked and we just unmasked them - * without changing EL (eg by clearing PSTATE.D) - * In either case we're going to take a swstep exception in the - * "did not step an insn" case, and so the syndrome ISV and EX - * bits should be zero. - */ - assert(dc->base.num_insns == 1); - gen_swstep_exception(dc, 0, 0); - dc->base.is_jmp = DISAS_NORETURN; - } else { - disas_a64_insn(env, dc); - } - - translator_loop_temp_check(&dc->base); + translator_loop_temp_check(&s->base); } static void aarch64_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) diff --git a/target/arm/translate.c b/target/arm/translate.c index 24b7f49d76..435c659723 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -9090,6 +9090,16 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) return; } + if (s->pstate_il) { + /* + * Illegal execution state. This has priority over BTI + * exceptions, but comes after instruction abort exceptions. + */ + gen_exception_insn(s, s->pc_curr, EXCP_UDEF, + syn_illegalstate(), default_exception_el(s)); + return; + } + if (cond == 0xf) { /* In ARMv3 and v4 the NV condition is UNPREDICTABLE; we * choose to UNDEF. In ARMv5 and above the space is used @@ -9358,6 +9368,7 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) #endif dc->fp_excp_el = EX_TBFLAG_ANY(tb_flags, FPEXC_EL); dc->align_mem = EX_TBFLAG_ANY(tb_flags, ALIGN_MEM); + dc->pstate_il = EX_TBFLAG_ANY(tb_flags, PSTATE__IL); if (arm_feature(env, ARM_FEATURE_M)) { dc->vfp_enabled = 1; @@ -9621,6 +9632,16 @@ static void thumb_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) } dc->insn = insn; + if (dc->pstate_il) { + /* + * Illegal execution state. This has priority over BTI + * exceptions, but comes after instruction abort exceptions. + */ + gen_exception_insn(dc, dc->pc_curr, EXCP_UDEF, + syn_illegalstate(), default_exception_el(dc)); + return; + } + if (dc->eci) { /* * For M-profile continuable instructions, ECI/ICI handling diff --git a/target/arm/translate.h b/target/arm/translate.h index 8636c20c3b..605d1f2e33 100644 --- a/target/arm/translate.h +++ b/target/arm/translate.h @@ -98,6 +98,8 @@ typedef struct DisasContext { bool hstr_active; /* True if memory operations require alignment */ bool align_mem; + /* True if PSTATE.IL is set */ + bool pstate_il; /* * >= 0, a copy of PSTATE.BTYPE, which will be 0 without v8.5-BTI. * < 0, set by the current instruction. diff --git a/tests/data/acpi/virt/IORT b/tests/data/acpi/virt/IORT new file mode 100644 index 0000000000..521acefe9b Binary files /dev/null and b/tests/data/acpi/virt/IORT differ diff --git a/tests/data/acpi/virt/IORT.memhp b/tests/data/acpi/virt/IORT.memhp new file mode 100644 index 0000000000..521acefe9b Binary files /dev/null and b/tests/data/acpi/virt/IORT.memhp differ diff --git a/tests/data/acpi/virt/IORT.numamem b/tests/data/acpi/virt/IORT.numamem new file mode 100644 index 0000000000..521acefe9b Binary files /dev/null and b/tests/data/acpi/virt/IORT.numamem differ diff --git a/tests/data/acpi/virt/IORT.pxb b/tests/data/acpi/virt/IORT.pxb new file mode 100644 index 0000000000..521acefe9b Binary files /dev/null and b/tests/data/acpi/virt/IORT.pxb differ