Signed-off-by: Gleb Natapov <gleb@xxxxxxxxxx> 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..f700e9c 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 halt(void) +{ + asm volatile("hlt"); +} #endif diff --git a/lib/x86/vm.c b/lib/x86/vm.c index 550ec9b..b030934 100644 --- a/lib/x86/vm.c +++ b/lib/x86/vm.c @@ -219,6 +219,11 @@ void *vmalloc(unsigned long size) return mem; } +unsigned long 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..ff4842f 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); +unsigned long 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..f02f983 --- /dev/null +++ b/x86/asyncpf.c @@ -0,0 +1,98 @@ +/* + * 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. + */ +#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 unsigned long 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) & ~4095ul); + + switch (get_apf_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) { + irq_enable(); + halt(); + 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; + } +} + +#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