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