blob: 0b1603efaae7ad5a0cf9bcc6dc4f1a4c4e67ee80 [file] [log] [blame]
Jason M. Billsd1e40602019-05-09 11:43:51 -07001/*
2// Copyright (c) 2019 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
17#include "peci_pcie.hpp"
18
19#include "pciDeviceClass.hpp"
20#include "pciVendors.hpp"
21
Jason M. Billsbce86a62020-10-08 16:03:47 -070022#include <boost/asio/io_service.hpp>
23#include <boost/asio/steady_timer.hpp>
Jason M. Billsd1e40602019-05-09 11:43:51 -070024#include <boost/container/flat_map.hpp>
25#include <boost/container/flat_set.hpp>
26#include <sdbusplus/asio/object_server.hpp>
Jason M. Billsee6d80b2021-06-11 07:37:30 -070027#include <sdbusplus/bus/match.hpp>
Jason M. Billsd1e40602019-05-09 11:43:51 -070028
29#include <iomanip>
30#include <iostream>
31#include <set>
32#include <sstream>
33
34namespace peci_pcie
35{
36static boost::container::flat_map<
37 int, boost::container::flat_map<
38 int, boost::container::flat_map<
39 int, std::shared_ptr<sdbusplus::asio::dbus_interface>>>>
40 pcieDeviceDBusMap;
41
Jason M. Billsee6d80b2021-06-11 07:37:30 -070042static bool abortScan;
43
Jason M. Billsd1e40602019-05-09 11:43:51 -070044namespace function
45{
46static constexpr char const* functionTypeName = "FunctionType";
47static constexpr char const* deviceClassName = "DeviceClass";
48static constexpr char const* vendorIdName = "VendorId";
49static constexpr char const* deviceIdName = "DeviceId";
50static constexpr char const* classCodeName = "ClassCode";
51static constexpr char const* revisionIdName = "RevisionId";
52static constexpr char const* subsystemIdName = "SubsystemId";
53static constexpr char const* subsystemVendorIdName = "SubsystemVendorId";
54} // namespace function
55} // namespace peci_pcie
56
Andrei Kartashevd570dfd2020-12-16 17:33:16 +030057enum class resCode
58{
59 resOk,
60 resErr
61};
62
Jason M. Bills5d049732019-10-25 15:55:15 -070063struct CPUInfo
Jason M. Billsd1e40602019-05-09 11:43:51 -070064{
Jason M. Bills5d049732019-10-25 15:55:15 -070065 size_t addr;
66 bool skipCpuBuses;
67 boost::container::flat_set<size_t> cpuBusNums;
68};
69
70// PECI Client Address Map
71static void getClientAddrMap(std::vector<CPUInfo>& cpuInfo)
72{
Andrei Kartashevd570dfd2020-12-16 17:33:16 +030073 cpuInfo.clear();
Jason M. Bills5d049732019-10-25 15:55:15 -070074 for (size_t i = MIN_CLIENT_ADDR; i <= MAX_CLIENT_ADDR; i++)
Jason M. Billsd1e40602019-05-09 11:43:51 -070075 {
76 if (peci_Ping(i) == PECI_CC_SUCCESS)
77 {
Jason M. Bills5d049732019-10-25 15:55:15 -070078 cpuInfo.emplace_back(CPUInfo{i, false, {}});
79 }
80 }
81}
82
83// Get CPU PCIe Bus Numbers
Andrei Kartashevd570dfd2020-12-16 17:33:16 +030084static resCode getCPUBusNums(std::vector<CPUInfo>& cpuInfo)
Jason M. Bills5d049732019-10-25 15:55:15 -070085{
86 for (CPUInfo& cpu : cpuInfo)
87 {
88 uint8_t cc = 0;
89 CPUModel model{};
90 uint8_t stepping = 0;
91 if (peci_GetCPUID(cpu.addr, &model, &stepping, &cc) != PECI_CC_SUCCESS)
92 {
93 std::cerr << "Cannot get CPUID!\n";
Andrei Kartashevd570dfd2020-12-16 17:33:16 +030094 return resCode::resErr;
Jason M. Bills5d049732019-10-25 15:55:15 -070095 }
96
97 switch (model)
98 {
99 case skx:
100 {
101 // Get the assigned CPU bus numbers from CPUBUSNO and CPUBUSNO1
102 // (B(0) D8 F2 offsets CCh and D0h)
103 uint32_t cpuBusNum = 0;
104 if (peci_RdPCIConfigLocal(cpu.addr, 0, 8, 2, 0xCC, 4,
105 (uint8_t*)&cpuBusNum,
106 &cc) != PECI_CC_SUCCESS)
107 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300108 return resCode::resErr;
Jason M. Bills5d049732019-10-25 15:55:15 -0700109 }
110 uint32_t cpuBusNum1 = 0;
111 if (peci_RdPCIConfigLocal(cpu.addr, 0, 8, 2, 0xD0, 4,
112 (uint8_t*)&cpuBusNum1,
113 &cc) != PECI_CC_SUCCESS)
114 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300115 return resCode::resErr;
Jason M. Bills5d049732019-10-25 15:55:15 -0700116 }
117
118 // Add the CPU bus numbers to the set for this CPU
119 while (cpuBusNum)
120 {
121 // Get the LSB
122 size_t busNum = cpuBusNum & 0xFF;
123 cpu.cpuBusNums.insert(busNum);
124 // Shift right by one byte
125 cpuBusNum >>= 8;
126 }
127 while (cpuBusNum1)
128 {
129 // Get the LSB
130 size_t busNum = cpuBusNum1 & 0xFF;
131 cpu.cpuBusNums.insert(busNum);
132 // Shift right by one byte
Zev Weiss9fa54b52020-09-02 21:20:33 +0000133 cpuBusNum1 >>= 8;
Jason M. Bills5d049732019-10-25 15:55:15 -0700134 }
135 cpu.skipCpuBuses = true;
136 }
Jason M. Billsd1e40602019-05-09 11:43:51 -0700137 }
138 }
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300139 return resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700140}
141
142static bool isPECIAvailable(void)
143{
Jason M. Bills5d049732019-10-25 15:55:15 -0700144 for (size_t i = MIN_CLIENT_ADDR; i <= MAX_CLIENT_ADDR; i++)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700145 {
Jason M. Bills5d049732019-10-25 15:55:15 -0700146 if (peci_Ping(i) == PECI_CC_SUCCESS)
147 {
148 return true;
149 }
Jason M. Billsd1e40602019-05-09 11:43:51 -0700150 }
Jason M. Bills5d049732019-10-25 15:55:15 -0700151 return false;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700152}
153
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300154static resCode getDataFromPCIeConfig(const int& clientAddr, const int& bus,
155 const int& dev, const int& func,
156 const int& offset, const int& size,
157 uint32_t& pciData)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700158{
159 // PECI RdPCIConfig() currently only supports 4 byte reads, so adjust
160 // the offset and size to get the right data
161 static constexpr const int pciReadSize = 4;
162 int mod = offset % pciReadSize;
163 int pciOffset = offset - mod;
164 if (mod + size > pciReadSize)
165 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300166 return resCode::resErr;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700167 }
168
169 std::array<uint8_t, pciReadSize> data;
170 uint8_t cc;
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300171 int ret = PECI_CC_TIMEOUT;
172 for (int index = 0; (index < 5) && (ret == PECI_CC_TIMEOUT); index++)
173 {
Jason M. Bills3b1665a2021-06-11 13:35:04 -0700174#ifdef USE_RDENDPOINTCFG
175 ret = peci_RdEndPointConfigPci(clientAddr, // CPU Address
176 0, // PCI Seg (use 0 for now)
177 bus, // PCI Bus
178 dev, // PCI Device
179 func, // PCI Function
180 pciOffset, // PCI Offset
181 pciReadSize, // PCI Read Size
182 data.data(), // PCI Read Data
183 &cc); // PECI Completion Code
184#else
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300185 ret = peci_RdPCIConfig(clientAddr, // CPU Address
Jason M. Billsd1e40602019-05-09 11:43:51 -0700186 bus, // PCI Bus
187 dev, // PCI Device
188 func, // PCI Function
189 pciOffset, // PCI Offset
190 data.data(), // PCI Read Data
191 &cc); // PECI Completion Code
Jason M. Bills3b1665a2021-06-11 13:35:04 -0700192#endif
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300193 }
Jason M. Billsd1e40602019-05-09 11:43:51 -0700194 if (ret != PECI_CC_SUCCESS || cc != PECI_DEV_CC_SUCCESS)
195 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300196 return resCode::resErr;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700197 }
198
199 // Now build the requested data into a single number
200 pciData = 0;
201 for (int i = mod; i < mod + size; i++)
202 {
Jason M. Billse55832b2021-06-14 16:00:49 -0700203 pciData |= static_cast<uint32_t>(data[i]) << 8 * (i - mod);
Jason M. Billsd1e40602019-05-09 11:43:51 -0700204 }
205
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300206 return resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700207}
208
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300209static resCode getStringFromPCIeConfig(const int& clientAddr, const int& bus,
210 const int& dev, const int& func,
211 const int& offset, const int& size,
212 std::string& res)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700213{
214 // Get the requested data
215 uint32_t data = 0;
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300216 if (getDataFromPCIeConfig(clientAddr, bus, dev, func, offset, size, data) !=
217 resCode::resOk)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700218 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300219 return resCode::resErr;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700220 }
221
222 // And convert it to a string
223 std::stringstream dataStream;
224 dataStream << "0x" << std::hex << std::setfill('0') << std::setw(size * 2)
225 << data;
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300226 res = dataStream.str();
227 return resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700228}
229
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300230static resCode getVendorName(const int& clientAddr, const int& bus,
231 const int& dev, std::string& res)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700232{
233 static constexpr const int vendorIDOffset = 0x00;
234 static constexpr const int vendorIDSize = 2;
235
236 // Get the header type register from function 0
237 uint32_t vendorID = 0;
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300238 if (getDataFromPCIeConfig(clientAddr, bus, dev, 0, vendorIDOffset,
239 vendorIDSize, vendorID) != resCode::resOk)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700240 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300241 return resCode::resErr;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700242 }
243 // Get the vendor name or use Other if it doesn't exist
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300244 res = pciVendors.try_emplace(vendorID, otherVendor).first->second;
245 return resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700246}
247
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300248static resCode getDeviceClass(const int& clientAddr, const int& bus,
249 const int& dev, const int& func, std::string& res)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700250{
251 static constexpr const int baseClassOffset = 0x0b;
252 static constexpr const int baseClassSize = 1;
253
254 // Get the Device Base Class
255 uint32_t baseClass = 0;
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300256 if (getDataFromPCIeConfig(clientAddr, bus, dev, func, baseClassOffset,
257 baseClassSize, baseClass) != resCode::resOk)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700258 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300259 return resCode::resErr;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700260 }
261 // Get the base class name or use Other if it doesn't exist
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300262 res = pciDeviceClasses.try_emplace(baseClass, otherClass).first->second;
263 return resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700264}
265
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300266static resCode isMultiFunction(const int& clientAddr, const int& bus,
267 const int& dev, bool& res)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700268{
269 static constexpr const int headerTypeOffset = 0x0e;
270 static constexpr const int headerTypeSize = 1;
271 static constexpr const int multiFuncBit = 1 << 7;
272
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300273 res = false;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700274 // Get the header type register from function 0
275 uint32_t headerType = 0;
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300276 if (getDataFromPCIeConfig(clientAddr, bus, dev, 0, headerTypeOffset,
277 headerTypeSize, headerType) != resCode::resOk)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700278 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300279 return resCode::resErr;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700280 }
281 // Check if it's a multifunction device
282 if (headerType & multiFuncBit)
283 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300284 res = true;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700285 }
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300286 return resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700287}
288
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300289static resCode pcieFunctionExists(const int& clientAddr, const int& bus,
290 const int& dev, const int& func, bool& res)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700291{
292 constexpr const int pciIDOffset = 0;
293 constexpr const int pciIDSize = 4;
294 uint32_t pciID = 0;
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300295 res = false;
296 if (getDataFromPCIeConfig(clientAddr, bus, dev, func, pciIDOffset,
297 pciIDSize, pciID) != resCode::resOk)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700298 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300299 return resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700300 }
301
302 // if VID and DID are all 0s or 1s, then the device doesn't exist
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300303 if (pciID != 0x00000000 && pciID != 0xFFFFFFFF)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700304 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300305 res = true;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700306 }
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300307 return resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700308}
309
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300310static resCode pcieDeviceExists(const int& clientAddr, const int& bus,
311 const int& dev, bool& res)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700312{
313 // Check if this device exists by checking function 0
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300314 return pcieFunctionExists(clientAddr, bus, dev, 0, res);
Jason M. Billsd1e40602019-05-09 11:43:51 -0700315}
316
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300317static resCode setPCIeProperty(const int& clientAddr, const int& bus,
318 const int& dev, const std::string& propertyName,
319 const std::string& propertyValue)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700320{
321 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
322 peci_pcie::pcieDeviceDBusMap[clientAddr][bus][dev];
323
324 if (iface->is_initialized())
325 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300326 if (!iface->set_property(propertyName, propertyValue))
327 return resCode::resErr;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700328 }
329 else
330 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300331 if (!iface->register_property(propertyName, propertyValue))
332 return resCode::resErr;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700333 }
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300334 return resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700335}
336
337static void setDefaultPCIeFunctionProperties(const int& clientAddr,
338 const int& bus, const int& dev,
339 const int& func)
340{
341 // Set the function-specific properties
342 static constexpr const std::array functionProperties{
343 peci_pcie::function::functionTypeName,
344 peci_pcie::function::deviceClassName,
345 peci_pcie::function::vendorIdName,
346 peci_pcie::function::deviceIdName,
347 peci_pcie::function::classCodeName,
348 peci_pcie::function::revisionIdName,
349 peci_pcie::function::subsystemIdName,
350 peci_pcie::function::subsystemVendorIdName,
351 };
352
353 for (const char* name : functionProperties)
354 {
355 setPCIeProperty(clientAddr, bus, dev,
356 "Function" + std::to_string(func) + std::string(name),
357 std::string());
358 }
359}
360
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300361static resCode setPCIeFunctionProperties(const int& clientAddr, const int& bus,
362 const int& dev, const int& func)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700363{
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300364 std::string res;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700365 // Set the function type always to physical for now
366 setPCIeProperty(clientAddr, bus, dev,
367 "Function" + std::to_string(func) +
368 std::string(peci_pcie::function::functionTypeName),
369 "Physical");
370
371 // Set the function Device Class
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300372 resCode error = getDeviceClass(clientAddr, bus, dev, func, res);
373 if (error != resCode::resOk)
374 {
375 return error;
376 }
Jason M. Billsd1e40602019-05-09 11:43:51 -0700377 setPCIeProperty(clientAddr, bus, dev,
378 "Function" + std::to_string(func) +
379 std::string(peci_pcie::function::deviceClassName),
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300380 res);
Jason M. Billsd1e40602019-05-09 11:43:51 -0700381
382 // Get PCI Function Properties that come from PCI config with the following
383 // offset and size info
384 static constexpr const std::array pciConfigInfo{
385 std::tuple<const char*, int, int>{peci_pcie::function::vendorIdName, 0,
386 2},
387 std::tuple<const char*, int, int>{peci_pcie::function::deviceIdName, 2,
388 2},
389 std::tuple<const char*, int, int>{peci_pcie::function::classCodeName, 9,
390 3},
391 std::tuple<const char*, int, int>{peci_pcie::function::revisionIdName,
392 8, 1},
393 std::tuple<const char*, int, int>{peci_pcie::function::subsystemIdName,
394 0x2e, 2},
395 std::tuple<const char*, int, int>{
396 peci_pcie::function::subsystemVendorIdName, 0x2c, 2}};
397
398 for (const auto& [name, offset, size] : pciConfigInfo)
399 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300400 error = getStringFromPCIeConfig(clientAddr, bus, dev, func, offset,
401 size, res);
402 if (error != resCode::resOk)
403 {
404 return error;
405 }
406 setPCIeProperty(clientAddr, bus, dev,
407 "Function" + std::to_string(func) + std::string(name),
408 res);
Jason M. Billsd1e40602019-05-09 11:43:51 -0700409 }
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300410 return resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700411}
412
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300413static resCode setPCIeDeviceProperties(const int& clientAddr, const int& bus,
414 const int& dev)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700415{
416 // Set the device manufacturer
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300417 std::string manuf;
418 resCode error = getVendorName(clientAddr, bus, dev, manuf);
419 if (error != resCode::resOk)
420 {
421 return error;
422 }
423 setPCIeProperty(clientAddr, bus, dev, "Manufacturer", manuf);
Jason M. Billsd1e40602019-05-09 11:43:51 -0700424
425 // Set the device type
426 constexpr char const* deviceTypeName = "DeviceType";
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300427 bool multiFunc;
428 error = isMultiFunction(clientAddr, bus, dev, multiFunc);
429 if (error != resCode::resOk)
430 {
431 return error;
432 }
433 if (multiFunc)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700434 {
435 setPCIeProperty(clientAddr, bus, dev, deviceTypeName, "MultiFunction");
436 }
437 else
438 {
439 setPCIeProperty(clientAddr, bus, dev, deviceTypeName, "SingleFunction");
440 }
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300441 return resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700442}
443
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300444static resCode updatePCIeDevice(const int& clientAddr, const int& bus,
445 const int& dev)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700446{
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300447 if (setPCIeDeviceProperties(clientAddr, bus, dev) != resCode::resOk)
448 {
449 return resCode::resErr;
450 }
Jason M. Billsd1e40602019-05-09 11:43:51 -0700451
452 // Walk through and populate the functions for this device
453 for (int func = 0; func < peci_pcie::maxPCIFunctions; func++)
454 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300455 bool res;
456 resCode error = pcieFunctionExists(clientAddr, bus, dev, func, res);
457 if (error != resCode::resOk)
458 {
459 return error;
460 }
461 if (res)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700462 {
463 // Set the properties for this function
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300464 if (setPCIeFunctionProperties(clientAddr, bus, dev, func) !=
465 resCode::resOk)
466 {
467 return resCode::resErr;
468 }
Jason M. Billsd1e40602019-05-09 11:43:51 -0700469 }
470 else
471 {
472 // Set default properties for unused functions
473 setDefaultPCIeFunctionProperties(clientAddr, bus, dev, func);
474 }
475 }
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300476 return resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700477}
478
479static void removePCIeDevice(sdbusplus::asio::object_server& objServer,
480 const int& clientAddr, const int& bus,
481 const int& dev)
482{
483 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
484 peci_pcie::pcieDeviceDBusMap[clientAddr][bus][dev];
485
486 objServer.remove_interface(iface);
487
488 peci_pcie::pcieDeviceDBusMap[clientAddr][bus].erase(dev);
489 if (peci_pcie::pcieDeviceDBusMap[clientAddr][bus].empty())
490 {
491 peci_pcie::pcieDeviceDBusMap[clientAddr].erase(bus);
492 }
493 if (peci_pcie::pcieDeviceDBusMap[clientAddr].empty())
494 {
495 peci_pcie::pcieDeviceDBusMap.erase(clientAddr);
496 }
497}
498
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300499static resCode addPCIeDevice(sdbusplus::asio::object_server& objServer,
500 const int& clientAddr, const int& cpu,
501 const int& bus, const int& dev)
502{
503 std::string pathName = std::string(peci_pcie::peciPCIePath) + "/S" +
504 std::to_string(cpu) + "B" + std::to_string(bus) +
505 "D" + std::to_string(dev);
506 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
507 objServer.add_interface(pathName, peci_pcie::peciPCIeDeviceInterface);
508 peci_pcie::pcieDeviceDBusMap[clientAddr][bus][dev] = iface;
509
510 // Update the properties for the new device
511 if (updatePCIeDevice(clientAddr, bus, dev) != resCode::resOk)
512 {
513 removePCIeDevice(objServer, clientAddr, bus, dev);
514 return resCode::resErr;
515 }
516
517 iface->initialize();
518 return resCode::resOk;
519}
520
Jason M. Billsd1e40602019-05-09 11:43:51 -0700521static bool pcieDeviceInDBusMap(const int& clientAddr, const int& bus,
522 const int& dev)
523{
524 if (auto clientAddrIt = peci_pcie::pcieDeviceDBusMap.find(clientAddr);
525 clientAddrIt != peci_pcie::pcieDeviceDBusMap.end())
526 {
527 if (auto busIt = clientAddrIt->second.find(bus);
528 busIt != clientAddrIt->second.end())
529 {
530 if (auto devIt = busIt->second.find(dev);
531 devIt != busIt->second.end())
532 {
533 if (devIt->second)
534 {
535 return true;
536 }
537 }
538 }
539 }
540 return false;
541}
542
Jason M. Bills5d049732019-10-25 15:55:15 -0700543static void scanNextPCIeDevice(boost::asio::io_service& io,
544 sdbusplus::asio::object_server& objServer,
545 std::vector<CPUInfo>& cpuInfo, int cpu, int bus,
546 int dev);
547
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300548static resCode probePCIeDevice(boost::asio::io_service& io,
549 sdbusplus::asio::object_server& objServer,
550 std::vector<CPUInfo>& cpuInfo, int cpu, int bus,
551 int dev)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700552{
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300553 bool res;
554 resCode error = pcieDeviceExists(cpuInfo[cpu].addr, bus, dev, res);
555 if (error != resCode::resOk)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700556 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300557 return error;
Jason M. Bills5d049732019-10-25 15:55:15 -0700558 }
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300559 if (res)
Jason M. Bills5d049732019-10-25 15:55:15 -0700560 {
561 if (pcieDeviceInDBusMap(cpuInfo[cpu].addr, bus, dev))
Jason M. Billsd1e40602019-05-09 11:43:51 -0700562 {
563 // This device is already in D-Bus, so update it
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300564 if (updatePCIeDevice(cpuInfo[cpu].addr, bus, dev) != resCode::resOk)
565 {
566 return resCode::resErr;
567 }
Jason M. Billsd1e40602019-05-09 11:43:51 -0700568 }
569 else
570 {
571 // This device is not in D-Bus, so add it
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300572 if (addPCIeDevice(objServer, cpuInfo[cpu].addr, cpu, bus, dev) !=
573 resCode::resOk)
574 {
575 return resCode::resErr;
576 }
Jason M. Billsd1e40602019-05-09 11:43:51 -0700577 }
578 }
579 else
580 {
581 // If PECI is not available, then stop scanning
582 if (!isPECIAvailable())
583 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300584 return resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700585 }
586
Jason M. Bills5d049732019-10-25 15:55:15 -0700587 if (pcieDeviceInDBusMap(cpuInfo[cpu].addr, bus, dev))
Jason M. Billsd1e40602019-05-09 11:43:51 -0700588 {
589 // This device is in D-Bus, so remove it
Jason M. Bills5d049732019-10-25 15:55:15 -0700590 removePCIeDevice(objServer, cpuInfo[cpu].addr, bus, dev);
Jason M. Billsd1e40602019-05-09 11:43:51 -0700591 }
592 }
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300593 return resCode::resOk;
594}
595
596static void scanPCIeDevice(boost::asio::io_service& io,
597 sdbusplus::asio::object_server& objServer,
598 std::vector<CPUInfo>& cpuInfo, int cpu, int bus,
599 int dev)
600{
601 // Check if this is a CPU bus that we should skip
602 if (cpuInfo[cpu].skipCpuBuses && cpuInfo[cpu].cpuBusNums.count(bus))
603 {
604 std::cout << "Skipping CPU " << cpu << " Bus Number " << bus << "\n";
605 // Skip all the devices on this bus
606 dev = peci_pcie::maxPCIDevices;
607 scanNextPCIeDevice(io, objServer, cpuInfo, cpu, bus, dev);
608 return;
609 }
610
611 if (probePCIeDevice(io, objServer, cpuInfo, cpu, bus, dev) !=
612 resCode::resOk)
613 {
614 std::cerr << "Failed to probe CPU " << cpu << " Bus " << bus
615 << " Device " << dev << "\n";
616 }
617
Jason M. Bills5d049732019-10-25 15:55:15 -0700618 scanNextPCIeDevice(io, objServer, cpuInfo, cpu, bus, dev);
619}
Jason M. Billsd1e40602019-05-09 11:43:51 -0700620
Jason M. Bills5d049732019-10-25 15:55:15 -0700621static void scanNextPCIeDevice(boost::asio::io_service& io,
622 sdbusplus::asio::object_server& objServer,
623 std::vector<CPUInfo>& cpuInfo, int cpu, int bus,
624 int dev)
625{
Jason M. Billsee6d80b2021-06-11 07:37:30 -0700626 if (peci_pcie::abortScan)
627 {
628 std::cerr << "PCIe scan aborted\n";
629 return;
630 }
631
Jason M. Billsd1e40602019-05-09 11:43:51 -0700632 // PCIe Device scan completed, so move to the next device
633 if (++dev >= peci_pcie::maxPCIDevices)
634 {
635 // All devices scanned, so move to the next bus
636 dev = 0;
637 if (++bus >= peci_pcie::maxPCIBuses)
638 {
639 // All buses scanned, so move to the next CPU
640 bus = 0;
Jason M. Bills5d049732019-10-25 15:55:15 -0700641 if (++cpu >= cpuInfo.size())
Jason M. Billsd1e40602019-05-09 11:43:51 -0700642 {
643 // All CPUs scanned, so we're done
Jason M. Billsee6d80b2021-06-11 07:37:30 -0700644 std::cerr << "PCIe scan completed\n";
Jason M. Billsd1e40602019-05-09 11:43:51 -0700645 return;
646 }
647 }
648 }
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300649 boost::asio::post(io, [&io, &objServer, &cpuInfo, cpu, bus, dev]() mutable {
Jason M. Bills5d049732019-10-25 15:55:15 -0700650 scanPCIeDevice(io, objServer, cpuInfo, cpu, bus, dev);
Jason M. Billsd1e40602019-05-09 11:43:51 -0700651 });
652}
653
654static void peciAvailableCheck(boost::asio::steady_timer& peciWaitTimer,
655 boost::asio::io_service& io,
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300656 sdbusplus::asio::object_server& objServer,
657 std::vector<CPUInfo>& cpuInfo)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700658{
Jason M. Bills5d049732019-10-25 15:55:15 -0700659 static bool lastPECIState = false;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700660 bool peciAvailable = isPECIAvailable();
661 if (peciAvailable && !lastPECIState)
662 {
663 lastPECIState = true;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700664 static boost::asio::steady_timer pcieTimeout(io);
665 constexpr const int pcieWaitTime = 60;
666 pcieTimeout.expires_after(std::chrono::seconds(pcieWaitTime));
667 pcieTimeout.async_wait(
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300668 [&io, &objServer, &cpuInfo](const boost::system::error_code& ec) {
Jason M. Billsd1e40602019-05-09 11:43:51 -0700669 if (ec)
670 {
671 // operation_aborted is expected if timer is canceled
672 // before completion.
673 if (ec != boost::asio::error::operation_aborted)
674 {
675 std::cerr << "PECI PCIe async_wait failed " << ec;
676 }
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300677 lastPECIState = false;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700678 return;
679 }
680 // get the PECI client address list
Jason M. Bills5d049732019-10-25 15:55:15 -0700681 getClientAddrMap(cpuInfo);
682 // get the CPU Bus Numbers to skip
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300683 if (getCPUBusNums(cpuInfo) != resCode::resOk)
684 {
685 lastPECIState = false;
686 return;
687 }
Jason M. Billsd1e40602019-05-09 11:43:51 -0700688 // scan PCIe starting from CPU 0, Bus 0, Device 0
Jason M. Billsee6d80b2021-06-11 07:37:30 -0700689 std::cerr << "PCIe scan started\n";
Jason M. Bills5d049732019-10-25 15:55:15 -0700690 scanPCIeDevice(io, objServer, cpuInfo, 0, 0, 0);
Jason M. Billsd1e40602019-05-09 11:43:51 -0700691 });
692 }
693 else if (!peciAvailable && lastPECIState)
694 {
695 lastPECIState = false;
696 }
697
698 peciWaitTimer.expires_after(
699 std::chrono::seconds(peci_pcie::peciCheckInterval));
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300700 peciWaitTimer.async_wait([&peciWaitTimer, &io, &objServer,
701 &cpuInfo](const boost::system::error_code& ec) {
Jason M. Billsd1e40602019-05-09 11:43:51 -0700702 if (ec)
703 {
704 // operation_aborted is expected if timer is canceled
705 // before completion.
706 if (ec != boost::asio::error::operation_aborted)
707 {
708 std::cerr << "PECI Available Check async_wait failed " << ec;
709 }
710 return;
711 }
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300712 peciAvailableCheck(peciWaitTimer, io, objServer, cpuInfo);
Jason M. Billsd1e40602019-05-09 11:43:51 -0700713 });
714}
715
Jason M. Billsee6d80b2021-06-11 07:37:30 -0700716static void waitForOSStandbyDelay(boost::asio::io_service& io,
717 sdbusplus::asio::object_server& objServer,
718 boost::asio::steady_timer& osStandbyTimer,
719 std::vector<CPUInfo>& cpuInfo)
720{
721 osStandbyTimer.expires_after(
722 std::chrono::seconds(peci_pcie::osStandbyDelaySeconds));
723
724 osStandbyTimer.async_wait(
725 [&io, &objServer, &cpuInfo](const boost::system::error_code& ec) {
726 if (ec == boost::asio::error::operation_aborted)
727 {
728 return; // we're being canceled
729 }
730 else if (ec)
731 {
732 std::cerr << "OS Standby async_wait failed: " << ec.value()
733 << ": " << ec.message() << "\n";
734 return;
735 }
736 // get the PECI client address list
737 getClientAddrMap(cpuInfo);
738 if (cpuInfo.empty())
739 {
740 std::cerr << "No CPUs found, scan skipped\n";
741 return;
742 }
743 // get the CPU Bus Numbers to skip
744 if (getCPUBusNums(cpuInfo) != resCode::resOk)
745 {
746 return;
747 }
748 // scan PCIe starting from CPU 0, Bus 0, Device 0
749 std::cerr << "PCIe scan started\n";
750 scanPCIeDevice(io, objServer, cpuInfo, 0, 0, 0);
751 });
752}
753
754static void monitorOSStandby(boost::asio::io_service& io,
755 std::shared_ptr<sdbusplus::asio::connection> conn,
756 sdbusplus::asio::object_server& objServer,
757 boost::asio::steady_timer& osStandbyTimer,
758 std::vector<CPUInfo>& cpuInfo)
759{
760 std::cerr << "Start OperatingSystemState Monitor\n";
761
762 static sdbusplus::bus::match::match osStateMatch(
763 *conn,
764 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
765 "PropertiesChanged',arg0='xyz.openbmc_project.State.OperatingSystem."
766 "Status'",
767 [&io, &objServer, &osStandbyTimer,
768 &cpuInfo](sdbusplus::message::message& msg) {
769 // Get the OS State from the message
770 std::string osStateInterface;
771 boost::container::flat_map<std::string, std::variant<std::string>>
772 propertiesChanged;
773 msg.read(osStateInterface, propertiesChanged);
774
775 for (const auto& [name, value] : propertiesChanged)
776 {
777 if (name == "OperatingSystemState")
778 {
779 const std::string* state = std::get_if<std::string>(&value);
780 if (state == nullptr)
781 {
782 std::cerr << "Unable to read OS state value\n";
783 return;
784 }
785
786 if (*state == "Standby")
787 {
788 peci_pcie::abortScan = false;
789 waitForOSStandbyDelay(io, objServer, osStandbyTimer,
790 cpuInfo);
791 }
792 else if (*state == "Inactive")
793 {
794 peci_pcie::abortScan = true;
795 osStandbyTimer.cancel();
796 }
797 }
798 }
799 });
800
801 // Check if the OS state is already available
802 conn->async_method_call(
803 [&io, &objServer, &osStandbyTimer,
804 &cpuInfo](boost::system::error_code ec,
805 const std::variant<std::string>& property) {
806 if (ec)
807 {
808 std::cerr << "error with OS state async_method_call\n";
809 return;
810 }
811
812 const std::string* state = std::get_if<std::string>(&property);
813 if (state == nullptr)
814 {
815 std::cerr << "Unable to read OS state value\n";
816 return;
817 }
818
819 // If the OS state is in Standby, then BIOS is done and we can
820 // continue. Otherwise, we just wait for the match
821 if (*state == "Standby")
822 {
823 waitForOSStandbyDelay(io, objServer, osStandbyTimer, cpuInfo);
824 }
825 },
826 "xyz.openbmc_project.State.OperatingSystem",
827 "/xyz/openbmc_project/state/os", "org.freedesktop.DBus.Properties",
828 "Get", "xyz.openbmc_project.State.OperatingSystem.Status",
829 "OperatingSystemState");
830}
831
Jason M. Billsd1e40602019-05-09 11:43:51 -0700832int main(int argc, char* argv[])
833{
834 // setup connection to dbus
835 boost::asio::io_service io;
836 std::shared_ptr<sdbusplus::asio::connection> conn =
837 std::make_shared<sdbusplus::asio::connection>(io);
838
839 // PECI PCIe Object
840 conn->request_name(peci_pcie::peciPCIeObject);
841 sdbusplus::asio::object_server server =
842 sdbusplus::asio::object_server(conn);
843
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300844 // CPU map
845 std::vector<CPUInfo> cpuInfo;
846
Jason M. Billsee6d80b2021-06-11 07:37:30 -0700847#ifdef WAIT_FOR_OS_STANDBY
848 boost::asio::steady_timer osStandbyTimer(io);
849 monitorOSStandby(io, conn, server, osStandbyTimer, cpuInfo);
850#else
Jason M. Billsd1e40602019-05-09 11:43:51 -0700851 // Start the PECI check loop
852 boost::asio::steady_timer peciWaitTimer(
853 io, std::chrono::seconds(peci_pcie::peciCheckInterval));
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300854 peciWaitTimer.async_wait([&peciWaitTimer, &io, &server,
855 &cpuInfo](const boost::system::error_code& ec) {
Jason M. Billsd1e40602019-05-09 11:43:51 -0700856 if (ec)
857 {
858 // operation_aborted is expected if timer is canceled
859 // before completion.
860 if (ec != boost::asio::error::operation_aborted)
861 {
862 std::cerr << "PECI Available Check async_wait failed " << ec;
863 }
864 return;
865 }
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300866 peciAvailableCheck(peciWaitTimer, io, server, cpuInfo);
Jason M. Billsd1e40602019-05-09 11:43:51 -0700867 });
Jason M. Billsee6d80b2021-06-11 07:37:30 -0700868#endif
Jason M. Billsd1e40602019-05-09 11:43:51 -0700869
870 io.run();
871
872 return 0;
873}