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