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> 19*7cc55b58SAndrew Jeffery #include <libpldm/oem/ibm/file_io.h> 202843ba28SMatt Spinler #include <systemd/sd-bus.h> 215c350fdfSMatt Spinler #include <unistd.h> 225c350fdfSMatt Spinler 231b41886dSMatt Spinler #include <phosphor-logging/lg2.hpp> 245c350fdfSMatt Spinler 252544b419SPatrick Williams #include <fstream> 262544b419SPatrick Williams 275c350fdfSMatt Spinler namespace openpower::pels 285c350fdfSMatt Spinler { 295c350fdfSMatt Spinler 302843ba28SMatt Spinler namespace service 312843ba28SMatt Spinler { 322843ba28SMatt Spinler constexpr auto pldm = "xyz.openbmc_project.PLDM"; 332843ba28SMatt Spinler } 342843ba28SMatt Spinler 352843ba28SMatt Spinler namespace object_path 362843ba28SMatt Spinler { 372843ba28SMatt Spinler constexpr auto pldm = "/xyz/openbmc_project/pldm"; 382843ba28SMatt Spinler } 392843ba28SMatt Spinler 402843ba28SMatt Spinler namespace interface 412843ba28SMatt Spinler { 422843ba28SMatt Spinler constexpr auto pldm_requester = "xyz.openbmc_project.PLDM.Requester"; 432843ba28SMatt Spinler } 442843ba28SMatt Spinler 455c350fdfSMatt Spinler using namespace sdeventplus; 465c350fdfSMatt Spinler using namespace sdeventplus::source; 475c350fdfSMatt Spinler 485c350fdfSMatt Spinler constexpr auto eidPath = "/usr/share/pldm/host_eid"; 495c350fdfSMatt Spinler constexpr mctp_eid_t defaultEIDValue = 9; 505c350fdfSMatt Spinler 515c350fdfSMatt Spinler constexpr uint16_t pelFileType = 0; 525c350fdfSMatt Spinler 535c350fdfSMatt Spinler PLDMInterface::~PLDMInterface() 545c350fdfSMatt Spinler { 552843ba28SMatt Spinler sd_bus_unref(_bus); 565c350fdfSMatt Spinler closeFD(); 575c350fdfSMatt Spinler } 585c350fdfSMatt Spinler 595c350fdfSMatt Spinler void PLDMInterface::closeFD() 605c350fdfSMatt Spinler { 615c350fdfSMatt Spinler if (_fd >= 0) 625c350fdfSMatt Spinler { 635c350fdfSMatt Spinler close(_fd); 645c350fdfSMatt Spinler _fd = -1; 655c350fdfSMatt Spinler } 665c350fdfSMatt Spinler } 675c350fdfSMatt Spinler 685c350fdfSMatt Spinler void PLDMInterface::readEID() 695c350fdfSMatt Spinler { 705c350fdfSMatt Spinler _eid = defaultEIDValue; 715c350fdfSMatt Spinler 725c350fdfSMatt Spinler std::ifstream eidFile{eidPath}; 735c350fdfSMatt Spinler if (!eidFile.good()) 745c350fdfSMatt Spinler { 751b41886dSMatt Spinler lg2::error("Could not open host EID file"); 765c350fdfSMatt Spinler } 775c350fdfSMatt Spinler else 785c350fdfSMatt Spinler { 795c350fdfSMatt Spinler std::string eid; 805c350fdfSMatt Spinler eidFile >> eid; 815c350fdfSMatt Spinler if (!eid.empty()) 825c350fdfSMatt Spinler { 835c350fdfSMatt Spinler _eid = atoi(eid.c_str()); 845c350fdfSMatt Spinler } 855c350fdfSMatt Spinler else 865c350fdfSMatt Spinler { 871b41886dSMatt Spinler lg2::error("EID file was empty"); 885c350fdfSMatt Spinler } 895c350fdfSMatt Spinler } 905c350fdfSMatt Spinler } 915c350fdfSMatt Spinler 925c350fdfSMatt Spinler void PLDMInterface::open() 935c350fdfSMatt Spinler { 945c350fdfSMatt Spinler _fd = pldm_open(); 955c350fdfSMatt Spinler if (_fd < 0) 965c350fdfSMatt Spinler { 975c350fdfSMatt Spinler auto e = errno; 981b41886dSMatt Spinler lg2::error("pldm_open failed. errno = {ERRNO}, rc = {RC}", "ERRNO", e, 991b41886dSMatt Spinler "RC", _fd); 100527ff346SMatt Spinler throw std::runtime_error{"pldm_open failed"}; 1015c350fdfSMatt Spinler } 1025c350fdfSMatt Spinler } 1035c350fdfSMatt Spinler 1042843ba28SMatt Spinler void PLDMInterface::instanceIDCallback(sd_bus_message* msg) 1052843ba28SMatt Spinler { 1062843ba28SMatt Spinler if (!_inProgress) 1072843ba28SMatt Spinler { 1081b41886dSMatt Spinler lg2::info("A command was canceled while waiting for the instance ID"); 1092843ba28SMatt Spinler return; 1102843ba28SMatt Spinler } 1112843ba28SMatt Spinler 1122843ba28SMatt Spinler bool failed = false; 1132843ba28SMatt Spinler 1142843ba28SMatt Spinler auto rc = sd_bus_message_get_errno(msg); 1152843ba28SMatt Spinler if (rc) 1162843ba28SMatt Spinler { 1171b41886dSMatt Spinler lg2::error("GetInstanceId D-Bus method failed, rc = {RC}", "RC", rc); 1182843ba28SMatt Spinler failed = true; 1192843ba28SMatt Spinler } 1202843ba28SMatt Spinler else 1212843ba28SMatt Spinler { 1222843ba28SMatt Spinler uint8_t id; 1232843ba28SMatt Spinler rc = sd_bus_message_read_basic(msg, 'y', &id); 1242843ba28SMatt Spinler if (rc < 0) 1252843ba28SMatt Spinler { 1261b41886dSMatt Spinler lg2::error("Could not read instance ID out of message, rc = {RC}", 1271b41886dSMatt Spinler "RC", rc); 1282843ba28SMatt Spinler failed = true; 1292843ba28SMatt Spinler } 1302843ba28SMatt Spinler else 1312843ba28SMatt Spinler { 1322843ba28SMatt Spinler _instanceID = id; 1332843ba28SMatt Spinler } 1342843ba28SMatt Spinler } 1352843ba28SMatt Spinler 1362843ba28SMatt Spinler if (failed) 1372843ba28SMatt Spinler { 1382843ba28SMatt Spinler _inProgress = false; 1392843ba28SMatt Spinler callResponseFunc(ResponseStatus::failure); 1402843ba28SMatt Spinler } 1412843ba28SMatt Spinler else 1422843ba28SMatt Spinler { 143527ff346SMatt Spinler try 144527ff346SMatt Spinler { 1452843ba28SMatt Spinler startCommand(); 1462843ba28SMatt Spinler } 147527ff346SMatt Spinler catch (const std::exception& e) 148527ff346SMatt Spinler { 149527ff346SMatt Spinler callResponseFunc(ResponseStatus::failure); 150527ff346SMatt Spinler } 151527ff346SMatt Spinler } 1522843ba28SMatt Spinler } 1532843ba28SMatt Spinler 154d26fa3e7SPatrick Williams int iidCallback(sd_bus_message* msg, void* data, sd_bus_error* /*err*/) 1552843ba28SMatt Spinler { 1562843ba28SMatt Spinler auto* interface = static_cast<PLDMInterface*>(data); 1572843ba28SMatt Spinler interface->instanceIDCallback(msg); 1582843ba28SMatt Spinler return 0; 1592843ba28SMatt Spinler } 1602843ba28SMatt Spinler 1612843ba28SMatt Spinler void PLDMInterface::startCommand() 1625c350fdfSMatt Spinler { 1635c350fdfSMatt Spinler try 1645c350fdfSMatt Spinler { 1655c350fdfSMatt Spinler closeFD(); 1665c350fdfSMatt Spinler 1675c350fdfSMatt Spinler open(); 1685c350fdfSMatt Spinler 1695c350fdfSMatt Spinler registerReceiveCallback(); 1705c350fdfSMatt Spinler 1712843ba28SMatt Spinler doSend(); 1722843ba28SMatt Spinler 1732843ba28SMatt Spinler _receiveTimer.restartOnce(_receiveTimeout); 1745c350fdfSMatt Spinler } 1755c350fdfSMatt Spinler catch (const std::exception& e) 1765c350fdfSMatt Spinler { 177527ff346SMatt Spinler lg2::error("startCommand exception: {ERROR}", "ERROR", e); 178527ff346SMatt Spinler 1792843ba28SMatt Spinler cleanupCmd(); 1805c350fdfSMatt Spinler 181527ff346SMatt Spinler throw; 1822843ba28SMatt Spinler } 1832843ba28SMatt Spinler } 1842843ba28SMatt Spinler 1852843ba28SMatt Spinler void PLDMInterface::startReadInstanceID() 1862843ba28SMatt Spinler { 1872843ba28SMatt Spinler auto rc = sd_bus_call_method_async( 1882843ba28SMatt Spinler _bus, NULL, service::pldm, object_path::pldm, interface::pldm_requester, 1892843ba28SMatt Spinler "GetInstanceId", iidCallback, this, "y", _eid); 1902843ba28SMatt Spinler 1912843ba28SMatt Spinler if (rc < 0) 1922843ba28SMatt Spinler { 1931b41886dSMatt Spinler lg2::error( 1941b41886dSMatt Spinler "Error calling sd_bus_call_method_async, rc = {RC}, msg = {MSG}", 1951b41886dSMatt Spinler "RC", rc, "MSG", strerror(-rc)); 196527ff346SMatt Spinler throw std::runtime_error{"sd_bus_call_method_async failed"}; 1972843ba28SMatt Spinler } 1982843ba28SMatt Spinler } 1992843ba28SMatt Spinler 2002843ba28SMatt Spinler CmdStatus PLDMInterface::sendNewLogCmd(uint32_t id, uint32_t size) 2012843ba28SMatt Spinler { 2022843ba28SMatt Spinler _pelID = id; 2032843ba28SMatt Spinler _pelSize = size; 2042843ba28SMatt Spinler _inProgress = true; 2052843ba28SMatt Spinler 2062843ba28SMatt Spinler try 2072843ba28SMatt Spinler { 2082843ba28SMatt Spinler // Kick off the async call to get the instance ID if 2092843ba28SMatt Spinler // necessary, otherwise start the command itself. 2102843ba28SMatt Spinler if (!_instanceID) 2112843ba28SMatt Spinler { 2122843ba28SMatt Spinler startReadInstanceID(); 2132843ba28SMatt Spinler } 2142843ba28SMatt Spinler else 2152843ba28SMatt Spinler { 2162843ba28SMatt Spinler startCommand(); 2172843ba28SMatt Spinler } 2182843ba28SMatt Spinler } 2192843ba28SMatt Spinler catch (const std::exception& e) 2202843ba28SMatt Spinler { 2215c350fdfSMatt Spinler _inProgress = false; 2225c350fdfSMatt Spinler return CmdStatus::failure; 2235c350fdfSMatt Spinler } 2245c350fdfSMatt Spinler 2255c350fdfSMatt Spinler return CmdStatus::success; 2265c350fdfSMatt Spinler } 2275c350fdfSMatt Spinler 2285c350fdfSMatt Spinler void PLDMInterface::registerReceiveCallback() 2295c350fdfSMatt Spinler { 2305c350fdfSMatt Spinler _source = std::make_unique<IO>( 2315c350fdfSMatt Spinler _event, _fd, EPOLLIN, 2325c350fdfSMatt Spinler std::bind(std::mem_fn(&PLDMInterface::receive), this, 2335c350fdfSMatt Spinler std::placeholders::_1, std::placeholders::_2, 2345c350fdfSMatt Spinler std::placeholders::_3)); 2355c350fdfSMatt Spinler } 2365c350fdfSMatt Spinler 2372843ba28SMatt Spinler void PLDMInterface::doSend() 2385c350fdfSMatt Spinler { 2395c350fdfSMatt Spinler std::array<uint8_t, sizeof(pldm_msg_hdr) + sizeof(pelFileType) + 2402843ba28SMatt Spinler sizeof(_pelID) + sizeof(uint64_t)> 2415c350fdfSMatt Spinler requestMsg; 2425c350fdfSMatt Spinler 2435c350fdfSMatt Spinler auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); 2445c350fdfSMatt Spinler 2452843ba28SMatt Spinler auto rc = encode_new_file_req(*_instanceID, pelFileType, _pelID, _pelSize, 2462843ba28SMatt Spinler request); 2475c350fdfSMatt Spinler if (rc != PLDM_SUCCESS) 2485c350fdfSMatt Spinler { 2491b41886dSMatt Spinler lg2::error("encode_new_file_req failed, rc = {RC}", "RC", rc); 250527ff346SMatt Spinler throw std::runtime_error{"encode_new_file_req failed"}; 2515c350fdfSMatt Spinler } 2525c350fdfSMatt Spinler 2535c350fdfSMatt Spinler rc = pldm_send(_eid, _fd, requestMsg.data(), requestMsg.size()); 2545c350fdfSMatt Spinler if (rc < 0) 2555c350fdfSMatt Spinler { 2565c350fdfSMatt Spinler auto e = errno; 2571b41886dSMatt Spinler lg2::error("pldm_send failed, rc = {RC}, errno = {ERRNO}", "RC", rc, 2581b41886dSMatt Spinler "ERRNO", e); 259527ff346SMatt Spinler throw std::runtime_error{"pldm_send failed"}; 2605c350fdfSMatt Spinler } 2615c350fdfSMatt Spinler } 2625c350fdfSMatt Spinler 263d26fa3e7SPatrick Williams void PLDMInterface::receive(IO& /*io*/, int fd, uint32_t revents) 2645c350fdfSMatt Spinler { 2655c350fdfSMatt Spinler if (!(revents & EPOLLIN)) 2665c350fdfSMatt Spinler { 2675c350fdfSMatt Spinler return; 2685c350fdfSMatt Spinler } 2695c350fdfSMatt Spinler 2705c350fdfSMatt Spinler uint8_t* responseMsg = nullptr; 2715c350fdfSMatt Spinler size_t responseSize = 0; 2725c350fdfSMatt Spinler ResponseStatus status = ResponseStatus::success; 2735c350fdfSMatt Spinler 2742843ba28SMatt Spinler auto rc = pldm_recv(_eid, fd, *_instanceID, &responseMsg, &responseSize); 2755c350fdfSMatt Spinler if (rc < 0) 2765c350fdfSMatt Spinler { 2775c350fdfSMatt Spinler if (rc == PLDM_REQUESTER_INSTANCE_ID_MISMATCH) 2785c350fdfSMatt Spinler { 2795c350fdfSMatt Spinler // We got a response to someone else's message. Ignore it. 2805c350fdfSMatt Spinler return; 2815c350fdfSMatt Spinler } 2825c350fdfSMatt Spinler else if (rc == PLDM_REQUESTER_NOT_RESP_MSG) 2835c350fdfSMatt Spinler { 2845c350fdfSMatt Spinler // Due to the MCTP loopback, we may get notified of the message 2855c350fdfSMatt Spinler // we just sent. 2865c350fdfSMatt Spinler return; 2875c350fdfSMatt Spinler } 2885c350fdfSMatt Spinler 2895c350fdfSMatt Spinler auto e = errno; 2901b41886dSMatt Spinler lg2::error("pldm_recv failed, rc = {RC}, errno = {ERRNO}", "RC", 2911b41886dSMatt Spinler static_cast<std::underlying_type_t<pldm_requester_rc_t>>(rc), 2921b41886dSMatt Spinler "ERRNO", e); 2935c350fdfSMatt Spinler status = ResponseStatus::failure; 2945c350fdfSMatt Spinler 2955c350fdfSMatt Spinler responseMsg = nullptr; 2965c350fdfSMatt Spinler } 2975c350fdfSMatt Spinler 2982843ba28SMatt Spinler cleanupCmd(); 2992843ba28SMatt Spinler 3002843ba28SMatt Spinler // Can't use this instance ID anymore. 3012843ba28SMatt Spinler _instanceID = std::nullopt; 3025c350fdfSMatt Spinler 3035c350fdfSMatt Spinler if (status == ResponseStatus::success) 3045c350fdfSMatt Spinler { 3055c350fdfSMatt Spinler uint8_t completionCode = 0; 3065c350fdfSMatt Spinler auto response = reinterpret_cast<pldm_msg*>(responseMsg); 3075c350fdfSMatt Spinler 3085c350fdfSMatt Spinler auto decodeRC = decode_new_file_resp(response, PLDM_NEW_FILE_RESP_BYTES, 3095c350fdfSMatt Spinler &completionCode); 3105c350fdfSMatt Spinler if (decodeRC < 0) 3115c350fdfSMatt Spinler { 3121b41886dSMatt Spinler lg2::error("decode_new_file_resp failed, rc = {RC}", "RC", 3131b41886dSMatt Spinler decodeRC); 3145c350fdfSMatt Spinler status = ResponseStatus::failure; 3155c350fdfSMatt Spinler } 3165c350fdfSMatt Spinler else 3175c350fdfSMatt Spinler { 3185c350fdfSMatt Spinler if (completionCode != PLDM_SUCCESS) 3195c350fdfSMatt Spinler { 3201b41886dSMatt Spinler lg2::error("Bad PLDM completion code {CODE}", "CODE", 3211b41886dSMatt Spinler completionCode); 3225c350fdfSMatt Spinler status = ResponseStatus::failure; 3235c350fdfSMatt Spinler } 3245c350fdfSMatt Spinler } 3255c350fdfSMatt Spinler } 3265c350fdfSMatt Spinler 327a44efe48SMatt Spinler callResponseFunc(status); 3285c350fdfSMatt Spinler 3295c350fdfSMatt Spinler if (responseMsg) 3305c350fdfSMatt Spinler { 3315c350fdfSMatt Spinler free(responseMsg); 3325c350fdfSMatt Spinler } 3335c350fdfSMatt Spinler } 3345c350fdfSMatt Spinler 3355c350fdfSMatt Spinler void PLDMInterface::receiveTimerExpired() 3365c350fdfSMatt Spinler { 3371b41886dSMatt Spinler lg2::error("Timed out waiting for PLDM response"); 3382843ba28SMatt Spinler 3392843ba28SMatt Spinler // Cleanup, but keep the instance ID because the host didn't 3402843ba28SMatt Spinler // respond so we can still use it. 3412843ba28SMatt Spinler cleanupCmd(); 3425c350fdfSMatt Spinler 343a44efe48SMatt Spinler callResponseFunc(ResponseStatus::failure); 3445c350fdfSMatt Spinler } 3455c350fdfSMatt Spinler 3465c350fdfSMatt Spinler void PLDMInterface::cancelCmd() 3475c350fdfSMatt Spinler { 3482843ba28SMatt Spinler _instanceID = std::nullopt; 3492843ba28SMatt Spinler cleanupCmd(); 3502843ba28SMatt Spinler } 3512843ba28SMatt Spinler 3522843ba28SMatt Spinler void PLDMInterface::cleanupCmd() 3532843ba28SMatt Spinler { 3545c350fdfSMatt Spinler _inProgress = false; 3555c350fdfSMatt Spinler _source.reset(); 3565c350fdfSMatt Spinler 3575c350fdfSMatt Spinler if (_receiveTimer.isEnabled()) 3585c350fdfSMatt Spinler { 3595c350fdfSMatt Spinler _receiveTimer.setEnabled(false); 3605c350fdfSMatt Spinler } 3615c350fdfSMatt Spinler 3625c350fdfSMatt Spinler closeFD(); 3635c350fdfSMatt Spinler } 3645c350fdfSMatt Spinler 3655c350fdfSMatt Spinler } // namespace openpower::pels 366