blob: c8f59e2554a435a7ea53b529d65b4e0b637c98cc [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 }
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);
Eddie Jamesec6f1412024-10-17 10:55:55 -0500278 struct pldm_msg_hdr* hdr = (struct pldm_msg_hdr*)responseMsg;
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500279 if (pldmTID != _eid)
Matt Spinler5c350fd2019-12-12 13:53:53 -0600280 {
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500281 // We got a response to someone else's message. Ignore it.
282 return;
283 }
284 if (rc)
285 {
286 if (rc == PLDM_REQUESTER_NOT_RESP_MSG)
Matt Spinler5c350fd2019-12-12 13:53:53 -0600287 {
288 // Due to the MCTP loopback, we may get notified of the message
289 // we just sent.
290 return;
291 }
292
293 auto e = errno;
Lakshmi Yadlapati0387a742024-07-01 17:04:37 -0500294 lg2::error("pldm_transport_recv_msg failed, rc = {RC}, errno = {ERRNO}",
295 "RC",
Matt Spinler1b418862023-06-29 12:37:41 -0500296 static_cast<std::underlying_type_t<pldm_requester_rc_t>>(rc),
297 "ERRNO", e);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600298 status = ResponseStatus::failure;
299
300 responseMsg = nullptr;
301 }
Eddie Jamesec6f1412024-10-17 10:55:55 -0500302 if (hdr && (hdr->request || hdr->datagram))
303 {
304 free(responseMsg);
305 return;
306 }
Matt Spinler5c350fd2019-12-12 13:53:53 -0600307
Matt Spinler2843ba22020-03-03 16:36:32 -0600308 cleanupCmd();
309
310 // Can't use this instance ID anymore.
Patrick Williams49bcbe92024-06-26 23:06:54 -0500311 freeIID();
Matt Spinler5c350fd2019-12-12 13:53:53 -0600312
313 if (status == ResponseStatus::success)
314 {
315 uint8_t completionCode = 0;
316 auto response = reinterpret_cast<pldm_msg*>(responseMsg);
317
Patrick Williams075c7922024-08-16 15:19:49 -0400318 auto decodeRC =
319 decode_new_file_resp(response, responseSize, &completionCode);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600320 if (decodeRC < 0)
321 {
Matt Spinler1b418862023-06-29 12:37:41 -0500322 lg2::error("decode_new_file_resp failed, rc = {RC}", "RC",
323 decodeRC);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600324 status = ResponseStatus::failure;
325 }
326 else
327 {
328 if (completionCode != PLDM_SUCCESS)
329 {
Matt Spinler1b418862023-06-29 12:37:41 -0500330 lg2::error("Bad PLDM completion code {CODE}", "CODE",
331 completionCode);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600332 status = ResponseStatus::failure;
333 }
334 }
335 }
336
Matt Spinlera44efe42020-03-03 10:30:16 -0600337 callResponseFunc(status);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600338
339 if (responseMsg)
340 {
341 free(responseMsg);
342 }
343}
344
345void PLDMInterface::receiveTimerExpired()
346{
Matt Spinler1b418862023-06-29 12:37:41 -0500347 lg2::error("Timed out waiting for PLDM response");
Matt Spinler2843ba22020-03-03 16:36:32 -0600348
349 // Cleanup, but keep the instance ID because the host didn't
350 // respond so we can still use it.
351 cleanupCmd();
Matt Spinler5c350fd2019-12-12 13:53:53 -0600352
Matt Spinlera44efe42020-03-03 10:30:16 -0600353 callResponseFunc(ResponseStatus::failure);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600354}
355
356void PLDMInterface::cancelCmd()
357{
Patrick Williams49bcbe92024-06-26 23:06:54 -0500358 freeIID();
Matt Spinler2843ba22020-03-03 16:36:32 -0600359 cleanupCmd();
360}
361
362void PLDMInterface::cleanupCmd()
363{
Matt Spinler5c350fd2019-12-12 13:53:53 -0600364 _inProgress = false;
365 _source.reset();
366
367 if (_receiveTimer.isEnabled())
368 {
369 _receiveTimer.setEnabled(false);
370 }
371
372 closeFD();
373}
374
375} // namespace openpower::pels