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