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