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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  *  PICL Ontario platform plug-in to message the SC to light
28  *  or extinguish the hdd 'OK2RM' ready-to-service light in
29  *  the event of a soft unconfigure or configure, respectively.
30  *
31  *  Erie platforms (T1000) do not have ok-to-remove LEDs
32  *  so they do not need handlers for the SBL events.
33  */
34 
35 #include <picl.h>
36 #include <picltree.h>
37 #include <picldefs.h>
38 #include <stdio.h>
39 #include <umem.h>
40 #include <unistd.h>
41 #include <libnvpair.h>
42 #include <strings.h>
43 #include <syslog.h>
44 #include <dlfcn.h>
45 #include <link.h>
46 #include <signal.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <fcntl.h>
50 #include <sys/param.h>
51 #include <sys/raidioctl.h>
52 #include <sys/utsname.h>
53 #include <sys/systeminfo.h>
54 #include <libpcp.h>
55 #include "piclsbl.h"
56 
57 #include "errno.h"
58 
59 #pragma init(piclsbl_register)
60 
61 static void *pcp_handle;
62 
63 static char hba_devctl[MAXPATHLEN];
64 
65 static int (* pcp_init_ptr)();
66 static int (* pcp_send_recv_ptr)();
67 static int (* pcp_close_ptr)();
68 
69 static int load_pcp_libs(void);
70 static void piclsbl_init(void);
71 static void piclsbl_fini(void);
72 static void piclsbl_register(void);
73 static void piclsbl_handler(const char *ename, const void *earg,
74 				size_t size, void *cookie);
75 
76 static picld_plugin_reg_t piclsbl_reg = {
77 	PICLD_PLUGIN_VERSION_1,
78 	PICLD_PLUGIN_CRITICAL,
79 	"piclsbl",
80 	piclsbl_init,
81 	piclsbl_fini
82 };
83 
84 /*
85  * called from init to load the pcp library
86  */
87 static int
load_pcp_libs()88 load_pcp_libs()
89 {
90 	char pcp_dl_lib[80];
91 
92 	(void) snprintf(pcp_dl_lib, sizeof (pcp_dl_lib), "%s%s",
93 	    LIB_PCP_PATH, PCPLIB);
94 
95 	/* load the library and set up function pointers */
96 	if ((pcp_handle = dlopen(pcp_dl_lib, RTLD_NOW)) == (void *) NULL)
97 		return (1);
98 
99 	pcp_init_ptr = (int(*)())dlsym(pcp_handle, "pcp_init");
100 	pcp_close_ptr = (int(*)())dlsym(pcp_handle, "pcp_close");
101 	pcp_send_recv_ptr = (int(*)())dlsym(pcp_handle, "pcp_send_recv");
102 
103 	if (pcp_init_ptr == NULL || pcp_send_recv_ptr == NULL ||
104 	    pcp_close_ptr == NULL)
105 		return (1);
106 
107 	return (0);
108 }
109 
110 /*
111  * callback routine for ptree_walk_tree_by_class()
112  */
113 static int
cb_find_disk(picl_nodehdl_t node,void * args)114 cb_find_disk(picl_nodehdl_t node, void *args)
115 {
116 	disk_lookup_t *lookup  = (disk_lookup_t *)args;
117 	int status = -1;
118 	char *n;
119 	char path[PICL_PROPNAMELEN_MAX];
120 
121 	status = ptree_get_propval_by_name(node, "Path", (void *)&path,
122 	    PICL_PROPNAMELEN_MAX);
123 	if (status != PICL_SUCCESS) {
124 		return (PICL_WALK_CONTINUE);
125 	}
126 
127 	if (strcmp(path, lookup->path) == 0) {
128 		lookup->disk = node;
129 		lookup->result = DISK_FOUND;
130 
131 		/* store the HBA's device path for use in check_raid() */
132 		n = strstr(path, "/sd");
133 		strncpy(n, "\0", 1);
134 		(void) snprintf(hba_devctl, MAXPATHLEN, "/devices%s:devctl",
135 		    path);
136 
137 		return (PICL_WALK_TERMINATE);
138 	}
139 
140 	return (PICL_WALK_CONTINUE);
141 }
142 
143 /*
144  * check a target for RAID membership
145  */
146 static int
check_raid(int target)147 check_raid(int target)
148 {
149 	raid_config_t config;
150 	int fd;
151 	int numvols;
152 	int i;
153 	int j;
154 
155 	/*
156 	 * hba_devctl is set to the onboard hba, so it will
157 	 * always house any onboard RAID volumes
158 	 */
159 	if ((fd = open(hba_devctl, O_RDONLY)) < 0) {
160 		syslog(LOG_ERR, "%s", strerror(errno));
161 		return (0);
162 	}
163 
164 	/*
165 	 * look up the RAID configurations for the onboard
166 	 * HBA and check target against all member targets
167 	 */
168 	if (ioctl(fd, RAID_NUMVOLUMES, &numvols)) {
169 		syslog(LOG_ERR, "%s", strerror(errno));
170 		(void) close(fd);
171 		return (0);
172 	}
173 
174 	for (i = 0; i < numvols; i++) {
175 		config.unitid = i;
176 		if (ioctl(fd, RAID_GETCONFIG, &config)) {
177 			syslog(LOG_ERR, "%s", strerror(errno));
178 			(void) close(fd);
179 			return (0);
180 		}
181 
182 		for (j = 0; j < config.ndisks; j++) {
183 			if (config.disk[j] == target) {
184 				(void) close(fd);
185 				return (1);
186 			}
187 		}
188 	}
189 	(void) close(fd);
190 	return (0);
191 }
192 
193 /*
194  * Ontario SBL event handler, subscribed to:
195  *	PICLEVENT_SYSEVENT_DEVICE_ADDED
196  *	PICLEVENT_SYSEVENT_DEVICE_REMOVED
197  */
198 static void
piclsbl_handler(const char * ename,const void * earg,size_t size,void * cookie)199 piclsbl_handler(const char *ename, const void *earg, size_t size,
200     void *cookie)
201 {
202 	char		*devfs_path;
203 	char		hdd_location[PICL_PROPNAMELEN_MAX];
204 	nvlist_t	*nvlp = NULL;
205 	pcp_msg_t	send_msg;
206 	pcp_msg_t	recv_msg;
207 	pcp_sbl_req_t	*req_ptr = NULL;
208 	pcp_sbl_resp_t	*resp_ptr = NULL;
209 	int		status = -1;
210 	int		target;
211 	disk_lookup_t	lookup;
212 	int		channel_fd;
213 
214 	/*
215 	 * setup the request data to attach to the libpcp msg
216 	 */
217 	if ((req_ptr = (pcp_sbl_req_t *)umem_zalloc(sizeof (pcp_sbl_req_t),
218 	    UMEM_DEFAULT)) == NULL)
219 		goto sbl_return;
220 
221 	/*
222 	 * This plugin serves to enable or disable the blue RAS
223 	 * 'ok-to-remove' LED that is on each of the 4 disks on the
224 	 * Ontario.  We catch the event via the picl handler, and
225 	 * if the event is DEVICE_ADDED for one of our onboard disks,
226 	 * then we'll be turning off the LED. Otherwise, if the event
227 	 * is DEVICE_REMOVED, then we turn it on.
228 	 */
229 	if (strcmp(ename, PICLEVENT_SYSEVENT_DEVICE_ADDED) == 0)
230 		req_ptr->sbl_action = PCP_SBL_DISABLE;
231 	else if (strcmp(ename, PICLEVENT_SYSEVENT_DEVICE_REMOVED) == 0)
232 		req_ptr->sbl_action = PCP_SBL_ENABLE;
233 	else
234 		goto sbl_return;
235 
236 	/*
237 	 * retrieve the device's physical path from the event payload
238 	 */
239 	if (nvlist_unpack((char *)earg, size, &nvlp, 0))
240 		goto sbl_return;
241 	if (nvlist_lookup_string(nvlp, "devfs-path", &devfs_path))
242 		goto sbl_return;
243 
244 	/*
245 	 * look for this disk in the picl tree, and if it's
246 	 * location indicates that it's one of our internal
247 	 * disks, then set sbl_id to incdicate which one.
248 	 * otherwise, return as it is not one of our disks.
249 	 */
250 	lookup.path = strdup(devfs_path);
251 	lookup.disk = 0;
252 	lookup.result = DISK_NOT_FOUND;
253 
254 	/* first, find the disk */
255 	status = ptree_walk_tree_by_class(root_node, "disk", (void *)&lookup,
256 	    cb_find_disk);
257 	if (status != PICL_SUCCESS)
258 		goto sbl_return;
259 
260 	if (lookup.result == DISK_FOUND) {
261 		/* now, lookup it's location in the node */
262 		status = ptree_get_propval_by_name(lookup.disk, "Location",
263 		    (void *)&hdd_location, PICL_PROPNAMELEN_MAX);
264 		if (status != PICL_SUCCESS) {
265 			syslog(LOG_ERR, "piclsbl: failed hdd discovery");
266 			goto sbl_return;
267 		}
268 	}
269 
270 	/*
271 	 * Strip off the target from the NAC name.
272 	 * The disk NAC will always be HDD#
273 	 */
274 	if (strncmp(hdd_location, NAC_DISK_PREFIX,
275 	    strlen(NAC_DISK_PREFIX)) == 0) {
276 		(void) sscanf(hdd_location, "%*3s%d", &req_ptr->sbl_id);
277 		target = (int)req_ptr->sbl_id;
278 	} else {
279 		/* this is not one of the onboard disks */
280 		goto sbl_return;
281 	}
282 
283 	/*
284 	 * check the onboard RAID configuration for this disk. if it is
285 	 * a member of a RAID and is not the RAID itself, ignore the event
286 	 */
287 	if (check_raid(target))
288 		goto sbl_return;
289 
290 	/*
291 	 * we have the information we need, init the platform channel.
292 	 * the platform channel driver will only allow one connection
293 	 * at a time on this socket. on the offchance that more than
294 	 * one event comes in, we'll retry to initialize this connection
295 	 * up to 3 times
296 	 */
297 	if ((channel_fd = (*pcp_init_ptr)(LED_CHANNEL)) < 0) {
298 		/* failed to init; wait and retry up to 3 times */
299 		int s = PCPINIT_TIMEOUT;
300 		int retries = 0;
301 		while (++retries) {
302 			(void) sleep(s);
303 			if ((channel_fd = (*pcp_init_ptr)(LED_CHANNEL)) >= 0)
304 				break;
305 			else if (retries == 3) {
306 				syslog(LOG_ERR, "piclsbl: ",
307 				    "SC channel initialization failed");
308 				goto sbl_return;
309 			}
310 			/* continue */
311 		}
312 	}
313 
314 	/*
315 	 * populate the message for libpcp
316 	 */
317 	send_msg.msg_type = PCP_SBL_CONTROL;
318 	send_msg.sub_type = 0;
319 	send_msg.msg_len = sizeof (pcp_sbl_req_t);
320 	send_msg.msg_data = (uint8_t *)req_ptr;
321 
322 	/*
323 	 * send the request, receive the response
324 	 */
325 	if ((*pcp_send_recv_ptr)(channel_fd, &send_msg, &recv_msg,
326 	    PCPCOMM_TIMEOUT) < 0) {
327 		/* we either timed out or erred; either way try again */
328 		int s = PCPCOMM_TIMEOUT;
329 		(void) sleep(s);
330 		if ((*pcp_send_recv_ptr)(channel_fd, &send_msg, &recv_msg,
331 		    PCPCOMM_TIMEOUT) < 0) {
332 			syslog(LOG_ERR, "piclsbl: communication failure");
333 			goto sbl_return;
334 		}
335 	}
336 
337 	/*
338 	 * validate that this data was meant for us
339 	 */
340 	if (recv_msg.msg_type != PCP_SBL_CONTROL_R) {
341 		syslog(LOG_ERR, "piclsbl: unbound packet received");
342 		goto sbl_return;
343 	}
344 
345 	/*
346 	 * verify that the LED action has taken place
347 	 */
348 	resp_ptr = (pcp_sbl_resp_t *)recv_msg.msg_data;
349 	if (resp_ptr->status == PCP_SBL_ERROR) {
350 		syslog(LOG_ERR, "piclsbl: OK2RM LED action error");
351 		goto sbl_return;
352 	}
353 
354 	/*
355 	 * ensure the LED action taken is the one requested
356 	 */
357 	if ((req_ptr->sbl_action == PCP_SBL_DISABLE) &&
358 	    (resp_ptr->sbl_state != SBL_STATE_OFF))
359 		syslog(LOG_ERR, "piclsbl: OK2RM LED not OFF after disk "
360 		    "configuration");
361 	else if ((req_ptr->sbl_action == PCP_SBL_ENABLE) &&
362 	    (resp_ptr->sbl_state != SBL_STATE_ON))
363 		syslog(LOG_ERR, "piclsbl: OK2RM LED not ON after disk "
364 		    "unconfiguration");
365 	else if (resp_ptr->sbl_state == SBL_STATE_UNKNOWN)
366 		syslog(LOG_ERR, "piclsbl: OK2RM LED set to unknown state");
367 
368 sbl_return:
369 
370 	(*pcp_close_ptr)(channel_fd);
371 	if (req_ptr != NULL)
372 		umem_free(req_ptr, sizeof (pcp_sbl_req_t));
373 	if (resp_ptr != NULL)
374 		free(resp_ptr);
375 	nvlist_free(nvlp);
376 }
377 
378 static void
piclsbl_init(void)379 piclsbl_init(void)
380 {
381 	char	platbuf[SYS_NMLN];
382 
383 	/* check for Erie platform name */
384 	if ((sysinfo(SI_PLATFORM, platbuf, SYS_NMLN) != -1) &&
385 	    ((strcmp(platbuf, ERIE_PLATFORM) == 0) ||
386 	    (strcmp(platbuf, ERIE_PLATFORM2) == 0)))
387 		return;
388 
389 	/* retrieve the root node for lookups in the event handler */
390 	if ((ptree_get_root(&root_node)) != 0)
391 		return;
392 
393 	/* load libpcp */
394 	if (load_pcp_libs()) {
395 		syslog(LOG_ERR, "piclsbl: failed to load libpcp");
396 		syslog(LOG_ERR, "piclsbl: aborting");
397 		return;
398 	}
399 
400 	/*
401 	 * register piclsbl_handler for both "sysevent-device-added" and
402 	 * and for "sysevent-device-removed" PICL events
403 	 */
404 	(void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED,
405 	    piclsbl_handler, NULL);
406 	(void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED,
407 	    piclsbl_handler, NULL);
408 }
409 
410 static void
piclsbl_fini(void)411 piclsbl_fini(void)
412 {
413 	/* unregister the event handler */
414 	(void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED,
415 	    piclsbl_handler, NULL);
416 	(void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED,
417 	    piclsbl_handler, NULL);
418 }
419 
420 static void
piclsbl_register(void)421 piclsbl_register(void)
422 {
423 	picld_plugin_register(&piclsbl_reg);
424 }
425