Created
July 30, 2024 01:14
-
-
Save Lunarixus/87b5d78c2cb6f2a72f01306817a05f2c to your computer and use it in GitHub Desktop.
Linux CPU TDP Monitor And Editor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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