blob: 78a59ede4ee6e141eebfcdbbcb96a60af8d9a218 [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>
Chicago Duan184f6022020-04-17 11:30:49 +080019
Riya Dixit49cfb132023-03-02 04:26:53 -060020PHOSPHOR_LOG2_USING;
21
Chicago Duan184f6022020-04-17 11:30:49 +080022namespace pldm
23{
Chicago Duan184f6022020-04-17 11:30:49 +080024using 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
Patrick Williams84b790c2022-07-22 19:26:56 -050033SoftPowerOff::SoftPowerOff(sdbusplus::bus_t& bus, sd_event* event) :
Chicago Duan184f6022020-04-17 11:30:49 +080034 bus(bus), timer(event)
35{
Manojkiran Eda31a78442021-09-12 15:18:25 +053036 getHostState();
Chicago Duan184f6022020-04-17 11:30:49 +080037 if (hasError || completed)
38 {
39 return;
40 }
41
Manojkiran Eda31a78442021-09-12 15:18:25 +053042 auto rc = getEffecterID();
Tom Joseph29d22112020-11-18 15:05:15 +053043 if (completed)
44 {
Riya Dixit49cfb132023-03-02 04:26:53 -060045 error("pldm-softpoweroff: effecter to initiate softoff not found");
Tom Joseph29d22112020-11-18 15:05:15 +053046 return;
47 }
48 else if (rc != PLDM_SUCCESS)
Chicago Duan184f6022020-04-17 11:30:49 +080049 {
50 hasError = true;
51 return;
52 }
53
54 rc = getSensorInfo();
55 if (rc != PLDM_SUCCESS)
56 {
Riya Dixit49cfb132023-03-02 04:26:53 -060057 error("Message get Sensor PDRs error. PLDM error code = {RC}", "RC",
58 lg2::hex, static_cast<int>(rc));
Chicago Duan184f6022020-04-17 11:30:49 +080059 hasError = true;
60 return;
61 }
62
63 // Matches on the pldm StateSensorEvent signal
64 pldmEventSignal = std::make_unique<sdbusplus::bus::match_t>(
65 bus,
66 sdbusRule::type::signal() + sdbusRule::member("StateSensorEvent") +
67 sdbusRule::path("/xyz/openbmc_project/pldm") +
68 sdbusRule::interface("xyz.openbmc_project.PLDM.Event"),
69 std::bind(std::mem_fn(&SoftPowerOff::hostSoftOffComplete), this,
70 std::placeholders::_1));
71}
72
73int SoftPowerOff::getHostState()
74{
75 try
76 {
77 pldm::utils::PropertyValue propertyValue =
78 pldm::utils::DBusHandler().getDbusPropertyVariant(
79 "/xyz/openbmc_project/state/host0", "CurrentHostState",
80 "xyz.openbmc_project.State.Host");
81
Andrew Geissler5b5fa432021-01-22 16:27:24 -060082 if ((std::get<std::string>(propertyValue) !=
83 "xyz.openbmc_project.State.Host.HostState.Running") &&
84 (std::get<std::string>(propertyValue) !=
85 "xyz.openbmc_project.State.Host.HostState.TransitioningToOff"))
Chicago Duan184f6022020-04-17 11:30:49 +080086 {
87 // Host state is not "Running", this app should return success
88 completed = true;
89 return PLDM_SUCCESS;
90 }
91 }
92 catch (const std::exception& e)
93 {
Kamalkumar Patel58cbcaf2023-10-06 03:48:25 -050094 error("PLDM host soft off: Can't get current host state: {ERROR}",
95 "ERROR", e);
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;
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000294 PldmTransport pldmTransport{};
Chicago Duan184f6022020-04-17 11:30:49 +0800295 uint8_t instanceID;
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000296 uint8_t mctpEID;
Chicago Duan184f6022020-04-17 11:30:49 +0800297
298 mctpEID = pldm::utils::readHostEID();
Dung Cao3d03f3f2023-09-07 06:51:33 +0000299 // TODO: fix mapping to work around OpenBMC ecosystem deficiencies
300 pldm_tid_t pldmTID = static_cast<pldm_tid_t>(mctpEID);
Chicago Duan184f6022020-04-17 11:30:49 +0800301
302 std::array<uint8_t, sizeof(pldm_msg_hdr) + sizeof(effecterID) +
303 sizeof(effecterCount) +
304 sizeof(set_effecter_state_field)>
305 requestMsg{};
306 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
307 set_effecter_state_field stateField{
308 PLDM_REQUEST_SET, PLDM_SW_TERM_GRACEFUL_SHUTDOWN_REQUESTED};
Dung Cao3d03f3f2023-09-07 06:51:33 +0000309 pldm::InstanceIdDb instanceIdDb;
310 instanceID = instanceIdDb.next(pldmTID);
Chicago Duan184f6022020-04-17 11:30:49 +0800311 auto rc = encode_set_state_effecter_states_req(
312 instanceID, effecterID, effecterCount, &stateField, request);
313 if (rc != PLDM_SUCCESS)
314 {
Dung Cao3d03f3f2023-09-07 06:51:33 +0000315 instanceIdDb.free(pldmTID, instanceID);
Riya Dixit49cfb132023-03-02 04:26:53 -0600316 error("Message encode failure. PLDM error code = {RC}", "RC", lg2::hex,
317 static_cast<int>(rc));
Chicago Duan184f6022020-04-17 11:30:49 +0800318 return PLDM_ERROR;
319 }
320
Chicago Duan184f6022020-04-17 11:30:49 +0800321 // Add a timer to the event loop, default 30s.
Patrick Williamsa6756622023-10-20 11:19:15 -0500322 auto timerCallback = [=, this](Timer& /*source*/,
323 Timer::TimePoint /*time*/) mutable {
Chicago Duan184f6022020-04-17 11:30:49 +0800324 if (!responseReceived)
325 {
Dung Cao3d03f3f2023-09-07 06:51:33 +0000326 instanceIdDb.free(pldmTID, instanceID);
Riya Dixit49cfb132023-03-02 04:26:53 -0600327 error(
328 "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 +0800329 exit(-1);
330 }
331 return;
332 };
333 Timer time(event, (Clock(event).now() + std::chrono::seconds{30}),
334 std::chrono::seconds{1}, std::move(timerCallback));
335
336 // Add a callback to handle EPOLLIN on fd
Patrick Williamsa6756622023-10-20 11:19:15 -0500337 auto callback = [=, &pldmTransport, this](IO& io, int fd,
338 uint32_t revents) mutable {
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000339 if (fd != pldmTransport.getEventSource())
340 {
341 return;
342 }
343
Chicago Duan184f6022020-04-17 11:30:49 +0800344 if (!(revents & EPOLLIN))
345 {
346 return;
347 }
348
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000349 void* responseMsg = nullptr;
Chicago Duan184f6022020-04-17 11:30:49 +0800350 size_t responseMsgSize{};
Dung Cao3d03f3f2023-09-07 06:51:33 +0000351 pldm_tid_t srcTID = pldmTID;
Chicago Duan184f6022020-04-17 11:30:49 +0800352
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000353 auto rc = pldmTransport.recvMsg(pldmTID, responseMsg, responseMsgSize);
Chicago Duan184f6022020-04-17 11:30:49 +0800354 if (rc)
355 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600356 error("Soft off: failed to recv pldm data. PLDM RC = {RC}", "RC",
357 static_cast<int>(rc));
Chicago Duan184f6022020-04-17 11:30:49 +0800358 return;
359 }
360
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000361 std::unique_ptr<void, decltype(std::free)*> responseMsgPtr{responseMsg,
362 std::free};
Chicago Duan184f6022020-04-17 11:30:49 +0800363
364 // We've got the response meant for the PLDM request msg that was
365 // sent out
366 io.set_enabled(Enabled::Off);
367 auto response = reinterpret_cast<pldm_msg*>(responseMsgPtr.get());
Dung Cao3d03f3f2023-09-07 06:51:33 +0000368
369 if (srcTID != pldmTID ||
370 !pldm_msg_hdr_correlate_response(&request->hdr, &response->hdr))
371 {
372 /* This isn't the response we were looking for */
373 return;
374 }
375
376 /* We have the right response, release the instance ID and process */
377 io.set_enabled(Enabled::Off);
378 instanceIdDb.free(pldmTID, instanceID);
379
George Liu9915d022021-12-21 14:04:31 +0800380 if (response->payload[0] != PLDM_SUCCESS)
381 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600382 error("Getting the wrong response. PLDM RC = {RC}", "RC",
383 (unsigned)response->payload[0]);
George Liu9915d022021-12-21 14:04:31 +0800384 exit(-1);
385 }
Chicago Duan184f6022020-04-17 11:30:49 +0800386
387 responseReceived = true;
388
389 // Start Timer
390 using namespace std::chrono;
391 auto timeMicroseconds =
392 duration_cast<microseconds>(seconds(SOFTOFF_TIMEOUT_SECONDS));
393
394 auto ret = startTimer(timeMicroseconds);
395 if (ret < 0)
396 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600397 error(
398 "Failure to start Host soft off wait timer, ERRNO = {RET}. Exit the pldm-softpoweroff",
399 "RET", ret);
Chicago Duan184f6022020-04-17 11:30:49 +0800400 exit(-1);
401 }
402 else
403 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600404 error(
405 "Timer started waiting for host soft off, TIMEOUT_IN_SEC = {TIMEOUT_SEC}",
406 "TIMEOUT_SEC", SOFTOFF_TIMEOUT_SECONDS);
Chicago Duan184f6022020-04-17 11:30:49 +0800407 }
408 return;
409 };
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000410 IO io(event, pldmTransport.getEventSource(), EPOLLIN, std::move(callback));
Chicago Duan184f6022020-04-17 11:30:49 +0800411
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000412 // Asynchronously send the PLDM request
413 rc = pldmTransport.sendMsg(pldmTID, requestMsg.data(), requestMsg.size());
Sampa Misra9f8d2b02021-03-24 08:33:14 +0000414 if (0 > rc)
Chicago Duan184f6022020-04-17 11:30:49 +0800415 {
Dung Cao3d03f3f2023-09-07 06:51:33 +0000416 instanceIdDb.free(pldmTID, instanceID);
Riya Dixit49cfb132023-03-02 04:26:53 -0600417 error(
418 "Failed to send message/receive response. RC = {RC}, errno = {ERR}",
419 "RC", static_cast<int>(rc), "ERR", errno);
Chicago Duan184f6022020-04-17 11:30:49 +0800420 return PLDM_ERROR;
421 }
422
423 // Time out or soft off complete
424 while (!isCompleted() && !isTimerExpired())
425 {
426 try
427 {
428 event.run(std::nullopt);
429 }
430 catch (const sdeventplus::SdEventError& e)
431 {
Dung Cao3d03f3f2023-09-07 06:51:33 +0000432 instanceIdDb.free(pldmTID, instanceID);
Riya Dixit49cfb132023-03-02 04:26:53 -0600433 error(
434 "PLDM host soft off: Failure in processing request.ERROR= {ERR_EXCEP}",
435 "ERR_EXCEP", e.what());
Chicago Duan184f6022020-04-17 11:30:49 +0800436 return PLDM_ERROR;
437 }
438 }
439
440 return PLDM_SUCCESS;
441}
442
443int SoftPowerOff::startTimer(const std::chrono::microseconds& usec)
444{
445 return timer.start(usec);
446}
447} // namespace pldm