155 lines
5.1 KiB
C++
155 lines
5.1 KiB
C++
// Copyright 2020 Google LLC
|
|
//
|
|
// 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.
|
|
|
|
// Intel platform specific code
|
|
|
|
#include <atomic>
|
|
#include <cassert>
|
|
#include <memory>
|
|
|
|
#include "../../fvt_controller.h"
|
|
#include "../../log.h"
|
|
|
|
namespace {
|
|
|
|
class IntelFVTController : public X86FVTController {
|
|
public:
|
|
explicit IntelFVTController(int cpu)
|
|
: X86FVTController(cpu) {
|
|
limit_mhz_ = GetAbsoluteFreqLimitMhzImpl();
|
|
// Set initial Turbo frequency to max.
|
|
SetCurrentMaxFreqMhzImpl(limit_mhz_);
|
|
}
|
|
|
|
~IntelFVTController() override {
|
|
// Upon exit, set Turbo frequency to max.
|
|
SetCurrentMaxFreqMhzImpl(limit_mhz_);
|
|
}
|
|
|
|
// Sets current maximum frequency. Warning: do this to both threads of HT
|
|
// pair. Requires 'mhz' multiple of 100, within legitimate range.
|
|
void SetCurrentFreqLimitMhz(int mhz) override {
|
|
SetCurrentMaxFreqMhzImpl(mhz);
|
|
}
|
|
|
|
// Returns absolute maximum CPU frequency.
|
|
int GetAbsoluteFreqLimitMhz() override {
|
|
return GetAbsoluteFreqLimitMhzImpl();
|
|
}
|
|
|
|
// Returns true if automatic Power Management enabled.
|
|
bool PowerManaged() const override {
|
|
return ReadMsr(k_IA32_PM_ENABLE) & 0x1;
|
|
}
|
|
|
|
// Returns frequency, thermal, and voltage condition.
|
|
std::string FVT() override {
|
|
constexpr double kVoltageScale = 1.0 / (1 << 13);
|
|
const uint64_t v = ReadMsr(k_IA32_THERM_STATUS);
|
|
const bool valid = (v >> 31) & 0x1;
|
|
const int c = valid ? (v >> 16) & 0x7f : 0;
|
|
const bool current_limit = (v >> 12) & 0x1;
|
|
const bool power_limit = (v >> 10) & 0x1;
|
|
const bool critical = (v >> 4) & 0x1;
|
|
const bool proc_hot = v & 0x1; // AKA "Thermal Status"
|
|
const uint64_t p = ReadMsr(k_IA32_PERF_STATUS);
|
|
const double voltage = ((p >> 32) & 0xffff) * kVoltageScale;
|
|
const int f = GetCurrentFreqMhz();
|
|
std::stringstream s;
|
|
s << (critical ? "Critical " : "")
|
|
<< (proc_hot ? "ProcIsHot " : "")
|
|
<< (current_limit ? "CurrentLimit " : "")
|
|
<< (power_limit ? "PowerLimit " : "");
|
|
return Json("f", f) + ", " + Json("voltage", voltage) + ", " +
|
|
Json("margin", c) +
|
|
(s.str().empty() ? "" : ", " + Json("pow_states", s.str()));
|
|
}
|
|
|
|
std::string InterestingEnables() const override {
|
|
const uint64_t v = ReadMsr(k_IA32_MISC_ENABLE);
|
|
const bool fast_strings = v & 0x1;
|
|
const bool auto_thermal_control = (v >> 3) & 0x1;
|
|
const bool pm = PowerManaged();
|
|
std::stringstream s;
|
|
s << (fast_strings ? "FastStrings " : "")
|
|
<< (auto_thermal_control ? "AutoThermalControl " : "")
|
|
<< (pm ? "PowerManagement" : "");
|
|
return s.str();
|
|
}
|
|
|
|
// TODO: separate this from FVT controller.
|
|
void ControlFastStringOps(bool enable) override {
|
|
uint64_t v = ReadMsr(k_IA32_MISC_ENABLE);
|
|
v = (v & ~0x1) | (enable & 0x1);
|
|
WriteMsr(k_IA32_MISC_ENABLE, v);
|
|
}
|
|
|
|
private:
|
|
static constexpr int k_SEND_COMMAND = 0x150;
|
|
static constexpr int k_IA32_PERF_STATUS = 0x198;
|
|
static constexpr int k_IA32_PERF_CTL = 0x199;
|
|
static constexpr int k_IA32_THERM_STATUS = 0x19c;
|
|
static constexpr int k_IA32_MISC_ENABLE = 0x1a0;
|
|
static constexpr int k_MSR_TURBO_RATIO_LIMIT = 0x1ad;
|
|
static constexpr int k_IA32_PM_ENABLE = 0x770;
|
|
|
|
// Returns the current CPU frequency limit. This is not virtual so that we
|
|
// can call this from constructor and destructor safely.
|
|
int GetCurrentFreqLimitMhzImpl() {
|
|
uint64_t v = ReadMsr(k_IA32_PERF_CTL);
|
|
return ((v >> 8) & 0xff) * 100;
|
|
}
|
|
|
|
int GetCurrentFreqLimitMhz() override {
|
|
return GetCurrentFreqLimitMhzImpl();
|
|
}
|
|
|
|
int GetCurrentFreqMhz() override {
|
|
uint64_t v = ReadMsr(k_IA32_PERF_STATUS);
|
|
return ((v >> 8) & 0xff) * 100;
|
|
}
|
|
|
|
// This sets the current maximum CPU frequency. This is not virtual so that we
|
|
// can call this from constructor and destructor safely.
|
|
void SetCurrentMaxFreqMhzImpl(int mhz) {
|
|
if (mhz == max_mHz_) return;
|
|
if (PowerManaged()) {
|
|
LOG_EVERY_N_SECS(ERROR, 10) << "Cpu: " << cpu_
|
|
<< "Cannot set turbo freq while Power Management enabled!";
|
|
}
|
|
ResetFrequencyMeter();
|
|
int hundreds = mhz / 100;
|
|
assert(100 * hundreds == mhz);
|
|
assert(mhz >= kMinTurboMHz);
|
|
assert(mhz <= limit_mhz_);
|
|
uint64_t v = hundreds << 8;
|
|
WriteMsr(k_IA32_PERF_CTL, v);
|
|
max_mHz_ = mhz;
|
|
LOG_EVERY_N_SECS(INFO, 15) << "Set cpu: " << cpu_
|
|
<< " max turbo freq to: " << mhz << " MHz";
|
|
}
|
|
|
|
// Returns the absolute maximum frequency.
|
|
int GetAbsoluteFreqLimitMhzImpl() {
|
|
uint64_t v = ReadMsr(k_MSR_TURBO_RATIO_LIMIT);
|
|
return (v & 0xff) * 100;
|
|
}
|
|
};
|
|
|
|
} // namespace
|
|
|
|
std::unique_ptr<FVTController> NewIntelFVTController(int cpu) {
|
|
return std::unique_ptr<FVTController> (new IntelFVTController(cpu));
|
|
}
|