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