blob: 3a07addff78e693cf4a1476ffed92217bcc32e18 [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 */
35#define IPMI_SSIF_PAYLOAD_MAX 254
36
Dung Caofaf6a6a2020-12-28 04:44:45 +000037using namespace phosphor::logging;
38
quang.ampere14480ee2022-10-18 09:04:51 +070039struct ipmi_cmd {
40 uint8_t netfn;
41 uint8_t lun;
42 uint8_t cmd;
43} 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 */
47static 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
56class SsifChannel
57{
58 public:
Dung Cao51835392022-10-27 04:29:54 +000059 static constexpr size_t ssifMessageSize = IPMI_SSIF_PAYLOAD_MAX +
60 sizeof(unsigned int);
61 size_t sizeofLenField = sizeof(unsigned int);
Dung Caofaf6a6a2020-12-28 04:44:45 +000062 static constexpr uint8_t netFnShift = 2;
63 static constexpr uint8_t lunMask = (1 << netFnShift) - 1;
64
65 SsifChannel(std::shared_ptr<boost::asio::io_context>& io,
66 std::shared_ptr<sdbusplus::asio::connection>& bus,
quang.name507782a2022-11-20 17:05:20 +070067 const std::string& channel, bool verbose,
68 int numberOfReqNotRsp);
Dung Caofaf6a6a2020-12-28 04:44:45 +000069 bool initOK() const
70 {
71 return !!dev;
72 }
73 void channelAbort(const char* msg, const boost::system::error_code& ec);
74 void async_read();
75 void processMessage(const boost::system::error_code& ecRd, size_t rlen);
Quang Nguyenf2994dc2022-11-25 16:31:49 +070076 int showNumOfReqNotRsp();
77 std::unique_ptr<boost::asio::posix::stream_descriptor> dev = nullptr;
Dung Caofaf6a6a2020-12-28 04:44:45 +000078
79 protected:
80 std::array<uint8_t, ssifMessageSize> xferBuffer;
81 std::shared_ptr<boost::asio::io_context> io;
82 std::shared_ptr<sdbusplus::asio::connection> bus;
83 std::shared_ptr<sdbusplus::asio::object_server> server;
Dung Caofaf6a6a2020-12-28 04:44:45 +000084 bool verbose;
quang.name507782a2022-11-20 17:05:20 +070085 /* This variable is always 0 when a request is responsed properly,
86 * any value larger than 0 meaning there is/are request(s) which
87 * not processed properly
88 * */
89 int numberOfReqNotRsp;
Dung Caofaf6a6a2020-12-28 04:44:45 +000090};
91
Quang Nguyenf2994dc2022-11-25 16:31:49 +070092std::unique_ptr<phosphor::Timer> rspTimer
93 __attribute__((init_priority(101)));
94std::unique_ptr<SsifChannel> ssifchannel = nullptr;
95
Dung Caofaf6a6a2020-12-28 04:44:45 +000096SsifChannel::SsifChannel(std::shared_ptr<boost::asio::io_context>& io,
97 std::shared_ptr<sdbusplus::asio::connection>& bus,
quang.name507782a2022-11-20 17:05:20 +070098 const std::string& device, bool verbose, int numberOfReqNotRsp) :
Dung Caofaf6a6a2020-12-28 04:44:45 +000099 io(io),
quang.name507782a2022-11-20 17:05:20 +0700100 bus(bus), verbose(verbose), numberOfReqNotRsp(numberOfReqNotRsp)
Dung Caofaf6a6a2020-12-28 04:44:45 +0000101{
102 std::string devName = devBase;
103 if (!device.empty())
104 {
105 devName = device;
106 }
107
108 // open device
109 int fd = open(devName.c_str(), O_RDWR | O_NONBLOCK);
110 if (fd < 0)
111 {
Dung Caof06b8fd2021-01-29 03:31:43 +0000112 std::string msgToLog = "Couldn't open SSIF driver with flags O_RDWR."
113 " FILENAME=" + devName +
114 " ERROR=" + strerror(errno);
115 log<level::ERR>(msgToLog.c_str());
Dung Caofaf6a6a2020-12-28 04:44:45 +0000116 return;
117 }
118 else
119 {
120 dev = std::make_unique<boost::asio::posix::stream_descriptor>(*io,
121 fd);
122 }
123
124 async_read();
125 // register interfaces...
126 server = std::make_shared<sdbusplus::asio::object_server>(bus);
127 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
128 server->add_interface(ssifObj, ssifBus);
129 iface->initialize();
130}
131
132void SsifChannel::channelAbort(const char* msg,
133 const boost::system::error_code& ec)
134{
Dung Caof06b8fd2021-01-29 03:31:43 +0000135 std::string msgToLog = std::string(msg) + " ERROR=" + ec.message();
136 log<level::ERR>(msgToLog.c_str());
Dung Caofaf6a6a2020-12-28 04:44:45 +0000137 // bail; maybe a restart from systemd can clear the error
138 io->stop();
139}
140
141void SsifChannel::async_read()
142{
143 boost::asio::async_read(*dev,
144 boost::asio::buffer(xferBuffer, xferBuffer.size()),
145 boost::asio::transfer_at_least(2),
146 [this](const boost::system::error_code& ec,
147 size_t rlen) {
148 processMessage(ec, rlen);
149 });
150}
151
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700152int SsifChannel::showNumOfReqNotRsp()
153{
154 return numberOfReqNotRsp;
155}
156
157void rspTimerHandler()
158{
159 std::vector<uint8_t> rsp;
160 constexpr uint8_t ccResponseNotAvailable = 0xce;
161
162 rsp.resize(ssifchannel->sizeofLenField +
163 sizeof(prev_req_cmd.cmd) +
164 sizeof(prev_req_cmd.netfn) +
165 sizeof(ccResponseNotAvailable));
166 std::string msgToLog = "timeout, send response to keep host alive"
167 " netfn=" + std::to_string(prev_req_cmd.netfn) +
168 " lun=" + std::to_string(prev_req_cmd.lun) +
169 " cmd=" + std::to_string(prev_req_cmd.cmd) +
170 " cc=" + std::to_string(ccResponseNotAvailable) +
171 " numberOfReqNotRsp=" +
172 std::to_string(ssifchannel->showNumOfReqNotRsp());
173 log<level::INFO>(msgToLog.c_str());
174
175 unsigned int *t = (unsigned int *)&rsp[0];
176 *t = 3;
177 rsp[ssifchannel->sizeofLenField] =
178 ((prev_req_cmd.netfn + 1) << ssifchannel->netFnShift) |
179 (prev_req_cmd.lun & ssifchannel->lunMask);
180 rsp[ssifchannel->sizeofLenField + 1] = prev_req_cmd.cmd;
181 rsp[ssifchannel->sizeofLenField + 2] = ccResponseNotAvailable;
182
183 boost::system::error_code ecWr;
184
185 size_t wlen =
186 boost::asio::write(*(ssifchannel->dev),
187 boost::asio::buffer(rsp), ecWr);
188 if (ecWr || wlen != rsp.size())
189 {
190 msgToLog = "Failed to send ssif respond message:"
191 " size=" + std::to_string(wlen) +
192 " expect=" + std::to_string(rsp.size()) +
193 " error=" + ecWr.message() +
194 " netfn=" + std::to_string(prev_req_cmd.netfn + 1) +
195 " lun=" + std::to_string(prev_req_cmd.lun) +
196 " cmd=" +
197 std::to_string(rsp[ssifchannel->sizeofLenField + 1]) +
198 " cc=" + std::to_string(ccResponseNotAvailable);
199 log<level::ERR>(msgToLog.c_str());
200 }
201}
202
203void initTimer()
204{
205 if (!rspTimer)
206 {
207 rspTimer =
208 std::make_unique<phosphor::Timer>(rspTimerHandler);
209 }
210}
211
Dung Caofaf6a6a2020-12-28 04:44:45 +0000212void SsifChannel::processMessage(const boost::system::error_code& ecRd,
213 size_t rlen)
214{
215 if (ecRd || rlen < 2)
216 {
217 channelAbort("Failed to read req msg", ecRd);
218 return;
219 }
220 async_read();
221
222 auto rawIter = xferBuffer.cbegin();
223 auto rawEnd = rawIter + rlen;
Dung Cao51835392022-10-27 04:29:54 +0000224 uint8_t netfn = rawIter[sizeofLenField] >> netFnShift;
225 uint8_t lun = rawIter[sizeofLenField] & lunMask;
226 uint8_t cmd = rawIter[sizeofLenField + 1];
quang.ampere14480ee2022-10-18 09:04:51 +0700227
quang.name507782a2022-11-20 17:05:20 +0700228 /* keep track of previous request */
quang.ampere14480ee2022-10-18 09:04:51 +0700229 prev_req_cmd.netfn = netfn;
230 prev_req_cmd.lun = lun;
231 prev_req_cmd.cmd = cmd;
232
quang.name507782a2022-11-20 17:05:20 +0700233 /* there is a request coming */
234 numberOfReqNotRsp++;
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700235 /* start response timer */
236 rspTimer->start(std::chrono::microseconds(hostReqTimeout), false);
quang.name507782a2022-11-20 17:05:20 +0700237
Dung Caofaf6a6a2020-12-28 04:44:45 +0000238 if (verbose)
239 {
Dung Cao51835392022-10-27 04:29:54 +0000240 unsigned int lenRecv;
241 unsigned int *p = (unsigned int *) rawIter;
242 lenRecv = p[0];
Dung Caof06b8fd2021-01-29 03:31:43 +0000243 std::string msgToLog = "Read ssif request message with"
Dung Cao51835392022-10-27 04:29:54 +0000244 " len=" + std::to_string(lenRecv) +
Dung Caof06b8fd2021-01-29 03:31:43 +0000245 " netfn=" + std::to_string(netfn) +
246 " lun=" + std::to_string(lun) +
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700247 " cmd=" + std::to_string(cmd) +
248 " numberOfReqNotRsp=" +
249 std::to_string(numberOfReqNotRsp);
Dung Caof06b8fd2021-01-29 03:31:43 +0000250 log<level::INFO>(msgToLog.c_str());
Dung Caofaf6a6a2020-12-28 04:44:45 +0000251 }
252 // copy out payload
Dung Cao51835392022-10-27 04:29:54 +0000253 std::vector<uint8_t> data(rawIter + sizeofLenField + 2, rawEnd);
Dung Caofaf6a6a2020-12-28 04:44:45 +0000254 // non-session bridges still need to pass an empty options map
255 std::map<std::string, std::variant<int>> options;
256 // the response is a tuple because dbus can only return a single value
257 using IpmiDbusRspType = std::tuple<uint8_t, uint8_t, uint8_t, uint8_t,
258 std::vector<uint8_t>>;
259 static constexpr const char ipmiQueueService[] =
260 "xyz.openbmc_project.Ipmi.Host";
261 static constexpr const char ipmiQueuePath[] =
262 "/xyz/openbmc_project/Ipmi";
263 static constexpr const char ipmiQueueIntf[] =
264 "xyz.openbmc_project.Ipmi.Server";
265 static constexpr const char ipmiQueueMethod[] = "execute";
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700266 /* now, we do not care dbus timeout, since we already have actions
267 * before dbus timeout occurs
268 */
269 static constexpr unsigned int dbusTimeout = 60000000;
quang.ampere14480ee2022-10-18 09:04:51 +0700270 bus->async_method_call_timed(
Dung Caofaf6a6a2020-12-28 04:44:45 +0000271 [this, netfnCap{netfn}, lunCap{lun},
272 cmdCap{cmd}](const boost::system::error_code& ec,
273 const IpmiDbusRspType& response) {
274 std::vector<uint8_t> rsp;
275 const auto& [netfn, lun, cmd, cc, payload] = response;
quang.name507782a2022-11-20 17:05:20 +0700276 numberOfReqNotRsp--;
Dung Caofaf6a6a2020-12-28 04:44:45 +0000277 if (ec)
278 {
Dung Caof06b8fd2021-01-29 03:31:43 +0000279 std::string msgToLog = "ssif<->ipmid bus error:"
280 " netfn=" + std::to_string(netfn) +
281 " lun=" + std::to_string(lun) +
282 " cmd=" + std::to_string(cmd) +
283 " error=" + ec.message();
284 log<level::ERR>(msgToLog.c_str());
Dung Cao51835392022-10-27 04:29:54 +0000285 rsp.resize(sizeofLenField + sizeof(netfn) + sizeof(cmd) +
Dung Caofaf6a6a2020-12-28 04:44:45 +0000286 sizeof(cc));
quang.ampere14480ee2022-10-18 09:04:51 +0700287 /* if dbusTimeout, just return and do not send any response
288 * to let host continue with other commands, response here
quang.name507782a2022-11-20 17:05:20 +0700289 * is potentially make the response duplicated
quang.ampere14480ee2022-10-18 09:04:51 +0700290 * */
291 return;
Dung Caofaf6a6a2020-12-28 04:44:45 +0000292 }
293 else
294 {
quang.name507782a2022-11-20 17:05:20 +0700295 if ((prev_req_cmd.netfn != (netfn-1) ||
quang.ampere14480ee2022-10-18 09:04:51 +0700296 prev_req_cmd.lun != lun ||
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700297 prev_req_cmd.cmd != cmd) ||
298 ((prev_req_cmd.netfn == (netfn-1) &&
299 prev_req_cmd.lun == lun &&
300 prev_req_cmd.cmd == cmd) &&
301 numberOfReqNotRsp != 0))
quang.ampere14480ee2022-10-18 09:04:51 +0700302 {
303 /* Only send response to the last request command to void
304 * duplicated response which makes host driver confused and
305 * failed to create interface
quang.name507782a2022-11-20 17:05:20 +0700306 *
307 * Drop responses which are (1) different from the request
308 * (2) parameters are the same as request but handshake flow
309 * are in dupplicate request state
quang.ampere14480ee2022-10-18 09:04:51 +0700310 * */
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700311 if (verbose)
312 {
313 std::string msgToLog = "Drop ssif respond message with"
314 " len=" + 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 }
quang.ampere14480ee2022-10-18 09:04:51 +0700323 return;
324 }
Dung Cao51835392022-10-27 04:29:54 +0000325 rsp.resize(sizeofLenField + sizeof(netfn) + sizeof(cmd) +
Dung Caofaf6a6a2020-12-28 04:44:45 +0000326 sizeof(cc) + payload.size());
327
328 // write the response
329 auto rspIter = rsp.begin();
Dung Cao51835392022-10-27 04:29:54 +0000330 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;
Dung Caofaf6a6a2020-12-28 04:44:45 +0000335 if (payload.size())
336 {
337 std::copy(payload.cbegin(), payload.cend(),
Dung Cao51835392022-10-27 04:29:54 +0000338 rspIter + sizeofLenField + 3);
Dung Caofaf6a6a2020-12-28 04:44:45 +0000339 }
340 }
341 if (verbose)
342 {
Dung Caof06b8fd2021-01-29 03:31:43 +0000343 std::string msgToLog = "Send ssif respond message with"
344 " len=" + std::to_string(payload.size() + 3) +
345 " netfn=" + std::to_string(netfn) +
346 " lun=" + std::to_string(lun) +
347 " cmd=" + std::to_string(cmd) +
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700348 " cc=" + std::to_string(cc) +
349 " numberOfReqNotRsp=" +
350 std::to_string(numberOfReqNotRsp);
Dung Caof06b8fd2021-01-29 03:31:43 +0000351 log<level::INFO>(msgToLog.c_str());
Dung Caofaf6a6a2020-12-28 04:44:45 +0000352 }
353 boost::system::error_code ecWr;
354 size_t wlen =
355 boost::asio::write(*dev, boost::asio::buffer(rsp), ecWr);
356 if (ecWr || wlen != rsp.size())
357 {
Dung Caof06b8fd2021-01-29 03:31:43 +0000358 std::string msgToLog = "Failed to send ssif respond message:"
359 " size=" + std::to_string(wlen) +
360 " expect=" + std::to_string(rsp.size()) +
361 " error=" + ecWr.message() +
362 " netfn=" + std::to_string(netfn) +
363 " lun=" + std::to_string(lun) +
364 " cmd=" + std::to_string(cmd) +
365 " cc=" + std::to_string(cc);
366 log<level::ERR>(msgToLog.c_str());
Dung Caofaf6a6a2020-12-28 04:44:45 +0000367 }
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700368 rspTimer->stop();
Dung Caofaf6a6a2020-12-28 04:44:45 +0000369 },
quang.ampere14480ee2022-10-18 09:04:51 +0700370 ipmiQueueService, ipmiQueuePath, ipmiQueueIntf, ipmiQueueMethod, dbusTimeout,
Dung Caofaf6a6a2020-12-28 04:44:45 +0000371 netfn, lun, cmd, data, options);
372}
373
374
375int main(int argc, char* argv[])
376{
377 CLI::App app("SSIF IPMI bridge");
378 std::string device;
379 app.add_option("-d,--device", device,
380 "use <DEVICE> file. Default is /dev/ipmi-ssif-host");
381 bool verbose = false;
quang.name507782a2022-11-20 17:05:20 +0700382 int numberOfReqNotRsp = 0;
Dung Caofaf6a6a2020-12-28 04:44:45 +0000383 app.add_option("-v,--verbose", verbose, "print more verbose output");
384 CLI11_PARSE(app, argc, argv);
385
386 // Connect to system bus
387 auto io = std::make_shared<boost::asio::io_context>();
388 sd_bus* dbus;
389 sd_bus_default_system(&dbus);
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700390
391 /* This might be a phosphor::Timer bug, without timer t2, rspTimer
392 * will not work
393 * */
394 phosphor::Timer t2([]() { ; });
395 t2.start(std::chrono::microseconds(500000), true);
Dung Caofaf6a6a2020-12-28 04:44:45 +0000396 auto bus = std::make_shared<sdbusplus::asio::connection>(*io, dbus);
397 bus->request_name(ssifBus);
398 // Create the SSIF channel, listening on D-Bus and on the SSIF device
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700399 ssifchannel = make_unique<SsifChannel>
400 (io, bus, device, verbose, numberOfReqNotRsp);
401 if (!ssifchannel->initOK())
Dung Caofaf6a6a2020-12-28 04:44:45 +0000402 {
403 return EXIT_FAILURE;
404 }
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700405 initTimer();
406 sdbusplus::asio::sd_event_wrapper sdEvents(*io);
Dung Caofaf6a6a2020-12-28 04:44:45 +0000407 io->run();
408
409 return 0;
410}