root/tools/testing/selftests/x86/test_vdso.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. vsyscall_getcpu
  2. fill_function_pointers
  3. sys_getcpu
  4. sys_clock_gettime
  5. sys_gettimeofday
  6. test_getcpu
  7. ts_leq
  8. tv_leq
  9. test_one_clock_gettime
  10. test_clock_gettime
  11. test_gettimeofday
  12. main

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

/* [<][>][^][v][top][bottom][index][help] */