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>
This commit is contained in:
Aleksandar Markovic 2016-09-22 18:56:50 +02:00 committed by Riku Voipio
parent da158a86c4
commit 19f59bcef9
4 changed files with 162 additions and 2 deletions

View file

@ -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