Add new oem command to set accel power mode
Signed-off-by: Gaurav Gandhi <gauravgandhi@google.com>
Change-Id: Ie0b47477b7ae9e8ae206ac0645571a7a79dbda40
diff --git a/README.md b/README.md
index cb531ee..95489f7 100644
--- a/README.md
+++ b/README.md
@@ -456,3 +456,56 @@
| Byte(s) | Value | Data |
| ------- | ----- | ---------- |
| 0x00 | 0x11 | Subcommand |
+
+### SysGetAccelVrSettings - SubCommand 0x12
+
+Get the accel's VR setting value for the given chip and settings ID
+
+Currently 3 settings are supported. [0] IdleMode [1] PowerBreak [2] Loadline
+
+On success, the response contains 2 bytes containing the setting value.
+
+If not enough data is proveded, `IPMI_CC_REQ_DATA_LEN_INVALID` is returned.
+
+Request
+
+| Byte(s) | Value | Data |
+| ------- | ----- | ---------- |
+| 0x00 | 0x12 | Subcommand |
+| 0x01 | | Chip ID |
+| 0x02 | | SettingsID |
+
+Response (if applicable)
+
+| Byte(s) | Value | Data |
+| ---------- | ----- | -------------- |
+| 0x00 | 0x12 | Subcommand |
+| 0x01..0x02 | 0x12 | Settings Value |
+
+### SysSetAccelVrSettings - SubCommand 0x13
+
+Update the VR settings of a given accel device for a specific settings id.
+
+Currently 3 settings are supported. [0] IdleMode [1] PowerBreak [2] Loadline
+
+The settings value parameter is a 2 byte value and is expected in little endian
+format
+
+On success, `IPMI_CC_OK` is returned.
+
+If not enough data is proveded, `IPMI_CC_REQ_DATA_LEN_INVALID` is returned.
+
+Request
+
+| Byte(s) | Value | Data |
+| ---------- | ----- | -------------- |
+| 0x00 | 0x13 | Subcommand |
+| 0x01 | | Chip ID |
+| 0x02 | | Settings ID |
+| 0x03..0x04 | 0x13 | Settings Value |
+
+Response (if applicable)
+
+| Byte(s) | Value | Data |
+| ------- | ----- | ---------- |
+| 0x00 | 0x13 | Subcommand |
diff --git a/commands.hpp b/commands.hpp
index 7c8858d..41494e3 100644
--- a/commands.hpp
+++ b/commands.hpp
@@ -57,6 +57,10 @@
SysGetBmcMode = 16,
// The Sys Linux Boot Done command
SysLinuxBootDone = 17,
+ // Google CustomAccel Get VR Settings
+ SysGetAccelVrSettings = 18,
+ // Google CustomAccel Set VR Settings
+ SysSetAccelVrSettings = 19,
};
} // namespace ipmi
diff --git a/google_accel_oob.cpp b/google_accel_oob.cpp
index 7e44864..0c2ffad 100644
--- a/google_accel_oob.cpp
+++ b/google_accel_oob.cpp
@@ -15,6 +15,8 @@
#include "google_accel_oob.hpp"
#include "commands.hpp"
+#include "errors.hpp"
+#include "handler.hpp"
#include <sdbusplus/bus.hpp>
#include <stdplus/print.hpp>
@@ -282,5 +284,57 @@
return ::ipmi::responseSuccess(SysOEMCommands::SysAccelOobWrite, replyBuf);
}
+Resp accelGetVrSettings(::ipmi::Context::ptr ctx, std::span<const uint8_t> data,
+ HandlerInterface* handler)
+{
+ uint16_t value;
+ if (data.size_bytes() != 2)
+ {
+ stdplus::println(
+ stderr,
+ "accelGetVrSettings command has incorrect size: %zuB != %dB\n",
+ data.size_bytes(), 2);
+ return ::ipmi::responseReqDataLenInvalid();
+ }
+
+ try
+ {
+ value = handler->accelGetVrSettings(ctx, /*chip_id*/ data[0],
+ /*settings_id*/ data[1]);
+ }
+ catch (const IpmiException& e)
+ {
+ return ::ipmi::response(e.getIpmiError());
+ }
+ return ::ipmi::responseSuccess(
+ SysOEMCommands::SysGetAccelVrSettings,
+ std::vector<uint8_t>{static_cast<uint8_t>(value),
+ static_cast<uint8_t>(value >> 8)});
+}
+
+Resp accelSetVrSettings(::ipmi::Context::ptr ctx, std::span<const uint8_t> data,
+ HandlerInterface* handler)
+{
+ if (data.size_bytes() != 4)
+ {
+ stdplus::println(
+ stderr,
+ "accelSetVrSettings command has incorrect size: %zuB != %dB\n",
+ data.size_bytes(), 4);
+ return ::ipmi::responseReqDataLenInvalid();
+ }
+ uint16_t value = static_cast<uint16_t>(data[2] | data[3] << 8);
+ try
+ {
+ handler->accelSetVrSettings(ctx, /*chip_id*/ data[0],
+ /*settings_id*/ data[1], /*value*/ value);
+ }
+ catch (const IpmiException& e)
+ {
+ return ::ipmi::response(e.getIpmiError());
+ }
+ return ::ipmi::responseSuccess(SysOEMCommands::SysSetAccelVrSettings,
+ std::vector<uint8_t>{});
+}
} // namespace ipmi
} // namespace google
diff --git a/google_accel_oob.hpp b/google_accel_oob.hpp
index 2624f2b..7b2bbf9 100644
--- a/google_accel_oob.hpp
+++ b/google_accel_oob.hpp
@@ -36,5 +36,10 @@
Resp accelOobWrite(std::span<const uint8_t> data, HandlerInterface* handler);
+// Handle the accel power setting command
+Resp accelSetVrSettings(::ipmi::Context::ptr ctx, std::span<const uint8_t> data,
+ HandlerInterface* handler);
+Resp accelGetVrSettings(::ipmi::Context::ptr ctx, std::span<const uint8_t> data,
+ HandlerInterface* handler);
} // namespace ipmi
} // namespace google
diff --git a/handler.cpp b/handler.cpp
index 5e65e07..d4a8897 100644
--- a/handler.cpp
+++ b/handler.cpp
@@ -68,6 +68,7 @@
using namespace phosphor::logging;
using InternalFailure =
sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+using Value = std::variant<double>;
uint8_t isBmcInBareMetalMode(const std::unique_ptr<FileSystemInterface>& fs)
{
@@ -699,5 +700,72 @@
}
}
+static constexpr char ACCEL_POWER_SERVICE[] = "xyz.openbmc_project.AccelPower";
+static constexpr char ACCEL_POWER_PATH_PREFIX[] =
+ "/xyz/openbmc_project/control/accel_power_";
+static constexpr char POWER_MODE_IFC[] =
+ "xyz.openbmc_project.Control.Power.Mode";
+
+void Handler::accelSetVrSettings(::ipmi::Context::ptr ctx, uint8_t chip_id,
+ uint8_t settings_id, uint16_t value) const
+{
+ int vrSettingsReq;
+ boost::system::error_code ec;
+ if (_vrSettingsMap.find(settings_id) == _vrSettingsMap.end())
+ {
+ log<level::ERR>("Settings ID is not supported",
+ entry("settings_id=%d", settings_id));
+ throw IpmiException(::ipmi::ccParmOutOfRange);
+ }
+
+ vrSettingsReq = static_cast<int>(settings_id | value << 8);
+ std::string object_name(
+ std::format("{}{}", ACCEL_POWER_PATH_PREFIX, chip_id));
+
+ std::variant<int> val = vrSettingsReq;
+ ctx->bus->yield_method_call(ctx->yield, ec, ACCEL_POWER_SERVICE,
+ object_name.c_str(),
+ "org.freedesktop.DBus.Properties", "Set",
+ POWER_MODE_IFC, "PowerMode", val);
+ if (ec)
+ {
+ log<level::ERR>("Failed to set PowerMode property");
+ throw IpmiException(::ipmi::ccUnspecifiedError);
+ }
+}
+
+static constexpr char EXTERNAL_SENSOR_SERVICE[] =
+ "xyz.openbmc_project.ExternalSensor";
+static constexpr char EXTERNAL_SENSOR_PATH_PREFIX[] =
+ "/xyz/openbmc_project/sensors/power/";
+static constexpr char SENSOR_VALUE_IFC[] = "xyz.openbmc_project.Sensor.Value";
+
+uint16_t Handler::accelGetVrSettings(::ipmi::Context::ptr ctx, uint8_t chip_id,
+ uint8_t settings_id) const
+{
+ Value value;
+ boost::system::error_code ec;
+ std::string object_name(std::format("{}{}{}", EXTERNAL_SENSOR_PATH_PREFIX,
+ _vrSettingsMap.at(settings_id),
+ chip_id));
+
+ if (_vrSettingsMap.find(settings_id) == _vrSettingsMap.end())
+ {
+ log<level::ERR>("Settings ID is not supported",
+ entry("settings_id=%d", settings_id));
+ throw IpmiException(::ipmi::ccParmOutOfRange);
+ }
+
+ value = ctx->bus->yield_method_call<std::variant<double>>(
+ ctx->yield, ec, EXTERNAL_SENSOR_SERVICE, object_name.c_str(),
+ "org.freedesktop.DBus.Properties", "Get", SENSOR_VALUE_IFC, "Value");
+ if (ec)
+ {
+ log<level::ERR>("accelGetVrSettings: Failed to call GetObject ");
+ throw IpmiException(::ipmi::ccUnspecifiedError);
+ }
+
+ return static_cast<uint16_t>(std::get<double>(value));
+}
} // namespace ipmi
} // namespace google
diff --git a/handler.hpp b/handler.hpp
index 16b8208..b06d986 100644
--- a/handler.hpp
+++ b/handler.hpp
@@ -15,6 +15,7 @@
#pragma once
#include <ipmid/api-types.hpp>
+#include <ipmid/message.hpp>
#include <cstdint>
#include <map>
@@ -211,6 +212,27 @@
* untrusted OS.
*/
virtual void linuxBootDone() const = 0;
+
+ /**
+ * Update the VR settings for the given settings_id
+ *
+ * @param[in] chip_id - Accel Device#
+ * @param[in] settings_id - ID of the setting to update
+ * @param[in] value - Value of the setting
+ */
+ virtual void accelSetVrSettings(::ipmi::Context::ptr ctx, uint8_t chip_id,
+ uint8_t settings_id,
+ uint16_t value) const = 0;
+
+ /**
+ * Read current VR settings value for the given settings_id
+ *
+ * @param[in] chip_id - Accel Device#
+ * @param[in] settings_id - ID of the setting to read
+ */
+ virtual uint16_t accelGetVrSettings(::ipmi::Context::ptr ctx,
+ uint8_t chip_id,
+ uint8_t settings_id) const = 0;
};
} // namespace ipmi
diff --git a/handler_impl.hpp b/handler_impl.hpp
index 7b58523..fe12728 100644
--- a/handler_impl.hpp
+++ b/handler_impl.hpp
@@ -74,6 +74,10 @@
void accelOobWrite(std::string_view name, uint64_t address,
uint8_t num_bytes, uint64_t data) const override;
void linuxBootDone() const override;
+ void accelSetVrSettings(::ipmi::Context::ptr ctx, uint8_t chip_id,
+ uint8_t settings_id, uint16_t value) const override;
+ uint16_t accelGetVrSettings(::ipmi::Context::ptr ctx, uint8_t chip_id,
+ uint8_t settings_id) const override;
protected:
// Exposed for dependency injection
@@ -102,6 +106,9 @@
{0x1E, "cooling_unit"},
{0x20, "memory_device"}};
+ const std::unordered_map<uint8_t, std::string> _vrSettingsMap{
+ {0, "idle_mode_"}, {1, "power_break_"}, {2, "loadline_"}};
+
nlohmann::json _entityConfig{};
std::vector<std::tuple<uint32_t, std::string>> _pcie_i2c_map;
diff --git a/ipmi.cpp b/ipmi.cpp
index 2c765be..d8d94e9 100644
--- a/ipmi.cpp
+++ b/ipmi.cpp
@@ -46,7 +46,7 @@
namespace ipmi
{
-Resp handleSysCommand(HandlerInterface* handler, ::ipmi::Context::ptr,
+Resp handleSysCommand(HandlerInterface* handler, ::ipmi::Context::ptr ctx,
uint8_t cmd, std::span<const uint8_t> data)
{
switch (cmd)
@@ -87,6 +87,10 @@
return pcieBifurcation(data, handler);
case SysLinuxBootDone:
return linuxBootDone(data, handler);
+ case SysGetAccelVrSettings:
+ return accelGetVrSettings(ctx, data, handler);
+ case SysSetAccelVrSettings:
+ return accelSetVrSettings(ctx, data, handler);
default:
stdplus::print(stderr, "Invalid subcommand: {:#x}\n", cmd);
return ::ipmi::responseInvalidCommand();
diff --git a/test/google_accel_oob_unittest.cpp b/test/google_accel_oob_unittest.cpp
index 163b602..5797cd3 100644
--- a/test/google_accel_oob_unittest.cpp
+++ b/test/google_accel_oob_unittest.cpp
@@ -26,6 +26,7 @@
namespace ipmi
{
+using ::testing::_;
using ::testing::Return;
TEST(GoogleAccelOobTest, DeviceCount_Success)
@@ -235,5 +236,96 @@
EXPECT_EQ(reply->data, kTestData);
}
+TEST(GoogleAccelOobTest, SetVrSettings_Success)
+{
+ ::testing::StrictMock<HandlerMock> h;
+ constexpr uint8_t kChipId = 2;
+ constexpr uint8_t kSettingsId = 1;
+ constexpr uint16_t kTestValue = 0xAABB;
+
+ std::vector<uint8_t> testData = {kChipId, kSettingsId, 0xBB, 0xAA};
+
+ EXPECT_CALL(h, accelSetVrSettings(_, kChipId, kSettingsId, kTestValue))
+ .WillOnce(Return());
+
+ Resp r = accelSetVrSettings(nullptr, testData, &h);
+
+ const auto response = std::get<0>(r);
+ EXPECT_EQ(response, IPMI_CC_OK);
+
+ const auto payload = std::get<1>(r);
+ ASSERT_EQ(payload.has_value(), true);
+ const auto payload_tuple = payload.value();
+ const auto reply_cmd = std::get<0>(payload_tuple);
+ EXPECT_EQ(reply_cmd, SysSetAccelVrSettings);
+ const auto reply_buff = std::get<1>(payload_tuple);
+ ASSERT_EQ(reply_buff.size(), 0);
+}
+
+TEST(GoogleAccelOobTest, SetVrSettings_HandleIncorrectDataSize)
+{
+ ::testing::StrictMock<HandlerMock> h;
+ constexpr uint8_t kChipId = 2;
+ uint8_t kSettingsId = 1;
+
+ std::vector<uint8_t> testData = {kChipId, kSettingsId};
+
+ EXPECT_CALL(h, accelSetVrSettings(_, _, _, _)).Times(0);
+
+ Resp r = accelSetVrSettings(nullptr, testData, &h);
+
+ const auto response = std::get<0>(r);
+ EXPECT_EQ(response, IPMI_CC_REQ_DATA_LEN_INVALID);
+
+ const auto payload = std::get<1>(r);
+ ASSERT_EQ(payload.has_value(), false);
+}
+
+TEST(GoogleAccelOobTest, GetVrSettings_Success)
+{
+ ::testing::StrictMock<HandlerMock> h;
+ constexpr uint8_t kChipId = 3;
+ constexpr uint8_t kSettingsId = 2;
+
+ std::vector<uint8_t> testData = {kChipId, kSettingsId};
+
+ EXPECT_CALL(h, accelGetVrSettings(_, kChipId, kSettingsId))
+ .WillOnce(Return(0xAABB));
+
+ Resp r = accelGetVrSettings(nullptr, testData, &h);
+
+ const auto response = std::get<0>(r);
+ EXPECT_EQ(response, IPMI_CC_OK);
+
+ const auto payload = std::get<1>(r);
+ ASSERT_EQ(payload.has_value(), true);
+ const auto payload_tuple = payload.value();
+ const auto reply_cmd = std::get<0>(payload_tuple);
+ EXPECT_EQ(reply_cmd, SysGetAccelVrSettings);
+ const auto reply_buff = std::get<1>(payload_tuple);
+ ASSERT_EQ(reply_buff.size(), 2);
+
+ EXPECT_EQ(reply_buff.at(0), 0xBB);
+ EXPECT_EQ(reply_buff.at(1), 0xAA);
+}
+
+TEST(GoogleAccelOobTest, GetVrSettings_HandleIncorrectDataSize)
+{
+ ::testing::StrictMock<HandlerMock> h;
+ constexpr uint8_t kChipId = 2;
+ uint8_t kSettingsId = 1;
+
+ std::vector<uint8_t> testData = {kChipId, kSettingsId, 0xCC};
+
+ EXPECT_CALL(h, accelGetVrSettings(_, _, _)).Times(0);
+
+ Resp r = accelGetVrSettings(nullptr, testData, &h);
+
+ const auto response = std::get<0>(r);
+ EXPECT_EQ(response, IPMI_CC_REQ_DATA_LEN_INVALID);
+
+ const auto payload = std::get<1>(r);
+ ASSERT_EQ(payload.has_value(), false);
+}
} // namespace ipmi
} // namespace google
diff --git a/test/handler_mock.hpp b/test/handler_mock.hpp
index f62564e..579dfc6 100644
--- a/test/handler_mock.hpp
+++ b/test/handler_mock.hpp
@@ -16,6 +16,8 @@
#include "handler.hpp"
+#include <ipmid/message.hpp>
+
#include <cstddef>
#include <cstdint>
#include <string>
@@ -64,6 +66,11 @@
MOCK_METHOD(std::vector<uint8_t>, pcieBifurcation, (uint8_t), (override));
MOCK_METHOD(uint8_t, getBmcMode, (), (override));
MOCK_METHOD(void, linuxBootDone, (), (const, override));
+ MOCK_METHOD(void, accelSetVrSettings,
+ (::ipmi::Context::ptr, uint8_t, uint8_t, uint16_t),
+ (const, override));
+ MOCK_METHOD(uint16_t, accelGetVrSettings,
+ (::ipmi::Context::ptr, uint8_t, uint8_t), (const, override));
};
} // namespace ipmi