1*5c350fdfSMatt Spinler /** 2*5c350fdfSMatt Spinler * Copyright © 2019 IBM Corporation 3*5c350fdfSMatt Spinler * 4*5c350fdfSMatt Spinler * Licensed under the Apache License, Version 2.0 (the "License"); 5*5c350fdfSMatt Spinler * you may not use this file except in compliance with the License. 6*5c350fdfSMatt Spinler * You may obtain a copy of the License at 7*5c350fdfSMatt Spinler * 8*5c350fdfSMatt Spinler * http://www.apache.org/licenses/LICENSE-2.0 9*5c350fdfSMatt Spinler * 10*5c350fdfSMatt Spinler * Unless required by applicable law or agreed to in writing, software 11*5c350fdfSMatt Spinler * distributed under the License is distributed on an "AS IS" BASIS, 12*5c350fdfSMatt Spinler * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*5c350fdfSMatt Spinler * See the License for the specific language governing permissions and 14*5c350fdfSMatt Spinler * limitations under the License. 15*5c350fdfSMatt Spinler */ 16*5c350fdfSMatt Spinler #include "pldm_interface.hpp" 17*5c350fdfSMatt Spinler 18*5c350fdfSMatt Spinler #include <libpldm/base.h> 19*5c350fdfSMatt Spinler #include <libpldm/file_io.h> 20*5c350fdfSMatt Spinler #include <unistd.h> 21*5c350fdfSMatt Spinler 22*5c350fdfSMatt Spinler #include <fstream> 23*5c350fdfSMatt Spinler #include <phosphor-logging/log.hpp> 24*5c350fdfSMatt Spinler 25*5c350fdfSMatt Spinler namespace openpower::pels 26*5c350fdfSMatt Spinler { 27*5c350fdfSMatt Spinler 28*5c350fdfSMatt Spinler using namespace phosphor::logging; 29*5c350fdfSMatt Spinler using namespace sdeventplus; 30*5c350fdfSMatt Spinler using namespace sdeventplus::source; 31*5c350fdfSMatt Spinler 32*5c350fdfSMatt Spinler constexpr auto eidPath = "/usr/share/pldm/host_eid"; 33*5c350fdfSMatt Spinler constexpr mctp_eid_t defaultEIDValue = 9; 34*5c350fdfSMatt Spinler 35*5c350fdfSMatt Spinler constexpr uint16_t pelFileType = 0; 36*5c350fdfSMatt Spinler 37*5c350fdfSMatt Spinler PLDMInterface::~PLDMInterface() 38*5c350fdfSMatt Spinler { 39*5c350fdfSMatt Spinler closeFD(); 40*5c350fdfSMatt Spinler } 41*5c350fdfSMatt Spinler 42*5c350fdfSMatt Spinler void PLDMInterface::closeFD() 43*5c350fdfSMatt Spinler { 44*5c350fdfSMatt Spinler if (_fd >= 0) 45*5c350fdfSMatt Spinler { 46*5c350fdfSMatt Spinler close(_fd); 47*5c350fdfSMatt Spinler _fd = -1; 48*5c350fdfSMatt Spinler } 49*5c350fdfSMatt Spinler } 50*5c350fdfSMatt Spinler 51*5c350fdfSMatt Spinler void PLDMInterface::readEID() 52*5c350fdfSMatt Spinler { 53*5c350fdfSMatt Spinler _eid = defaultEIDValue; 54*5c350fdfSMatt Spinler 55*5c350fdfSMatt Spinler std::ifstream eidFile{eidPath}; 56*5c350fdfSMatt Spinler if (!eidFile.good()) 57*5c350fdfSMatt Spinler { 58*5c350fdfSMatt Spinler log<level::ERR>("Could not open host EID file"); 59*5c350fdfSMatt Spinler } 60*5c350fdfSMatt Spinler else 61*5c350fdfSMatt Spinler { 62*5c350fdfSMatt Spinler std::string eid; 63*5c350fdfSMatt Spinler eidFile >> eid; 64*5c350fdfSMatt Spinler if (!eid.empty()) 65*5c350fdfSMatt Spinler { 66*5c350fdfSMatt Spinler _eid = atoi(eid.c_str()); 67*5c350fdfSMatt Spinler } 68*5c350fdfSMatt Spinler else 69*5c350fdfSMatt Spinler { 70*5c350fdfSMatt Spinler log<level::ERR>("EID file was empty"); 71*5c350fdfSMatt Spinler } 72*5c350fdfSMatt Spinler } 73*5c350fdfSMatt Spinler } 74*5c350fdfSMatt Spinler 75*5c350fdfSMatt Spinler void PLDMInterface::open() 76*5c350fdfSMatt Spinler { 77*5c350fdfSMatt Spinler _fd = pldm_open(); 78*5c350fdfSMatt Spinler if (_fd < 0) 79*5c350fdfSMatt Spinler { 80*5c350fdfSMatt Spinler auto e = errno; 81*5c350fdfSMatt Spinler log<level::ERR>("pldm_open failed", entry("ERRNO=%d", e), 82*5c350fdfSMatt Spinler entry("RC=%d\n", _fd)); 83*5c350fdfSMatt Spinler throw std::exception{}; 84*5c350fdfSMatt Spinler } 85*5c350fdfSMatt Spinler } 86*5c350fdfSMatt Spinler 87*5c350fdfSMatt Spinler CmdStatus PLDMInterface::sendNewLogCmd(uint32_t id, uint32_t size) 88*5c350fdfSMatt Spinler { 89*5c350fdfSMatt Spinler try 90*5c350fdfSMatt Spinler { 91*5c350fdfSMatt Spinler closeFD(); 92*5c350fdfSMatt Spinler 93*5c350fdfSMatt Spinler open(); 94*5c350fdfSMatt Spinler 95*5c350fdfSMatt Spinler readInstanceID(); 96*5c350fdfSMatt Spinler 97*5c350fdfSMatt Spinler registerReceiveCallback(); 98*5c350fdfSMatt Spinler 99*5c350fdfSMatt Spinler doSend(id, size); 100*5c350fdfSMatt Spinler } 101*5c350fdfSMatt Spinler catch (const std::exception& e) 102*5c350fdfSMatt Spinler { 103*5c350fdfSMatt Spinler closeFD(); 104*5c350fdfSMatt Spinler 105*5c350fdfSMatt Spinler _inProgress = false; 106*5c350fdfSMatt Spinler _source.reset(); 107*5c350fdfSMatt Spinler return CmdStatus::failure; 108*5c350fdfSMatt Spinler } 109*5c350fdfSMatt Spinler 110*5c350fdfSMatt Spinler _inProgress = true; 111*5c350fdfSMatt Spinler _receiveTimer.restartOnce(_receiveTimeout); 112*5c350fdfSMatt Spinler return CmdStatus::success; 113*5c350fdfSMatt Spinler } 114*5c350fdfSMatt Spinler 115*5c350fdfSMatt Spinler void PLDMInterface::registerReceiveCallback() 116*5c350fdfSMatt Spinler { 117*5c350fdfSMatt Spinler _source = std::make_unique<IO>( 118*5c350fdfSMatt Spinler _event, _fd, EPOLLIN, 119*5c350fdfSMatt Spinler std::bind(std::mem_fn(&PLDMInterface::receive), this, 120*5c350fdfSMatt Spinler std::placeholders::_1, std::placeholders::_2, 121*5c350fdfSMatt Spinler std::placeholders::_3)); 122*5c350fdfSMatt Spinler } 123*5c350fdfSMatt Spinler 124*5c350fdfSMatt Spinler void PLDMInterface::readInstanceID() 125*5c350fdfSMatt Spinler { 126*5c350fdfSMatt Spinler try 127*5c350fdfSMatt Spinler { 128*5c350fdfSMatt Spinler _instanceID = _dataIface.getPLDMInstanceID(_eid); 129*5c350fdfSMatt Spinler } 130*5c350fdfSMatt Spinler catch (const std::exception& e) 131*5c350fdfSMatt Spinler { 132*5c350fdfSMatt Spinler log<level::ERR>( 133*5c350fdfSMatt Spinler "Failed to get instance ID from PLDM Requester D-Bus daemon", 134*5c350fdfSMatt Spinler entry("ERROR=%s", e.what())); 135*5c350fdfSMatt Spinler throw; 136*5c350fdfSMatt Spinler } 137*5c350fdfSMatt Spinler } 138*5c350fdfSMatt Spinler 139*5c350fdfSMatt Spinler void PLDMInterface::doSend(uint32_t id, uint32_t size) 140*5c350fdfSMatt Spinler { 141*5c350fdfSMatt Spinler std::array<uint8_t, sizeof(pldm_msg_hdr) + sizeof(pelFileType) + 142*5c350fdfSMatt Spinler sizeof(id) + sizeof(size)> 143*5c350fdfSMatt Spinler requestMsg; 144*5c350fdfSMatt Spinler 145*5c350fdfSMatt Spinler auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); 146*5c350fdfSMatt Spinler 147*5c350fdfSMatt Spinler auto rc = encode_new_file_req(_instanceID, pelFileType, id, size, request); 148*5c350fdfSMatt Spinler if (rc != PLDM_SUCCESS) 149*5c350fdfSMatt Spinler { 150*5c350fdfSMatt Spinler log<level::ERR>("encode_new_file_req failed", entry("RC=%d", rc)); 151*5c350fdfSMatt Spinler throw std::exception{}; 152*5c350fdfSMatt Spinler } 153*5c350fdfSMatt Spinler 154*5c350fdfSMatt Spinler rc = pldm_send(_eid, _fd, requestMsg.data(), requestMsg.size()); 155*5c350fdfSMatt Spinler if (rc < 0) 156*5c350fdfSMatt Spinler { 157*5c350fdfSMatt Spinler auto e = errno; 158*5c350fdfSMatt Spinler log<level::ERR>("pldm_send failed", entry("RC=%d", rc), 159*5c350fdfSMatt Spinler entry("ERRNO=%d", e)); 160*5c350fdfSMatt Spinler 161*5c350fdfSMatt Spinler throw std::exception{}; 162*5c350fdfSMatt Spinler } 163*5c350fdfSMatt Spinler } 164*5c350fdfSMatt Spinler 165*5c350fdfSMatt Spinler void PLDMInterface::receive(IO& io, int fd, uint32_t revents) 166*5c350fdfSMatt Spinler { 167*5c350fdfSMatt Spinler if (!(revents & EPOLLIN)) 168*5c350fdfSMatt Spinler { 169*5c350fdfSMatt Spinler return; 170*5c350fdfSMatt Spinler } 171*5c350fdfSMatt Spinler 172*5c350fdfSMatt Spinler uint8_t* responseMsg = nullptr; 173*5c350fdfSMatt Spinler size_t responseSize = 0; 174*5c350fdfSMatt Spinler ResponseStatus status = ResponseStatus::success; 175*5c350fdfSMatt Spinler 176*5c350fdfSMatt Spinler auto rc = pldm_recv(_eid, fd, _instanceID, &responseMsg, &responseSize); 177*5c350fdfSMatt Spinler if (rc < 0) 178*5c350fdfSMatt Spinler { 179*5c350fdfSMatt Spinler if (rc == PLDM_REQUESTER_INSTANCE_ID_MISMATCH) 180*5c350fdfSMatt Spinler { 181*5c350fdfSMatt Spinler // We got a response to someone else's message. Ignore it. 182*5c350fdfSMatt Spinler return; 183*5c350fdfSMatt Spinler } 184*5c350fdfSMatt Spinler else if (rc == PLDM_REQUESTER_NOT_RESP_MSG) 185*5c350fdfSMatt Spinler { 186*5c350fdfSMatt Spinler // Due to the MCTP loopback, we may get notified of the message 187*5c350fdfSMatt Spinler // we just sent. 188*5c350fdfSMatt Spinler return; 189*5c350fdfSMatt Spinler } 190*5c350fdfSMatt Spinler 191*5c350fdfSMatt Spinler auto e = errno; 192*5c350fdfSMatt Spinler log<level::ERR>("pldm_recv failed", entry("RC=%d", rc), 193*5c350fdfSMatt Spinler entry("ERRNO=%d", e)); 194*5c350fdfSMatt Spinler status = ResponseStatus::failure; 195*5c350fdfSMatt Spinler 196*5c350fdfSMatt Spinler responseMsg = nullptr; 197*5c350fdfSMatt Spinler } 198*5c350fdfSMatt Spinler 199*5c350fdfSMatt Spinler _inProgress = false; 200*5c350fdfSMatt Spinler _receiveTimer.setEnabled(false); 201*5c350fdfSMatt Spinler closeFD(); 202*5c350fdfSMatt Spinler _source.reset(); 203*5c350fdfSMatt Spinler 204*5c350fdfSMatt Spinler if (status == ResponseStatus::success) 205*5c350fdfSMatt Spinler { 206*5c350fdfSMatt Spinler uint8_t completionCode = 0; 207*5c350fdfSMatt Spinler auto response = reinterpret_cast<pldm_msg*>(responseMsg); 208*5c350fdfSMatt Spinler 209*5c350fdfSMatt Spinler auto decodeRC = decode_new_file_resp(response, PLDM_NEW_FILE_RESP_BYTES, 210*5c350fdfSMatt Spinler &completionCode); 211*5c350fdfSMatt Spinler if (decodeRC < 0) 212*5c350fdfSMatt Spinler { 213*5c350fdfSMatt Spinler log<level::ERR>("decode_new_file_resp failed", 214*5c350fdfSMatt Spinler entry("RC=%d", decodeRC)); 215*5c350fdfSMatt Spinler status = ResponseStatus::failure; 216*5c350fdfSMatt Spinler } 217*5c350fdfSMatt Spinler else 218*5c350fdfSMatt Spinler { 219*5c350fdfSMatt Spinler if (completionCode != PLDM_SUCCESS) 220*5c350fdfSMatt Spinler { 221*5c350fdfSMatt Spinler log<level::ERR>("Bad PLDM completion code", 222*5c350fdfSMatt Spinler entry("COMPLETION_CODE=%d", completionCode)); 223*5c350fdfSMatt Spinler status = ResponseStatus::failure; 224*5c350fdfSMatt Spinler } 225*5c350fdfSMatt Spinler } 226*5c350fdfSMatt Spinler } 227*5c350fdfSMatt Spinler 228*5c350fdfSMatt Spinler if (_responseFunc) 229*5c350fdfSMatt Spinler { 230*5c350fdfSMatt Spinler try 231*5c350fdfSMatt Spinler { 232*5c350fdfSMatt Spinler (*_responseFunc)(status); 233*5c350fdfSMatt Spinler } 234*5c350fdfSMatt Spinler catch (const std::exception& e) 235*5c350fdfSMatt Spinler { 236*5c350fdfSMatt Spinler log<level::ERR>("PLDM response callback threw an exception", 237*5c350fdfSMatt Spinler entry("ERROR=%s", e.what())); 238*5c350fdfSMatt Spinler } 239*5c350fdfSMatt Spinler } 240*5c350fdfSMatt Spinler 241*5c350fdfSMatt Spinler if (responseMsg) 242*5c350fdfSMatt Spinler { 243*5c350fdfSMatt Spinler free(responseMsg); 244*5c350fdfSMatt Spinler } 245*5c350fdfSMatt Spinler } 246*5c350fdfSMatt Spinler 247*5c350fdfSMatt Spinler void PLDMInterface::receiveTimerExpired() 248*5c350fdfSMatt Spinler { 249*5c350fdfSMatt Spinler log<level::ERR>("Timed out waiting for PLDM response"); 250*5c350fdfSMatt Spinler cancelCmd(); 251*5c350fdfSMatt Spinler 252*5c350fdfSMatt Spinler if (_responseFunc) 253*5c350fdfSMatt Spinler { 254*5c350fdfSMatt Spinler try 255*5c350fdfSMatt Spinler { 256*5c350fdfSMatt Spinler (*_responseFunc)(ResponseStatus::failure); 257*5c350fdfSMatt Spinler } 258*5c350fdfSMatt Spinler catch (const std::exception& e) 259*5c350fdfSMatt Spinler { 260*5c350fdfSMatt Spinler log<level::ERR>("PLDM response callback threw an exception", 261*5c350fdfSMatt Spinler entry("ERROR=%s", e.what())); 262*5c350fdfSMatt Spinler } 263*5c350fdfSMatt Spinler } 264*5c350fdfSMatt Spinler } 265*5c350fdfSMatt Spinler 266*5c350fdfSMatt Spinler void PLDMInterface::cancelCmd() 267*5c350fdfSMatt Spinler { 268*5c350fdfSMatt Spinler _inProgress = false; 269*5c350fdfSMatt Spinler _source.reset(); 270*5c350fdfSMatt Spinler 271*5c350fdfSMatt Spinler if (_receiveTimer.isEnabled()) 272*5c350fdfSMatt Spinler { 273*5c350fdfSMatt Spinler _receiveTimer.setEnabled(false); 274*5c350fdfSMatt Spinler } 275*5c350fdfSMatt Spinler 276*5c350fdfSMatt Spinler closeFD(); 277*5c350fdfSMatt Spinler } 278*5c350fdfSMatt Spinler 279*5c350fdfSMatt Spinler } // namespace openpower::pels 280