18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci#define _GNU_SOURCE 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci#include <stdio.h> 68c2ecf20Sopenharmony_ci#include <sys/time.h> 78c2ecf20Sopenharmony_ci#include <time.h> 88c2ecf20Sopenharmony_ci#include <stdlib.h> 98c2ecf20Sopenharmony_ci#include <sys/syscall.h> 108c2ecf20Sopenharmony_ci#include <unistd.h> 118c2ecf20Sopenharmony_ci#include <dlfcn.h> 128c2ecf20Sopenharmony_ci#include <string.h> 138c2ecf20Sopenharmony_ci#include <inttypes.h> 148c2ecf20Sopenharmony_ci#include <signal.h> 158c2ecf20Sopenharmony_ci#include <sys/ucontext.h> 168c2ecf20Sopenharmony_ci#include <errno.h> 178c2ecf20Sopenharmony_ci#include <err.h> 188c2ecf20Sopenharmony_ci#include <sched.h> 198c2ecf20Sopenharmony_ci#include <stdbool.h> 208c2ecf20Sopenharmony_ci#include <setjmp.h> 218c2ecf20Sopenharmony_ci#include <sys/uio.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "helpers.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#ifdef __x86_64__ 268c2ecf20Sopenharmony_ci# define VSYS(x) (x) 278c2ecf20Sopenharmony_ci#else 288c2ecf20Sopenharmony_ci# define VSYS(x) 0 298c2ecf20Sopenharmony_ci#endif 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#ifndef SYS_getcpu 328c2ecf20Sopenharmony_ci# ifdef __x86_64__ 338c2ecf20Sopenharmony_ci# define SYS_getcpu 309 348c2ecf20Sopenharmony_ci# else 358c2ecf20Sopenharmony_ci# define SYS_getcpu 318 368c2ecf20Sopenharmony_ci# endif 378c2ecf20Sopenharmony_ci#endif 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* max length of lines in /proc/self/maps - anything longer is skipped here */ 408c2ecf20Sopenharmony_ci#define MAPS_LINE_LEN 128 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), 438c2ecf20Sopenharmony_ci int flags) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct sigaction sa; 468c2ecf20Sopenharmony_ci memset(&sa, 0, sizeof(sa)); 478c2ecf20Sopenharmony_ci sa.sa_sigaction = handler; 488c2ecf20Sopenharmony_ci sa.sa_flags = SA_SIGINFO | flags; 498c2ecf20Sopenharmony_ci sigemptyset(&sa.sa_mask); 508c2ecf20Sopenharmony_ci if (sigaction(sig, &sa, 0)) 518c2ecf20Sopenharmony_ci err(1, "sigaction"); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* vsyscalls and vDSO */ 558c2ecf20Sopenharmony_cibool vsyscall_map_r = false, vsyscall_map_x = false; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_citypedef long (*gtod_t)(struct timeval *tv, struct timezone *tz); 588c2ecf20Sopenharmony_ciconst gtod_t vgtod = (gtod_t)VSYS(0xffffffffff600000); 598c2ecf20Sopenharmony_cigtod_t vdso_gtod; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_citypedef int (*vgettime_t)(clockid_t, struct timespec *); 628c2ecf20Sopenharmony_civgettime_t vdso_gettime; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_citypedef long (*time_func_t)(time_t *t); 658c2ecf20Sopenharmony_ciconst time_func_t vtime = (time_func_t)VSYS(0xffffffffff600400); 668c2ecf20Sopenharmony_citime_func_t vdso_time; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_citypedef long (*getcpu_t)(unsigned *, unsigned *, void *); 698c2ecf20Sopenharmony_ciconst getcpu_t vgetcpu = (getcpu_t)VSYS(0xffffffffff600800); 708c2ecf20Sopenharmony_cigetcpu_t vdso_getcpu; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic void init_vdso(void) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci void *vdso = dlopen("linux-vdso.so.1", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD); 758c2ecf20Sopenharmony_ci if (!vdso) 768c2ecf20Sopenharmony_ci vdso = dlopen("linux-gate.so.1", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD); 778c2ecf20Sopenharmony_ci if (!vdso) { 788c2ecf20Sopenharmony_ci printf("[WARN]\tfailed to find vDSO\n"); 798c2ecf20Sopenharmony_ci return; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci vdso_gtod = (gtod_t)dlsym(vdso, "__vdso_gettimeofday"); 838c2ecf20Sopenharmony_ci if (!vdso_gtod) 848c2ecf20Sopenharmony_ci printf("[WARN]\tfailed to find gettimeofday in vDSO\n"); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci vdso_gettime = (vgettime_t)dlsym(vdso, "__vdso_clock_gettime"); 878c2ecf20Sopenharmony_ci if (!vdso_gettime) 888c2ecf20Sopenharmony_ci printf("[WARN]\tfailed to find clock_gettime in vDSO\n"); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci vdso_time = (time_func_t)dlsym(vdso, "__vdso_time"); 918c2ecf20Sopenharmony_ci if (!vdso_time) 928c2ecf20Sopenharmony_ci printf("[WARN]\tfailed to find time in vDSO\n"); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci vdso_getcpu = (getcpu_t)dlsym(vdso, "__vdso_getcpu"); 958c2ecf20Sopenharmony_ci if (!vdso_getcpu) { 968c2ecf20Sopenharmony_ci /* getcpu() was never wired up in the 32-bit vDSO. */ 978c2ecf20Sopenharmony_ci printf("[%s]\tfailed to find getcpu in vDSO\n", 988c2ecf20Sopenharmony_ci sizeof(long) == 8 ? "WARN" : "NOTE"); 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic int init_vsys(void) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci#ifdef __x86_64__ 1058c2ecf20Sopenharmony_ci int nerrs = 0; 1068c2ecf20Sopenharmony_ci FILE *maps; 1078c2ecf20Sopenharmony_ci char line[MAPS_LINE_LEN]; 1088c2ecf20Sopenharmony_ci bool found = false; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci maps = fopen("/proc/self/maps", "r"); 1118c2ecf20Sopenharmony_ci if (!maps) { 1128c2ecf20Sopenharmony_ci printf("[WARN]\tCould not open /proc/self/maps -- assuming vsyscall is r-x\n"); 1138c2ecf20Sopenharmony_ci vsyscall_map_r = true; 1148c2ecf20Sopenharmony_ci return 0; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci while (fgets(line, MAPS_LINE_LEN, maps)) { 1188c2ecf20Sopenharmony_ci char r, x; 1198c2ecf20Sopenharmony_ci void *start, *end; 1208c2ecf20Sopenharmony_ci char name[MAPS_LINE_LEN]; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* sscanf() is safe here as strlen(name) >= strlen(line) */ 1238c2ecf20Sopenharmony_ci if (sscanf(line, "%p-%p %c-%cp %*x %*x:%*x %*u %s", 1248c2ecf20Sopenharmony_ci &start, &end, &r, &x, name) != 5) 1258c2ecf20Sopenharmony_ci continue; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (strcmp(name, "[vsyscall]")) 1288c2ecf20Sopenharmony_ci continue; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci printf("\tvsyscall map: %s", line); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (start != (void *)0xffffffffff600000 || 1338c2ecf20Sopenharmony_ci end != (void *)0xffffffffff601000) { 1348c2ecf20Sopenharmony_ci printf("[FAIL]\taddress range is nonsense\n"); 1358c2ecf20Sopenharmony_ci nerrs++; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci printf("\tvsyscall permissions are %c-%c\n", r, x); 1398c2ecf20Sopenharmony_ci vsyscall_map_r = (r == 'r'); 1408c2ecf20Sopenharmony_ci vsyscall_map_x = (x == 'x'); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci found = true; 1438c2ecf20Sopenharmony_ci break; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci fclose(maps); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (!found) { 1498c2ecf20Sopenharmony_ci printf("\tno vsyscall map in /proc/self/maps\n"); 1508c2ecf20Sopenharmony_ci vsyscall_map_r = false; 1518c2ecf20Sopenharmony_ci vsyscall_map_x = false; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci return nerrs; 1558c2ecf20Sopenharmony_ci#else 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci#endif 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/* syscalls */ 1618c2ecf20Sopenharmony_cistatic inline long sys_gtod(struct timeval *tv, struct timezone *tz) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci return syscall(SYS_gettimeofday, tv, tz); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic inline int sys_clock_gettime(clockid_t id, struct timespec *ts) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci return syscall(SYS_clock_gettime, id, ts); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic inline long sys_time(time_t *t) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci return syscall(SYS_time, t); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic inline long sys_getcpu(unsigned * cpu, unsigned * node, 1778c2ecf20Sopenharmony_ci void* cache) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci return syscall(SYS_getcpu, cpu, node, cache); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic jmp_buf jmpbuf; 1838c2ecf20Sopenharmony_cistatic volatile unsigned long segv_err; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic void sigsegv(int sig, siginfo_t *info, void *ctx_void) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci ucontext_t *ctx = (ucontext_t *)ctx_void; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci segv_err = ctx->uc_mcontext.gregs[REG_ERR]; 1908c2ecf20Sopenharmony_ci siglongjmp(jmpbuf, 1); 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic double tv_diff(const struct timeval *a, const struct timeval *b) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci return (double)(a->tv_sec - b->tv_sec) + 1968c2ecf20Sopenharmony_ci (double)((int)a->tv_usec - (int)b->tv_usec) * 1e-6; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic int check_gtod(const struct timeval *tv_sys1, 2008c2ecf20Sopenharmony_ci const struct timeval *tv_sys2, 2018c2ecf20Sopenharmony_ci const struct timezone *tz_sys, 2028c2ecf20Sopenharmony_ci const char *which, 2038c2ecf20Sopenharmony_ci const struct timeval *tv_other, 2048c2ecf20Sopenharmony_ci const struct timezone *tz_other) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci int nerrs = 0; 2078c2ecf20Sopenharmony_ci double d1, d2; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (tz_other && (tz_sys->tz_minuteswest != tz_other->tz_minuteswest || tz_sys->tz_dsttime != tz_other->tz_dsttime)) { 2108c2ecf20Sopenharmony_ci printf("[FAIL] %s tz mismatch\n", which); 2118c2ecf20Sopenharmony_ci nerrs++; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci d1 = tv_diff(tv_other, tv_sys1); 2158c2ecf20Sopenharmony_ci d2 = tv_diff(tv_sys2, tv_other); 2168c2ecf20Sopenharmony_ci printf("\t%s time offsets: %lf %lf\n", which, d1, d2); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (d1 < 0 || d2 < 0) { 2198c2ecf20Sopenharmony_ci printf("[FAIL]\t%s time was inconsistent with the syscall\n", which); 2208c2ecf20Sopenharmony_ci nerrs++; 2218c2ecf20Sopenharmony_ci } else { 2228c2ecf20Sopenharmony_ci printf("[OK]\t%s gettimeofday()'s timeval was okay\n", which); 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return nerrs; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic int test_gtod(void) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct timeval tv_sys1, tv_sys2, tv_vdso, tv_vsys; 2318c2ecf20Sopenharmony_ci struct timezone tz_sys, tz_vdso, tz_vsys; 2328c2ecf20Sopenharmony_ci long ret_vdso = -1; 2338c2ecf20Sopenharmony_ci long ret_vsys = -1; 2348c2ecf20Sopenharmony_ci int nerrs = 0; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci printf("[RUN]\ttest gettimeofday()\n"); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (sys_gtod(&tv_sys1, &tz_sys) != 0) 2398c2ecf20Sopenharmony_ci err(1, "syscall gettimeofday"); 2408c2ecf20Sopenharmony_ci if (vdso_gtod) 2418c2ecf20Sopenharmony_ci ret_vdso = vdso_gtod(&tv_vdso, &tz_vdso); 2428c2ecf20Sopenharmony_ci if (vsyscall_map_x) 2438c2ecf20Sopenharmony_ci ret_vsys = vgtod(&tv_vsys, &tz_vsys); 2448c2ecf20Sopenharmony_ci if (sys_gtod(&tv_sys2, &tz_sys) != 0) 2458c2ecf20Sopenharmony_ci err(1, "syscall gettimeofday"); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (vdso_gtod) { 2488c2ecf20Sopenharmony_ci if (ret_vdso == 0) { 2498c2ecf20Sopenharmony_ci nerrs += check_gtod(&tv_sys1, &tv_sys2, &tz_sys, "vDSO", &tv_vdso, &tz_vdso); 2508c2ecf20Sopenharmony_ci } else { 2518c2ecf20Sopenharmony_ci printf("[FAIL]\tvDSO gettimeofday() failed: %ld\n", ret_vdso); 2528c2ecf20Sopenharmony_ci nerrs++; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (vsyscall_map_x) { 2578c2ecf20Sopenharmony_ci if (ret_vsys == 0) { 2588c2ecf20Sopenharmony_ci nerrs += check_gtod(&tv_sys1, &tv_sys2, &tz_sys, "vsyscall", &tv_vsys, &tz_vsys); 2598c2ecf20Sopenharmony_ci } else { 2608c2ecf20Sopenharmony_ci printf("[FAIL]\tvsys gettimeofday() failed: %ld\n", ret_vsys); 2618c2ecf20Sopenharmony_ci nerrs++; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci return nerrs; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic int test_time(void) { 2698c2ecf20Sopenharmony_ci int nerrs = 0; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci printf("[RUN]\ttest time()\n"); 2728c2ecf20Sopenharmony_ci long t_sys1, t_sys2, t_vdso = 0, t_vsys = 0; 2738c2ecf20Sopenharmony_ci long t2_sys1 = -1, t2_sys2 = -1, t2_vdso = -1, t2_vsys = -1; 2748c2ecf20Sopenharmony_ci t_sys1 = sys_time(&t2_sys1); 2758c2ecf20Sopenharmony_ci if (vdso_time) 2768c2ecf20Sopenharmony_ci t_vdso = vdso_time(&t2_vdso); 2778c2ecf20Sopenharmony_ci if (vsyscall_map_x) 2788c2ecf20Sopenharmony_ci t_vsys = vtime(&t2_vsys); 2798c2ecf20Sopenharmony_ci t_sys2 = sys_time(&t2_sys2); 2808c2ecf20Sopenharmony_ci if (t_sys1 < 0 || t_sys1 != t2_sys1 || t_sys2 < 0 || t_sys2 != t2_sys2) { 2818c2ecf20Sopenharmony_ci printf("[FAIL]\tsyscall failed (ret1:%ld output1:%ld ret2:%ld output2:%ld)\n", t_sys1, t2_sys1, t_sys2, t2_sys2); 2828c2ecf20Sopenharmony_ci nerrs++; 2838c2ecf20Sopenharmony_ci return nerrs; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (vdso_time) { 2878c2ecf20Sopenharmony_ci if (t_vdso < 0 || t_vdso != t2_vdso) { 2888c2ecf20Sopenharmony_ci printf("[FAIL]\tvDSO failed (ret:%ld output:%ld)\n", t_vdso, t2_vdso); 2898c2ecf20Sopenharmony_ci nerrs++; 2908c2ecf20Sopenharmony_ci } else if (t_vdso < t_sys1 || t_vdso > t_sys2) { 2918c2ecf20Sopenharmony_ci printf("[FAIL]\tvDSO returned the wrong time (%ld %ld %ld)\n", t_sys1, t_vdso, t_sys2); 2928c2ecf20Sopenharmony_ci nerrs++; 2938c2ecf20Sopenharmony_ci } else { 2948c2ecf20Sopenharmony_ci printf("[OK]\tvDSO time() is okay\n"); 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (vsyscall_map_x) { 2998c2ecf20Sopenharmony_ci if (t_vsys < 0 || t_vsys != t2_vsys) { 3008c2ecf20Sopenharmony_ci printf("[FAIL]\tvsyscall failed (ret:%ld output:%ld)\n", t_vsys, t2_vsys); 3018c2ecf20Sopenharmony_ci nerrs++; 3028c2ecf20Sopenharmony_ci } else if (t_vsys < t_sys1 || t_vsys > t_sys2) { 3038c2ecf20Sopenharmony_ci printf("[FAIL]\tvsyscall returned the wrong time (%ld %ld %ld)\n", t_sys1, t_vsys, t_sys2); 3048c2ecf20Sopenharmony_ci nerrs++; 3058c2ecf20Sopenharmony_ci } else { 3068c2ecf20Sopenharmony_ci printf("[OK]\tvsyscall time() is okay\n"); 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci return nerrs; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic int test_getcpu(int cpu) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci int nerrs = 0; 3168c2ecf20Sopenharmony_ci long ret_sys, ret_vdso = -1, ret_vsys = -1; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci printf("[RUN]\tgetcpu() on CPU %d\n", cpu); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci cpu_set_t cpuset; 3218c2ecf20Sopenharmony_ci CPU_ZERO(&cpuset); 3228c2ecf20Sopenharmony_ci CPU_SET(cpu, &cpuset); 3238c2ecf20Sopenharmony_ci if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) { 3248c2ecf20Sopenharmony_ci printf("[SKIP]\tfailed to force CPU %d\n", cpu); 3258c2ecf20Sopenharmony_ci return nerrs; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci unsigned cpu_sys, cpu_vdso, cpu_vsys, node_sys, node_vdso, node_vsys; 3298c2ecf20Sopenharmony_ci unsigned node = 0; 3308c2ecf20Sopenharmony_ci bool have_node = false; 3318c2ecf20Sopenharmony_ci ret_sys = sys_getcpu(&cpu_sys, &node_sys, 0); 3328c2ecf20Sopenharmony_ci if (vdso_getcpu) 3338c2ecf20Sopenharmony_ci ret_vdso = vdso_getcpu(&cpu_vdso, &node_vdso, 0); 3348c2ecf20Sopenharmony_ci if (vsyscall_map_x) 3358c2ecf20Sopenharmony_ci ret_vsys = vgetcpu(&cpu_vsys, &node_vsys, 0); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (ret_sys == 0) { 3388c2ecf20Sopenharmony_ci if (cpu_sys != cpu) { 3398c2ecf20Sopenharmony_ci printf("[FAIL]\tsyscall reported CPU %hu but should be %d\n", cpu_sys, cpu); 3408c2ecf20Sopenharmony_ci nerrs++; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci have_node = true; 3448c2ecf20Sopenharmony_ci node = node_sys; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (vdso_getcpu) { 3488c2ecf20Sopenharmony_ci if (ret_vdso) { 3498c2ecf20Sopenharmony_ci printf("[FAIL]\tvDSO getcpu() failed\n"); 3508c2ecf20Sopenharmony_ci nerrs++; 3518c2ecf20Sopenharmony_ci } else { 3528c2ecf20Sopenharmony_ci if (!have_node) { 3538c2ecf20Sopenharmony_ci have_node = true; 3548c2ecf20Sopenharmony_ci node = node_vdso; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (cpu_vdso != cpu) { 3588c2ecf20Sopenharmony_ci printf("[FAIL]\tvDSO reported CPU %hu but should be %d\n", cpu_vdso, cpu); 3598c2ecf20Sopenharmony_ci nerrs++; 3608c2ecf20Sopenharmony_ci } else { 3618c2ecf20Sopenharmony_ci printf("[OK]\tvDSO reported correct CPU\n"); 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci if (node_vdso != node) { 3658c2ecf20Sopenharmony_ci printf("[FAIL]\tvDSO reported node %hu but should be %hu\n", node_vdso, node); 3668c2ecf20Sopenharmony_ci nerrs++; 3678c2ecf20Sopenharmony_ci } else { 3688c2ecf20Sopenharmony_ci printf("[OK]\tvDSO reported correct node\n"); 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (vsyscall_map_x) { 3748c2ecf20Sopenharmony_ci if (ret_vsys) { 3758c2ecf20Sopenharmony_ci printf("[FAIL]\tvsyscall getcpu() failed\n"); 3768c2ecf20Sopenharmony_ci nerrs++; 3778c2ecf20Sopenharmony_ci } else { 3788c2ecf20Sopenharmony_ci if (!have_node) { 3798c2ecf20Sopenharmony_ci have_node = true; 3808c2ecf20Sopenharmony_ci node = node_vsys; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (cpu_vsys != cpu) { 3848c2ecf20Sopenharmony_ci printf("[FAIL]\tvsyscall reported CPU %hu but should be %d\n", cpu_vsys, cpu); 3858c2ecf20Sopenharmony_ci nerrs++; 3868c2ecf20Sopenharmony_ci } else { 3878c2ecf20Sopenharmony_ci printf("[OK]\tvsyscall reported correct CPU\n"); 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (node_vsys != node) { 3918c2ecf20Sopenharmony_ci printf("[FAIL]\tvsyscall reported node %hu but should be %hu\n", node_vsys, node); 3928c2ecf20Sopenharmony_ci nerrs++; 3938c2ecf20Sopenharmony_ci } else { 3948c2ecf20Sopenharmony_ci printf("[OK]\tvsyscall reported correct node\n"); 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci return nerrs; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic int test_vsys_r(void) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci#ifdef __x86_64__ 4058c2ecf20Sopenharmony_ci printf("[RUN]\tChecking read access to the vsyscall page\n"); 4068c2ecf20Sopenharmony_ci bool can_read; 4078c2ecf20Sopenharmony_ci if (sigsetjmp(jmpbuf, 1) == 0) { 4088c2ecf20Sopenharmony_ci *(volatile int *)0xffffffffff600000; 4098c2ecf20Sopenharmony_ci can_read = true; 4108c2ecf20Sopenharmony_ci } else { 4118c2ecf20Sopenharmony_ci can_read = false; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (can_read && !vsyscall_map_r) { 4158c2ecf20Sopenharmony_ci printf("[FAIL]\tWe have read access, but we shouldn't\n"); 4168c2ecf20Sopenharmony_ci return 1; 4178c2ecf20Sopenharmony_ci } else if (!can_read && vsyscall_map_r) { 4188c2ecf20Sopenharmony_ci printf("[FAIL]\tWe don't have read access, but we should\n"); 4198c2ecf20Sopenharmony_ci return 1; 4208c2ecf20Sopenharmony_ci } else if (can_read) { 4218c2ecf20Sopenharmony_ci printf("[OK]\tWe have read access\n"); 4228c2ecf20Sopenharmony_ci } else { 4238c2ecf20Sopenharmony_ci printf("[OK]\tWe do not have read access: #PF(0x%lx)\n", 4248c2ecf20Sopenharmony_ci segv_err); 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci#endif 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci return 0; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic int test_vsys_x(void) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci#ifdef __x86_64__ 4348c2ecf20Sopenharmony_ci if (vsyscall_map_x) { 4358c2ecf20Sopenharmony_ci /* We already tested this adequately. */ 4368c2ecf20Sopenharmony_ci return 0; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci printf("[RUN]\tMake sure that vsyscalls really page fault\n"); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci bool can_exec; 4428c2ecf20Sopenharmony_ci if (sigsetjmp(jmpbuf, 1) == 0) { 4438c2ecf20Sopenharmony_ci vgtod(NULL, NULL); 4448c2ecf20Sopenharmony_ci can_exec = true; 4458c2ecf20Sopenharmony_ci } else { 4468c2ecf20Sopenharmony_ci can_exec = false; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci if (can_exec) { 4508c2ecf20Sopenharmony_ci printf("[FAIL]\tExecuting the vsyscall did not page fault\n"); 4518c2ecf20Sopenharmony_ci return 1; 4528c2ecf20Sopenharmony_ci } else if (segv_err & (1 << 4)) { /* INSTR */ 4538c2ecf20Sopenharmony_ci printf("[OK]\tExecuting the vsyscall page failed: #PF(0x%lx)\n", 4548c2ecf20Sopenharmony_ci segv_err); 4558c2ecf20Sopenharmony_ci } else { 4568c2ecf20Sopenharmony_ci printf("[FAIL]\tExecution failed with the wrong error: #PF(0x%lx)\n", 4578c2ecf20Sopenharmony_ci segv_err); 4588c2ecf20Sopenharmony_ci return 1; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci#endif 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci return 0; 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci/* 4668c2ecf20Sopenharmony_ci * Debuggers expect ptrace() to be able to peek at the vsyscall page. 4678c2ecf20Sopenharmony_ci * Use process_vm_readv() as a proxy for ptrace() to test this. We 4688c2ecf20Sopenharmony_ci * want it to work in the vsyscall=emulate case and to fail in the 4698c2ecf20Sopenharmony_ci * vsyscall=xonly case. 4708c2ecf20Sopenharmony_ci * 4718c2ecf20Sopenharmony_ci * It's worth noting that this ABI is a bit nutty. write(2) can't 4728c2ecf20Sopenharmony_ci * read from the vsyscall page on any kernel version or mode. The 4738c2ecf20Sopenharmony_ci * fact that ptrace() ever worked was a nice courtesy of old kernels, 4748c2ecf20Sopenharmony_ci * but the code to support it is fairly gross. 4758c2ecf20Sopenharmony_ci */ 4768c2ecf20Sopenharmony_cistatic int test_process_vm_readv(void) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci#ifdef __x86_64__ 4798c2ecf20Sopenharmony_ci char buf[4096]; 4808c2ecf20Sopenharmony_ci struct iovec local, remote; 4818c2ecf20Sopenharmony_ci int ret; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci printf("[RUN]\tprocess_vm_readv() from vsyscall page\n"); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci local.iov_base = buf; 4868c2ecf20Sopenharmony_ci local.iov_len = 4096; 4878c2ecf20Sopenharmony_ci remote.iov_base = (void *)0xffffffffff600000; 4888c2ecf20Sopenharmony_ci remote.iov_len = 4096; 4898c2ecf20Sopenharmony_ci ret = process_vm_readv(getpid(), &local, 1, &remote, 1, 0); 4908c2ecf20Sopenharmony_ci if (ret != 4096) { 4918c2ecf20Sopenharmony_ci /* 4928c2ecf20Sopenharmony_ci * We expect process_vm_readv() to work if and only if the 4938c2ecf20Sopenharmony_ci * vsyscall page is readable. 4948c2ecf20Sopenharmony_ci */ 4958c2ecf20Sopenharmony_ci printf("[%s]\tprocess_vm_readv() failed (ret = %d, errno = %d)\n", vsyscall_map_r ? "FAIL" : "OK", ret, errno); 4968c2ecf20Sopenharmony_ci return vsyscall_map_r ? 1 : 0; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (vsyscall_map_r) { 5008c2ecf20Sopenharmony_ci if (!memcmp(buf, remote.iov_base, sizeof(buf))) { 5018c2ecf20Sopenharmony_ci printf("[OK]\tIt worked and read correct data\n"); 5028c2ecf20Sopenharmony_ci } else { 5038c2ecf20Sopenharmony_ci printf("[FAIL]\tIt worked but returned incorrect data\n"); 5048c2ecf20Sopenharmony_ci return 1; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci } else { 5078c2ecf20Sopenharmony_ci printf("[FAIL]\tprocess_rm_readv() succeeded, but it should have failed in this configuration\n"); 5088c2ecf20Sopenharmony_ci return 1; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci#endif 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci return 0; 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci#ifdef __x86_64__ 5168c2ecf20Sopenharmony_cistatic volatile sig_atomic_t num_vsyscall_traps; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_cistatic void sigtrap(int sig, siginfo_t *info, void *ctx_void) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci ucontext_t *ctx = (ucontext_t *)ctx_void; 5218c2ecf20Sopenharmony_ci unsigned long ip = ctx->uc_mcontext.gregs[REG_RIP]; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (((ip ^ 0xffffffffff600000UL) & ~0xfffUL) == 0) 5248c2ecf20Sopenharmony_ci num_vsyscall_traps++; 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic int test_emulation(void) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci time_t tmp; 5308c2ecf20Sopenharmony_ci bool is_native; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (!vsyscall_map_x) 5338c2ecf20Sopenharmony_ci return 0; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci printf("[RUN]\tchecking that vsyscalls are emulated\n"); 5368c2ecf20Sopenharmony_ci sethandler(SIGTRAP, sigtrap, 0); 5378c2ecf20Sopenharmony_ci set_eflags(get_eflags() | X86_EFLAGS_TF); 5388c2ecf20Sopenharmony_ci vtime(&tmp); 5398c2ecf20Sopenharmony_ci set_eflags(get_eflags() & ~X86_EFLAGS_TF); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci /* 5428c2ecf20Sopenharmony_ci * If vsyscalls are emulated, we expect a single trap in the 5438c2ecf20Sopenharmony_ci * vsyscall page -- the call instruction will trap with RIP 5448c2ecf20Sopenharmony_ci * pointing to the entry point before emulation takes over. 5458c2ecf20Sopenharmony_ci * In native mode, we expect two traps, since whatever code 5468c2ecf20Sopenharmony_ci * the vsyscall page contains will be more than just a ret 5478c2ecf20Sopenharmony_ci * instruction. 5488c2ecf20Sopenharmony_ci */ 5498c2ecf20Sopenharmony_ci is_native = (num_vsyscall_traps > 1); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci printf("[%s]\tvsyscalls are %s (%d instructions in vsyscall page)\n", 5528c2ecf20Sopenharmony_ci (is_native ? "FAIL" : "OK"), 5538c2ecf20Sopenharmony_ci (is_native ? "native" : "emulated"), 5548c2ecf20Sopenharmony_ci (int)num_vsyscall_traps); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci return is_native; 5578c2ecf20Sopenharmony_ci} 5588c2ecf20Sopenharmony_ci#endif 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ciint main(int argc, char **argv) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci int nerrs = 0; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci init_vdso(); 5658c2ecf20Sopenharmony_ci nerrs += init_vsys(); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci nerrs += test_gtod(); 5688c2ecf20Sopenharmony_ci nerrs += test_time(); 5698c2ecf20Sopenharmony_ci nerrs += test_getcpu(0); 5708c2ecf20Sopenharmony_ci nerrs += test_getcpu(1); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci sethandler(SIGSEGV, sigsegv, 0); 5738c2ecf20Sopenharmony_ci nerrs += test_vsys_r(); 5748c2ecf20Sopenharmony_ci nerrs += test_vsys_x(); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci nerrs += test_process_vm_readv(); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci#ifdef __x86_64__ 5798c2ecf20Sopenharmony_ci nerrs += test_emulation(); 5808c2ecf20Sopenharmony_ci#endif 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci return nerrs ? 1 : 0; 5838c2ecf20Sopenharmony_ci} 584