xref: /openbmc/openpower-occ-control/pldm.cpp (revision a8857c50)
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