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 
  39 extern xmlXPathContextPtr fab_xpathCtx;
  40 
  41 /* ARGSUSED */
  42 int
  43 fab_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 
  89 void
  90 fab_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;
 176 done:
 177         if (fmri)
 178                 xmlFree(fmri);
 179         fmd_hdl_debug(hdl, "Failed to send Target PCI ereport\n");
 180 }
 181 
 182 void
 183 fab_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;
 225 done:
 226         fmd_hdl_debug(hdl, "Failed  to send PCI ereport\n");
 227 }
 228 
 229 char *
 230 fab_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  */
 267 boolean_t
 268 fab_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 
 286 static boolean_t
 287 fab_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 
 321 boolean_t
 322 fab_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 
 337 boolean_t
 338 fab_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 
 351 char *
 352 fab_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 
 391 char *
 392 fab_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 
 468 found:
 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 
 483 propgroup:
 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         }
 501 fail:
 502         if (xpathObj != NULL)
 503                 xmlXPathFreeObject(xpathObj);
 504         return (NULL);
 505 }
 506 
 507 char *
 508 fab_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 */
 530 boolean_t
 531 fab_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);
 573 fail:
 574         return (B_FALSE);
 575 }
 576 
 577 char *
 578 fab_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         }
 619 fail:
 620         return (NULL);
 621 }
 622 
 623 char *
 624 fab_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 
 689 found:
 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 
 705 propgroup:
 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         }
 723 fail:
 724         if (xpathObj != NULL)
 725                 xmlXPathFreeObject(xpathObj);
 726         return (NULL);
 727 }
 728 
 729 void
 730 fab_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 
 866 char *
 867 fab_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 
 886 void
 887 fab_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 }