xref: /illumos-gate/usr/src/cmd/fm/modules/common/fabric-xlate/fx_subr.c (revision dd72704bd9e794056c558153663c739e2012d721)
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 /*
23  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2019 Joyent, Inc.
25  */
26 #include <strings.h>
27 #include <fm/topo_hc.h>
28 #include <sys/fm/util.h>
29 #include <libxml/xpath.h>
30 #include <libxml/parser.h>
31 #include <libxml/xpathInternals.h>
32 #include <libxml/tree.h>
33 
34 #include "fabric-xlate.h"
35 
36 #define	HAS_PROP(node, name) xmlHasProp(node, (const xmlChar *)name)
37 #define	GET_PROP(node, name) ((char *)xmlGetProp(node, (const xmlChar *)name))
38 #define	FREE_PROP(prop) xmlFree((xmlChar *)prop)
39 
40 extern xmlXPathContextPtr fab_xpathCtx;
41 
42 /* ARGSUSED */
43 int
44 fab_prep_basic_erpt(fmd_hdl_t *hdl, nvlist_t *nvl, nvlist_t *erpt,
45     boolean_t isRC)
46 {
47 	uint64_t	*now;
48 	uint64_t	ena;
49 	uint_t		nelem;
50 	nvlist_t	*detector, *new_detector;
51 	char		rcpath[255];
52 	int		err = 0;
53 
54 	/* Grab the tod, ena and detector(FMRI) */
55 	err |= nvlist_lookup_uint64_array(nvl, "__tod", &now, &nelem);
56 	err |= nvlist_lookup_uint64(nvl, "ena", &ena);
57 	err |= nvlist_lookup_nvlist(nvl, FM_EREPORT_DETECTOR, &detector);
58 	if (err)
59 		return (err);
60 
61 	/* Make a copy of the detector */
62 	err = nvlist_dup(detector, &new_detector, NV_UNIQUE_NAME);
63 	if (err)
64 		return (err);
65 
66 	/* Copy the tod and ena to erpt */
67 	(void) nvlist_add_uint64(erpt, FM_EREPORT_ENA, ena);
68 	(void) nvlist_add_uint64_array(erpt, "__tod", now, nelem);
69 
70 	/*
71 	 * Create the correct ROOT FMRI from PCIe leaf fabric ereports.	 Used
72 	 * only by fab_prep_fake_rc_erpt.  See the fab_pciex_fake_rc_erpt_tbl
73 	 * comments for more information.
74 	 */
75 	if (isRC && fab_get_rcpath(hdl, nvl, rcpath)) {
76 		/* Create the correct PCIe RC new_detector aka FMRI */
77 		(void) nvlist_remove(new_detector, FM_FMRI_DEV_PATH,
78 		    DATA_TYPE_STRING);
79 		(void) nvlist_add_string(new_detector, FM_FMRI_DEV_PATH,
80 		    rcpath);
81 	}
82 
83 	/* Copy the FMRI to erpt */
84 	(void) nvlist_add_nvlist(erpt, FM_EREPORT_DETECTOR, new_detector);
85 
86 	nvlist_free(new_detector);
87 	return (err);
88 }
89 
90 void
91 fab_send_tgt_erpt(fmd_hdl_t *hdl, fab_data_t *data, const char *class,
92     boolean_t isPrimary)
93 {
94 	nvlist_t	*nvl = data->nvl;
95 	nvlist_t	*erpt;
96 	char		*fmri = NULL;
97 	uint32_t	tgt_trans;
98 	uint64_t	tgt_addr;
99 	uint16_t	tgt_bdf;
100 
101 	if (isPrimary) {
102 		tgt_trans = data->pcie_ue_tgt_trans;
103 		tgt_addr = data->pcie_ue_tgt_addr;
104 		tgt_bdf = data->pcie_ue_tgt_bdf;
105 	} else {
106 		tgt_trans = data->pcie_sue_tgt_trans;
107 		tgt_addr = data->pcie_sue_tgt_addr;
108 		tgt_bdf = data->pcie_sue_tgt_bdf;
109 	}
110 
111 	fmd_hdl_debug(hdl, "Sending Target Ereport: "
112 	    "type 0x%x addr 0x%llx fltbdf 0x%x\n",
113 	    tgt_trans, tgt_addr, tgt_bdf);
114 
115 	if (!tgt_trans)
116 		return;
117 
118 	if ((tgt_trans == PF_ADDR_PIO) && tgt_addr)
119 		fmri = fab_find_addr(hdl, nvl, tgt_addr);
120 	else if ((tgt_trans == PF_ADDR_CFG || (tgt_trans == PF_ADDR_DMA)) &&
121 	    tgt_bdf)
122 		fmri = fab_find_bdf(hdl, nvl, tgt_bdf);
123 
124 	if (fmri) {
125 		uint64_t	*now;
126 		uint64_t	ena;
127 		uint_t		nelem;
128 		nvlist_t	*detector;
129 		int		err = 0;
130 
131 		/* Allocate space for new erpt */
132 		if (nvlist_alloc(&erpt, NV_UNIQUE_NAME, 0) != 0)
133 			goto done;
134 
135 		/* Generate the target ereport class */
136 		(void) snprintf(fab_buf, FM_MAX_CLASS, "ereport.io.%s.%s",
137 		    PCI_ERROR_SUBCLASS, class);
138 		(void) nvlist_add_string(erpt, FM_CLASS, fab_buf);
139 
140 		/* Grab the tod, ena and detector(FMRI) */
141 		err |= nvlist_lookup_uint64_array(nvl, "__tod", &now, &nelem);
142 		err |= nvlist_lookup_uint64(nvl, "ena", &ena);
143 
144 		/* Copy the tod and ena to erpt */
145 		(void) nvlist_add_uint64(erpt, FM_EREPORT_ENA, ena);
146 		(void) nvlist_add_uint64_array(erpt, "__tod", now, nelem);
147 
148 		/* Create the correct FMRI */
149 		if (nvlist_alloc(&detector, NV_UNIQUE_NAME, 0) != 0) {
150 			nvlist_free(erpt);
151 			goto done;
152 		}
153 		(void) nvlist_add_uint8(detector, FM_VERSION,
154 		    FM_DEV_SCHEME_VERSION);
155 		(void) nvlist_add_string(detector, FM_FMRI_SCHEME,
156 		    FM_FMRI_SCHEME_DEV);
157 		(void) nvlist_add_string(detector, FM_FMRI_DEV_PATH, fmri);
158 		(void) nvlist_add_nvlist(erpt, FM_EREPORT_DETECTOR, detector);
159 		nvlist_free(detector);
160 
161 		/* Add the address payload */
162 		(void) nvlist_add_uint64(erpt, PCI_PA, tgt_addr);
163 
164 		fmd_hdl_debug(hdl, "Sending target ereport: %s 0x%x\n",
165 		    fab_buf, tgt_addr);
166 		fmd_xprt_post(hdl, fab_fmd_xprt, erpt, 0);
167 		if (fmd_xprt_error(hdl, fab_fmd_xprt))
168 			goto done;
169 		fmd_hdl_strfree(hdl, fmri);
170 	} else {
171 		fmd_hdl_debug(hdl,
172 		    "Cannot find Target FMRI addr:0x%llx bdf 0x%x\n",
173 		    tgt_addr, tgt_bdf);
174 	}
175 
176 	return;
177 done:
178 	if (fmri)
179 		xmlFree(fmri);
180 	fmd_hdl_debug(hdl, "Failed to send Target PCI ereport\n");
181 }
182 
183 void
184 fab_send_erpt(fmd_hdl_t *hdl, fab_data_t *data, fab_err_tbl_t *tbl)
185 {
186 	fab_erpt_tbl_t	*erpt_tbl, *entry;
187 	nvlist_t	*erpt;
188 	uint32_t	reg;
189 	int		err;
190 
191 	erpt_tbl = tbl->erpt_tbl;
192 	if (tbl->reg_size == 16) {
193 		reg = (uint32_t)*((uint16_t *)
194 		    ((uint32_t)data + tbl->reg_offset));
195 	} else {
196 		reg = *((uint32_t *)((uint32_t)data + tbl->reg_offset));
197 	}
198 
199 	for (entry = erpt_tbl; entry->err_class; entry++) {
200 		if (!(reg & entry->reg_bit))
201 			continue;
202 
203 		if (nvlist_alloc(&erpt, NV_UNIQUE_NAME, 0) != 0)
204 			goto done;
205 
206 		err = tbl->fab_prep(hdl, data, erpt, entry);
207 		if (err != 0 && err != PF_EREPORT_IGNORE) {
208 			fmd_hdl_debug(hdl, "Prepping ereport failed: "
209 			    "class = %s\n", entry->err_class);
210 			nvlist_free(erpt);
211 			continue;
212 		}
213 
214 		if (data->pcie_rp_send_all) {
215 			fab_send_erpt_all_rps(hdl, erpt);
216 			nvlist_free(erpt);
217 			return;
218 		}
219 
220 		fmd_hdl_debug(hdl, "Sending ereport: %s 0x%x\n", fab_buf, reg);
221 		fmd_xprt_post(hdl, fab_fmd_xprt, erpt, 0);
222 		if (fmd_xprt_error(hdl, fab_fmd_xprt)) {
223 			fmd_hdl_debug(hdl, "Failed to send PCI ereport\n");
224 			return;
225 		}
226 	}
227 
228 	return;
229 done:
230 	fmd_hdl_debug(hdl, "Failed  to send PCI ereport\n");
231 }
232 
233 char *
234 fab_xpath_query(fmd_hdl_t *hdl, const char *query)
235 {
236 	xmlXPathObjectPtr xpathObj;
237 	xmlNodeSetPtr nodes;
238 	char *temp, *res;
239 
240 	fmd_hdl_debug(hdl, "xpathObj query %s\n", query);
241 
242 	xpathObj = xmlXPathEvalExpression((const xmlChar *)query,
243 	    fab_xpathCtx);
244 
245 	if (xpathObj == NULL)
246 		return (NULL);
247 
248 	fmd_hdl_debug(hdl, "xpathObj 0x%p type %d\n", xpathObj,
249 	    xpathObj->type);
250 	nodes = xpathObj->nodesetval;
251 
252 	if (nodes) {
253 		temp = (char *)xmlNodeGetContent(nodes->nodeTab[0]);
254 		fmd_hdl_debug(hdl, "query result: %s\n", temp);
255 		res = fmd_hdl_strdup(hdl, temp, FMD_SLEEP);
256 		xmlFree(temp);
257 		xmlXPathFreeObject(xpathObj);
258 		return (res);
259 	}
260 	xmlXPathFreeObject(xpathObj);
261 	return (NULL);
262 }
263 
264 #define	FAB_HC2DEV_QUERY_SIZE_MIN 160
265 #define	FAB_HC2DEV_QUERY_SIZE(sz) \
266 	((sz + FAB_HC2DEV_QUERY_SIZE_MIN) * sizeof (char))
267 
268 /*
269  * hc_path is in form of "/motherboard=0/hostbridge=0/pciexrc=0"
270  */
271 boolean_t
272 fab_hc2dev(fmd_hdl_t *hdl, const char *hc_path, char **dev_path)
273 {
274 	char *query;
275 	uint_t len = FAB_HC2DEV_QUERY_SIZE_MIN + strlen(hc_path);
276 
277 	query = fmd_hdl_alloc(hdl, len, FMD_SLEEP);
278 	(void) snprintf(query, len, "//propval[@name='resource' and contains("
279 	    "substring(@value, string-length(@value) - %d + 1), '%s')]"
280 	    "/parent::*/following-sibling::*/propval[@name='dev']/@value",
281 	    strlen(hc_path) + 1, hc_path);
282 
283 	*dev_path = fab_xpath_query(hdl, query);
284 
285 	fmd_hdl_free(hdl, query, len);
286 
287 	return (*dev_path != NULL);
288 }
289 
290 static boolean_t
291 fab_hc_path(fmd_hdl_t *hdl, nvlist_t *detector, char **hcpath, size_t *lenp)
292 {
293 	char c, *name, *id, *buf;
294 	uint_t i, size;
295 	nvlist_t **hcl;
296 	size_t len = 0, buf_size = 0;
297 
298 	if (nvlist_lookup_nvlist_array(detector, FM_FMRI_HC_LIST, &hcl,
299 	    &size) != 0)
300 		return (B_FALSE);
301 
302 	for (i = 0; i < size; i++) {
303 		if (nvlist_lookup_string(hcl[i], FM_FMRI_HC_NAME, &name) != 0)
304 			return (B_FALSE);
305 		if (nvlist_lookup_string(hcl[i], FM_FMRI_HC_ID, &id) != 0)
306 			return (B_FALSE);
307 		buf_size += snprintf(&c, 1, "/%s=%s", name, id);
308 	}
309 
310 	buf_size++;
311 	buf = fmd_hdl_alloc(hdl, buf_size, FMD_SLEEP);
312 
313 	for (i = 0; i < size; i++) {
314 		(void) nvlist_lookup_string(hcl[i], FM_FMRI_HC_NAME, &name);
315 		(void) nvlist_lookup_string(hcl[i], FM_FMRI_HC_ID, &id);
316 		len += snprintf(buf + len, buf_size - len, "/%s=%s", name, id);
317 	}
318 
319 	*hcpath = buf;
320 	*lenp = buf_size;
321 
322 	return (B_TRUE);
323 }
324 
325 boolean_t
326 fab_hc2dev_nvl(fmd_hdl_t *hdl, nvlist_t *detector, char **dev_path)
327 {
328 	char *hcl;
329 	size_t len;
330 
331 	if (! fab_hc_path(hdl, detector, &hcl, &len))
332 		return (B_FALSE);
333 
334 	(void) fab_hc2dev(hdl, hcl, dev_path);
335 
336 	fmd_hdl_free(hdl, hcl, len);
337 
338 	return (*dev_path != NULL);
339 }
340 
341 boolean_t
342 fab_get_hcpath(fmd_hdl_t *hdl, nvlist_t *nvl, char **hcpath, size_t *len)
343 {
344 	nvlist_t *detector;
345 	char *scheme;
346 
347 	if (nvlist_lookup_nvlist(nvl, FM_EREPORT_DETECTOR, &detector) != 0 ||
348 	    nvlist_lookup_string(detector, FM_FMRI_SCHEME, &scheme) != 0 ||
349 	    ! STRCMP(scheme, FM_FMRI_SCHEME_HC))
350 		return (B_FALSE);
351 
352 	return (fab_hc_path(hdl, detector, hcpath, len));
353 }
354 
355 char *
356 fab_find_rppath_by_df(fmd_hdl_t *hdl, nvlist_t *nvl, uint8_t df)
357 {
358 	char	query[500];
359 	char	str[10];
360 	char	*hcpath;
361 	size_t	len;
362 
363 	(void) snprintf(str, sizeof (str), "%0hhx", df);
364 
365 	/*
366 	 * get the string form of the hc detector, eg
367 	 * /chassis=0/motherboard=0/hostbridge=0
368 	 */
369 	if (!fab_get_hcpath(hdl, nvl, &hcpath, &len))
370 		return (NULL);
371 
372 	/*
373 	 * Explanation of the XSL XPATH Query
374 	 * Line 1: Look at all nodes with the node name "propval"
375 	 * Line 2: See if the "BDF" of the node matches DF
376 	 * Line 3-4: See if the the node is pciexrc
377 	 * Line 5-6: See if the "ASRU" contains root complex
378 	 * Line 7-8: Go up one level and get prop value of io/dev
379 	 */
380 	(void) snprintf(query, sizeof (query), "//propval["
381 	    "@name='BDF' and contains(substring(@value, "
382 	    "string-length(@value) - 1), '%s')]"
383 	    "/parent::*/parent::*/propgroup[@name='pci']/propval"
384 	    "[@name='extended-capabilities' and @value='%s']"
385 	    "/parent::*/parent::*/propgroup[@name='protocol']"
386 	    "/propval[@name='resource' and contains(@value, '%s')]"
387 	    "/parent::*/parent::*/propgroup[@name='io']"
388 	    "/propval[@name='dev']/@value", str, PCIEX_ROOT, hcpath);
389 
390 	fmd_hdl_free(hdl, hcpath, len);
391 
392 	return (fab_xpath_query(hdl, query));
393 }
394 
395 char *
396 fab_find_rppath_by_devbdf(fmd_hdl_t *hdl, nvlist_t *nvl, pcie_req_id_t bdf)
397 {
398 	xmlXPathObjectPtr xpathObj;
399 	xmlNodeSetPtr nodes;
400 	xmlNodePtr devNode;
401 	char	*retval, *temp;
402 	char	query[500];
403 	int	i, size, bus, dev, fn;
404 	char	*hcpath;
405 	size_t	len;
406 
407 	if (bdf != (uint16_t)-1) {
408 		bus = (bdf & PCIE_REQ_ID_BUS_MASK) >> PCIE_REQ_ID_BUS_SHIFT;
409 		dev = (bdf & PCIE_REQ_ID_DEV_MASK) >> PCIE_REQ_ID_DEV_SHIFT;
410 		fn = (bdf & PCIE_REQ_ID_FUNC_MASK) >> PCIE_REQ_ID_FUNC_SHIFT;
411 	}
412 
413 	/*
414 	 * get the string form of the hc detector, eg
415 	 * /chassis=0/motherboard=0/hostbridge=0
416 	 */
417 	if (!fab_get_hcpath(hdl, nvl, &hcpath, &len))
418 		goto fail;
419 
420 	/*
421 	 * Explanation of the XSL XPATH Query
422 	 * Line 1: Look at all nodes with the node name "propval"
423 	 * Line 2-3: See if the "value" of the node ends with correct PCIEx BDF
424 	 * Line 4-5: See if the "value" of the node ends with correct PCI BDF
425 	 * Line 6: Go up one level to the parent of the current node
426 	 * Line 7: See if child node contains "ASRU" with the same PCIe Root
427 	 * Line 8: Go up see all the ancestors
428 	 */
429 	(void) snprintf(query, sizeof (query), "//propval["
430 	    "contains(substring(@value, string-length(@value) - 34), "
431 	    "'pciexbus=%d/pciexdev=%d/pciexfn=%d') or "
432 	    "contains(substring(@value, string-length(@value) - 28), "
433 	    "'pcibus=%d/pcidev=%d/pcifn=%d')"
434 	    "]/parent::"
435 	    "*/propval[@name='resource' and contains(@value, '%s')]"
436 	    "/ancestor::*",
437 	    bus, dev, fn, bus, dev, fn, hcpath);
438 
439 	fmd_hdl_free(hdl, hcpath, len);
440 
441 	fmd_hdl_debug(hdl, "xpathObj query %s\n", query);
442 
443 	xpathObj = xmlXPathEvalExpression((const xmlChar *)query, fab_xpathCtx);
444 
445 	if (xpathObj == NULL)
446 		goto fail;
447 
448 	nodes = xpathObj->nodesetval;
449 	size = (nodes) ? nodes->nodeNr : 0;
450 
451 	fmd_hdl_debug(hdl, "xpathObj 0x%p type %d size %d\n",
452 	    xpathObj, xpathObj->type, size);
453 
454 	for (i = 0; i < size; i++) {
455 		devNode = nodes->nodeTab[i];
456 		if (STRCMP(devNode->name, "range") &&
457 		    HAS_PROP(devNode, "name")) {
458 			char *tprop = GET_PROP(devNode, "name");
459 
460 			/* find "range name='pciexrc'" in ancestors */
461 			if (STRCMP(tprop, PCIEX_ROOT)) {
462 				/* go down to the pciexrc instance node */
463 				FREE_PROP(tprop);
464 				devNode = nodes->nodeTab[i+1];
465 				goto found;
466 			}
467 			FREE_PROP(tprop);
468 		}
469 	}
470 	goto fail;
471 
472 found:
473 	/* Traverse down the xml tree to find the right propgroup */
474 	for (devNode = devNode->children; devNode; devNode = devNode->next) {
475 		if (STRCMP(devNode->name, "propgroup")) {
476 			char *tprop = GET_PROP(devNode, "name");
477 
478 			if (STRCMP(tprop, "io")) {
479 				FREE_PROP(tprop);
480 				goto propgroup;
481 			}
482 			FREE_PROP(tprop);
483 		}
484 	}
485 	goto fail;
486 
487 propgroup:
488 	/* Retrive the "dev" propval and return */
489 	for (devNode = devNode->children; devNode; devNode = devNode->next) {
490 		if (STRCMP(devNode->name, "propval")) {
491 			char *tprop = GET_PROP(devNode, "name");
492 
493 			if (STRCMP(tprop, "dev")) {
494 				temp = GET_PROP(devNode, "value");
495 				retval = fmd_hdl_strdup(hdl, temp, FMD_SLEEP);
496 				fmd_hdl_debug(hdl, "RP Path: %s\n", retval);
497 				xmlFree(temp);
498 				xmlXPathFreeObject(xpathObj);
499 			}
500 			FREE_PROP(tprop);
501 
502 			return (retval);
503 		}
504 	}
505 fail:
506 	if (xpathObj != NULL)
507 		xmlXPathFreeObject(xpathObj);
508 	return (NULL);
509 }
510 
511 char *
512 fab_find_rppath_by_devpath(fmd_hdl_t *hdl, const char *devpath)
513 {
514 	char	query[500];
515 
516 	/*
517 	 * Explanation of the XSL XPATH Query
518 	 * Line 1: Look at all nodes with the node name "propval"
519 	 * Line 2: See if the node is pciexrc
520 	 * Line 3: Go up to the io pgroup
521 	 * Line 4: See if the "dev" prop is parent of devpath
522 	 * Line 5: Get the 'dev' prop
523 	 */
524 	(void) snprintf(query, sizeof (query), "//propval"
525 	    "[@name='extended-capabilities' and @value='%s']"
526 	    "/parent::*/parent::*/propgroup[@name='io']"
527 	    "/propval[@name='dev' and starts-with('%s', concat(@value, '/'))]"
528 	    "/@value", PCIEX_ROOT, devpath);
529 
530 	return (fab_xpath_query(hdl, query));
531 }
532 
533 /* ARGSUSED */
534 boolean_t
535 fab_get_rcpath(fmd_hdl_t *hdl, nvlist_t *nvl, char *rcpath)
536 {
537 	nvlist_t	*detector;
538 	char		*path, *scheme;
539 
540 	if (nvlist_lookup_nvlist(nvl, FM_EREPORT_DETECTOR, &detector) != 0)
541 		goto fail;
542 	if (nvlist_lookup_string(detector, FM_FMRI_SCHEME, &scheme) != 0)
543 		goto fail;
544 
545 	if (STRCMP(scheme, FM_FMRI_SCHEME_DEV)) {
546 		if (nvlist_lookup_string(detector, FM_FMRI_DEV_PATH,
547 		    &path) != 0)
548 			goto fail;
549 		(void) strncpy(rcpath, path, FM_MAX_CLASS);
550 	} else if (STRCMP(scheme, FM_FMRI_SCHEME_HC)) {
551 		/*
552 		 * This should only occur for ereports that come from the RC
553 		 * itself.  In this case convert HC scheme to dev path.
554 		 */
555 		if (fab_hc2dev_nvl(hdl, detector, &path)) {
556 			(void) strncpy(rcpath, path, FM_MAX_CLASS);
557 			fmd_hdl_strfree(hdl, path);
558 		} else {
559 			goto fail;
560 		}
561 	} else {
562 		return (B_FALSE);
563 	}
564 
565 	/*
566 	 * Extract the RC path by taking the first device in the dev path
567 	 *
568 	 * /pci@0,0/pci8086,3605@2/pci8086,3500@0/pci8086,3514@1/pci8086,105e@0
569 	 * - to -
570 	 * /pci@0,0
571 	 */
572 	path = strchr(rcpath + 1, '/');
573 	if (path)
574 		path[0] = '\0';
575 
576 	return (B_TRUE);
577 fail:
578 	return (B_FALSE);
579 }
580 
581 char *
582 fab_find_bdf(fmd_hdl_t *hdl, nvlist_t *nvl, pcie_req_id_t bdf)
583 {
584 	char	*retval;
585 	char	query[500];
586 	int	bus, dev, fn;
587 	char	rcpath[255];
588 
589 	if (bdf != (uint16_t)-1) {
590 		bus = (bdf & PCIE_REQ_ID_BUS_MASK) >> PCIE_REQ_ID_BUS_SHIFT;
591 		dev = (bdf & PCIE_REQ_ID_DEV_MASK) >> PCIE_REQ_ID_DEV_SHIFT;
592 		fn = (bdf & PCIE_REQ_ID_FUNC_MASK) >> PCIE_REQ_ID_FUNC_SHIFT;
593 	}
594 
595 	if (!fab_get_rcpath(hdl, nvl, rcpath))
596 		goto fail;
597 
598 	/*
599 	 * Explanation of the XSL XPATH Query
600 	 * Line 1: Look at all nodes with the node name "propval"
601 	 * Line 2-3: See if the "value" of the node ends with correct PCIEx BDF
602 	 * Line 4-5: See if the "value" of the node ends with correct PCI BDF
603 	 * Line 6: Go up one level to the parent of the current node
604 	 * Line 7: See if child node contains "ASRU" with the same PCIe Root
605 	 * Line 8: Traverse up the parent and the other siblings and look for
606 	 *	   the io "propgroup" and get the value of the dev "propval"
607 	 */
608 	(void) snprintf(query, sizeof (query), "//propval["
609 	    "contains(substring(@value, string-length(@value) - 34), "
610 	    "'pciexbus=%d/pciexdev=%d/pciexfn=%d') or "
611 	    "contains(substring(@value, string-length(@value) - 28), "
612 	    "'pcibus=%d/pcidev=%d/pcifn=%d')"
613 	    "]/parent::"
614 	    "*/propval[@name='ASRU' and contains(@value, '%s')]"
615 	    "/parent::*/following-sibling::*[@name='io']/propval[@name='dev']/"
616 	    "@value", bus, dev, fn, bus, dev, fn, rcpath);
617 
618 	retval = fab_xpath_query(hdl, query);
619 	if (retval) {
620 		fmd_hdl_debug(hdl, "BDF Dev Path: %s\n", retval);
621 		return (retval);
622 	}
623 fail:
624 	return (NULL);
625 }
626 
627 char *
628 fab_find_addr(fmd_hdl_t *hdl, nvlist_t *nvl, uint64_t addr)
629 {
630 	xmlXPathObjectPtr xpathObj;
631 	xmlNodeSetPtr nodes;
632 	xmlNodePtr devNode;
633 	char *retval, *temp;
634 	char query[500];
635 	int size, i, j;
636 	uint32_t prop[50];
637 	char *token;
638 	pci_regspec_t *assign_p;
639 	uint64_t low, hi;
640 	char rcpath[255];
641 
642 	if (!fab_get_rcpath(hdl, nvl, rcpath))
643 		goto fail;
644 
645 	(void) snprintf(query, sizeof (query), "//propval["
646 	    "@name='ASRU' and contains(@value, '%s')]/"
647 	    "parent::*/following-sibling::*[@name='pci']/"
648 	    "propval[@name='assigned-addresses']", rcpath);
649 
650 	fmd_hdl_debug(hdl, "xpathObj query %s\n", query);
651 
652 	xpathObj = xmlXPathEvalExpression((const xmlChar *)query, fab_xpathCtx);
653 
654 	if (xpathObj == NULL)
655 		goto fail;
656 
657 	fmd_hdl_debug(hdl, "xpathObj 0x%p type %d\n", xpathObj, xpathObj->type);
658 
659 	nodes = xpathObj->nodesetval;
660 	size = (nodes) ? nodes->nodeNr : 0;
661 
662 	/* Decode the list of assigned addresses xml nodes for each device */
663 	for (i = 0; i < size; i++) {
664 		char *tprop;
665 
666 		devNode = nodes->nodeTab[i];
667 		if (!HAS_PROP(devNode, "value"))
668 			continue;
669 
670 		/* Convert "string" assigned-addresses to pci_regspec_t */
671 		j = 0;
672 		tprop = GET_PROP(devNode, "value");
673 		for (token = strtok(tprop, " "); token;
674 		    token = strtok(NULL, " ")) {
675 			prop[j++] = strtoul(token, (char **)NULL, 16);
676 		}
677 		prop[j] = (uint32_t)-1;
678 		FREE_PROP(tprop);
679 
680 		/* Check if address belongs to this device */
681 		for (assign_p = (pci_regspec_t *)prop;
682 		    assign_p->pci_phys_hi != (uint_t)-1; assign_p++) {
683 			low = assign_p->pci_phys_low;
684 			hi = low + assign_p->pci_size_low;
685 			if ((addr < hi) && (addr >= low)) {
686 				fmd_hdl_debug(hdl, "Found Address\n");
687 				goto found;
688 			}
689 		}
690 	}
691 	goto fail;
692 
693 found:
694 	/* Traverse up the xml tree and back down to find the right propgroup */
695 	for (devNode = devNode->parent->parent->children;
696 	    devNode; devNode = devNode->next) {
697 		char	*tprop;
698 
699 		tprop = GET_PROP(devNode, "name");
700 		if (STRCMP(devNode->name, "propgroup") &&
701 		    STRCMP(tprop, "io")) {
702 			FREE_PROP(tprop);
703 			goto propgroup;
704 		}
705 		FREE_PROP(tprop);
706 	}
707 	goto fail;
708 
709 propgroup:
710 	/* Retrive the "dev" propval and return */
711 	for (devNode = devNode->children; devNode; devNode = devNode->next) {
712 		char	*tprop;
713 
714 		tprop = GET_PROP(devNode, "name");
715 		if (STRCMP(devNode->name, "propval") &&
716 		    STRCMP(tprop, "dev")) {
717 			FREE_PROP(tprop);
718 			temp = GET_PROP(devNode, "value");
719 			retval = fmd_hdl_strdup(hdl, temp, FMD_SLEEP);
720 			fmd_hdl_debug(hdl, "Addr Dev Path: %s\n", retval);
721 			xmlFree(temp);
722 			xmlXPathFreeObject(xpathObj);
723 			return (retval);
724 		}
725 		FREE_PROP(tprop);
726 	}
727 fail:
728 	if (xpathObj != NULL)
729 		xmlXPathFreeObject(xpathObj);
730 	return (NULL);
731 }
732 
733 void
734 fab_pr(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl)
735 {
736 	nvpair_t *nvp;
737 
738 	for (nvp = nvlist_next_nvpair(nvl, NULL);
739 	    nvp != NULL;
740 	    nvp = nvlist_next_nvpair(nvl, nvp)) {
741 
742 		data_type_t type = nvpair_type(nvp);
743 		const char *name = nvpair_name(nvp);
744 
745 		boolean_t b;
746 		uint8_t i8;
747 		uint16_t i16;
748 		uint32_t i32;
749 		uint64_t i64;
750 		char *str;
751 		nvlist_t *cnv;
752 
753 		nvlist_t **nvlarr;
754 		uint_t arrsize;
755 		int arri;
756 
757 
758 		if (STRCMP(name, FM_CLASS))
759 			continue; /* already printed by caller */
760 
761 		fmd_hdl_debug(hdl, " %s=", name);
762 
763 		switch (type) {
764 		case DATA_TYPE_BOOLEAN:
765 			fmd_hdl_debug(hdl, "DATA_TYPE_BOOLEAN 1");
766 			break;
767 
768 		case DATA_TYPE_BOOLEAN_VALUE:
769 			(void) nvpair_value_boolean_value(nvp, &b);
770 			fmd_hdl_debug(hdl, "DATA_TYPE_BOOLEAN_VALUE %d",
771 			    b ? "1" : "0");
772 			break;
773 
774 		case DATA_TYPE_BYTE:
775 			(void) nvpair_value_byte(nvp, &i8);
776 			fmd_hdl_debug(hdl, "DATA_TYPE_BYTE 0x%x", i8);
777 			break;
778 
779 		case DATA_TYPE_INT8:
780 			(void) nvpair_value_int8(nvp, (void *)&i8);
781 			fmd_hdl_debug(hdl, "DATA_TYPE_INT8 0x%x", i8);
782 			break;
783 
784 		case DATA_TYPE_UINT8:
785 			(void) nvpair_value_uint8(nvp, &i8);
786 			fmd_hdl_debug(hdl, "DATA_TYPE_UINT8 0x%x", i8);
787 			break;
788 
789 		case DATA_TYPE_INT16:
790 			(void) nvpair_value_int16(nvp, (void *)&i16);
791 			fmd_hdl_debug(hdl, "DATA_TYPE_INT16 0x%x", i16);
792 			break;
793 
794 		case DATA_TYPE_UINT16:
795 			(void) nvpair_value_uint16(nvp, &i16);
796 			fmd_hdl_debug(hdl, "DATA_TYPE_UINT16 0x%x", i16);
797 			break;
798 
799 		case DATA_TYPE_INT32:
800 			(void) nvpair_value_int32(nvp, (void *)&i32);
801 			fmd_hdl_debug(hdl, "DATA_TYPE_INT32 0x%x", i32);
802 			break;
803 
804 		case DATA_TYPE_UINT32:
805 			(void) nvpair_value_uint32(nvp, &i32);
806 			fmd_hdl_debug(hdl, "DATA_TYPE_UINT32 0x%x", i32);
807 			break;
808 
809 		case DATA_TYPE_INT64:
810 			(void) nvpair_value_int64(nvp, (void *)&i64);
811 			fmd_hdl_debug(hdl, "DATA_TYPE_INT64 0x%llx",
812 			    (u_longlong_t)i64);
813 			break;
814 
815 		case DATA_TYPE_UINT64:
816 			(void) nvpair_value_uint64(nvp, &i64);
817 			fmd_hdl_debug(hdl, "DATA_TYPE_UINT64 0x%llx",
818 			    (u_longlong_t)i64);
819 			break;
820 
821 		case DATA_TYPE_HRTIME:
822 			(void) nvpair_value_hrtime(nvp, (void *)&i64);
823 			fmd_hdl_debug(hdl, "DATA_TYPE_HRTIME 0x%llx",
824 			    (u_longlong_t)i64);
825 			break;
826 
827 		case DATA_TYPE_STRING:
828 			(void) nvpair_value_string(nvp, &str);
829 			fmd_hdl_debug(hdl, "DATA_TYPE_STRING \"%s\"",
830 			    str ? str : "<NULL>");
831 			break;
832 
833 		case DATA_TYPE_NVLIST:
834 			fmd_hdl_debug(hdl, "[");
835 			(void) nvpair_value_nvlist(nvp, &cnv);
836 			fab_pr(hdl, NULL, cnv);
837 			fmd_hdl_debug(hdl, " ]");
838 			break;
839 
840 		case DATA_TYPE_BOOLEAN_ARRAY:
841 		case DATA_TYPE_BYTE_ARRAY:
842 		case DATA_TYPE_INT8_ARRAY:
843 		case DATA_TYPE_UINT8_ARRAY:
844 		case DATA_TYPE_INT16_ARRAY:
845 		case DATA_TYPE_UINT16_ARRAY:
846 		case DATA_TYPE_INT32_ARRAY:
847 		case DATA_TYPE_UINT32_ARRAY:
848 		case DATA_TYPE_INT64_ARRAY:
849 		case DATA_TYPE_UINT64_ARRAY:
850 		case DATA_TYPE_STRING_ARRAY:
851 			fmd_hdl_debug(hdl, "[...]");
852 			break;
853 		case DATA_TYPE_NVLIST_ARRAY:
854 			arrsize = 0;
855 			(void) nvpair_value_nvlist_array(nvp, &nvlarr,
856 			    &arrsize);
857 
858 			for (arri = 0; arri < arrsize; arri++) {
859 				fab_pr(hdl, ep, nvlarr[arri]);
860 			}
861 
862 			break;
863 		case DATA_TYPE_UNKNOWN:
864 			fmd_hdl_debug(hdl, "<unknown>");
865 			break;
866 		}
867 	}
868 }
869 
870 char *
871 fab_get_rpdev(fmd_hdl_t *hdl)
872 {
873 	char	*retval;
874 	char	query[500];
875 
876 	(void) snprintf(query, sizeof (query), "//propval["
877 	    "@name='extended-capabilities' and contains(@value, '%s')]"
878 	    "/parent::*/parent::*/propgroup[@name='io']"
879 	    "/propval[@name='dev']/@value", PCIEX_ROOT);
880 
881 	retval = fab_xpath_query(hdl, query);
882 	if (retval) {
883 		fmd_hdl_debug(hdl, "Root port path is %s\n", retval);
884 		return (retval);
885 	}
886 
887 	return (NULL);
888 }
889 
890 void
891 fab_send_erpt_all_rps(fmd_hdl_t *hdl, nvlist_t *erpt)
892 {
893 	xmlXPathObjectPtr xpathObj;
894 	xmlNodeSetPtr nodes;
895 	char	*rppath, *hbpath;
896 	char	query[600];
897 	nvlist_t *detector, *nvl;
898 	uint_t	i, size;
899 	size_t len;
900 
901 	/* get hostbridge's path */
902 	if (!fab_get_hcpath(hdl, erpt, &hbpath, &len)) {
903 		fmd_hdl_debug(hdl,
904 		    "fab_send_erpt_on_all_rps: fab_get_hcpath() failed.\n");
905 		return;
906 	}
907 
908 	(void) snprintf(query, sizeof (query), "//propval["
909 	    "@name='extended-capabilities' and contains(@value, '%s')]"
910 	    "/parent::*/parent::*/propgroup[@name='protocol']"
911 	    "/propval[@name='resource' and contains(@value, '%s/')"
912 	    "]/parent::*/parent::*/propgroup[@name='io']"
913 	    "/propval[@name='dev']/@value", PCIEX_ROOT, hbpath);
914 
915 	fmd_hdl_free(hdl, hbpath, len);
916 
917 	fmd_hdl_debug(hdl, "xpathObj query %s\n", query);
918 
919 	xpathObj = xmlXPathEvalExpression((const xmlChar *)query, fab_xpathCtx);
920 
921 	if (xpathObj == NULL)
922 		return;
923 
924 	nodes = xpathObj->nodesetval;
925 	size = (nodes) ? nodes->nodeNr : 0;
926 
927 	fmd_hdl_debug(hdl, "xpathObj 0x%p type %d size %d\n",
928 	    xpathObj, xpathObj->type, size);
929 
930 	for (i = 0; i < size; i++) {
931 		rppath = (char *)xmlNodeGetContent(nodes->nodeTab[i]);
932 		fmd_hdl_debug(hdl, "query result: %s\n", rppath);
933 
934 		nvl = detector = NULL;
935 		if (nvlist_dup(erpt, &nvl, NV_UNIQUE_NAME) != 0 ||
936 		    nvlist_alloc(&detector, NV_UNIQUE_NAME, 0) != 0) {
937 			xmlFree(rppath);
938 			nvlist_free(nvl);
939 			continue;
940 		}
941 
942 		/*
943 		 * set the detector in the original ereport to the root port
944 		 */
945 		(void) nvlist_add_string(detector, FM_VERSION,
946 		    FM_DEV_SCHEME_VERSION);
947 		(void) nvlist_add_string(detector, FM_FMRI_SCHEME,
948 		    FM_FMRI_SCHEME_DEV);
949 		(void) nvlist_add_string(detector, FM_FMRI_DEV_PATH,
950 		    rppath);
951 		(void) nvlist_remove_all(nvl, FM_EREPORT_DETECTOR);
952 		(void) nvlist_add_nvlist(nvl, FM_EREPORT_DETECTOR,
953 		    detector);
954 		nvlist_free(detector);
955 		xmlFree(rppath);
956 
957 		fmd_hdl_debug(hdl, "Sending ereport: %s\n", fab_buf);
958 		fmd_xprt_post(hdl, fab_fmd_xprt, nvl, 0);
959 		if (fmd_xprt_error(hdl, fab_fmd_xprt))
960 			fmd_hdl_debug(hdl,
961 			    "Failed to send PCI ereport\n");
962 	}
963 
964 	xmlXPathFreeObject(xpathObj);
965 }
966