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