xref: /openbmc/pldm/pldmd/pldmd.cpp (revision 772403b9)
1 #include "libpldm/base.h"
2 #include "libpldm/bios.h"
3 #include "libpldm/pdr.h"
4 #include "libpldm/platform.h"
5 
6 #include "common/utils.hpp"
7 #include "dbus_impl_requester.hpp"
8 #include "invoker.hpp"
9 
10 #include <err.h>
11 #include <getopt.h>
12 #include <poll.h>
13 #include <stdlib.h>
14 #include <sys/socket.h>
15 #include <sys/types.h>
16 #include <sys/un.h>
17 #include <unistd.h>
18 
19 #include <sdeventplus/event.hpp>
20 #include <sdeventplus/source/io.hpp>
21 
22 #include <cstdio>
23 #include <cstring>
24 #include <fstream>
25 #include <iomanip>
26 #include <iostream>
27 #include <iterator>
28 #include <memory>
29 #include <sstream>
30 #include <stdexcept>
31 #include <string>
32 #include <vector>
33 
34 #ifdef LIBPLDMRESPONDER
35 #include "dbus_impl_pdr.hpp"
36 #include "host-bmc/dbus_to_event_handler.hpp"
37 #include "host-bmc/dbus_to_host_effecters.hpp"
38 #include "host-bmc/host_pdr_handler.hpp"
39 #include "libpldmresponder/base.hpp"
40 #include "libpldmresponder/bios.hpp"
41 #include "libpldmresponder/fru.hpp"
42 #include "libpldmresponder/oem_handler.hpp"
43 #include "libpldmresponder/platform.hpp"
44 #include "xyz/openbmc_project/PLDM/Event/server.hpp"
45 #endif
46 
47 #ifdef OEM_IBM
48 #include "libpldmresponder/file_io.hpp"
49 #include "libpldmresponder/oem_ibm_handler.hpp"
50 #endif
51 
52 constexpr uint8_t MCTP_MSG_TYPE_PLDM = 1;
53 
54 using namespace pldm;
55 using namespace sdeventplus;
56 using namespace sdeventplus::source;
57 using namespace pldm::responder;
58 using namespace pldm::utils;
59 
60 static Response processRxMsg(const std::vector<uint8_t>& requestMsg,
61                              Invoker& invoker, dbus_api::Requester& requester)
62 {
63 
64     Response response;
65     uint8_t eid = requestMsg[0];
66     uint8_t type = requestMsg[1];
67     pldm_header_info hdrFields{};
68     auto hdr = reinterpret_cast<const pldm_msg_hdr*>(
69         requestMsg.data() + sizeof(eid) + sizeof(type));
70     if (PLDM_SUCCESS != unpack_pldm_header(hdr, &hdrFields))
71     {
72         std::cerr << "Empty PLDM request header \n";
73     }
74     else if (PLDM_RESPONSE != hdrFields.msg_type)
75     {
76         auto request = reinterpret_cast<const pldm_msg*>(hdr);
77         size_t requestLen = requestMsg.size() - sizeof(struct pldm_msg_hdr) -
78                             sizeof(eid) - sizeof(type);
79         try
80         {
81             response = invoker.handle(hdrFields.pldm_type, hdrFields.command,
82                                       request, requestLen);
83         }
84         catch (const std::out_of_range& e)
85         {
86             uint8_t completion_code = PLDM_ERROR_UNSUPPORTED_PLDM_CMD;
87             response.resize(sizeof(pldm_msg_hdr));
88             auto responseHdr = reinterpret_cast<pldm_msg_hdr*>(response.data());
89             pldm_header_info header{};
90             header.msg_type = PLDM_RESPONSE;
91             header.instance = hdrFields.instance;
92             header.pldm_type = hdrFields.pldm_type;
93             header.command = hdrFields.command;
94             auto result = pack_pldm_header(&header, responseHdr);
95             if (PLDM_SUCCESS != result)
96             {
97                 std::cerr << "Failed adding response header \n";
98             }
99             response.insert(response.end(), completion_code);
100         }
101     }
102     else
103     {
104         requester.markFree(eid, hdr->instance_id);
105     }
106     return response;
107 }
108 
109 void optionUsage(void)
110 {
111     std::cerr << "Usage: pldmd [options]\n";
112     std::cerr << "Options:\n";
113     std::cerr
114         << "  --verbose=<0/1>  0 - Disable verbosity, 1 - Enable verbosity\n";
115     std::cerr << "Defaulted settings:  --verbose=0 \n";
116 }
117 
118 int main(int argc, char** argv)
119 {
120 
121     bool verbose = false;
122     static struct option long_options[] = {
123         {"verbose", required_argument, 0, 'v'}, {0, 0, 0, 0}};
124 
125     auto argflag = getopt_long(argc, argv, "v:", long_options, nullptr);
126     switch (argflag)
127     {
128         case 'v':
129             switch (std::stoi(optarg))
130             {
131                 case 0:
132                     verbose = false;
133                     break;
134                 case 1:
135                     verbose = true;
136                     break;
137                 default:
138                     optionUsage();
139                     break;
140             }
141             break;
142         default:
143             optionUsage();
144             break;
145     }
146 
147     /* Create local socket. */
148     int returnCode = 0;
149     int sockfd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
150     if (-1 == sockfd)
151     {
152         returnCode = -errno;
153         std::cerr << "Failed to create the socket, RC= " << returnCode << "\n";
154         exit(EXIT_FAILURE);
155     }
156 
157     auto event = Event::get_default();
158     auto& bus = pldm::utils::DBusHandler::getBus();
159     dbus_api::Requester dbusImplReq(bus, "/xyz/openbmc_project/pldm");
160     Invoker invoker{};
161 
162 #ifdef LIBPLDMRESPONDER
163     using namespace pldm::state_sensor;
164     std::unique_ptr<pldm_pdr, decltype(&pldm_pdr_destroy)> pdrRepo(
165         pldm_pdr_init(), pldm_pdr_destroy);
166     std::unique_ptr<pldm_entity_association_tree,
167                     decltype(&pldm_entity_association_tree_destroy)>
168         entityTree(pldm_entity_association_tree_init(),
169                    pldm_entity_association_tree_destroy);
170     std::unique_ptr<pldm_entity_association_tree,
171                     decltype(&pldm_entity_association_tree_destroy)>
172         bmcEntityTree(pldm_entity_association_tree_init(),
173                       pldm_entity_association_tree_destroy);
174     std::unique_ptr<HostPDRHandler> hostPDRHandler;
175     std::unique_ptr<pldm::host_effecters::HostEffecterParser>
176         hostEffecterParser;
177     std::unique_ptr<DbusToPLDMEvent> dbusToPLDMEventHandler;
178     auto dbusHandler = std::make_unique<DBusHandler>();
179     auto hostEID = pldm::utils::readHostEID();
180     if (hostEID)
181     {
182         hostPDRHandler = std::make_unique<HostPDRHandler>(
183             sockfd, hostEID, event, pdrRepo.get(), EVENTS_JSONS_DIR,
184             entityTree.get(), bmcEntityTree.get(), dbusImplReq, verbose);
185         hostEffecterParser =
186             std::make_unique<pldm::host_effecters::HostEffecterParser>(
187                 &dbusImplReq, sockfd, pdrRepo.get(), dbusHandler.get(),
188                 HOST_JSONS_DIR, verbose);
189         dbusToPLDMEventHandler =
190             std::make_unique<DbusToPLDMEvent>(sockfd, hostEID, dbusImplReq);
191     }
192     std::unique_ptr<oem_platform::Handler> oemPlatformHandler{};
193 
194 #ifdef OEM_IBM
195     std::unique_ptr<pldm::responder::CodeUpdate> codeUpdate =
196         std::make_unique<pldm::responder::CodeUpdate>(dbusHandler.get());
197     codeUpdate->clearDirPath(LID_STAGING_DIR);
198     oemPlatformHandler = std::make_unique<oem_ibm_platform::Handler>(
199         dbusHandler.get(), codeUpdate.get(), sockfd, hostEID, dbusImplReq,
200         event);
201     codeUpdate->setOemPlatformHandler(oemPlatformHandler.get());
202     invoker.registerHandler(
203         PLDM_OEM, std::make_unique<oem_ibm::Handler>(
204                       oemPlatformHandler.get(), sockfd, hostEID, &dbusImplReq));
205 #endif
206     invoker.registerHandler(PLDM_BASE, std::make_unique<base::Handler>());
207     invoker.registerHandler(PLDM_BIOS, std::make_unique<bios::Handler>(
208                                            sockfd, hostEID, &dbusImplReq));
209     auto fruHandler = std::make_unique<fru::Handler>(
210         FRU_JSONS_DIR, pdrRepo.get(), entityTree.get(), bmcEntityTree.get());
211     // FRU table is built lazily when a FRU command or Get PDR command is
212     // handled. To enable building FRU table, the FRU handler is passed to the
213     // Platform handler.
214     auto platformHandler = std::make_unique<platform::Handler>(
215         dbusHandler.get(), PDR_JSONS_DIR, pdrRepo.get(), hostPDRHandler.get(),
216         dbusToPLDMEventHandler.get(), fruHandler.get(),
217         oemPlatformHandler.get(), event, true);
218 #ifdef OEM_IBM
219     pldm::responder::oem_ibm_platform::Handler* oemIbmPlatformHandler =
220         dynamic_cast<pldm::responder::oem_ibm_platform::Handler*>(
221             oemPlatformHandler.get());
222     oemIbmPlatformHandler->setPlatformHandler(platformHandler.get());
223 #endif
224 
225     invoker.registerHandler(PLDM_PLATFORM, std::move(platformHandler));
226     invoker.registerHandler(PLDM_FRU, std::move(fruHandler));
227     dbus_api::Pdr dbusImplPdr(bus, "/xyz/openbmc_project/pldm", pdrRepo.get());
228     sdbusplus::xyz::openbmc_project::PLDM::server::Event dbusImplEvent(
229         bus, "/xyz/openbmc_project/pldm");
230 #endif
231 
232     pldm::utils::CustomFD socketFd(sockfd);
233 
234     struct sockaddr_un addr
235     {};
236     addr.sun_family = AF_UNIX;
237     const char path[] = "\0mctp-mux";
238     memcpy(addr.sun_path, path, sizeof(path) - 1);
239     int result = connect(socketFd(), reinterpret_cast<struct sockaddr*>(&addr),
240                          sizeof(path) + sizeof(addr.sun_family) - 1);
241     if (-1 == result)
242     {
243         returnCode = -errno;
244         std::cerr << "Failed to connect to the socket, RC= " << returnCode
245                   << "\n";
246         exit(EXIT_FAILURE);
247     }
248 
249     result = write(socketFd(), &MCTP_MSG_TYPE_PLDM, sizeof(MCTP_MSG_TYPE_PLDM));
250     if (-1 == result)
251     {
252         returnCode = -errno;
253         std::cerr << "Failed to send message type as pldm to mctp, RC= "
254                   << returnCode << "\n";
255         exit(EXIT_FAILURE);
256     }
257 
258     auto callback = [verbose, &invoker, &dbusImplReq](IO& io, int fd,
259                                                       uint32_t revents) {
260         if (!(revents & EPOLLIN))
261         {
262             return;
263         }
264 
265         // Outgoing message.
266         struct iovec iov[2]{};
267 
268         // This structure contains the parameter information for the response
269         // message.
270         struct msghdr msg
271         {};
272 
273         int returnCode = 0;
274         ssize_t peekedLength = recv(fd, nullptr, 0, MSG_PEEK | MSG_TRUNC);
275         if (0 == peekedLength)
276         {
277             // MCTP daemon has closed the socket this daemon is connected to.
278             // This may or may not be an error scenario, in either case the
279             // recovery mechanism for this daemon is to restart, and hence exit
280             // the event loop, that will cause this daemon to exit with a
281             // failure code.
282             io.get_event().exit(0);
283         }
284         else if (peekedLength <= -1)
285         {
286             returnCode = -errno;
287             std::cerr << "recv system call failed, RC= " << returnCode << "\n";
288         }
289         else
290         {
291             std::vector<uint8_t> requestMsg(peekedLength);
292             auto recvDataLength = recv(
293                 fd, static_cast<void*>(requestMsg.data()), peekedLength, 0);
294             if (recvDataLength == peekedLength)
295             {
296                 if (verbose)
297                 {
298                     std::cout << "Received Msg" << std::endl;
299                     printBuffer(requestMsg, verbose);
300                 }
301                 if (MCTP_MSG_TYPE_PLDM != requestMsg[1])
302                 {
303                     // Skip this message and continue.
304                     std::cerr << "Encountered Non-PLDM type message"
305                               << "\n";
306                 }
307                 else
308                 {
309                     // process message and send response
310                     auto response =
311                         processRxMsg(requestMsg, invoker, dbusImplReq);
312                     if (!response.empty())
313                     {
314                         if (verbose)
315                         {
316                             std::cout << "Sending Msg" << std::endl;
317                             printBuffer(response, verbose);
318                         }
319 
320                         iov[0].iov_base = &requestMsg[0];
321                         iov[0].iov_len =
322                             sizeof(requestMsg[0]) + sizeof(requestMsg[1]);
323                         iov[1].iov_base = response.data();
324                         iov[1].iov_len = response.size();
325 
326                         msg.msg_iov = iov;
327                         msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]);
328 
329                         int result = sendmsg(fd, &msg, 0);
330                         if (-1 == result)
331                         {
332                             returnCode = -errno;
333                             std::cerr << "sendto system call failed, RC= "
334                                       << returnCode << "\n";
335                         }
336                     }
337                 }
338             }
339             else
340             {
341                 std::cerr
342                     << "Failure to read peeked length packet. peekedLength= "
343                     << peekedLength << " recvDataLength=" << recvDataLength
344                     << "\n";
345             }
346         }
347     };
348 
349     bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
350     bus.request_name("xyz.openbmc_project.PLDM");
351     IO io(event, socketFd(), EPOLLIN, std::move(callback));
352     event.loop();
353 
354     result = shutdown(sockfd, SHUT_RDWR);
355     if (-1 == result)
356     {
357         returnCode = -errno;
358         std::cerr << "Failed to shutdown the socket, RC=" << returnCode << "\n";
359         exit(EXIT_FAILURE);
360     }
361     exit(EXIT_FAILURE);
362 }
363