xref: /openbmc/phosphor-logging/extensions/openpower-pels/pldm_interface.cpp (revision 5c350fdfb1a92ebdf33d960cc69ac16413974889)
1*5c350fdfSMatt Spinler /**
2*5c350fdfSMatt Spinler  * Copyright © 2019 IBM Corporation
3*5c350fdfSMatt Spinler  *
4*5c350fdfSMatt Spinler  * Licensed under the Apache License, Version 2.0 (the "License");
5*5c350fdfSMatt Spinler  * you may not use this file except in compliance with the License.
6*5c350fdfSMatt Spinler  * You may obtain a copy of the License at
7*5c350fdfSMatt Spinler  *
8*5c350fdfSMatt Spinler  *     http://www.apache.org/licenses/LICENSE-2.0
9*5c350fdfSMatt Spinler  *
10*5c350fdfSMatt Spinler  * Unless required by applicable law or agreed to in writing, software
11*5c350fdfSMatt Spinler  * distributed under the License is distributed on an "AS IS" BASIS,
12*5c350fdfSMatt Spinler  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*5c350fdfSMatt Spinler  * See the License for the specific language governing permissions and
14*5c350fdfSMatt Spinler  * limitations under the License.
15*5c350fdfSMatt Spinler  */
16*5c350fdfSMatt Spinler #include "pldm_interface.hpp"
17*5c350fdfSMatt Spinler 
18*5c350fdfSMatt Spinler #include <libpldm/base.h>
19*5c350fdfSMatt Spinler #include <libpldm/file_io.h>
20*5c350fdfSMatt Spinler #include <unistd.h>
21*5c350fdfSMatt Spinler 
22*5c350fdfSMatt Spinler #include <fstream>
23*5c350fdfSMatt Spinler #include <phosphor-logging/log.hpp>
24*5c350fdfSMatt Spinler 
25*5c350fdfSMatt Spinler namespace openpower::pels
26*5c350fdfSMatt Spinler {
27*5c350fdfSMatt Spinler 
28*5c350fdfSMatt Spinler using namespace phosphor::logging;
29*5c350fdfSMatt Spinler using namespace sdeventplus;
30*5c350fdfSMatt Spinler using namespace sdeventplus::source;
31*5c350fdfSMatt Spinler 
32*5c350fdfSMatt Spinler constexpr auto eidPath = "/usr/share/pldm/host_eid";
33*5c350fdfSMatt Spinler constexpr mctp_eid_t defaultEIDValue = 9;
34*5c350fdfSMatt Spinler 
35*5c350fdfSMatt Spinler constexpr uint16_t pelFileType = 0;
36*5c350fdfSMatt Spinler 
37*5c350fdfSMatt Spinler PLDMInterface::~PLDMInterface()
38*5c350fdfSMatt Spinler {
39*5c350fdfSMatt Spinler     closeFD();
40*5c350fdfSMatt Spinler }
41*5c350fdfSMatt Spinler 
42*5c350fdfSMatt Spinler void PLDMInterface::closeFD()
43*5c350fdfSMatt Spinler {
44*5c350fdfSMatt Spinler     if (_fd >= 0)
45*5c350fdfSMatt Spinler     {
46*5c350fdfSMatt Spinler         close(_fd);
47*5c350fdfSMatt Spinler         _fd = -1;
48*5c350fdfSMatt Spinler     }
49*5c350fdfSMatt Spinler }
50*5c350fdfSMatt Spinler 
51*5c350fdfSMatt Spinler void PLDMInterface::readEID()
52*5c350fdfSMatt Spinler {
53*5c350fdfSMatt Spinler     _eid = defaultEIDValue;
54*5c350fdfSMatt Spinler 
55*5c350fdfSMatt Spinler     std::ifstream eidFile{eidPath};
56*5c350fdfSMatt Spinler     if (!eidFile.good())
57*5c350fdfSMatt Spinler     {
58*5c350fdfSMatt Spinler         log<level::ERR>("Could not open host EID file");
59*5c350fdfSMatt Spinler     }
60*5c350fdfSMatt Spinler     else
61*5c350fdfSMatt Spinler     {
62*5c350fdfSMatt Spinler         std::string eid;
63*5c350fdfSMatt Spinler         eidFile >> eid;
64*5c350fdfSMatt Spinler         if (!eid.empty())
65*5c350fdfSMatt Spinler         {
66*5c350fdfSMatt Spinler             _eid = atoi(eid.c_str());
67*5c350fdfSMatt Spinler         }
68*5c350fdfSMatt Spinler         else
69*5c350fdfSMatt Spinler         {
70*5c350fdfSMatt Spinler             log<level::ERR>("EID file was empty");
71*5c350fdfSMatt Spinler         }
72*5c350fdfSMatt Spinler     }
73*5c350fdfSMatt Spinler }
74*5c350fdfSMatt Spinler 
75*5c350fdfSMatt Spinler void PLDMInterface::open()
76*5c350fdfSMatt Spinler {
77*5c350fdfSMatt Spinler     _fd = pldm_open();
78*5c350fdfSMatt Spinler     if (_fd < 0)
79*5c350fdfSMatt Spinler     {
80*5c350fdfSMatt Spinler         auto e = errno;
81*5c350fdfSMatt Spinler         log<level::ERR>("pldm_open failed", entry("ERRNO=%d", e),
82*5c350fdfSMatt Spinler                         entry("RC=%d\n", _fd));
83*5c350fdfSMatt Spinler         throw std::exception{};
84*5c350fdfSMatt Spinler     }
85*5c350fdfSMatt Spinler }
86*5c350fdfSMatt Spinler 
87*5c350fdfSMatt Spinler CmdStatus PLDMInterface::sendNewLogCmd(uint32_t id, uint32_t size)
88*5c350fdfSMatt Spinler {
89*5c350fdfSMatt Spinler     try
90*5c350fdfSMatt Spinler     {
91*5c350fdfSMatt Spinler         closeFD();
92*5c350fdfSMatt Spinler 
93*5c350fdfSMatt Spinler         open();
94*5c350fdfSMatt Spinler 
95*5c350fdfSMatt Spinler         readInstanceID();
96*5c350fdfSMatt Spinler 
97*5c350fdfSMatt Spinler         registerReceiveCallback();
98*5c350fdfSMatt Spinler 
99*5c350fdfSMatt Spinler         doSend(id, size);
100*5c350fdfSMatt Spinler     }
101*5c350fdfSMatt Spinler     catch (const std::exception& e)
102*5c350fdfSMatt Spinler     {
103*5c350fdfSMatt Spinler         closeFD();
104*5c350fdfSMatt Spinler 
105*5c350fdfSMatt Spinler         _inProgress = false;
106*5c350fdfSMatt Spinler         _source.reset();
107*5c350fdfSMatt Spinler         return CmdStatus::failure;
108*5c350fdfSMatt Spinler     }
109*5c350fdfSMatt Spinler 
110*5c350fdfSMatt Spinler     _inProgress = true;
111*5c350fdfSMatt Spinler     _receiveTimer.restartOnce(_receiveTimeout);
112*5c350fdfSMatt Spinler     return CmdStatus::success;
113*5c350fdfSMatt Spinler }
114*5c350fdfSMatt Spinler 
115*5c350fdfSMatt Spinler void PLDMInterface::registerReceiveCallback()
116*5c350fdfSMatt Spinler {
117*5c350fdfSMatt Spinler     _source = std::make_unique<IO>(
118*5c350fdfSMatt Spinler         _event, _fd, EPOLLIN,
119*5c350fdfSMatt Spinler         std::bind(std::mem_fn(&PLDMInterface::receive), this,
120*5c350fdfSMatt Spinler                   std::placeholders::_1, std::placeholders::_2,
121*5c350fdfSMatt Spinler                   std::placeholders::_3));
122*5c350fdfSMatt Spinler }
123*5c350fdfSMatt Spinler 
124*5c350fdfSMatt Spinler void PLDMInterface::readInstanceID()
125*5c350fdfSMatt Spinler {
126*5c350fdfSMatt Spinler     try
127*5c350fdfSMatt Spinler     {
128*5c350fdfSMatt Spinler         _instanceID = _dataIface.getPLDMInstanceID(_eid);
129*5c350fdfSMatt Spinler     }
130*5c350fdfSMatt Spinler     catch (const std::exception& e)
131*5c350fdfSMatt Spinler     {
132*5c350fdfSMatt Spinler         log<level::ERR>(
133*5c350fdfSMatt Spinler             "Failed to get instance ID from PLDM Requester D-Bus daemon",
134*5c350fdfSMatt Spinler             entry("ERROR=%s", e.what()));
135*5c350fdfSMatt Spinler         throw;
136*5c350fdfSMatt Spinler     }
137*5c350fdfSMatt Spinler }
138*5c350fdfSMatt Spinler 
139*5c350fdfSMatt Spinler void PLDMInterface::doSend(uint32_t id, uint32_t size)
140*5c350fdfSMatt Spinler {
141*5c350fdfSMatt Spinler     std::array<uint8_t, sizeof(pldm_msg_hdr) + sizeof(pelFileType) +
142*5c350fdfSMatt Spinler                             sizeof(id) + sizeof(size)>
143*5c350fdfSMatt Spinler         requestMsg;
144*5c350fdfSMatt Spinler 
145*5c350fdfSMatt Spinler     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
146*5c350fdfSMatt Spinler 
147*5c350fdfSMatt Spinler     auto rc = encode_new_file_req(_instanceID, pelFileType, id, size, request);
148*5c350fdfSMatt Spinler     if (rc != PLDM_SUCCESS)
149*5c350fdfSMatt Spinler     {
150*5c350fdfSMatt Spinler         log<level::ERR>("encode_new_file_req failed", entry("RC=%d", rc));
151*5c350fdfSMatt Spinler         throw std::exception{};
152*5c350fdfSMatt Spinler     }
153*5c350fdfSMatt Spinler 
154*5c350fdfSMatt Spinler     rc = pldm_send(_eid, _fd, requestMsg.data(), requestMsg.size());
155*5c350fdfSMatt Spinler     if (rc < 0)
156*5c350fdfSMatt Spinler     {
157*5c350fdfSMatt Spinler         auto e = errno;
158*5c350fdfSMatt Spinler         log<level::ERR>("pldm_send failed", entry("RC=%d", rc),
159*5c350fdfSMatt Spinler                         entry("ERRNO=%d", e));
160*5c350fdfSMatt Spinler 
161*5c350fdfSMatt Spinler         throw std::exception{};
162*5c350fdfSMatt Spinler     }
163*5c350fdfSMatt Spinler }
164*5c350fdfSMatt Spinler 
165*5c350fdfSMatt Spinler void PLDMInterface::receive(IO& io, int fd, uint32_t revents)
166*5c350fdfSMatt Spinler {
167*5c350fdfSMatt Spinler     if (!(revents & EPOLLIN))
168*5c350fdfSMatt Spinler     {
169*5c350fdfSMatt Spinler         return;
170*5c350fdfSMatt Spinler     }
171*5c350fdfSMatt Spinler 
172*5c350fdfSMatt Spinler     uint8_t* responseMsg = nullptr;
173*5c350fdfSMatt Spinler     size_t responseSize = 0;
174*5c350fdfSMatt Spinler     ResponseStatus status = ResponseStatus::success;
175*5c350fdfSMatt Spinler 
176*5c350fdfSMatt Spinler     auto rc = pldm_recv(_eid, fd, _instanceID, &responseMsg, &responseSize);
177*5c350fdfSMatt Spinler     if (rc < 0)
178*5c350fdfSMatt Spinler     {
179*5c350fdfSMatt Spinler         if (rc == PLDM_REQUESTER_INSTANCE_ID_MISMATCH)
180*5c350fdfSMatt Spinler         {
181*5c350fdfSMatt Spinler             // We got a response to someone else's message. Ignore it.
182*5c350fdfSMatt Spinler             return;
183*5c350fdfSMatt Spinler         }
184*5c350fdfSMatt Spinler         else if (rc == PLDM_REQUESTER_NOT_RESP_MSG)
185*5c350fdfSMatt Spinler         {
186*5c350fdfSMatt Spinler             // Due to the MCTP loopback, we may get notified of the message
187*5c350fdfSMatt Spinler             // we just sent.
188*5c350fdfSMatt Spinler             return;
189*5c350fdfSMatt Spinler         }
190*5c350fdfSMatt Spinler 
191*5c350fdfSMatt Spinler         auto e = errno;
192*5c350fdfSMatt Spinler         log<level::ERR>("pldm_recv failed", entry("RC=%d", rc),
193*5c350fdfSMatt Spinler                         entry("ERRNO=%d", e));
194*5c350fdfSMatt Spinler         status = ResponseStatus::failure;
195*5c350fdfSMatt Spinler 
196*5c350fdfSMatt Spinler         responseMsg = nullptr;
197*5c350fdfSMatt Spinler     }
198*5c350fdfSMatt Spinler 
199*5c350fdfSMatt Spinler     _inProgress = false;
200*5c350fdfSMatt Spinler     _receiveTimer.setEnabled(false);
201*5c350fdfSMatt Spinler     closeFD();
202*5c350fdfSMatt Spinler     _source.reset();
203*5c350fdfSMatt Spinler 
204*5c350fdfSMatt Spinler     if (status == ResponseStatus::success)
205*5c350fdfSMatt Spinler     {
206*5c350fdfSMatt Spinler         uint8_t completionCode = 0;
207*5c350fdfSMatt Spinler         auto response = reinterpret_cast<pldm_msg*>(responseMsg);
208*5c350fdfSMatt Spinler 
209*5c350fdfSMatt Spinler         auto decodeRC = decode_new_file_resp(response, PLDM_NEW_FILE_RESP_BYTES,
210*5c350fdfSMatt Spinler                                              &completionCode);
211*5c350fdfSMatt Spinler         if (decodeRC < 0)
212*5c350fdfSMatt Spinler         {
213*5c350fdfSMatt Spinler             log<level::ERR>("decode_new_file_resp failed",
214*5c350fdfSMatt Spinler                             entry("RC=%d", decodeRC));
215*5c350fdfSMatt Spinler             status = ResponseStatus::failure;
216*5c350fdfSMatt Spinler         }
217*5c350fdfSMatt Spinler         else
218*5c350fdfSMatt Spinler         {
219*5c350fdfSMatt Spinler             if (completionCode != PLDM_SUCCESS)
220*5c350fdfSMatt Spinler             {
221*5c350fdfSMatt Spinler                 log<level::ERR>("Bad PLDM completion code",
222*5c350fdfSMatt Spinler                                 entry("COMPLETION_CODE=%d", completionCode));
223*5c350fdfSMatt Spinler                 status = ResponseStatus::failure;
224*5c350fdfSMatt Spinler             }
225*5c350fdfSMatt Spinler         }
226*5c350fdfSMatt Spinler     }
227*5c350fdfSMatt Spinler 
228*5c350fdfSMatt Spinler     if (_responseFunc)
229*5c350fdfSMatt Spinler     {
230*5c350fdfSMatt Spinler         try
231*5c350fdfSMatt Spinler         {
232*5c350fdfSMatt Spinler             (*_responseFunc)(status);
233*5c350fdfSMatt Spinler         }
234*5c350fdfSMatt Spinler         catch (const std::exception& e)
235*5c350fdfSMatt Spinler         {
236*5c350fdfSMatt Spinler             log<level::ERR>("PLDM response callback threw an exception",
237*5c350fdfSMatt Spinler                             entry("ERROR=%s", e.what()));
238*5c350fdfSMatt Spinler         }
239*5c350fdfSMatt Spinler     }
240*5c350fdfSMatt Spinler 
241*5c350fdfSMatt Spinler     if (responseMsg)
242*5c350fdfSMatt Spinler     {
243*5c350fdfSMatt Spinler         free(responseMsg);
244*5c350fdfSMatt Spinler     }
245*5c350fdfSMatt Spinler }
246*5c350fdfSMatt Spinler 
247*5c350fdfSMatt Spinler void PLDMInterface::receiveTimerExpired()
248*5c350fdfSMatt Spinler {
249*5c350fdfSMatt Spinler     log<level::ERR>("Timed out waiting for PLDM response");
250*5c350fdfSMatt Spinler     cancelCmd();
251*5c350fdfSMatt Spinler 
252*5c350fdfSMatt Spinler     if (_responseFunc)
253*5c350fdfSMatt Spinler     {
254*5c350fdfSMatt Spinler         try
255*5c350fdfSMatt Spinler         {
256*5c350fdfSMatt Spinler             (*_responseFunc)(ResponseStatus::failure);
257*5c350fdfSMatt Spinler         }
258*5c350fdfSMatt Spinler         catch (const std::exception& e)
259*5c350fdfSMatt Spinler         {
260*5c350fdfSMatt Spinler             log<level::ERR>("PLDM response callback threw an exception",
261*5c350fdfSMatt Spinler                             entry("ERROR=%s", e.what()));
262*5c350fdfSMatt Spinler         }
263*5c350fdfSMatt Spinler     }
264*5c350fdfSMatt Spinler }
265*5c350fdfSMatt Spinler 
266*5c350fdfSMatt Spinler void PLDMInterface::cancelCmd()
267*5c350fdfSMatt Spinler {
268*5c350fdfSMatt Spinler     _inProgress = false;
269*5c350fdfSMatt Spinler     _source.reset();
270*5c350fdfSMatt Spinler 
271*5c350fdfSMatt Spinler     if (_receiveTimer.isEnabled())
272*5c350fdfSMatt Spinler     {
273*5c350fdfSMatt Spinler         _receiveTimer.setEnabled(false);
274*5c350fdfSMatt Spinler     }
275*5c350fdfSMatt Spinler 
276*5c350fdfSMatt Spinler     closeFD();
277*5c350fdfSMatt Spinler }
278*5c350fdfSMatt Spinler 
279*5c350fdfSMatt Spinler } // namespace openpower::pels
280