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 /* 23 * Copyright 2012, Joyent, Inc. All rights reserved. 24 * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 25 */ 26 27 /* 28 * The ipmi driver is an openipmi compatible IPMI driver based on the FreeBSD 29 * driver. 30 * 31 * The current implementation has several limitations: 32 * 1) It only does discovery through the SMBIOS. The FreeBSD driver has 33 * several additional ways to discover the IPMI device (acpi, bus checking, 34 * etc.). This support could be ported if necessary. 35 * 2) The driver currently only supports the IPMI KCS_MODE mode (reported 36 * through the SMBIOS as SMBIOS SMB_IPMI_T_KCS). Support for the other modes 37 * (BT_MODE, SMIC_MODE, SSIF_MODE) could be ported if necessary. 38 * 3) The driver does not currently set up an IPMI watchdog. This also could 39 * be ported if necessary. 40 */ 41 42 #include <sys/devops.h> 43 #include <sys/conf.h> 44 #include <sys/modctl.h> 45 #include <sys/types.h> 46 #include <sys/file.h> 47 #include <sys/errno.h> 48 #include <sys/open.h> 49 #include <sys/cred.h> 50 #include <sys/uio.h> 51 #include <sys/stat.h> 52 #include <sys/cmn_err.h> 53 #include <sys/ddi.h> 54 #include <sys/sunddi.h> 55 #include <sys/smbios.h> 56 #include <sys/smbios_impl.h> 57 #include <sys/policy.h> 58 #include <sys/ipmi.h> 59 #include "ipmivars.h" 60 61 static dev_info_t *ipmi_dip; 62 static boolean_t ipmi_attached = B_FALSE; 63 static boolean_t ipmi_found = B_FALSE; 64 static struct ipmi_softc softc; 65 static struct ipmi_softc *sc = &softc; 66 static list_t dev_list; 67 static id_space_t *minor_ids; 68 69 #define PTRIN(p) ((void *)(uintptr_t)(p)) 70 #define PTROUT(p) ((uintptr_t)(p)) 71 72 /* 73 * Use the SMBIOS info to determine if the system has an IPMI. 74 */ 75 static int 76 get_smbios_ipmi_info(void) 77 { 78 smbios_ipmi_t ipmi; 79 80 if (ksmbios == NULL || smbios_info_ipmi(ksmbios, &ipmi) == SMB_ERR) 81 return (DDI_FAILURE); 82 83 cmn_err(CE_CONT, "!SMBIOS type 0x%x, addr 0x%llx", ipmi.smbip_type, 84 (long long unsigned int)(ipmi.smbip_addr)); 85 86 /* 87 * Some systems have a bios that will report an IPMI device even when 88 * it is not installed. In this case we see 0x0 as the base address. 89 * If we see this address, assume the device is not really present. 90 */ 91 if (ipmi.smbip_addr == NULL) { 92 cmn_err(CE_WARN, "!SMBIOS: Invalid base address"); 93 return (DDI_FAILURE); 94 } 95 96 sc->ipmi_io_type = ipmi.smbip_type; 97 switch (ipmi.smbip_type) { 98 case SMB_IPMI_T_KCS: 99 case SMB_IPMI_T_SMIC: 100 sc->ipmi_io_address = ipmi.smbip_addr; 101 sc->ipmi_io_mode = (ipmi.smbip_flags & SMB_IPMI_F_IOADDR) ? 102 1 : 0; 103 sc->ipmi_io_spacing = ipmi.smbip_regspacing; 104 break; 105 case SMB_IPMI_T_SSIF: 106 if ((ipmi.smbip_addr & 0xffffffffffffff00) != 0) { 107 cmn_err(CE_WARN, "!SMBIOS: Invalid SSIF SMBus address, " 108 "using BMC I2C slave address instead"); 109 sc->ipmi_io_address = ipmi.smbip_i2c; 110 } else { 111 sc->ipmi_io_address = ipmi.smbip_addr; 112 } 113 break; 114 default: 115 return (DDI_FAILURE); 116 } 117 118 if (ipmi.smbip_intr > 15) { 119 cmn_err(CE_WARN, "!SMBIOS: Non-ISA IRQ %d for IPMI", 120 ipmi.smbip_intr); 121 return (DDI_FAILURE); 122 } 123 124 sc->ipmi_io_irq = ipmi.smbip_intr; 125 return (DDI_SUCCESS); 126 } 127 128 static ipmi_device_t * 129 lookup_ipmidev_by_dev(dev_t dev) 130 { 131 ipmi_device_t *p; 132 133 for (p = list_head(&dev_list); p; p = list_next(&dev_list, p)) { 134 if (dev == p->ipmi_dev) 135 return (p); 136 } 137 return (NULL); 138 } 139 140 /* 141 * Each open returns a new pseudo device. 142 */ 143 /*ARGSUSED*/ 144 static int 145 ipmi_open(dev_t *devp, int flag, int otyp, cred_t *cred) 146 { 147 minor_t minor; 148 ipmi_device_t *dev; 149 150 if (ipmi_attached == B_FALSE) 151 return (ENXIO); 152 153 if (ipmi_found == B_FALSE) 154 return (ENODEV); 155 156 /* exclusive opens are not supported */ 157 if (flag & FEXCL) 158 return (ENOTSUP); 159 160 if ((minor = (minor_t)id_alloc_nosleep(minor_ids)) == 0) 161 return (ENODEV); 162 163 /* Initialize the per file descriptor data. */ 164 dev = kmem_zalloc(sizeof (ipmi_device_t), KM_SLEEP); 165 166 dev->ipmi_pollhead = kmem_zalloc(sizeof (pollhead_t), KM_SLEEP); 167 168 TAILQ_INIT(&dev->ipmi_completed_requests); 169 dev->ipmi_address = IPMI_BMC_SLAVE_ADDR; 170 dev->ipmi_lun = IPMI_BMC_SMS_LUN; 171 *devp = makedevice(getmajor(*devp), minor); 172 dev->ipmi_dev = *devp; 173 cv_init(&dev->ipmi_cv, NULL, CV_DEFAULT, NULL); 174 175 list_insert_head(&dev_list, dev); 176 177 return (0); 178 } 179 180 /*ARGSUSED*/ 181 static int 182 ipmi_close(dev_t dev, int flag, int otyp, cred_t *cred) 183 { 184 ipmi_device_t *dp; 185 struct ipmi_request *req, *next; 186 187 if ((dp = lookup_ipmidev_by_dev(dev)) == NULL) 188 return (ENODEV); 189 190 IPMI_LOCK(sc); 191 /* remove any pending requests */ 192 req = TAILQ_FIRST(&sc->ipmi_pending_requests); 193 while (req != NULL) { 194 next = TAILQ_NEXT(req, ir_link); 195 196 if (req->ir_owner == dp) { 197 TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link); 198 ipmi_free_request(req); 199 } 200 req = next; 201 } 202 203 dp->ipmi_status |= IPMI_CLOSING; 204 while (dp->ipmi_status & IPMI_BUSY) 205 cv_wait(&dp->ipmi_cv, &sc->ipmi_lock); 206 IPMI_UNLOCK(sc); 207 208 /* remove any requests in queue of stuff completed */ 209 while ((req = TAILQ_FIRST(&dp->ipmi_completed_requests)) != NULL) { 210 TAILQ_REMOVE(&dp->ipmi_completed_requests, req, ir_link); 211 ipmi_free_request(req); 212 } 213 214 list_remove(&dev_list, dp); 215 id_free(minor_ids, getminor(dev)); 216 cv_destroy(&dp->ipmi_cv); 217 kmem_free(dp->ipmi_pollhead, sizeof (pollhead_t)); 218 kmem_free(dp, sizeof (ipmi_device_t)); 219 220 return (0); 221 } 222 223 /*ARGSUSED*/ 224 static int 225 ipmi_ioctl(dev_t dv, int cmd, intptr_t data, int flags, cred_t *cr, int *rvalp) 226 { 227 struct ipmi_device *dev; 228 struct ipmi_request *kreq; 229 struct ipmi_req req; 230 struct ipmi_recv recv; 231 struct ipmi_recv32 recv32; 232 struct ipmi_addr addr; 233 int error, len; 234 model_t model; 235 int orig_cmd = 0; 236 uchar_t t_lun; 237 238 if (secpolicy_sys_config(cr, B_FALSE) != 0) 239 return (EPERM); 240 241 if ((dev = lookup_ipmidev_by_dev(dv)) == NULL) 242 return (ENODEV); 243 244 model = get_udatamodel(); 245 if (model == DATAMODEL_NATIVE) { 246 switch (cmd) { 247 case IPMICTL_SEND_COMMAND: 248 if (copyin((void *)data, &req, sizeof (req))) 249 return (EFAULT); 250 break; 251 case IPMICTL_RECEIVE_MSG_TRUNC: 252 case IPMICTL_RECEIVE_MSG: 253 if (copyin((void *)data, &recv, sizeof (recv))) 254 return (EFAULT); 255 break; 256 } 257 } else { 258 /* Convert 32-bit structures to native. */ 259 struct ipmi_req32 req32; 260 261 switch (cmd) { 262 case IPMICTL_SEND_COMMAND_32: 263 if (copyin((void *)data, &req32, sizeof (req32))) 264 return (EFAULT); 265 266 req.addr = PTRIN(req32.addr); 267 req.addr_len = req32.addr_len; 268 req.msgid = req32.msgid; 269 req.msg.netfn = req32.msg.netfn; 270 req.msg.cmd = req32.msg.cmd; 271 req.msg.data_len = req32.msg.data_len; 272 req.msg.data = PTRIN(req32.msg.data); 273 274 cmd = IPMICTL_SEND_COMMAND; 275 break; 276 277 case IPMICTL_RECEIVE_MSG_TRUNC_32: 278 case IPMICTL_RECEIVE_MSG_32: 279 if (copyin((void *)data, &recv32, sizeof (recv32))) 280 return (EFAULT); 281 282 recv.addr = PTRIN(recv32.addr); 283 recv.addr_len = recv32.addr_len; 284 recv.msg.data_len = recv32.msg.data_len; 285 recv.msg.data = PTRIN(recv32.msg.data); 286 287 orig_cmd = cmd; 288 cmd = (cmd == IPMICTL_RECEIVE_MSG_TRUNC_32) ? 289 IPMICTL_RECEIVE_MSG_TRUNC : IPMICTL_RECEIVE_MSG; 290 break; 291 } 292 } 293 294 switch (cmd) { 295 case IPMICTL_SEND_COMMAND: 296 /* Check that we didn't get a ridiculous length */ 297 if (req.msg.data_len > IPMI_MAX_RX) 298 return (EINVAL); 299 300 kreq = ipmi_alloc_request(dev, req.msgid, 301 IPMI_ADDR(req.msg.netfn, 0), req.msg.cmd, 302 req.msg.data_len, IPMI_MAX_RX); 303 /* This struct is the same for 32/64 */ 304 if (req.msg.data_len > 0 && 305 copyin(req.msg.data, kreq->ir_request, req.msg.data_len)) { 306 ipmi_free_request(kreq); 307 return (EFAULT); 308 } 309 IPMI_LOCK(sc); 310 dev->ipmi_requests++; 311 error = sc->ipmi_enqueue_request(sc, kreq); 312 IPMI_UNLOCK(sc); 313 if (error) 314 return (error); 315 break; 316 317 case IPMICTL_RECEIVE_MSG_TRUNC: 318 case IPMICTL_RECEIVE_MSG: 319 /* This struct is the same for 32/64 */ 320 if (copyin(recv.addr, &addr, sizeof (addr))) 321 return (EFAULT); 322 323 IPMI_LOCK(sc); 324 kreq = TAILQ_FIRST(&dev->ipmi_completed_requests); 325 if (kreq == NULL) { 326 IPMI_UNLOCK(sc); 327 return (EAGAIN); 328 } 329 addr.channel = IPMI_BMC_CHANNEL; 330 recv.recv_type = IPMI_RESPONSE_RECV_TYPE; 331 recv.msgid = kreq->ir_msgid; 332 recv.msg.netfn = IPMI_REPLY_ADDR(kreq->ir_addr) >> 2; 333 recv.msg.cmd = kreq->ir_command; 334 error = kreq->ir_error; 335 if (error) { 336 TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, 337 ir_link); 338 dev->ipmi_requests--; 339 IPMI_UNLOCK(sc); 340 ipmi_free_request(kreq); 341 return (error); 342 } 343 len = kreq->ir_replylen + 1; 344 if (recv.msg.data_len < len && cmd == IPMICTL_RECEIVE_MSG) { 345 IPMI_UNLOCK(sc); 346 return (EMSGSIZE); 347 } 348 TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, ir_link); 349 dev->ipmi_requests--; 350 IPMI_UNLOCK(sc); 351 len = min(recv.msg.data_len, len); 352 recv.msg.data_len = (unsigned short)len; 353 354 if (orig_cmd == IPMICTL_RECEIVE_MSG_TRUNC_32 || 355 orig_cmd == IPMICTL_RECEIVE_MSG_32) { 356 /* Update changed fields in 32-bit structure. */ 357 recv32.recv_type = recv.recv_type; 358 recv32.msgid = (int32_t)recv.msgid; 359 recv32.msg.netfn = recv.msg.netfn; 360 recv32.msg.cmd = recv.msg.cmd; 361 recv32.msg.data_len = recv.msg.data_len; 362 363 error = copyout(&recv32, (void *)data, sizeof (recv32)); 364 } else { 365 error = copyout(&recv, (void *)data, sizeof (recv)); 366 } 367 368 /* This struct is the same for 32/64 */ 369 if (error == 0) 370 error = copyout(&addr, recv.addr, sizeof (addr)); 371 if (error == 0) 372 error = copyout(&kreq->ir_compcode, recv.msg.data, 1); 373 if (error == 0) 374 error = copyout(kreq->ir_reply, recv.msg.data + 1, 375 len - 1); 376 ipmi_free_request(kreq); 377 378 if (error) 379 return (EFAULT); 380 381 break; 382 383 case IPMICTL_SET_MY_ADDRESS_CMD: 384 IPMI_LOCK(sc); 385 if (copyin((void *)data, &dev->ipmi_address, 386 sizeof (dev->ipmi_address))) { 387 IPMI_UNLOCK(sc); 388 return (EFAULT); 389 } 390 IPMI_UNLOCK(sc); 391 break; 392 393 case IPMICTL_GET_MY_ADDRESS_CMD: 394 IPMI_LOCK(sc); 395 if (copyout(&dev->ipmi_address, (void *)data, 396 sizeof (dev->ipmi_address))) { 397 IPMI_UNLOCK(sc); 398 return (EFAULT); 399 } 400 IPMI_UNLOCK(sc); 401 break; 402 403 case IPMICTL_SET_MY_LUN_CMD: 404 IPMI_LOCK(sc); 405 if (copyin((void *)data, &t_lun, sizeof (t_lun))) { 406 IPMI_UNLOCK(sc); 407 return (EFAULT); 408 } 409 dev->ipmi_lun = t_lun & 0x3; 410 IPMI_UNLOCK(sc); 411 break; 412 413 case IPMICTL_GET_MY_LUN_CMD: 414 IPMI_LOCK(sc); 415 if (copyout(&dev->ipmi_lun, (void *)data, 416 sizeof (dev->ipmi_lun))) { 417 IPMI_UNLOCK(sc); 418 return (EFAULT); 419 } 420 IPMI_UNLOCK(sc); 421 break; 422 423 case IPMICTL_SET_GETS_EVENTS_CMD: 424 break; 425 426 case IPMICTL_REGISTER_FOR_CMD: 427 case IPMICTL_UNREGISTER_FOR_CMD: 428 return (EINVAL); 429 430 default: 431 return (EINVAL); 432 } 433 434 return (0); 435 } 436 437 static int 438 ipmi_poll(dev_t dv, short events, int anyyet, short *reventsp, 439 pollhead_t **phpp) 440 { 441 struct ipmi_device *dev; 442 short revent = 0; 443 444 if ((dev = lookup_ipmidev_by_dev(dv)) == NULL) 445 return (ENODEV); 446 447 if (events & (POLLIN | POLLRDNORM)) { 448 if (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) 449 revent |= events & (POLLIN | POLLRDNORM); 450 if (dev->ipmi_requests == 0) 451 revent |= POLLERR; 452 } 453 454 if (revent == 0) { 455 /* nothing has occurred */ 456 if (!anyyet) 457 *phpp = dev->ipmi_pollhead; 458 } 459 460 *reventsp = revent; 461 return (0); 462 } 463 464 /*ARGSUSED*/ 465 static int 466 ipmi_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp) 467 { 468 switch (cmd) { 469 case DDI_INFO_DEVT2DEVINFO: 470 *resultp = ipmi_dip; 471 return (DDI_SUCCESS); 472 case DDI_INFO_DEVT2INSTANCE: 473 *resultp = NULL; 474 return (DDI_SUCCESS); 475 } 476 return (DDI_FAILURE); 477 } 478 479 static void 480 ipmi_cleanup(dev_info_t *dip) 481 { 482 /* poke the taskq so that it can terminate */ 483 IPMI_LOCK(sc); 484 sc->ipmi_detaching = 1; 485 cv_signal(&sc->ipmi_request_added); 486 IPMI_UNLOCK(sc); 487 488 ipmi_shutdown(sc); 489 ddi_remove_minor_node(dip, NULL); 490 ipmi_dip = NULL; 491 492 list_destroy(&dev_list); 493 id_space_destroy(minor_ids); 494 495 sc->ipmi_detaching = 0; 496 } 497 498 static int 499 ipmi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 500 { 501 if (cmd != DDI_ATTACH) 502 return (DDI_FAILURE); 503 504 /* this driver only supports one device instance */ 505 if (ddi_get_instance(dip) != 0) { 506 cmn_err(CE_WARN, 507 "!not attaching to non-zero device instance %d", 508 ddi_get_instance(dip)); 509 return (DDI_FAILURE); 510 } 511 512 if (get_smbios_ipmi_info() == DDI_FAILURE) 513 return (DDI_FAILURE); 514 515 /* 516 * Support for the other types (SMIC, SSIF) should be added here. 517 */ 518 switch (sc->ipmi_io_type) { 519 case SMB_IPMI_T_KCS: 520 if (ipmi_kcs_attach(sc) != 0) 521 return (DDI_FAILURE); 522 break; 523 default: 524 return (DDI_FAILURE); 525 } 526 ipmi_found = B_TRUE; 527 528 if (ddi_create_minor_node(dip, "ipmi", S_IFCHR, 0, DDI_PSEUDO, 529 0) == DDI_FAILURE) { 530 cmn_err(CE_WARN, "!attach could not create minor node"); 531 ddi_remove_minor_node(dip, NULL); 532 return (DDI_FAILURE); 533 } 534 535 ipmi_dip = dip; 536 537 list_create(&dev_list, sizeof (ipmi_device_t), 538 offsetof(ipmi_device_t, ipmi_node)); 539 540 /* Create ID space for open devs. ID 0 is reserved. */ 541 minor_ids = id_space_create("ipmi_id_space", 1, 128); 542 543 if (ipmi_startup(sc) != B_TRUE) { 544 ipmi_cleanup(dip); 545 return (DDI_FAILURE); 546 } 547 548 ipmi_attached = B_TRUE; 549 550 return (DDI_SUCCESS); 551 } 552 553 static int 554 ipmi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 555 { 556 if (cmd != DDI_DETACH) 557 return (DDI_FAILURE); 558 559 if (ipmi_found == B_FALSE) 560 return (DDI_SUCCESS); 561 562 if (!list_is_empty(&dev_list)) 563 return (DDI_FAILURE); 564 565 ipmi_cleanup(dip); 566 567 ipmi_attached = B_FALSE; 568 return (DDI_SUCCESS); 569 } 570 571 static struct cb_ops ipmi_cb_ops = { 572 ipmi_open, 573 ipmi_close, 574 nodev, /* strategy */ 575 nodev, /* print */ 576 nodev, /* dump */ 577 nodev, /* read */ 578 nodev, /* write */ 579 ipmi_ioctl, 580 nodev, /* devmap */ 581 nodev, /* mmap */ 582 nodev, /* segmap */ 583 ipmi_poll, 584 ddi_prop_op, 585 NULL, /* streamtab */ 586 D_NEW | D_MP, /* flags */ 587 CB_REV, 588 nodev, /* awread */ 589 nodev /* awrite */ 590 }; 591 592 static struct dev_ops ipmi_ops = { 593 DEVO_REV, 594 0, /* reference count */ 595 ipmi_info, 596 nulldev, /* identify */ 597 nulldev, /* probe */ 598 ipmi_attach, 599 ipmi_detach, 600 nodev, /* reset */ 601 &ipmi_cb_ops, 602 NULL, /* bus ops */ 603 NULL, /* power */ 604 ddi_quiesce_not_needed, 605 }; 606 607 static struct modldrv md = { 608 &mod_driverops, "ipmi driver", &ipmi_ops 609 }; 610 611 static struct modlinkage ml = { 612 MODREV_1, &md, NULL 613 }; 614 615 int 616 _init(void) 617 { 618 return (mod_install(&ml)); 619 } 620 621 int 622 _fini(void) 623 { 624 return (mod_remove(&ml)); 625 } 626 627 int 628 _info(struct modinfo *mip) 629 { 630 return (mod_info(&ml, mip)); 631 } 632