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