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