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