Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2018 Intel Corporation
3 : : * Copyright (c) 2018 Friedt Professional Engineering Services, Inc
4 : : * Copyright (c) 2025 Tenstorrent AI ULC
5 : : *
6 : : * SPDX-License-Identifier: Apache-2.0
7 : : */
8 : :
9 : : #include <errno.h>
10 : : #include <stdbool.h>
11 : : #include <stddef.h>
12 : : #include <stdint.h>
13 : :
14 : : #include <zephyr/kernel.h>
15 : : #include <zephyr/internal/syscall_handler.h>
16 : : #include <zephyr/sys/clock.h>
17 : : #include <zephyr/sys/timeutil.h>
18 : : #include <zephyr/toolchain.h>
19 : :
20 : : /*
21 : : * `k_uptime_get` returns a timestamp offset on an always increasing
22 : : * value from the system start. To support the `SYS_CLOCK_REALTIME`
23 : : * clock, this `rt_clock_offset` records the time that the system was
24 : : * started. This can either be set via 'sys_clock_settime', or could be
25 : : * set from a real time clock, if such hardware is present.
26 : : */
27 : : static struct timespec rt_clock_offset;
28 : : static struct k_spinlock rt_clock_offset_lock;
29 : :
30 : 0 : static bool is_valid_clock_id(int clock_id)
31 : : {
32 [ # # ]: 0 : switch (clock_id) {
33 : : case SYS_CLOCK_MONOTONIC:
34 : : case SYS_CLOCK_REALTIME:
35 : : return true;
36 : 0 : default:
37 : 0 : return false;
38 : : }
39 : : }
40 : :
41 : 0 : static void timespec_from_ticks(uint64_t ticks, struct timespec *ts)
42 : : {
43 : 0 : uint64_t elapsed_secs = ticks / CONFIG_SYS_CLOCK_TICKS_PER_SEC;
44 : 0 : uint64_t nremainder = ticks % CONFIG_SYS_CLOCK_TICKS_PER_SEC;
45 : :
46 : 0 : *ts = (struct timespec){
47 : 0 : .tv_sec = (time_t)elapsed_secs,
48 : : /* For ns 32 bit conversion can be used since its smaller than 1sec. */
49 : 0 : .tv_nsec = (int32_t)k_ticks_to_ns_floor32(nremainder),
50 : : };
51 : 0 : }
52 : :
53 : 0 : int sys_clock_from_clockid(int clock_id)
54 : : {
55 : 0 : switch (clock_id) {
56 : : #if defined(CLOCK_REALTIME) || defined(_POSIX_C_SOURCE)
57 : : case (int)CLOCK_REALTIME:
58 : : return SYS_CLOCK_REALTIME;
59 : : #endif
60 : : #if defined(CLOCK_MONOTONIC) || defined(_POSIX_MONOTONIC_CLOCK)
61 : : case (int)CLOCK_MONOTONIC:
62 : : return SYS_CLOCK_MONOTONIC;
63 : : #endif
64 : : default:
65 : 0 : return -EINVAL;
66 : : }
67 : : }
68 : :
69 : 0 : int sys_clock_gettime(int clock_id, struct timespec *ts)
70 : : {
71 [ # # ]: 0 : if (!is_valid_clock_id(clock_id)) {
72 : : return -EINVAL;
73 : : }
74 : :
75 [ # # # ]: 0 : switch (clock_id) {
76 : 0 : case SYS_CLOCK_REALTIME: {
77 : 0 : struct timespec offset;
78 : :
79 : 0 : timespec_from_ticks(k_uptime_ticks(), ts);
80 : 0 : sys_clock_getrtoffset(&offset);
81 [ # # ]: 0 : if (unlikely(!timespec_add(ts, &offset))) {
82 : : /* Saturate rather than reporting an overflow in 292 billion years */
83 : 0 : *ts = (struct timespec){
84 : : .tv_sec = (time_t)INT64_MAX,
85 : : .tv_nsec = NSEC_PER_SEC - 1,
86 : : };
87 : : }
88 : 0 : } break;
89 : :
90 : 0 : case SYS_CLOCK_MONOTONIC:
91 : 0 : timespec_from_ticks(k_uptime_ticks(), ts);
92 : 0 : break;
93 : :
94 : 0 : default:
95 : 0 : CODE_UNREACHABLE;
96 : : return -EINVAL; /* Should never reach here */
97 : : }
98 : :
99 [ # # ]: 0 : __ASSERT_NO_MSG(timespec_is_valid(ts));
100 : :
101 : : return 0;
102 : : }
103 : :
104 : 0 : void z_impl_sys_clock_getrtoffset(struct timespec *tp)
105 : : {
106 [ # # ]: 0 : __ASSERT_NO_MSG(tp != NULL);
107 : :
108 [ # # ]: 0 : K_SPINLOCK(&rt_clock_offset_lock) {
109 : 0 : *tp = rt_clock_offset;
110 : : }
111 : :
112 [ # # ]: 0 : __ASSERT_NO_MSG(timespec_is_valid(tp));
113 : 0 : }
114 : :
115 : : #ifdef CONFIG_USERSPACE
116 : : void z_vrfy_sys_clock_getrtoffset(struct timespec *tp)
117 : : {
118 : : K_OOPS(K_SYSCALL_MEMORY_WRITE(tp, sizeof(*tp)));
119 : : return z_impl_sys_clock_getrtoffset(tp);
120 : : }
121 : : #include <zephyr/syscalls/sys_clock_getrtoffset_mrsh.c>
122 : : #endif /* CONFIG_USERSPACE */
123 : :
124 : 0 : int z_impl_sys_clock_settime(int clock_id, const struct timespec *tp)
125 : : {
126 : 0 : struct timespec offset;
127 : :
128 [ # # ]: 0 : if (clock_id != SYS_CLOCK_REALTIME) {
129 : : return -EINVAL;
130 : : }
131 : :
132 [ # # ]: 0 : if (!timespec_is_valid(tp)) {
133 : : return -EINVAL;
134 : : }
135 : :
136 : 0 : timespec_from_ticks(k_uptime_ticks(), &offset);
137 : 0 : (void)timespec_negate(&offset);
138 : 0 : (void)timespec_add(&offset, tp);
139 : :
140 [ # # ]: 0 : K_SPINLOCK(&rt_clock_offset_lock) {
141 : 0 : rt_clock_offset = offset;
142 : : }
143 : :
144 : 0 : return 0;
145 : : }
146 : :
147 : : #ifdef CONFIG_USERSPACE
148 : : int z_vrfy_sys_clock_settime(int clock_id, const struct timespec *ts)
149 : : {
150 : : K_OOPS(K_SYSCALL_MEMORY_READ(ts, sizeof(*ts)));
151 : : return z_impl_sys_clock_settime(clock_id, ts);
152 : : }
153 : : #include <zephyr/syscalls/sys_clock_settime_mrsh.c>
154 : : #endif /* CONFIG_USERSPACE */
155 : :
156 : 0 : int z_impl_sys_clock_nanosleep(int clock_id, int flags, const struct timespec *rqtp,
157 : : struct timespec *rmtp)
158 : : {
159 : 0 : k_timepoint_t end;
160 : 0 : k_timeout_t timeout;
161 : 0 : struct timespec duration = {0, 0};
162 : 0 : const bool update_rmtp = rmtp != NULL;
163 : 0 : const bool abstime = (flags & SYS_TIMER_ABSTIME) != 0;
164 : :
165 [ # # ]: 0 : if (!is_valid_clock_id(clock_id)) {
166 : : return -EINVAL;
167 : : }
168 : :
169 [ # # # # ]: 0 : if ((rqtp->tv_sec < 0) || !timespec_is_valid(rqtp)) {
170 : 0 : return -EINVAL;
171 : : }
172 : :
173 [ # # ]: 0 : if (abstime) {
174 : : /* convert absolute time to relative time duration */
175 : 0 : (void)sys_clock_gettime(clock_id, &duration);
176 : 0 : (void)timespec_negate(&duration);
177 : 0 : (void)timespec_add(&duration, rqtp);
178 : : } else {
179 : 0 : duration = *rqtp;
180 : : }
181 : :
182 : : /* sleep for relative time duration */
183 : 0 : if ((sizeof(rqtp->tv_sec) == sizeof(int64_t)) &&
184 : : unlikely(rqtp->tv_sec >= (time_t)(UINT64_MAX / NSEC_PER_SEC))) {
185 : : uint64_t ns = (uint64_t)k_sleep(K_SECONDS(duration.tv_sec - 1)) * NSEC_PER_MSEC;
186 : : struct timespec rem = {
187 : : .tv_sec = (time_t)(ns / NSEC_PER_SEC),
188 : : .tv_nsec = ns % NSEC_PER_MSEC,
189 : : };
190 : :
191 : : duration.tv_sec = 1;
192 : : (void)timespec_add(&duration, &rem);
193 : : }
194 : :
195 : 0 : timeout = timespec_to_timeout(&duration, NULL);
196 : 0 : end = sys_timepoint_calc(timeout);
197 : 0 : do {
198 : 0 : (void)k_sleep(timeout);
199 : 0 : timeout = sys_timepoint_timeout(end);
200 [ # # ]: 0 : } while (!K_TIMEOUT_EQ(timeout, K_NO_WAIT));
201 : :
202 [ # # ]: 0 : if (update_rmtp) {
203 : 0 : *rmtp = (struct timespec){
204 : : .tv_sec = 0,
205 : : .tv_nsec = 0,
206 : : };
207 : : }
208 : :
209 : : return 0;
210 : : }
211 : :
212 : : #ifdef CONFIG_USERSPACE
213 : : int z_vrfy_sys_clock_nanosleep(int clock_id, int flags, const struct timespec *rqtp,
214 : : struct timespec *rmtp)
215 : : {
216 : : K_OOPS(K_SYSCALL_MEMORY_READ(rqtp, sizeof(*rqtp)));
217 : : if (rmtp != NULL) {
218 : : K_OOPS(K_SYSCALL_MEMORY_WRITE(rmtp, sizeof(*rmtp)));
219 : : }
220 : : return z_impl_sys_clock_nanosleep(clock_id, flags, rqtp, rmtp);
221 : : }
222 : : #include <zephyr/syscalls/sys_clock_nanosleep_mrsh.c>
223 : : #endif /* CONFIG_USERSPACE */
224 : :
225 : : #ifdef CONFIG_ZTEST
226 : : #include <zephyr/ztest.h>
227 : 42 : static void reset_clock_offset(void)
228 : : {
229 [ + + ]: 84 : K_SPINLOCK(&rt_clock_offset_lock) {
230 : 42 : rt_clock_offset = (struct timespec){0};
231 : : }
232 : 42 : }
233 : :
234 : 42 : static void clock_offset_reset_rule_after(const struct ztest_unit_test *test, void *data)
235 : : {
236 : 42 : ARG_UNUSED(test);
237 : 42 : ARG_UNUSED(data);
238 : :
239 : 42 : reset_clock_offset();
240 : 42 : }
241 : :
242 : : ZTEST_RULE(clock_offset_reset_rule, NULL, clock_offset_reset_rule_after);
243 : : #endif /* CONFIG_ZTEST */
|