1 /*
2 // Copyright (c) 2018 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 "IntelCPUSensor.hpp"
18 #include "Thresholds.hpp"
19 #include "Utils.hpp"
20 #include "VariantVisitors.hpp"
21 
22 #include <peci.h>
23 
24 #include <boost/algorithm/string/replace.hpp>
25 #include <boost/asio/error.hpp>
26 #include <boost/asio/io_context.hpp>
27 #include <boost/asio/steady_timer.hpp>
28 #include <boost/container/flat_map.hpp>
29 #include <boost/container/flat_set.hpp>
30 #include <sdbusplus/asio/connection.hpp>
31 #include <sdbusplus/asio/object_server.hpp>
32 #include <sdbusplus/bus/match.hpp>
33 #include <sdbusplus/message.hpp>
34 
35 #include <algorithm>
36 #include <array>
37 #include <cctype>
38 #include <cerrno>
39 #include <chrono>
40 #include <cstddef>
41 #include <cstdint>
42 #include <cstring>
43 #include <filesystem>
44 #include <fstream>
45 #include <functional>
46 #include <ios>
47 #include <iostream>
48 #include <iterator>
49 #include <memory>
50 #include <optional>
51 #include <regex>
52 #include <sstream>
53 #include <string>
54 #include <utility>
55 #include <variant>
56 #include <vector>
57 
58 // clang-format off
59 // this needs to be included last or we'll have build issues
60 #include <linux/peci-ioctl.h>
61 #if !defined(PECI_MBX_INDEX_DDR_DIMM_TEMP)
62 #define PECI_MBX_INDEX_DDR_DIMM_TEMP MBX_INDEX_DDR_DIMM_TEMP
63 #endif
64 // clang-format on
65 
66 static constexpr bool debug = false;
67 
68 boost::container::flat_map<std::string, std::shared_ptr<IntelCPUSensor>>
69     gCpuSensors;
70 boost::container::flat_map<std::string,
71                            std::shared_ptr<sdbusplus::asio::dbus_interface>>
72     inventoryIfaces;
73 
74 enum State
75 {
76     OFF,  // host powered down
77     ON,   // host powered on
78     READY // host powered on and mem test passed - fully ready
79 };
80 
81 struct CPUConfig
82 {
CPUConfigCPUConfig83     CPUConfig(const uint64_t& bus, const uint64_t& addr,
84               const std::string& name, const State& state) :
85         bus(bus),
86         addr(addr), name(name), state(state)
87     {}
88     int bus;
89     int addr;
90     std::string name;
91     State state;
92 
operator <CPUConfig93     bool operator<(const CPUConfig& rhs) const
94     {
95         // NOLINTNEXTLINE
96         return (name < rhs.name);
97     }
98 };
99 
100 static constexpr const char* peciDev = "/dev/peci-";
101 static constexpr const char* peciDevPath = "/sys/bus/peci/devices/";
102 static constexpr const char* rescanPath = "/sys/bus/peci/rescan";
103 static constexpr const unsigned int rankNumMax = 8;
104 
105 namespace fs = std::filesystem;
106 
107 static constexpr auto sensorTypes{std::to_array<const char*>({"XeonCPU"})};
108 static constexpr auto hiddenProps{std::to_array<const char*>(
109     {IntelCPUSensor::labelTcontrol, "Tthrottle", "Tjmax"})};
110 
111 void detectCpuAsync(
112     boost::asio::steady_timer& pingTimer,
113     boost::asio::steady_timer& creationTimer, boost::asio::io_context& io,
114     sdbusplus::asio::object_server& objectServer,
115     std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
116     boost::container::flat_set<CPUConfig>& cpuConfigs,
117     ManagedObjectType& sensorConfigs);
118 
createSensorName(const std::string & label,const std::string & item,const int & cpuId)119 std::string createSensorName(const std::string& label, const std::string& item,
120                              const int& cpuId)
121 {
122     std::string sensorName = label;
123     if (item != "input")
124     {
125         sensorName += " " + item;
126     }
127 
128     std::string cpuStr = "CPU" + std::to_string(cpuId);
129     constexpr const char* subLabel = "DIMM";
130     std::size_t found = label.find(subLabel);
131     if (found != std::string::npos)
132     {
133         sensorName = cpuStr + " " + sensorName;
134     }
135     else
136     {
137         sensorName += " " + cpuStr;
138     }
139     // converting to Upper Camel case whole name
140     bool isWordEnd = true;
141     std::transform(sensorName.begin(), sensorName.end(), sensorName.begin(),
142                    [&isWordEnd](int c) {
143         if (std::isspace(c) != 0)
144         {
145             isWordEnd = true;
146         }
147         else
148         {
149             if (isWordEnd)
150             {
151                 isWordEnd = false;
152                 return std::toupper(c);
153             }
154         }
155         return c;
156     });
157     return sensorName;
158 }
159 
createSensors(boost::asio::io_context & io,sdbusplus::asio::object_server & objectServer,std::shared_ptr<sdbusplus::asio::connection> & dbusConnection,boost::container::flat_set<CPUConfig> & cpuConfigs,ManagedObjectType & sensorConfigs)160 bool createSensors(boost::asio::io_context& io,
161                    sdbusplus::asio::object_server& objectServer,
162                    std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
163                    boost::container::flat_set<CPUConfig>& cpuConfigs,
164                    ManagedObjectType& sensorConfigs)
165 {
166     bool available = false;
167     for (const CPUConfig& cpu : cpuConfigs)
168     {
169         if (cpu.state != State::OFF)
170         {
171             available = true;
172             std::shared_ptr<sdbusplus::asio::dbus_interface>& iface =
173                 inventoryIfaces[cpu.name];
174             if (iface != nullptr)
175             {
176                 continue;
177             }
178             iface = objectServer.add_interface(
179                 cpuInventoryPath + std::string("/") + cpu.name,
180                 "xyz.openbmc_project.Inventory.Item");
181             iface->register_property("PrettyName", cpu.name);
182             iface->register_property("Present", true);
183             iface->initialize();
184         }
185     }
186     if (!available)
187     {
188         return false;
189     }
190 
191     if (sensorConfigs.empty())
192     {
193         return false;
194     }
195 
196     std::vector<fs::path> hwmonNamePaths;
197     findFiles(fs::path(peciDevPath),
198               R"(peci-\d+/\d+-.+/peci[-_].+/hwmon/hwmon\d+/name$)",
199               hwmonNamePaths, 6);
200     if (hwmonNamePaths.empty())
201     {
202         std::cerr << "No CPU sensors in system\n";
203         return false;
204     }
205 
206     boost::container::flat_set<std::string> scannedDirectories;
207     boost::container::flat_set<std::string> createdSensors;
208 
209     for (const fs::path& hwmonNamePath : hwmonNamePaths)
210     {
211         auto hwmonDirectory = hwmonNamePath.parent_path();
212 
213         auto ret = scannedDirectories.insert(hwmonDirectory.string());
214         if (!ret.second)
215         {
216             continue; // already searched this path
217         }
218 
219         fs::path::iterator it = hwmonNamePath.begin();
220         std::advance(it, 6); // pick the 6th part for a PECI client device name
221         std::string deviceName = *it;
222 
223         size_t bus = 0;
224         size_t addr = 0;
225         if (!getDeviceBusAddr(deviceName, bus, addr))
226         {
227             continue;
228         }
229 
230         std::ifstream nameFile(hwmonNamePath);
231         if (!nameFile.good())
232         {
233             std::cerr << "Failure reading " << hwmonNamePath << "\n";
234             continue;
235         }
236         std::string hwmonName;
237         std::getline(nameFile, hwmonName);
238         nameFile.close();
239         if (hwmonName.empty())
240         {
241             // shouldn't have an empty name file
242             continue;
243         }
244         if (debug)
245         {
246             std::cout << "Checking: " << hwmonNamePath << ": " << hwmonName
247                       << "\n";
248         }
249 
250         std::string sensorType;
251         const SensorData* sensorData = nullptr;
252         const std::string* interfacePath = nullptr;
253         const SensorBaseConfiguration* baseConfiguration = nullptr;
254 
255         for (const auto& [path, cfgData] : sensorConfigs)
256         {
257             sensorData = &cfgData;
258             for (const char* type : sensorTypes)
259             {
260                 sensorType = type;
261                 auto sensorBase =
262                     sensorData->find(configInterfaceName(sensorType));
263                 if (sensorBase != sensorData->end())
264                 {
265                     baseConfiguration = &(*sensorBase);
266                     break;
267                 }
268             }
269             if (baseConfiguration == nullptr)
270             {
271                 std::cerr << "error finding base configuration for" << hwmonName
272                           << "\n";
273                 continue;
274             }
275             auto configurationBus = baseConfiguration->second.find("Bus");
276             auto configurationAddress =
277                 baseConfiguration->second.find("Address");
278 
279             if (configurationBus == baseConfiguration->second.end() ||
280                 configurationAddress == baseConfiguration->second.end())
281             {
282                 std::cerr << "error finding bus or address in configuration";
283                 continue;
284             }
285 
286             if (std::get<uint64_t>(configurationBus->second) != bus ||
287                 std::get<uint64_t>(configurationAddress->second) != addr)
288             {
289                 continue;
290             }
291 
292             interfacePath = &path.str;
293             break;
294         }
295         if (interfacePath == nullptr)
296         {
297             std::cerr << "failed to find match for " << hwmonName << "\n";
298             continue;
299         }
300 
301         auto findCpuId = baseConfiguration->second.find("CpuID");
302         if (findCpuId == baseConfiguration->second.end())
303         {
304             std::cerr << "could not determine CPU ID for " << hwmonName << "\n";
305             continue;
306         }
307         int cpuId = std::visit(VariantToUnsignedIntVisitor(),
308                                findCpuId->second);
309 
310         auto directory = hwmonNamePath.parent_path();
311         std::vector<fs::path> inputPaths;
312         if (!findFiles(directory, R"((temp|power)\d+_(input|average|cap)$)",
313                        inputPaths, 0))
314         {
315             std::cerr << "No temperature sensors in system\n";
316             continue;
317         }
318 
319         // iterate through all found temp sensors
320         for (const auto& inputPath : inputPaths)
321         {
322             auto fileParts = splitFileName(inputPath);
323             if (!fileParts)
324             {
325                 continue;
326             }
327             auto& [type, nr, item] = *fileParts;
328             auto inputPathStr = inputPath.string();
329             auto labelPath = boost::replace_all_copy(inputPathStr, item,
330                                                      "label");
331             std::ifstream labelFile(labelPath);
332             if (!labelFile.good())
333             {
334                 std::cerr << "Failure reading " << labelPath << "\n";
335                 continue;
336             }
337             std::string label;
338             std::getline(labelFile, label);
339             labelFile.close();
340 
341             std::string sensorName = createSensorName(label, item, cpuId);
342 
343             auto findSensor = gCpuSensors.find(sensorName);
344             if (findSensor != gCpuSensors.end())
345             {
346                 if (debug)
347                 {
348                     std::cout << "Skipped: " << inputPath << ": " << sensorName
349                               << " is already created\n";
350                 }
351                 continue;
352             }
353 
354             // check hidden properties
355             bool show = true;
356             for (const char* prop : hiddenProps)
357             {
358                 if (label == prop)
359                 {
360                     show = false;
361                     break;
362                 }
363             }
364 
365             /*
366              * Find if there is DtsCritOffset is configured in config file
367              * set it if configured or else set it to 0
368              */
369             double dtsOffset = 0;
370             if (label == "DTS")
371             {
372                 auto findThrOffset =
373                     baseConfiguration->second.find("DtsCritOffset");
374                 if (findThrOffset != baseConfiguration->second.end())
375                 {
376                     dtsOffset = std::visit(VariantToDoubleVisitor(),
377                                            findThrOffset->second);
378                 }
379             }
380 
381             std::vector<thresholds::Threshold> sensorThresholds;
382             std::string labelHead = label.substr(0, label.find(' '));
383             parseThresholdsFromConfig(*sensorData, sensorThresholds,
384                                       &labelHead);
385             if (sensorThresholds.empty())
386             {
387                 if (!parseThresholdsFromAttr(sensorThresholds, inputPathStr,
388                                              IntelCPUSensor::sensorScaleFactor,
389                                              dtsOffset, 0))
390                 {
391                     std::cerr << "error populating thresholds for "
392                               << sensorName << "\n";
393                 }
394             }
395             auto& sensorPtr = gCpuSensors[sensorName];
396             // make sure destructor fires before creating a new one
397             sensorPtr = nullptr;
398             sensorPtr = std::make_shared<IntelCPUSensor>(
399                 inputPathStr, sensorType, objectServer, dbusConnection, io,
400                 sensorName, std::move(sensorThresholds), *interfacePath, cpuId,
401                 show, dtsOffset);
402             sensorPtr->setupRead();
403             createdSensors.insert(sensorName);
404             if (debug)
405             {
406                 std::cout << "Mapped: " << inputPath << " to " << sensorName
407                           << "\n";
408             }
409         }
410     }
411 
412     if (static_cast<unsigned int>(!createdSensors.empty()) != 0U)
413     {
414         std::cout << "Sensor" << (createdSensors.size() == 1 ? " is" : "s are")
415                   << " created\n";
416     }
417 
418     return true;
419 }
420 
exportDevice(const CPUConfig & config)421 bool exportDevice(const CPUConfig& config)
422 {
423     std::ostringstream hex;
424     hex << std::hex << config.addr;
425     const std::string& addrHexStr = hex.str();
426     std::string busStr = std::to_string(config.bus);
427 
428     std::string parameters = "peci-client 0x" + addrHexStr;
429     std::string devPath = peciDevPath;
430     std::string delDevice = devPath + "peci-" + busStr + "/delete_device";
431     std::string newDevice = devPath + "peci-" + busStr + "/new_device";
432     std::string newClient = devPath + busStr + "-" + addrHexStr + "/driver";
433 
434     std::filesystem::path devicePath(newDevice);
435     const std::string& dir = devicePath.parent_path().string();
436     for (const auto& path : std::filesystem::directory_iterator(dir))
437     {
438         if (!std::filesystem::is_directory(path))
439         {
440             continue;
441         }
442 
443         const std::string& directoryName = path.path().filename();
444         if (directoryName.starts_with(busStr) &&
445             directoryName.ends_with(addrHexStr))
446         {
447             if (debug)
448             {
449                 std::cout << parameters << " on bus " << busStr
450                           << " is already exported\n";
451             }
452 
453             std::ofstream delDeviceFile(delDevice);
454             if (!delDeviceFile.good())
455             {
456                 std::cerr << "Error opening " << delDevice << "\n";
457                 return false;
458             }
459             delDeviceFile << parameters;
460             delDeviceFile.close();
461 
462             break;
463         }
464     }
465 
466     std::ofstream deviceFile(newDevice);
467     if (!deviceFile.good())
468     {
469         std::cerr << "Error opening " << newDevice << "\n";
470         return false;
471     }
472     deviceFile << parameters;
473     deviceFile.close();
474 
475     if (!std::filesystem::exists(newClient))
476     {
477         std::cerr << "Error creating " << newClient << "\n";
478         return false;
479     }
480 
481     std::cout << parameters << " on bus " << busStr << " is exported\n";
482 
483     return true;
484 }
485 
detectCpu(boost::asio::steady_timer & pingTimer,boost::asio::steady_timer & creationTimer,boost::asio::io_context & io,sdbusplus::asio::object_server & objectServer,std::shared_ptr<sdbusplus::asio::connection> & dbusConnection,boost::container::flat_set<CPUConfig> & cpuConfigs,ManagedObjectType & sensorConfigs)486 void detectCpu(boost::asio::steady_timer& pingTimer,
487                boost::asio::steady_timer& creationTimer,
488                boost::asio::io_context& io,
489                sdbusplus::asio::object_server& objectServer,
490                std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
491                boost::container::flat_set<CPUConfig>& cpuConfigs,
492                ManagedObjectType& sensorConfigs)
493 {
494     size_t rescanDelaySeconds = 0;
495     static bool keepPinging = false;
496     int peciFd = -1;
497 
498     for (CPUConfig& config : cpuConfigs)
499     {
500         if (config.state == State::READY)
501         {
502             continue;
503         }
504 
505         std::fstream rescan{rescanPath, std::ios::out};
506         if (rescan.is_open())
507         {
508             std::vector<fs::path> peciPaths;
509             std::ostringstream searchPath;
510             searchPath << std::hex << "peci-" << config.bus << "/" << config.bus
511                        << "-" << config.addr;
512             findFiles(fs::path(peciDevPath + searchPath.str()),
513                       R"(peci_cpu.dimmtemp.+/hwmon/hwmon\d+/name$)", peciPaths,
514                       3);
515             if (!peciPaths.empty())
516             {
517                 config.state = State::READY;
518                 rescanDelaySeconds = 1;
519             }
520             else
521             {
522                 findFiles(fs::path(peciDevPath + searchPath.str()),
523                           R"(peci_cpu.cputemp.+/hwmon/hwmon\d+/name$)",
524                           peciPaths, 3);
525                 if (!peciPaths.empty())
526                 {
527                     config.state = State::ON;
528                     rescanDelaySeconds = 3;
529                 }
530                 else
531                 {
532                     // https://www.kernel.org/doc/html/latest/admin-guide/abi-testing.html#abi-sys-bus-peci-rescan
533                     rescan << "1";
534                 }
535             }
536             if (config.state != State::READY)
537             {
538                 keepPinging = true;
539             }
540 
541             continue;
542         }
543 
544         std::string peciDevPath = peciDev + std::to_string(config.bus);
545 
546         peci_SetDevName(peciDevPath.data());
547 
548         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
549         if ((peci_Lock(&peciFd, PECI_NO_WAIT) != PECI_CC_SUCCESS) ||
550             (peciFd < 0))
551         {
552             std::cerr << "unable to open " << peciDevPath << " "
553                       << std::strerror(errno) << "\n";
554             detectCpuAsync(pingTimer, creationTimer, io, objectServer,
555                            dbusConnection, cpuConfigs, sensorConfigs);
556             return;
557         }
558 
559         State newState = State::OFF;
560 
561         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
562         if (peci_Ping(config.addr) == PECI_CC_SUCCESS)
563         {
564             bool dimmReady = false;
565             for (unsigned int rank = 0; rank < rankNumMax; rank++)
566             {
567                 std::array<uint8_t, 8> pkgConfig{};
568                 uint8_t cc = 0;
569 
570                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
571                 if (peci_RdPkgConfig(config.addr, PECI_MBX_INDEX_DDR_DIMM_TEMP,
572                                      rank, 4, pkgConfig.data(),
573                                      &cc) == PECI_CC_SUCCESS)
574                 {
575                     // Depending on CPU generation, both 0 and 0xFF can be used
576                     // to indicate no DIMM presence
577                     if (((pkgConfig[0] != 0xFF) && (pkgConfig[0] != 0U)) ||
578                         ((pkgConfig[1] != 0xFF) && (pkgConfig[1] != 0U)))
579                     {
580                         dimmReady = true;
581                         break;
582                     }
583                 }
584                 else
585                 {
586                     break;
587                 }
588             }
589 
590             if (dimmReady)
591             {
592                 newState = State::READY;
593             }
594             else
595             {
596                 newState = State::ON;
597             }
598         }
599 
600         if (config.state != newState)
601         {
602             if (newState != State::OFF)
603             {
604                 if (config.state == State::OFF)
605                 {
606                     std::array<uint8_t, 8> pkgConfig{};
607                     uint8_t cc = 0;
608 
609                     if (peci_RdPkgConfig(config.addr, PECI_MBX_INDEX_CPU_ID, 0,
610                                          4, pkgConfig.data(),
611                                          &cc) == PECI_CC_SUCCESS)
612                     {
613                         std::cout << config.name << " is detected\n";
614                         if (!exportDevice(config))
615                         {
616                             newState = State::OFF;
617                         }
618                     }
619                     else
620                     {
621                         newState = State::OFF;
622                     }
623                 }
624 
625                 if (newState == State::ON)
626                 {
627                     rescanDelaySeconds = 3;
628                 }
629                 else if (newState == State::READY)
630                 {
631                     rescanDelaySeconds = 5;
632                     std::cout << "DIMM(s) on " << config.name
633                               << " is/are detected\n";
634                 }
635             }
636 
637             config.state = newState;
638         }
639 
640         if (config.state != State::READY)
641         {
642             keepPinging = true;
643         }
644 
645         if (debug)
646         {
647             std::cout << config.name << ", state: " << config.state << "\n";
648         }
649         peci_Unlock(peciFd);
650     }
651 
652     if (rescanDelaySeconds != 0U)
653     {
654         creationTimer.expires_after(std::chrono::seconds(rescanDelaySeconds));
655         creationTimer.async_wait([&](const boost::system::error_code& ec) {
656             if (ec == boost::asio::error::operation_aborted)
657             {
658                 return; // we're being canceled
659             }
660 
661             if (!createSensors(io, objectServer, dbusConnection, cpuConfigs,
662                                sensorConfigs) ||
663                 keepPinging)
664             {
665                 detectCpuAsync(pingTimer, creationTimer, io, objectServer,
666                                dbusConnection, cpuConfigs, sensorConfigs);
667             }
668         });
669     }
670     else if (keepPinging)
671     {
672         detectCpuAsync(pingTimer, creationTimer, io, objectServer,
673                        dbusConnection, cpuConfigs, sensorConfigs);
674     }
675 }
676 
detectCpuAsync(boost::asio::steady_timer & pingTimer,boost::asio::steady_timer & creationTimer,boost::asio::io_context & io,sdbusplus::asio::object_server & objectServer,std::shared_ptr<sdbusplus::asio::connection> & dbusConnection,boost::container::flat_set<CPUConfig> & cpuConfigs,ManagedObjectType & sensorConfigs)677 void detectCpuAsync(
678     boost::asio::steady_timer& pingTimer,
679     boost::asio::steady_timer& creationTimer, boost::asio::io_context& io,
680     sdbusplus::asio::object_server& objectServer,
681     std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
682     boost::container::flat_set<CPUConfig>& cpuConfigs,
683     ManagedObjectType& sensorConfigs)
684 {
685     pingTimer.expires_after(std::chrono::seconds(1));
686     pingTimer.async_wait([&](const boost::system::error_code& ec) {
687         if (ec == boost::asio::error::operation_aborted)
688         {
689             return; // we're being canceled
690         }
691 
692         detectCpu(pingTimer, creationTimer, io, objectServer, dbusConnection,
693                   cpuConfigs, sensorConfigs);
694     });
695 }
696 
getCpuConfig(const std::shared_ptr<sdbusplus::asio::connection> & systemBus,boost::container::flat_set<CPUConfig> & cpuConfigs,ManagedObjectType & sensorConfigs,sdbusplus::asio::object_server & objectServer)697 bool getCpuConfig(const std::shared_ptr<sdbusplus::asio::connection>& systemBus,
698                   boost::container::flat_set<CPUConfig>& cpuConfigs,
699                   ManagedObjectType& sensorConfigs,
700                   sdbusplus::asio::object_server& objectServer)
701 {
702     bool useCache = false;
703     sensorConfigs.clear();
704     // use new data the first time, then refresh
705     for (const char* type : sensorTypes)
706     {
707         if (!getSensorConfiguration(type, systemBus, sensorConfigs, useCache))
708         {
709             return false;
710         }
711         useCache = true;
712     }
713 
714     // check PECI client addresses and names from CPU configuration
715     // before starting ping operation
716     for (const char* type : sensorTypes)
717     {
718         for (const auto& [path, cfgData] : sensorConfigs)
719         {
720             for (const auto& [intf, cfg] : cfgData)
721             {
722                 if (intf != configInterfaceName(type))
723                 {
724                     continue;
725                 }
726 
727                 auto findName = cfg.find("Name");
728                 if (findName == cfg.end())
729                 {
730                     continue;
731                 }
732                 std::string nameRaw = std::visit(VariantToStringVisitor(),
733                                                  findName->second);
734                 std::string name = std::regex_replace(nameRaw, illegalDbusRegex,
735                                                       "_");
736 
737                 auto present = std::optional<bool>();
738                 // if we can't detect it via gpio, we set presence later
739                 for (const auto& [suppIntf, suppCfg] : cfgData)
740                 {
741                     if (suppIntf.find("PresenceGpio") != std::string::npos)
742                     {
743                         present = cpuIsPresent(suppCfg);
744                         break;
745                     }
746                 }
747 
748                 if (inventoryIfaces.find(name) == inventoryIfaces.end() &&
749                     present)
750                 {
751                     auto iface = objectServer.add_interface(
752                         cpuInventoryPath + std::string("/") + name,
753                         "xyz.openbmc_project.Inventory.Item");
754                     iface->register_property("PrettyName", name);
755                     iface->register_property("Present", *present);
756                     iface->initialize();
757                     inventoryIfaces[name] = std::move(iface);
758                 }
759 
760                 auto findBus = cfg.find("Bus");
761                 if (findBus == cfg.end())
762                 {
763                     std::cerr << "Can't find 'Bus' setting in " << name << "\n";
764                     continue;
765                 }
766                 uint64_t bus = std::visit(VariantToUnsignedIntVisitor(),
767                                           findBus->second);
768 
769                 auto findAddress = cfg.find("Address");
770                 if (findAddress == cfg.end())
771                 {
772                     std::cerr << "Can't find 'Address' setting in " << name
773                               << "\n";
774                     continue;
775                 }
776                 uint64_t addr = std::visit(VariantToUnsignedIntVisitor(),
777                                            findAddress->second);
778 
779                 if (debug)
780                 {
781                     std::cout << "bus: " << bus << "\n";
782                     std::cout << "addr: " << addr << "\n";
783                     std::cout << "name: " << name << "\n";
784                     std::cout << "type: " << type << "\n";
785                 }
786 
787                 cpuConfigs.emplace(bus, addr, name, State::OFF);
788             }
789         }
790     }
791 
792     if (static_cast<unsigned int>(!cpuConfigs.empty()) != 0U)
793     {
794         std::cout << "CPU config" << (cpuConfigs.size() == 1 ? " is" : "s are")
795                   << " parsed\n";
796         return true;
797     }
798 
799     return false;
800 }
801 
main()802 int main()
803 {
804     boost::asio::io_context io;
805     auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
806     boost::container::flat_set<CPUConfig> cpuConfigs;
807 
808     sdbusplus::asio::object_server objectServer(systemBus, true);
809     objectServer.add_manager("/xyz/openbmc_project/sensors");
810     boost::asio::steady_timer pingTimer(io);
811     boost::asio::steady_timer creationTimer(io);
812     boost::asio::steady_timer filterTimer(io);
813     ManagedObjectType sensorConfigs;
814 
815     filterTimer.expires_after(std::chrono::seconds(1));
816     filterTimer.async_wait([&](const boost::system::error_code& ec) {
817         if (ec == boost::asio::error::operation_aborted)
818         {
819             return; // we're being canceled
820         }
821 
822         if (getCpuConfig(systemBus, cpuConfigs, sensorConfigs, objectServer))
823         {
824             detectCpuAsync(pingTimer, creationTimer, io, objectServer,
825                            systemBus, cpuConfigs, sensorConfigs);
826         }
827     });
828 
829     std::function<void(sdbusplus::message_t&)> eventHandler =
830         [&](sdbusplus::message_t& message) {
831         if (message.is_method_error())
832         {
833             std::cerr << "callback method error\n";
834             return;
835         }
836 
837         if (debug)
838         {
839             std::cout << message.get_path() << " is changed\n";
840         }
841 
842         // this implicitly cancels the timer
843         filterTimer.expires_after(std::chrono::seconds(1));
844         filterTimer.async_wait([&](const boost::system::error_code& ec) {
845             if (ec == boost::asio::error::operation_aborted)
846             {
847                 return; // we're being canceled
848             }
849 
850             if (getCpuConfig(systemBus, cpuConfigs, sensorConfigs,
851                              objectServer))
852             {
853                 detectCpuAsync(pingTimer, creationTimer, io, objectServer,
854                                systemBus, cpuConfigs, sensorConfigs);
855             }
856         });
857     };
858 
859     std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches =
860         setupPropertiesChangedMatches(*systemBus, sensorTypes, eventHandler);
861 
862     systemBus->request_name("xyz.openbmc_project.IntelCPUSensor");
863 
864     setupManufacturingModeMatch(*systemBus);
865     io.run();
866     return 0;
867 }
868