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
vdt_open(dev_t * devp,int flag,int otype,cred_t * cr)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
vdt_close(dev_t dev,int flag,int otype,cred_t * cr)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 if (ss->vss_hold != NULL) {
84 vmm_drv_rele(ss->vss_hold);
85 ss->vss_hold = NULL;
86 }
87 mutex_destroy(&ss->vss_lock);
88 ddi_soft_state_free(vdt_state, minor);
89 id_free(vdt_minors, minor);
90
91 return (0);
92 }
93
94 static int
vdt_ioc_hold(vdt_soft_state_t * ss,cred_t * cr,int vmm_fd)95 vdt_ioc_hold(vdt_soft_state_t *ss, cred_t *cr, int vmm_fd)
96 {
97 mutex_enter(&ss->vss_lock);
98 if (ss->vss_hold != NULL) {
99 mutex_exit(&ss->vss_lock);
100 return (EEXIST);
101 }
102
103 file_t *fp = getf(vmm_fd);
104 if (fp == NULL) {
105 mutex_exit(&ss->vss_lock);
106 return (EBADF);
107 }
108
109 int err = vmm_drv_hold(fp, cr, &ss->vss_hold);
110 releasef(vmm_fd);
111 mutex_exit(&ss->vss_lock);
112 return (err);
113 }
114
115 static int
vdt_ioc_rele(vdt_soft_state_t * ss)116 vdt_ioc_rele(vdt_soft_state_t *ss)
117 {
118 mutex_enter(&ss->vss_lock);
119 if (ss->vss_hold == NULL) {
120 mutex_exit(&ss->vss_lock);
121 return (ENODEV);
122 }
123
124 vmm_drv_rele(ss->vss_hold);
125 ss->vss_hold = NULL;
126 mutex_exit(&ss->vss_lock);
127 return (0);
128 }
129
130 static int
vdt_ioctl(dev_t dev,int cmd,intptr_t data,int md,cred_t * cr,int * rv)131 vdt_ioctl(dev_t dev, int cmd, intptr_t data, int md, cred_t *cr, int *rv)
132 {
133 vdt_soft_state_t *ss = ddi_get_soft_state(vdt_state, getminor(dev));
134 if (ss == NULL) {
135 return (ENXIO);
136 }
137
138 int err = 0;
139 *rv = 0;
140 switch (cmd) {
141 case VDT_IOC_HOLD:
142 err = vdt_ioc_hold(ss, cr, (int)data);
143 break;
144 case VDT_IOC_RELE:
145 err = vdt_ioc_rele(ss);
146 break;
147 default:
148 err = ENOTTY;
149 break;
150 }
151
152 return (err);
153 }
154
155 static int
vdt_info(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)156 vdt_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
157 {
158 switch (cmd) {
159 case DDI_INFO_DEVT2DEVINFO:
160 *result = (void *)vdt_dip;
161 return (DDI_SUCCESS);
162 case DDI_INFO_DEVT2INSTANCE:
163 *result = (void *)0;
164 return (DDI_SUCCESS);
165 default:
166 return (DDI_FAILURE);
167 }
168 }
169
170 static int
vdt_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)171 vdt_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
172 {
173 if (cmd != DDI_ATTACH) {
174 return (DDI_FAILURE);
175 }
176
177 if (vdt_dip != NULL) {
178 return (DDI_FAILURE);
179 }
180
181 /* Create "control" node from which other instances are spawned */
182 if (ddi_create_minor_node(dip, VDT_CTL_NAME, S_IFCHR, VDT_CTL_MINOR,
183 DDI_PSEUDO, 0) != 0) {
184 return (DDI_FAILURE);
185 }
186
187 ddi_report_dev(dip);
188 vdt_dip = dip;
189 return (DDI_SUCCESS);
190 }
191
192 static int
vdt_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)193 vdt_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
194 {
195 if (cmd != DDI_DETACH) {
196 return (DDI_FAILURE);
197 }
198
199 ddi_remove_minor_node(vdt_dip, NULL);
200 vdt_dip = NULL;
201
202 return (DDI_SUCCESS);
203 }
204
205 static struct cb_ops vdt_cb_ops = {
206 .cb_open = vdt_open,
207 .cb_close = vdt_close,
208 .cb_strategy = nodev,
209 .cb_print = nodev,
210 .cb_dump = nodev,
211 .cb_read = nodev,
212 .cb_write = nodev,
213 .cb_ioctl = vdt_ioctl,
214 .cb_devmap = nodev,
215 .cb_mmap = nodev,
216 .cb_segmap = nodev,
217 .cb_chpoll = nochpoll,
218 .cb_prop_op = ddi_prop_op,
219
220 .cb_str = NULL,
221
222 .cb_flag = D_NEW | D_MP | D_DEVMAP,
223 .cb_rev = CB_REV,
224 .cb_aread = nodev,
225 .cb_awrite = nodev,
226 };
227
228 static struct dev_ops vdt_ops = {
229 .devo_rev = DEVO_REV,
230 .devo_refcnt = 0,
231
232 .devo_getinfo = vdt_info,
233 .devo_identify = nulldev,
234 .devo_probe = nulldev,
235 .devo_attach = vdt_attach,
236 .devo_detach = vdt_detach,
237 .devo_reset = nodev,
238 .devo_cb_ops = &vdt_cb_ops,
239
240 .devo_bus_ops = NULL,
241 .devo_power = ddi_power,
242 .devo_quiesce = ddi_quiesce_not_needed,
243 };
244
245 static struct modldrv modldrv = {
246 &mod_driverops,
247 "bhyve vmm drv test",
248 &vdt_ops
249 };
250
251 static struct modlinkage modlinkage = {
252 MODREV_1,
253 &modldrv,
254 NULL
255 };
256
257 int
_init(void)258 _init(void)
259 {
260 int err;
261
262 vdt_minors = id_space_create("vmm_drv_test_minors",
263 VDT_CTL_MINOR + 1, MAXMIN32);
264
265 err = ddi_soft_state_init(&vdt_state, sizeof (vdt_soft_state_t), 0);
266 if (err != 0) {
267 return (err);
268 }
269
270 err = mod_install(&modlinkage);
271 if (err != 0) {
272 ddi_soft_state_fini(&vdt_state);
273 }
274
275 return (0);
276 }
277
278 int
_fini(void)279 _fini(void)
280 {
281 int err = mod_remove(&modlinkage);
282 if (err != 0) {
283 return (err);
284 }
285
286 ddi_soft_state_fini(&vdt_state);
287
288 id_space_destroy(vdt_minors);
289
290 return (0);
291 }
292
293 int
_info(struct modinfo * modinfop)294 _info(struct modinfo *modinfop)
295 {
296 return (mod_info(&modlinkage, modinfop));
297 }
298