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 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <sys/cpuvar.h>
29 #include <sys/conf.h>
30 #include <sys/kmem.h>
31 #include <sys/async.h>
32 #include <sys/sysmacros.h>
33 #include <sys/sunddi.h>
34 #include <sys/sunndi.h>
35 #include <sys/ddi_impldefs.h>
36 #include <sys/open.h>
37 #include <sys/errno.h>
38 #include <sys/file.h>
39 #include <sys/policy.h>
40 #include <sys/pci_tools.h>
41 #include <sys/pci_impl.h>
42 #include <sys/hypervisor_api.h>
43 #include <sys/hotplug/pci/pcihp.h>
44 #include "niumx_var.h"
45
46 /*
47 * NIUMX PCITool interface
48 */
49 /*LINTLIBRARY*/
50
51 static int niumx_open(dev_t *devp, int flags, int otyp, cred_t *credp);
52 static int niumx_close(dev_t dev, int flags, int otyp, cred_t *credp);
53 static int niumx_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
54 cred_t *credp, int *rvalp);
55 static int niumx_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
56 int flags, char *name, caddr_t valuep, int *lengthp);
57
58 struct cb_ops niumx_cb_ops = {
59 niumx_open, /* open */
60 niumx_close, /* close */
61 nodev, /* strategy */
62 nodev, /* print */
63 nodev, /* dump */
64 nodev, /* read */
65 nodev, /* write */
66 niumx_ioctl, /* ioctl */
67 nodev, /* devmap */
68 nodev, /* mmap */
69 nodev, /* segmap */
70 nochpoll, /* poll */
71 niumx_prop_op, /* cb_prop_op */
72 NULL, /* streamtab */
73 D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */
74 CB_REV, /* rev */
75 nodev, /* int (*cb_aread)() */
76 nodev /* int (*cb_awrite)() */
77 };
78
79 static void niumxtool_fill_in_intr_devs(pcitool_intr_dev_t *dev,
80 char *driver_name, char *path_name, int instance);
81
82 static int niumxtool_intr(dev_info_t *dip, void *arg, int cmd, int mode);
83
84 int niumx_set_intr_target(niumx_devstate_t *niumxds_p, niudevino_t ino,
85 niucpuid_t cpu_id);
86
87 extern void *niumx_state;
88 /* ARGSUSED3 */
89 static int
niumx_open(dev_t * devp,int flags,int otyp,cred_t * credp)90 niumx_open(dev_t *devp, int flags, int otyp, cred_t *credp)
91 {
92 niumx_devstate_t *niumxds_p;
93 minor_t minor = getminor(*devp);
94
95 /*
96 * Make sure the open is for the right file type.
97 */
98 if (otyp != OTYP_CHR)
99 return (EINVAL);
100
101 /*
102 * Get the soft state structure for the device.
103 */
104 niumxds_p = (niumx_devstate_t *)ddi_get_soft_state(niumx_state,
105 PCI_MINOR_NUM_TO_INSTANCE(minor));
106 if (niumxds_p == NULL)
107 return (ENXIO);
108
109 /*
110 * Handle the open by tracking the device state.
111 */
112 mutex_enter(&niumxds_p->niumx_mutex);
113 if (flags & FEXCL) {
114 if (niumxds_p->niumx_soft_state != NIUMX_SOFT_STATE_CLOSED) {
115 mutex_exit(&niumxds_p->niumx_mutex);
116 return (EBUSY);
117 }
118 niumxds_p->niumx_soft_state = NIUMX_SOFT_STATE_OPEN_EXCL;
119 } else {
120 if (niumxds_p->niumx_soft_state == NIUMX_SOFT_STATE_OPEN_EXCL) {
121 mutex_exit(&niumxds_p->niumx_mutex);
122 return (EBUSY);
123 }
124 niumxds_p->niumx_soft_state = NIUMX_SOFT_STATE_OPEN;
125 }
126
127 niumxds_p->niumx_open_count++;
128 mutex_exit(&niumxds_p->niumx_mutex);
129 return (0);
130 }
131
132 /* ARGSUSED */
133 static int
niumx_close(dev_t dev,int flags,int otyp,cred_t * credp)134 niumx_close(dev_t dev, int flags, int otyp, cred_t *credp)
135 {
136 niumx_devstate_t *niumxds_p;
137 minor_t minor = getminor(dev);
138
139 if (otyp != OTYP_CHR)
140 return (EINVAL);
141
142 /*
143 * Get the soft state structure for the device.
144 */
145 niumxds_p = (niumx_devstate_t *)ddi_get_soft_state(niumx_state,
146 PCI_MINOR_NUM_TO_INSTANCE(minor));
147
148 if (niumxds_p == NULL)
149 return (ENXIO);
150
151 mutex_enter(&niumxds_p->niumx_mutex);
152
153 niumxds_p->niumx_soft_state = NIUMX_SOFT_STATE_CLOSED;
154 niumxds_p->niumx_open_count = 0;
155 mutex_exit(&niumxds_p->niumx_mutex);
156 return (0);
157 }
158
159 /* ARGSUSED */
160 int
niumx_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)161 niumx_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
162 int *rvalp)
163 {
164 niumx_devstate_t *niumxds_p;
165 dev_info_t *dip;
166 int rv = DDI_SUCCESS;
167 int minor = getminor(dev);
168
169 /*
170 * Get the soft state structure for the device.
171 */
172 niumxds_p = (niumx_devstate_t *)ddi_get_soft_state(niumx_state,
173 PCI_MINOR_NUM_TO_INSTANCE(minor));
174
175 if (niumxds_p == NULL) {
176 return (ENXIO);
177 }
178
179 dip = niumxds_p->dip;
180
181 switch (minor & 0xff) {
182
183 /*
184 * PCI tools.
185 */
186
187 case PCI_TOOL_INTR_MINOR_NUM:
188
189 switch (cmd) {
190 case PCITOOL_DEVICE_SET_INTR:
191
192 /* Require full privileges. */
193 if (secpolicy_kmdb(credp)) {
194 rv = EPERM;
195 break;
196 }
197
198 /*FALLTHRU*/
199 /* These require no special privileges. */
200 case PCITOOL_DEVICE_GET_INTR:
201 case PCITOOL_SYSTEM_INTR_INFO:
202 rv = niumxtool_intr(dip, (void *)arg, cmd, mode);
203 break;
204
205 default:
206 rv = ENOTTY;
207 }
208 return (rv);
209
210 default:
211 break;
212 }
213 return (rv);
214 }
215
niumx_prop_op(dev_t dev,dev_info_t * dip,ddi_prop_op_t prop_op,int flags,char * name,caddr_t valuep,int * lengthp)216 static int niumx_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
217 int flags, char *name, caddr_t valuep, int *lengthp)
218 {
219 return (ddi_prop_op(dev, dip, prop_op, flags, name, valuep, lengthp));
220 }
221
222 int
niumxtool_init(dev_info_t * dip)223 niumxtool_init(dev_info_t *dip)
224 {
225 int instance = ddi_get_instance(dip);
226
227 if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR,
228 PCI_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM),
229 DDI_NT_INTRCTL, 0) != DDI_SUCCESS) {
230 ddi_remove_minor_node(dip, PCI_MINOR_REG);
231 return (DDI_FAILURE);
232 }
233
234 return (DDI_SUCCESS);
235 }
236
237 void
niumxtool_uninit(dev_info_t * dip)238 niumxtool_uninit(dev_info_t *dip)
239 {
240 ddi_remove_minor_node(dip, PCI_MINOR_INTR);
241 }
242
243 static void
niumxtool_fill_in_intr_devs(pcitool_intr_dev_t * dev,char * driver_name,char * path_name,int instance)244 niumxtool_fill_in_intr_devs(pcitool_intr_dev_t *dev, char *driver_name,
245 char *path_name, int instance)
246 {
247 (void) strlcpy(dev->driver_name, driver_name, MAXMODCONFNAME);
248 (void) strlcpy(dev->path, path_name, MAXPATHLEN);
249 dev->dev_inst = instance;
250 }
251
252 /*ARGSUSED*/
253 static int
niumxtool_intr_info(dev_info_t * dip,void * arg,int mode)254 niumxtool_intr_info(dev_info_t *dip, void *arg, int mode)
255 {
256 pcitool_intr_info_t intr_info;
257 int rval = DDI_SUCCESS;
258
259 /* If we need user_version, and to ret same user version as passed in */
260 if (ddi_copyin(arg, &intr_info, sizeof (pcitool_intr_info_t), mode) !=
261 DDI_SUCCESS) {
262 return (EFAULT);
263 }
264
265 intr_info.ctlr_version = 0; /* XXX how to get real version? */
266 intr_info.ctlr_type = PCITOOL_CTLR_TYPE_RISC;
267 if (intr_info.flags & PCITOOL_INTR_FLAG_GET_MSI)
268 intr_info.num_intr = 0;
269 else
270 intr_info.num_intr = NIUMX_MAX_INTRS;
271
272 intr_info.drvr_version = PCITOOL_VERSION;
273 if (ddi_copyout(&intr_info, arg, sizeof (pcitool_intr_info_t), mode) !=
274 DDI_SUCCESS) {
275 rval = EFAULT;
276 }
277
278 return (rval);
279 }
280
281
282 /*
283 * Get interrupt information for a given ino.
284 * Returns info only for inos mapped to devices.
285 *
286 * Returned info is valid only when iget.num_devs is returned > 0.
287 * If ino is not enabled or is not mapped to a device,
288 * iget.num_devs will be returned as = 0.
289 */
290 /*ARGSUSED*/
291 static int
niumxtool_get_intr(dev_info_t * dip,void * arg,int mode)292 niumxtool_get_intr(dev_info_t *dip, void *arg, int mode)
293 {
294 /* Array part isn't used here, but oh well... */
295 pcitool_intr_get_t partial_iget;
296 pcitool_intr_get_t *iget_p = &partial_iget;
297 int copyout_rval;
298 niusysino_t sysino;
299 niucpuid_t cpu_id;
300 niumx_devstate_t *niumxds_p;
301 dev_info_t *ih_dip;
302 size_t iget_kmem_alloc_size = 0;
303 char pathname[MAXPATHLEN];
304 int rval = EIO;
305
306 niumxds_p = (niumx_devstate_t *)
307 ddi_get_soft_state(niumx_state, ddi_get_instance(dip));
308
309 /* Read in just the header part, no array section. */
310 if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) !=
311 DDI_SUCCESS)
312 return (EFAULT);
313
314 iget_p->status = PCITOOL_IO_ERROR;
315 iget_p->msi = (uint32_t)-1;
316
317 if (iget_p->flags & PCITOOL_INTR_FLAG_GET_MSI) {
318 iget_p->status = PCITOOL_INVALID_MSI;
319 rval = EINVAL;
320 goto done_get_intr;
321 }
322
323 /* Validate argument. */
324 if (iget_p->ino > NIUMX_MAX_INTRS) {
325 iget_p->status = PCITOOL_INVALID_INO;
326 rval = EINVAL;
327 goto done_get_intr;
328 }
329
330 /* Caller wants device information returned. */
331 if (iget_p->num_devs_ret > 0) {
332 /*
333 * Allocate room.
334 * Note if num_devs == 0 iget_p remains pointing to
335 * partial_iget.
336 */
337 iget_kmem_alloc_size = PCITOOL_IGET_SIZE(iget_p->num_devs_ret);
338 iget_p = kmem_zalloc(iget_kmem_alloc_size, KM_SLEEP);
339
340 /* Read in whole structure to verify there's room. */
341 if (ddi_copyin(arg, iget_p, iget_kmem_alloc_size, mode) !=
342 DDI_SUCCESS) {
343
344 /* Be consistent and just return EFAULT here. */
345 kmem_free(iget_p, iget_kmem_alloc_size);
346
347 return (EFAULT);
348 }
349 }
350
351 sysino = niumxds_p->niumx_ihtable[iget_p->ino].ih_sysino;
352 if (sysino == 0) {
353 iget_p->status = PCITOOL_IO_ERROR;
354 rval = EIO;
355 goto done_get_intr;
356 }
357
358 ih_dip = niumxds_p->niumx_ihtable[iget_p->ino].ih_dip;
359
360 ddi_pathname(ih_dip, pathname);
361
362 niumxtool_fill_in_intr_devs(&iget_p->dev[0],
363 (char *)ddi_driver_name(ih_dip), pathname,
364 ddi_get_instance(ih_dip));
365
366 if (hvio_intr_gettarget(sysino, &cpu_id) != H_EOK) {
367 iget_p->status = PCITOOL_IO_ERROR;
368 rval = EIO;
369 goto done_get_intr;
370 }
371 if (niumxds_p->niumx_ihtable[iget_p->ino].ih_cpuid != cpu_id) {
372 cmn_err(CE_WARN, "CPU Does not match %x %x", cpu_id,
373 niumxds_p->niumx_ihtable[iget_p->ino].ih_cpuid);
374 iget_p->status = PCITOOL_IO_ERROR;
375 rval = EIO;
376 goto done_get_intr;
377 }
378 iget_p->num_devs = 1;
379 iget_p->cpu_id = niumxds_p->niumx_ihtable[iget_p->ino].ih_cpuid;
380 iget_p->status = PCITOOL_SUCCESS;
381 rval = DDI_SUCCESS;
382
383 done_get_intr:
384 iget_p->drvr_version = PCITOOL_VERSION;
385 copyout_rval =
386 ddi_copyout(iget_p, arg, PCITOOL_IGET_SIZE(iget_p->num_devs_ret),
387 mode);
388
389 if (iget_kmem_alloc_size > 0)
390 kmem_free(iget_p, iget_kmem_alloc_size);
391
392 if (copyout_rval != DDI_SUCCESS)
393 rval = EFAULT;
394
395 return (rval);
396 }
397
398
399 /*
400 * Associate a new CPU with a given ino.
401 *
402 * Operate only on inos which are already mapped to devices.
403 */
404 static int
niumxtool_set_intr(dev_info_t * dip,void * arg,int mode)405 niumxtool_set_intr(dev_info_t *dip, void *arg, int mode)
406 {
407 pcitool_intr_set_t iset;
408 niucpuid_t old_cpu_id;
409 int rval = EIO;
410 int ret = DDI_SUCCESS;
411 size_t copyinout_size;
412 niumx_devstate_t *niumxds_p;
413
414 niumxds_p = (niumx_devstate_t *)
415 ddi_get_soft_state(niumx_state, ddi_get_instance(dip));
416
417 bzero(&iset, sizeof (pcitool_intr_set_t));
418
419 /* Version 1 of pcitool_intr_set_t doesn't have flags. */
420 copyinout_size = (size_t)&iset.flags - (size_t)&iset;
421
422 if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
423 return (EFAULT);
424
425 switch (iset.user_version) {
426 case PCITOOL_V1:
427 break;
428
429 case PCITOOL_V2:
430 copyinout_size = sizeof (pcitool_intr_set_t);
431 if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
432 return (EFAULT);
433 break;
434
435 default:
436 iset.status = PCITOOL_OUT_OF_RANGE;
437 rval = ENOTSUP;
438 goto done_set_intr;
439 }
440
441 if (iset.flags & PCITOOL_INTR_FLAG_SET_GROUP) {
442 iset.status = PCITOOL_IO_ERROR;
443 rval = ENOTSUP;
444 goto done_set_intr;
445 }
446
447 iset.status = PCITOOL_IO_ERROR;
448
449 iset.msi = (uint32_t)-1;
450
451 /* Validate input argument. */
452 if (iset.ino > NIUMX_MAX_INTRS) {
453 iset.status = PCITOOL_INVALID_INO;
454 rval = EINVAL;
455 goto done_set_intr;
456 }
457
458 old_cpu_id = niumxds_p->niumx_ihtable[iset.ino].ih_cpuid;
459
460 if ((ret = niumx_set_intr_target(niumxds_p, iset.ino,
461 iset.cpu_id)) == DDI_SUCCESS) {
462 iset.cpu_id = old_cpu_id;
463 iset.status = PCITOOL_SUCCESS;
464 rval = DDI_SUCCESS;
465 goto done_set_intr;
466 }
467
468 switch (ret) {
469 case DDI_EPENDING:
470 iset.status = PCITOOL_PENDING_INTRTIMEOUT;
471 rval = ETIME;
472 break;
473 case DDI_EINVAL:
474 iset.status = PCITOOL_INVALID_CPUID;
475 rval = EINVAL;
476 break;
477 default:
478 iset.status = PCITOOL_IO_ERROR;
479 rval = EIO;
480 break;
481 }
482
483 done_set_intr:
484 iset.drvr_version = PCITOOL_VERSION;
485 if (ddi_copyout(&iset, arg, copyinout_size, mode) != DDI_SUCCESS)
486 rval = EFAULT;
487
488 return (rval);
489 }
490
491
492 /* Main function for handling interrupt CPU binding requests and queries. */
493 static int
niumxtool_intr(dev_info_t * dip,void * arg,int cmd,int mode)494 niumxtool_intr(dev_info_t *dip, void *arg, int cmd, int mode)
495 {
496
497 int rval = DDI_SUCCESS;
498
499 switch (cmd) {
500
501 /* Get system interrupt information. */
502 case PCITOOL_SYSTEM_INTR_INFO:
503 rval = niumxtool_intr_info(dip, arg, mode);
504 break;
505
506 /* Get interrupt information for a given ino. */
507 case PCITOOL_DEVICE_GET_INTR:
508 rval = niumxtool_get_intr(dip, arg, mode);
509 break;
510
511 /* Associate a new CPU with a given ino. */
512 case PCITOOL_DEVICE_SET_INTR:
513 rval = niumxtool_set_intr(dip, arg, mode);
514 break;
515
516 default:
517 rval = ENOTTY;
518 }
519
520 return (rval);
521 }
522