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