kern_ntptime.c revision 2858
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> 482858Swollman#include <sys/kernel.h> 492858Swollman#include <sys/proc.h> 502858Swollman#include <sys/timex.h> 512858Swollman#include <sys/sysctl.h> 522858Swollman 532858Swollman/* 542858Swollman * The following variables are used by the hardclock() routine in the 552858Swollman * kern_clock.c module and are described in that module. 562858Swollman */ 572858Swollmanextern struct timeval time; /* kernel time variable */ 582858Swollmanextern int time_state; /* clock state */ 592858Swollmanextern int time_status; /* clock status bits */ 602858Swollmanextern long time_offset; /* time adjustment (us) */ 612858Swollmanextern long time_freq; /* frequency offset (scaled ppm) */ 622858Swollmanextern long time_maxerror; /* maximum error (us) */ 632858Swollmanextern long time_esterror; /* estimated error (us) */ 642858Swollmanextern long time_constant; /* pll time constant */ 652858Swollmanextern long time_precision; /* clock precision (us) */ 662858Swollmanextern long time_tolerance; /* frequency tolerance (scaled ppm) */ 672858Swollman 682858Swollman#ifdef PPS_SYNC 692858Swollman/* 702858Swollman * The following variables are used only if the PPS signal discipline 712858Swollman * is configured in the kernel. 722858Swollman */ 732858Swollmanextern int pps_shift; /* interval duration (s) (shift) */ 742858Swollmanextern long pps_freq; /* pps frequency offset (scaled ppm) */ 752858Swollmanextern long pps_jitter; /* pps jitter (us) */ 762858Swollmanextern long pps_stabil; /* pps stability (scaled ppm) */ 772858Swollmanextern long pps_jitcnt; /* jitter limit exceeded */ 782858Swollmanextern long pps_calcnt; /* calibration intervals */ 792858Swollmanextern long pps_errcnt; /* calibration errors */ 802858Swollmanextern long pps_stbcnt; /* stability limit exceeded */ 812858Swollman#endif /* PPS_SYNC */ 822858Swollman 832858Swollmanint 842858Swollmanntp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, 852858Swollman void *newp, size_t newlen, struct proc *p) 862858Swollman{ 872858Swollman struct timeval atv; 882858Swollman struct ntptimeval ntv; 892858Swollman int s; 902858Swollman 912858Swollman /* All names at this level are terminal. */ 922858Swollman if (namelen != 1) { 932858Swollman return ENOTDIR; 942858Swollman } 952858Swollman 962858Swollman if (name[0] != NTP_PLL_GETTIME) { 972858Swollman return EOPNOTSUPP; 982858Swollman } 992858Swollman 1002858Swollman s = splclock(); 1012858Swollman#ifdef EXT_CLOCK 1022858Swollman /* 1032858Swollman * The microtime() external clock routine returns a 1042858Swollman * status code. If less than zero, we declare an error 1052858Swollman * in the clock status word and return the kernel 1062858Swollman * (software) time variable. While there are other 1072858Swollman * places that call microtime(), this is the only place 1082858Swollman * that matters from an application point of view. 1092858Swollman */ 1102858Swollman if (microtime(&atv) < 0) { 1112858Swollman time_status |= STA_CLOCKERR; 1122858Swollman ntv.time = time; 1132858Swollman } else { 1142858Swollman time_status &= ~STA_CLOCKERR; 1152858Swollman } 1162858Swollman#else /* EXT_CLOCK */ 1172858Swollman microtime(&atv); 1182858Swollman#endif /* EXT_CLOCK */ 1192858Swollman ntv.time = atv; 1202858Swollman ntv.maxerror = time_maxerror; 1212858Swollman ntv.esterror = time_esterror; 1222858Swollman splx(s); 1232858Swollman 1242858Swollman ntv.time_state = time_state; 1252858Swollman 1262858Swollman /* 1272858Swollman * Status word error decode. If any of these conditions 1282858Swollman * occur, an error is returned, instead of the status 1292858Swollman * word. Most applications will care only about the fact 1302858Swollman * the system clock may not be trusted, not about the 1312858Swollman * details. 1322858Swollman * 1332858Swollman * Hardware or software error 1342858Swollman */ 1352858Swollman if (time_status & (STA_UNSYNC | STA_CLOCKERR)) { 1362858Swollman ntv.time_state = TIME_ERROR; 1372858Swollman } 1382858Swollman 1392858Swollman /* 1402858Swollman * PPS signal lost when either time or frequency 1412858Swollman * synchronization requested 1422858Swollman */ 1432858Swollman if (time_status & (STA_PPSFREQ | STA_PPSTIME) && 1442858Swollman !(time_status & STA_PPSSIGNAL)) { 1452858Swollman ntv.time_state = TIME_ERROR; 1462858Swollman } 1472858Swollman 1482858Swollman /* 1492858Swollman * PPS jitter exceeded when time synchronization 1502858Swollman * requested 1512858Swollman */ 1522858Swollman if (time_status & STA_PPSTIME && 1532858Swollman time_status & STA_PPSJITTER) { 1542858Swollman ntv.time_state = TIME_ERROR; 1552858Swollman } 1562858Swollman 1572858Swollman /* 1582858Swollman * PPS wander exceeded or calibration error when 1592858Swollman * frequency synchronization requested 1602858Swollman */ 1612858Swollman if (time_status & STA_PPSFREQ && 1622858Swollman time_status & (STA_PPSWANDER | STA_PPSERROR)) { 1632858Swollman ntv.time_state = TIME_ERROR; 1642858Swollman } 1652858Swollman return(sysctl_rdstruct(oldp, oldlenp, newp, &ntv, sizeof ntv)); 1662858Swollman} 1672858Swollman 1682858Swollman/* 1692858Swollman * ntp_adjtime() - NTP daemon application interface 1702858Swollman */ 1712858Swollmanstruct ntp_adjtime_args { 1722858Swollman struct timex *tp; 1732858Swollman}; 1742858Swollman 1752858Swollmanint 1762858Swollmanntp_adjtime(struct proc *p, struct ntp_adjtime_args *uap, int *retval) 1772858Swollman{ 1782858Swollman struct timex ntv; 1792858Swollman int modes; 1802858Swollman int s; 1812858Swollman int error; 1822858Swollman 1832858Swollman error = copyin((caddr_t)uap->tp, (caddr_t)&ntv, sizeof(ntv)); 1842858Swollman if (error) 1852858Swollman return error; 1862858Swollman 1872858Swollman /* 1882858Swollman * Update selected clock variables - only the superuser can 1892858Swollman * change anything. Note that there is no error checking here on 1902858Swollman * the assumption the superuser should know what it is doing. 1912858Swollman */ 1922858Swollman modes = ntv.modes; 1932858Swollman if ((modes != 0) 1942858Swollman && (error = suser(p->p_cred->pc_ucred, &p->p_acflag))) 1952858Swollman return error; 1962858Swollman 1972858Swollman s = splclock(); 1982858Swollman if (modes & MOD_FREQUENCY) 1992858Swollman#ifdef PPS_SYNC 2002858Swollman time_freq = ntv.freq - pps_freq; 2012858Swollman#else /* PPS_SYNC */ 2022858Swollman time_freq = ntv.freq; 2032858Swollman#endif /* PPS_SYNC */ 2042858Swollman if (modes & MOD_MAXERROR) 2052858Swollman time_maxerror = ntv.maxerror; 2062858Swollman if (modes & MOD_ESTERROR) 2072858Swollman time_esterror = ntv.esterror; 2082858Swollman if (modes & MOD_STATUS) { 2092858Swollman time_status &= STA_RONLY; 2102858Swollman time_status |= ntv.status & ~STA_RONLY; 2112858Swollman } 2122858Swollman if (modes & MOD_TIMECONST) 2132858Swollman time_constant = ntv.constant; 2142858Swollman if (modes & MOD_OFFSET) 2152858Swollman hardupdate(ntv.offset); 2162858Swollman 2172858Swollman /* 2182858Swollman * Retrieve all clock variables 2192858Swollman */ 2202858Swollman if (time_offset < 0) 2212858Swollman ntv.offset = -(-time_offset >> SHIFT_UPDATE); 2222858Swollman else 2232858Swollman ntv.offset = time_offset >> SHIFT_UPDATE; 2242858Swollman#ifdef PPS_SYNC 2252858Swollman ntv.freq = time_freq + pps_freq; 2262858Swollman#else /* PPS_SYNC */ 2272858Swollman ntv.freq = time_freq; 2282858Swollman#endif /* PPS_SYNC */ 2292858Swollman ntv.maxerror = time_maxerror; 2302858Swollman ntv.esterror = time_esterror; 2312858Swollman ntv.status = time_status; 2322858Swollman ntv.constant = time_constant; 2332858Swollman ntv.precision = time_precision; 2342858Swollman ntv.tolerance = time_tolerance; 2352858Swollman#ifdef PPS_SYNC 2362858Swollman ntv.shift = pps_shift; 2372858Swollman ntv.ppsfreq = pps_freq; 2382858Swollman ntv.jitter = pps_jitter >> PPS_AVG; 2392858Swollman ntv.stabil = pps_stabil; 2402858Swollman ntv.calcnt = pps_calcnt; 2412858Swollman ntv.errcnt = pps_errcnt; 2422858Swollman ntv.jitcnt = pps_jitcnt; 2432858Swollman ntv.stbcnt = pps_stbcnt; 2442858Swollman#endif /* PPS_SYNC */ 2452858Swollman (void)splx(s); 2462858Swollman 2472858Swollman error = copyout((caddr_t)&ntv, (caddr_t)uap->tp, sizeof(ntv)); 2482858Swollman if (!error) { 2492858Swollman /* 2502858Swollman * Status word error decode. See comments in 2512858Swollman * ntp_gettime() routine. 2522858Swollman */ 2532858Swollman retval[0] = time_state; 2542858Swollman if (time_status & (STA_UNSYNC | STA_CLOCKERR)) 2552858Swollman retval[0] = TIME_ERROR; 2562858Swollman if (time_status & (STA_PPSFREQ | STA_PPSTIME) && 2572858Swollman !(time_status & STA_PPSSIGNAL)) 2582858Swollman retval[0] = TIME_ERROR; 2592858Swollman if (time_status & STA_PPSTIME && 2602858Swollman time_status & STA_PPSJITTER) 2612858Swollman retval[0] = TIME_ERROR; 2622858Swollman if (time_status & STA_PPSFREQ && 2632858Swollman time_status & (STA_PPSWANDER | STA_PPSERROR)) 2642858Swollman retval[0] = TIME_ERROR; 2652858Swollman } 2662858Swollman return error; 2672858Swollman} 2682858Swollman 2692858Swollman 270