16752Sae112802 /*
26752Sae112802 * CDDL HEADER START
36752Sae112802 *
46752Sae112802 * The contents of this file are subject to the terms of the
56752Sae112802 * Common Development and Distribution License (the "License").
66752Sae112802 * You may not use this file except in compliance with the License.
76752Sae112802 *
86752Sae112802 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96752Sae112802 * or http://www.opensolaris.org/os/licensing.
106752Sae112802 * See the License for the specific language governing permissions
116752Sae112802 * and limitations under the License.
126752Sae112802 *
136752Sae112802 * When distributing Covered Code, include this CDDL HEADER in each
146752Sae112802 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156752Sae112802 * If applicable, add the following below this CDDL HEADER, with the
166752Sae112802 * fields enclosed by brackets "[]" replaced with your own identifying
176752Sae112802 * information: Portions Copyright [yyyy] [name of copyright owner]
186752Sae112802 *
196752Sae112802 * CDDL HEADER END
206752Sae112802 */
216752Sae112802 /*
22*8025SMary.Beale@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
236752Sae112802 * Use is subject to license terms.
246752Sae112802 */
256752Sae112802
266752Sae112802 /*
276752Sae112802 * PICL Ontario platform plug-in to message the SC to light
286752Sae112802 * or extinguish the hdd 'OK2RM' ready-to-service light in
296752Sae112802 * the event of a soft unconfigure or configure, respectively.
30*8025SMary.Beale@Sun.COM *
31*8025SMary.Beale@Sun.COM * Erie platforms (T1000) do not have ok-to-remove LEDs
32*8025SMary.Beale@Sun.COM * so they do not need handlers for the SBL events.
336752Sae112802 */
346752Sae112802
356752Sae112802 #include <picl.h>
366752Sae112802 #include <picltree.h>
376752Sae112802 #include <picldefs.h>
386752Sae112802 #include <stdio.h>
396752Sae112802 #include <umem.h>
406752Sae112802 #include <unistd.h>
416752Sae112802 #include <libnvpair.h>
426752Sae112802 #include <strings.h>
436752Sae112802 #include <syslog.h>
446752Sae112802 #include <dlfcn.h>
456752Sae112802 #include <link.h>
466752Sae112802 #include <signal.h>
476752Sae112802 #include <sys/types.h>
486752Sae112802 #include <sys/stat.h>
496752Sae112802 #include <fcntl.h>
506752Sae112802 #include <sys/param.h>
516752Sae112802 #include <sys/raidioctl.h>
52*8025SMary.Beale@Sun.COM #include <sys/utsname.h>
53*8025SMary.Beale@Sun.COM #include <sys/systeminfo.h>
546752Sae112802 #include <libpcp.h>
556752Sae112802 #include "piclsbl.h"
566752Sae112802
576752Sae112802 #include "errno.h"
586752Sae112802
596752Sae112802 #pragma init(piclsbl_register)
606752Sae112802
616752Sae112802 static void *pcp_handle;
626752Sae112802
636752Sae112802 static char hba_devctl[MAXPATHLEN];
646752Sae112802
656752Sae112802 static int (* pcp_init_ptr)();
666752Sae112802 static int (* pcp_send_recv_ptr)();
676752Sae112802 static int (* pcp_close_ptr)();
686752Sae112802
696752Sae112802 static int load_pcp_libs(void);
706752Sae112802 static void piclsbl_init(void);
716752Sae112802 static void piclsbl_fini(void);
726752Sae112802 static void piclsbl_register(void);
736752Sae112802 static void piclsbl_handler(const char *ename, const void *earg,
746752Sae112802 size_t size, void *cookie);
756752Sae112802
766752Sae112802 static picld_plugin_reg_t piclsbl_reg = {
776752Sae112802 PICLD_PLUGIN_VERSION_1,
786752Sae112802 PICLD_PLUGIN_CRITICAL,
796752Sae112802 "piclsbl",
806752Sae112802 piclsbl_init,
816752Sae112802 piclsbl_fini
826752Sae112802 };
836752Sae112802
846752Sae112802 /*
856752Sae112802 * called from init to load the pcp library
866752Sae112802 */
876752Sae112802 static int
load_pcp_libs()886752Sae112802 load_pcp_libs()
896752Sae112802 {
906752Sae112802 char pcp_dl_lib[80];
916752Sae112802
926752Sae112802 (void) snprintf(pcp_dl_lib, sizeof (pcp_dl_lib), "%s%s",
93*8025SMary.Beale@Sun.COM LIB_PCP_PATH, PCPLIB);
946752Sae112802
956752Sae112802 /* load the library and set up function pointers */
966752Sae112802 if ((pcp_handle = dlopen(pcp_dl_lib, RTLD_NOW)) == (void *) NULL)
976752Sae112802 return (1);
986752Sae112802
996752Sae112802 pcp_init_ptr = (int(*)())dlsym(pcp_handle, "pcp_init");
1006752Sae112802 pcp_close_ptr = (int(*)())dlsym(pcp_handle, "pcp_close");
1016752Sae112802 pcp_send_recv_ptr = (int(*)())dlsym(pcp_handle, "pcp_send_recv");
1026752Sae112802
1036752Sae112802 if (pcp_init_ptr == NULL || pcp_send_recv_ptr == NULL ||
104*8025SMary.Beale@Sun.COM pcp_close_ptr == NULL)
1056752Sae112802 return (1);
1066752Sae112802
1076752Sae112802 return (0);
1086752Sae112802 }
1096752Sae112802
1106752Sae112802 /*
1116752Sae112802 * callback routine for ptree_walk_tree_by_class()
1126752Sae112802 */
1136752Sae112802 static int
cb_find_disk(picl_nodehdl_t node,void * args)1146752Sae112802 cb_find_disk(picl_nodehdl_t node, void *args)
1156752Sae112802 {
1166752Sae112802 disk_lookup_t *lookup = (disk_lookup_t *)args;
1176752Sae112802 int status = -1;
1186752Sae112802 char *n;
1196752Sae112802 char path[PICL_PROPNAMELEN_MAX];
1206752Sae112802
1216752Sae112802 status = ptree_get_propval_by_name(node, "Path", (void *)&path,
1226752Sae112802 PICL_PROPNAMELEN_MAX);
1236752Sae112802 if (status != PICL_SUCCESS) {
1246752Sae112802 return (PICL_WALK_CONTINUE);
1256752Sae112802 }
1266752Sae112802
1276752Sae112802 if (strcmp(path, lookup->path) == 0) {
1286752Sae112802 lookup->disk = node;
1296752Sae112802 lookup->result = DISK_FOUND;
1306752Sae112802
1316752Sae112802 /* store the HBA's device path for use in check_raid() */
1326752Sae112802 n = strstr(path, "/sd");
1336752Sae112802 strncpy(n, "\0", 1);
1346752Sae112802 (void) snprintf(hba_devctl, MAXPATHLEN, "/devices%s:devctl",
135*8025SMary.Beale@Sun.COM path);
1366752Sae112802
1376752Sae112802 return (PICL_WALK_TERMINATE);
1386752Sae112802 }
1396752Sae112802
1406752Sae112802 return (PICL_WALK_CONTINUE);
1416752Sae112802 }
1426752Sae112802
1436752Sae112802 /*
1446752Sae112802 * check a target for RAID membership
1456752Sae112802 */
1466752Sae112802 static int
check_raid(int target)1476752Sae112802 check_raid(int target)
1486752Sae112802 {
1496752Sae112802 raid_config_t config;
1506752Sae112802 int fd;
1516752Sae112802 int numvols;
1526752Sae112802 int i;
1536752Sae112802 int j;
1546752Sae112802
1556752Sae112802 /*
1566752Sae112802 * hba_devctl is set to the onboard hba, so it will
1576752Sae112802 * always house any onboard RAID volumes
1586752Sae112802 */
1596752Sae112802 if ((fd = open(hba_devctl, O_RDONLY)) < 0) {
1606752Sae112802 syslog(LOG_ERR, "%s", strerror(errno));
1616752Sae112802 return (0);
1626752Sae112802 }
1636752Sae112802
1646752Sae112802 /*
1656752Sae112802 * look up the RAID configurations for the onboard
1666752Sae112802 * HBA and check target against all member targets
1676752Sae112802 */
1686752Sae112802 if (ioctl(fd, RAID_NUMVOLUMES, &numvols)) {
1696752Sae112802 syslog(LOG_ERR, "%s", strerror(errno));
1706752Sae112802 (void) close(fd);
1716752Sae112802 return (0);
1726752Sae112802 }
1736752Sae112802
1746752Sae112802 for (i = 0; i < numvols; i++) {
1756752Sae112802 config.unitid = i;
1766752Sae112802 if (ioctl(fd, RAID_GETCONFIG, &config)) {
1776752Sae112802 syslog(LOG_ERR, "%s", strerror(errno));
1786752Sae112802 (void) close(fd);
1796752Sae112802 return (0);
1806752Sae112802 }
1816752Sae112802
1826752Sae112802 for (j = 0; j < config.ndisks; j++) {
1836752Sae112802 if (config.disk[j] == target) {
1846752Sae112802 (void) close(fd);
1856752Sae112802 return (1);
1866752Sae112802 }
1876752Sae112802 }
1886752Sae112802 }
1896752Sae112802 (void) close(fd);
1906752Sae112802 return (0);
1916752Sae112802 }
1926752Sae112802
1936752Sae112802 /*
1946752Sae112802 * Ontario SBL event handler, subscribed to:
1956752Sae112802 * PICLEVENT_SYSEVENT_DEVICE_ADDED
1966752Sae112802 * PICLEVENT_SYSEVENT_DEVICE_REMOVED
1976752Sae112802 */
1986752Sae112802 static void
piclsbl_handler(const char * ename,const void * earg,size_t size,void * cookie)1996752Sae112802 piclsbl_handler(const char *ename, const void *earg, size_t size,
2006752Sae112802 void *cookie)
2016752Sae112802 {
2026752Sae112802 char *devfs_path;
2036752Sae112802 char hdd_location[PICL_PROPNAMELEN_MAX];
2046752Sae112802 nvlist_t *nvlp = NULL;
2056752Sae112802 pcp_msg_t send_msg;
2066752Sae112802 pcp_msg_t recv_msg;
2076752Sae112802 pcp_sbl_req_t *req_ptr = NULL;
2086752Sae112802 pcp_sbl_resp_t *resp_ptr = NULL;
2096752Sae112802 int status = -1;
2106752Sae112802 int target;
2116752Sae112802 disk_lookup_t lookup;
2126752Sae112802 int channel_fd;
2136752Sae112802
2146752Sae112802 /*
2156752Sae112802 * setup the request data to attach to the libpcp msg
2166752Sae112802 */
2176752Sae112802 if ((req_ptr = (pcp_sbl_req_t *)umem_zalloc(sizeof (pcp_sbl_req_t),
218*8025SMary.Beale@Sun.COM UMEM_DEFAULT)) == NULL)
2196752Sae112802 goto sbl_return;
2206752Sae112802
2216752Sae112802 /*
2226752Sae112802 * This plugin serves to enable or disable the blue RAS
2236752Sae112802 * 'ok-to-remove' LED that is on each of the 4 disks on the
2246752Sae112802 * Ontario. We catch the event via the picl handler, and
2256752Sae112802 * if the event is DEVICE_ADDED for one of our onboard disks,
2266752Sae112802 * then we'll be turning off the LED. Otherwise, if the event
2276752Sae112802 * is DEVICE_REMOVED, then we turn it on.
2286752Sae112802 */
2296752Sae112802 if (strcmp(ename, PICLEVENT_SYSEVENT_DEVICE_ADDED) == 0)
2306752Sae112802 req_ptr->sbl_action = PCP_SBL_DISABLE;
2316752Sae112802 else if (strcmp(ename, PICLEVENT_SYSEVENT_DEVICE_REMOVED) == 0)
2326752Sae112802 req_ptr->sbl_action = PCP_SBL_ENABLE;
2336752Sae112802 else
2346752Sae112802 goto sbl_return;
2356752Sae112802
2366752Sae112802 /*
2376752Sae112802 * retrieve the device's physical path from the event payload
2386752Sae112802 */
2396752Sae112802 if (nvlist_unpack((char *)earg, size, &nvlp, NULL))
2406752Sae112802 goto sbl_return;
2416752Sae112802 if (nvlist_lookup_string(nvlp, "devfs-path", &devfs_path))
2426752Sae112802 goto sbl_return;
2436752Sae112802
2446752Sae112802 /*
2456752Sae112802 * look for this disk in the picl tree, and if it's
2466752Sae112802 * location indicates that it's one of our internal
2476752Sae112802 * disks, then set sbl_id to incdicate which one.
2486752Sae112802 * otherwise, return as it is not one of our disks.
2496752Sae112802 */
2506752Sae112802 lookup.path = strdup(devfs_path);
2516752Sae112802 lookup.disk = NULL;
2526752Sae112802 lookup.result = DISK_NOT_FOUND;
2536752Sae112802
2546752Sae112802 /* first, find the disk */
2556752Sae112802 status = ptree_walk_tree_by_class(root_node, "disk", (void *)&lookup,
256*8025SMary.Beale@Sun.COM cb_find_disk);
2576752Sae112802 if (status != PICL_SUCCESS)
2586752Sae112802 goto sbl_return;
2596752Sae112802
2606752Sae112802 if (lookup.result == DISK_FOUND) {
2616752Sae112802 /* now, lookup it's location in the node */
2626752Sae112802 status = ptree_get_propval_by_name(lookup.disk, "Location",
263*8025SMary.Beale@Sun.COM (void *)&hdd_location, PICL_PROPNAMELEN_MAX);
2646752Sae112802 if (status != PICL_SUCCESS) {
2656752Sae112802 syslog(LOG_ERR, "piclsbl: failed hdd discovery");
2666752Sae112802 goto sbl_return;
2676752Sae112802 }
2686752Sae112802 }
2696752Sae112802
2706752Sae112802 /*
2716752Sae112802 * Strip off the target from the NAC name.
2726752Sae112802 * The disk NAC will always be HDD#
2736752Sae112802 */
2746752Sae112802 if (strncmp(hdd_location, NAC_DISK_PREFIX,
275*8025SMary.Beale@Sun.COM strlen(NAC_DISK_PREFIX)) == 0) {
276*8025SMary.Beale@Sun.COM (void) sscanf(hdd_location, "%*3s%d", &req_ptr->sbl_id);
277*8025SMary.Beale@Sun.COM target = (int)req_ptr->sbl_id;
2786752Sae112802 } else {
279*8025SMary.Beale@Sun.COM /* this is not one of the onboard disks */
280*8025SMary.Beale@Sun.COM goto sbl_return;
2816752Sae112802 }
2826752Sae112802
2836752Sae112802 /*
2846752Sae112802 * check the onboard RAID configuration for this disk. if it is
2856752Sae112802 * a member of a RAID and is not the RAID itself, ignore the event
2866752Sae112802 */
2876752Sae112802 if (check_raid(target))
2886752Sae112802 goto sbl_return;
2896752Sae112802
2906752Sae112802 /*
2916752Sae112802 * we have the information we need, init the platform channel.
2926752Sae112802 * the platform channel driver will only allow one connection
2936752Sae112802 * at a time on this socket. on the offchance that more than
2946752Sae112802 * one event comes in, we'll retry to initialize this connection
2956752Sae112802 * up to 3 times
2966752Sae112802 */
2976752Sae112802 if ((channel_fd = (*pcp_init_ptr)(LED_CHANNEL)) < 0) {
2986752Sae112802 /* failed to init; wait and retry up to 3 times */
2996752Sae112802 int s = PCPINIT_TIMEOUT;
3006752Sae112802 int retries = 0;
3016752Sae112802 while (++retries) {
3026752Sae112802 (void) sleep(s);
3036752Sae112802 if ((channel_fd = (*pcp_init_ptr)(LED_CHANNEL)) >= 0)
3046752Sae112802 break;
3056752Sae112802 else if (retries == 3) {
3066752Sae112802 syslog(LOG_ERR, "piclsbl: ",
307*8025SMary.Beale@Sun.COM "SC channel initialization failed");
3086752Sae112802 goto sbl_return;
3096752Sae112802 }
3106752Sae112802 /* continue */
3116752Sae112802 }
3126752Sae112802 }
3136752Sae112802
3146752Sae112802 /*
3156752Sae112802 * populate the message for libpcp
3166752Sae112802 */
3176752Sae112802 send_msg.msg_type = PCP_SBL_CONTROL;
3186752Sae112802 send_msg.sub_type = NULL;
3196752Sae112802 send_msg.msg_len = sizeof (pcp_sbl_req_t);
3206752Sae112802 send_msg.msg_data = (uint8_t *)req_ptr;
3216752Sae112802
3226752Sae112802 /*
3236752Sae112802 * send the request, receive the response
3246752Sae112802 */
3256752Sae112802 if ((*pcp_send_recv_ptr)(channel_fd, &send_msg, &recv_msg,
326*8025SMary.Beale@Sun.COM PCPCOMM_TIMEOUT) < 0) {
3276752Sae112802 /* we either timed out or erred; either way try again */
3286752Sae112802 int s = PCPCOMM_TIMEOUT;
3296752Sae112802 (void) sleep(s);
3306752Sae112802 if ((*pcp_send_recv_ptr)(channel_fd, &send_msg, &recv_msg,
331*8025SMary.Beale@Sun.COM PCPCOMM_TIMEOUT) < 0) {
3326752Sae112802 syslog(LOG_ERR, "piclsbl: communication failure");
3336752Sae112802 goto sbl_return;
3346752Sae112802 }
3356752Sae112802 }
3366752Sae112802
3376752Sae112802 /*
3386752Sae112802 * validate that this data was meant for us
3396752Sae112802 */
3406752Sae112802 if (recv_msg.msg_type != PCP_SBL_CONTROL_R) {
3416752Sae112802 syslog(LOG_ERR, "piclsbl: unbound packet received");
3426752Sae112802 goto sbl_return;
3436752Sae112802 }
3446752Sae112802
3456752Sae112802 /*
3466752Sae112802 * verify that the LED action has taken place
3476752Sae112802 */
3486752Sae112802 resp_ptr = (pcp_sbl_resp_t *)recv_msg.msg_data;
3496752Sae112802 if (resp_ptr->status == PCP_SBL_ERROR) {
3506752Sae112802 syslog(LOG_ERR, "piclsbl: OK2RM LED action error");
3516752Sae112802 goto sbl_return;
3526752Sae112802 }
3536752Sae112802
3546752Sae112802 /*
3556752Sae112802 * ensure the LED action taken is the one requested
3566752Sae112802 */
3576752Sae112802 if ((req_ptr->sbl_action == PCP_SBL_DISABLE) &&
358*8025SMary.Beale@Sun.COM (resp_ptr->sbl_state != SBL_STATE_OFF))
3596752Sae112802 syslog(LOG_ERR, "piclsbl: OK2RM LED not OFF after disk "
360*8025SMary.Beale@Sun.COM "configuration");
3616752Sae112802 else if ((req_ptr->sbl_action == PCP_SBL_ENABLE) &&
362*8025SMary.Beale@Sun.COM (resp_ptr->sbl_state != SBL_STATE_ON))
3636752Sae112802 syslog(LOG_ERR, "piclsbl: OK2RM LED not ON after disk "
364*8025SMary.Beale@Sun.COM "unconfiguration");
3656752Sae112802 else if (resp_ptr->sbl_state == SBL_STATE_UNKNOWN)
3666752Sae112802 syslog(LOG_ERR, "piclsbl: OK2RM LED set to unknown state");
3676752Sae112802
3686752Sae112802 sbl_return:
3696752Sae112802
3706752Sae112802 (*pcp_close_ptr)(channel_fd);
3716752Sae112802 if (req_ptr != NULL)
3726752Sae112802 umem_free(req_ptr, sizeof (pcp_sbl_req_t));
3736752Sae112802 if (resp_ptr != NULL)
3746752Sae112802 free(resp_ptr);
3756752Sae112802 if (nvlp != NULL)
3766752Sae112802 nvlist_free(nvlp);
3776752Sae112802 }
3786752Sae112802
3796752Sae112802 static void
piclsbl_init(void)3806752Sae112802 piclsbl_init(void)
3816752Sae112802 {
382*8025SMary.Beale@Sun.COM char platbuf[SYS_NMLN];
383*8025SMary.Beale@Sun.COM
384*8025SMary.Beale@Sun.COM /* check for Erie platform name */
385*8025SMary.Beale@Sun.COM if ((sysinfo(SI_PLATFORM, platbuf, SYS_NMLN) != -1) &&
386*8025SMary.Beale@Sun.COM ((strcmp(platbuf, ERIE_PLATFORM) == 0) ||
387*8025SMary.Beale@Sun.COM (strcmp(platbuf, ERIE_PLATFORM2) == 0)))
388*8025SMary.Beale@Sun.COM return;
389*8025SMary.Beale@Sun.COM
3906752Sae112802 /* retrieve the root node for lookups in the event handler */
3916752Sae112802 if ((ptree_get_root(&root_node)) != NULL)
3926752Sae112802 return;
3936752Sae112802
3946752Sae112802 /* load libpcp */
3956752Sae112802 if (load_pcp_libs()) {
3966752Sae112802 syslog(LOG_ERR, "piclsbl: failed to load libpcp");
3976752Sae112802 syslog(LOG_ERR, "piclsbl: aborting");
3986752Sae112802 return;
3996752Sae112802 }
4006752Sae112802
4016752Sae112802 /*
4026752Sae112802 * register piclsbl_handler for both "sysevent-device-added" and
4036752Sae112802 * and for "sysevent-device-removed" PICL events
4046752Sae112802 */
4056752Sae112802 (void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED,
4066752Sae112802 piclsbl_handler, NULL);
4076752Sae112802 (void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED,
4086752Sae112802 piclsbl_handler, NULL);
4096752Sae112802 }
4106752Sae112802
4116752Sae112802 static void
piclsbl_fini(void)4126752Sae112802 piclsbl_fini(void)
4136752Sae112802 {
4146752Sae112802 /* unregister the event handler */
4156752Sae112802 (void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED,
4166752Sae112802 piclsbl_handler, NULL);
4176752Sae112802 (void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED,
4186752Sae112802 piclsbl_handler, NULL);
4196752Sae112802 }
4206752Sae112802
4216752Sae112802 static void
piclsbl_register(void)4226752Sae112802 piclsbl_register(void)
4236752Sae112802 {
4246752Sae112802 picld_plugin_register(&piclsbl_reg);
4256752Sae112802 }
426