blob: d22a73ddb3282f7a834d0cee22fb36ad9b762775 [file] [log] [blame]
Patrick Williams05e95592021-09-02 09:28:14 -05001#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
12namespace pldm
13{
14
15using sdbusplus::exception::SdBusError;
16using namespace phosphor::logging;
17
18void 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
72void 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
141void 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
159void 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
208std::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
244void 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