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 #include "types.hpp" 9 10 extern const FruMap frus; 11 namespace ipmi 12 { 13 namespace fru 14 { 15 using namespace phosphor::logging; 16 using InternalFailure = 17 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 18 std::unique_ptr<sdbusplus::bus::match_t> matchPtr(nullptr); 19 20 static constexpr auto INV_INTF = "xyz.openbmc_project.Inventory.Manager"; 21 static constexpr auto OBJ_PATH = "/xyz/openbmc_project/inventory"; 22 static constexpr auto PROP_INTF = "org.freedesktop.DBus.Properties"; 23 24 namespace cache 25 { 26 //User initiate read FRU info area command followed by 27 //FRU read command. Also data is read in small chunks of 28 //the specified offset and count. 29 //Caching the data which will be invalidated when ever there 30 //is a change in FRU properties. 31 FRUAreaMap fruMap; 32 } 33 /** 34 * @brief Read all the property value's for the specified interface 35 * from Inventory. 36 * 37 * @param[in] intf Interface 38 * @param[in] path Object path 39 * @return map of properties 40 */ 41 ipmi::PropertyMap readAllProperties(const std::string& intf, 42 const std::string& path) 43 { 44 ipmi::PropertyMap properties; 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 "GetAll"); 52 method.append(intf); 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::ERR>("Error in reading property values from inventory", 58 entry("INTERFACE=%s", intf), 59 entry("PATH=%s", objPath)); 60 return properties; 61 } 62 reply.read(properties); 63 return properties; 64 } 65 66 void processFruPropChange(sdbusplus::message::message& msg) 67 { 68 if(cache::fruMap.empty()) 69 { 70 return; 71 } 72 std::string path = msg.get_path(); 73 //trim the object base path, if found at the beginning 74 if (path.compare(0, strlen(OBJ_PATH), OBJ_PATH) == 0) 75 { 76 path.erase(0, strlen(OBJ_PATH)); 77 } 78 for (auto& fru : frus) 79 { 80 bool found = false; 81 auto& fruId = fru.first; 82 auto& instanceList = fru.second; 83 for (auto& instance : instanceList) 84 { 85 if(instance.first == path) 86 { 87 found = true; 88 break; 89 } 90 } 91 if (found) 92 { 93 cache::fruMap.erase(fruId); 94 break; 95 } 96 } 97 } 98 99 //register for fru property change 100 int registerCallbackHandler() 101 { 102 if(matchPtr == nullptr) 103 { 104 using namespace sdbusplus::bus::match::rules; 105 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 106 matchPtr = std::make_unique<sdbusplus::bus::match_t>( 107 bus, 108 path_namespace(OBJ_PATH) + 109 type::signal() + 110 member("PropertiesChanged") + 111 interface(PROP_INTF), 112 std::bind(processFruPropChange, std::placeholders::_1)); 113 } 114 return 0; 115 } 116 117 /** 118 * @brief Read FRU property values from Inventory 119 * 120 * @param[in] fruNum FRU id 121 * @return populate FRU Inventory data 122 */ 123 FruInventoryData readDataFromInventory(const FRUId& fruNum) 124 { 125 auto iter = frus.find(fruNum); 126 if (iter == frus.end()) 127 { 128 log<level::ERR>("Unsupported FRU ID ",entry("FRUID=%d", fruNum)); 129 elog<InternalFailure>(); 130 } 131 132 FruInventoryData data; 133 auto& instanceList = iter->second; 134 for (auto& instance : instanceList) 135 { 136 for (auto& intf : instance.second) 137 { 138 ipmi::PropertyMap allProp = readAllProperties( 139 intf.first, instance.first); 140 for (auto& properties : intf.second) 141 { 142 auto iter = allProp.find(properties.first); 143 if (iter != allProp.end()) 144 { 145 data[properties.second.section].emplace(properties.first, 146 std::move(allProp[properties.first].get<std::string>())); 147 } 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