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