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 #pragma ident   "Copyright 2015 QLogic Corporation; qlc_fm.c"
  23 
  24 /*
  25  * ***********************************************************************
  26  * *                                                                    **
  27  * *                            NOTICE                                  **
  28  * *            COPYRIGHT (C) 1996-2015 QLOGIC CORPORATION              **
  29  * *                    ALL RIGHTS RESERVED                             **
  30  * *                                                                    **
  31  * ***********************************************************************
  32  *
  33  */
  34 
  35 #include <ql_apps.h>
  36 #include <ql_api.h>
  37 #include <ql_fm.h>
  38 
  39 /* Define default impact code */
  40 qlc_fm_ereport_t qlc_fm_ereport_tbl[] = {
  41 
  42         {QL_FM_EREPORT_DMA_ERR,
  43         "A DMA direction error",
  44         QL_FM_DEVICE_DMA_ERR,
  45         DDI_FM_DEVICE_INTERN_CORR,
  46         DDI_SERVICE_UNAFFECTED},
  47 
  48         {QL_FM_EREPORT_BAD_PAYLOAD,
  49         "A bad payload detected",
  50         QL_FM_DEVICE_BAD_PAYLOAD,
  51         DDI_FM_DEVICE_INTERN_CORR,
  52         DDI_SERVICE_UNAFFECTED},
  53 
  54         {QL_FM_EREPORT_CMD_FAILED,
  55         "A command failed",
  56         QL_FM_DEVICE_CMD_FAILED,
  57         DDI_FM_DEVICE_INTERN_CORR,
  58         DDI_SERVICE_UNAFFECTED},
  59 
  60         {QL_FM_EREPORT_CHIP_HANG,
  61         "fw is not responding",
  62         QL_FM_DEVICE_CHIP_HANG,
  63         DDI_FM_DEVICE_INTERN_CORR,
  64         DDI_SERVICE_UNAFFECTED},
  65 
  66         {QL_FM_EREPORT_UNKNOWN,
  67         "Unknown error reported",
  68         QL_FM_DEVICE_UNKNOWN,
  69         DDI_FM_DEVICE_INTERN_CORR,
  70         DDI_SERVICE_UNAFFECTED},
  71 
  72         {QL_FM_EREPORT_MBA_REQ_TRANSFER_ERR,
  73         "Async event request transfer error",
  74         QL_FM_DEVICE_MBA_REQ_TRANSFER_ERR,
  75         DDI_FM_DEVICE_INVAL_STATE,
  76         DDI_SERVICE_LOST},
  77 
  78         {QL_FM_EREPORT_MBA_RSP_TRANSFER_ERR,
  79         "Async event response transfer error",
  80         QL_FM_DEVICE_MBA_RSP_TRANSFER_ERR,
  81         DDI_FM_DEVICE_INVAL_STATE,
  82         DDI_SERVICE_LOST},
  83 
  84         {QL_FM_EREPORT_ACC_HANDLE_CHECK,
  85         "ACC handle check return failed",
  86         QL_FM_DEVICE_ACC_HANDLE_ERR,
  87         DDI_FM_DEVICE_INTERN_UNCORR,
  88         DDI_SERVICE_LOST},
  89 
  90         {QL_FM_EREPORT_DMA_HANDLE_CHECK,
  91         "DMA handle check return failed",
  92         QL_FM_DEVICE_DMA_HANDLE_ERR,
  93         DDI_FM_DEVICE_INTERN_CORR,
  94         DDI_SERVICE_UNAFFECTED},
  95 
  96         /* Reporting Standard I/O controller Errors */
  97 
  98         NULL,   /* End of table */
  99 };
 100 
 101 
 102 int
 103 qlc_fm_check_acc_handle(ql_adapter_state_t *ha, ddi_acc_handle_t handle)
 104 {
 105 
 106         ddi_fm_error_t err;
 107 
 108         if (!DDI_FM_ACC_ERR_CAP(ha->fm_capabilities)) {
 109                 return (DDI_FM_OK);
 110         }
 111         err.fme_status = DDI_FM_OK;
 112 
 113         ddi_fm_acc_err_get(handle, &err, DDI_FME_VERSION);
 114 
 115         if ((void *)&ddi_fm_acc_err_clear != NULL)
 116                 (void) ddi_fm_acc_err_clear(handle, DDI_FME_VERSION);
 117 
 118         return (err.fme_status);
 119 }
 120 
 121 /*ARGSUSED*/
 122 int
 123 qlc_fm_check_dma_handle(ql_adapter_state_t *ha, ddi_dma_handle_t handle)
 124 {
 125         ddi_fm_error_t err;
 126 
 127         if (!DDI_FM_DMA_ERR_CAP(ha->fm_capabilities)) {
 128                 return (DDI_FM_OK);
 129         }
 130 
 131         err.fme_status = DDI_FM_OK;
 132 
 133         ddi_fm_dma_err_get(handle, &err, DDI_FME_VERSION);
 134 
 135         return (err.fme_status);
 136 
 137 }
 138 
 139 
 140 void
 141 qlc_fm_check_pkt_dma_handle(ql_adapter_state_t *ha, ql_srb_t *sb)
 142 {
 143         fc_packet_t     *pkt = sb->pkt;
 144         int             rval = DDI_FM_OK;
 145 
 146 
 147         if (!DDI_FM_DMA_ERR_CAP(ha->fm_capabilities)) {
 148                 return;
 149         }
 150 
 151         if (pkt->pkt_cmd_acc != NULL && pkt->pkt_cmdlen) {
 152                 rval = qlc_fm_check_dma_handle(ha, pkt->pkt_cmd_dma);
 153         }
 154 
 155         if (pkt->pkt_resp_acc != NULL && rval == DDI_FM_OK &&
 156             pkt->pkt_rsplen != 0) {
 157                 rval = qlc_fm_check_dma_handle(ha, pkt->pkt_resp_dma);
 158         }
 159 
 160         if (((pkt->pkt_data_acc != NULL) & (rval == DDI_FM_OK) &
 161             (pkt->pkt_datalen != 0)) != 0) {
 162                 rval = qlc_fm_check_dma_handle(ha, pkt->pkt_data_dma);
 163         }
 164 
 165         if (rval != DDI_FM_OK) {
 166                 pkt->pkt_state = FC_PKT_TRAN_ERROR;
 167                 pkt->pkt_reason = FC_REASON_DMA_ERROR;
 168                 pkt->pkt_expln = FC_EXPLN_NONE;
 169                 pkt->pkt_action = FC_ACTION_RETRYABLE;
 170 
 171                 (void) qlc_fm_report_err_impact(ha,
 172                     QL_FM_EREPORT_DMA_HANDLE_CHECK);
 173         }
 174 
 175 }
 176 
 177 /*
 178  * The IO fault service error handling callback function
 179  */
 180 
 181 /*ARGSUSED*/
 182 int
 183 qlc_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *impl_data)
 184 {
 185         pci_ereport_post(dip, err, NULL);
 186 
 187         return (err->fme_status);
 188 
 189 }
 190 
 191 /*ARGSUSED*/
 192 void
 193 qlc_fm_service_impact(ql_adapter_state_t *ha, int impact)
 194 {
 195         if (!DDI_FM_EREPORT_CAP(ha->fm_capabilities)) {
 196                 return;
 197         }
 198 
 199         ddi_fm_service_impact(ha->dip, impact);
 200 }
 201 
 202 
 203 /*ARGSUSED*/
 204 void
 205 qlc_fm_init(ql_adapter_state_t *ha)
 206 {
 207         ddi_iblock_cookie_t iblk;
 208 
 209         if (ha->fm_capabilities == DDI_FM_NOT_CAPABLE) {
 210                 return;
 211         }
 212 
 213         /*
 214          * Register capabilities with IO Fault Services.
 215          */
 216         if (ha->fm_capabilities) {
 217                 ddi_fm_init(ha->dip, (int *)&ha->fm_capabilities, &iblk);
 218         }
 219 
 220         /*
 221          * Initialize pci ereport capabilities if ereport capable
 222          * PCI-related errors are automatically detected and reported
 223          */
 224         if (DDI_FM_EREPORT_CAP(ha->fm_capabilities) ||
 225             DDI_FM_ERRCB_CAP(ha->fm_capabilities)) {
 226                 pci_ereport_setup(ha->dip);
 227         }
 228 
 229         /*
 230          * Register error callback if error callback capable.
 231          */
 232         if (DDI_FM_ERRCB_CAP(ha->fm_capabilities)) {
 233                 ddi_fm_handler_register(ha->dip,
 234                     qlc_fm_error_cb, (void*)ha);
 235         }
 236 
 237         /*
 238          * DDI_FLAGERR_ACC indicates:
 239          * 1. Driver will check its access handle(s) for faults on
 240          *    a regular basis by calling ddi_fm_acc_err_get
 241          * 2. Driver is able to cope with incorrect results of I/O
 242          *    operations resulted from an I/O fault.
 243          */
 244         if (DDI_FM_ACC_ERR_CAP(ha->fm_capabilities)) {
 245                 ql_dev_acc_attr.devacc_attr_access = DDI_FLAGERR_ACC;
 246         } else {
 247                 ql_dev_acc_attr.devacc_attr_access = DDI_DEFAULT_ACC;
 248         }
 249 
 250         /*
 251          * per instance based setup only
 252          */
 253         if (DDI_FM_DMA_ERR_CAP(ha->fm_capabilities)) {
 254                 ha->bit32_io_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
 255                 ha->bit64_io_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
 256 
 257         } else {
 258                 ha->bit32_io_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR;
 259                 ha->bit64_io_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR;
 260         }
 261 
 262 }
 263 
 264 
 265 void
 266 qlc_fm_fini(ql_adapter_state_t *ha)
 267 {
 268         if (ha->fm_capabilities) {
 269                 /*
 270                  * Release any resources allocated by pci_ereport_setup()
 271                  */
 272                 if (DDI_FM_EREPORT_CAP(ha->fm_capabilities) ||
 273                     DDI_FM_ERRCB_CAP(ha->fm_capabilities)) {
 274                         pci_ereport_teardown(ha->dip);
 275                 }
 276 
 277                 if (DDI_FM_ERRCB_CAP(ha->fm_capabilities)) {
 278                         ddi_fm_handler_unregister(ha->dip);
 279                 }
 280 
 281                 /* Unregister from IO Fault Services */
 282                 ddi_fm_fini(ha->dip);
 283         }
 284 
 285 }
 286 
 287 
 288 void
 289 qlc_fm_report_err_impact(ql_adapter_state_t *ha, uint32_t fid)
 290 {
 291         uint64_t ena;
 292         char eclass[QL_FM_MAX_CLASS];
 293         qlc_fm_ereport_t *ereport = NULL;
 294 
 295         if (!DDI_FM_EREPORT_CAP(ha->fm_capabilities)) {
 296                 return;
 297         }
 298 
 299         if (fid > QL_FM_EREPORT_NONE) {
 300                 cmn_err(CE_NOTE, "Not reported yet");
 301                 return;
 302         }
 303 
 304         ereport = &qlc_fm_ereport_tbl[fid];
 305 
 306         /* We already have everything we need in ereport */
 307         (void) snprintf(eclass, QL_FM_MAX_CLASS, "%s.%s",
 308             DDI_FM_DEVICE,
 309             ereport->gen_eclass);
 310 
 311         ena = fm_ena_generate(0, FM_ENA_FMT1);
 312 
 313         switch (ereport->fid) {
 314         case QL_FM_EREPORT_DMA_ERR:
 315         case QL_FM_EREPORT_BAD_PAYLOAD:
 316         case QL_FM_EREPORT_CMD_FAILED:
 317         case QL_FM_EREPORT_CHIP_HANG:
 318         case QL_FM_EREPORT_UNKNOWN:
 319         case QL_FM_EREPORT_MBA_REQ_TRANSFER_ERR:
 320         case QL_FM_EREPORT_MBA_RSP_TRANSFER_ERR:
 321 
 322                 ddi_fm_ereport_post(ha->dip, eclass, ena,
 323                     DDI_NOSLEEP,
 324                     FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
 325                     "Detailed error desc", DATA_TYPE_STRING, ereport->desc,
 326                     "Instance number", DATA_TYPE_UINT8, ha->instance,
 327                     NULL);
 328 
 329                 break;
 330 
 331         case QL_FM_EREPORT_ACC_HANDLE_CHECK:
 332         case QL_FM_EREPORT_DMA_HANDLE_CHECK:
 333         /*
 334          * Adjust the impact code based on the state
 335          * of the device: For example, if check failed
 336          * during attach, then impact is DDI_SERVICE_LOST.
 337          *
 338          * driver's callback qlc_fm_error_cb() registerd will report error.
 339          * We only need to report service impact here.
 340          */
 341                 ddi_fm_ereport_post(ha->dip, eclass, ena,
 342                     DDI_NOSLEEP,
 343                     FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
 344                     "Detailed error desc", DATA_TYPE_STRING, ereport->desc,
 345                     "Instance number", DATA_TYPE_UINT8, ha->instance,
 346                     NULL);
 347 
 348                 break;
 349 
 350         default:
 351                 ddi_fm_ereport_post(ha->dip, eclass, ena,
 352                     DDI_NOSLEEP,
 353                     FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, NULL);
 354 
 355                 break;
 356         }
 357 
 358         qlc_fm_service_impact(ha, ereport->impact_code);
 359 }