xref: /openbmc/phosphor-logging/extensions/openpower-pels/pldm_interface.cpp (revision 49bcbe92f06e5d02c6c668184b0180056ae745ef)
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