xref: /illumos-gate/usr/src/cmd/oplhpd/scf_notify.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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