Implement the three registers that handle configuration of the
interrupt status table for physical LPIs:
* IRS_IST_BASER holds the base address of the table, and
has the VALID bit that tells the IRS to start using the config
* IRS_IST_CFGR has all the other config data for the table
* IRS_IST_STATUSR has the IDLE bit that tells software when
updates to IRS_IST_BASER have taken effect
Implement these registers. Note that neither BASER nor CFGR can be
written when VALID == 1, except to clear the VALID bit.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
---
hw/intc/arm_gicv5.c | 74 ++++++++++++++++++++++++++++++
hw/intc/arm_gicv5_common.c | 4 ++
include/hw/intc/arm_gicv5_common.h | 3 ++
3 files changed, 81 insertions(+)
diff --git a/hw/intc/arm_gicv5.c b/hw/intc/arm_gicv5.c
index 250925f004..cbb35c0270 100644
--- a/hw/intc/arm_gicv5.c
+++ b/hw/intc/arm_gicv5.c
@@ -265,6 +265,24 @@ REG64(IRS_SWERR_SYNDROMER0, 0x3c8)
REG64(IRS_SWERR_SYNDROMER1, 0x3d0)
FIELD(IRS_SWERR_SYNDROMER2, ADDR, 3, 53)
+static void irs_ist_baser_write(GICv5 *s, GICv5Domain domain, uint64_t value)
+{
+ GICv5Common *cs = ARM_GICV5_COMMON(s);
+
+ if (FIELD_EX64(cs->irs_ist_baser[domain], IRS_IST_BASER, VALID)) {
+ /* If VALID is set, ADDR is RO and we can only update VALID */
+ bool valid = FIELD_EX64(value, IRS_IST_BASER, VALID);
+ if (valid) {
+ /* Ignore 1->1 transition */
+ return;
+ }
+ cs->irs_ist_baser[domain] = FIELD_DP64(cs->irs_ist_baser[domain],
+ IRS_IST_BASER, VALID, valid);
+ return;
+ }
+ cs->irs_ist_baser[domain] = value;
+}
+
static bool config_readl(GICv5 *s, GICv5Domain domain, hwaddr offset,
uint64_t *data, MemTxAttrs attrs)
{
@@ -325,6 +343,26 @@ static bool config_readl(GICv5 *s, GICv5Domain domain, hwaddr offset,
case A_IRS_AIDR:
*data = cs->irs_aidr;
return true;
+
+ case A_IRS_IST_BASER:
+ *data = extract64(cs->irs_ist_baser[domain], 0, 32);
+ return true;
+
+ case A_IRS_IST_BASER + 4:
+ *data = extract64(cs->irs_ist_baser[domain], 32, 32);
+ return true;
+
+ case A_IRS_IST_STATUSR:
+ /*
+ * For QEMU writes to IRS_IST_BASER and IRS_MAP_L2_ISTR take effect
+ * instantaneously, and the guest can never see the IDLE bit as 0.
+ */
+ *data = R_IRS_IST_STATUSR_IDLE_MASK;
+ return true;
+
+ case A_IRS_IST_CFGR:
+ *data = cs->irs_ist_cfgr[domain];
+ return true;
}
return false;
@@ -333,18 +371,54 @@ static bool config_readl(GICv5 *s, GICv5Domain domain, hwaddr offset,
static bool config_writel(GICv5 *s, GICv5Domain domain, hwaddr offset,
uint64_t data, MemTxAttrs attrs)
{
+ GICv5Common *cs = ARM_GICV5_COMMON(s);
+
+ switch (offset) {
+ case A_IRS_IST_BASER:
+ irs_ist_baser_write(s, domain,
+ deposit64(cs->irs_ist_baser[domain], 0, 32, data));
+ return true;
+ case A_IRS_IST_BASER + 4:
+ irs_ist_baser_write(s, domain,
+ deposit64(cs->irs_ist_baser[domain], 32, 32, data));
+ return true;
+ case A_IRS_IST_CFGR:
+ if (FIELD_EX64(cs->irs_ist_baser[domain], IRS_IST_BASER, VALID)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "guest tried to write IRS_IST_CFGR for %s config frame "
+ "while IST_BASER.VALID set\n", domain_name[domain]);
+ } else {
+ cs->irs_ist_cfgr[domain] = data;
+ }
+ return true;
+ }
+
return false;
}
static bool config_readll(GICv5 *s, GICv5Domain domain, hwaddr offset,
uint64_t *data, MemTxAttrs attrs)
{
+ GICv5Common *cs = ARM_GICV5_COMMON(s);
+
+ switch (offset) {
+ case A_IRS_IST_BASER:
+ *data = cs->irs_ist_baser[domain];
+ return true;
+ }
+
return false;
}
static bool config_writell(GICv5 *s, GICv5Domain domain, hwaddr offset,
uint64_t data, MemTxAttrs attrs)
{
+ switch (offset) {
+ case A_IRS_IST_BASER:
+ irs_ist_baser_write(s, domain, data);
+ return true;
+ }
+
return false;
}
diff --git a/hw/intc/arm_gicv5_common.c b/hw/intc/arm_gicv5_common.c
index 54d75db014..44909d1b05 100644
--- a/hw/intc/arm_gicv5_common.c
+++ b/hw/intc/arm_gicv5_common.c
@@ -62,6 +62,10 @@ void gicv5_common_init_irqs_and_mmio(GICv5Common *cs,
static void gicv5_common_reset_hold(Object *obj, ResetType type)
{
+ GICv5Common *cs = ARM_GICV5_COMMON(obj);
+
+ memset(cs->irs_ist_baser, 0, sizeof(cs->irs_ist_baser));
+ memset(cs->irs_ist_cfgr, 0, sizeof(cs->irs_ist_cfgr));
}
static void gicv5_common_init(Object *obj)
diff --git a/include/hw/intc/arm_gicv5_common.h b/include/hw/intc/arm_gicv5_common.h
index 88e1b4d73d..9bfafcebfc 100644
--- a/include/hw/intc/arm_gicv5_common.h
+++ b/include/hw/intc/arm_gicv5_common.h
@@ -62,6 +62,9 @@ struct GICv5Common {
MemoryRegion iomem[NUM_GICV5_DOMAINS];
+ uint64_t irs_ist_baser[NUM_GICV5_DOMAINS];
+ uint32_t irs_ist_cfgr[NUM_GICV5_DOMAINS];
+
/* Bits here are set for each physical interrupt domain implemented */
uint8_t implemented_domains;
--
2.43.0