xref: /openbmc/intel-ipmi-oem/src/sensorcommands.cpp (revision a021e32b2486b3a2248f141c78b8e3b365ecf5fd)
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