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