1 /*
2 // Copyright (c) 2017 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 
17 #include "ipmi_to_redfish_hooks.hpp"
18 
19 #include <boost/algorithm/string.hpp>
20 #include <boost/container/flat_map.hpp>
21 #include <ipmid/api.hpp>
22 #include <ipmid/utils.hpp>
23 #include <phosphor-logging/log.hpp>
24 #include <sdbusplus/bus.hpp>
25 
26 #include <algorithm>
27 #include <array>
28 #include <cmath>
29 #include <cstdint>
30 #include <cstring>
31 #include <memory>
32 #include <optional>
33 #include <string>
34 
35 namespace ipmi
36 {
37 void registerSensorFunctions() __attribute__((constructor));
38 
39 namespace meHealth
40 {
41 constexpr const char* busname = "xyz.openbmc_project.NodeManagerProxy";
42 constexpr const char* path = "/xyz/openbmc_project/status/me";
43 constexpr const char* interface = "xyz.openbmc_project.SetHealth";
44 constexpr const char* method = "SetHealth";
45 constexpr const char* critical = "critical";
46 constexpr const char* warning = "warning";
47 constexpr const char* ok = "ok";
48 } // namespace meHealth
49 
setMeStatus(uint8_t eventData2,uint8_t eventData3,bool disable)50 static void setMeStatus(uint8_t eventData2, uint8_t eventData3, bool disable)
51 {
52     constexpr const std::array<uint8_t, 10> critical = {
53         0x1, 0x2, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xD, 0xE};
54     constexpr const std::array<uint8_t, 5> warning = {0x3, 0xA, 0x13, 0x19,
55                                                       0x1A};
56 
57     std::string state;
58     if (std::find(critical.begin(), critical.end(), eventData2) !=
59         critical.end())
60     {
61         state = meHealth::critical;
62     }
63     // special case 0x3 as we only care about a few states
64     else if (eventData2 == 0x3)
65     {
66         if (eventData3 <= 0x2)
67         {
68             state = meHealth::warning;
69         }
70         else
71         {
72             return;
73         }
74     }
75     else if (std::find(warning.begin(), warning.end(), eventData2) !=
76              warning.end())
77     {
78         state = meHealth::warning;
79     }
80     else
81     {
82         return;
83     }
84     if (disable)
85     {
86         state = meHealth::ok;
87     }
88 
89     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
90     auto setHealth =
91         dbus->new_method_call(meHealth::busname, meHealth::path,
92                               meHealth::interface, meHealth::method);
93     setHealth.append(std::to_string(static_cast<size_t>(eventData2)), state);
94     try
95     {
96         dbus->call(setHealth);
97     }
98     catch (const sdbusplus::exception_t&)
99     {
100         phosphor::logging::log<phosphor::logging::level::ERR>(
101             "Failed to set ME Health");
102     }
103 }
104 
ipmiSenPlatformEvent(ipmi::Context::ptr ctx,ipmi::message::Payload & p)105 ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
106                                      ipmi::message::Payload& p)
107 {
108     constexpr const uint8_t meId = 0x2C;
109     constexpr const uint8_t meSensorNum = 0x17;
110     constexpr const uint8_t disabled = 0x80;
111 
112     uint8_t sysgeneratorID = 0;
113     uint8_t evmRev = 0;
114     uint8_t sensorType = 0;
115     uint8_t sensorNum = 0;
116     uint8_t eventType = 0;
117     uint8_t eventData1 = 0;
118     std::optional<uint8_t> eventData2 = 0;
119     std::optional<uint8_t> eventData3 = 0;
120     uint16_t generatorID = 0;
121     ipmi::ChannelInfo chInfo;
122 
123     if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
124     {
125         phosphor::logging::log<phosphor::logging::level::ERR>(
126             "Failed to get Channel Info",
127             phosphor::logging::entry("CHANNEL=%d", ctx->channel));
128         return ipmi::responseUnspecifiedError();
129     }
130 
131     if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
132         ipmi::EChannelMediumType::systemInterface)
133     {
134         p.unpack(sysgeneratorID, evmRev, sensorType, sensorNum, eventType,
135                  eventData1, eventData2, eventData3);
136         constexpr const uint8_t isSoftwareID = 0x01;
137         if (!(sysgeneratorID & isSoftwareID))
138         {
139             return ipmi::responseInvalidFieldRequest();
140         }
141         // Refer to IPMI Spec Table 32: SEL Event Records
142         generatorID = (ctx->channel << 12) // Channel
143                       | (0x0 << 10)        // Reserved
144                       | (0x0 << 8)         // 0x0 for sys-soft ID
145                       | sysgeneratorID;
146     }
147     else
148     {
149         p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
150                  eventData2, eventData3);
151         // Refer to IPMI Spec Table 32: SEL Event Records
152         generatorID = (ctx->channel << 12)      // Channel
153                       | (0x0 << 10)             // Reserved
154                       | ((ctx->lun & 0x3) << 8) // Lun
155                       | (ctx->rqSA << 1);
156     }
157 
158     if (!p.fullyUnpacked())
159     {
160         return ipmi::responseReqDataLenInvalid();
161     }
162 
163     // Check for valid evmRev and Sensor Type(per Table 42 of spec)
164     if (evmRev != 0x04)
165     {
166         return ipmi::responseInvalidFieldRequest();
167     }
168     if ((sensorType > 0x2C) && (sensorType < 0xC0))
169     {
170         return ipmi::responseInvalidFieldRequest();
171     }
172 
173     // Send this request to the Redfish hooks to log it as a Redfish message
174     // instead.  There is no need to add it to the SEL, so just return success.
175     intel_oem::ipmi::sel::checkRedfishHooks(
176         generatorID, evmRev, sensorType, sensorNum, eventType, eventData1,
177         eventData2.value_or(0xFF), eventData3.value_or(0xFF));
178 
179     if (static_cast<uint8_t>(generatorID) == meId && sensorNum == meSensorNum &&
180         eventData2 && eventData3)
181     {
182         setMeStatus(*eventData2, *eventData3, (eventType & disabled));
183     }
184 
185     return ipmi::responseSuccess();
186 }
187 
registerSensorFunctions()188 void registerSensorFunctions()
189 {
190     // <Platform Event>
191     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
192                           ipmi::sensor_event::cmdPlatformEvent,
193                           ipmi::Privilege::Operator, ipmiSenPlatformEvent);
194 }
195 } // namespace ipmi
196