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