1 /*
2 // Copyright (c) 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 
17 #include "channelcommands.hpp"
18 
19 #include "apphandler.hpp"
20 #include "channel_layer.hpp"
21 
22 #include <ipmid/api.hpp>
23 #include <phosphor-logging/log.hpp>
24 #include <regex>
25 
26 using namespace phosphor::logging;
27 
28 namespace ipmi
29 {
30 
31 /** @struct GetChannelPayloadSupportReq
32  *
33  *  Structure for get channel payload support command request (refer spec
34  *  sec 24.8)
35  */
36 struct GetChannelPayloadSupportReq
37 {
38 #if BYTE_ORDER == LITTLE_ENDIAN
39     uint8_t chNum : 4;
40     uint8_t reserved : 4;
41 #endif
42 #if BYTE_ORDER == BIG_ENDIAN
43     uint8_t reserved : 4;
44     uint8_t chNum : 4;
45 #endif
46 } __attribute__((packed));
47 
48 /** @struct GetChannelPayloadSupportResp
49  *
50  *  Structure for get channel payload support command response (refer spec
51  *  sec 24.8)
52  */
53 struct GetChannelPayloadSupportResp
54 {
55     uint8_t stdPayloadType[2];
56     uint8_t sessSetupPayloadType[2];
57     uint8_t OEMPayloadType[2];
58     uint8_t reserved[2];
59 } __attribute__((packed));
60 
61 /** @brief implements the set channel access command
62  *  @ param ctx - context pointer
63  *  @ param channel - channel number
64  *  @ param reserved - skip 4 bits
65  *  @ param accessMode - access mode for IPMI messaging
66  *  @ param usrAuth - user level authentication (enable/disable)
67  *  @ param msgAuth - per message authentication (enable/disable)
68  *  @ param alertDisabled - PEF alerting (enable/disable)
69  *  @ param chanAccess - channel access
70  *  @ param channelPrivLimit - channel privilege limit
71  *  @ param reserved - skip 3 bits
72  *  @ param channelPrivMode - channel priviledge mode
73  *
74  *  @ returns IPMI completion code
75  **/
76 RspType<> ipmiSetChannelAccess(Context::ptr ctx, uint4_t channel,
77                                uint4_t reserved1, uint3_t accessMode,
78                                bool usrAuth, bool msgAuth, bool alertDisabled,
79                                uint2_t chanAccess, uint4_t channelPrivLimit,
80                                uint2_t reserved2, uint2_t channelPrivMode)
81 {
82     const uint8_t chNum =
83         convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel);
84 
85     if (!isValidChannel(chNum) || reserved1 != 0 || reserved2 != 0)
86     {
87         log<level::DEBUG>("Set channel access - Invalid field in request");
88         return responseInvalidFieldRequest();
89     }
90 
91     if (getChannelSessionSupport(chNum) == EChannelSessSupported::none)
92     {
93         log<level::DEBUG>("Set channel access - No support on channel");
94         return responseInvalidFieldRequest();
95     }
96 
97     ChannelAccess chActData;
98     ChannelAccess chNVData;
99     uint8_t setActFlag = 0;
100     uint8_t setNVFlag = 0;
101     Cc compCode;
102 
103     // cannot static cast directly from uint2_t to enum; must go via int
104     uint8_t channelAccessAction = static_cast<uint8_t>(chanAccess);
105     switch (static_cast<EChannelActionType>(channelAccessAction))
106     {
107         case doNotSet:
108             break;
109         case nvData:
110             chNVData.accessMode = static_cast<uint8_t>(accessMode);
111             chNVData.userAuthDisabled = usrAuth;
112             chNVData.perMsgAuthDisabled = msgAuth;
113             chNVData.alertingDisabled = alertDisabled;
114             setNVFlag |= (setAccessMode | setUserAuthEnabled |
115                           setMsgAuthEnabled | setAlertingEnabled);
116             break;
117 
118         case activeData:
119             chActData.accessMode = static_cast<uint8_t>(accessMode);
120             chActData.userAuthDisabled = usrAuth;
121             chActData.perMsgAuthDisabled = msgAuth;
122             chActData.alertingDisabled = alertDisabled;
123             setActFlag |= (setAccessMode | setUserAuthEnabled |
124                            setMsgAuthEnabled | setAlertingEnabled);
125             break;
126 
127         case reserved:
128         default:
129             log<level::DEBUG>("Set channel access - Invalid access set mode");
130             return responseInvalidFieldRequest();
131     }
132 
133     // cannot static cast directly from uint2_t to enum; must go via int
134     uint8_t channelPrivAction = static_cast<uint8_t>(channelPrivMode);
135     switch (static_cast<EChannelActionType>(channelPrivAction))
136     {
137         case doNotSet:
138             break;
139         case nvData:
140             chNVData.privLimit = static_cast<uint8_t>(channelPrivLimit);
141             setNVFlag |= setPrivLimit;
142             break;
143         case activeData:
144             chActData.privLimit = static_cast<uint8_t>(channelPrivLimit);
145 
146             setActFlag |= setPrivLimit;
147             break;
148         case reserved:
149         default:
150             log<level::DEBUG>("Set channel access - Invalid access priv mode");
151             return responseInvalidFieldRequest();
152     }
153 
154     if (setNVFlag != 0)
155     {
156         compCode = setChannelAccessPersistData(chNum, chNVData, setNVFlag);
157         if (compCode != IPMI_CC_OK)
158         {
159             log<level::DEBUG>("Set channel access - Failed to set access data");
160             return response(compCode);
161         }
162     }
163 
164     if (setActFlag != 0)
165     {
166         compCode = setChannelAccessData(chNum, chActData, setActFlag);
167         if (compCode != IPMI_CC_OK)
168         {
169             log<level::DEBUG>("Set channel access - Failed to set access data");
170             return response(compCode);
171         }
172     }
173 
174     return responseSuccess();
175 }
176 
177 /** @brief implements the get channel access command
178  *  @ param ctx - context pointer
179  *  @ param channel - channel number
180  *  @ param reserved1 - skip 4 bits
181  *  @ param reserved2 - skip 6 bits
182  *  @ param accessMode - get access mode
183  *
184  *  @returns ipmi completion code plus response data
185  *  - accessMode - get access mode
186  *  - usrAuthDisabled - user level authentication status
187  *  - msgAuthDisabled - message level authentication status
188  *  - alertDisabled - alerting status
189  *  - reserved - skip 2 bits
190  *  - privLimit - channel privilege limit
191  *  - reserved - skip 4 bits
192  * */
193 ipmi ::RspType<uint3_t, // access mode,
194                bool,    // user authentication status,
195                bool,    // message authentication status,
196                bool,    // alerting status,
197                uint2_t, // reserved,
198 
199                uint4_t, // channel privilege,
200                uint4_t  // reserved
201                >
202     ipmiGetChannelAccess(Context::ptr ctx, uint4_t channel, uint4_t reserved1,
203                          uint6_t reserved2, uint2_t accessSetMode)
204 {
205     const uint8_t chNum =
206         convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel);
207 
208     if (!isValidChannel(chNum) || reserved1 != 0 || reserved2 != 0)
209     {
210         log<level::DEBUG>("Get channel access - Invalid field in request");
211         return responseInvalidFieldRequest();
212     }
213 
214     if ((accessSetMode == doNotSet) || (accessSetMode == reserved))
215     {
216         log<level::DEBUG>("Get channel access - Invalid Access mode");
217         return responseInvalidFieldRequest();
218     }
219 
220     if (getChannelSessionSupport(chNum) == EChannelSessSupported::none)
221     {
222         log<level::DEBUG>("Get channel access - No support on channel");
223         return response(IPMI_CC_ACTION_NOT_SUPPORTED_FOR_CHANNEL);
224     }
225 
226     ChannelAccess chAccess;
227 
228     Cc compCode;
229 
230     if (accessSetMode == nvData)
231     {
232         compCode = getChannelAccessPersistData(chNum, chAccess);
233     }
234     else if (accessSetMode == activeData)
235     {
236         compCode = getChannelAccessData(chNum, chAccess);
237     }
238 
239     if (compCode != IPMI_CC_OK)
240     {
241         return response(compCode);
242     }
243 
244     constexpr uint2_t reservedOut1 = 0;
245     constexpr uint4_t reservedOut2 = 0;
246 
247     return responseSuccess(
248         static_cast<uint3_t>(chAccess.accessMode), chAccess.userAuthDisabled,
249         chAccess.perMsgAuthDisabled, chAccess.alertingDisabled, reservedOut1,
250         static_cast<uint4_t>(chAccess.privLimit), reservedOut2);
251 }
252 
253 /** @brief implements the get channel info command
254  *  @ param ctx - context pointer
255  *  @ param channel - channel number
256  *  @ param reserved - skip 4 bits
257  *
258  *  @returns ipmi completion code plus response data
259  *  - chNum - the channel number for this request
260  *  - mediumType - see Table 6-3, Channel Medium Type Numbers
261  *  - protocolType - Table 6-2, Channel Protocol Type Numbers
262  *  - activeSessionCount - number of active sessions
263  *  - sessionType - channel support for sessions
264  *  - vendorId - vendor for this channel protocol (IPMI - 7154)
265  *  - auxChInfo - auxiliary info for channel
266  * */
267 RspType<uint4_t,  // chNum
268         uint4_t,  // reserved
269         uint7_t,  // mediumType
270         bool,     // reserved
271         uint5_t,  // protocolType
272         uint3_t,  // reserved
273         uint6_t,  // activeSessionCount
274         uint2_t,  // sessionType
275         uint24_t, // Vendor IANA
276         uint16_t  // aux info
277         >
278     ipmiGetChannelInfo(Context::ptr ctx, uint4_t channel, uint4_t reserved)
279 {
280     uint8_t chNum =
281         convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel);
282     if (!isValidChannel(chNum) || reserved)
283     {
284         log<level::DEBUG>("Get channel access - Invalid field in request");
285         return responseInvalidFieldRequest();
286     }
287 
288     ChannelInfo chInfo;
289     Cc compCode = getChannelInfo(chNum, chInfo);
290     if (compCode != ccSuccess)
291     {
292         log<level::ERR>("Failed to get channel info",
293                         entry("CHANNEL=%x", chNum),
294                         entry("ERRNO=%x", compCode));
295         return response(compCode);
296     }
297 
298     constexpr uint4_t reserved1 = 0;
299     constexpr bool reserved2 = false;
300     constexpr uint3_t reserved3 = 0;
301     uint8_t mediumType = chInfo.mediumType;
302     uint8_t protocolType = chInfo.protocolType;
303     uint2_t sessionType = chInfo.sessionSupported;
304     uint6_t activeSessionCount = getChannelActiveSessions(chNum);
305     // IPMI Spec: The IPMI Enterprise Number is: 7154 (decimal)
306     constexpr uint24_t vendorId = 7154;
307     constexpr uint16_t auxChInfo = 0;
308 
309     return responseSuccess(chNum, reserved1, mediumType, reserved2,
310                            protocolType, reserved3, activeSessionCount,
311                            sessionType, vendorId, auxChInfo);
312 }
313 
314 ipmi_ret_t ipmiGetChannelPayloadSupport(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
315                                         ipmi_request_t request,
316                                         ipmi_response_t response,
317                                         ipmi_data_len_t data_len,
318                                         ipmi_context_t context)
319 {
320     const auto req = static_cast<GetChannelPayloadSupportReq*>(request);
321     size_t reqLength = *data_len;
322 
323     *data_len = 0;
324 
325     if (reqLength != sizeof(*req))
326     {
327         log<level::DEBUG>("Get channel payload - Invalid Length");
328         return IPMI_CC_REQ_DATA_LEN_INVALID;
329     }
330 
331     uint8_t chNum = convertCurrentChannelNum(req->chNum);
332     if (!isValidChannel(chNum) || req->reserved != 0)
333     {
334         log<level::DEBUG>("Get channel payload - Invalid field in request");
335         return IPMI_CC_INVALID_FIELD_REQUEST;
336     }
337 
338     // Not supported on sessionless channels.
339     if (EChannelSessSupported::none == getChannelSessionSupport(chNum))
340     {
341         log<level::DEBUG>("Get channel payload - Sessionless Channel");
342         return IPMI_CC_INVALID_FIELD_REQUEST;
343     }
344 
345     // Session support is available in active LAN channels.
346     if ((EChannelSessSupported::none != getChannelSessionSupport(chNum)) &&
347         (!(doesDeviceExist(chNum))))
348     {
349         log<level::DEBUG>("Get channel payload - Device not exist");
350         return IPMI_CC_INVALID_FIELD_REQUEST;
351     }
352 
353     auto resp = static_cast<GetChannelPayloadSupportResp*>(response);
354 
355     std::fill(reinterpret_cast<uint8_t*>(resp),
356               reinterpret_cast<uint8_t*>(resp) + sizeof(*resp), 0);
357 
358     // TODO: Hard coding for now.
359     // Mapping PayloadTypes to 'GetChannelPayloadSupportResp' fields:
360     // --------------------------------------------------------------
361     // Mask all except least 3 significant bits to get a value in the range of
362     // 0-7. This value maps to the bit position of given payload type in 'resp'
363     // fields.
364 
365     static constexpr uint8_t payloadByteMask = 0x07;
366     static constexpr uint8_t stdPayloadTypeIPMI =
367         1 << (static_cast<uint8_t>(PayloadType::IPMI) & payloadByteMask);
368     static constexpr uint8_t stdPayloadTypeSOL =
369         1 << (static_cast<uint8_t>(PayloadType::SOL) & payloadByteMask);
370 
371     static constexpr uint8_t sessPayloadTypeOpenReq =
372         1 << (static_cast<uint8_t>(PayloadType::OPEN_SESSION_REQUEST) &
373               payloadByteMask);
374     static constexpr uint8_t sessPayloadTypeRAKP1 =
375         1 << (static_cast<uint8_t>(PayloadType::RAKP1) & payloadByteMask);
376     static constexpr uint8_t sessPayloadTypeRAKP3 =
377         1 << (static_cast<uint8_t>(PayloadType::RAKP3) & payloadByteMask);
378 
379     resp->stdPayloadType[0] = stdPayloadTypeIPMI | stdPayloadTypeSOL;
380     // RMCP+ Open Session request, RAKP Message1 and RAKP Message3.
381     resp->sessSetupPayloadType[0] =
382         sessPayloadTypeOpenReq | sessPayloadTypeRAKP1 | sessPayloadTypeRAKP3;
383 
384     *data_len = sizeof(*resp);
385 
386     return IPMI_CC_OK;
387 }
388 
389 void registerChannelFunctions() __attribute__((constructor));
390 void registerChannelFunctions()
391 {
392     ipmiChannelInit();
393 
394     registerHandler(prioOpenBmcBase, netFnApp, app::cmdSetChannelAccess,
395                     Privilege::Admin, ipmiSetChannelAccess);
396 
397     registerHandler(prioOpenBmcBase, netFnApp, app::cmdGetChannelAccess,
398                     Privilege::User, ipmiGetChannelAccess);
399 
400     registerHandler(prioOpenBmcBase, netFnApp, app::cmdGetChannelInfoCommand,
401                     Privilege::User, ipmiGetChannelInfo);
402 
403     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHANNEL_PAYLOAD_SUPPORT,
404                            NULL, ipmiGetChannelPayloadSupport, PRIVILEGE_USER);
405 
406     return;
407 }
408 
409 } // namespace ipmi
410