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