xref: /openbmc/phosphor-logging/extensions/openpower-pels/pldm_interface.cpp (revision ec6f1411df5f8b052dbc469e693f6b038521cf50)
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>
200387a74eSLakshmi Yadlapati #include <libpldm/transport.h>
210387a74eSLakshmi Yadlapati #include <libpldm/transport/mctp-demux.h>
220387a74eSLakshmi 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;
340387a74eSLakshmi 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;
380387a74eSLakshmi Yadlapati constexpr TerminusID tid = defaultEIDValue;
395c350fdfSMatt Spinler 
405c350fdfSMatt Spinler constexpr uint16_t pelFileType = 0;
415c350fdfSMatt Spinler 
~PLDMInterface()425c350fdfSMatt Spinler PLDMInterface::~PLDMInterface()
435c350fdfSMatt Spinler {
4449bcbe92SPatrick Williams     freeIID();
4549bcbe92SPatrick Williams     pldm_instance_db_destroy(_pldm_idb);
465c350fdfSMatt Spinler     closeFD();
475c350fdfSMatt Spinler }
485c350fdfSMatt Spinler 
closeFD()495c350fdfSMatt Spinler void PLDMInterface::closeFD()
505c350fdfSMatt Spinler {
510387a74eSLakshmi Yadlapati     pldm_transport_mctp_demux_destroy(mctpDemux);
520387a74eSLakshmi Yadlapati     mctpDemux = nullptr;
535c350fdfSMatt Spinler     _fd = -1;
540387a74eSLakshmi Yadlapati     pldmTransport = nullptr;
555c350fdfSMatt Spinler }
565c350fdfSMatt Spinler 
readEID()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 
open()815c350fdfSMatt Spinler void PLDMInterface::open()
825c350fdfSMatt Spinler {
830387a74eSLakshmi Yadlapati     if (pldmTransport)
840387a74eSLakshmi Yadlapati     {
850387a74eSLakshmi Yadlapati         lg2::error("open: pldmTransport already setup!");
860387a74eSLakshmi Yadlapati         throw std::runtime_error{"open failed"};
870387a74eSLakshmi Yadlapati     }
880387a74eSLakshmi Yadlapati 
890387a74eSLakshmi Yadlapati     _fd = openMctpDemuxTransport();
905c350fdfSMatt Spinler     if (_fd < 0)
915c350fdfSMatt Spinler     {
925c350fdfSMatt Spinler         auto e = errno;
930387a74eSLakshmi Yadlapati         lg2::error("Transport open failed. errno = {ERRNO}, rc = {RC}", "ERRNO",
940387a74eSLakshmi Yadlapati                    e, "RC", _fd);
950387a74eSLakshmi Yadlapati         throw std::runtime_error{"Transport open failed"};
965c350fdfSMatt Spinler     }
975c350fdfSMatt Spinler }
985c350fdfSMatt Spinler 
openMctpDemuxTransport()990387a74eSLakshmi Yadlapati int PLDMInterface::openMctpDemuxTransport()
1000387a74eSLakshmi Yadlapati {
1010387a74eSLakshmi Yadlapati     int rc = pldm_transport_mctp_demux_init(&mctpDemux);
1020387a74eSLakshmi Yadlapati     if (rc)
1030387a74eSLakshmi Yadlapati     {
1040387a74eSLakshmi Yadlapati         lg2::error(
1050387a74eSLakshmi Yadlapati             "openMctpDemuxTransport: Failed to init MCTP demux transport. rc = {RC}",
1060387a74eSLakshmi Yadlapati             "RC", rc);
1070387a74eSLakshmi Yadlapati         return rc;
1080387a74eSLakshmi Yadlapati     }
1090387a74eSLakshmi Yadlapati 
1100387a74eSLakshmi Yadlapati     rc = pldm_transport_mctp_demux_map_tid(mctpDemux, tid, tid);
1110387a74eSLakshmi Yadlapati     if (rc)
1120387a74eSLakshmi Yadlapati     {
1130387a74eSLakshmi Yadlapati         lg2::error(
1140387a74eSLakshmi Yadlapati             "openMctpDemuxTransport: Failed to setup tid to eid mapping. rc = {RC}",
1150387a74eSLakshmi Yadlapati             "RC", rc);
1160387a74eSLakshmi Yadlapati         cleanupCmd();
1170387a74eSLakshmi Yadlapati         return rc;
1180387a74eSLakshmi Yadlapati     }
1190387a74eSLakshmi Yadlapati     pldmTransport = pldm_transport_mctp_demux_core(mctpDemux);
1200387a74eSLakshmi Yadlapati 
1210387a74eSLakshmi Yadlapati     struct pollfd pollfd;
1220387a74eSLakshmi Yadlapati     rc = pldm_transport_mctp_demux_init_pollfd(pldmTransport, &pollfd);
1230387a74eSLakshmi Yadlapati     if (rc)
1240387a74eSLakshmi Yadlapati     {
1250387a74eSLakshmi Yadlapati         lg2::error("openMctpDemuxTransport: Failed to get pollfd. rc = {RC}",
1260387a74eSLakshmi Yadlapati                    "RC", rc);
1270387a74eSLakshmi Yadlapati         cleanupCmd();
1280387a74eSLakshmi Yadlapati         return rc;
1290387a74eSLakshmi Yadlapati     }
1300387a74eSLakshmi Yadlapati     return pollfd.fd;
1310387a74eSLakshmi Yadlapati }
1320387a74eSLakshmi Yadlapati 
startCommand()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 
allocIID()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 
freeIID()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     {
190075c7923SPatrick Williams         throw std::runtime_error(
191075c7923SPatrick Williams             "Instance ID " + std::to_string(*_instanceID) + " for TID " +
192075c7923SPatrick Williams             std::to_string(_eid) + " 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 
sendNewLogCmd(uint32_t id,uint32_t size)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 
registerReceiveCallback()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,
2320387a74eSLakshmi Yadlapati                   std::placeholders::_3, pldmTransport));
2335c350fdfSMatt Spinler }
2345c350fdfSMatt Spinler 
doSend()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     }
2500387a74eSLakshmi Yadlapati     pldm_tid_t pldmTID = static_cast<pldm_tid_t>(_eid);
2510387a74eSLakshmi Yadlapati     rc = pldm_transport_send_msg(pldmTransport, pldmTID, requestMsg.data(),
2520387a74eSLakshmi Yadlapati                                  requestMsg.size());
2535c350fdfSMatt Spinler     if (rc < 0)
2545c350fdfSMatt Spinler     {
2555c350fdfSMatt Spinler         auto e = errno;
2560387a74eSLakshmi Yadlapati         lg2::error("pldm_transport_send_msg failed, rc = {RC}, errno = {ERRNO}",
2570387a74eSLakshmi Yadlapati                    "RC", rc, "ERRNO", e);
2580387a74eSLakshmi Yadlapati         throw std::runtime_error{"pldm_transport_send_msg failed"};
2595c350fdfSMatt Spinler     }
2605c350fdfSMatt Spinler }
2615c350fdfSMatt Spinler 
receive(IO &,int,uint32_t revents,pldm_transport * transport)2620387a74eSLakshmi Yadlapati void PLDMInterface::receive(IO& /*io*/, int /*fd*/, uint32_t revents,
2630387a74eSLakshmi Yadlapati                             pldm_transport* transport)
2640387a74eSLakshmi Yadlapati 
2655c350fdfSMatt Spinler {
2665c350fdfSMatt Spinler     if (!(revents & EPOLLIN))
2675c350fdfSMatt Spinler     {
2685c350fdfSMatt Spinler         return;
2695c350fdfSMatt Spinler     }
2705c350fdfSMatt Spinler 
2710387a74eSLakshmi Yadlapati     void* responseMsg = nullptr;
2725c350fdfSMatt Spinler     size_t responseSize = 0;
2735c350fdfSMatt Spinler     ResponseStatus status = ResponseStatus::success;
2745c350fdfSMatt Spinler 
2750387a74eSLakshmi Yadlapati     pldm_tid_t pldmTID;
2760387a74eSLakshmi Yadlapati     auto rc = pldm_transport_recv_msg(transport, &pldmTID, &responseMsg,
2770387a74eSLakshmi Yadlapati                                       &responseSize);
278*ec6f1411SEddie James     struct pldm_msg_hdr* hdr = (struct pldm_msg_hdr*)responseMsg;
2790387a74eSLakshmi Yadlapati     if (pldmTID != _eid)
2805c350fdfSMatt Spinler     {
2815c350fdfSMatt Spinler         // We got a response to someone else's message. Ignore it.
2825c350fdfSMatt Spinler         return;
2835c350fdfSMatt Spinler     }
2840387a74eSLakshmi Yadlapati     if (rc)
2850387a74eSLakshmi Yadlapati     {
2860387a74eSLakshmi Yadlapati         if (rc == PLDM_REQUESTER_NOT_RESP_MSG)
2875c350fdfSMatt Spinler         {
2885c350fdfSMatt Spinler             // Due to the MCTP loopback, we may get notified of the message
2895c350fdfSMatt Spinler             // we just sent.
2905c350fdfSMatt Spinler             return;
2915c350fdfSMatt Spinler         }
2925c350fdfSMatt Spinler 
2935c350fdfSMatt Spinler         auto e = errno;
2940387a74eSLakshmi Yadlapati         lg2::error("pldm_transport_recv_msg failed, rc = {RC}, errno = {ERRNO}",
2950387a74eSLakshmi Yadlapati                    "RC",
2961b41886dSMatt Spinler                    static_cast<std::underlying_type_t<pldm_requester_rc_t>>(rc),
2971b41886dSMatt Spinler                    "ERRNO", e);
2985c350fdfSMatt Spinler         status = ResponseStatus::failure;
2995c350fdfSMatt Spinler 
3005c350fdfSMatt Spinler         responseMsg = nullptr;
3015c350fdfSMatt Spinler     }
302*ec6f1411SEddie James     if (hdr && (hdr->request || hdr->datagram))
303*ec6f1411SEddie James     {
304*ec6f1411SEddie James         free(responseMsg);
305*ec6f1411SEddie James         return;
306*ec6f1411SEddie James     }
3075c350fdfSMatt Spinler 
3082843ba28SMatt Spinler     cleanupCmd();
3092843ba28SMatt Spinler 
3102843ba28SMatt Spinler     // Can't use this instance ID anymore.
31149bcbe92SPatrick Williams     freeIID();
3125c350fdfSMatt Spinler 
3135c350fdfSMatt Spinler     if (status == ResponseStatus::success)
3145c350fdfSMatt Spinler     {
3155c350fdfSMatt Spinler         uint8_t completionCode = 0;
3165c350fdfSMatt Spinler         auto response = reinterpret_cast<pldm_msg*>(responseMsg);
3175c350fdfSMatt Spinler 
318075c7923SPatrick Williams         auto decodeRC =
319075c7923SPatrick Williams             decode_new_file_resp(response, responseSize, &completionCode);
3205c350fdfSMatt Spinler         if (decodeRC < 0)
3215c350fdfSMatt Spinler         {
3221b41886dSMatt Spinler             lg2::error("decode_new_file_resp failed, rc = {RC}", "RC",
3231b41886dSMatt Spinler                        decodeRC);
3245c350fdfSMatt Spinler             status = ResponseStatus::failure;
3255c350fdfSMatt Spinler         }
3265c350fdfSMatt Spinler         else
3275c350fdfSMatt Spinler         {
3285c350fdfSMatt Spinler             if (completionCode != PLDM_SUCCESS)
3295c350fdfSMatt Spinler             {
3301b41886dSMatt Spinler                 lg2::error("Bad PLDM completion code {CODE}", "CODE",
3311b41886dSMatt Spinler                            completionCode);
3325c350fdfSMatt Spinler                 status = ResponseStatus::failure;
3335c350fdfSMatt Spinler             }
3345c350fdfSMatt Spinler         }
3355c350fdfSMatt Spinler     }
3365c350fdfSMatt Spinler 
337a44efe48SMatt Spinler     callResponseFunc(status);
3385c350fdfSMatt Spinler 
3395c350fdfSMatt Spinler     if (responseMsg)
3405c350fdfSMatt Spinler     {
3415c350fdfSMatt Spinler         free(responseMsg);
3425c350fdfSMatt Spinler     }
3435c350fdfSMatt Spinler }
3445c350fdfSMatt Spinler 
receiveTimerExpired()3455c350fdfSMatt Spinler void PLDMInterface::receiveTimerExpired()
3465c350fdfSMatt Spinler {
3471b41886dSMatt Spinler     lg2::error("Timed out waiting for PLDM response");
3482843ba28SMatt Spinler 
3492843ba28SMatt Spinler     // Cleanup, but keep the instance ID because the host didn't
3502843ba28SMatt Spinler     // respond so we can still use it.
3512843ba28SMatt Spinler     cleanupCmd();
3525c350fdfSMatt Spinler 
353a44efe48SMatt Spinler     callResponseFunc(ResponseStatus::failure);
3545c350fdfSMatt Spinler }
3555c350fdfSMatt Spinler 
cancelCmd()3565c350fdfSMatt Spinler void PLDMInterface::cancelCmd()
3575c350fdfSMatt Spinler {
35849bcbe92SPatrick Williams     freeIID();
3592843ba28SMatt Spinler     cleanupCmd();
3602843ba28SMatt Spinler }
3612843ba28SMatt Spinler 
cleanupCmd()3622843ba28SMatt Spinler void PLDMInterface::cleanupCmd()
3632843ba28SMatt Spinler {
3645c350fdfSMatt Spinler     _inProgress = false;
3655c350fdfSMatt Spinler     _source.reset();
3665c350fdfSMatt Spinler 
3675c350fdfSMatt Spinler     if (_receiveTimer.isEnabled())
3685c350fdfSMatt Spinler     {
3695c350fdfSMatt Spinler         _receiveTimer.setEnabled(false);
3705c350fdfSMatt Spinler     }
3715c350fdfSMatt Spinler 
3725c350fdfSMatt Spinler     closeFD();
3735c350fdfSMatt Spinler }
3745c350fdfSMatt Spinler 
3755c350fdfSMatt Spinler } // namespace openpower::pels
376