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:
		
							parent
							
								
									da158a86c4
								
							
						
					
					
						commit
						19f59bcef9
					
				| @ -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), | ||||
|  | ||||
| @ -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 }, | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Aleksandar Markovic
						Aleksandar Markovic