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 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * All Rights Reserved, Copyright (c) FUJITSU LIMITED 2006 28 */ 29 30 #pragma ident "%Z%%M% %I% %E% SMI" 31 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <fcntl.h> 35 #include <config_admin.h> 36 #include <strings.h> 37 #include <syslog.h> 38 #include <libsysevent.h> 39 #include <libdevinfo.h> 40 #include <libnvpair.h> 41 #include <assert.h> 42 #include <errno.h> 43 #include <unistd.h> 44 #include <stropts.h> 45 #include <sys/types.h> 46 #include <sys/stat.h> 47 #include <sys/sysevent/dr.h> 48 #include <sys/scfd/opcio.h> 49 50 51 /* Macros */ 52 #define SCF_DEV_DIR "/devices" /* device base dir */ 53 54 55 56 /* 57 * Connection for SCF driver 58 */ 59 60 /* Check the availability of SCF driver */ 61 static int scfdrv_enable = 0; 62 63 64 /* Device for SCF Driver */ 65 #define SCFIOCDEV "/devices/pseudo/scfd@200:rasctl" 66 #define SCFRETRY 10 67 #define SCFIOCWAIT 3 68 #define SCFDATA_DEV_INFO 32 69 #define SCFDATA_APID 1054 70 71 /* 72 * Data for XSCF 73 * Note the size of the ap_id must be SCFDATA_APID for proper data alignment 74 * for the ioctl. The SCF has a corresponding data structure which is matched 75 * here. 76 */ 77 typedef struct { 78 char ap_id[SCFDATA_APID]; 79 uint8_t ioua; 80 uint8_t vflag; 81 uint32_t r_state; 82 uint32_t o_state; 83 uint64_t tstamp; 84 char dev_name[SCFDATA_DEV_INFO]; 85 char dev_model[SCFDATA_DEV_INFO]; 86 } scf_slotinfo_t; 87 88 /* 89 * Data for scf notification of state changes. 90 * pci_name is an ap_id phys path for the hot pluggable pci device. 91 * r_state is the recepticle state. 92 * o_state is the occupant state. 93 * cache_fmri_str is a string representation of an fmri in the rsrc cache. 94 * fmri_asru_str is the asru for an fmri which is found in the topology. 95 * found is a boolean indicating whether the device was found in the topology. 96 */ 97 typedef struct { 98 char pci_name[MAXPATHLEN]; 99 uint32_t r_state; 100 uint32_t o_state; 101 } pci_notify_t; 102 103 /* 104 * Function Prototypes 105 */ 106 void scf_get_slotinfo(char *ap_id, cfga_stat_t *o_state, 107 cfga_stat_t *r_state); 108 static int scf_get_pci_name(const char *ap_phys_id, char *pci_name); 109 static int scf_get_devinfo(char *dev_name, char *dev_model, 110 const char *pci_name); 111 void notify_scf_of_hotplug(sysevent_t *ev); 112 113 114 /* 115 * Error report utility for libcfgadm functions 116 */ 117 void 118 config_error(cfga_err_t err, const char *func_name, const char *errstr, 119 const char *ap_id) 120 { 121 const char *ep; 122 123 ep = config_strerror(err); 124 if (ep == NULL) { 125 ep = "configuration administration unknown error"; 126 } 127 128 if (errstr != NULL && *errstr != '\0') { 129 syslog(LOG_DEBUG, "%s: %s (%s), ap_id = %s\n", 130 func_name, ep, errstr, ap_id); 131 } else { 132 syslog(LOG_DEBUG, "%s: %s , ap_id = %s\n", 133 func_name, ep, ap_id); 134 } 135 136 } 137 138 /* 139 * Get the slot status. 140 */ 141 void 142 scf_get_slotinfo(char *ap_pid, cfga_stat_t *r_state, cfga_stat_t *o_state) 143 { 144 cfga_err_t rv; /* return value */ 145 cfga_list_data_t *stat = NULL; /* slot info. */ 146 int nlist; /* number of slot */ 147 char *errstr = NULL; /* error code */ 148 149 /* 150 * Get the attachment point information. 151 */ 152 rv = config_list_ext(1, (char *const *)&ap_pid, &stat, &nlist, NULL, 153 NULL, &errstr, 0); 154 155 if (rv != CFGA_OK) { 156 config_error(rv, "config_list_ext", errstr, ap_pid); 157 goto out; 158 } 159 assert(nlist == 1); 160 161 syslog(LOG_DEBUG, "\n" 162 "ap_log_id = %.*s\n" 163 "ap_phys_id = %.*s\n" 164 "ap_r_state = %d\n" 165 "ap_o_state = %d\n" 166 "ap_cond = %d\n" 167 "ap_busy = %6d\n" 168 "ap_status_time = %s" 169 "ap_info = %.*s\n" 170 "ap_type = %.*s\n", 171 sizeof (stat->ap_log_id), stat->ap_log_id, 172 sizeof (stat->ap_phys_id), stat->ap_phys_id, 173 stat->ap_r_state, 174 stat->ap_o_state, 175 stat->ap_cond, 176 stat->ap_busy, 177 asctime(localtime(&stat->ap_status_time)), 178 sizeof (stat->ap_info), stat->ap_info, 179 sizeof (stat->ap_type), stat->ap_type); 180 181 /* Copy the slot status. */ 182 *r_state = stat->ap_r_state; 183 *o_state = stat->ap_o_state; 184 185 out: 186 if (stat) { 187 free(stat); 188 } 189 190 if (errstr) { 191 free(errstr); 192 } 193 } 194 195 196 /* 197 * Get the pci_name 198 */ 199 static int 200 scf_get_pci_name(const char *ap_phys_id, char *pci_name) 201 { 202 char *pci_name_ptr; /* pci node name pointer */ 203 char *ap_lid_ptr; /* logical ap_id pointer */ 204 205 int devices_len; /* "/device" length */ 206 int pci_name_len; /* pci node name length */ 207 int ap_lid_len; /* logical ap_id pointer */ 208 209 210 /* 211 * Pick pci node name up from physical ap_id string. 212 * "/devices/pci@XX,YYYYYY:PCI#ZZ" 213 */ 214 215 /* Check the length of physical ap_id string */ 216 if (strlen(ap_phys_id) >= MAXPATHLEN) { 217 return (-1); /* changed */ 218 } 219 220 /* Check the pci node name start, which is after "/devices". */ 221 if (strncmp(SCF_DEV_DIR, ap_phys_id, strlen(SCF_DEV_DIR)) == 0) { 222 devices_len = strlen(SCF_DEV_DIR); 223 } else { 224 devices_len = 0; 225 } 226 /* Check the pci node name end, which is before ":". */ 227 if ((ap_lid_ptr = strchr(ap_phys_id, ':')) == NULL) { 228 ap_lid_len = 0; 229 } else { 230 ap_lid_len = strlen(ap_lid_ptr); 231 } 232 233 /* 234 * Get the head of pci node name string. 235 * Get the length of pci node name string. 236 */ 237 pci_name_ptr = (char *)ap_phys_id + devices_len; 238 pci_name_len = strlen(ap_phys_id) - devices_len - ap_lid_len; 239 240 /* Copy the pci node name. */ 241 (void) strncpy(pci_name, pci_name_ptr, pci_name_len); 242 pci_name[pci_name_len] = '\0'; 243 244 syslog(LOG_DEBUG, "pci device path = %s\n", pci_name); 245 246 return (0); 247 248 } 249 250 251 /* 252 * Get the property of name and model. 253 */ 254 static int 255 scf_get_devinfo(char *dev_name, char *dev_model, const char *pci_name) 256 { 257 char *tmp; /* tmp */ 258 unsigned int devid, funcid; /* bus addr */ 259 unsigned int sdevid, sfuncid; /* sibling bus addr */ 260 261 di_node_t pci_node; /* pci device node */ 262 di_node_t child_node; /* child level node */ 263 di_node_t ap_node; /* hotplugged node */ 264 265 pci_node = ap_node = DI_NODE_NIL; 266 267 268 /* 269 * Take the snap shot of device node configuration, 270 * to get the names of node and model. 271 */ 272 if ((pci_node = di_init(pci_name, DINFOCPYALL)) == DI_NODE_NIL) { 273 syslog(LOG_NOTICE, 274 "Could not get dev info snapshot. errno=%d\n", 275 errno); 276 return (-1); /* changed */ 277 } 278 279 /* 280 * The new child under pci node should be added. Then the 281 * device and model names should be passed, which is in the 282 * node with the minimum bus address. 283 * 284 * - Move to the child node level. 285 * - Search the node with the minimum bus addrress in the 286 * sibling list. 287 */ 288 if ((child_node = di_child_node(pci_node)) == DI_NODE_NIL) { 289 syslog(LOG_NOTICE, "No slot device in snapshot\n"); 290 goto out; 291 } 292 293 ap_node = child_node; 294 if ((tmp = di_bus_addr(child_node)) != NULL) { 295 if (sscanf(tmp, "%x,%x", &devid, &funcid) != 2) { 296 funcid = 0; 297 if (sscanf(tmp, "%x", &devid) != 1) { 298 devid = 0; 299 syslog(LOG_DEBUG, 300 "no bus addrress on device\n"); 301 goto one_child; 302 } 303 } 304 } 305 306 while ((child_node = di_sibling_node(child_node)) != NULL) { 307 if ((tmp = di_bus_addr(child_node)) == NULL) { 308 ap_node = child_node; 309 break; 310 } 311 312 if (sscanf(tmp, "%x,%x", &sdevid, &sfuncid) == 2) { 313 /* 314 * We do need to update the child node 315 * Case 1. devid > sdevid 316 * Case 2. devid == sdevid && funcid > sfuncid 317 */ 318 if ((devid > sdevid) || ((devid == sdevid) && 319 (funcid > sfuncid))) { 320 ap_node = child_node; 321 devid = sdevid; 322 funcid = sfuncid; 323 } 324 325 } else if (sscanf(tmp, "%x", &sdevid) == 1) { 326 /* 327 * We do need to update the child node 328 * Case 1. devid >= sdevid 329 */ 330 if (devid >= sdevid) { 331 ap_node = child_node; 332 devid = sdevid; 333 funcid = 0; 334 } 335 336 } else { 337 ap_node = child_node; 338 break; 339 } 340 } 341 342 one_child: 343 /* 344 * Get the name and model properties. 345 */ 346 tmp = di_node_name(ap_node); 347 if (tmp != NULL) { 348 (void) strlcpy((char *)dev_name, tmp, SCFDATA_DEV_INFO); 349 } 350 351 tmp = NULL; 352 if (di_prop_lookup_strings(DDI_DEV_T_ANY, ap_node, "model", &tmp) > 0) { 353 if (tmp != NULL) { 354 (void) strlcpy((char *)dev_model, tmp, 355 SCFDATA_DEV_INFO); 356 } 357 } 358 359 syslog(LOG_DEBUG, "device: %s@%x,%x [model: %s]\n", 360 dev_name, devid, funcid, dev_model); 361 362 out: 363 di_fini(pci_node); 364 return (0); /* added */ 365 } 366 367 368 void 369 notify_scf_of_hotplug(sysevent_t *ev) 370 { 371 int rc; /* return code */ 372 373 /* For libsysevent */ 374 char *vendor = NULL; /* event vendor */ 375 char *publisher = NULL; /* event publisher */ 376 nvlist_t *ev_attr_list = NULL; /* attribute */ 377 378 /* For libcfgadm */ 379 char *ap_id = NULL; /* attachment point */ 380 cfga_stat_t r_state, o_state; /* slot status */ 381 382 /* For libdevinfo */ 383 char dev_name[SCFDATA_DEV_INFO]; /* name property */ 384 char dev_model[SCFDATA_DEV_INFO]; /* model property */ 385 386 /* Data for SCF */ 387 pci_notify_t pci_notify_dev_info; 388 scfsetphpinfo_t scfdata; 389 scf_slotinfo_t sdata; 390 time_t sec; /* hotplug event current time */ 391 int fd, retry = 0; 392 393 394 /* 395 * Initialization 396 */ 397 r_state = o_state = 0; 398 dev_name[0] = dev_model[0] = '\0'; 399 (void) memset((void *)&pci_notify_dev_info, 0, sizeof (pci_notify_t)); 400 401 /* Get the current time when event picked up. */ 402 sec = time(NULL); 403 404 /* 405 * Check the vendor and publisher name of event. 406 */ 407 vendor = sysevent_get_vendor_name(ev); 408 publisher = sysevent_get_pub_name(ev); 409 /* Check the vendor is "SUNW" */ 410 if (strncmp("SUNW", vendor, strlen("SUNW")) != 0) { 411 /* Just return when not from SUNW */ 412 syslog(LOG_DEBUG, "Event is not a SUNW vendor event\n"); 413 goto out; 414 } 415 416 /* Enough to check "px" at the beginning of string */ 417 if (strncmp("px", publisher, strlen("px")) != 0) { 418 /* Just return when not px event */ 419 syslog(LOG_DEBUG, "Event is not a px publisher event\n"); 420 goto out; 421 } 422 423 /* 424 * Get attribute values of attachment point. 425 */ 426 if (sysevent_get_attr_list(ev, &ev_attr_list) != 0) { 427 /* could not get attribute list */ 428 syslog(LOG_DEBUG, "Could not get attribute list\n"); 429 goto out; 430 } 431 if (nvlist_lookup_string(ev_attr_list, DR_AP_ID, &ap_id) != 0) { 432 /* could not find the attribute from the list */ 433 syslog(LOG_DEBUG, "Could not get ap_id in attribute list\n"); 434 goto out; 435 } 436 437 if (ap_id == NULL || strlen(ap_id) == 0) { 438 syslog(LOG_DEBUG, "ap_id is NULL\n"); 439 goto out; 440 } else { 441 /* 442 * Get the slot status. 443 */ 444 syslog(LOG_DEBUG, "ap_id = %s\n", ap_id); 445 scf_get_slotinfo(ap_id, &r_state, &o_state); 446 } 447 448 syslog(LOG_DEBUG, "r_state = %d\n", r_state); 449 syslog(LOG_DEBUG, "o_state = %d\n", o_state); 450 451 /* 452 * Get the pci name which is needed for both the configure and 453 * unconfigure. 454 */ 455 rc = scf_get_pci_name(ap_id, (char *)pci_notify_dev_info.pci_name); 456 if (rc != 0) { 457 goto out; 458 } 459 460 /* 461 * Event for configure case only, 462 * Get the name and model property 463 */ 464 if (o_state == CFGA_STAT_CONFIGURED) { 465 rc = scf_get_devinfo(dev_name, dev_model, 466 (char *)pci_notify_dev_info.pci_name); 467 if (rc != 0) { 468 goto out; 469 } 470 } 471 /* 472 * Copy the data for SCF. 473 * Initialize Data passed to SCF Driver. 474 */ 475 (void) memset(scfdata.buf, 0, sizeof (scfdata.buf)); 476 477 /* 478 * Set Data passed to SCF Driver. 479 */ 480 scfdata.size = sizeof (scf_slotinfo_t); 481 (void) strlcpy(sdata.ap_id, ap_id, sizeof (sdata.ap_id)); 482 483 sdata.vflag = (uint8_t)0x80; 484 sdata.r_state = (uint32_t)r_state; 485 sdata.o_state = (uint32_t)o_state; 486 sdata.tstamp = (uint64_t)sec; 487 (void) strlcpy(sdata.dev_name, dev_name, sizeof (dev_name)); 488 (void) strlcpy(sdata.dev_model, dev_model, sizeof (sdata.dev_model)); 489 490 (void) memcpy((void *)&(scfdata.buf), (void *)&sdata, 491 sizeof (scf_slotinfo_t)); 492 493 pci_notify_dev_info.r_state = (uint32_t)r_state; 494 pci_notify_dev_info.o_state = (uint32_t)o_state; 495 496 if (!scfdrv_enable) { 497 scfdrv_enable = 1; 498 499 /* 500 * Pass data to SCF driver by ioctl. 501 */ 502 if ((fd = open(SCFIOCDEV, O_WRONLY)) < 0) { 503 syslog(LOG_ERR, "open %s fail", SCFIOCDEV); 504 scfdrv_enable = 0; 505 goto out; 506 } 507 508 while (ioctl(fd, SCFIOCSETPHPINFO, scfdata) < 0) { 509 /* Check Retry Error Number */ 510 if ((errno != EBUSY) && (errno != EIO)) { 511 break; 512 } 513 514 /* Check Retry Times */ 515 if (++retry > SCFRETRY) { 516 break; 517 } 518 519 (void) sleep(SCFIOCWAIT); 520 } 521 522 if ((errno != EBUSY && errno != EIO) || retry > SCFRETRY) { 523 syslog(LOG_ERR, "ioctl to scf driver failed on retry " 524 "limit.\n"); 525 } 526 527 (void) close(fd); 528 scfdrv_enable = 0; 529 } 530 531 out: 532 if (vendor != NULL) { 533 free(vendor); 534 } 535 if (publisher != NULL) { 536 free(publisher); 537 } 538 539 if (ev_attr_list != NULL) { 540 nvlist_free(ev_attr_list); 541 } 542 543 } 544