pciutils-3.13.0/0000755000175000001440000000000014631121771011762 5ustar mjuserspciutils-3.13.0/ls-ecaps.c0000644000175000001440000016061214613646765013662 0ustar mjusers/* * The PCI Utilities -- Show Extended Capabilities * * Copyright (c) 1997--2022 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include "lspci.h" static void cap_tph(struct device *d, int where) { u32 tph_cap; printf("Transaction Processing Hints\n"); if (verbose < 2) return; if (!config_fetch(d, where + PCI_TPH_CAPABILITIES, 4)) return; tph_cap = get_conf_long(d, where + PCI_TPH_CAPABILITIES); if (tph_cap & PCI_TPH_INTVEC_SUP) printf("\t\tInterrupt vector mode supported\n"); if (tph_cap & PCI_TPH_DEV_SUP) printf("\t\tDevice specific mode supported\n"); if (tph_cap & PCI_TPH_EXT_REQ_SUP) printf("\t\tExtended requester support\n"); switch (tph_cap & PCI_TPH_ST_LOC_MASK) { case PCI_TPH_ST_NONE: printf("\t\tNo steering table available\n"); break; case PCI_TPH_ST_CAP: printf("\t\tSteering table in TPH capability structure\n"); break; case PCI_TPH_ST_MSIX: printf("\t\tSteering table in MSI-X table\n"); break; default: printf("\t\tReserved steering table location\n"); break; } } static u32 cap_ltr_scale(u8 scale) { return 1 << (scale * 5); } static void cap_ltr(struct device *d, int where) { u32 scale; u16 snoop, nosnoop; printf("Latency Tolerance Reporting\n"); if (verbose < 2) return; if (!config_fetch(d, where + PCI_LTR_MAX_SNOOP, 4)) return; snoop = get_conf_word(d, where + PCI_LTR_MAX_SNOOP); scale = cap_ltr_scale((snoop >> PCI_LTR_SCALE_SHIFT) & PCI_LTR_SCALE_MASK); printf("\t\tMax snoop latency: %" PCI_U64_FMT_U "ns\n", ((u64)snoop & PCI_LTR_VALUE_MASK) * scale); nosnoop = get_conf_word(d, where + PCI_LTR_MAX_NOSNOOP); scale = cap_ltr_scale((nosnoop >> PCI_LTR_SCALE_SHIFT) & PCI_LTR_SCALE_MASK); printf("\t\tMax no snoop latency: %" PCI_U64_FMT_U "ns\n", ((u64)nosnoop & PCI_LTR_VALUE_MASK) * scale); } static void cap_sec(struct device *d, int where) { u32 ctrl3, lane_err_stat; u8 lane; printf("Secondary PCI Express\n"); if (verbose < 2) return; if (!config_fetch(d, where + PCI_SEC_LNKCTL3, 12)) return; ctrl3 = get_conf_word(d, where + PCI_SEC_LNKCTL3); printf("\t\tLnkCtl3: LnkEquIntrruptEn%c PerformEqu%c\n", FLAG(ctrl3, PCI_SEC_LNKCTL3_LNK_EQU_REQ_INTR_EN), FLAG(ctrl3, PCI_SEC_LNKCTL3_PERFORM_LINK_EQU)); lane_err_stat = get_conf_word(d, where + PCI_SEC_LANE_ERR); printf("\t\tLaneErrStat: "); if (lane_err_stat) { printf("LaneErr at lane:"); for (lane = 0; lane_err_stat; lane_err_stat >>= 1, lane += 1) if (BITS(lane_err_stat, 0, 1)) printf(" %u", lane); } else printf("0"); printf("\n"); } static void cap_dsn(struct device *d, int where) { u32 t1, t2; if (!config_fetch(d, where + 4, 8)) return; t1 = get_conf_long(d, where + 4); t2 = get_conf_long(d, where + 8); printf("Device Serial Number %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n", t2 >> 24, (t2 >> 16) & 0xff, (t2 >> 8) & 0xff, t2 & 0xff, t1 >> 24, (t1 >> 16) & 0xff, (t1 >> 8) & 0xff, t1 & 0xff); } static void cap_aer(struct device *d, int where, int type) { u32 l, l0, l1, l2, l3; u16 w; printf("Advanced Error Reporting\n"); if (verbose < 2) return; if (!config_fetch(d, where + PCI_ERR_UNCOR_STATUS, 40)) return; l = get_conf_long(d, where + PCI_ERR_UNCOR_STATUS); printf("\t\tUESta:\tDLP%c SDES%c TLP%c FCP%c CmpltTO%c CmpltAbrt%c UnxCmplt%c RxOF%c MalfTLP%c\n" "\t\t\tECRC%c UnsupReq%c ACSViol%c UncorrIntErr%c BlockedTLP%c AtomicOpBlocked%c TLPBlockedErr%c\n" "\t\t\tPoisonTLPBlocked%c DMWrReqBlocked%c IDECheck%c MisIDETLP%c PCRC_CHECK%c TLPXlatBlocked%c\n", FLAG(l, PCI_ERR_UNC_DLP), FLAG(l, PCI_ERR_UNC_SDES), FLAG(l, PCI_ERR_UNC_POISON_TLP), FLAG(l, PCI_ERR_UNC_FCP), FLAG(l, PCI_ERR_UNC_COMP_TIME), FLAG(l, PCI_ERR_UNC_COMP_ABORT), FLAG(l, PCI_ERR_UNC_UNX_COMP), FLAG(l, PCI_ERR_UNC_RX_OVER), FLAG(l, PCI_ERR_UNC_MALF_TLP), FLAG(l, PCI_ERR_UNC_ECRC), FLAG(l, PCI_ERR_UNC_UNSUP), FLAG(l, PCI_ERR_UNC_ACS_VIOL), FLAG(l, PCI_ERR_UNC_INTERNAL), FLAG(l, PCI_ERR_UNC_MC_BLOCKED_TLP), FLAG(l, PCI_ERR_UNC_ATOMICOP_EGRESS_BLOCKED), FLAG(l, PCI_ERR_UNC_TLP_PREFIX_BLOCKED), FLAG(l, PCI_ERR_UNC_POISONED_TLP_EGRESS), FLAG(l, PCI_ERR_UNC_DMWR_REQ_EGRESS_BLOCKED), FLAG(l, PCI_ERR_UNC_IDE_CHECK), FLAG(l, PCI_ERR_UNC_MISR_IDE_TLP), FLAG(l, PCI_ERR_UNC_PCRC_CHECK), FLAG(l, PCI_ERR_UNC_TLP_XLAT_EGRESS_BLOCKED)); l = get_conf_long(d, where + PCI_ERR_UNCOR_MASK); printf("\t\tUEMsk:\tDLP%c SDES%c TLP%c FCP%c CmpltTO%c CmpltAbrt%c UnxCmplt%c RxOF%c MalfTLP%c\n" "\t\t\tECRC%c UnsupReq%c ACSViol%c UncorrIntErr%c BlockedTLP%c AtomicOpBlocked%c TLPBlockedErr%c\n" "\t\t\tPoisonTLPBlocked%c DMWrReqBlocked%c IDECheck%c MisIDETLP%c PCRC_CHECK%c TLPXlatBlocked%c\n", FLAG(l, PCI_ERR_UNC_DLP), FLAG(l, PCI_ERR_UNC_SDES), FLAG(l, PCI_ERR_UNC_POISON_TLP), FLAG(l, PCI_ERR_UNC_FCP), FLAG(l, PCI_ERR_UNC_COMP_TIME), FLAG(l, PCI_ERR_UNC_COMP_ABORT), FLAG(l, PCI_ERR_UNC_UNX_COMP), FLAG(l, PCI_ERR_UNC_RX_OVER), FLAG(l, PCI_ERR_UNC_MALF_TLP), FLAG(l, PCI_ERR_UNC_ECRC), FLAG(l, PCI_ERR_UNC_UNSUP), FLAG(l, PCI_ERR_UNC_ACS_VIOL), FLAG(l, PCI_ERR_UNC_INTERNAL), FLAG(l, PCI_ERR_UNC_MC_BLOCKED_TLP), FLAG(l, PCI_ERR_UNC_ATOMICOP_EGRESS_BLOCKED), FLAG(l, PCI_ERR_UNC_TLP_PREFIX_BLOCKED), FLAG(l, PCI_ERR_UNC_POISONED_TLP_EGRESS), FLAG(l, PCI_ERR_UNC_DMWR_REQ_EGRESS_BLOCKED), FLAG(l, PCI_ERR_UNC_IDE_CHECK), FLAG(l, PCI_ERR_UNC_MISR_IDE_TLP), FLAG(l, PCI_ERR_UNC_PCRC_CHECK), FLAG(l, PCI_ERR_UNC_TLP_XLAT_EGRESS_BLOCKED)); l = get_conf_long(d, where + PCI_ERR_UNCOR_SEVER); printf("\t\tUESvrt:\tDLP%c SDES%c TLP%c FCP%c CmpltTO%c CmpltAbrt%c UnxCmplt%c RxOF%c MalfTLP%c\n" "\t\t\tECRC%c UnsupReq%c ACSViol%c UncorrIntErr%c BlockedTLP%c AtomicOpBlocked%c TLPBlockedErr%c\n" "\t\t\tPoisonTLPBlocked%c DMWrReqBlocked%c IDECheck%c MisIDETLP%c PCRC_CHECK%c TLPXlatBlocked%c\n", FLAG(l, PCI_ERR_UNC_DLP), FLAG(l, PCI_ERR_UNC_SDES), FLAG(l, PCI_ERR_UNC_POISON_TLP), FLAG(l, PCI_ERR_UNC_FCP), FLAG(l, PCI_ERR_UNC_COMP_TIME), FLAG(l, PCI_ERR_UNC_COMP_ABORT), FLAG(l, PCI_ERR_UNC_UNX_COMP), FLAG(l, PCI_ERR_UNC_RX_OVER), FLAG(l, PCI_ERR_UNC_MALF_TLP), FLAG(l, PCI_ERR_UNC_ECRC), FLAG(l, PCI_ERR_UNC_UNSUP), FLAG(l, PCI_ERR_UNC_ACS_VIOL), FLAG(l, PCI_ERR_UNC_INTERNAL), FLAG(l, PCI_ERR_UNC_MC_BLOCKED_TLP), FLAG(l, PCI_ERR_UNC_ATOMICOP_EGRESS_BLOCKED), FLAG(l, PCI_ERR_UNC_TLP_PREFIX_BLOCKED), FLAG(l, PCI_ERR_UNC_POISONED_TLP_EGRESS), FLAG(l, PCI_ERR_UNC_DMWR_REQ_EGRESS_BLOCKED), FLAG(l, PCI_ERR_UNC_IDE_CHECK), FLAG(l, PCI_ERR_UNC_MISR_IDE_TLP), FLAG(l, PCI_ERR_UNC_PCRC_CHECK), FLAG(l, PCI_ERR_UNC_TLP_XLAT_EGRESS_BLOCKED)); l = get_conf_long(d, where + PCI_ERR_COR_STATUS); printf("\t\tCESta:\tRxErr%c BadTLP%c BadDLLP%c Rollover%c Timeout%c AdvNonFatalErr%c " "CorrIntErr%c HeaderOF%c\n", FLAG(l, PCI_ERR_COR_RCVR), FLAG(l, PCI_ERR_COR_BAD_TLP), FLAG(l, PCI_ERR_COR_BAD_DLLP), FLAG(l, PCI_ERR_COR_REP_ROLL), FLAG(l, PCI_ERR_COR_REP_TIMER), FLAG(l, PCI_ERR_COR_REP_ANFE), FLAG(l, PCI_ERR_COR_INTERNAL), FLAG(l, PCI_ERR_COR_HDRLOG_OVER)); l = get_conf_long(d, where + PCI_ERR_COR_MASK); printf("\t\tCEMsk:\tRxErr%c BadTLP%c BadDLLP%c Rollover%c Timeout%c AdvNonFatalErr%c " "CorrIntErr%c HeaderOF%c\n", FLAG(l, PCI_ERR_COR_RCVR), FLAG(l, PCI_ERR_COR_BAD_TLP), FLAG(l, PCI_ERR_COR_BAD_DLLP), FLAG(l, PCI_ERR_COR_REP_ROLL), FLAG(l, PCI_ERR_COR_REP_TIMER), FLAG(l, PCI_ERR_COR_REP_ANFE), FLAG(l, PCI_ERR_COR_INTERNAL), FLAG(l, PCI_ERR_COR_HDRLOG_OVER)); l = get_conf_long(d, where + PCI_ERR_CAP); printf("\t\tAERCap:\tFirst Error Pointer: %02x, ECRCGenCap%c ECRCGenEn%c ECRCChkCap%c ECRCChkEn%c\n" "\t\t\tMultHdrRecCap%c MultHdrRecEn%c TLPPfxPres%c HdrLogCap%c\n", PCI_ERR_CAP_FEP(l), FLAG(l, PCI_ERR_CAP_ECRC_GENC), FLAG(l, PCI_ERR_CAP_ECRC_GENE), FLAG(l, PCI_ERR_CAP_ECRC_CHKC), FLAG(l, PCI_ERR_CAP_ECRC_CHKE), FLAG(l, PCI_ERR_CAP_MULT_HDRC), FLAG(l, PCI_ERR_CAP_MULT_HDRE), FLAG(l, PCI_ERR_CAP_TLP_PFX), FLAG(l, PCI_ERR_CAP_HDR_LOG)); l0 = get_conf_long(d, where + PCI_ERR_HEADER_LOG); l1 = get_conf_long(d, where + PCI_ERR_HEADER_LOG + 4); l2 = get_conf_long(d, where + PCI_ERR_HEADER_LOG + 8); l3 = get_conf_long(d, where + PCI_ERR_HEADER_LOG + 12); printf("\t\tHeaderLog: %08x %08x %08x %08x\n", l0, l1, l2, l3); if (type == PCI_EXP_TYPE_ROOT_PORT || type == PCI_EXP_TYPE_ROOT_EC) { if (!config_fetch(d, where + PCI_ERR_ROOT_COMMAND, 12)) return; l = get_conf_long(d, where + PCI_ERR_ROOT_COMMAND); printf("\t\tRootCmd: CERptEn%c NFERptEn%c FERptEn%c\n", FLAG(l, PCI_ERR_ROOT_CMD_COR_EN), FLAG(l, PCI_ERR_ROOT_CMD_NONFATAL_EN), FLAG(l, PCI_ERR_ROOT_CMD_FATAL_EN)); l = get_conf_long(d, where + PCI_ERR_ROOT_STATUS); printf("\t\tRootSta: CERcvd%c MultCERcvd%c UERcvd%c MultUERcvd%c\n" "\t\t\t FirstFatal%c NonFatalMsg%c FatalMsg%c IntMsgNum %d\n", FLAG(l, PCI_ERR_ROOT_COR_RCV), FLAG(l, PCI_ERR_ROOT_MULTI_COR_RCV), FLAG(l, PCI_ERR_ROOT_UNCOR_RCV), FLAG(l, PCI_ERR_ROOT_MULTI_UNCOR_RCV), FLAG(l, PCI_ERR_ROOT_FIRST_FATAL), FLAG(l, PCI_ERR_ROOT_NONFATAL_RCV), FLAG(l, PCI_ERR_ROOT_FATAL_RCV), PCI_ERR_MSG_NUM(l)); w = get_conf_word(d, where + PCI_ERR_ROOT_COR_SRC); printf("\t\tErrorSrc: ERR_COR: %04x ", w); w = get_conf_word(d, where + PCI_ERR_ROOT_SRC); printf("ERR_FATAL/NONFATAL: %04x\n", w); } } static void cap_dpc(struct device *d, int where) { u16 l; printf("Downstream Port Containment\n"); if (verbose < 2) return; if (!config_fetch(d, where + PCI_DPC_CAP, 8)) return; l = get_conf_word(d, where + PCI_DPC_CAP); printf("\t\tDpcCap:\tIntMsgNum %d, RPExt%c PoisonedTLP%c SwTrigger%c RP PIO Log %d, DL_ActiveErr%c\n", PCI_DPC_CAP_INT_MSG(l), FLAG(l, PCI_DPC_CAP_RP_EXT), FLAG(l, PCI_DPC_CAP_TLP_BLOCK), FLAG(l, PCI_DPC_CAP_SW_TRIGGER), PCI_DPC_CAP_RP_LOG(l), FLAG(l, PCI_DPC_CAP_DL_ACT_ERR)); l = get_conf_word(d, where + PCI_DPC_CTL); printf("\t\tDpcCtl:\tTrigger:%x Cmpl%c INT%c ErrCor%c PoisonedTLP%c SwTrigger%c DL_ActiveErr%c\n", PCI_DPC_CTL_TRIGGER(l), FLAG(l, PCI_DPC_CTL_CMPL), FLAG(l, PCI_DPC_CTL_INT), FLAG(l, PCI_DPC_CTL_ERR_COR), FLAG(l, PCI_DPC_CTL_TLP), FLAG(l, PCI_DPC_CTL_SW_TRIGGER), FLAG(l, PCI_DPC_CTL_DL_ACTIVE)); l = get_conf_word(d, where + PCI_DPC_STATUS); printf("\t\tDpcSta:\tTrigger%c Reason:%02x INT%c RPBusy%c TriggerExt:%02x RP PIO ErrPtr:%02x\n", FLAG(l, PCI_DPC_STS_TRIGGER), PCI_DPC_STS_REASON(l), FLAG(l, PCI_DPC_STS_INT), FLAG(l, PCI_DPC_STS_RP_BUSY), PCI_DPC_STS_TRIGGER_EXT(l), PCI_DPC_STS_PIO_FEP(l)); l = get_conf_word(d, where + PCI_DPC_SOURCE); printf("\t\tSource:\t%04x\n", l); } static void cap_acs(struct device *d, int where) { u16 w; printf("Access Control Services\n"); if (verbose < 2) return; if (!config_fetch(d, where + PCI_ACS_CAP, 4)) return; w = get_conf_word(d, where + PCI_ACS_CAP); printf("\t\tACSCap:\tSrcValid%c TransBlk%c ReqRedir%c CmpltRedir%c UpstreamFwd%c EgressCtrl%c " "DirectTrans%c\n", FLAG(w, PCI_ACS_CAP_VALID), FLAG(w, PCI_ACS_CAP_BLOCK), FLAG(w, PCI_ACS_CAP_REQ_RED), FLAG(w, PCI_ACS_CAP_CMPLT_RED), FLAG(w, PCI_ACS_CAP_FORWARD), FLAG(w, PCI_ACS_CAP_EGRESS), FLAG(w, PCI_ACS_CAP_TRANS)); w = get_conf_word(d, where + PCI_ACS_CTRL); printf("\t\tACSCtl:\tSrcValid%c TransBlk%c ReqRedir%c CmpltRedir%c UpstreamFwd%c EgressCtrl%c " "DirectTrans%c\n", FLAG(w, PCI_ACS_CTRL_VALID), FLAG(w, PCI_ACS_CTRL_BLOCK), FLAG(w, PCI_ACS_CTRL_REQ_RED), FLAG(w, PCI_ACS_CTRL_CMPLT_RED), FLAG(w, PCI_ACS_CTRL_FORWARD), FLAG(w, PCI_ACS_CTRL_EGRESS), FLAG(w, PCI_ACS_CTRL_TRANS)); } static void cap_ari(struct device *d, int where) { u16 w; printf("Alternative Routing-ID Interpretation (ARI)\n"); if (verbose < 2) return; if (!config_fetch(d, where + PCI_ARI_CAP, 4)) return; w = get_conf_word(d, where + PCI_ARI_CAP); printf("\t\tARICap:\tMFVC%c ACS%c, Next Function: %d\n", FLAG(w, PCI_ARI_CAP_MFVC), FLAG(w, PCI_ARI_CAP_ACS), PCI_ARI_CAP_NFN(w)); w = get_conf_word(d, where + PCI_ARI_CTRL); printf("\t\tARICtl:\tMFVC%c ACS%c, Function Group: %d\n", FLAG(w, PCI_ARI_CTRL_MFVC), FLAG(w, PCI_ARI_CTRL_ACS), PCI_ARI_CTRL_FG(w)); } static void cap_ats(struct device *d, int where) { u16 w; printf("Address Translation Service (ATS)\n"); if (verbose < 2) return; if (!config_fetch(d, where + PCI_ATS_CAP, 4)) return; w = get_conf_word(d, where + PCI_ATS_CAP); printf("\t\tATSCap:\tInvalidate Queue Depth: %02x\n", PCI_ATS_CAP_IQD(w)); w = get_conf_word(d, where + PCI_ATS_CTRL); printf("\t\tATSCtl:\tEnable%c, Smallest Translation Unit: %02x\n", FLAG(w, PCI_ATS_CTRL_ENABLE), PCI_ATS_CTRL_STU(w)); } static void cap_pri(struct device *d, int where) { u16 w; u32 l; printf("Page Request Interface (PRI)\n"); if (verbose < 2) return; if (!config_fetch(d, where + PCI_PRI_CTRL, 0xc)) return; w = get_conf_word(d, where + PCI_PRI_CTRL); printf("\t\tPRICtl: Enable%c Reset%c\n", FLAG(w, PCI_PRI_CTRL_ENABLE), FLAG(w, PCI_PRI_CTRL_RESET)); w = get_conf_word(d, where + PCI_PRI_STATUS); printf("\t\tPRISta: RF%c UPRGI%c Stopped%c PASID%c\n", FLAG(w, PCI_PRI_STATUS_RF), FLAG(w, PCI_PRI_STATUS_UPRGI), FLAG(w, PCI_PRI_STATUS_STOPPED), FLAG(w, PCI_PRI_STATUS_PASID)); l = get_conf_long(d, where + PCI_PRI_MAX_REQ); printf("\t\tPage Request Capacity: %08x, ", l); l = get_conf_long(d, where + PCI_PRI_ALLOC_REQ); printf("Page Request Allocation: %08x\n", l); } static void cap_pasid(struct device *d, int where) { u16 w; printf("Process Address Space ID (PASID)\n"); if (verbose < 2) return; if (!config_fetch(d, where + PCI_PASID_CAP, 4)) return; w = get_conf_word(d, where + PCI_PASID_CAP); printf("\t\tPASIDCap: Exec%c Priv%c, Max PASID Width: %02x\n", FLAG(w, PCI_PASID_CAP_EXEC), FLAG(w, PCI_PASID_CAP_PRIV), PCI_PASID_CAP_WIDTH(w)); w = get_conf_word(d, where + PCI_PASID_CTRL); printf("\t\tPASIDCtl: Enable%c Exec%c Priv%c\n", FLAG(w, PCI_PASID_CTRL_ENABLE), FLAG(w, PCI_PASID_CTRL_EXEC), FLAG(w, PCI_PASID_CTRL_PRIV)); } static void cap_sriov(struct device *d, int where) { u16 b; u16 w; u32 l; int i; printf("Single Root I/O Virtualization (SR-IOV)\n"); if (verbose < 2) return; if (!config_fetch(d, where + PCI_IOV_CAP, 0x3c)) return; l = get_conf_long(d, where + PCI_IOV_CAP); printf("\t\tIOVCap:\tMigration%c 10BitTagReq%c IntMsgNum %d\n", FLAG(l, PCI_IOV_CAP_VFM), FLAG(l, PCI_IOV_CAP_VF_10BIT_TAG_REQ), PCI_IOV_CAP_IMN(l)); w = get_conf_word(d, where + PCI_IOV_CTRL); printf("\t\tIOVCtl:\tEnable%c Migration%c Interrupt%c MSE%c ARIHierarchy%c 10BitTagReq%c\n", FLAG(w, PCI_IOV_CTRL_VFE), FLAG(w, PCI_IOV_CTRL_VFME), FLAG(w, PCI_IOV_CTRL_VFMIE), FLAG(w, PCI_IOV_CTRL_MSE), FLAG(w, PCI_IOV_CTRL_ARI), FLAG(w, PCI_IOV_CTRL_VF_10BIT_TAG_REQ_EN)); w = get_conf_word(d, where + PCI_IOV_STATUS); printf("\t\tIOVSta:\tMigration%c\n", FLAG(w, PCI_IOV_STATUS_MS)); w = get_conf_word(d, where + PCI_IOV_INITIALVF); printf("\t\tInitial VFs: %d, ", w); w = get_conf_word(d, where + PCI_IOV_TOTALVF); printf("Total VFs: %d, ", w); w = get_conf_word(d, where + PCI_IOV_NUMVF); printf("Number of VFs: %d, ", w); b = get_conf_byte(d, where + PCI_IOV_FDL); printf("Function Dependency Link: %02x\n", b); w = get_conf_word(d, where + PCI_IOV_OFFSET); printf("\t\tVF offset: %d, ", w); w = get_conf_word(d, where + PCI_IOV_STRIDE); printf("stride: %d, ", w); w = get_conf_word(d, where + PCI_IOV_DID); printf("Device ID: %04x\n", w); l = get_conf_long(d, where + PCI_IOV_SUPPS); printf("\t\tSupported Page Size: %08x, ", l); l = get_conf_long(d, where + PCI_IOV_SYSPS); printf("System Page Size: %08x\n", l); for (i=0; i < PCI_IOV_NUM_BAR; i++) { u32 addr; int type; u32 h; l = get_conf_long(d, where + PCI_IOV_BAR_BASE + 4*i); if (l == 0xffffffff) l = 0; if (!l) continue; printf("\t\tRegion %d: Memory at ", i); addr = l & PCI_ADDR_MEM_MASK; type = l & PCI_BASE_ADDRESS_MEM_TYPE_MASK; if (type == PCI_BASE_ADDRESS_MEM_TYPE_64) { i++; h = get_conf_long(d, where + PCI_IOV_BAR_BASE + (i*4)); printf("%08x", h); } printf("%08x (%s-bit, %sprefetchable)\n", addr, (type == PCI_BASE_ADDRESS_MEM_TYPE_32) ? "32" : "64", (l & PCI_BASE_ADDRESS_MEM_PREFETCH) ? "" : "non-"); } l = get_conf_long(d, where + PCI_IOV_MSAO); printf("\t\tVF Migration: offset: %08x, BIR: %x\n", PCI_IOV_MSA_OFFSET(l), PCI_IOV_MSA_BIR(l)); } static void cap_multicast(struct device *d, int where, int type) { u16 w; u32 l; u64 bar, rcv, block; printf("Multicast\n"); if (verbose < 2) return; if (!config_fetch(d, where + PCI_MCAST_CAP, 0x30)) return; w = get_conf_word(d, where + PCI_MCAST_CAP); printf("\t\tMcastCap: MaxGroups %d", PCI_MCAST_CAP_MAX_GROUP(w) + 1); if (type == PCI_EXP_TYPE_ENDPOINT || type == PCI_EXP_TYPE_ROOT_INT_EP) printf(", WindowSz %d (%d bytes)", PCI_MCAST_CAP_WIN_SIZE(w), 1 << PCI_MCAST_CAP_WIN_SIZE(w)); if (type == PCI_EXP_TYPE_ROOT_PORT || type == PCI_EXP_TYPE_UPSTREAM || type == PCI_EXP_TYPE_DOWNSTREAM) printf(", ECRCRegen%c\n", FLAG(w, PCI_MCAST_CAP_ECRC)); w = get_conf_word(d, where + PCI_MCAST_CTRL); printf("\t\tMcastCtl: NumGroups %d, Enable%c\n", PCI_MCAST_CTRL_NUM_GROUP(w) + 1, FLAG(w, PCI_MCAST_CTRL_ENABLE)); bar = get_conf_long(d, where + PCI_MCAST_BAR); l = get_conf_long(d, where + PCI_MCAST_BAR + 4); bar |= (u64) l << 32; printf("\t\tMcastBAR: IndexPos %d, BaseAddr %016" PCI_U64_FMT_X "\n", PCI_MCAST_BAR_INDEX_POS(bar), bar & PCI_MCAST_BAR_MASK); rcv = get_conf_long(d, where + PCI_MCAST_RCV); l = get_conf_long(d, where + PCI_MCAST_RCV + 4); rcv |= (u64) l << 32; printf("\t\tMcastReceiveVec: %016" PCI_U64_FMT_X "\n", rcv); block = get_conf_long(d, where + PCI_MCAST_BLOCK); l = get_conf_long(d, where + PCI_MCAST_BLOCK + 4); block |= (u64) l << 32; printf("\t\tMcastBlockAllVec: %016" PCI_U64_FMT_X "\n", block); block = get_conf_long(d, where + PCI_MCAST_BLOCK_UNTRANS); l = get_conf_long(d, where + PCI_MCAST_BLOCK_UNTRANS + 4); block |= (u64) l << 32; printf("\t\tMcastBlockUntransVec: %016" PCI_U64_FMT_X "\n", block); if (type == PCI_EXP_TYPE_ENDPOINT || type == PCI_EXP_TYPE_ROOT_INT_EP) return; bar = get_conf_long(d, where + PCI_MCAST_OVL_BAR); l = get_conf_long(d, where + PCI_MCAST_OVL_BAR + 4); bar |= (u64) l << 32; printf("\t\tMcastOverlayBAR: OverlaySize %d ", PCI_MCAST_OVL_SIZE(bar)); if (PCI_MCAST_OVL_SIZE(bar) >= 6) printf("(%d bytes)", 1 << PCI_MCAST_OVL_SIZE(bar)); else printf("(disabled)"); printf(", BaseAddr %016" PCI_U64_FMT_X "\n", bar & PCI_MCAST_OVL_MASK); } static void cap_vc(struct device *d, int where) { u32 cr1, cr2; u16 ctrl, status; int evc_cnt; int arb_table_pos; int i, j; static const char ref_clocks[][6] = { "100ns" }; static const char arb_selects[8][7] = { "Fixed", "WRR32", "WRR64", "WRR128", "??4", "??5", "??6", "??7" }; static const char vc_arb_selects[8][8] = { "Fixed", "WRR32", "WRR64", "WRR128", "TWRR128", "WRR256", "??6", "??7" }; char buf[8]; printf("Virtual Channel\n"); if (verbose < 2) return; if (!config_fetch(d, where + 4, 0x1c - 4)) return; cr1 = get_conf_long(d, where + PCI_VC_PORT_REG1); cr2 = get_conf_long(d, where + PCI_VC_PORT_REG2); ctrl = get_conf_word(d, where + PCI_VC_PORT_CTRL); status = get_conf_word(d, where + PCI_VC_PORT_STATUS); evc_cnt = BITS(cr1, 0, 3); printf("\t\tCaps:\tLPEVC=%d RefClk=%s PATEntryBits=%d\n", BITS(cr1, 4, 3), TABLE(ref_clocks, BITS(cr1, 8, 2), buf), 1 << BITS(cr1, 10, 2)); printf("\t\tArb:"); for (i=0; i<8; i++) if (arb_selects[i][0] != '?' || cr2 & (1 << i)) printf("%c%s%c", (i ? ' ' : '\t'), arb_selects[i], FLAG(cr2, 1 << i)); arb_table_pos = BITS(cr2, 24, 8); printf("\n\t\tCtrl:\tArbSelect=%s\n", TABLE(arb_selects, BITS(ctrl, 1, 3), buf)); printf("\t\tStatus:\tInProgress%c\n", FLAG(status, 1)); if (arb_table_pos) { arb_table_pos = where + 16*arb_table_pos; printf("\t\tPort Arbitration Table [%x] \n", arb_table_pos); } for (i=0; i<=evc_cnt; i++) { int pos = where + PCI_VC_RES_CAP + 12*i; u32 rcap, rctrl; u16 rstatus; int pat_pos; printf("\t\tVC%d:\t", i); if (!config_fetch(d, pos, 12)) { printf("\n"); continue; } rcap = get_conf_long(d, pos); rctrl = get_conf_long(d, pos+4); rstatus = get_conf_word(d, pos+10); pat_pos = BITS(rcap, 24, 8); printf("Caps:\tPATOffset=%02x MaxTimeSlots=%d RejSnoopTrans%c\n", pat_pos, BITS(rcap, 16, 7) + 1, FLAG(rcap, 1 << 15)); printf("\t\t\tArb:"); for (j=0; j<8; j++) if (vc_arb_selects[j][0] != '?' || rcap & (1 << j)) printf("%c%s%c", (j ? ' ' : '\t'), vc_arb_selects[j], FLAG(rcap, 1 << j)); printf("\n\t\t\tCtrl:\tEnable%c ID=%d ArbSelect=%s TC/VC=%02x\n", FLAG(rctrl, 1 << 31), BITS(rctrl, 24, 3), TABLE(vc_arb_selects, BITS(rctrl, 17, 3), buf), BITS(rctrl, 0, 8)); printf("\t\t\tStatus:\tNegoPending%c InProgress%c\n", FLAG(rstatus, 2), FLAG(rstatus, 1)); if (pat_pos) printf("\t\t\tPort Arbitration Table \n"); } } static void cap_rclink(struct device *d, int where) { u32 esd; int num_links; int i; static const char elt_types[][9] = { "Config", "Egress", "Internal" }; char buf[8]; printf("Root Complex Link\n"); if (verbose < 2) return; if (!config_fetch(d, where + 4, PCI_RCLINK_LINK1 - 4)) return; esd = get_conf_long(d, where + PCI_RCLINK_ESD); num_links = BITS(esd, 8, 8); printf("\t\tDesc:\tPortNumber=%02x ComponentID=%02x EltType=%s\n", BITS(esd, 24, 8), BITS(esd, 16, 8), TABLE(elt_types, BITS(esd, 0, 8), buf)); for (i=0; i\n"); return; } desc = get_conf_long(d, pos + PCI_RCLINK_LINK_DESC); addr_lo = get_conf_long(d, pos + PCI_RCLINK_LINK_ADDR); addr_hi = get_conf_long(d, pos + PCI_RCLINK_LINK_ADDR + 4); printf("Desc:\tTargetPort=%02x TargetComponent=%02x AssocRCRB%c LinkType=%s LinkValid%c\n", BITS(desc, 24, 8), BITS(desc, 16, 8), FLAG(desc, 4), ((desc & 2) ? "Config" : "MemMapped"), FLAG(desc, 1)); if (desc & 2) { int n = addr_lo & 7; if (!n) n = 8; printf("\t\t\tAddr:\t%02x:%02x.%d CfgSpace=%08x%08x\n", BITS(addr_lo, 20, n), BITS(addr_lo, 15, 5), BITS(addr_lo, 12, 3), addr_hi, addr_lo); } else printf("\t\t\tAddr:\t%08x%08x\n", addr_hi, addr_lo); } } static void cap_rcec(struct device *d, int where) { printf("Root Complex Event Collector Endpoint Association\n"); if (verbose < 2) return; if (!config_fetch(d, where, 12)) return; u32 hdr = get_conf_long(d, where); byte cap_ver = PCI_RCEC_EP_CAP_VER(hdr); u32 bmap = get_conf_long(d, where + PCI_RCEC_RCIEP_BMAP); printf("\t\tRCiEPBitmap: "); if (bmap) { int prevmatched=0; int adjcount=0; int prevdev=0; printf("RCiEP at Device(s):"); for (int dev=0; dev < 32; dev++) { if (BITS(bmap, dev, 1)) { if (!adjcount) printf("%s %u", (prevmatched) ? "," : "", dev); adjcount++; prevdev=dev; prevmatched=1; } else { if (adjcount > 1) printf("-%u", prevdev); adjcount=0; } } } else printf("%s", (verbose > 2) ? "00000000 [none]" : "[none]"); printf("\n"); if (cap_ver < PCI_RCEC_BUSN_REG_VER) return; u32 busn = get_conf_long(d, where + PCI_RCEC_BUSN_REG); u8 lastbusn = BITS(busn, 16, 8); u8 nextbusn = BITS(busn, 8, 8); if ((lastbusn == 0x00) && (nextbusn == 0xff)) printf("\t\tAssociatedBusNumbers: %s\n", (verbose > 2) ? "ff-00 [none]" : "[none]"); else printf("\t\tAssociatedBusNumbers: %02x-%02x\n", nextbusn, lastbusn ); } static void cap_lmr(struct device *d, int where) { printf("Lane Margining at the Receiver\n"); if (verbose < 2) return; if (!config_fetch(d, where, 8)) return; u16 port_caps = get_conf_word(d, where + PCI_LMR_CAPS); u16 port_status = get_conf_word(d, where + PCI_LMR_PORT_STS); printf("\t\tPortCap: Uses Driver%c\n", FLAG(port_caps, PCI_LMR_CAPS_DRVR)); printf("\t\tPortSta: MargReady%c MargSoftReady%c\n", FLAG(port_status, PCI_LMR_PORT_STS_READY), FLAG(port_status, PCI_LMR_PORT_STS_SOFT_READY)); } static void cxl_range(u64 base, u64 size, int n) { u32 interleave[] = { 0, 256, 4096, 512, 1024, 2048, 8192, 16384 }; const char *type[] = { "Volatile", "Non-volatile", "CDAT" }; const char *class[] = { "DRAM", "Storage", "CDAT" }; u16 w; w = (u16) size; size &= ~0x0fffffffULL; printf("\t\tRange%d: %016"PCI_U64_FMT_X"-%016"PCI_U64_FMT_X" [size=0x%"PCI_U64_FMT_X"]\n", n, base, base + size - 1, size); printf("\t\t\tValid%c Active%c Type=%s Class=%s interleave=%d timeout=%ds\n", FLAG(w, PCI_CXL_RANGE_VALID), FLAG(w, PCI_CXL_RANGE_ACTIVE), type[PCI_CXL_RANGE_TYPE(w)], class[PCI_CXL_RANGE_CLASS(w)], interleave[PCI_CXL_RANGE_INTERLEAVE(w)], 1 << (PCI_CXL_RANGE_TIMEOUT(w) * 2)); } static void dvsec_cxl_device(struct device *d, int rev, int where, int len) { u32 cache_size, cache_unit_size; u64 range_base, range_size; u16 w; /* Legacy 1.1 revs aren't handled */ if (rev == 0) return; if (rev >= 1 && len >= PCI_CXL_DEV_LEN) { w = get_conf_word(d, where + PCI_CXL_DEV_CAP); printf("\t\tCXLCap:\tCache%c IO%c Mem%c MemHWInit%c HDMCount %d Viral%c\n", FLAG(w, PCI_CXL_DEV_CAP_CACHE), FLAG(w, PCI_CXL_DEV_CAP_IO), FLAG(w, PCI_CXL_DEV_CAP_MEM), FLAG(w, PCI_CXL_DEV_CAP_MEM_HWINIT), PCI_CXL_DEV_CAP_HDM_CNT(w), FLAG(w, PCI_CXL_DEV_CAP_VIRAL)); w = get_conf_word(d, where + PCI_CXL_DEV_CTRL); printf("\t\tCXLCtl:\tCache%c IO%c Mem%c CacheSFCov %d CacheSFGran %d CacheClean%c Viral%c\n", FLAG(w, PCI_CXL_DEV_CTRL_CACHE), FLAG(w, PCI_CXL_DEV_CTRL_IO), FLAG(w, PCI_CXL_DEV_CTRL_MEM), PCI_CXL_DEV_CTRL_CACHE_SF_COV(w), PCI_CXL_DEV_CTRL_CACHE_SF_GRAN(w), FLAG(w, PCI_CXL_DEV_CTRL_CACHE_CLN), FLAG(w, PCI_CXL_DEV_CTRL_VIRAL)); w = get_conf_word(d, where + PCI_CXL_DEV_STATUS); printf("\t\tCXLSta:\tViral%c\n", FLAG(w, PCI_CXL_DEV_STATUS_VIRAL)); w = get_conf_word(d, where + PCI_CXL_DEV_CTRL2); printf("\t\tCXLCtl2:\tDisableCaching%c InitCacheWB&Inval%c InitRst%c RstMemClrEn%c", FLAG(w, PCI_CXL_DEV_CTRL2_DISABLE_CACHING), FLAG(w, PCI_CXL_DEV_CTRL2_INIT_WB_INVAL), FLAG(w, PCI_CXL_DEV_CTRL2_INIT_CXL_RST), FLAG(w, PCI_CXL_DEV_CTRL2_INIT_CXL_RST_CLR_EN)); if (rev >= 2) printf(" DesiredVolatileHDMStateAfterHotReset%c", FLAG(w, PCI_CXL_DEV_CTRL2_INIT_CXL_HDM_STATE_HOTRST)); printf("\n"); w = get_conf_word(d, where + PCI_CXL_DEV_STATUS2); printf("\t\tCXLSta2:\tResetComplete%c ResetError%c PMComplete%c\n", FLAG(w, PCI_CXL_DEV_STATUS_RC), FLAG(w,PCI_CXL_DEV_STATUS_RE), FLAG(w, PCI_CXL_DEV_STATUS_PMC)); w = get_conf_word(d, where + PCI_CXL_DEV_CAP2); printf("\t\tCXLCap2:\t"); cache_unit_size = BITS(w, 0, 4); cache_size = BITS(w, 8, 8); switch (cache_unit_size) { case PCI_CXL_DEV_CAP2_CACHE_1M: printf("Cache Size: %08x\n", cache_size * (1<<20)); break; case PCI_CXL_DEV_CAP2_CACHE_64K: printf("Cache Size: %08x\n", cache_size * (64<<10)); break; case PCI_CXL_DEV_CAP2_CACHE_UNK: printf("Cache Size Not Reported\n"); break; default: printf("Cache Size: %d of unknown unit size (%d)\n", cache_size, cache_unit_size); break; } range_size = (u64) get_conf_long(d, where + PCI_CXL_DEV_RANGE1_SIZE_HI) << 32; range_size |= get_conf_long(d, where + PCI_CXL_DEV_RANGE1_SIZE_LO); range_base = (u64) get_conf_long(d, where + PCI_CXL_DEV_RANGE1_BASE_HI) << 32; range_base |= get_conf_long(d, where + PCI_CXL_DEV_RANGE1_BASE_LO); cxl_range(range_base, range_size, 1); range_size = (u64) get_conf_long(d, where + PCI_CXL_DEV_RANGE2_SIZE_HI) << 32; range_size |= get_conf_long(d, where + PCI_CXL_DEV_RANGE2_SIZE_LO); range_base = (u64) get_conf_long(d, where + PCI_CXL_DEV_RANGE2_BASE_HI) << 32; range_base |= get_conf_long(d, where + PCI_CXL_DEV_RANGE2_BASE_LO); cxl_range(range_base, range_size, 2); } if (rev >= 2 && len >= PCI_CXL_DEV_LEN_REV2) { w = get_conf_word(d, where + PCI_CXL_DEV_CAP3); printf("\t\tCXLCap3:\tDefaultVolatile HDM State After:\tColdReset%c WarmReset%c HotReset%c HotResetConfigurability%c\n", FLAG(w, PCI_CXL_DEV_CAP3_HDM_STATE_RST_COLD), FLAG(w, PCI_CXL_DEV_CAP3_HDM_STATE_RST_WARM), FLAG(w, PCI_CXL_DEV_CAP3_HDM_STATE_RST_HOT), FLAG(w, PCI_CXL_DEV_CAP3_HDM_STATE_RST_HOT_CFG)); } // Unparsed data if (len > PCI_CXL_DEV_LEN_REV2) printf("\t\t\n"); } static void dvsec_cxl_port(struct device *d, int where, int len) { u16 w, m1, m2; u8 b1, b2; if (len < PCI_CXL_PORT_EXT_LEN) return; w = get_conf_word(d, where + PCI_CXL_PORT_EXT_STATUS); printf("\t\tCXLPortSta:\tPMComplete%c\n", FLAG(w, PCI_CXL_PORT_EXT_STATUS)); w = get_conf_word(d, where + PCI_CXL_PORT_CTRL); printf("\t\tCXLPortCtl:\tUnmaskSBR%c UnmaskLinkDisable%c AltMem%c AltBME%c ViralEnable%c\n", FLAG(w, PCI_CXL_PORT_UNMASK_SBR), FLAG(w, PCI_CXL_PORT_UNMASK_LINK), FLAG(w, PCI_CXL_PORT_ALT_MEMORY), FLAG(w, PCI_CXL_PORT_ALT_BME), FLAG(w, PCI_CXL_PORT_VIRAL_EN)); b1 = get_conf_byte(d, where + PCI_CXL_PORT_ALT_BUS_BASE); b2 = get_conf_byte(d, where + PCI_CXL_PORT_ALT_BUS_LIMIT); printf("\t\tAlternateBus:\t%02x-%02x\n", b1, b2); m1 = get_conf_word(d, where + PCI_CXL_PORT_ALT_MEM_BASE); m2 = get_conf_word(d, where + PCI_CXL_PORT_ALT_MEM_LIMIT); printf("\t\tAlternateBus:\t%04x-%04x\n", m1, m2); } static void dvsec_cxl_register_locator(struct device *d, int where, int len) { static const char * const id_names[] = { "empty", "component registers", "BAR virtualization", "CXL device registers", "CPMU registers", }; for (int i=0; ; i++) { int pos = where + PCI_CXL_RL_BLOCK1_LO + 8*i; if (pos + 7 >= where + len) break; u32 lo = get_conf_long(d, pos); u32 hi = get_conf_long(d, pos + 4); unsigned int bir = BITS(lo, 0, 3); unsigned int block_id = BITS(lo, 8, 8); u64 base = (BITS(lo, 16, 16) << 16) | ((u64) hi << 32); if (!block_id) continue; const char *id_name; if (block_id < sizeof(id_names) / sizeof(*id_names)) id_name = id_names[block_id]; else if (block_id == 0xff) id_name = "vendor-specific"; else id_name = ""; printf("\t\tBlock%d: BIR: bar%d, ID: %s, offset: %016" PCI_U64_FMT_X "\n", i + 1, bir, id_name, base); } } static void dvsec_cxl_gpf_device(struct device *d, int where) { u32 l; u16 w, duration; u8 time_base, time_scale; w = get_conf_word(d, where + PCI_CXL_GPF_DEV_PHASE2_DUR); time_base = BITS(w, 0, 4); time_scale = BITS(w, 8, 4); switch (time_scale) { case PCI_CXL_GPF_DEV_100US: case PCI_CXL_GPF_DEV_100MS: duration = time_base * 100; break; case PCI_CXL_GPF_DEV_10US: case PCI_CXL_GPF_DEV_10MS: case PCI_CXL_GPF_DEV_10S: duration = time_base * 10; break; case PCI_CXL_GPF_DEV_1US: case PCI_CXL_GPF_DEV_1MS: case PCI_CXL_GPF_DEV_1S: duration = time_base; break; default: /* Reserved */ printf("\t\tReserved time scale encoding %x\n", time_scale); duration = time_base; } printf("\t\tGPF Phase 2 Duration: %u%s\n", duration, (time_scale < PCI_CXL_GPF_DEV_1MS) ? "us": (time_scale < PCI_CXL_GPF_DEV_1S) ? "ms" : (time_scale == PCI_CXL_GPF_DEV_1S) ? "s" : ""); l = get_conf_long(d, where + PCI_CXL_GPF_DEV_PHASE2_POW); printf("\t\tGPF Phase 2 Power: %umW\n", (unsigned int)l); } static void dvsec_cxl_gpf_port(struct device *d, int where) { u16 w, timeout; u8 time_base, time_scale; w = get_conf_word(d, where + PCI_CXL_GPF_PORT_PHASE1_CTRL); time_base = BITS(w, 0, 4); time_scale = BITS(w, 8, 4); switch (time_scale) { case PCI_CXL_GPF_PORT_100US: case PCI_CXL_GPF_PORT_100MS: timeout = time_base * 100; break; case PCI_CXL_GPF_PORT_10US: case PCI_CXL_GPF_PORT_10MS: case PCI_CXL_GPF_PORT_10S: timeout = time_base * 10; break; case PCI_CXL_GPF_PORT_1US: case PCI_CXL_GPF_PORT_1MS: case PCI_CXL_GPF_PORT_1S: timeout = time_base; break; default: /* Reserved */ printf("\t\tReserved time scale encoding %x\n", time_scale); timeout = time_base; } printf("\t\tGPF Phase 1 Timeout: %d%s\n", timeout, (time_scale < PCI_CXL_GPF_PORT_1MS) ? "us": (time_scale < PCI_CXL_GPF_PORT_1S) ? "ms" : (time_scale == PCI_CXL_GPF_PORT_1S) ? "s" : ""); w = get_conf_word(d, where + PCI_CXL_GPF_PORT_PHASE2_CTRL); time_base = BITS(w, 0, 4); time_scale = BITS(w, 8, 4); switch (time_scale) { case PCI_CXL_GPF_PORT_100US: case PCI_CXL_GPF_PORT_100MS: timeout = time_base * 100; break; case PCI_CXL_GPF_PORT_10US: case PCI_CXL_GPF_PORT_10MS: case PCI_CXL_GPF_PORT_10S: timeout = time_base * 10; break; case PCI_CXL_GPF_PORT_1US: case PCI_CXL_GPF_PORT_1MS: case PCI_CXL_GPF_PORT_1S: timeout = time_base; break; default: /* Reserved */ printf("\t\tReserved time scale encoding %x\n", time_scale); timeout = time_base; } printf("\t\tGPF Phase 2 Timeout: %d%s\n", timeout, (time_scale < PCI_CXL_GPF_PORT_1MS) ? "us": (time_scale < PCI_CXL_GPF_PORT_1S) ? "ms" : (time_scale == PCI_CXL_GPF_PORT_1S) ? "s" : ""); } static void dvsec_cxl_flex_bus(struct device *d, int where, int rev, int len) { u16 w; u32 l, data; // Sanity check: Does the length correspond to its revision? switch (rev) { case 0: if (len != PCI_CXL_FB_MOD_TS_DATA) printf("\t\t\n", rev); break; case 1: if (len != PCI_CXL_FB_PORT_CAP2) printf("\t\t\n", rev); break; case 2: if (len != PCI_CXL_FB_NEXT_UNSUPPORTED) printf("\t\t\n", rev); break; default: break; } // From Rev 0 w = get_conf_word(d, where + PCI_CXL_FB_PORT_CAP); printf("\t\tFBCap:\tCache%c IO%c Mem%c 68BFlit%c MltLogDev%c", FLAG(w, PCI_CXL_FB_CAP_CACHE), FLAG(w, PCI_CXL_FB_CAP_IO), FLAG(w, PCI_CXL_FB_CAP_MEM), FLAG(w, PCI_CXL_FB_CAP_68B_FLIT), FLAG(w, PCI_CXL_FB_CAP_MULT_LOG_DEV)); if (rev > 1) printf(" 256BFlit%c PBRFlit%c", FLAG(w, PCI_CXL_FB_CAP_256B_FLIT), FLAG(w, PCI_CXL_FB_CAP_PBR_FLIT)); w = get_conf_word(d, where + PCI_CXL_FB_PORT_CTRL); printf("\n\t\tFBCtl:\tCache%c IO%c Mem%c SynHdrByp%c DrftBuf%c 68BFlit%c MltLogDev%c RCD%c Retimer1%c Retimer2%c", FLAG(w, PCI_CXL_FB_CTRL_CACHE), FLAG(w, PCI_CXL_FB_CTRL_IO), FLAG(w, PCI_CXL_FB_CTRL_MEM), FLAG(w, PCI_CXL_FB_CTRL_SYNC_HDR_BYP), FLAG(w, PCI_CXL_FB_CTRL_DRFT_BUF), FLAG(w, PCI_CXL_FB_CTRL_68B_FLIT), FLAG(w, PCI_CXL_FB_CTRL_MULT_LOG_DEV), FLAG(w, PCI_CXL_FB_CTRL_RCD), FLAG(w, PCI_CXL_FB_CTRL_RETIMER1), FLAG(w, PCI_CXL_FB_CTRL_RETIMER2)); if (rev > 1) printf(" 256BFlit%c PBRFlit%c", FLAG(w, PCI_CXL_FB_CTRL_256B_FLIT), FLAG(w, PCI_CXL_FB_CTRL_PBR_FLIT)); w = get_conf_word(d, where + PCI_CXL_FB_PORT_STATUS); printf("\n\t\tFBSta:\tCache%c IO%c Mem%c SynHdrByp%c DrftBuf%c 68BFlit%c MltLogDev%c", FLAG(w, PCI_CXL_FB_STAT_CACHE), FLAG(w, PCI_CXL_FB_STAT_IO), FLAG(w, PCI_CXL_FB_STAT_MEM), FLAG(w, PCI_CXL_FB_STAT_SYNC_HDR_BYP), FLAG(w, PCI_CXL_FB_STAT_DRFT_BUF), FLAG(w, PCI_CXL_FB_STAT_68B_FLIT), FLAG(w, PCI_CXL_FB_STAT_MULT_LOG_DEV)); if (rev > 1) printf(" 256BFlit%c PBRFlit%c", FLAG(w, PCI_CXL_FB_STAT_256B_FLIT), FLAG(w, PCI_CXL_FB_STAT_PBR_FLIT)); printf("\n"); // From Rev 1 if (rev >= 1) { l = get_conf_long(d, where + PCI_CXL_FB_MOD_TS_DATA); data = BITS(l, 0, 24); printf("\t\tFBModTS:\tReceived FB Data: %06x\n", (unsigned int)data); } // From Rev 2 if (rev >= 2) { u8 nop; l = get_conf_long(d, where + PCI_CXL_FB_PORT_CAP2); printf("\t\tFBCap2:\tNOPHint%c\n", FLAG(l, PCI_CXL_FB_CAP2_NOP_HINT)); l = get_conf_long(d, where + PCI_CXL_FB_PORT_CTRL2); printf("\t\tFBCtl2:\tNOPHint%c\n", FLAG(l, PCI_CXL_FB_CTRL2_NOP_HINT)); l = get_conf_long(d, where + PCI_CXL_FB_PORT_STATUS2); nop = BITS(l, 0, 2); printf("\t\tFBSta2:\tNOPHintInfo: %x\n", nop); } // Unparsed data if (len > PCI_CXL_FB_LEN) printf("\t\t\n"); } static void dvsec_cxl_mld(struct device *d, int where) { u16 w; w = get_conf_word(d, where + PCI_CXL_MLD_NUM_LD); /* Encodings greater than 16 are reserved */ if (w && w <= PCI_CXL_MLD_MAX_LD) printf("\t\tNumLogDevs: %d\n", w); } static void dvsec_cxl_function_map(struct device *d, int where) { printf("\t\tFuncMap 0: %08x\n", (unsigned int)(get_conf_word(d, where + PCI_CXL_FUN_MAP_REG_0))); printf("\t\tFuncMap 1: %08x\n", (unsigned int)(get_conf_word(d, where + PCI_CXL_FUN_MAP_REG_1))); printf("\t\tFuncMap 2: %08x\n", (unsigned int)(get_conf_word(d, where + PCI_CXL_FUN_MAP_REG_2))); printf("\t\tFuncMap 3: %08x\n", (unsigned int)(get_conf_word(d, where + PCI_CXL_FUN_MAP_REG_3))); printf("\t\tFuncMap 4: %08x\n", (unsigned int)(get_conf_word(d, where + PCI_CXL_FUN_MAP_REG_4))); printf("\t\tFuncMap 5: %08x\n", (unsigned int)(get_conf_word(d, where + PCI_CXL_FUN_MAP_REG_5))); printf("\t\tFuncMap 6: %08x\n", (unsigned int)(get_conf_word(d, where + PCI_CXL_FUN_MAP_REG_6))); printf("\t\tFuncMap 7: %08x\n", (unsigned int)(get_conf_word(d, where + PCI_CXL_FUN_MAP_REG_7))); } static void cap_dvsec_cxl(struct device *d, int id, int rev, int where, int len) { printf(": CXL\n"); if (verbose < 2) return; if (!config_fetch(d, where, len)) return; switch (id) { case 0: printf("\t\tPCIe DVSEC for CXL Devices\n"); dvsec_cxl_device(d, rev, where, len); break; case 2: printf("\t\tNon-CXL Function Map DVSEC\n"); dvsec_cxl_function_map(d, where); break; case 3: printf("\t\tCXL Extensions DVSEC for Ports\n"); dvsec_cxl_port(d, where, len); break; case 4: printf("\t\tGPF DVSEC for CXL Ports\n"); dvsec_cxl_gpf_port(d, where); break; case 5: printf("\t\tGPF DVSEC for CXL Devices\n"); dvsec_cxl_gpf_device(d, where); break; case 7: printf("\t\tPCIe DVSEC for Flex Bus Port\n"); dvsec_cxl_flex_bus(d, where, rev, len); break; case 8: printf("\t\tRegister Locator DVSEC\n"); dvsec_cxl_register_locator(d, where, len); break; case 9: printf("\t\tMLD DVSEC\n"); dvsec_cxl_mld(d, where); break; case 0xa: printf("\t\tPCIe DVSEC for Test Capability \n"); break; default: printf("\t\tUnknown ID %04x\n", id); } } static void cap_dvsec(struct device *d, int where) { printf("Designated Vendor-Specific: "); if (!config_fetch(d, where + PCI_DVSEC_HEADER1, 8)) { printf("\n"); return; } u32 hdr = get_conf_long(d, where + PCI_DVSEC_HEADER1); u16 vendor = BITS(hdr, 0, 16); byte rev = BITS(hdr, 16, 4); u16 len = BITS(hdr, 20, 12); u16 id = get_conf_long(d, where + PCI_DVSEC_HEADER2); printf("Vendor=%04x ID=%04x Rev=%d Len=%d", vendor, id, rev, len); if (vendor == PCI_DVSEC_VENDOR_ID_CXL && len >= 16) cap_dvsec_cxl(d, id, rev, where, len); else printf(" \n"); } static void cap_evendor(struct device *d, int where) { u32 hdr; printf("Vendor Specific Information: "); if (!config_fetch(d, where + PCI_EVNDR_HEADER, 4)) { printf("\n"); return; } hdr = get_conf_long(d, where + PCI_EVNDR_HEADER); printf("ID=%04x Rev=%d Len=%03x \n", BITS(hdr, 0, 16), BITS(hdr, 16, 4), BITS(hdr, 20, 12)); } static int l1pm_calc_pwron(int scale, int value) { switch (scale) { case 0: return 2 * value; case 1: return 10 * value; case 2: return 100 * value; } return -1; } static void cap_l1pm(struct device *d, int where) { u32 l1_cap, val, scale; int time; printf("L1 PM Substates\n"); if (verbose < 2) return; if (!config_fetch(d, where + PCI_L1PM_SUBSTAT_CAP, 12)) { printf("\t\t\n"); return; } l1_cap = get_conf_long(d, where + PCI_L1PM_SUBSTAT_CAP); printf("\t\tL1SubCap: "); printf("PCI-PM_L1.2%c PCI-PM_L1.1%c ASPM_L1.2%c ASPM_L1.1%c L1_PM_Substates%c\n", FLAG(l1_cap, PCI_L1PM_SUBSTAT_CAP_PM_L12), FLAG(l1_cap, PCI_L1PM_SUBSTAT_CAP_PM_L11), FLAG(l1_cap, PCI_L1PM_SUBSTAT_CAP_ASPM_L12), FLAG(l1_cap, PCI_L1PM_SUBSTAT_CAP_ASPM_L11), FLAG(l1_cap, PCI_L1PM_SUBSTAT_CAP_L1PM_SUPP)); if (l1_cap & PCI_L1PM_SUBSTAT_CAP_PM_L12 || l1_cap & PCI_L1PM_SUBSTAT_CAP_ASPM_L12) { printf("\t\t\t PortCommonModeRestoreTime=%dus ", BITS(l1_cap, 8, 8)); time = l1pm_calc_pwron(BITS(l1_cap, 16, 2), BITS(l1_cap, 19, 5)); if (time != -1) printf("PortTPowerOnTime=%dus\n", time); else printf("PortTPowerOnTime=\n"); } val = get_conf_long(d, where + PCI_L1PM_SUBSTAT_CTL1); printf("\t\tL1SubCtl1: PCI-PM_L1.2%c PCI-PM_L1.1%c ASPM_L1.2%c ASPM_L1.1%c\n", FLAG(val, PCI_L1PM_SUBSTAT_CTL1_PM_L12), FLAG(val, PCI_L1PM_SUBSTAT_CTL1_PM_L11), FLAG(val, PCI_L1PM_SUBSTAT_CTL1_ASPM_L12), FLAG(val, PCI_L1PM_SUBSTAT_CTL1_ASPM_L11)); if (l1_cap & PCI_L1PM_SUBSTAT_CAP_PM_L12 || l1_cap & PCI_L1PM_SUBSTAT_CAP_ASPM_L12) { printf("\t\t\t T_CommonMode=%dus", BITS(val, 8, 8)); if (l1_cap & PCI_L1PM_SUBSTAT_CAP_ASPM_L12) { scale = BITS(val, 29, 3); if (scale > 5) printf(" LTR1.2_Threshold="); else printf(" LTR1.2_Threshold=%" PCI_U64_FMT_U "ns", BITS(val, 16, 10) * (u64) cap_ltr_scale(scale)); } printf("\n"); } val = get_conf_long(d, where + PCI_L1PM_SUBSTAT_CTL2); printf("\t\tL1SubCtl2:"); if (l1_cap & PCI_L1PM_SUBSTAT_CAP_PM_L12 || l1_cap & PCI_L1PM_SUBSTAT_CAP_ASPM_L12) { time = l1pm_calc_pwron(BITS(val, 0, 2), BITS(val, 3, 5)); if (time != -1) printf(" T_PwrOn=%dus", time); else printf(" T_PwrOn="); } printf("\n"); } static void cap_ptm(struct device *d, int where) { u32 buff; u16 clock; printf("Precision Time Measurement\n"); if (verbose < 2) return; if (!config_fetch(d, where + 4, 8)) { printf("\t\t\n"); return; } buff = get_conf_long(d, where + 4); printf("\t\tPTMCap: "); printf("Requester%c Responder%c Root%c\n", FLAG(buff, 0x1), FLAG(buff, 0x2), FLAG(buff, 0x4)); clock = BITS(buff, 8, 8); printf("\t\tPTMClockGranularity: "); switch (clock) { case 0x00: printf("Unimplemented\n"); break; case 0xff: printf("Greater than 254ns\n"); break; default: printf("%huns\n", clock); } buff = get_conf_long(d, where + 8); printf("\t\tPTMControl: "); printf("Enabled%c RootSelected%c\n", FLAG(buff, 0x1), FLAG(buff, 0x2)); clock = BITS(buff, 8, 8); printf("\t\tPTMEffectiveGranularity: "); switch (clock) { case 0x00: printf("Unknown\n"); break; case 0xff: printf("Greater than 254ns\n"); break; default: printf("%huns\n", clock); } } static void print_rebar_range_size(int ld2_size) { // This function prints the input as a power-of-2 size value // It is biased with 1MB = 0, ... // Maximum resizable BAR value supported is 2^63 bytes = 43 // for the extended resizable BAR capability definition // (otherwise it would stop at 2^28) if (ld2_size >= 0 && ld2_size < 10) printf(" %dMB", (1 << ld2_size)); else if (ld2_size >= 10 && ld2_size < 20) printf(" %dGB", (1 << (ld2_size-10))); else if (ld2_size >= 20 && ld2_size < 30) printf(" %dTB", (1 << (ld2_size-20))); else if (ld2_size >= 30 && ld2_size < 40) printf(" %dPB", (1 << (ld2_size-30))); else if (ld2_size >= 40 && ld2_size < 44) printf(" %dEB", (1 << (ld2_size-40))); else printf(" "); } static void cap_rebar(struct device *d, int where, int virtual) { u32 sizes_buffer, control_buffer, ext_sizes, current_size; u16 bar_index, barcount, i; // If the structure exists, at least one bar is defined u16 num_bars = 1; printf("%s Resizable BAR\n", (virtual) ? "Virtual" : "Physical"); if (verbose < 2) return; // Go through all defined BAR definitions of the caps, at minimum 1 // (loop also terminates if num_bars read from caps is > 6) for (barcount = 0; barcount < num_bars; barcount++) { where += 4; // Get the next BAR configuration if (!config_fetch(d, where, 8)) { printf("\t\t\n"); return; } sizes_buffer = get_conf_long(d, where) >> 4; where += 4; control_buffer = get_conf_long(d, where); bar_index = BITS(control_buffer, 0, 3); current_size = BITS(control_buffer, 8, 6); ext_sizes = BITS(control_buffer, 16, 16); if (barcount == 0) { // Only index 0 controlreg has the num_bar count definition num_bars = BITS(control_buffer, 5, 3); if (num_bars < 1 || num_bars > 6) { printf("\t\t\n", num_bars); break; } } // Resizable BAR list entry have an arbitrary index and current size printf("\t\tBAR %d: current size:", bar_index); print_rebar_range_size(current_size); if (sizes_buffer || ext_sizes) { printf(", supported:"); for (i=0; i<28; i++) if (sizes_buffer & (1U << i)) print_rebar_range_size(i); for (i=0; i<16; i++) if (ext_sizes & (1U << i)) print_rebar_range_size(i + 28); } printf("\n"); } } static void cap_doe(struct device *d, int where) { u32 l; printf("Data Object Exchange\n"); if (verbose < 2) return; if (!config_fetch(d, where + PCI_DOE_CAP, 0x14)) { printf("\t\t\n"); return; } l = get_conf_long(d, where + PCI_DOE_CAP); printf("\t\tDOECap: IntSup%c\n", FLAG(l, PCI_DOE_CAP_INT_SUPP)); if (l & PCI_DOE_CAP_INT_SUPP) printf("\t\t\tIntMsgNum %d\n", PCI_DOE_CAP_INT_MSG(l)); l = get_conf_long(d, where + PCI_DOE_CTL); printf("\t\tDOECtl: IntEn%c\n", FLAG(l, PCI_DOE_CTL_INT)); l = get_conf_long(d, where + PCI_DOE_STS); printf("\t\tDOESta: Busy%c IntSta%c Error%c ObjectReady%c\n", FLAG(l, PCI_DOE_STS_BUSY), FLAG(l, PCI_DOE_STS_INT), FLAG(l, PCI_DOE_STS_ERROR), FLAG(l, PCI_DOE_STS_OBJECT_READY)); } static const char *offstr(char *buf, u32 off) { if (verbose < 3) return ""; sprintf(buf, "[%x]", off); return buf; } static const char *ide_alg(char *buf, size_t len, u32 l) { const char *algo[] = { "AES-GCM-256-96b" }; // AES-GCM 256 key size, 96b MAC if (l == 0) snprintf(buf, len, "%s", algo[l]); else snprintf(buf, len, "%s", "reserved"); return buf; } static void cap_ide(struct device *d, int where) { const char *hdr_enc_mode[] = { "no", "17:2", "25:2", "33:2", "41:2" }; const char *stream_state[] = { "insecure", "reserved", "secure" }; const char *aggr[] = { "-", "=2", "=4", "=8" }; u32 l, l2, linknum = 0, selnum = 0, addrnum, off, i, j; char buf1[16], buf2[16], offs[16]; printf("Integrity & Data Encryption\n"); if (verbose < 2) return; if (!config_fetch(d, where + PCI_IDE_CAP, 8)) { printf("\t\t\n"); return; } l = get_conf_long(d, where + PCI_IDE_CAP); if (l & PCI_IDE_CAP_LINK_IDE_SUPP) linknum = PCI_IDE_CAP_LINK_TC_NUM(l) + 1; if (l & PCI_IDE_CAP_SELECTIVE_IDE_SUPP) selnum = PCI_IDE_CAP_SELECTIVE_STREAMS_NUM(l) + 1; printf("\t\tIDECap: Lnk=%d Sel=%d FlowThru%c PartHdr%c Aggr%c PCPC%c IDE_KM%c Alg='%s' TCs=%d TeeLim%c\n", linknum, selnum, FLAG(l, PCI_IDE_CAP_FLOWTHROUGH_IDE_SUPP), FLAG(l, PCI_IDE_CAP_PARTIAL_HEADER_ENC_SUPP), FLAG(l, PCI_IDE_CAP_AGGREGATION_SUPP), FLAG(l, PCI_IDE_CAP_PCRC_SUPP), FLAG(l, PCI_IDE_CAP_IDE_KM_SUPP), ide_alg(buf2, sizeof(buf2), PCI_IDE_CAP_ALG(l)), PCI_IDE_CAP_LINK_TC_NUM(l) + 1, FLAG(l, PCI_IDE_CAP_TEE_LIMITED_SUPP) ); l = get_conf_long(d, where + PCI_IDE_CTL); printf("\t\tIDECtl: FTEn%c\n", FLAG(l, PCI_IDE_CTL_FLOWTHROUGH_IDE)); // The rest of the capability is variable length arrays off = where + PCI_IDE_LINK_STREAM; // Link IDE Register Block repeated 0 to 8 times if (linknum) { if (!config_fetch(d, off, 8 * linknum)) { printf("\t\t\n"); return; } for (i = 0; i < linknum; ++i) { // Link IDE Stream Control Register l = get_conf_long(d, off); printf("\t\t%sLinkIDE#%d Ctl: En%c NPR%s PR%s CPL%s PCRC%c HdrEnc=%s Alg='%s' TC%d ID%d\n", offstr(offs, off), i, FLAG(l, PCI_IDE_LINK_CTL_EN), aggr[PCI_IDE_LINK_CTL_TX_AGGR_NPR(l)], aggr[PCI_IDE_LINK_CTL_TX_AGGR_PR(l)], aggr[PCI_IDE_LINK_CTL_TX_AGGR_CPL(l)], FLAG(l, PCI_IDE_LINK_CTL_EN), TABLE(hdr_enc_mode, PCI_IDE_LINK_CTL_PART_ENC(l), buf1), ide_alg(buf2, sizeof(buf2), PCI_IDE_LINK_CTL_ALG(l)), PCI_IDE_LINK_CTL_TC(l), PCI_IDE_LINK_CTL_ID(l) ); off += 4; /* Link IDE Stream Status Register */ l = get_conf_long(d, off); printf("\t\t%sLinkIDE#%d Sta: Status=%s RecvChkFail%c\n", offstr(offs, off), i, TABLE(stream_state, PCI_IDE_LINK_STS_STATUS(l), buf1), FLAG(l, PCI_IDE_LINK_STS_RECVD_INTEGRITY_CHECK)); off += 4; } } for (i = 0; i < selnum; ++i) { // Fetching Selective IDE Stream Capability/Control/Status/RID1/RID2 if (!config_fetch(d, off, 20)) { printf("\t\t\n"); return; } // Selective IDE Stream Capability Register l = get_conf_long(d, off); printf("\t\t%sSelectiveIDE#%d Cap: RID#=%d\n", offstr(offs, off), i, PCI_IDE_SEL_CAP_BLOCKS_NUM(l)); off += 4; addrnum = PCI_IDE_SEL_CAP_BLOCKS_NUM(l); // Selective IDE Stream Control Register l = get_conf_long(d, off); printf("\t\t%sSelectiveIDE#%d Ctl: En%c NPR%s PR%s CPL%s PCRC%c CFG%c HdrEnc=%s Alg='%s' TC%d ID%d%s\n", offstr(offs, off), i, FLAG(l, PCI_IDE_SEL_CTL_EN), aggr[PCI_IDE_SEL_CTL_TX_AGGR_NPR(l)], aggr[PCI_IDE_SEL_CTL_TX_AGGR_PR(l)], aggr[PCI_IDE_SEL_CTL_TX_AGGR_CPL(l)], FLAG(l, PCI_IDE_SEL_CTL_PCRC_EN), FLAG(l, PCI_IDE_SEL_CTL_CFG_EN), TABLE(hdr_enc_mode, PCI_IDE_SEL_CTL_PART_ENC(l), buf1), ide_alg(buf2, sizeof(buf2), PCI_IDE_SEL_CTL_ALG(l)), PCI_IDE_SEL_CTL_TC(l), PCI_IDE_SEL_CTL_ID(l), (l & PCI_IDE_SEL_CTL_DEFAULT) ? " Default" : "" ); off += 4; // Selective IDE Stream Status Register l = get_conf_long(d, off); printf("\t\t%sSelectiveIDE#%d Sta: %s RecvChkFail%c\n", offstr(offs, off), i , TABLE(stream_state, PCI_IDE_SEL_STS_STATUS(l), buf1), FLAG(l, PCI_IDE_SEL_STS_RECVD_INTEGRITY_CHECK)); off += 4; // IDE RID Association Registers l = get_conf_long(d, off); l2 = get_conf_long(d, off + 4); printf("\t\t%sSelectiveIDE#%d RID: Valid%c Base=%x Limit=%x SegBase=%x\n", offstr(offs, off), i, FLAG(l2, PCI_IDE_SEL_RID_2_VALID), PCI_IDE_SEL_RID_2_BASE(l2), PCI_IDE_SEL_RID_1_LIMIT(l), PCI_IDE_SEL_RID_2_SEG_BASE(l2)); off += 8; if (!config_fetch(d, off, addrnum * 12)) { printf("\t\t\n"); return; } // IDE Address Association Registers for (j = 0; j < addrnum; ++j) { u64 limit, base; l = get_conf_long(d, off); limit = get_conf_long(d, off + 4); limit <<= 32; limit |= (PCI_IDE_SEL_ADDR_1_LIMIT_LOW(l) << 20) | 0xFFFFF; base = get_conf_long(d, off + 8); base <<= 32; base |= PCI_IDE_SEL_ADDR_1_BASE_LOW(l) << 20; printf("\t\t%sSelectiveIDE#%d RID#%d: Valid%c Base=%lx Limit=%lx\n", offstr(offs, off), i, j, FLAG(l, PCI_IDE_SEL_ADDR_1_VALID), base, limit); off += 12; } } } void show_ext_caps(struct device *d, int type) { int where = 0x100; char been_there[0x1000]; memset(been_there, 0, 0x1000); do { u32 header; int id, version; if (!config_fetch(d, where, 4)) break; header = get_conf_long(d, where); if (!header || header == 0xffffffff) break; id = header & 0xffff; version = (header >> 16) & 0xf; printf("\tCapabilities: [%03x", where); if (verbose > 1) printf(" v%d", version); printf("] "); if (been_there[where]++) { printf("\n"); break; } switch (id) { case PCI_EXT_CAP_ID_NULL: printf("Null\n"); break; case PCI_EXT_CAP_ID_AER: cap_aer(d, where, type); break; case PCI_EXT_CAP_ID_DPC: cap_dpc(d, where); break; case PCI_EXT_CAP_ID_VC: case PCI_EXT_CAP_ID_VC2: cap_vc(d, where); break; case PCI_EXT_CAP_ID_DSN: cap_dsn(d, where); break; case PCI_EXT_CAP_ID_PB: printf("Power Budgeting \n"); break; case PCI_EXT_CAP_ID_RCLINK: cap_rclink(d, where); break; case PCI_EXT_CAP_ID_RCILINK: printf("Root Complex Internal Link \n"); break; case PCI_EXT_CAP_ID_RCEC: cap_rcec(d, where); break; case PCI_EXT_CAP_ID_MFVC: printf("Multi-Function Virtual Channel \n"); break; case PCI_EXT_CAP_ID_RCRB: printf("Root Complex Register Block \n"); break; case PCI_EXT_CAP_ID_VNDR: cap_evendor(d, where); break; case PCI_EXT_CAP_ID_ACS: cap_acs(d, where); break; case PCI_EXT_CAP_ID_ARI: cap_ari(d, where); break; case PCI_EXT_CAP_ID_ATS: cap_ats(d, where); break; case PCI_EXT_CAP_ID_SRIOV: cap_sriov(d, where); break; case PCI_EXT_CAP_ID_MRIOV: printf("Multi-Root I/O Virtualization \n"); break; case PCI_EXT_CAP_ID_MCAST: cap_multicast(d, where, type); break; case PCI_EXT_CAP_ID_PRI: cap_pri(d, where); break; case PCI_EXT_CAP_ID_REBAR: cap_rebar(d, where, 0); break; case PCI_EXT_CAP_ID_DPA: printf("Dynamic Power Allocation \n"); break; case PCI_EXT_CAP_ID_TPH: cap_tph(d, where); break; case PCI_EXT_CAP_ID_LTR: cap_ltr(d, where); break; case PCI_EXT_CAP_ID_SECPCI: cap_sec(d, where); break; case PCI_EXT_CAP_ID_PMUX: printf("Protocol Multiplexing \n"); break; case PCI_EXT_CAP_ID_PASID: cap_pasid(d, where); break; case PCI_EXT_CAP_ID_LNR: printf("LN Requester \n"); break; case PCI_EXT_CAP_ID_L1PM: cap_l1pm(d, where); break; case PCI_EXT_CAP_ID_PTM: cap_ptm(d, where); break; case PCI_EXT_CAP_ID_M_PCIE: printf("PCI Express over M_PHY \n"); break; case PCI_EXT_CAP_ID_FRS: printf("FRS Queueing \n"); break; case PCI_EXT_CAP_ID_RTR: printf("Readiness Time Reporting \n"); break; case PCI_EXT_CAP_ID_DVSEC: cap_dvsec(d, where); break; case PCI_EXT_CAP_ID_VF_REBAR: cap_rebar(d, where, 1); break; case PCI_EXT_CAP_ID_DLNK: printf("Data Link Feature \n"); break; case PCI_EXT_CAP_ID_16GT: printf("Physical Layer 16.0 GT/s \n"); break; case PCI_EXT_CAP_ID_LMR: cap_lmr(d, where); break; case PCI_EXT_CAP_ID_HIER_ID: printf("Hierarchy ID \n"); break; case PCI_EXT_CAP_ID_NPEM: printf("Native PCIe Enclosure Management \n"); break; case PCI_EXT_CAP_ID_32GT: printf("Physical Layer 32.0 GT/s \n"); break; case PCI_EXT_CAP_ID_DOE: cap_doe(d, where); break; case PCI_EXT_CAP_ID_IDE: cap_ide(d, where); break; default: printf("Extended Capability ID %#02x\n", id); break; } where = (header >> 20) & ~3; } while (where); } pciutils-3.13.0/lspci.h0000644000175000001440000000530214564233322013246 0ustar mjusers/* * The PCI Utilities -- List All PCI Devices * * Copyright (c) 1997--2018 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+ * * SPDX-License-Identifier: GPL-2.0-or-later */ #define PCIUTILS_LSPCI #include "pciutils.h" /* * If we aren't being compiled by GCC, use xmalloc() instead of alloca(). * This increases our memory footprint, but only slightly since we don't * use alloca() much. */ #if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) || defined (__DragonFly__) || defined (__DJGPP__) /* alloca() is defined in stdlib.h */ #elif defined(__GNUC__) && !defined(PCI_OS_WINDOWS) #include #else #undef alloca #define alloca xmalloc #endif /*** Options ***/ extern int verbose; extern struct pci_filter filter; extern char *opt_pcimap; /*** PCI devices and access to their config space ***/ struct device { struct device *next; struct pci_dev *dev; /* Bus topology calculated by grow_tree() */ struct device *bus_next; struct bus *parent_bus; struct bridge *bridge; /* Cache */ int no_config_access; unsigned int config_cached, config_bufsize; byte *config; /* Cached configuration space data */ byte *present; /* Maps which configuration bytes are present */ }; extern struct device *first_dev; extern struct pci_access *pacc; struct device *scan_device(struct pci_dev *p); void show_device(struct device *d); int config_fetch(struct device *d, unsigned int pos, unsigned int len); u32 get_conf_long(struct device *d, unsigned int pos); word get_conf_word(struct device *d, unsigned int pos); byte get_conf_byte(struct device *d, unsigned int pos); /* ls-vpd.c */ void cap_vpd(struct device *d); /* ls-caps.c */ void show_caps(struct device *d, int where); /* ls-ecaps.c */ void show_ext_caps(struct device *d, int type); /* ls-caps-vendor.c */ void show_vendor_caps(struct device *d, int where, int cap); /* ls-kernel.c */ void show_kernel_machine(struct device *d UNUSED); void show_kernel(struct device *d UNUSED); void show_kernel_cleanup(void); /* ls-tree.c */ struct bridge { struct bridge *chain; /* Single-linked list of bridges */ struct bridge *next, *prev, *child; /* Tree of bridges */ struct bus *first_bus, *last_bus; /* List of buses connected to this bridge */ unsigned int domain; unsigned int primary, secondary, subordinate; /* Bus numbers */ struct device *br_dev; }; struct bus { unsigned int domain; unsigned int number; struct bus *sibling; struct bridge *parent_bridge; struct device *first_dev, **last_dev; }; extern struct bridge host_bridge; void grow_tree(void); void show_forest(struct pci_filter *filter); /* ls-map.c */ void map_the_bus(void); pciutils-3.13.0/ls-map.c0000644000175000001440000001046214443575171013332 0ustar mjusers/* * The PCI Utilities -- Bus Mapping Mode * * Copyright (c) 1997--2008 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include "lspci.h" struct bus_bridge { struct bus_bridge *next; byte this, dev, func, first, last, bug; }; struct bus_info { byte exists; byte guestbook; struct bus_bridge *bridges, *via; }; static struct bus_info *bus_info; static void map_bridge(struct bus_info *bi, struct device *d, int np, int ns, int nl) { struct bus_bridge *b = xmalloc(sizeof(struct bus_bridge)); struct pci_dev *p = d->dev; b->next = bi->bridges; bi->bridges = b; b->this = get_conf_byte(d, np); b->dev = p->dev; b->func = p->func; b->first = get_conf_byte(d, ns); b->last = get_conf_byte(d, nl); printf("## %02x:%02x.%d is a bridge from %02x to %02x-%02x\n", p->bus, p->dev, p->func, b->this, b->first, b->last); if (b->this != p->bus) printf("!!! Bridge points to invalid primary bus.\n"); if (b->first > b->last) { printf("!!! Bridge points to invalid bus range.\n"); b->last = b->first; } } static void do_map_bus(int bus) { int domain = (filter.domain >= 0 ? filter.domain : 0); int dev, func; int verbose = pacc->debugging; struct bus_info *bi = bus_info + bus; struct device *d; if (verbose) printf("Mapping bus %04x:%02x\n", domain, bus); for (dev = 0; dev < 32; dev++) if (filter.slot < 0 || filter.slot == dev) { int func_limit = 1; for (func = 0; func < func_limit; func++) if (filter.func < 0 || filter.func == func) { struct pci_dev *p = pci_get_dev(pacc, domain, bus, dev, func); u16 vendor = pci_read_word(p, PCI_VENDOR_ID); if (vendor && vendor != 0xffff) { if (!func && (pci_read_byte(p, PCI_HEADER_TYPE) & 0x80)) func_limit = 8; if (verbose) printf("Discovered device %04x:%02x:%02x.%d\n", domain, bus, dev, func); bi->exists = 1; if (d = scan_device(p)) { show_device(d); switch (get_conf_byte(d, PCI_HEADER_TYPE) & 0x7f) { case PCI_HEADER_TYPE_BRIDGE: map_bridge(bi, d, PCI_PRIMARY_BUS, PCI_SECONDARY_BUS, PCI_SUBORDINATE_BUS); break; case PCI_HEADER_TYPE_CARDBUS: map_bridge(bi, d, PCI_CB_PRIMARY_BUS, PCI_CB_CARD_BUS, PCI_CB_SUBORDINATE_BUS); break; } free(d); } else if (verbose) printf("But it was filtered out.\n"); } pci_free_dev(p); } } } static void do_map_bridges(int bus, int min, int max) { struct bus_info *bi = bus_info + bus; struct bus_bridge *b; bi->guestbook = 1; for (b=bi->bridges; b; b=b->next) { if (bus_info[b->first].guestbook) b->bug = 1; else if (b->first < min || b->last > max) b->bug = 2; else { bus_info[b->first].via = b; do_map_bridges(b->first, b->first, b->last); } } } static void map_bridges(void) { int i; printf("\nSummary of buses:\n\n"); for (i=0; i<256; i++) if (bus_info[i].exists && !bus_info[i].guestbook) do_map_bridges(i, 0, 255); for (i=0; i<256; i++) { struct bus_info *bi = bus_info + i; struct bus_bridge *b = bi->via; if (bi->exists) { printf("%02x: ", i); if (b) printf("Entered via %02x:%02x.%d\n", b->this, b->dev, b->func); else if (!i) printf("Primary host bus\n"); else printf("Secondary host bus (?)\n"); } for (b=bi->bridges; b; b=b->next) { printf("\t%02x.%d Bridge to %02x-%02x", b->dev, b->func, b->first, b->last); switch (b->bug) { case 1: printf(" "); break; case 2: printf(" "); break; } putchar('\n'); } } } void map_the_bus(void) { if (pacc->method == PCI_ACCESS_PROC_BUS_PCI || pacc->method == PCI_ACCESS_SYS_BUS_PCI || pacc->method == PCI_ACCESS_WIN32_CFGMGR32 || pacc->method == PCI_ACCESS_DUMP) printf("WARNING: Bus mapping can be reliable only with direct hardware access enabled.\n\n"); bus_info = xmalloc(sizeof(struct bus_info) * 256); memset(bus_info, 0, sizeof(struct bus_info) * 256); if (filter.bus >= 0) do_map_bus(filter.bus); else { int bus; for (bus=0; bus<256; bus++) do_map_bus(bus); } map_bridges(); } pciutils-3.13.0/ls-vpd.c0000644000175000001440000001157014443575171013347 0ustar mjusers/* * The PCI Utilities -- Show Vital Product Data * * Copyright (c) 2008 Solarflare Communications * * Written by Ben Hutchings * Improved by Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include "lspci.h" /* * The list of all known VPD items and their formats. * Technically, this belongs to the pci.ids file, but the VPD does not seem * to be developed any longer, so we have chosen the easier way. */ enum vpd_format { F_BINARY, F_TEXT, F_RESVD, F_RDWR, }; static const struct vpd_item { byte id1, id2; byte format; const char *name; } vpd_items[] = { { 'C','P', F_BINARY, "Extended capability" }, { 'E','C', F_TEXT, "Engineering changes" }, { 'M','N', F_TEXT, "Manufacture ID" }, { 'P','N', F_TEXT, "Part number" }, { 'R','V', F_RESVD, "Reserved" }, { 'R','W', F_RDWR, "Read-write area" }, { 'S','N', F_TEXT, "Serial number" }, { 'Y','A', F_TEXT, "Asset tag" }, { 'V', 0 , F_TEXT, "Vendor specific" }, { 'Y', 0 , F_TEXT, "System specific" }, /* Non-standard extensions */ { 'C','C', F_TEXT, "CCIN" }, { 'F','C', F_TEXT, "Feature code" }, { 'F','N', F_TEXT, "FRU" }, { 'N','A', F_TEXT, "Network address" }, { 'R','M', F_TEXT, "Firmware version" }, { 'Z', 0 , F_TEXT, "Product specific" }, { 0, 0 , F_BINARY, "Unknown" } }; static void print_vpd_string(const byte *buf, word len) { while (len--) { byte ch = *buf++; if (ch == '\\') printf("\\\\"); else if (!ch && !len) ; /* Cards with null-terminated strings have been observed */ else if (ch < 32 || ch == 127) printf("\\x%02x", ch); else putchar(ch); } } static void print_vpd_binary(const byte *buf, word len) { int i; for (i = 0; i < len; i++) { if (i) putchar(' '); printf("%02x", buf[i]); } } static int read_vpd(struct device *d, int pos, byte *buf, int len, byte *csum) { if (!pci_read_vpd(d->dev, pos, buf, len)) return 0; while (len--) *csum += *buf++; return 1; } void cap_vpd(struct device *d) { word res_addr = 0, res_len, part_pos, part_len; byte buf[256]; byte tag; byte csum = 0; printf("Vital Product Data\n"); if (verbose < 2) return; while (res_addr <= PCI_VPD_ADDR_MASK) { if (!read_vpd(d, res_addr, &tag, 1, &csum)) break; if (tag & 0x80) { if (res_addr > PCI_VPD_ADDR_MASK + 1 - 3) break; if (!read_vpd(d, res_addr + 1, buf, 2, &csum)) break; res_len = buf[0] + (buf[1] << 8); res_addr += 3; } else { res_len = tag & 7; tag >>= 3; res_addr += 1; } if (res_len > PCI_VPD_ADDR_MASK + 1 - res_addr) break; part_pos = 0; switch (tag) { case 0x0f: printf("\t\tEnd\n"); return; case 0x82: printf("\t\tProduct Name: "); while (part_pos < res_len) { part_len = res_len - part_pos; if (part_len > sizeof(buf)) part_len = sizeof(buf); if (!read_vpd(d, res_addr + part_pos, buf, part_len, &csum)) break; print_vpd_string(buf, part_len); part_pos += part_len; } printf("\n"); break; case 0x90: case 0x91: printf("\t\t%s fields:\n", (tag == 0x90) ? "Read-only" : "Read/write"); while (part_pos + 3 <= res_len) { word read_len; const struct vpd_item *item; byte id[2], id1, id2; if (!read_vpd(d, res_addr + part_pos, buf, 3, &csum)) break; part_pos += 3; memcpy(id, buf, 2); id1 = id[0]; id2 = id[1]; part_len = buf[2]; if (part_len > res_len - part_pos) break; /* Is this item known? */ for (item=vpd_items; item->id1 && item->id1 != id1 || item->id2 && item->id2 != id2; item++) ; /* Only read the first byte of the RV field because the * remaining bytes are not included in the checksum. */ read_len = (item->format == F_RESVD) ? 1 : part_len; if (!read_vpd(d, res_addr + part_pos, buf, read_len, &csum)) break; printf("\t\t\t["); print_vpd_string(id, 2); printf("] %s: ", item->name); switch (item->format) { case F_TEXT: print_vpd_string(buf, part_len); printf("\n"); break; case F_BINARY: print_vpd_binary(buf, part_len); printf("\n"); break; case F_RESVD: printf("checksum %s, %d byte(s) reserved\n", csum ? "bad" : "good", part_len - 1); break; case F_RDWR: printf("%d byte(s) free\n", part_len); break; } part_pos += part_len; } break; default: printf("\t\tUnknown %s resource type %02x, will not decode more.\n", (tag & 0x80) ? "large" : "small", tag & ~0x80); return; } res_addr += res_len; } if (res_addr == 0) printf("\t\tNot readable\n"); else printf("\t\tNo end tag found\n"); } pciutils-3.13.0/.gitignore0000644000175000001440000000012014564251265013752 0ustar mjusers*.a *.o *.so *.exe *.[0-9] lspci setpci example update-pciids pci.ids.gz pcilmr pciutils-3.13.0/pciutils.spec0000644000175000001440000000234314631121771014474 0ustar mjusersName: pciutils Version: 3.13.0 Release: 1 Source: http://mj.ucw.cz/download/linux/pci/%{name}-%{version}.tar.gz Copyright: GNU GPL Buildroot: /tmp/%{name}-%{version}-root ExclusiveOS: Linux Summary: The PCI Utilities Summary(pl): Narzêdzia do manipulacji ustawieniami urz±dzeñ PCI Group: Utilities/System %description This package contains various utilities for inspecting and setting of devices connected to the PCI bus. %description -l pl Pakiet zawiera narzêdzia do ustawiania i odczytywania informacji o urz±dzeniach pod³±czonych do szyny PCI w Twoim komputerze. %description -l de Dieses Paket enthält verschiedene Programme zum Anzeigen und Einstellen von PCI-Bus Erweiterungen. %prep %setup -q %build make OPT="$RPM_OPT_FLAGS" %install rm -rf $RPM_BUILD_ROOT make install PREFIX=$RPM_BUILD_ROOT/usr ROOT=$RPM_BUILD_ROOT/ \ MANDIR=$RPM_BUILD_ROOT/%{_mandir} %files %defattr(0644, root, root, 0755) %attr(0644, root, man) %{_mandir}/man8/* %attr(0711, root, root) /usr/sbin/* %config /usr/share/pci.ids %doc README ChangeLog pciutils.lsm %clean rm -rf $RPM_BUILD_ROOT %changelog * Tue Sep 29 1998 Krzysztof G. Baranowski [1.07-1] - build from non-root account against glibc-2.0 - written spec from scratch pciutils-3.13.0/ls-kernel.c0000644000175000001440000001320014443575171014026 0ustar mjusers/* * The PCI Utilities -- Show Kernel Drivers * * Copyright (c) 1997--2013 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include "lspci.h" #ifdef PCI_OS_LINUX #include #ifdef PCI_USE_LIBKMOD #include static struct kmod_ctx *kmod_ctx; static int show_kernel_init(void) { static int show_kernel_inited = -1; if (show_kernel_inited >= 0) return show_kernel_inited; kmod_ctx = kmod_new(NULL, NULL); if (!kmod_ctx) { fprintf(stderr, "lspci: Unable to initialize libkmod context\n"); goto failed; } int err; if ((err = kmod_load_resources(kmod_ctx)) < 0) { fprintf(stderr, "lspci: Unable to load libkmod resources: error %d\n", err); goto failed; } show_kernel_inited = 1; return 1; failed: show_kernel_inited = 0; return 0; } void show_kernel_cleanup(void) { if (kmod_ctx) kmod_unref(kmod_ctx); } static const char *next_module(struct device *d) { static struct kmod_list *klist, *kcurrent; static struct kmod_module *kmodule; if (kmodule) { kmod_module_unref(kmodule); kmodule = NULL; } if (!klist) { pci_fill_info(d->dev, PCI_FILL_MODULE_ALIAS); if (!d->dev->module_alias) return NULL; int err = kmod_module_new_from_lookup(kmod_ctx, d->dev->module_alias, &klist); if (err < 0) { fprintf(stderr, "lspci: libkmod lookup failed: error %d\n", err); return NULL; } kcurrent = klist; } else kcurrent = kmod_list_next(klist, kcurrent); if (kcurrent) { kmodule = kmod_module_get_module(kcurrent); return kmod_module_get_name(kmodule); } kmod_module_unref_list(klist); klist = NULL; return NULL; } #else struct pcimap_entry { struct pcimap_entry *next; unsigned int vendor, device; unsigned int subvendor, subdevice; unsigned int class, class_mask; char module[1]; }; static struct pcimap_entry *pcimap_head; static int show_kernel_init(void) { static int tried_pcimap; struct utsname uts; char *name, line[1024]; FILE *f; if (tried_pcimap) return 1; tried_pcimap = 1; if (name = opt_pcimap) { f = fopen(name, "r"); if (!f) die("Cannot open pcimap file %s: %m", name); } else { if (uname(&uts) < 0) die("uname() failed: %m"); name = alloca(64 + strlen(uts.release)); sprintf(name, "/lib/modules/%s/modules.pcimap", uts.release); f = fopen(name, "r"); if (!f) return 1; } while (fgets(line, sizeof(line), f)) { char *c = strchr(line, '\n'); struct pcimap_entry *e; if (!c) die("Unterminated or too long line in %s", name); *c = 0; if (!line[0] || line[0] == '#') continue; c = line; while (*c && *c != ' ' && *c != '\t') c++; if (!*c) continue; /* FIXME: Emit warnings! */ *c++ = 0; e = xmalloc(sizeof(*e) + strlen(line)); if (sscanf(c, "%i%i%i%i%i%i", &e->vendor, &e->device, &e->subvendor, &e->subdevice, &e->class, &e->class_mask) != 6) continue; e->next = pcimap_head; pcimap_head = e; strcpy(e->module, line); } fclose(f); return 1; } static int match_pcimap(struct device *d, struct pcimap_entry *e) { struct pci_dev *dev = d->dev; unsigned int class = (((unsigned int)dev->device_class << 8) | dev->prog_if); #define MATCH(x, y) ((y) > 0xffff || (x) == (y)) return MATCH(dev->vendor_id, e->vendor) && MATCH(dev->device_id, e->device) && MATCH(dev->subsys_vendor_id, e->subvendor) && MATCH(dev->subsys_id, e->subdevice) && (class & e->class_mask) == e->class; #undef MATCH } static const char *next_module(struct device *d) { static struct pcimap_entry *current; if (!current) current = pcimap_head; else current = current->next; while (current) { if (match_pcimap(d, current)) return current->module; current = current->next; } return NULL; } void show_kernel_cleanup(void) { } #endif static const char * next_module_filtered(struct device *d) { static char prev_module[256]; const char *module; while (module = next_module(d)) { if (strcmp(module, prev_module)) { strncpy(prev_module, module, sizeof(prev_module)); prev_module[sizeof(prev_module) - 1] = 0; return module; } } prev_module[0] = 0; return NULL; } void show_kernel(struct device *d) { const char *driver, *module; pci_fill_info(d->dev, PCI_FILL_DRIVER); if (driver = pci_get_string_property(d->dev, PCI_FILL_DRIVER)) printf("\tKernel driver in use: %s\n", driver); if (!show_kernel_init()) return; int cnt = 0; while (module = next_module_filtered(d)) printf("%s %s", (cnt++ ? "," : "\tKernel modules:"), module); if (cnt) putchar('\n'); } void show_kernel_machine(struct device *d) { const char *driver, *module; pci_fill_info(d->dev, PCI_FILL_DRIVER); if (driver = pci_get_string_property(d->dev, PCI_FILL_DRIVER)) printf("Driver:\t%s\n", driver); if (!show_kernel_init()) return; while (module = next_module_filtered(d)) printf("Module:\t%s\n", module); } #else void show_kernel(struct device *d) { const char *driver; pci_fill_info(d->dev, PCI_FILL_DRIVER); if (driver = pci_get_string_property(d->dev, PCI_FILL_DRIVER)) printf("\tDriver in use: %s\n", driver); } void show_kernel_machine(struct device *d) { const char *driver; pci_fill_info(d->dev, PCI_FILL_DRIVER); if (driver = pci_get_string_property(d->dev, PCI_FILL_DRIVER)) printf("Driver:\t%s\n", driver); } void show_kernel_cleanup(void) { } #endif pciutils-3.13.0/Makefile0000644000175000001440000002037614626121011013421 0ustar mjusers# Makefile for The PCI Utilities # (c) 1998--2024 Martin Mares OPT=-O2 CFLAGS=$(OPT) -Wall -W -Wno-parentheses -Wstrict-prototypes -Wmissing-prototypes VERSION=3.13.0 DATE=2024-05-30 # Host OS and release (override if you are cross-compiling) HOST= RELEASE= CROSS_COMPILE= # Support for compressed pci.ids (yes/no, default: detect) ZLIB= # Support for resolving ID's by DNS (yes/no, default: detect) DNS= # Build libpci as a shared library (yes/no; or local for testing; requires GCC) SHARED=no # Use libkmod to resolve kernel modules on Linux (yes/no, default: detect) LIBKMOD= # Use libudev to resolve device names using hwdb on Linux (yes/no, default: detect) HWDB= # ABI version suffix in the name of the shared library # (as we use proper symbol versioning, this seldom needs changing) ABI_VERSION=3 # Installation directories PREFIX=/usr/local BINDIR=$(PREFIX)/bin SBINDIR=$(PREFIX)/sbin SHAREDIR=$(PREFIX)/share IDSDIR=$(SHAREDIR) MANDIR:=$(shell if [ -d $(PREFIX)/share/man ] ; then echo $(PREFIX)/share/man ; else echo $(PREFIX)/man ; fi) INCDIR=$(PREFIX)/include LIBDIR=$(PREFIX)/lib PKGCFDIR=$(LIBDIR)/pkgconfig # Commands INSTALL=install DIRINSTALL=install -d STRIP=-s ifdef CROSS_COMPILE STRIP+=--strip-program $(CROSS_COMPILE)strip CC=$(CROSS_COMPILE)gcc else CC=cc endif AR=$(CROSS_COMPILE)ar RANLIB=$(CROSS_COMPILE)ranlib DLLTOOL=$(CROSS_COMPILE)dlltool WINDRES=$(CROSS_COMPILE)windres # Base name of the library (overridden on NetBSD, which has its own libpci) LIBNAME=libpci -include lib/config.mk PCIINC=lib/config.h lib/header.h lib/pci.h lib/types.h lib/sysdep.h PCIINC_INS=lib/config.h lib/header.h lib/pci.h lib/types.h UTILINC=pciutils.h bitops.h $(PCIINC) LMR=margin_hw.o margin.o margin_log.o margin_results.o margin_args.o LMROBJS=$(addprefix lmr/,$(LMR)) LMRINC=lmr/lmr.h $(UTILINC) export all: lib/$(PCIIMPLIB) lspci$(EXEEXT) setpci$(EXEEXT) example$(EXEEXT) lspci.8 setpci.8 pcilib.7 pci.ids.5 update-pciids update-pciids.8 $(PCI_IDS) pcilmr$(EXEEXT) pcilmr.8 lib/$(PCIIMPLIB): $(PCIINC) force $(MAKE) -C lib all force: lib/config.h lib/config.mk: cd lib && ./configure COMMON=common.o ifeq ($(COMPAT_GETOPT),yes) PCIINC+=compat/getopt.h COMMON+=compat/getopt.o endif lspci$(EXEEXT): lspci.o ls-vpd.o ls-caps.o ls-caps-vendor.o ls-ecaps.o ls-kernel.o ls-tree.o ls-map.o $(COMMON) lib/$(PCIIMPLIB) setpci$(EXEEXT): setpci.o $(COMMON) lib/$(PCIIMPLIB) LSPCIINC=lspci.h $(UTILINC) lspci.o: lspci.c $(LSPCIINC) ls-vpd.o: ls-vpd.c $(LSPCIINC) ls-caps.o: ls-caps.c $(LSPCIINC) ls-ecaps.o: ls-ecaps.c $(LSPCIINC) ls-kernel.o: ls-kernel.c $(LSPCIINC) ls-tree.o: ls-tree.c $(LSPCIINC) ls-map.o: ls-map.c $(LSPCIINC) setpci.o: setpci.c $(UTILINC) common.o: common.c $(UTILINC) compat/getopt.o: compat/getopt.c lspci$(EXEEXT): LDLIBS+=$(LIBKMOD_LIBS) ls-kernel.o: override CFLAGS+=$(LIBKMOD_CFLAGS) update-pciids: update-pciids.sh sed <$< >$@ "s@^DEST=.*@DEST=$(if $(IDSDIR),$(IDSDIR)/,)$(PCI_IDS)@;s@^PCI_COMPRESSED_IDS=.*@PCI_COMPRESSED_IDS=$(PCI_COMPRESSED_IDS)@;s@VERSION=.*@VERSION=$(VERSION)@" chmod +x $@ # The example of use of libpci example$(EXEEXT): example.o lib/$(PCIIMPLIB) example.o: example.c $(PCIINC) $(LMROBJS) pcilmr.o: override CFLAGS+=-I . $(LMROBJS): %.o: %.c $(LMRINC) pcilmr$(EXEEXT): pcilmr.o $(LMROBJS) $(COMMON) lib/$(PCIIMPLIB) pcilmr.o: pcilmr.c $(LMRINC) %$(EXEEXT): %.o $(CC) $(CFLAGS) $(LDFLAGS) $(TARGET_ARCH) $^ $(LDLIBS) -o $@ ifdef PCI_OS_WINDOWS comma := , %-rsrc.rc: lib/winrsrc.rc.in sed <$< >$@ -e 's,@PCILIB_VERSION@,$(PCILIB_VERSION),' \ -e 's,@PCILIB_VERSION_WINRC@,$(subst .,\$(comma),$(PCILIB_VERSION).0),' \ -e 's,@FILENAME@,$(subst -rsrc.rc,$(EXEEXT),$@),' \ -e 's,@DESCRIPTION@,$(subst -rsrc.rc,,$@),' \ -e 's,@LIBRARY_BUILD@,0,' \ -e 's,@DEBUG_BUILD@,$(if $(findstring -g,$(CFLAGS)),1,0),' %-rsrc.o: %-rsrc.rc $(WINDRES) --input=$< --output=$@ --input-format=rc --output-format=coff lspci$(EXEEXT): lspci-rsrc.o setpci$(EXEEXT): setpci-rsrc.o pcilmr$(EXEEXT): pcilmr-rsrc.o endif %.8 %.7 %.5: %.man M=`echo $(DATE) | sed 's/-01-/-January-/;s/-02-/-February-/;s/-03-/-March-/;s/-04-/-April-/;s/-05-/-May-/;s/-06-/-June-/;s/-07-/-July-/;s/-08-/-August-/;s/-09-/-September-/;s/-10-/-October-/;s/-11-/-November-/;s/-12-/-December-/;s/\(.*\)-\(.*\)-\(.*\)/\3 \2 \1/'` ; sed <$< >$@ "s/@TODAY@/$$M/;s/@VERSION@/pciutils-$(VERSION)/;s#@IDSDIR@#$(IDSDIR)#;s#@PCI_IDS@#$(PCI_IDS)#" ctags: rm -f tags find . -name '*.[hc]' -exec ctags --append {} + TAGS: rm -f TAGS find . -name '*.[hc]' -exec etags --append {} + clean: rm -f `find . -name "*~" -o -name "*.[oa]" -o -name "\#*\#" -o -name TAGS -o -name core -o -name "*.orig"` rm -f update-pciids lspci$(EXEEXT) setpci$(EXEEXT) example$(EXEEXT) lib/config.* *.[578] pci.ids.gz lib/*.pc lib/*.so lib/*.so.* lib/*.dll lib/*.def lib/dllrsrc.rc *-rsrc.rc tags pcilmr$(EXEEXT) rm -rf maint/dist distclean: clean install: all # -c is ignored on Linux, but required on FreeBSD $(DIRINSTALL) -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(SBINDIR) $(DESTDIR)$(IDSDIR) $(DESTDIR)$(MANDIR)/man8 $(DESTDIR)$(MANDIR)/man7 $(DESTDIR)$(MANDIR)/man5 $(INSTALL) -c -m 755 $(STRIP) lspci$(EXEEXT) $(DESTDIR)$(LSPCIDIR) $(INSTALL) -c -m 755 $(STRIP) setpci$(EXEEXT) $(DESTDIR)$(SBINDIR) $(INSTALL) -c -m 755 $(STRIP) pcilmr$(EXEEXT) $(DESTDIR)$(SBINDIR) $(INSTALL) -c -m 755 update-pciids $(DESTDIR)$(SBINDIR) ifneq ($(IDSDIR),) $(INSTALL) -c -m 644 $(PCI_IDS) $(DESTDIR)$(IDSDIR) else $(INSTALL) -c -m 644 $(PCI_IDS) $(DESTDIR)$(SBINDIR) endif $(INSTALL) -c -m 644 lspci.8 setpci.8 pcilmr.8 update-pciids.8 $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -c -m 644 pcilib.7 $(DESTDIR)$(MANDIR)/man7 $(INSTALL) -c -m 644 pci.ids.5 $(DESTDIR)$(MANDIR)/man5 ifeq ($(SHARED),yes) ifeq ($(LIBEXT),dylib) ln -sf $(PCILIB) $(DESTDIR)$(LIBDIR)/$(LIBNAME).$(ABI_VERSION).$(LIBEXT) else ifeq ($(LIBEXT),so) ln -sf $(PCILIB) $(DESTDIR)$(LIBDIR)/$(LIBNAME).$(LIBEXT).$(ABI_VERSION) endif endif ifeq ($(SHARED),yes) install: install-pcilib endif install-pcilib: lib/$(PCILIB) $(DIRINSTALL) -m 755 $(DESTDIR)$(LIBDIR) ifeq ($(SHARED)_$(LIBEXT),yes_dll) # DLL library must have executable flag on disk and be placed in same directory as where are EXE files $(DIRINSTALL) -m 755 $(DESTDIR)$(SBINDIR) $(INSTALL) -c -m 755 lib/$(PCILIB) $(DESTDIR)$(SBINDIR) else $(INSTALL) -c -m 644 lib/$(PCILIB) $(DESTDIR)$(LIBDIR) endif install-lib: $(PCIINC_INS) install-pcilib $(DIRINSTALL) -m 755 $(DESTDIR)$(INCDIR)/pci $(DESTDIR)$(PKGCFDIR) $(INSTALL) -c -m 644 $(PCIINC_INS) $(DESTDIR)$(INCDIR)/pci $(INSTALL) -c -m 644 lib/$(PCILIBPC) $(DESTDIR)$(PKGCFDIR) ifneq ($(PCIIMPLIB),$(PCILIB)) $(INSTALL) -c -m 644 lib/$(PCIIMPLIB) $(DESTDIR)$(LIBDIR) endif ifneq ($(PCIIMPDEF),) $(INSTALL) -c -m 644 lib/$(PCIIMPDEF) $(DESTDIR)$(LIBDIR) endif ifeq ($(SHARED),yes) ifeq ($(LIBEXT),dylib) ln -sf $(PCILIB) $(DESTDIR)$(LIBDIR)/$(LIBNAME).$(ABI_VERSION).$(LIBEXT) ln -sf $(LIBNAME).$(ABI_VERSION).$(LIBEXT) $(DESTDIR)$(LIBDIR)/$(LIBNAME).$(LIBEXT) else ifeq ($(LIBEXT),so) ln -sf $(PCILIB) $(DESTDIR)$(LIBDIR)/$(LIBNAME).$(LIBEXT).$(ABI_VERSION) ln -sf $(LIBNAME).$(LIBEXT).$(ABI_VERSION) $(DESTDIR)$(LIBDIR)/$(LIBNAME).$(LIBEXT) endif endif uninstall: all rm -f $(DESTDIR)$(LSPCIDIR)/lspci$(EXEEXT) $(DESTDIR)$(SBINDIR)/setpci$(EXEEXT) $(DESTDIR)$(SBINDIR)/pcilmr$(EXEEXT) $(DESTDIR)$(SBINDIR)/update-pciids ifneq ($(IDSDIR),) rm -f $(DESTDIR)$(IDSDIR)/$(PCI_IDS) else rm -f $(DESTDIR)$(SBINDIR)/$(PCI_IDS) endif rm -f $(DESTDIR)$(MANDIR)/man8/lspci.8 $(DESTDIR)$(MANDIR)/man8/setpci.8 $(DESTDIR)$(MANDIR)/man8/pcilmr.8 $(DESTDIR)$(MANDIR)/man8/update-pciids.8 rm -f $(DESTDIR)$(MANDIR)/man7/pcilib.7 rm -f $(DESTDIR)$(MANDIR)/man5/pci.ids.5 ifeq ($(SHARED)_$(LIBEXT),yes_dll) rm -f $(DESTDIR)$(SBINDIR)/$(PCILIB) else rm -f $(DESTDIR)$(LIBDIR)/$(PCILIB) endif rm -f $(DESTDIR)$(PKGCFDIR)/$(PCILIBPC) rm -f $(addprefix $(DESTDIR)$(INCDIR)/pci/,$(notdir $(PCIINC_INS))) ifneq ($(PCIIMPLIB),$(PCILIB)) rm -f $(DESTDIR)$(LIBDIR)/$(PCIIMPLIB) endif ifneq ($(PCIIMPDEF),) rm -f $(DESTDIR)$(LIBDIR)/$(PCIIMPDEF) endif ifeq ($(SHARED),yes) ifneq ($(LIBEXT),dll) rm -f $(DESTDIR)$(LIBDIR)/$(LIBNAME).$(LIBEXT) ifeq ($(LIBEXT),dylib) rm -f $(DESTDIR)$(LIBDIR)/$(LIBNAME).$(ABI_VERSION).$(LIBEXT) else rm -f $(DESTDIR)$(LIBDIR)/$(LIBNAME).$(LIBEXT).$(ABI_VERSION) endif endif endif pci.ids.gz: pci.ids gzip -9n <$< >$@ .PHONY: all clean distclean install install-lib uninstall force tags TAGS pciutils-3.13.0/lspci.man0000644000175000001440000002547314574364215013614 0ustar mjusers.TH lspci 8 "@TODAY@" "@VERSION@" "The PCI Utilities" .SH NAME lspci \- list all PCI devices .SH SYNOPSIS .B lspci .RB [ options ] .SH DESCRIPTION .B lspci is a utility for displaying information about PCI buses in the system and devices connected to them. By default, it shows a brief list of devices. Use the options described below to request either a more verbose output or output intended for parsing by other programs. If you are going to report bugs in PCI device drivers or in .I lspci itself, please include output of "lspci -vvx" or even better "lspci -vvxxx" (however, see below for possible caveats). Some parts of the output, especially in the highly verbose modes, are probably intelligible only to experienced PCI hackers. For exact definitions of the fields, please consult either the PCI specifications or the .B header.h and .B /usr/include/linux/pci.h include files. Access to some parts of the PCI configuration space is restricted to root on many operating systems, so the features of .I lspci available to normal users are limited. However, .I lspci tries its best to display as much as available and mark all other information with .I text. .SH OPTIONS .SS Basic display modes .TP .B -m Dump PCI device data in a backward-compatible machine readable form. See below for details. .TP .B -mm Dump PCI device data in a machine readable form for easy parsing by scripts. See below for details. .TP .B -t Show a tree-like diagram containing all buses, bridges, devices and connections between them. .SS Display options .TP .B -v Be verbose and display detailed information about all devices. .TP .B -vv Be very verbose and display more details. This level includes everything deemed useful. .TP .B -vvv Be even more verbose and display everything we are able to parse, even if it doesn't look interesting at all (e.g., undefined memory regions). .TP .B -k Show kernel drivers handling each device and also kernel modules capable of handling it. Turned on by default when .B -v is given in the normal mode of output. (Currently works only on Linux with kernel 2.6 or newer.) .TP .B -x Show hexadecimal dump of the standard part of the configuration space (the first 64 bytes or 128 bytes for CardBus bridges). .TP .B -xxx Show hexadecimal dump of the whole PCI configuration space. It is available only to root as several PCI devices .B crash when you try to read some parts of the config space (this behavior probably doesn't violate the PCI standard, but it's at least very stupid). However, such devices are rare, so you needn't worry much. .TP .B -xxxx Show hexadecimal dump of the extended (4096-byte) PCI configuration space available on PCI-X 2.0 and PCI Express buses. .TP .B -b Bus-centric view. Show all IRQ numbers and addresses as seen by the cards on the PCI bus instead of as seen by the kernel. .TP .B -D Always show PCI domain numbers. By default, lspci suppresses them on machines which have only domain 0. .TP .B -P Identify PCI devices by path through each bridge, instead of by bus number. .TP .B -PP Identify PCI devices by path through each bridge, showing the bus number as well as the device number. .SS Options to control resolving ID's to names .TP .B -n Show PCI vendor and device codes as numbers instead of looking them up in the PCI ID list. .TP .B -nn Show PCI vendor and device codes as both numbers and names. .TP .B -q Use DNS to query the central PCI ID database if a device is not found in the local .B pci.ids file. If the DNS query succeeds, the result is cached in .B $XDG_CACHE_HOME/pci-ids and it is recognized in subsequent runs even if .B -q is not given any more. Please use this switch inside automated scripts only with caution to avoid overloading the database servers. .TP .B -qq Same as .BR -q , but the local cache is reset. .TP .B -Q Query the central database even for entries which are recognized locally. Use this if you suspect that the displayed entry is wrong. .SS Options for selection of devices .TP .B -s [[[[]:]]:][][.[]] Show only devices in the specified domain (in case your machine has several host bridges, they can either share a common bus number space or each of them can address a PCI domain of its own; domains are numbered from 0 to ffff), bus (0 to ff), device (0 to 1f) and function (0 to 7). Each component of the device address can be omitted or set to "*", both meaning "any value". All numbers are hexadecimal. E.g., "0:" means all devices on bus 0, "0" means all functions of device 0 on any bus, "0.3" selects third function of device 0 on all buses and ".4" shows only the fourth function of each device. .TP .B -d []:[][:[:]] Show only devices with specified vendor, device, class ID, and programming interface. The ID's are given in hexadecimal and may be omitted or given as "*", both meaning "any value". The class ID can contain "x" characters which stand for "any digit". .SS Other options .TP .B -i Use .B as the PCI ID list instead of @IDSDIR@/pci.ids. .TP .B -p Use .B as the map of PCI ID's handled by kernel modules. By default, lspci uses .RI /lib/modules/ kernel_version /modules.pcimap. Applies only to Linux systems with recent enough module tools. .TP .B -M Invoke bus mapping mode which performs a thorough scan of all PCI devices, including those behind misconfigured bridges, etc. This option gives meaningful results only with a direct hardware access mode, which usually requires root privileges. By default, the bus mapper scans domain. You can use the .B -s option to select a different domain. .TP .B --version Shows .I lspci version. This option should be used stand-alone. .SS PCI access options .PP The PCI utilities use the PCI library to talk to PCI devices (see \fBpcilib\fP(7) for details). You can use the following options to influence its behavior: .TP .B -A The library supports a variety of methods to access the PCI hardware. By default, it uses the first access method available, but you can use this option to override this decision. See \fB-A help\fP for a list of available methods and their descriptions. .TP .B -O = The behavior of the library is controlled by several named parameters. This option allows one to set the value of any of the parameters. Use \fB-O help\fP for a list of known parameters and their default values. .TP .B -H1 Use direct hardware access via Intel configuration mechanism 1. (This is a shorthand for \fB-A intel-conf1\fP.) .TP .B -H2 Use direct hardware access via Intel configuration mechanism 2. (This is a shorthand for \fB-A intel-conf2\fP.) .TP .B -F Instead of accessing real hardware, read the list of devices and values of their configuration registers from the given file produced by an earlier run of lspci -x. This is very useful for analysis of user-supplied bug reports, because you can display the hardware configuration in any way you want without disturbing the user with requests for more dumps. .TP .B -G Increase debug level of the library. .SH MACHINE READABLE OUTPUT If you intend to process the output of lspci automatically, please use one of the machine-readable output formats .RB ( -m , .BR -vm , .BR -vmm ) described in this section. All other formats are likely to change between versions of lspci. .P All numbers are always printed in hexadecimal. If you want to process numeric ID's instead of names, please add the .B -n switch. .SS Simple format (-m) In the simple format, each device is described on a single line, which is formatted as parameters suitable for passing to a shell script, i.e., values separated by whitespaces, quoted and escaped if necessary. Some of the arguments are positional: slot, class, vendor name, device name, subsystem vendor name and subsystem name (the last two are empty if the device has no subsystem); the remaining arguments are option-like: .TP .BI -r rev Revision number. .TP .BI -p progif Programming interface. .P The relative order of positional arguments and options is undefined. New options can be added in future versions, but they will always have a single argument not separated from the option by any spaces, so they can be easily ignored if not recognized. .SS Verbose format (-vmm) The verbose output is a sequence of records separated by blank lines. Each record describes a single device by a sequence of lines, each line containing a single .RI ` tag : .IR value ' pair. The .I tag and the .I value are separated by a single tab character. Neither the records nor the lines within a record are in any particular order. Tags are case-sensitive. .P The following tags are defined: .TP .B Slot The name of the slot where the device resides .RI ([ domain :] bus : device . function ). This tag is always the first in a record. .TP .B Class Name of the class. .TP .B Vendor Name of the vendor. .TP .B Device Name of the device. .TP .B SVendor Name of the subsystem vendor (optional). .TP .B SDevice Name of the subsystem (optional). .TP .B PhySlot The physical slot where the device resides (optional, Linux only). .TP .B Rev Revision number (optional). .TP .B ProgIf Programming interface (optional). .TP .B Driver Kernel driver currently handling the device (optional, Linux only). .TP .B Module Kernel module reporting that it is capable of handling the device (optional, Linux only). Multiple lines with this tag can occur. .TP .B NUMANode NUMA node this device is connected to (optional, Linux only). .TP .B IOMMUGroup IOMMU group that this device is part of (optional, Linux only). .P New tags can be added in future versions, so you should silently ignore any tags you don't recognize. .SS Backward-compatible verbose format (-vm) In this mode, lspci tries to be perfectly compatible with its old versions. It's almost the same as the regular verbose format, but the .B Device tag is used for both the slot and the device name, so it occurs twice in a single record. Please avoid using this format in any new code. .SH FILES .TP .B @IDSDIR@/pci.ids A list of all known PCI ID's (vendors, devices, classes and subclasses). Maintained at https://pci-ids.ucw.cz/, use the .B update-pciids utility to download the most recent version. .TP .B @IDSDIR@/pci.ids.gz If lspci is compiled with support for compression, this file is tried before pci.ids. .TP .B $XDG_CACHE_HOME/pci-ids All ID's found in the DNS query mode are cached in this file. .SH BUGS Sometimes, lspci is not able to decode the configuration registers completely. This usually happens when not enough documentation was available to the authors. In such cases, it at least prints the .B mark to signal that there is potentially something more to say. If you know the details, patches will be of course welcome. Access to the extended configuration space is currently supported only by the .B linux_sysfs back-end. .SH SEE ALSO .BR setpci (8), .BR pci.ids (5), .BR update-pciids (8), .BR pcilib (7) .SH AUTHOR The PCI Utilities are maintained by Martin Mares . pciutils-3.13.0/update-pciids.man0000644000175000001440000000142614162155726015223 0ustar mjusers.TH update-pciids 8 "@TODAY@" "@VERSION@" "The PCI Utilities" .SH NAME update-pciids \- download new version of the PCI ID list .SH SYNOPSIS .B update-pciids .RB [ -q ] .SH DESCRIPTION .B update-pciids fetches the current version of the pci.ids file from the primary distribution site and installs it. This utility requires curl, wget or lynx to be installed. If gzip or bzip2 are available, it automatically downloads the compressed version of the list. .SH OPTIONS .TP .B -q Be quiet and do not report anything except errors. .SH FILES .TP .B @IDSDIR@/@PCI_IDS@ Here we install the new list. .SH SEE ALSO .BR lspci (8), .BR pci.ids (5), .BR curl (1), .BR wget (1), .BR lynx (1), .BR gzip (1), .BR bzip2 (1) .SH AUTHOR The PCI Utilities are maintained by Martin Mares . pciutils-3.13.0/compat/0000755000175000001440000000000014631121771013245 5ustar mjuserspciutils-3.13.0/compat/getopt.h0000644000175000001440000001003014162626257014722 0ustar mjusers/* Declarations for getopt. Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _GETOPT_H #define _GETOPT_H 1 #ifdef __cplusplus extern "C" { #endif /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ extern char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns EOF, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ extern int optind; /* Callers store zero here to inhibit the error message `getopt' prints for unrecognized options. */ extern int opterr; /* Set to an option character which was unrecognized. */ extern int optopt; /* Describe the long-named options requested by the application. The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector of `struct option' terminated by an element containing a name which is zero. The field `has_arg' is: no_argument (or 0) if the option does not take an argument, required_argument (or 1) if the option requires an argument, optional_argument (or 2) if the option takes an optional argument. If the field `flag' is not NULL, it points to a variable that is set to the value given in the field `val' when the option is found, but left unchanged if the option is not found. To have a long-named option do something other than set an `int' to a compiled-in constant, such as set a value from `optarg', set the option's `flag' field to zero and its `val' field to a nonzero value (the equivalent single-letter option character, if there is one). For long options that have a zero `flag' field, `getopt' returns the contents of the `val' field. */ struct option { #if __STDC__ const char *name; #else char *name; #endif /* has_arg can't be an enum because some compilers complain about type mismatches in all the code that assumes it is an int. */ int has_arg; int *flag; int val; }; /* Names for the values of the `has_arg' field of `struct option'. */ #define no_argument 0 #define required_argument 1 #define optional_argument 2 #if __STDC__ || defined(PROTO) extern int getopt (int argc, char *const *argv, const char *shortopts); extern int getopt_long (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind); extern int getopt_long_only (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind); /* Internal only. Users should not call this directly. */ extern int _getopt_internal (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind, int long_only); #else /* not __STDC__ */ extern int getopt (); extern int getopt_long (); extern int getopt_long_only (); extern int _getopt_internal (); #endif /* not __STDC__ */ #ifdef __cplusplus } #endif #endif /* _GETOPT_H */ pciutils-3.13.0/compat/README0000644000175000001440000000017314631121771014126 0ustar mjusersThis directory contains implementations of standard library functions for systems where the default C libraries lack them. pciutils-3.13.0/compat/getopt.c0000644000175000001440000005153314443575171014732 0ustar mjusers/* Getopt for GNU. NOTE: getopt is now part of the C library, so if you don't know what "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu before changing it! Copyright (C) 1987, 88, 89, 90, 91, 92, 1993 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifndef __STDC__ # ifndef const # define const # endif #endif /* This tells Alpha OSF/1 not to define a getopt prototype in . */ #ifndef _NO_PROTO #define _NO_PROTO #endif #include /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C Library, but also included in many other GNU distributions. Compiling and linking in this code is a waste when using the GNU C library (especially if it is a shared library). Rather than having every GNU program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ #if defined (_LIBC) || !defined (__GNU_LIBRARY__) /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ #ifdef __GNU_LIBRARY__ /* Don't include stdlib.h for non-GNU C libraries because some of them contain conflicting prototypes for getopt. */ #include #endif /* GNU C library. */ /* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a long-named option. Because this is not POSIX.2 compliant, it is being phased out. */ /* #define GETOPT_COMPAT */ /* This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves differently for the user, since it allows the user to intersperse the options with the other arguments. As `getopt' works, it permutes the elements of ARGV so that, when it is done, all the options precede everything else. Thus all application programs are extended to handle flexible argument order. Setting the environment variable POSIXLY_CORRECT disables permutation. Then the behavior is completely standard. GNU application programs can use a third alternative mode in which they can distinguish the relative order of options and other arguments. */ #include "getopt.h" /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ char *optarg = 0; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns EOF, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ /* XXX 1003.2 says this must be 1 before any call. */ int optind = 0; /* The next char to be scanned in the option-element in which the last option character we returned was found. This allows us to pick up the scan where we left off. If this is zero, or a null string, it means resume the scan by advancing to the next ARGV-element. */ static char *nextchar; /* Callers store zero here to inhibit the error message for unrecognized options. */ int opterr = 1; /* Set to an option character which was unrecognized. This must be initialized on some systems to avoid linking in the system's own getopt implementation. */ #define BAD_OPTION '\0' int optopt = BAD_OPTION; /* Describe how to deal with options that follow non-option ARGV-elements. If the caller did not specify anything, the default is REQUIRE_ORDER if the environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise. REQUIRE_ORDER means don't recognize them as options; stop option processing when the first non-option is seen. This is what Unix does. This mode of operation is selected by either setting the environment variable POSIXLY_CORRECT, or using `+' as the first character of the list of option characters. PERMUTE is the default. We permute the contents of ARGV as we scan, so that eventually all the non-options are at the end. This allows options to be given in any order, even with programs that were not written to expect this. RETURN_IN_ORDER is an option available to programs that were written to expect options and other ARGV-elements in any order and that care about the ordering of the two. We describe each non-option ARGV-element as if it were the argument of an option with character code 1. Using `-' as the first character of the list of option characters selects this mode of operation. The special argument `--' forces an end of option-scanning regardless of the value of `ordering'. In the case of RETURN_IN_ORDER, only `--' can cause `getopt' to return EOF with `optind' != ARGC. */ static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; #ifdef __GNU_LIBRARY__ /* We want to avoid inclusion of string.h with non-GNU libraries because there are many ways it can cause trouble. On some systems, it contains special magic macros that don't work in GCC. */ #include #define my_index strchr #define my_strlen strlen #define my_strcmp strcmp #define my_strncmp strncmp #else /* Avoid depending on library functions or files whose names are inconsistent. */ #if __STDC__ || defined(PROTO) extern char *getenv(const char *name); static int my_strlen(const char *s); static char *my_index(const char *str, int chr); static int my_strncmp(const char *s1, const char *s2, int n); static int my_strcmp(const char *s1, const char *s2); #else extern char *getenv(); #endif static int my_strlen(const char *str) { int n = 0; while (*str++) n++; return n; } static char *my_index(const char *str, int chr) { while (*str) { if (*str == chr) return (char *) str; str++; } return 0; } static int my_strncmp(const char *s1, const char *s2, int n) { while (n && *s1 && (*s1 == *s2)) { ++s1; ++s2; --n; } if (n == 0) return 0; return *(const unsigned char *)s1 - *(const unsigned char *)s2; } static int my_strcmp(const char *s1, const char *s2) { return my_strncmp(s1, s2, -1); } #endif /* GNU C library. */ /* Handle permutation of arguments. */ /* Describe the part of ARGV that contains non-options that have been skipped. `first_nonopt' is the index in ARGV of the first of them; `last_nonopt' is the index after the last of them. */ static int first_nonopt; static int last_nonopt; /* Exchange two adjacent subsequences of ARGV. One subsequence is elements [first_nonopt,last_nonopt) which contains all the non-options that have been skipped so far. The other is elements [last_nonopt,optind), which contains all the options processed since those non-options were skipped. `first_nonopt' and `last_nonopt' are relocated so that they describe the new indices of the non-options in ARGV after they are moved. To perform the swap, we first reverse the order of all elements. So all options now come before all non options, but they are in the wrong order. So we put back the options and non options in original order by reversing them again. For example: original input: a b c -x -y reverse all: -y -x c b a reverse options: -x -y c b a reverse non options: -x -y a b c */ #if __STDC__ || defined(PROTO) static void exchange(char **argv); #endif static void exchange(char **argv) { char *temp, **first, **last; /* Reverse all the elements [first_nonopt, optind) */ first = &argv[first_nonopt]; last = &argv[optind - 1]; while (first < last) { temp = *first; *first = *last; *last = temp; first++; last--; } /* Put back the options in order */ first = &argv[first_nonopt]; first_nonopt += (optind - last_nonopt); last = &argv[first_nonopt - 1]; while (first < last) { temp = *first; *first = *last; *last = temp; first++; last--; } /* Put back the non options in order */ first = &argv[first_nonopt]; last_nonopt = optind; last = &argv[last_nonopt - 1]; while (first < last) { temp = *first; *first = *last; *last = temp; first++; last--; } } /* Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. If an element of ARGV starts with '-', and is not exactly "-" or "--", then it is an option element. The characters of this element (aside from the initial '-') are option characters. If `getopt' is called repeatedly, it returns successively each of the option characters from each of the option elements. If `getopt' finds another option character, it returns that character, updating `optind' and `nextchar' so that the next call to `getopt' can resume the scan with the following option character or ARGV-element. If there are no more option characters, `getopt' returns `EOF'. Then `optind' is the index in ARGV of the first ARGV-element that is not an option. (The ARGV-elements have been permuted so that those that are not options now come last.) OPTSTRING is a string containing the legitimate option characters. If an option character is seen that is not listed in OPTSTRING, return BAD_OPTION after printing an error message. If you set `opterr' to zero, the error message is suppressed but we still return BAD_OPTION. If a char in OPTSTRING is followed by a colon, that means it wants an arg, so the following text in the same ARGV-element, or the text of the following ARGV-element, is returned in `optarg'. Two colons mean an option that wants an optional arg; if there is text in the current ARGV-element, it is returned in `optarg', otherwise `optarg' is set to zero. If OPTSTRING starts with `-' or `+', it requests different methods of handling the non-option ARGV-elements. See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. Long-named options begin with `--' instead of `-'. Their names may be abbreviated as long as the abbreviation is unique or is an exact match for some defined option. If they have an argument, it follows the option name in the same ARGV-element, separated from the option name by a `=', or else the in next ARGV-element. When `getopt' finds a long-named option, it returns 0 if that option's `flag' field is nonzero, the value of the option's `val' field if the `flag' field is zero. The elements of ARGV aren't really const, because we permute them. But we pretend they're const in the prototype to be compatible with other systems. LONGOPTS is a vector of `struct option' terminated by an element containing a name which is zero. LONGIND returns the index in LONGOPT of the long-named option found. It is only valid when a long-named option has been found by the most recent call. If LONG_ONLY is nonzero, '-' as well as '--' can introduce long-named options. */ int _getopt_internal(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *longind, int long_only) { int option_index; optarg = 0; /* Initialize the internal data when the first call is made. Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the sequence of previously skipped non-option ARGV-elements is empty. */ if (optind == 0) { first_nonopt = last_nonopt = optind = 1; nextchar = NULL; /* Determine how to handle the ordering of options and nonoptions. */ if (optstring[0] == '-') { ordering = RETURN_IN_ORDER; ++optstring; } else if (optstring[0] == '+') { ordering = REQUIRE_ORDER; ++optstring; } else if (getenv("POSIXLY_CORRECT") != NULL) ordering = REQUIRE_ORDER; else ordering = PERMUTE; } if (nextchar == NULL || *nextchar == '\0') { if (ordering == PERMUTE) { /* If we have just processed some options following some non-options, exchange them so that the options come first. */ if (first_nonopt != last_nonopt && last_nonopt != optind) exchange((char **) argv); else if (last_nonopt != optind) first_nonopt = optind; /* Now skip any additional non-options and extend the range of non-options previously skipped. */ while (optind < argc && (argv[optind][0] != '-' || argv[optind][1] == '\0') #ifdef GETOPT_COMPAT && (longopts == NULL || argv[optind][0] != '+' || argv[optind][1] == '\0') #endif /* GETOPT_COMPAT */ ) optind++; last_nonopt = optind; } /* Special ARGV-element `--' means premature end of options. Skip it like a null option, then exchange with previous non-options as if it were an option, then skip everything else like a non-option. */ if (optind != argc && !my_strcmp(argv[optind], "--")) { optind++; if (first_nonopt != last_nonopt && last_nonopt != optind) exchange((char **) argv); else if (first_nonopt == last_nonopt) first_nonopt = optind; last_nonopt = argc; optind = argc; } /* If we have done all the ARGV-elements, stop the scan and back over any non-options that we skipped and permuted. */ if (optind == argc) { /* Set the next-arg-index to point at the non-options that we previously skipped, so the caller will digest them. */ if (first_nonopt != last_nonopt) optind = first_nonopt; return EOF; } /* If we have come to a non-option and did not permute it, either stop the scan or describe it to the caller and pass it by. */ if ((argv[optind][0] != '-' || argv[optind][1] == '\0') #ifdef GETOPT_COMPAT && (longopts == NULL || argv[optind][0] != '+' || argv[optind][1] == '\0') #endif /* GETOPT_COMPAT */ ) { if (ordering == REQUIRE_ORDER) return EOF; optarg = argv[optind++]; return 1; } /* We have found another option-ARGV-element. Start decoding its characters. */ nextchar = (argv[optind] + 1 + (longopts != NULL && argv[optind][1] == '-')); } if (longopts != NULL && ((argv[optind][0] == '-' && (argv[optind][1] == '-' || long_only)) #ifdef GETOPT_COMPAT || argv[optind][0] == '+' #endif /* GETOPT_COMPAT */ )) { const struct option *p; char *s = nextchar; int exact = 0; int ambig = 0; const struct option *pfound = NULL; int indfound = 0; while (*s && *s != '=') s++; /* Test all options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!my_strncmp(p->name, nextchar, s - nextchar)) { if (s - nextchar == my_strlen(p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else /* Second nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (opterr) fprintf(stderr, "%s: option `%s' is ambiguous\n", argv[0], argv[optind]); nextchar += my_strlen(nextchar); optind++; return BAD_OPTION; } if (pfound != NULL) { option_index = indfound; optind++; if (*s) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) optarg = s + 1; else { if (opterr) { if (argv[optind - 1][1] == '-') /* --option */ fprintf(stderr, "%s: option `--%s' doesn't allow an argument\n", argv[0], pfound->name); else /* +option or -option */ fprintf(stderr, "%s: option `%c%s' doesn't allow an argument\n", argv[0], argv[optind - 1][0], pfound->name); } nextchar += my_strlen(nextchar); return BAD_OPTION; } } else if (pfound->has_arg == 1) { if (optind < argc) optarg = argv[optind++]; else { if (opterr) fprintf(stderr, "%s: option `%s' requires an argument\n", argv[0], argv[optind - 1]); nextchar += my_strlen(nextchar); return optstring[0] == ':' ? ':' : BAD_OPTION; } } nextchar += my_strlen(nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } /* Can't find it as a long option. If this is not getopt_long_only, or the option starts with '--' or is not a valid short option, then it's an error. Otherwise interpret it as a short option. */ if (!long_only || argv[optind][1] == '-' #ifdef GETOPT_COMPAT || argv[optind][0] == '+' #endif /* GETOPT_COMPAT */ || my_index(optstring, *nextchar) == NULL) { if (opterr) { if (argv[optind][1] == '-') /* --option */ fprintf(stderr, "%s: unrecognized option `--%s'\n", argv[0], nextchar); else /* +option or -option */ fprintf(stderr, "%s: unrecognized option `%c%s'\n", argv[0], argv[optind][0], nextchar); } nextchar = (char *) ""; optind++; return BAD_OPTION; } } /* Look at and handle the next option-character. */ { char c = *nextchar++; char *temp = my_index(optstring, c); /* Increment `optind' when we start to process its last character. */ if (*nextchar == '\0') ++optind; if (temp == NULL || c == ':') { if (opterr) { #if 0 if (c < 040 || c >= 0177) fprintf(stderr, "%s: unrecognized option, character code 0%o\n", argv[0], c); else fprintf(stderr, "%s: unrecognized option `-%c'\n", argv[0], c); #else /* 1003.2 specifies the format of this message. */ fprintf(stderr, "%s: illegal option -- %c\n", argv[0], c); #endif } optopt = c; return BAD_OPTION; } if (temp[1] == ':') { if (temp[2] == ':') { /* This is an option that accepts an argument optionally. */ if (*nextchar != '\0') { optarg = nextchar; optind++; } else optarg = 0; nextchar = NULL; } else { /* This is an option that requires an argument. */ if (*nextchar != '\0') { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ optind++; } else if (optind == argc) { if (opterr) { #if 0 fprintf(stderr, "%s: option `-%c' requires an argument\n", argv[0], c); #else /* 1003.2 specifies the format of this message. */ fprintf(stderr, "%s: option requires an argument -- %c\n", argv[0], c); #endif } optopt = c; if (optstring[0] == ':') c = ':'; else c = BAD_OPTION; } else /* We already incremented `optind' once; increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++]; nextchar = NULL; } } return c; } } int getopt(int argc, char *const *argv, const char *optstring) { return _getopt_internal(argc, argv, optstring, (const struct option *) 0, (int *) 0, 0); } int getopt_long(int argc, char *const *argv, const char *options, const struct option *long_options, int *opt_index) { return _getopt_internal(argc, argv, options, long_options, opt_index, 0); } #endif /* _LIBC or not __GNU_LIBRARY__. */ #ifdef TEST /* Compile with -DTEST to make an executable for use in testing the above definition of `getopt'. */ int main(int argc, char **argv) { int c; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; c = getopt(argc, argv, "abc:d:0123456789"); if (c == EOF) break; switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (digit_optind != 0 && digit_optind != this_option_optind) printf("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf("option %c\n", c); break; case 'a': printf("option a\n"); break; case 'b': printf("option b\n"); break; case 'c': printf("option c with value `%s'\n", optarg); break; case BAD_OPTION: break; default: printf("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf("non-option ARGV-elements: "); while (optind < argc) printf("%s ", argv[optind++]); printf("\n"); } exit(0); } #endif /* TEST */ pciutils-3.13.0/pcilmr.c0000644000175000001440000001453114625077054013427 0ustar mjusers/* * The PCI Utilities -- Margining utility main function * * Copyright (c) 2023-2024 KNS Group LLC (YADRO) * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include "lmr/lmr.h" const char program_name[] = "pcilmr"; static void scan_links(struct pci_access *pacc, bool only_ready) { if (only_ready) printf("Links ready for margining:\n"); else printf("Links with Lane Margining at the Receiver capabilities:\n"); bool flag = true; for (struct pci_dev *p = pacc->devices; p; p = p->next) { if (pci_find_cap(p, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED) && margin_port_is_down(p)) { struct pci_dev *down = NULL; struct pci_dev *up = NULL; margin_find_pair(pacc, p, &down, &up); if (down && margin_verify_link(down, up)) { margin_log_bdfs(down, up); if (!only_ready && (margin_check_ready_bit(down) || margin_check_ready_bit(up))) printf(" - Ready"); printf("\n"); flag = false; } } } if (flag) printf("Links not found or you don't have enough privileges.\n"); pci_cleanup(pacc); exit(0); } int main(int argc, char **argv) { struct pci_access *pacc; u8 links_n = 0; struct margin_link *links; bool *checks_status_ports; enum margin_mode mode; /* each link has several receivers -> several results */ struct margin_results **results; u8 *results_n; pacc = pci_alloc(); pci_init(pacc); pci_scan_bus(pacc); margin_print_domain = false; for (struct pci_dev *dev = pacc->devices; dev; dev = dev->next) { if (dev->domain != 0) { margin_print_domain = true; break; } } margin_global_logging = true; struct option long_options[] = { { .name = "margin", .has_arg = no_argument, .flag = NULL, .val = 0 }, { .name = "scan", .has_arg = no_argument, .flag = NULL, .val = 1 }, { .name = "full", .has_arg = no_argument, .flag = NULL, .val = 2 }, { 0, 0, 0, 0 } }; opterr = 0; int c; c = getopt_long(argc, argv, "+", long_options, NULL); switch (c) { case -1: /* no options (strings like component are possible) */ /* FALLTHROUGH */ case 0: mode = MARGIN; break; case 1: mode = SCAN; if (optind == argc) scan_links(pacc, false); else die("Invalid arguments\n\n%s", usage); break; case 2: mode = FULL; break; default: /* unknown option symbol */ mode = MARGIN; optind--; break; } opterr = 1; links = margin_parse_util_args(pacc, argc, argv, mode, &links_n); struct margin_com_args *com_args = links[0].args.common; results = xmalloc(links_n * sizeof(*results)); results_n = xmalloc(links_n * sizeof(*results_n)); checks_status_ports = xmalloc(links_n * sizeof(*checks_status_ports)); for (int i = 0; i < links_n; i++) { enum margin_test_status args_status; if ((args_status = margin_process_args(&links[i])) != MARGIN_TEST_OK) { checks_status_ports[i] = false; results[i] = xmalloc(sizeof(*results[i])); results[i]->test_status = args_status; continue; } checks_status_ports[i] = true; struct margin_params params; struct margin_link_args *link_args = &links[i].args; for (int j = 0; j < link_args->recvs_n; j++) { if (margin_read_params( pacc, link_args->recvs[j] == 6 ? links[i].up_port.dev : links[i].down_port.dev, link_args->recvs[j], ¶ms)) { u8 steps_t = link_args->steps_t ? link_args->steps_t : params.timing_steps; u8 steps_v = link_args->steps_v ? link_args->steps_v : params.volt_steps; u8 parallel_recv = link_args->parallel_lanes > params.max_lanes + 1 ? params.max_lanes + 1 : link_args->parallel_lanes; u8 step_multiplier = link_args->lanes_n / parallel_recv + ((link_args->lanes_n % parallel_recv) > 0); com_args->steps_utility += steps_t * step_multiplier; if (params.ind_left_right_tim) com_args->steps_utility += steps_t * step_multiplier; if (params.volt_support) { com_args->steps_utility += steps_v * step_multiplier; if (params.ind_up_down_volt) com_args->steps_utility += steps_v * step_multiplier; } } } } for (int i = 0; i < links_n; i++) { if (checks_status_ports[i]) results[i] = margin_test_link(&links[i], &results_n[i]); else { results_n[i] = 1; if (results[i]->test_status == MARGIN_TEST_ARGS_RECVS) { margin_log_link(&links[i]); printf("\nInvalid RecNums specified.\n"); } else if (results[i]->test_status == MARGIN_TEST_ARGS_LANES) { margin_log_link(&links[i]); printf("\nInvalid lanes specified.\n"); } } printf("\n----\n\n"); } if (com_args->run_margin) { printf("Results:\n"); printf( "Margining statuses:\nLIM -\tErrorCount exceeded Error Count Limit (found device limit)\n"); printf("NAK -\tDevice didn't execute last command, \n\tso result may be less reliable\n"); printf("THR -\tThe set (using the utility options) \n\tstep threshold has been reached\n\n"); printf("Notations:\nst - steps\n\n"); for (int i = 0; i < links_n; i++) { printf("Link "); margin_log_bdfs(links[i].down_port.dev, links[i].up_port.dev); printf(":\n\n"); margin_results_print_brief(results[i], results_n[i], &links[i].args); if (com_args->save_csv) margin_results_save_csv(results[i], results_n[i], &links[i]); printf("\n"); } } for (int i = 0; i < links_n; i++) margin_free_results(results[i], results_n[i]); free(results_n); free(results); free(com_args); free(links); free(checks_status_ports); pci_cleanup(pacc); return 0; } pciutils-3.13.0/pci.ids.man0000644000175000001440000000726114162155726014024 0ustar mjusers.TH pci.ids 5 "@TODAY@" "@VERSION@" "The PCI Utilities" .SH NAME pci.ids \- list of known identifiers related to PCI devices .SH INTRODUCTION Devices on the PCI bus are identified by a combination of a vendor ID (assigned by the PCI SIG) and device ID (assigned by the vendor). Both IDs are 16-bit integers and the device itself provides no translation to a human-readable string. In addition to the vendor and device, devices also report several other identifiers: .IP \(bu 3 Device class and subclass (two 8-bit numbers) .IP \(bu 3 Programming interface (8-bit number, meaning specific for the subclass) .IP \(bu 3 Subsystem, which identifies the assembly in which the device is contained. A typical example is an Ethernet add-in card: the device is the Ethernet controller chip, while the card plays the role of the subsystem. Subsystems have their vendor ID (from the same namespace as device vendors) and subsystem ID. Generally, the meaning of the subsystem ID depends on the device, but there are cases in which a single subsystem ID is used for many devices - e.g., laptop motherboards. The PCI utilities use the .B pci.ids file to translate all these numeric IDs to strings. .SH KEEPING THE LIST UP-TO-DATE The .B pci.ids file is generated from the PCI ID database, which is maintained at .UR https://pci-ids.ucw.cz/ .UE . If you find any IDs missing from the list, please contribute them to the database. You can use the .B update-pciids command to download the current version of the list. Alternatively, you can use .B lspci -q to query the database online. .SH FILE FORMAT The pci.ids file is a text file in plain ASCII, interpreted line by line. Lines starting with the hash sign are treated as comments are ignored. Comments regarding a specific entry are written immediately before the entry. Vendor entries start with a 4-digit hexadecimal vendor ID, followed by one or more spaces, and the name of the vendor extending to the end of the line. Device entries are placed below the vendor entry. Each device entry consists of a single TAB character, a 4-digit hexadecimal device ID, followed by one or more spaces, and the name of the device extending to the end of the line. Subsystem entries are placed below the device entry. They start with two TAB characters, a 4-digit hexadecimal vendor ID (which must be defined elsewhere in the list), a single space, a 4-digit hexadecimal subsystem ID, one or more spaces, and the name of the subsystem extending to the end of the line. Class entries consist of "C", one space, 2-digit hexadecimal class ID, one or more spaces, and the name of the class. Subclasses are placed below the corresponding class, indented by a single TAB, followed by a 2-digit hexadecimal subclass ID, one or more spaces, and the name of the subclass. Programming interfaces are below the subclass, indented by two TABs, followed by a 2-digit hexadecimal prog-if ID, one or more spaces, and the name. There can be device-independent subsystem IDs, although the web interface of the database does not support them yet. They start with a subsystem vendor line consisting of "S", one space, and a 4-digit hexadecimal vendor ID (which must correspond to an already listed vendor). Subsystems follow on subsequent lines, each indented by one TAB, followed by a 4-digit hexadecimal subsystem ID, one or more spaces, and the name of the subsystem. To ensure extensibility of the format, lines starting with an unrecognized letter followed by a single space are ignored and so are all following TAB-indented lines. .SH FILES .TP .B @IDSDIR@/@PCI_IDS@ Location of the list. .SH SEE ALSO .BR lspci (8), .BR update-pciids (8), .BR pcilib (7) .SH AUTHOR The PCI Utilities are maintained by Martin Mares . pciutils-3.13.0/lib/0000755000175000001440000000000014631121771012530 5ustar mjuserspciutils-3.13.0/lib/names-net.c0000644000175000001440000001220314443575171014571 0ustar mjusers/* * The PCI Library -- Resolving ID's via DNS * * Copyright (c) 2007--2008 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include "internal.h" #include "names.h" #ifdef PCI_USE_DNS /* * Our definition of BYTE_ORDER confuses arpa/nameser_compat.h on * Solaris so we must undef it before including arpa/nameser.h. */ #ifdef PCI_OS_SUNOS #undef BYTE_ORDER #endif #include #include #include #include /* * Unfortunately, there are no portable functions for DNS RR parsing, * so we will do the bit shuffling with our own bare hands. */ #define GET16(x) do { if (p+2 > end) goto err; x = (p[0] << 8) | p[1]; p += 2; } while (0) #define GET32(x) do { if (p+4 > end) goto err; x = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; p += 4; } while (0) enum dns_section { DNS_SEC_QUESTION, DNS_SEC_ANSWER, DNS_SEC_AUTHORITY, DNS_SEC_ADDITIONAL, DNS_NUM_SECTIONS }; struct dns_state { u16 counts[DNS_NUM_SECTIONS]; byte *sections[DNS_NUM_SECTIONS+1]; byte *sec_ptr, *sec_end; /* Result of dns_parse_rr(): */ u16 rr_type; u16 rr_class; u32 rr_ttl; u16 rr_len; byte *rr_data; }; static byte * dns_skip_name(byte *p, byte *end) { while (p < end) { unsigned int x = *p++; if (!x) return p; switch (x & 0xc0) { case 0: /* Uncompressed: x = length */ p += x; break; case 0xc0: /* Indirection: 1 byte more for offset */ p++; return (p < end) ? p : NULL; default: /* RFU */ return NULL; } } return NULL; } static int dns_parse_packet(struct dns_state *s, byte *p, unsigned int plen) { byte *end = p + plen; unsigned int i, j, len; unsigned int UNUSED x; #if 0 /* Dump the packet */ for (i=0; icounts[i]); for (i=0; isections[i] = p; for (j=0; j < s->counts[i]; j++) { p = dns_skip_name(p, end); /* Name */ if (!p) goto err; GET32(x); /* Type and class */ if (i != DNS_SEC_QUESTION) { GET32(x); /* TTL */ GET16(len); /* Length of data */ p += len; if (p > end) goto err; } } } s->sections[i] = p; return 0; err: return -1; } static void dns_init_section(struct dns_state *s, int i) { s->sec_ptr = s->sections[i]; s->sec_end = s->sections[i+1]; } static int dns_parse_rr(struct dns_state *s) { byte *p = s->sec_ptr; byte *end = s->sec_end; if (p == end) return 0; p = dns_skip_name(p, end); if (!p) goto err; GET16(s->rr_type); GET16(s->rr_class); GET32(s->rr_ttl); GET16(s->rr_len); s->rr_data = p; s->sec_ptr = p + s->rr_len; return 1; err: return -1; } char *pci_id_net_lookup(struct pci_access *a, int cat, int id1, int id2, int id3, int id4) { static int resolver_inited; char name[256], dnsname[256], txt[256], *domain; byte answer[4096]; const byte *data; int res, j, dlen; struct dns_state ds; domain = pci_get_param(a, "net.domain"); if (!domain || !domain[0]) return NULL; switch (cat) { case ID_VENDOR: sprintf(name, "%04x", id1); break; case ID_DEVICE: sprintf(name, "%04x.%04x", id2, id1); break; case ID_SUBSYSTEM: sprintf(name, "%04x.%04x.%04x.%04x", id4, id3, id2, id1); break; case ID_GEN_SUBSYSTEM: sprintf(name, "%04x.%04x.s", id2, id1); break; case ID_CLASS: sprintf(name, "%02x.c", id1); break; case ID_SUBCLASS: sprintf(name, "%02x.%02x.c", id2, id1); break; case ID_PROGIF: sprintf(name, "%02x.%02x.%02x.c", id3, id2, id1); break; default: return NULL; } sprintf(dnsname, "%.100s.%.100s", name, domain); a->debug("Resolving %s\n", dnsname); if (!resolver_inited) { resolver_inited = 1; res_init(); } res = res_query(dnsname, ns_c_in, ns_t_txt, answer, sizeof(answer)); if (res < 0) { a->debug("\tfailed, h_errno=%d\n", h_errno); return NULL; } if (dns_parse_packet(&ds, answer, res) < 0) { a->debug("\tMalformed DNS packet received\n"); return NULL; } dns_init_section(&ds, DNS_SEC_ANSWER); while (dns_parse_rr(&ds) > 0) { if (ds.rr_class != ns_c_in || ds.rr_type != ns_t_txt) { a->debug("\tUnexpected RR in answer: class %d, type %d\n", ds.rr_class, ds.rr_type); continue; } data = ds.rr_data; dlen = ds.rr_len; j = 0; while (j < dlen && j+1+data[j] <= dlen) { memcpy(txt, &data[j+1], data[j]); txt[data[j]] = 0; j += 1+data[j]; a->debug("\t\"%s\"\n", txt); if (txt[0] == 'i' && txt[1] == '=') return strdup(txt+2); } } return NULL; } #else char *pci_id_net_lookup(struct pci_access *a UNUSED, int cat UNUSED, int id1 UNUSED, int id2 UNUSED, int id3 UNUSED, int id4 UNUSED) { return NULL; } #endif pciutils-3.13.0/lib/dump.c0000644000175000001440000001063014625075144013646 0ustar mjusers/* * The PCI Library -- Reading of Bus Dumps * * Copyright (c) 1997--2008 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include "internal.h" struct dump_data { int len, allocated; byte data[1]; }; static void dump_config(struct pci_access *a) { pci_define_param(a, "dump.name", "", "Name of the bus dump file to read from"); } static int dump_detect(struct pci_access *a) { char *name = pci_get_param(a, "dump.name"); return name && name[0]; } static void dump_alloc_data(struct pci_dev *dev, int len) { struct dump_data *dd = pci_malloc(dev->access, sizeof(struct dump_data) + len - 1); dd->allocated = len; dd->len = 0; memset(dd->data, 0xff, len); dev->backend_data = dd; } static int dump_validate(char *s, char *fmt) { while (*fmt) { if (*fmt == '#' ? !isxdigit(*s) : *fmt != *s) return 0; fmt++, s++; } return 1; } static void dump_init(struct pci_access *a) { char *name = pci_get_param(a, "dump.name"); FILE *f; char buf[256]; struct pci_dev *dev = NULL; int len, mn, bn, dn, fn, i, j; if (!name) a->error("dump: File name not given."); if (!(f = fopen(name, "r"))) a->error("dump: Cannot open %s: %s", name, strerror(errno)); while (fgets(buf, sizeof(buf)-1, f)) { char *z = strchr(buf, '\n'); if (!z) { fclose(f); a->error("dump: line too long or unterminated"); } *z-- = 0; if (z >= buf && *z == '\r') *z-- = 0; len = z - buf + 1; mn = 0; if (dump_validate(buf, "##:##.# ") && sscanf(buf, "%x:%x.%d", &bn, &dn, &fn) == 3 || dump_validate(buf, "####:##:##.# ") && sscanf(buf, "%x:%x:%x.%d", &mn, &bn, &dn, &fn) == 4 || dump_validate(buf, "#####:##:##.# ") && sscanf(buf, "%x:%x:%x.%d", &mn, &bn, &dn, &fn) == 4 || dump_validate(buf, "######:##:##.# ") && sscanf(buf, "%x:%x:%x.%d", &mn, &bn, &dn, &fn) == 4) { dev = pci_get_dev(a, mn, bn, dn, fn); dump_alloc_data(dev, 256); pci_link_dev(a, dev); } else if (!len) dev = NULL; else if (dev && (dump_validate(buf, "##: ") || dump_validate(buf, "###: ") || dump_validate(buf, "####: ") || dump_validate(buf, "#####: ") || dump_validate(buf, "######: ") || dump_validate(buf, "#######: ") || dump_validate(buf, "########: ")) && sscanf(buf, "%x: ", &i) == 1) { struct dump_data *dd = dev->backend_data; z = strchr(buf, ' ') + 1; while (isxdigit(z[0]) && isxdigit(z[1]) && (!z[2] || z[2] == ' ') && sscanf(z, "%x", &j) == 1 && j < 256) { if (i >= 4096) { fclose(f); a->error("dump: At most 4096 bytes of config space are supported"); } if (i >= dd->allocated) /* Need to re-allocate the buffer */ { dump_alloc_data(dev, 4096); memcpy(((struct dump_data *) dev->backend_data)->data, dd->data, 256); pci_mfree(dd); dd = dev->backend_data; } dd->data[i++] = j; if (i > dd->len) dd->len = i; z += 2; if (*z) z++; } if (*z) { fclose(f); a->error("dump: Malformed line"); } } } fclose(f); } static void dump_cleanup(struct pci_access *a UNUSED) { } static void dump_scan(struct pci_access *a UNUSED) { } static int dump_read(struct pci_dev *d, int pos, byte *buf, int len) { struct dump_data *dd; if (!(dd = d->backend_data)) { struct pci_dev *e = d->access->devices; while (e && (e->domain != d->domain || e->bus != d->bus || e->dev != d->dev || e->func != d->func)) e = e->next; if (!e) return 0; dd = e->backend_data; } if (pos + len > dd->len) return 0; memcpy(buf, dd->data + pos, len); return 1; } static int dump_write(struct pci_dev *d UNUSED, int pos UNUSED, byte *buf UNUSED, int len UNUSED) { d->access->error("Writing to dump files is not supported."); return 0; } static void dump_cleanup_dev(struct pci_dev *d) { if (d->backend_data) { pci_mfree(d->backend_data); d->backend_data = NULL; } } struct pci_methods pm_dump = { .name = "dump", .help = "Reading of register dumps (set the `dump.name' parameter)", .config = dump_config, .detect = dump_detect, .init = dump_init, .cleanup = dump_cleanup, .scan = dump_scan, .fill_info = pci_generic_fill_info, .read = dump_read, .write = dump_write, .cleanup_dev = dump_cleanup_dev, }; pciutils-3.13.0/lib/physmem-access.h0000600000175000001440000000224614564417723015630 0ustar mjusers/* * The PCI Library -- Compiler-specific wrappers for memory mapped I/O * * Copyright (c) 2023 Pali Rohár * * Can be freely distributed and used under the terms of the GNU GPL v2+ * * SPDX-License-Identifier: GPL-2.0-or-later */ /* * FIXME * Unfortunately gcc does not provide architecture independent way to read from * or write to memory mapped I/O. The best approximation is to use volatile and * for the write operation follow it by the read operation from the same address. */ static inline void physmem_writeb(unsigned char value, volatile void *ptr) { *(volatile unsigned char *)ptr = value; } static inline void physmem_writew(unsigned short value, volatile void *ptr) { *(volatile unsigned short *)ptr = value; } static inline void physmem_writel(u32 value, volatile void *ptr) { *(volatile u32 *)ptr = value; } static inline unsigned char physmem_readb(volatile void *ptr) { return *(volatile unsigned char *)ptr; } static inline unsigned short physmem_readw(volatile void *ptr) { return *(volatile unsigned short *)ptr; } static inline u32 physmem_readl(volatile void *ptr) { return *(volatile u32 *)ptr; } pciutils-3.13.0/lib/winrsrc.rc.in0000644000175000001440000000174214334212433015152 0ustar mjusers#include VS_VERSION_INFO VERSIONINFO FILEVERSION @PCILIB_VERSION_WINRC@ PRODUCTVERSION @PCILIB_VERSION_WINRC@ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK #if @DEBUG_BUILD@ FILEFLAGS VS_FF_DEBUG #else FILEFLAGS 0 #endif FILEOS VOS_NT_WINDOWS32 #if @LIBRARY_BUILD@ FILETYPE VFT_DLL #else FILETYPE VFT_APP #endif FILESUBTYPE 0 BEGIN BLOCK "StringFileInfo" BEGIN /* * GNU windres seems that converts 7-bit ASCII strings to UTF-16, * so specify UNICODE/UTF-16 encoding (0x04B0) for these strings. */ BLOCK "040904B0" /* Default U.S. English language, UNICODE/UTF-16 codepage */ BEGIN VALUE "FileDescription", "@DESCRIPTION@" VALUE "FileVersion", "@PCILIB_VERSION@" VALUE "InternalName", "@FILENAME@" VALUE "OriginalFilename", "@FILENAME@" VALUE "ProductName", "pciutils" VALUE "ProductVersion", "@PCILIB_VERSION@" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x0409, 0x04B0 /* Default U.S. English language, UNICODE/UTF-16 codepage */ END END pciutils-3.13.0/lib/sysfs.c0000644000175000001440000003737314626117073014064 0ustar mjusers/* * The PCI Library -- Configuration Access via /sys/bus/pci * * Copyright (c) 2003 Matthew Wilcox * Copyright (c) 1997--2024 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "internal.h" static void sysfs_config(struct pci_access *a) { pci_define_param(a, "sysfs.path", PCI_PATH_SYS_BUS_PCI, "Path to the sysfs device tree"); } static inline char * sysfs_name(struct pci_access *a) { return pci_get_param(a, "sysfs.path"); } static int sysfs_detect(struct pci_access *a) { if (access(sysfs_name(a), R_OK)) { a->debug("...cannot open %s", sysfs_name(a)); return 0; } a->debug("...using %s", sysfs_name(a)); return 1; } static void sysfs_init(struct pci_access *a) { a->fd = -1; a->fd_vpd = -1; } static void sysfs_flush_cache(struct pci_access *a) { if (a->fd >= 0) { close(a->fd); a->fd = -1; } if (a->fd_vpd >= 0) { close(a->fd_vpd); a->fd_vpd = -1; } a->cached_dev = NULL; } static void sysfs_cleanup(struct pci_access *a) { sysfs_flush_cache(a); } #define OBJNAMELEN 1024 static void sysfs_obj_name(struct pci_dev *d, char *object, char *buf) { int n = snprintf(buf, OBJNAMELEN, "%s/devices/%04x:%02x:%02x.%d/%s", sysfs_name(d->access), d->domain, d->bus, d->dev, d->func, object); if (n < 0 || n >= OBJNAMELEN) d->access->error("File name too long"); } #define OBJBUFSIZE 1024 static int sysfs_get_string(struct pci_dev *d, char *object, char *buf, int mandatory) { struct pci_access *a = d->access; int fd, n; char namebuf[OBJNAMELEN]; void (*warn)(char *msg, ...) = (mandatory ? a->error : a->warning); sysfs_obj_name(d, object, namebuf); fd = open(namebuf, O_RDONLY); if (fd < 0) { if (mandatory || errno != ENOENT) warn("Cannot open %s: %s", namebuf, strerror(errno)); return 0; } n = read(fd, buf, OBJBUFSIZE); int read_errno = errno; close(fd); if (n < 0) { warn("Error reading %s: %s", namebuf, strerror(read_errno)); return 0; } if (n >= OBJBUFSIZE) { warn("Value in %s too long", namebuf); return 0; } buf[n] = 0; return 1; } static char * sysfs_deref_link(struct pci_dev *d, char *link_name) { char path[2*OBJNAMELEN], rel_path[OBJNAMELEN]; sysfs_obj_name(d, link_name, path); memset(rel_path, 0, sizeof(rel_path)); if (readlink(path, rel_path, sizeof(rel_path)) < 0) return NULL; sysfs_obj_name(d, "", path); strcat(path, rel_path); // Returns a pointer to malloc'ed memory return realpath(path, NULL); } static int sysfs_get_value(struct pci_dev *d, char *object, int mandatory) { char buf[OBJBUFSIZE]; if (sysfs_get_string(d, object, buf, mandatory)) return strtol(buf, NULL, 0); else return -1; } static void sysfs_get_resources(struct pci_dev *d) { struct pci_access *a = d->access; char namebuf[OBJNAMELEN], buf[256]; struct { pciaddr_t flags, base_addr, size; } lines[10]; int have_bar_bases, have_rom_base, have_bridge_bases; FILE *file; int i; have_bar_bases = have_rom_base = have_bridge_bases = 0; sysfs_obj_name(d, "resource", namebuf); file = fopen(namebuf, "r"); if (!file) a->error("Cannot open %s: %s", namebuf, strerror(errno)); for (i = 0; i < 7+6+4+1; i++) { unsigned long long start, end, size, flags; if (!fgets(buf, sizeof(buf), file)) break; if (sscanf(buf, "%llx %llx %llx", &start, &end, &flags) != 3) a->error("Syntax error in %s", namebuf); if (end > start) size = end - start + 1; else size = 0; if (i < 6) { d->flags[i] = flags; flags &= PCI_ADDR_FLAG_MASK; d->base_addr[i] = start | flags; d->size[i] = size; have_bar_bases = 1; } else if (i == 6) { d->rom_flags = flags; flags &= PCI_ADDR_FLAG_MASK; d->rom_base_addr = start | flags; d->rom_size = size; have_rom_base = 1; } else if (i < 7+6+4) { /* * If kernel was compiled without CONFIG_PCI_IOV option then after * the ROM line for configured bridge device (that which had set * subordinary bus number to non-zero value) are four additional lines * which describe resources behind bridge. For PCI-to-PCI bridges they * are: IO, MEM, PREFMEM and empty. For CardBus bridges they are: IO0, * IO1, MEM0 and MEM1. For unconfigured bridges and other devices * there is no additional line after the ROM line. If kernel was * compiled with CONFIG_PCI_IOV option then after the ROM line and * before the first bridge resource line are six additional lines * which describe IOV resources. Read all remaining lines in resource * file and based on the number of remaining lines (0, 4, 6, 10) parse * resources behind bridge. */ lines[i-7].flags = flags; lines[i-7].base_addr = start; lines[i-7].size = size; } } if (i == 7+4 || i == 7+6+4) { int offset = (i == 7+6+4) ? 6 : 0; for (i = 0; i < 4; i++) { d->bridge_flags[i] = lines[offset+i].flags; d->bridge_base_addr[i] = lines[offset+i].base_addr; d->bridge_size[i] = lines[offset+i].size; } have_bridge_bases = 1; } fclose(file); if (!have_bar_bases) clear_fill(d, PCI_FILL_BASES | PCI_FILL_SIZES | PCI_FILL_IO_FLAGS); if (!have_rom_base) clear_fill(d, PCI_FILL_ROM_BASE); if (!have_bridge_bases) clear_fill(d, PCI_FILL_BRIDGE_BASES); } static void sysfs_scan(struct pci_access *a) { char dirname[1024]; DIR *dir; struct dirent *entry; int n; n = snprintf(dirname, sizeof(dirname), "%s/devices", sysfs_name(a)); if (n < 0 || n >= (int) sizeof(dirname)) a->error("Directory name too long"); dir = opendir(dirname); if (!dir) a->error("Cannot open %s", dirname); while ((entry = readdir(dir))) { struct pci_dev *d; unsigned int dom, bus, dev, func; /* ".", ".." or a special non-device perhaps */ if (entry->d_name[0] == '.') continue; d = pci_alloc_dev(a); if (sscanf(entry->d_name, "%x:%x:%x.%d", &dom, &bus, &dev, &func) < 4) a->error("sysfs_scan: Couldn't parse entry name %s", entry->d_name); /* Ensure kernel provided domain that fits in a signed integer */ if (dom > 0x7fffffff) a->error("sysfs_scan: Invalid domain %x", dom); d->domain = dom; d->bus = bus; d->dev = dev; d->func = func; pci_link_dev(a, d); } closedir(dir); } static void sysfs_fill_slots(struct pci_access *a) { char dirname[1024]; DIR *dir; struct dirent *entry; int n; n = snprintf(dirname, sizeof(dirname), "%s/slots", sysfs_name(a)); if (n < 0 || n >= (int) sizeof(dirname)) a->error("Directory name too long"); dir = opendir(dirname); if (!dir) return; while (entry = readdir(dir)) { char namebuf[OBJNAMELEN], buf[16]; FILE *file; unsigned int dom, bus, dev; int res = 0; struct pci_dev *d; /* ".", ".." or a special non-device perhaps */ if (entry->d_name[0] == '.') continue; n = snprintf(namebuf, OBJNAMELEN, "%s/%s/%s", dirname, entry->d_name, "address"); if (n < 0 || n >= OBJNAMELEN) a->error("File name too long"); file = fopen(namebuf, "r"); /* * Old versions of Linux had a fakephp which didn't have an 'address' * file. There's no useful information to be gleaned from these * devices, pretend they're not there. */ if (!file) continue; if (!fgets(buf, sizeof(buf), file) || (res = sscanf(buf, "%x:%x:%x", &dom, &bus, &dev)) < 3) { /* * In some cases, the slot is not tied to a specific device before * a card gets inserted. This happens for example on IBM pSeries * and we need not warn about it. */ if (res != 2) a->warning("sysfs_fill_slots: Couldn't parse entry address %s", buf); } else { for (d = a->devices; d; d = d->next) if (dom == (unsigned)d->domain && bus == d->bus && dev == d->dev && !d->phy_slot) d->phy_slot = pci_set_property(d, PCI_FILL_PHYS_SLOT, entry->d_name); } fclose(file); } closedir(dir); } static void sysfs_fill_info(struct pci_dev *d, unsigned int flags) { int value, want_class, want_class_ext; if (!d->access->buscentric) { /* * These fields can be read from the config registers, but we want to show * the kernel's view, which has regions and IRQs remapped and other fields * (most importantly classes) possibly fixed if the device is known broken. */ if (want_fill(d, flags, PCI_FILL_IDENT)) { d->vendor_id = sysfs_get_value(d, "vendor", 1); d->device_id = sysfs_get_value(d, "device", 1); } want_class = want_fill(d, flags, PCI_FILL_CLASS); want_class_ext = want_fill(d, flags, PCI_FILL_CLASS_EXT); if (want_class || want_class_ext) { value = sysfs_get_value(d, "class", 1); if (want_class) d->device_class = value >> 8; if (want_class_ext) { d->prog_if = value & 0xff; value = sysfs_get_value(d, "revision", 0); if (value < 0) value = pci_read_byte(d, PCI_REVISION_ID); if (value >= 0) d->rev_id = value; } } if (want_fill(d, flags, PCI_FILL_SUBSYS)) { value = sysfs_get_value(d, "subsystem_vendor", 0); if (value >= 0) { d->subsys_vendor_id = value; value = sysfs_get_value(d, "subsystem_device", 0); if (value >= 0) d->subsys_id = value; } else clear_fill(d, PCI_FILL_SUBSYS); } if (want_fill(d, flags, PCI_FILL_IRQ)) d->irq = sysfs_get_value(d, "irq", 1); if (want_fill(d, flags, PCI_FILL_BASES | PCI_FILL_ROM_BASE | PCI_FILL_SIZES | PCI_FILL_IO_FLAGS | PCI_FILL_BRIDGE_BASES)) sysfs_get_resources(d); if (want_fill(d, flags, PCI_FILL_PARENT)) { unsigned int domain, bus, dev, func; char *path_abs, *path_canon, *name; char path_rel[OBJNAMELEN]; struct pci_dev *parent; /* Construct sysfs path for parent device */ sysfs_obj_name(d, "..", path_rel); path_abs = realpath(path_rel, NULL); name = path_abs ? strrchr(path_abs, '/') : NULL; name = name ? name+1 : name; parent = NULL; if (name && sscanf(name, "%x:%x:%x.%d", &domain, &bus, &dev, &func) == 4 && domain <= 0x7fffffff) for (parent = d->access->devices; parent; parent = parent->next) if (parent->domain == (int)domain && parent->bus == bus && parent->dev == dev && parent->func == func) break; if (parent) { /* Check if parsed BDF address from parent sysfs device is really expected PCI device */ sysfs_obj_name(parent, ".", path_rel); path_canon = realpath(path_rel, NULL); if (!path_canon || strcmp(path_canon, path_abs) != 0) parent = NULL; if (path_canon) free(path_canon); } if (parent) d->parent = parent; else clear_fill(d, PCI_FILL_PARENT); if (path_abs) free(path_abs); } } if (want_fill(d, flags, PCI_FILL_PHYS_SLOT)) { struct pci_dev *pd; sysfs_fill_slots(d->access); for (pd = d->access->devices; pd; pd = pd->next) pd->known_fields |= PCI_FILL_PHYS_SLOT; } if (want_fill(d, flags, PCI_FILL_MODULE_ALIAS)) { char buf[OBJBUFSIZE]; if (sysfs_get_string(d, "modalias", buf, 0)) d->module_alias = pci_set_property(d, PCI_FILL_MODULE_ALIAS, buf); } if (want_fill(d, flags, PCI_FILL_LABEL)) { char buf[OBJBUFSIZE]; if (sysfs_get_string(d, "label", buf, 0)) d->label = pci_set_property(d, PCI_FILL_LABEL, buf); } if (want_fill(d, flags, PCI_FILL_NUMA_NODE)) d->numa_node = sysfs_get_value(d, "numa_node", 0); if (want_fill(d, flags, PCI_FILL_IOMMU_GROUP)) { char *group_link = sysfs_deref_link(d, "iommu_group"); if (group_link) { pci_set_property(d, PCI_FILL_IOMMU_GROUP, basename(group_link)); free(group_link); } } if (want_fill(d, flags, PCI_FILL_DT_NODE)) { char *node = sysfs_deref_link(d, "of_node"); if (node) { pci_set_property(d, PCI_FILL_DT_NODE, node); free(node); } } if (want_fill(d, flags, PCI_FILL_DRIVER)) { char *driver_path = sysfs_deref_link(d, "driver"); if (driver_path) { char *driver = strrchr(driver_path, '/'); driver = driver ? driver+1 : driver_path; pci_set_property(d, PCI_FILL_DRIVER, driver); free(driver_path); } else clear_fill(d, PCI_FILL_DRIVER); } if (want_fill(d, flags, PCI_FILL_RCD_LNK)) { char buf[OBJBUFSIZE]; if (sysfs_get_string(d, "rcd_link_cap", buf, 0)) d->rcd_link_cap = strtoul(buf, NULL, 16); if (sysfs_get_string(d, "rcd_link_ctrl", buf, 0)) d->rcd_link_ctrl = strtoul(buf, NULL, 16); if (sysfs_get_string(d, "rcd_link_status", buf, 0)) d->rcd_link_status = strtoul(buf, NULL, 16); } pci_generic_fill_info(d, flags); } /* Intent of the sysfs_setup() caller */ enum { SETUP_READ_CONFIG = 0, SETUP_WRITE_CONFIG = 1, SETUP_READ_VPD = 2 }; static int sysfs_setup(struct pci_dev *d, int intent) { struct pci_access *a = d->access; char namebuf[OBJNAMELEN]; if (a->cached_dev != d || (intent == SETUP_WRITE_CONFIG && !a->fd_rw)) { sysfs_flush_cache(a); a->cached_dev = d; } if (intent == SETUP_READ_VPD) { if (a->fd_vpd < 0) { sysfs_obj_name(d, "vpd", namebuf); a->fd_vpd = open(namebuf, O_RDONLY); /* No warning on error; vpd may be absent or accessible only to root */ } return a->fd_vpd; } if (a->fd < 0) { sysfs_obj_name(d, "config", namebuf); a->fd_rw = a->writeable || intent == SETUP_WRITE_CONFIG; a->fd = open(namebuf, a->fd_rw ? O_RDWR : O_RDONLY); if (a->fd < 0) a->warning("Cannot open %s", namebuf); } return a->fd; } static int sysfs_read(struct pci_dev *d, int pos, byte *buf, int len) { int fd = sysfs_setup(d, SETUP_READ_CONFIG); int res; if (fd < 0) return 0; res = pread(fd, buf, len, pos); if (res < 0) { d->access->warning("sysfs_read: read failed: %s", strerror(errno)); return 0; } else if (res != len) return 0; return 1; } static int sysfs_write(struct pci_dev *d, int pos, byte *buf, int len) { int fd = sysfs_setup(d, SETUP_WRITE_CONFIG); int res; if (fd < 0) return 0; res = pwrite(fd, buf, len, pos); if (res < 0) { d->access->warning("sysfs_write: write failed: %s", strerror(errno)); return 0; } else if (res != len) { d->access->warning("sysfs_write: tried to write %d bytes at %d, but only %d succeeded", len, pos, res); return 0; } return 1; } static int sysfs_read_vpd(struct pci_dev *d, int pos, byte *buf, int len) { int fd = sysfs_setup(d, SETUP_READ_VPD); int res; if (fd < 0) return 0; res = pread(fd, buf, len, pos); if (res < 0) { d->access->warning("sysfs_read_vpd: read failed: %s", strerror(errno)); return 0; } else if (res != len) return 0; return 1; } static void sysfs_cleanup_dev(struct pci_dev *d) { struct pci_access *a = d->access; if (a->cached_dev == d) sysfs_flush_cache(a); } struct pci_methods pm_linux_sysfs = { .name = "linux-sysfs", .help = "The sys filesystem on Linux", .config = sysfs_config, .detect = sysfs_detect, .init = sysfs_init, .cleanup = sysfs_cleanup, .scan = sysfs_scan, .fill_info = sysfs_fill_info, .read = sysfs_read, .write = sysfs_write, .read_vpd = sysfs_read_vpd, .cleanup_dev = sysfs_cleanup_dev, }; pciutils-3.13.0/lib/i386-io-hurd.h0000644000175000001440000000151114564230650014737 0ustar mjusers/* * The PCI Library -- Access to i386 I/O ports on GNU Hurd * * Copyright (c) 2003 Marco Gerards * Copyright (c) 2003 Martin Mares * Copyright (c) 2006 Samuel Thibault and * Thomas Schwinge * Copyright (c) 2007 Thomas Schwinge * * Can be freely distributed and used under the terms of the GNU GPL v2+ * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "i386-io-access.h" static inline int intel_setup_io(struct pci_access *a UNUSED) { return (ioperm (0, 65535, 1) == -1) ? 0 : 1; } static inline void intel_cleanup_io(struct pci_access *a UNUSED) { ioperm (0, 65535, 0); } static inline void intel_io_lock(void) { } static inline void intel_io_unlock(void) { } pciutils-3.13.0/lib/.gitignore0000644000175000001440000000003511511677734014527 0ustar mjusersconfig.h config.mk libpci.pc pciutils-3.13.0/lib/proc.c0000644000175000001440000001170414601633300013633 0ustar mjusers/* * The PCI Library -- Configuration Access via /proc/bus/pci * * Copyright (c) 1997--2023 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include "internal.h" static void proc_config(struct pci_access *a) { pci_define_param(a, "proc.path", PCI_PATH_PROC_BUS_PCI, "Path to the procfs bus tree"); } static int proc_detect(struct pci_access *a) { char *name = pci_get_param(a, "proc.path"); if (access(name, R_OK)) { a->warning("Cannot open %s", name); return 0; } a->debug("...using %s", name); return 1; } static void proc_init(struct pci_access *a) { a->fd = -1; } static void proc_cleanup(struct pci_access *a) { if (a->fd >= 0) { close(a->fd); a->fd = -1; } } static void proc_scan(struct pci_access *a) { FILE *f; char buf[512]; if (snprintf(buf, sizeof(buf), "%s/devices", pci_get_param(a, "proc.path")) == sizeof(buf)) a->error("File name too long"); f = fopen(buf, "r"); if (!f) a->error("Cannot open %s", buf); while (fgets(buf, sizeof(buf)-1, f)) { struct pci_dev *d = pci_alloc_dev(a); unsigned int dfn, vend, cnt, known; char *driver; int offset; #define F " " PCIADDR_T_FMT cnt = sscanf(buf, "%x %x %x" F F F F F F F F F F F F F F "%n", &dfn, &vend, &d->irq, &d->base_addr[0], &d->base_addr[1], &d->base_addr[2], &d->base_addr[3], &d->base_addr[4], &d->base_addr[5], &d->rom_base_addr, &d->size[0], &d->size[1], &d->size[2], &d->size[3], &d->size[4], &d->size[5], &d->rom_size, &offset); #undef F if (cnt != 9 && cnt != 10 && cnt != 17) a->error("proc: parse error (read only %d items)", cnt); d->bus = dfn >> 8U; d->dev = PCI_SLOT(dfn & 0xff); d->func = PCI_FUNC(dfn & 0xff); d->vendor_id = vend >> 16U; d->device_id = vend & 0xffff; known = 0; if (!a->buscentric) { known |= PCI_FILL_IDENT | PCI_FILL_IRQ | PCI_FILL_BASES; if (cnt >= 10) known |= PCI_FILL_ROM_BASE; if (cnt >= 17) known |= PCI_FILL_SIZES; } if (cnt >= 17) { while (buf[offset] && isspace(buf[offset])) ++offset; driver = &buf[offset]; while (buf[offset] && !isspace(buf[offset])) ++offset; buf[offset] = '\0'; if (driver[0]) { pci_set_property(d, PCI_FILL_DRIVER, driver); known |= PCI_FILL_DRIVER; } } d->known_fields = known; pci_link_dev(a, d); } fclose(f); } static int proc_setup(struct pci_dev *d, int rw) { struct pci_access *a = d->access; if (a->cached_dev != d || a->fd_rw < rw) { char buf[1024]; int e; if (a->fd >= 0) close(a->fd); e = snprintf(buf, sizeof(buf), "%s/%02x/%02x.%d", pci_get_param(a, "proc.path"), d->bus, d->dev, d->func); if (e < 0 || e >= (int) sizeof(buf)) a->error("File name too long"); a->fd_rw = a->writeable || rw; a->fd = open(buf, a->fd_rw ? O_RDWR : O_RDONLY); if (a->fd < 0) { e = snprintf(buf, sizeof(buf), "%s/%04x:%02x/%02x.%d", pci_get_param(a, "proc.path"), d->domain, d->bus, d->dev, d->func); if (e < 0 || e >= (int) sizeof(buf)) a->error("File name too long"); a->fd = open(buf, a->fd_rw ? O_RDWR : O_RDONLY); } if (a->fd < 0) a->warning("Cannot open %s", buf); a->cached_dev = d; } return a->fd; } static int proc_read(struct pci_dev *d, int pos, byte *buf, int len) { int fd = proc_setup(d, 0); int res; if (fd < 0) return 0; res = pread(fd, buf, len, pos); if (res < 0) { d->access->warning("proc_read: read failed: %s", strerror(errno)); return 0; } else if (res != len) return 0; return 1; } static int proc_write(struct pci_dev *d, int pos, byte *buf, int len) { int fd = proc_setup(d, 1); int res; if (fd < 0) return 0; res = pwrite(fd, buf, len, pos); if (res < 0) { d->access->warning("proc_write: write failed: %s", strerror(errno)); return 0; } else if (res != len) { d->access->warning("proc_write: tried to write %d bytes at %d, but only %d succeeded", len, pos, res); return 0; } return 1; } static void proc_cleanup_dev(struct pci_dev *d) { if (d->access->cached_dev == d) d->access->cached_dev = NULL; } struct pci_methods pm_linux_proc = { .name = "linux-proc", .help = "The proc file system on Linux", .config = proc_config, .detect = proc_detect, .init = proc_init, .cleanup = proc_cleanup, .scan = proc_scan, .fill_info = pci_generic_fill_info, .read = proc_read, .write = proc_write, .cleanup_dev = proc_cleanup_dev, }; pciutils-3.13.0/lib/i386-ports.c0000644000175000001440000001622314601633230014531 0ustar mjusers/* * The PCI Library -- Direct Configuration access via i386 Ports * * Copyright (c) 1997--2006 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #define _GNU_SOURCE #include "internal.h" #include #if defined(PCI_OS_LINUX) #include "i386-io-linux.h" #elif defined(PCI_OS_GNU) #include "i386-io-hurd.h" #elif defined(PCI_OS_SUNOS) #include "i386-io-sunos.h" #elif defined(PCI_OS_WINDOWS) #include "i386-io-windows.h" #elif defined(PCI_OS_CYGWIN) #include "i386-io-cygwin.h" #elif defined(PCI_OS_HAIKU) #include "i386-io-haiku.h" #elif defined(PCI_OS_BEOS) #include "i386-io-beos.h" #elif defined(PCI_OS_DJGPP) #include "i386-io-djgpp.h" #elif defined(PCI_OS_OPENBSD) #include "i386-io-openbsd.h" #else #error Do not know how to access I/O ports on this OS. #endif static int conf12_io_enabled = -1; /* -1=haven't tried, 0=failed, 1=succeeded */ static int conf12_setup_io(struct pci_access *a) { if (conf12_io_enabled < 0) conf12_io_enabled = intel_setup_io(a); return conf12_io_enabled; } static void conf12_init(struct pci_access *a) { if (!conf12_setup_io(a)) a->error("No permission to access I/O ports (you probably have to be root)."); } static void conf12_cleanup(struct pci_access *a) { if (conf12_io_enabled > 0) { intel_cleanup_io(a); conf12_io_enabled = -1; } } /* * Before we decide to use direct hardware access mechanisms, we try to do some * trivial checks to ensure it at least _seems_ to be working -- we just test * whether bus 00 contains a host bridge (this is similar to checking * techniques used in XFree86, but ours should be more reliable since we * attempt to make use of direct access hints provided by the PCI BIOS). * * This should be close to trivial, but it isn't, because there are buggy * chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID. */ static int intel_sanity_check(struct pci_access *a, struct pci_methods *m) { struct pci_dev d; memset(&d, 0, sizeof(d)); a->debug("...sanity check"); d.bus = 0; d.func = 0; for (d.dev = 0; d.dev < 32; d.dev++) { u16 class, vendor; if (m->read(&d, PCI_CLASS_DEVICE, (byte *) &class, sizeof(class)) && (class == cpu_to_le16(PCI_CLASS_BRIDGE_HOST) || class == cpu_to_le16(PCI_CLASS_DISPLAY_VGA)) || m->read(&d, PCI_VENDOR_ID, (byte *) &vendor, sizeof(vendor)) && (vendor == cpu_to_le16(PCI_VENDOR_ID_INTEL) || vendor == cpu_to_le16(PCI_VENDOR_ID_COMPAQ))) { a->debug("...outside the Asylum at 0/%02x/0", d.dev); return 1; } } a->debug("...insane"); return 0; } /* * Configuration type 1 */ #define CONFIG_CMD(bus, device_fn, where) (0x80000000 | (bus << 16) | (device_fn << 8) | (where & ~3)) static int conf1_detect(struct pci_access *a) { unsigned int tmp; int res = 0; if (!conf12_setup_io(a)) { a->debug("...no I/O permission"); return 0; } intel_io_lock(); intel_outb (0x01, 0xCFB); tmp = intel_inl (0xCF8); intel_outl (0x80000000, 0xCF8); if (intel_inl (0xCF8) == 0x80000000) res = 1; intel_outl (tmp, 0xCF8); intel_io_unlock(); if (res) res = intel_sanity_check(a, &pm_intel_conf1); return res; } static int conf1_read(struct pci_dev *d, int pos, byte *buf, int len) { int addr = 0xcfc + (pos&3); int res = 1; if (d->domain || pos >= 256) return 0; if (len != 1 && len != 2 && len != 4) return pci_generic_block_read(d, pos, buf, len); intel_io_lock(); intel_outl(0x80000000 | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos&~3), 0xcf8); switch (len) { case 1: buf[0] = intel_inb(addr); break; case 2: ((u16 *) buf)[0] = cpu_to_le16(intel_inw(addr)); break; case 4: ((u32 *) buf)[0] = cpu_to_le32(intel_inl(addr)); break; } intel_io_unlock(); return res; } static int conf1_write(struct pci_dev *d, int pos, byte *buf, int len) { int addr = 0xcfc + (pos&3); int res = 1; if (d->domain || pos >= 256) return 0; if (len != 1 && len != 2 && len != 4) return pci_generic_block_write(d, pos, buf, len); intel_io_lock(); intel_outl(0x80000000 | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos&~3), 0xcf8); switch (len) { case 1: intel_outb(buf[0], addr); break; case 2: intel_outw(le16_to_cpu(((u16 *) buf)[0]), addr); break; case 4: intel_outl(le32_to_cpu(((u32 *) buf)[0]), addr); break; } intel_io_unlock(); return res; } /* * Configuration type 2. Obsolete and brain-damaged, but existing. */ static int conf2_detect(struct pci_access *a) { int res = 0; if (!conf12_setup_io(a)) { a->debug("...no I/O permission"); return 0; } /* This is ugly and tends to produce false positives. Beware. */ intel_io_lock(); intel_outb(0x00, 0xCFB); intel_outb(0x00, 0xCF8); intel_outb(0x00, 0xCFA); if (intel_inb(0xCF8) == 0x00 && intel_inb(0xCFA) == 0x00) res = intel_sanity_check(a, &pm_intel_conf2); intel_io_unlock(); return res; } static int conf2_read(struct pci_dev *d, int pos, byte *buf, int len) { int res = 1; int addr = 0xc000 | (d->dev << 8) | pos; if (d->domain || pos >= 256) return 0; if (d->dev >= 16) /* conf2 supports only 16 devices per bus */ return 0; if (len != 1 && len != 2 && len != 4) return pci_generic_block_read(d, pos, buf, len); intel_io_lock(); intel_outb((d->func << 1) | 0xf0, 0xcf8); intel_outb(d->bus, 0xcfa); switch (len) { case 1: buf[0] = intel_inb(addr); break; case 2: ((u16 *) buf)[0] = cpu_to_le16(intel_inw(addr)); break; case 4: ((u32 *) buf)[0] = cpu_to_le32(intel_inl(addr)); break; } intel_outb(0, 0xcf8); intel_io_unlock(); return res; } static int conf2_write(struct pci_dev *d, int pos, byte *buf, int len) { int res = 1; int addr = 0xc000 | (d->dev << 8) | pos; if (d->domain || pos >= 256) return 0; if (d->dev >= 16) /* conf2 supports only 16 devices per bus */ return 0; if (len != 1 && len != 2 && len != 4) return pci_generic_block_write(d, pos, buf, len); intel_io_lock(); intel_outb((d->func << 1) | 0xf0, 0xcf8); intel_outb(d->bus, 0xcfa); switch (len) { case 1: intel_outb(buf[0], addr); break; case 2: intel_outw(le16_to_cpu(* (u16 *) buf), addr); break; case 4: intel_outl(le32_to_cpu(* (u32 *) buf), addr); break; } intel_outb(0, 0xcf8); intel_io_unlock(); return res; } struct pci_methods pm_intel_conf1 = { .name = "intel-conf1", .help = "Raw I/O port access using Intel conf1 interface", .detect = conf1_detect, .init = conf12_init, .cleanup = conf12_cleanup, .scan = pci_generic_scan, .fill_info = pci_generic_fill_info, .read = conf1_read, .write = conf1_write, }; struct pci_methods pm_intel_conf2 = { .name = "intel-conf2", .help = "Raw I/O port access using Intel conf2 interface", .detect = conf2_detect, .init = conf12_init, .cleanup = conf12_cleanup, .scan = pci_generic_scan, .fill_info = pci_generic_fill_info, .read = conf2_read, .write = conf2_write, }; pciutils-3.13.0/lib/names-parse.c0000644000175000001440000001311214564251265015114 0ustar mjusers/* * The PCI Library -- Parsing of the ID list * * Copyright (c) 1997--2008 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include "internal.h" #include "names.h" #ifdef PCI_COMPRESSED_IDS #include typedef gzFile pci_file; #define pci_gets(f, l, s) gzgets(f, l, s) #define pci_eof(f) gzeof(f) static pci_file pci_open(struct pci_access *a) { pci_file result; size_t len; char *new_name; result = gzopen(a->id_file_name, "rb"); if (result) return result; len = strlen(a->id_file_name); if (len < 3 || memcmp(a->id_file_name + len - 3, ".gz", 3) != 0) return result; new_name = malloc(len - 2); memcpy(new_name, a->id_file_name, len - 3); new_name[len - 3] = 0; pci_set_name_list_path(a, new_name, 1); return gzopen(a->id_file_name, "rb"); } #define pci_close(f) gzclose(f) #define PCI_ERROR(f, err) \ if (!err) { \ int errnum = 0; \ gzerror(f, &errnum); \ if (errnum >= 0) err = NULL; \ else if (errnum == Z_ERRNO) err = "I/O error"; \ else err = zError(errnum); \ } #else typedef FILE * pci_file; #define pci_gets(f, l, s) fgets(l, s, f) #define pci_eof(f) feof(f) #define pci_open(a) fopen(a->id_file_name, "r") #define pci_close(f) fclose(f) #define PCI_ERROR(f, err) if (!err && ferror(f)) err = "I/O error"; #endif static int id_hex(char *p, int cnt) { int x = 0; while (cnt--) { x <<= 4; if (*p >= '0' && *p <= '9') x += (*p - '0'); else if (*p >= 'a' && *p <= 'f') x += (*p - 'a' + 10); else if (*p >= 'A' && *p <= 'F') x += (*p - 'A' + 10); else return -1; p++; } return x; } static inline int id_white_p(int c) { return (c == ' ') || (c == '\t'); } static const char *id_parse_list(struct pci_access *a, pci_file f, int *lino) { char line[MAX_LINE]; char *p; int id1=0, id2=0, id3=0, id4=0; int cat = -1; int nest; static const char parse_error[] = "Parse error"; *lino = 0; while (pci_gets(f, line, sizeof(line))) { (*lino)++; p = line; while (*p && *p != '\n' && *p != '\r') p++; if (!*p && !pci_eof(f)) return "Line too long"; *p = 0; if (p > line && (p[-1] == ' ' || p[-1] == '\t')) *--p = 0; p = line; while (id_white_p(*p)) p++; if (!*p || *p == '#') continue; p = line; while (*p == '\t') p++; nest = p - line; if (!nest) /* Top-level entries */ { if (p[0] == 'C' && p[1] == ' ') /* Class block */ { if ((id1 = id_hex(p+2, 2)) < 0 || !id_white_p(p[4])) return parse_error; cat = ID_CLASS; p += 5; } else if (p[0] == 'S' && p[1] == ' ') { /* Generic subsystem block */ if ((id1 = id_hex(p+2, 4)) < 0 || p[6]) return parse_error; if (!pci_id_lookup(a, 0, ID_VENDOR, id1, 0, 0, 0)) return "Vendor does not exist"; cat = ID_GEN_SUBSYSTEM; continue; } else if (p[0] >= 'A' && p[0] <= 'Z' && p[1] == ' ') { /* Unrecognized block (RFU) */ cat = ID_UNKNOWN; continue; } else /* Vendor ID */ { if ((id1 = id_hex(p, 4)) < 0 || !id_white_p(p[4])) return parse_error; cat = ID_VENDOR; p += 5; } id2 = id3 = id4 = 0; } else if (cat == ID_UNKNOWN) /* Nested entries in RFU blocks are skipped */ continue; else if (nest == 1) /* Nesting level 1 */ switch (cat) { case ID_VENDOR: case ID_DEVICE: case ID_SUBSYSTEM: if ((id2 = id_hex(p, 4)) < 0 || !id_white_p(p[4])) return parse_error; p += 5; cat = ID_DEVICE; id3 = id4 = 0; break; case ID_GEN_SUBSYSTEM: if ((id2 = id_hex(p, 4)) < 0 || !id_white_p(p[4])) return parse_error; p += 5; id3 = id4 = 0; break; case ID_CLASS: case ID_SUBCLASS: case ID_PROGIF: if ((id2 = id_hex(p, 2)) < 0 || !id_white_p(p[2])) return parse_error; p += 3; cat = ID_SUBCLASS; id3 = id4 = 0; break; default: return parse_error; } else if (nest == 2) /* Nesting level 2 */ switch (cat) { case ID_DEVICE: case ID_SUBSYSTEM: if ((id3 = id_hex(p, 4)) < 0 || !id_white_p(p[4]) || (id4 = id_hex(p+5, 4)) < 0 || !id_white_p(p[9])) return parse_error; p += 10; cat = ID_SUBSYSTEM; break; case ID_CLASS: case ID_SUBCLASS: case ID_PROGIF: if ((id3 = id_hex(p, 2)) < 0 || !id_white_p(p[2])) return parse_error; p += 3; cat = ID_PROGIF; id4 = 0; break; default: return parse_error; } else /* Nesting level 3 or more */ return parse_error; while (id_white_p(*p)) p++; if (!*p) return parse_error; if (pci_id_insert(a, cat, id1, id2, id3, id4, p, SRC_LOCAL)) return "Duplicate entry"; } return NULL; } int pci_load_name_list(struct pci_access *a) { pci_file f; int lino; const char *err; pci_free_name_list(a); a->id_load_attempted = 1; if (!(f = pci_open(a))) return 0; err = id_parse_list(a, f, &lino); PCI_ERROR(f, err); pci_close(f); if (err) a->error("%s at %s, line %d\n", err, a->id_file_name, lino); return 1; } void pci_free_name_list(struct pci_access *a) { pci_id_cache_flush(a); pci_id_hash_free(a); pci_id_hwdb_free(a); a->id_load_attempted = 0; } void pci_set_name_list_path(struct pci_access *a, char *name, int to_be_freed) { if (a->free_id_name) free(a->id_file_name); a->id_file_name = name; a->free_id_name = to_be_freed; } pciutils-3.13.0/lib/names-cache.c0000644000175000001440000001265014564361460015052 0ustar mjusers/* * The PCI Library -- ID to Name Cache * * Copyright (c) 2008--2009 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "internal.h" #include "names.h" #ifdef PCI_USE_DNS #include #include #include #include #include #include #include #include static const char cache_version[] = "#PCI-CACHE-1.0"; static char *get_cache_name(struct pci_access *a) { if (!a->id_cache_name) { char *name = pci_get_param(a, "net.cache_name"); if (!name || !name[0]) return NULL; if (strncmp(name, "~/", 2)) a->id_cache_name = pci_strdup(a, name); else { uid_t uid = getuid(); struct passwd *pw = getpwuid(uid); if (!pw) return name; a->id_cache_name = pci_malloc(a, strlen(pw->pw_dir) + strlen(name+1) + 1); sprintf(a->id_cache_name, "%s%s", pw->pw_dir, name+1); } } return a->id_cache_name; } static void create_parent_dirs(struct pci_access *a, char *name) { // Assumes that we have a private copy of the name we can modify char *p = name + strlen(name); while (p > name && *p != '/') p--; if (p == name) return; while (p > name) { // We stand at a slash. Check if the current prefix exists. *p = 0; struct stat st; int res = stat(name, &st); *p = '/'; if (res >= 0) break; // Does not exist yet, move up one directory p--; while (p > name && *p != '/') p--; } // We now stand at the end of the longest existing prefix. // Create all directories to the right of it. for (;;) { p++; while (*p && *p != '/') p++; if (!*p) break; *p = 0; int res = mkdir(name, 0777); if (res < 0) { a->warning("Cannot create directory %s: %s", name, strerror(errno)); *p = '/'; break; } *p = '/'; } } int pci_id_cache_load(struct pci_access *a, int flags) { char *name; char line[MAX_LINE]; FILE *f; int lino; if (a->id_cache_status > 0) return 0; a->id_cache_status = 1; name = get_cache_name(a); if (!name) return 0; a->debug("Using cache %s\n", name); if (flags & PCI_LOOKUP_REFRESH_CACHE) { a->debug("Not loading cache, will refresh everything\n"); a->id_cache_status = 2; return 0; } f = fopen(name, "rb"); if (!f) { a->debug("Cache file does not exist\n"); return 0; } /* FIXME: Compare timestamp with the pci.ids file? */ lino = 0; while (fgets(line, sizeof(line), f)) { char *p = strchr(line, '\n'); lino++; if (p) { *p = 0; if (lino == 1) { if (strcmp(line, cache_version)) { a->debug("Unrecognized cache version %s, ignoring\n", line); break; } continue; } else { int cat, id1, id2, id3, id4, cnt; if (sscanf(line, "%d%x%x%x%x%n", &cat, &id1, &id2, &id3, &id4, &cnt) >= 5) { p = line + cnt; while (*p && *p == ' ') p++; pci_id_insert(a, cat, id1, id2, id3, id4, p, SRC_CACHE); continue; } } } a->warning("Malformed cache file %s (line %d), ignoring", name, lino); break; } if (ferror(f)) a->warning("Error while reading %s", name); fclose(f); return 1; } void pci_id_cache_flush(struct pci_access *a) { int orig_status = a->id_cache_status; FILE *f; unsigned int h; struct id_entry *e, *e2; char hostname[256], *tmpname, *name; int this_pid; a->id_cache_status = 0; if (orig_status < 2) return; name = get_cache_name(a); if (!name) return; create_parent_dirs(a, name); this_pid = getpid(); if (gethostname(hostname, sizeof(hostname)) < 0) hostname[0] = 0; else hostname[sizeof(hostname)-1] = 0; tmpname = pci_malloc(a, strlen(name) + strlen(hostname) + 64); sprintf(tmpname, "%s.tmp-%s-%d", name, hostname, this_pid); f = fopen(tmpname, "wb"); if (!f) { a->warning("Cannot write to %s: %s", name, strerror(errno)); pci_mfree(tmpname); return; } a->debug("Writing cache to %s\n", name); fprintf(f, "%s\n", cache_version); for (h=0; hid_hash[h]; e; e=e->next) if (e->src == SRC_CACHE || e->src == SRC_NET) { /* Negative entries are not written */ if (!e->name[0]) continue; /* Verify that every entry is written at most once */ for (e2=a->id_hash[h]; e2 != e; e2=e2->next) if ((e2->src == SRC_CACHE || e2->src == SRC_NET) && e2->cat == e->cat && e2->id12 == e->id12 && e2->id34 == e->id34) break; if (e2 == e) fprintf(f, "%d %x %x %x %x %s\n", e->cat, pair_first(e->id12), pair_second(e->id12), pair_first(e->id34), pair_second(e->id34), e->name); } fflush(f); if (ferror(f)) a->warning("Error writing %s", name); fclose(f); if (rename(tmpname, name) < 0) { a->warning("Cannot rename %s to %s: %s", tmpname, name, strerror(errno)); unlink(tmpname); } pci_mfree(tmpname); } #else int pci_id_cache_load(struct pci_access *a UNUSED, int flags UNUSED) { a->id_cache_status = 1; return 0; } void pci_id_cache_flush(struct pci_access *a) { a->id_cache_status = 0; pci_mfree(a->id_cache_name); a->id_cache_name = NULL; } #endif void pci_id_cache_dirty(struct pci_access *a) { if (a->id_cache_status >= 1) a->id_cache_status = 2; } pciutils-3.13.0/lib/Makefile0000600000175000001440000001075414566443671014204 0ustar mjusers# Makefile for The PCI Library # (c) 1999--2014 Martin Mares # Expects to be invoked from the top-level Makefile and uses lots of its variables. OBJS=init access generic dump names filter names-hash names-parse names-net names-cache names-hwdb params caps INCL=internal.h pci.h config.h header.h sysdep.h types.h ifdef PCI_HAVE_PM_LINUX_SYSFS OBJS += sysfs endif ifdef PCI_HAVE_PM_LINUX_PROC OBJS += proc endif ifdef PCI_HAVE_PM_INTEL_CONF OBJS += i386-ports endif ifdef PCI_HAVE_PM_MMIO_CONF OBJS += mmio-ports PCI_USE_PHYSMEM = 1 endif ifdef PCI_HAVE_PM_ECAM OBJS += ecam PCI_USE_PHYSMEM = 1 endif ifdef PCI_HAVE_PM_DUMP OBJS += dump endif ifdef PCI_HAVE_PM_FBSD_DEVICE OBJS += fbsd-device CFLAGS += -I/usr/src/sys ifdef FREEBSD_SYS CFLAGS += -I${FREEBSD_SYS} endif endif ifdef PCI_HAVE_PM_OBSD_DEVICE OBJS += obsd-device endif ifdef PCI_HAVE_PM_AIX_DEVICE OBJS += aix-device endif ifdef PCI_HAVE_PM_NBSD_LIBPCI OBJS += nbsd-libpci endif ifdef PCI_HAVE_PM_DARWIN_DEVICE OBJS += darwin endif ifdef PCI_HAVE_PM_SYLIXOS_DEVICE OBJS += sylixos-device endif ifdef PCI_HAVE_PM_HURD_CONF OBJS += hurd endif ifdef PCI_HAVE_PM_WIN32_CFGMGR32 OBJS += emulated OBJS += win32-cfgmgr32 endif ifdef PCI_HAVE_PM_WIN32_KLDBG OBJS += win32-kldbg endif ifdef PCI_HAVE_PM_WIN32_SYSDBG OBJS += win32-sysdbg endif ifdef PCI_OS_WINDOWS OBJS += win32-helpers endif ifdef PCI_USE_PHYSMEM ifndef PCI_OS_WINDOWS ifndef PCI_OS_DJGPP OBJS += physmem-posix endif endif endif ifdef PCI_HAVE_PM_AOS_EXPANSION OBJS += aos-expansion endif all: $(PCILIB) $(PCILIBPC) ifeq ($(SHARED),no) $(PCILIB): $(addsuffix .o,$(OBJS)) rm -f $@ $(AR) rcs $@ $^ $(RANLIB) $@ else ifeq ($(LIBEXT),dll) all: $(PCIIMPDEF) $(PCIIMPLIB) build.def: $(PCIIMPDEF) $(PCIIMPDEF): libpci.ver ver2def.pl perl ver2def.pl libpci.ver $(PCILIB) build.def $(PCIIMPDEF) $(PCIIMPLIB): $(PCIIMPDEF) $(DLLTOOL) --input-def $< --output-lib $@ comma := , dllrsrc.rc: winrsrc.rc.in sed <$< >$@ -e 's,@PCILIB_VERSION@,$(PCILIB_VERSION),' \ -e 's,@PCILIB_VERSION_WINRC@,$(subst .,\$(comma),$(PCILIB_VERSION).0),' \ -e 's,@FILENAME@,$(PCILIB),' \ -e 's,@DESCRIPTION@,libpci,' \ -e 's,@LIBRARY_BUILD@,1,' \ -e 's,@DEBUG_BUILD@,$(if $(findstring -g,$(CFLAGS)),1,0),' dllrsrc.o: dllrsrc.rc $(WINDRES) --input=$< --output=$@ --input-format=rc --output-format=coff OBJS += dllrsrc endif CFLAGS += -fPIC -fvisibility=hidden $(PCILIB): $(addsuffix .o,$(OBJS)) $(CC) -shared $(CFLAGS) $(LDFLAGS) $(PCILIB_LDFLAGS) -o $@ $^ $(LIB_LDLIBS) ifeq ($(LIBEXT),dll) $(PCILIB): build.def endif endif $(PCILIBPC): libpci.pc.in sed <$< >$@ -e 's,@PREFIX@,$(PREFIX),' \ -e 's,@INCDIR@,$(INCDIR),' \ -e 's,@LIBDIR@,$(LIBDIR),' \ -e 's,@IDSDIR@,$(IDSDIR),' \ -e 's,@VERSION@,$(VERSION),' \ -e 's,@LDLIBS@,$(LDLIBS),' \ -e 's,@WITH_LIBS@,$(WITH_LIBS),' init.o: init.c $(INCL) access.o: access.c $(INCL) params.o: params.c $(INCL) i386-ports.o: i386-ports.c $(INCL) i386-io-access.h i386-io-beos.h i386-io-cygwin.h i386-io-djgpp.h i386-io-haiku.h i386-io-hurd.h i386-io-linux.h i386-io-openbsd.h i386-io-sunos.h i386-io-windows.h mmio-ports.o: mmio-ports.c $(INCL) physmem.h physmem-access.h ecam.o: ecam.c $(INCL) physmem.h physmem-access.h proc.o: proc.c $(INCL) sysfs.o: sysfs.c $(INCL) generic.o: generic.c $(INCL) emulated.o: emulated.c $(INCL) syscalls.o: syscalls.c $(INCL) obsd-device.o: obsd-device.c $(INCL) fbsd-device.o: fbsd-device.c $(INCL) aix-device.o: aix-device.c $(INCL) dump.o: dump.c $(INCL) names.o: names.c $(INCL) names.h names-cache.o: names-cache.c $(INCL) names.h names-hash.o: names-hash.c $(INCL) names.h names-net.o: names-net.c $(INCL) names.h names-parse.o: names-parse.c $(INCL) names.h names-hwdb.o: names-hwdb.c $(INCL) names.h filter.o: filter.c $(INCL) nbsd-libpci.o: nbsd-libpci.c $(INCL) hurd.o: hurd.c $(INCL) win32-helpers.o: win32-helpers.c $(INCL) win32-helpers.h win32-cfgmgr32.o: win32-cfgmgr32.c $(INCL) win32-helpers.h win32-kldbg.o: win32-kldbg.c $(INCL) win32-helpers.h win32-sysdbg.o: win32-sysdbg.c $(INCL) win32-helpers.h i386-io-windows.h: win32-helpers.h # MinGW32 toolchain has some required Win32 header files in /ddk subdirectory. # But these header files include another header files from /ddk subdirectory # and expect that build system has already set /ddk subdirectory into includes. # So include /ddk subdirectory of each system predefined include path via -I. ifdef PCI_HAVE_PM_WIN32_CFGMGR32 DDKCFLAGS:=$(shell echo | $(CC) $(CFLAGS) -E -Wp,-v -o /dev/null - 2>&1 | sed -n 's/^ \(.*\)/-I\1\/ddk/p') win32-cfgmgr32.o: override CFLAGS+=$(DDKCFLAGS) endif pciutils-3.13.0/lib/names.c0000644000175000001440000001605514564251265014015 0ustar mjusers/* * The PCI Library -- ID to Name Translation * * Copyright (c) 1997--2014 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include "internal.h" #include "names.h" static char *id_lookup(struct pci_access *a, int flags, int cat, int id1, int id2, int id3, int id4) { char *name; int tried_hwdb = 0; while (!(name = pci_id_lookup(a, flags, cat, id1, id2, id3, id4))) { if ((flags & PCI_LOOKUP_CACHE) && !a->id_cache_status) { if (pci_id_cache_load(a, flags)) continue; } if (!tried_hwdb && !(flags & (PCI_LOOKUP_SKIP_LOCAL | PCI_LOOKUP_NO_HWDB))) { tried_hwdb = 1; if (name = pci_id_hwdb_lookup(a, cat, id1, id2, id3, id4)) { pci_id_insert(a, cat, id1, id2, id3, id4, name, SRC_HWDB); pci_mfree(name); continue; } } if (flags & PCI_LOOKUP_NETWORK) { if (name = pci_id_net_lookup(a, cat, id1, id2, id3, id4)) { pci_id_insert(a, cat, id1, id2, id3, id4, name, SRC_NET); pci_mfree(name); pci_id_cache_dirty(a); } else pci_id_insert(a, cat, id1, id2, id3, id4, "", SRC_NET); /* We want to iterate the lookup to get the allocated ID entry from the hash */ continue; } return NULL; } return (name[0] ? name : NULL); } static char * id_lookup_subsys(struct pci_access *a, int flags, int iv, int id, int isv, int isd) { char *d = NULL; if (iv > 0 && id > 0) /* Per-device lookup */ d = id_lookup(a, flags, ID_SUBSYSTEM, iv, id, isv, isd); if (!d) /* Generic lookup */ d = id_lookup(a, flags, ID_GEN_SUBSYSTEM, isv, isd, 0, 0); if (!d && iv == isv && id == isd) /* Check for subsystem == device */ d = id_lookup(a, flags, ID_DEVICE, iv, id, 0, 0); return d; } static char * format_name(char *buf, int size, int flags, char *name, char *num, char *unknown) { int res; if ((flags & PCI_LOOKUP_NO_NUMBERS) && !name) return NULL; else if (flags & PCI_LOOKUP_NUMERIC) res = snprintf(buf, size, "%s", num); else if (!name) res = snprintf(buf, size, ((flags & PCI_LOOKUP_MIXED) ? "%s [%s]" : "%s %s"), unknown, num); else if (!(flags & PCI_LOOKUP_MIXED)) res = snprintf(buf, size, "%s", name); else res = snprintf(buf, size, "%s [%s]", name, num); if (res >= size && size >= 4) buf[size-2] = buf[size-3] = buf[size-4] = '.'; else if (res < 0 || res >= size) return ""; return buf; } static char * format_name_pair(char *buf, int size, int flags, char *v, char *d, char *num) { int res; if ((flags & PCI_LOOKUP_NO_NUMBERS) && (!v || !d)) return NULL; if (flags & PCI_LOOKUP_NUMERIC) res = snprintf(buf, size, "%s", num); else if (flags & PCI_LOOKUP_MIXED) { if (v && d) res = snprintf(buf, size, "%s %s [%s]", v, d, num); else if (!v) res = snprintf(buf, size, "Device [%s]", num); else /* v && !d */ res = snprintf(buf, size, "%s Device [%s]", v, num); } else { if (v && d) res = snprintf(buf, size, "%s %s", v, d); else if (!v) res = snprintf(buf, size, "Device %s", num); else /* v && !d */ res = snprintf(buf, size, "%s Device %s", v, num+5); } if (res >= size && size >= 4) buf[size-2] = buf[size-3] = buf[size-4] = '.'; else if (res < 0 || res >= size) return ""; return buf; } char * pci_lookup_name(struct pci_access *a, char *buf, int size, int flags, ...) { va_list args; char *v, *d, *cls, *pif; int iv, id, isv, isd, icls, ipif; char numbuf[16], pifbuf[32]; va_start(args, flags); flags |= a->id_lookup_mode; if (!(flags & PCI_LOOKUP_NO_NUMBERS)) { if (a->numeric_ids > 1) flags |= PCI_LOOKUP_MIXED; else if (a->numeric_ids) flags |= PCI_LOOKUP_NUMERIC; } if (flags & PCI_LOOKUP_MIXED) flags &= ~PCI_LOOKUP_NUMERIC; if (!a->id_load_attempted && !(flags & (PCI_LOOKUP_NUMERIC | PCI_LOOKUP_SKIP_LOCAL))) pci_load_name_list(a); switch (flags & 0xffff) { case PCI_LOOKUP_VENDOR: iv = va_arg(args, int); sprintf(numbuf, "%04x", iv); va_end(args); return format_name(buf, size, flags, id_lookup(a, flags, ID_VENDOR, iv, 0, 0, 0), numbuf, "Vendor"); case PCI_LOOKUP_DEVICE: iv = va_arg(args, int); id = va_arg(args, int); sprintf(numbuf, "%04x", id); va_end(args); return format_name(buf, size, flags, id_lookup(a, flags, ID_DEVICE, iv, id, 0, 0), numbuf, "Device"); case PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE: iv = va_arg(args, int); id = va_arg(args, int); sprintf(numbuf, "%04x:%04x", iv, id); v = id_lookup(a, flags, ID_VENDOR, iv, 0, 0, 0); d = id_lookup(a, flags, ID_DEVICE, iv, id, 0, 0); va_end(args); return format_name_pair(buf, size, flags, v, d, numbuf); case PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_VENDOR: isv = va_arg(args, int); sprintf(numbuf, "%04x", isv); v = id_lookup(a, flags, ID_VENDOR, isv, 0, 0, 0); va_end(args); return format_name(buf, size, flags, v, numbuf, "Unknown vendor"); case PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_DEVICE: iv = va_arg(args, int); id = va_arg(args, int); isv = va_arg(args, int); isd = va_arg(args, int); sprintf(numbuf, "%04x", isd); va_end(args); return format_name(buf, size, flags, id_lookup_subsys(a, flags, iv, id, isv, isd), numbuf, "Device"); case PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE | PCI_LOOKUP_SUBSYSTEM: iv = va_arg(args, int); id = va_arg(args, int); isv = va_arg(args, int); isd = va_arg(args, int); v = id_lookup(a, flags, ID_VENDOR, isv, 0, 0, 0); d = id_lookup_subsys(a, flags, iv, id, isv, isd); sprintf(numbuf, "%04x:%04x", isv, isd); va_end(args); return format_name_pair(buf, size, flags, v, d, numbuf); case PCI_LOOKUP_CLASS: icls = va_arg(args, int); sprintf(numbuf, "%04x", icls); cls = id_lookup(a, flags, ID_SUBCLASS, icls >> 8, icls & 0xff, 0, 0); if (!cls && (cls = id_lookup(a, flags, ID_CLASS, icls >> 8, 0, 0, 0))) { if (!(flags & PCI_LOOKUP_NUMERIC)) /* Include full class number */ flags |= PCI_LOOKUP_MIXED; } va_end(args); return format_name(buf, size, flags, cls, numbuf, "Class"); case PCI_LOOKUP_PROGIF: icls = va_arg(args, int); ipif = va_arg(args, int); sprintf(numbuf, "%02x", ipif); pif = id_lookup(a, flags, ID_PROGIF, icls >> 8, icls & 0xff, ipif, 0); if (!pif && icls == 0x0101 && !(ipif & 0x70)) { /* IDE controllers have complex prog-if semantics */ sprintf(pifbuf, "%s%s%s%s%s", (ipif & 0x80) ? " Master" : "", (ipif & 0x08) ? " SecP" : "", (ipif & 0x04) ? " SecO" : "", (ipif & 0x02) ? " PriP" : "", (ipif & 0x01) ? " PriO" : ""); pif = pifbuf; if (*pif) pif++; } va_end(args); return format_name(buf, size, flags, pif, numbuf, "ProgIf"); default: va_end(args); return ""; } } pciutils-3.13.0/lib/i386-io-sunos.h0000600000175000001440000000126014566443671015150 0ustar mjusers/* * The PCI Library -- Access to i386 I/O ports on Solaris * * Copyright (c) 2003 Bill Moore * Copyright (c) 2003--2006 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+ * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include "i386-io-access.h" static int intel_setup_io(struct pci_access *a UNUSED) { return (sysi86(SI86V86, V86SC_IOPL, PS_IOPL) < 0) ? 0 : 1; } static inline void intel_cleanup_io(struct pci_access *a UNUSED) { sysi86(SI86V86, V86SC_IOPL, 0); } static inline void intel_io_lock(void) { } static inline void intel_io_unlock(void) { } pciutils-3.13.0/lib/win32-helpers.h0000600000175000001440000000143314564414275015305 0ustar mjusersconst char *win32_strerror(DWORD win32_error_id); BOOL win32_is_non_nt_system(void); BOOL win32_is_32bit_on_64bit_system(void); BOOL win32_is_32bit_on_win8_64bit_system(void); UINT win32_change_error_mode(UINT new_mode); BOOL win32_have_privilege(LUID luid_privilege); BOOL win32_enable_privilege(LUID luid_privilege, HANDLE *revert_token, BOOL *revert_only_privilege); VOID win32_revert_privilege(LUID luid_privilege, HANDLE revert_token, BOOL revert_only_privilege); BOOL win32_change_token(HANDLE new_token, HANDLE *old_token); VOID win32_revert_to_token(HANDLE token); HANDLE win32_find_and_open_process_for_query(LPCSTR exe_file); HANDLE win32_open_process_token_with_rights(HANDLE process, DWORD rights); BOOL win32_call_func_with_tcb_privilege(BOOL (*function)(LPVOID), LPVOID argument); pciutils-3.13.0/lib/physmem.h0000600000175000001440000000124114564417723014363 0ustar mjusers/* * The PCI Library -- Physical memory mapping API * * Copyright (c) 2023 Pali Rohár * * Can be freely distributed and used under the terms of the GNU GPL v2+ * * SPDX-License-Identifier: GPL-2.0-or-later */ struct physmem; void physmem_init_config(struct pci_access *a); int physmem_access(struct pci_access *a, int w); struct physmem *physmem_open(struct pci_access *a, int w); void physmem_close(struct physmem *physmem); long physmem_get_pagesize(struct physmem *physmem); void *physmem_map(struct physmem *physmem, u64 addr, size_t length, int w); int physmem_unmap(struct physmem *physmem, void *ptr, size_t length); pciutils-3.13.0/lib/init.c0000644000175000001440000003132614566467630013662 0ustar mjusers/* * The PCI Library -- Initialization and related things * * Copyright (c) 1997--2024 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include "internal.h" #ifdef PCI_OS_DJGPP #include /* for __dos_argv0 */ #endif #ifdef PCI_OS_WINDOWS #include /* Force usage of ANSI (char*) variant of GetModuleFileName() function */ #ifdef _WIN32 #ifdef GetModuleFileName #undef GetModuleFileName #endif #define GetModuleFileName GetModuleFileNameA #endif /* Define __ImageBase for all linkers */ #ifdef _WIN32 /* GNU LD provides __ImageBase symbol since 2.19, in previous versions it is * under name _image_base__, so add weak alias for compatibility. */ #ifdef __GNUC__ asm(".weak\t" PCI_STRINGIFY(__MINGW_USYMBOL(__ImageBase)) "\n\t" ".set\t" PCI_STRINGIFY(__MINGW_USYMBOL(__ImageBase)) "," PCI_STRINGIFY(__MINGW_USYMBOL(_image_base__))); #endif /* * MSVC link.exe provides __ImageBase symbol since 12.00 (MSVC 6.0), for * previous versions resolve it at runtime via GetModuleHandleA() which * returns base for main executable or via VirtualQuery() for DLL builds. */ #if defined(_MSC_VER) && _MSC_VER < 1200 static HMODULE get_current_module_handle(void) { #ifdef PCI_SHARED_LIB MEMORY_BASIC_INFORMATION info; size_t len = VirtualQuery(&get_current_module_handle, &info, sizeof(info)); if (len != sizeof(info)) return NULL; return (HMODULE)info.AllocationBase; #else return GetModuleHandleA(NULL); #endif } #define __ImageBase (*(IMAGE_DOS_HEADER *)get_current_module_handle()) #else extern IMAGE_DOS_HEADER __ImageBase; #endif #endif #if defined(_WINDLL) extern HINSTANCE _hModule; #elif defined(_WINDOWS) extern HINSTANCE _hInstance; #endif #endif static struct pci_methods *pci_methods[PCI_ACCESS_MAX] = { NULL, #ifdef PCI_HAVE_PM_LINUX_SYSFS &pm_linux_sysfs, #else NULL, #endif #ifdef PCI_HAVE_PM_LINUX_PROC &pm_linux_proc, #else NULL, #endif #ifdef PCI_HAVE_PM_INTEL_CONF &pm_intel_conf1, &pm_intel_conf2, #else NULL, NULL, #endif #ifdef PCI_HAVE_PM_FBSD_DEVICE &pm_fbsd_device, #else NULL, #endif #ifdef PCI_HAVE_PM_AIX_DEVICE &pm_aix_device, #else NULL, #endif #ifdef PCI_HAVE_PM_NBSD_LIBPCI &pm_nbsd_libpci, #else NULL, #endif #ifdef PCI_HAVE_PM_OBSD_DEVICE &pm_obsd_device, #else NULL, #endif #ifdef PCI_HAVE_PM_DUMP &pm_dump, #else NULL, #endif #ifdef PCI_HAVE_PM_DARWIN_DEVICE &pm_darwin, #else NULL, #endif #ifdef PCI_HAVE_PM_SYLIXOS_DEVICE &pm_sylixos_device, #else NULL, #endif #ifdef PCI_HAVE_PM_HURD_CONF &pm_hurd, #else NULL, #endif #ifdef PCI_HAVE_PM_WIN32_CFGMGR32 &pm_win32_cfgmgr32, #else NULL, #endif #ifdef PCI_HAVE_PM_WIN32_KLDBG &pm_win32_kldbg, #else NULL, #endif #ifdef PCI_HAVE_PM_WIN32_SYSDBG &pm_win32_sysdbg, #else NULL, #endif #ifdef PCI_HAVE_PM_MMIO_CONF &pm_mmio_conf1, &pm_mmio_conf1_ext, #else NULL, NULL, #endif #if defined(PCI_HAVE_PM_ECAM) &pm_ecam, #else NULL, #endif #if defined(PCI_HAVE_PM_AOS_EXPANSION) &pm_aos_expansion, #else NULL, #endif }; // If PCI_ACCESS_AUTO is selected, we probe the access methods in this order static int probe_sequence[] = { // System-specific methods PCI_ACCESS_SYS_BUS_PCI, PCI_ACCESS_PROC_BUS_PCI, PCI_ACCESS_FBSD_DEVICE, PCI_ACCESS_AIX_DEVICE, PCI_ACCESS_NBSD_LIBPCI, PCI_ACCESS_OBSD_DEVICE, PCI_ACCESS_DARWIN, PCI_ACCESS_SYLIXOS_DEVICE, PCI_ACCESS_HURD, PCI_ACCESS_WIN32_CFGMGR32, PCI_ACCESS_WIN32_KLDBG, PCI_ACCESS_WIN32_SYSDBG, PCI_ACCESS_AOS_EXPANSION, // Low-level methods poking the hardware directly PCI_ACCESS_ECAM, PCI_ACCESS_I386_TYPE1, PCI_ACCESS_I386_TYPE2, PCI_ACCESS_MMIO_TYPE1_EXT, PCI_ACCESS_MMIO_TYPE1, -1, }; static void PCI_NONRET pci_generic_error(char *msg, ...) { va_list args; va_start(args, msg); fputs("pcilib: ", stderr); vfprintf(stderr, msg, args); va_end(args); fputc('\n', stderr); exit(1); } static void pci_generic_warn(char *msg, ...) { va_list args; va_start(args, msg); fputs("pcilib: ", stderr); vfprintf(stderr, msg, args); va_end(args); fputc('\n', stderr); } static void pci_generic_debug(char *msg, ...) { va_list args; va_start(args, msg); vfprintf(stdout, msg, args); va_end(args); } static void pci_null_debug(char *msg UNUSED, ...) { } // Memory allocation functions are safe to call if pci_access is not fully initalized or even NULL void * pci_malloc(struct pci_access *a, int size) { void *x = malloc(size); if (!x) (a && a->error ? a->error : pci_generic_error)("Out of memory (allocation of %d bytes failed)", size); return x; } void pci_mfree(void *x) { if (x) free(x); } char * pci_strdup(struct pci_access *a, const char *s) { int len = strlen(s) + 1; char *t = pci_malloc(a, len); memcpy(t, s, len); return t; } int pci_lookup_method(char *name) { int i; for (i=0; iname, name)) return i; return -1; } char * pci_get_method_name(int index) { if (index < 0 || index >= PCI_ACCESS_MAX) return NULL; else if (!pci_methods[index]) return ""; else return pci_methods[index]->name; } #if defined(PCI_OS_WINDOWS) || defined(PCI_OS_DJGPP) static void pci_init_name_list_path(struct pci_access *a) { if ((PCI_PATH_IDS_DIR)[0]) pci_set_name_list_path(a, PCI_PATH_IDS_DIR "\\" PCI_IDS, 0); else { char *path, *sep; size_t len; #if defined(PCI_OS_WINDOWS) && (defined(_WIN32) || defined(_WINDLL) || defined(_WINDOWS)) HMODULE module; size_t size; #if defined(_WIN32) module = (HINSTANCE)&__ImageBase; #elif defined(_WINDLL) module = _hModule; #elif defined(_WINDOWS) module = _hInstance; #endif /* * Module file name can have arbitrary length despite all MS examples say * about MAX_PATH upper limit. This limit does not apply for example when * executable is running from network disk with very long UNC paths or * when using "\\??\\" prefix for specifying executable binary path. * Function GetModuleFileName() returns passed size argument when passed * buffer is too small and does not signal any error. In this case retry * again with larger buffer. */ size = 256; /* initial buffer size (more than sizeof(PCI_IDS)-4) */ retry: path = pci_malloc(a, size); len = GetModuleFileName(module, path, size-sizeof(PCI_IDS)-4); /* 4 for "\\\\?\\" */ if (len >= size-sizeof(PCI_IDS)-4) { free(path); size *= 2; goto retry; } else if (len == 0) path[0] = '\0'; /* * GetModuleFileName() has bugs. On Windows 10 it prepends current drive * letter if path is just pure NT namespace (with "\\??\\" prefix). Such * extra drive letter makes path fully invalid and unusable. So remove * extra drive letter to make path valid again. * Reproduce: CreateProcessW("\\??\\C:\\lspci.exe", ...) */ if (((path[0] >= 'a' && path[0] <= 'z') || (path[0] >= 'A' && path[0] <= 'Z')) && strncmp(path+1, ":\\??\\", 5) == 0) { memmove(path, path+2, len-2); len -= 2; path[len] = '\0'; } /* * GetModuleFileName() has bugs. On Windows 10 it does not add "\\\\?\\" * prefix when path is in native NT UNC namespace. Such path is treated by * WinAPI/DOS functions as standard DOS path relative to the current * directory, hence something completely different. So prepend missing * "\\\\?\\" prefix to make path valid again. * Reproduce: CreateProcessW("\\??\\UNC\\10.0.2.4\\qemu\\lspci.exe", ...) * * If path starts with DOS drive letter and with appended PCI_IDS is * longer than 260 bytes and is without "\\\\?\\" prefix then append it. * This prefix is required for paths and file names with DOS drive letter * longer than 260 bytes. */ if (strncmp(path, "\\UNC\\", 5) == 0 || strncmp(path, "UNC\\", 4) == 0 || (((path[0] >= 'a' && path[0] <= 'z') || (path[0] >= 'A' && path[0] <= 'Z')) && len + sizeof(PCI_IDS) >= 260)) { memmove(path+4, path, len); memcpy(path, "\\\\?\\", 4); len += 4; path[len] = '\0'; } #elif defined(PCI_OS_DJGPP) || defined(PCI_OS_WINDOWS) const char *exe_path; #ifdef PCI_OS_DJGPP exe_path = __dos_argv0; #else exe_path = _pgmptr; #endif len = strlen(exe_path); path = pci_malloc(a, len+sizeof(PCI_IDS)); memcpy(path, exe_path, len+1); #endif sep = strrchr(path, '\\'); if (!sep) { /* * If current module path (current executable for static builds or * current DLL library for shared build) cannot be determined then * fallback to the current directory. */ free(path); pci_set_name_list_path(a, PCI_IDS, 0); } else { memcpy(sep+1, PCI_IDS, sizeof(PCI_IDS)); pci_set_name_list_path(a, path, 1); } } } #elif defined PCI_OS_AMIGAOS static void pci_init_name_list_path(struct pci_access *a) { int len = strlen(PCI_PATH_IDS_DIR); if (!len) pci_set_name_list_path(a, PCI_IDS, 0); else { char last_char = PCI_PATH_IDS_DIR[len - 1]; if (last_char == ':' || last_char == '/') // root or parent char pci_set_name_list_path(a, PCI_PATH_IDS_DIR PCI_IDS, 0); else pci_set_name_list_path(a, PCI_PATH_IDS_DIR "/" PCI_IDS, 0); } } #else static void pci_init_name_list_path(struct pci_access *a) { pci_set_name_list_path(a, PCI_PATH_IDS_DIR "/" PCI_IDS, 0); } #endif #ifdef PCI_USE_DNS static void pci_init_dns(struct pci_access *a) { pci_define_param(a, "net.domain", PCI_ID_DOMAIN, "DNS domain used for resolving of ID's"); a->id_lookup_mode = PCI_LOOKUP_CACHE; char *cache_dir = getenv("XDG_CACHE_HOME"); if (!cache_dir) cache_dir = "~/.cache"; int name_len = strlen(cache_dir) + 32; char *cache_name = pci_malloc(NULL, name_len); snprintf(cache_name, name_len, "%s/pci-ids", cache_dir); struct pci_param *param = pci_define_param(a, "net.cache_name", cache_name, "Name of the ID cache file"); param->value_malloced = 1; } #endif struct pci_access * pci_alloc(void) { struct pci_access *a = pci_malloc(NULL, sizeof(struct pci_access)); int i; memset(a, 0, sizeof(*a)); pci_init_name_list_path(a); #ifdef PCI_USE_DNS pci_init_dns(a); #endif #ifdef PCI_HAVE_HWDB pci_define_param(a, "hwdb.disable", "0", "Do not look up names in UDEV's HWDB if non-zero"); #endif for (i=0; iconfig) pci_methods[i]->config(a); return a; } int pci_init_internal(struct pci_access *a, int skip_method) { if (!a->error) a->error = pci_generic_error; if (!a->warning) a->warning = pci_generic_warn; if (!a->debug) a->debug = pci_generic_debug; if (!a->debugging) a->debug = pci_null_debug; if (a->method != PCI_ACCESS_AUTO) { if (a->method >= PCI_ACCESS_MAX || !pci_methods[a->method]) a->error("This access method is not supported."); a->methods = pci_methods[a->method]; } else { unsigned int i; for (i=0; probe_sequence[i] >= 0; i++) { struct pci_methods *m = pci_methods[probe_sequence[i]]; if (!m) continue; if (skip_method == probe_sequence[i]) continue; a->debug("Trying method %s...", m->name); if (m->detect(a)) { a->debug("...OK\n"); a->methods = m; a->method = probe_sequence[i]; break; } a->debug("...No.\n"); } if (!a->methods) return 0; } a->debug("Decided to use %s\n", a->methods->name); a->methods->init(a); return 1; } void pci_init_v35(struct pci_access *a) { if (!pci_init_internal(a, -1)) a->error("Cannot find any working access method."); } STATIC_ALIAS(void pci_init(struct pci_access *a), pci_init_v35(a)); DEFINE_ALIAS(void pci_init_v30(struct pci_access *a), pci_init_v35); SYMBOL_VERSION(pci_init_v30, pci_init@LIBPCI_3.0); SYMBOL_VERSION(pci_init_v35, pci_init@@LIBPCI_3.5); struct pci_access * pci_clone_access(struct pci_access *a) { struct pci_access *b = pci_alloc(); b->writeable = a->writeable; b->buscentric = a->buscentric; b->debugging = a->debugging; b->error = a->error; b->warning = a->warning; b->debug = a->debug; return b; } void pci_cleanup(struct pci_access *a) { struct pci_dev *d, *e; for (d=a->devices; d; d=e) { e = d->next; pci_free_dev(d); } if (a->methods) a->methods->cleanup(a); pci_free_name_list(a); pci_free_params(a); pci_set_name_list_path(a, NULL, 0); pci_mfree(a); } pciutils-3.13.0/lib/mmio-ports.c0000600000175000001440000002360214601633243014774 0ustar mjusers/* * The PCI Library -- Direct Configuration access via memory mapped ports * * Copyright (c) 2022 Pali Rohár * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "internal.h" #include "physmem.h" #include "physmem-access.h" #include #include #include #include struct mmio_cache { u64 addr_page; u64 data_page; void *addr_map; void *data_map; }; struct mmio_access { struct mmio_cache *cache; struct physmem *physmem; long pagesize; }; static void munmap_regs(struct pci_access *a) { struct mmio_access *macc = a->backend_data; struct mmio_cache *cache = macc->cache; struct physmem *physmem = macc->physmem; long pagesize = macc->pagesize; if (!cache) return; physmem_unmap(physmem, cache->addr_map, pagesize); if (cache->addr_page != cache->data_page) physmem_unmap(physmem, cache->data_map, pagesize); pci_mfree(macc->cache); macc->cache = NULL; } static int mmap_regs(struct pci_access *a, u64 addr_reg, u64 data_reg, int data_off, volatile void **addr, volatile void **data) { struct mmio_access *macc = a->backend_data; struct mmio_cache *cache = macc->cache; struct physmem *physmem = macc->physmem; long pagesize = macc->pagesize; u64 addr_page = addr_reg & ~(pagesize-1); u64 data_page = data_reg & ~(pagesize-1); void *addr_map = (void *)-1; void *data_map = (void *)-1; if (cache && cache->addr_page == addr_page) addr_map = cache->addr_map; if (cache && cache->data_page == data_page) data_map = cache->data_map; if (addr_map == (void *)-1) addr_map = physmem_map(physmem, addr_page, pagesize, 1); if (addr_map == (void *)-1) return 0; if (data_map == (void *)-1) { if (data_page == addr_page) data_map = addr_map; else data_map = physmem_map(physmem, data_page, pagesize, 1); } if (data_map == (void *)-1) { if (!cache || cache->addr_map != addr_map) physmem_unmap(physmem, addr_map, pagesize); return 0; } if (cache && cache->addr_page != addr_page) physmem_unmap(physmem, cache->addr_map, pagesize); if (cache && cache->data_page != data_page && cache->data_page != cache->addr_page) physmem_unmap(physmem, cache->data_map, pagesize); if (!cache) cache = macc->cache = pci_malloc(a, sizeof(*cache)); cache->addr_page = addr_page; cache->data_page = data_page; cache->addr_map = addr_map; cache->data_map = data_map; *addr = (unsigned char *)addr_map + (addr_reg & (pagesize-1)); *data = (unsigned char *)data_map + (data_reg & (pagesize-1)) + data_off; return 1; } static int validate_addrs(const char *addrs) { const char *sep, *next; u64 num; char *endptr; if (!*addrs) return 0; while (1) { next = strchr(addrs, ','); if (!next) next = addrs + strlen(addrs); sep = strchr(addrs, '/'); if (!sep) return 0; if (!isxdigit(*addrs) || !isxdigit(*(sep+1))) return 0; errno = 0; num = strtoull(addrs, &endptr, 16); if (errno || endptr != sep || (num & 3)) return 0; errno = 0; num = strtoull(sep+1, &endptr, 16); if (errno || endptr != next || (num & 3)) return 0; if (!*next) return 1; addrs = next + 1; } } static int get_domain_count(const char *addrs) { int count = 1; while (addrs = strchr(addrs, ',')) { addrs++; count++; } return count; } static int get_domain_addr(const char *addrs, int domain, u64 *addr_reg, u64 *data_reg) { char *endptr; while (domain-- > 0) { addrs = strchr(addrs, ','); if (!addrs) return 0; addrs++; } *addr_reg = strtoull(addrs, &endptr, 16); *data_reg = strtoull(endptr+1, NULL, 16); return 1; } static void conf1_config(struct pci_access *a) { physmem_init_config(a); pci_define_param(a, "mmio-conf1.addrs", "", "Physical addresses of memory mapped Intel conf1 interface"); /* format: 0xaddr1/0xdata1,0xaddr2/0xdata2,... */ } static void conf1_ext_config(struct pci_access *a) { physmem_init_config(a); pci_define_param(a, "mmio-conf1-ext.addrs", "", "Physical addresses of memory mapped Intel conf1 extended interface"); /* format: 0xaddr1/0xdata1,0xaddr2/0xdata2,... */ } static int detect(struct pci_access *a, char *addrs_param_name) { char *addrs = pci_get_param(a, addrs_param_name); if (!*addrs) { a->debug("%s was not specified", addrs_param_name); return 0; } if (!validate_addrs(addrs)) { a->debug("%s has invalid address format %s", addrs_param_name, addrs); return 0; } if (physmem_access(a, 1)) { a->debug("cannot access physical memory: %s", strerror(errno)); return 0; } a->debug("using with %s", addrs); return 1; } static int conf1_detect(struct pci_access *a) { return detect(a, "mmio-conf1.addrs"); } static int conf1_ext_detect(struct pci_access *a) { return detect(a, "mmio-conf1-ext.addrs"); } static char* get_addrs_param_name(struct pci_access *a) { if (a->methods->config == conf1_ext_config) return "mmio-conf1-ext.addrs"; else return "mmio-conf1.addrs"; } static void conf1_init(struct pci_access *a) { char *addrs_param_name = get_addrs_param_name(a); char *addrs = pci_get_param(a, addrs_param_name); struct mmio_access *macc; struct physmem *physmem; long pagesize; if (!*addrs) a->error("Option %s was not specified.", addrs_param_name); if (!validate_addrs(addrs)) a->error("Option %s has invalid address format \"%s\".", addrs_param_name, addrs); physmem = physmem_open(a, 1); if (!physmem) a->error("Cannot open physcal memory: %s.", strerror(errno)); pagesize = physmem_get_pagesize(physmem); if (pagesize <= 0) a->error("Cannot get page size: %s.", strerror(errno)); macc = pci_malloc(a, sizeof(*macc)); macc->cache = NULL; macc->physmem = physmem; macc->pagesize = pagesize; a->backend_data = macc; } static void conf1_cleanup(struct pci_access *a) { struct mmio_access *macc = a->backend_data; munmap_regs(a); physmem_close(macc->physmem); pci_mfree(macc); } static void conf1_scan(struct pci_access *a) { char *addrs_param_name = get_addrs_param_name(a); char *addrs = pci_get_param(a, addrs_param_name); int domain_count = get_domain_count(addrs); int domain; for (domain = 0; domain < domain_count; domain++) pci_generic_scan_domain(a, domain); } static int conf1_ext_read(struct pci_dev *d, int pos, byte *buf, int len) { char *addrs_param_name = get_addrs_param_name(d->access); char *addrs = pci_get_param(d->access, addrs_param_name); volatile void *addr, *data; u64 addr_reg, data_reg; if (pos >= 4096) return 0; if (len != 1 && len != 2 && len != 4) return pci_generic_block_read(d, pos, buf, len); if (!get_domain_addr(addrs, d->domain, &addr_reg, &data_reg)) return 0; if (!mmap_regs(d->access, addr_reg, data_reg, pos&3, &addr, &data)) return 0; physmem_writel(0x80000000 | ((pos & 0xf00) << 16) | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos & 0xfc), addr); physmem_readl(addr); /* write barrier for address */ switch (len) { case 1: buf[0] = physmem_readb(data); break; case 2: ((u16 *) buf)[0] = physmem_readw(data); break; case 4: ((u32 *) buf)[0] = physmem_readl(data); break; } return 1; } static int conf1_read(struct pci_dev *d, int pos, byte *buf, int len) { if (pos >= 256) return 0; return conf1_ext_read(d, pos, buf, len); } static int conf1_ext_write(struct pci_dev *d, int pos, byte *buf, int len) { char *addrs_param_name = get_addrs_param_name(d->access); char *addrs = pci_get_param(d->access, addrs_param_name); volatile void *addr, *data; u64 addr_reg, data_reg; if (pos >= 4096) return 0; if (len != 1 && len != 2 && len != 4) return pci_generic_block_write(d, pos, buf, len); if (!get_domain_addr(addrs, d->domain, &addr_reg, &data_reg)) return 0; if (!mmap_regs(d->access, addr_reg, data_reg, pos&3, &addr, &data)) return 0; physmem_writel(0x80000000 | ((pos & 0xf00) << 16) | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos & 0xfc), addr); physmem_readl(addr); /* write barrier for address */ switch (len) { case 1: physmem_writeb(buf[0], data); break; case 2: physmem_writew(((u16 *) buf)[0], data); break; case 4: physmem_writel(((u32 *) buf)[0], data); break; } /* * write barrier for data * Note that we cannot read from data port because it may have side effect. * Instead we read from address port (which should not have side effect) to * create a barrier between two conf1_write() calls. But this does not have * to be 100% correct as it does not ensure barrier on data port itself. * Correct way is to issue CPU instruction for full hw sync barrier but gcc * does not provide any (builtin) function yet. */ physmem_readl(addr); return 1; } static int conf1_write(struct pci_dev *d, int pos, byte *buf, int len) { if (pos >= 256) return 0; return conf1_ext_write(d, pos, buf, len); } struct pci_methods pm_mmio_conf1 = { .name = "mmio-conf1", .help = "Raw memory mapped I/O port access using Intel conf1 interface", .config = conf1_config, .detect = conf1_detect, .init = conf1_init, .cleanup = conf1_cleanup, .scan = conf1_scan, .fill_info = pci_generic_fill_info, .read = conf1_read, .write = conf1_write, }; struct pci_methods pm_mmio_conf1_ext = { .name = "mmio-conf1-ext", .help = "Raw memory mapped I/O port access using Intel conf1 extended interface", .config = conf1_ext_config, .detect = conf1_ext_detect, .init = conf1_init, .cleanup = conf1_cleanup, .scan = conf1_scan, .fill_info = pci_generic_fill_info, .read = conf1_ext_read, .write = conf1_ext_write, }; pciutils-3.13.0/lib/i386-io-djgpp.h0000644000175000001440000000126614564230650015110 0ustar mjusers/* * The PCI Library -- Access to i386 I/O ports on DJGPP * * Copyright (c) 2010, 2017 Rudolf Marek * * Can be freely distributed and used under the terms of the GNU GPL v2+ * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "i386-io-access.h" static int irq_enabled; static int intel_setup_io(struct pci_access *a UNUSED) { return 1; } static inline void intel_cleanup_io(struct pci_access *a UNUSED) { } static inline void intel_io_lock(void) { asm volatile("" : : : "memory"); irq_enabled = disable(); } static inline void intel_io_unlock(void) { asm volatile("" : : : "memory"); if (irq_enabled) { enable(); } } pciutils-3.13.0/lib/hurd.c0000644000175000001440000002016714601633203013637 0ustar mjusers/* * The PCI Library -- Hurd access via RPCs * * Copyright (c) 2017 Joan Lledó * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #define _GNU_SOURCE #include "internal.h" #include #include #include #include #include #include #include #include #include #include #include /* Server path */ #define _SERVERS_BUS_PCI _SERVERS_BUS "/pci" /* File names */ #define FILE_CONFIG_NAME "config" #define FILE_ROM_NAME "rom" /* Level in the fs tree */ typedef enum { LEVEL_NONE, LEVEL_DOMAIN, LEVEL_BUS, LEVEL_DEV, LEVEL_FUNC } tree_level; /* Check whether there's a pci server */ static int hurd_detect(struct pci_access *a) { int err; struct stat st; err = stat(_SERVERS_BUS_PCI, &st); if (err) { a->error("Could not open file `%s'", _SERVERS_BUS_PCI); return 0; } /* The node must be a directory and a translator */ return S_ISDIR(st.st_mode) && ((st.st_mode & S_ITRANS) == S_IROOT); } /* Empty callbacks, we don't need any special init or cleanup */ static void hurd_init(struct pci_access *a UNUSED) { } static void hurd_cleanup(struct pci_access *a UNUSED) { } /* Each device has its own server path. Allocate space for the port. */ static void hurd_init_dev(struct pci_dev *d) { d->backend_data = pci_malloc(d->access, sizeof(mach_port_t)); *((mach_port_t *) d->backend_data) = MACH_PORT_NULL; } /* Deallocate the port and free its space */ static void hurd_cleanup_dev(struct pci_dev *d) { mach_port_t device_port; device_port = *((mach_port_t *) d->backend_data); mach_port_deallocate(mach_task_self(), device_port); pci_mfree(d->backend_data); d->backend_data = NULL; } static mach_port_t device_port_lookup(struct pci_dev *d) { char server[NAME_MAX]; mach_port_t device_port = *((mach_port_t *) d->backend_data); if (device_port != MACH_PORT_NULL) return device_port; snprintf(server, NAME_MAX, "%s/%04x/%02x/%02x/%01u/%s", _SERVERS_BUS_PCI, d->domain, d->bus, d->dev, d->func, FILE_CONFIG_NAME); device_port = file_name_lookup(server, 0, 0); if (device_port == MACH_PORT_NULL) d->access->error("Cannot find the PCI arbiter"); *((mach_port_t *) d->backend_data) = device_port; return device_port; } /* Walk through the FS tree to see what is allowed for us */ static void enum_devices(const char *parent, struct pci_access *a, int domain, int bus, int dev, int func, tree_level lev) { int ret; DIR *dir; struct dirent *entry; char path[NAME_MAX]; struct pci_dev *d; dir = opendir(parent); if (!dir) { if (errno == EPERM || errno == EACCES) /* The client lacks the permissions to access this function, skip */ return; else a->error("Cannot open directory: %s (%s)", parent, strerror(errno)); } while ((entry = readdir(dir)) != 0) { snprintf(path, NAME_MAX, "%s/%s", parent, entry->d_name); if (entry->d_type == DT_DIR) { if (!strncmp(entry->d_name, ".", NAME_MAX) || !strncmp(entry->d_name, "..", NAME_MAX)) continue; errno = 0; ret = strtol(entry->d_name, 0, 16); if (errno) { if (closedir(dir) < 0) a->warning("Cannot close directory: %s (%s)", parent, strerror(errno)); a->error("Wrong directory name: %s (number expected) probably " "not connected to an arbiter", entry->d_name); } /* * We found a valid directory. * Update the address and switch to the next level. */ switch (lev) { case LEVEL_DOMAIN: domain = ret; break; case LEVEL_BUS: bus = ret; break; case LEVEL_DEV: dev = ret; break; case LEVEL_FUNC: func = ret; break; default: if (closedir(dir) < 0) a->warning("Cannot close directory: %s (%s)", parent, strerror(errno)); a->error("Wrong directory tree, probably not connected to an arbiter"); } enum_devices(path, a, domain, bus, dev, func, lev + 1); } else { if (strncmp(entry->d_name, FILE_CONFIG_NAME, NAME_MAX)) /* We are looking for the config file */ continue; /* We found an available virtual device, add it to our list */ d = pci_alloc_dev(a); d->domain = domain; d->bus = bus; d->dev = dev; d->func = func; pci_link_dev(a, d); } } if (closedir(dir) < 0) a->error("Cannot close directory: %s (%s)", parent, strerror(errno)); } /* Enumerate devices */ static void hurd_scan(struct pci_access *a) { enum_devices(_SERVERS_BUS_PCI, a, -1, -1, -1, -1, LEVEL_DOMAIN); } /* * Read `len' bytes to `buf'. * * Returns error when the number of read bytes does not match `len'. */ static int hurd_read(struct pci_dev *d, int pos, byte * buf, int len) { int err; size_t nread; char *data; mach_port_t device_port = device_port_lookup(d); if (len > 4) return pci_generic_block_read(d, pos, buf, len); data = (char *) buf; err = pci_conf_read(device_port, pos, &data, &nread, len); if (data != (char *) buf) { if (nread > (size_t) len) /* Sanity check for bogus server. */ { vm_deallocate(mach_task_self(), (vm_address_t) data, nread); return 0; } memcpy(buf, data, nread); vm_deallocate(mach_task_self(), (vm_address_t) data, nread); } return !err && nread == (size_t) len; } /* * Write `len' bytes from `buf'. * * Returns error when the number of written bytes does not match `len'. */ static int hurd_write(struct pci_dev *d, int pos, byte * buf, int len) { int err; size_t nwrote; mach_port_t device_port = device_port_lookup(d); if (len > 4) return pci_generic_block_write(d, pos, buf, len); err = pci_conf_write(device_port, pos, (char *) buf, len, &nwrote); return !err && nwrote == (size_t) len; } /* Get requested info from the server */ static int hurd_fill_regions(struct pci_dev *d) { mach_port_t device_port = device_port_lookup(d); struct pci_bar regions[6]; char *buf = (char *) ®ions; size_t size = sizeof(regions); int err = pci_get_dev_regions(device_port, &buf, &size); if (err) return 0; if ((char *) ®ions != buf) { /* Sanity check for bogus server. */ if (size > sizeof(regions)) { vm_deallocate(mach_task_self(), (vm_address_t) buf, size); return 0; } memcpy(®ions, buf, size); vm_deallocate(mach_task_self(), (vm_address_t) buf, size); } for (int i = 0; i < 6; i++) { if (regions[i].size == 0) continue; d->base_addr[i] = regions[i].base_addr; d->base_addr[i] |= regions[i].is_IO; d->base_addr[i] |= regions[i].is_64 << 2; d->base_addr[i] |= regions[i].is_prefetchable << 3; d->size[i] = regions[i].size; } return 1; } static int hurd_fill_rom(struct pci_dev *d) { struct pci_xrom_bar rom; mach_port_t device_port = device_port_lookup(d); char *buf = (char *) &rom; size_t size = sizeof(rom); int err = pci_get_dev_rom(device_port, &buf, &size); if (err) return 0; if ((char *) &rom != buf) { /* Sanity check for bogus server. */ if (size > sizeof(rom)) { vm_deallocate(mach_task_self(), (vm_address_t) buf, size); return 0; } memcpy(&rom, buf, size); vm_deallocate(mach_task_self(), (vm_address_t) buf, size); } d->rom_base_addr = rom.base_addr; d->rom_size = rom.size; return 1; } static void hurd_fill_info(struct pci_dev *d, unsigned int flags) { if (!d->access->buscentric) { if (want_fill(d, flags, PCI_FILL_BASES | PCI_FILL_SIZES)) { if (hurd_fill_regions(d)) clear_fill(d, PCI_FILL_BASES | PCI_FILL_SIZES); } if (want_fill(d, flags, PCI_FILL_ROM_BASE)) { if (hurd_fill_rom(d)) clear_fill(d, PCI_FILL_ROM_BASE); } } pci_generic_fill_info(d, flags); } struct pci_methods pm_hurd = { .name = "hurd", .help = "Hurd access using RPCs", .detect = hurd_detect, .init = hurd_init, .cleanup = hurd_cleanup, .scan = hurd_scan, .fill_info = hurd_fill_info, .read = hurd_read, .write = hurd_write, .init_dev = hurd_init_dev, .cleanup_dev = hurd_cleanup_dev }; pciutils-3.13.0/lib/i386-io-cygwin.h0000644000175000001440000000107514564230650015302 0ustar mjusers/* * The PCI Library -- Access to i386 I/O ports under Windows with CYGWIN * * Copyright (c) 1997--2006 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+ * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "i386-io-access.h" static int intel_setup_io(struct pci_access *a UNUSED) { return (iopl(3) < 0) ? 0 : 1; } static inline void intel_cleanup_io(struct pci_access *a UNUSED) { iopl(0); } static inline void intel_io_lock(void) { } static inline void intel_io_unlock(void) { } pciutils-3.13.0/lib/darwin.c0000600000175000001440000001155514601633135014156 0ustar mjusers/* * The PCI Library -- Darwin kIOACPI access * * Copyright (c) 2013 Apple, Inc. * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include #include #include "internal.h" #include #include #include #include enum { kACPIMethodAddressSpaceRead = 0, kACPIMethodAddressSpaceWrite = 1, kACPIMethodDebuggerCommand = 2, kACPIMethodCount }; #pragma pack(1) typedef UInt32 IOACPIAddressSpaceID; enum { kIOACPIAddressSpaceIDSystemMemory = 0, kIOACPIAddressSpaceIDSystemIO = 1, kIOACPIAddressSpaceIDPCIConfiguration = 2, kIOACPIAddressSpaceIDEmbeddedController = 3, kIOACPIAddressSpaceIDSMBus = 4 }; /* * 64-bit ACPI address */ union IOACPIAddress { UInt64 addr64; struct { unsigned int offset :16; unsigned int function :3; unsigned int device :5; unsigned int bus :8; unsigned int segment :16; unsigned int reserved :16; } pci; }; typedef union IOACPIAddress IOACPIAddress; #pragma pack() struct AddressSpaceParam { UInt64 value; UInt32 spaceID; IOACPIAddress address; UInt32 bitWidth; UInt32 bitOffset; UInt32 options; }; typedef struct AddressSpaceParam AddressSpaceParam; static void darwin_config(struct pci_access *a UNUSED) { } static int darwin_detect(struct pci_access *a) { io_registry_entry_t service; io_connect_t connect; kern_return_t status; service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleACPIPlatformExpert")); if (service) { status = IOServiceOpen(service, mach_task_self(), 0, &connect); IOObjectRelease(service); } if (!service || (kIOReturnSuccess != status)) { a->warning("Cannot open AppleACPIPlatformExpert (add boot arg debug=0x144 & run as root)"); return 0; } a->debug("...using AppleACPIPlatformExpert"); a->fd = connect; return 1; } static void darwin_init(struct pci_access *a UNUSED) { } static void darwin_cleanup(struct pci_access *a UNUSED) { } static int darwin_read(struct pci_dev *d, int pos, byte *buf, int len) { if (!(len == 1 || len == 2 || len == 4)) return pci_generic_block_read(d, pos, buf, len); AddressSpaceParam param; kern_return_t status; param.spaceID = kIOACPIAddressSpaceIDPCIConfiguration; param.bitWidth = len * 8; param.bitOffset = 0; param.options = 0; param.address.pci.offset = pos; param.address.pci.function = d->func; param.address.pci.device = d->dev; param.address.pci.bus = d->bus; param.address.pci.segment = d->domain; param.address.pci.reserved = 0; param.value = -1ULL; size_t outSize = sizeof(param); status = IOConnectCallStructMethod(d->access->fd, kACPIMethodAddressSpaceRead, ¶m, sizeof(param), ¶m, &outSize); if ((kIOReturnSuccess != status)) d->access->error("darwin_read: kACPIMethodAddressSpaceRead failed: %s", mach_error_string(status)); switch (len) { case 1: buf[0] = (u8) param.value; break; case 2: ((u16 *) buf)[0] = cpu_to_le16((u16) param.value); break; case 4: ((u32 *) buf)[0] = cpu_to_le32((u32) param.value); break; } return 1; } static int darwin_write(struct pci_dev *d, int pos, byte *buf, int len) { if (!(len == 1 || len == 2 || len == 4)) return pci_generic_block_write(d, pos, buf, len); AddressSpaceParam param; kern_return_t status; param.spaceID = kIOACPIAddressSpaceIDPCIConfiguration; param.bitWidth = len * 8; param.bitOffset = 0; param.options = 0; param.address.pci.offset = pos; param.address.pci.function = d->func; param.address.pci.device = d->dev; param.address.pci.bus = d->bus; param.address.pci.segment = d->domain; param.address.pci.reserved = 0; switch (len) { case 1: param.value = buf[0]; break; case 2: param.value = le16_to_cpu(((u16 *) buf)[0]); break; case 4: param.value = le32_to_cpu(((u32 *) buf)[0]); break; } size_t outSize = 0; status = IOConnectCallStructMethod(d->access->fd, kACPIMethodAddressSpaceWrite, ¶m, sizeof(param), NULL, &outSize); if ((kIOReturnSuccess != status)) d->access->error("darwin_read: kACPIMethodAddressSpaceWrite failed: %s", mach_error_string(status)); return 1; } struct pci_methods pm_darwin = { .name = "darwin", .help = "Darwin", .config = darwin_config, .detect = darwin_detect, .init = darwin_init, .cleanup = darwin_cleanup, .scan = pci_generic_scan, .fill_info = pci_generic_fill_info, .read = darwin_read, .write = darwin_write, }; pciutils-3.13.0/lib/configure0000755000175000001440000002541414574364215014455 0ustar mjusers#!/bin/sh # Configuration script for the PCI library # (c) 1998--2013 Martin Mares LC_ALL=C export LC_ALL echo_n() { printf '%s' "$*" } if [ -z "$VERSION" ] ; then echo >&2 "Please run the configure script from the top-level Makefile" exit 1 fi echo_n "Configuring libpci for your system..." if [ -z "$HOST" ] ; then sys=`uname -s` rel=`uname -r` realsys="$sys" if [ "$sys" = "AIX" -a -x /usr/bin/oslevel -a -x /usr/sbin/lsattr ] then rel=`/usr/bin/oslevel` proc=`/usr/sbin/lsdev -C -c processor -S available -F name | head -1` cpu=`/usr/sbin/lsattr -F value -l $proc -a type | sed 's/_.*//'` else cpu=`uname -m | sed 's/^i.86-AT386/i386/;s/^i.86$/i386/;s/^sun4u$/sparc64/;s/^i86pc$/i386/;s/^BePC$/i386/;s/^BeMac$/powerpc/;s/^BeBox$/powerpc/'` fi if [ "$sys" = "DragonFly" ] then sys=freebsd fi if [ "$sys" = "GNU/kFreeBSD" ] then sys=kfreebsd fi if [ "$sys" = "GNU" ] then sys=gnu fi if [ "$sys" = "CYGWIN_NT-5.1" -o "$sys" = "CYGWIN_NT-6.0" ] then sys=cygwin fi HOST=${3:-$cpu-$sys} fi [ -n "$RELEASE" ] && rel="${RELEASE}" # CAVEAT: tr on Solaris is a bit weird and the extra [] is otherwise harmless. host=`echo $HOST | sed -e 's/^\([^-]*\)-\([^-]*\)-\([^-]*\)-\([^-]*\)$/\1-\3/' -e 's/^\([^-]*\)-\([^-]*\)-\([^-]*\)$/\1-\2/' -e 's/^\([^-]*\)-\([^-]*\)$/\1--\2/' | tr '[A-Z]' '[a-z]'` cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` sys=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` echo " $host $rel $cpu $sys" c=config.h m=config.mk echo >$c '#define PCI_CONFIG_H' echo >>$c "#define PCI_ARCH_`echo $cpu | tr '[a-z]' '[A-Z]'`" echo >>$c "#define PCI_OS_`echo $sys | tr '[a-z]' '[A-Z]'`" echo >$m 'WITH_LIBS=' echo_n "Looking for access methods..." LIBRESOLV=-lresolv LIBEXT=so EXEEXT= SYSINCLUDE=/usr/include LSPCIDIR=SBINDIR case $sys in linux*) echo_n " sysfs proc mem-ports ecam" echo >>$c '#define PCI_HAVE_PM_LINUX_SYSFS' echo >>$c '#define PCI_HAVE_PM_LINUX_PROC' echo >>$c '#define PCI_HAVE_PM_MMIO_CONF' echo >>$c '#define PCI_HAVE_PM_ECAM' echo >>$c '#define PCI_HAVE_LINUX_BYTEORDER_H' echo >>$c '#define PCI_PATH_PROC_BUS_PCI "/proc/bus/pci"' echo >>$c '#define PCI_PATH_SYS_BUS_PCI "/sys/bus/pci"' echo >>$c '#define PCI_PATH_DEVMEM_DEVICE "/dev/mem"' echo >>$c '#define PCI_PATH_ACPI_MCFG "/sys/firmware/acpi/tables/MCFG"' echo >>$c '#define PCI_PATH_EFI_SYSTAB "/sys/firmware/efi/systab"' case $cpu in i?86|x86_64) echo_n " i386-ports" echo >>$c '#define PCI_HAVE_PM_INTEL_CONF' ;; esac echo >>$c '#define PCI_HAVE_64BIT_ADDRESS' LSPCIDIR=BINDIR ;; sunos) echo_n " mem-ports ecam" case $cpu in i?86) echo_n " i386-ports" echo >>$c "#define PCI_HAVE_PM_INTEL_CONF" ;; esac echo >>$c '#define PCI_HAVE_PM_MMIO_CONF' echo >>$c '#define PCI_HAVE_PM_ECAM' echo >>$c '#define PCI_PATH_DEVMEM_DEVICE "/dev/xsvc"' echo >>$c '#define PCI_PATH_ACPI_MCFG ""' echo >>$c '#define PCI_PATH_EFI_SYSTAB ""' ;; freebsd*|kfreebsd*) echo_n " fbsd-device mem-ports ecam" echo >>$c '#define PCI_HAVE_PM_FBSD_DEVICE' echo >>$c '#define PCI_HAVE_PM_MMIO_CONF' echo >>$c '#define PCI_HAVE_PM_ECAM' echo >>$c '#define PCI_PATH_FBSD_DEVICE "/dev/pci"' echo >>$c '#define PCI_PATH_DEVMEM_DEVICE "/dev/mem"' echo >>$c '#define PCI_PATH_ACPI_MCFG ""' echo >>$c '#define PCI_PATH_EFI_SYSTAB ""' if [ "$sys" != "kfreebsd" ] ; then LIBRESOLV= fi ;; openbsd) echo_n " obsd-device mem-ports ecam" echo >>$c '#define PCI_HAVE_PM_OBSD_DEVICE' echo >>$c '#define PCI_HAVE_PM_MMIO_CONF' echo >>$c '#define PCI_HAVE_PM_ECAM' echo >>$c '#define PCI_PATH_OBSD_DEVICE "/dev/pci"' echo >>$c '#define PCI_PATH_DEVMEM_DEVICE "/dev/mem"' echo >>$c '#define PCI_PATH_ACPI_MCFG "/var/db/acpi/MCFG.*"' echo >>$c '#define PCI_PATH_EFI_SYSTAB ""' case $cpu in i386|amd64) echo_n " i386-ports" echo >>$c '#define PCI_HAVE_PM_INTEL_CONF' echo >>$m 'WITH_LIBS+=-l'$cpu ;; esac LIBRESOLV= ;; darwin*) echo_n " darwin" echo >>$c '#define PCI_HAVE_PM_DARWIN_DEVICE' echo >>$m 'WITH_LIBS+=-lresolv -framework CoreFoundation -framework IOKit' echo >>$c '#define PCI_HAVE_64BIT_ADDRESS' LIBRESOLV= LIBEXT=dylib SYSINCLUDE=$(xcrun --sdk macosx --show-sdk-path)/usr/include ;; aix) echo_n " aix-device" echo >>$c '#define PCI_HAVE_PM_AIX_DEVICE' echo >>$m 'CFLAGS=-g' echo >>$m 'INSTALL=installbsd' echo >>$m 'DIRINSTALL=mkdir -p' ;; netbsd) echo_n " nbsd-libpci mem-ports ecam" echo >>$c '#define PCI_HAVE_PM_NBSD_LIBPCI' echo >>$c '#define PCI_HAVE_PM_MMIO_CONF' echo >>$c '#define PCI_HAVE_PM_ECAM' echo >>$c '#define PCI_PATH_NBSD_DEVICE "/dev/pci0"' echo >>$c '#define PCI_PATH_DEVMEM_DEVICE "/dev/mem"' echo >>$c '#define PCI_PATH_ACPI_MCFG ""' echo >>$c '#define PCI_PATH_EFI_SYSTAB ""' echo >>$c '#define PCI_HAVE_64BIT_ADDRESS' echo >>$m 'LIBNAME=libpciutils' echo >>$m 'WITH_LIBS+=-lpci' LIBRESOLV= ;; gnu) echo_n " hurd i386-ports" echo >>$c '#define PCI_HAVE_PM_HURD_CONF' echo >>$c '#define PCI_HAVE_PM_INTEL_CONF' ;; djgpp) echo_n " i386-ports" echo >>$c '#define PCI_HAVE_PM_INTEL_CONF' EXEEXT=.exe ;; cygwin|windows) echo_n " win32-cfgmgr32 win32-kldbg win32-sysdbg" echo >>$c '#define PCI_HAVE_64BIT_ADDRESS' echo >>$c '#define PCI_HAVE_PM_WIN32_CFGMGR32' echo >>$c '#define PCI_HAVE_PM_WIN32_KLDBG' echo >>$c '#define PCI_HAVE_PM_WIN32_SYSDBG' # Warning: MinGW-w64 (incorrectly) provides cfgmgr32 functions # also in other import libraries, not only in libcfgmgr32.a. # So always set -lcfgmgr32 as a first library parameter which # instruct linker to prefer symbols from cfgmgr32.dll. echo >>$m 'WITH_LIBS+=-lcfgmgr32' case $cpu in i?86|x86_64) echo_n " i386-ports" echo >>$c '#define PCI_HAVE_PM_INTEL_CONF' if [ "$sys" = "cygwin" ] ; then # ioperm is cygwin specific library and used only by lib/i386-io-cygwin.h echo >>$m 'WITH_LIBS+=-lioperm' elif [ "$sys" = "windows" ] ; then # advapi32 is windows system library and used only by lib/i386-io-windows.h echo >>$m 'WITH_LIBS+=-ladvapi32' fi ;; esac EXEEXT=.exe LIBEXT=dll ;; beos|haiku) echo_n " mem-ports ecam" case $cpu in i?86|x86_64) echo_n " i386-ports" echo >>$c '#define PCI_HAVE_PM_INTEL_CONF' ;; esac echo >>$c '#define PCI_HAVE_PM_MMIO_CONF' echo >>$c '#define PCI_HAVE_PM_ECAM' echo >>$c '#define PCI_PATH_DEVMEM_DEVICE "/dev/misc/mem"' echo >>$c '#define PCI_PATH_ACPI_MCFG ""' echo >>$c '#define PCI_PATH_EFI_SYSTAB ""' if [ "$sys" != "beos" ] ; then LIBRESOLV=-lnetwork fi ;; sylixos) echo >>$c '#define PCI_PATH_SYLIXOS_DEVICE "/proc/pci"' echo >>$c '#define PCI_HAVE_64BIT_ADDRESS' echo >>$c '#define PCI_HAVE_PM_SYLIXOS_DEVICE' IDSDIR="/etc/pci" LIBRESOLV= ;; amigaos) echo_n " aos-expansion" echo >>$c '#define PCI_HAVE_STDINT_H' echo >>$c '#define PCI_HAVE_PM_AOS_EXPANSION' IDSDIR="DEVS:" echo >>$m 'CC=gcc' ;; *) echo " Unfortunately, your OS is not supported by the PCI Library" exit 1 ;; esac echo >>$m "LIBEXT="$LIBEXT echo >>$m "EXEEXT="$EXEEXT echo >>$m "LSPCIDIR=\$($LSPCIDIR)" echo >>$c '#define PCI_HAVE_PM_DUMP' echo " dump" echo_n "Checking for zlib support... " if [ "$ZLIB" = yes -o "$ZLIB" = no ] ; then echo "$ZLIB (set manually)" else if [ -f "$SYSINCLUDE/zlib.h" -o -f /usr/local/include/zlib.h ] ; then ZLIB=yes else ZLIB=no fi echo "$ZLIB (auto-detected)" fi if [ "$ZLIB" = yes ] ; then echo >>$c '#define PCI_COMPRESSED_IDS' echo >>$c '#define PCI_IDS "pci.ids.gz"' echo >>$m 'LIBZ=-lz' echo >>$m 'WITH_LIBS+=$(LIBZ)' else echo >>$c '#define PCI_IDS "pci.ids"' fi echo >>$c "#define PCI_PATH_IDS_DIR \"$IDSDIR\"" echo_n "Checking for DNS support... " if [ "$DNS" = yes -o "$DNS" = no ] ; then echo "$DNS (set manually)" else if [ "$sys" != "windows" -a -f "$SYSINCLUDE/resolv.h" ] ; then DNS=yes else DNS=no fi echo "$DNS (auto-detected)" fi if [ "$DNS" = yes ] ; then echo >>$c "#define PCI_USE_DNS" echo >>$c "#define PCI_ID_DOMAIN \"pci.id.ucw.cz\"" echo >>$m "WITH_LIBS+=$LIBRESOLV" fi if [ "$sys" = linux ] ; then echo_n "Checking for libkmod... " LIBKMOD_DETECTED= if [ -z "$PKG_CONFIG" ] ; then PKG_CONFIG=pkg-config fi if [ "$LIBKMOD" != no ] ; then if ! command -v $PKG_CONFIG >/dev/null ; then echo_n "($PKG_CONFIG not found) " elif $PKG_CONFIG libkmod ; then LIBKMOD_DETECTED=1 fi fi if [ "$LIBKMOD" = yes -o "$LIBKMOD" = no ] ; then echo "$LIBKMOD (set manually)" if [ "$LIBKMOD" = yes -a -z "$LIBKMOD_DETECTED" ] ; then echo "Requested use of libkmod, but it is not available. Giving up." exit 1 fi else if [ -n "$LIBKMOD_DETECTED" ] ; then LIBKMOD=yes else LIBKMOD=no fi echo "$LIBKMOD (auto-detected)" fi if [ "$LIBKMOD" = yes ] ; then echo >>$c "#define PCI_USE_LIBKMOD" echo >>$m "LIBKMOD_CFLAGS=$($PKG_CONFIG --cflags libkmod)" echo >>$m "LIBKMOD_LIBS=$($PKG_CONFIG --libs libkmod)" fi echo_n "Checking for udev hwdb support... " if [ "$HWDB" = yes -o "$HWDB" = no ] ; then echo "$HWDB (set manually)" else if `command -v $PKG_CONFIG >/dev/null && $PKG_CONFIG --atleast-version=196 libudev` ; then HWDB=yes else HWDB=no fi echo "$HWDB (auto-detected)" fi if [ "$HWDB" = yes ] ; then echo >>$c '#define PCI_HAVE_HWDB' echo >>$m 'LIBUDEV=-ludev' echo >>$m 'WITH_LIBS+=$(LIBUDEV)' fi fi echo "Checking whether to build a shared library... $SHARED (set manually)" if [ "$SHARED" = no ] ; then echo >>$m 'PCILIB=$(LIBNAME).a' echo >>$m 'LDLIBS=$(WITH_LIBS)' echo >>$m 'LIB_LDLIBS=' else if [ "$LIBEXT" = so ]; then echo >>$m 'PCILIB=$(LIBNAME).$(LIBEXT).$(VERSION)' elif [ "$LIBEXT" = dll ]; then echo >>$m 'PCILIB=$(LIBNAME)$(ABI_VERSION).$(LIBEXT)' else echo >>$m 'PCILIB=$(LIBNAME).$(VERSION).$(LIBEXT)' fi # We link the dependencies _to_ the library, so we do not need explicit deps in .pc echo >>$m 'LDLIBS=' echo >>$m 'LIB_LDLIBS=$(WITH_LIBS)' echo >>$c '#define PCI_SHARED_LIB' if [ "$LIBEXT" = so ]; then echo >>$m 'PCILIB_LDFLAGS+=-Wl,-soname,$(LIBNAME).$(LIBEXT).$(ABI_VERSION)' echo >>$m 'PCILIB_LDFLAGS+=-Wl,--version-script=libpci.ver' elif [ "$LIBEXT" = dylib ]; then echo >>$m 'PCILIB_LDFLAGS+=-Wl,-install_name,$(LIBDIR)/$(PCILIB)' elif [ "$LIBEXT" = dll ]; then echo >>$m 'PCIIMPDEF=$(LIBNAME)$(ABI_VERSION).def' # GCC's -fvisibility=hidden is broken for Windows targets, use -Wl,--exclude-all-symbols instead (supported since GNU LD 2.21) echo >>$m 'PCILIB_LDFLAGS+=-Wl,--exclude-all-symbols' fi fi echo >>$m 'PCILIBPC=$(LIBNAME).pc' if [ "$SHARED" != no ] && [ "$LIBEXT" = dll ]; then echo >>$m 'PCIIMPLIB=$(PCILIB).a' else echo >>$m 'PCIIMPLIB=$(PCILIB)' fi echo >>$c "#define PCILIB_VERSION \"$VERSION\"" echo >>$c "#define PCILIB_DATE_AMIGAOS \"`echo $DATE | sed 's/\(....\)-\(..\)-\(..\)/\3.\2.\1/'`\"" sed '/"/{s/^#define \([^ ]*\) "\(.*\)"$/\1=\2/;p;d;};s/^#define \(.*\)/\1=1/' <$c >>$m pciutils-3.13.0/lib/libpci.ver0000644000175000001440000000337714626120251014516 0ustar mjusers/* Version script for the libpci */ /* * Visibility declarations in the source take precedence over this script, * so we can boldly declare pci_* as public and still keep the internal * functions properly hidden. * * To preserve compatibility of Windows DLL file, always add new symbol at * the end of file and never change order of symbols nor version sections. * On Windows the last referenced version of the symbol is the default one. * For PE/COFF targets this file is processed by ver2def.pl script and not * by GNU LD linker like for ELF targets. */ LIBPCI_3.0 { global: pci_alloc; pci_cleanup; pci_fill_info; pci_filter_init; pci_filter_match; pci_filter_parse_id; pci_filter_parse_slot; pci_free_dev; pci_free_name_list; pci_get_dev; pci_get_method_name; pci_get_param; pci_id_cache_flush; pci_init; pci_load_name_list; pci_lookup_method; pci_lookup_name; pci_read_block; pci_read_byte; pci_read_long; pci_read_word; pci_scan_bus; pci_set_name_list_path; pci_set_param; pci_setup_cache; pci_walk_params; pci_write_block; pci_write_byte; pci_write_long; pci_write_word; local: *; }; LIBPCI_3.1 { global: pci_fill_info; pci_find_cap; pci_read_vpd; }; LIBPCI_3.2 { global: pci_fill_info; }; LIBPCI_3.3 { global: pci_fill_info; pci_filter_init; pci_filter_match; pci_filter_parse_id; pci_filter_parse_slot; }; LIBPCI_3.4 { global: pci_fill_info; }; LIBPCI_3.5 { global: pci_init; pci_fill_info; }; LIBPCI_3.6 { global: pci_get_string_property; }; LIBPCI_3.7 { global: pci_find_cap_nr; }; LIBPCI_3.8 { global: pci_fill_info; pci_filter_init; pci_filter_match; pci_filter_parse_id; pci_filter_parse_slot; }; LIBPCI_3.13 { global: pci_fill_info; }; pciutils-3.13.0/lib/access.c0000644000175000001440000001415614626120324014141 0ustar mjusers/* * The PCI Library -- User Access * * Copyright (c) 1997--2022 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include "internal.h" void pci_scan_bus(struct pci_access *a) { a->methods->scan(a); } struct pci_dev * pci_alloc_dev(struct pci_access *a) { struct pci_dev *d = pci_malloc(a, sizeof(struct pci_dev)); memset(d, 0, sizeof(*d)); d->access = a; d->methods = a->methods; d->hdrtype = -1; d->numa_node = -1; if (d->methods->init_dev) d->methods->init_dev(d); return d; } int pci_link_dev(struct pci_access *a, struct pci_dev *d) { d->next = a->devices; a->devices = d; /* * Applications compiled with older versions of libpci do not expect * 32-bit domain numbers. To keep them working, we keep a 16-bit * version of the domain number at the previous location in struct * pci_dev. This will keep backward compatibility on systems which * don't require large domain numbers. */ if (d->domain > 0xffff) d->domain_16 = 0xffff; else d->domain_16 = d->domain; return 1; } struct pci_dev * pci_get_dev(struct pci_access *a, int domain, int bus, int dev, int func) { struct pci_dev *d = pci_alloc_dev(a); d->domain = domain; d->bus = bus; d->dev = dev; d->func = func; return d; } static void pci_free_properties(struct pci_dev *d) { struct pci_property *p; while (p = d->properties) { d->properties = p->next; pci_mfree(p); } } void pci_free_dev(struct pci_dev *d) { if (d->methods->cleanup_dev) d->methods->cleanup_dev(d); pci_free_caps(d); pci_free_properties(d); pci_mfree(d); } static inline void pci_read_data(struct pci_dev *d, void *buf, int pos, int len) { if (pos & (len-1)) d->access->error("Unaligned read: pos=%02x, len=%d", pos, len); if (pos + len <= d->cache_len) memcpy(buf, d->cache + pos, len); else if (!d->methods->read(d, pos, buf, len)) memset(buf, 0xff, len); } byte pci_read_byte(struct pci_dev *d, int pos) { byte buf; pci_read_data(d, &buf, pos, 1); return buf; } word pci_read_word(struct pci_dev *d, int pos) { word buf; pci_read_data(d, &buf, pos, 2); return le16_to_cpu(buf); } u32 pci_read_long(struct pci_dev *d, int pos) { u32 buf; pci_read_data(d, &buf, pos, 4); return le32_to_cpu(buf); } int pci_read_block(struct pci_dev *d, int pos, byte *buf, int len) { return d->methods->read(d, pos, buf, len); } int pci_read_vpd(struct pci_dev *d, int pos, byte *buf, int len) { return d->methods->read_vpd ? d->methods->read_vpd(d, pos, buf, len) : 0; } static inline int pci_write_data(struct pci_dev *d, void *buf, int pos, int len) { if (pos & (len-1)) d->access->error("Unaligned write: pos=%02x,len=%d", pos, len); if (pos + len <= d->cache_len) memcpy(d->cache + pos, buf, len); return d->methods->write(d, pos, buf, len); } int pci_write_byte(struct pci_dev *d, int pos, byte data) { return pci_write_data(d, &data, pos, 1); } int pci_write_word(struct pci_dev *d, int pos, word data) { word buf = cpu_to_le16(data); return pci_write_data(d, &buf, pos, 2); } int pci_write_long(struct pci_dev *d, int pos, u32 data) { u32 buf = cpu_to_le32(data); return pci_write_data(d, &buf, pos, 4); } int pci_write_block(struct pci_dev *d, int pos, byte *buf, int len) { if (pos < d->cache_len) { int l = (pos + len >= d->cache_len) ? (d->cache_len - pos) : len; memcpy(d->cache + pos, buf, l); } return d->methods->write(d, pos, buf, len); } static void pci_reset_properties(struct pci_dev *d) { d->known_fields = 0; d->phy_slot = NULL; d->module_alias = NULL; d->label = NULL; pci_free_caps(d); pci_free_properties(d); } int pci_fill_info_v313(struct pci_dev *d, int flags) { unsigned int uflags = flags; if (uflags & PCI_FILL_RESCAN) { uflags &= ~PCI_FILL_RESCAN; pci_reset_properties(d); } if (uflags & ~d->known_fields) d->methods->fill_info(d, uflags); return d->known_fields; } /* In version 3.1, pci_fill_info got new flags => versioned alias */ /* In versions 3.2, 3.3, 3.4, 3.5, 3.8 and 3.12, the same has happened */ STATIC_ALIAS(int pci_fill_info(struct pci_dev *d, int flags), pci_fill_info_v313(d, flags)); DEFINE_ALIAS(int pci_fill_info_v30(struct pci_dev *d, int flags), pci_fill_info_v313); DEFINE_ALIAS(int pci_fill_info_v31(struct pci_dev *d, int flags), pci_fill_info_v313); DEFINE_ALIAS(int pci_fill_info_v32(struct pci_dev *d, int flags), pci_fill_info_v313); DEFINE_ALIAS(int pci_fill_info_v33(struct pci_dev *d, int flags), pci_fill_info_v313); DEFINE_ALIAS(int pci_fill_info_v34(struct pci_dev *d, int flags), pci_fill_info_v313); DEFINE_ALIAS(int pci_fill_info_v35(struct pci_dev *d, int flags), pci_fill_info_v313); DEFINE_ALIAS(int pci_fill_info_v38(struct pci_dev *d, int flags), pci_fill_info_v313); SYMBOL_VERSION(pci_fill_info_v30, pci_fill_info@LIBPCI_3.0); SYMBOL_VERSION(pci_fill_info_v31, pci_fill_info@LIBPCI_3.1); SYMBOL_VERSION(pci_fill_info_v32, pci_fill_info@LIBPCI_3.2); SYMBOL_VERSION(pci_fill_info_v33, pci_fill_info@LIBPCI_3.3); SYMBOL_VERSION(pci_fill_info_v34, pci_fill_info@LIBPCI_3.4); SYMBOL_VERSION(pci_fill_info_v35, pci_fill_info@LIBPCI_3.5); SYMBOL_VERSION(pci_fill_info_v38, pci_fill_info@LIBPCI_3.8); SYMBOL_VERSION(pci_fill_info_v313, pci_fill_info@@LIBPCI_3.13); void pci_setup_cache(struct pci_dev *d, byte *cache, int len) { d->cache = cache; d->cache_len = len; } char * pci_set_property(struct pci_dev *d, u32 key, char *value) { struct pci_property *p; struct pci_property **pp = &d->properties; while (p = *pp) { if (p->key == key) { *pp = p->next; pci_mfree(p); } else pp = &p->next; } if (!value) return NULL; p = pci_malloc(d->access, sizeof(*p) + strlen(value)); *pp = p; p->next = NULL; p->key = key; strcpy(p->value, value); return p->value; } char * pci_get_string_property(struct pci_dev *d, u32 prop) { struct pci_property *p; for (p = d->properties; p; p = p->next) if (p->key == prop) return p->value; return NULL; } pciutils-3.13.0/lib/i386-io-openbsd.h0000644000175000001440000000151714564230650015435 0ustar mjusers/* * The PCI Library -- Access to i386 I/O ports on OpenBSD * * Copyright (c) 2023 Grant Pannell * * Can be freely distributed and used under the terms of the GNU GPL. */ #include #include #include #include "i386-io-access.h" #if defined(__amd64__) #define obsd_iopl amd64_iopl #else #define obsd_iopl i386_iopl #endif static int iopl_enabled; static int intel_setup_io(struct pci_access *a UNUSED) { if (iopl_enabled) return 1; if (obsd_iopl(3) < 0) { return 0; } iopl_enabled = 1; return 1; } static inline void intel_cleanup_io(struct pci_access *a UNUSED) { if (iopl_enabled) { obsd_iopl(0); iopl_enabled = 0; } } static inline void intel_io_lock(void) { } static inline void intel_io_unlock(void) { } pciutils-3.13.0/lib/ecam.c0000600000175000001440000007132614603756371013613 0ustar mjusers/* * The PCI Library -- Direct Configuration access via PCIe ECAM * * Copyright (c) 2023 Pali Rohár * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "internal.h" #include "physmem.h" #include "physmem-access.h" #include #include #include #include #include #include #include #include #if defined (__FreeBSD__) || defined (__DragonFly__) || defined(__NetBSD__) #include #endif #if defined (__FreeBSD__) || defined (__DragonFly__) #include #endif struct acpi_rsdp { char signature[8]; u8 checksum; char oem_id[6]; u8 revision; u32 rsdt_address; struct { u32 length; u64 xsdt_address; u8 ext_checksum; u8 reserved[3]; } rsdp20[0]; } PCI_PACKED; struct acpi_sdt { char signature[4]; u32 length; u8 revision; u8 checksum; char oem_id[6]; char oem_table_id[8]; u32 oem_revision; char asl_compiler_id[4]; u32 asl_compiler_revision; } PCI_PACKED; struct acpi_rsdt { struct acpi_sdt sdt; u32 sdt_addresses[0]; } PCI_PACKED; struct acpi_xsdt { struct acpi_sdt sdt; u64 sdt_addresses[0]; } PCI_PACKED; struct acpi_mcfg { struct acpi_sdt sdt; u64 reserved; struct { u64 address; u16 pci_segment; u8 start_bus_number; u8 end_bus_number; u32 reserved; } allocations[0]; } PCI_PACKED; struct mmap_cache { void *map; u64 addr; u32 length; int domain; u8 bus; int w; }; // Back-end data linked to struct pci_access struct ecam_access { struct acpi_mcfg *mcfg; struct mmap_cache *cache; struct physmem *physmem; long pagesize; }; static unsigned int get_rsdt_addresses_count(struct acpi_rsdt *rsdt) { return (rsdt->sdt.length - ((unsigned char*)&rsdt->sdt_addresses - (unsigned char *)rsdt)) / sizeof(rsdt->sdt_addresses[0]); } static unsigned int get_xsdt_addresses_count(struct acpi_xsdt *xsdt) { return (xsdt->sdt.length - ((unsigned char*)&xsdt->sdt_addresses - (unsigned char *)xsdt)) / sizeof(xsdt->sdt_addresses[0]); } static unsigned int get_mcfg_allocations_count(struct acpi_mcfg *mcfg) { return (mcfg->sdt.length - ((unsigned char *)&mcfg->allocations - (unsigned char *)mcfg)) / sizeof(mcfg->allocations[0]); } static u8 calculate_checksum(const u8 *bytes, int len) { u8 checksum = 0; while (len-- > 0) checksum -= *(bytes++); return checksum; } static struct acpi_sdt * check_and_map_sdt(struct physmem *physmem, long pagesize, u64 addr, const char *signature, void **map_addr, u32 *map_length) { struct acpi_sdt *sdt; char sdt_signature[sizeof(sdt->signature)]; u32 length; void *map; if (addr + sizeof(*sdt) < addr) return NULL; map = physmem_map(physmem, addr & ~(pagesize-1), sizeof(*sdt) + (addr & (pagesize-1)), 0); if (map == (void *)-1) return NULL; sdt = (struct acpi_sdt *)((unsigned char *)map + (addr & (pagesize-1))); length = sdt->length; memcpy(sdt_signature, sdt->signature, sizeof(sdt->signature)); physmem_unmap(physmem, map, sizeof(*sdt) + (addr & (pagesize-1))); if (memcmp(sdt_signature, signature, sizeof(sdt_signature)) != 0) return NULL; if (length < sizeof(*sdt)) return NULL; map = physmem_map(physmem, addr & ~(pagesize-1), length + (addr & (pagesize-1)), 0); if (map == (void *)-1) return NULL; sdt = (struct acpi_sdt *)((unsigned char *)map + (addr & (pagesize-1))); if (calculate_checksum((u8 *)sdt, sdt->length) != 0) { physmem_unmap(physmem, map, length + (addr & (pagesize-1))); return NULL; } *map_addr = map; *map_length = length + (addr & (pagesize-1)); return sdt; } static int check_rsdp(struct acpi_rsdp *rsdp) { if (memcmp(rsdp->signature, "RSD PTR ", sizeof(rsdp->signature)) != 0) return 0; if (calculate_checksum((u8 *)rsdp, sizeof(*rsdp)) != 0) return 0; return 1; } static int check_and_parse_rsdp(struct physmem *physmem, long pagesize, u64 addr, u32 *rsdt_address, u64 *xsdt_address) { struct acpi_rsdp *rsdp; unsigned char buf[sizeof(*rsdp) + sizeof(*rsdp->rsdp20)]; void *map; map = physmem_map(physmem, addr & ~(pagesize-1), sizeof(buf) + (addr & (pagesize-1)), 0); if (map == (void *)-1) return 0; rsdp = (struct acpi_rsdp *)buf; memcpy(rsdp, (unsigned char *)map + (addr & (pagesize-1)), sizeof(buf)); physmem_unmap(physmem, map, sizeof(buf)); if (!check_rsdp(rsdp)) return 0; *rsdt_address = rsdp->rsdt_address; if (rsdp->revision != 0 && (*rsdp->rsdp20).length == sizeof(*rsdp) + sizeof(*rsdp->rsdp20) && calculate_checksum((u8 *)rsdp, (*rsdp->rsdp20).length) == 0) *xsdt_address = (*rsdp->rsdp20).xsdt_address; else *xsdt_address = 0; return 1; } static u64 find_rsdp_address(struct pci_access *a, const char *efisystab, int use_bsd UNUSED, int use_x86bios UNUSED) { u64 ullnum; #if defined (__FreeBSD__) || defined (__DragonFly__) || defined(__NetBSD__) unsigned long ulnum; #endif char buf[1024]; char *endptr; u64 acpi20; u64 acpi; #if defined(__amd64__) || defined(__i386__) struct ecam_access *eacc = a->backend_data; struct physmem *physmem = eacc->physmem; long pagesize = eacc->pagesize; u64 rsdp_addr; u64 addr; void *map; u64 ebda; #endif size_t len; FILE *f; if (efisystab[0]) { acpi = 0; acpi20 = 0; a->debug("reading EFI system table: %s...", efisystab); f = fopen(efisystab, "r"); if (f) { while (fgets(buf, sizeof(buf), f)) { len = strlen(buf); while (len > 0 && buf[len-1] == '\n') buf[--len] = '\0'; if (strncmp(buf, "ACPI20=", 7) == 0 && isxdigit(buf[7])) { errno = 0; ullnum = strtoull(buf+7, &endptr, 16); if (!errno && !*endptr) acpi20 = ullnum; } else if (strncmp(buf, "ACPI=", 5) == 0 && isxdigit(buf[5])) { errno = 0; ullnum = strtoull(buf+5, &endptr, 16); if (!errno && !*endptr) acpi = ullnum; } } fclose(f); } else a->debug("opening failed: %s...", strerror(errno)); if (acpi20) return acpi20; else if (acpi) return acpi; } #if defined (__FreeBSD__) || defined (__DragonFly__) if (use_bsd) { /* First try FreeBSD kenv hint.acpi.0.rsdp */ a->debug("calling kenv hint.acpi.0.rsdp..."); if (kenv(KENV_GET, "hint.acpi.0.rsdp", buf, sizeof(buf)) > 0) { errno = 0; ullnum = strtoull(buf, &endptr, 16); if (!errno && !*endptr) return ullnum; } /* Then try FreeBSD sysctl machdep.acpi_root */ a->debug("calling sysctl machdep.acpi_root..."); len = sizeof(ulnum); if (sysctlbyname("machdep.acpi_root", &ulnum, &len, NULL, 0) == 0) return ulnum; } #endif #if defined(__NetBSD__) if (use_bsd) { /* Try NetBSD sysctl hw.acpi.root */ a->debug("calling sysctl hw.acpi.root..."); len = sizeof(ulnum); if (sysctlbyname("hw.acpi.root", &ulnum, &len, NULL, 0) == 0) return ulnum; } #endif #if defined(__amd64__) || defined(__i386__) if (use_x86bios) { rsdp_addr = 0; /* Scan first kB of Extended BIOS Data Area */ a->debug("reading EBDA location from BDA..."); map = physmem_map(physmem, 0, 0x40E + 2, 0); if (map != (void *)-1) { ebda = (u64)physmem_readw((unsigned char *)map + 0x40E) << 4; if (physmem_unmap(physmem, map, 0x40E + 2) != 0) a->debug("unmapping of BDA failed: %s...", strerror(errno)); if (ebda >= 0x400) { a->debug("scanning first kB of EBDA at 0x%" PCI_U64_FMT_X "...", ebda); map = physmem_map(physmem, ebda & ~(pagesize-1), 1024 + (ebda & (pagesize-1)), 0); if (map != (void *)-1) { for (addr = ebda & (pagesize-1); addr < (ebda & (pagesize-1)) + 1024; addr += 16) { if (check_rsdp((struct acpi_rsdp *)((unsigned char *)map + addr))) { rsdp_addr = (ebda & ~(pagesize-1)) + addr; break; } } if (physmem_unmap(physmem, map, 1024 + (ebda & (pagesize-1))) != 0) a->debug("unmapping of EBDA failed: %s...", strerror(errno)); } else a->debug("mapping of EBDA failed: %s...", strerror(errno)); } else a->debug("EBDA location 0x%" PCI_U64_FMT_X " is insane...", ebda); } else a->debug("mapping of BDA failed: %s...", strerror(errno)); if (rsdp_addr) return rsdp_addr; /* Scan the main BIOS area below 1 MB */ a->debug("scanning BIOS below 1 MB..."); map = physmem_map(physmem, 0xE0000, 0x20000, 0); if (map != (void *)-1) { for (addr = 0x0; addr < 0x20000; addr += 16) { if (check_rsdp((struct acpi_rsdp *)((unsigned char *)map + addr))) { rsdp_addr = 0xE0000 + addr; break; } } if (physmem_unmap(physmem, map, 0x20000) != 0) a->debug("unmapping of BIOS failed: %s...", strerror(errno)); } else a->debug("mapping of BIOS failed: %s...", strerror(errno)); if (rsdp_addr) return rsdp_addr; } #endif return 0; } static struct acpi_mcfg * find_mcfg(struct pci_access *a, const char *acpimcfg, const char *efisystab, int use_bsd, int use_x86bios) { struct ecam_access *eacc = a->backend_data; struct physmem *physmem = eacc->physmem; long pagesize = eacc->pagesize; struct acpi_xsdt *xsdt; struct acpi_rsdt *rsdt; struct acpi_mcfg *mcfg; struct acpi_sdt *sdt; unsigned int i, count; u64 rsdp_address; u64 xsdt_address; u32 rsdt_address; void *map_addr; u32 map_length; void *map2_addr; u32 map2_length; long length; FILE *mcfg_file; const char *path; glob_t mcfg_glob; int ret; if (acpimcfg[0]) { ret = glob(acpimcfg, GLOB_NOCHECK, NULL, &mcfg_glob); if (ret == 0) { path = mcfg_glob.gl_pathv[0]; a->debug("reading acpi mcfg file: %s...", path); mcfg_file = fopen(path, "rb"); globfree(&mcfg_glob); if (mcfg_file) { if (fseek(mcfg_file, 0, SEEK_END) == 0) length = ftell(mcfg_file); else length = -1; if (length > 0 && (size_t)length > sizeof(*mcfg)) { rewind(mcfg_file); mcfg = pci_malloc(a, length); if (fread(mcfg, 1, length, mcfg_file) == (size_t)length && memcmp(mcfg->sdt.signature, "MCFG", 4) == 0 && mcfg->sdt.length <= (size_t)length && calculate_checksum((u8 *)mcfg, mcfg->sdt.length) == 0) { fclose(mcfg_file); return mcfg; } } fclose(mcfg_file); } a->debug("failed..."); } else a->debug("glob(%s) failed: %d...", acpimcfg, ret); } a->debug("searching for ACPI RSDP..."); rsdp_address = find_rsdp_address(a, efisystab, use_bsd, use_x86bios); if (!rsdp_address) { a->debug("not found..."); return NULL; } a->debug("found at 0x%" PCI_U64_FMT_X "...", rsdp_address); if (!check_and_parse_rsdp(physmem, pagesize, rsdp_address, &rsdt_address, &xsdt_address)) { a->debug("invalid..."); return NULL; } mcfg = NULL; a->debug("searching for ACPI MCFG (XSDT=0x%" PCI_U64_FMT_X ", RSDT=0x%lx)...", xsdt_address, (unsigned long)rsdt_address); xsdt = xsdt_address ? (struct acpi_xsdt *)check_and_map_sdt(physmem, pagesize, xsdt_address, "XSDT", &map_addr, &map_length) : NULL; if (xsdt) { a->debug("via XSDT..."); count = get_xsdt_addresses_count(xsdt); for (i = 0; i < count; i++) { sdt = check_and_map_sdt(physmem, pagesize, xsdt->sdt_addresses[i], "MCFG", &map2_addr, &map2_length); if (sdt) { mcfg = pci_malloc(a, sdt->length); memcpy(mcfg, sdt, sdt->length); physmem_unmap(physmem, map2_addr, map2_length); break; } } physmem_unmap(physmem, map_addr, map_length); if (mcfg) { a->debug("found..."); return mcfg; } } rsdt = (struct acpi_rsdt *)check_and_map_sdt(physmem, pagesize, rsdt_address, "RSDT", &map_addr, &map_length); if (rsdt) { a->debug("via RSDT..."); count = get_rsdt_addresses_count(rsdt); for (i = 0; i < count; i++) { sdt = check_and_map_sdt(physmem, pagesize, rsdt->sdt_addresses[i], "MCFG", &map2_addr, &map2_length); if (sdt) { mcfg = pci_malloc(a, sdt->length); memcpy(mcfg, sdt, sdt->length); physmem_unmap(physmem, map2_addr, map2_length); break; } } physmem_unmap(physmem, map_addr, map_length); if (mcfg) { a->debug("found..."); return mcfg; } } a->debug("not found..."); return NULL; } static void get_mcfg_allocation(struct acpi_mcfg *mcfg, unsigned int i, int *domain, u8 *start_bus, u8 *end_bus, u64 *addr, u32 *length) { int buses = (int)mcfg->allocations[i].end_bus_number - (int)mcfg->allocations[i].start_bus_number + 1; if (domain) *domain = mcfg->allocations[i].pci_segment; if (start_bus) *start_bus = mcfg->allocations[i].start_bus_number; if (end_bus) *end_bus = mcfg->allocations[i].end_bus_number; if (addr) *addr = mcfg->allocations[i].address; if (length) *length = (buses > 0) ? (buses * 32 * 8 * 4096) : 0; } static int parse_next_addrs(const char *addrs, const char **next, int *domain, u8 *start_bus, u8 *end_bus, u64 *addr, u32 *length) { u64 ullnum; const char *sep1, *sep2; int addr_len; char *endptr; long num; int buses; u64 start_addr; if (!*addrs) { if (next) *next = NULL; return 0; } endptr = strchr(addrs, ','); if (endptr) addr_len = endptr - addrs; else addr_len = strlen(addrs); if (next) *next = endptr ? (endptr+1) : NULL; sep1 = memchr(addrs, ':', addr_len); if (!sep1) return 0; sep2 = memchr(sep1+1, ':', addr_len - (sep1+1 - addrs)); if (!sep2) { sep2 = sep1; sep1 = NULL; } if (!sep1) { if (domain) *domain = 0; } else { if (!isxdigit(*addrs)) return 0; errno = 0; num = strtol(addrs, &endptr, 16); if (errno || endptr != sep1 || num < 0 || num > INT_MAX) return 0; if (domain) *domain = num; } errno = 0; num = strtol(sep1 ? (sep1+1) : addrs, &endptr, 16); if (errno || num < 0 || num > 0xff) return 0; if (start_bus) *start_bus = num; buses = -num; if (endptr != sep2) { if (*endptr != '-') return 0; errno = 0; num = strtol(endptr+1, &endptr, 16); if (errno || endptr != sep2 || num < 0 || num > 0xff) return 0; buses = num - -buses + 1; if (buses <= 0) return 0; if (end_bus) *end_bus = num; } if (!isxdigit(*(sep2+1))) return 0; errno = 0; ullnum = strtoull(sep2+1, &endptr, 16); if (errno || (ullnum & 3)) return 0; if (addr) *addr = ullnum; start_addr = ullnum; if (endptr == addrs + addr_len) { if (buses <= 0) { buses = 0xff - -buses + 1; if (end_bus) *end_bus = 0xff; } if (start_addr + (unsigned)buses * 32 * 8 * 4096 < start_addr) return 0; if (length) *length = buses * 32 * 8 * 4096; } else { if (*endptr != '+' || !isxdigit(*(endptr+1))) return 0; errno = 0; ullnum = strtoull(endptr+1, &endptr, 16); if (errno || endptr != addrs + addr_len || (ullnum & 3) || ullnum > 256 * 32 * 8 * 4096) return 0; if (start_addr + ullnum < start_addr) return 0; if (buses > 0 && ullnum > (unsigned)buses * 32 * 8 * 4096) return 0; if (buses <= 0 && ullnum > (0xff - (unsigned)-buses + 1) * 32 * 8 * 4096) return 0; if (length) *length = ullnum; if (buses <= 0 && end_bus) *end_bus = -buses + (ullnum + 32 * 8 * 4096 - 1) / (32 * 8 * 4096); } return 1; } static int validate_addrs(const char *addrs) { if (!*addrs) return 1; while (addrs) if (!parse_next_addrs(addrs, &addrs, NULL, NULL, NULL, NULL, NULL)) return 0; return 1; } static int calculate_bus_addr(u8 start_bus, u64 start_addr, u32 total_length, u8 bus, u64 *addr, u32 *length) { u32 offset; offset = 32*8*4096 * (bus - start_bus); if (offset >= total_length) return 0; *addr = start_addr + offset; *length = total_length - offset; if (*length > 32*8*4096) *length = 32*8*4096; return 1; } static int get_bus_addr(struct acpi_mcfg *mcfg, const char *addrs, int domain, u8 bus, u64 *addr, u32 *length) { int cur_domain; u8 start_bus; u8 end_bus; u64 start_addr; u32 total_length; int i, count; if (mcfg) { count = get_mcfg_allocations_count(mcfg); for (i = 0; i < count; i++) { get_mcfg_allocation(mcfg, i, &cur_domain, &start_bus, &end_bus, &start_addr, &total_length); if (domain == cur_domain && bus >= start_bus && bus <= end_bus) return calculate_bus_addr(start_bus, start_addr, total_length, bus, addr, length); } return 0; } else { while (addrs) { if (!parse_next_addrs(addrs, &addrs, &cur_domain, &start_bus, &end_bus, &start_addr, &total_length)) return 0; if (domain == cur_domain && bus >= start_bus && bus <= end_bus) return calculate_bus_addr(start_bus, start_addr, total_length, bus, addr, length); } return 0; } } static void munmap_reg(struct pci_access *a) { struct ecam_access *eacc = a->backend_data; struct mmap_cache *cache = eacc->cache; struct physmem *physmem = eacc->physmem; long pagesize = eacc->pagesize; if (!cache) return; physmem_unmap(physmem, cache->map, cache->length + (cache->addr & (pagesize-1))); pci_mfree(cache); eacc->cache = NULL; } static int mmap_reg(struct pci_access *a, int w, int domain, u8 bus, u8 dev, u8 func, int pos, volatile void **reg) { struct ecam_access *eacc = a->backend_data; struct mmap_cache *cache = eacc->cache; struct physmem *physmem = eacc->physmem; long pagesize = eacc->pagesize; const char *addrs; void *map; u64 addr; u32 length; u32 offset; if (cache && cache->domain == domain && cache->bus == bus && !!cache->w == !!w) { map = cache->map; addr = cache->addr; length = cache->length; } else { addrs = pci_get_param(a, "ecam.addrs"); if (!get_bus_addr(eacc->mcfg, addrs, domain, bus, &addr, &length)) return 0; map = physmem_map(physmem, addr & ~(pagesize-1), length + (addr & (pagesize-1)), w); if (map == (void *)-1) return 0; if (cache) physmem_unmap(physmem, cache->map, cache->length + (cache->addr & (pagesize-1))); else cache = eacc->cache = pci_malloc(a, sizeof(*cache)); cache->map = map; cache->addr = addr; cache->length = length; cache->domain = domain; cache->bus = bus; cache->w = w; } /* * Enhanced Configuration Access Mechanism (ECAM) offset according to: * PCI Express Base Specification, Revision 5.0, Version 1.0, Section 7.2.2, Table 7-1, p. 677 */ offset = ((dev & 0x1f) << 15) | ((func & 0x7) << 12) | (pos & 0xfff); if (offset + 4 > length) return 0; *reg = (unsigned char *)map + (addr & (pagesize-1)) + offset; return 1; } static void ecam_config(struct pci_access *a) { physmem_init_config(a); pci_define_param(a, "ecam.acpimcfg", PCI_PATH_ACPI_MCFG, "Path to the ACPI MCFG table"); pci_define_param(a, "ecam.efisystab", PCI_PATH_EFI_SYSTAB, "Path to the EFI system table"); #if defined (__FreeBSD__) || defined (__DragonFly__) || defined(__NetBSD__) pci_define_param(a, "ecam.bsd", "1", "Use BSD kenv or sysctl to find ACPI MCFG table"); #endif #if defined(__amd64__) || defined(__i386__) pci_define_param(a, "ecam.x86bios", "1", "Scan x86 BIOS memory for ACPI MCFG table"); #endif pci_define_param(a, "ecam.addrs", "", "Physical addresses of memory mapped PCIe ECAM interface"); /* format: [domain:]start_bus[-end_bus]:start_addr[+length],... */ } static int ecam_detect(struct pci_access *a) { int use_addrs = 1, use_acpimcfg = 1, use_efisystab = 1, use_bsd = 1, use_x86bios = 1; const char *acpimcfg = pci_get_param(a, "ecam.acpimcfg"); const char *efisystab = pci_get_param(a, "ecam.efisystab"); #if defined (__FreeBSD__) || defined (__DragonFly__) || defined(__NetBSD__) const char *bsd = pci_get_param(a, "ecam.bsd"); #endif #if defined(__amd64__) || defined(__i386__) const char *x86bios = pci_get_param(a, "ecam.x86bios"); #endif const char *addrs = pci_get_param(a, "ecam.addrs"); struct ecam_access *eacc; glob_t mcfg_glob; int ret; if (!*addrs) { a->debug("ecam.addrs was not specified..."); use_addrs = 0; } if (acpimcfg[0]) { ret = glob(acpimcfg, GLOB_NOCHECK, NULL, &mcfg_glob); if (ret == 0) { if (access(mcfg_glob.gl_pathv[0], R_OK)) { a->debug("cannot access acpimcfg: %s: %s...", mcfg_glob.gl_pathv[0], strerror(errno)); use_acpimcfg = 0; } globfree(&mcfg_glob); } else { a->debug("glob(%s) failed: %d...", acpimcfg, ret); use_acpimcfg = 0; } } else use_acpimcfg = 0; if (!efisystab[0] || access(efisystab, R_OK)) { if (efisystab[0]) a->debug("cannot access efisystab: %s: %s...", efisystab, strerror(errno)); use_efisystab = 0; } #if defined (__FreeBSD__) || defined (__DragonFly__) || defined(__NetBSD__) if (strcmp(bsd, "0") == 0) { a->debug("not using BSD kenv/sysctl..."); use_bsd = 0; } #else use_bsd = 0; #endif #if defined(__amd64__) || defined(__i386__) if (strcmp(x86bios, "0") == 0) { a->debug("not using x86 BIOS..."); use_x86bios = 0; } #else use_x86bios = 0; #endif if (!use_addrs && !use_acpimcfg && !use_efisystab && !use_bsd && !use_x86bios) { a->debug("no ecam source provided"); return 0; } if (!validate_addrs(addrs)) { a->debug("ecam.addrs has invalid format %s", addrs); return 0; } if (physmem_access(a, 0)) { a->debug("cannot access physical memory: %s", strerror(errno)); return 0; } if (!use_addrs) { eacc = pci_malloc(a, sizeof(*eacc)); eacc->physmem = physmem_open(a, a->writeable); if (!eacc->physmem) { a->debug("cannot open physcal memory: %s.", strerror(errno)); pci_mfree(eacc); return 0; } eacc->pagesize = physmem_get_pagesize(eacc->physmem); if (eacc->pagesize <= 0) { a->debug("Cannot get page size: %s.", strerror(errno)); physmem_close(eacc->physmem); pci_mfree(eacc); return 0; } eacc->mcfg = NULL; eacc->cache = NULL; a->backend_data = eacc; eacc->mcfg = find_mcfg(a, acpimcfg, efisystab, use_bsd, use_x86bios); if (!eacc->mcfg) { physmem_close(eacc->physmem); pci_mfree(eacc); a->backend_data = NULL; return 0; } } if (use_addrs) a->debug("using with ecam addresses %s", addrs); else a->debug("using with%s%s%s%s%s%s", use_acpimcfg ? " acpimcfg=" : "", use_acpimcfg ? acpimcfg : "", use_efisystab ? " efisystab=" : "", use_efisystab ? efisystab : "", use_bsd ? " bsd" : "", use_x86bios ? " x86bios" : ""); return 1; } static void ecam_init(struct pci_access *a) { const char *acpimcfg = pci_get_param(a, "ecam.acpimcfg"); const char *efisystab = pci_get_param(a, "ecam.efisystab"); #if defined (__FreeBSD__) || defined (__DragonFly__) || defined(__NetBSD__) const char *bsd = pci_get_param(a, "ecam.bsd"); #endif #if defined(__amd64__) || defined(__i386__) const char *x86bios = pci_get_param(a, "ecam.x86bios"); #endif const char *addrs = pci_get_param(a, "ecam.addrs"); struct physmem *physmem = NULL; struct ecam_access *eacc = a->backend_data; long pagesize = 0; int use_bsd = 0; int use_x86bios = 0; int test_domain = 0; u8 test_bus = 0; volatile void *test_reg; if (!validate_addrs(addrs)) a->error("Option ecam.addrs has invalid address format \"%s\".", addrs); if (!eacc) { physmem = physmem_open(a, a->writeable); if (!physmem) a->error("Cannot open physcal memory: %s.", strerror(errno)); pagesize = physmem_get_pagesize(physmem); if (pagesize <= 0) a->error("Cannot get page size: %s.", strerror(errno)); eacc = pci_malloc(a, sizeof(*eacc)); eacc->mcfg = NULL; eacc->cache = NULL; eacc->physmem = physmem; eacc->pagesize = pagesize; a->backend_data = eacc; } if (!*addrs) { #if defined (__FreeBSD__) || defined (__DragonFly__) if (strcmp(bsd, "0") != 0) use_bsd = 1; #endif #if defined(__amd64__) || defined(__i386__) if (strcmp(x86bios, "0") != 0) use_x86bios = 1; #endif if (!eacc->mcfg) eacc->mcfg = find_mcfg(a, acpimcfg, efisystab, use_bsd, use_x86bios); if (!eacc->mcfg) a->error("Option ecam.addrs was not specified and ACPI MCFG table cannot be found."); } if (eacc->mcfg) get_mcfg_allocation(eacc->mcfg, 0, &test_domain, &test_bus, NULL, NULL, NULL); else parse_next_addrs(addrs, NULL, &test_domain, &test_bus, NULL, NULL, NULL); errno = 0; if (!mmap_reg(a, 0, test_domain, test_bus, 0, 0, 0, &test_reg)) a->error("Cannot map ecam region: %s.", errno ? strerror(errno) : "Unknown error"); } static void ecam_cleanup(struct pci_access *a) { struct ecam_access *eacc = a->backend_data; munmap_reg(a); physmem_close(eacc->physmem); pci_mfree(eacc->mcfg); pci_mfree(eacc); a->backend_data = NULL; } static void ecam_scan(struct pci_access *a) { const char *addrs = pci_get_param(a, "ecam.addrs"); struct ecam_access *eacc = a->backend_data; u32 *segments; int i, j, count; int domain; segments = pci_malloc(a, 0xFFFF/8); memset(segments, 0, 0xFFFF/8); if (eacc->mcfg) { count = get_mcfg_allocations_count(eacc->mcfg); for (i = 0; i < count; i++) segments[eacc->mcfg->allocations[i].pci_segment / 32] |= 1 << (eacc->mcfg->allocations[i].pci_segment % 32); } else { while (addrs) { if (parse_next_addrs(addrs, &addrs, &domain, NULL, NULL, NULL, NULL)) segments[domain / 32] |= 1 << (domain % 32); } } for (i = 0; i < 0xFFFF/32; i++) { if (!segments[i]) continue; for (j = 0; j < 32; j++) if (segments[i] & (1 << j)) pci_generic_scan_domain(a, 32*i + j); } pci_mfree(segments); } static int ecam_read(struct pci_dev *d, int pos, byte *buf, int len) { volatile void *reg; if (pos >= 4096) return 0; if (len != 1 && len != 2 && len != 4) return pci_generic_block_read(d, pos, buf, len); if (!mmap_reg(d->access, 0, d->domain, d->bus, d->dev, d->func, pos, ®)) return 0; switch (len) { case 1: buf[0] = physmem_readb(reg); break; case 2: ((u16 *) buf)[0] = physmem_readw(reg); break; case 4: ((u32 *) buf)[0] = physmem_readl(reg); break; } return 1; } static int ecam_write(struct pci_dev *d, int pos, byte *buf, int len) { volatile void *reg; if (pos >= 4096) return 0; if (len != 1 && len != 2 && len != 4) return pci_generic_block_read(d, pos, buf, len); if (!mmap_reg(d->access, 1, d->domain, d->bus, d->dev, d->func, pos, ®)) return 0; switch (len) { case 1: physmem_writeb(buf[0], reg); break; case 2: physmem_writew(((u16 *) buf)[0], reg); break; case 4: physmem_writel(((u32 *) buf)[0], reg); break; } return 1; } struct pci_methods pm_ecam = { .name = "ecam", .help = "Raw memory mapped access using PCIe ECAM interface", .config = ecam_config, .detect = ecam_detect, .init = ecam_init, .cleanup = ecam_cleanup, .scan = ecam_scan, .fill_info = pci_generic_fill_info, .read = ecam_read, .write = ecam_write, }; pciutils-3.13.0/lib/win32-helpers.c0000600000175000001440000012615214603756376015313 0ustar mjusers/* * The PCI Library -- Win32 helper functions * * Copyright (c) 2023 Pali Rohár * * Can be freely distributed and used under the terms of the GNU GPL v2+ * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include /* for sprintf() */ #include "win32-helpers.h" /* Unfortunately i586-mingw32msvc toolchain does not provide this constant. */ #ifndef PROCESS_QUERY_LIMITED_INFORMATION #define PROCESS_QUERY_LIMITED_INFORMATION 0x1000 #endif /* Unfortunately some toolchains do not provide this constant. */ #ifndef SE_IMPERSONATE_NAME #define SE_IMPERSONATE_NAME TEXT("SeImpersonatePrivilege") #endif /* Unfortunately some toolchains do not provide these constants. */ #ifndef SE_DACL_AUTO_INHERIT_REQ #define SE_DACL_AUTO_INHERIT_REQ 0x0100 #endif #ifndef SE_SACL_AUTO_INHERIT_REQ #define SE_SACL_AUTO_INHERIT_REQ 0x0200 #endif #ifndef SE_DACL_AUTO_INHERITED #define SE_DACL_AUTO_INHERITED 0x0400 #endif #ifndef SE_SACL_AUTO_INHERITED #define SE_SACL_AUTO_INHERITED 0x0800 #endif /* * These psapi functions are available in kernel32.dll library with K32 prefix * on Windows 7 and higher systems. On older Windows systems these functions are * available in psapi.dll libary without K32 prefix. So resolve pointers to * these functions dynamically at runtime from the available system library. * Function GetProcessImageFileNameW() is not available on Windows 2000 and * older systems. */ typedef BOOL (WINAPI *EnumProcessesProt)(DWORD *lpidProcess, DWORD cb, DWORD *cbNeeded); typedef DWORD (WINAPI *GetProcessImageFileNameWProt)(HANDLE hProcess, LPWSTR lpImageFileName, DWORD nSize); typedef DWORD (WINAPI *GetModuleFileNameExWProt)(HANDLE hProcess, HMODULE hModule, LPWSTR lpImageFileName, DWORD nSize); /* * These aclapi function is available in advapi.dll library on Windows 2000 * and higher systems. */ typedef BOOL (WINAPI *SetSecurityDescriptorControlProt)(PSECURITY_DESCRIPTOR pSecurityDescriptor, SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest, SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet); /* * This errhandlingapi function is available in kernel32.dll library on * Windows 7 and higher systems. */ typedef BOOL (WINAPI *SetThreadErrorModeProt)(DWORD dwNewMode, LPDWORD lpOldMode); static DWORD format_message_from_system(DWORD win32_error_id, DWORD lang_id, LPSTR buffer, DWORD size) { return FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, win32_error_id, lang_id, buffer, size, NULL); } const char * win32_strerror(DWORD win32_error_id) { /* * Use static buffer which is large enough. * Hopefully no Win32 API error message string is longer than 4 kB. */ static char buffer[4096]; DWORD len; /* * If it is possible show error messages in US English language. * International Windows editions do not have to provide error * messages in English language, so fallback to the language * which system provides (neutral). */ len = format_message_from_system(win32_error_id, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), buffer, sizeof(buffer)); if (!len) len = format_message_from_system(win32_error_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer)); /* FormatMessage() automatically appends ".\r\n" to the error message. */ if (len && buffer[len-1] == '\n') buffer[--len] = '\0'; if (len && buffer[len-1] == '\r') buffer[--len] = '\0'; if (len && buffer[len-1] == '.') buffer[--len] = '\0'; if (!len) sprintf(buffer, "Unknown Win32 error %lu", win32_error_id); return buffer; } BOOL win32_is_non_nt_system(void) { OSVERSIONINFOA version; version.dwOSVersionInfoSize = sizeof(version); return GetVersionExA(&version) && version.dwPlatformId < VER_PLATFORM_WIN32_NT; } BOOL win32_is_32bit_on_64bit_system(void) { BOOL (WINAPI *MyIsWow64Process)(HANDLE, PBOOL); HMODULE kernel32; BOOL is_wow64; /* * Check for 64-bit system via IsWow64Process() function exported * from 32-bit kernel32.dll library available on the 64-bit systems. * Resolve pointer to this function at runtime as this code path is * primary running on 32-bit systems where are not available 64-bit * functions. */ kernel32 = GetModuleHandle(TEXT("kernel32.dll")); if (!kernel32) return FALSE; MyIsWow64Process = (void *)GetProcAddress(kernel32, "IsWow64Process"); if (!MyIsWow64Process) return FALSE; if (!MyIsWow64Process(GetCurrentProcess(), &is_wow64)) return FALSE; return is_wow64; } BOOL win32_is_32bit_on_win8_64bit_system(void) { #ifdef _WIN64 return FALSE; #else OSVERSIONINFOA version; /* Check for Windows 8 (NT 6.2). */ version.dwOSVersionInfoSize = sizeof(version); if (!GetVersionExA(&version) || version.dwPlatformId != VER_PLATFORM_WIN32_NT || version.dwMajorVersion < 6 || (version.dwMajorVersion == 6 && version.dwMinorVersion < 2)) return FALSE; return win32_is_32bit_on_64bit_system(); #endif } /* * Change error mode of the current thread. If it is not possible then change * error mode of the whole process. Always returns previous error mode. */ UINT win32_change_error_mode(UINT new_mode) { SetThreadErrorModeProt MySetThreadErrorMode = NULL; HMODULE kernel32; DWORD old_mode; /* * Function SetThreadErrorMode() was introduced in Windows 7, so use * GetProcAddress() for compatibility with older systems. */ kernel32 = GetModuleHandle(TEXT("kernel32.dll")); if (kernel32) MySetThreadErrorMode = (SetThreadErrorModeProt)(LPVOID)GetProcAddress(kernel32, "SetThreadErrorMode"); if (MySetThreadErrorMode && MySetThreadErrorMode(new_mode, &old_mode)) return old_mode; /* * Fallback to function SetErrorMode() which modifies error mode of the * whole process and returns old mode. */ return SetErrorMode(new_mode); } /* * Check if the current thread has particular privilege in current active access * token. Case when it not possible to determinate it (e.g. current thread does * not have permission to open its own current active access token) is evaluated * as thread does not have that privilege. */ BOOL win32_have_privilege(LUID luid_privilege) { PRIVILEGE_SET priv; HANDLE token; BOOL ret; /* * If the current thread does not have active access token then thread * uses primary process access token for all permission checks. */ if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token) && (GetLastError() != ERROR_NO_TOKEN || !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))) return FALSE; priv.PrivilegeCount = 1; priv.Control = PRIVILEGE_SET_ALL_NECESSARY; priv.Privilege[0].Luid = luid_privilege; priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED; if (!PrivilegeCheck(token, &priv, &ret)) return FALSE; return ret; } /* * Enable or disable particular privilege in specified access token. * * Note that it is not possible to disable privilege in access token with * SE_PRIVILEGE_ENABLED_BY_DEFAULT attribute. This function does not check * this case and incorrectly returns no error even when disabling failed. * Rationale for this decision: Simplification of this function as WinAPI * call AdjustTokenPrivileges() does not signal error in this case too. */ static BOOL set_privilege(HANDLE token, LUID luid_privilege, BOOL enable) { TOKEN_PRIVILEGES token_privileges; token_privileges.PrivilegeCount = 1; token_privileges.Privileges[0].Luid = luid_privilege; token_privileges.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0; /* * WinAPI function AdjustTokenPrivileges() success also when not all * privileges were enabled. It is always required to check for failure * via GetLastError() call. AdjustTokenPrivileges() always sets error * also when it success, as opposite to other WinAPI functions. */ if (!AdjustTokenPrivileges(token, FALSE, &token_privileges, sizeof(token_privileges), NULL, NULL) || GetLastError() != ERROR_SUCCESS) return FALSE; return TRUE; } /* * Change access token for the current thread to new specified access token. * Previously active access token is stored in old_token variable and can be * used for reverting to this access token. It is set to NULL if the current * thread previously used primary process access token. */ BOOL win32_change_token(HANDLE new_token, HANDLE *old_token) { HANDLE token; if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &token)) { if (GetLastError() != ERROR_NO_TOKEN) return FALSE; token = NULL; } if (!ImpersonateLoggedOnUser(new_token)) { if (token) CloseHandle(token); return FALSE; } *old_token = token; return TRUE; } /* * Change access token for the current thread to the primary process access * token. This function fails also when the current thread already uses primary * process access token. */ static BOOL change_token_to_primary(HANDLE *old_token) { HANDLE token; if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &token)) return FALSE; RevertToSelf(); *old_token = token; return TRUE; } /* * Revert to the specified access token for the current thread. When access * token is specified as NULL then revert to the primary process access token. * Use to revert after win32_change_token() or change_token_to_primary() call. */ VOID win32_revert_to_token(HANDLE token) { /* * If SetThreadToken() call fails then there is no option to revert to * the specified previous thread access token. So in this case revert to * the primary process access token. */ if (!token || !SetThreadToken(NULL, token)) RevertToSelf(); if (token) CloseHandle(token); } /* * Enable particular privilege for the current thread. And set method how to * revert this privilege (if to revert whole token or only privilege). */ BOOL win32_enable_privilege(LUID luid_privilege, HANDLE *revert_token, BOOL *revert_only_privilege) { HANDLE thread_token; HANDLE new_token; if (OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &thread_token)) { if (set_privilege(thread_token, luid_privilege, TRUE)) { /* * Indicate that correct revert method is just to * disable privilege in access token. */ if (revert_token && revert_only_privilege) { *revert_token = thread_token; *revert_only_privilege = TRUE; } else { CloseHandle(thread_token); } return TRUE; } CloseHandle(thread_token); /* * If enabling privilege failed then try to enable it via * primary process access token. */ } /* * If the current thread has already active thread access token then * open it with just impersonate right as it would be used only for * future revert. */ if (revert_token && revert_only_privilege) { if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &thread_token)) { if (GetLastError() != ERROR_NO_TOKEN) return FALSE; thread_token = NULL; } /* * If current thread has no access token (and uses primary * process access token) or it does not have permission to * adjust privileges or it does not have specified privilege * then create a copy of the primary process access token, * assign it for the current thread (= impersonate self) * and then try adjusting privilege again. */ if (!ImpersonateSelf(SecurityImpersonation)) { if (thread_token) CloseHandle(thread_token); return FALSE; } } if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &new_token)) { /* thread_token is set only when we were asked for revert method. */ if (revert_token && revert_only_privilege) win32_revert_to_token(thread_token); return FALSE; } if (!set_privilege(new_token, luid_privilege, TRUE)) { CloseHandle(new_token); /* thread_token is set only when we were asked for revert method. */ if (revert_token && revert_only_privilege) win32_revert_to_token(thread_token); return FALSE; } /* * Indicate that correct revert method is to change to the previous * access token. Either to the primary process access token or to the * previous thread access token. */ if (revert_token && revert_only_privilege) { *revert_token = thread_token; *revert_only_privilege = FALSE; } return TRUE; } /* * Revert particular privilege for the current thread was previously enabled by * win32_enable_privilege() call. Either disable privilege in specified access token * or revert to previous access token. */ VOID win32_revert_privilege(LUID luid_privilege, HANDLE revert_token, BOOL revert_only_privilege) { if (revert_only_privilege) { set_privilege(revert_token, luid_privilege, FALSE); CloseHandle(revert_token); } else { win32_revert_to_token(revert_token); } } /* * Return owner of the access token used by the current thread. Buffer for * returned owner needs to be released by LocalFree() call. */ static TOKEN_OWNER * get_current_token_owner(VOID) { HANDLE token; DWORD length; TOKEN_OWNER *owner; /* * If the current thread does not have active access token then thread * uses primary process access token for all permission checks. */ if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token) && (GetLastError() != ERROR_NO_TOKEN || !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))) return NULL; if (!GetTokenInformation(token, TokenOwner, NULL, 0, &length) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) { CloseHandle(token); return NULL; } retry: owner = (TOKEN_OWNER *)LocalAlloc(LPTR, length); if (!owner) { CloseHandle(token); return NULL; } if (!GetTokenInformation(token, TokenOwner, owner, length, &length)) { /* * Length of token owner (SID) buffer between two get calls may * changes (e.g. by another thread of process), so retry. */ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { LocalFree(owner); goto retry; } LocalFree(owner); CloseHandle(token); return NULL; } CloseHandle(token); return owner; } /* * Create a new security descriptor in absolute form from relative form. * Newly created security descriptor in absolute form is stored in linear buffer. */ static PSECURITY_DESCRIPTOR create_relsd_from_abssd(PSECURITY_DESCRIPTOR rel_security_descriptor) { PBYTE abs_security_descriptor_buffer; DWORD abs_security_descriptor_size=0, abs_dacl_size=0, abs_sacl_size=0, abs_owner_size=0, abs_primary_group_size=0; if (!MakeAbsoluteSD(rel_security_descriptor, NULL, &abs_security_descriptor_size, NULL, &abs_dacl_size, NULL, &abs_sacl_size, NULL, &abs_owner_size, NULL, &abs_primary_group_size) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) return NULL; abs_security_descriptor_buffer = (PBYTE)LocalAlloc(LPTR, abs_security_descriptor_size+abs_dacl_size+abs_sacl_size+abs_owner_size+abs_primary_group_size); if (!abs_security_descriptor_buffer) return NULL; if (!MakeAbsoluteSD(rel_security_descriptor, (PSECURITY_DESCRIPTOR)abs_security_descriptor_buffer, &abs_security_descriptor_size, (PACL)(abs_security_descriptor_buffer+abs_security_descriptor_size), &abs_dacl_size, (PACL)(abs_security_descriptor_buffer+abs_security_descriptor_size+abs_dacl_size), &abs_sacl_size, (PSID)(abs_security_descriptor_buffer+abs_security_descriptor_size+abs_dacl_size+abs_sacl_size), &abs_owner_size, (PSID)(abs_security_descriptor_buffer+abs_security_descriptor_size+abs_dacl_size+abs_sacl_size+abs_owner_size), &abs_primary_group_size)) return NULL; return (PSECURITY_DESCRIPTOR)abs_security_descriptor_buffer; } /* * Prepare security descriptor obtained by GetKernelObjectSecurity() so it can be * passed to SetKernelObjectSecurity() as identity operation. It modifies control * flags of security descriptor, which is needed for Windows 2000 and new. */ static BOOL prepare_security_descriptor_for_set_operation(PSECURITY_DESCRIPTOR security_descriptor) { SetSecurityDescriptorControlProt MySetSecurityDescriptorControl; SECURITY_DESCRIPTOR_CONTROL bits_mask; SECURITY_DESCRIPTOR_CONTROL bits_set; SECURITY_DESCRIPTOR_CONTROL control; OSVERSIONINFO version; HMODULE advapi32; DWORD revision; /* * SE_DACL_AUTO_INHERITED and SE_SACL_AUTO_INHERITED are flags introduced in * Windows 2000 to control client-side automatic inheritance (client - user * process - is responsible for propagating inherited ACEs to subobjects). * To prevent applications which do not understand client-side automatic * inheritance (applications created prior Windows 2000 or which use low * level API like SetKernelObjectSecurity()) to unintentionally set those * SE_DACL_AUTO_INHERITED and SE_SACL_AUTO_INHERITED control flags when * coping them from other security descriptor. * * As we are not modifying existing ACEs, we are compatible with Windows 2000 * client-side automatic inheritance model and therefore prepare security * descriptor for SetKernelObjectSecurity() to not clear existing automatic * inheritance control flags. * * Control flags SE_DACL_AUTO_INHERITED and SE_SACL_AUTO_INHERITED are set * into security object only when they are set together with set-only flags * SE_DACL_AUTO_INHERIT_REQ and SE_SACL_AUTO_INHERIT_REQ. Those flags are * never received by GetKernelObjectSecurity() and are just commands for * SetKernelObjectSecurity() how to interpret SE_DACL_AUTO_INHERITED and * SE_SACL_AUTO_INHERITED flags. * * Function symbol SetSecurityDescriptorControl is not available in the * older versions of advapi32.dll library, so resolve it at runtime. */ version.dwOSVersionInfoSize = sizeof(version); if (!GetVersionEx(&version) || version.dwPlatformId != VER_PLATFORM_WIN32_NT || version.dwMajorVersion < 5) return TRUE; if (!GetSecurityDescriptorControl(security_descriptor, &control, &revision)) return FALSE; bits_mask = 0; bits_set = 0; if (control & SE_DACL_AUTO_INHERITED) { bits_mask |= SE_DACL_AUTO_INHERIT_REQ; bits_set |= SE_DACL_AUTO_INHERIT_REQ; } if (control & SE_SACL_AUTO_INHERITED) { bits_mask |= SE_SACL_AUTO_INHERIT_REQ; bits_set |= SE_SACL_AUTO_INHERIT_REQ; } if (!bits_mask) return TRUE; advapi32 = GetModuleHandle(TEXT("advapi32.dll")); if (!advapi32) return FALSE; MySetSecurityDescriptorControl = (SetSecurityDescriptorControlProt)(LPVOID)GetProcAddress(advapi32, "SetSecurityDescriptorControl"); if (!MySetSecurityDescriptorControl) return FALSE; if (!MySetSecurityDescriptorControl(security_descriptor, bits_mask, bits_set)) return FALSE; return TRUE; } /* * Grant particular permissions in the primary access token of the specified * process for the owner of current thread token and set old DACL of the * process access token for reverting permissions. Security descriptor is * just memory buffer for old DACL. */ static BOOL grant_process_token_dacl_permissions(HANDLE process, DWORD permissions, HANDLE *token, PSECURITY_DESCRIPTOR *old_security_descriptor) { TOKEN_OWNER *owner; PACL old_dacl; BOOL old_dacl_present; BOOL old_dacl_defaulted; PACL new_dacl; WORD new_dacl_size; PSECURITY_DESCRIPTOR new_security_descriptor; DWORD length; owner = get_current_token_owner(); if (!owner) return FALSE; /* * READ_CONTROL is required for GetSecurityInfo(DACL_SECURITY_INFORMATION) * and WRITE_DAC is required for SetSecurityInfo(DACL_SECURITY_INFORMATION). */ if (!OpenProcessToken(process, READ_CONTROL | WRITE_DAC, token)) { LocalFree(owner); return FALSE; } if (!GetKernelObjectSecurity(*token, DACL_SECURITY_INFORMATION, NULL, 0, &length) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) { LocalFree(owner); CloseHandle(*token); return FALSE; } retry: *old_security_descriptor = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, length); if (!*old_security_descriptor) { LocalFree(owner); CloseHandle(*token); return FALSE; } if (!GetKernelObjectSecurity(*token, DACL_SECURITY_INFORMATION, *old_security_descriptor, length, &length)) { /* * Length of the security descriptor between two get calls * may changes (e.g. by another thread of process), so retry. */ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { LocalFree(*old_security_descriptor); goto retry; } LocalFree(*old_security_descriptor); LocalFree(owner); CloseHandle(*token); return FALSE; } if (!prepare_security_descriptor_for_set_operation(*old_security_descriptor)) { LocalFree(*old_security_descriptor); LocalFree(owner); CloseHandle(*token); return FALSE; } /* Retrieve the current DACL from security descriptor including present and defaulted properties. */ if (!GetSecurityDescriptorDacl(*old_security_descriptor, &old_dacl_present, &old_dacl, &old_dacl_defaulted)) { LocalFree(*old_security_descriptor); LocalFree(owner); CloseHandle(*token); return FALSE; } /* * If DACL is not present then system grants full access to everyone. It this * case do not modify DACL as it just adds one ACL allow rule for us, which * automatically disallow access to anybody else which had access before. */ if (!old_dacl_present || !old_dacl) { LocalFree(*old_security_descriptor); LocalFree(owner); *old_security_descriptor = NULL; return TRUE; } /* Create new DACL which would be copy of the current old one. */ new_dacl_size = old_dacl->AclSize + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(owner->Owner) - sizeof(DWORD); new_dacl = (PACL)LocalAlloc(LPTR, new_dacl_size); if (!new_dacl) { LocalFree(*old_security_descriptor); LocalFree(owner); CloseHandle(*token); return FALSE; } /* * Initialize new DACL structure to the same format as was the old one. * Set new explicit access for the owner of the current thread access * token with non-inherited granting access to specified permissions. * This permission is added in the first ACE, so has the highest priority. */ if (!InitializeAcl(new_dacl, new_dacl_size, old_dacl->AclRevision) || !AddAccessAllowedAce(new_dacl, ACL_REVISION2, permissions, owner->Owner)) { LocalFree(new_dacl); LocalFree(*old_security_descriptor); LocalFree(owner); CloseHandle(*token); return FALSE; } /* * Now (after setting our new permissions) append all ACE entries from the * old DACL to the new DACL, which preserve all other existing permissions. */ if (old_dacl->AceCount > 0) { WORD ace_index; LPVOID ace; for (ace_index = 0; ace_index < old_dacl->AceCount; ace_index++) { if (!GetAce(old_dacl, ace_index, &ace) || !AddAce(new_dacl, old_dacl->AclRevision, MAXDWORD, ace, ((PACE_HEADER)ace)->AceSize)) { LocalFree(new_dacl); LocalFree(*old_security_descriptor); LocalFree(owner); CloseHandle(*token); return FALSE; } } } /* * Create copy of the old security descriptor, so we can modify its DACL. * Function SetSecurityDescriptorDacl() works only with security descriptors * in absolute format. So use our helper function create_relsd_from_abssd() * for converting security descriptor from relative format (which is returned * by GetKernelObjectSecurity() function) to the absolute format. */ new_security_descriptor = create_relsd_from_abssd(*old_security_descriptor); if (!new_security_descriptor) { LocalFree(new_dacl); LocalFree(*old_security_descriptor); LocalFree(owner); CloseHandle(*token); return FALSE; } /* * In the new security descriptor replace old DACL by the new DACL (which has * new permissions) and then set this new security descriptor to the token, * so token would have new access permissions. */ if (!SetSecurityDescriptorDacl(new_security_descriptor, TRUE, new_dacl, FALSE) || !SetKernelObjectSecurity(*token, DACL_SECURITY_INFORMATION, new_security_descriptor)) { LocalFree(new_security_descriptor); LocalFree(new_dacl); LocalFree(*old_security_descriptor); LocalFree(owner); CloseHandle(*token); return FALSE; } LocalFree(new_security_descriptor); LocalFree(new_dacl); LocalFree(owner); return TRUE; } /* * Revert particular granted permissions in specified access token done by * grant_process_token_dacl_permissions() call. */ static VOID revert_token_dacl_permissions(HANDLE token, PSECURITY_DESCRIPTOR old_security_descriptor) { SetKernelObjectSecurity(token, DACL_SECURITY_INFORMATION, old_security_descriptor); LocalFree(old_security_descriptor); CloseHandle(token); } /* * Open process handle specified by the process id with the query right and * optionally also with vm read right. */ static HANDLE open_process_for_query(DWORD pid, BOOL with_vm_read) { BOOL revert_only_privilege; LUID luid_debug_privilege; OSVERSIONINFO version; DWORD process_right; HANDLE revert_token; HANDLE process; /* * Some processes on Windows Vista and higher systems can be opened only * with PROCESS_QUERY_LIMITED_INFORMATION right. This right is enough * for accessing primary process token. But this right is not supported * on older pre-Vista systems. When the current thread on these older * systems does not have Debug privilege then OpenProcess() fails with * ERROR_ACCESS_DENIED. If the current thread has Debug privilege then * OpenProcess() success and returns handle to requested process. * Problem is that this handle does not have PROCESS_QUERY_INFORMATION * right and so cannot be used for accessing primary process token * on those older systems. Moreover it has zero rights and therefore * such handle is fully useless. So never try to use open process with * PROCESS_QUERY_LIMITED_INFORMATION right on older systems than * Windows Vista (NT 6.0). */ version.dwOSVersionInfoSize = sizeof(version); if (GetVersionEx(&version) && version.dwPlatformId == VER_PLATFORM_WIN32_NT && version.dwMajorVersion >= 6) process_right = PROCESS_QUERY_LIMITED_INFORMATION; else process_right = PROCESS_QUERY_INFORMATION; if (with_vm_read) process_right |= PROCESS_VM_READ; process = OpenProcess(process_right, FALSE, pid); if (process) return process; /* * It is possible to open only processes to which owner of the current * thread access token has permissions. For opening other processing it * is required to have Debug privilege enabled. By default local * administrators have this privilege, but it is disabled. So try to * enable it and then try to open process again. */ if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid_debug_privilege)) return NULL; if (!win32_enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege)) return NULL; process = OpenProcess(process_right, FALSE, pid); win32_revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege); return process; } /* * Check if process image path name (wide string) matches exe file name * (7-bit ASCII string). Do case-insensitive string comparison. Process * image path name can be in any namespace format (DOS, Win32, UNC, ...). */ static BOOL check_process_name(LPCWSTR path, DWORD path_length, LPCSTR exe_file) { DWORD exe_file_length; WCHAR c1; UCHAR c2; DWORD i; exe_file_length = 0; while (exe_file[exe_file_length] != '\0') exe_file_length++; /* Path must have backslash before exe file name. */ if (exe_file_length >= path_length || path[path_length-exe_file_length-1] != L'\\') return FALSE; for (i = 0; i < exe_file_length; i++) { c1 = path[path_length-exe_file_length+i]; c2 = exe_file[i]; /* * Input string for comparison is 7-bit ASCII and file name part * of path must not contain backslash as it is path separator. */ if (c1 >= 0x80 || c2 >= 0x80 || c1 == L'\\') return FALSE; if (c1 >= L'a' && c1 <= L'z') c1 -= L'a' - L'A'; if (c2 >= 'a' && c2 <= 'z') c2 -= 'a' - 'A'; if (c1 != c2) return FALSE; } return TRUE; } /* Open process handle with the query right specified by process exe file. */ HANDLE win32_find_and_open_process_for_query(LPCSTR exe_file) { GetProcessImageFileNameWProt MyGetProcessImageFileNameW; GetModuleFileNameExWProt MyGetModuleFileNameExW; EnumProcessesProt MyEnumProcesses; HMODULE kernel32, psapi; UINT prev_error_mode; DWORD partial_retry; BOOL found_process; DWORD size, length; DWORD *processes; HANDLE process; LPWSTR path; DWORD error; DWORD count; DWORD i; psapi = NULL; kernel32 = GetModuleHandle(TEXT("kernel32.dll")); if (!kernel32) return NULL; /* * On Windows 7 and higher systems these functions are available in * kernel32.dll library with K32 prefix. */ MyGetModuleFileNameExW = NULL; MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(LPVOID)GetProcAddress(kernel32, "K32GetProcessImageFileNameW"); MyEnumProcesses = (EnumProcessesProt)(LPVOID)GetProcAddress(kernel32, "K32EnumProcesses"); if (!MyGetProcessImageFileNameW || !MyEnumProcesses) { /* * On older NT-based systems these functions are available in * psapi.dll library without K32 prefix. */ prev_error_mode = win32_change_error_mode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); psapi = LoadLibrary(TEXT("psapi.dll")); win32_change_error_mode(prev_error_mode); if (!psapi) return NULL; /* * Function GetProcessImageFileNameW() is available in * Windows XP and higher systems. On older versions is * available function GetModuleFileNameExW(). */ MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(LPVOID)GetProcAddress(psapi, "GetProcessImageFileNameW"); MyGetModuleFileNameExW = (GetModuleFileNameExWProt)(LPVOID)GetProcAddress(psapi, "GetModuleFileNameExW"); MyEnumProcesses = (EnumProcessesProt)(LPVOID)GetProcAddress(psapi, "EnumProcesses"); if ((!MyGetProcessImageFileNameW && !MyGetModuleFileNameExW) || !MyEnumProcesses) { FreeLibrary(psapi); return NULL; } } /* Make initial buffer size for 1024 processes. */ size = 1024 * sizeof(*processes); retry: processes = (DWORD *)LocalAlloc(LPTR, size); if (!processes) { if (psapi) FreeLibrary(psapi); return NULL; } if (!MyEnumProcesses(processes, size, &length)) { LocalFree(processes); if (psapi) FreeLibrary(psapi); return NULL; } else if (size == length) { /* * There is no indication given when the buffer is too small to * store all process identifiers. Therefore if returned length * is same as buffer size there can be more processes. Call * again with larger buffer. */ LocalFree(processes); size *= 2; goto retry; } process = NULL; count = length / sizeof(*processes); for (i = 0; i < count; i++) { /* Skip System Idle Process. */ if (processes[i] == 0) continue; /* * Function GetModuleFileNameExW() requires additional * PROCESS_VM_READ right as opposite to function * GetProcessImageFileNameW() which does not need it. */ process = open_process_for_query(processes[i], MyGetProcessImageFileNameW ? FALSE : TRUE); if (!process) continue; /* * Set initial buffer size to 256 (wide) characters. * Final path length on the modern NT-based systems can be also larger. */ size = 256; found_process = FALSE; partial_retry = 0; retry_path: path = (LPWSTR)LocalAlloc(LPTR, size * sizeof(*path)); if (!path) goto end_path; if (MyGetProcessImageFileNameW) length = MyGetProcessImageFileNameW(process, path, size); else length = MyGetModuleFileNameExW(process, NULL, path, size); error = GetLastError(); /* * GetModuleFileNameEx() returns zero and signal error ERROR_PARTIAL_COPY * when remote process is in the middle of updating its module table. * Sleep 10 ms and try again, max 10 attempts. */ if (!MyGetProcessImageFileNameW) { if (length == 0 && error == ERROR_PARTIAL_COPY && partial_retry++ < 10) { Sleep(10); goto retry_path; } partial_retry = 0; } /* * When buffer is too small then function GetModuleFileNameEx() returns * its size argument on older systems (Windows XP) or its size minus * argument one on new systems (Windows 10) without signalling any error. * Function GetProcessImageFileNameW() on the other hand returns zero * value and signals error ERROR_INSUFFICIENT_BUFFER. So in all these * cases call function again with larger buffer. */ if (MyGetProcessImageFileNameW && length == 0 && error != ERROR_INSUFFICIENT_BUFFER) goto end_path; if ((MyGetProcessImageFileNameW && length == 0) || (!MyGetProcessImageFileNameW && (length == size || length == size-1))) { LocalFree(path); size *= 2; goto retry_path; } if (length && check_process_name(path, length, exe_file)) found_process = TRUE; end_path: if (path) { LocalFree(path); path = NULL; } if (found_process) break; CloseHandle(process); process = NULL; } LocalFree(processes); if (psapi) FreeLibrary(psapi); return process; } /* * Try to open primary access token of the particular process with specified * rights. Before opening access token try to adjust DACL permissions of the * primary process access token, so following open does not fail on error * related to no open permissions. Revert DACL permissions after open attempt. * As following steps are not atomic, try to execute them more times in case * of possible race conditions caused by other threads or processes. */ static HANDLE try_grant_permissions_and_open_process_token(HANDLE process, DWORD rights) { PSECURITY_DESCRIPTOR old_security_descriptor; HANDLE grant_token; HANDLE token; DWORD retry; DWORD error; /* * This code is not atomic. Between grant and open calls can other * thread or process change or revert permissions. So try to execute * it more times. */ for (retry = 0; retry < 10; retry++) { if (!grant_process_token_dacl_permissions(process, rights, &grant_token, &old_security_descriptor)) return NULL; if (!OpenProcessToken(process, rights, &token)) { token = NULL; error = GetLastError(); } if (old_security_descriptor) revert_token_dacl_permissions(grant_token, old_security_descriptor); if (token) return token; else if (error != ERROR_ACCESS_DENIED) return NULL; } return NULL; } /* * Open primary access token of particular process handle with specified rights. * If permissions for specified rights are missing then try to grant them. */ HANDLE win32_open_process_token_with_rights(HANDLE process, DWORD rights) { HANDLE old_token; HANDLE token; /* First try to open primary access token of process handle directly. */ if (OpenProcessToken(process, rights, &token)) return token; /* * If opening failed then it means that owner of the current thread * access token does not have permission for it. Try it again with * primary process access token. */ if (change_token_to_primary(&old_token)) { if (!OpenProcessToken(process, rights, &token)) token = NULL; win32_revert_to_token(old_token); if (token) return token; } /* * If opening is still failing then try to grant specified permissions * for the current thread and try to open it again. */ token = try_grant_permissions_and_open_process_token(process, rights); if (token) return token; /* * And if it is still failing then try it again with granting * permissions for the primary process token of the current process. */ if (change_token_to_primary(&old_token)) { token = try_grant_permissions_and_open_process_token(process, rights); win32_revert_to_token(old_token); if (token) return token; } /* * TODO: Sorry, no other option for now... * It could be possible to use Take Ownership Name privilege to * temporary change token owner of specified process to the owner of * the current thread token, grant permissions for current thread in * that process token, change ownership back to original one, open * that process token and revert granted permissions. But this is * not implemented yet. */ return NULL; } /* * Call supplied function with its argument and if it fails with * ERROR_PRIVILEGE_NOT_HELD then try to enable Tcb privilege and * call function with its argument again. */ BOOL win32_call_func_with_tcb_privilege(BOOL (*function)(LPVOID), LPVOID argument) { LUID luid_tcb_privilege; LUID luid_impersonate_privilege; HANDLE revert_token_tcb_privilege; BOOL revert_only_tcb_privilege; HANDLE revert_token_impersonate_privilege; BOOL revert_only_impersonate_privilege; BOOL impersonate_privilege_enabled; BOOL revert_to_old_token; HANDLE old_token; HANDLE lsass_process; HANDLE lsass_token; DWORD error; BOOL ret; impersonate_privilege_enabled = FALSE; revert_to_old_token = FALSE; lsass_token = NULL; old_token = NULL; /* Call supplied function. */ ret = function(argument); if (ret || GetLastError() != ERROR_PRIVILEGE_NOT_HELD) goto ret; /* * If function call failed with ERROR_PRIVILEGE_NOT_HELD * error then it means that the current thread token does not have * Tcb privilege enabled. Try to enable it. */ if (!LookupPrivilegeValue(NULL, SE_TCB_NAME, &luid_tcb_privilege)) goto err_privilege_not_held; /* * If the current thread has already Tcb privilege enabled then there * is some additional unhanded restriction. */ if (win32_have_privilege(luid_tcb_privilege)) goto err_privilege_not_held; /* Try to enable Tcb privilege and try function call again. */ if (win32_enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege)) { ret = function(argument); win32_revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege); goto ret; } /* * If enabling of Tcb privilege failed then it means that current thread * does not have this privilege. But current process may have it. So try it * again with primary process access token. */ /* * If system supports Impersonate privilege (Windows 2000 SP4 or higher) then * all future actions in this function require this Impersonate privilege. * So try to enable it in case it is currently disabled. */ if (LookupPrivilegeValue(NULL, SE_IMPERSONATE_NAME, &luid_impersonate_privilege) && !win32_have_privilege(luid_impersonate_privilege)) { /* * If current thread does not have Impersonate privilege enabled * then first try to enable it just for the current thread. If * it is not possible to enable it just for the current thread * then try it to enable globally for whole process (which * affects all process threads). Both actions will be reverted * at the end of this function. */ if (win32_enable_privilege(luid_impersonate_privilege, &revert_token_impersonate_privilege, &revert_only_impersonate_privilege)) { impersonate_privilege_enabled = TRUE; } else if (win32_enable_privilege(luid_impersonate_privilege, NULL, NULL)) { impersonate_privilege_enabled = TRUE; revert_token_impersonate_privilege = NULL; revert_only_impersonate_privilege = TRUE; } else { goto err_privilege_not_held; } /* * Now when Impersonate privilege is enabled, try to enable Tcb * privilege again. Enabling other privileges for the current * thread requires Impersonate privilege, so enabling Tcb again * could now pass. */ if (win32_enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege)) { ret = function(argument); win32_revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege); goto ret; } } /* * If enabling Tcb privilege failed then it means that the current * thread access token does not have this privilege or does not * have permission to adjust privileges. * * Try to use more privileged token from Local Security Authority * Subsystem Service process (lsass.exe) which has Tcb privilege. * Retrieving this more privileged token is possible for local * administrators (unless it was disabled by local administrators). */ lsass_process = win32_find_and_open_process_for_query("lsass.exe"); if (!lsass_process) goto err_privilege_not_held; /* * Open primary lsass.exe process access token with query and duplicate * rights. Just these two rights are required for impersonating other * primary process token (impersonate right is really not required!). */ lsass_token = win32_open_process_token_with_rights(lsass_process, TOKEN_QUERY | TOKEN_DUPLICATE); CloseHandle(lsass_process); if (!lsass_token) goto err_privilege_not_held; /* * After successful open of the primary lsass.exe process access token, * assign its copy for the current thread. */ if (!win32_change_token(lsass_token, &old_token)) goto err_privilege_not_held; revert_to_old_token = TRUE; ret = function(argument); if (ret || GetLastError() != ERROR_PRIVILEGE_NOT_HELD) goto ret; /* * Now current thread is not using primary process token anymore * but is using custom access token. There is no need to revert * enabled Tcb privilege as the whole custom access token would * be reverted. So there is no need to setup revert method for * enabling privilege. */ if (win32_have_privilege(luid_tcb_privilege) || !win32_enable_privilege(luid_tcb_privilege, NULL, NULL)) goto err_privilege_not_held; ret = function(argument); goto ret; err_privilege_not_held: SetLastError(ERROR_PRIVILEGE_NOT_HELD); ret = FALSE; goto ret; ret: error = GetLastError(); if (revert_to_old_token) win32_revert_to_token(old_token); if (impersonate_privilege_enabled) win32_revert_privilege(luid_impersonate_privilege, revert_token_impersonate_privilege, revert_only_impersonate_privilege); if (lsass_token) CloseHandle(lsass_token); SetLastError(error); return ret; } pciutils-3.13.0/lib/libpci.pc.in0000644000175000001440000000031413763220061014716 0ustar mjusersprefix=@PREFIX@ includedir=@INCDIR@ libdir=@LIBDIR@ idsdir=@IDSDIR@ Name: libpci Description: libpci Version: @VERSION@ Libs: -L${libdir} -lpci Libs.private: @LDLIBS@ @WITH_LIBS@ Cflags: -I${includedir} pciutils-3.13.0/lib/i386-io-haiku.h0000644000175000001440000000620614564230650015104 0ustar mjusers/* * The PCI Library -- Access to i386 I/O ports on Haiku * * Copyright (c) 2009 Francois Revol * * Can be freely distributed and used under the terms of the GNU GPL v2+ * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include /* from haiku/trunk/headers/private/drivers/poke.h */ #define POKE_DEVICE_NAME "poke" #define POKE_DEVICE_FULLNAME "/dev/misc/poke" #define POKE_SIGNATURE 'wltp' // "We Like To Poke" enum { POKE_PORT_READ = B_DEVICE_OP_CODES_END + 1, POKE_PORT_WRITE, POKE_PORT_INDEXED_READ, POKE_PORT_INDEXED_WRITE, POKE_PCI_READ_CONFIG, POKE_PCI_WRITE_CONFIG, POKE_GET_NTH_PCI_INFO, POKE_GET_PHYSICAL_ADDRESS, POKE_MAP_MEMORY, POKE_UNMAP_MEMORY }; typedef struct { uint32 signature; uint8 index; pci_info* info; status_t status; } pci_info_args; typedef struct { uint32 signature; uint16 port; uint8 size; // == index for POKE_PORT_INDEXED_* uint32 value; } port_io_args; typedef struct { uint32 signature; uint8 bus; uint8 device; uint8 function; uint8 size; uint8 offset; uint32 value; } pci_io_args; /* en poke.h*/ static int poke_driver_fd; static int intel_setup_io(struct pci_access *a UNUSED) { /* * Opening poke device on systems with the linked change below * automatically changes process IOPL to 3 and closing its file * descriptor changes process IOPL back to 0, which give access * to all x86 IO ports via x86 in/out instructions for this * userspace process. To support also older systems without this * change, access IO ports via ioctl() instead of x86 in/out. * https://review.haiku-os.org/c/haiku/+/1077 */ poke_driver_fd = open(POKE_DEVICE_FULLNAME, O_RDWR); return (poke_driver_fd < 0) ? 0 : 1; } static inline void intel_cleanup_io(struct pci_access *a UNUSED) { close(poke_driver_fd); } static inline u8 intel_inb (u16 port) { port_io_args args = { POKE_SIGNATURE, port, sizeof(u8), 0 }; if (ioctl(poke_driver_fd, POKE_PORT_READ, &args, sizeof(args)) < 0) return 0; return (u8)args.value; } static inline u16 intel_inw (u16 port) { port_io_args args = { POKE_SIGNATURE, port, sizeof(u16), 0 }; if (ioctl(poke_driver_fd, POKE_PORT_READ, &args, sizeof(args)) < 0) return 0; return (u16)args.value; } static inline u32 intel_inl (u16 port) { port_io_args args = { POKE_SIGNATURE, port, sizeof(u32), 0 }; if (ioctl(poke_driver_fd, POKE_PORT_READ, &args, sizeof(args)) < 0) return 0; return (u32)args.value; } static inline void intel_outb (u8 value, u16 port) { port_io_args args = { POKE_SIGNATURE, port, sizeof(u8), value }; ioctl(poke_driver_fd, POKE_PORT_WRITE, &args, sizeof(args)); } static inline void intel_outw (u16 value, u16 port) { port_io_args args = { POKE_SIGNATURE, port, sizeof(u16), value }; ioctl(poke_driver_fd, POKE_PORT_WRITE, &args, sizeof(args)); } static inline void intel_outl (u32 value, u16 port) { port_io_args args = { POKE_SIGNATURE, port, sizeof(u32), value }; ioctl(poke_driver_fd, POKE_PORT_WRITE, &args, sizeof(args)); } static inline void intel_io_lock(void) { } static inline void intel_io_unlock(void) { } pciutils-3.13.0/lib/emulated.c0000644000175000001440000002347514443575171014517 0ustar mjusers/* * The PCI Library -- Virtual Emulated Config Space Access Functions * * Copyright (c) 2022 Pali Rohár * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "internal.h" static u32 ioflg_to_pciflg(pciaddr_t ioflg) { u32 flg = 0; if ((ioflg & PCI_IORESOURCE_TYPE_BITS) == PCI_IORESOURCE_IO) flg = PCI_BASE_ADDRESS_SPACE_IO; else if ((ioflg & PCI_IORESOURCE_TYPE_BITS) == PCI_IORESOURCE_MEM) { flg = PCI_BASE_ADDRESS_SPACE_MEMORY; if (ioflg & PCI_IORESOURCE_MEM_64) flg |= PCI_BASE_ADDRESS_MEM_TYPE_64; else flg |= PCI_BASE_ADDRESS_MEM_TYPE_32; if (ioflg & PCI_IORESOURCE_PREFETCH) flg |= PCI_BASE_ADDRESS_MEM_PREFETCH; } return flg; } static u32 baseres_to_pcires(pciaddr_t addr, pciaddr_t ioflg, int *have_sec, u32 *sec_val) { u32 val = ioflg_to_pciflg(ioflg); if (have_sec) *have_sec = 0; if ((val & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO && addr <= 0xffffffff) val |= addr & PCI_BASE_ADDRESS_IO_MASK; else if ((val & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) { val |= addr & PCI_BASE_ADDRESS_MEM_MASK; if ((val & PCI_BASE_ADDRESS_MEM_TYPE_64) && have_sec) { *have_sec = 1; *sec_val = addr >> 32; } } return val; } static inline u32 even_baseres_to_pcires(pciaddr_t addr, pciaddr_t ioflg) { return baseres_to_pcires(addr, ioflg, NULL, NULL); } static inline u32 odd_baseres_to_pcires(pciaddr_t addr0, pciaddr_t ioflg0, pciaddr_t addr, pciaddr_t ioflg) { int have_sec; u32 val; baseres_to_pcires(addr0, ioflg0, &have_sec, &val); if (!have_sec) val = baseres_to_pcires(addr, ioflg, NULL, NULL); return val; } int pci_emulated_read(struct pci_dev *d, int pos, byte *buf, int len) { u32 ht = PCI_HEADER_TYPE_NORMAL; u32 val = 0; int i; if (pos >= 64) return 0; if (len > 4) return pci_generic_block_read(d, pos, buf, len); if (d->device_class == PCI_CLASS_BRIDGE_PCI) ht = PCI_HEADER_TYPE_BRIDGE; else if (d->device_class == PCI_CLASS_BRIDGE_CARDBUS) ht = PCI_HEADER_TYPE_CARDBUS; switch (pos & ~3) { case PCI_COMMAND: for (i = 0; i < 6; i++) { if (!d->size[i]) continue; if ((d->flags[i] & PCI_IORESOURCE_TYPE_BITS) == PCI_IORESOURCE_IO) val |= PCI_COMMAND_IO; else if ((d->flags[i] & PCI_IORESOURCE_TYPE_BITS) == PCI_IORESOURCE_MEM) val |= PCI_COMMAND_MEMORY; } break; case PCI_VENDOR_ID: val = (d->device_id << 16) | d->vendor_id; break; case PCI_CLASS_REVISION: val = (d->device_class << 16) | (d->prog_if << 8) | d->rev_id; break; case PCI_CACHE_LINE_SIZE: val = ht << 16; break; case PCI_BASE_ADDRESS_0: val = even_baseres_to_pcires(d->base_addr[0], d->flags[0]); break; case PCI_INTERRUPT_LINE: val = (d->irq >= 0 && d->irq <= 0xff) ? d->irq : 0; break; } if ((pos & ~3) == PCI_BASE_ADDRESS_1 && (ht == PCI_HEADER_TYPE_NORMAL || ht == PCI_HEADER_TYPE_BRIDGE)) val = odd_baseres_to_pcires(d->base_addr[0], d->flags[0], d->base_addr[1], d->flags[1]); if (ht == PCI_HEADER_TYPE_NORMAL) switch (pos & ~3) { case PCI_BASE_ADDRESS_2: val = even_baseres_to_pcires(d->base_addr[2], d->flags[2]); break; case PCI_BASE_ADDRESS_3: val = odd_baseres_to_pcires(d->base_addr[2], d->flags[2], d->base_addr[3], d->flags[3]); break; case PCI_BASE_ADDRESS_4: val = even_baseres_to_pcires(d->base_addr[4], d->flags[4]); break; case PCI_BASE_ADDRESS_5: val = odd_baseres_to_pcires(d->base_addr[4], d->flags[4], d->base_addr[5], d->flags[5]); break; case PCI_SUBSYSTEM_VENDOR_ID: val = (d->subsys_id << 16) | d->subsys_vendor_id; break; case PCI_ROM_ADDRESS: val = d->rom_base_addr & PCI_ROM_ADDRESS_MASK; if (val) val |= PCI_ROM_ADDRESS_ENABLE; break; } else if (ht == PCI_HEADER_TYPE_BRIDGE) switch (pos & ~3) { case PCI_COMMAND: if (d->bridge_size[0]) val |= PCI_COMMAND_IO; if (d->bridge_size[1] || d->bridge_size[2]) val |= PCI_COMMAND_MEMORY; break; case PCI_PRIMARY_BUS: val = d->bus; break; case PCI_IO_BASE: if (d->bridge_size[0]) { val = (((((d->bridge_base_addr[0] + d->bridge_size[0] - 1) >> 8) & PCI_IO_RANGE_MASK) << 8) & 0xff00) | (((d->bridge_base_addr[0] >> 8) & PCI_IO_RANGE_MASK) & 0x00ff); if ((d->bridge_flags[0] & PCI_IORESOURCE_IO_16BIT_ADDR) && d->bridge_base_addr[0] + d->bridge_size[0] - 1 <= 0xffff) val |= (PCI_IO_RANGE_TYPE_16 << 8) | PCI_IO_RANGE_TYPE_16; else val |= (PCI_IO_RANGE_TYPE_32 << 8) | PCI_IO_RANGE_TYPE_32; } else val = 0xff & PCI_IO_RANGE_MASK; break; case PCI_MEMORY_BASE: if (d->bridge_size[1]) val = (((((d->bridge_base_addr[1] + d->bridge_size[1] - 1) >> 16) & PCI_MEMORY_RANGE_MASK) << 16) & 0xffff0000) | (((d->bridge_base_addr[1] >> 16) & PCI_MEMORY_RANGE_MASK) & 0x0000ffff); else val = 0xffff & PCI_MEMORY_RANGE_MASK; break; case PCI_PREF_MEMORY_BASE: if (d->bridge_size[2]) { val = (((((d->bridge_base_addr[2] + d->bridge_size[2] - 1) >> 16) & PCI_PREF_RANGE_MASK) << 16) & 0xffff0000) | (((d->bridge_base_addr[2] >> 16) & PCI_PREF_RANGE_MASK) & 0x0000ffff); if ((d->bridge_flags[2] & PCI_IORESOURCE_MEM_64) || d->bridge_base_addr[2] + d->bridge_size[2] - 1 > 0xffffffff) val |= (PCI_PREF_RANGE_TYPE_64 << 16) | PCI_PREF_RANGE_TYPE_64; else val |= (PCI_PREF_RANGE_TYPE_32 << 16) | PCI_PREF_RANGE_TYPE_32; } else val = 0xffff & PCI_PREF_RANGE_MASK; break; case PCI_PREF_BASE_UPPER32: if (d->bridge_size[2]) val = d->bridge_base_addr[2] >> 32; break; case PCI_PREF_LIMIT_UPPER32: if (d->bridge_size[2]) val = (d->bridge_base_addr[2] + d->bridge_size[2] - 1) >> 32; break; case PCI_IO_BASE_UPPER16: if (d->bridge_size[0]) val = ((((d->bridge_base_addr[0] + d->bridge_size[0] - 1) >> 16) << 16) & 0xffff0000) | ((d->bridge_base_addr[0] >> 16) & 0x0000ffff); break; case PCI_ROM_ADDRESS1: val = d->rom_base_addr & PCI_ROM_ADDRESS_MASK; if (val) val |= PCI_ROM_ADDRESS_ENABLE; break; } else if (ht == PCI_HEADER_TYPE_CARDBUS) switch (pos & ~3) { case PCI_COMMAND: if (d->bridge_size[0] || d->bridge_size[1]) val |= PCI_COMMAND_MEMORY; if (d->bridge_size[2] || d->bridge_size[3]) val |= PCI_COMMAND_IO; break; case PCI_CB_PRIMARY_BUS: val = d->bus; break; case PCI_CB_MEMORY_BASE_0: if (d->bridge_size[0]) val = d->bridge_base_addr[0] & ~0xfff; else val = 0xffffffff & ~0xfff; break; case PCI_CB_MEMORY_LIMIT_0: if (d->bridge_size[0]) val = (d->bridge_base_addr[0] + d->bridge_size[0] - 1) & ~0xfff; break; case PCI_CB_MEMORY_BASE_1: if (d->bridge_size[1]) val = d->bridge_base_addr[1] & ~0xfff; else val = 0xffffffff & ~0xfff; break; case PCI_CB_MEMORY_LIMIT_1: if (d->bridge_size[1]) val = (d->bridge_base_addr[1] + d->bridge_size[1] - 1) & ~0xfff; break; case PCI_CB_IO_BASE_0: if (d->bridge_size[2]) { val = d->bridge_base_addr[2] & PCI_CB_IO_RANGE_MASK; if ((d->bridge_flags[2] & PCI_IORESOURCE_IO_16BIT_ADDR) || d->bridge_base_addr[2] + d->bridge_size[2] - 1 <= 0xffff) val |= PCI_IO_RANGE_TYPE_16; else val |= PCI_IO_RANGE_TYPE_32; } else val = 0x0000ffff & PCI_CB_IO_RANGE_MASK; break; case PCI_CB_IO_LIMIT_0: if (d->bridge_size[2]) val = (d->bridge_base_addr[2] + d->bridge_size[2] - 1) & PCI_CB_IO_RANGE_MASK; break; case PCI_CB_IO_BASE_1: if (d->bridge_size[3]) { val = d->bridge_base_addr[3] & PCI_CB_IO_RANGE_MASK; if ((d->bridge_flags[3] & PCI_IORESOURCE_IO_16BIT_ADDR) || d->bridge_base_addr[3] + d->bridge_size[3] - 1 <= 0xffff) val |= PCI_IO_RANGE_TYPE_16; else val |= PCI_IO_RANGE_TYPE_32; } else val = 0x0000ffff & PCI_CB_IO_RANGE_MASK; break; case PCI_CB_IO_LIMIT_1: if (d->bridge_size[3]) val = (d->bridge_base_addr[3] + d->bridge_size[3] - 1) & PCI_CB_IO_RANGE_MASK; break; case PCI_CB_BRIDGE_CONTROL: if (d->bridge_flags[0] & PCI_IORESOURCE_PREFETCH) val |= PCI_CB_BRIDGE_CTL_PREFETCH_MEM0; if (d->bridge_flags[1] & PCI_IORESOURCE_PREFETCH) val |= PCI_CB_BRIDGE_CTL_PREFETCH_MEM1; break; case PCI_CB_SUBSYSTEM_VENDOR_ID: val = (d->subsys_id << 16) | d->subsys_vendor_id; break; } if (len <= 2) val = (val >> (8 * (pos & 3))) & ((1 << (len * 8)) - 1); while (len-- > 0) { *(buf++) = val & 0xff; val >>= 8; } return 1; } pciutils-3.13.0/lib/nbsd-libpci.c0000644000175000001440000000650714601633257015076 0ustar mjusers/* * The PCI Library -- NetBSD libpci access * (based on FreeBSD /dev/pci access) * * Copyright (c) 1999 Jari Kirma * Copyright (c) 2002 Quentin Garnier * Copyright (c) 2002 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ /* * Read functionality of this driver is briefly tested, and seems * to supply basic information correctly, but I promise no more. */ #include #include #include #include #include "internal.h" static void nbsd_config(struct pci_access *a) { pci_define_param(a, "nbsd.path", PCI_PATH_NBSD_DEVICE, "Path to the NetBSD PCI device"); } static int nbsd_detect(struct pci_access *a) { char *name = pci_get_param(a, "nbsd.path"); if (access(name, R_OK)) { a->warning("Cannot open %s", name); return 0; } if (!access(name, W_OK)) a->writeable = O_RDWR; a->debug("...using %s", name); return 1; } static void nbsd_init(struct pci_access *a) { char *name = pci_get_param(a, "nbsd.path"); int mode = a->writeable ? O_RDWR : O_RDONLY; a->fd = open(name, mode, 0); if (a->fd < 0) a->error("nbsd_init: %s open failed", name); } static void nbsd_cleanup(struct pci_access *a) { close(a->fd); } static int nbsd_read(struct pci_dev *d, int pos, byte *buf, int len) { pcireg_t val; int shift; if (!(len == 1 || len == 2 || len == 4)) return pci_generic_block_read(d, pos, buf, len); if (d->domain || pos >= 4096) return 0; shift = 8*(pos % 4); pos &= ~3; if (pcibus_conf_read(d->access->fd, d->bus, d->dev, d->func, pos, &val) < 0) d->access->error("nbsd_read: pci_bus_conf_read() failed"); switch (len) { case 1: *buf = val >> shift; break; case 2: *(u16*)buf = cpu_to_le16(val >> shift); break; case 4: *(u32*)buf = cpu_to_le32(val); break; } return 1; } static int nbsd_write(struct pci_dev *d, int pos, byte *buf, int len) { pcireg_t val = 0; int shift; if (!(len == 1 || len == 2 || len == 4)) return pci_generic_block_write(d, pos, buf, len); if (d->domain || pos >= 256) return 0; /* * BEWARE: NetBSD seems to support only 32-bit access, so we have * to emulate byte and word writes by read-modify-write, possibly * causing troubles. */ shift = 8*(pos % 4); pos &= ~3; if (len != 4) { if (pcibus_conf_read(d->access->fd, d->bus, d->dev, d->func, pos, &val) < 0) d->access->error("nbsd_write: pci_bus_conf_read() failed"); } switch (len) { case 1: val = (val & ~(0xff << shift)) | (buf[0] << shift); break; case 2: val = (val & ~(0xffff << shift)) | (le16_to_cpu(*(u16*)buf) << shift); break; case 4: val = le32_to_cpu(*(u32*)buf); break; } if (pcibus_conf_write(d->access->fd, d->bus, d->dev, d->func, pos, val) < 0) d->access->error("nbsd_write: pci_bus_conf_write() failed"); return 1; } struct pci_methods pm_nbsd_libpci = { .name = "nbsd-libpci", .help = "NetBSD libpci", .config = nbsd_config, .detect = nbsd_detect, .init = nbsd_init, .cleanup = nbsd_cleanup, .scan = pci_generic_scan, .fill_info = pci_generic_fill_info, .read = nbsd_read, .write = nbsd_write, }; pciutils-3.13.0/lib/pci.h0000644000175000001440000003212414626121022013447 0ustar mjusers/* * The PCI Library * * Copyright (c) 1997--2024 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+ * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef _PCI_LIB_H #define _PCI_LIB_H #ifndef PCI_CONFIG_H #include "config.h" #endif #include "header.h" #include "types.h" #define PCI_LIB_VERSION 0x030d00 #ifndef PCI_ABI #define PCI_ABI #endif /* * PCI Access Structure */ struct pci_methods; enum pci_access_type { /* Known access methods, remember to update init.c as well */ PCI_ACCESS_AUTO, /* Autodetection */ PCI_ACCESS_SYS_BUS_PCI, /* Linux /sys/bus/pci */ PCI_ACCESS_PROC_BUS_PCI, /* Linux /proc/bus/pci */ PCI_ACCESS_I386_TYPE1, /* i386 ports, type 1 */ PCI_ACCESS_I386_TYPE2, /* i386 ports, type 2 */ PCI_ACCESS_FBSD_DEVICE, /* FreeBSD /dev/pci */ PCI_ACCESS_AIX_DEVICE, /* /dev/pci0, /dev/bus0, etc. */ PCI_ACCESS_NBSD_LIBPCI, /* NetBSD libpci */ PCI_ACCESS_OBSD_DEVICE, /* OpenBSD /dev/pci */ PCI_ACCESS_DUMP, /* Dump file */ PCI_ACCESS_DARWIN, /* Darwin */ PCI_ACCESS_SYLIXOS_DEVICE, /* SylixOS pci */ PCI_ACCESS_HURD, /* GNU/Hurd */ PCI_ACCESS_WIN32_CFGMGR32, /* Win32 cfgmgr32.dll */ PCI_ACCESS_WIN32_KLDBG, /* Win32 kldbgdrv.sys */ PCI_ACCESS_WIN32_SYSDBG, /* Win32 NT SysDbg */ PCI_ACCESS_MMIO_TYPE1, /* MMIO ports, type 1 */ PCI_ACCESS_MMIO_TYPE1_EXT, /* MMIO ports, type 1 extended */ PCI_ACCESS_ECAM, /* PCIe ECAM via /dev/mem */ PCI_ACCESS_AOS_EXPANSION, /* AmigaOS Expansion library */ PCI_ACCESS_MAX }; struct pci_access { /* Options you can change: */ unsigned int method; /* Access method */ int writeable; /* Open in read/write mode */ int buscentric; /* Bus-centric view of the world */ char *id_file_name; /* Name of ID list file (use pci_set_name_list_path()) */ int free_id_name; /* Set if id_file_name is malloced */ int numeric_ids; /* Enforce PCI_LOOKUP_NUMERIC (>1 => PCI_LOOKUP_MIXED) */ unsigned int id_lookup_mode; /* pci_lookup_mode flags which are set automatically */ /* Default: PCI_LOOKUP_CACHE */ int debugging; /* Turn on debugging messages */ /* Functions you can override: */ void (*error)(char *msg, ...) PCI_PRINTF(1,2) PCI_NONRET; /* Write error message and quit */ void (*warning)(char *msg, ...) PCI_PRINTF(1,2); /* Write a warning message */ void (*debug)(char *msg, ...) PCI_PRINTF(1,2); /* Write a debugging message */ struct pci_dev *devices; /* Devices found on this bus */ /* Fields used internally: */ struct pci_methods *methods; struct pci_param *params; struct id_entry **id_hash; /* names.c */ struct id_bucket *current_id_bucket; int id_load_attempted; int id_cache_status; /* 0=not read, 1=read, 2=dirty */ char *id_cache_name; struct udev *id_udev; /* names-hwdb.c */ struct udev_hwdb *id_udev_hwdb; int fd; /* proc/sys: fd for config space */ int fd_rw; /* proc/sys: fd opened read-write */ int fd_vpd; /* sys: fd for VPD */ struct pci_dev *cached_dev; /* proc/sys: device the fds are for */ void *backend_data; /* Private data of the back end */ }; /* Initialize PCI access */ struct pci_access *pci_alloc(void) PCI_ABI; void pci_init(struct pci_access *) PCI_ABI; void pci_cleanup(struct pci_access *) PCI_ABI; /* Scanning of devices */ void pci_scan_bus(struct pci_access *acc) PCI_ABI; struct pci_dev *pci_get_dev(struct pci_access *acc, int domain, int bus, int dev, int func) PCI_ABI; /* Raw access to specified device */ void pci_free_dev(struct pci_dev *) PCI_ABI; /* Names of access methods */ int pci_lookup_method(char *name) PCI_ABI; /* Returns -1 if not found */ char *pci_get_method_name(int index) PCI_ABI; /* Returns "" if unavailable, NULL if index out of range */ /* * Named parameters */ struct pci_param { struct pci_param *next; /* Please use pci_walk_params() for traversing the list */ char *param; /* Name of the parameter */ char *value; /* Value of the parameter */ int value_malloced; /* used internally */ char *help; /* Explanation of the parameter */ }; char *pci_get_param(struct pci_access *acc, char *param) PCI_ABI; int pci_set_param(struct pci_access *acc, char *param, char *value) PCI_ABI; /* 0 on success, -1 if no such parameter */ /* To traverse the list, call pci_walk_params repeatedly, first with prev=NULL, and do not modify the parameters during traversal. */ struct pci_param *pci_walk_params(struct pci_access *acc, struct pci_param *prev) PCI_ABI; /* * Devices */ struct pci_dev { struct pci_dev *next; /* Next device in the chain */ u16 domain_16; /* 16-bit version of the PCI domain for backward compatibility */ /* 0xffff if the real domain doesn't fit in 16 bits */ u8 bus, dev, func; /* Bus inside domain, device and function */ /* These fields are set by pci_fill_info() */ unsigned int known_fields; /* Set of info fields already known (see pci_fill_info()) */ u16 vendor_id, device_id; /* Identity of the device */ u16 device_class; /* PCI device class */ int irq; /* IRQ number */ pciaddr_t base_addr[6]; /* Base addresses including flags in lower bits */ pciaddr_t size[6]; /* Region sizes */ pciaddr_t rom_base_addr; /* Expansion ROM base address */ pciaddr_t rom_size; /* Expansion ROM size */ struct pci_cap *first_cap; /* List of capabilities */ char *phy_slot; /* Physical slot */ char *module_alias; /* Linux kernel module alias */ char *label; /* Device name as exported by BIOS */ int numa_node; /* NUMA node */ pciaddr_t flags[6]; /* PCI_IORESOURCE_* flags for regions */ pciaddr_t rom_flags; /* PCI_IORESOURCE_* flags for expansion ROM */ int domain; /* PCI domain (host bridge) */ pciaddr_t bridge_base_addr[4]; /* Bridge base addresses (without flags) */ pciaddr_t bridge_size[4]; /* Bridge sizes */ pciaddr_t bridge_flags[4]; /* PCI_IORESOURCE_* flags for bridge addresses */ u8 prog_if, rev_id; /* Programming interface for device_class and revision id */ u16 subsys_vendor_id, subsys_id; /* Subsystem vendor id and subsystem id */ struct pci_dev *parent; /* Parent device, does not have to be always accessible */ int no_config_access; /* No access to config space for this device */ u32 rcd_link_cap; /* Link Capabilities register for Restricted CXL Devices */ u16 rcd_link_status; /* Link Status register for RCD */ u16 rcd_link_ctrl; /* Link Control register for RCD */ /* Fields used internally */ struct pci_access *access; struct pci_methods *methods; u8 *cache; /* Cached config registers */ int cache_len; int hdrtype; /* Cached low 7 bits of header type, -1 if unknown */ void *backend_data; /* Private data for of the back end */ struct pci_property *properties; /* A linked list of extra properties */ struct pci_cap *last_cap; /* Last capability in the list */ }; #define PCI_ADDR_IO_MASK (~(pciaddr_t) 0x3) #define PCI_ADDR_MEM_MASK (~(pciaddr_t) 0xf) #define PCI_ADDR_FLAG_MASK 0xf /* Access to configuration space */ u8 pci_read_byte(struct pci_dev *, int pos) PCI_ABI; u16 pci_read_word(struct pci_dev *, int pos) PCI_ABI; u32 pci_read_long(struct pci_dev *, int pos) PCI_ABI; int pci_read_vpd(struct pci_dev *d, int pos, u8 *buf, int len) PCI_ABI; int pci_write_byte(struct pci_dev *, int pos, u8 data) PCI_ABI; int pci_write_word(struct pci_dev *, int pos, u16 data) PCI_ABI; int pci_write_long(struct pci_dev *, int pos, u32 data) PCI_ABI; /* Configuration space as a sequence of bytes (little-endian) */ int pci_read_block(struct pci_dev *, int pos, u8 *buf, int len) PCI_ABI; int pci_write_block(struct pci_dev *, int pos, u8 *buf, int len) PCI_ABI; /* * Most device properties take some effort to obtain, so libpci does not * initialize them during default bus scan. Instead, you have to call * pci_fill_info() with the proper PCI_FILL_xxx constants OR'ed together. * * Some properties are stored directly in the pci_dev structure. * The remaining ones can be accessed through pci_get_string_property(). * * pci_fill_info() returns the current value of pci_dev->known_fields. * This is a bit mask of all fields, which were already obtained during * the lifetime of the device. This includes fields which are not supported * by the particular device -- in that case, the field is left at its default * value, which is 0 for integer fields and NULL for pointers. On the other * hand, we never consider known fields unsupported by the current back-end; * such fields always contain the default value. * * XXX: flags and the result should be unsigned, but we do not want to break the ABI. */ int pci_fill_info(struct pci_dev *, int flags) PCI_ABI; char *pci_get_string_property(struct pci_dev *d, u32 prop) PCI_ABI; #define PCI_FILL_IDENT 0x0001 /* vendor and device ID */ #define PCI_FILL_IRQ 0x0002 #define PCI_FILL_BASES 0x0004 #define PCI_FILL_ROM_BASE 0x0008 #define PCI_FILL_SIZES 0x0010 #define PCI_FILL_CLASS 0x0020 #define PCI_FILL_CAPS 0x0040 /* capabilities */ #define PCI_FILL_EXT_CAPS 0x0080 /* extended capabilities */ #define PCI_FILL_PHYS_SLOT 0x0100 /* physical slot (string property) */ #define PCI_FILL_MODULE_ALIAS 0x0200 /* Linux kernel module alias (string property) */ #define PCI_FILL_LABEL 0x0400 /* (string property) */ #define PCI_FILL_NUMA_NODE 0x0800 #define PCI_FILL_IO_FLAGS 0x1000 #define PCI_FILL_DT_NODE 0x2000 /* Device tree node (string property) */ #define PCI_FILL_IOMMU_GROUP 0x4000 /* (string property) */ #define PCI_FILL_BRIDGE_BASES 0x8000 #define PCI_FILL_RESCAN 0x00010000 /* force re-scan of cached properties */ #define PCI_FILL_CLASS_EXT 0x00020000 /* prog_if and rev_id */ #define PCI_FILL_SUBSYS 0x00040000 /* subsys_vendor_id and subsys_id */ #define PCI_FILL_PARENT 0x00080000 #define PCI_FILL_DRIVER 0x00100000 /* OS driver currently in use (string property) */ #define PCI_FILL_RCD_LNK 0x00200000 /* CXL RCD Link status properties (rcd_*) */ void pci_setup_cache(struct pci_dev *, u8 *cache, int len) PCI_ABI; /* * Capabilities */ struct pci_cap { struct pci_cap *next; u16 id; /* PCI_CAP_ID_xxx */ u16 type; /* PCI_CAP_xxx */ unsigned int addr; /* Position in the config space */ }; #define PCI_CAP_NORMAL 1 /* Traditional PCI capabilities */ #define PCI_CAP_EXTENDED 2 /* PCIe extended capabilities */ struct pci_cap *pci_find_cap(struct pci_dev *, unsigned int id, unsigned int type) PCI_ABI; struct pci_cap *pci_find_cap_nr(struct pci_dev *, unsigned int id, unsigned int type, unsigned int *cap_number) PCI_ABI; /* * Filters */ struct pci_filter { int domain, bus, slot, func; /* -1 = ANY */ int vendor, device; int device_class; unsigned int device_class_mask; /* Which bits of the device_class are compared, default=all */ int prog_if; int rfu[1]; }; void pci_filter_init(struct pci_access *, struct pci_filter *) PCI_ABI; char *pci_filter_parse_slot(struct pci_filter *, char *) PCI_ABI; char *pci_filter_parse_id(struct pci_filter *, char *) PCI_ABI; int pci_filter_match(struct pci_filter *, struct pci_dev *) PCI_ABI; /* * Conversion of PCI ID's to names (according to the pci.ids file) * * Call pci_lookup_name() to identify different types of ID's: * * VENDOR (vendorID) -> vendor * DEVICE (vendorID, deviceID) -> device * VENDOR | DEVICE (vendorID, deviceID) -> combined vendor and device * SUBSYSTEM | VENDOR (subvendorID) -> subsystem vendor * SUBSYSTEM | DEVICE (vendorID, deviceID, subvendorID, subdevID) -> subsystem device * SUBSYSTEM | VENDOR | DEVICE (vendorID, deviceID, subvendorID, subdevID) -> combined subsystem v+d * SUBSYSTEM | ... (-1, -1, subvendorID, subdevID) -> generic subsystem * CLASS (classID) -> class * PROGIF (classID, progif) -> programming interface */ char *pci_lookup_name(struct pci_access *a, char *buf, int size, int flags, ...) PCI_ABI; int pci_load_name_list(struct pci_access *a) PCI_ABI; /* Called automatically by pci_lookup_*() when needed; returns success */ void pci_free_name_list(struct pci_access *a) PCI_ABI; /* Called automatically by pci_cleanup() */ void pci_set_name_list_path(struct pci_access *a, char *name, int to_be_freed) PCI_ABI; void pci_id_cache_flush(struct pci_access *a) PCI_ABI; enum pci_lookup_mode { PCI_LOOKUP_VENDOR = 1, /* Vendor name (args: vendorID) */ PCI_LOOKUP_DEVICE = 2, /* Device name (args: vendorID, deviceID) */ PCI_LOOKUP_CLASS = 4, /* Device class (args: classID) */ PCI_LOOKUP_SUBSYSTEM = 8, PCI_LOOKUP_PROGIF = 16, /* Programming interface (args: classID, prog_if) */ PCI_LOOKUP_NUMERIC = 0x10000, /* Want only formatted numbers; default if access->numeric_ids is set */ PCI_LOOKUP_NO_NUMBERS = 0x20000, /* Return NULL if not found in the database; default is to print numerically */ PCI_LOOKUP_MIXED = 0x40000, /* Include both numbers and names */ PCI_LOOKUP_NETWORK = 0x80000, /* Try to resolve unknown ID's by DNS */ PCI_LOOKUP_SKIP_LOCAL = 0x100000, /* Do not consult local database */ PCI_LOOKUP_CACHE = 0x200000, /* Consult the local cache before using DNS */ PCI_LOOKUP_REFRESH_CACHE = 0x400000, /* Forget all previously cached entries, but still allow updating the cache */ PCI_LOOKUP_NO_HWDB = 0x800000, /* Do not ask udev's hwdb */ }; #endif pciutils-3.13.0/lib/aos-expansion.c0000644000175000001440000001303314601632511015454 0ustar mjusers/* * The PCI Library -- Configuration Access via AmigaOS 4.x expansion.library * * Copyright (c) 2024 Olrick Lefebvre * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #define _GNU_SOURCE #include #include #include #include // have to undef PCI values to avoid redefine warning #undef PCI_BASE_ADDRESS_MEM_MASK #undef PCI_BASE_ADDRESS_IO_MASK #undef PCI_ROM_ADDRESS_MASK #include #include #include #include #include #include #include "internal.h" // custom Amiga x.y version tag #define VERSTAG "\0$VER: pciutils " PCILIB_VERSION " (" PCILIB_DATE_AMIGAOS ") AmigaOS4 port" /*** AmigaOS access support ***/ typedef struct _PCIAccess { struct ExpansionBase *expansion; struct PCIIFace *ipci; } PCIAccess; static void aos_close_pci_interface(struct pci_access *a) { PCIAccess *pci = a->backend_data; if (pci) { if (pci->expansion) { if (pci->ipci) { IExec->DropInterface((struct Interface *)pci->ipci); pci->ipci = NULL; } IExec->CloseLibrary((struct Library *)pci->expansion); pci->expansion = NULL; } pci_mfree(pci); a->backend_data = NULL; } } static BOOL aos_open_pci_interface(struct pci_access *a) { PCIAccess *pci; BOOL res = FALSE; if (NULL == a->backend_data) { pci = pci_malloc(a, sizeof(PCIAccess)); a->backend_data = pci; pci->expansion = (struct ExpansionBase *)IExec->OpenLibrary("expansion.library", 0); if(NULL == pci->expansion) { a->warning("Unable to open expansion.library"); aos_close_pci_interface(a); } else { pci->ipci = (struct PCIIFace *)IExec->GetInterface((struct Library *)pci->expansion, "pci", 1, TAG_DONE); if(NULL == pci->ipci) { a->warning("Unable to obtain pci interface"); aos_close_pci_interface(a); } else { res = TRUE; } } } else { res = TRUE; // already opened } return res; } static int aos_expansion_detect(struct pci_access *a) { int res = FALSE; struct PCIDevice *device = NULL; PCIAccess *pci; if(TRUE == aos_open_pci_interface(a)) { pci = a->backend_data; // Try to read PCI first device device = pci->ipci->FindDeviceTags(FDT_Index, 0); if(NULL == device) { a->warning("AmigaOS Expansion PCI interface cannot find any device"); aos_close_pci_interface(a); } else { pci->ipci->FreeDevice(device); res = TRUE; } } return res; } static void aos_expansion_init(struct pci_access *a) { // to avoid flushing of version tag static STRPTR USED ver = (STRPTR)VERSTAG; if (!aos_open_pci_interface(a)) { a->debug("\n"); a->error("AmigaOS Expansion PCI interface cannot be accessed."); } } static void aos_expansion_cleanup(struct pci_access *a) { aos_close_pci_interface(a); } static void aos_expansion_scan(struct pci_access *a) { struct PCIDevice *device = NULL; PCIAccess *pci = NULL; UBYTE bus_num; UBYTE dev_num; UBYTE fn_num; struct pci_dev *d; int found_devs = 0; pci = a->backend_data; // X1000 has a bug which left shifts secondary bus by one bit, so we don't scan but get all devices identified by the system device = pci->ipci->FindDeviceTags(FDT_Index, found_devs); while (device) { d = pci_alloc_dev(a); d->domain = 0; // only one domain for AmigaOS device->GetAddress(&bus_num, &dev_num, &fn_num); d->bus = bus_num; d->dev = dev_num; d->func = fn_num; d->backend_data = device; d->vendor_id = device->ReadConfigWord(PCI_VENDOR_ID); d->device_id = device->ReadConfigWord(PCI_DEVICE_ID); d->known_fields = PCI_FILL_IDENT; d->hdrtype = device->ReadConfigByte(PCI_HEADER_TYPE) & ~PCI_HEADER_TYPE_MULTIFUNCTION; pci_link_dev(a, d); a->debug(" Found device %02x:%02x.%d %04x:%04x\n", d->bus, d->dev, d->func, d->vendor_id, d->device_id); found_devs++; device = pci->ipci->FindDeviceTags(FDT_Index, found_devs); } } static int aos_expansion_read(struct pci_dev *d, int pos, byte *buf, int len) { int res = FALSE; byte *ptr = buf; if (d->backend_data) { for (int i = 0; i < len; i++) { // byte by byte to avoid endianness troubles *ptr = ((struct PCIDevice *)(d->backend_data))->ReadConfigByte(pos + i); ptr++; res = TRUE; } } return res; } static int aos_expansion_write(struct pci_dev *d, int pos, byte *buf, int len) { int res = FALSE; byte *ptr = buf; if (d->backend_data) { for (int i = 0; i < len; i++) { // byte by byte to avoid endianness troubles ((struct PCIDevice *)(d->backend_data))->WriteConfigByte(pos + i, *ptr); ptr++; res = TRUE; } } return res; } static void aos_expansion_init_dev(struct pci_dev *d) { d->backend_data = NULL; // struct PCIDevice * to be obtained } static void aos_expansion_cleanup_dev(struct pci_dev *d) { PCIAccess *pci; if (d->backend_data && d->access->backend_data) { pci = d->access->backend_data; pci->ipci->FreeDevice((struct PCIDevice *)d->backend_data); d->backend_data = NULL; } } struct pci_methods pm_aos_expansion = { .name = "aos-expansion", .help = "The Expansion.library on AmigaOS 4.x", .detect = aos_expansion_detect, // detect, mandatory because called without check .init = aos_expansion_init, // init, called once access chosen, eventually after detect .cleanup = aos_expansion_cleanup, // cleanup, called at the end .scan = aos_expansion_scan, .fill_info = pci_generic_fill_info, .read = aos_expansion_read, .write = aos_expansion_write, .init_dev = aos_expansion_init_dev, .cleanup_dev = aos_expansion_cleanup_dev, }; pciutils-3.13.0/lib/internal.h0000600000175000001440000001237414626120240014506 0ustar mjusers/* * The PCI Library -- Internal Stuff * * Copyright (c) 1997--2022 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+ * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef _INTERNAL_H #define _INTERNAL_H #include "config.h" #ifdef PCI_SHARED_LIB #define PCI_ABI __attribute__((visibility("default"))) // Functions, which are bound to externally visible symbols by the versioning // mechanism, have to be declared as VERSIONED. Otherwise, GCC with global // optimizations is happy to optimize them away, leading to linker failures. #define VERSIONED_ABI __attribute__((used)) PCI_ABI #ifdef __APPLE__ #define STATIC_ALIAS(_decl, _for) VERSIONED_ABI _decl { return _for; } #define DEFINE_ALIAS(_decl, _for) #define SYMBOL_VERSION(_int, _ext) #else #define DEFINE_ALIAS(_decl, _for) extern _decl __attribute__((alias(#_for))) VERSIONED_ABI #ifdef _WIN32 #define STATIC_ALIAS(_decl, _for) VERSIONED_ABI _decl { return _for; } /* GCC does not support asm .symver directive for Windows targets, so define new external global function symbol as alias to internal symbol */ #define SYMBOL_VERSION(_int, _ext) asm(".globl\t" PCI_STRINGIFY(__MINGW_USYMBOL(_ext)) "\n\t" \ ".def\t" PCI_STRINGIFY(__MINGW_USYMBOL(_ext)) ";\t.scl\t2;\t.type\t32;\t.endef\n\t" \ ".set\t" PCI_STRINGIFY(__MINGW_USYMBOL(_ext)) "," PCI_STRINGIFY(__MINGW_USYMBOL(_int))) #else #define STATIC_ALIAS(_decl, _for) #define SYMBOL_VERSION(_int, _ext) asm(".symver " #_int "," #_ext) #endif #endif #else #define VERSIONED_ABI #define STATIC_ALIAS(_decl, _for) _decl { return _for; } #define DEFINE_ALIAS(_decl, _for) #define SYMBOL_VERSION(_int, _ext) #endif #include "pci.h" #include "sysdep.h" /* Old 32-bit-only versions of MinGW32 do not define __MINGW_USYMBOL macro */ #ifdef __MINGW32__ #ifndef __MINGW_USYMBOL #define __MINGW_USYMBOL(sym) _##sym #endif #endif #define _PCI_STRINGIFY(x) #x #define PCI_STRINGIFY(x) _PCI_STRINGIFY(x) struct pci_methods { char *name; char *help; void (*config)(struct pci_access *); int (*detect)(struct pci_access *); void (*init)(struct pci_access *); void (*cleanup)(struct pci_access *); void (*scan)(struct pci_access *); void (*fill_info)(struct pci_dev *, unsigned int flags); int (*read)(struct pci_dev *, int pos, byte *buf, int len); int (*write)(struct pci_dev *, int pos, byte *buf, int len); int (*read_vpd)(struct pci_dev *, int pos, byte *buf, int len); void (*init_dev)(struct pci_dev *); void (*cleanup_dev)(struct pci_dev *); }; /* generic.c */ void pci_generic_scan_bus(struct pci_access *, byte *busmap, int domain, int bus); void pci_generic_scan_domain(struct pci_access *, int domain); void pci_generic_scan(struct pci_access *); void pci_generic_fill_info(struct pci_dev *, unsigned int flags); int pci_generic_block_read(struct pci_dev *, int pos, byte *buf, int len); int pci_generic_block_write(struct pci_dev *, int pos, byte *buf, int len); /* emulated.c */ int pci_emulated_read(struct pci_dev *d, int pos, byte *buf, int len); /* init.c */ void *pci_malloc(struct pci_access *, int); void pci_mfree(void *); char *pci_strdup(struct pci_access *a, const char *s); struct pci_access *pci_clone_access(struct pci_access *a); int pci_init_internal(struct pci_access *a, int skip_method); void pci_init_v30(struct pci_access *a) VERSIONED_ABI; void pci_init_v35(struct pci_access *a) VERSIONED_ABI; /* access.c */ struct pci_dev *pci_alloc_dev(struct pci_access *); int pci_link_dev(struct pci_access *, struct pci_dev *); int pci_fill_info_v30(struct pci_dev *, int flags) VERSIONED_ABI; int pci_fill_info_v31(struct pci_dev *, int flags) VERSIONED_ABI; int pci_fill_info_v32(struct pci_dev *, int flags) VERSIONED_ABI; int pci_fill_info_v33(struct pci_dev *, int flags) VERSIONED_ABI; int pci_fill_info_v34(struct pci_dev *, int flags) VERSIONED_ABI; int pci_fill_info_v35(struct pci_dev *, int flags) VERSIONED_ABI; int pci_fill_info_v38(struct pci_dev *, int flags) VERSIONED_ABI; int pci_fill_info_v313(struct pci_dev *, int flags) VERSIONED_ABI; static inline int want_fill(struct pci_dev *d, unsigned want_fields, unsigned int try_fields) { want_fields &= try_fields; if ((d->known_fields & want_fields) == want_fields) return 0; else { d->known_fields |= try_fields; return 1; } } static inline void clear_fill(struct pci_dev *d, unsigned clear_fields) { d->known_fields &= ~clear_fields; } struct pci_property { struct pci_property *next; u32 key; char value[1]; }; char *pci_set_property(struct pci_dev *d, u32 key, char *value); /* params.c */ struct pci_param *pci_define_param(struct pci_access *acc, char *param, char *val, char *help); int pci_set_param_internal(struct pci_access *acc, char *param, char *val, int copy); void pci_free_params(struct pci_access *acc); /* caps.c */ void pci_scan_caps(struct pci_dev *, unsigned int want_fields); void pci_free_caps(struct pci_dev *); extern struct pci_methods pm_intel_conf1, pm_intel_conf2, pm_linux_proc, pm_fbsd_device, pm_aix_device, pm_nbsd_libpci, pm_obsd_device, pm_dump, pm_linux_sysfs, pm_darwin, pm_sylixos_device, pm_hurd, pm_mmio_conf1, pm_mmio_conf1_ext, pm_ecam, pm_win32_cfgmgr32, pm_win32_kldbg, pm_win32_sysdbg, pm_aos_expansion; #endif pciutils-3.13.0/lib/names.h0000644000175000001440000000313014443575015014006 0ustar mjusers/* * The PCI Library -- ID to Name Translation * * Copyright (c) 1997--2014 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+ * * SPDX-License-Identifier: GPL-2.0-or-later */ #define MAX_LINE 1024 /* names-hash.c */ struct id_entry { struct id_entry *next; u32 id12, id34; byte cat; byte src; char name[1]; }; enum id_entry_type { ID_UNKNOWN, ID_VENDOR, ID_DEVICE, ID_SUBSYSTEM, ID_GEN_SUBSYSTEM, ID_CLASS, ID_SUBCLASS, ID_PROGIF }; enum id_entry_src { SRC_UNKNOWN, SRC_CACHE, SRC_NET, SRC_HWDB, SRC_LOCAL, }; #define BUCKET_SIZE 8192 #define HASH_SIZE 4099 static inline u32 id_pair(unsigned int x, unsigned int y) { return ((x << 16) | y); } static inline unsigned int pair_first(unsigned int x) { return (x >> 16) & 0xffff; } static inline unsigned int pair_second(unsigned int x) { return x & 0xffff; } int pci_id_insert(struct pci_access *a, int cat, int id1, int id2, int id3, int id4, char *text, enum id_entry_src src); char *pci_id_lookup(struct pci_access *a, int flags, int cat, int id1, int id2, int id3, int id4); /* names-cache.c */ int pci_id_cache_load(struct pci_access *a, int flags); void pci_id_cache_dirty(struct pci_access *a); void pci_id_cache_flush(struct pci_access *a); void pci_id_hash_free(struct pci_access *a); /* names-dns.c */ char *pci_id_net_lookup(struct pci_access *a, int cat, int id1, int id2, int id3, int id4); /* names-hwdb.c */ char *pci_id_hwdb_lookup(struct pci_access *a, int cat, int id1, int id2, int id3, int id4); void pci_id_hwdb_free(struct pci_access *a); pciutils-3.13.0/lib/physmem-posix.c0000600000175000001440000000444714564417723015531 0ustar mjusers/* * The PCI Library -- Physical memory mapping for POSIX systems * * Copyright (c) 2023 Pali Rohár * * Can be freely distributed and used under the terms of the GNU GPL v2+ * * SPDX-License-Identifier: GPL-2.0-or-later */ /* * Tell 32-bit platforms that we are interested in 64-bit variant of off_t type * as 32-bit variant of off_t type is signed and so it cannot represent all * possible 32-bit offsets. It is required because off_t type is used by mmap(). */ #define _FILE_OFFSET_BITS 64 #include "internal.h" #include "physmem.h" #include #include #include #include #include #include #ifndef OFF_MAX #define OFF_MAX ((((off_t)1 << (sizeof(off_t) * CHAR_BIT - 2)) - 1) * 2 + 1) #endif struct physmem { int fd; }; void physmem_init_config(struct pci_access *a) { pci_define_param(a, "devmem.path", PCI_PATH_DEVMEM_DEVICE, "Path to the /dev/mem device"); } int physmem_access(struct pci_access *a, int w) { const char *devmem = pci_get_param(a, "devmem.path"); a->debug("checking access permission of physical memory device %s for %s mode...", devmem, w ? "read/write" : "read-only"); return access(devmem, R_OK | (w ? W_OK : 0)); } struct physmem * physmem_open(struct pci_access *a, int w) { const char *devmem = pci_get_param(a, "devmem.path"); struct physmem *physmem = pci_malloc(a, sizeof(struct physmem)); a->debug("trying to open physical memory device %s in %s mode...", devmem, w ? "read/write" : "read-only"); physmem->fd = open(devmem, (w ? O_RDWR : O_RDONLY) | O_DSYNC); /* O_DSYNC bypass CPU cache for mmap() on Linux */ if (physmem->fd < 0) { pci_mfree(physmem); return NULL; } return physmem; } void physmem_close(struct physmem *physmem) { close(physmem->fd); pci_mfree(physmem); } long physmem_get_pagesize(struct physmem *physmem UNUSED) { return sysconf(_SC_PAGESIZE); } void * physmem_map(struct physmem *physmem, u64 addr, size_t length, int w) { if (addr > OFF_MAX) { errno = EOVERFLOW; return (void *)-1; } return mmap(NULL, length, PROT_READ | (w ? PROT_WRITE : 0), MAP_SHARED, physmem->fd, addr); } int physmem_unmap(struct physmem *physmem UNUSED, void *ptr, size_t length) { return munmap(ptr, length); } pciutils-3.13.0/lib/ver2def.pl0000755000175000001440000000327514327513663014443 0ustar mjusers#!/usr/bin/perl use strict; use warnings; die "Usage: $0 script.ver dllname build.def import.def\n" if @ARGV != 4; my ($verfile, $dllname, $builddef, $importdef) = @ARGV; open my $verfh, '<', $verfile or die "Cannot open input file $verfile: $!\n"; my $input = join '', <$verfh>; close $verfh; my @syms; my (%cnt, %last, %ords); $input =~ s/\/\*.*?\*\///sg; # Remove C comments while ($input =~ m/(\S+)\s*\{((?:[^\{\}]|\{(?2)\})+)\}\s*;/sg) { # Split {...} my ($ver, $block) = ($1, $2); while ($block =~ s/(\S+)\s*:((?:[^\{\}:]|\{(?2)\})+)$//sg) { # Split section: my ($section, $syms) = ($1, $2); next if $section ne 'global'; $syms =~ s/\s+//g; foreach (split /;\s*/, $syms) { # Split symbols $cnt{$_}++; $last{$_} = $ver; push @syms, [$_, $ver]; } } } open my $importfh, '>', $importdef or die "Cannot open output file $importdef: $!\n"; open my $buildfh, '>', $builddef or die "Cannot open output file $builddef: $!\n"; print $importfh "LIBRARY \"$dllname\"\n"; print $importfh "EXPORTS\n"; print $buildfh "EXPORTS\n"; my $ord = 1; foreach (@syms) { my ($sym, $ver) = @{$_}; print $importfh "\"$sym\@$ver\" \@$ord\n"; if ($last{$sym} ne $ver) { print $buildfh "\"$sym\@$ver\" \@$ord\n"; } else { $ords{$sym} = $ord; print $buildfh "\"$sym\@$ver\" = " . (($cnt{$sym} > 1) ? "\"$sym\@\@$ver\"" : $sym) . " \@$ord\n" } $ord++; } # GNU dlltool has broken calculation of ordinals for aliased symbols, so specify ordinals explicitly # GNU LD prior 2.21 has broken handling of symbols with dot character # Operator == for defining symbol alias is supported since GNU dlltool 2.21 print $importfh "$_ \@$ords{$_} == \"$_\@$last{$_}\"\n" foreach sort keys %last; close $importfh; close $buildfh; pciutils-3.13.0/lib/obsd-device.c0000644000175000001440000000570514601633264015071 0ustar mjusers/* * The PCI Library -- OpenBSD /dev/pci access * * Adapted from fbsd-device.c by Matthieu Herrb , 2006 * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include #include #include #include #include "internal.h" static void obsd_config(struct pci_access *a) { pci_define_param(a, "obsd.path", PCI_PATH_OBSD_DEVICE, "Path to the OpenBSD PCI device"); } static int obsd_detect(struct pci_access *a) { char *name = pci_get_param(a, "obsd.path"); if (access(name, R_OK)) { a->warning("Cannot open %s", name); return 0; } a->debug("...using %s", name); return 1; } static void obsd_init(struct pci_access *a) { char *name = pci_get_param(a, "obsd.path"); a->fd = open(name, O_RDWR, 0); if (a->fd < 0) a->error("obsd_init: %s open failed", name); } static void obsd_cleanup(struct pci_access *a) { close(a->fd); } static int obsd_read(struct pci_dev *d, int pos, byte *buf, int len) { struct pci_io pi; union { u_int32_t u32; u_int16_t u16[2]; u_int8_t u8[4]; } u; if (!(len == 1 || len == 2 || len == 4)) return pci_generic_block_read(d, pos, buf, len); if (d->domain || pos >= 256) return 0; pi.pi_sel.pc_bus = d->bus; pi.pi_sel.pc_dev = d->dev; pi.pi_sel.pc_func = d->func; pi.pi_reg = pos - (pos % 4); pi.pi_width = 4; if (ioctl(d->access->fd, PCIOCREAD, &pi) < 0) { if (errno == ENXIO) pi.pi_data = 0xffffffff; else d->access->error("obsd_read: ioctl(PCIOCREAD) failed"); } u.u32 = pi.pi_data; switch (len) { case 1: buf[0] = (u8) u.u8[pos % 4]; break; case 2: ((u16 *) buf)[0] = letoh16(u.u16[(pos % 4) / 2]); break; case 4: ((u32 *) buf)[0] = (u32) letoh32(pi.pi_data); break; } return 1; } static int obsd_write(struct pci_dev *d, int pos, byte *buf, int len) { struct pci_io pi; if (!(len == 1 || len == 2 || len == 4)) return pci_generic_block_write(d, pos, buf, len); if (d->domain || pos >= 256) return 0; pi.pi_sel.pc_bus = d->bus; pi.pi_sel.pc_dev = d->dev; pi.pi_sel.pc_func = d->func; pi.pi_reg = pos; pi.pi_width = len; switch (len) { case 1: pi.pi_data = buf[0]; break; case 2: pi.pi_data = ((u16 *) buf)[0]; break; case 4: pi.pi_data = ((u32 *) buf)[0]; break; } if (ioctl(d->access->fd, PCIOCWRITE, &pi) < 0) d->access->error("obsd_write: ioctl(PCIOCWRITE) failed"); return 1; } struct pci_methods pm_obsd_device = { .name = "obsd-device", .help = "/dev/pci on OpenBSD", .config = obsd_config, .detect = obsd_detect, .init = obsd_init, .cleanup = obsd_cleanup, .scan = pci_generic_scan, .fill_info = pci_generic_fill_info, .read = obsd_read, .write = obsd_write, }; pciutils-3.13.0/lib/caps.c0000644000175000001440000000647014626120240013623 0ustar mjusers/* * The PCI Library -- Capabilities * * Copyright (c) 2008 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "internal.h" static void pci_add_cap(struct pci_dev *d, unsigned int addr, unsigned int id, unsigned int type) { struct pci_cap *cap = pci_malloc(d->access, sizeof(*cap)); if (d->last_cap) d->last_cap->next = cap; else d->first_cap = cap; d->last_cap = cap; cap->next = NULL; cap->addr = addr; cap->id = id; cap->type = type; d->access->debug("%04x:%02x:%02x.%d: Found capability %04x of type %d at %04x\n", d->domain, d->bus, d->dev, d->func, id, type, addr); } static void pci_scan_trad_caps(struct pci_dev *d) { word status = pci_read_word(d, PCI_STATUS); byte been_there[256]; int where; if (!(status & PCI_STATUS_CAP_LIST)) return; memset(been_there, 0, 256); where = pci_read_byte(d, PCI_CAPABILITY_LIST) & ~3; while (where) { byte id = pci_read_byte(d, where + PCI_CAP_LIST_ID); byte next = pci_read_byte(d, where + PCI_CAP_LIST_NEXT) & ~3; if (been_there[where]++) break; if (id == 0xff) break; pci_add_cap(d, where, id, PCI_CAP_NORMAL); where = next; } } static void pci_scan_ext_caps(struct pci_dev *d) { byte been_there[0x1000]; int where = 0x100; if (!pci_find_cap(d, PCI_CAP_ID_EXP, PCI_CAP_NORMAL)) return; memset(been_there, 0, 0x1000); do { u32 header; int id; header = pci_read_long(d, where); if (!header || header == 0xffffffff) break; id = header & 0xffff; if (been_there[where]++) break; pci_add_cap(d, where, id, PCI_CAP_EXTENDED); where = (header >> 20) & ~3; } while (where); } void pci_scan_caps(struct pci_dev *d, unsigned int want_fields) { if (want_fields & PCI_FILL_EXT_CAPS) want_fields |= PCI_FILL_CAPS; if (want_fill(d, want_fields, PCI_FILL_CAPS)) pci_scan_trad_caps(d); if (want_fill(d, want_fields, PCI_FILL_EXT_CAPS)) pci_scan_ext_caps(d); } void pci_free_caps(struct pci_dev *d) { struct pci_cap *cap; while (cap = d->first_cap) { d->first_cap = cap->next; pci_mfree(cap); } } struct pci_cap * pci_find_cap(struct pci_dev *d, unsigned int id, unsigned int type) { return pci_find_cap_nr(d, id, type, NULL); } /** * Finds a particular capability of a device * * To select one capability if there are more than one with the same id, you * can provide a pointer to an unsigned int that contains the index which you * want as cap_number. If you don't care and are fine with the first one you * can supply NULL. The cap_number will be replaced by the actual number * of capabilities with that id. */ struct pci_cap * pci_find_cap_nr(struct pci_dev *d, unsigned int id, unsigned int type, unsigned int *cap_number) { struct pci_cap *c; struct pci_cap *found = NULL; unsigned int target = (cap_number ? *cap_number : 0); unsigned int index = 0; pci_fill_info_v313(d, ((type == PCI_CAP_NORMAL) ? PCI_FILL_CAPS : PCI_FILL_EXT_CAPS)); for (c=d->first_cap; c; c=c->next) { if (c->type == type && c->id == id) { if (target == index) found = c; index++; } } if (cap_number) *cap_number = index; return found; } pciutils-3.13.0/lib/params.c0000644000175000001440000000376014564251265014174 0ustar mjusers/* * The PCI Library -- Parameters * * Copyright (c) 2008--2023 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include "internal.h" char * pci_get_param(struct pci_access *acc, char *param) { struct pci_param *p; for (p=acc->params; p; p=p->next) if (!strcmp(p->param, param)) return p->value; return NULL; } struct pci_param * pci_define_param(struct pci_access *acc, char *param, char *value, char *help) { struct pci_param *p, **pp; for (pp=&acc->params; p = *pp; pp=&p->next) { int cmp = strcmp(p->param, param); if (!cmp) { if (strcmp(p->value, value) || strcmp(p->help, help)) acc->error("Parameter %s re-defined differently", param); return p; } if (cmp > 0) break; } p = pci_malloc(acc, sizeof(*p)); p->next = *pp; *pp = p; p->param = param; p->value = value; p->value_malloced = 0; p->help = help; return p; } int pci_set_param_internal(struct pci_access *acc, char *param, char *value, int copy) { struct pci_param *p; for (p=acc->params; p; p=p->next) if (!strcmp(p->param, param)) { if (p->value_malloced) pci_mfree(p->value); p->value_malloced = copy; if (copy) p->value = pci_strdup(acc, value); else p->value = value; return 0; } return -1; } int pci_set_param(struct pci_access *acc, char *param, char *value) { return pci_set_param_internal(acc, param, value, 1); } void pci_free_params(struct pci_access *acc) { struct pci_param *p; while (p = acc->params) { acc->params = p->next; if (p->value_malloced) pci_mfree(p->value); pci_mfree(p); } } struct pci_param * pci_walk_params(struct pci_access *acc, struct pci_param *prev) { /* So far, the params form a simple linked list, but this can change in the future */ if (!prev) return acc->params; else return prev->next; } pciutils-3.13.0/lib/i386-io-windows.h0000600000175000001440000001751614603756376015507 0ustar mjusers/* * The PCI Library -- Access to i386 I/O ports on Windows * * Copyright (c) 2004 Alexander Stock * Copyright (c) 2006 Martin Mares * Copyright (c) 2021 Pali Rohár * * Can be freely distributed and used under the terms of the GNU GPL v2+ * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "win32-helpers.h" #include "i386-io-access.h" /* * Define __readeflags() for MSVC and GCC compilers. * MSVC since version 14.00 included in WDK 6001 and since version 15.00 * included in VS 2008 provides __readeflags() intrinsic for both 32 and 64-bit * modes. WDK 6001 defines macro __BUILDMACHINE__ to value WinDDK. VS 2008 does * not define this macro at all. MSVC throws error if name of user defined * function conflicts with some MSVC intrinsic. * MSVC supports inline assembly via __asm keyword in 32-bit mode only. * GCC version 4.9.0 and higher provides __builtin_ia32_readeflags_uXX() * builtin for XX-mode. This builtin is also available as __readeflags() * function indirectly via header file. * * CAVEAT: Semicolon in MSVC __asm block means start of the comment, and not * end of the __asm statement, like it is for all other C statements. Also * function which uses MSVC inline assembly cannot be inlined to another function * (compiler reports a warning about it, not a fatal error). So we add explicit * curly brackets for __asm blocks, remove misleading semicolons and do not * declare functions as inline. */ #if defined(_MSC_VER) && (_MSC_VER >= 1500 || (_MSC_VER >= 1400 && defined(__BUILDMACHINE__))) #pragma intrinsic(__readeflags) #elif defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 9) || (__GNUC__ > 4)) #include #elif defined(_MSC_VER) && defined(_M_IX86) static unsigned int __readeflags(void) { __asm { pushfd pop eax } } #elif defined(__GNUC__) static inline unsigned #ifdef __x86_64__ long long #endif int __readeflags(void) { unsigned #ifdef __x86_64__ long long #endif int eflags; asm volatile ("pushf\n\tpop %0\n" : "=r" (eflags)); return eflags; } #else #error "Unsupported compiler" #endif /* Read IOPL of the current process, IOPL is stored in eflag bits [13:12]. */ #define read_iopl() ((__readeflags() >> 12) & 0x3) /* * Unfortunately NtSetInformationProcess() function, ProcessUserModeIOPL * constant and all other helpers for its usage are not specified in any * standard WinAPI header file. So define all of required constants and types. * Function NtSetInformationProcess() is available in ntdll.dll library on all * Windows systems but marked as it can be removed in some future version. */ #ifndef NTSTATUS #define NTSTATUS LONG #endif #ifndef STATUS_NOT_IMPLEMENTED #define STATUS_NOT_IMPLEMENTED (NTSTATUS)0xC0000002 #endif #ifndef STATUS_PRIVILEGE_NOT_HELD #define STATUS_PRIVILEGE_NOT_HELD (NTSTATUS)0xC0000061 #endif #ifndef PROCESSINFOCLASS #define PROCESSINFOCLASS DWORD #endif #ifndef ProcessUserModeIOPL #define ProcessUserModeIOPL 16 #endif typedef NTSTATUS (NTAPI *NtSetInformationProcessProt)(HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength); typedef ULONG (NTAPI *RtlNtStatusToDosErrorProt)(NTSTATUS Status); /* * ProcessUserModeIOPL is syscall for NT kernel to change x86 IOPL * of the current running process to 3. * * Process handle argument for ProcessUserModeIOPL is ignored and * IOPL is always changed for the current running process. So pass * GetCurrentProcess() handle for documentation purpose. Process * information buffer and length are unused for ProcessUserModeIOPL. * * ProcessUserModeIOPL may success (return value >= 0) or may fail * because it is not implemented or because of missing privilege. * Other errors are not defined, so handle them as unknown. */ static BOOL SetProcessUserModeIOPLFunc(LPVOID Arg) { RtlNtStatusToDosErrorProt RtlNtStatusToDosErrorPtr = (RtlNtStatusToDosErrorProt)(((LPVOID *)Arg)[1]); NtSetInformationProcessProt NtSetInformationProcessPtr = (NtSetInformationProcessProt)(((LPVOID *)Arg)[0]); NTSTATUS nt_status = NtSetInformationProcessPtr(GetCurrentProcess(), ProcessUserModeIOPL, NULL, 0); if (nt_status >= 0) return TRUE; /* * If we have optional RtlNtStatusToDosError() function then use it for * translating NT status to Win32 error. If we do not have it then translate * two important status codes which we use later STATUS_NOT_IMPLEMENTED and * STATUS_PRIVILEGE_NOT_HELD. */ if (RtlNtStatusToDosErrorPtr) SetLastError(RtlNtStatusToDosErrorPtr(nt_status)); else if (nt_status == STATUS_NOT_IMPLEMENTED) SetLastError(ERROR_INVALID_FUNCTION); else if (nt_status == STATUS_PRIVILEGE_NOT_HELD) SetLastError(ERROR_PRIVILEGE_NOT_HELD); else SetLastError(ERROR_GEN_FAILURE); return FALSE; } /* * Set x86 I/O Privilege Level to 3 for the whole current NT process. Do it via * NtSetInformationProcess() call with ProcessUserModeIOPL information class, * which is supported by 32-bit Windows NT kernel versions and requires Tcb * privilege. */ static BOOL SetProcessUserModeIOPL(VOID) { LPVOID Arg[2]; UINT prev_error_mode; HMODULE ntdll; BOOL ret; /* * Load ntdll.dll library with disabled critical-error-handler and * file-not-found message box. * It means that NT kernel does not show unwanted GUI message box to user * when LoadLibrary() function fails. */ prev_error_mode = win32_change_error_mode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); ntdll = LoadLibrary(TEXT("ntdll.dll")); win32_change_error_mode(prev_error_mode); if (!ntdll) { SetLastError(ERROR_INVALID_FUNCTION); return FALSE; } /* Retrieve pointer to NtSetInformationProcess() function. */ Arg[0] = (LPVOID)GetProcAddress(ntdll, "NtSetInformationProcess"); if (!Arg[0]) { FreeLibrary(ntdll); SetLastError(ERROR_INVALID_FUNCTION); return FALSE; } /* Retrieve pointer to optional RtlNtStatusToDosError() function, it may be NULL. */ Arg[1] = (LPVOID)GetProcAddress(ntdll, "RtlNtStatusToDosError"); /* Call ProcessUserModeIOPL with Tcb privilege. */ ret = win32_call_func_with_tcb_privilege(SetProcessUserModeIOPLFunc, (LPVOID)&Arg); FreeLibrary(ntdll); if (!ret) return FALSE; /* * Some Windows NT kernel versions (e.g. Windows 2003 x64) do not * implement ProcessUserModeIOPL syscall at all but incorrectly * returns success when it is called by user process. So always * after this call verify that IOPL is set to 3. */ if (read_iopl() != 3) { SetLastError(ERROR_INVALID_FUNCTION); return FALSE; } return TRUE; } static int intel_setup_io(struct pci_access *a) { #ifndef _WIN64 /* 16/32-bit non-NT systems allow applications to access PCI I/O ports without any special setup. */ if (win32_is_non_nt_system()) { a->debug("Detected 16/32-bit non-NT system, skipping NT setup..."); return 1; } #endif /* Check if we have I/O permission */ if (read_iopl() == 3) { a->debug("IOPL is already set to 3, skipping NT setup..."); return 1; } /* On NT-based systems issue ProcessUserModeIOPL syscall which changes IOPL to 3. */ if (!SetProcessUserModeIOPL()) { DWORD error = GetLastError(); a->debug("NT ProcessUserModeIOPL call failed: %s.", error == ERROR_INVALID_FUNCTION ? "Call is not supported" : win32_strerror(error)); return 0; } a->debug("NT ProcessUserModeIOPL call succeeded..."); return 1; } static inline void intel_cleanup_io(struct pci_access *a UNUSED) { /* * 16/32-bit non-NT systems do not use any special setup and on NT-based * systems ProcessUserModeIOPL permanently changes IOPL to 3 for the current * NT process, no revert for current process is possible. */ } static inline void intel_io_lock(void) { } static inline void intel_io_unlock(void) { } pciutils-3.13.0/lib/i386-io-beos.h0000644000175000001440000000242714564230650014734 0ustar mjusers/* * The PCI Library -- Access to i386 I/O ports on BeOS * * Copyright (c) 2009 Francois Revol * * Can be freely distributed and used under the terms of the GNU GPL v2+ * * SPDX-License-Identifier: GPL-2.0-or-later */ /* those are private syscalls */ extern int read_isa_io(int pci_bus, void *addr, int size); extern int write_isa_io(int pci_bus, void *addr, int size, u32 value); static int intel_setup_io(struct pci_access *a UNUSED) { return 1; } static inline void intel_cleanup_io(struct pci_access *a UNUSED) { } static inline u8 intel_inb (u16 port) { return (u8)read_isa_io(0, (void *)(u32)port, sizeof(u8)); } static inline u16 intel_inw (u16 port) { return (u16)read_isa_io(0, (void *)(u32)port, sizeof(u16)); } static inline u32 intel_inl (u16 port) { return (u32)read_isa_io(0, (void *)(u32)port, sizeof(u32)); } static inline void intel_outb (u8 value, u16 port) { write_isa_io(0, (void *)(u32)port, sizeof(value), value); } static inline void intel_outw (u16 value, u16 port) { write_isa_io(0, (void *)(u32)port, sizeof(value), value); } static inline void intel_outl (u32 value, u16 port) { write_isa_io(0, (void *)(u32)port, sizeof(value), value); } static inline void intel_io_lock(void) { } static inline void intel_io_unlock(void) { } pciutils-3.13.0/lib/sysdep.h0000644000175000001440000000510214574035410014206 0ustar mjusers/* * The PCI Library -- System-Dependent Stuff * * Copyright (c) 1997--2020 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+ * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifdef __GNUC__ #define UNUSED __attribute__((unused)) #define NONRET __attribute__((noreturn)) #define FORMAT_CHECK(x,y,z) __attribute__((format(x,y,z))) #else #define UNUSED #define NONRET #define FORMAT_CHECK(x,y,z) #define inline #endif typedef u8 byte; typedef u16 word; #ifdef PCI_OS_WINDOWS #define strcasecmp _strcmpi #define strncasecmp _strnicmp #if defined(_MSC_VER) && _MSC_VER < 1800 #if _MSC_VER < 1300 #define strtoull strtoul #else #define strtoull _strtoui64 #endif #endif #if defined(_MSC_VER) && _MSC_VER < 1900 #define snprintf _snprintf #define vsnprintf _vsnprintf #endif #endif #ifdef PCI_HAVE_LINUX_BYTEORDER_H #include #define cpu_to_le16 __cpu_to_le16 #define cpu_to_le32 __cpu_to_le32 #define le16_to_cpu __le16_to_cpu #define le32_to_cpu __le32_to_cpu #else #ifdef PCI_OS_LINUX #include #define BYTE_ORDER __BYTE_ORDER #define BIG_ENDIAN __BIG_ENDIAN #endif #ifdef PCI_OS_SUNOS #include #if defined(__i386) && defined(LITTLE_ENDIAN) # define BYTE_ORDER LITTLE_ENDIAN #elif defined(__sparc) && defined(BIG_ENDIAN) # define BYTE_ORDER BIG_ENDIAN #else #define BIG_ENDIAN 4321 #endif #ifndef BYTE_ORDER #ifdef _LITTLE_ENDIAN #define BYTE_ORDER 1234 #else #define BYTE_ORDER 4321 #endif #endif /* BYTE_ORDER */ #endif /* PCI_OS_SUNOS */ #ifdef PCI_OS_WINDOWS #ifdef __MINGW32__ #include #else #include #define BIG_ENDIAN 4321 #define LITTLE_ENDIAN 1234 #define BYTE_ORDER LITTLE_ENDIAN #endif #endif #ifdef PCI_OS_HAIKU #include #endif #ifdef PCI_OS_SYLIXOS #include #endif #ifdef PCI_OS_DJGPP #define BIG_ENDIAN 4321 #define LITTLE_ENDIAN 1234 #define BYTE_ORDER LITTLE_ENDIAN #endif #ifdef PCI_OS_AMIGAOS #include #endif #if !defined(BYTE_ORDER) #error "BYTE_ORDER not defined for your platform" #endif #if BYTE_ORDER == BIG_ENDIAN #define cpu_to_le16 swab16 #define cpu_to_le32 swab32 #define le16_to_cpu swab16 #define le32_to_cpu swab32 static inline word swab16(word w) { return (w << 8) | ((w >> 8) & 0xff); } static inline u32 swab32(u32 w) { return ((w & 0xff000000) >> 24) | ((w & 0x00ff0000) >> 8) | ((w & 0x0000ff00) << 8) | ((w & 0x000000ff) << 24); } #else #define cpu_to_le16(x) (x) #define cpu_to_le32(x) (x) #define le16_to_cpu(x) (x) #define le32_to_cpu(x) (x) #endif #endif pciutils-3.13.0/lib/names-hwdb.c0000600000175000001440000000557714603756377014746 0ustar mjusers/* * The PCI Library -- Looking up Names via UDEV and HWDB * * Copyright (c) 2013--2014 Tom Gundersen * Copyright (c) 2014 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "internal.h" #include "names.h" #ifdef PCI_HAVE_HWDB #include #include #include char * pci_id_hwdb_lookup(struct pci_access *a, int cat, int id1, int id2, int id3, int id4 UNUSED) { char modalias[64]; const char *key = NULL; const char *disabled = pci_get_param(a, "hwdb.disable"); if (disabled && atoi(disabled)) return NULL; switch (cat) { case ID_VENDOR: sprintf(modalias, "pci:v%08X*", id1); key = "ID_VENDOR_FROM_DATABASE"; break; case ID_DEVICE: sprintf(modalias, "pci:v%08Xd%08X*", id1, id2); key = "ID_MODEL_FROM_DATABASE"; break; case ID_SUBSYSTEM: /* * There is no udev hwdb key which returns subsystem. Also note that query * modalias "pci:v%08Xd%08Xsv%08Xsd%08X*" matches also hwdb device with * modalias "pci:v%08Xd%08Xsv*sd*" (which is the default modalias), so * there is no way to get information specific for the subsystem. */ return NULL; case ID_GEN_SUBSYSTEM: /* There is no udev hwdb key which returns generic subsystem. */ return NULL; case ID_CLASS: sprintf(modalias, "pci:v*d*sv*sd*bc%02X*", id1); key = "ID_PCI_CLASS_FROM_DATABASE"; break; case ID_SUBCLASS: sprintf(modalias, "pci:v*d*sv*sd*bc%02Xsc%02X*", id1, id2); key = "ID_PCI_SUBCLASS_FROM_DATABASE"; break; case ID_PROGIF: sprintf(modalias, "pci:v*d*sv*sd*bc%02Xsc%02Xi%02X*", id1, id2, id3); key = "ID_PCI_INTERFACE_FROM_DATABASE"; break; } if (key) { if (!a->id_udev_hwdb) { a->debug("Initializing UDEV HWDB\n"); a->id_udev = udev_new(); a->id_udev_hwdb = udev_hwdb_new(a->id_udev); } struct udev_list_entry *entry; udev_list_entry_foreach(entry, udev_hwdb_get_properties_list_entry(a->id_udev_hwdb, modalias, 0)) { const char *entry_name = udev_list_entry_get_name(entry); if (entry_name && !strcmp(entry_name, key)) { const char *entry_value = udev_list_entry_get_value(entry); if (entry_value) return pci_strdup(a, entry_value); } } } return NULL; } void pci_id_hwdb_free(struct pci_access *a) { if (a->id_udev_hwdb) { udev_hwdb_unref(a->id_udev_hwdb); a->id_udev_hwdb = NULL; } if (a->id_udev) { udev_unref(a->id_udev); a->id_udev = NULL; } } #else char * pci_id_hwdb_lookup(struct pci_access *a UNUSED, int cat UNUSED, int id1 UNUSED, int id2 UNUSED, int id3 UNUSED, int id4 UNUSED) { return NULL; } void pci_id_hwdb_free(struct pci_access *a UNUSED) { } #endif pciutils-3.13.0/lib/win32-cfgmgr32.c0000600000175000001440000016255114601633322015245 0ustar mjusers/* * The PCI Library -- List PCI devices on Win32 via Configuration Manager * * Copyright (c) 2021 Pali Rohár * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include /* for isxdigit() */ #include /* for sprintf() */ #include /* for strlen(), strchr(), strncmp() */ #include /* for wcslen(), wcscpy() */ #include "internal.h" #include "win32-helpers.h" /* Unfortunately MinGW32 toolchain does not provide these cfgmgr32 constants. */ #ifndef RegDisposition_OpenExisting #define RegDisposition_OpenExisting 0x00000001 #endif #ifndef CM_REGISTRY_SOFTWARE #define CM_REGISTRY_SOFTWARE 0x00000001 #endif #ifndef CM_DRP_HARDWAREID #define CM_DRP_HARDWAREID 0x00000002 #endif #ifndef CM_DRP_SERVICE #define CM_DRP_SERVICE 0x00000005 #endif #ifndef CM_DRP_BUSNUMBER #define CM_DRP_BUSNUMBER 0x00000016 #endif #ifndef CM_DRP_ADDRESS #define CM_DRP_ADDRESS 0x0000001D #endif #ifndef CR_INVALID_CONFLICT_LIST #define CR_INVALID_CONFLICT_LIST 0x00000039 #endif #ifndef CR_INVALID_INDEX #define CR_INVALID_INDEX 0x0000003A #endif #ifndef CR_INVALID_STRUCTURE_SIZE #define CR_INVALID_STRUCTURE_SIZE 0x0000003B #endif #ifndef fIOD_10_BIT_DECODE #define fIOD_10_BIT_DECODE 0x0004 #endif #ifndef fIOD_12_BIT_DECODE #define fIOD_12_BIT_DECODE 0x0008 #endif #ifndef fIOD_16_BIT_DECODE #define fIOD_16_BIT_DECODE 0x0010 #endif #ifndef fIOD_POSITIVE_DECODE #define fIOD_POSITIVE_DECODE 0x0020 #endif #ifndef fIOD_PASSIVE_DECODE #define fIOD_PASSIVE_DECODE 0x0040 #endif #ifndef fIOD_WINDOW_DECODE #define fIOD_WINDOW_DECODE 0x0080 #endif #ifndef fIOD_PORT_BAR #define fIOD_PORT_BAR 0x0100 #endif #ifndef fMD_WINDOW_DECODE #define fMD_WINDOW_DECODE 0x0040 #endif #ifndef fMD_MEMORY_BAR #define fMD_MEMORY_BAR 0x0080 #endif #ifndef mMD_Prefetchable #define mMD_Prefetchable fMD_Prefetchable #endif /* * Unfortunately MinGW32 toolchain does not provide import library for these * cfgmgr32.dll functions. So resolve pointers to these functions at runtime. * MinGW-w64 toolchain provides them also in 32-bit mode. */ #if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) #ifdef CM_Get_DevNode_Registry_PropertyA #undef CM_Get_DevNode_Registry_PropertyA #endif static CONFIGRET (WINAPI *MyCM_Get_DevNode_Registry_PropertyA)(DEVINST dnDevInst, ULONG ulProperty, PULONG pulRegDataType, PVOID Buffer, PULONG pulLength, ULONG ulFlags); #define CM_Get_DevNode_Registry_PropertyA MyCM_Get_DevNode_Registry_PropertyA #ifdef CM_Get_DevNode_Registry_PropertyW #undef CM_Get_DevNode_Registry_PropertyW #endif static CONFIGRET (WINAPI *MyCM_Get_DevNode_Registry_PropertyW)(DEVINST dnDevInst, ULONG ulProperty, PULONG pulRegDataType, PVOID Buffer, PULONG pulLength, ULONG ulFlags); #define CM_Get_DevNode_Registry_PropertyW MyCM_Get_DevNode_Registry_PropertyW #ifndef CM_Open_DevNode_Key #undef CM_Open_DevNode_Key #endif static CONFIGRET (WINAPI *MyCM_Open_DevNode_Key)(DEVINST dnDevNode, REGSAM samDesired, ULONG ulHardwareProfile, REGDISPOSITION Disposition, PHKEY phkDevice, ULONG ulFlags); #define CM_Open_DevNode_Key MyCM_Open_DevNode_Key static BOOL resolve_cfgmgr32_functions(void) { HMODULE cfgmgr32; if (CM_Get_DevNode_Registry_PropertyA && CM_Get_DevNode_Registry_PropertyW && CM_Open_DevNode_Key) return TRUE; cfgmgr32 = GetModuleHandleA("cfgmgr32.dll"); if (!cfgmgr32) return FALSE; CM_Get_DevNode_Registry_PropertyA = (void *)GetProcAddress(cfgmgr32, "CM_Get_DevNode_Registry_PropertyA"); CM_Get_DevNode_Registry_PropertyW = (void *)GetProcAddress(cfgmgr32, "CM_Get_DevNode_Registry_PropertyW"); CM_Open_DevNode_Key = (void *)GetProcAddress(cfgmgr32, "CM_Open_DevNode_Key"); if (!CM_Get_DevNode_Registry_PropertyA || !CM_Get_DevNode_Registry_PropertyW || !CM_Open_DevNode_Key) return FALSE; return TRUE; } #endif /* * cfgmgr32.dll uses custom non-Win32 error numbers which are unsupported by * Win32 APIs like GetLastError() and FormatMessage() functions. * * Windows 7 introduced new cfgmgr32.dll function CM_MapCrToWin32Err() for * translating mapping CR_* errors to Win32 errors but most error codes are * not mapped. So this function is unusable. * * Error strings for CR_* errors are defined in cmapi.rc file which is * statically linked into some system libraries (e.g. filemgmt.dll, * acledit.dll, netui0.dll or netui2.dll) but due to static linking it is * not possible to access these error strings easily at runtime. * * So define own function for translating CR_* errors directly to strings. */ static const char * cr_strerror(CONFIGRET cr_error_id) { static char unknown_error[sizeof("Unknown CR error XXXXXXXXXX")]; static const char *cr_errors[] = { "The operation completed successfully", "CR_DEFAULT", "Not enough memory is available to process this command", "A required pointer parameter is invalid", "The ulFlags parameter specified is invalid for this operation", "The device instance handle parameter is not valid", "The supplied resource descriptor parameter is invalid", "The supplied logical configuration parameter is invalid", "CR_INVALID_ARBITRATOR", "CR_INVALID_NODELIST", "CR_DEVNODE_HAS_REQS/CR_DEVINST_HAS_REQS", "The RESOURCEID parameter does not contain a valid RESOURCEID", "CR_DLVXD_NOT_FOUND", "The specified device instance handle does not correspond to a present device", "There are no more logical configurations available", "There are no more resource descriptions available", "This device instance already exists", "The supplied range list parameter is invalid", "CR_INVALID_RANGE", "A general internal error occurred", "CR_NO_SUCH_LOGICAL_DEV", "The device is disabled for this configuration", "CR_NOT_SYSTEM_VM", "A service or application refused to allow removal of this device", "CR_APM_VETOED", "CR_INVALID_LOAD_TYPE", "An output parameter was too small to hold all the data available", "CR_NO_ARBITRATOR", "CR_NO_REGISTRY_HANDLE", "A required entry in the registry is missing or an attempt to write to the registry failed", "The specified Device ID is not a valid Device ID", "One or more parameters were invalid", "CR_INVALID_API", "CR_DEVLOADER_NOT_READY", "CR_NEED_RESTART", "There are no more hardware profiles available", "CR_DEVICE_NOT_THERE", "The specified value does not exist in the registry", "CR_WRONG_TYPE", "The specified priority is invalid for this operation", "This device cannot be disabled", "CR_FREE_RESOURCES", "CR_QUERY_VETOED", "CR_CANT_SHARE_IRQ", "CR_NO_DEPENDENT", "CR_SAME_RESOURCES", "The specified key does not exist in the registry", "The specified machine name does not meet the UNC naming conventions", "A general remote communication error occurred", "The machine selected for remote communication is not available at this time", "The Plug and Play service or another required service is not available", "Access denied", "This routine is not implemented in this version of the operating system", "The specified property type is invalid for this operation", "Device interface is active", "No such device interface", "Invalid reference string", "Invalid conflict list", "Invalid index", "Invalid structure size" }; if (cr_error_id <= 0 || cr_error_id >= sizeof(cr_errors)/sizeof(*cr_errors)) { sprintf(unknown_error, "Unknown CR error %lu", cr_error_id); return unknown_error; } return cr_errors[cr_error_id]; } static int fmt_validate(const char *s, int len, const char *fmt) { int i; for (i = 0; i < len; i++) if (!fmt[i] || (fmt[i] == '#' ? !isxdigit(s[i]) : fmt[i] != s[i])) return 0; return 1; } static int seq_xdigit_validate(const char *s, int mult, int min) { int i, len; len = strlen(s); if (len < min*mult || len % mult) return 0; for (i = 0; i < len; i++) if (!isxdigit(s[i])) return 0; return 1; } static LPWSTR get_device_service_name(struct pci_access *a, DEVINST devinst, DEVINSTID_A devinst_id, BOOL *supported) { ULONG reg_type, reg_size, reg_len; LPWSTR service_name; CONFIGRET cr; /* * All data are stored as 7-bit ASCII strings in system but service name is * exception. It can contain UNICODE. Moreover it is passed to other Win32 API * functions and therefore it cannot be converted to 8-bit ANSI string without * data loss. So use wide function CM_Get_DevNode_Registry_PropertyW() in this * case and deal with all wchar_t problems... */ reg_size = 0; cr = CM_Get_DevNode_Registry_PropertyW(devinst, CM_DRP_SERVICE, ®_type, NULL, ®_size, 0); if (cr == CR_CALL_NOT_IMPLEMENTED) { *supported = FALSE; return NULL; } else if (cr == CR_NO_SUCH_VALUE) { *supported = TRUE; return NULL; } else if (cr != CR_SUCCESS && cr != CR_BUFFER_SMALL) { a->warning("Cannot retrieve service name for PCI device %s: %s.", devinst_id, cr_strerror(cr)); *supported = TRUE; return NULL; } else if (reg_type != REG_SZ) { a->warning("Cannot retrieve service name for PCI device %s: Service name is stored as unknown type 0x%lx.", devinst_id, reg_type); *supported = TRUE; return NULL; } retry: /* * Returned size is on older Windows versions without nul-term char. * So explicitly increase size and fill nul-term byte. */ reg_size += sizeof(service_name[0]); service_name = pci_malloc(a, reg_size); reg_len = reg_size; cr = CM_Get_DevNode_Registry_PropertyW(devinst, CM_DRP_SERVICE, ®_type, service_name, ®_len, 0); service_name[reg_size/sizeof(service_name[0]) - 1] = 0; if (reg_len > reg_size) { pci_mfree(service_name); reg_size = reg_len; goto retry; } else if (cr != CR_SUCCESS) { a->warning("Cannot retrieve service name for PCI device %s: %s.", devinst_id, cr_strerror(cr)); pci_mfree(service_name); *supported = TRUE; return NULL; } else if (reg_type != REG_SZ) { a->warning("Cannot retrieve service name for PCI device %s: Service name is stored as unknown type 0x%lx.", devinst_id, reg_type); pci_mfree(service_name); *supported = TRUE; return NULL; } return service_name; } static char* get_driver_path_for_service(struct pci_access *a, LPCWSTR service_name, SC_HANDLE manager) { UINT (WINAPI *get_system_root_path)(LPWSTR buffer, UINT size) = NULL; DWORD service_config_size, service_config_len; LPQUERY_SERVICE_CONFIGW service_config = NULL; LPWSTR service_image_path = NULL; SERVICE_STATUS service_status; SC_HANDLE service = NULL; char *driver_path = NULL; int trim_system32 = 0; UINT systemroot_len; int driver_path_len; UINT system32_len; HMODULE kernel32; WCHAR *trim_ptr; DWORD error; service = OpenServiceW(manager, service_name, SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS); if (!service) { error = GetLastError(); if (error != ERROR_SERVICE_DOES_NOT_EXIST) a->warning("Cannot open service %ls with query rights: %s.", service_name, win32_strerror(error)); goto out; } if (!QueryServiceStatus(service, &service_status)) { error = GetLastError(); a->warning("Cannot query status of service %ls: %s.", service_name, win32_strerror(error)); goto out; } if (service_status.dwCurrentState == SERVICE_STOPPED) goto out; if (service_status.dwServiceType != SERVICE_KERNEL_DRIVER) goto out; if (!QueryServiceConfigW(service, NULL, 0, &service_config_size)) { error = GetLastError(); if (error != ERROR_INSUFFICIENT_BUFFER) { a->warning("Cannot query config of service %ls: %s.", service_name, win32_strerror(error)); goto out; } } retry_service_config: service_config = pci_malloc(a, service_config_size); if (!QueryServiceConfigW(service, service_config, service_config_size, &service_config_len)) { error = GetLastError(); if (error == ERROR_INSUFFICIENT_BUFFER) { pci_mfree(service_config); service_config_size = service_config_len; goto retry_service_config; } a->warning("Cannot query config of service %ls: %s.", service_name, win32_strerror(error)); goto out; } if (service_config->dwServiceType != SERVICE_KERNEL_DRIVER) goto out; /* * Despite QueryServiceConfig() is Win32 API, it returns lpBinaryPathName * (ImagePath registry) in NT format. Unfortunately there is no Win32 * function for converting NT paths to Win32 paths. So do it manually and * convert this NT format to human-readable Win32 path format. */ /* * GetSystemWindowsDirectoryW() returns path to NT SystemRoot namespace. * Alternativelly path to NT SystemRoot namespace can be constructed by * GetSystemDirectoryW() by trimming "\\system32" from the end of path. * GetSystemWindowsDirectoryW() is not provided in old Windows versions, * so use GetProcAddress() for compatibility with all Windows versions. */ kernel32 = GetModuleHandleW(L"kernel32.dll"); if (kernel32) get_system_root_path = (void *)GetProcAddress(kernel32, "GetSystemWindowsDirectoryW"); else { get_system_root_path = &GetSystemDirectoryW; trim_system32 = 1; } if (!service_config->lpBinaryPathName || !service_config->lpBinaryPathName[0]) { /* No ImagePath is specified, NT kernel assumes implicit kernel driver path by service name, which is relative to "\\system32\\drivers". */ /* GetSystemDirectoryW() returns path to "\\system32" directory on all Windows versions. */ system32_len = GetSystemDirectoryW(NULL, 0); /* Returns number of WCHARs plus 1 for nul-term. */ service_image_path = pci_malloc(a, sizeof(WCHAR) * (system32_len + sizeof("\\drivers\\")-1 + wcslen(service_name) + sizeof(".sys")-1)); system32_len = GetSystemDirectoryW(service_image_path, system32_len); /* Now it returns number of WCHARs without nul-term. */ if (system32_len && service_image_path[system32_len-1] != L'\\') service_image_path[system32_len++] = L'\\'; wcscpy(service_image_path + system32_len, L"drivers\\"); wcscpy(service_image_path + system32_len + sizeof("drivers\\")-1, service_name); wcscpy(service_image_path + system32_len + sizeof("drivers\\")-1 + wcslen(service_name), L".sys"); } else if (wcsncmp(service_config->lpBinaryPathName, L"\\SystemRoot\\", sizeof("\\SystemRoot\\")-1) == 0) { /* ImagePath is in NT SystemRoot namespace, convert to Win32 path via GetSystemWindowsDirectoryW()/GetSystemDirectoryW(). */ systemroot_len = get_system_root_path(NULL, 0); /* Returns number of WCHARs plus 1 for nul-term. */ service_image_path = pci_malloc(a, sizeof(WCHAR) * (systemroot_len + wcslen(service_config->lpBinaryPathName) - (sizeof("\\SystemRoot")-1))); systemroot_len = get_system_root_path(service_image_path, systemroot_len); /* Now it returns number of WCHARs without nul-term. */ if (trim_system32 && systemroot_len && (trim_ptr = wcsrchr(service_image_path, L'\\')) != NULL) systemroot_len = trim_ptr - service_image_path; if (systemroot_len && service_image_path[systemroot_len-1] != L'\\') service_image_path[systemroot_len++] = L'\\'; wcscpy(service_image_path + systemroot_len, service_config->lpBinaryPathName + sizeof("\\SystemRoot\\")-1); } else if (wcsncmp(service_config->lpBinaryPathName, L"\\??\\UNC\\", sizeof("\\??\\UNC\\")-1) == 0 || wcsncmp(service_config->lpBinaryPathName, L"\\??\\\\UNC\\", sizeof("\\??\\\\UNC\\")-1) == 0) { /* ImagePath is in NT UNC namespace, convert to Win32 UNC path via "\\\\" prefix. */ service_image_path = pci_malloc(a, sizeof(WCHAR) * (sizeof("\\\\") + wcslen(service_config->lpBinaryPathName) - (sizeof("\\??\\UNC\\")-1))); /* Namespace separator may be single or double backslash. */ driver_path_len = sizeof("\\??\\")-1; if (service_config->lpBinaryPathName[driver_path_len] == L'\\') driver_path_len++; driver_path_len += sizeof("UNC\\")-1; wcscpy(service_image_path, L"\\\\"); wcscpy(service_image_path + sizeof("\\\\")-1, service_config->lpBinaryPathName + driver_path_len); } else if (wcsncmp(service_config->lpBinaryPathName, L"\\??\\", sizeof("\\??\\")-1) == 0) { /* ImagePath is in NT Global?? namespace, root of the Win32 file namespace, so just remove "\\??\\" prefix to get Win32 path. */ service_image_path = pci_malloc(a, sizeof(WCHAR) * (wcslen(service_config->lpBinaryPathName) - (sizeof("\\??\\")-1))); /* Namespace separator may be single or double backslash. */ driver_path_len = sizeof("\\??\\")-1; if (service_config->lpBinaryPathName[driver_path_len] == L'\\') driver_path_len++; wcscpy(service_image_path, service_config->lpBinaryPathName + driver_path_len); } else if (service_config->lpBinaryPathName[0] != L'\\') { /* ImagePath is relative to the NT SystemRoot namespace, convert to Win32 path via GetSystemWindowsDirectoryW()/GetSystemDirectoryW(). */ systemroot_len = get_system_root_path(NULL, 0); /* Returns number of WCHARs plus 1 for nul-term. */ service_image_path = pci_malloc(a, sizeof(WCHAR) * (systemroot_len + sizeof("\\")-1 + wcslen(service_config->lpBinaryPathName))); systemroot_len = get_system_root_path(service_image_path, systemroot_len); /* Now it returns number of WCHARs without nul-term. */ if (trim_system32 && systemroot_len && (trim_ptr = wcsrchr(service_image_path, L'\\')) != NULL) systemroot_len = trim_ptr - service_image_path; if (systemroot_len && service_image_path[systemroot_len-1] != L'\\') service_image_path[systemroot_len++] = L'\\'; wcscpy(service_image_path + systemroot_len, service_config->lpBinaryPathName); } else { /* ImagePath is in some unhandled NT namespace, copy it as is. It cannot be used in Win32 API but libpci user can be still interested in it. */ service_image_path = pci_malloc(a, sizeof(WCHAR) * wcslen(service_config->lpBinaryPathName)); wcscpy(service_image_path, service_config->lpBinaryPathName); } /* Calculate len of buffer needed for conversion from LPWSTR to char*. */ driver_path_len = WideCharToMultiByte(CP_ACP, 0, service_image_path, -1, NULL, 0, NULL, NULL); if (driver_path_len <= 0) { error = GetLastError(); a->warning("Cannot convert kernel driver path from wide string to 8-bit string: %s.", win32_strerror(error)); goto out; } driver_path = pci_malloc(a, driver_path_len); driver_path_len = WideCharToMultiByte(CP_ACP, 0, service_image_path, -1, driver_path, driver_path_len, NULL, NULL); if (driver_path_len <= 0) { error = GetLastError(); a->warning("Cannot convert kernel driver path from wide string to 8-bit string: %s.", win32_strerror(error)); pci_mfree(driver_path); driver_path = NULL; goto out; } out: if (service_image_path) pci_mfree(service_image_path); if (service_config) pci_mfree(service_config); if (service) CloseServiceHandle(service); return driver_path; } static HKEY get_device_driver_devreg(struct pci_access *a, DEVINST devinst, DEVINSTID_A devinst_id) { CONFIGRET cr; HKEY key; cr = CM_Open_DevNode_Key(devinst, KEY_READ, 0, RegDisposition_OpenExisting, &key, CM_REGISTRY_SOFTWARE); if (cr != CR_SUCCESS) { if (cr != CR_NO_SUCH_VALUE) a->warning("Cannot retrieve driver key for device %s: %s.", devinst_id, cr_strerror(cr)); return NULL; } return key; } static char* read_reg_key_string_value(struct pci_access *a, HKEY key, const char *name, DWORD *unkn_reg_type) { DWORD reg_type, reg_size, reg_len; char *value; LONG error; reg_size = 0; error = RegQueryValueExA(key, name, NULL, ®_type, NULL, ®_size); if (error != ERROR_SUCCESS && error != ERROR_MORE_DATA) { SetLastError(error); return NULL; } else if (reg_type != REG_SZ) { SetLastError(0); *unkn_reg_type = reg_type; return NULL; } retry: value = pci_malloc(a, reg_size + 1); reg_len = reg_size; error = RegQueryValueExA(key, name, NULL, ®_type, (PBYTE)value, ®_len); if (error != ERROR_SUCCESS) { pci_mfree(value); if (error == ERROR_MORE_DATA) { reg_size = reg_len; goto retry; } SetLastError(error); return NULL; } else if (reg_type != REG_SZ) { pci_mfree(value); SetLastError(0); *unkn_reg_type = reg_type; return NULL; } value[reg_len] = '\0'; return value; } static int driver_cmp(const char *driver, const char *match) { int len = strlen(driver); if (driver[0] == '*') driver++; if (len >= 4 && strcasecmp(driver + len - 4, ".vxd") == 0) len -= 4; return strncasecmp(driver, match, len); } static char* get_driver_path_for_regkey(struct pci_access *a, DEVINSTID_A devinst_id, HKEY key) { char *driver_list, *driver, *driver_next; char *subdriver, *subname; char *driver_ptr; char *driver_path; DWORD unkn_reg_type; UINT systemdir_len; HKEY subkey; LONG error; BOOL vmm32; BOOL noext; int len; driver_list = read_reg_key_string_value(a, key, "DevLoader", &unkn_reg_type); if (!driver_list) { error = GetLastError(); if (error == 0) a->warning("Cannot read driver DevLoader key for PCI device %s: DevLoader key is stored as unknown type 0x%lx.", devinst_id, unkn_reg_type); else if (error != ERROR_FILE_NOT_FOUND) a->warning("Cannot read driver DevLoader key for PCI device %s: %s.", devinst_id, win32_strerror(error)); return NULL; } subdriver = NULL; driver = driver_list; while (*driver) { driver_next = strchr(driver, ','); if (driver_next) *(driver_next++) = '\0'; if (driver_cmp(driver, "ios") == 0 || driver_cmp(driver, "vcomm") == 0) subname = "PortDriver"; else if (driver_cmp(driver, "ntkern") == 0) subname = "NTMPDriver"; else if (driver_cmp(driver, "ndis") == 0) subname = "DeviceVxDs"; else if (driver_cmp(driver, "vdd") == 0) subname = "minivdd"; else subname = NULL; subkey = key; if (subname && strcmp(subname, "minivdd") == 0) { error = RegOpenKeyA(key, "Default", &subkey); if (error != ERROR_SUCCESS) { a->warning("Cannot open driver subkey Default for PCI device %s: %s.", devinst_id, win32_strerror(error)); subkey = NULL; } } if (!subname) break; if (subkey) { retry_subname: subdriver = read_reg_key_string_value(a, subkey, subname, &unkn_reg_type); if (!subdriver) { error = GetLastError(); if (error == 0) a->warning("Cannot read driver %s key for PCI device %s: %s key is stored as unknown type 0x%lx.", subname, devinst_id, subname, unkn_reg_type); else if (error != ERROR_FILE_NOT_FOUND) a->warning("Cannot read driver %s key for PCI device %s: %s.", subname, devinst_id, win32_strerror(error)); else if (strcmp(subname, "minivdd") == 0) { subname = "drv"; goto retry_subname; } else if (strcmp(subname, "drv") == 0) { subname = "vdd"; goto retry_subname; } } if (subkey != key) RegCloseKey(subkey); if (subdriver) { char *endptr = strchr(subdriver, ','); if (endptr) *endptr = '\0'; break; } } driver = driver_next; } if (subdriver && subdriver[0]) driver_ptr = subdriver; else if (driver[0]) driver_ptr = driver; else driver_ptr = NULL; if (driver_ptr && driver_ptr[0] == '*') { vmm32 = TRUE; driver_ptr++; } else vmm32 = FALSE; if (!driver_ptr[0]) driver_ptr = NULL; len = driver_ptr ? strlen(driver_ptr) : 0; noext = driver_ptr && (len < 4 || driver_ptr[len-4] != '.'); if (!driver_ptr) driver_path = NULL; else { if (tolower(driver_ptr[0]) >= 'a' && tolower(driver_ptr[0]) <= 'z' && driver_ptr[1] == ':') { /* Driver is already with absolute path. */ driver_path = pci_strdup(a, driver_ptr); } else if (driver_cmp(driver, "ntkern") == 0 && subdriver) { /* Driver is relative to system32\drivers\ directory which is relative to windows directory. */ systemdir_len = GetWindowsDirectoryA(NULL, 0); driver_path = pci_malloc(a, systemdir_len + 1 + sizeof("system32\\drivers\\")-1 + strlen(driver_ptr) + 4 + 1); systemdir_len = GetWindowsDirectoryA(driver_path, systemdir_len + 1); if (systemdir_len && driver_path[systemdir_len - 1] != '\\') driver_path[systemdir_len++] = '\\'; sprintf(driver_path + systemdir_len, "system32\\drivers\\%s%s", driver_ptr, noext ? ".sys" : ""); } else if (vmm32) { /* Driver is packed in vmm32.vxd which is stored in system directory. */ systemdir_len = GetSystemDirectoryA(NULL, 0); driver_path = pci_malloc(a, systemdir_len + 1 + sizeof("vmm32.vxd ()")-1 + strlen(driver_ptr) + 4 + 1); systemdir_len = GetSystemDirectoryA(driver_path, systemdir_len + 1); if (systemdir_len && driver_path[systemdir_len - 1] != '\\') driver_path[systemdir_len++] = '\\'; sprintf(driver_path + systemdir_len, "vmm32.vxd (%s%s)", driver_ptr, noext ? ".vxd" : ""); } else { /* Otherwise driver is relative to system directory. */ systemdir_len = GetSystemDirectoryA(NULL, 0); driver_path = pci_malloc(a, systemdir_len + 1 + strlen(driver_ptr) + 4 + 1); systemdir_len = GetSystemDirectoryA(driver_path, systemdir_len + 1); if (systemdir_len && driver_path[systemdir_len - 1] != '\\') driver_path[systemdir_len++] = '\\'; sprintf(driver_path + systemdir_len, "%s%s", driver_ptr, noext ? ".vxd" : ""); } } if (subdriver) pci_mfree(subdriver); pci_mfree(driver_list); return driver_path; } static char * get_device_driver_path(struct pci_dev *d, SC_HANDLE manager, BOOL manager_supported) { struct pci_access *a = d->access; BOOL service_supported = TRUE; DEVINSTID_A devinst_id = NULL; LPWSTR service_name = NULL; ULONG devinst_id_len = 0; char *driver_path = NULL; DEVINST devinst = (DEVINST)d->backend_data; ULONG problem = 0; ULONG status = 0; HKEY key = NULL; if (CM_Get_DevNode_Status(&status, &problem, devinst, 0) != CR_SUCCESS || !(status & DN_DRIVER_LOADED)) return NULL; if (CM_Get_Device_ID_Size(&devinst_id_len, devinst, 0) == CR_SUCCESS) { devinst_id = pci_malloc(a, devinst_id_len + 1); if (CM_Get_Device_IDA(devinst, devinst_id, devinst_id_len + 1, 0) != CR_SUCCESS) { pci_mfree(devinst_id); devinst_id = pci_strdup(a, "UNKNOWN"); } } else devinst_id = pci_strdup(a, "UNKNOWN"); service_name = get_device_service_name(d->access, devinst, devinst_id, &service_supported); if ((!service_name || !manager) && service_supported && manager_supported) goto out; else if (service_name && manager) { driver_path = get_driver_path_for_service(d->access, service_name, manager); goto out; } key = get_device_driver_devreg(d->access, devinst, devinst_id); if (key) { driver_path = get_driver_path_for_regkey(d->access, devinst_id, key); goto out; } out: if (key) RegCloseKey(key); if (service_name) pci_mfree(service_name); pci_mfree(devinst_id); return driver_path; } static void fill_drivers(struct pci_access *a) { BOOL manager_supported; SC_HANDLE manager; struct pci_dev *d; char *driver; DWORD error; /* ERROR_CALL_NOT_IMPLEMENTED is returned on systems without Service Manager support. */ manager_supported = TRUE; manager = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); if (!manager) { error = GetLastError(); if (error != ERROR_CALL_NOT_IMPLEMENTED) a->warning("Cannot open Service Manager with connect right: %s.", win32_strerror(error)); else manager_supported = FALSE; } for (d = a->devices; d; d = d->next) { driver = get_device_driver_path(d, manager, manager_supported); if (driver) { pci_set_property(d, PCI_FILL_DRIVER, driver); pci_mfree(driver); } d->known_fields |= PCI_FILL_DRIVER; } if (manager) CloseServiceHandle(manager); } static const char * res_id_to_str(RESOURCEID res_id) { static char hex_res_id[sizeof("0xffffffff")]; if (res_id == ResType_IO) return "IO"; else if (res_id == ResType_Mem) return "MEM"; else if (res_id == ResType_IRQ) return "IRQ"; sprintf(hex_res_id, "0x%lx", res_id); return hex_res_id; } static void fill_resources(struct pci_dev *d, DEVINST devinst, DEVINSTID_A devinst_id) { struct pci_access *a = d->access; CONFIGRET cr; LOG_CONF config; ULONG problem; ULONG status; RES_DES prev_res_des; RES_DES res_des; RESOURCEID res_id; DWORD bar_res_count; BOOL is_bar_res; BOOL non_nt_system; int last_irq = -1; int last_shared_irq = -1; cr = CM_Get_DevNode_Status(&status, &problem, devinst, 0); if (cr != CR_SUCCESS) { a->warning("Cannot retrieve status of PCI device %s: %s.", devinst_id, cr_strerror(cr)); return; } cr = CR_NO_MORE_LOG_CONF; /* * If the device is running then retrieve allocated configuration by PnP * manager which is currently in use by a device. */ if (!(status & DN_HAS_PROBLEM)) cr = CM_Get_First_Log_Conf(&config, devinst, ALLOC_LOG_CONF); /* * If the device is not running or it does not have allocated configuration by * PnP manager then retrieve forced configuration which prevents PnP manager * from assigning resources. */ if (cr == CR_NO_MORE_LOG_CONF) cr = CM_Get_First_Log_Conf(&config, devinst, FORCED_LOG_CONF); /* * If the device does not have neither allocated configuration by PnP manager * nor forced configuration and it is not disabled in the BIOS then retrieve * boot configuration supplied by the BIOS. */ if (cr == CR_NO_MORE_LOG_CONF && (!(status & DN_HAS_PROBLEM) || problem != CM_PROB_HARDWARE_DISABLED)) cr = CM_Get_First_Log_Conf(&config, devinst, BOOT_LOG_CONF); if (cr != CR_SUCCESS) { /* * Note: Starting with Windows 8, CM_Get_First_Log_Conf returns * CR_CALL_NOT_IMPLEMENTED when used in a Wow64 scenario. * To request information about the hardware resources on a local machine * it is necessary implement an architecture-native version of the * application using the hardware resource APIs. For example: An AMD64 * application for AMD64 systems. */ if (cr == CR_CALL_NOT_IMPLEMENTED && win32_is_32bit_on_win8_64bit_system()) { static BOOL warn_once = FALSE; if (!warn_once) { warn_once = TRUE; a->warning("Cannot retrieve resources of PCI devices from 32-bit application on 64-bit system."); } } else if (cr != CR_NO_MORE_LOG_CONF) a->warning("Cannot retrieve resources of PCI device %s: %s.", devinst_id, cr_strerror(cr)); return; } bar_res_count = 0; non_nt_system = win32_is_non_nt_system(); is_bar_res = TRUE; if (non_nt_system) { BOOL has_child; DEVINST child; ULONG child_name_len; PSTR child_name; BOOL is_bridge; if (CM_Get_Child(&child, devinst, 0) != CR_SUCCESS) has_child = FALSE; else if (CM_Get_Device_ID_Size(&child_name_len, child, 0) != CR_SUCCESS) has_child = FALSE; else { child_name_len++; child_name = pci_malloc(a, child_name_len); if (CM_Get_Device_IDA(child, child_name, child_name_len, 0) != CR_SUCCESS) has_child = FALSE; else if (strncmp(child_name, "PCI\\", 4) != 0) has_child = FALSE; else has_child = TRUE; pci_mfree(child_name); } if (has_child || d->device_class == PCI_CLASS_BRIDGE_PCI || d->device_class == PCI_CLASS_BRIDGE_CARDBUS) is_bridge = TRUE; else is_bridge = FALSE; if (is_bridge) is_bar_res = FALSE; } prev_res_des = (RES_DES)config; while ((cr = CM_Get_Next_Res_Des(&res_des, prev_res_des, ResType_All, &res_id, 0)) == CR_SUCCESS) { pciaddr_t start, end, size, flags; ULONG res_des_data_size; PBYTE res_des_data; if (prev_res_des != config) CM_Free_Res_Des_Handle(prev_res_des); prev_res_des = res_des; /* Skip other resources early */ if (res_id != ResType_IO && res_id != ResType_Mem && res_id != ResType_IRQ) continue; cr = CM_Get_Res_Des_Data_Size(&res_des_data_size, res_des, 0); if (cr != CR_SUCCESS) { a->warning("Cannot retrieve %s resource data of PCI device %s: %s.", res_id_to_str(res_id), devinst_id, cr_strerror(cr)); continue; } if (!res_des_data_size) { a->warning("Cannot retrieve %s resource data of PCI device %s: %s.", res_id_to_str(res_id), devinst_id, "Empty data"); continue; } res_des_data = pci_malloc(a, res_des_data_size); cr = CM_Get_Res_Des_Data(res_des, res_des_data, res_des_data_size, 0); if (cr != CR_SUCCESS) { a->warning("Cannot retrieve %s resource data of PCI device %s: %s.", res_id_to_str(res_id), devinst_id, cr_strerror(cr)); pci_mfree(res_des_data); continue; } /* * There can be more resources with the same id. In this case we are * interested in the last one in the list as at the beginning of the list * can be some virtual resources (which are not set in PCI config space). */ if (res_id == ResType_IO) { PIO_RESOURCE io_data = (PIO_RESOURCE)res_des_data; start = io_data->IO_Header.IOD_Alloc_Base; end = io_data->IO_Header.IOD_Alloc_End; size = (end > start) ? (end - start + 1) : 0; flags = PCI_IORESOURCE_IO; /* * If neither 10-bit, 12-bit, nor 16-bit support is presented then * expects that this is 32-bit I/O resource. If resource does not fit * into 16-bit space then it must be 32-bit. If PCI I/O resource is * not 32-bit then it is 16-bit. */ if (end <= 0xffff && (io_data->IO_Header.IOD_DesFlags & (fIOD_10_BIT_DECODE|fIOD_12_BIT_DECODE|fIOD_16_BIT_DECODE))) flags |= PCI_IORESOURCE_IO_16BIT_ADDR; /* * 16/32-bit non-NT systems do not support these two flags. * Most NT-based Windows versions support only the fIOD_WINDOW_DECODE * flag and put all BAR resources before window resources in this * resource list. So use this fIOD_WINDOW_DECODE flag as separator * between IO/MEM windows and IO/MEM BARs of PCI Bridges. */ if (io_data->IO_Header.IOD_DesFlags & fIOD_WINDOW_DECODE) is_bar_res = FALSE; else if (io_data->IO_Header.IOD_DesFlags & fIOD_PORT_BAR) is_bar_res = TRUE; if (is_bar_res && bar_res_count < 6) { d->flags[bar_res_count] = flags; d->base_addr[bar_res_count] = start; d->size[bar_res_count] = size; bar_res_count++; } else if (!is_bar_res) { d->bridge_flags[0] = flags; d->bridge_base_addr[0] = start; d->bridge_size[0] = size; d->known_fields |= PCI_FILL_BRIDGE_BASES; } } else if (res_id == ResType_Mem) { PMEM_RESOURCE mem_data = (PMEM_RESOURCE)res_des_data; start = mem_data->MEM_Header.MD_Alloc_Base; end = mem_data->MEM_Header.MD_Alloc_End; size = (end > start) ? (end - start + 1) : 0; flags = PCI_IORESOURCE_MEM; /* * If fMD_PrefetchAllowed flag is set then this is * PCI Prefetchable Memory resource. */ if ((mem_data->MEM_Header.MD_Flags & mMD_Prefetchable) == fMD_PrefetchAllowed) flags |= PCI_IORESOURCE_PREFETCH; /* If resource does not fit into 32-bit space then it must be 64-bit. */ if (is_bar_res && end > 0xffffffff) flags |= PCI_IORESOURCE_MEM_64; /* * These two flags (fMD_WINDOW_DECODE and fMD_MEMORY_BAR) are * unsupported on most Windows versions, so distinguish between * window and BAR based on previous resource type. */ if (mem_data->MEM_Header.MD_Flags & fMD_WINDOW_DECODE) is_bar_res = FALSE; else if (mem_data->MEM_Header.MD_Flags & fMD_MEMORY_BAR) is_bar_res = TRUE; /* 64-bit BAR resource must be at even position. */ if (is_bar_res && (flags & PCI_IORESOURCE_MEM_64) && bar_res_count % 2) bar_res_count++; if (is_bar_res && bar_res_count < 6) { d->flags[bar_res_count] = flags; d->base_addr[bar_res_count] = start; d->size[bar_res_count] = size; bar_res_count++; /* 64-bit BAR resource occupies two slots. */ if (flags & PCI_IORESOURCE_MEM_64) bar_res_count++; } else if (!is_bar_res && !(flags & PCI_IORESOURCE_PREFETCH)) { d->bridge_flags[1] = flags; d->bridge_base_addr[1] = start; d->bridge_size[1] = size; d->known_fields |= PCI_FILL_BRIDGE_BASES; } else if (!is_bar_res && (flags & PCI_IORESOURCE_PREFETCH)) { d->bridge_flags[2] = flags; d->bridge_base_addr[2] = start; d->bridge_size[2] = size; d->known_fields |= PCI_FILL_BRIDGE_BASES; } } else if (res_id == ResType_IRQ) { PIRQ_RESOURCE irq_data = (PIRQ_RESOURCE)res_des_data; /* * libpci's d->irq should be set to the non-MSI PCI IRQ and therefore * it should be level IRQ which may be shared with other PCI devices * and drivers in the system. As always we want to retrieve the last * IRQ number from the resource list. * * On 16/32-bit non-NT systems is fIRQD_Level set to 2 but on NT * systems to 0. Moreover it looks like that different PCI drivers * on both NT and non-NT systems set bits 0 and 1 to wrong values * and so reported value in this list may be incorrect. * * Therefore take the last level-shared IRQ number from the resource * list and if there is none of this type then take the last IRQ * number from the list. */ last_irq = irq_data->IRQ_Header.IRQD_Alloc_Num; if ((irq_data->IRQ_Header.IRQD_Flags & (mIRQD_Share|mIRQD_Edge_Level)) == (fIRQD_Share|fIRQD_Level)) last_shared_irq = irq_data->IRQ_Header.IRQD_Alloc_Num; /* * IRQ resource on 16/32-bit non-NT systems is separator between * IO/MEM windows and IO/MEM BARs of PCI Bridges. After the IRQ * resource are IO/MEM BAR resources. */ if (!is_bar_res && non_nt_system) is_bar_res = TRUE; } pci_mfree(res_des_data); } if (cr != CR_NO_MORE_RES_DES) a->warning("Cannot retrieve resources of PCI device %s: %s.", devinst_id, cr_strerror(cr)); if (prev_res_des != config) CM_Free_Res_Des_Handle(prev_res_des); CM_Free_Log_Conf_Handle(config); /* Set the last IRQ from the resource list to pci_dev. */ if (last_shared_irq >= 0) d->irq = last_shared_irq; else if (last_irq >= 0) d->irq = last_irq; if (last_shared_irq >= 0 || last_irq >= 0) d->known_fields |= PCI_FILL_IRQ; if (bar_res_count > 0) d->known_fields |= PCI_FILL_BASES | PCI_FILL_SIZES | PCI_FILL_IO_FLAGS; } static BOOL get_device_location(struct pci_access *a, DEVINST devinst, DEVINSTID_A devinst_id, unsigned int *domain, unsigned int *bus, unsigned int *dev, unsigned int *func) { ULONG reg_type, reg_len; CONFIGRET cr; BOOL have_bus, have_devfunc; DWORD drp_bus_num, drp_address; *domain = 0; have_bus = FALSE; have_devfunc = FALSE; /* * DRP_BUSNUMBER consists of PCI domain number in high 24 bits * and PCI bus number in low 8 bits. */ reg_len = sizeof(drp_bus_num); cr = CM_Get_DevNode_Registry_PropertyA(devinst, CM_DRP_BUSNUMBER, ®_type, &drp_bus_num, ®_len, 0); if (cr == CR_SUCCESS && reg_type == REG_DWORD && reg_len == sizeof(drp_bus_num)) { *domain = drp_bus_num >> 8; *bus = drp_bus_num & 0xff; have_bus = TRUE; } /* * DRP_ADDRESS consists of PCI device number in high 16 bits * and PCI function number in low 16 bits. */ reg_len = sizeof(drp_address); cr = CM_Get_DevNode_Registry_PropertyA(devinst, CM_DRP_ADDRESS, ®_type, &drp_address, ®_len, 0); if (cr == CR_SUCCESS && reg_type == REG_DWORD && reg_len == sizeof(drp_address)) { *dev = drp_address >> 16; *func = drp_address & 0xffff; have_devfunc = TRUE; } /* * Device Instance Id for PCI devices is of format: * "\\\\" * where: * "" is "PCI" * "" is "VEN_####&DEV_####&SUBSYS_########&REV_##" * and "" for PCI devices is at least in one of following format: * "BUS_##&DEV_##&FUNC_##" * "##.." (sequence of devfn hex bytes, where bytes represents tree path to the root) * "#..&#..&#..&#.." (four hex numbers separated by "&"; meaning is unknown yet) * * First two formats are used only on systems without support for multiple * domains. The second format uses intel-conf encoding of device and function * number: Low 3 bits is function number and high 5 bits is device number. * Bus number is not really encoded in second format! * * The third format is used on systems with support for multiple domains but * format is variable length and currently its meaning is unknown. Apparently * it looks like that DRP_BUSNUMBER and DRP_ADDRESS registry properties are * supported on these systems. * * If DRP_BUSNUMBER or DRP_ADDRESS failed then try to parse PCI bus, device * and function numbers from Instance Id part. */ if (!have_bus || !have_devfunc) { const char *device_id0 = strchr(devinst_id, '\\'); const char *instance_id0 = device_id0 ? strchr(device_id0 + 1, '\\') : NULL; const char *instance_id = instance_id0 ? instance_id0 + 1 : NULL; unsigned int devfn; if (instance_id) { if (fmt_validate(instance_id, strlen(instance_id), "BUS_##&DEV_##&FUNC_##") && sscanf(instance_id, "BUS_%x&DEV_%x&FUNC_%x", bus, dev, func) == 3) { have_bus = TRUE; have_devfunc = TRUE; } else if (seq_xdigit_validate(instance_id, 2, 2) && sscanf(instance_id, "%2x", &devfn) == 1) { *dev = devfn >> 3; *func = devfn & 0x7; have_devfunc = TRUE; } } } /* * Virtual IRQ holder devices do not have assigned any bus/dev/func number and * have "IRQHOLDER" in their Device Id part. So skip them. */ if (!have_bus && !have_devfunc && strncmp(devinst_id, "PCI\\IRQHOLDER\\", 14) == 0) return FALSE; /* * When some numbers cannot be retrieved via cfgmgr32 then set them to zeros * to have structure initialized. It makes sense to report via libpci also * such "incomplete" device as cfgmgr32 can provide additional information * like device/vendor ids or assigned resources. */ if (!have_bus && !have_devfunc) { *bus = *dev = *func = 0; a->warning("Cannot retrieve bus, device and function numbers for PCI device %s: %s.", devinst_id, cr_strerror(cr)); } else if (!have_bus) { *bus = 0; a->warning("Cannot retrieve bus number for PCI device %s: %s.", devinst_id, cr_strerror(cr)); } else if (!have_devfunc) { *dev = *func = 0; a->warning("Cannot retrieve device and function numbers for PCI device %s: %s.", devinst_id, cr_strerror(cr)); } return TRUE; } static void fill_data_from_string(struct pci_dev *d, const char *str) { BOOL have_device_id; BOOL have_vendor_id; BOOL have_prog_if; BOOL have_rev_id; const char *endptr, *endptr2; unsigned int hex; int len; have_device_id = have_vendor_id = (d->known_fields & PCI_FILL_IDENT); have_prog_if = have_rev_id = (d->known_fields & PCI_FILL_CLASS_EXT); while (1) { endptr = strchr(str, '&'); endptr2 = strchr(str, '\\'); if (endptr2 && (!endptr || endptr > endptr2)) endptr = endptr2; len = endptr ? endptr-str : (int)strlen(str); if (!have_vendor_id && fmt_validate(str, len, "VEN_####") && sscanf(str, "VEN_%x", &hex) == 1) { d->vendor_id = hex; have_vendor_id = TRUE; } else if (!have_device_id && fmt_validate(str, len, "DEV_####") && sscanf(str, "DEV_%x", &hex) == 1) { d->device_id = hex; have_device_id = TRUE; } else if (!(d->known_fields & PCI_FILL_SUBSYS) && fmt_validate(str, len, "SUBSYS_########") && sscanf(str, "SUBSYS_%x", &hex) == 1) { d->subsys_vendor_id = hex & 0xffff; d->subsys_id = hex >> 16; d->known_fields |= PCI_FILL_SUBSYS; } else if (!have_rev_id && fmt_validate(str, len, "REV_##") && sscanf(str, "REV_%x", &hex) == 1) { d->rev_id = hex; have_rev_id = TRUE; } else if (!((d->known_fields & PCI_FILL_CLASS) && have_prog_if) && (fmt_validate(str, len, "CC_####") || fmt_validate(str, len, "CC_######")) && sscanf(str, "CC_%x", &hex) == 1) { if (len == 9) { if (!have_prog_if) { d->prog_if = hex & 0xff; have_prog_if = TRUE; } hex >>= 8; } if (!(d->known_fields & PCI_FILL_CLASS)) { d->device_class = hex; d->known_fields |= PCI_FILL_CLASS; } } if (!endptr || endptr == endptr2) break; str = endptr + 1; } if ((have_device_id || d->device_id) && (have_vendor_id || d->vendor_id)) d->known_fields |= PCI_FILL_IDENT; if ((have_prog_if || d->prog_if) && (have_rev_id || d->rev_id)) d->known_fields |= PCI_FILL_CLASS_EXT; } static void fill_data_from_devinst_id(struct pci_dev *d, DEVINSTID_A devinst_id) { const char *device_id; device_id = strchr(devinst_id, '\\'); if (!device_id) return; device_id++; /* * Device Id part of Device Instance Id is in format: * "VEN_####&DEV_####&SUBSYS_########&REV_##" */ fill_data_from_string(d, device_id); } static void fill_data_from_hardware_ids(struct pci_dev *d, DEVINST devinst, DEVINSTID_A devinst_id) { ULONG reg_type, reg_size, reg_len; struct pci_access *a = d->access; char *hardware_ids = NULL; const char *str; CONFIGRET cr; reg_size = 0; cr = CM_Get_DevNode_Registry_PropertyA(devinst, CM_DRP_HARDWAREID, ®_type, NULL, ®_size, 0); if (cr != CR_SUCCESS && cr != CR_BUFFER_SMALL) { a->warning("Cannot retrieve hardware ids for PCI device %s: %s.", devinst_id, cr_strerror(cr)); return; } else if (reg_type != REG_MULTI_SZ && reg_type != REG_SZ) /* Older Windows versions return REG_SZ and new versions REG_MULTI_SZ. */ { a->warning("Cannot retrieve hardware ids for PCI device %s: Hardware ids are stored as unknown type 0x%lx.", devinst_id, reg_type); return; } retry: /* * Returned size is on older Windows versions without nul-term char. * So explicitly increase size and fill nul-term byte. */ reg_size++; hardware_ids = pci_malloc(a, reg_size); reg_len = reg_size; cr = CM_Get_DevNode_Registry_PropertyA(devinst, CM_DRP_HARDWAREID, ®_type, hardware_ids, ®_len, 0); hardware_ids[reg_size - 1] = 0; if (reg_len > reg_size) { pci_mfree(hardware_ids); reg_size = reg_len; goto retry; } else if (cr != CR_SUCCESS) { a->warning("Cannot retrieve hardware ids for PCI device %s: %s.", devinst_id, cr_strerror(cr)); pci_mfree(hardware_ids); return; } else if (reg_type != REG_MULTI_SZ && reg_type != REG_SZ) /* Older Windows versions return REG_SZ and new versions REG_MULTI_SZ. */ { a->warning("Cannot retrieve hardware ids for PCI device %s: Hardware ids are stored as unknown type 0x%lx.", devinst_id, reg_type); pci_mfree(hardware_ids); return; } /* * Hardware ids is nul-separated nul-term string list where each string has * one of the following format: * "PCI\\VEN_####&DEV_####&SUBSYS_########&REV_##" * "PCI\\VEN_####&DEV_####&SUBSYS_########" * "PCI\\VEN_####&DEV_####&REV_##&CC_####" * "PCI\\VEN_####&DEV_####&CC_######" * "PCI\\VEN_####&DEV_####&CC_####" * "PCI\\VEN_####&DEV_####&REV_##" * "PCI\\VEN_####&DEV_####" */ for (str = hardware_ids; *str != '\0'; str += strlen(str) + 1) { if (strncmp(str, "PCI\\", 4) != 0) continue; str += 4; fill_data_from_string(d, str); } pci_mfree(hardware_ids); } static void scan_devinst_id(struct pci_access *a, DEVINSTID_A devinst_id) { unsigned int domain, bus, dev, func; struct pci_dev *d; DEVINST devinst; CONFIGRET cr; cr = CM_Locate_DevNodeA(&devinst, devinst_id, CM_LOCATE_DEVNODE_NORMAL); if (cr != CR_SUCCESS) { /* Do not show warning when device is not present (= does not match NORMAL flag). */ if (cr != CR_NO_SUCH_DEVNODE) a->warning("Cannot retrieve handle for device %s: %s.", devinst_id, cr_strerror(cr)); return; } /* get_device_location() returns FALSE if devinst is not real PCI device. */ if (!get_device_location(a, devinst, devinst_id, &domain, &bus, &dev, &func)) return; d = pci_get_dev(a, domain, bus, dev, func); pci_link_dev(a, d); if (!d->access->backend_data) d->no_config_access = 1; d->backend_data = (void *)devinst; /* Parse device id part of devinst id and fill details into pci_dev. */ if (!a->buscentric) fill_data_from_devinst_id(d, devinst_id); /* Retrieve hardware ids of devinst, parse them and fill details into pci_dev. */ if (!a->buscentric) fill_data_from_hardware_ids(d, devinst, devinst_id); if (!a->buscentric) fill_resources(d, devinst, devinst_id); /* * Set parent field to cfgmgr32 parent devinst handle and backend_data field to current * devinst handle. At later stage in win32_cfgmgr32_scan() when all pci_dev * devices are linked, change every devinst handle by pci_dev. */ if (!a->buscentric) { DEVINST parent_devinst; if (CM_Get_Parent(&parent_devinst, devinst, 0) != CR_SUCCESS) { parent_devinst = 0; a->warning("Cannot retrieve parent handle for device %s: %s.", devinst_id, cr_strerror(cr)); } d->parent = (void *)parent_devinst; } } static void win32_cfgmgr32_scan(struct pci_access *a) { ULONG devinst_id_list_size; PCHAR devinst_id_list; DEVINSTID_A devinst_id; struct pci_dev *d; CONFIGRET cr; #if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) if (!resolve_cfgmgr32_functions()) { a->warning("Required cfgmgr32.dll functions are unavailable."); return; } #endif /* * Explicitly initialize size to zero as wine cfgmgr32 implementation does not * support this API but returns CR_SUCCESS without touching size argument. */ devinst_id_list_size = 0; cr = CM_Get_Device_ID_List_SizeA(&devinst_id_list_size, "PCI", CM_GETIDLIST_FILTER_ENUMERATOR); if (cr != CR_SUCCESS) { a->warning("Cannot retrieve list of PCI devices: %s.", cr_strerror(cr)); return; } else if (devinst_id_list_size <= 1) { a->warning("Cannot retrieve list of PCI devices: No device was found."); return; } devinst_id_list = pci_malloc(a, devinst_id_list_size); cr = CM_Get_Device_ID_ListA("PCI", devinst_id_list, devinst_id_list_size, CM_GETIDLIST_FILTER_ENUMERATOR); if (cr != CR_SUCCESS) { a->warning("Cannot retrieve list of PCI devices: %s.", cr_strerror(cr)); pci_mfree(devinst_id_list); return; } /* Register pci_dev for each cfgmgr32 devinst handle. */ for (devinst_id = devinst_id_list; *devinst_id; devinst_id += strlen(devinst_id) + 1) scan_devinst_id(a, devinst_id); /* Fill all drivers. */ if (!a->buscentric) fill_drivers(a); /* Switch parent fields from cfgmgr32 devinst handle to pci_dev. */ if (!a->buscentric) { struct pci_dev *d1, *d2; for (d1 = a->devices; d1; d1 = d1->next) { for (d2 = a->devices; d2; d2 = d2->next) if ((DEVINST)d1->parent == (DEVINST)d2->backend_data) break; d1->parent = d2; if (d1->parent) d1->known_fields |= PCI_FILL_PARENT; } } /* devinst stored in ->backend_data is not needed anymore, clear it. */ for (d = a->devices; d; d = d->next) d->backend_data = NULL; pci_mfree(devinst_id_list); } static void win32_cfgmgr32_config(struct pci_access *a) { pci_define_param(a, "win32.cfgmethod", "auto", "PCI config space access method"); } static int win32_cfgmgr32_detect(struct pci_access *a) { ULONG devinst_id_list_size; CONFIGRET cr; #if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) if (!resolve_cfgmgr32_functions()) { a->debug("Required cfgmgr32.dll functions are unavailable."); return 0; } #endif /* * Explicitly initialize size to zero as wine cfgmgr32 implementation does not * support this API but returns CR_SUCCESS without touching size argument. */ devinst_id_list_size = 0; cr = CM_Get_Device_ID_List_SizeA(&devinst_id_list_size, "PCI", CM_GETIDLIST_FILTER_ENUMERATOR); if (cr != CR_SUCCESS) { a->debug("CM_Get_Device_ID_List_SizeA(\"PCI\"): %s.", cr_strerror(cr)); return 0; } else if (devinst_id_list_size <= 1) { a->debug("CM_Get_Device_ID_List_SizeA(\"PCI\"): No device was found."); return 0; } return 1; } static void win32_cfgmgr32_fill_info(struct pci_dev *d, unsigned int flags) { /* * All available flags were filled by win32_cfgmgr32_scan(). * Filling more flags is possible only from config space. */ if (!d->access->backend_data) return; pci_generic_fill_info(d, flags); } static int win32_cfgmgr32_read(struct pci_dev *d, int pos, byte *buf, int len) { struct pci_access *a = d->access; struct pci_access *acfg = a->backend_data; struct pci_dev *dcfg = d->backend_data; if (!acfg) return pci_emulated_read(d, pos, buf, len); if (!dcfg) d->backend_data = dcfg = pci_get_dev(acfg, d->domain, d->bus, d->dev, d->func); return pci_read_block(dcfg, pos, buf, len); } static int win32_cfgmgr32_write(struct pci_dev *d, int pos, byte *buf, int len) { struct pci_access *a = d->access; struct pci_access *acfg = a->backend_data; struct pci_dev *dcfg = d->backend_data; if (!acfg) return 0; if (!dcfg) d->backend_data = dcfg = pci_get_dev(acfg, d->domain, d->bus, d->dev, d->func); return pci_write_block(dcfg, pos, buf, len); } static void win32_cfgmgr32_cleanup_dev(struct pci_dev *d) { struct pci_dev *dcfg = d->backend_data; if (dcfg) pci_free_dev(dcfg); } static void win32_cfgmgr32_init(struct pci_access *a) { char *cfgmethod = pci_get_param(a, "win32.cfgmethod"); struct pci_access *acfg; if (strcmp(cfgmethod, "") == 0 || strcmp(cfgmethod, "auto") == 0) { acfg = pci_clone_access(a); acfg->method = PCI_ACCESS_AUTO; } else if (strcmp(cfgmethod, "none") == 0 || strcmp(cfgmethod, "win32-cfgmgr32") == 0) { if (a->writeable) a->error("Write access requested but option win32.cfgmethod was not set."); return; } else { int m = pci_lookup_method(cfgmethod); if (m < 0) a->error("Option win32.cfgmethod is set to an unknown access method \"%s\".", cfgmethod); acfg = pci_clone_access(a); acfg->method = m; } a->debug("Loading config space access method...\n"); if (!pci_init_internal(acfg, PCI_ACCESS_WIN32_CFGMGR32)) { pci_cleanup(acfg); a->debug("Cannot find any working config space access method.\n"); if (a->writeable) a->error("Write access requested but no usable access method found."); return; } a->backend_data = acfg; } static void win32_cfgmgr32_cleanup(struct pci_access *a) { struct pci_access *acfg = a->backend_data; if (acfg) pci_cleanup(acfg); } struct pci_methods pm_win32_cfgmgr32 = { .name = "win32-cfgmgr32", .help = "Win32 device listing via Configuration Manager", .config = win32_cfgmgr32_config, .detect = win32_cfgmgr32_detect, .init = win32_cfgmgr32_init, .cleanup = win32_cfgmgr32_cleanup, .scan = win32_cfgmgr32_scan, .fill_info = win32_cfgmgr32_fill_info, .read = win32_cfgmgr32_read, .write = win32_cfgmgr32_write, .cleanup_dev = win32_cfgmgr32_cleanup_dev, }; pciutils-3.13.0/lib/names-hash.c0000600000175000001440000000612014443575171014717 0ustar mjusers/* * The PCI Library -- ID to Name Hash * * Copyright (c) 1997--2008 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "internal.h" #include "names.h" struct id_bucket { struct id_bucket *next; unsigned int full; }; #ifdef __GNUC__ #define BUCKET_ALIGNMENT __alignof__(struct id_bucket) #else union id_align { struct id_bucket *next; unsigned int full; }; #define BUCKET_ALIGNMENT sizeof(union id_align) #endif #define BUCKET_ALIGN(n) ((n)+BUCKET_ALIGNMENT-(n)%BUCKET_ALIGNMENT) static void *id_alloc(struct pci_access *a, unsigned int size) { struct id_bucket *buck = a->current_id_bucket; unsigned int pos; if (!a->id_hash) { a->id_hash = pci_malloc(a, sizeof(struct id_entry *) * HASH_SIZE); memset(a->id_hash, 0, sizeof(struct id_entry *) * HASH_SIZE); } if (!buck || buck->full + size > BUCKET_SIZE) { buck = pci_malloc(a, BUCKET_SIZE); buck->next = a->current_id_bucket; a->current_id_bucket = buck; buck->full = BUCKET_ALIGN(sizeof(struct id_bucket)); } pos = buck->full; buck->full = BUCKET_ALIGN(buck->full + size); return (byte *)buck + pos; } static inline unsigned int id_hash(int cat, u32 id12, u32 id34) { unsigned int h; h = id12 ^ (id34 << 3) ^ (cat << 5); return h % HASH_SIZE; } int pci_id_insert(struct pci_access *a, int cat, int id1, int id2, int id3, int id4, char *text, enum id_entry_src src) { u32 id12 = id_pair(id1, id2); u32 id34 = id_pair(id3, id4); unsigned int h = id_hash(cat, id12, id34); struct id_entry *n = a->id_hash ? a->id_hash[h] : NULL; int len = strlen(text); while (n && (n->id12 != id12 || n->id34 != id34 || n->cat != cat)) n = n->next; if (n) return 1; n = id_alloc(a, sizeof(struct id_entry) + len); n->id12 = id12; n->id34 = id34; n->cat = cat; n->src = src; memcpy(n->name, text, len+1); n->next = a->id_hash[h]; a->id_hash[h] = n; return 0; } char *pci_id_lookup(struct pci_access *a, int flags, int cat, int id1, int id2, int id3, int id4) { struct id_entry *n, *best; u32 id12 = id_pair(id1, id2); u32 id34 = id_pair(id3, id4); if (a->id_hash) { n = a->id_hash[id_hash(cat, id12, id34)]; best = NULL; for (; n; n=n->next) { if (n->id12 != id12 || n->id34 != id34 || n->cat != cat) continue; if (n->src == SRC_LOCAL && (flags & PCI_LOOKUP_SKIP_LOCAL)) continue; if (n->src == SRC_NET && !(flags & PCI_LOOKUP_NETWORK)) continue; if (n->src == SRC_CACHE && !(flags & PCI_LOOKUP_CACHE)) continue; if (n->src == SRC_HWDB && (flags & (PCI_LOOKUP_SKIP_LOCAL | PCI_LOOKUP_NO_HWDB))) continue; if (!best || best->src < n->src) best = n; } if (best) return best->name; } return NULL; } void pci_id_hash_free(struct pci_access *a) { pci_mfree(a->id_hash); a->id_hash = NULL; while (a->current_id_bucket) { struct id_bucket *buck = a->current_id_bucket; a->current_id_bucket = buck->next; pci_mfree(buck); } } pciutils-3.13.0/lib/aix-device.c0000644000175000001440000001272714601632435014724 0ustar mjusers/* * The PCI Library -- AIX /dev/pci[0-n] access * * Copyright (c) 1999 Jari Kirma * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ /* * Read functionality of this driver is briefly tested, and seems * to supply basic information correctly, but I promise no more. */ #include #include #include #include #include #include #include #include "internal.h" #define AIX_LSDEV_CMD "/usr/sbin/lsdev -C -c bus -t pci\\* -S available -F name" #define AIX_ODMGET_CMD \ "/usr/bin/odmget -q 'name=%s and attribute=bus_number' CuAt | \ /usr/bin/awk '$1 == \"value\" { print $3 }'" /* AIX PCI bus device information */ typedef struct aix_pci_bus { char *bus_name; int bus_number; int bus_fd; } aix_pci_bus; #define PCI_BUS_MAX 16 /* arbitrary choice */ static aix_pci_bus pci_buses[PCI_BUS_MAX]; static int pci_bus_count = 0; /* Utility Routines */ static aix_pci_bus * aix_find_bus(struct pci_access *a, int bus_number) { int i; for (i = 0; i < pci_bus_count; i++) { if (pci_buses[i].bus_number == bus_number) { return &pci_buses[i]; } } a->error("aix_find_bus: bus number %d not found", bus_number); } static int aix_bus_open(struct pci_dev *d) { struct pci_access *a = d->access; aix_pci_bus *bp = aix_find_bus(a, d->bus); if (bp->bus_fd < 0) { char devbuf[256]; int mode = a->writeable ? O_RDWR : O_RDONLY; snprintf(devbuf, sizeof (devbuf), "/dev/%s", bp->bus_name); bp->bus_fd = open(devbuf, mode, 0); if (bp->bus_fd < 0) a->error("aix_open_bus: %s open failed", devbuf); } return bp->bus_fd; } static int aix_bus_number(char *name) { int bus_number; FILE *odmget_pipe; char command[256]; char buf[256]; char *bp; char *ep; snprintf(command, sizeof (command), AIX_ODMGET_CMD, name); odmget_pipe = popen(command, "r"); if (odmget_pipe == NULL) { /* popen failed */ return -1; } if (fgets(buf, sizeof (buf) - 1, odmget_pipe) != NULL) { bp = buf + 1; /* skip leading double quote */ bus_number = strtol(bp, &ep, 0); if (bp == ep) { /* strtol failed */ bus_number = -1; } } else { /* first PCI bus_number is not recorded in ODM CuAt; default to 0 */ bus_number = 0; } (void) pclose(odmget_pipe); return bus_number; } /* Method entries */ static int aix_detect(struct pci_access *a) { int len; int mode = a->writeable ? W_OK : R_OK; char *command = AIX_LSDEV_CMD; FILE *lsdev_pipe; char buf[256]; char *name; lsdev_pipe = popen(command, "r"); if (lsdev_pipe == NULL) { a->error("aix_config: popen(\"%s\") failed", command); } while (fgets(buf, sizeof (buf) - 1, lsdev_pipe) != NULL) { len = strlen(buf); while (buf[len-1] == '\n' || buf[len-1] == '\r') len--; buf[len] = '\0'; /* clobber the newline */ name = (char *) pci_malloc(a, len + 1); strcpy(name, buf); pci_buses[pci_bus_count].bus_name = name; pci_buses[pci_bus_count].bus_number = 0; pci_buses[pci_bus_count].bus_fd = -1; if (!pci_bus_count) a->debug("...using %s", name); else a->debug(", %s", name); pci_bus_count++; if (pci_bus_count >= PCI_BUS_MAX) break; } (void) pclose(lsdev_pipe); return pci_bus_count; } static void aix_init(struct pci_access *a) { char *name; int i; for (i = 0; i < pci_bus_count; i++) { name = pci_buses[i].bus_name; pci_buses[i].bus_number = aix_bus_number(name); } } static void aix_cleanup(struct pci_access *a) { aix_pci_bus *bp; while (pci_bus_count-- > 0) { bp = &pci_buses[pci_bus_count]; (void) free(bp->bus_name); if (bp->bus_fd >= 0) { (void) close(bp->bus_fd); bp->bus_fd = -1; } } } void aix_scan(struct pci_access *a) { int i; int bus_number; byte busmap[256]; memset(busmap, 0, sizeof(busmap)); for (i = 0; i < pci_bus_count; i++) { bus_number = pci_buses[i].bus_number; if (!busmap[bus_number]) { pci_generic_scan_bus(a, busmap, 0, bus_number); } } } static int aix_read(struct pci_dev *d, int pos, byte *buf, int len) { struct mdio mdio; int fd; if (d->domain || pos + len > 256) return 0; fd = aix_bus_open(d); mdio.md_addr = (ulong) pos; mdio.md_size = len; mdio.md_incr = MV_BYTE; mdio.md_data = (char *) buf; mdio.md_sla = PCI_DEVFN(d->dev, d->func); if (ioctl(fd, MIOPCFGET, &mdio) < 0) d->access->error("aix_read: ioctl(MIOPCFGET) failed"); return 1; } static int aix_write(struct pci_dev *d, int pos, byte *buf, int len) { struct mdio mdio; int fd; if (d->domain || pos + len > 256) return 0; fd = aix_bus_open(d); mdio.md_addr = (ulong) pos; mdio.md_size = len; mdio.md_incr = MV_BYTE; mdio.md_data = (char *) buf; mdio.md_sla = PCI_DEVFN(d->dev, d->func); if (ioctl(fd, MIOPCFPUT, &mdio) < 0) { d->access->error("aix_write: ioctl(MIOPCFPUT) failed"); } return 1; } struct pci_methods pm_aix_device = { .name = "aix-device", .help = "AIX /dev/pci[0-n]", .detect = aix_detect, .init = aix_init, .cleanup = aix_cleanup, .scan = aix_scan, .fill_info = pci_generic_fill_info, .read = aix_read, .write = aix_write, }; pciutils-3.13.0/lib/win32-sysdbg.c0000600000175000001440000001757514603756376015154 0ustar mjusers/* * The PCI Library -- PCI config space access using NT SysDbg interface * * Copyright (c) 2022 Pali Rohár * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "internal.h" #include "win32-helpers.h" #ifndef NTSTATUS #define NTSTATUS LONG #endif #ifndef STATUS_UNSUCCESSFUL #define STATUS_UNSUCCESSFUL (NTSTATUS)0xC0000001 #endif #ifndef STATUS_NOT_IMPLEMENTED #define STATUS_NOT_IMPLEMENTED (NTSTATUS)0xC0000002 #endif #ifndef STATUS_INVALID_INFO_CLASS #define STATUS_INVALID_INFO_CLASS (NTSTATUS)0xC0000003 #endif #ifndef STATUS_ACCESS_DENIED #define STATUS_ACCESS_DENIED (NTSTATUS)0xC0000022 #endif #ifndef STATUS_DEBUGGER_INACTIVE #define STATUS_DEBUGGER_INACTIVE (NTSTATUS)0xC0000354 #endif #ifndef BUS_DATA_TYPE #define BUS_DATA_TYPE LONG #endif #ifndef PCIConfiguration #define PCIConfiguration (BUS_DATA_TYPE)4 #endif #ifndef SYSDBG_COMMAND #define SYSDBG_COMMAND ULONG #endif #ifndef SysDbgReadBusData #define SysDbgReadBusData (SYSDBG_COMMAND)18 #endif #ifndef SysDbgWriteBusData #define SysDbgWriteBusData (SYSDBG_COMMAND)19 #endif #ifndef SYSDBG_BUS_DATA typedef struct _SYSDBG_BUS_DATA { ULONG Address; PVOID Buffer; ULONG Request; BUS_DATA_TYPE BusDataType; ULONG BusNumber; ULONG SlotNumber; } SYSDBG_BUS_DATA, *PSYSDBG_BUS_DATA; #define SYSDBG_BUS_DATA SYSDBG_BUS_DATA #endif #ifndef PCI_SLOT_NUMBER typedef struct _PCI_SLOT_NUMBER { union { struct { ULONG DeviceNumber:5; ULONG FunctionNumber:3; ULONG Reserved:24; } bits; ULONG AsULONG; } u; } PCI_SLOT_NUMBER, *PPCI_SLOT_NUMBER; #define PCI_SLOT_NUMBER PCI_SLOT_NUMBER #endif #ifdef NtSystemDebugControl #undef NtSystemDebugControl #endif static NTSTATUS (NTAPI *MyNtSystemDebugControl)(SYSDBG_COMMAND Command, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength, PULONG ReturnLength); #define NtSystemDebugControl MyNtSystemDebugControl static BOOL debug_privilege_enabled; static LUID luid_debug_privilege; static BOOL revert_only_privilege; static HANDLE revert_token; static HMODULE ntdll; static int win32_sysdbg_initialized; static NTSTATUS win32_sysdbg_pci_bus_data(BOOL WriteBusData, BYTE BusNumber, BYTE DeviceNumber, BYTE FunctionNumber, BYTE Address, PVOID Buffer, BYTE BufferSize, PULONG Length) { SYSDBG_BUS_DATA sysdbg_cmd; PCI_SLOT_NUMBER pci_slot; if (!NtSystemDebugControl) return STATUS_NOT_IMPLEMENTED; memset(&pci_slot, 0, sizeof(pci_slot)); memset(&sysdbg_cmd, 0, sizeof(sysdbg_cmd)); sysdbg_cmd.Address = Address; sysdbg_cmd.Buffer = Buffer; sysdbg_cmd.Request = BufferSize; sysdbg_cmd.BusDataType = PCIConfiguration; sysdbg_cmd.BusNumber = BusNumber; pci_slot.u.bits.DeviceNumber = DeviceNumber; pci_slot.u.bits.FunctionNumber = FunctionNumber; sysdbg_cmd.SlotNumber = pci_slot.u.AsULONG; *Length = 0; return NtSystemDebugControl(WriteBusData ? SysDbgWriteBusData : SysDbgReadBusData, &sysdbg_cmd, sizeof(sysdbg_cmd), NULL, 0, Length); } static int win32_sysdbg_setup(struct pci_access *a) { UINT prev_error_mode; NTSTATUS status; ULONG ret_len; DWORD id; if (win32_sysdbg_initialized) return 1; prev_error_mode = win32_change_error_mode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); ntdll = LoadLibrary(TEXT("ntdll.dll")); win32_change_error_mode(prev_error_mode); if (!ntdll) { a->debug("Cannot open ntdll.dll library."); return 0; } NtSystemDebugControl = (LPVOID)GetProcAddress(ntdll, "NtSystemDebugControl"); if (!NtSystemDebugControl) { a->debug("Function NtSystemDebugControl() is not supported."); FreeLibrary(ntdll); ntdll = NULL; return 0; } /* * Try to read PCI id register from PCI device 00:00.0. * If this device does not exist and NT SysDbg API is working then * NT SysDbg returns STATUS_UNSUCCESSFUL. */ status = win32_sysdbg_pci_bus_data(FALSE, 0, 0, 0, 0, &id, sizeof(id), &ret_len); if ((status >= 0 && ret_len == sizeof(id)) || status == STATUS_UNSUCCESSFUL) { win32_sysdbg_initialized = 1; return 1; } else if (status != STATUS_ACCESS_DENIED) { if (status == STATUS_NOT_IMPLEMENTED || status == STATUS_INVALID_INFO_CLASS) a->debug("NT SysDbg is not supported."); else if (status == STATUS_DEBUGGER_INACTIVE) a->debug("NT SysDbg is disabled."); else a->debug("NT SysDbg returned error 0x%lx.", status); FreeLibrary(ntdll); ntdll = NULL; NtSystemDebugControl = NULL; return 0; } a->debug("NT SysDbg returned Access Denied, trying again with Debug privilege..."); if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid_debug_privilege)) { a->debug("Debug privilege is not supported."); FreeLibrary(ntdll); ntdll = NULL; NtSystemDebugControl = NULL; return 0; } if (!win32_enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege)) { a->debug("Cannot enable Debug privilege."); FreeLibrary(ntdll); ntdll = NULL; NtSystemDebugControl = NULL; return 0; } status = win32_sysdbg_pci_bus_data(FALSE, 0, 0, 0, 0, &id, sizeof(id), &ret_len); if ((status >= 0 && ret_len == sizeof(id)) || status == STATUS_UNSUCCESSFUL) { a->debug("Succeeded."); debug_privilege_enabled = TRUE; win32_sysdbg_initialized = 1; return 1; } win32_revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege); revert_token = NULL; revert_only_privilege = FALSE; FreeLibrary(ntdll); ntdll = NULL; NtSystemDebugControl = NULL; if (status == STATUS_NOT_IMPLEMENTED || status == STATUS_INVALID_INFO_CLASS) a->debug("NT SysDbg is not supported."); else if (status == STATUS_DEBUGGER_INACTIVE) a->debug("NT SysDbg is disabled."); else if (status == STATUS_ACCESS_DENIED) a->debug("NT SysDbg returned Access Denied."); else a->debug("NT SysDbg returned error 0x%lx.", status); return 0; } static int win32_sysdbg_detect(struct pci_access *a) { if (!win32_sysdbg_setup(a)) return 0; return 1; } static void win32_sysdbg_init(struct pci_access *a) { if (!win32_sysdbg_setup(a)) { a->debug("\n"); a->error("NT SysDbg PCI Bus Data interface cannot be accessed."); } } static void win32_sysdbg_cleanup(struct pci_access *a UNUSED) { if (!win32_sysdbg_initialized) return; if (debug_privilege_enabled) { win32_revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege); revert_token = NULL; revert_only_privilege = FALSE; debug_privilege_enabled = FALSE; } FreeLibrary(ntdll); ntdll = NULL; NtSystemDebugControl = NULL; win32_sysdbg_initialized = 0; } static int win32_sysdbg_read(struct pci_dev *d, int pos, byte *buf, int len) { NTSTATUS status; ULONG ret_len; if ((unsigned int)d->domain > 0 || (unsigned int)pos > 255 || (unsigned int)(pos+len) > 256) return 0; status = win32_sysdbg_pci_bus_data(FALSE, d->bus, d->dev, d->func, pos, buf, len, &ret_len); if (status < 0 || ret_len != (unsigned int)len) return 0; return 1; } static int win32_sysdbg_write(struct pci_dev *d, int pos, byte *buf, int len) { NTSTATUS status; ULONG ret_len; if ((unsigned int)d->domain > 0 || (unsigned int)pos > 255 || (unsigned int)(pos+len) > 256) return 0; status = win32_sysdbg_pci_bus_data(TRUE, d->bus, d->dev, d->func, pos, buf, len, &ret_len); if (status < 0 || ret_len != (unsigned int)len) return 0; return 1; } struct pci_methods pm_win32_sysdbg = { .name = "win32-sysdbg", .help = "Win32 PCI config space access using NT SysDbg Bus Data interface", .detect = win32_sysdbg_detect, .init = win32_sysdbg_init, .cleanup = win32_sysdbg_cleanup, .scan = pci_generic_scan, .fill_info = pci_generic_fill_info, .read = win32_sysdbg_read, .write = win32_sysdbg_write, }; pciutils-3.13.0/lib/header.h0000644000175000001440000025751514613646765014167 0ustar mjusers/* * The PCI Library -- PCI Header Structure (based on ) * * Copyright (c) 1997--2010 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+ * * SPDX-License-Identifier: GPL-2.0-or-later */ /* * Under PCI, each device has 256 bytes of configuration address space, * of which the first 64 bytes are standardized as follows: */ #define PCI_VENDOR_ID 0x00 /* 16 bits */ #define PCI_DEVICE_ID 0x02 /* 16 bits */ #define PCI_COMMAND 0x04 /* 16 bits */ #define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */ #define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */ #define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */ #define PCI_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */ #define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */ #define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */ #define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */ #define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */ #define PCI_COMMAND_SERR 0x100 /* Enable SERR */ #define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */ #define PCI_COMMAND_DISABLE_INTx 0x400 /* PCIE: Disable INTx interrupts */ #define PCI_STATUS 0x06 /* 16 bits */ #define PCI_STATUS_INTx 0x08 /* PCIE: INTx interrupt pending */ #define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */ #define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */ #define PCI_STATUS_UDF 0x40 /* Support User Definable Features [obsolete] */ #define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */ #define PCI_STATUS_PARITY 0x100 /* Detected parity error */ #define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */ #define PCI_STATUS_DEVSEL_FAST 0x000 #define PCI_STATUS_DEVSEL_MEDIUM 0x200 #define PCI_STATUS_DEVSEL_SLOW 0x400 #define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */ #define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */ #define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */ #define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */ #define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */ #define PCI_CLASS_REVISION 0x08 /* High 24 bits are class, low 8 revision */ #define PCI_REVISION_ID 0x08 /* Revision ID */ #define PCI_CLASS_PROG 0x09 /* Reg. Level Programming Interface */ #define PCI_CLASS_DEVICE 0x0a /* Device class */ #define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */ #define PCI_LATENCY_TIMER 0x0d /* 8 bits */ #define PCI_HEADER_TYPE 0x0e /* 8 bits */ #define PCI_HEADER_TYPE_NORMAL 0 #define PCI_HEADER_TYPE_BRIDGE 1 #define PCI_HEADER_TYPE_CARDBUS 2 #define PCI_BIST 0x0f /* 8 bits */ #define PCI_BIST_CODE_MASK 0x0f /* Return result */ #define PCI_BIST_START 0x40 /* 1 to start BIST, 2 secs or less */ #define PCI_BIST_CAPABLE 0x80 /* 1 if BIST capable */ /* * Base addresses specify locations in memory or I/O space. * Decoded size can be determined by writing a value of * 0xffffffff to the register, and reading it back. Only * 1 bits are decoded. */ #define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */ #define PCI_BASE_ADDRESS_1 0x14 /* 32 bits [htype 0,1 only] */ #define PCI_BASE_ADDRESS_2 0x18 /* 32 bits [htype 0 only] */ #define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */ #define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */ #define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */ #define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */ #define PCI_BASE_ADDRESS_SPACE_IO 0x01 #define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00 #define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06 #define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */ #define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */ #define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */ #define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */ #define PCI_BASE_ADDRESS_MEM_MASK (~(pciaddr_t)0x0f) #define PCI_BASE_ADDRESS_IO_MASK (~(pciaddr_t)0x03) /* bit 1 is reserved if address_space = 1 */ /* Header type 0 (normal devices) */ #define PCI_CARDBUS_CIS 0x28 #define PCI_SUBSYSTEM_VENDOR_ID 0x2c #define PCI_SUBSYSTEM_ID 0x2e #define PCI_ROM_ADDRESS 0x30 /* Bits 31..11 are address, 10..1 reserved */ #define PCI_ROM_ADDRESS_ENABLE 0x01 #define PCI_ROM_ADDRESS_MASK (~(pciaddr_t)0x7ff) #define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */ /* 0x35-0x3b are reserved */ #define PCI_INTERRUPT_LINE 0x3c /* 8 bits */ #define PCI_INTERRUPT_PIN 0x3d /* 8 bits */ #define PCI_MIN_GNT 0x3e /* 8 bits */ #define PCI_MAX_LAT 0x3f /* 8 bits */ /* Header type 1 (PCI-to-PCI bridges) */ #define PCI_PRIMARY_BUS 0x18 /* Primary bus number */ #define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */ #define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */ #define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */ #define PCI_IO_BASE 0x1c /* I/O range behind the bridge */ #define PCI_IO_LIMIT 0x1d #define PCI_IO_RANGE_TYPE_MASK 0x0f /* I/O bridging type */ #define PCI_IO_RANGE_TYPE_16 0x00 #define PCI_IO_RANGE_TYPE_32 0x01 #define PCI_IO_RANGE_MASK ~0x0f #define PCI_SEC_STATUS 0x1e /* Secondary status register */ #define PCI_MEMORY_BASE 0x20 /* Memory range behind */ #define PCI_MEMORY_LIMIT 0x22 #define PCI_MEMORY_RANGE_TYPE_MASK 0x0f #define PCI_MEMORY_RANGE_MASK ~0x0f #define PCI_PREF_MEMORY_BASE 0x24 /* Prefetchable memory range behind */ #define PCI_PREF_MEMORY_LIMIT 0x26 #define PCI_PREF_RANGE_TYPE_MASK 0x0f #define PCI_PREF_RANGE_TYPE_32 0x00 #define PCI_PREF_RANGE_TYPE_64 0x01 #define PCI_PREF_RANGE_MASK ~0x0f #define PCI_PREF_BASE_UPPER32 0x28 /* Upper half of prefetchable memory range */ #define PCI_PREF_LIMIT_UPPER32 0x2c #define PCI_IO_BASE_UPPER16 0x30 /* Upper half of I/O addresses */ #define PCI_IO_LIMIT_UPPER16 0x32 /* 0x34 same as for htype 0 */ /* 0x35-0x3b is reserved */ #define PCI_ROM_ADDRESS1 0x38 /* Same as PCI_ROM_ADDRESS, but for htype 1 */ /* 0x3c-0x3d are same as for htype 0 */ #define PCI_BRIDGE_CONTROL 0x3e #define PCI_BRIDGE_CTL_PARITY 0x01 /* Enable parity detection on secondary interface */ #define PCI_BRIDGE_CTL_SERR 0x02 /* The same for SERR forwarding */ #define PCI_BRIDGE_CTL_NO_ISA 0x04 /* Disable bridging of ISA ports */ #define PCI_BRIDGE_CTL_VGA 0x08 /* Forward VGA addresses */ #define PCI_BRIDGE_CTL_VGA_16BIT 0x10 /* VGA 16-bit decode */ #define PCI_BRIDGE_CTL_MASTER_ABORT 0x20 /* Report master aborts */ #define PCI_BRIDGE_CTL_BUS_RESET 0x40 /* Secondary bus reset */ #define PCI_BRIDGE_CTL_FAST_BACK 0x80 /* Fast Back2Back enabled on secondary interface */ #define PCI_BRIDGE_CTL_PRI_DISCARD_TIMER 0x100 /* PCI-X? */ #define PCI_BRIDGE_CTL_SEC_DISCARD_TIMER 0x200 /* PCI-X? */ #define PCI_BRIDGE_CTL_DISCARD_TIMER_STATUS 0x400 /* PCI-X? */ #define PCI_BRIDGE_CTL_DISCARD_TIMER_SERR_EN 0x800 /* PCI-X? */ /* Header type 2 (CardBus bridges) */ #define PCI_CB_CAPABILITY_LIST 0x14 /* 0x15 reserved */ #define PCI_CB_SEC_STATUS 0x16 /* Secondary status */ #define PCI_CB_PRIMARY_BUS 0x18 /* PCI bus number */ #define PCI_CB_CARD_BUS 0x19 /* CardBus bus number */ #define PCI_CB_SUBORDINATE_BUS 0x1a /* Subordinate bus number */ #define PCI_CB_LATENCY_TIMER 0x1b /* CardBus latency timer */ #define PCI_CB_MEMORY_BASE_0 0x1c #define PCI_CB_MEMORY_LIMIT_0 0x20 #define PCI_CB_MEMORY_BASE_1 0x24 #define PCI_CB_MEMORY_LIMIT_1 0x28 #define PCI_CB_IO_BASE_0 0x2c #define PCI_CB_IO_BASE_0_HI 0x2e #define PCI_CB_IO_LIMIT_0 0x30 #define PCI_CB_IO_LIMIT_0_HI 0x32 #define PCI_CB_IO_BASE_1 0x34 #define PCI_CB_IO_BASE_1_HI 0x36 #define PCI_CB_IO_LIMIT_1 0x38 #define PCI_CB_IO_LIMIT_1_HI 0x3a #define PCI_CB_IO_RANGE_MASK ~0x03 /* 0x3c-0x3d are same as for htype 0 */ #define PCI_CB_BRIDGE_CONTROL 0x3e #define PCI_CB_BRIDGE_CTL_PARITY 0x01 /* Similar to standard bridge control register */ #define PCI_CB_BRIDGE_CTL_SERR 0x02 #define PCI_CB_BRIDGE_CTL_ISA 0x04 #define PCI_CB_BRIDGE_CTL_VGA 0x08 #define PCI_CB_BRIDGE_CTL_MASTER_ABORT 0x20 #define PCI_CB_BRIDGE_CTL_CB_RESET 0x40 /* CardBus reset */ #define PCI_CB_BRIDGE_CTL_16BIT_INT 0x80 /* Enable interrupt for 16-bit cards */ #define PCI_CB_BRIDGE_CTL_PREFETCH_MEM0 0x100 /* Prefetch enable for both memory regions */ #define PCI_CB_BRIDGE_CTL_PREFETCH_MEM1 0x200 #define PCI_CB_BRIDGE_CTL_POST_WRITES 0x400 #define PCI_CB_SUBSYSTEM_VENDOR_ID 0x40 #define PCI_CB_SUBSYSTEM_ID 0x42 #define PCI_CB_LEGACY_MODE_BASE 0x44 /* 16-bit PC Card legacy mode base address (ExCa) */ /* 0x48-0x7f reserved */ /* Capability lists */ #define PCI_CAP_LIST_ID 0 /* Capability ID */ #define PCI_CAP_ID_NULL 0x00 /* Null Capability */ #define PCI_CAP_ID_PM 0x01 /* Power Management */ #define PCI_CAP_ID_AGP 0x02 /* Accelerated Graphics Port */ #define PCI_CAP_ID_VPD 0x03 /* Vital Product Data */ #define PCI_CAP_ID_SLOTID 0x04 /* Slot Identification */ #define PCI_CAP_ID_MSI 0x05 /* Message Signaled Interrupts */ #define PCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */ #define PCI_CAP_ID_PCIX 0x07 /* PCI-X */ #define PCI_CAP_ID_HT 0x08 /* HyperTransport */ #define PCI_CAP_ID_VNDR 0x09 /* Vendor specific */ #define PCI_CAP_ID_DBG 0x0A /* Debug port */ #define PCI_CAP_ID_CCRC 0x0B /* CompactPCI Central Resource Control */ #define PCI_CAP_ID_HOTPLUG 0x0C /* PCI hot-plug */ #define PCI_CAP_ID_SSVID 0x0D /* Bridge subsystem vendor/device ID */ #define PCI_CAP_ID_AGP3 0x0E /* AGP 8x */ #define PCI_CAP_ID_SECURE 0x0F /* Secure device (?) */ #define PCI_CAP_ID_EXP 0x10 /* PCI Express */ #define PCI_CAP_ID_MSIX 0x11 /* MSI-X */ #define PCI_CAP_ID_SATA 0x12 /* Serial-ATA HBA */ #define PCI_CAP_ID_AF 0x13 /* Advanced features of PCI devices integrated in PCIe root cplx */ #define PCI_CAP_ID_EA 0x14 /* Enhanced Allocation */ #define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */ #define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */ #define PCI_CAP_SIZEOF 4 /* Capabilities residing in the PCI Express extended configuration space */ #define PCI_EXT_CAP_ID_NULL 0x00 /* Null Capability */ #define PCI_EXT_CAP_ID_AER 0x01 /* Advanced Error Reporting */ #define PCI_EXT_CAP_ID_VC 0x02 /* Virtual Channel */ #define PCI_EXT_CAP_ID_DSN 0x03 /* Device Serial Number */ #define PCI_EXT_CAP_ID_PB 0x04 /* Power Budgeting */ #define PCI_EXT_CAP_ID_RCLINK 0x05 /* Root Complex Link Declaration */ #define PCI_EXT_CAP_ID_RCILINK 0x06 /* Root Complex Internal Link Declaration */ #define PCI_EXT_CAP_ID_RCEC 0x07 /* Root Complex Event Collector */ #define PCI_EXT_CAP_ID_MFVC 0x08 /* Multi-Function Virtual Channel */ #define PCI_EXT_CAP_ID_VC2 0x09 /* Virtual Channel (2nd ID) */ #define PCI_EXT_CAP_ID_RCRB 0x0a /* Root Complex Register Block */ #define PCI_EXT_CAP_ID_VNDR 0x0b /* Vendor specific */ #define PCI_EXT_CAP_ID_ACS 0x0d /* Access Controls */ #define PCI_EXT_CAP_ID_ARI 0x0e /* Alternative Routing-ID Interpretation */ #define PCI_EXT_CAP_ID_ATS 0x0f /* Address Translation Service */ #define PCI_EXT_CAP_ID_SRIOV 0x10 /* Single Root I/O Virtualization */ #define PCI_EXT_CAP_ID_MRIOV 0x11 /* Multi-Root I/O Virtualization */ #define PCI_EXT_CAP_ID_MCAST 0x12 /* Multicast */ #define PCI_EXT_CAP_ID_PRI 0x13 /* Page Request Interface */ #define PCI_EXT_CAP_ID_REBAR 0x15 /* Resizable BAR */ #define PCI_EXT_CAP_ID_DPA 0x16 /* Dynamic Power Allocation */ #define PCI_EXT_CAP_ID_TPH 0x17 /* Transaction processing hints */ #define PCI_EXT_CAP_ID_LTR 0x18 /* Latency Tolerance Reporting */ #define PCI_EXT_CAP_ID_SECPCI 0x19 /* Secondary PCI Express */ #define PCI_EXT_CAP_ID_PMUX 0x1a /* Protocol Multiplexing */ #define PCI_EXT_CAP_ID_PASID 0x1b /* Process Address Space ID */ #define PCI_EXT_CAP_ID_LNR 0x1c /* LN Requester */ #define PCI_EXT_CAP_ID_DPC 0x1d /* Downstream Port Containment */ #define PCI_EXT_CAP_ID_L1PM 0x1e /* L1 PM Substates */ #define PCI_EXT_CAP_ID_PTM 0x1f /* Precision Time Measurement */ #define PCI_EXT_CAP_ID_M_PCIE 0x20 /* PCIe over M-PHY */ #define PCI_EXT_CAP_ID_FRS 0x21 /* FRS Queuing */ #define PCI_EXT_CAP_ID_RTR 0x22 /* Readiness Time Reporting */ #define PCI_EXT_CAP_ID_DVSEC 0x23 /* Designated Vendor-Specific */ #define PCI_EXT_CAP_ID_VF_REBAR 0x24 /* VF Resizable BAR */ #define PCI_EXT_CAP_ID_DLNK 0x25 /* Data Link Feature */ #define PCI_EXT_CAP_ID_16GT 0x26 /* Physical Layer 16.0 GT/s */ #define PCI_EXT_CAP_ID_LMR 0x27 /* Lane Margining at Receiver */ #define PCI_EXT_CAP_ID_HIER_ID 0x28 /* Hierarchy ID */ #define PCI_EXT_CAP_ID_NPEM 0x29 /* Native PCIe Enclosure Management */ #define PCI_EXT_CAP_ID_32GT 0x2a /* Physical Layer 32.0 GT/s */ #define PCI_EXT_CAP_ID_DOE 0x2e /* Data Object Exchange */ #define PCI_EXT_CAP_ID_IDE 0x30 /* Integrity and Data Encryption */ /*** Definitions of capabilities ***/ /* Power Management Registers */ #define PCI_PM_CAP_VER_MASK 0x0007 /* Version (2=PM1.1) */ #define PCI_PM_CAP_PME_CLOCK 0x0008 /* Clock required for PME generation */ #define PCI_PM_CAP_DSI 0x0020 /* Device specific initialization required */ #define PCI_PM_CAP_AUX_C_MASK 0x01c0 /* Maximum aux current required in D3cold */ #define PCI_PM_CAP_D1 0x0200 /* D1 power state support */ #define PCI_PM_CAP_D2 0x0400 /* D2 power state support */ #define PCI_PM_CAP_PME_D0 0x0800 /* PME can be asserted from D0 */ #define PCI_PM_CAP_PME_D1 0x1000 /* PME can be asserted from D1 */ #define PCI_PM_CAP_PME_D2 0x2000 /* PME can be asserted from D2 */ #define PCI_PM_CAP_PME_D3_HOT 0x4000 /* PME can be asserted from D3hot */ #define PCI_PM_CAP_PME_D3_COLD 0x8000 /* PME can be asserted from D3cold */ #define PCI_PM_CTRL 4 /* PM control and status register */ #define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */ #define PCI_PM_CTRL_NO_SOFT_RST 0x0008 /* No Soft Reset from D3hot to D0 */ #define PCI_PM_CTRL_PME_ENABLE 0x0100 /* PME pin enable */ #define PCI_PM_CTRL_DATA_SEL_MASK 0x1e00 /* PM table data index */ #define PCI_PM_CTRL_DATA_SCALE_MASK 0x6000 /* PM table data scaling factor */ #define PCI_PM_CTRL_PME_STATUS 0x8000 /* PME pin status */ #define PCI_PM_PPB_EXTENSIONS 6 /* PPB support extensions */ #define PCI_PM_PPB_B2_B3 0x40 /* If bridge enters D3hot, bus enters: 0=B3, 1=B2 */ #define PCI_PM_BPCC_ENABLE 0x80 /* Secondary bus is power managed */ #define PCI_PM_DATA_REGISTER 7 /* PM table contents read here */ #define PCI_PM_SIZEOF 8 /* AGP registers */ #define PCI_AGP_VERSION 2 /* BCD version number */ #define PCI_AGP_RFU 3 /* Rest of capability flags */ #define PCI_AGP_STATUS 4 /* Status register */ #define PCI_AGP_STATUS_RQ_MASK 0xff000000 /* Maximum number of requests - 1 */ #define PCI_AGP_STATUS_ISOCH 0x10000 /* Isochronous transactions supported */ #define PCI_AGP_STATUS_ARQSZ_MASK 0xe000 /* log2(optimum async req size in bytes) - 4 */ #define PCI_AGP_STATUS_CAL_MASK 0x1c00 /* Calibration cycle timing */ #define PCI_AGP_STATUS_SBA 0x0200 /* Sideband addressing supported */ #define PCI_AGP_STATUS_ITA_COH 0x0100 /* In-aperture accesses always coherent */ #define PCI_AGP_STATUS_GART64 0x0080 /* 64-bit GART entries supported */ #define PCI_AGP_STATUS_HTRANS 0x0040 /* If 0, core logic can xlate host CPU accesses thru aperture */ #define PCI_AGP_STATUS_64BIT 0x0020 /* 64-bit addressing cycles supported */ #define PCI_AGP_STATUS_FW 0x0010 /* Fast write transfers supported */ #define PCI_AGP_STATUS_AGP3 0x0008 /* AGP3 mode supported */ #define PCI_AGP_STATUS_RATE4 0x0004 /* 4x transfer rate supported (RFU in AGP3 mode) */ #define PCI_AGP_STATUS_RATE2 0x0002 /* 2x transfer rate supported (8x in AGP3 mode) */ #define PCI_AGP_STATUS_RATE1 0x0001 /* 1x transfer rate supported (4x in AGP3 mode) */ #define PCI_AGP_COMMAND 8 /* Control register */ #define PCI_AGP_COMMAND_RQ_MASK 0xff000000 /* Master: Maximum number of requests */ #define PCI_AGP_COMMAND_ARQSZ_MASK 0xe000 /* log2(optimum async req size in bytes) - 4 */ #define PCI_AGP_COMMAND_CAL_MASK 0x1c00 /* Calibration cycle timing */ #define PCI_AGP_COMMAND_SBA 0x0200 /* Sideband addressing enabled */ #define PCI_AGP_COMMAND_AGP 0x0100 /* Allow processing of AGP transactions */ #define PCI_AGP_COMMAND_GART64 0x0080 /* 64-bit GART entries enabled */ #define PCI_AGP_COMMAND_64BIT 0x0020 /* Allow generation of 64-bit addr cycles */ #define PCI_AGP_COMMAND_FW 0x0010 /* Enable FW transfers */ #define PCI_AGP_COMMAND_RATE4 0x0004 /* Use 4x rate (RFU in AGP3 mode) */ #define PCI_AGP_COMMAND_RATE2 0x0002 /* Use 2x rate (8x in AGP3 mode) */ #define PCI_AGP_COMMAND_RATE1 0x0001 /* Use 1x rate (4x in AGP3 mode) */ #define PCI_AGP_SIZEOF 12 /* Vital Product Data */ #define PCI_VPD_ADDR 2 /* Address to access (15 bits!) */ #define PCI_VPD_ADDR_MASK 0x7fff /* Address mask */ #define PCI_VPD_ADDR_F 0x8000 /* Write 0, 1 indicates completion */ #define PCI_VPD_DATA 4 /* 32-bits of data returned here */ /* Slot Identification */ #define PCI_SID_ESR 2 /* Expansion Slot Register */ #define PCI_SID_ESR_NSLOTS 0x1f /* Number of expansion slots available */ #define PCI_SID_ESR_FIC 0x20 /* First In Chassis Flag */ #define PCI_SID_CHASSIS_NR 3 /* Chassis Number */ /* Message Signaled Interrupts registers */ #define PCI_MSI_FLAGS 2 /* Various flags */ #define PCI_MSI_FLAGS_MASK_BIT 0x100 /* interrupt masking & reporting supported */ #define PCI_MSI_FLAGS_64BIT 0x080 /* 64-bit addresses allowed */ #define PCI_MSI_FLAGS_QSIZE 0x070 /* Message queue size configured */ #define PCI_MSI_FLAGS_QMASK 0x00e /* Maximum queue size available */ #define PCI_MSI_FLAGS_ENABLE 0x001 /* MSI feature enabled */ #define PCI_MSI_RFU 3 /* Rest of capability flags */ #define PCI_MSI_ADDRESS_LO 4 /* Lower 32 bits */ #define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */ #define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */ #define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */ #define PCI_MSI_MASK_BIT_32 12 /* per-vector masking for 32-bit devices */ #define PCI_MSI_MASK_BIT_64 16 /* per-vector masking for 64-bit devices */ #define PCI_MSI_PENDING_32 16 /* per-vector interrupt pending for 32-bit devices */ #define PCI_MSI_PENDING_64 20 /* per-vector interrupt pending for 64-bit devices */ /* PCI-X */ #define PCI_PCIX_COMMAND 2 /* Command register offset */ #define PCI_PCIX_COMMAND_DPERE 0x0001 /* Data Parity Error Recover Enable */ #define PCI_PCIX_COMMAND_ERO 0x0002 /* Enable Relaxed Ordering */ #define PCI_PCIX_COMMAND_MAX_MEM_READ_BYTE_COUNT 0x000c /* Maximum Memory Read Byte Count */ #define PCI_PCIX_COMMAND_MAX_OUTSTANDING_SPLIT_TRANS 0x0070 #define PCI_PCIX_COMMAND_RESERVED 0xf80 #define PCI_PCIX_STATUS 4 /* Status register offset */ #define PCI_PCIX_STATUS_FUNCTION 0x00000007 #define PCI_PCIX_STATUS_DEVICE 0x000000f8 #define PCI_PCIX_STATUS_BUS 0x0000ff00 #define PCI_PCIX_STATUS_64BIT 0x00010000 #define PCI_PCIX_STATUS_133MHZ 0x00020000 #define PCI_PCIX_STATUS_SC_DISCARDED 0x00040000 /* Split Completion Discarded */ #define PCI_PCIX_STATUS_UNEXPECTED_SC 0x00080000 /* Unexpected Split Completion */ #define PCI_PCIX_STATUS_DEVICE_COMPLEXITY 0x00100000 /* 0 = simple device, 1 = bridge device */ #define PCI_PCIX_STATUS_DESIGNED_MAX_MEM_READ_BYTE_COUNT 0x00600000 /* 0 = 512 bytes, 1 = 1024, 2 = 2048, 3 = 4096 */ #define PCI_PCIX_STATUS_DESIGNED_MAX_OUTSTANDING_SPLIT_TRANS 0x03800000 #define PCI_PCIX_STATUS_DESIGNED_MAX_CUMULATIVE_READ_SIZE 0x1c000000 #define PCI_PCIX_STATUS_RCVD_SC_ERR_MESS 0x20000000 /* Received Split Completion Error Message */ #define PCI_PCIX_STATUS_266MHZ 0x40000000 /* 266 MHz capable */ #define PCI_PCIX_STATUS_533MHZ 0x80000000 /* 533 MHz capable */ #define PCI_PCIX_SIZEOF 4 /* PCI-X Bridges */ #define PCI_PCIX_BRIDGE_SEC_STATUS 2 /* Secondary bus status register offset */ #define PCI_PCIX_BRIDGE_SEC_STATUS_64BIT 0x0001 #define PCI_PCIX_BRIDGE_SEC_STATUS_133MHZ 0x0002 #define PCI_PCIX_BRIDGE_SEC_STATUS_SC_DISCARDED 0x0004 /* Split Completion Discarded on secondary bus */ #define PCI_PCIX_BRIDGE_SEC_STATUS_UNEXPECTED_SC 0x0008 /* Unexpected Split Completion on secondary bus */ #define PCI_PCIX_BRIDGE_SEC_STATUS_SC_OVERRUN 0x0010 /* Split Completion Overrun on secondary bus */ #define PCI_PCIX_BRIDGE_SEC_STATUS_SPLIT_REQUEST_DELAYED 0x0020 #define PCI_PCIX_BRIDGE_SEC_STATUS_CLOCK_FREQ 0x01c0 #define PCI_PCIX_BRIDGE_SEC_STATUS_RESERVED 0xfe00 #define PCI_PCIX_BRIDGE_STATUS 4 /* Primary bus status register offset */ #define PCI_PCIX_BRIDGE_STATUS_FUNCTION 0x00000007 #define PCI_PCIX_BRIDGE_STATUS_DEVICE 0x000000f8 #define PCI_PCIX_BRIDGE_STATUS_BUS 0x0000ff00 #define PCI_PCIX_BRIDGE_STATUS_64BIT 0x00010000 #define PCI_PCIX_BRIDGE_STATUS_133MHZ 0x00020000 #define PCI_PCIX_BRIDGE_STATUS_SC_DISCARDED 0x00040000 /* Split Completion Discarded */ #define PCI_PCIX_BRIDGE_STATUS_UNEXPECTED_SC 0x00080000 /* Unexpected Split Completion */ #define PCI_PCIX_BRIDGE_STATUS_SC_OVERRUN 0x00100000 /* Split Completion Overrun */ #define PCI_PCIX_BRIDGE_STATUS_SPLIT_REQUEST_DELAYED 0x00200000 #define PCI_PCIX_BRIDGE_STATUS_RESERVED 0xffc00000 #define PCI_PCIX_BRIDGE_UPSTREAM_SPLIT_TRANS_CTRL 8 /* Upstream Split Transaction Register offset */ #define PCI_PCIX_BRIDGE_DOWNSTREAM_SPLIT_TRANS_CTRL 12 /* Downstream Split Transaction Register offset */ #define PCI_PCIX_BRIDGE_STR_CAPACITY 0x0000ffff #define PCI_PCIX_BRIDGE_STR_COMMITMENT_LIMIT 0xffff0000 #define PCI_PCIX_BRIDGE_SIZEOF 12 /* HyperTransport (as of spec rev. 2.00) */ #define PCI_HT_CMD 2 /* Command Register */ #define PCI_HT_CMD_TYP_HI 0xe000 /* Capability Type high part */ #define PCI_HT_CMD_TYP_HI_PRI 0x0000 /* Slave or Primary Interface */ #define PCI_HT_CMD_TYP_HI_SEC 0x2000 /* Host or Secondary Interface */ #define PCI_HT_CMD_TYP 0xf800 /* Capability Type */ #define PCI_HT_CMD_TYP_SW 0x4000 /* Switch */ #define PCI_HT_CMD_TYP_IDC 0x8000 /* Interrupt Discovery and Configuration */ #define PCI_HT_CMD_TYP_RID 0x8800 /* Revision ID */ #define PCI_HT_CMD_TYP_UIDC 0x9000 /* UnitID Clumping */ #define PCI_HT_CMD_TYP_ECSA 0x9800 /* Extended Configuration Space Access */ #define PCI_HT_CMD_TYP_AM 0xa000 /* Address Mapping */ #define PCI_HT_CMD_TYP_MSIM 0xa800 /* MSI Mapping */ #define PCI_HT_CMD_TYP_DR 0xb000 /* DirectRoute */ #define PCI_HT_CMD_TYP_VCS 0xb800 /* VCSet */ #define PCI_HT_CMD_TYP_RM 0xc000 /* Retry Mode */ #define PCI_HT_CMD_TYP_X86 0xc800 /* X86 (reserved) */ /* Link Control Register */ #define PCI_HT_LCTR_CFLE 0x0002 /* CRC Flood Enable */ #define PCI_HT_LCTR_CST 0x0004 /* CRC Start Test */ #define PCI_HT_LCTR_CFE 0x0008 /* CRC Force Error */ #define PCI_HT_LCTR_LKFAIL 0x0010 /* Link Failure */ #define PCI_HT_LCTR_INIT 0x0020 /* Initialization Complete */ #define PCI_HT_LCTR_EOC 0x0040 /* End of Chain */ #define PCI_HT_LCTR_TXO 0x0080 /* Transmitter Off */ #define PCI_HT_LCTR_CRCERR 0x0f00 /* CRC Error */ #define PCI_HT_LCTR_ISOCEN 0x1000 /* Isochronous Flow Control Enable */ #define PCI_HT_LCTR_LSEN 0x2000 /* LDTSTOP# Tristate Enable */ #define PCI_HT_LCTR_EXTCTL 0x4000 /* Extended CTL Time */ #define PCI_HT_LCTR_64B 0x8000 /* 64-bit Addressing Enable */ /* Link Configuration Register */ #define PCI_HT_LCNF_MLWI 0x0007 /* Max Link Width In */ #define PCI_HT_LCNF_LW_8B 0x0 /* Link Width 8 bits */ #define PCI_HT_LCNF_LW_16B 0x1 /* Link Width 16 bits */ #define PCI_HT_LCNF_LW_32B 0x3 /* Link Width 32 bits */ #define PCI_HT_LCNF_LW_2B 0x4 /* Link Width 2 bits */ #define PCI_HT_LCNF_LW_4B 0x5 /* Link Width 4 bits */ #define PCI_HT_LCNF_LW_NC 0x7 /* Link physically not connected */ #define PCI_HT_LCNF_DFI 0x0008 /* Doubleword Flow Control In */ #define PCI_HT_LCNF_MLWO 0x0070 /* Max Link Width Out */ #define PCI_HT_LCNF_DFO 0x0080 /* Doubleword Flow Control Out */ #define PCI_HT_LCNF_LWI 0x0700 /* Link Width In */ #define PCI_HT_LCNF_DFIE 0x0800 /* Doubleword Flow Control In Enable */ #define PCI_HT_LCNF_LWO 0x7000 /* Link Width Out */ #define PCI_HT_LCNF_DFOE 0x8000 /* Doubleword Flow Control Out Enable */ /* Revision ID Register */ #define PCI_HT_RID_MIN 0x1f /* Minor Revision */ #define PCI_HT_RID_MAJ 0xe0 /* Major Revision */ /* Link Frequency/Error Register */ #define PCI_HT_LFRER_FREQ 0x0f /* Transmitter Clock Frequency */ #define PCI_HT_LFRER_200 0x00 /* 200MHz */ #define PCI_HT_LFRER_300 0x01 /* 300MHz */ #define PCI_HT_LFRER_400 0x02 /* 400MHz */ #define PCI_HT_LFRER_500 0x03 /* 500MHz */ #define PCI_HT_LFRER_600 0x04 /* 600MHz */ #define PCI_HT_LFRER_800 0x05 /* 800MHz */ #define PCI_HT_LFRER_1000 0x06 /* 1.0GHz */ #define PCI_HT_LFRER_1200 0x07 /* 1.2GHz */ #define PCI_HT_LFRER_1400 0x08 /* 1.4GHz */ #define PCI_HT_LFRER_1600 0x09 /* 1.6GHz */ #define PCI_HT_LFRER_VEND 0x0f /* Vendor-Specific */ #define PCI_HT_LFRER_ERR 0xf0 /* Link Error */ #define PCI_HT_LFRER_PROT 0x10 /* Protocol Error */ #define PCI_HT_LFRER_OV 0x20 /* Overflow Error */ #define PCI_HT_LFRER_EOC 0x40 /* End of Chain Error */ #define PCI_HT_LFRER_CTLT 0x80 /* CTL Timeout */ /* Link Frequency Capability Register */ #define PCI_HT_LFCAP_200 0x0001 /* 200MHz */ #define PCI_HT_LFCAP_300 0x0002 /* 300MHz */ #define PCI_HT_LFCAP_400 0x0004 /* 400MHz */ #define PCI_HT_LFCAP_500 0x0008 /* 500MHz */ #define PCI_HT_LFCAP_600 0x0010 /* 600MHz */ #define PCI_HT_LFCAP_800 0x0020 /* 800MHz */ #define PCI_HT_LFCAP_1000 0x0040 /* 1.0GHz */ #define PCI_HT_LFCAP_1200 0x0080 /* 1.2GHz */ #define PCI_HT_LFCAP_1400 0x0100 /* 1.4GHz */ #define PCI_HT_LFCAP_1600 0x0200 /* 1.6GHz */ #define PCI_HT_LFCAP_VEND 0x8000 /* Vendor-Specific */ /* Feature Register */ #define PCI_HT_FTR_ISOCFC 0x0001 /* Isochronous Flow Control Mode */ #define PCI_HT_FTR_LDTSTOP 0x0002 /* LDTSTOP# Supported */ #define PCI_HT_FTR_CRCTM 0x0004 /* CRC Test Mode */ #define PCI_HT_FTR_ECTLT 0x0008 /* Extended CTL Time Required */ #define PCI_HT_FTR_64BA 0x0010 /* 64-bit Addressing */ #define PCI_HT_FTR_UIDRD 0x0020 /* UnitID Reorder Disable */ /* Error Handling Register */ #define PCI_HT_EH_PFLE 0x0001 /* Protocol Error Flood Enable */ #define PCI_HT_EH_OFLE 0x0002 /* Overflow Error Flood Enable */ #define PCI_HT_EH_PFE 0x0004 /* Protocol Error Fatal Enable */ #define PCI_HT_EH_OFE 0x0008 /* Overflow Error Fatal Enable */ #define PCI_HT_EH_EOCFE 0x0010 /* End of Chain Error Fatal Enable */ #define PCI_HT_EH_RFE 0x0020 /* Response Error Fatal Enable */ #define PCI_HT_EH_CRCFE 0x0040 /* CRC Error Fatal Enable */ #define PCI_HT_EH_SERRFE 0x0080 /* System Error Fatal Enable (B */ #define PCI_HT_EH_CF 0x0100 /* Chain Fail */ #define PCI_HT_EH_RE 0x0200 /* Response Error */ #define PCI_HT_EH_PNFE 0x0400 /* Protocol Error Nonfatal Enable */ #define PCI_HT_EH_ONFE 0x0800 /* Overflow Error Nonfatal Enable */ #define PCI_HT_EH_EOCNFE 0x1000 /* End of Chain Error Nonfatal Enable */ #define PCI_HT_EH_RNFE 0x2000 /* Response Error Nonfatal Enable */ #define PCI_HT_EH_CRCNFE 0x4000 /* CRC Error Nonfatal Enable */ #define PCI_HT_EH_SERRNFE 0x8000 /* System Error Nonfatal Enable */ /* HyperTransport: Slave or Primary Interface */ #define PCI_HT_PRI_CMD 2 /* Command Register */ #define PCI_HT_PRI_CMD_BUID 0x001f /* Base UnitID */ #define PCI_HT_PRI_CMD_UC 0x03e0 /* Unit Count */ #define PCI_HT_PRI_CMD_MH 0x0400 /* Master Host */ #define PCI_HT_PRI_CMD_DD 0x0800 /* Default Direction */ #define PCI_HT_PRI_CMD_DUL 0x1000 /* Drop on Uninitialized Link */ #define PCI_HT_PRI_LCTR0 4 /* Link Control 0 Register */ #define PCI_HT_PRI_LCNF0 6 /* Link Config 0 Register */ #define PCI_HT_PRI_LCTR1 8 /* Link Control 1 Register */ #define PCI_HT_PRI_LCNF1 10 /* Link Config 1 Register */ #define PCI_HT_PRI_RID 12 /* Revision ID Register */ #define PCI_HT_PRI_LFRER0 13 /* Link Frequency/Error 0 Register */ #define PCI_HT_PRI_LFCAP0 14 /* Link Frequency Capability 0 Register */ #define PCI_HT_PRI_FTR 16 /* Feature Register */ #define PCI_HT_PRI_LFRER1 17 /* Link Frequency/Error 1 Register */ #define PCI_HT_PRI_LFCAP1 18 /* Link Frequency Capability 1 Register */ #define PCI_HT_PRI_ES 20 /* Enumeration Scratchpad Register */ #define PCI_HT_PRI_EH 22 /* Error Handling Register */ #define PCI_HT_PRI_MBU 24 /* Memory Base Upper Register */ #define PCI_HT_PRI_MLU 25 /* Memory Limit Upper Register */ #define PCI_HT_PRI_BN 26 /* Bus Number Register */ #define PCI_HT_PRI_SIZEOF 28 /* HyperTransport: Host or Secondary Interface */ #define PCI_HT_SEC_CMD 2 /* Command Register */ #define PCI_HT_SEC_CMD_WR 0x0001 /* Warm Reset */ #define PCI_HT_SEC_CMD_DE 0x0002 /* Double-Ended */ #define PCI_HT_SEC_CMD_DN 0x007c /* Device Number */ #define PCI_HT_SEC_CMD_CS 0x0080 /* Chain Side */ #define PCI_HT_SEC_CMD_HH 0x0100 /* Host Hide */ #define PCI_HT_SEC_CMD_AS 0x0400 /* Act as Slave */ #define PCI_HT_SEC_CMD_HIECE 0x0800 /* Host Inbound End of Chain Error */ #define PCI_HT_SEC_CMD_DUL 0x1000 /* Drop on Uninitialized Link */ #define PCI_HT_SEC_LCTR 4 /* Link Control Register */ #define PCI_HT_SEC_LCNF 6 /* Link Config Register */ #define PCI_HT_SEC_RID 8 /* Revision ID Register */ #define PCI_HT_SEC_LFRER 9 /* Link Frequency/Error Register */ #define PCI_HT_SEC_LFCAP 10 /* Link Frequency Capability Register */ #define PCI_HT_SEC_FTR 12 /* Feature Register */ #define PCI_HT_SEC_FTR_EXTRS 0x0100 /* Extended Register Set */ #define PCI_HT_SEC_FTR_UCNFE 0x0200 /* Upstream Configuration Enable */ #define PCI_HT_SEC_ES 16 /* Enumeration Scratchpad Register */ #define PCI_HT_SEC_EH 18 /* Error Handling Register */ #define PCI_HT_SEC_MBU 20 /* Memory Base Upper Register */ #define PCI_HT_SEC_MLU 21 /* Memory Limit Upper Register */ #define PCI_HT_SEC_SIZEOF 24 /* HyperTransport: Switch */ #define PCI_HT_SW_CMD 2 /* Switch Command Register */ #define PCI_HT_SW_CMD_VIBERR 0x0080 /* VIB Error */ #define PCI_HT_SW_CMD_VIBFL 0x0100 /* VIB Flood */ #define PCI_HT_SW_CMD_VIBFT 0x0200 /* VIB Fatal */ #define PCI_HT_SW_CMD_VIBNFT 0x0400 /* VIB Nonfatal */ #define PCI_HT_SW_PMASK 4 /* Partition Mask Register */ #define PCI_HT_SW_SWINF 8 /* Switch Info Register */ #define PCI_HT_SW_SWINF_DP 0x0000001f /* Default Port */ #define PCI_HT_SW_SWINF_EN 0x00000020 /* Enable Decode */ #define PCI_HT_SW_SWINF_CR 0x00000040 /* Cold Reset */ #define PCI_HT_SW_SWINF_PCIDX 0x00000f00 /* Performance Counter Index */ #define PCI_HT_SW_SWINF_BLRIDX 0x0003f000 /* Base/Limit Range Index */ #define PCI_HT_SW_SWINF_SBIDX 0x00002000 /* Secondary Base Range Index */ #define PCI_HT_SW_SWINF_HP 0x00040000 /* Hot Plug */ #define PCI_HT_SW_SWINF_HIDE 0x00080000 /* Hide Port */ #define PCI_HT_SW_PCD 12 /* Performance Counter Data Register */ #define PCI_HT_SW_BLRD 16 /* Base/Limit Range Data Register */ #define PCI_HT_SW_SBD 20 /* Secondary Base Data Register */ #define PCI_HT_SW_SIZEOF 24 /* Counter indices */ #define PCI_HT_SW_PC_PCR 0x0 /* Posted Command Receive */ #define PCI_HT_SW_PC_NPCR 0x1 /* Nonposted Command Receive */ #define PCI_HT_SW_PC_RCR 0x2 /* Response Command Receive */ #define PCI_HT_SW_PC_PDWR 0x3 /* Posted DW Receive */ #define PCI_HT_SW_PC_NPDWR 0x4 /* Nonposted DW Receive */ #define PCI_HT_SW_PC_RDWR 0x5 /* Response DW Receive */ #define PCI_HT_SW_PC_PCT 0x6 /* Posted Command Transmit */ #define PCI_HT_SW_PC_NPCT 0x7 /* Nonposted Command Transmit */ #define PCI_HT_SW_PC_RCT 0x8 /* Response Command Transmit */ #define PCI_HT_SW_PC_PDWT 0x9 /* Posted DW Transmit */ #define PCI_HT_SW_PC_NPDWT 0xa /* Nonposted DW Transmit */ #define PCI_HT_SW_PC_RDWT 0xb /* Response DW Transmit */ /* Base/Limit Range indices */ #define PCI_HT_SW_BLR_BASE0_LO 0x0 /* Base 0[31:1], Enable */ #define PCI_HT_SW_BLR_BASE0_HI 0x1 /* Base 0 Upper */ #define PCI_HT_SW_BLR_LIM0_LO 0x2 /* Limit 0 Lower */ #define PCI_HT_SW_BLR_LIM0_HI 0x3 /* Limit 0 Upper */ /* Secondary Base indices */ #define PCI_HT_SW_SB_LO 0x0 /* Secondary Base[31:1], Enable */ #define PCI_HT_SW_S0_HI 0x1 /* Secondary Base Upper */ /* HyperTransport: Interrupt Discovery and Configuration */ #define PCI_HT_IDC_IDX 2 /* Index Register */ #define PCI_HT_IDC_DATA 4 /* Data Register */ #define PCI_HT_IDC_SIZEOF 8 /* Register indices */ #define PCI_HT_IDC_IDX_LINT 0x01 /* Last Interrupt Register */ #define PCI_HT_IDC_LINT 0x00ff0000 /* Last interrupt definition */ #define PCI_HT_IDC_IDX_IDR 0x10 /* Interrupt Definition Registers */ /* Low part (at index) */ #define PCI_HT_IDC_IDR_MASK 0x10000001 /* Mask */ #define PCI_HT_IDC_IDR_POL 0x10000002 /* Polarity */ #define PCI_HT_IDC_IDR_II_2 0x1000001c /* IntrInfo[4:2]: Message Type */ #define PCI_HT_IDC_IDR_II_5 0x10000020 /* IntrInfo[5]: Request EOI */ #define PCI_HT_IDC_IDR_II_6 0x00ffffc0 /* IntrInfo[23:6] */ #define PCI_HT_IDC_IDR_II_24 0xff000000 /* IntrInfo[31:24] */ /* High part (at index + 1) */ #define PCI_HT_IDC_IDR_II_32 0x00ffffff /* IntrInfo[55:32] */ #define PCI_HT_IDC_IDR_PASSPW 0x40000000 /* PassPW setting for messages */ #define PCI_HT_IDC_IDR_WEOI 0x80000000 /* Waiting for EOI */ /* HyperTransport: Revision ID */ #define PCI_HT_RID_RID 2 /* Revision Register */ #define PCI_HT_RID_SIZEOF 4 /* HyperTransport: UnitID Clumping */ #define PCI_HT_UIDC_CS 4 /* Clumping Support Register */ #define PCI_HT_UIDC_CE 8 /* Clumping Enable Register */ #define PCI_HT_UIDC_SIZEOF 12 /* HyperTransport: Extended Configuration Space Access */ #define PCI_HT_ECSA_ADDR 4 /* Configuration Address Register */ #define PCI_HT_ECSA_ADDR_REG 0x00000ffc /* Register */ #define PCI_HT_ECSA_ADDR_FUN 0x00007000 /* Function */ #define PCI_HT_ECSA_ADDR_DEV 0x000f1000 /* Device */ #define PCI_HT_ECSA_ADDR_BUS 0x0ff00000 /* Bus Number */ #define PCI_HT_ECSA_ADDR_TYPE 0x10000000 /* Access Type */ #define PCI_HT_ECSA_DATA 8 /* Configuration Data Register */ #define PCI_HT_ECSA_SIZEOF 12 /* HyperTransport: Address Mapping */ #define PCI_HT_AM_CMD 2 /* Command Register */ #define PCI_HT_AM_CMD_NDMA 0x000f /* Number of DMA Mappings */ #define PCI_HT_AM_CMD_IOSIZ 0x01f0 /* I/O Size */ #define PCI_HT_AM_CMD_MT 0x0600 /* Map Type */ #define PCI_HT_AM_CMD_MT_40B 0x0000 /* 40-bit */ #define PCI_HT_AM_CMD_MT_64B 0x0200 /* 64-bit */ /* Window Control Register bits */ #define PCI_HT_AM_SBW_CTR_COMP 0x1 /* Compat */ #define PCI_HT_AM_SBW_CTR_NCOH 0x2 /* NonCoherent */ #define PCI_HT_AM_SBW_CTR_ISOC 0x4 /* Isochronous */ #define PCI_HT_AM_SBW_CTR_EN 0x8 /* Enable */ /* HyperTransport: 40-bit Address Mapping */ #define PCI_HT_AM40_SBNPW 4 /* Secondary Bus Non-Prefetchable Window Register */ #define PCI_HT_AM40_SBW_BASE 0x000fffff /* Window Base */ #define PCI_HT_AM40_SBW_CTR 0xf0000000 /* Window Control */ #define PCI_HT_AM40_SBPW 8 /* Secondary Bus Prefetchable Window Register */ #define PCI_HT_AM40_DMA_PBASE0 12 /* DMA Window Primary Base 0 Register */ #define PCI_HT_AM40_DMA_CTR0 15 /* DMA Window Control 0 Register */ #define PCI_HT_AM40_DMA_CTR_CTR 0xf0 /* Window Control */ #define PCI_HT_AM40_DMA_SLIM0 16 /* DMA Window Secondary Limit 0 Register */ #define PCI_HT_AM40_DMA_SBASE0 18 /* DMA Window Secondary Base 0 Register */ #define PCI_HT_AM40_SIZEOF 12 /* size is variable: 12 + 8 * NDMA */ /* HyperTransport: 64-bit Address Mapping */ #define PCI_HT_AM64_IDX 4 /* Index Register */ #define PCI_HT_AM64_DATA_LO 8 /* Data Lower Register */ #define PCI_HT_AM64_DATA_HI 12 /* Data Upper Register */ #define PCI_HT_AM64_SIZEOF 16 /* Register indices */ #define PCI_HT_AM64_IDX_SBNPW 0x00 /* Secondary Bus Non-Prefetchable Window Register */ #define PCI_HT_AM64_W_BASE_LO 0xfff00000 /* Window Base Lower */ #define PCI_HT_AM64_W_CTR 0x0000000f /* Window Control */ #define PCI_HT_AM64_IDX_SBPW 0x01 /* Secondary Bus Prefetchable Window Register */ #define PCI_HT_AM64_IDX_PBNPW 0x02 /* Primary Bus Non-Prefetchable Window Register */ #define PCI_HT_AM64_IDX_DMAPB0 0x04 /* DMA Window Primary Base 0 Register */ #define PCI_HT_AM64_IDX_DMASB0 0x05 /* DMA Window Secondary Base 0 Register */ #define PCI_HT_AM64_IDX_DMASL0 0x06 /* DMA Window Secondary Limit 0 Register */ /* HyperTransport: MSI Mapping */ #define PCI_HT_MSIM_CMD 2 /* Command Register */ #define PCI_HT_MSIM_CMD_EN 0x0001 /* Mapping Active */ #define PCI_HT_MSIM_CMD_FIXD 0x0002 /* MSI Mapping Address Fixed */ #define PCI_HT_MSIM_ADDR_LO 4 /* MSI Mapping Address Lower Register */ #define PCI_HT_MSIM_ADDR_HI 8 /* MSI Mapping Address Upper Register */ #define PCI_HT_MSIM_SIZEOF 12 /* HyperTransport: DirectRoute */ #define PCI_HT_DR_CMD 2 /* Command Register */ #define PCI_HT_DR_CMD_NDRS 0x000f /* Number of DirectRoute Spaces */ #define PCI_HT_DR_CMD_IDX 0x01f0 /* Index */ #define PCI_HT_DR_EN 4 /* Enable Vector Register */ #define PCI_HT_DR_DATA 8 /* Data Register */ #define PCI_HT_DR_SIZEOF 12 /* Register indices */ #define PCI_HT_DR_IDX_BASE_LO 0x00 /* DirectRoute Base Lower Register */ #define PCI_HT_DR_OTNRD 0x00000001 /* Opposite to Normal Request Direction */ #define PCI_HT_DR_BL_LO 0xffffff00 /* Base/Limit Lower */ #define PCI_HT_DR_IDX_BASE_HI 0x01 /* DirectRoute Base Upper Register */ #define PCI_HT_DR_IDX_LIMIT_LO 0x02 /* DirectRoute Limit Lower Register */ #define PCI_HT_DR_IDX_LIMIT_HI 0x03 /* DirectRoute Limit Upper Register */ /* HyperTransport: VCSet */ #define PCI_HT_VCS_SUP 4 /* VCSets Supported Register */ #define PCI_HT_VCS_L1EN 5 /* Link 1 VCSets Enabled Register */ #define PCI_HT_VCS_L0EN 6 /* Link 0 VCSets Enabled Register */ #define PCI_HT_VCS_SBD 8 /* Stream Bucket Depth Register */ #define PCI_HT_VCS_SINT 9 /* Stream Interval Register */ #define PCI_HT_VCS_SSUP 10 /* Number of Streaming VCs Supported Register */ #define PCI_HT_VCS_SSUP_0 0x00 /* Streaming VC 0 */ #define PCI_HT_VCS_SSUP_3 0x01 /* Streaming VCs 0-3 */ #define PCI_HT_VCS_SSUP_15 0x02 /* Streaming VCs 0-15 */ #define PCI_HT_VCS_NFCBD 12 /* Non-FC Bucket Depth Register */ #define PCI_HT_VCS_NFCINT 13 /* Non-FC Bucket Interval Register */ #define PCI_HT_VCS_SIZEOF 16 /* HyperTransport: Retry Mode */ #define PCI_HT_RM_CTR0 4 /* Control 0 Register */ #define PCI_HT_RM_CTR_LRETEN 0x01 /* Link Retry Enable */ #define PCI_HT_RM_CTR_FSER 0x02 /* Force Single Error */ #define PCI_HT_RM_CTR_ROLNEN 0x04 /* Rollover Nonfatal Enable */ #define PCI_HT_RM_CTR_FSS 0x08 /* Force Single Stomp */ #define PCI_HT_RM_CTR_RETNEN 0x10 /* Retry Nonfatal Enable */ #define PCI_HT_RM_CTR_RETFEN 0x20 /* Retry Fatal Enable */ #define PCI_HT_RM_CTR_AA 0xc0 /* Allowed Attempts */ #define PCI_HT_RM_STS0 5 /* Status 0 Register */ #define PCI_HT_RM_STS_RETSNT 0x01 /* Retry Sent */ #define PCI_HT_RM_STS_CNTROL 0x02 /* Count Rollover */ #define PCI_HT_RM_STS_SRCV 0x04 /* Stomp Received */ #define PCI_HT_RM_CTR1 6 /* Control 1 Register */ #define PCI_HT_RM_STS1 7 /* Status 1 Register */ #define PCI_HT_RM_CNT0 8 /* Retry Count 0 Register */ #define PCI_HT_RM_CNT1 10 /* Retry Count 1 Register */ #define PCI_HT_RM_SIZEOF 12 /* Vendor-Specific Capability (see PCI_EVNDR_xxx for the PCIe version) */ #define PCI_VNDR_LENGTH 2 /* Length byte */ /* PCI Express */ #define PCI_EXP_FLAGS 0x2 /* Capabilities register */ #define PCI_EXP_FLAGS_VERS 0x000f /* Capability version */ #define PCI_EXP_FLAGS_TYPE 0x00f0 /* Device/Port type */ #define PCI_EXP_TYPE_ENDPOINT 0x0 /* Express Endpoint */ #define PCI_EXP_TYPE_LEG_END 0x1 /* Legacy Endpoint */ #define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */ #define PCI_EXP_TYPE_UPSTREAM 0x5 /* Upstream Port */ #define PCI_EXP_TYPE_DOWNSTREAM 0x6 /* Downstream Port */ #define PCI_EXP_TYPE_PCI_BRIDGE 0x7 /* PCIe to PCI/PCI-X Bridge */ #define PCI_EXP_TYPE_PCIE_BRIDGE 0x8 /* PCI/PCI-X to PCIe Bridge */ #define PCI_EXP_TYPE_ROOT_INT_EP 0x9 /* Root Complex Integrated Endpoint */ #define PCI_EXP_TYPE_ROOT_EC 0xa /* Root Complex Event Collector */ #define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */ #define PCI_EXP_FLAGS_IRQ 0x3e00 /* Interrupt message number */ #define PCI_EXP_DEVCAP 0x4 /* Device capabilities */ #define PCI_EXP_DEVCAP_PAYLOAD 0x07 /* Max_Payload_Size */ #define PCI_EXP_DEVCAP_PHANTOM 0x18 /* Phantom functions */ #define PCI_EXP_DEVCAP_EXT_TAG 0x20 /* Extended tags */ #define PCI_EXP_DEVCAP_L0S 0x1c0 /* L0s Acceptable Latency */ #define PCI_EXP_DEVCAP_L1 0xe00 /* L1 Acceptable Latency */ #define PCI_EXP_DEVCAP_ATN_BUT 0x1000 /* Attention Button Present */ #define PCI_EXP_DEVCAP_ATN_IND 0x2000 /* Attention Indicator Present */ #define PCI_EXP_DEVCAP_PWR_IND 0x4000 /* Power Indicator Present */ #define PCI_EXP_DEVCAP_RBE 0x8000 /* Role-Based Error Reporting */ #define PCI_EXP_DEVCAP_PWR_VAL 0x3fc0000 /* Slot Power Limit Value */ #define PCI_EXP_DEVCAP_PWR_SCL 0xc000000 /* Slot Power Limit Scale */ #define PCI_EXP_DEVCAP_FLRESET 0x10000000 /* Function-Level Reset */ #define PCI_EXP_DEVCAP_TEE_IO 0x40000000 /* TEE-IO Supported (TDISP) */ #define PCI_EXP_DEVCTL 0x8 /* Device Control */ #define PCI_EXP_DEVCTL_CERE 0x0001 /* Correctable Error Reporting En. */ #define PCI_EXP_DEVCTL_NFERE 0x0002 /* Non-Fatal Error Reporting Enable */ #define PCI_EXP_DEVCTL_FERE 0x0004 /* Fatal Error Reporting Enable */ #define PCI_EXP_DEVCTL_URRE 0x0008 /* Unsupported Request Reporting En. */ #define PCI_EXP_DEVCTL_RELAXED 0x0010 /* Enable Relaxed Ordering */ #define PCI_EXP_DEVCTL_PAYLOAD 0x00e0 /* Max_Payload_Size */ #define PCI_EXP_DEVCTL_EXT_TAG 0x0100 /* Extended Tag Field Enable */ #define PCI_EXP_DEVCTL_PHANTOM 0x0200 /* Phantom Functions Enable */ #define PCI_EXP_DEVCTL_AUX_PME 0x0400 /* Auxiliary Power PM Enable */ #define PCI_EXP_DEVCTL_NOSNOOP 0x0800 /* Enable No Snoop */ #define PCI_EXP_DEVCTL_READRQ 0x7000 /* Max_Read_Request_Size */ #define PCI_EXP_DEVCTL_BCRE 0x8000 /* Bridge Configuration Retry Enable */ #define PCI_EXP_DEVCTL_FLRESET 0x8000 /* Function-Level Reset [bit shared with BCRE] */ #define PCI_EXP_DEVSTA 0xa /* Device Status */ #define PCI_EXP_DEVSTA_CED 0x01 /* Correctable Error Detected */ #define PCI_EXP_DEVSTA_NFED 0x02 /* Non-Fatal Error Detected */ #define PCI_EXP_DEVSTA_FED 0x04 /* Fatal Error Detected */ #define PCI_EXP_DEVSTA_URD 0x08 /* Unsupported Request Detected */ #define PCI_EXP_DEVSTA_AUXPD 0x10 /* AUX Power Detected */ #define PCI_EXP_DEVSTA_TRPND 0x20 /* Transactions Pending */ #define PCI_EXP_LNKCAP 0xc /* Link Capabilities */ #define PCI_EXP_LNKCAP_SPEED 0x0000f /* Maximum Link Speed */ #define PCI_EXP_LNKCAP_WIDTH 0x003f0 /* Maximum Link Width */ #define PCI_EXP_LNKCAP_ASPM 0x00c00 /* Active State Power Management */ #define PCI_EXP_LNKCAP_L0S 0x07000 /* L0s Exit Latency */ #define PCI_EXP_LNKCAP_L1 0x38000 /* L1 Exit Latency */ #define PCI_EXP_LNKCAP_CLOCKPM 0x40000 /* Clock Power Management */ #define PCI_EXP_LNKCAP_SURPRISE 0x80000 /* Surprise Down Error Reporting */ #define PCI_EXP_LNKCAP_DLLA 0x100000 /* Data Link Layer Active Reporting */ #define PCI_EXP_LNKCAP_LBNC 0x200000 /* Link Bandwidth Notification Capability */ #define PCI_EXP_LNKCAP_AOC 0x400000 /* ASPM Optionality Compliance */ #define PCI_EXP_LNKCAP_PORT 0xff000000 /* Port Number */ #define PCI_EXP_LNKCTL 0x10 /* Link Control */ #define PCI_EXP_LNKCTL_ASPM 0x0003 /* ASPM Control */ #define PCI_EXP_LNKCTL_RCB 0x0008 /* Read Completion Boundary */ #define PCI_EXP_LNKCTL_DISABLE 0x0010 /* Link Disable */ #define PCI_EXP_LNKCTL_RETRAIN 0x0020 /* Retrain Link */ #define PCI_EXP_LNKCTL_CLOCK 0x0040 /* Common Clock Configuration */ #define PCI_EXP_LNKCTL_XSYNCH 0x0080 /* Extended Synch */ #define PCI_EXP_LNKCTL_CLOCKPM 0x0100 /* Clock Power Management */ #define PCI_EXP_LNKCTL_HWAUTWD 0x0200 /* Hardware Autonomous Width Disable */ #define PCI_EXP_LNKCTL_BWMIE 0x0400 /* Bandwidth Mgmt Interrupt Enable */ #define PCI_EXP_LNKCTL_AUTBWIE 0x0800 /* Autonomous Bandwidth Mgmt Interrupt Enable */ #define PCI_EXP_LNKSTA 0x12 /* Link Status */ #define PCI_EXP_LNKSTA_SPEED 0x000f /* Negotiated Link Speed */ #define PCI_EXP_LNKSTA_WIDTH 0x03f0 /* Negotiated Link Width */ #define PCI_EXP_LNKSTA_TR_ERR 0x0400 /* Training Error (obsolete) */ #define PCI_EXP_LNKSTA_TRAIN 0x0800 /* Link Training */ #define PCI_EXP_LNKSTA_SL_CLK 0x1000 /* Slot Clock Configuration */ #define PCI_EXP_LNKSTA_DL_ACT 0x2000 /* Data Link Layer in DL_Active State */ #define PCI_EXP_LNKSTA_BWMGMT 0x4000 /* Bandwidth Mgmt Status */ #define PCI_EXP_LNKSTA_AUTBW 0x8000 /* Autonomous Bandwidth Mgmt Status */ #define PCI_EXP_SLTCAP 0x14 /* Slot Capabilities */ #define PCI_EXP_SLTCAP_ATNB 0x0001 /* Attention Button Present */ #define PCI_EXP_SLTCAP_PWRC 0x0002 /* Power Controller Present */ #define PCI_EXP_SLTCAP_MRL 0x0004 /* MRL Sensor Present */ #define PCI_EXP_SLTCAP_ATNI 0x0008 /* Attention Indicator Present */ #define PCI_EXP_SLTCAP_PWRI 0x0010 /* Power Indicator Present */ #define PCI_EXP_SLTCAP_HPS 0x0020 /* Hot-Plug Surprise */ #define PCI_EXP_SLTCAP_HPC 0x0040 /* Hot-Plug Capable */ #define PCI_EXP_SLTCAP_PWR_VAL 0x00007f80 /* Slot Power Limit Value */ #define PCI_EXP_SLTCAP_PWR_SCL 0x00018000 /* Slot Power Limit Scale */ #define PCI_EXP_SLTCAP_INTERLOCK 0x020000 /* Electromechanical Interlock Present */ #define PCI_EXP_SLTCAP_NOCMDCOMP 0x040000 /* No Command Completed Support */ #define PCI_EXP_SLTCAP_PSN 0xfff80000 /* Physical Slot Number */ #define PCI_EXP_SLTCTL 0x18 /* Slot Control */ #define PCI_EXP_SLTCTL_ATNB 0x0001 /* Attention Button Pressed Enable */ #define PCI_EXP_SLTCTL_PWRF 0x0002 /* Power Fault Detected Enable */ #define PCI_EXP_SLTCTL_MRLS 0x0004 /* MRL Sensor Changed Enable */ #define PCI_EXP_SLTCTL_PRSD 0x0008 /* Presence Detect Changed Enable */ #define PCI_EXP_SLTCTL_CMDC 0x0010 /* Command Completed Interrupt Enable */ #define PCI_EXP_SLTCTL_HPIE 0x0020 /* Hot-Plug Interrupt Enable */ #define PCI_EXP_SLTCTL_ATNI 0x00c0 /* Attention Indicator Control */ #define PCI_EXP_SLTCTL_PWRI 0x0300 /* Power Indicator Control */ #define PCI_EXP_SLTCTL_PWRC 0x0400 /* Power Controller Control */ #define PCI_EXP_SLTCTL_INTERLOCK 0x0800 /* Electromechanical Interlock Control */ #define PCI_EXP_SLTCTL_LLCHG 0x1000 /* Data Link Layer State Changed Enable */ #define PCI_EXP_SLTSTA 0x1a /* Slot Status */ #define PCI_EXP_SLTSTA_ATNB 0x0001 /* Attention Button Pressed */ #define PCI_EXP_SLTSTA_PWRF 0x0002 /* Power Fault Detected */ #define PCI_EXP_SLTSTA_MRLS 0x0004 /* MRL Sensor Changed */ #define PCI_EXP_SLTSTA_PRSD 0x0008 /* Presence Detect Changed */ #define PCI_EXP_SLTSTA_CMDC 0x0010 /* Command Completed */ #define PCI_EXP_SLTSTA_MRL_ST 0x0020 /* MRL Sensor State */ #define PCI_EXP_SLTSTA_PRES 0x0040 /* Presence Detect State */ #define PCI_EXP_SLTSTA_INTERLOCK 0x0080 /* Electromechanical Interlock Status */ #define PCI_EXP_SLTSTA_LLCHG 0x0100 /* Data Link Layer State Changed */ #define PCI_EXP_RTCTL 0x1c /* Root Control */ #define PCI_EXP_RTCTL_SECEE 0x0001 /* System Error on Correctable Error */ #define PCI_EXP_RTCTL_SENFEE 0x0002 /* System Error on Non-Fatal Error */ #define PCI_EXP_RTCTL_SEFEE 0x0004 /* System Error on Fatal Error */ #define PCI_EXP_RTCTL_PMEIE 0x0008 /* PME Interrupt Enable */ #define PCI_EXP_RTCTL_CRSVIS 0x0010 /* Configuration Request Retry Status Visible to SW */ #define PCI_EXP_RTCAP 0x1e /* Root Capabilities */ #define PCI_EXP_RTCAP_CRSVIS 0x0001 /* Configuration Request Retry Status Visible to SW */ #define PCI_EXP_RTSTA 0x20 /* Root Status */ #define PCI_EXP_RTSTA_PME_REQID 0x0000ffff /* PME Requester ID */ #define PCI_EXP_RTSTA_PME_STATUS 0x00010000 /* PME Status */ #define PCI_EXP_RTSTA_PME_PENDING 0x00020000 /* PME is Pending */ #define PCI_EXP_DEVCAP2 0x24 /* Device capabilities 2 */ #define PCI_EXP_DEVCAP2_TIMEOUT_RANGE(x) ((x) & 0xf) /* Completion Timeout Ranges Supported */ #define PCI_EXP_DEVCAP2_TIMEOUT_DIS 0x0010 /* Completion Timeout Disable Supported */ #define PCI_EXP_DEVCAP2_ARI 0x0020 /* ARI Forwarding Supported */ #define PCI_EXP_DEVCAP2_ATOMICOP_ROUTING 0x0040 /* AtomicOp Routing Supported */ #define PCI_EXP_DEVCAP2_32BIT_ATOMICOP_COMP 0x0080 /* 32bit AtomicOp Completer Supported */ #define PCI_EXP_DEVCAP2_64BIT_ATOMICOP_COMP 0x0100 /* 64bit AtomicOp Completer Supported */ #define PCI_EXP_DEVCAP2_128BIT_CAS_COMP 0x0200 /* 128bit CAS Completer Supported */ #define PCI_EXP_DEVCAP2_NROPRPRP 0x0400 /* No RO-enabled PR-PR Passing */ #define PCI_EXP_DEVCAP2_LTR 0x0800 /* LTR supported */ #define PCI_EXP_DEVCAP2_TPH_COMP(x) (((x) >> 12) & 3) /* TPH Completer Supported */ #define PCI_EXP_DEVCAP2_LN_CLS(x) (((x) >> 14) & 3) /* LN System CLS Supported */ #define PCI_EXP_DEVCAP2_10BIT_TAG_COMP 0x00010000 /* 10 Bit Tag Completer */ #define PCI_EXP_DEVCAP2_10BIT_TAG_REQ 0x00020000 /* 10 Bit Tag Requester */ #define PCI_EXP_DEVCAP2_OBFF(x) (((x) >> 18) & 3) /* OBFF supported */ #define PCI_EXP_DEVCAP2_EXTFMT 0x00100000 /* Extended Fmt Field Supported */ #define PCI_EXP_DEVCAP2_EE_TLP 0x00200000 /* End-End TLP Prefix Supported */ #define PCI_EXP_DEVCAP2_MEE_TLP(x) (((x) >> 22) & 3) /* Max End-End TLP Prefixes */ #define PCI_EXP_DEVCAP2_EPR(x) (((x) >> 24) & 3) /* Emergency Power Reduction Supported */ #define PCI_EXP_DEVCAP2_EPR_INIT 0x04000000 /* Emergency Power Reduction Initialization Required */ #define PCI_EXP_DEVCAP2_FRS 0x80000000 /* FRS supported */ #define PCI_EXP_DEVCTL2 0x28 /* Device Control */ #define PCI_EXP_DEVCTL2_TIMEOUT_VALUE(x) ((x) & 0xf) /* Completion Timeout Value */ #define PCI_EXP_DEVCTL2_TIMEOUT_DIS 0x0010 /* Completion Timeout Disable */ #define PCI_EXP_DEVCTL2_ARI 0x0020 /* ARI Forwarding */ #define PCI_EXP_DEVCTL2_ATOMICOP_REQUESTER_EN 0x0040 /* AtomicOp RequesterEnable */ #define PCI_EXP_DEVCTL2_ATOMICOP_EGRESS_BLOCK 0x0080 /* AtomicOp Egress Blocking */ #define PCI_EXP_DEVCTL2_IDO_REQ_EN 0x0100 /* Allow IDO for requests */ #define PCI_EXP_DEVCTL2_IDO_CMP_EN 0x0200 /* Allow IDO for completions */ #define PCI_EXP_DEVCTL2_LTR 0x0400 /* LTR enabled */ #define PCI_EXP_DEVCTL2_EPR_REQ 0x0800 /* Emergency Power Reduction Request */ #define PCI_EXP_DEVCTL2_10BIT_TAG_REQ 0x1000 /* 10 Bit Tag Requester enabled */ #define PCI_EXP_DEVCTL2_OBFF(x) (((x) >> 13) & 3) /* OBFF enabled */ #define PCI_EXP_DEVCTL2_EE_TLP_BLK 0x8000 /* End-End TLP Prefix Blocking */ #define PCI_EXP_DEVSTA2 0x2a /* Device Status */ #define PCI_EXP_LNKCAP2 0x2c /* Link Capabilities */ #define PCI_EXP_LNKCAP2_SPEED(x) (((x) >> 1) & 0x7f) #define PCI_EXP_LNKCAP2_CROSSLINK 0x00000100 /* Crosslink Supported */ #define PCI_EXP_LNKCAP2_RETIMER 0x00800000 /* Retimer Supported */ #define PCI_EXP_LNKCAP2_2RETIMERS 0x01000000 /* 2 Retimers Supported */ #define PCI_EXP_LNKCAP2_DRS 0x80000000 /* Device Readiness Status */ #define PCI_EXP_LNKCTL2 0x30 /* Link Control */ #define PCI_EXP_LNKCTL2_SPEED(x) ((x) & 0xf) /* Target Link Speed */ #define PCI_EXP_LNKCTL2_CMPLNC 0x0010 /* Enter Compliance */ #define PCI_EXP_LNKCTL2_SPEED_DIS 0x0020 /* Hardware Autonomous Speed Disable */ #define PCI_EXP_LNKCTL2_DEEMPHASIS(x) (((x) >> 6) & 1) /* Selectable De-emphasis */ #define PCI_EXP_LNKCTL2_MARGIN(x) (((x) >> 7) & 7) /* Transmit Margin */ #define PCI_EXP_LNKCTL2_MOD_CMPLNC 0x0400 /* Enter Modified Compliance */ #define PCI_EXP_LNKCTL2_CMPLNC_SOS 0x0800 /* Compliance SOS */ #define PCI_EXP_LNKCTL2_COM_DEEMPHASIS(x) (((x) >> 12) & 0xf) /* Compliance Preset/De-emphasis */ #define PCI_EXP_LNKSTA2 0x32 /* Link Status */ #define PCI_EXP_LINKSTA2_DEEMPHASIS(x) ((x) & 1) /* Current De-emphasis Level */ #define PCI_EXP_LINKSTA2_EQU_COMP 0x02 /* Equalization Complete */ #define PCI_EXP_LINKSTA2_EQU_PHASE1 0x04 /* Equalization Phase 1 Successful */ #define PCI_EXP_LINKSTA2_EQU_PHASE2 0x08 /* Equalization Phase 2 Successful */ #define PCI_EXP_LINKSTA2_EQU_PHASE3 0x10 /* Equalization Phase 3 Successful */ #define PCI_EXP_LINKSTA2_EQU_REQ 0x20 /* Link Equalization Request */ #define PCI_EXP_LINKSTA2_RETIMER 0x0040 /* Retimer Detected */ #define PCI_EXP_LINKSTA2_2RETIMERS 0x0080 /* 2 Retimers Detected */ #define PCI_EXP_LINKSTA2_CROSSLINK(x) (((x) >> 8) & 0x3) /* Crosslink Res */ #define PCI_EXP_LINKSTA2_COMPONENT(x) (((x) >> 12) & 0x7) /* Presence */ #define PCI_EXP_LINKSTA2_DRS_RCVD 0x8000 /* DRS Msg Received */ #define PCI_EXP_SLTCAP2 0x34 /* Slot Capabilities */ #define PCI_EXP_SLTCTL2 0x38 /* Slot Control */ #define PCI_EXP_SLTSTA2 0x3a /* Slot Status */ /* MSI-X */ #define PCI_MSIX_ENABLE 0x8000 #define PCI_MSIX_MASK 0x4000 #define PCI_MSIX_TABSIZE 0x07ff #define PCI_MSIX_TABLE 4 #define PCI_MSIX_PBA 8 #define PCI_MSIX_BIR 0x7 /* Subsystem vendor/device ID for PCI bridges */ #define PCI_SSVID_VENDOR 4 #define PCI_SSVID_DEVICE 6 /* PCI Advanced Features */ #define PCI_AF_CAP 3 #define PCI_AF_CAP_TP 0x01 #define PCI_AF_CAP_FLR 0x02 #define PCI_AF_CTRL 4 #define PCI_AF_CTRL_FLR 0x01 #define PCI_AF_STATUS 5 #define PCI_AF_STATUS_TP 0x01 /* SATA Host Bus Adapter */ #define PCI_SATA_HBA_BARS 4 #define PCI_SATA_HBA_REG0 8 /* Enhanced Allocation (EA) */ #define PCI_EA_CAP_TYPE1_SECONDARY 4 #define PCI_EA_CAP_TYPE1_SUBORDINATE 5 /* EA Entry header */ #define PCI_EA_CAP_ENT_WRITABLE 0x40000000 /* Writable: 1 = RW, 0 = HwInit */ #define PCI_EA_CAP_ENT_ENABLE 0x80000000 /* Enable for this entry */ /*** Definitions of extended capabilities ***/ /* Advanced Error Reporting */ #define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */ #define PCI_ERR_UNC_TRAIN 0x00000001 /* Undefined in PCIe rev1.1 & 2.0 spec */ #define PCI_ERR_UNC_DLP 0x00000010 /* Data Link Protocol */ #define PCI_ERR_UNC_SDES 0x00000020 /* Surprise Down Error */ #define PCI_ERR_UNC_POISON_TLP 0x00001000 /* Poisoned TLP */ #define PCI_ERR_UNC_FCP 0x00002000 /* Flow Control Protocol */ #define PCI_ERR_UNC_COMP_TIME 0x00004000 /* Completion Timeout */ #define PCI_ERR_UNC_COMP_ABORT 0x00008000 /* Completer Abort */ #define PCI_ERR_UNC_UNX_COMP 0x00010000 /* Unexpected Completion */ #define PCI_ERR_UNC_RX_OVER 0x00020000 /* Receiver Overflow */ #define PCI_ERR_UNC_MALF_TLP 0x00040000 /* Malformed TLP */ #define PCI_ERR_UNC_ECRC 0x00080000 /* ECRC Error Status */ #define PCI_ERR_UNC_UNSUP 0x00100000 /* Unsupported Request */ #define PCI_ERR_UNC_ACS_VIOL 0x00200000 /* ACS Violation */ #define PCI_ERR_UNC_INTERNAL 0x00400000 /* Uncorrectable Internal Error */ #define PCI_ERR_UNC_MC_BLOCKED_TLP 0x00800000 /* MC Blocked TLP */ #define PCI_ERR_UNC_ATOMICOP_EGRESS_BLOCKED 0x01000000 /* AtomicOp Egress Blocked */ #define PCI_ERR_UNC_TLP_PREFIX_BLOCKED 0x02000000 /* TLP Prefix Blocked Error */ #define PCI_ERR_UNC_POISONED_TLP_EGRESS 0x04000000 /* Poisoned TLP Egress Blocked */ #define PCI_ERR_UNC_DMWR_REQ_EGRESS_BLOCKED 0x08000000 /* DMWr Request Egress Blocked */ #define PCI_ERR_UNC_IDE_CHECK 0x10000000 /* IDE Check Failed */ #define PCI_ERR_UNC_MISR_IDE_TLP 0x20000000 /* Misrouted IDE TLP */ #define PCI_ERR_UNC_PCRC_CHECK 0x40000000 /* PCRC Check Failed */ #define PCI_ERR_UNC_TLP_XLAT_EGRESS_BLOCKED 0x80000000 /* TLP Translation Egress Blocked */ #define PCI_ERR_UNCOR_MASK 8 /* Uncorrectable Error Mask */ /* Same bits as above */ #define PCI_ERR_UNCOR_SEVER 12 /* Uncorrectable Error Severity */ /* Same bits as above */ #define PCI_ERR_COR_STATUS 16 /* Correctable Error Status */ #define PCI_ERR_COR_RCVR 0x00000001 /* Receiver Error Status */ #define PCI_ERR_COR_BAD_TLP 0x00000040 /* Bad TLP Status */ #define PCI_ERR_COR_BAD_DLLP 0x00000080 /* Bad DLLP Status */ #define PCI_ERR_COR_REP_ROLL 0x00000100 /* REPLAY_NUM Rollover */ #define PCI_ERR_COR_REP_TIMER 0x00001000 /* Replay Timer Timeout */ #define PCI_ERR_COR_REP_ANFE 0x00002000 /* Advisory Non-Fatal Error */ #define PCI_ERR_COR_INTERNAL 0x00004000 /* Corrected Internal Error */ #define PCI_ERR_COR_HDRLOG_OVER 0x00008000 /* Header Log Overflow */ #define PCI_ERR_COR_MASK 20 /* Correctable Error Mask */ /* Same bits as above */ #define PCI_ERR_CAP 24 /* Advanced Error Capabilities */ #define PCI_ERR_CAP_FEP(x) ((x) & 31) /* First Error Pointer */ #define PCI_ERR_CAP_ECRC_GENC 0x00000020 /* ECRC Generation Capable */ #define PCI_ERR_CAP_ECRC_GENE 0x00000040 /* ECRC Generation Enable */ #define PCI_ERR_CAP_ECRC_CHKC 0x00000080 /* ECRC Check Capable */ #define PCI_ERR_CAP_ECRC_CHKE 0x00000100 /* ECRC Check Enable */ #define PCI_ERR_CAP_MULT_HDRC 0x00000200 /* Multiple Header Capable */ #define PCI_ERR_CAP_MULT_HDRE 0x00000400 /* Multiple Header Enable */ #define PCI_ERR_CAP_TLP_PFX 0x00000800 /* TLP Prefix Log Present */ #define PCI_ERR_CAP_HDR_LOG 0x00001000 /* Completion Timeout Prefix/Header Log Capable */ #define PCI_ERR_HEADER_LOG 28 /* Header Log Register (16 bytes) */ #define PCI_ERR_ROOT_COMMAND 44 /* Root Error Command */ #define PCI_ERR_ROOT_CMD_COR_EN 0x00000001 /* Correctable Error Reporting Enable */ #define PCI_ERR_ROOT_CMD_NONFATAL_EN 0x00000002 /* Non-Fatal Error Reporting Enable*/ #define PCI_ERR_ROOT_CMD_FATAL_EN 0x00000004 /* Fatal Error Reporting Enable */ #define PCI_ERR_ROOT_STATUS 48 /* Root Error Status */ #define PCI_ERR_ROOT_COR_RCV 0x00000001 /* ERR_COR Received */ #define PCI_ERR_ROOT_MULTI_COR_RCV 0x00000002 /* Multiple ERR_COR Received */ #define PCI_ERR_ROOT_UNCOR_RCV 0x00000004 /* ERR_FATAL/NONFATAL Received */ #define PCI_ERR_ROOT_MULTI_UNCOR_RCV 0x00000008 /* Multiple ERR_FATAL/NONFATAL Received */ #define PCI_ERR_ROOT_FIRST_FATAL 0x00000010 /* First Uncorrectable Fatal */ #define PCI_ERR_ROOT_NONFATAL_RCV 0x00000020 /* Non-Fatal Error Messages Received */ #define PCI_ERR_ROOT_FATAL_RCV 0x00000040 /* Fatal Error Messages Received */ #define PCI_ERR_MSG_NUM(x) (((x) >> 27) & 0x1f) /* MSI/MSI-X vector */ #define PCI_ERR_ROOT_COR_SRC 52 #define PCI_ERR_ROOT_SRC 54 /* Virtual Channel */ #define PCI_VC_PORT_REG1 4 #define PCI_VC_PORT_REG2 8 #define PCI_VC_PORT_CTRL 12 #define PCI_VC_PORT_STATUS 14 #define PCI_VC_RES_CAP 16 #define PCI_VC_RES_CTRL 20 #define PCI_VC_RES_STATUS 26 /* Power Budgeting */ #define PCI_PWR_DSR 4 /* Data Select Register */ #define PCI_PWR_DATA 8 /* Data Register */ #define PCI_PWR_DATA_BASE(x) ((x) & 0xff) /* Base Power */ #define PCI_PWR_DATA_SCALE(x) (((x) >> 8) & 3) /* Data Scale */ #define PCI_PWR_DATA_PM_SUB(x) (((x) >> 10) & 7) /* PM Sub State */ #define PCI_PWR_DATA_PM_STATE(x) (((x) >> 13) & 3) /* PM State */ #define PCI_PWR_DATA_TYPE(x) (((x) >> 15) & 7) /* Type */ #define PCI_PWR_DATA_RAIL(x) (((x) >> 18) & 7) /* Power Rail */ #define PCI_PWR_CAP 12 /* Capability */ #define PCI_PWR_CAP_BUDGET(x) ((x) & 1) /* Included in system budget */ /* Root Complex Link */ #define PCI_RCLINK_ESD 4 /* Element Self Description */ #define PCI_RCLINK_LINK1 16 /* First Link Entry */ #define PCI_RCLINK_LINK_DESC 0 /* Link Entry: Description */ #define PCI_RCLINK_LINK_ADDR 8 /* Link Entry: Address (64-bit) */ #define PCI_RCLINK_LINK_SIZE 16 /* Link Entry: sizeof */ /* Root Complex Event Collector Endpoint Association */ #define PCI_RCEC_EP_CAP_VER(reg) (((reg) >> 16) & 0xf) #define PCI_RCEC_BUSN_REG_VER 0x02 /* as per PCIe sec 7.9.10.1 */ #define PCI_RCEC_RCIEP_BMAP 0x0004 /* as per PCIe sec 7.9.10.2 */ #define PCI_RCEC_BUSN_REG 0x0008 /* as per PCIe sec 7.9.10.3 */ /* PCIe Vendor-Specific Capability */ #define PCI_EVNDR_HEADER 4 /* Vendor-Specific Header */ #define PCI_EVNDR_REGISTERS 8 /* Vendor-Specific Registers */ /* PCIe Designated Vendor-Specific Capability */ #define PCI_DVSEC_HEADER1 4 /* Designated Vendor-Specific Header 1 */ #define PCI_DVSEC_HEADER2 8 /* Designated Vendor-Specific Header 2 */ #define PCI_DVSEC_VENDOR_ID_CXL 0x1e98 /* Designated Vendor-Specific Vendor ID for CXL */ #define PCI_DVSEC_ID_CXL 0 /* Designated Vendor-Specific ID for Intel CXL */ /* PCIe CXL Designated Vendor-Specific Capabilities for Devices: Control, Status */ #define PCI_CXL_DEV_LEN 0x38 /* CXL Device DVSEC Length for Rev1 */ #define PCI_CXL_DEV_LEN_REV2 0x3c /* ... for Rev2 */ #define PCI_CXL_DEV_CAP 0x0a /* CXL Capability Register */ #define PCI_CXL_DEV_CAP_CACHE 0x0001 /* CXL.cache Protocol Support */ #define PCI_CXL_DEV_CAP_IO 0x0002 /* CXL.io Protocol Support */ #define PCI_CXL_DEV_CAP_MEM 0x0004 /* CXL.mem Protocol Support */ #define PCI_CXL_DEV_CAP_MEM_HWINIT 0x0008 /* CXL.mem Initializes with HW/FW Support */ #define PCI_CXL_DEV_CAP_HDM_CNT(x) (((x) & (3 << 4)) >> 4) /* CXL Number of HDM ranges */ #define PCI_CXL_DEV_CAP_VIRAL 0x4000 /* CXL Viral Handling Support */ #define PCI_CXL_DEV_CTRL 0x0c /* CXL Control Register */ #define PCI_CXL_DEV_CTRL_CACHE 0x0001 /* CXL.cache Protocol Enable */ #define PCI_CXL_DEV_CTRL_IO 0x0002 /* CXL.io Protocol Enable */ #define PCI_CXL_DEV_CTRL_MEM 0x0004 /* CXL.mem Protocol Enable */ #define PCI_CXL_DEV_CTRL_CACHE_SF_COV(x) (((x) & (0x1f << 3)) >> 3) /* Snoop Filter Coverage */ #define PCI_CXL_DEV_CTRL_CACHE_SF_GRAN(x) (((x) & (0x7 << 8)) >> 8) /* Snoop Filter Granularity */ #define PCI_CXL_DEV_CTRL_CACHE_CLN 0x0800 /* CXL.cache Performance Hint on Clean Evictions */ #define PCI_CXL_DEV_CTRL_VIRAL 0x4000 /* CXL Viral Handling Enable */ #define PCI_CXL_DEV_STATUS 0x0e /* CXL Status Register */ #define PCI_CXL_DEV_STATUS_VIRAL 0x4000 /* CXL Viral Handling Status */ #define PCI_CXL_DEV_CTRL2 0x10 /* CXL Control Register 2 */ #define PCI_CXL_DEV_CTRL2_DISABLE_CACHING 0x0001 #define PCI_CXL_DEV_CTRL2_INIT_WB_INVAL 0x0002 #define PCI_CXL_DEV_CTRL2_INIT_CXL_RST 0x0003 #define PCI_CXL_DEV_CTRL2_INIT_CXL_RST_CLR_EN 0x0004 #define PCI_CXL_DEV_CTRL2_INIT_CXL_HDM_STATE_HOTRST 0x0005 #define PCI_CXL_DEV_STATUS2 0x12 #define PCI_CXL_DEV_STATUS_CACHE_INV 0x0001 #define PCI_CXL_DEV_STATUS_RC 0x0002 /* Device Reset Complete */ #define PCI_CXL_DEV_STATUS_RE 0x0004 /* Device Reset Error */ #define PCI_CXL_DEV_STATUS_PMC 0x8000 /* Power Management Init Complete */ #define PCI_CXL_DEV_CAP2 0x16 #define PCI_CXL_DEV_CAP2_CACHE_UNK 0x0000 /* Cache Size Isn't Reported */ #define PCI_CXL_DEV_CAP2_CACHE_64K 0x0001 /* Unit Size 64K */ #define PCI_CXL_DEV_CAP2_CACHE_1M 0x0002 /* Unit Size 1M */ #define PCI_CXL_DEV_RANGE1_SIZE_HI 0x18 #define PCI_CXL_DEV_RANGE1_SIZE_LO 0x1c #define PCI_CXL_RANGE_VALID 0x0001 #define PCI_CXL_RANGE_ACTIVE 0x0002 #define PCI_CXL_RANGE_TYPE(x) (((x) >> 2) & 0x7) #define PCI_CXL_RANGE_CLASS(x) (((x) >> 5) & 0x7) #define PCI_CXL_RANGE_INTERLEAVE(x) (((x) >> 8) & 0x1f) #define PCI_CXL_RANGE_TIMEOUT(x) (((x) >> 13) & 0x7) #define PCI_CXL_DEV_RANGE1_BASE_HI 0x20 #define PCI_CXL_DEV_RANGE1_BASE_LO 0x24 #define PCI_CXL_DEV_RANGE2_SIZE_HI 0x28 #define PCI_CXL_DEV_RANGE2_SIZE_LO 0x2c #define PCI_CXL_DEV_RANGE2_BASE_HI 0x30 #define PCI_CXL_DEV_RANGE2_BASE_LO 0x34 /* From Rev2 */ #define PCI_CXL_DEV_CAP3 0x38 #define PCI_CXL_DEV_CAP3_HDM_STATE_RST_COLD 0x0001 #define PCI_CXL_DEV_CAP3_HDM_STATE_RST_WARM 0x0002 #define PCI_CXL_DEV_CAP3_HDM_STATE_RST_HOT 0x0003 #define PCI_CXL_DEV_CAP3_HDM_STATE_RST_HOT_CFG 0x0004 /* PCIe CXL 2.0 Designated Vendor-Specific Capabilities for Ports */ #define PCI_CXL_PORT_EXT_LEN 0x28 /* CXL Extensions DVSEC for Ports Length */ #define PCI_CXL_PORT_EXT_STATUS 0x0a /* Port Extension Status */ #define PCI_CXL_PORT_PM_INIT_COMPLETE 0x1 /* Port Power Management Initialization Complete */ #define PCI_CXL_PORT_CTRL 0x0c /* Port Control Override */ #define PCI_CXL_PORT_UNMASK_SBR 0x0001 /* Unmask SBR */ #define PCI_CXL_PORT_UNMASK_LINK 0x0002 /* Unmask Link Disable */ #define PCI_CXL_PORT_ALT_MEMORY 0x0004 /* Alt Memory and ID Space Enable */ #define PCI_CXL_PORT_ALT_BME 0x0008 /* Alt BME */ #define PCI_CXL_PORT_VIRAL_EN 0x4000 /* Viral Enable */ #define PCI_CXL_PORT_ALT_BUS_BASE 0xe #define PCI_CXL_PORT_ALT_BUS_LIMIT 0xf #define PCI_CXL_PORT_ALT_MEM_BASE 0x10 #define PCI_CXL_PORT_ALT_MEM_LIMIT 0x12 /* PCIe CXL 2.0 Designated Vendor-Specific Capabilities for Register Locator */ #define PCI_CXL_RL_BLOCK1_LO 0x0c /* PCIe CXL Designated Vendor-Specific Capabilities for Global Persistent Flush */ #define PCI_CXL_GPF_DEV_LEN 0x10 #define PCI_CXL_GPF_DEV_PHASE2_DUR 0x0a /* GPF Phase 2 Duration Register */ #define PCI_CXL_GPF_DEV_PHASE2_POW 0x0c /* GPF Phase 2 Power Register */ #define PCI_CXL_GPF_DEV_1US 0x0 #define PCI_CXL_GPF_DEV_10US 0x1 #define PCI_CXL_GPF_DEV_100US 0x2 #define PCI_CXL_GPF_DEV_1MS 0x3 #define PCI_CXL_GPF_DEV_10MS 0x4 #define PCI_CXL_GPF_DEV_100MS 0x5 #define PCI_CXL_GPF_DEV_1S 0x6 #define PCI_CXL_GPF_DEV_10S 0x7 #define PCI_CXL_GPF_PORT_LEN 0x10 #define PCI_CXL_GPF_PORT_PHASE1_CTRL 0x0c /* GPF Phase 1 Control Register */ #define PCI_CXL_GPF_PORT_PHASE2_CTRL 0x0e /* GPF Phase 2 Control Register */ #define PCI_CXL_GPF_PORT_1US 0x0 #define PCI_CXL_GPF_PORT_10US 0x1 #define PCI_CXL_GPF_PORT_100US 0x2 #define PCI_CXL_GPF_PORT_1MS 0x3 #define PCI_CXL_GPF_PORT_10MS 0x4 #define PCI_CXL_GPF_PORT_100MS 0x5 #define PCI_CXL_GPF_PORT_1S 0x6 #define PCI_CXL_GPF_PORT_10S 0x7 /* PCIe CXL Designated Vendor-Specific Capabilities for Flex Bus Port */ #define PCI_CXL_FB_LEN 0x20 #define PCI_CXL_FB_PORT_CAP 0x0a /* CXL Flex Bus Port Capability Register */ #define PCI_CXL_FB_CAP_CACHE 0x0001 /* CXL.cache Capable */ #define PCI_CXL_FB_CAP_IO 0x0002 /* CXL.io Capable */ #define PCI_CXL_FB_CAP_MEM 0x0004 /* CXL.mem Capable */ #define PCI_CXL_FB_CAP_68B_FLIT 0x0020 /* CXL 68B Flit and VH Capable */ #define PCI_CXL_FB_CAP_MULT_LOG_DEV 0x0040 /* CXL Multi-Logical Device Capable */ #define PCI_CXL_FB_CAP_256B_FLIT 0x2000 /* CXL Latency Optimized 256B Flit Capable */ #define PCI_CXL_FB_CAP_PBR_FLIT 0x4000 /* CXL PBR Flit Capable */ #define PCI_CXL_FB_PORT_CTRL 0x0c /* CXL Flex Bus Port Control Register */ #define PCI_CXL_FB_CTRL_CACHE 0x0001 /* CXL.cache Enable */ #define PCI_CXL_FB_CTRL_IO 0x0002 /* CXL.io Enable */ #define PCI_CXL_FB_CTRL_MEM 0x0004 /* CXL.mem Enable */ #define PCI_CXL_FB_CTRL_SYNC_HDR_BYP 0x0008 /* CXL Sync Header Bypass Enable */ #define PCI_CXL_FB_CTRL_DRFT_BUF 0x0010 /* Drift Buffer Enable */ #define PCI_CXL_FB_CTRL_68B_FLIT 0x0020 /* CXL 68B Flit and VH Enable */ #define PCI_CXL_FB_CTRL_MULT_LOG_DEV 0x0040 /* CXL Multi Logical Device Enable */ #define PCI_CXL_FB_CTRL_RCD 0x0080 /* Disable RCD Training */ #define PCI_CXL_FB_CTRL_RETIMER1 0x0100 /* Retimer1 Present */ #define PCI_CXL_FB_CTRL_RETIMER2 0x0200 /* Retimer2 Present */ #define PCI_CXL_FB_CTRL_256B_FLIT 0x2000 /* CXL Latency Optimized 256B Flit Enable */ #define PCI_CXL_FB_CTRL_PBR_FLIT 0x4000 /* CXL PBR Flit Enable */ #define PCI_CXL_FB_PORT_STATUS 0x0e /* CXL Flex Bus Port Status Register */ #define PCI_CXL_FB_STAT_CACHE 0x0001 /* CXL.cache Enabled */ #define PCI_CXL_FB_STAT_IO 0x0002 /* CXL.io Enabled */ #define PCI_CXL_FB_STAT_MEM 0x0004 /* CXL.mem Enabled */ #define PCI_CXL_FB_STAT_SYNC_HDR_BYP 0x0008 /* CXL Sync Header Bypass Enabled */ #define PCI_CXL_FB_STAT_DRFT_BUF 0x0010 /* Drift Buffer Enabled */ #define PCI_CXL_FB_STAT_68B_FLIT 0x0020 /* CXL 68B Flit and VH Enabled */ #define PCI_CXL_FB_STAT_MULT_LOG_DEV 0x0040 /* CXL Multi Logical Device Enabled */ #define PCI_CXL_FB_STAT_256B_FLIT 0x2000 /* CXL Latency Optimized 256B Flit Enabled */ #define PCI_CXL_FB_STAT_PBR_FLIT 0x4000 /* CXL PBR Flit Enabled */ #define PCI_CXL_FB_MOD_TS_DATA 0x10 /* CXL Flex Bus Port Received Modified TS Data Phase1 Register */ #define PCI_CXL_FB_PORT_CAP2 0x14 /* CXL Flex Bus Port Capability2 Register */ #define PCI_CXL_FB_CAP2_NOP_HINT 0x01 /* NOP Hint Capable */ #define PCI_CXL_FB_PORT_CTRL2 0x18 /* CXL Flex Bus Port Control2 Register */ #define PCI_CXL_FB_CTRL2_NOP_HINT 0x01 /* NOP Hint Enable */ #define PCI_CXL_FB_PORT_STATUS2 0x1c /* CXL Flex Bus Port Status2 Register */ #define PCI_CXL_FB_NEXT_UNSUPPORTED 0x20 /* PCIe CXL Designated Vendor-Specific Capabilities for Multi-Logical Device */ #define PCI_CXL_MLD_LEN 0x10 #define PCI_CXL_MLD_NUM_LD 0xa #define PCI_CXL_MLD_MAX_LD 0x10 /* PCIe CXL Designated Vendor-Specific Capabilities for Non-CXL Function Map */ #define PCI_CXL_FUN_MAP_LEN 0x2c #define PCI_CXL_FUN_MAP_REG_0 0x0c #define PCI_CXL_FUN_MAP_REG_1 0x10 #define PCI_CXL_FUN_MAP_REG_2 0x14 #define PCI_CXL_FUN_MAP_REG_3 0x18 #define PCI_CXL_FUN_MAP_REG_4 0x1c #define PCI_CXL_FUN_MAP_REG_5 0x20 #define PCI_CXL_FUN_MAP_REG_6 0x24 #define PCI_CXL_FUN_MAP_REG_7 0x28 /* Access Control Services */ #define PCI_ACS_CAP 0x04 /* ACS Capability Register */ #define PCI_ACS_CAP_VALID 0x0001 /* ACS Source Validation */ #define PCI_ACS_CAP_BLOCK 0x0002 /* ACS Translation Blocking */ #define PCI_ACS_CAP_REQ_RED 0x0004 /* ACS P2P Request Redirect */ #define PCI_ACS_CAP_CMPLT_RED 0x0008 /* ACS P2P Completion Redirect */ #define PCI_ACS_CAP_FORWARD 0x0010 /* ACS Upstream Forwarding */ #define PCI_ACS_CAP_EGRESS 0x0020 /* ACS P2P Egress Control */ #define PCI_ACS_CAP_TRANS 0x0040 /* ACS Direct Translated P2P */ #define PCI_ACS_CAP_VECTOR(x) (((x) >> 8) & 0xff) /* Egress Control Vector Size */ #define PCI_ACS_CTRL 0x06 /* ACS Control Register */ #define PCI_ACS_CTRL_VALID 0x0001 /* ACS Source Validation Enable */ #define PCI_ACS_CTRL_BLOCK 0x0002 /* ACS Translation Blocking Enable */ #define PCI_ACS_CTRL_REQ_RED 0x0004 /* ACS P2P Request Redirect Enable */ #define PCI_ACS_CTRL_CMPLT_RED 0x0008 /* ACS P2P Completion Redirect Enable */ #define PCI_ACS_CTRL_FORWARD 0x0010 /* ACS Upstream Forwarding Enable */ #define PCI_ACS_CTRL_EGRESS 0x0020 /* ACS P2P Egress Control Enable */ #define PCI_ACS_CTRL_TRANS 0x0040 /* ACS Direct Translated P2P Enable */ #define PCI_ACS_EGRESS_CTRL 0x08 /* Egress Control Vector */ /* Alternative Routing-ID Interpretation */ #define PCI_ARI_CAP 0x04 /* ARI Capability Register */ #define PCI_ARI_CAP_MFVC 0x0001 /* MFVC Function Groups Capability */ #define PCI_ARI_CAP_ACS 0x0002 /* ACS Function Groups Capability */ #define PCI_ARI_CAP_NFN(x) (((x) >> 8) & 0xff) /* Next Function Number */ #define PCI_ARI_CTRL 0x06 /* ARI Control Register */ #define PCI_ARI_CTRL_MFVC 0x0001 /* MFVC Function Groups Enable */ #define PCI_ARI_CTRL_ACS 0x0002 /* ACS Function Groups Enable */ #define PCI_ARI_CTRL_FG(x) (((x) >> 4) & 7) /* Function Group */ /* Address Translation Service */ #define PCI_ATS_CAP 0x04 /* ATS Capability Register */ #define PCI_ATS_CAP_IQD(x) ((x) & 0x1f) /* Invalidate Queue Depth */ #define PCI_ATS_CTRL 0x06 /* ATS Control Register */ #define PCI_ATS_CTRL_STU(x) ((x) & 0x1f) /* Smallest Translation Unit */ #define PCI_ATS_CTRL_ENABLE 0x8000 /* ATS Enable */ /* Single Root I/O Virtualization */ #define PCI_IOV_CAP 0x04 /* SR-IOV Capability Register */ #define PCI_IOV_CAP_VFM 0x00000001 /* VF Migration Capable */ #define PCI_IOV_CAP_VF_10BIT_TAG_REQ 0x00000004 /* VF 10-Bit Tag Requester Supported */ #define PCI_IOV_CAP_IMN(x) ((x) >> 21) /* VF Migration Interrupt Message Number */ #define PCI_IOV_CTRL 0x08 /* SR-IOV Control Register */ #define PCI_IOV_CTRL_VFE 0x0001 /* VF Enable */ #define PCI_IOV_CTRL_VFME 0x0002 /* VF Migration Enable */ #define PCI_IOV_CTRL_VFMIE 0x0004 /* VF Migration Interrupt Enable */ #define PCI_IOV_CTRL_MSE 0x0008 /* VF MSE */ #define PCI_IOV_CTRL_ARI 0x0010 /* ARI Capable Hierarchy */ #define PCI_IOV_CTRL_VF_10BIT_TAG_REQ_EN 0x0020 /* VF 10-Bit Tag Requester Enable */ #define PCI_IOV_STATUS 0x0a /* SR-IOV Status Register */ #define PCI_IOV_STATUS_MS 0x0001 /* VF Migration Status */ #define PCI_IOV_INITIALVF 0x0c /* Number of VFs that are initially associated */ #define PCI_IOV_TOTALVF 0x0e /* Maximum number of VFs that could be associated */ #define PCI_IOV_NUMVF 0x10 /* Number of VFs that are available */ #define PCI_IOV_FDL 0x12 /* Function Dependency Link */ #define PCI_IOV_OFFSET 0x14 /* First VF Offset */ #define PCI_IOV_STRIDE 0x16 /* Routing ID offset from one VF to the next one */ #define PCI_IOV_DID 0x1a /* VF Device ID */ #define PCI_IOV_SUPPS 0x1c /* Supported Page Sizes */ #define PCI_IOV_SYSPS 0x20 /* System Page Size */ #define PCI_IOV_BAR_BASE 0x24 /* VF BAR0, VF BAR1, ... VF BAR5 */ #define PCI_IOV_NUM_BAR 6 /* Number of VF BARs */ #define PCI_IOV_MSAO 0x3c /* VF Migration State Array Offset */ #define PCI_IOV_MSA_BIR(x) ((x) & 7) /* VF Migration State BIR */ #define PCI_IOV_MSA_OFFSET(x) ((x) & 0xfffffff8) /* VF Migration State Offset */ /* Multicast */ #define PCI_MCAST_CAP 0x04 /* Multicast Capability */ #define PCI_MCAST_CAP_MAX_GROUP(x) ((x) & 0x3f) #define PCI_MCAST_CAP_WIN_SIZE(x) (((x) >> 8) & 0x3f) #define PCI_MCAST_CAP_ECRC 0x8000 /* ECRC Regeneration Supported */ #define PCI_MCAST_CTRL 0x06 /* Multicast Control */ #define PCI_MCAST_CTRL_NUM_GROUP(x) ((x) & 0x3f) #define PCI_MCAST_CTRL_ENABLE 0x8000 /* MC Enabled */ #define PCI_MCAST_BAR 0x08 /* Base Address */ #define PCI_MCAST_BAR_INDEX_POS(x) ((u32) ((x) & 0x3f)) #define PCI_MCAST_BAR_MASK (~0xfffUL) #define PCI_MCAST_RCV 0x10 /* Receive */ #define PCI_MCAST_BLOCK 0x18 /* Block All */ #define PCI_MCAST_BLOCK_UNTRANS 0x20 /* Block Untranslated */ #define PCI_MCAST_OVL_BAR 0x28 /* Overlay BAR */ #define PCI_MCAST_OVL_SIZE(x) ((u32) ((x) & 0x3f)) #define PCI_MCAST_OVL_MASK (~0x3fUL) /* Page Request Interface */ #define PCI_PRI_CTRL 0x04 /* PRI Control Register */ #define PCI_PRI_CTRL_ENABLE 0x01 /* Enable */ #define PCI_PRI_CTRL_RESET 0x02 /* Reset */ #define PCI_PRI_STATUS 0x06 /* PRI status register */ #define PCI_PRI_STATUS_RF 0x0001 /* Response Failure */ #define PCI_PRI_STATUS_UPRGI 0x0002 /* Unexpected PRG index */ #define PCI_PRI_STATUS_STOPPED 0x0100 /* PRI Stopped */ #define PCI_PRI_STATUS_PASID 0x8000 /* PASID required in PRG response */ #define PCI_PRI_MAX_REQ 0x08 /* PRI max reqs supported */ #define PCI_PRI_ALLOC_REQ 0x0c /* PRI max reqs allowed */ /* Transaction Processing Hints */ #define PCI_TPH_CAPABILITIES 4 #define PCI_TPH_INTVEC_SUP (1<<1) /* Supports interrupt vector mode */ #define PCI_TPH_DEV_SUP (1<<2) /* Device specific mode supported */ #define PCI_TPH_EXT_REQ_SUP (1<<8) /* Supports extended requests */ #define PCI_TPH_ST_LOC_MASK (3<<9) /* Steering table location bits */ #define PCI_TPH_ST_NONE (0<<9) /* No steering table */ #define PCI_TPH_ST_CAP (1<<9) /* Steering table in TPH cap */ #define PCI_TPH_ST_MSIX (2<<9) /* Steering table in MSI-X table */ #define PCI_TPH_ST_SIZE_SHIFT (16) /* Encoded as size - 1 */ /* Latency Tolerance Reporting */ #define PCI_LTR_MAX_SNOOP 4 /* 16 bit value */ #define PCI_LTR_VALUE_MASK (0x3ff) #define PCI_LTR_SCALE_SHIFT (10) #define PCI_LTR_SCALE_MASK (7) #define PCI_LTR_MAX_NOSNOOP 6 /* 16 bit value */ /* Secondary PCI Express Extended Capability */ #define PCI_SEC_LNKCTL3 4 /* Link Control 3 register */ #define PCI_SEC_LNKCTL3_PERFORM_LINK_EQU 0x01 #define PCI_SEC_LNKCTL3_LNK_EQU_REQ_INTR_EN 0x02 #define PCI_SEC_LNKCTL3_ENBL_LOWER_SKP_OS_GEN_VEC(x) ((x >> 8) & 0x7F) #define PCI_SEC_LANE_ERR 8 /* Lane Error status register */ #define PCI_SEC_LANE_EQU_CTRL 12 /* Lane Equalization control register */ /* Process Address Space ID */ #define PCI_PASID_CAP 0x04 /* PASID feature register */ #define PCI_PASID_CAP_EXEC 0x02 /* Exec permissions Supported */ #define PCI_PASID_CAP_PRIV 0x04 /* Privilege Mode Supported */ #define PCI_PASID_CAP_WIDTH(x) (((x) >> 8) & 0x1f) /* Max PASID Width */ #define PCI_PASID_CTRL 0x06 /* PASID control register */ #define PCI_PASID_CTRL_ENABLE 0x01 /* Enable bit */ #define PCI_PASID_CTRL_EXEC 0x02 /* Exec permissions Enable */ #define PCI_PASID_CTRL_PRIV 0x04 /* Privilege Mode Enable */ #define PCI_DPC_CAP 4 /* DPC Capability */ #define PCI_DPC_CAP_INT_MSG(x) ((x) & 0x1f) /* DPC Interrupt Message Number */ #define PCI_DPC_CAP_RP_EXT 0x20 /* DPC Root Port Extensions */ #define PCI_DPC_CAP_TLP_BLOCK 0x40 /* DPC Poisoned TLP Egress Blocking */ #define PCI_DPC_CAP_SW_TRIGGER 0x80 /* DPC Software Trigger */ #define PCI_DPC_CAP_RP_LOG(x) (((x) >> 8) & 0xf) /* DPC RP PIO Log Size */ #define PCI_DPC_CAP_DL_ACT_ERR 0x1000 /* DPC DL_Active ERR_COR Signal */ #define PCI_DPC_CTL 6 /* DPC Control */ #define PCI_DPC_CTL_TRIGGER(x) ((x) & 0x3) /* DPC Trigger Enable */ #define PCI_DPC_CTL_CMPL 0x4 /* DPC Completion Control */ #define PCI_DPC_CTL_INT 0x8 /* DPC Interrupt Enabled */ #define PCI_DPC_CTL_ERR_COR 0x10 /* DPC ERR_COR Enabled */ #define PCI_DPC_CTL_TLP 0x20 /* DPC Poisoned TLP Egress Blocking Enabled */ #define PCI_DPC_CTL_SW_TRIGGER 0x40 /* DPC Software Trigger */ #define PCI_DPC_CTL_DL_ACTIVE 0x80 /* DPC DL_Active ERR_COR Enable */ #define PCI_DPC_STATUS 8 /* DPC STATUS */ #define PCI_DPC_STS_TRIGGER 0x01 /* DPC Trigger Status */ #define PCI_DPC_STS_REASON(x) (((x) >> 1) & 0x3) /* DPC Trigger Reason */ #define PCI_DPC_STS_INT 0x08 /* DPC Interrupt Status */ #define PCI_DPC_STS_RP_BUSY 0x10 /* DPC Root Port Busy */ #define PCI_DPC_STS_TRIGGER_EXT(x) (((x) >> 5) & 0x3) /* Trigger Reason Extension */ #define PCI_DPC_STS_PIO_FEP(x) (((x) >> 8) & 0x1f) /* DPC PIO First Error Pointer */ #define PCI_DPC_SOURCE 10 /* DPC Source ID */ /* L1 PM Substates Extended Capability */ #define PCI_L1PM_SUBSTAT_CAP 0x4 /* L1 PM Substate Capability */ #define PCI_L1PM_SUBSTAT_CAP_PM_L12 0x1 /* PCI-PM L1.2 Supported */ #define PCI_L1PM_SUBSTAT_CAP_PM_L11 0x2 /* PCI-PM L1.1 Supported */ #define PCI_L1PM_SUBSTAT_CAP_ASPM_L12 0x4 /* ASPM L1.2 Supported */ #define PCI_L1PM_SUBSTAT_CAP_ASPM_L11 0x8 /* ASPM L1.1 Supported */ #define PCI_L1PM_SUBSTAT_CAP_L1PM_SUPP 0x10 /* L1 PM Substates supported */ #define PCI_L1PM_SUBSTAT_CTL1 0x8 /* L1 PM Substate Control 1 */ #define PCI_L1PM_SUBSTAT_CTL1_PM_L12 0x1 /* PCI-PM L1.2 Enable */ #define PCI_L1PM_SUBSTAT_CTL1_PM_L11 0x2 /* PCI-PM L1.1 Enable */ #define PCI_L1PM_SUBSTAT_CTL1_ASPM_L12 0x4 /* ASPM L1.2 Enable */ #define PCI_L1PM_SUBSTAT_CTL1_ASPM_L11 0x8 /* ASPM L1.1 Enable */ #define PCI_L1PM_SUBSTAT_CTL2 0xC /* L1 PM Substate Control 2 */ /* Data Object Exchange Extended Capability */ #define PCI_DOE_CAP 0x4 /* DOE Capabilities Register */ #define PCI_DOE_CAP_INT_SUPP 0x1 /* Interrupt Support */ #define PCI_DOE_CAP_INT_MSG(x) (((x) >> 1) & 0x7ff) /* DOE Interrupt Message Number */ #define PCI_DOE_CTL 0x8 /* DOE Control Register */ #define PCI_DOE_CTL_ABORT 0x1 /* DOE Abort */ #define PCI_DOE_CTL_INT 0x2 /* DOE Interrupt Enable */ #define PCI_DOE_CTL_GO 0x80000000 /* DOE Go */ #define PCI_DOE_STS 0xC /* DOE Status Register */ #define PCI_DOE_STS_BUSY 0x1 /* DOE Busy */ #define PCI_DOE_STS_INT 0x2 /* DOE Interrupt Status */ #define PCI_DOE_STS_ERROR 0x4 /* DOE Error */ #define PCI_DOE_STS_OBJECT_READY 0x80000000 /* Data Object Ready */ /* Lane Margining at the Receiver Extended Capability */ #define PCI_LMR_CAPS 0x4 /* Margining Port Capabilities Register */ #define PCI_LMR_CAPS_DRVR 0x1 /* Margining uses Driver Software */ #define PCI_LMR_PORT_STS 0x6 /* Margining Port Status Register */ #define PCI_LMR_PORT_STS_READY 0x1 /* Margining Ready */ #define PCI_LMR_PORT_STS_SOFT_READY 0x2 /* Margining Software Ready */ /* Integrity and Data Encryption Extended Capability */ #define PCI_IDE_CAP 0x4 #define PCI_IDE_CAP_LINK_IDE_SUPP 0x1 /* Link IDE Stream Supported */ #define PCI_IDE_CAP_SELECTIVE_IDE_SUPP 0x2 /* Selective IDE Streams Supported */ #define PCI_IDE_CAP_FLOWTHROUGH_IDE_SUPP 0x4 /* Flow-Through IDE Stream Supported */ #define PCI_IDE_CAP_PARTIAL_HEADER_ENC_SUPP 0x8 /* Partial Header Encryption Supported */ #define PCI_IDE_CAP_AGGREGATION_SUPP 0x10 /* Aggregation Supported */ #define PCI_IDE_CAP_PCRC_SUPP 0x20 /* PCRC Supported */ #define PCI_IDE_CAP_IDE_KM_SUPP 0x40 /* IDE_KM Protocol Supported */ #define PCI_IDE_CAP_ALG(x) (((x) >> 8) & 0x1f) /* Supported Algorithms */ #define PCI_IDE_CAP_ALG_AES_GCM_256 0 /* AES-GCM 256 key size, 96b MAC */ #define PCI_IDE_CAP_LINK_TC_NUM(x) (((x) >> 13) & 0x7) /* Number of TCs Supported for Link IDE */ #define PCI_IDE_CAP_SELECTIVE_STREAMS_NUM(x) (((x) >> 16) & 0xff) /* Number of Selective IDE Streams Supported */ #define PCI_IDE_CAP_TEE_LIMITED_SUPP 0x1000000 /* TEE-Limited Stream Supported */ #define PCI_IDE_CTL 0x8 #define PCI_IDE_CTL_FLOWTHROUGH_IDE 0x4 /* Flow-Through IDE Stream Enabled */ #define PCI_IDE_LINK_STREAM 0xC /* Link IDE Stream block, up to PCI_IDE_CAP_LINK_TC_NUM */ /* Link IDE Stream Control Register */ #define PCI_IDE_LINK_CTL_EN 0x1 /* Link IDE Stream Enable */ #define PCI_IDE_LINK_CTL_TX_AGGR_NPR(x)(((x) >> 2) & 0x3) /* Tx Aggregation Mode NPR */ #define PCI_IDE_LINK_CTL_TX_AGGR_PR(x) (((x) >> 4) & 0x3) /* Tx Aggregation Mode PR */ #define PCI_IDE_LINK_CTL_TX_AGGR_CPL(x)(((x) >> 6) & 0x3) /* Tx Aggregation Mode CPL */ #define PCI_IDE_LINK_CTL_PCRC_EN 0x100 /* PCRC Enable */ #define PCI_IDE_LINK_CTL_PART_ENC(x) (((x) >> 10) & 0xf) /* Partial Header Encryption Mode */ #define PCI_IDE_LINK_CTL_ALG(x) (((x) >> 14) & 0x1f) /* Selected Algorithm */ #define PCI_IDE_LINK_CTL_TC(x) (((x) >> 19) & 0x7) /* Traffic Class */ #define PCI_IDE_LINK_CTL_ID(x) (((x) >> 24) & 0xff) /* Stream ID */ /* Link IDE Stream Status Register */ #define PCI_IDE_LINK_STS_STATUS(x) ((x) & 0xf) /* Link IDE Stream State */ #define PCI_IDE_LINK_STS_RECVD_INTEGRITY_CHECK 0x80000000 /* Received Integrity Check Fail Message */ /* Selective IDE Stream block, up to PCI_IDE_CAP_SELECTIVE_STREAMS_NUM */ /* Selective IDE Stream Capability Register */ #define PCI_IDE_SEL_CAP_BLOCKS_NUM(x) ((x) & 0xf) /* Number of Address Association Register Blocks */ /* Selective IDE Stream Control Register */ #define PCI_IDE_SEL_CTL_EN 0x1 /* Selective IDE Stream Enable */ #define PCI_IDE_SEL_CTL_TX_AGGR_NPR(x) (((x) >> 2) & 0x3) /* Tx Aggregation Mode NPR */ #define PCI_IDE_SEL_CTL_TX_AGGR_PR(x) (((x) >> 4) & 0x3) /* Tx Aggregation Mode PR */ #define PCI_IDE_SEL_CTL_TX_AGGR_CPL(x) (((x) >> 6) & 0x3) /* Tx Aggregation Mode CPL */ #define PCI_IDE_SEL_CTL_PCRC_EN 0x100 /* PCRC Enable */ #define PCI_IDE_SEL_CTL_CFG_EN 0x200 /* Selective IDE for Configuration Requests Enable */ #define PCI_IDE_SEL_CTL_PART_ENC(x) (((x) >> 10) & 0xf) /* Partial Header Encryption Mode */ #define PCI_IDE_SEL_CTL_ALG(x) (((x) >> 14) & 0x1f) /* Selected Algorithm */ #define PCI_IDE_SEL_CTL_TC(x) (((x) >> 19) & 0x7) /* Traffic Class */ #define PCI_IDE_SEL_CTL_DEFAULT 0x400000 /* Default Stream */ #define PCI_IDE_SEL_CTL_ID(x) (((x) >> 24) & 0xff) /* Stream ID */ /* Selective IDE Stream Status Register */ #define PCI_IDE_SEL_STS_STATUS(x) ((x) & 0xf) /* Selective IDE Stream State */ #define PCI_IDE_SEL_STS_RECVD_INTEGRITY_CHECK 0x80000000 /* Received Integrity Check Fail Message */ /* IDE RID Association Register 1 */ #define PCI_IDE_SEL_RID_1_LIMIT(x) (((x) >> 8) & 0xffff) /* RID Limit */ /* IDE RID Association Register 2 */ #define PCI_IDE_SEL_RID_2_VALID 0x1 /* Valid */ #define PCI_IDE_SEL_RID_2_BASE(x) (((x) >> 8) & 0xffff) /* RID Base */ #define PCI_IDE_SEL_RID_2_SEG_BASE(x) (((x) >> 24) & 0xff) /* Segmeng Base */ /* Selective IDE Address Association Register Block, up to PCI_IDE_SEL_CAP_BLOCKS_NUM */ #define PCI_IDE_SEL_ADDR_1_VALID 0x1 /* Valid */ #define PCI_IDE_SEL_ADDR_1_BASE_LOW(x) (((x) >> 8) & 0xfff) /* Memory Base Lower */ #define PCI_IDE_SEL_ADDR_1_LIMIT_LOW(x)(((x) >> 20) & 0xfff) /* Memory Limit Lower */ /* IDE Address Association Register 2 is "Memory Limit Upper" */ /* IDE Address Association Register 3 is "Memory Base Upper" */ /* * The PCI interface treats multi-function devices as independent * devices. The slot/function address of each device is encoded * in a single byte as follows: * * 7:3 = slot * 2:0 = function */ #define PCI_DEVFN(slot,func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) #define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f) #define PCI_FUNC(devfn) ((devfn) & 0x07) /* Device classes and subclasses */ #define PCI_CLASS_NOT_DEFINED 0x0000 #define PCI_CLASS_NOT_DEFINED_VGA 0x0001 #define PCI_BASE_CLASS_STORAGE 0x01 #define PCI_CLASS_STORAGE_SCSI 0x0100 #define PCI_CLASS_STORAGE_IDE 0x0101 #define PCI_CLASS_STORAGE_FLOPPY 0x0102 #define PCI_CLASS_STORAGE_IPI 0x0103 #define PCI_CLASS_STORAGE_RAID 0x0104 #define PCI_CLASS_STORAGE_ATA 0x0105 #define PCI_CLASS_STORAGE_SATA 0x0106 #define PCI_CLASS_STORAGE_SAS 0x0107 #define PCI_CLASS_STORAGE_OTHER 0x0180 #define PCI_BASE_CLASS_NETWORK 0x02 #define PCI_CLASS_NETWORK_ETHERNET 0x0200 #define PCI_CLASS_NETWORK_TOKEN_RING 0x0201 #define PCI_CLASS_NETWORK_FDDI 0x0202 #define PCI_CLASS_NETWORK_ATM 0x0203 #define PCI_CLASS_NETWORK_ISDN 0x0204 #define PCI_CLASS_NETWORK_OTHER 0x0280 #define PCI_BASE_CLASS_DISPLAY 0x03 #define PCI_CLASS_DISPLAY_VGA 0x0300 #define PCI_CLASS_DISPLAY_XGA 0x0301 #define PCI_CLASS_DISPLAY_3D 0x0302 #define PCI_CLASS_DISPLAY_OTHER 0x0380 #define PCI_BASE_CLASS_MULTIMEDIA 0x04 #define PCI_CLASS_MULTIMEDIA_VIDEO 0x0400 #define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401 #define PCI_CLASS_MULTIMEDIA_PHONE 0x0402 #define PCI_CLASS_MULTIMEDIA_AUDIO_DEV 0x0403 #define PCI_CLASS_MULTIMEDIA_OTHER 0x0480 #define PCI_BASE_CLASS_MEMORY 0x05 #define PCI_CLASS_MEMORY_RAM 0x0500 #define PCI_CLASS_MEMORY_FLASH 0x0501 #define PCI_CLASS_MEMORY_OTHER 0x0580 #define PCI_BASE_CLASS_BRIDGE 0x06 #define PCI_CLASS_BRIDGE_HOST 0x0600 #define PCI_CLASS_BRIDGE_ISA 0x0601 #define PCI_CLASS_BRIDGE_EISA 0x0602 #define PCI_CLASS_BRIDGE_MC 0x0603 #define PCI_CLASS_BRIDGE_PCI 0x0604 #define PCI_CLASS_BRIDGE_PCMCIA 0x0605 #define PCI_CLASS_BRIDGE_NUBUS 0x0606 #define PCI_CLASS_BRIDGE_CARDBUS 0x0607 #define PCI_CLASS_BRIDGE_RACEWAY 0x0608 #define PCI_CLASS_BRIDGE_PCI_SEMI 0x0609 #define PCI_CLASS_BRIDGE_IB_TO_PCI 0x060a #define PCI_CLASS_BRIDGE_OTHER 0x0680 #define PCI_BASE_CLASS_COMMUNICATION 0x07 #define PCI_CLASS_COMMUNICATION_SERIAL 0x0700 #define PCI_CLASS_COMMUNICATION_PARALLEL 0x0701 #define PCI_CLASS_COMMUNICATION_MSERIAL 0x0702 #define PCI_CLASS_COMMUNICATION_MODEM 0x0703 #define PCI_CLASS_COMMUNICATION_OTHER 0x0780 #define PCI_BASE_CLASS_SYSTEM 0x08 #define PCI_CLASS_SYSTEM_PIC 0x0800 #define PCI_CLASS_SYSTEM_DMA 0x0801 #define PCI_CLASS_SYSTEM_TIMER 0x0802 #define PCI_CLASS_SYSTEM_RTC 0x0803 #define PCI_CLASS_SYSTEM_PCI_HOTPLUG 0x0804 #define PCI_CLASS_SYSTEM_OTHER 0x0880 #define PCI_BASE_CLASS_INPUT 0x09 #define PCI_CLASS_INPUT_KEYBOARD 0x0900 #define PCI_CLASS_INPUT_PEN 0x0901 #define PCI_CLASS_INPUT_MOUSE 0x0902 #define PCI_CLASS_INPUT_SCANNER 0x0903 #define PCI_CLASS_INPUT_GAMEPORT 0x0904 #define PCI_CLASS_INPUT_OTHER 0x0980 #define PCI_BASE_CLASS_DOCKING 0x0a #define PCI_CLASS_DOCKING_GENERIC 0x0a00 #define PCI_CLASS_DOCKING_OTHER 0x0a80 #define PCI_BASE_CLASS_PROCESSOR 0x0b #define PCI_CLASS_PROCESSOR_386 0x0b00 #define PCI_CLASS_PROCESSOR_486 0x0b01 #define PCI_CLASS_PROCESSOR_PENTIUM 0x0b02 #define PCI_CLASS_PROCESSOR_ALPHA 0x0b10 #define PCI_CLASS_PROCESSOR_POWERPC 0x0b20 #define PCI_CLASS_PROCESSOR_MIPS 0x0b30 #define PCI_CLASS_PROCESSOR_CO 0x0b40 #define PCI_BASE_CLASS_SERIAL 0x0c #define PCI_CLASS_SERIAL_FIREWIRE 0x0c00 #define PCI_CLASS_SERIAL_ACCESS 0x0c01 #define PCI_CLASS_SERIAL_SSA 0x0c02 #define PCI_CLASS_SERIAL_USB 0x0c03 #define PCI_CLASS_SERIAL_FIBER 0x0c04 #define PCI_CLASS_SERIAL_SMBUS 0x0c05 #define PCI_CLASS_SERIAL_INFINIBAND 0x0c06 #define PCI_BASE_CLASS_WIRELESS 0x0d #define PCI_CLASS_WIRELESS_IRDA 0x0d00 #define PCI_CLASS_WIRELESS_CONSUMER_IR 0x0d01 #define PCI_CLASS_WIRELESS_RF 0x0d10 #define PCI_CLASS_WIRELESS_OTHER 0x0d80 #define PCI_BASE_CLASS_INTELLIGENT 0x0e #define PCI_CLASS_INTELLIGENT_I2O 0x0e00 #define PCI_BASE_CLASS_SATELLITE 0x0f #define PCI_CLASS_SATELLITE_TV 0x0f00 #define PCI_CLASS_SATELLITE_AUDIO 0x0f01 #define PCI_CLASS_SATELLITE_VOICE 0x0f03 #define PCI_CLASS_SATELLITE_DATA 0x0f04 #define PCI_BASE_CLASS_CRYPT 0x10 #define PCI_CLASS_CRYPT_NETWORK 0x1000 #define PCI_CLASS_CRYPT_ENTERTAINMENT 0x1010 #define PCI_CLASS_CRYPT_OTHER 0x1080 #define PCI_BASE_CLASS_SIGNAL 0x11 #define PCI_CLASS_SIGNAL_DPIO 0x1100 #define PCI_CLASS_SIGNAL_PERF_CTR 0x1101 #define PCI_CLASS_SIGNAL_SYNCHRONIZER 0x1110 #define PCI_CLASS_SIGNAL_OTHER 0x1180 #define PCI_CLASS_OTHERS 0xff /* Several ID's we need in the library */ #define PCI_VENDOR_ID_INTEL 0x8086 #define PCI_VENDOR_ID_COMPAQ 0x0e11 /* I/O resource flags, compatible with */ #define PCI_IORESOURCE_TYPE_BITS 0x00001f00 #define PCI_IORESOURCE_IO 0x00000100 #define PCI_IORESOURCE_MEM 0x00000200 #define PCI_IORESOURCE_PREFETCH 0x00002000 #define PCI_IORESOURCE_MEM_64 0x00100000 #define PCI_IORESOURCE_IO_16BIT_ADDR (1<<0) #define PCI_IORESOURCE_PCI_EA_BEI (1<<5) pciutils-3.13.0/lib/win32-kldbg.c0000600000175000001440000005222114601633332014707 0ustar mjusers/* * The PCI Library -- PCI config space access using Kernel Local Debugging Driver * * Copyright (c) 2022 Pali Rohár * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include /* for sprintf() */ #include /* for memset() and memcpy() */ #include "internal.h" #include "win32-helpers.h" #ifndef ERROR_NOT_FOUND #define ERROR_NOT_FOUND 1168 #endif #ifndef LOAD_LIBRARY_AS_IMAGE_RESOURCE #define LOAD_LIBRARY_AS_IMAGE_RESOURCE 0x20 #endif #ifndef LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE #define LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE 0x40 #endif #ifndef IOCTL_KLDBG #define IOCTL_KLDBG CTL_CODE(FILE_DEVICE_UNKNOWN, 0x1, METHOD_NEITHER, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #endif #ifndef BUS_DATA_TYPE #define BUS_DATA_TYPE LONG #endif #ifndef PCIConfiguration #define PCIConfiguration (BUS_DATA_TYPE)4 #endif #ifndef SYSDBG_COMMAND #define SYSDBG_COMMAND ULONG #endif #ifndef SysDbgReadBusData #define SysDbgReadBusData (SYSDBG_COMMAND)18 #endif #ifndef SysDbgWriteBusData #define SysDbgWriteBusData (SYSDBG_COMMAND)19 #endif #ifndef SYSDBG_BUS_DATA typedef struct _SYSDBG_BUS_DATA { ULONG Address; PVOID Buffer; ULONG Request; BUS_DATA_TYPE BusDataType; ULONG BusNumber; ULONG SlotNumber; } SYSDBG_BUS_DATA, *PSYSDBG_BUS_DATA; #define SYSDBG_BUS_DATA SYSDBG_BUS_DATA #endif #ifndef PCI_SEGMENT_BUS_NUMBER typedef struct _PCI_SEGMENT_BUS_NUMBER { union { struct { ULONG BusNumber:8; ULONG SegmentNumber:16; ULONG Reserved:8; } bits; ULONG AsULONG; } u; } PCI_SEGMENT_BUS_NUMBER, *PPCI_SEGMENT_BUS_NUMBER; #define PCI_SEGMENT_BUS_NUMBER PCI_SEGMENT_BUS_NUMBER #endif #ifndef PCI_SLOT_NUMBER typedef struct _PCI_SLOT_NUMBER { union { struct { ULONG DeviceNumber:5; ULONG FunctionNumber:3; ULONG Reserved:24; } bits; ULONG AsULONG; } u; } PCI_SLOT_NUMBER, *PPCI_SLOT_NUMBER; #define PCI_SLOT_NUMBER PCI_SLOT_NUMBER #endif #ifndef KLDBG typedef struct _KLDBG { SYSDBG_COMMAND Command; PVOID Buffer; DWORD BufferLength; } KLDBG, *PKLDBG; #define KLDBG KLDBG #endif static BOOL debug_privilege_enabled; static LUID luid_debug_privilege; static BOOL revert_only_privilege; static HANDLE revert_token; static HANDLE kldbg_dev = INVALID_HANDLE_VALUE; static BOOL win32_kldbg_pci_bus_data(BOOL WriteBusData, USHORT SegmentNumber, BYTE BusNumber, BYTE DeviceNumber, BYTE FunctionNumber, USHORT Address, PVOID Buffer, ULONG BufferSize, LPDWORD Length); static WORD win32_get_current_process_machine(void) { IMAGE_DOS_HEADER *dos_header; IMAGE_NT_HEADERS *nt_header; dos_header = (IMAGE_DOS_HEADER *)GetModuleHandle(NULL); if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) return IMAGE_FILE_MACHINE_UNKNOWN; nt_header = (IMAGE_NT_HEADERS *)((BYTE *)dos_header + dos_header->e_lfanew); if (nt_header->Signature != IMAGE_NT_SIGNATURE) return IMAGE_FILE_MACHINE_UNKNOWN; return nt_header->FileHeader.Machine; } static BOOL win32_check_driver(BYTE *driver_data) { IMAGE_DOS_HEADER *dos_header; IMAGE_NT_HEADERS *nt_headers; WORD current_machine; current_machine = win32_get_current_process_machine(); if (current_machine == IMAGE_FILE_MACHINE_UNKNOWN) return FALSE; dos_header = (IMAGE_DOS_HEADER *)driver_data; if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) return FALSE; nt_headers = (IMAGE_NT_HEADERS *)((BYTE *)dos_header + dos_header->e_lfanew); if (nt_headers->Signature != IMAGE_NT_SIGNATURE) return FALSE; if (nt_headers->FileHeader.Machine != current_machine) return FALSE; if (!(nt_headers->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE)) return FALSE; #ifndef _WIN64 if (!(nt_headers->FileHeader.Characteristics & IMAGE_FILE_32BIT_MACHINE)) return FALSE; #endif /* IMAGE_NT_OPTIONAL_HDR_MAGIC is alias for the header magic used on the target compiler architecture. */ if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) return FALSE; if (nt_headers->OptionalHeader.Subsystem != IMAGE_SUBSYSTEM_NATIVE) return FALSE; return TRUE; } static int win32_kldbg_unpack_driver(struct pci_access *a, LPTSTR driver_path) { BOOL use_kd_exe = FALSE; HMODULE exe_with_driver = NULL; HRSRC driver_resource_info = NULL; HGLOBAL driver_resource = NULL; BYTE *driver_data = NULL; DWORD driver_size = 0; HANDLE driver_handle = INVALID_HANDLE_VALUE; DWORD written = 0; DWORD error = 0; int ret = 0; /* Try to find and open windbg.exe or kd.exe file in PATH. */ exe_with_driver = LoadLibraryEx(TEXT("windbg.exe"), NULL, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE); if (!exe_with_driver) { use_kd_exe = TRUE; exe_with_driver = LoadLibraryEx(TEXT("kd.exe"), NULL, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE); } if (!exe_with_driver) { error = GetLastError(); if (error == ERROR_FILE_NOT_FOUND || error == ERROR_MOD_NOT_FOUND) a->debug("Cannot find windbg.exe or kd.exe file in PATH"); else a->debug("Cannot load %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(error)); goto out; } /* kldbgdrv.sys is embedded in windbg.exe/kd.exe as a resource with name id 0x7777 and type id 0x4444. */ driver_resource_info = FindResource(exe_with_driver, MAKEINTRESOURCE(0x7777), MAKEINTRESOURCE(0x4444)); if (!driver_resource_info) { a->debug("Cannot find kldbgdrv.sys resource in %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(GetLastError())); goto out; } driver_resource = LoadResource(exe_with_driver, driver_resource_info); if (!driver_resource) { a->debug("Cannot load kldbgdrv.sys resource from %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(GetLastError())); goto out; } driver_size = SizeofResource(exe_with_driver, driver_resource_info); if (!driver_size) { a->debug("Cannot determinate size of kldbgdrv.sys resource from %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(GetLastError())); goto out; } driver_data = LockResource(driver_resource); if (!driver_data) { a->debug("Cannot load kldbgdrv.sys resouce data from %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(GetLastError())); goto out; } if (!win32_check_driver(driver_data)) { a->debug("Cannot use kldbgdrv.sys driver from %s file: Driver is from different architecture.", use_kd_exe ? "kd.exe" : "windbg.exe"); goto out; } driver_handle = CreateFile(driver_path, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); if (driver_handle == INVALID_HANDLE_VALUE) { error = GetLastError(); if (error != ERROR_FILE_EXISTS) { a->debug("Cannot create kldbgdrv.sys driver file in system32 directory: %s.", win32_strerror(error)); goto out; } /* If driver file in system32 directory already exists then treat it as successfull unpack. */ ret = 1; goto out; } if (!WriteFile(driver_handle, driver_data, driver_size, &written, NULL) || written != driver_size) { a->debug("Cannot store kldbgdrv.sys driver file to system32 directory: %s.", win32_strerror(GetLastError())); /* On error, delete file from system32 directory to allow another unpack attempt. */ CloseHandle(driver_handle); driver_handle = INVALID_HANDLE_VALUE; DeleteFile(driver_path); goto out; } a->debug("Driver kldbgdrv.sys was successfully unpacked from %s and stored in system32 directory...", use_kd_exe ? "kd.exe" : "windbg.exe"); ret = 1; out: if (driver_handle != INVALID_HANDLE_VALUE) CloseHandle(driver_handle); if (driver_resource) FreeResource(driver_resource); if (exe_with_driver) FreeLibrary(exe_with_driver); return ret; } static int win32_kldbg_register_driver(struct pci_access *a, SC_HANDLE manager, SC_HANDLE *service) { UINT system32_len; LPTSTR driver_path; HANDLE driver_handle; /* * COM library dbgeng.dll unpacks kldbg driver to file "\\system32\\kldbgdrv.sys" * and register this driver with service name kldbgdrv. Implement same behavior. * GetSystemDirectory() returns path to "\\system32" directory on all Windows versions. */ system32_len = GetSystemDirectory(NULL, 0); /* Returns number of TCHARs plus 1 for nul-term. */ if (!system32_len) system32_len = sizeof("C:\\Windows\\System32"); driver_path = pci_malloc(a, (system32_len + sizeof("\\kldbgdrv.sys")-1) * sizeof(TCHAR)); system32_len = GetSystemDirectory(driver_path, system32_len); /* Now it returns number of TCHARs without nul-term. */ if (!system32_len) { system32_len = sizeof("C:\\Windows\\System32")-1; memcpy(driver_path, TEXT("C:\\Windows\\System32"), system32_len); } /* GetSystemDirectory returns path without backslash unless the system directory is the root directory. */ if (driver_path[system32_len-1] != '\\') driver_path[system32_len++] = '\\'; memcpy(driver_path + system32_len, TEXT("kldbgdrv.sys"), sizeof(TEXT("kldbgdrv.sys"))); driver_handle = CreateFile(driver_path, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (driver_handle != INVALID_HANDLE_VALUE) CloseHandle(driver_handle); else if (GetLastError() == ERROR_FILE_NOT_FOUND) { a->debug("Driver kldbgdrv.sys is missing, trying to unpack it from windbg.exe or kd.exe..."); if (!win32_kldbg_unpack_driver(a, driver_path)) { pci_mfree(driver_path); return 0; } } *service = CreateService(manager, TEXT("kldbgdrv"), TEXT("kldbgdrv"), SERVICE_START, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, driver_path, NULL, NULL, NULL, NULL, NULL); if (!*service) { if (GetLastError() != ERROR_SERVICE_EXISTS) { a->debug("Cannot create kldbgdrv service: %s.", win32_strerror(GetLastError())); pci_mfree(driver_path); return 0; } *service = OpenService(manager, TEXT("kldbgdrv"), SERVICE_START); if (!*service) { a->debug("Cannot open kldbgdrv service: %s.", win32_strerror(GetLastError())); pci_mfree(driver_path); return 0; } } a->debug("Service kldbgdrv was successfully registered..."); pci_mfree(driver_path); return 1; } static int win32_kldbg_start_driver(struct pci_access *a) { SC_HANDLE manager = NULL; SC_HANDLE service = NULL; DWORD error = 0; int ret = 0; manager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE); if (!manager) manager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if (!manager) { a->debug("Cannot open Service Manager: %s.", win32_strerror(GetLastError())); return 0; } service = OpenService(manager, TEXT("kldbgdrv"), SERVICE_START); if (!service) { error = GetLastError(); if (error != ERROR_SERVICE_DOES_NOT_EXIST) { a->debug("Cannot open kldbgdrv service: %s.", win32_strerror(error)); goto out; } a->debug("Kernel Local Debugging Driver (kldbgdrv.sys) is not registered, trying to register it..."); if (win32_is_32bit_on_64bit_system()) { /* TODO */ a->debug("Registering driver from 32-bit process on 64-bit system is not implemented yet."); goto out; } if (!win32_kldbg_register_driver(a, manager, &service)) goto out; } if (!StartService(service, 0, NULL)) { error = GetLastError(); if (error != ERROR_SERVICE_ALREADY_RUNNING) { a->debug("Cannot start kldbgdrv service: %s.", win32_strerror(error)); goto out; } } a->debug("Service kldbgdrv successfully started..."); ret = 1; out: if (service) CloseServiceHandle(service); if (manager) CloseServiceHandle(manager); return ret; } static int win32_kldbg_setup(struct pci_access *a) { OSVERSIONINFO version; DWORD ret_len; DWORD error; DWORD id; if (kldbg_dev != INVALID_HANDLE_VALUE) return 1; /* Check for Windows Vista (NT 6.0). */ version.dwOSVersionInfoSize = sizeof(version); if (!GetVersionEx(&version) || version.dwPlatformId != VER_PLATFORM_WIN32_NT || version.dwMajorVersion < 6) { a->debug("Accessing PCI config space via Kernel Local Debugging Driver requires Windows Vista or higher version."); return 0; } kldbg_dev = CreateFile(TEXT("\\\\.\\kldbgdrv"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (kldbg_dev == INVALID_HANDLE_VALUE) { error = GetLastError(); if (error != ERROR_FILE_NOT_FOUND) { a->debug("Cannot open \"\\\\.\\kldbgdrv\" device: %s.", win32_strerror(error)); return 0; } a->debug("Kernel Local Debugging Driver (kldbgdrv.sys) is not running, trying to start it..."); if (!win32_kldbg_start_driver(a)) return 0; kldbg_dev = CreateFile(TEXT("\\\\.\\kldbgdrv"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (kldbg_dev == INVALID_HANDLE_VALUE) { error = GetLastError(); a->debug("Cannot open \"\\\\.\\kldbgdrv\" device: %s.", win32_strerror(error)); return 0; } } /* * Try to read PCI id register from PCI device 0000:00:00.0. * If this device does not exist and kldbg API is working then * kldbg returns success with read value 0xffffffff. */ if (win32_kldbg_pci_bus_data(FALSE, 0, 0, 0, 0, 0, &id, sizeof(id), &ret_len) && ret_len == sizeof(id)) return 1; error = GetLastError(); a->debug("Cannot read PCI config space via Kernel Local Debugging Driver: %s.", win32_strerror(error)); if (error != ERROR_ACCESS_DENIED) { CloseHandle(kldbg_dev); kldbg_dev = INVALID_HANDLE_VALUE; return 0; } a->debug("..Trying again with Debug privilege..."); if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid_debug_privilege)) { a->debug("Debug privilege is not supported."); CloseHandle(kldbg_dev); kldbg_dev = INVALID_HANDLE_VALUE; return 0; } if (!win32_enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege)) { a->debug("Process does not have right to enable Debug privilege."); CloseHandle(kldbg_dev); kldbg_dev = INVALID_HANDLE_VALUE; return 0; } if (win32_kldbg_pci_bus_data(FALSE, 0, 0, 0, 0, 0, &id, sizeof(id), &ret_len) && ret_len == sizeof(id)) { a->debug("Succeeded."); debug_privilege_enabled = TRUE; return 1; } error = GetLastError(); a->debug("Cannot read PCI config space via Kernel Local Debugging Driver: %s.", win32_strerror(error)); CloseHandle(kldbg_dev); kldbg_dev = INVALID_HANDLE_VALUE; win32_revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege); revert_token = NULL; revert_only_privilege = FALSE; return 0; } static int win32_kldbg_detect(struct pci_access *a) { if (!win32_kldbg_setup(a)) return 0; return 1; } static void win32_kldbg_init(struct pci_access *a) { if (!win32_kldbg_setup(a)) { a->debug("\n"); a->error("PCI config space via Kernel Local Debugging Driver cannot be accessed."); } } static void win32_kldbg_cleanup(struct pci_access *a UNUSED) { if (kldbg_dev == INVALID_HANDLE_VALUE) return; CloseHandle(kldbg_dev); kldbg_dev = INVALID_HANDLE_VALUE; if (debug_privilege_enabled) { win32_revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege); revert_token = NULL; revert_only_privilege = FALSE; debug_privilege_enabled = FALSE; } } struct acpi_mcfg { char signature[4]; u32 length; u8 revision; u8 checksum; char oem_id[6]; char oem_table_id[8]; u32 oem_revision; char asl_compiler_id[4]; u32 asl_compiler_revision; u64 reserved; struct { u64 address; u16 pci_segment; u8 start_bus_number; u8 end_bus_number; u32 reserved; } allocations[0]; } PCI_PACKED; static void win32_kldbg_scan(struct pci_access *a) { /* * There is no kldbg API to retrieve list of PCI segments. WinDBG pci plugin * kext.dll loads debug symbols from pci.pdb file for kernel module pci.sys. * Then it reads kernel memory which belongs to PciSegmentList local variable * which is the first entry of struct _PCI_SEGMENT linked list. And then it * iterates all entries in linked list and reads SegmentNumber for each entry. * * This is extremly ugly hack and does not work on systems without installed * kernel debug symbol files. * * Do something less ugly. Retrieve ACPI MCFG table via GetSystemFirmwareTable * and parse all PCI segment numbers from it. ACPI MCFG table contains PCIe * ECAM definitions, so all PCI segment numbers. */ UINT (*WINAPI MyGetSystemFirmwareTable)(DWORD FirmwareTableProviderSignature, DWORD FirmwareTableID, PVOID pFirmwareTableBuffer, DWORD BufferSize); int i, allocations_count; struct acpi_mcfg *mcfg; HMODULE kernel32; byte *segments; DWORD error; DWORD size; /* Always scan PCI segment 0. */ pci_generic_scan_domain(a, 0); kernel32 = GetModuleHandle(TEXT("kernel32.dll")); if (!kernel32) return; /* Function GetSystemFirmwareTable() is available since Windows Vista. */ MyGetSystemFirmwareTable = (void *)GetProcAddress(kernel32, "GetSystemFirmwareTable"); if (!MyGetSystemFirmwareTable) return; /* 0x41435049 = 'ACPI', 0x4746434D = 'MCFG' */ size = MyGetSystemFirmwareTable(0x41435049, 0x4746434D, NULL, 0); if (size == 0) { error = GetLastError(); if (error == ERROR_INVALID_FUNCTION) /* ACPI is not present, so only PCI segment 0 is available. */ return; else if (error == ERROR_NOT_FOUND) /* MCFG table is not present, so only PCI segment 0 is available. */ return; a->debug("Cannot retrieve ACPI MCFG table: %s.\n", win32_strerror(error)); return; } mcfg = pci_malloc(a, size); if (MyGetSystemFirmwareTable(0x41435049, 0x4746434D, mcfg, size) != size) { error = GetLastError(); a->debug("Cannot retrieve ACPI MCFG table: %s.\n", win32_strerror(error)); pci_mfree(mcfg); return; } if (size < sizeof(*mcfg) || size < mcfg->length) { a->debug("ACPI MCFG table is broken.\n"); pci_mfree(mcfg); return; } segments = pci_malloc(a, 0xFFFF/8); memset(segments, 0, 0xFFFF/8); /* Scan all MCFG allocations and set available PCI segments into bit field. */ allocations_count = (mcfg->length - ((unsigned char *)&mcfg->allocations - (unsigned char *)mcfg)) / sizeof(mcfg->allocations[0]); for (i = 0; i < allocations_count; i++) segments[mcfg->allocations[i].pci_segment / 8] |= 1 << (mcfg->allocations[i].pci_segment % 8); /* Skip PCI segment 0 which was already scanned. */ for (i = 1; i < 0xFFFF; i++) if (segments[i / 8] & (1 << (i % 8))) pci_generic_scan_domain(a, i); pci_mfree(segments); pci_mfree(mcfg); } static BOOL win32_kldbg_pci_bus_data(BOOL WriteBusData, USHORT SegmentNumber, BYTE BusNumber, BYTE DeviceNumber, BYTE FunctionNumber, USHORT Address, PVOID Buffer, ULONG BufferSize, LPDWORD Length) { KLDBG kldbg_cmd; SYSDBG_BUS_DATA sysdbg_cmd; PCI_SLOT_NUMBER pci_slot; PCI_SEGMENT_BUS_NUMBER pci_seg_bus; memset(&pci_slot, 0, sizeof(pci_slot)); memset(&sysdbg_cmd, 0, sizeof(sysdbg_cmd)); memset(&pci_seg_bus, 0, sizeof(pci_seg_bus)); sysdbg_cmd.Address = Address; sysdbg_cmd.Buffer = Buffer; sysdbg_cmd.Request = BufferSize; sysdbg_cmd.BusDataType = PCIConfiguration; pci_seg_bus.u.bits.BusNumber = BusNumber; pci_seg_bus.u.bits.SegmentNumber = SegmentNumber; sysdbg_cmd.BusNumber = pci_seg_bus.u.AsULONG; pci_slot.u.bits.DeviceNumber = DeviceNumber; pci_slot.u.bits.FunctionNumber = FunctionNumber; sysdbg_cmd.SlotNumber = pci_slot.u.AsULONG; kldbg_cmd.Command = WriteBusData ? SysDbgWriteBusData : SysDbgReadBusData; kldbg_cmd.Buffer = &sysdbg_cmd; kldbg_cmd.BufferLength = sizeof(sysdbg_cmd); *Length = 0; return DeviceIoControl(kldbg_dev, IOCTL_KLDBG, &kldbg_cmd, sizeof(kldbg_cmd), &sysdbg_cmd, sizeof(sysdbg_cmd), Length, NULL); } static int win32_kldbg_read(struct pci_dev *d, int pos, byte *buf, int len) { DWORD ret_len; if ((unsigned int)d->domain > 0xffff) return 0; if (!win32_kldbg_pci_bus_data(FALSE, d->domain, d->bus, d->dev, d->func, pos, buf, len, &ret_len)) return 0; if (ret_len != (unsigned int)len) return 0; return 1; } static int win32_kldbg_write(struct pci_dev *d, int pos, byte *buf, int len) { DWORD ret_len; if ((unsigned int)d->domain > 0xffff) return 0; if (!win32_kldbg_pci_bus_data(TRUE, d->domain, d->bus, d->dev, d->func, pos, buf, len, &ret_len)) return 0; if (ret_len != (unsigned int)len) return 0; return 1; } struct pci_methods pm_win32_kldbg = { .name = "win32-kldbg", .help = "Win32 PCI config space access using Kernel Local Debugging Driver", .detect = win32_kldbg_detect, .init = win32_kldbg_init, .cleanup = win32_kldbg_cleanup, .scan = win32_kldbg_scan, .fill_info = pci_generic_fill_info, .read = win32_kldbg_read, .write = win32_kldbg_write, }; pciutils-3.13.0/lib/fbsd-device.c0000644000175000001440000002004314601633171015045 0ustar mjusers/* * The PCI Library -- FreeBSD /dev/pci access * * Copyright (c) 1999 Jari Kirma * Updated in 2003 by Samy Al Bahra * Updated in 2017 by Imre Vadász * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include #include #include #include #ifdef __FreeBSD_kernel_version # ifndef __FreeBSD_version # define __FreeBSD_version __FreeBSD_kernel_version # endif #endif #if __FreeBSD_version < 430000 && !defined(__DragonFly__) # include # include #else # include #endif #include "internal.h" static void fbsd_config(struct pci_access *a) { pci_define_param(a, "fbsd.path", PCI_PATH_FBSD_DEVICE, "Path to the FreeBSD PCI device"); } static int fbsd_detect(struct pci_access *a) { char *name = pci_get_param(a, "fbsd.path"); if (access(name, R_OK)) { a->warning("Cannot open %s", name); return 0; } a->debug("...using %s", name); return 1; } static void fbsd_init(struct pci_access *a) { char *name = pci_get_param(a, "fbsd.path"); int fd; a->fd = -1; a->fd_rw = -1; /* * When opening /dev/pci as read-write fails, retry with readonly which * will still allow us to gain some information via the PCIOCGETCONF and * PCIOCGETBAR IOCTLs, even without generic read access to the PCI config * space. */ fd = open(name, O_RDWR, 0); if (fd < 0) { fd = open(name, O_RDONLY, 0); if (fd < 0) a->error("fbsd_init: %s open failed", name); else { a->debug("fbsd_init: Fallback to read-only opened %s", name); a->fd = fd; } } else a->fd_rw = fd; } static void fbsd_cleanup(struct pci_access *a) { if (a->fd >= 0) { close(a->fd); a->fd = -1; } if (a->fd_rw >= 0) { close(a->fd_rw); a->fd_rw = -1; } } static void fbsd_scan(struct pci_access *a) { struct pci_conf_io conf; struct pci_conf *matches; struct pci_dev *t; uint32_t offset = 0; unsigned int i; matches = calloc(32, sizeof(struct pci_conf)); if (matches == NULL) { a->error("calloc: %s", strerror(errno)); return; } conf.generation = 0; do { conf.pat_buf_len = 0; conf.num_patterns = 0; conf.patterns = NULL; conf.match_buf_len = 32 * sizeof(struct pci_conf); conf.num_matches = 32; conf.matches = matches; conf.offset = offset; conf.status = 0; if (ioctl(a->fd_rw >= 0 ? a->fd_rw : a->fd, PCIOCGETCONF, &conf) < 0) { if (errno == ENODEV) break; a->error("fbsd_scan: ioctl(PCIOCGETCONF) failed: %s", strerror(errno)); } /* PCI_GETCONF_LIST_CHANGED would require us to start over. */ if (conf.status == PCI_GETCONF_ERROR || conf.status == PCI_GETCONF_LIST_CHANGED) { a->error("fbsd_scan: ioctl(PCIOCGETCONF) failed"); break; } for (i = 0; i < conf.num_matches; i++) { t = pci_alloc_dev(a); t->bus = matches[i].pc_sel.pc_bus; t->dev = matches[i].pc_sel.pc_dev; t->func = matches[i].pc_sel.pc_func; t->domain = matches[i].pc_sel.pc_domain; t->domain_16 = matches[i].pc_sel.pc_domain; t->vendor_id = matches[i].pc_vendor; t->device_id = matches[i].pc_device; t->known_fields = PCI_FILL_IDENT; t->hdrtype = matches[i].pc_hdr; pci_link_dev(a, t); } offset += conf.num_matches; } while (conf.status == PCI_GETCONF_MORE_DEVS); free(matches); } static void fbsd_fill_info(struct pci_dev *d, unsigned int flags) { struct pci_conf_io conf; struct pci_bar_io bar; struct pci_match_conf pattern; struct pci_conf match; int i; if (d->access->fd_rw >= 0) return pci_generic_fill_info(d, flags); /* * Can only handle PCI_FILL_IDENT, PCI_FILL_CLASS, PCI_FILL_BASES and * PCI_FILL_SIZES requests with the PCIOCGETCONF and PCIOCGETBAR IOCTLs. */ conf.pat_buf_len = sizeof(struct pci_match_conf); conf.num_patterns = 1; conf.patterns = &pattern; conf.match_buf_len = sizeof(struct pci_conf); conf.num_matches = 1; conf.matches = &match; conf.offset = 0; conf.generation = 0; conf.status = 0; pattern.pc_sel.pc_domain = d->domain; pattern.pc_sel.pc_bus = d->bus; pattern.pc_sel.pc_dev = d->dev; pattern.pc_sel.pc_func = d->func; pattern.flags = PCI_GETCONF_MATCH_DOMAIN | PCI_GETCONF_MATCH_BUS | PCI_GETCONF_MATCH_DEV | PCI_GETCONF_MATCH_FUNC; if (ioctl(d->access->fd, PCIOCGETCONF, &conf) < 0) { if (errno != ENODEV) d->access->error("fbsd_fill_info: ioctl(PCIOCGETCONF) failed: %s", strerror(errno)); return; } if (want_fill(d, flags, PCI_FILL_IDENT)) { d->vendor_id = match.pc_vendor; d->device_id = match.pc_device; } if (want_fill(d, flags, PCI_FILL_CLASS)) d->device_class = (match.pc_class << 8) | match.pc_subclass; if (want_fill(d, flags, PCI_FILL_BASES | PCI_FILL_SIZES)) { d->rom_base_addr = 0; d->rom_size = 0; for (i = 0; i < 6; i++) { bar.pbi_sel.pc_domain = d->domain; bar.pbi_sel.pc_bus = d->bus; bar.pbi_sel.pc_dev = d->dev; bar.pbi_sel.pc_func = d->func; bar.pbi_reg = 0x10 + 4*i; bar.pbi_enabled = 0; bar.pbi_base = 0; bar.pbi_length = 0; if (ioctl(d->access->fd, PCIOCGETBAR, &bar) < 0) { if (errno == ENODEV) return; if (errno == EINVAL) { d->base_addr[i] = 0; d->size[i] = 0; } else d->access->error("fbsd_fill_info: ioctl(PCIOCGETBAR) failed: %s", strerror(errno)); } else { d->base_addr[i] = bar.pbi_base; d->size[i] = bar.pbi_length; } } } } static int fbsd_read(struct pci_dev *d, int pos, byte *buf, int len) { struct pci_io pi; if (d->access->fd_rw < 0) { d->access->warning("fbsd_read: missing permissions"); return 0; } if (!(len == 1 || len == 2 || len == 4)) return pci_generic_block_read(d, pos, buf, len); if (pos >= 4096) return 0; #if __FreeBSD_version >= 700053 || defined(__DragonFly__) pi.pi_sel.pc_domain = d->domain; #else if (d->domain) return 0; #endif pi.pi_sel.pc_bus = d->bus; pi.pi_sel.pc_dev = d->dev; pi.pi_sel.pc_func = d->func; pi.pi_reg = pos; pi.pi_width = len; if (ioctl(d->access->fd_rw, PCIOCREAD, &pi) < 0) { if (errno == ENODEV) return 0; d->access->error("fbsd_read: ioctl(PCIOCREAD) failed: %s", strerror(errno)); } switch (len) { case 1: buf[0] = (u8) pi.pi_data; break; case 2: ((u16 *) buf)[0] = cpu_to_le16((u16) pi.pi_data); break; case 4: ((u32 *) buf)[0] = cpu_to_le32((u32) pi.pi_data); break; } return 1; } static int fbsd_write(struct pci_dev *d, int pos, byte *buf, int len) { struct pci_io pi; if (d->access->fd_rw < 0) { d->access->warning("fbsd_write: missing permissions"); return 0; } if (!(len == 1 || len == 2 || len == 4)) return pci_generic_block_write(d, pos, buf, len); if (pos >= 4096) return 0; #if __FreeBSD_version >= 700053 || defined(__DragonFly__) pi.pi_sel.pc_domain = d->domain; #else if (d->domain) return 0; #endif pi.pi_sel.pc_bus = d->bus; pi.pi_sel.pc_dev = d->dev; pi.pi_sel.pc_func = d->func; pi.pi_reg = pos; pi.pi_width = len; switch (len) { case 1: pi.pi_data = buf[0]; break; case 2: pi.pi_data = le16_to_cpu(((u16 *) buf)[0]); break; case 4: pi.pi_data = le32_to_cpu(((u32 *) buf)[0]); break; } if (ioctl(d->access->fd_rw, PCIOCWRITE, &pi) < 0) { if (errno == ENODEV) return 0; d->access->error("fbsd_write: ioctl(PCIOCWRITE) failed: %s", strerror(errno)); } return 1; } struct pci_methods pm_fbsd_device = { .name = "fbsd-device", .help = "FreeBSD /dev/pci device", .config = fbsd_config, .detect = fbsd_detect, .init = fbsd_init, .cleanup = fbsd_cleanup, .scan = fbsd_scan, .fill_info = fbsd_fill_info, .read = fbsd_read, .write = fbsd_write, }; pciutils-3.13.0/lib/sylixos-device.c0000644000175000001440000000624214601633306015646 0ustar mjusers/* * The PCI Library -- Direct Configuration access via SylixOS Ports * * Copyright (c) 2018 YuJian.Gong * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #define _GNU_SOURCE #define __SYLIXOS_KERNEL #define __SYLIXOS_PCI_DRV #include #include #include #include "internal.h" static void sylixos_scan(struct pci_access *a) { u8 busmap[256]; int bus; memset(busmap, 0, sizeof(busmap)); for (bus = 0; bus < PCI_MAX_BUS; bus++) if (!busmap[bus]) pci_generic_scan_bus(a, busmap, 0, bus); } static void sylixos_config(struct pci_access *a) { pci_define_param(a, "sylixos.path", PCI_PATH_SYLIXOS_DEVICE, "Path to the SylixOS PCI device"); } static int sylixos_detect(struct pci_access *a) { char *name = pci_get_param(a, "sylixos.path"); if (access(name, R_OK)) { a->warning("Cannot open %s", name); return 0; } a->debug("...using %s", name); return 1; } static void sylixos_init(struct pci_access *a UNUSED) { } static void sylixos_cleanup(struct pci_access *a UNUSED) { } static int sylixos_read(struct pci_dev *d, int pos, byte *buf, int len) { int ret = -1; u8 data_byte = -1; u16 data_word = -1; u32 data_dword = -1; if (!(len == 1 || len == 2 || len == 4)) return pci_generic_block_read(d, pos, buf, len); if (pos >= 256) return 0; switch (len) { case 1: ret = pciConfigInByte(d->bus, d->dev, d->func, pos, &data_byte); if (ret != ERROR_NONE) return 0; buf[0] = (u8)data_byte; break; case 2: ret = pciConfigInWord(d->bus, d->dev, d->func, pos, &data_word); if (ret != ERROR_NONE) return 0; ((u16 *) buf)[0] = cpu_to_le16(data_word); break; case 4: ret = pciConfigInDword(d->bus, d->dev, d->func, pos, &data_dword); if (ret != ERROR_NONE) return 0; ((u32 *) buf)[0] = cpu_to_le32(data_dword); break; } return 1; } static int sylixos_write(struct pci_dev *d, int pos, byte *buf, int len) { int ret = PX_ERROR; u8 data_byte; u16 data_word; u32 data_dword; if (!(len == 1 || len == 2 || len == 4)) return pci_generic_block_write(d, pos, buf, len); if (pos >= 256) return 0; switch (len) { case 1: data_byte = buf[0]; ret = pciConfigOutByte(d->bus, d->dev, d->func, pos, data_byte); if (ret != ERROR_NONE) return 0; break; case 2: data_word = le16_to_cpu(((u16 *) buf)[0]); ret = pciConfigOutWord(d->bus, d->dev, d->func, pos, data_word); if (ret != ERROR_NONE) return 0; break; case 4: data_dword = le32_to_cpu(((u32 *) buf)[0]); ret = pciConfigOutDword(d->bus, d->dev, d->func, pos, data_dword); if (ret != ERROR_NONE) return 0; break; } return 1; } struct pci_methods pm_sylixos_device = { .name = "sylixos-device", .help = "SylixOS /proc/pci device", .config = sylixos_config, .detect = sylixos_detect, .init = sylixos_init, .cleanup = sylixos_cleanup, .scan = sylixos_scan, .fill_info = pci_generic_fill_info, .read = sylixos_read, .write = sylixos_write, }; pciutils-3.13.0/lib/i386-io-access.h0000644000175000001440000000321714564230650015243 0ustar mjusers/* * The PCI Library -- Compiler-specific wrappers around x86 I/O port access instructions * * Copyright (c) 2023 Pali Rohár * * Can be freely distributed and used under the terms of the GNU GPL v2+ * * SPDX-License-Identifier: GPL-2.0-or-later */ #if defined(__GNUC__) static inline unsigned char intel_inb(unsigned short int port) { unsigned char value; asm volatile ("inb %w1, %0" : "=a" (value) : "Nd" (port)); return value; } static inline unsigned short int intel_inw(unsigned short int port) { unsigned short value; asm volatile ("inw %w1, %0" : "=a" (value) : "Nd" (port)); return value; } static inline unsigned int intel_inl(unsigned short int port) { u32 value; asm volatile ("inl %w1, %0" : "=a" (value) : "Nd" (port)); return value; } static inline void intel_outb(unsigned char value, unsigned short int port) { asm volatile ("outb %b0, %w1" : : "a" (value), "Nd" (port)); } static inline void intel_outw(unsigned short int value, unsigned short int port) { asm volatile ("outw %w0, %w1" : : "a" (value), "Nd" (port)); } static inline void intel_outl(u32 value, unsigned short int port) { asm volatile ("outl %0, %w1" : : "a" (value), "Nd" (port)); } #elif defined(_MSC_VER) #pragma intrinsic(_outp) #pragma intrinsic(_outpw) #pragma intrinsic(_outpd) #pragma intrinsic(_inp) #pragma intrinsic(_inpw) #pragma intrinsic(_inpd) #define intel_outb(x, y) _outp(y, x) #define intel_outw(x, y) _outpw(y, x) #define intel_outl(x, y) _outpd(y, x) #define intel_inb(x) _inp(x) #define intel_inw(x) _inpw(x) #define intel_inl(x) _inpd(x) #else #error Do not know how to access I/O ports on this compiler #endif pciutils-3.13.0/lib/i386-io-linux.h0000644000175000001440000000322514564230650015140 0ustar mjusers/* * The PCI Library -- Access to i386 I/O ports on Linux * * Copyright (c) 1997--2006 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+ * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include "i386-io-access.h" static int ioperm_enabled; static int iopl_enabled; static int intel_setup_io(struct pci_access *a UNUSED) { if (ioperm_enabled || iopl_enabled) return 1; /* * Before Linux 2.6.8, only the first 0x3ff I/O ports permissions can be * modified via ioperm(). Since 2.6.8 all ports are supported. * Since Linux 5.5, EFLAGS-based iopl() implementation was removed and * replaced by new TSS-IOPB-map-all-based emulator. Before Linux 5.5, * EFLAGS-based iopl() allowed userspace to enable/disable interrupts, * which is dangerous. So prefer usage of ioperm() and fallback to iopl(). */ if (ioperm(0xcf8, 8, 1) < 0) /* conf1 + conf2 ports */ { if (errno == EINVAL) /* ioperm() unsupported */ { if (iopl(3) < 0) return 0; iopl_enabled = 1; return 1; } return 0; } if (ioperm(0xc000, 0xfff, 1) < 0) /* remaining conf2 ports */ { ioperm(0xcf8, 8, 0); return 0; } ioperm_enabled = 1; return 1; } static inline void intel_cleanup_io(struct pci_access *a UNUSED) { if (ioperm_enabled) { ioperm(0xcf8, 8, 0); ioperm(0xc000, 0xfff, 0); ioperm_enabled = 0; } if (iopl_enabled) { iopl(0); iopl_enabled = 0; } } static inline void intel_io_lock(void) { } static inline void intel_io_unlock(void) { } pciutils-3.13.0/lib/types.h0000644000175000001440000000300614564361705014054 0ustar mjusers/* * The PCI Library -- Types and Format Strings * * Copyright (c) 1997--2022 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+ * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #ifndef PCI_HAVE_Uxx_TYPES #ifdef PCI_OS_WINDOWS /* On Windows compilers, use */ #include typedef BYTE u8; typedef WORD u16; typedef DWORD u32; typedef unsigned __int64 u64; #define PCI_U64_FMT_X "I64x" #define PCI_U64_FMT_U "I64u" #else /* Use standard types in C99 and newer */ #include #include typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; #define PCI_U64_FMT_X PRIx64 #define PCI_U64_FMT_U PRIu64 #endif #endif /* PCI_HAVE_Uxx_TYPES */ #ifdef PCI_HAVE_64BIT_ADDRESS typedef u64 pciaddr_t; #define PCIADDR_T_FMT "%08" PCI_U64_FMT_X #define PCIADDR_PORT_FMT "%04" PCI_U64_FMT_X #else typedef u32 pciaddr_t; #define PCIADDR_T_FMT "%08x" #define PCIADDR_PORT_FMT "%04x" #endif #ifdef PCI_ARCH_SPARC64 /* On sparc64 Linux the kernel reports remapped port addresses and IRQ numbers */ #undef PCIADDR_PORT_FMT #define PCIADDR_PORT_FMT PCIADDR_T_FMT #define PCIIRQ_FMT "%08x" #else #define PCIIRQ_FMT "%d" #endif #if defined(__GNUC__) && __GNUC__ > 2 #define PCI_PRINTF(x,y) __attribute__((format(printf, x, y))) #define PCI_NONRET __attribute((noreturn)) #define PCI_PACKED __attribute((packed)) #else #define PCI_PRINTF(x,y) #define PCI_NONRET #define PCI_PACKED #endif pciutils-3.13.0/lib/filter.c0000644000175000001440000002152314626120240014156 0ustar mjusers/* * The PCI Library -- Device Filtering * * Copyright (c) 1998--2022 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include "internal.h" void pci_filter_init_v38(struct pci_access *a UNUSED, struct pci_filter *f) VERSIONED_ABI; char *pci_filter_parse_slot_v38(struct pci_filter *f, char *str) VERSIONED_ABI; char *pci_filter_parse_id_v38(struct pci_filter *f, char *str) VERSIONED_ABI; int pci_filter_match_v38(struct pci_filter *f, struct pci_dev *d) VERSIONED_ABI; void pci_filter_init_v38(struct pci_access *a UNUSED, struct pci_filter *f) { memset((byte *) f, 0, sizeof(*f)); f->domain = f->bus = f->slot = f->func = -1; f->vendor = f->device = -1; f->device_class = -1; f->device_class_mask = ~0U; f->prog_if = -1; } #define BUF_SIZE 64 static char * split_to_fields(char *str, char *buffer, int sep, char **fields, int num_fields) { if (buffer) { if (strlen(str) >= BUF_SIZE) return "Expression too long"; strcpy(buffer, str); str = buffer; } int i = 0; for (;;) { if (i >= num_fields) return "Too many fields"; fields[i++] = str; while (*str && *str != sep) str++; if (!*str) break; *str++ = 0; } while (i < num_fields) fields[i++] = NULL; return NULL; } static int field_defined(char *field) { return field && field[0] && strcmp(field, "*"); } static int parse_hex_field(char *str, int *outp, unsigned int *maskp, unsigned int max) { unsigned int out = 0; unsigned int mask = ~0U; unsigned int bound = 0; if (!field_defined(str)) return 1; // and keep the defaults // Historically, filters allowed writing hexadecimal numbers with leading "0x". // This was never intentional nor documented, but some people relied on it. if (!maskp && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) str += 2; while (*str) { int c = *str++; int d; if ((c == 'x' || c == 'X') && maskp) { out = out << 4; bound = (bound << 4) | 1; mask = mask << 4; } else { if (c >= '0' && c <= '9') d = c - '0'; else if (c >= 'A' && c <= 'F') d = c - 'A' + 10; else if (c >= 'a' && c <= 'f') d = c - 'a' + 10; else return 0; out = (out << 4) | d; bound = (bound << 4) | d; mask = (mask << 4) | 0xf; } if (bound > max) return 0; } *outp = out; if (maskp) *maskp = mask; return 1; } /* Slot filter syntax: [[[domain]:][bus]:][slot][.[func]] */ char * pci_filter_parse_slot_v38(struct pci_filter *f, char *str) { char buf[BUF_SIZE]; char *fields[3]; char *err; if (err = split_to_fields(str, buf, ':', fields, 3)) return err; int i = 0; if (fields[2]) { if (!parse_hex_field(fields[0], &f->domain, NULL, 0x7fffffff)) return "Invalid domain number"; i++; } if (fields[i+1]) { if (!parse_hex_field(fields[i], &f->bus, NULL, 0xff)) return "Invalid bus number"; i++; } char *fdev = fields[i]; if (field_defined(fdev)) { char *sfields[2]; if (split_to_fields(fdev, NULL, '.', sfields, 2)) return "Invalid slot/function number"; if (!parse_hex_field(sfields[0], &f->slot, NULL, 0x1f)) return "Invalid slot number"; if (!parse_hex_field(sfields[1], &f->func, NULL, 7)) return "Invalid function number"; } return NULL; } /* ID filter syntax: [vendor]:[device][:class[:progif]] */ char * pci_filter_parse_id_v38(struct pci_filter *f, char *str) { char buf[BUF_SIZE]; char *fields[4]; char *err; if (err = split_to_fields(str, buf, ':', fields, 4)) return err; if (!fields[1]) return "At least two fields must be given"; if (!parse_hex_field(fields[0], &f->vendor, NULL, 0xffff)) return "Invalid vendor ID"; if (!parse_hex_field(fields[1], &f->device, NULL, 0xffff)) return "Invalid device ID"; if (!parse_hex_field(fields[2], &f->device_class, &f->device_class_mask, 0xffff)) return "Invalid class code"; if (!parse_hex_field(fields[3], &f->prog_if, NULL, 0xff)) return "Invalid programming interface code"; return NULL; } int pci_filter_match_v38(struct pci_filter *f, struct pci_dev *d) { if ((f->domain >= 0 && f->domain != d->domain) || (f->bus >= 0 && f->bus != d->bus) || (f->slot >= 0 && f->slot != d->dev) || (f->func >= 0 && f->func != d->func)) return 0; if (f->device >= 0 || f->vendor >= 0) { pci_fill_info_v313(d, PCI_FILL_IDENT); if ((f->device >= 0 && f->device != d->device_id) || (f->vendor >= 0 && f->vendor != d->vendor_id)) return 0; } if (f->device_class >= 0) { pci_fill_info_v313(d, PCI_FILL_CLASS); if ((f->device_class ^ d->device_class) & f->device_class_mask) return 0; } if (f->prog_if >= 0) { pci_fill_info_v313(d, PCI_FILL_CLASS_EXT); if (f->prog_if != d->prog_if) return 0; } return 1; } /* * Before pciutils v3.3, struct pci_filter had fewer fields, * so we have to provide compatibility wrappers. */ struct pci_filter_v30 { int domain, bus, slot, func; /* -1 = ANY */ int vendor, device; }; void pci_filter_init_v30(struct pci_access *a, struct pci_filter_v30 *f) VERSIONED_ABI; char *pci_filter_parse_slot_v30(struct pci_filter_v30 *f, char *str) VERSIONED_ABI; char *pci_filter_parse_id_v30(struct pci_filter_v30 *f, char *str) VERSIONED_ABI; int pci_filter_match_v30(struct pci_filter_v30 *f, struct pci_dev *d) VERSIONED_ABI; static void pci_filter_import_v30(struct pci_filter_v30 *old, struct pci_filter *new) { new->domain = old->domain; new->bus = old->bus; new->slot = old->slot; new->func = old->func; new->vendor = old->vendor; new->device = old->device; new->device_class = -1; new->device_class_mask = ~0U; new->prog_if = -1; } static void pci_filter_export_v30(struct pci_filter *new, struct pci_filter_v30 *old) { old->domain = new->domain; old->bus = new->bus; old->slot = new->slot; old->func = new->func; old->vendor = new->vendor; old->device = new->device; } void pci_filter_init_v30(struct pci_access *a, struct pci_filter_v30 *f) { struct pci_filter new; pci_filter_init_v38(a, &new); pci_filter_export_v30(&new, f); } char * pci_filter_parse_slot_v30(struct pci_filter_v30 *f, char *str) { struct pci_filter new; char *err; pci_filter_import_v30(f, &new); if (err = pci_filter_parse_slot_v38(&new, str)) return err; pci_filter_export_v30(&new, f); return NULL; } char * pci_filter_parse_id_v30(struct pci_filter_v30 *f, char *str) { struct pci_filter new; char *err; pci_filter_import_v30(f, &new); if (err = pci_filter_parse_id_v38(&new, str)) return err; if (new.device_class >= 0 || new.prog_if >= 0) return "Filtering by class or programming interface not supported in this program"; pci_filter_export_v30(&new, f); return NULL; } int pci_filter_match_v30(struct pci_filter_v30 *f, struct pci_dev *d) { struct pci_filter new; pci_filter_import_v30(f, &new); return pci_filter_match_v38(&new, d); } // Version 3.3 is the same as version 3.8, only device_class_mask and prog_if were not implemented // (their positions in struct pci_filter were declared as RFU). STATIC_ALIAS(void pci_filter_init(struct pci_access *a, struct pci_filter *f), pci_filter_init_v38(a, f)); DEFINE_ALIAS(void pci_filter_init_v33(struct pci_access *a, struct pci_filter *f), pci_filter_init_v38); SYMBOL_VERSION(pci_filter_init_v30, pci_filter_init@LIBPCI_3.0); SYMBOL_VERSION(pci_filter_init_v33, pci_filter_init@LIBPCI_3.3); SYMBOL_VERSION(pci_filter_init_v38, pci_filter_init@@LIBPCI_3.8); STATIC_ALIAS(char *pci_filter_parse_slot(struct pci_filter *f, char *str), pci_filter_parse_slot_v38(f, str)); DEFINE_ALIAS(char *pci_filter_parse_slot_v33(struct pci_filter *f, char *str), pci_filter_parse_slot_v38); SYMBOL_VERSION(pci_filter_parse_slot_v30, pci_filter_parse_slot@LIBPCI_3.0); SYMBOL_VERSION(pci_filter_parse_slot_v33, pci_filter_parse_slot@LIBPCI_3.3); SYMBOL_VERSION(pci_filter_parse_slot_v38, pci_filter_parse_slot@@LIBPCI_3.8); STATIC_ALIAS(char *pci_filter_parse_id(struct pci_filter *f, char *str), pci_filter_parse_id_v38(f, str)); DEFINE_ALIAS(char *pci_filter_parse_id_v33(struct pci_filter *f, char *str), pci_filter_parse_id_v38); SYMBOL_VERSION(pci_filter_parse_id_v30, pci_filter_parse_id@LIBPCI_3.0); SYMBOL_VERSION(pci_filter_parse_id_v33, pci_filter_parse_id@LIBPCI_3.3); SYMBOL_VERSION(pci_filter_parse_id_v38, pci_filter_parse_id@@LIBPCI_3.8); STATIC_ALIAS(int pci_filter_match(struct pci_filter *f, struct pci_dev *d), pci_filter_match_v38(f, d)); DEFINE_ALIAS(int pci_filter_match_v33(struct pci_filter *f, struct pci_dev *d), pci_filter_match_v38); SYMBOL_VERSION(pci_filter_match_v30, pci_filter_match@LIBPCI_3.0); SYMBOL_VERSION(pci_filter_match_v33, pci_filter_match@LIBPCI_3.3); SYMBOL_VERSION(pci_filter_match_v38, pci_filter_match@@LIBPCI_3.8); pciutils-3.13.0/lib/generic.c0000644000175000001440000001405214443575171014322 0ustar mjusers/* * The PCI Library -- Generic Direct Access Functions * * Copyright (c) 1997--2022 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "internal.h" void pci_generic_scan_bus(struct pci_access *a, byte *busmap, int domain, int bus) { int dev, multi, ht; struct pci_dev *t; a->debug("Scanning bus %02x for devices...\n", bus); if (busmap[bus]) { a->warning("Bus %02x seen twice (firmware bug). Ignored.", bus); return; } busmap[bus] = 1; t = pci_alloc_dev(a); t->domain = domain; t->bus = bus; for (dev=0; dev<32; dev++) { t->dev = dev; multi = 0; for (t->func=0; !t->func || multi && t->func<8; t->func++) { u32 vd = pci_read_long(t, PCI_VENDOR_ID); struct pci_dev *d; if (!vd || vd == 0xffffffff) continue; ht = pci_read_byte(t, PCI_HEADER_TYPE); if (!t->func) multi = ht & 0x80; ht &= 0x7f; d = pci_alloc_dev(a); d->domain = t->domain; d->bus = t->bus; d->dev = t->dev; d->func = t->func; d->vendor_id = vd & 0xffff; d->device_id = vd >> 16U; d->known_fields = PCI_FILL_IDENT; d->hdrtype = ht; pci_link_dev(a, d); switch (ht) { case PCI_HEADER_TYPE_NORMAL: break; case PCI_HEADER_TYPE_BRIDGE: case PCI_HEADER_TYPE_CARDBUS: pci_generic_scan_bus(a, busmap, domain, pci_read_byte(t, PCI_SECONDARY_BUS)); break; default: a->debug("Device %04x:%02x:%02x.%d has unknown header type %02x.\n", d->domain, d->bus, d->dev, d->func, ht); } } } pci_free_dev(t); } void pci_generic_scan_domain(struct pci_access *a, int domain) { byte busmap[256]; memset(busmap, 0, sizeof(busmap)); pci_generic_scan_bus(a, busmap, domain, 0); } void pci_generic_scan(struct pci_access *a) { pci_generic_scan_domain(a, 0); } static int get_hdr_type(struct pci_dev *d) { if (d->hdrtype < 0) d->hdrtype = pci_read_byte(d, PCI_HEADER_TYPE) & 0x7f; return d->hdrtype; } void pci_generic_fill_info(struct pci_dev *d, unsigned int flags) { struct pci_access *a = d->access; struct pci_cap *cap; if (want_fill(d, flags, PCI_FILL_IDENT)) { d->vendor_id = pci_read_word(d, PCI_VENDOR_ID); d->device_id = pci_read_word(d, PCI_DEVICE_ID); } if (want_fill(d, flags, PCI_FILL_CLASS)) d->device_class = pci_read_word(d, PCI_CLASS_DEVICE); if (want_fill(d, flags, PCI_FILL_CLASS_EXT)) { d->prog_if = pci_read_byte(d, PCI_CLASS_PROG); d->rev_id = pci_read_byte(d, PCI_REVISION_ID); } if (want_fill(d, flags, PCI_FILL_SUBSYS)) { switch (get_hdr_type(d)) { case PCI_HEADER_TYPE_NORMAL: d->subsys_vendor_id = pci_read_word(d, PCI_SUBSYSTEM_VENDOR_ID); d->subsys_id = pci_read_word(d, PCI_SUBSYSTEM_ID); break; case PCI_HEADER_TYPE_BRIDGE: cap = pci_find_cap(d, PCI_CAP_ID_SSVID, PCI_CAP_NORMAL); if (cap) { d->subsys_vendor_id = pci_read_word(d, cap->addr + PCI_SSVID_VENDOR); d->subsys_id = pci_read_word(d, cap->addr + PCI_SSVID_DEVICE); } break; case PCI_HEADER_TYPE_CARDBUS: d->subsys_vendor_id = pci_read_word(d, PCI_CB_SUBSYSTEM_VENDOR_ID); d->subsys_id = pci_read_word(d, PCI_CB_SUBSYSTEM_ID); break; default: clear_fill(d, PCI_FILL_SUBSYS); } } if (want_fill(d, flags, PCI_FILL_IRQ)) d->irq = pci_read_byte(d, PCI_INTERRUPT_LINE); if (want_fill(d, flags, PCI_FILL_BASES)) { int cnt = 0, i; memset(d->base_addr, 0, sizeof(d->base_addr)); switch (get_hdr_type(d)) { case PCI_HEADER_TYPE_NORMAL: cnt = 6; break; case PCI_HEADER_TYPE_BRIDGE: cnt = 2; break; case PCI_HEADER_TYPE_CARDBUS: cnt = 1; break; } if (cnt) { for (i=0; ibase_addr[i] = x; else { if ((x & PCI_BASE_ADDRESS_MEM_TYPE_MASK) != PCI_BASE_ADDRESS_MEM_TYPE_64) d->base_addr[i] = x; else if (i >= cnt-1) a->warning("%04x:%02x:%02x.%d: Invalid 64-bit address seen for BAR %d.", d->domain, d->bus, d->dev, d->func, i); else { u32 y = pci_read_long(d, PCI_BASE_ADDRESS_0 + (++i)*4); #ifdef PCI_HAVE_64BIT_ADDRESS d->base_addr[i-1] = x | (((pciaddr_t) y) << 32); #else if (y) a->warning("%04x:%02x:%02x.%d 64-bit device address ignored.", d->domain, d->bus, d->dev, d->func); else d->base_addr[i-1] = x; #endif } } } } } if (want_fill(d, flags, PCI_FILL_ROM_BASE)) { int reg = 0; d->rom_base_addr = 0; switch (get_hdr_type(d)) { case PCI_HEADER_TYPE_NORMAL: reg = PCI_ROM_ADDRESS; break; case PCI_HEADER_TYPE_BRIDGE: reg = PCI_ROM_ADDRESS1; break; } if (reg) { u32 u = pci_read_long(d, reg); if (u != 0xffffffff) d->rom_base_addr = u; } } pci_scan_caps(d, flags); } static int pci_generic_block_op(struct pci_dev *d, int pos, byte *buf, int len, int (*r)(struct pci_dev *d, int pos, byte *buf, int len)) { if ((pos & 1) && len >= 1) { if (!r(d, pos, buf, 1)) return 0; pos++; buf++; len--; } if ((pos & 3) && len >= 2) { if (!r(d, pos, buf, 2)) return 0; pos += 2; buf += 2; len -= 2; } while (len >= 4) { if (!r(d, pos, buf, 4)) return 0; pos += 4; buf += 4; len -= 4; } if (len >= 2) { if (!r(d, pos, buf, 2)) return 0; pos += 2; buf += 2; len -= 2; } if (len && !r(d, pos, buf, 1)) return 0; return 1; } int pci_generic_block_read(struct pci_dev *d, int pos, byte *buf, int len) { return pci_generic_block_op(d, pos, buf, len, d->access->methods->read); } int pci_generic_block_write(struct pci_dev *d, int pos, byte *buf, int len) { return pci_generic_block_op(d, pos, buf, len, d->access->methods->write); } pciutils-3.13.0/lspci.c0000644000175000001440000010640314626117073013250 0ustar mjusers/* * The PCI Utilities -- List All PCI Devices * * Copyright (c) 1997--2020 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include "lspci.h" /* Options */ int verbose; /* Show detailed information */ static int opt_hex; /* Show contents of config space as hexadecimal numbers */ struct pci_filter filter; /* Device filter */ static int opt_filter; /* Any filter was given */ static int opt_tree; /* Show bus tree */ static int opt_path; /* Show bridge path */ static int opt_machine; /* Generate machine-readable output */ static int opt_map_mode; /* Bus mapping mode enabled */ static int opt_domains; /* Show domain numbers (0=disabled, 1=auto-detected, 2=requested) */ static int opt_kernel; /* Show kernel drivers */ static int opt_query_dns; /* Query the DNS (0=disabled, 1=enabled, 2=refresh cache) */ static int opt_query_all; /* Query the DNS for all entries */ char *opt_pcimap; /* Override path to Linux modules.pcimap */ const char program_name[] = "lspci"; static char options[] = "nvbxs:d:tPi:mgp:qkMDQ" GENERIC_OPTIONS ; static char help_msg[] = "Usage: lspci []\n" "\n" "Basic display modes:\n" "-mm\t\tProduce machine-readable output (single -m for an obsolete format)\n" "-t\t\tShow bus tree\n" "\n" "Display options:\n" "-v\t\tBe verbose (-vv or -vvv for higher verbosity)\n" #ifdef PCI_OS_LINUX "-k\t\tShow kernel drivers handling each device\n" #endif "-x\t\tShow hex-dump of the standard part of the config space\n" "-xxx\t\tShow hex-dump of the whole config space (dangerous; root only)\n" "-xxxx\t\tShow hex-dump of the 4096-byte extended config space (root only)\n" "-b\t\tBus-centric view (addresses and IRQ's as seen by the bus)\n" "-D\t\tAlways show domain numbers\n" "-P\t\tDisplay bridge path in addition to bus and device number\n" "-PP\t\tDisplay bus path in addition to bus and device number\n" "\n" "Resolving of device ID's to names:\n" "-n\t\tShow numeric ID's\n" "-nn\t\tShow both textual and numeric ID's (names & numbers)\n" #ifdef PCI_USE_DNS "-q\t\tQuery the PCI ID database for unknown ID's via DNS\n" "-qq\t\tAs above, but re-query locally cached entries\n" "-Q\t\tQuery the PCI ID database for all ID's via DNS\n" #endif "\n" "Selection of devices:\n" "-s [[[[]:]]:][][.[]]\tShow only devices in selected slots\n" "-d []:[][:]\t\tShow only devices with specified ID's\n" "\n" "Other options:\n" "-i \tUse specified ID database instead of %s\n" #ifdef PCI_OS_LINUX "-p \tLook up kernel modules in a given file instead of default modules.pcimap\n" #endif "-M\t\tEnable `bus mapping' mode (dangerous; root only)\n" "\n" "PCI access options:\n" GENERIC_HELP ; /*** Our view of the PCI bus ***/ struct pci_access *pacc; struct device *first_dev; static int seen_errors; static int need_topology; int config_fetch(struct device *d, unsigned int pos, unsigned int len) { unsigned int end = pos+len; int result; while (pos < d->config_bufsize && len && d->present[pos]) pos++, len--; while (pos+len <= d->config_bufsize && len && d->present[pos+len-1]) len--; if (!len) return 1; if (end > d->config_bufsize) { int orig_size = d->config_bufsize; while (end > d->config_bufsize) d->config_bufsize *= 2; d->config = xrealloc(d->config, d->config_bufsize); d->present = xrealloc(d->present, d->config_bufsize); memset(d->present + orig_size, 0, d->config_bufsize - orig_size); pci_setup_cache(d->dev, d->config, d->dev->cache_len); } result = pci_read_block(d->dev, pos, d->config + pos, len); if (result) memset(d->present + pos, 1, len); return result; } struct device * scan_device(struct pci_dev *p) { struct device *d; if (p->domain && !opt_domains) opt_domains = 1; if (!pci_filter_match(&filter, p) && !need_topology) return NULL; d = xmalloc(sizeof(struct device)); memset(d, 0, sizeof(*d)); d->dev = p; d->no_config_access = p->no_config_access; d->config_cached = d->config_bufsize = 64; d->config = xmalloc(64); d->present = xmalloc(64); memset(d->present, 1, 64); if (!d->no_config_access && !pci_read_block(p, 0, d->config, 64)) { d->no_config_access = 1; d->config_cached = d->config_bufsize = 0; memset(d->present, 0, 64); } if (!d->no_config_access && (d->config[PCI_HEADER_TYPE] & 0x7f) == PCI_HEADER_TYPE_CARDBUS) { /* For cardbus bridges, we need to fetch 64 bytes more to get the * full standard header... */ if (config_fetch(d, 64, 64)) d->config_cached += 64; } pci_setup_cache(p, d->config, d->config_cached); pci_fill_info(p, PCI_FILL_IDENT | PCI_FILL_CLASS | PCI_FILL_CLASS_EXT | PCI_FILL_SUBSYS | (need_topology ? PCI_FILL_PARENT : 0)); return d; } static void scan_devices(void) { struct device *d; struct pci_dev *p; pci_scan_bus(pacc); for (p=pacc->devices; p; p=p->next) if (d = scan_device(p)) { d->next = first_dev; first_dev = d; } } /*** Config space accesses ***/ static void check_conf_range(struct device *d, unsigned int pos, unsigned int len) { while (len) if (!d->present[pos]) die("Internal bug: Accessing non-read configuration byte at position %x", pos); else pos++, len--; } byte get_conf_byte(struct device *d, unsigned int pos) { check_conf_range(d, pos, 1); return d->config[pos]; } word get_conf_word(struct device *d, unsigned int pos) { check_conf_range(d, pos, 2); return d->config[pos] | (d->config[pos+1] << 8); } u32 get_conf_long(struct device *d, unsigned int pos) { check_conf_range(d, pos, 4); return d->config[pos] | (d->config[pos+1] << 8) | (d->config[pos+2] << 16) | (d->config[pos+3] << 24); } /*** Sorting ***/ static int compare_them(const void *A, const void *B) { const struct pci_dev *a = (*(const struct device **)A)->dev; const struct pci_dev *b = (*(const struct device **)B)->dev; if (a->domain < b->domain) return -1; if (a->domain > b->domain) return 1; if (a->bus < b->bus) return -1; if (a->bus > b->bus) return 1; if (a->dev < b->dev) return -1; if (a->dev > b->dev) return 1; if (a->func < b->func) return -1; if (a->func > b->func) return 1; return 0; } static void sort_them(void) { struct device **index, **h, **last_dev; int cnt; struct device *d; cnt = 0; for (d=first_dev; d; d=d->next) cnt++; h = index = alloca(sizeof(struct device *) * cnt); for (d=first_dev; d; d=d->next) *h++ = d; qsort(index, cnt, sizeof(struct device *), compare_them); last_dev = &first_dev; h = index; while (cnt--) { *last_dev = *h; last_dev = &(*h)->next; h++; } *last_dev = NULL; } /*** Normal output ***/ static void show_slot_path(struct device *d) { struct pci_dev *p = d->dev; if (opt_path) { struct bus *bus = d->parent_bus; struct bridge *br = bus->parent_bridge; if (br && br->br_dev) { show_slot_path(br->br_dev); if (opt_path > 1) printf("/%02x:%02x.%d", p->bus, p->dev, p->func); else printf("/%02x.%d", p->dev, p->func); return; } } printf("%02x:%02x.%d", p->bus, p->dev, p->func); } static void show_slot_name(struct device *d) { struct pci_dev *p = d->dev; if (!opt_machine ? opt_domains : (p->domain || opt_domains >= 2)) printf("%04x:", p->domain); show_slot_path(d); } static void show_terse(struct device *d) { int c; struct pci_dev *p = d->dev; char classbuf[256], devbuf[256]; show_slot_name(d); printf(" %s: %s", pci_lookup_name(pacc, classbuf, sizeof(classbuf), PCI_LOOKUP_CLASS, p->device_class), pci_lookup_name(pacc, devbuf, sizeof(devbuf), PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE, p->vendor_id, p->device_id)); if ((p->known_fields & PCI_FILL_CLASS_EXT) && p->rev_id) printf(" (rev %02x)", p->rev_id); if (verbose) { char *x; c = (p->known_fields & PCI_FILL_CLASS_EXT) ? p->prog_if : 0; x = pci_lookup_name(pacc, devbuf, sizeof(devbuf), PCI_LOOKUP_PROGIF | PCI_LOOKUP_NO_NUMBERS, p->device_class, c); if (c || x) { printf(" (prog-if %02x", c); if (x) printf(" [%s]", x); putchar(')'); } } putchar('\n'); if (verbose || opt_kernel) { char ssnamebuf[256]; pci_fill_info(p, PCI_FILL_LABEL); if (p->label) printf("\tDeviceName: %s", p->label); if ((p->known_fields & PCI_FILL_SUBSYS) && p->subsys_vendor_id && p->subsys_vendor_id != 0xffff) printf("\tSubsystem: %s\n", pci_lookup_name(pacc, ssnamebuf, sizeof(ssnamebuf), PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE, p->vendor_id, p->device_id, p->subsys_vendor_id, p->subsys_id)); } } /*** Verbose output ***/ static void show_size(u64 x) { static const char suffix[][2] = { "", "K", "M", "G", "T" }; unsigned i; if (!x) return; for (i = 0; i < (sizeof(suffix) / sizeof(*suffix) - 1); i++) { if (x % 1024) break; x /= 1024; } printf(" [size=%u%s]", (unsigned)x, suffix[i]); } static void show_range(const char *prefix, u64 base, u64 limit, int bits, int disabled) { printf("%s:", prefix); if (base <= limit || verbose > 2) printf(" %0*" PCI_U64_FMT_X "-%0*" PCI_U64_FMT_X, (bits+3)/4, base, (bits+3)/4, limit); if (!disabled && base <= limit) show_size(limit - base + 1); else printf(" [disabled]"); if (bits) printf(" [%d-bit]", bits); putchar('\n'); } static u32 ioflg_to_pciflg(pciaddr_t ioflg) { u32 flg; if (ioflg & PCI_IORESOURCE_IO) flg = PCI_BASE_ADDRESS_SPACE_IO; else if (!(ioflg & PCI_IORESOURCE_MEM)) flg = 0; else { flg = PCI_BASE_ADDRESS_SPACE_MEMORY; if (ioflg & PCI_IORESOURCE_MEM_64) flg |= PCI_BASE_ADDRESS_MEM_TYPE_64; else flg |= PCI_BASE_ADDRESS_MEM_TYPE_32; if (ioflg & PCI_IORESOURCE_PREFETCH) flg |= PCI_BASE_ADDRESS_MEM_PREFETCH; } return flg; } static void show_bases(struct device *d, int cnt, int without_config_data) { struct pci_dev *p = d->dev; word cmd = without_config_data ? (PCI_COMMAND_IO | PCI_COMMAND_MEMORY) : get_conf_word(d, PCI_COMMAND); int i; for (i=0; ibase_addr[i]; pciaddr_t len = (p->known_fields & PCI_FILL_SIZES) ? p->size[i] : 0; pciaddr_t ioflg = (p->known_fields & PCI_FILL_IO_FLAGS) ? p->flags[i] : 0; u32 flg = (p->known_fields & PCI_FILL_IO_FLAGS) ? ioflg_to_pciflg(ioflg) : without_config_data ? 0 : get_conf_long(d, PCI_BASE_ADDRESS_0 + 4*i); u32 hw_lower = 0; u32 hw_upper = 0; int broken = 0; int virtual = 0; if (flg == 0xffffffff) flg = 0; if (!pos && !flg && !len) continue; if (verbose > 1) printf("\tRegion %d: ", i); else putchar('\t'); /* Detect virtual regions, which are reported by the OS, but unassigned in the device */ if ((p->known_fields & PCI_FILL_IO_FLAGS) && !without_config_data) { /* Read address as seen by the hardware */ hw_lower = get_conf_long(d, PCI_BASE_ADDRESS_0 + 4*i); if ((hw_lower & PCI_BASE_ADDRESS_SPACE) == (ioflg_to_pciflg(ioflg) & PCI_BASE_ADDRESS_SPACE)) { if ((ioflg & PCI_IORESOURCE_TYPE_BITS) == PCI_IORESOURCE_MEM && (hw_lower & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == PCI_BASE_ADDRESS_MEM_TYPE_64) { if (i >= cnt - 1) broken = 1; else hw_upper = get_conf_long(d, PCI_BASE_ADDRESS_0 + 4*i + 1); } if (pos && !hw_lower && !hw_upper && !(ioflg & PCI_IORESOURCE_PCI_EA_BEI)) virtual = 1; } } /* Print base address */ if (flg & PCI_BASE_ADDRESS_SPACE_IO) { pciaddr_t a = pos & PCI_BASE_ADDRESS_IO_MASK; printf("I/O ports at "); if (a || (cmd & PCI_COMMAND_IO)) printf(PCIADDR_PORT_FMT, a); else if (hw_lower) printf(""); else printf(""); if (virtual) printf(" [virtual]"); else if (!(cmd & PCI_COMMAND_IO)) printf(" [disabled]"); } else { int t = flg & PCI_BASE_ADDRESS_MEM_TYPE_MASK; pciaddr_t a = pos & PCI_ADDR_MEM_MASK; printf("Memory at "); if (broken) printf(""); else if (a) printf(PCIADDR_T_FMT, a); else if (hw_lower || hw_upper) printf(""); else printf(""); printf(" (%s, %sprefetchable)", (t == PCI_BASE_ADDRESS_MEM_TYPE_32) ? "32-bit" : (t == PCI_BASE_ADDRESS_MEM_TYPE_64) ? "64-bit" : (t == PCI_BASE_ADDRESS_MEM_TYPE_1M) ? "low-1M" : "type 3", (flg & PCI_BASE_ADDRESS_MEM_PREFETCH) ? "" : "non-"); if (virtual) printf(" [virtual]"); else if (!(cmd & PCI_COMMAND_MEMORY)) printf(" [disabled]"); } if (ioflg & PCI_IORESOURCE_PCI_EA_BEI) printf(" [enhanced]"); show_size(len); putchar('\n'); } } static void show_rom(struct device *d, int reg) { struct pci_dev *p = d->dev; pciaddr_t rom = p->rom_base_addr; pciaddr_t len = (p->known_fields & PCI_FILL_SIZES) ? p->rom_size : 0; pciaddr_t ioflg = (p->known_fields & PCI_FILL_IO_FLAGS) ? p->rom_flags : 0; u32 flg = reg >= 0 ? get_conf_long(d, reg) : ioflg_to_pciflg(ioflg); word cmd = reg >= 0 ? get_conf_word(d, PCI_COMMAND) : PCI_COMMAND_MEMORY; int virtual = 0; if (!rom && !flg && !len) return; if (reg >= 0 && (rom & PCI_ROM_ADDRESS_MASK) && !(flg & PCI_ROM_ADDRESS_MASK) && !(ioflg & PCI_IORESOURCE_PCI_EA_BEI)) { flg = rom; virtual = 1; } printf("\tExpansion ROM at "); if (rom & PCI_ROM_ADDRESS_MASK) printf(PCIADDR_T_FMT, rom & PCI_ROM_ADDRESS_MASK); else if (flg & PCI_ROM_ADDRESS_MASK) printf(""); else printf(""); if (virtual) printf(" [virtual]"); if (!(flg & PCI_ROM_ADDRESS_ENABLE)) printf(" [disabled]"); else if (!virtual && !(cmd & PCI_COMMAND_MEMORY)) printf(" [disabled by cmd]"); if (ioflg & PCI_IORESOURCE_PCI_EA_BEI) printf(" [enhanced]"); show_size(len); putchar('\n'); } static void show_htype0(struct device *d) { show_bases(d, 6, 0); show_rom(d, PCI_ROM_ADDRESS); show_caps(d, PCI_CAPABILITY_LIST); } static void show_htype1(struct device *d) { struct pci_dev *p = d->dev; u32 io_base = get_conf_byte(d, PCI_IO_BASE); u32 io_limit = get_conf_byte(d, PCI_IO_LIMIT); u32 io_type = io_base & PCI_IO_RANGE_TYPE_MASK; u32 mem_base = get_conf_word(d, PCI_MEMORY_BASE); u32 mem_limit = get_conf_word(d, PCI_MEMORY_LIMIT); u32 mem_type = mem_base & PCI_MEMORY_RANGE_TYPE_MASK; u32 pref_base = get_conf_word(d, PCI_PREF_MEMORY_BASE); u32 pref_limit = get_conf_word(d, PCI_PREF_MEMORY_LIMIT); u32 pref_type = pref_base & PCI_PREF_RANGE_TYPE_MASK; word sec_stat = get_conf_word(d, PCI_SEC_STATUS); word brc = get_conf_word(d, PCI_BRIDGE_CONTROL); int io_disabled = (p->known_fields & PCI_FILL_BRIDGE_BASES) && !p->bridge_size[0]; int mem_disabled = (p->known_fields & PCI_FILL_BRIDGE_BASES) && !p->bridge_size[1]; int pref_disabled = (p->known_fields & PCI_FILL_BRIDGE_BASES) && !p->bridge_size[2]; int io_bits, pref_bits; show_bases(d, 2, 0); printf("\tBus: primary=%02x, secondary=%02x, subordinate=%02x, sec-latency=%d\n", get_conf_byte(d, PCI_PRIMARY_BUS), get_conf_byte(d, PCI_SECONDARY_BUS), get_conf_byte(d, PCI_SUBORDINATE_BUS), get_conf_byte(d, PCI_SEC_LATENCY_TIMER)); if ((p->known_fields & PCI_FILL_BRIDGE_BASES) && !io_disabled) { io_base = p->bridge_base_addr[0] & PCI_IO_RANGE_MASK; io_limit = io_base + p->bridge_size[0] - 1; io_type = p->bridge_base_addr[0] & PCI_IO_RANGE_TYPE_MASK; io_bits = (io_type == PCI_IO_RANGE_TYPE_32) ? 32 : 16; show_range("\tI/O behind bridge", io_base, io_limit, io_bits, io_disabled); } else if (io_type != (io_limit & PCI_IO_RANGE_TYPE_MASK) || (io_type != PCI_IO_RANGE_TYPE_16 && io_type != PCI_IO_RANGE_TYPE_32)) printf("\t!!! Unknown I/O range types %x/%x\n", io_base, io_limit); else { io_base = (io_base & PCI_IO_RANGE_MASK) << 8; io_limit = (io_limit & PCI_IO_RANGE_MASK) << 8; if (io_type == PCI_IO_RANGE_TYPE_32) { io_base |= (get_conf_word(d, PCI_IO_BASE_UPPER16) << 16); io_limit |= (get_conf_word(d, PCI_IO_LIMIT_UPPER16) << 16); } /* I/O is unsupported if both base and limit are zeros and resource is disabled */ if (!(io_base == 0x0 && io_limit == 0x0 && io_disabled)) { io_limit += 0xfff; io_bits = (io_type == PCI_IO_RANGE_TYPE_32) ? 32 : 16; show_range("\tI/O behind bridge", io_base, io_limit, io_bits, io_disabled); } } if ((p->known_fields & PCI_FILL_BRIDGE_BASES) && !mem_disabled) { mem_base = p->bridge_base_addr[1] & PCI_MEMORY_RANGE_MASK; mem_limit = mem_base + p->bridge_size[1] - 1; show_range("\tMemory behind bridge", mem_base, mem_limit, 32, mem_disabled); } else if (mem_type != (mem_limit & PCI_MEMORY_RANGE_TYPE_MASK) || mem_type) printf("\t!!! Unknown memory range types %x/%x\n", mem_base, mem_limit); else { mem_base = (mem_base & PCI_MEMORY_RANGE_MASK) << 16; mem_limit = (mem_limit & PCI_MEMORY_RANGE_MASK) << 16; show_range("\tMemory behind bridge", mem_base, mem_limit + 0xfffff, 32, mem_disabled); } if ((p->known_fields & PCI_FILL_BRIDGE_BASES) && !pref_disabled) { u64 pref_base_64 = p->bridge_base_addr[2] & PCI_MEMORY_RANGE_MASK; u64 pref_limit_64 = pref_base_64 + p->bridge_size[2] - 1; pref_type = p->bridge_base_addr[2] & PCI_MEMORY_RANGE_TYPE_MASK; pref_bits = (pref_type == PCI_PREF_RANGE_TYPE_64) ? 64 : 32; show_range("\tPrefetchable memory behind bridge", pref_base_64, pref_limit_64, pref_bits, pref_disabled); } else if (pref_type != (pref_limit & PCI_PREF_RANGE_TYPE_MASK) || (pref_type != PCI_PREF_RANGE_TYPE_32 && pref_type != PCI_PREF_RANGE_TYPE_64)) printf("\t!!! Unknown prefetchable memory range types %x/%x\n", pref_base, pref_limit); else { u64 pref_base_64 = (pref_base & PCI_PREF_RANGE_MASK) << 16; u64 pref_limit_64 = (pref_limit & PCI_PREF_RANGE_MASK) << 16; if (pref_type == PCI_PREF_RANGE_TYPE_64) { pref_base_64 |= (u64) get_conf_long(d, PCI_PREF_BASE_UPPER32) << 32; pref_limit_64 |= (u64) get_conf_long(d, PCI_PREF_LIMIT_UPPER32) << 32; } /* Prefetchable memory is unsupported if both base and limit are zeros and resource is disabled */ if (!(pref_base_64 == 0x0 && pref_limit_64 == 0x0 && pref_disabled)) { pref_limit_64 += 0xfffff; pref_bits = (pref_type == PCI_PREF_RANGE_TYPE_64) ? 64 : 32; show_range("\tPrefetchable memory behind bridge", pref_base_64, pref_limit_64, pref_bits, pref_disabled); } } if (verbose > 1) printf("\tSecondary status: 66MHz%c FastB2B%c ParErr%c DEVSEL=%s >TAbort%c 1) { printf("\tBridgeCtl: Parity%c SERR%c NoISA%c VGA%c VGA16%c MAbort%c >Reset%c FastB2B%c\n", FLAG(brc, PCI_BRIDGE_CTL_PARITY), FLAG(brc, PCI_BRIDGE_CTL_SERR), FLAG(brc, PCI_BRIDGE_CTL_NO_ISA), FLAG(brc, PCI_BRIDGE_CTL_VGA), FLAG(brc, PCI_BRIDGE_CTL_VGA_16BIT), FLAG(brc, PCI_BRIDGE_CTL_MASTER_ABORT), FLAG(brc, PCI_BRIDGE_CTL_BUS_RESET), FLAG(brc, PCI_BRIDGE_CTL_FAST_BACK)); printf("\t\tPriDiscTmr%c SecDiscTmr%c DiscTmrStat%c DiscTmrSERREn%c\n", FLAG(brc, PCI_BRIDGE_CTL_PRI_DISCARD_TIMER), FLAG(brc, PCI_BRIDGE_CTL_SEC_DISCARD_TIMER), FLAG(brc, PCI_BRIDGE_CTL_DISCARD_TIMER_STATUS), FLAG(brc, PCI_BRIDGE_CTL_DISCARD_TIMER_SERR_EN)); } show_caps(d, PCI_CAPABILITY_LIST); } static void show_htype2(struct device *d) { int i; word cmd = get_conf_word(d, PCI_COMMAND); word brc = get_conf_word(d, PCI_CB_BRIDGE_CONTROL); word exca; int verb = verbose > 2; show_bases(d, 1, 0); printf("\tBus: primary=%02x, secondary=%02x, subordinate=%02x, sec-latency=%d\n", get_conf_byte(d, PCI_CB_PRIMARY_BUS), get_conf_byte(d, PCI_CB_CARD_BUS), get_conf_byte(d, PCI_CB_SUBORDINATE_BUS), get_conf_byte(d, PCI_CB_LATENCY_TIMER)); for (i=0; i<2; i++) { int p = 8*i; u32 base = get_conf_long(d, PCI_CB_MEMORY_BASE_0 + p); u32 limit = get_conf_long(d, PCI_CB_MEMORY_LIMIT_0 + p); limit = limit + 0xfff; if (base <= limit || verb) printf("\tMemory window %d: %08x-%08x%s%s\n", i, base, limit, (cmd & PCI_COMMAND_MEMORY) ? "" : " [disabled]", (brc & (PCI_CB_BRIDGE_CTL_PREFETCH_MEM0 << i)) ? " (prefetchable)" : ""); } for (i=0; i<2; i++) { int p = 8*i; u32 base = get_conf_long(d, PCI_CB_IO_BASE_0 + p); u32 limit = get_conf_long(d, PCI_CB_IO_LIMIT_0 + p); if (!(base & PCI_IO_RANGE_TYPE_32)) { base &= 0xffff; limit &= 0xffff; } base &= PCI_CB_IO_RANGE_MASK; limit = (limit & PCI_CB_IO_RANGE_MASK) + 3; if (base <= limit || verb) printf("\tI/O window %d: %08x-%08x%s\n", i, base, limit, (cmd & PCI_COMMAND_IO) ? "" : " [disabled]"); } if (get_conf_word(d, PCI_CB_SEC_STATUS) & PCI_STATUS_SIG_SYSTEM_ERROR) printf("\tSecondary status: SERR\n"); if (verbose > 1) printf("\tBridgeCtl: Parity%c SERR%c ISA%c VGA%c MAbort%c >Reset%c 16bInt%c PostWrite%c\n", FLAG(brc, PCI_CB_BRIDGE_CTL_PARITY), FLAG(brc, PCI_CB_BRIDGE_CTL_SERR), FLAG(brc, PCI_CB_BRIDGE_CTL_ISA), FLAG(brc, PCI_CB_BRIDGE_CTL_VGA), FLAG(brc, PCI_CB_BRIDGE_CTL_MASTER_ABORT), FLAG(brc, PCI_CB_BRIDGE_CTL_CB_RESET), FLAG(brc, PCI_CB_BRIDGE_CTL_16BIT_INT), FLAG(brc, PCI_CB_BRIDGE_CTL_POST_WRITES)); if (d->config_cached < 128) { printf("\t\n"); return; } exca = get_conf_word(d, PCI_CB_LEGACY_MODE_BASE); if (exca) printf("\t16-bit legacy interface ports at %04x\n", exca); show_caps(d, PCI_CB_CAPABILITY_LIST); } static void show_htype_unknown(struct device *d) { struct pci_dev *p = d->dev; u64 base, limit, flags; const char *str; int i, bits; if (pacc->buscentric) return; show_bases(d, 6, 1); for (i = 0; i < 4; i++) { if (!p->bridge_base_addr[i]) continue; base = p->bridge_base_addr[i]; limit = base + p->bridge_size[i] - 1; flags = p->bridge_flags[i]; if (flags & PCI_IORESOURCE_IO) { bits = (flags & PCI_IORESOURCE_IO_16BIT_ADDR) ? 16 : 32; str = "\tI/O behind bridge"; } else if (flags & PCI_IORESOURCE_MEM) { bits = (flags & PCI_IORESOURCE_MEM_64) ? 64 : 32; if (flags & PCI_IORESOURCE_PREFETCH) str = "\tPrefetchable memory behind bridge"; else str = "\tMemory behind bridge"; } else { bits = 0; str = "\tUnknown resource behind bridge"; } show_range(str, base, limit, bits, 0); } show_rom(d, -1); } static void show_verbose(struct device *d) { struct pci_dev *p = d->dev; int unknown_config_data = 0; word class = p->device_class; byte htype = d->no_config_access ? -1 : (get_conf_byte(d, PCI_HEADER_TYPE) & 0x7f); byte bist; byte max_lat, min_gnt; char *dt_node, *iommu_group; show_terse(d); pci_fill_info(p, PCI_FILL_IRQ | PCI_FILL_BASES | PCI_FILL_ROM_BASE | PCI_FILL_SIZES | PCI_FILL_PHYS_SLOT | PCI_FILL_NUMA_NODE | PCI_FILL_DT_NODE | PCI_FILL_IOMMU_GROUP | PCI_FILL_BRIDGE_BASES | PCI_FILL_CLASS_EXT | PCI_FILL_SUBSYS | PCI_FILL_RCD_LNK); switch (htype) { case PCI_HEADER_TYPE_NORMAL: if (class == PCI_CLASS_BRIDGE_PCI) printf("\t!!! Invalid class %04x for header type %02x\n", class, htype); bist = get_conf_byte(d, PCI_BIST); max_lat = get_conf_byte(d, PCI_MAX_LAT); min_gnt = get_conf_byte(d, PCI_MIN_GNT); break; case PCI_HEADER_TYPE_BRIDGE: if ((class >> 8) != PCI_BASE_CLASS_BRIDGE) printf("\t!!! Invalid class %04x for header type %02x\n", class, htype); bist = get_conf_byte(d, PCI_BIST); min_gnt = max_lat = 0; break; case PCI_HEADER_TYPE_CARDBUS: if ((class >> 8) != PCI_BASE_CLASS_BRIDGE) printf("\t!!! Invalid class %04x for header type %02x\n", class, htype); bist = get_conf_byte(d, PCI_BIST); min_gnt = max_lat = 0; break; default: if (!d->no_config_access) printf("\t!!! Unknown header type %02x\n", htype); bist = 0; min_gnt = max_lat = 0; unknown_config_data = 1; } if (p->phy_slot) printf("\tPhysical Slot: %s\n", p->phy_slot); if (dt_node = pci_get_string_property(p, PCI_FILL_DT_NODE)) printf("\tDevice tree node: %s\n", dt_node); if (!unknown_config_data && verbose > 1) { word cmd = get_conf_word(d, PCI_COMMAND); word status = get_conf_word(d, PCI_STATUS); printf("\tControl: I/O%c Mem%c BusMaster%c SpecCycle%c MemWINV%c VGASnoop%c ParErr%c Stepping%c SERR%c FastB2B%c DisINTx%c\n", FLAG(cmd, PCI_COMMAND_IO), FLAG(cmd, PCI_COMMAND_MEMORY), FLAG(cmd, PCI_COMMAND_MASTER), FLAG(cmd, PCI_COMMAND_SPECIAL), FLAG(cmd, PCI_COMMAND_INVALIDATE), FLAG(cmd, PCI_COMMAND_VGA_PALETTE), FLAG(cmd, PCI_COMMAND_PARITY), FLAG(cmd, PCI_COMMAND_WAIT), FLAG(cmd, PCI_COMMAND_SERR), FLAG(cmd, PCI_COMMAND_FAST_BACK), FLAG(cmd, PCI_COMMAND_DISABLE_INTx)); printf("\tStatus: Cap%c 66MHz%c UDF%c FastB2B%c ParErr%c DEVSEL=%s >TAbort%c SERR%c 1) { byte int_pin = unknown_config_data ? 0 : get_conf_byte(d, PCI_INTERRUPT_PIN); if (int_pin || p->irq) printf("\tInterrupt: pin %c routed to IRQ " PCIIRQ_FMT "\n", (int_pin ? 'A' + int_pin - 1 : '?'), p->irq); if (p->numa_node != -1) printf("\tNUMA node: %d\n", p->numa_node); if (iommu_group = pci_get_string_property(p, PCI_FILL_IOMMU_GROUP)) printf("\tIOMMU group: %s\n", iommu_group); } if (!unknown_config_data && verbose <= 1) { word cmd = get_conf_word(d, PCI_COMMAND); word status = get_conf_word(d, PCI_STATUS); byte latency = get_conf_byte(d, PCI_LATENCY_TIMER); printf("\tFlags: "); if (cmd & PCI_COMMAND_MASTER) printf("bus master, "); if (cmd & PCI_COMMAND_VGA_PALETTE) printf("VGA palette snoop, "); if (cmd & PCI_COMMAND_WAIT) printf("stepping, "); if (cmd & PCI_COMMAND_FAST_BACK) printf("fast Back2Back, "); if (status & PCI_STATUS_66MHZ) printf("66MHz, "); if (status & PCI_STATUS_UDF) printf("user-definable features, "); printf("%s devsel", ((status & PCI_STATUS_DEVSEL_MASK) == PCI_STATUS_DEVSEL_SLOW) ? "slow" : ((status & PCI_STATUS_DEVSEL_MASK) == PCI_STATUS_DEVSEL_MEDIUM) ? "medium" : ((status & PCI_STATUS_DEVSEL_MASK) == PCI_STATUS_DEVSEL_FAST) ? "fast" : "??"); if (cmd & PCI_COMMAND_MASTER) printf(", latency %d", latency); if (p->irq) printf(", IRQ " PCIIRQ_FMT, p->irq); if (p->numa_node != -1) printf(", NUMA node %d", p->numa_node); if (iommu_group = pci_get_string_property(p, PCI_FILL_IOMMU_GROUP)) printf(", IOMMU group %s", iommu_group); putchar('\n'); } if (bist & PCI_BIST_CAPABLE) { if (bist & PCI_BIST_START) printf("\tBIST is running\n"); else printf("\tBIST result: %02x\n", bist & PCI_BIST_CODE_MASK); } switch (htype) { case PCI_HEADER_TYPE_NORMAL: show_htype0(d); break; case PCI_HEADER_TYPE_BRIDGE: show_htype1(d); break; case PCI_HEADER_TYPE_CARDBUS: show_htype2(d); break; default: show_htype_unknown(d); } } /*** Machine-readable dumps ***/ static void show_hex_dump(struct device *d) { unsigned int i, cnt; if (d->no_config_access) { printf("WARNING: Cannot show hex-dump of the config space\n"); return; } cnt = d->config_cached; if (opt_hex >= 3 && config_fetch(d, cnt, 256-cnt)) { cnt = 256; if (opt_hex >= 4 && config_fetch(d, 256, 4096-256)) cnt = 4096; } for (i=0; idev; char classbuf[256], vendbuf[256], devbuf[256], svbuf[256], sdbuf[256]; char *dt_node, *iommu_group; if (verbose) { pci_fill_info(p, PCI_FILL_PHYS_SLOT | PCI_FILL_NUMA_NODE | PCI_FILL_DT_NODE | PCI_FILL_IOMMU_GROUP); printf((opt_machine >= 2) ? "Slot:\t" : "Device:\t"); show_slot_name(d); putchar('\n'); printf("Class:\t%s\n", pci_lookup_name(pacc, classbuf, sizeof(classbuf), PCI_LOOKUP_CLASS, p->device_class)); printf("Vendor:\t%s\n", pci_lookup_name(pacc, vendbuf, sizeof(vendbuf), PCI_LOOKUP_VENDOR, p->vendor_id, p->device_id)); printf("Device:\t%s\n", pci_lookup_name(pacc, devbuf, sizeof(devbuf), PCI_LOOKUP_DEVICE, p->vendor_id, p->device_id)); if ((p->known_fields & PCI_FILL_SUBSYS) && p->subsys_vendor_id && p->subsys_vendor_id != 0xffff) { printf("SVendor:\t%s\n", pci_lookup_name(pacc, svbuf, sizeof(svbuf), PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_VENDOR, p->subsys_vendor_id)); printf("SDevice:\t%s\n", pci_lookup_name(pacc, sdbuf, sizeof(sdbuf), PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_DEVICE, p->vendor_id, p->device_id, p->subsys_vendor_id, p->subsys_id)); } if (p->phy_slot) printf("PhySlot:\t%s\n", p->phy_slot); if ((p->known_fields & PCI_FILL_CLASS_EXT) && p->rev_id) printf("Rev:\t%02x\n", p->rev_id); if (p->known_fields & PCI_FILL_CLASS_EXT) printf("ProgIf:\t%02x\n", p->prog_if); if (opt_kernel) show_kernel_machine(d); if (p->numa_node != -1) printf("NUMANode:\t%d\n", p->numa_node); if (dt_node = pci_get_string_property(p, PCI_FILL_DT_NODE)) printf("DTNode:\t%s\n", dt_node); if (iommu_group = pci_get_string_property(p, PCI_FILL_IOMMU_GROUP)) printf("IOMMUGroup:\t%s\n", iommu_group); } else { show_slot_name(d); print_shell_escaped(pci_lookup_name(pacc, classbuf, sizeof(classbuf), PCI_LOOKUP_CLASS, p->device_class)); print_shell_escaped(pci_lookup_name(pacc, vendbuf, sizeof(vendbuf), PCI_LOOKUP_VENDOR, p->vendor_id, p->device_id)); print_shell_escaped(pci_lookup_name(pacc, devbuf, sizeof(devbuf), PCI_LOOKUP_DEVICE, p->vendor_id, p->device_id)); if ((p->known_fields & PCI_FILL_CLASS_EXT) && p->rev_id) printf(" -r%02x", p->rev_id); if (p->known_fields & PCI_FILL_CLASS_EXT) printf(" -p%02x", p->prog_if); if ((p->known_fields & PCI_FILL_SUBSYS) && p->subsys_vendor_id && p->subsys_vendor_id != 0xffff) { print_shell_escaped(pci_lookup_name(pacc, svbuf, sizeof(svbuf), PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_VENDOR, p->subsys_vendor_id)); print_shell_escaped(pci_lookup_name(pacc, sdbuf, sizeof(sdbuf), PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_DEVICE, p->vendor_id, p->device_id, p->subsys_vendor_id, p->subsys_id)); } else printf(" \"\" \"\""); putchar('\n'); } } /*** Main show function ***/ void show_device(struct device *d) { if (opt_machine) show_machine(d); else { if (verbose) show_verbose(d); else show_terse(d); if (opt_kernel || verbose) show_kernel(d); } if (opt_hex) show_hex_dump(d); if (verbose || opt_hex) putchar('\n'); } static void show(void) { struct device *d; for (d=first_dev; d; d=d->next) if (pci_filter_match(&filter, d->dev)) show_device(d); } /* Main */ int main(int argc, char **argv) { int i; char *msg; if (argc == 2 && !strcmp(argv[1], "--version")) { puts("lspci version " PCIUTILS_VERSION); return 0; } pacc = pci_alloc(); pacc->error = die; pci_filter_init(pacc, &filter); while ((i = getopt(argc, argv, options)) != -1) switch (i) { case 'n': pacc->numeric_ids++; break; case 'v': verbose++; break; case 'b': pacc->buscentric = 1; break; case 's': if (msg = pci_filter_parse_slot(&filter, optarg)) die("-s: %s", msg); opt_filter = 1; break; case 'd': if (msg = pci_filter_parse_id(&filter, optarg)) die("-d: %s", msg); opt_filter = 1; break; case 'x': opt_hex++; break; case 'P': opt_path++; need_topology = 1; break; case 't': opt_tree++; need_topology = 1; break; case 'i': pci_set_name_list_path(pacc, optarg, 0); break; case 'm': opt_machine++; break; case 'p': opt_pcimap = optarg; break; #ifdef PCI_OS_LINUX case 'k': opt_kernel++; break; #endif case 'M': opt_map_mode++; break; case 'D': opt_domains = 2; break; #ifdef PCI_USE_DNS case 'q': opt_query_dns++; break; case 'Q': opt_query_all = 1; break; #else case 'q': case 'Q': die("DNS queries are not available in this version"); #endif default: if (parse_generic_option(i, pacc, optarg)) break; bad: fprintf(stderr, help_msg, pacc->id_file_name); return 1; } if (optind < argc) goto bad; if (opt_query_dns) { pacc->id_lookup_mode |= PCI_LOOKUP_NETWORK; if (opt_query_dns > 1) pacc->id_lookup_mode |= PCI_LOOKUP_REFRESH_CACHE; } if (opt_query_all) pacc->id_lookup_mode |= PCI_LOOKUP_NETWORK | PCI_LOOKUP_SKIP_LOCAL; pci_init(pacc); if (opt_map_mode) { if (need_topology) die("Bus mapping mode does not recognize bus topology"); map_the_bus(); } else { scan_devices(); sort_them(); if (need_topology) grow_tree(); if (opt_tree) show_forest(opt_filter ? &filter : NULL); else show(); } show_kernel_cleanup(); pci_cleanup(pacc); return (seen_errors ? 2 : 0); } pciutils-3.13.0/common.c0000644000175000001440000000523414443575171013432 0ustar mjusers/* * The PCI Utilities -- Common Functions * * Copyright (c) 1997--2016 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include "pciutils.h" void NONRET die(char *msg, ...) { va_list args; va_start(args, msg); fprintf(stderr, "%s: ", program_name); vfprintf(stderr, msg, args); fputc('\n', stderr); exit(1); } void * xmalloc(size_t howmuch) { void *p = malloc(howmuch); if (!p) die("Unable to allocate %d bytes of memory", (int) howmuch); return p; } void * xrealloc(void *ptr, size_t howmuch) { void *p = realloc(ptr, howmuch); if (!p) die("Unable to allocate %d bytes of memory", (int) howmuch); return p; } char * xstrdup(const char *str) { int len = strlen(str) + 1; char *copy = xmalloc(len); memcpy(copy, str, len); return copy; } static void set_pci_method(struct pci_access *pacc, char *arg) { char *name; int i; if (!strcmp(arg, "help")) { printf("Known PCI access methods:\n\n"); for (i=0; name = pci_get_method_name(i); i++) if (name[0]) printf("%s\n", name); exit(0); } else { i = pci_lookup_method(arg); if (i < 0) die("No such PCI access method: %s (see `-A help' for a list)", arg); pacc->method = i; } } static void set_pci_option(struct pci_access *pacc, char *arg) { if (!strcmp(arg, "help")) { struct pci_param *p; printf("Known PCI access parameters:\n\n"); for (p=NULL; p=pci_walk_params(pacc, p);) printf("%-20s %s (%s)\n", p->param, p->help, p->value); exit(0); } else { char *sep = strchr(arg, '='); if (!sep) die("Invalid PCI access parameter syntax: %s", arg); *sep++ = 0; if (pci_set_param(pacc, arg, sep) < 0) die("Unrecognized PCI access parameter: %s (see `-O help' for a list)", arg); } } int parse_generic_option(int i, struct pci_access *pacc, char *arg) { switch (i) { #ifdef PCI_HAVE_PM_INTEL_CONF case 'H': if (!strcmp(arg, "1")) pacc->method = PCI_ACCESS_I386_TYPE1; else if (!strcmp(arg, "2")) pacc->method = PCI_ACCESS_I386_TYPE2; else die("Unknown hardware configuration type %s", arg); break; #endif #ifdef PCI_HAVE_PM_DUMP case 'F': pci_set_param(pacc, "dump.name", arg); pacc->method = PCI_ACCESS_DUMP; break; #endif case 'A': set_pci_method(pacc, arg); break; case 'G': pacc->debugging++; break; case 'O': set_pci_option(pacc, arg); break; default: return 0; } return 1; } pciutils-3.13.0/pciutils.h0000644000175000001440000000364514564415164014006 0ustar mjusers/* * The PCI Utilities -- Declarations * * Copyright (c) 1997--2018 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+ * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "lib/pci.h" #include "lib/sysdep.h" #include "bitops.h" /* * gcc predefines macro __MINGW32__ for all MinGW targets. * Including some MinGW header (e.g. windef.h) defines additional * macro __MINGW32_MAJOR_VERSION (available for all MinGW targets). */ #if defined(PCI_OS_WINDOWS) && defined(__MINGW32__) #include #endif /* * On Windows only MinGW 3.0 and higher versions provides * header file. Older MinGW versions and MSVC do not have it. * DJGPP does not provide . */ #if defined(PCI_OS_DJGPP) || (defined(PCI_OS_WINDOWS) && !(defined(__MINGW32_MAJOR_VERSION) && __MINGW32_MAJOR_VERSION >= 3)) #include "compat/getopt.h" #else #include #endif #define PCIUTILS_VERSION PCILIB_VERSION extern const char program_name[]; void die(char *msg, ...) NONRET PCI_PRINTF(1,2); void *xmalloc(size_t howmuch); void *xrealloc(void *ptr, size_t howmuch); char *xstrdup(const char *str); int parse_generic_option(int i, struct pci_access *pacc, char *arg); #ifdef PCI_HAVE_PM_INTEL_CONF #define GENOPT_INTEL "H:" #define GENHELP_INTEL "-H \tUse direct hardware access ( = 1 or 2)\n" #else #define GENOPT_INTEL #define GENHELP_INTEL #endif #if defined(PCI_HAVE_PM_DUMP) && !defined(PCIUTILS_SETPCI) #define GENOPT_DUMP "F:" #define GENHELP_DUMP "-F \tRead PCI configuration dump from a given file\n" #else #define GENOPT_DUMP #define GENHELP_DUMP #endif #define GENERIC_OPTIONS "A:GO:" GENOPT_INTEL GENOPT_DUMP #define GENERIC_HELP \ "-A \tUse the specified PCI access method (see `-A help' for a list)\n" \ "-O =\tSet PCI access parameter (see `-O help' for a list)\n" \ "-G\t\tEnable PCI access debugging\n" \ GENHELP_INTEL GENHELP_DUMP pciutils-3.13.0/ls-tree.c0000644000175000001440000002625314443575171013521 0ustar mjusers/* * The PCI Utilities -- Show Bus Tree * * Copyright (c) 1997--2021 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include "lspci.h" struct bridge host_bridge = { NULL, NULL, NULL, NULL, NULL, NULL, ~0, ~0, ~0, ~0, NULL }; static struct bus * find_bus(struct bridge *b, unsigned int domain, unsigned int n) { struct bus *bus; for (bus=b->first_bus; bus; bus=bus->sibling) if (bus->domain == domain && bus->number == n) break; return bus; } static struct device * find_device(struct pci_dev *dd) { struct device *d; if (!dd) return NULL; for (d=first_dev; d; d=d->next) if (d->dev == dd) break; return d; } static struct bus * new_bus(struct bridge *b, unsigned int domain, unsigned int n) { struct bus *bus = xmalloc(sizeof(struct bus)); bus->domain = domain; bus->number = n; bus->sibling = NULL; bus->first_dev = NULL; bus->last_dev = &bus->first_dev; bus->parent_bridge = b; if (b->last_bus) b->last_bus->sibling = bus; b->last_bus = bus; if (!b->first_bus) b->first_bus = bus; return bus; } static void insert_dev(struct device *d, struct bridge *b) { struct pci_dev *p = d->dev; struct device *parent = NULL; struct bus *bus = NULL; if (p->known_fields & PCI_FILL_PARENT) parent = find_device(p->parent); if (parent && parent->bridge) { bus = parent->bridge->first_bus; if (!bus) bus = new_bus(parent->bridge, p->domain, p->bus); } if (!bus && b == &host_bridge) { for (b=b->child; b; b=b->prev) if (b->domain == (unsigned)p->domain) break; if (!b) b = &host_bridge; } if (!bus && ! (bus = find_bus(b, p->domain, p->bus))) { struct bridge *c; for (c=b->child; c; c=c->prev) if (c->domain == (unsigned)p->domain && c->secondary <= p->bus && p->bus <= c->subordinate) { insert_dev(d, c); return; } bus = new_bus(b, p->domain, p->bus); } /* Simple insertion at the end _does_ guarantee the correct order as the * original device list was sorted by (domain, bus, devfn) lexicographically * and all devices on the new list have the same bus number. */ *bus->last_dev = d; bus->last_dev = &d->bus_next; d->bus_next = NULL; d->parent_bus = bus; } void grow_tree(void) { struct device *d; struct bridge **last_br, *b; last_br = &host_bridge.chain; /* Build list of top level domain bridges */ for (d=first_dev; d; d=d->next) { for (b=host_bridge.chain; b; b=b->chain) if (b->domain == (unsigned)d->dev->domain) break; if (b) continue; b = xmalloc(sizeof(struct bridge)); b->domain = d->dev->domain; b->primary = ~0; b->secondary = 0; b->subordinate = ~0; *last_br = b; last_br = &b->chain; b->prev = b->next = b->child = NULL; b->first_bus = NULL; b->last_bus = NULL; b->br_dev = NULL; b->chain = NULL; pacc->debug("Tree: domain %04x\n", b->domain); } /* Build list of bridges */ for (d=first_dev; d; d=d->next) { struct pci_dev *dd = d->dev; word class = dd->device_class; byte ht = d->no_config_access ? -1 : (get_conf_byte(d, PCI_HEADER_TYPE) & 0x7f); if ((class >> 8) == PCI_BASE_CLASS_BRIDGE && (ht == PCI_HEADER_TYPE_BRIDGE || ht == PCI_HEADER_TYPE_CARDBUS)) { b = xmalloc(sizeof(struct bridge)); b->domain = dd->domain; b->primary = dd->bus; if (ht == PCI_HEADER_TYPE_BRIDGE) { b->secondary = get_conf_byte(d, PCI_SECONDARY_BUS); b->subordinate = get_conf_byte(d, PCI_SUBORDINATE_BUS); } else { b->secondary = get_conf_byte(d, PCI_CB_CARD_BUS); b->subordinate = get_conf_byte(d, PCI_CB_SUBORDINATE_BUS); } *last_br = b; last_br = &b->chain; b->prev = b->next = b->child = NULL; b->first_bus = NULL; b->last_bus = NULL; b->br_dev = d; d->bridge = b; pacc->debug("Tree: bridge %04x:%02x:%02x.%d: %02x -> %02x-%02x\n", dd->domain, dd->bus, dd->dev, dd->func, b->primary, b->secondary, b->subordinate); } } /* Append additional bridges reported by libpci via d->parent */ for (d=first_dev; d; d=d->next) { struct device *parent = NULL; if (d->dev->known_fields & PCI_FILL_PARENT) parent = find_device(d->dev->parent); if (!parent || parent->bridge) continue; b = xmalloc(sizeof(struct bridge)); b->domain = parent->dev->domain; b->primary = parent->dev->bus; b->secondary = d->dev->bus; /* At this stage subordinate number is unknown, so set it to secondary bus number. */ b->subordinate = b->secondary; *last_br = b; last_br = &b->chain; b->prev = b->next = b->child = NULL; b->first_bus = NULL; b->last_bus = NULL; b->br_dev = parent; parent->bridge = b; pacc->debug("Tree: bridge %04x:%02x:%02x.%d\n", b->domain, parent->dev->bus, parent->dev->dev, parent->dev->func); } *last_br = NULL; /* Create a bridge tree */ for (b=host_bridge.chain; b; b=b->chain) { struct device *br_dev = b->br_dev; struct bridge *c, *best = NULL; struct device *parent = NULL; if (br_dev && (br_dev->dev->known_fields & PCI_FILL_PARENT)) parent = find_device(br_dev->dev->parent); if (parent) best = parent->bridge; if (!best) for (c=&host_bridge; c; c=c->chain) if (c != b && (c == &host_bridge || b->domain == c->domain) && b->primary >= c->secondary && b->primary <= c->subordinate && (!best || best == &host_bridge || best->subordinate - best->primary > c->subordinate - c->primary)) best = c; if (best) { b->prev = best->child; best->child = b; } } /* Insert secondary bus for each bridge */ for (b=host_bridge.chain; b; b=b->chain) if (b->br_dev && !find_bus(b, b->domain, b->secondary)) new_bus(b, b->domain, b->secondary); /* Create bus structs and link devices */ for (d=first_dev; d; d=d->next) insert_dev(d, &host_bridge); } #define LINE_BUF_SIZE 1024 static void print_it(char *line, char *p) { *p = 0; fputs(line, stdout); if (p >= line + LINE_BUF_SIZE - 1) fputs("...", stdout); putchar('\n'); for (p=line; *p; p++) if (*p == '+' || *p == '|') *p = '|'; else *p = ' '; } static void show_tree_bridge(struct pci_filter *filter, struct bridge *, char *, char *); static char * FORMAT_CHECK(printf, 3, 4) tree_printf(char *line, char *p, char *fmt, ...) { va_list args; int space = line + LINE_BUF_SIZE - 1 - p; if (space <= 0) return p; va_start(args, fmt); int res = vsnprintf(p, space, fmt, args); if (res < 0) { /* Ancient C libraries return -1 on overflow and they do not truncate the output properly. */ *p = 0; p += space; } else if (res >= space) { /* Ancient C libraries do not truncate the output properly. */ *(p+space-1) = 0; p += space; } else p += res; va_end(args); return p; } static void show_tree_dev(struct pci_filter *filter, struct device *d, char *line, char *p) { struct pci_dev *q = d->dev; struct bridge *b; char namebuf[256]; p = tree_printf(line, p, "%02x.%x", q->dev, q->func); for (b=host_bridge.chain; b; b=b->chain) if (b->br_dev == d) { if (b->secondary == 0) p = tree_printf(line, p, "-"); else if (b->secondary == b->subordinate) p = tree_printf(line, p, "-[%02x]-", b->secondary); else p = tree_printf(line, p, "-[%02x-%02x]-", b->secondary, b->subordinate); show_tree_bridge(filter, b, line, p); return; } if (verbose) p = tree_printf(line, p, " %s", pci_lookup_name(pacc, namebuf, sizeof(namebuf), PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE, q->vendor_id, q->device_id)); print_it(line, p); } static struct pci_filter * get_filter_for_child(struct pci_filter *filter, struct device *d) { if (!filter) return NULL; if (pci_filter_match(filter, d->dev)) return NULL; return filter; } static int check_bus_filter(struct pci_filter *filter, struct bus *b); static int check_dev_filter(struct pci_filter *filter, struct device *d) { struct bridge *br; struct bus *b; if (!filter) return 1; if (pci_filter_match(filter, d->dev)) return 1; for (br = host_bridge.chain; br; br = br->chain) if (br->br_dev == d) { for (b = br->first_bus; b; b = b->sibling) if (check_bus_filter(filter, b)) return 1; break; } return 0; } static int check_bus_filter(struct pci_filter *filter, struct bus *b) { struct device *d; if (!filter) return 1; for (d = b->first_dev; d; d = d->bus_next) if (check_dev_filter(filter, d)) return 1; return 0; } static void show_tree_bus(struct pci_filter *filter, struct bus *b, char *line, char *p) { if (!b->first_dev) print_it(line, p); else if (!b->first_dev->bus_next) { if (check_dev_filter(filter, b->first_dev)) { p = tree_printf(line, p, "--"); show_tree_dev(get_filter_for_child(filter, b->first_dev), b->first_dev, line, p); } else print_it(line, p); } else { int i, count = 0; struct device *d = b->first_dev; do { if (check_dev_filter(filter, d)) count++; d = d->bus_next; } while (d); for (i = 0, d = b->first_dev; d; d = d->bus_next) { if (!check_dev_filter(filter, d)) continue; char *p2 = tree_printf(line, p, count == 1 ? "--" : count == i+1 ? "\\-" : "+-"); show_tree_dev(get_filter_for_child(filter, d), d, line, p2); i++; } if (count == 0) print_it(line, p); } } static void show_tree_bridge(struct pci_filter *filter, struct bridge *b, char *line, char *p) { *p++ = '-'; if (!b->first_bus->sibling) { if (check_bus_filter(filter, b->first_bus)) { if (!b->br_dev) p = tree_printf(line, p, "[%04x:%02x]-", b->first_bus->domain, b->first_bus->number); show_tree_bus(filter, b->first_bus, line, p); } else print_it(line, p); } else { int i, count = 0; struct bus *u = b->first_bus; char *k; do { if (check_bus_filter(filter, u)) count++; u = u->sibling; } while (u); for (i = 0, u = b->first_bus; u; u = u->sibling) { if (!check_bus_filter(filter, u)) continue; k = tree_printf(line, p, count == 1 ? "[%04x:%02x]-" : count == i+1 ? "\\-[%04x:%02x]-" : "+-[%04x:%02x]-", u->domain, u->number); show_tree_bus(filter, u, line, k); i++; } if (count == 0) print_it(line, p); } } void show_forest(struct pci_filter *filter) { char line[LINE_BUF_SIZE]; struct bridge *b; if (host_bridge.child) { for (b=host_bridge.child; b->prev; b=b->prev) b->prev->next = b; for (; b; b=b->next) show_tree_bridge(filter, b, line, line); } } pciutils-3.13.0/pcilmr.man0000644000175000001440000002050514625077120013750 0ustar mjusers.TH PCILMR 8 "@TODAY@" "@VERSION@" "The PCI Utilities" .SH NAME pcilmr \- margin PCIe Links .SH SYNOPSIS .B pcilmr .RB [ "--margin" ] .RI [ "" ] " " [ "" "] [" " " [ "" ] " ..." ] .br .B pcilmr --full .RI [ "" ] .br .B pcilmr --scan .SH CONFIGURATION List of the requirements for links and system settings to run the margining test. .B BIOS settings (depends on the system, relevant for server baseboards with Xeon CPUs): .IP \[bu] 3 Turn off PCIe Leaky Bucket Feature, Re-Equalization and Link Degradation; .IP \[bu] Set Error Thresholds to 0; .IP \[bu] Intel VMD for NVMe SSDs - in case of strange behavior of the .BR pcilmr, try to run it with the VMD turned off. .PP .B Device (link) requirements: .IP .I "Configured by the user before running the utility, the utility does not change them:" .RS .IP \[bu] 3 The current Link data rate must be 16.0 GT/s or higher (right now utility supports 16 GT/s and 32 GT/s Links); .IP \[bu] Link Downstream Component must be at D0 Power Management State. .RE .IP .I "Configured by the utility during operation, utility set them to their original " .I "state after receiving the results:" .RS .IP \[bu] 3 The ASPM must be disabled in both the Downstream Port and Upstream Port; .IP \[bu] The Hardware Autonomous Speed Disable bit of the Link Control 2 register must be Set in both the Downstream Port and Upstream Port; .IP \[bu] The Hardware Autonomous Width Disable bit of the Link Control register must be Set in both the Downstream Port and Upstream Port. .SH DESCRIPTION .B pcilmr utility allows you to take advantage of the PCIe Lane Margining at the Receiver capability which is mandatory for all Ports supporting a data rate of 16.0 GT/s or higher, including Pseudo Ports (Retimers). Lane Margining at Receiver enables system software to obtain the margin information of a given Receiver while the Link is in the L0 state. The margin information includes both voltage and time, in either direction from the current Receiver position. Margining support for timing is required, while support for voltage is optional at 16.0 GT/s and required at 32.0 GT/s and higher data rates. Also, independent time margining and independent voltage margining is optional. Utility allows to get an approximation of the eye margin diagram in the form of a rhombus (by four points). Lane Margining at the Receiver capability enables users to margin PCIe links without a hardware debugger and without the need to stop the target system. Utility can be useful to debug link issues due to receiver margins. .B pcilmr requires root privileges (to access Extended Configuration Space), but during our testing there were no problems with the devices and they successfully returned to their normal initial state after the end of testing. .SH RESULTS GRADING The PCIe specification provides reference values for the eye diagram, which are also used by the .B pcilmr to evaluate the results, but it seems that it makes sense to contact the manufacturer of a particular device for references. The utility uses values set in PCIe Base Spec Rev. 5.0 Section 8.4.2 as the default eye width and height minimum references. Recommended values were taken from the PCIe Architecture PHY Test Spec Rev 5.0 (Transmitter Electrical Compliance). Reference grading values currently used by the utility are presented in the table below: .TS box tab(:); C | Cb S | Cb S C | Cb | Cb | Cb | Cb Lb | C | C | C | C. \&:16 GT/s (Gen 4):32 GT/s (Gen 5) \&:EW:EH:EW:EH _ Min:T{ 18.75 ps .br 30% UI T}:15 mV:T{ 9.375 ps .br 30% UI T}:15 mV _ Rec:T{ 23.75 ps .br 38% UI T}:21 mV:T{ 10.157 ps .br 33% UI T}:19.75 mV .TE .B pcilmr uses full eye width and height values to grade lanes. However, it is possible that device supports only one side margining. In such cases by default utility will calculate EW or EH as a double one side result. If info for specific device is available, you can configure grading criteria and tweak utility behavior in one-side only cases for your device using .I -g link specific option (see below). .SH HARDWARE QUIRKS SUPPORT Thanks to testing or directly from the manufacturer's documentation, we know that some devices require special treatment during the margining. Utility detects such devices based on their Vendor ID - Device ID pair. Right now the list of special devices is hardcoded in .I margin_hw file. For such devices utility can automatically adjust port margining parameters or grading options. For example, for Ice Lake CPUs RC ports .B pcilmr will change device MaxVoltageOffset value and will force the use of .RI ' "one side is the whole" "' grading mode." .SH OPTIONS .SS Device Specifier .B "You can specify Downstream or Upstream Port of the Link." .TP .B "" \t .RI [ "" :] : . (see .BR lspci (8)) .SS Utility Modes .TP .BI --margin " ..." Margin selected Links. .TP .B --full Margin all ready for testing (in a meaning similar to the .B --scan option) Links in the system (one by one). .TP .B --scan Scan for Links with negotiated speed 16 GT/s or higher. Mark "Ready" those of them in which at least one of the Link sides have Margining Ready bit set meaning that these Links are ready for testing and you can run utility on them. .SS Margining Common (for all specified links) options .B -c Print Device Lane Margining Capabilities only. Do not run margining. .TP .BI -e " " Specify Error Count Limit for margining. .br Default: 4. .TP .BI -o " " Save margining results in csv form into the specified directory. Utility will generate file with the name in form of .RI "\[dq]lmr_" "" "_Rx" # _ ".csv\[dq]" for each successfully tested receiver. .TP .BI -d "