|
| 1 | +/* |
| 2 | + * Copyright (C) 2023 Intel Corporation |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: MIT |
| 5 | + * |
| 6 | + */ |
| 7 | + |
| 8 | +#include "level_zero/sysman/source/pci/linux/os_pci_imp.h" |
| 9 | + |
| 10 | +#include "shared/source/debug_settings/debug_settings_manager.h" |
| 11 | +#include "shared/source/utilities/directory.h" |
| 12 | + |
| 13 | +#include "level_zero/sysman/source/pci/pci_imp.h" |
| 14 | +#include "level_zero/sysman/source/sysman_const.h" |
| 15 | + |
| 16 | +#include <linux/pci_regs.h> |
| 17 | + |
| 18 | +namespace L0 { |
| 19 | +namespace Sysman { |
| 20 | + |
| 21 | +const std::string LinuxPciImp::deviceDir("device"); |
| 22 | +const std::string LinuxPciImp::resourceFile("device/resource"); |
| 23 | + |
| 24 | +ze_result_t LinuxPciImp::getProperties(zes_pci_properties_t *properties) { |
| 25 | + properties->haveBandwidthCounters = false; |
| 26 | + properties->havePacketCounters = false; |
| 27 | + properties->haveReplayCounters = false; |
| 28 | + return ZE_RESULT_SUCCESS; |
| 29 | +} |
| 30 | +ze_result_t LinuxPciImp::getPciBdf(zes_pci_properties_t &pciProperties) { |
| 31 | + std::string bdfDir; |
| 32 | + ze_result_t result = pSysfsAccess->readSymLink(deviceDir, bdfDir); |
| 33 | + if (ZE_RESULT_SUCCESS != result) { |
| 34 | + NEO::printDebugString(NEO::DebugManager.flags.PrintDebugMessages.get(), stderr, "Error@ %s(): readSymLink() failed to retrive BDF from %s and returning error:0x%x \n", __FUNCTION__, deviceDir.c_str(), result); |
| 35 | + return result; |
| 36 | + } |
| 37 | + const auto loc = bdfDir.find_last_of('/'); |
| 38 | + std::string bdf = bdfDir.substr(loc + 1); |
| 39 | + uint16_t domain = 0; |
| 40 | + uint8_t bus = 0, device = 0, function = 0; |
| 41 | + NEO::parseBdfString(bdf.c_str(), domain, bus, device, function); |
| 42 | + pciProperties.address.domain = static_cast<uint32_t>(domain); |
| 43 | + pciProperties.address.bus = static_cast<uint32_t>(bus); |
| 44 | + pciProperties.address.device = static_cast<uint32_t>(device); |
| 45 | + pciProperties.address.function = static_cast<uint32_t>(function); |
| 46 | + return ZE_RESULT_SUCCESS; |
| 47 | +} |
| 48 | + |
| 49 | +void LinuxPciImp::getMaxLinkCaps(double &maxLinkSpeed, int32_t &maxLinkWidth) { |
| 50 | + uint16_t linkCaps = 0; |
| 51 | + auto linkCapPos = getLinkCapabilityPos(); |
| 52 | + if (!linkCapPos) { |
| 53 | + maxLinkSpeed = 0; |
| 54 | + maxLinkWidth = -1; |
| 55 | + return; |
| 56 | + } |
| 57 | + |
| 58 | + linkCaps = getWordFromConfig(linkCapPos, (!(isIntegratedDevice)) ? uspConfigMemory.get() : configMemory.get()); |
| 59 | + maxLinkSpeed = convertPciGenToLinkSpeed(BITS(linkCaps, 0, 4)); |
| 60 | + maxLinkWidth = BITS(linkCaps, 4, 6); |
| 61 | + |
| 62 | + return; |
| 63 | +} |
| 64 | + |
| 65 | +void getBarBaseAndSize(std::string readBytes, uint64_t &baseAddr, uint64_t &barSize, uint64_t &barFlags) { |
| 66 | + unsigned long long start, end, flags; |
| 67 | + std::stringstream sStreamReadBytes; |
| 68 | + sStreamReadBytes << readBytes; |
| 69 | + sStreamReadBytes >> std::hex >> start; |
| 70 | + sStreamReadBytes >> end; |
| 71 | + sStreamReadBytes >> flags; |
| 72 | + |
| 73 | + flags &= 0xf; |
| 74 | + barFlags = flags; |
| 75 | + baseAddr = start; |
| 76 | + barSize = end - start + 1; |
| 77 | +} |
| 78 | + |
| 79 | +ze_result_t LinuxPciImp::initializeBarProperties(std::vector<zes_pci_bar_properties_t *> &pBarProperties) { |
| 80 | + std::vector<std::string> readBytes; |
| 81 | + ze_result_t result = pSysfsAccess->read(resourceFile, readBytes); |
| 82 | + if (result != ZE_RESULT_SUCCESS) { |
| 83 | + NEO::printDebugString(NEO::DebugManager.flags.PrintDebugMessages.get(), stderr, "Error@ %s(): read() failed to read %s and returning error:0x%x \n", __FUNCTION__, resourceFile.c_str(), result); |
| 84 | + return result; |
| 85 | + } |
| 86 | + for (uint32_t i = 0; i <= maxPciBars; i++) { |
| 87 | + uint64_t baseAddr, barSize, barFlags; |
| 88 | + getBarBaseAndSize(readBytes[i], baseAddr, barSize, barFlags); |
| 89 | + if (baseAddr && !(barFlags & 0x1)) { // we do not update for I/O ports |
| 90 | + zes_pci_bar_properties_t *pBarProp = new zes_pci_bar_properties_t; |
| 91 | + memset(pBarProp, 0, sizeof(zes_pci_bar_properties_t)); |
| 92 | + pBarProp->index = i; |
| 93 | + pBarProp->base = baseAddr; |
| 94 | + pBarProp->size = barSize; |
| 95 | + // Bar Flags Desc. |
| 96 | + // Bit-0 - Value 0x0 -> MMIO type BAR |
| 97 | + // Bit-0 - Value 0x1 -> I/O type BAR |
| 98 | + if (i == 0) { // GRaphics MMIO is at BAR0, and is a 64-bit |
| 99 | + pBarProp->type = ZES_PCI_BAR_TYPE_MMIO; |
| 100 | + } |
| 101 | + if (i == 2) { |
| 102 | + pBarProp->type = ZES_PCI_BAR_TYPE_MEM; // device memory is always at BAR2 |
| 103 | + } |
| 104 | + if (i == 6) { // the 7th entry of resource file is expected to be ROM BAR |
| 105 | + pBarProp->type = ZES_PCI_BAR_TYPE_ROM; |
| 106 | + } |
| 107 | + pBarProperties.push_back(pBarProp); |
| 108 | + } |
| 109 | + } |
| 110 | + if (pBarProperties.size() == 0) { |
| 111 | + NEO::printDebugString(NEO::DebugManager.flags.PrintDebugMessages.get(), stderr, "Error@ %s(): BarProperties = %d and returning error:0x%x \n", __FUNCTION__, pBarProperties.size(), result); |
| 112 | + result = ZE_RESULT_ERROR_UNKNOWN; |
| 113 | + } |
| 114 | + return result; |
| 115 | +} |
| 116 | + |
| 117 | +uint32_t LinuxPciImp::getRebarCapabilityPos() { |
| 118 | + uint32_t pos = PCI_CFG_SPACE_SIZE; |
| 119 | + uint32_t header = 0; |
| 120 | + |
| 121 | + if (!configMemory) { |
| 122 | + return 0; |
| 123 | + } |
| 124 | + |
| 125 | + // Minimum 8 bytes per capability. Hence maximum capabilities that |
| 126 | + // could be present in PCI extended configuration space are |
| 127 | + // represented by loopCount. |
| 128 | + auto loopCount = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8; |
| 129 | + header = getDwordFromConfig(pos); |
| 130 | + if (!header) { |
| 131 | + return 0; |
| 132 | + } |
| 133 | + |
| 134 | + while (loopCount-- > 0) { |
| 135 | + if (PCI_EXT_CAP_ID(header) == PCI_EXT_CAP_ID_REBAR) { |
| 136 | + return pos; |
| 137 | + } |
| 138 | + pos = PCI_EXT_CAP_NEXT(header); |
| 139 | + if (pos < PCI_CFG_SPACE_SIZE) { |
| 140 | + return 0; |
| 141 | + } |
| 142 | + header = getDwordFromConfig(pos); |
| 143 | + } |
| 144 | + return 0; |
| 145 | +} |
| 146 | + |
| 147 | +uint16_t LinuxPciImp::getLinkCapabilityPos() { |
| 148 | + uint16_t pos = PCI_CAPABILITY_LIST; |
| 149 | + uint8_t id, type = 0; |
| 150 | + uint16_t capRegister = 0; |
| 151 | + uint8_t *configMem = (!(isIntegratedDevice)) ? uspConfigMemory.get() : configMemory.get(); |
| 152 | + |
| 153 | + if ((!configMem) || (!(getByteFromConfig(PCI_STATUS, configMem) & PCI_STATUS_CAP_LIST))) { |
| 154 | + return 0; |
| 155 | + } |
| 156 | + |
| 157 | + // Minimum 8 bytes per capability. Hence maximum capabilities that |
| 158 | + // could be present in PCI configuration space are |
| 159 | + // represented by loopCount. |
| 160 | + auto loopCount = (PCI_CFG_SPACE_SIZE) / 8; |
| 161 | + |
| 162 | + // Bottom two bits of capability pointer register are reserved and |
| 163 | + // software should mask these bits to get pointer to capability list. |
| 164 | + |
| 165 | + pos = getByteFromConfig(pos, configMem) & 0xfc; |
| 166 | + while (loopCount-- > 0) { |
| 167 | + id = getByteFromConfig(pos + PCI_CAP_LIST_ID, configMem); |
| 168 | + if (id == PCI_CAP_ID_EXP) { |
| 169 | + capRegister = getWordFromConfig(pos + PCI_CAP_FLAGS, configMem); |
| 170 | + type = BITS(capRegister, 4, 4); |
| 171 | + |
| 172 | + // Root Complex Integrated end point and |
| 173 | + // Root Complex Event collector will not implement link capabilities |
| 174 | + |
| 175 | + if ((type != PCI_EXP_TYPE_RC_END) && (type != PCI_EXP_TYPE_RC_EC)) { |
| 176 | + return pos + PCI_EXP_LNKCAP; |
| 177 | + } |
| 178 | + } |
| 179 | + |
| 180 | + pos = getByteFromConfig(pos + PCI_CAP_LIST_NEXT, configMem) & 0xfc; |
| 181 | + if (!pos) { |
| 182 | + break; |
| 183 | + } |
| 184 | + } |
| 185 | + return 0; |
| 186 | +} |
| 187 | + |
| 188 | +// Parse PCIe configuration space to see if resizable Bar is supported |
| 189 | +bool LinuxPciImp::resizableBarSupported() { |
| 190 | + return (getRebarCapabilityPos() > 0); |
| 191 | +} |
| 192 | + |
| 193 | +bool LinuxPciImp::resizableBarEnabled(uint32_t barIndex) { |
| 194 | + bool isBarResizable = false; |
| 195 | + uint32_t capabilityRegister = 0, controlRegister = 0; |
| 196 | + uint32_t nBars = 1; |
| 197 | + auto rebarCapabilityPos = getRebarCapabilityPos(); |
| 198 | + |
| 199 | + // If resizable Bar is not supported then return false. |
| 200 | + if (!rebarCapabilityPos) { |
| 201 | + return false; |
| 202 | + } |
| 203 | + |
| 204 | + // As per PCI spec, resizable BAR's capability structure's 52 byte length could be represented as: |
| 205 | + // -------------------------------------------------------------- |
| 206 | + // | byte offset | Description of register | |
| 207 | + // -------------------------------------------------------------| |
| 208 | + // | +000h | PCI Express Extended Capability Header | |
| 209 | + // -------------------------------------------------------------| |
| 210 | + // | +004h | Resizable BAR Capability Register (0) | |
| 211 | + // -------------------------------------------------------------| |
| 212 | + // | +008h | Resizable BAR Control Register (0) | |
| 213 | + // -------------------------------------------------------------| |
| 214 | + // | +00Ch | Resizable BAR Capability Register (1) | |
| 215 | + // -------------------------------------------------------------| |
| 216 | + // | +010h | Resizable BAR Control Register (1) | |
| 217 | + // -------------------------------------------------------------| |
| 218 | + // | +014h | --- | |
| 219 | + // -------------------------------------------------------------| |
| 220 | + |
| 221 | + // Only first Control register(at offset 008h, as shown above), could tell about number of resizable Bars |
| 222 | + controlRegister = getDwordFromConfig(rebarCapabilityPos + PCI_REBAR_CTRL); |
| 223 | + nBars = BITS(controlRegister, 5, 3); // control register's bits 5,6 and 7 contain number of resizable bars information |
| 224 | + for (auto barNumber = 0u; barNumber < nBars; barNumber++) { |
| 225 | + uint32_t barId = 0; |
| 226 | + controlRegister = getDwordFromConfig(rebarCapabilityPos + PCI_REBAR_CTRL); |
| 227 | + barId = BITS(controlRegister, 0, 3); // Control register's bit 0,1,2 tells the index of bar |
| 228 | + if (barId == barIndex) { |
| 229 | + isBarResizable = true; |
| 230 | + break; |
| 231 | + } |
| 232 | + rebarCapabilityPos += 8; |
| 233 | + } |
| 234 | + |
| 235 | + if (isBarResizable == false) { |
| 236 | + return false; |
| 237 | + } |
| 238 | + |
| 239 | + capabilityRegister = getDwordFromConfig(rebarCapabilityPos + PCI_REBAR_CAP); |
| 240 | + // Capability register's bit 4 to 31 indicates supported Bar sizes. |
| 241 | + // In possibleBarSizes, position of each set bit indicates supported bar size. Example, if set bit |
| 242 | + // position of possibleBarSizes is from 0 to n, then this indicates BAR size from 2^0 MB to 2^n MB |
| 243 | + auto possibleBarSizes = (capabilityRegister & PCI_REBAR_CAP_SIZES) >> 4; // First 4 bits are reserved |
| 244 | + uint32_t largestPossibleBarSize = 0; |
| 245 | + while (possibleBarSizes >>= 1) { // most significant set bit position of possibleBarSizes would tell larget possible bar size |
| 246 | + largestPossibleBarSize++; |
| 247 | + } |
| 248 | + |
| 249 | + // Control register's bit 8 to 13 indicates current BAR size in encoded form. |
| 250 | + // Example, real value of current size could be 2^currentSize MB |
| 251 | + auto currentSize = BITS(controlRegister, 8, 6); |
| 252 | + |
| 253 | + // If current size is equal to larget possible BAR size, it indicates resizable BAR is enabled. |
| 254 | + return (currentSize == largestPossibleBarSize); |
| 255 | +} |
| 256 | + |
| 257 | +ze_result_t LinuxPciImp::getState(zes_pci_state_t *state) { |
| 258 | + NEO::printDebugString(NEO::DebugManager.flags.PrintDebugMessages.get(), stderr, "Error@ %s() returning UNSUPPORTED_FEATURE \n", __FUNCTION__); |
| 259 | + return ZE_RESULT_ERROR_UNSUPPORTED_FEATURE; |
| 260 | +} |
| 261 | + |
| 262 | +void LinuxPciImp::pciExtendedConfigRead() { |
| 263 | + std::string pciConfigNode; |
| 264 | + pSysfsAccess->getRealPath("device/config", pciConfigNode); |
| 265 | + |
| 266 | + int fdConfig = -1; |
| 267 | + fdConfig = this->openFunction(pciConfigNode.c_str(), O_RDONLY); |
| 268 | + if (fdConfig < 0) { |
| 269 | + return; |
| 270 | + } |
| 271 | + configMemory = std::make_unique<uint8_t[]>(PCI_CFG_SPACE_EXP_SIZE); |
| 272 | + memset(configMemory.get(), 0, PCI_CFG_SPACE_EXP_SIZE); |
| 273 | + this->preadFunction(fdConfig, configMemory.get(), PCI_CFG_SPACE_EXP_SIZE, 0); |
| 274 | + this->closeFunction(fdConfig); |
| 275 | +} |
| 276 | + |
| 277 | +void LinuxPciImp::pciCardBusConfigRead() { |
| 278 | + std::string pciConfigNode; |
| 279 | + std::string rootPortPath; |
| 280 | + pSysfsAccess->getRealPath(deviceDir, pciConfigNode); |
| 281 | + rootPortPath = pLinuxSysmanImp->getPciCardBusDirectoryPath(pciConfigNode); |
| 282 | + pciConfigNode = rootPortPath + "/config"; |
| 283 | + int fdConfig = -1; |
| 284 | + fdConfig = this->openFunction(pciConfigNode.c_str(), O_RDONLY); |
| 285 | + if (fdConfig < 0) { |
| 286 | + return; |
| 287 | + } |
| 288 | + uspConfigMemory = std::make_unique<uint8_t[]>(PCI_CFG_SPACE_SIZE); |
| 289 | + memset(uspConfigMemory.get(), 0, PCI_CFG_SPACE_SIZE); |
| 290 | + this->preadFunction(fdConfig, uspConfigMemory.get(), PCI_CFG_SPACE_SIZE, 0); |
| 291 | + this->closeFunction(fdConfig); |
| 292 | +} |
| 293 | + |
| 294 | +LinuxPciImp::LinuxPciImp(OsSysman *pOsSysman) { |
| 295 | + pLinuxSysmanImp = static_cast<LinuxSysmanImp *>(pOsSysman); |
| 296 | + pSysfsAccess = &pLinuxSysmanImp->getSysfsAccess(); |
| 297 | + isIntegratedDevice = pLinuxSysmanImp->getSysmanDeviceImp()->getRootDeviceEnvironment().getHardwareInfo()->capabilityTable.isIntegratedDevice; |
| 298 | + |
| 299 | + if (pSysfsAccess->isRootUser()) { |
| 300 | + pciExtendedConfigRead(); |
| 301 | + pciCardBusConfigRead(); |
| 302 | + } |
| 303 | +} |
| 304 | + |
| 305 | +OsPci *OsPci::create(OsSysman *pOsSysman) { |
| 306 | + LinuxPciImp *pLinuxPciImp = new LinuxPciImp(pOsSysman); |
| 307 | + return static_cast<OsPci *>(pLinuxPciImp); |
| 308 | +} |
| 309 | + |
| 310 | +} // namespace Sysman |
| 311 | +} // namespace L0 |
0 commit comments