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