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 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 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 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 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 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 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 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 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 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 294 _info(struct modinfo *modinfop) 295 { 296 return (mod_info(&modlinkage, modinfop)); 297 } 298