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 
23 #include <regex>
24 
25 using namespace phosphor::logging;
26 
27 namespace ipmi
28 {
29 
30 /** @brief implements the set channel access command
31  *  @ param ctx - context pointer
32  *  @ param channel - channel number
33  *  @ param reserved - skip 4 bits
34  *  @ param accessMode - access mode for IPMI messaging
35  *  @ param usrAuth - user level authentication (enable/disable)
36  *  @ param msgAuth - per message authentication (enable/disable)
37  *  @ param alertDisabled - PEF alerting (enable/disable)
38  *  @ param chanAccess - channel access
39  *  @ param channelPrivLimit - channel privilege limit
40  *  @ param reserved - skip 3 bits
41  *  @ param channelPrivMode - channel priviledge mode
42  *
43  *  @ returns IPMI completion code
44  **/
45 RspType<> ipmiSetChannelAccess(Context::ptr ctx, uint4_t channel,
46                                uint4_t reserved1, uint3_t accessMode,
47                                bool usrAuth, bool msgAuth, bool alertDisabled,
48                                uint2_t chanAccess, uint4_t channelPrivLimit,
49                                uint2_t reserved2, uint2_t channelPrivMode)
50 {
51     if (reserved1 || reserved2 ||
52         !isValidPrivLimit(static_cast<uint8_t>(channelPrivLimit)))
53     {
54         log<level::DEBUG>("Set channel access - Invalid field in request");
55         return responseInvalidFieldRequest();
56     }
57 
58     const uint8_t chNum =
59         convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel);
60     if ((getChannelSessionSupport(chNum) == EChannelSessSupported::none) ||
61         (!isValidChannel(chNum)))
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 response(ccAccessModeNotSupportedForChannel);
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 response(ccAccessModeNotSupportedForChannel);
122     }
123 
124     if (setNVFlag != 0)
125     {
126         compCode = setChannelAccessPersistData(chNum, chNVData, setNVFlag);
127         if (compCode != ccSuccess)
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 != ccSuccess)
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     if (reserved1 || reserved2)
176     {
177         log<level::DEBUG>("Get channel access - Invalid field in request");
178         return responseInvalidFieldRequest();
179     }
180 
181     if ((types::enum_cast<EChannelActionType>(accessSetMode) == doNotSet) ||
182         (types::enum_cast<EChannelActionType>(accessSetMode) == reserved))
183     {
184         log<level::DEBUG>("Get channel access - Invalid Access mode");
185         return responseInvalidFieldRequest();
186     }
187 
188     const uint8_t chNum =
189         convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel);
190 
191     if ((getChannelSessionSupport(chNum) == EChannelSessSupported::none) ||
192         (!isValidChannel(chNum)))
193     {
194         log<level::DEBUG>("Get channel access - No support on channel");
195         return response(ccActionNotSupportedForChannel);
196     }
197 
198     ChannelAccess chAccess = {};
199 
200     Cc compCode = ipmi::ccUnspecifiedError;
201 
202     if (types::enum_cast<EChannelActionType>(accessSetMode) == nvData)
203     {
204         compCode = getChannelAccessPersistData(chNum, chAccess);
205     }
206     else if (types::enum_cast<EChannelActionType>(accessSetMode) == activeData)
207     {
208         compCode = getChannelAccessData(chNum, chAccess);
209     }
210 
211     if (compCode != ccSuccess)
212     {
213         return response(compCode);
214     }
215 
216     constexpr uint2_t reservedOut1 = 0;
217     constexpr uint4_t reservedOut2 = 0;
218 
219     return responseSuccess(
220         types::enum_cast<uint3_t>(chAccess.accessMode),
221         chAccess.userAuthDisabled, chAccess.perMsgAuthDisabled,
222         chAccess.alertingDisabled, reservedOut1,
223         types::enum_cast<uint4_t>(chAccess.privLimit), reservedOut2);
224 }
225 
226 /** @brief implements the get channel info command
227  *  @ param ctx - context pointer
228  *  @ param channel - channel number
229  *  @ param reserved - skip 4 bits
230  *
231  *  @returns ipmi completion code plus response data
232  *  - chNum - the channel number for this request
233  *  - mediumType - see Table 6-3, Channel Medium Type Numbers
234  *  - protocolType - Table 6-2, Channel Protocol Type Numbers
235  *  - activeSessionCount - number of active sessions
236  *  - sessionType - channel support for sessions
237  *  - vendorId - vendor for this channel protocol (IPMI - 7154)
238  *  - auxChInfo - auxiliary info for channel
239  * */
240 RspType<uint4_t,  // chNum
241         uint4_t,  // reserved
242         uint7_t,  // mediumType
243         bool,     // reserved
244         uint5_t,  // protocolType
245         uint3_t,  // reserved
246         uint6_t,  // activeSessionCount
247         uint2_t,  // sessionType
248         uint24_t, // Vendor IANA
249         uint16_t  // aux info
250         >
251     ipmiGetChannelInfo(Context::ptr ctx, uint4_t channel, uint4_t reserved)
252 {
253     if (reserved)
254     {
255         log<level::DEBUG>("Get channel access - Invalid field in request");
256         return responseInvalidFieldRequest();
257     }
258 
259     uint8_t chNum = convertCurrentChannelNum(static_cast<uint8_t>(channel),
260                                              ctx->channel);
261     if (!isValidChannel(chNum))
262     {
263         log<level::DEBUG>("Get channel Info - No support on channel");
264         return responseInvalidFieldRequest();
265     }
266 
267     ChannelInfo chInfo;
268     Cc compCode = getChannelInfo(chNum, chInfo);
269     if (compCode != ccSuccess)
270     {
271         log<level::ERR>("Failed to get channel info",
272                         entry("CHANNEL=%x", chNum),
273                         entry("ERRNO=%x", compCode));
274         return response(compCode);
275     }
276 
277     constexpr uint4_t reserved1 = 0;
278     constexpr bool reserved2 = false;
279     constexpr uint3_t reserved3 = 0;
280     uint8_t mediumType = chInfo.mediumType;
281     uint8_t protocolType = chInfo.protocolType;
282     uint2_t sessionType = chInfo.sessionSupported;
283     uint6_t activeSessionCount = getChannelActiveSessions(chNum);
284     // IPMI Spec: The IPMI Enterprise Number is: 7154 (decimal)
285     constexpr uint24_t vendorId = 7154;
286     constexpr uint16_t auxChInfo = 0;
287 
288     return responseSuccess(chNum, reserved1, mediumType, reserved2,
289                            protocolType, reserved3, activeSessionCount,
290                            sessionType, vendorId, auxChInfo);
291 }
292 
293 namespace
294 {
295 constexpr uint16_t standardPayloadBit(PayloadType p)
296 {
297     return (1 << static_cast<size_t>(p));
298 }
299 
300 constexpr uint16_t sessionPayloadBit(PayloadType p)
301 {
302     constexpr size_t sessionShift =
303         static_cast<size_t>(PayloadType::OPEN_SESSION_REQUEST);
304     return ((1 << static_cast<size_t>(p)) >> sessionShift);
305 }
306 } // namespace
307 
308 /** @brief implements get channel payload support command
309  *  @ param ctx - ipmi context pointer
310  *  @ param chNum - channel number
311  *  @ param reserved - skip 4 bits
312  *
313  *  @ returns IPMI completion code plus response data
314  *  - stdPayloadType - bitmask of supported standard payload types
315  *  - sessSetupPayloadType - bitmask of supported session setup payload types
316  *  - OEMPayloadType - bitmask of supported OEM payload types
317  *  - reserved - 2 bytes of 0
318  **/
319 RspType<uint16_t, // stdPayloadType
320         uint16_t, // sessSetupPayloadType
321         uint16_t, // OEMPayloadType
322         uint16_t  // reserved
323         >
324     ipmiGetChannelPayloadSupport(Context::ptr ctx, uint4_t channel,
325                                  uint4_t reserved)
326 {
327     uint8_t chNum = convertCurrentChannelNum(static_cast<uint8_t>(channel),
328                                              ctx->channel);
329 
330     if (!doesDeviceExist(chNum) || !isValidChannel(chNum) || reserved)
331     {
332         log<level::DEBUG>("Get channel payload - Invalid field in request");
333         return responseInvalidFieldRequest();
334     }
335 
336     // Session support is available in active LAN channels.
337     if (getChannelSessionSupport(chNum) == EChannelSessSupported::none)
338     {
339         log<level::DEBUG>("Get channel payload - No support on channel");
340         return response(ccActionNotSupportedForChannel);
341     }
342     constexpr uint16_t stdPayloadType = standardPayloadBit(PayloadType::IPMI) |
343                                         standardPayloadBit(PayloadType::SOL);
344     constexpr uint16_t sessSetupPayloadType =
345         sessionPayloadBit(PayloadType::OPEN_SESSION_REQUEST) |
346         sessionPayloadBit(PayloadType::OPEN_SESSION_RESPONSE) |
347         sessionPayloadBit(PayloadType::RAKP1) |
348         sessionPayloadBit(PayloadType::RAKP2) |
349         sessionPayloadBit(PayloadType::RAKP3) |
350         sessionPayloadBit(PayloadType::RAKP4);
351     constexpr uint16_t OEMPayloadType = 0;
352     constexpr uint16_t rspRsvd = 0;
353     return responseSuccess(stdPayloadType, sessSetupPayloadType, OEMPayloadType,
354                            rspRsvd);
355 }
356 
357 /** @brief implements the get channel payload version command
358  *  @param ctx - IPMI context pointer (for channel)
359  *  @param chNum - channel number to get info about
360  *  @param reserved - skip 4 bits
361  *  @param payloadTypeNum - to get payload type info
362 
363  *  @returns IPMI completion code plus response data
364  *   - formatVersion - BCD encoded format version info
365  */
366 
367 RspType<uint8_t> // formatVersion
368     ipmiGetChannelPayloadVersion(Context::ptr ctx, uint4_t chNum,
369                                  uint4_t reserved, uint8_t payloadTypeNum)
370 {
371     uint8_t channel = convertCurrentChannelNum(static_cast<uint8_t>(chNum),
372                                                ctx->channel);
373     constexpr uint8_t payloadTypeNotSupported = 0x80;
374 
375     if (reserved || !isValidChannel(channel))
376     {
377         log<level::DEBUG>(
378             "Get channel payload version - Invalid field in request");
379         return responseInvalidFieldRequest();
380     }
381 
382     if (getChannelSessionSupport(channel) == EChannelSessSupported::none)
383     {
384         log<level::DEBUG>(
385             "Get channel payload version - No support on channel");
386         return response(payloadTypeNotSupported);
387     }
388 
389     if (!isValidPayloadType(static_cast<PayloadType>(payloadTypeNum)))
390     {
391         log<level::ERR>(
392             "Get channel payload version - Payload type unavailable");
393 
394         return response(payloadTypeNotSupported);
395     }
396 
397     // BCD encoded version representation - 1.0
398     constexpr uint8_t formatVersion = 0x10;
399 
400     return responseSuccess(formatVersion);
401 }
402 
403 void registerChannelFunctions() __attribute__((constructor));
404 void registerChannelFunctions()
405 {
406     ipmiChannelInit();
407 
408     registerHandler(prioOpenBmcBase, netFnApp, app::cmdSetChannelAccess,
409                     Privilege::Admin, ipmiSetChannelAccess);
410 
411     registerHandler(prioOpenBmcBase, netFnApp, app::cmdGetChannelAccess,
412                     Privilege::User, ipmiGetChannelAccess);
413 
414     registerHandler(prioOpenBmcBase, netFnApp, app::cmdGetChannelInfoCommand,
415                     Privilege::User, ipmiGetChannelInfo);
416 
417     registerHandler(prioOpenBmcBase, netFnApp, app::cmdGetChannelPayloadSupport,
418                     Privilege::User, ipmiGetChannelPayloadSupport);
419 
420     registerHandler(prioOpenBmcBase, netFnApp, app::cmdGetChannelPayloadVersion,
421                     Privilege::User, ipmiGetChannelPayloadVersion);
422 }
423 
424 } // namespace ipmi
425