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