1 2 #include "common/flight_recorder.hpp" 3 #include "common/instance_id.hpp" 4 #include "common/transport.hpp" 5 #include "common/utils.hpp" 6 #include "fw-update/manager.hpp" 7 #include "invoker.hpp" 8 #include "platform-mc/dbus_to_terminus_effecters.hpp" 9 #include "platform-mc/manager.hpp" 10 #include "requester/handler.hpp" 11 #include "requester/mctp_endpoint_discovery.hpp" 12 #include "requester/request.hpp" 13 14 #include <err.h> 15 #include <getopt.h> 16 #include <libpldm/base.h> 17 #include <libpldm/bios.h> 18 #include <libpldm/pdr.h> 19 #include <libpldm/platform.h> 20 #include <libpldm/transport.h> 21 #include <poll.h> 22 #include <sys/socket.h> 23 #include <sys/types.h> 24 #include <sys/un.h> 25 #include <unistd.h> 26 27 #include <phosphor-logging/lg2.hpp> 28 #include <sdeventplus/event.hpp> 29 #include <sdeventplus/source/io.hpp> 30 #include <sdeventplus/source/signal.hpp> 31 #include <stdplus/signal.hpp> 32 33 #include <cstdio> 34 #include <cstdlib> 35 #include <cstring> 36 #include <fstream> 37 #include <iomanip> 38 #include <iterator> 39 #include <memory> 40 #include <ranges> 41 #include <sstream> 42 #include <stdexcept> 43 #include <string> 44 #include <vector> 45 46 PHOSPHOR_LOG2_USING; 47 48 #ifdef LIBPLDMRESPONDER 49 #include "dbus_impl_pdr.hpp" 50 #include "host-bmc/dbus_to_event_handler.hpp" 51 #include "host-bmc/host_condition.hpp" 52 #include "host-bmc/host_pdr_handler.hpp" 53 #include "libpldmresponder/base.hpp" 54 #include "libpldmresponder/bios.hpp" 55 #include "libpldmresponder/fru.hpp" 56 #include "libpldmresponder/oem_handler.hpp" 57 #include "libpldmresponder/platform.hpp" 58 #include "libpldmresponder/platform_config.hpp" 59 #include "xyz/openbmc_project/PLDM/Event/server.hpp" 60 #endif 61 62 #ifdef OEM_IBM 63 #include "oem_ibm.hpp" 64 #endif 65 66 #ifdef OEM_AMPERE 67 #include "oem/ampere/oem_ampere.hpp" 68 #endif 69 70 constexpr const char* PLDMService = "xyz.openbmc_project.PLDM"; 71 72 using namespace pldm; 73 using namespace sdeventplus; 74 using namespace sdeventplus::source; 75 using namespace pldm::responder; 76 using namespace pldm::utils; 77 using sdeventplus::source::Signal; 78 using namespace pldm::flightrecorder; 79 80 void interruptFlightRecorderCallBack(Signal& /*signal*/, 81 const struct signalfd_siginfo*) 82 { 83 error("Received SIGUR1(10) Signal interrupt"); 84 // obtain the flight recorder instance and dump the recorder 85 FlightRecorder::GetInstance().playRecorder(); 86 } 87 88 void requestPLDMServiceName() 89 { 90 try 91 { 92 auto& bus = pldm::utils::DBusHandler::getBus(); 93 bus.request_name(PLDMService); 94 } 95 catch (const sdbusplus::exception_t& e) 96 { 97 error("Failed to request D-Bus name {NAME} with error {ERROR}.", "NAME", 98 PLDMService, "ERROR", e); 99 } 100 } 101 102 static std::optional<Response> processRxMsg( 103 const std::vector<uint8_t>& requestMsg, Invoker& invoker, 104 requester::Handler<requester::Request>& handler, 105 fw_update::Manager* fwManager, pldm_tid_t tid) 106 { 107 uint8_t eid = tid; 108 109 pldm_header_info hdrFields{}; 110 auto hdr = reinterpret_cast<const pldm_msg_hdr*>(requestMsg.data()); 111 if (PLDM_SUCCESS != unpack_pldm_header(hdr, &hdrFields)) 112 { 113 error("Empty PLDM request header"); 114 return std::nullopt; 115 } 116 117 if (PLDM_RESPONSE != hdrFields.msg_type) 118 { 119 Response response; 120 auto request = reinterpret_cast<const pldm_msg*>(hdr); 121 size_t requestLen = requestMsg.size() - sizeof(struct pldm_msg_hdr); 122 try 123 { 124 if (hdrFields.pldm_type != PLDM_FWUP) 125 { 126 response = 127 invoker.handle(tid, hdrFields.pldm_type, hdrFields.command, 128 request, requestLen); 129 } 130 else 131 { 132 response = fwManager->handleRequest(eid, hdrFields.command, 133 request, requestLen); 134 } 135 } 136 catch (const std::out_of_range& e) 137 { 138 uint8_t completion_code = PLDM_ERROR_UNSUPPORTED_PLDM_CMD; 139 response.resize(sizeof(pldm_msg_hdr)); 140 auto responseHdr = new (response.data()) pldm_msg_hdr; 141 pldm_header_info header{}; 142 header.msg_type = PLDM_RESPONSE; 143 header.instance = hdrFields.instance; 144 header.pldm_type = hdrFields.pldm_type; 145 header.command = hdrFields.command; 146 if (PLDM_SUCCESS != pack_pldm_header(&header, responseHdr)) 147 { 148 error( 149 "Failed to add response header for processing Rx, error - {ERROR}", 150 "ERROR", e); 151 return std::nullopt; 152 } 153 response.insert(response.end(), completion_code); 154 } 155 return response; 156 } 157 else if (PLDM_RESPONSE == hdrFields.msg_type) 158 { 159 auto response = reinterpret_cast<const pldm_msg*>(hdr); 160 size_t responseLen = requestMsg.size() - sizeof(struct pldm_msg_hdr); 161 handler.handleResponse(eid, hdrFields.instance, hdrFields.pldm_type, 162 hdrFields.command, response, responseLen); 163 } 164 return std::nullopt; 165 } 166 167 void optionUsage(void) 168 { 169 info("Usage: pldmd [options]"); 170 info("Options:"); 171 info(" [--verbose] - would enable verbosity"); 172 } 173 174 int main(int argc, char** argv) 175 { 176 bool verbose = false; 177 static struct option long_options[] = { 178 {"verbose", no_argument, nullptr, 'v'}, {nullptr, 0, nullptr, 0}}; 179 180 auto argflag = getopt_long(argc, argv, "v", long_options, nullptr); 181 switch (argflag) 182 { 183 case 'v': 184 verbose = true; 185 break; 186 case -1: 187 break; 188 default: 189 optionUsage(); 190 exit(EXIT_FAILURE); 191 } 192 // Setup PLDM requester transport 193 auto hostEID = pldm::utils::readHostEID(); 194 /* To maintain current behaviour until we have the infrastructure to find 195 * and use the correct TIDs */ 196 pldm_tid_t TID = hostEID; 197 PldmTransport pldmTransport{}; 198 auto event = Event::get_default(); 199 auto& bus = pldm::utils::DBusHandler::getBus(); 200 sdbusplus::server::manager_t objManager(bus, 201 "/xyz/openbmc_project/software"); 202 sdbusplus::server::manager_t sensorObjManager( 203 bus, "/xyz/openbmc_project/sensors"); 204 205 InstanceIdDb instanceIdDb; 206 sdbusplus::server::manager_t inventoryManager( 207 bus, "/xyz/openbmc_project/inventory"); 208 209 Invoker invoker{}; 210 requester::Handler<requester::Request> reqHandler(&pldmTransport, event, 211 instanceIdDb, verbose); 212 213 std::unique_ptr<pldm_pdr, decltype(&pldm_pdr_destroy)> pdrRepo( 214 pldm_pdr_init(), pldm_pdr_destroy); 215 if (!pdrRepo) 216 { 217 throw std::runtime_error("Failed to instantiate PDR repository"); 218 } 219 DBusHandler dbusHandler; 220 221 std::unique_ptr<platform_mc::Manager> platformManager = 222 std::make_unique<platform_mc::Manager>(event, reqHandler, instanceIdDb); 223 224 std::unique_ptr<pldm::host_effecters::HostEffecterParser> 225 hostEffecterParser = 226 std::make_unique<pldm::host_effecters::HostEffecterParser>( 227 &instanceIdDb, pldmTransport.getEventSource(), pdrRepo.get(), 228 &dbusHandler, HOST_JSONS_DIR, &reqHandler, 229 platformManager.get()); 230 #ifdef LIBPLDMRESPONDER 231 using namespace pldm::state_sensor; 232 dbus_api::Host dbusImplHost(bus, "/xyz/openbmc_project/pldm"); 233 std::unique_ptr<pldm_entity_association_tree, 234 decltype(&pldm_entity_association_tree_destroy)> 235 entityTree(pldm_entity_association_tree_init(), 236 pldm_entity_association_tree_destroy); 237 if (!entityTree) 238 { 239 throw std::runtime_error( 240 "Failed to instantiate general PDR entity association tree"); 241 } 242 std::unique_ptr<pldm_entity_association_tree, 243 decltype(&pldm_entity_association_tree_destroy)> 244 bmcEntityTree(pldm_entity_association_tree_init(), 245 pldm_entity_association_tree_destroy); 246 if (!bmcEntityTree) 247 { 248 throw std::runtime_error( 249 "Failed to instantiate BMC PDR entity association tree"); 250 } 251 std::shared_ptr<HostPDRHandler> hostPDRHandler; 252 std::unique_ptr<DbusToPLDMEvent> dbusToPLDMEventHandler; 253 std::unique_ptr<platform_config::Handler> platformConfigHandler{}; 254 platformConfigHandler = 255 std::make_unique<platform_config::Handler>(PDR_JSONS_DIR); 256 257 if (hostEID) 258 { 259 hostPDRHandler = std::make_shared<HostPDRHandler>( 260 pldmTransport.getEventSource(), hostEID, event, pdrRepo.get(), 261 EVENTS_JSONS_DIR, entityTree.get(), bmcEntityTree.get(), 262 instanceIdDb, &reqHandler); 263 264 // HostFirmware interface needs access to hostPDR to know if host 265 // is running 266 dbusImplHost.setHostPdrObj(hostPDRHandler); 267 268 dbusToPLDMEventHandler = std::make_unique<DbusToPLDMEvent>( 269 pldmTransport.getEventSource(), hostEID, instanceIdDb, &reqHandler); 270 } 271 272 auto fruHandler = std::make_unique<fru::Handler>( 273 FRU_JSONS_DIR, FRU_MASTER_JSON, pdrRepo.get(), entityTree.get(), 274 bmcEntityTree.get()); 275 276 // FRU table is built lazily when a FRU command or Get PDR command is 277 // handled. To enable building FRU table, the FRU handler is passed to the 278 // Platform handler. 279 280 pldm::responder::platform::EventMap addOnEventHandlers{ 281 {PLDM_CPER_EVENT, 282 {[&platformManager](const pldm_msg* request, size_t payloadLength, 283 uint8_t formatVersion, uint8_t tid, 284 size_t eventDataOffset) { 285 return platformManager->handleCperEvent( 286 request, payloadLength, formatVersion, tid, eventDataOffset); 287 }}}, 288 {PLDM_MESSAGE_POLL_EVENT, 289 {[&platformManager](const pldm_msg* request, size_t payloadLength, 290 uint8_t formatVersion, uint8_t tid, 291 size_t eventDataOffset) { 292 return platformManager->handlePldmMessagePollEvent( 293 request, payloadLength, formatVersion, tid, eventDataOffset); 294 }}}, 295 {PLDM_SENSOR_EVENT, 296 {[&platformManager](const pldm_msg* request, size_t payloadLength, 297 uint8_t formatVersion, uint8_t tid, 298 size_t eventDataOffset) { 299 return platformManager->handleSensorEvent( 300 request, payloadLength, formatVersion, tid, eventDataOffset); 301 }}}}; 302 303 auto platformHandler = std::make_unique<platform::Handler>( 304 &dbusHandler, hostEID, &instanceIdDb, PDR_JSONS_DIR, pdrRepo.get(), 305 hostPDRHandler.get(), dbusToPLDMEventHandler.get(), fruHandler.get(), 306 platformConfigHandler.get(), &reqHandler, event, true, 307 addOnEventHandlers); 308 309 auto biosHandler = std::make_unique<bios::Handler>( 310 pldmTransport.getEventSource(), hostEID, &instanceIdDb, &reqHandler, 311 platformConfigHandler.get(), requestPLDMServiceName); 312 313 auto baseHandler = std::make_unique<base::Handler>(event); 314 315 #ifdef OEM_AMPERE 316 pldm::oem_ampere::OemAMPERE oemAMPERE( 317 &dbusHandler, pldmTransport.getEventSource(), pdrRepo.get(), 318 instanceIdDb, event, invoker, hostPDRHandler.get(), 319 platformHandler.get(), fruHandler.get(), baseHandler.get(), 320 biosHandler.get(), platformManager.get(), &reqHandler); 321 #endif 322 323 #ifdef OEM_IBM 324 pldm::oem_ibm::OemIBM oemIBM( 325 &dbusHandler, pldmTransport.getEventSource(), hostEID, pdrRepo.get(), 326 instanceIdDb, event, invoker, hostPDRHandler.get(), 327 platformHandler.get(), fruHandler.get(), baseHandler.get(), 328 &reqHandler); 329 #endif 330 331 invoker.registerHandler(PLDM_BIOS, std::move(biosHandler)); 332 invoker.registerHandler(PLDM_PLATFORM, std::move(platformHandler)); 333 invoker.registerHandler(PLDM_FRU, std::move(fruHandler)); 334 invoker.registerHandler(PLDM_BASE, std::move(baseHandler)); 335 336 dbus_api::Pdr dbusImplPdr(bus, "/xyz/openbmc_project/pldm", pdrRepo.get()); 337 sdbusplus::xyz::openbmc_project::PLDM::server::Event dbusImplEvent( 338 bus, "/xyz/openbmc_project/pldm"); 339 340 #endif 341 342 std::unique_ptr<fw_update::Manager> fwManager = 343 std::make_unique<fw_update::Manager>(event, reqHandler, instanceIdDb); 344 std::unique_ptr<MctpDiscovery> mctpDiscoveryHandler = 345 std::make_unique<MctpDiscovery>( 346 bus, std::initializer_list<MctpDiscoveryHandlerIntf*>{ 347 fwManager.get(), platformManager.get()}); 348 auto callback = [verbose, &invoker, &reqHandler, &fwManager, &pldmTransport, 349 TID](IO& io, int fd, uint32_t revents) mutable { 350 if (!(revents & EPOLLIN)) 351 { 352 return; 353 } 354 if (fd < 0) 355 { 356 return; 357 } 358 359 int returnCode = 0; 360 void* requestMsg; 361 size_t recvDataLength; 362 returnCode = pldmTransport.recvMsg(TID, requestMsg, recvDataLength); 363 364 if (returnCode == PLDM_REQUESTER_SUCCESS) 365 { 366 std::vector<uint8_t> requestMsgVec( 367 static_cast<uint8_t*>(requestMsg), 368 static_cast<uint8_t*>(requestMsg) + recvDataLength); 369 FlightRecorder::GetInstance().saveRecord(requestMsgVec, false); 370 if (verbose) 371 { 372 printBuffer(Rx, requestMsgVec); 373 } 374 // process message and send response 375 auto response = processRxMsg(requestMsgVec, invoker, reqHandler, 376 fwManager.get(), TID); 377 if (response.has_value()) 378 { 379 FlightRecorder::GetInstance().saveRecord(*response, true); 380 if (verbose) 381 { 382 printBuffer(Tx, *response); 383 } 384 385 returnCode = pldmTransport.sendMsg(TID, (*response).data(), 386 (*response).size()); 387 if (returnCode != PLDM_REQUESTER_SUCCESS) 388 { 389 warning( 390 "Failed to send pldmTransport message for TID '{TID}', response code '{RETURN_CODE}'", 391 "TID", TID, "RETURN_CODE", returnCode); 392 } 393 } 394 } 395 // TODO check that we get here if mctp-demux dies? 396 else if (returnCode == PLDM_REQUESTER_RECV_FAIL) 397 { 398 // MCTP daemon has closed the socket this daemon is connected to. 399 // This may or may not be an error scenario, in either case the 400 // recovery mechanism for this daemon is to restart, and hence exit 401 // the event loop, that will cause this daemon to exit with a 402 // failure code. 403 error( 404 "MCTP daemon closed the socket, IO exiting with response code '{RC}'", 405 "RC", returnCode); 406 io.get_event().exit(0); 407 } 408 else 409 { 410 warning( 411 "Failed to receive PLDM request for pldmTransport, response code '{RETURN_CODE}'", 412 "RETURN_CODE", returnCode); 413 } 414 /* Free requestMsg after using */ 415 free(requestMsg); 416 }; 417 418 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL); 419 #ifndef SYSTEM_SPECIFIC_BIOS_JSON 420 try 421 { 422 bus.request_name(PLDMService); 423 } 424 catch (const sdbusplus::exception_t& e) 425 { 426 error("Failed to request D-Bus name {NAME} with error {ERROR}.", "NAME", 427 PLDMService, "ERROR", e); 428 } 429 #endif 430 IO io(event, pldmTransport.getEventSource(), EPOLLIN, std::move(callback)); 431 #ifdef LIBPLDMRESPONDER 432 if (hostPDRHandler) 433 { 434 hostPDRHandler->setHostFirmwareCondition(); 435 } 436 #endif 437 stdplus::signal::block(SIGUSR1); 438 sdeventplus::source::Signal sigUsr1( 439 event, SIGUSR1, std::bind_front(&interruptFlightRecorderCallBack)); 440 int returnCode = event.loop(); 441 if (returnCode) 442 { 443 exit(EXIT_FAILURE); 444 } 445 446 exit(EXIT_SUCCESS); 447 } 448