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