blob: f80e0486fde018405f262f0cd9f3a0aa3e6483d2 [file] [log] [blame]
Chicago Duan184f6022020-04-17 11:30:49 +08001#include "softoff.hpp"
2
Dung Cao3d03f3f2023-09-07 06:51:33 +00003#include "common/instance_id.hpp"
Chicago Duan184f6022020-04-17 11:30:49 +08004#include "common/utils.hpp"
5
George Liuc453e162022-12-21 17:16:23 +08006#include <libpldm/entity.h>
7#include <libpldm/platform.h>
8#include <libpldm/pldm.h>
9#include <libpldm/state_set.h>
10
Riya Dixit49cfb132023-03-02 04:26:53 -060011#include <phosphor-logging/lg2.hpp>
Chicago Duan184f6022020-04-17 11:30:49 +080012#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
Riya Dixit49cfb132023-03-02 04:26:53 -060021PHOSPHOR_LOG2_USING;
22
Chicago Duan184f6022020-04-17 11:30:49 +080023namespace pldm
24{
Chicago Duan184f6022020-04-17 11:30:49 +080025using namespace sdeventplus;
26using namespace sdeventplus::source;
27constexpr auto clockId = sdeventplus::ClockId::RealTime;
28using Clock = Clock<clockId>;
29using Timer = Time<clockId>;
30
Chicago Duan184f6022020-04-17 11:30:49 +080031constexpr pldm::pdr::TerminusID TID = 0; // TID will be implemented later.
32namespace sdbusRule = sdbusplus::bus::match::rules;
33
Patrick Williams84b790c2022-07-22 19:26:56 -050034SoftPowerOff::SoftPowerOff(sdbusplus::bus_t& bus, sd_event* event) :
Chicago Duan184f6022020-04-17 11:30:49 +080035 bus(bus), timer(event)
36{
Manojkiran Eda31a78442021-09-12 15:18:25 +053037 getHostState();
Chicago Duan184f6022020-04-17 11:30:49 +080038 if (hasError || completed)
39 {
40 return;
41 }
42
Manojkiran Eda31a78442021-09-12 15:18:25 +053043 auto rc = getEffecterID();
Tom Joseph29d22112020-11-18 15:05:15 +053044 if (completed)
45 {
Riya Dixit49cfb132023-03-02 04:26:53 -060046 error("pldm-softpoweroff: effecter to initiate softoff not found");
Tom Joseph29d22112020-11-18 15:05:15 +053047 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 {
Riya Dixit49cfb132023-03-02 04:26:53 -060058 error("Message get Sensor PDRs error. PLDM error code = {RC}", "RC",
59 lg2::hex, static_cast<int>(rc));
Chicago Duan184f6022020-04-17 11:30:49 +080060 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 {
Riya Dixit49cfb132023-03-02 04:26:53 -060095 error("PLDM host soft off: Can't get current host state.");
Chicago Duan184f6022020-04-17 11:30:49 +080096 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 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600122 error("PLDM soft off: Failure to STOP the timer. ERRNO={RC}", "RC",
123 rc);
Chicago Duan184f6022020-04-17 11:30:49 +0800124 }
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
vkaverap@in.ibm.com91a092f2023-09-18 23:39:44 -0500147 auto VMMResponseMsg = bus.call(VMMMethod, dbusTimeout);
Chicago Duan184f6022020-04-17 11:30:49 +0800148
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 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600166 error("PLDM soft off: Error get VMM PDR,ERROR={ERR_EXCEP}", "ERR_EXCEP",
167 e.what());
Chicago Duan184f6022020-04-17 11:30:49 +0800168 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
vkaverap@in.ibm.com91a092f2023-09-18 23:39:44 -0500189 auto sysFwResponseMsg = bus.call(sysFwMethod, dbusTimeout);
Chicago Duan184f6022020-04-17 11:30:49 +0800190
191 sysFwResponseMsg.read(sysFwResponse);
192
193 if (sysFwResponse.size() == 0)
194 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600195 error("No effecter ID has been found that matches the criteria");
Chicago Duan184f6022020-04-17 11:30:49 +0800196 return PLDM_ERROR;
197 }
198
199 for (auto& rep : sysFwResponse)
200 {
201 auto sysFwPdr =
202 reinterpret_cast<pldm_state_effecter_pdr*>(rep.data());
203 effecterID = sysFwPdr->effecter_id;
204 }
205 }
Patrick Williams84b790c2022-07-22 19:26:56 -0500206 catch (const sdbusplus::exception_t& e)
Chicago Duan184f6022020-04-17 11:30:49 +0800207 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600208 error("PLDM soft off: Error get system firmware PDR,ERROR={ERR_EXCEP}",
209 "ERR_EXCEP", e.what());
Tom Joseph29d22112020-11-18 15:05:15 +0530210 completed = true;
Chicago Duan184f6022020-04-17 11:30:49 +0800211 return PLDM_ERROR;
212 }
213
214 return PLDM_SUCCESS;
215}
216
217int SoftPowerOff::getSensorInfo()
218{
219 pldm::pdr::EntityType entityType;
220
221 entityType = VMMPdrExist ? PLDM_ENTITY_VIRTUAL_MACHINE_MANAGER
222 : PLDM_ENTITY_SYS_FIRMWARE;
223
224 // The Virtual machine manager/System firmware is logical entity, so bit 15
225 // need to be set.
226 entityType = entityType | 0x8000;
227
228 try
229 {
230 auto& bus = pldm::utils::DBusHandler::getBus();
231 std::vector<std::vector<uint8_t>> Response{};
232 auto method = bus.new_method_call(
233 "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",
234 "xyz.openbmc_project.PLDM.PDR", "FindStateSensorPDR");
235 method.append(TID, entityType,
236 (uint16_t)PLDM_STATE_SET_SW_TERMINATION_STATUS);
237
vkaverap@in.ibm.com91a092f2023-09-18 23:39:44 -0500238 auto ResponseMsg = bus.call(method, dbusTimeout);
Chicago Duan184f6022020-04-17 11:30:49 +0800239
240 ResponseMsg.read(Response);
241
242 if (Response.size() == 0)
243 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600244 error("No sensor PDR has been found that matches the criteria");
Chicago Duan184f6022020-04-17 11:30:49 +0800245 return PLDM_ERROR;
246 }
247
ThuBaNguyen499a29d2023-05-30 06:32:06 +0700248 pldm_state_sensor_pdr* pdr = nullptr;
Chicago Duan184f6022020-04-17 11:30:49 +0800249 for (auto& rep : Response)
250 {
251 pdr = reinterpret_cast<pldm_state_sensor_pdr*>(rep.data());
Manojkiran Eda31a78442021-09-12 15:18:25 +0530252 if (!pdr)
253 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600254 error("Failed to get state sensor PDR.");
Manojkiran Eda31a78442021-09-12 15:18:25 +0530255 return PLDM_ERROR;
256 }
Manojkiran Edabcf91ac2021-03-14 13:50:48 +0530257 }
258
Chicago Duan184f6022020-04-17 11:30:49 +0800259 sensorID = pdr->sensor_id;
260
261 auto compositeSensorCount = pdr->composite_sensor_count;
262 auto possibleStatesStart = pdr->possible_states;
263
264 for (auto offset = 0; offset < compositeSensorCount; offset++)
265 {
266 auto possibleStates =
267 reinterpret_cast<state_sensor_possible_states*>(
268 possibleStatesStart);
269 auto setId = possibleStates->state_set_id;
270 auto possibleStateSize = possibleStates->possible_states_size;
271
272 if (setId == PLDM_STATE_SET_SW_TERMINATION_STATUS)
273 {
274 sensorOffset = offset;
275 break;
276 }
Patrick Williams6da4f912023-05-10 07:50:53 -0500277 possibleStatesStart += possibleStateSize + sizeof(setId) +
278 sizeof(possibleStateSize);
Chicago Duan184f6022020-04-17 11:30:49 +0800279 }
280 }
Patrick Williams84b790c2022-07-22 19:26:56 -0500281 catch (const sdbusplus::exception_t& e)
Chicago Duan184f6022020-04-17 11:30:49 +0800282 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600283 error("PLDM soft off: Error get State Sensor PDR,ERROR={ERR_EXCEP}",
284 "ERR_EXCEP", e.what());
Chicago Duan184f6022020-04-17 11:30:49 +0800285 return PLDM_ERROR;
286 }
287
288 return PLDM_SUCCESS;
289}
290
291int SoftPowerOff::hostSoftOff(sdeventplus::Event& event)
292{
293 constexpr uint8_t effecterCount = 1;
294 uint8_t mctpEID;
295 uint8_t instanceID;
296
297 mctpEID = pldm::utils::readHostEID();
Dung Cao3d03f3f2023-09-07 06:51:33 +0000298 // TODO: fix mapping to work around OpenBMC ecosystem deficiencies
299 pldm_tid_t pldmTID = static_cast<pldm_tid_t>(mctpEID);
Chicago Duan184f6022020-04-17 11:30:49 +0800300
301 std::array<uint8_t, sizeof(pldm_msg_hdr) + sizeof(effecterID) +
302 sizeof(effecterCount) +
303 sizeof(set_effecter_state_field)>
304 requestMsg{};
305 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
306 set_effecter_state_field stateField{
307 PLDM_REQUEST_SET, PLDM_SW_TERM_GRACEFUL_SHUTDOWN_REQUESTED};
Dung Cao3d03f3f2023-09-07 06:51:33 +0000308 pldm::InstanceIdDb instanceIdDb;
309 instanceID = instanceIdDb.next(pldmTID);
Chicago Duan184f6022020-04-17 11:30:49 +0800310 auto rc = encode_set_state_effecter_states_req(
311 instanceID, effecterID, effecterCount, &stateField, request);
312 if (rc != PLDM_SUCCESS)
313 {
Dung Cao3d03f3f2023-09-07 06:51:33 +0000314 instanceIdDb.free(pldmTID, instanceID);
Riya Dixit49cfb132023-03-02 04:26:53 -0600315 error("Message encode failure. PLDM error code = {RC}", "RC", lg2::hex,
316 static_cast<int>(rc));
Chicago Duan184f6022020-04-17 11:30:49 +0800317 return PLDM_ERROR;
318 }
319
320 // Open connection to MCTP socket
321 int fd = pldm_open();
322 if (-1 == fd)
323 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600324 error("Failed to connect to mctp demux daemon");
Chicago Duan184f6022020-04-17 11:30:49 +0800325 return PLDM_ERROR;
326 }
327
328 // Add a timer to the event loop, default 30s.
Patrick Williams6da4f912023-05-10 07:50:53 -0500329 auto timerCallback =
Dung Cao3d03f3f2023-09-07 06:51:33 +0000330 [=, this](Timer& /*source*/, Timer::TimePoint /*time*/) mutable {
Chicago Duan184f6022020-04-17 11:30:49 +0800331 if (!responseReceived)
332 {
Dung Cao3d03f3f2023-09-07 06:51:33 +0000333 instanceIdDb.free(pldmTID, instanceID);
Riya Dixit49cfb132023-03-02 04:26:53 -0600334 error(
335 "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 +0800336 exit(-1);
337 }
338 return;
339 };
340 Timer time(event, (Clock(event).now() + std::chrono::seconds{30}),
341 std::chrono::seconds{1}, std::move(timerCallback));
342
343 // Add a callback to handle EPOLLIN on fd
Dung Cao3d03f3f2023-09-07 06:51:33 +0000344 auto callback = [=, this](IO& io, int fd, uint32_t revents) mutable {
Chicago Duan184f6022020-04-17 11:30:49 +0800345 if (!(revents & EPOLLIN))
346 {
347 return;
348 }
349
350 uint8_t* responseMsg = nullptr;
351 size_t responseMsgSize{};
Dung Cao3d03f3f2023-09-07 06:51:33 +0000352 pldm_tid_t srcTID = pldmTID;
Chicago Duan184f6022020-04-17 11:30:49 +0800353
354 auto rc = pldm_recv(mctpEID, fd, request->hdr.instance_id, &responseMsg,
355 &responseMsgSize);
356 if (rc)
357 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600358 error("Soft off: failed to recv pldm data. PLDM RC = {RC}", "RC",
359 static_cast<int>(rc));
Chicago Duan184f6022020-04-17 11:30:49 +0800360 return;
361 }
362
363 std::unique_ptr<uint8_t, decltype(std::free)*> responseMsgPtr{
364 responseMsg, std::free};
365
366 // We've got the response meant for the PLDM request msg that was
367 // sent out
368 io.set_enabled(Enabled::Off);
369 auto response = reinterpret_cast<pldm_msg*>(responseMsgPtr.get());
Dung Cao3d03f3f2023-09-07 06:51:33 +0000370
371 if (srcTID != pldmTID ||
372 !pldm_msg_hdr_correlate_response(&request->hdr, &response->hdr))
373 {
374 /* This isn't the response we were looking for */
375 return;
376 }
377
378 /* We have the right response, release the instance ID and process */
379 io.set_enabled(Enabled::Off);
380 instanceIdDb.free(pldmTID, instanceID);
381
George Liu9915d022021-12-21 14:04:31 +0800382 if (response->payload[0] != PLDM_SUCCESS)
383 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600384 error("Getting the wrong response. PLDM RC = {RC}", "RC",
385 (unsigned)response->payload[0]);
George Liu9915d022021-12-21 14:04:31 +0800386 exit(-1);
387 }
Chicago Duan184f6022020-04-17 11:30:49 +0800388
389 responseReceived = true;
390
391 // Start Timer
392 using namespace std::chrono;
393 auto timeMicroseconds =
394 duration_cast<microseconds>(seconds(SOFTOFF_TIMEOUT_SECONDS));
395
396 auto ret = startTimer(timeMicroseconds);
397 if (ret < 0)
398 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600399 error(
400 "Failure to start Host soft off wait timer, ERRNO = {RET}. Exit the pldm-softpoweroff",
401 "RET", ret);
Chicago Duan184f6022020-04-17 11:30:49 +0800402 exit(-1);
403 }
404 else
405 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600406 error(
407 "Timer started waiting for host soft off, TIMEOUT_IN_SEC = {TIMEOUT_SEC}",
408 "TIMEOUT_SEC", SOFTOFF_TIMEOUT_SECONDS);
Chicago Duan184f6022020-04-17 11:30:49 +0800409 }
410 return;
411 };
412 IO io(event, fd, EPOLLIN, std::move(callback));
413
414 // Send PLDM Request message - pldm_send doesn't wait for response
415 rc = pldm_send(mctpEID, fd, requestMsg.data(), requestMsg.size());
Sampa Misra9f8d2b02021-03-24 08:33:14 +0000416 if (0 > rc)
Chicago Duan184f6022020-04-17 11:30:49 +0800417 {
Dung Cao3d03f3f2023-09-07 06:51:33 +0000418 instanceIdDb.free(pldmTID, instanceID);
Riya Dixit49cfb132023-03-02 04:26:53 -0600419 error(
420 "Failed to send message/receive response. RC = {RC}, errno = {ERR}",
421 "RC", static_cast<int>(rc), "ERR", errno);
Chicago Duan184f6022020-04-17 11:30:49 +0800422 return PLDM_ERROR;
423 }
424
425 // Time out or soft off complete
426 while (!isCompleted() && !isTimerExpired())
427 {
428 try
429 {
430 event.run(std::nullopt);
431 }
432 catch (const sdeventplus::SdEventError& e)
433 {
Dung Cao3d03f3f2023-09-07 06:51:33 +0000434 instanceIdDb.free(pldmTID, instanceID);
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