xref: /illumos-gate/usr/src/uts/intel/io/ipmi/ipmi_main.c (revision 2c76d751)
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 /*
2322f89f96SJason 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
get_smbios_ipmi_info(void)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 	 */
924dae0814SToomas Soome 	if (ipmi.smbip_addr == 0) {
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 *
lookup_ipmidev_by_dev(dev_t dev)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);
139164b0ee5SRobert Mustacchi 		}
140989f2807SJerry Jelinek 	}
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
ipmi_open(dev_t * devp,int flag,int otyp,cred_t * cred)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;
15422f89f96SJason 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 
16622f89f96SJason King 	if ((mid = id_alloc_nosleep(minor_ids)) == -1)
167989f2807SJerry Jelinek 		return (ENODEV);
16822f89f96SJason 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 	TAILQ_INIT(&dev->ipmi_completed_requests);
174989f2807SJerry Jelinek 	dev->ipmi_address = IPMI_BMC_SLAVE_ADDR;
175989f2807SJerry Jelinek 	dev->ipmi_lun = IPMI_BMC_SMS_LUN;
176989f2807SJerry Jelinek 	*devp = makedevice(getmajor(*devp), minor);
177989f2807SJerry Jelinek 	dev->ipmi_dev = *devp;
178cc944374SMarcel Telka 	cv_init(&dev->ipmi_cv, NULL, CV_DEFAULT, NULL);
179989f2807SJerry Jelinek 
180164b0ee5SRobert Mustacchi 	mutex_enter(&dev_list_lock);
181989f2807SJerry Jelinek 	list_insert_head(&dev_list, dev);
182164b0ee5SRobert Mustacchi 	mutex_exit(&dev_list_lock);
183989f2807SJerry Jelinek 
184989f2807SJerry Jelinek 	return (0);
185989f2807SJerry Jelinek }
186989f2807SJerry Jelinek 
187989f2807SJerry Jelinek /*ARGSUSED*/
188989f2807SJerry Jelinek static int
ipmi_close(dev_t dev,int flag,int otyp,cred_t * cred)189989f2807SJerry Jelinek ipmi_close(dev_t dev, int flag, int otyp, cred_t *cred)
190989f2807SJerry Jelinek {
191989f2807SJerry Jelinek 	ipmi_device_t *dp;
192989f2807SJerry Jelinek 	struct ipmi_request *req, *next;
193989f2807SJerry Jelinek 
194989f2807SJerry Jelinek 	if ((dp = lookup_ipmidev_by_dev(dev)) == NULL)
195989f2807SJerry Jelinek 		return (ENODEV);
196989f2807SJerry Jelinek 
197989f2807SJerry Jelinek 	IPMI_LOCK(sc);
198989f2807SJerry Jelinek 	/* remove any pending requests */
199989f2807SJerry Jelinek 	req = TAILQ_FIRST(&sc->ipmi_pending_requests);
200989f2807SJerry Jelinek 	while (req != NULL) {
201989f2807SJerry Jelinek 		next = TAILQ_NEXT(req, ir_link);
202989f2807SJerry Jelinek 
203989f2807SJerry Jelinek 		if (req->ir_owner == dp) {
204989f2807SJerry Jelinek 			TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link);
205989f2807SJerry Jelinek 			ipmi_free_request(req);
206989f2807SJerry Jelinek 		}
207989f2807SJerry Jelinek 		req = next;
208989f2807SJerry Jelinek 	}
209cc944374SMarcel Telka 
210cc944374SMarcel Telka 	dp->ipmi_status |= IPMI_CLOSING;
211cc944374SMarcel Telka 	while (dp->ipmi_status & IPMI_BUSY)
212cc944374SMarcel Telka 		cv_wait(&dp->ipmi_cv, &sc->ipmi_lock);
213989f2807SJerry Jelinek 	IPMI_UNLOCK(sc);
214989f2807SJerry Jelinek 
215989f2807SJerry Jelinek 	/* remove any requests in queue of stuff completed */
216989f2807SJerry Jelinek 	while ((req = TAILQ_FIRST(&dp->ipmi_completed_requests)) != NULL) {
217989f2807SJerry Jelinek 		TAILQ_REMOVE(&dp->ipmi_completed_requests, req, ir_link);
218989f2807SJerry Jelinek 		ipmi_free_request(req);
219989f2807SJerry Jelinek 	}
220989f2807SJerry Jelinek 
221164b0ee5SRobert Mustacchi 	mutex_enter(&dev_list_lock);
222989f2807SJerry Jelinek 	list_remove(&dev_list, dp);
223164b0ee5SRobert Mustacchi 	mutex_exit(&dev_list_lock);
224989f2807SJerry Jelinek 	id_free(minor_ids, getminor(dev));
225cc944374SMarcel Telka 	cv_destroy(&dp->ipmi_cv);
226*2c76d751SPatrick Mooney 	pollhead_clean(&dp->ipmi_pollhead);
227989f2807SJerry Jelinek 	kmem_free(dp, sizeof (ipmi_device_t));
228989f2807SJerry Jelinek 
229989f2807SJerry Jelinek 	return (0);
230989f2807SJerry Jelinek }
231989f2807SJerry Jelinek 
232989f2807SJerry Jelinek /*ARGSUSED*/
233989f2807SJerry Jelinek static int
ipmi_ioctl(dev_t dv,int cmd,intptr_t data,int flags,cred_t * cr,int * rvalp)234989f2807SJerry Jelinek ipmi_ioctl(dev_t dv, int cmd, intptr_t data, int flags, cred_t *cr, int *rvalp)
235989f2807SJerry Jelinek {
236989f2807SJerry Jelinek 	struct ipmi_device *dev;
237989f2807SJerry Jelinek 	struct ipmi_request *kreq;
238989f2807SJerry Jelinek 	struct ipmi_req req;
239989f2807SJerry Jelinek 	struct ipmi_recv recv;
240989f2807SJerry Jelinek 	struct ipmi_recv32 recv32;
241989f2807SJerry Jelinek 	struct ipmi_addr addr;
242989f2807SJerry Jelinek 	int error, len;
243989f2807SJerry Jelinek 	model_t model;
244989f2807SJerry Jelinek 	int orig_cmd = 0;
245989f2807SJerry Jelinek 	uchar_t	t_lun;
246989f2807SJerry Jelinek 
247989f2807SJerry Jelinek 	if (secpolicy_sys_config(cr, B_FALSE) != 0)
248989f2807SJerry Jelinek 		return (EPERM);
249989f2807SJerry Jelinek 
250989f2807SJerry Jelinek 	if ((dev = lookup_ipmidev_by_dev(dv)) == NULL)
251989f2807SJerry Jelinek 		return (ENODEV);
252989f2807SJerry Jelinek 
253989f2807SJerry Jelinek 	model = get_udatamodel();
254989f2807SJerry Jelinek 	if (model == DATAMODEL_NATIVE) {
255989f2807SJerry Jelinek 		switch (cmd) {
256989f2807SJerry Jelinek 		case IPMICTL_SEND_COMMAND:
257989f2807SJerry Jelinek 			if (copyin((void *)data, &req, sizeof (req)))
258989f2807SJerry Jelinek 				return (EFAULT);
259989f2807SJerry Jelinek 			break;
260989f2807SJerry Jelinek 		case IPMICTL_RECEIVE_MSG_TRUNC:
261989f2807SJerry Jelinek 		case IPMICTL_RECEIVE_MSG:
262989f2807SJerry Jelinek 			if (copyin((void *)data, &recv, sizeof (recv)))
263989f2807SJerry Jelinek 				return (EFAULT);
264989f2807SJerry Jelinek 			break;
265989f2807SJerry Jelinek 		}
266989f2807SJerry Jelinek 	} else {
267989f2807SJerry Jelinek 		/* Convert 32-bit structures to native. */
268989f2807SJerry Jelinek 		struct ipmi_req32 req32;
269989f2807SJerry Jelinek 
270989f2807SJerry Jelinek 		switch (cmd) {
271989f2807SJerry Jelinek 		case IPMICTL_SEND_COMMAND_32:
272989f2807SJerry Jelinek 			if (copyin((void *)data, &req32, sizeof (req32)))
273989f2807SJerry Jelinek 				return (EFAULT);
274989f2807SJerry Jelinek 
275989f2807SJerry Jelinek 			req.addr = PTRIN(req32.addr);
276989f2807SJerry Jelinek 			req.addr_len = req32.addr_len;
277989f2807SJerry Jelinek 			req.msgid = req32.msgid;
278989f2807SJerry Jelinek 			req.msg.netfn = req32.msg.netfn;
279989f2807SJerry Jelinek 			req.msg.cmd = req32.msg.cmd;
280989f2807SJerry Jelinek 			req.msg.data_len = req32.msg.data_len;
281989f2807SJerry Jelinek 			req.msg.data = PTRIN(req32.msg.data);
282989f2807SJerry Jelinek 
283989f2807SJerry Jelinek 			cmd = IPMICTL_SEND_COMMAND;
284989f2807SJerry Jelinek 			break;
285989f2807SJerry Jelinek 
286989f2807SJerry Jelinek 		case IPMICTL_RECEIVE_MSG_TRUNC_32:
287989f2807SJerry Jelinek 		case IPMICTL_RECEIVE_MSG_32:
288989f2807SJerry Jelinek 			if (copyin((void *)data, &recv32, sizeof (recv32)))
289989f2807SJerry Jelinek 				return (EFAULT);
290989f2807SJerry Jelinek 
291989f2807SJerry Jelinek 			recv.addr = PTRIN(recv32.addr);
292989f2807SJerry Jelinek 			recv.addr_len = recv32.addr_len;
293989f2807SJerry Jelinek 			recv.msg.data_len = recv32.msg.data_len;
294989f2807SJerry Jelinek 			recv.msg.data = PTRIN(recv32.msg.data);
295989f2807SJerry Jelinek 
296989f2807SJerry Jelinek 			orig_cmd = cmd;
297989f2807SJerry Jelinek 			cmd = (cmd == IPMICTL_RECEIVE_MSG_TRUNC_32) ?
298989f2807SJerry Jelinek 			    IPMICTL_RECEIVE_MSG_TRUNC : IPMICTL_RECEIVE_MSG;
299989f2807SJerry Jelinek 			break;
300989f2807SJerry Jelinek 		}
301989f2807SJerry Jelinek 	}
302989f2807SJerry Jelinek 
303989f2807SJerry Jelinek 	switch (cmd) {
304989f2807SJerry Jelinek 	case IPMICTL_SEND_COMMAND:
305989f2807SJerry Jelinek 		/* Check that we didn't get a ridiculous length */
306989f2807SJerry Jelinek 		if (req.msg.data_len > IPMI_MAX_RX)
307989f2807SJerry Jelinek 			return (EINVAL);
308989f2807SJerry Jelinek 
309989f2807SJerry Jelinek 		kreq = ipmi_alloc_request(dev, req.msgid,
310989f2807SJerry Jelinek 		    IPMI_ADDR(req.msg.netfn, 0), req.msg.cmd,
311989f2807SJerry Jelinek 		    req.msg.data_len, IPMI_MAX_RX);
312989f2807SJerry Jelinek 		/* This struct is the same for 32/64 */
313989f2807SJerry Jelinek 		if (req.msg.data_len > 0 &&
314989f2807SJerry Jelinek 		    copyin(req.msg.data, kreq->ir_request, req.msg.data_len)) {
315989f2807SJerry Jelinek 			ipmi_free_request(kreq);
316989f2807SJerry Jelinek 			return (EFAULT);
317989f2807SJerry Jelinek 		}
318989f2807SJerry Jelinek 		IPMI_LOCK(sc);
319989f2807SJerry Jelinek 		dev->ipmi_requests++;
320989f2807SJerry Jelinek 		error = sc->ipmi_enqueue_request(sc, kreq);
321989f2807SJerry Jelinek 		IPMI_UNLOCK(sc);
322989f2807SJerry Jelinek 		if (error)
323989f2807SJerry Jelinek 			return (error);
324989f2807SJerry Jelinek 		break;
325989f2807SJerry Jelinek 
326989f2807SJerry Jelinek 	case IPMICTL_RECEIVE_MSG_TRUNC:
327989f2807SJerry Jelinek 	case IPMICTL_RECEIVE_MSG:
328989f2807SJerry Jelinek 		/* This struct is the same for 32/64 */
329989f2807SJerry Jelinek 		if (copyin(recv.addr, &addr, sizeof (addr)))
330989f2807SJerry Jelinek 			return (EFAULT);
331989f2807SJerry Jelinek 
332989f2807SJerry Jelinek 		IPMI_LOCK(sc);
333989f2807SJerry Jelinek 		kreq = TAILQ_FIRST(&dev->ipmi_completed_requests);
334989f2807SJerry Jelinek 		if (kreq == NULL) {
335989f2807SJerry Jelinek 			IPMI_UNLOCK(sc);
336989f2807SJerry Jelinek 			return (EAGAIN);
337989f2807SJerry Jelinek 		}
338989f2807SJerry Jelinek 		addr.channel = IPMI_BMC_CHANNEL;
339989f2807SJerry Jelinek 		recv.recv_type = IPMI_RESPONSE_RECV_TYPE;
340989f2807SJerry Jelinek 		recv.msgid = kreq->ir_msgid;
341989f2807SJerry Jelinek 		recv.msg.netfn = IPMI_REPLY_ADDR(kreq->ir_addr) >> 2;
342989f2807SJerry Jelinek 		recv.msg.cmd = kreq->ir_command;
343989f2807SJerry Jelinek 		error = kreq->ir_error;
344989f2807SJerry Jelinek 		if (error) {
345989f2807SJerry Jelinek 			TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq,
346989f2807SJerry Jelinek 			    ir_link);
347989f2807SJerry Jelinek 			dev->ipmi_requests--;
348989f2807SJerry Jelinek 			IPMI_UNLOCK(sc);
349989f2807SJerry Jelinek 			ipmi_free_request(kreq);
350989f2807SJerry Jelinek 			return (error);
351989f2807SJerry Jelinek 		}
352989f2807SJerry Jelinek 		len = kreq->ir_replylen + 1;
353989f2807SJerry Jelinek 		if (recv.msg.data_len < len && cmd == IPMICTL_RECEIVE_MSG) {
354989f2807SJerry Jelinek 			IPMI_UNLOCK(sc);
355989f2807SJerry Jelinek 			return (EMSGSIZE);
356989f2807SJerry Jelinek 		}
357989f2807SJerry Jelinek 		TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, ir_link);
358989f2807SJerry Jelinek 		dev->ipmi_requests--;
359989f2807SJerry Jelinek 		IPMI_UNLOCK(sc);
360989f2807SJerry Jelinek 		len = min(recv.msg.data_len, len);
361989f2807SJerry Jelinek 		recv.msg.data_len = (unsigned short)len;
362989f2807SJerry Jelinek 
363989f2807SJerry Jelinek 		if (orig_cmd == IPMICTL_RECEIVE_MSG_TRUNC_32 ||
364989f2807SJerry Jelinek 		    orig_cmd == IPMICTL_RECEIVE_MSG_32) {
365989f2807SJerry Jelinek 			/* Update changed fields in 32-bit structure. */
366989f2807SJerry Jelinek 			recv32.recv_type = recv.recv_type;
367989f2807SJerry Jelinek 			recv32.msgid = (int32_t)recv.msgid;
368989f2807SJerry Jelinek 			recv32.msg.netfn = recv.msg.netfn;
369989f2807SJerry Jelinek 			recv32.msg.cmd = recv.msg.cmd;
370989f2807SJerry Jelinek 			recv32.msg.data_len = recv.msg.data_len;
371989f2807SJerry Jelinek 
372989f2807SJerry Jelinek 			error = copyout(&recv32, (void *)data, sizeof (recv32));
373989f2807SJerry Jelinek 		} else {
374989f2807SJerry Jelinek 			error = copyout(&recv, (void *)data, sizeof (recv));
375989f2807SJerry Jelinek 		}
376989f2807SJerry Jelinek 
377989f2807SJerry Jelinek 		/* This struct is the same for 32/64 */
378989f2807SJerry Jelinek 		if (error == 0)
379989f2807SJerry Jelinek 			error = copyout(&addr, recv.addr, sizeof (addr));
380989f2807SJerry Jelinek 		if (error == 0)
381989f2807SJerry Jelinek 			error = copyout(&kreq->ir_compcode, recv.msg.data, 1);
382989f2807SJerry Jelinek 		if (error == 0)
383989f2807SJerry Jelinek 			error = copyout(kreq->ir_reply, recv.msg.data + 1,
384989f2807SJerry Jelinek 			    len - 1);
385989f2807SJerry Jelinek 		ipmi_free_request(kreq);
386989f2807SJerry Jelinek 
387989f2807SJerry Jelinek 		if (error)
388989f2807SJerry Jelinek 			return (EFAULT);
389989f2807SJerry Jelinek 
390989f2807SJerry Jelinek 		break;
391989f2807SJerry Jelinek 
392989f2807SJerry Jelinek 	case IPMICTL_SET_MY_ADDRESS_CMD:
393989f2807SJerry Jelinek 		IPMI_LOCK(sc);
394989f2807SJerry Jelinek 		if (copyin((void *)data, &dev->ipmi_address,
395989f2807SJerry Jelinek 		    sizeof (dev->ipmi_address))) {
396989f2807SJerry Jelinek 			IPMI_UNLOCK(sc);
397989f2807SJerry Jelinek 			return (EFAULT);
398989f2807SJerry Jelinek 		}
399989f2807SJerry Jelinek 		IPMI_UNLOCK(sc);
400989f2807SJerry Jelinek 		break;
401989f2807SJerry Jelinek 
402989f2807SJerry Jelinek 	case IPMICTL_GET_MY_ADDRESS_CMD:
403989f2807SJerry Jelinek 		IPMI_LOCK(sc);
404989f2807SJerry Jelinek 		if (copyout(&dev->ipmi_address, (void *)data,
405989f2807SJerry Jelinek 		    sizeof (dev->ipmi_address))) {
406989f2807SJerry Jelinek 			IPMI_UNLOCK(sc);
407989f2807SJerry Jelinek 			return (EFAULT);
408989f2807SJerry Jelinek 		}
409989f2807SJerry Jelinek 		IPMI_UNLOCK(sc);
410989f2807SJerry Jelinek 		break;
411989f2807SJerry Jelinek 
412989f2807SJerry Jelinek 	case IPMICTL_SET_MY_LUN_CMD:
413989f2807SJerry Jelinek 		IPMI_LOCK(sc);
414989f2807SJerry Jelinek 		if (copyin((void *)data, &t_lun, sizeof (t_lun))) {
415989f2807SJerry Jelinek 			IPMI_UNLOCK(sc);
416989f2807SJerry Jelinek 			return (EFAULT);
417989f2807SJerry Jelinek 		}
418989f2807SJerry Jelinek 		dev->ipmi_lun = t_lun & 0x3;
419989f2807SJerry Jelinek 		IPMI_UNLOCK(sc);
420989f2807SJerry Jelinek 		break;
421989f2807SJerry Jelinek 
422989f2807SJerry Jelinek 	case IPMICTL_GET_MY_LUN_CMD:
423989f2807SJerry Jelinek 		IPMI_LOCK(sc);
424989f2807SJerry Jelinek 		if (copyout(&dev->ipmi_lun, (void *)data,
425989f2807SJerry Jelinek 		    sizeof (dev->ipmi_lun))) {
426989f2807SJerry Jelinek 			IPMI_UNLOCK(sc);
427989f2807SJerry Jelinek 			return (EFAULT);
428989f2807SJerry Jelinek 		}
429989f2807SJerry Jelinek 		IPMI_UNLOCK(sc);
430989f2807SJerry Jelinek 		break;
431989f2807SJerry Jelinek 
432989f2807SJerry Jelinek 	case IPMICTL_SET_GETS_EVENTS_CMD:
433989f2807SJerry Jelinek 		break;
434989f2807SJerry Jelinek 
435989f2807SJerry Jelinek 	case IPMICTL_REGISTER_FOR_CMD:
436989f2807SJerry Jelinek 	case IPMICTL_UNREGISTER_FOR_CMD:
437989f2807SJerry Jelinek 		return (EINVAL);
438989f2807SJerry Jelinek 
439989f2807SJerry Jelinek 	default:
440989f2807SJerry Jelinek 		return (EINVAL);
441989f2807SJerry Jelinek 	}
442989f2807SJerry Jelinek 
443989f2807SJerry Jelinek 	return (0);
444989f2807SJerry Jelinek }
445989f2807SJerry Jelinek 
446989f2807SJerry Jelinek static int
ipmi_poll(dev_t dv,short events,int anyyet,short * reventsp,pollhead_t ** phpp)447989f2807SJerry Jelinek ipmi_poll(dev_t dv, short events, int anyyet, short *reventsp,
448989f2807SJerry Jelinek     pollhead_t **phpp)
449989f2807SJerry Jelinek {
450989f2807SJerry Jelinek 	struct ipmi_device *dev;
451989f2807SJerry Jelinek 	short revent = 0;
452989f2807SJerry Jelinek 
453989f2807SJerry Jelinek 	if ((dev = lookup_ipmidev_by_dev(dv)) == NULL)
454989f2807SJerry Jelinek 		return (ENODEV);
455989f2807SJerry Jelinek 
456989f2807SJerry Jelinek 	if (events & (POLLIN | POLLRDNORM)) {
457989f2807SJerry Jelinek 		if (!TAILQ_EMPTY(&dev->ipmi_completed_requests))
458989f2807SJerry Jelinek 			revent |= events & (POLLIN | POLLRDNORM);
459989f2807SJerry Jelinek 		if (dev->ipmi_requests == 0)
460989f2807SJerry Jelinek 			revent |= POLLERR;
461989f2807SJerry Jelinek 	}
462989f2807SJerry Jelinek 
46380d5689fSPatrick Mooney 	if ((revent == 0 && !anyyet) || (events & POLLET)) {
464*2c76d751SPatrick Mooney 		*phpp = &dev->ipmi_pollhead;
465989f2807SJerry Jelinek 	}
466989f2807SJerry Jelinek 
467989f2807SJerry Jelinek 	*reventsp = revent;
468989f2807SJerry Jelinek 	return (0);
469989f2807SJerry Jelinek }
470989f2807SJerry Jelinek 
471989f2807SJerry Jelinek /*ARGSUSED*/
472989f2807SJerry Jelinek static int
ipmi_info(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)473989f2807SJerry Jelinek ipmi_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
474989f2807SJerry Jelinek {
475989f2807SJerry Jelinek 	switch (cmd) {
476989f2807SJerry Jelinek 	case DDI_INFO_DEVT2DEVINFO:
477989f2807SJerry Jelinek 		*resultp = ipmi_dip;
478989f2807SJerry Jelinek 		return (DDI_SUCCESS);
479989f2807SJerry Jelinek 	case DDI_INFO_DEVT2INSTANCE:
480989f2807SJerry Jelinek 		*resultp = NULL;
481989f2807SJerry Jelinek 		return (DDI_SUCCESS);
482989f2807SJerry Jelinek 	}
483989f2807SJerry Jelinek 	return (DDI_FAILURE);
484989f2807SJerry Jelinek }
485989f2807SJerry Jelinek 
4861e393477SMarcel Telka static void
ipmi_cleanup(dev_info_t * dip)4871e393477SMarcel Telka ipmi_cleanup(dev_info_t *dip)
4881e393477SMarcel Telka {
4891e393477SMarcel Telka 	/* poke the taskq so that it can terminate */
4901e393477SMarcel Telka 	IPMI_LOCK(sc);
4911e393477SMarcel Telka 	sc->ipmi_detaching = 1;
4921e393477SMarcel Telka 	cv_signal(&sc->ipmi_request_added);
4931e393477SMarcel Telka 	IPMI_UNLOCK(sc);
4941e393477SMarcel Telka 
4951e393477SMarcel Telka 	ipmi_shutdown(sc);
4961e393477SMarcel Telka 	ddi_remove_minor_node(dip, NULL);
4971e393477SMarcel Telka 	ipmi_dip = NULL;
4981e393477SMarcel Telka 
499164b0ee5SRobert Mustacchi 	mutex_destroy(&dev_list_lock);
5001e393477SMarcel Telka 	list_destroy(&dev_list);
5011e393477SMarcel Telka 	id_space_destroy(minor_ids);
5021e393477SMarcel Telka 
5031e393477SMarcel Telka 	sc->ipmi_detaching = 0;
5041e393477SMarcel Telka }
5051e393477SMarcel Telka 
506989f2807SJerry Jelinek static int
ipmi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)507989f2807SJerry Jelinek ipmi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
508989f2807SJerry Jelinek {
509989f2807SJerry Jelinek 	if (cmd != DDI_ATTACH)
510989f2807SJerry Jelinek 		return (DDI_FAILURE);
511989f2807SJerry Jelinek 
512e1c99a74SAlek Pinchuk 	/* this driver only supports one device instance */
513e1c99a74SAlek Pinchuk 	if (ddi_get_instance(dip) != 0) {
514e1c99a74SAlek Pinchuk 		cmn_err(CE_WARN,
515e1c99a74SAlek Pinchuk 		    "!not attaching to non-zero device instance %d",
516e1c99a74SAlek Pinchuk 		    ddi_get_instance(dip));
517e1c99a74SAlek Pinchuk 		return (DDI_FAILURE);
518e1c99a74SAlek Pinchuk 	}
519e1c99a74SAlek Pinchuk 
520989f2807SJerry Jelinek 	if (get_smbios_ipmi_info() == DDI_FAILURE)
521989f2807SJerry Jelinek 		return (DDI_FAILURE);
522989f2807SJerry Jelinek 
523989f2807SJerry Jelinek 	/*
524989f2807SJerry Jelinek 	 * Support for the other types (SMIC, SSIF) should be added here.
525989f2807SJerry Jelinek 	 */
526989f2807SJerry Jelinek 	switch (sc->ipmi_io_type) {
527989f2807SJerry Jelinek 	case SMB_IPMI_T_KCS:
528989f2807SJerry Jelinek 		if (ipmi_kcs_attach(sc) != 0)
529989f2807SJerry Jelinek 			return (DDI_FAILURE);
530989f2807SJerry Jelinek 		break;
531989f2807SJerry Jelinek 	default:
532989f2807SJerry Jelinek 		return (DDI_FAILURE);
533989f2807SJerry Jelinek 	}
534989f2807SJerry Jelinek 	ipmi_found = B_TRUE;
535989f2807SJerry Jelinek 
536989f2807SJerry Jelinek 	if (ddi_create_minor_node(dip, "ipmi", S_IFCHR, 0, DDI_PSEUDO,
537989f2807SJerry Jelinek 	    0) == DDI_FAILURE) {
538989f2807SJerry Jelinek 		cmn_err(CE_WARN, "!attach could not create minor node");
539989f2807SJerry Jelinek 		ddi_remove_minor_node(dip, NULL);
540989f2807SJerry Jelinek 		return (DDI_FAILURE);
541989f2807SJerry Jelinek 	}
542989f2807SJerry Jelinek 
543989f2807SJerry Jelinek 	ipmi_dip = dip;
544989f2807SJerry Jelinek 
545989f2807SJerry Jelinek 	list_create(&dev_list, sizeof (ipmi_device_t),
546989f2807SJerry Jelinek 	    offsetof(ipmi_device_t, ipmi_node));
547164b0ee5SRobert Mustacchi 	mutex_init(&dev_list_lock, NULL, MUTEX_DRIVER, NULL);
548989f2807SJerry Jelinek 
549989f2807SJerry Jelinek 	/* Create ID space for open devs.  ID 0 is reserved. */
550989f2807SJerry Jelinek 	minor_ids = id_space_create("ipmi_id_space", 1, 128);
551989f2807SJerry Jelinek 
552e1c99a74SAlek Pinchuk 	if (ipmi_startup(sc) != B_TRUE) {
5531e393477SMarcel Telka 		ipmi_cleanup(dip);
554e1c99a74SAlek Pinchuk 		return (DDI_FAILURE);
555e1c99a74SAlek Pinchuk 	}
556e1c99a74SAlek Pinchuk 
557989f2807SJerry Jelinek 	ipmi_attached = B_TRUE;
558989f2807SJerry Jelinek 
559989f2807SJerry Jelinek 	return (DDI_SUCCESS);
560989f2807SJerry Jelinek }
561989f2807SJerry Jelinek 
562989f2807SJerry Jelinek static int
ipmi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)563989f2807SJerry Jelinek ipmi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
564989f2807SJerry Jelinek {
565989f2807SJerry Jelinek 	if (cmd != DDI_DETACH)
566989f2807SJerry Jelinek 		return (DDI_FAILURE);
567989f2807SJerry Jelinek 
568989f2807SJerry Jelinek 	if (ipmi_found == B_FALSE)
569989f2807SJerry Jelinek 		return (DDI_SUCCESS);
570989f2807SJerry Jelinek 
571164b0ee5SRobert Mustacchi 	mutex_enter(&dev_list_lock);
572164b0ee5SRobert Mustacchi 	if (!list_is_empty(&dev_list)) {
573164b0ee5SRobert Mustacchi 		mutex_exit(&dev_list_lock);
574989f2807SJerry Jelinek 		return (DDI_FAILURE);
575164b0ee5SRobert Mustacchi 	}
576164b0ee5SRobert Mustacchi 	mutex_exit(&dev_list_lock);
577989f2807SJerry Jelinek 
5781e393477SMarcel Telka 	ipmi_cleanup(dip);
579989f2807SJerry Jelinek 
580989f2807SJerry Jelinek 	ipmi_attached = B_FALSE;
581989f2807SJerry Jelinek 	return (DDI_SUCCESS);
582989f2807SJerry Jelinek }
583989f2807SJerry Jelinek 
584989f2807SJerry Jelinek static struct cb_ops ipmi_cb_ops = {
585989f2807SJerry Jelinek 	ipmi_open,
586989f2807SJerry Jelinek 	ipmi_close,
587989f2807SJerry Jelinek 	nodev,			/* strategy */
588989f2807SJerry Jelinek 	nodev,			/* print */
589989f2807SJerry Jelinek 	nodev,			/* dump */
590989f2807SJerry Jelinek 	nodev,			/* read */
591989f2807SJerry Jelinek 	nodev,			/* write */
592989f2807SJerry Jelinek 	ipmi_ioctl,
593989f2807SJerry Jelinek 	nodev,			/* devmap */
594989f2807SJerry Jelinek 	nodev,			/* mmap */
595989f2807SJerry Jelinek 	nodev,			/* segmap */
596989f2807SJerry Jelinek 	ipmi_poll,
597989f2807SJerry Jelinek 	ddi_prop_op,
598989f2807SJerry Jelinek 	NULL,			/* streamtab */
599e1c99a74SAlek Pinchuk 	D_NEW | D_MP,		/* flags */
600e1c99a74SAlek Pinchuk 	CB_REV,
601e1c99a74SAlek Pinchuk 	nodev,			/* awread */
602e1c99a74SAlek Pinchuk 	nodev			/* awrite */
603989f2807SJerry Jelinek };
604989f2807SJerry Jelinek 
605989f2807SJerry Jelinek static struct dev_ops ipmi_ops = {
606989f2807SJerry Jelinek 	DEVO_REV,
607989f2807SJerry Jelinek 	0,			/* reference count */
608989f2807SJerry Jelinek 	ipmi_info,
609989f2807SJerry Jelinek 	nulldev,		/* identify */
610989f2807SJerry Jelinek 	nulldev,		/* probe */
611989f2807SJerry Jelinek 	ipmi_attach,
612989f2807SJerry Jelinek 	ipmi_detach,
613989f2807SJerry Jelinek 	nodev,			/* reset */
614989f2807SJerry Jelinek 	&ipmi_cb_ops,
615989f2807SJerry Jelinek 	NULL,			/* bus ops */
616989f2807SJerry Jelinek 	NULL,			/* power */
617989f2807SJerry Jelinek 	ddi_quiesce_not_needed,
618989f2807SJerry Jelinek };
619989f2807SJerry Jelinek 
620989f2807SJerry Jelinek static struct modldrv md = {
621989f2807SJerry Jelinek 	&mod_driverops, "ipmi driver", &ipmi_ops
622989f2807SJerry Jelinek };
623989f2807SJerry Jelinek 
624989f2807SJerry Jelinek static struct modlinkage ml = {
625989f2807SJerry Jelinek 	MODREV_1, &md, NULL
626989f2807SJerry Jelinek };
627989f2807SJerry Jelinek 
628989f2807SJerry Jelinek int
_init(void)629989f2807SJerry Jelinek _init(void)
630989f2807SJerry Jelinek {
631989f2807SJerry Jelinek 	return (mod_install(&ml));
632989f2807SJerry Jelinek }
633989f2807SJerry Jelinek 
634989f2807SJerry Jelinek int
_fini(void)635989f2807SJerry Jelinek _fini(void)
636989f2807SJerry Jelinek {
637989f2807SJerry Jelinek 	return (mod_remove(&ml));
638989f2807SJerry Jelinek }
639989f2807SJerry Jelinek 
640989f2807SJerry Jelinek int
_info(struct modinfo * mip)641989f2807SJerry Jelinek _info(struct modinfo *mip)
642989f2807SJerry Jelinek {
643989f2807SJerry Jelinek 	return (mod_info(&ml, mip));
644989f2807SJerry Jelinek }
645