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