blob: 51160f89f1d0e6c7a5edc647ad0eda47dfc372ce [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 {
190 throw std::runtime_error("Instance ID " + std::to_string(*_instanceID) +
191 " for TID " + std::to_string(_eid) +
192 " was not previously allocated");
193 }
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 }
260}
261
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500262void PLDMInterface::receive(IO& /*io*/, int /*fd*/, uint32_t revents,
263 pldm_transport* transport)
264
Matt Spinler5c350fd2019-12-12 13:53:53 -0600265{
266 if (!(revents & EPOLLIN))
267 {
268 return;
269 }
270
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500271 void* responseMsg = nullptr;
Matt Spinler5c350fd2019-12-12 13:53:53 -0600272 size_t responseSize = 0;
273 ResponseStatus status = ResponseStatus::success;
274
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500275 pldm_tid_t pldmTID;
276 auto rc = pldm_transport_recv_msg(transport, &pldmTID, &responseMsg,
277 &responseSize);
278 if (pldmTID != _eid)
Matt Spinler5c350fd2019-12-12 13:53:53 -0600279 {
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500280 // We got a response to someone else's message. Ignore it.
281 return;
282 }
283 if (rc)
284 {
285 if (rc == PLDM_REQUESTER_NOT_RESP_MSG)
Matt Spinler5c350fd2019-12-12 13:53:53 -0600286 {
287 // Due to the MCTP loopback, we may get notified of the message
288 // we just sent.
289 return;
290 }
291
292 auto e = errno;
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500293 lg2::error("pldm_transport_recv_msg failed, rc = {RC}, errno = {ERRNO}",
294 "RC",
Matt Spinler1b418862023-06-29 12:37:41 -0500295 static_cast<std::underlying_type_t<pldm_requester_rc_t>>(rc),
296 "ERRNO", e);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600297 status = ResponseStatus::failure;
298
299 responseMsg = nullptr;
300 }
301
Matt Spinler2843ba22020-03-03 16:36:32 -0600302 cleanupCmd();
303
304 // Can't use this instance ID anymore.
Patrick Williams49bcbe92024-06-26 23:06:54 -0500305 freeIID();
Matt Spinler5c350fd2019-12-12 13:53:53 -0600306
307 if (status == ResponseStatus::success)
308 {
309 uint8_t completionCode = 0;
310 auto response = reinterpret_cast<pldm_msg*>(responseMsg);
311
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500312 auto decodeRC = decode_new_file_resp(response, responseSize,
Matt Spinler5c350fd2019-12-12 13:53:53 -0600313 &completionCode);
314 if (decodeRC < 0)
315 {
Matt Spinler1b418862023-06-29 12:37:41 -0500316 lg2::error("decode_new_file_resp failed, rc = {RC}", "RC",
317 decodeRC);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600318 status = ResponseStatus::failure;
319 }
320 else
321 {
322 if (completionCode != PLDM_SUCCESS)
323 {
Matt Spinler1b418862023-06-29 12:37:41 -0500324 lg2::error("Bad PLDM completion code {CODE}", "CODE",
325 completionCode);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600326 status = ResponseStatus::failure;
327 }
328 }
329 }
330
Matt Spinlera44efe42020-03-03 10:30:16 -0600331 callResponseFunc(status);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600332
333 if (responseMsg)
334 {
335 free(responseMsg);
336 }
337}
338
339void PLDMInterface::receiveTimerExpired()
340{
Matt Spinler1b418862023-06-29 12:37:41 -0500341 lg2::error("Timed out waiting for PLDM response");
Matt Spinler2843ba22020-03-03 16:36:32 -0600342
343 // Cleanup, but keep the instance ID because the host didn't
344 // respond so we can still use it.
345 cleanupCmd();
Matt Spinler5c350fd2019-12-12 13:53:53 -0600346
Matt Spinlera44efe42020-03-03 10:30:16 -0600347 callResponseFunc(ResponseStatus::failure);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600348}
349
350void PLDMInterface::cancelCmd()
351{
Patrick Williams49bcbe92024-06-26 23:06:54 -0500352 freeIID();
Matt Spinler2843ba22020-03-03 16:36:32 -0600353 cleanupCmd();
354}
355
356void PLDMInterface::cleanupCmd()
357{
Matt Spinler5c350fd2019-12-12 13:53:53 -0600358 _inProgress = false;
359 _source.reset();
360
361 if (_receiveTimer.isEnabled())
362 {
363 _receiveTimer.setEnabled(false);
364 }
365
366 closeFD();
367}
368
369} // namespace openpower::pels