blob: 97fff16cfabde779ae2f61fc05c428077312c3c6 [file] [log] [blame]
Alexander Hansen40fb5492025-10-28 17:56:12 +01001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright 2019 IBM Corporation
3
Matt Spinler5c350fd2019-12-12 13:53:53 -06004#include "pldm_interface.hpp"
5
6#include <libpldm/base.h>
Andrew Jeffery7cc55b52024-06-19 20:45:13 +09307#include <libpldm/oem/ibm/file_io.h>
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -05008#include <libpldm/transport.h>
9#include <libpldm/transport/mctp-demux.h>
10#include <poll.h>
Matt Spinler5c350fd2019-12-12 13:53:53 -060011#include <unistd.h>
12
Matt Spinler1b418862023-06-29 12:37:41 -050013#include <phosphor-logging/lg2.hpp>
Matt Spinler5c350fd2019-12-12 13:53:53 -060014
Patrick Williams2544b412022-10-04 08:41:06 -050015#include <fstream>
16
Matt Spinler5c350fd2019-12-12 13:53:53 -060017namespace openpower::pels
18{
19
Matt Spinler5c350fd2019-12-12 13:53:53 -060020using namespace sdeventplus;
21using namespace sdeventplus::source;
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -050022using TerminusID = uint8_t;
Matt Spinler5c350fd2019-12-12 13:53:53 -060023
24constexpr auto eidPath = "/usr/share/pldm/host_eid";
25constexpr mctp_eid_t defaultEIDValue = 9;
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -050026constexpr TerminusID tid = defaultEIDValue;
Matt Spinler5c350fd2019-12-12 13:53:53 -060027
28constexpr uint16_t pelFileType = 0;
29
30PLDMInterface::~PLDMInterface()
31{
Patrick Williams49bcbe92024-06-26 23:06:54 -050032 freeIID();
33 pldm_instance_db_destroy(_pldm_idb);
Matt Spinler5c350fd2019-12-12 13:53:53 -060034 closeFD();
35}
36
37void PLDMInterface::closeFD()
38{
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -050039 pldm_transport_mctp_demux_destroy(mctpDemux);
40 mctpDemux = nullptr;
41 _fd = -1;
42 pldmTransport = nullptr;
Matt Spinler5c350fd2019-12-12 13:53:53 -060043}
44
45void PLDMInterface::readEID()
46{
47 _eid = defaultEIDValue;
48
49 std::ifstream eidFile{eidPath};
50 if (!eidFile.good())
51 {
Matt Spinler1b418862023-06-29 12:37:41 -050052 lg2::error("Could not open host EID file");
Matt Spinler5c350fd2019-12-12 13:53:53 -060053 }
54 else
55 {
56 std::string eid;
57 eidFile >> eid;
58 if (!eid.empty())
59 {
60 _eid = atoi(eid.c_str());
61 }
62 else
63 {
Matt Spinler1b418862023-06-29 12:37:41 -050064 lg2::error("EID file was empty");
Matt Spinler5c350fd2019-12-12 13:53:53 -060065 }
66 }
67}
68
69void PLDMInterface::open()
70{
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -050071 if (pldmTransport)
72 {
73 lg2::error("open: pldmTransport already setup!");
74 throw std::runtime_error{"open failed"};
75 }
76
77 _fd = openMctpDemuxTransport();
Matt Spinler5c350fd2019-12-12 13:53:53 -060078 if (_fd < 0)
79 {
80 auto e = errno;
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -050081 lg2::error("Transport open failed. errno = {ERRNO}, rc = {RC}", "ERRNO",
82 e, "RC", _fd);
83 throw std::runtime_error{"Transport open failed"};
Matt Spinler5c350fd2019-12-12 13:53:53 -060084 }
85}
86
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -050087int PLDMInterface::openMctpDemuxTransport()
88{
89 int rc = pldm_transport_mctp_demux_init(&mctpDemux);
90 if (rc)
91 {
92 lg2::error(
93 "openMctpDemuxTransport: Failed to init MCTP demux transport. rc = {RC}",
94 "RC", rc);
95 return rc;
96 }
97
98 rc = pldm_transport_mctp_demux_map_tid(mctpDemux, tid, tid);
99 if (rc)
100 {
101 lg2::error(
102 "openMctpDemuxTransport: Failed to setup tid to eid mapping. rc = {RC}",
103 "RC", rc);
104 cleanupCmd();
105 return rc;
106 }
107 pldmTransport = pldm_transport_mctp_demux_core(mctpDemux);
108
109 struct pollfd pollfd;
110 rc = pldm_transport_mctp_demux_init_pollfd(pldmTransport, &pollfd);
111 if (rc)
112 {
113 lg2::error("openMctpDemuxTransport: Failed to get pollfd. rc = {RC}",
114 "RC", rc);
115 cleanupCmd();
116 return rc;
117 }
118 return pollfd.fd;
119}
120
Matt Spinler2843ba22020-03-03 16:36:32 -0600121void PLDMInterface::startCommand()
Matt Spinler5c350fd2019-12-12 13:53:53 -0600122{
123 try
124 {
125 closeFD();
126
127 open();
128
Matt Spinler5c350fd2019-12-12 13:53:53 -0600129 registerReceiveCallback();
130
Matt Spinler2843ba22020-03-03 16:36:32 -0600131 doSend();
132
133 _receiveTimer.restartOnce(_receiveTimeout);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600134 }
135 catch (const std::exception& e)
136 {
Matt Spinler527ff342023-06-29 12:52:34 -0500137 lg2::error("startCommand exception: {ERROR}", "ERROR", e);
138
Matt Spinler2843ba22020-03-03 16:36:32 -0600139 cleanupCmd();
Matt Spinler5c350fd2019-12-12 13:53:53 -0600140
Matt Spinler527ff342023-06-29 12:52:34 -0500141 throw;
Matt Spinler2843ba22020-03-03 16:36:32 -0600142 }
143}
144
Patrick Williams49bcbe92024-06-26 23:06:54 -0500145void PLDMInterface::allocIID()
Matt Spinler2843ba22020-03-03 16:36:32 -0600146{
Patrick Williams49bcbe92024-06-26 23:06:54 -0500147 if (_instanceID)
Matt Spinler2843ba22020-03-03 16:36:32 -0600148 {
Patrick Williams49bcbe92024-06-26 23:06:54 -0500149 return;
Matt Spinler2843ba22020-03-03 16:36:32 -0600150 }
Patrick Williams49bcbe92024-06-26 23:06:54 -0500151
152 pldm_instance_id_t iid = 0;
153 auto rc = pldm_instance_id_alloc(_pldm_idb, _eid, &iid);
154
155 if (rc == -EAGAIN)
156 {
157 throw std::runtime_error("No free instance ids");
158 }
159 else if (rc)
160 {
161 throw std::system_category().default_error_condition(rc);
162 }
163
164 _instanceID = iid;
165}
166
167void PLDMInterface::freeIID()
168{
169 if (!_instanceID)
170 {
171 return;
172 }
173
174 auto rc = pldm_instance_id_free(_pldm_idb, _eid, *_instanceID);
175
176 if (rc == -EINVAL)
177 {
Patrick Williams075c7922024-08-16 15:19:49 -0400178 throw std::runtime_error(
179 "Instance ID " + std::to_string(*_instanceID) + " for TID " +
180 std::to_string(_eid) + " was not previously allocated");
Patrick Williams49bcbe92024-06-26 23:06:54 -0500181 }
182 else if (rc)
183 {
184 throw std::system_category().default_error_condition(rc);
185 }
186
187 _instanceID = std::nullopt;
Matt Spinler2843ba22020-03-03 16:36:32 -0600188}
189
190CmdStatus PLDMInterface::sendNewLogCmd(uint32_t id, uint32_t size)
191{
192 _pelID = id;
193 _pelSize = size;
194 _inProgress = true;
195
196 try
197 {
Patrick Williams49bcbe92024-06-26 23:06:54 -0500198 // Allocate the instance ID, as needed.
Matt Spinler2843ba22020-03-03 16:36:32 -0600199 if (!_instanceID)
200 {
Patrick Williams49bcbe92024-06-26 23:06:54 -0500201 allocIID();
Matt Spinler2843ba22020-03-03 16:36:32 -0600202 }
Patrick Williams49bcbe92024-06-26 23:06:54 -0500203 startCommand();
Matt Spinler2843ba22020-03-03 16:36:32 -0600204 }
205 catch (const std::exception& e)
206 {
Matt Spinler5c350fd2019-12-12 13:53:53 -0600207 _inProgress = false;
Matt Spinler5c350fd2019-12-12 13:53:53 -0600208 return CmdStatus::failure;
209 }
210
Matt Spinler5c350fd2019-12-12 13:53:53 -0600211 return CmdStatus::success;
212}
213
214void PLDMInterface::registerReceiveCallback()
215{
216 _source = std::make_unique<IO>(
217 _event, _fd, EPOLLIN,
218 std::bind(std::mem_fn(&PLDMInterface::receive), this,
219 std::placeholders::_1, std::placeholders::_2,
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500220 std::placeholders::_3, pldmTransport));
Matt Spinler5c350fd2019-12-12 13:53:53 -0600221}
222
Matt Spinler2843ba22020-03-03 16:36:32 -0600223void PLDMInterface::doSend()
Matt Spinler5c350fd2019-12-12 13:53:53 -0600224{
225 std::array<uint8_t, sizeof(pldm_msg_hdr) + sizeof(pelFileType) +
Matt Spinler2843ba22020-03-03 16:36:32 -0600226 sizeof(_pelID) + sizeof(uint64_t)>
Matt Spinler5c350fd2019-12-12 13:53:53 -0600227 requestMsg;
228
229 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
230
Matt Spinler2843ba22020-03-03 16:36:32 -0600231 auto rc = encode_new_file_req(*_instanceID, pelFileType, _pelID, _pelSize,
232 request);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600233 if (rc != PLDM_SUCCESS)
234 {
Matt Spinler1b418862023-06-29 12:37:41 -0500235 lg2::error("encode_new_file_req failed, rc = {RC}", "RC", rc);
Matt Spinler527ff342023-06-29 12:52:34 -0500236 throw std::runtime_error{"encode_new_file_req failed"};
Matt Spinler5c350fd2019-12-12 13:53:53 -0600237 }
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500238 pldm_tid_t pldmTID = static_cast<pldm_tid_t>(_eid);
239 rc = pldm_transport_send_msg(pldmTransport, pldmTID, requestMsg.data(),
240 requestMsg.size());
Matt Spinler5c350fd2019-12-12 13:53:53 -0600241 if (rc < 0)
242 {
243 auto e = errno;
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500244 lg2::error("pldm_transport_send_msg failed, rc = {RC}, errno = {ERRNO}",
245 "RC", rc, "ERRNO", e);
246 throw std::runtime_error{"pldm_transport_send_msg failed"};
Matt Spinler5c350fd2019-12-12 13:53:53 -0600247 }
Matt Spinler9fdcad52025-02-07 10:12:43 -0600248
249 memcpy(&_requestHeader, request, sizeof(pldm_msg_hdr));
Matt Spinler5c350fd2019-12-12 13:53:53 -0600250}
251
Matt Spinlerf045e832025-03-13 11:07:10 -0500252struct Response
253{
254 Response(void* r) : response(r) {}
255 ~Response()
256 {
257 if (response != nullptr)
258 {
259 free(response);
260 }
261 }
262 void* response = nullptr;
263};
264
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500265void PLDMInterface::receive(IO& /*io*/, int /*fd*/, uint32_t revents,
266 pldm_transport* transport)
267
Matt Spinler5c350fd2019-12-12 13:53:53 -0600268{
269 if (!(revents & EPOLLIN))
270 {
271 return;
272 }
273
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500274 void* responseMsg = nullptr;
Matt Spinler5c350fd2019-12-12 13:53:53 -0600275 size_t responseSize = 0;
276 ResponseStatus status = ResponseStatus::success;
277
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500278 pldm_tid_t pldmTID;
279 auto rc = pldm_transport_recv_msg(transport, &pldmTID, &responseMsg,
280 &responseSize);
Eddie Jamesec6f1412024-10-17 10:55:55 -0500281 struct pldm_msg_hdr* hdr = (struct pldm_msg_hdr*)responseMsg;
Matt Spinlerf045e832025-03-13 11:07:10 -0500282 Response r{responseMsg};
283
284 if (rc == PLDM_REQUESTER_SUCCESS)
Matt Spinler5c350fd2019-12-12 13:53:53 -0600285 {
Matt Spinlerf045e832025-03-13 11:07:10 -0500286 if ((pldmTID != _eid) ||
287 !pldm_msg_hdr_correlate_response(&_requestHeader, hdr))
288 {
289 // We got a response to someone else's message. Ignore it.
290 return;
291 }
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500292 }
Matt Spinlerf045e832025-03-13 11:07:10 -0500293 else
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500294 {
295 if (rc == PLDM_REQUESTER_NOT_RESP_MSG)
Matt Spinler5c350fd2019-12-12 13:53:53 -0600296 {
297 // Due to the MCTP loopback, we may get notified of the message
298 // we just sent.
299 return;
300 }
301
302 auto e = errno;
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500303 lg2::error("pldm_transport_recv_msg failed, rc = {RC}, errno = {ERRNO}",
304 "RC",
Matt Spinler1b418862023-06-29 12:37:41 -0500305 static_cast<std::underlying_type_t<pldm_requester_rc_t>>(rc),
306 "ERRNO", e);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600307 status = ResponseStatus::failure;
Matt Spinler5c350fd2019-12-12 13:53:53 -0600308 }
Eddie Jamesec6f1412024-10-17 10:55:55 -0500309 if (hdr && (hdr->request || hdr->datagram))
310 {
Eddie Jamesec6f1412024-10-17 10:55:55 -0500311 return;
312 }
Matt Spinler5c350fd2019-12-12 13:53:53 -0600313
Matt Spinler2843ba22020-03-03 16:36:32 -0600314 cleanupCmd();
315
316 // Can't use this instance ID anymore.
Patrick Williams49bcbe92024-06-26 23:06:54 -0500317 freeIID();
Matt Spinler5c350fd2019-12-12 13:53:53 -0600318
319 if (status == ResponseStatus::success)
320 {
321 uint8_t completionCode = 0;
322 auto response = reinterpret_cast<pldm_msg*>(responseMsg);
323
Patrick Williams075c7922024-08-16 15:19:49 -0400324 auto decodeRC =
325 decode_new_file_resp(response, responseSize, &completionCode);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600326 if (decodeRC < 0)
327 {
Matt Spinler1b418862023-06-29 12:37:41 -0500328 lg2::error("decode_new_file_resp failed, rc = {RC}", "RC",
329 decodeRC);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600330 status = ResponseStatus::failure;
331 }
332 else
333 {
334 if (completionCode != PLDM_SUCCESS)
335 {
Matt Spinler1b418862023-06-29 12:37:41 -0500336 lg2::error("Bad PLDM completion code {CODE}", "CODE",
337 completionCode);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600338 status = ResponseStatus::failure;
339 }
340 }
341 }
342
Matt Spinlera44efe42020-03-03 10:30:16 -0600343 callResponseFunc(status);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600344}
345
346void PLDMInterface::receiveTimerExpired()
347{
Matt Spinler1b418862023-06-29 12:37:41 -0500348 lg2::error("Timed out waiting for PLDM response");
Matt Spinler2843ba22020-03-03 16:36:32 -0600349
350 // Cleanup, but keep the instance ID because the host didn't
351 // respond so we can still use it.
352 cleanupCmd();
Matt Spinler5c350fd2019-12-12 13:53:53 -0600353
Matt Spinlera44efe42020-03-03 10:30:16 -0600354 callResponseFunc(ResponseStatus::failure);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600355}
356
357void PLDMInterface::cancelCmd()
358{
Patrick Williams49bcbe92024-06-26 23:06:54 -0500359 freeIID();
Matt Spinler2843ba22020-03-03 16:36:32 -0600360 cleanupCmd();
361}
362
363void PLDMInterface::cleanupCmd()
364{
Matt Spinler5c350fd2019-12-12 13:53:53 -0600365 _inProgress = false;
366 _source.reset();
367
368 if (_receiveTimer.isEnabled())
369 {
370 _receiveTimer.setEnabled(false);
371 }
372
373 closeFD();
374}
375
376} // namespace openpower::pels