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