Files
2026-04-23 17:07:55 +08:00

2058 lines
66 KiB
C++
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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);
}