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