blob: c891a4a6ebd43d3530ea74f4aec70387ce79630e [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>
Matt Spinler2843ba22020-03-03 16:36:32 -060020#include <systemd/sd-bus.h>
Matt Spinler5c350fd2019-12-12 13:53:53 -060021#include <unistd.h>
22
23#include <fstream>
24#include <phosphor-logging/log.hpp>
25
26namespace openpower::pels
27{
28
Matt Spinler2843ba22020-03-03 16:36:32 -060029namespace service
30{
31constexpr auto pldm = "xyz.openbmc_project.PLDM";
32}
33
34namespace object_path
35{
36constexpr auto pldm = "/xyz/openbmc_project/pldm";
37}
38
39namespace interface
40{
41constexpr auto pldm_requester = "xyz.openbmc_project.PLDM.Requester";
42}
43
Matt Spinler5c350fd2019-12-12 13:53:53 -060044using namespace phosphor::logging;
45using namespace sdeventplus;
46using namespace sdeventplus::source;
47
48constexpr auto eidPath = "/usr/share/pldm/host_eid";
49constexpr mctp_eid_t defaultEIDValue = 9;
50
51constexpr uint16_t pelFileType = 0;
52
53PLDMInterface::~PLDMInterface()
54{
Matt Spinler2843ba22020-03-03 16:36:32 -060055 sd_bus_unref(_bus);
Matt Spinler5c350fd2019-12-12 13:53:53 -060056 closeFD();
57}
58
59void PLDMInterface::closeFD()
60{
61 if (_fd >= 0)
62 {
63 close(_fd);
64 _fd = -1;
65 }
66}
67
68void PLDMInterface::readEID()
69{
70 _eid = defaultEIDValue;
71
72 std::ifstream eidFile{eidPath};
73 if (!eidFile.good())
74 {
75 log<level::ERR>("Could not open host EID file");
76 }
77 else
78 {
79 std::string eid;
80 eidFile >> eid;
81 if (!eid.empty())
82 {
83 _eid = atoi(eid.c_str());
84 }
85 else
86 {
87 log<level::ERR>("EID file was empty");
88 }
89 }
90}
91
92void PLDMInterface::open()
93{
94 _fd = pldm_open();
95 if (_fd < 0)
96 {
97 auto e = errno;
98 log<level::ERR>("pldm_open failed", entry("ERRNO=%d", e),
99 entry("RC=%d\n", _fd));
100 throw std::exception{};
101 }
102}
103
Matt Spinler2843ba22020-03-03 16:36:32 -0600104void PLDMInterface::instanceIDCallback(sd_bus_message* msg)
105{
106 if (!_inProgress)
107 {
108 // A cancelCmd was run, just return
109 log<level::INFO>(
110 "A command was canceled while waiting for the instance ID");
111 return;
112 }
113
114 bool failed = false;
115
116 auto rc = sd_bus_message_get_errno(msg);
117 if (rc)
118 {
119 log<level::ERR>("GetInstanceId D-Bus method failed",
120 entry("ERRNO=%d", rc));
121 failed = true;
122 }
123 else
124 {
125 uint8_t id;
126 rc = sd_bus_message_read_basic(msg, 'y', &id);
127 if (rc < 0)
128 {
129 log<level::ERR>("Could not read instance ID out of message",
130 entry("ERROR=%d", rc));
131 failed = true;
132 }
133 else
134 {
135 _instanceID = id;
136 }
137 }
138
139 if (failed)
140 {
141 _inProgress = false;
142 callResponseFunc(ResponseStatus::failure);
143 }
144 else
145 {
146 startCommand();
147 }
148}
149
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500150int iidCallback(sd_bus_message* msg, void* data, sd_bus_error* /*err*/)
Matt Spinler2843ba22020-03-03 16:36:32 -0600151{
152 auto* interface = static_cast<PLDMInterface*>(data);
153 interface->instanceIDCallback(msg);
154 return 0;
155}
156
157void PLDMInterface::startCommand()
Matt Spinler5c350fd2019-12-12 13:53:53 -0600158{
159 try
160 {
161 closeFD();
162
163 open();
164
Matt Spinler5c350fd2019-12-12 13:53:53 -0600165 registerReceiveCallback();
166
Matt Spinler2843ba22020-03-03 16:36:32 -0600167 doSend();
168
169 _receiveTimer.restartOnce(_receiveTimeout);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600170 }
171 catch (const std::exception& e)
172 {
Matt Spinler2843ba22020-03-03 16:36:32 -0600173 cleanupCmd();
Matt Spinler5c350fd2019-12-12 13:53:53 -0600174
Matt Spinler2843ba22020-03-03 16:36:32 -0600175 callResponseFunc(ResponseStatus::failure);
176 }
177}
178
179void PLDMInterface::startReadInstanceID()
180{
181 auto rc = sd_bus_call_method_async(
182 _bus, NULL, service::pldm, object_path::pldm, interface::pldm_requester,
183 "GetInstanceId", iidCallback, this, "y", _eid);
184
185 if (rc < 0)
186 {
187 log<level::ERR>("Error calling sd_bus_call_method_async",
188 entry("RC=%d", rc), entry("MSG=%s", strerror(-rc)));
189 throw std::exception{};
190 }
191}
192
193CmdStatus PLDMInterface::sendNewLogCmd(uint32_t id, uint32_t size)
194{
195 _pelID = id;
196 _pelSize = size;
197 _inProgress = true;
198
199 try
200 {
201 // Kick off the async call to get the instance ID if
202 // necessary, otherwise start the command itself.
203 if (!_instanceID)
204 {
205 startReadInstanceID();
206 }
207 else
208 {
209 startCommand();
210 }
211 }
212 catch (const std::exception& e)
213 {
Matt Spinler5c350fd2019-12-12 13:53:53 -0600214 _inProgress = false;
Matt Spinler5c350fd2019-12-12 13:53:53 -0600215 return CmdStatus::failure;
216 }
217
Matt Spinler5c350fd2019-12-12 13:53:53 -0600218 return CmdStatus::success;
219}
220
221void PLDMInterface::registerReceiveCallback()
222{
223 _source = std::make_unique<IO>(
224 _event, _fd, EPOLLIN,
225 std::bind(std::mem_fn(&PLDMInterface::receive), this,
226 std::placeholders::_1, std::placeholders::_2,
227 std::placeholders::_3));
228}
229
Matt Spinler2843ba22020-03-03 16:36:32 -0600230void PLDMInterface::doSend()
Matt Spinler5c350fd2019-12-12 13:53:53 -0600231{
232 std::array<uint8_t, sizeof(pldm_msg_hdr) + sizeof(pelFileType) +
Matt Spinler2843ba22020-03-03 16:36:32 -0600233 sizeof(_pelID) + sizeof(uint64_t)>
Matt Spinler5c350fd2019-12-12 13:53:53 -0600234 requestMsg;
235
236 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
237
Matt Spinler2843ba22020-03-03 16:36:32 -0600238 auto rc = encode_new_file_req(*_instanceID, pelFileType, _pelID, _pelSize,
239 request);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600240 if (rc != PLDM_SUCCESS)
241 {
242 log<level::ERR>("encode_new_file_req failed", entry("RC=%d", rc));
243 throw std::exception{};
244 }
245
246 rc = pldm_send(_eid, _fd, requestMsg.data(), requestMsg.size());
247 if (rc < 0)
248 {
249 auto e = errno;
250 log<level::ERR>("pldm_send failed", entry("RC=%d", rc),
251 entry("ERRNO=%d", e));
252
253 throw std::exception{};
254 }
255}
256
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500257void PLDMInterface::receive(IO& /*io*/, int fd, uint32_t revents)
Matt Spinler5c350fd2019-12-12 13:53:53 -0600258{
259 if (!(revents & EPOLLIN))
260 {
261 return;
262 }
263
264 uint8_t* responseMsg = nullptr;
265 size_t responseSize = 0;
266 ResponseStatus status = ResponseStatus::success;
267
Matt Spinler2843ba22020-03-03 16:36:32 -0600268 auto rc = pldm_recv(_eid, fd, *_instanceID, &responseMsg, &responseSize);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600269 if (rc < 0)
270 {
271 if (rc == PLDM_REQUESTER_INSTANCE_ID_MISMATCH)
272 {
273 // We got a response to someone else's message. Ignore it.
274 return;
275 }
276 else if (rc == PLDM_REQUESTER_NOT_RESP_MSG)
277 {
278 // Due to the MCTP loopback, we may get notified of the message
279 // we just sent.
280 return;
281 }
282
283 auto e = errno;
284 log<level::ERR>("pldm_recv failed", entry("RC=%d", rc),
285 entry("ERRNO=%d", e));
286 status = ResponseStatus::failure;
287
288 responseMsg = nullptr;
289 }
290
Matt Spinler2843ba22020-03-03 16:36:32 -0600291 cleanupCmd();
292
293 // Can't use this instance ID anymore.
294 _instanceID = std::nullopt;
Matt Spinler5c350fd2019-12-12 13:53:53 -0600295
296 if (status == ResponseStatus::success)
297 {
298 uint8_t completionCode = 0;
299 auto response = reinterpret_cast<pldm_msg*>(responseMsg);
300
301 auto decodeRC = decode_new_file_resp(response, PLDM_NEW_FILE_RESP_BYTES,
302 &completionCode);
303 if (decodeRC < 0)
304 {
305 log<level::ERR>("decode_new_file_resp failed",
306 entry("RC=%d", decodeRC));
307 status = ResponseStatus::failure;
308 }
309 else
310 {
311 if (completionCode != PLDM_SUCCESS)
312 {
313 log<level::ERR>("Bad PLDM completion code",
314 entry("COMPLETION_CODE=%d", completionCode));
315 status = ResponseStatus::failure;
316 }
317 }
318 }
319
Matt Spinlera44efe42020-03-03 10:30:16 -0600320 callResponseFunc(status);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600321
322 if (responseMsg)
323 {
324 free(responseMsg);
325 }
326}
327
328void PLDMInterface::receiveTimerExpired()
329{
330 log<level::ERR>("Timed out waiting for PLDM response");
Matt Spinler2843ba22020-03-03 16:36:32 -0600331
332 // Cleanup, but keep the instance ID because the host didn't
333 // respond so we can still use it.
334 cleanupCmd();
Matt Spinler5c350fd2019-12-12 13:53:53 -0600335
Matt Spinlera44efe42020-03-03 10:30:16 -0600336 callResponseFunc(ResponseStatus::failure);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600337}
338
339void PLDMInterface::cancelCmd()
340{
Matt Spinler2843ba22020-03-03 16:36:32 -0600341 _instanceID = std::nullopt;
342 cleanupCmd();
343}
344
345void PLDMInterface::cleanupCmd()
346{
Matt Spinler5c350fd2019-12-12 13:53:53 -0600347 _inProgress = false;
348 _source.reset();
349
350 if (_receiveTimer.isEnabled())
351 {
352 _receiveTimer.setEnabled(false);
353 }
354
355 closeFD();
356}
357
358} // namespace openpower::pels