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