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