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 /// \file fru_device.cpp 17 18 #include "fru_utils.hpp" 19 #include "utils.hpp" 20 21 #include <fcntl.h> 22 #include <sys/inotify.h> 23 #include <sys/ioctl.h> 24 25 #include <boost/algorithm/string/predicate.hpp> 26 #include <boost/asio/deadline_timer.hpp> 27 #include <boost/asio/io_service.hpp> 28 #include <boost/container/flat_map.hpp> 29 #include <nlohmann/json.hpp> 30 #include <sdbusplus/asio/connection.hpp> 31 #include <sdbusplus/asio/object_server.hpp> 32 33 #include <array> 34 #include <cerrno> 35 #include <chrono> 36 #include <ctime> 37 #include <filesystem> 38 #include <fstream> 39 #include <functional> 40 #include <future> 41 #include <iomanip> 42 #include <iostream> 43 #include <limits> 44 #include <map> 45 #include <regex> 46 #include <set> 47 #include <sstream> 48 #include <string> 49 #include <thread> 50 #include <utility> 51 #include <variant> 52 #include <vector> 53 54 extern "C" 55 { 56 #include <i2c/smbus.h> 57 #include <linux/i2c-dev.h> 58 } 59 60 namespace fs = std::filesystem; 61 static constexpr bool debug = false; 62 constexpr size_t maxFruSize = 512; 63 constexpr size_t maxEepromPageIndex = 255; 64 constexpr size_t busTimeoutSeconds = 5; 65 66 constexpr const char* blacklistPath = PACKAGE_DIR "blacklist.json"; 67 68 const static constexpr char* baseboardFruLocation = 69 "/etc/fru/baseboard.fru.bin"; 70 71 const static constexpr char* i2CDevLocation = "/dev"; 72 73 static std::set<size_t> busBlacklist; 74 struct FindDevicesWithCallback; 75 76 static boost::container::flat_map< 77 std::pair<size_t, size_t>, std::shared_ptr<sdbusplus::asio::dbus_interface>> 78 foundDevices; 79 80 static boost::container::flat_map<size_t, std::set<size_t>> failedAddresses; 81 82 boost::asio::io_service io; 83 84 uint8_t calculateChecksum(std::vector<uint8_t>::const_iterator iter, 85 std::vector<uint8_t>::const_iterator end); 86 bool updateFRUProperty( 87 const std::string& assetTag, uint32_t bus, uint32_t address, 88 const std::string& propertyName, 89 boost::container::flat_map< 90 std::pair<size_t, size_t>, 91 std::shared_ptr<sdbusplus::asio::dbus_interface>>& dbusInterfaceMap, 92 size_t& unknownBusObjectCount, const bool& powerIsOn, 93 sdbusplus::asio::object_server& objServer, 94 std::shared_ptr<sdbusplus::asio::connection>& systemBus); 95 96 // Given a bus/address, produce the path in sysfs for an eeprom. 97 static std::string getEepromPath(size_t bus, size_t address) 98 { 99 std::stringstream output; 100 output << "/sys/bus/i2c/devices/" << bus << "-" << std::right 101 << std::setfill('0') << std::setw(4) << std::hex << address 102 << "/eeprom"; 103 return output.str(); 104 } 105 106 static bool hasEepromFile(size_t bus, size_t address) 107 { 108 auto path = getEepromPath(bus, address); 109 try 110 { 111 return fs::exists(path); 112 } 113 catch (...) 114 { 115 return false; 116 } 117 } 118 119 static int64_t readFromEeprom(int fd, off_t offset, size_t len, uint8_t* buf) 120 { 121 auto result = lseek(fd, offset, SEEK_SET); 122 if (result < 0) 123 { 124 std::cerr << "failed to seek\n"; 125 return -1; 126 } 127 128 return read(fd, buf, len); 129 } 130 131 static int busStrToInt(const std::string& busName) 132 { 133 auto findBus = busName.rfind('-'); 134 if (findBus == std::string::npos) 135 { 136 return -1; 137 } 138 return std::stoi(busName.substr(findBus + 1)); 139 } 140 141 static int getRootBus(size_t bus) 142 { 143 auto ec = std::error_code(); 144 auto path = std::filesystem::read_symlink( 145 std::filesystem::path("/sys/bus/i2c/devices/i2c-" + 146 std::to_string(bus) + "/mux_device"), 147 ec); 148 if (ec) 149 { 150 return -1; 151 } 152 153 std::string filename = path.filename(); 154 auto findBus = filename.find('-'); 155 if (findBus == std::string::npos) 156 { 157 return -1; 158 } 159 return std::stoi(filename.substr(0, findBus)); 160 } 161 162 static bool isMuxBus(size_t bus) 163 { 164 return is_symlink(std::filesystem::path( 165 "/sys/bus/i2c/devices/i2c-" + std::to_string(bus) + "/mux_device")); 166 } 167 168 static void makeProbeInterface(size_t bus, size_t address, 169 sdbusplus::asio::object_server& objServer) 170 { 171 if (isMuxBus(bus)) 172 { 173 return; // the mux buses are random, no need to publish 174 } 175 auto [it, success] = foundDevices.emplace( 176 std::make_pair(bus, address), 177 objServer.add_interface( 178 "/xyz/openbmc_project/FruDevice/" + std::to_string(bus) + "_" + 179 std::to_string(address), 180 "xyz.openbmc_project.Inventory.Item.I2CDevice")); 181 if (!success) 182 { 183 return; // already added 184 } 185 it->second->register_property("Bus", bus); 186 it->second->register_property("Address", address); 187 it->second->initialize(); 188 } 189 190 static std::optional<bool> isDevice16Bit(int file) 191 { 192 // Set the higher data word address bits to 0. It's safe on 8-bit addressing 193 // EEPROMs because it doesn't write any actual data. 194 int ret = i2c_smbus_write_byte(file, 0); 195 if (ret < 0) 196 { 197 return std::nullopt; 198 } 199 200 /* Get first byte */ 201 int byte1 = i2c_smbus_read_byte_data(file, 0); 202 if (byte1 < 0) 203 { 204 return std::nullopt; 205 } 206 /* Read 7 more bytes, it will read same first byte in case of 207 * 8 bit but it will read next byte in case of 16 bit 208 */ 209 for (int i = 0; i < 7; i++) 210 { 211 int byte2 = i2c_smbus_read_byte_data(file, 0); 212 if (byte2 < 0) 213 { 214 return std::nullopt; 215 } 216 if (byte2 != byte1) 217 { 218 return true; 219 } 220 } 221 return false; 222 } 223 224 // Issue an I2C transaction to first write to_slave_buf_len bytes,then read 225 // from_slave_buf_len bytes. 226 static int i2cSmbusWriteThenRead(int file, uint16_t address, 227 uint8_t* toSlaveBuf, uint8_t toSlaveBufLen, 228 uint8_t* fromSlaveBuf, uint8_t fromSlaveBufLen) 229 { 230 if (toSlaveBuf == nullptr || toSlaveBufLen == 0 || 231 fromSlaveBuf == nullptr || fromSlaveBufLen == 0) 232 { 233 return -1; 234 } 235 236 #define SMBUS_IOCTL_WRITE_THEN_READ_MSG_COUNT 2 237 struct i2c_msg msgs[SMBUS_IOCTL_WRITE_THEN_READ_MSG_COUNT]; 238 struct i2c_rdwr_ioctl_data rdwr; 239 240 msgs[0].addr = address; 241 msgs[0].flags = 0; 242 msgs[0].len = toSlaveBufLen; 243 msgs[0].buf = toSlaveBuf; 244 msgs[1].addr = address; 245 msgs[1].flags = I2C_M_RD; 246 msgs[1].len = fromSlaveBufLen; 247 msgs[1].buf = fromSlaveBuf; 248 249 rdwr.msgs = msgs; 250 rdwr.nmsgs = SMBUS_IOCTL_WRITE_THEN_READ_MSG_COUNT; 251 252 int ret = ioctl(file, I2C_RDWR, &rdwr); 253 254 return (ret == SMBUS_IOCTL_WRITE_THEN_READ_MSG_COUNT) ? msgs[1].len : -1; 255 } 256 257 static int64_t readBlockData(bool is16bit, int file, uint16_t address, 258 off_t offset, size_t len, uint8_t* buf) 259 { 260 if (!is16bit) 261 { 262 return i2c_smbus_read_i2c_block_data(file, static_cast<uint8_t>(offset), 263 len, buf); 264 } 265 266 offset = htobe16(offset); 267 return i2cSmbusWriteThenRead( 268 file, address, reinterpret_cast<uint8_t*>(&offset), 2, buf, len); 269 } 270 271 // TODO: This code is very similar to the non-eeprom version and can be merged 272 // with some tweaks. 273 static std::vector<uint8_t> processEeprom(int bus, int address) 274 { 275 auto path = getEepromPath(bus, address); 276 277 int file = open(path.c_str(), O_RDONLY); 278 if (file < 0) 279 { 280 std::cerr << "Unable to open eeprom file: " << path << "\n"; 281 return {}; 282 } 283 284 std::string errorMessage = "eeprom at " + std::to_string(bus) + 285 " address " + std::to_string(address); 286 auto readFunc = [file](off_t offset, size_t length, uint8_t* outbuf) { 287 return readFromEeprom(file, offset, length, outbuf); 288 }; 289 FRUReader reader(std::move(readFunc)); 290 std::vector<uint8_t> device = readFRUContents(reader, errorMessage); 291 292 close(file); 293 return device; 294 } 295 296 std::set<int> findI2CEeproms(int i2cBus, 297 const std::shared_ptr<DeviceMap>& devices) 298 { 299 std::set<int> foundList; 300 301 std::string path = "/sys/bus/i2c/devices/i2c-" + std::to_string(i2cBus); 302 303 // For each file listed under the i2c device 304 // NOTE: This should be faster than just checking for each possible address 305 // path. 306 for (const auto& p : fs::directory_iterator(path)) 307 { 308 const std::string node = p.path().string(); 309 std::smatch m; 310 bool found = 311 std::regex_match(node, m, std::regex(".+\\d+-([0-9abcdef]+$)")); 312 313 if (!found) 314 { 315 continue; 316 } 317 if (m.size() != 2) 318 { 319 std::cerr << "regex didn't capture\n"; 320 continue; 321 } 322 323 std::ssub_match subMatch = m[1]; 324 std::string addressString = subMatch.str(); 325 326 std::size_t ignored; 327 const int hexBase = 16; 328 int address = std::stoi(addressString, &ignored, hexBase); 329 330 const std::string eeprom = node + "/eeprom"; 331 332 try 333 { 334 if (!fs::exists(eeprom)) 335 { 336 continue; 337 } 338 } 339 catch (...) 340 { 341 continue; 342 } 343 344 // There is an eeprom file at this address, it may have invalid 345 // contents, but we found it. 346 foundList.insert(address); 347 348 std::vector<uint8_t> device = processEeprom(i2cBus, address); 349 if (!device.empty()) 350 { 351 devices->emplace(address, device); 352 } 353 } 354 355 return foundList; 356 } 357 358 int getBusFRUs(int file, int first, int last, int bus, 359 std::shared_ptr<DeviceMap> devices, const bool& powerIsOn, 360 sdbusplus::asio::object_server& objServer) 361 { 362 363 std::future<int> future = std::async(std::launch::async, [&]() { 364 // NOTE: When reading the devices raw on the bus, it can interfere with 365 // the driver's ability to operate, therefore read eeproms first before 366 // scanning for devices without drivers. Several experiments were run 367 // and it was determined that if there were any devices on the bus 368 // before the eeprom was hit and read, the eeprom driver wouldn't open 369 // while the bus device was open. An experiment was not performed to see 370 // if this issue was resolved if the i2c bus device was closed, but 371 // hexdumps of the eeprom later were successful. 372 373 // Scan for i2c eeproms loaded on this bus. 374 std::set<int> skipList = findI2CEeproms(bus, devices); 375 std::set<size_t>& failedItems = failedAddresses[bus]; 376 377 std::set<size_t>* rootFailures = nullptr; 378 int rootBus = getRootBus(bus); 379 380 if (rootBus >= 0) 381 { 382 rootFailures = &(failedAddresses[rootBus]); 383 } 384 385 constexpr int startSkipSlaveAddr = 0; 386 constexpr int endSkipSlaveAddr = 12; 387 388 for (int ii = first; ii <= last; ii++) 389 { 390 if (skipList.find(ii) != skipList.end()) 391 { 392 continue; 393 } 394 // skipping since no device is present in this range 395 if (ii >= startSkipSlaveAddr && ii <= endSkipSlaveAddr) 396 { 397 continue; 398 } 399 // Set slave address 400 if (ioctl(file, I2C_SLAVE, ii) < 0) 401 { 402 std::cerr << "device at bus " << bus << " address " << ii 403 << " busy\n"; 404 continue; 405 } 406 // probe 407 if (i2c_smbus_read_byte(file) < 0) 408 { 409 continue; 410 } 411 412 if (debug) 413 { 414 std::cout << "something at bus " << bus << " addr " << ii 415 << "\n"; 416 } 417 418 makeProbeInterface(bus, ii, objServer); 419 420 if (failedItems.find(ii) != failedItems.end()) 421 { 422 // if we failed to read it once, unlikely we can read it later 423 continue; 424 } 425 426 if (rootFailures != nullptr) 427 { 428 if (rootFailures->find(ii) != rootFailures->end()) 429 { 430 continue; 431 } 432 } 433 434 /* Check for Device type if it is 8 bit or 16 bit */ 435 std::optional<bool> is16Bit = isDevice16Bit(file); 436 if (!is16Bit.has_value()) 437 { 438 std::cerr << "failed to read bus " << bus << " address " << ii 439 << "\n"; 440 if (powerIsOn) 441 { 442 failedItems.insert(ii); 443 } 444 continue; 445 } 446 bool is16BitBool{*is16Bit}; 447 448 auto readFunc = [is16BitBool, file, ii](off_t offset, size_t length, 449 uint8_t* outbuf) { 450 return readBlockData(is16BitBool, file, ii, offset, length, 451 outbuf); 452 }; 453 FRUReader reader(std::move(readFunc)); 454 std::string errorMessage = 455 "bus " + std::to_string(bus) + " address " + std::to_string(ii); 456 std::vector<uint8_t> device = readFRUContents(reader, errorMessage); 457 if (device.empty()) 458 { 459 continue; 460 } 461 462 devices->emplace(ii, device); 463 } 464 return 1; 465 }); 466 std::future_status status = 467 future.wait_for(std::chrono::seconds(busTimeoutSeconds)); 468 if (status == std::future_status::timeout) 469 { 470 std::cerr << "Error reading bus " << bus << "\n"; 471 if (powerIsOn) 472 { 473 busBlacklist.insert(bus); 474 } 475 close(file); 476 return -1; 477 } 478 479 close(file); 480 return future.get(); 481 } 482 483 void loadBlacklist(const char* path) 484 { 485 std::ifstream blacklistStream(path); 486 if (!blacklistStream.good()) 487 { 488 // File is optional. 489 std::cerr << "Cannot open blacklist file.\n\n"; 490 return; 491 } 492 493 nlohmann::json data = 494 nlohmann::json::parse(blacklistStream, nullptr, false); 495 if (data.is_discarded()) 496 { 497 std::cerr << "Illegal blacklist file detected, cannot validate JSON, " 498 "exiting\n"; 499 std::exit(EXIT_FAILURE); 500 } 501 502 // It's expected to have at least one field, "buses" that is an array of the 503 // buses by integer. Allow for future options to exclude further aspects, 504 // such as specific addresses or ranges. 505 if (data.type() != nlohmann::json::value_t::object) 506 { 507 std::cerr << "Illegal blacklist, expected to read dictionary\n"; 508 std::exit(EXIT_FAILURE); 509 } 510 511 // If buses field is missing, that's fine. 512 if (data.count("buses") == 1) 513 { 514 // Parse the buses array after a little validation. 515 auto buses = data.at("buses"); 516 if (buses.type() != nlohmann::json::value_t::array) 517 { 518 // Buses field present but invalid, therefore this is an error. 519 std::cerr << "Invalid contents for blacklist buses field\n"; 520 std::exit(EXIT_FAILURE); 521 } 522 523 // Catch exception here for type mis-match. 524 try 525 { 526 for (const auto& bus : buses) 527 { 528 busBlacklist.insert(bus.get<size_t>()); 529 } 530 } 531 catch (const nlohmann::detail::type_error& e) 532 { 533 // Type mis-match is a critical error. 534 std::cerr << "Invalid bus type: " << e.what() << "\n"; 535 std::exit(EXIT_FAILURE); 536 } 537 } 538 539 return; 540 } 541 542 static void findI2CDevices(const std::vector<fs::path>& i2cBuses, 543 BusMap& busmap, const bool& powerIsOn, 544 sdbusplus::asio::object_server& objServer) 545 { 546 for (auto& i2cBus : i2cBuses) 547 { 548 int bus = busStrToInt(i2cBus); 549 550 if (bus < 0) 551 { 552 std::cerr << "Cannot translate " << i2cBus << " to int\n"; 553 continue; 554 } 555 if (busBlacklist.find(bus) != busBlacklist.end()) 556 { 557 continue; // skip previously failed busses 558 } 559 560 int rootBus = getRootBus(bus); 561 if (busBlacklist.find(rootBus) != busBlacklist.end()) 562 { 563 continue; 564 } 565 566 auto file = open(i2cBus.c_str(), O_RDWR); 567 if (file < 0) 568 { 569 std::cerr << "unable to open i2c device " << i2cBus.string() 570 << "\n"; 571 continue; 572 } 573 unsigned long funcs = 0; 574 575 if (ioctl(file, I2C_FUNCS, &funcs) < 0) 576 { 577 std::cerr 578 << "Error: Could not get the adapter functionality matrix bus " 579 << bus << "\n"; 580 close(file); 581 continue; 582 } 583 if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE) || 584 !(I2C_FUNC_SMBUS_READ_I2C_BLOCK)) 585 { 586 std::cerr << "Error: Can't use SMBus Receive Byte command bus " 587 << bus << "\n"; 588 continue; 589 } 590 auto& device = busmap[bus]; 591 device = std::make_shared<DeviceMap>(); 592 593 // i2cdetect by default uses the range 0x03 to 0x77, as 594 // this is what we have tested with, use this range. Could be 595 // changed in future. 596 if (debug) 597 { 598 std::cerr << "Scanning bus " << bus << "\n"; 599 } 600 601 // fd is closed in this function in case the bus locks up 602 getBusFRUs(file, 0x03, 0x77, bus, device, powerIsOn, objServer); 603 604 if (debug) 605 { 606 std::cerr << "Done scanning bus " << bus << "\n"; 607 } 608 } 609 } 610 611 // this class allows an async response after all i2c devices are discovered 612 struct FindDevicesWithCallback : 613 std::enable_shared_from_this<FindDevicesWithCallback> 614 { 615 FindDevicesWithCallback(const std::vector<fs::path>& i2cBuses, 616 BusMap& busmap, const bool& powerIsOn, 617 sdbusplus::asio::object_server& objServer, 618 std::function<void(void)>&& callback) : 619 _i2cBuses(i2cBuses), 620 _busMap(busmap), _powerIsOn(powerIsOn), _objServer(objServer), 621 _callback(std::move(callback)) 622 {} 623 ~FindDevicesWithCallback() 624 { 625 _callback(); 626 } 627 void run() 628 { 629 findI2CDevices(_i2cBuses, _busMap, _powerIsOn, _objServer); 630 } 631 632 const std::vector<fs::path>& _i2cBuses; 633 BusMap& _busMap; 634 const bool& _powerIsOn; 635 sdbusplus::asio::object_server& _objServer; 636 std::function<void(void)> _callback; 637 }; 638 639 void addFruObjectToDbus( 640 std::vector<uint8_t>& device, 641 boost::container::flat_map< 642 std::pair<size_t, size_t>, 643 std::shared_ptr<sdbusplus::asio::dbus_interface>>& dbusInterfaceMap, 644 uint32_t bus, uint32_t address, size_t& unknownBusObjectCount, 645 const bool& powerIsOn, sdbusplus::asio::object_server& objServer, 646 std::shared_ptr<sdbusplus::asio::connection>& systemBus) 647 { 648 boost::container::flat_map<std::string, std::string> formattedFRU; 649 resCodes res = formatIPMIFRU(device, formattedFRU); 650 if (res == resCodes::resErr) 651 { 652 std::cerr << "failed to parse FRU for device at bus " << bus 653 << " address " << address << "\n"; 654 return; 655 } 656 if (res == resCodes::resWarn) 657 { 658 std::cerr << "there were warnings while parsing FRU for device at bus " 659 << bus << " address " << address << "\n"; 660 } 661 662 auto productNameFind = formattedFRU.find("BOARD_PRODUCT_NAME"); 663 std::string productName; 664 // Not found under Board section or an empty string. 665 if (productNameFind == formattedFRU.end() || 666 productNameFind->second.empty()) 667 { 668 productNameFind = formattedFRU.find("PRODUCT_PRODUCT_NAME"); 669 } 670 // Found under Product section and not an empty string. 671 if (productNameFind != formattedFRU.end() && 672 !productNameFind->second.empty()) 673 { 674 productName = productNameFind->second; 675 std::regex illegalObject("[^A-Za-z0-9_]"); 676 productName = std::regex_replace(productName, illegalObject, "_"); 677 } 678 else 679 { 680 productName = "UNKNOWN" + std::to_string(unknownBusObjectCount); 681 unknownBusObjectCount++; 682 } 683 684 productName = "/xyz/openbmc_project/FruDevice/" + productName; 685 // avoid duplicates by checking to see if on a mux 686 if (bus > 0) 687 { 688 int highest = -1; 689 bool found = false; 690 691 for (auto const& busIface : dbusInterfaceMap) 692 { 693 std::string path = busIface.second->get_object_path(); 694 if (std::regex_match(path, std::regex(productName + "(_\\d+|)$"))) 695 { 696 if (isMuxBus(bus) && bus != busIface.first.first && 697 address == busIface.first.second && 698 (getFRUInfo(static_cast<uint8_t>(busIface.first.first), 699 static_cast<uint8_t>(busIface.first.second)) == 700 getFRUInfo(static_cast<uint8_t>(bus), 701 static_cast<uint8_t>(address)))) 702 { 703 // This device is already added to the lower numbered bus, 704 // do not replicate it. 705 return; 706 } 707 708 // Check if the match named has extra information. 709 found = true; 710 std::smatch baseMatch; 711 712 bool match = std::regex_match( 713 path, baseMatch, std::regex(productName + "_(\\d+)$")); 714 if (match) 715 { 716 if (baseMatch.size() == 2) 717 { 718 std::ssub_match baseSubMatch = baseMatch[1]; 719 std::string base = baseSubMatch.str(); 720 721 int value = std::stoi(base); 722 highest = (value > highest) ? value : highest; 723 } 724 } 725 } 726 } // end searching objects 727 728 if (found) 729 { 730 // We found something with the same name. If highest was still -1, 731 // it means this new entry will be _0. 732 productName += "_"; 733 productName += std::to_string(++highest); 734 } 735 } 736 737 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = 738 objServer.add_interface(productName, "xyz.openbmc_project.FruDevice"); 739 dbusInterfaceMap[std::pair<size_t, size_t>(bus, address)] = iface; 740 741 for (auto& property : formattedFRU) 742 { 743 744 std::regex_replace(property.second.begin(), property.second.begin(), 745 property.second.end(), nonAsciiRegex, "_"); 746 if (property.second.empty() && property.first != "PRODUCT_ASSET_TAG") 747 { 748 continue; 749 } 750 std::string key = 751 std::regex_replace(property.first, nonAsciiRegex, "_"); 752 753 if (property.first == "PRODUCT_ASSET_TAG") 754 { 755 std::string propertyName = property.first; 756 iface->register_property( 757 key, property.second + '\0', 758 [bus, address, propertyName, &dbusInterfaceMap, 759 &unknownBusObjectCount, &powerIsOn, &objServer, 760 &systemBus](const std::string& req, std::string& resp) { 761 if (strcmp(req.c_str(), resp.c_str()) != 0) 762 { 763 // call the method which will update 764 if (updateFRUProperty(req, bus, address, propertyName, 765 dbusInterfaceMap, 766 unknownBusObjectCount, powerIsOn, 767 objServer, systemBus)) 768 { 769 resp = req; 770 } 771 else 772 { 773 throw std::invalid_argument( 774 "FRU property update failed."); 775 } 776 } 777 return 1; 778 }); 779 } 780 else if (!iface->register_property(key, property.second + '\0')) 781 { 782 std::cerr << "illegal key: " << key << "\n"; 783 } 784 if (debug) 785 { 786 std::cout << property.first << ": " << property.second << "\n"; 787 } 788 } 789 790 // baseboard will be 0, 0 791 iface->register_property("BUS", bus); 792 iface->register_property("ADDRESS", address); 793 794 iface->initialize(); 795 } 796 797 static bool readBaseboardFRU(std::vector<uint8_t>& baseboardFRU) 798 { 799 // try to read baseboard fru from file 800 std::ifstream baseboardFRUFile(baseboardFruLocation, std::ios::binary); 801 if (baseboardFRUFile.good()) 802 { 803 baseboardFRUFile.seekg(0, std::ios_base::end); 804 size_t fileSize = static_cast<size_t>(baseboardFRUFile.tellg()); 805 baseboardFRU.resize(fileSize); 806 baseboardFRUFile.seekg(0, std::ios_base::beg); 807 baseboardFRUFile.read(reinterpret_cast<char*>(baseboardFRU.data()), 808 fileSize); 809 } 810 else 811 { 812 return false; 813 } 814 return true; 815 } 816 817 bool writeFRU(uint8_t bus, uint8_t address, const std::vector<uint8_t>& fru) 818 { 819 boost::container::flat_map<std::string, std::string> tmp; 820 if (fru.size() > maxFruSize) 821 { 822 std::cerr << "Invalid fru.size() during writeFRU\n"; 823 return false; 824 } 825 // verify legal fru by running it through fru parsing logic 826 if (formatIPMIFRU(fru, tmp) != resCodes::resOK) 827 { 828 std::cerr << "Invalid fru format during writeFRU\n"; 829 return false; 830 } 831 // baseboard fru 832 if (bus == 0 && address == 0) 833 { 834 std::ofstream file(baseboardFruLocation, std::ios_base::binary); 835 if (!file.good()) 836 { 837 std::cerr << "Error opening file " << baseboardFruLocation << "\n"; 838 throw DBusInternalError(); 839 return false; 840 } 841 file.write(reinterpret_cast<const char*>(fru.data()), fru.size()); 842 return file.good(); 843 } 844 845 if (hasEepromFile(bus, address)) 846 { 847 auto path = getEepromPath(bus, address); 848 int eeprom = open(path.c_str(), O_RDWR | O_CLOEXEC); 849 if (eeprom < 0) 850 { 851 std::cerr << "unable to open i2c device " << path << "\n"; 852 throw DBusInternalError(); 853 return false; 854 } 855 856 ssize_t writtenBytes = write(eeprom, fru.data(), fru.size()); 857 if (writtenBytes < 0) 858 { 859 std::cerr << "unable to write to i2c device " << path << "\n"; 860 close(eeprom); 861 throw DBusInternalError(); 862 return false; 863 } 864 865 close(eeprom); 866 return true; 867 } 868 869 std::string i2cBus = "/dev/i2c-" + std::to_string(bus); 870 871 int file = open(i2cBus.c_str(), O_RDWR | O_CLOEXEC); 872 if (file < 0) 873 { 874 std::cerr << "unable to open i2c device " << i2cBus << "\n"; 875 throw DBusInternalError(); 876 return false; 877 } 878 if (ioctl(file, I2C_SLAVE_FORCE, address) < 0) 879 { 880 std::cerr << "unable to set device address\n"; 881 close(file); 882 throw DBusInternalError(); 883 return false; 884 } 885 886 constexpr const size_t retryMax = 2; 887 uint16_t index = 0; 888 size_t retries = retryMax; 889 while (index < fru.size()) 890 { 891 if ((index && ((index % (maxEepromPageIndex + 1)) == 0)) && 892 (retries == retryMax)) 893 { 894 // The 4K EEPROM only uses the A2 and A1 device address bits 895 // with the third bit being a memory page address bit. 896 if (ioctl(file, I2C_SLAVE_FORCE, ++address) < 0) 897 { 898 std::cerr << "unable to set device address\n"; 899 close(file); 900 throw DBusInternalError(); 901 return false; 902 } 903 } 904 905 if (i2c_smbus_write_byte_data(file, static_cast<uint8_t>(index), 906 fru[index]) < 0) 907 { 908 if (!retries--) 909 { 910 std::cerr << "error writing fru: " << strerror(errno) << "\n"; 911 close(file); 912 throw DBusInternalError(); 913 return false; 914 } 915 } 916 else 917 { 918 retries = retryMax; 919 index++; 920 } 921 // most eeproms require 5-10ms between writes 922 std::this_thread::sleep_for(std::chrono::milliseconds(10)); 923 } 924 close(file); 925 return true; 926 } 927 928 void rescanOneBus( 929 BusMap& busmap, uint8_t busNum, 930 boost::container::flat_map< 931 std::pair<size_t, size_t>, 932 std::shared_ptr<sdbusplus::asio::dbus_interface>>& dbusInterfaceMap, 933 bool dbusCall, size_t& unknownBusObjectCount, const bool& powerIsOn, 934 sdbusplus::asio::object_server& objServer, 935 std::shared_ptr<sdbusplus::asio::connection>& systemBus) 936 { 937 for (auto& [pair, interface] : foundDevices) 938 { 939 if (pair.first == static_cast<size_t>(busNum)) 940 { 941 objServer.remove_interface(interface); 942 foundDevices.erase(pair); 943 } 944 } 945 946 fs::path busPath = fs::path("/dev/i2c-" + std::to_string(busNum)); 947 if (!fs::exists(busPath)) 948 { 949 if (dbusCall) 950 { 951 std::cerr << "Unable to access i2c bus " << static_cast<int>(busNum) 952 << "\n"; 953 throw std::invalid_argument("Invalid Bus."); 954 } 955 return; 956 } 957 958 std::vector<fs::path> i2cBuses; 959 i2cBuses.emplace_back(busPath); 960 961 auto scan = std::make_shared<FindDevicesWithCallback>( 962 i2cBuses, busmap, powerIsOn, objServer, 963 [busNum, &busmap, &dbusInterfaceMap, &unknownBusObjectCount, &powerIsOn, 964 &objServer, &systemBus]() { 965 for (auto& busIface : dbusInterfaceMap) 966 { 967 if (busIface.first.first == static_cast<size_t>(busNum)) 968 { 969 objServer.remove_interface(busIface.second); 970 } 971 } 972 auto found = busmap.find(busNum); 973 if (found == busmap.end() || found->second == nullptr) 974 { 975 return; 976 } 977 for (auto& device : *(found->second)) 978 { 979 addFruObjectToDbus(device.second, dbusInterfaceMap, 980 static_cast<uint32_t>(busNum), device.first, 981 unknownBusObjectCount, powerIsOn, objServer, 982 systemBus); 983 } 984 }); 985 scan->run(); 986 } 987 988 void rescanBusses( 989 BusMap& busmap, 990 boost::container::flat_map< 991 std::pair<size_t, size_t>, 992 std::shared_ptr<sdbusplus::asio::dbus_interface>>& dbusInterfaceMap, 993 size_t& unknownBusObjectCount, const bool& powerIsOn, 994 sdbusplus::asio::object_server& objServer, 995 std::shared_ptr<sdbusplus::asio::connection>& systemBus) 996 { 997 static boost::asio::deadline_timer timer(io); 998 timer.expires_from_now(boost::posix_time::seconds(1)); 999 1000 // setup an async wait in case we get flooded with requests 1001 timer.async_wait([&](const boost::system::error_code&) { 1002 auto devDir = fs::path("/dev/"); 1003 std::vector<fs::path> i2cBuses; 1004 1005 boost::container::flat_map<size_t, fs::path> busPaths; 1006 if (!getI2cDevicePaths(devDir, busPaths)) 1007 { 1008 std::cerr << "unable to find i2c devices\n"; 1009 return; 1010 } 1011 1012 for (const auto& busPath : busPaths) 1013 { 1014 i2cBuses.emplace_back(busPath.second); 1015 } 1016 1017 busmap.clear(); 1018 for (auto& [pair, interface] : foundDevices) 1019 { 1020 objServer.remove_interface(interface); 1021 } 1022 foundDevices.clear(); 1023 1024 auto scan = std::make_shared<FindDevicesWithCallback>( 1025 i2cBuses, busmap, powerIsOn, objServer, [&]() { 1026 for (auto& busIface : dbusInterfaceMap) 1027 { 1028 objServer.remove_interface(busIface.second); 1029 } 1030 1031 dbusInterfaceMap.clear(); 1032 unknownBusObjectCount = 0; 1033 1034 // todo, get this from a more sensable place 1035 std::vector<uint8_t> baseboardFRU; 1036 if (readBaseboardFRU(baseboardFRU)) 1037 { 1038 // If no device on i2c bus 0, the insertion will happen. 1039 auto bus0 = 1040 busmap.try_emplace(0, std::make_shared<DeviceMap>()); 1041 bus0.first->second->emplace(0, baseboardFRU); 1042 } 1043 for (auto& devicemap : busmap) 1044 { 1045 for (auto& device : *devicemap.second) 1046 { 1047 addFruObjectToDbus(device.second, dbusInterfaceMap, 1048 devicemap.first, device.first, 1049 unknownBusObjectCount, powerIsOn, 1050 objServer, systemBus); 1051 } 1052 } 1053 }); 1054 scan->run(); 1055 }); 1056 } 1057 1058 // Details with example of Asset Tag Update 1059 // To find location of Product Info Area asset tag as per FRU specification 1060 // 1. Find product Info area starting offset (*8 - as header will be in 1061 // multiple of 8 bytes). 1062 // 2. Skip 3 bytes of product info area (like format version, area length, 1063 // and language code). 1064 // 3. Traverse manufacturer name, product name, product version, & product 1065 // serial number, by reading type/length code to reach the Asset Tag. 1066 // 4. Update the Asset Tag, reposition the product Info area in multiple of 1067 // 8 bytes. Update the Product area length and checksum. 1068 1069 bool updateFRUProperty( 1070 const std::string& updatePropertyReq, uint32_t bus, uint32_t address, 1071 const std::string& propertyName, 1072 boost::container::flat_map< 1073 std::pair<size_t, size_t>, 1074 std::shared_ptr<sdbusplus::asio::dbus_interface>>& dbusInterfaceMap, 1075 size_t& unknownBusObjectCount, const bool& powerIsOn, 1076 sdbusplus::asio::object_server& objServer, 1077 std::shared_ptr<sdbusplus::asio::connection>& systemBus) 1078 { 1079 size_t updatePropertyReqLen = updatePropertyReq.length(); 1080 if (updatePropertyReqLen == 1 || updatePropertyReqLen > 63) 1081 { 1082 std::cerr 1083 << "FRU field data cannot be of 1 char or more than 63 chars. " 1084 "Invalid Length " 1085 << updatePropertyReqLen << "\n"; 1086 return false; 1087 } 1088 1089 std::vector<uint8_t> fruData; 1090 try 1091 { 1092 fruData = getFRUInfo(static_cast<uint8_t>(bus), 1093 static_cast<uint8_t>(address)); 1094 } 1095 catch (const std::invalid_argument& e) 1096 { 1097 std::cerr << "Failure getting FRU Info" << e.what() << "\n"; 1098 return false; 1099 } 1100 1101 if (fruData.empty()) 1102 { 1103 return false; 1104 } 1105 1106 const std::vector<std::string>* fruAreaFieldNames; 1107 1108 uint8_t fruAreaOffsetFieldValue = 0; 1109 size_t offset = 0; 1110 std::string areaName = propertyName.substr(0, propertyName.find('_')); 1111 std::string propertyNamePrefix = areaName + "_"; 1112 auto it = std::find(fruAreaNames.begin(), fruAreaNames.end(), areaName); 1113 if (it == fruAreaNames.end()) 1114 { 1115 std::cerr << "Can't parse area name for property " << propertyName 1116 << " \n"; 1117 return false; 1118 } 1119 fruAreas fruAreaToUpdate = static_cast<fruAreas>(it - fruAreaNames.begin()); 1120 fruAreaOffsetFieldValue = 1121 fruData[getHeaderAreaFieldOffset(fruAreaToUpdate)]; 1122 switch (fruAreaToUpdate) 1123 { 1124 case fruAreas::fruAreaChassis: 1125 offset = 3; // chassis part number offset. Skip fixed first 3 bytes 1126 fruAreaFieldNames = &chassisFruAreas; 1127 break; 1128 case fruAreas::fruAreaBoard: 1129 offset = 6; // board manufacturer offset. Skip fixed first 6 bytes 1130 fruAreaFieldNames = &boardFruAreas; 1131 break; 1132 case fruAreas::fruAreaProduct: 1133 // Manufacturer name offset. Skip fixed first 3 product fru bytes 1134 // i.e. version, area length and language code 1135 offset = 3; 1136 fruAreaFieldNames = &productFruAreas; 1137 break; 1138 default: 1139 std::cerr << "Don't know how to handle property " << propertyName 1140 << " \n"; 1141 return false; 1142 } 1143 if (fruAreaOffsetFieldValue == 0) 1144 { 1145 std::cerr << "FRU Area for " << propertyName << " not present \n"; 1146 return false; 1147 } 1148 1149 size_t fruAreaStart = fruAreaOffsetFieldValue * fruBlockSize; 1150 size_t fruAreaSize = fruData[fruAreaStart + 1] * fruBlockSize; 1151 size_t fruAreaEnd = fruAreaStart + fruAreaSize; 1152 size_t fruDataIter = fruAreaStart + offset; 1153 size_t skipToFRUUpdateField = 0; 1154 ssize_t fieldLength; 1155 1156 bool found = false; 1157 for (auto& field : *fruAreaFieldNames) 1158 { 1159 skipToFRUUpdateField++; 1160 if (propertyName == propertyNamePrefix + field) 1161 { 1162 found = true; 1163 break; 1164 } 1165 } 1166 if (!found) 1167 { 1168 std::size_t pos = propertyName.find(fruCustomFieldName); 1169 if (pos == std::string::npos) 1170 { 1171 std::cerr << "PropertyName doesn't exist in FRU Area Vectors: " 1172 << propertyName << "\n"; 1173 return false; 1174 } 1175 std::string fieldNumStr = 1176 propertyName.substr(pos + fruCustomFieldName.length()); 1177 size_t fieldNum = std::stoi(fieldNumStr); 1178 if (fieldNum == 0) 1179 { 1180 std::cerr << "PropertyName not recognized: " << propertyName 1181 << "\n"; 1182 return false; 1183 } 1184 skipToFRUUpdateField += fieldNum; 1185 } 1186 1187 for (size_t i = 1; i < skipToFRUUpdateField; i++) 1188 { 1189 fieldLength = getFieldLength(fruData[fruDataIter]); 1190 if (fieldLength < 0) 1191 { 1192 break; 1193 } 1194 fruDataIter += 1 + fieldLength; 1195 } 1196 size_t fruUpdateFieldLoc = fruDataIter; 1197 1198 // Push post update fru field bytes to a vector 1199 fieldLength = getFieldLength(fruData[fruUpdateFieldLoc]); 1200 if (fieldLength < 0) 1201 { 1202 std::cerr << "Property " << propertyName << " not present \n"; 1203 return false; 1204 } 1205 fruDataIter += 1 + fieldLength; 1206 size_t restFRUFieldsLoc = fruDataIter; 1207 size_t endOfFieldsLoc = 0; 1208 while ((fieldLength = getFieldLength(fruData[fruDataIter])) >= 0) 1209 { 1210 if (fruDataIter >= (fruAreaStart + fruAreaSize)) 1211 { 1212 fruDataIter = fruAreaStart + fruAreaSize; 1213 break; 1214 } 1215 fruDataIter += 1 + fieldLength; 1216 } 1217 endOfFieldsLoc = fruDataIter; 1218 1219 std::vector<uint8_t> restFRUAreaFieldsData; 1220 std::copy_n(fruData.begin() + restFRUFieldsLoc, 1221 endOfFieldsLoc - restFRUFieldsLoc + 1, 1222 std::back_inserter(restFRUAreaFieldsData)); 1223 1224 // Push post update fru areas if any 1225 unsigned int nextFRUAreaLoc = 0; 1226 for (fruAreas nextFRUArea = fruAreas::fruAreaInternal; 1227 nextFRUArea <= fruAreas::fruAreaMultirecord; ++nextFRUArea) 1228 { 1229 unsigned int fruAreaLoc = 1230 fruData[getHeaderAreaFieldOffset(nextFRUArea)] * fruBlockSize; 1231 if ((fruAreaLoc > endOfFieldsLoc) && 1232 ((nextFRUAreaLoc == 0) || (fruAreaLoc < nextFRUAreaLoc))) 1233 { 1234 nextFRUAreaLoc = fruAreaLoc; 1235 } 1236 } 1237 std::vector<uint8_t> restFRUAreasData; 1238 if (nextFRUAreaLoc) 1239 { 1240 std::copy_n(fruData.begin() + nextFRUAreaLoc, 1241 fruData.size() - nextFRUAreaLoc, 1242 std::back_inserter(restFRUAreasData)); 1243 } 1244 1245 // check FRU area size 1246 size_t fruAreaDataSize = 1247 ((fruUpdateFieldLoc - fruAreaStart + 1) + restFRUAreaFieldsData.size()); 1248 size_t fruAreaAvailableSize = fruAreaSize - fruAreaDataSize; 1249 if ((updatePropertyReqLen + 1) > fruAreaAvailableSize) 1250 { 1251 1252 #ifdef ENABLE_FRU_AREA_RESIZE 1253 size_t newFRUAreaSize = fruAreaDataSize + updatePropertyReqLen + 1; 1254 // round size to 8-byte blocks 1255 newFRUAreaSize = 1256 ((newFRUAreaSize - 1) / fruBlockSize + 1) * fruBlockSize; 1257 size_t newFRUDataSize = fruData.size() + newFRUAreaSize - fruAreaSize; 1258 fruData.resize(newFRUDataSize); 1259 fruAreaSize = newFRUAreaSize; 1260 fruAreaEnd = fruAreaStart + fruAreaSize; 1261 #else 1262 std::cerr << "FRU field length: " << updatePropertyReqLen + 1 1263 << " should not be greater than available FRU area size: " 1264 << fruAreaAvailableSize << "\n"; 1265 return false; 1266 #endif // ENABLE_FRU_AREA_RESIZE 1267 } 1268 1269 // write new requested property field length and data 1270 constexpr uint8_t newTypeLenMask = 0xC0; 1271 fruData[fruUpdateFieldLoc] = 1272 static_cast<uint8_t>(updatePropertyReqLen | newTypeLenMask); 1273 fruUpdateFieldLoc++; 1274 std::copy(updatePropertyReq.begin(), updatePropertyReq.end(), 1275 fruData.begin() + fruUpdateFieldLoc); 1276 1277 // Copy remaining data to main fru area - post updated fru field vector 1278 restFRUFieldsLoc = fruUpdateFieldLoc + updatePropertyReqLen; 1279 size_t fruAreaDataEnd = restFRUFieldsLoc + restFRUAreaFieldsData.size(); 1280 std::copy(restFRUAreaFieldsData.begin(), restFRUAreaFieldsData.end(), 1281 fruData.begin() + restFRUFieldsLoc); 1282 1283 // Update final fru with new fru area length and checksum 1284 unsigned int nextFRUAreaNewLoc = updateFRUAreaLenAndChecksum( 1285 fruData, fruAreaStart, fruAreaDataEnd, fruAreaEnd); 1286 1287 #ifdef ENABLE_FRU_AREA_RESIZE 1288 ++nextFRUAreaNewLoc; 1289 ssize_t nextFRUAreaOffsetDiff = 1290 (nextFRUAreaNewLoc - nextFRUAreaLoc) / fruBlockSize; 1291 // Append rest FRU Areas if size changed and there were other sections after 1292 // updated one 1293 if (nextFRUAreaOffsetDiff && nextFRUAreaLoc) 1294 { 1295 std::copy(restFRUAreasData.begin(), restFRUAreasData.end(), 1296 fruData.begin() + nextFRUAreaNewLoc); 1297 // Update Common Header 1298 for (int fruArea = fruAreaInternal; fruArea <= fruAreaMultirecord; 1299 fruArea++) 1300 { 1301 unsigned int fruAreaOffsetField = getHeaderAreaFieldOffset(fruArea); 1302 size_t curFRUAreaOffset = fruData[fruAreaOffsetField]; 1303 if (curFRUAreaOffset > fruAreaOffsetFieldValue) 1304 { 1305 fruData[fruAreaOffsetField] = static_cast<int8_t>( 1306 curFRUAreaOffset + nextFRUAreaOffsetDiff); 1307 } 1308 } 1309 // Calculate new checksum 1310 std::vector<uint8_t> headerFRUData; 1311 std::copy_n(fruData.begin(), 7, std::back_inserter(headerFRUData)); 1312 size_t checksumVal = calculateChecksum(headerFRUData); 1313 fruData[7] = static_cast<uint8_t>(checksumVal); 1314 // fill zeros if FRU Area size decreased 1315 if (nextFRUAreaOffsetDiff < 0) 1316 { 1317 std::fill(fruData.begin() + nextFRUAreaNewLoc + 1318 restFRUAreasData.size(), 1319 fruData.end(), 0); 1320 } 1321 } 1322 #else 1323 // this is to avoid "unused variable" warning 1324 (void)nextFRUAreaNewLoc; 1325 #endif // ENABLE_FRU_AREA_RESIZE 1326 if (fruData.empty()) 1327 { 1328 return false; 1329 } 1330 1331 if (!writeFRU(static_cast<uint8_t>(bus), static_cast<uint8_t>(address), 1332 fruData)) 1333 { 1334 return false; 1335 } 1336 1337 // Rescan the bus so that GetRawFru dbus-call fetches updated values 1338 rescanBusses(busMap, dbusInterfaceMap, unknownBusObjectCount, powerIsOn, 1339 objServer, systemBus); 1340 return true; 1341 } 1342 1343 int main() 1344 { 1345 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io); 1346 sdbusplus::asio::object_server objServer(systemBus); 1347 1348 static size_t unknownBusObjectCount = 0; 1349 static bool powerIsOn = false; 1350 auto devDir = fs::path("/dev/"); 1351 auto matchString = std::string(R"(i2c-\d+$)"); 1352 std::vector<fs::path> i2cBuses; 1353 1354 if (!findFiles(devDir, matchString, i2cBuses)) 1355 { 1356 std::cerr << "unable to find i2c devices\n"; 1357 return 1; 1358 } 1359 1360 // check for and load blacklist with initial buses. 1361 loadBlacklist(blacklistPath); 1362 1363 systemBus->request_name("xyz.openbmc_project.FruDevice"); 1364 1365 // this is a map with keys of pair(bus number, address) and values of 1366 // the object on dbus 1367 boost::container::flat_map<std::pair<size_t, size_t>, 1368 std::shared_ptr<sdbusplus::asio::dbus_interface>> 1369 dbusInterfaceMap; 1370 1371 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = 1372 objServer.add_interface("/xyz/openbmc_project/FruDevice", 1373 "xyz.openbmc_project.FruDeviceManager"); 1374 1375 iface->register_method("ReScan", [&]() { 1376 rescanBusses(busMap, dbusInterfaceMap, unknownBusObjectCount, powerIsOn, 1377 objServer, systemBus); 1378 }); 1379 1380 iface->register_method("ReScanBus", [&](uint8_t bus) { 1381 rescanOneBus(busMap, bus, dbusInterfaceMap, true, unknownBusObjectCount, 1382 powerIsOn, objServer, systemBus); 1383 }); 1384 1385 iface->register_method("GetRawFru", getFRUInfo); 1386 1387 iface->register_method("WriteFru", [&](const uint8_t bus, 1388 const uint8_t address, 1389 const std::vector<uint8_t>& data) { 1390 if (!writeFRU(bus, address, data)) 1391 { 1392 throw std::invalid_argument("Invalid Arguments."); 1393 return; 1394 } 1395 // schedule rescan on success 1396 rescanBusses(busMap, dbusInterfaceMap, unknownBusObjectCount, powerIsOn, 1397 objServer, systemBus); 1398 }); 1399 iface->initialize(); 1400 1401 std::function<void(sdbusplus::message::message & message)> eventHandler = 1402 [&](sdbusplus::message::message& message) { 1403 std::string objectName; 1404 boost::container::flat_map< 1405 std::string, 1406 std::variant<std::string, bool, int64_t, uint64_t, double>> 1407 values; 1408 message.read(objectName, values); 1409 auto findState = values.find("CurrentHostState"); 1410 if (findState != values.end()) 1411 { 1412 powerIsOn = boost::ends_with( 1413 std::get<std::string>(findState->second), "Running"); 1414 } 1415 1416 if (powerIsOn) 1417 { 1418 rescanBusses(busMap, dbusInterfaceMap, unknownBusObjectCount, 1419 powerIsOn, objServer, systemBus); 1420 } 1421 }; 1422 1423 sdbusplus::bus::match::match powerMatch = sdbusplus::bus::match::match( 1424 static_cast<sdbusplus::bus::bus&>(*systemBus), 1425 "type='signal',interface='org.freedesktop.DBus.Properties',path='/xyz/" 1426 "openbmc_project/state/" 1427 "host0',arg0='xyz.openbmc_project.State.Host'", 1428 eventHandler); 1429 1430 int fd = inotify_init(); 1431 inotify_add_watch(fd, i2CDevLocation, IN_CREATE | IN_MOVED_TO | IN_DELETE); 1432 std::array<char, 4096> readBuffer; 1433 // monitor for new i2c devices 1434 boost::asio::posix::stream_descriptor dirWatch(io, fd); 1435 std::function<void(const boost::system::error_code, std::size_t)> 1436 watchI2cBusses = [&](const boost::system::error_code& ec, 1437 std::size_t bytesTransferred) { 1438 if (ec) 1439 { 1440 std::cout << "Callback Error " << ec << "\n"; 1441 return; 1442 } 1443 size_t index = 0; 1444 while ((index + sizeof(inotify_event)) <= bytesTransferred) 1445 { 1446 const inotify_event* iEvent = 1447 reinterpret_cast<const inotify_event*>(&readBuffer[index]); 1448 switch (iEvent->mask) 1449 { 1450 case IN_CREATE: 1451 case IN_MOVED_TO: 1452 case IN_DELETE: 1453 std::string name(iEvent->name); 1454 if (boost::starts_with(name, "i2c")) 1455 { 1456 int bus = busStrToInt(name); 1457 if (bus < 0) 1458 { 1459 std::cerr << "Could not parse bus " << name 1460 << "\n"; 1461 continue; 1462 } 1463 rescanOneBus(busMap, static_cast<uint8_t>(bus), 1464 dbusInterfaceMap, false, 1465 unknownBusObjectCount, powerIsOn, 1466 objServer, systemBus); 1467 } 1468 } 1469 index += sizeof(inotify_event) + iEvent->len; 1470 } 1471 1472 dirWatch.async_read_some(boost::asio::buffer(readBuffer), 1473 watchI2cBusses); 1474 }; 1475 1476 dirWatch.async_read_some(boost::asio::buffer(readBuffer), watchI2cBusses); 1477 // run the initial scan 1478 rescanBusses(busMap, dbusInterfaceMap, unknownBusObjectCount, powerIsOn, 1479 objServer, systemBus); 1480 1481 io.run(); 1482 return 0; 1483 } 1484