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 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * fdd messenger
29  *
30  * This module sends fdd running on service processor a message which
31  * indicates the Solaris host FMA capability when fmd is started. The
32  * message is sent via the BMC driver (KCS interface) to the IPMI stack
33  * of ILOM using the IPMI Sun OEM core tunnel command. The sub-command
34  * is CORE_TUNNEL_SUBCMD_HOSTCAP. The IPMI stack posts an host FMA
35  * capability event to the event manager upon receiving this message.
36  * fdd subscribes to the event manager for this event. Upon receving
37  * this event, fdd will adjust its configuration.
38  *
39  */
40 
41 #include <errno.h>
42 #include <stdio.h>
43 #include <strings.h>
44 #include <sys/systeminfo.h>
45 #include <libipmi.h>
46 #include <fm/fmd_api.h>
47 
48 #define	CMD_SUNOEM_CORE_TUNNEL 0x44
49 #define	CORE_TUNNEL_SUBCMD_HOSTFMACAP 2
50 #define	OEM_DATA_LENGTH 3
51 #define	VERSION 0x10
52 #define	HOST_CAPABILITY 2
53 
54 static int
55 check_sunoem(ipmi_handle_t *ipmi_hdl)
56 {
57 	ipmi_deviceid_t *devid;
58 
59 	if ((devid = ipmi_get_deviceid(ipmi_hdl)) == NULL)
60 		return (-1);
61 
62 	if (!ipmi_is_sun_ilom(devid))
63 		return (-2);
64 
65 	return (0);
66 }
67 
68 /*ARGSUSED*/
69 static void
70 send_fma_cap(fmd_hdl_t *hdl, id_t id, void *data)
71 {
72 	ipmi_handle_t *ipmi_hdl;
73 	ipmi_cmd_t cmd;
74 	uint8_t oem_data[OEM_DATA_LENGTH];
75 
76 	ipmi_hdl = fmd_hdl_getspecific(hdl);
77 
78 	oem_data[0] = CORE_TUNNEL_SUBCMD_HOSTFMACAP;
79 	oem_data[1] = VERSION;
80 	oem_data[2] = HOST_CAPABILITY;
81 
82 	cmd.ic_netfn = IPMI_NETFN_OEM;
83 	cmd.ic_lun = 0;
84 	cmd.ic_cmd = CMD_SUNOEM_CORE_TUNNEL;
85 	cmd.ic_dlen = OEM_DATA_LENGTH;
86 	cmd.ic_data = oem_data;
87 
88 	if (ipmi_send(ipmi_hdl, &cmd) == NULL) {
89 		fmd_hdl_debug(hdl, "Failed to send Solaris FMA "
90 		    "capability to fdd: %s", ipmi_errmsg(ipmi_hdl));
91 	}
92 
93 	ipmi_close(ipmi_hdl);
94 	fmd_hdl_setspecific(hdl, NULL);
95 	fmd_hdl_unregister(hdl);
96 }
97 
98 static const fmd_hdl_ops_t fmd_ops = {
99 	NULL,		/* fmdo_recv */
100 	send_fma_cap,	/* fmdo_timeout */
101 	NULL,		/* fmdo_close */
102 	NULL,		/* fmdo_stats */
103 	NULL,		/* fmdo_gc */
104 	NULL,		/* fmdo_send */
105 	NULL,		/* fmdo_topo */
106 };
107 
108 static const fmd_prop_t fmd_props[] = {
109 	{ "interval", FMD_TYPE_TIME, "1s" },
110 	{ NULL, 0, NULL }
111 };
112 
113 static const fmd_hdl_info_t fmd_info = {
114 	"fdd Messenger", "1.0", &fmd_ops, fmd_props
115 };
116 
117 void
118 _fmd_init(fmd_hdl_t *hdl)
119 {
120 	ipmi_handle_t	*ipmi_hdl;
121 	int error;
122 	char *msg;
123 	char isa[8];
124 
125 	/* For now the module only sends message to ILOM on i386 platforms */
126 	if ((sysinfo(SI_ARCHITECTURE, isa, sizeof (isa)) == -1) ||
127 	    (strncmp(isa, "i386", 4) != 0))
128 		return;
129 
130 	if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0)
131 		return;
132 
133 	if ((ipmi_hdl = ipmi_open(&error, &msg, IPMI_TRANSPORT_BMC, NULL))
134 	    == NULL) {
135 		/*
136 		 * If /dev/bmc doesn't exist on the system, then unload the
137 		 * module without doing anything.
138 		 */
139 		if (error != EIPMI_BMC_OPEN_FAILED)
140 			fmd_hdl_abort(hdl, "Failed to initialize IPMI "
141 			    "connection: %s\n", msg);
142 		fmd_hdl_debug(hdl, "Failed to load: no IPMI connection "
143 		    "present");
144 		fmd_hdl_unregister(hdl);
145 		return;
146 	}
147 
148 	/*
149 	 * Check if it's Sun ILOM
150 	 */
151 	if (check_sunoem(ipmi_hdl) != 0) {
152 		fmd_hdl_debug(hdl, "Service Processor does not run "
153 		    "Sun ILOM");
154 		ipmi_close(ipmi_hdl);
155 		fmd_hdl_unregister(hdl);
156 		return;
157 	}
158 
159 	fmd_hdl_setspecific(hdl, ipmi_hdl);
160 
161 	/*
162 	 * Setup the timer.
163 	 */
164 	(void) fmd_timer_install(hdl, NULL, NULL, 2000000000ULL);
165 }
166 
167 void
168 _fmd_fini(fmd_hdl_t *hdl)
169 {
170 	ipmi_handle_t *ipmi_hdl = fmd_hdl_getspecific(hdl);
171 
172 	if (ipmi_hdl) {
173 		ipmi_close(ipmi_hdl);
174 	}
175 }
176