1 #pragma once
2 
3 #include "message_handler.hpp"
4 
5 #include <ipmid/api.h>
6 
7 #include <cstddef>
8 #include <functional>
9 #include <map>
10 
11 namespace command
12 {
13 
14 struct CommandID
15 {
16     static constexpr size_t lunBits = 2;
17     CommandID(uint32_t command) : command(command)
18     {
19     }
20 
21     uint8_t netFnLun() const
22     {
23         return static_cast<uint8_t>(command >> CHAR_BIT);
24     }
25     uint8_t netFn() const
26     {
27         return netFnLun() >> lunBits;
28     }
29     uint8_t lun() const
30     {
31         return netFnLun() & ((1 << (lunBits + 1)) - 1);
32     }
33     uint8_t cmd() const
34     {
35         return static_cast<uint8_t>(command);
36     }
37     uint32_t command;
38 };
39 
40 /**
41  * CommandFunctor is the functor register for commands defined in
42  * phosphor-net-ipmid. This would take the request part of the command as a
43  * vector and a reference to the message handler. The response part of the
44  * command is returned as a vector.
45  */
46 using CommandFunctor = std::function<std::vector<uint8_t>(
47     const std::vector<uint8_t>&, std::shared_ptr<message::Handler>&)>;
48 
49 /**
50  * @struct CmdDetails
51  *
52  * Command details is used to register commands supported in phosphor-net-ipmid.
53  */
54 struct CmdDetails
55 {
56     CommandID command;
57     CommandFunctor functor;
58     session::Privilege privilege;
59     bool sessionless;
60 };
61 
62 /**
63  * @enum NetFns
64  *
65  * A field that identifies the functional class of the message. The Network
66  * Function clusters IPMI commands into different sets.
67  */
68 enum class NetFns
69 {
70     CHASSIS = (0x00 << 10),
71     CHASSIS_RESP = (0x01 << 10),
72 
73     BRIDGE = (0x02 << 10),
74     BRIDGE_RESP = (0x03 << 10),
75 
76     SENSOR = (0x04 << 10),
77     SENSOR_RESP = (0x05 << 10),
78     EVENT = (0x04 << 10),
79     EVENT_RESP = (0x05 << 10),
80 
81     APP = (0x06 << 10),
82     APP_RESP = (0x07 << 10),
83 
84     FIRMWARE = (0x08 << 10),
85     FIRMWARE_RESP = (0x09 << 10),
86 
87     STORAGE = (0x0A << 10),
88     STORAGE_RESP = (0x0B << 10),
89 
90     TRANSPORT = (0x0C << 10),
91     TRANSPORT_RESP = (0x0D << 10),
92 
93     //>>
94     RESERVED_START = (0x0E << 10),
95     RESERVED_END = (0x2B << 10),
96     //<<
97 
98     GROUP_EXTN = (0x2C << 10),
99     GROUP_EXTN_RESP = (0x2D << 10),
100 
101     OEM = (0x2E << 10),
102     OEM_RESP = (0x2F << 10),
103 };
104 
105 /**
106  * @class Entry
107  *
108  * This is the base class for registering IPMI commands. There are two ways of
109  * registering commands to phosphor-net-ipmid, the session related commands and
110  * provider commands
111  *
112  * Every commands has a privilege level which mentions the minimum session
113  * privilege level needed to execute the command
114  */
115 
116 class Entry
117 {
118 
119   public:
120     Entry(CommandID command, session::Privilege privilege) :
121         command(command), privilege(privilege)
122     {
123     }
124 
125     /**
126      * @brief Execute the command
127      *
128      * Execute the command
129      *
130      * @param[in] commandData - Request Data for the command
131      * @param[in] handler - Reference to the Message Handler
132      *
133      * @return Response data for the command
134      */
135     virtual std::vector<uint8_t>
136         executeCommand(std::vector<uint8_t>& commandData,
137                        std::shared_ptr<message::Handler> handler) = 0;
138 
139     auto getCommand() const
140     {
141         return command;
142     }
143 
144     auto getPrivilege() const
145     {
146         return privilege;
147     }
148 
149     virtual ~Entry() = default;
150     Entry(const Entry&) = default;
151     Entry& operator=(const Entry&) = default;
152     Entry(Entry&&) = default;
153     Entry& operator=(Entry&&) = default;
154 
155   protected:
156     CommandID command;
157 
158     // Specifies the minimum privilege level required to execute this command
159     session::Privilege privilege;
160 };
161 
162 /**
163  * @class NetIpmidEntry
164  *
165  * NetIpmidEntry is used to register commands that are consumed only in
166  * phosphor-net-ipmid. The RAKP commands, session commands and user management
167  * commands are examples of this.
168  *
169  * There are certain IPMI commands that can be executed before session can be
170  * established like Get System GUID, Get Channel Authentication Capabilities
171  * and RAKP commands.
172  */
173 class NetIpmidEntry final : public Entry
174 {
175 
176   public:
177     NetIpmidEntry(CommandID command, CommandFunctor functor,
178                   session::Privilege privilege, bool sessionless) :
179         Entry(command, privilege),
180         functor(functor), sessionless(sessionless)
181     {
182     }
183 
184     /**
185      * @brief Execute the command
186      *
187      * Execute the command
188      *
189      * @param[in] commandData - Request Data for the command
190      * @param[in] handler - Reference to the Message Handler
191      *
192      * @return Response data for the command
193      */
194     std::vector<uint8_t>
195         executeCommand(std::vector<uint8_t>& commandData,
196                        std::shared_ptr<message::Handler> handler) override;
197 
198     virtual ~NetIpmidEntry() = default;
199     NetIpmidEntry(const NetIpmidEntry&) = default;
200     NetIpmidEntry& operator=(const NetIpmidEntry&) = default;
201     NetIpmidEntry(NetIpmidEntry&&) = default;
202     NetIpmidEntry& operator=(NetIpmidEntry&&) = default;
203 
204   private:
205     CommandFunctor functor;
206 
207     bool sessionless;
208 };
209 
210 /**
211  * @class Table
212  *
213  * Table keeps the IPMI command entries as a sorted associative container with
214  * Command ID as the unique key. It has interfaces for registering commands
215  * and executing a command.
216  */
217 class Table
218 {
219   private:
220     struct Private
221     {
222     };
223 
224   public:
225     explicit Table(const Private&)
226     {
227     }
228     Table() = delete;
229     ~Table() = default;
230     // Command Table is a singleton so copy, copy-assignment, move and
231     // move assignment is deleted
232     Table(const Table&) = delete;
233     Table& operator=(const Table&) = delete;
234     Table(Table&&) = default;
235     Table& operator=(Table&&) = default;
236 
237     /**
238      * @brief Get a reference to the singleton Table
239      *
240      * @return Table reference
241      */
242     static Table& get()
243     {
244         static std::shared_ptr<Table> ptr = nullptr;
245         if (!ptr)
246         {
247             ptr = std::make_shared<Table>(Private());
248         }
249         return *ptr;
250     }
251 
252     using CommandTable = std::map<uint32_t, std::unique_ptr<Entry>>;
253 
254     /**
255      * @brief Register a command
256      *
257      * Register a command with the command table
258      *
259      * @param[in] inCommand - Command ID
260      * @param[in] entry - Command Entry
261      *
262      * @return: None
263      *
264      * @note: Duplicate registrations will be rejected.
265      *
266      */
267     void registerCommand(CommandID inCommand, std::unique_ptr<Entry>&& entry);
268 
269     /**
270      * @brief Execute the command
271      *
272      * Execute the command for the corresponding CommandID
273      *
274      * @param[in] inCommand - Command ID to execute.
275      * @param[in] commandData - Request Data for the command
276      * @param[in] handler - Reference to the Message Handler
277      *
278      */
279     void executeCommand(uint32_t inCommand, std::vector<uint8_t>& commandData,
280                         std::shared_ptr<message::Handler> handler);
281 
282   private:
283     CommandTable commandTable;
284 };
285 
286 } // namespace command
287