summaryrefslogtreecommitdiff
path: root/linux-user
diff options
context:
space:
mode:
authorAleksandar Markovic <aleksandar.markovic@imgtec.com>2016-09-22 18:56:50 +0200
committerRiku Voipio <riku.voipio@linaro.org>2016-10-21 15:19:39 +0300
commit19f59bcef91cd4abc04d10c9ecbf5183b71f1b06 (patch)
tree0ebd24f32a475fe2ac3ddf9177798b486c6ce9bf /linux-user
parentda158a86c407fa7b9da848b571356a26809d8df9 (diff)
downloadqemu-19f59bcef91cd4abc04d10c9ecbf5183b71f1b06.tar.gz
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.c46
-rw-r--r--linux-user/strace.list3
-rw-r--r--linux-user/syscall.c87
-rw-r--r--linux-user/syscall_defs.h28
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