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