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 
27 #include <bitset>
28 #include <cstring>
29 #include <vector>
30 
31 static constexpr const char* wdtService = "xyz.openbmc_project.Watchdog";
32 static constexpr const char* wdtInterface =
33     "xyz.openbmc_project.State.Watchdog";
34 static constexpr const char* wdtObjPath = "/xyz/openbmc_project/watchdog/host0";
35 static constexpr const char* wdtInterruptFlagProp =
36     "PreTimeoutInterruptOccurFlag";
37 
38 static constexpr const char* ipmbBus = "xyz.openbmc_project.Ipmi.Channel.Ipmb";
39 static constexpr const char* ipmbObj = "/xyz/openbmc_project/Ipmi/Channel/Ipmb";
40 static constexpr const char* ipmbIntf = "org.openbmc.Ipmb";
41 
42 static Bridging bridging;
43 static bool eventMessageBufferFlag = false;
44 
45 void Bridging::clearResponseQueue()
46 {
47     responseQueue.clear();
48 }
49 
50 /**
51  * @brief utils for checksum
52  */
53 static bool ipmbChecksumValidate(const uint8_t* data, uint8_t length)
54 {
55     if (data == nullptr)
56     {
57         return false;
58     }
59 
60     uint8_t checksum = 0;
61 
62     for (uint8_t idx = 0; idx < length; idx++)
63     {
64         checksum += data[idx];
65     }
66 
67     if (0 == checksum)
68     {
69         return true;
70     }
71 
72     return false;
73 }
74 
75 static uint8_t ipmbChecksumCompute(uint8_t* data, uint8_t length)
76 {
77     if (data == nullptr)
78     {
79         return 0;
80     }
81 
82     uint8_t checksum = 0;
83 
84     for (uint8_t idx = 0; idx < length; idx++)
85     {
86         checksum += data[idx];
87     }
88 
89     checksum = (~checksum) + 1;
90     return checksum;
91 }
92 
93 static inline bool
94     ipmbConnectionHeaderChecksumValidate(const ipmbHeader* ipmbHeader)
95 {
96     return ipmbChecksumValidate(reinterpret_cast<const uint8_t*>(ipmbHeader),
97                                 ipmbConnectionHeaderLength);
98 }
99 
100 static inline bool ipmbDataChecksumValidate(const ipmbHeader* ipmbHeader,
101                                             uint8_t length)
102 {
103     return ipmbChecksumValidate((reinterpret_cast<const uint8_t*>(ipmbHeader) +
104                                  ipmbConnectionHeaderLength),
105                                 (length - ipmbConnectionHeaderLength));
106 }
107 
108 static bool isFrameValid(const ipmbHeader* frame, uint8_t length)
109 {
110     if ((length < ipmbMinFrameLength) || (length > ipmbMaxFrameLength))
111     {
112         return false;
113     }
114 
115     if (false == ipmbConnectionHeaderChecksumValidate(frame))
116     {
117         return false;
118     }
119 
120     if (false == ipmbDataChecksumValidate(frame, length))
121     {
122         return false;
123     }
124 
125     return true;
126 }
127 
128 IpmbRequest::IpmbRequest(const ipmbHeader* ipmbBuffer, size_t bufferLength)
129 {
130     address = ipmbBuffer->Header.Req.address;
131     netFn = ipmbNetFnGet(ipmbBuffer->Header.Req.rsNetFnLUN);
132     rsLun = ipmbLunFromNetFnLunGet(ipmbBuffer->Header.Req.rsNetFnLUN);
133     rqSA = ipmbBuffer->Header.Req.rqSA;
134     seq = ipmbSeqGet(ipmbBuffer->Header.Req.rqSeqLUN);
135     rqLun = ipmbLunFromSeqLunGet(ipmbBuffer->Header.Req.rqSeqLUN);
136     cmd = ipmbBuffer->Header.Req.cmd;
137 
138     size_t dataLength =
139         bufferLength - (ipmbConnectionHeaderLength +
140                         ipmbRequestDataHeaderLength + ipmbChecksumSize);
141 
142     if (dataLength > 0)
143     {
144         data.insert(data.end(), ipmbBuffer->Header.Req.data,
145                     &ipmbBuffer->Header.Req.data[dataLength]);
146     }
147 }
148 
149 IpmbResponse::IpmbResponse(uint8_t address, uint8_t netFn, uint8_t rqLun,
150                            uint8_t rsSA, uint8_t seq, uint8_t rsLun,
151                            uint8_t cmd, uint8_t completionCode,
152                            std::vector<uint8_t>& inputData) :
153     address(address),
154     netFn(netFn), rqLun(rqLun), rsSA(rsSA), seq(seq), rsLun(rsLun), cmd(cmd),
155     completionCode(completionCode)
156 {
157     data.reserve(ipmbMaxDataSize);
158 
159     if (inputData.size() > 0)
160     {
161         data = std::move(inputData);
162     }
163 }
164 
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 
194 void IpmbRequest::prepareRequest(sdbusplus::message::message& mesg)
195 {
196     mesg.append(ipmbMeChannelNum, netFn, rqLun, cmd, data);
197 }
198 
199 static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd)
200 {
201     return (netFn << 8) | cmd;
202 }
203 
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 cmdMeOemSlotI2cMasterWriteRead = 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 Master 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, cmdMeOemSlotI2cMasterWriteRead):
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 
242 ipmi::Cc Bridging::handleIpmbChannel(ipmi::Context::ptr ctx,
243                                      const uint8_t tracking,
244                                      const std::vector<uint8_t>& msgData,
245                                      std::vector<uint8_t>& rspData)
246 {
247     ipmi::Manufacturing mtm;
248 
249     size_t msgLen = msgData.size();
250     if ((msgLen < ipmbMinFrameLength) || (msgLen > ipmbMaxFrameLength))
251     {
252         phosphor::logging::log<phosphor::logging::level::INFO>(
253             "handleIpmbChannel, IPMB data length is invalid");
254         return ipmi::ccReqDataLenInvalid;
255     }
256 
257     // Bridging to ME requires Administrator lvl
258     if ((ctx->priv) != ipmi::Privilege::Admin)
259     {
260         return ipmi::ccInsufficientPrivilege;
261     }
262 
263     auto sendMsgReqData = reinterpret_cast<const ipmbHeader*>(msgData.data());
264 
265     // allow bridging to ME only
266     if (sendMsgReqData->Header.Req.address != ipmbMeSlaveAddress)
267     {
268         phosphor::logging::log<phosphor::logging::level::INFO>(
269             "handleIpmbChannel, IPMB address invalid");
270         return ipmi::ccParmOutOfRange;
271     }
272 
273     constexpr uint8_t shiftLUN = 2;
274     if (mtm.getMfgMode() == ipmi::SpecialMode::none)
275     {
276         if (!isMeCmdAllowed((sendMsgReqData->Header.Req.rsNetFnLUN >> shiftLUN),
277                             sendMsgReqData->Header.Req.cmd))
278         {
279             constexpr ipmi::Cc ccCmdNotSupportedInPresentState = 0xD5;
280             return ccCmdNotSupportedInPresentState;
281         }
282     }
283 
284     // check allowed modes
285     if (tracking != modeNoTracking && tracking != modeTrackRequest)
286     {
287         phosphor::logging::log<phosphor::logging::level::INFO>(
288             "handleIpmbChannel, mode not supported");
289         return ipmi::ccParmOutOfRange;
290     }
291 
292     // check if request contains valid IPMB frame
293     if (!isFrameValid(sendMsgReqData, msgLen))
294     {
295         phosphor::logging::log<phosphor::logging::level::INFO>(
296             "handleIpmbChannel, IPMB frame invalid");
297         return ipmi::ccParmOutOfRange;
298     }
299 
300     auto ipmbRequest = IpmbRequest(sendMsgReqData, msgLen);
301 
302     typedef std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t,
303                        std::vector<uint8_t>>
304         IPMBResponse;
305 
306     // send request to IPMB
307     boost::system::error_code ec;
308     auto ipmbResponse = ctx->bus->yield_method_call<IPMBResponse>(
309         ctx->yield, ec, ipmbBus, ipmbObj, ipmbIntf, "sendRequest",
310         ipmbMeChannelNum, ipmbRequest.netFn, ipmbRequest.rqLun, ipmbRequest.cmd,
311         ipmbRequest.data);
312     if (ec)
313     {
314         phosphor::logging::log<phosphor::logging::level::ERR>(
315             "handleIpmbChannel, dbus call exception");
316         return ipmi::ccUnspecifiedError;
317     }
318 
319     std::vector<uint8_t> dataReceived(0);
320     int status = -1;
321     uint8_t netFn = 0, lun = 0, cmd = 0, cc = 0;
322 
323     std::tie(status, netFn, lun, cmd, cc, dataReceived) = ipmbResponse;
324 
325     auto respReceived =
326         IpmbResponse(ipmbRequest.rqSA, netFn, lun, ipmbRequest.address,
327                      ipmbRequest.seq, lun, cmd, cc, dataReceived);
328 
329     // check IPMB layer status
330     if (status)
331     {
332         phosphor::logging::log<phosphor::logging::level::WARNING>(
333             "handleIpmbChannel, ipmb returned non zero status");
334         return ipmi::ccResponseError;
335     }
336 
337     switch (tracking)
338     {
339         case modeNoTracking:
340         {
341             if (getResponseQueueSize() == responseQueueMaxSize)
342             {
343                 return ipmi::ccBusy;
344             }
345             insertMessageInQueue(respReceived);
346             break;
347         }
348         case modeTrackRequest:
349         {
350             size_t dataLength = 0;
351             respReceived.ipmbToi2cConstruct(rspData.data(), &dataLength);
352             // resizing the rspData to its correct length
353             rspData.resize(dataLength);
354             break;
355         }
356         default:
357         {
358             phosphor::logging::log<phosphor::logging::level::INFO>(
359                 "handleIpmbChannel, mode not supported");
360             return ipmi::ccParmOutOfRange;
361         }
362     }
363 
364     return ipmi::ccSuccess;
365 }
366 
367 void Bridging::insertMessageInQueue(IpmbResponse msg)
368 {
369     responseQueue.insert(responseQueue.end(), std::move(msg));
370 }
371 
372 void Bridging::eraseMessageFromQueue()
373 {
374     responseQueue.erase(responseQueue.begin());
375 }
376 
377 IpmbResponse Bridging::getMessageFromQueue()
378 {
379     return responseQueue.front();
380 }
381 
382 /**
383  * @brief This command is used for bridging ipmi message between channels.
384  * @param channelNumber         - channel number to send message to
385  * @param authenticationEnabled - authentication.
386  * @param encryptionEnabled     - encryption
387  * @param Tracking              - track request
388  * @param msg                   - message data
389  *
390  * @return IPMI completion code plus response data on success.
391  * - rspData - response data
392  **/
393 ipmi::RspType<std::vector<uint8_t> // responseData
394               >
395     ipmiAppSendMessage(ipmi::Context::ptr ctx, const uint4_t channelNumber,
396                        const bool authenticationEnabled,
397                        const bool encryptionEnabled, const uint2_t tracking,
398                        ipmi::message::Payload& msg)
399 {
400     // check message fields:
401     // encryption not supported
402     if (encryptionEnabled)
403     {
404         phosphor::logging::log<phosphor::logging::level::INFO>(
405             "ipmiAppSendMessage, encryption not supported");
406         return ipmi::responseParmOutOfRange();
407     }
408 
409     // authentication not supported
410     if (authenticationEnabled)
411     {
412         phosphor::logging::log<phosphor::logging::level::INFO>(
413             "ipmiAppSendMessage, authentication not supported");
414         return ipmi::responseParmOutOfRange();
415     }
416 
417     ipmi::Cc returnVal;
418     std::vector<uint8_t> rspData(ipmbMaxFrameLength);
419     size_t dataLength = 0;
420     std::vector<uint8_t> unpackMsg;
421 
422     auto channelNo = static_cast<const 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<const 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               >
471     ipmiAppGetMessage()
472 {
473     uint8_t channelData = 0;
474     std::vector<uint8_t> res(ipmbMaxFrameLength);
475     size_t dataLength = 0;
476 
477     if (!bridging.getResponseQueueSize())
478     {
479         constexpr ipmi::Cc ipmiGetMessageCmdDataNotAvailable = 0x80;
480         phosphor::logging::log<phosphor::logging::level::INFO>(
481             "ipmiAppGetMessage, no data available");
482         return ipmi::response(ipmiGetMessageCmdDataNotAvailable);
483     }
484 
485     // channel number set.
486     channelData |= static_cast<uint8_t>(targetChannelSystemInterface) & 0x0F;
487 
488     // Priviledge level set.
489     channelData |= SYSTEM_INTERFACE & 0xF0;
490 
491     // Get the first message from queue
492     auto respQueueItem = bridging.getMessageFromQueue();
493 
494     // construct response data.
495     respQueueItem.ipmbToi2cConstruct(res.data(), &dataLength);
496 
497     // Remove the message from queue
498     bridging.eraseMessageFromQueue();
499 
500     // resizing the rspData to its correct length
501     res.resize(dataLength);
502 
503     return ipmi::responseSuccess(channelData, res);
504 }
505 
506 std::size_t Bridging::getResponseQueueSize()
507 {
508     return responseQueue.size();
509 }
510 
511 /**
512 @brief This command is used to retrive present message available states.
513 
514 @return IPMI completion code plus Flags as response data on success.
515 **/
516 ipmi::RspType<std::bitset<8>> ipmiAppGetMessageFlags()
517 {
518     std::bitset<8> getMsgFlagsRes;
519 
520     // set event message buffer bit
521     if (!eventMessageBufferFlag)
522     {
523         getMsgFlagsRes.set(getMsgFlagEventMessageBit);
524     }
525     else
526     {
527         getMsgFlagsRes.reset(getMsgFlagEventMessageBit);
528     }
529 
530     // set message fields
531     if (bridging.getResponseQueueSize() > 0)
532     {
533         getMsgFlagsRes.set(getMsgFlagReceiveMessageBit);
534     }
535     else
536     {
537         getMsgFlagsRes.reset(getMsgFlagReceiveMessageBit);
538     }
539 
540     try
541     {
542         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
543         ipmi::Value variant = ipmi::getDbusProperty(
544             *dbus, wdtService, wdtObjPath, wdtInterface, wdtInterruptFlagProp);
545         if (std::get<bool>(variant))
546         {
547             getMsgFlagsRes.set(getMsgFlagWatchdogPreTimeOutBit);
548         }
549     }
550     catch (sdbusplus::exception::SdBusError& e)
551     {
552         phosphor::logging::log<phosphor::logging::level::ERR>(
553             "ipmiAppGetMessageFlags, dbus call exception");
554         return ipmi::responseUnspecifiedError();
555     }
556 
557     return ipmi::responseSuccess(getMsgFlagsRes);
558 }
559 
560 /** @brief This command is used to flush unread data from the receive
561  *   message queue
562  *  @param receiveMessage  - clear receive message queue
563  *  @param eventMsgBufFull - clear event message buffer full
564  *  @param reserved2       - reserved bit
565  *  @param watchdogTimeout - clear watchdog pre-timeout interrupt flag
566  *  @param reserved1       - reserved bit
567  *  @param oem0            - clear OEM 0 data
568  *  @param oem1            - clear OEM 1 data
569  *  @param oem2            - clear OEM 2 data
570 
571  *  @return IPMI completion code on success
572  */
573 ipmi::RspType<> ipmiAppClearMessageFlags(bool receiveMessage,
574                                          bool eventMsgBufFull, bool reserved2,
575                                          bool watchdogTimeout, bool reserved1,
576                                          bool oem0, bool oem1, bool oem2)
577 {
578     if (reserved1 || reserved2)
579     {
580         return ipmi::responseInvalidFieldRequest();
581     }
582 
583     if (receiveMessage)
584     {
585         bridging.clearResponseQueue();
586     }
587 
588     if (eventMessageBufferFlag != true && eventMsgBufFull == true)
589     {
590         eventMessageBufferFlag = true;
591     }
592 
593     try
594     {
595         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
596         ipmi::setDbusProperty(*dbus, wdtService, wdtObjPath, wdtInterface,
597                               wdtInterruptFlagProp, false);
598     }
599     catch (const sdbusplus::exception::SdBusError& e)
600     {
601         phosphor::logging::log<phosphor::logging::level::ERR>(
602             "ipmiAppClearMessageFlags: can't Clear/Set "
603             "PreTimeoutInterruptOccurFlag");
604         return ipmi::responseUnspecifiedError();
605     }
606 
607     return ipmi::responseSuccess();
608 }
609 
610 using systemEventType = std::tuple<
611     uint16_t, // Generator ID
612     uint32_t, // Timestamp
613     uint8_t,  // Sensor Type
614     uint8_t,  // EvM Rev
615     uint8_t,  // Sensor Number
616     uint7_t,  // Event Type
617     bool,     // Event Direction
618     std::array<uint8_t, intel_oem::ipmi::sel::systemEventSize>>; // Event Data
619 using oemTsEventType = std::tuple<
620     uint32_t,                                                   // Timestamp
621     std::array<uint8_t, intel_oem::ipmi::sel::oemTsEventSize>>; // Event Data
622 using oemEventType =
623     std::array<uint8_t, intel_oem::ipmi::sel::oemEventSize>; // Event Data
624 
625 /** @brief implements of Read event message buffer command
626  *
627  *  @returns IPMI completion code plus response data
628  *   - recordID - SEL Record ID
629  *   - recordType - Record Type
630  *   - generatorID - Generator ID
631  *   - timeStamp - Timestamp
632  *   - sensorType - Sensor Type
633  *   - eventMsgFormatRev - Event Message format version
634  *   - sensorNumber - Sensor Number
635  *   - eventType - Event Type
636  *   - eventDir - Event Direction
637  *   - eventData - Event Data field
638  */
639 ipmi::RspType<uint16_t, // Record ID
640               uint8_t,  // Record Type
641               std::variant<systemEventType, oemTsEventType,
642                            oemEventType>> // Record Content
643     ipmiAppReadEventMessageBuffer()
644 {
645     uint16_t recordId =
646         static_cast<uint16_t>(0x5555); // recordId: 0x55 << 8 | 0x55
647     uint16_t generatorId =
648         static_cast<uint16_t>(0xA741); // generatorId: 0xA7 << 8 | 0x41
649     constexpr uint8_t recordType = 0xC0;
650     constexpr uint8_t eventMsgFormatRev = 0x3A;
651     constexpr uint8_t sensorNumber = 0xFF;
652 
653     // TODO need to be implemented.
654     std::array<uint8_t, intel_oem::ipmi::sel::systemEventSize> eventData{};
655     // All '0xFF' since unused.
656     eventData.fill(0xFF);
657 
658     // Set the event message buffer flag
659     eventMessageBufferFlag = true;
660 
661     return ipmi::responseSuccess(
662         recordId, recordType,
663         systemEventType{generatorId, 0, 0, eventMsgFormatRev, sensorNumber,
664                         static_cast<uint7_t>(0), false, eventData});
665 }
666 
667 static void register_bridging_functions() __attribute__((constructor));
668 static void register_bridging_functions()
669 {
670     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp,
671                           ipmi::app::cmdClearMessageFlags,
672                           ipmi::Privilege::User, ipmiAppClearMessageFlags);
673 
674     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp,
675                           ipmi::app::cmdGetMessageFlags, ipmi::Privilege::User,
676                           ipmiAppGetMessageFlags);
677 
678     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp,
679                           ipmi::app::cmdGetMessage, ipmi::Privilege::User,
680                           ipmiAppGetMessage);
681 
682     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp,
683                           ipmi::app::cmdSendMessage, ipmi::Privilege::User,
684                           ipmiAppSendMessage);
685 
686     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp,
687                           ipmi::app::cmdReadEventMessageBuffer,
688                           ipmi::Privilege::User, ipmiAppReadEventMessageBuffer);
689 
690     return;
691 }
692