xref: /openbmc/ipmbbridge/ipmbbridged.cpp (revision 9898d612c3f3fbacd08daff934a83bdb2a7c0dd5)
1a642a948SDawid Frycki /* Copyright 2018 Intel
2a642a948SDawid Frycki  *
3a642a948SDawid Frycki  * Licensed under the Apache License, Version 2.0 (the "License");
4a642a948SDawid Frycki  * you may not use this file except in compliance with the License.
5a642a948SDawid Frycki  * You may obtain a copy of the License at
6a642a948SDawid Frycki  *
7a642a948SDawid Frycki  *    http://www.apache.org/licenses/LICENSE-2.0
8a642a948SDawid Frycki  *
9a642a948SDawid Frycki  *  Unless required by applicable law or agreed to in writing, software
10a642a948SDawid Frycki  *  distributed under the License is distributed on an "AS IS" BASIS,
11a642a948SDawid Frycki  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12a642a948SDawid Frycki  *  See the License for the specific language governing permissions and
13a642a948SDawid Frycki  *  limitations under the License.
14a642a948SDawid Frycki  */
15a642a948SDawid Frycki 
16a642a948SDawid Frycki #include "ipmbbridged.hpp"
17a642a948SDawid Frycki 
18a642a948SDawid Frycki #include "ipmbdefines.hpp"
19a642a948SDawid Frycki #include "ipmbutils.hpp"
20a642a948SDawid Frycki 
218edcf1a0SQiang XU #include <boost/algorithm/string/replace.hpp>
221486b8acSEd Tanous #include <boost/asio/io_context.hpp>
2309027c0eSEd Tanous #include <boost/asio/write.hpp>
24fe0d38a0SPatrick Williams #include <nlohmann/json.hpp>
25fe0d38a0SPatrick Williams #include <phosphor-logging/log.hpp>
26fe0d38a0SPatrick Williams 
278edcf1a0SQiang XU #include <filesystem>
28314862d9SAmithash Prasad #include <fstream>
297867cd70SJason M. Bills #include <list>
30a642a948SDawid Frycki #include <tuple>
31314862d9SAmithash Prasad #include <unordered_map>
32a642a948SDawid Frycki 
33a642a948SDawid Frycki /**
34a642a948SDawid Frycki  * @brief Dbus
35a642a948SDawid Frycki  */
36a642a948SDawid Frycki static constexpr const char* ipmbBus = "xyz.openbmc_project.Ipmi.Channel.Ipmb";
37a642a948SDawid Frycki static constexpr const char* ipmbObj = "/xyz/openbmc_project/Ipmi/Channel/Ipmb";
38a642a948SDawid Frycki static constexpr const char* ipmbDbusIntf = "org.openbmc.Ipmb";
39a642a948SDawid Frycki 
401486b8acSEd Tanous boost::asio::io_context io;
41a642a948SDawid Frycki auto conn = std::make_shared<sdbusplus::asio::connection>(io);
42a642a948SDawid Frycki 
43a642a948SDawid Frycki static std::list<IpmbChannel> ipmbChannels;
44314862d9SAmithash Prasad static const std::unordered_map<std::string, ipmbChannelType>
45314862d9SAmithash Prasad     ipmbChannelTypeMap = {{"me", ipmbChannelType::me},
46314862d9SAmithash Prasad                           {"ipmb", ipmbChannelType::ipmb}};
47a642a948SDawid Frycki 
48a642a948SDawid Frycki /**
49a642a948SDawid Frycki  * @brief Ipmb request class methods
50a642a948SDawid Frycki  */
IpmbRequest()51a642a948SDawid Frycki IpmbRequest::IpmbRequest()
52a642a948SDawid Frycki {
53a642a948SDawid Frycki     data.reserve(ipmbMaxDataSize);
54a642a948SDawid Frycki }
55a642a948SDawid Frycki 
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)56a642a948SDawid Frycki IpmbRequest::IpmbRequest(uint8_t address, uint8_t netFn, uint8_t rsLun,
57a642a948SDawid Frycki                          uint8_t rqSA, uint8_t seq, uint8_t rqLun, uint8_t cmd,
588188d765SDawid Frycki                          const std::vector<uint8_t>& inputData) :
59*9898d612SPatrick Williams     address(address), netFn(netFn), rsLun(rsLun), rqSA(rqSA), seq(seq),
60*9898d612SPatrick Williams     rqLun(rqLun), cmd(cmd), timer(io)
61a642a948SDawid Frycki {
62a642a948SDawid Frycki     data.reserve(ipmbMaxDataSize);
63a642a948SDawid Frycki     state = ipmbRequestState::invalid;
64a642a948SDawid Frycki 
65a642a948SDawid Frycki     if (inputData.size() > 0)
66a642a948SDawid Frycki     {
67a642a948SDawid Frycki         data = std::move(inputData);
68a642a948SDawid Frycki     }
69a642a948SDawid Frycki }
70a642a948SDawid Frycki 
i2cToIpmbConstruct(IPMB_HEADER * ipmbBuffer,size_t bufferLength)71a642a948SDawid Frycki void IpmbRequest::i2cToIpmbConstruct(IPMB_HEADER* ipmbBuffer,
72a642a948SDawid Frycki                                      size_t bufferLength)
73a642a948SDawid Frycki {
74a642a948SDawid Frycki     // constructing ipmb request from i2c buffer
75a642a948SDawid Frycki     netFn = ipmbNetFnGet(ipmbBuffer->Header.Req.rsNetFnLUN);
76a642a948SDawid Frycki     rsLun = ipmbLunFromNetFnLunGet(ipmbBuffer->Header.Req.rsNetFnLUN);
77a642a948SDawid Frycki     rqSA = ipmbBuffer->Header.Req.rqSA;
78a642a948SDawid Frycki     seq = ipmbSeqGet(ipmbBuffer->Header.Req.rqSeqLUN);
79a642a948SDawid Frycki     rqLun = ipmbLunFromSeqLunGet(ipmbBuffer->Header.Req.rqSeqLUN);
80a642a948SDawid Frycki     cmd = ipmbBuffer->Header.Req.cmd;
81a642a948SDawid Frycki 
82*9898d612SPatrick Williams     size_t dataLength =
83*9898d612SPatrick Williams         bufferLength - (ipmbConnectionHeaderLength +
84a642a948SDawid Frycki                         ipmbRequestDataHeaderLength + ipmbChecksumSize);
85a642a948SDawid Frycki 
86a642a948SDawid Frycki     if (dataLength > 0)
87a642a948SDawid Frycki     {
88a642a948SDawid Frycki         data.insert(data.end(), ipmbBuffer->Header.Req.data,
89a642a948SDawid Frycki                     &ipmbBuffer->Header.Req.data[dataLength]);
90a642a948SDawid Frycki     }
91a642a948SDawid Frycki }
92a642a948SDawid Frycki 
ipmbToi2cConstruct(std::vector<uint8_t> & buffer)93a642a948SDawid Frycki int IpmbRequest::ipmbToi2cConstruct(std::vector<uint8_t>& buffer)
94a642a948SDawid Frycki {
9537a7eaceSVijay Khemka     /* Add one byte for length byte as per required by driver */
9637a7eaceSVijay Khemka     size_t bufferLength = 1 + data.size() + ipmbRequestDataHeaderLength +
97a642a948SDawid Frycki                           ipmbConnectionHeaderLength + ipmbChecksumSize;
98a642a948SDawid Frycki 
99a642a948SDawid Frycki     if (bufferLength > ipmbMaxFrameLength)
100a642a948SDawid Frycki     {
101a642a948SDawid Frycki         return -1;
102a642a948SDawid Frycki     }
103a642a948SDawid Frycki 
104a642a948SDawid Frycki     buffer.resize(bufferLength);
105a642a948SDawid Frycki     static_assert(ipmbMaxFrameLength >= sizeof(IPMB_HEADER));
10637a7eaceSVijay Khemka     IPMB_PKT* ipmbPkt = reinterpret_cast<IPMB_PKT*>(buffer.data());
10737a7eaceSVijay Khemka     ipmbPkt->len = bufferLength - 1;
10837a7eaceSVijay Khemka     IPMB_HEADER* ipmbBuffer = &(ipmbPkt->hdr);
109a642a948SDawid Frycki 
110a642a948SDawid Frycki     // constructing buffer from ipmb request
111a642a948SDawid Frycki     ipmbBuffer->Header.Req.address = address;
112a642a948SDawid Frycki     ipmbBuffer->Header.Req.rsNetFnLUN = ipmbNetFnLunSet(netFn, rsLun);
113a642a948SDawid Frycki     ipmbBuffer->Header.Req.rqSA = rqSA;
114a642a948SDawid Frycki     ipmbBuffer->Header.Req.rqSeqLUN = ipmbSeqLunSet(seq, rqLun);
115a642a948SDawid Frycki     ipmbBuffer->Header.Req.cmd = cmd;
116a642a948SDawid Frycki 
117bbfd00abSQiang XU     ipmbBuffer->Header.Req.checksum1 = ipmbChecksumCompute(
11837a7eaceSVijay Khemka         (uint8_t*)ipmbBuffer, ipmbConnectionHeaderLength - ipmbChecksumSize);
119a642a948SDawid Frycki 
120a642a948SDawid Frycki     if (data.size() > 0)
121a642a948SDawid Frycki     {
122a642a948SDawid Frycki         std::copy(data.begin(), data.end(), ipmbBuffer->Header.Req.data);
123a642a948SDawid Frycki     }
124a642a948SDawid Frycki 
125a642a948SDawid Frycki     buffer[bufferLength - ipmbChecksumSize] =
12637a7eaceSVijay Khemka         ipmbChecksumCompute((uint8_t*)ipmbBuffer + ipmbChecksum2StartOffset,
127a642a948SDawid Frycki                             (ipmbRequestDataHeaderLength + data.size()));
128a642a948SDawid Frycki 
129a642a948SDawid Frycki     return 0;
130a642a948SDawid Frycki }
131a642a948SDawid Frycki 
132a642a948SDawid Frycki std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
returnMatchedResponse()133a642a948SDawid Frycki     IpmbRequest::returnMatchedResponse()
134a642a948SDawid Frycki {
135a642a948SDawid Frycki     return std::make_tuple(
136a642a948SDawid Frycki         static_cast<int>(ipmbResponseStatus::success), matchedResponse->netFn,
137a642a948SDawid Frycki         matchedResponse->rsLun, matchedResponse->cmd,
138a642a948SDawid Frycki         matchedResponse->completionCode, matchedResponse->data);
139a642a948SDawid Frycki }
140a642a948SDawid Frycki 
141a642a948SDawid Frycki static std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
returnStatus(ipmbResponseStatus status)142a642a948SDawid Frycki     returnStatus(ipmbResponseStatus status)
143a642a948SDawid Frycki {
144a642a948SDawid Frycki     // we only want to send status here, other fields are not relevant
145a642a948SDawid Frycki     return std::make_tuple(static_cast<int>(status), 0, 0, 0, 0,
146a642a948SDawid Frycki                            std::vector<uint8_t>(0));
147a642a948SDawid Frycki }
148a642a948SDawid Frycki 
149a642a948SDawid Frycki /**
150a642a948SDawid Frycki  * @brief Ipmb response class methods
151a642a948SDawid Frycki  */
IpmbResponse()152a642a948SDawid Frycki IpmbResponse::IpmbResponse()
153a642a948SDawid Frycki {
154a642a948SDawid Frycki     data.reserve(ipmbMaxDataSize);
155a642a948SDawid Frycki }
156a642a948SDawid Frycki 
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)157a642a948SDawid Frycki IpmbResponse::IpmbResponse(uint8_t address, uint8_t netFn, uint8_t rqLun,
158a642a948SDawid Frycki                            uint8_t rsSA, uint8_t seq, uint8_t rsLun,
159a642a948SDawid Frycki                            uint8_t cmd, uint8_t completionCode,
1608188d765SDawid Frycki                            const std::vector<uint8_t>& inputData) :
161*9898d612SPatrick Williams     address(address), netFn(netFn), rqLun(rqLun), rsSA(rsSA), seq(seq),
162*9898d612SPatrick Williams     rsLun(rsLun), cmd(cmd), completionCode(completionCode)
163a642a948SDawid Frycki {
164a642a948SDawid Frycki     data.reserve(ipmbMaxDataSize);
165a642a948SDawid Frycki 
166a642a948SDawid Frycki     if (inputData.size() > 0)
167a642a948SDawid Frycki     {
168a642a948SDawid Frycki         data = std::move(inputData);
169a642a948SDawid Frycki     }
170a642a948SDawid Frycki }
171a642a948SDawid Frycki 
i2cToIpmbConstruct(IPMB_HEADER * ipmbBuffer,size_t bufferLength)172a642a948SDawid Frycki void IpmbResponse::i2cToIpmbConstruct(IPMB_HEADER* ipmbBuffer,
173a642a948SDawid Frycki                                       size_t bufferLength)
174a642a948SDawid Frycki {
175a642a948SDawid Frycki     netFn = ipmbNetFnGet(ipmbBuffer->Header.Resp.rqNetFnLUN);
176a642a948SDawid Frycki     rqLun = ipmbLunFromNetFnLunGet(ipmbBuffer->Header.Resp.rqNetFnLUN);
177a642a948SDawid Frycki     rsSA = ipmbBuffer->Header.Resp.rsSA;
178a642a948SDawid Frycki     seq = ipmbSeqGet(ipmbBuffer->Header.Resp.rsSeqLUN);
179a642a948SDawid Frycki     rsLun = ipmbLunFromSeqLunGet(ipmbBuffer->Header.Resp.rsSeqLUN);
180a642a948SDawid Frycki     cmd = ipmbBuffer->Header.Resp.cmd;
181a642a948SDawid Frycki     completionCode = ipmbBuffer->Header.Resp.completionCode;
182a642a948SDawid Frycki 
183*9898d612SPatrick Williams     size_t dataLength =
184*9898d612SPatrick Williams         bufferLength - (ipmbConnectionHeaderLength +
185a642a948SDawid Frycki                         ipmbResponseDataHeaderLength + ipmbChecksumSize);
186a642a948SDawid Frycki 
187a642a948SDawid Frycki     if (dataLength > 0)
188a642a948SDawid Frycki     {
189a642a948SDawid Frycki         data.insert(data.end(), ipmbBuffer->Header.Resp.data,
190a642a948SDawid Frycki                     &ipmbBuffer->Header.Resp.data[dataLength]);
191a642a948SDawid Frycki     }
192a642a948SDawid Frycki }
193a642a948SDawid Frycki 
ipmbToi2cConstruct()1948188d765SDawid Frycki std::shared_ptr<std::vector<uint8_t>> IpmbResponse::ipmbToi2cConstruct()
195a642a948SDawid Frycki {
19637a7eaceSVijay Khemka     /* Add one byte for length byte as per required by driver */
19737a7eaceSVijay Khemka     size_t bufferLength = 1 + data.size() + ipmbResponseDataHeaderLength +
198a642a948SDawid Frycki                           ipmbConnectionHeaderLength + ipmbChecksumSize;
199a642a948SDawid Frycki 
200a642a948SDawid Frycki     if (bufferLength > ipmbMaxFrameLength)
201a642a948SDawid Frycki     {
2028188d765SDawid Frycki         return nullptr;
203a642a948SDawid Frycki     }
204a642a948SDawid Frycki 
2058188d765SDawid Frycki     std::shared_ptr<std::vector<uint8_t>> buffer =
2068188d765SDawid Frycki         std::make_shared<std::vector<uint8_t>>(bufferLength);
2078188d765SDawid Frycki 
20837a7eaceSVijay Khemka     IPMB_PKT* ipmbPkt = reinterpret_cast<IPMB_PKT*>(buffer->data());
20937a7eaceSVijay Khemka     ipmbPkt->len = bufferLength - 1;
21037a7eaceSVijay Khemka     IPMB_HEADER* ipmbBuffer = &(ipmbPkt->hdr);
211a642a948SDawid Frycki 
212a642a948SDawid Frycki     ipmbBuffer->Header.Resp.address = address;
213a642a948SDawid Frycki     ipmbBuffer->Header.Resp.rqNetFnLUN = ipmbNetFnLunSet(netFn, rqLun);
214a642a948SDawid Frycki     ipmbBuffer->Header.Resp.rsSA = rsSA;
215a642a948SDawid Frycki     ipmbBuffer->Header.Resp.rsSeqLUN = ipmbSeqLunSet(seq, rsLun);
216a642a948SDawid Frycki     ipmbBuffer->Header.Resp.cmd = cmd;
217a642a948SDawid Frycki     ipmbBuffer->Header.Resp.completionCode = completionCode;
218a642a948SDawid Frycki 
219a642a948SDawid Frycki     ipmbBuffer->Header.Resp.checksum1 = ipmbChecksumCompute(
22037a7eaceSVijay Khemka         (uint8_t*)ipmbBuffer, ipmbConnectionHeaderLength - ipmbChecksumSize);
221a642a948SDawid Frycki 
222a642a948SDawid Frycki     if (data.size() > 0)
223a642a948SDawid Frycki     {
224a642a948SDawid Frycki         std::copy(data.begin(), data.end(), ipmbBuffer->Header.Resp.data);
225a642a948SDawid Frycki     }
226a642a948SDawid Frycki 
2278188d765SDawid Frycki     (*buffer)[bufferLength - ipmbChecksumSize] =
22837a7eaceSVijay Khemka         ipmbChecksumCompute((uint8_t*)ipmbBuffer + ipmbChecksum2StartOffset,
229a642a948SDawid Frycki                             (ipmbResponseDataHeaderLength + data.size()));
230a642a948SDawid Frycki 
2318188d765SDawid Frycki     return buffer;
232a642a948SDawid Frycki }
233a642a948SDawid Frycki 
isBlocked(const uint8_t reqNetFn,const uint8_t cmd)234a642a948SDawid Frycki bool IpmbCommandFilter::isBlocked(const uint8_t reqNetFn, const uint8_t cmd)
235a642a948SDawid Frycki {
236a642a948SDawid Frycki     auto blockedCmd = unhandledCommands.find({reqNetFn, cmd});
237a642a948SDawid Frycki 
238a642a948SDawid Frycki     if (blockedCmd != unhandledCommands.end())
239a642a948SDawid Frycki     {
240a642a948SDawid Frycki         return true;
241a642a948SDawid Frycki     }
242a642a948SDawid Frycki 
243a642a948SDawid Frycki     return false;
244a642a948SDawid Frycki }
245a642a948SDawid Frycki 
addFilter(const uint8_t reqNetFn,const uint8_t cmd)246a642a948SDawid Frycki void IpmbCommandFilter::addFilter(const uint8_t reqNetFn, const uint8_t cmd)
247a642a948SDawid Frycki {
248a642a948SDawid Frycki     if (unhandledCommands.insert({reqNetFn, cmd}).second)
249a642a948SDawid Frycki     {
250a642a948SDawid Frycki         phosphor::logging::log<phosphor::logging::level::INFO>(
251a642a948SDawid Frycki             "addFilter: added command to filter",
252a642a948SDawid Frycki             phosphor::logging::entry("netFn = %d", reqNetFn),
253a642a948SDawid Frycki             phosphor::logging::entry("cmd = %d", cmd));
254a642a948SDawid Frycki     }
255a642a948SDawid Frycki }
256a642a948SDawid Frycki 
257a642a948SDawid Frycki /**
258a642a948SDawid Frycki  * @brief Ipmb channel
259a642a948SDawid Frycki  */
ipmbSendI2cFrame(std::shared_ptr<std::vector<uint8_t>> buffer,size_t retriesAttempted=0)260bbfd00abSQiang XU void IpmbChannel::ipmbSendI2cFrame(std::shared_ptr<std::vector<uint8_t>> buffer,
261a642a948SDawid Frycki                                    size_t retriesAttempted = 0)
262a642a948SDawid Frycki {
26337a7eaceSVijay Khemka     IPMB_PKT* ipmbPkt = reinterpret_cast<IPMB_PKT*>(buffer->data());
26437a7eaceSVijay Khemka     uint8_t targetAddr = ipmbIsResponse(&(ipmbPkt->hdr))
26537a7eaceSVijay Khemka                              ? ipmbPkt->hdr.Header.Resp.address
26637a7eaceSVijay Khemka                              : ipmbPkt->hdr.Header.Req.address;
267*9898d612SPatrick Williams     boost::asio::async_write(
268*9898d612SPatrick Williams         i2cTargetDescriptor, boost::asio::buffer(*buffer),
269*9898d612SPatrick Williams         [this, buffer, retriesAttempted, targetAddr](
270*9898d612SPatrick Williams             const boost::system::error_code& ec, size_t /* bytesSent */) {
271a642a948SDawid Frycki             if (ec)
272a642a948SDawid Frycki             {
273a642a948SDawid Frycki                 size_t currentRetryCnt = retriesAttempted;
27437a7eaceSVijay Khemka 
275a642a948SDawid Frycki                 if (currentRetryCnt > ipmbI2cNumberOfRetries)
276a642a948SDawid Frycki                 {
277bbfd00abSQiang XU                     std::string msgToLog =
27837a7eaceSVijay Khemka                         "ipmbSendI2cFrame: send to I2C failed after retries."
279bbfd00abSQiang XU                         " busId=" +
280bbfd00abSQiang XU                         std::to_string(ipmbBusId) +
281bbfd00abSQiang XU                         ", targetAddr=" + std::to_string(targetAddr) +
282bbfd00abSQiang XU                         ", error=" + ec.message();
283a642a948SDawid Frycki                     phosphor::logging::log<phosphor::logging::level::ERR>(
284bbfd00abSQiang XU                         msgToLog.c_str());
285a642a948SDawid Frycki                     return;
286a642a948SDawid Frycki                 }
287a642a948SDawid Frycki                 currentRetryCnt++;
288bbfd00abSQiang XU                 ipmbSendI2cFrame(buffer, currentRetryCnt);
289a642a948SDawid Frycki             }
290a642a948SDawid Frycki         });
291a642a948SDawid Frycki }
292a642a948SDawid Frycki 
293a642a948SDawid Frycki /**
294a642a948SDawid Frycki  * @brief Ipmb Outstanding Requests
295a642a948SDawid Frycki  */
makeRequestInvalid(IpmbRequest & request)296a642a948SDawid Frycki void IpmbChannel::makeRequestInvalid(IpmbRequest& request)
297a642a948SDawid Frycki {
298a642a948SDawid Frycki     // change request state to invalid and remove it from outstanding requests
299a642a948SDawid Frycki     // list
300a642a948SDawid Frycki     request.state = ipmbRequestState::invalid;
301a642a948SDawid Frycki     outstandingRequests[request.seq] = nullptr;
302a642a948SDawid Frycki }
303a642a948SDawid Frycki 
makeRequestValid(std::shared_ptr<IpmbRequest> request)304a642a948SDawid Frycki void IpmbChannel::makeRequestValid(std::shared_ptr<IpmbRequest> request)
305a642a948SDawid Frycki {
306a642a948SDawid Frycki     // change request state to valid and add it to outstanding requests list
307a642a948SDawid Frycki     request->state = ipmbRequestState::valid;
308a642a948SDawid Frycki     outstandingRequests[request->seq] = request;
309a642a948SDawid Frycki }
310a642a948SDawid Frycki 
seqNumGet(uint8_t & seq)311a642a948SDawid Frycki bool IpmbChannel::seqNumGet(uint8_t& seq)
312a642a948SDawid Frycki {
313a642a948SDawid Frycki     static uint8_t seqNum = 0;
314a642a948SDawid Frycki 
315a642a948SDawid Frycki     for (int i = 0; i < ipmbMaxOutstandingRequestsCount; i++)
316a642a948SDawid Frycki     {
317e523af33SPatrick Williams         seqNum = (seqNum + 1) % ipmbMaxOutstandingRequestsCount;
318a642a948SDawid Frycki 
319a642a948SDawid Frycki         if (outstandingRequests[seqNum] == nullptr)
320a642a948SDawid Frycki         {
321a642a948SDawid Frycki             seq = seqNum;
322a642a948SDawid Frycki             return true;
323a642a948SDawid Frycki         }
324a642a948SDawid Frycki     }
325a642a948SDawid Frycki 
326a642a948SDawid Frycki     return false;
327a642a948SDawid Frycki }
328a642a948SDawid Frycki 
responseMatch(std::unique_ptr<IpmbResponse> & response)329a642a948SDawid Frycki void IpmbChannel::responseMatch(std::unique_ptr<IpmbResponse>& response)
330a642a948SDawid Frycki {
331a642a948SDawid Frycki     std::shared_ptr<IpmbRequest> request = outstandingRequests[response->seq];
332a642a948SDawid Frycki 
333a642a948SDawid Frycki     if (request != nullptr)
334a642a948SDawid Frycki     {
335a642a948SDawid Frycki         if (((ipmbRespNetFn(request->netFn)) == (response->netFn)) &&
336a642a948SDawid Frycki             ((request->rqLun) == (response->rqLun)) &&
337a642a948SDawid Frycki             ((request->rsLun) == (response->rsLun)) &&
338a642a948SDawid Frycki             ((request->cmd) == (response->cmd)))
339a642a948SDawid Frycki         {
340a642a948SDawid Frycki             // match, response is corresponding to previously sent request
341a642a948SDawid Frycki             request->state = ipmbRequestState::matched;
342a642a948SDawid Frycki             request->timer->cancel();
343a642a948SDawid Frycki             request->matchedResponse = std::move(response);
344a642a948SDawid Frycki         }
345a642a948SDawid Frycki     }
346a642a948SDawid Frycki }
347a642a948SDawid Frycki 
processI2cEvent()348a642a948SDawid Frycki void IpmbChannel::processI2cEvent()
349a642a948SDawid Frycki {
350a642a948SDawid Frycki     std::array<uint8_t, ipmbMaxFrameLength> buffer{};
35137a7eaceSVijay Khemka     IPMB_PKT* ipmbPkt = reinterpret_cast<IPMB_PKT*>(buffer.data());
35237a7eaceSVijay Khemka     IPMB_HEADER* ipmbFrame = &(ipmbPkt->hdr);
353a642a948SDawid Frycki 
3540736e213SMatt Simmering     lseek(ipmbi2cTargetFd, 0, SEEK_SET);
3550736e213SMatt Simmering     ssize_t r = read(ipmbi2cTargetFd, buffer.data(), ipmbMaxFrameLength);
3561f5b24baSPatrick Williams 
3571f5b24baSPatrick Williams     // Handle error cases.
3581f5b24baSPatrick Williams     if (r < 0)
3591f5b24baSPatrick Williams     {
3601f5b24baSPatrick Williams         goto end;
3611f5b24baSPatrick Williams     }
36237a7eaceSVijay Khemka 
363524f753fSManojkiran Eda     /* Subtract first byte len size from total frame length */
36437a7eaceSVijay Khemka     r--;
36537a7eaceSVijay Khemka 
366a642a948SDawid Frycki     if ((r < ipmbMinFrameLength) || (r > ipmbMaxFrameLength))
367a642a948SDawid Frycki     {
368a642a948SDawid Frycki         goto end;
369a642a948SDawid Frycki     }
370a642a948SDawid Frycki 
371524f753fSManojkiran Eda     // validate the frame
372a642a948SDawid Frycki     if (!isFrameValid(ipmbFrame, r))
373a642a948SDawid Frycki     {
374a642a948SDawid Frycki         goto end;
375a642a948SDawid Frycki     }
376a642a948SDawid Frycki 
37715185ff6SChen Yugang     // if it is message received from ipmb channel, send out dbus signal
37815185ff6SChen Yugang     if (getChannelType() == ipmbChannelType::ipmb)
379bbfd00abSQiang XU     {
380bbfd00abSQiang XU         auto ipmbMessageReceived = IpmbRequest();
381bbfd00abSQiang XU         ipmbMessageReceived.i2cToIpmbConstruct(ipmbFrame, r);
382*9898d612SPatrick Williams         sdbusplus::message_t msg =
383*9898d612SPatrick Williams             conn->new_signal(ipmbObj, ipmbDbusIntf, "receiveBroadcast");
384bbfd00abSQiang XU         msg.append(ipmbMessageReceived.netFn, ipmbMessageReceived.cmd,
385bbfd00abSQiang XU                    ipmbMessageReceived.data);
386bbfd00abSQiang XU         msg.signal_send();
387bbfd00abSQiang XU     }
388bbfd00abSQiang XU 
389a642a948SDawid Frycki     // copy frame to ipmib message buffer
3903e07b9eaSChen Yugang     if (ipmbIsResponse(ipmbFrame))
391a642a948SDawid Frycki     {
392a642a948SDawid Frycki         std::unique_ptr<IpmbResponse> ipmbMessageReceived =
393a642a948SDawid Frycki             std::make_unique<IpmbResponse>();
394a642a948SDawid Frycki 
395a642a948SDawid Frycki         ipmbMessageReceived->i2cToIpmbConstruct(ipmbFrame, r);
396a642a948SDawid Frycki 
397a642a948SDawid Frycki         // try to match response with outstanding request
398a642a948SDawid Frycki         responseMatch(ipmbMessageReceived);
399a642a948SDawid Frycki     }
400a642a948SDawid Frycki     else
401a642a948SDawid Frycki     {
402a642a948SDawid Frycki         // if command is blocked - respond with 'invalid command'
403a642a948SDawid Frycki         // completion code
404a642a948SDawid Frycki         if (commandFilter)
405a642a948SDawid Frycki         {
406a642a948SDawid Frycki             uint8_t netFn = ipmbNetFnGet(ipmbFrame->Header.Req.rsNetFnLUN);
407a642a948SDawid Frycki             uint8_t cmd = ipmbFrame->Header.Req.cmd;
408bbfd00abSQiang XU             uint8_t rqSA = ipmbFrame->Header.Req.rqSA;
409a642a948SDawid Frycki 
410a642a948SDawid Frycki             if (commandFilter->isBlocked(netFn, cmd))
411a642a948SDawid Frycki             {
412a642a948SDawid Frycki                 uint8_t seq = ipmbSeqGet(ipmbFrame->Header.Req.rqSeqLUN);
413a642a948SDawid Frycki                 uint8_t lun =
414a642a948SDawid Frycki                     ipmbLunFromSeqLunGet(ipmbFrame->Header.Req.rqSeqLUN);
415a642a948SDawid Frycki 
416a642a948SDawid Frycki                 // prepare generic response
417bbfd00abSQiang XU                 auto ipmbResponse = IpmbResponse(
4180736e213SMatt Simmering                     rqSA, ipmbRespNetFn(netFn), lun, ipmbBmcTargetAddress, seq,
419bbfd00abSQiang XU                     ipmbRsLun, cmd, ipmbIpmiInvalidCmd, {});
420a642a948SDawid Frycki 
4218188d765SDawid Frycki                 auto buffer = ipmbResponse.ipmbToi2cConstruct();
4228188d765SDawid Frycki                 if (buffer)
423a642a948SDawid Frycki                 {
424bbfd00abSQiang XU                     ipmbSendI2cFrame(buffer);
425a642a948SDawid Frycki                 }
426a642a948SDawid Frycki 
427a642a948SDawid Frycki                 goto end;
428a642a948SDawid Frycki             }
429a642a948SDawid Frycki         }
430a642a948SDawid Frycki 
431a642a948SDawid Frycki         auto ipmbMessageReceived = IpmbRequest();
432a642a948SDawid Frycki         ipmbMessageReceived.i2cToIpmbConstruct(ipmbFrame, r);
433a642a948SDawid Frycki 
4348fe0abe6SKumar Thangavel         int devId = getDevIndex();
4358fe0abe6SKumar Thangavel 
4368188d765SDawid Frycki         std::map<std::string, std::variant<int>> options{
4378fe0abe6SKumar Thangavel             {"rqSA", ipmbAddressTo7BitSet(ipmbMessageReceived.rqSA)},
4388fe0abe6SKumar Thangavel             {"hostId", devId}};
4398fe0abe6SKumar Thangavel 
4408188d765SDawid Frycki         using IpmiDbusRspType = std::tuple<uint8_t, uint8_t, uint8_t, uint8_t,
4418188d765SDawid Frycki                                            std::vector<uint8_t>>;
4428188d765SDawid Frycki         conn->async_method_call(
4438188d765SDawid Frycki             [this, rqLun{ipmbMessageReceived.rqLun},
444bbfd00abSQiang XU              seq{ipmbMessageReceived.seq}, address{ipmbMessageReceived.rqSA}](
445bbfd00abSQiang XU                 const boost::system::error_code& ec,
4468188d765SDawid Frycki                 const IpmiDbusRspType& response) {
4478188d765SDawid Frycki                 const auto& [netfn, lun, cmd, cc, payload] = response;
4488188d765SDawid Frycki                 if (ec)
4498188d765SDawid Frycki                 {
4508188d765SDawid Frycki                     phosphor::logging::log<phosphor::logging::level::ERR>(
4518188d765SDawid Frycki                         "processI2cEvent: error getting response from IPMI");
4528188d765SDawid Frycki                     return;
4538188d765SDawid Frycki                 }
454a642a948SDawid Frycki 
4550736e213SMatt Simmering                 uint8_t bmcTargetAddress = getBmcTargetAddress();
4568188d765SDawid Frycki 
4578188d765SDawid Frycki                 if (payload.size() > ipmbMaxDataSize)
4588188d765SDawid Frycki                 {
4598188d765SDawid Frycki                     phosphor::logging::log<phosphor::logging::level::ERR>(
4608188d765SDawid Frycki                         "processI2cEvent: response exceeding maximum size");
4618188d765SDawid Frycki 
4628188d765SDawid Frycki                     // prepare generic response
4638188d765SDawid Frycki                     auto ipmbResponse = IpmbResponse(
4640736e213SMatt Simmering                         address, netfn, rqLun, bmcTargetAddress, seq, ipmbRsLun,
4650736e213SMatt Simmering                         cmd, ipmbIpmiCmdRespNotProvided, {});
4668188d765SDawid Frycki 
4678188d765SDawid Frycki                     auto buffer = ipmbResponse.ipmbToi2cConstruct();
4688188d765SDawid Frycki                     if (buffer)
4698188d765SDawid Frycki                     {
470bbfd00abSQiang XU                         ipmbSendI2cFrame(buffer);
4718188d765SDawid Frycki                     }
4728188d765SDawid Frycki 
4738188d765SDawid Frycki                     return;
4748188d765SDawid Frycki                 }
4758188d765SDawid Frycki 
4768188d765SDawid Frycki                 if (!(netfn & ipmbNetFnResponseMask))
4778188d765SDawid Frycki                 {
4788188d765SDawid Frycki                     // we are not expecting request here
4798188d765SDawid Frycki                     phosphor::logging::log<phosphor::logging::level::ERR>(
4808188d765SDawid Frycki                         "processI2cEvent: got a request instead of response");
4818188d765SDawid Frycki                     return;
4828188d765SDawid Frycki                 }
4838188d765SDawid Frycki 
4848188d765SDawid Frycki                 // if command is not supported, add it to filter
4858188d765SDawid Frycki                 if (cc == ipmbIpmiInvalidCmd)
4868188d765SDawid Frycki                 {
4878188d765SDawid Frycki                     addFilter(ipmbReqNetFnFromRespNetFn(netfn), cmd);
4888188d765SDawid Frycki                 }
4898188d765SDawid Frycki 
4908188d765SDawid Frycki                 // payload is empty after constructor invocation
491*9898d612SPatrick Williams                 auto ipmbResponse =
492*9898d612SPatrick Williams                     IpmbResponse(address, netfn, rqLun, bmcTargetAddress, seq,
493*9898d612SPatrick Williams                                  lun, cmd, cc, payload);
4948188d765SDawid Frycki 
4958188d765SDawid Frycki                 auto buffer = ipmbResponse.ipmbToi2cConstruct();
4968188d765SDawid Frycki                 if (!buffer)
4978188d765SDawid Frycki                 {
4988188d765SDawid Frycki                     phosphor::logging::log<phosphor::logging::level::ERR>(
4998188d765SDawid Frycki                         "processI2cEvent: error constructing a request");
5008188d765SDawid Frycki                     return;
5018188d765SDawid Frycki                 }
5028188d765SDawid Frycki 
503bbfd00abSQiang XU                 ipmbSendI2cFrame(buffer);
5048188d765SDawid Frycki             },
5058188d765SDawid Frycki             "xyz.openbmc_project.Ipmi.Host", "/xyz/openbmc_project/Ipmi",
5068188d765SDawid Frycki             "xyz.openbmc_project.Ipmi.Server", "execute",
5078188d765SDawid Frycki             ipmbMessageReceived.netFn, ipmbMessageReceived.rsLun,
5088188d765SDawid Frycki             ipmbMessageReceived.cmd, ipmbMessageReceived.data, options);
509a642a948SDawid Frycki     }
510a642a948SDawid Frycki 
511a642a948SDawid Frycki end:
5120736e213SMatt Simmering     i2cTargetDescriptor.async_wait(
51337a7eaceSVijay Khemka         boost::asio::posix::descriptor_base::wait_read,
514a642a948SDawid Frycki         [this](const boost::system::error_code& ec) {
515a642a948SDawid Frycki             if (ec)
516a642a948SDawid Frycki             {
517a642a948SDawid Frycki                 phosphor::logging::log<phosphor::logging::level::ERR>(
518a642a948SDawid Frycki                     "Error: processI2cEvent()");
519a642a948SDawid Frycki                 return;
520a642a948SDawid Frycki             }
521a642a948SDawid Frycki 
522a642a948SDawid Frycki             processI2cEvent();
523a642a948SDawid Frycki         });
524a642a948SDawid Frycki }
525a642a948SDawid Frycki 
IpmbChannel(boost::asio::io_context & io,uint8_t ipmbBmcTargetAddress,uint8_t ipmbRqTargetAddress,uint8_t channelIdx,std::shared_ptr<IpmbCommandFilter> commandFilter)5261486b8acSEd Tanous IpmbChannel::IpmbChannel(boost::asio::io_context& io,
5270736e213SMatt Simmering                          uint8_t ipmbBmcTargetAddress,
5280736e213SMatt Simmering                          uint8_t ipmbRqTargetAddress, uint8_t channelIdx,
529a642a948SDawid Frycki                          std::shared_ptr<IpmbCommandFilter> commandFilter) :
530*9898d612SPatrick Williams     i2cTargetDescriptor(io), ipmbBmcTargetAddress(ipmbBmcTargetAddress),
5310736e213SMatt Simmering     ipmbRqTargetAddress(ipmbRqTargetAddress), channelIdx(channelIdx),
532a642a948SDawid Frycki     commandFilter(commandFilter)
533fe0d38a0SPatrick Williams {}
534a642a948SDawid Frycki 
ipmbChannelInit(const char * ipmbI2cTarget)5350736e213SMatt Simmering int IpmbChannel::ipmbChannelInit(const char* ipmbI2cTarget)
536a642a948SDawid Frycki {
5370736e213SMatt Simmering     // extract bus id from target path and save
5380736e213SMatt Simmering     std::string ipmbI2cTargetStr(ipmbI2cTarget);
5390736e213SMatt Simmering     auto findHyphen = ipmbI2cTargetStr.find("-");
5400736e213SMatt Simmering     std::string busStr = ipmbI2cTargetStr.substr(findHyphen + 1);
5418edcf1a0SQiang XU     try
5428edcf1a0SQiang XU     {
5438edcf1a0SQiang XU         ipmbBusId = std::stoi(busStr);
5448edcf1a0SQiang XU     }
545c0dd70d8SPatrick Williams     catch (const std::invalid_argument&)
5468edcf1a0SQiang XU     {
5478edcf1a0SQiang XU         phosphor::logging::log<phosphor::logging::level::ERR>(
5480736e213SMatt Simmering             "ipmbChannelInit: invalid bus id in target-path config");
5498edcf1a0SQiang XU         return -1;
5508edcf1a0SQiang XU     }
5518edcf1a0SQiang XU 
5520736e213SMatt Simmering     // Check if sysfs has device. If not, enable I2C target driver by command
55337a7eaceSVijay Khemka     // echo "ipmb-dev 0x1010" > /sys/bus/i2c/devices/i2c-0/new_device
5540736e213SMatt Simmering     bool hasSysfs = std::filesystem::exists(ipmbI2cTarget);
5558edcf1a0SQiang XU     if (!hasSysfs)
5568edcf1a0SQiang XU     {
557*9898d612SPatrick Williams         std::string deviceFileName =
558*9898d612SPatrick Williams             "/sys/bus/i2c/devices/i2c-" + busStr + "/new_device";
55937a7eaceSVijay Khemka         std::string para = "ipmb-dev 0x1010"; // init with BMC addr 0x20
5608edcf1a0SQiang XU         std::fstream deviceFile;
5618edcf1a0SQiang XU         deviceFile.open(deviceFileName, std::ios::out);
5628edcf1a0SQiang XU         if (!deviceFile.good())
5638edcf1a0SQiang XU         {
5648edcf1a0SQiang XU             phosphor::logging::log<phosphor::logging::level::ERR>(
5658edcf1a0SQiang XU                 "ipmbChannelInit: error opening deviceFile");
5668edcf1a0SQiang XU             return -1;
5678edcf1a0SQiang XU         }
5688edcf1a0SQiang XU         deviceFile << para;
5698edcf1a0SQiang XU         deviceFile.close();
5708edcf1a0SQiang XU     }
5718edcf1a0SQiang XU 
5720736e213SMatt Simmering     // open fd to i2c target device for read write
5730736e213SMatt Simmering     ipmbi2cTargetFd = open(ipmbI2cTarget, O_RDWR | O_NONBLOCK | O_CLOEXEC);
5740736e213SMatt Simmering     if (ipmbi2cTargetFd < 0)
575a642a948SDawid Frycki     {
576a642a948SDawid Frycki         phosphor::logging::log<phosphor::logging::level::ERR>(
5770736e213SMatt Simmering             "ipmbChannelInit: error opening ipmbI2cTarget");
578a642a948SDawid Frycki         return -1;
579a642a948SDawid Frycki     }
580a642a948SDawid Frycki 
5810736e213SMatt Simmering     i2cTargetDescriptor.assign(ipmbi2cTargetFd);
582a642a948SDawid Frycki 
5830736e213SMatt Simmering     i2cTargetDescriptor.async_wait(
58437a7eaceSVijay Khemka         boost::asio::posix::descriptor_base::wait_read,
585a642a948SDawid Frycki         [this](const boost::system::error_code& ec) {
586a642a948SDawid Frycki             if (ec)
587a642a948SDawid Frycki             {
588a642a948SDawid Frycki                 phosphor::logging::log<phosphor::logging::level::ERR>(
589a642a948SDawid Frycki                     "Error: processI2cEvent()");
590a642a948SDawid Frycki                 return;
591a642a948SDawid Frycki             }
592a642a948SDawid Frycki 
593a642a948SDawid Frycki             processI2cEvent();
594a642a948SDawid Frycki         });
595a642a948SDawid Frycki 
596a642a948SDawid Frycki     return 0;
597a642a948SDawid Frycki }
598a642a948SDawid Frycki 
ipmbChannelUpdateTargetAddress(const uint8_t newBmcTargetAddr)5990736e213SMatt Simmering int IpmbChannel::ipmbChannelUpdateTargetAddress(const uint8_t newBmcTargetAddr)
6008edcf1a0SQiang XU {
6010736e213SMatt Simmering     if (ipmbi2cTargetFd > 0)
6028edcf1a0SQiang XU     {
6030736e213SMatt Simmering         i2cTargetDescriptor.close();
6040736e213SMatt Simmering         close(ipmbi2cTargetFd);
6050736e213SMatt Simmering         ipmbi2cTargetFd = 0;
6068edcf1a0SQiang XU     }
6078edcf1a0SQiang XU 
6080736e213SMatt Simmering     // disable old I2C target driver by command:
6098edcf1a0SQiang XU     //     echo "0x1010" > /sys/bus/i2c/devices/i2c-0/delete_device
6108edcf1a0SQiang XU     std::string deviceFileName;
6118edcf1a0SQiang XU     std::string para;
6128edcf1a0SQiang XU     std::fstream deviceFile;
6138edcf1a0SQiang XU     deviceFileName = "/sys/bus/i2c/devices/i2c-" + std::to_string(ipmbBusId) +
6148edcf1a0SQiang XU                      "/delete_device";
6158edcf1a0SQiang XU     para = "0x1010"; // align with removed ipmb0 definition in dts file
6168edcf1a0SQiang XU     deviceFile.open(deviceFileName, std::ios::out);
6178edcf1a0SQiang XU     if (!deviceFile.good())
6188edcf1a0SQiang XU     {
6198edcf1a0SQiang XU         phosphor::logging::log<phosphor::logging::level::ERR>(
6200736e213SMatt Simmering             "ipmbChannelUpdateTargetAddress: error opening deviceFile to delete "
6218edcf1a0SQiang XU             "sysfs");
6228edcf1a0SQiang XU         return -1;
6238edcf1a0SQiang XU     }
6248edcf1a0SQiang XU     deviceFile << para;
6258edcf1a0SQiang XU     deviceFile.close();
6268edcf1a0SQiang XU 
6270736e213SMatt Simmering     // enable new I2C target driver by command:
62837a7eaceSVijay Khemka     //      echo "ipmb-dev 0x1012" > /sys/bus/i2c/devices/i2c-0/new_device
629*9898d612SPatrick Williams     deviceFileName =
630*9898d612SPatrick Williams         "/sys/bus/i2c/devices/i2c-" + std::to_string(ipmbBusId) + "/new_device";
6318edcf1a0SQiang XU     std::ostringstream hex;
6320736e213SMatt Simmering     uint16_t addr = 0x1000 + (newBmcTargetAddr >> 1);
6338edcf1a0SQiang XU     hex << std::hex << static_cast<uint16_t>(addr);
6348edcf1a0SQiang XU     const std::string& addressHexStr = hex.str();
63537a7eaceSVijay Khemka     para = "ipmb-dev 0x" + addressHexStr;
6368edcf1a0SQiang XU     deviceFile.open(deviceFileName, std::ios::out);
6378edcf1a0SQiang XU     if (!deviceFile.good())
6388edcf1a0SQiang XU     {
6398edcf1a0SQiang XU         phosphor::logging::log<phosphor::logging::level::ERR>(
6400736e213SMatt Simmering             "ipmbChannelUpdateTargetAddress: error opening deviceFile to create "
6418edcf1a0SQiang XU             "sysfs");
6428edcf1a0SQiang XU         return -1;
6438edcf1a0SQiang XU     }
6448edcf1a0SQiang XU     deviceFile << para;
6458edcf1a0SQiang XU     deviceFile.close();
6468edcf1a0SQiang XU 
6470736e213SMatt Simmering     // open fd to i2c target device
6480736e213SMatt Simmering     std::string ipmbI2cTargetStr = "/dev/ipmb-" + std::to_string(ipmbBusId);
6490736e213SMatt Simmering     ipmbi2cTargetFd = open(ipmbI2cTargetStr.c_str(), O_RDWR | O_NONBLOCK);
6500736e213SMatt Simmering     if (ipmbi2cTargetFd < 0)
6518edcf1a0SQiang XU     {
6528edcf1a0SQiang XU         phosphor::logging::log<phosphor::logging::level::ERR>(
6530736e213SMatt Simmering             "ipmbChannelInit: error opening ipmbI2cTarget");
6548edcf1a0SQiang XU         return -1;
6558edcf1a0SQiang XU     }
6568edcf1a0SQiang XU 
6570736e213SMatt Simmering     // start to receive i2c data as target
6580736e213SMatt Simmering     i2cTargetDescriptor.assign(ipmbi2cTargetFd);
6590736e213SMatt Simmering     i2cTargetDescriptor.async_wait(
66037a7eaceSVijay Khemka         boost::asio::posix::descriptor_base::wait_read,
6618edcf1a0SQiang XU         [this](const boost::system::error_code& ec) {
6628edcf1a0SQiang XU             if (ec)
6638edcf1a0SQiang XU             {
6648edcf1a0SQiang XU                 phosphor::logging::log<phosphor::logging::level::ERR>(
6658edcf1a0SQiang XU                     "Error: processI2cEvent()");
6668edcf1a0SQiang XU                 return;
6678edcf1a0SQiang XU             }
6688edcf1a0SQiang XU 
6698edcf1a0SQiang XU             processI2cEvent();
6708edcf1a0SQiang XU         });
6718edcf1a0SQiang XU 
6720736e213SMatt Simmering     ipmbBmcTargetAddress = newBmcTargetAddr;
673bbfd00abSQiang XU 
6748edcf1a0SQiang XU     return 0;
6758edcf1a0SQiang XU }
6768edcf1a0SQiang XU 
getBusId()6778edcf1a0SQiang XU uint8_t IpmbChannel::getBusId()
6788edcf1a0SQiang XU {
6798edcf1a0SQiang XU     return ipmbBusId;
6808edcf1a0SQiang XU }
6818edcf1a0SQiang XU 
getBmcTargetAddress()6820736e213SMatt Simmering uint8_t IpmbChannel::getBmcTargetAddress()
683a642a948SDawid Frycki {
6840736e213SMatt Simmering     return ipmbBmcTargetAddress;
685a642a948SDawid Frycki }
686a642a948SDawid Frycki 
getRqTargetAddress()6870736e213SMatt Simmering uint8_t IpmbChannel::getRqTargetAddress()
688a642a948SDawid Frycki {
6890736e213SMatt Simmering     return ipmbRqTargetAddress;
690a642a948SDawid Frycki }
691a642a948SDawid Frycki 
getDevIndex()692950a2e81SKumar Thangavel uint8_t IpmbChannel::getDevIndex()
693950a2e81SKumar Thangavel {
694950a2e81SKumar Thangavel     return channelIdx >> 2;
695950a2e81SKumar Thangavel }
696950a2e81SKumar Thangavel 
getChannelIdx()697950a2e81SKumar Thangavel uint8_t IpmbChannel::getChannelIdx()
698950a2e81SKumar Thangavel {
699950a2e81SKumar Thangavel     return channelIdx;
700950a2e81SKumar Thangavel }
701950a2e81SKumar Thangavel 
getChannelType()702a642a948SDawid Frycki ipmbChannelType IpmbChannel::getChannelType()
703a642a948SDawid Frycki {
704950a2e81SKumar Thangavel     return static_cast<ipmbChannelType>((channelIdx & 3));
705a642a948SDawid Frycki }
706a642a948SDawid Frycki 
addFilter(const uint8_t respNetFn,const uint8_t cmd)707a642a948SDawid Frycki void IpmbChannel::addFilter(const uint8_t respNetFn, const uint8_t cmd)
708a642a948SDawid Frycki {
709a642a948SDawid Frycki     if (commandFilter)
710a642a948SDawid Frycki     {
711a642a948SDawid Frycki         commandFilter->addFilter(respNetFn, cmd);
712a642a948SDawid Frycki     }
713a642a948SDawid Frycki }
714a642a948SDawid Frycki 
715a642a948SDawid Frycki 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)716a642a948SDawid Frycki     IpmbChannel::requestAdd(boost::asio::yield_context& yield,
717a642a948SDawid Frycki                             std::shared_ptr<IpmbRequest> request)
718a642a948SDawid Frycki {
719a642a948SDawid Frycki     makeRequestValid(request);
720a642a948SDawid Frycki 
7219ae88935SPatrick Williams     std::vector<uint8_t> buffer{};
722a642a948SDawid Frycki     if (request->ipmbToi2cConstruct(buffer) != 0)
723a642a948SDawid Frycki     {
724a642a948SDawid Frycki         return returnStatus(ipmbResponseStatus::error);
725a642a948SDawid Frycki     }
726a642a948SDawid Frycki 
727a642a948SDawid Frycki     for (int i = 0; i < ipmbNumberOfTries; i++)
728a642a948SDawid Frycki     {
729a642a948SDawid Frycki         boost::system::error_code ec;
73025e85c79SJae Hyun Yoo         int i2cRetryCnt = 0;
731a642a948SDawid Frycki 
73225e85c79SJae Hyun Yoo         for (; i2cRetryCnt < ipmbI2cNumberOfRetries; i2cRetryCnt++)
733a642a948SDawid Frycki         {
7340736e213SMatt Simmering             boost::asio::async_write(i2cTargetDescriptor,
73537a7eaceSVijay Khemka                                      boost::asio::buffer(buffer), yield[ec]);
736a642a948SDawid Frycki 
737a642a948SDawid Frycki             if (ec)
738a642a948SDawid Frycki             {
73925e85c79SJae Hyun Yoo                 continue; // retry
740a642a948SDawid Frycki             }
741a642a948SDawid Frycki             break;
742a642a948SDawid Frycki         }
743a642a948SDawid Frycki 
74425e85c79SJae Hyun Yoo         if (i2cRetryCnt == ipmbI2cNumberOfRetries)
74525e85c79SJae Hyun Yoo         {
746bbfd00abSQiang XU             std::string msgToLog =
747bbfd00abSQiang XU                 "requestAdd: Sent to I2C failed after retries."
748bbfd00abSQiang XU                 " busId=" +
749bbfd00abSQiang XU                 std::to_string(ipmbBusId) + ", error=" + ec.message();
75025e85c79SJae Hyun Yoo             phosphor::logging::log<phosphor::logging::level::INFO>(
751bbfd00abSQiang XU                 msgToLog.c_str());
75225e85c79SJae Hyun Yoo         }
75325e85c79SJae Hyun Yoo 
754a642a948SDawid Frycki         request->timer->expires_after(
755a642a948SDawid Frycki             std::chrono::milliseconds(ipmbRequestRetryTimeout));
756a642a948SDawid Frycki         request->timer->async_wait(yield[ec]);
757a642a948SDawid Frycki 
758a642a948SDawid Frycki         if (ec && ec != boost::asio::error::operation_aborted)
759a642a948SDawid Frycki         {
760a642a948SDawid Frycki             // unexpected error - invalidate request and return generic error
761a642a948SDawid Frycki             phosphor::logging::log<phosphor::logging::level::ERR>(
762a642a948SDawid Frycki                 "requestAdd: async_wait error");
763a642a948SDawid Frycki             makeRequestInvalid(*request);
764a642a948SDawid Frycki             return returnStatus(ipmbResponseStatus::error);
765a642a948SDawid Frycki         }
766a642a948SDawid Frycki 
767a642a948SDawid Frycki         if (request->state == ipmbRequestState::matched)
768a642a948SDawid Frycki         {
769a642a948SDawid Frycki             // matched response, send it to client application
770a642a948SDawid Frycki             makeRequestInvalid(*request);
771a642a948SDawid Frycki             return request->returnMatchedResponse();
772a642a948SDawid Frycki         }
773a642a948SDawid Frycki     }
774a642a948SDawid Frycki 
775a642a948SDawid Frycki     makeRequestInvalid(*request);
776a642a948SDawid Frycki     return returnStatus(ipmbResponseStatus::timeout);
777a642a948SDawid Frycki }
778a642a948SDawid Frycki 
getChannel(uint8_t reqChannel)779950a2e81SKumar Thangavel static IpmbChannel* getChannel(uint8_t reqChannel)
780a642a948SDawid Frycki {
781*9898d612SPatrick Williams     auto channel =
782*9898d612SPatrick Williams         std::find_if(ipmbChannels.begin(), ipmbChannels.end(),
783950a2e81SKumar Thangavel                      [reqChannel](IpmbChannel& channel) {
784950a2e81SKumar Thangavel                          return channel.getChannelIdx() == reqChannel;
785a642a948SDawid Frycki                      });
786a642a948SDawid Frycki     if (channel != ipmbChannels.end())
787a642a948SDawid Frycki     {
788a642a948SDawid Frycki         return &(*channel);
789a642a948SDawid Frycki     }
790a642a948SDawid Frycki 
791a642a948SDawid Frycki     return nullptr;
792a642a948SDawid Frycki }
793a642a948SDawid Frycki 
initializeChannels()794a642a948SDawid Frycki static int initializeChannels()
795a642a948SDawid Frycki {
796a642a948SDawid Frycki     std::shared_ptr<IpmbCommandFilter> commandFilter =
797a642a948SDawid Frycki         std::make_shared<IpmbCommandFilter>();
798a642a948SDawid Frycki 
799314862d9SAmithash Prasad     constexpr const char* configFilePath =
800314862d9SAmithash Prasad         "/usr/share/ipmbbridge/ipmb-channels.json";
801314862d9SAmithash Prasad     std::ifstream configFile(configFilePath);
802314862d9SAmithash Prasad     if (!configFile.is_open())
803a642a948SDawid Frycki     {
804314862d9SAmithash Prasad         phosphor::logging::log<phosphor::logging::level::ERR>(
805314862d9SAmithash Prasad             "initializeChannels: Cannot open config path");
806314862d9SAmithash Prasad         return -1;
807314862d9SAmithash Prasad     }
808314862d9SAmithash Prasad     try
809314862d9SAmithash Prasad     {
810950a2e81SKumar Thangavel         uint8_t devIndex = 0;
811314862d9SAmithash Prasad         auto data = nlohmann::json::parse(configFile, nullptr);
812314862d9SAmithash Prasad         for (const auto& channelConfig : data["channels"])
813314862d9SAmithash Prasad         {
814314862d9SAmithash Prasad             const std::string& typeConfig = channelConfig["type"];
8150736e213SMatt Simmering             const std::string& targetPath = channelConfig["slave-path"];
816314862d9SAmithash Prasad             uint8_t bmcAddr = channelConfig["bmc-addr"];
817314862d9SAmithash Prasad             uint8_t reqAddr = channelConfig["remote-addr"];
818950a2e81SKumar Thangavel 
819314862d9SAmithash Prasad             ipmbChannelType type = ipmbChannelTypeMap.at(typeConfig);
820a642a948SDawid Frycki 
821950a2e81SKumar Thangavel             if (channelConfig.contains("devIndex"))
822950a2e81SKumar Thangavel             {
823950a2e81SKumar Thangavel                 devIndex = channelConfig["devIndex"];
824950a2e81SKumar Thangavel             }
825950a2e81SKumar Thangavel 
826950a2e81SKumar Thangavel             auto channel = ipmbChannels.emplace(
827950a2e81SKumar Thangavel                 ipmbChannels.end(), io, bmcAddr, reqAddr,
828950a2e81SKumar Thangavel                 ((devIndex << 2) | static_cast<uint8_t>(type)), commandFilter);
8290736e213SMatt Simmering             if (channel->ipmbChannelInit(targetPath.c_str()) < 0)
830a642a948SDawid Frycki             {
831a642a948SDawid Frycki                 phosphor::logging::log<phosphor::logging::level::ERR>(
832a642a948SDawid Frycki                     "initializeChannels: channel initialization failed");
833a642a948SDawid Frycki                 return -1;
834a642a948SDawid Frycki             }
835a642a948SDawid Frycki         }
836314862d9SAmithash Prasad     }
837c0dd70d8SPatrick Williams     catch (const nlohmann::json::exception& e)
838314862d9SAmithash Prasad     {
839314862d9SAmithash Prasad         phosphor::logging::log<phosphor::logging::level::ERR>(
840314862d9SAmithash Prasad             "initializeChannels: Error parsing config file");
841314862d9SAmithash Prasad         return -1;
842314862d9SAmithash Prasad     }
843c0dd70d8SPatrick Williams     catch (const std::out_of_range& e)
844314862d9SAmithash Prasad     {
845314862d9SAmithash Prasad         phosphor::logging::log<phosphor::logging::level::ERR>(
846314862d9SAmithash Prasad             "initializeChannels: Error invalid type");
847314862d9SAmithash Prasad         return -1;
848314862d9SAmithash Prasad     }
849a642a948SDawid Frycki     return 0;
850a642a948SDawid Frycki }
851a642a948SDawid Frycki 
852*9898d612SPatrick Williams auto ipmbHandleRequest =
853*9898d612SPatrick Williams     [](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*9898d612SPatrick Williams        uint8_t lun, uint8_t cmd, std::vector<uint8_t> dataReceived) {
855950a2e81SKumar Thangavel         IpmbChannel* channel = getChannel(reqChannel);
856950a2e81SKumar Thangavel 
857a642a948SDawid Frycki         if (channel == nullptr)
858a642a948SDawid Frycki         {
859a642a948SDawid Frycki             phosphor::logging::log<phosphor::logging::level::ERR>(
860a642a948SDawid Frycki                 "ipmbHandleRequest: requested channel does not exist");
861a642a948SDawid Frycki             return returnStatus(ipmbResponseStatus::invalid_param);
862a642a948SDawid Frycki         }
863a642a948SDawid Frycki 
864a642a948SDawid Frycki         // check outstanding request list for valid sequence number
865a642a948SDawid Frycki         uint8_t seqNum = 0;
866a642a948SDawid Frycki         bool seqValid = channel->seqNumGet(seqNum);
867a642a948SDawid Frycki         if (!seqValid)
868a642a948SDawid Frycki         {
869a642a948SDawid Frycki             phosphor::logging::log<phosphor::logging::level::WARNING>(
870a642a948SDawid Frycki                 "ipmbHandleRequest: cannot add more requests to the list");
871a642a948SDawid Frycki             return returnStatus(ipmbResponseStatus::busy);
872a642a948SDawid Frycki         }
873a642a948SDawid Frycki 
8740736e213SMatt Simmering         uint8_t bmcTargetAddress = channel->getBmcTargetAddress();
8750736e213SMatt Simmering         uint8_t rqTargetAddress = channel->getRqTargetAddress();
876a642a948SDawid Frycki 
877a642a948SDawid Frycki         // construct the request to add it to outstanding request list
878a642a948SDawid Frycki         std::shared_ptr<IpmbRequest> request = std::make_shared<IpmbRequest>(
879*9898d612SPatrick Williams             rqTargetAddress, netfn, ipmbRsLun, bmcTargetAddress, seqNum, lun,
880*9898d612SPatrick Williams             cmd, dataReceived);
881a642a948SDawid Frycki 
882a642a948SDawid Frycki         if (!request->timer)
883a642a948SDawid Frycki         {
884a642a948SDawid Frycki             phosphor::logging::log<phosphor::logging::level::ERR>(
885a642a948SDawid Frycki                 "ipmbHandleRequest: timer object does not exist");
886a642a948SDawid Frycki             return returnStatus(ipmbResponseStatus::error);
887a642a948SDawid Frycki         }
888a642a948SDawid Frycki 
889a642a948SDawid Frycki         return channel->requestAdd(yield, request);
890a642a948SDawid Frycki     };
891a642a948SDawid Frycki 
addUpdateTargetAddrHandler()8920736e213SMatt Simmering void addUpdateTargetAddrHandler()
8938edcf1a0SQiang XU {
8940736e213SMatt Simmering     // callback to handle dbus signal of updating target addr
8950736e213SMatt Simmering     std::function<void(sdbusplus::message_t&)> updateTargetAddrHandler =
8963852f8ecSPatrick Williams         [](sdbusplus::message_t& message) {
8970736e213SMatt Simmering             uint8_t reqChannel, busId, targetAddr;
8988edcf1a0SQiang XU 
8998edcf1a0SQiang XU             // valid source of signal, check whether from multi-node manager
9008edcf1a0SQiang XU             std::string pathName = message.get_path();
9018edcf1a0SQiang XU             if (pathName != "/xyz/openbmc_project/MultiNode/Status")
9028edcf1a0SQiang XU             {
9038edcf1a0SQiang XU                 phosphor::logging::log<phosphor::logging::level::ERR>(
9040736e213SMatt Simmering                     "addUpdateTargetAddrHandler: invalid obj path");
9058edcf1a0SQiang XU                 return;
9068edcf1a0SQiang XU             }
9078edcf1a0SQiang XU 
9080736e213SMatt Simmering             message.read(reqChannel, busId, targetAddr);
9098edcf1a0SQiang XU 
910950a2e81SKumar Thangavel             IpmbChannel* channel = getChannel(reqChannel);
911950a2e81SKumar Thangavel 
9128edcf1a0SQiang XU             if (channel == nullptr ||
913950a2e81SKumar Thangavel                 channel->getChannelType() != ipmbChannelType::ipmb)
9148edcf1a0SQiang XU             {
9158edcf1a0SQiang XU                 phosphor::logging::log<phosphor::logging::level::ERR>(
9160736e213SMatt Simmering                     "addUpdateTargetAddrHandler: invalid channel");
9178edcf1a0SQiang XU                 return;
9188edcf1a0SQiang XU             }
9198edcf1a0SQiang XU             if (busId != channel->getBusId())
9208edcf1a0SQiang XU             {
9218edcf1a0SQiang XU                 phosphor::logging::log<phosphor::logging::level::ERR>(
9220736e213SMatt Simmering                     "addUpdateTargetAddrHandler: invalid busId");
9238edcf1a0SQiang XU                 return;
9248edcf1a0SQiang XU             }
9250736e213SMatt Simmering             if (channel->getBmcTargetAddress() == targetAddr)
9268edcf1a0SQiang XU             {
9278edcf1a0SQiang XU                 phosphor::logging::log<phosphor::logging::level::INFO>(
9280736e213SMatt Simmering                     "addUpdateTargetAddrHandler: channel bmc target addr is "
9298edcf1a0SQiang XU                     "unchanged, do nothing");
9308edcf1a0SQiang XU                 return;
9318edcf1a0SQiang XU             }
9328edcf1a0SQiang XU 
9330736e213SMatt Simmering             channel->ipmbChannelUpdateTargetAddress(targetAddr);
9348edcf1a0SQiang XU         };
9358edcf1a0SQiang XU 
9363852f8ecSPatrick Williams     static auto match = std::make_unique<sdbusplus::bus::match_t>(
9373852f8ecSPatrick Williams         static_cast<sdbusplus::bus_t&>(*conn),
9380736e213SMatt Simmering         "type='signal',member='updateBmcSlaveAddr',", updateTargetAddrHandler);
9398edcf1a0SQiang XU }
9408edcf1a0SQiang XU 
addSendBroadcastHandler()941bbfd00abSQiang XU void addSendBroadcastHandler()
942bbfd00abSQiang XU {
943bbfd00abSQiang XU     // callback to handle dbus signal of sending broadcast message
9443852f8ecSPatrick Williams     std::function<void(sdbusplus::message_t&)> sendBroadcastHandler =
9453852f8ecSPatrick Williams         [](sdbusplus::message_t& message) {
946bbfd00abSQiang XU             uint8_t reqChannel, netFn, lun, cmd;
947bbfd00abSQiang XU             std::vector<uint8_t> dataReceived;
948bbfd00abSQiang XU             message.read(reqChannel, netFn, lun, cmd, dataReceived);
949bbfd00abSQiang XU 
950950a2e81SKumar Thangavel             IpmbChannel* channel = getChannel(reqChannel);
951950a2e81SKumar Thangavel 
952bbfd00abSQiang XU             if (channel == nullptr)
953bbfd00abSQiang XU             {
954bbfd00abSQiang XU                 phosphor::logging::log<phosphor::logging::level::ERR>(
955bbfd00abSQiang XU                     "addSendBroadcastMsgHandler: requested channel does not "
956bbfd00abSQiang XU                     "exist");
957bbfd00abSQiang XU                 return;
958bbfd00abSQiang XU             }
959bbfd00abSQiang XU 
9600736e213SMatt Simmering             uint8_t bmcTargetAddress = channel->getBmcTargetAddress();
961bbfd00abSQiang XU             uint8_t seqNum = 0; // seqNum is not used in broadcast msg
962bbfd00abSQiang XU             uint8_t targetAddr = broadcastAddress;
963bbfd00abSQiang XU 
964*9898d612SPatrick Williams             std::shared_ptr<IpmbRequest> request =
965*9898d612SPatrick Williams                 std::make_shared<IpmbRequest>(targetAddr, netFn, ipmbRsLun,
966*9898d612SPatrick Williams                                               bmcTargetAddress, seqNum, lun,
967*9898d612SPatrick Williams                                               cmd, dataReceived);
968bbfd00abSQiang XU 
969bbfd00abSQiang XU             std::shared_ptr<std::vector<uint8_t>> buffer =
970bbfd00abSQiang XU                 std::make_shared<std::vector<uint8_t>>();
971bbfd00abSQiang XU 
972bbfd00abSQiang XU             if (request->ipmbToi2cConstruct(*buffer) != 0)
973bbfd00abSQiang XU             {
974bbfd00abSQiang XU                 return;
975bbfd00abSQiang XU             }
976bbfd00abSQiang XU 
977bbfd00abSQiang XU             channel->ipmbSendI2cFrame(buffer);
978bbfd00abSQiang XU         };
979bbfd00abSQiang XU 
9803852f8ecSPatrick Williams     static auto match = std::make_unique<sdbusplus::bus::match_t>(
9813852f8ecSPatrick Williams         static_cast<sdbusplus::bus_t&>(*conn),
982bbfd00abSQiang XU         "type='signal',member='sendBroadcast',", sendBroadcastHandler);
983bbfd00abSQiang XU }
984bbfd00abSQiang XU 
985a642a948SDawid Frycki /**
986a642a948SDawid Frycki  * @brief Main
987a642a948SDawid Frycki  */
main()988825ad836SPatrick Williams int main()
989a642a948SDawid Frycki {
990a642a948SDawid Frycki     conn->request_name(ipmbBus);
991a642a948SDawid Frycki 
992a642a948SDawid Frycki     auto server = sdbusplus::asio::object_server(conn);
993a642a948SDawid Frycki 
994a642a948SDawid Frycki     std::shared_ptr<sdbusplus::asio::dbus_interface> ipmbIface =
995a642a948SDawid Frycki         server.add_interface(ipmbObj, ipmbDbusIntf);
996a642a948SDawid Frycki 
997a642a948SDawid Frycki     ipmbIface->register_method("sendRequest", std::move(ipmbHandleRequest));
998a642a948SDawid Frycki     ipmbIface->initialize();
999a642a948SDawid Frycki 
1000a642a948SDawid Frycki     if (initializeChannels() < 0)
1001a642a948SDawid Frycki     {
1002a642a948SDawid Frycki         phosphor::logging::log<phosphor::logging::level::ERR>(
1003a642a948SDawid Frycki             "Error initializeChannels");
1004a642a948SDawid Frycki         return -1;
1005a642a948SDawid Frycki     }
1006a642a948SDawid Frycki 
10070736e213SMatt Simmering     addUpdateTargetAddrHandler();
10088edcf1a0SQiang XU 
1009bbfd00abSQiang XU     addSendBroadcastHandler();
1010bbfd00abSQiang XU 
1011a642a948SDawid Frycki     io.run();
1012a642a948SDawid Frycki     return 0;
1013a642a948SDawid Frycki }
1014