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 "px_obj.h"
42 #include <sys/pci_tools.h>
43 #include "px_tools_ext.h"
44 #include <sys/pcie_pwr.h>
45
46 /*LINTLIBRARY*/
47
48 static int px_open(dev_t *devp, int flags, int otyp, cred_t *credp);
49 static int px_close(dev_t dev, int flags, int otyp, cred_t *credp);
50 static int px_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
51 cred_t *credp, int *rvalp);
52
53 struct cb_ops px_cb_ops = {
54 px_open, /* open */
55 px_close, /* close */
56 nodev, /* strategy */
57 nodev, /* print */
58 nodev, /* dump */
59 nodev, /* read */
60 nodev, /* write */
61 px_ioctl, /* ioctl */
62 nodev, /* devmap */
63 nodev, /* mmap */
64 nodev, /* segmap */
65 nochpoll, /* poll */
66 pcie_prop_op, /* cb_prop_op */
67 NULL, /* streamtab */
68 D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */
69 CB_REV, /* rev */
70 nodev, /* int (*cb_aread)() */
71 nodev /* int (*cb_awrite)() */
72 };
73
74 /* ARGSUSED3 */
75 static int
px_open(dev_t * devp,int flags,int otyp,cred_t * credp)76 px_open(dev_t *devp, int flags, int otyp, cred_t *credp)
77 {
78 px_t *px_p = PX_DEV_TO_SOFTSTATE(*devp);
79 int minor = getminor(*devp);
80 int rval;
81
82 /*
83 * Make sure the open is for the right file type.
84 */
85 if (otyp != OTYP_CHR)
86 return (EINVAL);
87
88 /*
89 * Get the soft state structure for the device.
90 */
91 if (px_p == NULL)
92 return (ENXIO);
93
94 DBG(DBG_OPEN, px_p->px_dip, "devp=%x: flags=%x\n", devp, flags);
95
96 /*
97 * Handle the open by tracking the device state.
98 */
99 mutex_enter(&px_p->px_mutex);
100
101 switch (PCI_MINOR_NUM_TO_PCI_DEVNUM(minor)) {
102 case PCI_TOOL_REG_MINOR_NUM:
103 case PCI_TOOL_INTR_MINOR_NUM:
104 break;
105 default:
106 /* To handle devctl and hotplug related ioctls */
107 if (rval = pcie_open(px_p->px_dip, devp, flags, otyp, credp)) {
108 mutex_exit(&px_p->px_mutex);
109 return (rval);
110 }
111 }
112
113 if (flags & FEXCL) {
114 if (px_p->px_soft_state != PCI_SOFT_STATE_CLOSED) {
115 mutex_exit(&px_p->px_mutex);
116 DBG(DBG_OPEN, px_p->px_dip, "busy\n");
117 return (EBUSY);
118 }
119 px_p->px_soft_state = PCI_SOFT_STATE_OPEN_EXCL;
120 } else {
121 if (px_p->px_soft_state == PCI_SOFT_STATE_OPEN_EXCL) {
122 mutex_exit(&px_p->px_mutex);
123 DBG(DBG_OPEN, px_p->px_dip, "busy\n");
124 return (EBUSY);
125 }
126 px_p->px_soft_state = PCI_SOFT_STATE_OPEN;
127 }
128
129 mutex_exit(&px_p->px_mutex);
130 return (0);
131 }
132
133
134 /* ARGSUSED */
135 static int
px_close(dev_t dev,int flags,int otyp,cred_t * credp)136 px_close(dev_t dev, int flags, int otyp, cred_t *credp)
137 {
138 px_t *px_p = PX_DEV_TO_SOFTSTATE(dev);
139 int minor = getminor(dev);
140 int rval;
141
142 if (otyp != OTYP_CHR)
143 return (EINVAL);
144
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 switch (PCI_MINOR_NUM_TO_PCI_DEVNUM(minor)) {
152 case PCI_TOOL_REG_MINOR_NUM:
153 case PCI_TOOL_INTR_MINOR_NUM:
154 break;
155 default:
156 /* To handle devctl and hotplug related ioctls */
157 if (rval = pcie_close(px_p->px_dip, dev, flags, otyp, credp)) {
158 mutex_exit(&px_p->px_mutex);
159 return (rval);
160 }
161 }
162
163 px_p->px_soft_state = PCI_SOFT_STATE_CLOSED;
164 mutex_exit(&px_p->px_mutex);
165 return (0);
166 }
167
168 /* ARGSUSED */
169 static int
px_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)170 px_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
171 {
172 px_t *px_p = PX_DEV_TO_SOFTSTATE(dev);
173 int minor = getminor(dev);
174 dev_info_t *dip;
175 int rv = ENOTTY;
176
177 if (px_p == NULL)
178 return (ENXIO);
179
180 dip = px_p->px_dip;
181 DBG(DBG_IOCTL, dip, "dev=%x: cmd=%x\n", dev, cmd);
182
183 #ifdef PX_DMA_TEST
184 if (IS_DMATEST(cmd)) {
185 *rvalp = px_dma_test(cmd, dip, px_p, arg);
186 return (0);
187 }
188 #endif /* PX_DMA_TEST */
189
190 switch (PCI_MINOR_NUM_TO_PCI_DEVNUM(minor)) {
191 /*
192 * PCI tools.
193 */
194 case PCI_TOOL_REG_MINOR_NUM:
195 switch (cmd) {
196 case PCITOOL_DEVICE_SET_REG:
197 case PCITOOL_DEVICE_GET_REG:
198
199 /* Require full privileges. */
200 if (secpolicy_kmdb(credp))
201 rv = EPERM;
202 else
203 rv = pxtool_dev_reg_ops(dip,
204 (void *)arg, cmd, mode);
205 break;
206
207 case PCITOOL_NEXUS_SET_REG:
208 case PCITOOL_NEXUS_GET_REG:
209
210 /* Require full privileges. */
211 if (secpolicy_kmdb(credp))
212 rv = EPERM;
213 else
214 rv = pxtool_bus_reg_ops(dip,
215 (void *)arg, cmd, mode);
216 break;
217
218 default:
219 rv = ENOTTY;
220 }
221 return (rv);
222 case PCI_TOOL_INTR_MINOR_NUM:
223 switch (cmd) {
224 case PCITOOL_DEVICE_SET_INTR:
225
226 /* Require full privileges. */
227 if (secpolicy_kmdb(credp)) {
228 rv = EPERM;
229 break;
230 }
231
232 /*FALLTHRU*/
233 /* These require no special privileges. */
234 case PCITOOL_DEVICE_GET_INTR:
235 case PCITOOL_SYSTEM_INTR_INFO:
236 rv = pxtool_intr(dip, (void *)arg, cmd, mode);
237 break;
238
239 default:
240 rv = ENOTTY;
241 }
242 return (rv);
243 default:
244 /* To handle devctl and hotplug related ioctls */
245 rv = pcie_ioctl(dip, dev, cmd, arg, mode, credp, rvalp);
246 break;
247 }
248
249 if ((cmd & ~PPMREQ_MASK) == PPMREQ) {
250
251 /* Need privileges to use these ioctls. */
252 if (drv_priv(credp)) {
253 DBG(DBG_TOOLS, dip,
254 "px_tools: Insufficient privileges\n");
255
256 return (EPERM);
257 }
258 return (px_lib_pmctl(cmd, px_p));
259 }
260
261 return (rv);
262 }
263