xref: /kvm-unit-tests/x86/asyncpf.c (revision 4363f1d9a646a5c7ea673bee8fc33ca6f2cddbd8)
1 /*
2  * Async PF test. For the test to actually do anything it needs to be started
3  * in memory cgroup with 512M of memory and with more then 1G memory provided
4  * to the guest.
5  *
6  * To create cgroup do as root:
7  * mkdir /dev/cgroup
8  * mount -t cgroup none -omemory /dev/cgroup
9  * chmod a+rxw /dev/cgroup/
10  *
11  * From a shell you will start qemu from:
12  * mkdir /dev/cgroup/1
13  * echo $$ >  /dev/cgroup/1/tasks
14  * echo 512M > /dev/cgroup/1/memory.limit_in_bytes
15  *
16  */
17 #include "x86/msr.h"
18 #include "x86/processor.h"
19 #include "x86/apic-defs.h"
20 #include "x86/apic.h"
21 #include "x86/desc.h"
22 #include "x86/isr.h"
23 #include "x86/vm.h"
24 
25 #include "libcflat.h"
26 #include <stdint.h>
27 
28 #define KVM_PV_REASON_PAGE_NOT_PRESENT 1
29 #define KVM_PV_REASON_PAGE_READY 2
30 
31 #define MSR_KVM_ASYNC_PF_EN 0x4b564d02
32 
33 #define KVM_ASYNC_PF_ENABLED                    (1 << 0)
34 #define KVM_ASYNC_PF_SEND_ALWAYS                (1 << 1)
35 
36 volatile uint32_t apf_reason __attribute__((aligned(64)));
37 char *buf;
38 volatile uint64_t  i;
39 volatile uint64_t phys;
40 
41 static inline uint32_t get_apf_reason(void)
42 {
43 	uint32_t r = apf_reason;
44 	apf_reason = 0;
45 	return r;
46 }
47 
48 static void pf_isr(struct ex_regs *r)
49 {
50 	void* virt = (void*)((ulong)(buf+i) & ~(PAGE_SIZE-1));
51 	uint32_t reason = get_apf_reason();
52 
53 	switch (reason) {
54 		case 0:
55 			report("unexpected #PF at %#lx", false, read_cr2());
56 			break;
57 		case KVM_PV_REASON_PAGE_NOT_PRESENT:
58 			phys = virt_to_phys_cr3(virt);
59 			install_pte(phys_to_virt(read_cr3()), 1, virt, phys, 0);
60 			write_cr3(read_cr3());
61 			report("Got not present #PF token %lx virt addr %p phys addr %#" PRIx64,
62 					true, read_cr2(), virt, phys);
63 			while(phys) {
64 				safe_halt(); /* enables irq */
65 				irq_disable();
66 			}
67 			break;
68 		case KVM_PV_REASON_PAGE_READY:
69 			report("Got present #PF token %lx", true, read_cr2());
70 			if ((uint32_t)read_cr2() == ~0)
71 				break;
72 			install_pte(phys_to_virt(read_cr3()), 1, virt, phys | PT_PRESENT_MASK | PT_WRITABLE_MASK, 0);
73 			write_cr3(read_cr3());
74 			phys = 0;
75 			break;
76 		default:
77 			report("unexpected async pf reason %d", false, reason);
78 			break;
79 	}
80 }
81 
82 #define MEM 1ull*1024*1024*1024
83 
84 int main(int ac, char **av)
85 {
86 	int loop = 2;
87 
88 	setup_vm();
89 	setup_idt();
90 	printf("install handler\n");
91 	handle_exception(14, pf_isr);
92 	apf_reason = 0;
93 	printf("enable async pf\n");
94 	wrmsr(MSR_KVM_ASYNC_PF_EN, virt_to_phys((void*)&apf_reason) |
95 			KVM_ASYNC_PF_SEND_ALWAYS | KVM_ASYNC_PF_ENABLED);
96 	printf("alloc memory\n");
97 	buf = vmalloc(MEM);
98 	irq_enable();
99 	while(loop--) {
100 		printf("start loop\n");
101 		/* access a lot of memory to make host swap it out */
102 		for (i=0; i < MEM; i+=4096)
103 			buf[i] = 1;
104 		printf("end loop\n");
105 	}
106 	irq_disable();
107 
108 	return report_summary();
109 }
110