xref: /kvm-unit-tests/x86/asyncpf.c (revision e521b10727bd6233dfc94b1ef4058a0d19b791e3)
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 than 1G memory provided
4  * to the guest.
5  *
6  * To identify the cgroup version on Linux:
7  * stat -fc %T /sys/fs/cgroup/
8  *
9  * If the output is tmpfs, your system is using cgroup v1:
10  * To create cgroup do as root:
11  * mkdir /dev/cgroup
12  * mount -t cgroup none -omemory /dev/cgroup
13  * chmod a+rxw /dev/cgroup/
14  *
15  * From a shell you will start qemu from:
16  * mkdir /dev/cgroup/1
17  * echo $$ >  /dev/cgroup/1/tasks
18  * echo 512M > /dev/cgroup/1/memory.limit_in_bytes
19  *
20  * If the output is cgroup2fs, your system is using cgroup v2:
21  * mkdir /sys/fs/cgroup/cg1
22  * echo $$ >  /sys/fs/cgroup/cg1/cgroup.procs
23  * echo 512M > /sys/fs/cgroup/cg1/memory.max
24  *
25  */
26 #include "x86/processor.h"
27 #include "x86/apic.h"
28 #include "x86/isr.h"
29 #include "x86/vm.h"
30 #include "alloc.h"
31 #include "vmalloc.h"
32 
33 #define KVM_PV_REASON_PAGE_NOT_PRESENT 1
34 
35 #define MSR_KVM_ASYNC_PF_EN 0x4b564d02
36 #define MSR_KVM_ASYNC_PF_INT    0x4b564d06
37 #define MSR_KVM_ASYNC_PF_ACK    0x4b564d07
38 
39 #define KVM_ASYNC_PF_ENABLED                    (1 << 0)
40 #define KVM_ASYNC_PF_SEND_ALWAYS                (1 << 1)
41 #define KVM_ASYNC_PF_DELIVERY_AS_INT            (1 << 3)
42 
43 #define HYPERVISOR_CALLBACK_VECTOR	0xf3
44 
45 struct kvm_vcpu_pv_apf_data {
46       /* Used for 'page not present' events delivered via #PF */
47       uint32_t  flags;
48 
49       /* Used for 'page ready' events delivered via interrupt notification */
50       uint32_t  token;
51 
52       uint8_t  pad[56];
53       uint32_t  enabled;
54 } apf_reason __attribute__((aligned(64)));
55 
56 char *buf;
57 void* virt;
58 volatile uint64_t  i;
59 volatile uint64_t phys;
60 volatile uint32_t saved_token;
61 volatile unsigned asyncpf_num;
62 
63 static inline uint32_t get_and_clear_apf_reason(void)
64 {
65 	uint32_t r = apf_reason.flags;
66 	apf_reason.flags = 0;
67 	return r;
68 }
69 
70 static void handle_interrupt(isr_regs_t *regs)
71 {
72 	uint32_t apf_token = apf_reason.token;
73 
74 	apf_reason.token = 0;
75 	wrmsr(MSR_KVM_ASYNC_PF_ACK, 1);
76 
77 	if (apf_token == 0xffffffff) {
78 		report_pass("Wakeup all, got token 0x%" PRIx32, apf_token);
79 	} else if (apf_token == saved_token) {
80 		asyncpf_num++;
81 		install_pte(phys_to_virt(read_cr3()), 1, virt, phys | PT_PRESENT_MASK | PT_WRITABLE_MASK, 0);
82 		phys = 0;
83 	} else {
84 		report_fail("unexpected async pf int token 0x%" PRIx32, apf_token);
85 	}
86 
87 	eoi();
88 }
89 
90 static void handle_pf(struct ex_regs *r)
91 {
92 	virt = (void*)((ulong)(buf+i) & ~(PAGE_SIZE-1));
93 	uint32_t reason = get_and_clear_apf_reason();
94 	switch (reason) {
95 	case 0:
96 		report_fail("unexpected #PF at %#lx", read_cr2());
97 		exit(report_summary());
98 	case KVM_PV_REASON_PAGE_NOT_PRESENT:
99 		phys = virt_to_pte_phys(phys_to_virt(read_cr3()), virt);
100 		install_pte(phys_to_virt(read_cr3()), 1, virt, phys, 0);
101 		write_cr3(read_cr3());
102 		saved_token = read_cr2();
103 		while (phys) {
104 			safe_halt(); /* enables irq */
105 		}
106 		break;
107 	default:
108 		report_fail("unexpected async pf with reason 0x%" PRIx32, reason);
109 		exit(report_summary());
110 	}
111 }
112 
113 #define MEM (1ull*1024*1024*1024)
114 
115 int main(int ac, char **av)
116 {
117 	if (!this_cpu_has(KVM_FEATURE_ASYNC_PF)) {
118 		report_skip("KVM_FEATURE_ASYNC_PF is not supported\n");
119 		return report_summary();
120 	}
121 
122 	if (!this_cpu_has(KVM_FEATURE_ASYNC_PF_INT)) {
123 		report_skip("KVM_FEATURE_ASYNC_PF_INT is not supported\n");
124 		return report_summary();
125 	}
126 
127 	setup_vm();
128 
129 	handle_exception(PF_VECTOR, handle_pf);
130 	handle_irq(HYPERVISOR_CALLBACK_VECTOR, handle_interrupt);
131 	memset(&apf_reason, 0, sizeof(apf_reason));
132 
133 	wrmsr(MSR_KVM_ASYNC_PF_INT, HYPERVISOR_CALLBACK_VECTOR);
134 	wrmsr(MSR_KVM_ASYNC_PF_EN, virt_to_phys((void*)&apf_reason) |
135 			KVM_ASYNC_PF_SEND_ALWAYS | KVM_ASYNC_PF_ENABLED | KVM_ASYNC_PF_DELIVERY_AS_INT);
136 
137 	buf = malloc(MEM);
138 	sti();
139 
140 	/* access a lot of memory to make host swap it out */
141 	for (i = 0; i < MEM; i += 4096)
142 		buf[i] = 1;
143 
144 	cli();
145 	if (!asyncpf_num)
146 		report_skip("No async page fault events, cgroup configuration likely needed");
147 	else
148 		report_pass("Serviced %d async page faults events (!PRESENT #PF + READY IRQ)",
149 			    asyncpf_num);
150 	return report_summary();
151 }
152