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