xref: /kvm-unit-tests/x86/asyncpf.c (revision c41e032ae372acf810ef1810b81de99c29454d78)
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 "vmalloc.h"
27 #include <stdint.h>
28 
29 #define KVM_PV_REASON_PAGE_NOT_PRESENT 1
30 #define KVM_PV_REASON_PAGE_READY 2
31 
32 #define MSR_KVM_ASYNC_PF_EN 0x4b564d02
33 
34 #define KVM_ASYNC_PF_ENABLED                    (1 << 0)
35 #define KVM_ASYNC_PF_SEND_ALWAYS                (1 << 1)
36 
37 volatile uint32_t apf_reason __attribute__((aligned(64)));
38 char *buf;
39 volatile uint64_t  i;
40 volatile uint64_t phys;
41 
42 static inline uint32_t get_apf_reason(void)
43 {
44 	uint32_t r = apf_reason;
45 	apf_reason = 0;
46 	return r;
47 }
48 
49 static void pf_isr(struct ex_regs *r)
50 {
51 	void* virt = (void*)((ulong)(buf+i) & ~(PAGE_SIZE-1));
52 	uint32_t reason = get_apf_reason();
53 
54 	switch (reason) {
55 		case 0:
56 			report("unexpected #PF at %#lx", false, read_cr2());
57 			break;
58 		case KVM_PV_REASON_PAGE_NOT_PRESENT:
59 			phys = virt_to_pte_phys(phys_to_virt(read_cr3()), virt);
60 			install_pte(phys_to_virt(read_cr3()), 1, virt, phys, 0);
61 			write_cr3(read_cr3());
62 			report("Got not present #PF token %lx virt addr %p phys addr %#" PRIx64,
63 					true, read_cr2(), virt, phys);
64 			while(phys) {
65 				safe_halt(); /* enables irq */
66 				irq_disable();
67 			}
68 			break;
69 		case KVM_PV_REASON_PAGE_READY:
70 			report("Got present #PF token %lx", true, read_cr2());
71 			if ((uint32_t)read_cr2() == ~0)
72 				break;
73 			install_pte(phys_to_virt(read_cr3()), 1, virt, phys | PT_PRESENT_MASK | PT_WRITABLE_MASK, 0);
74 			write_cr3(read_cr3());
75 			phys = 0;
76 			break;
77 		default:
78 			report("unexpected async pf reason %d", false, reason);
79 			break;
80 	}
81 }
82 
83 #define MEM 1ull*1024*1024*1024
84 
85 int main(int ac, char **av)
86 {
87 	int loop = 2;
88 
89 	setup_vm();
90 	setup_idt();
91 	printf("install handler\n");
92 	handle_exception(14, pf_isr);
93 	apf_reason = 0;
94 	printf("enable async pf\n");
95 	wrmsr(MSR_KVM_ASYNC_PF_EN, virt_to_phys((void*)&apf_reason) |
96 			KVM_ASYNC_PF_SEND_ALWAYS | KVM_ASYNC_PF_ENABLED);
97 	printf("alloc memory\n");
98 	buf = vmalloc(MEM);
99 	irq_enable();
100 	while(loop--) {
101 		printf("start loop\n");
102 		/* access a lot of memory to make host swap it out */
103 		for (i=0; i < MEM; i+=4096)
104 			buf[i] = 1;
105 		printf("end loop\n");
106 	}
107 	irq_disable();
108 
109 	return report_summary();
110 }
111