1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate #include <sys/types.h> 30*0Sstevel@tonic-gate #include <sys/sunddi.h> 31*0Sstevel@tonic-gate #include <sys/sunndi.h> 32*0Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 33*0Sstevel@tonic-gate #include <sys/async.h> 34*0Sstevel@tonic-gate #include <sys/membar.h> 35*0Sstevel@tonic-gate #include <sys/spl.h> 36*0Sstevel@tonic-gate #include <sys/iommu.h> 37*0Sstevel@tonic-gate #include <sys/pci/pci_obj.h> 38*0Sstevel@tonic-gate #include <sys/fm/util.h> 39*0Sstevel@tonic-gate #include <sys/fm/io/pci.h> 40*0Sstevel@tonic-gate #include <sys/fm/io/ddi.h> 41*0Sstevel@tonic-gate #include <sys/fm/io/sun4upci.h> 42*0Sstevel@tonic-gate #include <sys/fm/protocol.h> 43*0Sstevel@tonic-gate #include <sys/intr.h> 44*0Sstevel@tonic-gate 45*0Sstevel@tonic-gate /*LINTLIBRARY*/ 46*0Sstevel@tonic-gate 47*0Sstevel@tonic-gate /* 48*0Sstevel@tonic-gate * The routines below are generic sun4u PCI interfaces to support 49*0Sstevel@tonic-gate * Fault Management. 50*0Sstevel@tonic-gate * 51*0Sstevel@tonic-gate * pci_dma_check, pci_acc_check, pci_handle_lookup are functions used 52*0Sstevel@tonic-gate * to associate a captured PCI address to a particular dma/acc handle. 53*0Sstevel@tonic-gate * 54*0Sstevel@tonic-gate * pci_fm_acc_setup, pci_fm_init_child, pci_fm_create, 55*0Sstevel@tonic-gate * pci_fm_destroy are constructors/destructors used to setup and teardown 56*0Sstevel@tonic-gate * necessary resources. 57*0Sstevel@tonic-gate * 58*0Sstevel@tonic-gate * pci_bus_enter, pci_bus_exit are registered via busops and are used to 59*0Sstevel@tonic-gate * provide exclusive access to the PCI bus. 60*0Sstevel@tonic-gate * 61*0Sstevel@tonic-gate * pci_err_callback is the registered callback for PCI which is called 62*0Sstevel@tonic-gate * by the CPU code when it detects a UE/TO/BERR. 63*0Sstevel@tonic-gate * 64*0Sstevel@tonic-gate * pbm_ereport_post is used by the PBM code to generically report all 65*0Sstevel@tonic-gate * PBM errors. 66*0Sstevel@tonic-gate * 67*0Sstevel@tonic-gate */ 68*0Sstevel@tonic-gate 69*0Sstevel@tonic-gate /* 70*0Sstevel@tonic-gate * Function called after a dma fault occurred to find out whether the 71*0Sstevel@tonic-gate * fault address is associated with a driver that is able to handle faults 72*0Sstevel@tonic-gate * and recover from faults. 73*0Sstevel@tonic-gate */ 74*0Sstevel@tonic-gate /* ARGSUSED */ 75*0Sstevel@tonic-gate static int 76*0Sstevel@tonic-gate pci_dma_check(dev_info_t *dip, const void *handle, const void *comp_addr, 77*0Sstevel@tonic-gate const void *not_used) 78*0Sstevel@tonic-gate { 79*0Sstevel@tonic-gate ddi_dma_impl_t *mp = (ddi_dma_impl_t *)handle; 80*0Sstevel@tonic-gate pfn_t fault_pfn = mmu_btop(*(uint64_t *)comp_addr); 81*0Sstevel@tonic-gate pfn_t comp_pfn; 82*0Sstevel@tonic-gate int page; 83*0Sstevel@tonic-gate 84*0Sstevel@tonic-gate /* 85*0Sstevel@tonic-gate * The driver has to set DDI_DMA_FLAGERR to recover from dma faults. 86*0Sstevel@tonic-gate */ 87*0Sstevel@tonic-gate ASSERT(mp); 88*0Sstevel@tonic-gate 89*0Sstevel@tonic-gate for (page = 0; page < mp->dmai_ndvmapages; page++) { 90*0Sstevel@tonic-gate comp_pfn = PCI_GET_MP_PFN(mp, page); 91*0Sstevel@tonic-gate if (fault_pfn == comp_pfn) 92*0Sstevel@tonic-gate return (DDI_FM_NONFATAL); 93*0Sstevel@tonic-gate } 94*0Sstevel@tonic-gate 95*0Sstevel@tonic-gate return (DDI_FM_UNKNOWN); 96*0Sstevel@tonic-gate } 97*0Sstevel@tonic-gate 98*0Sstevel@tonic-gate /* 99*0Sstevel@tonic-gate * Function used to check if a given access handle owns the failing address. 100*0Sstevel@tonic-gate * Called by ndi_fmc_error, when we detect a PIO error. 101*0Sstevel@tonic-gate */ 102*0Sstevel@tonic-gate /* ARGSUSED */ 103*0Sstevel@tonic-gate static int 104*0Sstevel@tonic-gate pci_acc_check(dev_info_t *dip, const void *handle, const void *comp_addr, 105*0Sstevel@tonic-gate const void *not_used) 106*0Sstevel@tonic-gate { 107*0Sstevel@tonic-gate pfn_t pfn, fault_pfn; 108*0Sstevel@tonic-gate ddi_acc_hdl_t *hp; 109*0Sstevel@tonic-gate 110*0Sstevel@tonic-gate hp = impl_acc_hdl_get((ddi_acc_handle_t)handle); 111*0Sstevel@tonic-gate 112*0Sstevel@tonic-gate ASSERT(hp); 113*0Sstevel@tonic-gate 114*0Sstevel@tonic-gate pfn = hp->ah_pfn; 115*0Sstevel@tonic-gate fault_pfn = mmu_btop(*(uint64_t *)comp_addr); 116*0Sstevel@tonic-gate if (fault_pfn >= pfn && fault_pfn < (pfn + hp->ah_pnum)) 117*0Sstevel@tonic-gate return (DDI_FM_NONFATAL); 118*0Sstevel@tonic-gate 119*0Sstevel@tonic-gate return (DDI_FM_UNKNOWN); 120*0Sstevel@tonic-gate } 121*0Sstevel@tonic-gate 122*0Sstevel@tonic-gate /* 123*0Sstevel@tonic-gate * Function used by PCI error handlers to check if captured address is stored 124*0Sstevel@tonic-gate * in the DMA or ACC handle caches. 125*0Sstevel@tonic-gate */ 126*0Sstevel@tonic-gate int 127*0Sstevel@tonic-gate pci_handle_lookup(dev_info_t *dip, int type, uint64_t fme_ena, void *afar) 128*0Sstevel@tonic-gate { 129*0Sstevel@tonic-gate int status = DDI_FM_UNKNOWN; 130*0Sstevel@tonic-gate pci_t *pci_p = get_pci_soft_state(ddi_get_instance(dip)); 131*0Sstevel@tonic-gate 132*0Sstevel@tonic-gate if (type == DMA_HANDLE && DDI_FM_DMA_ERR_CAP(pci_p->pci_fm_cap)) 133*0Sstevel@tonic-gate status = ndi_fmc_error(dip, NULL, type, pci_dma_check, 134*0Sstevel@tonic-gate fme_ena, afar); 135*0Sstevel@tonic-gate else if (DDI_FM_ACC_ERR_CAP(pci_p->pci_fm_cap)) 136*0Sstevel@tonic-gate status = ndi_fmc_error(dip, NULL, type, pci_acc_check, 137*0Sstevel@tonic-gate fme_ena, afar); 138*0Sstevel@tonic-gate 139*0Sstevel@tonic-gate return (status); 140*0Sstevel@tonic-gate } 141*0Sstevel@tonic-gate 142*0Sstevel@tonic-gate /* 143*0Sstevel@tonic-gate * Function used to setup access functions depending on level of desired 144*0Sstevel@tonic-gate * protection. 145*0Sstevel@tonic-gate */ 146*0Sstevel@tonic-gate void 147*0Sstevel@tonic-gate pci_fm_acc_setup(ddi_map_req_t *mp, dev_info_t *rdip) 148*0Sstevel@tonic-gate { 149*0Sstevel@tonic-gate uchar_t fflag; 150*0Sstevel@tonic-gate ddi_acc_hdl_t *hp; 151*0Sstevel@tonic-gate ddi_acc_impl_t *ap; 152*0Sstevel@tonic-gate 153*0Sstevel@tonic-gate hp = mp->map_handlep; 154*0Sstevel@tonic-gate ap = (ddi_acc_impl_t *)hp->ah_platform_private; 155*0Sstevel@tonic-gate fflag = ap->ahi_common.ah_acc.devacc_attr_access; 156*0Sstevel@tonic-gate 157*0Sstevel@tonic-gate if (mp->map_op == DDI_MO_MAP_LOCKED) { 158*0Sstevel@tonic-gate ndi_fmc_insert(rdip, ACC_HANDLE, (void *)hp, NULL); 159*0Sstevel@tonic-gate switch (fflag) { 160*0Sstevel@tonic-gate case DDI_FLAGERR_ACC: 161*0Sstevel@tonic-gate ap->ahi_get8 = i_ddi_prot_get8; 162*0Sstevel@tonic-gate ap->ahi_get16 = i_ddi_prot_get16; 163*0Sstevel@tonic-gate ap->ahi_get32 = i_ddi_prot_get32; 164*0Sstevel@tonic-gate ap->ahi_get64 = i_ddi_prot_get64; 165*0Sstevel@tonic-gate ap->ahi_put8 = i_ddi_prot_put8; 166*0Sstevel@tonic-gate ap->ahi_put16 = i_ddi_prot_put16; 167*0Sstevel@tonic-gate ap->ahi_put32 = i_ddi_prot_put32; 168*0Sstevel@tonic-gate ap->ahi_put64 = i_ddi_prot_put64; 169*0Sstevel@tonic-gate ap->ahi_rep_get8 = i_ddi_prot_rep_get8; 170*0Sstevel@tonic-gate ap->ahi_rep_get16 = i_ddi_prot_rep_get16; 171*0Sstevel@tonic-gate ap->ahi_rep_get32 = i_ddi_prot_rep_get32; 172*0Sstevel@tonic-gate ap->ahi_rep_get64 = i_ddi_prot_rep_get64; 173*0Sstevel@tonic-gate ap->ahi_rep_put8 = i_ddi_prot_rep_put8; 174*0Sstevel@tonic-gate ap->ahi_rep_put16 = i_ddi_prot_rep_put16; 175*0Sstevel@tonic-gate ap->ahi_rep_put32 = i_ddi_prot_rep_put32; 176*0Sstevel@tonic-gate ap->ahi_rep_put64 = i_ddi_prot_rep_put64; 177*0Sstevel@tonic-gate break; 178*0Sstevel@tonic-gate case DDI_CAUTIOUS_ACC : 179*0Sstevel@tonic-gate ap->ahi_get8 = i_ddi_caut_get8; 180*0Sstevel@tonic-gate ap->ahi_get16 = i_ddi_caut_get16; 181*0Sstevel@tonic-gate ap->ahi_get32 = i_ddi_caut_get32; 182*0Sstevel@tonic-gate ap->ahi_get64 = i_ddi_caut_get64; 183*0Sstevel@tonic-gate ap->ahi_put8 = i_ddi_caut_put8; 184*0Sstevel@tonic-gate ap->ahi_put16 = i_ddi_caut_put16; 185*0Sstevel@tonic-gate ap->ahi_put32 = i_ddi_caut_put32; 186*0Sstevel@tonic-gate ap->ahi_put64 = i_ddi_caut_put64; 187*0Sstevel@tonic-gate ap->ahi_rep_get8 = i_ddi_caut_rep_get8; 188*0Sstevel@tonic-gate ap->ahi_rep_get16 = i_ddi_caut_rep_get16; 189*0Sstevel@tonic-gate ap->ahi_rep_get32 = i_ddi_caut_rep_get32; 190*0Sstevel@tonic-gate ap->ahi_rep_get64 = i_ddi_caut_rep_get64; 191*0Sstevel@tonic-gate ap->ahi_rep_put8 = i_ddi_caut_rep_put8; 192*0Sstevel@tonic-gate ap->ahi_rep_put16 = i_ddi_caut_rep_put16; 193*0Sstevel@tonic-gate ap->ahi_rep_put32 = i_ddi_caut_rep_put32; 194*0Sstevel@tonic-gate ap->ahi_rep_put64 = i_ddi_caut_rep_put64; 195*0Sstevel@tonic-gate break; 196*0Sstevel@tonic-gate default: 197*0Sstevel@tonic-gate break; 198*0Sstevel@tonic-gate } 199*0Sstevel@tonic-gate } else if (mp->map_op == DDI_MO_UNMAP) { 200*0Sstevel@tonic-gate ndi_fmc_remove(rdip, ACC_HANDLE, (void *)hp); 201*0Sstevel@tonic-gate } 202*0Sstevel@tonic-gate } 203*0Sstevel@tonic-gate 204*0Sstevel@tonic-gate /* 205*0Sstevel@tonic-gate * Function used to initialize FMA for our children nodes. Called 206*0Sstevel@tonic-gate * through pci busops when child node calls ddi_fm_init. 207*0Sstevel@tonic-gate */ 208*0Sstevel@tonic-gate /* ARGSUSED */ 209*0Sstevel@tonic-gate int 210*0Sstevel@tonic-gate pci_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap, 211*0Sstevel@tonic-gate ddi_iblock_cookie_t *ibc) 212*0Sstevel@tonic-gate { 213*0Sstevel@tonic-gate pci_t *pci_p = get_pci_soft_state(ddi_get_instance(dip)); 214*0Sstevel@tonic-gate 215*0Sstevel@tonic-gate ASSERT(ibc != NULL); 216*0Sstevel@tonic-gate *ibc = pci_p->pci_pbm_p->pbm_iblock_cookie; 217*0Sstevel@tonic-gate 218*0Sstevel@tonic-gate return (pci_p->pci_fm_cap); 219*0Sstevel@tonic-gate } 220*0Sstevel@tonic-gate 221*0Sstevel@tonic-gate /* 222*0Sstevel@tonic-gate * Lock accesses to the pci bus, to be able to protect against bus errors. 223*0Sstevel@tonic-gate */ 224*0Sstevel@tonic-gate void 225*0Sstevel@tonic-gate pci_bus_enter(dev_info_t *dip, ddi_acc_handle_t handle) 226*0Sstevel@tonic-gate { 227*0Sstevel@tonic-gate pci_t *pci_p = get_pci_soft_state(ddi_get_instance(dip)); 228*0Sstevel@tonic-gate pbm_t *pbm_p = pci_p->pci_pbm_p; 229*0Sstevel@tonic-gate 230*0Sstevel@tonic-gate membar_sync(); 231*0Sstevel@tonic-gate 232*0Sstevel@tonic-gate mutex_enter(&pbm_p->pbm_pokefault_mutex); 233*0Sstevel@tonic-gate pbm_p->pbm_excl_handle = handle; 234*0Sstevel@tonic-gate } 235*0Sstevel@tonic-gate 236*0Sstevel@tonic-gate /* 237*0Sstevel@tonic-gate * Unlock access to bus and clear errors before exiting. 238*0Sstevel@tonic-gate */ 239*0Sstevel@tonic-gate /* ARGSUSED */ 240*0Sstevel@tonic-gate void 241*0Sstevel@tonic-gate pci_bus_exit(dev_info_t *dip, ddi_acc_handle_t handle) 242*0Sstevel@tonic-gate { 243*0Sstevel@tonic-gate pci_t *pci_p = get_pci_soft_state(ddi_get_instance(dip)); 244*0Sstevel@tonic-gate pbm_t *pbm_p = pci_p->pci_pbm_p; 245*0Sstevel@tonic-gate ddi_fm_error_t derr; 246*0Sstevel@tonic-gate 247*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&pbm_p->pbm_pokefault_mutex)); 248*0Sstevel@tonic-gate 249*0Sstevel@tonic-gate membar_sync(); 250*0Sstevel@tonic-gate 251*0Sstevel@tonic-gate mutex_enter(&pci_p->pci_common_p->pci_fm_mutex); 252*0Sstevel@tonic-gate ddi_fm_acc_err_get(pbm_p->pbm_excl_handle, &derr, DDI_FME_VERSION); 253*0Sstevel@tonic-gate 254*0Sstevel@tonic-gate if (derr.fme_status == DDI_FM_OK) { 255*0Sstevel@tonic-gate if (pci_check_error(pci_p) != 0) { 256*0Sstevel@tonic-gate (void) pci_pbm_err_handler(pci_p->pci_dip, &derr, 257*0Sstevel@tonic-gate (const void *)pci_p, PCI_BUS_EXIT_CALL); 258*0Sstevel@tonic-gate } 259*0Sstevel@tonic-gate } 260*0Sstevel@tonic-gate mutex_exit(&pci_p->pci_common_p->pci_fm_mutex); 261*0Sstevel@tonic-gate 262*0Sstevel@tonic-gate pbm_p->pbm_excl_handle = NULL; 263*0Sstevel@tonic-gate mutex_exit(&pbm_p->pbm_pokefault_mutex); 264*0Sstevel@tonic-gate } 265*0Sstevel@tonic-gate 266*0Sstevel@tonic-gate /* 267*0Sstevel@tonic-gate * PCI error callback which is registered with our parent to call 268*0Sstevel@tonic-gate * for PCI logging when the CPU traps due to BERR/TO/UE. 269*0Sstevel@tonic-gate */ 270*0Sstevel@tonic-gate int 271*0Sstevel@tonic-gate pci_err_callback(dev_info_t *dip, ddi_fm_error_t *derr, 272*0Sstevel@tonic-gate const void *impl_data) 273*0Sstevel@tonic-gate { 274*0Sstevel@tonic-gate pci_t *pci_p = (pci_t *)impl_data; 275*0Sstevel@tonic-gate pci_common_t *cmn_p = pci_p->pci_common_p; 276*0Sstevel@tonic-gate ecc_t *ecc_p = cmn_p->pci_common_ecc_p; 277*0Sstevel@tonic-gate ecc_errstate_t ecc_err; 278*0Sstevel@tonic-gate int fatal = 0; 279*0Sstevel@tonic-gate int nonfatal = 0; 280*0Sstevel@tonic-gate int unknown = 0; 281*0Sstevel@tonic-gate int ret = DDI_FM_OK; 282*0Sstevel@tonic-gate 283*0Sstevel@tonic-gate bzero(&ecc_err, sizeof (ecc_err)); 284*0Sstevel@tonic-gate mutex_enter(&cmn_p->pci_fm_mutex); 285*0Sstevel@tonic-gate /* 286*0Sstevel@tonic-gate * Check and log ecc and pbm errors 287*0Sstevel@tonic-gate */ 288*0Sstevel@tonic-gate ecc_err.ecc_ii_p = ecc_p->ecc_ue; 289*0Sstevel@tonic-gate ecc_err.ecc_ena = derr->fme_ena; 290*0Sstevel@tonic-gate ecc_err.ecc_caller = PCI_TRAP_CALL; 291*0Sstevel@tonic-gate 292*0Sstevel@tonic-gate if ((ret = ecc_err_handler(&ecc_err)) == DDI_FM_FATAL) 293*0Sstevel@tonic-gate fatal++; 294*0Sstevel@tonic-gate else if (ret == DDI_FM_NONFATAL) 295*0Sstevel@tonic-gate nonfatal++; 296*0Sstevel@tonic-gate else if (ret == DDI_FM_UNKNOWN) 297*0Sstevel@tonic-gate unknown++; 298*0Sstevel@tonic-gate 299*0Sstevel@tonic-gate if (pci_check_error(pci_p) != 0) { 300*0Sstevel@tonic-gate int err = pci_pbm_err_handler(pci_p->pci_dip, derr, 301*0Sstevel@tonic-gate (const void *)pci_p, PCI_TRAP_CALL); 302*0Sstevel@tonic-gate if (err == DDI_FM_FATAL) 303*0Sstevel@tonic-gate fatal++; 304*0Sstevel@tonic-gate else if (err == DDI_FM_NONFATAL) 305*0Sstevel@tonic-gate nonfatal++; 306*0Sstevel@tonic-gate else if (err == DDI_FM_UNKNOWN) 307*0Sstevel@tonic-gate unknown++; 308*0Sstevel@tonic-gate } 309*0Sstevel@tonic-gate 310*0Sstevel@tonic-gate mutex_exit(&cmn_p->pci_fm_mutex); 311*0Sstevel@tonic-gate 312*0Sstevel@tonic-gate if (fatal) 313*0Sstevel@tonic-gate return (DDI_FM_FATAL); 314*0Sstevel@tonic-gate else if (nonfatal) 315*0Sstevel@tonic-gate return (DDI_FM_NONFATAL); 316*0Sstevel@tonic-gate else if (unknown) 317*0Sstevel@tonic-gate return (DDI_FM_UNKNOWN); 318*0Sstevel@tonic-gate else 319*0Sstevel@tonic-gate return (DDI_FM_OK); 320*0Sstevel@tonic-gate } 321*0Sstevel@tonic-gate 322*0Sstevel@tonic-gate /* 323*0Sstevel@tonic-gate * private version of walk_devs() that can be used during panic. No 324*0Sstevel@tonic-gate * sleeping or locking required. 325*0Sstevel@tonic-gate */ 326*0Sstevel@tonic-gate static int 327*0Sstevel@tonic-gate pci_tgt_walk_devs(dev_info_t *dip, int (*f)(dev_info_t *, void *), void *arg) 328*0Sstevel@tonic-gate { 329*0Sstevel@tonic-gate while (dip) { 330*0Sstevel@tonic-gate switch ((*f)(dip, arg)) { 331*0Sstevel@tonic-gate case DDI_WALK_TERMINATE: 332*0Sstevel@tonic-gate return (DDI_WALK_TERMINATE); 333*0Sstevel@tonic-gate case DDI_WALK_CONTINUE: 334*0Sstevel@tonic-gate if (pci_tgt_walk_devs(ddi_get_child(dip), f, 335*0Sstevel@tonic-gate arg) == DDI_WALK_TERMINATE) 336*0Sstevel@tonic-gate return (DDI_WALK_TERMINATE); 337*0Sstevel@tonic-gate break; 338*0Sstevel@tonic-gate case DDI_WALK_PRUNECHILD: 339*0Sstevel@tonic-gate break; 340*0Sstevel@tonic-gate } 341*0Sstevel@tonic-gate dip = ddi_get_next_sibling(dip); 342*0Sstevel@tonic-gate } 343*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 344*0Sstevel@tonic-gate } 345*0Sstevel@tonic-gate 346*0Sstevel@tonic-gate static int 347*0Sstevel@tonic-gate pci_check_regs(dev_info_t *dip, void *arg) 348*0Sstevel@tonic-gate { 349*0Sstevel@tonic-gate int reglen; 350*0Sstevel@tonic-gate int rn; 351*0Sstevel@tonic-gate int totreg; 352*0Sstevel@tonic-gate pci_regspec_t *drv_regp; 353*0Sstevel@tonic-gate pci_target_err_t *tgt_err = (pci_target_err_t *)arg; 354*0Sstevel@tonic-gate 355*0Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS, 356*0Sstevel@tonic-gate "assigned-addresses", (caddr_t)&drv_regp, ®len) != DDI_SUCCESS) 357*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 358*0Sstevel@tonic-gate 359*0Sstevel@tonic-gate totreg = reglen / sizeof (pci_regspec_t); 360*0Sstevel@tonic-gate for (rn = 0; rn < totreg; rn++) { 361*0Sstevel@tonic-gate if (tgt_err->tgt_pci_space == 362*0Sstevel@tonic-gate PCI_REG_ADDR_G(drv_regp[rn].pci_phys_hi) && 363*0Sstevel@tonic-gate (tgt_err->tgt_pci_addr >= 364*0Sstevel@tonic-gate (uint64_t)drv_regp[rn].pci_phys_low + 365*0Sstevel@tonic-gate ((uint64_t)drv_regp[rn].pci_phys_mid << 32)) && 366*0Sstevel@tonic-gate (tgt_err->tgt_pci_addr < 367*0Sstevel@tonic-gate (uint64_t)drv_regp[rn].pci_phys_low + 368*0Sstevel@tonic-gate ((uint64_t)drv_regp[rn].pci_phys_mid << 32) + 369*0Sstevel@tonic-gate (uint64_t)drv_regp[rn].pci_size_low + 370*0Sstevel@tonic-gate ((uint64_t)drv_regp[rn].pci_size_hi << 32))) { 371*0Sstevel@tonic-gate tgt_err->tgt_dip = dip; 372*0Sstevel@tonic-gate kmem_free(drv_regp, reglen); 373*0Sstevel@tonic-gate return (DDI_WALK_TERMINATE); 374*0Sstevel@tonic-gate } 375*0Sstevel@tonic-gate } 376*0Sstevel@tonic-gate kmem_free(drv_regp, reglen); 377*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 378*0Sstevel@tonic-gate } 379*0Sstevel@tonic-gate 380*0Sstevel@tonic-gate static int 381*0Sstevel@tonic-gate pci_check_ranges(dev_info_t *dip, void *arg) 382*0Sstevel@tonic-gate { 383*0Sstevel@tonic-gate uint64_t range_parent_begin; 384*0Sstevel@tonic-gate uint64_t range_parent_size; 385*0Sstevel@tonic-gate uint64_t range_parent_end; 386*0Sstevel@tonic-gate uint32_t space_type; 387*0Sstevel@tonic-gate uint32_t bus_num; 388*0Sstevel@tonic-gate uint32_t range_offset; 389*0Sstevel@tonic-gate pci_ranges_t *pci_ranges, *rangep; 390*0Sstevel@tonic-gate pci_bus_range_t *pci_bus_rangep; 391*0Sstevel@tonic-gate int pci_ranges_length; 392*0Sstevel@tonic-gate int nrange; 393*0Sstevel@tonic-gate pci_target_err_t *tgt_err = (pci_target_err_t *)arg; 394*0Sstevel@tonic-gate int i, size; 395*0Sstevel@tonic-gate 396*0Sstevel@tonic-gate if (strcmp(ddi_node_name(dip), "pci") != 0) 397*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 398*0Sstevel@tonic-gate 399*0Sstevel@tonic-gate /* 400*0Sstevel@tonic-gate * Get the ranges property. 401*0Sstevel@tonic-gate */ 402*0Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "ranges", 403*0Sstevel@tonic-gate (caddr_t)&pci_ranges, &pci_ranges_length) != DDI_SUCCESS) { 404*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 405*0Sstevel@tonic-gate } 406*0Sstevel@tonic-gate nrange = pci_ranges_length / sizeof (pci_ranges_t); 407*0Sstevel@tonic-gate rangep = pci_ranges; 408*0Sstevel@tonic-gate pci_fix_ranges(pci_ranges, nrange); 409*0Sstevel@tonic-gate 410*0Sstevel@tonic-gate for (i = 0; i < nrange; i++, rangep++) { 411*0Sstevel@tonic-gate range_parent_begin = ((uint64_t)rangep->parent_high << 32) + 412*0Sstevel@tonic-gate rangep->parent_low; 413*0Sstevel@tonic-gate range_parent_size = ((uint64_t)rangep->size_high << 32) + 414*0Sstevel@tonic-gate rangep->size_low; 415*0Sstevel@tonic-gate range_parent_end = range_parent_begin + range_parent_size - 1; 416*0Sstevel@tonic-gate 417*0Sstevel@tonic-gate if ((tgt_err->tgt_err_addr < range_parent_begin) || 418*0Sstevel@tonic-gate (tgt_err->tgt_err_addr > range_parent_end)) { 419*0Sstevel@tonic-gate /* Not in range */ 420*0Sstevel@tonic-gate continue; 421*0Sstevel@tonic-gate } 422*0Sstevel@tonic-gate space_type = PCI_REG_ADDR_G(rangep->child_high); 423*0Sstevel@tonic-gate if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) { 424*0Sstevel@tonic-gate /* Config space address - check bus range */ 425*0Sstevel@tonic-gate range_offset = tgt_err->tgt_err_addr - 426*0Sstevel@tonic-gate range_parent_begin; 427*0Sstevel@tonic-gate bus_num = PCI_REG_BUS_G(range_offset); 428*0Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, dip, 429*0Sstevel@tonic-gate DDI_PROP_DONTPASS, "bus-range", 430*0Sstevel@tonic-gate (caddr_t)&pci_bus_rangep, &size) != DDI_SUCCESS) { 431*0Sstevel@tonic-gate continue; 432*0Sstevel@tonic-gate } 433*0Sstevel@tonic-gate if ((bus_num < pci_bus_rangep->lo) || 434*0Sstevel@tonic-gate (bus_num > pci_bus_rangep->hi)) { 435*0Sstevel@tonic-gate /* 436*0Sstevel@tonic-gate * Bus number not appropriate for this 437*0Sstevel@tonic-gate * pci nexus. 438*0Sstevel@tonic-gate */ 439*0Sstevel@tonic-gate kmem_free(pci_bus_rangep, size); 440*0Sstevel@tonic-gate continue; 441*0Sstevel@tonic-gate } 442*0Sstevel@tonic-gate kmem_free(pci_bus_rangep, size); 443*0Sstevel@tonic-gate } 444*0Sstevel@tonic-gate 445*0Sstevel@tonic-gate /* We have a match if we get here - compute pci address */ 446*0Sstevel@tonic-gate tgt_err->tgt_pci_addr = tgt_err->tgt_err_addr - 447*0Sstevel@tonic-gate range_parent_begin; 448*0Sstevel@tonic-gate tgt_err->tgt_pci_addr += (((uint64_t)rangep->child_mid << 32) + 449*0Sstevel@tonic-gate rangep->child_low); 450*0Sstevel@tonic-gate tgt_err->tgt_pci_space = space_type; 451*0Sstevel@tonic-gate if (panicstr) 452*0Sstevel@tonic-gate pci_tgt_walk_devs(dip, pci_check_regs, (void *)tgt_err); 453*0Sstevel@tonic-gate else 454*0Sstevel@tonic-gate ddi_walk_devs(dip, pci_check_regs, (void *)tgt_err); 455*0Sstevel@tonic-gate if (tgt_err->tgt_dip != NULL) { 456*0Sstevel@tonic-gate kmem_free(pci_ranges, pci_ranges_length); 457*0Sstevel@tonic-gate return (DDI_WALK_TERMINATE); 458*0Sstevel@tonic-gate } 459*0Sstevel@tonic-gate } 460*0Sstevel@tonic-gate kmem_free(pci_ranges, pci_ranges_length); 461*0Sstevel@tonic-gate return (DDI_WALK_PRUNECHILD); 462*0Sstevel@tonic-gate } 463*0Sstevel@tonic-gate 464*0Sstevel@tonic-gate /* 465*0Sstevel@tonic-gate * need special version of ddi_fm_ereport_post() as the leaf driver may 466*0Sstevel@tonic-gate * not be hardened. 467*0Sstevel@tonic-gate */ 468*0Sstevel@tonic-gate void 469*0Sstevel@tonic-gate pci_tgt_ereport_post(dev_info_t *dip, const char *error_class, uint64_t ena, 470*0Sstevel@tonic-gate uint8_t version, ...) 471*0Sstevel@tonic-gate { 472*0Sstevel@tonic-gate char *name; 473*0Sstevel@tonic-gate char device_path[MAXPATHLEN]; 474*0Sstevel@tonic-gate char ddi_error_class[FM_MAX_CLASS]; 475*0Sstevel@tonic-gate nvlist_t *ereport, *detector; 476*0Sstevel@tonic-gate nv_alloc_t *nva; 477*0Sstevel@tonic-gate errorq_elem_t *eqep; 478*0Sstevel@tonic-gate va_list ap; 479*0Sstevel@tonic-gate 480*0Sstevel@tonic-gate if (panicstr) { 481*0Sstevel@tonic-gate eqep = errorq_reserve(ereport_errorq); 482*0Sstevel@tonic-gate if (eqep == NULL) 483*0Sstevel@tonic-gate return; 484*0Sstevel@tonic-gate ereport = errorq_elem_nvl(ereport_errorq, eqep); 485*0Sstevel@tonic-gate nva = errorq_elem_nva(ereport_errorq, eqep); 486*0Sstevel@tonic-gate detector = fm_nvlist_create(nva); 487*0Sstevel@tonic-gate } else { 488*0Sstevel@tonic-gate ereport = fm_nvlist_create(NULL); 489*0Sstevel@tonic-gate detector = fm_nvlist_create(NULL); 490*0Sstevel@tonic-gate } 491*0Sstevel@tonic-gate 492*0Sstevel@tonic-gate (void) ddi_pathname(dip, device_path); 493*0Sstevel@tonic-gate fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, 494*0Sstevel@tonic-gate device_path, NULL); 495*0Sstevel@tonic-gate (void) snprintf(ddi_error_class, FM_MAX_CLASS, "%s.%s", 496*0Sstevel@tonic-gate DDI_IO_CLASS, error_class); 497*0Sstevel@tonic-gate fm_ereport_set(ereport, version, ddi_error_class, ena, detector, NULL); 498*0Sstevel@tonic-gate 499*0Sstevel@tonic-gate va_start(ap, version); 500*0Sstevel@tonic-gate name = va_arg(ap, char *); 501*0Sstevel@tonic-gate (void) i_fm_payload_set(ereport, name, ap); 502*0Sstevel@tonic-gate va_end(ap); 503*0Sstevel@tonic-gate 504*0Sstevel@tonic-gate if (panicstr) { 505*0Sstevel@tonic-gate errorq_commit(ereport_errorq, eqep, ERRORQ_SYNC); 506*0Sstevel@tonic-gate } else { 507*0Sstevel@tonic-gate (void) fm_ereport_post(ereport, EVCH_TRYHARD); 508*0Sstevel@tonic-gate fm_nvlist_destroy(ereport, FM_NVA_FREE); 509*0Sstevel@tonic-gate fm_nvlist_destroy(detector, FM_NVA_FREE); 510*0Sstevel@tonic-gate } 511*0Sstevel@tonic-gate } 512*0Sstevel@tonic-gate 513*0Sstevel@tonic-gate /* 514*0Sstevel@tonic-gate * Function used to drain pci_target_queue, either during panic or after softint 515*0Sstevel@tonic-gate * is generated, to generate target device ereports based on captured physical 516*0Sstevel@tonic-gate * addresss 517*0Sstevel@tonic-gate */ 518*0Sstevel@tonic-gate static void 519*0Sstevel@tonic-gate pci_target_drain(void *private_p, pci_target_err_t *tgt_err) 520*0Sstevel@tonic-gate { 521*0Sstevel@tonic-gate char buf[FM_MAX_CLASS]; 522*0Sstevel@tonic-gate 523*0Sstevel@tonic-gate /* 524*0Sstevel@tonic-gate * The following assumes that all pci_pci bridge devices 525*0Sstevel@tonic-gate * are configured as transparant. Find the top-level pci 526*0Sstevel@tonic-gate * nexus which has tgt_err_addr in one of its ranges, converting this 527*0Sstevel@tonic-gate * to a pci address in the process. Then starting at this node do 528*0Sstevel@tonic-gate * another tree walk to find a device with the pci address we've 529*0Sstevel@tonic-gate * found within range of one of it's assigned-addresses properties. 530*0Sstevel@tonic-gate */ 531*0Sstevel@tonic-gate tgt_err->tgt_dip = NULL; 532*0Sstevel@tonic-gate if (panicstr) 533*0Sstevel@tonic-gate pci_tgt_walk_devs(ddi_root_node(), pci_check_ranges, 534*0Sstevel@tonic-gate (void *)tgt_err); 535*0Sstevel@tonic-gate else 536*0Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pci_check_ranges, 537*0Sstevel@tonic-gate (void *)tgt_err); 538*0Sstevel@tonic-gate if (tgt_err->tgt_dip == NULL) 539*0Sstevel@tonic-gate return; 540*0Sstevel@tonic-gate 541*0Sstevel@tonic-gate (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", tgt_err->tgt_bridge_type, 542*0Sstevel@tonic-gate tgt_err->tgt_err_class); 543*0Sstevel@tonic-gate pci_tgt_ereport_post(tgt_err->tgt_dip, buf, tgt_err->tgt_err_ena, 0, 544*0Sstevel@tonic-gate PCI_PA, DATA_TYPE_UINT64, tgt_err->tgt_err_addr, NULL); 545*0Sstevel@tonic-gate } 546*0Sstevel@tonic-gate 547*0Sstevel@tonic-gate void 548*0Sstevel@tonic-gate pci_fm_create(pci_t *pci_p) 549*0Sstevel@tonic-gate { 550*0Sstevel@tonic-gate pci_common_t *cmn_p = pci_p->pci_common_p; 551*0Sstevel@tonic-gate 552*0Sstevel@tonic-gate /* 553*0Sstevel@tonic-gate * PCI detected ECC errorq, to schedule async handling 554*0Sstevel@tonic-gate * of ECC errors and logging. 555*0Sstevel@tonic-gate * The errorq is created here but destroyed when _fini is called 556*0Sstevel@tonic-gate * for the pci module. 557*0Sstevel@tonic-gate */ 558*0Sstevel@tonic-gate if (pci_ecc_queue == NULL) { 559*0Sstevel@tonic-gate pci_ecc_queue = errorq_create("pci_ecc_queue", 560*0Sstevel@tonic-gate (errorq_func_t)ecc_err_drain, 561*0Sstevel@tonic-gate (void *)NULL, 562*0Sstevel@tonic-gate ECC_MAX_ERRS, sizeof (ecc_errstate_t), 563*0Sstevel@tonic-gate PIL_2, ERRORQ_VITAL); 564*0Sstevel@tonic-gate if (pci_ecc_queue == NULL) 565*0Sstevel@tonic-gate panic("failed to create required system error queue"); 566*0Sstevel@tonic-gate } 567*0Sstevel@tonic-gate 568*0Sstevel@tonic-gate /* 569*0Sstevel@tonic-gate * PCI target errorq, to schedule async handling of generation of 570*0Sstevel@tonic-gate * target device ereports based on captured physical address. 571*0Sstevel@tonic-gate * The errorq is created here but destroyed when _fini is called 572*0Sstevel@tonic-gate * for the pci module. 573*0Sstevel@tonic-gate */ 574*0Sstevel@tonic-gate if (pci_target_queue == NULL) { 575*0Sstevel@tonic-gate pci_target_queue = errorq_create("pci_target_queue", 576*0Sstevel@tonic-gate (errorq_func_t)pci_target_drain, 577*0Sstevel@tonic-gate (void *)NULL, 578*0Sstevel@tonic-gate TARGET_MAX_ERRS, sizeof (pci_target_err_t), 579*0Sstevel@tonic-gate PIL_2, ERRORQ_VITAL); 580*0Sstevel@tonic-gate if (pci_target_queue == NULL) 581*0Sstevel@tonic-gate panic("failed to create required system error queue"); 582*0Sstevel@tonic-gate } 583*0Sstevel@tonic-gate 584*0Sstevel@tonic-gate /* 585*0Sstevel@tonic-gate * Initialize FMA support 586*0Sstevel@tonic-gate * The axq workaround prevents fault management of access errors 587*0Sstevel@tonic-gate */ 588*0Sstevel@tonic-gate if (pci_p->pci_pbm_p->pbm_pio_limit == 0) 589*0Sstevel@tonic-gate pci_p->pci_fm_cap = DDI_FM_EREPORT_CAPABLE | 590*0Sstevel@tonic-gate DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE | 591*0Sstevel@tonic-gate DDI_FM_ERRCB_CAPABLE; 592*0Sstevel@tonic-gate else 593*0Sstevel@tonic-gate pci_p->pci_fm_cap = DDI_FM_EREPORT_CAPABLE | 594*0Sstevel@tonic-gate DDI_FM_DMACHK_CAPABLE | DDI_FM_ERRCB_CAPABLE; 595*0Sstevel@tonic-gate /* 596*0Sstevel@tonic-gate * Call parent to get it's capablity 597*0Sstevel@tonic-gate */ 598*0Sstevel@tonic-gate ddi_fm_init(pci_p->pci_dip, &pci_p->pci_fm_cap, 599*0Sstevel@tonic-gate &pci_p->pci_fm_ibc); 600*0Sstevel@tonic-gate /* 601*0Sstevel@tonic-gate * Need to be ereport and error handler cabable 602*0Sstevel@tonic-gate */ 603*0Sstevel@tonic-gate ASSERT((pci_p->pci_fm_cap & DDI_FM_ERRCB_CAPABLE) && 604*0Sstevel@tonic-gate (pci_p->pci_fm_cap & DDI_FM_EREPORT_CAPABLE)); 605*0Sstevel@tonic-gate /* 606*0Sstevel@tonic-gate * Initialize error handling mutex. 607*0Sstevel@tonic-gate */ 608*0Sstevel@tonic-gate if (cmn_p->pci_common_refcnt == 0) { 609*0Sstevel@tonic-gate mutex_init(&cmn_p->pci_fm_mutex, NULL, MUTEX_DRIVER, 610*0Sstevel@tonic-gate (void *)pci_p->pci_fm_ibc); 611*0Sstevel@tonic-gate } 612*0Sstevel@tonic-gate 613*0Sstevel@tonic-gate /* 614*0Sstevel@tonic-gate * Register error callback with our parent. 615*0Sstevel@tonic-gate */ 616*0Sstevel@tonic-gate ddi_fm_handler_register(pci_p->pci_dip, pci_err_callback, 617*0Sstevel@tonic-gate pci_p); 618*0Sstevel@tonic-gate 619*0Sstevel@tonic-gate } 620*0Sstevel@tonic-gate 621*0Sstevel@tonic-gate void 622*0Sstevel@tonic-gate pci_fm_destroy(pci_t *pci_p) 623*0Sstevel@tonic-gate { 624*0Sstevel@tonic-gate pci_common_t *cmn_p = pci_p->pci_common_p; 625*0Sstevel@tonic-gate 626*0Sstevel@tonic-gate /* schizo non-shared objects */ 627*0Sstevel@tonic-gate ddi_fm_handler_unregister(pci_p->pci_dip); 628*0Sstevel@tonic-gate ddi_fm_fini(pci_p->pci_dip); 629*0Sstevel@tonic-gate 630*0Sstevel@tonic-gate if (cmn_p->pci_common_refcnt != 0) 631*0Sstevel@tonic-gate return; 632*0Sstevel@tonic-gate 633*0Sstevel@tonic-gate mutex_destroy(&cmn_p->pci_fm_mutex); 634*0Sstevel@tonic-gate } 635*0Sstevel@tonic-gate 636*0Sstevel@tonic-gate /* 637*0Sstevel@tonic-gate * Function used to post PCI block module specific ereports. 638*0Sstevel@tonic-gate */ 639*0Sstevel@tonic-gate void 640*0Sstevel@tonic-gate pbm_ereport_post(dev_info_t *dip, uint64_t ena, pbm_errstate_t *pbm_err) 641*0Sstevel@tonic-gate { 642*0Sstevel@tonic-gate char buf[FM_MAX_CLASS]; 643*0Sstevel@tonic-gate 644*0Sstevel@tonic-gate (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", 645*0Sstevel@tonic-gate pbm_err->pbm_bridge_type, pbm_err->pbm_err_class); 646*0Sstevel@tonic-gate 647*0Sstevel@tonic-gate ena = ena ? ena : fm_ena_generate(0, FM_ENA_FMT1); 648*0Sstevel@tonic-gate 649*0Sstevel@tonic-gate ddi_fm_ereport_post(dip, buf, ena, DDI_NOSLEEP, 650*0Sstevel@tonic-gate FM_VERSION, DATA_TYPE_UINT8, 0, 651*0Sstevel@tonic-gate PCI_CONFIG_STATUS, DATA_TYPE_UINT16, pbm_err->pbm_pci.pci_cfg_stat, 652*0Sstevel@tonic-gate PCI_CONFIG_COMMAND, DATA_TYPE_UINT16, pbm_err->pbm_pci.pci_cfg_comm, 653*0Sstevel@tonic-gate PCI_PBM_CSR, DATA_TYPE_UINT64, pbm_err->pbm_ctl_stat, 654*0Sstevel@tonic-gate PCI_PBM_AFSR, DATA_TYPE_UINT64, pbm_err->pbm_afsr, 655*0Sstevel@tonic-gate PCI_PBM_AFAR, DATA_TYPE_UINT64, pbm_err->pbm_afar, 656*0Sstevel@tonic-gate PCI_PBM_SLOT, DATA_TYPE_UINT64, pbm_err->pbm_err_sl, 657*0Sstevel@tonic-gate PCI_PBM_VALOG, DATA_TYPE_UINT64, pbm_err->pbm_va_log, 658*0Sstevel@tonic-gate NULL); 659*0Sstevel@tonic-gate } 660