xref: /openbmc/phosphor-logging/extensions/openpower-pels/pldm_interface.cpp (revision 0387a74ef17ca9ff30968dccf27769b79e711e70)
15c350fdfSMatt Spinler /**
25c350fdfSMatt Spinler  * Copyright © 2019 IBM Corporation
35c350fdfSMatt Spinler  *
45c350fdfSMatt Spinler  * Licensed under the Apache License, Version 2.0 (the "License");
55c350fdfSMatt Spinler  * you may not use this file except in compliance with the License.
65c350fdfSMatt Spinler  * You may obtain a copy of the License at
75c350fdfSMatt Spinler  *
85c350fdfSMatt Spinler  *     http://www.apache.org/licenses/LICENSE-2.0
95c350fdfSMatt Spinler  *
105c350fdfSMatt Spinler  * Unless required by applicable law or agreed to in writing, software
115c350fdfSMatt Spinler  * distributed under the License is distributed on an "AS IS" BASIS,
125c350fdfSMatt Spinler  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135c350fdfSMatt Spinler  * See the License for the specific language governing permissions and
145c350fdfSMatt Spinler  * limitations under the License.
155c350fdfSMatt Spinler  */
165c350fdfSMatt Spinler #include "pldm_interface.hpp"
175c350fdfSMatt Spinler 
185c350fdfSMatt Spinler #include <libpldm/base.h>
197cc55b58SAndrew Jeffery #include <libpldm/oem/ibm/file_io.h>
20*0387a74eSLakshmi Yadlapati #include <libpldm/transport.h>
21*0387a74eSLakshmi Yadlapati #include <libpldm/transport/mctp-demux.h>
22*0387a74eSLakshmi Yadlapati #include <poll.h>
235c350fdfSMatt Spinler #include <unistd.h>
245c350fdfSMatt Spinler 
251b41886dSMatt Spinler #include <phosphor-logging/lg2.hpp>
265c350fdfSMatt Spinler 
272544b419SPatrick Williams #include <fstream>
282544b419SPatrick Williams 
295c350fdfSMatt Spinler namespace openpower::pels
305c350fdfSMatt Spinler {
315c350fdfSMatt Spinler 
325c350fdfSMatt Spinler using namespace sdeventplus;
335c350fdfSMatt Spinler using namespace sdeventplus::source;
34*0387a74eSLakshmi Yadlapati using TerminusID = uint8_t;
355c350fdfSMatt Spinler 
365c350fdfSMatt Spinler constexpr auto eidPath = "/usr/share/pldm/host_eid";
375c350fdfSMatt Spinler constexpr mctp_eid_t defaultEIDValue = 9;
38*0387a74eSLakshmi Yadlapati constexpr TerminusID tid = defaultEIDValue;
395c350fdfSMatt Spinler 
405c350fdfSMatt Spinler constexpr uint16_t pelFileType = 0;
415c350fdfSMatt Spinler 
425c350fdfSMatt Spinler PLDMInterface::~PLDMInterface()
435c350fdfSMatt Spinler {
4449bcbe92SPatrick Williams     freeIID();
4549bcbe92SPatrick Williams     pldm_instance_db_destroy(_pldm_idb);
465c350fdfSMatt Spinler     closeFD();
475c350fdfSMatt Spinler }
485c350fdfSMatt Spinler 
495c350fdfSMatt Spinler void PLDMInterface::closeFD()
505c350fdfSMatt Spinler {
51*0387a74eSLakshmi Yadlapati     pldm_transport_mctp_demux_destroy(mctpDemux);
52*0387a74eSLakshmi Yadlapati     mctpDemux = nullptr;
535c350fdfSMatt Spinler     _fd = -1;
54*0387a74eSLakshmi Yadlapati     pldmTransport = nullptr;
555c350fdfSMatt Spinler }
565c350fdfSMatt Spinler 
575c350fdfSMatt Spinler void PLDMInterface::readEID()
585c350fdfSMatt Spinler {
595c350fdfSMatt Spinler     _eid = defaultEIDValue;
605c350fdfSMatt Spinler 
615c350fdfSMatt Spinler     std::ifstream eidFile{eidPath};
625c350fdfSMatt Spinler     if (!eidFile.good())
635c350fdfSMatt Spinler     {
641b41886dSMatt Spinler         lg2::error("Could not open host EID file");
655c350fdfSMatt Spinler     }
665c350fdfSMatt Spinler     else
675c350fdfSMatt Spinler     {
685c350fdfSMatt Spinler         std::string eid;
695c350fdfSMatt Spinler         eidFile >> eid;
705c350fdfSMatt Spinler         if (!eid.empty())
715c350fdfSMatt Spinler         {
725c350fdfSMatt Spinler             _eid = atoi(eid.c_str());
735c350fdfSMatt Spinler         }
745c350fdfSMatt Spinler         else
755c350fdfSMatt Spinler         {
761b41886dSMatt Spinler             lg2::error("EID file was empty");
775c350fdfSMatt Spinler         }
785c350fdfSMatt Spinler     }
795c350fdfSMatt Spinler }
805c350fdfSMatt Spinler 
815c350fdfSMatt Spinler void PLDMInterface::open()
825c350fdfSMatt Spinler {
83*0387a74eSLakshmi Yadlapati     if (pldmTransport)
84*0387a74eSLakshmi Yadlapati     {
85*0387a74eSLakshmi Yadlapati         lg2::error("open: pldmTransport already setup!");
86*0387a74eSLakshmi Yadlapati         throw std::runtime_error{"open failed"};
87*0387a74eSLakshmi Yadlapati     }
88*0387a74eSLakshmi Yadlapati 
89*0387a74eSLakshmi Yadlapati     _fd = openMctpDemuxTransport();
905c350fdfSMatt Spinler     if (_fd < 0)
915c350fdfSMatt Spinler     {
925c350fdfSMatt Spinler         auto e = errno;
93*0387a74eSLakshmi Yadlapati         lg2::error("Transport open failed. errno = {ERRNO}, rc = {RC}", "ERRNO",
94*0387a74eSLakshmi Yadlapati                    e, "RC", _fd);
95*0387a74eSLakshmi Yadlapati         throw std::runtime_error{"Transport open failed"};
965c350fdfSMatt Spinler     }
975c350fdfSMatt Spinler }
985c350fdfSMatt Spinler 
99*0387a74eSLakshmi Yadlapati int PLDMInterface::openMctpDemuxTransport()
100*0387a74eSLakshmi Yadlapati {
101*0387a74eSLakshmi Yadlapati     int rc = pldm_transport_mctp_demux_init(&mctpDemux);
102*0387a74eSLakshmi Yadlapati     if (rc)
103*0387a74eSLakshmi Yadlapati     {
104*0387a74eSLakshmi Yadlapati         lg2::error(
105*0387a74eSLakshmi Yadlapati             "openMctpDemuxTransport: Failed to init MCTP demux transport. rc = {RC}",
106*0387a74eSLakshmi Yadlapati             "RC", rc);
107*0387a74eSLakshmi Yadlapati         return rc;
108*0387a74eSLakshmi Yadlapati     }
109*0387a74eSLakshmi Yadlapati 
110*0387a74eSLakshmi Yadlapati     rc = pldm_transport_mctp_demux_map_tid(mctpDemux, tid, tid);
111*0387a74eSLakshmi Yadlapati     if (rc)
112*0387a74eSLakshmi Yadlapati     {
113*0387a74eSLakshmi Yadlapati         lg2::error(
114*0387a74eSLakshmi Yadlapati             "openMctpDemuxTransport: Failed to setup tid to eid mapping. rc = {RC}",
115*0387a74eSLakshmi Yadlapati             "RC", rc);
116*0387a74eSLakshmi Yadlapati         cleanupCmd();
117*0387a74eSLakshmi Yadlapati         return rc;
118*0387a74eSLakshmi Yadlapati     }
119*0387a74eSLakshmi Yadlapati     pldmTransport = pldm_transport_mctp_demux_core(mctpDemux);
120*0387a74eSLakshmi Yadlapati 
121*0387a74eSLakshmi Yadlapati     struct pollfd pollfd;
122*0387a74eSLakshmi Yadlapati     rc = pldm_transport_mctp_demux_init_pollfd(pldmTransport, &pollfd);
123*0387a74eSLakshmi Yadlapati     if (rc)
124*0387a74eSLakshmi Yadlapati     {
125*0387a74eSLakshmi Yadlapati         lg2::error("openMctpDemuxTransport: Failed to get pollfd. rc = {RC}",
126*0387a74eSLakshmi Yadlapati                    "RC", rc);
127*0387a74eSLakshmi Yadlapati         cleanupCmd();
128*0387a74eSLakshmi Yadlapati         return rc;
129*0387a74eSLakshmi Yadlapati     }
130*0387a74eSLakshmi Yadlapati     return pollfd.fd;
131*0387a74eSLakshmi Yadlapati }
132*0387a74eSLakshmi Yadlapati 
1332843ba28SMatt Spinler void PLDMInterface::startCommand()
1345c350fdfSMatt Spinler {
1355c350fdfSMatt Spinler     try
1365c350fdfSMatt Spinler     {
1375c350fdfSMatt Spinler         closeFD();
1385c350fdfSMatt Spinler 
1395c350fdfSMatt Spinler         open();
1405c350fdfSMatt Spinler 
1415c350fdfSMatt Spinler         registerReceiveCallback();
1425c350fdfSMatt Spinler 
1432843ba28SMatt Spinler         doSend();
1442843ba28SMatt Spinler 
1452843ba28SMatt Spinler         _receiveTimer.restartOnce(_receiveTimeout);
1465c350fdfSMatt Spinler     }
1475c350fdfSMatt Spinler     catch (const std::exception& e)
1485c350fdfSMatt Spinler     {
149527ff346SMatt Spinler         lg2::error("startCommand exception: {ERROR}", "ERROR", e);
150527ff346SMatt Spinler 
1512843ba28SMatt Spinler         cleanupCmd();
1525c350fdfSMatt Spinler 
153527ff346SMatt Spinler         throw;
1542843ba28SMatt Spinler     }
1552843ba28SMatt Spinler }
1562843ba28SMatt Spinler 
15749bcbe92SPatrick Williams void PLDMInterface::allocIID()
1582843ba28SMatt Spinler {
15949bcbe92SPatrick Williams     if (_instanceID)
1602843ba28SMatt Spinler     {
16149bcbe92SPatrick Williams         return;
1622843ba28SMatt Spinler     }
16349bcbe92SPatrick Williams 
16449bcbe92SPatrick Williams     pldm_instance_id_t iid = 0;
16549bcbe92SPatrick Williams     auto rc = pldm_instance_id_alloc(_pldm_idb, _eid, &iid);
16649bcbe92SPatrick Williams 
16749bcbe92SPatrick Williams     if (rc == -EAGAIN)
16849bcbe92SPatrick Williams     {
16949bcbe92SPatrick Williams         throw std::runtime_error("No free instance ids");
17049bcbe92SPatrick Williams     }
17149bcbe92SPatrick Williams     else if (rc)
17249bcbe92SPatrick Williams     {
17349bcbe92SPatrick Williams         throw std::system_category().default_error_condition(rc);
17449bcbe92SPatrick Williams     }
17549bcbe92SPatrick Williams 
17649bcbe92SPatrick Williams     _instanceID = iid;
17749bcbe92SPatrick Williams }
17849bcbe92SPatrick Williams 
17949bcbe92SPatrick Williams void PLDMInterface::freeIID()
18049bcbe92SPatrick Williams {
18149bcbe92SPatrick Williams     if (!_instanceID)
18249bcbe92SPatrick Williams     {
18349bcbe92SPatrick Williams         return;
18449bcbe92SPatrick Williams     }
18549bcbe92SPatrick Williams 
18649bcbe92SPatrick Williams     auto rc = pldm_instance_id_free(_pldm_idb, _eid, *_instanceID);
18749bcbe92SPatrick Williams 
18849bcbe92SPatrick Williams     if (rc == -EINVAL)
18949bcbe92SPatrick Williams     {
19049bcbe92SPatrick Williams         throw std::runtime_error("Instance ID " + std::to_string(*_instanceID) +
19149bcbe92SPatrick Williams                                  " for TID " + std::to_string(_eid) +
19249bcbe92SPatrick Williams                                  " was not previously allocated");
19349bcbe92SPatrick Williams     }
19449bcbe92SPatrick Williams     else if (rc)
19549bcbe92SPatrick Williams     {
19649bcbe92SPatrick Williams         throw std::system_category().default_error_condition(rc);
19749bcbe92SPatrick Williams     }
19849bcbe92SPatrick Williams 
19949bcbe92SPatrick Williams     _instanceID = std::nullopt;
2002843ba28SMatt Spinler }
2012843ba28SMatt Spinler 
2022843ba28SMatt Spinler CmdStatus PLDMInterface::sendNewLogCmd(uint32_t id, uint32_t size)
2032843ba28SMatt Spinler {
2042843ba28SMatt Spinler     _pelID = id;
2052843ba28SMatt Spinler     _pelSize = size;
2062843ba28SMatt Spinler     _inProgress = true;
2072843ba28SMatt Spinler 
2082843ba28SMatt Spinler     try
2092843ba28SMatt Spinler     {
21049bcbe92SPatrick Williams         // Allocate the instance ID, as needed.
2112843ba28SMatt Spinler         if (!_instanceID)
2122843ba28SMatt Spinler         {
21349bcbe92SPatrick Williams             allocIID();
2142843ba28SMatt Spinler         }
2152843ba28SMatt Spinler         startCommand();
2162843ba28SMatt Spinler     }
2172843ba28SMatt Spinler     catch (const std::exception& e)
2182843ba28SMatt Spinler     {
2195c350fdfSMatt Spinler         _inProgress = false;
2205c350fdfSMatt Spinler         return CmdStatus::failure;
2215c350fdfSMatt Spinler     }
2225c350fdfSMatt Spinler 
2235c350fdfSMatt Spinler     return CmdStatus::success;
2245c350fdfSMatt Spinler }
2255c350fdfSMatt Spinler 
2265c350fdfSMatt Spinler void PLDMInterface::registerReceiveCallback()
2275c350fdfSMatt Spinler {
2285c350fdfSMatt Spinler     _source = std::make_unique<IO>(
2295c350fdfSMatt Spinler         _event, _fd, EPOLLIN,
2305c350fdfSMatt Spinler         std::bind(std::mem_fn(&PLDMInterface::receive), this,
2315c350fdfSMatt Spinler                   std::placeholders::_1, std::placeholders::_2,
232*0387a74eSLakshmi Yadlapati                   std::placeholders::_3, pldmTransport));
2335c350fdfSMatt Spinler }
2345c350fdfSMatt Spinler 
2352843ba28SMatt Spinler void PLDMInterface::doSend()
2365c350fdfSMatt Spinler {
2375c350fdfSMatt Spinler     std::array<uint8_t, sizeof(pldm_msg_hdr) + sizeof(pelFileType) +
2382843ba28SMatt Spinler                             sizeof(_pelID) + sizeof(uint64_t)>
2395c350fdfSMatt Spinler         requestMsg;
2405c350fdfSMatt Spinler 
2415c350fdfSMatt Spinler     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
2425c350fdfSMatt Spinler 
2432843ba28SMatt Spinler     auto rc = encode_new_file_req(*_instanceID, pelFileType, _pelID, _pelSize,
2442843ba28SMatt Spinler                                   request);
2455c350fdfSMatt Spinler     if (rc != PLDM_SUCCESS)
2465c350fdfSMatt Spinler     {
2471b41886dSMatt Spinler         lg2::error("encode_new_file_req failed, rc = {RC}", "RC", rc);
248527ff346SMatt Spinler         throw std::runtime_error{"encode_new_file_req failed"};
2495c350fdfSMatt Spinler     }
250*0387a74eSLakshmi Yadlapati     pldm_tid_t pldmTID = static_cast<pldm_tid_t>(_eid);
251*0387a74eSLakshmi Yadlapati     rc = pldm_transport_send_msg(pldmTransport, pldmTID, requestMsg.data(),
252*0387a74eSLakshmi Yadlapati                                  requestMsg.size());
2535c350fdfSMatt Spinler     if (rc < 0)
2545c350fdfSMatt Spinler     {
2555c350fdfSMatt Spinler         auto e = errno;
256*0387a74eSLakshmi Yadlapati         lg2::error("pldm_transport_send_msg failed, rc = {RC}, errno = {ERRNO}",
257*0387a74eSLakshmi Yadlapati                    "RC", rc, "ERRNO", e);
258*0387a74eSLakshmi Yadlapati         throw std::runtime_error{"pldm_transport_send_msg failed"};
2595c350fdfSMatt Spinler     }
2605c350fdfSMatt Spinler }
2615c350fdfSMatt Spinler 
262*0387a74eSLakshmi Yadlapati void PLDMInterface::receive(IO& /*io*/, int /*fd*/, uint32_t revents,
263*0387a74eSLakshmi Yadlapati                             pldm_transport* transport)
264*0387a74eSLakshmi Yadlapati 
2655c350fdfSMatt Spinler {
2665c350fdfSMatt Spinler     if (!(revents & EPOLLIN))
2675c350fdfSMatt Spinler     {
2685c350fdfSMatt Spinler         return;
2695c350fdfSMatt Spinler     }
2705c350fdfSMatt Spinler 
271*0387a74eSLakshmi Yadlapati     void* responseMsg = nullptr;
2725c350fdfSMatt Spinler     size_t responseSize = 0;
2735c350fdfSMatt Spinler     ResponseStatus status = ResponseStatus::success;
2745c350fdfSMatt Spinler 
275*0387a74eSLakshmi Yadlapati     pldm_tid_t pldmTID;
276*0387a74eSLakshmi Yadlapati     auto rc = pldm_transport_recv_msg(transport, &pldmTID, &responseMsg,
277*0387a74eSLakshmi Yadlapati                                       &responseSize);
278*0387a74eSLakshmi Yadlapati     if (pldmTID != _eid)
2795c350fdfSMatt Spinler     {
2805c350fdfSMatt Spinler         // We got a response to someone else's message. Ignore it.
2815c350fdfSMatt Spinler         return;
2825c350fdfSMatt Spinler     }
283*0387a74eSLakshmi Yadlapati     if (rc)
284*0387a74eSLakshmi Yadlapati     {
285*0387a74eSLakshmi Yadlapati         if (rc == PLDM_REQUESTER_NOT_RESP_MSG)
2865c350fdfSMatt Spinler         {
2875c350fdfSMatt Spinler             // Due to the MCTP loopback, we may get notified of the message
2885c350fdfSMatt Spinler             // we just sent.
2895c350fdfSMatt Spinler             return;
2905c350fdfSMatt Spinler         }
2915c350fdfSMatt Spinler 
2925c350fdfSMatt Spinler         auto e = errno;
293*0387a74eSLakshmi Yadlapati         lg2::error("pldm_transport_recv_msg failed, rc = {RC}, errno = {ERRNO}",
294*0387a74eSLakshmi Yadlapati                    "RC",
2951b41886dSMatt Spinler                    static_cast<std::underlying_type_t<pldm_requester_rc_t>>(rc),
2961b41886dSMatt Spinler                    "ERRNO", e);
2975c350fdfSMatt Spinler         status = ResponseStatus::failure;
2985c350fdfSMatt Spinler 
2995c350fdfSMatt Spinler         responseMsg = nullptr;
3005c350fdfSMatt Spinler     }
3015c350fdfSMatt Spinler 
3022843ba28SMatt Spinler     cleanupCmd();
3032843ba28SMatt Spinler 
3042843ba28SMatt Spinler     // Can't use this instance ID anymore.
30549bcbe92SPatrick Williams     freeIID();
3065c350fdfSMatt Spinler 
3075c350fdfSMatt Spinler     if (status == ResponseStatus::success)
3085c350fdfSMatt Spinler     {
3095c350fdfSMatt Spinler         uint8_t completionCode = 0;
3105c350fdfSMatt Spinler         auto response = reinterpret_cast<pldm_msg*>(responseMsg);
3115c350fdfSMatt Spinler 
312*0387a74eSLakshmi Yadlapati         auto decodeRC = decode_new_file_resp(response, responseSize,
3135c350fdfSMatt Spinler                                              &completionCode);
3145c350fdfSMatt Spinler         if (decodeRC < 0)
3155c350fdfSMatt Spinler         {
3161b41886dSMatt Spinler             lg2::error("decode_new_file_resp failed, rc = {RC}", "RC",
3171b41886dSMatt Spinler                        decodeRC);
3185c350fdfSMatt Spinler             status = ResponseStatus::failure;
3195c350fdfSMatt Spinler         }
3205c350fdfSMatt Spinler         else
3215c350fdfSMatt Spinler         {
3225c350fdfSMatt Spinler             if (completionCode != PLDM_SUCCESS)
3235c350fdfSMatt Spinler             {
3241b41886dSMatt Spinler                 lg2::error("Bad PLDM completion code {CODE}", "CODE",
3251b41886dSMatt Spinler                            completionCode);
3265c350fdfSMatt Spinler                 status = ResponseStatus::failure;
3275c350fdfSMatt Spinler             }
3285c350fdfSMatt Spinler         }
3295c350fdfSMatt Spinler     }
3305c350fdfSMatt Spinler 
331a44efe48SMatt Spinler     callResponseFunc(status);
3325c350fdfSMatt Spinler 
3335c350fdfSMatt Spinler     if (responseMsg)
3345c350fdfSMatt Spinler     {
3355c350fdfSMatt Spinler         free(responseMsg);
3365c350fdfSMatt Spinler     }
3375c350fdfSMatt Spinler }
3385c350fdfSMatt Spinler 
3395c350fdfSMatt Spinler void PLDMInterface::receiveTimerExpired()
3405c350fdfSMatt Spinler {
3411b41886dSMatt Spinler     lg2::error("Timed out waiting for PLDM response");
3422843ba28SMatt Spinler 
3432843ba28SMatt Spinler     // Cleanup, but keep the instance ID because the host didn't
3442843ba28SMatt Spinler     // respond so we can still use it.
3452843ba28SMatt Spinler     cleanupCmd();
3465c350fdfSMatt Spinler 
347a44efe48SMatt Spinler     callResponseFunc(ResponseStatus::failure);
3485c350fdfSMatt Spinler }
3495c350fdfSMatt Spinler 
3505c350fdfSMatt Spinler void PLDMInterface::cancelCmd()
3515c350fdfSMatt Spinler {
35249bcbe92SPatrick Williams     freeIID();
3532843ba28SMatt Spinler     cleanupCmd();
3542843ba28SMatt Spinler }
3552843ba28SMatt Spinler 
3562843ba28SMatt Spinler void PLDMInterface::cleanupCmd()
3572843ba28SMatt Spinler {
3585c350fdfSMatt Spinler     _inProgress = false;
3595c350fdfSMatt Spinler     _source.reset();
3605c350fdfSMatt Spinler 
3615c350fdfSMatt Spinler     if (_receiveTimer.isEnabled())
3625c350fdfSMatt Spinler     {
3635c350fdfSMatt Spinler         _receiveTimer.setEnabled(false);
3645c350fdfSMatt Spinler     }
3655c350fdfSMatt Spinler 
3665c350fdfSMatt Spinler     closeFD();
3675c350fdfSMatt Spinler }
3685c350fdfSMatt Spinler 
3695c350fdfSMatt Spinler } // namespace openpower::pels
370