xref: /kvm-unit-tests/x86/asyncpf.c (revision 8f4755faa9a9ded7731b6825ac5d236251680229)
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 bool fail;
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 			printf("unexpected #PF at %p\n", read_cr2());
57 			fail = true;
58 			break;
59 		case KVM_PV_REASON_PAGE_NOT_PRESENT:
60 			phys = virt_to_phys_cr3(virt);
61 			install_pte(phys_to_virt(read_cr3()), 1, virt, phys, 0);
62 			write_cr3(read_cr3());
63 			printf("Got not present #PF token %x virt addr %p phys addr %p\n", 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 			printf("Got present #PF token %x\n", read_cr2());
71 			if ((uint32_t)read_cr2() == ~0)
72 				break;
73 			install_pte(phys_to_virt(read_cr3()), 1, virt, phys | PTE_PRESENT | PTE_WRITE, 0);
74 			write_cr3(read_cr3());
75 			phys = 0;
76 			break;
77 		default:
78 			printf("unexpected async pf reason %d\n", reason);
79 			fail = true;
80 			break;
81 	}
82 }
83 
84 #define MEM 1ull*1024*1024*1024
85 
86 int main(int ac, char **av)
87 {
88 	int loop = 2;
89 
90 	setup_vm();
91 	setup_idt();
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 = vmalloc(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 	printf("%s\n", fail ? "FAIL" : "PASS");
111 	return fail;
112 }
113