blob: 12f8e998b886981da541750b76487cd363e4badc [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
34static constexpr const char devBase[] = "/dev/ipmi-ssif-host";
35/* SSIF use IPMI SSIF channel */
36static constexpr const char* ssifBus =
37 "xyz.openbmc_project.Ipmi.Channel.ipmi_ssif";
38static constexpr const char* ssifObj =
39 "/xyz/openbmc_project/Ipmi/Channel/ipmi_ssif";
40
41class SsifChannel
42{
43 public:
Dung Caob26a9b62021-01-29 05:19:47 +000044 static constexpr size_t ssifMessageSize = 255;
Dung Caofaf6a6a2020-12-28 04:44:45 +000045 static constexpr uint8_t netFnShift = 2;
46 static constexpr uint8_t lunMask = (1 << netFnShift) - 1;
47
48 SsifChannel(std::shared_ptr<boost::asio::io_context>& io,
49 std::shared_ptr<sdbusplus::asio::connection>& bus,
50 const std::string& channel, bool verbose);
51 bool initOK() const
52 {
53 return !!dev;
54 }
55 void channelAbort(const char* msg, const boost::system::error_code& ec);
56 void async_read();
57 void processMessage(const boost::system::error_code& ecRd, size_t rlen);
58
59 protected:
60 std::array<uint8_t, ssifMessageSize> xferBuffer;
61 std::shared_ptr<boost::asio::io_context> io;
62 std::shared_ptr<sdbusplus::asio::connection> bus;
63 std::shared_ptr<sdbusplus::asio::object_server> server;
64 std::unique_ptr<boost::asio::posix::stream_descriptor> dev = nullptr;
65 bool verbose;
66};
67
68SsifChannel::SsifChannel(std::shared_ptr<boost::asio::io_context>& io,
69 std::shared_ptr<sdbusplus::asio::connection>& bus,
70 const std::string& device, bool verbose) :
71 io(io),
72 bus(bus), verbose(verbose)
73{
74 std::string devName = devBase;
75 if (!device.empty())
76 {
77 devName = device;
78 }
79
80 // open device
81 int fd = open(devName.c_str(), O_RDWR | O_NONBLOCK);
82 if (fd < 0)
83 {
Dung Caof06b8fd2021-01-29 03:31:43 +000084 std::string msgToLog = "Couldn't open SSIF driver with flags O_RDWR."
85 " FILENAME=" + devName +
86 " ERROR=" + strerror(errno);
87 log<level::ERR>(msgToLog.c_str());
Dung Caofaf6a6a2020-12-28 04:44:45 +000088 return;
89 }
90 else
91 {
92 dev = std::make_unique<boost::asio::posix::stream_descriptor>(*io,
93 fd);
94 }
95
96 async_read();
97 // register interfaces...
98 server = std::make_shared<sdbusplus::asio::object_server>(bus);
99 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
100 server->add_interface(ssifObj, ssifBus);
101 iface->initialize();
102}
103
104void SsifChannel::channelAbort(const char* msg,
105 const boost::system::error_code& ec)
106{
Dung Caof06b8fd2021-01-29 03:31:43 +0000107 std::string msgToLog = std::string(msg) + " ERROR=" + ec.message();
108 log<level::ERR>(msgToLog.c_str());
Dung Caofaf6a6a2020-12-28 04:44:45 +0000109 // bail; maybe a restart from systemd can clear the error
110 io->stop();
111}
112
113void SsifChannel::async_read()
114{
115 boost::asio::async_read(*dev,
116 boost::asio::buffer(xferBuffer, xferBuffer.size()),
117 boost::asio::transfer_at_least(2),
118 [this](const boost::system::error_code& ec,
119 size_t rlen) {
120 processMessage(ec, rlen);
121 });
122}
123
124void SsifChannel::processMessage(const boost::system::error_code& ecRd,
125 size_t rlen)
126{
127 if (ecRd || rlen < 2)
128 {
129 channelAbort("Failed to read req msg", ecRd);
130 return;
131 }
132 async_read();
133
134 auto rawIter = xferBuffer.cbegin();
135 auto rawEnd = rawIter + rlen;
Dung Caofaf6a6a2020-12-28 04:44:45 +0000136 uint8_t netfn = rawIter[1] >> netFnShift;
137 uint8_t lun = rawIter[1] & lunMask;
138 uint8_t cmd = rawIter[2];
139 if (verbose)
140 {
Dung Caof06b8fd2021-01-29 03:31:43 +0000141 std::string msgToLog = "Read ssif request message with"
142 " len=" + std::to_string(rawIter[0] + 1) +
143 " netfn=" + std::to_string(netfn) +
144 " lun=" + std::to_string(lun) +
145 " cmd=" + std::to_string(cmd);
146 log<level::INFO>(msgToLog.c_str());
Dung Caofaf6a6a2020-12-28 04:44:45 +0000147 }
148 // copy out payload
149 std::vector<uint8_t> data(&rawIter[3], rawEnd);
150 // non-session bridges still need to pass an empty options map
151 std::map<std::string, std::variant<int>> options;
152 // the response is a tuple because dbus can only return a single value
153 using IpmiDbusRspType = std::tuple<uint8_t, uint8_t, uint8_t, uint8_t,
154 std::vector<uint8_t>>;
155 static constexpr const char ipmiQueueService[] =
156 "xyz.openbmc_project.Ipmi.Host";
157 static constexpr const char ipmiQueuePath[] =
158 "/xyz/openbmc_project/Ipmi";
159 static constexpr const char ipmiQueueIntf[] =
160 "xyz.openbmc_project.Ipmi.Server";
161 static constexpr const char ipmiQueueMethod[] = "execute";
162 bus->async_method_call(
163 [this, netfnCap{netfn}, lunCap{lun},
164 cmdCap{cmd}](const boost::system::error_code& ec,
165 const IpmiDbusRspType& response) {
166 std::vector<uint8_t> rsp;
167 const auto& [netfn, lun, cmd, cc, payload] = response;
168 if (ec)
169 {
Dung Caof06b8fd2021-01-29 03:31:43 +0000170 std::string msgToLog = "ssif<->ipmid bus error:"
171 " netfn=" + std::to_string(netfn) +
172 " lun=" + std::to_string(lun) +
173 " cmd=" + std::to_string(cmd) +
174 " error=" + ec.message();
175 log<level::ERR>(msgToLog.c_str());
Dung Caofaf6a6a2020-12-28 04:44:45 +0000176 // send unspecified error for a D-Bus error
177 constexpr uint8_t ccResponseNotAvailable = 0xce;
178 rsp.resize(sizeof(uint8_t) + sizeof(netfn) + sizeof(cmd) +
179 sizeof(cc));
180 rsp[0] = 3;
181 rsp[1] =
182 ((netfnCap + 1) << netFnShift) | (lunCap & lunMask);
183 rsp[2] = cmdCap;
184 rsp[3] = ccResponseNotAvailable;
185 }
186 else
187 {
188 rsp.resize(sizeof(uint8_t) + sizeof(netfn) + sizeof(cmd) +
189 sizeof(cc) + payload.size());
190
191 // write the response
192 auto rspIter = rsp.begin();
193 rspIter[0] = payload.size() + 3;
194 rspIter[1] = (netfn << netFnShift) | (lun & lunMask);
195 rspIter[2] = cmd;
196 rspIter[3] = cc;
197 if (payload.size())
198 {
199 std::copy(payload.cbegin(), payload.cend(),
200 &rspIter[4]);
201 }
202 }
203 if (verbose)
204 {
Dung Caof06b8fd2021-01-29 03:31:43 +0000205 std::string msgToLog = "Send ssif respond message with"
206 " len=" + std::to_string(payload.size() + 3) +
207 " netfn=" + std::to_string(netfn) +
208 " lun=" + std::to_string(lun) +
209 " cmd=" + std::to_string(cmd) +
210 " cc=" + std::to_string(cc);
211 log<level::INFO>(msgToLog.c_str());
Dung Caofaf6a6a2020-12-28 04:44:45 +0000212 }
213 boost::system::error_code ecWr;
214 size_t wlen =
215 boost::asio::write(*dev, boost::asio::buffer(rsp), ecWr);
216 if (ecWr || wlen != rsp.size())
217 {
Dung Caof06b8fd2021-01-29 03:31:43 +0000218 std::string msgToLog = "Failed to send ssif respond message:"
219 " size=" + std::to_string(wlen) +
220 " expect=" + std::to_string(rsp.size()) +
221 " error=" + ecWr.message() +
222 " netfn=" + std::to_string(netfn) +
223 " lun=" + std::to_string(lun) +
224 " cmd=" + std::to_string(cmd) +
225 " cc=" + std::to_string(cc);
226 log<level::ERR>(msgToLog.c_str());
Dung Caofaf6a6a2020-12-28 04:44:45 +0000227 }
228 },
229 ipmiQueueService, ipmiQueuePath, ipmiQueueIntf, ipmiQueueMethod,
230 netfn, lun, cmd, data, options);
231}
232
233
234int main(int argc, char* argv[])
235{
236 CLI::App app("SSIF IPMI bridge");
237 std::string device;
238 app.add_option("-d,--device", device,
239 "use <DEVICE> file. Default is /dev/ipmi-ssif-host");
240 bool verbose = false;
241 app.add_option("-v,--verbose", verbose, "print more verbose output");
242 CLI11_PARSE(app, argc, argv);
243
244 // Connect to system bus
245 auto io = std::make_shared<boost::asio::io_context>();
246 sd_bus* dbus;
247 sd_bus_default_system(&dbus);
248 auto bus = std::make_shared<sdbusplus::asio::connection>(*io, dbus);
249 bus->request_name(ssifBus);
250 // Create the SSIF channel, listening on D-Bus and on the SSIF device
251 SsifChannel ssifchannel(io, bus, device, verbose);
252 if (!ssifchannel.initOK())
253 {
254 return EXIT_FAILURE;
255 }
256 io->run();
257
258 return 0;
259}