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