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