aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2021-09-13 21:06:15 +0100
committerPeter Maydell <peter.maydell@linaro.org>2021-09-13 21:06:15 +0100
commitc6f5e042d89e79206cd1ce5525d3df219f13c3cc (patch)
tree6ca87baa62a6309cfc0c88841a57bf16511a7af3 /hw
parent7d79344d4fa44e520e6e89f8fed9a27d3d554a9b (diff)
parent28e987a7e7edaa3ca7feeac65edca26145df8814 (diff)
downloadqemu-c6f5e042d89e79206cd1ce5525d3df219f13c3cc.zip
qemu-c6f5e042d89e79206cd1ce5525d3df219f13c3cc.tar.gz
qemu-c6f5e042d89e79206cd1ce5525d3df219f13c3cc.tar.bz2
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20210913-3' into staging
target-arm queue: * mark MPS2/MPS3 board-internal i2c buses as 'full' so that command line user-created devices are not plugged into them * Take an exception if PSTATE.IL is set * Support an emulated ITS in the virt board * Add support for kudo-bmc board * Probe for KVM_CAP_ARM_VM_IPA_SIZE when creating scratch VM * cadence_uart: Fix clock handling issues that prevented u-boot from running # gpg: Signature made Mon 13 Sep 2021 21:04:52 BST # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate] # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20210913-3: (23 commits) hw/arm/mps2.c: Mark internal-only I2C buses as 'full' hw/arm/mps2-tz.c: Mark internal-only I2C buses as 'full' hw/arm/mps2-tz.c: Add extra data parameter to MakeDevFn qdev: Support marking individual buses as 'full' target/arm: Merge disas_a64_insn into aarch64_tr_translate_insn target/arm: Take an exception if PSTATE.IL is set tests/data/acpi/virt: Update IORT files for ITS hw/arm/virt: add ITS support in virt GIC tests/data/acpi/virt: Add IORT files for ITS hw/intc: GICv3 redistributor ITS processing hw/intc: GICv3 ITS Feature enablement hw/intc: GICv3 ITS Command processing hw/intc: GICv3 ITS command queue framework hw/intc: GICv3 ITS register definitions added hw/intc: GICv3 ITS initial framework hw/arm: Add support for kudo-bmc board. hw/arm/virt: KVM: Probe for KVM_CAP_ARM_VM_IPA_SIZE when creating scratch VM hw/char: cadence_uart: Log a guest error when device is unclocked or in reset hw/char: cadence_uart: Ignore access when unclocked or in reset for uart_{read, write}() hw/char: cadence_uart: Convert to memop_with_attrs() ops ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw')
-rw-r--r--hw/arm/mps2-tz.c92
-rw-r--r--hw/arm/mps2.c12
-rw-r--r--hw/arm/npcm7xx_boards.c34
-rw-r--r--hw/arm/virt.c29
-rw-r--r--hw/char/cadence_uart.c61
-rw-r--r--hw/intc/arm_gicv3.c14
-rw-r--r--hw/intc/arm_gicv3_common.c13
-rw-r--r--hw/intc/arm_gicv3_cpuif.c7
-rw-r--r--hw/intc/arm_gicv3_dist.c5
-rw-r--r--hw/intc/arm_gicv3_its.c1322
-rw-r--r--hw/intc/arm_gicv3_its_common.c7
-rw-r--r--hw/intc/arm_gicv3_its_kvm.c2
-rw-r--r--hw/intc/arm_gicv3_redist.c153
-rw-r--r--hw/intc/gicv3_internal.h188
-rw-r--r--hw/intc/meson.build1
-rw-r--r--hw/misc/zynq_slcr.c31
16 files changed, 1891 insertions, 80 deletions
diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c
index e23830f..f40e854 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 4634aa1..bb76fa6 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 e5a3243..a656169 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 73e9c6b..1d59f0e 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 b4b5e8a..c069a30 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 d63f8af..3f24707 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 58ef65f..223db16 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 a032d50..462a35f 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 5beb7c4..4164500 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 0000000..84bcbb5
--- /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 <shashi.mallela@linaro.org>
+ *
+ * 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<n>.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 66c4c6a..7d7f388 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 b554d2e..0b4cbed 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 53da703..7072bfc 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 05303a5..a0369da 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)
+
+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_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)
+#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 6e52a16..4dcfea6 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 5086e6b..8b70285 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);
}