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