/* * Async PF test. For the test to actually do anything it needs 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 #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; 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: report("unexpected #PF at %#lx", false, read_cr2()); 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()); report("Got not present #PF token %lx virt addr %p phys addr %#" PRIx64, true, read_cr2(), virt, phys); while(phys) { safe_halt(); /* enables irq */ irq_disable(); } break; case KVM_PV_REASON_PAGE_READY: report("Got present #PF token %lx", true, read_cr2()); if ((uint32_t)read_cr2() == ~0) break; install_pte(phys_to_virt(read_cr3()), 1, virt, phys | PT_PRESENT_MASK | PT_WRITABLE_MASK, 0); write_cr3(read_cr3()); phys = 0; break; default: report("unexpected async pf reason %d", false, reason); break; } } #define MEM 1ull*1024*1024*1024 int main(int ac, char **av) { int loop = 2; setup_vm(); setup_idt(); 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(); return report_summary(); }