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> 195c350fdfSMatt Spinler #include <libpldm/file_io.h> 205c350fdfSMatt Spinler #include <unistd.h> 215c350fdfSMatt Spinler 225c350fdfSMatt Spinler #include <fstream> 235c350fdfSMatt Spinler #include <phosphor-logging/log.hpp> 245c350fdfSMatt Spinler 255c350fdfSMatt Spinler namespace openpower::pels 265c350fdfSMatt Spinler { 275c350fdfSMatt Spinler 285c350fdfSMatt Spinler using namespace phosphor::logging; 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 { 395c350fdfSMatt Spinler closeFD(); 405c350fdfSMatt Spinler } 415c350fdfSMatt Spinler 425c350fdfSMatt Spinler void PLDMInterface::closeFD() 435c350fdfSMatt Spinler { 445c350fdfSMatt Spinler if (_fd >= 0) 455c350fdfSMatt Spinler { 465c350fdfSMatt Spinler close(_fd); 475c350fdfSMatt Spinler _fd = -1; 485c350fdfSMatt Spinler } 495c350fdfSMatt Spinler } 505c350fdfSMatt Spinler 515c350fdfSMatt Spinler void PLDMInterface::readEID() 525c350fdfSMatt Spinler { 535c350fdfSMatt Spinler _eid = defaultEIDValue; 545c350fdfSMatt Spinler 555c350fdfSMatt Spinler std::ifstream eidFile{eidPath}; 565c350fdfSMatt Spinler if (!eidFile.good()) 575c350fdfSMatt Spinler { 585c350fdfSMatt Spinler log<level::ERR>("Could not open host EID file"); 595c350fdfSMatt Spinler } 605c350fdfSMatt Spinler else 615c350fdfSMatt Spinler { 625c350fdfSMatt Spinler std::string eid; 635c350fdfSMatt Spinler eidFile >> eid; 645c350fdfSMatt Spinler if (!eid.empty()) 655c350fdfSMatt Spinler { 665c350fdfSMatt Spinler _eid = atoi(eid.c_str()); 675c350fdfSMatt Spinler } 685c350fdfSMatt Spinler else 695c350fdfSMatt Spinler { 705c350fdfSMatt Spinler log<level::ERR>("EID file was empty"); 715c350fdfSMatt Spinler } 725c350fdfSMatt Spinler } 735c350fdfSMatt Spinler } 745c350fdfSMatt Spinler 755c350fdfSMatt Spinler void PLDMInterface::open() 765c350fdfSMatt Spinler { 775c350fdfSMatt Spinler _fd = pldm_open(); 785c350fdfSMatt Spinler if (_fd < 0) 795c350fdfSMatt Spinler { 805c350fdfSMatt Spinler auto e = errno; 815c350fdfSMatt Spinler log<level::ERR>("pldm_open failed", entry("ERRNO=%d", e), 825c350fdfSMatt Spinler entry("RC=%d\n", _fd)); 835c350fdfSMatt Spinler throw std::exception{}; 845c350fdfSMatt Spinler } 855c350fdfSMatt Spinler } 865c350fdfSMatt Spinler 875c350fdfSMatt Spinler CmdStatus PLDMInterface::sendNewLogCmd(uint32_t id, uint32_t size) 885c350fdfSMatt Spinler { 895c350fdfSMatt Spinler try 905c350fdfSMatt Spinler { 915c350fdfSMatt Spinler closeFD(); 925c350fdfSMatt Spinler 935c350fdfSMatt Spinler open(); 945c350fdfSMatt Spinler 955c350fdfSMatt Spinler readInstanceID(); 965c350fdfSMatt Spinler 975c350fdfSMatt Spinler registerReceiveCallback(); 985c350fdfSMatt Spinler 995c350fdfSMatt Spinler doSend(id, size); 1005c350fdfSMatt Spinler } 1015c350fdfSMatt Spinler catch (const std::exception& e) 1025c350fdfSMatt Spinler { 1035c350fdfSMatt Spinler closeFD(); 1045c350fdfSMatt Spinler 1055c350fdfSMatt Spinler _inProgress = false; 1065c350fdfSMatt Spinler _source.reset(); 1075c350fdfSMatt Spinler return CmdStatus::failure; 1085c350fdfSMatt Spinler } 1095c350fdfSMatt Spinler 1105c350fdfSMatt Spinler _inProgress = true; 1115c350fdfSMatt Spinler _receiveTimer.restartOnce(_receiveTimeout); 1125c350fdfSMatt Spinler return CmdStatus::success; 1135c350fdfSMatt Spinler } 1145c350fdfSMatt Spinler 1155c350fdfSMatt Spinler void PLDMInterface::registerReceiveCallback() 1165c350fdfSMatt Spinler { 1175c350fdfSMatt Spinler _source = std::make_unique<IO>( 1185c350fdfSMatt Spinler _event, _fd, EPOLLIN, 1195c350fdfSMatt Spinler std::bind(std::mem_fn(&PLDMInterface::receive), this, 1205c350fdfSMatt Spinler std::placeholders::_1, std::placeholders::_2, 1215c350fdfSMatt Spinler std::placeholders::_3)); 1225c350fdfSMatt Spinler } 1235c350fdfSMatt Spinler 1245c350fdfSMatt Spinler void PLDMInterface::readInstanceID() 1255c350fdfSMatt Spinler { 1265c350fdfSMatt Spinler try 1275c350fdfSMatt Spinler { 1285c350fdfSMatt Spinler _instanceID = _dataIface.getPLDMInstanceID(_eid); 1295c350fdfSMatt Spinler } 1305c350fdfSMatt Spinler catch (const std::exception& e) 1315c350fdfSMatt Spinler { 1325c350fdfSMatt Spinler log<level::ERR>( 1335c350fdfSMatt Spinler "Failed to get instance ID from PLDM Requester D-Bus daemon", 1345c350fdfSMatt Spinler entry("ERROR=%s", e.what())); 1355c350fdfSMatt Spinler throw; 1365c350fdfSMatt Spinler } 1375c350fdfSMatt Spinler } 1385c350fdfSMatt Spinler 1395c350fdfSMatt Spinler void PLDMInterface::doSend(uint32_t id, uint32_t size) 1405c350fdfSMatt Spinler { 1415c350fdfSMatt Spinler std::array<uint8_t, sizeof(pldm_msg_hdr) + sizeof(pelFileType) + 1429694ba62SDeepak Kodihalli sizeof(id) + sizeof(uint64_t)> 1435c350fdfSMatt Spinler requestMsg; 1445c350fdfSMatt Spinler 1455c350fdfSMatt Spinler auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); 1465c350fdfSMatt Spinler 1475c350fdfSMatt Spinler auto rc = encode_new_file_req(_instanceID, pelFileType, id, size, request); 1485c350fdfSMatt Spinler if (rc != PLDM_SUCCESS) 1495c350fdfSMatt Spinler { 1505c350fdfSMatt Spinler log<level::ERR>("encode_new_file_req failed", entry("RC=%d", rc)); 1515c350fdfSMatt Spinler throw std::exception{}; 1525c350fdfSMatt Spinler } 1535c350fdfSMatt Spinler 1545c350fdfSMatt Spinler rc = pldm_send(_eid, _fd, requestMsg.data(), requestMsg.size()); 1555c350fdfSMatt Spinler if (rc < 0) 1565c350fdfSMatt Spinler { 1575c350fdfSMatt Spinler auto e = errno; 1585c350fdfSMatt Spinler log<level::ERR>("pldm_send failed", entry("RC=%d", rc), 1595c350fdfSMatt Spinler entry("ERRNO=%d", e)); 1605c350fdfSMatt Spinler 1615c350fdfSMatt Spinler throw std::exception{}; 1625c350fdfSMatt Spinler } 1635c350fdfSMatt Spinler } 1645c350fdfSMatt Spinler 1655c350fdfSMatt Spinler void PLDMInterface::receive(IO& io, int fd, uint32_t revents) 1665c350fdfSMatt Spinler { 1675c350fdfSMatt Spinler if (!(revents & EPOLLIN)) 1685c350fdfSMatt Spinler { 1695c350fdfSMatt Spinler return; 1705c350fdfSMatt Spinler } 1715c350fdfSMatt Spinler 1725c350fdfSMatt Spinler uint8_t* responseMsg = nullptr; 1735c350fdfSMatt Spinler size_t responseSize = 0; 1745c350fdfSMatt Spinler ResponseStatus status = ResponseStatus::success; 1755c350fdfSMatt Spinler 1765c350fdfSMatt Spinler auto rc = pldm_recv(_eid, fd, _instanceID, &responseMsg, &responseSize); 1775c350fdfSMatt Spinler if (rc < 0) 1785c350fdfSMatt Spinler { 1795c350fdfSMatt Spinler if (rc == PLDM_REQUESTER_INSTANCE_ID_MISMATCH) 1805c350fdfSMatt Spinler { 1815c350fdfSMatt Spinler // We got a response to someone else's message. Ignore it. 1825c350fdfSMatt Spinler return; 1835c350fdfSMatt Spinler } 1845c350fdfSMatt Spinler else if (rc == PLDM_REQUESTER_NOT_RESP_MSG) 1855c350fdfSMatt Spinler { 1865c350fdfSMatt Spinler // Due to the MCTP loopback, we may get notified of the message 1875c350fdfSMatt Spinler // we just sent. 1885c350fdfSMatt Spinler return; 1895c350fdfSMatt Spinler } 1905c350fdfSMatt Spinler 1915c350fdfSMatt Spinler auto e = errno; 1925c350fdfSMatt Spinler log<level::ERR>("pldm_recv failed", entry("RC=%d", rc), 1935c350fdfSMatt Spinler entry("ERRNO=%d", e)); 1945c350fdfSMatt Spinler status = ResponseStatus::failure; 1955c350fdfSMatt Spinler 1965c350fdfSMatt Spinler responseMsg = nullptr; 1975c350fdfSMatt Spinler } 1985c350fdfSMatt Spinler 1995c350fdfSMatt Spinler _inProgress = false; 2005c350fdfSMatt Spinler _receiveTimer.setEnabled(false); 2015c350fdfSMatt Spinler closeFD(); 2025c350fdfSMatt Spinler _source.reset(); 2035c350fdfSMatt Spinler 2045c350fdfSMatt Spinler if (status == ResponseStatus::success) 2055c350fdfSMatt Spinler { 2065c350fdfSMatt Spinler uint8_t completionCode = 0; 2075c350fdfSMatt Spinler auto response = reinterpret_cast<pldm_msg*>(responseMsg); 2085c350fdfSMatt Spinler 2095c350fdfSMatt Spinler auto decodeRC = decode_new_file_resp(response, PLDM_NEW_FILE_RESP_BYTES, 2105c350fdfSMatt Spinler &completionCode); 2115c350fdfSMatt Spinler if (decodeRC < 0) 2125c350fdfSMatt Spinler { 2135c350fdfSMatt Spinler log<level::ERR>("decode_new_file_resp failed", 2145c350fdfSMatt Spinler entry("RC=%d", decodeRC)); 2155c350fdfSMatt Spinler status = ResponseStatus::failure; 2165c350fdfSMatt Spinler } 2175c350fdfSMatt Spinler else 2185c350fdfSMatt Spinler { 2195c350fdfSMatt Spinler if (completionCode != PLDM_SUCCESS) 2205c350fdfSMatt Spinler { 2215c350fdfSMatt Spinler log<level::ERR>("Bad PLDM completion code", 2225c350fdfSMatt Spinler entry("COMPLETION_CODE=%d", completionCode)); 2235c350fdfSMatt Spinler status = ResponseStatus::failure; 2245c350fdfSMatt Spinler } 2255c350fdfSMatt Spinler } 2265c350fdfSMatt Spinler } 2275c350fdfSMatt Spinler 228*a44efe48SMatt Spinler callResponseFunc(status); 2295c350fdfSMatt Spinler 2305c350fdfSMatt Spinler if (responseMsg) 2315c350fdfSMatt Spinler { 2325c350fdfSMatt Spinler free(responseMsg); 2335c350fdfSMatt Spinler } 2345c350fdfSMatt Spinler } 2355c350fdfSMatt Spinler 2365c350fdfSMatt Spinler void PLDMInterface::receiveTimerExpired() 2375c350fdfSMatt Spinler { 2385c350fdfSMatt Spinler log<level::ERR>("Timed out waiting for PLDM response"); 2395c350fdfSMatt Spinler cancelCmd(); 2405c350fdfSMatt Spinler 241*a44efe48SMatt Spinler callResponseFunc(ResponseStatus::failure); 2425c350fdfSMatt Spinler } 2435c350fdfSMatt Spinler 2445c350fdfSMatt Spinler void PLDMInterface::cancelCmd() 2455c350fdfSMatt Spinler { 2465c350fdfSMatt Spinler _inProgress = false; 2475c350fdfSMatt Spinler _source.reset(); 2485c350fdfSMatt Spinler 2495c350fdfSMatt Spinler if (_receiveTimer.isEnabled()) 2505c350fdfSMatt Spinler { 2515c350fdfSMatt Spinler _receiveTimer.setEnabled(false); 2525c350fdfSMatt Spinler } 2535c350fdfSMatt Spinler 2545c350fdfSMatt Spinler closeFD(); 2555c350fdfSMatt Spinler } 2565c350fdfSMatt Spinler 2575c350fdfSMatt Spinler } // namespace openpower::pels 258