xref: /illumos-gate/usr/src/uts/sun4/io/px/px_devctl.c (revision 43449cdcd0600512dd862537f2cf014140dd0844)
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
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
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
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