KVM: selftests: Add hugepage support for x86-64

Add x86-64 hugepage support in the form of a x86-only variant of
virt_pg_map() that takes an explicit page size.  To keep things simple,
follow the existing logic for 4k pages and disallow creating a hugepage
if the upper-level entry is present, even if the desired pfn matches.

Opportunistically fix a double "beyond beyond" reported by checkpatch.

Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210622200529.3650424-19-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Sean Christopherson 2021-06-22 13:05:28 -07:00 committed by Paolo Bonzini
parent b007e904b3
commit ad5f16e422
2 changed files with 67 additions and 24 deletions

View File

@ -412,6 +412,14 @@ struct kvm_cpuid2 *kvm_get_supported_hv_cpuid(void);
void vcpu_set_hv_cpuid(struct kvm_vm *vm, uint32_t vcpuid);
struct kvm_cpuid2 *vcpu_get_supported_hv_cpuid(struct kvm_vm *vm, uint32_t vcpuid);
enum x86_page_size {
X86_PAGE_SIZE_4K = 0,
X86_PAGE_SIZE_2M,
X86_PAGE_SIZE_1G,
};
void __virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr,
enum x86_page_size page_size);
/*
* Basic CPU control in CR0
*/

View File

@ -198,55 +198,90 @@ static void *virt_get_pte(struct kvm_vm *vm, uint64_t pt_pfn, uint64_t vaddr,
static struct pageUpperEntry *virt_create_upper_pte(struct kvm_vm *vm,
uint64_t pt_pfn,
uint64_t vaddr,
int level)
uint64_t paddr,
int level,
enum x86_page_size page_size)
{
struct pageUpperEntry *pte = virt_get_pte(vm, pt_pfn, vaddr, level);
if (!pte->present) {
pte->pfn = vm_alloc_page_table(vm) >> vm->page_shift;
pte->writable = true;
pte->present = true;
pte->page_size = (level == page_size);
if (pte->page_size)
pte->pfn = paddr >> vm->page_shift;
else
pte->pfn = vm_alloc_page_table(vm) >> vm->page_shift;
} else {
/*
* Entry already present. Assert that the caller doesn't want
* a hugepage at this level, and that there isn't a hugepage at
* this level.
*/
TEST_ASSERT(level != page_size,
"Cannot create hugepage at level: %u, vaddr: 0x%lx\n",
page_size, vaddr);
TEST_ASSERT(!pte->page_size,
"Cannot create page table at level: %u, vaddr: 0x%lx\n",
level, vaddr);
}
return pte;
}
void virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr)
void __virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr,
enum x86_page_size page_size)
{
const uint64_t pg_size = 1ull << ((page_size * 9) + 12);
struct pageUpperEntry *pml4e, *pdpe, *pde;
struct pageTableEntry *pte;
TEST_ASSERT(vm->mode == VM_MODE_PXXV48_4K, "Attempt to use "
"unknown or unsupported guest mode, mode: 0x%x", vm->mode);
TEST_ASSERT(vm->mode == VM_MODE_PXXV48_4K,
"Unknown or unsupported guest mode, mode: 0x%x", vm->mode);
TEST_ASSERT((vaddr % vm->page_size) == 0,
"Virtual address not on page boundary,\n"
" vaddr: 0x%lx vm->page_size: 0x%x",
vaddr, vm->page_size);
TEST_ASSERT(sparsebit_is_set(vm->vpages_valid,
(vaddr >> vm->page_shift)),
"Invalid virtual address, vaddr: 0x%lx",
vaddr);
TEST_ASSERT((paddr % vm->page_size) == 0,
"Physical address not on page boundary,\n"
" paddr: 0x%lx vm->page_size: 0x%x",
paddr, vm->page_size);
TEST_ASSERT((vaddr % pg_size) == 0,
"Virtual address not aligned,\n"
"vaddr: 0x%lx page size: 0x%lx", vaddr, pg_size);
TEST_ASSERT(sparsebit_is_set(vm->vpages_valid, (vaddr >> vm->page_shift)),
"Invalid virtual address, vaddr: 0x%lx", vaddr);
TEST_ASSERT((paddr % pg_size) == 0,
"Physical address not aligned,\n"
" paddr: 0x%lx page size: 0x%lx", paddr, pg_size);
TEST_ASSERT((paddr >> vm->page_shift) <= vm->max_gfn,
"Physical address beyond beyond maximum supported,\n"
"Physical address beyond maximum supported,\n"
" paddr: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x",
paddr, vm->max_gfn, vm->page_size);
/* Allocate upper level page tables, if not already present. */
pml4e = virt_create_upper_pte(vm, vm->pgd >> vm->page_shift, vaddr, 3);
pdpe = virt_create_upper_pte(vm, pml4e->pfn, vaddr, 2);
pde = virt_create_upper_pte(vm, pdpe->pfn, vaddr, 1);
/*
* Allocate upper level page tables, if not already present. Return
* early if a hugepage was created.
*/
pml4e = virt_create_upper_pte(vm, vm->pgd >> vm->page_shift,
vaddr, paddr, 3, page_size);
if (pml4e->page_size)
return;
pdpe = virt_create_upper_pte(vm, pml4e->pfn, vaddr, paddr, 2, page_size);
if (pdpe->page_size)
return;
pde = virt_create_upper_pte(vm, pdpe->pfn, vaddr, paddr, 1, page_size);
if (pde->page_size)
return;
/* Fill in page table entry. */
pte = virt_get_pte(vm, pde->pfn, vaddr, 0);
TEST_ASSERT(!pte->present,
"PTE already present for 4k page at vaddr: 0x%lx\n", vaddr);
pte->pfn = paddr >> vm->page_shift;
pte->writable = true;
pte->present = 1;
}
void virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr)
{
__virt_pg_map(vm, vaddr, paddr, X86_PAGE_SIZE_4K);
}
void virt_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent)
{
struct pageUpperEntry *pml4e, *pml4e_start;