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