blob: 84dd35051c60f6f40cd8039239975a3a037a609e [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
Ed Tanousa1585be2024-02-14 10:57:31 -080043struct IpmiCmd
Patrick Williams8b050c92023-10-20 11:19:32 -050044{
45 uint8_t netfn;
46 uint8_t lun;
47 uint8_t cmd;
Ed Tanousa1585be2024-02-14 10:57:31 -080048} prevReqCmd;
quang.ampere14480ee2022-10-18 09:04:51 +070049
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";
Ed Tanousa1585be2024-02-14 10:57:31 -080054
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,
Ed Tanousa1585be2024-02-14 10:57:31 -080071 const std::string& device, bool verbose, int numberOfReqNotRsp);
Patrick Williams8b050c92023-10-20 11:19:32 -050072 bool initOK() const
73 {
Ed Tanousfcd0ea42024-02-14 09:15:41 -080074 return dev.is_open();
Patrick Williams8b050c92023-10-20 11:19:32 -050075 }
76 void channelAbort(const char* msg, const boost::system::error_code& ec);
Ed Tanousa1585be2024-02-14 10:57:31 -080077 void asyncRead();
Ed Tanousb6ad9dc2024-02-14 09:28:20 -080078 using IpmiDbusRspType =
79 std::tuple<uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>;
80
81 void afterMethodCall(const boost::system::error_code& ec,
82 const IpmiDbusRspType& response);
Patrick Williams8b050c92023-10-20 11:19:32 -050083 void processMessage(const boost::system::error_code& ecRd, size_t rlen);
Ed Tanousa1585be2024-02-14 10:57:31 -080084 int showNumOfReqNotRsp() const;
Ed Tanousfcd0ea42024-02-14 09:15:41 -080085 boost::asio::posix::stream_descriptor dev;
Dung Caofaf6a6a2020-12-28 04:44:45 +000086
Patrick Williams8b050c92023-10-20 11:19:32 -050087 protected:
Ed Tanousa1585be2024-02-14 10:57:31 -080088 std::array<uint8_t, ssifMessageSize> xferBuffer{};
Patrick Williams8b050c92023-10-20 11:19:32 -050089 std::shared_ptr<boost::asio::io_context> io;
90 std::shared_ptr<sdbusplus::asio::connection> bus;
91 std::shared_ptr<sdbusplus::asio::object_server> server;
92 bool verbose;
93 /* This variable is always 0 when a request is responsed properly,
94 * any value larger than 0 meaning there is/are request(s) which
95 * not processed properly
96 * */
97 int numberOfReqNotRsp;
Ed Tanous4ae3b9e2024-02-14 08:54:03 -080098
99 boost::asio::steady_timer rspTimer;
Dung Caofaf6a6a2020-12-28 04:44:45 +0000100};
101
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700102std::unique_ptr<SsifChannel> ssifchannel = nullptr;
103
Patrick Williams8b050c92023-10-20 11:19:32 -0500104SsifChannel::SsifChannel(std::shared_ptr<boost::asio::io_context>& io,
105 std::shared_ptr<sdbusplus::asio::connection>& bus,
106 const std::string& device, bool verbose,
107 int numberOfReqNotRsp) :
Ed Tanousfcd0ea42024-02-14 09:15:41 -0800108 dev(*io),
109 io(io), bus(bus), verbose(verbose), numberOfReqNotRsp(numberOfReqNotRsp),
Ed Tanous4ae3b9e2024-02-14 08:54:03 -0800110 rspTimer(*io)
Dung Caofaf6a6a2020-12-28 04:44:45 +0000111{
Patrick Williams8b050c92023-10-20 11:19:32 -0500112 std::string devName = devBase;
113 if (!device.empty())
114 {
115 devName = device;
116 }
Dung Caofaf6a6a2020-12-28 04:44:45 +0000117
Patrick Williams8b050c92023-10-20 11:19:32 -0500118 // open device
119 int fd = open(devName.c_str(), O_RDWR | O_NONBLOCK);
120 if (fd < 0)
121 {
122 std::string msgToLog = "Couldn't open SSIF driver with flags O_RDWR."
123 " FILENAME=" +
124 devName + " ERROR=" + strerror(errno);
125 log<level::ERR>(msgToLog.c_str());
126 return;
127 }
Dung Caofaf6a6a2020-12-28 04:44:45 +0000128
Ed Tanousa1585be2024-02-14 10:57:31 -0800129 dev.assign(fd);
130
131 asyncRead();
Patrick Williams8b050c92023-10-20 11:19:32 -0500132 // register interfaces...
133 server = std::make_shared<sdbusplus::asio::object_server>(bus);
134 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
Ed Tanousa1585be2024-02-14 10:57:31 -0800135 server->add_interface("/xyz/openbmc_project/Ipmi/Channel/ipmi_ssif",
136 ssifBus);
Patrick Williams8b050c92023-10-20 11:19:32 -0500137 iface->initialize();
Dung Caofaf6a6a2020-12-28 04:44:45 +0000138}
139
Patrick Williams8b050c92023-10-20 11:19:32 -0500140void SsifChannel::channelAbort(const char* msg,
141 const boost::system::error_code& ec)
Dung Caofaf6a6a2020-12-28 04:44:45 +0000142{
Patrick Williams8b050c92023-10-20 11:19:32 -0500143 std::string msgToLog = std::string(msg) + " ERROR=" + ec.message();
144 log<level::ERR>(msgToLog.c_str());
145 // bail; maybe a restart from systemd can clear the error
146 io->stop();
Dung Caofaf6a6a2020-12-28 04:44:45 +0000147}
148
Ed Tanousa1585be2024-02-14 10:57:31 -0800149void SsifChannel::asyncRead()
Dung Caofaf6a6a2020-12-28 04:44:45 +0000150{
Ed Tanousfcd0ea42024-02-14 09:15:41 -0800151 boost::asio::async_read(dev,
Patrick Williams8b050c92023-10-20 11:19:32 -0500152 boost::asio::buffer(xferBuffer, xferBuffer.size()),
153 boost::asio::transfer_at_least(2),
154 [this](const boost::system::error_code& ec,
155 size_t rlen) { processMessage(ec, rlen); });
Dung Caofaf6a6a2020-12-28 04:44:45 +0000156}
157
Ed Tanousa1585be2024-02-14 10:57:31 -0800158int SsifChannel::showNumOfReqNotRsp() const
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700159{
Patrick Williams8b050c92023-10-20 11:19:32 -0500160 return numberOfReqNotRsp;
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700161}
162
Ed Tanousd289aea2024-02-06 20:33:26 -0800163void rspTimerHandler(const boost::system::error_code& ec)
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700164{
Ed Tanousd289aea2024-02-06 20:33:26 -0800165 if (ec == boost::asio::error::operation_aborted)
166 {
167 return;
168 }
Patrick Williams8b050c92023-10-20 11:19:32 -0500169 std::vector<uint8_t> rsp;
170 constexpr uint8_t ccResponseNotAvailable = 0xce;
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700171
Ed Tanousa1585be2024-02-14 10:57:31 -0800172 rsp.resize(ssifchannel->sizeofLenField + sizeof(prevReqCmd.cmd) +
173 sizeof(prevReqCmd.netfn) + sizeof(ccResponseNotAvailable));
Patrick Williams8b050c92023-10-20 11:19:32 -0500174 std::string msgToLog = "timeout, send response to keep host alive"
175 " netfn=" +
Ed Tanousa1585be2024-02-14 10:57:31 -0800176 std::to_string(prevReqCmd.netfn) +
177 " lun=" + std::to_string(prevReqCmd.lun) +
178 " cmd=" + std::to_string(prevReqCmd.cmd) +
Patrick Williams8b050c92023-10-20 11:19:32 -0500179 " cc=" + std::to_string(ccResponseNotAvailable) +
180 " numberOfReqNotRsp=" +
181 std::to_string(ssifchannel->showNumOfReqNotRsp());
182 log<level::INFO>(msgToLog.c_str());
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700183
Ed Tanousa1585be2024-02-14 10:57:31 -0800184 unsigned int* t = (unsigned int*)rsp.data();
Patrick Williams8b050c92023-10-20 11:19:32 -0500185 *t = 3;
Ed Tanousa1585be2024-02-14 10:57:31 -0800186 rsp[ssifchannel->sizeofLenField] = ((prevReqCmd.netfn + 1)
187 << ssifchannel->netFnShift) |
188 (prevReqCmd.lun & ssifchannel->lunMask);
189 rsp[ssifchannel->sizeofLenField + 1] = prevReqCmd.cmd;
Patrick Williams8b050c92023-10-20 11:19:32 -0500190 rsp[ssifchannel->sizeofLenField + 2] = ccResponseNotAvailable;
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700191
Patrick Williams8b050c92023-10-20 11:19:32 -0500192 boost::system::error_code ecWr;
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700193
Ed Tanousfcd0ea42024-02-14 09:15:41 -0800194 size_t wlen = boost::asio::write(ssifchannel->dev, boost::asio::buffer(rsp),
195 ecWr);
Patrick Williams8b050c92023-10-20 11:19:32 -0500196 if (ecWr || wlen != rsp.size())
197 {
198 msgToLog =
199 "Failed to send ssif respond message:"
200 " size=" +
201 std::to_string(wlen) + " expect=" + std::to_string(rsp.size()) +
202 " error=" + ecWr.message() +
Ed Tanousa1585be2024-02-14 10:57:31 -0800203 " netfn=" + std::to_string(prevReqCmd.netfn + 1) +
204 " lun=" + std::to_string(prevReqCmd.lun) +
Patrick Williams8b050c92023-10-20 11:19:32 -0500205 " cmd=" + std::to_string(rsp[ssifchannel->sizeofLenField + 1]) +
206 " cc=" + std::to_string(ccResponseNotAvailable);
207 log<level::ERR>(msgToLog.c_str());
208 }
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700209}
210
Ed Tanousb6ad9dc2024-02-14 09:28:20 -0800211void SsifChannel::afterMethodCall(const boost::system::error_code& ec,
212 const IpmiDbusRspType& response)
213{
214 std::vector<uint8_t> rsp;
215 const auto& [netfn, lun, cmd, cc, payload] = response;
216 numberOfReqNotRsp--;
217 if (ec)
218 {
219 std::string msgToLog =
220 "ssif<->ipmid bus error:"
221 " netfn=" +
222 std::to_string(netfn) + " lun=" + std::to_string(lun) +
223 " cmd=" + std::to_string(cmd) + " error=" + ec.message();
224 log<level::ERR>(msgToLog.c_str());
225 rsp.resize(sizeofLenField + sizeof(netfn) + sizeof(cmd) + sizeof(cc));
226 /* if dbusTimeout, just return and do not send any response
227 * to let host continue with other commands, response here
228 * is potentially make the response duplicated
229 * */
230 return;
231 }
232 else
233 {
Ed Tanousa1585be2024-02-14 10:57:31 -0800234 if ((prevReqCmd.netfn != (netfn - 1) || prevReqCmd.lun != lun ||
235 prevReqCmd.cmd != cmd) ||
236 ((prevReqCmd.netfn == (netfn - 1) && prevReqCmd.lun == lun &&
237 prevReqCmd.cmd == cmd) &&
Ed Tanousb6ad9dc2024-02-14 09:28:20 -0800238 numberOfReqNotRsp != 0))
239 {
240 /* Only send response to the last request command to void
241 * duplicated response which makes host driver confused and
242 * failed to create interface
243 *
244 * Drop responses which are (1) different from the request
245 * (2) parameters are the same as request but handshake flow
246 * are in dupplicate request state
247 * */
248 if (verbose)
249 {
250 std::string msgToLog =
251 "Drop ssif respond message with"
252 " len=" +
253 std::to_string(payload.size() + 3) +
254 " netfn=" + std::to_string(netfn) +
255 " lun=" + std::to_string(lun) +
256 " cmd=" + std::to_string(cmd) +
257 " cc=" + std::to_string(cc) +
258 " numberOfReqNotRsp=" + std::to_string(numberOfReqNotRsp);
259 log<level::INFO>(msgToLog.c_str());
260 }
261 return;
262 }
263 rsp.resize(sizeofLenField + sizeof(netfn) + sizeof(cmd) + sizeof(cc) +
264 payload.size());
265
266 // write the response
267 auto rspIter = rsp.begin();
268 unsigned int* p = (unsigned int*)&rspIter[0];
269 *p = payload.size() + 3;
270 rspIter[sizeofLenField] = (netfn << netFnShift) | (lun & lunMask);
271 rspIter[sizeofLenField + 1] = cmd;
272 rspIter[sizeofLenField + 2] = cc;
Ed Tanousa1585be2024-02-14 10:57:31 -0800273 if (!payload.empty() != 0u)
Ed Tanousb6ad9dc2024-02-14 09:28:20 -0800274 {
275 std::copy(payload.cbegin(), payload.cend(),
276 rspIter + sizeofLenField + 3);
277 }
278 }
279 if (verbose)
280 {
281 std::string msgToLog =
282 "Send ssif respond message with"
283 " len=" +
284 std::to_string(payload.size() + 3) +
285 " netfn=" + std::to_string(netfn) + " lun=" + std::to_string(lun) +
286 " cmd=" + std::to_string(cmd) + " cc=" + std::to_string(cc) +
287 " numberOfReqNotRsp=" + std::to_string(numberOfReqNotRsp);
288 log<level::INFO>(msgToLog.c_str());
289 }
290 boost::system::error_code ecWr;
291 size_t wlen = boost::asio::write(dev, boost::asio::buffer(rsp), ecWr);
292 if (ecWr || wlen != rsp.size())
293 {
294 std::string msgToLog =
295 "Failed to send ssif respond message:"
296 " size=" +
297 std::to_string(wlen) + " expect=" + std::to_string(rsp.size()) +
298 " error=" + ecWr.message() + " netfn=" + std::to_string(netfn) +
299 " lun=" + std::to_string(lun) + " cmd=" + std::to_string(cmd) +
300 " cc=" + std::to_string(cc);
301 log<level::ERR>(msgToLog.c_str());
302 }
303 rspTimer.cancel();
304}
305
Patrick Williams8b050c92023-10-20 11:19:32 -0500306void SsifChannel::processMessage(const boost::system::error_code& ecRd,
307 size_t rlen)
Dung Caofaf6a6a2020-12-28 04:44:45 +0000308{
Patrick Williams8b050c92023-10-20 11:19:32 -0500309 if (ecRd || rlen < 2)
310 {
311 channelAbort("Failed to read req msg", ecRd);
312 return;
313 }
Ed Tanousa1585be2024-02-14 10:57:31 -0800314 asyncRead();
Dung Caofaf6a6a2020-12-28 04:44:45 +0000315
Ed Tanousa1585be2024-02-14 10:57:31 -0800316 const auto* rawIter = xferBuffer.cbegin();
317 const auto* rawEnd = rawIter + rlen;
Patrick Williams8b050c92023-10-20 11:19:32 -0500318 uint8_t netfn = rawIter[sizeofLenField] >> netFnShift;
319 uint8_t lun = rawIter[sizeofLenField] & lunMask;
320 uint8_t cmd = rawIter[sizeofLenField + 1];
quang.ampere14480ee2022-10-18 09:04:51 +0700321
Patrick Williams8b050c92023-10-20 11:19:32 -0500322 /* keep track of previous request */
Ed Tanousa1585be2024-02-14 10:57:31 -0800323 prevReqCmd.netfn = netfn;
324 prevReqCmd.lun = lun;
325 prevReqCmd.cmd = cmd;
quang.ampere14480ee2022-10-18 09:04:51 +0700326
Patrick Williams8b050c92023-10-20 11:19:32 -0500327 /* there is a request coming */
328 numberOfReqNotRsp++;
329 /* start response timer */
Ed Tanous4ae3b9e2024-02-14 08:54:03 -0800330 rspTimer.expires_after(std::chrono::microseconds(hostReqTimeout));
331 rspTimer.async_wait(rspTimerHandler);
quang.name507782a2022-11-20 17:05:20 +0700332
Patrick Williams8b050c92023-10-20 11:19:32 -0500333 if (verbose)
334 {
Ed Tanousa1585be2024-02-14 10:57:31 -0800335 unsigned int lenRecv = 0;
Patrick Williams8b050c92023-10-20 11:19:32 -0500336 unsigned int* p = (unsigned int*)rawIter;
337 lenRecv = p[0];
338 std::string msgToLog =
339 "Read ssif request message with"
340 " len=" +
341 std::to_string(lenRecv) + " netfn=" + std::to_string(netfn) +
342 " lun=" + std::to_string(lun) + " cmd=" + std::to_string(cmd) +
343 " numberOfReqNotRsp=" + std::to_string(numberOfReqNotRsp);
344 log<level::INFO>(msgToLog.c_str());
345 }
346 // copy out payload
347 std::vector<uint8_t> data(rawIter + sizeofLenField + 2, rawEnd);
348 // non-session bridges still need to pass an empty options map
349 std::map<std::string, std::variant<int>> options;
Patrick Williams8b050c92023-10-20 11:19:32 -0500350 static constexpr const char ipmiQueueService[] =
351 "xyz.openbmc_project.Ipmi.Host";
352 static constexpr const char ipmiQueuePath[] = "/xyz/openbmc_project/Ipmi";
353 static constexpr const char ipmiQueueIntf[] =
354 "xyz.openbmc_project.Ipmi.Server";
355 static constexpr const char ipmiQueueMethod[] = "execute";
356 /* now, we do not care dbus timeout, since we already have actions
357 * before dbus timeout occurs
358 */
359 static constexpr unsigned int dbusTimeout = 60000000;
360 bus->async_method_call_timed(
Ed Tanousb6ad9dc2024-02-14 09:28:20 -0800361 [this](const boost::system::error_code& ec,
362 const IpmiDbusRspType& response) {
363 afterMethodCall(ec, response);
Patrick Williams8b050c92023-10-20 11:19:32 -0500364 },
365 ipmiQueueService, ipmiQueuePath, ipmiQueueIntf, ipmiQueueMethod,
366 dbusTimeout, netfn, lun, cmd, data, options);
Dung Caofaf6a6a2020-12-28 04:44:45 +0000367}
368
Patrick Williams8b050c92023-10-20 11:19:32 -0500369int main(int argc, char* argv[])
Dung Caofaf6a6a2020-12-28 04:44:45 +0000370{
Patrick Williams8b050c92023-10-20 11:19:32 -0500371 CLI::App app("SSIF IPMI bridge");
372 std::string device;
373 app.add_option("-d,--device", device,
374 "use <DEVICE> file. Default is /dev/ipmi-ssif-host");
375 bool verbose = false;
376 int numberOfReqNotRsp = 0;
377 app.add_option("-v,--verbose", verbose, "print more verbose output");
378 CLI11_PARSE(app, argc, argv);
Dung Caofaf6a6a2020-12-28 04:44:45 +0000379
Patrick Williams8b050c92023-10-20 11:19:32 -0500380 auto io = std::make_shared<boost::asio::io_context>();
Dung Caofaf6a6a2020-12-28 04:44:45 +0000381
Ed Tanousd289aea2024-02-06 20:33:26 -0800382 auto bus = std::make_shared<sdbusplus::asio::connection>(*io);
Patrick Williams8b050c92023-10-20 11:19:32 -0500383 bus->request_name(ssifBus);
384 // Create the SSIF channel, listening on D-Bus and on the SSIF device
385 ssifchannel = make_unique<SsifChannel>(io, bus, device, verbose,
386 numberOfReqNotRsp);
387 if (!ssifchannel->initOK())
388 {
389 return EXIT_FAILURE;
390 }
Patrick Williams8b050c92023-10-20 11:19:32 -0500391 io->run();
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700392
Patrick Williams8b050c92023-10-20 11:19:32 -0500393 return 0;
Dung Caofaf6a6a2020-12-28 04:44:45 +0000394}