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]
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
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>
44 #include <sys/modctl.h>
45 #include <sys/types.h>
47 #include <sys/errno.h>
52 #include <sys/cmn_err.h>
54 #include <sys/sunddi.h>
55 #include <sys/smbios.h>
56 #include <sys/smbios_impl.h>
57 #include <sys/policy.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.
76 get_smbios_ipmi_info(void)
80 if (ksmbios
== NULL
|| smbios_info_ipmi(ksmbios
, &ipmi
) == SMB_ERR
)
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");
96 sc
->ipmi_io_type
= ipmi
.smbip_type
;
97 switch (ipmi
.smbip_type
) {
100 sc
->ipmi_io_address
= ipmi
.smbip_addr
;
101 sc
->ipmi_io_mode
= (ipmi
.smbip_flags
& SMB_IPMI_F_IOADDR
) ?
103 sc
->ipmi_io_spacing
= ipmi
.smbip_regspacing
;
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
;
111 sc
->ipmi_io_address
= ipmi
.smbip_addr
;
115 return (DDI_FAILURE
);
118 if (ipmi
.smbip_intr
> 15) {
119 cmn_err(CE_WARN
, "!SMBIOS: Non-ISA IRQ %d for IPMI",
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
)
133 for (p
= list_head(&dev_list
); p
; p
= list_next(&dev_list
, p
)) {
134 if (dev
== p
->ipmi_dev
)
141 * Each open returns a new pseudo device.
145 ipmi_open(dev_t
*devp
, int flag
, int otyp
, cred_t
*cred
)
150 if (ipmi_attached
== B_FALSE
)
153 if (ipmi_found
== B_FALSE
)
156 /* exclusive opens are not supported */
160 if ((minor
= (minor_t
)id_alloc_nosleep(minor_ids
)) == 0)
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
);
182 ipmi_close(dev_t dev
, int flag
, int otyp
, cred_t
*cred
)
185 struct ipmi_request
*req
, *next
;
187 if ((dp
= lookup_ipmidev_by_dev(dev
)) == NULL
)
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
);
203 dp
->ipmi_status
|= IPMI_CLOSING
;
204 while (dp
->ipmi_status
& IPMI_BUSY
)
205 cv_wait(&dp
->ipmi_cv
, &sc
->ipmi_lock
);
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
));
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
;
230 struct ipmi_recv recv
;
231 struct ipmi_recv32 recv32
;
232 struct ipmi_addr addr
;
238 if (secpolicy_sys_config(cr
, B_FALSE
) != 0)
241 if ((dev
= lookup_ipmidev_by_dev(dv
)) == NULL
)
244 model
= get_udatamodel();
245 if (model
== DATAMODEL_NATIVE
) {
247 case IPMICTL_SEND_COMMAND
:
248 if (copyin((void *)data
, &req
, sizeof (req
)))
251 case IPMICTL_RECEIVE_MSG_TRUNC
:
252 case IPMICTL_RECEIVE_MSG
:
253 if (copyin((void *)data
, &recv
, sizeof (recv
)))
258 /* Convert 32-bit structures to native. */
259 struct ipmi_req32 req32
;
262 case IPMICTL_SEND_COMMAND_32
:
263 if (copyin((void *)data
, &req32
, sizeof (req32
)))
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
;
277 case IPMICTL_RECEIVE_MSG_TRUNC_32
:
278 case IPMICTL_RECEIVE_MSG_32
:
279 if (copyin((void *)data
, &recv32
, sizeof (recv32
)))
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
);
288 cmd
= (cmd
== IPMICTL_RECEIVE_MSG_TRUNC_32
) ?
289 IPMICTL_RECEIVE_MSG_TRUNC
: IPMICTL_RECEIVE_MSG
;
295 case IPMICTL_SEND_COMMAND
:
296 /* Check that we didn't get a ridiculous length */
297 if (req
.msg
.data_len
> IPMI_MAX_RX
)
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
);
310 dev
->ipmi_requests
++;
311 error
= sc
->ipmi_enqueue_request(sc
, kreq
);
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
)))
324 kreq
= TAILQ_FIRST(&dev
->ipmi_completed_requests
);
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
;
336 TAILQ_REMOVE(&dev
->ipmi_completed_requests
, kreq
,
338 dev
->ipmi_requests
--;
340 ipmi_free_request(kreq
);
343 len
= kreq
->ir_replylen
+ 1;
344 if (recv
.msg
.data_len
< len
&& cmd
== IPMICTL_RECEIVE_MSG
) {
348 TAILQ_REMOVE(&dev
->ipmi_completed_requests
, kreq
, ir_link
);
349 dev
->ipmi_requests
--;
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
));
365 error
= copyout(&recv
, (void *)data
, sizeof (recv
));
368 /* This struct is the same for 32/64 */
370 error
= copyout(&addr
, recv
.addr
, sizeof (addr
));
372 error
= copyout(&kreq
->ir_compcode
, recv
.msg
.data
, 1);
374 error
= copyout(kreq
->ir_reply
, recv
.msg
.data
+ 1,
376 ipmi_free_request(kreq
);
383 case IPMICTL_SET_MY_ADDRESS_CMD
:
385 if (copyin((void *)data
, &dev
->ipmi_address
,
386 sizeof (dev
->ipmi_address
))) {
393 case IPMICTL_GET_MY_ADDRESS_CMD
:
395 if (copyout(&dev
->ipmi_address
, (void *)data
,
396 sizeof (dev
->ipmi_address
))) {
403 case IPMICTL_SET_MY_LUN_CMD
:
405 if (copyin((void *)data
, &t_lun
, sizeof (t_lun
))) {
409 dev
->ipmi_lun
= t_lun
& 0x3;
413 case IPMICTL_GET_MY_LUN_CMD
:
415 if (copyout(&dev
->ipmi_lun
, (void *)data
,
416 sizeof (dev
->ipmi_lun
))) {
423 case IPMICTL_SET_GETS_EVENTS_CMD
:
426 case IPMICTL_REGISTER_FOR_CMD
:
427 case IPMICTL_UNREGISTER_FOR_CMD
:
438 ipmi_poll(dev_t dv
, short events
, int anyyet
, short *reventsp
,
441 struct ipmi_device
*dev
;
444 if ((dev
= lookup_ipmidev_by_dev(dv
)) == NULL
)
447 if (events
& (POLLIN
| POLLRDNORM
)) {
448 if (!TAILQ_EMPTY(&dev
->ipmi_completed_requests
))
449 revent
|= events
& (POLLIN
| POLLRDNORM
);
450 if (dev
->ipmi_requests
== 0)
455 /* nothing has occurred */
457 *phpp
= dev
->ipmi_pollhead
;
466 ipmi_info(dev_info_t
*dip
, ddi_info_cmd_t cmd
, void *arg
, void **resultp
)
469 case DDI_INFO_DEVT2DEVINFO
:
471 return (DDI_SUCCESS
);
472 case DDI_INFO_DEVT2INSTANCE
:
474 return (DDI_SUCCESS
);
476 return (DDI_FAILURE
);
480 ipmi_cleanup(dev_info_t
*dip
)
482 /* poke the taskq so that it can terminate */
484 sc
->ipmi_detaching
= 1;
485 cv_signal(&sc
->ipmi_request_added
);
489 ddi_remove_minor_node(dip
, NULL
);
492 list_destroy(&dev_list
);
493 id_space_destroy(minor_ids
);
495 sc
->ipmi_detaching
= 0;
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) {
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
) {
520 if (ipmi_kcs_attach(sc
) != 0)
521 return (DDI_FAILURE
);
524 return (DDI_FAILURE
);
528 if (ddi_create_minor_node(dip
, "ipmi", S_IFCHR
, 0, DDI_PSEUDO
,
530 cmn_err(CE_WARN
, "!attach could not create minor node");
531 ddi_remove_minor_node(dip
, NULL
);
532 return (DDI_FAILURE
);
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
) {
545 return (DDI_FAILURE
);
548 ipmi_attached
= B_TRUE
;
550 return (DDI_SUCCESS
);
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
);
567 ipmi_attached
= B_FALSE
;
568 return (DDI_SUCCESS
);
571 static struct cb_ops ipmi_cb_ops
= {
574 nodev
, /* strategy */
585 NULL
, /* streamtab */
586 D_NEW
| D_MP
, /* flags */
592 static struct dev_ops ipmi_ops
= {
594 0, /* reference count */
596 nulldev
, /* identify */
604 ddi_quiesce_not_needed
,
607 static struct modldrv md
= {
608 &mod_driverops
, "ipmi driver", &ipmi_ops
611 static struct modlinkage ml
= {
618 return (mod_install(&ml
));
624 return (mod_remove(&ml
));
628 _info(struct modinfo
*mip
)
630 return (mod_info(&ml
, mip
));