]> rtime.felk.cvut.cz Git - hercules2020/jailhouse-build.git/blob - test/memguard-test.c
934e1bfd0cb99c12933bd23064cd3571a3d7f21d
[hercules2020/jailhouse-build.git] / test / memguard-test.c
1 #define _GNU_SOURCE         /* See feature_test_macros(7) */
2 #include <err.h>
3 #include <sched.h>
4 #include <stdint.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <time.h>
9 #include <inttypes.h>
10 #include <sys/mman.h>
11 #include <stdbool.h>
12 #include <pthread.h>
13 #include <stdlib.h>
14 #include <error.h>
15
16 #ifndef SYS_memguard
17 #define SYS_memguard 793
18 #endif
19
20 /* Return value:
21  * 0-31  - Sum of memory events
22  * 32-55 - Total time
23  * 61    - Memory budget overrun
24  * 62    - Timeout
25  * 63    - Error */
26 #define MGRET_MEM_POS           0
27 #define MGRET_TIM_POS           32
28 #define MGRET_OVER_MEM_POS      61
29 #define MGRET_OVER_TIM_POS      62
30 #define MGRET_ERROR_POS         63
31
32 #define MGRET_TIM_MASK          (0x00FFFFFFul << MGRET_TIM_POS)
33 #define MGRET_MEM_MASK          (0xFFFFFFFFul << MGRET_MEM_POS)
34 #define MGRET_OVER_MEM_MASK     (1ul << MGRET_OVER_MEM_POS)
35 #define MGRET_OVER_TIM_MASK     (1ul << MGRET_OVER_TIM_POS)
36 #define MGRET_ERROR_MASK        (1ul << MGRET_ERROR_POS)
37
38 #define MAX_CORES               6
39
40 char memory[128*10204*1024];
41
42 static pthread_barrier_t barrier;
43
44 struct mg_ret {
45         uint32_t time;
46         uint32_t mem;
47         bool time_ovf;
48         bool mem_ovf;
49 };
50
51 static struct mg_ret mgret(uint64_t retval)
52 {
53         struct mg_ret mgr = {};
54         mgr.time = (retval & MGRET_TIM_MASK) >> MGRET_TIM_POS;
55         mgr.mem = (retval & MGRET_MEM_MASK) >> MGRET_MEM_POS;
56         mgr.time_ovf = (retval >> MGRET_OVER_TIM_POS) & 1;
57         mgr.mem_ovf = (retval >> MGRET_OVER_MEM_POS) & 1;
58         return mgr;
59 }
60
61 void compute_kernel(int time_us)
62 {
63         struct timespec ts;
64         uint64_t current_us, end_us;
65         clock_gettime(CLOCK_MONOTONIC, &ts);
66         end_us = ts.tv_sec * 1000000 + ts.tv_nsec / 1000 + time_us;
67         do {
68                 clock_gettime(CLOCK_MONOTONIC, &ts);
69                 current_us = ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
70         } while (current_us < end_us);
71 }
72
73 void read_memory(long lines)
74 {
75         uint64_t sum = 0;
76
77         for (uint64_t i = 0; i < lines; i++) {
78                 sum += memory[(i * 64) % sizeof(memory)];
79         }
80         /* Do not optimize this function out */
81         volatile uint64_t x = sum;
82         (void)x;
83 }
84
85 void write_memory(long lines)
86 {
87         uint64_t *ptr;
88         for (uint64_t i = 0; i < lines; i++) {
89                 ptr = (void*)&memory[(i * 64) % sizeof(memory)];
90                 *ptr = i;
91         }
92 }
93
94 void read_memory_rnd(long lines)
95 {
96         static unsigned rnd;
97         uint64_t sum = 0;
98
99         for (long i = 0; i < lines; i++) {
100                 rnd = (rnd + 523) * 253573;
101                 sum += memory[rnd % sizeof(memory)];
102         }
103         /* Do not optimize this function out */
104         volatile uint64_t x = sum;
105         (void)x;
106 }
107
108 /* Memguard flags */
109 #define MGF_PERIODIC                                                           \
110         (1 << 0) /* Chooses between periodic or one-shot budget replenishment */
111 #define MGF_MASK_INT                                                           \
112         (1                                                                     \
113          << 1) /* Mask (disable) low priority interrupts until next memguard call */
114
115 long memguard(unsigned long timeout, unsigned long memory_budget,
116               unsigned long flags)
117 {
118         return syscall(SYS_memguard, timeout, memory_budget, flags);
119 }
120
121 void wvtest_pass(bool cond, const char* file, int line, const char* str)
122 {
123         printf("! %s:%d  %s %s\n", file, line, str, cond ? "ok" : "FAILURE");
124 }
125
126 #define WVPASS(cond) wvtest_pass(cond, __FILE__, __LINE__, #cond)
127
128 static void print_test_info(uint64_t timeout_us, uint64_t mem_budget, uint64_t flags,
129                             int64_t retval, const char *code)
130 {
131         char call[80];
132         snprintf(call, sizeof(call), "memguard(%luus, %lumiss, %c%c):",
133                  timeout_us, mem_budget,
134                  flags & MGF_PERIODIC ? 'P' : '-',
135                  flags & MGF_MASK_INT ? 'I' : '-'
136                 );
137         struct mg_ret r = mgret(retval);
138         printf("Testing \"CPU%d: %-40s %-25s ⇒ time:%8u%c mem:%8u%c\" in %s:\n",
139                sched_getcpu(), call, code,
140                r.time, r.time_ovf ? '!' : ' ',
141                r.mem, r.mem_ovf ? '!' : ' ',
142                __FILE__);
143         WVPASS((retval & MGRET_ERROR_MASK) == 0);
144 }
145
146 pthread_mutex_t wvtest_lock = PTHREAD_MUTEX_INITIALIZER;
147
148 #define MGTEST(timeout_us, mem_budget, flags, code)                     \
149         ({                                                              \
150                 long retval;                                            \
151                 retval = memguard(timeout_us, mem_budget, flags);       \
152                 code;                                                   \
153                 retval = memguard(0, 0, 0);                             \
154                 pthread_mutex_lock(&wvtest_lock);                       \
155                 print_test_info(timeout_us, mem_budget, flags, retval, #code); \
156                 mgret(retval);                                          \
157         })
158
159 #define MGTESTEND() pthread_mutex_unlock(&wvtest_lock)
160
161 void *test_thread(void *ptr)
162 {
163         cpu_set_t set;
164         int cpu = (intptr_t)ptr;
165
166         /* Ensure that our test thread does not migrate to another CPU
167          * during memguarding */
168         CPU_ZERO(&set);
169         CPU_SET(cpu, &set);
170         if (sched_setaffinity(0, sizeof(set), &set) < 0)
171                 err(1, "sched_setaffinity");
172
173         pthread_barrier_wait(&barrier);
174
175         struct mg_ret r;
176
177         for (uint64_t flags = 0; flags < 4; flags++) {
178                 compute_kernel(1); /* warm up */
179
180                 ///////////////////////////////////////////////////////
181                 r = MGTEST(5000, 10000, flags, compute_kernel(1000));
182                 WVPASS(!r.time_ovf);
183                 WVPASS(r.time > 900);
184                 MGTESTEND();
185
186                 r = MGTEST(500, 10000, flags, compute_kernel(1000));
187                 WVPASS(r.time_ovf);
188                 WVPASS(r.time > 900);
189                 MGTESTEND();
190
191                 r = MGTEST(5000, 10000, flags, compute_kernel(2000));
192                 WVPASS(!r.time_ovf);
193                 WVPASS(r.time > 1900);
194                 MGTESTEND();
195
196                 r = MGTEST(500, 10000, flags, compute_kernel(2000));
197                 WVPASS(r.time_ovf);
198                 WVPASS(r.time > 1900);
199                 MGTESTEND();
200
201                 ///////////////////////////////////////////////////////
202                 r = MGTEST(100000, 500000, flags, read_memory(100000));
203                 WVPASS(!r.mem_ovf);
204                 WVPASS(r.mem >= 90000);
205                 MGTESTEND();
206
207                 r = MGTEST(100000, 50000,  flags, read_memory(100000));
208                 WVPASS(r.mem_ovf);
209                 WVPASS(r.mem >= 90000);
210                 MGTESTEND();
211
212                 r = MGTEST(100000, 500000, flags, read_memory_rnd(100000));
213                 WVPASS(!r.mem_ovf);
214                 WVPASS(r.mem >= 90000);
215                 MGTESTEND();
216
217                 r = MGTEST(100000, 50000,  flags, read_memory_rnd(100000));
218                 WVPASS(r.mem_ovf);
219                 WVPASS(r.mem >= 90000);
220                 MGTESTEND();
221
222                 r = MGTEST(100000, 500000, flags, write_memory(100000));
223                 WVPASS(!r.mem_ovf);
224                 WVPASS(r.mem >= 90000);
225                 MGTESTEND();
226
227                 r = MGTEST(100000, 50000,  flags, write_memory(100000));
228                 WVPASS(r.mem_ovf);
229                 WVPASS(r.mem >= 90000);
230                 MGTESTEND();
231
232
233                 ///////////////////////////////////////////////////////
234                 r = MGTEST(100000, 5000000, flags, read_memory(1000000));
235                 WVPASS(!r.mem_ovf);
236                 WVPASS(r.mem >= 900000);
237                 MGTESTEND();
238
239                 r = MGTEST(100000, 500000,  flags, read_memory(1000000));
240                 WVPASS(r.mem_ovf);
241                 WVPASS(r.mem >= 900000);
242                 MGTESTEND();
243
244                 r = MGTEST(100000, 5000000, flags, read_memory_rnd(1000000));
245                 WVPASS(!r.mem_ovf);
246                 WVPASS(r.mem >= 900000);
247                 MGTESTEND();
248
249                 r = MGTEST(100000, 500000,  flags, read_memory_rnd(1000000));
250                 WVPASS(r.mem_ovf);
251                 WVPASS(r.mem >= 900000);
252                 MGTESTEND();
253
254                 r = MGTEST(100000, 5000000, flags, write_memory(1000000));
255                 WVPASS(!r.mem_ovf);
256                 WVPASS(r.mem >= 900000);
257                 MGTESTEND();
258
259                 r = MGTEST(100000, 500000,  flags, write_memory(1000000));
260                 WVPASS(r.mem_ovf);
261                 WVPASS(r.mem >= 900000);
262                 MGTESTEND();
263
264                 printf("\n");
265         }
266
267         /* Throttling tests */
268         struct mg_ret r1, r2, r3;
269         r1 = MGTEST(10000, 9000, MGF_PERIODIC | MGF_MASK_INT, read_memory(100000));
270         WVPASS(r1.time > 100*1000);
271         MGTESTEND();
272
273         r2 = MGTEST(10000, 3000, MGF_PERIODIC | MGF_MASK_INT, read_memory(100000));
274         WVPASS(r2.time > 2 * r1.time);
275         MGTESTEND();
276
277         r3 = MGTEST(10000, 1000, MGF_PERIODIC | MGF_MASK_INT, read_memory(100000));
278         WVPASS(r3.time > 2 * r2.time);
279         MGTESTEND();
280
281         return NULL;
282 }
283
284 int main(int argc, char *argv[])
285 {
286         int cpu_mask = 1;
287         int cpu_count = 0;
288         pthread_t threads[MAX_CORES];
289
290         /* TODO: currently shared memory */
291         for (int i = 0; i < sizeof(memory); i += 64)
292                 memory[i] = 1;
293
294         if (argc > 1)
295                 cpu_mask = strtol(argv[1], NULL, 16);
296
297         for (int i = 0; i < MAX_CORES; i++) {
298                 if (cpu_mask & (1 << i)) {
299                         cpu_count++;
300                 }
301         }
302
303         printf("CPU count:%d  CPU mask:%#x\n", cpu_count, cpu_mask);
304         int s = pthread_barrier_init(&barrier, NULL, cpu_count);
305         if (s != 0)
306                 error(1, s, "pthread_barrier_init");
307
308         for (intptr_t i = 0; i < MAX_CORES; i++) {
309                 if (cpu_mask & (1 << i))
310                         pthread_create(&threads[i], NULL, test_thread, (void *)i);
311         }
312
313         for (int i = 0; i < MAX_CORES; i++) {
314                 if (cpu_mask & (1 << i)) {
315                         pthread_join(threads[i], NULL);
316                 }
317         }
318
319         return 0;
320 }