1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * PCI nexus HotPlug devctl interface 28 */ 29 #include <sys/types.h> 30 #include <sys/conf.h> 31 #include <sys/kmem.h> 32 #include <sys/async.h> 33 #include <sys/sysmacros.h> 34 #include <sys/sunddi.h> 35 #include <sys/sunndi.h> 36 #include <sys/ddi_impldefs.h> 37 #include <sys/open.h> 38 #include <sys/errno.h> 39 #include <sys/file.h> 40 #include <sys/policy.h> 41 #include <sys/hotplug/pci/pcihp.h> 42 #include "px_obj.h" 43 #include <sys/pci_tools.h> 44 #include "px_tools_ext.h" 45 #include <sys/pcie_pwr.h> 46 47 /*LINTLIBRARY*/ 48 49 static int px_open(dev_t *devp, int flags, int otyp, cred_t *credp); 50 static int px_close(dev_t dev, int flags, int otyp, cred_t *credp); 51 static int px_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, 52 cred_t *credp, int *rvalp); 53 static int px_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, 54 int flags, char *name, caddr_t valuep, int *lengthp); 55 56 struct cb_ops px_cb_ops = { 57 px_open, /* open */ 58 px_close, /* close */ 59 nodev, /* strategy */ 60 nodev, /* print */ 61 nodev, /* dump */ 62 nodev, /* read */ 63 nodev, /* write */ 64 px_ioctl, /* ioctl */ 65 nodev, /* devmap */ 66 nodev, /* mmap */ 67 nodev, /* segmap */ 68 nochpoll, /* poll */ 69 px_prop_op, /* cb_prop_op */ 70 NULL, /* streamtab */ 71 D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ 72 CB_REV, /* rev */ 73 nodev, /* int (*cb_aread)() */ 74 nodev /* int (*cb_awrite)() */ 75 }; 76 77 /* ARGSUSED3 */ 78 static int 79 px_open(dev_t *devp, int flags, int otyp, cred_t *credp) 80 { 81 px_t *px_p; 82 int rval; 83 uint_t orig_px_soft_state; 84 85 /* 86 * Make sure the open is for the right file type. 87 */ 88 if (otyp != OTYP_CHR) 89 return (EINVAL); 90 91 /* 92 * Get the soft state structure for the device. 93 */ 94 px_p = PX_DEV_TO_SOFTSTATE(*devp); 95 if (px_p == NULL) 96 return (ENXIO); 97 98 /* 99 * Handle the open by tracking the device state. 100 */ 101 DBG(DBG_OPEN, px_p->px_dip, "devp=%x: flags=%x\n", devp, flags); 102 mutex_enter(&px_p->px_mutex); 103 orig_px_soft_state = px_p->px_soft_state; 104 if (flags & FEXCL) { 105 if (px_p->px_soft_state != PX_SOFT_STATE_CLOSED) { 106 mutex_exit(&px_p->px_mutex); 107 DBG(DBG_OPEN, px_p->px_dip, "busy\n"); 108 return (EBUSY); 109 } 110 px_p->px_soft_state = PX_SOFT_STATE_OPEN_EXCL; 111 } else { 112 if (px_p->px_soft_state == PX_SOFT_STATE_OPEN_EXCL) { 113 mutex_exit(&px_p->px_mutex); 114 DBG(DBG_OPEN, px_p->px_dip, "busy\n"); 115 return (EBUSY); 116 } 117 px_p->px_soft_state = PX_SOFT_STATE_OPEN; 118 } 119 120 if (px_p->px_dev_caps & PX_HOTPLUG_CAPABLE) 121 if (rval = (pcihp_get_cb_ops())->cb_open(devp, flags, 122 otyp, credp)) { 123 px_p->px_soft_state = orig_px_soft_state; 124 mutex_exit(&px_p->px_mutex); 125 return (rval); 126 } 127 128 px_p->px_open_count++; 129 mutex_exit(&px_p->px_mutex); 130 return (0); 131 } 132 133 134 /* ARGSUSED */ 135 static int 136 px_close(dev_t dev, int flags, int otyp, cred_t *credp) 137 { 138 px_t *px_p; 139 int rval; 140 141 if (otyp != OTYP_CHR) 142 return (EINVAL); 143 144 px_p = PX_DEV_TO_SOFTSTATE(dev); 145 if (px_p == NULL) 146 return (ENXIO); 147 148 DBG(DBG_CLOSE, px_p->px_dip, "dev=%x: flags=%x\n", dev, flags); 149 mutex_enter(&px_p->px_mutex); 150 151 if (px_p->px_dev_caps & PX_HOTPLUG_CAPABLE) 152 if (rval = (pcihp_get_cb_ops())->cb_close(dev, flags, 153 otyp, credp)) { 154 mutex_exit(&px_p->px_mutex); 155 return (rval); 156 } 157 158 px_p->px_soft_state = PX_SOFT_STATE_CLOSED; 159 px_p->px_open_count = 0; 160 mutex_exit(&px_p->px_mutex); 161 return (0); 162 } 163 164 /* ARGSUSED */ 165 static int 166 px_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) 167 { 168 px_t *px_p; 169 dev_info_t *dip; 170 struct devctl_iocdata *dcp; 171 uint_t bus_state; 172 int rv = DDI_SUCCESS; 173 int minor = getminor(dev); 174 175 px_p = PX_DEV_TO_SOFTSTATE(dev); 176 if (px_p == NULL) 177 return (ENXIO); 178 179 dip = px_p->px_dip; 180 DBG(DBG_IOCTL, dip, "dev=%x: cmd=%x\n", dev, cmd); 181 182 #ifdef PX_DMA_TEST 183 if (IS_DMATEST(cmd)) { 184 *rvalp = px_dma_test(cmd, dip, px_p, arg); 185 return (0); 186 } 187 #endif /* PX_DMA_TEST */ 188 189 switch (PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(minor)) { 190 191 /* 192 * PCI tools. 193 */ 194 case PCI_TOOL_REG_MINOR_NUM: 195 196 switch (cmd) { 197 case PCITOOL_DEVICE_SET_REG: 198 case PCITOOL_DEVICE_GET_REG: 199 200 /* Require full privileges. */ 201 if (secpolicy_kmdb(credp)) 202 rv = EPERM; 203 else 204 rv = pxtool_dev_reg_ops(dip, 205 (void *)arg, cmd, mode); 206 break; 207 208 case PCITOOL_NEXUS_SET_REG: 209 case PCITOOL_NEXUS_GET_REG: 210 211 /* Require full privileges. */ 212 if (secpolicy_kmdb(credp)) 213 rv = EPERM; 214 else 215 rv = pxtool_bus_reg_ops(dip, 216 (void *)arg, cmd, mode); 217 break; 218 219 default: 220 rv = ENOTTY; 221 } 222 return (rv); 223 224 case PCI_TOOL_INTR_MINOR_NUM: 225 226 switch (cmd) { 227 case PCITOOL_DEVICE_SET_INTR: 228 229 /* Require full privileges. */ 230 if (secpolicy_kmdb(credp)) { 231 rv = EPERM; 232 break; 233 } 234 235 /*FALLTHRU*/ 236 /* These require no special privileges. */ 237 case PCITOOL_DEVICE_GET_INTR: 238 case PCITOOL_SYSTEM_INTR_INFO: 239 rv = pxtool_intr(dip, (void *)arg, cmd, mode); 240 break; 241 242 default: 243 rv = ENOTTY; 244 } 245 return (rv); 246 247 default: 248 if (px_p->px_dev_caps & PX_HOTPLUG_CAPABLE) 249 return ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd, 250 arg, mode, credp, rvalp)); 251 break; 252 } 253 254 if ((cmd & ~PPMREQ_MASK) == PPMREQ) { 255 256 /* Need privileges to use these ioctls. */ 257 if (drv_priv(credp)) { 258 DBG(DBG_TOOLS, dip, 259 "px_tools: Insufficient privileges\n"); 260 261 return (EPERM); 262 } 263 return (px_lib_pmctl(cmd, px_p)); 264 } 265 266 /* 267 * We can use the generic implementation for these ioctls 268 */ 269 switch (cmd) { 270 case DEVCTL_DEVICE_GETSTATE: 271 case DEVCTL_DEVICE_ONLINE: 272 case DEVCTL_DEVICE_OFFLINE: 273 case DEVCTL_BUS_GETSTATE: 274 return (ndi_devctl_ioctl(dip, cmd, arg, mode, 0)); 275 } 276 277 /* 278 * read devctl ioctl data 279 */ 280 if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS) 281 return (EFAULT); 282 283 switch (cmd) { 284 285 case DEVCTL_DEVICE_RESET: 286 DBG(DBG_IOCTL, dip, "DEVCTL_DEVICE_RESET\n"); 287 rv = ENOTSUP; 288 break; 289 290 291 case DEVCTL_BUS_QUIESCE: 292 DBG(DBG_IOCTL, dip, "DEVCTL_BUS_QUIESCE\n"); 293 if (ndi_get_bus_state(dip, &bus_state) == NDI_SUCCESS) 294 if (bus_state == BUS_QUIESCED) 295 break; 296 (void) ndi_set_bus_state(dip, BUS_QUIESCED); 297 break; 298 299 case DEVCTL_BUS_UNQUIESCE: 300 DBG(DBG_IOCTL, dip, "DEVCTL_BUS_UNQUIESCE\n"); 301 if (ndi_get_bus_state(dip, &bus_state) == NDI_SUCCESS) 302 if (bus_state == BUS_ACTIVE) 303 break; 304 (void) ndi_set_bus_state(dip, BUS_ACTIVE); 305 break; 306 307 case DEVCTL_BUS_RESET: 308 DBG(DBG_IOCTL, dip, "DEVCTL_BUS_RESET\n"); 309 rv = ENOTSUP; 310 break; 311 312 case DEVCTL_BUS_RESETALL: 313 DBG(DBG_IOCTL, dip, "DEVCTL_BUS_RESETALL\n"); 314 rv = ENOTSUP; 315 break; 316 317 default: 318 rv = ENOTTY; 319 } 320 321 ndi_dc_freehdl(dcp); 322 return (rv); 323 } 324 325 static int px_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, 326 int flags, char *name, caddr_t valuep, int *lengthp) 327 { 328 if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 329 "hotplug-capable")) 330 return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, 331 prop_op, flags, name, valuep, lengthp)); 332 333 return (ddi_prop_op(dev, dip, prop_op, flags, name, valuep, lengthp)); 334 } 335