blob: aa8bbae22eb390efc46a239bbb531427c7f99cb6 [file] [log] [blame]
William A. Kennington III03e6def2021-05-11 15:23:15 -07001#include "cmd.hpp"
2
3#include <fmt/format.h>
4
5#include <sdbusplus/bus.hpp>
6#include <sdbusplus/exception.hpp>
7#include <sdbusplus/message.hpp>
8#include <sdbusplus/slot.hpp>
9#include <stdplus/exception.hpp>
10#include <stdplus/fd/ops.hpp>
William A. Kennington III03e6def2021-05-11 15:23:15 -070011
12#include <array>
13#include <map>
Patrick Williams08041c62021-10-06 11:08:34 -050014#include <span>
William A. Kennington III03e6def2021-05-11 15:23:15 -070015#include <stdexcept>
16#include <tuple>
17#include <utility>
18#include <variant>
19#include <vector>
20
21namespace kcsbridge
22{
23
24using sdbusplus::bus::bus;
25using sdbusplus::message::message;
26using sdbusplus::slot::slot;
27
28void write(stdplus::Fd& kcs, message&& m)
29{
30 std::array<uint8_t, 1024> buffer;
Patrick Williams08041c62021-10-06 11:08:34 -050031 std::span<uint8_t> out(buffer.begin(), 3);
William A. Kennington III03e6def2021-05-11 15:23:15 -070032 try
33 {
34 if (m.is_method_error())
35 {
36 // Extra copy to workaround lack of `const sd_bus_error` constructor
37 auto error = *m.get_error();
38 throw sdbusplus::exception::SdBusError(&error, "ipmid response");
39 }
40 std::tuple<uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
41 ret;
42 m.read(ret);
43 const auto& [netfn, lun, cmd, cc, data] = ret;
44 // Based on the IPMI KCS spec Figure 9-2
45 // netfn needs to be changed to odd in KCS responses
46 if (data.size() + 3 > buffer.size())
47 {
48 throw std::runtime_error(fmt::format(
49 "too large {} > {}", data.size() + 3, buffer.size()));
50 }
51 buffer[0] = (netfn | 1) << 2;
52 buffer[0] |= lun;
53 buffer[1] = cmd;
54 buffer[2] = cc;
55 memcpy(&buffer[3], data.data(), data.size());
Patrick Williams08041c62021-10-06 11:08:34 -050056 out = std::span<uint8_t>(buffer.begin(), data.size() + 3);
William A. Kennington III03e6def2021-05-11 15:23:15 -070057 }
58 catch (const std::exception& e)
59 {
60 fmt::print(stderr, "IPMI response failure: {}\n", e.what());
61 buffer[0] |= 1 << 2;
62 buffer[2] = 0xff;
63 }
64 stdplus::fd::writeExact(kcs, out);
65}
66
67void read(stdplus::Fd& kcs, bus& bus, slot& outstanding)
68{
69 std::array<uint8_t, 1024> buffer;
70 auto in = stdplus::fd::read(kcs, buffer);
71 if (in.empty())
72 {
73 return;
74 }
75 if (outstanding)
76 {
77 fmt::print(stderr, "Canceling outstanding request\n");
78 outstanding = slot(nullptr);
79 }
80 if (in.size() < 2)
81 {
82 fmt::print(stderr, "Read too small, ignoring\n");
83 return;
84 }
85 auto m = bus.new_method_call("xyz.openbmc_project.Ipmi.Host",
86 "/xyz/openbmc_project/Ipmi",
87 "xyz.openbmc_project.Ipmi.Server", "execute");
88 std::map<std::string, std::variant<int>> options;
89 // Based on the IPMI KCS spec Figure 9-1
90 uint8_t netfn = in[0] >> 2, lun = in[0] & 3, cmd = in[1];
91 m.append(netfn, lun, cmd, in.subspan(2), options);
92 outstanding = m.call_async(
93 stdplus::exception::ignore([&outstanding, &kcs](message&& m) {
94 outstanding = slot(nullptr);
95 write(kcs, std::move(m));
96 }));
97}
98
99} // namespace kcsbridge