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