1 #include <map> 2 #include <phosphor-logging/elog-errors.hpp> 3 #include "xyz/openbmc_project/Common/error.hpp" 4 #include "read_fru_data.hpp" 5 #include "fruread.hpp" 6 #include "host-ipmid/ipmid-api.h" 7 #include "utils.hpp" 8 9 extern const FruMap frus; 10 namespace ipmi 11 { 12 namespace fru 13 { 14 using namespace phosphor::logging; 15 using InternalFailure = 16 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 17 std::unique_ptr<sdbusplus::bus::match_t> matchPtr(nullptr); 18 19 static constexpr auto INV_INTF = "xyz.openbmc_project.Inventory.Manager"; 20 static constexpr auto OBJ_PATH = "/xyz/openbmc_project/inventory"; 21 static constexpr auto PROP_INTF = "org.freedesktop.DBus.Properties"; 22 23 namespace cache 24 { 25 //User initiate read FRU info area command followed by 26 //FRU read command. Also data is read in small chunks of 27 //the specified offset and count. 28 //Caching the data which will be invalidated when ever there 29 //is a change in FRU properties. 30 FRUAreaMap fruMap; 31 } 32 /** 33 * @brief Read the property value from Inventory 34 * 35 * @param[in] bus dbus 36 * @param[in] intf Interface 37 * @param[in] propertyName Name of the property 38 * @param[in] path Object path 39 * @return property value 40 */ 41 std::string readProperty(const std::string& intf, 42 const std::string& propertyName, 43 const std::string& path) 44 { 45 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 46 auto service = ipmi::getService(bus, INV_INTF, OBJ_PATH); 47 std::string objPath = OBJ_PATH + path; 48 auto method = bus.new_method_call(service.c_str(), 49 objPath.c_str(), 50 PROP_INTF, 51 "Get"); 52 method.append(intf, propertyName); 53 auto reply = bus.call(method); 54 if (reply.is_method_error()) 55 { 56 //If property is not found simply return empty value 57 log<level::INFO>("Property value not set", 58 entry("Property=%s", propertyName), 59 entry("Path=%s", objPath)); 60 return {}; 61 } 62 sdbusplus::message::variant<std::string> property; 63 reply.read(property); 64 std::string value = 65 sdbusplus::message::variant_ns::get<std::string>(property); 66 return value; 67 } 68 69 void processFruPropChange(sdbusplus::message::message& msg) 70 { 71 if(cache::fruMap.empty()) 72 { 73 return; 74 } 75 std::string path = msg.get_path(); 76 //trim the object base path, if found at the beginning 77 if (path.compare(0, strlen(OBJ_PATH), OBJ_PATH) == 0) 78 { 79 path.erase(0, strlen(OBJ_PATH)); 80 } 81 for (auto& fru : frus) 82 { 83 bool found = false; 84 auto& fruId = fru.first; 85 auto& instanceList = fru.second; 86 for (auto& instance : instanceList) 87 { 88 if(instance.first == path) 89 { 90 found = true; 91 break; 92 } 93 } 94 if (found) 95 { 96 cache::fruMap.erase(fruId); 97 break; 98 } 99 } 100 } 101 102 //register for fru property change 103 int registerCallbackHandler() 104 { 105 if(matchPtr == nullptr) 106 { 107 using namespace sdbusplus::bus::match::rules; 108 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 109 matchPtr = std::make_unique<sdbusplus::bus::match_t>( 110 bus, 111 path_namespace(OBJ_PATH) + 112 type::signal() + 113 member("PropertiesChanged") + 114 interface(PROP_INTF), 115 std::bind(processFruPropChange, std::placeholders::_1)); 116 } 117 return 0; 118 } 119 120 /** 121 * @brief Read FRU property values from Inventory 122 * 123 * @param[in] fruNum FRU id 124 * @return populate FRU Inventory data 125 */ 126 FruInventoryData readDataFromInventory(const FRUId& fruNum) 127 { 128 auto iter = frus.find(fruNum); 129 if (iter == frus.end()) 130 { 131 log<level::ERR>("Unsupported FRU ID ",entry("FRUID=%d", fruNum)); 132 elog<InternalFailure>(); 133 } 134 135 FruInventoryData data; 136 auto& instanceList = iter->second; 137 for (auto& instance : instanceList) 138 { 139 for (auto& interfaceList : instance.second) 140 { 141 for (auto& properties : interfaceList.second) 142 { 143 decltype(auto) pdata = properties.second; 144 auto value = readProperty( 145 interfaceList.first, properties.first, 146 instance.first); 147 data[pdata.section].emplace(properties.first, value); 148 } 149 } 150 } 151 return data; 152 } 153 154 const FruAreaData& getFruAreaData(const FRUId& fruNum) 155 { 156 auto iter = cache::fruMap.find(fruNum); 157 if (iter != cache::fruMap.end()) 158 { 159 return iter->second; 160 } 161 auto invData = readDataFromInventory(fruNum); 162 163 //Build area info based on inventory data 164 FruAreaData newdata = buildFruAreaData(std::move(invData)); 165 cache::fruMap.emplace(fruNum, std::move(newdata)); 166 return cache::fruMap.at(fruNum); 167 } 168 } //fru 169 } //ipmi 170