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 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 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 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