blob: b5d9e8cd308ed44d0e2334f084508adadf9fe13f [file] [log] [blame]
Chicago Duan184f6022020-04-17 11:30:49 +08001#include "config.h"
2
3#include "softoff.hpp"
4
Chicago Duan184f6022020-04-17 11:30:49 +08005#include "common/utils.hpp"
6
George Liuc453e162022-12-21 17:16:23 +08007#include <libpldm/entity.h>
8#include <libpldm/platform.h>
9#include <libpldm/pldm.h>
10#include <libpldm/state_set.h>
11
Riya Dixit49cfb132023-03-02 04:26:53 -060012#include <phosphor-logging/lg2.hpp>
Chicago Duan184f6022020-04-17 11:30:49 +080013#include <sdbusplus/bus.hpp>
14#include <sdeventplus/clock.hpp>
George Liua2767e62021-02-24 18:53:34 +080015#include <sdeventplus/exception.hpp>
Chicago Duan184f6022020-04-17 11:30:49 +080016#include <sdeventplus/source/io.hpp>
17#include <sdeventplus/source/time.hpp>
18
19#include <array>
20#include <iostream>
21
Riya Dixit49cfb132023-03-02 04:26:53 -060022PHOSPHOR_LOG2_USING;
23
Chicago Duan184f6022020-04-17 11:30:49 +080024namespace pldm
25{
Chicago Duan184f6022020-04-17 11:30:49 +080026using namespace sdeventplus;
27using namespace sdeventplus::source;
28constexpr auto clockId = sdeventplus::ClockId::RealTime;
29using Clock = Clock<clockId>;
30using Timer = Time<clockId>;
31
Chicago Duan184f6022020-04-17 11:30:49 +080032constexpr pldm::pdr::TerminusID TID = 0; // TID will be implemented later.
33namespace sdbusRule = sdbusplus::bus::match::rules;
34
Patrick Williams84b790c2022-07-22 19:26:56 -050035SoftPowerOff::SoftPowerOff(sdbusplus::bus_t& bus, sd_event* event) :
Chicago Duan184f6022020-04-17 11:30:49 +080036 bus(bus), timer(event)
37{
Manojkiran Eda31a78442021-09-12 15:18:25 +053038 getHostState();
Chicago Duan184f6022020-04-17 11:30:49 +080039 if (hasError || completed)
40 {
41 return;
42 }
43
Manojkiran Eda31a78442021-09-12 15:18:25 +053044 auto rc = getEffecterID();
Tom Joseph29d22112020-11-18 15:05:15 +053045 if (completed)
46 {
Riya Dixit49cfb132023-03-02 04:26:53 -060047 error("pldm-softpoweroff: effecter to initiate softoff not found");
Tom Joseph29d22112020-11-18 15:05:15 +053048 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 {
Riya Dixit49cfb132023-03-02 04:26:53 -060059 error("Message get Sensor PDRs error. PLDM error code = {RC}", "RC",
60 lg2::hex, static_cast<int>(rc));
Chicago Duan184f6022020-04-17 11:30:49 +080061 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 {
Riya Dixit49cfb132023-03-02 04:26:53 -060096 error("PLDM host soft off: Can't get current host state.");
Chicago Duan184f6022020-04-17 11:30:49 +080097 hasError = true;
98 return PLDM_ERROR;
99 }
100
101 return PLDM_SUCCESS;
102}
103
Patrick Williams84b790c2022-07-22 19:26:56 -0500104void SoftPowerOff::hostSoftOffComplete(sdbusplus::message_t& msg)
Chicago Duan184f6022020-04-17 11:30:49 +0800105{
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 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600123 error("PLDM soft off: Failure to STOP the timer. ERRNO={RC}", "RC",
124 rc);
Chicago Duan184f6022020-04-17 11:30:49 +0800125 }
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
vkaverap@in.ibm.com9138c202023-05-19 07:50:47 -0500148 auto VMMResponseMsg = bus.call(
149 VMMMethod,
150 std::chrono::duration_cast<microsec>(sec(DBUS_TIMEOUT)).count());
Chicago Duan184f6022020-04-17 11:30:49 +0800151
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 }
Patrick Williams84b790c2022-07-22 19:26:56 -0500167 catch (const sdbusplus::exception_t& e)
Chicago Duan184f6022020-04-17 11:30:49 +0800168 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600169 error("PLDM soft off: Error get VMM PDR,ERROR={ERR_EXCEP}", "ERR_EXCEP",
170 e.what());
Chicago Duan184f6022020-04-17 11:30:49 +0800171 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
vkaverap@in.ibm.com9138c202023-05-19 07:50:47 -0500192 auto sysFwResponseMsg = bus.call(
193 sysFwMethod,
194 std::chrono::duration_cast<microsec>(sec(DBUS_TIMEOUT)).count());
Chicago Duan184f6022020-04-17 11:30:49 +0800195
196 sysFwResponseMsg.read(sysFwResponse);
197
198 if (sysFwResponse.size() == 0)
199 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600200 error("No effecter ID has been found that matches the criteria");
Chicago Duan184f6022020-04-17 11:30:49 +0800201 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 }
Patrick Williams84b790c2022-07-22 19:26:56 -0500211 catch (const sdbusplus::exception_t& e)
Chicago Duan184f6022020-04-17 11:30:49 +0800212 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600213 error("PLDM soft off: Error get system firmware PDR,ERROR={ERR_EXCEP}",
214 "ERR_EXCEP", e.what());
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
vkaverap@in.ibm.com9138c202023-05-19 07:50:47 -0500243 auto ResponseMsg = bus.call(
244 method,
245 std::chrono::duration_cast<microsec>(sec(DBUS_TIMEOUT)).count());
Chicago Duan184f6022020-04-17 11:30:49 +0800246
247 ResponseMsg.read(Response);
248
249 if (Response.size() == 0)
250 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600251 error("No sensor PDR has been found that matches the criteria");
Chicago Duan184f6022020-04-17 11:30:49 +0800252 return PLDM_ERROR;
253 }
254
ThuBaNguyen499a29d2023-05-30 06:32:06 +0700255 pldm_state_sensor_pdr* pdr = nullptr;
Chicago Duan184f6022020-04-17 11:30:49 +0800256 for (auto& rep : Response)
257 {
258 pdr = reinterpret_cast<pldm_state_sensor_pdr*>(rep.data());
Manojkiran Eda31a78442021-09-12 15:18:25 +0530259 if (!pdr)
260 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600261 error("Failed to get state sensor PDR.");
Manojkiran Eda31a78442021-09-12 15:18:25 +0530262 return PLDM_ERROR;
263 }
Manojkiran Edabcf91ac2021-03-14 13:50:48 +0530264 }
265
Chicago Duan184f6022020-04-17 11:30:49 +0800266 sensorID = pdr->sensor_id;
267
268 auto compositeSensorCount = pdr->composite_sensor_count;
269 auto possibleStatesStart = pdr->possible_states;
270
271 for (auto offset = 0; offset < compositeSensorCount; offset++)
272 {
273 auto possibleStates =
274 reinterpret_cast<state_sensor_possible_states*>(
275 possibleStatesStart);
276 auto setId = possibleStates->state_set_id;
277 auto possibleStateSize = possibleStates->possible_states_size;
278
279 if (setId == PLDM_STATE_SET_SW_TERMINATION_STATUS)
280 {
281 sensorOffset = offset;
282 break;
283 }
Patrick Williams6da4f912023-05-10 07:50:53 -0500284 possibleStatesStart += possibleStateSize + sizeof(setId) +
285 sizeof(possibleStateSize);
Chicago Duan184f6022020-04-17 11:30:49 +0800286 }
287 }
Patrick Williams84b790c2022-07-22 19:26:56 -0500288 catch (const sdbusplus::exception_t& e)
Chicago Duan184f6022020-04-17 11:30:49 +0800289 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600290 error("PLDM soft off: Error get State Sensor PDR,ERROR={ERR_EXCEP}",
291 "ERR_EXCEP", e.what());
Chicago Duan184f6022020-04-17 11:30:49 +0800292 return PLDM_ERROR;
293 }
294
295 return PLDM_SUCCESS;
296}
297
298int SoftPowerOff::hostSoftOff(sdeventplus::Event& event)
299{
300 constexpr uint8_t effecterCount = 1;
301 uint8_t mctpEID;
302 uint8_t instanceID;
303
304 mctpEID = pldm::utils::readHostEID();
305
306 // Get instanceID
307 try
308 {
309 auto& bus = pldm::utils::DBusHandler::getBus();
310 auto method = bus.new_method_call(
311 "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",
312 "xyz.openbmc_project.PLDM.Requester", "GetInstanceId");
313 method.append(mctpEID);
314
vkaverap@in.ibm.com9138c202023-05-19 07:50:47 -0500315 auto ResponseMsg = bus.call(
316 method,
317 std::chrono::duration_cast<microsec>(sec(DBUS_TIMEOUT)).count());
Chicago Duan184f6022020-04-17 11:30:49 +0800318
319 ResponseMsg.read(instanceID);
320 }
Patrick Williams84b790c2022-07-22 19:26:56 -0500321 catch (const sdbusplus::exception_t& e)
Chicago Duan184f6022020-04-17 11:30:49 +0800322 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600323 error("PLDM soft off: Error get instanceID,ERROR={ERR_EXCEP}",
324 "ERR_EXCEP", e.what());
Chicago Duan184f6022020-04-17 11:30:49 +0800325 return PLDM_ERROR;
326 }
327
328 std::array<uint8_t, sizeof(pldm_msg_hdr) + sizeof(effecterID) +
329 sizeof(effecterCount) +
330 sizeof(set_effecter_state_field)>
331 requestMsg{};
332 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
333 set_effecter_state_field stateField{
334 PLDM_REQUEST_SET, PLDM_SW_TERM_GRACEFUL_SHUTDOWN_REQUESTED};
335 auto rc = encode_set_state_effecter_states_req(
336 instanceID, effecterID, effecterCount, &stateField, request);
337 if (rc != PLDM_SUCCESS)
338 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600339 error("Message encode failure. PLDM error code = {RC}", "RC", lg2::hex,
340 static_cast<int>(rc));
Chicago Duan184f6022020-04-17 11:30:49 +0800341 return PLDM_ERROR;
342 }
343
344 // Open connection to MCTP socket
345 int fd = pldm_open();
346 if (-1 == fd)
347 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600348 error("Failed to connect to mctp demux daemon");
Chicago Duan184f6022020-04-17 11:30:49 +0800349 return PLDM_ERROR;
350 }
351
352 // Add a timer to the event loop, default 30s.
Patrick Williams6da4f912023-05-10 07:50:53 -0500353 auto timerCallback =
354 [=, this](Timer& /*source*/, Timer::TimePoint /*time*/) {
Chicago Duan184f6022020-04-17 11:30:49 +0800355 if (!responseReceived)
356 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600357 error(
358 "PLDM soft off: ERROR! Can't get the response for the PLDM request msg. Time out! Exit the pldm-softpoweroff");
Chicago Duan184f6022020-04-17 11:30:49 +0800359 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
Deepak Kodihallib0d15f12021-04-16 12:18:10 +0530367 auto callback = [=, this](IO& io, int fd, uint32_t revents) {
Chicago Duan184f6022020-04-17 11:30:49 +0800368 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 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600380 error("Soft off: failed to recv pldm data. PLDM RC = {RC}", "RC",
381 static_cast<int>(rc));
Chicago Duan184f6022020-04-17 11:30:49 +0800382 return;
383 }
384
385 std::unique_ptr<uint8_t, decltype(std::free)*> responseMsgPtr{
386 responseMsg, std::free};
387
388 // We've got the response meant for the PLDM request msg that was
389 // sent out
390 io.set_enabled(Enabled::Off);
391 auto response = reinterpret_cast<pldm_msg*>(responseMsgPtr.get());
George Liu9915d022021-12-21 14:04:31 +0800392 if (response->payload[0] != PLDM_SUCCESS)
393 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600394 error("Getting the wrong response. PLDM RC = {RC}", "RC",
395 (unsigned)response->payload[0]);
George Liu9915d022021-12-21 14:04:31 +0800396 exit(-1);
397 }
Chicago Duan184f6022020-04-17 11:30:49 +0800398
399 responseReceived = true;
400
401 // Start Timer
402 using namespace std::chrono;
403 auto timeMicroseconds =
404 duration_cast<microseconds>(seconds(SOFTOFF_TIMEOUT_SECONDS));
405
406 auto ret = startTimer(timeMicroseconds);
407 if (ret < 0)
408 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600409 error(
410 "Failure to start Host soft off wait timer, ERRNO = {RET}. Exit the pldm-softpoweroff",
411 "RET", ret);
Chicago Duan184f6022020-04-17 11:30:49 +0800412 exit(-1);
413 }
414 else
415 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600416 error(
417 "Timer started waiting for host soft off, TIMEOUT_IN_SEC = {TIMEOUT_SEC}",
418 "TIMEOUT_SEC", SOFTOFF_TIMEOUT_SECONDS);
Chicago Duan184f6022020-04-17 11:30:49 +0800419 }
420 return;
421 };
422 IO io(event, fd, EPOLLIN, std::move(callback));
423
424 // Send PLDM Request message - pldm_send doesn't wait for response
425 rc = pldm_send(mctpEID, fd, requestMsg.data(), requestMsg.size());
Sampa Misra9f8d2b02021-03-24 08:33:14 +0000426 if (0 > rc)
Chicago Duan184f6022020-04-17 11:30:49 +0800427 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600428 error(
429 "Failed to send message/receive response. RC = {RC}, errno = {ERR}",
430 "RC", static_cast<int>(rc), "ERR", errno);
Chicago Duan184f6022020-04-17 11:30:49 +0800431 return PLDM_ERROR;
432 }
433
434 // Time out or soft off complete
435 while (!isCompleted() && !isTimerExpired())
436 {
437 try
438 {
439 event.run(std::nullopt);
440 }
441 catch (const sdeventplus::SdEventError& e)
442 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600443 error(
444 "PLDM host soft off: Failure in processing request.ERROR= {ERR_EXCEP}",
445 "ERR_EXCEP", e.what());
Chicago Duan184f6022020-04-17 11:30:49 +0800446 return PLDM_ERROR;
447 }
448 }
449
450 return PLDM_SUCCESS;
451}
452
453int SoftPowerOff::startTimer(const std::chrono::microseconds& usec)
454{
455 return timer.start(usec);
456}
457} // namespace pldm