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 <bridgingcommands.hpp>
18 #include <ipmid/api.hpp>
19 #include <ipmid/utils.hpp>
20 #include <manufacturingcommands.hpp>
21 #include <phosphor-logging/log.hpp>
22 #include <sdbusplus/bus.hpp>
23 #include <sdbusplus/bus/match.hpp>
24 #include <sdbusplus/message.hpp>
25 #include <storagecommands.hpp>
26 #include <user_channel/channel_layer.hpp>
27 
28 #include <bitset>
29 #include <cstring>
30 #include <vector>
31 
32 static constexpr const char* wdtService = "xyz.openbmc_project.Watchdog";
33 static constexpr const char* wdtInterface =
34     "xyz.openbmc_project.State.Watchdog";
35 static constexpr const char* wdtObjPath = "/xyz/openbmc_project/watchdog/host0";
36 static constexpr const char* wdtInterruptFlagProp =
37     "PreTimeoutInterruptOccurFlag";
38 
39 static constexpr const char* ipmbBus = "xyz.openbmc_project.Ipmi.Channel.Ipmb";
40 static constexpr const char* ipmbObj = "/xyz/openbmc_project/Ipmi/Channel/Ipmb";
41 static constexpr const char* ipmbIntf = "org.openbmc.Ipmb";
42 
43 static Bridging bridging;
44 static bool eventMessageBufferFlag = false;
45 
clearResponseQueue()46 void Bridging::clearResponseQueue()
47 {
48     responseQueue.clear();
49 }
50 
51 /**
52  * @brief utils for checksum
53  */
ipmbChecksumValidate(const uint8_t * data,uint8_t length)54 static bool ipmbChecksumValidate(const uint8_t* data, uint8_t length)
55 {
56     if (data == nullptr)
57     {
58         return false;
59     }
60 
61     uint8_t checksum = 0;
62 
63     for (uint8_t idx = 0; idx < length; idx++)
64     {
65         checksum += data[idx];
66     }
67 
68     if (0 == checksum)
69     {
70         return true;
71     }
72 
73     return false;
74 }
75 
ipmbChecksumCompute(uint8_t * data,uint8_t length)76 static uint8_t ipmbChecksumCompute(uint8_t* data, uint8_t length)
77 {
78     if (data == nullptr)
79     {
80         return 0;
81     }
82 
83     uint8_t checksum = 0;
84 
85     for (uint8_t idx = 0; idx < length; idx++)
86     {
87         checksum += data[idx];
88     }
89 
90     checksum = (~checksum) + 1;
91     return checksum;
92 }
93 
94 static inline bool
ipmbConnectionHeaderChecksumValidate(const ipmbHeader * ipmbHeader)95     ipmbConnectionHeaderChecksumValidate(const ipmbHeader* ipmbHeader)
96 {
97     return ipmbChecksumValidate(reinterpret_cast<const uint8_t*>(ipmbHeader),
98                                 ipmbConnectionHeaderLength);
99 }
100 
ipmbDataChecksumValidate(const ipmbHeader * ipmbHeader,size_t length)101 static inline bool ipmbDataChecksumValidate(const ipmbHeader* ipmbHeader,
102                                             size_t length)
103 {
104     return ipmbChecksumValidate((reinterpret_cast<const uint8_t*>(ipmbHeader) +
105                                  ipmbConnectionHeaderLength),
106                                 (length - ipmbConnectionHeaderLength));
107 }
108 
isFrameValid(const ipmbHeader * frame,size_t length)109 static bool isFrameValid(const ipmbHeader* frame, size_t length)
110 {
111     if ((length < ipmbMinFrameLength) || (length > ipmbMaxFrameLength))
112     {
113         return false;
114     }
115 
116     if (false == ipmbConnectionHeaderChecksumValidate(frame))
117     {
118         return false;
119     }
120 
121     if (false == ipmbDataChecksumValidate(frame, length))
122     {
123         return false;
124     }
125 
126     return true;
127 }
128 
IpmbRequest(const ipmbHeader * ipmbBuffer,size_t bufferLength)129 IpmbRequest::IpmbRequest(const ipmbHeader* ipmbBuffer, size_t bufferLength)
130 {
131     address = ipmbBuffer->Header.Req.address;
132     netFn = ipmbNetFnGet(ipmbBuffer->Header.Req.rsNetFnLUN);
133     rsLun = ipmbLunFromNetFnLunGet(ipmbBuffer->Header.Req.rsNetFnLUN);
134     rqSA = ipmbBuffer->Header.Req.rqSA;
135     seq = ipmbSeqGet(ipmbBuffer->Header.Req.rqSeqLUN);
136     rqLun = ipmbLunFromSeqLunGet(ipmbBuffer->Header.Req.rqSeqLUN);
137     cmd = ipmbBuffer->Header.Req.cmd;
138 
139     size_t dataLength = bufferLength -
140                         (ipmbConnectionHeaderLength +
141                          ipmbRequestDataHeaderLength + ipmbChecksumSize);
142 
143     if (dataLength > 0)
144     {
145         data.insert(data.end(), ipmbBuffer->Header.Req.data,
146                     &ipmbBuffer->Header.Req.data[dataLength]);
147     }
148 }
149 
IpmbResponse(uint8_t address,uint8_t netFn,uint8_t rqLun,uint8_t rsSA,uint8_t seq,uint8_t rsLun,uint8_t cmd,uint8_t completionCode,std::vector<uint8_t> & inputData)150 IpmbResponse::IpmbResponse(uint8_t address, uint8_t netFn, uint8_t rqLun,
151                            uint8_t rsSA, uint8_t seq, uint8_t rsLun,
152                            uint8_t cmd, uint8_t completionCode,
153                            std::vector<uint8_t>& inputData) :
154     address(address),
155     netFn(netFn), rqLun(rqLun), rsSA(rsSA), seq(seq), rsLun(rsLun), cmd(cmd),
156     completionCode(completionCode)
157 {
158     data.reserve(ipmbMaxDataSize);
159 
160     if (inputData.size() > 0)
161     {
162         data = std::move(inputData);
163     }
164 }
165 
ipmbToi2cConstruct(uint8_t * buffer,size_t * bufferLength)166 void IpmbResponse::ipmbToi2cConstruct(uint8_t* buffer, size_t* bufferLength)
167 {
168     ipmbHeader* ipmbBuffer = (ipmbHeader*)buffer;
169 
170     ipmbBuffer->Header.Resp.address = address;
171     ipmbBuffer->Header.Resp.rqNetFnLUN = ipmbNetFnLunSet(netFn, rqLun);
172     ipmbBuffer->Header.Resp.rsSA = rsSA;
173     ipmbBuffer->Header.Resp.rsSeqLUN = ipmbSeqLunSet(seq, rsLun);
174     ipmbBuffer->Header.Resp.cmd = cmd;
175     ipmbBuffer->Header.Resp.completionCode = completionCode;
176 
177     ipmbBuffer->Header.Resp.checksum1 = ipmbChecksumCompute(
178         buffer, ipmbConnectionHeaderLength - ipmbChecksumSize);
179 
180     if (data.size() > 0)
181     {
182         std::copy(
183             data.begin(), data.end(),
184             &buffer[ipmbConnectionHeaderLength + ipmbResponseDataHeaderLength]);
185     }
186 
187     *bufferLength = data.size() + ipmbResponseDataHeaderLength +
188                     ipmbConnectionHeaderLength + ipmbChecksumSize;
189 
190     buffer[*bufferLength - ipmbChecksumSize] =
191         ipmbChecksumCompute(&buffer[ipmbChecksum2StartOffset],
192                             (ipmbResponseDataHeaderLength + data.size()));
193 }
194 
prepareRequest(sdbusplus::message_t & mesg)195 void IpmbRequest::prepareRequest(sdbusplus::message_t& mesg)
196 {
197     mesg.append(ipmbMeChannelNum, netFn, rqLun, cmd, data);
198 }
199 
makeCmdKey(unsigned int netFn,unsigned int cmd)200 static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd)
201 {
202     return (netFn << 8) | cmd;
203 }
204 
isMeCmdAllowed(uint8_t netFn,uint8_t cmd)205 static constexpr bool isMeCmdAllowed(uint8_t netFn, uint8_t cmd)
206 {
207     constexpr uint8_t netFnMeOEM = 0x2E;
208     constexpr uint8_t netFnMeOEMGeneral = 0x3E;
209     constexpr uint8_t cmdMeOemSendRawPeci = 0x40;
210     constexpr uint8_t cmdMeOemAggSendRawPeci = 0x41;
211     constexpr uint8_t cmdMeOemCpuPkgConfWrite = 0x43;
212     constexpr uint8_t cmdMeOemCpuPciConfWrite = 0x45;
213     constexpr uint8_t cmdMeOemReadMemSmbus = 0x47;
214     constexpr uint8_t cmdMeOemWriteMemSmbus = 0x48;
215     constexpr uint8_t cmdMeOemSlotIpmb = 0x51;
216     constexpr uint8_t cmdMeOemSlotI2cControllerWriteRead = 0x52;
217     constexpr uint8_t cmdMeOemSendRawPmbus = 0xD9;
218     constexpr uint8_t cmdMeOemUnlockMeRegion = 0xE7;
219     constexpr uint8_t cmdMeOemAggSendRawPmbus = 0xEC;
220 
221     switch (makeCmdKey(netFn, cmd))
222     {
223         // Restrict ME Controller write command
224         case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead):
225         // Restrict ME OEM commands
226         case makeCmdKey(netFnMeOEM, cmdMeOemSendRawPeci):
227         case makeCmdKey(netFnMeOEM, cmdMeOemAggSendRawPeci):
228         case makeCmdKey(netFnMeOEM, cmdMeOemCpuPkgConfWrite):
229         case makeCmdKey(netFnMeOEM, cmdMeOemCpuPciConfWrite):
230         case makeCmdKey(netFnMeOEM, cmdMeOemReadMemSmbus):
231         case makeCmdKey(netFnMeOEM, cmdMeOemWriteMemSmbus):
232         case makeCmdKey(netFnMeOEMGeneral, cmdMeOemSlotIpmb):
233         case makeCmdKey(netFnMeOEMGeneral, cmdMeOemSlotI2cControllerWriteRead):
234         case makeCmdKey(netFnMeOEM, cmdMeOemSendRawPmbus):
235         case makeCmdKey(netFnMeOEM, cmdMeOemUnlockMeRegion):
236         case makeCmdKey(netFnMeOEM, cmdMeOemAggSendRawPmbus):
237             return false;
238         default:
239             return true;
240     }
241 }
242 
handleIpmbChannel(ipmi::Context::ptr & ctx,const uint8_t tracking,const std::vector<uint8_t> & msgData,std::vector<uint8_t> & rspData)243 ipmi::Cc Bridging::handleIpmbChannel(ipmi::Context::ptr& ctx,
244                                      const uint8_t tracking,
245                                      const std::vector<uint8_t>& msgData,
246                                      std::vector<uint8_t>& rspData)
247 {
248     ipmi::Manufacturing mtm;
249 
250     size_t msgLen = msgData.size();
251     if ((msgLen < ipmbMinFrameLength) || (msgLen > ipmbMaxFrameLength))
252     {
253         phosphor::logging::log<phosphor::logging::level::INFO>(
254             "handleIpmbChannel, IPMB data length is invalid");
255         return ipmi::ccReqDataLenInvalid;
256     }
257 
258     // Bridging to ME requires Administrator lvl
259     if ((ctx->priv) != ipmi::Privilege::Admin)
260     {
261         return ipmi::ccInsufficientPrivilege;
262     }
263 
264     auto sendMsgReqData = reinterpret_cast<const ipmbHeader*>(msgData.data());
265 
266     // allow bridging to ME only
267     if (sendMsgReqData->Header.Req.address != ipmbMeTargetAddress)
268     {
269         phosphor::logging::log<phosphor::logging::level::INFO>(
270             "handleIpmbChannel, IPMB address invalid");
271         return ipmi::ccParmOutOfRange;
272     }
273 
274     constexpr uint8_t shiftLUN = 2;
275     if (mtm.getMfgMode() == ipmi::SpecialMode::none)
276     {
277         if (!isMeCmdAllowed((sendMsgReqData->Header.Req.rsNetFnLUN >> shiftLUN),
278                             sendMsgReqData->Header.Req.cmd))
279         {
280             constexpr ipmi::Cc ccCmdNotSupportedInPresentState = 0xD5;
281             return ccCmdNotSupportedInPresentState;
282         }
283     }
284 
285     // check allowed modes
286     if (tracking != modeNoTracking && tracking != modeTrackRequest)
287     {
288         phosphor::logging::log<phosphor::logging::level::INFO>(
289             "handleIpmbChannel, mode not supported");
290         return ipmi::ccParmOutOfRange;
291     }
292 
293     // check if request contains valid IPMB frame
294     if (!isFrameValid(sendMsgReqData, msgLen))
295     {
296         phosphor::logging::log<phosphor::logging::level::INFO>(
297             "handleIpmbChannel, IPMB frame invalid");
298         return ipmi::ccParmOutOfRange;
299     }
300 
301     auto ipmbRequest = IpmbRequest(sendMsgReqData, msgLen);
302 
303     typedef std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t,
304                        std::vector<uint8_t>>
305         IPMBResponse;
306 
307     // send request to IPMB
308     boost::system::error_code ec;
309     auto ipmbResponse = ctx->bus->yield_method_call<IPMBResponse>(
310         ctx->yield, ec, ipmbBus, ipmbObj, ipmbIntf, "sendRequest",
311         ipmbMeChannelNum, ipmbRequest.netFn, ipmbRequest.rqLun, ipmbRequest.cmd,
312         ipmbRequest.data);
313     if (ec)
314     {
315         phosphor::logging::log<phosphor::logging::level::ERR>(
316             "handleIpmbChannel, dbus call exception");
317         return ipmi::ccUnspecifiedError;
318     }
319 
320     std::vector<uint8_t> dataReceived(0);
321     int status = -1;
322     uint8_t netFn = 0, lun = 0, cmd = 0, cc = 0;
323 
324     std::tie(status, netFn, lun, cmd, cc, dataReceived) = ipmbResponse;
325 
326     auto respReceived = IpmbResponse(ipmbRequest.rqSA, netFn, lun,
327                                      ipmbRequest.address, ipmbRequest.seq, lun,
328                                      cmd, cc, dataReceived);
329 
330     // check IPMB layer status
331     if (status)
332     {
333         phosphor::logging::log<phosphor::logging::level::WARNING>(
334             "handleIpmbChannel, ipmb returned non zero status");
335         return ipmi::ccResponseError;
336     }
337 
338     switch (tracking)
339     {
340         case modeNoTracking:
341         {
342             if (getResponseQueueSize() == responseQueueMaxSize)
343             {
344                 return ipmi::ccBusy;
345             }
346             insertMessageInQueue(respReceived);
347             break;
348         }
349         case modeTrackRequest:
350         {
351             size_t dataLength = 0;
352             respReceived.ipmbToi2cConstruct(rspData.data(), &dataLength);
353             // resizing the rspData to its correct length
354             rspData.resize(dataLength);
355             break;
356         }
357         default:
358         {
359             phosphor::logging::log<phosphor::logging::level::INFO>(
360                 "handleIpmbChannel, mode not supported");
361             return ipmi::ccParmOutOfRange;
362         }
363     }
364 
365     return ipmi::ccSuccess;
366 }
367 
insertMessageInQueue(IpmbResponse msg)368 void Bridging::insertMessageInQueue(IpmbResponse msg)
369 {
370     responseQueue.insert(responseQueue.end(), std::move(msg));
371 }
372 
eraseMessageFromQueue()373 void Bridging::eraseMessageFromQueue()
374 {
375     responseQueue.erase(responseQueue.begin());
376 }
377 
getMessageFromQueue()378 IpmbResponse Bridging::getMessageFromQueue()
379 {
380     return responseQueue.front();
381 }
382 
383 /**
384  * @brief This command is used for bridging ipmi message between channels.
385  * @param channelNumber         - channel number to send message to
386  * @param authenticationEnabled - authentication.
387  * @param encryptionEnabled     - encryption
388  * @param Tracking              - track request
389  * @param msg                   - message data
390  *
391  * @return IPMI completion code plus response data on success.
392  * - rspData - response data
393  **/
394 ipmi::RspType<std::vector<uint8_t> // responseData
395               >
ipmiAppSendMessage(ipmi::Context::ptr & ctx,const uint4_t channelNumber,const bool authenticationEnabled,const bool encryptionEnabled,const uint2_t tracking,ipmi::message::Payload & msg)396     ipmiAppSendMessage(ipmi::Context::ptr& ctx, const uint4_t channelNumber,
397                        const bool authenticationEnabled,
398                        const bool encryptionEnabled, const uint2_t tracking,
399                        ipmi::message::Payload& msg)
400 {
401     // check message fields:
402     // encryption not supported
403     if (encryptionEnabled)
404     {
405         phosphor::logging::log<phosphor::logging::level::INFO>(
406             "ipmiAppSendMessage, encryption not supported");
407         return ipmi::responseParmOutOfRange();
408     }
409 
410     // authentication not supported
411     if (authenticationEnabled)
412     {
413         phosphor::logging::log<phosphor::logging::level::INFO>(
414             "ipmiAppSendMessage, authentication not supported");
415         return ipmi::responseParmOutOfRange();
416     }
417 
418     ipmi::Cc returnVal;
419     std::vector<uint8_t> rspData(ipmbMaxFrameLength);
420     std::vector<uint8_t> unpackMsg;
421 
422     auto channelNo = static_cast<uint8_t>(channelNumber);
423     // Get the channel number
424     switch (channelNo)
425     {
426         // we only handle ipmb for now
427         case targetChannelIpmb:
428         case targetChannelOtherLan:
429             if (msg.unpack(unpackMsg) || !msg.fullyUnpacked())
430             {
431                 return ipmi::responseReqDataLenInvalid();
432             }
433 
434             returnVal = bridging.handleIpmbChannel(
435                 ctx, static_cast<uint8_t>(tracking), unpackMsg, rspData);
436             break;
437         // fall through to default
438         case targetChannelIcmb10:
439         case targetChannelIcmb09:
440         case targetChannelLan:
441         case targetChannelSerialModem:
442         case targetChannelPciSmbus:
443         case targetChannelSmbus10:
444         case targetChannelSmbus20:
445         case targetChannelSystemInterface:
446         default:
447             phosphor::logging::log<phosphor::logging::level::INFO>(
448                 "ipmiAppSendMessage, TargetChannel invalid");
449             return ipmi::responseParmOutOfRange();
450     }
451     if (returnVal != ipmi::ccSuccess)
452     {
453         return ipmi::response(returnVal);
454     }
455 
456     return ipmi::responseSuccess(rspData);
457 }
458 
459 /**
460  * @brief This command is used to Get data from the receive message queue.
461  *  This command should be executed executed via system interface only.
462  *
463  * @return IPMI completion code plus response data on success.
464  * - channelNumber
465  * - messageData
466  **/
467 
468 ipmi::RspType<uint8_t,             // channelNumber
469               std::vector<uint8_t> // messageData
470               >
ipmiAppGetMessage(ipmi::Context::ptr & ctx)471     ipmiAppGetMessage(ipmi::Context::ptr& ctx)
472 {
473     ipmi::ChannelInfo chInfo;
474 
475     try
476     {
477         getChannelInfo(ctx->channel, chInfo);
478     }
479     catch (const sdbusplus::exception_t& e)
480     {
481         phosphor::logging::log<phosphor::logging::level::ERR>(
482             "ipmiAppGetMessage: Failed to get Channel Info",
483             phosphor::logging::entry("MSG: %s", e.description()));
484         return ipmi::responseUnspecifiedError();
485     }
486     if (chInfo.mediumType !=
487         static_cast<uint8_t>(ipmi::EChannelMediumType::systemInterface))
488     {
489         phosphor::logging::log<phosphor::logging::level::ERR>(
490             "ipmiAppGetMessage: Error - supported only in System(SMS) "
491             "interface");
492         return ipmi::responseCommandNotAvailable();
493     }
494 
495     uint8_t channelData = 0;
496     std::vector<uint8_t> res(ipmbMaxFrameLength);
497     size_t dataLength = 0;
498 
499     if (!bridging.getResponseQueueSize())
500     {
501         constexpr ipmi::Cc ipmiGetMessageCmdDataNotAvailable = 0x80;
502         phosphor::logging::log<phosphor::logging::level::INFO>(
503             "ipmiAppGetMessage, no data available");
504         return ipmi::response(ipmiGetMessageCmdDataNotAvailable);
505     }
506 
507     // channel number set.
508     channelData |= static_cast<uint8_t>(targetChannelSystemInterface) & 0x0F;
509 
510     // Priviledge level set.
511     channelData |= SYSTEM_INTERFACE & 0xF0;
512 
513     // Get the first message from queue
514     auto respQueueItem = bridging.getMessageFromQueue();
515 
516     // construct response data.
517     respQueueItem.ipmbToi2cConstruct(res.data(), &dataLength);
518 
519     // Remove the message from queue
520     bridging.eraseMessageFromQueue();
521 
522     // resizing the rspData to its correct length
523     res.resize(dataLength);
524 
525     return ipmi::responseSuccess(channelData, res);
526 }
527 
getResponseQueueSize()528 std::size_t Bridging::getResponseQueueSize()
529 {
530     return responseQueue.size();
531 }
532 
533 /**
534 @brief This command is used to retrive present message available states.
535 
536 @return IPMI completion code plus Flags as response data on success.
537 **/
ipmiAppGetMessageFlags(ipmi::Context::ptr & ctx)538 ipmi::RspType<std::bitset<8>> ipmiAppGetMessageFlags(ipmi::Context::ptr& ctx)
539 {
540     ipmi::ChannelInfo chInfo;
541 
542     try
543     {
544         getChannelInfo(ctx->channel, chInfo);
545     }
546     catch (const sdbusplus::exception_t& e)
547     {
548         phosphor::logging::log<phosphor::logging::level::ERR>(
549             "ipmiAppGetMessageFlags: Failed to get Channel Info",
550             phosphor::logging::entry("MSG: %s", e.description()));
551         return ipmi::responseUnspecifiedError();
552     }
553     if (chInfo.mediumType !=
554         static_cast<uint8_t>(ipmi::EChannelMediumType::systemInterface))
555     {
556         phosphor::logging::log<phosphor::logging::level::ERR>(
557             "ipmiAppGetMessageFlags: Error - supported only in System(SMS) "
558             "interface");
559         return ipmi::responseCommandNotAvailable();
560     }
561 
562     std::bitset<8> getMsgFlagsRes;
563 
564     // set event message buffer bit
565     if (!eventMessageBufferFlag)
566     {
567         getMsgFlagsRes.set(getMsgFlagEventMessageBit);
568     }
569     else
570     {
571         getMsgFlagsRes.reset(getMsgFlagEventMessageBit);
572     }
573 
574     // set message fields
575     if (bridging.getResponseQueueSize() > 0)
576     {
577         getMsgFlagsRes.set(getMsgFlagReceiveMessageBit);
578     }
579     else
580     {
581         getMsgFlagsRes.reset(getMsgFlagReceiveMessageBit);
582     }
583 
584     try
585     {
586         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
587         ipmi::Value variant = ipmi::getDbusProperty(
588             *dbus, wdtService, wdtObjPath, wdtInterface, wdtInterruptFlagProp);
589         if (std::get<bool>(variant))
590         {
591             getMsgFlagsRes.set(getMsgFlagWatchdogPreTimeOutBit);
592         }
593     }
594     catch (const sdbusplus::exception_t& e)
595     {
596         phosphor::logging::log<phosphor::logging::level::ERR>(
597             "ipmiAppGetMessageFlags, dbus call exception");
598         return ipmi::responseUnspecifiedError();
599     }
600 
601     return ipmi::responseSuccess(getMsgFlagsRes);
602 }
603 
604 /** @brief This command is used to flush unread data from the receive
605  *   message queue
606  *  @param receiveMessage  - clear receive message queue
607  *  @param eventMsgBufFull - clear event message buffer full
608  *  @param reserved2       - reserved bit
609  *  @param watchdogTimeout - clear watchdog pre-timeout interrupt flag
610  *  @param reserved1       - reserved bit
611  *  @param oem0            - clear OEM 0 data
612  *  @param oem1            - clear OEM 1 data
613  *  @param oem2            - clear OEM 2 data
614 
615  *  @return IPMI completion code on success
616  */
ipmiAppClearMessageFlags(ipmi::Context::ptr & ctx,bool receiveMessage,bool eventMsgBufFull,bool reserved2,bool watchdogTimeout,bool reserved1,bool,bool,bool)617 ipmi::RspType<> ipmiAppClearMessageFlags(ipmi::Context::ptr& ctx,
618                                          bool receiveMessage,
619                                          bool eventMsgBufFull, bool reserved2,
620                                          bool watchdogTimeout, bool reserved1,
621                                          bool /* oem0 */, bool /* oem1 */,
622                                          bool /* oem2 */)
623 {
624     ipmi::ChannelInfo chInfo;
625 
626     try
627     {
628         getChannelInfo(ctx->channel, chInfo);
629     }
630     catch (const sdbusplus::exception_t& e)
631     {
632         phosphor::logging::log<phosphor::logging::level::ERR>(
633             "ipmiAppClearMessageFlags: Failed to get Channel Info",
634             phosphor::logging::entry("MSG: %s", e.description()));
635         return ipmi::responseUnspecifiedError();
636     }
637     if (chInfo.mediumType !=
638         static_cast<uint8_t>(ipmi::EChannelMediumType::systemInterface))
639     {
640         phosphor::logging::log<phosphor::logging::level::ERR>(
641             "ipmiAppClearMessageFlags: Error - supported only in System(SMS) "
642             "interface");
643         return ipmi::responseCommandNotAvailable();
644     }
645 
646     if (reserved1 || reserved2)
647     {
648         return ipmi::responseInvalidFieldRequest();
649     }
650 
651     if (receiveMessage)
652     {
653         bridging.clearResponseQueue();
654     }
655 
656     if (eventMessageBufferFlag != true && eventMsgBufFull == true)
657     {
658         eventMessageBufferFlag = true;
659     }
660 
661     try
662     {
663         if (watchdogTimeout)
664         {
665             std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
666             ipmi::setDbusProperty(*dbus, wdtService, wdtObjPath, wdtInterface,
667                                   wdtInterruptFlagProp, false);
668         }
669     }
670     catch (const sdbusplus::exception_t& e)
671     {
672         phosphor::logging::log<phosphor::logging::level::ERR>(
673             "ipmiAppClearMessageFlags: can't Clear/Set "
674             "PreTimeoutInterruptOccurFlag");
675         return ipmi::responseUnspecifiedError();
676     }
677 
678     return ipmi::responseSuccess();
679 }
680 
681 using systemEventType = std::tuple<
682     uint16_t, // Generator ID
683     uint32_t, // Timestamp
684     uint8_t,  // Sensor Type
685     uint8_t,  // EvM Rev
686     uint8_t,  // Sensor Number
687     uint7_t,  // Event Type
688     bool,     // Event Direction
689     std::array<uint8_t, intel_oem::ipmi::sel::systemEventSize>>; // Event Data
690 using oemTsEventType = std::tuple<
691     uint32_t,                                                    // Timestamp
692     std::array<uint8_t, intel_oem::ipmi::sel::oemTsEventSize>>;  // Event Data
693 using oemEventType =
694     std::array<uint8_t, intel_oem::ipmi::sel::oemEventSize>;     // Event Data
695 
696 /** @brief implements of Read event message buffer command
697  *
698  *  @returns IPMI completion code plus response data
699  *   - recordID - SEL Record ID
700  *   - recordType - Record Type
701  *   - generatorID - Generator ID
702  *   - timeStamp - Timestamp
703  *   - sensorType - Sensor Type
704  *   - eventMsgFormatRev - Event Message format version
705  *   - sensorNumber - Sensor Number
706  *   - eventType - Event Type
707  *   - eventDir - Event Direction
708  *   - eventData - Event Data field
709  */
710 ipmi::RspType<uint16_t,                   // Record ID
711               uint8_t,                    // Record Type
712               std::variant<systemEventType, oemTsEventType,
713                            oemEventType>> // Record Content
ipmiAppReadEventMessageBuffer(ipmi::Context::ptr & ctx)714     ipmiAppReadEventMessageBuffer(ipmi::Context::ptr& ctx)
715 {
716     ipmi::ChannelInfo chInfo;
717 
718     try
719     {
720         getChannelInfo(ctx->channel, chInfo);
721     }
722     catch (const sdbusplus::exception_t& e)
723     {
724         phosphor::logging::log<phosphor::logging::level::ERR>(
725             "ipmiAppReadEventMessageBuffer: Failed to get Channel Info",
726             phosphor::logging::entry("MSG: %s", e.description()));
727         return ipmi::responseUnspecifiedError();
728     }
729     if (chInfo.mediumType !=
730         static_cast<uint8_t>(ipmi::EChannelMediumType::systemInterface))
731     {
732         phosphor::logging::log<phosphor::logging::level::ERR>(
733             "ipmiAppReadEventMessageBuffer: Error - supported only in "
734             "System(SMS) interface");
735         return ipmi::responseCommandNotAvailable();
736     }
737 
738     uint16_t recordId =
739         static_cast<uint16_t>(0x5555); // recordId: 0x55 << 8 | 0x55
740     uint16_t generatorId =
741         static_cast<uint16_t>(0xA741); // generatorId: 0xA7 << 8 | 0x41
742     constexpr uint8_t recordType = 0xC0;
743     constexpr uint8_t eventMsgFormatRev = 0x3A;
744     constexpr uint8_t sensorNumber = 0xFF;
745 
746     // TODO need to be implemented.
747     std::array<uint8_t, intel_oem::ipmi::sel::systemEventSize> eventData{};
748     // All '0xFF' since unused.
749     eventData.fill(0xFF);
750 
751     // Set the event message buffer flag
752     eventMessageBufferFlag = true;
753 
754     return ipmi::responseSuccess(
755         recordId, recordType,
756         systemEventType{generatorId, 0, 0, eventMsgFormatRev, sensorNumber,
757                         static_cast<uint7_t>(0), false, eventData});
758 }
759 
760 static void register_bridging_functions() __attribute__((constructor));
register_bridging_functions()761 static void register_bridging_functions()
762 {
763     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp,
764                           ipmi::app::cmdClearMessageFlags,
765                           ipmi::Privilege::User, ipmiAppClearMessageFlags);
766 
767     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp,
768                           ipmi::app::cmdGetMessageFlags, ipmi::Privilege::User,
769                           ipmiAppGetMessageFlags);
770 
771     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp,
772                           ipmi::app::cmdGetMessage, ipmi::Privilege::User,
773                           ipmiAppGetMessage);
774 
775     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp,
776                           ipmi::app::cmdSendMessage, ipmi::Privilege::User,
777                           ipmiAppSendMessage);
778 
779     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp,
780                           ipmi::app::cmdReadEventMessageBuffer,
781                           ipmi::Privilege::User, ipmiAppReadEventMessageBuffer);
782 
783     return;
784 }
785