blob: e5fd1967a5634638074d8c2946c9b39ca3555a02 [file] [log] [blame]
Dawid Fryckia642a942018-06-12 10:44:23 -07001/* Copyright 2018 Intel
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "ipmbbridged.hpp"
17
18#include "ipmbdefines.hpp"
19#include "ipmbutils.hpp"
20
Dawid Fryckia642a942018-06-12 10:44:23 -070021#include <phosphor-logging/log.hpp>
22#include <tuple>
23
James Feista8c77dc2019-01-30 16:55:46 -080024extern "C" {
25#include <i2c/smbus.h>
26#include <linux/i2c-dev.h>
27}
28
Dawid Fryckia642a942018-06-12 10:44:23 -070029/**
30 * @brief Dbus
31 */
32static constexpr const char *ipmbBus = "xyz.openbmc_project.Ipmi.Channel.Ipmb";
33static constexpr const char *ipmbObj = "/xyz/openbmc_project/Ipmi/Channel/Ipmb";
34static constexpr const char *hostIpmiIntf = "org.openbmc.HostIpmi";
35static constexpr const char *ipmbDbusIntf = "org.openbmc.Ipmb";
36
37boost::asio::io_service io;
38auto conn = std::make_shared<sdbusplus::asio::connection>(io);
39
40/**
41 * @brief Channel configuration table
42 * TODO : move to user configuration as JSON file
43 */
44static const std::vector<IpmbChannelConfig> ipmbChannelsConfig = {
45 // ME channel
46 {ipmbChannelType::me, "/sys/bus/i2c/devices/5-1010/slave-mqueue",
47 "/dev/i2c-5", 0x20, 0x2C}, // 8 bit addresses
48 // IPMB header channel
49 {ipmbChannelType::ipmb, "/sys/bus/i2c/devices/0-1010/slave-mqueue",
50 "/dev/i2c-0", 0x20, 0x58}}; // 8 bit addresses
51
52static std::list<IpmbChannel> ipmbChannels;
53
54/**
55 * @brief Ipmb request class methods
56 */
57IpmbRequest::IpmbRequest()
58{
59 data.reserve(ipmbMaxDataSize);
60}
61
62IpmbRequest::IpmbRequest(uint8_t address, uint8_t netFn, uint8_t rsLun,
63 uint8_t rqSA, uint8_t seq, uint8_t rqLun, uint8_t cmd,
64 std::vector<uint8_t> &inputData) :
65 address(address),
66 netFn(netFn), rsLun(rsLun), rqSA(rqSA), seq(seq), rqLun(rqLun), cmd(cmd),
67 timer(io)
68{
69 data.reserve(ipmbMaxDataSize);
70 state = ipmbRequestState::invalid;
71
72 if (inputData.size() > 0)
73 {
74 data = std::move(inputData);
75 }
76}
77
78void IpmbRequest::incomingMessageHandler()
79{
80 sdbusplus::message::message mesg =
81 conn->new_signal(ipmbObj, hostIpmiIntf, "ReceivedMessage");
82 mesg.append(seq, netFn, rsLun, cmd, data);
83 mesg.signal_send();
84}
85
86void IpmbRequest::i2cToIpmbConstruct(IPMB_HEADER *ipmbBuffer,
87 size_t bufferLength)
88{
89 // constructing ipmb request from i2c buffer
90 netFn = ipmbNetFnGet(ipmbBuffer->Header.Req.rsNetFnLUN);
91 rsLun = ipmbLunFromNetFnLunGet(ipmbBuffer->Header.Req.rsNetFnLUN);
92 rqSA = ipmbBuffer->Header.Req.rqSA;
93 seq = ipmbSeqGet(ipmbBuffer->Header.Req.rqSeqLUN);
94 rqLun = ipmbLunFromSeqLunGet(ipmbBuffer->Header.Req.rqSeqLUN);
95 cmd = ipmbBuffer->Header.Req.cmd;
96
97 size_t dataLength =
98 bufferLength - (ipmbConnectionHeaderLength +
99 ipmbRequestDataHeaderLength + ipmbChecksumSize);
100
101 if (dataLength > 0)
102 {
103 data.insert(data.end(), ipmbBuffer->Header.Req.data,
104 &ipmbBuffer->Header.Req.data[dataLength]);
105 }
106}
107
108int IpmbRequest::ipmbToi2cConstruct(std::vector<uint8_t> &buffer)
109{
110 size_t bufferLength = data.size() + ipmbRequestDataHeaderLength +
111 ipmbConnectionHeaderLength + ipmbChecksumSize;
112
113 if (bufferLength > ipmbMaxFrameLength)
114 {
115 return -1;
116 }
117
118 buffer.resize(bufferLength);
119 static_assert(ipmbMaxFrameLength >= sizeof(IPMB_HEADER));
120 auto ipmbBuffer = reinterpret_cast<IPMB_HEADER *>(buffer.data());
121
122 // constructing buffer from ipmb request
123 ipmbBuffer->Header.Req.address = address;
124 ipmbBuffer->Header.Req.rsNetFnLUN = ipmbNetFnLunSet(netFn, rsLun);
125 ipmbBuffer->Header.Req.rqSA = rqSA;
126 ipmbBuffer->Header.Req.rqSeqLUN = ipmbSeqLunSet(seq, rqLun);
127 ipmbBuffer->Header.Req.cmd = cmd;
128
129 ipmbBuffer->Header.Resp.checksum1 = ipmbChecksumCompute(
130 buffer.data(), ipmbConnectionHeaderLength - ipmbChecksumSize);
131
132 if (data.size() > 0)
133 {
134 std::copy(data.begin(), data.end(), ipmbBuffer->Header.Req.data);
135 }
136
137 buffer[bufferLength - ipmbChecksumSize] =
138 ipmbChecksumCompute(buffer.data() + ipmbChecksum2StartOffset,
139 (ipmbRequestDataHeaderLength + data.size()));
140
141 return 0;
142}
143
144std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
145 IpmbRequest::returnMatchedResponse()
146{
147 return std::make_tuple(
148 static_cast<int>(ipmbResponseStatus::success), matchedResponse->netFn,
149 matchedResponse->rsLun, matchedResponse->cmd,
150 matchedResponse->completionCode, matchedResponse->data);
151}
152
153static std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
154 returnStatus(ipmbResponseStatus status)
155{
156 // we only want to send status here, other fields are not relevant
157 return std::make_tuple(static_cast<int>(status), 0, 0, 0, 0,
158 std::vector<uint8_t>(0));
159}
160
161// TODO w/a to differentiate channel origin of incoming IPMI response: saving
162// channel number at two oldest unused bits of seq
163void IpmbRequest::addChannelToSeq(const ipmbChannelType &channelType)
164{
165 uint8_t newSeq = (seq | ((static_cast<uint8_t>(channelType) & 0x3) << 6));
166 seq = newSeq;
167}
168
169/**
170 * @brief Ipmb response class methods
171 */
172IpmbResponse::IpmbResponse()
173{
174 data.reserve(ipmbMaxDataSize);
175}
176
177IpmbResponse::IpmbResponse(uint8_t address, uint8_t netFn, uint8_t rqLun,
178 uint8_t rsSA, uint8_t seq, uint8_t rsLun,
179 uint8_t cmd, uint8_t completionCode,
180 std::vector<uint8_t> &inputData) :
181 address(address),
182 netFn(netFn), rqLun(rqLun), rsSA(rsSA), seq(seq), rsLun(rsLun), cmd(cmd),
183 completionCode(completionCode)
184{
185 data.reserve(ipmbMaxDataSize);
186
187 if (inputData.size() > 0)
188 {
189 data = std::move(inputData);
190 }
191}
192
193void IpmbResponse::i2cToIpmbConstruct(IPMB_HEADER *ipmbBuffer,
194 size_t bufferLength)
195{
196 netFn = ipmbNetFnGet(ipmbBuffer->Header.Resp.rqNetFnLUN);
197 rqLun = ipmbLunFromNetFnLunGet(ipmbBuffer->Header.Resp.rqNetFnLUN);
198 rsSA = ipmbBuffer->Header.Resp.rsSA;
199 seq = ipmbSeqGet(ipmbBuffer->Header.Resp.rsSeqLUN);
200 rsLun = ipmbLunFromSeqLunGet(ipmbBuffer->Header.Resp.rsSeqLUN);
201 cmd = ipmbBuffer->Header.Resp.cmd;
202 completionCode = ipmbBuffer->Header.Resp.completionCode;
203
204 size_t dataLength =
205 bufferLength - (ipmbConnectionHeaderLength +
206 ipmbResponseDataHeaderLength + ipmbChecksumSize);
207
208 if (dataLength > 0)
209 {
210 data.insert(data.end(), ipmbBuffer->Header.Resp.data,
211 &ipmbBuffer->Header.Resp.data[dataLength]);
212 }
213}
214
215int IpmbResponse::ipmbToi2cConstruct(std::vector<uint8_t> &buffer)
216{
217 size_t bufferLength = data.size() + ipmbResponseDataHeaderLength +
218 ipmbConnectionHeaderLength + ipmbChecksumSize;
219
220 if (bufferLength > ipmbMaxFrameLength)
221 {
222 return -1;
223 }
224
225 buffer.resize(bufferLength);
226 auto ipmbBuffer = reinterpret_cast<IPMB_HEADER *>(buffer.data());
227
228 ipmbBuffer->Header.Resp.address = address;
229 ipmbBuffer->Header.Resp.rqNetFnLUN = ipmbNetFnLunSet(netFn, rqLun);
230 ipmbBuffer->Header.Resp.rsSA = rsSA;
231 ipmbBuffer->Header.Resp.rsSeqLUN = ipmbSeqLunSet(seq, rsLun);
232 ipmbBuffer->Header.Resp.cmd = cmd;
233 ipmbBuffer->Header.Resp.completionCode = completionCode;
234
235 ipmbBuffer->Header.Resp.checksum1 = ipmbChecksumCompute(
236 buffer.data(), ipmbConnectionHeaderLength - ipmbChecksumSize);
237
238 if (data.size() > 0)
239 {
240 std::copy(data.begin(), data.end(), ipmbBuffer->Header.Resp.data);
241 }
242
243 buffer[bufferLength - ipmbChecksumSize] =
244 ipmbChecksumCompute(buffer.data() + ipmbChecksum2StartOffset,
245 (ipmbResponseDataHeaderLength + data.size()));
246
247 return 0;
248}
249
250bool IpmbCommandFilter::isBlocked(const uint8_t reqNetFn, const uint8_t cmd)
251{
252 auto blockedCmd = unhandledCommands.find({reqNetFn, cmd});
253
254 if (blockedCmd != unhandledCommands.end())
255 {
256 return true;
257 }
258
259 return false;
260}
261
262void IpmbCommandFilter::addFilter(const uint8_t reqNetFn, const uint8_t cmd)
263{
264 if (unhandledCommands.insert({reqNetFn, cmd}).second)
265 {
266 phosphor::logging::log<phosphor::logging::level::INFO>(
267 "addFilter: added command to filter",
268 phosphor::logging::entry("netFn = %d", reqNetFn),
269 phosphor::logging::entry("cmd = %d", cmd));
270 }
271}
272
273/**
274 * @brief Ipmb channel
275 */
276void IpmbChannel::ipmbResponseSend(std::shared_ptr<std::vector<uint8_t>> buffer,
277 size_t retriesAttempted = 0)
278{
279 boost::asio::async_write(
280 i2cMasterSocket,
281 boost::asio::buffer(buffer->data() + ipmbAddressSize,
282 buffer->size() - ipmbAddressSize),
283 [this, buffer, retriesAttempted](const boost::system::error_code ec,
284 size_t bytesSent) {
285 if (ec)
286 {
287 size_t currentRetryCnt = retriesAttempted;
288
289 if (currentRetryCnt > ipmbI2cNumberOfRetries)
290 {
291 phosphor::logging::log<phosphor::logging::level::ERR>(
292 "ipmbResponseSend: sent to I2C failed after retries");
293 return;
294 }
295 currentRetryCnt++;
296 ipmbResponseSend(buffer, currentRetryCnt);
297 }
298 });
299}
300
301/**
302 * @brief Ipmb Outstanding Requests
303 */
304void IpmbChannel::makeRequestInvalid(IpmbRequest &request)
305{
306 // change request state to invalid and remove it from outstanding requests
307 // list
308 request.state = ipmbRequestState::invalid;
309 outstandingRequests[request.seq] = nullptr;
310}
311
312void IpmbChannel::makeRequestValid(std::shared_ptr<IpmbRequest> request)
313{
314 // change request state to valid and add it to outstanding requests list
315 request->state = ipmbRequestState::valid;
316 outstandingRequests[request->seq] = request;
317}
318
319bool IpmbChannel::seqNumGet(uint8_t &seq)
320{
321 static uint8_t seqNum = 0;
322
323 for (int i = 0; i < ipmbMaxOutstandingRequestsCount; i++)
324 {
325 seqNum = ++seqNum & ipmbSeqMask;
326 if (seqNum == ipmbMaxOutstandingRequestsCount)
327 {
328 seqNum = 0;
329 }
330
331 if (outstandingRequests[seqNum] == nullptr)
332 {
333 seq = seqNum;
334 return true;
335 }
336 }
337
338 return false;
339}
340
341void IpmbChannel::responseMatch(std::unique_ptr<IpmbResponse> &response)
342{
343 std::shared_ptr<IpmbRequest> request = outstandingRequests[response->seq];
344
345 if (request != nullptr)
346 {
347 if (((ipmbRespNetFn(request->netFn)) == (response->netFn)) &&
348 ((request->rqLun) == (response->rqLun)) &&
349 ((request->rsLun) == (response->rsLun)) &&
350 ((request->cmd) == (response->cmd)))
351 {
352 // match, response is corresponding to previously sent request
353 request->state = ipmbRequestState::matched;
354 request->timer->cancel();
355 request->matchedResponse = std::move(response);
356 }
357 }
358}
359
360void IpmbChannel::processI2cEvent()
361{
362 std::array<uint8_t, ipmbMaxFrameLength> buffer{};
363 auto ipmbFrame = reinterpret_cast<IPMB_HEADER *>(buffer.data());
364
365 lseek(ipmbi2cSlaveFd, 0, SEEK_SET);
366 int r = read(ipmbi2cSlaveFd, buffer.data(), ipmbMaxFrameLength);
367 if ((r < ipmbMinFrameLength) || (r > ipmbMaxFrameLength))
368 {
369 goto end;
370 }
371
372 // valiate the frame
373 if (!isFrameValid(ipmbFrame, r))
374 {
375 goto end;
376 }
377
378 // copy frame to ipmib message buffer
379 if (ipmbIsResponse(ipmbFrame))
380 {
381 std::unique_ptr<IpmbResponse> ipmbMessageReceived =
382 std::make_unique<IpmbResponse>();
383
384 ipmbMessageReceived->i2cToIpmbConstruct(ipmbFrame, r);
385
386 // try to match response with outstanding request
387 responseMatch(ipmbMessageReceived);
388 }
389 else
390 {
391 // if command is blocked - respond with 'invalid command'
392 // completion code
393 if (commandFilter)
394 {
395 uint8_t netFn = ipmbNetFnGet(ipmbFrame->Header.Req.rsNetFnLUN);
396 uint8_t cmd = ipmbFrame->Header.Req.cmd;
397
398 if (commandFilter->isBlocked(netFn, cmd))
399 {
400 uint8_t seq = ipmbSeqGet(ipmbFrame->Header.Req.rqSeqLUN);
401 uint8_t lun =
402 ipmbLunFromSeqLunGet(ipmbFrame->Header.Req.rqSeqLUN);
403 std::vector<uint8_t> data;
404
405 // prepare generic response
406 auto ipmbResponse =
407 IpmbResponse(ipmbRqSlaveAddress, ipmbRespNetFn(netFn), lun,
408 ipmbBmcSlaveAddress, seq, ipmbRsLun, cmd,
409 ipmbIpmiInvalidCommand, data);
410
411 std::shared_ptr<std::vector<uint8_t>> buffer =
412 std::make_shared<std::vector<uint8_t>>();
413
414 if (ipmbResponse.ipmbToi2cConstruct(*buffer) == 0)
415 {
416 ipmbResponseSend(buffer);
417 }
418
419 goto end;
420 }
421 }
422
423 auto ipmbMessageReceived = IpmbRequest();
424
425 ipmbMessageReceived.i2cToIpmbConstruct(ipmbFrame, r);
426
427 // TODO w/a to differentiate channel origin of incoming IPMI
428 // response: extracting channel number from seq
429 ipmbMessageReceived.addChannelToSeq(getChannelType());
430
431 // send request to the client
432 ipmbMessageReceived.incomingMessageHandler();
433 }
434
435end:
436 i2cSlaveSocket.async_wait(
437 boost::asio::ip::tcp::socket::wait_error,
438 [this](const boost::system::error_code &ec) {
439 if (ec)
440 {
441 phosphor::logging::log<phosphor::logging::level::ERR>(
442 "Error: processI2cEvent()");
443 return;
444 }
445
446 processI2cEvent();
447 });
448}
449
450IpmbChannel::IpmbChannel(boost::asio::io_service &io,
451 uint8_t ipmbBmcSlaveAddress,
452 uint8_t ipmbRqSlaveAddress, ipmbChannelType type,
453 std::shared_ptr<IpmbCommandFilter> commandFilter) :
454 i2cSlaveSocket(io),
455 i2cMasterSocket(io), ipmbBmcSlaveAddress(ipmbBmcSlaveAddress),
456 ipmbRqSlaveAddress(ipmbRqSlaveAddress), type(type),
457 commandFilter(commandFilter)
458{
459}
460
461int IpmbChannel::ipmbChannelInit(const char *ipmbI2cSlave,
462 const char *ipmbI2cMaster)
463{
464 // open fd to i2c slave device
465 ipmbi2cSlaveFd = open(ipmbI2cSlave, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
466 if (ipmbi2cSlaveFd < 0)
467 {
468 phosphor::logging::log<phosphor::logging::level::ERR>(
469 "ipmbChannelInit: error opening ipmbI2cSlave");
470 return -1;
471 }
472
473 // open fd to i2c master device
474 ipmbi2cMasterFd = open(ipmbI2cMaster, O_RDWR | O_NONBLOCK);
475 if (ipmbi2cMasterFd < 0)
476 {
477 phosphor::logging::log<phosphor::logging::level::ERR>(
478 "ipmbChannelInit: error opening ipmbI2cMaster");
479 close(ipmbi2cSlaveFd);
480 return -1;
481 }
482
483 // set slave address of recipient
484 if (ioctl(ipmbi2cMasterFd, I2C_SLAVE,
485 ipmbAddressTo7BitSet(ipmbRqSlaveAddress)) < 0)
486 {
487 phosphor::logging::log<phosphor::logging::level::ERR>(
488 "ipmbChannelInit: error setting ipmbi2cMasterFd slave address");
489 close(ipmbi2cSlaveFd);
490 close(ipmbi2cMasterFd);
491 return -1;
492 }
493
494 i2cMasterSocket.assign(ipmbi2cMasterFd);
495 i2cSlaveSocket.assign(boost::asio::ip::tcp::v4(), ipmbi2cSlaveFd);
496 i2cSlaveSocket.async_wait(
497 boost::asio::ip::tcp::socket::wait_error,
498 [this](const boost::system::error_code &ec) {
499 if (ec)
500 {
501 phosphor::logging::log<phosphor::logging::level::ERR>(
502 "Error: processI2cEvent()");
503 return;
504 }
505
506 processI2cEvent();
507 });
508
509 return 0;
510}
511
512uint8_t IpmbChannel::getBmcSlaveAddress()
513{
514 return ipmbBmcSlaveAddress;
515}
516
517uint8_t IpmbChannel::getRqSlaveAddress()
518{
519 return ipmbRqSlaveAddress;
520}
521
522ipmbChannelType IpmbChannel::getChannelType()
523{
524 return type;
525}
526
527void IpmbChannel::addFilter(const uint8_t respNetFn, const uint8_t cmd)
528{
529 if (commandFilter)
530 {
531 commandFilter->addFilter(respNetFn, cmd);
532 }
533}
534
535std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
536 IpmbChannel::requestAdd(boost::asio::yield_context &yield,
537 std::shared_ptr<IpmbRequest> request)
538{
539 makeRequestValid(request);
540
541 std::vector<uint8_t> buffer(0);
542 if (request->ipmbToi2cConstruct(buffer) != 0)
543 {
544 return returnStatus(ipmbResponseStatus::error);
545 }
546
547 for (int i = 0; i < ipmbNumberOfTries; i++)
548 {
549 boost::system::error_code ec;
550
551 for (int j = 0; j < ipmbI2cNumberOfRetries; j++)
552 {
553 boost::asio::async_write(
554 i2cMasterSocket,
555 boost::asio::buffer(buffer.data() + ipmbAddressSize,
556 buffer.size() - ipmbAddressSize),
557 yield[ec]);
558
559 if (ec)
560 {
561 phosphor::logging::log<phosphor::logging::level::INFO>(
562 "requestAdd: Sent to I2C failed");
563 continue;
564 }
565 break;
566 }
567
568 request->timer->expires_after(
569 std::chrono::milliseconds(ipmbRequestRetryTimeout));
570 request->timer->async_wait(yield[ec]);
571
572 if (ec && ec != boost::asio::error::operation_aborted)
573 {
574 // unexpected error - invalidate request and return generic error
575 phosphor::logging::log<phosphor::logging::level::ERR>(
576 "requestAdd: async_wait error");
577 makeRequestInvalid(*request);
578 return returnStatus(ipmbResponseStatus::error);
579 }
580
581 if (request->state == ipmbRequestState::matched)
582 {
583 // matched response, send it to client application
584 makeRequestInvalid(*request);
585 return request->returnMatchedResponse();
586 }
587 }
588
589 makeRequestInvalid(*request);
590 return returnStatus(ipmbResponseStatus::timeout);
591}
592
593static IpmbChannel *getChannel(ipmbChannelType channelType)
594{
595 auto channel =
596 std::find_if(ipmbChannels.begin(), ipmbChannels.end(),
597 [channelType](IpmbChannel &channel) {
598 return channel.getChannelType() == channelType;
599 });
600 if (channel != ipmbChannels.end())
601 {
602 return &(*channel);
603 }
604
605 return nullptr;
606}
607
608static int initializeChannels()
609{
610 std::shared_ptr<IpmbCommandFilter> commandFilter =
611 std::make_shared<IpmbCommandFilter>();
612
613 for (const auto &channelConfig : ipmbChannelsConfig)
614 {
615 auto channel = ipmbChannels.emplace(ipmbChannels.end(), io,
616 channelConfig.ipmbBmcSlaveAddress,
617 channelConfig.ipmbRqSlaveAddress,
618 channelConfig.type, commandFilter);
619
620 if (channel->ipmbChannelInit(channelConfig.ipmbI2cSlave,
621 channelConfig.ipmbI2cMaster) < 0)
622 {
623 phosphor::logging::log<phosphor::logging::level::ERR>(
624 "initializeChannels: channel initialization failed");
625 return -1;
626 }
627 }
628
629 return 0;
630}
631
632/**
633 * @brief Dbus callbacks
634 */
635auto ipmbSendMessage = [](uint8_t seq, uint8_t netfn, uint8_t lun, uint8_t cmd,
636 uint8_t cc, std::vector<uint8_t> &dataReceived) {
637 int64_t status = -1;
638 std::shared_ptr<std::vector<uint8_t>> buffer =
639 std::make_shared<std::vector<uint8_t>>();
640
641 if (dataReceived.size() > ipmbMaxDataSize)
642 {
643 return status;
644 }
645
646 if (netfn & ipmbNetFnResponseMask)
647 {
648 IpmbChannel *channel = getChannel(getChannelFromSeq(seq));
649 if (channel == nullptr)
650 {
651 phosphor::logging::log<phosphor::logging::level::ERR>(
652 "ipmbSendMessage: channel does not exist");
653 return status;
654 }
655
656 // if command is not supported, add it to filter
657 if (cc == ipmbIpmiInvalidCommand)
658 {
659 channel->addFilter(ipmbReqNetFnFromRespNetFn(netfn), cmd);
660 }
661
662 uint8_t rqSlaveAddress = channel->getRqSlaveAddress();
663 uint8_t bmcSlaveAddress = channel->getBmcSlaveAddress();
664
665 // response received
666 // dataReceived is empty after constructor invocation
667 std::unique_ptr<IpmbResponse> ipmbMessageReceived =
668 std::make_unique<IpmbResponse>(rqSlaveAddress, netfn, lun,
669 bmcSlaveAddress, seq, lun, cmd, cc,
670 dataReceived);
671
672 status = ipmbMessageReceived->ipmbToi2cConstruct(*buffer);
673 if (status != 0)
674 {
675 return status;
676 }
677
678 channel->ipmbResponseSend(buffer);
679 return status;
680 }
681
682 // we are not expecting request here
683 phosphor::logging::log<phosphor::logging::level::ERR>(
684 "ipmbSendMessage: got a request");
685 return status;
686};
687
688auto ipmbHandleRequest = [](boost::asio::yield_context yield,
689 uint8_t reqChannel, uint8_t netfn, uint8_t lun,
690 uint8_t cmd, std::vector<uint8_t> dataReceived) {
691 IpmbChannel *channel = getChannel(static_cast<ipmbChannelType>(reqChannel));
692 if (channel == nullptr)
693 {
694 phosphor::logging::log<phosphor::logging::level::ERR>(
695 "ipmbHandleRequest: requested channel does not exist");
696 return returnStatus(ipmbResponseStatus::invalid_param);
697 }
698
699 // check outstanding request list for valid sequence number
700 uint8_t seqNum = 0;
701 bool seqValid = channel->seqNumGet(seqNum);
702 if (!seqValid)
703 {
704 phosphor::logging::log<phosphor::logging::level::WARNING>(
705 "ipmbHandleRequest: cannot add more requests to the list");
706 return returnStatus(ipmbResponseStatus::busy);
707 }
708
709 uint8_t bmcSlaveAddress = channel->getBmcSlaveAddress();
710 uint8_t rqSlaveAddress = channel->getRqSlaveAddress();
711
712 // construct the request to add it to outstanding request list
713 std::shared_ptr<IpmbRequest> request = std::make_shared<IpmbRequest>(
714 rqSlaveAddress, netfn, ipmbRsLun, bmcSlaveAddress, seqNum, lun, cmd,
715 dataReceived);
716
717 if (!request->timer)
718 {
719 phosphor::logging::log<phosphor::logging::level::ERR>(
720 "ipmbHandleRequest: timer object does not exist");
721 return returnStatus(ipmbResponseStatus::error);
722 }
723
724 return channel->requestAdd(yield, request);
725};
726
727/**
728 * @brief Main
729 */
730int main(int argc, char *argv[])
731{
732 conn->request_name(ipmbBus);
733
734 auto server = sdbusplus::asio::object_server(conn);
735
736 std::shared_ptr<sdbusplus::asio::dbus_interface> ipmiIface =
737 server.add_interface(ipmbObj, hostIpmiIntf);
738 std::shared_ptr<sdbusplus::asio::dbus_interface> ipmbIface =
739 server.add_interface(ipmbObj, ipmbDbusIntf);
740
741 ipmiIface->register_method("sendMessage", std::move(ipmbSendMessage));
742 ipmbIface->register_method("sendRequest", std::move(ipmbHandleRequest));
743 ipmiIface->initialize();
744 ipmbIface->initialize();
745
746 if (initializeChannels() < 0)
747 {
748 phosphor::logging::log<phosphor::logging::level::ERR>(
749 "Error initializeChannels");
750 return -1;
751 }
752
753 io.run();
754 return 0;
755}