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
config_error(cfga_err_t err,const char * func_name,const char * errstr,const char * ap_id)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
scf_get_slotinfo(char * ap_pid,cfga_stat_t * r_state,cfga_stat_t * o_state)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
scf_get_pci_name(const char * ap_phys_id,char * pci_name)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
scf_get_devinfo(char * dev_name,char * dev_model,const char * pci_name)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
notify_scf_of_hotplug(sysevent_t * ev)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