1 #include "command_table.hpp" 2 3 #include "main.hpp" 4 #include "message_handler.hpp" 5 #include "message_parsers.hpp" 6 #include "sessions_manager.hpp" 7 8 #include <iomanip> 9 #include <phosphor-logging/elog-errors.hpp> 10 #include <phosphor-logging/log.hpp> 11 #include <xyz/openbmc_project/Common/error.hpp> 12 13 using namespace phosphor::logging; 14 15 namespace command 16 { 17 18 void Table::registerCommand(CommandID inCommand, std::unique_ptr<Entry>&& entry) 19 { 20 auto& command = commandTable[inCommand.command]; 21 22 if (command) 23 { 24 log<level::DEBUG>( 25 "Already Registered", 26 phosphor::logging::entry("SKIPPED_ENTRY=0x%x", 27 uint32_t(inCommand.command))); 28 return; 29 } 30 31 command = std::move(entry); 32 } 33 34 std::vector<uint8_t> Table::executeCommand(uint32_t inCommand, 35 std::vector<uint8_t>& commandData, 36 const message::Handler& handler) 37 { 38 using namespace std::chrono_literals; 39 40 std::vector<uint8_t> response; 41 42 auto iterator = commandTable.find(inCommand); 43 44 if (iterator == commandTable.end()) 45 { 46 response.resize(1); 47 response[0] = IPMI_CC_INVALID; 48 } 49 else 50 { 51 auto start = std::chrono::steady_clock::now(); 52 53 response = iterator->second->executeCommand(commandData, handler); 54 55 auto end = std::chrono::steady_clock::now(); 56 57 auto elapsedSeconds = 58 std::chrono::duration_cast<std::chrono::seconds>(end - start); 59 60 // If command time execution time exceeds 2 seconds, log a time 61 // exceeded message 62 if (elapsedSeconds > 2s) 63 { 64 log<level::ERR>("IPMI command timed out", 65 entry("DELAY=%d", elapsedSeconds.count())); 66 } 67 } 68 return response; 69 } 70 71 std::vector<uint8_t> 72 NetIpmidEntry::executeCommand(std::vector<uint8_t>& commandData, 73 const message::Handler& handler) 74 { 75 std::vector<uint8_t> errResponse; 76 77 // Check if the command qualifies to be run prior to establishing a session 78 if (!sessionless && (handler.sessionID == session::SESSION_ZERO)) 79 { 80 errResponse.resize(1); 81 errResponse[0] = IPMI_CC_INSUFFICIENT_PRIVILEGE; 82 log<level::INFO>("Table: Insufficient privilege for command", 83 entry("LUN=%x", int(command.NetFnLun.lun)), 84 entry("NETFN=%x", int(command.NetFnLun.netFn)), 85 entry("CMD=%x", command.cmd)); 86 return errResponse; 87 } 88 89 return functor(commandData, handler); 90 } 91 92 std::vector<uint8_t> 93 ProviderIpmidEntry::executeCommand(std::vector<uint8_t>& commandData, 94 const message::Handler& handler) 95 { 96 std::vector<uint8_t> response(message::parser::MAX_PAYLOAD_SIZE - 1); 97 size_t respSize = commandData.size(); 98 ipmi_ret_t ipmiRC = IPMI_CC_UNSPECIFIED_ERROR; 99 std::shared_ptr<session::Session> session = 100 std::get<session::Manager&>(singletonPool) 101 .getSession(handler.sessionID); 102 103 if (session->curPrivLevel >= Entry::getPrivilege()) 104 { 105 try 106 { 107 ipmiRC = functor(0, 0, reinterpret_cast<void*>(commandData.data()), 108 reinterpret_cast<void*>(response.data() + 1), 109 &respSize, NULL); 110 } 111 // IPMI command handlers can throw unhandled exceptions, catch those 112 // and return sane error code. 113 catch (const std::exception& e) 114 { 115 log<level::ERR>("Table: Unspecified error for command", 116 entry("EXCEPTION=%s", e.what()), 117 entry("LUN=%x", int(command.NetFnLun.lun)), 118 entry("NETFN=%x", int(command.NetFnLun.netFn)), 119 entry("CMD=%x", command.cmd)); 120 respSize = 0; 121 // fall through 122 } 123 } 124 else 125 { 126 respSize = 0; 127 ipmiRC = IPMI_CC_INSUFFICIENT_PRIVILEGE; 128 } 129 /* 130 * respSize gets you the size of the response data for the IPMI command. The 131 * first byte in a response to the IPMI command is the Completion Code. 132 * So we are inserting completion code as the first byte and incrementing 133 * the response payload size by the size of the completion code. 134 */ 135 response[0] = ipmiRC; 136 response.resize(respSize + sizeof(ipmi_ret_t)); 137 138 return response; 139 } 140 141 } // namespace command 142