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 <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 using namespace pldm::utils; 14 15 namespace pldm 16 { 17 namespace host_effecters 18 { 19 using InternalFailure = 20 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 21 22 constexpr auto hostEffecterJson = "dbus_to_host_effecter.json"; 23 24 void HostEffecterParser::populatePropVals( 25 const Json& dBusValues, std::vector<PropertyValue>& propertyValues, 26 const std::string& propertyType) 27 28 { 29 for (const auto& elem : dBusValues) 30 { 31 auto value = jsonEntryToDbusVal(propertyType, elem); 32 propertyValues.emplace_back(value); 33 } 34 } 35 36 void HostEffecterParser::parseEffecterJson(const std::string& jsonPath) 37 { 38 fs::path jsonDir(jsonPath); 39 if (!fs::exists(jsonDir) || fs::is_empty(jsonDir)) 40 { 41 std::cerr << "Host Effecter json path does not exist, DIR=" << jsonPath 42 << "\n"; 43 return; 44 } 45 46 fs::path jsonFilePath = jsonDir / hostEffecterJson; 47 if (!fs::exists(jsonFilePath)) 48 { 49 std::cerr << "json does not exist, PATH=" << jsonFilePath << "\n"; 50 throw InternalFailure(); 51 } 52 53 std::ifstream jsonFile(jsonFilePath); 54 auto data = Json::parse(jsonFile, nullptr, false); 55 if (data.is_discarded()) 56 { 57 std::cerr << "Parsing json file failed, FILE=" << jsonFilePath << "\n"; 58 throw InternalFailure(); 59 } 60 const Json empty{}; 61 const std::vector<Json> emptyList{}; 62 63 auto entries = data.value("entries", emptyList); 64 for (const auto& entry : entries) 65 { 66 EffecterInfo effecterInfo; 67 effecterInfo.mctpEid = entry.value("mctp_eid", 0xFF); 68 auto jsonEffecterInfo = entry.value("effecter_info", empty); 69 auto effecterId = 70 jsonEffecterInfo.value("effecterID", PLDM_INVALID_EFFECTER_ID); 71 effecterInfo.containerId = jsonEffecterInfo.value("containerID", 0); 72 effecterInfo.entityType = jsonEffecterInfo.value("entityType", 0); 73 effecterInfo.entityInstance = 74 jsonEffecterInfo.value("entityInstance", 0); 75 effecterInfo.compEffecterCnt = 76 jsonEffecterInfo.value("compositeEffecterCount", 0); 77 auto effecters = entry.value("effecters", emptyList); 78 for (const auto& effecter : effecters) 79 { 80 DBusEffecterMapping dbusInfo{}; 81 auto jsonDbusInfo = effecter.value("dbus_info", empty); 82 dbusInfo.dbusMap.objectPath = jsonDbusInfo.value("object_path", ""); 83 dbusInfo.dbusMap.interface = jsonDbusInfo.value("interface", ""); 84 dbusInfo.dbusMap.propertyName = 85 jsonDbusInfo.value("property_name", ""); 86 dbusInfo.dbusMap.propertyType = 87 jsonDbusInfo.value("property_type", ""); 88 Json propertyValues = jsonDbusInfo["property_values"]; 89 90 populatePropVals(propertyValues, dbusInfo.propertyValues, 91 dbusInfo.dbusMap.propertyType); 92 93 const std::vector<uint8_t> emptyStates{}; 94 auto state = effecter.value("state", empty); 95 dbusInfo.state.stateSetId = state.value("id", 0); 96 auto states = state.value("state_values", emptyStates); 97 if (dbusInfo.propertyValues.size() != states.size()) 98 { 99 std::cerr << "Number of states do not match with" 100 << " number of D-Bus property values in the json. " 101 << "Object path " << dbusInfo.dbusMap.objectPath 102 << " and property " << dbusInfo.dbusMap.propertyName 103 << " will not be monitored \n"; 104 continue; 105 } 106 for (const auto& s : states) 107 { 108 dbusInfo.state.states.emplace_back(s); 109 } 110 111 auto effecterInfoIndex = hostEffecterInfo.size(); 112 auto dbusInfoIndex = effecterInfo.dbusInfo.size(); 113 createHostEffecterMatch( 114 dbusInfo.dbusMap.objectPath, dbusInfo.dbusMap.interface, 115 effecterInfoIndex, dbusInfoIndex, effecterId); 116 effecterInfo.dbusInfo.emplace_back(std::move(dbusInfo)); 117 } 118 hostEffecterInfo.emplace_back(std::move(effecterInfo)); 119 } 120 } 121 122 void HostEffecterParser::processHostEffecterChangeNotification( 123 const DbusChgHostEffecterProps& chProperties, size_t effecterInfoIndex, 124 size_t dbusInfoIndex, uint16_t effecterId) 125 { 126 const auto& propertyName = hostEffecterInfo[effecterInfoIndex] 127 .dbusInfo[dbusInfoIndex] 128 .dbusMap.propertyName; 129 130 const auto& it = chProperties.find(propertyName); 131 132 if (it == chProperties.end()) 133 { 134 return; 135 } 136 137 if (effecterId == PLDM_INVALID_EFFECTER_ID) 138 { 139 constexpr auto localOrRemote = false; 140 effecterId = findStateEffecterId( 141 pdrRepo, hostEffecterInfo[effecterInfoIndex].entityType, 142 hostEffecterInfo[effecterInfoIndex].entityInstance, 143 hostEffecterInfo[effecterInfoIndex].containerId, 144 hostEffecterInfo[effecterInfoIndex] 145 .dbusInfo[dbusInfoIndex] 146 .state.stateSetId, 147 localOrRemote); 148 if (effecterId == PLDM_INVALID_EFFECTER_ID) 149 { 150 std::cerr << "Effecter id not found in pdr repo \n"; 151 return; 152 } 153 } 154 constexpr auto hostStateInterface = 155 "xyz.openbmc_project.State.Boot.Progress"; 156 constexpr auto hostStatePath = "/xyz/openbmc_project/state/host0"; 157 158 try 159 { 160 auto propVal = dbusHandler->getDbusPropertyVariant( 161 hostStatePath, "BootProgress", hostStateInterface); 162 const auto& currHostState = std::get<std::string>(propVal); 163 if ((currHostState != "xyz.openbmc_project.State.Boot.Progress." 164 "ProgressStages.SystemInitComplete") && 165 (currHostState != "xyz.openbmc_project.State.Boot.Progress." 166 "ProgressStages.OSRunning") && 167 (currHostState != "xyz.openbmc_project.State.Boot.Progress." 168 "ProgressStages.OSStart") && 169 (currHostState != "xyz.openbmc_project.State.Boot.Progress." 170 "ProgressStages.SystemSetup")) 171 { 172 std::cout << "Host is not up. Current host state: " 173 << currHostState.c_str() << "\n"; 174 return; 175 } 176 } 177 catch (const sdbusplus::exception_t& e) 178 { 179 std::cerr << "Error in getting current host state. Will still " 180 "continue to set the host effecter - " 181 << e.what() << std::endl; 182 } 183 uint8_t newState{}; 184 try 185 { 186 newState = 187 findNewStateValue(effecterInfoIndex, dbusInfoIndex, it->second); 188 } 189 catch (const std::out_of_range& e) 190 { 191 std::cerr << "new state not found in json" 192 << "\n"; 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 std::cerr << "Could not set host state effecter \n"; 217 return; 218 } 219 if (rc != PLDM_SUCCESS) 220 { 221 std::cerr << "Could not set the host state effecter, rc= " << rc 222 << " \n"; 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 = requester->getInstanceId(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 std::cerr << "Message encode failure. PLDM error code = " << std::hex 269 << std::showbase << rc << "\n"; 270 requester->markFree(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 std::cerr << "Failed to receive response for " 279 << "setStateEffecterStates command \n"; 280 return; 281 } 282 uint8_t completionCode{}; 283 auto rc = decode_set_state_effecter_states_resp( 284 response, respMsgLen, &completionCode); 285 if (rc) 286 { 287 std::cerr << "Failed to decode setStateEffecterStates response," 288 << " rc " << rc << "\n"; 289 pldm::utils::reportError( 290 "xyz.openbmc_project.bmc.pldm.SetHostEffecterFailed"); 291 } 292 if (completionCode) 293 { 294 std::cerr << "Failed to set a Host effecter " 295 << ", cc=" << static_cast<unsigned>(completionCode) 296 << "\n"; 297 pldm::utils::reportError( 298 "xyz.openbmc_project.bmc.pldm.SetHostEffecterFailed"); 299 } 300 }; 301 302 rc = handler->registerRequest( 303 mctpEid, instanceId, PLDM_PLATFORM, PLDM_SET_STATE_EFFECTER_STATES, 304 std::move(requestMsg), std::move(setStateEffecterStatesRespHandler)); 305 if (rc) 306 { 307 std::cerr << "Failed to send request to set an effecter on Host \n"; 308 } 309 return rc; 310 } 311 312 void HostEffecterParser::createHostEffecterMatch(const std::string& objectPath, 313 const std::string& interface, 314 size_t effecterInfoIndex, 315 size_t dbusInfoIndex, 316 uint16_t effecterId) 317 { 318 using namespace sdbusplus::bus::match::rules; 319 effecterInfoMatch.emplace_back(std::make_unique<sdbusplus::bus::match_t>( 320 pldm::utils::DBusHandler::getBus(), 321 propertiesChanged(objectPath, interface), 322 [this, effecterInfoIndex, dbusInfoIndex, 323 effecterId](sdbusplus::message_t& msg) { 324 DbusChgHostEffecterProps props; 325 std::string iface; 326 msg.read(iface, props); 327 processHostEffecterChangeNotification(props, effecterInfoIndex, 328 dbusInfoIndex, effecterId); 329 })); 330 } 331 332 } // namespace host_effecters 333 } // namespace pldm 334