1 #include "dbus_to_host_effecters.hpp" 2 3 #include "libpldm/pdr.h" 4 #include "libpldm/platform.h" 5 #include "libpldm/requester/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 20 using InternalFailure = 21 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 22 23 constexpr auto hostEffecterJson = "dbus_to_host_effecter.json"; 24 25 void HostEffecterParser::populatePropVals( 26 const Json& dBusValues, std::vector<PropertyValue>& propertyValues, 27 const std::string& propertyType) 28 29 { 30 for (const auto& elem : dBusValues) 31 { 32 auto value = jsonEntryToDbusVal(propertyType, elem); 33 propertyValues.emplace_back(value); 34 } 35 } 36 37 void HostEffecterParser::parseEffecterJson(const std::string& jsonPath) 38 { 39 fs::path jsonDir(jsonPath); 40 if (!fs::exists(jsonDir) || fs::is_empty(jsonDir)) 41 { 42 std::cerr << "Host Effecter json path does not exist, DIR=" << jsonPath 43 << "\n"; 44 return; 45 } 46 47 fs::path jsonFilePath = jsonDir / hostEffecterJson; 48 if (!fs::exists(jsonFilePath)) 49 { 50 std::cerr << "json does not exist, PATH=" << jsonFilePath << "\n"; 51 throw InternalFailure(); 52 } 53 54 std::ifstream jsonFile(jsonFilePath); 55 auto data = Json::parse(jsonFile, nullptr, false); 56 if (data.is_discarded()) 57 { 58 std::cerr << "Parsing json file failed, FILE=" << jsonFilePath << "\n"; 59 throw InternalFailure(); 60 } 61 const Json empty{}; 62 const std::vector<Json> emptyList{}; 63 64 auto entries = data.value("entries", emptyList); 65 for (const auto& entry : entries) 66 { 67 EffecterInfo effecterInfo; 68 effecterInfo.mctpEid = entry.value("mctp_eid", 0xFF); 69 auto jsonEffecterInfo = entry.value("effecter_info", empty); 70 auto effecterId = 71 jsonEffecterInfo.value("effecterID", PLDM_INVALID_EFFECTER_ID); 72 effecterInfo.containerId = jsonEffecterInfo.value("containerID", 0); 73 effecterInfo.entityType = jsonEffecterInfo.value("entityType", 0); 74 effecterInfo.entityInstance = 75 jsonEffecterInfo.value("entityInstance", 0); 76 effecterInfo.compEffecterCnt = 77 jsonEffecterInfo.value("compositeEffecterCount", 0); 78 auto effecters = entry.value("effecters", emptyList); 79 for (const auto& effecter : effecters) 80 { 81 DBusEffecterMapping dbusInfo{}; 82 auto jsonDbusInfo = effecter.value("dbus_info", empty); 83 dbusInfo.dbusMap.objectPath = jsonDbusInfo.value("object_path", ""); 84 dbusInfo.dbusMap.interface = jsonDbusInfo.value("interface", ""); 85 dbusInfo.dbusMap.propertyName = 86 jsonDbusInfo.value("property_name", ""); 87 dbusInfo.dbusMap.propertyType = 88 jsonDbusInfo.value("property_type", ""); 89 Json propertyValues = jsonDbusInfo["property_values"]; 90 91 populatePropVals(propertyValues, dbusInfo.propertyValues, 92 dbusInfo.dbusMap.propertyType); 93 94 const std::vector<uint8_t> emptyStates{}; 95 auto state = effecter.value("state", empty); 96 dbusInfo.state.stateSetId = state.value("id", 0); 97 auto states = state.value("state_values", emptyStates); 98 if (dbusInfo.propertyValues.size() != states.size()) 99 { 100 std::cerr << "Number of states do not match with" 101 << " number of D-Bus property values in the json. " 102 << "Object path " << dbusInfo.dbusMap.objectPath 103 << " and property " << dbusInfo.dbusMap.propertyName 104 << " will not be monitored \n"; 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 const auto& propertyName = hostEffecterInfo[effecterInfoIndex] 128 .dbusInfo[dbusInfoIndex] 129 .dbusMap.propertyName; 130 131 const auto& it = chProperties.find(propertyName); 132 133 if (it == chProperties.end()) 134 { 135 return; 136 } 137 138 if (effecterId == PLDM_INVALID_EFFECTER_ID) 139 { 140 constexpr auto localOrRemote = false; 141 effecterId = findStateEffecterId( 142 pdrRepo, hostEffecterInfo[effecterInfoIndex].entityType, 143 hostEffecterInfo[effecterInfoIndex].entityInstance, 144 hostEffecterInfo[effecterInfoIndex].containerId, 145 hostEffecterInfo[effecterInfoIndex] 146 .dbusInfo[dbusInfoIndex] 147 .state.stateSetId, 148 localOrRemote); 149 if (effecterId == PLDM_INVALID_EFFECTER_ID) 150 { 151 std::cerr << "Effecter id not found in pdr repo \n"; 152 return; 153 } 154 } 155 constexpr auto hostStateInterface = 156 "xyz.openbmc_project.State.Boot.Progress"; 157 constexpr auto hostStatePath = "/xyz/openbmc_project/state/host0"; 158 159 try 160 { 161 auto propVal = dbusHandler->getDbusPropertyVariant( 162 hostStatePath, "BootProgress", hostStateInterface); 163 const auto& currHostState = std::get<std::string>(propVal); 164 if ((currHostState != "xyz.openbmc_project.State.Boot.Progress." 165 "ProgressStages.SystemInitComplete") && 166 (currHostState != "xyz.openbmc_project.State.Boot.Progress." 167 "ProgressStages.OSRunning") && 168 (currHostState != "xyz.openbmc_project.State.Boot.Progress." 169 "ProgressStages.OSStart")) 170 { 171 std::cout << "Host is not up. Current host state: " 172 << currHostState.c_str() << "\n"; 173 return; 174 } 175 } 176 catch (const sdbusplus::exception::exception& e) 177 { 178 std::cerr << "Error in getting current host state. Will still " 179 "continue to set the host effecter \n"; 180 } 181 uint8_t newState{}; 182 try 183 { 184 newState = 185 findNewStateValue(effecterInfoIndex, dbusInfoIndex, it->second); 186 } 187 catch (const std::out_of_range& e) 188 { 189 std::cerr << "new state not found in json" 190 << "\n"; 191 return; 192 } 193 194 std::vector<set_effecter_state_field> stateField; 195 for (uint8_t i = 0; i < hostEffecterInfo[effecterInfoIndex].compEffecterCnt; 196 i++) 197 { 198 if (i == dbusInfoIndex) 199 { 200 stateField.push_back({PLDM_REQUEST_SET, newState}); 201 } 202 else 203 { 204 stateField.push_back({PLDM_NO_CHANGE, 0}); 205 } 206 } 207 int rc{}; 208 try 209 { 210 rc = setHostStateEffecter(effecterInfoIndex, stateField, effecterId); 211 } 212 catch (const std::runtime_error& e) 213 { 214 std::cerr << "Could not set host state effecter \n"; 215 return; 216 } 217 if (rc != PLDM_SUCCESS) 218 { 219 std::cerr << "Could not set the host state effecter, rc= " << rc 220 << " \n"; 221 } 222 } 223 224 uint8_t 225 HostEffecterParser::findNewStateValue(size_t effecterInfoIndex, 226 size_t dbusInfoIndex, 227 const PropertyValue& propertyValue) 228 { 229 const auto& propValues = hostEffecterInfo[effecterInfoIndex] 230 .dbusInfo[dbusInfoIndex] 231 .propertyValues; 232 auto it = std::find(propValues.begin(), propValues.end(), propertyValue); 233 uint8_t newState{}; 234 if (it != propValues.end()) 235 { 236 auto index = std::distance(propValues.begin(), it); 237 newState = hostEffecterInfo[effecterInfoIndex] 238 .dbusInfo[dbusInfoIndex] 239 .state.states[index]; 240 } 241 else 242 { 243 throw std::out_of_range("new state not found in json"); 244 } 245 return newState; 246 } 247 248 int HostEffecterParser::setHostStateEffecter( 249 size_t effecterInfoIndex, std::vector<set_effecter_state_field>& stateField, 250 uint16_t effecterId) 251 { 252 uint8_t& mctpEid = hostEffecterInfo[effecterInfoIndex].mctpEid; 253 uint8_t& compEffCnt = hostEffecterInfo[effecterInfoIndex].compEffecterCnt; 254 auto instanceId = requester->getInstanceId(mctpEid); 255 256 std::vector<uint8_t> requestMsg( 257 sizeof(pldm_msg_hdr) + sizeof(effecterId) + sizeof(compEffCnt) + 258 sizeof(set_effecter_state_field) * compEffCnt, 259 0); 260 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); 261 auto rc = encode_set_state_effecter_states_req( 262 instanceId, effecterId, compEffCnt, stateField.data(), request); 263 264 if (rc != PLDM_SUCCESS) 265 { 266 std::cerr << "Message encode failure. PLDM error code = " << std::hex 267 << std::showbase << rc << "\n"; 268 requester->markFree(mctpEid, instanceId); 269 return rc; 270 } 271 272 auto setStateEffecterStatesRespHandler = 273 [](mctp_eid_t /*eid*/, const pldm_msg* response, size_t respMsgLen) { 274 if (response == nullptr || !respMsgLen) 275 { 276 std::cerr << "Failed to receive response for " 277 << "setStateEffecterStates command \n"; 278 return; 279 } 280 uint8_t completionCode{}; 281 auto rc = decode_set_state_effecter_states_resp( 282 response, respMsgLen, &completionCode); 283 if (rc) 284 { 285 std::cerr << "Failed to decode setStateEffecterStates response," 286 << " rc " << rc << "\n"; 287 pldm::utils::reportError( 288 "xyz.openbmc_project.bmc.pldm.SetHostEffecterFailed"); 289 } 290 if (completionCode) 291 { 292 std::cerr << "Failed to set a Host effecter " 293 << ", cc=" << static_cast<unsigned>(completionCode) 294 << "\n"; 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 std::cerr << "Failed to send request to set an effecter on Host \n"; 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( 318 std::make_unique<sdbusplus::bus::match::match>( 319 pldm::utils::DBusHandler::getBus(), 320 propertiesChanged(objectPath, interface), 321 [this, effecterInfoIndex, dbusInfoIndex, 322 effecterId](sdbusplus::message::message& msg) { 323 DbusChgHostEffecterProps props; 324 std::string iface; 325 msg.read(iface, props); 326 processHostEffecterChangeNotification( 327 props, effecterInfoIndex, dbusInfoIndex, effecterId); 328 })); 329 } 330 331 } // namespace host_effecters 332 } // namespace pldm 333