xref: /illumos-gate/usr/src/uts/intel/io/ipmi/ipmi_main.c (revision 989f2807)
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