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> 20*2843ba28SMatt Spinler #include <systemd/sd-bus.h> 215c350fdfSMatt Spinler #include <unistd.h> 225c350fdfSMatt Spinler 235c350fdfSMatt Spinler #include <fstream> 245c350fdfSMatt Spinler #include <phosphor-logging/log.hpp> 255c350fdfSMatt Spinler 265c350fdfSMatt Spinler namespace openpower::pels 275c350fdfSMatt Spinler { 285c350fdfSMatt Spinler 29*2843ba28SMatt Spinler namespace service 30*2843ba28SMatt Spinler { 31*2843ba28SMatt Spinler constexpr auto pldm = "xyz.openbmc_project.PLDM"; 32*2843ba28SMatt Spinler } 33*2843ba28SMatt Spinler 34*2843ba28SMatt Spinler namespace object_path 35*2843ba28SMatt Spinler { 36*2843ba28SMatt Spinler constexpr auto pldm = "/xyz/openbmc_project/pldm"; 37*2843ba28SMatt Spinler } 38*2843ba28SMatt Spinler 39*2843ba28SMatt Spinler namespace interface 40*2843ba28SMatt Spinler { 41*2843ba28SMatt Spinler constexpr auto pldm_requester = "xyz.openbmc_project.PLDM.Requester"; 42*2843ba28SMatt Spinler } 43*2843ba28SMatt Spinler 445c350fdfSMatt Spinler using namespace phosphor::logging; 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 { 55*2843ba28SMatt 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 { 755c350fdfSMatt Spinler log<level::ERR>("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 { 875c350fdfSMatt Spinler log<level::ERR>("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; 985c350fdfSMatt Spinler log<level::ERR>("pldm_open failed", entry("ERRNO=%d", e), 995c350fdfSMatt Spinler entry("RC=%d\n", _fd)); 1005c350fdfSMatt Spinler throw std::exception{}; 1015c350fdfSMatt Spinler } 1025c350fdfSMatt Spinler } 1035c350fdfSMatt Spinler 104*2843ba28SMatt Spinler void PLDMInterface::instanceIDCallback(sd_bus_message* msg) 105*2843ba28SMatt Spinler { 106*2843ba28SMatt Spinler if (!_inProgress) 107*2843ba28SMatt Spinler { 108*2843ba28SMatt Spinler // A cancelCmd was run, just return 109*2843ba28SMatt Spinler log<level::INFO>( 110*2843ba28SMatt Spinler "A command was canceled while waiting for the instance ID"); 111*2843ba28SMatt Spinler return; 112*2843ba28SMatt Spinler } 113*2843ba28SMatt Spinler 114*2843ba28SMatt Spinler bool failed = false; 115*2843ba28SMatt Spinler 116*2843ba28SMatt Spinler auto rc = sd_bus_message_get_errno(msg); 117*2843ba28SMatt Spinler if (rc) 118*2843ba28SMatt Spinler { 119*2843ba28SMatt Spinler log<level::ERR>("GetInstanceId D-Bus method failed", 120*2843ba28SMatt Spinler entry("ERRNO=%d", rc)); 121*2843ba28SMatt Spinler failed = true; 122*2843ba28SMatt Spinler } 123*2843ba28SMatt Spinler else 124*2843ba28SMatt Spinler { 125*2843ba28SMatt Spinler uint8_t id; 126*2843ba28SMatt Spinler rc = sd_bus_message_read_basic(msg, 'y', &id); 127*2843ba28SMatt Spinler if (rc < 0) 128*2843ba28SMatt Spinler { 129*2843ba28SMatt Spinler log<level::ERR>("Could not read instance ID out of message", 130*2843ba28SMatt Spinler entry("ERROR=%d", rc)); 131*2843ba28SMatt Spinler failed = true; 132*2843ba28SMatt Spinler } 133*2843ba28SMatt Spinler else 134*2843ba28SMatt Spinler { 135*2843ba28SMatt Spinler _instanceID = id; 136*2843ba28SMatt Spinler } 137*2843ba28SMatt Spinler } 138*2843ba28SMatt Spinler 139*2843ba28SMatt Spinler if (failed) 140*2843ba28SMatt Spinler { 141*2843ba28SMatt Spinler _inProgress = false; 142*2843ba28SMatt Spinler callResponseFunc(ResponseStatus::failure); 143*2843ba28SMatt Spinler } 144*2843ba28SMatt Spinler else 145*2843ba28SMatt Spinler { 146*2843ba28SMatt Spinler startCommand(); 147*2843ba28SMatt Spinler } 148*2843ba28SMatt Spinler } 149*2843ba28SMatt Spinler 150*2843ba28SMatt Spinler int iidCallback(sd_bus_message* msg, void* data, sd_bus_error* err) 151*2843ba28SMatt Spinler { 152*2843ba28SMatt Spinler auto* interface = static_cast<PLDMInterface*>(data); 153*2843ba28SMatt Spinler interface->instanceIDCallback(msg); 154*2843ba28SMatt Spinler return 0; 155*2843ba28SMatt Spinler } 156*2843ba28SMatt Spinler 157*2843ba28SMatt Spinler void PLDMInterface::startCommand() 1585c350fdfSMatt Spinler { 1595c350fdfSMatt Spinler try 1605c350fdfSMatt Spinler { 1615c350fdfSMatt Spinler closeFD(); 1625c350fdfSMatt Spinler 1635c350fdfSMatt Spinler open(); 1645c350fdfSMatt Spinler 1655c350fdfSMatt Spinler registerReceiveCallback(); 1665c350fdfSMatt Spinler 167*2843ba28SMatt Spinler doSend(); 168*2843ba28SMatt Spinler 169*2843ba28SMatt Spinler _receiveTimer.restartOnce(_receiveTimeout); 1705c350fdfSMatt Spinler } 1715c350fdfSMatt Spinler catch (const std::exception& e) 1725c350fdfSMatt Spinler { 173*2843ba28SMatt Spinler cleanupCmd(); 1745c350fdfSMatt Spinler 175*2843ba28SMatt Spinler callResponseFunc(ResponseStatus::failure); 176*2843ba28SMatt Spinler } 177*2843ba28SMatt Spinler } 178*2843ba28SMatt Spinler 179*2843ba28SMatt Spinler void PLDMInterface::startReadInstanceID() 180*2843ba28SMatt Spinler { 181*2843ba28SMatt Spinler auto rc = sd_bus_call_method_async( 182*2843ba28SMatt Spinler _bus, NULL, service::pldm, object_path::pldm, interface::pldm_requester, 183*2843ba28SMatt Spinler "GetInstanceId", iidCallback, this, "y", _eid); 184*2843ba28SMatt Spinler 185*2843ba28SMatt Spinler if (rc < 0) 186*2843ba28SMatt Spinler { 187*2843ba28SMatt Spinler log<level::ERR>("Error calling sd_bus_call_method_async", 188*2843ba28SMatt Spinler entry("RC=%d", rc), entry("MSG=%s", strerror(-rc))); 189*2843ba28SMatt Spinler throw std::exception{}; 190*2843ba28SMatt Spinler } 191*2843ba28SMatt Spinler } 192*2843ba28SMatt Spinler 193*2843ba28SMatt Spinler CmdStatus PLDMInterface::sendNewLogCmd(uint32_t id, uint32_t size) 194*2843ba28SMatt Spinler { 195*2843ba28SMatt Spinler _pelID = id; 196*2843ba28SMatt Spinler _pelSize = size; 197*2843ba28SMatt Spinler _inProgress = true; 198*2843ba28SMatt Spinler 199*2843ba28SMatt Spinler try 200*2843ba28SMatt Spinler { 201*2843ba28SMatt Spinler // Kick off the async call to get the instance ID if 202*2843ba28SMatt Spinler // necessary, otherwise start the command itself. 203*2843ba28SMatt Spinler if (!_instanceID) 204*2843ba28SMatt Spinler { 205*2843ba28SMatt Spinler startReadInstanceID(); 206*2843ba28SMatt Spinler } 207*2843ba28SMatt Spinler else 208*2843ba28SMatt Spinler { 209*2843ba28SMatt Spinler startCommand(); 210*2843ba28SMatt Spinler } 211*2843ba28SMatt Spinler } 212*2843ba28SMatt Spinler catch (const std::exception& e) 213*2843ba28SMatt Spinler { 2145c350fdfSMatt Spinler _inProgress = false; 2155c350fdfSMatt Spinler return CmdStatus::failure; 2165c350fdfSMatt Spinler } 2175c350fdfSMatt Spinler 2185c350fdfSMatt Spinler return CmdStatus::success; 2195c350fdfSMatt Spinler } 2205c350fdfSMatt Spinler 2215c350fdfSMatt Spinler void PLDMInterface::registerReceiveCallback() 2225c350fdfSMatt Spinler { 2235c350fdfSMatt Spinler _source = std::make_unique<IO>( 2245c350fdfSMatt Spinler _event, _fd, EPOLLIN, 2255c350fdfSMatt Spinler std::bind(std::mem_fn(&PLDMInterface::receive), this, 2265c350fdfSMatt Spinler std::placeholders::_1, std::placeholders::_2, 2275c350fdfSMatt Spinler std::placeholders::_3)); 2285c350fdfSMatt Spinler } 2295c350fdfSMatt Spinler 230*2843ba28SMatt Spinler void PLDMInterface::doSend() 2315c350fdfSMatt Spinler { 2325c350fdfSMatt Spinler std::array<uint8_t, sizeof(pldm_msg_hdr) + sizeof(pelFileType) + 233*2843ba28SMatt Spinler sizeof(_pelID) + sizeof(uint64_t)> 2345c350fdfSMatt Spinler requestMsg; 2355c350fdfSMatt Spinler 2365c350fdfSMatt Spinler auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); 2375c350fdfSMatt Spinler 238*2843ba28SMatt Spinler auto rc = encode_new_file_req(*_instanceID, pelFileType, _pelID, _pelSize, 239*2843ba28SMatt Spinler request); 2405c350fdfSMatt Spinler if (rc != PLDM_SUCCESS) 2415c350fdfSMatt Spinler { 2425c350fdfSMatt Spinler log<level::ERR>("encode_new_file_req failed", entry("RC=%d", rc)); 2435c350fdfSMatt Spinler throw std::exception{}; 2445c350fdfSMatt Spinler } 2455c350fdfSMatt Spinler 2465c350fdfSMatt Spinler rc = pldm_send(_eid, _fd, requestMsg.data(), requestMsg.size()); 2475c350fdfSMatt Spinler if (rc < 0) 2485c350fdfSMatt Spinler { 2495c350fdfSMatt Spinler auto e = errno; 2505c350fdfSMatt Spinler log<level::ERR>("pldm_send failed", entry("RC=%d", rc), 2515c350fdfSMatt Spinler entry("ERRNO=%d", e)); 2525c350fdfSMatt Spinler 2535c350fdfSMatt Spinler throw std::exception{}; 2545c350fdfSMatt Spinler } 2555c350fdfSMatt Spinler } 2565c350fdfSMatt Spinler 2575c350fdfSMatt Spinler void PLDMInterface::receive(IO& io, int fd, uint32_t revents) 2585c350fdfSMatt Spinler { 2595c350fdfSMatt Spinler if (!(revents & EPOLLIN)) 2605c350fdfSMatt Spinler { 2615c350fdfSMatt Spinler return; 2625c350fdfSMatt Spinler } 2635c350fdfSMatt Spinler 2645c350fdfSMatt Spinler uint8_t* responseMsg = nullptr; 2655c350fdfSMatt Spinler size_t responseSize = 0; 2665c350fdfSMatt Spinler ResponseStatus status = ResponseStatus::success; 2675c350fdfSMatt Spinler 268*2843ba28SMatt Spinler auto rc = pldm_recv(_eid, fd, *_instanceID, &responseMsg, &responseSize); 2695c350fdfSMatt Spinler if (rc < 0) 2705c350fdfSMatt Spinler { 2715c350fdfSMatt Spinler if (rc == PLDM_REQUESTER_INSTANCE_ID_MISMATCH) 2725c350fdfSMatt Spinler { 2735c350fdfSMatt Spinler // We got a response to someone else's message. Ignore it. 2745c350fdfSMatt Spinler return; 2755c350fdfSMatt Spinler } 2765c350fdfSMatt Spinler else if (rc == PLDM_REQUESTER_NOT_RESP_MSG) 2775c350fdfSMatt Spinler { 2785c350fdfSMatt Spinler // Due to the MCTP loopback, we may get notified of the message 2795c350fdfSMatt Spinler // we just sent. 2805c350fdfSMatt Spinler return; 2815c350fdfSMatt Spinler } 2825c350fdfSMatt Spinler 2835c350fdfSMatt Spinler auto e = errno; 2845c350fdfSMatt Spinler log<level::ERR>("pldm_recv failed", entry("RC=%d", rc), 2855c350fdfSMatt Spinler entry("ERRNO=%d", e)); 2865c350fdfSMatt Spinler status = ResponseStatus::failure; 2875c350fdfSMatt Spinler 2885c350fdfSMatt Spinler responseMsg = nullptr; 2895c350fdfSMatt Spinler } 2905c350fdfSMatt Spinler 291*2843ba28SMatt Spinler cleanupCmd(); 292*2843ba28SMatt Spinler 293*2843ba28SMatt Spinler // Can't use this instance ID anymore. 294*2843ba28SMatt Spinler _instanceID = std::nullopt; 2955c350fdfSMatt Spinler 2965c350fdfSMatt Spinler if (status == ResponseStatus::success) 2975c350fdfSMatt Spinler { 2985c350fdfSMatt Spinler uint8_t completionCode = 0; 2995c350fdfSMatt Spinler auto response = reinterpret_cast<pldm_msg*>(responseMsg); 3005c350fdfSMatt Spinler 3015c350fdfSMatt Spinler auto decodeRC = decode_new_file_resp(response, PLDM_NEW_FILE_RESP_BYTES, 3025c350fdfSMatt Spinler &completionCode); 3035c350fdfSMatt Spinler if (decodeRC < 0) 3045c350fdfSMatt Spinler { 3055c350fdfSMatt Spinler log<level::ERR>("decode_new_file_resp failed", 3065c350fdfSMatt Spinler entry("RC=%d", decodeRC)); 3075c350fdfSMatt Spinler status = ResponseStatus::failure; 3085c350fdfSMatt Spinler } 3095c350fdfSMatt Spinler else 3105c350fdfSMatt Spinler { 3115c350fdfSMatt Spinler if (completionCode != PLDM_SUCCESS) 3125c350fdfSMatt Spinler { 3135c350fdfSMatt Spinler log<level::ERR>("Bad PLDM completion code", 3145c350fdfSMatt Spinler entry("COMPLETION_CODE=%d", completionCode)); 3155c350fdfSMatt Spinler status = ResponseStatus::failure; 3165c350fdfSMatt Spinler } 3175c350fdfSMatt Spinler } 3185c350fdfSMatt Spinler } 3195c350fdfSMatt Spinler 320a44efe48SMatt Spinler callResponseFunc(status); 3215c350fdfSMatt Spinler 3225c350fdfSMatt Spinler if (responseMsg) 3235c350fdfSMatt Spinler { 3245c350fdfSMatt Spinler free(responseMsg); 3255c350fdfSMatt Spinler } 3265c350fdfSMatt Spinler } 3275c350fdfSMatt Spinler 3285c350fdfSMatt Spinler void PLDMInterface::receiveTimerExpired() 3295c350fdfSMatt Spinler { 3305c350fdfSMatt Spinler log<level::ERR>("Timed out waiting for PLDM response"); 331*2843ba28SMatt Spinler 332*2843ba28SMatt Spinler // Cleanup, but keep the instance ID because the host didn't 333*2843ba28SMatt Spinler // respond so we can still use it. 334*2843ba28SMatt Spinler cleanupCmd(); 3355c350fdfSMatt Spinler 336a44efe48SMatt Spinler callResponseFunc(ResponseStatus::failure); 3375c350fdfSMatt Spinler } 3385c350fdfSMatt Spinler 3395c350fdfSMatt Spinler void PLDMInterface::cancelCmd() 3405c350fdfSMatt Spinler { 341*2843ba28SMatt Spinler _instanceID = std::nullopt; 342*2843ba28SMatt Spinler cleanupCmd(); 343*2843ba28SMatt Spinler } 344*2843ba28SMatt Spinler 345*2843ba28SMatt Spinler void PLDMInterface::cleanupCmd() 346*2843ba28SMatt Spinler { 3475c350fdfSMatt Spinler _inProgress = false; 3485c350fdfSMatt Spinler _source.reset(); 3495c350fdfSMatt Spinler 3505c350fdfSMatt Spinler if (_receiveTimer.isEnabled()) 3515c350fdfSMatt Spinler { 3525c350fdfSMatt Spinler _receiveTimer.setEnabled(false); 3535c350fdfSMatt Spinler } 3545c350fdfSMatt Spinler 3555c350fdfSMatt Spinler closeFD(); 3565c350fdfSMatt Spinler } 3575c350fdfSMatt Spinler 3585c350fdfSMatt Spinler } // namespace openpower::pels 359