xref: /openbmc/phosphor-logging/extensions/openpower-pels/pldm_interface.cpp (revision 1b41886d1ba223437ed35f4e04f3cad2e77c94ef)
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>
202843ba28SMatt Spinler #include <systemd/sd-bus.h>
215c350fdfSMatt Spinler #include <unistd.h>
225c350fdfSMatt Spinler 
23*1b41886dSMatt 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     {
75*1b41886dSMatt 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         {
87*1b41886dSMatt 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;
98*1b41886dSMatt Spinler         lg2::error("pldm_open failed.  errno = {ERRNO}, rc = {RC}", "ERRNO", e,
99*1b41886dSMatt Spinler                    "RC", _fd);
1005c350fdfSMatt Spinler         throw std::exception{};
1015c350fdfSMatt Spinler     }
1025c350fdfSMatt Spinler }
1035c350fdfSMatt Spinler 
1042843ba28SMatt Spinler void PLDMInterface::instanceIDCallback(sd_bus_message* msg)
1052843ba28SMatt Spinler {
1062843ba28SMatt Spinler     if (!_inProgress)
1072843ba28SMatt Spinler     {
108*1b41886dSMatt 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     {
117*1b41886dSMatt 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         {
126*1b41886dSMatt Spinler             lg2::error("Could not read instance ID out of message, rc = {RC}",
127*1b41886dSMatt 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     {
1432843ba28SMatt Spinler         startCommand();
1442843ba28SMatt Spinler     }
1452843ba28SMatt Spinler }
1462843ba28SMatt Spinler 
147d26fa3e7SPatrick Williams int iidCallback(sd_bus_message* msg, void* data, sd_bus_error* /*err*/)
1482843ba28SMatt Spinler {
1492843ba28SMatt Spinler     auto* interface = static_cast<PLDMInterface*>(data);
1502843ba28SMatt Spinler     interface->instanceIDCallback(msg);
1512843ba28SMatt Spinler     return 0;
1522843ba28SMatt Spinler }
1532843ba28SMatt Spinler 
1542843ba28SMatt Spinler void PLDMInterface::startCommand()
1555c350fdfSMatt Spinler {
1565c350fdfSMatt Spinler     try
1575c350fdfSMatt Spinler     {
1585c350fdfSMatt Spinler         closeFD();
1595c350fdfSMatt Spinler 
1605c350fdfSMatt Spinler         open();
1615c350fdfSMatt Spinler 
1625c350fdfSMatt Spinler         registerReceiveCallback();
1635c350fdfSMatt Spinler 
1642843ba28SMatt Spinler         doSend();
1652843ba28SMatt Spinler 
1662843ba28SMatt Spinler         _receiveTimer.restartOnce(_receiveTimeout);
1675c350fdfSMatt Spinler     }
1685c350fdfSMatt Spinler     catch (const std::exception& e)
1695c350fdfSMatt Spinler     {
1702843ba28SMatt Spinler         cleanupCmd();
1715c350fdfSMatt Spinler 
1722843ba28SMatt Spinler         callResponseFunc(ResponseStatus::failure);
1732843ba28SMatt Spinler     }
1742843ba28SMatt Spinler }
1752843ba28SMatt Spinler 
1762843ba28SMatt Spinler void PLDMInterface::startReadInstanceID()
1772843ba28SMatt Spinler {
1782843ba28SMatt Spinler     auto rc = sd_bus_call_method_async(
1792843ba28SMatt Spinler         _bus, NULL, service::pldm, object_path::pldm, interface::pldm_requester,
1802843ba28SMatt Spinler         "GetInstanceId", iidCallback, this, "y", _eid);
1812843ba28SMatt Spinler 
1822843ba28SMatt Spinler     if (rc < 0)
1832843ba28SMatt Spinler     {
184*1b41886dSMatt Spinler         lg2::error(
185*1b41886dSMatt Spinler             "Error calling sd_bus_call_method_async, rc = {RC}, msg = {MSG}",
186*1b41886dSMatt Spinler             "RC", rc, "MSG", strerror(-rc));
1872843ba28SMatt Spinler     }
1882843ba28SMatt Spinler }
1892843ba28SMatt Spinler 
1902843ba28SMatt Spinler CmdStatus PLDMInterface::sendNewLogCmd(uint32_t id, uint32_t size)
1912843ba28SMatt Spinler {
1922843ba28SMatt Spinler     _pelID = id;
1932843ba28SMatt Spinler     _pelSize = size;
1942843ba28SMatt Spinler     _inProgress = true;
1952843ba28SMatt Spinler 
1962843ba28SMatt Spinler     try
1972843ba28SMatt Spinler     {
1982843ba28SMatt Spinler         // Kick off the async call to get the instance ID if
1992843ba28SMatt Spinler         // necessary, otherwise start the command itself.
2002843ba28SMatt Spinler         if (!_instanceID)
2012843ba28SMatt Spinler         {
2022843ba28SMatt Spinler             startReadInstanceID();
2032843ba28SMatt Spinler         }
2042843ba28SMatt Spinler         else
2052843ba28SMatt Spinler         {
2062843ba28SMatt Spinler             startCommand();
2072843ba28SMatt Spinler         }
2082843ba28SMatt Spinler     }
2092843ba28SMatt Spinler     catch (const std::exception& e)
2102843ba28SMatt Spinler     {
2115c350fdfSMatt Spinler         _inProgress = false;
2125c350fdfSMatt Spinler         return CmdStatus::failure;
2135c350fdfSMatt Spinler     }
2145c350fdfSMatt Spinler 
2155c350fdfSMatt Spinler     return CmdStatus::success;
2165c350fdfSMatt Spinler }
2175c350fdfSMatt Spinler 
2185c350fdfSMatt Spinler void PLDMInterface::registerReceiveCallback()
2195c350fdfSMatt Spinler {
2205c350fdfSMatt Spinler     _source = std::make_unique<IO>(
2215c350fdfSMatt Spinler         _event, _fd, EPOLLIN,
2225c350fdfSMatt Spinler         std::bind(std::mem_fn(&PLDMInterface::receive), this,
2235c350fdfSMatt Spinler                   std::placeholders::_1, std::placeholders::_2,
2245c350fdfSMatt Spinler                   std::placeholders::_3));
2255c350fdfSMatt Spinler }
2265c350fdfSMatt Spinler 
2272843ba28SMatt Spinler void PLDMInterface::doSend()
2285c350fdfSMatt Spinler {
2295c350fdfSMatt Spinler     std::array<uint8_t, sizeof(pldm_msg_hdr) + sizeof(pelFileType) +
2302843ba28SMatt Spinler                             sizeof(_pelID) + sizeof(uint64_t)>
2315c350fdfSMatt Spinler         requestMsg;
2325c350fdfSMatt Spinler 
2335c350fdfSMatt Spinler     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
2345c350fdfSMatt Spinler 
2352843ba28SMatt Spinler     auto rc = encode_new_file_req(*_instanceID, pelFileType, _pelID, _pelSize,
2362843ba28SMatt Spinler                                   request);
2375c350fdfSMatt Spinler     if (rc != PLDM_SUCCESS)
2385c350fdfSMatt Spinler     {
239*1b41886dSMatt Spinler         lg2::error("encode_new_file_req failed, rc = {RC}", "RC", rc);
2405c350fdfSMatt Spinler         throw std::exception{};
2415c350fdfSMatt Spinler     }
2425c350fdfSMatt Spinler 
2435c350fdfSMatt Spinler     rc = pldm_send(_eid, _fd, requestMsg.data(), requestMsg.size());
2445c350fdfSMatt Spinler     if (rc < 0)
2455c350fdfSMatt Spinler     {
2465c350fdfSMatt Spinler         auto e = errno;
247*1b41886dSMatt Spinler         lg2::error("pldm_send failed, rc = {RC}, errno = {ERRNO}", "RC", rc,
248*1b41886dSMatt Spinler                    "ERRNO", e);
2495c350fdfSMatt Spinler 
2505c350fdfSMatt Spinler         throw std::exception{};
2515c350fdfSMatt Spinler     }
2525c350fdfSMatt Spinler }
2535c350fdfSMatt Spinler 
254d26fa3e7SPatrick Williams void PLDMInterface::receive(IO& /*io*/, int fd, uint32_t revents)
2555c350fdfSMatt Spinler {
2565c350fdfSMatt Spinler     if (!(revents & EPOLLIN))
2575c350fdfSMatt Spinler     {
2585c350fdfSMatt Spinler         return;
2595c350fdfSMatt Spinler     }
2605c350fdfSMatt Spinler 
2615c350fdfSMatt Spinler     uint8_t* responseMsg = nullptr;
2625c350fdfSMatt Spinler     size_t responseSize = 0;
2635c350fdfSMatt Spinler     ResponseStatus status = ResponseStatus::success;
2645c350fdfSMatt Spinler 
2652843ba28SMatt Spinler     auto rc = pldm_recv(_eid, fd, *_instanceID, &responseMsg, &responseSize);
2665c350fdfSMatt Spinler     if (rc < 0)
2675c350fdfSMatt Spinler     {
2685c350fdfSMatt Spinler         if (rc == PLDM_REQUESTER_INSTANCE_ID_MISMATCH)
2695c350fdfSMatt Spinler         {
2705c350fdfSMatt Spinler             // We got a response to someone else's message. Ignore it.
2715c350fdfSMatt Spinler             return;
2725c350fdfSMatt Spinler         }
2735c350fdfSMatt Spinler         else if (rc == PLDM_REQUESTER_NOT_RESP_MSG)
2745c350fdfSMatt Spinler         {
2755c350fdfSMatt Spinler             // Due to the MCTP loopback, we may get notified of the message
2765c350fdfSMatt Spinler             // we just sent.
2775c350fdfSMatt Spinler             return;
2785c350fdfSMatt Spinler         }
2795c350fdfSMatt Spinler 
2805c350fdfSMatt Spinler         auto e = errno;
281*1b41886dSMatt Spinler         lg2::error("pldm_recv failed, rc = {RC}, errno = {ERRNO}", "RC",
282*1b41886dSMatt Spinler                    static_cast<std::underlying_type_t<pldm_requester_rc_t>>(rc),
283*1b41886dSMatt Spinler                    "ERRNO", e);
2845c350fdfSMatt Spinler         status = ResponseStatus::failure;
2855c350fdfSMatt Spinler 
2865c350fdfSMatt Spinler         responseMsg = nullptr;
2875c350fdfSMatt Spinler     }
2885c350fdfSMatt Spinler 
2892843ba28SMatt Spinler     cleanupCmd();
2902843ba28SMatt Spinler 
2912843ba28SMatt Spinler     // Can't use this instance ID anymore.
2922843ba28SMatt Spinler     _instanceID = std::nullopt;
2935c350fdfSMatt Spinler 
2945c350fdfSMatt Spinler     if (status == ResponseStatus::success)
2955c350fdfSMatt Spinler     {
2965c350fdfSMatt Spinler         uint8_t completionCode = 0;
2975c350fdfSMatt Spinler         auto response = reinterpret_cast<pldm_msg*>(responseMsg);
2985c350fdfSMatt Spinler 
2995c350fdfSMatt Spinler         auto decodeRC = decode_new_file_resp(response, PLDM_NEW_FILE_RESP_BYTES,
3005c350fdfSMatt Spinler                                              &completionCode);
3015c350fdfSMatt Spinler         if (decodeRC < 0)
3025c350fdfSMatt Spinler         {
303*1b41886dSMatt Spinler             lg2::error("decode_new_file_resp failed, rc = {RC}", "RC",
304*1b41886dSMatt Spinler                        decodeRC);
3055c350fdfSMatt Spinler             status = ResponseStatus::failure;
3065c350fdfSMatt Spinler         }
3075c350fdfSMatt Spinler         else
3085c350fdfSMatt Spinler         {
3095c350fdfSMatt Spinler             if (completionCode != PLDM_SUCCESS)
3105c350fdfSMatt Spinler             {
311*1b41886dSMatt Spinler                 lg2::error("Bad PLDM completion code {CODE}", "CODE",
312*1b41886dSMatt Spinler                            completionCode);
3135c350fdfSMatt Spinler                 status = ResponseStatus::failure;
3145c350fdfSMatt Spinler             }
3155c350fdfSMatt Spinler         }
3165c350fdfSMatt Spinler     }
3175c350fdfSMatt Spinler 
318a44efe48SMatt Spinler     callResponseFunc(status);
3195c350fdfSMatt Spinler 
3205c350fdfSMatt Spinler     if (responseMsg)
3215c350fdfSMatt Spinler     {
3225c350fdfSMatt Spinler         free(responseMsg);
3235c350fdfSMatt Spinler     }
3245c350fdfSMatt Spinler }
3255c350fdfSMatt Spinler 
3265c350fdfSMatt Spinler void PLDMInterface::receiveTimerExpired()
3275c350fdfSMatt Spinler {
328*1b41886dSMatt Spinler     lg2::error("Timed out waiting for PLDM response");
3292843ba28SMatt Spinler 
3302843ba28SMatt Spinler     // Cleanup, but keep the instance ID because the host didn't
3312843ba28SMatt Spinler     // respond so we can still use it.
3322843ba28SMatt Spinler     cleanupCmd();
3335c350fdfSMatt Spinler 
334a44efe48SMatt Spinler     callResponseFunc(ResponseStatus::failure);
3355c350fdfSMatt Spinler }
3365c350fdfSMatt Spinler 
3375c350fdfSMatt Spinler void PLDMInterface::cancelCmd()
3385c350fdfSMatt Spinler {
3392843ba28SMatt Spinler     _instanceID = std::nullopt;
3402843ba28SMatt Spinler     cleanupCmd();
3412843ba28SMatt Spinler }
3422843ba28SMatt Spinler 
3432843ba28SMatt Spinler void PLDMInterface::cleanupCmd()
3442843ba28SMatt Spinler {
3455c350fdfSMatt Spinler     _inProgress = false;
3465c350fdfSMatt Spinler     _source.reset();
3475c350fdfSMatt Spinler 
3485c350fdfSMatt Spinler     if (_receiveTimer.isEnabled())
3495c350fdfSMatt Spinler     {
3505c350fdfSMatt Spinler         _receiveTimer.setEnabled(false);
3515c350fdfSMatt Spinler     }
3525c350fdfSMatt Spinler 
3535c350fdfSMatt Spinler     closeFD();
3545c350fdfSMatt Spinler }
3555c350fdfSMatt Spinler 
3565c350fdfSMatt Spinler } // namespace openpower::pels
357