Refactor SST host processor interface
In order to support future host processors that use a different
interface to SST, separate the SST logic into 1) high-level discovery
logic + D-Bus interfaces, and 2) low-level backend processor interface.
This is a pure refactor with no functional change.
Tested:
Ran sst-compare-redfish-os.py tool on platform with SPR host CPU, and
verified no mismatches reported.
Used sst-info.sh to change configs and verify new config was reflected
in Redfish.
Change-Id: I6825eb7541cbe2214844e7b64d462f2688dedcec
Signed-off-by: Jonathan Doman <jonathan.doman@intel.com>
diff --git a/src/cpuinfo_main.cpp b/src/cpuinfo_main.cpp
index c9dd3e5..1d91f17 100644
--- a/src/cpuinfo_main.cpp
+++ b/src/cpuinfo_main.cpp
@@ -636,9 +636,9 @@
int main(int argc, char* argv[])
{
// setup connection to dbus
- boost::asio::io_service io;
+ boost::asio::io_service& io = cpu_info::dbus::getIOContext();
std::shared_ptr<sdbusplus::asio::connection> conn =
- std::make_shared<sdbusplus::asio::connection>(io);
+ cpu_info::dbus::getConnection();
// CPUInfo Object
conn->request_name(cpu_info::cpuInfoObject);
diff --git a/src/cpuinfo_utils.cpp b/src/cpuinfo_utils.cpp
index e30a275..1c69e6a 100644
--- a/src/cpuinfo_utils.cpp
+++ b/src/cpuinfo_utils.cpp
@@ -288,4 +288,18 @@
initialized = true;
}
+namespace dbus
+{
+boost::asio::io_context& getIOContext()
+{
+ static boost::asio::io_context ioc;
+ return ioc;
+}
+std::shared_ptr<sdbusplus::asio::connection> getConnection()
+{
+ static auto conn =
+ std::make_shared<sdbusplus::asio::connection>(getIOContext());
+ return conn;
+}
+} // namespace dbus
} // namespace cpu_info
diff --git a/src/speed_select.cpp b/src/speed_select.cpp
index c320e39..3a2f8f4 100644
--- a/src/speed_select.cpp
+++ b/src/speed_select.cpp
@@ -25,8 +25,10 @@
#include <xyz/openbmc_project/Control/Processor/CurrentOperatingConfig/server.hpp>
#include <xyz/openbmc_project/Inventory/Item/Cpu/OperatingConfig/server.hpp>
+#include <algorithm>
#include <iostream>
#include <memory>
+#include <stdexcept>
#include <string>
namespace cpu_info
@@ -34,418 +36,57 @@
namespace sst
{
-class PECIError : public std::runtime_error
+// Specialize char to print the integer value instead of ascii. We basically
+// never want to print a single ascii char.
+std::ostream& operator<<(std::ostream& os, uint8_t value)
{
- using std::runtime_error::runtime_error;
-};
-
-constexpr uint64_t bit(int index)
-{
- return (1ull << index);
+ return os << static_cast<int>(value);
}
-constexpr int extendedModel(CPUModel model)
-{
- return (model >> 16) & 0xF;
-}
-
-constexpr bool modelSupportsDiscovery(CPUModel model)
-{
- return extendedModel(model) >= extendedModel(icx);
-}
-
-constexpr bool modelSupportsControl(CPUModel model)
-{
- return extendedModel(model) > extendedModel(icx);
-}
-
-/**
- * Construct a list of indexes of the set bits in the input value.
- * E.g. fn(0x7A) -> {1,3,4,5,6}
- *
- * @param[in] mask Bitmask to convert.
- *
- * @return List of bit indexes.
- */
-static std::vector<uint32_t> convertMaskToList(std::bitset<64> mask)
-{
- std::vector<uint32_t> bitList;
- for (size_t i = 0; i < mask.size(); ++i)
- {
- if (mask.test(i))
- {
- bitList.push_back(i);
- }
- }
- return bitList;
-}
-
-static bool checkPECIStatus(EPECIStatus libStatus, uint8_t completionCode)
+bool checkPECIStatus(EPECIStatus libStatus, uint8_t completionCode)
{
if (libStatus != PECI_CC_SUCCESS || completionCode != PECI_DEV_CC_SUCCESS)
{
std::cerr << "PECI command failed."
<< " Driver Status = " << libStatus << ","
- << " Completion Code = " << static_cast<int>(completionCode)
- << '\n';
+ << " Completion Code = " << completionCode << '\n';
return false;
}
return true;
}
-/**
- * Convenience RAII object for Wake-On-PECI (WOP) management, since PECI Config
- * Local accesses to the OS Mailbox require the package to pop up to PC2. Also
- * provides PCode OS Mailbox routine.
- *
- * Since multiple applications may be modifing WOP, we'll use this algorithm:
- * Whenever a PECI command fails with associated error code, set WOP bit and
- * retry command. Upon manager destruction, clear WOP bit only if we previously
- * set it.
- */
-struct PECIManager
+static std::vector<BackendProvider>& getProviders()
{
- int peciAddress;
- bool peciWoken;
- CPUModel cpuModel;
- int mbBus;
+ static auto* providers = new std::vector<BackendProvider>;
+ return *providers;
+}
- PECIManager(int address, CPUModel model) :
- peciAddress(address), peciWoken(false), cpuModel(model)
- {
- mbBus = (model == icx) ? 14 : 31;
- }
+void registerBackend(BackendProvider providerFn)
+{
+ getProviders().push_back(providerFn);
+}
- ~PECIManager()
+std::unique_ptr<SSTInterface> getInstance(uint8_t address, CPUModel model)
+{
+ DEBUG_PRINT << "Searching for provider for " << address << ", model "
+ << std::hex << model << '\n';
+ for (const auto& provider : getProviders())
{
- // If we're being destroyed due to a PECIError, try to clear the mode
- // bit, but catch and ignore any duplicate error it might raise to
- // prevent termination.
try
{
- if (peciWoken)
+ auto interface = provider(address, model);
+ DEBUG_PRINT << "returned " << interface << '\n';
+ if (interface)
{
- setWakeOnPECI(false);
+ return interface;
}
}
- catch (const PECIError& err)
+ catch (...)
{}
}
-
- static bool isSleeping(EPECIStatus libStatus, uint8_t completionCode)
- {
- // PECI completion code defined in peci-ioctl.h which is not available
- // for us to include.
- constexpr int PECI_DEV_CC_UNAVAIL_RESOURCE = 0x82;
- // Observed library returning DRIVER_ERR for reads and TIMEOUT for
- // writes while PECI is sleeping. Either way, the completion code from
- // PECI client should be reliable indicator of need to set WOP.
- return libStatus != PECI_CC_SUCCESS &&
- completionCode == PECI_DEV_CC_UNAVAIL_RESOURCE;
- }
-
- /**
- * Send a single PECI PCS write to modify the Wake-On-PECI mode bit
- */
- void setWakeOnPECI(bool enable)
- {
- uint8_t completionCode;
- EPECIStatus libStatus =
- peci_WrPkgConfig(peciAddress, 5, enable ? 1 : 0, 0,
- sizeof(uint32_t), &completionCode);
- if (!checkPECIStatus(libStatus, completionCode))
- {
- throw PECIError("Failed to set Wake-On-PECI mode bit");
- }
-
- if (enable)
- {
- peciWoken = true;
- }
- }
-
- // PCode OS Mailbox interface register locations
- static constexpr int mbSegment = 0;
- static constexpr int mbDevice = 30;
- static constexpr int mbFunction = 1;
- static constexpr int mbDataReg = 0xA0;
- static constexpr int mbInterfaceReg = 0xA4;
- static constexpr int mbRegSize = sizeof(uint32_t);
-
- enum class MailboxStatus
- {
- NoError = 0x0,
- InvalidCommand = 0x1,
- IllegalData = 0x16
- };
-
- /**
- * Send a single Write PCI Config Local command, targeting the PCU CR1
- * register block.
- *
- * @param[in] regAddress PCI Offset of register.
- * @param[in] data Data to write.
- */
- void wrMailboxReg(uint16_t regAddress, uint32_t data)
- {
- uint8_t completionCode;
- bool tryWaking = true;
- while (true)
- {
- EPECIStatus libStatus = peci_WrEndPointPCIConfigLocal(
- peciAddress, mbSegment, mbBus, mbDevice, mbFunction, regAddress,
- mbRegSize, data, &completionCode);
- if (tryWaking && isSleeping(libStatus, completionCode))
- {
- setWakeOnPECI(true);
- tryWaking = false;
- continue;
- }
- else if (!checkPECIStatus(libStatus, completionCode))
- {
- throw PECIError("Failed to write mailbox reg");
- }
- break;
- }
- }
-
- /**
- * Send a single Read PCI Config Local command, targeting the PCU CR1
- * register block.
- *
- * @param[in] regAddress PCI offset of register.
- *
- * @return Register value
- */
- uint32_t rdMailboxReg(uint16_t regAddress)
- {
- uint8_t completionCode;
- uint32_t outputData;
- bool tryWaking = true;
- while (true)
- {
- EPECIStatus libStatus = peci_RdEndPointConfigPciLocal(
- peciAddress, mbSegment, mbBus, mbDevice, mbFunction, regAddress,
- mbRegSize, reinterpret_cast<uint8_t*>(&outputData),
- &completionCode);
- if (tryWaking && isSleeping(libStatus, completionCode))
- {
- setWakeOnPECI(true);
- tryWaking = false;
- continue;
- }
- if (!checkPECIStatus(libStatus, completionCode))
- {
- throw PECIError("Failed to read mailbox reg");
- }
- break;
- }
- return outputData;
- }
-
- /**
- * Send command on PCode OS Mailbox interface.
- *
- * @param[in] command Main command ID.
- * @param[in] subCommand Sub command ID.
- * @param[in] inputData Data to put in mailbox. Is always written, but
- * will be ignored by PCode if command is a
- * "getter".
- * @param[out] responseCode Optional parameter to receive the
- * mailbox-level response status. If null, a
- * PECIError will be thrown for error status.
- *
- * @return Data returned in mailbox. Value is undefined if command is a
- * "setter".
- */
- uint32_t sendPECIOSMailboxCmd(uint8_t command, uint8_t subCommand,
- uint32_t inputData = 0,
- MailboxStatus* responseCode = nullptr)
- {
- // The simple mailbox algorithm just says to wait until the busy bit
- // is clear, but we'll give up after 10 tries. It's arbitrary but that's
- // quite long wall clock time.
- constexpr int mbRetries = 10;
- constexpr uint32_t mbBusyBit = bit(31);
-
- // Wait until RUN_BUSY == 0
- int attempts = mbRetries;
- while ((rdMailboxReg(mbInterfaceReg) & mbBusyBit) != 0 &&
- --attempts > 0)
- ;
- if (attempts == 0)
- {
- throw PECIError("OS Mailbox failed to become free");
- }
-
- // Write required command specific input data to data register
- wrMailboxReg(mbDataReg, inputData);
-
- // Write required command specific command/sub-command values and set
- // RUN_BUSY bit in interface register.
- uint32_t interfaceReg =
- mbBusyBit | (static_cast<uint32_t>(subCommand) << 8) | command;
- wrMailboxReg(mbInterfaceReg, interfaceReg);
-
- // Wait until RUN_BUSY == 0
- attempts = mbRetries;
- do
- {
- interfaceReg = rdMailboxReg(mbInterfaceReg);
- } while ((interfaceReg & mbBusyBit) != 0 && --attempts > 0);
- if (attempts == 0)
- {
- throw PECIError("OS Mailbox failed to return");
- }
-
- // Read command return status or error code from interface register
- auto status = static_cast<MailboxStatus>(interfaceReg & 0xFF);
- if (responseCode != nullptr)
- {
- *responseCode = status;
- }
- else if (status != MailboxStatus::NoError)
- {
- throw PECIError(std::string("OS Mailbox returned with error: ") +
- std::to_string(static_cast<int>(status)));
- }
-
- // Read command return data from the data register
- return rdMailboxReg(mbDataReg);
- }
-};
-
-/**
- * Base class for set of PECI OS Mailbox commands.
- * Constructing it runs the command and stores the value for use by derived
- * class accessor methods.
- */
-template <uint8_t subcommand>
-struct OsMailboxCommand
-{
- enum ErrorPolicy
- {
- Throw,
- NoThrow
- };
-
- uint32_t value;
- PECIManager::MailboxStatus status;
- /**
- * Construct the command object with required PECI address and up to 4
- * optional 1-byte input data parameters.
- */
- OsMailboxCommand(PECIManager& pm, uint8_t param1 = 0, uint8_t param2 = 0,
- uint8_t param3 = 0, uint8_t param4 = 0) :
- OsMailboxCommand(pm, ErrorPolicy::Throw, param1, param2, param3, param4)
- {}
-
- OsMailboxCommand(PECIManager& pm, ErrorPolicy errorPolicy,
- uint8_t param1 = 0, uint8_t param2 = 0, uint8_t param3 = 0,
- uint8_t param4 = 0)
- {
- PECIManager::MailboxStatus* callStatus =
- errorPolicy == Throw ? nullptr : &status;
- uint32_t param =
- (param4 << 24) | (param3 << 16) | (param2 << 8) | param1;
- value = pm.sendPECIOSMailboxCmd(0x7F, subcommand, param, callStatus);
- }
-
- /** Return whether the mailbox status indicated success or not. */
- bool success() const
- {
- return status == PECIManager::MailboxStatus::NoError;
- }
-};
-
-/**
- * Macro to define a derived class accessor method.
- *
- * @param[in] type Return type of accessor method.
- * @param[in] name Name of accessor method.
- * @param[in] hibit Most significant bit of field to access.
- * @param[in] lobit Least significant bit of field to access.
- */
-#define FIELD(type, name, hibit, lobit) \
- type name() const \
- { \
- return (value >> lobit) & (bit(hibit - lobit + 1) - 1); \
- }
-
-struct GetLevelsInfo : OsMailboxCommand<0x0>
-{
- using OsMailboxCommand::OsMailboxCommand;
- FIELD(bool, enabled, 31, 31)
- FIELD(bool, lock, 24, 24)
- FIELD(int, currentConfigTdpLevel, 23, 16)
- FIELD(int, configTdpLevels, 15, 8)
- FIELD(int, version, 7, 0)
-};
-
-struct GetConfigTdpControl : OsMailboxCommand<0x1>
-{
- using OsMailboxCommand::OsMailboxCommand;
- FIELD(bool, pbfEnabled, 17, 17);
- FIELD(bool, factEnabled, 16, 16);
- FIELD(bool, pbfSupport, 1, 1);
- FIELD(bool, factSupport, 0, 0);
-};
-
-struct SetConfigTdpControl : OsMailboxCommand<0x2>
-{
- using OsMailboxCommand::OsMailboxCommand;
-};
-
-struct GetTdpInfo : OsMailboxCommand<0x3>
-{
- using OsMailboxCommand::OsMailboxCommand;
- FIELD(int, tdpRatio, 23, 16);
- FIELD(int, pkgTdp, 14, 0);
-};
-
-struct GetCoreMask : OsMailboxCommand<0x6>
-{
- using OsMailboxCommand::OsMailboxCommand;
- FIELD(uint32_t, coresMask, 31, 0);
-};
-
-struct GetTurboLimitRatios : OsMailboxCommand<0x7>
-{
- using OsMailboxCommand::OsMailboxCommand;
-};
-
-struct SetLevel : OsMailboxCommand<0x8>
-{
- using OsMailboxCommand::OsMailboxCommand;
-};
-
-struct GetRatioInfo : OsMailboxCommand<0xC>
-{
- using OsMailboxCommand::OsMailboxCommand;
- FIELD(int, pm, 31, 24);
- FIELD(int, pn, 23, 16);
- FIELD(int, p1, 15, 8);
- FIELD(int, p0, 7, 0);
-};
-
-struct GetTjmaxInfo : OsMailboxCommand<0x5>
-{
- using OsMailboxCommand::OsMailboxCommand;
- FIELD(int, tProchot, 7, 0);
-};
-
-struct PbfGetCoreMaskInfo : OsMailboxCommand<0x20>
-{
- using OsMailboxCommand::OsMailboxCommand;
- FIELD(uint32_t, p1HiCoreMask, 31, 0);
-};
-
-struct PbfGetP1HiP1LoInfo : OsMailboxCommand<0x21>
-{
- using OsMailboxCommand::OsMailboxCommand;
- FIELD(int, p1Hi, 15, 8);
- FIELD(int, p1Lo, 7, 0);
-};
+ DEBUG_PRINT << "No supported backends found\n";
+ return nullptr;
+}
using BaseCurrentOperatingConfig =
sdbusplus::server::object_t<sdbusplus::xyz::openbmc_project::Control::
@@ -459,11 +100,12 @@
{
public:
std::string path;
- int level;
+ unsigned int level;
public:
using BaseOperatingConfig::BaseOperatingConfig;
- OperatingConfig(sdbusplus::bus::bus& bus, int level_, std::string path_) :
+ OperatingConfig(sdbusplus::bus::bus& bus, unsigned int level_,
+ std::string path_) :
BaseOperatingConfig(bus, path_.c_str(), action::defer_emit),
path(std::move(path_)), level(level_)
{}
@@ -475,10 +117,9 @@
/** Objects describing all available SST configs - not modifiable. */
std::vector<std::unique_ptr<OperatingConfig>> availConfigs;
sdbusplus::bus::bus& bus;
- const int peciAddress;
+ const uint8_t peciAddress;
const std::string path; ///< D-Bus path of CPU object
const CPUModel cpuModel;
- const bool modificationAllowed;
// Keep mutable copies of the properties so we can cache values that we
// retrieve in the getters. We don't want to throw an error on a D-Bus
@@ -487,38 +128,31 @@
// These values can be changed by in-band software so we have to do a full
// PECI read on every get-property, and can't assume that values will change
// only when set-property is done.
- mutable int currentLevel;
+ mutable unsigned int currentLevel;
mutable bool bfEnabled;
- /**
- * Cached SST-TF enablement status. This is not exposed on D-Bus, but it's
- * needed because the command SetConfigTdpControl requires setting both
- * bits at once.
- */
- mutable bool tfEnabled;
/**
* Enforce common pre-conditions for D-Bus set property handlers.
*/
- void setPropertyCheckOrThrow()
+ void setPropertyCheckOrThrow(SSTInterface& sst)
{
- if (!modificationAllowed)
+ if (!sst.supportsControl())
{
throw sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed();
}
- if (hostState != HostState::postComplete)
+ if (hostState != HostState::postComplete || !sst.ready())
{
throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable();
}
}
public:
- CPUConfig(sdbusplus::bus::bus& bus_, int index, CPUModel model) :
+ CPUConfig(sdbusplus::bus::bus& bus_, uint8_t index, CPUModel model) :
BaseCurrentOperatingConfig(bus_, generatePath(index).c_str(),
action::defer_emit),
bus(bus_), peciAddress(index + MIN_CLIENT_ADDR),
- path(generatePath(index)), cpuModel(model),
- modificationAllowed(modelSupportsControl(model)), currentLevel(0),
- bfEnabled(false), tfEnabled(false)
+ path(generatePath(index)), cpuModel(model), currentLevel(0),
+ bfEnabled(false)
{}
//
@@ -527,20 +161,29 @@
sdbusplus::message::object_path appliedConfig() const override
{
+ DEBUG_PRINT << "Reading AppliedConfig\n";
// If CPU is powered off, return power-up default value of Level 0.
- int level = 0;
+ unsigned int level = 0;
if (hostState != HostState::off)
{
// Otherwise, try to read current state
- try
+ auto sst = getInstance(peciAddress, cpuModel);
+ if (!sst)
{
- PECIManager pm(peciAddress, cpuModel);
- currentLevel = GetLevelsInfo(pm).currentConfigTdpLevel();
+ std::cerr << __func__
+ << ": Failed to get SST provider instance\n";
}
- catch (const PECIError& error)
+ else
{
- std::cerr << "Failed to get SST-PP level: " << error.what()
- << "\n";
+ try
+ {
+ currentLevel = sst->currentLevel();
+ }
+ catch (const PECIError& error)
+ {
+ std::cerr << "Failed to get SST-PP level: " << error.what()
+ << "\n";
+ }
}
level = currentLevel;
}
@@ -549,20 +192,27 @@
bool baseSpeedPriorityEnabled() const override
{
+ DEBUG_PRINT << "Reading BaseSpeedPriorityEnabled\n";
bool enabled = false;
if (hostState != HostState::off)
{
- try
+ auto sst = getInstance(peciAddress, cpuModel);
+ if (!sst)
{
- PECIManager pm(peciAddress, cpuModel);
- GetConfigTdpControl tdpControl(pm, currentLevel);
- bfEnabled = tdpControl.pbfEnabled();
- tfEnabled = tdpControl.factEnabled();
+ std::cerr << __func__
+ << ": Failed to get SST provider instance\n";
}
- catch (const PECIError& error)
+ else
{
- std::cerr << "Failed to get SST-BF status: " << error.what()
- << "\n";
+ try
+ {
+ bfEnabled = sst->bfEnabled(currentLevel);
+ }
+ catch (const PECIError& error)
+ {
+ std::cerr << "Failed to get SST-BF status: " << error.what()
+ << "\n";
+ }
}
enabled = bfEnabled;
}
@@ -572,8 +222,7 @@
sdbusplus::message::object_path
appliedConfig(sdbusplus::message::object_path value) override
{
- setPropertyCheckOrThrow();
-
+ DEBUG_PRINT << "Writing AppliedConfig\n";
const OperatingConfig* newConfig = nullptr;
for (const auto& config : availConfigs)
{
@@ -589,10 +238,16 @@
InvalidArgument();
}
+ auto sst = getInstance(peciAddress, cpuModel);
+ if (!sst)
+ {
+ std::cerr << __func__ << ": Failed to get SST provider instance\n";
+ return sdbusplus::message::object_path();
+ }
+ setPropertyCheckOrThrow(*sst);
try
{
- PECIManager pm(peciAddress, cpuModel);
- SetLevel(pm, newConfig->level);
+ sst->setCurrentLevel(newConfig->level);
currentLevel = newConfig->level;
}
catch (const PECIError& error)
@@ -609,16 +264,17 @@
bool baseSpeedPriorityEnabled(bool value) override
{
- setPropertyCheckOrThrow();
-
+ DEBUG_PRINT << "Writing BaseSpeedPriorityEnabled\n";
+ auto sst = getInstance(peciAddress, cpuModel);
+ if (!sst)
+ {
+ std::cerr << __func__ << ": Failed to get SST provider instance\n";
+ return false;
+ }
+ setPropertyCheckOrThrow(*sst);
try
{
- constexpr uint32_t bfEnabledBit = bit(17);
- constexpr uint32_t tfEnabledBit = bit(16);
- PECIManager pm(peciAddress, cpuModel);
- uint32_t param =
- (value ? bfEnabledBit : 0) | (tfEnabled ? tfEnabledBit : 0);
- SetConfigTdpControl tdpControl(pm, 0, 0, param >> 16);
+ sst->setBfEnabled(value);
}
catch (const PECIError& error)
{
@@ -636,14 +292,14 @@
// Additions
//
- OperatingConfig& newConfig(int level)
+ OperatingConfig& newConfig(unsigned int level)
{
availConfigs.emplace_back(std::make_unique<OperatingConfig>(
bus, level, generateConfigPath(level)));
return *availConfigs.back();
}
- std::string generateConfigPath(int level) const
+ std::string generateConfigPath(unsigned int level) const
{
return path + "/config" + std::to_string(level);
}
@@ -672,78 +328,41 @@
* Retrieve the SST parameters for a single config and fill the values into the
* properties on the D-Bus interface.
*
- * @param[in,out] peciManager PECI context to use.
- * @param[in] level Config TDP level to retrieve.
- * @param[out] config D-Bus interface to update.
- * @param[in] trlCores Turbo ratio limit core ranges from MSR
- * 0x1AE. This is constant across all configs
- * in a CPU.
+ * @param[in,out] sst Interface to SST backend.
+ * @param[in] level Config TDP level to retrieve.
+ * @param[out] config D-Bus interface to update.
*/
-static void getSingleConfig(PECIManager& peciManager, int level,
- OperatingConfig& config, uint64_t trlCores)
+static void getSingleConfig(SSTInterface& sst, unsigned int level,
+ OperatingConfig& config)
{
- constexpr int mhzPerRatio = 100;
+ config.powerLimit(sst.tdp(level));
- // PowerLimit <= GET_TDP_INFO.PKG_TDP
- config.powerLimit(GetTdpInfo(peciManager, level).pkgTdp());
+ config.availableCoreCount(sst.coreCount(level));
- // AvailableCoreCount <= GET_CORE_MASK.CORES_MASK
- uint64_t coreMaskLo = GetCoreMask(peciManager, level, 0).coresMask();
- uint64_t coreMaskHi = GetCoreMask(peciManager, level, 1).coresMask();
- std::bitset<64> coreMask = (coreMaskHi << 32) | coreMaskLo;
- config.availableCoreCount(coreMask.count());
+ config.baseSpeed(sst.p1Freq(level));
- // BaseSpeed <= GET_RATIO_INFO.P1
- GetRatioInfo getRatioInfo(peciManager, level);
- config.baseSpeed(getRatioInfo.p1() * mhzPerRatio);
+ config.maxSpeed(sst.p0Freq(level));
- // MaxSpeed <= GET_RATIO_INFO.P0
- config.maxSpeed(getRatioInfo.p0() * mhzPerRatio);
-
- // MaxJunctionTemperature <= GET_TJMAX_INFO.T_PROCHOT
- config.maxJunctionTemperature(GetTjmaxInfo(peciManager, level).tProchot());
+ config.maxJunctionTemperature(sst.prochotTemp(level));
// Construct BaseSpeedPrioritySettings
- GetConfigTdpControl getConfigTdpControl(peciManager, level);
std::vector<std::tuple<uint32_t, std::vector<uint32_t>>> baseSpeeds;
- if (getConfigTdpControl.pbfSupport())
+ if (sst.bfSupported(level))
{
- coreMaskLo = PbfGetCoreMaskInfo(peciManager, level, 0).p1HiCoreMask();
- coreMaskHi = PbfGetCoreMaskInfo(peciManager, level, 1).p1HiCoreMask();
- std::bitset<64> hiFreqCoreMask = (coreMaskHi << 32) | coreMaskLo;
+ std::vector<uint32_t> totalCoreList, loFreqCoreList, hiFreqCoreList;
+ totalCoreList = sst.enabledCoreList(level);
+ hiFreqCoreList = sst.bfHighPriorityCoreList(level);
+ std::set_difference(
+ totalCoreList.begin(), totalCoreList.end(), hiFreqCoreList.begin(),
+ hiFreqCoreList.end(),
+ std::inserter(loFreqCoreList, loFreqCoreList.begin()));
- std::vector<uint32_t> hiFreqCoreList, loFreqCoreList;
- hiFreqCoreList = convertMaskToList(hiFreqCoreMask);
- loFreqCoreList = convertMaskToList(coreMask & ~hiFreqCoreMask);
-
- PbfGetP1HiP1LoInfo pbfGetP1HiP1LoInfo(peciManager, level);
- baseSpeeds = {
- {pbfGetP1HiP1LoInfo.p1Hi() * mhzPerRatio, hiFreqCoreList},
- {pbfGetP1HiP1LoInfo.p1Lo() * mhzPerRatio, loFreqCoreList}};
+ baseSpeeds = {{sst.bfHighPriorityFreq(level), hiFreqCoreList},
+ {sst.bfLowPriorityFreq(level), loFreqCoreList}};
}
config.baseSpeedPrioritySettings(baseSpeeds);
- // Construct TurboProfile
- std::vector<std::tuple<uint32_t, size_t>> turboSpeeds;
-
- // Only read the SSE ratios (don't need AVX2/AVX512).
- uint64_t limitRatioLo = GetTurboLimitRatios(peciManager, level, 0, 0).value;
- uint64_t limitRatioHi = GetTurboLimitRatios(peciManager, level, 1, 0).value;
- uint64_t limitRatios = (limitRatioHi << 32) | limitRatioLo;
-
- constexpr int maxTFBuckets = 8;
- for (int i = 0; i < maxTFBuckets; ++i)
- {
- size_t bucketCount = trlCores & 0xFF;
- int bucketSpeed = limitRatios & 0xFF;
- if (bucketCount != 0 && bucketSpeed != 0)
- {
- turboSpeeds.push_back({bucketSpeed * mhzPerRatio, bucketCount});
- }
- trlCores >>= 8;
- limitRatios >>= 8;
- }
- config.turboProfile(turboSpeeds);
+ config.turboProfile(sst.sseTurboProfile(level));
}
/**
@@ -754,6 +373,7 @@
* including pointers to D-Bus objects to keep them
* alive. No items may be added to list in case host
* system is powered off and no CPUs are accessible.
+ * @param[in,out] ioc ASIO context.
* @param[in,out] conn D-Bus ASIO connection.
*
* @return Whether discovery was successfully finished.
@@ -766,7 +386,7 @@
boost::asio::io_context& ioc,
sdbusplus::asio::connection& conn)
{
- for (int i = MIN_CLIENT_ADDR; i <= MAX_CLIENT_ADDR; ++i)
+ for (uint8_t i = MIN_CLIENT_ADDR; i <= MAX_CLIENT_ADDR; ++i)
{
// Let the event handler run any waiting tasks. If there is a lot of
// PECI contention, SST discovery could take a long time. This lets us
@@ -778,6 +398,9 @@
return false;
}
+ unsigned int cpuIndex = i - MIN_CLIENT_ADDR;
+ DEBUG_PRINT << "Discovering CPU " << cpuIndex << '\n';
+
// We could possibly check D-Bus for CPU presence and model, but PECI is
// 10x faster and so much simpler.
uint8_t cc, stepping;
@@ -789,32 +412,37 @@
// working yet. Try again later.
throw PECIError("Get CPUID timed out");
}
- if (status != PECI_CC_SUCCESS || cc != PECI_DEV_CC_SUCCESS ||
- !modelSupportsDiscovery(cpuModel))
+ if (status == PECI_CC_CPU_NOT_PRESENT)
{
continue;
}
-
- PECIManager peciManager(i, cpuModel);
-
- // Continue if processor does not support SST-PP
- GetLevelsInfo getLevelsInfo(peciManager);
- if (!getLevelsInfo.enabled())
+ if (status != PECI_CC_SUCCESS || cc != PECI_DEV_CC_SUCCESS)
{
+ std::cerr << "GetCPUID returned status " << status
+ << ", cc = " << cc << '\n';
continue;
}
- // Generate D-Bus object path for this processor.
- int cpuIndex = i - MIN_CLIENT_ADDR;
+ std::unique_ptr<SSTInterface> sst = getInstance(i, cpuModel);
- // Read the Turbo Ratio Limit Cores MSR which is used to generate the
- // Turbo Profile for each profile. This is a package scope MSR, so just
- // read thread 0.
- uint64_t trlCores;
- status = peci_RdIAMSR(i, 0, 0x1AE, &trlCores, &cc);
- if (!checkPECIStatus(status, cc))
+ if (!sst)
{
- throw PECIError("Failed to read TRL MSR");
+ // No supported backend for this CPU.
+ continue;
+ }
+
+ if (!sst->ready())
+ {
+ // Supported CPU but it can't be queried yet. Try again later.
+ std::cerr << "sst not ready yet\n";
+ return false;
+ }
+
+ if (!sst->ppEnabled())
+ {
+ // Supported CPU but the specific SKU doesn't support SST-PP.
+ std::cerr << "CPU doesn't support SST-PP\n";
+ continue;
}
// Create the per-CPU configuration object
@@ -824,23 +452,19 @@
bool foundCurrentLevel = false;
- for (int level = 0; level <= getLevelsInfo.configTdpLevels(); ++level)
+ for (unsigned int level = 0; level <= sst->numLevels(); ++level)
{
- // levels 1 and 2 are legacy/deprecated, originally used for AVX
+ // levels 1 and 2 were legacy/deprecated, originally used for AVX
// license pre-granting. They may be reused for more levels in
- // future generations.
- // We can check if they are supported by running any command for
- // this level and checking the mailbox return status.
- GetConfigTdpControl tdpControl(
- peciManager, GetConfigTdpControl::ErrorPolicy::NoThrow, level);
- if (!tdpControl.success())
+ // future generations. So we need to check for discontinuities.
+ if (!sst->levelSupported(level))
{
continue;
}
- getSingleConfig(peciManager, level, cpu.newConfig(level), trlCores);
+ getSingleConfig(*sst, level, cpu.newConfig(level));
- if (level == getLevelsInfo.currentConfigTdpLevel())
+ if (level == sst->currentLevel())
{
foundCurrentLevel = true;
}
@@ -883,7 +507,7 @@
// In case of repeated failure to finish discovery, turn off this
// feature altogether. Possible cause is that the CPU model does not
- // actually support the necessary mailbox commands.
+ // actually support the necessary commands.
if (++peciErrorCount >= 50)
{
std::cerr << "Aborting SST discovery\n";
diff --git a/src/sst_mailbox.cpp b/src/sst_mailbox.cpp
new file mode 100644
index 0000000..bd929d4
--- /dev/null
+++ b/src/sst_mailbox.cpp
@@ -0,0 +1,580 @@
+// Copyright (c) 2022 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cpuinfo_utils.hpp"
+#include "speed_select.hpp"
+
+#include <iostream>
+
+namespace cpu_info
+{
+namespace sst
+{
+
+/**
+ * Convenience RAII object for Wake-On-PECI (WOP) management, since PECI Config
+ * Local accesses to the OS Mailbox require the package to pop up to PC2. Also
+ * provides PCode OS Mailbox routine.
+ *
+ * Since multiple applications may be modifing WOP, we'll use this algorithm:
+ * Whenever a PECI command fails with associated error code, set WOP bit and
+ * retry command. Upon manager destruction, clear WOP bit only if we previously
+ * set it.
+ */
+struct PECIManager
+{
+ uint8_t peciAddress;
+ bool peciWoken;
+ CPUModel cpuModel;
+ uint8_t mbBus;
+
+ PECIManager(uint8_t address, CPUModel model) :
+ peciAddress(address), peciWoken(false), cpuModel(model)
+ {
+ mbBus = (model == icx) ? mbBusICX : mbBusOther;
+ }
+
+ ~PECIManager()
+ {
+ // If we're being destroyed due to a PECIError, try to clear the mode
+ // bit, but catch and ignore any duplicate error it might raise to
+ // prevent termination.
+ try
+ {
+ if (peciWoken)
+ {
+ setWakeOnPECI(false);
+ }
+ }
+ catch (const PECIError& err)
+ {}
+ }
+
+ static bool isSleeping(EPECIStatus libStatus, uint8_t completionCode)
+ {
+ // PECI completion code defined in peci-ioctl.h which is not available
+ // for us to include.
+ constexpr int PECI_DEV_CC_UNAVAIL_RESOURCE = 0x82;
+ // Observed library returning DRIVER_ERR for reads and TIMEOUT for
+ // writes while PECI is sleeping. Either way, the completion code from
+ // PECI client should be reliable indicator of need to set WOP.
+ return libStatus != PECI_CC_SUCCESS &&
+ completionCode == PECI_DEV_CC_UNAVAIL_RESOURCE;
+ }
+
+ /**
+ * Send a single PECI PCS write to modify the Wake-On-PECI mode bit
+ */
+ void setWakeOnPECI(bool enable)
+ {
+ uint8_t completionCode;
+ EPECIStatus libStatus =
+ peci_WrPkgConfig(peciAddress, 5, enable ? 1 : 0, 0,
+ sizeof(uint32_t), &completionCode);
+ if (!checkPECIStatus(libStatus, completionCode))
+ {
+ throw PECIError("Failed to set Wake-On-PECI mode bit");
+ }
+
+ if (enable)
+ {
+ peciWoken = true;
+ }
+ }
+
+ // PCode OS Mailbox interface register locations
+ static constexpr int mbBusICX = 14;
+ static constexpr int mbBusOther = 31;
+ static constexpr int mbSegment = 0;
+ static constexpr int mbDevice = 30;
+ static constexpr int mbFunction = 1;
+ static constexpr int mbDataReg = 0xA0;
+ static constexpr int mbInterfaceReg = 0xA4;
+ static constexpr int mbRegSize = sizeof(uint32_t);
+
+ enum class MailboxStatus
+ {
+ NoError = 0x0,
+ InvalidCommand = 0x1,
+ IllegalData = 0x16
+ };
+
+ /**
+ * Send a single Write PCI Config Local command, targeting the PCU CR1
+ * register block.
+ *
+ * @param[in] regAddress PCI Offset of register.
+ * @param[in] data Data to write.
+ */
+ void wrMailboxReg(uint16_t regAddress, uint32_t data)
+ {
+ uint8_t completionCode;
+ bool tryWaking = true;
+ while (true)
+ {
+ EPECIStatus libStatus = peci_WrEndPointPCIConfigLocal(
+ peciAddress, mbSegment, mbBus, mbDevice, mbFunction, regAddress,
+ mbRegSize, data, &completionCode);
+ if (tryWaking && isSleeping(libStatus, completionCode))
+ {
+ setWakeOnPECI(true);
+ tryWaking = false;
+ continue;
+ }
+ else if (!checkPECIStatus(libStatus, completionCode))
+ {
+ throw PECIError("Failed to write mailbox reg");
+ }
+ break;
+ }
+ }
+
+ /**
+ * Send a single Read PCI Config Local command, targeting the PCU CR1
+ * register block.
+ *
+ * @param[in] regAddress PCI offset of register.
+ *
+ * @return Register value
+ */
+ uint32_t rdMailboxReg(uint16_t regAddress)
+ {
+ uint8_t completionCode;
+ uint32_t outputData;
+ bool tryWaking = true;
+ while (true)
+ {
+ EPECIStatus libStatus = peci_RdEndPointConfigPciLocal(
+ peciAddress, mbSegment, mbBus, mbDevice, mbFunction, regAddress,
+ mbRegSize, reinterpret_cast<uint8_t*>(&outputData),
+ &completionCode);
+ if (tryWaking && isSleeping(libStatus, completionCode))
+ {
+ setWakeOnPECI(true);
+ tryWaking = false;
+ continue;
+ }
+ if (!checkPECIStatus(libStatus, completionCode))
+ {
+ throw PECIError("Failed to read mailbox reg");
+ }
+ break;
+ }
+ return outputData;
+ }
+
+ /**
+ * Send command on PCode OS Mailbox interface.
+ *
+ * @param[in] command Main command ID.
+ * @param[in] subCommand Sub command ID.
+ * @param[in] inputData Data to put in mailbox. Is always written, but
+ * will be ignored by PCode if command is a
+ * "getter".
+ * @param[out] responseCode Optional parameter to receive the
+ * mailbox-level response status. If null, a
+ * PECIError will be thrown for error status.
+ *
+ * @return Data returned in mailbox. Value is undefined if command is a
+ * "setter".
+ */
+ uint32_t sendPECIOSMailboxCmd(uint8_t command, uint8_t subCommand,
+ uint32_t inputData = 0,
+ MailboxStatus* responseCode = nullptr)
+ {
+ // The simple mailbox algorithm just says to wait until the busy bit
+ // is clear, but we'll give up after 10 tries. It's arbitrary but that's
+ // quite long wall clock time.
+ constexpr int mbRetries = 10;
+ constexpr uint32_t mbBusyBit = bit(31);
+
+ // Wait until RUN_BUSY == 0
+ int attempts = mbRetries;
+ while ((rdMailboxReg(mbInterfaceReg) & mbBusyBit) != 0 &&
+ --attempts > 0)
+ ;
+ if (attempts == 0)
+ {
+ throw PECIError("OS Mailbox failed to become free");
+ }
+
+ // Write required command specific input data to data register
+ wrMailboxReg(mbDataReg, inputData);
+
+ // Write required command specific command/sub-command values and set
+ // RUN_BUSY bit in interface register.
+ uint32_t interfaceReg =
+ mbBusyBit | (static_cast<uint32_t>(subCommand) << 8) | command;
+ wrMailboxReg(mbInterfaceReg, interfaceReg);
+
+ // Wait until RUN_BUSY == 0
+ attempts = mbRetries;
+ do
+ {
+ interfaceReg = rdMailboxReg(mbInterfaceReg);
+ } while ((interfaceReg & mbBusyBit) != 0 && --attempts > 0);
+ if (attempts == 0)
+ {
+ throw PECIError("OS Mailbox failed to return");
+ }
+
+ // Read command return status or error code from interface register
+ auto status = static_cast<MailboxStatus>(interfaceReg & 0xFF);
+ if (responseCode != nullptr)
+ {
+ *responseCode = status;
+ }
+ else if (status != MailboxStatus::NoError)
+ {
+ throw PECIError(std::string("OS Mailbox returned with error: ") +
+ std::to_string(static_cast<int>(status)));
+ }
+
+ // Read command return data from the data register
+ return rdMailboxReg(mbDataReg);
+ }
+};
+
+/**
+ * Base class for set of PECI OS Mailbox commands.
+ * Constructing it runs the command and stores the value for use by derived
+ * class accessor methods.
+ */
+template <uint8_t subcommand>
+struct OsMailboxCommand
+{
+ enum ErrorPolicy
+ {
+ Throw,
+ NoThrow
+ };
+
+ uint32_t value;
+ PECIManager::MailboxStatus status;
+ /**
+ * Construct the command object with required PECI address and up to 4
+ * optional 1-byte input data parameters.
+ */
+ OsMailboxCommand(PECIManager& pm, uint8_t param1 = 0, uint8_t param2 = 0,
+ uint8_t param3 = 0, uint8_t param4 = 0) :
+ OsMailboxCommand(pm, ErrorPolicy::Throw, param1, param2, param3, param4)
+ {}
+
+ OsMailboxCommand(PECIManager& pm, ErrorPolicy errorPolicy,
+ uint8_t param1 = 0, uint8_t param2 = 0, uint8_t param3 = 0,
+ uint8_t param4 = 0)
+ {
+ DEBUG_PRINT << "Running OS Mailbox command "
+ << static_cast<int>(subcommand) << '\n';
+ PECIManager::MailboxStatus* callStatus =
+ errorPolicy == Throw ? nullptr : &status;
+ uint32_t param = (static_cast<uint32_t>(param4) << 24) |
+ (static_cast<uint32_t>(param3) << 16) |
+ (static_cast<uint32_t>(param2) << 8) | param1;
+ value = pm.sendPECIOSMailboxCmd(0x7F, subcommand, param, callStatus);
+ }
+
+ /** Return whether the mailbox status indicated success or not. */
+ bool success() const
+ {
+ return status == PECIManager::MailboxStatus::NoError;
+ }
+};
+
+/**
+ * Macro to define a derived class accessor method.
+ *
+ * @param[in] type Return type of accessor method.
+ * @param[in] name Name of accessor method.
+ * @param[in] hibit Most significant bit of field to access.
+ * @param[in] lobit Least significant bit of field to access.
+ */
+#define FIELD(type, name, hibit, lobit) \
+ type name() const \
+ { \
+ return (value >> lobit) & (bit(hibit - lobit + 1) - 1); \
+ }
+
+struct GetLevelsInfo : OsMailboxCommand<0x0>
+{
+ using OsMailboxCommand::OsMailboxCommand;
+ FIELD(bool, enabled, 31, 31)
+ FIELD(bool, lock, 24, 24)
+ FIELD(unsigned, currentConfigTdpLevel, 23, 16)
+ FIELD(unsigned, configTdpLevels, 15, 8)
+ FIELD(unsigned, version, 7, 0)
+};
+
+struct GetConfigTdpControl : OsMailboxCommand<0x1>
+{
+ using OsMailboxCommand::OsMailboxCommand;
+ FIELD(bool, pbfEnabled, 17, 17);
+ FIELD(bool, factEnabled, 16, 16);
+ FIELD(bool, pbfSupport, 1, 1);
+ FIELD(bool, factSupport, 0, 0);
+};
+
+struct SetConfigTdpControl : OsMailboxCommand<0x2>
+{
+ using OsMailboxCommand::OsMailboxCommand;
+};
+
+struct GetTdpInfo : OsMailboxCommand<0x3>
+{
+ using OsMailboxCommand::OsMailboxCommand;
+ FIELD(unsigned, tdpRatio, 23, 16);
+ FIELD(unsigned, pkgTdp, 14, 0);
+};
+
+struct GetCoreMask : OsMailboxCommand<0x6>
+{
+ using OsMailboxCommand::OsMailboxCommand;
+ FIELD(uint32_t, coresMask, 31, 0);
+};
+
+struct GetTurboLimitRatios : OsMailboxCommand<0x7>
+{
+ using OsMailboxCommand::OsMailboxCommand;
+};
+
+struct SetLevel : OsMailboxCommand<0x8>
+{
+ using OsMailboxCommand::OsMailboxCommand;
+};
+
+struct GetRatioInfo : OsMailboxCommand<0xC>
+{
+ using OsMailboxCommand::OsMailboxCommand;
+ FIELD(unsigned, pm, 31, 24);
+ FIELD(unsigned, pn, 23, 16);
+ FIELD(unsigned, p1, 15, 8);
+ FIELD(unsigned, p0, 7, 0);
+};
+
+struct GetTjmaxInfo : OsMailboxCommand<0x5>
+{
+ using OsMailboxCommand::OsMailboxCommand;
+ FIELD(unsigned, tProchot, 7, 0);
+};
+
+struct PbfGetCoreMaskInfo : OsMailboxCommand<0x20>
+{
+ using OsMailboxCommand::OsMailboxCommand;
+ FIELD(uint32_t, p1HiCoreMask, 31, 0);
+};
+
+struct PbfGetP1HiP1LoInfo : OsMailboxCommand<0x21>
+{
+ using OsMailboxCommand::OsMailboxCommand;
+ FIELD(unsigned, p1Hi, 15, 8);
+ FIELD(unsigned, p1Lo, 7, 0);
+};
+
+/**
+ * Implementation of SSTInterface based on OS Mailbox interface supported on ICX
+ * and SPR processors.
+ * It's expected that an instance of this class will be created for each
+ * "atomic" set of operations.
+ */
+class SSTMailbox : public SSTInterface
+{
+ private:
+ uint8_t address;
+ CPUModel model;
+ PECIManager pm;
+
+ static constexpr int mhzPerRatio = 100;
+
+ public:
+ SSTMailbox(uint8_t _address, CPUModel _model) :
+ address(_address), model(_model),
+ pm(static_cast<uint8_t>(address), model)
+ {}
+ ~SSTMailbox()
+ {}
+
+ bool ready() override
+ {
+ return true;
+ }
+
+ bool supportsControl() override
+ {
+ return model == spr;
+ }
+
+ unsigned int currentLevel() override
+ {
+ return GetLevelsInfo(pm).currentConfigTdpLevel();
+ }
+ unsigned int numLevels() override
+ {
+ return GetLevelsInfo(pm).configTdpLevels();
+ }
+ bool ppEnabled() override
+ {
+ return GetLevelsInfo(pm).enabled();
+ }
+
+ bool levelSupported(unsigned int level) override
+ {
+ GetConfigTdpControl tdpControl(
+ pm, GetConfigTdpControl::ErrorPolicy::NoThrow,
+ static_cast<uint8_t>(level));
+ return tdpControl.success();
+ }
+ bool bfSupported(unsigned int level) override
+ {
+ return GetConfigTdpControl(pm, static_cast<uint8_t>(level))
+ .pbfSupport();
+ }
+ bool tfSupported(unsigned int level) override
+ {
+ return GetConfigTdpControl(pm, static_cast<uint8_t>(level))
+ .factSupport();
+ }
+ bool bfEnabled(unsigned int level) override
+ {
+ return GetConfigTdpControl(pm, static_cast<uint8_t>(level))
+ .pbfEnabled();
+ }
+ bool tfEnabled(unsigned int level) override
+ {
+ return GetConfigTdpControl(pm, static_cast<uint8_t>(level))
+ .factEnabled();
+ }
+ unsigned int tdp(unsigned int level) override
+ {
+ return GetTdpInfo(pm, static_cast<uint8_t>(level)).pkgTdp();
+ }
+ unsigned int coreCount(unsigned int level) override
+ {
+ return enabledCoreList(level).size();
+ }
+ std::vector<unsigned int> enabledCoreList(unsigned int level) override
+ {
+ uint64_t coreMaskLo =
+ GetCoreMask(pm, static_cast<uint8_t>(level), 0).coresMask();
+ uint64_t coreMaskHi =
+ GetCoreMask(pm, static_cast<uint8_t>(level), 1).coresMask();
+ std::bitset<64> coreMask = (coreMaskHi << 32 | coreMaskLo);
+ return convertMaskToList(coreMask);
+ }
+ std::vector<TurboEntry> sseTurboProfile(unsigned int level) override
+ {
+ // Read the Turbo Ratio Limit Cores MSR which is used to generate the
+ // Turbo Profile for each profile. This is a package scope MSR, so just
+ // read thread 0.
+ uint64_t trlCores;
+ uint8_t cc;
+ EPECIStatus status = peci_RdIAMSR(static_cast<uint8_t>(address), 0,
+ 0x1AE, &trlCores, &cc);
+ if (!checkPECIStatus(status, cc))
+ {
+ throw PECIError("Failed to read TRL MSR");
+ }
+
+ std::vector<TurboEntry> turboSpeeds;
+ uint64_t limitRatioLo =
+ GetTurboLimitRatios(pm, static_cast<uint8_t>(level), 0, 0).value;
+ uint64_t limitRatioHi =
+ GetTurboLimitRatios(pm, static_cast<uint8_t>(level), 1, 0).value;
+ uint64_t limitRatios = (limitRatioHi << 32) | limitRatioLo;
+
+ constexpr int maxTFBuckets = 8;
+ for (int i = 0; i < maxTFBuckets; ++i)
+ {
+ size_t bucketCount = trlCores & 0xFF;
+ int bucketSpeed = limitRatios & 0xFF;
+ if (bucketCount != 0 && bucketSpeed != 0)
+ {
+ turboSpeeds.push_back({bucketSpeed * mhzPerRatio, bucketCount});
+ }
+
+ trlCores >>= 8;
+ limitRatios >>= 8;
+ }
+ return turboSpeeds;
+ }
+ unsigned int p1Freq(unsigned int level) override
+ {
+ return GetRatioInfo(pm, static_cast<uint8_t>(level)).p1() * mhzPerRatio;
+ }
+ unsigned int p0Freq(unsigned int level) override
+ {
+ return GetRatioInfo(pm, static_cast<uint8_t>(level)).p0() * mhzPerRatio;
+ }
+ unsigned int prochotTemp(unsigned int level) override
+ {
+ return GetTjmaxInfo(pm, static_cast<uint8_t>(level)).tProchot();
+ }
+ std::vector<unsigned int>
+ bfHighPriorityCoreList(unsigned int level) override
+ {
+ uint64_t coreMaskLo =
+ PbfGetCoreMaskInfo(pm, static_cast<uint8_t>(level), 0)
+ .p1HiCoreMask();
+ uint64_t coreMaskHi =
+ PbfGetCoreMaskInfo(pm, static_cast<uint8_t>(level), 1)
+ .p1HiCoreMask();
+ std::bitset<64> hiFreqCoreList = (coreMaskHi << 32) | coreMaskLo;
+ return convertMaskToList(hiFreqCoreList);
+ }
+ unsigned int bfHighPriorityFreq(unsigned int level) override
+ {
+ return PbfGetP1HiP1LoInfo(pm, static_cast<uint8_t>(level)).p1Hi() *
+ mhzPerRatio;
+ }
+ unsigned int bfLowPriorityFreq(unsigned int level) override
+ {
+ return PbfGetP1HiP1LoInfo(pm, static_cast<uint8_t>(level)).p1Lo() *
+ mhzPerRatio;
+ }
+
+ void setBfEnabled(bool enable) override
+ {
+ GetConfigTdpControl getTDPControl(pm);
+ bool tfEnabled = false;
+ uint8_t param = (enable ? bit(1) : 0) | (tfEnabled ? bit(0) : 0);
+ SetConfigTdpControl(pm, 0, 0, param);
+ }
+ void setTfEnabled(bool enable) override
+ {
+ // TODO: use cached BF value
+ bool bfEnabled = false;
+ uint8_t param = (bfEnabled ? bit(1) : 0) | (enable ? bit(0) : 0);
+ SetConfigTdpControl(pm, 0, 0, param);
+ }
+ void setCurrentLevel(unsigned int level) override
+ {
+ SetLevel(pm, static_cast<uint8_t>(level));
+ }
+};
+
+static std::unique_ptr<SSTInterface> createMailbox(uint8_t address,
+ CPUModel model)
+{
+ DEBUG_PRINT << "createMailbox\n";
+ if (model == icx || model == icxd || model == spr)
+ {
+ return std::make_unique<SSTMailbox>(address, model);
+ }
+
+ return nullptr;
+}
+
+SSTProviderRegistration(createMailbox);
+
+} // namespace sst
+} // namespace cpu_info