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