kern_ntptime.c revision 12623
12858Swollman/****************************************************************************** 22858Swollman * * 32858Swollman * Copyright (c) David L. Mills 1993, 1994 * 42858Swollman * * 52858Swollman * Permission to use, copy, modify, and distribute this software and its * 62858Swollman * documentation for any purpose and without fee is hereby granted, provided * 72858Swollman * that the above copyright notice appears in all copies and that both the * 82858Swollman * copyright notice and this permission notice appear in supporting * 92858Swollman * documentation, and that the name University of Delaware not be used in * 102858Swollman * advertising or publicity pertaining to distribution of the software * 112858Swollman * without specific, written prior permission. The University of Delaware * 122858Swollman * makes no representations about the suitability this software for any * 132858Swollman * purpose. It is provided "as is" without express or implied warranty. * 142858Swollman * * 152858Swollman ******************************************************************************/ 162858Swollman 172858Swollman/* 182858Swollman * Modification history kern_ntptime.c 192858Swollman * 202858Swollman * 24 Mar 94 David L. Mills 212858Swollman * Revised syscall interface to include new variables for PPS 222858Swollman * time discipline. 232858Swollman * 242858Swollman * 14 Feb 94 David L. Mills 252858Swollman * Added code for external clock 262858Swollman * 272858Swollman * 28 Nov 93 David L. Mills 282858Swollman * Revised frequency scaling to conform with adjusted parameters 292858Swollman * 302858Swollman * 17 Sep 93 David L. Mills 312858Swollman * Created file 322858Swollman */ 332858Swollman/* 342858Swollman * ntp_gettime(), ntp_adjtime() - precision time interface for SunOS 352858Swollman * 4.1.1 and 4.1.3 362858Swollman * 372858Swollman * These routines consitute the Network Time Protocol (NTP) interfaces 382858Swollman * for user and daemon application programs. The ntp_gettime() routine 392858Swollman * provides the time, maximum error (synch distance) and estimated error 402858Swollman * (dispersion) to client user application programs. The ntp_adjtime() 412858Swollman * routine is used by the NTP daemon to adjust the system clock to an 422858Swollman * externally derived time. The time offset and related variables set by 432858Swollman * this routine are used by hardclock() to adjust the phase and 442858Swollman * frequency of the phase-lock loop which controls the system clock. 452858Swollman */ 462858Swollman#include <sys/param.h> 472858Swollman#include <sys/systm.h> 4812221Sbde#include <sys/sysproto.h> 492858Swollman#include <sys/kernel.h> 502858Swollman#include <sys/proc.h> 512858Swollman#include <sys/timex.h> 522896Sache#include <vm/vm.h> 532858Swollman#include <sys/sysctl.h> 542858Swollman 552858Swollman/* 562858Swollman * The following variables are used by the hardclock() routine in the 578876Srgrimes * kern_clock.c module and are described in that module. 582858Swollman */ 592858Swollmanextern int time_state; /* clock state */ 602858Swollmanextern int time_status; /* clock status bits */ 612858Swollmanextern long time_offset; /* time adjustment (us) */ 622858Swollmanextern long time_freq; /* frequency offset (scaled ppm) */ 632858Swollmanextern long time_maxerror; /* maximum error (us) */ 642858Swollmanextern long time_esterror; /* estimated error (us) */ 652858Swollmanextern long time_constant; /* pll time constant */ 662858Swollmanextern long time_precision; /* clock precision (us) */ 672858Swollmanextern long time_tolerance; /* frequency tolerance (scaled ppm) */ 682858Swollman 692858Swollman#ifdef PPS_SYNC 702858Swollman/* 712858Swollman * The following variables are used only if the PPS signal discipline 722858Swollman * is configured in the kernel. 732858Swollman */ 742858Swollmanextern int pps_shift; /* interval duration (s) (shift) */ 752858Swollmanextern long pps_freq; /* pps frequency offset (scaled ppm) */ 762858Swollmanextern long pps_jitter; /* pps jitter (us) */ 772858Swollmanextern long pps_stabil; /* pps stability (scaled ppm) */ 782858Swollmanextern long pps_jitcnt; /* jitter limit exceeded */ 792858Swollmanextern long pps_calcnt; /* calibration intervals */ 802858Swollmanextern long pps_errcnt; /* calibration errors */ 812858Swollmanextern long pps_stbcnt; /* stability limit exceeded */ 822858Swollman#endif /* PPS_SYNC */ 832858Swollman 8412279Sphkstatic int 8512279Sphkntp_sysctl SYSCTL_HANDLER_ARGS 862858Swollman{ 872858Swollman struct timeval atv; 882858Swollman struct ntptimeval ntv; 892858Swollman int s; 902858Swollman 912858Swollman s = splclock(); 922858Swollman#ifdef EXT_CLOCK 932858Swollman /* 942858Swollman * The microtime() external clock routine returns a 952858Swollman * status code. If less than zero, we declare an error 962858Swollman * in the clock status word and return the kernel 972858Swollman * (software) time variable. While there are other 982858Swollman * places that call microtime(), this is the only place 992858Swollman * that matters from an application point of view. 1002858Swollman */ 1012858Swollman if (microtime(&atv) < 0) { 1022858Swollman time_status |= STA_CLOCKERR; 1032858Swollman ntv.time = time; 1042858Swollman } else { 1052858Swollman time_status &= ~STA_CLOCKERR; 1062858Swollman } 1072858Swollman#else /* EXT_CLOCK */ 1082858Swollman microtime(&atv); 1092858Swollman#endif /* EXT_CLOCK */ 1102858Swollman ntv.time = atv; 1112858Swollman ntv.maxerror = time_maxerror; 1122858Swollman ntv.esterror = time_esterror; 1132858Swollman splx(s); 1148876Srgrimes 1152858Swollman ntv.time_state = time_state; 1162858Swollman 1172858Swollman /* 1182858Swollman * Status word error decode. If any of these conditions 1192858Swollman * occur, an error is returned, instead of the status 1202858Swollman * word. Most applications will care only about the fact 1212858Swollman * the system clock may not be trusted, not about the 1222858Swollman * details. 1232858Swollman * 1242858Swollman * Hardware or software error 1252858Swollman */ 1262858Swollman if (time_status & (STA_UNSYNC | STA_CLOCKERR)) { 1272858Swollman ntv.time_state = TIME_ERROR; 1282858Swollman } 1292858Swollman 1302858Swollman /* 1312858Swollman * PPS signal lost when either time or frequency 1322858Swollman * synchronization requested 1332858Swollman */ 1342858Swollman if (time_status & (STA_PPSFREQ | STA_PPSTIME) && 1352858Swollman !(time_status & STA_PPSSIGNAL)) { 1362858Swollman ntv.time_state = TIME_ERROR; 1372858Swollman } 1382858Swollman 1392858Swollman /* 1402858Swollman * PPS jitter exceeded when time synchronization 1412858Swollman * requested 1422858Swollman */ 1432858Swollman if (time_status & STA_PPSTIME && 1442858Swollman time_status & STA_PPSJITTER) { 1452858Swollman ntv.time_state = TIME_ERROR; 1462858Swollman } 1472858Swollman 1482858Swollman /* 1492858Swollman * PPS wander exceeded or calibration error when 1502858Swollman * frequency synchronization requested 1512858Swollman */ 1522858Swollman if (time_status & STA_PPSFREQ && 1532858Swollman time_status & (STA_PPSWANDER | STA_PPSERROR)) { 1542858Swollman ntv.time_state = TIME_ERROR; 1552858Swollman } 15612279Sphk return (sysctl_handle_opaque(oidp, &ntv, sizeof ntv, req)); 1572858Swollman} 1582858Swollman 15912279SphkSYSCTL_NODE(_kern, KERN_NTP_PLL, ntp_pll, CTLFLAG_RW, 0, 16012279Sphk "NTP kernel PLL related stuff"); 16112279SphkSYSCTL_PROC(_kern_ntp_pll, NTP_PLL_GETTIME, gettime, CTLTYPE_OPAQUE|CTLFLAG_RD, 16212623Sphk 0, sizeof(struct ntptimeval) , ntp_sysctl, "S,ntptimeval", ""); 16312279Sphk 1642858Swollman/* 1652858Swollman * ntp_adjtime() - NTP daemon application interface 1662858Swollman */ 16712221Sbde#ifndef _SYS_SYSPROTO_H_ 1682858Swollmanstruct ntp_adjtime_args { 1692858Swollman struct timex *tp; 1702858Swollman}; 17112221Sbde#endif 1722858Swollman 1732858Swollmanint 1742858Swollmanntp_adjtime(struct proc *p, struct ntp_adjtime_args *uap, int *retval) 1752858Swollman{ 1762858Swollman struct timex ntv; 1772858Swollman int modes; 1782858Swollman int s; 1792858Swollman int error; 1802858Swollman 1812858Swollman error = copyin((caddr_t)uap->tp, (caddr_t)&ntv, sizeof(ntv)); 1822858Swollman if (error) 1832858Swollman return error; 1842858Swollman 1852858Swollman /* 1862858Swollman * Update selected clock variables - only the superuser can 1872858Swollman * change anything. Note that there is no error checking here on 1882858Swollman * the assumption the superuser should know what it is doing. 1892858Swollman */ 1902858Swollman modes = ntv.modes; 1912858Swollman if ((modes != 0) 1922858Swollman && (error = suser(p->p_cred->pc_ucred, &p->p_acflag))) 1932858Swollman return error; 1942858Swollman 1952858Swollman s = splclock(); 1962858Swollman if (modes & MOD_FREQUENCY) 1972858Swollman#ifdef PPS_SYNC 1982858Swollman time_freq = ntv.freq - pps_freq; 1992858Swollman#else /* PPS_SYNC */ 2002858Swollman time_freq = ntv.freq; 2012858Swollman#endif /* PPS_SYNC */ 2022858Swollman if (modes & MOD_MAXERROR) 2032858Swollman time_maxerror = ntv.maxerror; 2042858Swollman if (modes & MOD_ESTERROR) 2052858Swollman time_esterror = ntv.esterror; 2062858Swollman if (modes & MOD_STATUS) { 2072858Swollman time_status &= STA_RONLY; 2082858Swollman time_status |= ntv.status & ~STA_RONLY; 2092858Swollman } 2102858Swollman if (modes & MOD_TIMECONST) 2112858Swollman time_constant = ntv.constant; 2122858Swollman if (modes & MOD_OFFSET) 2132858Swollman hardupdate(ntv.offset); 2142858Swollman 2152858Swollman /* 2162858Swollman * Retrieve all clock variables 2172858Swollman */ 2182858Swollman if (time_offset < 0) 2192858Swollman ntv.offset = -(-time_offset >> SHIFT_UPDATE); 2202858Swollman else 2212858Swollman ntv.offset = time_offset >> SHIFT_UPDATE; 2222858Swollman#ifdef PPS_SYNC 2232858Swollman ntv.freq = time_freq + pps_freq; 2242858Swollman#else /* PPS_SYNC */ 2252858Swollman ntv.freq = time_freq; 2262858Swollman#endif /* PPS_SYNC */ 2272858Swollman ntv.maxerror = time_maxerror; 2282858Swollman ntv.esterror = time_esterror; 2292858Swollman ntv.status = time_status; 2302858Swollman ntv.constant = time_constant; 2312858Swollman ntv.precision = time_precision; 2322858Swollman ntv.tolerance = time_tolerance; 2332858Swollman#ifdef PPS_SYNC 2342858Swollman ntv.shift = pps_shift; 2352858Swollman ntv.ppsfreq = pps_freq; 2362858Swollman ntv.jitter = pps_jitter >> PPS_AVG; 2372858Swollman ntv.stabil = pps_stabil; 2382858Swollman ntv.calcnt = pps_calcnt; 2392858Swollman ntv.errcnt = pps_errcnt; 2402858Swollman ntv.jitcnt = pps_jitcnt; 2412858Swollman ntv.stbcnt = pps_stbcnt; 2422858Swollman#endif /* PPS_SYNC */ 2432858Swollman (void)splx(s); 2442858Swollman 2452858Swollman error = copyout((caddr_t)&ntv, (caddr_t)uap->tp, sizeof(ntv)); 2462858Swollman if (!error) { 2472858Swollman /* 2482858Swollman * Status word error decode. See comments in 2492858Swollman * ntp_gettime() routine. 2502858Swollman */ 2512858Swollman retval[0] = time_state; 2522858Swollman if (time_status & (STA_UNSYNC | STA_CLOCKERR)) 2532858Swollman retval[0] = TIME_ERROR; 2542858Swollman if (time_status & (STA_PPSFREQ | STA_PPSTIME) && 2552858Swollman !(time_status & STA_PPSSIGNAL)) 2562858Swollman retval[0] = TIME_ERROR; 2572858Swollman if (time_status & STA_PPSTIME && 2582858Swollman time_status & STA_PPSJITTER) 2592858Swollman retval[0] = TIME_ERROR; 2602858Swollman if (time_status & STA_PPSFREQ && 2612858Swollman time_status & (STA_PPSWANDER | STA_PPSERROR)) 2622858Swollman retval[0] = TIME_ERROR; 2632858Swollman } 2642858Swollman return error; 2652858Swollman} 2662858Swollman 2672858Swollman 268