1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2020 Oxide Computer Company
14 */
15
16 /*
17 * This pseudo-device driver implements access to kernel sensors. See
18 * uts/common/os/ksensor.c for more information on the framework and how this
19 * driver fits in.
20 */
21
22 #include <sys/types.h>
23 #include <sys/file.h>
24 #include <sys/errno.h>
25 #include <sys/open.h>
26 #include <sys/cred.h>
27 #include <sys/stat.h>
28 #include <sys/ddi.h>
29 #include <sys/sunddi.h>
30 #include <sys/modctl.h>
31 #include <sys/conf.h>
32 #include <sys/devops.h>
33 #include <sys/zone.h>
34 #include <sys/ksensor_impl.h>
35
36 static dev_info_t *ksensor_dip;
37
38 static int
ksensor_create_cb(id_t id,const char * class,const char * name)39 ksensor_create_cb(id_t id, const char *class, const char *name)
40 {
41 if (ddi_create_minor_node(ksensor_dip, name, S_IFCHR, (minor_t)id,
42 class, 0) != 0) {
43 dev_err(ksensor_dip, CE_WARN, "!failed to create ksensor node "
44 "for %s:%s (minor %d)", class, name, id);
45 return (EIO);
46 }
47
48 return (0);
49 }
50
51 static void
ksensor_remove_cb(id_t id,const char * name)52 ksensor_remove_cb(id_t id, const char *name)
53 {
54 ddi_remove_minor_node(ksensor_dip, (char *)name);
55 }
56
57 static int
ksensor_open(dev_t * devp,int flags,int otype,cred_t * credp)58 ksensor_open(dev_t *devp, int flags, int otype, cred_t *credp)
59 {
60 if (crgetzoneid(credp) != GLOBAL_ZONEID || drv_priv(credp) != 0) {
61 return (EPERM);
62 }
63
64 if ((flags & (FEXCL | FNDELAY | FNONBLOCK | FWRITE)) != 0) {
65 return (EINVAL);
66 }
67
68 if (otype != OTYP_CHR) {
69 return (EINVAL);
70 }
71
72 return (0);
73 }
74
75 static int
ksensor_ioctl_kind(minor_t min,intptr_t arg,int mode)76 ksensor_ioctl_kind(minor_t min, intptr_t arg, int mode)
77 {
78 int ret;
79 sensor_ioctl_kind_t kind;
80
81 bzero(&kind, sizeof (kind));
82 ret = ksensor_op_kind((id_t)min, &kind);
83 if (ret == 0) {
84 if (ddi_copyout(&kind, (void *)arg, sizeof (kind),
85 mode & FKIOCTL) != 0) {
86 ret = EFAULT;
87 }
88 }
89 return (ret);
90 }
91
92 static int
ksensor_ioctl_scalar(minor_t min,intptr_t arg,int mode)93 ksensor_ioctl_scalar(minor_t min, intptr_t arg, int mode)
94 {
95 int ret;
96 sensor_ioctl_scalar_t scalar;
97
98 bzero(&scalar, sizeof (scalar));
99 ret = ksensor_op_scalar((id_t)min, &scalar);
100 if (ret == 0) {
101 if (ddi_copyout(&scalar, (void *)arg, sizeof (scalar),
102 mode & FKIOCTL) != 0) {
103 ret = EFAULT;
104 }
105 }
106 return (ret);
107 }
108
109 static int
ksensor_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)110 ksensor_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
111 int *rvalp)
112 {
113 minor_t m;
114
115 if ((mode & FREAD) == 0) {
116 return (EINVAL);
117 }
118
119 m = getminor(dev);
120 switch (cmd) {
121 case SENSOR_IOCTL_KIND:
122 return (ksensor_ioctl_kind(m, arg, mode));
123 case SENSOR_IOCTL_SCALAR:
124 return (ksensor_ioctl_scalar(m, arg, mode));
125 default:
126 return (ENOTTY);
127 }
128 }
129
130 static int
ksensor_close(dev_t dev,int flags,int otype,cred_t * credp)131 ksensor_close(dev_t dev, int flags, int otype, cred_t *credp)
132 {
133 return (0);
134 }
135
136 static int
ksensor_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)137 ksensor_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
138 {
139 switch (cmd) {
140 case DDI_RESUME:
141 return (DDI_SUCCESS);
142 case DDI_ATTACH:
143 break;
144 default:
145 return (DDI_FAILURE);
146 }
147
148 if (ksensor_dip != NULL) {
149 dev_err(dip, CE_WARN, "ksensor driver already attatched");
150 return (DDI_FAILURE);
151 }
152
153 ksensor_dip = dip;
154 if (ksensor_register(dip, ksensor_create_cb, ksensor_remove_cb) != 0) {
155 ksensor_dip = NULL;
156 return (DDI_FAILURE);
157 }
158
159 return (DDI_SUCCESS);
160 }
161
162 /*
163 * All minors always maps to a single instance. Don't worry about minor validity
164 * here.
165 */
166 static int
ksensor_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)167 ksensor_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
168 void **resultp)
169 {
170 if (cmd != DDI_INFO_DEVT2DEVINFO && cmd != DDI_INFO_DEVT2INSTANCE) {
171 return (DDI_FAILURE);
172 }
173
174 if (cmd == DDI_INFO_DEVT2DEVINFO) {
175 *resultp = ksensor_dip;
176 } else {
177 int inst = ddi_get_instance(ksensor_dip);
178 *resultp = (void *)(uintptr_t)inst;
179 }
180
181 return (DDI_SUCCESS);
182 }
183
184 static int
ksensor_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)185 ksensor_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
186 {
187 switch (cmd) {
188 case DDI_DETACH:
189 break;
190 case DDI_SUSPEND:
191 return (DDI_SUCCESS);
192 default:
193 return (DDI_FAILURE);
194 }
195
196 if (ksensor_dip == NULL) {
197 dev_err(dip, CE_WARN, "asked to detach ksensor driver when no "
198 "dip is attached");
199 return (DDI_FAILURE);
200 }
201
202 if (ksensor_dip != dip) {
203 dev_err(dip, CE_WARN, "asked to detach ksensor driver, but dip "
204 "doesn't match");
205 return (DDI_FAILURE);
206 }
207
208 ksensor_unregister(dip);
209 ddi_remove_minor_node(dip, NULL);
210 ksensor_dip = NULL;
211 return (DDI_SUCCESS);
212 }
213
214 static struct cb_ops ksensor_cb_ops = {
215 .cb_open = ksensor_open,
216 .cb_close = ksensor_close,
217 .cb_strategy = nodev,
218 .cb_print = nodev,
219 .cb_dump = nodev,
220 .cb_read = nodev,
221 .cb_write = nodev,
222 .cb_ioctl = ksensor_ioctl,
223 .cb_devmap = nodev,
224 .cb_mmap = nodev,
225 .cb_segmap = nodev,
226 .cb_chpoll = nochpoll,
227 .cb_prop_op = ddi_prop_op,
228 .cb_flag = D_MP,
229 .cb_rev = CB_REV,
230 .cb_aread = nodev,
231 .cb_awrite = nodev
232 };
233
234 static struct dev_ops ksensor_dev_ops = {
235 .devo_rev = DEVO_REV,
236 .devo_refcnt = 0,
237 .devo_getinfo = ksensor_getinfo,
238 .devo_identify = nulldev,
239 .devo_probe = nulldev,
240 .devo_attach = ksensor_attach,
241 .devo_detach = ksensor_detach,
242 .devo_reset = nodev,
243 .devo_power = ddi_power,
244 .devo_quiesce = ddi_quiesce_not_needed,
245 .devo_cb_ops = &ksensor_cb_ops
246 };
247
248 static struct modldrv ksensor_modldrv = {
249 .drv_modops = &mod_driverops,
250 .drv_linkinfo = "Kernel Sensor driver",
251 .drv_dev_ops = &ksensor_dev_ops
252 };
253
254 static struct modlinkage ksensor_modlinkage = {
255 .ml_rev = MODREV_1,
256 .ml_linkage = { &ksensor_modldrv, NULL }
257 };
258
259 int
_init(void)260 _init(void)
261 {
262 return (mod_install(&ksensor_modlinkage));
263 }
264
265 int
_info(struct modinfo * modinfop)266 _info(struct modinfo *modinfop)
267 {
268 return (mod_info(&ksensor_modlinkage, modinfop));
269 }
270
271 int
_fini(void)272 _fini(void)
273 {
274 return (mod_remove(&ksensor_modlinkage));
275 }
276