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