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