1 #include "config.h" 2 3 #include "manager.hpp" 4 5 #include "common_utility.hpp" 6 #include "editor_impl.hpp" 7 #include "gpioMonitor.hpp" 8 #include "ibm_vpd_utils.hpp" 9 #include "ipz_parser.hpp" 10 #include "reader_impl.hpp" 11 #include "vpd_exceptions.hpp" 12 13 #include <filesystem> 14 #include <phosphor-logging/elog-errors.hpp> 15 16 using namespace openpower::vpd::manager; 17 using namespace openpower::vpd::constants; 18 using namespace openpower::vpd::inventory; 19 using namespace openpower::vpd::manager::editor; 20 using namespace openpower::vpd::manager::reader; 21 using namespace std; 22 using namespace openpower::vpd::parser; 23 using namespace openpower::vpd::exceptions; 24 using namespace phosphor::logging; 25 26 namespace openpower 27 { 28 namespace vpd 29 { 30 namespace manager 31 { 32 Manager::Manager(sdbusplus::bus::bus&& bus, const char* busName, 33 const char* objPath, const char* /*iFace*/) : 34 ServerObject<ManagerIface>(bus, objPath), 35 _bus(std::move(bus)), _manager(_bus, objPath) 36 { 37 _bus.request_name(busName); 38 } 39 40 void Manager::run() 41 { 42 try 43 { 44 processJSON(); 45 listenHostState(); 46 listenAssetTag(); 47 48 auto event = sdeventplus::Event::get_default(); 49 GpioMonitor gpioMon1(jsonFile, event); 50 51 _bus.attach_event(event.get(), SD_EVENT_PRIORITY_IMPORTANT); 52 cout << "VPD manager event loop started\n"; 53 event.loop(); 54 } 55 catch (const std::exception& e) 56 { 57 std::cerr << e.what() << "\n"; 58 } 59 } 60 61 void Manager::listenHostState() 62 { 63 static std::shared_ptr<sdbusplus::bus::match::match> hostState = 64 std::make_shared<sdbusplus::bus::match::match>( 65 _bus, 66 sdbusplus::bus::match::rules::propertiesChanged( 67 "/xyz/openbmc_project/state/host0", 68 "xyz.openbmc_project.State.Host"), 69 [this](sdbusplus::message::message& msg) { 70 hostStateCallBack(msg); 71 }); 72 } 73 74 void Manager::hostStateCallBack(sdbusplus::message::message& msg) 75 { 76 if (msg.is_method_error()) 77 { 78 std::cerr << "Error in reading signal " << std::endl; 79 } 80 81 Path object; 82 PropertyMap propMap; 83 msg.read(object, propMap); 84 const auto itr = propMap.find("CurrentHostState"); 85 if (itr != propMap.end()) 86 { 87 if (auto hostState = std::get_if<std::string>(&(itr->second))) 88 { 89 // implies system is moving from standby to power on state 90 if (*hostState == "xyz.openbmc_project.State.Host.HostState." 91 "TransitioningToRunning") 92 { 93 // check and perfrom recollection for FRUs replaceable at 94 // standby. 95 performVPDRecollection(); 96 return; 97 } 98 } 99 std::cerr << "Failed to read Host state" << std::endl; 100 } 101 } 102 103 void Manager::listenAssetTag() 104 { 105 static std::shared_ptr<sdbusplus::bus::match::match> assetMatcher = 106 std::make_shared<sdbusplus::bus::match::match>( 107 _bus, 108 sdbusplus::bus::match::rules::propertiesChanged( 109 "/xyz/openbmc_project/inventory/system", 110 "xyz.openbmc_project.Inventory.Decorator.AssetTag"), 111 [this](sdbusplus::message::message& msg) { 112 assetTagCallback(msg); 113 }); 114 } 115 116 void Manager::assetTagCallback(sdbusplus::message::message& msg) 117 { 118 if (msg.is_method_error()) 119 { 120 std::cerr << "Error in reading signal " << std::endl; 121 } 122 123 Path object; 124 PropertyMap propMap; 125 msg.read(object, propMap); 126 const auto itr = propMap.find("AssetTag"); 127 if (itr != propMap.end()) 128 { 129 if (auto assetTag = std::get_if<std::string>(&(itr->second))) 130 { 131 // Call Notify to persist the AssetTag 132 inventory::ObjectMap objectMap = { 133 {std::string{"/system"}, 134 {{"xyz.openbmc_project.Inventory.Decorator.AssetTag", 135 {{"AssetTag", *assetTag}}}}}}; 136 137 common::utility::callPIM(std::move(objectMap)); 138 } 139 else 140 { 141 std::cerr << "Failed to read asset tag" << std::endl; 142 } 143 } 144 } 145 146 void Manager::processJSON() 147 { 148 std::ifstream json(INVENTORY_JSON_SYM_LINK, std::ios::binary); 149 150 if (!json) 151 { 152 throw std::runtime_error("json file not found"); 153 } 154 155 jsonFile = nlohmann::json::parse(json); 156 if (jsonFile.find("frus") == jsonFile.end()) 157 { 158 throw std::runtime_error("frus group not found in json"); 159 } 160 161 const nlohmann::json& groupFRUS = 162 jsonFile["frus"].get_ref<const nlohmann::json::object_t&>(); 163 for (const auto& itemFRUS : groupFRUS.items()) 164 { 165 const std::vector<nlohmann::json>& groupEEPROM = 166 itemFRUS.value().get_ref<const nlohmann::json::array_t&>(); 167 for (const auto& itemEEPROM : groupEEPROM) 168 { 169 bool isMotherboard = false; 170 std::string redundantPath; 171 172 if (itemEEPROM["extraInterfaces"].find( 173 "xyz.openbmc_project.Inventory.Item.Board.Motherboard") != 174 itemEEPROM["extraInterfaces"].end()) 175 { 176 isMotherboard = true; 177 } 178 if (itemEEPROM.find("redundantEeprom") != itemEEPROM.end()) 179 { 180 redundantPath = itemEEPROM["redundantEeprom"] 181 .get_ref<const nlohmann::json::string_t&>(); 182 } 183 frus.emplace( 184 itemEEPROM["inventoryPath"] 185 .get_ref<const nlohmann::json::string_t&>(), 186 std::make_tuple(itemFRUS.key(), redundantPath, isMotherboard)); 187 188 if (itemEEPROM["extraInterfaces"].find(IBM_LOCATION_CODE_INF) != 189 itemEEPROM["extraInterfaces"].end()) 190 { 191 fruLocationCode.emplace( 192 itemEEPROM["extraInterfaces"][IBM_LOCATION_CODE_INF] 193 ["LocationCode"] 194 .get_ref<const nlohmann::json::string_t&>(), 195 itemEEPROM["inventoryPath"] 196 .get_ref<const nlohmann::json::string_t&>()); 197 } 198 199 if (itemEEPROM.value("replaceableAtStandby", false)) 200 { 201 replaceableFrus.emplace_back(itemFRUS.key()); 202 } 203 } 204 } 205 } 206 207 void Manager::writeKeyword(const sdbusplus::message::object_path path, 208 const std::string recordName, 209 const std::string keyword, const Binary value) 210 { 211 try 212 { 213 std::string objPath{path}; 214 // Strip any inventory prefix in path 215 if (objPath.find(INVENTORY_PATH) == 0) 216 { 217 objPath = objPath.substr(sizeof(INVENTORY_PATH) - 1); 218 } 219 220 if (frus.find(objPath) == frus.end()) 221 { 222 throw std::runtime_error("Inventory path not found"); 223 } 224 225 inventory::Path vpdFilePath = std::get<0>(frus.find(objPath)->second); 226 227 // instantiate editor class to update the data 228 EditorImpl edit(vpdFilePath, jsonFile, recordName, keyword, objPath); 229 230 uint32_t offset = 0; 231 // Setup offset, if any 232 for (const auto& item : jsonFile["frus"][vpdFilePath]) 233 { 234 if (item.find("offset") != item.end()) 235 { 236 offset = item["offset"]; 237 break; 238 } 239 } 240 241 edit.updateKeyword(value, offset, true); 242 243 // If we have a redundant EEPROM to update, then update just the EEPROM, 244 // not the cache since that is already done when we updated the primary 245 if (!std::get<1>(frus.find(objPath)->second).empty()) 246 { 247 EditorImpl edit(std::get<1>(frus.find(objPath)->second), jsonFile, 248 recordName, keyword); 249 edit.updateKeyword(value, offset, false); 250 } 251 252 // if it is a motehrboard FRU need to check for location expansion 253 if (std::get<2>(frus.find(objPath)->second)) 254 { 255 if (recordName == "VCEN" && (keyword == "FC" || keyword == "SE")) 256 { 257 edit.expandLocationCode("fcs"); 258 } 259 else if (recordName == "VSYS" && 260 (keyword == "TM" || keyword == "SE")) 261 { 262 edit.expandLocationCode("mts"); 263 } 264 } 265 266 return; 267 } 268 catch (const std::exception& e) 269 { 270 std::cerr << e.what() << std::endl; 271 } 272 } 273 274 ListOfPaths 275 Manager::getFRUsByUnexpandedLocationCode(const LocationCode locationCode, 276 const NodeNumber nodeNumber) 277 { 278 ReaderImpl read; 279 return read.getFrusAtLocation(locationCode, nodeNumber, fruLocationCode); 280 } 281 282 ListOfPaths 283 Manager::getFRUsByExpandedLocationCode(const LocationCode locationCode) 284 { 285 ReaderImpl read; 286 return read.getFRUsByExpandedLocationCode(locationCode, fruLocationCode); 287 } 288 289 LocationCode Manager::getExpandedLocationCode(const LocationCode locationCode, 290 const NodeNumber nodeNumber) 291 { 292 ReaderImpl read; 293 return read.getExpandedLocationCode(locationCode, nodeNumber, 294 fruLocationCode); 295 } 296 297 void Manager::performVPDRecollection() 298 { 299 // get list of FRUs replaceable at standby 300 for (const auto& item : replaceableFrus) 301 { 302 const vector<nlohmann::json>& groupEEPROM = jsonFile["frus"][item]; 303 const nlohmann::json& singleFru = groupEEPROM[0]; 304 305 const string& inventoryPath = 306 singleFru["inventoryPath"] 307 .get_ref<const nlohmann::json::string_t&>(); 308 309 bool prePostActionRequired = false; 310 311 if ((jsonFile["frus"][item].at(0)).find("preAction") != 312 jsonFile["frus"][item].at(0).end()) 313 { 314 if (!executePreAction(jsonFile, item)) 315 { 316 // if the FRU has preAction defined then its execution should 317 // pass to ensure bind/unbind of data. 318 // preAction execution failed. should not call bind/unbind. 319 log<level::ERR>( 320 "Pre-Action execution failed for the FRU", 321 entry("ERROR=%s", 322 ("Inventory path: " + inventoryPath).c_str())); 323 continue; 324 } 325 prePostActionRequired = true; 326 } 327 328 if ((singleFru.find("devAddress") == singleFru.end()) || 329 (singleFru.find("driverType") == singleFru.end()) || 330 (singleFru.find("busType") == singleFru.end())) 331 { 332 // The FRUs is marked for replacement but missing mandatory 333 // fields for recollection. Skip to another replaceable fru. 334 log<level::ERR>( 335 "Recollection Failed as mandatory field missing in Json", 336 entry("ERROR=%s", 337 ("Recollection failed for " + inventoryPath).c_str())); 338 continue; 339 } 340 341 string str = "echo "; 342 string deviceAddress = singleFru["devAddress"]; 343 const string& driverType = singleFru["driverType"]; 344 const string& busType = singleFru["busType"]; 345 346 // devTreeStatus flag is present in json as false to mention 347 // that the EEPROM is not mentioned in device tree. If this flag 348 // is absent consider the value to be true, i.e EEPROM is 349 // mentioned in device tree 350 if (!singleFru.value("devTreeStatus", true)) 351 { 352 auto pos = deviceAddress.find('-'); 353 if (pos != string::npos) 354 { 355 string busNum = deviceAddress.substr(0, pos); 356 deviceAddress = 357 "0x" + deviceAddress.substr(pos + 1, string::npos); 358 359 string deleteDevice = str + deviceAddress + " > /sys/bus/" + 360 busType + "/devices/" + busType + "-" + 361 busNum + "/delete_device"; 362 executeCmd(deleteDevice); 363 364 string addDevice = str + driverType + " " + deviceAddress + 365 " > /sys/bus/" + busType + "/devices/" + 366 busType + "-" + busNum + "/new_device"; 367 executeCmd(addDevice); 368 } 369 else 370 { 371 log<level::ERR>( 372 "Wrong format of device address in Json", 373 entry( 374 "ERROR=%s", 375 ("Recollection failed for " + inventoryPath).c_str())); 376 continue; 377 } 378 } 379 else 380 { 381 executeCmd(createBindUnbindDriverCmnd(deviceAddress, busType, 382 driverType, "/unbind")); 383 executeCmd(createBindUnbindDriverCmnd(deviceAddress, busType, 384 driverType, "/bind")); 385 } 386 387 // this check is added to avoid file system expensive call in case not 388 // required. 389 if (prePostActionRequired) 390 { 391 // Check if device showed up (test for file) 392 if (!filesystem::exists(item)) 393 { 394 // If not, then take failure postAction 395 executePostFailAction(jsonFile, item); 396 } 397 } 398 } 399 } 400 401 } // namespace manager 402 } // namespace vpd 403 } // namespace openpower