blob: 0f0416ef12da60ffd2b2de18ea3736f74483dac0 [file] [log] [blame]
#include <fmt/format.h>
#include <util/dbus.hpp>
#include <util/trace.hpp>
#include <xyz/openbmc_project/State/Boot/Progress/server.hpp>
namespace util
{
namespace dbus
{
//------------------------------------------------------------------------------
constexpr auto objectMapperService = "xyz.openbmc_project.ObjectMapper";
constexpr auto objectMapperPath = "/xyz/openbmc_project/object_mapper";
constexpr auto objectMapperInterface = "xyz.openbmc_project.ObjectMapper";
constexpr uint8_t terminusIdZero = 0;
/** @brief Find the path and service that implements the given interface */
int find(const std::string& i_interface, std::string& o_path,
std::string& o_service)
{
int rc = 1; // assume not success
auto bus = sdbusplus::bus::new_default();
try
{
constexpr auto function = "GetSubTree";
auto method = bus.new_method_call(objectMapperService, objectMapperPath,
objectMapperInterface, function);
// Search the entire dbus tree for the specified interface
method.append(std::string{"/"}, 0,
std::vector<std::string>{i_interface});
auto reply = bus.call(method);
DBusSubTree response;
reply.read(response);
if (!response.empty())
{
// Response is a map of object paths to a map of service, interfaces
auto object = *(response.begin());
o_path = object.first; // return path
o_service = object.second.begin()->first; // return service
rc = 0; // success
}
}
catch (const sdbusplus::exception::SdBusError& e)
{
trace::err("util::dbus::find exception");
std::string traceMsg = std::string(e.what());
trace::err(traceMsg.c_str());
}
return rc;
}
/** @brief Find the service that implements the given object and interface */
int findService(const std::string& i_interface, const std::string& i_path,
std::string& o_service)
{
int rc = 1; // assume not success
auto bus = sdbusplus::bus::new_default();
try
{
constexpr auto function = "GetObject";
auto method = bus.new_method_call(objectMapperService, objectMapperPath,
objectMapperInterface, function);
// Find services that implement the object path, constrain the search
// to the given interface.
method.append(i_path, std::vector<std::string>{i_interface});
auto reply = bus.call(method);
// response is a map of service names to their interfaces
std::map<DBusService, DBusInterfaceList> response;
reply.read(response);
if (!response.empty())
{
// return the service
o_service = response.begin()->first;
rc = 0; // success
}
}
catch (const sdbusplus::exception::SdBusError& e)
{
trace::err("util::dbus::map exception");
std::string traceMsg = std::string(e.what());
trace::err(traceMsg.c_str());
}
return rc;
}
/** @brief Read a property from a dbus object interface */
int getProperty(const std::string& i_interface, const std::string& i_path,
const std::string& i_service, const std::string& i_property,
DBusValue& o_response)
{
int rc = 1; // assume not success
auto bus = sdbusplus::bus::new_default();
try
{
constexpr auto interface = "org.freedesktop.DBus.Properties";
constexpr auto function = "Get";
// calling the get property method
auto method = bus.new_method_call(i_service.c_str(), i_path.c_str(),
interface, function);
method.append(i_interface, i_property);
auto reply = bus.call(method);
// returning the property value
reply.read(o_response);
rc = 0; // success
}
catch (const sdbusplus::exception::SdBusError& e)
{
trace::err("util::dbus::getProperty exception");
std::string traceMsg = std::string(e.what());
trace::err(traceMsg.c_str());
}
return rc;
}
/** @brief Get the IBM compatible names defined for this system */
std::vector<std::string> systemNames()
{
std::vector<std::string> names;
constexpr auto interface =
"xyz.openbmc_project.Configuration.IBMCompatibleSystem";
DBusService service;
DBusPath path;
// find a dbus object and path that implements the interface
if (0 == find(interface, path, service))
{
DBusValue value;
// compatible system names are implemented as a property
constexpr auto property = "Names";
if (0 == getProperty(interface, path, service, property, value))
{
// return value is a variant, names are in the vector
names = std::get<std::vector<std::string>>(value);
}
}
return names;
}
/** @brief Transition the host state */
void transitionHost(const HostState i_hostState)
{
try
{
// We will be transitioning host by starting appropriate dbus target
std::string target = "obmc-host-quiesce@0.target"; // quiesce is default
// crash (mpipl) mode state requested
if (HostState::Crash == i_hostState)
{
target = "obmc-host-crash@0.target";
}
// If the system is powering off for any reason (ex. we hit a PHYP TI
// in the graceful power off path), then we want to call the immediate
// power off target
if (hostRunningState() == HostRunningState::Stopping)
{
trace::inf("system is powering off so no dump will be requested");
target = "obmc-chassis-hard-poweroff@0.target";
}
auto bus = sdbusplus::bus::new_system();
auto method = bus.new_method_call(
"org.freedesktop.systemd1", "/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager", "StartUnit");
method.append(target); // target unit to start
method.append("replace"); // mode = replace conflicting queued jobs
bus.call_noreply(method); // start the service
}
catch (const sdbusplus::exception::SdBusError& e)
{
trace::err("util::dbus::transitionHost exception");
std::string traceMsg = std::string(e.what());
trace::err(traceMsg.c_str());
}
}
/** @brief Read state of autoRebootEnabled property via dbus */
bool autoRebootEnabled()
{
// Assume true in case autoRebootEnbabled property is not available
bool autoReboot = true;
constexpr auto interface = "xyz.openbmc_project.Control.Boot.RebootPolicy";
DBusService service; // will find this
DBusPath path; // will find this
// find a dbus object and path that implements the interface
if (0 == find(interface, path, service))
{
DBusValue value;
// autoreboot policy is implemented as a property
constexpr auto property = "AutoReboot";
if (0 == getProperty(interface, path, service, property, value))
{
// return value is a variant, autoreboot policy is boolean
autoReboot = std::get<bool>(value);
}
}
return autoReboot;
}
/** @brief Get the running state of the host */
HostRunningState hostRunningState()
{
// assume not able to get host running state
HostRunningState host = HostRunningState::Unknown;
constexpr auto interface = "xyz.openbmc_project.State.Boot.Progress";
DBusService service;
DBusPath path;
// find a dbus object and path that implements the interface
if (0 == find(interface, path, service))
{
DBusValue value;
// boot progress is implemented as a property
constexpr auto property = "BootProgress";
if (0 == getProperty(interface, path, service, property, value))
{
// return value is a variant, progress is in the vector of strings
std::string bootProgress(std::get<std::string>(value));
// convert boot progress to host state
using BootProgress = sdbusplus::xyz::openbmc_project::State::Boot::
server::Progress::ProgressStages;
BootProgress stage = sdbusplus::xyz::openbmc_project::State::Boot::
server::Progress::convertProgressStagesFromString(bootProgress);
if ((stage == BootProgress::SystemInitComplete) ||
(stage == BootProgress::OSStart) ||
(stage == BootProgress::OSRunning))
{
host = HostRunningState::Started;
}
else
{
host = HostRunningState::NotStarted;
}
}
}
// See if host in process of powering off when we get NotStarted
if (host == HostRunningState::NotStarted)
{
constexpr auto hostStateInterface = "xyz.openbmc_project.State.Host";
if (0 == find(hostStateInterface, path, service))
{
DBusValue value;
// current host state is implemented as a property
constexpr auto stateProperty = "CurrentHostState";
if (0 == getProperty(hostStateInterface, path, service,
stateProperty, value))
{
// return value is a variant, host state is in the vector of
// strings
std::string hostState(std::get<std::string>(value));
if (hostState == "xyz.openbmc_project.State.Host.HostState."
"TransitioningToOff")
{
host = HostRunningState::Stopping;
}
}
}
}
return host;
}
/** @brief Read state of dumpPolicyEnabled property via dbus */
bool dumpPolicyEnabled()
{
// Assume true In case dumpPolicyEnabled property is not available
bool dumpPolicyEnabled = true;
constexpr auto interface = "xyz.openbmc_project.Object.Enable";
constexpr auto path = "/xyz/openbmc_project/dump/system_dump_policy";
DBusService service; // will find this
// find a dbus object and path that implements the interface
if (0 == findService(interface, path, service))
{
DBusValue value;
// autoreboot policy is implemented as a property
constexpr auto property = "Enabled";
if (0 == getProperty(interface, path, service, property, value))
{
// return value is a variant, dump policy enabled is a boolean
dumpPolicyEnabled = std::get<bool>(value);
}
}
return dumpPolicyEnabled;
}
/** @brief Create a PEL */
uint32_t createPel(const std::string& i_message, const std::string& i_severity,
std::map<std::string, std::string>& io_additional,
const std::vector<util::FFDCTuple>& i_ffdc)
{
// CreatePELWithFFDCFiles returns plid
int plid = 0;
// Sdbus call specifics
constexpr auto interface = "org.open_power.Logging.PEL";
constexpr auto path = "/xyz/openbmc_project/logging";
// we need to find the service implementing the interface
util::dbus::DBusService service;
if (0 == findService(interface, path, service))
{
try
{
constexpr auto function = "CreatePELWithFFDCFiles";
// The "Create" method requires manually adding the process ID.
io_additional["_PID"] = std::to_string(getpid());
// create dbus method
auto bus = sdbusplus::bus::new_system();
sdbusplus::message_t method =
bus.new_method_call(service.c_str(), path, interface, function);
// append additional dbus call paramaters
method.append(i_message, i_severity, io_additional, i_ffdc);
// using system dbus
auto response = bus.call(method);
// reply will be tuple containing bmc log id, platform log id
std::tuple<uint32_t, uint32_t> reply = {0, 0};
// parse dbus response into reply
response.read(reply);
plid = std::get<1>(reply); // platform log id is tuple "second"
}
catch (const sdbusplus::exception::SdBusError& e)
{
trace::err("createPel exception");
trace::err(e.what());
}
}
return plid; // platform log id or 0
}
MachineType getMachineType()
{
// default to Rainier 2S4U
MachineType machineType = MachineType::Rainier_2S4U;
// The return value of the dbus operation is a vector of 4 uint8_ts
std::vector<uint8_t> ids;
constexpr auto interface = "com.ibm.ipzvpd.VSBP";
DBusService service;
DBusPath path;
if (0 == find(interface, path, service))
{
DBusValue value;
// Machine ID is given from the "IM" keyword
constexpr auto property = "IM";
if (0 == getProperty(interface, path, service, property, value))
{
// return value is a variant, ID value is a vector of 4 uint8_ts
ids = std::get<std::vector<uint8_t>>(value);
// Convert the returned ID value to a hex string to determine
// machine type. The hex values corresponding to the machine type
// are defined in /openbmc/openpower-vpd-parser/const.hpp
// RAINIER_2S4U == 0x50001000
// RAINIER_2S2U == 0x50001001
// RAINIER_1S4U == 0x50001002
// RAINIER_1S2U == 0x50001003
// EVEREST == 0x50003000
try
{
// Format the vector into a single hex string to compare to.
std::string hexId =
fmt::format("0x{:02x}{:02x}{:02x}{:02x}", ids.at(0),
ids.at(1), ids.at(2), ids.at(3));
std::map<std::string, MachineType> typeMap = {
{"0x50001000", MachineType::Rainier_2S4U},
{"0x50001001", MachineType::Rainier_2S2U},
{"0x50001002", MachineType::Rainier_1S4U},
{"0x50001003", MachineType::Rainier_1S2U},
{"0x50003000", MachineType::Everest},
};
machineType = typeMap.at(hexId);
}
catch (const std::out_of_range& e)
{
trace::err("Out of range exception caught from returned "
"machine ID.");
for (const auto& id : ids)
{
trace::err("Returned Machine ID value: 0x%x", id);
}
throw;
}
}
}
else
{
throw std::invalid_argument(
"Unable to find dbus service to get machine type.");
}
return machineType;
}
/** @brief Get list of state effecter PDRs */
bool getStateEffecterPdrs(std::vector<std::vector<uint8_t>>& pdrList,
uint16_t stateSetId)
{
constexpr auto service = "xyz.openbmc_project.PLDM";
constexpr auto path = "/xyz/openbmc_project/pldm";
constexpr auto interface = "xyz.openbmc_project.PLDM.PDR";
constexpr auto function = "FindStateEffecterPDR";
constexpr uint16_t PLDM_ENTITY_PROC = 135;
try
{
// create dbus method
auto bus = sdbusplus::bus::new_default();
sdbusplus::message_t method =
bus.new_method_call(service, path, interface, function);
// append additional method data
method.append(terminusIdZero, PLDM_ENTITY_PROC, stateSetId);
// request PDRs
auto reply = bus.call(method);
reply.read(pdrList);
}
catch (const sdbusplus::exception_t& e)
{
trace::err("failed to find state effecter PDRs");
trace::err(e.what());
return false;
}
return true;
}
/** @brief Get list of state sensor PDRs */
bool getStateSensorPdrs(std::vector<std::vector<uint8_t>>& pdrList,
uint16_t stateSetId)
{
constexpr auto service = "xyz.openbmc_project.PLDM";
constexpr auto path = "/xyz/openbmc_project/pldm";
constexpr auto interface = "xyz.openbmc_project.PLDM.PDR";
constexpr auto function = "FindStateSensorPDR";
constexpr uint16_t PLDM_ENTITY_PROC = 135;
try
{
// create dbus method
auto bus = sdbusplus::bus::new_default();
sdbusplus::message_t method =
bus.new_method_call(service, path, interface, function);
// append additional method data
method.append(terminusIdZero, PLDM_ENTITY_PROC, stateSetId);
// request PDRs
auto reply = bus.call(method);
reply.read(pdrList);
}
catch (const sdbusplus::exception_t& e)
{
trace::err("failed to find state sensor PDRs");
trace::err(e.what());
return false;
}
return true;
}
/** @brief Get MCTP instance associated with endpoint */
bool getMctpInstance(uint8_t& mctpInstance, uint8_t Eid)
{
constexpr auto service = "xyz.openbmc_project.PLDM";
constexpr auto path = "/xyz/openbmc_project/pldm";
constexpr auto interface = "xyz.openbmc_project.PLDM.Requester";
constexpr auto function = "GetInstanceId";
try
{
// create dbus method
auto bus = sdbusplus::bus::new_default();
sdbusplus::message_t method =
bus.new_method_call(service, path, interface, function);
// append endpoint ID
method.append(Eid);
// request MCTP instance ID
auto reply = bus.call(method);
reply.read(mctpInstance);
}
catch (const sdbusplus::exception_t& e)
{
trace::err("get MCTP instance exception");
trace::err(e.what());
return false;
}
return true;
}
/** @brief Determine if power fault was detected */
bool powerFault()
{
// power fault based on pgood property
int32_t pgood = 0; // assume fault or unknown
constexpr auto interface = "org.openbmc.control.Power";
DBusService service;
DBusPath path;
// find a dbus service and object path that implements the interface
if (0 == find(interface, path, service))
{
DBusValue value;
// chassis pgood is implemented as a property
constexpr auto property = "pgood";
if (0 == getProperty(interface, path, service, property, value))
{
// return value is a variant, int32 == 1 for pgood OK
pgood = std::get<int32_t>(value);
}
}
return pgood != 1 ? true : false; // if not pgood then power fault
}
} // namespace dbus
} // namespace util