xref: /freebsd/sys/kern/kern_ntptime.c (revision 0ea3482342b4d7d6e71f3007ce4dafe445c639fd)
1 /******************************************************************************
2  *                                                                            *
3  * Copyright (c) David L. Mills 1993, 1994                                    *
4  *                                                                            *
5  * Permission to use, copy, modify, and distribute this software and its      *
6  * documentation for any purpose and without fee is hereby granted, provided  *
7  * that the above copyright notice appears in all copies and that both the    *
8  * copyright notice and this permission notice appear in supporting           *
9  * documentation, and that the name University of Delaware not be used in     *
10  * advertising or publicity pertaining to distribution of the software        *
11  * without specific, written prior permission.  The University of Delaware    *
12  * makes no representations about the suitability this software for any       *
13  * purpose.  It is provided "as is" without express or implied warranty.      *
14  *                                                                            *
15  ******************************************************************************/
16 
17 /*
18  * Modification history kern_ntptime.c
19  *
20  * 24 Mar 94	David L. Mills
21  *	Revised syscall interface to include new variables for PPS
22  *	time discipline.
23  *
24  * 14 Feb 94	David L. Mills
25  *	Added code for external clock
26  *
27  * 28 Nov 93	David L. Mills
28  *	Revised frequency scaling to conform with adjusted parameters
29  *
30  * 17 Sep 93	David L. Mills
31  *	Created file
32  */
33 /*
34  * ntp_gettime(), ntp_adjtime() - precision time interface for SunOS
35  * 4.1.1 and 4.1.3
36  *
37  * These routines consitute the Network Time Protocol (NTP) interfaces
38  * for user and daemon application programs. The ntp_gettime() routine
39  * provides the time, maximum error (synch distance) and estimated error
40  * (dispersion) to client user application programs. The ntp_adjtime()
41  * routine is used by the NTP daemon to adjust the system clock to an
42  * externally derived time. The time offset and related variables set by
43  * this routine are used by hardclock() to adjust the phase and
44  * frequency of the phase-lock loop which controls the system clock.
45  */
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/sysproto.h>
49 #include <sys/kernel.h>
50 #include <sys/proc.h>
51 #include <sys/timex.h>
52 #include <vm/vm.h>
53 #include <sys/sysctl.h>
54 
55 /*
56  * The following variables are used by the hardclock() routine in the
57  * kern_clock.c module and are described in that module.
58  */
59 extern int time_state;		/* clock state */
60 extern int time_status;		/* clock status bits */
61 extern long time_offset;	/* time adjustment (us) */
62 extern long time_freq;		/* frequency offset (scaled ppm) */
63 extern long time_maxerror;	/* maximum error (us) */
64 extern long time_esterror;	/* estimated error (us) */
65 extern long time_constant;	/* pll time constant */
66 extern long time_precision;	/* clock precision (us) */
67 extern long time_tolerance;	/* frequency tolerance (scaled ppm) */
68 
69 #ifdef PPS_SYNC
70 /*
71  * The following variables are used only if the PPS signal discipline
72  * is configured in the kernel.
73  */
74 extern int pps_shift;		/* interval duration (s) (shift) */
75 extern long pps_freq;		/* pps frequency offset (scaled ppm) */
76 extern long pps_jitter;		/* pps jitter (us) */
77 extern long pps_stabil;		/* pps stability (scaled ppm) */
78 extern long pps_jitcnt;		/* jitter limit exceeded */
79 extern long pps_calcnt;		/* calibration intervals */
80 extern long pps_errcnt;		/* calibration errors */
81 extern long pps_stbcnt;		/* stability limit exceeded */
82 #endif /* PPS_SYNC */
83 
84 int
85 ntp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
86 	   void *newp, size_t newlen, struct proc *p)
87 {
88 	struct timeval atv;
89 	struct ntptimeval ntv;
90 	int s;
91 
92 	/* All names at this level are terminal. */
93 	if (namelen != 1) {
94 		return ENOTDIR;
95 	}
96 
97 	if (name[0] != NTP_PLL_GETTIME) {
98 		return EOPNOTSUPP;
99 	}
100 
101 	s = splclock();
102 #ifdef EXT_CLOCK
103 	/*
104 	 * The microtime() external clock routine returns a
105 	 * status code. If less than zero, we declare an error
106 	 * in the clock status word and return the kernel
107 	 * (software) time variable. While there are other
108 	 * places that call microtime(), this is the only place
109 	 * that matters from an application point of view.
110 	 */
111 	if (microtime(&atv) < 0) {
112 		time_status |= STA_CLOCKERR;
113 		ntv.time = time;
114 	} else {
115 		time_status &= ~STA_CLOCKERR;
116 	}
117 #else /* EXT_CLOCK */
118 	microtime(&atv);
119 #endif /* EXT_CLOCK */
120 	ntv.time = atv;
121 	ntv.maxerror = time_maxerror;
122 	ntv.esterror = time_esterror;
123 	splx(s);
124 
125 	ntv.time_state = time_state;
126 
127 	/*
128 	 * Status word error decode. If any of these conditions
129 	 * occur, an error is returned, instead of the status
130 	 * word. Most applications will care only about the fact
131 	 * the system clock may not be trusted, not about the
132 	 * details.
133 	 *
134 	 * Hardware or software error
135 	 */
136 	if (time_status & (STA_UNSYNC | STA_CLOCKERR)) {
137 		ntv.time_state = TIME_ERROR;
138 	}
139 
140 	/*
141 	 * PPS signal lost when either time or frequency
142 	 * synchronization requested
143 	 */
144 	if (time_status & (STA_PPSFREQ | STA_PPSTIME) &&
145 	    !(time_status & STA_PPSSIGNAL)) {
146 		ntv.time_state = TIME_ERROR;
147 	}
148 
149 	/*
150 	 * PPS jitter exceeded when time synchronization
151 	 * requested
152 	 */
153 	if (time_status & STA_PPSTIME &&
154 	    time_status & STA_PPSJITTER) {
155 		ntv.time_state = TIME_ERROR;
156 	}
157 
158 	/*
159 	 * PPS wander exceeded or calibration error when
160 	 * frequency synchronization requested
161 	 */
162 	if (time_status & STA_PPSFREQ &&
163 	    time_status & (STA_PPSWANDER | STA_PPSERROR)) {
164 		ntv.time_state = TIME_ERROR;
165 	}
166 	return(sysctl_rdstruct(oldp, oldlenp, newp, &ntv, sizeof ntv));
167 }
168 
169 /*
170  * ntp_adjtime() - NTP daemon application interface
171  */
172 #ifndef _SYS_SYSPROTO_H_
173 struct ntp_adjtime_args {
174   struct timex *tp;
175 };
176 #endif
177 
178 int
179 ntp_adjtime(struct proc *p, struct ntp_adjtime_args *uap, int *retval)
180 {
181 	struct timex ntv;
182 	int modes;
183 	int s;
184 	int error;
185 
186 	error = copyin((caddr_t)uap->tp, (caddr_t)&ntv, sizeof(ntv));
187 	if (error)
188 		return error;
189 
190 	/*
191 	 * Update selected clock variables - only the superuser can
192 	 * change anything. Note that there is no error checking here on
193 	 * the assumption the superuser should know what it is doing.
194 	 */
195 	modes = ntv.modes;
196 	if ((modes != 0)
197 	    && (error = suser(p->p_cred->pc_ucred, &p->p_acflag)))
198 		return error;
199 
200 	s = splclock();
201 	if (modes & MOD_FREQUENCY)
202 #ifdef PPS_SYNC
203 		time_freq = ntv.freq - pps_freq;
204 #else /* PPS_SYNC */
205 		time_freq = ntv.freq;
206 #endif /* PPS_SYNC */
207 	if (modes & MOD_MAXERROR)
208 		time_maxerror = ntv.maxerror;
209 	if (modes & MOD_ESTERROR)
210 		time_esterror = ntv.esterror;
211 	if (modes & MOD_STATUS) {
212 		time_status &= STA_RONLY;
213 		time_status |= ntv.status & ~STA_RONLY;
214 	}
215 	if (modes & MOD_TIMECONST)
216 		time_constant = ntv.constant;
217 	if (modes & MOD_OFFSET)
218 		hardupdate(ntv.offset);
219 
220 	/*
221 	 * Retrieve all clock variables
222 	 */
223 	if (time_offset < 0)
224 		ntv.offset = -(-time_offset >> SHIFT_UPDATE);
225 	else
226 		ntv.offset = time_offset >> SHIFT_UPDATE;
227 #ifdef PPS_SYNC
228 	ntv.freq = time_freq + pps_freq;
229 #else /* PPS_SYNC */
230 	ntv.freq = time_freq;
231 #endif /* PPS_SYNC */
232 	ntv.maxerror = time_maxerror;
233 	ntv.esterror = time_esterror;
234 	ntv.status = time_status;
235 	ntv.constant = time_constant;
236 	ntv.precision = time_precision;
237 	ntv.tolerance = time_tolerance;
238 #ifdef PPS_SYNC
239 	ntv.shift = pps_shift;
240 	ntv.ppsfreq = pps_freq;
241 	ntv.jitter = pps_jitter >> PPS_AVG;
242 	ntv.stabil = pps_stabil;
243 	ntv.calcnt = pps_calcnt;
244 	ntv.errcnt = pps_errcnt;
245 	ntv.jitcnt = pps_jitcnt;
246 	ntv.stbcnt = pps_stbcnt;
247 #endif /* PPS_SYNC */
248 	(void)splx(s);
249 
250 	error = copyout((caddr_t)&ntv, (caddr_t)uap->tp, sizeof(ntv));
251 	if (!error) {
252 		/*
253 		 * Status word error decode. See comments in
254 		 * ntp_gettime() routine.
255 		 */
256 		retval[0] = time_state;
257 		if (time_status & (STA_UNSYNC | STA_CLOCKERR))
258 			retval[0] = TIME_ERROR;
259 		if (time_status & (STA_PPSFREQ | STA_PPSTIME) &&
260 		    !(time_status & STA_PPSSIGNAL))
261 			retval[0] = TIME_ERROR;
262 		if (time_status & STA_PPSTIME &&
263 		    time_status & STA_PPSJITTER)
264 			retval[0] = TIME_ERROR;
265 		if (time_status & STA_PPSFREQ &&
266 		    time_status & (STA_PPSWANDER | STA_PPSERROR))
267 			retval[0] = TIME_ERROR;
268 	}
269 	return error;
270 }
271 
272 
273