1 /* 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
21 #include <boost/algorithm/string/replace.hpp>
22 #include <boost/asio/io_context.hpp>
23 #include <boost/asio/write.hpp>
24 #include <nlohmann/json.hpp>
25 #include <phosphor-logging/log.hpp>
26
27 #include <filesystem>
28 #include <fstream>
29 #include <list>
30 #include <tuple>
31 #include <unordered_map>
32
33 /**
34 * @brief Dbus
35 */
36 static constexpr const char* ipmbBus = "xyz.openbmc_project.Ipmi.Channel.Ipmb";
37 static constexpr const char* ipmbObj = "/xyz/openbmc_project/Ipmi/Channel/Ipmb";
38 static constexpr const char* ipmbDbusIntf = "org.openbmc.Ipmb";
39
40 boost::asio::io_context io;
41 auto conn = std::make_shared<sdbusplus::asio::connection>(io);
42
43 static std::list<IpmbChannel> ipmbChannels;
44 static const std::unordered_map<std::string, ipmbChannelType>
45 ipmbChannelTypeMap = {{"me", ipmbChannelType::me},
46 {"ipmb", ipmbChannelType::ipmb}};
47
48 /**
49 * @brief Ipmb request class methods
50 */
IpmbRequest()51 IpmbRequest::IpmbRequest()
52 {
53 data.reserve(ipmbMaxDataSize);
54 }
55
IpmbRequest(uint8_t address,uint8_t netFn,uint8_t rsLun,uint8_t rqSA,uint8_t seq,uint8_t rqLun,uint8_t cmd,const std::vector<uint8_t> & inputData)56 IpmbRequest::IpmbRequest(uint8_t address, uint8_t netFn, uint8_t rsLun,
57 uint8_t rqSA, uint8_t seq, uint8_t rqLun, uint8_t cmd,
58 const std::vector<uint8_t>& inputData) :
59 address(address), netFn(netFn), rsLun(rsLun), rqSA(rqSA), seq(seq),
60 rqLun(rqLun), cmd(cmd), 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
i2cToIpmbConstruct(IPMB_HEADER * ipmbBuffer,size_t bufferLength)71 void 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
ipmbToi2cConstruct(std::vector<uint8_t> & buffer)93 int IpmbRequest::ipmbToi2cConstruct(std::vector<uint8_t>& buffer)
94 {
95 /* Add one byte for length byte as per required by driver */
96 size_t bufferLength = 1 + data.size() + ipmbRequestDataHeaderLength +
97 ipmbConnectionHeaderLength + ipmbChecksumSize;
98
99 if (bufferLength > ipmbMaxFrameLength)
100 {
101 return -1;
102 }
103
104 buffer.resize(bufferLength);
105 static_assert(ipmbMaxFrameLength >= sizeof(IPMB_HEADER));
106 IPMB_PKT* ipmbPkt = reinterpret_cast<IPMB_PKT*>(buffer.data());
107 ipmbPkt->len = bufferLength - 1;
108 IPMB_HEADER* ipmbBuffer = &(ipmbPkt->hdr);
109
110 // constructing buffer from ipmb request
111 ipmbBuffer->Header.Req.address = address;
112 ipmbBuffer->Header.Req.rsNetFnLUN = ipmbNetFnLunSet(netFn, rsLun);
113 ipmbBuffer->Header.Req.rqSA = rqSA;
114 ipmbBuffer->Header.Req.rqSeqLUN = ipmbSeqLunSet(seq, rqLun);
115 ipmbBuffer->Header.Req.cmd = cmd;
116
117 ipmbBuffer->Header.Req.checksum1 = ipmbChecksumCompute(
118 (uint8_t*)ipmbBuffer, ipmbConnectionHeaderLength - ipmbChecksumSize);
119
120 if (data.size() > 0)
121 {
122 std::copy(data.begin(), data.end(), ipmbBuffer->Header.Req.data);
123 }
124
125 buffer[bufferLength - ipmbChecksumSize] =
126 ipmbChecksumCompute((uint8_t*)ipmbBuffer + ipmbChecksum2StartOffset,
127 (ipmbRequestDataHeaderLength + data.size()));
128
129 return 0;
130 }
131
132 std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
returnMatchedResponse()133 IpmbRequest::returnMatchedResponse()
134 {
135 return std::make_tuple(
136 static_cast<int>(ipmbResponseStatus::success), matchedResponse->netFn,
137 matchedResponse->rsLun, matchedResponse->cmd,
138 matchedResponse->completionCode, matchedResponse->data);
139 }
140
141 static std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
returnStatus(ipmbResponseStatus status)142 returnStatus(ipmbResponseStatus status)
143 {
144 // we only want to send status here, other fields are not relevant
145 return std::make_tuple(static_cast<int>(status), 0, 0, 0, 0,
146 std::vector<uint8_t>(0));
147 }
148
149 /**
150 * @brief Ipmb response class methods
151 */
IpmbResponse()152 IpmbResponse::IpmbResponse()
153 {
154 data.reserve(ipmbMaxDataSize);
155 }
156
IpmbResponse(uint8_t address,uint8_t netFn,uint8_t rqLun,uint8_t rsSA,uint8_t seq,uint8_t rsLun,uint8_t cmd,uint8_t completionCode,const std::vector<uint8_t> & inputData)157 IpmbResponse::IpmbResponse(uint8_t address, uint8_t netFn, uint8_t rqLun,
158 uint8_t rsSA, uint8_t seq, uint8_t rsLun,
159 uint8_t cmd, uint8_t completionCode,
160 const std::vector<uint8_t>& inputData) :
161 address(address), netFn(netFn), rqLun(rqLun), rsSA(rsSA), seq(seq),
162 rsLun(rsLun), cmd(cmd), completionCode(completionCode)
163 {
164 data.reserve(ipmbMaxDataSize);
165
166 if (inputData.size() > 0)
167 {
168 data = std::move(inputData);
169 }
170 }
171
i2cToIpmbConstruct(IPMB_HEADER * ipmbBuffer,size_t bufferLength)172 void 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
ipmbToi2cConstruct()194 std::shared_ptr<std::vector<uint8_t>> IpmbResponse::ipmbToi2cConstruct()
195 {
196 /* Add one byte for length byte as per required by driver */
197 size_t bufferLength = 1 + data.size() + ipmbResponseDataHeaderLength +
198 ipmbConnectionHeaderLength + ipmbChecksumSize;
199
200 if (bufferLength > ipmbMaxFrameLength)
201 {
202 return nullptr;
203 }
204
205 std::shared_ptr<std::vector<uint8_t>> buffer =
206 std::make_shared<std::vector<uint8_t>>(bufferLength);
207
208 IPMB_PKT* ipmbPkt = reinterpret_cast<IPMB_PKT*>(buffer->data());
209 ipmbPkt->len = bufferLength - 1;
210 IPMB_HEADER* ipmbBuffer = &(ipmbPkt->hdr);
211
212 ipmbBuffer->Header.Resp.address = address;
213 ipmbBuffer->Header.Resp.rqNetFnLUN = ipmbNetFnLunSet(netFn, rqLun);
214 ipmbBuffer->Header.Resp.rsSA = rsSA;
215 ipmbBuffer->Header.Resp.rsSeqLUN = ipmbSeqLunSet(seq, rsLun);
216 ipmbBuffer->Header.Resp.cmd = cmd;
217 ipmbBuffer->Header.Resp.completionCode = completionCode;
218
219 ipmbBuffer->Header.Resp.checksum1 = ipmbChecksumCompute(
220 (uint8_t*)ipmbBuffer, ipmbConnectionHeaderLength - ipmbChecksumSize);
221
222 if (data.size() > 0)
223 {
224 std::copy(data.begin(), data.end(), ipmbBuffer->Header.Resp.data);
225 }
226
227 (*buffer)[bufferLength - ipmbChecksumSize] =
228 ipmbChecksumCompute((uint8_t*)ipmbBuffer + ipmbChecksum2StartOffset,
229 (ipmbResponseDataHeaderLength + data.size()));
230
231 return buffer;
232 }
233
isBlocked(const uint8_t reqNetFn,const uint8_t cmd)234 bool IpmbCommandFilter::isBlocked(const uint8_t reqNetFn, const uint8_t cmd)
235 {
236 auto blockedCmd = unhandledCommands.find({reqNetFn, cmd});
237
238 if (blockedCmd != unhandledCommands.end())
239 {
240 return true;
241 }
242
243 return false;
244 }
245
addFilter(const uint8_t reqNetFn,const uint8_t cmd)246 void IpmbCommandFilter::addFilter(const uint8_t reqNetFn, const uint8_t cmd)
247 {
248 if (unhandledCommands.insert({reqNetFn, cmd}).second)
249 {
250 phosphor::logging::log<phosphor::logging::level::INFO>(
251 "addFilter: added command to filter",
252 phosphor::logging::entry("netFn = %d", reqNetFn),
253 phosphor::logging::entry("cmd = %d", cmd));
254 }
255 }
256
257 /**
258 * @brief Ipmb channel
259 */
ipmbSendI2cFrame(std::shared_ptr<std::vector<uint8_t>> buffer,size_t retriesAttempted=0)260 void IpmbChannel::ipmbSendI2cFrame(std::shared_ptr<std::vector<uint8_t>> buffer,
261 size_t retriesAttempted = 0)
262 {
263 IPMB_PKT* ipmbPkt = reinterpret_cast<IPMB_PKT*>(buffer->data());
264 uint8_t targetAddr = ipmbIsResponse(&(ipmbPkt->hdr))
265 ? ipmbPkt->hdr.Header.Resp.address
266 : ipmbPkt->hdr.Header.Req.address;
267 boost::asio::async_write(
268 i2cTargetDescriptor, boost::asio::buffer(*buffer),
269 [this, buffer, retriesAttempted, targetAddr](
270 const boost::system::error_code& ec, size_t /* bytesSent */) {
271 if (ec)
272 {
273 size_t currentRetryCnt = retriesAttempted;
274
275 if (currentRetryCnt > ipmbI2cNumberOfRetries)
276 {
277 std::string msgToLog =
278 "ipmbSendI2cFrame: send to I2C failed after retries."
279 " busId=" +
280 std::to_string(ipmbBusId) +
281 ", targetAddr=" + std::to_string(targetAddr) +
282 ", error=" + ec.message();
283 phosphor::logging::log<phosphor::logging::level::ERR>(
284 msgToLog.c_str());
285 return;
286 }
287 currentRetryCnt++;
288 ipmbSendI2cFrame(buffer, currentRetryCnt);
289 }
290 });
291 }
292
293 /**
294 * @brief Ipmb Outstanding Requests
295 */
makeRequestInvalid(IpmbRequest & request)296 void IpmbChannel::makeRequestInvalid(IpmbRequest& request)
297 {
298 // change request state to invalid and remove it from outstanding requests
299 // list
300 request.state = ipmbRequestState::invalid;
301 outstandingRequests[request.seq] = nullptr;
302 }
303
makeRequestValid(std::shared_ptr<IpmbRequest> request)304 void IpmbChannel::makeRequestValid(std::shared_ptr<IpmbRequest> request)
305 {
306 // change request state to valid and add it to outstanding requests list
307 request->state = ipmbRequestState::valid;
308 outstandingRequests[request->seq] = request;
309 }
310
seqNumGet(uint8_t & seq)311 bool IpmbChannel::seqNumGet(uint8_t& seq)
312 {
313 static uint8_t seqNum = 0;
314
315 for (int i = 0; i < ipmbMaxOutstandingRequestsCount; i++)
316 {
317 seqNum = (seqNum + 1) % ipmbMaxOutstandingRequestsCount;
318
319 if (outstandingRequests[seqNum] == nullptr)
320 {
321 seq = seqNum;
322 return true;
323 }
324 }
325
326 return false;
327 }
328
responseMatch(std::unique_ptr<IpmbResponse> & response)329 void IpmbChannel::responseMatch(std::unique_ptr<IpmbResponse>& response)
330 {
331 std::shared_ptr<IpmbRequest> request = outstandingRequests[response->seq];
332
333 if (request != nullptr)
334 {
335 if (((ipmbRespNetFn(request->netFn)) == (response->netFn)) &&
336 ((request->rqLun) == (response->rqLun)) &&
337 ((request->rsLun) == (response->rsLun)) &&
338 ((request->cmd) == (response->cmd)))
339 {
340 // match, response is corresponding to previously sent request
341 request->state = ipmbRequestState::matched;
342 request->timer->cancel();
343 request->matchedResponse = std::move(response);
344 }
345 }
346 }
347
processI2cEvent()348 void IpmbChannel::processI2cEvent()
349 {
350 std::array<uint8_t, ipmbMaxFrameLength> buffer{};
351 IPMB_PKT* ipmbPkt = reinterpret_cast<IPMB_PKT*>(buffer.data());
352 IPMB_HEADER* ipmbFrame = &(ipmbPkt->hdr);
353
354 lseek(ipmbi2cTargetFd, 0, SEEK_SET);
355 ssize_t r = read(ipmbi2cTargetFd, buffer.data(), ipmbMaxFrameLength);
356
357 // Handle error cases.
358 if (r < 0)
359 {
360 goto end;
361 }
362
363 /* Subtract first byte len size from total frame length */
364 r--;
365
366 if ((r < ipmbMinFrameLength) || (r > ipmbMaxFrameLength))
367 {
368 goto end;
369 }
370
371 // validate the frame
372 if (!isFrameValid(ipmbFrame, r))
373 {
374 goto end;
375 }
376
377 // if it is message received from ipmb channel, send out dbus signal
378 if (getChannelType() == ipmbChannelType::ipmb)
379 {
380 auto ipmbMessageReceived = IpmbRequest();
381 ipmbMessageReceived.i2cToIpmbConstruct(ipmbFrame, r);
382 sdbusplus::message_t msg =
383 conn->new_signal(ipmbObj, ipmbDbusIntf, "receiveBroadcast");
384 msg.append(ipmbMessageReceived.netFn, ipmbMessageReceived.cmd,
385 ipmbMessageReceived.data);
386 msg.signal_send();
387 }
388
389 // copy frame to ipmib message buffer
390 if (ipmbIsResponse(ipmbFrame))
391 {
392 std::unique_ptr<IpmbResponse> ipmbMessageReceived =
393 std::make_unique<IpmbResponse>();
394
395 ipmbMessageReceived->i2cToIpmbConstruct(ipmbFrame, r);
396
397 // try to match response with outstanding request
398 responseMatch(ipmbMessageReceived);
399 }
400 else
401 {
402 // if command is blocked - respond with 'invalid command'
403 // completion code
404 if (commandFilter)
405 {
406 uint8_t netFn = ipmbNetFnGet(ipmbFrame->Header.Req.rsNetFnLUN);
407 uint8_t cmd = ipmbFrame->Header.Req.cmd;
408 uint8_t rqSA = ipmbFrame->Header.Req.rqSA;
409
410 if (commandFilter->isBlocked(netFn, cmd))
411 {
412 uint8_t seq = ipmbSeqGet(ipmbFrame->Header.Req.rqSeqLUN);
413 uint8_t lun =
414 ipmbLunFromSeqLunGet(ipmbFrame->Header.Req.rqSeqLUN);
415
416 // prepare generic response
417 auto ipmbResponse = IpmbResponse(
418 rqSA, ipmbRespNetFn(netFn), lun, ipmbBmcTargetAddress, seq,
419 ipmbRsLun, cmd, ipmbIpmiInvalidCmd, {});
420
421 auto buffer = ipmbResponse.ipmbToi2cConstruct();
422 if (buffer)
423 {
424 ipmbSendI2cFrame(buffer);
425 }
426
427 goto end;
428 }
429 }
430
431 auto ipmbMessageReceived = IpmbRequest();
432 ipmbMessageReceived.i2cToIpmbConstruct(ipmbFrame, r);
433
434 int devId = getDevIndex();
435
436 std::map<std::string, std::variant<int>> options{
437 {"rqSA", ipmbAddressTo7BitSet(ipmbMessageReceived.rqSA)},
438 {"hostId", devId}};
439
440 using IpmiDbusRspType = std::tuple<uint8_t, uint8_t, uint8_t, uint8_t,
441 std::vector<uint8_t>>;
442 conn->async_method_call(
443 [this, rqLun{ipmbMessageReceived.rqLun},
444 seq{ipmbMessageReceived.seq}, address{ipmbMessageReceived.rqSA}](
445 const boost::system::error_code& ec,
446 const IpmiDbusRspType& response) {
447 const auto& [netfn, lun, cmd, cc, payload] = response;
448 if (ec)
449 {
450 phosphor::logging::log<phosphor::logging::level::ERR>(
451 "processI2cEvent: error getting response from IPMI");
452 return;
453 }
454
455 uint8_t bmcTargetAddress = getBmcTargetAddress();
456
457 if (payload.size() > ipmbMaxDataSize)
458 {
459 phosphor::logging::log<phosphor::logging::level::ERR>(
460 "processI2cEvent: response exceeding maximum size");
461
462 // prepare generic response
463 auto ipmbResponse = IpmbResponse(
464 address, netfn, rqLun, bmcTargetAddress, seq, ipmbRsLun,
465 cmd, ipmbIpmiCmdRespNotProvided, {});
466
467 auto buffer = ipmbResponse.ipmbToi2cConstruct();
468 if (buffer)
469 {
470 ipmbSendI2cFrame(buffer);
471 }
472
473 return;
474 }
475
476 if (!(netfn & ipmbNetFnResponseMask))
477 {
478 // we are not expecting request here
479 phosphor::logging::log<phosphor::logging::level::ERR>(
480 "processI2cEvent: got a request instead of response");
481 return;
482 }
483
484 // if command is not supported, add it to filter
485 if (cc == ipmbIpmiInvalidCmd)
486 {
487 addFilter(ipmbReqNetFnFromRespNetFn(netfn), cmd);
488 }
489
490 // payload is empty after constructor invocation
491 auto ipmbResponse =
492 IpmbResponse(address, netfn, rqLun, bmcTargetAddress, seq,
493 lun, cmd, cc, payload);
494
495 auto buffer = ipmbResponse.ipmbToi2cConstruct();
496 if (!buffer)
497 {
498 phosphor::logging::log<phosphor::logging::level::ERR>(
499 "processI2cEvent: error constructing a request");
500 return;
501 }
502
503 ipmbSendI2cFrame(buffer);
504 },
505 "xyz.openbmc_project.Ipmi.Host", "/xyz/openbmc_project/Ipmi",
506 "xyz.openbmc_project.Ipmi.Server", "execute",
507 ipmbMessageReceived.netFn, ipmbMessageReceived.rsLun,
508 ipmbMessageReceived.cmd, ipmbMessageReceived.data, options);
509 }
510
511 end:
512 i2cTargetDescriptor.async_wait(
513 boost::asio::posix::descriptor_base::wait_read,
514 [this](const boost::system::error_code& ec) {
515 if (ec)
516 {
517 phosphor::logging::log<phosphor::logging::level::ERR>(
518 "Error: processI2cEvent()");
519 return;
520 }
521
522 processI2cEvent();
523 });
524 }
525
IpmbChannel(boost::asio::io_context & io,uint8_t ipmbBmcTargetAddress,uint8_t ipmbRqTargetAddress,uint8_t channelIdx,std::shared_ptr<IpmbCommandFilter> commandFilter)526 IpmbChannel::IpmbChannel(boost::asio::io_context& io,
527 uint8_t ipmbBmcTargetAddress,
528 uint8_t ipmbRqTargetAddress, uint8_t channelIdx,
529 std::shared_ptr<IpmbCommandFilter> commandFilter) :
530 i2cTargetDescriptor(io), ipmbBmcTargetAddress(ipmbBmcTargetAddress),
531 ipmbRqTargetAddress(ipmbRqTargetAddress), channelIdx(channelIdx),
532 commandFilter(commandFilter)
533 {}
534
ipmbChannelInit(const char * ipmbI2cTarget)535 int IpmbChannel::ipmbChannelInit(const char* ipmbI2cTarget)
536 {
537 // extract bus id from target path and save
538 std::string ipmbI2cTargetStr(ipmbI2cTarget);
539 auto findHyphen = ipmbI2cTargetStr.find("-");
540 std::string busStr = ipmbI2cTargetStr.substr(findHyphen + 1);
541 try
542 {
543 ipmbBusId = std::stoi(busStr);
544 }
545 catch (const std::invalid_argument&)
546 {
547 phosphor::logging::log<phosphor::logging::level::ERR>(
548 "ipmbChannelInit: invalid bus id in target-path config");
549 return -1;
550 }
551
552 // Check if sysfs has device. If not, enable I2C target driver by command
553 // echo "ipmb-dev 0x1010" > /sys/bus/i2c/devices/i2c-0/new_device
554 bool hasSysfs = std::filesystem::exists(ipmbI2cTarget);
555 if (!hasSysfs)
556 {
557 std::string deviceFileName =
558 "/sys/bus/i2c/devices/i2c-" + busStr + "/new_device";
559 std::string para = "ipmb-dev 0x1010"; // init with BMC addr 0x20
560 std::fstream deviceFile;
561 deviceFile.open(deviceFileName, std::ios::out);
562 if (!deviceFile.good())
563 {
564 phosphor::logging::log<phosphor::logging::level::ERR>(
565 "ipmbChannelInit: error opening deviceFile");
566 return -1;
567 }
568 deviceFile << para;
569 deviceFile.close();
570 }
571
572 // open fd to i2c target device for read write
573 ipmbi2cTargetFd = open(ipmbI2cTarget, O_RDWR | O_NONBLOCK | O_CLOEXEC);
574 if (ipmbi2cTargetFd < 0)
575 {
576 phosphor::logging::log<phosphor::logging::level::ERR>(
577 "ipmbChannelInit: error opening ipmbI2cTarget");
578 return -1;
579 }
580
581 i2cTargetDescriptor.assign(ipmbi2cTargetFd);
582
583 i2cTargetDescriptor.async_wait(
584 boost::asio::posix::descriptor_base::wait_read,
585 [this](const boost::system::error_code& ec) {
586 if (ec)
587 {
588 phosphor::logging::log<phosphor::logging::level::ERR>(
589 "Error: processI2cEvent()");
590 return;
591 }
592
593 processI2cEvent();
594 });
595
596 return 0;
597 }
598
ipmbChannelUpdateTargetAddress(const uint8_t newBmcTargetAddr)599 int IpmbChannel::ipmbChannelUpdateTargetAddress(const uint8_t newBmcTargetAddr)
600 {
601 if (ipmbi2cTargetFd > 0)
602 {
603 i2cTargetDescriptor.close();
604 close(ipmbi2cTargetFd);
605 ipmbi2cTargetFd = 0;
606 }
607
608 // disable old I2C target driver by command:
609 // echo "0x1010" > /sys/bus/i2c/devices/i2c-0/delete_device
610 std::string deviceFileName;
611 std::string para;
612 std::fstream deviceFile;
613 deviceFileName = "/sys/bus/i2c/devices/i2c-" + std::to_string(ipmbBusId) +
614 "/delete_device";
615 para = "0x1010"; // align with removed ipmb0 definition in dts file
616 deviceFile.open(deviceFileName, std::ios::out);
617 if (!deviceFile.good())
618 {
619 phosphor::logging::log<phosphor::logging::level::ERR>(
620 "ipmbChannelUpdateTargetAddress: error opening deviceFile to delete "
621 "sysfs");
622 return -1;
623 }
624 deviceFile << para;
625 deviceFile.close();
626
627 // enable new I2C target driver by command:
628 // echo "ipmb-dev 0x1012" > /sys/bus/i2c/devices/i2c-0/new_device
629 deviceFileName =
630 "/sys/bus/i2c/devices/i2c-" + std::to_string(ipmbBusId) + "/new_device";
631 std::ostringstream hex;
632 uint16_t addr = 0x1000 + (newBmcTargetAddr >> 1);
633 hex << std::hex << static_cast<uint16_t>(addr);
634 const std::string& addressHexStr = hex.str();
635 para = "ipmb-dev 0x" + addressHexStr;
636 deviceFile.open(deviceFileName, std::ios::out);
637 if (!deviceFile.good())
638 {
639 phosphor::logging::log<phosphor::logging::level::ERR>(
640 "ipmbChannelUpdateTargetAddress: error opening deviceFile to create "
641 "sysfs");
642 return -1;
643 }
644 deviceFile << para;
645 deviceFile.close();
646
647 // open fd to i2c target device
648 std::string ipmbI2cTargetStr = "/dev/ipmb-" + std::to_string(ipmbBusId);
649 ipmbi2cTargetFd = open(ipmbI2cTargetStr.c_str(), O_RDWR | O_NONBLOCK);
650 if (ipmbi2cTargetFd < 0)
651 {
652 phosphor::logging::log<phosphor::logging::level::ERR>(
653 "ipmbChannelInit: error opening ipmbI2cTarget");
654 return -1;
655 }
656
657 // start to receive i2c data as target
658 i2cTargetDescriptor.assign(ipmbi2cTargetFd);
659 i2cTargetDescriptor.async_wait(
660 boost::asio::posix::descriptor_base::wait_read,
661 [this](const boost::system::error_code& ec) {
662 if (ec)
663 {
664 phosphor::logging::log<phosphor::logging::level::ERR>(
665 "Error: processI2cEvent()");
666 return;
667 }
668
669 processI2cEvent();
670 });
671
672 ipmbBmcTargetAddress = newBmcTargetAddr;
673
674 return 0;
675 }
676
getBusId()677 uint8_t IpmbChannel::getBusId()
678 {
679 return ipmbBusId;
680 }
681
getBmcTargetAddress()682 uint8_t IpmbChannel::getBmcTargetAddress()
683 {
684 return ipmbBmcTargetAddress;
685 }
686
getRqTargetAddress()687 uint8_t IpmbChannel::getRqTargetAddress()
688 {
689 return ipmbRqTargetAddress;
690 }
691
getDevIndex()692 uint8_t IpmbChannel::getDevIndex()
693 {
694 return channelIdx >> 2;
695 }
696
getChannelIdx()697 uint8_t IpmbChannel::getChannelIdx()
698 {
699 return channelIdx;
700 }
701
getChannelType()702 ipmbChannelType IpmbChannel::getChannelType()
703 {
704 return static_cast<ipmbChannelType>((channelIdx & 3));
705 }
706
addFilter(const uint8_t respNetFn,const uint8_t cmd)707 void IpmbChannel::addFilter(const uint8_t respNetFn, const uint8_t cmd)
708 {
709 if (commandFilter)
710 {
711 commandFilter->addFilter(respNetFn, cmd);
712 }
713 }
714
715 std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
requestAdd(boost::asio::yield_context & yield,std::shared_ptr<IpmbRequest> request)716 IpmbChannel::requestAdd(boost::asio::yield_context& yield,
717 std::shared_ptr<IpmbRequest> request)
718 {
719 makeRequestValid(request);
720
721 std::vector<uint8_t> buffer{};
722 if (request->ipmbToi2cConstruct(buffer) != 0)
723 {
724 return returnStatus(ipmbResponseStatus::error);
725 }
726
727 for (int i = 0; i < ipmbNumberOfTries; i++)
728 {
729 boost::system::error_code ec;
730 int i2cRetryCnt = 0;
731
732 for (; i2cRetryCnt < ipmbI2cNumberOfRetries; i2cRetryCnt++)
733 {
734 boost::asio::async_write(i2cTargetDescriptor,
735 boost::asio::buffer(buffer), yield[ec]);
736
737 if (ec)
738 {
739 continue; // retry
740 }
741 break;
742 }
743
744 if (i2cRetryCnt == ipmbI2cNumberOfRetries)
745 {
746 std::string msgToLog =
747 "requestAdd: Sent to I2C failed after retries."
748 " busId=" +
749 std::to_string(ipmbBusId) + ", error=" + ec.message();
750 phosphor::logging::log<phosphor::logging::level::INFO>(
751 msgToLog.c_str());
752 }
753
754 request->timer->expires_after(
755 std::chrono::milliseconds(ipmbRequestRetryTimeout));
756 request->timer->async_wait(yield[ec]);
757
758 if (ec && ec != boost::asio::error::operation_aborted)
759 {
760 // unexpected error - invalidate request and return generic error
761 phosphor::logging::log<phosphor::logging::level::ERR>(
762 "requestAdd: async_wait error");
763 makeRequestInvalid(*request);
764 return returnStatus(ipmbResponseStatus::error);
765 }
766
767 if (request->state == ipmbRequestState::matched)
768 {
769 // matched response, send it to client application
770 makeRequestInvalid(*request);
771 return request->returnMatchedResponse();
772 }
773 }
774
775 makeRequestInvalid(*request);
776 return returnStatus(ipmbResponseStatus::timeout);
777 }
778
getChannel(uint8_t reqChannel)779 static IpmbChannel* getChannel(uint8_t reqChannel)
780 {
781 auto channel =
782 std::find_if(ipmbChannels.begin(), ipmbChannels.end(),
783 [reqChannel](IpmbChannel& channel) {
784 return channel.getChannelIdx() == reqChannel;
785 });
786 if (channel != ipmbChannels.end())
787 {
788 return &(*channel);
789 }
790
791 return nullptr;
792 }
793
initializeChannels()794 static int initializeChannels()
795 {
796 std::shared_ptr<IpmbCommandFilter> commandFilter =
797 std::make_shared<IpmbCommandFilter>();
798
799 constexpr const char* configFilePath =
800 "/usr/share/ipmbbridge/ipmb-channels.json";
801 std::ifstream configFile(configFilePath);
802 if (!configFile.is_open())
803 {
804 phosphor::logging::log<phosphor::logging::level::ERR>(
805 "initializeChannels: Cannot open config path");
806 return -1;
807 }
808 try
809 {
810 uint8_t devIndex = 0;
811 auto data = nlohmann::json::parse(configFile, nullptr);
812 for (const auto& channelConfig : data["channels"])
813 {
814 const std::string& typeConfig = channelConfig["type"];
815 const std::string& targetPath = channelConfig["slave-path"];
816 uint8_t bmcAddr = channelConfig["bmc-addr"];
817 uint8_t reqAddr = channelConfig["remote-addr"];
818
819 ipmbChannelType type = ipmbChannelTypeMap.at(typeConfig);
820
821 if (channelConfig.contains("devIndex"))
822 {
823 devIndex = channelConfig["devIndex"];
824 }
825
826 auto channel = ipmbChannels.emplace(
827 ipmbChannels.end(), io, bmcAddr, reqAddr,
828 ((devIndex << 2) | static_cast<uint8_t>(type)), commandFilter);
829 if (channel->ipmbChannelInit(targetPath.c_str()) < 0)
830 {
831 phosphor::logging::log<phosphor::logging::level::ERR>(
832 "initializeChannels: channel initialization failed");
833 return -1;
834 }
835 }
836 }
837 catch (const nlohmann::json::exception& e)
838 {
839 phosphor::logging::log<phosphor::logging::level::ERR>(
840 "initializeChannels: Error parsing config file");
841 return -1;
842 }
843 catch (const std::out_of_range& e)
844 {
845 phosphor::logging::log<phosphor::logging::level::ERR>(
846 "initializeChannels: Error invalid type");
847 return -1;
848 }
849 return 0;
850 }
851
852 auto ipmbHandleRequest =
853 [](boost::asio::yield_context yield, uint8_t reqChannel, uint8_t netfn,
__anon377db2e00702(boost::asio::yield_context yield, uint8_t reqChannel, uint8_t netfn, uint8_t lun, uint8_t cmd, std::vector<uint8_t> dataReceived) 854 uint8_t lun, uint8_t cmd, std::vector<uint8_t> dataReceived) {
855 IpmbChannel* channel = getChannel(reqChannel);
856
857 if (channel == nullptr)
858 {
859 phosphor::logging::log<phosphor::logging::level::ERR>(
860 "ipmbHandleRequest: requested channel does not exist");
861 return returnStatus(ipmbResponseStatus::invalid_param);
862 }
863
864 // check outstanding request list for valid sequence number
865 uint8_t seqNum = 0;
866 bool seqValid = channel->seqNumGet(seqNum);
867 if (!seqValid)
868 {
869 phosphor::logging::log<phosphor::logging::level::WARNING>(
870 "ipmbHandleRequest: cannot add more requests to the list");
871 return returnStatus(ipmbResponseStatus::busy);
872 }
873
874 uint8_t bmcTargetAddress = channel->getBmcTargetAddress();
875 uint8_t rqTargetAddress = channel->getRqTargetAddress();
876
877 // construct the request to add it to outstanding request list
878 std::shared_ptr<IpmbRequest> request = std::make_shared<IpmbRequest>(
879 rqTargetAddress, netfn, ipmbRsLun, bmcTargetAddress, seqNum, lun,
880 cmd, dataReceived);
881
882 if (!request->timer)
883 {
884 phosphor::logging::log<phosphor::logging::level::ERR>(
885 "ipmbHandleRequest: timer object does not exist");
886 return returnStatus(ipmbResponseStatus::error);
887 }
888
889 return channel->requestAdd(yield, request);
890 };
891
addUpdateTargetAddrHandler()892 void addUpdateTargetAddrHandler()
893 {
894 // callback to handle dbus signal of updating target addr
895 std::function<void(sdbusplus::message_t&)> updateTargetAddrHandler =
896 [](sdbusplus::message_t& message) {
897 uint8_t reqChannel, busId, targetAddr;
898
899 // valid source of signal, check whether from multi-node manager
900 std::string pathName = message.get_path();
901 if (pathName != "/xyz/openbmc_project/MultiNode/Status")
902 {
903 phosphor::logging::log<phosphor::logging::level::ERR>(
904 "addUpdateTargetAddrHandler: invalid obj path");
905 return;
906 }
907
908 message.read(reqChannel, busId, targetAddr);
909
910 IpmbChannel* channel = getChannel(reqChannel);
911
912 if (channel == nullptr ||
913 channel->getChannelType() != ipmbChannelType::ipmb)
914 {
915 phosphor::logging::log<phosphor::logging::level::ERR>(
916 "addUpdateTargetAddrHandler: invalid channel");
917 return;
918 }
919 if (busId != channel->getBusId())
920 {
921 phosphor::logging::log<phosphor::logging::level::ERR>(
922 "addUpdateTargetAddrHandler: invalid busId");
923 return;
924 }
925 if (channel->getBmcTargetAddress() == targetAddr)
926 {
927 phosphor::logging::log<phosphor::logging::level::INFO>(
928 "addUpdateTargetAddrHandler: channel bmc target addr is "
929 "unchanged, do nothing");
930 return;
931 }
932
933 channel->ipmbChannelUpdateTargetAddress(targetAddr);
934 };
935
936 static auto match = std::make_unique<sdbusplus::bus::match_t>(
937 static_cast<sdbusplus::bus_t&>(*conn),
938 "type='signal',member='updateBmcSlaveAddr',", updateTargetAddrHandler);
939 }
940
addSendBroadcastHandler()941 void addSendBroadcastHandler()
942 {
943 // callback to handle dbus signal of sending broadcast message
944 std::function<void(sdbusplus::message_t&)> sendBroadcastHandler =
945 [](sdbusplus::message_t& message) {
946 uint8_t reqChannel, netFn, lun, cmd;
947 std::vector<uint8_t> dataReceived;
948 message.read(reqChannel, netFn, lun, cmd, dataReceived);
949
950 IpmbChannel* channel = getChannel(reqChannel);
951
952 if (channel == nullptr)
953 {
954 phosphor::logging::log<phosphor::logging::level::ERR>(
955 "addSendBroadcastMsgHandler: requested channel does not "
956 "exist");
957 return;
958 }
959
960 uint8_t bmcTargetAddress = channel->getBmcTargetAddress();
961 uint8_t seqNum = 0; // seqNum is not used in broadcast msg
962 uint8_t targetAddr = broadcastAddress;
963
964 std::shared_ptr<IpmbRequest> request =
965 std::make_shared<IpmbRequest>(targetAddr, netFn, ipmbRsLun,
966 bmcTargetAddress, seqNum, lun,
967 cmd, dataReceived);
968
969 std::shared_ptr<std::vector<uint8_t>> buffer =
970 std::make_shared<std::vector<uint8_t>>();
971
972 if (request->ipmbToi2cConstruct(*buffer) != 0)
973 {
974 return;
975 }
976
977 channel->ipmbSendI2cFrame(buffer);
978 };
979
980 static auto match = std::make_unique<sdbusplus::bus::match_t>(
981 static_cast<sdbusplus::bus_t&>(*conn),
982 "type='signal',member='sendBroadcast',", sendBroadcastHandler);
983 }
984
985 /**
986 * @brief Main
987 */
main()988 int main()
989 {
990 conn->request_name(ipmbBus);
991
992 auto server = sdbusplus::asio::object_server(conn);
993
994 std::shared_ptr<sdbusplus::asio::dbus_interface> ipmbIface =
995 server.add_interface(ipmbObj, ipmbDbusIntf);
996
997 ipmbIface->register_method("sendRequest", std::move(ipmbHandleRequest));
998 ipmbIface->initialize();
999
1000 if (initializeChannels() < 0)
1001 {
1002 phosphor::logging::log<phosphor::logging::level::ERR>(
1003 "Error initializeChannels");
1004 return -1;
1005 }
1006
1007 addUpdateTargetAddrHandler();
1008
1009 addSendBroadcastHandler();
1010
1011 io.run();
1012 return 0;
1013 }
1014