xref: /openbmc/kcsbridge/src/cmd.cpp (revision 03e6defc)
1 #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>
11 #include <stdplus/types.hpp>
12 
13 #include <array>
14 #include <map>
15 #include <stdexcept>
16 #include <tuple>
17 #include <utility>
18 #include <variant>
19 #include <vector>
20 
21 namespace kcsbridge
22 {
23 
24 using sdbusplus::bus::bus;
25 using sdbusplus::message::message;
26 using sdbusplus::slot::slot;
27 
28 void write(stdplus::Fd& kcs, message&& m)
29 {
30     std::array<uint8_t, 1024> buffer;
31     stdplus::span<uint8_t> out(buffer.begin(), 3);
32     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());
56         out = stdplus::span<uint8_t>(buffer.begin(), data.size() + 3);
57     }
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 
67 void 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
100