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