1 /* $NetBSD: kern_ntptime.c,v 1.22 2003/01/18 10:06:28 thorpej Exp $ */ 2 3 /****************************************************************************** 4 * * 5 * Copyright (c) David L. Mills 1993, 1994 * 6 * * 7 * Permission to use, copy, modify, and distribute this software and its * 8 * documentation for any purpose and without fee is hereby granted, provided * 9 * that the above copyright notice appears in all copies and that both the * 10 * copyright notice and this permission notice appear in supporting * 11 * documentation, and that the name University of Delaware not be used in * 12 * advertising or publicity pertaining to distribution of the software * 13 * without specific, written prior permission. The University of Delaware * 14 * makes no representations about the suitability this software for any * 15 * purpose. It is provided "as is" without express or implied warranty. * 16 * * 17 ******************************************************************************/ 18 19 /* 20 * Modification history kern_ntptime.c 21 * 22 * 24 Sep 94 David L. Mills 23 * Tightened code at exits. 24 * 25 * 24 Mar 94 David L. Mills 26 * Revised syscall interface to include new variables for PPS 27 * time discipline. 28 * 29 * 14 Feb 94 David L. Mills 30 * Added code for external clock 31 * 32 * 28 Nov 93 David L. Mills 33 * Revised frequency scaling to conform with adjusted parameters 34 * 35 * 17 Sep 93 David L. Mills 36 * Created file 37 */ 38 /* 39 * ntp_gettime(), ntp_adjtime() - precision time interface for SunOS 40 * V4.1.1 and V4.1.3 41 * 42 * These routines consitute the Network Time Protocol (NTP) interfaces 43 * for user and daemon application programs. The ntp_gettime() routine 44 * provides the time, maximum error (synch distance) and estimated error 45 * (dispersion) to client user application programs. The ntp_adjtime() 46 * routine is used by the NTP daemon to adjust the system clock to an 47 * externally derived time. The time offset and related variables set by 48 * this routine are used by hardclock() to adjust the phase and 49 * frequency of the phase-lock loop which controls the system clock. 50 */ 51 52 #include <sys/cdefs.h> 53 __KERNEL_RCSID(0, "$NetBSD: kern_ntptime.c,v 1.22 2003/01/18 10:06:28 thorpej Exp $"); 54 55 #include "opt_ntp.h" 56 57 #include <sys/param.h> 58 #include <sys/resourcevar.h> 59 #include <sys/systm.h> 60 #include <sys/kernel.h> 61 #include <sys/proc.h> 62 #include <sys/sysctl.h> 63 #include <sys/timex.h> 64 #include <sys/vnode.h> 65 66 #include <sys/mount.h> 67 #include <sys/sa.h> 68 #include <sys/syscallargs.h> 69 70 #include <machine/cpu.h> 71 72 #ifdef NTP 73 /* 74 * The following variables are used by the hardclock() routine in the 75 * kern_clock.c module and are described in that module. 76 */ 77 extern int time_state; /* clock state */ 78 extern int time_status; /* clock status bits */ 79 extern long time_offset; /* time adjustment (us) */ 80 extern long time_freq; /* frequency offset (scaled ppm) */ 81 extern long time_maxerror; /* maximum error (us) */ 82 extern long time_esterror; /* estimated error (us) */ 83 extern long time_constant; /* pll time constant */ 84 extern long time_precision; /* clock precision (us) */ 85 extern long time_tolerance; /* frequency tolerance (scaled ppm) */ 86 87 #ifdef PPS_SYNC 88 /* 89 * The following variables are used only if the PPS signal discipline 90 * is configured in the kernel. 91 */ 92 extern int pps_shift; /* interval duration (s) (shift) */ 93 extern long pps_freq; /* pps frequency offset (scaled ppm) */ 94 extern long pps_jitter; /* pps jitter (us) */ 95 extern long pps_stabil; /* pps stability (scaled ppm) */ 96 extern long pps_jitcnt; /* jitter limit exceeded */ 97 extern long pps_calcnt; /* calibration intervals */ 98 extern long pps_errcnt; /* calibration errors */ 99 extern long pps_stbcnt; /* stability limit exceeded */ 100 #endif /* PPS_SYNC */ 101 102 /*ARGSUSED*/ 103 /* 104 * ntp_gettime() - NTP user application interface 105 */ 106 int 107 sys_ntp_gettime(l, v, retval) 108 struct lwp *l; 109 void *v; 110 register_t *retval; 111 112 { 113 struct sys_ntp_gettime_args /* { 114 syscallarg(struct ntptimeval *) ntvp; 115 } */ *uap = v; 116 struct timeval atv; 117 struct ntptimeval ntv; 118 int error = 0; 119 int s; 120 121 if (SCARG(uap, ntvp)) { 122 s = splclock(); 123 #ifdef EXT_CLOCK 124 /* 125 * The microtime() external clock routine returns a 126 * status code. If less than zero, we declare an error 127 * in the clock status word and return the kernel 128 * (software) time variable. While there are other 129 * places that call microtime(), this is the only place 130 * that matters from an application point of view. 131 */ 132 if (microtime(&atv) < 0) { 133 time_status |= STA_CLOCKERR; 134 ntv.time = time; 135 } else 136 time_status &= ~STA_CLOCKERR; 137 #else /* EXT_CLOCK */ 138 microtime(&atv); 139 #endif /* EXT_CLOCK */ 140 ntv.time = atv; 141 ntv.maxerror = time_maxerror; 142 ntv.esterror = time_esterror; 143 (void) splx(s); 144 145 error = copyout((caddr_t)&ntv, (caddr_t)SCARG(uap, ntvp), 146 sizeof(ntv)); 147 } 148 if (!error) { 149 150 /* 151 * Status word error decode. If any of these conditions 152 * occur, an error is returned, instead of the status 153 * word. Most applications will care only about the fact 154 * the system clock may not be trusted, not about the 155 * details. 156 * 157 * Hardware or software error 158 */ 159 if ((time_status & (STA_UNSYNC | STA_CLOCKERR)) || 160 161 /* 162 * PPS signal lost when either time or frequency 163 * synchronization requested 164 */ 165 (time_status & (STA_PPSFREQ | STA_PPSTIME) && 166 !(time_status & STA_PPSSIGNAL)) || 167 168 /* 169 * PPS jitter exceeded when time synchronization 170 * requested 171 */ 172 (time_status & STA_PPSTIME && 173 time_status & STA_PPSJITTER) || 174 175 /* 176 * PPS wander exceeded or calibration error when 177 * frequency synchronization requested 178 */ 179 (time_status & STA_PPSFREQ && 180 time_status & (STA_PPSWANDER | STA_PPSERROR))) 181 *retval = TIME_ERROR; 182 else 183 *retval = (register_t)time_state; 184 } 185 return(error); 186 } 187 188 /* ARGSUSED */ 189 /* 190 * ntp_adjtime() - NTP daemon application interface 191 */ 192 int 193 sys_ntp_adjtime(l, v, retval) 194 struct lwp *l; 195 void *v; 196 register_t *retval; 197 { 198 struct sys_ntp_adjtime_args /* { 199 syscallarg(struct timex *) tp; 200 } */ *uap = v; 201 struct proc *p = l->l_proc; 202 struct timex ntv; 203 int error = 0; 204 205 if ((error = copyin((caddr_t)SCARG(uap, tp), (caddr_t)&ntv, 206 sizeof(ntv))) != 0) 207 return (error); 208 209 if (ntv.modes != 0 && (error = suser(p->p_ucred, &p->p_acflag)) != 0) 210 return (error); 211 212 return (ntp_adjtime1(&ntv, v, retval)); 213 } 214 215 int 216 ntp_adjtime1(ntv, v, retval) 217 struct timex *ntv; 218 void *v; 219 register_t *retval; 220 { 221 struct sys_ntp_adjtime_args /* { 222 syscallarg(struct timex *) tp; 223 } */ *uap = v; 224 int error = 0; 225 int modes; 226 int s; 227 228 /* 229 * Update selected clock variables. Note that there is no error 230 * checking here on the assumption the superuser should know 231 * what it is doing. 232 */ 233 modes = ntv->modes; 234 s = splclock(); 235 if (modes & MOD_FREQUENCY) 236 #ifdef PPS_SYNC 237 time_freq = ntv->freq - pps_freq; 238 #else /* PPS_SYNC */ 239 time_freq = ntv->freq; 240 #endif /* PPS_SYNC */ 241 if (modes & MOD_MAXERROR) 242 time_maxerror = ntv->maxerror; 243 if (modes & MOD_ESTERROR) 244 time_esterror = ntv->esterror; 245 if (modes & MOD_STATUS) { 246 time_status &= STA_RONLY; 247 time_status |= ntv->status & ~STA_RONLY; 248 } 249 if (modes & MOD_TIMECONST) 250 time_constant = ntv->constant; 251 if (modes & MOD_OFFSET) 252 hardupdate(ntv->offset); 253 254 /* 255 * Retrieve all clock variables 256 */ 257 if (time_offset < 0) 258 ntv->offset = -(-time_offset >> SHIFT_UPDATE); 259 else 260 ntv->offset = time_offset >> SHIFT_UPDATE; 261 #ifdef PPS_SYNC 262 ntv->freq = time_freq + pps_freq; 263 #else /* PPS_SYNC */ 264 ntv->freq = time_freq; 265 #endif /* PPS_SYNC */ 266 ntv->maxerror = time_maxerror; 267 ntv->esterror = time_esterror; 268 ntv->status = time_status; 269 ntv->constant = time_constant; 270 ntv->precision = time_precision; 271 ntv->tolerance = time_tolerance; 272 #ifdef PPS_SYNC 273 ntv->shift = pps_shift; 274 ntv->ppsfreq = pps_freq; 275 ntv->jitter = pps_jitter >> PPS_AVG; 276 ntv->stabil = pps_stabil; 277 ntv->calcnt = pps_calcnt; 278 ntv->errcnt = pps_errcnt; 279 ntv->jitcnt = pps_jitcnt; 280 ntv->stbcnt = pps_stbcnt; 281 #endif /* PPS_SYNC */ 282 (void)splx(s); 283 284 error = copyout((caddr_t)ntv, (caddr_t)SCARG(uap, tp), sizeof(*ntv)); 285 if (!error) { 286 287 /* 288 * Status word error decode. See comments in 289 * ntp_gettime() routine. 290 */ 291 if ((time_status & (STA_UNSYNC | STA_CLOCKERR)) || 292 (time_status & (STA_PPSFREQ | STA_PPSTIME) && 293 !(time_status & STA_PPSSIGNAL)) || 294 (time_status & STA_PPSTIME && 295 time_status & STA_PPSJITTER) || 296 (time_status & STA_PPSFREQ && 297 time_status & (STA_PPSWANDER | STA_PPSERROR))) 298 *retval = TIME_ERROR; 299 else 300 *retval = (register_t)time_state; 301 } 302 return error; 303 } 304 305 /* 306 * return information about kernel precision timekeeping 307 */ 308 int 309 sysctl_ntptime(where, sizep) 310 void *where; 311 size_t *sizep; 312 { 313 struct timeval atv; 314 struct ntptimeval ntv; 315 int s; 316 317 /* 318 * Construct ntp_timeval. 319 */ 320 321 s = splclock(); 322 #ifdef EXT_CLOCK 323 /* 324 * The microtime() external clock routine returns a 325 * status code. If less than zero, we declare an error 326 * in the clock status word and return the kernel 327 * (software) time variable. While there are other 328 * places that call microtime(), this is the only place 329 * that matters from an application point of view. 330 */ 331 if (microtime(&atv) < 0) { 332 time_status |= STA_CLOCKERR; 333 ntv.time = time; 334 } else { 335 time_status &= ~STA_CLOCKERR; 336 } 337 #else /* EXT_CLOCK */ 338 microtime(&atv); 339 #endif /* EXT_CLOCK */ 340 ntv.time = atv; 341 ntv.maxerror = time_maxerror; 342 ntv.esterror = time_esterror; 343 splx(s); 344 345 #ifdef notyet 346 /* 347 * Status word error decode. If any of these conditions 348 * occur, an error is returned, instead of the status 349 * word. Most applications will care only about the fact 350 * the system clock may not be trusted, not about the 351 * details. 352 * 353 * Hardware or software error 354 */ 355 if ((time_status & (STA_UNSYNC | STA_CLOCKERR)) || 356 ntv.time_state = TIME_ERROR; 357 358 /* 359 * PPS signal lost when either time or frequency 360 * synchronization requested 361 */ 362 (time_status & (STA_PPSFREQ | STA_PPSTIME) && 363 !(time_status & STA_PPSSIGNAL)) || 364 365 /* 366 * PPS jitter exceeded when time synchronization 367 * requested 368 */ 369 (time_status & STA_PPSTIME && 370 time_status & STA_PPSJITTER) || 371 372 /* 373 * PPS wander exceeded or calibration error when 374 * frequency synchronization requested 375 */ 376 (time_status & STA_PPSFREQ && 377 time_status & (STA_PPSWANDER | STA_PPSERROR))) 378 ntv.time_state = TIME_ERROR; 379 else 380 ntv.time_state = time_state; 381 #endif /* notyet */ 382 return (sysctl_rdstruct(where, sizep, NULL, &ntv, sizeof(ntv))); 383 } 384 #else /* !NTP */ 385 /* For some reason, raising SIGSYS (as sys_nosys would) is problematic. */ 386 387 int 388 sys_ntp_gettime(l, v, retval) 389 struct lwp *l; 390 void *v; 391 register_t *retval; 392 { 393 394 return(ENOSYS); 395 } 396 #endif /* !NTP */ 397