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 */
54static 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 */
70typedef 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 */
90typedef 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 */
99void scf_get_slotinfo(char *ap_id, cfga_stat_t *o_state,
100		cfga_stat_t *r_state);
101static int scf_get_pci_name(const char *ap_phys_id, char *pci_name);
102static int scf_get_devinfo(char *dev_name, char *dev_model,
103		const char *pci_name);
104void notify_scf_of_hotplug(sysevent_t *ev);
105
106
107/*
108 * Error report utility for libcfgadm functions
109 */
110void
111config_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 */
134void
135scf_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
178out:
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 */
192static int
193scf_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 */
247static int
248scf_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
335one_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
355out:
356	di_fini(pci_node);
357	return (0); /* added */
358}
359
360
361void
362notify_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
519out:
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