1 //
2 // Copyright (c) 2013-2017 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::__anond751c6210111::LibPCI30     LibPCI()
31     {
32         if (access("/sys/bus/pci/", F_OK) != 0 && access("/sys/bs/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     }
61 
IsValidangle::__anond751c6210111::LibPCI62     bool IsValid() const { return mValid; }
63 
~LibPCIangle::__anond751c6210111::LibPCI64     ~LibPCI()
65     {
66         if (mHandle != nullptr)
67         {
68             dlclose(mHandle);
69         }
70     }
71 
72     decltype(&::pci_alloc) Alloc            = nullptr;
73     decltype(&::pci_init) Init              = nullptr;
74     decltype(&::pci_cleanup) Cleanup        = nullptr;
75     decltype(&::pci_scan_bus) ScanBus       = nullptr;
76     decltype(&::pci_fill_info) FillInfo     = nullptr;
77     decltype(&::pci_lookup_name) LookupName = nullptr;
78 
79   private:
80     void *mHandle = nullptr;
81     bool mValid   = false;
82 };
83 
84 }  // anonymous namespace
85 
86 // Adds an entry per PCI GPU found and fills the device and vendor ID.
GetPCIDevicesWithLibPCI(std::vector<GPUDeviceInfo> * devices)87 bool GetPCIDevicesWithLibPCI(std::vector<GPUDeviceInfo> *devices)
88 {
89     LibPCI pci;
90     if (!pci.IsValid())
91     {
92         return false;
93     }
94 
95     pci_access *access = pci.Alloc();
96     ASSERT(access != nullptr);
97     pci.Init(access);
98     pci.ScanBus(access);
99 
100     for (pci_dev *device = access->devices; device != nullptr; device = device->next)
101     {
102         pci.FillInfo(device, PCI_FILL_IDENT | PCI_FILL_CLASS);
103 
104         // Skip non-GPU devices
105         switch (device->device_class)
106         {
107             case PCI_CLASS_DISPLAY_VGA:
108             case PCI_CLASS_DISPLAY_XGA:
109             case PCI_CLASS_DISPLAY_3D:
110                 break;
111             default:
112                 continue;
113         }
114 
115         // Skip unknown devices
116         if (device->vendor_id == 0 || device->device_id == 0)
117         {
118             continue;
119         }
120 
121         GPUDeviceInfo info;
122         info.vendorId = device->vendor_id;
123         info.deviceId = device->device_id;
124 
125         devices->push_back(info);
126     }
127 
128     pci.Cleanup(access);
129 
130     return true;
131 }
132 }
133