blob: 80abbf73e5426d5af0b63bd803529f255c104cf5 [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
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 Williams84b790c2022-07-22 19:26:56 -0500165 catch (const sdbusplus::exception_t& e)
Chicago Duan184f6022020-04-17 11:30:49 +0800166 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600167 error("PLDM soft off: Error get VMM PDR,ERROR={ERR_EXCEP}", "ERR_EXCEP",
168 e.what());
Chicago Duan184f6022020-04-17 11:30:49 +0800169 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 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600196 error("No effecter ID has been found that matches the criteria");
Chicago Duan184f6022020-04-17 11:30:49 +0800197 return PLDM_ERROR;
198 }
199
200 for (auto& rep : sysFwResponse)
201 {
202 auto sysFwPdr =
203 reinterpret_cast<pldm_state_effecter_pdr*>(rep.data());
204 effecterID = sysFwPdr->effecter_id;
205 }
206 }
Patrick Williams84b790c2022-07-22 19:26:56 -0500207 catch (const sdbusplus::exception_t& e)
Chicago Duan184f6022020-04-17 11:30:49 +0800208 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600209 error("PLDM soft off: Error get system firmware PDR,ERROR={ERR_EXCEP}",
210 "ERR_EXCEP", e.what());
Tom Joseph29d22112020-11-18 15:05:15 +0530211 completed = true;
Chicago Duan184f6022020-04-17 11:30:49 +0800212 return PLDM_ERROR;
213 }
214
215 return PLDM_SUCCESS;
216}
217
218int SoftPowerOff::getSensorInfo()
219{
220 pldm::pdr::EntityType entityType;
221
222 entityType = VMMPdrExist ? PLDM_ENTITY_VIRTUAL_MACHINE_MANAGER
223 : PLDM_ENTITY_SYS_FIRMWARE;
224
225 // The Virtual machine manager/System firmware is logical entity, so bit 15
226 // need to be set.
227 entityType = entityType | 0x8000;
228
229 try
230 {
231 auto& bus = pldm::utils::DBusHandler::getBus();
232 std::vector<std::vector<uint8_t>> Response{};
233 auto method = bus.new_method_call(
234 "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",
235 "xyz.openbmc_project.PLDM.PDR", "FindStateSensorPDR");
236 method.append(TID, entityType,
237 (uint16_t)PLDM_STATE_SET_SW_TERMINATION_STATUS);
238
239 auto ResponseMsg = bus.call(method);
240
241 ResponseMsg.read(Response);
242
243 if (Response.size() == 0)
244 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600245 error("No sensor PDR has been found that matches the criteria");
Chicago Duan184f6022020-04-17 11:30:49 +0800246 return PLDM_ERROR;
247 }
248
ThuBaNguyen499a29d2023-05-30 06:32:06 +0700249 pldm_state_sensor_pdr* pdr = nullptr;
Chicago Duan184f6022020-04-17 11:30:49 +0800250 for (auto& rep : Response)
251 {
252 pdr = reinterpret_cast<pldm_state_sensor_pdr*>(rep.data());
Manojkiran Eda31a78442021-09-12 15:18:25 +0530253 if (!pdr)
254 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600255 error("Failed to get state sensor PDR.");
Manojkiran Eda31a78442021-09-12 15:18:25 +0530256 return PLDM_ERROR;
257 }
Manojkiran Edabcf91ac2021-03-14 13:50:48 +0530258 }
259
Chicago Duan184f6022020-04-17 11:30:49 +0800260 sensorID = pdr->sensor_id;
261
262 auto compositeSensorCount = pdr->composite_sensor_count;
263 auto possibleStatesStart = pdr->possible_states;
264
265 for (auto offset = 0; offset < compositeSensorCount; offset++)
266 {
267 auto possibleStates =
268 reinterpret_cast<state_sensor_possible_states*>(
269 possibleStatesStart);
270 auto setId = possibleStates->state_set_id;
271 auto possibleStateSize = possibleStates->possible_states_size;
272
273 if (setId == PLDM_STATE_SET_SW_TERMINATION_STATUS)
274 {
275 sensorOffset = offset;
276 break;
277 }
Patrick Williams6da4f912023-05-10 07:50:53 -0500278 possibleStatesStart += possibleStateSize + sizeof(setId) +
279 sizeof(possibleStateSize);
Chicago Duan184f6022020-04-17 11:30:49 +0800280 }
281 }
Patrick Williams84b790c2022-07-22 19:26:56 -0500282 catch (const sdbusplus::exception_t& e)
Chicago Duan184f6022020-04-17 11:30:49 +0800283 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600284 error("PLDM soft off: Error get State Sensor PDR,ERROR={ERR_EXCEP}",
285 "ERR_EXCEP", e.what());
Chicago Duan184f6022020-04-17 11:30:49 +0800286 return PLDM_ERROR;
287 }
288
289 return PLDM_SUCCESS;
290}
291
292int SoftPowerOff::hostSoftOff(sdeventplus::Event& event)
293{
294 constexpr uint8_t effecterCount = 1;
295 uint8_t mctpEID;
296 uint8_t instanceID;
297
298 mctpEID = pldm::utils::readHostEID();
299
300 // Get instanceID
301 try
302 {
303 auto& bus = pldm::utils::DBusHandler::getBus();
304 auto method = bus.new_method_call(
305 "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",
306 "xyz.openbmc_project.PLDM.Requester", "GetInstanceId");
307 method.append(mctpEID);
308
309 auto ResponseMsg = bus.call(method);
310
311 ResponseMsg.read(instanceID);
312 }
Patrick Williams84b790c2022-07-22 19:26:56 -0500313 catch (const sdbusplus::exception_t& e)
Chicago Duan184f6022020-04-17 11:30:49 +0800314 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600315 error("PLDM soft off: Error get instanceID,ERROR={ERR_EXCEP}",
316 "ERR_EXCEP", e.what());
Chicago Duan184f6022020-04-17 11:30:49 +0800317 return PLDM_ERROR;
318 }
319
320 std::array<uint8_t, sizeof(pldm_msg_hdr) + sizeof(effecterID) +
321 sizeof(effecterCount) +
322 sizeof(set_effecter_state_field)>
323 requestMsg{};
324 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
325 set_effecter_state_field stateField{
326 PLDM_REQUEST_SET, PLDM_SW_TERM_GRACEFUL_SHUTDOWN_REQUESTED};
327 auto rc = encode_set_state_effecter_states_req(
328 instanceID, effecterID, effecterCount, &stateField, request);
329 if (rc != PLDM_SUCCESS)
330 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600331 error("Message encode failure. PLDM error code = {RC}", "RC", lg2::hex,
332 static_cast<int>(rc));
Chicago Duan184f6022020-04-17 11:30:49 +0800333 return PLDM_ERROR;
334 }
335
336 // Open connection to MCTP socket
337 int fd = pldm_open();
338 if (-1 == fd)
339 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600340 error("Failed to connect to mctp demux daemon");
Chicago Duan184f6022020-04-17 11:30:49 +0800341 return PLDM_ERROR;
342 }
343
344 // Add a timer to the event loop, default 30s.
Patrick Williams6da4f912023-05-10 07:50:53 -0500345 auto timerCallback =
346 [=, this](Timer& /*source*/, Timer::TimePoint /*time*/) {
Chicago Duan184f6022020-04-17 11:30:49 +0800347 if (!responseReceived)
348 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600349 error(
350 "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 +0800351 exit(-1);
352 }
353 return;
354 };
355 Timer time(event, (Clock(event).now() + std::chrono::seconds{30}),
356 std::chrono::seconds{1}, std::move(timerCallback));
357
358 // Add a callback to handle EPOLLIN on fd
Deepak Kodihallib0d15f12021-04-16 12:18:10 +0530359 auto callback = [=, this](IO& io, int fd, uint32_t revents) {
Chicago Duan184f6022020-04-17 11:30:49 +0800360 if (!(revents & EPOLLIN))
361 {
362 return;
363 }
364
365 uint8_t* responseMsg = nullptr;
366 size_t responseMsgSize{};
367
368 auto rc = pldm_recv(mctpEID, fd, request->hdr.instance_id, &responseMsg,
369 &responseMsgSize);
370 if (rc)
371 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600372 error("Soft off: failed to recv pldm data. PLDM RC = {RC}", "RC",
373 static_cast<int>(rc));
Chicago Duan184f6022020-04-17 11:30:49 +0800374 return;
375 }
376
377 std::unique_ptr<uint8_t, decltype(std::free)*> responseMsgPtr{
378 responseMsg, std::free};
379
380 // We've got the response meant for the PLDM request msg that was
381 // sent out
382 io.set_enabled(Enabled::Off);
383 auto response = reinterpret_cast<pldm_msg*>(responseMsgPtr.get());
George Liu9915d022021-12-21 14:04:31 +0800384 if (response->payload[0] != PLDM_SUCCESS)
385 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600386 error("Getting the wrong response. PLDM RC = {RC}", "RC",
387 (unsigned)response->payload[0]);
George Liu9915d022021-12-21 14:04:31 +0800388 exit(-1);
389 }
Chicago Duan184f6022020-04-17 11:30:49 +0800390
391 responseReceived = true;
392
393 // Start Timer
394 using namespace std::chrono;
395 auto timeMicroseconds =
396 duration_cast<microseconds>(seconds(SOFTOFF_TIMEOUT_SECONDS));
397
398 auto ret = startTimer(timeMicroseconds);
399 if (ret < 0)
400 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600401 error(
402 "Failure to start Host soft off wait timer, ERRNO = {RET}. Exit the pldm-softpoweroff",
403 "RET", ret);
Chicago Duan184f6022020-04-17 11:30:49 +0800404 exit(-1);
405 }
406 else
407 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600408 error(
409 "Timer started waiting for host soft off, TIMEOUT_IN_SEC = {TIMEOUT_SEC}",
410 "TIMEOUT_SEC", SOFTOFF_TIMEOUT_SECONDS);
Chicago Duan184f6022020-04-17 11:30:49 +0800411 }
412 return;
413 };
414 IO io(event, fd, EPOLLIN, std::move(callback));
415
416 // Send PLDM Request message - pldm_send doesn't wait for response
417 rc = pldm_send(mctpEID, fd, requestMsg.data(), requestMsg.size());
Sampa Misra9f8d2b02021-03-24 08:33:14 +0000418 if (0 > rc)
Chicago Duan184f6022020-04-17 11:30:49 +0800419 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600420 error(
421 "Failed to send message/receive response. RC = {RC}, errno = {ERR}",
422 "RC", static_cast<int>(rc), "ERR", errno);
Chicago Duan184f6022020-04-17 11:30:49 +0800423 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 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600435 error(
436 "PLDM host soft off: Failure in processing request.ERROR= {ERR_EXCEP}",
437 "ERR_EXCEP", e.what());
Chicago Duan184f6022020-04-17 11:30:49 +0800438 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