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 
101 static inline bool
ipmbDataChecksumValidate(const ipmbHeader * ipmbHeader,size_t length)102     ipmbDataChecksumValidate(const ipmbHeader* ipmbHeader, 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(0);
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         default:
356         {
357             phosphor::logging::log<phosphor::logging::level::INFO>(
358                 "handleIpmbChannel, mode not supported");
359             return ipmi::ccParmOutOfRange;
360         }
361     }
362 
363     return ipmi::ccSuccess;
364 }
365 
insertMessageInQueue(IpmbResponse msg)366 void Bridging::insertMessageInQueue(IpmbResponse msg)
367 {
368     responseQueue.insert(responseQueue.end(), std::move(msg));
369 }
370 
eraseMessageFromQueue()371 void Bridging::eraseMessageFromQueue()
372 {
373     responseQueue.erase(responseQueue.begin());
374 }
375 
getMessageFromQueue()376 IpmbResponse Bridging::getMessageFromQueue()
377 {
378     return responseQueue.front();
379 }
380 
381 /**
382  * @brief This command is used for bridging ipmi message between channels.
383  * @param channelNumber         - channel number to send message to
384  * @param authenticationEnabled - authentication.
385  * @param encryptionEnabled     - encryption
386  * @param Tracking              - track request
387  * @param msg                   - message data
388  *
389  * @return IPMI completion code plus response data on success.
390  * - rspData - response data
391  **/
392 ipmi::RspType<std::vector<uint8_t> // responseData
393               >
ipmiAppSendMessage(ipmi::Context::ptr & ctx,const uint4_t channelNumber,const bool authenticationEnabled,const bool encryptionEnabled,const uint2_t tracking,ipmi::message::Payload & msg)394     ipmiAppSendMessage(ipmi::Context::ptr& ctx, const uint4_t channelNumber,
395                        const bool authenticationEnabled,
396                        const bool encryptionEnabled, const uint2_t tracking,
397                        ipmi::message::Payload& msg)
398 {
399     // check message fields:
400     // encryption not supported
401     if (encryptionEnabled)
402     {
403         phosphor::logging::log<phosphor::logging::level::INFO>(
404             "ipmiAppSendMessage, encryption not supported");
405         return ipmi::responseParmOutOfRange();
406     }
407 
408     // authentication not supported
409     if (authenticationEnabled)
410     {
411         phosphor::logging::log<phosphor::logging::level::INFO>(
412             "ipmiAppSendMessage, authentication not supported");
413         return ipmi::responseParmOutOfRange();
414     }
415 
416     ipmi::Cc returnVal;
417     std::vector<uint8_t> rspData(ipmbMaxFrameLength);
418     std::vector<uint8_t> unpackMsg;
419 
420     auto channelNo = static_cast<uint8_t>(channelNumber);
421     // Get the channel number
422     switch (channelNo)
423     {
424         // we only handle ipmb for now
425         case targetChannelIpmb:
426         case targetChannelOtherLan:
427             if (msg.unpack(unpackMsg) || !msg.fullyUnpacked())
428             {
429                 return ipmi::responseReqDataLenInvalid();
430             }
431 
432             returnVal = bridging.handleIpmbChannel(
433                 ctx, static_cast<uint8_t>(tracking), unpackMsg, rspData);
434             break;
435         // fall through to default
436         case targetChannelIcmb10:
437         case targetChannelIcmb09:
438         case targetChannelLan:
439         case targetChannelSerialModem:
440         case targetChannelPciSmbus:
441         case targetChannelSmbus10:
442         case targetChannelSmbus20:
443         case targetChannelSystemInterface:
444         default:
445             phosphor::logging::log<phosphor::logging::level::INFO>(
446                 "ipmiAppSendMessage, TargetChannel invalid");
447             return ipmi::responseParmOutOfRange();
448     }
449     if (returnVal != ipmi::ccSuccess)
450     {
451         return ipmi::response(returnVal);
452     }
453 
454     return ipmi::responseSuccess(rspData);
455 }
456 
457 /**
458  * @brief This command is used to Get data from the receive message queue.
459  *  This command should be executed executed via system interface only.
460  *
461  * @return IPMI completion code plus response data on success.
462  * - channelNumber
463  * - messageData
464  **/
465 
466 ipmi::RspType<uint8_t,             // channelNumber
467               std::vector<uint8_t> // messageData
468               >
ipmiAppGetMessage(ipmi::Context::ptr & ctx)469     ipmiAppGetMessage(ipmi::Context::ptr& ctx)
470 {
471     ipmi::ChannelInfo chInfo;
472 
473     try
474     {
475         getChannelInfo(ctx->channel, chInfo);
476     }
477     catch (const sdbusplus::exception_t& e)
478     {
479         phosphor::logging::log<phosphor::logging::level::ERR>(
480             "ipmiAppGetMessage: Failed to get Channel Info",
481             phosphor::logging::entry("MSG: %s", e.description()));
482         return ipmi::responseUnspecifiedError();
483     }
484     if (chInfo.mediumType !=
485         static_cast<uint8_t>(ipmi::EChannelMediumType::systemInterface))
486     {
487         phosphor::logging::log<phosphor::logging::level::ERR>(
488             "ipmiAppGetMessage: Error - supported only in System(SMS) "
489             "interface");
490         return ipmi::responseCommandNotAvailable();
491     }
492 
493     uint8_t channelData = 0;
494     std::vector<uint8_t> res(ipmbMaxFrameLength);
495     size_t dataLength = 0;
496 
497     if (!bridging.getResponseQueueSize())
498     {
499         constexpr ipmi::Cc ipmiGetMessageCmdDataNotAvailable = 0x80;
500         phosphor::logging::log<phosphor::logging::level::INFO>(
501             "ipmiAppGetMessage, no data available");
502         return ipmi::response(ipmiGetMessageCmdDataNotAvailable);
503     }
504 
505     // channel number set.
506     channelData |= static_cast<uint8_t>(targetChannelSystemInterface) & 0x0F;
507 
508     // Priviledge level set.
509     channelData |= SYSTEM_INTERFACE & 0xF0;
510 
511     // Get the first message from queue
512     auto respQueueItem = bridging.getMessageFromQueue();
513 
514     // construct response data.
515     respQueueItem.ipmbToi2cConstruct(res.data(), &dataLength);
516 
517     // Remove the message from queue
518     bridging.eraseMessageFromQueue();
519 
520     // resizing the rspData to its correct length
521     res.resize(dataLength);
522 
523     return ipmi::responseSuccess(channelData, res);
524 }
525 
getResponseQueueSize()526 std::size_t Bridging::getResponseQueueSize()
527 {
528     return responseQueue.size();
529 }
530 
531 /**
532 @brief This command is used to retrive present message available states.
533 
534 @return IPMI completion code plus Flags as response data on success.
535 **/
ipmiAppGetMessageFlags(ipmi::Context::ptr & ctx)536 ipmi::RspType<std::bitset<8>> ipmiAppGetMessageFlags(ipmi::Context::ptr& ctx)
537 {
538     ipmi::ChannelInfo chInfo;
539 
540     try
541     {
542         getChannelInfo(ctx->channel, chInfo);
543     }
544     catch (const sdbusplus::exception_t& e)
545     {
546         phosphor::logging::log<phosphor::logging::level::ERR>(
547             "ipmiAppGetMessageFlags: Failed to get Channel Info",
548             phosphor::logging::entry("MSG: %s", e.description()));
549         return ipmi::responseUnspecifiedError();
550     }
551     if (chInfo.mediumType !=
552         static_cast<uint8_t>(ipmi::EChannelMediumType::systemInterface))
553     {
554         phosphor::logging::log<phosphor::logging::level::ERR>(
555             "ipmiAppGetMessageFlags: Error - supported only in System(SMS) "
556             "interface");
557         return ipmi::responseCommandNotAvailable();
558     }
559 
560     std::bitset<8> getMsgFlagsRes;
561 
562     // set event message buffer bit
563     if (!eventMessageBufferFlag)
564     {
565         getMsgFlagsRes.set(getMsgFlagEventMessageBit);
566     }
567     else
568     {
569         getMsgFlagsRes.reset(getMsgFlagEventMessageBit);
570     }
571 
572     // set message fields
573     if (bridging.getResponseQueueSize() > 0)
574     {
575         getMsgFlagsRes.set(getMsgFlagReceiveMessageBit);
576     }
577     else
578     {
579         getMsgFlagsRes.reset(getMsgFlagReceiveMessageBit);
580     }
581 
582     try
583     {
584         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
585         ipmi::Value variant = ipmi::getDbusProperty(
586             *dbus, wdtService, wdtObjPath, wdtInterface, wdtInterruptFlagProp);
587         if (std::get<bool>(variant))
588         {
589             getMsgFlagsRes.set(getMsgFlagWatchdogPreTimeOutBit);
590         }
591     }
592     catch (const sdbusplus::exception_t& e)
593     {
594         phosphor::logging::log<phosphor::logging::level::ERR>(
595             "ipmiAppGetMessageFlags, dbus call exception");
596         return ipmi::responseUnspecifiedError();
597     }
598 
599     return ipmi::responseSuccess(getMsgFlagsRes);
600 }
601 
602 /** @brief This command is used to flush unread data from the receive
603  *   message queue
604  *  @param receiveMessage  - clear receive message queue
605  *  @param eventMsgBufFull - clear event message buffer full
606  *  @param reserved2       - reserved bit
607  *  @param watchdogTimeout - clear watchdog pre-timeout interrupt flag
608  *  @param reserved1       - reserved bit
609  *  @param oem0            - clear OEM 0 data
610  *  @param oem1            - clear OEM 1 data
611  *  @param oem2            - clear OEM 2 data
612 
613  *  @return IPMI completion code on success
614  */
ipmiAppClearMessageFlags(ipmi::Context::ptr & ctx,bool receiveMessage,bool eventMsgBufFull,bool reserved2,bool watchdogTimeout,bool reserved1,bool,bool,bool)615 ipmi::RspType<> ipmiAppClearMessageFlags(
616     ipmi::Context::ptr& ctx, bool receiveMessage, bool eventMsgBufFull,
617     bool reserved2, bool watchdogTimeout, bool reserved1, bool /* oem0 */,
618     bool /* oem1 */, bool /* oem2 */)
619 {
620     ipmi::ChannelInfo chInfo;
621 
622     try
623     {
624         getChannelInfo(ctx->channel, chInfo);
625     }
626     catch (const sdbusplus::exception_t& e)
627     {
628         phosphor::logging::log<phosphor::logging::level::ERR>(
629             "ipmiAppClearMessageFlags: Failed to get Channel Info",
630             phosphor::logging::entry("MSG: %s", e.description()));
631         return ipmi::responseUnspecifiedError();
632     }
633     if (chInfo.mediumType !=
634         static_cast<uint8_t>(ipmi::EChannelMediumType::systemInterface))
635     {
636         phosphor::logging::log<phosphor::logging::level::ERR>(
637             "ipmiAppClearMessageFlags: Error - supported only in System(SMS) "
638             "interface");
639         return ipmi::responseCommandNotAvailable();
640     }
641 
642     if (reserved1 || reserved2)
643     {
644         return ipmi::responseInvalidFieldRequest();
645     }
646 
647     if (receiveMessage)
648     {
649         bridging.clearResponseQueue();
650     }
651 
652     if (eventMessageBufferFlag != true && eventMsgBufFull == true)
653     {
654         eventMessageBufferFlag = true;
655     }
656 
657     try
658     {
659         if (watchdogTimeout)
660         {
661             std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
662             ipmi::setDbusProperty(*dbus, wdtService, wdtObjPath, wdtInterface,
663                                   wdtInterruptFlagProp, false);
664         }
665     }
666     catch (const sdbusplus::exception_t& e)
667     {
668         phosphor::logging::log<phosphor::logging::level::ERR>(
669             "ipmiAppClearMessageFlags: can't Clear/Set "
670             "PreTimeoutInterruptOccurFlag");
671         return ipmi::responseUnspecifiedError();
672     }
673 
674     return ipmi::responseSuccess();
675 }
676 
677 using systemEventType = std::tuple<
678     uint16_t, // Generator ID
679     uint32_t, // Timestamp
680     uint8_t,  // Sensor Type
681     uint8_t,  // EvM Rev
682     uint8_t,  // Sensor Number
683     uint7_t,  // Event Type
684     bool,     // Event Direction
685     std::array<uint8_t, intel_oem::ipmi::sel::systemEventSize>>; // Event Data
686 using oemTsEventType = std::tuple<
687     uint32_t,                                                    // Timestamp
688     std::array<uint8_t, intel_oem::ipmi::sel::oemTsEventSize>>;  // Event Data
689 using oemEventType =
690     std::array<uint8_t, intel_oem::ipmi::sel::oemEventSize>;     // Event Data
691 
692 /** @brief implements of Read event message buffer command
693  *
694  *  @returns IPMI completion code plus response data
695  *   - recordID - SEL Record ID
696  *   - recordType - Record Type
697  *   - generatorID - Generator ID
698  *   - timeStamp - Timestamp
699  *   - sensorType - Sensor Type
700  *   - eventMsgFormatRev - Event Message format version
701  *   - sensorNumber - Sensor Number
702  *   - eventType - Event Type
703  *   - eventDir - Event Direction
704  *   - eventData - Event Data field
705  */
706 ipmi::RspType<uint16_t,                   // Record ID
707               uint8_t,                    // Record Type
708               std::variant<systemEventType, oemTsEventType,
709                            oemEventType>> // Record Content
ipmiAppReadEventMessageBuffer(ipmi::Context::ptr & ctx)710     ipmiAppReadEventMessageBuffer(ipmi::Context::ptr& ctx)
711 {
712     ipmi::ChannelInfo chInfo;
713 
714     try
715     {
716         getChannelInfo(ctx->channel, chInfo);
717     }
718     catch (const sdbusplus::exception_t& e)
719     {
720         phosphor::logging::log<phosphor::logging::level::ERR>(
721             "ipmiAppReadEventMessageBuffer: Failed to get Channel Info",
722             phosphor::logging::entry("MSG: %s", e.description()));
723         return ipmi::responseUnspecifiedError();
724     }
725     if (chInfo.mediumType !=
726         static_cast<uint8_t>(ipmi::EChannelMediumType::systemInterface))
727     {
728         phosphor::logging::log<phosphor::logging::level::ERR>(
729             "ipmiAppReadEventMessageBuffer: Error - supported only in "
730             "System(SMS) interface");
731         return ipmi::responseCommandNotAvailable();
732     }
733 
734     uint16_t recordId =
735         static_cast<uint16_t>(0x5555); // recordId: 0x55 << 8 | 0x55
736     uint16_t generatorId =
737         static_cast<uint16_t>(0xA741); // generatorId: 0xA7 << 8 | 0x41
738     constexpr uint8_t recordType = 0xC0;
739     constexpr uint8_t eventMsgFormatRev = 0x3A;
740     constexpr uint8_t sensorNumber = 0xFF;
741 
742     // TODO need to be implemented.
743     std::array<uint8_t, intel_oem::ipmi::sel::systemEventSize> eventData{};
744     // All '0xFF' since unused.
745     eventData.fill(0xFF);
746 
747     // Set the event message buffer flag
748     eventMessageBufferFlag = true;
749 
750     return ipmi::responseSuccess(
751         recordId, recordType,
752         systemEventType{generatorId, 0, 0, eventMsgFormatRev, sensorNumber,
753                         static_cast<uint7_t>(0), false, eventData});
754 }
755 
756 static void register_bridging_functions() __attribute__((constructor));
register_bridging_functions()757 static void register_bridging_functions()
758 {
759     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp,
760                           ipmi::app::cmdClearMessageFlags,
761                           ipmi::Privilege::User, ipmiAppClearMessageFlags);
762 
763     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp,
764                           ipmi::app::cmdGetMessageFlags, ipmi::Privilege::User,
765                           ipmiAppGetMessageFlags);
766 
767     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp,
768                           ipmi::app::cmdGetMessage, ipmi::Privilege::User,
769                           ipmiAppGetMessage);
770 
771     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp,
772                           ipmi::app::cmdSendMessage, ipmi::Privilege::User,
773                           ipmiAppSendMessage);
774 
775     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp,
776                           ipmi::app::cmdReadEventMessageBuffer,
777                           ipmi::Privilege::User, ipmiAppReadEventMessageBuffer);
778 
779     return;
780 }
781