blob: aab7a54bcab923fdc83d3ff8439651462d113f92 [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>
Matt Spinler5c350fd2019-12-12 13:53:53 -060020#include <unistd.h>
21
Matt Spinler1b418862023-06-29 12:37:41 -050022#include <phosphor-logging/lg2.hpp>
Matt Spinler5c350fd2019-12-12 13:53:53 -060023
Patrick Williams2544b412022-10-04 08:41:06 -050024#include <fstream>
25
Matt Spinler5c350fd2019-12-12 13:53:53 -060026namespace openpower::pels
27{
28
Matt Spinler5c350fd2019-12-12 13:53:53 -060029using namespace sdeventplus;
30using namespace sdeventplus::source;
31
32constexpr auto eidPath = "/usr/share/pldm/host_eid";
33constexpr mctp_eid_t defaultEIDValue = 9;
34
35constexpr uint16_t pelFileType = 0;
36
37PLDMInterface::~PLDMInterface()
38{
Patrick Williams49bcbe92024-06-26 23:06:54 -050039 freeIID();
40 pldm_instance_db_destroy(_pldm_idb);
Matt Spinler5c350fd2019-12-12 13:53:53 -060041 closeFD();
42}
43
44void PLDMInterface::closeFD()
45{
46 if (_fd >= 0)
47 {
48 close(_fd);
49 _fd = -1;
50 }
51}
52
53void PLDMInterface::readEID()
54{
55 _eid = defaultEIDValue;
56
57 std::ifstream eidFile{eidPath};
58 if (!eidFile.good())
59 {
Matt Spinler1b418862023-06-29 12:37:41 -050060 lg2::error("Could not open host EID file");
Matt Spinler5c350fd2019-12-12 13:53:53 -060061 }
62 else
63 {
64 std::string eid;
65 eidFile >> eid;
66 if (!eid.empty())
67 {
68 _eid = atoi(eid.c_str());
69 }
70 else
71 {
Matt Spinler1b418862023-06-29 12:37:41 -050072 lg2::error("EID file was empty");
Matt Spinler5c350fd2019-12-12 13:53:53 -060073 }
74 }
75}
76
77void PLDMInterface::open()
78{
79 _fd = pldm_open();
80 if (_fd < 0)
81 {
82 auto e = errno;
Matt Spinler1b418862023-06-29 12:37:41 -050083 lg2::error("pldm_open failed. errno = {ERRNO}, rc = {RC}", "ERRNO", e,
84 "RC", _fd);
Matt Spinler527ff342023-06-29 12:52:34 -050085 throw std::runtime_error{"pldm_open failed"};
Matt Spinler5c350fd2019-12-12 13:53:53 -060086 }
87}
88
Matt Spinler2843ba22020-03-03 16:36:32 -060089void PLDMInterface::startCommand()
Matt Spinler5c350fd2019-12-12 13:53:53 -060090{
91 try
92 {
93 closeFD();
94
95 open();
96
Matt Spinler5c350fd2019-12-12 13:53:53 -060097 registerReceiveCallback();
98
Matt Spinler2843ba22020-03-03 16:36:32 -060099 doSend();
100
101 _receiveTimer.restartOnce(_receiveTimeout);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600102 }
103 catch (const std::exception& e)
104 {
Matt Spinler527ff342023-06-29 12:52:34 -0500105 lg2::error("startCommand exception: {ERROR}", "ERROR", e);
106
Matt Spinler2843ba22020-03-03 16:36:32 -0600107 cleanupCmd();
Matt Spinler5c350fd2019-12-12 13:53:53 -0600108
Matt Spinler527ff342023-06-29 12:52:34 -0500109 throw;
Matt Spinler2843ba22020-03-03 16:36:32 -0600110 }
111}
112
Patrick Williams49bcbe92024-06-26 23:06:54 -0500113void PLDMInterface::allocIID()
Matt Spinler2843ba22020-03-03 16:36:32 -0600114{
Patrick Williams49bcbe92024-06-26 23:06:54 -0500115 if (_instanceID)
Matt Spinler2843ba22020-03-03 16:36:32 -0600116 {
Patrick Williams49bcbe92024-06-26 23:06:54 -0500117 return;
Matt Spinler2843ba22020-03-03 16:36:32 -0600118 }
Patrick Williams49bcbe92024-06-26 23:06:54 -0500119
120 pldm_instance_id_t iid = 0;
121 auto rc = pldm_instance_id_alloc(_pldm_idb, _eid, &iid);
122
123 if (rc == -EAGAIN)
124 {
125 throw std::runtime_error("No free instance ids");
126 }
127 else if (rc)
128 {
129 throw std::system_category().default_error_condition(rc);
130 }
131
132 _instanceID = iid;
133}
134
135void PLDMInterface::freeIID()
136{
137 if (!_instanceID)
138 {
139 return;
140 }
141
142 auto rc = pldm_instance_id_free(_pldm_idb, _eid, *_instanceID);
143
144 if (rc == -EINVAL)
145 {
146 throw std::runtime_error("Instance ID " + std::to_string(*_instanceID) +
147 " for TID " + std::to_string(_eid) +
148 " was not previously allocated");
149 }
150 else if (rc)
151 {
152 throw std::system_category().default_error_condition(rc);
153 }
154
155 _instanceID = std::nullopt;
Matt Spinler2843ba22020-03-03 16:36:32 -0600156}
157
158CmdStatus PLDMInterface::sendNewLogCmd(uint32_t id, uint32_t size)
159{
160 _pelID = id;
161 _pelSize = size;
162 _inProgress = true;
163
164 try
165 {
Patrick Williams49bcbe92024-06-26 23:06:54 -0500166 // Allocate the instance ID, as needed.
Matt Spinler2843ba22020-03-03 16:36:32 -0600167 if (!_instanceID)
168 {
Patrick Williams49bcbe92024-06-26 23:06:54 -0500169 allocIID();
Matt Spinler2843ba22020-03-03 16:36:32 -0600170 }
Patrick Williams49bcbe92024-06-26 23:06:54 -0500171 startCommand();
Matt Spinler2843ba22020-03-03 16:36:32 -0600172 }
173 catch (const std::exception& e)
174 {
Matt Spinler5c350fd2019-12-12 13:53:53 -0600175 _inProgress = false;
Matt Spinler5c350fd2019-12-12 13:53:53 -0600176 return CmdStatus::failure;
177 }
178
Matt Spinler5c350fd2019-12-12 13:53:53 -0600179 return CmdStatus::success;
180}
181
182void PLDMInterface::registerReceiveCallback()
183{
184 _source = std::make_unique<IO>(
185 _event, _fd, EPOLLIN,
186 std::bind(std::mem_fn(&PLDMInterface::receive), this,
187 std::placeholders::_1, std::placeholders::_2,
188 std::placeholders::_3));
189}
190
Matt Spinler2843ba22020-03-03 16:36:32 -0600191void PLDMInterface::doSend()
Matt Spinler5c350fd2019-12-12 13:53:53 -0600192{
193 std::array<uint8_t, sizeof(pldm_msg_hdr) + sizeof(pelFileType) +
Matt Spinler2843ba22020-03-03 16:36:32 -0600194 sizeof(_pelID) + sizeof(uint64_t)>
Matt Spinler5c350fd2019-12-12 13:53:53 -0600195 requestMsg;
196
197 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
198
Matt Spinler2843ba22020-03-03 16:36:32 -0600199 auto rc = encode_new_file_req(*_instanceID, pelFileType, _pelID, _pelSize,
200 request);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600201 if (rc != PLDM_SUCCESS)
202 {
Matt Spinler1b418862023-06-29 12:37:41 -0500203 lg2::error("encode_new_file_req failed, rc = {RC}", "RC", rc);
Matt Spinler527ff342023-06-29 12:52:34 -0500204 throw std::runtime_error{"encode_new_file_req failed"};
Matt Spinler5c350fd2019-12-12 13:53:53 -0600205 }
206
207 rc = pldm_send(_eid, _fd, requestMsg.data(), requestMsg.size());
208 if (rc < 0)
209 {
210 auto e = errno;
Matt Spinler1b418862023-06-29 12:37:41 -0500211 lg2::error("pldm_send failed, rc = {RC}, errno = {ERRNO}", "RC", rc,
212 "ERRNO", e);
Matt Spinler527ff342023-06-29 12:52:34 -0500213 throw std::runtime_error{"pldm_send failed"};
Matt Spinler5c350fd2019-12-12 13:53:53 -0600214 }
215}
216
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500217void PLDMInterface::receive(IO& /*io*/, int fd, uint32_t revents)
Matt Spinler5c350fd2019-12-12 13:53:53 -0600218{
219 if (!(revents & EPOLLIN))
220 {
221 return;
222 }
223
224 uint8_t* responseMsg = nullptr;
225 size_t responseSize = 0;
226 ResponseStatus status = ResponseStatus::success;
227
Matt Spinler2843ba22020-03-03 16:36:32 -0600228 auto rc = pldm_recv(_eid, fd, *_instanceID, &responseMsg, &responseSize);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600229 if (rc < 0)
230 {
231 if (rc == PLDM_REQUESTER_INSTANCE_ID_MISMATCH)
232 {
233 // We got a response to someone else's message. Ignore it.
234 return;
235 }
236 else if (rc == PLDM_REQUESTER_NOT_RESP_MSG)
237 {
238 // Due to the MCTP loopback, we may get notified of the message
239 // we just sent.
240 return;
241 }
242
243 auto e = errno;
Matt Spinler1b418862023-06-29 12:37:41 -0500244 lg2::error("pldm_recv failed, rc = {RC}, errno = {ERRNO}", "RC",
245 static_cast<std::underlying_type_t<pldm_requester_rc_t>>(rc),
246 "ERRNO", e);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600247 status = ResponseStatus::failure;
248
249 responseMsg = nullptr;
250 }
251
Matt Spinler2843ba22020-03-03 16:36:32 -0600252 cleanupCmd();
253
254 // Can't use this instance ID anymore.
Patrick Williams49bcbe92024-06-26 23:06:54 -0500255 freeIID();
Matt Spinler5c350fd2019-12-12 13:53:53 -0600256
257 if (status == ResponseStatus::success)
258 {
259 uint8_t completionCode = 0;
260 auto response = reinterpret_cast<pldm_msg*>(responseMsg);
261
262 auto decodeRC = decode_new_file_resp(response, PLDM_NEW_FILE_RESP_BYTES,
263 &completionCode);
264 if (decodeRC < 0)
265 {
Matt Spinler1b418862023-06-29 12:37:41 -0500266 lg2::error("decode_new_file_resp failed, rc = {RC}", "RC",
267 decodeRC);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600268 status = ResponseStatus::failure;
269 }
270 else
271 {
272 if (completionCode != PLDM_SUCCESS)
273 {
Matt Spinler1b418862023-06-29 12:37:41 -0500274 lg2::error("Bad PLDM completion code {CODE}", "CODE",
275 completionCode);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600276 status = ResponseStatus::failure;
277 }
278 }
279 }
280
Matt Spinlera44efe42020-03-03 10:30:16 -0600281 callResponseFunc(status);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600282
283 if (responseMsg)
284 {
285 free(responseMsg);
286 }
287}
288
289void PLDMInterface::receiveTimerExpired()
290{
Matt Spinler1b418862023-06-29 12:37:41 -0500291 lg2::error("Timed out waiting for PLDM response");
Matt Spinler2843ba22020-03-03 16:36:32 -0600292
293 // Cleanup, but keep the instance ID because the host didn't
294 // respond so we can still use it.
295 cleanupCmd();
Matt Spinler5c350fd2019-12-12 13:53:53 -0600296
Matt Spinlera44efe42020-03-03 10:30:16 -0600297 callResponseFunc(ResponseStatus::failure);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600298}
299
300void PLDMInterface::cancelCmd()
301{
Patrick Williams49bcbe92024-06-26 23:06:54 -0500302 freeIID();
Matt Spinler2843ba22020-03-03 16:36:32 -0600303 cleanupCmd();
304}
305
306void PLDMInterface::cleanupCmd()
307{
Matt Spinler5c350fd2019-12-12 13:53:53 -0600308 _inProgress = false;
309 _source.reset();
310
311 if (_receiveTimer.isEnabled())
312 {
313 _receiveTimer.setEnabled(false);
314 }
315
316 closeFD();
317}
318
319} // namespace openpower::pels