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/lg2.hpp>
22 
23 #include <regex>
24 
25 namespace ipmi
26 {
27 
28 /** @brief implements the set channel access command
29  *  @ param ctx - context pointer
30  *  @ param channel - channel number
31  *  @ param reserved - skip 4 bits
32  *  @ param accessMode - access mode for IPMI messaging
33  *  @ param usrAuth - user level authentication (enable/disable)
34  *  @ param msgAuth - per message authentication (enable/disable)
35  *  @ param alertDisabled - PEF alerting (enable/disable)
36  *  @ param chanAccess - channel access
37  *  @ param channelPrivLimit - channel privilege limit
38  *  @ param reserved - skip 3 bits
39  *  @ param channelPrivMode - channel priviledge mode
40  *
41  *  @ returns IPMI completion code
42  **/
ipmiSetChannelAccess(Context::ptr ctx,uint4_t channel,uint4_t reserved1,uint3_t accessMode,bool usrAuth,bool msgAuth,bool alertDisabled,uint2_t chanAccess,uint4_t channelPrivLimit,uint2_t reserved2,uint2_t channelPrivMode)43 RspType<> ipmiSetChannelAccess(
44     Context::ptr ctx, uint4_t channel, uint4_t reserved1, uint3_t accessMode,
45     bool usrAuth, bool msgAuth, bool alertDisabled, uint2_t chanAccess,
46     uint4_t channelPrivLimit, uint2_t reserved2, uint2_t channelPrivMode)
47 {
48     if (reserved1 || reserved2 ||
49         !isValidPrivLimit(static_cast<uint8_t>(channelPrivLimit)))
50     {
51         lg2::debug("Set channel access - Invalid field in request");
52         return responseInvalidFieldRequest();
53     }
54 
55     const uint8_t chNum =
56         convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel);
57     if ((getChannelSessionSupport(chNum) == EChannelSessSupported::none) ||
58         (!isValidChannel(chNum)))
59     {
60         lg2::debug("Set channel access - No support on channel: {CHANNEL}",
61                    "CHANNEL", chNum);
62         return response(ccActionNotSupportedForChannel);
63     }
64 
65     ChannelAccess chActData;
66     ChannelAccess chNVData;
67     uint8_t setActFlag = 0;
68     uint8_t setNVFlag = 0;
69     Cc compCode;
70 
71     // cannot static cast directly from uint2_t to enum; must go via int
72     uint8_t channelAccessAction = static_cast<uint8_t>(chanAccess);
73     switch (static_cast<EChannelActionType>(channelAccessAction))
74     {
75         case doNotSet:
76             break;
77         case nvData:
78             chNVData.accessMode = static_cast<uint8_t>(accessMode);
79             chNVData.userAuthDisabled = usrAuth;
80             chNVData.perMsgAuthDisabled = msgAuth;
81             chNVData.alertingDisabled = alertDisabled;
82             setNVFlag |= (setAccessMode | setUserAuthEnabled |
83                           setMsgAuthEnabled | setAlertingEnabled);
84             break;
85 
86         case activeData:
87             chActData.accessMode = static_cast<uint8_t>(accessMode);
88             chActData.userAuthDisabled = usrAuth;
89             chActData.perMsgAuthDisabled = msgAuth;
90             chActData.alertingDisabled = alertDisabled;
91             setActFlag |= (setAccessMode | setUserAuthEnabled |
92                            setMsgAuthEnabled | setAlertingEnabled);
93             break;
94 
95         case reserved:
96         default:
97             lg2::debug("Set channel access - Invalid access set mode");
98             return response(ccAccessModeNotSupportedForChannel);
99     }
100 
101     // cannot static cast directly from uint2_t to enum; must go via int
102     uint8_t channelPrivAction = static_cast<uint8_t>(channelPrivMode);
103     switch (static_cast<EChannelActionType>(channelPrivAction))
104     {
105         case doNotSet:
106             break;
107         case nvData:
108             chNVData.privLimit = static_cast<uint8_t>(channelPrivLimit);
109             setNVFlag |= setPrivLimit;
110             break;
111         case activeData:
112             chActData.privLimit = static_cast<uint8_t>(channelPrivLimit);
113 
114             setActFlag |= setPrivLimit;
115             break;
116         case reserved:
117         default:
118             lg2::debug("Set channel access - Invalid access priv mode");
119             return response(ccAccessModeNotSupportedForChannel);
120     }
121 
122     if (setNVFlag != 0)
123     {
124         compCode = setChannelAccessPersistData(chNum, chNVData, setNVFlag);
125         if (compCode != ccSuccess)
126         {
127             lg2::debug("Set channel access - Failed to set access data");
128             return response(compCode);
129         }
130     }
131 
132     if (setActFlag != 0)
133     {
134         compCode = setChannelAccessData(chNum, chActData, setActFlag);
135         if (compCode != ccSuccess)
136         {
137             lg2::debug("Set channel access - Failed to set access data");
138             return response(compCode);
139         }
140     }
141 
142     return responseSuccess();
143 }
144 
145 /** @brief implements the get channel access command
146  *  @ param ctx - context pointer
147  *  @ param channel - channel number
148  *  @ param reserved1 - skip 4 bits
149  *  @ param reserved2 - skip 6 bits
150  *  @ param accessMode - get access mode
151  *
152  *  @returns ipmi completion code plus response data
153  *  - accessMode - get access mode
154  *  - usrAuthDisabled - user level authentication status
155  *  - msgAuthDisabled - message level authentication status
156  *  - alertDisabled - alerting status
157  *  - reserved - skip 2 bits
158  *  - privLimit - channel privilege limit
159  *  - reserved - skip 4 bits
160  * */
161 ipmi ::RspType<uint3_t, // access mode,
162                bool,    // user authentication status,
163                bool,    // message authentication status,
164                bool,    // alerting status,
165                uint2_t, // reserved,
166 
167                uint4_t, // channel privilege,
168                uint4_t  // reserved
169                >
ipmiGetChannelAccess(Context::ptr ctx,uint4_t channel,uint4_t reserved1,uint6_t reserved2,uint2_t accessSetMode)170     ipmiGetChannelAccess(Context::ptr ctx, uint4_t channel, uint4_t reserved1,
171                          uint6_t reserved2, uint2_t accessSetMode)
172 {
173     if (reserved1 || reserved2)
174     {
175         lg2::debug("Get channel access - Invalid field in request");
176         return responseInvalidFieldRequest();
177     }
178 
179     if ((types::enum_cast<EChannelActionType>(accessSetMode) == doNotSet) ||
180         (types::enum_cast<EChannelActionType>(accessSetMode) == reserved))
181     {
182         lg2::debug("Get channel access - Invalid Access mode");
183         return responseInvalidFieldRequest();
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         lg2::debug("Get channel access - No support on channel: {CHANNEL}",
193                    "CHANNEL", chNum);
194         return response(ccActionNotSupportedForChannel);
195     }
196 
197     ChannelAccess chAccess = {};
198 
199     Cc compCode = ipmi::ccUnspecifiedError;
200 
201     if (types::enum_cast<EChannelActionType>(accessSetMode) == nvData)
202     {
203         compCode = getChannelAccessPersistData(chNum, chAccess);
204     }
205     else if (types::enum_cast<EChannelActionType>(accessSetMode) == activeData)
206     {
207         compCode = getChannelAccessData(chNum, chAccess);
208     }
209 
210     if (compCode != ccSuccess)
211     {
212         return response(compCode);
213     }
214 
215     constexpr uint2_t reservedOut1 = 0;
216     constexpr uint4_t reservedOut2 = 0;
217 
218     return responseSuccess(
219         types::enum_cast<uint3_t>(chAccess.accessMode),
220         chAccess.userAuthDisabled, chAccess.perMsgAuthDisabled,
221         chAccess.alertingDisabled, reservedOut1,
222         types::enum_cast<uint4_t>(chAccess.privLimit), reservedOut2);
223 }
224 
225 /** @brief implements the get channel info command
226  *  @ param ctx - context pointer
227  *  @ param channel - channel number
228  *  @ param reserved - skip 4 bits
229  *
230  *  @returns ipmi completion code plus response data
231  *  - chNum - the channel number for this request
232  *  - mediumType - see Table 6-3, Channel Medium Type Numbers
233  *  - protocolType - Table 6-2, Channel Protocol Type Numbers
234  *  - activeSessionCount - number of active sessions
235  *  - sessionType - channel support for sessions
236  *  - vendorId - vendor for this channel protocol (IPMI - 7154)
237  *  - auxChInfo - auxiliary info for channel
238  * */
239 RspType<uint4_t,  // chNum
240         uint4_t,  // reserved
241         uint7_t,  // mediumType
242         bool,     // reserved
243         uint5_t,  // protocolType
244         uint3_t,  // reserved
245         uint6_t,  // activeSessionCount
246         uint2_t,  // sessionType
247         uint24_t, // Vendor IANA
248         uint16_t  // aux info
249         >
ipmiGetChannelInfo(Context::ptr ctx,uint4_t channel,uint4_t reserved)250     ipmiGetChannelInfo(Context::ptr ctx, uint4_t channel, uint4_t reserved)
251 {
252     if (reserved)
253     {
254         lg2::debug("Get channel access - Invalid field in request");
255         return responseInvalidFieldRequest();
256     }
257 
258     uint8_t chNum =
259         convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel);
260     if (!isValidChannel(chNum))
261     {
262         lg2::debug("Get channel Info - No support on channel: {CHANNEL}",
263                    "CHANNEL", chNum);
264         return responseInvalidFieldRequest();
265     }
266 
267     ChannelInfo chInfo;
268     Cc compCode = getChannelInfo(chNum, chInfo);
269     if (compCode != ccSuccess)
270     {
271         lg2::error("Failed to get channel info, channel: {CHANNEL}, "
272                    "errno: {ERRNO}",
273                    "CHANNEL", chNum, "ERRNO", 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 {
standardPayloadBit(PayloadType p)295 constexpr uint16_t standardPayloadBit(PayloadType p)
296 {
297     return (1 << static_cast<size_t>(p));
298 }
299 
sessionPayloadBit(PayloadType p)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         >
ipmiGetChannelPayloadSupport(Context::ptr ctx,uint4_t channel,uint4_t reserved)324     ipmiGetChannelPayloadSupport(Context::ptr ctx, uint4_t channel,
325                                  uint4_t reserved)
326 {
327     uint8_t chNum =
328         convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel);
329 
330     if (!doesDeviceExist(chNum) || !isValidChannel(chNum) || reserved)
331     {
332         lg2::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         lg2::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
ipmiGetChannelPayloadVersion(Context::ptr ctx,uint4_t chNum,uint4_t reserved,uint8_t payloadTypeNum)368     ipmiGetChannelPayloadVersion(Context::ptr ctx, uint4_t chNum,
369                                  uint4_t reserved, uint8_t payloadTypeNum)
370 {
371     uint8_t channel =
372         convertCurrentChannelNum(static_cast<uint8_t>(chNum), ctx->channel);
373     constexpr uint8_t payloadTypeNotSupported = 0x80;
374 
375     if (reserved || !isValidChannel(channel))
376     {
377         lg2::debug("Get channel payload version - Invalid field in request");
378         return responseInvalidFieldRequest();
379     }
380 
381     if (getChannelSessionSupport(channel) == EChannelSessSupported::none)
382     {
383         lg2::debug("Get channel payload version - No support on channel");
384         return response(payloadTypeNotSupported);
385     }
386 
387     if (!isValidPayloadType(static_cast<PayloadType>(payloadTypeNum)))
388     {
389         lg2::error("Get channel payload version - Payload type unavailable");
390 
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));
registerChannelFunctions()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