blob: 07edc9fce8d9b4cf8143df54069e5d00e16090d0 [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
Ed Tanous498b87f2024-02-14 09:07:34 -080040using phosphor::logging::level;
41using phosphor::logging::log;
Dung Caofaf6a6a2020-12-28 04:44:45 +000042
Patrick Williams8b050c92023-10-20 11:19:32 -050043struct ipmi_cmd
44{
45 uint8_t netfn;
46 uint8_t lun;
47 uint8_t cmd;
quang.ampere14480ee2022-10-18 09:04:51 +070048} prev_req_cmd;
49
Dung Caofaf6a6a2020-12-28 04:44:45 +000050static constexpr const char devBase[] = "/dev/ipmi-ssif-host";
51/* SSIF use IPMI SSIF channel */
Patrick Williams8b050c92023-10-20 11:19:32 -050052static constexpr const char* ssifBus =
53 "xyz.openbmc_project.Ipmi.Channel.ipmi_ssif";
54static constexpr const char* ssifObj =
55 "/xyz/openbmc_project/Ipmi/Channel/ipmi_ssif";
Quang Nguyenf2994dc2022-11-25 16:31:49 +070056/* The timer of driver is set to 15 seconds, need to send
57 * response before timeout occurs
58 */
59static constexpr const unsigned int hostReqTimeout = 14000000;
Dung Caofaf6a6a2020-12-28 04:44:45 +000060
Patrick Williams8b050c92023-10-20 11:19:32 -050061class SsifChannel
62{
63 public:
64 static constexpr size_t ssifMessageSize = IPMI_SSIF_PAYLOAD_MAX +
65 sizeof(unsigned int);
66 size_t sizeofLenField = sizeof(unsigned int);
67 static constexpr uint8_t netFnShift = 2;
68 static constexpr uint8_t lunMask = (1 << netFnShift) - 1;
Dung Caofaf6a6a2020-12-28 04:44:45 +000069
Patrick Williams8b050c92023-10-20 11:19:32 -050070 SsifChannel(std::shared_ptr<boost::asio::io_context>& io,
71 std::shared_ptr<sdbusplus::asio::connection>& bus,
72 const std::string& channel, bool verbose,
73 int numberOfReqNotRsp);
74 bool initOK() const
75 {
Ed Tanousfcd0ea42024-02-14 09:15:41 -080076 return dev.is_open();
Patrick Williams8b050c92023-10-20 11:19:32 -050077 }
78 void channelAbort(const char* msg, const boost::system::error_code& ec);
79 void async_read();
Ed Tanousb6ad9dc2024-02-14 09:28:20 -080080 using IpmiDbusRspType =
81 std::tuple<uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>;
82
83 void afterMethodCall(const boost::system::error_code& ec,
84 const IpmiDbusRspType& response);
Patrick Williams8b050c92023-10-20 11:19:32 -050085 void processMessage(const boost::system::error_code& ecRd, size_t rlen);
86 int showNumOfReqNotRsp();
Ed Tanousfcd0ea42024-02-14 09:15:41 -080087 boost::asio::posix::stream_descriptor dev;
Dung Caofaf6a6a2020-12-28 04:44:45 +000088
Patrick Williams8b050c92023-10-20 11:19:32 -050089 protected:
90 std::array<uint8_t, ssifMessageSize> xferBuffer;
91 std::shared_ptr<boost::asio::io_context> io;
92 std::shared_ptr<sdbusplus::asio::connection> bus;
93 std::shared_ptr<sdbusplus::asio::object_server> server;
94 bool verbose;
95 /* This variable is always 0 when a request is responsed properly,
96 * any value larger than 0 meaning there is/are request(s) which
97 * not processed properly
98 * */
99 int numberOfReqNotRsp;
Ed Tanous4ae3b9e2024-02-14 08:54:03 -0800100
101 boost::asio::steady_timer rspTimer;
Dung Caofaf6a6a2020-12-28 04:44:45 +0000102};
103
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700104std::unique_ptr<SsifChannel> ssifchannel = nullptr;
105
Patrick Williams8b050c92023-10-20 11:19:32 -0500106SsifChannel::SsifChannel(std::shared_ptr<boost::asio::io_context>& io,
107 std::shared_ptr<sdbusplus::asio::connection>& bus,
108 const std::string& device, bool verbose,
109 int numberOfReqNotRsp) :
Ed Tanousfcd0ea42024-02-14 09:15:41 -0800110 dev(*io),
111 io(io), bus(bus), verbose(verbose), numberOfReqNotRsp(numberOfReqNotRsp),
Ed Tanous4ae3b9e2024-02-14 08:54:03 -0800112 rspTimer(*io)
Dung Caofaf6a6a2020-12-28 04:44:45 +0000113{
Patrick Williams8b050c92023-10-20 11:19:32 -0500114 std::string devName = devBase;
115 if (!device.empty())
116 {
117 devName = device;
118 }
Dung Caofaf6a6a2020-12-28 04:44:45 +0000119
Patrick Williams8b050c92023-10-20 11:19:32 -0500120 // open device
121 int fd = open(devName.c_str(), O_RDWR | O_NONBLOCK);
122 if (fd < 0)
123 {
124 std::string msgToLog = "Couldn't open SSIF driver with flags O_RDWR."
125 " FILENAME=" +
126 devName + " ERROR=" + strerror(errno);
127 log<level::ERR>(msgToLog.c_str());
128 return;
129 }
130 else
131 {
Ed Tanousfcd0ea42024-02-14 09:15:41 -0800132 dev.assign(fd);
Patrick Williams8b050c92023-10-20 11:19:32 -0500133 }
Dung Caofaf6a6a2020-12-28 04:44:45 +0000134
Patrick Williams8b050c92023-10-20 11:19:32 -0500135 async_read();
136 // register interfaces...
137 server = std::make_shared<sdbusplus::asio::object_server>(bus);
138 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
139 server->add_interface(ssifObj, ssifBus);
140 iface->initialize();
Dung Caofaf6a6a2020-12-28 04:44:45 +0000141}
142
Patrick Williams8b050c92023-10-20 11:19:32 -0500143void SsifChannel::channelAbort(const char* msg,
144 const boost::system::error_code& ec)
Dung Caofaf6a6a2020-12-28 04:44:45 +0000145{
Patrick Williams8b050c92023-10-20 11:19:32 -0500146 std::string msgToLog = std::string(msg) + " ERROR=" + ec.message();
147 log<level::ERR>(msgToLog.c_str());
148 // bail; maybe a restart from systemd can clear the error
149 io->stop();
Dung Caofaf6a6a2020-12-28 04:44:45 +0000150}
151
152void SsifChannel::async_read()
153{
Ed Tanousfcd0ea42024-02-14 09:15:41 -0800154 boost::asio::async_read(dev,
Patrick Williams8b050c92023-10-20 11:19:32 -0500155 boost::asio::buffer(xferBuffer, xferBuffer.size()),
156 boost::asio::transfer_at_least(2),
157 [this](const boost::system::error_code& ec,
158 size_t rlen) { processMessage(ec, rlen); });
Dung Caofaf6a6a2020-12-28 04:44:45 +0000159}
160
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700161int SsifChannel::showNumOfReqNotRsp()
162{
Patrick Williams8b050c92023-10-20 11:19:32 -0500163 return numberOfReqNotRsp;
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700164}
165
Ed Tanousd289aea2024-02-06 20:33:26 -0800166void rspTimerHandler(const boost::system::error_code& ec)
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700167{
Ed Tanousd289aea2024-02-06 20:33:26 -0800168 if (ec == boost::asio::error::operation_aborted)
169 {
170 return;
171 }
Patrick Williams8b050c92023-10-20 11:19:32 -0500172 std::vector<uint8_t> rsp;
173 constexpr uint8_t ccResponseNotAvailable = 0xce;
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700174
Patrick Williams8b050c92023-10-20 11:19:32 -0500175 rsp.resize(ssifchannel->sizeofLenField + sizeof(prev_req_cmd.cmd) +
176 sizeof(prev_req_cmd.netfn) + sizeof(ccResponseNotAvailable));
177 std::string msgToLog = "timeout, send response to keep host alive"
178 " netfn=" +
179 std::to_string(prev_req_cmd.netfn) +
180 " lun=" + std::to_string(prev_req_cmd.lun) +
181 " cmd=" + std::to_string(prev_req_cmd.cmd) +
182 " cc=" + std::to_string(ccResponseNotAvailable) +
183 " numberOfReqNotRsp=" +
184 std::to_string(ssifchannel->showNumOfReqNotRsp());
185 log<level::INFO>(msgToLog.c_str());
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700186
Patrick Williams8b050c92023-10-20 11:19:32 -0500187 unsigned int* t = (unsigned int*)&rsp[0];
188 *t = 3;
189 rsp[ssifchannel->sizeofLenField] =
190 ((prev_req_cmd.netfn + 1) << ssifchannel->netFnShift) |
191 (prev_req_cmd.lun & ssifchannel->lunMask);
192 rsp[ssifchannel->sizeofLenField + 1] = prev_req_cmd.cmd;
193 rsp[ssifchannel->sizeofLenField + 2] = ccResponseNotAvailable;
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700194
Patrick Williams8b050c92023-10-20 11:19:32 -0500195 boost::system::error_code ecWr;
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700196
Ed Tanousfcd0ea42024-02-14 09:15:41 -0800197 size_t wlen = boost::asio::write(ssifchannel->dev, boost::asio::buffer(rsp),
198 ecWr);
Patrick Williams8b050c92023-10-20 11:19:32 -0500199 if (ecWr || wlen != rsp.size())
200 {
201 msgToLog =
202 "Failed to send ssif respond message:"
203 " size=" +
204 std::to_string(wlen) + " expect=" + std::to_string(rsp.size()) +
205 " error=" + ecWr.message() +
206 " netfn=" + std::to_string(prev_req_cmd.netfn + 1) +
207 " lun=" + std::to_string(prev_req_cmd.lun) +
208 " cmd=" + std::to_string(rsp[ssifchannel->sizeofLenField + 1]) +
209 " cc=" + std::to_string(ccResponseNotAvailable);
210 log<level::ERR>(msgToLog.c_str());
211 }
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700212}
213
Ed Tanousb6ad9dc2024-02-14 09:28:20 -0800214void SsifChannel::afterMethodCall(const boost::system::error_code& ec,
215 const IpmiDbusRspType& response)
216{
217 std::vector<uint8_t> rsp;
218 const auto& [netfn, lun, cmd, cc, payload] = response;
219 numberOfReqNotRsp--;
220 if (ec)
221 {
222 std::string msgToLog =
223 "ssif<->ipmid bus error:"
224 " netfn=" +
225 std::to_string(netfn) + " lun=" + std::to_string(lun) +
226 " cmd=" + std::to_string(cmd) + " error=" + ec.message();
227 log<level::ERR>(msgToLog.c_str());
228 rsp.resize(sizeofLenField + sizeof(netfn) + sizeof(cmd) + sizeof(cc));
229 /* if dbusTimeout, just return and do not send any response
230 * to let host continue with other commands, response here
231 * is potentially make the response duplicated
232 * */
233 return;
234 }
235 else
236 {
237 if ((prev_req_cmd.netfn != (netfn - 1) || prev_req_cmd.lun != lun ||
238 prev_req_cmd.cmd != cmd) ||
239 ((prev_req_cmd.netfn == (netfn - 1) && prev_req_cmd.lun == lun &&
240 prev_req_cmd.cmd == cmd) &&
241 numberOfReqNotRsp != 0))
242 {
243 /* Only send response to the last request command to void
244 * duplicated response which makes host driver confused and
245 * failed to create interface
246 *
247 * Drop responses which are (1) different from the request
248 * (2) parameters are the same as request but handshake flow
249 * are in dupplicate request state
250 * */
251 if (verbose)
252 {
253 std::string msgToLog =
254 "Drop ssif respond message with"
255 " len=" +
256 std::to_string(payload.size() + 3) +
257 " netfn=" + std::to_string(netfn) +
258 " lun=" + std::to_string(lun) +
259 " cmd=" + std::to_string(cmd) +
260 " cc=" + std::to_string(cc) +
261 " numberOfReqNotRsp=" + std::to_string(numberOfReqNotRsp);
262 log<level::INFO>(msgToLog.c_str());
263 }
264 return;
265 }
266 rsp.resize(sizeofLenField + sizeof(netfn) + sizeof(cmd) + sizeof(cc) +
267 payload.size());
268
269 // write the response
270 auto rspIter = rsp.begin();
271 unsigned int* p = (unsigned int*)&rspIter[0];
272 *p = payload.size() + 3;
273 rspIter[sizeofLenField] = (netfn << netFnShift) | (lun & lunMask);
274 rspIter[sizeofLenField + 1] = cmd;
275 rspIter[sizeofLenField + 2] = cc;
276 if (payload.size())
277 {
278 std::copy(payload.cbegin(), payload.cend(),
279 rspIter + sizeofLenField + 3);
280 }
281 }
282 if (verbose)
283 {
284 std::string msgToLog =
285 "Send ssif respond message with"
286 " len=" +
287 std::to_string(payload.size() + 3) +
288 " netfn=" + std::to_string(netfn) + " lun=" + std::to_string(lun) +
289 " cmd=" + std::to_string(cmd) + " cc=" + std::to_string(cc) +
290 " numberOfReqNotRsp=" + std::to_string(numberOfReqNotRsp);
291 log<level::INFO>(msgToLog.c_str());
292 }
293 boost::system::error_code ecWr;
294 size_t wlen = boost::asio::write(dev, boost::asio::buffer(rsp), ecWr);
295 if (ecWr || wlen != rsp.size())
296 {
297 std::string msgToLog =
298 "Failed to send ssif respond message:"
299 " size=" +
300 std::to_string(wlen) + " expect=" + std::to_string(rsp.size()) +
301 " error=" + ecWr.message() + " netfn=" + std::to_string(netfn) +
302 " lun=" + std::to_string(lun) + " cmd=" + std::to_string(cmd) +
303 " cc=" + std::to_string(cc);
304 log<level::ERR>(msgToLog.c_str());
305 }
306 rspTimer.cancel();
307}
308
Patrick Williams8b050c92023-10-20 11:19:32 -0500309void SsifChannel::processMessage(const boost::system::error_code& ecRd,
310 size_t rlen)
Dung Caofaf6a6a2020-12-28 04:44:45 +0000311{
Patrick Williams8b050c92023-10-20 11:19:32 -0500312 if (ecRd || rlen < 2)
313 {
314 channelAbort("Failed to read req msg", ecRd);
315 return;
316 }
317 async_read();
Dung Caofaf6a6a2020-12-28 04:44:45 +0000318
Patrick Williams8b050c92023-10-20 11:19:32 -0500319 auto rawIter = xferBuffer.cbegin();
320 auto rawEnd = rawIter + rlen;
321 uint8_t netfn = rawIter[sizeofLenField] >> netFnShift;
322 uint8_t lun = rawIter[sizeofLenField] & lunMask;
323 uint8_t cmd = rawIter[sizeofLenField + 1];
quang.ampere14480ee2022-10-18 09:04:51 +0700324
Patrick Williams8b050c92023-10-20 11:19:32 -0500325 /* keep track of previous request */
326 prev_req_cmd.netfn = netfn;
327 prev_req_cmd.lun = lun;
328 prev_req_cmd.cmd = cmd;
quang.ampere14480ee2022-10-18 09:04:51 +0700329
Patrick Williams8b050c92023-10-20 11:19:32 -0500330 /* there is a request coming */
331 numberOfReqNotRsp++;
332 /* start response timer */
Ed Tanous4ae3b9e2024-02-14 08:54:03 -0800333 rspTimer.expires_after(std::chrono::microseconds(hostReqTimeout));
334 rspTimer.async_wait(rspTimerHandler);
quang.name507782a2022-11-20 17:05:20 +0700335
Patrick Williams8b050c92023-10-20 11:19:32 -0500336 if (verbose)
337 {
338 unsigned int lenRecv;
339 unsigned int* p = (unsigned int*)rawIter;
340 lenRecv = p[0];
341 std::string msgToLog =
342 "Read ssif request message with"
343 " len=" +
344 std::to_string(lenRecv) + " netfn=" + std::to_string(netfn) +
345 " lun=" + std::to_string(lun) + " cmd=" + std::to_string(cmd) +
346 " numberOfReqNotRsp=" + std::to_string(numberOfReqNotRsp);
347 log<level::INFO>(msgToLog.c_str());
348 }
349 // copy out payload
350 std::vector<uint8_t> data(rawIter + sizeofLenField + 2, rawEnd);
351 // non-session bridges still need to pass an empty options map
352 std::map<std::string, std::variant<int>> options;
Patrick Williams8b050c92023-10-20 11:19:32 -0500353 static constexpr const char ipmiQueueService[] =
354 "xyz.openbmc_project.Ipmi.Host";
355 static constexpr const char ipmiQueuePath[] = "/xyz/openbmc_project/Ipmi";
356 static constexpr const char ipmiQueueIntf[] =
357 "xyz.openbmc_project.Ipmi.Server";
358 static constexpr const char ipmiQueueMethod[] = "execute";
359 /* now, we do not care dbus timeout, since we already have actions
360 * before dbus timeout occurs
361 */
362 static constexpr unsigned int dbusTimeout = 60000000;
363 bus->async_method_call_timed(
Ed Tanousb6ad9dc2024-02-14 09:28:20 -0800364 [this](const boost::system::error_code& ec,
365 const IpmiDbusRspType& response) {
366 afterMethodCall(ec, response);
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 }
Patrick Williams8b050c92023-10-20 11:19:32 -0500394 io->run();
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700395
Patrick Williams8b050c92023-10-20 11:19:32 -0500396 return 0;
Dung Caofaf6a6a2020-12-28 04:44:45 +0000397}