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> 205c350fdfSMatt Spinler #include <unistd.h> 215c350fdfSMatt Spinler 221b41886dSMatt Spinler #include <phosphor-logging/lg2.hpp> 235c350fdfSMatt Spinler 242544b419SPatrick Williams #include <fstream> 252544b419SPatrick Williams 265c350fdfSMatt Spinler namespace openpower::pels 275c350fdfSMatt Spinler { 285c350fdfSMatt Spinler 295c350fdfSMatt Spinler using namespace sdeventplus; 305c350fdfSMatt Spinler using namespace sdeventplus::source; 315c350fdfSMatt Spinler 325c350fdfSMatt Spinler constexpr auto eidPath = "/usr/share/pldm/host_eid"; 335c350fdfSMatt Spinler constexpr mctp_eid_t defaultEIDValue = 9; 345c350fdfSMatt Spinler 355c350fdfSMatt Spinler constexpr uint16_t pelFileType = 0; 365c350fdfSMatt Spinler 375c350fdfSMatt Spinler PLDMInterface::~PLDMInterface() 385c350fdfSMatt Spinler { 39*49bcbe92SPatrick Williams freeIID(); 40*49bcbe92SPatrick Williams pldm_instance_db_destroy(_pldm_idb); 415c350fdfSMatt Spinler closeFD(); 425c350fdfSMatt Spinler } 435c350fdfSMatt Spinler 445c350fdfSMatt Spinler void PLDMInterface::closeFD() 455c350fdfSMatt Spinler { 465c350fdfSMatt Spinler if (_fd >= 0) 475c350fdfSMatt Spinler { 485c350fdfSMatt Spinler close(_fd); 495c350fdfSMatt Spinler _fd = -1; 505c350fdfSMatt Spinler } 515c350fdfSMatt Spinler } 525c350fdfSMatt Spinler 535c350fdfSMatt Spinler void PLDMInterface::readEID() 545c350fdfSMatt Spinler { 555c350fdfSMatt Spinler _eid = defaultEIDValue; 565c350fdfSMatt Spinler 575c350fdfSMatt Spinler std::ifstream eidFile{eidPath}; 585c350fdfSMatt Spinler if (!eidFile.good()) 595c350fdfSMatt Spinler { 601b41886dSMatt Spinler lg2::error("Could not open host EID file"); 615c350fdfSMatt Spinler } 625c350fdfSMatt Spinler else 635c350fdfSMatt Spinler { 645c350fdfSMatt Spinler std::string eid; 655c350fdfSMatt Spinler eidFile >> eid; 665c350fdfSMatt Spinler if (!eid.empty()) 675c350fdfSMatt Spinler { 685c350fdfSMatt Spinler _eid = atoi(eid.c_str()); 695c350fdfSMatt Spinler } 705c350fdfSMatt Spinler else 715c350fdfSMatt Spinler { 721b41886dSMatt Spinler lg2::error("EID file was empty"); 735c350fdfSMatt Spinler } 745c350fdfSMatt Spinler } 755c350fdfSMatt Spinler } 765c350fdfSMatt Spinler 775c350fdfSMatt Spinler void PLDMInterface::open() 785c350fdfSMatt Spinler { 795c350fdfSMatt Spinler _fd = pldm_open(); 805c350fdfSMatt Spinler if (_fd < 0) 815c350fdfSMatt Spinler { 825c350fdfSMatt Spinler auto e = errno; 831b41886dSMatt Spinler lg2::error("pldm_open failed. errno = {ERRNO}, rc = {RC}", "ERRNO", e, 841b41886dSMatt Spinler "RC", _fd); 85527ff346SMatt Spinler throw std::runtime_error{"pldm_open failed"}; 865c350fdfSMatt Spinler } 875c350fdfSMatt Spinler } 885c350fdfSMatt Spinler 892843ba28SMatt Spinler void PLDMInterface::startCommand() 905c350fdfSMatt Spinler { 915c350fdfSMatt Spinler try 925c350fdfSMatt Spinler { 935c350fdfSMatt Spinler closeFD(); 945c350fdfSMatt Spinler 955c350fdfSMatt Spinler open(); 965c350fdfSMatt Spinler 975c350fdfSMatt Spinler registerReceiveCallback(); 985c350fdfSMatt Spinler 992843ba28SMatt Spinler doSend(); 1002843ba28SMatt Spinler 1012843ba28SMatt Spinler _receiveTimer.restartOnce(_receiveTimeout); 1025c350fdfSMatt Spinler } 1035c350fdfSMatt Spinler catch (const std::exception& e) 1045c350fdfSMatt Spinler { 105527ff346SMatt Spinler lg2::error("startCommand exception: {ERROR}", "ERROR", e); 106527ff346SMatt Spinler 1072843ba28SMatt Spinler cleanupCmd(); 1085c350fdfSMatt Spinler 109527ff346SMatt Spinler throw; 1102843ba28SMatt Spinler } 1112843ba28SMatt Spinler } 1122843ba28SMatt Spinler 113*49bcbe92SPatrick Williams void PLDMInterface::allocIID() 1142843ba28SMatt Spinler { 115*49bcbe92SPatrick Williams if (_instanceID) 1162843ba28SMatt Spinler { 117*49bcbe92SPatrick Williams return; 1182843ba28SMatt Spinler } 119*49bcbe92SPatrick Williams 120*49bcbe92SPatrick Williams pldm_instance_id_t iid = 0; 121*49bcbe92SPatrick Williams auto rc = pldm_instance_id_alloc(_pldm_idb, _eid, &iid); 122*49bcbe92SPatrick Williams 123*49bcbe92SPatrick Williams if (rc == -EAGAIN) 124*49bcbe92SPatrick Williams { 125*49bcbe92SPatrick Williams throw std::runtime_error("No free instance ids"); 126*49bcbe92SPatrick Williams } 127*49bcbe92SPatrick Williams else if (rc) 128*49bcbe92SPatrick Williams { 129*49bcbe92SPatrick Williams throw std::system_category().default_error_condition(rc); 130*49bcbe92SPatrick Williams } 131*49bcbe92SPatrick Williams 132*49bcbe92SPatrick Williams _instanceID = iid; 133*49bcbe92SPatrick Williams } 134*49bcbe92SPatrick Williams 135*49bcbe92SPatrick Williams void PLDMInterface::freeIID() 136*49bcbe92SPatrick Williams { 137*49bcbe92SPatrick Williams if (!_instanceID) 138*49bcbe92SPatrick Williams { 139*49bcbe92SPatrick Williams return; 140*49bcbe92SPatrick Williams } 141*49bcbe92SPatrick Williams 142*49bcbe92SPatrick Williams auto rc = pldm_instance_id_free(_pldm_idb, _eid, *_instanceID); 143*49bcbe92SPatrick Williams 144*49bcbe92SPatrick Williams if (rc == -EINVAL) 145*49bcbe92SPatrick Williams { 146*49bcbe92SPatrick Williams throw std::runtime_error("Instance ID " + std::to_string(*_instanceID) + 147*49bcbe92SPatrick Williams " for TID " + std::to_string(_eid) + 148*49bcbe92SPatrick Williams " was not previously allocated"); 149*49bcbe92SPatrick Williams } 150*49bcbe92SPatrick Williams else if (rc) 151*49bcbe92SPatrick Williams { 152*49bcbe92SPatrick Williams throw std::system_category().default_error_condition(rc); 153*49bcbe92SPatrick Williams } 154*49bcbe92SPatrick Williams 155*49bcbe92SPatrick Williams _instanceID = std::nullopt; 1562843ba28SMatt Spinler } 1572843ba28SMatt Spinler 1582843ba28SMatt Spinler CmdStatus PLDMInterface::sendNewLogCmd(uint32_t id, uint32_t size) 1592843ba28SMatt Spinler { 1602843ba28SMatt Spinler _pelID = id; 1612843ba28SMatt Spinler _pelSize = size; 1622843ba28SMatt Spinler _inProgress = true; 1632843ba28SMatt Spinler 1642843ba28SMatt Spinler try 1652843ba28SMatt Spinler { 166*49bcbe92SPatrick Williams // Allocate the instance ID, as needed. 1672843ba28SMatt Spinler if (!_instanceID) 1682843ba28SMatt Spinler { 169*49bcbe92SPatrick Williams allocIID(); 1702843ba28SMatt Spinler } 1712843ba28SMatt Spinler startCommand(); 1722843ba28SMatt Spinler } 1732843ba28SMatt Spinler catch (const std::exception& e) 1742843ba28SMatt Spinler { 1755c350fdfSMatt Spinler _inProgress = false; 1765c350fdfSMatt Spinler return CmdStatus::failure; 1775c350fdfSMatt Spinler } 1785c350fdfSMatt Spinler 1795c350fdfSMatt Spinler return CmdStatus::success; 1805c350fdfSMatt Spinler } 1815c350fdfSMatt Spinler 1825c350fdfSMatt Spinler void PLDMInterface::registerReceiveCallback() 1835c350fdfSMatt Spinler { 1845c350fdfSMatt Spinler _source = std::make_unique<IO>( 1855c350fdfSMatt Spinler _event, _fd, EPOLLIN, 1865c350fdfSMatt Spinler std::bind(std::mem_fn(&PLDMInterface::receive), this, 1875c350fdfSMatt Spinler std::placeholders::_1, std::placeholders::_2, 1885c350fdfSMatt Spinler std::placeholders::_3)); 1895c350fdfSMatt Spinler } 1905c350fdfSMatt Spinler 1912843ba28SMatt Spinler void PLDMInterface::doSend() 1925c350fdfSMatt Spinler { 1935c350fdfSMatt Spinler std::array<uint8_t, sizeof(pldm_msg_hdr) + sizeof(pelFileType) + 1942843ba28SMatt Spinler sizeof(_pelID) + sizeof(uint64_t)> 1955c350fdfSMatt Spinler requestMsg; 1965c350fdfSMatt Spinler 1975c350fdfSMatt Spinler auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); 1985c350fdfSMatt Spinler 1992843ba28SMatt Spinler auto rc = encode_new_file_req(*_instanceID, pelFileType, _pelID, _pelSize, 2002843ba28SMatt Spinler request); 2015c350fdfSMatt Spinler if (rc != PLDM_SUCCESS) 2025c350fdfSMatt Spinler { 2031b41886dSMatt Spinler lg2::error("encode_new_file_req failed, rc = {RC}", "RC", rc); 204527ff346SMatt Spinler throw std::runtime_error{"encode_new_file_req failed"}; 2055c350fdfSMatt Spinler } 2065c350fdfSMatt Spinler 2075c350fdfSMatt Spinler rc = pldm_send(_eid, _fd, requestMsg.data(), requestMsg.size()); 2085c350fdfSMatt Spinler if (rc < 0) 2095c350fdfSMatt Spinler { 2105c350fdfSMatt Spinler auto e = errno; 2111b41886dSMatt Spinler lg2::error("pldm_send failed, rc = {RC}, errno = {ERRNO}", "RC", rc, 2121b41886dSMatt Spinler "ERRNO", e); 213527ff346SMatt Spinler throw std::runtime_error{"pldm_send failed"}; 2145c350fdfSMatt Spinler } 2155c350fdfSMatt Spinler } 2165c350fdfSMatt Spinler 217d26fa3e7SPatrick Williams void PLDMInterface::receive(IO& /*io*/, int fd, uint32_t revents) 2185c350fdfSMatt Spinler { 2195c350fdfSMatt Spinler if (!(revents & EPOLLIN)) 2205c350fdfSMatt Spinler { 2215c350fdfSMatt Spinler return; 2225c350fdfSMatt Spinler } 2235c350fdfSMatt Spinler 2245c350fdfSMatt Spinler uint8_t* responseMsg = nullptr; 2255c350fdfSMatt Spinler size_t responseSize = 0; 2265c350fdfSMatt Spinler ResponseStatus status = ResponseStatus::success; 2275c350fdfSMatt Spinler 2282843ba28SMatt Spinler auto rc = pldm_recv(_eid, fd, *_instanceID, &responseMsg, &responseSize); 2295c350fdfSMatt Spinler if (rc < 0) 2305c350fdfSMatt Spinler { 2315c350fdfSMatt Spinler if (rc == PLDM_REQUESTER_INSTANCE_ID_MISMATCH) 2325c350fdfSMatt Spinler { 2335c350fdfSMatt Spinler // We got a response to someone else's message. Ignore it. 2345c350fdfSMatt Spinler return; 2355c350fdfSMatt Spinler } 2365c350fdfSMatt Spinler else if (rc == PLDM_REQUESTER_NOT_RESP_MSG) 2375c350fdfSMatt Spinler { 2385c350fdfSMatt Spinler // Due to the MCTP loopback, we may get notified of the message 2395c350fdfSMatt Spinler // we just sent. 2405c350fdfSMatt Spinler return; 2415c350fdfSMatt Spinler } 2425c350fdfSMatt Spinler 2435c350fdfSMatt Spinler auto e = errno; 2441b41886dSMatt Spinler lg2::error("pldm_recv failed, rc = {RC}, errno = {ERRNO}", "RC", 2451b41886dSMatt Spinler static_cast<std::underlying_type_t<pldm_requester_rc_t>>(rc), 2461b41886dSMatt Spinler "ERRNO", e); 2475c350fdfSMatt Spinler status = ResponseStatus::failure; 2485c350fdfSMatt Spinler 2495c350fdfSMatt Spinler responseMsg = nullptr; 2505c350fdfSMatt Spinler } 2515c350fdfSMatt Spinler 2522843ba28SMatt Spinler cleanupCmd(); 2532843ba28SMatt Spinler 2542843ba28SMatt Spinler // Can't use this instance ID anymore. 255*49bcbe92SPatrick Williams freeIID(); 2565c350fdfSMatt Spinler 2575c350fdfSMatt Spinler if (status == ResponseStatus::success) 2585c350fdfSMatt Spinler { 2595c350fdfSMatt Spinler uint8_t completionCode = 0; 2605c350fdfSMatt Spinler auto response = reinterpret_cast<pldm_msg*>(responseMsg); 2615c350fdfSMatt Spinler 2625c350fdfSMatt Spinler auto decodeRC = decode_new_file_resp(response, PLDM_NEW_FILE_RESP_BYTES, 2635c350fdfSMatt Spinler &completionCode); 2645c350fdfSMatt Spinler if (decodeRC < 0) 2655c350fdfSMatt Spinler { 2661b41886dSMatt Spinler lg2::error("decode_new_file_resp failed, rc = {RC}", "RC", 2671b41886dSMatt Spinler decodeRC); 2685c350fdfSMatt Spinler status = ResponseStatus::failure; 2695c350fdfSMatt Spinler } 2705c350fdfSMatt Spinler else 2715c350fdfSMatt Spinler { 2725c350fdfSMatt Spinler if (completionCode != PLDM_SUCCESS) 2735c350fdfSMatt Spinler { 2741b41886dSMatt Spinler lg2::error("Bad PLDM completion code {CODE}", "CODE", 2751b41886dSMatt Spinler completionCode); 2765c350fdfSMatt Spinler status = ResponseStatus::failure; 2775c350fdfSMatt Spinler } 2785c350fdfSMatt Spinler } 2795c350fdfSMatt Spinler } 2805c350fdfSMatt Spinler 281a44efe48SMatt Spinler callResponseFunc(status); 2825c350fdfSMatt Spinler 2835c350fdfSMatt Spinler if (responseMsg) 2845c350fdfSMatt Spinler { 2855c350fdfSMatt Spinler free(responseMsg); 2865c350fdfSMatt Spinler } 2875c350fdfSMatt Spinler } 2885c350fdfSMatt Spinler 2895c350fdfSMatt Spinler void PLDMInterface::receiveTimerExpired() 2905c350fdfSMatt Spinler { 2911b41886dSMatt Spinler lg2::error("Timed out waiting for PLDM response"); 2922843ba28SMatt Spinler 2932843ba28SMatt Spinler // Cleanup, but keep the instance ID because the host didn't 2942843ba28SMatt Spinler // respond so we can still use it. 2952843ba28SMatt Spinler cleanupCmd(); 2965c350fdfSMatt Spinler 297a44efe48SMatt Spinler callResponseFunc(ResponseStatus::failure); 2985c350fdfSMatt Spinler } 2995c350fdfSMatt Spinler 3005c350fdfSMatt Spinler void PLDMInterface::cancelCmd() 3015c350fdfSMatt Spinler { 302*49bcbe92SPatrick Williams freeIID(); 3032843ba28SMatt Spinler cleanupCmd(); 3042843ba28SMatt Spinler } 3052843ba28SMatt Spinler 3062843ba28SMatt Spinler void PLDMInterface::cleanupCmd() 3072843ba28SMatt Spinler { 3085c350fdfSMatt Spinler _inProgress = false; 3095c350fdfSMatt Spinler _source.reset(); 3105c350fdfSMatt Spinler 3115c350fdfSMatt Spinler if (_receiveTimer.isEnabled()) 3125c350fdfSMatt Spinler { 3135c350fdfSMatt Spinler _receiveTimer.setEnabled(false); 3145c350fdfSMatt Spinler } 3155c350fdfSMatt Spinler 3165c350fdfSMatt Spinler closeFD(); 3175c350fdfSMatt Spinler } 3185c350fdfSMatt Spinler 3195c350fdfSMatt Spinler } // namespace openpower::pels 320