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