blob: 810404337ceee05be90d6c48cd55dd54c82f0768 [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
Qiang XU8edcf1a2019-06-14 22:18:15 +080021#include <boost/algorithm/string/replace.hpp>
22#include <filesystem>
Amithash Prasad314862d2019-03-26 11:14:03 -070023#include <fstream>
24#include <nlohmann/json.hpp>
Dawid Fryckia642a942018-06-12 10:44:23 -070025#include <phosphor-logging/log.hpp>
26#include <tuple>
Amithash Prasad314862d2019-03-26 11:14:03 -070027#include <unordered_map>
Dawid Fryckia642a942018-06-12 10:44:23 -070028
James Feista8c77dc2019-01-30 16:55:46 -080029extern "C" {
30#include <i2c/smbus.h>
31#include <linux/i2c-dev.h>
32}
33
Dawid Fryckia642a942018-06-12 10:44:23 -070034/**
35 * @brief Dbus
36 */
37static constexpr const char *ipmbBus = "xyz.openbmc_project.Ipmi.Channel.Ipmb";
38static constexpr const char *ipmbObj = "/xyz/openbmc_project/Ipmi/Channel/Ipmb";
Dawid Fryckia642a942018-06-12 10:44:23 -070039static constexpr const char *ipmbDbusIntf = "org.openbmc.Ipmb";
40
41boost::asio::io_service io;
42auto conn = std::make_shared<sdbusplus::asio::connection>(io);
43
Dawid Fryckia642a942018-06-12 10:44:23 -070044static std::list<IpmbChannel> ipmbChannels;
Amithash Prasad314862d2019-03-26 11:14:03 -070045static const std::unordered_map<std::string, ipmbChannelType>
46 ipmbChannelTypeMap = {{"me", ipmbChannelType::me},
47 {"ipmb", ipmbChannelType::ipmb}};
Dawid Fryckia642a942018-06-12 10:44:23 -070048
49/**
50 * @brief Ipmb request class methods
51 */
52IpmbRequest::IpmbRequest()
53{
54 data.reserve(ipmbMaxDataSize);
55}
56
57IpmbRequest::IpmbRequest(uint8_t address, uint8_t netFn, uint8_t rsLun,
58 uint8_t rqSA, uint8_t seq, uint8_t rqLun, uint8_t cmd,
Dawid Frycki8188d762019-04-01 18:03:48 -070059 const std::vector<uint8_t> &inputData) :
Dawid Fryckia642a942018-06-12 10:44:23 -070060 address(address),
61 netFn(netFn), rsLun(rsLun), rqSA(rqSA), seq(seq), rqLun(rqLun), cmd(cmd),
62 timer(io)
63{
64 data.reserve(ipmbMaxDataSize);
65 state = ipmbRequestState::invalid;
66
67 if (inputData.size() > 0)
68 {
69 data = std::move(inputData);
70 }
71}
72
Dawid Fryckia642a942018-06-12 10:44:23 -070073void IpmbRequest::i2cToIpmbConstruct(IPMB_HEADER *ipmbBuffer,
74 size_t bufferLength)
75{
76 // constructing ipmb request from i2c buffer
77 netFn = ipmbNetFnGet(ipmbBuffer->Header.Req.rsNetFnLUN);
78 rsLun = ipmbLunFromNetFnLunGet(ipmbBuffer->Header.Req.rsNetFnLUN);
79 rqSA = ipmbBuffer->Header.Req.rqSA;
80 seq = ipmbSeqGet(ipmbBuffer->Header.Req.rqSeqLUN);
81 rqLun = ipmbLunFromSeqLunGet(ipmbBuffer->Header.Req.rqSeqLUN);
82 cmd = ipmbBuffer->Header.Req.cmd;
83
84 size_t dataLength =
85 bufferLength - (ipmbConnectionHeaderLength +
86 ipmbRequestDataHeaderLength + ipmbChecksumSize);
87
88 if (dataLength > 0)
89 {
90 data.insert(data.end(), ipmbBuffer->Header.Req.data,
91 &ipmbBuffer->Header.Req.data[dataLength]);
92 }
93}
94
95int IpmbRequest::ipmbToi2cConstruct(std::vector<uint8_t> &buffer)
96{
97 size_t bufferLength = data.size() + ipmbRequestDataHeaderLength +
98 ipmbConnectionHeaderLength + ipmbChecksumSize;
99
100 if (bufferLength > ipmbMaxFrameLength)
101 {
102 return -1;
103 }
104
105 buffer.resize(bufferLength);
106 static_assert(ipmbMaxFrameLength >= sizeof(IPMB_HEADER));
107 auto ipmbBuffer = reinterpret_cast<IPMB_HEADER *>(buffer.data());
108
109 // constructing buffer from ipmb request
110 ipmbBuffer->Header.Req.address = address;
111 ipmbBuffer->Header.Req.rsNetFnLUN = ipmbNetFnLunSet(netFn, rsLun);
112 ipmbBuffer->Header.Req.rqSA = rqSA;
113 ipmbBuffer->Header.Req.rqSeqLUN = ipmbSeqLunSet(seq, rqLun);
114 ipmbBuffer->Header.Req.cmd = cmd;
115
116 ipmbBuffer->Header.Resp.checksum1 = ipmbChecksumCompute(
117 buffer.data(), ipmbConnectionHeaderLength - ipmbChecksumSize);
118
119 if (data.size() > 0)
120 {
121 std::copy(data.begin(), data.end(), ipmbBuffer->Header.Req.data);
122 }
123
124 buffer[bufferLength - ipmbChecksumSize] =
125 ipmbChecksumCompute(buffer.data() + ipmbChecksum2StartOffset,
126 (ipmbRequestDataHeaderLength + data.size()));
127
128 return 0;
129}
130
131std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
132 IpmbRequest::returnMatchedResponse()
133{
134 return std::make_tuple(
135 static_cast<int>(ipmbResponseStatus::success), matchedResponse->netFn,
136 matchedResponse->rsLun, matchedResponse->cmd,
137 matchedResponse->completionCode, matchedResponse->data);
138}
139
140static std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
141 returnStatus(ipmbResponseStatus status)
142{
143 // we only want to send status here, other fields are not relevant
144 return std::make_tuple(static_cast<int>(status), 0, 0, 0, 0,
145 std::vector<uint8_t>(0));
146}
147
Dawid Fryckia642a942018-06-12 10:44:23 -0700148/**
149 * @brief Ipmb response class methods
150 */
151IpmbResponse::IpmbResponse()
152{
153 data.reserve(ipmbMaxDataSize);
154}
155
156IpmbResponse::IpmbResponse(uint8_t address, uint8_t netFn, uint8_t rqLun,
157 uint8_t rsSA, uint8_t seq, uint8_t rsLun,
158 uint8_t cmd, uint8_t completionCode,
Dawid Frycki8188d762019-04-01 18:03:48 -0700159 const std::vector<uint8_t> &inputData) :
Dawid Fryckia642a942018-06-12 10:44:23 -0700160 address(address),
161 netFn(netFn), rqLun(rqLun), rsSA(rsSA), seq(seq), rsLun(rsLun), cmd(cmd),
162 completionCode(completionCode)
163{
164 data.reserve(ipmbMaxDataSize);
165
166 if (inputData.size() > 0)
167 {
168 data = std::move(inputData);
169 }
170}
171
172void IpmbResponse::i2cToIpmbConstruct(IPMB_HEADER *ipmbBuffer,
173 size_t bufferLength)
174{
175 netFn = ipmbNetFnGet(ipmbBuffer->Header.Resp.rqNetFnLUN);
176 rqLun = ipmbLunFromNetFnLunGet(ipmbBuffer->Header.Resp.rqNetFnLUN);
177 rsSA = ipmbBuffer->Header.Resp.rsSA;
178 seq = ipmbSeqGet(ipmbBuffer->Header.Resp.rsSeqLUN);
179 rsLun = ipmbLunFromSeqLunGet(ipmbBuffer->Header.Resp.rsSeqLUN);
180 cmd = ipmbBuffer->Header.Resp.cmd;
181 completionCode = ipmbBuffer->Header.Resp.completionCode;
182
183 size_t dataLength =
184 bufferLength - (ipmbConnectionHeaderLength +
185 ipmbResponseDataHeaderLength + ipmbChecksumSize);
186
187 if (dataLength > 0)
188 {
189 data.insert(data.end(), ipmbBuffer->Header.Resp.data,
190 &ipmbBuffer->Header.Resp.data[dataLength]);
191 }
192}
193
Dawid Frycki8188d762019-04-01 18:03:48 -0700194std::shared_ptr<std::vector<uint8_t>> IpmbResponse::ipmbToi2cConstruct()
Dawid Fryckia642a942018-06-12 10:44:23 -0700195{
196 size_t bufferLength = data.size() + ipmbResponseDataHeaderLength +
197 ipmbConnectionHeaderLength + ipmbChecksumSize;
198
199 if (bufferLength > ipmbMaxFrameLength)
200 {
Dawid Frycki8188d762019-04-01 18:03:48 -0700201 return nullptr;
Dawid Fryckia642a942018-06-12 10:44:23 -0700202 }
203
Dawid Frycki8188d762019-04-01 18:03:48 -0700204 std::shared_ptr<std::vector<uint8_t>> buffer =
205 std::make_shared<std::vector<uint8_t>>(bufferLength);
206
207 auto ipmbBuffer = reinterpret_cast<IPMB_HEADER *>(buffer->data());
Dawid Fryckia642a942018-06-12 10:44:23 -0700208
209 ipmbBuffer->Header.Resp.address = address;
210 ipmbBuffer->Header.Resp.rqNetFnLUN = ipmbNetFnLunSet(netFn, rqLun);
211 ipmbBuffer->Header.Resp.rsSA = rsSA;
212 ipmbBuffer->Header.Resp.rsSeqLUN = ipmbSeqLunSet(seq, rsLun);
213 ipmbBuffer->Header.Resp.cmd = cmd;
214 ipmbBuffer->Header.Resp.completionCode = completionCode;
215
216 ipmbBuffer->Header.Resp.checksum1 = ipmbChecksumCompute(
Dawid Frycki8188d762019-04-01 18:03:48 -0700217 buffer->data(), ipmbConnectionHeaderLength - ipmbChecksumSize);
Dawid Fryckia642a942018-06-12 10:44:23 -0700218
219 if (data.size() > 0)
220 {
221 std::copy(data.begin(), data.end(), ipmbBuffer->Header.Resp.data);
222 }
223
Dawid Frycki8188d762019-04-01 18:03:48 -0700224 (*buffer)[bufferLength - ipmbChecksumSize] =
225 ipmbChecksumCompute(buffer->data() + ipmbChecksum2StartOffset,
Dawid Fryckia642a942018-06-12 10:44:23 -0700226 (ipmbResponseDataHeaderLength + data.size()));
227
Dawid Frycki8188d762019-04-01 18:03:48 -0700228 return buffer;
Dawid Fryckia642a942018-06-12 10:44:23 -0700229}
230
231bool IpmbCommandFilter::isBlocked(const uint8_t reqNetFn, const uint8_t cmd)
232{
233 auto blockedCmd = unhandledCommands.find({reqNetFn, cmd});
234
235 if (blockedCmd != unhandledCommands.end())
236 {
237 return true;
238 }
239
240 return false;
241}
242
243void IpmbCommandFilter::addFilter(const uint8_t reqNetFn, const uint8_t cmd)
244{
245 if (unhandledCommands.insert({reqNetFn, cmd}).second)
246 {
247 phosphor::logging::log<phosphor::logging::level::INFO>(
248 "addFilter: added command to filter",
249 phosphor::logging::entry("netFn = %d", reqNetFn),
250 phosphor::logging::entry("cmd = %d", cmd));
251 }
252}
253
254/**
255 * @brief Ipmb channel
256 */
257void IpmbChannel::ipmbResponseSend(std::shared_ptr<std::vector<uint8_t>> buffer,
258 size_t retriesAttempted = 0)
259{
260 boost::asio::async_write(
261 i2cMasterSocket,
262 boost::asio::buffer(buffer->data() + ipmbAddressSize,
263 buffer->size() - ipmbAddressSize),
Dawid Frycki8188d762019-04-01 18:03:48 -0700264 [this, buffer, retriesAttempted](const boost::system::error_code &ec,
Dawid Fryckia642a942018-06-12 10:44:23 -0700265 size_t bytesSent) {
266 if (ec)
267 {
268 size_t currentRetryCnt = retriesAttempted;
269
270 if (currentRetryCnt > ipmbI2cNumberOfRetries)
271 {
272 phosphor::logging::log<phosphor::logging::level::ERR>(
273 "ipmbResponseSend: sent to I2C failed after retries");
274 return;
275 }
276 currentRetryCnt++;
277 ipmbResponseSend(buffer, currentRetryCnt);
278 }
279 });
280}
281
282/**
283 * @brief Ipmb Outstanding Requests
284 */
285void IpmbChannel::makeRequestInvalid(IpmbRequest &request)
286{
287 // change request state to invalid and remove it from outstanding requests
288 // list
289 request.state = ipmbRequestState::invalid;
290 outstandingRequests[request.seq] = nullptr;
291}
292
293void IpmbChannel::makeRequestValid(std::shared_ptr<IpmbRequest> request)
294{
295 // change request state to valid and add it to outstanding requests list
296 request->state = ipmbRequestState::valid;
297 outstandingRequests[request->seq] = request;
298}
299
300bool IpmbChannel::seqNumGet(uint8_t &seq)
301{
302 static uint8_t seqNum = 0;
303
304 for (int i = 0; i < ipmbMaxOutstandingRequestsCount; i++)
305 {
306 seqNum = ++seqNum & ipmbSeqMask;
307 if (seqNum == ipmbMaxOutstandingRequestsCount)
308 {
309 seqNum = 0;
310 }
311
312 if (outstandingRequests[seqNum] == nullptr)
313 {
314 seq = seqNum;
315 return true;
316 }
317 }
318
319 return false;
320}
321
322void IpmbChannel::responseMatch(std::unique_ptr<IpmbResponse> &response)
323{
324 std::shared_ptr<IpmbRequest> request = outstandingRequests[response->seq];
325
326 if (request != nullptr)
327 {
328 if (((ipmbRespNetFn(request->netFn)) == (response->netFn)) &&
329 ((request->rqLun) == (response->rqLun)) &&
330 ((request->rsLun) == (response->rsLun)) &&
331 ((request->cmd) == (response->cmd)))
332 {
333 // match, response is corresponding to previously sent request
334 request->state = ipmbRequestState::matched;
335 request->timer->cancel();
336 request->matchedResponse = std::move(response);
337 }
338 }
339}
340
341void IpmbChannel::processI2cEvent()
342{
343 std::array<uint8_t, ipmbMaxFrameLength> buffer{};
344 auto ipmbFrame = reinterpret_cast<IPMB_HEADER *>(buffer.data());
345
346 lseek(ipmbi2cSlaveFd, 0, SEEK_SET);
347 int r = read(ipmbi2cSlaveFd, buffer.data(), ipmbMaxFrameLength);
348 if ((r < ipmbMinFrameLength) || (r > ipmbMaxFrameLength))
349 {
350 goto end;
351 }
352
353 // valiate the frame
354 if (!isFrameValid(ipmbFrame, r))
355 {
356 goto end;
357 }
358
359 // copy frame to ipmib message buffer
360 if (ipmbIsResponse(ipmbFrame))
361 {
362 std::unique_ptr<IpmbResponse> ipmbMessageReceived =
363 std::make_unique<IpmbResponse>();
364
365 ipmbMessageReceived->i2cToIpmbConstruct(ipmbFrame, r);
366
367 // try to match response with outstanding request
368 responseMatch(ipmbMessageReceived);
369 }
370 else
371 {
372 // if command is blocked - respond with 'invalid command'
373 // completion code
374 if (commandFilter)
375 {
376 uint8_t netFn = ipmbNetFnGet(ipmbFrame->Header.Req.rsNetFnLUN);
377 uint8_t cmd = ipmbFrame->Header.Req.cmd;
378
379 if (commandFilter->isBlocked(netFn, cmd))
380 {
381 uint8_t seq = ipmbSeqGet(ipmbFrame->Header.Req.rqSeqLUN);
382 uint8_t lun =
383 ipmbLunFromSeqLunGet(ipmbFrame->Header.Req.rqSeqLUN);
Dawid Fryckia642a942018-06-12 10:44:23 -0700384
385 // prepare generic response
386 auto ipmbResponse =
387 IpmbResponse(ipmbRqSlaveAddress, ipmbRespNetFn(netFn), lun,
388 ipmbBmcSlaveAddress, seq, ipmbRsLun, cmd,
Dawid Frycki8188d762019-04-01 18:03:48 -0700389 ipmbIpmiInvalidCmd, {});
Dawid Fryckia642a942018-06-12 10:44:23 -0700390
Dawid Frycki8188d762019-04-01 18:03:48 -0700391 auto buffer = ipmbResponse.ipmbToi2cConstruct();
392 if (buffer)
Dawid Fryckia642a942018-06-12 10:44:23 -0700393 {
394 ipmbResponseSend(buffer);
395 }
396
397 goto end;
398 }
399 }
400
401 auto ipmbMessageReceived = IpmbRequest();
Dawid Fryckia642a942018-06-12 10:44:23 -0700402 ipmbMessageReceived.i2cToIpmbConstruct(ipmbFrame, r);
403
Dawid Frycki8188d762019-04-01 18:03:48 -0700404 std::map<std::string, std::variant<int>> options{
405 {"rqSA", ipmbAddressTo7BitSet(ipmbRqSlaveAddress)}};
406 using IpmiDbusRspType = std::tuple<uint8_t, uint8_t, uint8_t, uint8_t,
407 std::vector<uint8_t>>;
408 conn->async_method_call(
409 [this, rqLun{ipmbMessageReceived.rqLun},
410 seq{ipmbMessageReceived.seq}](const boost::system::error_code &ec,
411 const IpmiDbusRspType &response) {
412 const auto &[netfn, lun, cmd, cc, payload] = response;
413 if (ec)
414 {
415 phosphor::logging::log<phosphor::logging::level::ERR>(
416 "processI2cEvent: error getting response from IPMI");
417 return;
418 }
Dawid Fryckia642a942018-06-12 10:44:23 -0700419
Dawid Frycki8188d762019-04-01 18:03:48 -0700420 uint8_t rqSlaveAddress = getRqSlaveAddress();
421 uint8_t bmcSlaveAddress = getBmcSlaveAddress();
422
423 if (payload.size() > ipmbMaxDataSize)
424 {
425 phosphor::logging::log<phosphor::logging::level::ERR>(
426 "processI2cEvent: response exceeding maximum size");
427
428 // prepare generic response
429 auto ipmbResponse = IpmbResponse(
430 rqSlaveAddress, netfn, rqLun, bmcSlaveAddress, seq,
431 ipmbRsLun, cmd, ipmbIpmiCmdRespNotProvided, {});
432
433 auto buffer = ipmbResponse.ipmbToi2cConstruct();
434 if (buffer)
435 {
436 ipmbResponseSend(buffer);
437 }
438
439 return;
440 }
441
442 if (!(netfn & ipmbNetFnResponseMask))
443 {
444 // we are not expecting request here
445 phosphor::logging::log<phosphor::logging::level::ERR>(
446 "processI2cEvent: got a request instead of response");
447 return;
448 }
449
450 // if command is not supported, add it to filter
451 if (cc == ipmbIpmiInvalidCmd)
452 {
453 addFilter(ipmbReqNetFnFromRespNetFn(netfn), cmd);
454 }
455
456 // payload is empty after constructor invocation
457 auto ipmbResponse =
458 IpmbResponse(rqSlaveAddress, netfn, rqLun, bmcSlaveAddress,
459 seq, lun, cmd, cc, payload);
460
461 auto buffer = ipmbResponse.ipmbToi2cConstruct();
462 if (!buffer)
463 {
464 phosphor::logging::log<phosphor::logging::level::ERR>(
465 "processI2cEvent: error constructing a request");
466 return;
467 }
468
469 ipmbResponseSend(buffer);
470 },
471 "xyz.openbmc_project.Ipmi.Host", "/xyz/openbmc_project/Ipmi",
472 "xyz.openbmc_project.Ipmi.Server", "execute",
473 ipmbMessageReceived.netFn, ipmbMessageReceived.rsLun,
474 ipmbMessageReceived.cmd, ipmbMessageReceived.data, options);
Dawid Fryckia642a942018-06-12 10:44:23 -0700475 }
476
477end:
478 i2cSlaveSocket.async_wait(
479 boost::asio::ip::tcp::socket::wait_error,
480 [this](const boost::system::error_code &ec) {
481 if (ec)
482 {
483 phosphor::logging::log<phosphor::logging::level::ERR>(
484 "Error: processI2cEvent()");
485 return;
486 }
487
488 processI2cEvent();
489 });
490}
491
492IpmbChannel::IpmbChannel(boost::asio::io_service &io,
493 uint8_t ipmbBmcSlaveAddress,
494 uint8_t ipmbRqSlaveAddress, ipmbChannelType type,
495 std::shared_ptr<IpmbCommandFilter> commandFilter) :
496 i2cSlaveSocket(io),
497 i2cMasterSocket(io), ipmbBmcSlaveAddress(ipmbBmcSlaveAddress),
498 ipmbRqSlaveAddress(ipmbRqSlaveAddress), type(type),
499 commandFilter(commandFilter)
500{
501}
502
503int IpmbChannel::ipmbChannelInit(const char *ipmbI2cSlave,
504 const char *ipmbI2cMaster)
505{
Qiang XU8edcf1a2019-06-14 22:18:15 +0800506 // extract bus id from master path and save
507 std::string ipmbI2cMasterStr(ipmbI2cMaster);
508 auto findHyphen = ipmbI2cMasterStr.find("-");
509 std::string busStr = ipmbI2cMasterStr.substr(findHyphen + 1);
510 try
511 {
512 ipmbBusId = std::stoi(busStr);
513 }
514 catch (std::invalid_argument)
515 {
516 phosphor::logging::log<phosphor::logging::level::ERR>(
517 "ipmbChannelInit: invalid bus id in master-path config");
518 return -1;
519 }
520
521 // Check if sysfs has device. If not, enable I2C slave driver by command
522 // echo "slave-mqueue 0x1010" > /sys/bus/i2c/devices/i2c-0/new_device
523 bool hasSysfs = std::filesystem::exists(ipmbI2cSlave);
524 if (!hasSysfs)
525 {
526 std::string deviceFileName =
527 "/sys/bus/i2c/devices/i2c-" + busStr + "/new_device";
528 std::string para = "slave-mqueue 0x1010"; // init with BMC addr 0x20
529 std::fstream deviceFile;
530 deviceFile.open(deviceFileName, std::ios::out);
531 if (!deviceFile.good())
532 {
533 phosphor::logging::log<phosphor::logging::level::ERR>(
534 "ipmbChannelInit: error opening deviceFile");
535 return -1;
536 }
537 deviceFile << para;
538 deviceFile.close();
539 }
540
Dawid Fryckia642a942018-06-12 10:44:23 -0700541 // open fd to i2c slave device
542 ipmbi2cSlaveFd = open(ipmbI2cSlave, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
543 if (ipmbi2cSlaveFd < 0)
544 {
545 phosphor::logging::log<phosphor::logging::level::ERR>(
546 "ipmbChannelInit: error opening ipmbI2cSlave");
547 return -1;
548 }
549
550 // open fd to i2c master device
551 ipmbi2cMasterFd = open(ipmbI2cMaster, O_RDWR | O_NONBLOCK);
552 if (ipmbi2cMasterFd < 0)
553 {
554 phosphor::logging::log<phosphor::logging::level::ERR>(
555 "ipmbChannelInit: error opening ipmbI2cMaster");
556 close(ipmbi2cSlaveFd);
Qiang XU8edcf1a2019-06-14 22:18:15 +0800557 ipmbi2cSlaveFd = 0;
Dawid Fryckia642a942018-06-12 10:44:23 -0700558 return -1;
559 }
560
561 // set slave address of recipient
562 if (ioctl(ipmbi2cMasterFd, I2C_SLAVE,
563 ipmbAddressTo7BitSet(ipmbRqSlaveAddress)) < 0)
564 {
565 phosphor::logging::log<phosphor::logging::level::ERR>(
566 "ipmbChannelInit: error setting ipmbi2cMasterFd slave address");
567 close(ipmbi2cSlaveFd);
568 close(ipmbi2cMasterFd);
Qiang XU8edcf1a2019-06-14 22:18:15 +0800569 ipmbi2cSlaveFd = 0;
570 ipmbi2cMasterFd = 0;
Dawid Fryckia642a942018-06-12 10:44:23 -0700571 return -1;
572 }
573
574 i2cMasterSocket.assign(ipmbi2cMasterFd);
575 i2cSlaveSocket.assign(boost::asio::ip::tcp::v4(), ipmbi2cSlaveFd);
576 i2cSlaveSocket.async_wait(
577 boost::asio::ip::tcp::socket::wait_error,
578 [this](const boost::system::error_code &ec) {
579 if (ec)
580 {
581 phosphor::logging::log<phosphor::logging::level::ERR>(
582 "Error: processI2cEvent()");
583 return;
584 }
585
586 processI2cEvent();
587 });
588
589 return 0;
590}
591
Qiang XU8edcf1a2019-06-14 22:18:15 +0800592int IpmbChannel::ipmbChannelUpdateSlaveAddress(const uint8_t newBmcSlaveAddr)
593{
594 if (ipmbi2cSlaveFd > 0)
595 {
596 i2cSlaveSocket.close();
597 close(ipmbi2cSlaveFd);
598 ipmbi2cSlaveFd = 0;
599 }
600
601 // disable old I2C slave driver by command:
602 // echo "0x1010" > /sys/bus/i2c/devices/i2c-0/delete_device
603 std::string deviceFileName;
604 std::string para;
605 std::fstream deviceFile;
606 deviceFileName = "/sys/bus/i2c/devices/i2c-" + std::to_string(ipmbBusId) +
607 "/delete_device";
608 para = "0x1010"; // align with removed ipmb0 definition in dts file
609 deviceFile.open(deviceFileName, std::ios::out);
610 if (!deviceFile.good())
611 {
612 phosphor::logging::log<phosphor::logging::level::ERR>(
613 "ipmbChannelUpdateSlaveAddress: error opening deviceFile to delete "
614 "sysfs");
615 return -1;
616 }
617 deviceFile << para;
618 deviceFile.close();
619
620 // enable new I2C slave driver by command:
621 // echo "slave-mqueue 0x1012" > /sys/bus/i2c/devices/i2c-0/new_device
622 deviceFileName =
623 "/sys/bus/i2c/devices/i2c-" + std::to_string(ipmbBusId) + "/new_device";
624 std::ostringstream hex;
625 uint16_t addr = 0x1000 + (newBmcSlaveAddr >> 1);
626 hex << std::hex << static_cast<uint16_t>(addr);
627 const std::string &addressHexStr = hex.str();
628 para = "slave-mqueue 0x" + addressHexStr;
629 deviceFile.open(deviceFileName, std::ios::out);
630 if (!deviceFile.good())
631 {
632 phosphor::logging::log<phosphor::logging::level::ERR>(
633 "ipmbChannelUpdateSlaveAddress: error opening deviceFile to create "
634 "sysfs");
635 return -1;
636 }
637 deviceFile << para;
638 deviceFile.close();
639
640 // open fd to i2c slave device
641 std::string ipmbI2cSlaveStr = "/sys/bus/i2c/devices/" +
642 std::to_string(ipmbBusId) + "-" +
643 addressHexStr + "/slave-mqueue";
644 ipmbi2cSlaveFd =
645 open(ipmbI2cSlaveStr.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC);
646 if (ipmbi2cSlaveFd < 0)
647 {
648 phosphor::logging::log<phosphor::logging::level::ERR>(
649 "ipmbChannelInit: error opening ipmbI2cSlave");
650 return -1;
651 }
652
653 // start to receive i2c data as slave
654 i2cSlaveSocket.assign(boost::asio::ip::tcp::v4(), ipmbi2cSlaveFd);
655 i2cSlaveSocket.async_wait(
656 boost::asio::ip::tcp::socket::wait_error,
657 [this](const boost::system::error_code &ec) {
658 if (ec)
659 {
660 phosphor::logging::log<phosphor::logging::level::ERR>(
661 "Error: processI2cEvent()");
662 return;
663 }
664
665 processI2cEvent();
666 });
667
668 return 0;
669}
670
671uint8_t IpmbChannel::getBusId()
672{
673 return ipmbBusId;
674}
675
Dawid Fryckia642a942018-06-12 10:44:23 -0700676uint8_t IpmbChannel::getBmcSlaveAddress()
677{
678 return ipmbBmcSlaveAddress;
679}
680
681uint8_t IpmbChannel::getRqSlaveAddress()
682{
683 return ipmbRqSlaveAddress;
684}
685
686ipmbChannelType IpmbChannel::getChannelType()
687{
688 return type;
689}
690
691void IpmbChannel::addFilter(const uint8_t respNetFn, const uint8_t cmd)
692{
693 if (commandFilter)
694 {
695 commandFilter->addFilter(respNetFn, cmd);
696 }
697}
698
699std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
700 IpmbChannel::requestAdd(boost::asio::yield_context &yield,
701 std::shared_ptr<IpmbRequest> request)
702{
703 makeRequestValid(request);
704
705 std::vector<uint8_t> buffer(0);
706 if (request->ipmbToi2cConstruct(buffer) != 0)
707 {
708 return returnStatus(ipmbResponseStatus::error);
709 }
710
711 for (int i = 0; i < ipmbNumberOfTries; i++)
712 {
713 boost::system::error_code ec;
Jae Hyun Yoo25e85c72019-03-05 14:28:13 -0800714 int i2cRetryCnt = 0;
Dawid Fryckia642a942018-06-12 10:44:23 -0700715
Jae Hyun Yoo25e85c72019-03-05 14:28:13 -0800716 for (; i2cRetryCnt < ipmbI2cNumberOfRetries; i2cRetryCnt++)
Dawid Fryckia642a942018-06-12 10:44:23 -0700717 {
718 boost::asio::async_write(
719 i2cMasterSocket,
720 boost::asio::buffer(buffer.data() + ipmbAddressSize,
721 buffer.size() - ipmbAddressSize),
722 yield[ec]);
723
724 if (ec)
725 {
Jae Hyun Yoo25e85c72019-03-05 14:28:13 -0800726 continue; // retry
Dawid Fryckia642a942018-06-12 10:44:23 -0700727 }
728 break;
729 }
730
Jae Hyun Yoo25e85c72019-03-05 14:28:13 -0800731 if (i2cRetryCnt == ipmbI2cNumberOfRetries)
732 {
733 phosphor::logging::log<phosphor::logging::level::INFO>(
734 "requestAdd: Sent to I2C failed after retries");
735 }
736
Dawid Fryckia642a942018-06-12 10:44:23 -0700737 request->timer->expires_after(
738 std::chrono::milliseconds(ipmbRequestRetryTimeout));
739 request->timer->async_wait(yield[ec]);
740
741 if (ec && ec != boost::asio::error::operation_aborted)
742 {
743 // unexpected error - invalidate request and return generic error
744 phosphor::logging::log<phosphor::logging::level::ERR>(
745 "requestAdd: async_wait error");
746 makeRequestInvalid(*request);
747 return returnStatus(ipmbResponseStatus::error);
748 }
749
750 if (request->state == ipmbRequestState::matched)
751 {
752 // matched response, send it to client application
753 makeRequestInvalid(*request);
754 return request->returnMatchedResponse();
755 }
756 }
757
758 makeRequestInvalid(*request);
759 return returnStatus(ipmbResponseStatus::timeout);
760}
761
762static IpmbChannel *getChannel(ipmbChannelType channelType)
763{
764 auto channel =
765 std::find_if(ipmbChannels.begin(), ipmbChannels.end(),
766 [channelType](IpmbChannel &channel) {
767 return channel.getChannelType() == channelType;
768 });
769 if (channel != ipmbChannels.end())
770 {
771 return &(*channel);
772 }
773
774 return nullptr;
775}
776
777static int initializeChannels()
778{
779 std::shared_ptr<IpmbCommandFilter> commandFilter =
780 std::make_shared<IpmbCommandFilter>();
781
Amithash Prasad314862d2019-03-26 11:14:03 -0700782 constexpr const char *configFilePath =
783 "/usr/share/ipmbbridge/ipmb-channels.json";
784 std::ifstream configFile(configFilePath);
785 if (!configFile.is_open())
Dawid Fryckia642a942018-06-12 10:44:23 -0700786 {
Amithash Prasad314862d2019-03-26 11:14:03 -0700787 phosphor::logging::log<phosphor::logging::level::ERR>(
788 "initializeChannels: Cannot open config path");
789 return -1;
790 }
791 try
792 {
793 auto data = nlohmann::json::parse(configFile, nullptr);
794 for (const auto &channelConfig : data["channels"])
Dawid Fryckia642a942018-06-12 10:44:23 -0700795 {
Amithash Prasad314862d2019-03-26 11:14:03 -0700796 const std::string &typeConfig = channelConfig["type"];
797 const std::string &masterPath = channelConfig["master-path"];
798 const std::string &slavePath = channelConfig["slave-path"];
799 uint8_t bmcAddr = channelConfig["bmc-addr"];
800 uint8_t reqAddr = channelConfig["remote-addr"];
801 ipmbChannelType type = ipmbChannelTypeMap.at(typeConfig);
802
803 auto channel = ipmbChannels.emplace(ipmbChannels.end(), io, bmcAddr,
804 reqAddr, type, commandFilter);
805 if (channel->ipmbChannelInit(slavePath.c_str(),
806 masterPath.c_str()) < 0)
807 {
808 phosphor::logging::log<phosphor::logging::level::ERR>(
809 "initializeChannels: channel initialization failed");
810 return -1;
811 }
Dawid Fryckia642a942018-06-12 10:44:23 -0700812 }
813 }
Amithash Prasad314862d2019-03-26 11:14:03 -0700814 catch (nlohmann::json::exception &e)
815 {
816 phosphor::logging::log<phosphor::logging::level::ERR>(
817 "initializeChannels: Error parsing config file");
818 return -1;
819 }
820 catch (std::out_of_range &e)
821 {
822 phosphor::logging::log<phosphor::logging::level::ERR>(
823 "initializeChannels: Error invalid type");
824 return -1;
825 }
Dawid Fryckia642a942018-06-12 10:44:23 -0700826 return 0;
827}
828
Dawid Fryckia642a942018-06-12 10:44:23 -0700829auto ipmbHandleRequest = [](boost::asio::yield_context yield,
830 uint8_t reqChannel, uint8_t netfn, uint8_t lun,
831 uint8_t cmd, std::vector<uint8_t> dataReceived) {
832 IpmbChannel *channel = getChannel(static_cast<ipmbChannelType>(reqChannel));
833 if (channel == nullptr)
834 {
835 phosphor::logging::log<phosphor::logging::level::ERR>(
836 "ipmbHandleRequest: requested channel does not exist");
837 return returnStatus(ipmbResponseStatus::invalid_param);
838 }
839
840 // check outstanding request list for valid sequence number
841 uint8_t seqNum = 0;
842 bool seqValid = channel->seqNumGet(seqNum);
843 if (!seqValid)
844 {
845 phosphor::logging::log<phosphor::logging::level::WARNING>(
846 "ipmbHandleRequest: cannot add more requests to the list");
847 return returnStatus(ipmbResponseStatus::busy);
848 }
849
850 uint8_t bmcSlaveAddress = channel->getBmcSlaveAddress();
851 uint8_t rqSlaveAddress = channel->getRqSlaveAddress();
852
853 // construct the request to add it to outstanding request list
854 std::shared_ptr<IpmbRequest> request = std::make_shared<IpmbRequest>(
855 rqSlaveAddress, netfn, ipmbRsLun, bmcSlaveAddress, seqNum, lun, cmd,
856 dataReceived);
857
858 if (!request->timer)
859 {
860 phosphor::logging::log<phosphor::logging::level::ERR>(
861 "ipmbHandleRequest: timer object does not exist");
862 return returnStatus(ipmbResponseStatus::error);
863 }
864
865 return channel->requestAdd(yield, request);
866};
867
Qiang XU8edcf1a2019-06-14 22:18:15 +0800868void addUpdateSlaveAddrHandler()
869{
870 // callback to handle dbus signal of updating slave addr
871 std::function<void(sdbusplus::message::message &)> updateSlaveAddrHandler =
872 [](sdbusplus::message::message &message) {
873 uint8_t reqChannel, busId, slaveAddr;
874
875 // valid source of signal, check whether from multi-node manager
876 std::string pathName = message.get_path();
877 if (pathName != "/xyz/openbmc_project/MultiNode/Status")
878 {
879 phosphor::logging::log<phosphor::logging::level::ERR>(
880 "addUpdateSlaveAddrHandler: invalid obj path");
881 return;
882 }
883
884 message.read(reqChannel, busId, slaveAddr);
885
886 IpmbChannel *channel =
887 getChannel(static_cast<ipmbChannelType>(reqChannel));
888 if (channel == nullptr ||
889 reqChannel != static_cast<uint8_t>(ipmbChannelType::ipmb))
890 {
891 phosphor::logging::log<phosphor::logging::level::ERR>(
892 "addUpdateSlaveAddrHandler: invalid channel");
893 return;
894 }
895 if (busId != channel->getBusId())
896 {
897 phosphor::logging::log<phosphor::logging::level::ERR>(
898 "addUpdateSlaveAddrHandler: invalid busId");
899 return;
900 }
901 if (channel->getBmcSlaveAddress() == slaveAddr)
902 {
903 phosphor::logging::log<phosphor::logging::level::INFO>(
904 "addUpdateSlaveAddrHandler: channel bmc slave addr is "
905 "unchanged, do nothing");
906 return;
907 }
908
909 channel->ipmbChannelUpdateSlaveAddress(slaveAddr);
910 };
911
912 static auto match = std::make_unique<sdbusplus::bus::match::match>(
913 static_cast<sdbusplus::bus::bus &>(*conn),
914 "type='signal',member='updateBmcSlaveAddr',", updateSlaveAddrHandler);
915}
916
Dawid Fryckia642a942018-06-12 10:44:23 -0700917/**
918 * @brief Main
919 */
920int main(int argc, char *argv[])
921{
922 conn->request_name(ipmbBus);
923
924 auto server = sdbusplus::asio::object_server(conn);
925
Dawid Fryckia642a942018-06-12 10:44:23 -0700926 std::shared_ptr<sdbusplus::asio::dbus_interface> ipmbIface =
927 server.add_interface(ipmbObj, ipmbDbusIntf);
928
Dawid Fryckia642a942018-06-12 10:44:23 -0700929 ipmbIface->register_method("sendRequest", std::move(ipmbHandleRequest));
Dawid Fryckia642a942018-06-12 10:44:23 -0700930 ipmbIface->initialize();
931
932 if (initializeChannels() < 0)
933 {
934 phosphor::logging::log<phosphor::logging::level::ERR>(
935 "Error initializeChannels");
936 return -1;
937 }
938
Qiang XU8edcf1a2019-06-14 22:18:15 +0800939 addUpdateSlaveAddrHandler();
940
Dawid Fryckia642a942018-06-12 10:44:23 -0700941 io.run();
942 return 0;
943}