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