xref: /kvm-unit-tests/x86/asyncpf.c (revision 6afb94812d924a754e2d44f6c5de9e1859b2df28) !
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_fail("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_pass("Got not present #PF token %lx virt addr %p phys addr %#" PRIx64,
65 				    read_cr2(), virt, phys);
66 			while(phys) {
67 				safe_halt(); /* enables irq */
68 				irq_disable();
69 			}
70 			break;
71 		case KVM_PV_REASON_PAGE_READY:
72 			report_pass("Got present #PF token %lx", read_cr2());
73 			if ((uint32_t)read_cr2() == ~0)
74 				break;
75 			install_pte(phys_to_virt(read_cr3()), 1, virt, phys | PT_PRESENT_MASK | PT_WRITABLE_MASK, 0);
76 			write_cr3(read_cr3());
77 			phys = 0;
78 			break;
79 		default:
80 			report_fail("unexpected async pf reason %" PRId32, reason);
81 			break;
82 	}
83 }
84 
85 #define MEM 1ull*1024*1024*1024
86 
87 int main(int ac, char **av)
88 {
89 	int loop = 2;
90 
91 	setup_vm();
92 	printf("install handler\n");
93 	handle_exception(14, pf_isr);
94 	apf_reason = 0;
95 	printf("enable async pf\n");
96 	wrmsr(MSR_KVM_ASYNC_PF_EN, virt_to_phys((void*)&apf_reason) |
97 			KVM_ASYNC_PF_SEND_ALWAYS | KVM_ASYNC_PF_ENABLED);
98 	printf("alloc memory\n");
99 	buf = malloc(MEM);
100 	irq_enable();
101 	while(loop--) {
102 		printf("start loop\n");
103 		/* access a lot of memory to make host swap it out */
104 		for (i=0; i < MEM; i+=4096)
105 			buf[i] = 1;
106 		printf("end loop\n");
107 	}
108 	irq_disable();
109 
110 	return report_summary();
111 }
112