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