Files

2058 lines
66 KiB
C++
Raw Permalink Normal View History

2026-04-23 17:07:55 +08:00
#include "ipmi-oem.hpp"
#include <algorithm>
#include <cstdio>
#include <fcntl.h>
#include <linux/i2c.h>
#include <phosphor-logging/lg2.hpp>
#include <sys/ioctl.h>
#include <unistd.h>
#include <vector>
#include <fstream>
#include <unordered_map>
#include <boost/container/flat_map.hpp>
#include <filesystem>
#include <iostream>
#include <ctime>
#include <iomanip>
#include <nlohmann/json.hpp>
#include <iostream>
#include <string>
#include <cstdint>
#include <dirent.h>
#include <cstring>
#include <cerrno>
#include <xyz/openbmc_project/Software/Activation/server.hpp>
#include <xyz/openbmc_project/Software/Version/server.hpp>
#include <xyz/openbmc_project/State/BMC/server.hpp>
extern "C"
{
#include <i2c/smbus.h>
#include <linux/i2c-dev.h>
}
#define PSU0_ADDR 0x58
#define PSU1_ADDR 0x59
#define PSU_READ_ADC_CMD 0x8B
#define PSU_WRITE_ADC_CMD 0x21
#define PSU_ADC_VAL_MIN 0x1730
#define PSU_ADC_VAL_MAX 0x199E
constexpr uint8_t COVER_CPLD_I2C_BUS = 7;
constexpr uint8_t COVER_TMP468_I2C_SLAVE_ADDR = 0x90;
constexpr uint8_t COVER_CPLD_I2C_SLAVE_ADDR = 0xf8;
constexpr uint8_t COVER_OPEN_STATUS_REG = 0x0037;
constexpr uint8_t COVER_OPEN_CONTROL_REG = 0x008e;
constexpr uint8_t COVER_COMMAND_OPENED_STATUS = 0x01;
constexpr uint8_t COVER_COMMAND_CLOSE_STATUS = 0x02;
constexpr uint8_t COVER_COMMAND_UNCERTAIN_STATUS = 0x03;
constexpr uint8_t COVER_REG_OPENED = 0x04;
constexpr uint8_t COVER_REG_CLOSED = 0x05;
constexpr uint8_t COVER_REG_UNCERTAIN_STATUS = 0x07;
constexpr uint8_t MAX_CPU_ID_NUM = 2;
constexpr uint8_t MAX_DIMM_RANK_NUM = 12;
constexpr uint8_t GET_DIMM_TEMP_BUFF_SIZE = MAX_DIMM_RANK_NUM * 2;
constexpr uint8_t maxRetries = 5;
//define for thermal oem command
#define DIMM_SETPOINT_PATH "/tmp/dimm_setpoint"
#define COVER_SETPOINT_PATH "/tmp/cover_setpoint"
#define COVER_STATUS_PATH "/tmp/cover_status"
#define FSC_CONTROL_MODE "/tmp/fsc_control_mode"
#define FSC_ENABLE_STATUS_PATH "/tmp/fsc_enable"
#define MAIN_BMC_BOOT_SOURCE "/sys/devices/platform/ahb/1e620000.spi/access_primary"
#define BACKUP_BMC_BOOT_SOURCE "/sys/devices/platform/ahb/1e620000.spi/access_backup"
using namespace phosphor::logging;
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 static const char* softwareServiceName =
"xyz.openbmc_project.Software.BMC.Updater";
constexpr static const char* biosActiveObjectName =
"/xyz/openbmc_project/software/bios_active";
constexpr static const char* softwareVersionInterface =
"xyz.openbmc_project.Software.Version";
constexpr static const char* versionProperty = "Version";
#define BIOSVERSIONLEN 32
constexpr auto DIMMTempSensorBusname = "xyz.openbmc_project.DIMMTempSensor";
constexpr auto dimmLEDInterface = "xyz.openbmc_project.Sensor.dimmLED";
constexpr auto dbusProperties = "org.freedesktop.DBus.Properties";
constexpr auto dimmSpdReaderService = "xyz.openbmc_project.DimmSpdReader";
constexpr auto tempInterface = "xyz.openbmc_project.Dimm.Temperature";
constexpr auto voltInterface = "xyz.openbmc_project.Dimm.Voltage";
constexpr auto curInterface = "xyz.openbmc_project.Dimm.Current";
constexpr auto powInterface = "xyz.openbmc_project.Dimm.Power";
constexpr auto dimmInfoPath = "/var/log/dimmInfo.json";
constexpr auto HostMiscDbusName = "xyz.openbmc_project.Host.Misc.Manager";
constexpr auto platformStatePath = "/xyz/openbmc_project/misc/platform_state";
constexpr auto platformStateInterface = "xyz.openbmc_project.State.Host.Misc";
static const std::unordered_map<uint8_t, std::string> dimm_map = {
{ 0x90, "DIMM_CPU0_A_Temp" },
{ 0x92, "DIMM_CPU0_B_Temp" },
{ 0x94, "DIMM_CPU0_C_Temp" },
{ 0x96, "DIMM_CPU0_D_Temp" },
{ 0x98, "DIMM_CPU0_E_Temp" },
{ 0x9A, "DIMM_CPU0_F_Temp" },
{ 0x9C, "DIMM_CPU0_G_Temp" },
{ 0x9E, "DIMM_CPU0_H_Temp" },
{ 0xA0, "DIMM_CPU0_I_Temp" },
{ 0xA2, "DIMM_CPU0_J_Temp" },
{ 0xA4, "DIMM_CPU0_K_Temp" },
{ 0xA6, "DIMM_CPU0_L_Temp" },
{ 0xB0, "DIMM_CPU1_A_Temp" },
{ 0xB2, "DIMM_CPU1_B_Temp" },
{ 0xB4, "DIMM_CPU1_C_Temp" },
{ 0xB6, "DIMM_CPU1_D_Temp" },
{ 0xB8, "DIMM_CPU1_E_Temp" },
{ 0xBA, "DIMM_CPU1_F_Temp" },
{ 0xBC, "DIMM_CPU1_G_Temp" },
{ 0xBE, "DIMM_CPU1_H_Temp" },
{ 0xC0, "DIMM_CPU1_I_Temp" },
{ 0xC2, "DIMM_CPU1_J_Temp" },
{ 0xC4, "DIMM_CPU1_K_Temp" },
{ 0xC6, "DIMM_CPU1_L_Temp" }
};
static const std::unordered_map<uint8_t, std::string> dimm_state_map = {
{ 0x0, "ON" },
{ 0x1, "FAULT" },
{ 0x2, "NA" }
};
enum class DeviceType : uint8_t {
MLB_BMC = 0,
MLB_BIOS,
MLB_CPLD,
PSU,
FPGA,
ALL_BP_CPLD
};
enum class PSUInfoType : uint8_t {
MFG_ID = 1,
MFG_MODEL,
MFG_REVISION,
MFG_DATE,
MFG_LOCATION,
MFG_SERIAL,
PART_NUM,
SERIAL_NUM,
FW_REVISION,
MAX_POUT,
INPUT_LOST,
STATUS,
POWER_UNIT_GROUP,
LOCATION,
HOT_REPLACEABLE,
POWER_SUPPLY_TYPE,
HEALTH,
INPUT_VOLTAGE_RANGE_SWITCH
};
constexpr uint8_t administrator = 0x01;
constexpr uint8_t user = 0x02;
std::vector<uint8_t> adminPasswd = {0xff,administrator,0}; //Encrypted Password Length = 0
std::vector<uint8_t> userPasswd = {0xff,user,0};
void register_ipmi_oem_functions() __attribute__((constructor));
ipmi::RspType<std::vector<uint8_t>> GetDeviceFWVersion(ipmi::Context::ptr ctx,
uint8_t deviceType) {
uint8_t deviceCount = 1;
std::string firstVersion{}, secondVersion{}, thirdVersion{};
ipmi::ObjectTree objectTree;
std::vector<uint8_t> response{};
auto purposeNeed = Version::VersionPurpose::BMC;
switch (static_cast<DeviceType>(deviceType)) {
case DeviceType::MLB_BMC:
break;
case DeviceType::MLB_BIOS:
purposeNeed = Version::VersionPurpose::Host;
break;
case DeviceType::MLB_CPLD:
purposeNeed = Version::VersionPurpose::CPLD;
break;
case DeviceType::PSU:
break;
case DeviceType::FPGA:
break;
case DeviceType::ALL_BP_CPLD:
purposeNeed = Version::VersionPurpose::FBCPLD;
break;
default:
return ipmi::responseParmOutOfRange();
}
try {
objectTree =
ipmi::getAllDbusObjects(*ctx->bus, "/xyz/openbmc_project/software",
"xyz.openbmc_project.Software.ExtendedVersion");
} catch (const sdbusplus::exception_t &e) {
lg2::error("Failed to fetch redundancy object from dbus");
}
for (auto &softObject : objectTree) {
auto service = ipmi::getService(
*ctx->bus, "xyz.openbmc_project.Software.ExtendedVersion",
softObject.first);
auto objValueTree = ipmi::getManagedObjects(
*ctx->bus, service, "/xyz/openbmc_project/software");
for (const auto &objIter : objValueTree) {
try {
auto &intfMap = objIter.second;
auto &versionProps = intfMap.at("xyz.openbmc_project.Software.Version");
auto &activationProps =
intfMap.at("xyz.openbmc_project.Software.Activation");
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) ==
purposeNeed) &&
(Activation::convertActivationsFromString(activation) ==
Activation::Activations::Active)) {
firstVersion = std::move(version);
break; // Example to find primary version
}
} catch (const std::exception &e) {
lg2::error(e.what());
}
}
}
firstVersion.resize(16);
secondVersion.resize(16);
thirdVersion.resize(16);
response.push_back(deviceCount);
std::copy(firstVersion.begin(), firstVersion.end(),
std::back_inserter(response));
std::copy(secondVersion.begin(), secondVersion.end(),
std::back_inserter(response));
std::copy(thirdVersion.begin(), thirdVersion.end(),
std::back_inserter(response));
return ipmi::responseSuccess(response);
}
ipmi::RspType<> SetFanSpeedControlMode(std::vector<uint8_t> data) {
std::vector<uint8_t> newArray(30);
size_t i = 0;
if(data.size() < 1)
{
lg2::error("invalid request size");
return ipmi::responseReqDataLenInvalid();
}
if(data[0] > 2)
{
lg2::error("invalid fan control mode");
return ipmi::responseInvalidFieldRequest();
}
else if(data[0] == FSC_FAN_TABLE_MANUAL)
{
/*request is manual mode */
if(data.size() < 2)
{
lg2::error("invalid request size for manual mode");
return ipmi::responseReqDataLenInvalid();
}
if(data[1] != 0)
{
if(data[1] > 100)
{
return ipmi::responseInvalidFieldRequest();
}
/*set all pwm to request manual pwm */
for (i = 1; i < newArray.size(); ++i)
{
newArray[i] = data[1];
}
}
else
{
if(data.size() != 31)
{
lg2::error("invalid request size for manual mode");
return ipmi::responseReqDataLenInvalid();
}
for (i = 1; i < data.size(); ++i)
{
if (data[i] > 100)
{
return ipmi::responseInvalidFieldRequest();
}
}
size_t startIndex = 2;
for (i = 0; i < MAX_FSC_PWM_CHANNEL; ++i)
{
if (startIndex + i < data.size())
{
newArray[i + 1] = data[startIndex + i];
}
else
{
newArray[i + 1] = 0;
}
}
}
}
else
{
/*request is auto mode */
for (i = 0; i < MAX_FSC_PWM_CHANNEL; ++i)
{
newArray[i + 1] = 0;
}
}
newArray[0] = data[0];
std::ofstream outFile(FSC_CONTROL_MODE, std::ios::binary);
if (!outFile) {
lg2::error("Error opening file /tmp/fsc_control_mode");
return ipmi::responseBusy();
}
outFile.write(reinterpret_cast<const char*>(newArray.data()), newArray.size());
if (!outFile) {
lg2::error("Error write file /tmp/fsc_control_mode");
outFile.close();
return ipmi::responseBusy();
}
outFile.close();
return ipmi::responseSuccess();
}
ipmi::RspType<std::vector<uint8_t>> GetFanSpeedControlMode() {
std::vector<uint8_t> response(30);
if(!std::filesystem::exists(FSC_CONTROL_MODE))
{
lg2::error("Error write file /tmp/fsc_control_mode");
response[0] = FSC_FAN_TABLE_AUTO;
response.resize(1);
return ipmi::responseSuccess(response);
}
std::ifstream inputFile(FSC_CONTROL_MODE, std::ios::binary);
if (!inputFile)
{
return ipmi::responseBusy();
}
inputFile.read(reinterpret_cast<char*>(response.data()), 30);
if(inputFile.gcount() < 30)
{
return ipmi::responseUnspecifiedError();
}
inputFile.close();
return ipmi::responseSuccess(response);
}
ipmi::RspType<> SetBIOSPasswordConfig(std::vector<uint8_t> data) {
if(!(data[0] == 0x00 || data[0] == 0x01 || data[0] == 0xff))
{
return ipmi::responseParmOutOfRange();
}
// Clear Passwd
if(data[1] == administrator)
{
adminPasswd[0] = data[0];
}
else if(data[1] == user)
{
userPasswd[0] = data[0];
}
if (data[0] == 0x01)
{
//todo set passwd
}
return ipmi::responseSuccess();
}
ipmi::RspType<std::vector<uint8_t>> GetBIOSPasswordConfig(std::vector<uint8_t> data) {
std::vector<uint8_t> response(2);
if(!(data[0] == 0x01 || data[0] == 0x02))
{
return ipmi::responseParmOutOfRange();
}
if (data[0] == administrator)
{
response.at(0) = adminPasswd[0];
response.at(1) = adminPasswd[1];
}
else if (data[0] == user)
{
response.at(0) = userPasswd[0];
response.at(1) = userPasswd[1];
}
return ipmi::responseSuccess(response);
}
ipmi::RspType<std::vector<uint8_t>> GetPSUInfo(uint8_t psuIndex, uint8_t parameter) {
uint8_t success = 0;
psuIndex = psuIndex-1;
std::string psuIndexStr = std::to_string(psuIndex);
std::vector<uint8_t> response (34,0);
constexpr auto PSU_INTERFACE = "xyz.openbmc_project.Configuration.Status";
std::string PSU_PATH = "/xyz/openbmc_project/inventory/system/board/Lux_Baseboard/PSU"
+ psuIndexStr + "_Supply";
std::string PSU_PROP = "Vendor";
std::string type = "string";
std::string propertyValueTmp;
double dpropertyValueTmp = 0;
std::string psuInfo;
PSUInfoType parmType = static_cast<PSUInfoType>(parameter);
switch (parmType) {
case PSUInfoType::MFG_ID:
PSU_PROP = "MFG_ID";
break;
case PSUInfoType::MFG_MODEL:
PSU_PROP = "MFG_MODEL";
break;
case PSUInfoType::MFG_REVISION:
PSU_PROP = "MFG_REVISION";
break;
case PSUInfoType::MFG_DATE:
PSU_PROP = "MFG_DATE";
break;
case PSUInfoType::MFG_LOCATION:
PSU_PROP = "MFG_LOCATION";
break;
case PSUInfoType::MFG_SERIAL:
PSU_PROP = "MFG_SERIAL";
break;
case PSUInfoType::PART_NUM:
PSU_PROP = "PART_NUM";
break;
case PSUInfoType::SERIAL_NUM:
PSU_PROP = "SERIAL_NUM";
break;
case PSUInfoType::FW_REVISION:
PSU_PROP = "FW_REVISION";
break;
case PSUInfoType::MAX_POUT:
PSU_PROP = "MAX_POUT";
break;
case PSUInfoType::INPUT_LOST:
PSU_PROP = "INPUT_LOST";
break;
case PSUInfoType::STATUS:
PSU_PROP = "Status";
type = "double";
break;
case PSUInfoType::POWER_UNIT_GROUP:
PSU_PROP = "POWER_UNIT_GROUP";
type = "double";
break;
case PSUInfoType::LOCATION:
PSU_PROP = "Name";
break;
case PSUInfoType::HOT_REPLACEABLE:
PSU_PROP = "HOT_REPLACEABLE";
type = "double";
break;
case PSUInfoType::POWER_SUPPLY_TYPE:
PSU_PROP = "POWER_SUPPLY_TYPE";
break;
case PSUInfoType::HEALTH:
type = "null";
break;
case PSUInfoType::INPUT_VOLTAGE_RANGE_SWITCH:
type = "null";
break;
default:
return ipmi::responseParmOutOfRange();
}
//Get dbus property value
sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
auto settingService = ipmi::getService(bus, PSU_INTERFACE, PSU_PATH.c_str());
auto method = bus.new_method_call(settingService.c_str(), PSU_PATH.c_str(),
"org.freedesktop.DBus.Properties", "Get");
method.append(PSU_INTERFACE, PSU_PROP);
if(type == "string"){
std::variant<std::string> propertyValue{};
try
{
auto reply = bus.call(method);
reply.read(propertyValue);
propertyValueTmp = std::get<std::string>(propertyValue);
std::cerr << "PSU propertyValueTmp " << propertyValueTmp << "\n";
}
catch (const std::exception& e)
{
lg2::error("Error get PSU Info ");
return ipmi::responseUnspecifiedError();
}
}
else if(type == "double")
{
std::variant<double> propertyValue{};
try
{
auto reply = bus.call(method);
reply.read(propertyValue);
dpropertyValueTmp = std::get<double>(propertyValue);
std::cerr << "PSU dpropertyValueTmp " << static_cast<double>(dpropertyValueTmp) << "\n";
}
catch (const std::exception& e)
{
lg2::error("Error get PSU Info ");
return ipmi::responseUnspecifiedError();
}
}
//Return PSU Info
if(parmType == PSUInfoType::INPUT_LOST)
{
//INPUT_LOSTBIOS 0 = AC Input OK, 1 = AC Input Lost
psuInfo = (propertyValueTmp == "1") ? "0" : "1";
uint8_t acStatus = std::stoi(psuInfo, 0, 16);
response.at(0) = success;
response.at(1) = sizeof(uint16_t);
response.at(2) = acStatus;
return ipmi::responseSuccess(response);
}
else if(parmType == PSUInfoType::STATUS)
{
uint8_t status = 0;
if(dpropertyValueTmp != 0 && dpropertyValueTmp != 32768 && dpropertyValueTmp != 32770) //psu status 0x8000 = 32768
{
status = 1;
}
response.at(0) = success;
response.at(1) = sizeof(uint16_t);
response.at(2) = status;
return ipmi::responseSuccess(response);
}
else if(parmType == PSUInfoType::MAX_POUT)
{
if(propertyValueTmp == "INVALID")
{
response.at(0) = success;
response.at(1) = 2;
response.at(2) = 0xFF; // 低字节 0xe8
response.at(3) = 0xFF; // 高字节 0x0b
}
else
{
uint16_t maxPout = std::stoi(propertyValueTmp, 0, 10); // 0x0be8
response.at(0) = success;
response.at(1) = sizeof(maxPout);
response.at(2) = static_cast<uint8_t>(maxPout & 0xFF); // 低字节 0xe8
response.at(3) = static_cast<uint8_t>((maxPout >> 8) & 0xFF); // 高字节 0x0b
}
return ipmi::responseSuccess(response);
}
else if(parmType == PSUInfoType::POWER_UNIT_GROUP || parmType == PSUInfoType::HOT_REPLACEABLE)
{
int valueTemp = static_cast<uint8_t>(dpropertyValueTmp);
int8_t value = static_cast<int8_t>(valueTemp);
response.at(0) = success;
response.at(1) = sizeof(uint16_t); //bios request uint16_t
response.at(2) = value;
return ipmi::responseSuccess(response);
}
else if(parmType == PSUInfoType::POWER_SUPPLY_TYPE)
{
int8_t value = 1;
if(propertyValueTmp == "switching")
{
value = 4;
}
response.at(0) = success;
response.at(1) = sizeof(uint16_t); //bios request
response.at(2) = value;
return ipmi::responseSuccess(response);
}
else if(parmType == PSUInfoType::HEALTH)
{
uint8_t health = 3; // 3 = OK
response.at(0) = success;
response.at(1) = sizeof(uint16_t);
response.at(2) = health;
return ipmi::responseSuccess(response);
}
else if(parmType == PSUInfoType::INPUT_VOLTAGE_RANGE_SWITCH)
{
uint8_t status = 4; // 4 = Auto-switch
response.at(0) = success;
response.at(1) = sizeof(uint16_t);
response.at(2) = status;
return ipmi::responseSuccess(response);
}
else
{
psuInfo = std::move(propertyValueTmp);
if(parmType == PSUInfoType::LOCATION)
{
psuInfo = psuInfo.substr(0,4); // PSU0
}
else if(parmType == PSUInfoType::FW_REVISION)
{
psuInfo = psuInfo.substr(0,8); // 00.01.02
}
response.at(0) = success;
response.at(1) = psuInfo.size();
std::copy_n(psuInfo.c_str(), psuInfo.size(),
response.begin() + 2);
return ipmi::responseSuccess(response);
}
}
ipmi::RspType<int32_t> Get12VPSUADCValue(uint8_t psuNum)
{
std::string i2cDev = "/dev/i2c-7";
int fd = -1;
uint8_t addr;
fd = open(i2cDev.c_str(), O_RDWR | O_CLOEXEC);
if (fd < 0)
{
close(fd);
throw std::runtime_error("Unable to open i2c device.");
return ipmi::responseUnspecifiedError();
}
if(psuNum == 0) addr = PSU0_ADDR;
else if(psuNum == 1) addr = PSU1_ADDR;
else {
close(fd);
throw std::runtime_error("Invalid PSU Number, range 0 ~ 1.");
return ipmi::responseParmOutOfRange();
}
if (ioctl(fd, I2C_SLAVE_FORCE, addr) < 0)
{
close(fd);
fd = -1;
throw std::runtime_error("Unable to set i2c slave address.");
return ipmi::responseUnspecifiedError();
}
int32_t val = i2c_smbus_read_word_data(fd, PSU_READ_ADC_CMD);
if (val < 0)
{
close(fd);
fd = -1;
throw std::runtime_error("i2c read failed");
return ipmi::responseUnspecifiedError();
}
return ipmi::responseSuccess(val);
}
ipmi::RspType<> Set12VPSUADCValue(uint8_t psuNum, uint16_t val)
{
std::string i2cDev = "/dev/i2c-7";
int fd = -1;
uint8_t addr;
fd = open(i2cDev.c_str(), O_RDWR | O_CLOEXEC);
if (fd < 0)
{
close(fd);
throw std::runtime_error("Unable to open i2c device.");
return ipmi::responseUnspecifiedError();
}
if(psuNum == 0) addr = PSU0_ADDR;
else if(psuNum == 1) addr = PSU1_ADDR;
else {
close(fd);
throw std::runtime_error("PSU Number is out of range: 0 ~ 1.");
return ipmi::responseParmOutOfRange();
}
if(val < PSU_ADC_VAL_MIN || val > PSU_ADC_VAL_MAX)
{
close(fd);
throw std::runtime_error("ADC Value is out of range: 0x1730 ~ 0x199E.");
return ipmi::responseParmOutOfRange();
}
if (ioctl(fd, I2C_SLAVE_FORCE, addr) < 0)
{
close(fd);
fd = -1;
throw std::runtime_error("Unable to set i2c slave address.");
return ipmi::responseUnspecifiedError();
}
int32_t ret = i2c_smbus_write_word_data(fd, PSU_WRITE_ADC_CMD, val);
if (ret < 0)
{
close(fd);
fd = -1;
throw std::runtime_error("i2c write failed");
return ipmi::responseUnspecifiedError();
}
return ipmi::responseSuccess();
}
double combineToDouble(uint8_t tempInteger, uint8_t tempDecimal) {
int exponent = 0;
int decimal = tempDecimal;
while (decimal > 0) {
decimal /= 10;
exponent++;
}
double doubleValue = tempInteger + static_cast<double>(tempDecimal) / pow(10, exponent);
return doubleValue;
}
ipmi::RspType<uint8_t> GetDIMMTemperatureValue(std::vector<uint8_t> Data) {
uint8_t response = 0;
constexpr auto service = "xyz.openbmc_project.DIMMTempSensor";
constexpr auto dbusProperties = "org.freedesktop.DBus.Properties";
constexpr auto statusInterface = "xyz.openbmc_project.Sensor.Value";
char sensorPath[128];
//BIOS sends 128 byte of data ,Remove the first eight useless bytes
if(Data.size() != 128)
{
std::cout << "BIOS raw data size :" << Data.size() << "\n";
return ipmi::responseReqDataLenInvalid();
}
//struct array
Data.erase(Data.begin(), Data.begin() + 8);
const size_t structCount = Data.size() / sizeof(memory_info_t);
if (Data.size() != structCount * sizeof(memory_info_t)) {
std::cout << "Data size :" << Data.size() << "\n";
return ipmi::responseReqDataLenInvalid();
}
memory_info_t memoryArray[structCount];
std::memcpy(memoryArray, Data.data(), structCount * sizeof(memory_info_t));
//Map path and temperature
std::map<std::string, int> DIMMTempMap;
double DIMMTemp;
int dimmNum = 0;
for (int socketId = 0; socketId < 2; socketId++) {
for (int channelId = 0; channelId < 12; channelId++) {
int num = 65 + channelId;
char channel = static_cast<char>(num);
snprintf(sensorPath, sizeof(sensorPath),
"/xyz/openbmc_project/sensors/temperature/DIMM_CPU%d_%c_Temp", socketId,channel);
DIMMTemp = combineToDouble(memoryArray[dimmNum].temp_integer,
memoryArray[dimmNum].temp_decimal);
dimmNum++;
DIMMTempMap[sensorPath] = DIMMTemp;
printf("Formatted string: %s ;", sensorPath);
printf("Socket:%d,Channel:%d,temperature%f°C\n",socketId,channelId, DIMMTemp);
}
}
//Set DIMM temperature to dbus
for (const auto& iter : DIMMTempMap) {
double DIMMTempValue = static_cast<double>(iter.second);
std::variant<double> value{(double)DIMMTempValue};
auto bus = sdbusplus::bus::new_default();
std::string path = iter.first;
auto method = bus.new_method_call(service, path.c_str(),dbusProperties, "Set");
method.append(statusInterface, "Value", value);
try
{
bus.call_noreply(method);
}
catch (const sdbusplus::exception::exception& e)
{
lg2::info("Error setting DIMM sensor Temp, ERROR = {ERROR}",
"ERROR", e);
}
}
return ipmi::responseSuccess(response);
}
bool i2cReadWriteNative(uint8_t bus, uint8_t slaveAddr,
std::vector<uint8_t> writeData,
std::vector<uint8_t> &readData, uint8_t readCount) {
if (writeData.empty() && readCount == 0) {
return false;
}
std::string busPath = "/dev/i2c-" + std::to_string(bus);
int fd = open(busPath.c_str(), O_RDWR);
if (fd < 0) {
return false;
}
struct i2c_msg messages[2];
struct i2c_rdwr_ioctl_data ioctl_data;
if (!writeData.empty()) {
messages[0].addr = slaveAddr;
messages[0].flags = 0;
messages[0].len = writeData.size();
messages[0].buf = writeData.data();
}
if (readCount > 0) {
messages[1].addr = slaveAddr;
messages[1].flags = I2C_M_RD;
messages[1].len = readCount;
messages[1].buf = new uint8_t[readCount];
}
ioctl_data.msgs = messages;
ioctl_data.nmsgs = (writeData.empty() ? 0 : 1) + (readCount > 0 ? 1 : 0);
if (ioctl(fd, I2C_RDWR, &ioctl_data) < 0) {
if (readCount > 0)
delete[] messages[1].buf;
close(fd);
return false;
}
if (readCount > 0) {
readData.assign(static_cast<uint8_t *>(messages[1].buf),
static_cast<uint8_t *>(messages[1].buf) + readCount);
delete[] messages[1].buf;
}
close(fd);
return true;
}
ipmi::RspType<std::vector<uint8_t>>
ipmiOemI2cReadWrite(uint8_t bus, uint8_t address, uint8_t readCount,
std::vector<uint8_t> writeData) {
std::vector<uint8_t> response(readCount);
if (i2cReadWriteNative(bus, address >> 1, writeData, response, readCount))
return ipmi::responseSuccess(std::move(response));
return ipmi::responseDestinationUnavailable();
}
ipmi::RspType<uint8_t> ipmiOemSetDIMMSetPoint(uint8_t dimmSetPoint)
{
uint8_t RspStatus = 0x00;
if(dimmSetPoint > 120)
{
return ipmi::responseParmOutOfRange();
}
if(dimmSetPoint <= 40)
{
dimmSetPoint = 40;
}
else
{
dimmSetPoint = 74;
}
std::ofstream outFile(DIMM_SETPOINT_PATH, std::ios::out | std::ios::trunc);
if (!outFile)
{
return ipmi::responseBusy();
}
outFile << static_cast<int>(dimmSetPoint);
outFile.close();
return ipmi::responseSuccess(RspStatus);
}
ipmi::RspType<uint8_t> ipmiOemGetDIMMSetPoint()
{
std::ifstream inFile(DIMM_SETPOINT_PATH);
if (!inFile)
{
return ipmi::responseUnspecifiedError();
}
std::string value;
inFile >> value;
if (value.empty() || !std::all_of(value.begin(), value.end(), ::isdigit))
{
return ipmi::responseUnspecifiedError();
}
uint8_t dimmSetPoint = static_cast<uint8_t>(std::stoi(value));
inFile.close();
return ipmi::responseSuccess(dimmSetPoint);
}
ipmi::RspType<uint8_t> ipmiOemSetCoverStatus(uint8_t coverStatus)
{
uint8_t readCount = 1;
std::vector<uint8_t> response(readCount);
std::vector<uint8_t> writeData(3);
uint8_t RspStatus = 0x00;
if((coverStatus != COVER_COMMAND_OPENED_STATUS) && (coverStatus != COVER_COMMAND_CLOSE_STATUS))
{
return ipmi::responseInvalidFieldRequest();
}
writeData[0] = (COVER_OPEN_CONTROL_REG & 0x00ff);
writeData[1] = (COVER_OPEN_CONTROL_REG & 0xff00) >>8;
if(COVER_COMMAND_OPENED_STATUS == coverStatus)
{
writeData[2] = 0x02;
}
else
{
writeData[2] = 0x01;
}
if(i2cReadWriteNative(COVER_CPLD_I2C_BUS, COVER_CPLD_I2C_SLAVE_ADDR >> 1, writeData, response, readCount))
{
writeData[2] = 0x00;
/*After open or close cover, need write reg to 0x00 */
i2cReadWriteNative(COVER_CPLD_I2C_BUS, COVER_CPLD_I2C_SLAVE_ADDR >> 1, writeData, response, readCount);
return ipmi::responseSuccess(RspStatus);
}
else
return ipmi::responseBusy();
}
ipmi::RspType<uint8_t> ipmiOemGetCoverStatus()
{
uint8_t readCount = 1;
std::vector<uint8_t> response(readCount);
std::vector<uint8_t> writeData(2);
uint8_t coverStatus;
writeData[0] = (COVER_OPEN_STATUS_REG & 0x00ff);
writeData[1] = (COVER_OPEN_STATUS_REG & 0xff00) >>8;
if(i2cReadWriteNative(COVER_CPLD_I2C_BUS, COVER_CPLD_I2C_SLAVE_ADDR >> 1, writeData, response, readCount))
{
coverStatus = response[0] & 0x07;
if(COVER_REG_OPENED == coverStatus)
{
return ipmi::responseSuccess(COVER_COMMAND_OPENED_STATUS);
}
else if(COVER_REG_CLOSED == coverStatus)
{
return ipmi::responseSuccess(COVER_COMMAND_CLOSE_STATUS);
}
else
{
return ipmi::responseSuccess(COVER_COMMAND_UNCERTAIN_STATUS);
}
}
else
return ipmi::responseBusy();
}
ipmi::RspType<uint8_t> ipmiOemSetCoverHeatTemp(uint8_t coverTemp)
{
uint8_t RspStatus = 0x00;
if(coverTemp > 80)
{
return ipmi::responseParmOutOfRange();
}
std::ofstream outFile(COVER_SETPOINT_PATH, std::ios::out | std::ios::trunc);
if (!outFile)
{
return ipmi::responseBusy();
}
outFile << static_cast<int>(coverTemp);
outFile.close();
return ipmi::responseSuccess(RspStatus);
}
ipmi::RspType<uint8_t> ipmiOemGetCoverHeatTemp()
{
std::ifstream inFile(COVER_SETPOINT_PATH);
if (!inFile)
{
return ipmi::responseBusy();
}
std::string value;
inFile >> value;
if (value.empty() || !std::all_of(value.begin(), value.end(), ::isdigit))
{
return ipmi::responseUnspecifiedError();
}
uint8_t coverTemp = static_cast<uint8_t>(std::stoi(value));
inFile.close();
return ipmi::responseSuccess(coverTemp);
}
ipmi::RspType<uint8_t> ipmiOemGetCurrentCoverTemp()
{
std::vector<uint8_t> writeData(1);
std::vector<uint8_t> readData(2);
uint8_t readtemp;
uint8_t maxtemp;
uint8_t readCount = 2;
uint8_t sensorindex = 0;
for(sensorindex = 0; sensorindex < 4; sensorindex++)
{
writeData[0] = sensorindex + 1; //TMP468 sensor channel is from 1 to 5
if(i2cReadWriteNative(COVER_CPLD_I2C_BUS, COVER_TMP468_I2C_SLAVE_ADDR >> 1, writeData, readData, readCount))
{
if((readData[0] & 0x80) == 0)
{
readtemp = (uint8_t)((((uint16_t)((readData[0] << 8) + readData[1])) >> 3) * 0.0625);
if(maxtemp < readtemp)
{
maxtemp = readtemp;
}
}
}
else
return ipmi::responseBusy();
}
return ipmi::responseSuccess(maxtemp);
}
ipmi::RspType<uint8_t> ipmiOemSetCoverFSCEnableStatus(uint8_t fscStatus)
{
uint8_t RspStatus = 0x00;
if(fscStatus > 1)
{
return ipmi::responseParmOutOfRange();
}
std::ofstream outFile(FSC_ENABLE_STATUS_PATH, std::ios::out | std::ios::trunc);
if (!outFile)
{
return ipmi::responseBusy();
}
outFile << static_cast<int>(fscStatus);
outFile.close();
return ipmi::responseSuccess(RspStatus);
}
ipmi::RspType<uint8_t> ipmiOemGetCoverFSCEnableStatus()
{
uint8_t fscStatus;
if(!std::filesystem::exists(FSC_ENABLE_STATUS_PATH))
{
fscStatus = 0;
return ipmi::responseSuccess(fscStatus);
}
std::ifstream inFile(FSC_ENABLE_STATUS_PATH);
if (!inFile)
{
return ipmi::responseBusy();
}
std::string value;
inFile >> value;
if (value.empty() || !std::all_of(value.begin(), value.end(), ::isdigit))
{
return ipmi::responseUnspecifiedError();
}
fscStatus = static_cast<uint8_t>(std::stoi(value));
inFile.close();
return ipmi::responseSuccess(fscStatus);
}
ipmi::RspType<std::vector<uint8_t>>
ipmiSetGetBiosVersion(const ipmi::Context::ptr& ctx, uint8_t operation,
std::vector<uint8_t> dataToWrite)
{
std::vector<uint8_t> biosVerData;
boost::system::error_code ec;
if ((operation != 0) && (operation != 1))
{
return ipmi::responseInvalidFieldRequest();
}
if (operation == 1) // set bios version
{
auto len = dataToWrite.size();
if ((BIOSVERSIONLEN < len) || (0 == len))
{
return ipmi::responseReqDataLenExceeded();
}
std::string writeString;
writeString.assign(dataToWrite.begin(), dataToWrite.end());
ec = ipmi::setDbusProperty(
ctx, softwareServiceName, biosActiveObjectName,
softwareVersionInterface, versionProperty, writeString);
if (ec)
{
lg2::error("Failed in setting bios version property: {EC}", "EC",
ec.message());
return ipmi::responseResponseError();
}
return ipmi::responseSuccess();
}
// get bios version
std::string biosVer;
ec = ipmi::getDbusProperty(ctx, softwareServiceName, biosActiveObjectName,
softwareVersionInterface, versionProperty,
biosVer);
if (ec)
{
lg2::error("Failed in getting bios version property: {EC}", "EC",
ec.message());
return ipmi::responseResponseError();
}
biosVerData.assign(biosVer.begin(), biosVer.end());
return ipmi::responseSuccess(biosVerData);
}
bool factoryReset(const ipmi::Context::ptr& ctx)
{
constexpr auto softwareServiceName =
"xyz.openbmc_project.Software.BMC.Updater";
constexpr auto softwareRoot = "/xyz/openbmc_project/software";
constexpr auto factoryResetInterface =
"xyz.openbmc_project.Common.FactoryReset";
constexpr auto method = "Reset";
boost::system::error_code ec;
ctx->bus->yield_method_call(ctx->yield, ec, softwareServiceName,
softwareRoot, factoryResetInterface, method);
if (ec)
{
lg2::error("Error resetting BMC: {ERROR}", "ERROR", ec.message());
return false;
}
return true;
}
static bool resetBmc()
{
/* systemd busname*/
auto systemdBusname = "org.freedesktop.systemd1";
/* systemd path*/
auto systemdPath = "/org/freedesktop/systemd1";
/* systemd interface*/
auto systemdInterface = "org.freedesktop.systemd1.Manager";
auto bus = getSdBus();
auto method = bus->new_method_call(systemdBusname, systemdPath,
systemdInterface, "StartUnit");
method.append("obmc-reboot-delay.service", "replace");
try
{
bus->call_noreply(method);
}
catch (const std::exception& e)
{
lg2::error("Error reboot failed {ERROR}", "ERROR", e);
return false;
}
return true;
}
ipmi::RspType<> ipmiOemRestoreFactoryDefault(const ipmi::Context::ptr& ctx)
{
if (!factoryReset(ctx))
{
lg2::error("Restore factory default error.");
return ipmi::responseUnspecifiedError();
}
if (!resetBmc())
{
lg2::error("Restore factory default reset bmc fail.");
return ipmi::responseUnspecifiedError();
}
lg2::info("Restore factory default success.");
return ipmi::responseSuccess();
}
static void setDimmLEDState(const std::string& sensorPath,const std::string& ledState)
{
auto bus = sdbusplus::bus::new_default();
auto method = bus.new_method_call(DIMMTempSensorBusname, sensorPath.c_str(),
dbusProperties, "Set");
std::variant<std::string> value{ledState};
method.append(dimmLEDInterface, "LEDState", value);
try
{
bus.call_noreply(method);
}
catch (const sdbusplus::exception::exception& e)
{
lg2::info("Error setting Dimm LED State, ERROR = {ERROR}",
"ERROR", e);
}
return;
}
ipmi::RspType<uint8_t> ipmiBiosSetDimmState(std::vector<uint8_t> data) {
//BIOS sends 128 byte of data ,Remove the first eight useless bytes
if(data.size() != 2)
{
std::cerr << "invalid data size :" << (int)data.size() << "\n";
return ipmi::responseReqDataLenInvalid();
}
if(dimm_map.find(data[0]) == dimm_map.end())
{
std::cerr << "invalid sensor number :" << (int)data[0] << "\n";
return ipmi::responseReqDataLenInvalid();
}
if(data[1] > 2)
{
std::cerr << "invalid dimm sensor " << (int)data[0] << " with state " << (int)data[1] << ".\n";
return ipmi::responseReqDataLenInvalid();
}
std::string ledStatePath = "/xyz/openbmc_project/sensors/temperature/" + dimm_map.at(data[0]);
std::string dimmState = dimm_state_map.at(data[1]);
setDimmLEDState(ledStatePath, dimmState);
return ipmi::responseSuccess();
}
ipmi::RspType<uint8_t>
ipmiOemSetSOLBitrate(const ipmi::Context::ptr& ctx, uint8_t bitrate)
{
uint64_t baudRate = 115200;
boost::system::error_code ec;
if ((bitrate != 0) && (bitrate != 1))
{
return ipmi::responseInvalidFieldRequest();
}
if (bitrate == 0)
{
baudRate = 115200;
}
else if(bitrate == 1)
{
baudRate = 921600;
}
ec = ipmi::setDbusProperty(ctx,
"xyz.openbmc_project.Console.default",
"/xyz/openbmc_project/console/default",
"xyz.openbmc_project.Console.UART", "NonVbitrate", baudRate);
if (ec)
{
lg2::error("Failed in setting oem sol bitrate property: {EC}", "EC",
ec.message());
return ipmi::responseResponseError();
}
return ipmi::responseSuccess();
}
using BasicVariantType =
std::variant<std::vector<std::string>, std::vector<uint64_t>, std::string,
int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
uint16_t, uint8_t, bool>;
using PropertyMapType =
boost::container::flat_map<std::string, BasicVariantType>;
int setDimmParaToDbus(ipmi::Context::ptr ctx, const std::string& dimmName, const std::string& type, const std::string& propertyName, double wrValue)
{
boost::system::error_code ec;
std::string path = "/xyz/openbmc_project/dimm/" + dimmName;
std::string interface = "xyz.openbmc_project.Dimm." + type;
ctx->bus->yield_method_call<void>(
ctx->yield, ec, dimmSpdReaderService, path, dbusProperties, "Set",
interface, propertyName, std::variant<double>(wrValue));
if (ec)
{
std::cerr << "Setting DIMM " << dimmName << " " << type << " " << propertyName << " failed. \n";
return -1;
}
return 0;
}
inline double dimmTempTodouble(int8_t intPart, uint8_t fracPart) {
double temp;
if (intPart & 0x80) {
temp = -1.0 * (std::fabs(intPart) + fracPart / 100.0);
} else {
temp = intPart + fracPart / 100.0;
}
return temp;
}
ipmi::RspType<uint8_t> ipmiBiosSetDimmPara(ipmi::Context::ptr ctx, std::vector<uint8_t> data) {
size_t size = sizeof(oem_dimm_info_t);
if(data.size() != size)
{
std::cerr << "invalid data size :" << (int)data.size() << "\n";
return ipmi::responseReqDataLenInvalid();
}
std::vector<std::string> tempName = {"TS0", "TS1", "SPD_TS"};
std::vector<std::string> railName = {"SWA", "SWB", "SWC", "SWD", "SWE", "SWF"};
oem_dimm_info_t dimmInfo;
std::memcpy(&dimmInfo, data.data(), size);
int ret = -1;
//dimmId format: ((Socket << 6) | (Channel << 2) | Dimm)
int channel = (dimmInfo.dimmId >> 2) & 0x0f;
int cpuid = (dimmInfo.dimmId >> 6) & 0x03;
if(cpuid > 1 || channel > 11)
{
std::cerr << "invalid dimmid :" << (int)dimmInfo.dimmId << "\n";
return ipmi::responseParmOutOfRange();
}
std::string dimmName = "DIMM_CPU" + std::string(1, '0' + cpuid) + "_" + std::string(1, 'A' + channel);
//Set temperature
double temp[3];
temp[0] = dimmTempTodouble(dimmInfo.dimmTemp.TS0_int, dimmInfo.dimmTemp.TS0_frac);
temp[1] = dimmTempTodouble(dimmInfo.dimmTemp.TS1_int, dimmInfo.dimmTemp.TS1_frac);
temp[2] = dimmTempTodouble(dimmInfo.dimmTemp.SPD_int, dimmInfo.dimmTemp.SPD_frac);
for(int i=0; i<3; i++) {
ret = setDimmParaToDbus(ctx, dimmName, "Temperature", tempName[i], temp[i]);
if(ret < 0)
return ipmi::responseUnspecifiedError();
}
//Set voltage
double railData[6];
railData[0] = static_cast<double>(dimmInfo.dimmVolt.SWA) / 1000.0;
railData[1] = static_cast<double>(dimmInfo.dimmVolt.SWB) / 1000.0;
railData[2] = static_cast<double>(dimmInfo.dimmVolt.SWC) / 1000.0;
railData[3] = static_cast<double>(dimmInfo.dimmVolt.SWD) / 1000.0;
railData[4] = static_cast<double>(dimmInfo.dimmVolt.SWE) / 1000.0;
railData[5] = static_cast<double>(dimmInfo.dimmVolt.SWF) / 1000.0;
for(int i=0; i<6; i++) {
ret = setDimmParaToDbus(ctx, dimmName, "Voltage", railName[i], railData[i]);
if(ret < 0)
return ipmi::responseUnspecifiedError();
}
//Set Current
railData[0] = static_cast<double>(dimmInfo.dimmCur.SWA) / 1000.0;
railData[1] = static_cast<double>(dimmInfo.dimmCur.SWB) / 1000.0;
railData[2] = static_cast<double>(dimmInfo.dimmCur.SWC) / 1000.0;
railData[3] = static_cast<double>(dimmInfo.dimmCur.SWD) / 1000.0;
railData[4] = static_cast<double>(dimmInfo.dimmCur.SWE) / 1000.0;
railData[5] = static_cast<double>(dimmInfo.dimmCur.SWF) / 1000.0;
for(int i=0; i<4; i++) {
ret = setDimmParaToDbus(ctx, dimmName, "Current", railName[i], railData[i]);
if(ret < 0)
return ipmi::responseUnspecifiedError();
}
//Set power
railData[0] = static_cast<double>(dimmInfo.dimmPow.SWA) / 1000.0;
railData[1] = static_cast<double>(dimmInfo.dimmPow.SWB) / 1000.0;
railData[2] = static_cast<double>(dimmInfo.dimmPow.SWC) / 1000.0;
railData[3] = static_cast<double>(dimmInfo.dimmPow.SWD) / 1000.0;
railData[4] = static_cast<double>(dimmInfo.dimmPow.SWE) / 1000.0;
railData[5] = static_cast<double>(dimmInfo.dimmPow.SWF) / 1000.0;
for(int i=0; i<4; i++) {
ret = setDimmParaToDbus(ctx, dimmName, "Power", railName[i], railData[i]);
if(ret < 0)
return ipmi::responseUnspecifiedError();
}
return ipmi::responseSuccess();
}
inline int8_t getTempIntPart (double temp) {
int16_t int_part = static_cast<int16_t>(temp);
if(int_part > INT8_MAX) int_part = INT8_MAX;
if(int_part < INT8_MIN) int_part = INT8_MIN;
return static_cast<uint8_t>(int_part);
}
inline uint8_t getTempFracPart (double temp) {
uint8_t frac_part = 0;
int16_t int_part = static_cast<int16_t>(temp);
if(int_part & 0x80) {
frac_part = static_cast<uint8_t>((int_part - temp) * 100);
} else {
frac_part = static_cast<uint8_t>((temp - int_part) * 100);
}
return frac_part;
}
inline uint16_t doubleToU16 (double data) {
uint32_t temp = static_cast<uint32_t>(std::floor(data * 1000.0));
if(temp > UINT16_MAX) temp = UINT16_MAX;
return static_cast<uint16_t>(temp);
}
ipmi::RspType<std::vector<uint8_t>> ipmiBiosGetDimmPara(ipmi::Context::ptr ctx, uint8_t dimmId) {
oem_dimm_info_t dimmInfo;
memset((void *)&dimmInfo, 0xFF, sizeof(oem_dimm_info_t));
boost::system::error_code ec;
size_t size = sizeof(oem_dimm_info_t);
std::vector<uint8_t> byte_vector(size);
int channel = (dimmId >> 2) & 0x0f;
int cpuid = (dimmId >> 6) & 0x03;
if(cpuid > 1 || channel > 11)
{
std::cerr << "invalid dimmId :" << (int)dimmId << "\n";
return ipmi::responseParmOutOfRange();
}
dimmInfo.dimmId = dimmId;
std::string dimmName = "DIMM_CPU" + std::string(1, '0' + cpuid) + "_" + std::string(1, 'A' + channel);
std::string path = "/xyz/openbmc_project/dimm/" + dimmName;
//Get temperature
PropertyMapType propTemp =
ctx->bus->yield_method_call<PropertyMapType>(
ctx->yield, ec, dimmSpdReaderService, path,
dbusProperties, "GetAll", tempInterface);
if (ec)
{
std::cerr << "Getting DIMM " << dimmName << " Temperature failed." << "\n";
return ipmi::responseUnspecifiedError();
}
auto ts0_temp = std::get_if<double>(&propTemp["TS0"]);
auto ts1_temp = std::get_if<double>(&propTemp["TS1"]);
auto spd_temp = std::get_if<double>(&propTemp["SPD_TS"]);
if(!std::isnan(*ts0_temp)) {
dimmInfo.dimmTemp.TS0_int = getTempIntPart(*ts0_temp);
dimmInfo.dimmTemp.TS0_frac = getTempFracPart(*ts0_temp);
}
if(!std::isnan(*ts1_temp)) {
dimmInfo.dimmTemp.TS1_int = getTempIntPart(*ts1_temp);
dimmInfo.dimmTemp.TS1_frac = getTempFracPart(*ts1_temp);
}
if(!std::isnan(*spd_temp)) {
dimmInfo.dimmTemp.SPD_int = getTempIntPart(*spd_temp);
dimmInfo.dimmTemp.SPD_frac = getTempFracPart(*spd_temp);
}
//Get voltage
ec.clear();
PropertyMapType propVolt =
ctx->bus->yield_method_call<PropertyMapType>(
ctx->yield, ec, dimmSpdReaderService, path,
dbusProperties, "GetAll", voltInterface);
if (ec)
{
std::cerr << "Getting DIMM " << dimmName << " Voltage failed." << "\n";
return ipmi::responseUnspecifiedError();
}
auto swa = std::get_if<double>(&propVolt["SWA"]);
auto swb = std::get_if<double>(&propVolt["SWB"]);
auto swc = std::get_if<double>(&propVolt["SWC"]);
auto swd = std::get_if<double>(&propVolt["SWD"]);
auto swe = std::get_if<double>(&propVolt["SWE"]);
auto swf = std::get_if<double>(&propVolt["SWF"]);
if(!std::isnan(*swa))
dimmInfo.dimmVolt.SWA = doubleToU16(*swa);
if(!std::isnan(*swb))
dimmInfo.dimmVolt.SWB = doubleToU16(*swb);
if(!std::isnan(*swc))
dimmInfo.dimmVolt.SWC = doubleToU16(*swc);
if(!std::isnan(*swd))
dimmInfo.dimmVolt.SWD = doubleToU16(*swd);
if(!std::isnan(*swe))
dimmInfo.dimmVolt.SWE = doubleToU16(*swe);
if(!std::isnan(*swf))
dimmInfo.dimmVolt.SWF = doubleToU16(*swf);
//Get Current
ec.clear();
PropertyMapType propCur =
ctx->bus->yield_method_call<PropertyMapType>(
ctx->yield, ec, dimmSpdReaderService, path,
dbusProperties, "GetAll", curInterface);
if (ec)
{
std::cerr << "Getting DIMM " << dimmName << " Current failed." << "\n";
return ipmi::responseUnspecifiedError();
}
swa = std::get_if<double>(&propCur["SWA"]);
swb = std::get_if<double>(&propCur["SWB"]);
swc = std::get_if<double>(&propCur["SWC"]);
swd = std::get_if<double>(&propCur["SWD"]);
swe = std::get_if<double>(&propCur["SWE"]);
swf = std::get_if<double>(&propCur["SWF"]);
if(!std::isnan(*swa))
dimmInfo.dimmCur.SWA = doubleToU16(*swa);
if(!std::isnan(*swb))
dimmInfo.dimmCur.SWB = doubleToU16(*swb);
if(!std::isnan(*swc))
dimmInfo.dimmCur.SWC = doubleToU16(*swc);
if(!std::isnan(*swd))
dimmInfo.dimmCur.SWD = doubleToU16(*swd);
if(!std::isnan(*swe))
dimmInfo.dimmCur.SWE = doubleToU16(*swe);
if(!std::isnan(*swf))
dimmInfo.dimmCur.SWF = doubleToU16(*swf);
//Set power
ec.clear();
PropertyMapType propPow =
ctx->bus->yield_method_call<PropertyMapType>(
ctx->yield, ec, dimmSpdReaderService, path,
dbusProperties, "GetAll", powInterface);
if (ec)
{
std::cerr << "Getting DIMM " << dimmName << " Power failed." << "\n";
return ipmi::responseUnspecifiedError();
}
swa = std::get_if<double>(&propPow["SWA"]);
swb = std::get_if<double>(&propPow["SWB"]);
swc = std::get_if<double>(&propPow["SWC"]);
swd = std::get_if<double>(&propPow["SWD"]);
swe = std::get_if<double>(&propPow["SWE"]);
swf = std::get_if<double>(&propPow["SWF"]);
if(!std::isnan(*swa))
dimmInfo.dimmPow.SWA = doubleToU16(*swa);
if(!std::isnan(*swb))
dimmInfo.dimmPow.SWB = doubleToU16(*swb);
if(!std::isnan(*swc))
dimmInfo.dimmPow.SWC = doubleToU16(*swc);
if(!std::isnan(*swd))
dimmInfo.dimmPow.SWD = doubleToU16(*swd);
if(!std::isnan(*swe))
dimmInfo.dimmPow.SWE = doubleToU16(*swe);
if(!std::isnan(*swf))
dimmInfo.dimmPow.SWF = doubleToU16(*swf);
std::memcpy(byte_vector.data(), &dimmInfo, size);
return ipmi::responseSuccess(byte_vector);
}
bool getPlatformState(ipmi::Context::ptr ctx)
{
bool* espiPltRst = nullptr;
bool* i3cSwitch = nullptr;
boost::system::error_code ec;
//Set power
ec.clear();
PropertyMapType propPow =
ctx->bus->yield_method_call<PropertyMapType>(
ctx->yield, ec, HostMiscDbusName, platformStatePath,
dbusProperties, "GetAll", platformStateInterface);
if (ec)
{
std::cerr << "Failed to read platform State" << "\n";
return false;
}
espiPltRst = std::get_if<bool>(&propPow["ESpiPlatformReset"]);
i3cSwitch = std::get_if<bool>(&propPow["dimmI3cSwitch"]);
if(espiPltRst == nullptr || i3cSwitch == nullptr)
return false;
else {
return *espiPltRst && *i3cSwitch;
}
}
static std::vector<uint8_t> convertDoubleToTwoBytes(std::optional<double> value)
{
if(!value.has_value()) {
return std::vector<uint8_t>{0xFF, 0xFF};
}
double val = value.value();
if (val < 0.0 || val > 255.994) {
return std::vector<uint8_t>{0xFF, 0xFF};
}
long long scaled = std::llround(val * 100.0);
if (scaled < 0 || scaled > 25599) {
return std::vector<uint8_t>{0xFF, 0xFF};
}
uint8_t integerPart = static_cast<uint8_t>(scaled / 100);
uint8_t fractionalRounded = static_cast<uint8_t>(scaled % 100);
return std::vector<uint8_t>{integerPart, fractionalRounded};
}
template<typename T>
static std::optional<T> getDIMMData(const nlohmann::json& js, const std::string& dimmName,
DataType dataType, auto indexId)
{
static_assert(std::is_arithmetic_v<T>, "The return type must be an arithmetic type");
if (!js.contains(dimmName)) {
return std::nullopt;
}
const nlohmann::json& dimmData = js[dimmName];
try {
if (dataType == DataType::TEMPERATURE) {
auto sensor = static_cast<TempSensor>(indexId);
switch (sensor) {
case TempSensor::TS0: {
auto value = dimmData["Temperature_DegreesC"]["TS0"];
if (std::isnan(value.get<double>())) return std::nullopt;
return static_cast<T>(value.get<double>());
}
case TempSensor::TS1: {
auto value = dimmData["Temperature_DegreesC"]["TS1"];
if (std::isnan(value.get<double>())) return std::nullopt;
return static_cast<T>(value.get<double>());
}
case TempSensor::SPD_TS: {
auto value = dimmData["Temperature_DegreesC"]["SPD_TS"];
if (std::isnan(value.get<double>())) return std::nullopt;
return static_cast<T>(value.get<double>());
}
case TempSensor::MAX_TS01: {
auto ts0 = dimmData["Temperature_DegreesC"]["TS0"];
auto ts1 = dimmData["Temperature_DegreesC"]["TS1"];
bool ts0_valid = !std::isnan(ts0.get<double>());
bool ts1_valid = !std::isnan(ts1.get<double>());
if (ts0_valid && ts1_valid) {
return static_cast<T>(std::max(
ts0.get<double>(),
ts1.get<double>()
));
} else if (ts0_valid) {
return static_cast<T>(ts0.get<double>());
} else if (ts1_valid) {
return static_cast<T>(ts1.get<double>());
} else {
return std::nullopt;
}
}
default:
return std::nullopt;
}
}
else if (dataType == DataType::CURRENT) {
auto channel = static_cast<RailChannel>(indexId);
std::string channelStr;
switch (channel) {
case RailChannel::SWA: channelStr = "SWA"; break;
case RailChannel::SWB: channelStr = "SWB"; break;
case RailChannel::SWC: channelStr = "SWC"; break;
case RailChannel::SWD: channelStr = "SWD"; break;
case RailChannel::SWE: channelStr = "SWE"; break;
case RailChannel::SWF: channelStr = "SWF"; break;
default: return std::nullopt;
}
auto value = dimmData["Current_Ampere"][channelStr];
if (std::isnan(value.get<double>())) return std::nullopt;
return static_cast<T>(value.get<double>());
}
else if (dataType == DataType::POWER) {
auto channel = static_cast<RailChannel>(indexId);
std::string channelStr;
switch (channel) {
case RailChannel::SWA: channelStr = "SWA"; break;
case RailChannel::SWB: channelStr = "SWB"; break;
case RailChannel::SWC: channelStr = "SWC"; break;
case RailChannel::SWD: channelStr = "SWD"; break;
case RailChannel::SWE: channelStr = "SWE"; break;
case RailChannel::SWF: channelStr = "SWF"; break;
default: return std::nullopt;
}
auto value = dimmData["Power_Watt"][channelStr];
if (std::isnan(value.get<double>())) return std::nullopt;
return static_cast<T>(value.get<double>());
}
else if (dataType == DataType::VOLTAGE) {
auto channel = static_cast<RailChannel>(indexId);
std::string channelStr;
switch (channel) {
case RailChannel::SWA: channelStr = "SWA"; break;
case RailChannel::SWB: channelStr = "SWB"; break;
case RailChannel::SWC: channelStr = "SWC"; break;
case RailChannel::SWD: channelStr = "SWD"; break;
case RailChannel::SWE: channelStr = "SWE"; break;
case RailChannel::SWF: channelStr = "SWF"; break;
default: return std::nullopt;
}
auto value = dimmData["Voltage_Volt"][channelStr];
if (std::isnan(value.get<double>())) return std::nullopt;
return static_cast<T>(value.get<double>());
}
else if (dataType == DataType::STATUS) {
auto reg = static_cast<StatusReg>(indexId);
std::string regStr;
char hexStr[5];
snprintf(hexStr, sizeof(hexStr), "0x%02x", static_cast<int>(reg));
regStr = hexStr;
if (!dimmData["Status_Reg"].contains(regStr)) {
return std::nullopt;
}
auto value = dimmData["Status_Reg"][regStr];
return static_cast<T>(value.get<int>());
}
} catch (std::exception& e) {
return std::nullopt;
}
return std::nullopt;
}
/****************************************************************************
reqId: 0:TS0 1:TS1 2:SPD_TEMP 3: Max Temp 4:Voltage 5:Current 6: Status Reg
*****************************************************************************/
ipmi::RspType<std::vector<uint8_t>> ipmiGetDimmTemp(uint8_t reqId)
{
bool retry;
uint8_t attempts = 0;
const nlohmann::json empty{};
nlohmann::json js;
if(reqId > 6) {
std::cout << "invalid reqId :" << (int)reqId << std::endl;
return ipmi::responseParmOutOfRange();
}
std::vector<uint8_t> byte_vector;
do
{
retry = false;
const std::filesystem::path path(dimmInfoPath);
std::ifstream jsonInputFile(path);
if (jsonInputFile.is_open()) {
try
{
js = nlohmann::json::parse(jsonInputFile);
}
catch (const std::exception& e)
{
++attempts;
retry = true;
jsonInputFile.close();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
else {
++attempts;
retry = true;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
} while ((attempts < maxRetries) && retry);
if(attempts == maxRetries) {
std::cout << "Failed to parse json file " << std::endl;
return ipmi::responseUnspecifiedError();
}
for(int cpuid = 0; cpuid < MAX_CPU_ID_NUM; cpuid++)
{
for(int i = 0; i < MAX_DIMM_RANK_NUM; i++)
{
dimm_temp_sensor_t tempSensor;
memset(&tempSensor, 0xFF, sizeof(dimm_temp_sensor_t));
std::string dimmName = "DIMM_CPU" + std::string(1, '0' + cpuid)
+ "_" + std::string(1, 'A' + i);
std::optional<double> temp, swa, swb, swc, swd, swe, swf;
std::optional<uint8_t> r4, r5, r6, r7, r8, r9, ra, rb;
switch(static_cast<ReqId>(reqId))
{
case ReqId::TS0:
temp = getDIMMData<double>(js, dimmName, DataType::TEMPERATURE, TempSensor::TS0);
break;
case ReqId::TS1:
temp = getDIMMData<double>(js, dimmName, DataType::TEMPERATURE, TempSensor::TS1);
break;
case ReqId::SPD_TEMP:
temp = getDIMMData<double>(js, dimmName, DataType::TEMPERATURE, TempSensor::SPD_TS);
break;
case ReqId::MAX_TEMP:
temp = getDIMMData<double>(js, dimmName, DataType::TEMPERATURE, TempSensor::MAX_TS01);
break;
case ReqId::VOLTAGE:
swa = getDIMMData<double>(js, dimmName, DataType::VOLTAGE, RailChannel::SWA);
swb = getDIMMData<double>(js, dimmName, DataType::VOLTAGE, RailChannel::SWB);
swc = getDIMMData<double>(js, dimmName, DataType::VOLTAGE, RailChannel::SWC);
swd = getDIMMData<double>(js, dimmName, DataType::VOLTAGE, RailChannel::SWD);
swe = getDIMMData<double>(js, dimmName, DataType::VOLTAGE, RailChannel::SWE);
swf = getDIMMData<double>(js, dimmName, DataType::VOLTAGE, RailChannel::SWF);
break;
case ReqId::CURRENT:
swa = getDIMMData<double>(js, dimmName, DataType::CURRENT, RailChannel::SWA);
swb = getDIMMData<double>(js, dimmName, DataType::CURRENT, RailChannel::SWB);
swc = getDIMMData<double>(js, dimmName, DataType::CURRENT, RailChannel::SWC);
swd = getDIMMData<double>(js, dimmName, DataType::CURRENT, RailChannel::SWD);
swe = getDIMMData<double>(js, dimmName, DataType::CURRENT, RailChannel::SWE);
swf = getDIMMData<double>(js, dimmName, DataType::CURRENT, RailChannel::SWF);
break;
case ReqId::STATUS_REG:
r4 = getDIMMData<uint8_t>(js, dimmName, DataType::STATUS, StatusReg::REG_04);
r5 = getDIMMData<uint8_t>(js, dimmName, DataType::STATUS, StatusReg::REG_05);
r6 = getDIMMData<uint8_t>(js, dimmName, DataType::STATUS, StatusReg::REG_06);
r7 = getDIMMData<uint8_t>(js, dimmName, DataType::STATUS, StatusReg::REG_07);
r8 = getDIMMData<uint8_t>(js, dimmName, DataType::STATUS, StatusReg::REG_08);
r9 = getDIMMData<uint8_t>(js, dimmName, DataType::STATUS, StatusReg::REG_09);
ra = getDIMMData<uint8_t>(js, dimmName, DataType::STATUS, StatusReg::REG_0A);
rb = getDIMMData<uint8_t>(js, dimmName, DataType::STATUS, StatusReg::REG_0B);
break;
default:
std::cout << "invalid reqId :" << (int)reqId << std::endl;
return ipmi::responseParmOutOfRange();
}
if(reqId <= 3) {
if(temp) {
tempSensor.int_part = getTempIntPart(*temp);
tempSensor.frac_part = getTempFracPart(*temp);
}
byte_vector.push_back(static_cast<uint8_t>(tempSensor.int_part));
byte_vector.push_back(tempSensor.frac_part);
}
else if(reqId == 6) {
for (const auto& opt : {r4, r5, r6, r7, r8, r9, ra, rb}) {
byte_vector.push_back(opt.value_or(0xFF));
}
}
else {
for (const auto& rail : {swa, swb, swc, swd, swe, swf}) {
auto bytes = convertDoubleToTwoBytes(rail);
byte_vector.insert(byte_vector.end(), bytes.begin(), bytes.end());
}
}
}
}
return ipmi::responseSuccess(byte_vector);
}
ipmi::RspType<uint8_t> ipmiSwitchBMCFw(uint8_t bootsource)
{
char cmd[256] = {0};
if(bootsource !=1 && bootsource !=2)
{
return ipmi::responseParmOutOfRange();
}
if(bootsource == 1)//switch bmc to main flash
{
if(std::filesystem::exists(MAIN_BMC_BOOT_SOURCE))
{
memset(cmd, 0 , sizeof(cmd));
snprintf(cmd, sizeof(cmd), "echo 1 > %s", MAIN_BMC_BOOT_SOURCE);
if(system(cmd))
{
lg2::error("Set BMC boot from main flash failed.");
return ipmi::responseUnspecifiedError();
}
}
else
{
return ipmi::responseCommandNotAvailable();
}
}
else if(bootsource == 2)//switch bmc to backup flash
{
if(std::filesystem::exists(BACKUP_BMC_BOOT_SOURCE))
{
memset(cmd, 0 , sizeof(cmd));
snprintf(cmd, sizeof(cmd), "echo 1 > %s", BACKUP_BMC_BOOT_SOURCE);
if(system(cmd))
{
lg2::error("Set BMC boot from backup flash failed.");
return ipmi::responseUnspecifiedError();
}
}
else
{
return ipmi::responseCommandNotAvailable();
}
}
return ipmi::responseSuccess();
}
void register_ipmi_oem_functions() {
// <Get Device Firmware Version>
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdGetDeviceFWVersion,
ipmi::Privilege::Admin, GetDeviceFWVersion);
// <Set Fan speed control mode>
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdSetFanSpeedControlMode,
ipmi::Privilege::Admin, SetFanSpeedControlMode);
// <Get Fan speed control mode>
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdGetFanSpeedControlMode,
ipmi::Privilege::Admin, GetFanSpeedControlMode);
// <Set BIOS Password Config>
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdSetBIOSPasswordConfig,
ipmi::Privilege::Admin, SetBIOSPasswordConfig);
// <Get BIOS Password Config>
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdGetBIOSPasswordConfig,
ipmi::Privilege::Admin, GetBIOSPasswordConfig);
// <Get PSU Info>
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdGetPSUInfo,
ipmi::Privilege::Admin, GetPSUInfo);
// <Get 12V PSU ADC Value>
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdGet12VPSUADCValue,
ipmi::Privilege::Admin, Get12VPSUADCValue);
// <Set 12V PSU ADC Value>
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdSet12VPSUADCValue,
ipmi::Privilege::Admin, Set12VPSUADCValue);
// <Get Memory Info>
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdDIMMTemperatureValue,
ipmi::Privilege::Admin, GetDIMMTemperatureValue);
// <OEM I2C Read Write>
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdOemI2CReadWrite,
ipmi::Privilege::Admin, ipmiOemI2cReadWrite);
// <OEM Set DIMM Setpoint>
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdSetDIMMSetPoint,
ipmi::Privilege::Admin, ipmiOemSetDIMMSetPoint);
// <OEM Get DIMM Setpoint>
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdGetDIMMSetPoint,
ipmi::Privilege::Admin, ipmiOemGetDIMMSetPoint);
// <OEM Set heat cover status>
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdSetCoverStatus,
ipmi::Privilege::Admin, ipmiOemSetCoverStatus);
// <OEM Get heat cover status>
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdGetCoverStatus,
ipmi::Privilege::Admin, ipmiOemGetCoverStatus);
// <OEM Set heat cover temperature>
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdSetCoverPreHeatTemperature,
ipmi::Privilege::Admin, ipmiOemSetCoverHeatTemp);
// <OEM heat cover temperature>
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdGetCoverPreHeatTemperature,
ipmi::Privilege::Admin, ipmiOemGetCoverHeatTemp);
// <OEM Set/Get BIOS Version>
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdGetSetBiosVer,
ipmi::Privilege::Admin, ipmiSetGetBiosVersion);
// <OEM restore factory default>
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdRestoreFactoryDefault,
ipmi::Privilege::Admin, ipmiOemRestoreFactoryDefault);
// <BIOS set Dimm State to BMC>
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdBiosSetDimmState,
ipmi::Privilege::Admin, ipmiBiosSetDimmState);
// <OEM set sol bitrate>
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdSetSOLBitrate,
ipmi::Privilege::Admin, ipmiOemSetSOLBitrate);
// <BIOS set Dimm Parameters to BMC>
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdBiosSetDimmPara,
ipmi::Privilege::Admin, ipmiBiosSetDimmPara);
// <BIOS get Dimm Parameters to BMC>
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdBiosGetDimmPara,
ipmi::Privilege::Admin, ipmiBiosGetDimmPara);
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdGetDimmTemp,
ipmi::Privilege::Admin, ipmiGetDimmTemp);
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdGetCoverCurrentTemp,
ipmi::Privilege::Admin, ipmiOemGetCurrentCoverTemp);
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdSetCoverFSCEnableStatus,
ipmi::Privilege::Admin, ipmiOemSetCoverFSCEnableStatus);
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdGetCoverFSCEnableStatus,
ipmi::Privilege::Admin, ipmiOemGetCoverFSCEnableStatus);
// <OEM Switch BMC Main Backup Flash>
ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
ipmi::ipmiOem::cmdSwitchBMCFw,
ipmi::Privilege::Admin, ipmiSwitchBMCFw);
}