1 #include "pldm.hpp" 2 3 #include "file.hpp" 4 5 #include <fmt/core.h> 6 #include <libpldm/entity.h> 7 #include <libpldm/platform.h> 8 #include <libpldm/state_set.h> 9 10 #include <phosphor-logging/log.hpp> 11 12 namespace pldm 13 { 14 15 using sdbusplus::exception::SdBusError; 16 using namespace phosphor::logging; 17 18 void Interface::fetchOCCSensorInfo(const PdrList& pdrs, 19 SensorToOCCInstance& sensorInstanceMap, 20 SensorOffset& sensorOffset) 21 { 22 bool offsetFound = false; 23 auto pdr = 24 reinterpret_cast<const pldm_state_sensor_pdr*>(pdrs.front().data()); 25 auto possibleStatesPtr = pdr->possible_states; 26 for (auto offset = 0; offset < pdr->composite_sensor_count; offset++) 27 { 28 auto possibleStates = 29 reinterpret_cast<const state_sensor_possible_states*>( 30 possibleStatesPtr); 31 32 if (possibleStates->state_set_id == 33 PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS) 34 { 35 sensorOffset = offset; 36 offsetFound = true; 37 break; 38 } 39 possibleStatesPtr += sizeof(possibleStates->state_set_id) + 40 sizeof(possibleStates->possible_states_size) + 41 possibleStates->possible_states_size; 42 } 43 44 if (!offsetFound) 45 { 46 log<level::ERR>("pldm: OCC state sensor PDR with StateSetId " 47 "PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS not found"); 48 return; 49 } 50 51 // To order SensorID based on the EntityInstance 52 std::map<EntityInstance, SensorID> entityInstMap{}; 53 for (auto& pdr : pdrs) 54 { 55 auto pdrPtr = 56 reinterpret_cast<const pldm_state_sensor_pdr*>(pdr.data()); 57 entityInstMap.emplace( 58 static_cast<EntityInstance>(pdrPtr->entity_instance), 59 static_cast<SensorID>(pdrPtr->sensor_id)); 60 } 61 62 open_power::occ::instanceID count = start; 63 for (auto const& pair : entityInstMap) 64 { 65 sensorInstanceMap.emplace(pair.second, count); 66 count++; 67 } 68 } 69 70 void Interface::sensorEvent(sdbusplus::message::message& msg) 71 { 72 if (!isOCCSensorCacheValid()) 73 { 74 PdrList pdrs{}; 75 76 try 77 { 78 auto method = bus.new_method_call( 79 "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm", 80 "xyz.openbmc_project.PLDM.PDR", "FindStateSensorPDR"); 81 method.append(tid, (uint16_t)PLDM_ENTITY_PROC_MODULE, 82 (uint16_t)PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS); 83 84 auto responseMsg = bus.call(method); 85 responseMsg.read(pdrs); 86 } 87 catch (const SdBusError& e) 88 { 89 log<level::ERR>("pldm: Failed to fetch the OCC state sensor PDRs", 90 entry("ERROR=%s", e.what())); 91 } 92 93 if (!pdrs.size()) 94 { 95 log<level::ERR>("pldm: OCC state sensor PDRs not present"); 96 return; 97 } 98 99 fetchOCCSensorInfo(pdrs, sensorToOCCInstance, sensorOffset); 100 } 101 102 TerminusID tid{}; 103 SensorID sensorId{}; 104 SensorOffset msgSensorOffset{}; 105 EventState eventState{}; 106 EventState previousEventState{}; 107 108 msg.read(tid, sensorId, msgSensorOffset, eventState, previousEventState); 109 110 auto sensorEntry = sensorToOCCInstance.find(sensorId); 111 if (sensorEntry == sensorToOCCInstance.end() || 112 (msgSensorOffset != sensorOffset)) 113 { 114 // No action for non matching sensorEvents 115 return; 116 } 117 118 if (eventState == static_cast<EventState>( 119 PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_IN_SERVICE)) 120 { 121 log<level::INFO>( 122 fmt::format("PLDM: OCC{} is RUNNING", sensorEntry->second).c_str()); 123 callBack(sensorEntry->second, true); 124 } 125 else if (eventState == 126 static_cast<EventState>( 127 PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_STOPPED)) 128 { 129 log<level::INFO>( 130 fmt::format("PLDM: OCC{} has now STOPPED", sensorEntry->second) 131 .c_str()); 132 callBack(sensorEntry->second, false); 133 } 134 135 return; 136 } 137 138 void Interface::hostStateEvent(sdbusplus::message::message& msg) 139 { 140 std::map<std::string, std::variant<std::string>> properties{}; 141 std::string interface; 142 msg.read(interface, properties); 143 const auto stateEntry = properties.find("CurrentHostState"); 144 if (stateEntry != properties.end()) 145 { 146 auto stateEntryValue = stateEntry->second; 147 auto propVal = std::get<std::string>(stateEntryValue); 148 if (propVal == "xyz.openbmc_project.State.Host.HostState.Off") 149 { 150 sensorToOCCInstance.clear(); 151 occInstanceToEffecter.clear(); 152 } 153 } 154 } 155 156 void Interface::fetchOCCEffecterInfo( 157 const PdrList& pdrs, OccInstanceToEffecter& instanceToEffecterMap, 158 CompositeEffecterCount& count, uint8_t& bootRestartPos) 159 { 160 bool offsetFound = false; 161 auto pdr = 162 reinterpret_cast<const pldm_state_effecter_pdr*>(pdrs.front().data()); 163 auto possibleStatesPtr = pdr->possible_states; 164 for (auto offset = 0; offset < pdr->composite_effecter_count; offset++) 165 { 166 auto possibleStates = 167 reinterpret_cast<const state_effecter_possible_states*>( 168 possibleStatesPtr); 169 170 if (possibleStates->state_set_id == PLDM_STATE_SET_BOOT_RESTART_CAUSE) 171 { 172 bootRestartPos = offset; 173 effecterCount = pdr->composite_effecter_count; 174 offsetFound = true; 175 break; 176 } 177 possibleStatesPtr += sizeof(possibleStates->state_set_id) + 178 sizeof(possibleStates->possible_states_size) + 179 possibleStates->possible_states_size; 180 } 181 182 if (!offsetFound) 183 { 184 return; 185 } 186 187 std::map<EntityInstance, EffecterID> entityInstMap{}; 188 for (auto& pdr : pdrs) 189 { 190 auto pdrPtr = 191 reinterpret_cast<const pldm_state_effecter_pdr*>(pdr.data()); 192 entityInstMap.emplace( 193 static_cast<EntityInstance>(pdrPtr->entity_instance), 194 static_cast<SensorID>(pdrPtr->effecter_id)); 195 } 196 197 open_power::occ::instanceID position = start; 198 for (auto const& pair : entityInstMap) 199 { 200 occInstanceToEffecter.emplace(position, pair.second); 201 position++; 202 } 203 } 204 205 std::vector<uint8_t> 206 Interface::prepareSetEffecterReq(uint8_t instanceId, EffecterID effecterId, 207 CompositeEffecterCount effecterCount, 208 uint8_t bootRestartPos) 209 { 210 std::vector<uint8_t> request( 211 sizeof(pldm_msg_hdr) + sizeof(effecterId) + sizeof(effecterCount) + 212 (effecterCount * sizeof(set_effecter_state_field))); 213 auto requestMsg = reinterpret_cast<pldm_msg*>(request.data()); 214 std::vector<set_effecter_state_field> stateField; 215 216 for (uint8_t effecterPos = 0; effecterPos < effecterCount; effecterPos++) 217 { 218 if (effecterPos == bootRestartPos) 219 { 220 stateField.emplace_back(set_effecter_state_field{ 221 PLDM_REQUEST_SET, 222 PLDM_STATE_SET_BOOT_RESTART_CAUSE_WARM_RESET}); 223 } 224 else 225 { 226 stateField.emplace_back( 227 set_effecter_state_field{PLDM_NO_CHANGE, 0}); 228 } 229 } 230 auto rc = encode_set_state_effecter_states_req( 231 instanceId, effecterId, effecterCount, stateField.data(), requestMsg); 232 if (rc != PLDM_SUCCESS) 233 { 234 log<level::ERR>("encode set effecter states request returned error ", 235 entry("RC=%d", rc)); 236 request.clear(); 237 } 238 return request; 239 } 240 241 void Interface::resetOCC(open_power::occ::instanceID occInstanceId) 242 { 243 if (!isPDREffecterCacheValid()) 244 { 245 PdrList pdrs{}; 246 247 try 248 { 249 auto method = bus.new_method_call( 250 "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm", 251 "xyz.openbmc_project.PLDM.PDR", "FindStateEffecterPDR"); 252 method.append(tid, (uint16_t)PLDM_ENTITY_PROC_MODULE, 253 (uint16_t)PLDM_STATE_SET_BOOT_RESTART_CAUSE); 254 255 auto responseMsg = bus.call(method); 256 responseMsg.read(pdrs); 257 } 258 catch (const SdBusError& e) 259 { 260 log<level::ERR>("pldm: Failed to fetch the OCC state effecter PDRs", 261 entry("ERROR=%s", e.what())); 262 } 263 264 if (!pdrs.size()) 265 { 266 log<level::ERR>("pldm: OCC state effecter PDRs not present"); 267 return; 268 } 269 270 fetchOCCEffecterInfo(pdrs, occInstanceToEffecter, effecterCount, 271 bootRestartPosition); 272 } 273 274 // Find the matching effecter for the OCC instance 275 auto effecterEntry = occInstanceToEffecter.find(occInstanceId); 276 if (effecterEntry == occInstanceToEffecter.end()) 277 { 278 log<level::ERR>( 279 "pldm: Failed to find a matching effecter for OCC instance", 280 entry("OCC_INSTANCE_ID=%d", occInstanceId)); 281 282 return; 283 } 284 285 uint8_t instanceId{}; 286 287 try 288 { 289 auto method = bus.new_method_call( 290 "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm", 291 "xyz.openbmc_project.PLDM.Requester", "GetInstanceId"); 292 method.append(mctpEid); 293 auto reply = bus.call(method); 294 reply.read(instanceId); 295 } 296 catch (const SdBusError& e) 297 { 298 log<level::ERR>("pldm: GetInstanceId returned error", 299 entry("ERROR=%s", e.what())); 300 return; 301 } 302 303 // Prepare the SetStateEffecterStates request to reset the OCC 304 auto request = prepareSetEffecterReq(instanceId, effecterEntry->second, 305 effecterCount, bootRestartPosition); 306 307 if (request.empty()) 308 { 309 log<level::ERR>("pldm: SetStateEffecterStates request message empty"); 310 return; 311 } 312 313 // Connect to MCTP scoket 314 int fd = pldm_open(); 315 if (fd == -1) 316 { 317 log<level::ERR>("pldm: Failed to connect to MCTP socket"); 318 return; 319 } 320 open_power::occ::FileDescriptor fileFd(fd); 321 322 // Send the PLDM request message to HBRT 323 uint8_t* response = nullptr; 324 size_t responseSize{}; 325 auto rc = pldm_send_recv(mctpEid, fileFd(), request.data(), request.size(), 326 &response, &responseSize); 327 std::unique_ptr<uint8_t, decltype(std::free)*> responsePtr{response, 328 std::free}; 329 if (rc) 330 { 331 log<level::ERR>("pldm: pldm_send_recv failed for OCC reset", 332 entry("RC=%d", rc)); 333 } 334 335 uint8_t completionCode{}; 336 auto responseMsg = reinterpret_cast<const pldm_msg*>(responsePtr.get()); 337 auto rcDecode = decode_set_state_effecter_states_resp( 338 responseMsg, responseSize - sizeof(pldm_msg_hdr), &completionCode); 339 if (rcDecode || completionCode) 340 { 341 log<level::ERR>( 342 "pldm: decode_set_state_effecter_states_resp returned error", 343 entry("RC=%d", rcDecode), 344 entry("COMPLETION_CODE=%d", completionCode)); 345 } 346 347 return; 348 } 349 350 } // namespace pldm 351