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