blob: dd1533c7245c27813e7772f4f264f74d700e5823 [file] [log] [blame]
Dung Caofaf6a6a2020-12-28 04:44:45 +00001/*
2 * Copyright (c) 2021 Ampere Computing LLC
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * This is a daemon that forwards requests and receive responses from SSIF over
17 * the D-Bus IPMI Interface.
18 */
19
20#include <getopt.h>
21#include <linux/ipmi_bmc.h>
22
23#include <CLI/CLI.hpp>
24#include <boost/algorithm/string/replace.hpp>
Ed Tanousd289aea2024-02-06 20:33:26 -080025#include <boost/asio/completion_condition.hpp>
26#include <boost/asio/io_context.hpp>
27#include <boost/asio/read.hpp>
28#include <boost/asio/steady_timer.hpp>
29#include <boost/asio/write.hpp>
Dung Caofaf6a6a2020-12-28 04:44:45 +000030#include <phosphor-logging/log.hpp>
31#include <sdbusplus/asio/connection.hpp>
32#include <sdbusplus/asio/object_server.hpp>
Patrick Williams8b050c92023-10-20 11:19:32 -050033#include <sdbusplus/timer.hpp>
Dung Caofaf6a6a2020-12-28 04:44:45 +000034
35#include <iostream>
36
Dung Cao51835392022-10-27 04:29:54 +000037/* Max length of ipmi ssif message included netfn and cmd field */
Thang Q. Nguyen4300d212023-05-15 12:22:53 +070038#define IPMI_SSIF_PAYLOAD_MAX 254
Dung Cao51835392022-10-27 04:29:54 +000039
Dung Caofaf6a6a2020-12-28 04:44:45 +000040using namespace phosphor::logging;
41
Patrick Williams8b050c92023-10-20 11:19:32 -050042struct ipmi_cmd
43{
44 uint8_t netfn;
45 uint8_t lun;
46 uint8_t cmd;
quang.ampere14480ee2022-10-18 09:04:51 +070047} prev_req_cmd;
48
Dung Caofaf6a6a2020-12-28 04:44:45 +000049static constexpr const char devBase[] = "/dev/ipmi-ssif-host";
50/* SSIF use IPMI SSIF channel */
Patrick Williams8b050c92023-10-20 11:19:32 -050051static constexpr const char* ssifBus =
52 "xyz.openbmc_project.Ipmi.Channel.ipmi_ssif";
53static constexpr const char* ssifObj =
54 "/xyz/openbmc_project/Ipmi/Channel/ipmi_ssif";
Quang Nguyenf2994dc2022-11-25 16:31:49 +070055/* The timer of driver is set to 15 seconds, need to send
56 * response before timeout occurs
57 */
58static constexpr const unsigned int hostReqTimeout = 14000000;
Dung Caofaf6a6a2020-12-28 04:44:45 +000059
Patrick Williams8b050c92023-10-20 11:19:32 -050060class SsifChannel
61{
62 public:
63 static constexpr size_t ssifMessageSize = IPMI_SSIF_PAYLOAD_MAX +
64 sizeof(unsigned int);
65 size_t sizeofLenField = sizeof(unsigned int);
66 static constexpr uint8_t netFnShift = 2;
67 static constexpr uint8_t lunMask = (1 << netFnShift) - 1;
Dung Caofaf6a6a2020-12-28 04:44:45 +000068
Patrick Williams8b050c92023-10-20 11:19:32 -050069 SsifChannel(std::shared_ptr<boost::asio::io_context>& io,
70 std::shared_ptr<sdbusplus::asio::connection>& bus,
71 const std::string& channel, bool verbose,
72 int numberOfReqNotRsp);
73 bool initOK() const
74 {
75 return !!dev;
76 }
77 void channelAbort(const char* msg, const boost::system::error_code& ec);
78 void async_read();
79 void processMessage(const boost::system::error_code& ecRd, size_t rlen);
80 int showNumOfReqNotRsp();
81 std::unique_ptr<boost::asio::posix::stream_descriptor> dev = nullptr;
Dung Caofaf6a6a2020-12-28 04:44:45 +000082
Patrick Williams8b050c92023-10-20 11:19:32 -050083 protected:
84 std::array<uint8_t, ssifMessageSize> xferBuffer;
85 std::shared_ptr<boost::asio::io_context> io;
86 std::shared_ptr<sdbusplus::asio::connection> bus;
87 std::shared_ptr<sdbusplus::asio::object_server> server;
88 bool verbose;
89 /* This variable is always 0 when a request is responsed properly,
90 * any value larger than 0 meaning there is/are request(s) which
91 * not processed properly
92 * */
93 int numberOfReqNotRsp;
Dung Caofaf6a6a2020-12-28 04:44:45 +000094};
95
Ed Tanousd289aea2024-02-06 20:33:26 -080096std::unique_ptr<boost::asio::steady_timer> rspTimer;
Quang Nguyenf2994dc2022-11-25 16:31:49 +070097std::unique_ptr<SsifChannel> ssifchannel = nullptr;
98
Patrick Williams8b050c92023-10-20 11:19:32 -050099SsifChannel::SsifChannel(std::shared_ptr<boost::asio::io_context>& io,
100 std::shared_ptr<sdbusplus::asio::connection>& bus,
101 const std::string& device, bool verbose,
102 int numberOfReqNotRsp) :
103 io(io),
104 bus(bus), verbose(verbose), numberOfReqNotRsp(numberOfReqNotRsp)
Dung Caofaf6a6a2020-12-28 04:44:45 +0000105{
Patrick Williams8b050c92023-10-20 11:19:32 -0500106 std::string devName = devBase;
107 if (!device.empty())
108 {
109 devName = device;
110 }
Dung Caofaf6a6a2020-12-28 04:44:45 +0000111
Patrick Williams8b050c92023-10-20 11:19:32 -0500112 // open device
113 int fd = open(devName.c_str(), O_RDWR | O_NONBLOCK);
114 if (fd < 0)
115 {
116 std::string msgToLog = "Couldn't open SSIF driver with flags O_RDWR."
117 " FILENAME=" +
118 devName + " ERROR=" + strerror(errno);
119 log<level::ERR>(msgToLog.c_str());
120 return;
121 }
122 else
123 {
124 dev = std::make_unique<boost::asio::posix::stream_descriptor>(*io, fd);
125 }
Dung Caofaf6a6a2020-12-28 04:44:45 +0000126
Patrick Williams8b050c92023-10-20 11:19:32 -0500127 async_read();
128 // register interfaces...
129 server = std::make_shared<sdbusplus::asio::object_server>(bus);
130 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
131 server->add_interface(ssifObj, ssifBus);
132 iface->initialize();
Dung Caofaf6a6a2020-12-28 04:44:45 +0000133}
134
Patrick Williams8b050c92023-10-20 11:19:32 -0500135void SsifChannel::channelAbort(const char* msg,
136 const boost::system::error_code& ec)
Dung Caofaf6a6a2020-12-28 04:44:45 +0000137{
Patrick Williams8b050c92023-10-20 11:19:32 -0500138 std::string msgToLog = std::string(msg) + " ERROR=" + ec.message();
139 log<level::ERR>(msgToLog.c_str());
140 // bail; maybe a restart from systemd can clear the error
141 io->stop();
Dung Caofaf6a6a2020-12-28 04:44:45 +0000142}
143
144void SsifChannel::async_read()
145{
Patrick Williams8b050c92023-10-20 11:19:32 -0500146 boost::asio::async_read(*dev,
147 boost::asio::buffer(xferBuffer, xferBuffer.size()),
148 boost::asio::transfer_at_least(2),
149 [this](const boost::system::error_code& ec,
150 size_t rlen) { processMessage(ec, rlen); });
Dung Caofaf6a6a2020-12-28 04:44:45 +0000151}
152
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700153int SsifChannel::showNumOfReqNotRsp()
154{
Patrick Williams8b050c92023-10-20 11:19:32 -0500155 return numberOfReqNotRsp;
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700156}
157
Ed Tanousd289aea2024-02-06 20:33:26 -0800158void rspTimerHandler(const boost::system::error_code& ec)
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700159{
Ed Tanousd289aea2024-02-06 20:33:26 -0800160 if (ec == boost::asio::error::operation_aborted)
161 {
162 return;
163 }
Patrick Williams8b050c92023-10-20 11:19:32 -0500164 std::vector<uint8_t> rsp;
165 constexpr uint8_t ccResponseNotAvailable = 0xce;
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700166
Patrick Williams8b050c92023-10-20 11:19:32 -0500167 rsp.resize(ssifchannel->sizeofLenField + sizeof(prev_req_cmd.cmd) +
168 sizeof(prev_req_cmd.netfn) + sizeof(ccResponseNotAvailable));
169 std::string msgToLog = "timeout, send response to keep host alive"
170 " netfn=" +
171 std::to_string(prev_req_cmd.netfn) +
172 " lun=" + std::to_string(prev_req_cmd.lun) +
173 " cmd=" + std::to_string(prev_req_cmd.cmd) +
174 " cc=" + std::to_string(ccResponseNotAvailable) +
175 " numberOfReqNotRsp=" +
176 std::to_string(ssifchannel->showNumOfReqNotRsp());
177 log<level::INFO>(msgToLog.c_str());
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700178
Patrick Williams8b050c92023-10-20 11:19:32 -0500179 unsigned int* t = (unsigned int*)&rsp[0];
180 *t = 3;
181 rsp[ssifchannel->sizeofLenField] =
182 ((prev_req_cmd.netfn + 1) << ssifchannel->netFnShift) |
183 (prev_req_cmd.lun & ssifchannel->lunMask);
184 rsp[ssifchannel->sizeofLenField + 1] = prev_req_cmd.cmd;
185 rsp[ssifchannel->sizeofLenField + 2] = ccResponseNotAvailable;
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700186
Patrick Williams8b050c92023-10-20 11:19:32 -0500187 boost::system::error_code ecWr;
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700188
Patrick Williams8b050c92023-10-20 11:19:32 -0500189 size_t wlen = boost::asio::write(*(ssifchannel->dev),
190 boost::asio::buffer(rsp), ecWr);
191 if (ecWr || wlen != rsp.size())
192 {
193 msgToLog =
194 "Failed to send ssif respond message:"
195 " size=" +
196 std::to_string(wlen) + " expect=" + std::to_string(rsp.size()) +
197 " error=" + ecWr.message() +
198 " netfn=" + std::to_string(prev_req_cmd.netfn + 1) +
199 " lun=" + std::to_string(prev_req_cmd.lun) +
200 " cmd=" + std::to_string(rsp[ssifchannel->sizeofLenField + 1]) +
201 " cc=" + std::to_string(ccResponseNotAvailable);
202 log<level::ERR>(msgToLog.c_str());
203 }
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700204}
205
Ed Tanousd289aea2024-02-06 20:33:26 -0800206void initTimer(boost::asio::io_context& io)
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700207{
Patrick Williams8b050c92023-10-20 11:19:32 -0500208 if (!rspTimer)
209 {
Ed Tanousd289aea2024-02-06 20:33:26 -0800210 rspTimer = std::make_unique<boost::asio::steady_timer>(io);
Patrick Williams8b050c92023-10-20 11:19:32 -0500211 }
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700212}
213
Patrick Williams8b050c92023-10-20 11:19:32 -0500214void SsifChannel::processMessage(const boost::system::error_code& ecRd,
215 size_t rlen)
Dung Caofaf6a6a2020-12-28 04:44:45 +0000216{
Patrick Williams8b050c92023-10-20 11:19:32 -0500217 if (ecRd || rlen < 2)
218 {
219 channelAbort("Failed to read req msg", ecRd);
220 return;
221 }
222 async_read();
Dung Caofaf6a6a2020-12-28 04:44:45 +0000223
Patrick Williams8b050c92023-10-20 11:19:32 -0500224 auto rawIter = xferBuffer.cbegin();
225 auto rawEnd = rawIter + rlen;
226 uint8_t netfn = rawIter[sizeofLenField] >> netFnShift;
227 uint8_t lun = rawIter[sizeofLenField] & lunMask;
228 uint8_t cmd = rawIter[sizeofLenField + 1];
quang.ampere14480ee2022-10-18 09:04:51 +0700229
Patrick Williams8b050c92023-10-20 11:19:32 -0500230 /* keep track of previous request */
231 prev_req_cmd.netfn = netfn;
232 prev_req_cmd.lun = lun;
233 prev_req_cmd.cmd = cmd;
quang.ampere14480ee2022-10-18 09:04:51 +0700234
Patrick Williams8b050c92023-10-20 11:19:32 -0500235 /* there is a request coming */
236 numberOfReqNotRsp++;
237 /* start response timer */
Ed Tanousd289aea2024-02-06 20:33:26 -0800238 rspTimer->expires_after(std::chrono::microseconds(hostReqTimeout));
239 rspTimer->async_wait(rspTimerHandler);
quang.name507782a2022-11-20 17:05:20 +0700240
Patrick Williams8b050c92023-10-20 11:19:32 -0500241 if (verbose)
242 {
243 unsigned int lenRecv;
244 unsigned int* p = (unsigned int*)rawIter;
245 lenRecv = p[0];
246 std::string msgToLog =
247 "Read ssif request message with"
248 " len=" +
249 std::to_string(lenRecv) + " netfn=" + std::to_string(netfn) +
250 " lun=" + std::to_string(lun) + " cmd=" + std::to_string(cmd) +
251 " numberOfReqNotRsp=" + std::to_string(numberOfReqNotRsp);
252 log<level::INFO>(msgToLog.c_str());
253 }
254 // copy out payload
255 std::vector<uint8_t> data(rawIter + sizeofLenField + 2, rawEnd);
256 // non-session bridges still need to pass an empty options map
257 std::map<std::string, std::variant<int>> options;
258 // the response is a tuple because dbus can only return a single value
259 using IpmiDbusRspType =
260 std::tuple<uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>;
261 static constexpr const char ipmiQueueService[] =
262 "xyz.openbmc_project.Ipmi.Host";
263 static constexpr const char ipmiQueuePath[] = "/xyz/openbmc_project/Ipmi";
264 static constexpr const char ipmiQueueIntf[] =
265 "xyz.openbmc_project.Ipmi.Server";
266 static constexpr const char ipmiQueueMethod[] = "execute";
267 /* now, we do not care dbus timeout, since we already have actions
268 * before dbus timeout occurs
269 */
270 static constexpr unsigned int dbusTimeout = 60000000;
271 bus->async_method_call_timed(
272 [this, netfnCap{netfn}, lunCap{lun},
273 cmdCap{cmd}](const boost::system::error_code& ec,
274 const IpmiDbusRspType& response) {
275 std::vector<uint8_t> rsp;
276 const auto& [netfn, lun, cmd, cc, payload] = response;
277 numberOfReqNotRsp--;
278 if (ec)
279 {
280 std::string msgToLog =
281 "ssif<->ipmid bus error:"
282 " netfn=" +
283 std::to_string(netfn) + " lun=" + std::to_string(lun) +
284 " cmd=" + std::to_string(cmd) + " error=" + ec.message();
285 log<level::ERR>(msgToLog.c_str());
286 rsp.resize(sizeofLenField + sizeof(netfn) + sizeof(cmd) +
287 sizeof(cc));
288 /* if dbusTimeout, just return and do not send any response
289 * to let host continue with other commands, response here
290 * is potentially make the response duplicated
291 * */
292 return;
293 }
294 else
295 {
296 if ((prev_req_cmd.netfn != (netfn - 1) || prev_req_cmd.lun != lun ||
297 prev_req_cmd.cmd != cmd) ||
298 ((prev_req_cmd.netfn == (netfn - 1) &&
299 prev_req_cmd.lun == lun && prev_req_cmd.cmd == cmd) &&
300 numberOfReqNotRsp != 0))
301 {
302 /* Only send response to the last request command to void
303 * duplicated response which makes host driver confused and
304 * failed to create interface
305 *
306 * Drop responses which are (1) different from the request
307 * (2) parameters are the same as request but handshake flow
308 * are in dupplicate request state
309 * */
310 if (verbose)
311 {
312 std::string msgToLog = "Drop ssif respond message with"
313 " len=" +
314 std::to_string(payload.size() + 3) +
315 " netfn=" + std::to_string(netfn) +
316 " lun=" + std::to_string(lun) +
317 " cmd=" + std::to_string(cmd) +
318 " cc=" + std::to_string(cc) +
319 " numberOfReqNotRsp=" +
320 std::to_string(numberOfReqNotRsp);
321 log<level::INFO>(msgToLog.c_str());
322 }
323 return;
324 }
325 rsp.resize(sizeofLenField + sizeof(netfn) + sizeof(cmd) +
326 sizeof(cc) + payload.size());
Dung Caofaf6a6a2020-12-28 04:44:45 +0000327
Patrick Williams8b050c92023-10-20 11:19:32 -0500328 // write the response
329 auto rspIter = rsp.begin();
330 unsigned int* p = (unsigned int*)&rspIter[0];
331 *p = payload.size() + 3;
332 rspIter[sizeofLenField] = (netfn << netFnShift) | (lun & lunMask);
333 rspIter[sizeofLenField + 1] = cmd;
334 rspIter[sizeofLenField + 2] = cc;
335 if (payload.size())
336 {
337 std::copy(payload.cbegin(), payload.cend(),
338 rspIter + sizeofLenField + 3);
339 }
340 }
341 if (verbose)
342 {
343 std::string msgToLog =
344 "Send ssif respond message with"
345 " len=" +
346 std::to_string(payload.size() + 3) +
347 " netfn=" + std::to_string(netfn) +
348 " lun=" + std::to_string(lun) + " cmd=" + std::to_string(cmd) +
349 " cc=" + std::to_string(cc) +
350 " numberOfReqNotRsp=" + std::to_string(numberOfReqNotRsp);
351 log<level::INFO>(msgToLog.c_str());
352 }
353 boost::system::error_code ecWr;
354 size_t wlen = boost::asio::write(*dev, boost::asio::buffer(rsp), ecWr);
355 if (ecWr || wlen != rsp.size())
356 {
357 std::string msgToLog =
358 "Failed to send ssif respond message:"
359 " size=" +
360 std::to_string(wlen) + " expect=" + std::to_string(rsp.size()) +
361 " error=" + ecWr.message() + " netfn=" + std::to_string(netfn) +
362 " lun=" + std::to_string(lun) + " cmd=" + std::to_string(cmd) +
363 " cc=" + std::to_string(cc);
364 log<level::ERR>(msgToLog.c_str());
365 }
Ed Tanousd289aea2024-02-06 20:33:26 -0800366 rspTimer->cancel();
Patrick Williams8b050c92023-10-20 11:19:32 -0500367 },
368 ipmiQueueService, ipmiQueuePath, ipmiQueueIntf, ipmiQueueMethod,
369 dbusTimeout, netfn, lun, cmd, data, options);
Dung Caofaf6a6a2020-12-28 04:44:45 +0000370}
371
Patrick Williams8b050c92023-10-20 11:19:32 -0500372int main(int argc, char* argv[])
Dung Caofaf6a6a2020-12-28 04:44:45 +0000373{
Patrick Williams8b050c92023-10-20 11:19:32 -0500374 CLI::App app("SSIF IPMI bridge");
375 std::string device;
376 app.add_option("-d,--device", device,
377 "use <DEVICE> file. Default is /dev/ipmi-ssif-host");
378 bool verbose = false;
379 int numberOfReqNotRsp = 0;
380 app.add_option("-v,--verbose", verbose, "print more verbose output");
381 CLI11_PARSE(app, argc, argv);
Dung Caofaf6a6a2020-12-28 04:44:45 +0000382
Patrick Williams8b050c92023-10-20 11:19:32 -0500383 auto io = std::make_shared<boost::asio::io_context>();
Dung Caofaf6a6a2020-12-28 04:44:45 +0000384
Ed Tanousd289aea2024-02-06 20:33:26 -0800385 auto bus = std::make_shared<sdbusplus::asio::connection>(*io);
Patrick Williams8b050c92023-10-20 11:19:32 -0500386 bus->request_name(ssifBus);
387 // Create the SSIF channel, listening on D-Bus and on the SSIF device
388 ssifchannel = make_unique<SsifChannel>(io, bus, device, verbose,
389 numberOfReqNotRsp);
390 if (!ssifchannel->initOK())
391 {
392 return EXIT_FAILURE;
393 }
Ed Tanousd289aea2024-02-06 20:33:26 -0800394 initTimer(*io);
Patrick Williams8b050c92023-10-20 11:19:32 -0500395 io->run();
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700396
Patrick Williams8b050c92023-10-20 11:19:32 -0500397 return 0;
Dung Caofaf6a6a2020-12-28 04:44:45 +0000398}