blob: 1b6f16ef0a2ff512f90c33dd921921a47f517052 [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
Chicago Duan184f6022020-04-17 11:30:49 +080030constexpr pldm::pdr::TerminusID TID = 0; // TID will be implemented later.
31namespace sdbusRule = sdbusplus::bus::match::rules;
32
Patrick Williams84b790c2022-07-22 19:26:56 -050033SoftPowerOff::SoftPowerOff(sdbusplus::bus_t& bus, sd_event* event) :
Chicago Duan184f6022020-04-17 11:30:49 +080034 bus(bus), timer(event)
35{
Manojkiran Eda31a78442021-09-12 15:18:25 +053036 getHostState();
Chicago Duan184f6022020-04-17 11:30:49 +080037 if (hasError || completed)
38 {
39 return;
40 }
41
Manojkiran Eda31a78442021-09-12 15:18:25 +053042 auto rc = getEffecterID();
Tom Joseph29d22112020-11-18 15:05:15 +053043 if (completed)
44 {
45 std::cerr
46 << "pldm-softpoweroff: effecter to initiate softoff not found \n";
47 return;
48 }
49 else if (rc != PLDM_SUCCESS)
Chicago Duan184f6022020-04-17 11:30:49 +080050 {
51 hasError = true;
52 return;
53 }
54
55 rc = getSensorInfo();
56 if (rc != PLDM_SUCCESS)
57 {
George Liu9915d022021-12-21 14:04:31 +080058 std::cerr << "Message get Sensor PDRs error. PLDM error code = "
Chicago Duan184f6022020-04-17 11:30:49 +080059 << std::hex << std::showbase << rc << "\n";
60 hasError = true;
61 return;
62 }
63
64 // Matches on the pldm StateSensorEvent signal
65 pldmEventSignal = std::make_unique<sdbusplus::bus::match_t>(
66 bus,
67 sdbusRule::type::signal() + sdbusRule::member("StateSensorEvent") +
68 sdbusRule::path("/xyz/openbmc_project/pldm") +
69 sdbusRule::interface("xyz.openbmc_project.PLDM.Event"),
70 std::bind(std::mem_fn(&SoftPowerOff::hostSoftOffComplete), this,
71 std::placeholders::_1));
72}
73
74int SoftPowerOff::getHostState()
75{
76 try
77 {
78 pldm::utils::PropertyValue propertyValue =
79 pldm::utils::DBusHandler().getDbusPropertyVariant(
80 "/xyz/openbmc_project/state/host0", "CurrentHostState",
81 "xyz.openbmc_project.State.Host");
82
Andrew Geissler5b5fa432021-01-22 16:27:24 -060083 if ((std::get<std::string>(propertyValue) !=
84 "xyz.openbmc_project.State.Host.HostState.Running") &&
85 (std::get<std::string>(propertyValue) !=
86 "xyz.openbmc_project.State.Host.HostState.TransitioningToOff"))
Chicago Duan184f6022020-04-17 11:30:49 +080087 {
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
Patrick Williams84b790c2022-07-22 19:26:56 -0500103void SoftPowerOff::hostSoftOffComplete(sdbusplus::message_t& msg)
Chicago Duan184f6022020-04-17 11:30:49 +0800104{
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 }
Patrick Williams84b790c2022-07-22 19:26:56 -0500164 catch (const sdbusplus::exception_t& e)
Chicago Duan184f6022020-04-17 11:30:49 +0800165 {
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 }
Patrick Williams84b790c2022-07-22 19:26:56 -0500208 catch (const sdbusplus::exception_t& e)
Chicago Duan184f6022020-04-17 11:30:49 +0800209 {
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());
Manojkiran Eda31a78442021-09-12 15:18:25 +0530256 if (!pdr)
257 {
258 std::cerr << "Failed to get state sensor PDR.\n";
259 return PLDM_ERROR;
260 }
Manojkiran Edabcf91ac2021-03-14 13:50:48 +0530261 }
262
Chicago Duan184f6022020-04-17 11:30:49 +0800263 sensorID = pdr->sensor_id;
264
265 auto compositeSensorCount = pdr->composite_sensor_count;
266 auto possibleStatesStart = pdr->possible_states;
267
268 for (auto offset = 0; offset < compositeSensorCount; offset++)
269 {
270 auto possibleStates =
271 reinterpret_cast<state_sensor_possible_states*>(
272 possibleStatesStart);
273 auto setId = possibleStates->state_set_id;
274 auto possibleStateSize = possibleStates->possible_states_size;
275
276 if (setId == PLDM_STATE_SET_SW_TERMINATION_STATUS)
277 {
278 sensorOffset = offset;
279 break;
280 }
281 possibleStatesStart +=
282 possibleStateSize + sizeof(setId) + sizeof(possibleStateSize);
283 }
284 }
Patrick Williams84b790c2022-07-22 19:26:56 -0500285 catch (const sdbusplus::exception_t& e)
Chicago Duan184f6022020-04-17 11:30:49 +0800286 {
287 std::cerr << "PLDM soft off: Error get State Sensor PDR,ERROR="
288 << e.what() << "\n";
289 return PLDM_ERROR;
290 }
291
292 return PLDM_SUCCESS;
293}
294
295int SoftPowerOff::hostSoftOff(sdeventplus::Event& event)
296{
297 constexpr uint8_t effecterCount = 1;
298 uint8_t mctpEID;
299 uint8_t instanceID;
300
301 mctpEID = pldm::utils::readHostEID();
302
303 // Get instanceID
304 try
305 {
306 auto& bus = pldm::utils::DBusHandler::getBus();
307 auto method = bus.new_method_call(
308 "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",
309 "xyz.openbmc_project.PLDM.Requester", "GetInstanceId");
310 method.append(mctpEID);
311
312 auto ResponseMsg = bus.call(method);
313
314 ResponseMsg.read(instanceID);
315 }
Patrick Williams84b790c2022-07-22 19:26:56 -0500316 catch (const sdbusplus::exception_t& e)
Chicago Duan184f6022020-04-17 11:30:49 +0800317 {
318 std::cerr << "PLDM soft off: Error get instanceID,ERROR=" << e.what()
319 << "\n";
320 return PLDM_ERROR;
321 }
322
323 std::array<uint8_t, sizeof(pldm_msg_hdr) + sizeof(effecterID) +
324 sizeof(effecterCount) +
325 sizeof(set_effecter_state_field)>
326 requestMsg{};
327 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
328 set_effecter_state_field stateField{
329 PLDM_REQUEST_SET, PLDM_SW_TERM_GRACEFUL_SHUTDOWN_REQUESTED};
330 auto rc = encode_set_state_effecter_states_req(
331 instanceID, effecterID, effecterCount, &stateField, request);
332 if (rc != PLDM_SUCCESS)
333 {
334 std::cerr << "Message encode failure. PLDM error code = " << std::hex
335 << std::showbase << rc << "\n";
336 return PLDM_ERROR;
337 }
338
339 // Open connection to MCTP socket
340 int fd = pldm_open();
341 if (-1 == fd)
342 {
343 std::cerr << "Failed to connect to mctp demux daemon"
344 << "\n";
345 return PLDM_ERROR;
346 }
347
348 // Add a timer to the event loop, default 30s.
Deepak Kodihallib0d15f12021-04-16 12:18:10 +0530349 auto timerCallback = [=, this](Timer& /*source*/,
350 Timer::TimePoint /*time*/) {
Chicago Duan184f6022020-04-17 11:30:49 +0800351 if (!responseReceived)
352 {
George Liu9915d022021-12-21 14:04:31 +0800353 std::cerr
354 << "PLDM soft off: ERROR! Can't get the response for the PLDM request msg. Time out!\n"
355 << "Exit the pldm-softpoweroff\n";
Chicago Duan184f6022020-04-17 11:30:49 +0800356 exit(-1);
357 }
358 return;
359 };
360 Timer time(event, (Clock(event).now() + std::chrono::seconds{30}),
361 std::chrono::seconds{1}, std::move(timerCallback));
362
363 // Add a callback to handle EPOLLIN on fd
Deepak Kodihallib0d15f12021-04-16 12:18:10 +0530364 auto callback = [=, this](IO& io, int fd, uint32_t revents) {
Chicago Duan184f6022020-04-17 11:30:49 +0800365 if (!(revents & EPOLLIN))
366 {
367 return;
368 }
369
370 uint8_t* responseMsg = nullptr;
371 size_t responseMsgSize{};
372
373 auto rc = pldm_recv(mctpEID, fd, request->hdr.instance_id, &responseMsg,
374 &responseMsgSize);
375 if (rc)
376 {
George Liu9915d022021-12-21 14:04:31 +0800377 std::cerr << "Soft off: failed to recv pldm data. PLDM RC = " << rc
378 << "\n";
Chicago Duan184f6022020-04-17 11:30:49 +0800379 return;
380 }
381
382 std::unique_ptr<uint8_t, decltype(std::free)*> responseMsgPtr{
383 responseMsg, std::free};
384
385 // We've got the response meant for the PLDM request msg that was
386 // sent out
387 io.set_enabled(Enabled::Off);
388 auto response = reinterpret_cast<pldm_msg*>(responseMsgPtr.get());
George Liu9915d022021-12-21 14:04:31 +0800389 if (response->payload[0] != PLDM_SUCCESS)
390 {
391 std::cerr << "Getting the wrong response. PLDM RC = "
392 << (unsigned)response->payload[0] << "\n";
393 exit(-1);
394 }
Chicago Duan184f6022020-04-17 11:30:49 +0800395
396 responseReceived = true;
397
398 // Start Timer
399 using namespace std::chrono;
400 auto timeMicroseconds =
401 duration_cast<microseconds>(seconds(SOFTOFF_TIMEOUT_SECONDS));
402
403 auto ret = startTimer(timeMicroseconds);
404 if (ret < 0)
405 {
406 std::cerr << "Failure to start Host soft off wait timer, ERRNO = "
407 << ret << "Exit the pldm-softpoweroff\n";
408 exit(-1);
409 }
410 else
411 {
George Liu9915d022021-12-21 14:04:31 +0800412 std::cerr
413 << "Timer started waiting for host soft off, TIMEOUT_IN_SEC = "
414 << SOFTOFF_TIMEOUT_SECONDS << "\n";
Chicago Duan184f6022020-04-17 11:30:49 +0800415 }
416 return;
417 };
418 IO io(event, fd, EPOLLIN, std::move(callback));
419
420 // Send PLDM Request message - pldm_send doesn't wait for response
421 rc = pldm_send(mctpEID, fd, requestMsg.data(), requestMsg.size());
Sampa Misra9f8d2b02021-03-24 08:33:14 +0000422 if (0 > rc)
Chicago Duan184f6022020-04-17 11:30:49 +0800423 {
424 std::cerr << "Failed to send message/receive response. RC = " << rc
425 << ", errno = " << errno << "\n";
426 return PLDM_ERROR;
427 }
428
429 // Time out or soft off complete
430 while (!isCompleted() && !isTimerExpired())
431 {
432 try
433 {
434 event.run(std::nullopt);
435 }
436 catch (const sdeventplus::SdEventError& e)
437 {
438 std::cerr
439 << "PLDM host soft off: Failure in processing request.ERROR= "
440 << e.what() << "\n";
441 return PLDM_ERROR;
442 }
443 }
444
445 return PLDM_SUCCESS;
446}
447
448int SoftPowerOff::startTimer(const std::chrono::microseconds& usec)
449{
450 return timer.start(usec);
451}
452} // namespace pldm