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