blob: ea75c9d1393faaab46b312d52b13bea44422f203 [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
Andrei Kartashev6f552032021-05-11 21:25:29 +030055
56static constexpr const std::array pciConfigInfo{
57 std::tuple<const char*, int, int>{function::functionTypeName, -1, -1},
58 std::tuple<const char*, int, int>{function::deviceClassName, -1, -1},
59 std::tuple<const char*, int, int>{function::vendorIdName, 0, 2},
60 std::tuple<const char*, int, int>{function::deviceIdName, 2, 2},
61 std::tuple<const char*, int, int>{function::classCodeName, 9, 3},
62 std::tuple<const char*, int, int>{function::revisionIdName, 8, 1},
63 std::tuple<const char*, int, int>{function::subsystemIdName, 0x2e, 2},
64 std::tuple<const char*, int, int>{function::subsystemVendorIdName, 0x2c,
65 2}};
Jason M. Billsd1e40602019-05-09 11:43:51 -070066} // namespace peci_pcie
67
Andrei Kartashevd570dfd2020-12-16 17:33:16 +030068enum class resCode
69{
70 resOk,
71 resErr
72};
73
Jason M. Bills5d049732019-10-25 15:55:15 -070074struct CPUInfo
Jason M. Billsd1e40602019-05-09 11:43:51 -070075{
Jason M. Bills5d049732019-10-25 15:55:15 -070076 size_t addr;
77 bool skipCpuBuses;
78 boost::container::flat_set<size_t> cpuBusNums;
79};
80
81// PECI Client Address Map
Andrei Kartashev8e966032021-07-02 20:04:30 +030082static resCode getCPUBusMap(std::vector<CPUInfo>& cpuInfo)
Jason M. Bills5d049732019-10-25 15:55:15 -070083{
Andrei Kartashevd570dfd2020-12-16 17:33:16 +030084 cpuInfo.clear();
Andrei Kartashev8e966032021-07-02 20:04:30 +030085 for (size_t addr = MIN_CLIENT_ADDR; addr <= MAX_CLIENT_ADDR; addr++)
Jason M. Billsd1e40602019-05-09 11:43:51 -070086 {
Andrei Kartashev8e966032021-07-02 20:04:30 +030087 if (peci_Ping(addr) != PECI_CC_SUCCESS)
Jason M. Billsd1e40602019-05-09 11:43:51 -070088 {
Andrei Kartashev8e966032021-07-02 20:04:30 +030089 continue;
Jason M. Bills5d049732019-10-25 15:55:15 -070090 }
Jason M. Bills5d049732019-10-25 15:55:15 -070091
Andrei Kartashev8e966032021-07-02 20:04:30 +030092 auto& cpu = cpuInfo.emplace_back(CPUInfo{addr, false, {}});
Jason M. Bills5d049732019-10-25 15:55:15 -070093 uint8_t cc = 0;
94 CPUModel model{};
95 uint8_t stepping = 0;
Andrei Kartashev8e966032021-07-02 20:04:30 +030096 if (peci_GetCPUID(addr, &model, &stepping, &cc) != PECI_CC_SUCCESS)
Jason M. Bills5d049732019-10-25 15:55:15 -070097 {
98 std::cerr << "Cannot get CPUID!\n";
Andrei Kartashevd570dfd2020-12-16 17:33:16 +030099 return resCode::resErr;
Jason M. Bills5d049732019-10-25 15:55:15 -0700100 }
101
102 switch (model)
103 {
104 case skx:
105 {
106 // Get the assigned CPU bus numbers from CPUBUSNO and CPUBUSNO1
107 // (B(0) D8 F2 offsets CCh and D0h)
108 uint32_t cpuBusNum = 0;
Andrei Kartashev8e966032021-07-02 20:04:30 +0300109 if (peci_RdPCIConfigLocal(addr, 0, 8, 2, 0xCC, 4,
Jason M. Bills5d049732019-10-25 15:55:15 -0700110 (uint8_t*)&cpuBusNum,
111 &cc) != PECI_CC_SUCCESS)
112 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300113 return resCode::resErr;
Jason M. Bills5d049732019-10-25 15:55:15 -0700114 }
115 uint32_t cpuBusNum1 = 0;
Andrei Kartashev8e966032021-07-02 20:04:30 +0300116 if (peci_RdPCIConfigLocal(addr, 0, 8, 2, 0xD0, 4,
Jason M. Bills5d049732019-10-25 15:55:15 -0700117 (uint8_t*)&cpuBusNum1,
118 &cc) != PECI_CC_SUCCESS)
119 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300120 return resCode::resErr;
Jason M. Bills5d049732019-10-25 15:55:15 -0700121 }
122
123 // Add the CPU bus numbers to the set for this CPU
124 while (cpuBusNum)
125 {
126 // Get the LSB
127 size_t busNum = cpuBusNum & 0xFF;
128 cpu.cpuBusNums.insert(busNum);
129 // Shift right by one byte
130 cpuBusNum >>= 8;
131 }
132 while (cpuBusNum1)
133 {
134 // Get the LSB
135 size_t busNum = cpuBusNum1 & 0xFF;
136 cpu.cpuBusNums.insert(busNum);
137 // Shift right by one byte
Zev Weiss9fa54b52020-09-02 21:20:33 +0000138 cpuBusNum1 >>= 8;
Jason M. Bills5d049732019-10-25 15:55:15 -0700139 }
140 cpu.skipCpuBuses = true;
141 }
Jason M. Billsd1e40602019-05-09 11:43:51 -0700142 }
143 }
Andrei Kartashev8e966032021-07-02 20:04:30 +0300144 return cpuInfo.empty() ? resCode::resErr : resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700145}
146
147static bool isPECIAvailable(void)
148{
Jason M. Bills5d049732019-10-25 15:55:15 -0700149 for (size_t i = MIN_CLIENT_ADDR; i <= MAX_CLIENT_ADDR; i++)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700150 {
Jason M. Bills5d049732019-10-25 15:55:15 -0700151 if (peci_Ping(i) == PECI_CC_SUCCESS)
152 {
153 return true;
154 }
Jason M. Billsd1e40602019-05-09 11:43:51 -0700155 }
Jason M. Bills5d049732019-10-25 15:55:15 -0700156 return false;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700157}
158
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300159static resCode getDataFromPCIeConfig(const int& clientAddr, const int& bus,
160 const int& dev, const int& func,
161 const int& offset, const int& size,
162 uint32_t& pciData)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700163{
164 // PECI RdPCIConfig() currently only supports 4 byte reads, so adjust
165 // the offset and size to get the right data
166 static constexpr const int pciReadSize = 4;
167 int mod = offset % pciReadSize;
168 int pciOffset = offset - mod;
169 if (mod + size > pciReadSize)
170 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300171 return resCode::resErr;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700172 }
173
174 std::array<uint8_t, pciReadSize> data;
175 uint8_t cc;
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300176 int ret = PECI_CC_TIMEOUT;
177 for (int index = 0; (index < 5) && (ret == PECI_CC_TIMEOUT); index++)
178 {
Jason M. Bills3b1665a2021-06-11 13:35:04 -0700179#ifdef USE_RDENDPOINTCFG
180 ret = peci_RdEndPointConfigPci(clientAddr, // CPU Address
181 0, // PCI Seg (use 0 for now)
182 bus, // PCI Bus
183 dev, // PCI Device
184 func, // PCI Function
185 pciOffset, // PCI Offset
186 pciReadSize, // PCI Read Size
187 data.data(), // PCI Read Data
188 &cc); // PECI Completion Code
189#else
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300190 ret = peci_RdPCIConfig(clientAddr, // CPU Address
Jason M. Billsd1e40602019-05-09 11:43:51 -0700191 bus, // PCI Bus
192 dev, // PCI Device
193 func, // PCI Function
194 pciOffset, // PCI Offset
195 data.data(), // PCI Read Data
196 &cc); // PECI Completion Code
Jason M. Bills3b1665a2021-06-11 13:35:04 -0700197#endif
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300198 }
Jason M. Billsd1e40602019-05-09 11:43:51 -0700199 if (ret != PECI_CC_SUCCESS || cc != PECI_DEV_CC_SUCCESS)
200 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300201 return resCode::resErr;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700202 }
203
204 // Now build the requested data into a single number
205 pciData = 0;
206 for (int i = mod; i < mod + size; i++)
207 {
Jason M. Billse55832b2021-06-14 16:00:49 -0700208 pciData |= static_cast<uint32_t>(data[i]) << 8 * (i - mod);
Jason M. Billsd1e40602019-05-09 11:43:51 -0700209 }
210
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300211 return resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700212}
213
Andrei Kartashev6f552032021-05-11 21:25:29 +0300214static resCode getStringFromData(const int& size, const uint32_t& data,
215 std::string& res)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700216{
Jason M. Billsd1e40602019-05-09 11:43:51 -0700217 // And convert it to a string
218 std::stringstream dataStream;
219 dataStream << "0x" << std::hex << std::setfill('0') << std::setw(size * 2)
220 << data;
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300221 res = dataStream.str();
222 return resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700223}
224
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300225static resCode getVendorName(const int& clientAddr, const int& bus,
226 const int& dev, std::string& res)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700227{
228 static constexpr const int vendorIDOffset = 0x00;
229 static constexpr const int vendorIDSize = 2;
230
231 // Get the header type register from function 0
232 uint32_t vendorID = 0;
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300233 if (getDataFromPCIeConfig(clientAddr, bus, dev, 0, vendorIDOffset,
234 vendorIDSize, vendorID) != resCode::resOk)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700235 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300236 return resCode::resErr;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700237 }
238 // Get the vendor name or use Other if it doesn't exist
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300239 res = pciVendors.try_emplace(vendorID, otherVendor).first->second;
240 return resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700241}
242
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300243static resCode getDeviceClass(const int& clientAddr, const int& bus,
244 const int& dev, const int& func, std::string& res)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700245{
246 static constexpr const int baseClassOffset = 0x0b;
247 static constexpr const int baseClassSize = 1;
248
249 // Get the Device Base Class
250 uint32_t baseClass = 0;
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300251 if (getDataFromPCIeConfig(clientAddr, bus, dev, func, baseClassOffset,
252 baseClassSize, baseClass) != resCode::resOk)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700253 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300254 return resCode::resErr;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700255 }
256 // Get the base class name or use Other if it doesn't exist
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300257 res = pciDeviceClasses.try_emplace(baseClass, otherClass).first->second;
258 return resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700259}
260
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300261static resCode isMultiFunction(const int& clientAddr, const int& bus,
262 const int& dev, bool& res)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700263{
264 static constexpr const int headerTypeOffset = 0x0e;
265 static constexpr const int headerTypeSize = 1;
266 static constexpr const int multiFuncBit = 1 << 7;
267
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300268 res = false;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700269 // Get the header type register from function 0
270 uint32_t headerType = 0;
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300271 if (getDataFromPCIeConfig(clientAddr, bus, dev, 0, headerTypeOffset,
272 headerTypeSize, headerType) != resCode::resOk)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700273 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300274 return resCode::resErr;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700275 }
276 // Check if it's a multifunction device
277 if (headerType & multiFuncBit)
278 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300279 res = true;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700280 }
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300281 return resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700282}
283
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300284static resCode pcieFunctionExists(const int& clientAddr, const int& bus,
285 const int& dev, const int& func, bool& res)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700286{
287 constexpr const int pciIDOffset = 0;
288 constexpr const int pciIDSize = 4;
289 uint32_t pciID = 0;
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300290 res = false;
291 if (getDataFromPCIeConfig(clientAddr, bus, dev, func, pciIDOffset,
292 pciIDSize, pciID) != resCode::resOk)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700293 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300294 return resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700295 }
296
297 // if VID and DID are all 0s or 1s, then the device doesn't exist
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300298 if (pciID != 0x00000000 && pciID != 0xFFFFFFFF)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700299 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300300 res = true;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700301 }
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300302 return resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700303}
304
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300305static resCode pcieDeviceExists(const int& clientAddr, const int& bus,
306 const int& dev, bool& res)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700307{
308 // Check if this device exists by checking function 0
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300309 return pcieFunctionExists(clientAddr, bus, dev, 0, res);
Jason M. Billsd1e40602019-05-09 11:43:51 -0700310}
311
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300312static resCode setPCIeProperty(const int& clientAddr, const int& bus,
313 const int& dev, const std::string& propertyName,
314 const std::string& propertyValue)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700315{
316 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
317 peci_pcie::pcieDeviceDBusMap[clientAddr][bus][dev];
318
319 if (iface->is_initialized())
320 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300321 if (!iface->set_property(propertyName, propertyValue))
322 return resCode::resErr;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700323 }
324 else
325 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300326 if (!iface->register_property(propertyName, propertyValue))
327 return resCode::resErr;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700328 }
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300329 return resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700330}
331
332static void setDefaultPCIeFunctionProperties(const int& clientAddr,
333 const int& bus, const int& dev,
334 const int& func)
335{
336 // Set the function-specific properties
Andrei Kartashev6f552032021-05-11 21:25:29 +0300337 for (const auto& [name, offset, size] : peci_pcie::pciConfigInfo)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700338 {
339 setPCIeProperty(clientAddr, bus, dev,
340 "Function" + std::to_string(func) + std::string(name),
341 std::string());
342 }
343}
344
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300345static resCode setPCIeFunctionProperties(const int& clientAddr, const int& bus,
346 const int& dev, const int& func)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700347{
Andrei Kartashev6f552032021-05-11 21:25:29 +0300348 uint32_t data = 0;
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300349 std::string res;
Andrei Kartashev6f552032021-05-11 21:25:29 +0300350 resCode error;
351
352 for (const auto& [name, offset, size] : peci_pcie::pciConfigInfo)
353 {
354 if (offset < 0)
355 {
356 continue;
357 }
358
359 error = getDataFromPCIeConfig(clientAddr, bus, dev, func, offset, size,
360 data);
361 if (error != resCode::resOk)
362 {
363 return error;
364 }
365 getStringFromData(size, data, res);
366 setPCIeProperty(clientAddr, bus, dev,
367 "Function" + std::to_string(func) + std::string(name),
368 res);
369 }
370
Jason M. Billsd1e40602019-05-09 11:43:51 -0700371 // Set the function type always to physical for now
372 setPCIeProperty(clientAddr, bus, dev,
373 "Function" + std::to_string(func) +
374 std::string(peci_pcie::function::functionTypeName),
375 "Physical");
376
377 // Set the function Device Class
Andrei Kartashev6f552032021-05-11 21:25:29 +0300378 error = getDeviceClass(clientAddr, bus, dev, func, res);
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300379 if (error != resCode::resOk)
380 {
381 return error;
382 }
Jason M. Billsd1e40602019-05-09 11:43:51 -0700383 setPCIeProperty(clientAddr, bus, dev,
384 "Function" + std::to_string(func) +
385 std::string(peci_pcie::function::deviceClassName),
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300386 res);
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300387 return resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700388}
389
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300390static resCode setPCIeDeviceProperties(const int& clientAddr, const int& bus,
391 const int& dev)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700392{
393 // Set the device manufacturer
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300394 std::string manuf;
395 resCode error = getVendorName(clientAddr, bus, dev, manuf);
396 if (error != resCode::resOk)
397 {
398 return error;
399 }
400 setPCIeProperty(clientAddr, bus, dev, "Manufacturer", manuf);
Jason M. Billsd1e40602019-05-09 11:43:51 -0700401
402 // Set the device type
403 constexpr char const* deviceTypeName = "DeviceType";
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300404 bool multiFunc;
405 error = isMultiFunction(clientAddr, bus, dev, multiFunc);
406 if (error != resCode::resOk)
407 {
408 return error;
409 }
410 if (multiFunc)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700411 {
412 setPCIeProperty(clientAddr, bus, dev, deviceTypeName, "MultiFunction");
413 }
414 else
415 {
416 setPCIeProperty(clientAddr, bus, dev, deviceTypeName, "SingleFunction");
417 }
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300418 return resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700419}
420
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300421static resCode updatePCIeDevice(const int& clientAddr, const int& bus,
422 const int& dev)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700423{
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300424 if (setPCIeDeviceProperties(clientAddr, bus, dev) != resCode::resOk)
425 {
426 return resCode::resErr;
427 }
Jason M. Billsd1e40602019-05-09 11:43:51 -0700428
429 // Walk through and populate the functions for this device
430 for (int func = 0; func < peci_pcie::maxPCIFunctions; func++)
431 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300432 bool res;
433 resCode error = pcieFunctionExists(clientAddr, bus, dev, func, res);
434 if (error != resCode::resOk)
435 {
436 return error;
437 }
438 if (res)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700439 {
440 // Set the properties for this function
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300441 if (setPCIeFunctionProperties(clientAddr, bus, dev, func) !=
442 resCode::resOk)
443 {
444 return resCode::resErr;
445 }
Jason M. Billsd1e40602019-05-09 11:43:51 -0700446 }
447 else
448 {
449 // Set default properties for unused functions
450 setDefaultPCIeFunctionProperties(clientAddr, bus, dev, func);
451 }
452 }
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300453 return resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700454}
455
456static void removePCIeDevice(sdbusplus::asio::object_server& objServer,
457 const int& clientAddr, const int& bus,
458 const int& dev)
459{
460 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
461 peci_pcie::pcieDeviceDBusMap[clientAddr][bus][dev];
462
463 objServer.remove_interface(iface);
464
465 peci_pcie::pcieDeviceDBusMap[clientAddr][bus].erase(dev);
466 if (peci_pcie::pcieDeviceDBusMap[clientAddr][bus].empty())
467 {
468 peci_pcie::pcieDeviceDBusMap[clientAddr].erase(bus);
469 }
470 if (peci_pcie::pcieDeviceDBusMap[clientAddr].empty())
471 {
472 peci_pcie::pcieDeviceDBusMap.erase(clientAddr);
473 }
474}
475
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300476static resCode addPCIeDevice(sdbusplus::asio::object_server& objServer,
477 const int& clientAddr, const int& cpu,
478 const int& bus, const int& dev)
479{
480 std::string pathName = std::string(peci_pcie::peciPCIePath) + "/S" +
481 std::to_string(cpu) + "B" + std::to_string(bus) +
482 "D" + std::to_string(dev);
483 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
484 objServer.add_interface(pathName, peci_pcie::peciPCIeDeviceInterface);
485 peci_pcie::pcieDeviceDBusMap[clientAddr][bus][dev] = iface;
486
487 // Update the properties for the new device
488 if (updatePCIeDevice(clientAddr, bus, dev) != resCode::resOk)
489 {
490 removePCIeDevice(objServer, clientAddr, bus, dev);
491 return resCode::resErr;
492 }
493
494 iface->initialize();
495 return resCode::resOk;
496}
497
Jason M. Billsd1e40602019-05-09 11:43:51 -0700498static bool pcieDeviceInDBusMap(const int& clientAddr, const int& bus,
499 const int& dev)
500{
501 if (auto clientAddrIt = peci_pcie::pcieDeviceDBusMap.find(clientAddr);
502 clientAddrIt != peci_pcie::pcieDeviceDBusMap.end())
503 {
504 if (auto busIt = clientAddrIt->second.find(bus);
505 busIt != clientAddrIt->second.end())
506 {
507 if (auto devIt = busIt->second.find(dev);
508 devIt != busIt->second.end())
509 {
510 if (devIt->second)
511 {
512 return true;
513 }
514 }
515 }
516 }
517 return false;
518}
519
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300520static resCode probePCIeDevice(boost::asio::io_service& io,
521 sdbusplus::asio::object_server& objServer,
Andrei Kartashev8e966032021-07-02 20:04:30 +0300522 size_t addr, int cpu, int bus, int dev)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700523{
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300524 bool res;
Andrei Kartashev8e966032021-07-02 20:04:30 +0300525 resCode error = pcieDeviceExists(addr, bus, dev, res);
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300526 if (error != resCode::resOk)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700527 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300528 return error;
Jason M. Bills5d049732019-10-25 15:55:15 -0700529 }
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300530 if (res)
Jason M. Bills5d049732019-10-25 15:55:15 -0700531 {
Andrei Kartashev8e966032021-07-02 20:04:30 +0300532 if (pcieDeviceInDBusMap(addr, bus, dev))
Jason M. Billsd1e40602019-05-09 11:43:51 -0700533 {
534 // This device is already in D-Bus, so update it
Andrei Kartashev8e966032021-07-02 20:04:30 +0300535 if (updatePCIeDevice(addr, bus, dev) != resCode::resOk)
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300536 {
537 return resCode::resErr;
538 }
Jason M. Billsd1e40602019-05-09 11:43:51 -0700539 }
540 else
541 {
542 // This device is not in D-Bus, so add it
Andrei Kartashev8e966032021-07-02 20:04:30 +0300543 if (addPCIeDevice(objServer, addr, cpu, bus, dev) != resCode::resOk)
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300544 {
545 return resCode::resErr;
546 }
Jason M. Billsd1e40602019-05-09 11:43:51 -0700547 }
548 }
549 else
550 {
551 // If PECI is not available, then stop scanning
552 if (!isPECIAvailable())
553 {
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300554 return resCode::resOk;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700555 }
556
Andrei Kartashev8e966032021-07-02 20:04:30 +0300557 if (pcieDeviceInDBusMap(addr, bus, dev))
Jason M. Billsd1e40602019-05-09 11:43:51 -0700558 {
559 // This device is in D-Bus, so remove it
Andrei Kartashev8e966032021-07-02 20:04:30 +0300560 removePCIeDevice(objServer, addr, bus, dev);
Jason M. Billsd1e40602019-05-09 11:43:51 -0700561 }
562 }
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300563 return resCode::resOk;
564}
565
Andrei Kartashev8e966032021-07-02 20:04:30 +0300566static void scanNextPCIeDevice(boost::asio::io_service& io,
567 sdbusplus::asio::object_server& objServer,
568 std::vector<CPUInfo>& cpuInfo, int cpu, int bus,
569 int dev);
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300570static void scanPCIeDevice(boost::asio::io_service& io,
571 sdbusplus::asio::object_server& objServer,
572 std::vector<CPUInfo>& cpuInfo, int cpu, int bus,
573 int dev)
574{
Andrei Kartashev8e966032021-07-02 20:04:30 +0300575 if (cpu >= cpuInfo.size())
576 {
577 std::cerr << "Request to scan CPU" << cpu
578 << " while CPU array has size " << cpuInfo.size() << "\n";
579 return;
580 }
581 auto& info = cpuInfo[cpu];
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300582 // Check if this is a CPU bus that we should skip
Andrei Kartashev8e966032021-07-02 20:04:30 +0300583 if (info.skipCpuBuses && info.cpuBusNums.count(bus))
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300584 {
585 std::cout << "Skipping CPU " << cpu << " Bus Number " << bus << "\n";
586 // Skip all the devices on this bus
587 dev = peci_pcie::maxPCIDevices;
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300588 }
Andrei Kartashev8e966032021-07-02 20:04:30 +0300589 else
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300590 {
Andrei Kartashev8e966032021-07-02 20:04:30 +0300591 if (probePCIeDevice(io, objServer, info.addr, cpu, bus, dev) !=
592 resCode::resOk)
593 {
594 std::cerr << "Failed to probe CPU " << cpu << " Bus " << bus
595 << " Device " << dev << "\n";
596 }
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300597 }
598
Jason M. Bills5d049732019-10-25 15:55:15 -0700599 scanNextPCIeDevice(io, objServer, cpuInfo, cpu, bus, dev);
Andrei Kartashev8e966032021-07-02 20:04:30 +0300600 return;
Jason M. Bills5d049732019-10-25 15:55:15 -0700601}
Jason M. Billsd1e40602019-05-09 11:43:51 -0700602
Jason M. Bills5d049732019-10-25 15:55:15 -0700603static void scanNextPCIeDevice(boost::asio::io_service& io,
604 sdbusplus::asio::object_server& objServer,
605 std::vector<CPUInfo>& cpuInfo, int cpu, int bus,
606 int dev)
607{
Jason M. Billsee6d80b2021-06-11 07:37:30 -0700608 if (peci_pcie::abortScan)
609 {
610 std::cerr << "PCIe scan aborted\n";
611 return;
612 }
613
Jason M. Billsd1e40602019-05-09 11:43:51 -0700614 // PCIe Device scan completed, so move to the next device
615 if (++dev >= peci_pcie::maxPCIDevices)
616 {
617 // All devices scanned, so move to the next bus
618 dev = 0;
619 if (++bus >= peci_pcie::maxPCIBuses)
620 {
621 // All buses scanned, so move to the next CPU
622 bus = 0;
Jason M. Bills5d049732019-10-25 15:55:15 -0700623 if (++cpu >= cpuInfo.size())
Jason M. Billsd1e40602019-05-09 11:43:51 -0700624 {
625 // All CPUs scanned, so we're done
Jason M. Billsee6d80b2021-06-11 07:37:30 -0700626 std::cerr << "PCIe scan completed\n";
Jason M. Billsd1e40602019-05-09 11:43:51 -0700627 return;
628 }
629 }
630 }
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300631 boost::asio::post(io, [&io, &objServer, &cpuInfo, cpu, bus, dev]() mutable {
Jason M. Bills5d049732019-10-25 15:55:15 -0700632 scanPCIeDevice(io, objServer, cpuInfo, cpu, bus, dev);
Jason M. Billsd1e40602019-05-09 11:43:51 -0700633 });
634}
635
636static void peciAvailableCheck(boost::asio::steady_timer& peciWaitTimer,
637 boost::asio::io_service& io,
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300638 sdbusplus::asio::object_server& objServer,
639 std::vector<CPUInfo>& cpuInfo)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700640{
Jason M. Bills5d049732019-10-25 15:55:15 -0700641 static bool lastPECIState = false;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700642 bool peciAvailable = isPECIAvailable();
643 if (peciAvailable && !lastPECIState)
644 {
645 lastPECIState = true;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700646 static boost::asio::steady_timer pcieTimeout(io);
647 constexpr const int pcieWaitTime = 60;
648 pcieTimeout.expires_after(std::chrono::seconds(pcieWaitTime));
649 pcieTimeout.async_wait(
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300650 [&io, &objServer, &cpuInfo](const boost::system::error_code& ec) {
Jason M. Billsd1e40602019-05-09 11:43:51 -0700651 if (ec)
652 {
653 // operation_aborted is expected if timer is canceled
654 // before completion.
655 if (ec != boost::asio::error::operation_aborted)
656 {
657 std::cerr << "PECI PCIe async_wait failed " << ec;
658 }
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300659 lastPECIState = false;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700660 return;
661 }
662 // get the PECI client address list
Andrei Kartashev8e966032021-07-02 20:04:30 +0300663 if (getCPUBusMap(cpuInfo) != resCode::resOk)
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300664 {
665 lastPECIState = false;
666 return;
667 }
Jason M. Billsd1e40602019-05-09 11:43:51 -0700668 // scan PCIe starting from CPU 0, Bus 0, Device 0
Jason M. Billsee6d80b2021-06-11 07:37:30 -0700669 std::cerr << "PCIe scan started\n";
Jason M. Bills5d049732019-10-25 15:55:15 -0700670 scanPCIeDevice(io, objServer, cpuInfo, 0, 0, 0);
Jason M. Billsd1e40602019-05-09 11:43:51 -0700671 });
672 }
673 else if (!peciAvailable && lastPECIState)
674 {
675 lastPECIState = false;
676 }
677
678 peciWaitTimer.expires_after(
679 std::chrono::seconds(peci_pcie::peciCheckInterval));
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300680 peciWaitTimer.async_wait([&peciWaitTimer, &io, &objServer,
681 &cpuInfo](const boost::system::error_code& ec) {
Jason M. Billsd1e40602019-05-09 11:43:51 -0700682 if (ec)
683 {
684 // operation_aborted is expected if timer is canceled
685 // before completion.
686 if (ec != boost::asio::error::operation_aborted)
687 {
688 std::cerr << "PECI Available Check async_wait failed " << ec;
689 }
690 return;
691 }
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300692 peciAvailableCheck(peciWaitTimer, io, objServer, cpuInfo);
Jason M. Billsd1e40602019-05-09 11:43:51 -0700693 });
694}
695
Jason M. Billsee6d80b2021-06-11 07:37:30 -0700696static void waitForOSStandbyDelay(boost::asio::io_service& io,
697 sdbusplus::asio::object_server& objServer,
698 boost::asio::steady_timer& osStandbyTimer,
699 std::vector<CPUInfo>& cpuInfo)
700{
701 osStandbyTimer.expires_after(
702 std::chrono::seconds(peci_pcie::osStandbyDelaySeconds));
703
704 osStandbyTimer.async_wait(
705 [&io, &objServer, &cpuInfo](const boost::system::error_code& ec) {
706 if (ec == boost::asio::error::operation_aborted)
707 {
708 return; // we're being canceled
709 }
710 else if (ec)
711 {
712 std::cerr << "OS Standby async_wait failed: " << ec.value()
713 << ": " << ec.message() << "\n";
714 return;
715 }
716 // get the PECI client address list
Andrei Kartashev8e966032021-07-02 20:04:30 +0300717 if (getCPUBusMap(cpuInfo) != resCode::resOk)
Jason M. Billsee6d80b2021-06-11 07:37:30 -0700718 {
719 return;
720 }
721 // scan PCIe starting from CPU 0, Bus 0, Device 0
722 std::cerr << "PCIe scan started\n";
723 scanPCIeDevice(io, objServer, cpuInfo, 0, 0, 0);
724 });
725}
726
727static void monitorOSStandby(boost::asio::io_service& io,
728 std::shared_ptr<sdbusplus::asio::connection> conn,
729 sdbusplus::asio::object_server& objServer,
730 boost::asio::steady_timer& osStandbyTimer,
731 std::vector<CPUInfo>& cpuInfo)
732{
733 std::cerr << "Start OperatingSystemState Monitor\n";
734
735 static sdbusplus::bus::match::match osStateMatch(
736 *conn,
737 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
738 "PropertiesChanged',arg0='xyz.openbmc_project.State.OperatingSystem."
739 "Status'",
740 [&io, &objServer, &osStandbyTimer,
741 &cpuInfo](sdbusplus::message::message& msg) {
742 // Get the OS State from the message
743 std::string osStateInterface;
744 boost::container::flat_map<std::string, std::variant<std::string>>
745 propertiesChanged;
746 msg.read(osStateInterface, propertiesChanged);
747
748 for (const auto& [name, value] : propertiesChanged)
749 {
750 if (name == "OperatingSystemState")
751 {
752 const std::string* state = std::get_if<std::string>(&value);
753 if (state == nullptr)
754 {
755 std::cerr << "Unable to read OS state value\n";
756 return;
757 }
758
759 if (*state == "Standby")
760 {
761 peci_pcie::abortScan = false;
762 waitForOSStandbyDelay(io, objServer, osStandbyTimer,
763 cpuInfo);
764 }
765 else if (*state == "Inactive")
766 {
767 peci_pcie::abortScan = true;
768 osStandbyTimer.cancel();
769 }
770 }
771 }
772 });
773
774 // Check if the OS state is already available
775 conn->async_method_call(
776 [&io, &objServer, &osStandbyTimer,
777 &cpuInfo](boost::system::error_code ec,
778 const std::variant<std::string>& property) {
779 if (ec)
780 {
781 std::cerr << "error with OS state async_method_call\n";
782 return;
783 }
784
785 const std::string* state = std::get_if<std::string>(&property);
786 if (state == nullptr)
787 {
788 std::cerr << "Unable to read OS state value\n";
789 return;
790 }
791
792 // If the OS state is in Standby, then BIOS is done and we can
793 // continue. Otherwise, we just wait for the match
794 if (*state == "Standby")
795 {
796 waitForOSStandbyDelay(io, objServer, osStandbyTimer, cpuInfo);
797 }
798 },
799 "xyz.openbmc_project.State.OperatingSystem",
800 "/xyz/openbmc_project/state/os", "org.freedesktop.DBus.Properties",
801 "Get", "xyz.openbmc_project.State.OperatingSystem.Status",
802 "OperatingSystemState");
803}
804
Jason M. Billsd1e40602019-05-09 11:43:51 -0700805int main(int argc, char* argv[])
806{
807 // setup connection to dbus
808 boost::asio::io_service io;
809 std::shared_ptr<sdbusplus::asio::connection> conn =
810 std::make_shared<sdbusplus::asio::connection>(io);
811
812 // PECI PCIe Object
813 conn->request_name(peci_pcie::peciPCIeObject);
814 sdbusplus::asio::object_server server =
815 sdbusplus::asio::object_server(conn);
816
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300817 // CPU map
818 std::vector<CPUInfo> cpuInfo;
819
Jason M. Billsee6d80b2021-06-11 07:37:30 -0700820#ifdef WAIT_FOR_OS_STANDBY
821 boost::asio::steady_timer osStandbyTimer(io);
822 monitorOSStandby(io, conn, server, osStandbyTimer, cpuInfo);
823#else
Jason M. Billsd1e40602019-05-09 11:43:51 -0700824 // Start the PECI check loop
825 boost::asio::steady_timer peciWaitTimer(
826 io, std::chrono::seconds(peci_pcie::peciCheckInterval));
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300827 peciWaitTimer.async_wait([&peciWaitTimer, &io, &server,
828 &cpuInfo](const boost::system::error_code& ec) {
Jason M. Billsd1e40602019-05-09 11:43:51 -0700829 if (ec)
830 {
831 // operation_aborted is expected if timer is canceled
832 // before completion.
833 if (ec != boost::asio::error::operation_aborted)
834 {
835 std::cerr << "PECI Available Check async_wait failed " << ec;
836 }
837 return;
838 }
Andrei Kartashevd570dfd2020-12-16 17:33:16 +0300839 peciAvailableCheck(peciWaitTimer, io, server, cpuInfo);
Jason M. Billsd1e40602019-05-09 11:43:51 -0700840 });
Jason M. Billsee6d80b2021-06-11 07:37:30 -0700841#endif
Jason M. Billsd1e40602019-05-09 11:43:51 -0700842
843 io.run();
844
845 return 0;
846}