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 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 lg2::error("No CPU sensors in system");
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 lg2::error("Failure reading '{PATH}'", "PATH", hwmonNamePath);
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 lg2::info("Checking: '{PATH}': '{NAME}'", "PATH", hwmonNamePath,
244 "NAME", hwmonName);
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 lg2::error("error finding base configuration for '{NAME}'",
269 "NAME", hwmonName);
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 lg2::error("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 lg2::error("failed to find match for '{NAME}'", "NAME", hwmonName);
295 continue;
296 }
297
298 auto findCpuId = baseConfiguration->second.find("CpuID");
299 if (findCpuId == baseConfiguration->second.end())
300 {
301 lg2::error("could not determine CPU ID for '{NAME}'", "NAME",
302 hwmonName);
303 continue;
304 }
305 int cpuId =
306 std::visit(VariantToUnsignedIntVisitor(), findCpuId->second);
307
308 auto directory = hwmonNamePath.parent_path();
309 std::vector<std::filesystem::path> inputPaths;
310 if (!findFiles(directory, R"((temp|power)\d+_(input|average|cap)$)",
311 inputPaths, 0))
312 {
313 lg2::error("No temperature sensors in system");
314 continue;
315 }
316
317 // iterate through all found temp sensors
318 for (const auto& inputPath : inputPaths)
319 {
320 auto fileParts = splitFileName(inputPath);
321 if (!fileParts)
322 {
323 continue;
324 }
325 auto& [type, nr, item] = *fileParts;
326 auto inputPathStr = inputPath.string();
327 auto labelPath =
328 boost::replace_all_copy(inputPathStr, item, "label");
329 std::ifstream labelFile(labelPath);
330 if (!labelFile.good())
331 {
332 lg2::error("Failure reading '{PATH}'", "PATH", labelPath);
333 continue;
334 }
335 std::string label;
336 std::getline(labelFile, label);
337 labelFile.close();
338
339 std::string sensorName = createSensorName(label, item, cpuId);
340
341 auto findSensor = gCpuSensors.find(sensorName);
342 if (findSensor != gCpuSensors.end())
343 {
344 if (debug)
345 {
346 lg2::info("Skipped: '{PATH}': '{NAME}' is already created",
347 "PATH", inputPath, "NAME", sensorName);
348 }
349 continue;
350 }
351
352 // check hidden properties
353 bool show = true;
354 for (const char* prop : hiddenProps)
355 {
356 if (label == prop)
357 {
358 show = false;
359 break;
360 }
361 }
362
363 /*
364 * Find if there is DtsCritOffset is configured in config file
365 * set it if configured or else set it to 0
366 */
367 double dtsOffset = 0;
368 if (label == "DTS")
369 {
370 auto findThrOffset =
371 baseConfiguration->second.find("DtsCritOffset");
372 if (findThrOffset != baseConfiguration->second.end())
373 {
374 dtsOffset = std::visit(VariantToDoubleVisitor(),
375 findThrOffset->second);
376 }
377 }
378
379 std::vector<thresholds::Threshold> sensorThresholds;
380 std::string labelHead = label.substr(0, label.find(' '));
381 parseThresholdsFromConfig(*sensorData, sensorThresholds,
382 &labelHead);
383 if (sensorThresholds.empty())
384 {
385 if (!parseThresholdsFromAttr(sensorThresholds, inputPathStr,
386 IntelCPUSensor::sensorScaleFactor,
387 dtsOffset, 0))
388 {
389 lg2::error("error populating thresholds for '{NAME}'",
390 "NAME", sensorName);
391 }
392 }
393 auto& sensorPtr = gCpuSensors[sensorName];
394 // make sure destructor fires before creating a new one
395 sensorPtr = nullptr;
396 sensorPtr = std::make_shared<IntelCPUSensor>(
397 inputPathStr, sensorType, objectServer, dbusConnection, io,
398 sensorName, std::move(sensorThresholds), *interfacePath, cpuId,
399 show, dtsOffset);
400 sensorPtr->setupRead();
401 createdSensors.insert(sensorName);
402 if (debug)
403 {
404 lg2::info("Mapped: '{PATH}' to '{NAME}'", "PATH", inputPath,
405 "NAME", sensorName);
406 }
407 }
408 }
409
410 if (static_cast<unsigned int>(!createdSensors.empty()) != 0U)
411 {
412 if (createdSensors.size() == 1)
413 {
414 lg2::info("Sensor is created");
415 }
416 else
417 {
418 lg2::info("Sensors are created");
419 }
420 }
421
422 return true;
423 }
424
exportDevice(const CPUConfig & config)425 bool exportDevice(const CPUConfig& config)
426 {
427 std::ostringstream hex;
428 hex << std::hex << config.addr;
429 const std::string& addrHexStr = hex.str();
430 std::string busStr = std::to_string(config.bus);
431
432 std::string parameters = "peci-client 0x" + addrHexStr;
433 std::string devPath = peciDevPath;
434 std::string delDevice = devPath + "peci-" + busStr + "/delete_device";
435 std::string newDevice = devPath + "peci-" + busStr + "/new_device";
436 std::string newClient = devPath + busStr + "-" + addrHexStr + "/driver";
437
438 std::filesystem::path devicePath(newDevice);
439 const std::string& dir = devicePath.parent_path().string();
440 for (const auto& path : std::filesystem::directory_iterator(dir))
441 {
442 if (!std::filesystem::is_directory(path))
443 {
444 continue;
445 }
446
447 const std::string& directoryName = path.path().filename();
448 if (directoryName.starts_with(busStr) &&
449 directoryName.ends_with(addrHexStr))
450 {
451 if (debug)
452 {
453 lg2::info("'{PARAMETERS}' on bus '{BUS}' is already exported",
454 "PARAMETERS", parameters, "BUS", busStr);
455 }
456
457 std::ofstream delDeviceFile(delDevice);
458 if (!delDeviceFile.good())
459 {
460 lg2::error("Error opening '{DEVICE}'", "DEVICE", delDevice);
461 return false;
462 }
463 delDeviceFile << parameters;
464 delDeviceFile.close();
465
466 break;
467 }
468 }
469
470 std::ofstream deviceFile(newDevice);
471 if (!deviceFile.good())
472 {
473 lg2::error("Error opening '{DEVICE}'", "DEVICE", newDevice);
474 return false;
475 }
476 deviceFile << parameters;
477 deviceFile.close();
478
479 if (!std::filesystem::exists(newClient))
480 {
481 lg2::error("Error creating '{CLIENT}'", "CLIENT", newClient);
482 return false;
483 }
484
485 lg2::info("'{PARAMETERS}' on bus '{BUS}' is exported", "PARAMETERS",
486 parameters, "BUS", busStr);
487
488 return true;
489 }
490
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)491 void detectCpu(boost::asio::steady_timer& pingTimer,
492 boost::asio::steady_timer& creationTimer,
493 boost::asio::io_context& io,
494 sdbusplus::asio::object_server& objectServer,
495 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
496 boost::container::flat_set<CPUConfig>& cpuConfigs,
497 ManagedObjectType& sensorConfigs)
498 {
499 size_t rescanDelaySeconds = 0;
500 static bool keepPinging = false;
501 int peciFd = -1;
502
503 for (CPUConfig& config : cpuConfigs)
504 {
505 if (config.state == State::READY)
506 {
507 continue;
508 }
509
510 std::fstream rescan{rescanPath, std::ios::out};
511 if (rescan.is_open())
512 {
513 std::vector<std::filesystem::path> peciPaths;
514 std::ostringstream searchPath;
515 searchPath << std::hex << "peci-" << config.bus << "/" << config.bus
516 << "-" << config.addr;
517 findFiles(std::filesystem::path(peciDevPath + searchPath.str()),
518 R"(peci_cpu.dimmtemp.+/hwmon/hwmon\d+/name$)", peciPaths,
519 3);
520 if (!peciPaths.empty())
521 {
522 config.state = State::READY;
523 rescanDelaySeconds = 1;
524 }
525 else
526 {
527 findFiles(std::filesystem::path(peciDevPath + searchPath.str()),
528 R"(peci_cpu.cputemp.+/hwmon/hwmon\d+/name$)",
529 peciPaths, 3);
530 if (!peciPaths.empty())
531 {
532 config.state = State::ON;
533 rescanDelaySeconds = 3;
534 }
535 else
536 {
537 // https://www.kernel.org/doc/html/latest/admin-guide/abi-testing.html#abi-sys-bus-peci-rescan
538 rescan << "1";
539 }
540 }
541 if (config.state != State::READY)
542 {
543 keepPinging = true;
544 }
545
546 continue;
547 }
548
549 std::string peciDevPath = peciDev + std::to_string(config.bus);
550
551 peci_SetDevName(peciDevPath.data());
552
553 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
554 if ((peci_Lock(&peciFd, PECI_NO_WAIT) != PECI_CC_SUCCESS) ||
555 (peciFd < 0))
556 {
557 lg2::error("unable to open '{PATH}', '{ERRNO}'", "PATH",
558 peciDevPath, "ERRNO", std::strerror(errno));
559 detectCpuAsync(pingTimer, creationTimer, io, objectServer,
560 dbusConnection, cpuConfigs, sensorConfigs);
561 return;
562 }
563
564 State newState = State::OFF;
565
566 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
567 if (peci_Ping(config.addr) == PECI_CC_SUCCESS)
568 {
569 bool dimmReady = false;
570 for (unsigned int rank = 0; rank < rankNumMax; rank++)
571 {
572 std::array<uint8_t, 8> pkgConfig{};
573 uint8_t cc = 0;
574
575 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
576 if (peci_RdPkgConfig(config.addr, PECI_MBX_INDEX_DDR_DIMM_TEMP,
577 rank, 4, pkgConfig.data(), &cc) ==
578 PECI_CC_SUCCESS)
579 {
580 // Depending on CPU generation, both 0 and 0xFF can be used
581 // to indicate no DIMM presence
582 if (((pkgConfig[0] != 0xFF) && (pkgConfig[0] != 0U)) ||
583 ((pkgConfig[1] != 0xFF) && (pkgConfig[1] != 0U)))
584 {
585 dimmReady = true;
586 break;
587 }
588 }
589 else
590 {
591 break;
592 }
593 }
594
595 if (dimmReady)
596 {
597 newState = State::READY;
598 }
599 else
600 {
601 newState = State::ON;
602 }
603 }
604
605 if (config.state != newState)
606 {
607 if (newState != State::OFF)
608 {
609 if (config.state == State::OFF)
610 {
611 std::array<uint8_t, 8> pkgConfig{};
612 uint8_t cc = 0;
613
614 if (peci_RdPkgConfig(config.addr, PECI_MBX_INDEX_CPU_ID, 0,
615 4, pkgConfig.data(), &cc) ==
616 PECI_CC_SUCCESS)
617 {
618 lg2::info("'{NAME}' is detected", "NAME", config.name);
619 if (!exportDevice(config))
620 {
621 newState = State::OFF;
622 }
623 }
624 else
625 {
626 newState = State::OFF;
627 }
628 }
629
630 if (newState == State::ON)
631 {
632 rescanDelaySeconds = 3;
633 }
634 else if (newState == State::READY)
635 {
636 rescanDelaySeconds = 5;
637 lg2::info("DIMM(s) on '{NAME}' is/are detected", "NAME",
638 config.name);
639 }
640 }
641
642 config.state = newState;
643 }
644
645 if (config.state != State::READY)
646 {
647 keepPinging = true;
648 }
649
650 if (debug)
651 {
652 lg2::info("'{NAME}', state: '{STATE}'", "NAME", config.name,
653 "STATE", config.state);
654 }
655 peci_Unlock(peciFd);
656 }
657
658 if (rescanDelaySeconds != 0U)
659 {
660 creationTimer.expires_after(std::chrono::seconds(rescanDelaySeconds));
661 creationTimer.async_wait([&](const boost::system::error_code& ec) {
662 if (ec == boost::asio::error::operation_aborted)
663 {
664 return; // we're being canceled
665 }
666
667 if (!createSensors(io, objectServer, dbusConnection, cpuConfigs,
668 sensorConfigs) ||
669 keepPinging)
670 {
671 detectCpuAsync(pingTimer, creationTimer, io, objectServer,
672 dbusConnection, cpuConfigs, sensorConfigs);
673 }
674 });
675 }
676 else if (keepPinging)
677 {
678 detectCpuAsync(pingTimer, creationTimer, io, objectServer,
679 dbusConnection, cpuConfigs, sensorConfigs);
680 }
681 }
682
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)683 void detectCpuAsync(
684 boost::asio::steady_timer& pingTimer,
685 boost::asio::steady_timer& creationTimer, boost::asio::io_context& io,
686 sdbusplus::asio::object_server& objectServer,
687 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
688 boost::container::flat_set<CPUConfig>& cpuConfigs,
689 ManagedObjectType& sensorConfigs)
690 {
691 pingTimer.expires_after(std::chrono::seconds(1));
692 pingTimer.async_wait([&](const boost::system::error_code& ec) {
693 if (ec == boost::asio::error::operation_aborted)
694 {
695 return; // we're being canceled
696 }
697
698 detectCpu(pingTimer, creationTimer, io, objectServer, dbusConnection,
699 cpuConfigs, sensorConfigs);
700 });
701 }
702
getCpuConfig(const std::shared_ptr<sdbusplus::asio::connection> & systemBus,boost::container::flat_set<CPUConfig> & cpuConfigs,ManagedObjectType & sensorConfigs,sdbusplus::asio::object_server & objectServer)703 bool getCpuConfig(const std::shared_ptr<sdbusplus::asio::connection>& systemBus,
704 boost::container::flat_set<CPUConfig>& cpuConfigs,
705 ManagedObjectType& sensorConfigs,
706 sdbusplus::asio::object_server& objectServer)
707 {
708 bool useCache = false;
709 sensorConfigs.clear();
710 // use new data the first time, then refresh
711 for (const char* type : sensorTypes)
712 {
713 if (!getSensorConfiguration(type, systemBus, sensorConfigs, useCache))
714 {
715 return false;
716 }
717 useCache = true;
718 }
719
720 // check PECI client addresses and names from CPU configuration
721 // before starting ping operation
722 for (const char* type : sensorTypes)
723 {
724 for (const auto& [path, cfgData] : sensorConfigs)
725 {
726 for (const auto& [intf, cfg] : cfgData)
727 {
728 if (intf != configInterfaceName(type))
729 {
730 continue;
731 }
732
733 auto findName = cfg.find("Name");
734 if (findName == cfg.end())
735 {
736 continue;
737 }
738 std::string nameRaw =
739 std::visit(VariantToStringVisitor(), findName->second);
740 std::string name =
741 std::regex_replace(nameRaw, illegalDbusRegex, "_");
742
743 auto present = std::optional<bool>();
744 // if we can't detect it via gpio, we set presence later
745 for (const auto& [suppIntf, suppCfg] : cfgData)
746 {
747 if (suppIntf.find("PresenceGpio") != std::string::npos)
748 {
749 present = cpuIsPresent(suppCfg);
750 break;
751 }
752 }
753
754 if (inventoryIfaces.find(name) == inventoryIfaces.end() &&
755 present)
756 {
757 auto iface = objectServer.add_interface(
758 cpuInventoryPath + std::string("/") + name,
759 "xyz.openbmc_project.Inventory.Item");
760 iface->register_property("PrettyName", name);
761 iface->register_property("Present", *present);
762 iface->initialize();
763 inventoryIfaces[name] = std::move(iface);
764 }
765
766 auto findBus = cfg.find("Bus");
767 if (findBus == cfg.end())
768 {
769 lg2::error("Can't find 'Bus' setting in '{NAME}'", "NAME",
770 name);
771 continue;
772 }
773 uint64_t bus =
774 std::visit(VariantToUnsignedIntVisitor(), findBus->second);
775
776 auto findAddress = cfg.find("Address");
777 if (findAddress == cfg.end())
778 {
779 lg2::error("Can't find 'Address' setting in '{NAME}'",
780 "NAME", name);
781 continue;
782 }
783 uint64_t addr = std::visit(VariantToUnsignedIntVisitor(),
784 findAddress->second);
785
786 if (debug)
787 {
788 lg2::info(
789 "bus: {BUS}, addr: {ADDR}, name: {NAME}, type: {TYPE}",
790 "BUS", bus, "ADDR", addr, "NAME", name, "TYPE", type);
791 }
792
793 cpuConfigs.emplace(bus, addr, name, State::OFF);
794 }
795 }
796 }
797
798 if (static_cast<unsigned int>(!cpuConfigs.empty()) != 0U)
799 {
800 if (cpuConfigs.size() == 1)
801 {
802 lg2::info("CPU config is parsed");
803 }
804 else
805 {
806 lg2::info("CPU configs are parsed");
807 }
808 return true;
809 }
810
811 return false;
812 }
813
main()814 int main()
815 {
816 boost::asio::io_context io;
817 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
818 boost::container::flat_set<CPUConfig> cpuConfigs;
819
820 sdbusplus::asio::object_server objectServer(systemBus, true);
821 objectServer.add_manager("/xyz/openbmc_project/sensors");
822 boost::asio::steady_timer pingTimer(io);
823 boost::asio::steady_timer creationTimer(io);
824 boost::asio::steady_timer filterTimer(io);
825 ManagedObjectType sensorConfigs;
826
827 filterTimer.expires_after(std::chrono::seconds(1));
828 filterTimer.async_wait([&](const boost::system::error_code& ec) {
829 if (ec == boost::asio::error::operation_aborted)
830 {
831 return; // we're being canceled
832 }
833
834 if (getCpuConfig(systemBus, cpuConfigs, sensorConfigs, objectServer))
835 {
836 detectCpuAsync(pingTimer, creationTimer, io, objectServer,
837 systemBus, cpuConfigs, sensorConfigs);
838 }
839 });
840
841 std::function<void(sdbusplus::message_t&)> eventHandler =
842 [&](sdbusplus::message_t& message) {
843 if (message.is_method_error())
844 {
845 lg2::error("callback method error");
846 return;
847 }
848
849 if (debug)
850 {
851 lg2::info("'{PATH}' is changed", "PATH", message.get_path());
852 }
853
854 // this implicitly cancels the timer
855 filterTimer.expires_after(std::chrono::seconds(1));
856 filterTimer.async_wait([&](const boost::system::error_code& ec) {
857 if (ec == boost::asio::error::operation_aborted)
858 {
859 return; // we're being canceled
860 }
861
862 if (getCpuConfig(systemBus, cpuConfigs, sensorConfigs,
863 objectServer))
864 {
865 detectCpuAsync(pingTimer, creationTimer, io, objectServer,
866 systemBus, cpuConfigs, sensorConfigs);
867 }
868 });
869 };
870
871 std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches =
872 setupPropertiesChangedMatches(*systemBus, sensorTypes, eventHandler);
873
874 systemBus->request_name("xyz.openbmc_project.IntelCPUSensor");
875
876 setupManufacturingModeMatch(*systemBus);
877 io.run();
878 return 0;
879 }
880