blob: 0de30af98c591004c3b86e370600723968a0e1be [file] [log] [blame]
/*
// Copyright (c) 2019 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 "xyz/openbmc_project/Common/error.hpp"
#include <fstream>
#include <ipmid/api.hpp>
#include <ipmid/utils.hpp>
#include <nlohmann/json.hpp>
#include <phosphor-logging/log.hpp>
#include <regex>
#include <xyz/openbmc_project/Software/Activation/server.hpp>
#include <xyz/openbmc_project/Software/Version/server.hpp>
#include <xyz/openbmc_project/State/BMC/server.hpp>
namespace ipmi
{
static void registerAPPFunctions() __attribute__((constructor));
namespace Log = phosphor::logging;
namespace Error = sdbusplus::xyz::openbmc_project::Common::Error;
using Version = sdbusplus::xyz::openbmc_project::Software::server::Version;
using Activation =
sdbusplus::xyz::openbmc_project::Software::server::Activation;
using BMC = sdbusplus::xyz::openbmc_project::State::server::BMC;
constexpr auto bmc_state_interface = "xyz.openbmc_project.State.BMC";
constexpr auto bmc_state_property = "CurrentBMCState";
namespace
{
static constexpr auto redundancyIntf =
"xyz.openbmc_project.Software.RedundancyPriority";
static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version";
static constexpr auto activationIntf =
"xyz.openbmc_project.Software.Activation";
static constexpr auto softwareRoot = "/xyz/openbmc_project/software";
bool getCurrentBmcState()
{
sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
// Get the Inventory object implementing the BMC interface
ipmi::DbusObjectInfo bmcObject =
ipmi::getDbusObject(bus, bmc_state_interface);
auto variant =
ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
bmc_state_interface, bmc_state_property);
return std::holds_alternative<std::string>(variant) &&
BMC::convertBMCStateFromString(std::get<std::string>(variant)) ==
BMC::BMCState::Ready;
}
bool getCurrentBmcStateWithFallback(const bool fallbackAvailability)
{
try
{
return getCurrentBmcState();
}
catch (...)
{
// Nothing provided the BMC interface, therefore return whatever was
// configured as the default.
return fallbackAvailability;
}
}
/**
* @brief Returns the Version info from primary s/w object
*
* Get the Version info from the active s/w object which is having high
* "Priority" value(a smaller number is a higher priority) and "Purpose"
* is "BMC" from the list of all s/w objects those are implementing
* RedundancyPriority interface from the given softwareRoot path.
*
* @return On success returns the Version info from primary s/w object.
*
*/
std::string getActiveSoftwareVersionInfo()
{
auto busp = getSdBus();
std::string revision{};
ipmi::ObjectTree objectTree;
try
{
objectTree =
ipmi::getAllDbusObjects(*busp, softwareRoot, redundancyIntf);
}
catch (sdbusplus::exception::SdBusError& e)
{
Log::log<Log::level::ERR>("Failed to fetch redundancy object from dbus",
Log::entry("INTERFACE=%s", redundancyIntf),
Log::entry("ERRMSG=%s", e.what()));
}
auto objectFound = false;
for (auto& softObject : objectTree)
{
auto service =
ipmi::getService(*busp, redundancyIntf, softObject.first);
auto objValueTree =
ipmi::getManagedObjects(*busp, service, softwareRoot);
auto minPriority = 0xFF;
for (const auto& objIter : objValueTree)
{
try
{
auto& intfMap = objIter.second;
auto& redundancyPriorityProps = intfMap.at(redundancyIntf);
auto& versionProps = intfMap.at(versionIntf);
auto& activationProps = intfMap.at(activationIntf);
auto priority =
std::get<uint8_t>(redundancyPriorityProps.at("Priority"));
auto purpose =
std::get<std::string>(versionProps.at("Purpose"));
auto activation =
std::get<std::string>(activationProps.at("Activation"));
auto version =
std::get<std::string>(versionProps.at("Version"));
if ((Version::convertVersionPurposeFromString(purpose) ==
Version::VersionPurpose::BMC) &&
(Activation::convertActivationsFromString(activation) ==
Activation::Activations::Active))
{
if (priority < minPriority)
{
minPriority = priority;
objectFound = true;
revision = std::move(version);
}
}
}
catch (const std::exception& e)
{
Log::log<Log::level::ERR>(e.what());
}
}
}
if (!objectFound)
{
Log::log<Log::level::ERR>("Could not found an BMC software Object");
}
return revision;
}
typedef struct
{
std::string platform;
uint8_t major;
uint8_t minor;
uint32_t buildNo;
std::string openbmcHash;
std::string metaHash;
std::string buildType;
} MetaRevision;
// Support both 2 solutions:
// 1.Current solution 2.7.0-dev-533-g14dc00e79-5e7d997
// openbmcTag 2.7.0-dev
// BuildNo 533
// openbmcHash g14dc00e79
// MetaHasg 5e7d997
//
// 2.New solution whtref-0.1-45-023125-a1295e-release
// IdStr whtref
// Major 0
// Minor 1
// buildNo 45
// openbmcHash 023125
// MetaHash a1295e
// BuildType release/CI/<devloperId>
std::optional<MetaRevision> convertIntelVersion(std::string& s)
{
std::smatch results;
MetaRevision rev;
std::regex pattern1("(\\d+?).(\\d+?).\\d+?-\\w*?-(\\d+?)-(\\w+?)-(\\w+?)");
constexpr size_t matchedPhosphor = 6;
if (std::regex_match(s, results, pattern1))
{
if (results.size() == matchedPhosphor)
{
rev.platform = "whtref";
rev.major = static_cast<uint8_t>(std::stoi(results[1]));
rev.minor = static_cast<uint8_t>(std::stoi(results[2]));
rev.buildNo = static_cast<uint32_t>(std::stoi(results[3]));
rev.openbmcHash = results[4];
rev.metaHash = results[5];
rev.buildType = "release";
std::string versionString =
rev.platform + ":" + std::to_string(rev.major) + ":" +
std::to_string(rev.minor) + ":" + std::to_string(rev.buildNo) +
":" + rev.openbmcHash + ":" + rev.metaHash + ":" +
rev.buildType;
phosphor::logging::log<phosphor::logging::level::INFO>(
versionString.c_str());
return rev;
}
}
constexpr size_t matchedIntel = 8;
std::regex pattern2(
"(\\w+?)-(\\d+?).(\\d+?)-(\\d+?)-(\\w+?)-(\\w+?)-(\\w+?)");
if (std::regex_match(s, results, pattern2))
{
if (results.size() == matchedIntel)
{
rev.platform = results[1];
rev.major = static_cast<uint8_t>(std::stoi(results[2]));
rev.minor = static_cast<uint8_t>(std::stoi(results[3]));
rev.buildNo = static_cast<uint32_t>(std::stoi(results[4]));
rev.openbmcHash = results[5];
rev.metaHash = results[6];
rev.buildType = results[7];
std::string versionString =
rev.platform + ":" + std::to_string(rev.major) + ":" +
std::to_string(rev.minor) + ":" + std::to_string(rev.buildNo) +
":" + rev.openbmcHash + ":" + rev.metaHash + ":" +
rev.buildType;
phosphor::logging::log<phosphor::logging::level::INFO>(
versionString.c_str());
return rev;
}
}
return std::nullopt;
}
} // namespace
auto ipmiAppGetDeviceId() -> ipmi::RspType<uint8_t, // Device ID
uint8_t, // Device Revision
uint8_t, // Firmware Revision Major
uint8_t, // Firmware Revision minor
uint8_t, // IPMI version
uint8_t, // Additional device support
uint24_t, // MFG ID
uint16_t, // Product ID
uint32_t // AUX info
>
{
std::optional<MetaRevision> rev;
static struct
{
uint8_t id;
uint8_t revision;
uint8_t fw[2];
uint8_t ipmiVer;
uint8_t addnDevSupport;
uint24_t manufId;
uint16_t prodId;
uint32_t aux;
} devId;
static bool dev_id_initialized = false;
static bool defaultActivationSetting = true;
const char* filename = "/usr/share/ipmi-providers/dev_id.json";
constexpr auto ipmiDevIdStateShift = 7;
constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
if (!dev_id_initialized)
{
try
{
auto version = getActiveSoftwareVersionInfo();
rev = convertIntelVersion(version);
}
catch (const std::exception& e)
{
Log::log<Log::level::ERR>(e.what());
}
if (rev.has_value())
{
// bit7 identifies if the device is available
// 0=normal operation
// 1=device firmware, SDR update,
// or self-initialization in progress.
// The availability may change in run time, so mask here
// and initialize later.
MetaRevision revision = rev.value();
devId.fw[0] = revision.major & ipmiDevIdFw1Mask;
revision.minor = (revision.minor > 99 ? 99 : revision.minor);
devId.fw[1] = revision.minor % 10 + (revision.minor / 10) * 16;
devId.aux = revision.buildNo;
}
// IPMI Spec version 2.0
devId.ipmiVer = 2;
std::ifstream devIdFile(filename);
if (devIdFile.is_open())
{
auto data = nlohmann::json::parse(devIdFile, nullptr, false);
if (!data.is_discarded())
{
devId.id = data.value("id", 0);
devId.revision = data.value("revision", 0);
devId.addnDevSupport = data.value("addn_dev_support", 0);
devId.manufId = data.value("manuf_id", 0);
try
{
auto busp = getSdBus();
const ipmi::DbusObjectInfo& object = ipmi::getDbusObject(
*busp, "xyz.openbmc_project.Inventory.Item.Board",
"/xyz/openbmc_project/inventory/system/board/",
"Baseboard");
const ipmi::Value& propValue = ipmi::getDbusProperty(
*busp, object.second, object.first,
"xyz.openbmc_project.Inventory.Item.Board",
"ProductId");
devId.prodId =
static_cast<uint8_t>(std::get<uint64_t>(propValue));
}
catch (std::exception& e)
{
devId.prodId = data.value("prod_id", 0);
}
// Set the availablitity of the BMC.
defaultActivationSetting = data.value("availability", true);
// Don't read the file every time if successful
dev_id_initialized = true;
}
else
{
Log::log<Log::level::ERR>("Device ID JSON parser failure");
return ipmi::responseUnspecifiedError();
}
}
else
{
Log::log<Log::level::ERR>("Device ID file not found");
return ipmi::responseUnspecifiedError();
}
}
// Set availability to the actual current BMC state
devId.fw[0] &= ipmiDevIdFw1Mask;
if (!getCurrentBmcStateWithFallback(defaultActivationSetting))
{
devId.fw[0] |= (1 << ipmiDevIdStateShift);
}
return ipmi::responseSuccess(
devId.id, devId.revision, devId.fw[0], devId.fw[1], devId.ipmiVer,
devId.addnDevSupport, devId.manufId, devId.prodId, devId.aux);
}
static void registerAPPFunctions(void)
{
Log::log<Log::level::INFO>("Registering App commands");
// <Get Device ID>
ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp,
ipmi::app::cmdGetDeviceId, ipmi::Privilege::User,
ipmiAppGetDeviceId);
return;
}
} // namespace ipmi