xref: /titanic_52/usr/src/uts/intel/io/ipmi/ipmi_main.c (revision c0e7977a434048a8bc7386ea0e8befaa77a646cf)
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 
174 	list_insert_head(&dev_list, dev);
175 
176 	return (0);
177 }
178 
179 /*ARGSUSED*/
180 static int
181 ipmi_close(dev_t dev, int flag, int otyp, cred_t *cred)
182 {
183 	ipmi_device_t *dp;
184 	struct ipmi_request *req, *next;
185 
186 	if ((dp = lookup_ipmidev_by_dev(dev)) == NULL)
187 		return (ENODEV);
188 
189 	IPMI_LOCK(sc);
190 	/* remove any pending requests */
191 	req = TAILQ_FIRST(&sc->ipmi_pending_requests);
192 	while (req != NULL) {
193 		next = TAILQ_NEXT(req, ir_link);
194 
195 		if (req->ir_owner == dp) {
196 			TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link);
197 			ipmi_free_request(req);
198 		}
199 		req = next;
200 	}
201 	IPMI_UNLOCK(sc);
202 
203 	/* remove any requests in queue of stuff completed */
204 	while ((req = TAILQ_FIRST(&dp->ipmi_completed_requests)) != NULL) {
205 		TAILQ_REMOVE(&dp->ipmi_completed_requests, req, ir_link);
206 		ipmi_free_request(req);
207 	}
208 
209 	list_remove(&dev_list, dp);
210 	id_free(minor_ids, getminor(dev));
211 	kmem_free(dp->ipmi_pollhead, sizeof (pollhead_t));
212 	kmem_free(dp, sizeof (ipmi_device_t));
213 
214 	return (0);
215 }
216 
217 /*ARGSUSED*/
218 static int
219 ipmi_ioctl(dev_t dv, int cmd, intptr_t data, int flags, cred_t *cr, int *rvalp)
220 {
221 	struct ipmi_device *dev;
222 	struct ipmi_request *kreq;
223 	struct ipmi_req req;
224 	struct ipmi_recv recv;
225 	struct ipmi_recv32 recv32;
226 	struct ipmi_addr addr;
227 	int error, len;
228 	model_t model;
229 	int orig_cmd = 0;
230 	uchar_t	t_lun;
231 
232 	if (secpolicy_sys_config(cr, B_FALSE) != 0)
233 		return (EPERM);
234 
235 	if ((dev = lookup_ipmidev_by_dev(dv)) == NULL)
236 		return (ENODEV);
237 
238 	model = get_udatamodel();
239 	if (model == DATAMODEL_NATIVE) {
240 		switch (cmd) {
241 		case IPMICTL_SEND_COMMAND:
242 			if (copyin((void *)data, &req, sizeof (req)))
243 				return (EFAULT);
244 			break;
245 		case IPMICTL_RECEIVE_MSG_TRUNC:
246 		case IPMICTL_RECEIVE_MSG:
247 			if (copyin((void *)data, &recv, sizeof (recv)))
248 				return (EFAULT);
249 			break;
250 		}
251 	} else {
252 		/* Convert 32-bit structures to native. */
253 		struct ipmi_req32 req32;
254 
255 		switch (cmd) {
256 		case IPMICTL_SEND_COMMAND_32:
257 			if (copyin((void *)data, &req32, sizeof (req32)))
258 				return (EFAULT);
259 
260 			req.addr = PTRIN(req32.addr);
261 			req.addr_len = req32.addr_len;
262 			req.msgid = req32.msgid;
263 			req.msg.netfn = req32.msg.netfn;
264 			req.msg.cmd = req32.msg.cmd;
265 			req.msg.data_len = req32.msg.data_len;
266 			req.msg.data = PTRIN(req32.msg.data);
267 
268 			cmd = IPMICTL_SEND_COMMAND;
269 			break;
270 
271 		case IPMICTL_RECEIVE_MSG_TRUNC_32:
272 		case IPMICTL_RECEIVE_MSG_32:
273 			if (copyin((void *)data, &recv32, sizeof (recv32)))
274 				return (EFAULT);
275 
276 			recv.addr = PTRIN(recv32.addr);
277 			recv.addr_len = recv32.addr_len;
278 			recv.msg.data_len = recv32.msg.data_len;
279 			recv.msg.data = PTRIN(recv32.msg.data);
280 
281 			orig_cmd = cmd;
282 			cmd = (cmd == IPMICTL_RECEIVE_MSG_TRUNC_32) ?
283 			    IPMICTL_RECEIVE_MSG_TRUNC : IPMICTL_RECEIVE_MSG;
284 			break;
285 		}
286 	}
287 
288 	switch (cmd) {
289 	case IPMICTL_SEND_COMMAND:
290 		IPMI_LOCK(sc);
291 		/* clear out old stuff in queue of stuff done */
292 		while ((kreq = TAILQ_FIRST(&dev->ipmi_completed_requests))
293 		    != NULL) {
294 			TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq,
295 			    ir_link);
296 			dev->ipmi_requests--;
297 			ipmi_free_request(kreq);
298 		}
299 		IPMI_UNLOCK(sc);
300 
301 		/* Check that we didn't get a ridiculous length */
302 		if (req.msg.data_len > IPMI_MAX_RX)
303 			return (EINVAL);
304 
305 		kreq = ipmi_alloc_request(dev, req.msgid,
306 		    IPMI_ADDR(req.msg.netfn, 0), req.msg.cmd,
307 		    req.msg.data_len, IPMI_MAX_RX);
308 		/* This struct is the same for 32/64 */
309 		if (req.msg.data_len > 0 &&
310 		    copyin(req.msg.data, kreq->ir_request, req.msg.data_len)) {
311 			ipmi_free_request(kreq);
312 			return (EFAULT);
313 		}
314 		IPMI_LOCK(sc);
315 		dev->ipmi_requests++;
316 		error = sc->ipmi_enqueue_request(sc, kreq);
317 		IPMI_UNLOCK(sc);
318 		if (error)
319 			return (error);
320 		break;
321 
322 	case IPMICTL_RECEIVE_MSG_TRUNC:
323 	case IPMICTL_RECEIVE_MSG:
324 		/* This struct is the same for 32/64 */
325 		if (copyin(recv.addr, &addr, sizeof (addr)))
326 			return (EFAULT);
327 
328 		IPMI_LOCK(sc);
329 		kreq = TAILQ_FIRST(&dev->ipmi_completed_requests);
330 		if (kreq == NULL) {
331 			IPMI_UNLOCK(sc);
332 			return (EAGAIN);
333 		}
334 		addr.channel = IPMI_BMC_CHANNEL;
335 		recv.recv_type = IPMI_RESPONSE_RECV_TYPE;
336 		recv.msgid = kreq->ir_msgid;
337 		recv.msg.netfn = IPMI_REPLY_ADDR(kreq->ir_addr) >> 2;
338 		recv.msg.cmd = kreq->ir_command;
339 		error = kreq->ir_error;
340 		if (error) {
341 			TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq,
342 			    ir_link);
343 			dev->ipmi_requests--;
344 			IPMI_UNLOCK(sc);
345 			ipmi_free_request(kreq);
346 			return (error);
347 		}
348 		len = kreq->ir_replylen + 1;
349 		if (recv.msg.data_len < len && cmd == IPMICTL_RECEIVE_MSG) {
350 			IPMI_UNLOCK(sc);
351 			return (EMSGSIZE);
352 		}
353 		TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, ir_link);
354 		dev->ipmi_requests--;
355 		IPMI_UNLOCK(sc);
356 		len = min(recv.msg.data_len, len);
357 		recv.msg.data_len = (unsigned short)len;
358 
359 		if (orig_cmd == IPMICTL_RECEIVE_MSG_TRUNC_32 ||
360 		    orig_cmd == IPMICTL_RECEIVE_MSG_32) {
361 			/* Update changed fields in 32-bit structure. */
362 			recv32.recv_type = recv.recv_type;
363 			recv32.msgid = (int32_t)recv.msgid;
364 			recv32.msg.netfn = recv.msg.netfn;
365 			recv32.msg.cmd = recv.msg.cmd;
366 			recv32.msg.data_len = recv.msg.data_len;
367 
368 			error = copyout(&recv32, (void *)data, sizeof (recv32));
369 		} else {
370 			error = copyout(&recv, (void *)data, sizeof (recv));
371 		}
372 
373 		/* This struct is the same for 32/64 */
374 		if (error == 0)
375 			error = copyout(&addr, recv.addr, sizeof (addr));
376 		if (error == 0)
377 			error = copyout(&kreq->ir_compcode, recv.msg.data, 1);
378 		if (error == 0)
379 			error = copyout(kreq->ir_reply, recv.msg.data + 1,
380 			    len - 1);
381 		ipmi_free_request(kreq);
382 
383 		if (error)
384 			return (EFAULT);
385 
386 		break;
387 
388 	case IPMICTL_SET_MY_ADDRESS_CMD:
389 		IPMI_LOCK(sc);
390 		if (copyin((void *)data, &dev->ipmi_address,
391 		    sizeof (dev->ipmi_address))) {
392 			IPMI_UNLOCK(sc);
393 			return (EFAULT);
394 		}
395 		IPMI_UNLOCK(sc);
396 		break;
397 
398 	case IPMICTL_GET_MY_ADDRESS_CMD:
399 		IPMI_LOCK(sc);
400 		if (copyout(&dev->ipmi_address, (void *)data,
401 		    sizeof (dev->ipmi_address))) {
402 			IPMI_UNLOCK(sc);
403 			return (EFAULT);
404 		}
405 		IPMI_UNLOCK(sc);
406 		break;
407 
408 	case IPMICTL_SET_MY_LUN_CMD:
409 		IPMI_LOCK(sc);
410 		if (copyin((void *)data, &t_lun, sizeof (t_lun))) {
411 			IPMI_UNLOCK(sc);
412 			return (EFAULT);
413 		}
414 		dev->ipmi_lun = t_lun & 0x3;
415 		IPMI_UNLOCK(sc);
416 		break;
417 
418 	case IPMICTL_GET_MY_LUN_CMD:
419 		IPMI_LOCK(sc);
420 		if (copyout(&dev->ipmi_lun, (void *)data,
421 		    sizeof (dev->ipmi_lun))) {
422 			IPMI_UNLOCK(sc);
423 			return (EFAULT);
424 		}
425 		IPMI_UNLOCK(sc);
426 		break;
427 
428 	case IPMICTL_SET_GETS_EVENTS_CMD:
429 		break;
430 
431 	case IPMICTL_REGISTER_FOR_CMD:
432 	case IPMICTL_UNREGISTER_FOR_CMD:
433 		return (EINVAL);
434 
435 	default:
436 		return (EINVAL);
437 	}
438 
439 	return (0);
440 }
441 
442 static int
443 ipmi_poll(dev_t dv, short events, int anyyet, short *reventsp,
444     pollhead_t **phpp)
445 {
446 	struct ipmi_device *dev;
447 	short revent = 0;
448 
449 	if ((dev = lookup_ipmidev_by_dev(dv)) == NULL)
450 		return (ENODEV);
451 
452 	if (events & (POLLIN | POLLRDNORM)) {
453 		if (!TAILQ_EMPTY(&dev->ipmi_completed_requests))
454 			revent |= events & (POLLIN | POLLRDNORM);
455 		if (dev->ipmi_requests == 0)
456 			revent |= POLLERR;
457 	}
458 
459 	if (revent == 0) {
460 		/* nothing has occurred */
461 		if (!anyyet)
462 			*phpp = dev->ipmi_pollhead;
463 	}
464 
465 	*reventsp = revent;
466 	return (0);
467 }
468 
469 /*ARGSUSED*/
470 static int
471 ipmi_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
472 {
473 	switch (cmd) {
474 	case DDI_INFO_DEVT2DEVINFO:
475 		*resultp = ipmi_dip;
476 		return (DDI_SUCCESS);
477 	case DDI_INFO_DEVT2INSTANCE:
478 		*resultp = NULL;
479 		return (DDI_SUCCESS);
480 	}
481 	return (DDI_FAILURE);
482 }
483 
484 static void
485 ipmi_cleanup(dev_info_t *dip)
486 {
487 	/* poke the taskq so that it can terminate */
488 	IPMI_LOCK(sc);
489 	sc->ipmi_detaching = 1;
490 	cv_signal(&sc->ipmi_request_added);
491 	IPMI_UNLOCK(sc);
492 
493 	ipmi_shutdown(sc);
494 	ddi_remove_minor_node(dip, NULL);
495 	ipmi_dip = NULL;
496 
497 	list_destroy(&dev_list);
498 	id_space_destroy(minor_ids);
499 
500 	sc->ipmi_detaching = 0;
501 }
502 
503 static int
504 ipmi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
505 {
506 	if (cmd != DDI_ATTACH)
507 		return (DDI_FAILURE);
508 
509 	/* this driver only supports one device instance */
510 	if (ddi_get_instance(dip) != 0) {
511 		cmn_err(CE_WARN,
512 		    "!not attaching to non-zero device instance %d",
513 		    ddi_get_instance(dip));
514 		return (DDI_FAILURE);
515 	}
516 
517 	if (get_smbios_ipmi_info() == DDI_FAILURE)
518 		return (DDI_FAILURE);
519 
520 	/*
521 	 * Support for the other types (SMIC, SSIF) should be added here.
522 	 */
523 	switch (sc->ipmi_io_type) {
524 	case SMB_IPMI_T_KCS:
525 		if (ipmi_kcs_attach(sc) != 0)
526 			return (DDI_FAILURE);
527 		break;
528 	default:
529 		return (DDI_FAILURE);
530 	}
531 	ipmi_found = B_TRUE;
532 
533 	if (ddi_create_minor_node(dip, "ipmi", S_IFCHR, 0, DDI_PSEUDO,
534 	    0) == DDI_FAILURE) {
535 		cmn_err(CE_WARN, "!attach could not create minor node");
536 		ddi_remove_minor_node(dip, NULL);
537 		return (DDI_FAILURE);
538 	}
539 
540 	ipmi_dip = dip;
541 
542 	list_create(&dev_list, sizeof (ipmi_device_t),
543 	    offsetof(ipmi_device_t, ipmi_node));
544 
545 	/* Create ID space for open devs.  ID 0 is reserved. */
546 	minor_ids = id_space_create("ipmi_id_space", 1, 128);
547 
548 	if (ipmi_startup(sc) != B_TRUE) {
549 		ipmi_cleanup(dip);
550 		return (DDI_FAILURE);
551 	}
552 
553 	ipmi_attached = B_TRUE;
554 
555 	return (DDI_SUCCESS);
556 }
557 
558 static int
559 ipmi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
560 {
561 	if (cmd != DDI_DETACH)
562 		return (DDI_FAILURE);
563 
564 	if (ipmi_found == B_FALSE)
565 		return (DDI_SUCCESS);
566 
567 	if (!list_is_empty(&dev_list))
568 		return (DDI_FAILURE);
569 
570 	ipmi_cleanup(dip);
571 
572 	ipmi_attached = B_FALSE;
573 	return (DDI_SUCCESS);
574 }
575 
576 static struct cb_ops ipmi_cb_ops = {
577 	ipmi_open,
578 	ipmi_close,
579 	nodev,			/* strategy */
580 	nodev,			/* print */
581 	nodev,			/* dump */
582 	nodev,			/* read */
583 	nodev,			/* write */
584 	ipmi_ioctl,
585 	nodev,			/* devmap */
586 	nodev,			/* mmap */
587 	nodev,			/* segmap */
588 	ipmi_poll,
589 	ddi_prop_op,
590 	NULL,			/* streamtab */
591 	D_NEW | D_MP,		/* flags */
592 	CB_REV,
593 	nodev,			/* awread */
594 	nodev			/* awrite */
595 };
596 
597 static struct dev_ops ipmi_ops = {
598 	DEVO_REV,
599 	0,			/* reference count */
600 	ipmi_info,
601 	nulldev,		/* identify */
602 	nulldev,		/* probe */
603 	ipmi_attach,
604 	ipmi_detach,
605 	nodev,			/* reset */
606 	&ipmi_cb_ops,
607 	NULL,			/* bus ops */
608 	NULL,			/* power */
609 	ddi_quiesce_not_needed,
610 };
611 
612 static struct modldrv md = {
613 	&mod_driverops, "ipmi driver", &ipmi_ops
614 };
615 
616 static struct modlinkage ml = {
617 	MODREV_1, &md, NULL
618 };
619 
620 int
621 _init(void)
622 {
623 	return (mod_install(&ml));
624 }
625 
626 int
627 _fini(void)
628 {
629 	return (mod_remove(&ml));
630 }
631 
632 int
633 _info(struct modinfo *mip)
634 {
635 	return (mod_info(&ml, mip));
636 }
637