[PATCH 04/17] target/arm: Raise alignment fault for DC_ZVA on Device memory

Richard Henderson <[email protected]>
Newsgroups org.nongnu.qemu-arm,org.nongnu.qemu-devel
Message-ID <[email protected]>
Raise alignment fault when DC_ZVA targets Device memory, as required
by the architecture.  With MTE, an alignment exception is not always
correctly ordered vs a tag check fail exception -- to be fixed in
followup patches.

Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3005
Signed-off-by: Richard Henderson <[email protected]>
---
 target/arm/tcg/mte_helper.c | 104 ++++++++++++++++++++++--------------
 1 file changed, 65 insertions(+), 39 deletions(-)

diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c
index 993aba5df4..dc87dc283f 100644
--- a/target/arm/tcg/mte_helper.c
+++ b/target/arm/tcg/mte_helper.c
@@ -1018,56 +1018,82 @@ bool mte_probe(CPUARMState *env, uint32_t desc, uint64_t ptr)
     return ret != 0;
 }
 
-void HELPER(dc_zva)(CPUARMState *env, uint64_t vaddr_in)
+/* Traps for DC_ZVA, DC_GVA, and friends, after successful page lookup. */
+static int do_dcxva_traps(CPUARMState *env, vaddr addr, size_t len, int mmu_idx,
+                          int flags, MemTxAttrs attrs, uintptr_t ra)
 {
-    uintptr_t ra = GETPC();
-
-    /*
-     * Implement DC ZVA, which zeroes a fixed-length block of memory.
-     * Note that we do not implement the (architecturally mandated)
-     * alignment fault for attempts to use this on Device memory
-     * (which matches the usual QEMU behaviour of not implementing either
-     * alignment faults or any memory attribute handling).
-     */
-    int blocklen = 4 << get_dczid_bs(env_archcpu(env));
-    uint64_t vaddr = vaddr_in & ~(blocklen - 1);
-    int mmu_idx = arm_env_mmu_index(env);
-    void *mem;
-
-    /*
-     * Trapless lookup.  In addition to actual invalid page, may
-     * return NULL for I/O, watchpoints, clean pages, etc.
-     */
-    mem = tlb_vaddr_to_host(env, vaddr, MMU_DATA_STORE, mmu_idx);
-
 #ifndef CONFIG_USER_ONLY
-    if (unlikely(!mem)) {
-        /*
-         * Trap if accessing an invalid page.  DC_ZVA requires that we supply
-         * the original pointer for an invalid page.  But watchpoints require
-         * that we probe the actual space.  So do both.
-         */
-        (void) probe_write(env, vaddr_in, 1, mmu_idx, ra);
-        mem = probe_write(env, vaddr, blocklen, mmu_idx, ra);
+    /*
+     * DC_ZVA traps on pages without caching enabled.  This can be either
+     * pages mapped as Device, or when the mmu is disabled entirely.
+     * We have already detected such pages and have set TLB_CHECK_ALIGNED
+     * so that unaligned accesses are trapped under the same conditions.
+     */
+    if (unlikely(flags & TLB_CHECK_ALIGNED)) {
+        arm_cpu_do_unaligned_access(env_cpu(env), addr, MMU_DATA_STORE,
+                                    mmu_idx, ra);
+    }
 
-        if (unlikely(!mem)) {
-            /*
-             * The only remaining reason for mem == NULL is I/O.
-             * Just do a series of byte writes as the architecture demands.
-             */
-            for (int i = 0; i < blocklen; i++) {
-                cpu_stb_mmuidx_ra(env, vaddr + i, 0, mmu_idx, ra);
-            }
-            return;
+    /* Watchpoints have lower priority than alignment faults. */
+    if (unlikely(flags & TLB_WATCHPOINT)) {
+        cpu_check_watchpoint(env_cpu(env), addr & -len, len,
+                             attrs, BP_MEM_WRITE, ra);
+        flags &= ~TLB_WATCHPOINT;
+    }
+#endif
+    return flags;
+}
+
+static void do_dczva_0(CPUARMState *env, vaddr addr, size_t len, void *mem,
+                       int mmu_idx, int flags, uintptr_t ra)
+{
+#ifndef CONFIG_USER_ONLY
+    /*
+     * There are a couple of cases where QEMU wants the slow path;
+     * just do a series of byte writes as the architecture demands.
+     */
+    if (unlikely(flags)) {
+        for (size_t i = 0; i < len; i++) {
+            cpu_stb_mmuidx_ra(env, addr + i, 0, mmu_idx, ra);
         }
+        return;
     }
 #endif
 
     set_helper_retaddr(ra);
-    memset(mem, 0, blocklen);
+    memset(mem, 0, len);
     clear_helper_retaddr();
 }
 
+void HELPER(dc_zva)(CPUARMState *env, uint64_t addr)
+{
+    uintptr_t ra = GETPC();
+    size_t len = (size_t)4 << get_dczid_bs(env_archcpu(env));
+    int mmu_idx = arm_env_mmu_index(env);
+    MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
+    int flags;
+    void *mem;
+
+    /* DC_ZVA requires that we supply the original pointer for traps. */
+#ifdef CONFIG_USER_ONLY
+    flags = probe_access_flags(env, addr, 0, MMU_DATA_STORE, mmu_idx,
+                               false, &mem, ra);
+#else
+    CPUTLBEntryFull *full;
+    flags = probe_access_full(env, addr, 0, MMU_DATA_STORE, mmu_idx,
+                              false, &mem, &full, ra);
+    attrs = full->attrs;
+#endif
+
+    flags = do_dcxva_traps(env, addr, len, mmu_idx, flags, attrs, ra);
+
+    /* After traps, treat as aligned blocks. */
+    addr = addr & -len;
+    mem = (void *)((uintptr_t)mem & -len);
+
+    do_dczva_0(env, addr, len, mem, mmu_idx, flags, ra);
+}
+
 /*
  * Perform an MTE checked access for DC_ZVA.
  */
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.