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