diff options
author | Aleksandar Markovic <aleksandar.markovic@imgtec.com> | 2016-09-22 18:56:50 +0200 |
---|---|---|
committer | Riku Voipio <riku.voipio@linaro.org> | 2016-10-21 15:19:39 +0300 |
commit | 19f59bcef91cd4abc04d10c9ecbf5183b71f1b06 (patch) | |
tree | 0ebd24f32a475fe2ac3ddf9177798b486c6ce9bf /linux-user | |
parent | da158a86c407fa7b9da848b571356a26809d8df9 (diff) |
linux-user: Add support for adjtimex() syscall
This patch implements Qemu user mode adjtimex() syscall support.
Syscall adjtimex() reads and optionally sets parameters for a clock
adjustment algorithm used in network synchonization or similar scenarios.
Its declaration is:
int adjtimex(struct timex *buf);
The correspondent source code in the Linux kernel is at kernel/time.c,
line 206.
The Qemu implementation is based on invocation of host's adjtimex(), and
its key part is in the "TARGET_NR_adjtimex" case segment of the the main
switch statement of the function do_syscall(), in linux-user/syscalls.c. All
necessary conversions of the data structures from target to host and from
host to target are covered. Two new functions, target_to_host_timex() and
host_to_target_timex(), are provided for the purpose of such conversions.
For that purpose, the support for related structure "timex" had tp be added
to the file linux-user/syscall_defs.h, based on its definition in Linux
kernel. Also, the relevant support for "-strace" Qemu option is included
in files linux-user/strace.c and linux-user/strace.list.
This patch also fixes failures of LTP tests adjtimex01 and adjtimex02, if
executed in Qemu user mode.
Signed-off-by: Aleksandar Rikalo <aleksandar.rikalo@imgtec.com>
Signed-off-by: Aleksandar Markovic <aleksandar.markovic@imgtec.com>
Signed-off-by: Riku Voipio <riku.voipio@linaro.org>
Diffstat (limited to 'linux-user')
-rw-r--r-- | linux-user/strace.c | 46 | ||||
-rw-r--r-- | linux-user/strace.list | 3 | ||||
-rw-r--r-- | linux-user/syscall.c | 87 | ||||
-rw-r--r-- | linux-user/syscall_defs.h | 28 |
4 files changed, 162 insertions, 2 deletions
diff --git a/linux-user/strace.c b/linux-user/strace.c index 1e5136098e..f37b386bda 100644 --- a/linux-user/strace.c +++ b/linux-user/strace.c @@ -577,6 +577,52 @@ print_syscall_ret_newselect(const struct syscallname *name, abi_long ret) } #endif +/* special meanings of adjtimex()' non-negative return values */ +#define TARGET_TIME_OK 0 /* clock synchronized, no leap second */ +#define TARGET_TIME_INS 1 /* insert leap second */ +#define TARGET_TIME_DEL 2 /* delete leap second */ +#define TARGET_TIME_OOP 3 /* leap second in progress */ +#define TARGET_TIME_WAIT 4 /* leap second has occurred */ +#define TARGET_TIME_ERROR 5 /* clock not synchronized */ +static void +print_syscall_ret_adjtimex(const struct syscallname *name, abi_long ret) +{ + const char *errstr = NULL; + + gemu_log(" = "); + if (ret < 0) { + gemu_log("-1 errno=%d", errno); + errstr = target_strerror(-ret); + if (errstr) { + gemu_log(" (%s)", errstr); + } + } else { + gemu_log(TARGET_ABI_FMT_ld, ret); + switch (ret) { + case TARGET_TIME_OK: + gemu_log(" TIME_OK (clock synchronized, no leap second)"); + break; + case TARGET_TIME_INS: + gemu_log(" TIME_INS (insert leap second)"); + break; + case TARGET_TIME_DEL: + gemu_log(" TIME_DEL (delete leap second)"); + break; + case TARGET_TIME_OOP: + gemu_log(" TIME_OOP (leap second in progress)"); + break; + case TARGET_TIME_WAIT: + gemu_log(" TIME_WAIT (leap second has occurred)"); + break; + case TARGET_TIME_ERROR: + gemu_log(" TIME_ERROR (clock not synchronized)"); + break; + } + } + + gemu_log("\n"); +} + UNUSED static struct flags access_flags[] = { FLAG_GENERIC(F_OK), FLAG_GENERIC(R_OK), diff --git a/linux-user/strace.list b/linux-user/strace.list index 608f7e0932..f6dd044d76 100644 --- a/linux-user/strace.list +++ b/linux-user/strace.list @@ -19,7 +19,8 @@ { TARGET_NR_add_key, "add_key" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_adjtimex -{ TARGET_NR_adjtimex, "adjtimex" , NULL, NULL, NULL }, +{ TARGET_NR_adjtimex, "adjtimex" , "%s(%p)", NULL, + print_syscall_ret_adjtimex }, #endif #ifdef TARGET_NR_afs_syscall { TARGET_NR_afs_syscall, "afs_syscall" , NULL, NULL, NULL }, diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 03339ba0de..0379b8a21b 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -35,6 +35,7 @@ #include <sys/swap.h> #include <linux/capability.h> #include <sched.h> +#include <sys/timex.h> #ifdef __ia64__ int __clone2(int (*fn)(void *), void *child_stack_base, size_t stack_size, int flags, void *arg, ...); @@ -6770,6 +6771,77 @@ static inline abi_long host_to_target_itimerspec(abi_ulong target_addr, return 0; } +static inline abi_long target_to_host_timex(struct timex *host_tx, + abi_long target_addr) +{ + struct target_timex *target_tx; + + if (!lock_user_struct(VERIFY_READ, target_tx, target_addr, 1)) { + return -TARGET_EFAULT; + } + + __get_user(host_tx->modes, &target_tx->modes); + __get_user(host_tx->offset, &target_tx->offset); + __get_user(host_tx->freq, &target_tx->freq); + __get_user(host_tx->maxerror, &target_tx->maxerror); + __get_user(host_tx->esterror, &target_tx->esterror); + __get_user(host_tx->status, &target_tx->status); + __get_user(host_tx->constant, &target_tx->constant); + __get_user(host_tx->precision, &target_tx->precision); + __get_user(host_tx->tolerance, &target_tx->tolerance); + __get_user(host_tx->time.tv_sec, &target_tx->time.tv_sec); + __get_user(host_tx->time.tv_usec, &target_tx->time.tv_usec); + __get_user(host_tx->tick, &target_tx->tick); + __get_user(host_tx->ppsfreq, &target_tx->ppsfreq); + __get_user(host_tx->jitter, &target_tx->jitter); + __get_user(host_tx->shift, &target_tx->shift); + __get_user(host_tx->stabil, &target_tx->stabil); + __get_user(host_tx->jitcnt, &target_tx->jitcnt); + __get_user(host_tx->calcnt, &target_tx->calcnt); + __get_user(host_tx->errcnt, &target_tx->errcnt); + __get_user(host_tx->stbcnt, &target_tx->stbcnt); + __get_user(host_tx->tai, &target_tx->tai); + + unlock_user_struct(target_tx, target_addr, 0); + return 0; +} + +static inline abi_long host_to_target_timex(abi_long target_addr, + struct timex *host_tx) +{ + struct target_timex *target_tx; + + if (!lock_user_struct(VERIFY_WRITE, target_tx, target_addr, 0)) { + return -TARGET_EFAULT; + } + + __put_user(host_tx->modes, &target_tx->modes); + __put_user(host_tx->offset, &target_tx->offset); + __put_user(host_tx->freq, &target_tx->freq); + __put_user(host_tx->maxerror, &target_tx->maxerror); + __put_user(host_tx->esterror, &target_tx->esterror); + __put_user(host_tx->status, &target_tx->status); + __put_user(host_tx->constant, &target_tx->constant); + __put_user(host_tx->precision, &target_tx->precision); + __put_user(host_tx->tolerance, &target_tx->tolerance); + __put_user(host_tx->time.tv_sec, &target_tx->time.tv_sec); + __put_user(host_tx->time.tv_usec, &target_tx->time.tv_usec); + __put_user(host_tx->tick, &target_tx->tick); + __put_user(host_tx->ppsfreq, &target_tx->ppsfreq); + __put_user(host_tx->jitter, &target_tx->jitter); + __put_user(host_tx->shift, &target_tx->shift); + __put_user(host_tx->stabil, &target_tx->stabil); + __put_user(host_tx->jitcnt, &target_tx->jitcnt); + __put_user(host_tx->calcnt, &target_tx->calcnt); + __put_user(host_tx->errcnt, &target_tx->errcnt); + __put_user(host_tx->stbcnt, &target_tx->stbcnt); + __put_user(host_tx->tai, &target_tx->tai); + + unlock_user_struct(target_tx, target_addr, 1); + return 0; +} + + static inline abi_long target_to_host_sigevent(struct sigevent *host_sevp, abi_ulong target_addr) { @@ -9543,7 +9615,20 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #endif case TARGET_NR_adjtimex: - goto unimplemented; + { + struct timex host_buf; + + if (target_to_host_timex(&host_buf, arg1) != 0) { + goto efault; + } + ret = get_errno(adjtimex(&host_buf)); + if (!is_error(ret)) { + if (host_to_target_timex(arg1, &host_buf) != 0) { + goto efault; + } + } + } + break; #ifdef TARGET_NR_create_module case TARGET_NR_create_module: #endif diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 9fdbe865dc..ca8fa6ee91 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -207,6 +207,34 @@ struct target_itimerspec { struct target_timespec it_value; }; +struct target_timex { + abi_uint modes; /* Mode selector */ + abi_long offset; /* Time offset */ + abi_long freq; /* Frequency offset */ + abi_long maxerror; /* Maximum error (microseconds) */ + abi_long esterror; /* Estimated error (microseconds) */ + abi_int status; /* Clock command/status */ + abi_long constant; /* PLL (phase-locked loop) time constant */ + abi_long precision; /* Clock precision (microseconds, ro) */ + abi_long tolerance; /* Clock freq. tolerance (ppm, ro) */ + struct target_timeval time; /* Current time */ + abi_long tick; /* Microseconds between clock ticks */ + abi_long ppsfreq; /* PPS (pulse per second) frequency */ + abi_long jitter; /* PPS jitter (ro); nanoseconds */ + abi_int shift; /* PPS interval duration (seconds) */ + abi_long stabil; /* PPS stability */ + abi_long jitcnt; /* PPS jitter limit exceeded (ro) */ + abi_long calcnt; /* PPS calibration intervals */ + abi_long errcnt; /* PPS calibration errors */ + abi_long stbcnt; /* PPS stability limit exceeded */ + abi_int tai; /* TAI offset */ + + /* Further padding bytes to allow for future expansion */ + abi_int:32; abi_int:32; abi_int:32; abi_int:32; + abi_int:32; abi_int:32; abi_int:32; abi_int:32; + abi_int:32; abi_int:32; abi_int:32; +}; + typedef abi_long target_clock_t; #define TARGET_HZ 100 |