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