xref: /openbmc/entity-manager/src/fru_device/fru_device.cpp (revision 6459993cc24e519603bbadf4bed407b8aab869ec)
13cbff97fSChristopher Meis /*
23cbff97fSChristopher Meis // Copyright (c) 2018 Intel Corporation
33cbff97fSChristopher Meis //
43cbff97fSChristopher Meis // Licensed under the Apache License, Version 2.0 (the "License");
53cbff97fSChristopher Meis // you may not use this file except in compliance with the License.
63cbff97fSChristopher Meis // You may obtain a copy of the License at
73cbff97fSChristopher Meis //
83cbff97fSChristopher Meis //      http://www.apache.org/licenses/LICENSE-2.0
93cbff97fSChristopher Meis //
103cbff97fSChristopher Meis // Unless required by applicable law or agreed to in writing, software
113cbff97fSChristopher Meis // distributed under the License is distributed on an "AS IS" BASIS,
123cbff97fSChristopher Meis // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133cbff97fSChristopher Meis // See the License for the specific language governing permissions and
143cbff97fSChristopher Meis // limitations under the License.
153cbff97fSChristopher Meis */
163cbff97fSChristopher Meis /// \file fru_device.cpp
173cbff97fSChristopher Meis 
183cbff97fSChristopher Meis #include "../utils.hpp"
193cbff97fSChristopher Meis #include "fru_utils.hpp"
203cbff97fSChristopher Meis 
213cbff97fSChristopher Meis #include <fcntl.h>
223cbff97fSChristopher Meis #include <sys/inotify.h>
233cbff97fSChristopher Meis #include <sys/ioctl.h>
243cbff97fSChristopher Meis 
253cbff97fSChristopher Meis #include <boost/algorithm/string/predicate.hpp>
263cbff97fSChristopher Meis #include <boost/asio/io_context.hpp>
273cbff97fSChristopher Meis #include <boost/asio/steady_timer.hpp>
283cbff97fSChristopher Meis #include <boost/container/flat_map.hpp>
293cbff97fSChristopher Meis #include <nlohmann/json.hpp>
303cbff97fSChristopher Meis #include <phosphor-logging/lg2.hpp>
313cbff97fSChristopher Meis #include <sdbusplus/asio/connection.hpp>
323cbff97fSChristopher Meis #include <sdbusplus/asio/object_server.hpp>
333cbff97fSChristopher Meis 
343cbff97fSChristopher Meis #include <array>
353cbff97fSChristopher Meis #include <cerrno>
363cbff97fSChristopher Meis #include <charconv>
373cbff97fSChristopher Meis #include <chrono>
383cbff97fSChristopher Meis #include <ctime>
393cbff97fSChristopher Meis #include <filesystem>
403cbff97fSChristopher Meis #include <fstream>
413cbff97fSChristopher Meis #include <functional>
423cbff97fSChristopher Meis #include <future>
433cbff97fSChristopher Meis #include <iomanip>
443cbff97fSChristopher Meis #include <iostream>
453cbff97fSChristopher Meis #include <limits>
463cbff97fSChristopher Meis #include <map>
473cbff97fSChristopher Meis #include <optional>
483cbff97fSChristopher Meis #include <regex>
493cbff97fSChristopher Meis #include <set>
503cbff97fSChristopher Meis #include <sstream>
513cbff97fSChristopher Meis #include <string>
523cbff97fSChristopher Meis #include <thread>
533cbff97fSChristopher Meis #include <utility>
543cbff97fSChristopher Meis #include <variant>
553cbff97fSChristopher Meis #include <vector>
563cbff97fSChristopher Meis 
573cbff97fSChristopher Meis extern "C"
583cbff97fSChristopher Meis {
593cbff97fSChristopher Meis #include <i2c/smbus.h>
603cbff97fSChristopher Meis #include <linux/i2c-dev.h>
613cbff97fSChristopher Meis }
623cbff97fSChristopher Meis 
633cbff97fSChristopher Meis namespace fs = std::filesystem;
643cbff97fSChristopher Meis constexpr size_t maxFruSize = 512;
653cbff97fSChristopher Meis constexpr size_t maxEepromPageIndex = 255;
663cbff97fSChristopher Meis constexpr size_t busTimeoutSeconds = 10;
673cbff97fSChristopher Meis 
683cbff97fSChristopher Meis constexpr const char* blocklistPath = PACKAGE_DIR "blacklist.json";
693cbff97fSChristopher Meis 
703cbff97fSChristopher Meis const static constexpr char* baseboardFruLocation =
713cbff97fSChristopher Meis     "/etc/fru/baseboard.fru.bin";
723cbff97fSChristopher Meis 
733cbff97fSChristopher Meis const static constexpr char* i2CDevLocation = "/dev";
743cbff97fSChristopher Meis 
753cbff97fSChristopher Meis constexpr const char* fruDevice16BitDetectMode = FRU_DEVICE_16BITDETECTMODE;
763cbff97fSChristopher Meis 
773cbff97fSChristopher Meis // TODO Refactor these to not be globals
783cbff97fSChristopher Meis // NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)
793cbff97fSChristopher Meis static boost::container::flat_map<size_t, std::optional<std::set<size_t>>>
803cbff97fSChristopher Meis     busBlocklist;
813cbff97fSChristopher Meis struct FindDevicesWithCallback;
823cbff97fSChristopher Meis 
833cbff97fSChristopher Meis static boost::container::flat_map<
843cbff97fSChristopher Meis     std::pair<size_t, size_t>, std::shared_ptr<sdbusplus::asio::dbus_interface>>
853cbff97fSChristopher Meis     foundDevices;
863cbff97fSChristopher Meis 
873cbff97fSChristopher Meis static boost::container::flat_map<size_t, std::set<size_t>> failedAddresses;
883cbff97fSChristopher Meis static boost::container::flat_map<size_t, std::set<size_t>> fruAddresses;
893cbff97fSChristopher Meis 
903cbff97fSChristopher Meis boost::asio::io_context io;
913cbff97fSChristopher Meis // NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
923cbff97fSChristopher Meis 
933cbff97fSChristopher Meis bool updateFRUProperty(
943cbff97fSChristopher Meis     const std::string& updatePropertyReq, uint32_t bus, uint32_t address,
953cbff97fSChristopher Meis     const std::string& propertyName,
963cbff97fSChristopher Meis     boost::container::flat_map<
973cbff97fSChristopher Meis         std::pair<size_t, size_t>,
983cbff97fSChristopher Meis         std::shared_ptr<sdbusplus::asio::dbus_interface>>& dbusInterfaceMap,
993cbff97fSChristopher Meis     size_t& unknownBusObjectCount, const bool& powerIsOn,
1003cbff97fSChristopher Meis     sdbusplus::asio::object_server& objServer,
1013cbff97fSChristopher Meis     std::shared_ptr<sdbusplus::asio::connection>& systemBus);
1023cbff97fSChristopher Meis 
1033cbff97fSChristopher Meis // Given a bus/address, produce the path in sysfs for an eeprom.
getEepromPath(size_t bus,size_t address)1043cbff97fSChristopher Meis static std::string getEepromPath(size_t bus, size_t address)
1053cbff97fSChristopher Meis {
1063cbff97fSChristopher Meis     std::stringstream output;
1073cbff97fSChristopher Meis     output << "/sys/bus/i2c/devices/" << bus << "-" << std::right
1083cbff97fSChristopher Meis            << std::setfill('0') << std::setw(4) << std::hex << address
1093cbff97fSChristopher Meis            << "/eeprom";
1103cbff97fSChristopher Meis     return output.str();
1113cbff97fSChristopher Meis }
1123cbff97fSChristopher Meis 
hasEepromFile(size_t bus,size_t address)1133cbff97fSChristopher Meis static bool hasEepromFile(size_t bus, size_t address)
1143cbff97fSChristopher Meis {
1153cbff97fSChristopher Meis     auto path = getEepromPath(bus, address);
1163cbff97fSChristopher Meis     try
1173cbff97fSChristopher Meis     {
1183cbff97fSChristopher Meis         return fs::exists(path);
1193cbff97fSChristopher Meis     }
1203cbff97fSChristopher Meis     catch (...)
1213cbff97fSChristopher Meis     {
1223cbff97fSChristopher Meis         return false;
1233cbff97fSChristopher Meis     }
1243cbff97fSChristopher Meis }
1253cbff97fSChristopher Meis 
readFromEeprom(int fd,off_t offset,size_t len,uint8_t * buf)1263cbff97fSChristopher Meis static int64_t readFromEeprom(int fd, off_t offset, size_t len, uint8_t* buf)
1273cbff97fSChristopher Meis {
1283cbff97fSChristopher Meis     auto result = lseek(fd, offset, SEEK_SET);
1293cbff97fSChristopher Meis     if (result < 0)
1303cbff97fSChristopher Meis     {
1313cbff97fSChristopher Meis         std::cerr << "failed to seek\n";
1323cbff97fSChristopher Meis         return -1;
1333cbff97fSChristopher Meis     }
1343cbff97fSChristopher Meis 
1353cbff97fSChristopher Meis     return read(fd, buf, len);
1363cbff97fSChristopher Meis }
1373cbff97fSChristopher Meis 
busStrToInt(const std::string_view busName)1383cbff97fSChristopher Meis static int busStrToInt(const std::string_view busName)
1393cbff97fSChristopher Meis {
1403cbff97fSChristopher Meis     auto findBus = busName.rfind('-');
1413cbff97fSChristopher Meis     if (findBus == std::string::npos)
1423cbff97fSChristopher Meis     {
1433cbff97fSChristopher Meis         return -1;
1443cbff97fSChristopher Meis     }
1453cbff97fSChristopher Meis     std::string_view num = busName.substr(findBus + 1);
1463cbff97fSChristopher Meis     int val = 0;
1473cbff97fSChristopher Meis     std::from_chars(num.data(), num.data() + num.size(), val);
1483cbff97fSChristopher Meis     return val;
1493cbff97fSChristopher Meis }
1503cbff97fSChristopher Meis 
getRootBus(size_t bus)1513cbff97fSChristopher Meis static int getRootBus(size_t bus)
1523cbff97fSChristopher Meis {
1533cbff97fSChristopher Meis     auto ec = std::error_code();
1543cbff97fSChristopher Meis     auto path = std::filesystem::read_symlink(
1553cbff97fSChristopher Meis         std::filesystem::path(
1563cbff97fSChristopher Meis             "/sys/bus/i2c/devices/i2c-" + std::to_string(bus) + "/mux_device"),
1573cbff97fSChristopher Meis         ec);
1583cbff97fSChristopher Meis     if (ec)
1593cbff97fSChristopher Meis     {
1603cbff97fSChristopher Meis         return -1;
1613cbff97fSChristopher Meis     }
1623cbff97fSChristopher Meis 
1633cbff97fSChristopher Meis     std::string filename = path.filename();
1643cbff97fSChristopher Meis     auto findBus = filename.find('-');
1653cbff97fSChristopher Meis     if (findBus == std::string::npos)
1663cbff97fSChristopher Meis     {
1673cbff97fSChristopher Meis         return -1;
1683cbff97fSChristopher Meis     }
1693cbff97fSChristopher Meis     return std::stoi(filename.substr(0, findBus));
1703cbff97fSChristopher Meis }
1713cbff97fSChristopher Meis 
isMuxBus(size_t bus)1723cbff97fSChristopher Meis static bool isMuxBus(size_t bus)
1733cbff97fSChristopher Meis {
1743cbff97fSChristopher Meis     auto ec = std::error_code();
1753cbff97fSChristopher Meis     auto isSymlink =
1763cbff97fSChristopher Meis         is_symlink(std::filesystem::path("/sys/bus/i2c/devices/i2c-" +
1773cbff97fSChristopher Meis                                          std::to_string(bus) + "/mux_device"),
1783cbff97fSChristopher Meis                    ec);
1793cbff97fSChristopher Meis     return (!ec && isSymlink);
1803cbff97fSChristopher Meis }
1813cbff97fSChristopher Meis 
makeProbeInterface(size_t bus,size_t address,sdbusplus::asio::object_server & objServer)1823cbff97fSChristopher Meis static void makeProbeInterface(size_t bus, size_t address,
1833cbff97fSChristopher Meis                                sdbusplus::asio::object_server& objServer)
1843cbff97fSChristopher Meis {
1853cbff97fSChristopher Meis     if (isMuxBus(bus))
1863cbff97fSChristopher Meis     {
1873cbff97fSChristopher Meis         return; // the mux buses are random, no need to publish
1883cbff97fSChristopher Meis     }
1893cbff97fSChristopher Meis     auto [it, success] = foundDevices.emplace(
1903cbff97fSChristopher Meis         std::make_pair(bus, address),
1913cbff97fSChristopher Meis         objServer.add_interface(
1923cbff97fSChristopher Meis             "/xyz/openbmc_project/FruDevice/" + std::to_string(bus) + "_" +
1933cbff97fSChristopher Meis                 std::to_string(address),
1943cbff97fSChristopher Meis             "xyz.openbmc_project.Inventory.Item.I2CDevice"));
1953cbff97fSChristopher Meis     if (!success)
1963cbff97fSChristopher Meis     {
1973cbff97fSChristopher Meis         return; // already added
1983cbff97fSChristopher Meis     }
1993cbff97fSChristopher Meis     it->second->register_property("Bus", bus);
2003cbff97fSChristopher Meis     it->second->register_property("Address", address);
2013cbff97fSChristopher Meis     it->second->initialize();
2023cbff97fSChristopher Meis }
2033cbff97fSChristopher Meis 
2043cbff97fSChristopher Meis // Issue an I2C transaction to first write to_target_buf_len bytes,then read
2053cbff97fSChristopher Meis // from_target_buf_len bytes.
i2cSmbusWriteThenRead(int file,uint16_t address,uint8_t * toTargetBuf,uint8_t toTargetBufLen,uint8_t * fromTargetBuf,uint8_t fromTargetBufLen)2063cbff97fSChristopher Meis static int i2cSmbusWriteThenRead(
2073cbff97fSChristopher Meis     int file, uint16_t address, uint8_t* toTargetBuf, uint8_t toTargetBufLen,
2083cbff97fSChristopher Meis     uint8_t* fromTargetBuf, uint8_t fromTargetBufLen)
2093cbff97fSChristopher Meis {
2103cbff97fSChristopher Meis     if (toTargetBuf == nullptr || toTargetBufLen == 0 ||
2113cbff97fSChristopher Meis         fromTargetBuf == nullptr || fromTargetBufLen == 0)
2123cbff97fSChristopher Meis     {
2133cbff97fSChristopher Meis         return -1;
2143cbff97fSChristopher Meis     }
2153cbff97fSChristopher Meis 
2163cbff97fSChristopher Meis     constexpr size_t smbusWriteThenReadMsgCount = 2;
2173cbff97fSChristopher Meis     std::array<struct i2c_msg, smbusWriteThenReadMsgCount> msgs{};
2183cbff97fSChristopher Meis     struct i2c_rdwr_ioctl_data rdwr{};
2193cbff97fSChristopher Meis 
2203cbff97fSChristopher Meis     msgs[0].addr = address;
2213cbff97fSChristopher Meis     msgs[0].flags = 0;
2223cbff97fSChristopher Meis     msgs[0].len = toTargetBufLen;
2233cbff97fSChristopher Meis     msgs[0].buf = toTargetBuf;
2243cbff97fSChristopher Meis     msgs[1].addr = address;
2253cbff97fSChristopher Meis     msgs[1].flags = I2C_M_RD;
2263cbff97fSChristopher Meis     msgs[1].len = fromTargetBufLen;
2273cbff97fSChristopher Meis     msgs[1].buf = fromTargetBuf;
2283cbff97fSChristopher Meis 
2293cbff97fSChristopher Meis     rdwr.msgs = msgs.data();
2303cbff97fSChristopher Meis     rdwr.nmsgs = msgs.size();
2313cbff97fSChristopher Meis 
2323cbff97fSChristopher Meis     int ret = ioctl(file, I2C_RDWR, &rdwr);
2333cbff97fSChristopher Meis 
2343cbff97fSChristopher Meis     return (ret == static_cast<int>(msgs.size())) ? msgs[1].len : -1;
2353cbff97fSChristopher Meis }
2363cbff97fSChristopher Meis 
readData(bool is16bit,bool isBytewise,int file,uint16_t address,off_t offset,size_t len,uint8_t * buf)2373cbff97fSChristopher Meis static int64_t readData(bool is16bit, bool isBytewise, int file,
2383cbff97fSChristopher Meis                         uint16_t address, off_t offset, size_t len,
2393cbff97fSChristopher Meis                         uint8_t* buf)
2403cbff97fSChristopher Meis {
2413cbff97fSChristopher Meis     if (!is16bit)
2423cbff97fSChristopher Meis     {
2433cbff97fSChristopher Meis         if (!isBytewise)
2443cbff97fSChristopher Meis         {
2453cbff97fSChristopher Meis             return i2c_smbus_read_i2c_block_data(
2463cbff97fSChristopher Meis                 file, static_cast<uint8_t>(offset), len, buf);
2473cbff97fSChristopher Meis         }
2483cbff97fSChristopher Meis 
2493cbff97fSChristopher Meis         std::span<uint8_t> bufspan{buf, len};
2503cbff97fSChristopher Meis         for (size_t i = 0; i < len; i++)
2513cbff97fSChristopher Meis         {
2523cbff97fSChristopher Meis             int byte = i2c_smbus_read_byte_data(
2533cbff97fSChristopher Meis                 file, static_cast<uint8_t>(offset + i));
2543cbff97fSChristopher Meis             if (byte < 0)
2553cbff97fSChristopher Meis             {
2563cbff97fSChristopher Meis                 return static_cast<int64_t>(byte);
2573cbff97fSChristopher Meis             }
2583cbff97fSChristopher Meis             bufspan[i] = static_cast<uint8_t>(byte);
2593cbff97fSChristopher Meis         }
2603cbff97fSChristopher Meis         return static_cast<int64_t>(len);
2613cbff97fSChristopher Meis     }
2623cbff97fSChristopher Meis 
2633cbff97fSChristopher Meis     offset = htobe16(offset);
2643cbff97fSChristopher Meis     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
2653cbff97fSChristopher Meis     uint8_t* u8Offset = reinterpret_cast<uint8_t*>(&offset);
2663cbff97fSChristopher Meis     return i2cSmbusWriteThenRead(file, address, u8Offset, 2, buf, len);
2673cbff97fSChristopher Meis }
2683cbff97fSChristopher Meis 
2693cbff97fSChristopher Meis // Mode_1:
2703cbff97fSChristopher Meis // --------
2713cbff97fSChristopher Meis // Please refer to document docs/address_size_detection_modes.md for
2723cbff97fSChristopher Meis // more details and explanations.
isDevice16BitMode1(int file)2733cbff97fSChristopher Meis static std::optional<bool> isDevice16BitMode1(int file)
2743cbff97fSChristopher Meis {
2753cbff97fSChristopher Meis     // Set the higher data word address bits to 0. It's safe on 8-bit
2763cbff97fSChristopher Meis     // addressing EEPROMs because it doesn't write any actual data.
2773cbff97fSChristopher Meis     int ret = i2c_smbus_write_byte(file, 0);
2783cbff97fSChristopher Meis     if (ret < 0)
2793cbff97fSChristopher Meis     {
2803cbff97fSChristopher Meis         return std::nullopt;
2813cbff97fSChristopher Meis     }
2823cbff97fSChristopher Meis 
2833cbff97fSChristopher Meis     /* Get first byte */
2843cbff97fSChristopher Meis     int byte1 = i2c_smbus_read_byte_data(file, 0);
2853cbff97fSChristopher Meis     if (byte1 < 0)
2863cbff97fSChristopher Meis     {
2873cbff97fSChristopher Meis         return std::nullopt;
2883cbff97fSChristopher Meis     }
2893cbff97fSChristopher Meis     /* Read 7 more bytes, it will read same first byte in case of
2903cbff97fSChristopher Meis      * 8 bit but it will read next byte in case of 16 bit
2913cbff97fSChristopher Meis      */
2923cbff97fSChristopher Meis     for (int i = 0; i < 7; i++)
2933cbff97fSChristopher Meis     {
2943cbff97fSChristopher Meis         int byte2 = i2c_smbus_read_byte_data(file, 0);
2953cbff97fSChristopher Meis         if (byte2 < 0)
2963cbff97fSChristopher Meis         {
2973cbff97fSChristopher Meis             return std::nullopt;
2983cbff97fSChristopher Meis         }
2993cbff97fSChristopher Meis         if (byte2 != byte1)
3003cbff97fSChristopher Meis         {
3013cbff97fSChristopher Meis             return true;
3023cbff97fSChristopher Meis         }
3033cbff97fSChristopher Meis     }
3043cbff97fSChristopher Meis     return false;
3053cbff97fSChristopher Meis }
3063cbff97fSChristopher Meis 
3073cbff97fSChristopher Meis // Mode_2:
3083cbff97fSChristopher Meis // --------
3093cbff97fSChristopher Meis // Please refer to document docs/address_size_detection_modes.md for
3103cbff97fSChristopher Meis // more details and explanations.
isDevice16BitMode2(int file,uint16_t address)3113cbff97fSChristopher Meis static std::optional<bool> isDevice16BitMode2(int file, uint16_t address)
3123cbff97fSChristopher Meis {
3133cbff97fSChristopher Meis     uint8_t first = 0;
3143cbff97fSChristopher Meis     uint8_t cur = 0;
3153cbff97fSChristopher Meis     uint16_t v = 0;
3163cbff97fSChristopher Meis     int ret = 0;
3173cbff97fSChristopher Meis     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
3183cbff97fSChristopher Meis     uint8_t* p = reinterpret_cast<uint8_t*>(&v);
3193cbff97fSChristopher Meis 
3203cbff97fSChristopher Meis     /*
3213cbff97fSChristopher Meis      * Write 2 bytes byte0 = 0, byte1 = {0..7} and then subsequent read byte
3223cbff97fSChristopher Meis      * It will read same first byte in case of 8 bit but
3233cbff97fSChristopher Meis      * it will read next byte in case of 16 bit
3243cbff97fSChristopher Meis      */
3253cbff97fSChristopher Meis     for (int i = 0; i < 8; i++)
3263cbff97fSChristopher Meis     {
3273cbff97fSChristopher Meis         v = htobe16(i);
3283cbff97fSChristopher Meis 
3293cbff97fSChristopher Meis         ret = i2cSmbusWriteThenRead(file, address, p, 2, &cur, 1);
3303cbff97fSChristopher Meis         if (ret < 0)
3313cbff97fSChristopher Meis         {
3323cbff97fSChristopher Meis             return std::nullopt;
3333cbff97fSChristopher Meis         }
3343cbff97fSChristopher Meis 
3353cbff97fSChristopher Meis         if (i == 0)
3363cbff97fSChristopher Meis         {
3373cbff97fSChristopher Meis             first = cur;
3383cbff97fSChristopher Meis         }
3393cbff97fSChristopher Meis 
3403cbff97fSChristopher Meis         if (first != cur)
3413cbff97fSChristopher Meis         {
3423cbff97fSChristopher Meis             return true;
3433cbff97fSChristopher Meis         }
3443cbff97fSChristopher Meis     }
3453cbff97fSChristopher Meis     return false;
3463cbff97fSChristopher Meis }
3473cbff97fSChristopher Meis 
isDevice16Bit(int file,uint16_t address)3483cbff97fSChristopher Meis static std::optional<bool> isDevice16Bit(int file, uint16_t address)
3493cbff97fSChristopher Meis {
3503cbff97fSChristopher Meis     std::string mode(fruDevice16BitDetectMode);
3513cbff97fSChristopher Meis 
3523cbff97fSChristopher Meis     if (mode == "MODE_2")
3533cbff97fSChristopher Meis     {
3543cbff97fSChristopher Meis         return isDevice16BitMode2(file, address);
3553cbff97fSChristopher Meis     }
3563cbff97fSChristopher Meis 
3573cbff97fSChristopher Meis     return isDevice16BitMode1(file);
3583cbff97fSChristopher Meis }
3593cbff97fSChristopher Meis 
3603cbff97fSChristopher Meis // TODO: This code is very similar to the non-eeprom version and can be merged
3613cbff97fSChristopher Meis // with some tweaks.
processEeprom(int bus,int address)3623cbff97fSChristopher Meis static std::vector<uint8_t> processEeprom(int bus, int address)
3633cbff97fSChristopher Meis {
3643cbff97fSChristopher Meis     auto path = getEepromPath(bus, address);
3653cbff97fSChristopher Meis 
3663cbff97fSChristopher Meis     int file = open(path.c_str(), O_RDONLY);
3673cbff97fSChristopher Meis     if (file < 0)
3683cbff97fSChristopher Meis     {
3693cbff97fSChristopher Meis         std::cerr << "Unable to open eeprom file: " << path << "\n";
3703cbff97fSChristopher Meis         return {};
3713cbff97fSChristopher Meis     }
3723cbff97fSChristopher Meis 
3733cbff97fSChristopher Meis     std::string errorMessage = "eeprom at " + std::to_string(bus) +
3743cbff97fSChristopher Meis                                " address " + std::to_string(address);
3753cbff97fSChristopher Meis     auto readFunc = [file](off_t offset, size_t length, uint8_t* outbuf) {
3763cbff97fSChristopher Meis         return readFromEeprom(file, offset, length, outbuf);
3773cbff97fSChristopher Meis     };
3783cbff97fSChristopher Meis     FRUReader reader(std::move(readFunc));
3793cbff97fSChristopher Meis     std::pair<std::vector<uint8_t>, bool> pair =
3803cbff97fSChristopher Meis         readFRUContents(reader, errorMessage);
3813cbff97fSChristopher Meis 
3823cbff97fSChristopher Meis     close(file);
3833cbff97fSChristopher Meis     return pair.first;
3843cbff97fSChristopher Meis }
3853cbff97fSChristopher Meis 
findI2CEeproms(int i2cBus,const std::shared_ptr<DeviceMap> & devices)3863cbff97fSChristopher Meis std::set<size_t> findI2CEeproms(int i2cBus,
3873cbff97fSChristopher Meis                                 const std::shared_ptr<DeviceMap>& devices)
3883cbff97fSChristopher Meis {
3893cbff97fSChristopher Meis     std::set<size_t> foundList;
3903cbff97fSChristopher Meis 
3913cbff97fSChristopher Meis     std::string path = "/sys/bus/i2c/devices/i2c-" + std::to_string(i2cBus);
3923cbff97fSChristopher Meis 
3933cbff97fSChristopher Meis     // For each file listed under the i2c device
3943cbff97fSChristopher Meis     // NOTE: This should be faster than just checking for each possible address
3953cbff97fSChristopher Meis     // path.
3963cbff97fSChristopher Meis     auto ec = std::error_code();
3973cbff97fSChristopher Meis     for (const auto& p : fs::directory_iterator(path, ec))
3983cbff97fSChristopher Meis     {
3993cbff97fSChristopher Meis         if (ec)
4003cbff97fSChristopher Meis         {
4013cbff97fSChristopher Meis             std::cerr << "directory_iterator err " << ec.message() << "\n";
4023cbff97fSChristopher Meis             break;
4033cbff97fSChristopher Meis         }
4043cbff97fSChristopher Meis         const std::string node = p.path().string();
4053cbff97fSChristopher Meis         std::smatch m;
4063cbff97fSChristopher Meis         bool found =
4073cbff97fSChristopher Meis             std::regex_match(node, m, std::regex(".+\\d+-([0-9abcdef]+$)"));
4083cbff97fSChristopher Meis 
4093cbff97fSChristopher Meis         if (!found)
4103cbff97fSChristopher Meis         {
4113cbff97fSChristopher Meis             continue;
4123cbff97fSChristopher Meis         }
4133cbff97fSChristopher Meis         if (m.size() != 2)
4143cbff97fSChristopher Meis         {
4153cbff97fSChristopher Meis             std::cerr << "regex didn't capture\n";
4163cbff97fSChristopher Meis             continue;
4173cbff97fSChristopher Meis         }
4183cbff97fSChristopher Meis 
4193cbff97fSChristopher Meis         std::ssub_match subMatch = m[1];
4203cbff97fSChristopher Meis         std::string addressString = subMatch.str();
4213cbff97fSChristopher Meis         std::string_view addressStringView(addressString);
4223cbff97fSChristopher Meis 
4233cbff97fSChristopher Meis         size_t address = 0;
4243cbff97fSChristopher Meis         std::from_chars(addressStringView.begin(), addressStringView.end(),
4253cbff97fSChristopher Meis                         address, 16);
4263cbff97fSChristopher Meis 
4273cbff97fSChristopher Meis         const std::string eeprom = node + "/eeprom";
4283cbff97fSChristopher Meis 
4293cbff97fSChristopher Meis         try
4303cbff97fSChristopher Meis         {
4313cbff97fSChristopher Meis             if (!fs::exists(eeprom))
4323cbff97fSChristopher Meis             {
4333cbff97fSChristopher Meis                 continue;
4343cbff97fSChristopher Meis             }
4353cbff97fSChristopher Meis         }
4363cbff97fSChristopher Meis         catch (...)
4373cbff97fSChristopher Meis         {
4383cbff97fSChristopher Meis             continue;
4393cbff97fSChristopher Meis         }
4403cbff97fSChristopher Meis 
4413cbff97fSChristopher Meis         // There is an eeprom file at this address, it may have invalid
4423cbff97fSChristopher Meis         // contents, but we found it.
4433cbff97fSChristopher Meis         foundList.insert(address);
4443cbff97fSChristopher Meis 
4453cbff97fSChristopher Meis         std::vector<uint8_t> device = processEeprom(i2cBus, address);
4463cbff97fSChristopher Meis         if (!device.empty())
4473cbff97fSChristopher Meis         {
4483cbff97fSChristopher Meis             devices->emplace(address, device);
4493cbff97fSChristopher Meis         }
4503cbff97fSChristopher Meis     }
4513cbff97fSChristopher Meis 
4523cbff97fSChristopher Meis     return foundList;
4533cbff97fSChristopher Meis }
4543cbff97fSChristopher Meis 
getBusFRUs(int file,int first,int last,int bus,std::shared_ptr<DeviceMap> devices,const bool & powerIsOn,sdbusplus::asio::object_server & objServer)4553cbff97fSChristopher Meis int getBusFRUs(int file, int first, int last, int bus,
4563cbff97fSChristopher Meis                std::shared_ptr<DeviceMap> devices, const bool& powerIsOn,
4573cbff97fSChristopher Meis                sdbusplus::asio::object_server& objServer)
4583cbff97fSChristopher Meis {
4593cbff97fSChristopher Meis     std::future<int> future = std::async(std::launch::async, [&]() {
4603cbff97fSChristopher Meis         // NOTE: When reading the devices raw on the bus, it can interfere with
4613cbff97fSChristopher Meis         // the driver's ability to operate, therefore read eeproms first before
4623cbff97fSChristopher Meis         // scanning for devices without drivers. Several experiments were run
4633cbff97fSChristopher Meis         // and it was determined that if there were any devices on the bus
4643cbff97fSChristopher Meis         // before the eeprom was hit and read, the eeprom driver wouldn't open
4653cbff97fSChristopher Meis         // while the bus device was open. An experiment was not performed to see
4663cbff97fSChristopher Meis         // if this issue was resolved if the i2c bus device was closed, but
4673cbff97fSChristopher Meis         // hexdumps of the eeprom later were successful.
4683cbff97fSChristopher Meis 
4693cbff97fSChristopher Meis         // Scan for i2c eeproms loaded on this bus.
4703cbff97fSChristopher Meis         std::set<size_t> skipList = findI2CEeproms(bus, devices);
4713cbff97fSChristopher Meis         std::set<size_t>& failedItems = failedAddresses[bus];
4723cbff97fSChristopher Meis         std::set<size_t>& foundItems = fruAddresses[bus];
4733cbff97fSChristopher Meis         foundItems.clear();
4743cbff97fSChristopher Meis 
4753cbff97fSChristopher Meis         auto busFind = busBlocklist.find(bus);
4763cbff97fSChristopher Meis         if (busFind != busBlocklist.end())
4773cbff97fSChristopher Meis         {
4783cbff97fSChristopher Meis             if (busFind->second != std::nullopt)
4793cbff97fSChristopher Meis             {
4803cbff97fSChristopher Meis                 for (const auto& address : *(busFind->second))
4813cbff97fSChristopher Meis                 {
4823cbff97fSChristopher Meis                     skipList.insert(address);
4833cbff97fSChristopher Meis                 }
4843cbff97fSChristopher Meis             }
4853cbff97fSChristopher Meis         }
4863cbff97fSChristopher Meis 
4873cbff97fSChristopher Meis         std::set<size_t>* rootFailures = nullptr;
4883cbff97fSChristopher Meis         int rootBus = getRootBus(bus);
4893cbff97fSChristopher Meis 
4903cbff97fSChristopher Meis         if (rootBus >= 0)
4913cbff97fSChristopher Meis         {
4923cbff97fSChristopher Meis             auto rootBusFind = busBlocklist.find(rootBus);
4933cbff97fSChristopher Meis             if (rootBusFind != busBlocklist.end())
4943cbff97fSChristopher Meis             {
4953cbff97fSChristopher Meis                 if (rootBusFind->second != std::nullopt)
4963cbff97fSChristopher Meis                 {
4973cbff97fSChristopher Meis                     for (const auto& rootAddress : *(rootBusFind->second))
4983cbff97fSChristopher Meis                     {
4993cbff97fSChristopher Meis                         skipList.insert(rootAddress);
5003cbff97fSChristopher Meis                     }
5013cbff97fSChristopher Meis                 }
5023cbff97fSChristopher Meis             }
5033cbff97fSChristopher Meis             rootFailures = &(failedAddresses[rootBus]);
5043cbff97fSChristopher Meis             foundItems = fruAddresses[rootBus];
5053cbff97fSChristopher Meis         }
5063cbff97fSChristopher Meis 
5073cbff97fSChristopher Meis         constexpr int startSkipTargetAddr = 0;
5083cbff97fSChristopher Meis         constexpr int endSkipTargetAddr = 12;
5093cbff97fSChristopher Meis 
5103cbff97fSChristopher Meis         for (int ii = first; ii <= last; ii++)
5113cbff97fSChristopher Meis         {
512*6459993cSPatrick Williams             if (foundItems.contains(ii))
5133cbff97fSChristopher Meis             {
5143cbff97fSChristopher Meis                 continue;
5153cbff97fSChristopher Meis             }
516*6459993cSPatrick Williams             if (skipList.contains(ii))
5173cbff97fSChristopher Meis             {
5183cbff97fSChristopher Meis                 continue;
5193cbff97fSChristopher Meis             }
5203cbff97fSChristopher Meis             // skipping since no device is present in this range
5213cbff97fSChristopher Meis             if (ii >= startSkipTargetAddr && ii <= endSkipTargetAddr)
5223cbff97fSChristopher Meis             {
5233cbff97fSChristopher Meis                 continue;
5243cbff97fSChristopher Meis             }
5253cbff97fSChristopher Meis             // Set target address
5263cbff97fSChristopher Meis             if (ioctl(file, I2C_SLAVE, ii) < 0)
5273cbff97fSChristopher Meis             {
5283cbff97fSChristopher Meis                 std::cerr << "device at bus " << bus << " address " << ii
5293cbff97fSChristopher Meis                           << " busy\n";
5303cbff97fSChristopher Meis                 continue;
5313cbff97fSChristopher Meis             }
5323cbff97fSChristopher Meis             // probe
5333cbff97fSChristopher Meis             if (i2c_smbus_read_byte(file) < 0)
5343cbff97fSChristopher Meis             {
5353cbff97fSChristopher Meis                 continue;
5363cbff97fSChristopher Meis             }
5373cbff97fSChristopher Meis 
5383cbff97fSChristopher Meis             lg2::debug("something at bus {BUS}, addr {ADDR}", "BUS", bus,
5393cbff97fSChristopher Meis                        "ADDR", ii);
5403cbff97fSChristopher Meis 
5413cbff97fSChristopher Meis             makeProbeInterface(bus, ii, objServer);
5423cbff97fSChristopher Meis 
543*6459993cSPatrick Williams             if (failedItems.contains(ii))
5443cbff97fSChristopher Meis             {
5453cbff97fSChristopher Meis                 // if we failed to read it once, unlikely we can read it later
5463cbff97fSChristopher Meis                 continue;
5473cbff97fSChristopher Meis             }
5483cbff97fSChristopher Meis 
5493cbff97fSChristopher Meis             if (rootFailures != nullptr)
5503cbff97fSChristopher Meis             {
551*6459993cSPatrick Williams                 if (rootFailures->contains(ii))
5523cbff97fSChristopher Meis                 {
5533cbff97fSChristopher Meis                     continue;
5543cbff97fSChristopher Meis                 }
5553cbff97fSChristopher Meis             }
5563cbff97fSChristopher Meis 
5573cbff97fSChristopher Meis             /* Check for Device type if it is 8 bit or 16 bit */
5583cbff97fSChristopher Meis             std::optional<bool> is16Bit = isDevice16Bit(file, ii);
5593cbff97fSChristopher Meis             if (!is16Bit.has_value())
5603cbff97fSChristopher Meis             {
5613cbff97fSChristopher Meis                 std::cerr << "failed to read bus " << bus << " address " << ii
5623cbff97fSChristopher Meis                           << "\n";
5633cbff97fSChristopher Meis                 if (powerIsOn)
5643cbff97fSChristopher Meis                 {
5653cbff97fSChristopher Meis                     failedItems.insert(ii);
5663cbff97fSChristopher Meis                 }
5673cbff97fSChristopher Meis                 continue;
5683cbff97fSChristopher Meis             }
5693cbff97fSChristopher Meis             bool is16BitBool{*is16Bit};
5703cbff97fSChristopher Meis 
5713cbff97fSChristopher Meis             auto readFunc = [is16BitBool, file,
5723cbff97fSChristopher Meis                              ii](off_t offset, size_t length, uint8_t* outbuf) {
5733cbff97fSChristopher Meis                 return readData(is16BitBool, false, file, ii, offset, length,
5743cbff97fSChristopher Meis                                 outbuf);
5753cbff97fSChristopher Meis             };
5763cbff97fSChristopher Meis             FRUReader reader(std::move(readFunc));
5773cbff97fSChristopher Meis             std::string errorMessage =
5783cbff97fSChristopher Meis                 "bus " + std::to_string(bus) + " address " + std::to_string(ii);
5793cbff97fSChristopher Meis             std::pair<std::vector<uint8_t>, bool> pair =
5803cbff97fSChristopher Meis                 readFRUContents(reader, errorMessage);
5813cbff97fSChristopher Meis             const bool foundHeader = pair.second;
5823cbff97fSChristopher Meis 
5833cbff97fSChristopher Meis             if (!foundHeader && !is16BitBool)
5843cbff97fSChristopher Meis             {
5853cbff97fSChristopher Meis                 // certain FRU eeproms require bytewise reading.
5863cbff97fSChristopher Meis                 // otherwise garbage is read. e.g. SuperMicro PWS 920P-SQ
5873cbff97fSChristopher Meis 
5883cbff97fSChristopher Meis                 auto readFunc =
5893cbff97fSChristopher Meis                     [is16BitBool, file,
5903cbff97fSChristopher Meis                      ii](off_t offset, size_t length, uint8_t* outbuf) {
5913cbff97fSChristopher Meis                         return readData(is16BitBool, true, file, ii, offset,
5923cbff97fSChristopher Meis                                         length, outbuf);
5933cbff97fSChristopher Meis                     };
5943cbff97fSChristopher Meis                 FRUReader readerBytewise(std::move(readFunc));
5953cbff97fSChristopher Meis                 pair = readFRUContents(readerBytewise, errorMessage);
5963cbff97fSChristopher Meis             }
5973cbff97fSChristopher Meis 
5983cbff97fSChristopher Meis             if (pair.first.empty())
5993cbff97fSChristopher Meis             {
6003cbff97fSChristopher Meis                 continue;
6013cbff97fSChristopher Meis             }
6023cbff97fSChristopher Meis 
6033cbff97fSChristopher Meis             devices->emplace(ii, pair.first);
6043cbff97fSChristopher Meis             fruAddresses[bus].insert(ii);
6053cbff97fSChristopher Meis         }
6063cbff97fSChristopher Meis         return 1;
6073cbff97fSChristopher Meis     });
6083cbff97fSChristopher Meis     std::future_status status =
6093cbff97fSChristopher Meis         future.wait_for(std::chrono::seconds(busTimeoutSeconds));
6103cbff97fSChristopher Meis     if (status == std::future_status::timeout)
6113cbff97fSChristopher Meis     {
6123cbff97fSChristopher Meis         std::cerr << "Error reading bus " << bus << "\n";
6133cbff97fSChristopher Meis         if (powerIsOn)
6143cbff97fSChristopher Meis         {
6153cbff97fSChristopher Meis             busBlocklist[bus] = std::nullopt;
6163cbff97fSChristopher Meis         }
6173cbff97fSChristopher Meis         close(file);
6183cbff97fSChristopher Meis         return -1;
6193cbff97fSChristopher Meis     }
6203cbff97fSChristopher Meis 
6213cbff97fSChristopher Meis     close(file);
6223cbff97fSChristopher Meis     return future.get();
6233cbff97fSChristopher Meis }
6243cbff97fSChristopher Meis 
loadBlocklist(const char * path)6253cbff97fSChristopher Meis void loadBlocklist(const char* path)
6263cbff97fSChristopher Meis {
6273cbff97fSChristopher Meis     std::ifstream blocklistStream(path);
6283cbff97fSChristopher Meis     if (!blocklistStream.good())
6293cbff97fSChristopher Meis     {
6303cbff97fSChristopher Meis         // File is optional.
6313cbff97fSChristopher Meis         std::cerr << "Cannot open blocklist file.\n\n";
6323cbff97fSChristopher Meis         return;
6333cbff97fSChristopher Meis     }
6343cbff97fSChristopher Meis 
6353cbff97fSChristopher Meis     nlohmann::json data =
6363cbff97fSChristopher Meis         nlohmann::json::parse(blocklistStream, nullptr, false);
6373cbff97fSChristopher Meis     if (data.is_discarded())
6383cbff97fSChristopher Meis     {
6393cbff97fSChristopher Meis         std::cerr << "Illegal blocklist file detected, cannot validate JSON, "
6403cbff97fSChristopher Meis                      "exiting\n";
6413cbff97fSChristopher Meis         std::exit(EXIT_FAILURE);
6423cbff97fSChristopher Meis     }
6433cbff97fSChristopher Meis 
6443cbff97fSChristopher Meis     // It's expected to have at least one field, "buses" that is an array of the
6453cbff97fSChristopher Meis     // buses by integer. Allow for future options to exclude further aspects,
6463cbff97fSChristopher Meis     // such as specific addresses or ranges.
6473cbff97fSChristopher Meis     if (data.type() != nlohmann::json::value_t::object)
6483cbff97fSChristopher Meis     {
6493cbff97fSChristopher Meis         std::cerr << "Illegal blocklist, expected to read dictionary\n";
6503cbff97fSChristopher Meis         std::exit(EXIT_FAILURE);
6513cbff97fSChristopher Meis     }
6523cbff97fSChristopher Meis 
6533cbff97fSChristopher Meis     // If buses field is missing, that's fine.
6543cbff97fSChristopher Meis     if (data.count("buses") == 1)
6553cbff97fSChristopher Meis     {
6563cbff97fSChristopher Meis         // Parse the buses array after a little validation.
6573cbff97fSChristopher Meis         auto buses = data.at("buses");
6583cbff97fSChristopher Meis         if (buses.type() != nlohmann::json::value_t::array)
6593cbff97fSChristopher Meis         {
6603cbff97fSChristopher Meis             // Buses field present but invalid, therefore this is an error.
6613cbff97fSChristopher Meis             std::cerr << "Invalid contents for blocklist buses field\n";
6623cbff97fSChristopher Meis             std::exit(EXIT_FAILURE);
6633cbff97fSChristopher Meis         }
6643cbff97fSChristopher Meis 
6653cbff97fSChristopher Meis         // Catch exception here for type mis-match.
6663cbff97fSChristopher Meis         try
6673cbff97fSChristopher Meis         {
6683cbff97fSChristopher Meis             for (const auto& busIterator : buses)
6693cbff97fSChristopher Meis             {
6703cbff97fSChristopher Meis                 // If bus and addresses field are missing, that's fine.
6713cbff97fSChristopher Meis                 if (busIterator.contains("bus") &&
6723cbff97fSChristopher Meis                     busIterator.contains("addresses"))
6733cbff97fSChristopher Meis                 {
6743cbff97fSChristopher Meis                     auto busData = busIterator.at("bus");
6753cbff97fSChristopher Meis                     auto bus = busData.get<size_t>();
6763cbff97fSChristopher Meis 
6773cbff97fSChristopher Meis                     auto addressData = busIterator.at("addresses");
6783cbff97fSChristopher Meis                     auto addresses =
6793cbff97fSChristopher Meis                         addressData.get<std::set<std::string_view>>();
6803cbff97fSChristopher Meis 
6813cbff97fSChristopher Meis                     auto& block = busBlocklist[bus].emplace();
6823cbff97fSChristopher Meis                     for (const auto& address : addresses)
6833cbff97fSChristopher Meis                     {
6843cbff97fSChristopher Meis                         size_t addressInt = 0;
6853cbff97fSChristopher Meis                         std::from_chars(address.begin() + 2, address.end(),
6863cbff97fSChristopher Meis                                         addressInt, 16);
6873cbff97fSChristopher Meis                         block.insert(addressInt);
6883cbff97fSChristopher Meis                     }
6893cbff97fSChristopher Meis                 }
6903cbff97fSChristopher Meis                 else
6913cbff97fSChristopher Meis                 {
6923cbff97fSChristopher Meis                     busBlocklist[busIterator.get<size_t>()] = std::nullopt;
6933cbff97fSChristopher Meis                 }
6943cbff97fSChristopher Meis             }
6953cbff97fSChristopher Meis         }
6963cbff97fSChristopher Meis         catch (const nlohmann::detail::type_error& e)
6973cbff97fSChristopher Meis         {
6983cbff97fSChristopher Meis             // Type mis-match is a critical error.
6993cbff97fSChristopher Meis             std::cerr << "Invalid bus type: " << e.what() << "\n";
7003cbff97fSChristopher Meis             std::exit(EXIT_FAILURE);
7013cbff97fSChristopher Meis         }
7023cbff97fSChristopher Meis     }
7033cbff97fSChristopher Meis }
7043cbff97fSChristopher Meis 
findI2CDevices(const std::vector<fs::path> & i2cBuses,BusMap & busmap,const bool & powerIsOn,sdbusplus::asio::object_server & objServer)7053cbff97fSChristopher Meis static void findI2CDevices(const std::vector<fs::path>& i2cBuses,
7063cbff97fSChristopher Meis                            BusMap& busmap, const bool& powerIsOn,
7073cbff97fSChristopher Meis                            sdbusplus::asio::object_server& objServer)
7083cbff97fSChristopher Meis {
7093cbff97fSChristopher Meis     for (const auto& i2cBus : i2cBuses)
7103cbff97fSChristopher Meis     {
7113cbff97fSChristopher Meis         int bus = busStrToInt(i2cBus.string());
7123cbff97fSChristopher Meis 
7133cbff97fSChristopher Meis         if (bus < 0)
7143cbff97fSChristopher Meis         {
7153cbff97fSChristopher Meis             std::cerr << "Cannot translate " << i2cBus << " to int\n";
7163cbff97fSChristopher Meis             continue;
7173cbff97fSChristopher Meis         }
7183cbff97fSChristopher Meis         auto busFind = busBlocklist.find(bus);
7193cbff97fSChristopher Meis         if (busFind != busBlocklist.end())
7203cbff97fSChristopher Meis         {
7213cbff97fSChristopher Meis             if (busFind->second == std::nullopt)
7223cbff97fSChristopher Meis             {
7233cbff97fSChristopher Meis                 continue; // Skip blocked busses.
7243cbff97fSChristopher Meis             }
7253cbff97fSChristopher Meis         }
7263cbff97fSChristopher Meis         int rootBus = getRootBus(bus);
7273cbff97fSChristopher Meis         auto rootBusFind = busBlocklist.find(rootBus);
7283cbff97fSChristopher Meis         if (rootBusFind != busBlocklist.end())
7293cbff97fSChristopher Meis         {
7303cbff97fSChristopher Meis             if (rootBusFind->second == std::nullopt)
7313cbff97fSChristopher Meis             {
7323cbff97fSChristopher Meis                 continue;
7333cbff97fSChristopher Meis             }
7343cbff97fSChristopher Meis         }
7353cbff97fSChristopher Meis 
7363cbff97fSChristopher Meis         auto file = open(i2cBus.c_str(), O_RDWR);
7373cbff97fSChristopher Meis         if (file < 0)
7383cbff97fSChristopher Meis         {
7393cbff97fSChristopher Meis             std::cerr << "unable to open i2c device " << i2cBus.string()
7403cbff97fSChristopher Meis                       << "\n";
7413cbff97fSChristopher Meis             continue;
7423cbff97fSChristopher Meis         }
7433cbff97fSChristopher Meis         unsigned long funcs = 0;
7443cbff97fSChristopher Meis 
7453cbff97fSChristopher Meis         if (ioctl(file, I2C_FUNCS, &funcs) < 0)
7463cbff97fSChristopher Meis         {
7473cbff97fSChristopher Meis             std::cerr
7483cbff97fSChristopher Meis                 << "Error: Could not get the adapter functionality matrix bus "
7493cbff97fSChristopher Meis                 << bus << "\n";
7503cbff97fSChristopher Meis             close(file);
7513cbff97fSChristopher Meis             continue;
7523cbff97fSChristopher Meis         }
7533cbff97fSChristopher Meis         if (((funcs & I2C_FUNC_SMBUS_READ_BYTE) == 0U) ||
7543cbff97fSChristopher Meis             ((I2C_FUNC_SMBUS_READ_I2C_BLOCK) == 0))
7553cbff97fSChristopher Meis         {
7563cbff97fSChristopher Meis             std::cerr << "Error: Can't use SMBus Receive Byte command bus "
7573cbff97fSChristopher Meis                       << bus << "\n";
7583cbff97fSChristopher Meis             close(file);
7593cbff97fSChristopher Meis             continue;
7603cbff97fSChristopher Meis         }
7613cbff97fSChristopher Meis         auto& device = busmap[bus];
7623cbff97fSChristopher Meis         device = std::make_shared<DeviceMap>();
7633cbff97fSChristopher Meis 
7643cbff97fSChristopher Meis         //  i2cdetect by default uses the range 0x03 to 0x77, as
7653cbff97fSChristopher Meis         //  this is  what we have tested with, use this range. Could be
7663cbff97fSChristopher Meis         //  changed in future.
7673cbff97fSChristopher Meis         lg2::debug("Scanning bus {BUS}", "BUS", bus);
7683cbff97fSChristopher Meis 
7693cbff97fSChristopher Meis         // fd is closed in this function in case the bus locks up
7703cbff97fSChristopher Meis         getBusFRUs(file, 0x03, 0x77, bus, device, powerIsOn, objServer);
7713cbff97fSChristopher Meis 
7723cbff97fSChristopher Meis         lg2::debug("Done scanning bus {BUS}", "BUS", bus);
7733cbff97fSChristopher Meis     }
7743cbff97fSChristopher Meis }
7753cbff97fSChristopher Meis 
7763cbff97fSChristopher Meis // this class allows an async response after all i2c devices are discovered
7773cbff97fSChristopher Meis struct FindDevicesWithCallback :
7783cbff97fSChristopher Meis     std::enable_shared_from_this<FindDevicesWithCallback>
7793cbff97fSChristopher Meis {
FindDevicesWithCallbackFindDevicesWithCallback7803cbff97fSChristopher Meis     FindDevicesWithCallback(const std::vector<fs::path>& i2cBuses,
7813cbff97fSChristopher Meis                             BusMap& busmap, const bool& powerIsOn,
7823cbff97fSChristopher Meis                             sdbusplus::asio::object_server& objServer,
7833cbff97fSChristopher Meis                             std::function<void()>&& callback) :
7843cbff97fSChristopher Meis         _i2cBuses(i2cBuses), _busMap(busmap), _powerIsOn(powerIsOn),
7853cbff97fSChristopher Meis         _objServer(objServer), _callback(std::move(callback))
7863cbff97fSChristopher Meis     {}
~FindDevicesWithCallbackFindDevicesWithCallback7873cbff97fSChristopher Meis     ~FindDevicesWithCallback()
7883cbff97fSChristopher Meis     {
7893cbff97fSChristopher Meis         _callback();
7903cbff97fSChristopher Meis     }
runFindDevicesWithCallback7913cbff97fSChristopher Meis     void run()
7923cbff97fSChristopher Meis     {
7933cbff97fSChristopher Meis         findI2CDevices(_i2cBuses, _busMap, _powerIsOn, _objServer);
7943cbff97fSChristopher Meis     }
7953cbff97fSChristopher Meis 
7963cbff97fSChristopher Meis     const std::vector<fs::path>& _i2cBuses;
7973cbff97fSChristopher Meis     BusMap& _busMap;
7983cbff97fSChristopher Meis     const bool& _powerIsOn;
7993cbff97fSChristopher Meis     sdbusplus::asio::object_server& _objServer;
8003cbff97fSChristopher Meis     std::function<void()> _callback;
8013cbff97fSChristopher Meis };
8023cbff97fSChristopher Meis 
addFruObjectToDbus(std::vector<uint8_t> & device,boost::container::flat_map<std::pair<size_t,size_t>,std::shared_ptr<sdbusplus::asio::dbus_interface>> & dbusInterfaceMap,uint32_t bus,uint32_t address,size_t & unknownBusObjectCount,const bool & powerIsOn,sdbusplus::asio::object_server & objServer,std::shared_ptr<sdbusplus::asio::connection> & systemBus)8033cbff97fSChristopher Meis void addFruObjectToDbus(
8043cbff97fSChristopher Meis     std::vector<uint8_t>& device,
8053cbff97fSChristopher Meis     boost::container::flat_map<
8063cbff97fSChristopher Meis         std::pair<size_t, size_t>,
8073cbff97fSChristopher Meis         std::shared_ptr<sdbusplus::asio::dbus_interface>>& dbusInterfaceMap,
8083cbff97fSChristopher Meis     uint32_t bus, uint32_t address, size_t& unknownBusObjectCount,
8093cbff97fSChristopher Meis     const bool& powerIsOn, sdbusplus::asio::object_server& objServer,
8103cbff97fSChristopher Meis     std::shared_ptr<sdbusplus::asio::connection>& systemBus)
8113cbff97fSChristopher Meis {
8123cbff97fSChristopher Meis     boost::container::flat_map<std::string, std::string> formattedFRU;
8133cbff97fSChristopher Meis 
8143cbff97fSChristopher Meis     std::optional<std::string> optionalProductName = getProductName(
8153cbff97fSChristopher Meis         device, formattedFRU, bus, address, unknownBusObjectCount);
8163cbff97fSChristopher Meis     if (!optionalProductName)
8173cbff97fSChristopher Meis     {
8183cbff97fSChristopher Meis         std::cerr << "getProductName failed. product name is empty.\n";
8193cbff97fSChristopher Meis         return;
8203cbff97fSChristopher Meis     }
8213cbff97fSChristopher Meis 
8223cbff97fSChristopher Meis     std::string productName =
8233cbff97fSChristopher Meis         "/xyz/openbmc_project/FruDevice/" + optionalProductName.value();
8243cbff97fSChristopher Meis 
8253cbff97fSChristopher Meis     std::optional<int> index = findIndexForFRU(dbusInterfaceMap, productName);
8263cbff97fSChristopher Meis     if (index.has_value())
8273cbff97fSChristopher Meis     {
8283cbff97fSChristopher Meis         productName += "_";
8293cbff97fSChristopher Meis         productName += std::to_string(++(*index));
8303cbff97fSChristopher Meis     }
8313cbff97fSChristopher Meis 
8323cbff97fSChristopher Meis     std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
8333cbff97fSChristopher Meis         objServer.add_interface(productName, "xyz.openbmc_project.FruDevice");
8343cbff97fSChristopher Meis     dbusInterfaceMap[std::pair<size_t, size_t>(bus, address)] = iface;
8353cbff97fSChristopher Meis 
8363cbff97fSChristopher Meis     for (auto& property : formattedFRU)
8373cbff97fSChristopher Meis     {
8383cbff97fSChristopher Meis         std::regex_replace(property.second.begin(), property.second.begin(),
8393cbff97fSChristopher Meis                            property.second.end(), nonAsciiRegex, "_");
8403cbff97fSChristopher Meis         if (property.second.empty() && property.first != "PRODUCT_ASSET_TAG")
8413cbff97fSChristopher Meis         {
8423cbff97fSChristopher Meis             continue;
8433cbff97fSChristopher Meis         }
8443cbff97fSChristopher Meis         std::string key =
8453cbff97fSChristopher Meis             std::regex_replace(property.first, nonAsciiRegex, "_");
8463cbff97fSChristopher Meis 
8473cbff97fSChristopher Meis         if (property.first == "PRODUCT_ASSET_TAG")
8483cbff97fSChristopher Meis         {
8493cbff97fSChristopher Meis             std::string propertyName = property.first;
8503cbff97fSChristopher Meis             iface->register_property(
8513cbff97fSChristopher Meis                 key, property.second + '\0',
8523cbff97fSChristopher Meis                 [bus, address, propertyName, &dbusInterfaceMap,
8533cbff97fSChristopher Meis                  &unknownBusObjectCount, &powerIsOn, &objServer,
8543cbff97fSChristopher Meis                  &systemBus](const std::string& req, std::string& resp) {
8553cbff97fSChristopher Meis                     if (strcmp(req.c_str(), resp.c_str()) != 0)
8563cbff97fSChristopher Meis                     {
8573cbff97fSChristopher Meis                         // call the method which will update
8583cbff97fSChristopher Meis                         if (updateFRUProperty(req, bus, address, propertyName,
8593cbff97fSChristopher Meis                                               dbusInterfaceMap,
8603cbff97fSChristopher Meis                                               unknownBusObjectCount, powerIsOn,
8613cbff97fSChristopher Meis                                               objServer, systemBus))
8623cbff97fSChristopher Meis                         {
8633cbff97fSChristopher Meis                             resp = req;
8643cbff97fSChristopher Meis                         }
8653cbff97fSChristopher Meis                         else
8663cbff97fSChristopher Meis                         {
8673cbff97fSChristopher Meis                             throw std::invalid_argument(
8683cbff97fSChristopher Meis                                 "FRU property update failed.");
8693cbff97fSChristopher Meis                         }
8703cbff97fSChristopher Meis                     }
8713cbff97fSChristopher Meis                     return 1;
8723cbff97fSChristopher Meis                 });
8733cbff97fSChristopher Meis         }
8743cbff97fSChristopher Meis         else if (!iface->register_property(key, property.second + '\0'))
8753cbff97fSChristopher Meis         {
8763cbff97fSChristopher Meis             std::cerr << "illegal key: " << key << "\n";
8773cbff97fSChristopher Meis         }
8783cbff97fSChristopher Meis         lg2::debug("parsed FRU property: {FIRST}: {SECOND}", "FIRST",
8793cbff97fSChristopher Meis                    property.first, "SECOND", property.second);
8803cbff97fSChristopher Meis     }
8813cbff97fSChristopher Meis 
8823cbff97fSChristopher Meis     // baseboard will be 0, 0
8833cbff97fSChristopher Meis     iface->register_property("BUS", bus);
8843cbff97fSChristopher Meis     iface->register_property("ADDRESS", address);
8853cbff97fSChristopher Meis 
8863cbff97fSChristopher Meis     iface->initialize();
8873cbff97fSChristopher Meis }
8883cbff97fSChristopher Meis 
readBaseboardFRU(std::vector<uint8_t> & baseboardFRU)8893cbff97fSChristopher Meis static bool readBaseboardFRU(std::vector<uint8_t>& baseboardFRU)
8903cbff97fSChristopher Meis {
8913cbff97fSChristopher Meis     // try to read baseboard fru from file
8923cbff97fSChristopher Meis     std::ifstream baseboardFRUFile(baseboardFruLocation, std::ios::binary);
8933cbff97fSChristopher Meis     if (baseboardFRUFile.good())
8943cbff97fSChristopher Meis     {
8953cbff97fSChristopher Meis         baseboardFRUFile.seekg(0, std::ios_base::end);
8963cbff97fSChristopher Meis         size_t fileSize = static_cast<size_t>(baseboardFRUFile.tellg());
8973cbff97fSChristopher Meis         baseboardFRU.resize(fileSize);
8983cbff97fSChristopher Meis         baseboardFRUFile.seekg(0, std::ios_base::beg);
8993cbff97fSChristopher Meis         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
9003cbff97fSChristopher Meis         char* charOffset = reinterpret_cast<char*>(baseboardFRU.data());
9013cbff97fSChristopher Meis         baseboardFRUFile.read(charOffset, fileSize);
9023cbff97fSChristopher Meis     }
9033cbff97fSChristopher Meis     else
9043cbff97fSChristopher Meis     {
9053cbff97fSChristopher Meis         return false;
9063cbff97fSChristopher Meis     }
9073cbff97fSChristopher Meis     return true;
9083cbff97fSChristopher Meis }
9093cbff97fSChristopher Meis 
writeFRU(uint8_t bus,uint8_t address,const std::vector<uint8_t> & fru)9103cbff97fSChristopher Meis bool writeFRU(uint8_t bus, uint8_t address, const std::vector<uint8_t>& fru)
9113cbff97fSChristopher Meis {
9123cbff97fSChristopher Meis     boost::container::flat_map<std::string, std::string> tmp;
9133cbff97fSChristopher Meis     if (fru.size() > maxFruSize)
9143cbff97fSChristopher Meis     {
9153cbff97fSChristopher Meis         std::cerr << "Invalid fru.size() during writeFRU\n";
9163cbff97fSChristopher Meis         return false;
9173cbff97fSChristopher Meis     }
9183cbff97fSChristopher Meis     // verify legal fru by running it through fru parsing logic
9193cbff97fSChristopher Meis     if (formatIPMIFRU(fru, tmp) != resCodes::resOK)
9203cbff97fSChristopher Meis     {
9213cbff97fSChristopher Meis         std::cerr << "Invalid fru format during writeFRU\n";
9223cbff97fSChristopher Meis         return false;
9233cbff97fSChristopher Meis     }
9243cbff97fSChristopher Meis     // baseboard fru
9253cbff97fSChristopher Meis     if (bus == 0 && address == 0)
9263cbff97fSChristopher Meis     {
9273cbff97fSChristopher Meis         std::ofstream file(baseboardFruLocation, std::ios_base::binary);
9283cbff97fSChristopher Meis         if (!file.good())
9293cbff97fSChristopher Meis         {
9303cbff97fSChristopher Meis             std::cerr << "Error opening file " << baseboardFruLocation << "\n";
9313cbff97fSChristopher Meis             throw DBusInternalError();
9323cbff97fSChristopher Meis             return false;
9333cbff97fSChristopher Meis         }
9343cbff97fSChristopher Meis         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
9353cbff97fSChristopher Meis         const char* charOffset = reinterpret_cast<const char*>(fru.data());
9363cbff97fSChristopher Meis         file.write(charOffset, fru.size());
9373cbff97fSChristopher Meis         return file.good();
9383cbff97fSChristopher Meis     }
9393cbff97fSChristopher Meis 
9403cbff97fSChristopher Meis     if (hasEepromFile(bus, address))
9413cbff97fSChristopher Meis     {
9423cbff97fSChristopher Meis         auto path = getEepromPath(bus, address);
9433cbff97fSChristopher Meis         off_t offset = 0;
9443cbff97fSChristopher Meis 
9453cbff97fSChristopher Meis         int eeprom = open(path.c_str(), O_RDWR | O_CLOEXEC);
9463cbff97fSChristopher Meis         if (eeprom < 0)
9473cbff97fSChristopher Meis         {
9483cbff97fSChristopher Meis             std::cerr << "unable to open i2c device " << path << "\n";
9493cbff97fSChristopher Meis             throw DBusInternalError();
9503cbff97fSChristopher Meis             return false;
9513cbff97fSChristopher Meis         }
9523cbff97fSChristopher Meis 
9533cbff97fSChristopher Meis         std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{};
9543cbff97fSChristopher Meis         std::string errorMessage = "eeprom at " + std::to_string(bus) +
9553cbff97fSChristopher Meis                                    " address " + std::to_string(address);
9563cbff97fSChristopher Meis         auto readFunc = [eeprom](off_t offset, size_t length, uint8_t* outbuf) {
9573cbff97fSChristopher Meis             return readFromEeprom(eeprom, offset, length, outbuf);
9583cbff97fSChristopher Meis         };
9593cbff97fSChristopher Meis         FRUReader reader(std::move(readFunc));
9603cbff97fSChristopher Meis 
9613cbff97fSChristopher Meis         if (!findFRUHeader(reader, errorMessage, blockData, offset))
9623cbff97fSChristopher Meis         {
9633cbff97fSChristopher Meis             offset = 0;
9643cbff97fSChristopher Meis         }
9653cbff97fSChristopher Meis 
9663cbff97fSChristopher Meis         if (lseek(eeprom, offset, SEEK_SET) < 0)
9673cbff97fSChristopher Meis         {
9683cbff97fSChristopher Meis             std::cerr << "Unable to seek to offset " << offset
9693cbff97fSChristopher Meis                       << " in device: " << path << "\n";
9703cbff97fSChristopher Meis             close(eeprom);
9713cbff97fSChristopher Meis             throw DBusInternalError();
9723cbff97fSChristopher Meis         }
9733cbff97fSChristopher Meis 
9743cbff97fSChristopher Meis         ssize_t writtenBytes = write(eeprom, fru.data(), fru.size());
9753cbff97fSChristopher Meis         if (writtenBytes < 0)
9763cbff97fSChristopher Meis         {
9773cbff97fSChristopher Meis             std::cerr << "unable to write to i2c device " << path << "\n";
9783cbff97fSChristopher Meis             close(eeprom);
9793cbff97fSChristopher Meis             throw DBusInternalError();
9803cbff97fSChristopher Meis             return false;
9813cbff97fSChristopher Meis         }
9823cbff97fSChristopher Meis 
9833cbff97fSChristopher Meis         close(eeprom);
9843cbff97fSChristopher Meis         return true;
9853cbff97fSChristopher Meis     }
9863cbff97fSChristopher Meis 
9873cbff97fSChristopher Meis     std::string i2cBus = "/dev/i2c-" + std::to_string(bus);
9883cbff97fSChristopher Meis 
9893cbff97fSChristopher Meis     int file = open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
9903cbff97fSChristopher Meis     if (file < 0)
9913cbff97fSChristopher Meis     {
9923cbff97fSChristopher Meis         std::cerr << "unable to open i2c device " << i2cBus << "\n";
9933cbff97fSChristopher Meis         throw DBusInternalError();
9943cbff97fSChristopher Meis         return false;
9953cbff97fSChristopher Meis     }
9963cbff97fSChristopher Meis     if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
9973cbff97fSChristopher Meis     {
9983cbff97fSChristopher Meis         std::cerr << "unable to set device address\n";
9993cbff97fSChristopher Meis         close(file);
10003cbff97fSChristopher Meis         throw DBusInternalError();
10013cbff97fSChristopher Meis         return false;
10023cbff97fSChristopher Meis     }
10033cbff97fSChristopher Meis 
10043cbff97fSChristopher Meis     constexpr const size_t retryMax = 2;
10053cbff97fSChristopher Meis     uint16_t index = 0;
10063cbff97fSChristopher Meis     size_t retries = retryMax;
10073cbff97fSChristopher Meis     while (index < fru.size())
10083cbff97fSChristopher Meis     {
10093cbff97fSChristopher Meis         if (((index != 0U) && ((index % (maxEepromPageIndex + 1)) == 0)) &&
10103cbff97fSChristopher Meis             (retries == retryMax))
10113cbff97fSChristopher Meis         {
10123cbff97fSChristopher Meis             // The 4K EEPROM only uses the A2 and A1 device address bits
10133cbff97fSChristopher Meis             // with the third bit being a memory page address bit.
10143cbff97fSChristopher Meis             if (ioctl(file, I2C_SLAVE_FORCE, ++address) < 0)
10153cbff97fSChristopher Meis             {
10163cbff97fSChristopher Meis                 std::cerr << "unable to set device address\n";
10173cbff97fSChristopher Meis                 close(file);
10183cbff97fSChristopher Meis                 throw DBusInternalError();
10193cbff97fSChristopher Meis                 return false;
10203cbff97fSChristopher Meis             }
10213cbff97fSChristopher Meis         }
10223cbff97fSChristopher Meis 
10233cbff97fSChristopher Meis         if (i2c_smbus_write_byte_data(file, static_cast<uint8_t>(index),
10243cbff97fSChristopher Meis                                       fru[index]) < 0)
10253cbff97fSChristopher Meis         {
10263cbff97fSChristopher Meis             if ((retries--) == 0U)
10273cbff97fSChristopher Meis             {
10283cbff97fSChristopher Meis                 std::cerr << "error writing fru: " << strerror(errno) << "\n";
10293cbff97fSChristopher Meis                 close(file);
10303cbff97fSChristopher Meis                 throw DBusInternalError();
10313cbff97fSChristopher Meis                 return false;
10323cbff97fSChristopher Meis             }
10333cbff97fSChristopher Meis         }
10343cbff97fSChristopher Meis         else
10353cbff97fSChristopher Meis         {
10363cbff97fSChristopher Meis             retries = retryMax;
10373cbff97fSChristopher Meis             index++;
10383cbff97fSChristopher Meis         }
10393cbff97fSChristopher Meis         // most eeproms require 5-10ms between writes
10403cbff97fSChristopher Meis         std::this_thread::sleep_for(std::chrono::milliseconds(10));
10413cbff97fSChristopher Meis     }
10423cbff97fSChristopher Meis     close(file);
10433cbff97fSChristopher Meis     return true;
10443cbff97fSChristopher Meis }
10453cbff97fSChristopher Meis 
rescanOneBus(BusMap & busmap,uint16_t busNum,boost::container::flat_map<std::pair<size_t,size_t>,std::shared_ptr<sdbusplus::asio::dbus_interface>> & dbusInterfaceMap,bool dbusCall,size_t & unknownBusObjectCount,const bool & powerIsOn,sdbusplus::asio::object_server & objServer,std::shared_ptr<sdbusplus::asio::connection> & systemBus)10463cbff97fSChristopher Meis void rescanOneBus(
10473cbff97fSChristopher Meis     BusMap& busmap, uint16_t busNum,
10483cbff97fSChristopher Meis     boost::container::flat_map<
10493cbff97fSChristopher Meis         std::pair<size_t, size_t>,
10503cbff97fSChristopher Meis         std::shared_ptr<sdbusplus::asio::dbus_interface>>& dbusInterfaceMap,
10513cbff97fSChristopher Meis     bool dbusCall, size_t& unknownBusObjectCount, const bool& powerIsOn,
10523cbff97fSChristopher Meis     sdbusplus::asio::object_server& objServer,
10533cbff97fSChristopher Meis     std::shared_ptr<sdbusplus::asio::connection>& systemBus)
10543cbff97fSChristopher Meis {
10553cbff97fSChristopher Meis     for (auto device = foundDevices.begin(); device != foundDevices.end();)
10563cbff97fSChristopher Meis     {
10573cbff97fSChristopher Meis         if (device->first.first == static_cast<size_t>(busNum))
10583cbff97fSChristopher Meis         {
10593cbff97fSChristopher Meis             objServer.remove_interface(device->second);
10603cbff97fSChristopher Meis             device = foundDevices.erase(device);
10613cbff97fSChristopher Meis         }
10623cbff97fSChristopher Meis         else
10633cbff97fSChristopher Meis         {
10643cbff97fSChristopher Meis             device++;
10653cbff97fSChristopher Meis         }
10663cbff97fSChristopher Meis     }
10673cbff97fSChristopher Meis 
10683cbff97fSChristopher Meis     fs::path busPath = fs::path("/dev/i2c-" + std::to_string(busNum));
10693cbff97fSChristopher Meis     if (!fs::exists(busPath))
10703cbff97fSChristopher Meis     {
10713cbff97fSChristopher Meis         if (dbusCall)
10723cbff97fSChristopher Meis         {
10733cbff97fSChristopher Meis             std::cerr << "Unable to access i2c bus " << static_cast<int>(busNum)
10743cbff97fSChristopher Meis                       << "\n";
10753cbff97fSChristopher Meis             throw std::invalid_argument("Invalid Bus.");
10763cbff97fSChristopher Meis         }
10773cbff97fSChristopher Meis         return;
10783cbff97fSChristopher Meis     }
10793cbff97fSChristopher Meis 
10803cbff97fSChristopher Meis     std::vector<fs::path> i2cBuses;
10813cbff97fSChristopher Meis     i2cBuses.emplace_back(busPath);
10823cbff97fSChristopher Meis 
10833cbff97fSChristopher Meis     auto scan = std::make_shared<FindDevicesWithCallback>(
10843cbff97fSChristopher Meis         i2cBuses, busmap, powerIsOn, objServer,
10853cbff97fSChristopher Meis         [busNum, &busmap, &dbusInterfaceMap, &unknownBusObjectCount, &powerIsOn,
10863cbff97fSChristopher Meis          &objServer, &systemBus]() {
10873cbff97fSChristopher Meis             for (auto busIface = dbusInterfaceMap.begin();
10883cbff97fSChristopher Meis                  busIface != dbusInterfaceMap.end();)
10893cbff97fSChristopher Meis             {
10903cbff97fSChristopher Meis                 if (busIface->first.first == static_cast<size_t>(busNum))
10913cbff97fSChristopher Meis                 {
10923cbff97fSChristopher Meis                     objServer.remove_interface(busIface->second);
10933cbff97fSChristopher Meis                     busIface = dbusInterfaceMap.erase(busIface);
10943cbff97fSChristopher Meis                 }
10953cbff97fSChristopher Meis                 else
10963cbff97fSChristopher Meis                 {
10973cbff97fSChristopher Meis                     busIface++;
10983cbff97fSChristopher Meis                 }
10993cbff97fSChristopher Meis             }
11003cbff97fSChristopher Meis             auto found = busmap.find(busNum);
11013cbff97fSChristopher Meis             if (found == busmap.end() || found->second == nullptr)
11023cbff97fSChristopher Meis             {
11033cbff97fSChristopher Meis                 return;
11043cbff97fSChristopher Meis             }
11053cbff97fSChristopher Meis             for (auto& device : *(found->second))
11063cbff97fSChristopher Meis             {
11073cbff97fSChristopher Meis                 addFruObjectToDbus(device.second, dbusInterfaceMap,
11083cbff97fSChristopher Meis                                    static_cast<uint32_t>(busNum), device.first,
11093cbff97fSChristopher Meis                                    unknownBusObjectCount, powerIsOn, objServer,
11103cbff97fSChristopher Meis                                    systemBus);
11113cbff97fSChristopher Meis             }
11123cbff97fSChristopher Meis         });
11133cbff97fSChristopher Meis     scan->run();
11143cbff97fSChristopher Meis }
11153cbff97fSChristopher Meis 
rescanBusses(BusMap & busmap,boost::container::flat_map<std::pair<size_t,size_t>,std::shared_ptr<sdbusplus::asio::dbus_interface>> & dbusInterfaceMap,size_t & unknownBusObjectCount,const bool & powerIsOn,sdbusplus::asio::object_server & objServer,std::shared_ptr<sdbusplus::asio::connection> & systemBus)11163cbff97fSChristopher Meis void rescanBusses(
11173cbff97fSChristopher Meis     BusMap& busmap,
11183cbff97fSChristopher Meis     boost::container::flat_map<
11193cbff97fSChristopher Meis         std::pair<size_t, size_t>,
11203cbff97fSChristopher Meis         std::shared_ptr<sdbusplus::asio::dbus_interface>>& dbusInterfaceMap,
11213cbff97fSChristopher Meis     size_t& unknownBusObjectCount, const bool& powerIsOn,
11223cbff97fSChristopher Meis     sdbusplus::asio::object_server& objServer,
11233cbff97fSChristopher Meis     std::shared_ptr<sdbusplus::asio::connection>& systemBus)
11243cbff97fSChristopher Meis {
11253cbff97fSChristopher Meis     static boost::asio::steady_timer timer(io);
11263cbff97fSChristopher Meis     timer.expires_after(std::chrono::seconds(1));
11273cbff97fSChristopher Meis 
11283cbff97fSChristopher Meis     // setup an async wait in case we get flooded with requests
11293cbff97fSChristopher Meis     timer.async_wait([&](const boost::system::error_code& ec) {
11303cbff97fSChristopher Meis         if (ec == boost::asio::error::operation_aborted)
11313cbff97fSChristopher Meis         {
11323cbff97fSChristopher Meis             return;
11333cbff97fSChristopher Meis         }
11343cbff97fSChristopher Meis 
11353cbff97fSChristopher Meis         if (ec)
11363cbff97fSChristopher Meis         {
11373cbff97fSChristopher Meis             std::cerr << "Error in timer: " << ec.message() << "\n";
11383cbff97fSChristopher Meis             return;
11393cbff97fSChristopher Meis         }
11403cbff97fSChristopher Meis 
11413cbff97fSChristopher Meis         auto devDir = fs::path("/dev/");
11423cbff97fSChristopher Meis         std::vector<fs::path> i2cBuses;
11433cbff97fSChristopher Meis 
11443cbff97fSChristopher Meis         boost::container::flat_map<size_t, fs::path> busPaths;
11453cbff97fSChristopher Meis         if (!getI2cDevicePaths(devDir, busPaths))
11463cbff97fSChristopher Meis         {
11473cbff97fSChristopher Meis             std::cerr << "unable to find i2c devices\n";
11483cbff97fSChristopher Meis             return;
11493cbff97fSChristopher Meis         }
11503cbff97fSChristopher Meis 
11513cbff97fSChristopher Meis         for (const auto& busPath : busPaths)
11523cbff97fSChristopher Meis         {
11533cbff97fSChristopher Meis             i2cBuses.emplace_back(busPath.second);
11543cbff97fSChristopher Meis         }
11553cbff97fSChristopher Meis 
11563cbff97fSChristopher Meis         busmap.clear();
11573cbff97fSChristopher Meis         for (auto& [pair, interface] : foundDevices)
11583cbff97fSChristopher Meis         {
11593cbff97fSChristopher Meis             objServer.remove_interface(interface);
11603cbff97fSChristopher Meis         }
11613cbff97fSChristopher Meis         foundDevices.clear();
11623cbff97fSChristopher Meis 
11633cbff97fSChristopher Meis         auto scan = std::make_shared<FindDevicesWithCallback>(
11643cbff97fSChristopher Meis             i2cBuses, busmap, powerIsOn, objServer, [&]() {
11653cbff97fSChristopher Meis                 for (auto& busIface : dbusInterfaceMap)
11663cbff97fSChristopher Meis                 {
11673cbff97fSChristopher Meis                     objServer.remove_interface(busIface.second);
11683cbff97fSChristopher Meis                 }
11693cbff97fSChristopher Meis 
11703cbff97fSChristopher Meis                 dbusInterfaceMap.clear();
11713cbff97fSChristopher Meis                 unknownBusObjectCount = 0;
11723cbff97fSChristopher Meis 
11733cbff97fSChristopher Meis                 // todo, get this from a more sensable place
11743cbff97fSChristopher Meis                 std::vector<uint8_t> baseboardFRU;
11753cbff97fSChristopher Meis                 if (readBaseboardFRU(baseboardFRU))
11763cbff97fSChristopher Meis                 {
11773cbff97fSChristopher Meis                     // If no device on i2c bus 0, the insertion will happen.
11783cbff97fSChristopher Meis                     auto bus0 =
11793cbff97fSChristopher Meis                         busmap.try_emplace(0, std::make_shared<DeviceMap>());
11803cbff97fSChristopher Meis                     bus0.first->second->emplace(0, baseboardFRU);
11813cbff97fSChristopher Meis                 }
11823cbff97fSChristopher Meis                 for (auto& devicemap : busmap)
11833cbff97fSChristopher Meis                 {
11843cbff97fSChristopher Meis                     for (auto& device : *devicemap.second)
11853cbff97fSChristopher Meis                     {
11863cbff97fSChristopher Meis                         addFruObjectToDbus(device.second, dbusInterfaceMap,
11873cbff97fSChristopher Meis                                            devicemap.first, device.first,
11883cbff97fSChristopher Meis                                            unknownBusObjectCount, powerIsOn,
11893cbff97fSChristopher Meis                                            objServer, systemBus);
11903cbff97fSChristopher Meis                     }
11913cbff97fSChristopher Meis                 }
11923cbff97fSChristopher Meis             });
11933cbff97fSChristopher Meis         scan->run();
11943cbff97fSChristopher Meis     });
11953cbff97fSChristopher Meis }
11963cbff97fSChristopher Meis 
11973cbff97fSChristopher Meis // Details with example of Asset Tag Update
11983cbff97fSChristopher Meis // To find location of Product Info Area asset tag as per FRU specification
11993cbff97fSChristopher Meis // 1. Find product Info area starting offset (*8 - as header will be in
12003cbff97fSChristopher Meis // multiple of 8 bytes).
12013cbff97fSChristopher Meis // 2. Skip 3 bytes of product info area (like format version, area length,
12023cbff97fSChristopher Meis // and language code).
12033cbff97fSChristopher Meis // 3. Traverse manufacturer name, product name, product version, & product
12043cbff97fSChristopher Meis // serial number, by reading type/length code to reach the Asset Tag.
12053cbff97fSChristopher Meis // 4. Update the Asset Tag, reposition the product Info area in multiple of
12063cbff97fSChristopher Meis // 8 bytes. Update the Product area length and checksum.
12073cbff97fSChristopher Meis 
updateFRUProperty(const std::string & updatePropertyReq,uint32_t bus,uint32_t address,const std::string & propertyName,boost::container::flat_map<std::pair<size_t,size_t>,std::shared_ptr<sdbusplus::asio::dbus_interface>> & dbusInterfaceMap,size_t & unknownBusObjectCount,const bool & powerIsOn,sdbusplus::asio::object_server & objServer,std::shared_ptr<sdbusplus::asio::connection> & systemBus)12083cbff97fSChristopher Meis bool updateFRUProperty(
12093cbff97fSChristopher Meis     const std::string& updatePropertyReq, uint32_t bus, uint32_t address,
12103cbff97fSChristopher Meis     const std::string& propertyName,
12113cbff97fSChristopher Meis     boost::container::flat_map<
12123cbff97fSChristopher Meis         std::pair<size_t, size_t>,
12133cbff97fSChristopher Meis         std::shared_ptr<sdbusplus::asio::dbus_interface>>& dbusInterfaceMap,
12143cbff97fSChristopher Meis     size_t& unknownBusObjectCount, const bool& powerIsOn,
12153cbff97fSChristopher Meis     sdbusplus::asio::object_server& objServer,
12163cbff97fSChristopher Meis     std::shared_ptr<sdbusplus::asio::connection>& systemBus)
12173cbff97fSChristopher Meis {
12183cbff97fSChristopher Meis     size_t updatePropertyReqLen = updatePropertyReq.length();
12193cbff97fSChristopher Meis     if (updatePropertyReqLen == 1 || updatePropertyReqLen > 63)
12203cbff97fSChristopher Meis     {
12213cbff97fSChristopher Meis         std::cerr
12223cbff97fSChristopher Meis             << "FRU field data cannot be of 1 char or more than 63 chars. "
12233cbff97fSChristopher Meis                "Invalid Length "
12243cbff97fSChristopher Meis             << updatePropertyReqLen << "\n";
12253cbff97fSChristopher Meis         return false;
12263cbff97fSChristopher Meis     }
12273cbff97fSChristopher Meis 
12283cbff97fSChristopher Meis     std::vector<uint8_t> fruData;
12293cbff97fSChristopher Meis 
12303cbff97fSChristopher Meis     if (!getFruData(fruData, bus, address))
12313cbff97fSChristopher Meis     {
12323cbff97fSChristopher Meis         std::cerr << "Failure getting FRU Data \n";
12333cbff97fSChristopher Meis         return false;
12343cbff97fSChristopher Meis     }
12353cbff97fSChristopher Meis 
12363cbff97fSChristopher Meis     struct FruArea fruAreaParams{};
12373cbff97fSChristopher Meis 
12383cbff97fSChristopher Meis     if (!findFruAreaLocationAndField(fruData, propertyName, fruAreaParams))
12393cbff97fSChristopher Meis     {
12403cbff97fSChristopher Meis         std::cerr << "findFruAreaLocationAndField failed \n";
12413cbff97fSChristopher Meis         return false;
12423cbff97fSChristopher Meis     }
12433cbff97fSChristopher Meis 
12443cbff97fSChristopher Meis     std::vector<uint8_t> restFRUAreaFieldsData;
12453cbff97fSChristopher Meis     if (!copyRestFRUArea(fruData, propertyName, fruAreaParams,
12463cbff97fSChristopher Meis                          restFRUAreaFieldsData))
12473cbff97fSChristopher Meis     {
12483cbff97fSChristopher Meis         std::cerr << "copyRestFRUArea failed \n";
12493cbff97fSChristopher Meis         return false;
12503cbff97fSChristopher Meis     }
12513cbff97fSChristopher Meis 
12523cbff97fSChristopher Meis     // Push post update fru areas if any
12533cbff97fSChristopher Meis     unsigned int nextFRUAreaLoc = 0;
12543cbff97fSChristopher Meis     for (fruAreas nextFRUArea = fruAreas::fruAreaInternal;
12553cbff97fSChristopher Meis          nextFRUArea <= fruAreas::fruAreaMultirecord; ++nextFRUArea)
12563cbff97fSChristopher Meis     {
12573cbff97fSChristopher Meis         unsigned int fruAreaLoc =
12583cbff97fSChristopher Meis             fruData[getHeaderAreaFieldOffset(nextFRUArea)] * fruBlockSize;
12593cbff97fSChristopher Meis         if ((fruAreaLoc > fruAreaParams.restFieldsEnd) &&
12603cbff97fSChristopher Meis             ((nextFRUAreaLoc == 0) || (fruAreaLoc < nextFRUAreaLoc)))
12613cbff97fSChristopher Meis         {
12623cbff97fSChristopher Meis             nextFRUAreaLoc = fruAreaLoc;
12633cbff97fSChristopher Meis         }
12643cbff97fSChristopher Meis     }
12653cbff97fSChristopher Meis     std::vector<uint8_t> restFRUAreasData;
12663cbff97fSChristopher Meis     if (nextFRUAreaLoc != 0U)
12673cbff97fSChristopher Meis     {
12683cbff97fSChristopher Meis         std::copy_n(fruData.begin() + nextFRUAreaLoc,
12693cbff97fSChristopher Meis                     fruData.size() - nextFRUAreaLoc,
12703cbff97fSChristopher Meis                     std::back_inserter(restFRUAreasData));
12713cbff97fSChristopher Meis     }
12723cbff97fSChristopher Meis 
12733cbff97fSChristopher Meis     // check FRU area size
12743cbff97fSChristopher Meis     size_t fruAreaDataSize =
12753cbff97fSChristopher Meis         ((fruAreaParams.updateFieldLoc - fruAreaParams.start + 1) +
12763cbff97fSChristopher Meis          restFRUAreaFieldsData.size());
12773cbff97fSChristopher Meis     size_t fruAreaAvailableSize = fruAreaParams.size - fruAreaDataSize;
12783cbff97fSChristopher Meis     if ((updatePropertyReqLen + 1) > fruAreaAvailableSize)
12793cbff97fSChristopher Meis     {
12803cbff97fSChristopher Meis #ifdef ENABLE_FRU_AREA_RESIZE
12813cbff97fSChristopher Meis         size_t newFRUAreaSize = fruAreaDataSize + updatePropertyReqLen + 1;
12823cbff97fSChristopher Meis         // round size to 8-byte blocks
12833cbff97fSChristopher Meis         newFRUAreaSize =
12843cbff97fSChristopher Meis             ((newFRUAreaSize - 1) / fruBlockSize + 1) * fruBlockSize;
12853cbff97fSChristopher Meis         size_t newFRUDataSize =
12863cbff97fSChristopher Meis             fruData.size() + newFRUAreaSize - fruAreaParams.size;
12873cbff97fSChristopher Meis         fruData.resize(newFRUDataSize);
12883cbff97fSChristopher Meis         fruAreaParams.size = newFRUAreaSize;
12893cbff97fSChristopher Meis         fruAreaParams.end = fruAreaParams.start + fruAreaParams.size;
12903cbff97fSChristopher Meis #else
12913cbff97fSChristopher Meis         std::cerr << "FRU field length: " << updatePropertyReqLen + 1
12923cbff97fSChristopher Meis                   << " should not be greater than available FRU area size: "
12933cbff97fSChristopher Meis                   << fruAreaAvailableSize << "\n";
12943cbff97fSChristopher Meis         return false;
12953cbff97fSChristopher Meis #endif // ENABLE_FRU_AREA_RESIZE
12963cbff97fSChristopher Meis     }
12973cbff97fSChristopher Meis 
12983cbff97fSChristopher Meis     // write new requested property field length and data
12993cbff97fSChristopher Meis     constexpr uint8_t newTypeLenMask = 0xC0;
13003cbff97fSChristopher Meis     fruData[fruAreaParams.updateFieldLoc] =
13013cbff97fSChristopher Meis         static_cast<uint8_t>(updatePropertyReqLen | newTypeLenMask);
13023cbff97fSChristopher Meis     fruAreaParams.updateFieldLoc++;
13033cbff97fSChristopher Meis     std::copy(updatePropertyReq.begin(), updatePropertyReq.end(),
13043cbff97fSChristopher Meis               fruData.begin() + fruAreaParams.updateFieldLoc);
13053cbff97fSChristopher Meis 
13063cbff97fSChristopher Meis     // Copy remaining data to main fru area - post updated fru field vector
13073cbff97fSChristopher Meis     fruAreaParams.restFieldsLoc =
13083cbff97fSChristopher Meis         fruAreaParams.updateFieldLoc + updatePropertyReqLen;
13093cbff97fSChristopher Meis     size_t fruAreaDataEnd =
13103cbff97fSChristopher Meis         fruAreaParams.restFieldsLoc + restFRUAreaFieldsData.size();
13113cbff97fSChristopher Meis 
13123cbff97fSChristopher Meis     std::copy(restFRUAreaFieldsData.begin(), restFRUAreaFieldsData.end(),
13133cbff97fSChristopher Meis               fruData.begin() + fruAreaParams.restFieldsLoc);
13143cbff97fSChristopher Meis 
13153cbff97fSChristopher Meis     // Update final fru with new fru area length and checksum
13163cbff97fSChristopher Meis     unsigned int nextFRUAreaNewLoc = updateFRUAreaLenAndChecksum(
13173cbff97fSChristopher Meis         fruData, fruAreaParams.start, fruAreaDataEnd, fruAreaParams.end);
13183cbff97fSChristopher Meis 
13193cbff97fSChristopher Meis #ifdef ENABLE_FRU_AREA_RESIZE
13203cbff97fSChristopher Meis     ++nextFRUAreaNewLoc;
13213cbff97fSChristopher Meis     ssize_t nextFRUAreaOffsetDiff =
13223cbff97fSChristopher Meis         (nextFRUAreaNewLoc - nextFRUAreaLoc) / fruBlockSize;
13233cbff97fSChristopher Meis     // Append rest FRU Areas if size changed and there were other sections after
13243cbff97fSChristopher Meis     // updated one
13253cbff97fSChristopher Meis     if (nextFRUAreaOffsetDiff && nextFRUAreaLoc)
13263cbff97fSChristopher Meis     {
13273cbff97fSChristopher Meis         std::copy(restFRUAreasData.begin(), restFRUAreasData.end(),
13283cbff97fSChristopher Meis                   fruData.begin() + nextFRUAreaNewLoc);
13293cbff97fSChristopher Meis         // Update Common Header
13303cbff97fSChristopher Meis         for (fruAreas nextFRUArea = fruAreas::fruAreaInternal;
13313cbff97fSChristopher Meis              nextFRUArea <= fruAreas::fruAreaMultirecord; ++nextFRUArea)
13323cbff97fSChristopher Meis         {
13333cbff97fSChristopher Meis             unsigned int fruAreaOffsetField =
13343cbff97fSChristopher Meis                 getHeaderAreaFieldOffset(nextFRUArea);
13353cbff97fSChristopher Meis             size_t curFRUAreaOffset = fruData[fruAreaOffsetField];
13363cbff97fSChristopher Meis             if (curFRUAreaOffset > fruAreaParams.end)
13373cbff97fSChristopher Meis             {
13383cbff97fSChristopher Meis                 fruData[fruAreaOffsetField] = static_cast<int8_t>(
13393cbff97fSChristopher Meis                     curFRUAreaOffset + nextFRUAreaOffsetDiff);
13403cbff97fSChristopher Meis             }
13413cbff97fSChristopher Meis         }
13423cbff97fSChristopher Meis         // Calculate new checksum
13433cbff97fSChristopher Meis         std::vector<uint8_t> headerFRUData;
13443cbff97fSChristopher Meis         std::copy_n(fruData.begin(), 7, std::back_inserter(headerFRUData));
13453cbff97fSChristopher Meis         size_t checksumVal = calculateChecksum(headerFRUData);
13463cbff97fSChristopher Meis         fruData[7] = static_cast<uint8_t>(checksumVal);
13473cbff97fSChristopher Meis         // fill zeros if FRU Area size decreased
13483cbff97fSChristopher Meis         if (nextFRUAreaOffsetDiff < 0)
13493cbff97fSChristopher Meis         {
13503cbff97fSChristopher Meis             std::fill(fruData.begin() + nextFRUAreaNewLoc +
13513cbff97fSChristopher Meis                           restFRUAreasData.size(),
13523cbff97fSChristopher Meis                       fruData.end(), 0);
13533cbff97fSChristopher Meis         }
13543cbff97fSChristopher Meis     }
13553cbff97fSChristopher Meis #else
13563cbff97fSChristopher Meis     // this is to avoid "unused variable" warning
13573cbff97fSChristopher Meis     (void)nextFRUAreaNewLoc;
13583cbff97fSChristopher Meis #endif // ENABLE_FRU_AREA_RESIZE
13593cbff97fSChristopher Meis     if (fruData.empty())
13603cbff97fSChristopher Meis     {
13613cbff97fSChristopher Meis         return false;
13623cbff97fSChristopher Meis     }
13633cbff97fSChristopher Meis 
13643cbff97fSChristopher Meis     if (!writeFRU(static_cast<uint8_t>(bus), static_cast<uint8_t>(address),
13653cbff97fSChristopher Meis                   fruData))
13663cbff97fSChristopher Meis     {
13673cbff97fSChristopher Meis         return false;
13683cbff97fSChristopher Meis     }
13693cbff97fSChristopher Meis 
13703cbff97fSChristopher Meis     // Rescan the bus so that GetRawFru dbus-call fetches updated values
13713cbff97fSChristopher Meis     rescanBusses(busMap, dbusInterfaceMap, unknownBusObjectCount, powerIsOn,
13723cbff97fSChristopher Meis                  objServer, systemBus);
13733cbff97fSChristopher Meis     return true;
13743cbff97fSChristopher Meis }
13753cbff97fSChristopher Meis 
main()13763cbff97fSChristopher Meis int main()
13773cbff97fSChristopher Meis {
13783cbff97fSChristopher Meis     auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
13793cbff97fSChristopher Meis     sdbusplus::asio::object_server objServer(systemBus);
13803cbff97fSChristopher Meis 
13813cbff97fSChristopher Meis     static size_t unknownBusObjectCount = 0;
13823cbff97fSChristopher Meis     static bool powerIsOn = false;
13833cbff97fSChristopher Meis     auto devDir = fs::path("/dev/");
13843cbff97fSChristopher Meis     auto matchString = std::string(R"(i2c-\d+$)");
13853cbff97fSChristopher Meis     std::vector<fs::path> i2cBuses;
13863cbff97fSChristopher Meis 
13873cbff97fSChristopher Meis     if (!findFiles(devDir, matchString, i2cBuses))
13883cbff97fSChristopher Meis     {
13893cbff97fSChristopher Meis         std::cerr << "unable to find i2c devices\n";
13903cbff97fSChristopher Meis         return 1;
13913cbff97fSChristopher Meis     }
13923cbff97fSChristopher Meis 
13933cbff97fSChristopher Meis     // check for and load blocklist with initial buses.
13943cbff97fSChristopher Meis     loadBlocklist(blocklistPath);
13953cbff97fSChristopher Meis 
13963cbff97fSChristopher Meis     systemBus->request_name("xyz.openbmc_project.FruDevice");
13973cbff97fSChristopher Meis 
13983cbff97fSChristopher Meis     // this is a map with keys of pair(bus number, address) and values of
13993cbff97fSChristopher Meis     // the object on dbus
14003cbff97fSChristopher Meis     boost::container::flat_map<std::pair<size_t, size_t>,
14013cbff97fSChristopher Meis                                std::shared_ptr<sdbusplus::asio::dbus_interface>>
14023cbff97fSChristopher Meis         dbusInterfaceMap;
14033cbff97fSChristopher Meis 
14043cbff97fSChristopher Meis     std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
14053cbff97fSChristopher Meis         objServer.add_interface("/xyz/openbmc_project/FruDevice",
14063cbff97fSChristopher Meis                                 "xyz.openbmc_project.FruDeviceManager");
14073cbff97fSChristopher Meis 
14083cbff97fSChristopher Meis     iface->register_method("ReScan", [&]() {
14093cbff97fSChristopher Meis         rescanBusses(busMap, dbusInterfaceMap, unknownBusObjectCount, powerIsOn,
14103cbff97fSChristopher Meis                      objServer, systemBus);
14113cbff97fSChristopher Meis     });
14123cbff97fSChristopher Meis 
14133cbff97fSChristopher Meis     iface->register_method("ReScanBus", [&](uint16_t bus) {
14143cbff97fSChristopher Meis         rescanOneBus(busMap, bus, dbusInterfaceMap, true, unknownBusObjectCount,
14153cbff97fSChristopher Meis                      powerIsOn, objServer, systemBus);
14163cbff97fSChristopher Meis     });
14173cbff97fSChristopher Meis 
14183cbff97fSChristopher Meis     iface->register_method("GetRawFru", getFRUInfo);
14193cbff97fSChristopher Meis 
14203cbff97fSChristopher Meis     iface->register_method(
14213cbff97fSChristopher Meis         "WriteFru", [&](const uint16_t bus, const uint8_t address,
14223cbff97fSChristopher Meis                         const std::vector<uint8_t>& data) {
14233cbff97fSChristopher Meis             if (!writeFRU(bus, address, data))
14243cbff97fSChristopher Meis             {
14253cbff97fSChristopher Meis                 throw std::invalid_argument("Invalid Arguments.");
14263cbff97fSChristopher Meis                 return;
14273cbff97fSChristopher Meis             }
14283cbff97fSChristopher Meis             // schedule rescan on success
14293cbff97fSChristopher Meis             rescanBusses(busMap, dbusInterfaceMap, unknownBusObjectCount,
14303cbff97fSChristopher Meis                          powerIsOn, objServer, systemBus);
14313cbff97fSChristopher Meis         });
14323cbff97fSChristopher Meis     iface->initialize();
14333cbff97fSChristopher Meis 
14343cbff97fSChristopher Meis     std::function<void(sdbusplus::message_t & message)> eventHandler =
14353cbff97fSChristopher Meis         [&](sdbusplus::message_t& message) {
14363cbff97fSChristopher Meis             std::string objectName;
14373cbff97fSChristopher Meis             boost::container::flat_map<
14383cbff97fSChristopher Meis                 std::string,
14393cbff97fSChristopher Meis                 std::variant<std::string, bool, int64_t, uint64_t, double>>
14403cbff97fSChristopher Meis                 values;
14413cbff97fSChristopher Meis             message.read(objectName, values);
14423cbff97fSChristopher Meis             auto findState = values.find("CurrentHostState");
14433cbff97fSChristopher Meis             if (findState != values.end())
14443cbff97fSChristopher Meis             {
14453cbff97fSChristopher Meis                 if (std::get<std::string>(findState->second) ==
14463cbff97fSChristopher Meis                     "xyz.openbmc_project.State.Host.HostState.Running")
14473cbff97fSChristopher Meis                 {
14483cbff97fSChristopher Meis                     powerIsOn = true;
14493cbff97fSChristopher Meis                 }
14503cbff97fSChristopher Meis             }
14513cbff97fSChristopher Meis 
14523cbff97fSChristopher Meis             if (powerIsOn)
14533cbff97fSChristopher Meis             {
14543cbff97fSChristopher Meis                 rescanBusses(busMap, dbusInterfaceMap, unknownBusObjectCount,
14553cbff97fSChristopher Meis                              powerIsOn, objServer, systemBus);
14563cbff97fSChristopher Meis             }
14573cbff97fSChristopher Meis         };
14583cbff97fSChristopher Meis 
14593cbff97fSChristopher Meis     sdbusplus::bus::match_t powerMatch = sdbusplus::bus::match_t(
14603cbff97fSChristopher Meis         static_cast<sdbusplus::bus_t&>(*systemBus),
14613cbff97fSChristopher Meis         "type='signal',interface='org.freedesktop.DBus.Properties',path='/xyz/"
14623cbff97fSChristopher Meis         "openbmc_project/state/"
14633cbff97fSChristopher Meis         "host0',arg0='xyz.openbmc_project.State.Host'",
14643cbff97fSChristopher Meis         eventHandler);
14653cbff97fSChristopher Meis 
14663cbff97fSChristopher Meis     int fd = inotify_init();
14673cbff97fSChristopher Meis     inotify_add_watch(fd, i2CDevLocation, IN_CREATE | IN_MOVED_TO | IN_DELETE);
14683cbff97fSChristopher Meis     std::array<char, 4096> readBuffer{};
14693cbff97fSChristopher Meis     // monitor for new i2c devices
14703cbff97fSChristopher Meis     boost::asio::posix::stream_descriptor dirWatch(io, fd);
14713cbff97fSChristopher Meis     std::function<void(const boost::system::error_code, std::size_t)>
14723cbff97fSChristopher Meis         watchI2cBusses = [&](const boost::system::error_code& ec,
14733cbff97fSChristopher Meis                              std::size_t bytesTransferred) {
14743cbff97fSChristopher Meis             if (ec)
14753cbff97fSChristopher Meis             {
14763cbff97fSChristopher Meis                 std::cout << "Callback Error " << ec << "\n";
14773cbff97fSChristopher Meis                 return;
14783cbff97fSChristopher Meis             }
14793cbff97fSChristopher Meis             size_t index = 0;
14803cbff97fSChristopher Meis             while ((index + sizeof(inotify_event)) <= bytesTransferred)
14813cbff97fSChristopher Meis             {
14823cbff97fSChristopher Meis                 const char* p = &readBuffer[index];
14833cbff97fSChristopher Meis                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
14843cbff97fSChristopher Meis                 const auto* iEvent = reinterpret_cast<const inotify_event*>(p);
14853cbff97fSChristopher Meis                 switch (iEvent->mask)
14863cbff97fSChristopher Meis                 {
14873cbff97fSChristopher Meis                     case IN_CREATE:
14883cbff97fSChristopher Meis                     case IN_MOVED_TO:
14893cbff97fSChristopher Meis                     case IN_DELETE:
14903cbff97fSChristopher Meis                     {
14913cbff97fSChristopher Meis                         std::string_view name(&iEvent->name[0], iEvent->len);
14923cbff97fSChristopher Meis                         if (boost::starts_with(name, "i2c"))
14933cbff97fSChristopher Meis                         {
14943cbff97fSChristopher Meis                             int bus = busStrToInt(name);
14953cbff97fSChristopher Meis                             if (bus < 0)
14963cbff97fSChristopher Meis                             {
14973cbff97fSChristopher Meis                                 std::cerr
14983cbff97fSChristopher Meis                                     << "Could not parse bus " << name << "\n";
14993cbff97fSChristopher Meis                                 continue;
15003cbff97fSChristopher Meis                             }
15013cbff97fSChristopher Meis                             int rootBus = getRootBus(bus);
15023cbff97fSChristopher Meis                             if (rootBus >= 0)
15033cbff97fSChristopher Meis                             {
15043cbff97fSChristopher Meis                                 rescanOneBus(busMap,
15053cbff97fSChristopher Meis                                              static_cast<uint16_t>(rootBus),
15063cbff97fSChristopher Meis                                              dbusInterfaceMap, false,
15073cbff97fSChristopher Meis                                              unknownBusObjectCount, powerIsOn,
15083cbff97fSChristopher Meis                                              objServer, systemBus);
15093cbff97fSChristopher Meis                             }
15103cbff97fSChristopher Meis                             rescanOneBus(busMap, static_cast<uint16_t>(bus),
15113cbff97fSChristopher Meis                                          dbusInterfaceMap, false,
15123cbff97fSChristopher Meis                                          unknownBusObjectCount, powerIsOn,
15133cbff97fSChristopher Meis                                          objServer, systemBus);
15143cbff97fSChristopher Meis                         }
15153cbff97fSChristopher Meis                     }
15163cbff97fSChristopher Meis                     break;
15173cbff97fSChristopher Meis                     default:
15183cbff97fSChristopher Meis                         break;
15193cbff97fSChristopher Meis                 }
15203cbff97fSChristopher Meis                 index += sizeof(inotify_event) + iEvent->len;
15213cbff97fSChristopher Meis             }
15223cbff97fSChristopher Meis 
15233cbff97fSChristopher Meis             dirWatch.async_read_some(boost::asio::buffer(readBuffer),
15243cbff97fSChristopher Meis                                      watchI2cBusses);
15253cbff97fSChristopher Meis         };
15263cbff97fSChristopher Meis 
15273cbff97fSChristopher Meis     dirWatch.async_read_some(boost::asio::buffer(readBuffer), watchI2cBusses);
15283cbff97fSChristopher Meis     // run the initial scan
15293cbff97fSChristopher Meis     rescanBusses(busMap, dbusInterfaceMap, unknownBusObjectCount, powerIsOn,
15303cbff97fSChristopher Meis                  objServer, systemBus);
15313cbff97fSChristopher Meis 
15323cbff97fSChristopher Meis     io.run();
15333cbff97fSChristopher Meis     return 0;
15343cbff97fSChristopher Meis }
1535