blob: 20fc6084afbd419f7df53414158896187958ab05 [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>
29
30#include <iostream>
31
Dung Cao51835392022-10-27 04:29:54 +000032/* Max length of ipmi ssif message included netfn and cmd field */
33#define IPMI_SSIF_PAYLOAD_MAX 254
34
Dung Caofaf6a6a2020-12-28 04:44:45 +000035using namespace phosphor::logging;
36
quang.ampere14480ee2022-10-18 09:04:51 +070037struct ipmi_cmd {
38 uint8_t netfn;
39 uint8_t lun;
40 uint8_t cmd;
41} prev_req_cmd;
42
Dung Caofaf6a6a2020-12-28 04:44:45 +000043static constexpr const char devBase[] = "/dev/ipmi-ssif-host";
44/* SSIF use IPMI SSIF channel */
45static constexpr const char* ssifBus =
46 "xyz.openbmc_project.Ipmi.Channel.ipmi_ssif";
47static constexpr const char* ssifObj =
48 "/xyz/openbmc_project/Ipmi/Channel/ipmi_ssif";
49
50class SsifChannel
51{
52 public:
Dung Cao51835392022-10-27 04:29:54 +000053 static constexpr size_t ssifMessageSize = IPMI_SSIF_PAYLOAD_MAX +
54 sizeof(unsigned int);
55 size_t sizeofLenField = sizeof(unsigned int);
Dung Caofaf6a6a2020-12-28 04:44:45 +000056 static constexpr uint8_t netFnShift = 2;
57 static constexpr uint8_t lunMask = (1 << netFnShift) - 1;
58
59 SsifChannel(std::shared_ptr<boost::asio::io_context>& io,
60 std::shared_ptr<sdbusplus::asio::connection>& bus,
quang.name507782a2022-11-20 17:05:20 +070061 const std::string& channel, bool verbose,
62 int numberOfReqNotRsp);
Dung Caofaf6a6a2020-12-28 04:44:45 +000063 bool initOK() const
64 {
65 return !!dev;
66 }
67 void channelAbort(const char* msg, const boost::system::error_code& ec);
68 void async_read();
69 void processMessage(const boost::system::error_code& ecRd, size_t rlen);
70
71 protected:
72 std::array<uint8_t, ssifMessageSize> xferBuffer;
73 std::shared_ptr<boost::asio::io_context> io;
74 std::shared_ptr<sdbusplus::asio::connection> bus;
75 std::shared_ptr<sdbusplus::asio::object_server> server;
76 std::unique_ptr<boost::asio::posix::stream_descriptor> dev = nullptr;
77 bool verbose;
quang.name507782a2022-11-20 17:05:20 +070078 /* This variable is always 0 when a request is responsed properly,
79 * any value larger than 0 meaning there is/are request(s) which
80 * not processed properly
81 * */
82 int numberOfReqNotRsp;
Dung Caofaf6a6a2020-12-28 04:44:45 +000083};
84
85SsifChannel::SsifChannel(std::shared_ptr<boost::asio::io_context>& io,
86 std::shared_ptr<sdbusplus::asio::connection>& bus,
quang.name507782a2022-11-20 17:05:20 +070087 const std::string& device, bool verbose, int numberOfReqNotRsp) :
Dung Caofaf6a6a2020-12-28 04:44:45 +000088 io(io),
quang.name507782a2022-11-20 17:05:20 +070089 bus(bus), verbose(verbose), numberOfReqNotRsp(numberOfReqNotRsp)
Dung Caofaf6a6a2020-12-28 04:44:45 +000090{
91 std::string devName = devBase;
92 if (!device.empty())
93 {
94 devName = device;
95 }
96
97 // open device
98 int fd = open(devName.c_str(), O_RDWR | O_NONBLOCK);
99 if (fd < 0)
100 {
Dung Caof06b8fd2021-01-29 03:31:43 +0000101 std::string msgToLog = "Couldn't open SSIF driver with flags O_RDWR."
102 " FILENAME=" + devName +
103 " ERROR=" + strerror(errno);
104 log<level::ERR>(msgToLog.c_str());
Dung Caofaf6a6a2020-12-28 04:44:45 +0000105 return;
106 }
107 else
108 {
109 dev = std::make_unique<boost::asio::posix::stream_descriptor>(*io,
110 fd);
111 }
112
113 async_read();
114 // register interfaces...
115 server = std::make_shared<sdbusplus::asio::object_server>(bus);
116 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
117 server->add_interface(ssifObj, ssifBus);
118 iface->initialize();
119}
120
121void SsifChannel::channelAbort(const char* msg,
122 const boost::system::error_code& ec)
123{
Dung Caof06b8fd2021-01-29 03:31:43 +0000124 std::string msgToLog = std::string(msg) + " ERROR=" + ec.message();
125 log<level::ERR>(msgToLog.c_str());
Dung Caofaf6a6a2020-12-28 04:44:45 +0000126 // bail; maybe a restart from systemd can clear the error
127 io->stop();
128}
129
130void SsifChannel::async_read()
131{
132 boost::asio::async_read(*dev,
133 boost::asio::buffer(xferBuffer, xferBuffer.size()),
134 boost::asio::transfer_at_least(2),
135 [this](const boost::system::error_code& ec,
136 size_t rlen) {
137 processMessage(ec, rlen);
138 });
139}
140
141void SsifChannel::processMessage(const boost::system::error_code& ecRd,
142 size_t rlen)
143{
144 if (ecRd || rlen < 2)
145 {
146 channelAbort("Failed to read req msg", ecRd);
147 return;
148 }
149 async_read();
150
151 auto rawIter = xferBuffer.cbegin();
152 auto rawEnd = rawIter + rlen;
Dung Cao51835392022-10-27 04:29:54 +0000153 uint8_t netfn = rawIter[sizeofLenField] >> netFnShift;
154 uint8_t lun = rawIter[sizeofLenField] & lunMask;
155 uint8_t cmd = rawIter[sizeofLenField + 1];
quang.ampere14480ee2022-10-18 09:04:51 +0700156
quang.name507782a2022-11-20 17:05:20 +0700157 /* keep track of previous request */
quang.ampere14480ee2022-10-18 09:04:51 +0700158 prev_req_cmd.netfn = netfn;
159 prev_req_cmd.lun = lun;
160 prev_req_cmd.cmd = cmd;
161
quang.name507782a2022-11-20 17:05:20 +0700162 /* there is a request coming */
163 numberOfReqNotRsp++;
164
Dung Caofaf6a6a2020-12-28 04:44:45 +0000165 if (verbose)
166 {
Dung Cao51835392022-10-27 04:29:54 +0000167 unsigned int lenRecv;
168 unsigned int *p = (unsigned int *) rawIter;
169 lenRecv = p[0];
Dung Caof06b8fd2021-01-29 03:31:43 +0000170 std::string msgToLog = "Read ssif request message with"
Dung Cao51835392022-10-27 04:29:54 +0000171 " len=" + std::to_string(lenRecv) +
Dung Caof06b8fd2021-01-29 03:31:43 +0000172 " netfn=" + std::to_string(netfn) +
173 " lun=" + std::to_string(lun) +
174 " cmd=" + std::to_string(cmd);
175 log<level::INFO>(msgToLog.c_str());
Dung Caofaf6a6a2020-12-28 04:44:45 +0000176 }
177 // copy out payload
Dung Cao51835392022-10-27 04:29:54 +0000178 std::vector<uint8_t> data(rawIter + sizeofLenField + 2, rawEnd);
Dung Caofaf6a6a2020-12-28 04:44:45 +0000179 // non-session bridges still need to pass an empty options map
180 std::map<std::string, std::variant<int>> options;
181 // the response is a tuple because dbus can only return a single value
182 using IpmiDbusRspType = std::tuple<uint8_t, uint8_t, uint8_t, uint8_t,
183 std::vector<uint8_t>>;
184 static constexpr const char ipmiQueueService[] =
185 "xyz.openbmc_project.Ipmi.Host";
186 static constexpr const char ipmiQueuePath[] =
187 "/xyz/openbmc_project/Ipmi";
188 static constexpr const char ipmiQueueIntf[] =
189 "xyz.openbmc_project.Ipmi.Server";
190 static constexpr const char ipmiQueueMethod[] = "execute";
quang.ampere14480ee2022-10-18 09:04:51 +0700191 static constexpr int dbusTimeout = 40000000;
192 bus->async_method_call_timed(
Dung Caofaf6a6a2020-12-28 04:44:45 +0000193 [this, netfnCap{netfn}, lunCap{lun},
194 cmdCap{cmd}](const boost::system::error_code& ec,
195 const IpmiDbusRspType& response) {
196 std::vector<uint8_t> rsp;
197 const auto& [netfn, lun, cmd, cc, payload] = response;
quang.name507782a2022-11-20 17:05:20 +0700198 numberOfReqNotRsp--;
Dung Caofaf6a6a2020-12-28 04:44:45 +0000199 if (ec)
200 {
Dung Caof06b8fd2021-01-29 03:31:43 +0000201 std::string msgToLog = "ssif<->ipmid bus error:"
202 " netfn=" + std::to_string(netfn) +
203 " lun=" + std::to_string(lun) +
204 " cmd=" + std::to_string(cmd) +
205 " error=" + ec.message();
206 log<level::ERR>(msgToLog.c_str());
Dung Cao51835392022-10-27 04:29:54 +0000207 rsp.resize(sizeofLenField + sizeof(netfn) + sizeof(cmd) +
Dung Caofaf6a6a2020-12-28 04:44:45 +0000208 sizeof(cc));
quang.ampere14480ee2022-10-18 09:04:51 +0700209 /* if dbusTimeout, just return and do not send any response
210 * to let host continue with other commands, response here
quang.name507782a2022-11-20 17:05:20 +0700211 * is potentially make the response duplicated
quang.ampere14480ee2022-10-18 09:04:51 +0700212 * */
213 return;
Dung Caofaf6a6a2020-12-28 04:44:45 +0000214 }
215 else
216 {
quang.name507782a2022-11-20 17:05:20 +0700217 if ((prev_req_cmd.netfn != (netfn-1) ||
quang.ampere14480ee2022-10-18 09:04:51 +0700218 prev_req_cmd.lun != lun ||
quang.name507782a2022-11-20 17:05:20 +0700219 prev_req_cmd.cmd != cmd) ||
220 ((prev_req_cmd.netfn == (netfn-1) ||
221 prev_req_cmd.lun == lun ||
222 prev_req_cmd.cmd == cmd) &&
223 numberOfReqNotRsp > 0))
quang.ampere14480ee2022-10-18 09:04:51 +0700224 {
225 /* Only send response to the last request command to void
226 * duplicated response which makes host driver confused and
227 * failed to create interface
quang.name507782a2022-11-20 17:05:20 +0700228 *
229 * Drop responses which are (1) different from the request
230 * (2) parameters are the same as request but handshake flow
231 * are in dupplicate request state
quang.ampere14480ee2022-10-18 09:04:51 +0700232 * */
233 return;
234 }
Dung Cao51835392022-10-27 04:29:54 +0000235 rsp.resize(sizeofLenField + sizeof(netfn) + sizeof(cmd) +
Dung Caofaf6a6a2020-12-28 04:44:45 +0000236 sizeof(cc) + payload.size());
237
238 // write the response
239 auto rspIter = rsp.begin();
Dung Cao51835392022-10-27 04:29:54 +0000240 unsigned int *p = (unsigned int *) &rspIter[0];
241 *p = payload.size() + 3;
242 rspIter[sizeofLenField] = (netfn << netFnShift) | (lun & lunMask);
243 rspIter[sizeofLenField + 1] = cmd;
244 rspIter[sizeofLenField + 2] = cc;
Dung Caofaf6a6a2020-12-28 04:44:45 +0000245 if (payload.size())
246 {
247 std::copy(payload.cbegin(), payload.cend(),
Dung Cao51835392022-10-27 04:29:54 +0000248 rspIter + sizeofLenField + 3);
Dung Caofaf6a6a2020-12-28 04:44:45 +0000249 }
250 }
251 if (verbose)
252 {
Dung Caof06b8fd2021-01-29 03:31:43 +0000253 std::string msgToLog = "Send ssif respond message with"
254 " len=" + std::to_string(payload.size() + 3) +
255 " netfn=" + std::to_string(netfn) +
256 " lun=" + std::to_string(lun) +
257 " cmd=" + std::to_string(cmd) +
258 " cc=" + std::to_string(cc);
259 log<level::INFO>(msgToLog.c_str());
Dung Caofaf6a6a2020-12-28 04:44:45 +0000260 }
261 boost::system::error_code ecWr;
262 size_t wlen =
263 boost::asio::write(*dev, boost::asio::buffer(rsp), ecWr);
264 if (ecWr || wlen != rsp.size())
265 {
Dung Caof06b8fd2021-01-29 03:31:43 +0000266 std::string msgToLog = "Failed to send ssif respond message:"
267 " size=" + std::to_string(wlen) +
268 " expect=" + std::to_string(rsp.size()) +
269 " error=" + ecWr.message() +
270 " netfn=" + std::to_string(netfn) +
271 " lun=" + std::to_string(lun) +
272 " cmd=" + std::to_string(cmd) +
273 " cc=" + std::to_string(cc);
274 log<level::ERR>(msgToLog.c_str());
Dung Caofaf6a6a2020-12-28 04:44:45 +0000275 }
276 },
quang.ampere14480ee2022-10-18 09:04:51 +0700277 ipmiQueueService, ipmiQueuePath, ipmiQueueIntf, ipmiQueueMethod, dbusTimeout,
Dung Caofaf6a6a2020-12-28 04:44:45 +0000278 netfn, lun, cmd, data, options);
279}
280
281
282int main(int argc, char* argv[])
283{
284 CLI::App app("SSIF IPMI bridge");
285 std::string device;
286 app.add_option("-d,--device", device,
287 "use <DEVICE> file. Default is /dev/ipmi-ssif-host");
288 bool verbose = false;
quang.name507782a2022-11-20 17:05:20 +0700289 int numberOfReqNotRsp = 0;
Dung Caofaf6a6a2020-12-28 04:44:45 +0000290 app.add_option("-v,--verbose", verbose, "print more verbose output");
291 CLI11_PARSE(app, argc, argv);
292
293 // Connect to system bus
294 auto io = std::make_shared<boost::asio::io_context>();
295 sd_bus* dbus;
296 sd_bus_default_system(&dbus);
297 auto bus = std::make_shared<sdbusplus::asio::connection>(*io, dbus);
298 bus->request_name(ssifBus);
299 // Create the SSIF channel, listening on D-Bus and on the SSIF device
quang.name507782a2022-11-20 17:05:20 +0700300 SsifChannel ssifchannel(io, bus, device, verbose, numberOfReqNotRsp);
Dung Caofaf6a6a2020-12-28 04:44:45 +0000301 if (!ssifchannel.initOK())
302 {
303 return EXIT_FAILURE;
304 }
305 io->run();
306
307 return 0;
308}