xref: /openbmc/linux/kernel/time/vsyscall.c (revision b9890054)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright 2019 ARM Ltd.
4  *
5  * Generic implementation of update_vsyscall and update_vsyscall_tz.
6  *
7  * Based on the x86 specific implementation.
8  */
9 
10 #include <linux/hrtimer.h>
11 #include <linux/timekeeper_internal.h>
12 #include <vdso/datapage.h>
13 #include <vdso/helpers.h>
14 #include <vdso/vsyscall.h>
15 
16 static inline void update_vdso_data(struct vdso_data *vdata,
17 				    struct timekeeper *tk)
18 {
19 	struct vdso_timestamp *vdso_ts;
20 	u64 nsec, sec;
21 
22 	vdata[CS_HRES_COARSE].cycle_last	= tk->tkr_mono.cycle_last;
23 	vdata[CS_HRES_COARSE].mask		= tk->tkr_mono.mask;
24 	vdata[CS_HRES_COARSE].mult		= tk->tkr_mono.mult;
25 	vdata[CS_HRES_COARSE].shift		= tk->tkr_mono.shift;
26 	vdata[CS_RAW].cycle_last		= tk->tkr_raw.cycle_last;
27 	vdata[CS_RAW].mask			= tk->tkr_raw.mask;
28 	vdata[CS_RAW].mult			= tk->tkr_raw.mult;
29 	vdata[CS_RAW].shift			= tk->tkr_raw.shift;
30 
31 	/* CLOCK_REALTIME */
32 	vdso_ts		= &vdata[CS_HRES_COARSE].basetime[CLOCK_REALTIME];
33 	vdso_ts->sec	= tk->xtime_sec;
34 	vdso_ts->nsec	= tk->tkr_mono.xtime_nsec;
35 
36 	/* CLOCK_MONOTONIC */
37 	vdso_ts		= &vdata[CS_HRES_COARSE].basetime[CLOCK_MONOTONIC];
38 	vdso_ts->sec	= tk->xtime_sec + tk->wall_to_monotonic.tv_sec;
39 
40 	nsec = tk->tkr_mono.xtime_nsec;
41 	nsec += ((u64)tk->wall_to_monotonic.tv_nsec << tk->tkr_mono.shift);
42 	while (nsec >= (((u64)NSEC_PER_SEC) << tk->tkr_mono.shift)) {
43 		nsec -= (((u64)NSEC_PER_SEC) << tk->tkr_mono.shift);
44 		vdso_ts->sec++;
45 	}
46 	vdso_ts->nsec	= nsec;
47 
48 	/* Copy MONOTONIC time for BOOTTIME */
49 	sec	= vdso_ts->sec;
50 	/* Add the boot offset */
51 	sec	+= tk->monotonic_to_boot.tv_sec;
52 	nsec	+= (u64)tk->monotonic_to_boot.tv_nsec << tk->tkr_mono.shift;
53 
54 	/* CLOCK_BOOTTIME */
55 	vdso_ts		= &vdata[CS_HRES_COARSE].basetime[CLOCK_BOOTTIME];
56 	vdso_ts->sec	= sec;
57 
58 	while (nsec >= (((u64)NSEC_PER_SEC) << tk->tkr_mono.shift)) {
59 		nsec -= (((u64)NSEC_PER_SEC) << tk->tkr_mono.shift);
60 		vdso_ts->sec++;
61 	}
62 	vdso_ts->nsec	= nsec;
63 
64 	/* CLOCK_MONOTONIC_RAW */
65 	vdso_ts		= &vdata[CS_RAW].basetime[CLOCK_MONOTONIC_RAW];
66 	vdso_ts->sec	= tk->raw_sec;
67 	vdso_ts->nsec	= tk->tkr_raw.xtime_nsec;
68 
69 	/* CLOCK_TAI */
70 	vdso_ts		= &vdata[CS_HRES_COARSE].basetime[CLOCK_TAI];
71 	vdso_ts->sec	= tk->xtime_sec + (s64)tk->tai_offset;
72 	vdso_ts->nsec	= tk->tkr_mono.xtime_nsec;
73 
74 	/*
75 	 * Read without the seqlock held by clock_getres().
76 	 * Note: No need to have a second copy.
77 	 */
78 	WRITE_ONCE(vdata[CS_HRES_COARSE].hrtimer_res, hrtimer_resolution);
79 }
80 
81 void update_vsyscall(struct timekeeper *tk)
82 {
83 	struct vdso_data *vdata = __arch_get_k_vdso_data();
84 	struct vdso_timestamp *vdso_ts;
85 	u64 nsec;
86 
87 	if (__arch_update_vdso_data()) {
88 		/*
89 		 * Some architectures might want to skip the update of the
90 		 * data page.
91 		 */
92 		return;
93 	}
94 
95 	/* copy vsyscall data */
96 	vdso_write_begin(vdata);
97 
98 	vdata[CS_HRES_COARSE].clock_mode	= __arch_get_clock_mode(tk);
99 	vdata[CS_RAW].clock_mode		= __arch_get_clock_mode(tk);
100 
101 	/* CLOCK_REALTIME_COARSE */
102 	vdso_ts		= &vdata[CS_HRES_COARSE].basetime[CLOCK_REALTIME_COARSE];
103 	vdso_ts->sec	= tk->xtime_sec;
104 	vdso_ts->nsec	= tk->tkr_mono.xtime_nsec >> tk->tkr_mono.shift;
105 
106 	/* CLOCK_MONOTONIC_COARSE */
107 	vdso_ts		= &vdata[CS_HRES_COARSE].basetime[CLOCK_MONOTONIC_COARSE];
108 	vdso_ts->sec	= tk->xtime_sec + tk->wall_to_monotonic.tv_sec;
109 	nsec		= tk->tkr_mono.xtime_nsec >> tk->tkr_mono.shift;
110 	nsec		= nsec + tk->wall_to_monotonic.tv_nsec;
111 	vdso_ts->sec	+= __iter_div_u64_rem(nsec, NSEC_PER_SEC, &vdso_ts->nsec);
112 
113 	update_vdso_data(vdata, tk);
114 
115 	__arch_update_vsyscall(vdata, tk);
116 
117 	vdso_write_end(vdata);
118 
119 	__arch_sync_vdso_data(vdata);
120 }
121 
122 void update_vsyscall_tz(void)
123 {
124 	struct vdso_data *vdata = __arch_get_k_vdso_data();
125 
126 	vdata[CS_HRES_COARSE].tz_minuteswest = sys_tz.tz_minuteswest;
127 	vdata[CS_HRES_COARSE].tz_dsttime = sys_tz.tz_dsttime;
128 
129 	__arch_sync_vdso_data(vdata);
130 }
131