xref: /openbmc/pldm/pldmd/pldmd.cpp (revision df2fa47b61443d013d5e0baecdfaa8ac00e705a8)
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<HostPDRHandler> hostPDRHandler;
171     std::unique_ptr<pldm::host_effecters::HostEffecterParser>
172         hostEffecterParser;
173     std::unique_ptr<DbusToPLDMEvent> dbusToPLDMEventHandler;
174     auto dbusHandler = std::make_unique<DBusHandler>();
175     auto hostEID = pldm::utils::readHostEID();
176     if (hostEID)
177     {
178         hostPDRHandler = std::make_unique<HostPDRHandler>(
179             sockfd, hostEID, event, pdrRepo.get(), EVENTS_JSONS_DIR,
180             entityTree.get(), dbusImplReq, verbose);
181         hostEffecterParser =
182             std::make_unique<pldm::host_effecters::HostEffecterParser>(
183                 &dbusImplReq, sockfd, pdrRepo.get(), dbusHandler.get(),
184                 HOST_JSONS_DIR, verbose);
185         dbusToPLDMEventHandler =
186             std::make_unique<DbusToPLDMEvent>(sockfd, hostEID, dbusImplReq);
187     }
188     std::unique_ptr<oem_platform::Handler> oemPlatformHandler{};
189 
190 #ifdef OEM_IBM
191     std::unique_ptr<pldm::responder::CodeUpdate> codeUpdate =
192         std::make_unique<pldm::responder::CodeUpdate>(dbusHandler.get());
193     codeUpdate->clearDirPath(LID_STAGING_DIR);
194     oemPlatformHandler = std::make_unique<oem_ibm_platform::Handler>(
195         dbusHandler.get(), codeUpdate.get(), sockfd, hostEID, dbusImplReq,
196         event);
197     codeUpdate->setOemPlatformHandler(oemPlatformHandler.get());
198     invoker.registerHandler(
199         PLDM_OEM, std::make_unique<oem_ibm::Handler>(
200                       oemPlatformHandler.get(), sockfd, hostEID, &dbusImplReq));
201 #endif
202     invoker.registerHandler(PLDM_BASE, std::make_unique<base::Handler>());
203     invoker.registerHandler(PLDM_BIOS, std::make_unique<bios::Handler>(
204                                            sockfd, hostEID, &dbusImplReq));
205     auto fruHandler = std::make_unique<fru::Handler>(
206         FRU_JSONS_DIR, pdrRepo.get(), entityTree.get());
207     // FRU table is built lazily when a FRU command or Get PDR command is
208     // handled. To enable building FRU table, the FRU handler is passed to the
209     // Platform handler.
210     auto platformHandler = std::make_unique<platform::Handler>(
211         dbusHandler.get(), PDR_JSONS_DIR, pdrRepo.get(), hostPDRHandler.get(),
212         dbusToPLDMEventHandler.get(), fruHandler.get(),
213         oemPlatformHandler.get(), event, true);
214 #ifdef OEM_IBM
215     pldm::responder::oem_ibm_platform::Handler* oemIbmPlatformHandler =
216         dynamic_cast<pldm::responder::oem_ibm_platform::Handler*>(
217             oemPlatformHandler.get());
218     oemIbmPlatformHandler->setPlatformHandler(platformHandler.get());
219 #endif
220 
221     invoker.registerHandler(PLDM_PLATFORM, std::move(platformHandler));
222     invoker.registerHandler(PLDM_FRU, std::move(fruHandler));
223     dbus_api::Pdr dbusImplPdr(bus, "/xyz/openbmc_project/pldm", pdrRepo.get());
224     sdbusplus::xyz::openbmc_project::PLDM::server::Event dbusImplEvent(
225         bus, "/xyz/openbmc_project/pldm");
226 #endif
227 
228     pldm::utils::CustomFD socketFd(sockfd);
229 
230     struct sockaddr_un addr
231     {};
232     addr.sun_family = AF_UNIX;
233     const char path[] = "\0mctp-mux";
234     memcpy(addr.sun_path, path, sizeof(path) - 1);
235     int result = connect(socketFd(), reinterpret_cast<struct sockaddr*>(&addr),
236                          sizeof(path) + sizeof(addr.sun_family) - 1);
237     if (-1 == result)
238     {
239         returnCode = -errno;
240         std::cerr << "Failed to connect to the socket, RC= " << returnCode
241                   << "\n";
242         exit(EXIT_FAILURE);
243     }
244 
245     result = write(socketFd(), &MCTP_MSG_TYPE_PLDM, sizeof(MCTP_MSG_TYPE_PLDM));
246     if (-1 == result)
247     {
248         returnCode = -errno;
249         std::cerr << "Failed to send message type as pldm to mctp, RC= "
250                   << returnCode << "\n";
251         exit(EXIT_FAILURE);
252     }
253 
254     auto callback = [verbose, &invoker, &dbusImplReq](IO& io, int fd,
255                                                       uint32_t revents) {
256         if (!(revents & EPOLLIN))
257         {
258             return;
259         }
260 
261         // Outgoing message.
262         struct iovec iov[2]{};
263 
264         // This structure contains the parameter information for the response
265         // message.
266         struct msghdr msg
267         {};
268 
269         int returnCode = 0;
270         ssize_t peekedLength = recv(fd, nullptr, 0, MSG_PEEK | MSG_TRUNC);
271         if (0 == peekedLength)
272         {
273             // MCTP daemon has closed the socket this daemon is connected to.
274             // This may or may not be an error scenario, in either case the
275             // recovery mechanism for this daemon is to restart, and hence exit
276             // the event loop, that will cause this daemon to exit with a
277             // failure code.
278             io.get_event().exit(0);
279         }
280         else if (peekedLength <= -1)
281         {
282             returnCode = -errno;
283             std::cerr << "recv system call failed, RC= " << returnCode << "\n";
284         }
285         else
286         {
287             std::vector<uint8_t> requestMsg(peekedLength);
288             auto recvDataLength = recv(
289                 fd, static_cast<void*>(requestMsg.data()), peekedLength, 0);
290             if (recvDataLength == peekedLength)
291             {
292                 if (verbose)
293                 {
294                     std::cout << "Received Msg" << std::endl;
295                     printBuffer(requestMsg, verbose);
296                 }
297                 if (MCTP_MSG_TYPE_PLDM != requestMsg[1])
298                 {
299                     // Skip this message and continue.
300                     std::cerr << "Encountered Non-PLDM type message"
301                               << "\n";
302                 }
303                 else
304                 {
305                     // process message and send response
306                     auto response =
307                         processRxMsg(requestMsg, invoker, dbusImplReq);
308                     if (!response.empty())
309                     {
310                         if (verbose)
311                         {
312                             std::cout << "Sending Msg" << std::endl;
313                             printBuffer(response, verbose);
314                         }
315 
316                         iov[0].iov_base = &requestMsg[0];
317                         iov[0].iov_len =
318                             sizeof(requestMsg[0]) + sizeof(requestMsg[1]);
319                         iov[1].iov_base = response.data();
320                         iov[1].iov_len = response.size();
321 
322                         msg.msg_iov = iov;
323                         msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]);
324 
325                         int result = sendmsg(fd, &msg, 0);
326                         if (-1 == result)
327                         {
328                             returnCode = -errno;
329                             std::cerr << "sendto system call failed, RC= "
330                                       << returnCode << "\n";
331                         }
332                     }
333                 }
334             }
335             else
336             {
337                 std::cerr
338                     << "Failure to read peeked length packet. peekedLength= "
339                     << peekedLength << " recvDataLength=" << recvDataLength
340                     << "\n";
341             }
342         }
343     };
344 
345     bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
346     bus.request_name("xyz.openbmc_project.PLDM");
347     IO io(event, socketFd(), EPOLLIN, std::move(callback));
348     event.loop();
349 
350     result = shutdown(sockfd, SHUT_RDWR);
351     if (-1 == result)
352     {
353         returnCode = -errno;
354         std::cerr << "Failed to shutdown the socket, RC=" << returnCode << "\n";
355         exit(EXIT_FAILURE);
356     }
357     exit(EXIT_FAILURE);
358 }
359