#include "ipmi-oem.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern "C" { #include #include } #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 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 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 adminPasswd = {0xff,administrator,0}; //Encrypted Password Length = 0 std::vector userPasswd = {0xff,user,0}; void register_ipmi_oem_functions() __attribute__((constructor)); ipmi::RspType> GetDeviceFWVersion(ipmi::Context::ptr ctx, uint8_t deviceType) { uint8_t deviceCount = 1; std::string firstVersion{}, secondVersion{}, thirdVersion{}; ipmi::ObjectTree objectTree; std::vector response{}; auto purposeNeed = Version::VersionPurpose::BMC; switch (static_cast(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(versionProps.at("Purpose")); auto activation = std::get(activationProps.at("Activation")); auto version = std::get(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 data) { std::vector 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(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> GetFanSpeedControlMode() { std::vector 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(response.data()), 30); if(inputFile.gcount() < 30) { return ipmi::responseUnspecifiedError(); } inputFile.close(); return ipmi::responseSuccess(response); } ipmi::RspType<> SetBIOSPasswordConfig(std::vector 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> GetBIOSPasswordConfig(std::vector data) { std::vector 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> GetPSUInfo(uint8_t psuIndex, uint8_t parameter) { uint8_t success = 0; psuIndex = psuIndex-1; std::string psuIndexStr = std::to_string(psuIndex); std::vector 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(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 propertyValue{}; try { auto reply = bus.call(method); reply.read(propertyValue); propertyValueTmp = std::get(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 propertyValue{}; try { auto reply = bus.call(method); reply.read(propertyValue); dpropertyValueTmp = std::get(propertyValue); std::cerr << "PSU dpropertyValueTmp " << static_cast(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_LOST:BIOS 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(maxPout & 0xFF); // 低字节 0xe8 response.at(3) = static_cast((maxPout >> 8) & 0xFF); // 高字节 0x0b } return ipmi::responseSuccess(response); } else if(parmType == PSUInfoType::POWER_UNIT_GROUP || parmType == PSUInfoType::HOT_REPLACEABLE) { int valueTemp = static_cast(dpropertyValueTmp); int8_t value = static_cast(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 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(tempDecimal) / pow(10, exponent); return doubleValue; } ipmi::RspType GetDIMMTemperatureValue(std::vector 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 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(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(iter.second); std::variant 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 writeData, std::vector &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(messages[1].buf), static_cast(messages[1].buf) + readCount); delete[] messages[1].buf; } close(fd); return true; } ipmi::RspType> ipmiOemI2cReadWrite(uint8_t bus, uint8_t address, uint8_t readCount, std::vector writeData) { std::vector response(readCount); if (i2cReadWriteNative(bus, address >> 1, writeData, response, readCount)) return ipmi::responseSuccess(std::move(response)); return ipmi::responseDestinationUnavailable(); } ipmi::RspType 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(dimmSetPoint); outFile.close(); return ipmi::responseSuccess(RspStatus); } ipmi::RspType 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(std::stoi(value)); inFile.close(); return ipmi::responseSuccess(dimmSetPoint); } ipmi::RspType ipmiOemSetCoverStatus(uint8_t coverStatus) { uint8_t readCount = 1; std::vector response(readCount); std::vector 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 ipmiOemGetCoverStatus() { uint8_t readCount = 1; std::vector response(readCount); std::vector 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 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(coverTemp); outFile.close(); return ipmi::responseSuccess(RspStatus); } ipmi::RspType 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(std::stoi(value)); inFile.close(); return ipmi::responseSuccess(coverTemp); } ipmi::RspType ipmiOemGetCurrentCoverTemp() { std::vector writeData(1); std::vector 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 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(fscStatus); outFile.close(); return ipmi::responseSuccess(RspStatus); } ipmi::RspType 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(std::stoi(value)); inFile.close(); return ipmi::responseSuccess(fscStatus); } ipmi::RspType> ipmiSetGetBiosVersion(const ipmi::Context::ptr& ctx, uint8_t operation, std::vector dataToWrite) { std::vector 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 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 ipmiBiosSetDimmState(std::vector 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 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, int64_t, uint64_t, double, int32_t, uint32_t, int16_t, uint16_t, uint8_t, bool>; using PropertyMapType = boost::container::flat_map; 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( ctx->yield, ec, dimmSpdReaderService, path, dbusProperties, "Set", interface, propertyName, std::variant(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 ipmiBiosSetDimmPara(ipmi::Context::ptr ctx, std::vector 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 tempName = {"TS0", "TS1", "SPD_TS"}; std::vector 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(dimmInfo.dimmVolt.SWA) / 1000.0; railData[1] = static_cast(dimmInfo.dimmVolt.SWB) / 1000.0; railData[2] = static_cast(dimmInfo.dimmVolt.SWC) / 1000.0; railData[3] = static_cast(dimmInfo.dimmVolt.SWD) / 1000.0; railData[4] = static_cast(dimmInfo.dimmVolt.SWE) / 1000.0; railData[5] = static_cast(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(dimmInfo.dimmCur.SWA) / 1000.0; railData[1] = static_cast(dimmInfo.dimmCur.SWB) / 1000.0; railData[2] = static_cast(dimmInfo.dimmCur.SWC) / 1000.0; railData[3] = static_cast(dimmInfo.dimmCur.SWD) / 1000.0; railData[4] = static_cast(dimmInfo.dimmCur.SWE) / 1000.0; railData[5] = static_cast(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(dimmInfo.dimmPow.SWA) / 1000.0; railData[1] = static_cast(dimmInfo.dimmPow.SWB) / 1000.0; railData[2] = static_cast(dimmInfo.dimmPow.SWC) / 1000.0; railData[3] = static_cast(dimmInfo.dimmPow.SWD) / 1000.0; railData[4] = static_cast(dimmInfo.dimmPow.SWE) / 1000.0; railData[5] = static_cast(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(temp); if(int_part > INT8_MAX) int_part = INT8_MAX; if(int_part < INT8_MIN) int_part = INT8_MIN; return static_cast(int_part); } inline uint8_t getTempFracPart (double temp) { uint8_t frac_part = 0; int16_t int_part = static_cast(temp); if(int_part & 0x80) { frac_part = static_cast((int_part - temp) * 100); } else { frac_part = static_cast((temp - int_part) * 100); } return frac_part; } inline uint16_t doubleToU16 (double data) { uint32_t temp = static_cast(std::floor(data * 1000.0)); if(temp > UINT16_MAX) temp = UINT16_MAX; return static_cast(temp); } ipmi::RspType> 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 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( 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(&propTemp["TS0"]); auto ts1_temp = std::get_if(&propTemp["TS1"]); auto spd_temp = std::get_if(&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( 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(&propVolt["SWA"]); auto swb = std::get_if(&propVolt["SWB"]); auto swc = std::get_if(&propVolt["SWC"]); auto swd = std::get_if(&propVolt["SWD"]); auto swe = std::get_if(&propVolt["SWE"]); auto swf = std::get_if(&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( 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(&propCur["SWA"]); swb = std::get_if(&propCur["SWB"]); swc = std::get_if(&propCur["SWC"]); swd = std::get_if(&propCur["SWD"]); swe = std::get_if(&propCur["SWE"]); swf = std::get_if(&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( 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(&propPow["SWA"]); swb = std::get_if(&propPow["SWB"]); swc = std::get_if(&propPow["SWC"]); swd = std::get_if(&propPow["SWD"]); swe = std::get_if(&propPow["SWE"]); swf = std::get_if(&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( ctx->yield, ec, HostMiscDbusName, platformStatePath, dbusProperties, "GetAll", platformStateInterface); if (ec) { std::cerr << "Failed to read platform State" << "\n"; return false; } espiPltRst = std::get_if(&propPow["ESpiPlatformReset"]); i3cSwitch = std::get_if(&propPow["dimmI3cSwitch"]); if(espiPltRst == nullptr || i3cSwitch == nullptr) return false; else { return *espiPltRst && *i3cSwitch; } } static std::vector convertDoubleToTwoBytes(std::optional value) { if(!value.has_value()) { return std::vector{0xFF, 0xFF}; } double val = value.value(); if (val < 0.0 || val > 255.994) { return std::vector{0xFF, 0xFF}; } long long scaled = std::llround(val * 100.0); if (scaled < 0 || scaled > 25599) { return std::vector{0xFF, 0xFF}; } uint8_t integerPart = static_cast(scaled / 100); uint8_t fractionalRounded = static_cast(scaled % 100); return std::vector{integerPart, fractionalRounded}; } template static std::optional getDIMMData(const nlohmann::json& js, const std::string& dimmName, DataType dataType, auto indexId) { static_assert(std::is_arithmetic_v, "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(indexId); switch (sensor) { case TempSensor::TS0: { auto value = dimmData["Temperature_DegreesC"]["TS0"]; if (std::isnan(value.get())) return std::nullopt; return static_cast(value.get()); } case TempSensor::TS1: { auto value = dimmData["Temperature_DegreesC"]["TS1"]; if (std::isnan(value.get())) return std::nullopt; return static_cast(value.get()); } case TempSensor::SPD_TS: { auto value = dimmData["Temperature_DegreesC"]["SPD_TS"]; if (std::isnan(value.get())) return std::nullopt; return static_cast(value.get()); } case TempSensor::MAX_TS01: { auto ts0 = dimmData["Temperature_DegreesC"]["TS0"]; auto ts1 = dimmData["Temperature_DegreesC"]["TS1"]; bool ts0_valid = !std::isnan(ts0.get()); bool ts1_valid = !std::isnan(ts1.get()); if (ts0_valid && ts1_valid) { return static_cast(std::max( ts0.get(), ts1.get() )); } else if (ts0_valid) { return static_cast(ts0.get()); } else if (ts1_valid) { return static_cast(ts1.get()); } else { return std::nullopt; } } default: return std::nullopt; } } else if (dataType == DataType::CURRENT) { auto channel = static_cast(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())) return std::nullopt; return static_cast(value.get()); } else if (dataType == DataType::POWER) { auto channel = static_cast(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())) return std::nullopt; return static_cast(value.get()); } else if (dataType == DataType::VOLTAGE) { auto channel = static_cast(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())) return std::nullopt; return static_cast(value.get()); } else if (dataType == DataType::STATUS) { auto reg = static_cast(indexId); std::string regStr; char hexStr[5]; snprintf(hexStr, sizeof(hexStr), "0x%02x", static_cast(reg)); regStr = hexStr; if (!dimmData["Status_Reg"].contains(regStr)) { return std::nullopt; } auto value = dimmData["Status_Reg"][regStr]; return static_cast(value.get()); } } 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> 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 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 temp, swa, swb, swc, swd, swe, swf; std::optional r4, r5, r6, r7, r8, r9, ra, rb; switch(static_cast(reqId)) { case ReqId::TS0: temp = getDIMMData(js, dimmName, DataType::TEMPERATURE, TempSensor::TS0); break; case ReqId::TS1: temp = getDIMMData(js, dimmName, DataType::TEMPERATURE, TempSensor::TS1); break; case ReqId::SPD_TEMP: temp = getDIMMData(js, dimmName, DataType::TEMPERATURE, TempSensor::SPD_TS); break; case ReqId::MAX_TEMP: temp = getDIMMData(js, dimmName, DataType::TEMPERATURE, TempSensor::MAX_TS01); break; case ReqId::VOLTAGE: swa = getDIMMData(js, dimmName, DataType::VOLTAGE, RailChannel::SWA); swb = getDIMMData(js, dimmName, DataType::VOLTAGE, RailChannel::SWB); swc = getDIMMData(js, dimmName, DataType::VOLTAGE, RailChannel::SWC); swd = getDIMMData(js, dimmName, DataType::VOLTAGE, RailChannel::SWD); swe = getDIMMData(js, dimmName, DataType::VOLTAGE, RailChannel::SWE); swf = getDIMMData(js, dimmName, DataType::VOLTAGE, RailChannel::SWF); break; case ReqId::CURRENT: swa = getDIMMData(js, dimmName, DataType::CURRENT, RailChannel::SWA); swb = getDIMMData(js, dimmName, DataType::CURRENT, RailChannel::SWB); swc = getDIMMData(js, dimmName, DataType::CURRENT, RailChannel::SWC); swd = getDIMMData(js, dimmName, DataType::CURRENT, RailChannel::SWD); swe = getDIMMData(js, dimmName, DataType::CURRENT, RailChannel::SWE); swf = getDIMMData(js, dimmName, DataType::CURRENT, RailChannel::SWF); break; case ReqId::STATUS_REG: r4 = getDIMMData(js, dimmName, DataType::STATUS, StatusReg::REG_04); r5 = getDIMMData(js, dimmName, DataType::STATUS, StatusReg::REG_05); r6 = getDIMMData(js, dimmName, DataType::STATUS, StatusReg::REG_06); r7 = getDIMMData(js, dimmName, DataType::STATUS, StatusReg::REG_07); r8 = getDIMMData(js, dimmName, DataType::STATUS, StatusReg::REG_08); r9 = getDIMMData(js, dimmName, DataType::STATUS, StatusReg::REG_09); ra = getDIMMData(js, dimmName, DataType::STATUS, StatusReg::REG_0A); rb = getDIMMData(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(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 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() { // ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA, ipmi::ipmiOem::cmdGetDeviceFWVersion, ipmi::Privilege::Admin, GetDeviceFWVersion); // ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA, ipmi::ipmiOem::cmdSetFanSpeedControlMode, ipmi::Privilege::Admin, SetFanSpeedControlMode); // ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA, ipmi::ipmiOem::cmdGetFanSpeedControlMode, ipmi::Privilege::Admin, GetFanSpeedControlMode); // ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA, ipmi::ipmiOem::cmdSetBIOSPasswordConfig, ipmi::Privilege::Admin, SetBIOSPasswordConfig); // ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA, ipmi::ipmiOem::cmdGetBIOSPasswordConfig, ipmi::Privilege::Admin, GetBIOSPasswordConfig); // ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA, ipmi::ipmiOem::cmdGetPSUInfo, ipmi::Privilege::Admin, GetPSUInfo); // ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA, ipmi::ipmiOem::cmdGet12VPSUADCValue, ipmi::Privilege::Admin, Get12VPSUADCValue); // ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA, ipmi::ipmiOem::cmdSet12VPSUADCValue, ipmi::Privilege::Admin, Set12VPSUADCValue); // ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA, ipmi::ipmiOem::cmdDIMMTemperatureValue, ipmi::Privilege::Admin, GetDIMMTemperatureValue); // ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA, ipmi::ipmiOem::cmdOemI2CReadWrite, ipmi::Privilege::Admin, ipmiOemI2cReadWrite); // ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA, ipmi::ipmiOem::cmdSetDIMMSetPoint, ipmi::Privilege::Admin, ipmiOemSetDIMMSetPoint); // ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA, ipmi::ipmiOem::cmdGetDIMMSetPoint, ipmi::Privilege::Admin, ipmiOemGetDIMMSetPoint); // ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA, ipmi::ipmiOem::cmdSetCoverStatus, ipmi::Privilege::Admin, ipmiOemSetCoverStatus); // ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA, ipmi::ipmiOem::cmdGetCoverStatus, ipmi::Privilege::Admin, ipmiOemGetCoverStatus); // ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA, ipmi::ipmiOem::cmdSetCoverPreHeatTemperature, ipmi::Privilege::Admin, ipmiOemSetCoverHeatTemp); // ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA, ipmi::ipmiOem::cmdGetCoverPreHeatTemperature, ipmi::Privilege::Admin, ipmiOemGetCoverHeatTemp); // ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA, ipmi::ipmiOem::cmdGetSetBiosVer, ipmi::Privilege::Admin, ipmiSetGetBiosVersion); // ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA, ipmi::ipmiOem::cmdRestoreFactoryDefault, ipmi::Privilege::Admin, ipmiOemRestoreFactoryDefault); // ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA, ipmi::ipmiOem::cmdBiosSetDimmState, ipmi::Privilege::Admin, ipmiBiosSetDimmState); // ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA, ipmi::ipmiOem::cmdSetSOLBitrate, ipmi::Privilege::Admin, ipmiOemSetSOLBitrate); // ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA, ipmi::ipmiOem::cmdBiosSetDimmPara, ipmi::Privilege::Admin, ipmiBiosSetDimmPara); // 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); // ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA, ipmi::ipmiOem::cmdSwitchBMCFw, ipmi::Privilege::Admin, ipmiSwitchBMCFw); }