1 #include "config.h"
2 
3 #include "systemintfcmds.hpp"
4 
5 #include "host-cmd-manager.hpp"
6 #include "host-interface.hpp"
7 
8 #include <ipmid-host/cmd.hpp>
9 #include <ipmid/api.hpp>
10 #include <nlohmann/json.hpp>
11 #include <phosphor-logging/lg2.hpp>
12 
13 #include <cstring>
14 #include <fstream>
15 
16 void register_netfn_app_functions() __attribute__((constructor));
17 
18 using namespace sdbusplus::server::xyz::openbmc_project::control;
19 
20 // For accessing Host command manager
21 using cmdManagerPtr = std::unique_ptr<phosphor::host::command::Manager>;
22 extern cmdManagerPtr& ipmid_get_host_cmd_manager();
23 
24 //-------------------------------------------------------------------
25 // Called by Host post response from Get_Message_Flags
26 //-------------------------------------------------------------------
27 ipmi::RspType<uint16_t,              // id
28               uint8_t,               // type
29               uint24_t,              //  manuf_id
30               uint32_t,              // timestamp
31               uint8_t,               // netfun
32               uint8_t,               // cmd
33               std::array<uint8_t, 4> // data
34               >
ipmiAppReadEventBuffer(ipmi::Context::ptr & ctx)35     ipmiAppReadEventBuffer(ipmi::Context::ptr& ctx)
36 {
37     // require this to be limited to system interface
38     if (ctx->channel != ipmi::channelSystemIface)
39     {
40         return ipmi::responseInvalidCommand();
41     }
42 
43     constexpr uint16_t selOemId = 0x5555;
44     constexpr uint8_t selRecordTypeOem = 0xc0;
45 
46     // read manufacturer ID from dev_id file
47     static uint24_t manufId{};
48     if (!manufId)
49     {
50         const char* filename = "/usr/share/ipmi-providers/dev_id.json";
51         std::ifstream devIdFile(filename);
52         if (devIdFile.is_open())
53         {
54             auto data = nlohmann::json::parse(devIdFile, nullptr, false);
55             if (!data.is_discarded())
56             {
57                 manufId = data.value("manuf_id", 0);
58             }
59         }
60     }
61 
62     constexpr uint32_t timestamp{0};
63 
64     // per IPMI spec NetFuntion for OEM
65     constexpr uint8_t netfun = 0x3a;
66 
67     // Read from the Command Manager queue. What gets returned is a
68     // pair of <command, data> that can be directly used here
69     const auto& [cmd, data0] = ipmid_get_host_cmd_manager()->getNextCommand();
70     constexpr uint8_t dataUnused = 0xff;
71 
72     return ipmi::responseSuccess(
73         selOemId, selRecordTypeOem, manufId, timestamp, netfun, cmd,
74         std::to_array<uint8_t>({data0, dataUnused, dataUnused, dataUnused}));
75 }
76 
77 //---------------------------------------------------------------------
78 // Called by Host on seeing a SMS_ATN bit set. Return a hardcoded
79 // value of 0x0 to indicate Event Message Buffer is not supported
80 //-------------------------------------------------------------------
ipmiAppGetMessageFlags()81 ipmi::RspType<uint8_t> ipmiAppGetMessageFlags()
82 {
83     // From IPMI spec V2.0 for Get Message Flags Command :
84     // bit:[1] from LSB : 1b = Event Message Buffer Full.
85     // Return as 0 if Event Message Buffer is not supported,
86     // or when the Event Message buffer is disabled.
87     // This path is used to communicate messages to the host
88     // from within the phosphor::host::command::Manager
89     constexpr uint8_t setEventMsgBufferNotSupported = 0x0;
90     return ipmi::responseSuccess(setEventMsgBufferNotSupported);
91 }
92 
93 ipmi::RspType<bool,    // Receive Message Queue Interrupt Enabled
94               bool,    // Event Message Buffer Full Interrupt Enabled
95               bool,    // Event Message Buffer Enabled
96               bool,    // System Event Logging Enabled
97               uint1_t, // Reserved
98               bool,    // OEM 0 enabled
99               bool,    // OEM 1 enabled
100               bool     // OEM 2 enabled
101               >
ipmiAppGetBMCGlobalEnable()102     ipmiAppGetBMCGlobalEnable()
103 {
104     return ipmi::responseSuccess(true, false, false, true, 0, false, false,
105                                  false);
106 }
107 
ipmiAppSetBMCGlobalEnable(ipmi::Context::ptr ctx,bool receiveMessageQueueInterruptEnabled,bool eventMessageBufferFullInterruptEnabled,bool eventMessageBufferEnabled,bool systemEventLogEnable,uint1_t reserved,bool OEM0Enabled,bool OEM1Enabled,bool OEM2Enabled)108 ipmi::RspType<> ipmiAppSetBMCGlobalEnable(
109     ipmi::Context::ptr ctx, bool receiveMessageQueueInterruptEnabled,
110     bool eventMessageBufferFullInterruptEnabled, bool eventMessageBufferEnabled,
111     bool systemEventLogEnable, uint1_t reserved, bool OEM0Enabled,
112     bool OEM1Enabled, bool OEM2Enabled)
113 {
114     ipmi::ChannelInfo chInfo;
115 
116     if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
117     {
118         lg2::error("Failed to get Channel Info, channel={CHANNEL}", "CHANNEL",
119                    ctx->channel);
120         return ipmi::responseUnspecifiedError();
121     }
122 
123     if (chInfo.mediumType !=
124         static_cast<uint8_t>(ipmi::EChannelMediumType::systemInterface))
125     {
126         lg2::error("Error - supported only in system interface");
127         return ipmi::responseCommandNotAvailable();
128     }
129 
130     // Recv Message Queue and SEL are enabled by default.
131     // Event Message buffer are disabled by default (not supported).
132     // Any request that try to change the mask will be rejected
133     if (!receiveMessageQueueInterruptEnabled || !systemEventLogEnable ||
134         eventMessageBufferFullInterruptEnabled || eventMessageBufferEnabled ||
135         OEM0Enabled || OEM1Enabled || OEM2Enabled || reserved)
136     {
137         return ipmi::responseInvalidFieldRequest();
138     }
139 
140     return ipmi::responseSuccess();
141 }
142 
143 namespace
144 {
145 // Static storage to keep the object alive during process life
146 std::unique_ptr<phosphor::host::command::Host> host
147     __attribute__((init_priority(101)));
148 std::unique_ptr<sdbusplus::server::manager_t> objManager
149     __attribute__((init_priority(101)));
150 } // namespace
151 
register_netfn_app_functions()152 void register_netfn_app_functions()
153 {
154     // <Read Event Message Buffer>
155     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
156                           ipmi::app::cmdReadEventMessageBuffer,
157                           ipmi::Privilege::Admin, ipmiAppReadEventBuffer);
158 
159     // <Set BMC Global Enables>
160     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
161                           ipmi::app::cmdSetBmcGlobalEnables,
162                           ipmi::Privilege::Admin, ipmiAppSetBMCGlobalEnable);
163 
164     // <Get BMC Global Enables>
165     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
166                           ipmi::app::cmdGetBmcGlobalEnables,
167                           ipmi::Privilege::User, ipmiAppGetBMCGlobalEnable);
168 
169     // <Get Message Flags>
170     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
171                           ipmi::app::cmdGetMessageFlags, ipmi::Privilege::Admin,
172                           ipmiAppGetMessageFlags);
173 
174     // Create new xyz.openbmc_project.host object on the bus
175     auto objPath = std::string{CONTROL_HOST_OBJ_MGR} + '/' + HOST_NAME + '0';
176 
177     std::unique_ptr<sdbusplus::asio::connection>& sdbusp =
178         ipmid_get_sdbus_plus_handler();
179 
180     // Add sdbusplus ObjectManager.
181     objManager = std::make_unique<sdbusplus::server::manager_t>(
182         *sdbusp, CONTROL_HOST_OBJ_MGR);
183 
184     host = std::make_unique<phosphor::host::command::Host>(
185         *sdbusp, objPath.c_str());
186     sdbusp->request_name(CONTROL_HOST_BUSNAME);
187 
188     return;
189 }
190