blob: da5060bd21048b8d7741b287b9e131337d6be081 [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
32using namespace phosphor::logging;
33
quang.ampere14480ee2022-10-18 09:04:51 +070034struct ipmi_cmd {
35 uint8_t netfn;
36 uint8_t lun;
37 uint8_t cmd;
38} prev_req_cmd;
39
Dung Caofaf6a6a2020-12-28 04:44:45 +000040static constexpr const char devBase[] = "/dev/ipmi-ssif-host";
41/* SSIF use IPMI SSIF channel */
42static constexpr const char* ssifBus =
43 "xyz.openbmc_project.Ipmi.Channel.ipmi_ssif";
44static constexpr const char* ssifObj =
45 "/xyz/openbmc_project/Ipmi/Channel/ipmi_ssif";
46
47class SsifChannel
48{
49 public:
Dung Caob26a9b62021-01-29 05:19:47 +000050 static constexpr size_t ssifMessageSize = 255;
Dung Caofaf6a6a2020-12-28 04:44:45 +000051 static constexpr uint8_t netFnShift = 2;
52 static constexpr uint8_t lunMask = (1 << netFnShift) - 1;
53
54 SsifChannel(std::shared_ptr<boost::asio::io_context>& io,
55 std::shared_ptr<sdbusplus::asio::connection>& bus,
56 const std::string& channel, bool verbose);
57 bool initOK() const
58 {
59 return !!dev;
60 }
61 void channelAbort(const char* msg, const boost::system::error_code& ec);
62 void async_read();
63 void processMessage(const boost::system::error_code& ecRd, size_t rlen);
64
65 protected:
66 std::array<uint8_t, ssifMessageSize> xferBuffer;
67 std::shared_ptr<boost::asio::io_context> io;
68 std::shared_ptr<sdbusplus::asio::connection> bus;
69 std::shared_ptr<sdbusplus::asio::object_server> server;
70 std::unique_ptr<boost::asio::posix::stream_descriptor> dev = nullptr;
71 bool verbose;
72};
73
74SsifChannel::SsifChannel(std::shared_ptr<boost::asio::io_context>& io,
75 std::shared_ptr<sdbusplus::asio::connection>& bus,
76 const std::string& device, bool verbose) :
77 io(io),
78 bus(bus), verbose(verbose)
79{
80 std::string devName = devBase;
81 if (!device.empty())
82 {
83 devName = device;
84 }
85
86 // open device
87 int fd = open(devName.c_str(), O_RDWR | O_NONBLOCK);
88 if (fd < 0)
89 {
Dung Caof06b8fd2021-01-29 03:31:43 +000090 std::string msgToLog = "Couldn't open SSIF driver with flags O_RDWR."
91 " FILENAME=" + devName +
92 " ERROR=" + strerror(errno);
93 log<level::ERR>(msgToLog.c_str());
Dung Caofaf6a6a2020-12-28 04:44:45 +000094 return;
95 }
96 else
97 {
98 dev = std::make_unique<boost::asio::posix::stream_descriptor>(*io,
99 fd);
100 }
101
102 async_read();
103 // register interfaces...
104 server = std::make_shared<sdbusplus::asio::object_server>(bus);
105 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
106 server->add_interface(ssifObj, ssifBus);
107 iface->initialize();
108}
109
110void SsifChannel::channelAbort(const char* msg,
111 const boost::system::error_code& ec)
112{
Dung Caof06b8fd2021-01-29 03:31:43 +0000113 std::string msgToLog = std::string(msg) + " ERROR=" + ec.message();
114 log<level::ERR>(msgToLog.c_str());
Dung Caofaf6a6a2020-12-28 04:44:45 +0000115 // bail; maybe a restart from systemd can clear the error
116 io->stop();
117}
118
119void SsifChannel::async_read()
120{
121 boost::asio::async_read(*dev,
122 boost::asio::buffer(xferBuffer, xferBuffer.size()),
123 boost::asio::transfer_at_least(2),
124 [this](const boost::system::error_code& ec,
125 size_t rlen) {
126 processMessage(ec, rlen);
127 });
128}
129
130void SsifChannel::processMessage(const boost::system::error_code& ecRd,
131 size_t rlen)
132{
133 if (ecRd || rlen < 2)
134 {
135 channelAbort("Failed to read req msg", ecRd);
136 return;
137 }
138 async_read();
139
140 auto rawIter = xferBuffer.cbegin();
141 auto rawEnd = rawIter + rlen;
Dung Caofaf6a6a2020-12-28 04:44:45 +0000142 uint8_t netfn = rawIter[1] >> netFnShift;
143 uint8_t lun = rawIter[1] & lunMask;
144 uint8_t cmd = rawIter[2];
quang.ampere14480ee2022-10-18 09:04:51 +0700145
146 prev_req_cmd.netfn = netfn;
147 prev_req_cmd.lun = lun;
148 prev_req_cmd.cmd = cmd;
149
Dung Caofaf6a6a2020-12-28 04:44:45 +0000150 if (verbose)
151 {
Dung Caof06b8fd2021-01-29 03:31:43 +0000152 std::string msgToLog = "Read ssif request message with"
153 " len=" + std::to_string(rawIter[0] + 1) +
154 " netfn=" + std::to_string(netfn) +
155 " lun=" + std::to_string(lun) +
156 " cmd=" + std::to_string(cmd);
157 log<level::INFO>(msgToLog.c_str());
Dung Caofaf6a6a2020-12-28 04:44:45 +0000158 }
159 // copy out payload
160 std::vector<uint8_t> data(&rawIter[3], rawEnd);
161 // non-session bridges still need to pass an empty options map
162 std::map<std::string, std::variant<int>> options;
163 // the response is a tuple because dbus can only return a single value
164 using IpmiDbusRspType = std::tuple<uint8_t, uint8_t, uint8_t, uint8_t,
165 std::vector<uint8_t>>;
166 static constexpr const char ipmiQueueService[] =
167 "xyz.openbmc_project.Ipmi.Host";
168 static constexpr const char ipmiQueuePath[] =
169 "/xyz/openbmc_project/Ipmi";
170 static constexpr const char ipmiQueueIntf[] =
171 "xyz.openbmc_project.Ipmi.Server";
172 static constexpr const char ipmiQueueMethod[] = "execute";
quang.ampere14480ee2022-10-18 09:04:51 +0700173 static constexpr int dbusTimeout = 40000000;
174 bus->async_method_call_timed(
Dung Caofaf6a6a2020-12-28 04:44:45 +0000175 [this, netfnCap{netfn}, lunCap{lun},
176 cmdCap{cmd}](const boost::system::error_code& ec,
177 const IpmiDbusRspType& response) {
178 std::vector<uint8_t> rsp;
179 const auto& [netfn, lun, cmd, cc, payload] = response;
180 if (ec)
181 {
Dung Caof06b8fd2021-01-29 03:31:43 +0000182 std::string msgToLog = "ssif<->ipmid bus error:"
183 " netfn=" + std::to_string(netfn) +
184 " lun=" + std::to_string(lun) +
185 " cmd=" + std::to_string(cmd) +
186 " error=" + ec.message();
187 log<level::ERR>(msgToLog.c_str());
Dung Caofaf6a6a2020-12-28 04:44:45 +0000188 rsp.resize(sizeof(uint8_t) + sizeof(netfn) + sizeof(cmd) +
189 sizeof(cc));
quang.ampere14480ee2022-10-18 09:04:51 +0700190 /* if dbusTimeout, just return and do not send any response
191 * to let host continue with other commands, response here
192 * is potentionally make the response duplicated
193 * */
194 return;
Dung Caofaf6a6a2020-12-28 04:44:45 +0000195 }
196 else
197 {
quang.ampere14480ee2022-10-18 09:04:51 +0700198 if(prev_req_cmd.netfn != (netfn-1) ||
199 prev_req_cmd.lun != lun ||
200 prev_req_cmd.cmd != cmd)
201 {
202 /* Only send response to the last request command to void
203 * duplicated response which makes host driver confused and
204 * failed to create interface
205 * */
206 return;
207 }
Dung Caofaf6a6a2020-12-28 04:44:45 +0000208 rsp.resize(sizeof(uint8_t) + sizeof(netfn) + sizeof(cmd) +
209 sizeof(cc) + payload.size());
210
211 // write the response
212 auto rspIter = rsp.begin();
213 rspIter[0] = payload.size() + 3;
214 rspIter[1] = (netfn << netFnShift) | (lun & lunMask);
215 rspIter[2] = cmd;
216 rspIter[3] = cc;
217 if (payload.size())
218 {
219 std::copy(payload.cbegin(), payload.cend(),
220 &rspIter[4]);
221 }
222 }
223 if (verbose)
224 {
Dung Caof06b8fd2021-01-29 03:31:43 +0000225 std::string msgToLog = "Send ssif respond message with"
226 " len=" + std::to_string(payload.size() + 3) +
227 " netfn=" + std::to_string(netfn) +
228 " lun=" + std::to_string(lun) +
229 " cmd=" + std::to_string(cmd) +
230 " cc=" + std::to_string(cc);
231 log<level::INFO>(msgToLog.c_str());
Dung Caofaf6a6a2020-12-28 04:44:45 +0000232 }
233 boost::system::error_code ecWr;
234 size_t wlen =
235 boost::asio::write(*dev, boost::asio::buffer(rsp), ecWr);
236 if (ecWr || wlen != rsp.size())
237 {
Dung Caof06b8fd2021-01-29 03:31:43 +0000238 std::string msgToLog = "Failed to send ssif respond message:"
239 " size=" + std::to_string(wlen) +
240 " expect=" + std::to_string(rsp.size()) +
241 " error=" + ecWr.message() +
242 " netfn=" + std::to_string(netfn) +
243 " lun=" + std::to_string(lun) +
244 " cmd=" + std::to_string(cmd) +
245 " cc=" + std::to_string(cc);
246 log<level::ERR>(msgToLog.c_str());
Dung Caofaf6a6a2020-12-28 04:44:45 +0000247 }
248 },
quang.ampere14480ee2022-10-18 09:04:51 +0700249 ipmiQueueService, ipmiQueuePath, ipmiQueueIntf, ipmiQueueMethod, dbusTimeout,
Dung Caofaf6a6a2020-12-28 04:44:45 +0000250 netfn, lun, cmd, data, options);
251}
252
253
254int main(int argc, char* argv[])
255{
256 CLI::App app("SSIF IPMI bridge");
257 std::string device;
258 app.add_option("-d,--device", device,
259 "use <DEVICE> file. Default is /dev/ipmi-ssif-host");
260 bool verbose = false;
261 app.add_option("-v,--verbose", verbose, "print more verbose output");
262 CLI11_PARSE(app, argc, argv);
263
264 // Connect to system bus
265 auto io = std::make_shared<boost::asio::io_context>();
266 sd_bus* dbus;
267 sd_bus_default_system(&dbus);
268 auto bus = std::make_shared<sdbusplus::asio::connection>(*io, dbus);
269 bus->request_name(ssifBus);
270 // Create the SSIF channel, listening on D-Bus and on the SSIF device
271 SsifChannel ssifchannel(io, bus, device, verbose);
272 if (!ssifchannel.initOK())
273 {
274 return EXIT_FAILURE;
275 }
276 io->run();
277
278 return 0;
279}