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