Linux Audio

Check our new training course

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