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 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 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 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 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 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 238 niumxtool_uninit(dev_info_t *dip) 239 { 240 ddi_remove_minor_node(dip, PCI_MINOR_INTR); 241 } 242 243 static void 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 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 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 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 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