1989f2807SJerry Jelinek /* 2989f2807SJerry Jelinek * CDDL HEADER START 3989f2807SJerry Jelinek * 4989f2807SJerry Jelinek * The contents of this file are subject to the terms of the 5989f2807SJerry Jelinek * Common Development and Distribution License (the "License"). 6989f2807SJerry Jelinek * You may not use this file except in compliance with the License. 7989f2807SJerry Jelinek * 8989f2807SJerry Jelinek * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9989f2807SJerry Jelinek * or http://www.opensolaris.org/os/licensing. 10989f2807SJerry Jelinek * See the License for the specific language governing permissions 11989f2807SJerry Jelinek * and limitations under the License. 12989f2807SJerry Jelinek * 13989f2807SJerry Jelinek * When distributing Covered Code, include this CDDL HEADER in each 14989f2807SJerry Jelinek * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15989f2807SJerry Jelinek * If applicable, add the following below this CDDL HEADER, with the 16989f2807SJerry Jelinek * fields enclosed by brackets "[]" replaced with your own identifying 17989f2807SJerry Jelinek * information: Portions Copyright [yyyy] [name of copyright owner] 18989f2807SJerry Jelinek * 19989f2807SJerry Jelinek * CDDL HEADER END 20989f2807SJerry Jelinek */ 21989f2807SJerry Jelinek 22989f2807SJerry Jelinek /* 23*866e9cd0SJason King * Copyright 2019 Joyent, Inc. 241e393477SMarcel Telka * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 25989f2807SJerry Jelinek */ 26989f2807SJerry Jelinek 27989f2807SJerry Jelinek /* 28989f2807SJerry Jelinek * The ipmi driver is an openipmi compatible IPMI driver based on the FreeBSD 29989f2807SJerry Jelinek * driver. 30989f2807SJerry Jelinek * 31989f2807SJerry Jelinek * The current implementation has several limitations: 32989f2807SJerry Jelinek * 1) It only does discovery through the SMBIOS. The FreeBSD driver has 33989f2807SJerry Jelinek * several additional ways to discover the IPMI device (acpi, bus checking, 34989f2807SJerry Jelinek * etc.). This support could be ported if necessary. 35989f2807SJerry Jelinek * 2) The driver currently only supports the IPMI KCS_MODE mode (reported 36989f2807SJerry Jelinek * through the SMBIOS as SMBIOS SMB_IPMI_T_KCS). Support for the other modes 37989f2807SJerry Jelinek * (BT_MODE, SMIC_MODE, SSIF_MODE) could be ported if necessary. 38989f2807SJerry Jelinek * 3) The driver does not currently set up an IPMI watchdog. This also could 39989f2807SJerry Jelinek * be ported if necessary. 40989f2807SJerry Jelinek */ 41989f2807SJerry Jelinek 42989f2807SJerry Jelinek #include <sys/devops.h> 43989f2807SJerry Jelinek #include <sys/conf.h> 44989f2807SJerry Jelinek #include <sys/modctl.h> 45989f2807SJerry Jelinek #include <sys/types.h> 46989f2807SJerry Jelinek #include <sys/file.h> 47989f2807SJerry Jelinek #include <sys/errno.h> 48989f2807SJerry Jelinek #include <sys/open.h> 49989f2807SJerry Jelinek #include <sys/cred.h> 50989f2807SJerry Jelinek #include <sys/uio.h> 51989f2807SJerry Jelinek #include <sys/stat.h> 52989f2807SJerry Jelinek #include <sys/cmn_err.h> 53989f2807SJerry Jelinek #include <sys/ddi.h> 54989f2807SJerry Jelinek #include <sys/sunddi.h> 55989f2807SJerry Jelinek #include <sys/smbios.h> 56989f2807SJerry Jelinek #include <sys/smbios_impl.h> 57989f2807SJerry Jelinek #include <sys/policy.h> 58989f2807SJerry Jelinek #include <sys/ipmi.h> 59989f2807SJerry Jelinek #include "ipmivars.h" 60989f2807SJerry Jelinek 61989f2807SJerry Jelinek static dev_info_t *ipmi_dip; 62989f2807SJerry Jelinek static boolean_t ipmi_attached = B_FALSE; 63989f2807SJerry Jelinek static boolean_t ipmi_found = B_FALSE; 64989f2807SJerry Jelinek static struct ipmi_softc softc; 65989f2807SJerry Jelinek static struct ipmi_softc *sc = &softc; 66989f2807SJerry Jelinek static list_t dev_list; 67989f2807SJerry Jelinek static id_space_t *minor_ids; 68164b0ee5SRobert Mustacchi static kmutex_t dev_list_lock; 69989f2807SJerry Jelinek 70989f2807SJerry Jelinek #define PTRIN(p) ((void *)(uintptr_t)(p)) 71989f2807SJerry Jelinek #define PTROUT(p) ((uintptr_t)(p)) 72989f2807SJerry Jelinek 73989f2807SJerry Jelinek /* 74989f2807SJerry Jelinek * Use the SMBIOS info to determine if the system has an IPMI. 75989f2807SJerry Jelinek */ 76989f2807SJerry Jelinek static int 77989f2807SJerry Jelinek get_smbios_ipmi_info(void) 78989f2807SJerry Jelinek { 79989f2807SJerry Jelinek smbios_ipmi_t ipmi; 80989f2807SJerry Jelinek 81989f2807SJerry Jelinek if (ksmbios == NULL || smbios_info_ipmi(ksmbios, &ipmi) == SMB_ERR) 82989f2807SJerry Jelinek return (DDI_FAILURE); 83989f2807SJerry Jelinek 84989f2807SJerry Jelinek cmn_err(CE_CONT, "!SMBIOS type 0x%x, addr 0x%llx", ipmi.smbip_type, 85989f2807SJerry Jelinek (long long unsigned int)(ipmi.smbip_addr)); 86989f2807SJerry Jelinek 87989f2807SJerry Jelinek /* 88989f2807SJerry Jelinek * Some systems have a bios that will report an IPMI device even when 89989f2807SJerry Jelinek * it is not installed. In this case we see 0x0 as the base address. 90989f2807SJerry Jelinek * If we see this address, assume the device is not really present. 91989f2807SJerry Jelinek */ 92989f2807SJerry Jelinek if (ipmi.smbip_addr == NULL) { 93989f2807SJerry Jelinek cmn_err(CE_WARN, "!SMBIOS: Invalid base address"); 94989f2807SJerry Jelinek return (DDI_FAILURE); 95989f2807SJerry Jelinek } 96989f2807SJerry Jelinek 97989f2807SJerry Jelinek sc->ipmi_io_type = ipmi.smbip_type; 98989f2807SJerry Jelinek switch (ipmi.smbip_type) { 99989f2807SJerry Jelinek case SMB_IPMI_T_KCS: 100989f2807SJerry Jelinek case SMB_IPMI_T_SMIC: 101989f2807SJerry Jelinek sc->ipmi_io_address = ipmi.smbip_addr; 102989f2807SJerry Jelinek sc->ipmi_io_mode = (ipmi.smbip_flags & SMB_IPMI_F_IOADDR) ? 103989f2807SJerry Jelinek 1 : 0; 104989f2807SJerry Jelinek sc->ipmi_io_spacing = ipmi.smbip_regspacing; 105989f2807SJerry Jelinek break; 106989f2807SJerry Jelinek case SMB_IPMI_T_SSIF: 107989f2807SJerry Jelinek if ((ipmi.smbip_addr & 0xffffffffffffff00) != 0) { 108989f2807SJerry Jelinek cmn_err(CE_WARN, "!SMBIOS: Invalid SSIF SMBus address, " 109989f2807SJerry Jelinek "using BMC I2C slave address instead"); 110989f2807SJerry Jelinek sc->ipmi_io_address = ipmi.smbip_i2c; 111989f2807SJerry Jelinek } else { 112989f2807SJerry Jelinek sc->ipmi_io_address = ipmi.smbip_addr; 113989f2807SJerry Jelinek } 114989f2807SJerry Jelinek break; 115989f2807SJerry Jelinek default: 116989f2807SJerry Jelinek return (DDI_FAILURE); 117989f2807SJerry Jelinek } 118989f2807SJerry Jelinek 119989f2807SJerry Jelinek if (ipmi.smbip_intr > 15) { 120989f2807SJerry Jelinek cmn_err(CE_WARN, "!SMBIOS: Non-ISA IRQ %d for IPMI", 121989f2807SJerry Jelinek ipmi.smbip_intr); 122989f2807SJerry Jelinek return (DDI_FAILURE); 123989f2807SJerry Jelinek } 124989f2807SJerry Jelinek 125989f2807SJerry Jelinek sc->ipmi_io_irq = ipmi.smbip_intr; 126989f2807SJerry Jelinek return (DDI_SUCCESS); 127989f2807SJerry Jelinek } 128989f2807SJerry Jelinek 129989f2807SJerry Jelinek static ipmi_device_t * 130989f2807SJerry Jelinek lookup_ipmidev_by_dev(dev_t dev) 131989f2807SJerry Jelinek { 132989f2807SJerry Jelinek ipmi_device_t *p; 133989f2807SJerry Jelinek 134164b0ee5SRobert Mustacchi mutex_enter(&dev_list_lock); 135989f2807SJerry Jelinek for (p = list_head(&dev_list); p; p = list_next(&dev_list, p)) { 136164b0ee5SRobert Mustacchi if (dev == p->ipmi_dev) { 137164b0ee5SRobert Mustacchi mutex_exit(&dev_list_lock); 138989f2807SJerry Jelinek return (p); 139989f2807SJerry Jelinek } 140164b0ee5SRobert Mustacchi } 141164b0ee5SRobert Mustacchi mutex_exit(&dev_list_lock); 142989f2807SJerry Jelinek return (NULL); 143989f2807SJerry Jelinek } 144989f2807SJerry Jelinek 145989f2807SJerry Jelinek /* 146989f2807SJerry Jelinek * Each open returns a new pseudo device. 147989f2807SJerry Jelinek */ 148989f2807SJerry Jelinek /*ARGSUSED*/ 149989f2807SJerry Jelinek static int 150989f2807SJerry Jelinek ipmi_open(dev_t *devp, int flag, int otyp, cred_t *cred) 151989f2807SJerry Jelinek { 152989f2807SJerry Jelinek minor_t minor; 153989f2807SJerry Jelinek ipmi_device_t *dev; 154*866e9cd0SJason King id_t mid; 155989f2807SJerry Jelinek 156989f2807SJerry Jelinek if (ipmi_attached == B_FALSE) 157989f2807SJerry Jelinek return (ENXIO); 158989f2807SJerry Jelinek 159989f2807SJerry Jelinek if (ipmi_found == B_FALSE) 160989f2807SJerry Jelinek return (ENODEV); 161989f2807SJerry Jelinek 162989f2807SJerry Jelinek /* exclusive opens are not supported */ 163989f2807SJerry Jelinek if (flag & FEXCL) 164989f2807SJerry Jelinek return (ENOTSUP); 165989f2807SJerry Jelinek 166*866e9cd0SJason King if ((mid = id_alloc_nosleep(minor_ids)) == -1) 167989f2807SJerry Jelinek return (ENODEV); 168*866e9cd0SJason King minor = (minor_t)mid; 169989f2807SJerry Jelinek 170989f2807SJerry Jelinek /* Initialize the per file descriptor data. */ 171989f2807SJerry Jelinek dev = kmem_zalloc(sizeof (ipmi_device_t), KM_SLEEP); 172989f2807SJerry Jelinek 173989f2807SJerry Jelinek dev->ipmi_pollhead = kmem_zalloc(sizeof (pollhead_t), KM_SLEEP); 174989f2807SJerry Jelinek 175989f2807SJerry Jelinek TAILQ_INIT(&dev->ipmi_completed_requests); 176989f2807SJerry Jelinek dev->ipmi_address = IPMI_BMC_SLAVE_ADDR; 177989f2807SJerry Jelinek dev->ipmi_lun = IPMI_BMC_SMS_LUN; 178989f2807SJerry Jelinek *devp = makedevice(getmajor(*devp), minor); 179989f2807SJerry Jelinek dev->ipmi_dev = *devp; 180cc944374SMarcel Telka cv_init(&dev->ipmi_cv, NULL, CV_DEFAULT, NULL); 181989f2807SJerry Jelinek 182164b0ee5SRobert Mustacchi mutex_enter(&dev_list_lock); 183989f2807SJerry Jelinek list_insert_head(&dev_list, dev); 184164b0ee5SRobert Mustacchi mutex_exit(&dev_list_lock); 185989f2807SJerry Jelinek 186989f2807SJerry Jelinek return (0); 187989f2807SJerry Jelinek } 188989f2807SJerry Jelinek 189989f2807SJerry Jelinek /*ARGSUSED*/ 190989f2807SJerry Jelinek static int 191989f2807SJerry Jelinek ipmi_close(dev_t dev, int flag, int otyp, cred_t *cred) 192989f2807SJerry Jelinek { 193989f2807SJerry Jelinek ipmi_device_t *dp; 194989f2807SJerry Jelinek struct ipmi_request *req, *next; 195989f2807SJerry Jelinek 196989f2807SJerry Jelinek if ((dp = lookup_ipmidev_by_dev(dev)) == NULL) 197989f2807SJerry Jelinek return (ENODEV); 198989f2807SJerry Jelinek 199989f2807SJerry Jelinek IPMI_LOCK(sc); 200989f2807SJerry Jelinek /* remove any pending requests */ 201989f2807SJerry Jelinek req = TAILQ_FIRST(&sc->ipmi_pending_requests); 202989f2807SJerry Jelinek while (req != NULL) { 203989f2807SJerry Jelinek next = TAILQ_NEXT(req, ir_link); 204989f2807SJerry Jelinek 205989f2807SJerry Jelinek if (req->ir_owner == dp) { 206989f2807SJerry Jelinek TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link); 207989f2807SJerry Jelinek ipmi_free_request(req); 208989f2807SJerry Jelinek } 209989f2807SJerry Jelinek req = next; 210989f2807SJerry Jelinek } 211cc944374SMarcel Telka 212cc944374SMarcel Telka dp->ipmi_status |= IPMI_CLOSING; 213cc944374SMarcel Telka while (dp->ipmi_status & IPMI_BUSY) 214cc944374SMarcel Telka cv_wait(&dp->ipmi_cv, &sc->ipmi_lock); 215989f2807SJerry Jelinek IPMI_UNLOCK(sc); 216989f2807SJerry Jelinek 217989f2807SJerry Jelinek /* remove any requests in queue of stuff completed */ 218989f2807SJerry Jelinek while ((req = TAILQ_FIRST(&dp->ipmi_completed_requests)) != NULL) { 219989f2807SJerry Jelinek TAILQ_REMOVE(&dp->ipmi_completed_requests, req, ir_link); 220989f2807SJerry Jelinek ipmi_free_request(req); 221989f2807SJerry Jelinek } 222989f2807SJerry Jelinek 223164b0ee5SRobert Mustacchi mutex_enter(&dev_list_lock); 224989f2807SJerry Jelinek list_remove(&dev_list, dp); 225164b0ee5SRobert Mustacchi mutex_exit(&dev_list_lock); 226989f2807SJerry Jelinek id_free(minor_ids, getminor(dev)); 227cc944374SMarcel Telka cv_destroy(&dp->ipmi_cv); 228989f2807SJerry Jelinek kmem_free(dp->ipmi_pollhead, sizeof (pollhead_t)); 229989f2807SJerry Jelinek kmem_free(dp, sizeof (ipmi_device_t)); 230989f2807SJerry Jelinek 231989f2807SJerry Jelinek return (0); 232989f2807SJerry Jelinek } 233989f2807SJerry Jelinek 234989f2807SJerry Jelinek /*ARGSUSED*/ 235989f2807SJerry Jelinek static int 236989f2807SJerry Jelinek ipmi_ioctl(dev_t dv, int cmd, intptr_t data, int flags, cred_t *cr, int *rvalp) 237989f2807SJerry Jelinek { 238989f2807SJerry Jelinek struct ipmi_device *dev; 239989f2807SJerry Jelinek struct ipmi_request *kreq; 240989f2807SJerry Jelinek struct ipmi_req req; 241989f2807SJerry Jelinek struct ipmi_recv recv; 242989f2807SJerry Jelinek struct ipmi_recv32 recv32; 243989f2807SJerry Jelinek struct ipmi_addr addr; 244989f2807SJerry Jelinek int error, len; 245989f2807SJerry Jelinek model_t model; 246989f2807SJerry Jelinek int orig_cmd = 0; 247989f2807SJerry Jelinek uchar_t t_lun; 248989f2807SJerry Jelinek 249989f2807SJerry Jelinek if (secpolicy_sys_config(cr, B_FALSE) != 0) 250989f2807SJerry Jelinek return (EPERM); 251989f2807SJerry Jelinek 252989f2807SJerry Jelinek if ((dev = lookup_ipmidev_by_dev(dv)) == NULL) 253989f2807SJerry Jelinek return (ENODEV); 254989f2807SJerry Jelinek 255989f2807SJerry Jelinek model = get_udatamodel(); 256989f2807SJerry Jelinek if (model == DATAMODEL_NATIVE) { 257989f2807SJerry Jelinek switch (cmd) { 258989f2807SJerry Jelinek case IPMICTL_SEND_COMMAND: 259989f2807SJerry Jelinek if (copyin((void *)data, &req, sizeof (req))) 260989f2807SJerry Jelinek return (EFAULT); 261989f2807SJerry Jelinek break; 262989f2807SJerry Jelinek case IPMICTL_RECEIVE_MSG_TRUNC: 263989f2807SJerry Jelinek case IPMICTL_RECEIVE_MSG: 264989f2807SJerry Jelinek if (copyin((void *)data, &recv, sizeof (recv))) 265989f2807SJerry Jelinek return (EFAULT); 266989f2807SJerry Jelinek break; 267989f2807SJerry Jelinek } 268989f2807SJerry Jelinek } else { 269989f2807SJerry Jelinek /* Convert 32-bit structures to native. */ 270989f2807SJerry Jelinek struct ipmi_req32 req32; 271989f2807SJerry Jelinek 272989f2807SJerry Jelinek switch (cmd) { 273989f2807SJerry Jelinek case IPMICTL_SEND_COMMAND_32: 274989f2807SJerry Jelinek if (copyin((void *)data, &req32, sizeof (req32))) 275989f2807SJerry Jelinek return (EFAULT); 276989f2807SJerry Jelinek 277989f2807SJerry Jelinek req.addr = PTRIN(req32.addr); 278989f2807SJerry Jelinek req.addr_len = req32.addr_len; 279989f2807SJerry Jelinek req.msgid = req32.msgid; 280989f2807SJerry Jelinek req.msg.netfn = req32.msg.netfn; 281989f2807SJerry Jelinek req.msg.cmd = req32.msg.cmd; 282989f2807SJerry Jelinek req.msg.data_len = req32.msg.data_len; 283989f2807SJerry Jelinek req.msg.data = PTRIN(req32.msg.data); 284989f2807SJerry Jelinek 285989f2807SJerry Jelinek cmd = IPMICTL_SEND_COMMAND; 286989f2807SJerry Jelinek break; 287989f2807SJerry Jelinek 288989f2807SJerry Jelinek case IPMICTL_RECEIVE_MSG_TRUNC_32: 289989f2807SJerry Jelinek case IPMICTL_RECEIVE_MSG_32: 290989f2807SJerry Jelinek if (copyin((void *)data, &recv32, sizeof (recv32))) 291989f2807SJerry Jelinek return (EFAULT); 292989f2807SJerry Jelinek 293989f2807SJerry Jelinek recv.addr = PTRIN(recv32.addr); 294989f2807SJerry Jelinek recv.addr_len = recv32.addr_len; 295989f2807SJerry Jelinek recv.msg.data_len = recv32.msg.data_len; 296989f2807SJerry Jelinek recv.msg.data = PTRIN(recv32.msg.data); 297989f2807SJerry Jelinek 298989f2807SJerry Jelinek orig_cmd = cmd; 299989f2807SJerry Jelinek cmd = (cmd == IPMICTL_RECEIVE_MSG_TRUNC_32) ? 300989f2807SJerry Jelinek IPMICTL_RECEIVE_MSG_TRUNC : IPMICTL_RECEIVE_MSG; 301989f2807SJerry Jelinek break; 302989f2807SJerry Jelinek } 303989f2807SJerry Jelinek } 304989f2807SJerry Jelinek 305989f2807SJerry Jelinek switch (cmd) { 306989f2807SJerry Jelinek case IPMICTL_SEND_COMMAND: 307989f2807SJerry Jelinek /* Check that we didn't get a ridiculous length */ 308989f2807SJerry Jelinek if (req.msg.data_len > IPMI_MAX_RX) 309989f2807SJerry Jelinek return (EINVAL); 310989f2807SJerry Jelinek 311989f2807SJerry Jelinek kreq = ipmi_alloc_request(dev, req.msgid, 312989f2807SJerry Jelinek IPMI_ADDR(req.msg.netfn, 0), req.msg.cmd, 313989f2807SJerry Jelinek req.msg.data_len, IPMI_MAX_RX); 314989f2807SJerry Jelinek /* This struct is the same for 32/64 */ 315989f2807SJerry Jelinek if (req.msg.data_len > 0 && 316989f2807SJerry Jelinek copyin(req.msg.data, kreq->ir_request, req.msg.data_len)) { 317989f2807SJerry Jelinek ipmi_free_request(kreq); 318989f2807SJerry Jelinek return (EFAULT); 319989f2807SJerry Jelinek } 320989f2807SJerry Jelinek IPMI_LOCK(sc); 321989f2807SJerry Jelinek dev->ipmi_requests++; 322989f2807SJerry Jelinek error = sc->ipmi_enqueue_request(sc, kreq); 323989f2807SJerry Jelinek IPMI_UNLOCK(sc); 324989f2807SJerry Jelinek if (error) 325989f2807SJerry Jelinek return (error); 326989f2807SJerry Jelinek break; 327989f2807SJerry Jelinek 328989f2807SJerry Jelinek case IPMICTL_RECEIVE_MSG_TRUNC: 329989f2807SJerry Jelinek case IPMICTL_RECEIVE_MSG: 330989f2807SJerry Jelinek /* This struct is the same for 32/64 */ 331989f2807SJerry Jelinek if (copyin(recv.addr, &addr, sizeof (addr))) 332989f2807SJerry Jelinek return (EFAULT); 333989f2807SJerry Jelinek 334989f2807SJerry Jelinek IPMI_LOCK(sc); 335989f2807SJerry Jelinek kreq = TAILQ_FIRST(&dev->ipmi_completed_requests); 336989f2807SJerry Jelinek if (kreq == NULL) { 337989f2807SJerry Jelinek IPMI_UNLOCK(sc); 338989f2807SJerry Jelinek return (EAGAIN); 339989f2807SJerry Jelinek } 340989f2807SJerry Jelinek addr.channel = IPMI_BMC_CHANNEL; 341989f2807SJerry Jelinek recv.recv_type = IPMI_RESPONSE_RECV_TYPE; 342989f2807SJerry Jelinek recv.msgid = kreq->ir_msgid; 343989f2807SJerry Jelinek recv.msg.netfn = IPMI_REPLY_ADDR(kreq->ir_addr) >> 2; 344989f2807SJerry Jelinek recv.msg.cmd = kreq->ir_command; 345989f2807SJerry Jelinek error = kreq->ir_error; 346989f2807SJerry Jelinek if (error) { 347989f2807SJerry Jelinek TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, 348989f2807SJerry Jelinek ir_link); 349989f2807SJerry Jelinek dev->ipmi_requests--; 350989f2807SJerry Jelinek IPMI_UNLOCK(sc); 351989f2807SJerry Jelinek ipmi_free_request(kreq); 352989f2807SJerry Jelinek return (error); 353989f2807SJerry Jelinek } 354989f2807SJerry Jelinek len = kreq->ir_replylen + 1; 355989f2807SJerry Jelinek if (recv.msg.data_len < len && cmd == IPMICTL_RECEIVE_MSG) { 356989f2807SJerry Jelinek IPMI_UNLOCK(sc); 357989f2807SJerry Jelinek return (EMSGSIZE); 358989f2807SJerry Jelinek } 359989f2807SJerry Jelinek TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, ir_link); 360989f2807SJerry Jelinek dev->ipmi_requests--; 361989f2807SJerry Jelinek IPMI_UNLOCK(sc); 362989f2807SJerry Jelinek len = min(recv.msg.data_len, len); 363989f2807SJerry Jelinek recv.msg.data_len = (unsigned short)len; 364989f2807SJerry Jelinek 365989f2807SJerry Jelinek if (orig_cmd == IPMICTL_RECEIVE_MSG_TRUNC_32 || 366989f2807SJerry Jelinek orig_cmd == IPMICTL_RECEIVE_MSG_32) { 367989f2807SJerry Jelinek /* Update changed fields in 32-bit structure. */ 368989f2807SJerry Jelinek recv32.recv_type = recv.recv_type; 369989f2807SJerry Jelinek recv32.msgid = (int32_t)recv.msgid; 370989f2807SJerry Jelinek recv32.msg.netfn = recv.msg.netfn; 371989f2807SJerry Jelinek recv32.msg.cmd = recv.msg.cmd; 372989f2807SJerry Jelinek recv32.msg.data_len = recv.msg.data_len; 373989f2807SJerry Jelinek 374989f2807SJerry Jelinek error = copyout(&recv32, (void *)data, sizeof (recv32)); 375989f2807SJerry Jelinek } else { 376989f2807SJerry Jelinek error = copyout(&recv, (void *)data, sizeof (recv)); 377989f2807SJerry Jelinek } 378989f2807SJerry Jelinek 379989f2807SJerry Jelinek /* This struct is the same for 32/64 */ 380989f2807SJerry Jelinek if (error == 0) 381989f2807SJerry Jelinek error = copyout(&addr, recv.addr, sizeof (addr)); 382989f2807SJerry Jelinek if (error == 0) 383989f2807SJerry Jelinek error = copyout(&kreq->ir_compcode, recv.msg.data, 1); 384989f2807SJerry Jelinek if (error == 0) 385989f2807SJerry Jelinek error = copyout(kreq->ir_reply, recv.msg.data + 1, 386989f2807SJerry Jelinek len - 1); 387989f2807SJerry Jelinek ipmi_free_request(kreq); 388989f2807SJerry Jelinek 389989f2807SJerry Jelinek if (error) 390989f2807SJerry Jelinek return (EFAULT); 391989f2807SJerry Jelinek 392989f2807SJerry Jelinek break; 393989f2807SJerry Jelinek 394989f2807SJerry Jelinek case IPMICTL_SET_MY_ADDRESS_CMD: 395989f2807SJerry Jelinek IPMI_LOCK(sc); 396989f2807SJerry Jelinek if (copyin((void *)data, &dev->ipmi_address, 397989f2807SJerry Jelinek sizeof (dev->ipmi_address))) { 398989f2807SJerry Jelinek IPMI_UNLOCK(sc); 399989f2807SJerry Jelinek return (EFAULT); 400989f2807SJerry Jelinek } 401989f2807SJerry Jelinek IPMI_UNLOCK(sc); 402989f2807SJerry Jelinek break; 403989f2807SJerry Jelinek 404989f2807SJerry Jelinek case IPMICTL_GET_MY_ADDRESS_CMD: 405989f2807SJerry Jelinek IPMI_LOCK(sc); 406989f2807SJerry Jelinek if (copyout(&dev->ipmi_address, (void *)data, 407989f2807SJerry Jelinek sizeof (dev->ipmi_address))) { 408989f2807SJerry Jelinek IPMI_UNLOCK(sc); 409989f2807SJerry Jelinek return (EFAULT); 410989f2807SJerry Jelinek } 411989f2807SJerry Jelinek IPMI_UNLOCK(sc); 412989f2807SJerry Jelinek break; 413989f2807SJerry Jelinek 414989f2807SJerry Jelinek case IPMICTL_SET_MY_LUN_CMD: 415989f2807SJerry Jelinek IPMI_LOCK(sc); 416989f2807SJerry Jelinek if (copyin((void *)data, &t_lun, sizeof (t_lun))) { 417989f2807SJerry Jelinek IPMI_UNLOCK(sc); 418989f2807SJerry Jelinek return (EFAULT); 419989f2807SJerry Jelinek } 420989f2807SJerry Jelinek dev->ipmi_lun = t_lun & 0x3; 421989f2807SJerry Jelinek IPMI_UNLOCK(sc); 422989f2807SJerry Jelinek break; 423989f2807SJerry Jelinek 424989f2807SJerry Jelinek case IPMICTL_GET_MY_LUN_CMD: 425989f2807SJerry Jelinek IPMI_LOCK(sc); 426989f2807SJerry Jelinek if (copyout(&dev->ipmi_lun, (void *)data, 427989f2807SJerry Jelinek sizeof (dev->ipmi_lun))) { 428989f2807SJerry Jelinek IPMI_UNLOCK(sc); 429989f2807SJerry Jelinek return (EFAULT); 430989f2807SJerry Jelinek } 431989f2807SJerry Jelinek IPMI_UNLOCK(sc); 432989f2807SJerry Jelinek break; 433989f2807SJerry Jelinek 434989f2807SJerry Jelinek case IPMICTL_SET_GETS_EVENTS_CMD: 435989f2807SJerry Jelinek break; 436989f2807SJerry Jelinek 437989f2807SJerry Jelinek case IPMICTL_REGISTER_FOR_CMD: 438989f2807SJerry Jelinek case IPMICTL_UNREGISTER_FOR_CMD: 439989f2807SJerry Jelinek return (EINVAL); 440989f2807SJerry Jelinek 441989f2807SJerry Jelinek default: 442989f2807SJerry Jelinek return (EINVAL); 443989f2807SJerry Jelinek } 444989f2807SJerry Jelinek 445989f2807SJerry Jelinek return (0); 446989f2807SJerry Jelinek } 447989f2807SJerry Jelinek 448989f2807SJerry Jelinek static int 449989f2807SJerry Jelinek ipmi_poll(dev_t dv, short events, int anyyet, short *reventsp, 450989f2807SJerry Jelinek pollhead_t **phpp) 451989f2807SJerry Jelinek { 452989f2807SJerry Jelinek struct ipmi_device *dev; 453989f2807SJerry Jelinek short revent = 0; 454989f2807SJerry Jelinek 455989f2807SJerry Jelinek if ((dev = lookup_ipmidev_by_dev(dv)) == NULL) 456989f2807SJerry Jelinek return (ENODEV); 457989f2807SJerry Jelinek 458989f2807SJerry Jelinek if (events & (POLLIN | POLLRDNORM)) { 459989f2807SJerry Jelinek if (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) 460989f2807SJerry Jelinek revent |= events & (POLLIN | POLLRDNORM); 461989f2807SJerry Jelinek if (dev->ipmi_requests == 0) 462989f2807SJerry Jelinek revent |= POLLERR; 463989f2807SJerry Jelinek } 464989f2807SJerry Jelinek 465989f2807SJerry Jelinek if (revent == 0) { 466989f2807SJerry Jelinek /* nothing has occurred */ 467989f2807SJerry Jelinek if (!anyyet) 468989f2807SJerry Jelinek *phpp = dev->ipmi_pollhead; 469989f2807SJerry Jelinek } 470989f2807SJerry Jelinek 471989f2807SJerry Jelinek *reventsp = revent; 472989f2807SJerry Jelinek return (0); 473989f2807SJerry Jelinek } 474989f2807SJerry Jelinek 475989f2807SJerry Jelinek /*ARGSUSED*/ 476989f2807SJerry Jelinek static int 477989f2807SJerry Jelinek ipmi_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp) 478989f2807SJerry Jelinek { 479989f2807SJerry Jelinek switch (cmd) { 480989f2807SJerry Jelinek case DDI_INFO_DEVT2DEVINFO: 481989f2807SJerry Jelinek *resultp = ipmi_dip; 482989f2807SJerry Jelinek return (DDI_SUCCESS); 483989f2807SJerry Jelinek case DDI_INFO_DEVT2INSTANCE: 484989f2807SJerry Jelinek *resultp = NULL; 485989f2807SJerry Jelinek return (DDI_SUCCESS); 486989f2807SJerry Jelinek } 487989f2807SJerry Jelinek return (DDI_FAILURE); 488989f2807SJerry Jelinek } 489989f2807SJerry Jelinek 4901e393477SMarcel Telka static void 4911e393477SMarcel Telka ipmi_cleanup(dev_info_t *dip) 4921e393477SMarcel Telka { 4931e393477SMarcel Telka /* poke the taskq so that it can terminate */ 4941e393477SMarcel Telka IPMI_LOCK(sc); 4951e393477SMarcel Telka sc->ipmi_detaching = 1; 4961e393477SMarcel Telka cv_signal(&sc->ipmi_request_added); 4971e393477SMarcel Telka IPMI_UNLOCK(sc); 4981e393477SMarcel Telka 4991e393477SMarcel Telka ipmi_shutdown(sc); 5001e393477SMarcel Telka ddi_remove_minor_node(dip, NULL); 5011e393477SMarcel Telka ipmi_dip = NULL; 5021e393477SMarcel Telka 503164b0ee5SRobert Mustacchi mutex_destroy(&dev_list_lock); 5041e393477SMarcel Telka list_destroy(&dev_list); 5051e393477SMarcel Telka id_space_destroy(minor_ids); 5061e393477SMarcel Telka 5071e393477SMarcel Telka sc->ipmi_detaching = 0; 5081e393477SMarcel Telka } 5091e393477SMarcel Telka 510989f2807SJerry Jelinek static int 511989f2807SJerry Jelinek ipmi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 512989f2807SJerry Jelinek { 513989f2807SJerry Jelinek if (cmd != DDI_ATTACH) 514989f2807SJerry Jelinek return (DDI_FAILURE); 515989f2807SJerry Jelinek 516e1c99a74SAlek Pinchuk /* this driver only supports one device instance */ 517e1c99a74SAlek Pinchuk if (ddi_get_instance(dip) != 0) { 518e1c99a74SAlek Pinchuk cmn_err(CE_WARN, 519e1c99a74SAlek Pinchuk "!not attaching to non-zero device instance %d", 520e1c99a74SAlek Pinchuk ddi_get_instance(dip)); 521e1c99a74SAlek Pinchuk return (DDI_FAILURE); 522e1c99a74SAlek Pinchuk } 523e1c99a74SAlek Pinchuk 524989f2807SJerry Jelinek if (get_smbios_ipmi_info() == DDI_FAILURE) 525989f2807SJerry Jelinek return (DDI_FAILURE); 526989f2807SJerry Jelinek 527989f2807SJerry Jelinek /* 528989f2807SJerry Jelinek * Support for the other types (SMIC, SSIF) should be added here. 529989f2807SJerry Jelinek */ 530989f2807SJerry Jelinek switch (sc->ipmi_io_type) { 531989f2807SJerry Jelinek case SMB_IPMI_T_KCS: 532989f2807SJerry Jelinek if (ipmi_kcs_attach(sc) != 0) 533989f2807SJerry Jelinek return (DDI_FAILURE); 534989f2807SJerry Jelinek break; 535989f2807SJerry Jelinek default: 536989f2807SJerry Jelinek return (DDI_FAILURE); 537989f2807SJerry Jelinek } 538989f2807SJerry Jelinek ipmi_found = B_TRUE; 539989f2807SJerry Jelinek 540989f2807SJerry Jelinek if (ddi_create_minor_node(dip, "ipmi", S_IFCHR, 0, DDI_PSEUDO, 541989f2807SJerry Jelinek 0) == DDI_FAILURE) { 542989f2807SJerry Jelinek cmn_err(CE_WARN, "!attach could not create minor node"); 543989f2807SJerry Jelinek ddi_remove_minor_node(dip, NULL); 544989f2807SJerry Jelinek return (DDI_FAILURE); 545989f2807SJerry Jelinek } 546989f2807SJerry Jelinek 547989f2807SJerry Jelinek ipmi_dip = dip; 548989f2807SJerry Jelinek 549989f2807SJerry Jelinek list_create(&dev_list, sizeof (ipmi_device_t), 550989f2807SJerry Jelinek offsetof(ipmi_device_t, ipmi_node)); 551164b0ee5SRobert Mustacchi mutex_init(&dev_list_lock, NULL, MUTEX_DRIVER, NULL); 552989f2807SJerry Jelinek 553989f2807SJerry Jelinek /* Create ID space for open devs. ID 0 is reserved. */ 554989f2807SJerry Jelinek minor_ids = id_space_create("ipmi_id_space", 1, 128); 555989f2807SJerry Jelinek 556e1c99a74SAlek Pinchuk if (ipmi_startup(sc) != B_TRUE) { 5571e393477SMarcel Telka ipmi_cleanup(dip); 558e1c99a74SAlek Pinchuk return (DDI_FAILURE); 559e1c99a74SAlek Pinchuk } 560e1c99a74SAlek Pinchuk 561989f2807SJerry Jelinek ipmi_attached = B_TRUE; 562989f2807SJerry Jelinek 563989f2807SJerry Jelinek return (DDI_SUCCESS); 564989f2807SJerry Jelinek } 565989f2807SJerry Jelinek 566989f2807SJerry Jelinek static int 567989f2807SJerry Jelinek ipmi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 568989f2807SJerry Jelinek { 569989f2807SJerry Jelinek if (cmd != DDI_DETACH) 570989f2807SJerry Jelinek return (DDI_FAILURE); 571989f2807SJerry Jelinek 572989f2807SJerry Jelinek if (ipmi_found == B_FALSE) 573989f2807SJerry Jelinek return (DDI_SUCCESS); 574989f2807SJerry Jelinek 575164b0ee5SRobert Mustacchi mutex_enter(&dev_list_lock); 576164b0ee5SRobert Mustacchi if (!list_is_empty(&dev_list)) { 577164b0ee5SRobert Mustacchi mutex_exit(&dev_list_lock); 578989f2807SJerry Jelinek return (DDI_FAILURE); 579164b0ee5SRobert Mustacchi } 580164b0ee5SRobert Mustacchi mutex_exit(&dev_list_lock); 581989f2807SJerry Jelinek 5821e393477SMarcel Telka ipmi_cleanup(dip); 583989f2807SJerry Jelinek 584989f2807SJerry Jelinek ipmi_attached = B_FALSE; 585989f2807SJerry Jelinek return (DDI_SUCCESS); 586989f2807SJerry Jelinek } 587989f2807SJerry Jelinek 588989f2807SJerry Jelinek static struct cb_ops ipmi_cb_ops = { 589989f2807SJerry Jelinek ipmi_open, 590989f2807SJerry Jelinek ipmi_close, 591989f2807SJerry Jelinek nodev, /* strategy */ 592989f2807SJerry Jelinek nodev, /* print */ 593989f2807SJerry Jelinek nodev, /* dump */ 594989f2807SJerry Jelinek nodev, /* read */ 595989f2807SJerry Jelinek nodev, /* write */ 596989f2807SJerry Jelinek ipmi_ioctl, 597989f2807SJerry Jelinek nodev, /* devmap */ 598989f2807SJerry Jelinek nodev, /* mmap */ 599989f2807SJerry Jelinek nodev, /* segmap */ 600989f2807SJerry Jelinek ipmi_poll, 601989f2807SJerry Jelinek ddi_prop_op, 602989f2807SJerry Jelinek NULL, /* streamtab */ 603e1c99a74SAlek Pinchuk D_NEW | D_MP, /* flags */ 604e1c99a74SAlek Pinchuk CB_REV, 605e1c99a74SAlek Pinchuk nodev, /* awread */ 606e1c99a74SAlek Pinchuk nodev /* awrite */ 607989f2807SJerry Jelinek }; 608989f2807SJerry Jelinek 609989f2807SJerry Jelinek static struct dev_ops ipmi_ops = { 610989f2807SJerry Jelinek DEVO_REV, 611989f2807SJerry Jelinek 0, /* reference count */ 612989f2807SJerry Jelinek ipmi_info, 613989f2807SJerry Jelinek nulldev, /* identify */ 614989f2807SJerry Jelinek nulldev, /* probe */ 615989f2807SJerry Jelinek ipmi_attach, 616989f2807SJerry Jelinek ipmi_detach, 617989f2807SJerry Jelinek nodev, /* reset */ 618989f2807SJerry Jelinek &ipmi_cb_ops, 619989f2807SJerry Jelinek NULL, /* bus ops */ 620989f2807SJerry Jelinek NULL, /* power */ 621989f2807SJerry Jelinek ddi_quiesce_not_needed, 622989f2807SJerry Jelinek }; 623989f2807SJerry Jelinek 624989f2807SJerry Jelinek static struct modldrv md = { 625989f2807SJerry Jelinek &mod_driverops, "ipmi driver", &ipmi_ops 626989f2807SJerry Jelinek }; 627989f2807SJerry Jelinek 628989f2807SJerry Jelinek static struct modlinkage ml = { 629989f2807SJerry Jelinek MODREV_1, &md, NULL 630989f2807SJerry Jelinek }; 631989f2807SJerry Jelinek 632989f2807SJerry Jelinek int 633989f2807SJerry Jelinek _init(void) 634989f2807SJerry Jelinek { 635989f2807SJerry Jelinek return (mod_install(&ml)); 636989f2807SJerry Jelinek } 637989f2807SJerry Jelinek 638989f2807SJerry Jelinek int 639989f2807SJerry Jelinek _fini(void) 640989f2807SJerry Jelinek { 641989f2807SJerry Jelinek return (mod_remove(&ml)); 642989f2807SJerry Jelinek } 643989f2807SJerry Jelinek 644989f2807SJerry Jelinek int 645989f2807SJerry Jelinek _info(struct modinfo *mip) 646989f2807SJerry Jelinek { 647989f2807SJerry Jelinek return (mod_info(&ml, mip)); 648989f2807SJerry Jelinek } 649