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