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