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