blob: 4f45243c9ff996193fdbcf05a60fdbbfa0052d22 [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
22#include <boost/container/flat_map.hpp>
23#include <boost/container/flat_set.hpp>
24#include <sdbusplus/asio/object_server.hpp>
25
26#include <iomanip>
27#include <iostream>
28#include <set>
29#include <sstream>
30
31namespace peci_pcie
32{
33static boost::container::flat_map<
34 int, boost::container::flat_map<
35 int, boost::container::flat_map<
36 int, std::shared_ptr<sdbusplus::asio::dbus_interface>>>>
37 pcieDeviceDBusMap;
38
39namespace function
40{
41static constexpr char const* functionTypeName = "FunctionType";
42static constexpr char const* deviceClassName = "DeviceClass";
43static constexpr char const* vendorIdName = "VendorId";
44static constexpr char const* deviceIdName = "DeviceId";
45static constexpr char const* classCodeName = "ClassCode";
46static constexpr char const* revisionIdName = "RevisionId";
47static constexpr char const* subsystemIdName = "SubsystemId";
48static constexpr char const* subsystemVendorIdName = "SubsystemVendorId";
49} // namespace function
50} // namespace peci_pcie
51
Jason M. Bills5d049732019-10-25 15:55:15 -070052struct CPUInfo
Jason M. Billsd1e40602019-05-09 11:43:51 -070053{
Jason M. Bills5d049732019-10-25 15:55:15 -070054 size_t addr;
55 bool skipCpuBuses;
56 boost::container::flat_set<size_t> cpuBusNums;
57};
58
59// PECI Client Address Map
60static void getClientAddrMap(std::vector<CPUInfo>& cpuInfo)
61{
62 for (size_t i = MIN_CLIENT_ADDR; i <= MAX_CLIENT_ADDR; i++)
Jason M. Billsd1e40602019-05-09 11:43:51 -070063 {
64 if (peci_Ping(i) == PECI_CC_SUCCESS)
65 {
Jason M. Bills5d049732019-10-25 15:55:15 -070066 cpuInfo.emplace_back(CPUInfo{i, false, {}});
67 }
68 }
69}
70
71// Get CPU PCIe Bus Numbers
72static void getCPUBusNums(std::vector<CPUInfo>& cpuInfo)
73{
74 for (CPUInfo& cpu : cpuInfo)
75 {
76 uint8_t cc = 0;
77 CPUModel model{};
78 uint8_t stepping = 0;
79 if (peci_GetCPUID(cpu.addr, &model, &stepping, &cc) != PECI_CC_SUCCESS)
80 {
81 std::cerr << "Cannot get CPUID!\n";
82 continue;
83 }
84
85 switch (model)
86 {
87 case skx:
88 {
89 // Get the assigned CPU bus numbers from CPUBUSNO and CPUBUSNO1
90 // (B(0) D8 F2 offsets CCh and D0h)
91 uint32_t cpuBusNum = 0;
92 if (peci_RdPCIConfigLocal(cpu.addr, 0, 8, 2, 0xCC, 4,
93 (uint8_t*)&cpuBusNum,
94 &cc) != PECI_CC_SUCCESS)
95 {
96 continue;
97 }
98 uint32_t cpuBusNum1 = 0;
99 if (peci_RdPCIConfigLocal(cpu.addr, 0, 8, 2, 0xD0, 4,
100 (uint8_t*)&cpuBusNum1,
101 &cc) != PECI_CC_SUCCESS)
102 {
103 continue;
104 }
105
106 // Add the CPU bus numbers to the set for this CPU
107 while (cpuBusNum)
108 {
109 // Get the LSB
110 size_t busNum = cpuBusNum & 0xFF;
111 cpu.cpuBusNums.insert(busNum);
112 // Shift right by one byte
113 cpuBusNum >>= 8;
114 }
115 while (cpuBusNum1)
116 {
117 // Get the LSB
118 size_t busNum = cpuBusNum1 & 0xFF;
119 cpu.cpuBusNums.insert(busNum);
120 // Shift right by one byte
121 cpuBusNum >>= 8;
122 }
123 cpu.skipCpuBuses = true;
124 }
Jason M. Billsd1e40602019-05-09 11:43:51 -0700125 }
126 }
127}
128
129static bool isPECIAvailable(void)
130{
Jason M. Bills5d049732019-10-25 15:55:15 -0700131 for (size_t i = MIN_CLIENT_ADDR; i <= MAX_CLIENT_ADDR; i++)
Jason M. Billsd1e40602019-05-09 11:43:51 -0700132 {
Jason M. Bills5d049732019-10-25 15:55:15 -0700133 if (peci_Ping(i) == PECI_CC_SUCCESS)
134 {
135 return true;
136 }
Jason M. Billsd1e40602019-05-09 11:43:51 -0700137 }
Jason M. Bills5d049732019-10-25 15:55:15 -0700138 return false;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700139}
140
141static bool getDataFromPCIeConfig(const int& clientAddr, const int& bus,
142 const int& dev, const int& func,
143 const int& offset, const int& size,
144 uint32_t& pciData)
145{
146 // PECI RdPCIConfig() currently only supports 4 byte reads, so adjust
147 // the offset and size to get the right data
148 static constexpr const int pciReadSize = 4;
149 int mod = offset % pciReadSize;
150 int pciOffset = offset - mod;
151 if (mod + size > pciReadSize)
152 {
153 return false;
154 }
155
156 std::array<uint8_t, pciReadSize> data;
157 uint8_t cc;
158 int ret = peci_RdPCIConfig(clientAddr, // CPU Address
159 bus, // PCI Bus
160 dev, // PCI Device
161 func, // PCI Function
162 pciOffset, // PCI Offset
163 data.data(), // PCI Read Data
164 &cc); // PECI Completion Code
165
166 if (ret != PECI_CC_SUCCESS || cc != PECI_DEV_CC_SUCCESS)
167 {
168 return false;
169 }
170
171 // Now build the requested data into a single number
172 pciData = 0;
173 for (int i = mod; i < mod + size; i++)
174 {
175 pciData |= data[i] << 8 * (i - mod);
176 }
177
178 return true;
179}
180
181static std::string getStringFromPCIeConfig(const int& clientAddr,
182 const int& bus, const int& dev,
183 const int& func, const int& offset,
184 const int& size)
185{
186 // Get the requested data
187 uint32_t data = 0;
188 if (!getDataFromPCIeConfig(clientAddr, bus, dev, func, offset, size, data))
189 {
190 return std::string();
191 }
192
193 // And convert it to a string
194 std::stringstream dataStream;
195 dataStream << "0x" << std::hex << std::setfill('0') << std::setw(size * 2)
196 << data;
197 return dataStream.str();
198}
199
200static std::string getVendorName(const int& clientAddr, const int& bus,
201 const int& dev)
202{
203 static constexpr const int vendorIDOffset = 0x00;
204 static constexpr const int vendorIDSize = 2;
205
206 // Get the header type register from function 0
207 uint32_t vendorID = 0;
208 if (!getDataFromPCIeConfig(clientAddr, bus, dev, 0, vendorIDOffset,
209 vendorIDSize, vendorID))
210 {
211 return std::string();
212 }
213 // Get the vendor name or use Other if it doesn't exist
214 return pciVendors.try_emplace(vendorID, otherVendor).first->second;
215}
216
217static std::string getDeviceClass(const int& clientAddr, const int& bus,
218 const int& dev, const int& func)
219{
220 static constexpr const int baseClassOffset = 0x0b;
221 static constexpr const int baseClassSize = 1;
222
223 // Get the Device Base Class
224 uint32_t baseClass = 0;
225 if (!getDataFromPCIeConfig(clientAddr, bus, dev, func, baseClassOffset,
226 baseClassSize, baseClass))
227 {
228 return std::string();
229 }
230 // Get the base class name or use Other if it doesn't exist
231 return pciDeviceClasses.try_emplace(baseClass, otherClass).first->second;
232}
233
234static bool isMultiFunction(const int& clientAddr, const int& bus,
235 const int& dev)
236{
237 static constexpr const int headerTypeOffset = 0x0e;
238 static constexpr const int headerTypeSize = 1;
239 static constexpr const int multiFuncBit = 1 << 7;
240
241 // Get the header type register from function 0
242 uint32_t headerType = 0;
243 if (!getDataFromPCIeConfig(clientAddr, bus, dev, 0, headerTypeOffset,
244 headerTypeSize, headerType))
245 {
246 return false;
247 }
248 // Check if it's a multifunction device
249 if (headerType & multiFuncBit)
250 {
251 return true;
252 }
253 return false;
254}
255
256static bool pcieFunctionExists(const int& clientAddr, const int& bus,
257 const int& dev, const int& func)
258{
259 constexpr const int pciIDOffset = 0;
260 constexpr const int pciIDSize = 4;
261 uint32_t pciID = 0;
262 if (!getDataFromPCIeConfig(clientAddr, bus, dev, func, pciIDOffset,
263 pciIDSize, pciID))
264 {
265 return false;
266 }
267
268 // if VID and DID are all 0s or 1s, then the device doesn't exist
269 if (pciID == 0x00000000 || pciID == 0xFFFFFFFF)
270 {
271 return false;
272 }
273
274 return true;
275}
276
277static bool pcieDeviceExists(const int& clientAddr, const int& bus,
278 const int& dev)
279{
280 // Check if this device exists by checking function 0
281 return (pcieFunctionExists(clientAddr, bus, dev, 0));
282}
283
284static void setPCIeProperty(const int& clientAddr, const int& bus,
285 const int& dev, const std::string& propertyName,
286 const std::string& propertyValue)
287{
288 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
289 peci_pcie::pcieDeviceDBusMap[clientAddr][bus][dev];
290
291 if (iface->is_initialized())
292 {
293 iface->set_property(propertyName, propertyValue);
294 }
295 else
296 {
297 iface->register_property(propertyName, propertyValue);
298 }
299}
300
301static void setDefaultPCIeFunctionProperties(const int& clientAddr,
302 const int& bus, const int& dev,
303 const int& func)
304{
305 // Set the function-specific properties
306 static constexpr const std::array functionProperties{
307 peci_pcie::function::functionTypeName,
308 peci_pcie::function::deviceClassName,
309 peci_pcie::function::vendorIdName,
310 peci_pcie::function::deviceIdName,
311 peci_pcie::function::classCodeName,
312 peci_pcie::function::revisionIdName,
313 peci_pcie::function::subsystemIdName,
314 peci_pcie::function::subsystemVendorIdName,
315 };
316
317 for (const char* name : functionProperties)
318 {
319 setPCIeProperty(clientAddr, bus, dev,
320 "Function" + std::to_string(func) + std::string(name),
321 std::string());
322 }
323}
324
325static void setPCIeFunctionProperties(const int& clientAddr, const int& bus,
326 const int& dev, const int& func)
327{
328 // Set the function type always to physical for now
329 setPCIeProperty(clientAddr, bus, dev,
330 "Function" + std::to_string(func) +
331 std::string(peci_pcie::function::functionTypeName),
332 "Physical");
333
334 // Set the function Device Class
335 setPCIeProperty(clientAddr, bus, dev,
336 "Function" + std::to_string(func) +
337 std::string(peci_pcie::function::deviceClassName),
338 getDeviceClass(clientAddr, bus, dev, func));
339
340 // Get PCI Function Properties that come from PCI config with the following
341 // offset and size info
342 static constexpr const std::array pciConfigInfo{
343 std::tuple<const char*, int, int>{peci_pcie::function::vendorIdName, 0,
344 2},
345 std::tuple<const char*, int, int>{peci_pcie::function::deviceIdName, 2,
346 2},
347 std::tuple<const char*, int, int>{peci_pcie::function::classCodeName, 9,
348 3},
349 std::tuple<const char*, int, int>{peci_pcie::function::revisionIdName,
350 8, 1},
351 std::tuple<const char*, int, int>{peci_pcie::function::subsystemIdName,
352 0x2e, 2},
353 std::tuple<const char*, int, int>{
354 peci_pcie::function::subsystemVendorIdName, 0x2c, 2}};
355
356 for (const auto& [name, offset, size] : pciConfigInfo)
357 {
358 setPCIeProperty(
359 clientAddr, bus, dev,
360 "Function" + std::to_string(func) + std::string(name),
361 getStringFromPCIeConfig(clientAddr, bus, dev, func, offset, size));
362 }
363}
364
365static void setPCIeDeviceProperties(const int& clientAddr, const int& bus,
366 const int& dev)
367{
368 // Set the device manufacturer
369 setPCIeProperty(clientAddr, bus, dev, "Manufacturer",
370 getVendorName(clientAddr, bus, dev));
371
372 // Set the device type
373 constexpr char const* deviceTypeName = "DeviceType";
374 if (isMultiFunction(clientAddr, bus, dev))
375 {
376 setPCIeProperty(clientAddr, bus, dev, deviceTypeName, "MultiFunction");
377 }
378 else
379 {
380 setPCIeProperty(clientAddr, bus, dev, deviceTypeName, "SingleFunction");
381 }
382}
383
384static void updatePCIeDevice(const int& clientAddr, const int& bus,
385 const int& dev)
386{
387 setPCIeDeviceProperties(clientAddr, bus, dev);
388
389 // Walk through and populate the functions for this device
390 for (int func = 0; func < peci_pcie::maxPCIFunctions; func++)
391 {
392 if (pcieFunctionExists(clientAddr, bus, dev, func))
393 {
394 // Set the properties for this function
395 setPCIeFunctionProperties(clientAddr, bus, dev, func);
396 }
397 else
398 {
399 // Set default properties for unused functions
400 setDefaultPCIeFunctionProperties(clientAddr, bus, dev, func);
401 }
402 }
403}
404
405static void addPCIeDevice(sdbusplus::asio::object_server& objServer,
406 const int& clientAddr, const int& cpu, const int& bus,
407 const int& dev)
408{
409 std::string pathName = std::string(peci_pcie::peciPCIePath) + "/S" +
410 std::to_string(cpu) + "B" + std::to_string(bus) +
411 "D" + std::to_string(dev);
412 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
413 objServer.add_interface(pathName, peci_pcie::peciPCIeDeviceInterface);
414 peci_pcie::pcieDeviceDBusMap[clientAddr][bus][dev] = iface;
415
416 // Update the properties for the new device
417 updatePCIeDevice(clientAddr, bus, dev);
418
419 iface->initialize();
420}
421
422static void removePCIeDevice(sdbusplus::asio::object_server& objServer,
423 const int& clientAddr, const int& bus,
424 const int& dev)
425{
426 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
427 peci_pcie::pcieDeviceDBusMap[clientAddr][bus][dev];
428
429 objServer.remove_interface(iface);
430
431 peci_pcie::pcieDeviceDBusMap[clientAddr][bus].erase(dev);
432 if (peci_pcie::pcieDeviceDBusMap[clientAddr][bus].empty())
433 {
434 peci_pcie::pcieDeviceDBusMap[clientAddr].erase(bus);
435 }
436 if (peci_pcie::pcieDeviceDBusMap[clientAddr].empty())
437 {
438 peci_pcie::pcieDeviceDBusMap.erase(clientAddr);
439 }
440}
441
442static bool pcieDeviceInDBusMap(const int& clientAddr, const int& bus,
443 const int& dev)
444{
445 if (auto clientAddrIt = peci_pcie::pcieDeviceDBusMap.find(clientAddr);
446 clientAddrIt != peci_pcie::pcieDeviceDBusMap.end())
447 {
448 if (auto busIt = clientAddrIt->second.find(bus);
449 busIt != clientAddrIt->second.end())
450 {
451 if (auto devIt = busIt->second.find(dev);
452 devIt != busIt->second.end())
453 {
454 if (devIt->second)
455 {
456 return true;
457 }
458 }
459 }
460 }
461 return false;
462}
463
Jason M. Bills5d049732019-10-25 15:55:15 -0700464static void scanNextPCIeDevice(boost::asio::io_service& io,
465 sdbusplus::asio::object_server& objServer,
466 std::vector<CPUInfo>& cpuInfo, int cpu, int bus,
467 int dev);
468
Jason M. Billsd1e40602019-05-09 11:43:51 -0700469static void scanPCIeDevice(boost::asio::io_service& io,
470 sdbusplus::asio::object_server& objServer,
Jason M. Bills5d049732019-10-25 15:55:15 -0700471 std::vector<CPUInfo>& cpuInfo, int cpu, int bus,
Jason M. Billsd1e40602019-05-09 11:43:51 -0700472 int dev)
473{
Jason M. Bills5d049732019-10-25 15:55:15 -0700474 // Check if this is a CPU bus that we should skip
475 if (cpuInfo[cpu].skipCpuBuses && cpuInfo[cpu].cpuBusNums.count(bus))
Jason M. Billsd1e40602019-05-09 11:43:51 -0700476 {
Jason M. Bills5d049732019-10-25 15:55:15 -0700477 std::cerr << "Skipping CPU " << cpu << " Bus Number " << bus << "\n";
478 // Skip all the devices on this bus
479 dev = peci_pcie::maxPCIDevices;
480 scanNextPCIeDevice(io, objServer, cpuInfo, cpu, bus, dev);
481 return;
482 }
483
484 if (pcieDeviceExists(cpuInfo[cpu].addr, bus, dev))
485 {
486 if (pcieDeviceInDBusMap(cpuInfo[cpu].addr, bus, dev))
Jason M. Billsd1e40602019-05-09 11:43:51 -0700487 {
488 // This device is already in D-Bus, so update it
Jason M. Bills5d049732019-10-25 15:55:15 -0700489 updatePCIeDevice(cpuInfo[cpu].addr, bus, dev);
Jason M. Billsd1e40602019-05-09 11:43:51 -0700490 }
491 else
492 {
493 // This device is not in D-Bus, so add it
Jason M. Bills5d049732019-10-25 15:55:15 -0700494 addPCIeDevice(objServer, cpuInfo[cpu].addr, cpu, bus, dev);
Jason M. Billsd1e40602019-05-09 11:43:51 -0700495 }
496 }
497 else
498 {
499 // If PECI is not available, then stop scanning
500 if (!isPECIAvailable())
501 {
502 return;
503 }
504
Jason M. Bills5d049732019-10-25 15:55:15 -0700505 if (pcieDeviceInDBusMap(cpuInfo[cpu].addr, bus, dev))
Jason M. Billsd1e40602019-05-09 11:43:51 -0700506 {
507 // This device is in D-Bus, so remove it
Jason M. Bills5d049732019-10-25 15:55:15 -0700508 removePCIeDevice(objServer, cpuInfo[cpu].addr, bus, dev);
Jason M. Billsd1e40602019-05-09 11:43:51 -0700509 }
510 }
Jason M. Bills5d049732019-10-25 15:55:15 -0700511 scanNextPCIeDevice(io, objServer, cpuInfo, cpu, bus, dev);
512}
Jason M. Billsd1e40602019-05-09 11:43:51 -0700513
Jason M. Bills5d049732019-10-25 15:55:15 -0700514static void scanNextPCIeDevice(boost::asio::io_service& io,
515 sdbusplus::asio::object_server& objServer,
516 std::vector<CPUInfo>& cpuInfo, int cpu, int bus,
517 int dev)
518{
Jason M. Billsd1e40602019-05-09 11:43:51 -0700519 // PCIe Device scan completed, so move to the next device
520 if (++dev >= peci_pcie::maxPCIDevices)
521 {
522 // All devices scanned, so move to the next bus
523 dev = 0;
524 if (++bus >= peci_pcie::maxPCIBuses)
525 {
526 // All buses scanned, so move to the next CPU
527 bus = 0;
Jason M. Bills5d049732019-10-25 15:55:15 -0700528 if (++cpu >= cpuInfo.size())
Jason M. Billsd1e40602019-05-09 11:43:51 -0700529 {
530 // All CPUs scanned, so we're done
531 return;
532 }
533 }
534 }
Jason M. Bills5d049732019-10-25 15:55:15 -0700535 boost::asio::post(io, [&io, &objServer, cpuInfo, cpu, bus, dev]() mutable {
536 scanPCIeDevice(io, objServer, cpuInfo, cpu, bus, dev);
Jason M. Billsd1e40602019-05-09 11:43:51 -0700537 });
538}
539
540static void peciAvailableCheck(boost::asio::steady_timer& peciWaitTimer,
541 boost::asio::io_service& io,
542 sdbusplus::asio::object_server& objServer)
543{
Jason M. Bills5d049732019-10-25 15:55:15 -0700544 static bool lastPECIState = false;
Jason M. Billsd1e40602019-05-09 11:43:51 -0700545 bool peciAvailable = isPECIAvailable();
546 if (peciAvailable && !lastPECIState)
547 {
548 lastPECIState = true;
549
550 static boost::asio::steady_timer pcieTimeout(io);
551 constexpr const int pcieWaitTime = 60;
552 pcieTimeout.expires_after(std::chrono::seconds(pcieWaitTime));
553 pcieTimeout.async_wait(
554 [&io, &objServer](const boost::system::error_code& ec) {
555 if (ec)
556 {
557 // operation_aborted is expected if timer is canceled
558 // before completion.
559 if (ec != boost::asio::error::operation_aborted)
560 {
561 std::cerr << "PECI PCIe async_wait failed " << ec;
562 }
563 return;
564 }
565 // get the PECI client address list
Jason M. Bills5d049732019-10-25 15:55:15 -0700566 std::vector<CPUInfo> cpuInfo;
567 getClientAddrMap(cpuInfo);
568 // get the CPU Bus Numbers to skip
569 getCPUBusNums(cpuInfo);
Jason M. Billsd1e40602019-05-09 11:43:51 -0700570 // scan PCIe starting from CPU 0, Bus 0, Device 0
Jason M. Bills5d049732019-10-25 15:55:15 -0700571 scanPCIeDevice(io, objServer, cpuInfo, 0, 0, 0);
Jason M. Billsd1e40602019-05-09 11:43:51 -0700572 });
573 }
574 else if (!peciAvailable && lastPECIState)
575 {
576 lastPECIState = false;
577 }
578
579 peciWaitTimer.expires_after(
580 std::chrono::seconds(peci_pcie::peciCheckInterval));
581 peciWaitTimer.async_wait([&peciWaitTimer, &io,
582 &objServer](const boost::system::error_code& ec) {
583 if (ec)
584 {
585 // operation_aborted is expected if timer is canceled
586 // before completion.
587 if (ec != boost::asio::error::operation_aborted)
588 {
589 std::cerr << "PECI Available Check async_wait failed " << ec;
590 }
591 return;
592 }
593 peciAvailableCheck(peciWaitTimer, io, objServer);
594 });
595}
596
597int main(int argc, char* argv[])
598{
599 // setup connection to dbus
600 boost::asio::io_service io;
601 std::shared_ptr<sdbusplus::asio::connection> conn =
602 std::make_shared<sdbusplus::asio::connection>(io);
603
604 // PECI PCIe Object
605 conn->request_name(peci_pcie::peciPCIeObject);
606 sdbusplus::asio::object_server server =
607 sdbusplus::asio::object_server(conn);
608
609 // Start the PECI check loop
610 boost::asio::steady_timer peciWaitTimer(
611 io, std::chrono::seconds(peci_pcie::peciCheckInterval));
612 peciWaitTimer.async_wait([&peciWaitTimer, &io,
613 &server](const boost::system::error_code& ec) {
614 if (ec)
615 {
616 // operation_aborted is expected if timer is canceled
617 // before completion.
618 if (ec != boost::asio::error::operation_aborted)
619 {
620 std::cerr << "PECI Available Check async_wait failed " << ec;
621 }
622 return;
623 }
624 peciAvailableCheck(peciWaitTimer, io, server);
625 });
626
627 io.run();
628
629 return 0;
630}