1 //
2 // Copyright 2013 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 // SystemInfo_libpci.cpp: implementation of the libPCI-specific parts of SystemInfo.h
8
9 #include "gpu_info_util/SystemInfo_internal.h"
10
11 #include <dlfcn.h>
12 #include <pci/pci.h>
13 #include <unistd.h>
14
15 #include "common/angleutils.h"
16 #include "common/debug.h"
17
18 #if !defined(GPU_INFO_USE_LIBPCI)
19 # error SystemInfo_libpci.cpp compiled without GPU_INFO_USE_LIBPCI
20 #endif
21
22 namespace angle
23 {
24
25 namespace
26 {
27
28 struct LibPCI : private angle::NonCopyable
29 {
LibPCIangle::__anonbd9a802b0111::LibPCI30 LibPCI()
31 {
32 if (access("/sys/bus/pci/", F_OK) != 0 && access("/sys/bus/pci_express/", F_OK) != 0)
33 {
34 return;
35 }
36
37 mHandle = dlopen("libpci.so.3", RTLD_LAZY);
38
39 if (mHandle == nullptr)
40 {
41 mHandle = dlopen("libpci.so", RTLD_LAZY);
42 }
43
44 if (mHandle == nullptr)
45 {
46 return;
47 }
48
49 mValid =
50 (Alloc = reinterpret_cast<decltype(Alloc)>(dlsym(mHandle, "pci_alloc"))) != nullptr &&
51 (Init = reinterpret_cast<decltype(Init)>(dlsym(mHandle, "pci_init"))) != nullptr &&
52 (Cleanup = reinterpret_cast<decltype(Cleanup)>(dlsym(mHandle, "pci_cleanup"))) !=
53 nullptr &&
54 (ScanBus = reinterpret_cast<decltype(ScanBus)>(dlsym(mHandle, "pci_scan_bus"))) !=
55 nullptr &&
56 (FillInfo = reinterpret_cast<decltype(FillInfo)>(dlsym(mHandle, "pci_fill_info"))) !=
57 nullptr &&
58 (LookupName = reinterpret_cast<decltype(LookupName)>(
59 dlsym(mHandle, "pci_lookup_name"))) != nullptr &&
60 (PCIReadByte = reinterpret_cast<decltype(PCIReadByte)>(
61 dlsym(mHandle, "pci_read_byte"))) != nullptr;
62 }
63
IsValidangle::__anonbd9a802b0111::LibPCI64 bool IsValid() const { return mValid; }
65
~LibPCIangle::__anonbd9a802b0111::LibPCI66 ~LibPCI()
67 {
68 if (mHandle != nullptr)
69 {
70 dlclose(mHandle);
71 }
72 }
73
74 decltype(&::pci_alloc) Alloc = nullptr;
75 decltype(&::pci_init) Init = nullptr;
76 decltype(&::pci_cleanup) Cleanup = nullptr;
77 decltype(&::pci_scan_bus) ScanBus = nullptr;
78 decltype(&::pci_fill_info) FillInfo = nullptr;
79 decltype(&::pci_lookup_name) LookupName = nullptr;
80 decltype(&::pci_read_byte) PCIReadByte = nullptr;
81
82 private:
83 void *mHandle = nullptr;
84 bool mValid = false;
85 };
86
87 } // anonymous namespace
88
89 // Adds an entry per PCI GPU found and fills the device and vendor ID.
GetPCIDevicesWithLibPCI(std::vector<GPUDeviceInfo> * devices)90 bool GetPCIDevicesWithLibPCI(std::vector<GPUDeviceInfo> *devices)
91 {
92 LibPCI pci;
93 if (!pci.IsValid())
94 {
95 return false;
96 }
97
98 pci_access *access = pci.Alloc();
99 ASSERT(access != nullptr);
100 pci.Init(access);
101 pci.ScanBus(access);
102
103 for (pci_dev *device = access->devices; device != nullptr; device = device->next)
104 {
105 pci.FillInfo(device, PCI_FILL_IDENT | PCI_FILL_CLASS);
106
107 // Skip non-GPU devices
108 if (device->device_class >> 8 != PCI_BASE_CLASS_DISPLAY)
109 {
110 continue;
111 }
112
113 // Skip unknown devices
114 if (device->vendor_id == 0 || device->device_id == 0)
115 {
116 continue;
117 }
118
119 GPUDeviceInfo info;
120 info.vendorId = device->vendor_id;
121 info.deviceId = device->device_id;
122 info.revisionId = pci.PCIReadByte(device, PCI_REVISION_ID);
123
124 devices->push_back(info);
125 }
126
127 pci.Cleanup(access);
128
129 return true;
130 }
131 } // namespace angle
132