blob: 502cc7d69d058c5dab0571ef64d4cffaa1393f43 [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 Spinler1b418862023-06-29 12:37:41 -050023#include <phosphor-logging/lg2.hpp>
Matt Spinler5c350fd2019-12-12 13:53:53 -060024
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 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 {
Matt Spinler1b418862023-06-29 12:37:41 -050075 lg2::error("Could not open host EID file");
Matt Spinler5c350fd2019-12-12 13:53:53 -060076 }
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 {
Matt Spinler1b418862023-06-29 12:37:41 -050087 lg2::error("EID file was empty");
Matt Spinler5c350fd2019-12-12 13:53:53 -060088 }
89 }
90}
91
92void PLDMInterface::open()
93{
94 _fd = pldm_open();
95 if (_fd < 0)
96 {
97 auto e = errno;
Matt Spinler1b418862023-06-29 12:37:41 -050098 lg2::error("pldm_open failed. errno = {ERRNO}, rc = {RC}", "ERRNO", e,
99 "RC", _fd);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600100 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 {
Matt Spinler1b418862023-06-29 12:37:41 -0500108 lg2::info("A command was canceled while waiting for the instance ID");
Matt Spinler2843ba22020-03-03 16:36:32 -0600109 return;
110 }
111
112 bool failed = false;
113
114 auto rc = sd_bus_message_get_errno(msg);
115 if (rc)
116 {
Matt Spinler1b418862023-06-29 12:37:41 -0500117 lg2::error("GetInstanceId D-Bus method failed, rc = {RC}", "RC", rc);
Matt Spinler2843ba22020-03-03 16:36:32 -0600118 failed = true;
119 }
120 else
121 {
122 uint8_t id;
123 rc = sd_bus_message_read_basic(msg, 'y', &id);
124 if (rc < 0)
125 {
Matt Spinler1b418862023-06-29 12:37:41 -0500126 lg2::error("Could not read instance ID out of message, rc = {RC}",
127 "RC", rc);
Matt Spinler2843ba22020-03-03 16:36:32 -0600128 failed = true;
129 }
130 else
131 {
132 _instanceID = id;
133 }
134 }
135
136 if (failed)
137 {
138 _inProgress = false;
139 callResponseFunc(ResponseStatus::failure);
140 }
141 else
142 {
143 startCommand();
144 }
145}
146
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500147int iidCallback(sd_bus_message* msg, void* data, sd_bus_error* /*err*/)
Matt Spinler2843ba22020-03-03 16:36:32 -0600148{
149 auto* interface = static_cast<PLDMInterface*>(data);
150 interface->instanceIDCallback(msg);
151 return 0;
152}
153
154void PLDMInterface::startCommand()
Matt Spinler5c350fd2019-12-12 13:53:53 -0600155{
156 try
157 {
158 closeFD();
159
160 open();
161
Matt Spinler5c350fd2019-12-12 13:53:53 -0600162 registerReceiveCallback();
163
Matt Spinler2843ba22020-03-03 16:36:32 -0600164 doSend();
165
166 _receiveTimer.restartOnce(_receiveTimeout);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600167 }
168 catch (const std::exception& e)
169 {
Matt Spinler2843ba22020-03-03 16:36:32 -0600170 cleanupCmd();
Matt Spinler5c350fd2019-12-12 13:53:53 -0600171
Matt Spinler2843ba22020-03-03 16:36:32 -0600172 callResponseFunc(ResponseStatus::failure);
173 }
174}
175
176void PLDMInterface::startReadInstanceID()
177{
178 auto rc = sd_bus_call_method_async(
179 _bus, NULL, service::pldm, object_path::pldm, interface::pldm_requester,
180 "GetInstanceId", iidCallback, this, "y", _eid);
181
182 if (rc < 0)
183 {
Matt Spinler1b418862023-06-29 12:37:41 -0500184 lg2::error(
185 "Error calling sd_bus_call_method_async, rc = {RC}, msg = {MSG}",
186 "RC", rc, "MSG", strerror(-rc));
Matt Spinler2843ba22020-03-03 16:36:32 -0600187 }
188}
189
190CmdStatus PLDMInterface::sendNewLogCmd(uint32_t id, uint32_t size)
191{
192 _pelID = id;
193 _pelSize = size;
194 _inProgress = true;
195
196 try
197 {
198 // Kick off the async call to get the instance ID if
199 // necessary, otherwise start the command itself.
200 if (!_instanceID)
201 {
202 startReadInstanceID();
203 }
204 else
205 {
206 startCommand();
207 }
208 }
209 catch (const std::exception& e)
210 {
Matt Spinler5c350fd2019-12-12 13:53:53 -0600211 _inProgress = false;
Matt Spinler5c350fd2019-12-12 13:53:53 -0600212 return CmdStatus::failure;
213 }
214
Matt Spinler5c350fd2019-12-12 13:53:53 -0600215 return CmdStatus::success;
216}
217
218void PLDMInterface::registerReceiveCallback()
219{
220 _source = std::make_unique<IO>(
221 _event, _fd, EPOLLIN,
222 std::bind(std::mem_fn(&PLDMInterface::receive), this,
223 std::placeholders::_1, std::placeholders::_2,
224 std::placeholders::_3));
225}
226
Matt Spinler2843ba22020-03-03 16:36:32 -0600227void PLDMInterface::doSend()
Matt Spinler5c350fd2019-12-12 13:53:53 -0600228{
229 std::array<uint8_t, sizeof(pldm_msg_hdr) + sizeof(pelFileType) +
Matt Spinler2843ba22020-03-03 16:36:32 -0600230 sizeof(_pelID) + sizeof(uint64_t)>
Matt Spinler5c350fd2019-12-12 13:53:53 -0600231 requestMsg;
232
233 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
234
Matt Spinler2843ba22020-03-03 16:36:32 -0600235 auto rc = encode_new_file_req(*_instanceID, pelFileType, _pelID, _pelSize,
236 request);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600237 if (rc != PLDM_SUCCESS)
238 {
Matt Spinler1b418862023-06-29 12:37:41 -0500239 lg2::error("encode_new_file_req failed, rc = {RC}", "RC", rc);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600240 throw std::exception{};
241 }
242
243 rc = pldm_send(_eid, _fd, requestMsg.data(), requestMsg.size());
244 if (rc < 0)
245 {
246 auto e = errno;
Matt Spinler1b418862023-06-29 12:37:41 -0500247 lg2::error("pldm_send failed, rc = {RC}, errno = {ERRNO}", "RC", rc,
248 "ERRNO", e);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600249
250 throw std::exception{};
251 }
252}
253
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500254void PLDMInterface::receive(IO& /*io*/, int fd, uint32_t revents)
Matt Spinler5c350fd2019-12-12 13:53:53 -0600255{
256 if (!(revents & EPOLLIN))
257 {
258 return;
259 }
260
261 uint8_t* responseMsg = nullptr;
262 size_t responseSize = 0;
263 ResponseStatus status = ResponseStatus::success;
264
Matt Spinler2843ba22020-03-03 16:36:32 -0600265 auto rc = pldm_recv(_eid, fd, *_instanceID, &responseMsg, &responseSize);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600266 if (rc < 0)
267 {
268 if (rc == PLDM_REQUESTER_INSTANCE_ID_MISMATCH)
269 {
270 // We got a response to someone else's message. Ignore it.
271 return;
272 }
273 else if (rc == PLDM_REQUESTER_NOT_RESP_MSG)
274 {
275 // Due to the MCTP loopback, we may get notified of the message
276 // we just sent.
277 return;
278 }
279
280 auto e = errno;
Matt Spinler1b418862023-06-29 12:37:41 -0500281 lg2::error("pldm_recv failed, rc = {RC}, errno = {ERRNO}", "RC",
282 static_cast<std::underlying_type_t<pldm_requester_rc_t>>(rc),
283 "ERRNO", e);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600284 status = ResponseStatus::failure;
285
286 responseMsg = nullptr;
287 }
288
Matt Spinler2843ba22020-03-03 16:36:32 -0600289 cleanupCmd();
290
291 // Can't use this instance ID anymore.
292 _instanceID = std::nullopt;
Matt Spinler5c350fd2019-12-12 13:53:53 -0600293
294 if (status == ResponseStatus::success)
295 {
296 uint8_t completionCode = 0;
297 auto response = reinterpret_cast<pldm_msg*>(responseMsg);
298
299 auto decodeRC = decode_new_file_resp(response, PLDM_NEW_FILE_RESP_BYTES,
300 &completionCode);
301 if (decodeRC < 0)
302 {
Matt Spinler1b418862023-06-29 12:37:41 -0500303 lg2::error("decode_new_file_resp failed, rc = {RC}", "RC",
304 decodeRC);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600305 status = ResponseStatus::failure;
306 }
307 else
308 {
309 if (completionCode != PLDM_SUCCESS)
310 {
Matt Spinler1b418862023-06-29 12:37:41 -0500311 lg2::error("Bad PLDM completion code {CODE}", "CODE",
312 completionCode);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600313 status = ResponseStatus::failure;
314 }
315 }
316 }
317
Matt Spinlera44efe42020-03-03 10:30:16 -0600318 callResponseFunc(status);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600319
320 if (responseMsg)
321 {
322 free(responseMsg);
323 }
324}
325
326void PLDMInterface::receiveTimerExpired()
327{
Matt Spinler1b418862023-06-29 12:37:41 -0500328 lg2::error("Timed out waiting for PLDM response");
Matt Spinler2843ba22020-03-03 16:36:32 -0600329
330 // Cleanup, but keep the instance ID because the host didn't
331 // respond so we can still use it.
332 cleanupCmd();
Matt Spinler5c350fd2019-12-12 13:53:53 -0600333
Matt Spinlera44efe42020-03-03 10:30:16 -0600334 callResponseFunc(ResponseStatus::failure);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600335}
336
337void PLDMInterface::cancelCmd()
338{
Matt Spinler2843ba22020-03-03 16:36:32 -0600339 _instanceID = std::nullopt;
340 cleanupCmd();
341}
342
343void PLDMInterface::cleanupCmd()
344{
Matt Spinler5c350fd2019-12-12 13:53:53 -0600345 _inProgress = false;
346 _source.reset();
347
348 if (_receiveTimer.isEnabled())
349 {
350 _receiveTimer.setEnabled(false);
351 }
352
353 closeFD();
354}
355
356} // namespace openpower::pels