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 "apphandler.hpp"
18 #include "channel_layer.hpp"
19 
20 #include <ipmid/api.hpp>
21 #include <phosphor-logging/log.hpp>
22 #include <regex>
23 
24 using namespace phosphor::logging;
25 
26 namespace ipmi
27 {
28 
29 static constexpr const uint8_t ccActionNotSupportedForChannel = 0x82;
30 
31 /** @brief implements the set channel access command
32  *  @ param ctx - context pointer
33  *  @ param channel - channel number
34  *  @ param reserved - skip 4 bits
35  *  @ param accessMode - access mode for IPMI messaging
36  *  @ param usrAuth - user level authentication (enable/disable)
37  *  @ param msgAuth - per message authentication (enable/disable)
38  *  @ param alertDisabled - PEF alerting (enable/disable)
39  *  @ param chanAccess - channel access
40  *  @ param channelPrivLimit - channel privilege limit
41  *  @ param reserved - skip 3 bits
42  *  @ param channelPrivMode - channel priviledge mode
43  *
44  *  @ returns IPMI completion code
45  **/
46 RspType<> ipmiSetChannelAccess(Context::ptr ctx, uint4_t channel,
47                                uint4_t reserved1, uint3_t accessMode,
48                                bool usrAuth, bool msgAuth, bool alertDisabled,
49                                uint2_t chanAccess, uint4_t channelPrivLimit,
50                                uint2_t reserved2, uint2_t channelPrivMode)
51 {
52     const uint8_t chNum =
53         convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel);
54 
55     if (!isValidChannel(chNum) || reserved1 != 0 || reserved2 != 0)
56     {
57         log<level::DEBUG>("Set channel access - Invalid field in request");
58         return responseInvalidFieldRequest();
59     }
60 
61     if (getChannelSessionSupport(chNum) == EChannelSessSupported::none)
62     {
63         log<level::DEBUG>("Set channel access - No support on channel");
64         return response(ccActionNotSupportedForChannel);
65     }
66 
67     ChannelAccess chActData;
68     ChannelAccess chNVData;
69     uint8_t setActFlag = 0;
70     uint8_t setNVFlag = 0;
71     Cc compCode;
72 
73     // cannot static cast directly from uint2_t to enum; must go via int
74     uint8_t channelAccessAction = static_cast<uint8_t>(chanAccess);
75     switch (static_cast<EChannelActionType>(channelAccessAction))
76     {
77         case doNotSet:
78             break;
79         case nvData:
80             chNVData.accessMode = static_cast<uint8_t>(accessMode);
81             chNVData.userAuthDisabled = usrAuth;
82             chNVData.perMsgAuthDisabled = msgAuth;
83             chNVData.alertingDisabled = alertDisabled;
84             setNVFlag |= (setAccessMode | setUserAuthEnabled |
85                           setMsgAuthEnabled | setAlertingEnabled);
86             break;
87 
88         case activeData:
89             chActData.accessMode = static_cast<uint8_t>(accessMode);
90             chActData.userAuthDisabled = usrAuth;
91             chActData.perMsgAuthDisabled = msgAuth;
92             chActData.alertingDisabled = alertDisabled;
93             setActFlag |= (setAccessMode | setUserAuthEnabled |
94                            setMsgAuthEnabled | setAlertingEnabled);
95             break;
96 
97         case reserved:
98         default:
99             log<level::DEBUG>("Set channel access - Invalid access set mode");
100             return responseInvalidFieldRequest();
101     }
102 
103     // cannot static cast directly from uint2_t to enum; must go via int
104     uint8_t channelPrivAction = static_cast<uint8_t>(channelPrivMode);
105     switch (static_cast<EChannelActionType>(channelPrivAction))
106     {
107         case doNotSet:
108             break;
109         case nvData:
110             chNVData.privLimit = static_cast<uint8_t>(channelPrivLimit);
111             setNVFlag |= setPrivLimit;
112             break;
113         case activeData:
114             chActData.privLimit = static_cast<uint8_t>(channelPrivLimit);
115 
116             setActFlag |= setPrivLimit;
117             break;
118         case reserved:
119         default:
120             log<level::DEBUG>("Set channel access - Invalid access priv mode");
121             return responseInvalidFieldRequest();
122     }
123 
124     if (setNVFlag != 0)
125     {
126         compCode = setChannelAccessPersistData(chNum, chNVData, setNVFlag);
127         if (compCode != IPMI_CC_OK)
128         {
129             log<level::DEBUG>("Set channel access - Failed to set access data");
130             return response(compCode);
131         }
132     }
133 
134     if (setActFlag != 0)
135     {
136         compCode = setChannelAccessData(chNum, chActData, setActFlag);
137         if (compCode != IPMI_CC_OK)
138         {
139             log<level::DEBUG>("Set channel access - Failed to set access data");
140             return response(compCode);
141         }
142     }
143 
144     return responseSuccess();
145 }
146 
147 /** @brief implements the get channel access command
148  *  @ param ctx - context pointer
149  *  @ param channel - channel number
150  *  @ param reserved1 - skip 4 bits
151  *  @ param reserved2 - skip 6 bits
152  *  @ param accessMode - get access mode
153  *
154  *  @returns ipmi completion code plus response data
155  *  - accessMode - get access mode
156  *  - usrAuthDisabled - user level authentication status
157  *  - msgAuthDisabled - message level authentication status
158  *  - alertDisabled - alerting status
159  *  - reserved - skip 2 bits
160  *  - privLimit - channel privilege limit
161  *  - reserved - skip 4 bits
162  * */
163 ipmi ::RspType<uint3_t, // access mode,
164                bool,    // user authentication status,
165                bool,    // message authentication status,
166                bool,    // alerting status,
167                uint2_t, // reserved,
168 
169                uint4_t, // channel privilege,
170                uint4_t  // reserved
171                >
172     ipmiGetChannelAccess(Context::ptr ctx, uint4_t channel, uint4_t reserved1,
173                          uint6_t reserved2, uint2_t accessSetMode)
174 {
175     const uint8_t chNum =
176         convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel);
177 
178     if (!isValidChannel(chNum) || reserved1 != 0 || reserved2 != 0)
179     {
180         log<level::DEBUG>("Get channel access - Invalid field in request");
181         return responseInvalidFieldRequest();
182     }
183 
184     if ((accessSetMode == doNotSet) || (accessSetMode == reserved))
185     {
186         log<level::DEBUG>("Get channel access - Invalid Access mode");
187         return responseInvalidFieldRequest();
188     }
189 
190     if (getChannelSessionSupport(chNum) == EChannelSessSupported::none)
191     {
192         log<level::DEBUG>("Get channel access - No support on channel");
193         return response(ccActionNotSupportedForChannel);
194     }
195 
196     ChannelAccess chAccess;
197 
198     Cc compCode;
199 
200     if (accessSetMode == nvData)
201     {
202         compCode = getChannelAccessPersistData(chNum, chAccess);
203     }
204     else if (accessSetMode == activeData)
205     {
206         compCode = getChannelAccessData(chNum, chAccess);
207     }
208 
209     if (compCode != IPMI_CC_OK)
210     {
211         return response(compCode);
212     }
213 
214     constexpr uint2_t reservedOut1 = 0;
215     constexpr uint4_t reservedOut2 = 0;
216 
217     return responseSuccess(
218         static_cast<uint3_t>(chAccess.accessMode), chAccess.userAuthDisabled,
219         chAccess.perMsgAuthDisabled, chAccess.alertingDisabled, reservedOut1,
220         static_cast<uint4_t>(chAccess.privLimit), reservedOut2);
221 }
222 
223 /** @brief implements the get channel info command
224  *  @ param ctx - context pointer
225  *  @ param channel - channel number
226  *  @ param reserved - skip 4 bits
227  *
228  *  @returns ipmi completion code plus response data
229  *  - chNum - the channel number for this request
230  *  - mediumType - see Table 6-3, Channel Medium Type Numbers
231  *  - protocolType - Table 6-2, Channel Protocol Type Numbers
232  *  - activeSessionCount - number of active sessions
233  *  - sessionType - channel support for sessions
234  *  - vendorId - vendor for this channel protocol (IPMI - 7154)
235  *  - auxChInfo - auxiliary info for channel
236  * */
237 RspType<uint4_t,  // chNum
238         uint4_t,  // reserved
239         uint7_t,  // mediumType
240         bool,     // reserved
241         uint5_t,  // protocolType
242         uint3_t,  // reserved
243         uint6_t,  // activeSessionCount
244         uint2_t,  // sessionType
245         uint24_t, // Vendor IANA
246         uint16_t  // aux info
247         >
248     ipmiGetChannelInfo(Context::ptr ctx, uint4_t channel, uint4_t reserved)
249 {
250     uint8_t chNum =
251         convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel);
252     if (!isValidChannel(chNum) || reserved)
253     {
254         log<level::DEBUG>("Get channel access - Invalid field in request");
255         return responseInvalidFieldRequest();
256     }
257 
258     ChannelInfo chInfo;
259     Cc compCode = getChannelInfo(chNum, chInfo);
260     if (compCode != ccSuccess)
261     {
262         log<level::ERR>("Failed to get channel info",
263                         entry("CHANNEL=%x", chNum),
264                         entry("ERRNO=%x", compCode));
265         return response(compCode);
266     }
267 
268     constexpr uint4_t reserved1 = 0;
269     constexpr bool reserved2 = false;
270     constexpr uint3_t reserved3 = 0;
271     uint8_t mediumType = chInfo.mediumType;
272     uint8_t protocolType = chInfo.protocolType;
273     uint2_t sessionType = chInfo.sessionSupported;
274     uint6_t activeSessionCount = getChannelActiveSessions(chNum);
275     // IPMI Spec: The IPMI Enterprise Number is: 7154 (decimal)
276     constexpr uint24_t vendorId = 7154;
277     constexpr uint16_t auxChInfo = 0;
278 
279     return responseSuccess(chNum, reserved1, mediumType, reserved2,
280                            protocolType, reserved3, activeSessionCount,
281                            sessionType, vendorId, auxChInfo);
282 }
283 
284 namespace
285 {
286 constexpr uint16_t standardPayloadBit(PayloadType p)
287 {
288     return (1 << static_cast<size_t>(p));
289 }
290 
291 constexpr uint16_t sessionPayloadBit(PayloadType p)
292 {
293     constexpr size_t sessionShift =
294         static_cast<size_t>(PayloadType::OPEN_SESSION_REQUEST);
295     return ((1 << static_cast<size_t>(p)) >> sessionShift);
296 }
297 } // namespace
298 
299 /** @brief implements get channel payload support command
300  *  @ param ctx - ipmi context pointer
301  *  @ param chNum - channel number
302  *  @ param reserved - skip 4 bits
303  *
304  *  @ returns IPMI completion code plus response data
305  *  - stdPayloadType - bitmask of supported standard payload types
306  *  - sessSetupPayloadType - bitmask of supported session setup payload types
307  *  - OEMPayloadType - bitmask of supported OEM payload types
308  *  - reserved - 2 bytes of 0
309  **/
310 RspType<uint16_t, // stdPayloadType
311         uint16_t, // sessSetupPayloadType
312         uint16_t, // OEMPayloadType
313         uint16_t  // reserved
314         >
315     ipmiGetChannelPayloadSupport(Context::ptr ctx, uint4_t channel,
316                                  uint4_t reserved)
317 {
318     uint8_t chNum =
319         convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel);
320     if (!isValidChannel(chNum) || reserved)
321     {
322         log<level::DEBUG>("Get channel access - Invalid field in request");
323         return responseInvalidFieldRequest();
324     }
325 
326     // Session support is available in active LAN channels.
327     if ((getChannelSessionSupport(chNum) == EChannelSessSupported::none) ||
328         !(doesDeviceExist(chNum)))
329     {
330         log<level::DEBUG>("Get channel payload - Device not exist");
331         return responseInvalidFieldRequest();
332     }
333 
334     constexpr uint16_t stdPayloadType = standardPayloadBit(PayloadType::IPMI) |
335                                         standardPayloadBit(PayloadType::SOL);
336     constexpr uint16_t sessSetupPayloadType =
337         sessionPayloadBit(PayloadType::OPEN_SESSION_REQUEST) |
338         sessionPayloadBit(PayloadType::OPEN_SESSION_RESPONSE) |
339         sessionPayloadBit(PayloadType::RAKP1) |
340         sessionPayloadBit(PayloadType::RAKP2) |
341         sessionPayloadBit(PayloadType::RAKP3) |
342         sessionPayloadBit(PayloadType::RAKP4);
343     constexpr uint16_t OEMPayloadType = 0;
344     constexpr uint16_t rspRsvd = 0;
345     return responseSuccess(stdPayloadType, sessSetupPayloadType, OEMPayloadType,
346                            rspRsvd);
347 }
348 
349 /** @brief implements the get channel payload version command
350  *  @param ctx - IPMI context pointer (for channel)
351  *  @param chNum - channel number to get info about
352  *  @param reserved - skip 4 bits
353  *  @param payloadTypeNum - to get payload type info
354 
355  *  @returns IPMI completion code plus response data
356  *   - formatVersion - BCD encoded format version info
357  */
358 
359 RspType<uint8_t> // formatVersion
360     ipmiGetChannelPayloadVersion(Context::ptr ctx, uint4_t chNum,
361                                  uint4_t reserved, uint8_t payloadTypeNum)
362 {
363     uint8_t channel =
364         convertCurrentChannelNum(static_cast<uint8_t>(chNum), ctx->channel);
365 
366     if (reserved || !isValidChannel(channel) ||
367         (getChannelSessionSupport(channel)) == EChannelSessSupported::none)
368     {
369         return responseInvalidFieldRequest();
370     }
371 
372     if (!isValidPayloadType(static_cast<PayloadType>(payloadTypeNum)))
373     {
374         log<level::ERR>("Channel payload version - Payload type unavailable");
375 
376         constexpr uint8_t payloadTypeNotSupported = 0x80;
377         return response(payloadTypeNotSupported);
378     }
379 
380     // BCD encoded version representation - 1.0
381     constexpr uint8_t formatVersion = 0x10;
382 
383     return responseSuccess(formatVersion);
384 }
385 
386 void registerChannelFunctions() __attribute__((constructor));
387 void registerChannelFunctions()
388 {
389     ipmiChannelInit();
390 
391     registerHandler(prioOpenBmcBase, netFnApp, app::cmdSetChannelAccess,
392                     Privilege::Admin, ipmiSetChannelAccess);
393 
394     registerHandler(prioOpenBmcBase, netFnApp, app::cmdGetChannelAccess,
395                     Privilege::User, ipmiGetChannelAccess);
396 
397     registerHandler(prioOpenBmcBase, netFnApp, app::cmdGetChannelInfoCommand,
398                     Privilege::User, ipmiGetChannelInfo);
399 
400     registerHandler(prioOpenBmcBase, netFnApp, app::cmdGetChannelPayloadSupport,
401                     Privilege::User, ipmiGetChannelPayloadSupport);
402 
403     registerHandler(prioOpenBmcBase, netFnApp, app::cmdGetChannelPayloadVersion,
404                     Privilege::User, ipmiGetChannelPayloadVersion);
405 }
406 
407 } // namespace ipmi
408