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