The test checks that when hypervisor prefaults swapped in page it does not generates spurious #PFs in case page was unmapped while it was swapped out. diff --git a/config-x86-common.mak b/config-x86-common.mak index c8fbda7..6976f78 100644 --- a/config-x86-common.mak +++ b/config-x86-common.mak @@ -34,7 +34,7 @@ tests-common = $(TEST_DIR)/vmexit.flat $(TEST_DIR)/tsc.flat \ $(TEST_DIR)/realmode.flat $(TEST_DIR)/msr.flat \ $(TEST_DIR)/hypercall.flat $(TEST_DIR)/sieve.flat \ $(TEST_DIR)/kvmclock_test.flat $(TEST_DIR)/eventinj.flat \ - $(TEST_DIR)/s3.flat $(TEST_DIR)/pmu.flat + $(TEST_DIR)/s3.flat $(TEST_DIR)/pmu.flat $(TEST_DIR)/asyncpf.flat ifdef API tests-common += api/api-sample @@ -90,6 +90,8 @@ $(TEST_DIR)/s3.elf: $(cstart.o) $(TEST_DIR)/s3.o $(TEST_DIR)/pmu.elf: $(cstart.o) $(TEST_DIR)/pmu.o +$(TEST_DIR)/asyncpf.elf: $(cstart.o) $(TEST_DIR)/asyncpf.o + arch_clean: $(RM) $(TEST_DIR)/*.o $(TEST_DIR)/*.flat $(TEST_DIR)/*.elf \ $(TEST_DIR)/.*.d $(TEST_DIR)/lib/.*.d $(TEST_DIR)/lib/*.o diff --git a/lib/x86/processor.h b/lib/x86/processor.h index c7e1afb..e46d8d0 100644 --- a/lib/x86/processor.h +++ b/lib/x86/processor.h @@ -302,4 +302,9 @@ static inline void invlpg(void *va) { asm volatile("invlpg (%0)" ::"r" (va) : "memory"); } + +static inline void safe_halt(void) +{ + asm volatile("sti; hlt"); +} #endif diff --git a/lib/x86/vm.c b/lib/x86/vm.c index 550ec9b..71b70fd 100644 --- a/lib/x86/vm.c +++ b/lib/x86/vm.c @@ -219,6 +219,11 @@ void *vmalloc(unsigned long size) return mem; } +uint64_t virt_to_phys_cr3(void *mem) +{ + return (get_pte(phys_to_virt(read_cr3()), mem) & PTE_ADDR) + ((ulong)mem & (PAGE_SIZE - 1)); +} + void vfree(void *mem) { unsigned long size = ((unsigned long *)mem)[-1]; diff --git a/lib/x86/vm.h b/lib/x86/vm.h index 71ab4a8..3473f8d 100644 --- a/lib/x86/vm.h +++ b/lib/x86/vm.h @@ -22,6 +22,7 @@ void vfree(void *mem); void *vmap(unsigned long long phys, unsigned long size); void *alloc_vpage(void); void *alloc_vpages(ulong nr); +uint64_t virt_to_phys_cr3(void *mem); void install_pte(unsigned long *cr3, int pte_level, diff --git a/x86/asyncpf.c b/x86/asyncpf.c new file mode 100644 index 0000000..62c0455 --- /dev/null +++ b/x86/asyncpf.c @@ -0,0 +1,113 @@ +/* + * Async PF test. For the test to actually do anywathing it ineeds to be started + * in memory cgroup with 512M of memory and with more then 1G memory provided + * to the guest. + * + * To create cgroup do as root: + * mkdir /dev/cgroup + * mount -t cgroup none -omemory /dev/cgroup + * chmod a+rxw /dev/cgroup/ + * + * From a shell you will start qemu from: + * mkdir /dev/cgroup/1 + * echo $$ > /dev/cgroup/1/tasks + * echo 512M > /dev/cgroup/1/memory.limit_in_bytes + * + */ +#include "x86/msr.h" +#include "x86/processor.h" +#include "x86/apic-defs.h" +#include "x86/apic.h" +#include "x86/desc.h" +#include "x86/isr.h" +#include "x86/vm.h" + +#include "libcflat.h" +#include <stdint.h> + +#define KVM_PV_REASON_PAGE_NOT_PRESENT 1 +#define KVM_PV_REASON_PAGE_READY 2 + +#define MSR_KVM_ASYNC_PF_EN 0x4b564d02 + +#define KVM_ASYNC_PF_ENABLED (1 << 0) +#define KVM_ASYNC_PF_SEND_ALWAYS (1 << 1) + +volatile uint32_t apf_reason __attribute__((aligned(64))); +char *buf; +volatile uint64_t i; +volatile uint64_t phys; +bool fail; + +static inline uint32_t get_apf_reason(void) +{ + uint32_t r = apf_reason; + apf_reason = 0; + return r; +} + +static void pf_isr(struct ex_regs *r) +{ + void* virt = (void*)((ulong)(buf+i) & ~(PAGE_SIZE-1)); + uint32_t reason = get_apf_reason(); + + switch (reason) { + case 0: + printf("unexpected #PF at %p\n", read_cr2()); + fail = true; + break; + case KVM_PV_REASON_PAGE_NOT_PRESENT: + phys = virt_to_phys_cr3(virt); + install_pte(phys_to_virt(read_cr3()), 1, virt, phys, 0); + write_cr3(read_cr3()); + printf("Got not present #PF token %x virt addr %p phys addr %p\n", read_cr2(), virt, phys); + while(phys) { + safe_halt(); /* enables irq */ + irq_disable(); + } + break; + case KVM_PV_REASON_PAGE_READY: + printf("Got present #PF token %x\n", read_cr2()); + if ((uint32_t)read_cr2() == ~0) + break; + install_pte(phys_to_virt(read_cr3()), 1, virt, phys | PTE_PRESENT | PTE_WRITE, 0); + write_cr3(read_cr3()); + phys = 0; + break; + default: + printf("unexpected async pf reason %d\n", reason); + fail = true; + break; + } +} + +#define MEM 1ull*1024*1024*1024 + +int main(int ac, char **av) +{ + int loop = 2; + + setup_vm(); + setup_idt(); + setup_gdt(); + printf("install handler\n"); + handle_exception(14, pf_isr); + apf_reason = 0; + printf("enable async pf\n"); + wrmsr(MSR_KVM_ASYNC_PF_EN, virt_to_phys((void*)&apf_reason) | + KVM_ASYNC_PF_SEND_ALWAYS | KVM_ASYNC_PF_ENABLED); + printf("alloc memory\n"); + buf = vmalloc(MEM); + irq_enable(); + while(loop--) { + printf("start loop\n"); + /* access a lot of memory to make host swap it out */ + for (i=0; i < MEM; i+=4096) + buf[i] = 1; + printf("end loop\n"); + } + irq_disable(); + + printf("%s\n", fail ? "FAIL" : "PASS"); + return fail; +} -- Gleb. -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html