1 /* $NetBSD: kern_ntptime.c,v 1.27 2004/04/08 06:20:29 atatat 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.27 2004/04/08 06:20:29 atatat 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 extern int time_adjusted; /* ntp might have changed the system time */ 87 88 #ifdef PPS_SYNC 89 /* 90 * The following variables are used only if the PPS signal discipline 91 * is configured in the kernel. 92 */ 93 extern int pps_shift; /* interval duration (s) (shift) */ 94 extern long pps_freq; /* pps frequency offset (scaled ppm) */ 95 extern long pps_jitter; /* pps jitter (us) */ 96 extern long pps_stabil; /* pps stability (scaled ppm) */ 97 extern long pps_jitcnt; /* jitter limit exceeded */ 98 extern long pps_calcnt; /* calibration intervals */ 99 extern long pps_errcnt; /* calibration errors */ 100 extern long pps_stbcnt; /* stability limit exceeded */ 101 #endif /* PPS_SYNC */ 102 103 /*ARGSUSED*/ 104 /* 105 * ntp_gettime() - NTP user application interface 106 */ 107 int 108 sys_ntp_gettime(l, v, retval) 109 struct lwp *l; 110 void *v; 111 register_t *retval; 112 113 { 114 struct sys_ntp_gettime_args /* { 115 syscallarg(struct ntptimeval *) ntvp; 116 } */ *uap = v; 117 struct timeval atv; 118 struct ntptimeval ntv; 119 int error = 0; 120 int s; 121 122 if (SCARG(uap, ntvp)) { 123 s = splclock(); 124 #ifdef EXT_CLOCK 125 /* 126 * The microtime() external clock routine returns a 127 * status code. If less than zero, we declare an error 128 * in the clock status word and return the kernel 129 * (software) time variable. While there are other 130 * places that call microtime(), this is the only place 131 * that matters from an application point of view. 132 */ 133 if (microtime(&atv) < 0) { 134 time_status |= STA_CLOCKERR; 135 ntv.time = time; 136 } else 137 time_status &= ~STA_CLOCKERR; 138 #else /* EXT_CLOCK */ 139 microtime(&atv); 140 #endif /* EXT_CLOCK */ 141 ntv.time = atv; 142 ntv.maxerror = time_maxerror; 143 ntv.esterror = time_esterror; 144 (void) splx(s); 145 146 error = copyout((caddr_t)&ntv, (caddr_t)SCARG(uap, ntvp), 147 sizeof(ntv)); 148 } 149 if (!error) { 150 151 /* 152 * Status word error decode. If any of these conditions 153 * occur, an error is returned, instead of the status 154 * word. Most applications will care only about the fact 155 * the system clock may not be trusted, not about the 156 * details. 157 * 158 * Hardware or software error 159 */ 160 if ((time_status & (STA_UNSYNC | STA_CLOCKERR)) || 161 162 /* 163 * PPS signal lost when either time or frequency 164 * synchronization requested 165 */ 166 (time_status & (STA_PPSFREQ | STA_PPSTIME) && 167 !(time_status & STA_PPSSIGNAL)) || 168 169 /* 170 * PPS jitter exceeded when time synchronization 171 * requested 172 */ 173 (time_status & STA_PPSTIME && 174 time_status & STA_PPSJITTER) || 175 176 /* 177 * PPS wander exceeded or calibration error when 178 * frequency synchronization requested 179 */ 180 (time_status & STA_PPSFREQ && 181 time_status & (STA_PPSWANDER | STA_PPSERROR))) 182 *retval = TIME_ERROR; 183 else 184 *retval = (register_t)time_state; 185 } 186 return(error); 187 } 188 189 /* ARGSUSED */ 190 /* 191 * ntp_adjtime() - NTP daemon application interface 192 */ 193 int 194 sys_ntp_adjtime(l, v, retval) 195 struct lwp *l; 196 void *v; 197 register_t *retval; 198 { 199 struct sys_ntp_adjtime_args /* { 200 syscallarg(struct timex *) tp; 201 } */ *uap = v; 202 struct proc *p = l->l_proc; 203 struct timex ntv; 204 int error = 0; 205 206 if ((error = copyin((caddr_t)SCARG(uap, tp), (caddr_t)&ntv, 207 sizeof(ntv))) != 0) 208 return (error); 209 210 if (ntv.modes != 0 && (error = suser(p->p_ucred, &p->p_acflag)) != 0) 211 return (error); 212 213 return (ntp_adjtime1(&ntv, v, retval)); 214 } 215 216 int 217 ntp_adjtime1(ntv, v, retval) 218 struct timex *ntv; 219 void *v; 220 register_t *retval; 221 { 222 struct sys_ntp_adjtime_args /* { 223 syscallarg(struct timex *) tp; 224 } */ *uap = v; 225 int error = 0; 226 int modes; 227 int s; 228 229 /* 230 * Update selected clock variables. Note that there is no error 231 * checking here on the assumption the superuser should know 232 * what it is doing. 233 */ 234 modes = ntv->modes; 235 if (modes != 0) 236 /* We need to save the system time during shutdown */ 237 time_adjusted |= 2; 238 s = splclock(); 239 if (modes & MOD_FREQUENCY) 240 #ifdef PPS_SYNC 241 time_freq = ntv->freq - pps_freq; 242 #else /* PPS_SYNC */ 243 time_freq = ntv->freq; 244 #endif /* PPS_SYNC */ 245 if (modes & MOD_MAXERROR) 246 time_maxerror = ntv->maxerror; 247 if (modes & MOD_ESTERROR) 248 time_esterror = ntv->esterror; 249 if (modes & MOD_STATUS) { 250 time_status &= STA_RONLY; 251 time_status |= ntv->status & ~STA_RONLY; 252 } 253 if (modes & MOD_TIMECONST) 254 time_constant = ntv->constant; 255 if (modes & MOD_OFFSET) 256 hardupdate(ntv->offset); 257 258 /* 259 * Retrieve all clock variables 260 */ 261 if (time_offset < 0) 262 ntv->offset = -(-time_offset >> SHIFT_UPDATE); 263 else 264 ntv->offset = time_offset >> SHIFT_UPDATE; 265 #ifdef PPS_SYNC 266 ntv->freq = time_freq + pps_freq; 267 #else /* PPS_SYNC */ 268 ntv->freq = time_freq; 269 #endif /* PPS_SYNC */ 270 ntv->maxerror = time_maxerror; 271 ntv->esterror = time_esterror; 272 ntv->status = time_status; 273 ntv->constant = time_constant; 274 ntv->precision = time_precision; 275 ntv->tolerance = time_tolerance; 276 #ifdef PPS_SYNC 277 ntv->shift = pps_shift; 278 ntv->ppsfreq = pps_freq; 279 ntv->jitter = pps_jitter >> PPS_AVG; 280 ntv->stabil = pps_stabil; 281 ntv->calcnt = pps_calcnt; 282 ntv->errcnt = pps_errcnt; 283 ntv->jitcnt = pps_jitcnt; 284 ntv->stbcnt = pps_stbcnt; 285 #endif /* PPS_SYNC */ 286 (void)splx(s); 287 288 error = copyout((caddr_t)ntv, (caddr_t)SCARG(uap, tp), sizeof(*ntv)); 289 if (!error) { 290 291 /* 292 * Status word error decode. See comments in 293 * ntp_gettime() routine. 294 */ 295 if ((time_status & (STA_UNSYNC | STA_CLOCKERR)) || 296 (time_status & (STA_PPSFREQ | STA_PPSTIME) && 297 !(time_status & STA_PPSSIGNAL)) || 298 (time_status & STA_PPSTIME && 299 time_status & STA_PPSJITTER) || 300 (time_status & STA_PPSFREQ && 301 time_status & (STA_PPSWANDER | STA_PPSERROR))) 302 *retval = TIME_ERROR; 303 else 304 *retval = (register_t)time_state; 305 } 306 return error; 307 } 308 309 /* 310 * return information about kernel precision timekeeping 311 */ 312 static int 313 sysctl_kern_ntptime(SYSCTLFN_ARGS) 314 { 315 struct sysctlnode node; 316 struct timeval atv; 317 struct ntptimeval ntv; 318 int s; 319 320 /* 321 * Construct ntp_timeval. 322 */ 323 324 s = splclock(); 325 #ifdef EXT_CLOCK 326 /* 327 * The microtime() external clock routine returns a 328 * status code. If less than zero, we declare an error 329 * in the clock status word and return the kernel 330 * (software) time variable. While there are other 331 * places that call microtime(), this is the only place 332 * that matters from an application point of view. 333 */ 334 if (microtime(&atv) < 0) { 335 time_status |= STA_CLOCKERR; 336 ntv.time = time; 337 } else { 338 time_status &= ~STA_CLOCKERR; 339 } 340 #else /* EXT_CLOCK */ 341 microtime(&atv); 342 #endif /* EXT_CLOCK */ 343 ntv.time = atv; 344 ntv.maxerror = time_maxerror; 345 ntv.esterror = time_esterror; 346 splx(s); 347 348 #ifdef notyet 349 /* 350 * Status word error decode. If any of these conditions 351 * occur, an error is returned, instead of the status 352 * word. Most applications will care only about the fact 353 * the system clock may not be trusted, not about the 354 * details. 355 * 356 * Hardware or software error 357 */ 358 if ((time_status & (STA_UNSYNC | STA_CLOCKERR)) || 359 ntv.time_state = TIME_ERROR; 360 361 /* 362 * PPS signal lost when either time or frequency 363 * synchronization requested 364 */ 365 (time_status & (STA_PPSFREQ | STA_PPSTIME) && 366 !(time_status & STA_PPSSIGNAL)) || 367 368 /* 369 * PPS jitter exceeded when time synchronization 370 * requested 371 */ 372 (time_status & STA_PPSTIME && 373 time_status & STA_PPSJITTER) || 374 375 /* 376 * PPS wander exceeded or calibration error when 377 * frequency synchronization requested 378 */ 379 (time_status & STA_PPSFREQ && 380 time_status & (STA_PPSWANDER | STA_PPSERROR))) 381 ntv.time_state = TIME_ERROR; 382 else 383 ntv.time_state = time_state; 384 #endif /* notyet */ 385 386 node = *rnode; 387 node.sysctl_data = &ntv; 388 node.sysctl_size = sizeof(ntv); 389 return (sysctl_lookup(SYSCTLFN_CALL(&node))); 390 } 391 392 SYSCTL_SETUP(sysctl_kern_ntptime_setup, "sysctl kern.ntptime node setup") 393 { 394 395 sysctl_createv(clog, 0, NULL, NULL, 396 CTLFLAG_PERMANENT, 397 CTLTYPE_NODE, "kern", NULL, 398 NULL, 0, NULL, 0, 399 CTL_KERN, CTL_EOL); 400 401 sysctl_createv(clog, 0, NULL, NULL, 402 CTLFLAG_PERMANENT, 403 CTLTYPE_STRUCT, "ntptime", 404 SYSCTL_DESCR("Kernel clock values for NTP"), 405 sysctl_kern_ntptime, 0, NULL, 406 sizeof(struct ntptimeval), 407 CTL_KERN, KERN_NTPTIME, CTL_EOL); 408 } 409 #else /* !NTP */ 410 /* For some reason, raising SIGSYS (as sys_nosys would) is problematic. */ 411 412 int 413 sys_ntp_gettime(l, v, retval) 414 struct lwp *l; 415 void *v; 416 register_t *retval; 417 { 418 419 return(ENOSYS); 420 } 421 #endif /* !NTP */ 422