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