blob: 2c11468170528be69e89e6a4c644f683140f9219 [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>
Andrew Jeffery7cc55b52024-06-19 20:45:13 +093019#include <libpldm/oem/ibm/file_io.h>
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -050020#include <libpldm/transport.h>
21#include <libpldm/transport/mctp-demux.h>
22#include <poll.h>
Matt Spinler5c350fd2019-12-12 13:53:53 -060023#include <unistd.h>
24
Matt Spinler1b418862023-06-29 12:37:41 -050025#include <phosphor-logging/lg2.hpp>
Matt Spinler5c350fd2019-12-12 13:53:53 -060026
Patrick Williams2544b412022-10-04 08:41:06 -050027#include <fstream>
28
Matt Spinler5c350fd2019-12-12 13:53:53 -060029namespace openpower::pels
30{
31
Matt Spinler5c350fd2019-12-12 13:53:53 -060032using namespace sdeventplus;
33using namespace sdeventplus::source;
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -050034using TerminusID = uint8_t;
Matt Spinler5c350fd2019-12-12 13:53:53 -060035
36constexpr auto eidPath = "/usr/share/pldm/host_eid";
37constexpr mctp_eid_t defaultEIDValue = 9;
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -050038constexpr TerminusID tid = defaultEIDValue;
Matt Spinler5c350fd2019-12-12 13:53:53 -060039
40constexpr uint16_t pelFileType = 0;
41
42PLDMInterface::~PLDMInterface()
43{
Patrick Williams49bcbe92024-06-26 23:06:54 -050044 freeIID();
45 pldm_instance_db_destroy(_pldm_idb);
Matt Spinler5c350fd2019-12-12 13:53:53 -060046 closeFD();
47}
48
49void PLDMInterface::closeFD()
50{
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -050051 pldm_transport_mctp_demux_destroy(mctpDemux);
52 mctpDemux = nullptr;
53 _fd = -1;
54 pldmTransport = nullptr;
Matt Spinler5c350fd2019-12-12 13:53:53 -060055}
56
57void PLDMInterface::readEID()
58{
59 _eid = defaultEIDValue;
60
61 std::ifstream eidFile{eidPath};
62 if (!eidFile.good())
63 {
Matt Spinler1b418862023-06-29 12:37:41 -050064 lg2::error("Could not open host EID file");
Matt Spinler5c350fd2019-12-12 13:53:53 -060065 }
66 else
67 {
68 std::string eid;
69 eidFile >> eid;
70 if (!eid.empty())
71 {
72 _eid = atoi(eid.c_str());
73 }
74 else
75 {
Matt Spinler1b418862023-06-29 12:37:41 -050076 lg2::error("EID file was empty");
Matt Spinler5c350fd2019-12-12 13:53:53 -060077 }
78 }
79}
80
81void PLDMInterface::open()
82{
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -050083 if (pldmTransport)
84 {
85 lg2::error("open: pldmTransport already setup!");
86 throw std::runtime_error{"open failed"};
87 }
88
89 _fd = openMctpDemuxTransport();
Matt Spinler5c350fd2019-12-12 13:53:53 -060090 if (_fd < 0)
91 {
92 auto e = errno;
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -050093 lg2::error("Transport open failed. errno = {ERRNO}, rc = {RC}", "ERRNO",
94 e, "RC", _fd);
95 throw std::runtime_error{"Transport open failed"};
Matt Spinler5c350fd2019-12-12 13:53:53 -060096 }
97}
98
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -050099int PLDMInterface::openMctpDemuxTransport()
100{
101 int rc = pldm_transport_mctp_demux_init(&mctpDemux);
102 if (rc)
103 {
104 lg2::error(
105 "openMctpDemuxTransport: Failed to init MCTP demux transport. rc = {RC}",
106 "RC", rc);
107 return rc;
108 }
109
110 rc = pldm_transport_mctp_demux_map_tid(mctpDemux, tid, tid);
111 if (rc)
112 {
113 lg2::error(
114 "openMctpDemuxTransport: Failed to setup tid to eid mapping. rc = {RC}",
115 "RC", rc);
116 cleanupCmd();
117 return rc;
118 }
119 pldmTransport = pldm_transport_mctp_demux_core(mctpDemux);
120
121 struct pollfd pollfd;
122 rc = pldm_transport_mctp_demux_init_pollfd(pldmTransport, &pollfd);
123 if (rc)
124 {
125 lg2::error("openMctpDemuxTransport: Failed to get pollfd. rc = {RC}",
126 "RC", rc);
127 cleanupCmd();
128 return rc;
129 }
130 return pollfd.fd;
131}
132
Matt Spinler2843ba22020-03-03 16:36:32 -0600133void PLDMInterface::startCommand()
Matt Spinler5c350fd2019-12-12 13:53:53 -0600134{
135 try
136 {
137 closeFD();
138
139 open();
140
Matt Spinler5c350fd2019-12-12 13:53:53 -0600141 registerReceiveCallback();
142
Matt Spinler2843ba22020-03-03 16:36:32 -0600143 doSend();
144
145 _receiveTimer.restartOnce(_receiveTimeout);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600146 }
147 catch (const std::exception& e)
148 {
Matt Spinler527ff342023-06-29 12:52:34 -0500149 lg2::error("startCommand exception: {ERROR}", "ERROR", e);
150
Matt Spinler2843ba22020-03-03 16:36:32 -0600151 cleanupCmd();
Matt Spinler5c350fd2019-12-12 13:53:53 -0600152
Matt Spinler527ff342023-06-29 12:52:34 -0500153 throw;
Matt Spinler2843ba22020-03-03 16:36:32 -0600154 }
155}
156
Patrick Williams49bcbe92024-06-26 23:06:54 -0500157void PLDMInterface::allocIID()
Matt Spinler2843ba22020-03-03 16:36:32 -0600158{
Patrick Williams49bcbe92024-06-26 23:06:54 -0500159 if (_instanceID)
Matt Spinler2843ba22020-03-03 16:36:32 -0600160 {
Patrick Williams49bcbe92024-06-26 23:06:54 -0500161 return;
Matt Spinler2843ba22020-03-03 16:36:32 -0600162 }
Patrick Williams49bcbe92024-06-26 23:06:54 -0500163
164 pldm_instance_id_t iid = 0;
165 auto rc = pldm_instance_id_alloc(_pldm_idb, _eid, &iid);
166
167 if (rc == -EAGAIN)
168 {
169 throw std::runtime_error("No free instance ids");
170 }
171 else if (rc)
172 {
173 throw std::system_category().default_error_condition(rc);
174 }
175
176 _instanceID = iid;
177}
178
179void PLDMInterface::freeIID()
180{
181 if (!_instanceID)
182 {
183 return;
184 }
185
186 auto rc = pldm_instance_id_free(_pldm_idb, _eid, *_instanceID);
187
188 if (rc == -EINVAL)
189 {
Patrick Williams075c7922024-08-16 15:19:49 -0400190 throw std::runtime_error(
191 "Instance ID " + std::to_string(*_instanceID) + " for TID " +
192 std::to_string(_eid) + " was not previously allocated");
Patrick Williams49bcbe92024-06-26 23:06:54 -0500193 }
194 else if (rc)
195 {
196 throw std::system_category().default_error_condition(rc);
197 }
198
199 _instanceID = std::nullopt;
Matt Spinler2843ba22020-03-03 16:36:32 -0600200}
201
202CmdStatus PLDMInterface::sendNewLogCmd(uint32_t id, uint32_t size)
203{
204 _pelID = id;
205 _pelSize = size;
206 _inProgress = true;
207
208 try
209 {
Patrick Williams49bcbe92024-06-26 23:06:54 -0500210 // Allocate the instance ID, as needed.
Matt Spinler2843ba22020-03-03 16:36:32 -0600211 if (!_instanceID)
212 {
Patrick Williams49bcbe92024-06-26 23:06:54 -0500213 allocIID();
Matt Spinler2843ba22020-03-03 16:36:32 -0600214 }
Patrick Williams49bcbe92024-06-26 23:06:54 -0500215 startCommand();
Matt Spinler2843ba22020-03-03 16:36:32 -0600216 }
217 catch (const std::exception& e)
218 {
Matt Spinler5c350fd2019-12-12 13:53:53 -0600219 _inProgress = false;
Matt Spinler5c350fd2019-12-12 13:53:53 -0600220 return CmdStatus::failure;
221 }
222
Matt Spinler5c350fd2019-12-12 13:53:53 -0600223 return CmdStatus::success;
224}
225
226void PLDMInterface::registerReceiveCallback()
227{
228 _source = std::make_unique<IO>(
229 _event, _fd, EPOLLIN,
230 std::bind(std::mem_fn(&PLDMInterface::receive), this,
231 std::placeholders::_1, std::placeholders::_2,
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500232 std::placeholders::_3, pldmTransport));
Matt Spinler5c350fd2019-12-12 13:53:53 -0600233}
234
Matt Spinler2843ba22020-03-03 16:36:32 -0600235void PLDMInterface::doSend()
Matt Spinler5c350fd2019-12-12 13:53:53 -0600236{
237 std::array<uint8_t, sizeof(pldm_msg_hdr) + sizeof(pelFileType) +
Matt Spinler2843ba22020-03-03 16:36:32 -0600238 sizeof(_pelID) + sizeof(uint64_t)>
Matt Spinler5c350fd2019-12-12 13:53:53 -0600239 requestMsg;
240
241 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
242
Matt Spinler2843ba22020-03-03 16:36:32 -0600243 auto rc = encode_new_file_req(*_instanceID, pelFileType, _pelID, _pelSize,
244 request);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600245 if (rc != PLDM_SUCCESS)
246 {
Matt Spinler1b418862023-06-29 12:37:41 -0500247 lg2::error("encode_new_file_req failed, rc = {RC}", "RC", rc);
Matt Spinler527ff342023-06-29 12:52:34 -0500248 throw std::runtime_error{"encode_new_file_req failed"};
Matt Spinler5c350fd2019-12-12 13:53:53 -0600249 }
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500250 pldm_tid_t pldmTID = static_cast<pldm_tid_t>(_eid);
251 rc = pldm_transport_send_msg(pldmTransport, pldmTID, requestMsg.data(),
252 requestMsg.size());
Matt Spinler5c350fd2019-12-12 13:53:53 -0600253 if (rc < 0)
254 {
255 auto e = errno;
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500256 lg2::error("pldm_transport_send_msg failed, rc = {RC}, errno = {ERRNO}",
257 "RC", rc, "ERRNO", e);
258 throw std::runtime_error{"pldm_transport_send_msg failed"};
Matt Spinler5c350fd2019-12-12 13:53:53 -0600259 }
Matt Spinler9fdcad52025-02-07 10:12:43 -0600260
261 memcpy(&_requestHeader, request, sizeof(pldm_msg_hdr));
Matt Spinler5c350fd2019-12-12 13:53:53 -0600262}
263
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500264void PLDMInterface::receive(IO& /*io*/, int /*fd*/, uint32_t revents,
265 pldm_transport* transport)
266
Matt Spinler5c350fd2019-12-12 13:53:53 -0600267{
268 if (!(revents & EPOLLIN))
269 {
270 return;
271 }
272
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500273 void* responseMsg = nullptr;
Matt Spinler5c350fd2019-12-12 13:53:53 -0600274 size_t responseSize = 0;
275 ResponseStatus status = ResponseStatus::success;
276
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500277 pldm_tid_t pldmTID;
278 auto rc = pldm_transport_recv_msg(transport, &pldmTID, &responseMsg,
279 &responseSize);
Eddie Jamesec6f1412024-10-17 10:55:55 -0500280 struct pldm_msg_hdr* hdr = (struct pldm_msg_hdr*)responseMsg;
Matt Spinler9fdcad52025-02-07 10:12:43 -0600281 if ((pldmTID != _eid) ||
282 !pldm_msg_hdr_correlate_response(&_requestHeader, hdr))
Matt Spinler5c350fd2019-12-12 13:53:53 -0600283 {
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500284 // We got a response to someone else's message. Ignore it.
285 return;
286 }
287 if (rc)
288 {
289 if (rc == PLDM_REQUESTER_NOT_RESP_MSG)
Matt Spinler5c350fd2019-12-12 13:53:53 -0600290 {
291 // Due to the MCTP loopback, we may get notified of the message
292 // we just sent.
293 return;
294 }
295
296 auto e = errno;
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500297 lg2::error("pldm_transport_recv_msg failed, rc = {RC}, errno = {ERRNO}",
298 "RC",
Matt Spinler1b418862023-06-29 12:37:41 -0500299 static_cast<std::underlying_type_t<pldm_requester_rc_t>>(rc),
300 "ERRNO", e);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600301 status = ResponseStatus::failure;
302
303 responseMsg = nullptr;
304 }
Eddie Jamesec6f1412024-10-17 10:55:55 -0500305 if (hdr && (hdr->request || hdr->datagram))
306 {
307 free(responseMsg);
308 return;
309 }
Matt Spinler5c350fd2019-12-12 13:53:53 -0600310
Matt Spinler2843ba22020-03-03 16:36:32 -0600311 cleanupCmd();
312
313 // Can't use this instance ID anymore.
Patrick Williams49bcbe92024-06-26 23:06:54 -0500314 freeIID();
Matt Spinler5c350fd2019-12-12 13:53:53 -0600315
316 if (status == ResponseStatus::success)
317 {
318 uint8_t completionCode = 0;
319 auto response = reinterpret_cast<pldm_msg*>(responseMsg);
320
Patrick Williams075c7922024-08-16 15:19:49 -0400321 auto decodeRC =
322 decode_new_file_resp(response, responseSize, &completionCode);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600323 if (decodeRC < 0)
324 {
Matt Spinler1b418862023-06-29 12:37:41 -0500325 lg2::error("decode_new_file_resp failed, rc = {RC}", "RC",
326 decodeRC);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600327 status = ResponseStatus::failure;
328 }
329 else
330 {
331 if (completionCode != PLDM_SUCCESS)
332 {
Matt Spinler1b418862023-06-29 12:37:41 -0500333 lg2::error("Bad PLDM completion code {CODE}", "CODE",
334 completionCode);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600335 status = ResponseStatus::failure;
336 }
337 }
338 }
339
Matt Spinlera44efe42020-03-03 10:30:16 -0600340 callResponseFunc(status);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600341
342 if (responseMsg)
343 {
344 free(responseMsg);
345 }
346}
347
348void PLDMInterface::receiveTimerExpired()
349{
Matt Spinler1b418862023-06-29 12:37:41 -0500350 lg2::error("Timed out waiting for PLDM response");
Matt Spinler2843ba22020-03-03 16:36:32 -0600351
352 // Cleanup, but keep the instance ID because the host didn't
353 // respond so we can still use it.
354 cleanupCmd();
Matt Spinler5c350fd2019-12-12 13:53:53 -0600355
Matt Spinlera44efe42020-03-03 10:30:16 -0600356 callResponseFunc(ResponseStatus::failure);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600357}
358
359void PLDMInterface::cancelCmd()
360{
Patrick Williams49bcbe92024-06-26 23:06:54 -0500361 freeIID();
Matt Spinler2843ba22020-03-03 16:36:32 -0600362 cleanupCmd();
363}
364
365void PLDMInterface::cleanupCmd()
366{
Matt Spinler5c350fd2019-12-12 13:53:53 -0600367 _inProgress = false;
368 _source.reset();
369
370 if (_receiveTimer.isEnabled())
371 {
372 _receiveTimer.setEnabled(false);
373 }
374
375 closeFD();
376}
377
378} // namespace openpower::pels