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