blob: c2f20516e1bc51443d2e05b91a4621d1c0dea924 [file] [log] [blame]
Chicago Duan184f6022020-04-17 11:30:49 +08001#include "config.h"
2
3#include "softoff.hpp"
4
5#include "libpldm/entity.h"
6#include "libpldm/platform.h"
7#include "libpldm/requester/pldm.h"
8#include "libpldm/state_set.h"
9
10#include "common/utils.hpp"
11
12#include <sdbusplus/bus.hpp>
13#include <sdeventplus/clock.hpp>
14#include <sdeventplus/source/io.hpp>
15#include <sdeventplus/source/time.hpp>
16
17#include <array>
18#include <iostream>
19
20namespace pldm
21{
22
23using namespace sdeventplus;
24using namespace sdeventplus::source;
25constexpr auto clockId = sdeventplus::ClockId::RealTime;
26using Clock = Clock<clockId>;
27using Timer = Time<clockId>;
28
29using sdbusplus::exception::SdBusError;
30
31constexpr pldm::pdr::TerminusID TID = 0; // TID will be implemented later.
32namespace sdbusRule = sdbusplus::bus::match::rules;
33
34SoftPowerOff::SoftPowerOff(sdbusplus::bus::bus& bus, sd_event* event) :
35 bus(bus), timer(event)
36{
37 auto rc = getHostState();
38 if (hasError || completed)
39 {
40 return;
41 }
42
43 rc = getEffecterID();
44 if (rc != PLDM_SUCCESS)
45 {
46 hasError = true;
47 return;
48 }
49
50 rc = getSensorInfo();
51 if (rc != PLDM_SUCCESS)
52 {
53 std::cerr << "Message get Sensor PDRs error. PLDM "
54 "error code = "
55 << std::hex << std::showbase << rc << "\n";
56 hasError = true;
57 return;
58 }
59
60 // Matches on the pldm StateSensorEvent signal
61 pldmEventSignal = std::make_unique<sdbusplus::bus::match_t>(
62 bus,
63 sdbusRule::type::signal() + sdbusRule::member("StateSensorEvent") +
64 sdbusRule::path("/xyz/openbmc_project/pldm") +
65 sdbusRule::interface("xyz.openbmc_project.PLDM.Event"),
66 std::bind(std::mem_fn(&SoftPowerOff::hostSoftOffComplete), this,
67 std::placeholders::_1));
68}
69
70int SoftPowerOff::getHostState()
71{
72 try
73 {
74 pldm::utils::PropertyValue propertyValue =
75 pldm::utils::DBusHandler().getDbusPropertyVariant(
76 "/xyz/openbmc_project/state/host0", "CurrentHostState",
77 "xyz.openbmc_project.State.Host");
78
79 if (std::get<std::string>(propertyValue) !=
80 "xyz.openbmc_project.State.Host.HostState.Running")
81 {
82 // Host state is not "Running", this app should return success
83 completed = true;
84 return PLDM_SUCCESS;
85 }
86 }
87 catch (const std::exception& e)
88 {
89 std::cerr << "PLDM host soft off: Can't get current host state.\n";
90 hasError = true;
91 return PLDM_ERROR;
92 }
93
94 return PLDM_SUCCESS;
95}
96
97void SoftPowerOff::hostSoftOffComplete(sdbusplus::message::message& msg)
98{
99 pldm::pdr::TerminusID msgTID;
100 pldm::pdr::SensorID msgSensorID;
101 pldm::pdr::SensorOffset msgSensorOffset;
102 pldm::pdr::EventState msgEventState;
103 pldm::pdr::EventState msgPreviousEventState;
104
105 // Read the msg and populate each variable
106 msg.read(msgTID, msgSensorID, msgSensorOffset, msgEventState,
107 msgPreviousEventState);
108
109 if (msgSensorID == sensorID && msgSensorOffset == sensorOffset &&
110 msgEventState == PLDM_SW_TERM_GRACEFUL_SHUTDOWN)
111 {
112 // Receive Graceful shutdown completion event message. Disable the timer
113 auto rc = timer.stop();
114 if (rc < 0)
115 {
116 std::cerr << "PLDM soft off: Failure to STOP the timer. ERRNO="
117 << rc << "\n";
118 }
119
120 // This marks the completion of pldm soft power off.
121 completed = true;
122 }
123}
124
125int SoftPowerOff::getEffecterID()
126{
127 auto& bus = pldm::utils::DBusHandler::getBus();
128
129 // VMM is a logical entity, so the bit 15 in entity type is set.
130 pdr::EntityType entityType = PLDM_ENTITY_VIRTUAL_MACHINE_MANAGER | 0x8000;
131
132 try
133 {
134 std::vector<std::vector<uint8_t>> VMMResponse{};
135 auto VMMMethod = bus.new_method_call(
136 "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",
137 "xyz.openbmc_project.PLDM.PDR", "FindStateEffecterPDR");
138 VMMMethod.append(TID, entityType,
139 (uint16_t)PLDM_STATE_SET_SW_TERMINATION_STATUS);
140
141 auto VMMResponseMsg = bus.call(VMMMethod);
142
143 VMMResponseMsg.read(VMMResponse);
144 if (VMMResponse.size() != 0)
145 {
146 for (auto& rep : VMMResponse)
147 {
148 auto VMMPdr =
149 reinterpret_cast<pldm_state_effecter_pdr*>(rep.data());
150 effecterID = VMMPdr->effecter_id;
151 }
152 }
153 else
154 {
155 VMMPdrExist = false;
156 }
157 }
158 catch (const SdBusError& e)
159 {
160 std::cerr << "PLDM soft off: Error get VMM PDR,ERROR=" << e.what()
161 << "\n";
162 VMMPdrExist = false;
163 }
164
165 if (VMMPdrExist)
166 {
167 return PLDM_SUCCESS;
168 }
169
170 // If the Virtual Machine Manager PDRs doesn't exist, go find the System
171 // Firmware PDRs.
172 // System Firmware is a logical entity, so the bit 15 in entity type is set
173 entityType = PLDM_ENTITY_SYS_FIRMWARE | 0x8000;
174 try
175 {
176 std::vector<std::vector<uint8_t>> sysFwResponse{};
177 auto sysFwMethod = bus.new_method_call(
178 "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",
179 "xyz.openbmc_project.PLDM.PDR", "FindStateEffecterPDR");
180 sysFwMethod.append(TID, entityType,
181 (uint16_t)PLDM_STATE_SET_SW_TERMINATION_STATUS);
182
183 auto sysFwResponseMsg = bus.call(sysFwMethod);
184
185 sysFwResponseMsg.read(sysFwResponse);
186
187 if (sysFwResponse.size() == 0)
188 {
189 std::cerr
190 << "No effecter ID has been found that matches the criteria"
191 << "\n";
192 return PLDM_ERROR;
193 }
194
195 for (auto& rep : sysFwResponse)
196 {
197 auto sysFwPdr =
198 reinterpret_cast<pldm_state_effecter_pdr*>(rep.data());
199 effecterID = sysFwPdr->effecter_id;
200 }
201 }
202 catch (const SdBusError& e)
203 {
204 std::cerr << "PLDM soft off: Error get system firmware PDR,ERROR="
205 << e.what() << "\n";
206 return PLDM_ERROR;
207 }
208
209 return PLDM_SUCCESS;
210}
211
212int SoftPowerOff::getSensorInfo()
213{
214 pldm::pdr::EntityType entityType;
215
216 entityType = VMMPdrExist ? PLDM_ENTITY_VIRTUAL_MACHINE_MANAGER
217 : PLDM_ENTITY_SYS_FIRMWARE;
218
219 // The Virtual machine manager/System firmware is logical entity, so bit 15
220 // need to be set.
221 entityType = entityType | 0x8000;
222
223 try
224 {
225 auto& bus = pldm::utils::DBusHandler::getBus();
226 std::vector<std::vector<uint8_t>> Response{};
227 auto method = bus.new_method_call(
228 "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",
229 "xyz.openbmc_project.PLDM.PDR", "FindStateSensorPDR");
230 method.append(TID, entityType,
231 (uint16_t)PLDM_STATE_SET_SW_TERMINATION_STATUS);
232
233 auto ResponseMsg = bus.call(method);
234
235 ResponseMsg.read(Response);
236
237 if (Response.size() == 0)
238 {
239 std::cerr
240 << "No sensor PDR has been found that matches the criteria"
241 << "\n";
242 return PLDM_ERROR;
243 }
244
245 pldm_state_sensor_pdr* pdr;
246 for (auto& rep : Response)
247 {
248 pdr = reinterpret_cast<pldm_state_sensor_pdr*>(rep.data());
249 }
250
251 sensorID = pdr->sensor_id;
252
253 auto compositeSensorCount = pdr->composite_sensor_count;
254 auto possibleStatesStart = pdr->possible_states;
255
256 for (auto offset = 0; offset < compositeSensorCount; offset++)
257 {
258 auto possibleStates =
259 reinterpret_cast<state_sensor_possible_states*>(
260 possibleStatesStart);
261 auto setId = possibleStates->state_set_id;
262 auto possibleStateSize = possibleStates->possible_states_size;
263
264 if (setId == PLDM_STATE_SET_SW_TERMINATION_STATUS)
265 {
266 sensorOffset = offset;
267 break;
268 }
269 possibleStatesStart +=
270 possibleStateSize + sizeof(setId) + sizeof(possibleStateSize);
271 }
272 }
273 catch (const SdBusError& e)
274 {
275 std::cerr << "PLDM soft off: Error get State Sensor PDR,ERROR="
276 << e.what() << "\n";
277 return PLDM_ERROR;
278 }
279
280 return PLDM_SUCCESS;
281}
282
283int SoftPowerOff::hostSoftOff(sdeventplus::Event& event)
284{
285 constexpr uint8_t effecterCount = 1;
286 uint8_t mctpEID;
287 uint8_t instanceID;
288
289 mctpEID = pldm::utils::readHostEID();
290
291 // Get instanceID
292 try
293 {
294 auto& bus = pldm::utils::DBusHandler::getBus();
295 auto method = bus.new_method_call(
296 "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",
297 "xyz.openbmc_project.PLDM.Requester", "GetInstanceId");
298 method.append(mctpEID);
299
300 auto ResponseMsg = bus.call(method);
301
302 ResponseMsg.read(instanceID);
303 }
304 catch (const SdBusError& e)
305 {
306 std::cerr << "PLDM soft off: Error get instanceID,ERROR=" << e.what()
307 << "\n";
308 return PLDM_ERROR;
309 }
310
311 std::array<uint8_t, sizeof(pldm_msg_hdr) + sizeof(effecterID) +
312 sizeof(effecterCount) +
313 sizeof(set_effecter_state_field)>
314 requestMsg{};
315 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
316 set_effecter_state_field stateField{
317 PLDM_REQUEST_SET, PLDM_SW_TERM_GRACEFUL_SHUTDOWN_REQUESTED};
318 auto rc = encode_set_state_effecter_states_req(
319 instanceID, effecterID, effecterCount, &stateField, request);
320 if (rc != PLDM_SUCCESS)
321 {
322 std::cerr << "Message encode failure. PLDM error code = " << std::hex
323 << std::showbase << rc << "\n";
324 return PLDM_ERROR;
325 }
326
327 // Open connection to MCTP socket
328 int fd = pldm_open();
329 if (-1 == fd)
330 {
331 std::cerr << "Failed to connect to mctp demux daemon"
332 << "\n";
333 return PLDM_ERROR;
334 }
335
336 // Add a timer to the event loop, default 30s.
337 auto timerCallback = [=](Timer& /*source*/, Timer::TimePoint /*time*/) {
338 if (!responseReceived)
339 {
340 std::cerr << "PLDM soft off: ERROR! Can't get the response for the "
341 "PLDM request msg. Time out!\n"
342 << "Exit the pldm-softpoweroff\n";
343 exit(-1);
344 }
345 return;
346 };
347 Timer time(event, (Clock(event).now() + std::chrono::seconds{30}),
348 std::chrono::seconds{1}, std::move(timerCallback));
349
350 // Add a callback to handle EPOLLIN on fd
351 auto callback = [=](IO& io, int fd, uint32_t revents) {
352 if (!(revents & EPOLLIN))
353 {
354 return;
355 }
356
357 uint8_t* responseMsg = nullptr;
358 size_t responseMsgSize{};
359
360 auto rc = pldm_recv(mctpEID, fd, request->hdr.instance_id, &responseMsg,
361 &responseMsgSize);
362 if (rc)
363 {
364 return;
365 }
366
367 std::unique_ptr<uint8_t, decltype(std::free)*> responseMsgPtr{
368 responseMsg, std::free};
369
370 // We've got the response meant for the PLDM request msg that was
371 // sent out
372 io.set_enabled(Enabled::Off);
373 auto response = reinterpret_cast<pldm_msg*>(responseMsgPtr.get());
374 std::cerr << "Getting the response. PLDM RC = " << std::hex
375 << std::showbase
376 << static_cast<uint16_t>(response->payload[0]) << "\n";
377
378 responseReceived = true;
379
380 // Start Timer
381 using namespace std::chrono;
382 auto timeMicroseconds =
383 duration_cast<microseconds>(seconds(SOFTOFF_TIMEOUT_SECONDS));
384
385 auto ret = startTimer(timeMicroseconds);
386 if (ret < 0)
387 {
388 std::cerr << "Failure to start Host soft off wait timer, ERRNO = "
389 << ret << "Exit the pldm-softpoweroff\n";
390 exit(-1);
391 }
392 else
393 {
394 std::cerr << "Timer started waiting for host soft off, "
395 "TIMEOUT_IN_SEC = "
396 << SOFTOFF_TIMEOUT_SECONDS << "\n";
397 }
398 return;
399 };
400 IO io(event, fd, EPOLLIN, std::move(callback));
401
402 // Send PLDM Request message - pldm_send doesn't wait for response
403 rc = pldm_send(mctpEID, fd, requestMsg.data(), requestMsg.size());
404 if (0 > rc)
405 {
406 std::cerr << "Failed to send message/receive response. RC = " << rc
407 << ", errno = " << errno << "\n";
408 return PLDM_ERROR;
409 }
410
411 // Time out or soft off complete
412 while (!isCompleted() && !isTimerExpired())
413 {
414 try
415 {
416 event.run(std::nullopt);
417 }
418 catch (const sdeventplus::SdEventError& e)
419 {
420 std::cerr
421 << "PLDM host soft off: Failure in processing request.ERROR= "
422 << e.what() << "\n";
423 return PLDM_ERROR;
424 }
425 }
426
427 return PLDM_SUCCESS;
428}
429
430int SoftPowerOff::startTimer(const std::chrono::microseconds& usec)
431{
432 return timer.start(usec);
433}
434} // namespace pldm