xref: /netbsd-src/sys/dev/clockctl.c (revision e7bed289116c9b32f7fb79bfcc85271d4154b7e8)
1*e7bed289Sriastradh /*      $NetBSD: clockctl.c,v 1.39 2022/03/28 12:33:20 riastradh Exp $ */
25a8892e2Smanu 
35a8892e2Smanu /*-
45a8892e2Smanu  * Copyright (c) 2001 The NetBSD Foundation, Inc.
55a8892e2Smanu  * All rights reserved.
65a8892e2Smanu  *
75a8892e2Smanu  * This code is derived from software contributed to The NetBSD Foundation
85a8892e2Smanu  * by Emmanuel Dreyfus.
95a8892e2Smanu  *
105a8892e2Smanu  * Redistribution and use in source and binary forms, with or without
115a8892e2Smanu  * modification, are permitted provided that the following conditions
125a8892e2Smanu  * are met:
135a8892e2Smanu  * 1. Redistributions of source code must retain the above copyright
145a8892e2Smanu  *    notice, this list of conditions and the following disclaimer.
155a8892e2Smanu  * 2. Redistributions in binary form must reproduce the above copyright
165a8892e2Smanu  *    notice, this list of conditions and the following disclaimer in the
175a8892e2Smanu  *    documentation and/or other materials provided with the distribution.
185a8892e2Smanu  * 3. The name of the author may not be used to endorse or promote products
195a8892e2Smanu  *    derived from this software without specific prior written permission.
205a8892e2Smanu  *
215a8892e2Smanu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
225a8892e2Smanu  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
235a8892e2Smanu  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
245a8892e2Smanu  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
255a8892e2Smanu  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
265a8892e2Smanu  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
275a8892e2Smanu  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
285a8892e2Smanu  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
295a8892e2Smanu  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
305a8892e2Smanu  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
315a8892e2Smanu  */
325a8892e2Smanu 
332bbe2de6Slukem #include <sys/cdefs.h>
34*e7bed289Sriastradh __KERNEL_RCSID(0, "$NetBSD: clockctl.c,v 1.39 2022/03/28 12:33:20 riastradh Exp $");
35f8e0ee9eSmanu 
368285eddaSpgoyette #ifdef _KERNEL_OPT
37f8e0ee9eSmanu #include "opt_ntp.h"
38461a86f9Schristos #include "opt_compat_netbsd.h"
398285eddaSpgoyette #endif
402bbe2de6Slukem 
415a8892e2Smanu #include <sys/param.h>
425a8892e2Smanu #include <sys/systm.h>
435a8892e2Smanu #include <sys/proc.h>
445a8892e2Smanu #include <sys/errno.h>
455a8892e2Smanu #include <sys/ioctl.h>
465a8892e2Smanu #include <sys/device.h>
475a8892e2Smanu #include <sys/time.h>
4877a6b82bSgehenna #include <sys/conf.h>
495a8892e2Smanu #include <sys/timex.h>
500dd621a0Selad #include <sys/kauth.h>
518285eddaSpgoyette #include <sys/module.h>
528285eddaSpgoyette #include <sys/mutex.h>
53d91f98a8Spgoyette #include <sys/compat_stub.h>
545a8892e2Smanu 
555a8892e2Smanu #include <sys/clockctl.h>
56461a86f9Schristos #include <compat/sys/clockctl.h>
578285eddaSpgoyette #include <compat/sys/time_types.h>
58d91f98a8Spgoyette 
595a8892e2Smanu 
608285eddaSpgoyette kmutex_t clockctl_mtx;
618285eddaSpgoyette int clockctl_refcnt;
628285eddaSpgoyette 
638285eddaSpgoyette #include "ioconf.h"
648285eddaSpgoyette 
6577a6b82bSgehenna dev_type_ioctl(clockctlioctl);
6677a6b82bSgehenna 
6777a6b82bSgehenna const struct cdevsw clockctl_cdevsw = {
688285eddaSpgoyette 	.d_open = clockctlopen,
698285eddaSpgoyette 	.d_close = clockctlclose,
70a68f9396Sdholland 	.d_read = noread,
71a68f9396Sdholland 	.d_write = nowrite,
72a68f9396Sdholland 	.d_ioctl = clockctlioctl,
73a68f9396Sdholland 	.d_stop = nostop,
74a68f9396Sdholland 	.d_tty = notty,
75a68f9396Sdholland 	.d_poll = nopoll,
76a68f9396Sdholland 	.d_mmap = nommap,
77a68f9396Sdholland 	.d_kqfilter = nokqfilter,
78f9228f42Sdholland 	.d_discard = nodiscard,
79a68f9396Sdholland 	.d_flag = D_OTHER,
8077a6b82bSgehenna };
815a8892e2Smanu 
820dd621a0Selad static kauth_listener_t clockctl_listener;
830dd621a0Selad 
840dd621a0Selad static int
clockctl_listener_cb(kauth_cred_t cred,kauth_action_t action,void * cookie,void * arg0,void * arg1,void * arg2,void * arg3)850dd621a0Selad clockctl_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie,
860dd621a0Selad     void *arg0, void *arg1, void *arg2, void *arg3)
870dd621a0Selad {
880dd621a0Selad 	int result;
890dd621a0Selad 	enum kauth_system_req req;
900dd621a0Selad 	bool device_context;
910dd621a0Selad 
920dd621a0Selad 	result = KAUTH_RESULT_DEFER;
93ce578dfcSjoerg 	req = (enum kauth_system_req)(uintptr_t)arg0;
940dd621a0Selad 
950dd621a0Selad 	if ((action != KAUTH_SYSTEM_TIME) ||
960dd621a0Selad 	    (req != KAUTH_REQ_SYSTEM_TIME_SYSTEM))
970dd621a0Selad 		return result;
980dd621a0Selad 
99ce578dfcSjoerg 	device_context = arg3 != NULL;
1000dd621a0Selad 
1010dd621a0Selad 	/* Device is controlled by permissions, so allow. */
1020dd621a0Selad 	if (device_context)
1030dd621a0Selad 		result = KAUTH_RESULT_ALLOW;
1040dd621a0Selad 
1050dd621a0Selad 	return result;
1060dd621a0Selad }
1070dd621a0Selad 
108d42c7a04Sperry /*ARGSUSED*/
1095a8892e2Smanu void
clockctlattach(int num)110168cd830Schristos clockctlattach(int num)
1115a8892e2Smanu {
1120dd621a0Selad 
1138285eddaSpgoyette /*
1148285eddaSpgoyette  * Don't initialize the listener here - it will get handled as part
1158285eddaSpgoyette  * of module initialization.
1168285eddaSpgoyette  */
1178285eddaSpgoyette #if 0
1180dd621a0Selad 	clockctl_listener = kauth_listen_scope(KAUTH_SCOPE_SYSTEM,
1190dd621a0Selad 	    clockctl_listener_cb, NULL);
1208285eddaSpgoyette #endif
1218285eddaSpgoyette }
1228285eddaSpgoyette 
1238285eddaSpgoyette /*
1248285eddaSpgoyette  * Maintain a refcount for each open/close, so we know when it is
1258285eddaSpgoyette  * safe to call devsw_detach()
1268285eddaSpgoyette  */
1278285eddaSpgoyette int
clockctlopen(dev_t dev,int flag,int mode,struct lwp * l)1288285eddaSpgoyette clockctlopen(dev_t dev, int flag, int mode, struct lwp *l)
1298285eddaSpgoyette {
1308285eddaSpgoyette 
1318285eddaSpgoyette 	mutex_enter(&clockctl_mtx);
1328285eddaSpgoyette 	clockctl_refcnt++;
1338285eddaSpgoyette 	mutex_exit(&clockctl_mtx);
1348285eddaSpgoyette 
1358285eddaSpgoyette 	return 0;
1368285eddaSpgoyette }
1378285eddaSpgoyette 
1388285eddaSpgoyette int
clockctlclose(dev_t dev,int flag,int mode,struct lwp * l)1398285eddaSpgoyette clockctlclose(dev_t dev, int flag, int mode, struct lwp *l)
1408285eddaSpgoyette {
1418285eddaSpgoyette 
1428285eddaSpgoyette 	mutex_enter(&clockctl_mtx);
1438285eddaSpgoyette 	clockctl_refcnt--;
1448285eddaSpgoyette 	mutex_exit(&clockctl_mtx);
1458285eddaSpgoyette 
1468285eddaSpgoyette 	return 0;
1478285eddaSpgoyette }
1488285eddaSpgoyette 
1498285eddaSpgoyette MODULE(MODULE_CLASS_DRIVER, clockctl, NULL);
1508285eddaSpgoyette 
1518285eddaSpgoyette int
clockctl_modcmd(modcmd_t cmd,void * data)1528285eddaSpgoyette clockctl_modcmd(modcmd_t cmd, void *data)
1538285eddaSpgoyette {
1548285eddaSpgoyette 	int error;
1558285eddaSpgoyette #ifdef _MODULE
1568285eddaSpgoyette 	int bmajor, cmajor;
1578285eddaSpgoyette #endif
1588285eddaSpgoyette 
1598285eddaSpgoyette 	error = 0;
1608285eddaSpgoyette 
1618285eddaSpgoyette 	switch (cmd) {
1628285eddaSpgoyette 	case MODULE_CMD_INIT:
16396bedaecSchristos 		mutex_init(&clockctl_mtx, MUTEX_DEFAULT, IPL_NONE);
1648285eddaSpgoyette 
1658285eddaSpgoyette 		clockctl_listener = kauth_listen_scope(KAUTH_SCOPE_SYSTEM,
1668285eddaSpgoyette 		    clockctl_listener_cb, NULL);
1678285eddaSpgoyette 
1688285eddaSpgoyette #ifdef _MODULE
1698285eddaSpgoyette 		bmajor = cmajor = -1;
1708285eddaSpgoyette 		error = devsw_attach("clockctl", NULL, &bmajor,
1718285eddaSpgoyette 		    &clockctl_cdevsw, &cmajor);
1728285eddaSpgoyette 		if (error != 0)
1738285eddaSpgoyette 			kauth_unlisten_scope(clockctl_listener);
1748285eddaSpgoyette #endif
1758285eddaSpgoyette 
1768285eddaSpgoyette 		break;
1778285eddaSpgoyette 
1788285eddaSpgoyette 	case MODULE_CMD_FINI:
1798285eddaSpgoyette 		mutex_enter(&clockctl_mtx);
1808285eddaSpgoyette 		if (clockctl_refcnt != 0) {
1818285eddaSpgoyette 			mutex_exit(&clockctl_mtx);
1828285eddaSpgoyette 			return EBUSY;
1838285eddaSpgoyette 		}
1848285eddaSpgoyette #ifdef _MODULE
185*e7bed289Sriastradh 		devsw_detach(NULL, &clockctl_cdevsw);
1868285eddaSpgoyette #endif
1878285eddaSpgoyette 		mutex_exit(&clockctl_mtx);
1888285eddaSpgoyette 
1898285eddaSpgoyette 		kauth_unlisten_scope(clockctl_listener);
19096bedaecSchristos 		mutex_destroy(&clockctl_mtx);
1918285eddaSpgoyette 		break;
1928285eddaSpgoyette 
1938285eddaSpgoyette 	default:
1948285eddaSpgoyette 		error = ENOTTY;
1958285eddaSpgoyette 		break;
1968285eddaSpgoyette 	}
1978285eddaSpgoyette 
1988285eddaSpgoyette 	return error;
1995a8892e2Smanu }
2005a8892e2Smanu 
2015a8892e2Smanu int
clockctlioctl(dev_t dev,u_long cmd,void * data,int flags,struct lwp * l)2024d595fd7Schristos clockctlioctl(
203168cd830Schristos     dev_t dev,
2044d595fd7Schristos     u_long cmd,
20553524e44Schristos     void *data,
206168cd830Schristos     int flags,
2074d595fd7Schristos     struct lwp *l)
2085a8892e2Smanu {
2095a8892e2Smanu 	int error = 0;
2105a8892e2Smanu 
2115a8892e2Smanu 	switch (cmd) {
2125a8892e2Smanu 	case CLOCKCTL_SETTIMEOFDAY: {
213461a86f9Schristos 		struct clockctl_settimeofday *args = data;
2145a8892e2Smanu 
215ef3fdc4aSdsl 		error = settimeofday1(args->tv, true, args->tzp, l, false);
2165a8892e2Smanu 		break;
2175a8892e2Smanu 	}
2185a8892e2Smanu 	case CLOCKCTL_ADJTIME: {
219461a86f9Schristos 		struct timeval atv, oldatv;
220461a86f9Schristos 		struct clockctl_adjtime *args = data;
2215a8892e2Smanu 
222461a86f9Schristos 		if (args->delta) {
223c99ea852Snakayama 			error = copyin(args->delta, &atv, sizeof(atv));
2245a8892e2Smanu 			if (error)
2255a8892e2Smanu 				return (error);
226461a86f9Schristos 		}
227461a86f9Schristos 		adjtime1(args->delta ? &atv : NULL,
228461a86f9Schristos 		    args->olddelta ? &oldatv : NULL, l->l_proc);
229461a86f9Schristos 		if (args->olddelta)
230461a86f9Schristos 			error = copyout(&oldatv, args->olddelta,
231c99ea852Snakayama 			    sizeof(oldatv));
2325a8892e2Smanu 		break;
2335a8892e2Smanu 	}
2345a8892e2Smanu 	case CLOCKCTL_CLOCK_SETTIME: {
235461a86f9Schristos 		struct clockctl_clock_settime *args = data;
2366e512790Smrg 		struct timespec ts;
2375a8892e2Smanu 
2386e512790Smrg 		error = copyin(args->tp, &ts, sizeof ts);
2396e512790Smrg 		if (error)
2406e512790Smrg 			return (error);
241f5e9e809Smrg 		error = clock_settime1(l->l_proc, args->clock_id, &ts, false);
2425a8892e2Smanu 		break;
2435a8892e2Smanu 	}
2445a8892e2Smanu 	case CLOCKCTL_NTP_ADJTIME: {
245461a86f9Schristos 		struct clockctl_ntp_adjtime *args = data;
246f48c7db8Smanu 		struct timex ntv;
2475a8892e2Smanu 
248d91f98a8Spgoyette 		if (vec_ntp_timestatus == NULL) {
249d91f98a8Spgoyette 			error = ENOTTY;
250d91f98a8Spgoyette 			break;
251d91f98a8Spgoyette 		}
252f7f0b661Schristos 		error = copyin(args->tp, &ntv, sizeof(ntv));
253f48c7db8Smanu 		if (error)
2545a8892e2Smanu 			return (error);
255f48c7db8Smanu 
256d91f98a8Spgoyette 		(*vec_ntp_adjtime1)(&ntv);
257de4337abSkardel 
258de4337abSkardel 		error = copyout(&ntv, args->tp, sizeof(ntv));
259de4337abSkardel 		if (error == 0)
260d91f98a8Spgoyette 			args->retval = (*vec_ntp_timestatus)();
261c99ea852Snakayama 		break;
2625a8892e2Smanu 	}
2635a8892e2Smanu 	default:
2648c2f80f1Spgoyette 		MODULE_HOOK_CALL(clockctl_ioctl_50_hook,
265d91f98a8Spgoyette 		    (dev, cmd, data, flags, l), enosys(), error);
266d91f98a8Spgoyette 		if (error == ENOSYS)
2670de36015Srin 			error = ENOTTY;
2688285eddaSpgoyette 	}
2698285eddaSpgoyette 
2708285eddaSpgoyette 	return (error);
2718285eddaSpgoyette }
272