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