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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/stat.h> 31 #include <sys/cpuvar.h> 32 #include <sys/kmem.h> 33 #include <sys/sunddi.h> 34 #include <sys/hotplug/pci/pcihp.h> 35 #include "px_obj.h" 36 #include <sys/pci_tools.h> 37 #include "px_tools_ext.h" 38 #include "px_tools_var.h" 39 40 /* 41 * PCI Space definitions. 42 */ 43 #define PCI_CONFIG_SPACE (PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) 44 #define PCI_IO_SPACE (PCI_REG_ADDR_G(PCI_ADDR_IO)) 45 #define PCI_MEM32_SPACE (PCI_REG_ADDR_G(PCI_ADDR_MEM32)) 46 #define PCI_MEM64_SPACE (PCI_REG_ADDR_G(PCI_ADDR_MEM64)) 47 48 /* 49 * Config space range for a device. IEEE 1275 spec defines for PCI. 50 * Space for PCI-express is multiplied by PX_PCI_BDF_OFFSET_DELTA 51 */ 52 #define DEV_CFG_SPACE_SIZE \ 53 (1 << (PCI_REG_FUNC_SHIFT + PX_PCI_BDF_OFFSET_DELTA)) 54 55 /* 56 * Offsets of BARS in config space. First entry of 0 means config space. 57 * Entries here correlate to pcitool_bars_t enumerated type. 58 */ 59 uint8_t pci_bars[] = { 60 0x0, 61 PCI_CONF_BASE0, 62 PCI_CONF_BASE1, 63 PCI_CONF_BASE2, 64 PCI_CONF_BASE3, 65 PCI_CONF_BASE4, 66 PCI_CONF_BASE5, 67 PCI_CONF_ROM 68 }; 69 70 int pci_num_bars = sizeof (pci_bars) / sizeof (pci_bars[0]); 71 72 /* 73 * Validate the cpu_id passed in. 74 * A value of 1 will be returned for success and zero for failure. 75 */ 76 static int 77 pxtool_validate_cpuid(uint32_t cpuid) 78 { 79 extern const int _ncpu; 80 extern cpu_t *cpu[]; 81 82 ASSERT(mutex_owned(&cpu_lock)); 83 84 return ((cpuid < _ncpu) && (cpu[cpuid] && cpu_is_online(cpu[cpuid]))); 85 } 86 87 88 static int 89 pxtool_intr_get_max_ino(uint32_t *arg, int mode) 90 { 91 if (ddi_copyout(&pxtool_num_inos, arg, sizeof (uint32_t), mode) != 92 DDI_SUCCESS) 93 return (EFAULT); 94 else 95 return (SUCCESS); 96 } 97 /* 98 * Get interrupt information for a given ino. 99 * Returns info only for inos mapped to devices. 100 * 101 * Returned info is valid only when iget.num_devs is returned > 0. 102 * If ino is not enabled or is not mapped to a device, 103 * iget.num_devs will be returned as = 0. 104 */ 105 /*ARGSUSED*/ 106 static int 107 pxtool_get_intr(dev_info_t *dip, void *arg, int mode) 108 { 109 /* Array part isn't used here, but oh well... */ 110 pcitool_intr_get_t partial_iget; 111 uint32_t ino; 112 uint8_t num_devs_ret; 113 int copyout_rval; 114 sysino_t sysino; 115 intr_valid_state_t intr_valid_state; 116 cpuid_t old_cpu_id; 117 px_t *px_p = DIP_TO_STATE(dip); 118 pcitool_intr_get_t *iget = &partial_iget; 119 size_t iget_kmem_alloc_size = 0; 120 int rval = SUCCESS; 121 122 /* Read in just the header part, no array section. */ 123 if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) != 124 DDI_SUCCESS) 125 return (EFAULT); 126 127 ino = partial_iget.ino; 128 num_devs_ret = partial_iget.num_devs_ret; 129 130 partial_iget.num_devs_ret = 0; /* Assume error for now. */ 131 partial_iget.status = PCITOOL_INVALID_INO; 132 rval = EINVAL; 133 134 /* Validate argument. */ 135 if (partial_iget.ino > pxtool_num_inos) { 136 goto done_get_intr; 137 } 138 139 /* Caller wants device information returned. */ 140 if (num_devs_ret > 0) { 141 142 /* 143 * Allocate room. 144 * Note if num_devs == 0 iget remains pointing to 145 * partial_iget. 146 */ 147 iget_kmem_alloc_size = PCITOOL_IGET_SIZE(num_devs_ret); 148 iget = kmem_alloc(iget_kmem_alloc_size, KM_SLEEP); 149 150 /* Read in whole structure to verify there's room. */ 151 if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) != 152 SUCCESS) { 153 154 /* Be consistent and just return EFAULT here. */ 155 kmem_free(iget, iget_kmem_alloc_size); 156 157 return (EFAULT); 158 } 159 } 160 161 bzero(iget, PCITOOL_IGET_SIZE(num_devs_ret)); 162 iget->ino = ino; 163 iget->num_devs_ret = num_devs_ret; 164 165 /* Convert leaf-wide intr to system-wide intr */ 166 if (px_lib_intr_devino_to_sysino(dip, iget->ino, &sysino) == 167 DDI_FAILURE) { 168 iget->status = PCITOOL_IO_ERROR; 169 rval = EIO; 170 goto done_get_intr; 171 } 172 173 /* Operate only on inos which are already enabled. */ 174 if (px_lib_intr_getvalid(dip, sysino, &intr_valid_state) == 175 DDI_FAILURE) { 176 iget->status = PCITOOL_IO_ERROR; 177 rval = EIO; 178 goto done_get_intr; 179 } 180 181 /* 182 * Consider all valid inos: those mapped to the root complex itself 183 * as well as those mapped to devices. 184 */ 185 if (intr_valid_state == INTR_VALID) { 186 187 /* 188 * The following looks up the px_ib_ino_info and returns 189 * info of devices mapped to this ino. 190 */ 191 iget->num_devs = pxtool_ib_get_ino_devs( 192 px_p, ino, &iget->num_devs_ret, iget->dev); 193 194 if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) == 195 DDI_FAILURE) { 196 iget->status = PCITOOL_IO_ERROR; 197 rval = EIO; 198 goto done_get_intr; 199 } 200 iget->cpu_id = old_cpu_id; 201 } 202 203 iget->status = PCITOOL_SUCCESS; 204 rval = SUCCESS; 205 206 done_get_intr: 207 copyout_rval = 208 ddi_copyout(iget, arg, PCITOOL_IGET_SIZE(num_devs_ret), mode); 209 210 if (iget_kmem_alloc_size > 0) 211 kmem_free(iget, iget_kmem_alloc_size); 212 213 if (copyout_rval != DDI_SUCCESS) 214 rval = EFAULT; 215 216 return (rval); 217 } 218 219 220 /* 221 * Associate a new CPU with a given ino. 222 * 223 * Operate only on inos which are already mapped to devices. 224 */ 225 static int 226 pxtool_set_intr(dev_info_t *dip, void *arg, int mode) 227 { 228 pcitool_intr_set_t iset; 229 cpuid_t old_cpu_id; 230 sysino_t sysino; 231 px_t *px_p = DIP_TO_STATE(dip); 232 px_ib_t *ib_p = px_p->px_ib_p; 233 uint8_t zero = 0; 234 int rval = SUCCESS; 235 236 if (ddi_copyin(arg, &iset, sizeof (pcitool_intr_set_t), mode) != 237 DDI_SUCCESS) 238 return (EFAULT); 239 240 iset.status = PCITOOL_INVALID_INO; 241 rval = EINVAL; 242 243 /* Validate input argument. */ 244 if (iset.ino > pxtool_num_inos) 245 goto done_set_intr; 246 247 /* Validate that ino given belongs to a device. */ 248 if (pxtool_ib_get_ino_devs(px_p, iset.ino, &zero, NULL) == 0) 249 goto done_set_intr; 250 251 /* 252 * Get lock, validate cpu and write new mapreg value. 253 * Return original cpu value to caller via iset.cpu. 254 */ 255 mutex_enter(&cpu_lock); 256 if (pxtool_validate_cpuid(iset.cpu_id)) { 257 258 DBG(DBG_TOOLS, dip, "Enabling CPU %d\n", iset.cpu_id); 259 260 if (px_lib_intr_devino_to_sysino(dip, iset.ino, &sysino) == 261 DDI_FAILURE) 262 goto done_set_intr; 263 264 if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) == 265 DDI_FAILURE) 266 goto done_set_intr; 267 268 px_ib_intr_dist_en(dip, iset.cpu_id, iset.ino, B_TRUE); 269 270 px_ib_log_new_cpu(ib_p, old_cpu_id, iset.cpu_id, iset.ino); 271 272 iset.cpu_id = old_cpu_id; 273 iset.status = PCITOOL_SUCCESS; 274 rval = SUCCESS; 275 276 } else { /* Invalid cpu. Restore original register image. */ 277 278 DBG(DBG_TOOLS, dip, 279 "Invalid cpuid: writing orig mapreg value\n"); 280 281 iset.status = PCITOOL_INVALID_CPUID; 282 rval = EINVAL; 283 } 284 mutex_exit(&cpu_lock); 285 286 done_set_intr: 287 if (ddi_copyout(&iset, arg, sizeof (pcitool_intr_set_t), mode) != 288 DDI_SUCCESS) 289 rval = EFAULT; 290 291 return (rval); 292 } 293 294 295 /* Main function for handling interrupt CPU binding requests and queries. */ 296 int 297 pxtool_intr(dev_info_t *dip, void *arg, int cmd, int mode) 298 { 299 int rval = SUCCESS; 300 301 switch (cmd) { 302 303 /* Return the number of interrupts supported by a PCI bus. */ 304 case PCITOOL_DEVICE_NUM_INTR: 305 rval = pxtool_intr_get_max_ino(arg, mode); 306 break; 307 308 /* Get interrupt information for a given ino. */ 309 case PCITOOL_DEVICE_GET_INTR: 310 rval = pxtool_get_intr(dip, arg, mode); 311 break; 312 313 /* Associate a new CPU with a given ino. */ 314 case PCITOOL_DEVICE_SET_INTR: 315 rval = pxtool_set_intr(dip, arg, mode); 316 break; 317 318 default: 319 rval = ENOTTY; 320 } 321 322 return (rval); 323 } 324 325 326 static int 327 pxtool_validate_barnum_bdf(pcitool_reg_t *prg) 328 { 329 int rval = SUCCESS; 330 331 if (prg->barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) { 332 prg->status = PCITOOL_OUT_OF_RANGE; 333 rval = EINVAL; 334 335 /* Validate address arguments of bus / dev / func */ 336 } else if (((prg->bus_no & 337 (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != prg->bus_no) || 338 ((prg->dev_no & 339 (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != prg->dev_no) || 340 ((prg->func_no & 341 (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != prg->func_no)) { 342 prg->status = PCITOOL_INVALID_ADDRESS; 343 rval = EINVAL; 344 } 345 346 return (rval); 347 } 348 349 static int 350 pxtool_get_bar(px_t *px_p, pcitool_reg_t *prg_p, uint64_t max_addr, 351 uint64_t *bar_p, uint32_t *space_p) 352 { 353 int rval; 354 pcitool_reg_t cfg_prg = *prg_p; /* Make local copy. */ 355 dev_info_t *dip = px_p->px_dip; 356 357 *space_p = PCI_MEM32_SPACE; 358 *bar_p = 0; 359 360 /* 361 * Translate BAR number into offset of the BAR in 362 * the device's config space. 363 */ 364 cfg_prg.offset = PCI_BAR_OFFSET((*prg_p)); 365 cfg_prg.phys_addr = prg_p->phys_addr + cfg_prg.offset; 366 cfg_prg.acc_attr = 367 PCITOOL_ACC_ATTR_SIZE_4 | PCITOOL_ACC_ATTR_ENDN_LTL; 368 369 DBG(DBG_TOOLS, dip, "barnum:%d, phys_addr:0x%" PRIx64 "\n", 370 cfg_prg.barnum, cfg_prg.phys_addr); 371 372 /* 373 * Get Bus Address Register (BAR) from config space. 374 * cfg_prg.offset is the offset into config space of the 375 * BAR desired. prg_p->status is modified on error. 376 */ 377 rval = pxtool_pcicfg_access(px_p, &cfg_prg, max_addr, bar_p, PX_ISREAD); 378 379 if (rval != SUCCESS) { 380 prg_p->status = cfg_prg.status; 381 return (rval); 382 } 383 384 DBG(DBG_TOOLS, dip, "bar returned is 0x%" PRIx64 "\n", *bar_p); 385 386 /* 387 * BAR has bits saying this space is IO space, unless 388 * this is the ROM address register. 389 */ 390 if (((PCI_BASE_SPACE_M & *bar_p) == PCI_BASE_SPACE_IO) && 391 (cfg_prg.offset != PCI_CONF_ROM)) { 392 *space_p = PCI_IO_SPACE; 393 *bar_p &= PCI_BASE_IO_ADDR_M; 394 395 /* 396 * BAR has bits saying this space is 64 bit memory 397 * space, unless this is the ROM address register. 398 * 399 * The 64 bit address stored in two BAR cells is not 400 * necessarily aligned on an 8-byte boundary. 401 * Need to keep the first 4 bytes read, 402 * and do a separate read of the high 4 bytes. 403 */ 404 } else if ((PCI_BASE_TYPE_ALL & *bar_p) && 405 (cfg_prg.offset != PCI_CONF_ROM)) { 406 407 uint32_t low_bytes = (uint32_t)(*bar_p & ~PCI_BASE_TYPE_ALL); 408 409 /* Don't try to read the next 4 bytes past the end of BARs. */ 410 if (cfg_prg.offset >= PCI_CONF_BASE5) { 411 prg_p->status = PCITOOL_OUT_OF_RANGE; 412 return (EIO); 413 } 414 415 /* Access device. prg_p->status is modified on error. */ 416 cfg_prg.phys_addr += sizeof (uint32_t); 417 cfg_prg.offset += sizeof (uint32_t); 418 419 rval = pxtool_pcicfg_access(px_p, &cfg_prg, max_addr, bar_p, 420 PX_ISREAD); 421 if (rval != SUCCESS) { 422 prg_p->status = cfg_prg.status; 423 return (rval); 424 } 425 426 /* 427 * Honor the 64 bit BAR as such, only when the upper 32 bits 428 * store a non-zero value. 429 */ 430 if (*bar_p) { 431 *space_p = PCI_MEM64_SPACE; 432 *bar_p = (*bar_p << 32) | low_bytes; 433 } else 434 *bar_p = low_bytes; 435 436 } else if (cfg_prg.offset == PCI_CONF_ROM) { /* ROM requested */ 437 438 /* 439 * ROM enabled. Filter ROM enable bit from the BAR. 440 * Treat as Mem32 henceforth. 441 */ 442 if (!(*bar_p & PCI_BASE_ROM_ENABLE)) 443 *bar_p ^= PCI_BASE_ROM_ENABLE; 444 445 else { /* ROM disabled. */ 446 prg_p->status = PCITOOL_ROM_DISABLED; 447 return (EIO); 448 } 449 } 450 451 /* Accept a bar of 0 only for IO space. */ 452 if ((*space_p != PCI_IO_SPACE) && (!(*bar_p))) { 453 prg_p->status = PCITOOL_INVALID_ADDRESS; 454 return (EINVAL); 455 } 456 457 return (SUCCESS); 458 } 459 460 461 /* Perform register accesses on PCI leaf devices. */ 462 int 463 pxtool_dev_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode) 464 { 465 pcitool_reg_t prg; 466 uint64_t base_addr; 467 uint64_t range_base; 468 uint64_t range_base_size; 469 uint64_t max_addr; 470 uint64_t bar; 471 uint32_t space; 472 boolean_t write_flag = B_FALSE; 473 px_t *px_p = DIP_TO_STATE(dip); 474 px_ranges_t *rp = px_p->px_ranges_p; 475 int rval = 0; 476 477 if (cmd == PCITOOL_DEVICE_SET_REG) 478 write_flag = B_TRUE; 479 480 DBG(DBG_TOOLS, dip, "pxtool_dev_reg_ops set/get reg\n"); 481 if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), 482 mode) != DDI_SUCCESS) { 483 DBG(DBG_TOOLS, dip, "Error reading arguments\n"); 484 return (EFAULT); 485 } 486 487 if ((rval = pxtool_dev_reg_ops_platchk(dip, &prg)) != SUCCESS) { 488 goto done_reg; 489 } 490 491 DBG(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n", 492 prg.bus_no, prg.dev_no, prg.func_no); 493 DBG(DBG_TOOLS, dip, "barnum:0x%x, offset:0x%" PRIx64 ", acc:0x%x\n", 494 prg.barnum, prg.offset, prg.acc_attr); 495 496 if ((rval = pxtool_validate_barnum_bdf(&prg)) != SUCCESS) 497 goto done_reg; 498 499 range_base = PX_GET_RANGE_PROP(rp, PCI_CONFIG_SPACE); 500 range_base_size = PX_GET_RANGE_PROP_SIZE(rp, PCI_CONFIG_SPACE); 501 502 base_addr = range_base + (PX_GET_BDF(&prg) << PX_PCI_BDF_OFFSET_DELTA); 503 max_addr = base_addr + DEV_CFG_SPACE_SIZE - 1; 504 505 prg.phys_addr = base_addr; 506 507 if (prg.barnum == 0) { /* Proper config space desired. */ 508 509 /* Access config space and we're done. */ 510 prg.phys_addr = base_addr + prg.offset; 511 512 DBG(DBG_TOOLS, dip, 513 "config access: base:0x%" PRIx64 ", offset:0x%" PRIx64 ", " 514 "phys_addr:0x%" PRIx64 ", end:%s\n", 515 base_addr, prg.offset, prg.phys_addr, 516 PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr) ? "big":"ltl"); 517 518 /* 519 * Access device. pr.status is modified. 520 * BDF is assumed valid at this point. 521 */ 522 rval = pxtool_pcicfg_access(px_p, &prg, max_addr, &prg.data, 523 write_flag); 524 goto done_reg; 525 } 526 527 /* IO/ MEM/ MEM64 space. */ 528 529 if ((rval = pxtool_get_bar(px_p, &prg, max_addr, &bar, &space)) != 530 SUCCESS) 531 goto done_reg; 532 533 if (space == PCI_IO_SPACE) { /* IO space. */ 534 535 DBG(DBG_TOOLS, dip, "IO space\n"); 536 537 /* Focus on IO space. */ 538 range_base = PX_GET_RANGE_PROP(rp, PCI_IO_SPACE); 539 range_base_size = PX_GET_RANGE_PROP_SIZE(rp, PCI_IO_SPACE); 540 541 } else if (space == PCI_MEM64_SPACE) { /* 64 bit memory space. */ 542 543 DBG(DBG_TOOLS, dip, 544 "64 bit mem space. 64-bit bar is 0x%" PRIx64 "\n", bar); 545 546 /* Set focus to MEM64 range space. */ 547 range_base = PX_GET_RANGE_PROP(rp, PCI_MEM64_SPACE); 548 range_base_size = PX_GET_RANGE_PROP_SIZE(rp, PCI_MEM64_SPACE); 549 550 } else { /* Mem32 space, including ROM */ 551 552 DBG(DBG_TOOLS, dip, "32 bit mem space\n"); 553 554 /* Can't write to ROM */ 555 if ((PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) && (write_flag)) { 556 prg.status = PCITOOL_ROM_WRITE; 557 rval = EIO; 558 goto done_reg; 559 } 560 561 range_base = PX_GET_RANGE_PROP(rp, PCI_MEM32_SPACE); 562 range_base_size = PX_GET_RANGE_PROP_SIZE(rp, PCI_MEM32_SPACE); 563 } 564 565 /* Common code for all IO/MEM range spaces. */ 566 max_addr = range_base + range_base_size; 567 DBG(DBG_TOOLS, dip, "Range from 0x%" PRIx64 " to 0x%" PRIx64 "\n", 568 range_base, range_base + range_base_size); 569 570 base_addr = range_base + bar; 571 572 DBG(DBG_TOOLS, dip, 573 "addr portion of bar is 0x%" PRIx64 ", base=0x%" PRIx64 ", " 574 "offset:0x%" PRIx64 "\n", bar, base_addr, prg.offset); 575 576 /* 577 * Use offset provided by caller to index into desired space. 578 * Note that prg.status is modified on error. 579 */ 580 prg.phys_addr = base_addr + prg.offset; 581 582 rval = pxtool_pciiomem_access(px_p, &prg, max_addr, &prg.data, 583 write_flag); 584 585 done_reg: 586 if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), 587 mode) != DDI_SUCCESS) { 588 DBG(DBG_TOOLS, dip, "Error returning arguments.\n"); 589 rval = EFAULT; 590 } 591 return (rval); 592 } 593 594 595 int 596 pxtool_init(dev_info_t *dip) 597 { 598 int instance = ddi_get_instance(dip); 599 600 if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR, 601 PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM), 602 DDI_NT_REGACC, 0) != DDI_SUCCESS) { 603 return (DDI_FAILURE); 604 } 605 606 if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR, 607 PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM), 608 DDI_NT_INTRCTL, 0) != DDI_SUCCESS) { 609 ddi_remove_minor_node(dip, PCI_MINOR_REG); 610 return (DDI_FAILURE); 611 } 612 613 return (DDI_SUCCESS); 614 } 615 616 617 void 618 pxtool_uninit(dev_info_t *dip) 619 { 620 ddi_remove_minor_node(dip, PCI_MINOR_REG); 621 ddi_remove_minor_node(dip, PCI_MINOR_INTR); 622 } 623