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 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 { 510387a74eSLakshmi Yadlapati pldm_transport_mctp_demux_destroy(mctpDemux); 520387a74eSLakshmi Yadlapati mctpDemux = nullptr; 535c350fdfSMatt Spinler _fd = -1; 540387a74eSLakshmi 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 { 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 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 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 { 190*075c7923SPatrick Williams throw std::runtime_error( 191*075c7923SPatrick Williams "Instance ID " + std::to_string(*_instanceID) + " for TID " + 192*075c7923SPatrick 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 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, 2320387a74eSLakshmi 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 } 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 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); 2780387a74eSLakshmi Yadlapati if (pldmTID != _eid) 2795c350fdfSMatt Spinler { 2805c350fdfSMatt Spinler // We got a response to someone else's message. Ignore it. 2815c350fdfSMatt Spinler return; 2825c350fdfSMatt Spinler } 2830387a74eSLakshmi Yadlapati if (rc) 2840387a74eSLakshmi Yadlapati { 2850387a74eSLakshmi 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; 2930387a74eSLakshmi Yadlapati lg2::error("pldm_transport_recv_msg failed, rc = {RC}, errno = {ERRNO}", 2940387a74eSLakshmi 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*075c7923SPatrick Williams auto decodeRC = 313*075c7923SPatrick Williams decode_new_file_resp(response, responseSize, &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