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