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