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 | */ |
64 | struct timezone linux_sys_tz; |
65 | |
66 | int |
67 | linux_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 | |
90 | int |
91 | linux_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 | |
118 | void |
119 | native_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 | |
125 | void |
126 | linux_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 | |
132 | int |
133 | linux_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 | |
159 | int |
160 | linux_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 | |
180 | int |
181 | linux_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(<s, &ts); |
201 | return copyout(<s, SCARG(uap, tp), sizeof lts); |
202 | } |
203 | |
204 | int |
205 | linux_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), <s, sizeof lts); |
221 | if (error != 0) |
222 | return error; |
223 | |
224 | linux_to_native_timespec(&ts, <s); |
225 | |
226 | return clock_settime1(l->l_proc, id, &ts, true); |
227 | } |
228 | |
229 | int |
230 | linux_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(<s, &ts); |
250 | return copyout(<s, SCARG(uap, tp), sizeof lts); |
251 | } |
252 | |
253 | int |
254 | linux_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 | |