Add p4d_index() for when 5-level paging required, i.e., V52 guest mode IDs. With the index helper function, _virt_pg_map() handles 5-level paging case, and aarch64_vcpu_setup() now is aware of the new V52 guest mode IDs.
Signed-off-by: Itaru Kitayama <[email protected]> Reviewed-by: Takayuki Okamoto <[email protected]> --- .../selftests/kvm/include/arm64/processor.h | 2 + tools/testing/selftests/kvm/lib/arm64/processor.c | 87 ++++++++++++++++++++-- 2 files changed, 84 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/kvm/include/arm64/processor.h b/tools/testing/selftests/kvm/include/arm64/processor.h index b8a902ba8573..a6fd7a9b379e 100644 --- a/tools/testing/selftests/kvm/include/arm64/processor.h +++ b/tools/testing/selftests/kvm/include/arm64/processor.h @@ -113,6 +113,7 @@ #define PTE_VALID BIT(0) #define PGD_TYPE_TABLE BIT(1) +#define P4D_TYPE_TABLE BIT(1) #define PUD_TYPE_TABLE BIT(1) #define PMD_TYPE_TABLE BIT(1) #define PTE_TYPE_PAGE BIT(1) @@ -167,6 +168,7 @@ enum { (v) == VECTOR_SYNC_LOWER_64 || \ (v) == VECTOR_SYNC_LOWER_32) +u32 aarch64_get_supported_va_size(void); void aarch64_get_supported_page_sizes(u32 ipa, u32 *ipa4k, u32 *ipa16k, u32 *ipa64k); diff --git a/tools/testing/selftests/kvm/lib/arm64/processor.c b/tools/testing/selftests/kvm/lib/arm64/processor.c index 01325bf4d36f..9108e14a9b5b 100644 --- a/tools/testing/selftests/kvm/lib/arm64/processor.c +++ b/tools/testing/selftests/kvm/lib/arm64/processor.c @@ -29,13 +29,25 @@ static u64 pgd_index(struct kvm_vm *vm, gva_t gva) return (gva >> shift) & mask; } +static u64 p4d_index(struct kvm_vm *vm, gva_t gva) +{ + unsigned int shift = 3 * (vm->page_shift - 3) + vm->page_shift; + u64 mask = (1UL << (vm->page_shift - 3)) - 1; + + TEST_ASSERT(vm->mmu.pgtable_levels == 5, + "Mode %d does not have 5 page table levels", vm->mode); + + return (gva >> shift) & mask; +} + static u64 pud_index(struct kvm_vm *vm, gva_t gva) { unsigned int shift = 2 * (vm->page_shift - 3) + vm->page_shift; u64 mask = (1UL << (vm->page_shift - 3)) - 1; - TEST_ASSERT(vm->mmu.pgtable_levels == 4, - "Mode %d does not have 4 page table levels", vm->mode); + TEST_ASSERT(vm->mmu.pgtable_levels >= 4, + "Mode %d does not have at least 4 page table levels", + vm->mode); return (gva >> shift) & mask; } @@ -147,6 +159,12 @@ static void _virt_pg_map(struct kvm_vm *vm, gva_t gva, gpa_t gpa, PGD_TYPE_TABLE | PTE_VALID); switch (vm->mmu.pgtable_levels) { + case 5: + ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) + p4d_index(vm, gva) * 8; + if (!*ptep) + *ptep = addr_pte(vm, vm_alloc_page_table(vm), + P4D_TYPE_TABLE | PTE_VALID); + /* fall through */ case 4: ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) + pud_index(vm, gva) * 8; if (!*ptep) @@ -163,7 +181,7 @@ static void _virt_pg_map(struct kvm_vm *vm, gva_t gva, gpa_t gpa, ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) + pte_index(vm, gva) * 8; break; default: - TEST_FAIL("Page table levels must be 2, 3, or 4"); + TEST_FAIL("Page table levels must be 2, 3, 4, or 5"); } pg_attr = PTE_AF | PTE_ATTRINDX(attr_idx) | PTE_TYPE_PAGE | PTE_VALID; @@ -182,18 +200,35 @@ void virt_arch_pg_map(struct kvm_vm *vm, gva_t gva, gpa_t gpa) u64 *virt_get_pte_hva_at_level(struct kvm_vm *vm, gva_t gva, int level) { + int start_level = 4 - vm->mmu.pgtable_levels; u64 *ptep; + TEST_ASSERT(level >= start_level && level <= 3, + "Invalid translation level %d, valid range is %d-3", + level, start_level); + if (!vm->mmu.pgd_created) goto unmapped_gva; ptep = addr_gpa2hva(vm, vm->mmu.pgd) + pgd_index(vm, gva) * 8; if (!ptep) goto unmapped_gva; - if (level == 0) + /* + * Stage-1 translation starts at level -1 for a five-level page + * table, and at levels 0, 1, or 2 for four-, three-, or two-level + * page tables, respectively. + */ + if (level == start_level) return ptep; switch (vm->mmu.pgtable_levels) { + case 5: + ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) + p4d_index(vm, gva) * 8; + if (!ptep) + goto unmapped_gva; + if (level == 0) + break; + /* fall through */ case 4: ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) + pud_index(vm, gva) * 8; if (!ptep) @@ -214,7 +249,7 @@ u64 *virt_get_pte_hva_at_level(struct kvm_vm *vm, gva_t gva, int level) goto unmapped_gva; break; default: - TEST_FAIL("Page table levels must be 2, 3, or 4"); + TEST_FAIL("Page table levels must be 2, 3, 4, or 5"); } return ptep; @@ -321,12 +356,14 @@ void aarch64_vcpu_setup(struct kvm_vcpu *vcpu, struct kvm_vcpu_init *init) case VM_MODE_PXXVYY_4K: TEST_FAIL("AArch64 does not support 4K sized pages " "with ANY-bit physical address ranges"); + case VM_MODE_P52V52_64K: case VM_MODE_P52V48_64K: case VM_MODE_P48V48_64K: case VM_MODE_P40V48_64K: case VM_MODE_P36V48_64K: tcr_el1 |= TCR_TG0_64K; break; + case VM_MODE_P52V52_16K: case VM_MODE_P52V48_16K: case VM_MODE_P48V48_16K: case VM_MODE_P40V48_16K: @@ -334,6 +371,7 @@ void aarch64_vcpu_setup(struct kvm_vcpu *vcpu, struct kvm_vcpu_init *init) case VM_MODE_P36V47_16K: tcr_el1 |= TCR_TG0_16K; break; + case VM_MODE_P52V52_4K: case VM_MODE_P52V48_4K: case VM_MODE_P48V48_4K: case VM_MODE_P40V48_4K: @@ -348,6 +386,9 @@ void aarch64_vcpu_setup(struct kvm_vcpu *vcpu, struct kvm_vcpu_init *init) /* Configure output size */ switch (vm->mode) { + case VM_MODE_P52V52_4K: + case VM_MODE_P52V52_16K: + case VM_MODE_P52V52_64K: case VM_MODE_P52V48_4K: case VM_MODE_P52V48_16K: case VM_MODE_P52V48_64K: @@ -578,6 +619,42 @@ static u32 max_ipa_for_page_size(u32 vm_ipa, u32 gran, return min(vm_ipa, 48U); } +u32 aarch64_get_supported_va_size(void) +{ + struct kvm_vcpu_init preferred_init = {}; + int kvm_fd, vm_fd, vcpu_fd, err; + u64 val; + u32 va_range; + struct kvm_one_reg reg = { + .id = KVM_ARM64_SYS_REG(SYS_ID_AA64MMFR2_EL1), + .addr = (u64)&val, + }; + + kvm_fd = open_kvm_dev_path_or_exit(); + vm_fd = __kvm_ioctl(kvm_fd, KVM_CREATE_VM, NULL); + TEST_ASSERT(vm_fd >= 0, KVM_IOCTL_ERROR(KVM_CREATE_VM, vm_fd)); + + vcpu_fd = ioctl(vm_fd, KVM_CREATE_VCPU, 0); + TEST_ASSERT(vcpu_fd >= 0, KVM_IOCTL_ERROR(KVM_CREATE_VCPU, vcpu_fd)); + + err = ioctl(vm_fd, KVM_ARM_PREFERRED_TARGET, &preferred_init); + TEST_ASSERT(err == 0, KVM_IOCTL_ERROR(KVM_ARM_PREFERRED_TARGET, err)); + + err = ioctl(vcpu_fd, KVM_ARM_VCPU_INIT, &preferred_init); + TEST_ASSERT(err == 0, KVM_IOCTL_ERROR(KVM_ARM_VCPU_INIT, err)); + + err = ioctl(vcpu_fd, KVM_GET_ONE_REG, ®); + TEST_ASSERT(err == 0, KVM_IOCTL_ERROR(KVM_GET_ONE_REG, err)); + + va_range = FIELD_GET(ID_AA64MMFR2_EL1_VARange, val); + + close(vcpu_fd); + close(vm_fd); + close(kvm_fd); + + return va_range >= ID_AA64MMFR2_EL1_VARange_52 ? 52 : 48; +} + void aarch64_get_supported_page_sizes(u32 ipa, u32 *ipa4k, u32 *ipa16k, u32 *ipa64k) { -- 2.43.0

