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