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