xref: /illumos-gate/usr/src/uts/common/io/ksensor/ksensor_drv.c (revision 3ce5372277f4657ad0e52d36c979527c4ca22de2)
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
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
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
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
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
93 ksensor_ioctl_temp(minor_t min, intptr_t arg, int mode)
94 {
95 	int ret;
96 	sensor_ioctl_temperature_t temp;
97 
98 	bzero(&temp, sizeof (temp));
99 	ret = ksensor_op_temperature((id_t)min, &temp);
100 	if (ret == 0) {
101 		if (ddi_copyout(&temp, (void *)arg, sizeof (temp),
102 		    mode & FKIOCTL) != 0) {
103 			ret = EFAULT;
104 		}
105 	}
106 	return (ret);
107 }
108 
109 static int
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_TYPE:
122 		return (ksensor_ioctl_kind(m, arg, mode));
123 	case SENSOR_IOCTL_TEMPERATURE:
124 		return (ksensor_ioctl_temp(m, arg, mode));
125 	default:
126 		return (ENOTTY);
127 	}
128 }
129 
130 static int
131 ksensor_close(dev_t dev, int flags, int otype, cred_t *credp)
132 {
133 	return (0);
134 }
135 
136 static int
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
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
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
260 _init(void)
261 {
262 	return (mod_install(&ksensor_modlinkage));
263 }
264 
265 int
266 _info(struct modinfo *modinfop)
267 {
268 	return (mod_info(&ksensor_modlinkage, modinfop));
269 }
270 
271 int
272 _fini(void)
273 {
274 	return (mod_remove(&ksensor_modlinkage));
275 }
276