blob: 2e67db983bff20ebace47af9e097ca288f431df0 [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"
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +10004#include "common/transport.hpp"
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>
George Liuc453e162022-12-21 17:16:23 +08009#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 {
Kamalkumar Patel58cbcaf2023-10-06 03:48:25 -050095 error("PLDM host soft off: Can't get current host state: {ERROR}",
96 "ERROR", e);
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.com91a092f2023-09-18 23:39:44 -0500148 auto VMMResponseMsg = bus.call(VMMMethod, dbusTimeout);
Chicago Duan184f6022020-04-17 11:30:49 +0800149
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
vkaverap@in.ibm.com91a092f2023-09-18 23:39:44 -0500190 auto sysFwResponseMsg = bus.call(sysFwMethod, dbusTimeout);
Chicago Duan184f6022020-04-17 11:30:49 +0800191
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
vkaverap@in.ibm.com91a092f2023-09-18 23:39:44 -0500239 auto ResponseMsg = bus.call(method, dbusTimeout);
Chicago Duan184f6022020-04-17 11:30:49 +0800240
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;
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000295 PldmTransport pldmTransport{};
Chicago Duan184f6022020-04-17 11:30:49 +0800296 uint8_t instanceID;
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000297 uint8_t mctpEID;
Chicago Duan184f6022020-04-17 11:30:49 +0800298
299 mctpEID = pldm::utils::readHostEID();
Dung Cao3d03f3f2023-09-07 06:51:33 +0000300 // TODO: fix mapping to work around OpenBMC ecosystem deficiencies
301 pldm_tid_t pldmTID = static_cast<pldm_tid_t>(mctpEID);
Chicago Duan184f6022020-04-17 11:30:49 +0800302
303 std::array<uint8_t, sizeof(pldm_msg_hdr) + sizeof(effecterID) +
304 sizeof(effecterCount) +
305 sizeof(set_effecter_state_field)>
306 requestMsg{};
307 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
308 set_effecter_state_field stateField{
309 PLDM_REQUEST_SET, PLDM_SW_TERM_GRACEFUL_SHUTDOWN_REQUESTED};
Dung Cao3d03f3f2023-09-07 06:51:33 +0000310 pldm::InstanceIdDb instanceIdDb;
311 instanceID = instanceIdDb.next(pldmTID);
Chicago Duan184f6022020-04-17 11:30:49 +0800312 auto rc = encode_set_state_effecter_states_req(
313 instanceID, effecterID, effecterCount, &stateField, request);
314 if (rc != PLDM_SUCCESS)
315 {
Dung Cao3d03f3f2023-09-07 06:51:33 +0000316 instanceIdDb.free(pldmTID, instanceID);
Riya Dixit49cfb132023-03-02 04:26:53 -0600317 error("Message encode failure. PLDM error code = {RC}", "RC", lg2::hex,
318 static_cast<int>(rc));
Chicago Duan184f6022020-04-17 11:30:49 +0800319 return PLDM_ERROR;
320 }
321
Chicago Duan184f6022020-04-17 11:30:49 +0800322 // Add a timer to the event loop, default 30s.
Patrick Williamsa6756622023-10-20 11:19:15 -0500323 auto timerCallback = [=, this](Timer& /*source*/,
324 Timer::TimePoint /*time*/) mutable {
Chicago Duan184f6022020-04-17 11:30:49 +0800325 if (!responseReceived)
326 {
Dung Cao3d03f3f2023-09-07 06:51:33 +0000327 instanceIdDb.free(pldmTID, instanceID);
Riya Dixit49cfb132023-03-02 04:26:53 -0600328 error(
329 "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 +0800330 exit(-1);
331 }
332 return;
333 };
334 Timer time(event, (Clock(event).now() + std::chrono::seconds{30}),
335 std::chrono::seconds{1}, std::move(timerCallback));
336
337 // Add a callback to handle EPOLLIN on fd
Patrick Williamsa6756622023-10-20 11:19:15 -0500338 auto callback = [=, &pldmTransport, this](IO& io, int fd,
339 uint32_t revents) mutable {
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000340 if (fd != pldmTransport.getEventSource())
341 {
342 return;
343 }
344
Chicago Duan184f6022020-04-17 11:30:49 +0800345 if (!(revents & EPOLLIN))
346 {
347 return;
348 }
349
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000350 void* responseMsg = nullptr;
Chicago Duan184f6022020-04-17 11:30:49 +0800351 size_t responseMsgSize{};
Dung Cao3d03f3f2023-09-07 06:51:33 +0000352 pldm_tid_t srcTID = pldmTID;
Chicago Duan184f6022020-04-17 11:30:49 +0800353
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000354 auto rc = pldmTransport.recvMsg(pldmTID, responseMsg, responseMsgSize);
Chicago Duan184f6022020-04-17 11:30:49 +0800355 if (rc)
356 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600357 error("Soft off: failed to recv pldm data. PLDM RC = {RC}", "RC",
358 static_cast<int>(rc));
Chicago Duan184f6022020-04-17 11:30:49 +0800359 return;
360 }
361
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000362 std::unique_ptr<void, decltype(std::free)*> responseMsgPtr{responseMsg,
363 std::free};
Chicago Duan184f6022020-04-17 11:30:49 +0800364
365 // We've got the response meant for the PLDM request msg that was
366 // sent out
367 io.set_enabled(Enabled::Off);
368 auto response = reinterpret_cast<pldm_msg*>(responseMsgPtr.get());
Dung Cao3d03f3f2023-09-07 06:51:33 +0000369
370 if (srcTID != pldmTID ||
371 !pldm_msg_hdr_correlate_response(&request->hdr, &response->hdr))
372 {
373 /* This isn't the response we were looking for */
374 return;
375 }
376
377 /* We have the right response, release the instance ID and process */
378 io.set_enabled(Enabled::Off);
379 instanceIdDb.free(pldmTID, instanceID);
380
George Liu9915d022021-12-21 14:04:31 +0800381 if (response->payload[0] != PLDM_SUCCESS)
382 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600383 error("Getting the wrong response. PLDM RC = {RC}", "RC",
384 (unsigned)response->payload[0]);
George Liu9915d022021-12-21 14:04:31 +0800385 exit(-1);
386 }
Chicago Duan184f6022020-04-17 11:30:49 +0800387
388 responseReceived = true;
389
390 // Start Timer
391 using namespace std::chrono;
392 auto timeMicroseconds =
393 duration_cast<microseconds>(seconds(SOFTOFF_TIMEOUT_SECONDS));
394
395 auto ret = startTimer(timeMicroseconds);
396 if (ret < 0)
397 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600398 error(
399 "Failure to start Host soft off wait timer, ERRNO = {RET}. Exit the pldm-softpoweroff",
400 "RET", ret);
Chicago Duan184f6022020-04-17 11:30:49 +0800401 exit(-1);
402 }
403 else
404 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600405 error(
406 "Timer started waiting for host soft off, TIMEOUT_IN_SEC = {TIMEOUT_SEC}",
407 "TIMEOUT_SEC", SOFTOFF_TIMEOUT_SECONDS);
Chicago Duan184f6022020-04-17 11:30:49 +0800408 }
409 return;
410 };
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000411 IO io(event, pldmTransport.getEventSource(), EPOLLIN, std::move(callback));
Chicago Duan184f6022020-04-17 11:30:49 +0800412
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000413 // Asynchronously send the PLDM request
414 rc = pldmTransport.sendMsg(pldmTID, requestMsg.data(), requestMsg.size());
Sampa Misra9f8d2b02021-03-24 08:33:14 +0000415 if (0 > rc)
Chicago Duan184f6022020-04-17 11:30:49 +0800416 {
Dung Cao3d03f3f2023-09-07 06:51:33 +0000417 instanceIdDb.free(pldmTID, instanceID);
Riya Dixit49cfb132023-03-02 04:26:53 -0600418 error(
419 "Failed to send message/receive response. RC = {RC}, errno = {ERR}",
420 "RC", static_cast<int>(rc), "ERR", errno);
Chicago Duan184f6022020-04-17 11:30:49 +0800421 return PLDM_ERROR;
422 }
423
424 // Time out or soft off complete
425 while (!isCompleted() && !isTimerExpired())
426 {
427 try
428 {
429 event.run(std::nullopt);
430 }
431 catch (const sdeventplus::SdEventError& e)
432 {
Dung Cao3d03f3f2023-09-07 06:51:33 +0000433 instanceIdDb.free(pldmTID, instanceID);
Riya Dixit49cfb132023-03-02 04:26:53 -0600434 error(
435 "PLDM host soft off: Failure in processing request.ERROR= {ERR_EXCEP}",
436 "ERR_EXCEP", e.what());
Chicago Duan184f6022020-04-17 11:30:49 +0800437 return PLDM_ERROR;
438 }
439 }
440
441 return PLDM_SUCCESS;
442}
443
444int SoftPowerOff::startTimer(const std::chrono::microseconds& usec)
445{
446 return timer.start(usec);
447}
448} // namespace pldm