Merge tag 'asoc-v5.19' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie...
[linux-2.6-microblaze.git] / tools / testing / selftests / vDSO / vdso_test_correctness.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * ldt_gdt.c - Test cases for LDT and GDT access
4  * Copyright (c) 2011-2015 Andrew Lutomirski
5  */
6
7 #define _GNU_SOURCE
8
9 #include <stdio.h>
10 #include <sys/time.h>
11 #include <time.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <sys/syscall.h>
15 #include <dlfcn.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <sched.h>
19 #include <stdbool.h>
20 #include <limits.h>
21
22 #include "vdso_config.h"
23 #include "../kselftest.h"
24
25 static const char **name;
26
27 #ifndef SYS_getcpu
28 # ifdef __x86_64__
29 #  define SYS_getcpu 309
30 # else
31 #  define SYS_getcpu 318
32 # endif
33 #endif
34
35 #ifndef __NR_clock_gettime64
36 #define __NR_clock_gettime64    403
37 #endif
38
39 #ifndef __kernel_timespec
40 struct __kernel_timespec {
41         long long       tv_sec;
42         long long       tv_nsec;
43 };
44 #endif
45
46 /* max length of lines in /proc/self/maps - anything longer is skipped here */
47 #define MAPS_LINE_LEN 128
48
49 int nerrs = 0;
50
51 typedef int (*vgettime_t)(clockid_t, struct timespec *);
52
53 vgettime_t vdso_clock_gettime;
54
55 typedef int (*vgettime64_t)(clockid_t, struct __kernel_timespec *);
56
57 vgettime64_t vdso_clock_gettime64;
58
59 typedef long (*vgtod_t)(struct timeval *tv, struct timezone *tz);
60
61 vgtod_t vdso_gettimeofday;
62
63 typedef long (*getcpu_t)(unsigned *, unsigned *, void *);
64
65 getcpu_t vgetcpu;
66 getcpu_t vdso_getcpu;
67
68 static void *vsyscall_getcpu(void)
69 {
70 #ifdef __x86_64__
71         FILE *maps;
72         char line[MAPS_LINE_LEN];
73         bool found = false;
74
75         maps = fopen("/proc/self/maps", "r");
76         if (!maps) /* might still be present, but ignore it here, as we test vDSO not vsyscall */
77                 return NULL;
78
79         while (fgets(line, MAPS_LINE_LEN, maps)) {
80                 char r, x;
81                 void *start, *end;
82                 char name[MAPS_LINE_LEN];
83
84                 /* sscanf() is safe here as strlen(name) >= strlen(line) */
85                 if (sscanf(line, "%p-%p %c-%cp %*x %*x:%*x %*u %s",
86                            &start, &end, &r, &x, name) != 5)
87                         continue;
88
89                 if (strcmp(name, "[vsyscall]"))
90                         continue;
91
92                 /* assume entries are OK, as we test vDSO here not vsyscall */
93                 found = true;
94                 break;
95         }
96
97         fclose(maps);
98
99         if (!found) {
100                 printf("Warning: failed to find vsyscall getcpu\n");
101                 return NULL;
102         }
103         return (void *) (0xffffffffff600800);
104 #else
105         return NULL;
106 #endif
107 }
108
109
110 static void fill_function_pointers()
111 {
112         void *vdso = dlopen("linux-vdso.so.1",
113                             RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
114         if (!vdso)
115                 vdso = dlopen("linux-gate.so.1",
116                               RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
117         if (!vdso) {
118                 printf("[WARN]\tfailed to find vDSO\n");
119                 return;
120         }
121
122         vdso_getcpu = (getcpu_t)dlsym(vdso, name[4]);
123         if (!vdso_getcpu)
124                 printf("Warning: failed to find getcpu in vDSO\n");
125
126         vgetcpu = (getcpu_t) vsyscall_getcpu();
127
128         vdso_clock_gettime = (vgettime_t)dlsym(vdso, name[1]);
129         if (!vdso_clock_gettime)
130                 printf("Warning: failed to find clock_gettime in vDSO\n");
131
132 #if defined(VDSO_32BIT)
133         vdso_clock_gettime64 = (vgettime64_t)dlsym(vdso, name[5]);
134         if (!vdso_clock_gettime64)
135                 printf("Warning: failed to find clock_gettime64 in vDSO\n");
136 #endif
137
138         vdso_gettimeofday = (vgtod_t)dlsym(vdso, name[0]);
139         if (!vdso_gettimeofday)
140                 printf("Warning: failed to find gettimeofday in vDSO\n");
141
142 }
143
144 static long sys_getcpu(unsigned * cpu, unsigned * node,
145                        void* cache)
146 {
147         return syscall(__NR_getcpu, cpu, node, cache);
148 }
149
150 static inline int sys_clock_gettime(clockid_t id, struct timespec *ts)
151 {
152         return syscall(__NR_clock_gettime, id, ts);
153 }
154
155 static inline int sys_clock_gettime64(clockid_t id, struct __kernel_timespec *ts)
156 {
157         return syscall(__NR_clock_gettime64, id, ts);
158 }
159
160 static inline int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
161 {
162         return syscall(__NR_gettimeofday, tv, tz);
163 }
164
165 static void test_getcpu(void)
166 {
167         printf("[RUN]\tTesting getcpu...\n");
168
169         for (int cpu = 0; ; cpu++) {
170                 cpu_set_t cpuset;
171                 CPU_ZERO(&cpuset);
172                 CPU_SET(cpu, &cpuset);
173                 if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0)
174                         return;
175
176                 unsigned cpu_sys, cpu_vdso, cpu_vsys,
177                         node_sys, node_vdso, node_vsys;
178                 long ret_sys, ret_vdso = 1, ret_vsys = 1;
179                 unsigned node;
180
181                 ret_sys = sys_getcpu(&cpu_sys, &node_sys, 0);
182                 if (vdso_getcpu)
183                         ret_vdso = vdso_getcpu(&cpu_vdso, &node_vdso, 0);
184                 if (vgetcpu)
185                         ret_vsys = vgetcpu(&cpu_vsys, &node_vsys, 0);
186
187                 if (!ret_sys)
188                         node = node_sys;
189                 else if (!ret_vdso)
190                         node = node_vdso;
191                 else if (!ret_vsys)
192                         node = node_vsys;
193
194                 bool ok = true;
195                 if (!ret_sys && (cpu_sys != cpu || node_sys != node))
196                         ok = false;
197                 if (!ret_vdso && (cpu_vdso != cpu || node_vdso != node))
198                         ok = false;
199                 if (!ret_vsys && (cpu_vsys != cpu || node_vsys != node))
200                         ok = false;
201
202                 printf("[%s]\tCPU %u:", ok ? "OK" : "FAIL", cpu);
203                 if (!ret_sys)
204                         printf(" syscall: cpu %u, node %u", cpu_sys, node_sys);
205                 if (!ret_vdso)
206                         printf(" vdso: cpu %u, node %u", cpu_vdso, node_vdso);
207                 if (!ret_vsys)
208                         printf(" vsyscall: cpu %u, node %u", cpu_vsys,
209                                node_vsys);
210                 printf("\n");
211
212                 if (!ok)
213                         nerrs++;
214         }
215 }
216
217 static bool ts_leq(const struct timespec *a, const struct timespec *b)
218 {
219         if (a->tv_sec != b->tv_sec)
220                 return a->tv_sec < b->tv_sec;
221         else
222                 return a->tv_nsec <= b->tv_nsec;
223 }
224
225 static bool ts64_leq(const struct __kernel_timespec *a,
226                      const struct __kernel_timespec *b)
227 {
228         if (a->tv_sec != b->tv_sec)
229                 return a->tv_sec < b->tv_sec;
230         else
231                 return a->tv_nsec <= b->tv_nsec;
232 }
233
234 static bool tv_leq(const struct timeval *a, const struct timeval *b)
235 {
236         if (a->tv_sec != b->tv_sec)
237                 return a->tv_sec < b->tv_sec;
238         else
239                 return a->tv_usec <= b->tv_usec;
240 }
241
242 static char const * const clocknames[] = {
243         [0] = "CLOCK_REALTIME",
244         [1] = "CLOCK_MONOTONIC",
245         [2] = "CLOCK_PROCESS_CPUTIME_ID",
246         [3] = "CLOCK_THREAD_CPUTIME_ID",
247         [4] = "CLOCK_MONOTONIC_RAW",
248         [5] = "CLOCK_REALTIME_COARSE",
249         [6] = "CLOCK_MONOTONIC_COARSE",
250         [7] = "CLOCK_BOOTTIME",
251         [8] = "CLOCK_REALTIME_ALARM",
252         [9] = "CLOCK_BOOTTIME_ALARM",
253         [10] = "CLOCK_SGI_CYCLE",
254         [11] = "CLOCK_TAI",
255 };
256
257 static void test_one_clock_gettime(int clock, const char *name)
258 {
259         struct timespec start, vdso, end;
260         int vdso_ret, end_ret;
261
262         printf("[RUN]\tTesting clock_gettime for clock %s (%d)...\n", name, clock);
263
264         if (sys_clock_gettime(clock, &start) < 0) {
265                 if (errno == EINVAL) {
266                         vdso_ret = vdso_clock_gettime(clock, &vdso);
267                         if (vdso_ret == -EINVAL) {
268                                 printf("[OK]\tNo such clock.\n");
269                         } else {
270                                 printf("[FAIL]\tNo such clock, but __vdso_clock_gettime returned %d\n", vdso_ret);
271                                 nerrs++;
272                         }
273                 } else {
274                         printf("[WARN]\t clock_gettime(%d) syscall returned error %d\n", clock, errno);
275                 }
276                 return;
277         }
278
279         vdso_ret = vdso_clock_gettime(clock, &vdso);
280         end_ret = sys_clock_gettime(clock, &end);
281
282         if (vdso_ret != 0 || end_ret != 0) {
283                 printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n",
284                        vdso_ret, errno);
285                 nerrs++;
286                 return;
287         }
288
289         printf("\t%llu.%09ld %llu.%09ld %llu.%09ld\n",
290                (unsigned long long)start.tv_sec, start.tv_nsec,
291                (unsigned long long)vdso.tv_sec, vdso.tv_nsec,
292                (unsigned long long)end.tv_sec, end.tv_nsec);
293
294         if (!ts_leq(&start, &vdso) || !ts_leq(&vdso, &end)) {
295                 printf("[FAIL]\tTimes are out of sequence\n");
296                 nerrs++;
297                 return;
298         }
299
300         printf("[OK]\tTest Passed.\n");
301 }
302
303 static void test_clock_gettime(void)
304 {
305         if (!vdso_clock_gettime) {
306                 printf("[SKIP]\tNo vDSO, so skipping clock_gettime() tests\n");
307                 return;
308         }
309
310         for (int clock = 0; clock < ARRAY_SIZE(clocknames); clock++)
311                 test_one_clock_gettime(clock, clocknames[clock]);
312
313         /* Also test some invalid clock ids */
314         test_one_clock_gettime(-1, "invalid");
315         test_one_clock_gettime(INT_MIN, "invalid");
316         test_one_clock_gettime(INT_MAX, "invalid");
317 }
318
319 static void test_one_clock_gettime64(int clock, const char *name)
320 {
321         struct __kernel_timespec start, vdso, end;
322         int vdso_ret, end_ret;
323
324         printf("[RUN]\tTesting clock_gettime64 for clock %s (%d)...\n", name, clock);
325
326         if (sys_clock_gettime64(clock, &start) < 0) {
327                 if (errno == EINVAL) {
328                         vdso_ret = vdso_clock_gettime64(clock, &vdso);
329                         if (vdso_ret == -EINVAL) {
330                                 printf("[OK]\tNo such clock.\n");
331                         } else {
332                                 printf("[FAIL]\tNo such clock, but __vdso_clock_gettime64 returned %d\n", vdso_ret);
333                                 nerrs++;
334                         }
335                 } else {
336                         printf("[WARN]\t clock_gettime64(%d) syscall returned error %d\n", clock, errno);
337                 }
338                 return;
339         }
340
341         vdso_ret = vdso_clock_gettime64(clock, &vdso);
342         end_ret = sys_clock_gettime64(clock, &end);
343
344         if (vdso_ret != 0 || end_ret != 0) {
345                 printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n",
346                        vdso_ret, errno);
347                 nerrs++;
348                 return;
349         }
350
351         printf("\t%llu.%09lld %llu.%09lld %llu.%09lld\n",
352                (unsigned long long)start.tv_sec, start.tv_nsec,
353                (unsigned long long)vdso.tv_sec, vdso.tv_nsec,
354                (unsigned long long)end.tv_sec, end.tv_nsec);
355
356         if (!ts64_leq(&start, &vdso) || !ts64_leq(&vdso, &end)) {
357                 printf("[FAIL]\tTimes are out of sequence\n");
358                 nerrs++;
359                 return;
360         }
361
362         printf("[OK]\tTest Passed.\n");
363 }
364
365 static void test_clock_gettime64(void)
366 {
367         if (!vdso_clock_gettime64) {
368                 printf("[SKIP]\tNo vDSO, so skipping clock_gettime64() tests\n");
369                 return;
370         }
371
372         for (int clock = 0; clock < ARRAY_SIZE(clocknames); clock++)
373                 test_one_clock_gettime64(clock, clocknames[clock]);
374
375         /* Also test some invalid clock ids */
376         test_one_clock_gettime64(-1, "invalid");
377         test_one_clock_gettime64(INT_MIN, "invalid");
378         test_one_clock_gettime64(INT_MAX, "invalid");
379 }
380
381 static void test_gettimeofday(void)
382 {
383         struct timeval start, vdso, end;
384         struct timezone sys_tz, vdso_tz;
385         int vdso_ret, end_ret;
386
387         if (!vdso_gettimeofday)
388                 return;
389
390         printf("[RUN]\tTesting gettimeofday...\n");
391
392         if (sys_gettimeofday(&start, &sys_tz) < 0) {
393                 printf("[FAIL]\tsys_gettimeofday failed (%d)\n", errno);
394                 nerrs++;
395                 return;
396         }
397
398         vdso_ret = vdso_gettimeofday(&vdso, &vdso_tz);
399         end_ret = sys_gettimeofday(&end, NULL);
400
401         if (vdso_ret != 0 || end_ret != 0) {
402                 printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n",
403                        vdso_ret, errno);
404                 nerrs++;
405                 return;
406         }
407
408         printf("\t%llu.%06ld %llu.%06ld %llu.%06ld\n",
409                (unsigned long long)start.tv_sec, start.tv_usec,
410                (unsigned long long)vdso.tv_sec, vdso.tv_usec,
411                (unsigned long long)end.tv_sec, end.tv_usec);
412
413         if (!tv_leq(&start, &vdso) || !tv_leq(&vdso, &end)) {
414                 printf("[FAIL]\tTimes are out of sequence\n");
415                 nerrs++;
416         }
417
418         if (sys_tz.tz_minuteswest == vdso_tz.tz_minuteswest &&
419             sys_tz.tz_dsttime == vdso_tz.tz_dsttime) {
420                 printf("[OK]\ttimezones match: minuteswest=%d, dsttime=%d\n",
421                        sys_tz.tz_minuteswest, sys_tz.tz_dsttime);
422         } else {
423                 printf("[FAIL]\ttimezones do not match\n");
424                 nerrs++;
425         }
426
427         /* And make sure that passing NULL for tz doesn't crash. */
428         vdso_gettimeofday(&vdso, NULL);
429 }
430
431 int main(int argc, char **argv)
432 {
433         name = (const char **)&names[VDSO_NAMES];
434
435         fill_function_pointers();
436
437         test_clock_gettime();
438         test_clock_gettime64();
439         test_gettimeofday();
440
441         /*
442          * Test getcpu() last so that, if something goes wrong setting affinity,
443          * we still run the other tests.
444          */
445         test_getcpu();
446
447         return nerrs ? 1 : 0;
448 }