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