blob: 00914b557237f788ef7b5190724c64c260159311 [file] [log] [blame]
Vernon Mauery9ce5a9a2019-03-12 13:59:25 -07001/* Copyright 2017 - 2019 Intel
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include <getopt.h>
17#include <linux/ipmi_bmc.h>
18
19#include <CLI/CLI.hpp>
Konstantin Aladyshev4a4d1d02020-10-14 21:18:09 +030020#include <boost/algorithm/string/replace.hpp>
Vernon Mauery9ce5a9a2019-03-12 13:59:25 -070021#include <boost/asio.hpp>
22#include <iostream>
23#include <phosphor-logging/log.hpp>
24#include <sdbusplus/asio/connection.hpp>
25#include <sdbusplus/asio/object_server.hpp>
26
27using namespace phosphor::logging;
28
29namespace
30{
31namespace io_control
32{
33struct ClearSmsAttention
34{
35 // Get the name of the IO control command.
36 int name() const
37 {
38 return static_cast<int>(IPMI_BMC_IOCTL_CLEAR_SMS_ATN);
39 }
40
41 // Get the address of the command data.
42 boost::asio::detail::ioctl_arg_type* data()
43 {
44 return nullptr;
45 }
46};
47
48struct SetSmsAttention
49{
50 // Get the name of the IO control command.
51 int name() const
52 {
53 return static_cast<int>(IPMI_BMC_IOCTL_SET_SMS_ATN);
54 }
55
56 // Get the address of the command data.
57 boost::asio::detail::ioctl_arg_type* data()
58 {
59 return nullptr;
60 }
61};
62
63struct ForceAbort
64{
65 // Get the name of the IO control command.
66 int name() const
67 {
68 return static_cast<int>(IPMI_BMC_IOCTL_FORCE_ABORT);
69 }
70
71 // Get the address of the command data.
72 boost::asio::detail::ioctl_arg_type* data()
73 {
74 return nullptr;
75 }
76};
77} // namespace io_control
78
79class SmsChannel
80{
81 public:
Rashmi RV58d596a2020-04-03 12:40:59 +053082 static constexpr size_t kcsMessageSize = 4096;
Vernon Mauery9ce5a9a2019-03-12 13:59:25 -070083 static constexpr uint8_t netFnShift = 2;
84 static constexpr uint8_t lunMask = (1 << netFnShift) - 1;
85
86 SmsChannel(std::shared_ptr<boost::asio::io_context>& io,
87 std::shared_ptr<sdbusplus::asio::connection>& bus,
88 const std::string& channel, bool verbose) :
89 io(io),
90 bus(bus), verbose(verbose)
91 {
92 static constexpr const char devBase[] = "/dev/";
93 std::string devName = devBase + channel;
94 // open device
95 int fd = open(devName.c_str(), O_RDWR | O_NONBLOCK);
96 if (fd < 0)
97 {
98 log<level::ERR>("Couldn't open SMS channel O_RDWR",
99 entry("FILENAME=%s", devName.c_str()),
100 entry("ERROR=%s", strerror(errno)));
101 return;
102 }
103 else
104 {
105 dev = std::make_unique<boost::asio::posix::stream_descriptor>(*io,
106 fd);
107 }
108
109 async_read();
110
111 // register interfaces...
112 server = std::make_shared<sdbusplus::asio::object_server>(bus);
113
114 static constexpr const char pathBase[] =
115 "/xyz/openbmc_project/Ipmi/Channel/";
116 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
Konstantin Aladyshev4a4d1d02020-10-14 21:18:09 +0300117 server->add_interface(
118 pathBase + boost::replace_all_copy(channel, "-", "_"),
119 "xyz.openbmc_project.Ipmi.Channel.SMS");
Vernon Mauery9ce5a9a2019-03-12 13:59:25 -0700120 iface->register_method("setAttention",
121 [this]() { return setAttention(); });
122 iface->register_method("clearAttention",
123 [this]() { return clearAttention(); });
124 iface->register_method("forceAbort", [this]() { return forceAbort(); });
125 iface->initialize();
126 }
127
128 bool initOK() const
129 {
130 return !!dev;
131 }
132
133 void channelAbort(const char* msg, const boost::system::error_code& ec)
134 {
135 log<level::ERR>(msg, entry("ERROR=%s", ec.message().c_str()));
136 // bail; maybe a restart from systemd can clear the error
137 io->stop();
138 }
139
140 void async_read()
141 {
142 boost::asio::async_read(
143 *dev, boost::asio::buffer(xferBuffer, xferBuffer.size()),
144 boost::asio::transfer_at_least(2),
145 [this](const boost::system::error_code& ec, size_t rlen) {
146 processMessage(ec, rlen);
147 });
148 }
149
150 void processMessage(const boost::system::error_code& ecRd, size_t rlen)
151 {
152 if (ecRd || rlen < 2)
153 {
154 channelAbort("Failed to read req msg", ecRd);
155 return;
156 }
157
158 async_read();
159
160 // trim raw to be only bytes returned from read
161 // separate netfn/lun/cmd from payload
162 auto rawIter = xferBuffer.cbegin();
163 auto rawEnd = rawIter + rlen;
164 uint8_t netfn = *rawIter >> netFnShift;
165 uint8_t lun = *rawIter++ & lunMask;
166 uint8_t cmd = *rawIter++;
167 if (verbose)
168 {
169 log<level::INFO>("Read req msg", entry("NETFN=0x%02x", netfn),
170 entry("LUN=0x%02x", lun),
171 entry("CMD=0x%02x", cmd));
172 }
173 // copy out payload
174 std::vector<uint8_t> data(rawIter, rawEnd);
175 // non-session bridges still need to pass an empty options map
Patrick Williamsbc2d2c92020-06-04 06:44:57 -0500176 std::map<std::string, std::variant<int>> options;
Vernon Mauery9ce5a9a2019-03-12 13:59:25 -0700177 // the response is a tuple because dbus can only return a single value
178 using IpmiDbusRspType = std::tuple<uint8_t, uint8_t, uint8_t, uint8_t,
179 std::vector<uint8_t>>;
180 static constexpr const char ipmiQueueService[] =
181 "xyz.openbmc_project.Ipmi.Host";
182 static constexpr const char ipmiQueuePath[] =
183 "/xyz/openbmc_project/Ipmi";
184 static constexpr const char ipmiQueueIntf[] =
185 "xyz.openbmc_project.Ipmi.Server";
186 static constexpr const char ipmiQueueMethod[] = "execute";
187 bus->async_method_call(
188 [this, netfnCap{netfn}, lunCap{lun},
189 cmdCap{cmd}](const boost::system::error_code& ec,
190 const IpmiDbusRspType& response) {
191 std::vector<uint8_t> rsp;
192 const auto& [netfn, lun, cmd, cc, payload] = response;
193 if (ec)
194 {
195 log<level::ERR>(
196 "kcs<->ipmid bus error:", entry("NETFN=0x%02x", netfn),
197 entry("LUN=0x%02x", lun), entry("CMD=0x%02x", cmd),
198 entry("ERROR=%s", ec.message().c_str()));
199 // send unspecified error for a D-Bus error
200 constexpr uint8_t ccResponseNotAvailable = 0xce;
201 rsp.resize(sizeof(netfn) + sizeof(cmd) + sizeof(cc));
202 rsp[0] =
203 ((netfnCap + 1) << netFnShift) | (lunCap & lunMask);
204 rsp[1] = cmdCap;
205 rsp[2] = ccResponseNotAvailable;
206 }
207 else
208 {
209 rsp.resize(sizeof(netfn) + sizeof(cmd) + sizeof(cc) +
210 payload.size());
211
212 // write the response
213 auto rspIter = rsp.begin();
214 *rspIter++ = (netfn << netFnShift) | (lun & lunMask);
215 *rspIter++ = cmd;
216 *rspIter++ = cc;
217 if (payload.size())
218 {
219 std::copy(payload.cbegin(), payload.cend(), rspIter);
220 }
221 }
222 if (verbose)
223 {
224 log<level::INFO>(
225 "Send rsp msg", entry("NETFN=0x%02x", netfn),
226 entry("LUN=0x%02x", lun), entry("CMD=0x%02x", cmd),
227 entry("CC=0x%02x", cc));
228 }
229 boost::system::error_code ecWr;
230 size_t wlen =
231 boost::asio::write(*dev, boost::asio::buffer(rsp), ecWr);
232 if (ecWr || wlen != rsp.size())
233 {
234 log<level::ERR>(
235 "Failed to send rsp msg", entry("SIZE=%d", wlen),
236 entry("EXPECT=%d", rsp.size()),
237 entry("ERROR=%s", ecWr.message().c_str()),
238 entry("NETFN=0x%02x", netfn), entry("LUN=0x%02x", lun),
239 entry("CMD=0x%02x", cmd), entry("CC=0x%02x", cc));
240 }
241 },
242 ipmiQueueService, ipmiQueuePath, ipmiQueueIntf, ipmiQueueMethod,
243 netfn, lun, cmd, data, options);
244 }
245
246 int64_t setAttention()
247 {
248 if (verbose)
249 {
250 log<level::INFO>("Sending SET_SMS_ATTENTION");
251 }
252 io_control::SetSmsAttention command;
253 boost::system::error_code ec;
254 dev->io_control(command, ec);
255 if (ec)
256 {
257 log<level::ERR>("Couldn't SET_SMS_ATTENTION",
258 entry("ERROR=%s", ec.message().c_str()));
259 return ec.value();
260 }
261 return 0;
262 }
263
264 int64_t clearAttention()
265 {
266 if (verbose)
267 {
268 log<level::INFO>("Sending CLEAR_SMS_ATTENTION");
269 }
270 io_control::ClearSmsAttention command;
271 boost::system::error_code ec;
272 dev->io_control(command, ec);
273 if (ec)
274 {
275 log<level::ERR>("Couldn't CLEAR_SMS_ATTENTION",
276 entry("ERROR=%s", ec.message().c_str()));
277 return ec.value();
278 }
279 return 0;
280 }
281
282 int64_t forceAbort()
283 {
284 if (verbose)
285 {
286 log<level::INFO>("Sending FORCE_ABORT");
287 }
288 io_control::ForceAbort command;
289 boost::system::error_code ec;
290 dev->io_control(command, ec);
291 if (ec)
292 {
293 log<level::ERR>("Couldn't FORCE_ABORT",
294 entry("ERROR=%s", ec.message().c_str()));
295 return ec.value();
296 }
297 return 0;
298 }
299
300 protected:
301 std::array<uint8_t, kcsMessageSize> xferBuffer;
302 std::shared_ptr<boost::asio::io_context> io;
303 std::shared_ptr<sdbusplus::asio::connection> bus;
304 std::shared_ptr<sdbusplus::asio::object_server> server;
305 std::unique_ptr<boost::asio::posix::stream_descriptor> dev = nullptr;
306 bool verbose;
307};
308
309} // namespace
310
Vernon Mauery9ce5a9a2019-03-12 13:59:25 -0700311int main(int argc, char* argv[])
312{
313 CLI::App app("KCS IPMI bridge");
314 std::string channel;
Vernon Mauery2cdc4952019-04-12 11:10:18 -0700315 app.add_option("-c,--channel", channel, "channel name. e.g., ipmi-kcs3")
316 ->required();
Vernon Mauery9ce5a9a2019-03-12 13:59:25 -0700317 bool verbose = false;
318 app.add_option("-v,--verbose", verbose, "print more verbose output");
Vernon Mauery9ce5a9a2019-03-12 13:59:25 -0700319 CLI11_PARSE(app, argc, argv);
320
Vernon Mauery9ce5a9a2019-03-12 13:59:25 -0700321 // Connect to system bus
322 auto io = std::make_shared<boost::asio::io_context>();
323 sd_bus* dbus;
324 sd_bus_default_system(&dbus);
325 auto bus = std::make_shared<sdbusplus::asio::connection>(*io, dbus);
326
327 // Create the channel, listening on D-Bus and on the SMS device
328 SmsChannel smsChannel(io, bus, channel, verbose);
329
330 if (!smsChannel.initOK())
331 {
332 return EXIT_FAILURE;
333 }
334
335 static constexpr const char busBase[] = "xyz.openbmc_project.Ipmi.Channel.";
Konstantin Aladyshev4a4d1d02020-10-14 21:18:09 +0300336 std::string busName(busBase + boost::replace_all_copy(channel, "-", "_"));
Vernon Mauery9ce5a9a2019-03-12 13:59:25 -0700337 bus->request_name(busName.c_str());
338
339 io->run();
340
341 return 0;
342}