blob: 3326d82d4b02f7f1b5290f29b927b2f5502a9506 [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>
19#include <libpldm/file_io.h>
Matt Spinler2843ba22020-03-03 16:36:32 -060020#include <systemd/sd-bus.h>
Matt Spinler5c350fd2019-12-12 13:53:53 -060021#include <unistd.h>
22
Matt Spinler1b418862023-06-29 12:37:41 -050023#include <phosphor-logging/lg2.hpp>
Matt Spinler5c350fd2019-12-12 13:53:53 -060024
Patrick Williams2544b412022-10-04 08:41:06 -050025#include <fstream>
26
Matt Spinler5c350fd2019-12-12 13:53:53 -060027namespace openpower::pels
28{
29
Matt Spinler2843ba22020-03-03 16:36:32 -060030namespace service
31{
32constexpr auto pldm = "xyz.openbmc_project.PLDM";
33}
34
35namespace object_path
36{
37constexpr auto pldm = "/xyz/openbmc_project/pldm";
38}
39
40namespace interface
41{
42constexpr auto pldm_requester = "xyz.openbmc_project.PLDM.Requester";
43}
44
Matt Spinler5c350fd2019-12-12 13:53:53 -060045using namespace sdeventplus;
46using namespace sdeventplus::source;
47
48constexpr auto eidPath = "/usr/share/pldm/host_eid";
49constexpr mctp_eid_t defaultEIDValue = 9;
50
51constexpr uint16_t pelFileType = 0;
52
53PLDMInterface::~PLDMInterface()
54{
Matt Spinler2843ba22020-03-03 16:36:32 -060055 sd_bus_unref(_bus);
Matt Spinler5c350fd2019-12-12 13:53:53 -060056 closeFD();
57}
58
59void PLDMInterface::closeFD()
60{
61 if (_fd >= 0)
62 {
63 close(_fd);
64 _fd = -1;
65 }
66}
67
68void PLDMInterface::readEID()
69{
70 _eid = defaultEIDValue;
71
72 std::ifstream eidFile{eidPath};
73 if (!eidFile.good())
74 {
Matt Spinler1b418862023-06-29 12:37:41 -050075 lg2::error("Could not open host EID file");
Matt Spinler5c350fd2019-12-12 13:53:53 -060076 }
77 else
78 {
79 std::string eid;
80 eidFile >> eid;
81 if (!eid.empty())
82 {
83 _eid = atoi(eid.c_str());
84 }
85 else
86 {
Matt Spinler1b418862023-06-29 12:37:41 -050087 lg2::error("EID file was empty");
Matt Spinler5c350fd2019-12-12 13:53:53 -060088 }
89 }
90}
91
92void PLDMInterface::open()
93{
94 _fd = pldm_open();
95 if (_fd < 0)
96 {
97 auto e = errno;
Matt Spinler1b418862023-06-29 12:37:41 -050098 lg2::error("pldm_open failed. errno = {ERRNO}, rc = {RC}", "ERRNO", e,
99 "RC", _fd);
Matt Spinler527ff342023-06-29 12:52:34 -0500100 throw std::runtime_error{"pldm_open failed"};
Matt Spinler5c350fd2019-12-12 13:53:53 -0600101 }
102}
103
Matt Spinler2843ba22020-03-03 16:36:32 -0600104void PLDMInterface::instanceIDCallback(sd_bus_message* msg)
105{
106 if (!_inProgress)
107 {
Matt Spinler1b418862023-06-29 12:37:41 -0500108 lg2::info("A command was canceled while waiting for the instance ID");
Matt Spinler2843ba22020-03-03 16:36:32 -0600109 return;
110 }
111
112 bool failed = false;
113
114 auto rc = sd_bus_message_get_errno(msg);
115 if (rc)
116 {
Matt Spinler1b418862023-06-29 12:37:41 -0500117 lg2::error("GetInstanceId D-Bus method failed, rc = {RC}", "RC", rc);
Matt Spinler2843ba22020-03-03 16:36:32 -0600118 failed = true;
119 }
120 else
121 {
122 uint8_t id;
123 rc = sd_bus_message_read_basic(msg, 'y', &id);
124 if (rc < 0)
125 {
Matt Spinler1b418862023-06-29 12:37:41 -0500126 lg2::error("Could not read instance ID out of message, rc = {RC}",
127 "RC", rc);
Matt Spinler2843ba22020-03-03 16:36:32 -0600128 failed = true;
129 }
130 else
131 {
132 _instanceID = id;
133 }
134 }
135
136 if (failed)
137 {
138 _inProgress = false;
139 callResponseFunc(ResponseStatus::failure);
140 }
141 else
142 {
Matt Spinler527ff342023-06-29 12:52:34 -0500143 try
144 {
145 startCommand();
146 }
147 catch (const std::exception& e)
148 {
149 callResponseFunc(ResponseStatus::failure);
150 }
Matt Spinler2843ba22020-03-03 16:36:32 -0600151 }
152}
153
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500154int iidCallback(sd_bus_message* msg, void* data, sd_bus_error* /*err*/)
Matt Spinler2843ba22020-03-03 16:36:32 -0600155{
156 auto* interface = static_cast<PLDMInterface*>(data);
157 interface->instanceIDCallback(msg);
158 return 0;
159}
160
161void PLDMInterface::startCommand()
Matt Spinler5c350fd2019-12-12 13:53:53 -0600162{
163 try
164 {
165 closeFD();
166
167 open();
168
Matt Spinler5c350fd2019-12-12 13:53:53 -0600169 registerReceiveCallback();
170
Matt Spinler2843ba22020-03-03 16:36:32 -0600171 doSend();
172
173 _receiveTimer.restartOnce(_receiveTimeout);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600174 }
175 catch (const std::exception& e)
176 {
Matt Spinler527ff342023-06-29 12:52:34 -0500177 lg2::error("startCommand exception: {ERROR}", "ERROR", e);
178
Matt Spinler2843ba22020-03-03 16:36:32 -0600179 cleanupCmd();
Matt Spinler5c350fd2019-12-12 13:53:53 -0600180
Matt Spinler527ff342023-06-29 12:52:34 -0500181 throw;
Matt Spinler2843ba22020-03-03 16:36:32 -0600182 }
183}
184
185void PLDMInterface::startReadInstanceID()
186{
187 auto rc = sd_bus_call_method_async(
188 _bus, NULL, service::pldm, object_path::pldm, interface::pldm_requester,
189 "GetInstanceId", iidCallback, this, "y", _eid);
190
191 if (rc < 0)
192 {
Matt Spinler1b418862023-06-29 12:37:41 -0500193 lg2::error(
194 "Error calling sd_bus_call_method_async, rc = {RC}, msg = {MSG}",
195 "RC", rc, "MSG", strerror(-rc));
Matt Spinler527ff342023-06-29 12:52:34 -0500196 throw std::runtime_error{"sd_bus_call_method_async failed"};
Matt Spinler2843ba22020-03-03 16:36:32 -0600197 }
198}
199
200CmdStatus PLDMInterface::sendNewLogCmd(uint32_t id, uint32_t size)
201{
202 _pelID = id;
203 _pelSize = size;
204 _inProgress = true;
205
206 try
207 {
208 // Kick off the async call to get the instance ID if
209 // necessary, otherwise start the command itself.
210 if (!_instanceID)
211 {
212 startReadInstanceID();
213 }
214 else
215 {
216 startCommand();
217 }
218 }
219 catch (const std::exception& e)
220 {
Matt Spinler5c350fd2019-12-12 13:53:53 -0600221 _inProgress = false;
Matt Spinler5c350fd2019-12-12 13:53:53 -0600222 return CmdStatus::failure;
223 }
224
Matt Spinler5c350fd2019-12-12 13:53:53 -0600225 return CmdStatus::success;
226}
227
228void PLDMInterface::registerReceiveCallback()
229{
230 _source = std::make_unique<IO>(
231 _event, _fd, EPOLLIN,
232 std::bind(std::mem_fn(&PLDMInterface::receive), this,
233 std::placeholders::_1, std::placeholders::_2,
234 std::placeholders::_3));
235}
236
Matt Spinler2843ba22020-03-03 16:36:32 -0600237void PLDMInterface::doSend()
Matt Spinler5c350fd2019-12-12 13:53:53 -0600238{
239 std::array<uint8_t, sizeof(pldm_msg_hdr) + sizeof(pelFileType) +
Matt Spinler2843ba22020-03-03 16:36:32 -0600240 sizeof(_pelID) + sizeof(uint64_t)>
Matt Spinler5c350fd2019-12-12 13:53:53 -0600241 requestMsg;
242
243 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
244
Matt Spinler2843ba22020-03-03 16:36:32 -0600245 auto rc = encode_new_file_req(*_instanceID, pelFileType, _pelID, _pelSize,
246 request);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600247 if (rc != PLDM_SUCCESS)
248 {
Matt Spinler1b418862023-06-29 12:37:41 -0500249 lg2::error("encode_new_file_req failed, rc = {RC}", "RC", rc);
Matt Spinler527ff342023-06-29 12:52:34 -0500250 throw std::runtime_error{"encode_new_file_req failed"};
Matt Spinler5c350fd2019-12-12 13:53:53 -0600251 }
252
253 rc = pldm_send(_eid, _fd, requestMsg.data(), requestMsg.size());
254 if (rc < 0)
255 {
256 auto e = errno;
Matt Spinler1b418862023-06-29 12:37:41 -0500257 lg2::error("pldm_send failed, rc = {RC}, errno = {ERRNO}", "RC", rc,
258 "ERRNO", e);
Matt Spinler527ff342023-06-29 12:52:34 -0500259 throw std::runtime_error{"pldm_send failed"};
Matt Spinler5c350fd2019-12-12 13:53:53 -0600260 }
261}
262
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500263void PLDMInterface::receive(IO& /*io*/, int fd, uint32_t revents)
Matt Spinler5c350fd2019-12-12 13:53:53 -0600264{
265 if (!(revents & EPOLLIN))
266 {
267 return;
268 }
269
270 uint8_t* responseMsg = nullptr;
271 size_t responseSize = 0;
272 ResponseStatus status = ResponseStatus::success;
273
Matt Spinler2843ba22020-03-03 16:36:32 -0600274 auto rc = pldm_recv(_eid, fd, *_instanceID, &responseMsg, &responseSize);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600275 if (rc < 0)
276 {
277 if (rc == PLDM_REQUESTER_INSTANCE_ID_MISMATCH)
278 {
279 // We got a response to someone else's message. Ignore it.
280 return;
281 }
282 else if (rc == PLDM_REQUESTER_NOT_RESP_MSG)
283 {
284 // Due to the MCTP loopback, we may get notified of the message
285 // we just sent.
286 return;
287 }
288
289 auto e = errno;
Matt Spinler1b418862023-06-29 12:37:41 -0500290 lg2::error("pldm_recv failed, rc = {RC}, errno = {ERRNO}", "RC",
291 static_cast<std::underlying_type_t<pldm_requester_rc_t>>(rc),
292 "ERRNO", e);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600293 status = ResponseStatus::failure;
294
295 responseMsg = nullptr;
296 }
297
Matt Spinler2843ba22020-03-03 16:36:32 -0600298 cleanupCmd();
299
300 // Can't use this instance ID anymore.
301 _instanceID = std::nullopt;
Matt Spinler5c350fd2019-12-12 13:53:53 -0600302
303 if (status == ResponseStatus::success)
304 {
305 uint8_t completionCode = 0;
306 auto response = reinterpret_cast<pldm_msg*>(responseMsg);
307
308 auto decodeRC = decode_new_file_resp(response, PLDM_NEW_FILE_RESP_BYTES,
309 &completionCode);
310 if (decodeRC < 0)
311 {
Matt Spinler1b418862023-06-29 12:37:41 -0500312 lg2::error("decode_new_file_resp failed, rc = {RC}", "RC",
313 decodeRC);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600314 status = ResponseStatus::failure;
315 }
316 else
317 {
318 if (completionCode != PLDM_SUCCESS)
319 {
Matt Spinler1b418862023-06-29 12:37:41 -0500320 lg2::error("Bad PLDM completion code {CODE}", "CODE",
321 completionCode);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600322 status = ResponseStatus::failure;
323 }
324 }
325 }
326
Matt Spinlera44efe42020-03-03 10:30:16 -0600327 callResponseFunc(status);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600328
329 if (responseMsg)
330 {
331 free(responseMsg);
332 }
333}
334
335void PLDMInterface::receiveTimerExpired()
336{
Matt Spinler1b418862023-06-29 12:37:41 -0500337 lg2::error("Timed out waiting for PLDM response");
Matt Spinler2843ba22020-03-03 16:36:32 -0600338
339 // Cleanup, but keep the instance ID because the host didn't
340 // respond so we can still use it.
341 cleanupCmd();
Matt Spinler5c350fd2019-12-12 13:53:53 -0600342
Matt Spinlera44efe42020-03-03 10:30:16 -0600343 callResponseFunc(ResponseStatus::failure);
Matt Spinler5c350fd2019-12-12 13:53:53 -0600344}
345
346void PLDMInterface::cancelCmd()
347{
Matt Spinler2843ba22020-03-03 16:36:32 -0600348 _instanceID = std::nullopt;
349 cleanupCmd();
350}
351
352void PLDMInterface::cleanupCmd()
353{
Matt Spinler5c350fd2019-12-12 13:53:53 -0600354 _inProgress = false;
355 _source.reset();
356
357 if (_receiveTimer.isEnabled())
358 {
359 _receiveTimer.setEnabled(false);
360 }
361
362 closeFD();
363}
364
365} // namespace openpower::pels