blob: 6b74deab2ed1c8a5c252ab18ae6e40a30d884525 [file] [log] [blame]
/*
// Copyright (c) 2017 2018 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/
#include "ipmi_to_redfish_hooks.hpp"
#include <boost/algorithm/string.hpp>
#include <boost/container/flat_map.hpp>
#include <ipmid/api.hpp>
#include <ipmid/utils.hpp>
#include <phosphor-logging/log.hpp>
#include <sdbusplus/bus.hpp>
#include <algorithm>
#include <array>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <memory>
#include <optional>
#include <string>
namespace ipmi
{
void registerSensorFunctions() __attribute__((constructor));
namespace meHealth
{
constexpr const char* busname = "xyz.openbmc_project.NodeManagerProxy";
constexpr const char* path = "/xyz/openbmc_project/status/me";
constexpr const char* interface = "xyz.openbmc_project.SetHealth";
constexpr const char* method = "SetHealth";
constexpr const char* critical = "critical";
constexpr const char* warning = "warning";
constexpr const char* ok = "ok";
} // namespace meHealth
static void setMeStatus(uint8_t eventData2, uint8_t eventData3, bool disable)
{
constexpr const std::array<uint8_t, 10> critical = {
0x1, 0x2, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xD, 0xE};
constexpr const std::array<uint8_t, 5> warning = {0x3, 0xA, 0x13, 0x19,
0x1A};
std::string state;
if (std::find(critical.begin(), critical.end(), eventData2) !=
critical.end())
{
state = meHealth::critical;
}
// special case 0x3 as we only care about a few states
else if (eventData2 == 0x3)
{
if (eventData3 <= 0x2)
{
state = meHealth::warning;
}
else
{
return;
}
}
else if (std::find(warning.begin(), warning.end(), eventData2) !=
warning.end())
{
state = meHealth::warning;
}
else
{
return;
}
if (disable)
{
state = meHealth::ok;
}
std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
auto setHealth =
dbus->new_method_call(meHealth::busname, meHealth::path,
meHealth::interface, meHealth::method);
setHealth.append(std::to_string(static_cast<size_t>(eventData2)), state);
try
{
dbus->call(setHealth);
}
catch (const sdbusplus::exception_t&)
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"Failed to set ME Health");
}
}
ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
ipmi::message::Payload& p)
{
constexpr const uint8_t meId = 0x2C;
constexpr const uint8_t meSensorNum = 0x17;
constexpr const uint8_t disabled = 0x80;
uint8_t sysgeneratorID = 0;
uint8_t evmRev = 0;
uint8_t sensorType = 0;
uint8_t sensorNum = 0;
uint8_t eventType = 0;
uint8_t eventData1 = 0;
std::optional<uint8_t> eventData2 = 0;
std::optional<uint8_t> eventData3 = 0;
uint16_t generatorID = 0;
ipmi::ChannelInfo chInfo;
if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"Failed to get Channel Info",
phosphor::logging::entry("CHANNEL=%d", ctx->channel));
return ipmi::responseUnspecifiedError();
}
if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
ipmi::EChannelMediumType::systemInterface)
{
p.unpack(sysgeneratorID, evmRev, sensorType, sensorNum, eventType,
eventData1, eventData2, eventData3);
constexpr const uint8_t isSoftwareID = 0x01;
if (!(sysgeneratorID & isSoftwareID))
{
return ipmi::responseInvalidFieldRequest();
}
// Refer to IPMI Spec Table 32: SEL Event Records
generatorID = (ctx->channel << 12) // Channel
| (0x0 << 10) // Reserved
| (0x0 << 8) // 0x0 for sys-soft ID
| sysgeneratorID;
}
else
{
p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
eventData2, eventData3);
// Refer to IPMI Spec Table 32: SEL Event Records
generatorID = (ctx->channel << 12) // Channel
| (0x0 << 10) // Reserved
| ((ctx->lun & 0x3) << 8) // Lun
| (ctx->rqSA << 1);
}
if (!p.fullyUnpacked())
{
return ipmi::responseReqDataLenInvalid();
}
// Check for valid evmRev and Sensor Type(per Table 42 of spec)
if (evmRev != 0x04)
{
return ipmi::responseInvalidFieldRequest();
}
if ((sensorType > 0x2C) && (sensorType < 0xC0))
{
return ipmi::responseInvalidFieldRequest();
}
// Send this request to the Redfish hooks to log it as a Redfish message
// instead. There is no need to add it to the SEL, so just return success.
intel_oem::ipmi::sel::checkRedfishHooks(
generatorID, evmRev, sensorType, sensorNum, eventType, eventData1,
eventData2.value_or(0xFF), eventData3.value_or(0xFF));
if (static_cast<uint8_t>(generatorID) == meId && sensorNum == meSensorNum &&
eventData2 && eventData3)
{
setMeStatus(*eventData2, *eventData3, (eventType & disabled));
}
return ipmi::responseSuccess();
}
void registerSensorFunctions()
{
// <Platform Event>
ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
ipmi::sensor_event::cmdPlatformEvent,
ipmi::Privilege::Operator, ipmiSenPlatformEvent);
}
} // namespace ipmi