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) strncpy(dev->driver_name, driver_name, MAXMODCONFNAME-1); 248 dev->driver_name[MAXMODCONFNAME] = '\0'; 249 (void) strncpy(dev->path, path_name, MAXPATHLEN-1); 250 dev->dev_inst = instance; 251 } 252 253 /*ARGSUSED*/ 254 static int 255 niumxtool_intr_info(dev_info_t *dip, void *arg, int mode) 256 { 257 pcitool_intr_info_t intr_info; 258 int rval = DDI_SUCCESS; 259 260 /* If we need user_version, and to ret same user version as passed in */ 261 if (ddi_copyin(arg, &intr_info, sizeof (pcitool_intr_info_t), mode) != 262 DDI_SUCCESS) { 263 return (EFAULT); 264 } 265 266 intr_info.ctlr_version = 0; /* XXX how to get real version? */ 267 intr_info.ctlr_type = PCITOOL_CTLR_TYPE_RISC; 268 if (intr_info.flags & PCITOOL_INTR_FLAG_GET_MSI) 269 intr_info.num_intr = 0; 270 else 271 intr_info.num_intr = NIUMX_MAX_INTRS; 272 273 intr_info.drvr_version = PCITOOL_VERSION; 274 if (ddi_copyout(&intr_info, arg, sizeof (pcitool_intr_info_t), mode) != 275 DDI_SUCCESS) { 276 rval = EFAULT; 277 } 278 279 return (rval); 280 } 281 282 283 /* 284 * Get interrupt information for a given ino. 285 * Returns info only for inos mapped to devices. 286 * 287 * Returned info is valid only when iget.num_devs is returned > 0. 288 * If ino is not enabled or is not mapped to a device, 289 * iget.num_devs will be returned as = 0. 290 */ 291 /*ARGSUSED*/ 292 static int 293 niumxtool_get_intr(dev_info_t *dip, void *arg, int mode) 294 { 295 /* Array part isn't used here, but oh well... */ 296 pcitool_intr_get_t partial_iget; 297 pcitool_intr_get_t *iget_p = &partial_iget; 298 int copyout_rval; 299 niusysino_t sysino; 300 niucpuid_t cpu_id; 301 niumx_devstate_t *niumxds_p; 302 dev_info_t *ih_dip; 303 size_t iget_kmem_alloc_size = 0; 304 char pathname[MAXPATHLEN]; 305 int rval = EIO; 306 307 niumxds_p = (niumx_devstate_t *) 308 ddi_get_soft_state(niumx_state, ddi_get_instance(dip)); 309 310 /* Read in just the header part, no array section. */ 311 if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) != 312 DDI_SUCCESS) 313 return (EFAULT); 314 315 iget_p->status = PCITOOL_IO_ERROR; 316 iget_p->msi = (uint32_t)-1; 317 318 if (iget_p->flags & PCITOOL_INTR_FLAG_GET_MSI) { 319 iget_p->status = PCITOOL_INVALID_MSI; 320 rval = EINVAL; 321 goto done_get_intr; 322 } 323 324 /* Validate argument. */ 325 if (iget_p->ino > NIUMX_MAX_INTRS) { 326 iget_p->status = PCITOOL_INVALID_INO; 327 rval = EINVAL; 328 goto done_get_intr; 329 } 330 331 /* Caller wants device information returned. */ 332 if (iget_p->num_devs_ret > 0) { 333 /* 334 * Allocate room. 335 * Note if num_devs == 0 iget_p remains pointing to 336 * partial_iget. 337 */ 338 iget_kmem_alloc_size = PCITOOL_IGET_SIZE(iget_p->num_devs_ret); 339 iget_p = kmem_zalloc(iget_kmem_alloc_size, KM_SLEEP); 340 341 /* Read in whole structure to verify there's room. */ 342 if (ddi_copyin(arg, iget_p, iget_kmem_alloc_size, mode) != 343 DDI_SUCCESS) { 344 345 /* Be consistent and just return EFAULT here. */ 346 kmem_free(iget_p, iget_kmem_alloc_size); 347 348 return (EFAULT); 349 } 350 } 351 352 sysino = niumxds_p->niumx_ihtable[iget_p->ino].ih_sysino; 353 if (sysino == 0) { 354 iget_p->status = PCITOOL_IO_ERROR; 355 rval = EIO; 356 goto done_get_intr; 357 } 358 359 ih_dip = niumxds_p->niumx_ihtable[iget_p->ino].ih_dip; 360 361 ddi_pathname(ih_dip, pathname); 362 363 niumxtool_fill_in_intr_devs(&iget_p->dev[0], 364 (char *)ddi_driver_name(ih_dip), pathname, 365 ddi_get_instance(ih_dip)); 366 367 if (hvio_intr_gettarget(sysino, &cpu_id) != H_EOK) { 368 iget_p->status = PCITOOL_IO_ERROR; 369 rval = EIO; 370 goto done_get_intr; 371 } 372 if (niumxds_p->niumx_ihtable[iget_p->ino].ih_cpuid != cpu_id) { 373 cmn_err(CE_WARN, "CPU Does not match %x %x", cpu_id, 374 niumxds_p->niumx_ihtable[iget_p->ino].ih_cpuid); 375 iget_p->status = PCITOOL_IO_ERROR; 376 rval = EIO; 377 goto done_get_intr; 378 } 379 iget_p->num_devs = 1; 380 iget_p->cpu_id = niumxds_p->niumx_ihtable[iget_p->ino].ih_cpuid; 381 iget_p->status = PCITOOL_SUCCESS; 382 rval = DDI_SUCCESS; 383 384 done_get_intr: 385 iget_p->drvr_version = PCITOOL_VERSION; 386 copyout_rval = 387 ddi_copyout(iget_p, arg, PCITOOL_IGET_SIZE(iget_p->num_devs_ret), 388 mode); 389 390 if (iget_kmem_alloc_size > 0) 391 kmem_free(iget_p, iget_kmem_alloc_size); 392 393 if (copyout_rval != DDI_SUCCESS) 394 rval = EFAULT; 395 396 return (rval); 397 } 398 399 400 /* 401 * Associate a new CPU with a given ino. 402 * 403 * Operate only on inos which are already mapped to devices. 404 */ 405 static int 406 niumxtool_set_intr(dev_info_t *dip, void *arg, int mode) 407 { 408 pcitool_intr_set_t iset; 409 niucpuid_t old_cpu_id; 410 int rval = EIO; 411 int ret = DDI_SUCCESS; 412 size_t copyinout_size; 413 niumx_devstate_t *niumxds_p; 414 415 niumxds_p = (niumx_devstate_t *) 416 ddi_get_soft_state(niumx_state, ddi_get_instance(dip)); 417 418 bzero(&iset, sizeof (pcitool_intr_set_t)); 419 420 /* Version 1 of pcitool_intr_set_t doesn't have flags. */ 421 copyinout_size = (size_t)&iset.flags - (size_t)&iset; 422 423 if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS) 424 return (EFAULT); 425 426 switch (iset.user_version) { 427 case PCITOOL_V1: 428 break; 429 430 case PCITOOL_V2: 431 copyinout_size = sizeof (pcitool_intr_set_t); 432 if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS) 433 return (EFAULT); 434 break; 435 436 default: 437 iset.status = PCITOOL_OUT_OF_RANGE; 438 rval = ENOTSUP; 439 goto done_set_intr; 440 } 441 442 if (iset.flags & PCITOOL_INTR_FLAG_SET_GROUP) { 443 iset.status = PCITOOL_IO_ERROR; 444 rval = ENOTSUP; 445 goto done_set_intr; 446 } 447 448 iset.status = PCITOOL_IO_ERROR; 449 450 iset.msi = (uint32_t)-1; 451 452 /* Validate input argument. */ 453 if (iset.ino > NIUMX_MAX_INTRS) { 454 iset.status = PCITOOL_INVALID_INO; 455 rval = EINVAL; 456 goto done_set_intr; 457 } 458 459 old_cpu_id = niumxds_p->niumx_ihtable[iset.ino].ih_cpuid; 460 461 if ((ret = niumx_set_intr_target(niumxds_p, iset.ino, 462 iset.cpu_id)) == DDI_SUCCESS) { 463 iset.cpu_id = old_cpu_id; 464 iset.status = PCITOOL_SUCCESS; 465 rval = DDI_SUCCESS; 466 goto done_set_intr; 467 } 468 469 switch (ret) { 470 case DDI_EPENDING: 471 iset.status = PCITOOL_PENDING_INTRTIMEOUT; 472 rval = ETIME; 473 break; 474 case DDI_EINVAL: 475 iset.status = PCITOOL_INVALID_CPUID; 476 rval = EINVAL; 477 break; 478 default: 479 iset.status = PCITOOL_IO_ERROR; 480 rval = EIO; 481 break; 482 } 483 484 done_set_intr: 485 iset.drvr_version = PCITOOL_VERSION; 486 if (ddi_copyout(&iset, arg, copyinout_size, mode) != DDI_SUCCESS) 487 rval = EFAULT; 488 489 return (rval); 490 } 491 492 493 /* Main function for handling interrupt CPU binding requests and queries. */ 494 static int 495 niumxtool_intr(dev_info_t *dip, void *arg, int cmd, int mode) 496 { 497 498 int rval = DDI_SUCCESS; 499 500 switch (cmd) { 501 502 /* Get system interrupt information. */ 503 case PCITOOL_SYSTEM_INTR_INFO: 504 rval = niumxtool_intr_info(dip, arg, mode); 505 break; 506 507 /* Get interrupt information for a given ino. */ 508 case PCITOOL_DEVICE_GET_INTR: 509 rval = niumxtool_get_intr(dip, arg, mode); 510 break; 511 512 /* Associate a new CPU with a given ino. */ 513 case PCITOOL_DEVICE_SET_INTR: 514 rval = niumxtool_set_intr(dip, arg, mode); 515 break; 516 517 default: 518 rval = ENOTTY; 519 } 520 521 return (rval); 522 } 523