4282 Race between ipmi_close() and kcs_loop() could lead to panic
[illumos-gate.git] / usr / src / uts / intel / io / ipmi / ipmi_main.c
blobb77434a0a5980766ebd8409cc9bb96ecae0cb41c
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2012, Joyent, Inc. All rights reserved.
24 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
28 * The ipmi driver is an openipmi compatible IPMI driver based on the FreeBSD
29 * driver.
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.
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"
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;
69 #define PTRIN(p) ((void *)(uintptr_t)(p))
70 #define PTROUT(p) ((uintptr_t)(p))
73 * Use the SMBIOS info to determine if the system has an IPMI.
75 static int
76 get_smbios_ipmi_info(void)
78 smbios_ipmi_t ipmi;
80 if (ksmbios == NULL || smbios_info_ipmi(ksmbios, &ipmi) == SMB_ERR)
81 return (DDI_FAILURE);
83 cmn_err(CE_CONT, "!SMBIOS type 0x%x, addr 0x%llx", ipmi.smbip_type,
84 (long long unsigned int)(ipmi.smbip_addr));
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.
91 if (ipmi.smbip_addr == NULL) {
92 cmn_err(CE_WARN, "!SMBIOS: Invalid base address");
93 return (DDI_FAILURE);
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;
113 break;
114 default:
115 return (DDI_FAILURE);
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);
124 sc->ipmi_io_irq = ipmi.smbip_intr;
125 return (DDI_SUCCESS);
128 static ipmi_device_t *
129 lookup_ipmidev_by_dev(dev_t dev)
131 ipmi_device_t *p;
133 for (p = list_head(&dev_list); p; p = list_next(&dev_list, p)) {
134 if (dev == p->ipmi_dev)
135 return (p);
137 return (NULL);
141 * Each open returns a new pseudo device.
143 /*ARGSUSED*/
144 static int
145 ipmi_open(dev_t *devp, int flag, int otyp, cred_t *cred)
147 minor_t minor;
148 ipmi_device_t *dev;
150 if (ipmi_attached == B_FALSE)
151 return (ENXIO);
153 if (ipmi_found == B_FALSE)
154 return (ENODEV);
156 /* exclusive opens are not supported */
157 if (flag & FEXCL)
158 return (ENOTSUP);
160 if ((minor = (minor_t)id_alloc_nosleep(minor_ids)) == 0)
161 return (ENODEV);
163 /* Initialize the per file descriptor data. */
164 dev = kmem_zalloc(sizeof (ipmi_device_t), KM_SLEEP);
166 dev->ipmi_pollhead = kmem_zalloc(sizeof (pollhead_t), KM_SLEEP);
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);
175 list_insert_head(&dev_list, dev);
177 return (0);
180 /*ARGSUSED*/
181 static int
182 ipmi_close(dev_t dev, int flag, int otyp, cred_t *cred)
184 ipmi_device_t *dp;
185 struct ipmi_request *req, *next;
187 if ((dp = lookup_ipmidev_by_dev(dev)) == NULL)
188 return (ENODEV);
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);
196 if (req->ir_owner == dp) {
197 TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link);
198 ipmi_free_request(req);
200 req = next;
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);
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);
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));
220 return (0);
223 /*ARGSUSED*/
224 static int
225 ipmi_ioctl(dev_t dv, int cmd, intptr_t data, int flags, cred_t *cr, int *rvalp)
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;
238 if (secpolicy_sys_config(cr, B_FALSE) != 0)
239 return (EPERM);
241 if ((dev = lookup_ipmidev_by_dev(dv)) == NULL)
242 return (ENODEV);
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;
257 } else {
258 /* Convert 32-bit structures to native. */
259 struct ipmi_req32 req32;
261 switch (cmd) {
262 case IPMICTL_SEND_COMMAND_32:
263 if (copyin((void *)data, &req32, sizeof (req32)))
264 return (EFAULT);
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);
274 cmd = IPMICTL_SEND_COMMAND;
275 break;
277 case IPMICTL_RECEIVE_MSG_TRUNC_32:
278 case IPMICTL_RECEIVE_MSG_32:
279 if (copyin((void *)data, &recv32, sizeof (recv32)))
280 return (EFAULT);
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);
287 orig_cmd = cmd;
288 cmd = (cmd == IPMICTL_RECEIVE_MSG_TRUNC_32) ?
289 IPMICTL_RECEIVE_MSG_TRUNC : IPMICTL_RECEIVE_MSG;
290 break;
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);
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);
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;
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);
323 IPMI_LOCK(sc);
324 kreq = TAILQ_FIRST(&dev->ipmi_completed_requests);
325 if (kreq == NULL) {
326 IPMI_UNLOCK(sc);
327 return (EAGAIN);
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);
343 len = kreq->ir_replylen + 1;
344 if (recv.msg.data_len < len && cmd == IPMICTL_RECEIVE_MSG) {
345 IPMI_UNLOCK(sc);
346 return (EMSGSIZE);
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;
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;
363 error = copyout(&recv32, (void *)data, sizeof (recv32));
364 } else {
365 error = copyout(&recv, (void *)data, sizeof (recv));
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);
378 if (error)
379 return (EFAULT);
381 break;
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);
390 IPMI_UNLOCK(sc);
391 break;
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);
400 IPMI_UNLOCK(sc);
401 break;
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);
409 dev->ipmi_lun = t_lun & 0x3;
410 IPMI_UNLOCK(sc);
411 break;
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);
420 IPMI_UNLOCK(sc);
421 break;
423 case IPMICTL_SET_GETS_EVENTS_CMD:
424 break;
426 case IPMICTL_REGISTER_FOR_CMD:
427 case IPMICTL_UNREGISTER_FOR_CMD:
428 return (EINVAL);
430 default:
431 return (EINVAL);
434 return (0);
437 static int
438 ipmi_poll(dev_t dv, short events, int anyyet, short *reventsp,
439 pollhead_t **phpp)
441 struct ipmi_device *dev;
442 short revent = 0;
444 if ((dev = lookup_ipmidev_by_dev(dv)) == NULL)
445 return (ENODEV);
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;
454 if (revent == 0) {
455 /* nothing has occurred */
456 if (!anyyet)
457 *phpp = dev->ipmi_pollhead;
460 *reventsp = revent;
461 return (0);
464 /*ARGSUSED*/
465 static int
466 ipmi_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
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);
476 return (DDI_FAILURE);
479 static void
480 ipmi_cleanup(dev_info_t *dip)
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);
488 ipmi_shutdown(sc);
489 ddi_remove_minor_node(dip, NULL);
490 ipmi_dip = NULL;
492 list_destroy(&dev_list);
493 id_space_destroy(minor_ids);
495 sc->ipmi_detaching = 0;
498 static int
499 ipmi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
501 if (cmd != DDI_ATTACH)
502 return (DDI_FAILURE);
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);
512 if (get_smbios_ipmi_info() == DDI_FAILURE)
513 return (DDI_FAILURE);
516 * Support for the other types (SMIC, SSIF) should be added here.
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);
526 ipmi_found = B_TRUE;
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);
535 ipmi_dip = dip;
537 list_create(&dev_list, sizeof (ipmi_device_t),
538 offsetof(ipmi_device_t, ipmi_node));
540 /* Create ID space for open devs. ID 0 is reserved. */
541 minor_ids = id_space_create("ipmi_id_space", 1, 128);
543 if (ipmi_startup(sc) != B_TRUE) {
544 ipmi_cleanup(dip);
545 return (DDI_FAILURE);
548 ipmi_attached = B_TRUE;
550 return (DDI_SUCCESS);
553 static int
554 ipmi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
556 if (cmd != DDI_DETACH)
557 return (DDI_FAILURE);
559 if (ipmi_found == B_FALSE)
560 return (DDI_SUCCESS);
562 if (!list_is_empty(&dev_list))
563 return (DDI_FAILURE);
565 ipmi_cleanup(dip);
567 ipmi_attached = B_FALSE;
568 return (DDI_SUCCESS);
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 */
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,
607 static struct modldrv md = {
608 &mod_driverops, "ipmi driver", &ipmi_ops
611 static struct modlinkage ml = {
612 MODREV_1, &md, NULL
616 _init(void)
618 return (mod_install(&ml));
622 _fini(void)
624 return (mod_remove(&ml));
628 _info(struct modinfo *mip)
630 return (mod_info(&ml, mip));