Implement read/write handlers for the SMMU_S_INIT secure-only
register.

Writing to this register provides a mechanism for software to perform a
global invalidation of ALL caches within the SMMU. This includes the
IOTLBs and Configuration Caches across all security states.

This feature is critical for secure hypervisors like Hafnium, which
use it as a final step in their SMMU initialization sequence. It
provides a reliable, architecturally defined method to ensure a clean
and known-good cache state before enabling translations.

Signed-off-by: Tao Tang <[email protected]>
---
 hw/arm/smmuv3.c     | 33 +++++++++++++++++++++++++++++++++
 hw/arm/trace-events |  1 +
 2 files changed, 34 insertions(+)

diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index 100caeeb35..432de88610 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -354,6 +354,21 @@ static int smmu_get_ste(SMMUv3State *s, dma_addr_t addr, 
STE *buf,
 
 }
 
+static void smmuv3_invalidate_all_caches(SMMUv3State *s)
+{
+    trace_smmuv3_invalidate_all_caches();
+    SMMUState *bs = &s->smmu_state;
+
+    /* Clear all cached configs including STE and CD */
+    if (bs->configs) {
+        g_hash_table_remove_all(bs->configs);
+    }
+
+    /* Invalidate all SMMU IOTLB entries */
+    smmu_inv_notifiers_all(&s->smmu_state);
+    smmu_iotlb_inv_all(bs);
+}
+
 static SMMUTranslationStatus smmuv3_do_translate(SMMUv3State *s, hwaddr addr,
                                                  SMMUTransCfg *cfg,
                                                  SMMUEventInfo *event,
@@ -1969,6 +1984,21 @@ static MemTxResult smmu_writel(SMMUv3State *s, hwaddr 
offset,
 
         bank->eventq_irq_cfg2 = data;
         return MEMTX_OK;
+    case (A_S_INIT & 0xfff):
+        if (data & R_S_INIT_INV_ALL_MASK) {
+            int cr0_smmuen = smmu_enabled(s, reg_sec_sid);
+            int s_cr0_smmuen = smmuv3_get_cr0ack_smmuen(s, reg_sec_sid);
+            if (cr0_smmuen || s_cr0_smmuen) {
+                /* CONSTRAINED UNPREDICTABLE behavior: Ignore this write */
+                qemu_log_mask(LOG_GUEST_ERROR, "S_INIT write ignored: "
+                              "CR0.SMMUEN=%d or S_CR0.SMMUEN=%d is set\n",
+                              cr0_smmuen, s_cr0_smmuen);
+                return MEMTX_OK;
+            }
+            smmuv3_invalidate_all_caches(s);
+        }
+        /* Synchronous emulation: invalidation completed instantly. */
+        return MEMTX_OK;
     default:
         qemu_log_mask(LOG_UNIMP,
                       "%s Unexpected 32-bit access to 0x%"PRIx64" (WI)\n",
@@ -2172,6 +2202,9 @@ static MemTxResult smmu_readl(SMMUv3State *s, hwaddr 
offset,
     case A_EVENTQ_CONS:
         *data = bank->eventq.cons;
         return MEMTX_OK;
+    case (A_S_INIT & 0xfff):
+        *data = 0;
+        return MEMTX_OK;
     default:
         *data = 0;
         qemu_log_mask(LOG_UNIMP,
diff --git a/hw/arm/trace-events b/hw/arm/trace-events
index 434d6abfc2..0e7ad8fee3 100644
--- a/hw/arm/trace-events
+++ b/hw/arm/trace-events
@@ -64,6 +64,7 @@ smmuv3_cmdq_tlbi_s12_vmid(int vmid) "vmid=%d"
 smmuv3_notify_flag_add(const char *iommu) "ADD SMMUNotifier node for iommu 
mr=%s"
 smmuv3_notify_flag_del(const char *iommu) "DEL SMMUNotifier node for iommu 
mr=%s"
 smmuv3_inv_notifiers_iova(const char *name, int asid, int vmid, uint64_t iova, 
uint8_t tg, uint64_t num_pages, int stage) "iommu mr=%s asid=%d vmid=%d 
iova=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" stage=%d"
+smmuv3_invalidate_all_caches(void) "Invalidate all SMMU caches and TLBs"
 smmu_reset_exit(void) ""
 
 # strongarm.c
-- 
2.34.1


Reply via email to