xref: /openbmc/phosphor-net-ipmid/command/payload_cmds.cpp (revision 067d178bcc0c572afad75ceadf3c55f293639e8a)
1 #include "payload_cmds.hpp"
2 
3 #include "sessions_manager.hpp"
4 #include "sol/sol_manager.hpp"
5 #include "sol_cmds.hpp"
6 
7 #include <ipmid/api.h>
8 
9 #include <ipmid/api-types.hpp>
10 #include <phosphor-logging/lg2.hpp>
11 
12 namespace sol
13 {
14 
15 namespace command
16 {
17 
activatePayload(const std::vector<uint8_t> & inPayload,std::shared_ptr<message::Handler> & handler)18 std::vector<uint8_t> activatePayload(const std::vector<uint8_t>& inPayload,
19                                      std::shared_ptr<message::Handler>& handler)
20 {
21     auto request =
22         reinterpret_cast<const ActivatePayloadRequest*>(inPayload.data());
23     if (inPayload.size() != sizeof(*request))
24     {
25         std::vector<uint8_t> errorPayload{IPMI_CC_REQ_DATA_LEN_INVALID};
26         return errorPayload;
27     }
28 
29     std::vector<uint8_t> outPayload(sizeof(ActivatePayloadResponse));
30     auto response =
31         reinterpret_cast<ActivatePayloadResponse*>(outPayload.data());
32 
33     response->completionCode = ipmi::ccSuccess;
34 
35     // SOL is the payload currently supported for activation.
36     if (static_cast<uint8_t>(message::PayloadType::SOL) != request->payloadType)
37     {
38         response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST;
39         return outPayload;
40     }
41 
42     sol::Manager::get().updateSOLParameter(ipmi::convertCurrentChannelNum(
43         ipmi::currentChNum, getInterfaceIndex()));
44     if (!sol::Manager::get().enable)
45     {
46         response->completionCode = IPMI_CC_PAYLOAD_TYPE_DISABLED;
47         return outPayload;
48     }
49 
50     // Only one instance of SOL is currently supported.
51     if (request->payloadInstance != 1)
52     {
53         response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST;
54         return outPayload;
55     }
56 
57     auto session = session::Manager::get().getSession(handler->sessionID);
58 
59     if (!request->encryption && session->isCryptAlgoEnabled())
60     {
61         response->completionCode = IPMI_CC_PAYLOAD_WITHOUT_ENCRYPTION;
62         return outPayload;
63     }
64 
65     if (session->currentPrivilege() <
66         static_cast<uint8_t>(sol::Manager::get().solMinPrivilege))
67     {
68         response->completionCode = IPMI_CC_INSUFFICIENT_PRIVILEGE;
69         return outPayload;
70     }
71 
72     // Is SOL Payload enabled for this user & channel.
73     auto userId = ipmi::ipmiUserGetUserId(session->userName);
74     ipmi::PayloadAccess payloadAccess = {};
75     if ((ipmi::ipmiUserGetUserPayloadAccess(session->channelNum(), userId,
76                                             payloadAccess) !=
77          ipmi::ccSuccess) ||
78         !(payloadAccess.stdPayloadEnables1[static_cast<uint8_t>(
79             message::PayloadType::SOL)]))
80     {
81         response->completionCode = IPMI_CC_PAYLOAD_TYPE_DISABLED;
82         return outPayload;
83     }
84 
85     auto status = sol::Manager::get().isPayloadActive(request->payloadInstance);
86     if (status)
87     {
88         response->completionCode = IPMI_CC_PAYLOAD_ALREADY_ACTIVE;
89         return outPayload;
90     }
91 
92     // Set the current command's socket channel to the session
93     handler->setChannelInSession();
94 
95     // Start the SOL payload
96     try
97     {
98         sol::Manager::get().startPayloadInstance(request->payloadInstance,
99                                                  handler->sessionID);
100     }
101     catch (const std::exception& e)
102     {
103         lg2::error("Failed to start SOL payload: {ERROR}", "ERROR", e);
104         response->completionCode = IPMI_CC_UNSPECIFIED_ERROR;
105         return outPayload;
106     }
107 
108     response->inPayloadSize = endian::to_ipmi<uint16_t>(MAX_PAYLOAD_SIZE);
109     response->outPayloadSize = endian::to_ipmi<uint16_t>(MAX_PAYLOAD_SIZE);
110     response->portNum = endian::to_ipmi<uint16_t>(IPMI_STD_PORT);
111 
112     // VLAN addressing is not used
113     response->vlanNum = 0xFFFF;
114 
115     return outPayload;
116 }
117 
deactivatePayload(const std::vector<uint8_t> & inPayload,std::shared_ptr<message::Handler> & handler)118 std::vector<uint8_t> deactivatePayload(
119     const std::vector<uint8_t>& inPayload,
120     std::shared_ptr<message::Handler>& handler)
121 {
122     auto request =
123         reinterpret_cast<const DeactivatePayloadRequest*>(inPayload.data());
124     if (inPayload.size() != sizeof(*request))
125     {
126         std::vector<uint8_t> errorPayload{IPMI_CC_REQ_DATA_LEN_INVALID};
127         return errorPayload;
128     }
129 
130     std::vector<uint8_t> outPayload(sizeof(DeactivatePayloadResponse));
131     auto response =
132         reinterpret_cast<DeactivatePayloadResponse*>(outPayload.data());
133     response->completionCode = ipmi::ccSuccess;
134 
135     // SOL is the payload currently supported for deactivation
136     if (static_cast<uint8_t>(message::PayloadType::SOL) != request->payloadType)
137     {
138         response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST;
139         return outPayload;
140     }
141 
142     // Only one instance of SOL is supported
143     if (request->payloadInstance != 1)
144     {
145         response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST;
146         return outPayload;
147     }
148 
149     auto status = sol::Manager::get().isPayloadActive(request->payloadInstance);
150     if (!status)
151     {
152         response->completionCode = IPMI_CC_PAYLOAD_DEACTIVATED;
153         return outPayload;
154     }
155 
156     auto currentSession =
157         session::Manager::get().getSession(handler->sessionID);
158     auto solSessionID =
159         sol::Manager::get().getContext(request->payloadInstance).sessionID;
160     auto solActiveSession =
161         sol::Manager::get().getContext(request->payloadInstance).session;
162     // The session owner or the ADMIN could deactivate the session
163     if (currentSession->userName != solActiveSession->userName &&
164         currentSession->currentPrivilege() !=
165             static_cast<uint8_t>(session::Privilege::ADMIN))
166     {
167         response->completionCode = IPMI_CC_INSUFFICIENT_PRIVILEGE;
168         return outPayload;
169     }
170 
171     try
172     {
173         sol::Manager::get().stopPayloadInstance(request->payloadInstance);
174 
175         try
176         {
177             activating(request->payloadInstance, solSessionID);
178         }
179         catch (const std::exception& e)
180         {
181             lg2::info("Failed to call the activating method: {ERROR}", "ERROR",
182                       e);
183             /*
184              * In case session has been closed (like in the case of inactivity
185              * timeout), then activating function would throw an exception,
186              * since solSessionID is not found. As session is already closed,
187              * returning IPMI status code for Payload already deactivated
188              * as BMC automatically deactivates all active payloads when
189              * session is terminated.
190              */
191             response->completionCode = IPMI_CC_PAYLOAD_DEACTIVATED;
192             return outPayload;
193         }
194     }
195     catch (const std::exception& e)
196     {
197         lg2::error("Failed to call the getContext method: {ERROR}", "ERROR", e);
198         response->completionCode = IPMI_CC_UNSPECIFIED_ERROR;
199         return outPayload;
200     }
201 
202     return outPayload;
203 }
204 
getPayloadStatus(const std::vector<uint8_t> & inPayload,std::shared_ptr<message::Handler> &)205 std::vector<uint8_t> getPayloadStatus(
206     const std::vector<uint8_t>& inPayload,
207     std::shared_ptr<message::Handler>& /* handler */)
208 {
209     auto request =
210         reinterpret_cast<const GetPayloadStatusRequest*>(inPayload.data());
211     if (inPayload.size() != sizeof(*request))
212     {
213         std::vector<uint8_t> errorPayload{IPMI_CC_REQ_DATA_LEN_INVALID};
214         return errorPayload;
215     }
216 
217     std::vector<uint8_t> outPayload(sizeof(GetPayloadStatusResponse));
218     auto response =
219         reinterpret_cast<GetPayloadStatusResponse*>(outPayload.data());
220 
221     // SOL is the payload currently supported for payload status
222     if (static_cast<uint8_t>(message::PayloadType::SOL) != request->payloadType)
223     {
224         response->completionCode = IPMI_CC_UNSPECIFIED_ERROR;
225         return outPayload;
226     }
227 
228     response->completionCode = ipmi::ccSuccess;
229 
230     constexpr size_t maxSolPayloadInstances = 1;
231     response->capacity = maxSolPayloadInstances;
232 
233     // Currently we support only one SOL session
234     response->instance1 = sol::Manager::get().isPayloadActive(1);
235 
236     return outPayload;
237 }
238 
getPayloadInfo(const std::vector<uint8_t> & inPayload,std::shared_ptr<message::Handler> &)239 std::vector<uint8_t> getPayloadInfo(
240     const std::vector<uint8_t>& inPayload,
241     std::shared_ptr<message::Handler>& /* handler */)
242 {
243     auto request =
244         reinterpret_cast<const GetPayloadInfoRequest*>(inPayload.data());
245 
246     if (inPayload.size() != sizeof(*request))
247     {
248         std::vector<uint8_t> errorPayload{IPMI_CC_REQ_DATA_LEN_INVALID};
249         return errorPayload;
250     }
251 
252     std::vector<uint8_t> outPayload(sizeof(GetPayloadInfoResponse));
253     auto response =
254         reinterpret_cast<GetPayloadInfoResponse*>(outPayload.data());
255 
256     // SOL is the payload currently supported for payload status & only one
257     // instance of SOL is supported.
258     if (static_cast<uint8_t>(message::PayloadType::SOL) !=
259             request->payloadType ||
260         request->payloadInstance != 1)
261     {
262         response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST;
263         return outPayload;
264     }
265     auto status = sol::Manager::get().isPayloadActive(request->payloadInstance);
266 
267     if (status)
268     {
269         auto& context =
270             sol::Manager::get().getContext(request->payloadInstance);
271         response->sessionID = context.sessionID;
272     }
273     else
274     {
275         // No active payload - return session id as 0
276         response->sessionID = 0;
277     }
278     response->completionCode = ipmi::ccSuccess;
279     return outPayload;
280 }
281 
282 } // namespace command
283 
284 } // namespace sol
285