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