[RFC PATCH] target/arm: fix user-mode emulation of ID_AA64MMFR0_EL1

Alex Bennée posted 1 patch 5 days, 4 hours ago
target/arm/helper.c         |  7 +++++--
tests/tcg/aarch64/sysregs.c | 31 ++++++++++++++++++++++++++++---
2 files changed, 33 insertions(+), 5 deletions(-)
[RFC PATCH] target/arm: fix user-mode emulation of ID_AA64MMFR0_EL1
Posted by Alex Bennée 5 days, 4 hours ago
Since kernel commit b130a8f70cbbf9 (KVM: arm64: Check advertised
Stage-2 page size capability) the relevant Stage 2 have been pegged at
1 f(4KB granule not supported at stage 2) by the kernel. As a result
the behaviour of the test differs when run on real HW compared to
under emulation:

  id_aa64mmfr0_el1    : 0x00000111ff000000
    !!extra bits!!    : 0x0000011100000000

Update the test with a new helper to explicitly test for fixed bits
and update v8_user_idregs to match real systems.

Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4566
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
 target/arm/helper.c         |  7 +++++--
 tests/tcg/aarch64/sysregs.c | 31 ++++++++++++++++++++++++++++---
 2 files changed, 33 insertions(+), 5 deletions(-)

diff --git a/target/arm/helper.c b/target/arm/helper.c
index c3f607e6d6b..7e65f072a4a 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -6902,8 +6902,11 @@ void register_cp_regs_for_features(ARMCPU *cpu)
                                R_ID_AA64FPFR0_F8CVT_MASK },
             { .name = "ID_AA64MMFR0_EL1",
               .exported_bits = R_ID_AA64MMFR0_ECV_MASK,
-              .fixed_bits = (0xfu << R_ID_AA64MMFR0_TGRAN64_SHIFT) |
-                            (0xfu << R_ID_AA64MMFR0_TGRAN4_SHIFT) },
+              .fixed_bits = (0xfULL << R_ID_AA64MMFR0_TGRAN64_SHIFT) |
+                            (0xfULL << R_ID_AA64MMFR0_TGRAN4_SHIFT) |
+                            (0x1ULL << R_ID_AA64MMFR0_TGRAN4_2_SHIFT) |
+                            (0x1ULL << R_ID_AA64MMFR0_TGRAN64_2_SHIFT) |
+                            (0x1ULL << R_ID_AA64MMFR0_TGRAN16_2_SHIFT) },
             { .name = "ID_AA64MMFR1_EL1",
               .exported_bits = R_ID_AA64MMFR1_AFP_MASK },
             { .name = "ID_AA64MMFR2_EL1",
diff --git a/tests/tcg/aarch64/sysregs.c b/tests/tcg/aarch64/sysregs.c
index 301e61d0dd4..8f7c5303f77 100644
--- a/tests/tcg/aarch64/sysregs.c
+++ b/tests/tcg/aarch64/sysregs.c
@@ -54,6 +54,23 @@ int failed_bit_count;
             }                                                   \
 })
 
+/* As above but also check that required fixed bits have expected values */
+#define get_cpu_reg_check_fixed(id, allowed_mask, fixed_mask, expected_val) ({ \
+            unsigned long __cval = get_cpu_reg(id);                     \
+            unsigned long __extra = __cval & ~(allowed_mask);           \
+            if (__extra) {                                              \
+                printf("%-20s: 0x%016lx\n", "  !!extra bits!!", __extra);   \
+                failed_bit_count++;                                     \
+            }                                                           \
+            if ((__cval & (fixed_mask)) != (expected_val)) {            \
+                printf("%-20s: 0x%016llx (expected fixed 0x%016llx)\n",  \
+                       "  !!fixed mismatch!!",                          \
+                       (unsigned long long)(__cval & (fixed_mask)),     \
+                       (unsigned long long)(expected_val));             \
+                failed_bit_count++;                                     \
+            }                                                           \
+})
+
 /* As above but check RAZ */
 #define get_cpu_reg_check_zero(id) ({                           \
             unsigned long __val = 0xdeadbeef;                   \
@@ -127,8 +144,16 @@ int main(void)
     get_cpu_reg_check_mask(id_aa64isar0_el1, _m(f0ff,ffff,f0ff,fff0));
     get_cpu_reg_check_mask(id_aa64isar1_el1, _m(00ff,f0ff,ffff,ffff));
     get_cpu_reg_check_mask(SYS_ID_AA64ISAR2_EL1, _m(00ff,0000,00ff,ffff));
-    /* TGran4 & TGran64 as pegged to -1 */
-    get_cpu_reg_check_mask(id_aa64mmfr0_el1, _m(f000,0000,ff00,0000));
+    /*
+     * TGran4 & TGran64 are pegged to -1 (0xf).
+     * Stage 2 translation granule support (TGran4_2, TGran64_2, TGran16_2)
+     * are pegged to safe value 1 as exposed by Linux kernel since
+     * b130a8f70cbbf9 (KVM: arm64: Check advertised Stage-2 page size capability).
+     */
+    get_cpu_reg_check_fixed(id_aa64mmfr0_el1,
+                            _m(f000, 0111, ff00, 0000),
+                            _m(0000, 0111, ff00, 0000),
+                            _m(0000, 0111, ff00, 0000));
     get_cpu_reg_check_mask(id_aa64mmfr1_el1, _m(0000,f000,0000,0000));
     get_cpu_reg_check_mask(SYS_ID_AA64MMFR2_EL1, _m(0000,000f,0000,0000));
     /* EL1/EL0 reported as AA64 only */
@@ -179,7 +204,7 @@ int main(void)
     }
 
     if (failed_bit_count > 0) {
-        printf("Extra information leaked to user-space!\n");
+        printf("Bit check failure or extra info leaked to user-space!\n");
         return 1;
     }
 
-- 
2.47.3