* [PATCH 1/2] KVM: selftest: arm64: Support 5-level paging in stage 1 translation table
2026-08-25 21:18 [PATCH 0/2] 52-bit VA guest mode ID support Itaru Kitayama
@ 2026-08-25 21:18 ` Itaru Kitayama
2026-08-25 21:35 ` sashiko-bot
2026-08-25 21:18 ` [PATCH 2/2] KVM: selftests: arm64: Add 52-bit VA guest modes Itaru Kitayama
1 sibling, 1 reply; 5+ messages in thread
From: Itaru Kitayama @ 2026-08-25 21:18 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Paolo Bonzini, Shuah Khan
Cc: linux-arm-kernel, kvmarm, kvm, linux-kselftest, linux-kernel,
Itaru Kitayama, Takayuki Okamoto
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 <itaru.kitayama@fujitsu.com>
Reviewed-by: Takayuki Okamoto <tokamoto@fujitsu.com>
---
.../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
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 2/2] KVM: selftests: arm64: Add 52-bit VA guest modes
2026-08-25 21:18 [PATCH 0/2] 52-bit VA guest mode ID support Itaru Kitayama
2026-08-25 21:18 ` [PATCH 1/2] KVM: selftest: arm64: Support 5-level paging in stage 1 translation table Itaru Kitayama
@ 2026-08-25 21:18 ` Itaru Kitayama
2026-08-25 21:32 ` sashiko-bot
1 sibling, 1 reply; 5+ messages in thread
From: Itaru Kitayama @ 2026-08-25 21:18 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Paolo Bonzini, Shuah Khan
Cc: linux-arm-kernel, kvmarm, kvm, linux-kselftest, linux-kernel,
Itaru Kitayama, Takayuki Okamoto
Add P52V52 guest mode IDs for the 4K, 16K and 64K translation
granules, along with their mode descriptions and parameters.
Advertise the 4KB and 16KB modes when the corresponding granule supports
a 52-bit IPA, implying FEAT_LPA2 support. For the 64K mode, also require
52-bit VA support.
This makes the 52-bit VA configurations available to KVM selftests now
that the arm64 page-table code supports 5-level stage-1 translation.
Signed-off-by: Itaru Kitayama <itaru.kitayama@fujitsu.com>
Reviewed-by: Takayuki Okamoto <tokamoto@fujitsu.com>
---
tools/testing/selftests/kvm/include/kvm_util.h | 3 +++
tools/testing/selftests/kvm/lib/arm64/processor.c | 2 +-
tools/testing/selftests/kvm/lib/guest_modes.c | 5 +++++
tools/testing/selftests/kvm/lib/kvm_util.c | 11 +++++++++++
4 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
index 04a910164a29..9040281c0992 100644
--- a/tools/testing/selftests/kvm/include/kvm_util.h
+++ b/tools/testing/selftests/kvm/include/kvm_util.h
@@ -184,6 +184,9 @@ enum vm_guest_mode {
VM_MODE_P52V48_4K,
VM_MODE_P52V48_16K,
VM_MODE_P52V48_64K,
+ VM_MODE_P52V52_4K,
+ VM_MODE_P52V52_16K,
+ VM_MODE_P52V52_64K,
VM_MODE_P48V48_4K,
VM_MODE_P48V48_16K,
VM_MODE_P48V48_64K,
diff --git a/tools/testing/selftests/kvm/lib/arm64/processor.c b/tools/testing/selftests/kvm/lib/arm64/processor.c
index 9108e14a9b5b..bcdf670c9eb8 100644
--- a/tools/testing/selftests/kvm/lib/arm64/processor.c
+++ b/tools/testing/selftests/kvm/lib/arm64/processor.c
@@ -46,7 +46,7 @@ static u64 pud_index(struct kvm_vm *vm, gva_t gva)
u64 mask = (1UL << (vm->page_shift - 3)) - 1;
TEST_ASSERT(vm->mmu.pgtable_levels >= 4,
- "Mode %d does not have at least 4 page table levels",
+ "Mode %d does not have >= 4 page table levels",
vm->mode);
return (gva >> shift) & mask;
diff --git a/tools/testing/selftests/kvm/lib/guest_modes.c b/tools/testing/selftests/kvm/lib/guest_modes.c
index 7a96c43b5704..4f121e2987e9 100644
--- a/tools/testing/selftests/kvm/lib/guest_modes.c
+++ b/tools/testing/selftests/kvm/lib/guest_modes.c
@@ -21,10 +21,15 @@ void guest_modes_append_default(void)
{
unsigned int limit = kvm_check_cap(KVM_CAP_ARM_VM_IPA_SIZE);
u32 ipa4k, ipa16k, ipa64k;
+ u32 va;
int i;
aarch64_get_supported_page_sizes(limit, &ipa4k, &ipa16k, &ipa64k);
+ va = aarch64_get_supported_va_size();
+ guest_mode_append(VM_MODE_P52V52_4K, ipa4k >= 52);
+ guest_mode_append(VM_MODE_P52V52_16K, ipa16k >= 52);
+ guest_mode_append(VM_MODE_P52V52_64K, ipa64k >= 52 && va >= 52);
guest_mode_append(VM_MODE_P52V48_4K, ipa4k >= 52);
guest_mode_append(VM_MODE_P52V48_16K, ipa16k >= 52);
guest_mode_append(VM_MODE_P52V48_64K, ipa64k >= 52);
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index 195f3fdae1e3..3fbc5b0d75ff 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -195,6 +195,9 @@ const char *vm_guest_mode_string(u32 i)
[VM_MODE_P52V48_4K] = "PA-bits:52, VA-bits:48, 4K pages",
[VM_MODE_P52V48_16K] = "PA-bits:52, VA-bits:48, 16K pages",
[VM_MODE_P52V48_64K] = "PA-bits:52, VA-bits:48, 64K pages",
+ [VM_MODE_P52V52_4K] = "PA-bits:52, VA-bits:52, 4K pages",
+ [VM_MODE_P52V52_16K] = "PA-bits:52, VA-bits:52, 16K pages",
+ [VM_MODE_P52V52_64K] = "PA-bits:52, VA-bits:52, 64K pages",
[VM_MODE_P48V48_4K] = "PA-bits:48, VA-bits:48, 4K pages",
[VM_MODE_P48V48_16K] = "PA-bits:48, VA-bits:48, 16K pages",
[VM_MODE_P48V48_64K] = "PA-bits:48, VA-bits:48, 64K pages",
@@ -231,6 +234,9 @@ const struct vm_guest_mode_params vm_guest_mode_params[] = {
[VM_MODE_P52V48_4K] = { 52, 48, 0x1000, 12 },
[VM_MODE_P52V48_16K] = { 52, 48, 0x4000, 14 },
[VM_MODE_P52V48_64K] = { 52, 48, 0x10000, 16 },
+ [VM_MODE_P52V52_4K] = { 52, 52, 0x1000, 12 },
+ [VM_MODE_P52V52_16K] = { 52, 52, 0x4000, 14 },
+ [VM_MODE_P52V52_64K] = { 52, 52, 0x10000, 16 },
[VM_MODE_P48V48_4K] = { 48, 48, 0x1000, 12 },
[VM_MODE_P48V48_16K] = { 48, 48, 0x4000, 14 },
[VM_MODE_P48V48_64K] = { 48, 48, 0x10000, 16 },
@@ -298,10 +304,14 @@ struct kvm_vm *____vm_create(struct vm_shape shape)
/* Setup mode specific traits. */
switch (vm->mode) {
+ case VM_MODE_P52V52_4K:
+ vm->mmu.pgtable_levels = 5;
+ break;
case VM_MODE_P52V48_4K:
vm->mmu.pgtable_levels = 4;
break;
case VM_MODE_P52V48_64K:
+ case VM_MODE_P52V52_64K:
vm->mmu.pgtable_levels = 3;
break;
case VM_MODE_P48V48_4K:
@@ -322,6 +332,7 @@ struct kvm_vm *____vm_create(struct vm_shape shape)
case VM_MODE_P48V48_16K:
case VM_MODE_P40V48_16K:
case VM_MODE_P36V48_16K:
+ case VM_MODE_P52V52_16K:
vm->mmu.pgtable_levels = 4;
break;
case VM_MODE_P47V47_16K:
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread