[RFC v3 02/10] KVM: selftests: Add aligned guest physical page allocator

From: Ritesh Harjani (IBM)

Date: Wed May 27 2026 - 08:55:47 EST


From: Nicholas Piggin <npiggin@xxxxxxxxx>

powerpc will require this to allocate MMU tables in guest memory that
are larger than guest base page size.

Signed-off-by: Nicholas Piggin <npiggin@xxxxxxxxx>
[Rebased to latest mainline tree]
Signed-off-by: Ritesh Harjani (IBM) <ritesh.list@xxxxxxxxx>
---
.../testing/selftests/kvm/include/kvm_util.h | 20 +++++++++--
tools/testing/selftests/kvm/lib/kvm_util.c | 33 +++++++++----------
2 files changed, 33 insertions(+), 20 deletions(-)

diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
index 3666a8530f31..c515c918c2c9 100644
--- a/tools/testing/selftests/kvm/include/kvm_util.h
+++ b/tools/testing/selftests/kvm/include/kvm_util.h
@@ -991,8 +991,8 @@ void kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing);
const char *exit_reason_str(unsigned int exit_reason);

gpa_t vm_phy_page_alloc(struct kvm_vm *vm, gpa_t min_gpa, u32 memslot);
-gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, gpa_t min_gpa,
- u32 memslot, bool protected);
+gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, size_t align,
+ gpa_t min_gpa, u32 memslot, bool protected);
gpa_t vm_alloc_page_table(struct kvm_vm *vm);

static inline gpa_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t num,
@@ -1003,10 +1003,24 @@ static inline gpa_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t num,
* protected memory, as the majority of memory for such VMs is
* protected, i.e. using shared memory is effectively opt-in.
*/
- return __vm_phy_pages_alloc(vm, num, min_gpa, memslot,
+ return __vm_phy_pages_alloc(vm, num, 1, min_gpa, memslot,
vm_arch_has_protected_memory(vm));
}

+static inline gpa_t vm_phy_pages_alloc_align(struct kvm_vm *vm, size_t num,
+ size_t align, gpa_t min_gpa,
+ u32 memslot)
+{
+ /*
+ * By default, allocate memory as protected for VMs that support
+ * protected memory, as the majority of memory for such VMs is
+ * protected, i.e. using shared memory is effectively opt-in.
+ */
+ return __vm_phy_pages_alloc(vm, num, align, min_gpa, memslot,
+ vm_arch_has_protected_memory(vm));
+}
+
+
/*
* ____vm_create() does KVM_CREATE_VM and little else. __vm_create() also
* loads the test binary into guest memory and creates an IRQ chip (x86 only).
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index e08967ef7b7b..ac7215824203 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -1442,7 +1442,7 @@ static gva_t ____vm_alloc(struct kvm_vm *vm, size_t sz, gva_t min_gva,
u64 pages = (sz >> vm->page_shift) + ((sz % vm->page_size) != 0);

virt_pgd_alloc(vm);
- gpa_t gpa = __vm_phy_pages_alloc(vm, pages,
+ gpa_t gpa = __vm_phy_pages_alloc(vm, pages, 1,
KVM_UTIL_MIN_PFN * vm->page_size,
vm->memslots[type], protected);

@@ -2021,7 +2021,7 @@ const char *exit_reason_str(unsigned int exit_reason)
* and their base address is returned. A TEST_ASSERT failure occurs if
* not enough pages are available at or above min_gpa.
*/
-gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num,
+gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, size_t align,
gpa_t min_gpa, u32 memslot,
bool protected)
{
@@ -2039,23 +2039,22 @@ gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num,
TEST_ASSERT(!protected || region->protected_phy_pages,
"Region doesn't support protected memory");

- base = pg = min_gpa >> vm->page_shift;
- do {
- for (; pg < base + num; ++pg) {
- if (!sparsebit_is_set(region->unused_phy_pages, pg)) {
- base = pg = sparsebit_next_set(region->unused_phy_pages, pg);
- break;
+ base = min_gpa >> vm->page_shift;
+again:
+ base = (base + align - 1) & ~(align - 1);
+ for (pg = base; pg < base + num; ++pg) {
+ if (!sparsebit_is_set(region->unused_phy_pages, pg)) {
+ base = sparsebit_next_set(region->unused_phy_pages, pg);
+ if (!base) {
+ fprintf(stderr, "No guest physical page available, "
+ "min_gpa: 0x%lx page_size: 0x%x memslot: %u\n",
+ min_gpa, vm->page_size, memslot);
+ fputs("---- vm dump ----\n", stderr);
+ vm_dump(stderr, vm, 2);
+ abort();
}
+ goto again;
}
- } while (pg && pg != base + num);
-
- if (pg == 0) {
- fprintf(stderr, "No guest physical page available, "
- "min_gpa: 0x%lx page_size: 0x%x memslot: %u\n",
- min_gpa, vm->page_size, memslot);
- fputs("---- vm dump ----\n", stderr);
- vm_dump(stderr, vm, 2);
- abort();
}

for (pg = base; pg < base + num; ++pg) {
--
2.39.5