1/* $NetBSD: linux_time.c,v 1.37 2014/01/13 10:33:03 njoly Exp $ */
2
3/*-
4 * Copyright (c) 2001 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Emmanuel Dreyfus.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: linux_time.c,v 1.37 2014/01/13 10:33:03 njoly Exp $");
34
35#include <sys/param.h>
36#include <sys/ucred.h>
37#include <sys/kauth.h>
38#include <sys/mount.h>
39#include <sys/signal.h>
40#include <sys/stdint.h>
41#include <sys/time.h>
42#include <sys/systm.h>
43#include <sys/sched.h>
44#include <sys/syscallargs.h>
45#include <sys/lwp.h>
46#include <sys/proc.h>
47
48#include <compat/linux/common/linux_types.h>
49#include <compat/linux/common/linux_signal.h>
50#include <compat/linux/common/linux_machdep.h>
51#include <compat/linux/common/linux_sched.h>
52#include <compat/linux/common/linux_ipc.h>
53#include <compat/linux/common/linux_sem.h>
54
55#include <compat/linux/linux_syscallargs.h>
56
57#include <compat/common/compat_util.h>
58
59/*
60 * Linux keeps track of a system timezone in the kernel. It is readen
61 * by gettimeofday and set by settimeofday. This emulates this behavior
62 * See linux/kernel/time.c
63 */
64struct timezone linux_sys_tz;
65
66int
67linux_sys_gettimeofday(struct lwp *l, const struct linux_sys_gettimeofday_args *uap, register_t *retval)
68{
69 /* {
70 syscallarg(struct timeval50 *) tz;
71 syscallarg(struct timezone *) tzp;
72 } */
73 int error = 0;
74
75 if (SCARG(uap, tp)) {
76 error = compat_50_sys_gettimeofday(l, (const void *)uap, retval);
77 if (error)
78 return (error);
79 }
80
81 if (SCARG(uap, tzp)) {
82 error = copyout(&linux_sys_tz, SCARG(uap, tzp), sizeof(linux_sys_tz));
83 if (error)
84 return (error);
85 }
86
87 return (0);
88}
89
90int
91linux_sys_settimeofday(struct lwp *l, const struct linux_sys_settimeofday_args *uap, register_t *retval)
92{
93 /* {
94 syscallarg(struct timeval50 *) tp;
95 syscallarg(struct timezone *) tzp;
96 } */
97 int error = 0;
98
99 if (SCARG(uap, tp)) {
100 error = compat_50_sys_settimeofday(l, (const void *)uap, retval);
101 if (error)
102 return (error);
103 }
104
105 /*
106 * If user is not the superuser, we returned
107 * after the sys_settimeofday() call.
108 */
109 if (SCARG(uap, tzp)) {
110 error = copyin(SCARG(uap, tzp), &linux_sys_tz, sizeof(linux_sys_tz));
111 if (error)
112 return (error);
113 }
114
115 return (0);
116}
117
118void
119native_to_linux_timespec(struct linux_timespec *ltp, struct timespec *ntp)
120{
121 ltp->tv_sec = ntp->tv_sec;
122 ltp->tv_nsec = ntp->tv_nsec;
123}
124
125void
126linux_to_native_timespec(struct timespec *ntp, struct linux_timespec *ltp)
127{
128 ntp->tv_sec = ltp->tv_sec;
129 ntp->tv_nsec = ltp->tv_nsec;
130}
131
132int
133linux_sys_nanosleep(struct lwp *l, const struct linux_sys_nanosleep_args *uap,
134 register_t *retval)
135{
136 /* {
137 syscallarg(struct linux_timespec *) rqtp;
138 syscallarg(struct linux_timespec *) rmtp;
139 } */
140 struct timespec rqts, rmts;
141 struct linux_timespec lrqts, lrmts;
142 int error, error1;
143
144 error = copyin(SCARG(uap, rqtp), &lrqts, sizeof(lrqts));
145 if (error != 0)
146 return error;
147 linux_to_native_timespec(&rqts, &lrqts);
148
149 error = nanosleep1(l, CLOCK_MONOTONIC, 0, &rqts,
150 SCARG(uap, rmtp) ? &rmts : NULL);
151 if (SCARG(uap, rmtp) == NULL || (error != 0 && error != EINTR))
152 return error;
153
154 native_to_linux_timespec(&lrmts, &rmts);
155 error1 = copyout(&lrmts, SCARG(uap, rmtp), sizeof(lrmts));
156 return error1 ? error1 : error;
157}
158
159int
160linux_to_native_clockid(clockid_t *n, clockid_t l)
161{
162 switch (l) {
163 case LINUX_CLOCK_REALTIME:
164 *n = CLOCK_REALTIME;
165 break;
166 case LINUX_CLOCK_MONOTONIC:
167 *n = CLOCK_MONOTONIC;
168 break;
169 case LINUX_CLOCK_PROCESS_CPUTIME_ID:
170 case LINUX_CLOCK_THREAD_CPUTIME_ID:
171 case LINUX_CLOCK_REALTIME_HR:
172 case LINUX_CLOCK_MONOTONIC_HR:
173 default:
174 return EINVAL;
175 }
176
177 return 0;
178}
179
180int
181linux_sys_clock_gettime(struct lwp *l, const struct linux_sys_clock_gettime_args *uap, register_t *retval)
182{
183 /* {
184 syscallarg(clockid_t) which;
185 syscallarg(struct linux_timespec *)tp;
186 } */
187 int error;
188 clockid_t id;
189 struct timespec ts;
190 struct linux_timespec lts;
191
192 error = linux_to_native_clockid(&id, SCARG(uap, which));
193 if (error != 0)
194 return error;
195
196 error = clock_gettime1(id, &ts);
197 if (error != 0)
198 return error;
199
200 native_to_linux_timespec(&lts, &ts);
201 return copyout(&lts, SCARG(uap, tp), sizeof lts);
202}
203
204int
205linux_sys_clock_settime(struct lwp *l, const struct linux_sys_clock_settime_args *uap, register_t *retval)
206{
207 /* {
208 syscallarg(clockid_t) which;
209 syscallarg(struct linux_timespec *)tp;
210 } */
211 struct timespec ts;
212 struct linux_timespec lts;
213 clockid_t id;
214 int error;
215
216 error = linux_to_native_clockid(&id, SCARG(uap, which));
217 if (error != 0)
218 return error;
219
220 error = copyin(SCARG(uap, tp), &lts, sizeof lts);
221 if (error != 0)
222 return error;
223
224 linux_to_native_timespec(&ts, &lts);
225
226 return clock_settime1(l->l_proc, id, &ts, true);
227}
228
229int
230linux_sys_clock_getres(struct lwp *l, const struct linux_sys_clock_getres_args *uap, register_t *retval)
231{
232 /* {
233 syscallarg(clockid_t) which;
234 syscallarg(struct linux_timespec *)tp;
235 } */
236 struct timespec ts;
237 struct linux_timespec lts;
238 int error;
239 clockid_t nwhich = 0; /* XXX: GCC */
240
241 error = linux_to_native_clockid(&nwhich, SCARG(uap, which));
242 if (error != 0 || SCARG(uap, tp) == NULL)
243 return error;
244
245 error = clock_getres1(nwhich, &ts);
246 if (error != 0)
247 return error;
248
249 native_to_linux_timespec(&lts, &ts);
250 return copyout(&lts, SCARG(uap, tp), sizeof lts);
251}
252
253int
254linux_sys_clock_nanosleep(struct lwp *l, const struct linux_sys_clock_nanosleep_args *uap, register_t *retval)
255{
256 /* {
257 syscallarg(clockid_t) which;
258 syscallarg(int) flags;
259 syscallarg(struct linux_timespec) *rqtp;
260 syscallarg(struct linux_timespec) *rmtp;
261 } */
262 struct linux_timespec lrqts, lrmts;
263 struct timespec rqts, rmts;
264 int error, error1, flags;
265 clockid_t nwhich;
266
267 flags = SCARG(uap, flags) != 0 ? TIMER_ABSTIME : 0;
268
269 error = linux_to_native_clockid(&nwhich, SCARG(uap, which));
270 if (error != 0)
271 return error;
272
273 error = copyin(SCARG(uap, rqtp), &lrqts, sizeof lrqts);
274 if (error != 0)
275 return error;
276
277 linux_to_native_timespec(&rqts, &lrqts);
278
279 error = nanosleep1(l, nwhich, flags, &rqts,
280 SCARG(uap, rmtp) ? &rmts : NULL);
281 if (SCARG(uap, rmtp) == NULL || (error != 0 && error != EINTR))
282 return error;
283
284 native_to_linux_timespec(&lrmts, &rmts);
285 error1 = copyout(&lrmts, SCARG(uap, rmtp), sizeof lrmts);
286 return error1 ? error1 : error;
287}
288