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