xref: /illumos-gate/usr/src/uts/intel/io/vmm/vmm_drv_test.c (revision 2e401babeb53295c8df347e32364beadc0ed1620)
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 /* This file is dual-licensed; see usr/src/contrib/bhyve/LICENSE */
12 
13 /*
14  * Copyright 2022 Oxide Computer Company
15  */
16 
17 #include <sys/types.h>
18 #include <sys/conf.h>
19 #include <sys/ddi.h>
20 #include <sys/mkdev.h>
21 #include <sys/sunddi.h>
22 #include <sys/id_space.h>
23 #include <sys/stat.h>
24 
25 #include <sys/vmm_drv.h>
26 #include <sys/vmm_drv_test.h>
27 
28 #define	VDT_CTL_NAME	"vmm_drv_test"
29 #define	VDT_CTL_MINOR	0
30 
31 static dev_info_t	*vdt_dip;
32 static void		*vdt_state;
33 static id_space_t	*vdt_minors;
34 
35 typedef struct vdt_soft_state {
36 	kmutex_t	vss_lock;
37 	vmm_hold_t	*vss_hold;
38 } vdt_soft_state_t;
39 
40 
41 static int
42 vdt_open(dev_t *devp, int flag, int otype, cred_t *cr)
43 {
44 	id_t minor;
45 
46 	if (otype != OTYP_CHR) {
47 		return (EINVAL);
48 	}
49 	if (getminor(*devp) != VDT_CTL_MINOR) {
50 		return (ENXIO);
51 	}
52 
53 	minor = id_alloc_nosleep(vdt_minors);
54 	if (minor == -1) {
55 		return (EBUSY);
56 	}
57 	if (ddi_soft_state_zalloc(vdt_state, minor) != DDI_SUCCESS) {
58 		id_free(vdt_minors, minor);
59 		return (ENOMEM);
60 	}
61 
62 	vdt_soft_state_t *ss;
63 	ss = ddi_get_soft_state(vdt_state, minor);
64 	mutex_init(&ss->vss_lock, NULL, MUTEX_DEFAULT, NULL);
65 	*devp = makedevice(getmajor(*devp), minor);
66 
67 	return (0);
68 }
69 
70 static int
71 vdt_close(dev_t dev, int flag, int otype, cred_t *cr)
72 {
73 	if (otype != OTYP_CHR) {
74 		return (EINVAL);
75 	}
76 
77 	id_t minor = getminor(dev);
78 	vdt_soft_state_t *ss = ddi_get_soft_state(vdt_state, minor);
79 	if (ss == NULL) {
80 		return (ENXIO);
81 	}
82 
83 	mutex_destroy(&ss->vss_lock);
84 	ddi_soft_state_free(vdt_state, minor);
85 	id_free(vdt_minors, minor);
86 
87 	return (0);
88 }
89 
90 static int
91 vdt_ioc_hold(vdt_soft_state_t *ss, cred_t *cr, int vmm_fd)
92 {
93 	mutex_enter(&ss->vss_lock);
94 	if (ss->vss_hold != NULL) {
95 		mutex_exit(&ss->vss_lock);
96 		return (EEXIST);
97 	}
98 
99 	file_t *fp = getf(vmm_fd);
100 	if (fp == NULL) {
101 		mutex_exit(&ss->vss_lock);
102 		return (EBADF);
103 	}
104 
105 	int err = vmm_drv_hold(fp, cr, &ss->vss_hold);
106 	releasef(vmm_fd);
107 	mutex_exit(&ss->vss_lock);
108 	return (err);
109 }
110 
111 static int
112 vdt_ioc_rele(vdt_soft_state_t *ss)
113 {
114 	mutex_enter(&ss->vss_lock);
115 	if (ss->vss_hold == NULL) {
116 		mutex_exit(&ss->vss_lock);
117 		return (ENODEV);
118 	}
119 
120 	vmm_drv_rele(ss->vss_hold);
121 	ss->vss_hold = NULL;
122 	mutex_exit(&ss->vss_lock);
123 	return (0);
124 }
125 
126 static int
127 vdt_ioctl(dev_t dev, int cmd, intptr_t data, int md, cred_t *cr, int *rv)
128 {
129 	vdt_soft_state_t *ss = ddi_get_soft_state(vdt_state, getminor(dev));
130 	if (ss == NULL) {
131 		return (ENXIO);
132 	}
133 
134 	int err = 0;
135 	*rv = 0;
136 	switch (cmd) {
137 	case VDT_IOC_HOLD:
138 		err = vdt_ioc_hold(ss, cr, (int)data);
139 		break;
140 	case VDT_IOC_RELE:
141 		err = vdt_ioc_rele(ss);
142 		break;
143 	default:
144 		err = ENOTTY;
145 		break;
146 	}
147 
148 	return (err);
149 }
150 
151 static int
152 vdt_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
153 {
154 	switch (cmd) {
155 	case DDI_INFO_DEVT2DEVINFO:
156 		*result = (void *)vdt_dip;
157 		return (DDI_SUCCESS);
158 	case DDI_INFO_DEVT2INSTANCE:
159 		*result = (void *)0;
160 		return (DDI_SUCCESS);
161 	default:
162 		return (DDI_FAILURE);
163 	}
164 }
165 
166 static int
167 vdt_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
168 {
169 	if (cmd != DDI_ATTACH) {
170 		return (DDI_FAILURE);
171 	}
172 
173 	if (vdt_dip != NULL) {
174 		return (DDI_FAILURE);
175 	}
176 
177 	/* Create "control" node from which other instances are spawned */
178 	if (ddi_create_minor_node(dip, VDT_CTL_NAME, S_IFCHR, VDT_CTL_MINOR,
179 	    DDI_PSEUDO, 0) != 0) {
180 		return (DDI_FAILURE);
181 	}
182 
183 	ddi_report_dev(dip);
184 	vdt_dip = dip;
185 	return (DDI_SUCCESS);
186 }
187 
188 static int
189 vdt_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
190 {
191 	if (cmd != DDI_DETACH) {
192 		return (DDI_FAILURE);
193 	}
194 
195 	ddi_remove_minor_node(vdt_dip, NULL);
196 	vdt_dip = NULL;
197 
198 	return (DDI_SUCCESS);
199 }
200 
201 static struct cb_ops vdt_cb_ops = {
202 	.cb_open	= vdt_open,
203 	.cb_close	= vdt_close,
204 	.cb_strategy	= nodev,
205 	.cb_print	= nodev,
206 	.cb_dump	= nodev,
207 	.cb_read	= nodev,
208 	.cb_write	= nodev,
209 	.cb_ioctl	= vdt_ioctl,
210 	.cb_devmap	= nodev,
211 	.cb_mmap	= nodev,
212 	.cb_segmap	= nodev,
213 	.cb_chpoll	= nochpoll,
214 	.cb_prop_op	= ddi_prop_op,
215 
216 	.cb_str		= NULL,
217 
218 	.cb_flag	= D_NEW | D_MP | D_DEVMAP,
219 	.cb_rev		= CB_REV,
220 	.cb_aread	= nodev,
221 	.cb_awrite	= nodev,
222 };
223 
224 static struct dev_ops vdt_ops = {
225 	.devo_rev	= DEVO_REV,
226 	.devo_refcnt	= 0,
227 
228 	.devo_getinfo	= vdt_info,
229 	.devo_identify	= nulldev,
230 	.devo_probe	= nulldev,
231 	.devo_attach	= vdt_attach,
232 	.devo_detach	= vdt_detach,
233 	.devo_reset	= nodev,
234 	.devo_cb_ops	= &vdt_cb_ops,
235 
236 	.devo_bus_ops	= NULL,
237 	.devo_power	= ddi_power,
238 	.devo_quiesce	= ddi_quiesce_not_needed,
239 };
240 
241 static struct modldrv modldrv = {
242 	&mod_driverops,
243 	"bhyve vmm drv test",
244 	&vdt_ops
245 };
246 
247 static struct modlinkage modlinkage = {
248 	MODREV_1,
249 	&modldrv,
250 	NULL
251 };
252 
253 int
254 _init(void)
255 {
256 	int err;
257 
258 	vdt_minors = id_space_create("vmm_drv_test_minors",
259 	    VDT_CTL_MINOR + 1, MAXMIN32);
260 
261 	err = ddi_soft_state_init(&vdt_state, sizeof (vdt_soft_state_t), 0);
262 	if (err != 0) {
263 		return (err);
264 	}
265 
266 	err = mod_install(&modlinkage);
267 	if (err != 0) {
268 		ddi_soft_state_fini(&vdt_state);
269 	}
270 
271 	return (0);
272 }
273 
274 int
275 _fini(void)
276 {
277 	int err = mod_remove(&modlinkage);
278 	if (err != 0) {
279 		return (err);
280 	}
281 
282 	ddi_soft_state_fini(&vdt_state);
283 
284 	id_space_destroy(vdt_minors);
285 
286 	return (0);
287 }
288 
289 int
290 _info(struct modinfo *modinfop)
291 {
292 	return (mod_info(&modlinkage, modinfop));
293 }
294