xref: /titanic_50/usr/src/uts/intel/io/ipmi/ipmi_main.c (revision c92bf99ee60c0b1fe1289fef2b4034126c7867a5)
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  */
25 
26 /*
27  * The ipmi driver is an openipmi compatible IPMI driver based on the FreeBSD
28  * driver.
29  *
30  * The current implementation has several limitations:
31  * 1) It only does discovery through the SMBIOS.  The FreeBSD driver has
32  *    several additional ways to discover the IPMI device (acpi, bus checking,
33  *    etc.).  This support could be ported if necessary.
34  * 2) The driver currently only supports the IPMI KCS_MODE mode (reported
35  *    through the SMBIOS as SMBIOS SMB_IPMI_T_KCS). Support for the other modes
36  *    (BT_MODE, SMIC_MODE, SSIF_MODE) could be ported if necessary.
37  * 3) The driver does not currently set up an IPMI watchdog.  This also could
38  *    be ported if necessary.
39  */
40 
41 #include <sys/devops.h>
42 #include <sys/conf.h>
43 #include <sys/modctl.h>
44 #include <sys/types.h>
45 #include <sys/file.h>
46 #include <sys/errno.h>
47 #include <sys/open.h>
48 #include <sys/cred.h>
49 #include <sys/uio.h>
50 #include <sys/stat.h>
51 #include <sys/cmn_err.h>
52 #include <sys/ddi.h>
53 #include <sys/sunddi.h>
54 #include <sys/smbios.h>
55 #include <sys/smbios_impl.h>
56 #include <sys/policy.h>
57 #include <sys/ipmi.h>
58 #include "ipmivars.h"
59 
60 static dev_info_t		*ipmi_dip;
61 static boolean_t		ipmi_attached = B_FALSE;
62 static boolean_t		ipmi_found = B_FALSE;
63 static struct ipmi_softc	softc;
64 static struct ipmi_softc	*sc = &softc;
65 static list_t			dev_list;
66 static id_space_t		*minor_ids;
67 
68 #define	PTRIN(p)	((void *)(uintptr_t)(p))
69 #define	PTROUT(p)	((uintptr_t)(p))
70 
71 /*
72  * Use the SMBIOS info to determine if the system has an IPMI.
73  */
74 static int
75 get_smbios_ipmi_info(void)
76 {
77 	smbios_ipmi_t ipmi;
78 
79 	if (ksmbios == NULL || smbios_info_ipmi(ksmbios, &ipmi) == SMB_ERR)
80 		return (DDI_FAILURE);
81 
82 	cmn_err(CE_CONT, "!SMBIOS type 0x%x, addr 0x%llx", ipmi.smbip_type,
83 	    (long long unsigned int)(ipmi.smbip_addr));
84 
85 	/*
86 	 * Some systems have a bios that will report an IPMI device even when
87 	 * it is not installed. In this case we see 0x0 as the base address.
88 	 * If we see this address, assume the device is not really present.
89 	 */
90 	if (ipmi.smbip_addr == NULL) {
91 		cmn_err(CE_WARN, "!SMBIOS: Invalid base address");
92 		return (DDI_FAILURE);
93 	}
94 
95 	sc->ipmi_io_type = ipmi.smbip_type;
96 	switch (ipmi.smbip_type) {
97 	case SMB_IPMI_T_KCS:
98 	case SMB_IPMI_T_SMIC:
99 		sc->ipmi_io_address = ipmi.smbip_addr;
100 		sc->ipmi_io_mode = (ipmi.smbip_flags & SMB_IPMI_F_IOADDR) ?
101 		    1 : 0;
102 		sc->ipmi_io_spacing = ipmi.smbip_regspacing;
103 		break;
104 	case SMB_IPMI_T_SSIF:
105 		if ((ipmi.smbip_addr & 0xffffffffffffff00) != 0) {
106 			cmn_err(CE_WARN, "!SMBIOS: Invalid SSIF SMBus address, "
107 			    "using BMC I2C slave address instead");
108 			sc->ipmi_io_address = ipmi.smbip_i2c;
109 		} else {
110 			sc->ipmi_io_address = ipmi.smbip_addr;
111 		}
112 		break;
113 	default:
114 		return (DDI_FAILURE);
115 	}
116 
117 	if (ipmi.smbip_intr > 15) {
118 		cmn_err(CE_WARN, "!SMBIOS: Non-ISA IRQ %d for IPMI",
119 		    ipmi.smbip_intr);
120 		return (DDI_FAILURE);
121 	}
122 
123 	sc->ipmi_io_irq = ipmi.smbip_intr;
124 	return (DDI_SUCCESS);
125 }
126 
127 static ipmi_device_t *
128 lookup_ipmidev_by_dev(dev_t dev)
129 {
130 	ipmi_device_t	*p;
131 
132 	for (p = list_head(&dev_list); p; p = list_next(&dev_list, p)) {
133 		if (dev == p->ipmi_dev)
134 			return (p);
135 	}
136 	return (NULL);
137 }
138 
139 /*
140  * Each open returns a new pseudo device.
141  */
142 /*ARGSUSED*/
143 static int
144 ipmi_open(dev_t *devp, int flag, int otyp, cred_t *cred)
145 {
146 	minor_t minor;
147 	ipmi_device_t *dev;
148 
149 	if (ipmi_attached == B_FALSE)
150 		return (ENXIO);
151 
152 	if (ipmi_found == B_FALSE)
153 		return (ENODEV);
154 
155 	/* exclusive opens are not supported */
156 	if (flag & FEXCL)
157 		return (ENOTSUP);
158 
159 	if ((minor = (minor_t)id_alloc_nosleep(minor_ids)) == 0)
160 		return (ENODEV);
161 
162 	/* Initialize the per file descriptor data. */
163 	dev = kmem_zalloc(sizeof (ipmi_device_t), KM_SLEEP);
164 
165 	dev->ipmi_pollhead = kmem_zalloc(sizeof (pollhead_t), KM_SLEEP);
166 
167 	TAILQ_INIT(&dev->ipmi_completed_requests);
168 	dev->ipmi_address = IPMI_BMC_SLAVE_ADDR;
169 	dev->ipmi_lun = IPMI_BMC_SMS_LUN;
170 	*devp = makedevice(getmajor(*devp), minor);
171 	dev->ipmi_dev = *devp;
172 
173 	list_insert_head(&dev_list, dev);
174 
175 	return (0);
176 }
177 
178 /*ARGSUSED*/
179 static int
180 ipmi_close(dev_t dev, int flag, int otyp, cred_t *cred)
181 {
182 	ipmi_device_t *dp;
183 	struct ipmi_request *req, *next;
184 
185 	if ((dp = lookup_ipmidev_by_dev(dev)) == NULL)
186 		return (ENODEV);
187 
188 	IPMI_LOCK(sc);
189 	/* remove any pending requests */
190 	req = TAILQ_FIRST(&sc->ipmi_pending_requests);
191 	while (req != NULL) {
192 		next = TAILQ_NEXT(req, ir_link);
193 
194 		if (req->ir_owner == dp) {
195 			TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link);
196 			ipmi_free_request(req);
197 		}
198 		req = next;
199 	}
200 	IPMI_UNLOCK(sc);
201 
202 	/* remove any requests in queue of stuff completed */
203 	while ((req = TAILQ_FIRST(&dp->ipmi_completed_requests)) != NULL) {
204 		TAILQ_REMOVE(&dp->ipmi_completed_requests, req, ir_link);
205 		ipmi_free_request(req);
206 	}
207 
208 	list_remove(&dev_list, dp);
209 	id_free(minor_ids, getminor(dev));
210 	kmem_free(dp->ipmi_pollhead, sizeof (pollhead_t));
211 	kmem_free(dp, sizeof (ipmi_device_t));
212 
213 	return (0);
214 }
215 
216 /*ARGSUSED*/
217 static int
218 ipmi_ioctl(dev_t dv, int cmd, intptr_t data, int flags, cred_t *cr, int *rvalp)
219 {
220 	struct ipmi_device *dev;
221 	struct ipmi_request *kreq;
222 	struct ipmi_req req;
223 	struct ipmi_recv recv;
224 	struct ipmi_recv32 recv32;
225 	struct ipmi_addr addr;
226 	int error, len;
227 	model_t model;
228 	int orig_cmd = 0;
229 	uchar_t	t_lun;
230 
231 	if (secpolicy_sys_config(cr, B_FALSE) != 0)
232 		return (EPERM);
233 
234 	if ((dev = lookup_ipmidev_by_dev(dv)) == NULL)
235 		return (ENODEV);
236 
237 	model = get_udatamodel();
238 	if (model == DATAMODEL_NATIVE) {
239 		switch (cmd) {
240 		case IPMICTL_SEND_COMMAND:
241 			if (copyin((void *)data, &req, sizeof (req)))
242 				return (EFAULT);
243 			break;
244 		case IPMICTL_RECEIVE_MSG_TRUNC:
245 		case IPMICTL_RECEIVE_MSG:
246 			if (copyin((void *)data, &recv, sizeof (recv)))
247 				return (EFAULT);
248 			break;
249 		}
250 	} else {
251 		/* Convert 32-bit structures to native. */
252 		struct ipmi_req32 req32;
253 
254 		switch (cmd) {
255 		case IPMICTL_SEND_COMMAND_32:
256 			if (copyin((void *)data, &req32, sizeof (req32)))
257 				return (EFAULT);
258 
259 			req.addr = PTRIN(req32.addr);
260 			req.addr_len = req32.addr_len;
261 			req.msgid = req32.msgid;
262 			req.msg.netfn = req32.msg.netfn;
263 			req.msg.cmd = req32.msg.cmd;
264 			req.msg.data_len = req32.msg.data_len;
265 			req.msg.data = PTRIN(req32.msg.data);
266 
267 			cmd = IPMICTL_SEND_COMMAND;
268 			break;
269 
270 		case IPMICTL_RECEIVE_MSG_TRUNC_32:
271 		case IPMICTL_RECEIVE_MSG_32:
272 			if (copyin((void *)data, &recv32, sizeof (recv32)))
273 				return (EFAULT);
274 
275 			recv.addr = PTRIN(recv32.addr);
276 			recv.addr_len = recv32.addr_len;
277 			recv.msg.data_len = recv32.msg.data_len;
278 			recv.msg.data = PTRIN(recv32.msg.data);
279 
280 			orig_cmd = cmd;
281 			cmd = (cmd == IPMICTL_RECEIVE_MSG_TRUNC_32) ?
282 			    IPMICTL_RECEIVE_MSG_TRUNC : IPMICTL_RECEIVE_MSG;
283 			break;
284 		}
285 	}
286 
287 	switch (cmd) {
288 	case IPMICTL_SEND_COMMAND:
289 		IPMI_LOCK(sc);
290 		/* clear out old stuff in queue of stuff done */
291 		while ((kreq = TAILQ_FIRST(&dev->ipmi_completed_requests))
292 		    != NULL) {
293 			TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq,
294 			    ir_link);
295 			dev->ipmi_requests--;
296 			ipmi_free_request(kreq);
297 		}
298 		IPMI_UNLOCK(sc);
299 
300 		/* Check that we didn't get a ridiculous length */
301 		if (req.msg.data_len > IPMI_MAX_RX)
302 			return (EINVAL);
303 
304 		kreq = ipmi_alloc_request(dev, req.msgid,
305 		    IPMI_ADDR(req.msg.netfn, 0), req.msg.cmd,
306 		    req.msg.data_len, IPMI_MAX_RX);
307 		/* This struct is the same for 32/64 */
308 		if (req.msg.data_len > 0 &&
309 		    copyin(req.msg.data, kreq->ir_request, req.msg.data_len)) {
310 			ipmi_free_request(kreq);
311 			return (EFAULT);
312 		}
313 		IPMI_LOCK(sc);
314 		dev->ipmi_requests++;
315 		error = sc->ipmi_enqueue_request(sc, kreq);
316 		IPMI_UNLOCK(sc);
317 		if (error)
318 			return (error);
319 		break;
320 
321 	case IPMICTL_RECEIVE_MSG_TRUNC:
322 	case IPMICTL_RECEIVE_MSG:
323 		/* This struct is the same for 32/64 */
324 		if (copyin(recv.addr, &addr, sizeof (addr)))
325 			return (EFAULT);
326 
327 		IPMI_LOCK(sc);
328 		kreq = TAILQ_FIRST(&dev->ipmi_completed_requests);
329 		if (kreq == NULL) {
330 			IPMI_UNLOCK(sc);
331 			return (EAGAIN);
332 		}
333 		addr.channel = IPMI_BMC_CHANNEL;
334 		recv.recv_type = IPMI_RESPONSE_RECV_TYPE;
335 		recv.msgid = kreq->ir_msgid;
336 		recv.msg.netfn = IPMI_REPLY_ADDR(kreq->ir_addr) >> 2;
337 		recv.msg.cmd = kreq->ir_command;
338 		error = kreq->ir_error;
339 		if (error) {
340 			TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq,
341 			    ir_link);
342 			dev->ipmi_requests--;
343 			IPMI_UNLOCK(sc);
344 			ipmi_free_request(kreq);
345 			return (error);
346 		}
347 		len = kreq->ir_replylen + 1;
348 		if (recv.msg.data_len < len && cmd == IPMICTL_RECEIVE_MSG) {
349 			IPMI_UNLOCK(sc);
350 			ipmi_free_request(kreq);
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 int
485 ipmi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
486 {
487 	if (cmd != DDI_ATTACH)
488 		return (DDI_FAILURE);
489 
490 	if (get_smbios_ipmi_info() == DDI_FAILURE)
491 		return (DDI_FAILURE);
492 
493 	/*
494 	 * Support for the other types (SMIC, SSIF) should be added here.
495 	 */
496 	switch (sc->ipmi_io_type) {
497 	case SMB_IPMI_T_KCS:
498 		if (ipmi_kcs_attach(sc) != 0)
499 			return (DDI_FAILURE);
500 		break;
501 	default:
502 		return (DDI_FAILURE);
503 	}
504 	ipmi_found = B_TRUE;
505 
506 	if (ddi_create_minor_node(dip, "ipmi", S_IFCHR, 0, DDI_PSEUDO,
507 	    0) == DDI_FAILURE) {
508 		cmn_err(CE_WARN, "!attach could not create minor node");
509 		ddi_remove_minor_node(dip, NULL);
510 		return (DDI_FAILURE);
511 	}
512 
513 	ipmi_dip = dip;
514 
515 	list_create(&dev_list, sizeof (ipmi_device_t),
516 	    offsetof(ipmi_device_t, ipmi_node));
517 
518 	/* Create ID space for open devs.  ID 0 is reserved. */
519 	minor_ids = id_space_create("ipmi_id_space", 1, 128);
520 
521 	ipmi_startup(sc);
522 	ipmi_attached = B_TRUE;
523 
524 	return (DDI_SUCCESS);
525 }
526 
527 static int
528 ipmi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
529 {
530 	if (cmd != DDI_DETACH)
531 		return (DDI_FAILURE);
532 
533 	if (ipmi_found == B_FALSE)
534 		return (DDI_SUCCESS);
535 
536 	if (!list_is_empty(&dev_list))
537 		return (DDI_FAILURE);
538 
539 	/* poke the taskq so that it can terminate */
540 	sc->ipmi_detaching = 1;
541 	cv_signal(&sc->ipmi_request_added);
542 
543 	ddi_remove_minor_node(dip, NULL);
544 	ipmi_dip = NULL;
545 
546 	taskq_destroy(sc->ipmi_kthread);
547 	list_destroy(&dev_list);
548 	id_space_destroy(minor_ids);
549 
550 	ipmi_attached = B_FALSE;
551 	return (DDI_SUCCESS);
552 }
553 
554 static struct cb_ops ipmi_cb_ops = {
555 	ipmi_open,
556 	ipmi_close,
557 	nodev,			/* strategy */
558 	nodev,			/* print */
559 	nodev,			/* dump */
560 	nodev,			/* read */
561 	nodev,			/* write */
562 	ipmi_ioctl,
563 	nodev,			/* devmap */
564 	nodev,			/* mmap */
565 	nodev,			/* segmap */
566 	ipmi_poll,
567 	ddi_prop_op,
568 	NULL,			/* streamtab */
569 	D_NEW | D_MP		/* flags */
570 };
571 
572 static struct dev_ops ipmi_ops = {
573 	DEVO_REV,
574 	0,			/* reference count */
575 	ipmi_info,
576 	nulldev,		/* identify */
577 	nulldev,		/* probe */
578 	ipmi_attach,
579 	ipmi_detach,
580 	nodev,			/* reset */
581 	&ipmi_cb_ops,
582 	NULL,			/* bus ops */
583 	NULL,			/* power */
584 	ddi_quiesce_not_needed,
585 };
586 
587 static struct modldrv md = {
588 	&mod_driverops, "ipmi driver", &ipmi_ops
589 };
590 
591 static struct modlinkage ml = {
592 	MODREV_1, &md, NULL
593 };
594 
595 int
596 _init(void)
597 {
598 	return (mod_install(&ml));
599 }
600 
601 int
602 _fini(void)
603 {
604 	return (mod_remove(&ml));
605 }
606 
607 int
608 _info(struct modinfo *mip)
609 {
610 	return (mod_info(&ml, mip));
611 }
612