Skip to content

Instantly share code, notes, and snippets.

@Lunarixus
Created July 30, 2024 01:14
Show Gist options
  • Save Lunarixus/87b5d78c2cb6f2a72f01306817a05f2c to your computer and use it in GitHub Desktop.
Save Lunarixus/87b5d78c2cb6f2a72f01306817a05f2c to your computer and use it in GitHub Desktop.
Linux CPU TDP Monitor And Editor
#include <iostream>
#include <fstream>
#include <string>
#include <thread>
#include <chrono>
#include <iomanip>
#include <fcntl.h>
#include <unistd.h>
#include <cstdint>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
// Define MSR addresses
#define MSR_PKG_ENERGY_STATUS 0x611
#define MSR_RAPL_POWER_UNIT 0x606
#define MSR_PKG_POWER_LIMIT 0x610
// Define memory-mapped I/O address
#define TDP_DISABLE_PORT 0xFED170A8
// Disable 2nd TDP Limit
#define TDP_DISABLE_VALUE 0x0000
// Base I/O port for accessing PCI configuration space
#define IO_PORT_BASE 0xCF8
// Function to read from MSR
unsigned long long read_msr(int cpu, int which) {
int fd;
std::string filename = "/dev/cpu/" + std::to_string(cpu) + "/msr";
fd = open(filename.c_str(), O_RDONLY);
if (fd < 0) {
perror("rdmsr: open");
exit(127);
}
unsigned long long data;
if (pread(fd, &data, sizeof data, which) != sizeof data) {
perror("rdmsr: pread");
exit(127);
}
close(fd);
return data;
}
// Function to write to MSR
void write_msr(int cpu, int which, uint64_t value) {
int fd;
std::string filename = "/dev/cpu/" + std::to_string(cpu) + "/msr";
fd = open(filename.c_str(), O_WRONLY);
if (fd < 0) {
perror("wrmsr: open");
exit(127);
}
if (pwrite(fd, &value, sizeof value, which) != sizeof value) {
perror("wrmsr: pwrite");
exit(127);
}
close(fd);
}
// Function to write to I/O port
void write_io_port(uint16_t port, uint16_t value) {
int fd = open("/dev/port", O_WRONLY);
if (fd < 0) {
perror("Failed to open /dev/port");
exit(1);
}
if (pwrite(fd, &value, sizeof(value), port) != sizeof(value)) {
perror("Failed to write to I/O port");
close(fd);
exit(1);
}
close(fd);
}
// Function to disable the second TDP limit using I/O port
void disable_second_tdp_limit() {
write_io_port(TDP_DISABLE_PORT, TDP_DISABLE_VALUE);
std::cout << "Disabled the second TDP limit." << std::endl;
}
// Function to get CPU information
void print_cpu_info() {
std::ifstream cpuinfo("/proc/cpuinfo");
std::string line;
while (std::getline(cpuinfo, line)) {
if (line.find("model name") != std::string::npos ||
line.find("cpu cores") != std::string::npos ||
line.find("cpu MHz") != std::string::npos) {
std::cout << line << std::endl;
}
}
cpuinfo.close();
}
// Function to monitor and print TDP
void monitor_tdp() {
int cpu = 0; // Monitor the first CPU
unsigned long long energy_status, energy_units;
double energy_unit_joules, energy_value_joules, power_watts;
// Read energy units
energy_units = read_msr(cpu, MSR_RAPL_POWER_UNIT);
energy_unit_joules = 1.0 / (1 << ((energy_units >> 8) & 0x1F));
// Initial energy value
unsigned long long previous_energy_status = read_msr(cpu, MSR_PKG_ENERGY_STATUS);
auto start_time = std::chrono::high_resolution_clock::now();
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(1));
energy_status = read_msr(cpu, MSR_PKG_ENERGY_STATUS);
auto end_time = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed_time = end_time - start_time;
energy_value_joules = (energy_status - previous_energy_status) * energy_unit_joules;
power_watts = energy_value_joules / elapsed_time.count();
previous_energy_status = energy_status;
start_time = end_time;
std::cout << "Current TDP: " << std::fixed << std::setprecision(4) << power_watts << " Watts" << std::endl;
}
}
// Function to set TDP limit and verify
void set_tdp_limit(int option) {
int cpu = 0; // Apply the limit to the first CPU
uint64_t value, read_back_value;
switch(option) {
case 1:
value = 0x00DD8A00; // 10W
break;
case 2:
value = 0x00DD8F00; // 15W
break;
case 3:
value = 0x00000000; // Unlimited
break;
default:
std::cerr << "Invalid option!" << std::endl;
return;
}
write_msr(cpu, MSR_PKG_POWER_LIMIT, value);
read_back_value = read_msr(cpu, MSR_PKG_POWER_LIMIT);
std::cout << "Written value: " << std::hex << value << std::endl;
std::cout << "Read-back value: " << std::hex << read_back_value << std::endl;
// Check if the TDP limit has been applied successfully
if (read_back_value == value) {
std::cout << "TDP limit set to " << (option == 1 ? "10W" : option == 2 ? "15W" : "unlimited") << " successfully." << std::endl;
} else {
std::cerr << "Failed to set TDP limit. BIOS might be locked." << std::endl;
}
}
int main() {
print_cpu_info();
std::cout << "Select TDP limit option: " << std::endl;
std::cout << "1. 10W" << std::endl;
std::cout << "2. 15W" << std::endl;
std::cout << "3. Unlimited" << std::endl;
std::cout << "Enter your choice: ";
int option;
std::cin >> option;
set_tdp_limit(option);
// Disable the second TDP limit
disable_second_tdp_limit();
std::cout << "Monitoring TDP..." << std::endl;
std::thread tdp_thread(monitor_tdp);
tdp_thread.join();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment