blob: 22bc9c69a21397d8a655070af9419ce6b62e4eac [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
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";
Dawid Fryckia642a942018-06-12 10:44:23 -070034static constexpr const char *ipmbDbusIntf = "org.openbmc.Ipmb";
35
36boost::asio::io_service io;
37auto conn = std::make_shared<sdbusplus::asio::connection>(io);
38
Dawid Fryckia642a942018-06-12 10:44:23 -070039static std::list<IpmbChannel> ipmbChannels;
Amithash Prasad314862d2019-03-26 11:14:03 -070040static const std::unordered_map<std::string, ipmbChannelType>
41 ipmbChannelTypeMap = {{"me", ipmbChannelType::me},
42 {"ipmb", ipmbChannelType::ipmb}};
Dawid Fryckia642a942018-06-12 10:44:23 -070043
44/**
45 * @brief Ipmb request class methods
46 */
47IpmbRequest::IpmbRequest()
48{
49 data.reserve(ipmbMaxDataSize);
50}
51
52IpmbRequest::IpmbRequest(uint8_t address, uint8_t netFn, uint8_t rsLun,
53 uint8_t rqSA, uint8_t seq, uint8_t rqLun, uint8_t cmd,
Dawid Frycki8188d762019-04-01 18:03:48 -070054 const std::vector<uint8_t> &inputData) :
Dawid Fryckia642a942018-06-12 10:44:23 -070055 address(address),
56 netFn(netFn), rsLun(rsLun), rqSA(rqSA), seq(seq), rqLun(rqLun), cmd(cmd),
57 timer(io)
58{
59 data.reserve(ipmbMaxDataSize);
60 state = ipmbRequestState::invalid;
61
62 if (inputData.size() > 0)
63 {
64 data = std::move(inputData);
65 }
66}
67
Dawid Fryckia642a942018-06-12 10:44:23 -070068void IpmbRequest::i2cToIpmbConstruct(IPMB_HEADER *ipmbBuffer,
69 size_t bufferLength)
70{
71 // constructing ipmb request from i2c buffer
72 netFn = ipmbNetFnGet(ipmbBuffer->Header.Req.rsNetFnLUN);
73 rsLun = ipmbLunFromNetFnLunGet(ipmbBuffer->Header.Req.rsNetFnLUN);
74 rqSA = ipmbBuffer->Header.Req.rqSA;
75 seq = ipmbSeqGet(ipmbBuffer->Header.Req.rqSeqLUN);
76 rqLun = ipmbLunFromSeqLunGet(ipmbBuffer->Header.Req.rqSeqLUN);
77 cmd = ipmbBuffer->Header.Req.cmd;
78
79 size_t dataLength =
80 bufferLength - (ipmbConnectionHeaderLength +
81 ipmbRequestDataHeaderLength + ipmbChecksumSize);
82
83 if (dataLength > 0)
84 {
85 data.insert(data.end(), ipmbBuffer->Header.Req.data,
86 &ipmbBuffer->Header.Req.data[dataLength]);
87 }
88}
89
90int IpmbRequest::ipmbToi2cConstruct(std::vector<uint8_t> &buffer)
91{
92 size_t bufferLength = data.size() + ipmbRequestDataHeaderLength +
93 ipmbConnectionHeaderLength + ipmbChecksumSize;
94
95 if (bufferLength > ipmbMaxFrameLength)
96 {
97 return -1;
98 }
99
100 buffer.resize(bufferLength);
101 static_assert(ipmbMaxFrameLength >= sizeof(IPMB_HEADER));
102 auto ipmbBuffer = reinterpret_cast<IPMB_HEADER *>(buffer.data());
103
104 // constructing buffer from ipmb request
105 ipmbBuffer->Header.Req.address = address;
106 ipmbBuffer->Header.Req.rsNetFnLUN = ipmbNetFnLunSet(netFn, rsLun);
107 ipmbBuffer->Header.Req.rqSA = rqSA;
108 ipmbBuffer->Header.Req.rqSeqLUN = ipmbSeqLunSet(seq, rqLun);
109 ipmbBuffer->Header.Req.cmd = cmd;
110
Qiang XUbbfd00a2019-06-27 21:10:06 +0800111 ipmbBuffer->Header.Req.checksum1 = ipmbChecksumCompute(
Dawid Fryckia642a942018-06-12 10:44:23 -0700112 buffer.data(), ipmbConnectionHeaderLength - ipmbChecksumSize);
113
114 if (data.size() > 0)
115 {
116 std::copy(data.begin(), data.end(), ipmbBuffer->Header.Req.data);
117 }
118
119 buffer[bufferLength - ipmbChecksumSize] =
120 ipmbChecksumCompute(buffer.data() + ipmbChecksum2StartOffset,
121 (ipmbRequestDataHeaderLength + data.size()));
122
123 return 0;
124}
125
126std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
127 IpmbRequest::returnMatchedResponse()
128{
129 return std::make_tuple(
130 static_cast<int>(ipmbResponseStatus::success), matchedResponse->netFn,
131 matchedResponse->rsLun, matchedResponse->cmd,
132 matchedResponse->completionCode, matchedResponse->data);
133}
134
135static std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
136 returnStatus(ipmbResponseStatus status)
137{
138 // we only want to send status here, other fields are not relevant
139 return std::make_tuple(static_cast<int>(status), 0, 0, 0, 0,
140 std::vector<uint8_t>(0));
141}
142
Dawid Fryckia642a942018-06-12 10:44:23 -0700143/**
144 * @brief Ipmb response class methods
145 */
146IpmbResponse::IpmbResponse()
147{
148 data.reserve(ipmbMaxDataSize);
149}
150
151IpmbResponse::IpmbResponse(uint8_t address, uint8_t netFn, uint8_t rqLun,
152 uint8_t rsSA, uint8_t seq, uint8_t rsLun,
153 uint8_t cmd, uint8_t completionCode,
Dawid Frycki8188d762019-04-01 18:03:48 -0700154 const std::vector<uint8_t> &inputData) :
Dawid Fryckia642a942018-06-12 10:44:23 -0700155 address(address),
156 netFn(netFn), rqLun(rqLun), rsSA(rsSA), seq(seq), rsLun(rsLun), cmd(cmd),
157 completionCode(completionCode)
158{
159 data.reserve(ipmbMaxDataSize);
160
161 if (inputData.size() > 0)
162 {
163 data = std::move(inputData);
164 }
165}
166
167void IpmbResponse::i2cToIpmbConstruct(IPMB_HEADER *ipmbBuffer,
168 size_t bufferLength)
169{
170 netFn = ipmbNetFnGet(ipmbBuffer->Header.Resp.rqNetFnLUN);
171 rqLun = ipmbLunFromNetFnLunGet(ipmbBuffer->Header.Resp.rqNetFnLUN);
172 rsSA = ipmbBuffer->Header.Resp.rsSA;
173 seq = ipmbSeqGet(ipmbBuffer->Header.Resp.rsSeqLUN);
174 rsLun = ipmbLunFromSeqLunGet(ipmbBuffer->Header.Resp.rsSeqLUN);
175 cmd = ipmbBuffer->Header.Resp.cmd;
176 completionCode = ipmbBuffer->Header.Resp.completionCode;
177
178 size_t dataLength =
179 bufferLength - (ipmbConnectionHeaderLength +
180 ipmbResponseDataHeaderLength + ipmbChecksumSize);
181
182 if (dataLength > 0)
183 {
184 data.insert(data.end(), ipmbBuffer->Header.Resp.data,
185 &ipmbBuffer->Header.Resp.data[dataLength]);
186 }
187}
188
Dawid Frycki8188d762019-04-01 18:03:48 -0700189std::shared_ptr<std::vector<uint8_t>> IpmbResponse::ipmbToi2cConstruct()
Dawid Fryckia642a942018-06-12 10:44:23 -0700190{
191 size_t bufferLength = data.size() + ipmbResponseDataHeaderLength +
192 ipmbConnectionHeaderLength + ipmbChecksumSize;
193
194 if (bufferLength > ipmbMaxFrameLength)
195 {
Dawid Frycki8188d762019-04-01 18:03:48 -0700196 return nullptr;
Dawid Fryckia642a942018-06-12 10:44:23 -0700197 }
198
Dawid Frycki8188d762019-04-01 18:03:48 -0700199 std::shared_ptr<std::vector<uint8_t>> buffer =
200 std::make_shared<std::vector<uint8_t>>(bufferLength);
201
202 auto ipmbBuffer = reinterpret_cast<IPMB_HEADER *>(buffer->data());
Dawid Fryckia642a942018-06-12 10:44:23 -0700203
204 ipmbBuffer->Header.Resp.address = address;
205 ipmbBuffer->Header.Resp.rqNetFnLUN = ipmbNetFnLunSet(netFn, rqLun);
206 ipmbBuffer->Header.Resp.rsSA = rsSA;
207 ipmbBuffer->Header.Resp.rsSeqLUN = ipmbSeqLunSet(seq, rsLun);
208 ipmbBuffer->Header.Resp.cmd = cmd;
209 ipmbBuffer->Header.Resp.completionCode = completionCode;
210
211 ipmbBuffer->Header.Resp.checksum1 = ipmbChecksumCompute(
Dawid Frycki8188d762019-04-01 18:03:48 -0700212 buffer->data(), ipmbConnectionHeaderLength - ipmbChecksumSize);
Dawid Fryckia642a942018-06-12 10:44:23 -0700213
214 if (data.size() > 0)
215 {
216 std::copy(data.begin(), data.end(), ipmbBuffer->Header.Resp.data);
217 }
218
Dawid Frycki8188d762019-04-01 18:03:48 -0700219 (*buffer)[bufferLength - ipmbChecksumSize] =
220 ipmbChecksumCompute(buffer->data() + ipmbChecksum2StartOffset,
Dawid Fryckia642a942018-06-12 10:44:23 -0700221 (ipmbResponseDataHeaderLength + data.size()));
222
Dawid Frycki8188d762019-04-01 18:03:48 -0700223 return buffer;
Dawid Fryckia642a942018-06-12 10:44:23 -0700224}
225
226bool IpmbCommandFilter::isBlocked(const uint8_t reqNetFn, const uint8_t cmd)
227{
228 auto blockedCmd = unhandledCommands.find({reqNetFn, cmd});
229
230 if (blockedCmd != unhandledCommands.end())
231 {
232 return true;
233 }
234
235 return false;
236}
237
238void IpmbCommandFilter::addFilter(const uint8_t reqNetFn, const uint8_t cmd)
239{
240 if (unhandledCommands.insert({reqNetFn, cmd}).second)
241 {
242 phosphor::logging::log<phosphor::logging::level::INFO>(
243 "addFilter: added command to filter",
244 phosphor::logging::entry("netFn = %d", reqNetFn),
245 phosphor::logging::entry("cmd = %d", cmd));
246 }
247}
248
249/**
250 * @brief Ipmb channel
251 */
Qiang XUbbfd00a2019-06-27 21:10:06 +0800252void IpmbChannel::ipmbSendI2cFrame(std::shared_ptr<std::vector<uint8_t>> buffer,
Dawid Fryckia642a942018-06-12 10:44:23 -0700253 size_t retriesAttempted = 0)
254{
Qiang XUbbfd00a2019-06-27 21:10:06 +0800255 // construct i2c frame and call ioctl to send it
256 auto ipmbFrame = reinterpret_cast<IPMB_HEADER *>(buffer->data());
257 uint8_t targetAddr = ipmbIsResponse(ipmbFrame)
258 ? ipmbFrame->Header.Resp.address
259 : ipmbFrame->Header.Req.address;
260 io.post([this, buffer, retriesAttempted, targetAddr]() {
261 ioWrite ioData(*buffer);
262 boost::system::error_code ec;
263 i2cMasterSocket.io_control(ioData, ec);
264 if (ec)
265 {
266 size_t currentRetryCnt = retriesAttempted;
267 if (currentRetryCnt > ipmbI2cNumberOfRetries)
Dawid Fryckia642a942018-06-12 10:44:23 -0700268 {
Qiang XUbbfd00a2019-06-27 21:10:06 +0800269 std::string msgToLog =
270 "ipmbSendI2cFrame: sent to I2C failed after retries."
271 " busId=" +
272 std::to_string(ipmbBusId) +
273 ", targetAddr=" + std::to_string(targetAddr) +
274 ", error=" + ec.message();
275 phosphor::logging::log<phosphor::logging::level::ERR>(
276 msgToLog.c_str());
277 return;
Dawid Fryckia642a942018-06-12 10:44:23 -0700278 }
Qiang XUbbfd00a2019-06-27 21:10:06 +0800279 currentRetryCnt++;
280 ipmbSendI2cFrame(buffer, currentRetryCnt);
281 }
282 });
Dawid Fryckia642a942018-06-12 10:44:23 -0700283}
284
285/**
286 * @brief Ipmb Outstanding Requests
287 */
288void IpmbChannel::makeRequestInvalid(IpmbRequest &request)
289{
290 // change request state to invalid and remove it from outstanding requests
291 // list
292 request.state = ipmbRequestState::invalid;
293 outstandingRequests[request.seq] = nullptr;
294}
295
296void IpmbChannel::makeRequestValid(std::shared_ptr<IpmbRequest> request)
297{
298 // change request state to valid and add it to outstanding requests list
299 request->state = ipmbRequestState::valid;
300 outstandingRequests[request->seq] = request;
301}
302
303bool IpmbChannel::seqNumGet(uint8_t &seq)
304{
305 static uint8_t seqNum = 0;
306
307 for (int i = 0; i < ipmbMaxOutstandingRequestsCount; i++)
308 {
309 seqNum = ++seqNum & ipmbSeqMask;
310 if (seqNum == ipmbMaxOutstandingRequestsCount)
311 {
312 seqNum = 0;
313 }
314
315 if (outstandingRequests[seqNum] == nullptr)
316 {
317 seq = seqNum;
318 return true;
319 }
320 }
321
322 return false;
323}
324
325void IpmbChannel::responseMatch(std::unique_ptr<IpmbResponse> &response)
326{
327 std::shared_ptr<IpmbRequest> request = outstandingRequests[response->seq];
328
329 if (request != nullptr)
330 {
331 if (((ipmbRespNetFn(request->netFn)) == (response->netFn)) &&
332 ((request->rqLun) == (response->rqLun)) &&
333 ((request->rsLun) == (response->rsLun)) &&
334 ((request->cmd) == (response->cmd)))
335 {
336 // match, response is corresponding to previously sent request
337 request->state = ipmbRequestState::matched;
338 request->timer->cancel();
339 request->matchedResponse = std::move(response);
340 }
341 }
342}
343
344void IpmbChannel::processI2cEvent()
345{
346 std::array<uint8_t, ipmbMaxFrameLength> buffer{};
347 auto ipmbFrame = reinterpret_cast<IPMB_HEADER *>(buffer.data());
348
349 lseek(ipmbi2cSlaveFd, 0, SEEK_SET);
350 int r = read(ipmbi2cSlaveFd, buffer.data(), ipmbMaxFrameLength);
351 if ((r < ipmbMinFrameLength) || (r > ipmbMaxFrameLength))
352 {
353 goto end;
354 }
355
356 // valiate the frame
357 if (!isFrameValid(ipmbFrame, r))
358 {
359 goto end;
360 }
361
Qiang XUbbfd00a2019-06-27 21:10:06 +0800362 // if it is broadcast message from ipmb channel, send out dbus signal
363 if (ipmbFrame->Header.Req.address == broadcastAddress &&
364 getChannelType() == ipmbChannelType::ipmb)
365 {
366 auto ipmbMessageReceived = IpmbRequest();
367 ipmbMessageReceived.i2cToIpmbConstruct(ipmbFrame, r);
368 sdbusplus::message::message msg =
369 conn->new_signal(ipmbObj, ipmbDbusIntf, "receiveBroadcast");
370 msg.append(ipmbMessageReceived.netFn, ipmbMessageReceived.cmd,
371 ipmbMessageReceived.data);
372 msg.signal_send();
373 }
374
Dawid Fryckia642a942018-06-12 10:44:23 -0700375 // copy frame to ipmib message buffer
Qiang XUbbfd00a2019-06-27 21:10:06 +0800376 else if (ipmbIsResponse(ipmbFrame))
Dawid Fryckia642a942018-06-12 10:44:23 -0700377 {
378 std::unique_ptr<IpmbResponse> ipmbMessageReceived =
379 std::make_unique<IpmbResponse>();
380
381 ipmbMessageReceived->i2cToIpmbConstruct(ipmbFrame, r);
382
383 // try to match response with outstanding request
384 responseMatch(ipmbMessageReceived);
385 }
386 else
387 {
388 // if command is blocked - respond with 'invalid command'
389 // completion code
390 if (commandFilter)
391 {
392 uint8_t netFn = ipmbNetFnGet(ipmbFrame->Header.Req.rsNetFnLUN);
393 uint8_t cmd = ipmbFrame->Header.Req.cmd;
Qiang XUbbfd00a2019-06-27 21:10:06 +0800394 uint8_t rqSA = ipmbFrame->Header.Req.rqSA;
Dawid Fryckia642a942018-06-12 10:44:23 -0700395
396 if (commandFilter->isBlocked(netFn, cmd))
397 {
398 uint8_t seq = ipmbSeqGet(ipmbFrame->Header.Req.rqSeqLUN);
399 uint8_t lun =
400 ipmbLunFromSeqLunGet(ipmbFrame->Header.Req.rqSeqLUN);
Dawid Fryckia642a942018-06-12 10:44:23 -0700401
402 // prepare generic response
Qiang XUbbfd00a2019-06-27 21:10:06 +0800403 auto ipmbResponse = IpmbResponse(
404 rqSA, ipmbRespNetFn(netFn), lun, ipmbBmcSlaveAddress, seq,
405 ipmbRsLun, cmd, ipmbIpmiInvalidCmd, {});
Dawid Fryckia642a942018-06-12 10:44:23 -0700406
Dawid Frycki8188d762019-04-01 18:03:48 -0700407 auto buffer = ipmbResponse.ipmbToi2cConstruct();
408 if (buffer)
Dawid Fryckia642a942018-06-12 10:44:23 -0700409 {
Qiang XUbbfd00a2019-06-27 21:10:06 +0800410 ipmbSendI2cFrame(buffer);
Dawid Fryckia642a942018-06-12 10:44:23 -0700411 }
412
413 goto end;
414 }
415 }
416
417 auto ipmbMessageReceived = IpmbRequest();
Dawid Fryckia642a942018-06-12 10:44:23 -0700418 ipmbMessageReceived.i2cToIpmbConstruct(ipmbFrame, r);
419
Dawid Frycki8188d762019-04-01 18:03:48 -0700420 std::map<std::string, std::variant<int>> options{
Qiang XUbbfd00a2019-06-27 21:10:06 +0800421 {"rqSA", ipmbAddressTo7BitSet(ipmbMessageReceived.rqSA)}};
Dawid Frycki8188d762019-04-01 18:03:48 -0700422 using IpmiDbusRspType = std::tuple<uint8_t, uint8_t, uint8_t, uint8_t,
423 std::vector<uint8_t>>;
424 conn->async_method_call(
425 [this, rqLun{ipmbMessageReceived.rqLun},
Qiang XUbbfd00a2019-06-27 21:10:06 +0800426 seq{ipmbMessageReceived.seq}, address{ipmbMessageReceived.rqSA}](
427 const boost::system::error_code &ec,
428 const IpmiDbusRspType &response) {
Dawid Frycki8188d762019-04-01 18:03:48 -0700429 const auto &[netfn, lun, cmd, cc, payload] = response;
430 if (ec)
431 {
432 phosphor::logging::log<phosphor::logging::level::ERR>(
433 "processI2cEvent: error getting response from IPMI");
434 return;
435 }
Dawid Fryckia642a942018-06-12 10:44:23 -0700436
Dawid Frycki8188d762019-04-01 18:03:48 -0700437 uint8_t bmcSlaveAddress = getBmcSlaveAddress();
438
439 if (payload.size() > ipmbMaxDataSize)
440 {
441 phosphor::logging::log<phosphor::logging::level::ERR>(
442 "processI2cEvent: response exceeding maximum size");
443
444 // prepare generic response
445 auto ipmbResponse = IpmbResponse(
Qiang XUbbfd00a2019-06-27 21:10:06 +0800446 address, netfn, rqLun, bmcSlaveAddress, seq, ipmbRsLun,
447 cmd, ipmbIpmiCmdRespNotProvided, {});
Dawid Frycki8188d762019-04-01 18:03:48 -0700448
449 auto buffer = ipmbResponse.ipmbToi2cConstruct();
450 if (buffer)
451 {
Qiang XUbbfd00a2019-06-27 21:10:06 +0800452 ipmbSendI2cFrame(buffer);
Dawid Frycki8188d762019-04-01 18:03:48 -0700453 }
454
455 return;
456 }
457
458 if (!(netfn & ipmbNetFnResponseMask))
459 {
460 // we are not expecting request here
461 phosphor::logging::log<phosphor::logging::level::ERR>(
462 "processI2cEvent: got a request instead of response");
463 return;
464 }
465
466 // if command is not supported, add it to filter
467 if (cc == ipmbIpmiInvalidCmd)
468 {
469 addFilter(ipmbReqNetFnFromRespNetFn(netfn), cmd);
470 }
471
472 // payload is empty after constructor invocation
473 auto ipmbResponse =
Qiang XUbbfd00a2019-06-27 21:10:06 +0800474 IpmbResponse(address, netfn, rqLun, bmcSlaveAddress, seq,
475 lun, cmd, cc, payload);
Dawid Frycki8188d762019-04-01 18:03:48 -0700476
477 auto buffer = ipmbResponse.ipmbToi2cConstruct();
478 if (!buffer)
479 {
480 phosphor::logging::log<phosphor::logging::level::ERR>(
481 "processI2cEvent: error constructing a request");
482 return;
483 }
484
Qiang XUbbfd00a2019-06-27 21:10:06 +0800485 ipmbSendI2cFrame(buffer);
Dawid Frycki8188d762019-04-01 18:03:48 -0700486 },
487 "xyz.openbmc_project.Ipmi.Host", "/xyz/openbmc_project/Ipmi",
488 "xyz.openbmc_project.Ipmi.Server", "execute",
489 ipmbMessageReceived.netFn, ipmbMessageReceived.rsLun,
490 ipmbMessageReceived.cmd, ipmbMessageReceived.data, options);
Dawid Fryckia642a942018-06-12 10:44:23 -0700491 }
492
493end:
494 i2cSlaveSocket.async_wait(
495 boost::asio::ip::tcp::socket::wait_error,
496 [this](const boost::system::error_code &ec) {
497 if (ec)
498 {
499 phosphor::logging::log<phosphor::logging::level::ERR>(
500 "Error: processI2cEvent()");
501 return;
502 }
503
504 processI2cEvent();
505 });
506}
507
508IpmbChannel::IpmbChannel(boost::asio::io_service &io,
509 uint8_t ipmbBmcSlaveAddress,
510 uint8_t ipmbRqSlaveAddress, ipmbChannelType type,
511 std::shared_ptr<IpmbCommandFilter> commandFilter) :
512 i2cSlaveSocket(io),
513 i2cMasterSocket(io), ipmbBmcSlaveAddress(ipmbBmcSlaveAddress),
514 ipmbRqSlaveAddress(ipmbRqSlaveAddress), type(type),
515 commandFilter(commandFilter)
516{
517}
518
519int IpmbChannel::ipmbChannelInit(const char *ipmbI2cSlave,
520 const char *ipmbI2cMaster)
521{
Qiang XU8edcf1a2019-06-14 22:18:15 +0800522 // extract bus id from master path and save
523 std::string ipmbI2cMasterStr(ipmbI2cMaster);
524 auto findHyphen = ipmbI2cMasterStr.find("-");
525 std::string busStr = ipmbI2cMasterStr.substr(findHyphen + 1);
526 try
527 {
528 ipmbBusId = std::stoi(busStr);
529 }
530 catch (std::invalid_argument)
531 {
532 phosphor::logging::log<phosphor::logging::level::ERR>(
533 "ipmbChannelInit: invalid bus id in master-path config");
534 return -1;
535 }
536
537 // Check if sysfs has device. If not, enable I2C slave driver by command
538 // echo "slave-mqueue 0x1010" > /sys/bus/i2c/devices/i2c-0/new_device
539 bool hasSysfs = std::filesystem::exists(ipmbI2cSlave);
540 if (!hasSysfs)
541 {
542 std::string deviceFileName =
543 "/sys/bus/i2c/devices/i2c-" + busStr + "/new_device";
544 std::string para = "slave-mqueue 0x1010"; // init with BMC addr 0x20
545 std::fstream deviceFile;
546 deviceFile.open(deviceFileName, std::ios::out);
547 if (!deviceFile.good())
548 {
549 phosphor::logging::log<phosphor::logging::level::ERR>(
550 "ipmbChannelInit: error opening deviceFile");
551 return -1;
552 }
553 deviceFile << para;
554 deviceFile.close();
555 }
556
Dawid Fryckia642a942018-06-12 10:44:23 -0700557 // open fd to i2c slave device
558 ipmbi2cSlaveFd = open(ipmbI2cSlave, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
559 if (ipmbi2cSlaveFd < 0)
560 {
561 phosphor::logging::log<phosphor::logging::level::ERR>(
562 "ipmbChannelInit: error opening ipmbI2cSlave");
563 return -1;
564 }
565
566 // open fd to i2c master device
567 ipmbi2cMasterFd = open(ipmbI2cMaster, O_RDWR | O_NONBLOCK);
568 if (ipmbi2cMasterFd < 0)
569 {
570 phosphor::logging::log<phosphor::logging::level::ERR>(
571 "ipmbChannelInit: error opening ipmbI2cMaster");
572 close(ipmbi2cSlaveFd);
Qiang XU8edcf1a2019-06-14 22:18:15 +0800573 ipmbi2cSlaveFd = 0;
Dawid Fryckia642a942018-06-12 10:44:23 -0700574 return -1;
575 }
576
577 // set slave address of recipient
578 if (ioctl(ipmbi2cMasterFd, I2C_SLAVE,
579 ipmbAddressTo7BitSet(ipmbRqSlaveAddress)) < 0)
580 {
581 phosphor::logging::log<phosphor::logging::level::ERR>(
582 "ipmbChannelInit: error setting ipmbi2cMasterFd slave address");
583 close(ipmbi2cSlaveFd);
584 close(ipmbi2cMasterFd);
Qiang XU8edcf1a2019-06-14 22:18:15 +0800585 ipmbi2cSlaveFd = 0;
586 ipmbi2cMasterFd = 0;
Dawid Fryckia642a942018-06-12 10:44:23 -0700587 return -1;
588 }
589
590 i2cMasterSocket.assign(ipmbi2cMasterFd);
591 i2cSlaveSocket.assign(boost::asio::ip::tcp::v4(), ipmbi2cSlaveFd);
592 i2cSlaveSocket.async_wait(
593 boost::asio::ip::tcp::socket::wait_error,
594 [this](const boost::system::error_code &ec) {
595 if (ec)
596 {
597 phosphor::logging::log<phosphor::logging::level::ERR>(
598 "Error: processI2cEvent()");
599 return;
600 }
601
602 processI2cEvent();
603 });
604
605 return 0;
606}
607
Qiang XU8edcf1a2019-06-14 22:18:15 +0800608int IpmbChannel::ipmbChannelUpdateSlaveAddress(const uint8_t newBmcSlaveAddr)
609{
610 if (ipmbi2cSlaveFd > 0)
611 {
612 i2cSlaveSocket.close();
613 close(ipmbi2cSlaveFd);
614 ipmbi2cSlaveFd = 0;
615 }
616
617 // disable old I2C slave driver by command:
618 // echo "0x1010" > /sys/bus/i2c/devices/i2c-0/delete_device
619 std::string deviceFileName;
620 std::string para;
621 std::fstream deviceFile;
622 deviceFileName = "/sys/bus/i2c/devices/i2c-" + std::to_string(ipmbBusId) +
623 "/delete_device";
624 para = "0x1010"; // align with removed ipmb0 definition in dts file
625 deviceFile.open(deviceFileName, std::ios::out);
626 if (!deviceFile.good())
627 {
628 phosphor::logging::log<phosphor::logging::level::ERR>(
629 "ipmbChannelUpdateSlaveAddress: error opening deviceFile to delete "
630 "sysfs");
631 return -1;
632 }
633 deviceFile << para;
634 deviceFile.close();
635
636 // enable new I2C slave driver by command:
637 // echo "slave-mqueue 0x1012" > /sys/bus/i2c/devices/i2c-0/new_device
638 deviceFileName =
639 "/sys/bus/i2c/devices/i2c-" + std::to_string(ipmbBusId) + "/new_device";
640 std::ostringstream hex;
641 uint16_t addr = 0x1000 + (newBmcSlaveAddr >> 1);
642 hex << std::hex << static_cast<uint16_t>(addr);
643 const std::string &addressHexStr = hex.str();
644 para = "slave-mqueue 0x" + addressHexStr;
645 deviceFile.open(deviceFileName, std::ios::out);
646 if (!deviceFile.good())
647 {
648 phosphor::logging::log<phosphor::logging::level::ERR>(
649 "ipmbChannelUpdateSlaveAddress: error opening deviceFile to create "
650 "sysfs");
651 return -1;
652 }
653 deviceFile << para;
654 deviceFile.close();
655
656 // open fd to i2c slave device
657 std::string ipmbI2cSlaveStr = "/sys/bus/i2c/devices/" +
658 std::to_string(ipmbBusId) + "-" +
659 addressHexStr + "/slave-mqueue";
660 ipmbi2cSlaveFd =
661 open(ipmbI2cSlaveStr.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC);
662 if (ipmbi2cSlaveFd < 0)
663 {
664 phosphor::logging::log<phosphor::logging::level::ERR>(
665 "ipmbChannelInit: error opening ipmbI2cSlave");
666 return -1;
667 }
668
669 // start to receive i2c data as slave
670 i2cSlaveSocket.assign(boost::asio::ip::tcp::v4(), ipmbi2cSlaveFd);
671 i2cSlaveSocket.async_wait(
672 boost::asio::ip::tcp::socket::wait_error,
673 [this](const boost::system::error_code &ec) {
674 if (ec)
675 {
676 phosphor::logging::log<phosphor::logging::level::ERR>(
677 "Error: processI2cEvent()");
678 return;
679 }
680
681 processI2cEvent();
682 });
683
Qiang XUbbfd00a2019-06-27 21:10:06 +0800684 ipmbBmcSlaveAddress = newBmcSlaveAddr;
685
Qiang XU8edcf1a2019-06-14 22:18:15 +0800686 return 0;
687}
688
689uint8_t IpmbChannel::getBusId()
690{
691 return ipmbBusId;
692}
693
Dawid Fryckia642a942018-06-12 10:44:23 -0700694uint8_t IpmbChannel::getBmcSlaveAddress()
695{
696 return ipmbBmcSlaveAddress;
697}
698
699uint8_t IpmbChannel::getRqSlaveAddress()
700{
701 return ipmbRqSlaveAddress;
702}
703
704ipmbChannelType IpmbChannel::getChannelType()
705{
706 return type;
707}
708
709void IpmbChannel::addFilter(const uint8_t respNetFn, const uint8_t cmd)
710{
711 if (commandFilter)
712 {
713 commandFilter->addFilter(respNetFn, cmd);
714 }
715}
716
717std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
718 IpmbChannel::requestAdd(boost::asio::yield_context &yield,
719 std::shared_ptr<IpmbRequest> request)
720{
721 makeRequestValid(request);
722
723 std::vector<uint8_t> buffer(0);
724 if (request->ipmbToi2cConstruct(buffer) != 0)
725 {
726 return returnStatus(ipmbResponseStatus::error);
727 }
728
729 for (int i = 0; i < ipmbNumberOfTries; i++)
730 {
731 boost::system::error_code ec;
Jae Hyun Yoo25e85c72019-03-05 14:28:13 -0800732 int i2cRetryCnt = 0;
Dawid Fryckia642a942018-06-12 10:44:23 -0700733
Jae Hyun Yoo25e85c72019-03-05 14:28:13 -0800734 for (; i2cRetryCnt < ipmbI2cNumberOfRetries; i2cRetryCnt++)
Dawid Fryckia642a942018-06-12 10:44:23 -0700735 {
736 boost::asio::async_write(
737 i2cMasterSocket,
738 boost::asio::buffer(buffer.data() + ipmbAddressSize,
739 buffer.size() - ipmbAddressSize),
740 yield[ec]);
741
742 if (ec)
743 {
Jae Hyun Yoo25e85c72019-03-05 14:28:13 -0800744 continue; // retry
Dawid Fryckia642a942018-06-12 10:44:23 -0700745 }
746 break;
747 }
748
Jae Hyun Yoo25e85c72019-03-05 14:28:13 -0800749 if (i2cRetryCnt == ipmbI2cNumberOfRetries)
750 {
Qiang XUbbfd00a2019-06-27 21:10:06 +0800751 std::string msgToLog =
752 "requestAdd: Sent to I2C failed after retries."
753 " busId=" +
754 std::to_string(ipmbBusId) + ", error=" + ec.message();
Jae Hyun Yoo25e85c72019-03-05 14:28:13 -0800755 phosphor::logging::log<phosphor::logging::level::INFO>(
Qiang XUbbfd00a2019-06-27 21:10:06 +0800756 msgToLog.c_str());
Jae Hyun Yoo25e85c72019-03-05 14:28:13 -0800757 }
758
Dawid Fryckia642a942018-06-12 10:44:23 -0700759 request->timer->expires_after(
760 std::chrono::milliseconds(ipmbRequestRetryTimeout));
761 request->timer->async_wait(yield[ec]);
762
763 if (ec && ec != boost::asio::error::operation_aborted)
764 {
765 // unexpected error - invalidate request and return generic error
766 phosphor::logging::log<phosphor::logging::level::ERR>(
767 "requestAdd: async_wait error");
768 makeRequestInvalid(*request);
769 return returnStatus(ipmbResponseStatus::error);
770 }
771
772 if (request->state == ipmbRequestState::matched)
773 {
774 // matched response, send it to client application
775 makeRequestInvalid(*request);
776 return request->returnMatchedResponse();
777 }
778 }
779
780 makeRequestInvalid(*request);
781 return returnStatus(ipmbResponseStatus::timeout);
782}
783
784static IpmbChannel *getChannel(ipmbChannelType channelType)
785{
786 auto channel =
787 std::find_if(ipmbChannels.begin(), ipmbChannels.end(),
788 [channelType](IpmbChannel &channel) {
789 return channel.getChannelType() == channelType;
790 });
791 if (channel != ipmbChannels.end())
792 {
793 return &(*channel);
794 }
795
796 return nullptr;
797}
798
799static int initializeChannels()
800{
801 std::shared_ptr<IpmbCommandFilter> commandFilter =
802 std::make_shared<IpmbCommandFilter>();
803
Amithash Prasad314862d2019-03-26 11:14:03 -0700804 constexpr const char *configFilePath =
805 "/usr/share/ipmbbridge/ipmb-channels.json";
806 std::ifstream configFile(configFilePath);
807 if (!configFile.is_open())
Dawid Fryckia642a942018-06-12 10:44:23 -0700808 {
Amithash Prasad314862d2019-03-26 11:14:03 -0700809 phosphor::logging::log<phosphor::logging::level::ERR>(
810 "initializeChannels: Cannot open config path");
811 return -1;
812 }
813 try
814 {
815 auto data = nlohmann::json::parse(configFile, nullptr);
816 for (const auto &channelConfig : data["channels"])
Dawid Fryckia642a942018-06-12 10:44:23 -0700817 {
Amithash Prasad314862d2019-03-26 11:14:03 -0700818 const std::string &typeConfig = channelConfig["type"];
819 const std::string &masterPath = channelConfig["master-path"];
820 const std::string &slavePath = channelConfig["slave-path"];
821 uint8_t bmcAddr = channelConfig["bmc-addr"];
822 uint8_t reqAddr = channelConfig["remote-addr"];
823 ipmbChannelType type = ipmbChannelTypeMap.at(typeConfig);
824
825 auto channel = ipmbChannels.emplace(ipmbChannels.end(), io, bmcAddr,
826 reqAddr, type, commandFilter);
827 if (channel->ipmbChannelInit(slavePath.c_str(),
828 masterPath.c_str()) < 0)
829 {
830 phosphor::logging::log<phosphor::logging::level::ERR>(
831 "initializeChannels: channel initialization failed");
832 return -1;
833 }
Dawid Fryckia642a942018-06-12 10:44:23 -0700834 }
835 }
Amithash Prasad314862d2019-03-26 11:14:03 -0700836 catch (nlohmann::json::exception &e)
837 {
838 phosphor::logging::log<phosphor::logging::level::ERR>(
839 "initializeChannels: Error parsing config file");
840 return -1;
841 }
842 catch (std::out_of_range &e)
843 {
844 phosphor::logging::log<phosphor::logging::level::ERR>(
845 "initializeChannels: Error invalid type");
846 return -1;
847 }
Dawid Fryckia642a942018-06-12 10:44:23 -0700848 return 0;
849}
850
Dawid Fryckia642a942018-06-12 10:44:23 -0700851auto ipmbHandleRequest = [](boost::asio::yield_context yield,
852 uint8_t reqChannel, uint8_t netfn, uint8_t lun,
853 uint8_t cmd, std::vector<uint8_t> dataReceived) {
854 IpmbChannel *channel = getChannel(static_cast<ipmbChannelType>(reqChannel));
855 if (channel == nullptr)
856 {
857 phosphor::logging::log<phosphor::logging::level::ERR>(
858 "ipmbHandleRequest: requested channel does not exist");
859 return returnStatus(ipmbResponseStatus::invalid_param);
860 }
861
862 // check outstanding request list for valid sequence number
863 uint8_t seqNum = 0;
864 bool seqValid = channel->seqNumGet(seqNum);
865 if (!seqValid)
866 {
867 phosphor::logging::log<phosphor::logging::level::WARNING>(
868 "ipmbHandleRequest: cannot add more requests to the list");
869 return returnStatus(ipmbResponseStatus::busy);
870 }
871
872 uint8_t bmcSlaveAddress = channel->getBmcSlaveAddress();
873 uint8_t rqSlaveAddress = channel->getRqSlaveAddress();
874
875 // construct the request to add it to outstanding request list
876 std::shared_ptr<IpmbRequest> request = std::make_shared<IpmbRequest>(
877 rqSlaveAddress, netfn, ipmbRsLun, bmcSlaveAddress, seqNum, lun, cmd,
878 dataReceived);
879
880 if (!request->timer)
881 {
882 phosphor::logging::log<phosphor::logging::level::ERR>(
883 "ipmbHandleRequest: timer object does not exist");
884 return returnStatus(ipmbResponseStatus::error);
885 }
886
887 return channel->requestAdd(yield, request);
888};
889
Qiang XU8edcf1a2019-06-14 22:18:15 +0800890void addUpdateSlaveAddrHandler()
891{
892 // callback to handle dbus signal of updating slave addr
893 std::function<void(sdbusplus::message::message &)> updateSlaveAddrHandler =
894 [](sdbusplus::message::message &message) {
895 uint8_t reqChannel, busId, slaveAddr;
896
897 // valid source of signal, check whether from multi-node manager
898 std::string pathName = message.get_path();
899 if (pathName != "/xyz/openbmc_project/MultiNode/Status")
900 {
901 phosphor::logging::log<phosphor::logging::level::ERR>(
902 "addUpdateSlaveAddrHandler: invalid obj path");
903 return;
904 }
905
906 message.read(reqChannel, busId, slaveAddr);
907
908 IpmbChannel *channel =
909 getChannel(static_cast<ipmbChannelType>(reqChannel));
910 if (channel == nullptr ||
911 reqChannel != static_cast<uint8_t>(ipmbChannelType::ipmb))
912 {
913 phosphor::logging::log<phosphor::logging::level::ERR>(
914 "addUpdateSlaveAddrHandler: invalid channel");
915 return;
916 }
917 if (busId != channel->getBusId())
918 {
919 phosphor::logging::log<phosphor::logging::level::ERR>(
920 "addUpdateSlaveAddrHandler: invalid busId");
921 return;
922 }
923 if (channel->getBmcSlaveAddress() == slaveAddr)
924 {
925 phosphor::logging::log<phosphor::logging::level::INFO>(
926 "addUpdateSlaveAddrHandler: channel bmc slave addr is "
927 "unchanged, do nothing");
928 return;
929 }
930
931 channel->ipmbChannelUpdateSlaveAddress(slaveAddr);
932 };
933
934 static auto match = std::make_unique<sdbusplus::bus::match::match>(
935 static_cast<sdbusplus::bus::bus &>(*conn),
936 "type='signal',member='updateBmcSlaveAddr',", updateSlaveAddrHandler);
937}
938
Qiang XUbbfd00a2019-06-27 21:10:06 +0800939void addSendBroadcastHandler()
940{
941 // callback to handle dbus signal of sending broadcast message
942 std::function<void(sdbusplus::message::message &)> sendBroadcastHandler =
943 [](sdbusplus::message::message &message) {
944 uint8_t reqChannel, netFn, lun, cmd;
945 std::vector<uint8_t> dataReceived;
946 message.read(reqChannel, netFn, lun, cmd, dataReceived);
947
948 IpmbChannel *channel =
949 getChannel(static_cast<ipmbChannelType>(reqChannel));
950 if (channel == nullptr)
951 {
952 phosphor::logging::log<phosphor::logging::level::ERR>(
953 "addSendBroadcastMsgHandler: requested channel does not "
954 "exist");
955 return;
956 }
957
958 uint8_t bmcSlaveAddress = channel->getBmcSlaveAddress();
959 uint8_t seqNum = 0; // seqNum is not used in broadcast msg
960 uint8_t targetAddr = broadcastAddress;
961
962 std::shared_ptr<IpmbRequest> request =
963 std::make_shared<IpmbRequest>(targetAddr, netFn, ipmbRsLun,
964 bmcSlaveAddress, seqNum, lun, cmd,
965 dataReceived);
966
967 std::shared_ptr<std::vector<uint8_t>> buffer =
968 std::make_shared<std::vector<uint8_t>>();
969
970 if (request->ipmbToi2cConstruct(*buffer) != 0)
971 {
972 return;
973 }
974
975 channel->ipmbSendI2cFrame(buffer);
976 };
977
978 static auto match = std::make_unique<sdbusplus::bus::match::match>(
979 static_cast<sdbusplus::bus::bus &>(*conn),
980 "type='signal',member='sendBroadcast',", sendBroadcastHandler);
981}
982
Dawid Fryckia642a942018-06-12 10:44:23 -0700983/**
984 * @brief Main
985 */
986int main(int argc, char *argv[])
987{
988 conn->request_name(ipmbBus);
989
990 auto server = sdbusplus::asio::object_server(conn);
991
Dawid Fryckia642a942018-06-12 10:44:23 -0700992 std::shared_ptr<sdbusplus::asio::dbus_interface> ipmbIface =
993 server.add_interface(ipmbObj, ipmbDbusIntf);
994
Dawid Fryckia642a942018-06-12 10:44:23 -0700995 ipmbIface->register_method("sendRequest", std::move(ipmbHandleRequest));
Dawid Fryckia642a942018-06-12 10:44:23 -0700996 ipmbIface->initialize();
997
998 if (initializeChannels() < 0)
999 {
1000 phosphor::logging::log<phosphor::logging::level::ERR>(
1001 "Error initializeChannels");
1002 return -1;
1003 }
1004
Qiang XU8edcf1a2019-06-14 22:18:15 +08001005 addUpdateSlaveAddrHandler();
1006
Qiang XUbbfd00a2019-06-27 21:10:06 +08001007 addSendBroadcastHandler();
1008
Dawid Fryckia642a942018-06-12 10:44:23 -07001009 io.run();
1010 return 0;
1011}