1 #include "parser.hpp" 2 3 #include "constants.hpp" 4 #include "event_logger.hpp" 5 6 #include <utility/dbus_utility.hpp> 7 #include <utility/json_utility.hpp> 8 #include <utility/vpd_specific_utility.hpp> 9 10 #include <fstream> 11 12 namespace vpd 13 { 14 Parser::Parser(const std::string& vpdFilePath, nlohmann::json parsedJson) : 15 m_vpdFilePath(vpdFilePath), m_parsedJson(parsedJson) 16 { 17 std::error_code l_errCode; 18 19 // ToDo: Add minimum file size check in all the concert praser classes, 20 // depends on their VPD type. 21 if (!std::filesystem::exists(m_vpdFilePath, l_errCode)) 22 { 23 std::string l_message{"Parser object creation failed, file [" + 24 m_vpdFilePath + "] doesn't exists."}; 25 26 if (l_errCode) 27 { 28 l_message += " Error message: " + l_errCode.message(); 29 } 30 31 throw std::runtime_error(l_message); 32 } 33 34 // Read VPD offset if applicable. 35 if (!m_parsedJson.empty()) 36 { 37 uint16_t l_errorCode = 0; 38 39 m_vpdStartOffset = 40 jsonUtility::getVPDOffset(m_parsedJson, vpdFilePath, l_errorCode); 41 42 if (l_errorCode) 43 { 44 logging::logMessage( 45 "Failed to get vpd offset for path [" + m_vpdFilePath + 46 "], error: " + commonUtility::getErrCodeMsg(l_errorCode)); 47 } 48 } 49 } 50 51 std::shared_ptr<vpd::ParserInterface> Parser::getVpdParserInstance() 52 { 53 // Read the VPD data into a vector. 54 uint16_t l_errCode = 0; 55 vpdSpecificUtility::getVpdDataInVector(m_vpdFilePath, m_vpdVector, 56 m_vpdStartOffset, l_errCode); 57 58 if (l_errCode) 59 { 60 logging::logMessage("Failed to get VPD in vector, error : " + 61 commonUtility::getErrCodeMsg(l_errCode)); 62 } 63 64 // This will detect the type of parser required. 65 std::shared_ptr<vpd::ParserInterface> l_parser = 66 ParserFactory::getParser(m_vpdVector, m_vpdFilePath, m_vpdStartOffset); 67 68 return l_parser; 69 } 70 71 types::VPDMapVariant Parser::parse() 72 { 73 std::shared_ptr<vpd::ParserInterface> l_parser = getVpdParserInstance(); 74 return l_parser->parse(); 75 } 76 77 int Parser::updateVpdKeyword(const types::WriteVpdParams& i_paramsToWriteData, 78 types::DbusVariantType& o_updatedValue) 79 { 80 int l_bytesUpdatedOnHardware = constants::FAILURE; 81 82 // A lambda to extract Record : Keyword string from i_paramsToWriteData 83 auto l_keyWordIdentifier = 84 [](const types::WriteVpdParams& i_paramsToWriteData) -> std::string { 85 std::string l_keywordString{}; 86 if (const types::IpzData* l_ipzData = 87 std::get_if<types::IpzData>(&i_paramsToWriteData)) 88 { 89 l_keywordString = 90 std::get<0>(*l_ipzData) + ":" + std::get<1>(*l_ipzData); 91 } 92 else if (const types::KwData* l_kwData = 93 std::get_if<types::KwData>(&i_paramsToWriteData)) 94 { 95 l_keywordString = std::get<0>(*l_kwData); 96 } 97 return l_keywordString; 98 }; 99 100 try 101 { 102 // Enable Reboot Guard 103 if (constants::FAILURE == dbusUtility::EnableRebootGuard()) 104 { 105 EventLogger::createAsyncPel( 106 types::ErrorType::DbusFailure, 107 types::SeverityType::Informational, __FILE__, __FUNCTION__, 0, 108 std::string( 109 "Failed to enable BMC Reboot Guard while updating " + 110 l_keyWordIdentifier(i_paramsToWriteData)), 111 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 112 113 return constants::FAILURE; 114 } 115 116 // Update keyword's value on hardware 117 try 118 { 119 std::shared_ptr<ParserInterface> l_vpdParserInstance = 120 getVpdParserInstance(); 121 l_bytesUpdatedOnHardware = 122 l_vpdParserInstance->writeKeywordOnHardware( 123 i_paramsToWriteData); 124 } 125 catch (const std::exception& l_exception) 126 { 127 std::string l_errMsg( 128 "Error while updating keyword's value on hardware path " + 129 m_vpdFilePath + ", error: " + std::string(l_exception.what())); 130 131 // TODO : Log PEL 132 133 throw std::runtime_error(l_errMsg); 134 } 135 136 uint16_t l_errCode = 0; 137 138 auto [l_fruPath, l_inventoryObjPath, l_redundantFruPath] = 139 jsonUtility::getAllPathsToUpdateKeyword(m_parsedJson, m_vpdFilePath, 140 l_errCode); 141 142 if (l_errCode == error_code::INVALID_INPUT_PARAMETER || 143 l_errCode == error_code::INVALID_JSON) 144 { 145 throw std::runtime_error( 146 "Failed to get paths to update keyword. Error : " + 147 commonUtility::getErrCodeMsg(l_errCode)); 148 } 149 150 // If inventory D-bus object path is present, update keyword's value on 151 // DBus 152 if (!l_inventoryObjPath.empty()) 153 { 154 types::Record l_recordName; 155 std::string l_interfaceName; 156 std::string l_propertyName; 157 types::DbusVariantType l_keywordValue; 158 159 if (const types::IpzData* l_ipzData = 160 std::get_if<types::IpzData>(&i_paramsToWriteData)) 161 { 162 l_recordName = std::get<0>(*l_ipzData); 163 l_interfaceName = constants::ipzVpdInf + l_recordName; 164 l_propertyName = std::get<1>(*l_ipzData); 165 166 try 167 { 168 // Read keyword's value from hardware to write the same on 169 // D-bus. 170 std::shared_ptr<ParserInterface> l_vpdParserInstance = 171 getVpdParserInstance(); 172 173 l_keywordValue = 174 l_vpdParserInstance->readKeywordFromHardware( 175 types::ReadVpdParams( 176 std::make_tuple(l_recordName, l_propertyName))); 177 178 // return the actual value updated on hardware 179 o_updatedValue = l_keywordValue; 180 } 181 catch (const std::exception& l_exception) 182 { 183 // Unable to read keyword's value from hardware. 184 std::string l_errMsg( 185 "Error while reading keyword's value from hadware path " + 186 m_vpdFilePath + 187 ", error: " + std::string(l_exception.what())); 188 189 // TODO: Log PEL 190 191 throw std::runtime_error(l_errMsg); 192 } 193 } 194 else 195 { 196 // Input parameter type provided isn't compatible to perform 197 // update. 198 std::string l_errMsg( 199 "Input parameter type isn't compatible to update keyword's value on DBus for object path: " + 200 l_inventoryObjPath); 201 throw std::runtime_error(l_errMsg); 202 } 203 204 // Get D-bus name for the given keyword 205 l_propertyName = vpdSpecificUtility::getDbusPropNameForGivenKw( 206 l_propertyName, l_errCode); 207 208 if (l_errCode) 209 { 210 logging::logMessage( 211 "Failed to get Dbus property name for given keyword, error : " + 212 commonUtility::getErrCodeMsg(l_errCode)); 213 } 214 215 // Create D-bus object map 216 types::ObjectMap l_dbusObjMap = {std::make_pair( 217 l_inventoryObjPath, 218 types::InterfaceMap{std::make_pair( 219 l_interfaceName, types::PropertyMap{std::make_pair( 220 l_propertyName, l_keywordValue)})})}; 221 222 // Call PIM's Notify method to perform update 223 if (!dbusUtility::callPIM(std::move(l_dbusObjMap))) 224 { 225 // Call to PIM's Notify method failed. 226 std::string l_errMsg("Notify PIM is failed for object path: " + 227 l_inventoryObjPath); 228 throw std::runtime_error(l_errMsg); 229 } 230 } 231 232 if (l_errCode == error_code::ERROR_GETTING_REDUNDANT_PATH) 233 { 234 logging::logMessage(commonUtility::getErrCodeMsg(l_errCode)); 235 } 236 237 // Update keyword's value on redundant hardware if present 238 if (!l_redundantFruPath.empty()) 239 { 240 if (updateVpdKeywordOnRedundantPath(l_redundantFruPath, 241 i_paramsToWriteData) < 0) 242 { 243 std::string l_errMsg( 244 "Error while updating keyword's value on redundant path " + 245 l_redundantFruPath); 246 throw std::runtime_error(l_errMsg); 247 } 248 } 249 250 // TODO: Check if revert is required when any of the writes fails. 251 // TODO: Handle error logging 252 } 253 catch (const std::exception& l_ex) 254 { 255 logging::logMessage("Update VPD Keyword failed for : " + 256 l_keyWordIdentifier(i_paramsToWriteData) + 257 " failed due to error: " + l_ex.what()); 258 259 // update failed, set return value to failure 260 l_bytesUpdatedOnHardware = constants::FAILURE; 261 } 262 263 // Disable Reboot Guard 264 if (constants::FAILURE == dbusUtility::DisableRebootGuard()) 265 { 266 EventLogger::createAsyncPel( 267 types::ErrorType::DbusFailure, types::SeverityType::Critical, 268 __FILE__, __FUNCTION__, 0, 269 std::string("Failed to disable BMC Reboot Guard while updating " + 270 l_keyWordIdentifier(i_paramsToWriteData)), 271 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 272 } 273 274 return l_bytesUpdatedOnHardware; 275 } 276 277 int Parser::updateVpdKeyword(const types::WriteVpdParams& i_paramsToWriteData) 278 { 279 types::DbusVariantType o_updatedValue; 280 return updateVpdKeyword(i_paramsToWriteData, o_updatedValue); 281 } 282 283 int Parser::updateVpdKeywordOnRedundantPath( 284 const std::string& i_fruPath, 285 const types::WriteVpdParams& i_paramsToWriteData) 286 { 287 try 288 { 289 std::shared_ptr<Parser> l_parserObj = 290 std::make_shared<Parser>(i_fruPath, m_parsedJson); 291 292 std::shared_ptr<ParserInterface> l_vpdParserInstance = 293 l_parserObj->getVpdParserInstance(); 294 295 return l_vpdParserInstance->writeKeywordOnHardware(i_paramsToWriteData); 296 } 297 catch (const std::exception& l_exception) 298 { 299 EventLogger::createSyncPel( 300 types::ErrorType::InvalidVpdMessage, 301 types::SeverityType::Informational, __FILE__, __FUNCTION__, 0, 302 "Error while updating keyword's value on redundant path " + 303 i_fruPath + ", error: " + std::string(l_exception.what()), 304 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 305 return -1; 306 } 307 } 308 309 int Parser::updateVpdKeywordOnHardware( 310 const types::WriteVpdParams& i_paramsToWriteData) 311 { 312 int l_bytesUpdatedOnHardware = constants::FAILURE; 313 314 // A lambda to extract Record : Keyword string from i_paramsToWriteData 315 auto l_keyWordIdentifier = 316 [](const types::WriteVpdParams& i_paramsToWriteData) -> std::string { 317 std::string l_keywordString{}; 318 if (const types::IpzData* l_ipzData = 319 std::get_if<types::IpzData>(&i_paramsToWriteData)) 320 { 321 l_keywordString = 322 std::get<0>(*l_ipzData) + ":" + std::get<1>(*l_ipzData); 323 } 324 else if (const types::KwData* l_kwData = 325 std::get_if<types::KwData>(&i_paramsToWriteData)) 326 { 327 l_keywordString = std::get<0>(*l_kwData); 328 } 329 return l_keywordString; 330 }; 331 332 try 333 { 334 // Enable Reboot Guard 335 if (constants::FAILURE == dbusUtility::EnableRebootGuard()) 336 { 337 EventLogger::createAsyncPel( 338 types::ErrorType::DbusFailure, 339 types::SeverityType::Informational, __FILE__, __FUNCTION__, 0, 340 std::string( 341 "Failed to enable BMC Reboot Guard while updating " + 342 l_keyWordIdentifier(i_paramsToWriteData)), 343 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 344 345 return constants::FAILURE; 346 } 347 348 std::shared_ptr<ParserInterface> l_vpdParserInstance = 349 getVpdParserInstance(); 350 l_bytesUpdatedOnHardware = 351 l_vpdParserInstance->writeKeywordOnHardware(i_paramsToWriteData); 352 } 353 catch (const std::exception& l_exception) 354 { 355 types::ErrorType l_errorType; 356 357 if (typeid(l_exception) == typeid(EccException)) 358 { 359 l_errorType = types::ErrorType::EccCheckFailed; 360 } 361 else 362 { 363 l_errorType = types::ErrorType::InvalidVpdMessage; 364 } 365 366 EventLogger::createAsyncPel( 367 l_errorType, types::SeverityType::Informational, __FILE__, 368 __FUNCTION__, 0, 369 "Error while updating keyword's value on hardware path [" + 370 m_vpdFilePath + "], error: " + std::string(l_exception.what()), 371 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 372 } 373 374 // Disable Reboot Guard 375 if (constants::FAILURE == dbusUtility::DisableRebootGuard()) 376 { 377 EventLogger::createAsyncPel( 378 types::ErrorType::DbusFailure, types::SeverityType::Critical, 379 __FILE__, __FUNCTION__, 0, 380 std::string("Failed to disable BMC Reboot Guard while updating " + 381 l_keyWordIdentifier(i_paramsToWriteData)), 382 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 383 } 384 385 return l_bytesUpdatedOnHardware; 386 } 387 388 } // namespace vpd 389