1 #include "dbus_to_host_effecters.hpp" 2 3 #include <libpldm/pdr.h> 4 #include <libpldm/platform.h> 5 6 #include <phosphor-logging/lg2.hpp> 7 #include <xyz/openbmc_project/Common/error.hpp> 8 #include <xyz/openbmc_project/State/Boot/Progress/client.hpp> 9 #include <xyz/openbmc_project/State/OperatingSystem/Status/server.hpp> 10 11 #include <fstream> 12 13 PHOSPHOR_LOG2_USING; 14 15 using namespace pldm::utils; 16 17 namespace pldm 18 { 19 namespace host_effecters 20 { 21 using InternalFailure = 22 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 23 24 constexpr auto hostEffecterJson = "dbus_to_host_effecter.json"; 25 26 void HostEffecterParser::populatePropVals( 27 const Json& dBusValues, std::vector<PropertyValue>& propertyValues, 28 const std::string& propertyType) 29 30 { 31 for (const auto& elem : dBusValues) 32 { 33 auto value = jsonEntryToDbusVal(propertyType, elem); 34 propertyValues.emplace_back(value); 35 } 36 } 37 38 void HostEffecterParser::parseEffecterJson(const std::string& jsonPath) 39 { 40 fs::path jsonDir(jsonPath); 41 if (!fs::exists(jsonDir) || fs::is_empty(jsonDir)) 42 { 43 error("Effecter json file for remote terminus '{PATH}' does not exist.", 44 "PATH", jsonPath); 45 return; 46 } 47 48 fs::path jsonFilePath = jsonDir / hostEffecterJson; 49 if (!fs::exists(jsonFilePath)) 50 { 51 error("Json at path '{PATH}' does not exist.", "PATH", jsonFilePath); 52 throw InternalFailure(); 53 } 54 55 std::ifstream jsonFile(jsonFilePath); 56 auto data = Json::parse(jsonFile, nullptr, false); 57 if (data.is_discarded()) 58 { 59 error("Failed to parse json file {PATH}", "PATH", jsonFilePath); 60 throw InternalFailure(); 61 } 62 const Json empty{}; 63 const std::vector<Json> emptyList{}; 64 65 auto entries = data.value("entries", emptyList); 66 for (const auto& entry : entries) 67 { 68 EffecterInfo effecterInfo; 69 effecterInfo.mctpEid = entry.value("mctp_eid", 0xFF); 70 auto jsonEffecterInfo = entry.value("effecter_info", empty); 71 auto effecterId = jsonEffecterInfo.value("effecterID", 72 PLDM_INVALID_EFFECTER_ID); 73 effecterInfo.containerId = jsonEffecterInfo.value("containerID", 0); 74 effecterInfo.entityType = jsonEffecterInfo.value("entityType", 0); 75 effecterInfo.entityInstance = jsonEffecterInfo.value("entityInstance", 76 0); 77 effecterInfo.compEffecterCnt = 78 jsonEffecterInfo.value("compositeEffecterCount", 0); 79 auto effecters = entry.value("effecters", emptyList); 80 for (const auto& effecter : effecters) 81 { 82 DBusEffecterMapping dbusInfo{}; 83 auto jsonDbusInfo = effecter.value("dbus_info", empty); 84 dbusInfo.dbusMap.objectPath = jsonDbusInfo.value("object_path", ""); 85 dbusInfo.dbusMap.interface = jsonDbusInfo.value("interface", ""); 86 dbusInfo.dbusMap.propertyName = jsonDbusInfo.value("property_name", 87 ""); 88 dbusInfo.dbusMap.propertyType = jsonDbusInfo.value("property_type", 89 ""); 90 Json propertyValues = jsonDbusInfo["property_values"]; 91 92 populatePropVals(propertyValues, dbusInfo.propertyValues, 93 dbusInfo.dbusMap.propertyType); 94 95 const std::vector<uint8_t> emptyStates{}; 96 auto state = effecter.value("state", empty); 97 dbusInfo.state.stateSetId = state.value("id", 0); 98 auto states = state.value("state_values", emptyStates); 99 if (dbusInfo.propertyValues.size() != states.size()) 100 { 101 error( 102 "Number of states do not match with number of D-Bus property values in the json. Object path at '{PATH}' and property '{PROPERTY}' will not be monitored", 103 "PATH", dbusInfo.dbusMap.objectPath, "PROPERTY", 104 dbusInfo.dbusMap.propertyName); 105 continue; 106 } 107 for (const auto& s : states) 108 { 109 dbusInfo.state.states.emplace_back(s); 110 } 111 112 auto effecterInfoIndex = hostEffecterInfo.size(); 113 auto dbusInfoIndex = effecterInfo.dbusInfo.size(); 114 createHostEffecterMatch( 115 dbusInfo.dbusMap.objectPath, dbusInfo.dbusMap.interface, 116 effecterInfoIndex, dbusInfoIndex, effecterId); 117 effecterInfo.dbusInfo.emplace_back(std::move(dbusInfo)); 118 } 119 hostEffecterInfo.emplace_back(std::move(effecterInfo)); 120 } 121 } 122 123 void HostEffecterParser::processHostEffecterChangeNotification( 124 const DbusChgHostEffecterProps& chProperties, size_t effecterInfoIndex, 125 size_t dbusInfoIndex, uint16_t effecterId) 126 { 127 using BootProgress = 128 sdbusplus::client::xyz::openbmc_project::state::boot::Progress<>; 129 130 const auto& propertyName = hostEffecterInfo[effecterInfoIndex] 131 .dbusInfo[dbusInfoIndex] 132 .dbusMap.propertyName; 133 134 const auto& it = chProperties.find(propertyName); 135 136 if (it == chProperties.end()) 137 { 138 return; 139 } 140 141 if (effecterId == PLDM_INVALID_EFFECTER_ID) 142 { 143 constexpr auto localOrRemote = false; 144 effecterId = findStateEffecterId( 145 pdrRepo, hostEffecterInfo[effecterInfoIndex].entityType, 146 hostEffecterInfo[effecterInfoIndex].entityInstance, 147 hostEffecterInfo[effecterInfoIndex].containerId, 148 hostEffecterInfo[effecterInfoIndex] 149 .dbusInfo[dbusInfoIndex] 150 .state.stateSetId, 151 localOrRemote); 152 if (effecterId == PLDM_INVALID_EFFECTER_ID) 153 { 154 error( 155 "Effecter ID '{EFFECTERID}' of entity type '{TYPE}', entityInstance '{INSTANCE}' and containerID '{CONTAINER_ID}' not found in pdr repo", 156 "EFFECTERID", effecterId, "TYPE", 157 hostEffecterInfo[effecterInfoIndex].entityType, "INSTANCE", 158 hostEffecterInfo[effecterInfoIndex].entityInstance, 159 "CONTAINER_ID", 160 hostEffecterInfo[effecterInfoIndex].containerId); 161 return; 162 } 163 } 164 constexpr auto hostStatePath = "/xyz/openbmc_project/state/host0"; 165 166 try 167 { 168 auto propVal = dbusHandler->getDbusPropertyVariant( 169 hostStatePath, "BootProgress", BootProgress::interface); 170 171 using Stages = BootProgress::ProgressStages; 172 auto currHostState = sdbusplus::message::convert_from_string<Stages>( 173 std::get<std::string>(propVal)) 174 .value(); 175 176 if (currHostState != Stages::SystemInitComplete && 177 currHostState != Stages::OSRunning && 178 currHostState != Stages::SystemSetup) 179 { 180 info( 181 "Remote terminus is not up/active, current remote terminus state is: '{CURRENT_HOST_STATE}'", 182 "CURRENT_HOST_STATE", currHostState); 183 return; 184 } 185 } 186 catch (const sdbusplus::exception_t& e) 187 { 188 error( 189 "Error in getting current remote terminus state. Will still continue to set the remote terminus effecter, error - {ERROR}", 190 "ERROR", e); 191 } 192 uint8_t newState{}; 193 try 194 { 195 newState = findNewStateValue(effecterInfoIndex, dbusInfoIndex, 196 it->second); 197 } 198 catch (const std::out_of_range& e) 199 { 200 error("Failed to find new state '{NEW_STATE}' in json, error - {ERROR}", 201 "ERROR", e, "NEW_STATE", newState); 202 return; 203 } 204 205 std::vector<set_effecter_state_field> stateField; 206 for (uint8_t i = 0; i < hostEffecterInfo[effecterInfoIndex].compEffecterCnt; 207 i++) 208 { 209 if (i == dbusInfoIndex) 210 { 211 stateField.push_back({PLDM_REQUEST_SET, newState}); 212 } 213 else 214 { 215 stateField.push_back({PLDM_NO_CHANGE, 0}); 216 } 217 } 218 int rc{}; 219 try 220 { 221 rc = setHostStateEffecter(effecterInfoIndex, stateField, effecterId); 222 } 223 catch (const std::runtime_error& e) 224 { 225 error( 226 "Failed to set remote terminus state effecter for effecter ID '{EFFECTERID}', error - {ERROR}", 227 "ERROR", e, "EFFECTERID", effecterId); 228 return; 229 } 230 if (rc != PLDM_SUCCESS) 231 { 232 error( 233 "Failed to set the remote terminus state effecter for effecter ID '{EFFECTERID}', response code '{RC}'", 234 "EFFECTERID", effecterId, "RC", rc); 235 } 236 } 237 238 uint8_t 239 HostEffecterParser::findNewStateValue(size_t effecterInfoIndex, 240 size_t dbusInfoIndex, 241 const PropertyValue& propertyValue) 242 { 243 const auto& propValues = hostEffecterInfo[effecterInfoIndex] 244 .dbusInfo[dbusInfoIndex] 245 .propertyValues; 246 auto it = std::find(propValues.begin(), propValues.end(), propertyValue); 247 uint8_t newState{}; 248 if (it != propValues.end()) 249 { 250 auto index = std::distance(propValues.begin(), it); 251 newState = hostEffecterInfo[effecterInfoIndex] 252 .dbusInfo[dbusInfoIndex] 253 .state.states[index]; 254 } 255 else 256 { 257 throw std::out_of_range("new state not found in json"); 258 } 259 return newState; 260 } 261 262 int HostEffecterParser::setHostStateEffecter( 263 size_t effecterInfoIndex, std::vector<set_effecter_state_field>& stateField, 264 uint16_t effecterId) 265 { 266 uint8_t& mctpEid = hostEffecterInfo[effecterInfoIndex].mctpEid; 267 uint8_t& compEffCnt = hostEffecterInfo[effecterInfoIndex].compEffecterCnt; 268 auto instanceId = instanceIdDb->next(mctpEid); 269 270 std::vector<uint8_t> requestMsg( 271 sizeof(pldm_msg_hdr) + sizeof(effecterId) + sizeof(compEffCnt) + 272 sizeof(set_effecter_state_field) * compEffCnt, 273 0); 274 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); 275 auto rc = encode_set_state_effecter_states_req( 276 instanceId, effecterId, compEffCnt, stateField.data(), request); 277 278 if (rc != PLDM_SUCCESS) 279 { 280 error( 281 "Failed to encode set state effecter states message for effecter ID '{EFFECTERID}' and instanceID '{INSTANCE}' with response code '{RC}'", 282 "EFFECTERID", effecterId, "INSTANCE", instanceId, "RC", lg2::hex, 283 rc); 284 instanceIdDb->free(mctpEid, instanceId); 285 return rc; 286 } 287 288 auto setStateEffecterStatesRespHandler = 289 [](mctp_eid_t /*eid*/, const pldm_msg* response, size_t respMsgLen) { 290 if (response == nullptr || !respMsgLen) 291 { 292 error( 293 "Failed to receive response for setting state effecter states."); 294 return; 295 } 296 uint8_t completionCode{}; 297 auto rc = decode_set_state_effecter_states_resp(response, respMsgLen, 298 &completionCode); 299 if (rc) 300 { 301 error( 302 "Failed to decode response of set state effecter states, response code '{RC}'", 303 "RC", rc); 304 pldm::utils::reportError( 305 "xyz.openbmc_project.bmc.pldm.SetHostEffecterFailed"); 306 } 307 if (completionCode) 308 { 309 error( 310 "Failed to set a remote terminus effecter, completion code '{CC}'", 311 "CC", completionCode); 312 pldm::utils::reportError( 313 "xyz.openbmc_project.bmc.pldm.SetHostEffecterFailed"); 314 } 315 }; 316 317 rc = handler->registerRequest( 318 mctpEid, instanceId, PLDM_PLATFORM, PLDM_SET_STATE_EFFECTER_STATES, 319 std::move(requestMsg), std::move(setStateEffecterStatesRespHandler)); 320 if (rc) 321 { 322 error( 323 "Failed to send request to set an effecter on remote terminus for effecter ID '{EFFECTERID}', response code '{RC}'", 324 "EFFECTERID", effecterId, "RC", rc); 325 } 326 return rc; 327 } 328 329 void HostEffecterParser::createHostEffecterMatch(const std::string& objectPath, 330 const std::string& interface, 331 size_t effecterInfoIndex, 332 size_t dbusInfoIndex, 333 uint16_t effecterId) 334 { 335 using namespace sdbusplus::bus::match::rules; 336 effecterInfoMatch.emplace_back(std::make_unique<sdbusplus::bus::match_t>( 337 pldm::utils::DBusHandler::getBus(), 338 propertiesChanged(objectPath, interface), 339 [this, effecterInfoIndex, dbusInfoIndex, 340 effecterId](sdbusplus::message_t& msg) { 341 DbusChgHostEffecterProps props; 342 std::string iface; 343 msg.read(iface, props); 344 processHostEffecterChangeNotification(props, effecterInfoIndex, 345 dbusInfoIndex, effecterId); 346 })); 347 } 348 349 } // namespace host_effecters 350 } // namespace pldm 351