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