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/pci/pci_obj.h>
38 #include <sys/pci_tools.h>
39 #include <sys/pci/pci_tools_ext.h>
40 #include <sys/open.h>
41 #include <sys/errno.h>
42 #include <sys/file.h>
43 #include <sys/policy.h>
44 #include <sys/hotplug/pci/pcihp.h>
45
46 /*LINTLIBRARY*/
47
48 static int pci_open(dev_t *devp, int flags, int otyp, cred_t *credp);
49 static int pci_close(dev_t dev, int flags, int otyp, cred_t *credp);
50 static int pci_devctl_ioctl(dev_info_t *dip, int cmd, intptr_t arg, int mode,
51 cred_t *credp, int *rvalp);
52 static int pci_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
53 cred_t *credp, int *rvalp);
54 static int pci_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
55 int flags, char *name, caddr_t valuep, int *lengthp);
56
57 struct cb_ops pci_cb_ops = {
58 pci_open, /* open */
59 pci_close, /* close */
60 nodev, /* strategy */
61 nodev, /* print */
62 nodev, /* dump */
63 nodev, /* read */
64 nodev, /* write */
65 pci_ioctl, /* ioctl */
66 nodev, /* devmap */
67 nodev, /* mmap */
68 nodev, /* segmap */
69 nochpoll, /* poll */
70 pci_prop_op, /* cb_prop_op */
71 NULL, /* streamtab */
72 D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */
73 CB_REV, /* rev */
74 nodev, /* int (*cb_aread)() */
75 nodev /* int (*cb_awrite)() */
76 };
77
78 extern struct cb_ops *pcihp_ops;
79
80 /* ARGSUSED3 */
81 static int
pci_open(dev_t * devp,int flags,int otyp,cred_t * credp)82 pci_open(dev_t *devp, int flags, int otyp, cred_t *credp)
83 {
84 pci_t *pci_p;
85 int rval;
86 uint_t orig_pci_soft_state;
87
88 /*
89 * Make sure the open is for the right file type.
90 */
91 if (otyp != OTYP_CHR)
92 return (EINVAL);
93
94 /*
95 * Get the soft state structure for the device.
96 */
97 pci_p = DEV_TO_SOFTSTATE(*devp);
98 if (pci_p == NULL)
99 return (ENXIO);
100
101 /*
102 * Handle the open by tracking the device state.
103 */
104 DEBUG2(DBG_OPEN, pci_p->pci_dip, "devp=%x: flags=%x\n", devp, flags);
105 mutex_enter(&pci_p->pci_mutex);
106 orig_pci_soft_state = pci_p->pci_soft_state;
107 if (flags & FEXCL) {
108 if (pci_p->pci_soft_state != PCI_SOFT_STATE_CLOSED) {
109 mutex_exit(&pci_p->pci_mutex);
110 DEBUG0(DBG_OPEN, pci_p->pci_dip, "busy\n");
111 return (EBUSY);
112 }
113 pci_p->pci_soft_state = PCI_SOFT_STATE_OPEN_EXCL;
114 } else {
115 if (pci_p->pci_soft_state == PCI_SOFT_STATE_OPEN_EXCL) {
116 mutex_exit(&pci_p->pci_mutex);
117 DEBUG0(DBG_OPEN, pci_p->pci_dip, "busy\n");
118 return (EBUSY);
119 }
120 pci_p->pci_soft_state = PCI_SOFT_STATE_OPEN;
121 }
122
123 if (pci_p->hotplug_capable == B_TRUE) {
124 if (rval = pcihp_ops->cb_open(devp, flags, otyp, credp)) {
125 pci_p->pci_soft_state = orig_pci_soft_state;
126 mutex_exit(&pci_p->pci_mutex);
127 return (rval);
128 }
129 }
130
131 mutex_exit(&pci_p->pci_mutex);
132
133 return (0);
134 }
135
136
137 /* ARGSUSED */
138 static int
pci_close(dev_t dev,int flags,int otyp,cred_t * credp)139 pci_close(dev_t dev, int flags, int otyp, cred_t *credp)
140 {
141 pci_t *pci_p;
142 int rval;
143
144 if (otyp != OTYP_CHR)
145 return (EINVAL);
146
147 pci_p = DEV_TO_SOFTSTATE(dev);
148 if (pci_p == NULL)
149 return (ENXIO);
150
151 DEBUG2(DBG_CLOSE, pci_p->pci_dip, "dev=%x: flags=%x\n", dev, flags);
152 mutex_enter(&pci_p->pci_mutex);
153
154 if (pci_p->hotplug_capable == B_TRUE)
155 if (rval = pcihp_ops->cb_close(dev, flags, otyp, credp)) {
156 mutex_exit(&pci_p->pci_mutex);
157 return (rval);
158 }
159
160 pci_p->pci_soft_state = PCI_SOFT_STATE_CLOSED;
161 mutex_exit(&pci_p->pci_mutex);
162 return (0);
163 }
164
165 /* ARGSUSED */
166 static int
pci_devctl_ioctl(dev_info_t * dip,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)167 pci_devctl_ioctl(dev_info_t *dip, int cmd, intptr_t arg, int mode,
168 cred_t *credp, int *rvalp)
169 {
170 int rv = 0;
171 struct devctl_iocdata *dcp;
172 uint_t bus_state;
173
174 /*
175 * We can use the generic implementation for these ioctls
176 */
177 switch (cmd) {
178 case DEVCTL_DEVICE_GETSTATE:
179 case DEVCTL_DEVICE_ONLINE:
180 case DEVCTL_DEVICE_OFFLINE:
181 case DEVCTL_BUS_GETSTATE:
182 return (ndi_devctl_ioctl(dip, cmd, arg, mode, 0));
183 }
184
185 /*
186 * read devctl ioctl data
187 */
188 if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
189 return (EFAULT);
190
191 switch (cmd) {
192
193 case DEVCTL_DEVICE_RESET:
194 DEBUG0(DBG_IOCTL, dip, "DEVCTL_DEVICE_RESET\n");
195 rv = ENOTSUP;
196 break;
197
198
199 case DEVCTL_BUS_QUIESCE:
200 DEBUG0(DBG_IOCTL, dip, "DEVCTL_BUS_QUIESCE\n");
201 if (ndi_get_bus_state(dip, &bus_state) == NDI_SUCCESS)
202 if (bus_state == BUS_QUIESCED)
203 break;
204 (void) ndi_set_bus_state(dip, BUS_QUIESCED);
205 break;
206
207 case DEVCTL_BUS_UNQUIESCE:
208 DEBUG0(DBG_IOCTL, dip, "DEVCTL_BUS_UNQUIESCE\n");
209 if (ndi_get_bus_state(dip, &bus_state) == NDI_SUCCESS)
210 if (bus_state == BUS_ACTIVE)
211 break;
212 (void) ndi_set_bus_state(dip, BUS_ACTIVE);
213 break;
214
215 case DEVCTL_BUS_RESET:
216 DEBUG0(DBG_IOCTL, dip, "DEVCTL_BUS_RESET\n");
217 rv = ENOTSUP;
218 break;
219
220 case DEVCTL_BUS_RESETALL:
221 DEBUG0(DBG_IOCTL, dip, "DEVCTL_BUS_RESETALL\n");
222 rv = ENOTSUP;
223 break;
224
225 default:
226 rv = ENOTTY;
227 }
228
229 ndi_dc_freehdl(dcp);
230 return (rv);
231 }
232
233
234 static int
pci_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)235 pci_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
236 {
237 pci_t *pci_p;
238 dev_info_t *dip;
239 minor_t minor = getminor(dev);
240 int rv = ENOTTY;
241
242 pci_p = DEV_TO_SOFTSTATE(dev);
243 if (pci_p == NULL)
244 return (ENXIO);
245
246 dip = pci_p->pci_dip;
247 DEBUG2(DBG_IOCTL, dip, "dev=%x: cmd=%x\n", dev, cmd);
248
249 #ifdef PCI_DMA_TEST
250 if (IS_DMATEST(cmd)) {
251 *rvalp = pci_dma_test(cmd, dip, pci_p, arg);
252 return (0);
253 }
254 #endif
255
256 switch (PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(minor)) {
257 case PCI_TOOL_REG_MINOR_NUM:
258
259 switch (cmd) {
260 case PCITOOL_DEVICE_SET_REG:
261 case PCITOOL_DEVICE_GET_REG:
262
263 /* Require full privileges. */
264 if (secpolicy_kmdb(credp))
265 rv = EPERM;
266 else
267 rv = pcitool_dev_reg_ops(
268 dev, (void *)arg, cmd, mode);
269 break;
270
271 case PCITOOL_NEXUS_SET_REG:
272 case PCITOOL_NEXUS_GET_REG:
273
274 /* Require full privileges. */
275 if (secpolicy_kmdb(credp))
276 rv = EPERM;
277 else
278 rv = pcitool_bus_reg_ops(
279 dev, (void *)arg, cmd, mode);
280 break;
281 }
282
283 break;
284
285 case PCI_TOOL_INTR_MINOR_NUM:
286
287 switch (cmd) {
288 case PCITOOL_DEVICE_SET_INTR:
289
290 /* Require PRIV_SYS_RES_CONFIG, same as psradm */
291 if (secpolicy_ponline(credp)) {
292 rv = EPERM;
293 break;
294 }
295
296 /*FALLTHRU*/
297 /* These require no special privileges. */
298 case PCITOOL_DEVICE_GET_INTR:
299 case PCITOOL_SYSTEM_INTR_INFO:
300 rv = pcitool_intr_admn(dev, (void *)arg, cmd, mode);
301 break;
302 }
303
304 break;
305
306 /*
307 * All non-PCItool ioctls go through here, including:
308 * devctl ioctls with minor number PCIHP_DEVCTL_MINOR and
309 * those for attachment points with where minor number is the
310 * device number.
311 */
312 default:
313 if (pci_p->hotplug_capable == B_TRUE)
314 rv = pcihp_ops->cb_ioctl(
315 dev, cmd, arg, mode, credp, rvalp);
316 else
317 rv = pci_devctl_ioctl(
318 dip, cmd, arg, mode, credp, rvalp);
319 break;
320 }
321
322 return (rv);
323 }
324
pci_prop_op(dev_t dev,dev_info_t * dip,ddi_prop_op_t prop_op,int flags,char * name,caddr_t valuep,int * lengthp)325 static int pci_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