blob: ee30df9c277d3aa84612e6f11d770c9983b48b01 [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>
25#include <boost/asio.hpp>
26#include <phosphor-logging/log.hpp>
27#include <sdbusplus/asio/connection.hpp>
28#include <sdbusplus/asio/object_server.hpp>
Quang Nguyenf2994dc2022-11-25 16:31:49 +070029#include <sdbusplus/timer.hpp>
30#include <sdbusplus/asio/sd_event.hpp>
Dung Caofaf6a6a2020-12-28 04:44:45 +000031
32#include <iostream>
33
Dung Cao51835392022-10-27 04:29:54 +000034/* Max length of ipmi ssif message included netfn and cmd field */
Thang Q. Nguyen4300d212023-05-15 12:22:53 +070035#define IPMI_SSIF_PAYLOAD_MAX 254
Dung Cao51835392022-10-27 04:29:54 +000036
Dung Caofaf6a6a2020-12-28 04:44:45 +000037using namespace phosphor::logging;
38
quang.ampere14480ee2022-10-18 09:04:51 +070039struct ipmi_cmd {
Thang Q. Nguyen4300d212023-05-15 12:22:53 +070040 uint8_t netfn;
41 uint8_t lun;
42 uint8_t cmd;
quang.ampere14480ee2022-10-18 09:04:51 +070043} prev_req_cmd;
44
Dung Caofaf6a6a2020-12-28 04:44:45 +000045static constexpr const char devBase[] = "/dev/ipmi-ssif-host";
46/* SSIF use IPMI SSIF channel */
Thang Q. Nguyen4300d212023-05-15 12:22:53 +070047static constexpr const char *ssifBus =
48 "xyz.openbmc_project.Ipmi.Channel.ipmi_ssif";
49static constexpr const char *ssifObj =
50 "/xyz/openbmc_project/Ipmi/Channel/ipmi_ssif";
Quang Nguyenf2994dc2022-11-25 16:31:49 +070051/* The timer of driver is set to 15 seconds, need to send
52 * response before timeout occurs
53 */
54static constexpr const unsigned int hostReqTimeout = 14000000;
Dung Caofaf6a6a2020-12-28 04:44:45 +000055
Thang Q. Nguyen4300d212023-05-15 12:22:53 +070056class SsifChannel {
57 public:
58 static constexpr size_t ssifMessageSize =
59 IPMI_SSIF_PAYLOAD_MAX + sizeof(unsigned int);
60 size_t sizeofLenField = sizeof(unsigned int);
61 static constexpr uint8_t netFnShift = 2;
62 static constexpr uint8_t lunMask = (1 << netFnShift) - 1;
Dung Caofaf6a6a2020-12-28 04:44:45 +000063
Thang Q. Nguyen4300d212023-05-15 12:22:53 +070064 SsifChannel(std::shared_ptr<boost::asio::io_context> &io,
65 std::shared_ptr<sdbusplus::asio::connection> &bus,
66 const std::string &channel, bool verbose,
67 int numberOfReqNotRsp);
68 bool initOK() const
69 {
70 return !!dev;
71 }
72 void channelAbort(const char *msg, const boost::system::error_code &ec);
73 void async_read();
74 void processMessage(const boost::system::error_code &ecRd, size_t rlen);
75 int showNumOfReqNotRsp();
76 std::unique_ptr<boost::asio::posix::stream_descriptor> dev = nullptr;
Dung Caofaf6a6a2020-12-28 04:44:45 +000077
Thang Q. Nguyen4300d212023-05-15 12:22:53 +070078 protected:
79 std::array<uint8_t, ssifMessageSize> xferBuffer;
80 std::shared_ptr<boost::asio::io_context> io;
81 std::shared_ptr<sdbusplus::asio::connection> bus;
82 std::shared_ptr<sdbusplus::asio::object_server> server;
83 bool verbose;
84 /* This variable is always 0 when a request is responsed properly,
85 * any value larger than 0 meaning there is/are request(s) which
86 * not processed properly
87 * */
88 int numberOfReqNotRsp;
Dung Caofaf6a6a2020-12-28 04:44:45 +000089};
90
Thang Q. Nguyen4300d212023-05-15 12:22:53 +070091std::unique_ptr<phosphor::Timer> rspTimer __attribute__((init_priority(101)));
Quang Nguyenf2994dc2022-11-25 16:31:49 +070092std::unique_ptr<SsifChannel> ssifchannel = nullptr;
93
Thang Q. Nguyen4300d212023-05-15 12:22:53 +070094SsifChannel::SsifChannel(std::shared_ptr<boost::asio::io_context> &io,
95 std::shared_ptr<sdbusplus::asio::connection> &bus,
96 const std::string &device, bool verbose,
97 int numberOfReqNotRsp)
98 : io(io), bus(bus), verbose(verbose),
99 numberOfReqNotRsp(numberOfReqNotRsp)
Dung Caofaf6a6a2020-12-28 04:44:45 +0000100{
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700101 std::string devName = devBase;
102 if (!device.empty()) {
103 devName = device;
104 }
Dung Caofaf6a6a2020-12-28 04:44:45 +0000105
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700106 // open device
107 int fd = open(devName.c_str(), O_RDWR | O_NONBLOCK);
108 if (fd < 0) {
109 std::string msgToLog =
110 "Couldn't open SSIF driver with flags O_RDWR."
111 " FILENAME=" +
112 devName + " ERROR=" + strerror(errno);
113 log<level::ERR>(msgToLog.c_str());
114 return;
115 } else {
116 dev = std::make_unique<boost::asio::posix::stream_descriptor>(
117 *io, fd);
118 }
Dung Caofaf6a6a2020-12-28 04:44:45 +0000119
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700120 async_read();
121 // register interfaces...
122 server = std::make_shared<sdbusplus::asio::object_server>(bus);
123 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
124 server->add_interface(ssifObj, ssifBus);
125 iface->initialize();
Dung Caofaf6a6a2020-12-28 04:44:45 +0000126}
127
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700128void SsifChannel::channelAbort(const char *msg,
129 const boost::system::error_code &ec)
Dung Caofaf6a6a2020-12-28 04:44:45 +0000130{
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700131 std::string msgToLog = std::string(msg) + " ERROR=" + ec.message();
132 log<level::ERR>(msgToLog.c_str());
133 // bail; maybe a restart from systemd can clear the error
134 io->stop();
Dung Caofaf6a6a2020-12-28 04:44:45 +0000135}
136
137void SsifChannel::async_read()
138{
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700139 boost::asio::async_read(
140 *dev, boost::asio::buffer(xferBuffer, xferBuffer.size()),
141 boost::asio::transfer_at_least(2),
142 [this](const boost::system::error_code &ec, size_t rlen) {
143 processMessage(ec, rlen);
144 });
Dung Caofaf6a6a2020-12-28 04:44:45 +0000145}
146
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700147int SsifChannel::showNumOfReqNotRsp()
148{
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700149 return numberOfReqNotRsp;
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700150}
151
152void rspTimerHandler()
153{
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700154 std::vector<uint8_t> rsp;
155 constexpr uint8_t ccResponseNotAvailable = 0xce;
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700156
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700157 rsp.resize(ssifchannel->sizeofLenField + sizeof(prev_req_cmd.cmd) +
158 sizeof(prev_req_cmd.netfn) + sizeof(ccResponseNotAvailable));
159 std::string msgToLog =
160 "timeout, send response to keep host alive"
161 " netfn=" +
162 std::to_string(prev_req_cmd.netfn) +
163 " lun=" + std::to_string(prev_req_cmd.lun) +
164 " cmd=" + std::to_string(prev_req_cmd.cmd) +
165 " cc=" + std::to_string(ccResponseNotAvailable) +
166 " numberOfReqNotRsp=" +
167 std::to_string(ssifchannel->showNumOfReqNotRsp());
168 log<level::INFO>(msgToLog.c_str());
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700169
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700170 unsigned int *t = (unsigned int *)&rsp[0];
171 *t = 3;
172 rsp[ssifchannel->sizeofLenField] =
173 ((prev_req_cmd.netfn + 1) << ssifchannel->netFnShift) |
174 (prev_req_cmd.lun & ssifchannel->lunMask);
175 rsp[ssifchannel->sizeofLenField + 1] = prev_req_cmd.cmd;
176 rsp[ssifchannel->sizeofLenField + 2] = ccResponseNotAvailable;
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700177
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700178 boost::system::error_code ecWr;
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700179
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700180 size_t wlen = boost::asio::write(*(ssifchannel->dev),
181 boost::asio::buffer(rsp), ecWr);
182 if (ecWr || wlen != rsp.size()) {
183 msgToLog =
184 "Failed to send ssif respond message:"
185 " size=" +
186 std::to_string(wlen) +
187 " expect=" + std::to_string(rsp.size()) +
188 " error=" + ecWr.message() +
189 " netfn=" + std::to_string(prev_req_cmd.netfn + 1) +
190 " lun=" + std::to_string(prev_req_cmd.lun) + " cmd=" +
191 std::to_string(rsp[ssifchannel->sizeofLenField + 1]) +
192 " cc=" + std::to_string(ccResponseNotAvailable);
193 log<level::ERR>(msgToLog.c_str());
194 }
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700195}
196
197void initTimer()
198{
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700199 if (!rspTimer) {
200 rspTimer = std::make_unique<phosphor::Timer>(rspTimerHandler);
201 }
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700202}
203
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700204void SsifChannel::processMessage(const boost::system::error_code &ecRd,
205 size_t rlen)
Dung Caofaf6a6a2020-12-28 04:44:45 +0000206{
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700207 if (ecRd || rlen < 2) {
208 channelAbort("Failed to read req msg", ecRd);
209 return;
210 }
211 async_read();
Dung Caofaf6a6a2020-12-28 04:44:45 +0000212
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700213 auto rawIter = xferBuffer.cbegin();
214 auto rawEnd = rawIter + rlen;
215 uint8_t netfn = rawIter[sizeofLenField] >> netFnShift;
216 uint8_t lun = rawIter[sizeofLenField] & lunMask;
217 uint8_t cmd = rawIter[sizeofLenField + 1];
quang.ampere14480ee2022-10-18 09:04:51 +0700218
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700219 /* keep track of previous request */
220 prev_req_cmd.netfn = netfn;
221 prev_req_cmd.lun = lun;
222 prev_req_cmd.cmd = cmd;
quang.ampere14480ee2022-10-18 09:04:51 +0700223
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700224 /* there is a request coming */
225 numberOfReqNotRsp++;
226 /* start response timer */
227 rspTimer->start(std::chrono::microseconds(hostReqTimeout), false);
quang.name507782a2022-11-20 17:05:20 +0700228
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700229 if (verbose) {
230 unsigned int lenRecv;
231 unsigned int *p = (unsigned int *)rawIter;
232 lenRecv = p[0];
233 std::string msgToLog = "Read ssif request message with"
234 " len=" +
235 std::to_string(lenRecv) +
236 " netfn=" + std::to_string(netfn) +
237 " lun=" + std::to_string(lun) +
238 " cmd=" + std::to_string(cmd) +
239 " numberOfReqNotRsp=" +
240 std::to_string(numberOfReqNotRsp);
241 log<level::INFO>(msgToLog.c_str());
242 }
243 // copy out payload
244 std::vector<uint8_t> data(rawIter + sizeofLenField + 2, rawEnd);
245 // non-session bridges still need to pass an empty options map
246 std::map<std::string, std::variant<int> > options;
247 // the response is a tuple because dbus can only return a single value
248 using IpmiDbusRspType = std::tuple<uint8_t, uint8_t, uint8_t, uint8_t,
249 std::vector<uint8_t> >;
250 static constexpr const char ipmiQueueService[] =
251 "xyz.openbmc_project.Ipmi.Host";
252 static constexpr const char ipmiQueuePath[] =
253 "/xyz/openbmc_project/Ipmi";
254 static constexpr const char ipmiQueueIntf[] =
255 "xyz.openbmc_project.Ipmi.Server";
256 static constexpr const char ipmiQueueMethod[] = "execute";
257 /* now, we do not care dbus timeout, since we already have actions
258 * before dbus timeout occurs
259 */
260 static constexpr unsigned int dbusTimeout = 60000000;
261 bus->async_method_call_timed(
262 [this, netfnCap{ netfn }, lunCap{ lun },
263 cmdCap{ cmd }](const boost::system::error_code &ec,
264 const IpmiDbusRspType &response) {
265 std::vector<uint8_t> rsp;
266 const auto &[netfn, lun, cmd, cc, payload] = response;
267 numberOfReqNotRsp--;
268 if (ec) {
269 std::string msgToLog =
270 "ssif<->ipmid bus error:"
271 " netfn=" +
272 std::to_string(netfn) +
273 " lun=" + std::to_string(lun) +
274 " cmd=" + std::to_string(cmd) +
275 " error=" + ec.message();
276 log<level::ERR>(msgToLog.c_str());
277 rsp.resize(sizeofLenField + sizeof(netfn) +
278 sizeof(cmd) + sizeof(cc));
279 /* if dbusTimeout, just return and do not send any response
280 * to let host continue with other commands, response here
281 * is potentially make the response duplicated
282 * */
283 return;
284 } else {
285 if ((prev_req_cmd.netfn != (netfn - 1) ||
286 prev_req_cmd.lun != lun ||
287 prev_req_cmd.cmd != cmd) ||
288 ((prev_req_cmd.netfn == (netfn - 1) &&
289 prev_req_cmd.lun == lun &&
290 prev_req_cmd.cmd == cmd) &&
291 numberOfReqNotRsp != 0)) {
292 /* Only send response to the last request command to void
293 * duplicated response which makes host driver confused and
294 * failed to create interface
295 *
296 * Drop responses which are (1) different from the request
297 * (2) parameters are the same as request but handshake flow
298 * are in dupplicate request state
299 * */
300 if (verbose) {
301 std::string msgToLog =
302 "Drop ssif respond message with"
303 " len=" +
304 std::to_string(
305 payload.size() +
306 3) +
307 " netfn=" +
308 std::to_string(netfn) +
309 " lun=" +
310 std::to_string(lun) +
311 " cmd=" +
312 std::to_string(cmd) +
313 " cc=" +
314 std::to_string(cc) +
315 " numberOfReqNotRsp=" +
316 std::to_string(
317 numberOfReqNotRsp);
318 log<level::INFO>(
319 msgToLog.c_str());
320 }
321 return;
322 }
323 rsp.resize(sizeofLenField + sizeof(netfn) +
324 sizeof(cmd) + sizeof(cc) +
325 payload.size());
Dung Caofaf6a6a2020-12-28 04:44:45 +0000326
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700327 // write the response
328 auto rspIter = rsp.begin();
329 unsigned int *p = (unsigned int *)&rspIter[0];
330 *p = payload.size() + 3;
331 rspIter[sizeofLenField] =
332 (netfn << netFnShift) | (lun & lunMask);
333 rspIter[sizeofLenField + 1] = cmd;
334 rspIter[sizeofLenField + 2] = cc;
335 if (payload.size()) {
336 std::copy(payload.cbegin(),
337 payload.cend(),
338 rspIter + sizeofLenField + 3);
339 }
340 }
341 if (verbose) {
342 std::string msgToLog =
343 "Send ssif respond message with"
344 " len=" +
345 std::to_string(payload.size() + 3) +
346 " netfn=" + std::to_string(netfn) +
347 " lun=" + std::to_string(lun) +
348 " cmd=" + std::to_string(cmd) +
349 " cc=" + std::to_string(cc) +
350 " numberOfReqNotRsp=" +
351 std::to_string(numberOfReqNotRsp);
352 log<level::INFO>(msgToLog.c_str());
353 }
354 boost::system::error_code ecWr;
355 size_t wlen = boost::asio::write(
356 *dev, boost::asio::buffer(rsp), ecWr);
357 if (ecWr || wlen != rsp.size()) {
358 std::string msgToLog =
359 "Failed to send ssif respond message:"
360 " size=" +
361 std::to_string(wlen) + " expect=" +
362 std::to_string(rsp.size()) +
363 " error=" + ecWr.message() +
364 " netfn=" + std::to_string(netfn) +
365 " lun=" + std::to_string(lun) +
366 " cmd=" + std::to_string(cmd) +
367 " cc=" + std::to_string(cc);
368 log<level::ERR>(msgToLog.c_str());
369 }
370 rspTimer->stop();
371 },
372 ipmiQueueService, ipmiQueuePath, ipmiQueueIntf, ipmiQueueMethod,
373 dbusTimeout, netfn, lun, cmd, data, options);
Dung Caofaf6a6a2020-12-28 04:44:45 +0000374}
375
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700376int main(int argc, char *argv[])
Dung Caofaf6a6a2020-12-28 04:44:45 +0000377{
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700378 CLI::App app("SSIF IPMI bridge");
379 std::string device;
380 app.add_option("-d,--device", device,
381 "use <DEVICE> file. Default is /dev/ipmi-ssif-host");
382 bool verbose = false;
383 int numberOfReqNotRsp = 0;
384 app.add_option("-v,--verbose", verbose, "print more verbose output");
385 CLI11_PARSE(app, argc, argv);
Dung Caofaf6a6a2020-12-28 04:44:45 +0000386
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700387 // Connect to system bus
388 auto io = std::make_shared<boost::asio::io_context>();
389 sd_bus *dbus;
390 sd_bus_default_system(&dbus);
Dung Caofaf6a6a2020-12-28 04:44:45 +0000391
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700392 /* This might be a phosphor::Timer bug, without timer t2, rspTimer
393 * will not work
394 */
395 phosphor::Timer t2([]() { ; });
396 t2.start(std::chrono::microseconds(500000), true);
397 auto bus = std::make_shared<sdbusplus::asio::connection>(*io, dbus);
398 bus->request_name(ssifBus);
399 // Create the SSIF channel, listening on D-Bus and on the SSIF device
400 ssifchannel = make_unique<SsifChannel>(io, bus, device, verbose,
401 numberOfReqNotRsp);
402 if (!ssifchannel->initOK()) {
403 return EXIT_FAILURE;
404 }
405 initTimer();
406 sdbusplus::asio::sd_event_wrapper sdEvents(*io);
407 io->run();
408
409 return 0;
Dung Caofaf6a6a2020-12-28 04:44:45 +0000410}