KVM: selftests: arm64: Determine max ipa size per-page size
authorRyan Roberts <ryan.roberts@arm.com>
Mon, 27 Nov 2023 11:17:36 +0000 (11:17 +0000)
committerMarc Zyngier <maz@kernel.org>
Mon, 27 Nov 2023 15:03:50 +0000 (15:03 +0000)
We are about to add 52 bit PA guest modes for 4K and 16K pages when the
system supports LPA2. In preparation beef up the logic that parses mmfr0
to also tell us what the maximum supported PA size is for each page
size. Max PA size = 0 implies the page size is not supported at all.

Reviewed-by: Oliver Upton <oliver.upton@linux.dev>
Signed-off-by: Ryan Roberts <ryan.roberts@arm.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20231127111737.1897081-12-ryan.roberts@arm.com
tools/testing/selftests/kvm/include/aarch64/processor.h
tools/testing/selftests/kvm/include/guest_modes.h
tools/testing/selftests/kvm/lib/aarch64/processor.c
tools/testing/selftests/kvm/lib/guest_modes.c

index c42d683102c7a477ddca956285d8854bd44fa7c6..cf20e44e86f2f9fb7feeccf88fdc93fecd7fbfd2 100644 (file)
@@ -119,8 +119,8 @@ enum {
 /* Access flag update enable/disable */
 #define TCR_EL1_HA             (1ULL << 39)
 
-void aarch64_get_supported_page_sizes(uint32_t ipa,
-                                     bool *ps4k, bool *ps16k, bool *ps64k);
+void aarch64_get_supported_page_sizes(uint32_t ipa, uint32_t *ipa4k,
+                                       uint32_t *ipa16k, uint32_t *ipa64k);
 
 void vm_init_descriptor_tables(struct kvm_vm *vm);
 void vcpu_init_descriptor_tables(struct kvm_vcpu *vcpu);
index b691df33e64e122a87996ef5cc17e8aceead4685..63f5167397ccb4bf9ed07ce90b99cc89b36bd20c 100644 (file)
@@ -11,8 +11,8 @@ struct guest_mode {
 
 extern struct guest_mode guest_modes[NUM_VM_MODES];
 
-#define guest_mode_append(mode, supported, enabled) ({ \
-       guest_modes[mode] = (struct guest_mode){ supported, enabled }; \
+#define guest_mode_append(mode, enabled) ({ \
+       guest_modes[mode] = (struct guest_mode){ (enabled), (enabled) }; \
 })
 
 void guest_modes_append_default(void);
index 6fe12e985ba568bea7b2a7829730f503b674af76..e6ffd9037c373ca226b71a8e1364b6bbe931eca6 100644 (file)
@@ -492,12 +492,24 @@ uint32_t guest_get_vcpuid(void)
        return read_sysreg(tpidr_el1);
 }
 
-void aarch64_get_supported_page_sizes(uint32_t ipa,
-                                     bool *ps4k, bool *ps16k, bool *ps64k)
+static uint32_t max_ipa_for_page_size(uint32_t vm_ipa, uint32_t gran,
+                               uint32_t not_sup_val, uint32_t ipa52_min_val)
+{
+       if (gran == not_sup_val)
+               return 0;
+       else if (gran >= ipa52_min_val && vm_ipa >= 52)
+               return 52;
+       else
+               return min(vm_ipa, 48U);
+}
+
+void aarch64_get_supported_page_sizes(uint32_t ipa, uint32_t *ipa4k,
+                                       uint32_t *ipa16k, uint32_t *ipa64k)
 {
        struct kvm_vcpu_init preferred_init;
        int kvm_fd, vm_fd, vcpu_fd, err;
        uint64_t val;
+       uint32_t gran;
        struct kvm_one_reg reg = {
                .id     = KVM_ARM64_SYS_REG(SYS_ID_AA64MMFR0_EL1),
                .addr   = (uint64_t)&val,
@@ -518,9 +530,17 @@ void aarch64_get_supported_page_sizes(uint32_t ipa,
        err = ioctl(vcpu_fd, KVM_GET_ONE_REG, &reg);
        TEST_ASSERT(err == 0, KVM_IOCTL_ERROR(KVM_GET_ONE_REG, vcpu_fd));
 
-       *ps4k = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_TGRAN4), val) != 0xf;
-       *ps64k = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_TGRAN64), val) == 0;
-       *ps16k = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_TGRAN16), val) != 0;
+       gran = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_TGRAN4), val);
+       *ipa4k = max_ipa_for_page_size(ipa, gran, ID_AA64MMFR0_EL1_TGRAN4_NI,
+                                       ID_AA64MMFR0_EL1_TGRAN4_52_BIT);
+
+       gran = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_TGRAN64), val);
+       *ipa64k = max_ipa_for_page_size(ipa, gran, ID_AA64MMFR0_EL1_TGRAN64_NI,
+                                       ID_AA64MMFR0_EL1_TGRAN64_IMP);
+
+       gran = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_TGRAN16), val);
+       *ipa16k = max_ipa_for_page_size(ipa, gran, ID_AA64MMFR0_EL1_TGRAN16_NI,
+                                       ID_AA64MMFR0_EL1_TGRAN16_52_BIT);
 
        close(vcpu_fd);
        close(vm_fd);
index 1df3ce4b16fd86a3f1750700d1392872a60cbf92..772a7dd15db48d8d48b8d17f2f15b905bcef101d 100644 (file)
@@ -14,37 +14,31 @@ struct guest_mode guest_modes[NUM_VM_MODES];
 void guest_modes_append_default(void)
 {
 #ifndef __aarch64__
-       guest_mode_append(VM_MODE_DEFAULT, true, true);
+       guest_mode_append(VM_MODE_DEFAULT, true);
 #else
        {
                unsigned int limit = kvm_check_cap(KVM_CAP_ARM_VM_IPA_SIZE);
-               bool ps4k, ps16k, ps64k;
+               uint32_t ipa4k, ipa16k, ipa64k;
                int i;
 
-               aarch64_get_supported_page_sizes(limit, &ps4k, &ps16k, &ps64k);
+               aarch64_get_supported_page_sizes(limit, &ipa4k, &ipa16k, &ipa64k);
 
-               vm_mode_default = NUM_VM_MODES;
+               guest_mode_append(VM_MODE_P52V48_64K, ipa64k >= 52);
 
-               if (limit >= 52)
-                       guest_mode_append(VM_MODE_P52V48_64K, ps64k, ps64k);
-               if (limit >= 48) {
-                       guest_mode_append(VM_MODE_P48V48_4K, ps4k, ps4k);
-                       guest_mode_append(VM_MODE_P48V48_16K, ps16k, ps16k);
-                       guest_mode_append(VM_MODE_P48V48_64K, ps64k, ps64k);
-               }
-               if (limit >= 40) {
-                       guest_mode_append(VM_MODE_P40V48_4K, ps4k, ps4k);
-                       guest_mode_append(VM_MODE_P40V48_16K, ps16k, ps16k);
-                       guest_mode_append(VM_MODE_P40V48_64K, ps64k, ps64k);
-                       if (ps4k)
-                               vm_mode_default = VM_MODE_P40V48_4K;
-               }
-               if (limit >= 36) {
-                       guest_mode_append(VM_MODE_P36V48_4K, ps4k, ps4k);
-                       guest_mode_append(VM_MODE_P36V48_16K, ps16k, ps16k);
-                       guest_mode_append(VM_MODE_P36V48_64K, ps64k, ps64k);
-                       guest_mode_append(VM_MODE_P36V47_16K, ps16k, ps16k);
-               }
+               guest_mode_append(VM_MODE_P48V48_4K, ipa4k >= 48);
+               guest_mode_append(VM_MODE_P48V48_16K, ipa16k >= 48);
+               guest_mode_append(VM_MODE_P48V48_64K, ipa64k >= 48);
+
+               guest_mode_append(VM_MODE_P40V48_4K, ipa4k >= 40);
+               guest_mode_append(VM_MODE_P40V48_16K, ipa16k >= 40);
+               guest_mode_append(VM_MODE_P40V48_64K, ipa64k >= 40);
+
+               guest_mode_append(VM_MODE_P36V48_4K, ipa4k >= 36);
+               guest_mode_append(VM_MODE_P36V48_16K, ipa16k >= 36);
+               guest_mode_append(VM_MODE_P36V48_64K, ipa64k >= 36);
+               guest_mode_append(VM_MODE_P36V47_16K, ipa16k >= 36);
+
+               vm_mode_default = ipa4k >= 40 ? VM_MODE_P40V48_4K : NUM_VM_MODES;
 
                /*
                 * Pick the first supported IPA size if the default
@@ -72,7 +66,7 @@ void guest_modes_append_default(void)
                close(kvm_fd);
                /* Starting with z13 we have 47bits of physical address */
                if (info.ibc >= 0x30)
-                       guest_mode_append(VM_MODE_P47V64_4K, true, true);
+                       guest_mode_append(VM_MODE_P47V64_4K, true);
        }
 #endif
 #ifdef __riscv
@@ -80,9 +74,9 @@ void guest_modes_append_default(void)
                unsigned int sz = kvm_check_cap(KVM_CAP_VM_GPA_BITS);
 
                if (sz >= 52)
-                       guest_mode_append(VM_MODE_P52V48_4K, true, true);
+                       guest_mode_append(VM_MODE_P52V48_4K, true);
                if (sz >= 48)
-                       guest_mode_append(VM_MODE_P48V48_4K, true, true);
+                       guest_mode_append(VM_MODE_P48V48_4K, true);
        }
 #endif
 }