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.
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.
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]
22 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
28 #include <config_admin.h>
31 #include <libsysevent.h>
32 #include <libdevinfo.h>
33 #include <libnvpair.h>
38 #include <sys/types.h>
40 #include <sys/sysevent/dr.h>
41 #include <sys/scfd/opcioif.h>
45 #define SCF_DEV_DIR "/devices" /* device base dir */
50 * Connection for SCF driver
53 /* Check the availability of SCF driver */
54 static int scfdrv_enable
= 0;
57 /* Device for SCF Driver */
58 #define SCFIOCDEV "/devices/pseudo/scfd@200:rasctl"
61 #define SCFDATA_DEV_INFO 32
62 #define SCFDATA_APID 1054
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
71 char ap_id
[SCFDATA_APID
];
77 char dev_name
[SCFDATA_DEV_INFO
];
78 char dev_model
[SCFDATA_DEV_INFO
];
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.
91 char pci_name
[MAXPATHLEN
];
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
);
108 * Error report utility for libcfgadm functions
111 config_error(cfga_err_t err
, const char *func_name
, const char *errstr
,
116 ep
= config_strerror(err
);
118 ep
= "configuration administration unknown error";
121 if (errstr
!= NULL
&& *errstr
!= '\0') {
122 syslog(LOG_DEBUG
, "%s: %s (%s), ap_id = %s\n",
123 func_name
, ep
, errstr
, ap_id
);
125 syslog(LOG_DEBUG
, "%s: %s , ap_id = %s\n",
126 func_name
, ep
, ap_id
);
132 * Get the slot status.
135 scf_get_slotinfo(char *ap_pid
, cfga_stat_t
*r_state
, cfga_stat_t
*o_state
)
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 */
143 * Get the attachment point information.
145 rv
= config_list_ext(1, (char *const *)&ap_pid
, &stat
, &nlist
, NULL
,
149 config_error(rv
, "config_list_ext", errstr
, ap_pid
);
154 syslog(LOG_DEBUG
, "\n"
156 "ap_phys_id = %.*s\n"
161 "ap_status_time = %s"
164 sizeof (stat
->ap_log_id
), stat
->ap_log_id
,
165 sizeof (stat
->ap_phys_id
), stat
->ap_phys_id
,
170 asctime(localtime(&stat
->ap_status_time
)),
171 sizeof (stat
->ap_info
), stat
->ap_info
,
172 sizeof (stat
->ap_type
), stat
->ap_type
);
174 /* Copy the slot status. */
175 *r_state
= stat
->ap_r_state
;
176 *o_state
= stat
->ap_o_state
;
193 scf_get_pci_name(const char *ap_phys_id
, char *pci_name
)
195 char *pci_name_ptr
; /* pci node name pointer */
196 char *ap_lid_ptr
; /* logical ap_id pointer */
198 int devices_len
; /* "/device" length */
199 int pci_name_len
; /* pci node name length */
200 int ap_lid_len
; /* logical ap_id pointer */
204 * Pick pci node name up from physical ap_id string.
205 * "/devices/pci@XX,YYYYYY:PCI#ZZ"
208 /* Check the length of physical ap_id string */
209 if (strlen(ap_phys_id
) >= MAXPATHLEN
) {
210 return (-1); /* changed */
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
);
219 /* Check the pci node name end, which is before ":". */
220 if ((ap_lid_ptr
= strchr(ap_phys_id
, ':')) == NULL
) {
223 ap_lid_len
= strlen(ap_lid_ptr
);
227 * Get the head of pci node name string.
228 * Get the length of pci node name string.
230 pci_name_ptr
= (char *)ap_phys_id
+ devices_len
;
231 pci_name_len
= strlen(ap_phys_id
) - devices_len
- ap_lid_len
;
233 /* Copy the pci node name. */
234 (void) strncpy(pci_name
, pci_name_ptr
, pci_name_len
);
235 pci_name
[pci_name_len
] = '\0';
237 syslog(LOG_DEBUG
, "pci device path = %s\n", pci_name
);
245 * Get the property of name and model.
248 scf_get_devinfo(char *dev_name
, char *dev_model
, const char *pci_name
)
251 unsigned int devid
, funcid
; /* bus addr */
252 unsigned int sdevid
, sfuncid
; /* sibling bus addr */
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 */
258 pci_node
= ap_node
= DI_NODE_NIL
;
262 * Take the snap shot of device node configuration,
263 * to get the names of node and model.
265 if ((pci_node
= di_init(pci_name
, DINFOCPYALL
)) == DI_NODE_NIL
) {
267 "Could not get dev info snapshot. errno=%d\n",
269 return (-1); /* changed */
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.
277 * - Move to the child node level.
278 * - Search the node with the minimum bus addrress in the
281 if ((child_node
= di_child_node(pci_node
)) == DI_NODE_NIL
) {
282 syslog(LOG_NOTICE
, "No slot device in snapshot\n");
286 ap_node
= child_node
;
287 if ((tmp
= di_bus_addr(child_node
)) != NULL
) {
288 if (sscanf(tmp
, "%x,%x", &devid
, &funcid
) != 2) {
290 if (sscanf(tmp
, "%x", &devid
) != 1) {
293 "no bus addrress on device\n");
299 while ((child_node
= di_sibling_node(child_node
)) != NULL
) {
300 if ((tmp
= di_bus_addr(child_node
)) == NULL
) {
301 ap_node
= child_node
;
305 if (sscanf(tmp
, "%x,%x", &sdevid
, &sfuncid
) == 2) {
307 * We do need to update the child node
308 * Case 1. devid > sdevid
309 * Case 2. devid == sdevid && funcid > sfuncid
311 if ((devid
> sdevid
) || ((devid
== sdevid
) &&
312 (funcid
> sfuncid
))) {
313 ap_node
= child_node
;
318 } else if (sscanf(tmp
, "%x", &sdevid
) == 1) {
320 * We do need to update the child node
321 * Case 1. devid >= sdevid
323 if (devid
>= sdevid
) {
324 ap_node
= child_node
;
330 ap_node
= child_node
;
337 * Get the name and model properties.
339 tmp
= di_node_name(ap_node
);
341 (void) strlcpy((char *)dev_name
, tmp
, SCFDATA_DEV_INFO
);
345 if (di_prop_lookup_strings(DDI_DEV_T_ANY
, ap_node
, "model", &tmp
) > 0) {
347 (void) strlcpy((char *)dev_model
, tmp
,
352 syslog(LOG_DEBUG
, "device: %s@%x,%x [model: %s]\n",
353 dev_name
, devid
, funcid
, dev_model
);
357 return (0); /* added */
362 notify_scf_of_hotplug(sysevent_t
*ev
)
364 int rc
; /* return code */
366 /* For libsysevent */
367 char *vendor
= NULL
; /* event vendor */
368 char *publisher
= NULL
; /* event publisher */
369 nvlist_t
*ev_attr_list
= NULL
; /* attribute */
372 char *ap_id
= NULL
; /* attachment point */
373 cfga_stat_t r_state
, o_state
; /* slot status */
376 char dev_name
[SCFDATA_DEV_INFO
]; /* name property */
377 char dev_model
[SCFDATA_DEV_INFO
]; /* model property */
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 */
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
));
394 /* Get the current time when event picked up. */
398 * Check the vendor and publisher name of event.
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");
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");
418 * Get attribute values of attachment point.
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");
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");
431 if (ap_id
== NULL
|| strlen(ap_id
) == 0) {
432 syslog(LOG_DEBUG
, "ap_id is NULL\n");
436 * Get the slot status.
438 syslog(LOG_DEBUG
, "ap_id = %s\n", ap_id
);
439 scf_get_slotinfo(ap_id
, &r_state
, &o_state
);
442 syslog(LOG_DEBUG
, "r_state = %d\n", r_state
);
443 syslog(LOG_DEBUG
, "o_state = %d\n", o_state
);
446 * Get the pci name which is needed for both the configure and
449 rc
= scf_get_pci_name(ap_id
, (char *)pci_notify_dev_info
.pci_name
);
455 * Event for configure case only,
456 * Get the name and model property
458 if (o_state
== CFGA_STAT_CONFIGURED
) {
459 rc
= scf_get_devinfo(dev_name
, dev_model
,
460 (char *)pci_notify_dev_info
.pci_name
);
466 * Copy the data for SCF.
467 * Initialize Data passed to SCF Driver.
469 (void) memset(scfdata
.buf
, 0, sizeof (scfdata
.buf
));
472 * Set Data passed to SCF Driver.
474 scfdata
.size
= sizeof (scf_slotinfo_t
);
475 (void) strlcpy(sdata
.ap_id
, ap_id
, sizeof (sdata
.ap_id
));
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
));
484 (void) memcpy((void *)&(scfdata
.buf
), (void *)&sdata
,
485 sizeof (scf_slotinfo_t
));
487 pci_notify_dev_info
.r_state
= (uint32_t)r_state
;
488 pci_notify_dev_info
.o_state
= (uint32_t)o_state
;
490 if (!scfdrv_enable
) {
494 * Pass data to SCF driver by ioctl.
496 if ((fd
= open(SCFIOCDEV
, O_WRONLY
)) < 0) {
497 syslog(LOG_ERR
, "open %s fail", SCFIOCDEV
);
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
);
510 syslog(LOG_ERR
, "SCFIOCSETPHPINFO failed: %s.",
520 if (vendor
!= NULL
) {
523 if (publisher
!= NULL
) {
527 if (ev_attr_list
!= NULL
) {
528 nvlist_free(ev_attr_list
);