Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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