Add support for populating and walking five-level stage-1 page tables. A five-level translation starts at architectural level -1, so extend the page-table helpers and level validation accordingly.
This enables the page-table infrastructure required by 52-bit VA configurations using the 4K translation granule. Signed-off-by: Itaru Kitayama <[email protected]> Reviewed-by: Takayuki Okamoto <[email protected]> --- .../selftests/kvm/include/arm64/processor.h | 1 + tools/testing/selftests/kvm/lib/arm64/processor.c | 45 +++++++++++++++++++--- 2 files changed, 41 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..7457503df956 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) diff --git a/tools/testing/selftests/kvm/lib/arm64/processor.c b/tools/testing/selftests/kvm/lib/arm64/processor.c index 01325bf4d36f..b589368bf822 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 >= 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; -- 2.43.0

