109 lines
2.6 KiB
C
109 lines
2.6 KiB
C
/*
|
|
* 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 <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;
|
|
|
|
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();
|
|
}
|