blob: 7ed8c66a08d7777a12f03f69b3ae2f1bcac588d4 [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/asio/sd_event.hpp>
Patrick Williams8b050c92023-10-20 11:19:32 -050030#include <sdbusplus/timer.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
Patrick Williams8b050c92023-10-20 11:19:32 -050039struct ipmi_cmd
40{
41 uint8_t netfn;
42 uint8_t lun;
43 uint8_t cmd;
quang.ampere14480ee2022-10-18 09:04:51 +070044} prev_req_cmd;
45
Dung Caofaf6a6a2020-12-28 04:44:45 +000046static constexpr const char devBase[] = "/dev/ipmi-ssif-host";
47/* SSIF use IPMI SSIF channel */
Patrick Williams8b050c92023-10-20 11:19:32 -050048static constexpr const char* ssifBus =
49 "xyz.openbmc_project.Ipmi.Channel.ipmi_ssif";
50static constexpr const char* ssifObj =
51 "/xyz/openbmc_project/Ipmi/Channel/ipmi_ssif";
Quang Nguyenf2994dc2022-11-25 16:31:49 +070052/* The timer of driver is set to 15 seconds, need to send
53 * response before timeout occurs
54 */
55static constexpr const unsigned int hostReqTimeout = 14000000;
Dung Caofaf6a6a2020-12-28 04:44:45 +000056
Patrick Williams8b050c92023-10-20 11:19:32 -050057class SsifChannel
58{
59 public:
60 static constexpr size_t ssifMessageSize = IPMI_SSIF_PAYLOAD_MAX +
61 sizeof(unsigned int);
62 size_t sizeofLenField = sizeof(unsigned int);
63 static constexpr uint8_t netFnShift = 2;
64 static constexpr uint8_t lunMask = (1 << netFnShift) - 1;
Dung Caofaf6a6a2020-12-28 04:44:45 +000065
Patrick Williams8b050c92023-10-20 11:19:32 -050066 SsifChannel(std::shared_ptr<boost::asio::io_context>& io,
67 std::shared_ptr<sdbusplus::asio::connection>& bus,
68 const std::string& channel, bool verbose,
69 int numberOfReqNotRsp);
70 bool initOK() const
71 {
72 return !!dev;
73 }
74 void channelAbort(const char* msg, const boost::system::error_code& ec);
75 void async_read();
76 void processMessage(const boost::system::error_code& ecRd, size_t rlen);
77 int showNumOfReqNotRsp();
78 std::unique_ptr<boost::asio::posix::stream_descriptor> dev = nullptr;
Dung Caofaf6a6a2020-12-28 04:44:45 +000079
Patrick Williams8b050c92023-10-20 11:19:32 -050080 protected:
81 std::array<uint8_t, ssifMessageSize> xferBuffer;
82 std::shared_ptr<boost::asio::io_context> io;
83 std::shared_ptr<sdbusplus::asio::connection> bus;
84 std::shared_ptr<sdbusplus::asio::object_server> server;
85 bool verbose;
86 /* This variable is always 0 when a request is responsed properly,
87 * any value larger than 0 meaning there is/are request(s) which
88 * not processed properly
89 * */
90 int numberOfReqNotRsp;
Dung Caofaf6a6a2020-12-28 04:44:45 +000091};
92
Thang Q. Nguyen4300d212023-05-15 12:22:53 +070093std::unique_ptr<phosphor::Timer> rspTimer __attribute__((init_priority(101)));
Quang Nguyenf2994dc2022-11-25 16:31:49 +070094std::unique_ptr<SsifChannel> ssifchannel = nullptr;
95
Patrick Williams8b050c92023-10-20 11:19:32 -050096SsifChannel::SsifChannel(std::shared_ptr<boost::asio::io_context>& io,
97 std::shared_ptr<sdbusplus::asio::connection>& bus,
98 const std::string& device, bool verbose,
99 int numberOfReqNotRsp) :
100 io(io),
101 bus(bus), verbose(verbose), numberOfReqNotRsp(numberOfReqNotRsp)
Dung Caofaf6a6a2020-12-28 04:44:45 +0000102{
Patrick Williams8b050c92023-10-20 11:19:32 -0500103 std::string devName = devBase;
104 if (!device.empty())
105 {
106 devName = device;
107 }
Dung Caofaf6a6a2020-12-28 04:44:45 +0000108
Patrick Williams8b050c92023-10-20 11:19:32 -0500109 // open device
110 int fd = open(devName.c_str(), O_RDWR | O_NONBLOCK);
111 if (fd < 0)
112 {
113 std::string msgToLog = "Couldn't open SSIF driver with flags O_RDWR."
114 " FILENAME=" +
115 devName + " ERROR=" + strerror(errno);
116 log<level::ERR>(msgToLog.c_str());
117 return;
118 }
119 else
120 {
121 dev = std::make_unique<boost::asio::posix::stream_descriptor>(*io, fd);
122 }
Dung Caofaf6a6a2020-12-28 04:44:45 +0000123
Patrick Williams8b050c92023-10-20 11:19:32 -0500124 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();
Dung Caofaf6a6a2020-12-28 04:44:45 +0000130}
131
Patrick Williams8b050c92023-10-20 11:19:32 -0500132void SsifChannel::channelAbort(const char* msg,
133 const boost::system::error_code& ec)
Dung Caofaf6a6a2020-12-28 04:44:45 +0000134{
Patrick Williams8b050c92023-10-20 11:19:32 -0500135 std::string msgToLog = std::string(msg) + " ERROR=" + ec.message();
136 log<level::ERR>(msgToLog.c_str());
137 // bail; maybe a restart from systemd can clear the error
138 io->stop();
Dung Caofaf6a6a2020-12-28 04:44:45 +0000139}
140
141void SsifChannel::async_read()
142{
Patrick Williams8b050c92023-10-20 11:19:32 -0500143 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) { processMessage(ec, rlen); });
Dung Caofaf6a6a2020-12-28 04:44:45 +0000148}
149
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700150int SsifChannel::showNumOfReqNotRsp()
151{
Patrick Williams8b050c92023-10-20 11:19:32 -0500152 return numberOfReqNotRsp;
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700153}
154
155void rspTimerHandler()
156{
Patrick Williams8b050c92023-10-20 11:19:32 -0500157 std::vector<uint8_t> rsp;
158 constexpr uint8_t ccResponseNotAvailable = 0xce;
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700159
Patrick Williams8b050c92023-10-20 11:19:32 -0500160 rsp.resize(ssifchannel->sizeofLenField + sizeof(prev_req_cmd.cmd) +
161 sizeof(prev_req_cmd.netfn) + sizeof(ccResponseNotAvailable));
162 std::string msgToLog = "timeout, send response to keep host alive"
163 " netfn=" +
164 std::to_string(prev_req_cmd.netfn) +
165 " lun=" + std::to_string(prev_req_cmd.lun) +
166 " cmd=" + std::to_string(prev_req_cmd.cmd) +
167 " cc=" + std::to_string(ccResponseNotAvailable) +
168 " numberOfReqNotRsp=" +
169 std::to_string(ssifchannel->showNumOfReqNotRsp());
170 log<level::INFO>(msgToLog.c_str());
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700171
Patrick Williams8b050c92023-10-20 11:19:32 -0500172 unsigned int* t = (unsigned int*)&rsp[0];
173 *t = 3;
174 rsp[ssifchannel->sizeofLenField] =
175 ((prev_req_cmd.netfn + 1) << ssifchannel->netFnShift) |
176 (prev_req_cmd.lun & ssifchannel->lunMask);
177 rsp[ssifchannel->sizeofLenField + 1] = prev_req_cmd.cmd;
178 rsp[ssifchannel->sizeofLenField + 2] = ccResponseNotAvailable;
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700179
Patrick Williams8b050c92023-10-20 11:19:32 -0500180 boost::system::error_code ecWr;
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700181
Patrick Williams8b050c92023-10-20 11:19:32 -0500182 size_t wlen = boost::asio::write(*(ssifchannel->dev),
183 boost::asio::buffer(rsp), ecWr);
184 if (ecWr || wlen != rsp.size())
185 {
186 msgToLog =
187 "Failed to send ssif respond message:"
188 " size=" +
189 std::to_string(wlen) + " expect=" + std::to_string(rsp.size()) +
190 " error=" + ecWr.message() +
191 " netfn=" + std::to_string(prev_req_cmd.netfn + 1) +
192 " lun=" + std::to_string(prev_req_cmd.lun) +
193 " cmd=" + std::to_string(rsp[ssifchannel->sizeofLenField + 1]) +
194 " cc=" + std::to_string(ccResponseNotAvailable);
195 log<level::ERR>(msgToLog.c_str());
196 }
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700197}
198
199void initTimer()
200{
Patrick Williams8b050c92023-10-20 11:19:32 -0500201 if (!rspTimer)
202 {
203 rspTimer = std::make_unique<phosphor::Timer>(rspTimerHandler);
204 }
Quang Nguyenf2994dc2022-11-25 16:31:49 +0700205}
206
Patrick Williams8b050c92023-10-20 11:19:32 -0500207void SsifChannel::processMessage(const boost::system::error_code& ecRd,
208 size_t rlen)
Dung Caofaf6a6a2020-12-28 04:44:45 +0000209{
Patrick Williams8b050c92023-10-20 11:19:32 -0500210 if (ecRd || rlen < 2)
211 {
212 channelAbort("Failed to read req msg", ecRd);
213 return;
214 }
215 async_read();
Dung Caofaf6a6a2020-12-28 04:44:45 +0000216
Patrick Williams8b050c92023-10-20 11:19:32 -0500217 auto rawIter = xferBuffer.cbegin();
218 auto rawEnd = rawIter + rlen;
219 uint8_t netfn = rawIter[sizeofLenField] >> netFnShift;
220 uint8_t lun = rawIter[sizeofLenField] & lunMask;
221 uint8_t cmd = rawIter[sizeofLenField + 1];
quang.ampere14480ee2022-10-18 09:04:51 +0700222
Patrick Williams8b050c92023-10-20 11:19:32 -0500223 /* keep track of previous request */
224 prev_req_cmd.netfn = netfn;
225 prev_req_cmd.lun = lun;
226 prev_req_cmd.cmd = cmd;
quang.ampere14480ee2022-10-18 09:04:51 +0700227
Patrick Williams8b050c92023-10-20 11:19:32 -0500228 /* there is a request coming */
229 numberOfReqNotRsp++;
230 /* start response timer */
231 rspTimer->start(std::chrono::microseconds(hostReqTimeout), false);
quang.name507782a2022-11-20 17:05:20 +0700232
Patrick Williams8b050c92023-10-20 11:19:32 -0500233 if (verbose)
234 {
235 unsigned int lenRecv;
236 unsigned int* p = (unsigned int*)rawIter;
237 lenRecv = p[0];
238 std::string msgToLog =
239 "Read ssif request message with"
240 " len=" +
241 std::to_string(lenRecv) + " netfn=" + std::to_string(netfn) +
242 " lun=" + std::to_string(lun) + " cmd=" + std::to_string(cmd) +
243 " numberOfReqNotRsp=" + std::to_string(numberOfReqNotRsp);
244 log<level::INFO>(msgToLog.c_str());
245 }
246 // copy out payload
247 std::vector<uint8_t> data(rawIter + sizeofLenField + 2, rawEnd);
248 // non-session bridges still need to pass an empty options map
249 std::map<std::string, std::variant<int>> options;
250 // the response is a tuple because dbus can only return a single value
251 using IpmiDbusRspType =
252 std::tuple<uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>;
253 static constexpr const char ipmiQueueService[] =
254 "xyz.openbmc_project.Ipmi.Host";
255 static constexpr const char ipmiQueuePath[] = "/xyz/openbmc_project/Ipmi";
256 static constexpr const char ipmiQueueIntf[] =
257 "xyz.openbmc_project.Ipmi.Server";
258 static constexpr const char ipmiQueueMethod[] = "execute";
259 /* now, we do not care dbus timeout, since we already have actions
260 * before dbus timeout occurs
261 */
262 static constexpr unsigned int dbusTimeout = 60000000;
263 bus->async_method_call_timed(
264 [this, netfnCap{netfn}, lunCap{lun},
265 cmdCap{cmd}](const boost::system::error_code& ec,
266 const IpmiDbusRspType& response) {
267 std::vector<uint8_t> rsp;
268 const auto& [netfn, lun, cmd, cc, payload] = response;
269 numberOfReqNotRsp--;
270 if (ec)
271 {
272 std::string msgToLog =
273 "ssif<->ipmid bus error:"
274 " netfn=" +
275 std::to_string(netfn) + " lun=" + std::to_string(lun) +
276 " cmd=" + std::to_string(cmd) + " error=" + ec.message();
277 log<level::ERR>(msgToLog.c_str());
278 rsp.resize(sizeofLenField + sizeof(netfn) + sizeof(cmd) +
279 sizeof(cc));
280 /* if dbusTimeout, just return and do not send any response
281 * to let host continue with other commands, response here
282 * is potentially make the response duplicated
283 * */
284 return;
285 }
286 else
287 {
288 if ((prev_req_cmd.netfn != (netfn - 1) || prev_req_cmd.lun != lun ||
289 prev_req_cmd.cmd != cmd) ||
290 ((prev_req_cmd.netfn == (netfn - 1) &&
291 prev_req_cmd.lun == lun && prev_req_cmd.cmd == cmd) &&
292 numberOfReqNotRsp != 0))
293 {
294 /* Only send response to the last request command to void
295 * duplicated response which makes host driver confused and
296 * failed to create interface
297 *
298 * Drop responses which are (1) different from the request
299 * (2) parameters are the same as request but handshake flow
300 * are in dupplicate request state
301 * */
302 if (verbose)
303 {
304 std::string msgToLog = "Drop ssif respond message with"
305 " len=" +
306 std::to_string(payload.size() + 3) +
307 " netfn=" + std::to_string(netfn) +
308 " lun=" + std::to_string(lun) +
309 " cmd=" + std::to_string(cmd) +
310 " cc=" + std::to_string(cc) +
311 " numberOfReqNotRsp=" +
312 std::to_string(numberOfReqNotRsp);
313 log<level::INFO>(msgToLog.c_str());
314 }
315 return;
316 }
317 rsp.resize(sizeofLenField + sizeof(netfn) + sizeof(cmd) +
318 sizeof(cc) + payload.size());
Dung Caofaf6a6a2020-12-28 04:44:45 +0000319
Patrick Williams8b050c92023-10-20 11:19:32 -0500320 // write the response
321 auto rspIter = rsp.begin();
322 unsigned int* p = (unsigned int*)&rspIter[0];
323 *p = payload.size() + 3;
324 rspIter[sizeofLenField] = (netfn << netFnShift) | (lun & lunMask);
325 rspIter[sizeofLenField + 1] = cmd;
326 rspIter[sizeofLenField + 2] = cc;
327 if (payload.size())
328 {
329 std::copy(payload.cbegin(), payload.cend(),
330 rspIter + sizeofLenField + 3);
331 }
332 }
333 if (verbose)
334 {
335 std::string msgToLog =
336 "Send ssif respond message with"
337 " len=" +
338 std::to_string(payload.size() + 3) +
339 " netfn=" + std::to_string(netfn) +
340 " lun=" + std::to_string(lun) + " cmd=" + std::to_string(cmd) +
341 " cc=" + std::to_string(cc) +
342 " numberOfReqNotRsp=" + std::to_string(numberOfReqNotRsp);
343 log<level::INFO>(msgToLog.c_str());
344 }
345 boost::system::error_code ecWr;
346 size_t wlen = boost::asio::write(*dev, boost::asio::buffer(rsp), ecWr);
347 if (ecWr || wlen != rsp.size())
348 {
349 std::string msgToLog =
350 "Failed to send ssif respond message:"
351 " size=" +
352 std::to_string(wlen) + " expect=" + std::to_string(rsp.size()) +
353 " error=" + ecWr.message() + " netfn=" + std::to_string(netfn) +
354 " lun=" + std::to_string(lun) + " cmd=" + std::to_string(cmd) +
355 " cc=" + std::to_string(cc);
356 log<level::ERR>(msgToLog.c_str());
357 }
358 rspTimer->stop();
359 },
360 ipmiQueueService, ipmiQueuePath, ipmiQueueIntf, ipmiQueueMethod,
361 dbusTimeout, netfn, lun, cmd, data, options);
Dung Caofaf6a6a2020-12-28 04:44:45 +0000362}
363
Patrick Williams8b050c92023-10-20 11:19:32 -0500364int main(int argc, char* argv[])
Dung Caofaf6a6a2020-12-28 04:44:45 +0000365{
Patrick Williams8b050c92023-10-20 11:19:32 -0500366 CLI::App app("SSIF IPMI bridge");
367 std::string device;
368 app.add_option("-d,--device", device,
369 "use <DEVICE> file. Default is /dev/ipmi-ssif-host");
370 bool verbose = false;
371 int numberOfReqNotRsp = 0;
372 app.add_option("-v,--verbose", verbose, "print more verbose output");
373 CLI11_PARSE(app, argc, argv);
Dung Caofaf6a6a2020-12-28 04:44:45 +0000374
Patrick Williams8b050c92023-10-20 11:19:32 -0500375 // Connect to system bus
376 auto io = std::make_shared<boost::asio::io_context>();
377 sd_bus* dbus;
378 sd_bus_default_system(&dbus);
Dung Caofaf6a6a2020-12-28 04:44:45 +0000379
Patrick Williams8b050c92023-10-20 11:19:32 -0500380 /* This might be a phosphor::Timer bug, without timer t2, rspTimer
381 * will not work
382 */
383 phosphor::Timer t2([]() { ; });
384 t2.start(std::chrono::microseconds(500000), true);
385 auto bus = std::make_shared<sdbusplus::asio::connection>(*io, dbus);
386 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 }
394 initTimer();
395 sdbusplus::asio::sd_event_wrapper sdEvents(*io);
396 io->run();
Thang Q. Nguyen4300d212023-05-15 12:22:53 +0700397
Patrick Williams8b050c92023-10-20 11:19:32 -0500398 return 0;
Dung Caofaf6a6a2020-12-28 04:44:45 +0000399}